From 8876a8fa35007c242d25261ecab5a13bfa7745fa Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Mon, 9 Mar 2026 16:10:07 +0000 Subject: [PATCH] implements PR-09.3: enforce qualified entrypoint and moduleId-only ordering in LowerToIRVM --- .../backend/irvm/LowerToIRVMService.java | 46 +++++++------------ .../compiler/backend/GoldenArtifactsTest.java | 2 + .../backend/irvm/LowerToIRVMServiceTest.java | 13 ++++++ .../OptimizeIRVMEquivalenceHarnessTest.java | 2 + .../BackendGateIIntegrationTest.java | 2 + .../stages/BackendSafetyGateSUTest.java | 4 ++ .../stages/LowerToIRVMPipelineStageTest.java | 9 ++++ 7 files changed, 48 insertions(+), 30 deletions(-) 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 075f0c87..1a6b7292 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 @@ -297,6 +297,11 @@ public class LowerToIRVMService { "frontend IRBackend entrypoint declaration is missing"); } final var entryPointModuleId = backend.getEntryPointModuleId(); + if (entryPointModuleId == null || entryPointModuleId.isNone()) { + throw new IRVMLoweringException( + IRVMLoweringErrorCode.LOWER_IRVM_ENTRYPOINT_DECLARATION_MISSING, + "frontend IRBackend qualified entrypoint declaration is missing entryPointModuleId"); + } final var sorted = new ArrayList<>(backend.getExecutableFunctions().asList()); sorted.sort((left, right) -> { final var leftModuleKey = moduleSortKey(backend, left.moduleId()); @@ -315,44 +320,25 @@ public class LowerToIRVMService { } return Integer.compare(left.sourceStart(), right.sourceStart()); }); - final java.util.List entrypoints; - if (entryPointModuleId != null && !entryPointModuleId.isNone()) { - final var expectedModuleIndex = entryPointModuleId.getIndex(); - entrypoints = sorted.stream() - .filter(candidate -> entryPointCallableName.equals(candidate.callableName())) - .filter(candidate -> !candidate.moduleId().isNone() && candidate.moduleId().getIndex() == expectedModuleIndex) - .toList(); - } else { - entrypoints = sorted.stream() - .filter(candidate -> entryPointCallableName.equals(candidate.callableName())) - .toList(); - } + final var expectedModuleIndex = entryPointModuleId.getIndex(); + final var entrypoints = sorted.stream() + .filter(candidate -> entryPointCallableName.equals(candidate.callableName())) + .filter(candidate -> !candidate.moduleId().isNone() && candidate.moduleId().getIndex() == expectedModuleIndex) + .toList(); if (entrypoints.isEmpty()) { - if (entryPointModuleId != null && !entryPointModuleId.isNone()) { - throw new IRVMLoweringException( - IRVMLoweringErrorCode.LOWER_IRVM_ENTRYPOINT_MISSING, - "missing qualified entrypoint callable '%s' for moduleId=%d".formatted( - entryPointCallableName, - entryPointModuleId.getIndex())); - } throw new IRVMLoweringException( IRVMLoweringErrorCode.LOWER_IRVM_ENTRYPOINT_MISSING, - "missing entrypoint callable '%s'".formatted(entryPointCallableName)); + "missing qualified entrypoint callable '%s' for moduleId=%d".formatted( + entryPointCallableName, + entryPointModuleId.getIndex())); } if (entrypoints.size() > 1) { - if (entryPointModuleId != null && !entryPointModuleId.isNone()) { - throw new IRVMLoweringException( - IRVMLoweringErrorCode.LOWER_IRVM_ENTRYPOINT_AMBIGUOUS, - "ambiguous qualified entrypoint: found %d callables named '%s' in moduleId=%d".formatted( - entrypoints.size(), - entryPointCallableName, - entryPointModuleId.getIndex())); - } throw new IRVMLoweringException( IRVMLoweringErrorCode.LOWER_IRVM_ENTRYPOINT_AMBIGUOUS, - "ambiguous entrypoint: found %d callables named '%s'".formatted( + "ambiguous qualified entrypoint: found %d callables named '%s' in moduleId=%d".formatted( entrypoints.size(), - entryPointCallableName)); + entryPointCallableName, + entryPointModuleId.getIndex())); } final var entrypoint = entrypoints.getFirst(); final var ordered = new ArrayList(sorted.size()); diff --git a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/backend/GoldenArtifactsTest.java b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/backend/GoldenArtifactsTest.java index 0b39f615..264538b8 100644 --- a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/backend/GoldenArtifactsTest.java +++ b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/backend/GoldenArtifactsTest.java @@ -43,8 +43,10 @@ class GoldenArtifactsTest { private IRBackend fixtureBackend() { return IRBackend.builder() + .entryPointModuleId(new p.studio.compiler.source.identifiers.ModuleId(0)) .executableFunctions(ReadOnlyList.from(new IRBackendExecutableFunction( new FileId(1), + new p.studio.compiler.source.identifiers.ModuleId(0), "main", new CallableId(0), 0, diff --git a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/backend/irvm/LowerToIRVMServiceTest.java b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/backend/irvm/LowerToIRVMServiceTest.java index 6f1271c6..f7566c45 100644 --- a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/backend/irvm/LowerToIRVMServiceTest.java +++ b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/backend/irvm/LowerToIRVMServiceTest.java @@ -21,6 +21,7 @@ class LowerToIRVMServiceTest { @Test void lowerMustAssignEntrypointIdZeroAndSortRemainingDeterministically() { final var backend = IRBackend.builder() + .entryPointModuleId(new ModuleId(0)) .executableFunctions(ReadOnlyList.from( fn("aux", "app", 11, ReadOnlyList.from( callFunc("app", "main", 10), @@ -42,6 +43,7 @@ class LowerToIRVMServiceTest { void lowerMustUseFrontendDeclaredEntrypointCallable() { final var backend = IRBackend.builder() .entryPointCallableName("frame") + .entryPointModuleId(new ModuleId(0)) .executableFunctions(ReadOnlyList.from( fn("aux", "app", 11, ReadOnlyList.from( callFunc("app", "frame", 10), @@ -60,6 +62,7 @@ class LowerToIRVMServiceTest { @Test void lowerMustMapHostAndIntrinsicCallsites() { final var backend = IRBackend.builder() + .entryPointModuleId(new ModuleId(0)) .executableFunctions(ReadOnlyList.from( fn("main", "app", 10, ReadOnlyList.from( callHost("gfx", "draw_pixel", 1, 0, 0), @@ -83,6 +86,7 @@ class LowerToIRVMServiceTest { @Test void lowerMustRejectUnterminatedFunction() { final var backend = IRBackend.builder() + .entryPointModuleId(new ModuleId(0)) .executableFunctions(ReadOnlyList.from( fn("main", "app", 10, ReadOnlyList.from( callFunc("app", "main", 10))))) @@ -95,6 +99,7 @@ class LowerToIRVMServiceTest { @Test void lowerMustRejectMissingCallee() { final var backend = IRBackend.builder() + .entryPointModuleId(new ModuleId(0)) .executableFunctions(ReadOnlyList.from( fn("main", "app", 10, ReadOnlyList.from( callFunc("app", "missing", 77), @@ -108,6 +113,7 @@ class LowerToIRVMServiceTest { @Test void lowerMustRejectUnknownCanonicalIntrinsicIdentity() { final var backend = IRBackend.builder() + .entryPointModuleId(new ModuleId(0)) .executableFunctions(ReadOnlyList.from( fn("main", "app", 10, ReadOnlyList.from( callIntrinsic("core.color.pack", 1, 0), @@ -123,6 +129,7 @@ class LowerToIRVMServiceTest { @Test void lowerMustRejectCallArgSlotMismatch() { final var backend = IRBackend.builder() + .entryPointModuleId(new ModuleId(0)) .executableFunctions(ReadOnlyList.from( fn("callee", "app", 20, ReadOnlyList.from(ret())), fn("main", "app", 10, ReadOnlyList.from( @@ -137,6 +144,7 @@ class LowerToIRVMServiceTest { @Test void lowerMustResolveJumpTargetsFromLabels() { final var backend = IRBackend.builder() + .entryPointModuleId(new ModuleId(0)) .executableFunctions(ReadOnlyList.from( fn("main", "app", 10, ReadOnlyList.from( label("entry"), @@ -156,6 +164,7 @@ class LowerToIRVMServiceTest { @Test void lowerMustRejectMissingJumpTargetLabel() { final var backend = IRBackend.builder() + .entryPointModuleId(new ModuleId(0)) .executableFunctions(ReadOnlyList.from( fn("main", "app", 10, ReadOnlyList.from( jmp("missing"), @@ -169,6 +178,7 @@ class LowerToIRVMServiceTest { @Test void lowerMustRejectWhenEntrypointIsMissing() { final var backend = IRBackend.builder() + .entryPointModuleId(new ModuleId(0)) .executableFunctions(ReadOnlyList.from( fn("helper", "app", 10, ReadOnlyList.from(ret())))) .build(); @@ -192,6 +202,7 @@ class LowerToIRVMServiceTest { @Test void lowerMustRejectWhenEntrypointIsAmbiguous() { final var backend = IRBackend.builder() + .entryPointModuleId(new ModuleId(0)) .executableFunctions(ReadOnlyList.from( fn("main", "app", 10, ReadOnlyList.from(ret())), fn("main", "sdk", 11, ReadOnlyList.from(ret())))) @@ -205,6 +216,7 @@ class LowerToIRVMServiceTest { void lowerMustUseModulePoolCanonicalIdentityForNonEntrypointOrdering() { final var backend = IRBackend.builder() .entryPointCallableName("frame") + .entryPointModuleId(new ModuleId(1)) .modulePool(ReadOnlyList.from( new ModuleReference("app", ReadOnlyList.from("alpha")), new ModuleReference("app", ReadOnlyList.from("zeta")))) @@ -264,6 +276,7 @@ class LowerToIRVMServiceTest { final ReadOnlyList instructions) { return new IRBackendExecutableFunction( new FileId(0), + new ModuleId(0), name, new CallableId(callableId), 0, diff --git a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/backend/irvm/OptimizeIRVMEquivalenceHarnessTest.java b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/backend/irvm/OptimizeIRVMEquivalenceHarnessTest.java index 4c5ceec5..20dcc9cb 100644 --- a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/backend/irvm/OptimizeIRVMEquivalenceHarnessTest.java +++ b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/backend/irvm/OptimizeIRVMEquivalenceHarnessTest.java @@ -28,6 +28,7 @@ class OptimizeIRVMEquivalenceHarnessTest { @Test void optimizeOnOffMustPreserveObservableTraceForLoweredHostIntrinsicFixture() { final var backend = IRBackend.builder() + .entryPointModuleId(new p.studio.compiler.source.identifiers.ModuleId(0)) .executableFunctions(ReadOnlyList.from( fn("main", 1, ReadOnlyList.from( callHost("gfx", "draw_pixel", 1, 0, 0), @@ -322,6 +323,7 @@ class OptimizeIRVMEquivalenceHarnessTest { final ReadOnlyList instructions) { return new IRBackendExecutableFunction( new FileId(1), + new p.studio.compiler.source.identifiers.ModuleId(0), name, new CallableId(callableId), 0, diff --git a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/BackendGateIIntegrationTest.java b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/BackendGateIIntegrationTest.java index 236bd152..4e132104 100644 --- a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/BackendGateIIntegrationTest.java +++ b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/integration/BackendGateIIntegrationTest.java @@ -178,6 +178,7 @@ class BackendGateIIntegrationTest { final IRBackendExecutableFunction function, final ReadOnlyList intrinsicPool) { return IRBackend.builder() + .entryPointModuleId(new p.studio.compiler.source.identifiers.ModuleId(0)) .executableFunctions(ReadOnlyList.from(function)) .intrinsicPool(intrinsicPool) .build(); @@ -188,6 +189,7 @@ class BackendGateIIntegrationTest { final ReadOnlyList instructions) { return new IRBackendExecutableFunction( new FileId(1), + new p.studio.compiler.source.identifiers.ModuleId(0), name, new CallableId(1), 0, 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 09f776f3..23921d35 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 @@ -26,8 +26,10 @@ class BackendSafetyGateSUTest { @Test void lowerStageMustExposeDeterministicFailureCodeForSameInvalidInput() { final var backend = IRBackend.builder() + .entryPointModuleId(new p.studio.compiler.source.identifiers.ModuleId(0)) .executableFunctions(ReadOnlyList.from(new IRBackendExecutableFunction( new FileId(1), + new p.studio.compiler.source.identifiers.ModuleId(0), "main", new CallableId(1), 0, @@ -109,8 +111,10 @@ class BackendSafetyGateSUTest { @Test void fullPipelineMustProduceDeterministicBytecodeForSameInput() { final var backend = IRBackend.builder() + .entryPointModuleId(new p.studio.compiler.source.identifiers.ModuleId(0)) .executableFunctions(ReadOnlyList.from(new IRBackendExecutableFunction( new FileId(1), + new p.studio.compiler.source.identifiers.ModuleId(0), "main", new CallableId(1), 0, diff --git a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/workspaces/stages/LowerToIRVMPipelineStageTest.java b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/workspaces/stages/LowerToIRVMPipelineStageTest.java index db1f4390..013017f8 100644 --- a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/workspaces/stages/LowerToIRVMPipelineStageTest.java +++ b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/workspaces/stages/LowerToIRVMPipelineStageTest.java @@ -36,8 +36,10 @@ class LowerToIRVMPipelineStageTest { void runMustLowerExecutableFunctionsToIrvm() { final var ctx = BuilderPipelineContext.compilerContext(new BuilderPipelineConfig(false, ".")); ctx.irBackend = IRBackend.builder() + .entryPointModuleId(new p.studio.compiler.source.identifiers.ModuleId(0)) .executableFunctions(ReadOnlyList.from(new IRBackendExecutableFunction( new FileId(0), + new p.studio.compiler.source.identifiers.ModuleId(0), "main", new CallableId(1), 0, @@ -67,8 +69,10 @@ class LowerToIRVMPipelineStageTest { void runMustPropagateConfiguredVmProfileToLowering() { final var ctx = BuilderPipelineContext.compilerContext(new BuilderPipelineConfig(false, ".", "experimental-v1")); ctx.irBackend = IRBackend.builder() + .entryPointModuleId(new p.studio.compiler.source.identifiers.ModuleId(0)) .executableFunctions(ReadOnlyList.from(new IRBackendExecutableFunction( new FileId(0), + new p.studio.compiler.source.identifiers.ModuleId(0), "main", new CallableId(1), 0, @@ -98,8 +102,10 @@ class LowerToIRVMPipelineStageTest { final var callSpan = new Span(new FileId(9), 12, 24); final var ctx = BuilderPipelineContext.compilerContext(new BuilderPipelineConfig(false, ".")); ctx.irBackend = IRBackend.builder() + .entryPointModuleId(new p.studio.compiler.source.identifiers.ModuleId(0)) .executableFunctions(ReadOnlyList.from(new IRBackendExecutableFunction( new FileId(9), + new p.studio.compiler.source.identifiers.ModuleId(0), "main", new CallableId(1), 10, @@ -140,9 +146,11 @@ class LowerToIRVMPipelineStageTest { void runMustRejectCallWhenStackDoesNotProvideDeclaredArgs() { final var ctx = BuilderPipelineContext.compilerContext(new BuilderPipelineConfig(false, ".")); ctx.irBackend = IRBackend.builder() + .entryPointModuleId(new p.studio.compiler.source.identifiers.ModuleId(0)) .executableFunctions(ReadOnlyList.from( new IRBackendExecutableFunction( new FileId(0), + new p.studio.compiler.source.identifiers.ModuleId(0), "callee", new CallableId(2), 0, @@ -160,6 +168,7 @@ class LowerToIRVMPipelineStageTest { Span.none()), new IRBackendExecutableFunction( new FileId(0), + new p.studio.compiler.source.identifiers.ModuleId(0), "main", new CallableId(1), 0,