Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions .github/workflows/oreslang-accelerator-source.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: Oreslang GPU and quantum source IR parity
on:
push:
branches: [ci/accelerator-source-parity-20261010]
pull_request:
paths:
- mirror/dop/**
- .github/workflows/oreslang-accelerator-source.yml

jobs:
source-ir:
runs-on: ubuntu-latest
timeout-minutes: 18
steps:
- name: Check out funded local source mirror
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262
- name: Verify exact upstream private source blobs
shell: bash
run: |
set -euo pipefail
test "$(git hash-object mirror/dop/java/src/main/java/dev/oreslang/runtime/accel/AcceleratorSourceLowering.java)" = "dda00ac62fdddb03a4c5a3c0349986e8d7a99929"
test "$(git hash-object mirror/dop/llvm/src/main/cpp/oreslang/frontend/AcceleratorSourceLowering.hpp)" = "b5fb7676b5593fae2f2c03fe1e49e1baedbcef1c"
test "$(git hash-object mirror/dop/llvm/tests/accelerator_source_lowering_test.cpp)" = "3af5fecac736a90418dfcc71e32b446e0ae85b51"
test "$(git hash-object mirror/dop/java/src/main/java/dev/oreslang/parser/Lexer.java)" = "ed27368ff879eb14b45e05c64c9c41c990f6f4e5"
test "$(git hash-object mirror/dop/java/src/main/java/dev/oreslang/parser/Token.java)" = "aa62cb4db0a5e6129a3bed0a9fb3a254d17021c2"
test "$(git hash-object mirror/dop/java/src/main/java/dev/oreslang/runtime/DataOrientedLayout.java)" = "e21041fbca7508dab8f53d28c6d23765f33eae35"
test "$(git hash-object mirror/dop/java/src/main/java/dev/oreslang/runtime/accel/DopKernelIR.java)" = "09fccea7a05a424da08bf787b2ac4d9dbd27d602"
test "$(git hash-object mirror/dop/java/src/main/java/dev/oreslang/runtime/accel/QuantumCircuitIR.java)" = "7fa87ab68ac5a2c80f0c0785f495c54ba36c2c7a"
test "$(git hash-object mirror/dop/llvm/src/main/cpp/oreslang/runtime/DopLayout.hpp)" = "fea958c2fffd2a3023819c7afff2a8ddc9e82db3"
test "$(git hash-object mirror/dop/llvm/src/main/cpp/oreslang/runtime/DopKernelIR.hpp)" = "ee384bca438104c6cf1d1dacd6d42493aeed1702"
test "$(git hash-object mirror/dop/llvm/src/main/cpp/oreslang/runtime/QuantumCircuitIR.hpp)" = "18036cdba7e9bf0605947eabfc38d3429a000a70"
- name: Compile and execute native source rejection and lowering tests
shell: bash
run: |
set -euo pipefail
g++ -std=c++17 -O2 -DNDEBUG -Wall -Wextra -Wpedantic -Werror \
-Imirror/dop/llvm/src/main/cpp \
mirror/dop/llvm/tests/accelerator_source_lowering_test.cpp \
-o /tmp/accelerator-source-tests
/tmp/accelerator-source-tests
g++ -std=c++17 -O2 -DNDEBUG -Wall -Wextra -Wpedantic -Werror \
-Imirror/dop/llvm/src/main/cpp \
mirror/dop/llvm/tests/source_parity_smoke.cpp \
-o /tmp/accelerator-source-parity
/tmp/accelerator-source-parity
- name: Install JDK 21
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '21'
- name: Compile and execute Java source admission and lowering smoke
shell: bash
run: |
set -euo pipefail
mkdir -p /tmp/accelerator-source-java
javac -Xlint:all -d /tmp/accelerator-source-java \
mirror/dop/java/src/main/java/dev/oreslang/parser/Token.java \
mirror/dop/java/src/main/java/dev/oreslang/parser/Lexer.java \
mirror/dop/java/src/main/java/dev/oreslang/runtime/DataOrientedLayout.java \
mirror/dop/java/src/main/java/dev/oreslang/runtime/accel/DopKernelIR.java \
mirror/dop/java/src/main/java/dev/oreslang/runtime/accel/QuantumCircuitIR.java \
mirror/dop/java/src/main/java/dev/oreslang/runtime/accel/AcceleratorSourceLowering.java \
mirror/dop/java/SourceParitySmoke.java
java -cp /tmp/accelerator-source-java SourceParitySmoke
- name: Verify Java and native source-derived outputs agree
shell: bash
run: |
set -euo pipefail
cmp out/java-source/cpu.txt out/native-source/cpu.txt
cmp out/java-source/bell.qasm out/native-source/bell.qasm
for target in java-source native-source; do
for idx in 0 1 2; do
clang -x cl -cl-std=CL1.2 -fsyntax-only -Werror "out/${target}/kernel${idx}.cl"
done
done
75 changes: 75 additions & 0 deletions mirror/dop/java/SourceParitySmoke.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import dev.oreslang.runtime.DataOrientedLayout;
import dev.oreslang.runtime.accel.AcceleratorSourceLowering;
import dev.oreslang.runtime.accel.DopKernelIR;
import dev.oreslang.runtime.accel.QuantumCircuitIR;
import java.nio.file.Files;
import java.nio.file.Path;

public final class SourceParitySmoke {
static void reject(Runnable work) {
try { work.run(); }
catch (IllegalArgumentException expected) { return; }
throw new AssertionError("invalid accelerator source was accepted");
}
static void check(boolean ok) {
if (!ok) throw new AssertionError("accelerator source parity failure");
}
public static void main(String[] ignored) throws Exception {
Path output = Path.of("out/java-source");
Files.createDirectories(output);
String gpu = "gpu fnc work(f32 value): f32 { return value * 2.0 + 1.0; }";
StringBuilder numbers = new StringBuilder();
int index = 0;
for (var kind : DataOrientedLayout.Kind.values()) {
var layout = new DataOrientedLayout.Plan(kind, 7, 3, 4, 16,
kind == DataOrientedLayout.Kind.AOSOA ? 4 : 1);
var program = AcceleratorSourceLowering.compileGpu(gpu, layout);
check(program.kernel().operation() == DopKernelIR.Op.SCALE_BIAS);
try (var input = new DopKernelIR.HostBuffer(layout);
var result = new DopKernelIR.HostBuffer(layout)) {
for (int row = 0; row < 7; row++)
for (int field = 0; field < 3; field++)
input.write(row, field, row * 3 + field);
DopKernelIR.executeCpu(program.kernel(), input, result);
for (int row = 0; row < 7; row++)
for (int field = 0; field < 3; field++)
numbers.append((int)result.read(row, field)).append(',');
numbers.append('\n');
}
var device = program.emitOpenCl(new DopKernelIR.GpuTarget(64, 4096, 64, true));
Files.writeString(output.resolve("kernel" + index++ + ".cl"), device.deviceSource());
}
Files.writeString(output.resolve("cpu.txt"), numbers.toString());
String quantum = "quantum fnc bell(): Circuit<2,2> { h(0); cx(0,1); measure(0,0); measure(1,1); }";
var circuit = AcceleratorSourceLowering.compileQuantum(quantum);
check(circuit.name().equals("bell"));
var probs = QuantumCircuitIR.simulateProbabilities(circuit.circuit());
check(Math.abs(probs[0] - .5) < 1e-12 && probs[1] == 0 &&
probs[2] == 0 && Math.abs(probs[3] - .5) < 1e-12);
Files.writeString(output.resolve("bell.qasm"), circuit.emitOpenQasm3());

for (var body : new String[]{
"return await value;", "return unsafe_host(value);",
"return value * 2.0; spawn Worker();",
"return value / 2.0;", "return value * 3e400;"
}) reject(() -> AcceleratorSourceLowering.compileGpu(
"gpu fnc work(f32 value): f32 { " + body + " }",
new DataOrientedLayout.Plan(DataOrientedLayout.Kind.SOA, 7, 3, 4, 16, 1)));
for (var body : new String[]{
"h(0); spawn Worker(); measure(0,0);",
"measure(0,0); h(1);", "cx(0,0); measure(0,0);",
"measure(0,0); measure(0,1);", "print(0); measure(0,0);"
}) reject(() -> AcceleratorSourceLowering.compileQuantum(
"quantum fnc bell(): Circuit<2,2> { " + body + " }"));
var subnormal = AcceleratorSourceLowering.compileGpu(
"gpu fnc tiny(f32 value): f32 { return value * 1e-45; }",
new DataOrientedLayout.Plan(DataOrientedLayout.Kind.SOA, 7, 3, 4, 16, 1));
check(subnormal.kernel().scale() == Float.MIN_VALUE);
reject(() -> AcceleratorSourceLowering.compileGpu(
"gpu fnc tiny(f32 value): f32 { return value * 1e-1000; }",
new DataOrientedLayout.Plan(DataOrientedLayout.Kind.SOA, 7, 3, 4, 16, 1)));
reject(() -> AcceleratorSourceLowering.compileQuantum(
"quantum fnc huge(): Circuit<2,2> { measure(0,0); }" + " ".repeat(70_000)));
System.out.println("Java source lowering parity smoke succeeded");
}
}
169 changes: 169 additions & 0 deletions mirror/dop/java/src/main/java/dev/oreslang/parser/Lexer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package dev.oreslang.parser;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static dev.oreslang.parser.Token.Type.*;

public final class Lexer {
private static final Map<String, Token.Type> KEYWORDS = new HashMap<>();

static {
KEYWORDS.put("define", DEFINE); KEYWORDS.put("class", CLASS); KEYWORDS.put("module", MODULE); KEYWORDS.put("contract", CONTRACT); KEYWORDS.put("conforms", CONFORMS); KEYWORDS.put("namespace", NAMESPACE);
KEYWORDS.put("import", IMPORT); KEYWORDS.put("from", FROM); KEYWORDS.put("as", AS);
KEYWORDS.put("extends", EXTENDS); KEYWORDS.put("implements", IMPLEMENTS);
KEYWORDS.put("try", TRY); KEYWORDS.put("catch", CATCH); KEYWORDS.put("finally", FINALLY);
KEYWORDS.put("end", END); KEYWORDS.put("fi", FI); KEYWORDS.put("if", IF); KEYWORDS.put("do", DO);
KEYWORDS.put("else", ELSE); KEYWORDS.put("then", THEN); KEYWORDS.put("new", NEW); KEYWORDS.put("spawn", SPAWN); KEYWORDS.put("stop", STOP); KEYWORDS.put("done", DONE);
KEYWORDS.put("gpu", GPU); KEYWORDS.put("quantum", QUANTUM); KEYWORDS.put("await", AWAIT); KEYWORDS.put("rt", RT); KEYWORDS.put("async", ASYNC); KEYWORDS.put("generator", GENERATOR); KEYWORDS.put("nlex", NLEX); KEYWORDS.put("trap", TRAP); KEYWORDS.put("actor", ACTOR); KEYWORDS.put("isoactor", ISOACTOR); KEYWORDS.put("def", DEF); KEYWORDS.put("fnc", FNC); KEYWORDS.put("routine", ROUTINE);
KEYWORDS.put("for", FOR); KEYWORDS.put("of", OF); KEYWORDS.put("loop", LOOP); KEYWORDS.put("block", BLOCK); KEYWORDS.put("break", BREAK); KEYWORDS.put("continue", CONTINUE); KEYWORDS.put("yield", YIELD); KEYWORDS.put("super", SUPER); KEYWORDS.put("elseif", ELSEIF); KEYWORDS.put("elif", ELSEIF); KEYWORDS.put("switch", SWITCH); KEYWORDS.put("match", MATCH); KEYWORDS.put("matches", MATCHES); KEYWORDS.put("is", IS); KEYWORDS.put("default", DEFAULT);
KEYWORDS.put("eq", EQ); KEYWORDS.put("neq", NEQ);
KEYWORDS.put("nb", NB); KEYWORDS.put("select", SELECT); KEYWORDS.put("readch", READCH); KEYWORDS.put("writech", WRITECH);
KEYWORDS.put("cb", CB); KEYWORDS.put("shared", SHARED); KEYWORDS.put("untrusted", UNTRUSTED);
KEYWORDS.put("type", TYPE); KEYWORDS.put("types", TYPES); KEYWORDS.put("typeof", TYPEOF); KEYWORDS.put("interface", INTERFACE); KEYWORDS.put("trait", TRAIT); KEYWORDS.put("struct", STRUCT); KEYWORDS.put("impl", IMPL); KEYWORDS.put("abstract", ABSTRACT);
KEYWORDS.put("void", VOID); KEYWORDS.put("static", STATIC); KEYWORDS.put("pub", PUB); KEYWORDS.put("private", PRIVATE);
KEYWORDS.put("return", RETURN); KEYWORDS.put("defer", DEFER); KEYWORDS.put("val", VAL); KEYWORDS.put("const", CONST);
KEYWORDS.put("let", LET); KEYWORDS.put("mut", MUT); KEYWORDS.put("self", SELF); KEYWORDS.put("true", TRUE); KEYWORDS.put("false", FALSE);
KEYWORDS.put("null", NULL); KEYWORDS.put("obj", OBJ); KEYWORDS.put("arr", ARR);
}

private final String source;
private final List<Token> tokens = new ArrayList<>();
private int start;
private int current;
private int line = 1;
private int column = 1;
private int startColumn = 1;

public Lexer(String source) { this.source = source == null ? "" : source; }

public List<Token> scan() {
while (!isAtEnd()) {
start = current;
startColumn = column;
scanToken();
}
tokens.add(new Token(EOF, "", line, column));
return List.copyOf(tokens);
}

private void scanToken() {
char c = advance();
switch (c) {
case '(' -> add(LPAREN); case ')' -> add(RPAREN); case '{' -> add(LBRACE); case '}' -> add(RBRACE);
case '[' -> add(LBRACKET); case ']' -> add(RBRACKET); case ',' -> add(COMMA);
case '.' -> {
if (match('.')) {
if (!match('.')) throw error("unexpected '..'; argument spread uses '...'");
add(ELLIPSIS);
} else add(DOT);
}
case ';' -> add(SEMICOLON); case ':' -> add(COLON); case '?' -> add(QUESTION); case '@' -> add(AT); case '`' -> add(BACKTICK); case '+' -> add(PLUS);
case '*' -> add(STAR); case '%' -> add(PERCENT); case '|' -> add(PIPE); case '&' -> add(AMP);
case '^' -> add(CARET); case '~' -> add(TILDE);
case '-' -> add(match('>') ? ARROW : MINUS);
case '!' -> add(match('=') ? BANG_EQUAL : BANG);
case '=' -> add(match('>') ? FAT_ARROW : match('=') ? EQUAL_EQUAL : EQUAL);
case '<' -> add(match('=') ? LTE : LT); case '>' -> add(match('=') ? GTE : GT);
case '/' -> {
if (match('/')) while (peek() != '\n' && !isAtEnd()) advance();
else if (match('*')) blockComment();
else add(SLASH);
}
case ' ', '\r', '\t' -> { }
case '\n' -> newline();
case '"', '\'' -> string(c);
default -> {
if (isDigit(c)) number();
else if (isIdentStart(c)) identifier();
else throw error("unexpected character '" + c + "'");
}
}
}

private void blockComment() {
int depth = 1;
while (depth > 0) {
if (isAtEnd()) throw error("unterminated block comment");
char c = advance();
if (c == '\n') newline();
else if (c == '/' && match('*')) depth++;
else if (c == '*' && match('/')) depth--;
}
}

private void string(char quote) {
StringBuilder value = new StringBuilder();
while (!isAtEnd() && peek() != quote) {
char c = advance();
if (c == '\n') { newline(); value.append('\n'); continue; }
if (c == '\\') {
if (isAtEnd()) throw error("unterminated string escape");
char escaped = advance();
value.append(switch (escaped) {
case 'u' -> unicodeEscape();
case 'n' -> '\n'; case 'r' -> '\r'; case 't' -> '\t'; case '"' -> '"'; case '\'' -> '\''; case '\\' -> '\\'; default -> escaped;
});
} else value.append(c);
}
if (isAtEnd()) throw error("unterminated string");
advance();
tokens.add(new Token(STRING, value.toString(), line, startColumn));
}

private char unicodeEscape() {
int code = 0;
for (int i = 0; i < 4; i++) {
if (isAtEnd()) throw error("incomplete Unicode escape");
char digit = advance();
int hex = digit >= '0' && digit <= '9' ? digit - '0'
: digit >= 'a' && digit <= 'f' ? digit - 'a' + 10
: digit >= 'A' && digit <= 'F' ? digit - 'A' + 10 : -1;
if (hex < 0) throw error("invalid Unicode escape");
code = (code << 4) | hex;
}
return (char) code;
}

private void number() {
while (isDigit(peek()) || peek() == '_') advance();
boolean floating = false;
if (peek() == '.' && isDigit(peekNext())) {
floating = true; advance();
while (isDigit(peek()) || peek() == '_') advance();
}
if (peek() == 'e' || peek() == 'E') {
floating = true; advance();
if (peek() == '+' || peek() == '-') advance();
if (!isDigit(peek())) throw error("malformed exponent");
while (isDigit(peek()) || peek() == '_') advance();
}
if (peek() == 'i') { advance(); add(IMAG); }
else add(floating ? FLOAT : INT);
}

private void identifier() {
while (isIdentPart(peek())) advance();
String text = source.substring(start, current);
add(KEYWORDS.getOrDefault(text, IDENT));
}

private boolean isAtEnd() { return current >= source.length(); }
private char advance() { char c = source.charAt(current++); column++; return c; }
private boolean match(char expected) {
if (isAtEnd() || source.charAt(current) != expected) return false;
current++; column++; return true;
}
private char peek() { return isAtEnd() ? '\0' : source.charAt(current); }
private char peekNext() { return current + 1 >= source.length() ? '\0' : source.charAt(current + 1); }
private void newline() { line++; column = 1; }
private void add(Token.Type type) { tokens.add(new Token(type, source.substring(start, current), line, startColumn)); }
private boolean isDigit(char c) { return c >= '0' && c <= '9'; }
private boolean isIdentStart(char c) { return Character.isLetter(c) || c == '_'; }
private boolean isIdentPart(char c) { return isIdentStart(c) || isDigit(c); }
private IllegalArgumentException error(String message) {
return new IllegalArgumentException("Oreslang lexer error at " + line + ":" + startColumn + ": " + message);
}
}
25 changes: 25 additions & 0 deletions mirror/dop/java/src/main/java/dev/oreslang/parser/Token.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package dev.oreslang.parser;

public record Token(Type type, String lexeme, int line, int column) {
public enum Type {
IDENT,
INT,
FLOAT,
IMAG,
STRING,

DEFINE, CLASS, MODULE, CONTRACT, CONFORMS, NAMESPACE, IMPORT, FROM, AS, EXTENDS, IMPLEMENTS,
TRY, CATCH, FINALLY, END, FI, IF, DO, ELSE, THEN,
NEW, SPAWN, STOP, DONE, GPU, QUANTUM, AWAIT, RT, ASYNC, GENERATOR, NLEX, TRAP, NB, CB, SELECT, READCH, WRITECH, ACTOR, ISOACTOR, SHARED, DEF, FNC, ROUTINE, FOR, OF, LOOP, BLOCK, BREAK, CONTINUE, YIELD, SUPER, ELSEIF, SWITCH, MATCH, MATCHES, EQ, NEQ, IS, WHEN, CASE, DEFAULT, FIRST, UNTRUSTED, TYPE, TYPEOF,
INTERFACE, TRAIT, STRUCT, INFER, TYPES, IMPL, ABSTRACT, VOID, STATIC, PUB, PRIVATE, STRUCTURAL, RETURN, DEFER,
VAL, CONST, LET, MUT, SELF, TRUE, FALSE, NULL, OBJ, ARR,

LPAREN, RPAREN, LBRACE, RBRACE, LBRACKET, RBRACKET,
COMMA, DOT, ELLIPSIS, SEMICOLON, COLON, QUESTION, AT, BACKTICK,
PLUS, MINUS, STAR, SLASH, PERCENT, PIPE, AMP, CARET, TILDE, BANG,
EQUAL, EQUAL_EQUAL, BANG_EQUAL,
LT, LTE, GT, GTE,
ARROW, FAT_ARROW,
EOF
}
}
Loading
Loading