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 index 073f9033..a1c32d3a 100644 --- 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 @@ -1,14 +1,25 @@ package p.studio.compiler.backend.irvm; +import java.util.List; +import java.util.Objects; + public class OptimizeIRVMService { private final IRVMValidator validator; + private final List passes; public OptimizeIRVMService() { - this(new IRVMValidator()); + this(new IRVMValidator(), List.of(new NoOpPass())); } OptimizeIRVMService(final IRVMValidator validator) { + this(validator, List.of(new NoOpPass())); + } + + OptimizeIRVMService( + final IRVMValidator validator, + final List passes) { this.validator = validator; + this.passes = passes == null ? List.of(new NoOpPass()) : List.copyOf(passes); } public IRVMProgram optimize(final IRVMProgram input) { @@ -16,11 +27,41 @@ public class OptimizeIRVMService { 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; + var current = program; + validator.validate(current.module(), false); + for (final var pass : passes) { + if (pass == null || !pass.enabled()) { + continue; + } + final var beforeProfile = current.module().vmProfile(); + current = Objects.requireNonNull(pass.apply(current), "pass output"); + if (!beforeProfile.equals(current.module().vmProfile())) { + throw new IllegalArgumentException("pass changed vm profile: " + pass.name()); + } + validator.validate(current.module(), false); + } + return current; + } + + public interface IRVMPass { + String name(); + + default boolean enabled() { + return true; + } + + IRVMProgram apply(IRVMProgram input); + } + + private static final class NoOpPass implements IRVMPass { + @Override + public String name() { + return "NoOpPass"; + } + + @Override + public IRVMProgram apply(final IRVMProgram input) { + return input; + } } } - diff --git a/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/backend/irvm/OptimizeIRVMServiceTest.java b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/backend/irvm/OptimizeIRVMServiceTest.java new file mode 100644 index 00000000..5721168c --- /dev/null +++ b/prometeu-compiler/prometeu-build-pipeline/src/test/java/p/studio/compiler/backend/irvm/OptimizeIRVMServiceTest.java @@ -0,0 +1,94 @@ +package p.studio.compiler.backend.irvm; + +import org.junit.jupiter.api.Test; +import p.studio.utilities.structures.ReadOnlyList; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class OptimizeIRVMServiceTest { + + @Test + void optimizeMustExecuteEnabledPassesInDeclaredOrder() { + final var order = new ArrayList(); + final var service = new OptimizeIRVMService(new IRVMValidator(), List.of( + namedPass("A", order, true), + namedPass("B", order, true))); + + final var output = service.optimize(validProgram()); + + assertEquals(validProgram(), output); + assertEquals(List.of("A", "B"), order); + } + + @Test + void optimizeMustSkipDisabledPasses() { + final var order = new ArrayList(); + final var service = new OptimizeIRVMService(new IRVMValidator(), List.of( + namedPass("A", order, false), + namedPass("B", order, true))); + + service.optimize(validProgram()); + + assertEquals(List.of("B"), order); + } + + @Test + void optimizeMustRejectPassThatMutatesVmProfile() { + final var service = new OptimizeIRVMService(new IRVMValidator(), List.of(new OptimizeIRVMService.IRVMPass() { + @Override + public String name() { + return "MutateProfile"; + } + + @Override + public IRVMProgram apply(final IRVMProgram input) { + return new IRVMProgram(new IRVMModule( + "experimental", + input.module().functions())); + } + })); + + final var thrown = assertThrows(IllegalArgumentException.class, () -> service.optimize(validProgram())); + assertTrue(thrown.getMessage().contains("vm profile")); + } + + private OptimizeIRVMService.IRVMPass namedPass( + final String name, + final List order, + final boolean enabled) { + return new OptimizeIRVMService.IRVMPass() { + @Override + public String name() { + return name; + } + + @Override + public boolean enabled() { + return enabled; + } + + @Override + public IRVMProgram apply(final IRVMProgram input) { + order.add(name); + return input; + } + }; + } + + private IRVMProgram validProgram() { + return new IRVMProgram(new IRVMModule( + "core-v1", + ReadOnlyList.from(new IRVMFunction( + "main", + 0, + 0, + 0, + 1, + ReadOnlyList.from(new IRVMInstruction(IRVMOp.HALT, null)))))); + } +}