re-shape of compiler pipeline

This commit is contained in:
bQUARKz 2026-03-30 19:40:00 +01:00
parent 9d2a9955c0
commit d22c7d16ef
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
6 changed files with 84 additions and 57 deletions

View File

@ -1,15 +1,15 @@
package p.studio.compiler.models; package p.studio.compiler.models;
import p.studio.compiler.messages.BuildingIssue; import p.studio.compiler.messages.BuildingIssue;
import p.studio.compiler.source.tables.FileTable; import p.studio.compiler.source.tables.FileTableReader;
import java.util.List; import java.util.List;
public record AnalysisSnapshot( public record AnalysisSnapshot(
List<BuildingIssue> diagnostics, List<BuildingIssue> diagnostics,
ResolvedWorkspace resolvedWorkspace, ResolvedWorkspace resolvedWorkspace,
FileTable fileTable, FileTableReader fileTable,
IRBackend irBackend) { IRBackendReader irBackend) {
public AnalysisSnapshot { public AnalysisSnapshot {
diagnostics = List.copyOf(diagnostics); diagnostics = List.copyOf(diagnostics);
} }

View File

@ -3,9 +3,9 @@ package p.studio.compiler.workspaces;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import p.studio.compiler.exceptions.BuildException; import p.studio.compiler.exceptions.BuildException;
import p.studio.compiler.messages.BuildingIssue; import p.studio.compiler.messages.BuildingIssue;
import p.studio.compiler.models.BuilderPipelineContext;
import p.studio.compiler.models.AnalysisSnapshot; import p.studio.compiler.models.AnalysisSnapshot;
import p.studio.compiler.models.BuildResult; import p.studio.compiler.models.BuildResult;
import p.studio.compiler.models.BuilderPipelineContext;
import p.studio.compiler.models.CompileResult; import p.studio.compiler.models.CompileResult;
import p.studio.compiler.workspaces.stages.*; import p.studio.compiler.workspaces.stages.*;
import p.studio.utilities.logs.LogAggregator; import p.studio.utilities.logs.LogAggregator;
@ -19,30 +19,45 @@ public class BuilderPipelineService {
public final static BuilderPipelineService INSTANCE; public final static BuilderPipelineService INSTANCE;
static { static {
final var stages = List.<PipelineStage>of(
final var analyses = List.<PipelineStage>of(
new ResolveDepsPipelineStage(), new ResolveDepsPipelineStage(),
new LoadSourcesPipelineStage(), new LoadSourcesPipelineStage(),
new FrontendPhasePipelineStage(), new FrontendPhasePipelineStage()
);
final var compile = List.<PipelineStage>of(
new LowerToIRVMPipelineStage(), new LowerToIRVMPipelineStage(),
new OptimizeIRVMPipelineStage(), new OptimizeIRVMPipelineStage(),
new EmitBytecodePipelineStage(), new EmitBytecodePipelineStage(),
new LinkBytecodePipelineStage(), new LinkBytecodePipelineStage(),
new VerifyBytecodePipelineStage(), new VerifyBytecodePipelineStage()
);
final var build = List.<PipelineStage>of(
new WriteBytecodeArtifactPipelineStage() new WriteBytecodeArtifactPipelineStage()
); );
INSTANCE = new BuilderPipelineService(stages);
INSTANCE = new BuilderPipelineService(analyses, compile, build);
} }
private final List<PipelineStage> stages; private final List<PipelineStage> analyses;
private final List<PipelineStage> compile;
private final List<PipelineStage> build;
BuilderPipelineService(List<PipelineStage> stages) { BuilderPipelineService(
this.stages = stages; final List<PipelineStage> analyses,
final List<PipelineStage> compile,
final List<PipelineStage> build) {
this.analyses = analyses;
this.compile = compile;
this.build = build;
} }
public AnalysisSnapshot analyze( public AnalysisSnapshot analyze(
final BuilderPipelineContext ctx, final BuilderPipelineContext ctx,
final LogAggregator logs) { final LogAggregator logs) {
final var diagnostics = runToTerminal(ctx, logs, FrontendPhasePipelineStage.class); final var diagnostics = run(ctx, logs, analyses, new ArrayList<>());
return new AnalysisSnapshot( return new AnalysisSnapshot(
diagnostics, diagnostics,
ctx.resolvedWorkspace, ctx.resolvedWorkspace,
@ -53,12 +68,8 @@ public class BuilderPipelineService {
public CompileResult compile( public CompileResult compile(
final BuilderPipelineContext ctx, final BuilderPipelineContext ctx,
final LogAggregator logs) { final LogAggregator logs) {
final var diagnostics = runToTerminal(ctx, logs, VerifyBytecodePipelineStage.class); final var analysisSnapshot = this.analyze(ctx, logs);
final var analysisSnapshot = new AnalysisSnapshot( final var diagnostics = run(ctx, logs, compile, new ArrayList<>(analysisSnapshot.diagnostics()));
diagnostics,
ctx.resolvedWorkspace,
ctx.fileTable,
ctx.irBackend);
return new CompileResult( return new CompileResult(
analysisSnapshot, analysisSnapshot,
diagnostics, diagnostics,
@ -71,31 +82,19 @@ public class BuilderPipelineService {
public BuildResult build( public BuildResult build(
final BuilderPipelineContext ctx, final BuilderPipelineContext ctx,
final LogAggregator logs) { final LogAggregator logs) {
final var diagnostics = runToTerminal(ctx, logs, WriteBytecodeArtifactPipelineStage.class); final var compileResult = this.compile(ctx, logs);
final var analysisSnapshot = new AnalysisSnapshot( final var diagnostics = run(ctx, logs, build, new ArrayList<>(compileResult.diagnostics()));
diagnostics,
ctx.resolvedWorkspace,
ctx.fileTable,
ctx.irBackend);
final var compileResult = new CompileResult(
analysisSnapshot,
diagnostics,
ctx.irvm,
ctx.optimizedIrvm,
ctx.bytecodeModule,
ctx.bytecodeBytes);
return new BuildResult( return new BuildResult(
compileResult, compileResult,
diagnostics, diagnostics,
ctx.bytecodeArtifactPath); ctx.bytecodeArtifactPath);
} }
private List<BuildingIssue> runToTerminal( private List<BuildingIssue> run(
final BuilderPipelineContext ctx, final BuilderPipelineContext ctx,
final LogAggregator logs, final LogAggregator logs,
final Class<? extends PipelineStage> terminalStage) { final List<PipelineStage> stages,
final var diagnostics = new ArrayList<BuildingIssue>(); final List<BuildingIssue> diagnostics) {
var completed = false;
for (final var builderPipelineStage : stages) { for (final var builderPipelineStage : stages) {
final var issues = builderPipelineStage.run(ctx, logs); final var issues = builderPipelineStage.run(ctx, logs);
diagnostics.addAll(issues.asCollection()); diagnostics.addAll(issues.asCollection());
@ -103,15 +102,8 @@ public class BuilderPipelineService {
if (issues.hasErrors()) { if (issues.hasErrors()) {
throw new BuildException("issues found on pipeline stage: " + builderPipelineStage.getClass().getSimpleName()); throw new BuildException("issues found on pipeline stage: " + builderPipelineStage.getClass().getSimpleName());
} }
if (terminalStage.isInstance(builderPipelineStage)) {
completed = true;
break;
}
} }
if (!completed) { logs.using(log).info("builder pipeline completed successfully");
throw new BuildException("terminal stage not found on builder pipeline: " + terminalStage.getSimpleName());
}
logs.using(log).info("builder pipeline completed successfully through " + terminalStage.getSimpleName());
return List.copyOf(diagnostics); return List.copyOf(diagnostics);
} }

View File

@ -14,31 +14,36 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
class BuilderPipelineServiceOrderTest { class BuilderPipelineServiceOrderTest {
@Test @Test
void canonicalOrderMustContainOptimizeBetweenLowerAndEmit() throws Exception { void canonicalOrderMustExposeExpectedStageGroups() throws Exception {
final Field field = BuilderPipelineService.class.getDeclaredField("stages"); final var analyzeStages = stages("analyses");
field.setAccessible(true); final var compileStages = stages("compile");
final var buildStages = stages("build");
@SuppressWarnings("unchecked")
final var stages = (List<PipelineStage>) field.get(BuilderPipelineService.INSTANCE);
final var stageTypes = stages.stream().map(Object::getClass).toList();
assertEquals( assertEquals(
List.of( List.of(
ResolveDepsPipelineStage.class, ResolveDepsPipelineStage.class,
LoadSourcesPipelineStage.class, LoadSourcesPipelineStage.class,
FrontendPhasePipelineStage.class, FrontendPhasePipelineStage.class),
analyzeStages);
assertEquals(
List.of(
LowerToIRVMPipelineStage.class, LowerToIRVMPipelineStage.class,
OptimizeIRVMPipelineStage.class, OptimizeIRVMPipelineStage.class,
EmitBytecodePipelineStage.class, EmitBytecodePipelineStage.class,
LinkBytecodePipelineStage.class, LinkBytecodePipelineStage.class,
VerifyBytecodePipelineStage.class, VerifyBytecodePipelineStage.class),
WriteBytecodeArtifactPipelineStage.class), compileStages);
stageTypes);
assertEquals(
List.of(WriteBytecodeArtifactPipelineStage.class),
buildStages);
} }
@Test @Test
void publicSurfaceMustExposeExplicitEntryPointsWithoutRun() { void publicSurfaceMustExposeExplicitEntryPointsWithoutRun() {
final var methodNames = List.of(BuilderPipelineService.class.getDeclaredMethods()).stream() final var methodNames = List.of(BuilderPipelineService.class.getMethods()).stream()
.filter(method -> method.getDeclaringClass().equals(BuilderPipelineService.class))
.map(Method::getName) .map(Method::getName)
.toList(); .toList();
@ -47,4 +52,12 @@ class BuilderPipelineServiceOrderTest {
assertTrue(methodNames.contains("build")); assertTrue(methodNames.contains("build"));
assertFalse(methodNames.contains("run")); assertFalse(methodNames.contains("run"));
} }
@SuppressWarnings("unchecked")
private List<Class<?>> stages(final String fieldName) throws Exception {
final Field field = BuilderPipelineService.class.getDeclaredField(fieldName);
field.setAccessible(true);
final var stages = (List<PipelineStage>) field.get(BuilderPipelineService.INSTANCE);
return stages.stream().map(Object::getClass).toList();
}
} }

View File

@ -13,7 +13,7 @@ import java.util.LinkedHashSet;
@Builder @Builder
@Getter @Getter
public class IRBackend { public class IRBackend implements IRBackendReader {
@Builder.Default @Builder.Default
private final String entryPointCallableName = "main"; private final String entryPointCallableName = "main";
@Builder.Default @Builder.Default

View File

@ -0,0 +1,20 @@
package p.studio.compiler.models;
import p.studio.compiler.source.identifiers.ModuleId;
import p.studio.compiler.source.tables.CallableSignatureRef;
import p.studio.compiler.source.tables.IntrinsicReference;
import p.studio.compiler.source.tables.ModuleReference;
import p.studio.utilities.structures.ReadOnlyList;
public interface IRBackendReader {
String getEntryPointCallableName();
ModuleId getEntryPointModuleId();
ReadOnlyList<IRFunction> getFunctions();
ReadOnlyList<IRSyntheticFunction> getSyntheticFunctions();
ReadOnlyList<IRGlobal> getGlobals();
ReadOnlyList<IRBackendExecutableFunction> getExecutableFunctions();
ReadOnlyList<ModuleReference> getModulePool();
ReadOnlyList<CallableSignatureRef> getCallableSignatures();
ReadOnlyList<IntrinsicReference> getIntrinsicPool();
IRReservedMetadata getReservedMetadata();
}

View File

@ -7,6 +7,7 @@ import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import p.studio.Container; import p.studio.Container;
import p.studio.compiler.messages.BuilderPipelineConfig; import p.studio.compiler.messages.BuilderPipelineConfig;
import p.studio.compiler.models.BuilderPipelineContext;
import p.studio.compiler.workspaces.BuilderPipelineService; import p.studio.compiler.workspaces.BuilderPipelineService;
import p.studio.projects.ProjectReference; import p.studio.projects.ProjectReference;
import p.studio.utilities.i18n.I18n; import p.studio.utilities.i18n.I18n;
@ -67,7 +68,8 @@ public class ShipperWorkspace extends Workspace {
logs.clear(); logs.clear();
final var logAggregator = LogAggregator.with(logs::appendText); final var logAggregator = LogAggregator.with(logs::appendText);
final var config = new BuilderPipelineConfig(false, projectReference.rootPath().toString()); final var config = new BuilderPipelineConfig(false, projectReference.rootPath().toString());
BuilderPipelineService.INSTANCE.run(config, logAggregator); final var ctx = BuilderPipelineContext.fromConfig(config);
BuilderPipelineService.INSTANCE.build(ctx, logAggregator);
}); });
clearButton.textProperty().bind(Container.i18n().bind(I18n.WORKSPACE_SHIPPER_BUTTON_CLEAR)); clearButton.textProperty().bind(Container.i18n().bind(I18n.WORKSPACE_SHIPPER_BUTTON_CLEAR));