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 714c978b..45cd457f 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 @@ -1,17 +1,49 @@ package p.studio.compiler.backend.irvm; import p.studio.compiler.backend.bytecode.BytecodeEmitter; +import p.studio.utilities.structures.ReadOnlyList; public record IRVMProgram( - boolean hasInternalOpcodes, + IRVMModule module, BytecodeEmitter.EmissionPlan emissionPlan) { public IRVMProgram { + module = module == null ? IRVMModule.empty() : module; emissionPlan = emissionPlan == null ? BytecodeEmitter.EmissionPlan.empty() : emissionPlan; } + public IRVMProgram(final IRVMModule module) { + this(module, BytecodeEmitter.EmissionPlan.empty()); + } + + public IRVMProgram( + final boolean hasInternalOpcodes, + final BytecodeEmitter.EmissionPlan emissionPlan) { + this(new IRVMModule( + "core-v1", + ReadOnlyList.from(new IRVMFunction( + "__synthetic__", + 0, + 0, + 0, + 1, + ReadOnlyList.from( + new IRVMInstruction(hasInternalOpcodes ? IRVMOp.INTERNAL_EXT : IRVMOp.HALT, null))))), + emissionPlan); + } + public static IRVMProgram empty() { - return new IRVMProgram(false, BytecodeEmitter.EmissionPlan.empty()); + return new IRVMProgram(IRVMModule.empty(), BytecodeEmitter.EmissionPlan.empty()); + } + + public boolean hasInternalOpcodes() { + for (final var function : module.functions()) { + for (final var instruction : function.instructions()) { + if (instruction.op().internal()) { + return true; + } + } + } + return false; } } - diff --git a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/OptimizeIRVMService.java b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/OptimizeIRVMService.java new file mode 100644 index 00000000..073f9033 --- /dev/null +++ b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/backend/irvm/OptimizeIRVMService.java @@ -0,0 +1,26 @@ +package p.studio.compiler.backend.irvm; + +public class OptimizeIRVMService { + private final IRVMValidator validator; + + public OptimizeIRVMService() { + this(new IRVMValidator()); + } + + OptimizeIRVMService(final IRVMValidator validator) { + this.validator = validator; + } + + public IRVMProgram optimize(final IRVMProgram input) { + final var program = input == null ? IRVMProgram.empty() : input; + if (!"core-v1".equals(program.module().vmProfile())) { + throw new IllegalArgumentException("unsupported vm profile: " + program.module().vmProfile()); + } + validator.validate(program.module(), false); + // Baseline pass manager: no-op pass preserving semantic shape. + final var optimized = program; + validator.validate(optimized.module(), false); + return optimized; + } +} + diff --git a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/workspaces/stages/OptimizeIRVMPipelineStage.java b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/workspaces/stages/OptimizeIRVMPipelineStage.java index 9bb20fd7..a615fc3e 100644 --- a/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/workspaces/stages/OptimizeIRVMPipelineStage.java +++ b/prometeu-compiler/prometeu-build-pipeline/src/main/java/p/studio/compiler/workspaces/stages/OptimizeIRVMPipelineStage.java @@ -1,6 +1,8 @@ package p.studio.compiler.workspaces.stages; import lombok.extern.slf4j.Slf4j; +import p.studio.compiler.backend.irvm.IRVMValidationException; +import p.studio.compiler.backend.irvm.OptimizeIRVMService; import p.studio.compiler.messages.BuildingIssueSink; import p.studio.compiler.models.BuilderPipelineContext; import p.studio.compiler.workspaces.PipelineStage; @@ -8,8 +10,40 @@ import p.studio.utilities.logs.LogAggregator; @Slf4j public class OptimizeIRVMPipelineStage implements PipelineStage { + private final OptimizeIRVMService optimizeIRVMService; + + public OptimizeIRVMPipelineStage() { + this(new OptimizeIRVMService()); + } + + OptimizeIRVMPipelineStage(final OptimizeIRVMService optimizeIRVMService) { + this.optimizeIRVMService = optimizeIRVMService; + } + @Override public BuildingIssueSink run(BuilderPipelineContext ctx, LogAggregator logs) { + if (ctx.irvm == null) { + return BuildingIssueSink.empty() + .report(builder -> builder + .error(true) + .message("[BUILD]: IRVM is missing before OptimizeIRVM stage")); + } + try { + ctx.optimizedIrvm = optimizeIRVMService.optimize(ctx.irvm); + } catch (IRVMValidationException e) { + return BuildingIssueSink.empty() + .report(builder -> builder + .error(true) + .message("[BUILD]: optimize irvm validation failed (%s): %s" + .formatted(e.code().name(), e.getMessage())) + .exception(e)); + } catch (RuntimeException e) { + return BuildingIssueSink.empty() + .report(builder -> builder + .error(true) + .message("[BUILD]: optimize irvm failed: " + e.getMessage()) + .exception(e)); + } return BuildingIssueSink.empty(); } } diff --git a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/workspaces/BuilderPipelineServiceOrderTest.java b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/workspaces/BuilderPipelineServiceOrderTest.java new file mode 100644 index 00000000..28ac2fd4 --- /dev/null +++ b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/workspaces/BuilderPipelineServiceOrderTest.java @@ -0,0 +1,38 @@ +package p.studio.compiler.workspaces; + +import org.junit.jupiter.api.Test; +import p.studio.compiler.workspaces.stages.EmitBytecodePipelineStage; +import p.studio.compiler.workspaces.stages.FrontendPhasePipelineStage; +import p.studio.compiler.workspaces.stages.LoadSourcesPipelineStage; +import p.studio.compiler.workspaces.stages.LowerToIRVMPipelineStage; +import p.studio.compiler.workspaces.stages.OptimizeIRVMPipelineStage; +import p.studio.compiler.workspaces.stages.ResolveDepsPipelineStage; + +import java.lang.reflect.Field; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class BuilderPipelineServiceOrderTest { + + @Test + void canonicalOrderMustContainOptimizeBetweenLowerAndEmit() throws Exception { + final Field field = BuilderPipelineService.class.getDeclaredField("stages"); + field.setAccessible(true); + + @SuppressWarnings("unchecked") + final var stages = (List) field.get(BuilderPipelineService.INSTANCE); + final var stageTypes = stages.stream().map(Object::getClass).toList(); + + assertEquals( + List.of( + ResolveDepsPipelineStage.class, + LoadSourcesPipelineStage.class, + FrontendPhasePipelineStage.class, + LowerToIRVMPipelineStage.class, + OptimizeIRVMPipelineStage.class, + EmitBytecodePipelineStage.class), + stageTypes); + } +} + diff --git a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/workspaces/stages/OptimizeIRVMPipelineStageTest.java b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/workspaces/stages/OptimizeIRVMPipelineStageTest.java new file mode 100644 index 00000000..4b94e0cc --- /dev/null +++ b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/workspaces/stages/OptimizeIRVMPipelineStageTest.java @@ -0,0 +1,75 @@ +package p.studio.compiler.workspaces.stages; + +import org.junit.jupiter.api.Test; +import p.studio.compiler.backend.irvm.IRVMFunction; +import p.studio.compiler.backend.irvm.IRVMInstruction; +import p.studio.compiler.backend.irvm.IRVMModule; +import p.studio.compiler.backend.irvm.IRVMOp; +import p.studio.compiler.backend.irvm.IRVMProgram; +import p.studio.compiler.messages.BuilderPipelineConfig; +import p.studio.compiler.models.BuilderPipelineContext; +import p.studio.utilities.logs.LogAggregator; +import p.studio.utilities.structures.ReadOnlyList; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class OptimizeIRVMPipelineStageTest { + + @Test + void runMustFailWhenIrvmIsMissing() { + final var ctx = BuilderPipelineContext.compilerContext(new BuilderPipelineConfig(false, ".")); + final var stage = new OptimizeIRVMPipelineStage(); + + final var issues = stage.run(ctx, LogAggregator.empty()); + + assertTrue(issues.hasErrors()); + assertEquals(1, issues.size()); + } + + @Test + void runMustProduceNoopOptimizedProgram() { + final var ctx = BuilderPipelineContext.compilerContext(new BuilderPipelineConfig(false, ".")); + ctx.irvm = new IRVMProgram(new IRVMModule( + "core-v1", + ReadOnlyList.from(new IRVMFunction( + "main", + 0, + 0, + 0, + 1, + ReadOnlyList.from( + new IRVMInstruction(IRVMOp.HALT, null)))))); + final var stage = new OptimizeIRVMPipelineStage(); + + final var issues = stage.run(ctx, LogAggregator.empty()); + + assertFalse(issues.hasErrors()); + assertNotNull(ctx.optimizedIrvm); + assertEquals(ctx.irvm, ctx.optimizedIrvm); + } + + @Test + void runMustRejectUnsupportedVmProfile() { + final var ctx = BuilderPipelineContext.compilerContext(new BuilderPipelineConfig(false, ".")); + ctx.irvm = new IRVMProgram(new IRVMModule( + "experimental", + ReadOnlyList.from(new IRVMFunction( + "main", + 0, + 0, + 0, + 1, + ReadOnlyList.from( + new IRVMInstruction(IRVMOp.HALT, null)))))); + final var stage = new OptimizeIRVMPipelineStage(); + + final var issues = stage.run(ctx, LogAggregator.empty()); + + assertTrue(issues.hasErrors()); + assertEquals(1, issues.size()); + } +} +