From 2bc99cc9017d6e7eecbdea587ae24cbe55768c0a Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Mon, 9 Mar 2026 17:00:29 +0000 Subject: [PATCH] aritmetics --- .../compiler/pbs/PbsFrontendCompiler.java | 221 +++++++++++++++++- .../backend/bytecode/BytecodeEmitter.java | 185 +++++++++++++++ .../bytecode/BytecodeOpcodeLayout.java | 16 ++ .../studio/compiler/backend/irvm/IRVMOp.java | 18 ++ .../backend/irvm/IRVMProfileFeatureGate.java | 36 +++ .../compiler/backend/irvm/IRVMProgram.java | 54 +++++ .../backend/irvm/LowerToIRVMService.java | 111 +++++++++ .../models/IRBackendExecutableFunction.java | 51 +++- test-projects/main/cartridge/program.pbx | Bin 1624 -> 2734 bytes test-projects/main/src/main.pbs | 6 +- 10 files changed, 684 insertions(+), 14 deletions(-) diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/PbsFrontendCompiler.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/PbsFrontendCompiler.java index e5b71635..c0a73e13 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/PbsFrontendCompiler.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/PbsFrontendCompiler.java @@ -388,7 +388,8 @@ public final class PbsFrontendCompiler { callableSignatureByCallableId, returnSlotsByCallableId, intrinsicIdTable, - localSlotByNameId); + localSlotByNameId, + fn.parameters().size()); final var terminated = lowerBlock(fn.body(), loweringContext); final var instructions = loweringContext.instructions(); if (!terminated) { @@ -417,6 +418,7 @@ public final class PbsFrontendCompiler { } final var start = safeToInt(fn.span().getStart()); final var end = safeToInt(fn.span().getEnd()); + final var localSlots = Math.max(0, loweringContext.nextLocalSlot() - fn.parameters().size()); executableFunctions.add(new IRBackendExecutableFunction( fileId, normalizedModuleId, @@ -425,7 +427,7 @@ public final class PbsFrontendCompiler { start, end, fn.parameters().size(), - 0, + localSlots, returnSlots, maxStackSlots, ReadOnlyList.wrap(instructions), @@ -629,8 +631,39 @@ public final class PbsFrontendCompiler { return false; } switch (statement) { - case PbsAst.LetStatement letStatement -> lowerExpression(letStatement.initializer(), context); - case PbsAst.AssignStatement assignStatement -> lowerExpression(assignStatement.value(), context); + case PbsAst.LetStatement letStatement -> { + lowerExpression(letStatement.initializer(), context); + final var localSlot = context.declareLocalSlot(letStatement.name()); + emitSetLocal(localSlot, letStatement.span(), context); + } + case PbsAst.AssignStatement assignStatement -> { + final var target = assignStatement.target(); + if (target == null || target.rootName() == null || target.rootName().isBlank()) { + reportUnsupportedLowering("assignment target requires a local root name", assignStatement.span(), context); + return false; + } + if (target.pathSegments() != null && !target.pathSegments().isEmpty()) { + reportUnsupportedLowering("path assignment lowering is not supported in executable lowering v1", assignStatement.span(), context); + return false; + } + final var targetSlot = context.resolveLocalSlot(target.rootName()); + if (targetSlot == null) { + reportUnsupportedLowering("assignment target is not a known local: " + target.rootName(), assignStatement.span(), context); + return false; + } + switch (assignStatement.operator()) { + case ASSIGN -> { + lowerExpression(assignStatement.value(), context); + emitSetLocal(targetSlot, assignStatement.span(), context); + } + case ADD_ASSIGN, SUB_ASSIGN, MUL_ASSIGN, DIV_ASSIGN, MOD_ASSIGN -> { + emitGetLocal(targetSlot, assignStatement.span(), context); + lowerExpression(assignStatement.value(), context); + emitBinaryOperatorInstruction(compoundAssignBinaryOperator(assignStatement.operator()), assignStatement.span(), context); + emitSetLocal(targetSlot, assignStatement.span(), context); + } + } + } case PbsAst.ReturnStatement returnStatement -> { if (returnStatement.value() != null) { lowerExpression(returnStatement.value(), context); @@ -741,8 +774,12 @@ public final class PbsFrontendCompiler { case PbsAst.BinaryExpr binaryExpr -> { lowerExpression(binaryExpr.left(), context); lowerExpression(binaryExpr.right(), context); + emitBinaryOperatorInstruction(binaryExpr.operator(), binaryExpr.span(), context); + } + case PbsAst.UnaryExpr unaryExpr -> { + lowerExpression(unaryExpr.expression(), context); + emitUnaryOperatorInstruction(unaryExpr.operator(), unaryExpr.span(), context); } - case PbsAst.UnaryExpr unaryExpr -> lowerExpression(unaryExpr.expression(), context); case PbsAst.ElseExpr elseExpr -> { lowerExpression(elseExpr.optionalExpression(), context); lowerExpression(elseExpr.fallbackExpression(), context); @@ -809,8 +846,7 @@ public final class PbsFrontendCompiler { case PbsAst.BoundedLiteralExpr ignored -> { } case PbsAst.StringLiteralExpr stringLiteralExpr -> emitPushConst(stringLiteralExpr.value(), stringLiteralExpr.span(), context); - case PbsAst.BoolLiteralExpr ignored -> { - } + case PbsAst.BoolLiteralExpr boolLiteralExpr -> emitPushBool(boolLiteralExpr.value(), boolLiteralExpr.span(), context); case PbsAst.ThisExpr ignored -> { } case PbsAst.NoneExpr ignored -> { @@ -890,6 +926,7 @@ public final class PbsFrontendCompiler { return; } final var host = hostCandidates.getFirst(); + final var effectiveArgSlots = callExpr.arguments().size() + implicitReceiverArgSlots(callExpr.callee()); context.instructions().add(new IRBackendExecutableFunction.Instruction( IRBackendExecutableFunction.InstructionKind.CALL_HOST, "", @@ -897,7 +934,7 @@ public final class PbsFrontendCompiler { host.abiModule(), host.abiMethod(), host.abiVersion(), - callExpr.arguments().size(), + effectiveArgSlots, 0), null, callExpr.span())); @@ -939,6 +976,7 @@ public final class PbsFrontendCompiler { reportUnsupportedLowering("executable lowering resolved callable without signature metadata", callExpr.span(), context); return; } + final var effectiveArgSlots = callExpr.arguments().size() + implicitReceiverArgSlots(callExpr.callee()); context.instructions().add(new IRBackendExecutableFunction.Instruction( IRBackendExecutableFunction.InstructionKind.CALL_FUNC, calleeSignature.moduleId(), @@ -946,7 +984,7 @@ public final class PbsFrontendCompiler { calleeCallableId, null, null, - callExpr.arguments().size(), + effectiveArgSlots, context.returnSlotsByCallableId().get(calleeCallableId), callExpr.span())); } @@ -1139,6 +1177,24 @@ public final class PbsFrontendCompiler { span)); } + private void emitPushBool( + final boolean value, + final p.studio.compiler.source.Span span, + final ExecutableLoweringContext context) { + context.instructions().add(new IRBackendExecutableFunction.Instruction( + IRBackendExecutableFunction.InstructionKind.PUSH_BOOL, + ModuleId.none(), + "", + null, + null, + null, + value ? "true" : "false", + "", + null, + null, + span)); + } + private void emitPushConst( final String value, final p.studio.compiler.source.Span span, @@ -1157,6 +1213,102 @@ public final class PbsFrontendCompiler { span)); } + private void emitSetLocal( + final int slot, + final p.studio.compiler.source.Span span, + final ExecutableLoweringContext context) { + context.instructions().add(new IRBackendExecutableFunction.Instruction( + IRBackendExecutableFunction.InstructionKind.SET_LOCAL, + ModuleId.none(), + "", + null, + null, + null, + "", + "", + slot, + null, + span)); + } + + private String compoundAssignBinaryOperator(final PbsAst.AssignOperator operator) { + return switch (operator) { + case ADD_ASSIGN -> "+"; + case SUB_ASSIGN -> "-"; + case MUL_ASSIGN -> "*"; + case DIV_ASSIGN -> "/"; + case MOD_ASSIGN -> "%"; + case ASSIGN -> "="; + }; + } + + private void emitUnaryOperatorInstruction( + final String operator, + final p.studio.compiler.source.Span span, + final ExecutableLoweringContext context) { + final var normalized = operator == null ? "" : operator; + final IRBackendExecutableFunction.InstructionKind instructionKind = switch (normalized) { + case "-", "neg" -> IRBackendExecutableFunction.InstructionKind.NEG; + case "!", "not" -> IRBackendExecutableFunction.InstructionKind.NOT; + default -> null; + }; + if (instructionKind == null) { + reportUnsupportedLowering("unsupported unary operator in executable lowering: " + normalized, span, context); + return; + } + context.instructions().add(new IRBackendExecutableFunction.Instruction( + instructionKind, + ModuleId.none(), + "", + null, + null, + null, + "", + "", + null, + null, + span)); + } + + private void emitBinaryOperatorInstruction( + final String operator, + final p.studio.compiler.source.Span span, + final ExecutableLoweringContext context) { + final var normalized = operator == null ? "" : operator; + final IRBackendExecutableFunction.InstructionKind instructionKind = switch (normalized) { + case "+" -> IRBackendExecutableFunction.InstructionKind.ADD; + case "-" -> IRBackendExecutableFunction.InstructionKind.SUB; + case "*" -> IRBackendExecutableFunction.InstructionKind.MUL; + case "/" -> IRBackendExecutableFunction.InstructionKind.DIV; + case "%" -> IRBackendExecutableFunction.InstructionKind.MOD; + case "==" -> IRBackendExecutableFunction.InstructionKind.EQ; + case "!=" -> IRBackendExecutableFunction.InstructionKind.NEQ; + case "<" -> IRBackendExecutableFunction.InstructionKind.LT; + case "<=" -> IRBackendExecutableFunction.InstructionKind.LTE; + case ">" -> IRBackendExecutableFunction.InstructionKind.GT; + case ">=" -> IRBackendExecutableFunction.InstructionKind.GTE; + case "&&", "and" -> IRBackendExecutableFunction.InstructionKind.AND; + case "||", "or" -> IRBackendExecutableFunction.InstructionKind.OR; + default -> null; + }; + if (instructionKind == null) { + reportUnsupportedLowering("unsupported binary operator in executable lowering: " + normalized, span, context); + return; + } + context.instructions().add(new IRBackendExecutableFunction.Instruction( + instructionKind, + ModuleId.none(), + "", + null, + null, + null, + "", + "", + null, + null, + span)); + } + private void emitGetLocal( final int slot, final p.studio.compiler.source.Span span, @@ -1219,7 +1371,27 @@ public final class PbsFrontendCompiler { var outHeight = incomingHeightByInstruction.get(index); switch (instruction.kind()) { - case PUSH_I32, PUSH_CONST, GET_LOCAL -> outHeight += 1; + case PUSH_I32, PUSH_BOOL, PUSH_CONST, GET_LOCAL -> outHeight += 1; + case POP, SET_LOCAL -> { + if (outHeight <= 0) { + throw new ExecutableLoweringAnalysisException( + "stack underflow at " + instruction.kind().name().toLowerCase() + ": need=1 have=" + outHeight); + } + outHeight -= 1; + } + case ADD, SUB, MUL, DIV, MOD, EQ, NEQ, LT, LTE, GT, GTE, AND, OR -> { + if (outHeight < 2) { + throw new ExecutableLoweringAnalysisException( + "stack underflow at " + instruction.kind().name().toLowerCase() + ": need=2 have=" + outHeight); + } + outHeight -= 1; + } + case NOT, NEG -> { + if (outHeight <= 0) { + throw new ExecutableLoweringAnalysisException( + "stack underflow at " + instruction.kind().name().toLowerCase() + ": need=1 have=" + outHeight); + } + } case CALL_FUNC -> { final var argSlots = instruction.expectedArgSlots() == null ? 0 : instruction.expectedArgSlots(); final var retSlots = instruction.expectedRetSlots() == null ? 0 : instruction.expectedRetSlots(); @@ -1327,6 +1499,7 @@ public final class PbsFrontendCompiler { private final ArrayList instructions = new ArrayList<>(); private final ArrayDeque loopTargets = new ArrayDeque<>(); private int nextLabelId = 0; + private int nextLocalSlot; private ExecutableLoweringContext( final ModuleId moduleId, @@ -1340,7 +1513,8 @@ public final class PbsFrontendCompiler { final Map callableSignatureByCallableId, final Map returnSlotsByCallableId, final IntrinsicTable intrinsicIdTable, - final Map localSlotByNameId) { + final Map localSlotByNameId, + final int initialLocalSlot) { this.moduleId = moduleId == null ? ModuleId.none() : moduleId; this.diagnostics = diagnostics; this.nameTable = nameTable; @@ -1352,7 +1526,8 @@ public final class PbsFrontendCompiler { this.callableSignatureByCallableId = callableSignatureByCallableId; this.returnSlotsByCallableId = returnSlotsByCallableId; this.intrinsicIdTable = intrinsicIdTable; - this.localSlotByNameId = localSlotByNameId == null ? Map.of() : localSlotByNameId; + this.localSlotByNameId = localSlotByNameId == null ? new HashMap<>() : localSlotByNameId; + this.nextLocalSlot = Math.max(0, initialLocalSlot); } private ModuleId moduleId() { @@ -1403,6 +1578,28 @@ public final class PbsFrontendCompiler { return localSlotByNameId; } + private Integer resolveLocalSlot(final String localName) { + if (localName == null || localName.isBlank()) { + return null; + } + return localSlotByNameId.get(nameTable.register(localName)); + } + + private int declareLocalSlot(final String localName) { + final var nameId = nameTable.register(localName); + final var existing = localSlotByNameId.get(nameId); + if (existing != null) { + return existing; + } + final var slot = nextLocalSlot++; + localSlotByNameId.put(nameId, slot); + return slot; + } + + private int nextLocalSlot() { + return nextLocalSlot; + } + private ArrayList instructions() { return instructions; } diff --git a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/bytecode/BytecodeEmitter.java b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/bytecode/BytecodeEmitter.java index 15a158fe..36ebcdca 100644 --- a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/bytecode/BytecodeEmitter.java +++ b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/bytecode/BytecodeEmitter.java @@ -18,7 +18,25 @@ public class BytecodeEmitter { 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_POP = 0x11; + private static final int OP_PUSH_BOOL = 0x16; + private static final int OP_ADD = 0x20; + private static final int OP_SUB = 0x21; + private static final int OP_MUL = 0x22; + private static final int OP_DIV = 0x23; + private static final int OP_MOD = 0x24; + private static final int OP_EQ = 0x30; + private static final int OP_NEQ = 0x31; + private static final int OP_LT = 0x32; + private static final int OP_GT = 0x33; + private static final int OP_AND = 0x34; + private static final int OP_OR = 0x35; + private static final int OP_NOT = 0x36; + private static final int OP_LTE = 0x3C; + private static final int OP_GTE = 0x3D; + private static final int OP_NEG = 0x3E; private static final int OP_GET_LOCAL = 0x42; + private static final int OP_SET_LOCAL = 0x43; private static final int OP_PUSH_I32 = 0x17; private static final int OP_HOSTCALL = 0x71; private static final int OP_SYSCALL = 0x70; @@ -55,14 +73,86 @@ public class BytecodeEmitter { writeOpU32(code, OP_PUSH_CONST, op.immediate()); spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, spanOrUnknown(op.span()))); } + case POP -> { + writeOpNoImm(code, OP_POP); + 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 SET_LOCAL -> { + writeOpU32(code, OP_SET_LOCAL, op.immediate()); + spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, spanOrUnknown(op.span()))); + } + case PUSH_BOOL -> { + writeOpU8(code, OP_PUSH_BOOL, op.immediate() == 0 ? 0 : 1); + 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 ADD -> { + writeOpNoImm(code, OP_ADD); + spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, spanOrUnknown(op.span()))); + } + case SUB -> { + writeOpNoImm(code, OP_SUB); + spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, spanOrUnknown(op.span()))); + } + case MUL -> { + writeOpNoImm(code, OP_MUL); + spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, spanOrUnknown(op.span()))); + } + case DIV -> { + writeOpNoImm(code, OP_DIV); + spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, spanOrUnknown(op.span()))); + } + case MOD -> { + writeOpNoImm(code, OP_MOD); + spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, spanOrUnknown(op.span()))); + } + case EQ -> { + writeOpNoImm(code, OP_EQ); + spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, spanOrUnknown(op.span()))); + } + case NEQ -> { + writeOpNoImm(code, OP_NEQ); + spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, spanOrUnknown(op.span()))); + } + case LT -> { + writeOpNoImm(code, OP_LT); + spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, spanOrUnknown(op.span()))); + } + case LTE -> { + writeOpNoImm(code, OP_LTE); + spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, spanOrUnknown(op.span()))); + } + case GT -> { + writeOpNoImm(code, OP_GT); + spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, spanOrUnknown(op.span()))); + } + case GTE -> { + writeOpNoImm(code, OP_GTE); + spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, spanOrUnknown(op.span()))); + } + case AND -> { + writeOpNoImm(code, OP_AND); + spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, spanOrUnknown(op.span()))); + } + case OR -> { + writeOpNoImm(code, OP_OR); + spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, spanOrUnknown(op.span()))); + } + case NOT -> { + writeOpNoImm(code, OP_NOT); + spans.add(new BytecodeFunctionLayoutBuilder.InstructionSpan(pc, spanOrUnknown(op.span()))); + } + case NEG -> { + writeOpNoImm(code, OP_NEG); + 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()))); @@ -136,6 +226,11 @@ public class BytecodeEmitter { out.writeBytes(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(immediate).array()); } + private static void writeOpU8(final ByteArrayOutputStream out, final int opcode, final int immediate) { + out.writeBytes(ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN).putShort((short) opcode).array()); + out.write(immediate & 0xFF); + } + private static BytecodeModule.SourceSpan spanOrUnknown(final BytecodeModule.SourceSpan span) { if (span == null) { return new BytecodeModule.SourceSpan(-1, 0, 0); @@ -210,8 +305,26 @@ public class BytecodeEmitter { HALT, RET, PUSH_CONST, + POP, GET_LOCAL, + SET_LOCAL, + PUSH_BOOL, PUSH_I32, + ADD, + SUB, + MUL, + DIV, + MOD, + EQ, + NEQ, + LT, + LTE, + GT, + GTE, + AND, + OR, + NOT, + NEG, CALL_FUNC, JMP, JMP_IF_TRUE, @@ -276,14 +389,86 @@ public class BytecodeEmitter { return new Operation(OperationKind.PUSH_CONST, constPoolIndex, null, null, null, span); } + public static Operation pop(final BytecodeModule.SourceSpan span) { + return new Operation(OperationKind.POP, 0, 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 setLocal(final int slot, final BytecodeModule.SourceSpan span) { + return new Operation(OperationKind.SET_LOCAL, slot, null, null, null, span); + } + + public static Operation pushBool(final boolean value, final BytecodeModule.SourceSpan span) { + return new Operation(OperationKind.PUSH_BOOL, value ? 1 : 0, 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 add(final BytecodeModule.SourceSpan span) { + return new Operation(OperationKind.ADD, 0, null, null, null, span); + } + + public static Operation sub(final BytecodeModule.SourceSpan span) { + return new Operation(OperationKind.SUB, 0, null, null, null, span); + } + + public static Operation mul(final BytecodeModule.SourceSpan span) { + return new Operation(OperationKind.MUL, 0, null, null, null, span); + } + + public static Operation div(final BytecodeModule.SourceSpan span) { + return new Operation(OperationKind.DIV, 0, null, null, null, span); + } + + public static Operation mod(final BytecodeModule.SourceSpan span) { + return new Operation(OperationKind.MOD, 0, null, null, null, span); + } + + public static Operation eq(final BytecodeModule.SourceSpan span) { + return new Operation(OperationKind.EQ, 0, null, null, null, span); + } + + public static Operation neq(final BytecodeModule.SourceSpan span) { + return new Operation(OperationKind.NEQ, 0, null, null, null, span); + } + + public static Operation lt(final BytecodeModule.SourceSpan span) { + return new Operation(OperationKind.LT, 0, null, null, null, span); + } + + public static Operation lte(final BytecodeModule.SourceSpan span) { + return new Operation(OperationKind.LTE, 0, null, null, null, span); + } + + public static Operation gt(final BytecodeModule.SourceSpan span) { + return new Operation(OperationKind.GT, 0, null, null, null, span); + } + + public static Operation gte(final BytecodeModule.SourceSpan span) { + return new Operation(OperationKind.GTE, 0, null, null, null, span); + } + + public static Operation and(final BytecodeModule.SourceSpan span) { + return new Operation(OperationKind.AND, 0, null, null, null, span); + } + + public static Operation or(final BytecodeModule.SourceSpan span) { + return new Operation(OperationKind.OR, 0, null, null, null, span); + } + + public static Operation not(final BytecodeModule.SourceSpan span) { + return new Operation(OperationKind.NOT, 0, null, null, null, span); + } + + public static Operation neg(final BytecodeModule.SourceSpan span) { + return new Operation(OperationKind.NEG, 0, null, null, null, span); + } + public static Operation hostcall( final BytecodeModule.SyscallDecl decl, final Integer expectedArgSlots, diff --git a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/bytecode/BytecodeOpcodeLayout.java b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/bytecode/BytecodeOpcodeLayout.java index 4cf19b75..b0cf78ae 100644 --- a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/bytecode/BytecodeOpcodeLayout.java +++ b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/bytecode/BytecodeOpcodeLayout.java @@ -9,11 +9,27 @@ final class BytecodeOpcodeLayout { Map.entry(0x03, 6), Map.entry(0x04, 6), Map.entry(0x10, 6), + Map.entry(0x11, 2), Map.entry(0x14, 10), Map.entry(0x15, 10), Map.entry(0x16, 3), Map.entry(0x17, 6), Map.entry(0x18, 6), + Map.entry(0x20, 2), + Map.entry(0x21, 2), + Map.entry(0x22, 2), + Map.entry(0x23, 2), + Map.entry(0x24, 2), + Map.entry(0x30, 2), + Map.entry(0x31, 2), + Map.entry(0x32, 2), + Map.entry(0x33, 2), + Map.entry(0x34, 2), + Map.entry(0x35, 2), + Map.entry(0x36, 2), + Map.entry(0x3C, 2), + Map.entry(0x3D, 2), + Map.entry(0x3E, 2), Map.entry(0x40, 6), Map.entry(0x41, 6), Map.entry(0x42, 6), diff --git a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/IRVMOp.java b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/IRVMOp.java index 4c95747d..93fecbce 100644 --- a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/IRVMOp.java +++ b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/IRVMOp.java @@ -13,13 +13,31 @@ 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 POP = new IRVMOp("POP", 0x11, 0, 1, 0, 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); public static final IRVMOp JMP_IF_FALSE = new IRVMOp("JMP_IF_FALSE", 0x03, 4, 1, 0, true, false, false); 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_BOOL = new IRVMOp("PUSH_BOOL", 0x16, 1, 0, 1, false, false, false); public static final IRVMOp PUSH_I32 = new IRVMOp("PUSH_I32", 0x17, 4, 0, 1, false, false, false); + public static final IRVMOp ADD = new IRVMOp("ADD", 0x20, 0, 2, 1, false, false, false); + public static final IRVMOp SUB = new IRVMOp("SUB", 0x21, 0, 2, 1, false, false, false); + public static final IRVMOp MUL = new IRVMOp("MUL", 0x22, 0, 2, 1, false, false, false); + public static final IRVMOp DIV = new IRVMOp("DIV", 0x23, 0, 2, 1, false, false, false); + public static final IRVMOp MOD = new IRVMOp("MOD", 0x24, 0, 2, 1, false, false, false); + public static final IRVMOp EQ = new IRVMOp("EQ", 0x30, 0, 2, 1, false, false, false); + public static final IRVMOp NEQ = new IRVMOp("NEQ", 0x31, 0, 2, 1, false, false, false); + public static final IRVMOp LT = new IRVMOp("LT", 0x32, 0, 2, 1, false, false, false); + public static final IRVMOp GT = new IRVMOp("GT", 0x33, 0, 2, 1, false, false, false); + public static final IRVMOp AND = new IRVMOp("AND", 0x34, 0, 2, 1, false, false, false); + public static final IRVMOp OR = new IRVMOp("OR", 0x35, 0, 2, 1, false, false, false); + public static final IRVMOp NOT = new IRVMOp("NOT", 0x36, 0, 1, 1, false, false, false); + public static final IRVMOp LTE = new IRVMOp("LTE", 0x3C, 0, 2, 1, false, false, false); + public static final IRVMOp GTE = new IRVMOp("GTE", 0x3D, 0, 2, 1, false, false, false); + public static final IRVMOp NEG = new IRVMOp("NEG", 0x3E, 0, 1, 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 SET_LOCAL = new IRVMOp("SET_LOCAL", 0x43, 4, 1, 0, false, false, false); public static final IRVMOp INTERNAL_EXT = new IRVMOp("IRVM_EXT_NOP", 0xFFFF, 0, 0, 0, false, false, true); } diff --git a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/IRVMProfileFeatureGate.java b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/IRVMProfileFeatureGate.java index 1f2f3d6c..213820eb 100644 --- a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/IRVMProfileFeatureGate.java +++ b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/IRVMProfileFeatureGate.java @@ -12,25 +12,61 @@ public final class IRVMProfileFeatureGate { IRVMOp.HALT, IRVMOp.RET, IRVMOp.PUSH_CONST, + IRVMOp.POP, IRVMOp.GET_LOCAL, + IRVMOp.SET_LOCAL, IRVMOp.CALL, IRVMOp.JMP, IRVMOp.JMP_IF_TRUE, IRVMOp.JMP_IF_FALSE, IRVMOp.HOSTCALL, IRVMOp.INTRINSIC, + IRVMOp.PUSH_BOOL, + IRVMOp.ADD, + IRVMOp.SUB, + IRVMOp.MUL, + IRVMOp.DIV, + IRVMOp.MOD, + IRVMOp.EQ, + IRVMOp.NEQ, + IRVMOp.LT, + IRVMOp.LTE, + IRVMOp.GT, + IRVMOp.GTE, + IRVMOp.AND, + IRVMOp.OR, + IRVMOp.NOT, + IRVMOp.NEG, IRVMOp.PUSH_I32), "experimental-v1", Set.of( IRVMOp.HALT, IRVMOp.RET, IRVMOp.PUSH_CONST, + IRVMOp.POP, IRVMOp.GET_LOCAL, + IRVMOp.SET_LOCAL, IRVMOp.CALL, IRVMOp.JMP, IRVMOp.JMP_IF_TRUE, IRVMOp.JMP_IF_FALSE, IRVMOp.HOSTCALL, IRVMOp.INTRINSIC, + IRVMOp.PUSH_BOOL, + IRVMOp.ADD, + IRVMOp.SUB, + IRVMOp.MUL, + IRVMOp.DIV, + IRVMOp.MOD, + IRVMOp.EQ, + IRVMOp.NEQ, + IRVMOp.LT, + IRVMOp.LTE, + IRVMOp.GT, + IRVMOp.GTE, + IRVMOp.AND, + IRVMOp.OR, + IRVMOp.NOT, + IRVMOp.NEG, IRVMOp.PUSH_I32, IRVMOp.INTERNAL_EXT))); } diff --git a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/IRVMProgram.java b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/IRVMProgram.java index fb53c3cd..b62bf3f4 100644 --- a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/IRVMProgram.java +++ b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/IRVMProgram.java @@ -93,8 +93,26 @@ public record IRVMProgram( 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 0x11 -> operation.kind() == BytecodeEmitter.OperationKind.POP; + case 0x16 -> operation.kind() == BytecodeEmitter.OperationKind.PUSH_BOOL; case 0x42 -> operation.kind() == BytecodeEmitter.OperationKind.GET_LOCAL && operation.immediate() == immediate; + case 0x43 -> operation.kind() == BytecodeEmitter.OperationKind.SET_LOCAL && operation.immediate() == immediate; case 0x17 -> operation.kind() == BytecodeEmitter.OperationKind.PUSH_I32 && operation.immediate() == immediate; + case 0x20 -> operation.kind() == BytecodeEmitter.OperationKind.ADD; + case 0x21 -> operation.kind() == BytecodeEmitter.OperationKind.SUB; + case 0x22 -> operation.kind() == BytecodeEmitter.OperationKind.MUL; + case 0x23 -> operation.kind() == BytecodeEmitter.OperationKind.DIV; + case 0x24 -> operation.kind() == BytecodeEmitter.OperationKind.MOD; + case 0x30 -> operation.kind() == BytecodeEmitter.OperationKind.EQ; + case 0x31 -> operation.kind() == BytecodeEmitter.OperationKind.NEQ; + case 0x32 -> operation.kind() == BytecodeEmitter.OperationKind.LT; + case 0x33 -> operation.kind() == BytecodeEmitter.OperationKind.GT; + case 0x34 -> operation.kind() == BytecodeEmitter.OperationKind.AND; + case 0x35 -> operation.kind() == BytecodeEmitter.OperationKind.OR; + case 0x36 -> operation.kind() == BytecodeEmitter.OperationKind.NOT; + case 0x3C -> operation.kind() == BytecodeEmitter.OperationKind.LTE; + case 0x3D -> operation.kind() == BytecodeEmitter.OperationKind.GTE; + case 0x3E -> operation.kind() == BytecodeEmitter.OperationKind.NEG; 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; @@ -157,8 +175,26 @@ public record IRVMProgram( case 0x01 -> BytecodeEmitter.Operation.halt(); case 0x51 -> BytecodeEmitter.Operation.ret(); case 0x10 -> BytecodeEmitter.Operation.pushConst(immediate, null); + case 0x11 -> BytecodeEmitter.Operation.pop(null); + case 0x16 -> BytecodeEmitter.Operation.pushBool(immediate != 0, null); case 0x42 -> BytecodeEmitter.Operation.getLocal(immediate, null); + case 0x43 -> BytecodeEmitter.Operation.setLocal(immediate, null); case 0x17 -> BytecodeEmitter.Operation.pushI32(immediate, null); + case 0x20 -> BytecodeEmitter.Operation.add(null); + case 0x21 -> BytecodeEmitter.Operation.sub(null); + case 0x22 -> BytecodeEmitter.Operation.mul(null); + case 0x23 -> BytecodeEmitter.Operation.div(null); + case 0x24 -> BytecodeEmitter.Operation.mod(null); + case 0x30 -> BytecodeEmitter.Operation.eq(null); + case 0x31 -> BytecodeEmitter.Operation.neq(null); + case 0x32 -> BytecodeEmitter.Operation.lt(null); + case 0x33 -> BytecodeEmitter.Operation.gt(null); + case 0x34 -> BytecodeEmitter.Operation.and(null); + case 0x35 -> BytecodeEmitter.Operation.or(null); + case 0x36 -> BytecodeEmitter.Operation.not(null); + case 0x3C -> BytecodeEmitter.Operation.lte(null); + case 0x3D -> BytecodeEmitter.Operation.gte(null); + case 0x3E -> BytecodeEmitter.Operation.neg(null); case 0x50 -> BytecodeEmitter.Operation.callFunc(immediate); case 0x02 -> BytecodeEmitter.Operation.jmp(immediate, null); case 0x04 -> BytecodeEmitter.Operation.jmpIfTrue(immediate, null); @@ -211,8 +247,26 @@ public record IRVMProgram( 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 POP -> new IRVMInstruction(IRVMOp.POP, null); + case PUSH_BOOL -> new IRVMInstruction(IRVMOp.PUSH_BOOL, operation.immediate()); case GET_LOCAL -> new IRVMInstruction(IRVMOp.GET_LOCAL, operation.immediate()); + case SET_LOCAL -> new IRVMInstruction(IRVMOp.SET_LOCAL, operation.immediate()); case PUSH_I32 -> new IRVMInstruction(IRVMOp.PUSH_I32, operation.immediate()); + case ADD -> new IRVMInstruction(IRVMOp.ADD, null); + case SUB -> new IRVMInstruction(IRVMOp.SUB, null); + case MUL -> new IRVMInstruction(IRVMOp.MUL, null); + case DIV -> new IRVMInstruction(IRVMOp.DIV, null); + case MOD -> new IRVMInstruction(IRVMOp.MOD, null); + case EQ -> new IRVMInstruction(IRVMOp.EQ, null); + case NEQ -> new IRVMInstruction(IRVMOp.NEQ, null); + case LT -> new IRVMInstruction(IRVMOp.LT, null); + case LTE -> new IRVMInstruction(IRVMOp.LTE, null); + case GT -> new IRVMInstruction(IRVMOp.GT, null); + case GTE -> new IRVMInstruction(IRVMOp.GTE, null); + case AND -> new IRVMInstruction(IRVMOp.AND, null); + case OR -> new IRVMInstruction(IRVMOp.OR, null); + case NOT -> new IRVMInstruction(IRVMOp.NOT, null); + case NEG -> new IRVMInstruction(IRVMOp.NEG, null); 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()); diff --git a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/LowerToIRVMService.java b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/LowerToIRVMService.java index 1a6b7292..1f7795c0 100644 --- a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/LowerToIRVMService.java +++ b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/LowerToIRVMService.java @@ -90,6 +90,24 @@ public class LowerToIRVMService { operations.add(BytecodeEmitter.Operation.pushConst(constPoolIndex, sourceSpan)); functionPc += IRVMOp.PUSH_CONST.immediateSize() + 2; } + case PUSH_BOOL -> { + final var raw = instr.label() == null ? "" : instr.label().trim().toLowerCase(); + final Integer value = switch (raw) { + case "1", "true" -> 1; + case "0", "false" -> 0; + default -> null; + }; + if (value == null) { + throw loweringError( + fn, + instr, + IRVMLoweringErrorCode.LOWER_IRVM_INVALID_INTRINSIC_ID, + "invalid PUSH_BOOL literal payload: " + instr.label()); + } + instructions.add(new IRVMInstruction(IRVMOp.PUSH_BOOL, value)); + operations.add(BytecodeEmitter.Operation.pushBool(value != 0, sourceSpan)); + functionPc += IRVMOp.PUSH_BOOL.immediateSize() + 2; + } case GET_LOCAL -> { final var slot = instr.expectedArgSlots(); if (slot == null) { @@ -103,6 +121,99 @@ public class LowerToIRVMService { operations.add(BytecodeEmitter.Operation.getLocal(slot, sourceSpan)); functionPc += IRVMOp.GET_LOCAL.immediateSize() + 2; } + case SET_LOCAL -> { + final var slot = instr.expectedArgSlots(); + if (slot == null) { + throw loweringError( + fn, + instr, + IRVMLoweringErrorCode.LOWER_IRVM_MISSING_CALLEE, + "missing local slot for SET_LOCAL"); + } + instructions.add(new IRVMInstruction(IRVMOp.SET_LOCAL, slot)); + operations.add(BytecodeEmitter.Operation.setLocal(slot, sourceSpan)); + functionPc += IRVMOp.SET_LOCAL.immediateSize() + 2; + } + case POP -> { + instructions.add(new IRVMInstruction(IRVMOp.POP, null)); + operations.add(BytecodeEmitter.Operation.pop(sourceSpan)); + functionPc += IRVMOp.POP.immediateSize() + 2; + } + case ADD -> { + instructions.add(new IRVMInstruction(IRVMOp.ADD, null)); + operations.add(BytecodeEmitter.Operation.add(sourceSpan)); + functionPc += IRVMOp.ADD.immediateSize() + 2; + } + case SUB -> { + instructions.add(new IRVMInstruction(IRVMOp.SUB, null)); + operations.add(BytecodeEmitter.Operation.sub(sourceSpan)); + functionPc += IRVMOp.SUB.immediateSize() + 2; + } + case MUL -> { + instructions.add(new IRVMInstruction(IRVMOp.MUL, null)); + operations.add(BytecodeEmitter.Operation.mul(sourceSpan)); + functionPc += IRVMOp.MUL.immediateSize() + 2; + } + case DIV -> { + instructions.add(new IRVMInstruction(IRVMOp.DIV, null)); + operations.add(BytecodeEmitter.Operation.div(sourceSpan)); + functionPc += IRVMOp.DIV.immediateSize() + 2; + } + case MOD -> { + instructions.add(new IRVMInstruction(IRVMOp.MOD, null)); + operations.add(BytecodeEmitter.Operation.mod(sourceSpan)); + functionPc += IRVMOp.MOD.immediateSize() + 2; + } + case EQ -> { + instructions.add(new IRVMInstruction(IRVMOp.EQ, null)); + operations.add(BytecodeEmitter.Operation.eq(sourceSpan)); + functionPc += IRVMOp.EQ.immediateSize() + 2; + } + case NEQ -> { + instructions.add(new IRVMInstruction(IRVMOp.NEQ, null)); + operations.add(BytecodeEmitter.Operation.neq(sourceSpan)); + functionPc += IRVMOp.NEQ.immediateSize() + 2; + } + case LT -> { + instructions.add(new IRVMInstruction(IRVMOp.LT, null)); + operations.add(BytecodeEmitter.Operation.lt(sourceSpan)); + functionPc += IRVMOp.LT.immediateSize() + 2; + } + case LTE -> { + instructions.add(new IRVMInstruction(IRVMOp.LTE, null)); + operations.add(BytecodeEmitter.Operation.lte(sourceSpan)); + functionPc += IRVMOp.LTE.immediateSize() + 2; + } + case GT -> { + instructions.add(new IRVMInstruction(IRVMOp.GT, null)); + operations.add(BytecodeEmitter.Operation.gt(sourceSpan)); + functionPc += IRVMOp.GT.immediateSize() + 2; + } + case GTE -> { + instructions.add(new IRVMInstruction(IRVMOp.GTE, null)); + operations.add(BytecodeEmitter.Operation.gte(sourceSpan)); + functionPc += IRVMOp.GTE.immediateSize() + 2; + } + case AND -> { + instructions.add(new IRVMInstruction(IRVMOp.AND, null)); + operations.add(BytecodeEmitter.Operation.and(sourceSpan)); + functionPc += IRVMOp.AND.immediateSize() + 2; + } + case OR -> { + instructions.add(new IRVMInstruction(IRVMOp.OR, null)); + operations.add(BytecodeEmitter.Operation.or(sourceSpan)); + functionPc += IRVMOp.OR.immediateSize() + 2; + } + case NOT -> { + instructions.add(new IRVMInstruction(IRVMOp.NOT, null)); + operations.add(BytecodeEmitter.Operation.not(sourceSpan)); + functionPc += IRVMOp.NOT.immediateSize() + 2; + } + case NEG -> { + instructions.add(new IRVMInstruction(IRVMOp.NEG, null)); + operations.add(BytecodeEmitter.Operation.neg(sourceSpan)); + functionPc += IRVMOp.NEG.immediateSize() + 2; + } case HALT -> { instructions.add(new IRVMInstruction(IRVMOp.HALT, null)); operations.add(BytecodeEmitter.Operation.halt(sourceSpan)); diff --git a/prometeu-compiler/prometeu-frontend-api/src/main/java/p/studio/compiler/models/IRBackendExecutableFunction.java b/prometeu-compiler/prometeu-frontend-api/src/main/java/p/studio/compiler/models/IRBackendExecutableFunction.java index c05867f6..d0c3053f 100644 --- a/prometeu-compiler/prometeu-frontend-api/src/main/java/p/studio/compiler/models/IRBackendExecutableFunction.java +++ b/prometeu-compiler/prometeu-frontend-api/src/main/java/p/studio/compiler/models/IRBackendExecutableFunction.java @@ -232,6 +232,20 @@ public record IRBackendExecutableFunction( throw new IllegalArgumentException("PUSH_I32 must not carry call metadata"); } } + case PUSH_BOOL -> { + if (label.isBlank()) { + throw new IllegalArgumentException("PUSH_BOOL requires immediate label payload"); + } + if (!targetLabel.isBlank() + || !calleeCallableName.isBlank() + || calleeCallableId != null + || hostCall != null + || intrinsicCall != null + || expectedArgSlots != null + || expectedRetSlots != null) { + throw new IllegalArgumentException("PUSH_BOOL must not carry call metadata"); + } + } case PUSH_CONST -> { if (!targetLabel.isBlank() || !calleeCallableName.isBlank() @@ -257,6 +271,20 @@ public record IRBackendExecutableFunction( throw new IllegalArgumentException("GET_LOCAL must not carry call metadata"); } } + case SET_LOCAL -> { + if (expectedArgSlots == null) { + throw new IllegalArgumentException("SET_LOCAL requires local slot in expectedArgSlots"); + } + if (!label.isBlank() + || !targetLabel.isBlank() + || !calleeCallableName.isBlank() + || calleeCallableId != null + || hostCall != null + || intrinsicCall != null + || expectedRetSlots != null) { + throw new IllegalArgumentException("SET_LOCAL must not carry call metadata"); + } + } case CALL_FUNC -> { if (calleeCallableId == null) { throw new IllegalArgumentException("CALL_FUNC requires calleeCallableId"); @@ -287,10 +315,13 @@ public record IRBackendExecutableFunction( throw new IllegalArgumentException("CALL_INTRINSIC must not carry host metadata"); } } - case HALT, RET -> { + case HALT, RET, POP, ADD, SUB, MUL, DIV, MOD, EQ, NEQ, LT, LTE, GT, GTE, AND, OR, NOT, NEG -> { if (!calleeCallableName.isBlank() || calleeCallableId != null || hostCall != null || intrinsicCall != null) { throw new IllegalArgumentException(kind + " must not carry callsite metadata"); } + if (!label.isBlank() || !targetLabel.isBlank()) { + throw new IllegalArgumentException(kind + " must not carry label metadata"); + } if (expectedArgSlots != null || expectedRetSlots != null) { throw new IllegalArgumentException(kind + " must not carry expected slot metadata"); } @@ -329,8 +360,26 @@ public record IRBackendExecutableFunction( public enum InstructionKind { PUSH_I32, + PUSH_BOOL, PUSH_CONST, GET_LOCAL, + SET_LOCAL, + POP, + ADD, + SUB, + MUL, + DIV, + MOD, + EQ, + NEQ, + LT, + LTE, + GT, + GTE, + AND, + OR, + NOT, + NEG, HALT, RET, CALL_FUNC, diff --git a/test-projects/main/cartridge/program.pbx b/test-projects/main/cartridge/program.pbx index 7b0d05893f00bed0f09b24ccd196d4ceb9e1f059..32ba652d81e5bd503d4f3c23cde70b43d72462a4 100644 GIT binary patch literal 2734 zcmZ{mU5J!b7{`yZV~!T?hohsh*=$*fgbaq&MM!O4h!jj8h_0*atUJ!mvOCkh&AKH~ zDCUBBAzoBO7ZuuvMS^T1S!f@`reIx!7bO%+7g8Wy#1gbtzyF!%b!QLu;QyTG_dL%z z?|ILecaQnbr<`oAZ*Sam6E2}S=qogi5@?h_3r-XM7|q`|)#ur_z`1*gDW1&G+?UIi zpUu|#i`k(GbQ)lGl18=dxLU&}jL>1xpB_iRD@!V8na${ zF<+CnF4`ixrH(pXS3_;a1N0+ksqq2&F{Ja69Yn976#`6ovA!_b9&`xlla;-S-bOk( znfMrbP=JX~pmhT52m46KgHT-net-oxvU?*lyL6* zI=aK~2k4UV82)i|**LDBG(Jo2YjnkU0{=YH8OW3u|4~PG24BycGX535hNg{w#Qz5+ zkSwlG882e&Qnb*x9e+7WH{``jjGOWGymsRU@O#i5#;fsnpq0jt@XRu-`-0BteZECG z;|uf`k}?*yU=>$Zu}m!(YS!$hji|$-+*64n~WFZ4*Z6b%bLb=E zqxfH-lg1JL1k#9zKA#`-p1M&B53px4@U&Kf^Wa}DW?#sAVInEwak zB>p_4->LGy)1=^Ej8DVO@T74UycJzB-iE&eT{V6jzkvQUz6-w}O&Pbc*KKIpIE|k{ z32OM~mf^Rfl(DY3+t5Pei>ze@9REK^xBITBFjQnde~CU;Ev@b=_KxTynH^x{^*}L- zsu9!74rsCz(j4W=uBtAjMxNI&pcWN&7M0Y^tpPqDh)0YTqRN7RwWuaHSS`(UqfxoW Ss;@`p*CRc(LTL^)&iw~;W&%Y3 literal 1624 zcmZvcNoZ416ozkx+QEW0q@;>Nh1P|LsJIYUnuTOhp$;I%);5}Z9BaJ}AI*@IyC+RtS){QTRcSeKs!xj?y1&Ws0S8Ij8fmSnlLwp$InadC_K}duVhjHi-VO)bduug<=3GRa2TN!)cA;`NT*E+)+^$z6a z68}J+h3)E7Wz;E?c|Un1=*|#WRGk void { total += 5; } + else if (Input.pad().x().pressed()) + { + total -= 10; + } if (total == 30) { @@ -24,7 +28,7 @@ fn frame() -> void { Log.error("50 is the magic number!"); } - else + else if (total == 15) { Log.warn("The magic number is neither 30 nor 50!"); }