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;
import p.studio.compiler.messages.BuildingIssue;
import p.studio.compiler.source.tables.FileTable;
import p.studio.compiler.source.tables.FileTableReader;
import java.util.List;
public record AnalysisSnapshot(
List<BuildingIssue> diagnostics,
ResolvedWorkspace resolvedWorkspace,
FileTable fileTable,
IRBackend irBackend) {
FileTableReader fileTable,
IRBackendReader irBackend) {
public AnalysisSnapshot {
diagnostics = List.copyOf(diagnostics);
}

View File

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

View File

@ -14,31 +14,36 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
class BuilderPipelineServiceOrderTest {
@Test
void canonicalOrderMustContainOptimizeBetweenLowerAndEmit() throws Exception {
final Field field = BuilderPipelineService.class.getDeclaredField("stages");
field.setAccessible(true);
@SuppressWarnings("unchecked")
final var stages = (List<PipelineStage>) field.get(BuilderPipelineService.INSTANCE);
final var stageTypes = stages.stream().map(Object::getClass).toList();
void canonicalOrderMustExposeExpectedStageGroups() throws Exception {
final var analyzeStages = stages("analyses");
final var compileStages = stages("compile");
final var buildStages = stages("build");
assertEquals(
List.of(
ResolveDepsPipelineStage.class,
LoadSourcesPipelineStage.class,
FrontendPhasePipelineStage.class,
FrontendPhasePipelineStage.class),
analyzeStages);
assertEquals(
List.of(
LowerToIRVMPipelineStage.class,
OptimizeIRVMPipelineStage.class,
EmitBytecodePipelineStage.class,
LinkBytecodePipelineStage.class,
VerifyBytecodePipelineStage.class,
WriteBytecodeArtifactPipelineStage.class),
stageTypes);
VerifyBytecodePipelineStage.class),
compileStages);
assertEquals(
List.of(WriteBytecodeArtifactPipelineStage.class),
buildStages);
}
@Test
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)
.toList();
@ -47,4 +52,12 @@ class BuilderPipelineServiceOrderTest {
assertTrue(methodNames.contains("build"));
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
@Getter
public class IRBackend {
public class IRBackend implements IRBackendReader {
@Builder.Default
private final String entryPointCallableName = "main";
@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 p.studio.Container;
import p.studio.compiler.messages.BuilderPipelineConfig;
import p.studio.compiler.models.BuilderPipelineContext;
import p.studio.compiler.workspaces.BuilderPipelineService;
import p.studio.projects.ProjectReference;
import p.studio.utilities.i18n.I18n;
@ -67,7 +68,8 @@ public class ShipperWorkspace extends Workspace {
logs.clear();
final var logAggregator = LogAggregator.with(logs::appendText);
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));