implements PR-O4.3
This commit is contained in:
parent
3449771f33
commit
9e9ce76c6c
@ -23,9 +23,12 @@ import p.studio.compiler.source.tables.IntrinsicTable;
|
||||
import p.studio.utilities.structures.ReadOnlyList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public final class PbsFrontendCompiler {
|
||||
private final PbsDeclarationSemanticsValidator declarationSemanticsValidator = new PbsDeclarationSemanticsValidator();
|
||||
@ -198,13 +201,6 @@ public final class PbsFrontendCompiler {
|
||||
final var instructions = new ArrayList<IRBackendExecutableFunction.Instruction>();
|
||||
final var callsites = new ArrayList<PbsAst.CallExpr>();
|
||||
collectCallsFromBlock(fn.body(), callsites);
|
||||
callsites.sort((a, b) -> {
|
||||
final var deltaStart = Long.compare(a.span().getStart(), b.span().getStart());
|
||||
if (deltaStart != 0) {
|
||||
return deltaStart;
|
||||
}
|
||||
return Long.compare(a.span().getEnd(), b.span().getEnd());
|
||||
});
|
||||
for (final var callExpr : callsites) {
|
||||
final var calleeName = extractSimpleCalleeName(callExpr.callee());
|
||||
if (calleeName == null || calleeName.isBlank()) {
|
||||
@ -294,6 +290,19 @@ public final class PbsFrontendCompiler {
|
||||
fn.span()));
|
||||
|
||||
final var returnSlots = returnSlotsFor(fn);
|
||||
final int maxStackSlots;
|
||||
try {
|
||||
maxStackSlots = analyzeMaxStackSlots(ReadOnlyList.wrap(instructions), returnSlots);
|
||||
} catch (ExecutableLoweringAnalysisException e) {
|
||||
diagnostics.error(
|
||||
DiagnosticPhase.STATIC_SEMANTICS,
|
||||
PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name(),
|
||||
PbsSemanticsErrors.E_SEM_EXEC_LOWERING_UNRESOLVED_CALLEE.name(),
|
||||
Map.of(),
|
||||
e.getMessage(),
|
||||
fn.span());
|
||||
continue;
|
||||
}
|
||||
final var start = safeToInt(fn.span().getStart());
|
||||
final var end = safeToInt(fn.span().getEnd());
|
||||
executableFunctions.add(new IRBackendExecutableFunction(
|
||||
@ -306,7 +315,7 @@ public final class PbsFrontendCompiler {
|
||||
fn.parameters().size(),
|
||||
0,
|
||||
returnSlots,
|
||||
Math.max(4, fn.parameters().size() + 2),
|
||||
maxStackSlots,
|
||||
ReadOnlyList.wrap(instructions),
|
||||
fn.span()));
|
||||
}
|
||||
@ -555,9 +564,112 @@ public final class PbsFrontendCompiler {
|
||||
};
|
||||
}
|
||||
|
||||
private int analyzeMaxStackSlots(
|
||||
final ReadOnlyList<IRBackendExecutableFunction.Instruction> instructions,
|
||||
final int returnSlots) {
|
||||
if (instructions.isEmpty()) {
|
||||
return Math.max(1, returnSlots);
|
||||
}
|
||||
|
||||
final var labelToIndex = new HashMap<String, Integer>();
|
||||
for (var i = 0; i < instructions.size(); i++) {
|
||||
final var instruction = instructions.get(i);
|
||||
if (instruction.kind() == IRBackendExecutableFunction.InstructionKind.LABEL) {
|
||||
if (labelToIndex.putIfAbsent(instruction.label(), i) != null) {
|
||||
throw new ExecutableLoweringAnalysisException("duplicate label during stack analysis: " + instruction.label());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final var incomingHeightByInstruction = new HashMap<Integer, Integer>();
|
||||
final var worklist = new ArrayDeque<Integer>();
|
||||
final var visited = new HashSet<Integer>();
|
||||
incomingHeightByInstruction.put(0, 0);
|
||||
worklist.add(0);
|
||||
|
||||
var maxHeight = 0;
|
||||
while (!worklist.isEmpty()) {
|
||||
final var index = worklist.removeFirst();
|
||||
visited.add(index);
|
||||
final var instruction = instructions.get(index);
|
||||
var outHeight = incomingHeightByInstruction.get(index);
|
||||
|
||||
switch (instruction.kind()) {
|
||||
case CALL_FUNC -> outHeight += instruction.expectedRetSlots() == null ? 0 : instruction.expectedRetSlots();
|
||||
case CALL_HOST -> outHeight += instruction.hostCall() == null ? 0 : instruction.hostCall().retSlots();
|
||||
case CALL_INTRINSIC, HALT, LABEL, JMP, RET -> {
|
||||
}
|
||||
case JMP_IF_TRUE, JMP_IF_FALSE -> {
|
||||
if (outHeight <= 0) {
|
||||
throw new ExecutableLoweringAnalysisException("conditional jump requires stack height > 0");
|
||||
}
|
||||
outHeight -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
maxHeight = Math.max(maxHeight, outHeight);
|
||||
if (instruction.kind() == IRBackendExecutableFunction.InstructionKind.RET
|
||||
|| instruction.kind() == IRBackendExecutableFunction.InstructionKind.HALT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final var successors = new ArrayList<Integer>(2);
|
||||
if (instruction.kind() == IRBackendExecutableFunction.InstructionKind.JMP
|
||||
|| instruction.kind() == IRBackendExecutableFunction.InstructionKind.JMP_IF_TRUE
|
||||
|| instruction.kind() == IRBackendExecutableFunction.InstructionKind.JMP_IF_FALSE) {
|
||||
final var targetIndex = labelToIndex.get(instruction.targetLabel());
|
||||
if (targetIndex == null) {
|
||||
throw new ExecutableLoweringAnalysisException("missing jump target label: " + instruction.targetLabel());
|
||||
}
|
||||
successors.add(targetIndex);
|
||||
if (instruction.kind() == IRBackendExecutableFunction.InstructionKind.JMP) {
|
||||
mergeIncomingHeights(incomingHeightByInstruction, worklist, targetIndex, outHeight);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
final var nextIndex = index + 1;
|
||||
if (nextIndex >= instructions.size()) {
|
||||
throw new ExecutableLoweringAnalysisException("reachable path ends without terminal instruction");
|
||||
}
|
||||
successors.add(nextIndex);
|
||||
for (final var successor : successors) {
|
||||
mergeIncomingHeights(incomingHeightByInstruction, worklist, successor, outHeight);
|
||||
}
|
||||
}
|
||||
|
||||
if (!visited.contains(instructions.size() - 1)) {
|
||||
throw new ExecutableLoweringAnalysisException("entrypoint does not reach function tail");
|
||||
}
|
||||
return Math.max(1, Math.max(maxHeight, returnSlots));
|
||||
}
|
||||
|
||||
private void mergeIncomingHeights(
|
||||
final Map<Integer, Integer> incomingHeightByInstruction,
|
||||
final ArrayDeque<Integer> worklist,
|
||||
final int targetIndex,
|
||||
final int newHeight) {
|
||||
final var existingHeight = incomingHeightByInstruction.get(targetIndex);
|
||||
if (existingHeight == null) {
|
||||
incomingHeightByInstruction.put(targetIndex, newHeight);
|
||||
worklist.add(targetIndex);
|
||||
return;
|
||||
}
|
||||
if (existingHeight != newHeight) {
|
||||
throw new ExecutableLoweringAnalysisException(
|
||||
"stack mismatch at join: existing=%d new=%d".formatted(existingHeight, newHeight));
|
||||
}
|
||||
}
|
||||
|
||||
private record ExecutableLoweringResult(
|
||||
ReadOnlyList<IRBackendExecutableFunction> executableFunctions,
|
||||
ReadOnlyList<CallableSignatureRef> callableSignatures,
|
||||
ReadOnlyList<IntrinsicReference> intrinsicPool) {
|
||||
}
|
||||
|
||||
private static final class ExecutableLoweringAnalysisException extends RuntimeException {
|
||||
private ExecutableLoweringAnalysisException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user