implements PR-05.1
This commit is contained in:
parent
4e0f3a3532
commit
541ada32a1
@ -223,97 +223,27 @@ public final class PbsFrontendCompiler {
|
||||
if (functionCallableId == null) {
|
||||
continue;
|
||||
}
|
||||
final var instructions = new ArrayList<IRBackendExecutableFunction.Instruction>();
|
||||
final var callsites = new ArrayList<PbsAst.CallExpr>();
|
||||
collectCallsFromBlock(fn.body(), callsites);
|
||||
for (final var callExpr : callsites) {
|
||||
final var calleeName = extractSimpleCalleeName(callExpr.callee());
|
||||
if (calleeName == null || calleeName.isBlank()) {
|
||||
diagnostics.error(
|
||||
DiagnosticPhase.STATIC_SEMANTICS,
|
||||
PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name(),
|
||||
PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name(),
|
||||
Map.of(),
|
||||
"executable lowering requires resolvable callee identity",
|
||||
callExpr.span());
|
||||
continue;
|
||||
}
|
||||
final var host = hostByMethodName.get(calleeName);
|
||||
if (host != null) {
|
||||
instructions.add(new IRBackendExecutableFunction.Instruction(
|
||||
IRBackendExecutableFunction.InstructionKind.CALL_HOST,
|
||||
"",
|
||||
"",
|
||||
new IRBackendExecutableFunction.HostCallMetadata(
|
||||
host.abiModule(),
|
||||
host.abiMethod(),
|
||||
host.abiVersion(),
|
||||
callExpr.arguments().size(),
|
||||
0),
|
||||
null,
|
||||
callExpr.span()));
|
||||
continue;
|
||||
}
|
||||
|
||||
final var intrinsic = intrinsicByMethodName.get(calleeName);
|
||||
if (intrinsic != null) {
|
||||
instructions.add(new IRBackendExecutableFunction.Instruction(
|
||||
IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC,
|
||||
"",
|
||||
"",
|
||||
null,
|
||||
new IRBackendExecutableFunction.IntrinsicCallMetadata(
|
||||
intrinsic.canonicalName(),
|
||||
intrinsic.canonicalVersion(),
|
||||
intrinsicIdTable
|
||||
.register(intrinsic.canonicalName(), intrinsic.canonicalVersion())
|
||||
),
|
||||
callExpr.span()));
|
||||
continue;
|
||||
}
|
||||
|
||||
final var candidateCallableIds = callableIdsByNameAndArity.get(
|
||||
new CallableResolutionKey(nameTable.register(calleeName), callExpr.arguments().size()));
|
||||
if (candidateCallableIds == null || candidateCallableIds.isEmpty()) {
|
||||
diagnostics.error(
|
||||
DiagnosticPhase.STATIC_SEMANTICS,
|
||||
PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name(),
|
||||
PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name(),
|
||||
Map.of(),
|
||||
"executable lowering requires resolvable callee identity",
|
||||
callExpr.span());
|
||||
continue;
|
||||
}
|
||||
if (candidateCallableIds.size() > 1) {
|
||||
diagnostics.error(
|
||||
DiagnosticPhase.STATIC_SEMANTICS,
|
||||
PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name(),
|
||||
PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name(),
|
||||
Map.of(),
|
||||
"executable lowering found ambiguous callable identity",
|
||||
callExpr.span());
|
||||
continue;
|
||||
}
|
||||
final var calleeCallableId = candidateCallableIds.getFirst();
|
||||
final var loweringContext = new ExecutableLoweringContext(
|
||||
normalizedModuleKey,
|
||||
diagnostics,
|
||||
nameTable,
|
||||
hostByMethodName,
|
||||
intrinsicByMethodName,
|
||||
callableIdsByNameAndArity,
|
||||
returnSlotsByCallableId,
|
||||
intrinsicIdTable);
|
||||
final var terminated = lowerBlock(fn.body(), loweringContext);
|
||||
final var instructions = loweringContext.instructions();
|
||||
if (!terminated) {
|
||||
instructions.add(new IRBackendExecutableFunction.Instruction(
|
||||
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
|
||||
normalizedModuleKey,
|
||||
calleeName,
|
||||
calleeCallableId,
|
||||
IRBackendExecutableFunction.InstructionKind.RET,
|
||||
"",
|
||||
"",
|
||||
null,
|
||||
null,
|
||||
callExpr.arguments().size(),
|
||||
returnSlotsByCallableId.get(calleeCallableId),
|
||||
callExpr.span()));
|
||||
null,
|
||||
fn.span()));
|
||||
}
|
||||
instructions.add(new IRBackendExecutableFunction.Instruction(
|
||||
IRBackendExecutableFunction.InstructionKind.RET,
|
||||
"",
|
||||
"",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
fn.span()));
|
||||
|
||||
final var returnSlots = returnSlotsFor(fn);
|
||||
final int maxStackSlots;
|
||||
@ -449,127 +379,192 @@ public final class PbsFrontendCompiler {
|
||||
return (int) value;
|
||||
}
|
||||
|
||||
private void collectCallsFromBlock(
|
||||
private boolean lowerBlock(
|
||||
final PbsAst.Block block,
|
||||
final List<PbsAst.CallExpr> output) {
|
||||
final ExecutableLoweringContext context) {
|
||||
if (block == null) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
var terminated = false;
|
||||
for (final var statement : block.statements()) {
|
||||
collectCallsFromStatement(statement, output);
|
||||
if (terminated) {
|
||||
break;
|
||||
}
|
||||
terminated = lowerStatement(statement, context);
|
||||
}
|
||||
if (block.tailExpression() != null) {
|
||||
collectCallsFromExpression(block.tailExpression(), output);
|
||||
if (!terminated && block.tailExpression() != null) {
|
||||
lowerExpression(block.tailExpression(), context);
|
||||
}
|
||||
return terminated;
|
||||
}
|
||||
|
||||
private void collectCallsFromStatement(
|
||||
private boolean lowerStatement(
|
||||
final PbsAst.Statement statement,
|
||||
final List<PbsAst.CallExpr> output) {
|
||||
final ExecutableLoweringContext context) {
|
||||
if (statement == null) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
switch (statement) {
|
||||
case PbsAst.LetStatement letStatement -> collectCallsFromExpression(letStatement.initializer(), output);
|
||||
case PbsAst.AssignStatement assignStatement -> collectCallsFromExpression(assignStatement.value(), output);
|
||||
case PbsAst.LetStatement letStatement -> lowerExpression(letStatement.initializer(), context);
|
||||
case PbsAst.AssignStatement assignStatement -> lowerExpression(assignStatement.value(), context);
|
||||
case PbsAst.ReturnStatement returnStatement -> {
|
||||
if (returnStatement.value() != null) {
|
||||
collectCallsFromExpression(returnStatement.value(), output);
|
||||
lowerExpression(returnStatement.value(), context);
|
||||
}
|
||||
context.instructions().add(new IRBackendExecutableFunction.Instruction(
|
||||
IRBackendExecutableFunction.InstructionKind.RET,
|
||||
"",
|
||||
"",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
returnStatement.span()));
|
||||
return true;
|
||||
}
|
||||
case PbsAst.IfStatement ifStatement -> {
|
||||
collectCallsFromExpression(ifStatement.condition(), output);
|
||||
collectCallsFromBlock(ifStatement.thenBlock(), output);
|
||||
lowerExpression(ifStatement.condition(), context);
|
||||
final var elseLabel = context.nextLabel("if_else");
|
||||
final var endLabel = context.nextLabel("if_end");
|
||||
emitJump(IRBackendExecutableFunction.InstructionKind.JMP_IF_FALSE, elseLabel, ifStatement.span(), context);
|
||||
final var thenTerminated = lowerBlock(ifStatement.thenBlock(), context);
|
||||
if (!thenTerminated) {
|
||||
emitJump(IRBackendExecutableFunction.InstructionKind.JMP, endLabel, ifStatement.span(), context);
|
||||
}
|
||||
emitLabel(elseLabel, ifStatement.span(), context);
|
||||
final boolean elseTerminated;
|
||||
if (ifStatement.elseIf() != null) {
|
||||
collectCallsFromStatement(ifStatement.elseIf(), output);
|
||||
elseTerminated = lowerStatement(ifStatement.elseIf(), context);
|
||||
} else if (ifStatement.elseBlock() != null) {
|
||||
elseTerminated = lowerBlock(ifStatement.elseBlock(), context);
|
||||
} else {
|
||||
elseTerminated = false;
|
||||
}
|
||||
if (ifStatement.elseBlock() != null) {
|
||||
collectCallsFromBlock(ifStatement.elseBlock(), output);
|
||||
if (!thenTerminated || !elseTerminated) {
|
||||
emitLabel(endLabel, ifStatement.span(), context);
|
||||
}
|
||||
return (ifStatement.elseIf() != null || ifStatement.elseBlock() != null)
|
||||
&& thenTerminated
|
||||
&& elseTerminated;
|
||||
}
|
||||
case PbsAst.ForStatement forStatement -> {
|
||||
collectCallsFromExpression(forStatement.fromExpression(), output);
|
||||
collectCallsFromExpression(forStatement.untilExpression(), output);
|
||||
lowerExpression(forStatement.fromExpression(), context);
|
||||
final var loopStart = context.nextLabel("for_start");
|
||||
final var loopExit = context.nextLabel("for_exit");
|
||||
final var loopContinue = context.nextLabel("for_continue");
|
||||
emitLabel(loopStart, forStatement.span(), context);
|
||||
lowerExpression(forStatement.untilExpression(), context);
|
||||
emitJump(IRBackendExecutableFunction.InstructionKind.JMP_IF_FALSE, loopExit, forStatement.span(), context);
|
||||
context.pushLoop(loopContinue, loopExit);
|
||||
lowerBlock(forStatement.body(), context);
|
||||
context.popLoop();
|
||||
emitLabel(loopContinue, forStatement.span(), context);
|
||||
if (forStatement.stepExpression() != null) {
|
||||
collectCallsFromExpression(forStatement.stepExpression(), output);
|
||||
lowerExpression(forStatement.stepExpression(), context);
|
||||
}
|
||||
collectCallsFromBlock(forStatement.body(), output);
|
||||
emitJump(IRBackendExecutableFunction.InstructionKind.JMP, loopStart, forStatement.span(), context);
|
||||
emitLabel(loopExit, forStatement.span(), context);
|
||||
}
|
||||
case PbsAst.WhileStatement whileStatement -> {
|
||||
collectCallsFromExpression(whileStatement.condition(), output);
|
||||
collectCallsFromBlock(whileStatement.body(), output);
|
||||
final var loopStart = context.nextLabel("while_start");
|
||||
final var loopExit = context.nextLabel("while_exit");
|
||||
emitLabel(loopStart, whileStatement.span(), context);
|
||||
lowerExpression(whileStatement.condition(), context);
|
||||
emitJump(IRBackendExecutableFunction.InstructionKind.JMP_IF_FALSE, loopExit, whileStatement.span(), context);
|
||||
context.pushLoop(loopStart, loopExit);
|
||||
lowerBlock(whileStatement.body(), context);
|
||||
context.popLoop();
|
||||
emitJump(IRBackendExecutableFunction.InstructionKind.JMP, loopStart, whileStatement.span(), context);
|
||||
emitLabel(loopExit, whileStatement.span(), context);
|
||||
}
|
||||
case PbsAst.ExpressionStatement expressionStatement ->
|
||||
collectCallsFromExpression(expressionStatement.expression(), output);
|
||||
case PbsAst.BreakStatement ignored -> {
|
||||
case PbsAst.ExpressionStatement expressionStatement -> lowerExpression(expressionStatement.expression(), context);
|
||||
case PbsAst.BreakStatement breakStatement -> {
|
||||
if (context.loopTargets().isEmpty()) {
|
||||
reportUnsupportedLowering("break statement outside loop", breakStatement.span(), context);
|
||||
return true;
|
||||
}
|
||||
emitJump(IRBackendExecutableFunction.InstructionKind.JMP, context.loopTargets().peek().breakLabel(), breakStatement.span(), context);
|
||||
return true;
|
||||
}
|
||||
case PbsAst.ContinueStatement ignored -> {
|
||||
case PbsAst.ContinueStatement continueStatement -> {
|
||||
if (context.loopTargets().isEmpty()) {
|
||||
reportUnsupportedLowering("continue statement outside loop", continueStatement.span(), context);
|
||||
return true;
|
||||
}
|
||||
emitJump(IRBackendExecutableFunction.InstructionKind.JMP, context.loopTargets().peek().continueLabel(), continueStatement.span(), context);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void collectCallsFromExpression(
|
||||
private void lowerExpression(
|
||||
final PbsAst.Expression expression,
|
||||
final List<PbsAst.CallExpr> output) {
|
||||
final ExecutableLoweringContext context) {
|
||||
if (expression == null) {
|
||||
return;
|
||||
}
|
||||
switch (expression) {
|
||||
case PbsAst.CallExpr callExpr -> {
|
||||
output.add(callExpr);
|
||||
collectCallsFromExpression(callExpr.callee(), output);
|
||||
lowerExpression(callExpr.callee(), context);
|
||||
for (final var arg : callExpr.arguments()) {
|
||||
collectCallsFromExpression(arg, output);
|
||||
lowerExpression(arg, context);
|
||||
}
|
||||
lowerCallsite(callExpr, context);
|
||||
}
|
||||
case PbsAst.ApplyExpr applyExpr -> {
|
||||
collectCallsFromExpression(applyExpr.callee(), output);
|
||||
collectCallsFromExpression(applyExpr.argument(), output);
|
||||
lowerExpression(applyExpr.callee(), context);
|
||||
lowerExpression(applyExpr.argument(), context);
|
||||
}
|
||||
case PbsAst.BinaryExpr binaryExpr -> {
|
||||
collectCallsFromExpression(binaryExpr.left(), output);
|
||||
collectCallsFromExpression(binaryExpr.right(), output);
|
||||
lowerExpression(binaryExpr.left(), context);
|
||||
lowerExpression(binaryExpr.right(), context);
|
||||
}
|
||||
case PbsAst.UnaryExpr unaryExpr -> collectCallsFromExpression(unaryExpr.expression(), output);
|
||||
case PbsAst.UnaryExpr unaryExpr -> lowerExpression(unaryExpr.expression(), context);
|
||||
case PbsAst.ElseExpr elseExpr -> {
|
||||
collectCallsFromExpression(elseExpr.optionalExpression(), output);
|
||||
collectCallsFromExpression(elseExpr.fallbackExpression(), output);
|
||||
lowerExpression(elseExpr.optionalExpression(), context);
|
||||
lowerExpression(elseExpr.fallbackExpression(), context);
|
||||
}
|
||||
case PbsAst.IfExpr ifExpr -> {
|
||||
collectCallsFromExpression(ifExpr.condition(), output);
|
||||
collectCallsFromBlock(ifExpr.thenBlock(), output);
|
||||
collectCallsFromExpression(ifExpr.elseExpression(), output);
|
||||
lowerExpression(ifExpr.condition(), context);
|
||||
final var elseLabel = context.nextLabel("ifexpr_else");
|
||||
final var endLabel = context.nextLabel("ifexpr_end");
|
||||
emitJump(IRBackendExecutableFunction.InstructionKind.JMP_IF_FALSE, elseLabel, ifExpr.span(), context);
|
||||
final var thenTerminated = lowerBlock(ifExpr.thenBlock(), context);
|
||||
if (!thenTerminated) {
|
||||
emitJump(IRBackendExecutableFunction.InstructionKind.JMP, endLabel, ifExpr.span(), context);
|
||||
}
|
||||
emitLabel(elseLabel, ifExpr.span(), context);
|
||||
lowerExpression(ifExpr.elseExpression(), context);
|
||||
if (!thenTerminated) {
|
||||
emitLabel(endLabel, ifExpr.span(), context);
|
||||
}
|
||||
}
|
||||
case PbsAst.SwitchExpr switchExpr -> {
|
||||
collectCallsFromExpression(switchExpr.selector(), output);
|
||||
for (final var arm : switchExpr.arms()) {
|
||||
collectCallsFromBlock(arm.block(), output);
|
||||
}
|
||||
lowerExpression(switchExpr.selector(), context);
|
||||
reportUnsupportedLowering("switch expression is not lowerable in executable lowering v1", switchExpr.span(), context);
|
||||
}
|
||||
case PbsAst.HandleExpr handleExpr -> {
|
||||
collectCallsFromExpression(handleExpr.value(), output);
|
||||
for (final var arm : handleExpr.arms()) {
|
||||
collectCallsFromBlock(arm.block(), output);
|
||||
}
|
||||
lowerExpression(handleExpr.value(), context);
|
||||
reportUnsupportedLowering("handle expression is not lowerable in executable lowering v1", handleExpr.span(), context);
|
||||
}
|
||||
case PbsAst.AsExpr asExpr -> collectCallsFromExpression(asExpr.expression(), output);
|
||||
case PbsAst.MemberExpr memberExpr -> collectCallsFromExpression(memberExpr.receiver(), output);
|
||||
case PbsAst.PropagateExpr propagateExpr -> collectCallsFromExpression(propagateExpr.expression(), output);
|
||||
case PbsAst.GroupExpr groupExpr -> collectCallsFromExpression(groupExpr.expression(), output);
|
||||
case PbsAst.AsExpr asExpr -> lowerExpression(asExpr.expression(), context);
|
||||
case PbsAst.MemberExpr memberExpr -> lowerExpression(memberExpr.receiver(), context);
|
||||
case PbsAst.PropagateExpr propagateExpr -> lowerExpression(propagateExpr.expression(), context);
|
||||
case PbsAst.GroupExpr groupExpr -> lowerExpression(groupExpr.expression(), context);
|
||||
case PbsAst.NewExpr newExpr -> {
|
||||
for (final var arg : newExpr.arguments()) {
|
||||
collectCallsFromExpression(arg, output);
|
||||
lowerExpression(arg, context);
|
||||
}
|
||||
}
|
||||
case PbsAst.BindExpr bindExpr -> collectCallsFromExpression(bindExpr.contextExpression(), output);
|
||||
case PbsAst.SomeExpr someExpr -> collectCallsFromExpression(someExpr.value(), output);
|
||||
case PbsAst.OkExpr okExpr -> collectCallsFromExpression(okExpr.value(), output);
|
||||
case PbsAst.BindExpr bindExpr -> lowerExpression(bindExpr.contextExpression(), context);
|
||||
case PbsAst.SomeExpr someExpr -> lowerExpression(someExpr.value(), context);
|
||||
case PbsAst.OkExpr okExpr -> lowerExpression(okExpr.value(), context);
|
||||
case PbsAst.TupleExpr tupleExpr -> {
|
||||
for (final var item : tupleExpr.items()) {
|
||||
collectCallsFromExpression(item.expression(), output);
|
||||
lowerExpression(item.expression(), context);
|
||||
}
|
||||
}
|
||||
case PbsAst.BlockExpr blockExpr -> collectCallsFromBlock(blockExpr.block(), output);
|
||||
case PbsAst.BlockExpr blockExpr -> lowerBlock(blockExpr.block(), context);
|
||||
case PbsAst.IdentifierExpr ignored -> {
|
||||
}
|
||||
case PbsAst.IntLiteralExpr ignored -> {
|
||||
@ -593,6 +588,119 @@ public final class PbsFrontendCompiler {
|
||||
}
|
||||
}
|
||||
|
||||
private void lowerCallsite(
|
||||
final PbsAst.CallExpr callExpr,
|
||||
final ExecutableLoweringContext context) {
|
||||
final var calleeName = extractSimpleCalleeName(callExpr.callee());
|
||||
if (calleeName == null || calleeName.isBlank()) {
|
||||
reportUnsupportedLowering("executable lowering requires resolvable callee identity", callExpr.span(), context);
|
||||
return;
|
||||
}
|
||||
final var host = context.hostByMethodName().get(calleeName);
|
||||
if (host != null) {
|
||||
context.instructions().add(new IRBackendExecutableFunction.Instruction(
|
||||
IRBackendExecutableFunction.InstructionKind.CALL_HOST,
|
||||
"",
|
||||
"",
|
||||
new IRBackendExecutableFunction.HostCallMetadata(
|
||||
host.abiModule(),
|
||||
host.abiMethod(),
|
||||
host.abiVersion(),
|
||||
callExpr.arguments().size(),
|
||||
0),
|
||||
null,
|
||||
callExpr.span()));
|
||||
return;
|
||||
}
|
||||
|
||||
final var intrinsic = context.intrinsicByMethodName().get(calleeName);
|
||||
if (intrinsic != null) {
|
||||
context.instructions().add(new IRBackendExecutableFunction.Instruction(
|
||||
IRBackendExecutableFunction.InstructionKind.CALL_INTRINSIC,
|
||||
"",
|
||||
"",
|
||||
null,
|
||||
new IRBackendExecutableFunction.IntrinsicCallMetadata(
|
||||
intrinsic.canonicalName(),
|
||||
intrinsic.canonicalVersion(),
|
||||
context.intrinsicIdTable().register(intrinsic.canonicalName(), intrinsic.canonicalVersion())),
|
||||
callExpr.span()));
|
||||
return;
|
||||
}
|
||||
|
||||
final var candidateCallableIds = context.callableIdsByNameAndArity().get(
|
||||
new CallableResolutionKey(context.nameTable().register(calleeName), callExpr.arguments().size()));
|
||||
if (candidateCallableIds == null || candidateCallableIds.isEmpty()) {
|
||||
reportUnsupportedLowering("executable lowering requires resolvable callee identity", callExpr.span(), context);
|
||||
return;
|
||||
}
|
||||
if (candidateCallableIds.size() > 1) {
|
||||
reportUnsupportedLowering("executable lowering found ambiguous callable identity", callExpr.span(), context);
|
||||
return;
|
||||
}
|
||||
final var calleeCallableId = candidateCallableIds.getFirst();
|
||||
context.instructions().add(new IRBackendExecutableFunction.Instruction(
|
||||
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
|
||||
context.moduleKey(),
|
||||
calleeName,
|
||||
calleeCallableId,
|
||||
null,
|
||||
null,
|
||||
callExpr.arguments().size(),
|
||||
context.returnSlotsByCallableId().get(calleeCallableId),
|
||||
callExpr.span()));
|
||||
}
|
||||
|
||||
private void emitJump(
|
||||
final IRBackendExecutableFunction.InstructionKind jumpKind,
|
||||
final String targetLabel,
|
||||
final p.studio.compiler.source.Span span,
|
||||
final ExecutableLoweringContext context) {
|
||||
context.instructions().add(new IRBackendExecutableFunction.Instruction(
|
||||
jumpKind,
|
||||
"",
|
||||
"",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"",
|
||||
targetLabel,
|
||||
null,
|
||||
null,
|
||||
span));
|
||||
}
|
||||
|
||||
private void emitLabel(
|
||||
final String label,
|
||||
final p.studio.compiler.source.Span span,
|
||||
final ExecutableLoweringContext context) {
|
||||
context.instructions().add(new IRBackendExecutableFunction.Instruction(
|
||||
IRBackendExecutableFunction.InstructionKind.LABEL,
|
||||
"",
|
||||
"",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
label,
|
||||
"",
|
||||
null,
|
||||
null,
|
||||
span));
|
||||
}
|
||||
|
||||
private void reportUnsupportedLowering(
|
||||
final String message,
|
||||
final p.studio.compiler.source.Span span,
|
||||
final ExecutableLoweringContext context) {
|
||||
context.diagnostics().error(
|
||||
DiagnosticPhase.STATIC_SEMANTICS,
|
||||
PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name(),
|
||||
PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name(),
|
||||
Map.of(),
|
||||
message,
|
||||
span);
|
||||
}
|
||||
|
||||
private String extractSimpleCalleeName(final PbsAst.Expression callee) {
|
||||
return switch (callee) {
|
||||
case PbsAst.IdentifierExpr identifierExpr -> identifierExpr.name();
|
||||
@ -700,6 +808,98 @@ public final class PbsFrontendCompiler {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ExecutableLoweringContext {
|
||||
private final String moduleKey;
|
||||
private final DiagnosticSink diagnostics;
|
||||
private final NameTable nameTable;
|
||||
private final Map<String, IRReservedMetadata.HostMethodBinding> hostByMethodName;
|
||||
private final Map<String, IRReservedMetadata.IntrinsicSurface> intrinsicByMethodName;
|
||||
private final Map<CallableResolutionKey, List<CallableId>> callableIdsByNameAndArity;
|
||||
private final Map<CallableId, Integer> returnSlotsByCallableId;
|
||||
private final IntrinsicTable intrinsicIdTable;
|
||||
private final ArrayList<IRBackendExecutableFunction.Instruction> instructions = new ArrayList<>();
|
||||
private final ArrayDeque<LoopTargets> loopTargets = new ArrayDeque<>();
|
||||
private int nextLabelId = 0;
|
||||
|
||||
private ExecutableLoweringContext(
|
||||
final String moduleKey,
|
||||
final DiagnosticSink diagnostics,
|
||||
final NameTable nameTable,
|
||||
final Map<String, IRReservedMetadata.HostMethodBinding> hostByMethodName,
|
||||
final Map<String, IRReservedMetadata.IntrinsicSurface> intrinsicByMethodName,
|
||||
final Map<CallableResolutionKey, List<CallableId>> callableIdsByNameAndArity,
|
||||
final Map<CallableId, Integer> returnSlotsByCallableId,
|
||||
final IntrinsicTable intrinsicIdTable) {
|
||||
this.moduleKey = moduleKey;
|
||||
this.diagnostics = diagnostics;
|
||||
this.nameTable = nameTable;
|
||||
this.hostByMethodName = hostByMethodName;
|
||||
this.intrinsicByMethodName = intrinsicByMethodName;
|
||||
this.callableIdsByNameAndArity = callableIdsByNameAndArity;
|
||||
this.returnSlotsByCallableId = returnSlotsByCallableId;
|
||||
this.intrinsicIdTable = intrinsicIdTable;
|
||||
}
|
||||
|
||||
private String moduleKey() {
|
||||
return moduleKey;
|
||||
}
|
||||
|
||||
private DiagnosticSink diagnostics() {
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
private NameTable nameTable() {
|
||||
return nameTable;
|
||||
}
|
||||
|
||||
private Map<String, IRReservedMetadata.HostMethodBinding> hostByMethodName() {
|
||||
return hostByMethodName;
|
||||
}
|
||||
|
||||
private Map<String, IRReservedMetadata.IntrinsicSurface> intrinsicByMethodName() {
|
||||
return intrinsicByMethodName;
|
||||
}
|
||||
|
||||
private Map<CallableResolutionKey, List<CallableId>> callableIdsByNameAndArity() {
|
||||
return callableIdsByNameAndArity;
|
||||
}
|
||||
|
||||
private Map<CallableId, Integer> returnSlotsByCallableId() {
|
||||
return returnSlotsByCallableId;
|
||||
}
|
||||
|
||||
private IntrinsicTable intrinsicIdTable() {
|
||||
return intrinsicIdTable;
|
||||
}
|
||||
|
||||
private ArrayList<IRBackendExecutableFunction.Instruction> instructions() {
|
||||
return instructions;
|
||||
}
|
||||
|
||||
private ArrayDeque<LoopTargets> loopTargets() {
|
||||
return loopTargets;
|
||||
}
|
||||
|
||||
private void pushLoop(
|
||||
final String continueLabel,
|
||||
final String breakLabel) {
|
||||
loopTargets.push(new LoopTargets(continueLabel, breakLabel));
|
||||
}
|
||||
|
||||
private void popLoop() {
|
||||
loopTargets.pop();
|
||||
}
|
||||
|
||||
private String nextLabel(final String prefix) {
|
||||
return "__" + prefix + "_" + nextLabelId++;
|
||||
}
|
||||
}
|
||||
|
||||
private record LoopTargets(
|
||||
String continueLabel,
|
||||
String breakLabel) {
|
||||
}
|
||||
|
||||
private record ExecutableLoweringResult(
|
||||
ReadOnlyList<IRBackendExecutableFunction> executableFunctions,
|
||||
ReadOnlyList<CallableSignatureRef> callableSignatures,
|
||||
|
||||
@ -123,6 +123,67 @@ class PbsFrontendCompilerTest {
|
||||
assertEquals("c", executableA.instructions().get(1).calleeCallableName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldStopLoweringReachablePathAfterReturn() {
|
||||
final var source = """
|
||||
fn a() -> int { return 1; }
|
||||
fn b() -> int { return 2; }
|
||||
fn main() -> int {
|
||||
return a();
|
||||
b();
|
||||
}
|
||||
""";
|
||||
|
||||
final var diagnostics = DiagnosticSink.empty();
|
||||
final var compiler = new PbsFrontendCompiler();
|
||||
final var fileBackend = compiler.compileFile(new FileId(100), source, diagnostics);
|
||||
|
||||
assertTrue(diagnostics.isEmpty(), "Valid program should not report diagnostics");
|
||||
final var executableMain = fileBackend.executableFunctions().stream()
|
||||
.filter(fn -> fn.callableName().equals("main"))
|
||||
.findFirst()
|
||||
.orElseThrow();
|
||||
assertEquals(2, executableMain.instructions().size());
|
||||
assertEquals("a", executableMain.instructions().get(0).calleeCallableName());
|
||||
assertEquals(p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.RET, executableMain.instructions().get(1).kind());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldEmitControlFlowInstructionsForIfAndWhile() {
|
||||
final var source = """
|
||||
fn cond() -> bool { return true; }
|
||||
fn t() -> int { return 1; }
|
||||
fn f() -> int { return 2; }
|
||||
fn loopBody() -> void { return; }
|
||||
fn main() -> int {
|
||||
while cond() {
|
||||
break;
|
||||
}
|
||||
if cond() {
|
||||
return t();
|
||||
} else {
|
||||
return f();
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
final var diagnostics = DiagnosticSink.empty();
|
||||
final var compiler = new PbsFrontendCompiler();
|
||||
final var fileBackend = compiler.compileFile(new FileId(101), source, diagnostics);
|
||||
|
||||
assertTrue(diagnostics.isEmpty(), "Valid program should not report diagnostics");
|
||||
final var executableMain = fileBackend.executableFunctions().stream()
|
||||
.filter(fn -> fn.callableName().equals("main"))
|
||||
.findFirst()
|
||||
.orElseThrow();
|
||||
assertTrue(executableMain.instructions().stream().anyMatch(i ->
|
||||
i.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.LABEL));
|
||||
assertTrue(executableMain.instructions().stream().anyMatch(i ->
|
||||
i.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.JMP));
|
||||
assertTrue(executableMain.instructions().stream().anyMatch(i ->
|
||||
i.kind() == p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.JMP_IF_FALSE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotLowerWhenSyntaxErrorsExist() {
|
||||
final var source = """
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user