new test main with code directly (fixes) runtime runing
This commit is contained in:
parent
5630f8f119
commit
54f0b4b6ba
@ -287,6 +287,10 @@ public final class PbsFrontendCompiler {
|
||||
if (functionCallableId == null) {
|
||||
continue;
|
||||
}
|
||||
final var localSlotByNameId = new HashMap<NameId, Integer>();
|
||||
for (var paramIndex = 0; paramIndex < fn.parameters().size(); paramIndex++) {
|
||||
localSlotByNameId.put(nameTable.register(fn.parameters().get(paramIndex).name()), paramIndex);
|
||||
}
|
||||
final var loweringContext = new ExecutableLoweringContext(
|
||||
normalizedModuleKey,
|
||||
diagnostics,
|
||||
@ -295,7 +299,8 @@ public final class PbsFrontendCompiler {
|
||||
intrinsicByMethodName,
|
||||
callableIdsByNameAndArity,
|
||||
returnSlotsByCallableId,
|
||||
intrinsicIdTable);
|
||||
intrinsicIdTable,
|
||||
localSlotByNameId);
|
||||
final var terminated = lowerBlock(fn.body(), loweringContext);
|
||||
final var instructions = loweringContext.instructions();
|
||||
if (!terminated) {
|
||||
@ -618,7 +623,7 @@ public final class PbsFrontendCompiler {
|
||||
}
|
||||
switch (expression) {
|
||||
case PbsAst.CallExpr callExpr -> {
|
||||
lowerExpression(callExpr.callee(), context);
|
||||
lowerCallsiteReceiver(callExpr.callee(), context);
|
||||
for (final var arg : callExpr.arguments()) {
|
||||
lowerExpression(arg, context);
|
||||
}
|
||||
@ -678,16 +683,27 @@ public final class PbsFrontendCompiler {
|
||||
}
|
||||
}
|
||||
case PbsAst.BlockExpr blockExpr -> lowerBlock(blockExpr.block(), context);
|
||||
case PbsAst.IdentifierExpr ignored -> {
|
||||
case PbsAst.IdentifierExpr identifierExpr -> {
|
||||
final var slot = context.localSlotByNameId().get(context.nameTable().register(identifierExpr.name()));
|
||||
if (slot != null) {
|
||||
emitGetLocal(slot, identifierExpr.span(), context);
|
||||
}
|
||||
}
|
||||
case PbsAst.IntLiteralExpr ignored -> {
|
||||
case PbsAst.IntLiteralExpr intLiteralExpr -> {
|
||||
if (intLiteralExpr.value() < Integer.MIN_VALUE || intLiteralExpr.value() > Integer.MAX_VALUE) {
|
||||
reportUnsupportedLowering(
|
||||
"int literal exceeds i32 lowering range: " + intLiteralExpr.value(),
|
||||
intLiteralExpr.span(),
|
||||
context);
|
||||
return;
|
||||
}
|
||||
emitPushI32((int) intLiteralExpr.value(), intLiteralExpr.span(), context);
|
||||
}
|
||||
case PbsAst.FloatLiteralExpr ignored -> {
|
||||
}
|
||||
case PbsAst.BoundedLiteralExpr ignored -> {
|
||||
}
|
||||
case PbsAst.StringLiteralExpr ignored -> {
|
||||
}
|
||||
case PbsAst.StringLiteralExpr stringLiteralExpr -> emitPushConst(stringLiteralExpr.value(), stringLiteralExpr.span(), context);
|
||||
case PbsAst.BoolLiteralExpr ignored -> {
|
||||
}
|
||||
case PbsAst.ThisExpr ignored -> {
|
||||
@ -701,6 +717,20 @@ public final class PbsFrontendCompiler {
|
||||
}
|
||||
}
|
||||
|
||||
private void lowerCallsiteReceiver(
|
||||
final PbsAst.Expression callee,
|
||||
final ExecutableLoweringContext context) {
|
||||
if (callee == null) {
|
||||
return;
|
||||
}
|
||||
switch (callee) {
|
||||
case PbsAst.MemberExpr memberExpr -> lowerExpression(memberExpr.receiver(), context);
|
||||
case PbsAst.GroupExpr groupExpr -> lowerCallsiteReceiver(groupExpr.expression(), context);
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void lowerCallsite(
|
||||
final PbsAst.CallExpr callExpr,
|
||||
final ExecutableLoweringContext context) {
|
||||
@ -920,6 +950,60 @@ public final class PbsFrontendCompiler {
|
||||
span));
|
||||
}
|
||||
|
||||
private void emitPushI32(
|
||||
final int value,
|
||||
final p.studio.compiler.source.Span span,
|
||||
final ExecutableLoweringContext context) {
|
||||
context.instructions().add(new IRBackendExecutableFunction.Instruction(
|
||||
IRBackendExecutableFunction.InstructionKind.PUSH_I32,
|
||||
"",
|
||||
"",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
Integer.toString(value),
|
||||
"",
|
||||
null,
|
||||
null,
|
||||
span));
|
||||
}
|
||||
|
||||
private void emitPushConst(
|
||||
final String value,
|
||||
final p.studio.compiler.source.Span span,
|
||||
final ExecutableLoweringContext context) {
|
||||
context.instructions().add(new IRBackendExecutableFunction.Instruction(
|
||||
IRBackendExecutableFunction.InstructionKind.PUSH_CONST,
|
||||
"",
|
||||
"",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
value == null ? "" : value,
|
||||
"",
|
||||
null,
|
||||
null,
|
||||
span));
|
||||
}
|
||||
|
||||
private void emitGetLocal(
|
||||
final int slot,
|
||||
final p.studio.compiler.source.Span span,
|
||||
final ExecutableLoweringContext context) {
|
||||
context.instructions().add(new IRBackendExecutableFunction.Instruction(
|
||||
IRBackendExecutableFunction.InstructionKind.GET_LOCAL,
|
||||
"",
|
||||
"",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"",
|
||||
"",
|
||||
slot,
|
||||
null,
|
||||
span));
|
||||
}
|
||||
|
||||
private void reportUnsupportedLowering(
|
||||
final String message,
|
||||
final p.studio.compiler.source.Span span,
|
||||
@ -964,9 +1048,34 @@ public final class PbsFrontendCompiler {
|
||||
var outHeight = incomingHeightByInstruction.get(index);
|
||||
|
||||
switch (instruction.kind()) {
|
||||
case CALL_FUNC -> outHeight += instruction.expectedRetSlots() == null ? 0 : instruction.expectedRetSlots();
|
||||
case CALL_HOST -> outHeight += instruction.hostCall() == null ? 0 : instruction.hostCall().retSlots();
|
||||
case CALL_INTRINSIC -> outHeight += instruction.expectedRetSlots() == null ? 0 : instruction.expectedRetSlots();
|
||||
case PUSH_I32, PUSH_CONST, GET_LOCAL -> outHeight += 1;
|
||||
case CALL_FUNC -> {
|
||||
final var argSlots = instruction.expectedArgSlots() == null ? 0 : instruction.expectedArgSlots();
|
||||
final var retSlots = instruction.expectedRetSlots() == null ? 0 : instruction.expectedRetSlots();
|
||||
if (outHeight < argSlots) {
|
||||
throw new ExecutableLoweringAnalysisException(
|
||||
"stack underflow at call_func: need=%d have=%d".formatted(argSlots, outHeight));
|
||||
}
|
||||
outHeight = outHeight - argSlots + retSlots;
|
||||
}
|
||||
case CALL_HOST -> {
|
||||
final var argSlots = instruction.hostCall() == null ? 0 : instruction.hostCall().argSlots();
|
||||
final var retSlots = instruction.hostCall() == null ? 0 : instruction.hostCall().retSlots();
|
||||
if (outHeight < argSlots) {
|
||||
throw new ExecutableLoweringAnalysisException(
|
||||
"stack underflow at call_host: need=%d have=%d".formatted(argSlots, outHeight));
|
||||
}
|
||||
outHeight = outHeight - argSlots + retSlots;
|
||||
}
|
||||
case CALL_INTRINSIC -> {
|
||||
final var argSlots = instruction.expectedArgSlots() == null ? 0 : instruction.expectedArgSlots();
|
||||
final var retSlots = instruction.expectedRetSlots() == null ? 0 : instruction.expectedRetSlots();
|
||||
if (outHeight < argSlots) {
|
||||
throw new ExecutableLoweringAnalysisException(
|
||||
"stack underflow at call_intrinsic: need=%d have=%d".formatted(argSlots, outHeight));
|
||||
}
|
||||
outHeight = outHeight - argSlots + retSlots;
|
||||
}
|
||||
case HALT, LABEL, JMP, RET -> {
|
||||
}
|
||||
case JMP_IF_TRUE, JMP_IF_FALSE -> {
|
||||
@ -1040,6 +1149,7 @@ public final class PbsFrontendCompiler {
|
||||
private final Map<CallableResolutionKey, List<CallableId>> callableIdsByNameAndArity;
|
||||
private final Map<CallableId, Integer> returnSlotsByCallableId;
|
||||
private final IntrinsicTable intrinsicIdTable;
|
||||
private final Map<NameId, Integer> localSlotByNameId;
|
||||
private final ArrayList<IRBackendExecutableFunction.Instruction> instructions = new ArrayList<>();
|
||||
private final ArrayDeque<LoopTargets> loopTargets = new ArrayDeque<>();
|
||||
private int nextLabelId = 0;
|
||||
@ -1052,7 +1162,8 @@ public final class PbsFrontendCompiler {
|
||||
final Map<NameId, List<IRReservedMetadata.IntrinsicSurface>> intrinsicByMethodName,
|
||||
final Map<CallableResolutionKey, List<CallableId>> callableIdsByNameAndArity,
|
||||
final Map<CallableId, Integer> returnSlotsByCallableId,
|
||||
final IntrinsicTable intrinsicIdTable) {
|
||||
final IntrinsicTable intrinsicIdTable,
|
||||
final Map<NameId, Integer> localSlotByNameId) {
|
||||
this.moduleKey = moduleKey;
|
||||
this.diagnostics = diagnostics;
|
||||
this.nameTable = nameTable;
|
||||
@ -1061,6 +1172,7 @@ public final class PbsFrontendCompiler {
|
||||
this.callableIdsByNameAndArity = callableIdsByNameAndArity;
|
||||
this.returnSlotsByCallableId = returnSlotsByCallableId;
|
||||
this.intrinsicIdTable = intrinsicIdTable;
|
||||
this.localSlotByNameId = localSlotByNameId == null ? Map.of() : localSlotByNameId;
|
||||
}
|
||||
|
||||
private String moduleKey() {
|
||||
@ -1095,6 +1207,10 @@ public final class PbsFrontendCompiler {
|
||||
return intrinsicIdTable;
|
||||
}
|
||||
|
||||
private Map<NameId, Integer> localSlotByNameId() {
|
||||
return localSlotByNameId;
|
||||
}
|
||||
|
||||
private ArrayList<IRBackendExecutableFunction.Instruction> instructions() {
|
||||
return instructions;
|
||||
}
|
||||
|
||||
@ -3,7 +3,9 @@ callables=2
|
||||
1:::a/0|()->simple:int
|
||||
intrinsics=0
|
||||
fn#0:::b id=0
|
||||
GET_LOCAL calleeId=- intrinsicId=-
|
||||
RET calleeId=- intrinsicId=-
|
||||
fn#1:::a id=1
|
||||
PUSH_I32 calleeId=- intrinsicId=-
|
||||
CALL_FUNC calleeId=0 intrinsicId=-
|
||||
RET calleeId=- intrinsicId=-
|
||||
|
||||
@ -17,6 +17,9 @@ public class BytecodeEmitter {
|
||||
private static final int OP_JMP = 0x02;
|
||||
private static final int OP_JMP_IF_FALSE = 0x03;
|
||||
private static final int OP_JMP_IF_TRUE = 0x04;
|
||||
private static final int OP_PUSH_CONST = 0x10;
|
||||
private static final int OP_GET_LOCAL = 0x42;
|
||||
private static final int OP_PUSH_I32 = 0x17;
|
||||
private static final int OP_HOSTCALL = 0x71;
|
||||
private static final int OP_SYSCALL = 0x70;
|
||||
private static final int OP_INTRINSIC = 0x72;
|
||||
@ -48,6 +51,18 @@ public class BytecodeEmitter {
|
||||
writeOpU32(code, OP_CALL, op.immediate());
|
||||
spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, spanOrUnknown(op.span())));
|
||||
}
|
||||
case PUSH_CONST -> {
|
||||
writeOpU32(code, OP_PUSH_CONST, op.immediate());
|
||||
spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, spanOrUnknown(op.span())));
|
||||
}
|
||||
case GET_LOCAL -> {
|
||||
writeOpU32(code, OP_GET_LOCAL, op.immediate());
|
||||
spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, spanOrUnknown(op.span())));
|
||||
}
|
||||
case PUSH_I32 -> {
|
||||
writeOpU32(code, OP_PUSH_I32, op.immediate());
|
||||
spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, spanOrUnknown(op.span())));
|
||||
}
|
||||
case JMP -> {
|
||||
writeOpU32(code, OP_JMP, op.immediate());
|
||||
spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, spanOrUnknown(op.span())));
|
||||
@ -194,6 +209,9 @@ public class BytecodeEmitter {
|
||||
public enum OperationKind {
|
||||
HALT,
|
||||
RET,
|
||||
PUSH_CONST,
|
||||
GET_LOCAL,
|
||||
PUSH_I32,
|
||||
CALL_FUNC,
|
||||
JMP,
|
||||
JMP_IF_TRUE,
|
||||
@ -254,6 +272,18 @@ public class BytecodeEmitter {
|
||||
return new Operation(OperationKind.CALL_FUNC, funcId, null, null, null, span);
|
||||
}
|
||||
|
||||
public static Operation pushConst(final int constPoolIndex, final BytecodeModule.SourceSpan span) {
|
||||
return new Operation(OperationKind.PUSH_CONST, constPoolIndex, null, null, null, span);
|
||||
}
|
||||
|
||||
public static Operation getLocal(final int slot, final BytecodeModule.SourceSpan span) {
|
||||
return new Operation(OperationKind.GET_LOCAL, slot, null, null, null, span);
|
||||
}
|
||||
|
||||
public static Operation pushI32(final int value, final BytecodeModule.SourceSpan span) {
|
||||
return new Operation(OperationKind.PUSH_I32, value, null, null, null, span);
|
||||
}
|
||||
|
||||
public static Operation hostcall(
|
||||
final BytecodeModule.SyscallDecl decl,
|
||||
final Integer expectedArgSlots,
|
||||
|
||||
@ -12,6 +12,7 @@ public record IRVMOp(
|
||||
|
||||
public static final IRVMOp HALT = new IRVMOp("HALT", 0x01, 0, 0, 0, false, true, false);
|
||||
public static final IRVMOp RET = new IRVMOp("RET", 0x51, 0, 0, 0, false, true, false);
|
||||
public static final IRVMOp PUSH_CONST = new IRVMOp("PUSH_CONST", 0x10, 4, 0, 1, false, false, false);
|
||||
public static final IRVMOp CALL = new IRVMOp("CALL", 0x50, 4, 0, 0, false, false, false);
|
||||
public static final IRVMOp JMP = new IRVMOp("JMP", 0x02, 4, 0, 0, true, true, false);
|
||||
public static final IRVMOp JMP_IF_TRUE = new IRVMOp("JMP_IF_TRUE", 0x04, 4, 1, 0, true, false, false);
|
||||
@ -19,6 +20,6 @@ public record IRVMOp(
|
||||
public static final IRVMOp HOSTCALL = new IRVMOp("HOSTCALL", 0x71, 4, 0, 0, false, false, false);
|
||||
public static final IRVMOp INTRINSIC = new IRVMOp("INTRINSIC", 0x72, 4, 0, 0, false, false, false);
|
||||
public static final IRVMOp PUSH_I32 = new IRVMOp("PUSH_I32", 0x17, 4, 0, 1, false, false, false);
|
||||
public static final IRVMOp GET_LOCAL = new IRVMOp("GET_LOCAL", 0x42, 4, 0, 1, false, false, false);
|
||||
public static final IRVMOp INTERNAL_EXT = new IRVMOp("IRVM_EXT_NOP", 0xFFFF, 0, 0, 0, false, false, true);
|
||||
}
|
||||
|
||||
|
||||
@ -11,6 +11,8 @@ public final class IRVMProfileFeatureGate {
|
||||
"core-v1", Set.of(
|
||||
IRVMOp.HALT,
|
||||
IRVMOp.RET,
|
||||
IRVMOp.PUSH_CONST,
|
||||
IRVMOp.GET_LOCAL,
|
||||
IRVMOp.CALL,
|
||||
IRVMOp.JMP,
|
||||
IRVMOp.JMP_IF_TRUE,
|
||||
@ -21,6 +23,8 @@ public final class IRVMProfileFeatureGate {
|
||||
"experimental-v1", Set.of(
|
||||
IRVMOp.HALT,
|
||||
IRVMOp.RET,
|
||||
IRVMOp.PUSH_CONST,
|
||||
IRVMOp.GET_LOCAL,
|
||||
IRVMOp.CALL,
|
||||
IRVMOp.JMP,
|
||||
IRVMOp.JMP_IF_TRUE,
|
||||
|
||||
@ -92,6 +92,9 @@ public record IRVMProgram(
|
||||
return switch (instruction.op().opcode()) {
|
||||
case 0x01 -> operation.kind() == BytecodeEmitter.OperationKind.HALT;
|
||||
case 0x51 -> operation.kind() == BytecodeEmitter.OperationKind.RET;
|
||||
case 0x10 -> operation.kind() == BytecodeEmitter.OperationKind.PUSH_CONST && operation.immediate() == immediate;
|
||||
case 0x42 -> operation.kind() == BytecodeEmitter.OperationKind.GET_LOCAL && operation.immediate() == immediate;
|
||||
case 0x17 -> operation.kind() == BytecodeEmitter.OperationKind.PUSH_I32 && operation.immediate() == immediate;
|
||||
case 0x50 -> operation.kind() == BytecodeEmitter.OperationKind.CALL_FUNC && operation.immediate() == immediate;
|
||||
case 0x02 -> operation.kind() == BytecodeEmitter.OperationKind.JMP && operation.immediate() == immediate;
|
||||
case 0x04 -> operation.kind() == BytecodeEmitter.OperationKind.JMP_IF_TRUE && operation.immediate() == immediate;
|
||||
@ -153,6 +156,9 @@ public record IRVMProgram(
|
||||
return switch (instruction.op().opcode()) {
|
||||
case 0x01 -> BytecodeEmitter.Operation.halt();
|
||||
case 0x51 -> BytecodeEmitter.Operation.ret();
|
||||
case 0x10 -> BytecodeEmitter.Operation.pushConst(immediate, null);
|
||||
case 0x42 -> BytecodeEmitter.Operation.getLocal(immediate, null);
|
||||
case 0x17 -> BytecodeEmitter.Operation.pushI32(immediate, null);
|
||||
case 0x50 -> BytecodeEmitter.Operation.callFunc(immediate);
|
||||
case 0x02 -> BytecodeEmitter.Operation.jmp(immediate, null);
|
||||
case 0x04 -> BytecodeEmitter.Operation.jmpIfTrue(immediate, null);
|
||||
@ -204,6 +210,9 @@ public record IRVMProgram(
|
||||
return switch (operation.kind()) {
|
||||
case HALT -> new IRVMInstruction(IRVMOp.HALT, null);
|
||||
case RET -> new IRVMInstruction(IRVMOp.RET, null);
|
||||
case PUSH_CONST -> new IRVMInstruction(IRVMOp.PUSH_CONST, operation.immediate());
|
||||
case GET_LOCAL -> new IRVMInstruction(IRVMOp.GET_LOCAL, operation.immediate());
|
||||
case PUSH_I32 -> new IRVMInstruction(IRVMOp.PUSH_I32, operation.immediate());
|
||||
case CALL_FUNC -> new IRVMInstruction(IRVMOp.CALL, operation.immediate());
|
||||
case JMP -> new IRVMInstruction(IRVMOp.JMP, operation.immediate());
|
||||
case JMP_IF_TRUE -> new IRVMInstruction(IRVMOp.JMP_IF_TRUE, operation.immediate());
|
||||
|
||||
@ -56,6 +56,8 @@ public class LowerToIRVMService {
|
||||
|
||||
final var loweredFunctions = new ArrayList<IRVMFunction>(ordered.size());
|
||||
final var emissionFunctions = new ArrayList<BytecodeEmitter.FunctionPlan>(ordered.size());
|
||||
final var constPool = new ArrayList<BytecodeModule.ConstantPoolEntry>();
|
||||
final var constPoolIndexByString = new HashMap<String, Integer>();
|
||||
|
||||
for (var i = 0; i < ordered.size(); i++) {
|
||||
final var fn = ordered.get(i);
|
||||
@ -67,6 +69,44 @@ public class LowerToIRVMService {
|
||||
for (final var instr : fn.instructions()) {
|
||||
final var sourceSpan = toBytecodeSpan(fn.fileId().getId(), instr.span());
|
||||
switch (instr.kind()) {
|
||||
case PUSH_I32 -> {
|
||||
final int value;
|
||||
try {
|
||||
value = Integer.parseInt(instr.label());
|
||||
} catch (NumberFormatException e) {
|
||||
throw loweringError(
|
||||
fn,
|
||||
instr,
|
||||
IRVMLoweringErrorCode.LOWER_IRVM_INVALID_INTRINSIC_ID,
|
||||
"invalid PUSH_I32 literal payload: " + instr.label());
|
||||
}
|
||||
instructions.add(new IRVMInstruction(IRVMOp.PUSH_I32, value));
|
||||
operations.add(BytecodeEmitter.Operation.pushI32(value, sourceSpan));
|
||||
functionPc += IRVMOp.PUSH_I32.immediateSize() + 2;
|
||||
}
|
||||
case PUSH_CONST -> {
|
||||
final var value = instr.label() == null ? "" : instr.label();
|
||||
final var constPoolIndex = constPoolIndexByString.computeIfAbsent(value, ignored -> {
|
||||
constPool.add(new BytecodeModule.StringConstant(value));
|
||||
return constPool.size() - 1;
|
||||
});
|
||||
instructions.add(new IRVMInstruction(IRVMOp.PUSH_CONST, constPoolIndex));
|
||||
operations.add(BytecodeEmitter.Operation.pushConst(constPoolIndex, sourceSpan));
|
||||
functionPc += IRVMOp.PUSH_CONST.immediateSize() + 2;
|
||||
}
|
||||
case GET_LOCAL -> {
|
||||
final var slot = instr.expectedArgSlots();
|
||||
if (slot == null) {
|
||||
throw loweringError(
|
||||
fn,
|
||||
instr,
|
||||
IRVMLoweringErrorCode.LOWER_IRVM_MISSING_CALLEE,
|
||||
"missing local slot for GET_LOCAL");
|
||||
}
|
||||
instructions.add(new IRVMInstruction(IRVMOp.GET_LOCAL, slot));
|
||||
operations.add(BytecodeEmitter.Operation.getLocal(slot, sourceSpan));
|
||||
functionPc += IRVMOp.GET_LOCAL.immediateSize() + 2;
|
||||
}
|
||||
case HALT -> {
|
||||
instructions.add(new IRVMInstruction(IRVMOp.HALT, null));
|
||||
operations.add(BytecodeEmitter.Operation.halt(sourceSpan));
|
||||
@ -246,7 +286,7 @@ public class LowerToIRVMService {
|
||||
new IRVMModule(vmProfile, ReadOnlyList.wrap(loweredFunctions)),
|
||||
new BytecodeEmitter.EmissionPlan(
|
||||
0,
|
||||
ReadOnlyList.empty(),
|
||||
ReadOnlyList.wrap(constPool),
|
||||
ReadOnlyList.empty(),
|
||||
ReadOnlyList.wrap(emissionFunctions)));
|
||||
validator.validate(program, false);
|
||||
|
||||
@ -0,0 +1,53 @@
|
||||
package p.studio.compiler.integration;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import p.studio.compiler.messages.BuilderPipelineConfig;
|
||||
import p.studio.compiler.workspaces.BuilderPipelineService;
|
||||
import p.studio.utilities.logs.LogAggregator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
class MainProjectPipelineIntegrationTest {
|
||||
|
||||
@Test
|
||||
void shouldCompileMainProjectAndWriteProgramBytecode() throws IOException {
|
||||
final var repoRoot = findRepoRoot(Path.of("").toAbsolutePath().normalize());
|
||||
final var projectRoot = repoRoot.resolve("test-projects").resolve("main");
|
||||
final var outputPath = projectRoot.resolve("build").resolve("program.pbx");
|
||||
Files.createDirectories(outputPath.getParent());
|
||||
Files.deleteIfExists(outputPath);
|
||||
|
||||
final var logsOut = new StringBuilder();
|
||||
final var logs = LogAggregator.with(line -> {
|
||||
logsOut.append(line);
|
||||
if (!line.endsWith("\n")) {
|
||||
logsOut.append('\n');
|
||||
}
|
||||
});
|
||||
|
||||
assertDoesNotThrow(
|
||||
() -> BuilderPipelineService.INSTANCE.run(new BuilderPipelineConfig(false, projectRoot.toString()), logs),
|
||||
logsOut::toString);
|
||||
assertTrue(Files.exists(outputPath), "pipeline did not write output: " + outputPath + "\n" + logsOut);
|
||||
assertTrue(Files.size(outputPath) > 0, "pipeline wrote empty bytecode file: " + outputPath + "\n" + logsOut);
|
||||
}
|
||||
|
||||
private Path findRepoRoot(final Path start) {
|
||||
var current = start;
|
||||
while (current != null) {
|
||||
if (Files.exists(current.resolve("settings.gradle.kts"))
|
||||
&& Files.exists(current.resolve("test-projects").resolve("main").resolve("prometeu.json"))) {
|
||||
return current;
|
||||
}
|
||||
current = current.getParent();
|
||||
}
|
||||
fail("unable to locate repository root from " + start);
|
||||
return start;
|
||||
}
|
||||
}
|
||||
@ -128,6 +128,45 @@ public record IRBackendExecutableFunction(
|
||||
throw new IllegalArgumentException("expectedRetSlots must be non-negative");
|
||||
}
|
||||
switch (kind) {
|
||||
case PUSH_I32 -> {
|
||||
if (label.isBlank()) {
|
||||
throw new IllegalArgumentException("PUSH_I32 requires immediate label payload");
|
||||
}
|
||||
if (!targetLabel.isBlank()
|
||||
|| !calleeCallableName.isBlank()
|
||||
|| calleeCallableId != null
|
||||
|| hostCall != null
|
||||
|| intrinsicCall != null
|
||||
|| expectedArgSlots != null
|
||||
|| expectedRetSlots != null) {
|
||||
throw new IllegalArgumentException("PUSH_I32 must not carry call metadata");
|
||||
}
|
||||
}
|
||||
case PUSH_CONST -> {
|
||||
if (!targetLabel.isBlank()
|
||||
|| !calleeCallableName.isBlank()
|
||||
|| calleeCallableId != null
|
||||
|| hostCall != null
|
||||
|| intrinsicCall != null
|
||||
|| expectedArgSlots != null
|
||||
|| expectedRetSlots != null) {
|
||||
throw new IllegalArgumentException("PUSH_CONST must not carry call metadata");
|
||||
}
|
||||
}
|
||||
case GET_LOCAL -> {
|
||||
if (expectedArgSlots == null) {
|
||||
throw new IllegalArgumentException("GET_LOCAL requires local slot in expectedArgSlots");
|
||||
}
|
||||
if (!label.isBlank()
|
||||
|| !targetLabel.isBlank()
|
||||
|| !calleeCallableName.isBlank()
|
||||
|| calleeCallableId != null
|
||||
|| hostCall != null
|
||||
|| intrinsicCall != null
|
||||
|| expectedRetSlots != null) {
|
||||
throw new IllegalArgumentException("GET_LOCAL must not carry call metadata");
|
||||
}
|
||||
}
|
||||
case CALL_FUNC -> {
|
||||
if (calleeCallableId == null) {
|
||||
throw new IllegalArgumentException("CALL_FUNC requires calleeCallableId");
|
||||
@ -199,6 +238,9 @@ public record IRBackendExecutableFunction(
|
||||
}
|
||||
|
||||
public enum InstructionKind {
|
||||
PUSH_I32,
|
||||
PUSH_CONST,
|
||||
GET_LOCAL,
|
||||
HALT,
|
||||
RET,
|
||||
CALL_FUNC,
|
||||
|
||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user