From 3449771f33e74494710357d43dbd838aef716af4 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Sat, 7 Mar 2026 19:36:19 +0000 Subject: [PATCH] implements PR-O4.2 --- .../compiler/pbs/PbsFrontendCompiler.java | 16 +++-- .../backend/irvm/IRVMLoweringErrorCode.java | 1 + .../backend/irvm/LowerToIRVMService.java | 5 ++ .../backend/irvm/LowerToIRVMServiceTest.java | 6 +- .../BackendGateIIntegrationTest.java | 13 +++- .../p/studio/compiler/models/IRBackend.java | 53 +++++++++++++- .../studio/compiler/models/IRBackendFile.java | 11 +-- .../IRBackendExecutableContractTest.java | 72 ++++++++++++++++++- 8 files changed, 160 insertions(+), 17 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 a1940c09..cf60f4dd 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 @@ -18,6 +18,7 @@ import p.studio.compiler.source.diagnostics.DiagnosticPhase; import p.studio.compiler.source.identifiers.FileId; import p.studio.compiler.source.tables.CallableSignatureRef; import p.studio.compiler.source.tables.CallableTable; +import p.studio.compiler.source.tables.IntrinsicReference; import p.studio.compiler.source.tables.IntrinsicTable; import p.studio.utilities.structures.ReadOnlyList; @@ -124,7 +125,7 @@ public final class PbsFrontendCompiler { : lowerFunctions(fileId, ast); final var loweringErrorBaseline = diagnostics.errorCount(); final var executableLowering = sourceKind == SourceKind.SDK_INTERFACE - ? new ExecutableLoweringResult(ReadOnlyList.empty(), ReadOnlyList.empty()) + ? new ExecutableLoweringResult(ReadOnlyList.empty(), ReadOnlyList.empty(), ReadOnlyList.empty()) : lowerExecutableFunctions(fileId, ast, moduleKey, reservedMetadata, diagnostics); if (diagnostics.errorCount() > loweringErrorBaseline) { return IRBackendFile.empty(fileId); @@ -134,7 +135,8 @@ public final class PbsFrontendCompiler { functions, executableLowering.executableFunctions(), reservedMetadata, - executableLowering.callableSignatures()); + executableLowering.callableSignatures(), + executableLowering.intrinsicPool()); } private ReadOnlyList lowerFunctions(final FileId fileId, final PbsAst.File ast) { @@ -312,9 +314,14 @@ public final class PbsFrontendCompiler { for (final var callableId : callableIdTable.identifiers()) { callableSignatures.add(callableIdTable.get(callableId)); } + final var intrinsicPool = new ArrayList(intrinsicIdTable.size()); + for (final var intrinsicId : intrinsicIdTable.identifiers()) { + intrinsicPool.add(intrinsicIdTable.get(intrinsicId)); + } return new ExecutableLoweringResult( ReadOnlyList.wrap(executableFunctions), - ReadOnlyList.wrap(callableSignatures)); + ReadOnlyList.wrap(callableSignatures), + ReadOnlyList.wrap(intrinsicPool)); } private int returnSlotsFor(final PbsAst.FunctionDecl functionDecl) { @@ -550,6 +557,7 @@ public final class PbsFrontendCompiler { private record ExecutableLoweringResult( ReadOnlyList executableFunctions, - ReadOnlyList callableSignatures) { + ReadOnlyList callableSignatures, + ReadOnlyList intrinsicPool) { } } diff --git a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/IRVMLoweringErrorCode.java b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/IRVMLoweringErrorCode.java index c665520a..f0a6cc34 100644 --- a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/IRVMLoweringErrorCode.java +++ b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/IRVMLoweringErrorCode.java @@ -3,6 +3,7 @@ package p.studio.compiler.backend.irvm; public enum IRVMLoweringErrorCode { LOWER_IRVM_EMPTY_EXECUTABLE_INPUT, LOWER_IRVM_MISSING_CALLEE, + LOWER_IRVM_INVALID_INTRINSIC_ID, LOWER_IRVM_CALL_ARG_SLOTS_MISMATCH, LOWER_IRVM_CALL_RET_SLOTS_MISMATCH, LOWER_IRVM_MISSING_JUMP_TARGET, 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 59a488ec..1c28b39a 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 @@ -121,6 +121,11 @@ public class LowerToIRVMService { } case CALL_INTRINSIC -> { final var intrinsic = instr.intrinsicCall(); + if (intrinsic.intrinsicId() < 0 || intrinsic.intrinsicId() >= backend.getIntrinsicPool().size()) { + throw new IRVMLoweringException( + IRVMLoweringErrorCode.LOWER_IRVM_INVALID_INTRINSIC_ID, + "invalid intrinsic id: " + intrinsic.intrinsicId()); + } instructions.add(new IRVMInstruction(IRVMOp.INTRINSIC, intrinsic.intrinsicId())); operations.add(BytecodeEmitter.Operation.intrinsic(intrinsic.intrinsicId(), sourceSpan)); functionPc += IRVMOp.INTRINSIC.immediateSize() + 2; 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 f3c15282..4882f847 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 @@ -5,6 +5,7 @@ import p.studio.compiler.models.IRBackend; import p.studio.compiler.models.IRBackendExecutableFunction; import p.studio.compiler.source.Span; import p.studio.compiler.source.identifiers.FileId; +import p.studio.compiler.source.tables.IntrinsicReference; import p.studio.utilities.structures.ReadOnlyList; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -39,8 +40,9 @@ class LowerToIRVMServiceTest { .executableFunctions(ReadOnlyList.from( fn("main", "app", 10, ReadOnlyList.from( callHost("gfx", "draw_pixel", 1, 2, 0), - callIntrinsic("core.color.pack", 1, 0x2001), + callIntrinsic("core.color.pack", 1, 0), ret())))) + .intrinsicPool(ReadOnlyList.from(new IntrinsicReference("core.color.pack", 1))) .build(); final var lowered = new LowerToIRVMService().lower(backend); @@ -48,7 +50,7 @@ class LowerToIRVMServiceTest { final var instructions = lowered.module().functions().getFirst().instructions(); assertEquals(IRVMOp.HOSTCALL, instructions.get(0).op()); assertEquals(IRVMOp.INTRINSIC, instructions.get(1).op()); - assertEquals(0x2001, instructions.get(1).immediate()); + assertEquals(0, instructions.get(1).immediate()); final var emissionOps = lowered.emissionPlan().functions().getFirst().operations(); assertEquals(p.studio.compiler.backend.bytecode.BytecodeEmitter.OperationKind.HOSTCALL, emissionOps.get(0).kind()); assertEquals(p.studio.compiler.backend.bytecode.BytecodeEmitter.OperationKind.INTRINSIC, emissionOps.get(1).kind()); 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 727ff710..9a06220b 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 @@ -11,6 +11,7 @@ import p.studio.compiler.models.IRBackend; import p.studio.compiler.models.IRBackendExecutableFunction; import p.studio.compiler.source.Span; import p.studio.compiler.source.identifiers.FileId; +import p.studio.compiler.source.tables.IntrinsicReference; import p.studio.compiler.workspaces.stages.EmitBytecodePipelineStage; import p.studio.compiler.workspaces.stages.LowerToIRVMPipelineStage; import p.studio.compiler.workspaces.stages.OptimizeIRVMPipelineStage; @@ -141,8 +142,9 @@ class BackendGateIIntegrationTest { void gateI_validIntrinsicPath() { final var module = emitFromBackend(backendWithSingleFunction( fn("main", ReadOnlyList.from( - callIntrinsic("core.color.pack", 1, 0x2000), - ret())))); + callIntrinsic("core.color.pack", 1, 0), + ret())), + ReadOnlyList.from(new IntrinsicReference("core.color.pack", 1)))); final var check = compatibilityAdapter.check(module); assertEquals(CompatibilityError.NONE, check.error()); @@ -166,8 +168,15 @@ class BackendGateIIntegrationTest { } private IRBackend backendWithSingleFunction(final IRBackendExecutableFunction function) { + return backendWithSingleFunction(function, ReadOnlyList.empty()); + } + + private IRBackend backendWithSingleFunction( + final IRBackendExecutableFunction function, + final ReadOnlyList intrinsicPool) { return IRBackend.builder() .executableFunctions(ReadOnlyList.from(function)) + .intrinsicPool(intrinsicPool) .build(); } diff --git a/prometeu-compiler/prometeu-frontend-api/src/main/java/p/studio/compiler/models/IRBackend.java b/prometeu-compiler/prometeu-frontend-api/src/main/java/p/studio/compiler/models/IRBackend.java index ce64d617..5dcd39b9 100644 --- a/prometeu-compiler/prometeu-frontend-api/src/main/java/p/studio/compiler/models/IRBackend.java +++ b/prometeu-compiler/prometeu-frontend-api/src/main/java/p/studio/compiler/models/IRBackend.java @@ -4,6 +4,8 @@ import lombok.Builder; import lombok.Getter; import p.studio.compiler.source.tables.CallableSignatureRef; import p.studio.compiler.source.tables.CallableTable; +import p.studio.compiler.source.tables.IntrinsicReference; +import p.studio.compiler.source.tables.IntrinsicTable; import p.studio.utilities.structures.ReadOnlyList; import java.util.ArrayList; @@ -19,6 +21,8 @@ public class IRBackend { @Builder.Default private final ReadOnlyList callableSignatures = ReadOnlyList.empty(); @Builder.Default + private final ReadOnlyList intrinsicPool = ReadOnlyList.empty(); + @Builder.Default private final IRReservedMetadata reservedMetadata = IRReservedMetadata.empty(); public static IRBackendAggregator aggregator() { @@ -29,6 +33,7 @@ public class IRBackend { private final ArrayList functions = new ArrayList<>(); private final ArrayList executableFunctions = new ArrayList<>(); private final CallableTable callableTable = new CallableTable(); + private final IntrinsicTable intrinsicTable = new IntrinsicTable(); private final ArrayList hostMethodBindings = new ArrayList<>(); private final ArrayList builtinTypeSurfaces = new ArrayList<>(); private final ArrayList builtinConstSurfaces = new ArrayList<>(); @@ -40,8 +45,9 @@ public class IRBackend { } functions.addAll(backendFile.functions().asList()); final var callableRemap = reindexCallables(backendFile.callableSignatures()); + final var intrinsicRemap = reindexIntrinsics(backendFile.intrinsicPool()); for (final var function : backendFile.executableFunctions()) { - executableFunctions.add(remapExecutableFunction(function, callableRemap)); + executableFunctions.add(remapExecutableFunction(function, callableRemap, intrinsicRemap)); } final var metadata = backendFile.reservedMetadata(); hostMethodBindings.addAll(metadata.hostMethodBindings().asList()); @@ -63,7 +69,8 @@ public class IRBackend { private IRBackendExecutableFunction remapExecutableFunction( final IRBackendExecutableFunction function, - final int[] callableRemap) { + final int[] callableRemap, + final int[] intrinsicRemap) { final var remappedInstructions = new ArrayList(function.instructions().size()); for (final var instruction : function.instructions()) { final Integer remappedCallee; @@ -72,13 +79,23 @@ public class IRBackend { } else { remappedCallee = remapCallableId(instruction.calleeCallableId(), callableRemap, "callee"); } + final IRBackendExecutableFunction.IntrinsicCallMetadata remappedIntrinsic; + if (instruction.kind() == IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC && instruction.intrinsicCall() != null) { + final var intrinsic = instruction.intrinsicCall(); + remappedIntrinsic = new IRBackendExecutableFunction.IntrinsicCallMetadata( + intrinsic.canonicalName(), + intrinsic.canonicalVersion(), + remapIntrinsicId(intrinsic.intrinsicId(), intrinsicRemap)); + } else { + remappedIntrinsic = instruction.intrinsicCall(); + } remappedInstructions.add(new IRBackendExecutableFunction.Instruction( instruction.kind(), instruction.calleeModuleKey(), instruction.calleeCallableName(), remappedCallee, instruction.hostCall(), - instruction.intrinsicCall(), + remappedIntrinsic, instruction.label(), instruction.targetLabel(), instruction.expectedArgSlots(), @@ -110,6 +127,26 @@ public class IRBackend { return callableRemap[localCallableId]; } + private int[] reindexIntrinsics(final ReadOnlyList localIntrinsicPool) { + if (localIntrinsicPool == null || localIntrinsicPool.isEmpty()) { + return new int[0]; + } + final var remap = new int[localIntrinsicPool.size()]; + for (var i = 0; i < localIntrinsicPool.size(); i++) { + remap[i] = intrinsicTable.register(localIntrinsicPool.get(i)).getId(); + } + return remap; + } + + private int remapIntrinsicId( + final int localIntrinsicId, + final int[] intrinsicRemap) { + if (localIntrinsicId < 0 || localIntrinsicId >= intrinsicRemap.length) { + throw new IllegalArgumentException("invalid local intrinsic id: " + localIntrinsicId); + } + return intrinsicRemap[localIntrinsicId]; + } + private ReadOnlyList emitCallableSignatures() { final var signatures = new ArrayList(callableTable.size()); for (final var callableId : callableTable.identifiers()) { @@ -118,12 +155,21 @@ public class IRBackend { return ReadOnlyList.wrap(signatures); } + private ReadOnlyList emitIntrinsicPool() { + final var pool = new ArrayList(intrinsicTable.size()); + for (final var intrinsicId : intrinsicTable.identifiers()) { + pool.add(intrinsicTable.get(intrinsicId)); + } + return ReadOnlyList.wrap(pool); + } + public IRBackend emit() { return IRBackend .builder() .functions(ReadOnlyList.wrap(functions)) .executableFunctions(ReadOnlyList.wrap(executableFunctions)) .callableSignatures(emitCallableSignatures()) + .intrinsicPool(emitIntrinsicPool()) .reservedMetadata(new IRReservedMetadata( ReadOnlyList.wrap(hostMethodBindings), ReadOnlyList.wrap(builtinTypeSurfaces), @@ -139,6 +185,7 @@ public class IRBackend { sb.append("IRBackend{functions=").append(functions.size()) .append(", executableFunctions=").append(executableFunctions.size()) .append(", callableSignatures=").append(callableSignatures.size()) + .append(", intrinsicPool=").append(intrinsicPool.size()) .append(", hostBindings=").append(reservedMetadata.hostMethodBindings().size()) .append(", builtinTypes=").append(reservedMetadata.builtinTypeSurfaces().size()) .append(", builtinConsts=").append(reservedMetadata.builtinConstSurfaces().size()) diff --git a/prometeu-compiler/prometeu-frontend-api/src/main/java/p/studio/compiler/models/IRBackendFile.java b/prometeu-compiler/prometeu-frontend-api/src/main/java/p/studio/compiler/models/IRBackendFile.java index 3daca9ed..86ea8415 100644 --- a/prometeu-compiler/prometeu-frontend-api/src/main/java/p/studio/compiler/models/IRBackendFile.java +++ b/prometeu-compiler/prometeu-frontend-api/src/main/java/p/studio/compiler/models/IRBackendFile.java @@ -2,6 +2,7 @@ package p.studio.compiler.models; import p.studio.compiler.source.identifiers.FileId; import p.studio.compiler.source.tables.CallableSignatureRef; +import p.studio.compiler.source.tables.IntrinsicReference; import p.studio.utilities.structures.ReadOnlyList; import java.util.Objects; @@ -11,29 +12,31 @@ public record IRBackendFile( ReadOnlyList functions, ReadOnlyList executableFunctions, IRReservedMetadata reservedMetadata, - ReadOnlyList callableSignatures) { + ReadOnlyList callableSignatures, + ReadOnlyList intrinsicPool) { public IRBackendFile { fileId = Objects.requireNonNull(fileId, "fileId"); functions = functions == null ? ReadOnlyList.empty() : functions; executableFunctions = executableFunctions == null ? ReadOnlyList.empty() : executableFunctions; reservedMetadata = reservedMetadata == null ? IRReservedMetadata.empty() : reservedMetadata; callableSignatures = callableSignatures == null ? ReadOnlyList.empty() : callableSignatures; + intrinsicPool = intrinsicPool == null ? ReadOnlyList.empty() : intrinsicPool; } public IRBackendFile( final FileId fileId, final ReadOnlyList functions) { - this(fileId, functions, ReadOnlyList.empty(), IRReservedMetadata.empty(), ReadOnlyList.empty()); + this(fileId, functions, ReadOnlyList.empty(), IRReservedMetadata.empty(), ReadOnlyList.empty(), ReadOnlyList.empty()); } public static IRBackendFile empty(final FileId fileId) { - return new IRBackendFile(fileId, ReadOnlyList.empty(), ReadOnlyList.empty(), IRReservedMetadata.empty(), ReadOnlyList.empty()); + return new IRBackendFile(fileId, ReadOnlyList.empty(), ReadOnlyList.empty(), IRReservedMetadata.empty(), ReadOnlyList.empty(), ReadOnlyList.empty()); } public IRBackendFile( final FileId fileId, final ReadOnlyList functions, final IRReservedMetadata reservedMetadata) { - this(fileId, functions, ReadOnlyList.empty(), reservedMetadata, ReadOnlyList.empty()); + this(fileId, functions, ReadOnlyList.empty(), reservedMetadata, ReadOnlyList.empty(), ReadOnlyList.empty()); } } diff --git a/prometeu-compiler/prometeu-frontend-api/src/test/java/p/studio/compiler/models/IRBackendExecutableContractTest.java b/prometeu-compiler/prometeu-frontend-api/src/test/java/p/studio/compiler/models/IRBackendExecutableContractTest.java index 4052edb5..7d1c3d33 100644 --- a/prometeu-compiler/prometeu-frontend-api/src/test/java/p/studio/compiler/models/IRBackendExecutableContractTest.java +++ b/prometeu-compiler/prometeu-frontend-api/src/test/java/p/studio/compiler/models/IRBackendExecutableContractTest.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.Test; import p.studio.compiler.source.Span; import p.studio.compiler.source.identifiers.FileId; import p.studio.compiler.source.tables.CallableSignatureRef; +import p.studio.compiler.source.tables.IntrinsicReference; import p.studio.utilities.structures.ReadOnlyList; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -139,7 +140,8 @@ class IRBackendExecutableContractTest { Span.none())), Span.none())), IRReservedMetadata.empty(), - ReadOnlyList.from(new CallableSignatureRef("app/main", "entry", 0, "() -> unit"))); + ReadOnlyList.from(new CallableSignatureRef("app/main", "entry", 0, "() -> unit")), + ReadOnlyList.empty()); final var fileB = new IRBackendFile( new FileId(2), ReadOnlyList.empty(), @@ -164,7 +166,8 @@ class IRBackendExecutableContractTest { Span.none())), Span.none())), IRReservedMetadata.empty(), - ReadOnlyList.from(new CallableSignatureRef("app/main", "aux", 0, "() -> unit"))); + ReadOnlyList.from(new CallableSignatureRef("app/main", "aux", 0, "() -> unit")), + ReadOnlyList.empty()); final var aggregator = IRBackend.aggregator(); aggregator.merge(fileA); @@ -176,4 +179,69 @@ class IRBackendExecutableContractTest { assertEquals("aux", backend.getExecutableFunctions().get(1).callableName()); assertEquals(2, backend.getCallableSignatures().size()); } + + @Test + void aggregatorMustReindexIntrinsicsToBuildGlobalPool() { + final var fileA = new IRBackendFile( + new FileId(1), + ReadOnlyList.empty(), + ReadOnlyList.from(new IRBackendExecutableFunction( + new FileId(1), + "app/main", + "entry", + 0, + 0, + 10, + 0, + 0, + 0, + 1, + ReadOnlyList.from(new IRBackendExecutableFunction.Instruction( + IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC, + "", + "", + null, + new IRBackendExecutableFunction.IntrinsicCallMetadata("core.color.pack", 1, 0), + Span.none())), + Span.none())), + IRReservedMetadata.empty(), + ReadOnlyList.from(new CallableSignatureRef("app/main", "entry", 0, "() -> unit")), + ReadOnlyList.from(new IntrinsicReference("core.color.pack", 1))); + final var fileB = new IRBackendFile( + new FileId(2), + ReadOnlyList.empty(), + ReadOnlyList.from(new IRBackendExecutableFunction( + new FileId(2), + "app/main", + "aux", + 0, + 11, + 20, + 0, + 0, + 0, + 1, + ReadOnlyList.from(new IRBackendExecutableFunction.Instruction( + IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC, + "", + "", + null, + new IRBackendExecutableFunction.IntrinsicCallMetadata("core.color.pack", 1, 0), + Span.none())), + Span.none())), + IRReservedMetadata.empty(), + ReadOnlyList.from(new CallableSignatureRef("app/main", "aux", 0, "() -> unit")), + ReadOnlyList.from(new IntrinsicReference("core.color.pack", 1))); + + final var aggregator = IRBackend.aggregator(); + aggregator.merge(fileA); + aggregator.merge(fileB); + final var backend = aggregator.emit(); + + final var firstIntrinsicId = backend.getExecutableFunctions().get(0).instructions().getFirst().intrinsicCall().intrinsicId(); + final var secondIntrinsicId = backend.getExecutableFunctions().get(1).instructions().getFirst().intrinsicCall().intrinsicId(); + assertEquals(1, backend.getIntrinsicPool().size()); + assertEquals(firstIntrinsicId, secondIntrinsicId); + assertEquals("core.color.pack", backend.getIntrinsicPool().getFirst().canonicalName()); + } }