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 a1676c06..5519f363 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 @@ -36,9 +36,7 @@ public record IRVMProgram( if (emissionPlan.functions().isEmpty()) { return deriveEmissionPlan(module); } - if (containsRawSyscall(emissionPlan)) { - return emissionPlan; - } + validateCanonicalPlan(emissionPlan); validateCoherence(module, emissionPlan); return emissionPlan; } @@ -134,6 +132,19 @@ public record IRVMProgram( return false; } + private void validateCanonicalPlan(final BytecodeEmitter.EmissionPlan plan) { + for (final var function : plan.functions()) { + for (final var operation : function.operations()) { + if (operation.kind() == BytecodeEmitter.OperationKind.RAW_SYSCALL) { + throw new IllegalArgumentException("raw syscall operation is forbidden in canonical IRVM emission plan"); + } + if (operation.kind() == BytecodeEmitter.OperationKind.HOSTCALL && operation.syscallDecl() == null) { + throw new IllegalArgumentException("hostcall operation requires syscall declaration metadata"); + } + } + } + } + private BytecodeEmitter.Operation deriveOperation(final IRVMInstruction instruction) { final var immediate = instruction.immediate() == null ? 0 : instruction.immediate(); return switch (instruction.op().opcode()) { @@ -143,10 +154,8 @@ public record IRVMProgram( case 0x02 -> BytecodeEmitter.Operation.jmp(immediate, null); case 0x04 -> BytecodeEmitter.Operation.jmpIfTrue(immediate, null); case 0x03 -> BytecodeEmitter.Operation.jmpIfFalse(immediate, null); - case 0x71 -> BytecodeEmitter.Operation.hostcall( - new BytecodeModule.SyscallDecl("__unknown__", "__unknown__", 0, 0, 0), - null, - null); + case 0x71 -> throw new IllegalArgumentException( + "cannot derive HOSTCALL operation without canonical syscall metadata"); case 0x72 -> BytecodeEmitter.Operation.intrinsic(immediate); default -> throw new IllegalArgumentException("cannot derive emission op for irvm opcode: " + instruction.op().opcode()); }; diff --git a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/backend/irvm/IRVMProgramTest.java b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/backend/irvm/IRVMProgramTest.java index c5604a92..b0b13cc8 100644 --- a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/backend/irvm/IRVMProgramTest.java +++ b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/backend/irvm/IRVMProgramTest.java @@ -52,4 +52,38 @@ class IRVMProgramTest { assertEquals(1, plan.functions().size()); assertEquals(BytecodeEmitter.OperationKind.HALT, plan.functions().getFirst().operations().getFirst().kind()); } + + @Test + void coherentEmissionPlanMustRejectRawSyscallOperationInCanonicalPlan() { + final var plan = new BytecodeEmitter.EmissionPlan( + 0, + ReadOnlyList.empty(), + ReadOnlyList.empty(), + ReadOnlyList.from(new BytecodeEmitter.FunctionPlan( + "main", + 0, + 0, + 0, + 1, + ReadOnlyList.from(BytecodeEmitter.Operation.rawSyscall(0x1001))))); + final var program = new IRVMProgram(false, plan); + + assertThrows(IllegalArgumentException.class, program::coherentEmissionPlan); + } + + @Test + void coherentEmissionPlanMustRejectHostcallWithoutMetadataWhenDerived() { + final var module = new IRVMModule( + "core-v1", + ReadOnlyList.from(new IRVMFunction( + "main", + 0, + 0, + 0, + 1, + ReadOnlyList.from(new IRVMInstruction(IRVMOp.HOSTCALL, 0))))); + final var program = new IRVMProgram(module, BytecodeEmitter.EmissionPlan.empty()); + + assertThrows(IllegalArgumentException.class, program::coherentEmissionPlan); + } } diff --git a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/workspaces/stages/BackendSafetyGateSUTest.java b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/workspaces/stages/BackendSafetyGateSUTest.java index 80036d75..2b8f4f94 100644 --- a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/workspaces/stages/BackendSafetyGateSUTest.java +++ b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/workspaces/stages/BackendSafetyGateSUTest.java @@ -103,7 +103,7 @@ class BackendSafetyGateSUTest { final var issueA = runEmit(new IRVMProgram(false, plan)).asCollection().iterator().next(); final var issueB = runEmit(new IRVMProgram(false, plan)).asCollection().iterator().next(); - assertEquals("MARSHAL_LINKAGE_RAW_SYSCALL_IN_PRELOAD", issueA.getCode()); + assertEquals("MARSHAL_VERIFY_PRECHECK_IRVM_PROGRAM_COHERENCE", issueA.getCode()); assertEquals(issueA.getCode(), issueB.getCode()); assertEquals(issueA.getPhase(), issueB.getPhase()); }