implements PR-035
This commit is contained in:
parent
69da7a5924
commit
68bd72bb21
@ -0,0 +1,17 @@
|
||||
package p.studio.compiler.backend.irvm;
|
||||
|
||||
import p.studio.compiler.backend.bytecode.BytecodeEmitter;
|
||||
|
||||
public record IRVMProgram(
|
||||
boolean hasInternalOpcodes,
|
||||
BytecodeEmitter.EmissionPlan emissionPlan) {
|
||||
|
||||
public IRVMProgram {
|
||||
emissionPlan = emissionPlan == null ? BytecodeEmitter.EmissionPlan.empty() : emissionPlan;
|
||||
}
|
||||
|
||||
public static IRVMProgram empty() {
|
||||
return new IRVMProgram(false, BytecodeEmitter.EmissionPlan.empty());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package p.studio.compiler.models;
|
||||
|
||||
import p.studio.compiler.backend.bytecode.BytecodeModule;
|
||||
import p.studio.compiler.backend.irvm.IRVMProgram;
|
||||
import p.studio.compiler.messages.BuilderPipelineConfig;
|
||||
import p.studio.compiler.source.tables.FileTable;
|
||||
import p.studio.compiler.utilities.SourceProviderFactory;
|
||||
@ -15,6 +17,10 @@ public class BuilderPipelineContext {
|
||||
|
||||
public FileTable fileTable;
|
||||
public IRBackend irBackend;
|
||||
public IRVMProgram irvm;
|
||||
public IRVMProgram optimizedIrvm;
|
||||
public BytecodeModule bytecodeModule;
|
||||
public byte[] bytecodeBytes;
|
||||
|
||||
private BuilderPipelineContext(
|
||||
final BuilderPipelineConfig config,
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
package p.studio.compiler.workspaces.stages;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import p.studio.compiler.backend.bytecode.BytecodeEmitter;
|
||||
import p.studio.compiler.backend.bytecode.BytecodeMarshalingException;
|
||||
import p.studio.compiler.messages.BuildingIssueSink;
|
||||
import p.studio.compiler.models.BuilderPipelineContext;
|
||||
import p.studio.compiler.workspaces.PipelineStage;
|
||||
@ -8,8 +10,41 @@ import p.studio.utilities.logs.LogAggregator;
|
||||
|
||||
@Slf4j
|
||||
public class EmitBytecodePipelineStage implements PipelineStage {
|
||||
private final BytecodeEmitter emitter;
|
||||
|
||||
public EmitBytecodePipelineStage() {
|
||||
this(new BytecodeEmitter());
|
||||
}
|
||||
|
||||
EmitBytecodePipelineStage(final BytecodeEmitter emitter) {
|
||||
this.emitter = emitter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuildingIssueSink run(BuilderPipelineContext ctx, LogAggregator logs) {
|
||||
if (ctx.optimizedIrvm == null) {
|
||||
return BuildingIssueSink.empty()
|
||||
.report(builder -> builder
|
||||
.error(true)
|
||||
.message("[BUILD]: optimized IRVM is missing before EmitBytecode stage"));
|
||||
}
|
||||
if (ctx.optimizedIrvm.hasInternalOpcodes()) {
|
||||
return BuildingIssueSink.empty()
|
||||
.report(builder -> builder
|
||||
.error(true)
|
||||
.message("[BUILD]: optimized IRVM still contains internal opcodes"));
|
||||
}
|
||||
try {
|
||||
ctx.bytecodeModule = emitter.emit(ctx.optimizedIrvm.emissionPlan());
|
||||
ctx.bytecodeBytes = ctx.bytecodeModule.serialize();
|
||||
} catch (BytecodeMarshalingException e) {
|
||||
return BuildingIssueSink.empty()
|
||||
.report(builder -> builder
|
||||
.error(true)
|
||||
.message("[BUILD]: bytecode marshaling failed (%s): %s"
|
||||
.formatted(e.code().name(), e.getMessage()))
|
||||
.exception(e));
|
||||
}
|
||||
return BuildingIssueSink.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,69 @@
|
||||
package p.studio.compiler.workspaces.stages;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import p.studio.compiler.backend.bytecode.BytecodeEmitter;
|
||||
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 EmitBytecodePipelineStageTest {
|
||||
|
||||
@Test
|
||||
void runMustFailWhenOptimizedIrvmIsMissing() {
|
||||
final var ctx = BuilderPipelineContext.compilerContext(new BuilderPipelineConfig(false, "."));
|
||||
final var stage = new EmitBytecodePipelineStage();
|
||||
|
||||
final var issues = stage.run(ctx, LogAggregator.empty());
|
||||
|
||||
assertTrue(issues.hasErrors());
|
||||
assertEquals(1, issues.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void runMustFailWhenInternalOpcodesRemain() {
|
||||
final var ctx = BuilderPipelineContext.compilerContext(new BuilderPipelineConfig(false, "."));
|
||||
ctx.optimizedIrvm = new IRVMProgram(true, BytecodeEmitter.EmissionPlan.empty());
|
||||
final var stage = new EmitBytecodePipelineStage();
|
||||
|
||||
final var issues = stage.run(ctx, LogAggregator.empty());
|
||||
|
||||
assertTrue(issues.hasErrors());
|
||||
assertEquals(1, issues.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void runMustEmitBytecodeWhenPreconditionsAreSatisfied() {
|
||||
final var ctx = BuilderPipelineContext.compilerContext(new BuilderPipelineConfig(false, "."));
|
||||
ctx.optimizedIrvm = new IRVMProgram(
|
||||
false,
|
||||
new BytecodeEmitter.EmissionPlan(
|
||||
0,
|
||||
ReadOnlyList.empty(),
|
||||
ReadOnlyList.empty(),
|
||||
ReadOnlyList.from(
|
||||
new BytecodeEmitter.FunctionPlan(
|
||||
"main",
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
ReadOnlyList.from(BytecodeEmitter.Operation.halt())))));
|
||||
final var stage = new EmitBytecodePipelineStage();
|
||||
|
||||
final var issues = stage.run(ctx, LogAggregator.empty());
|
||||
|
||||
assertFalse(issues.hasErrors());
|
||||
assertNotNull(ctx.bytecodeModule);
|
||||
assertNotNull(ctx.bytecodeBytes);
|
||||
assertTrue(ctx.bytecodeBytes.length > 0);
|
||||
assertEquals(0, ctx.bytecodeModule.syscalls().size());
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user