implements PR-O1.4

This commit is contained in:
bQUARKz 2026-03-07 18:01:49 +00:00
parent 3e14c13979
commit e36c59fe26
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
4 changed files with 78 additions and 7 deletions

View File

@ -158,6 +158,17 @@ public final class PbsFrontendCompiler {
intrinsicByMethodName.put(intrinsicSurface.sourceMethodName(), intrinsicSurface);
}
}
final var returnSlotsByCallableAndArity = new HashMap<String, Integer>();
for (final var declaredFn : ast.functions()) {
final var key = callableArityKey(declaredFn.name(), declaredFn.parameters().size());
final var retSlots = returnSlotsFor(declaredFn);
if (returnSlotsByCallableAndArity.containsKey(key)
&& !returnSlotsByCallableAndArity.get(key).equals(retSlots)) {
returnSlotsByCallableAndArity.put(key, null);
} else {
returnSlotsByCallableAndArity.put(key, retSlots);
}
}
final var executableFunctions = new ArrayList<IRBackendExecutableFunction>(ast.functions().size());
for (final var fn : ast.functions()) {
@ -221,6 +232,8 @@ public final class PbsFrontendCompiler {
calleeName,
null,
null,
callExpr.arguments().size(),
returnSlotsByCallableAndArity.get(callableArityKey(calleeName, callExpr.arguments().size())),
callExpr.span()));
}
instructions.add(new IRBackendExecutableFunction.Instruction(
@ -231,10 +244,7 @@ public final class PbsFrontendCompiler {
null,
fn.span()));
final var returnSlots = switch (fn.returnKind()) {
case INFERRED_UNIT, EXPLICIT_UNIT -> 0;
case PLAIN, RESULT -> 1;
};
final var returnSlots = returnSlotsFor(fn);
final var start = safeToInt(fn.span().getStart());
final var end = safeToInt(fn.span().getEnd());
executableFunctions.add(new IRBackendExecutableFunction(
@ -259,6 +269,19 @@ public final class PbsFrontendCompiler {
return (canonicalName + "#" + canonicalVersion).hashCode();
}
private int returnSlotsFor(final PbsAst.FunctionDecl functionDecl) {
return switch (functionDecl.returnKind()) {
case INFERRED_UNIT, EXPLICIT_UNIT -> 0;
case PLAIN, RESULT -> 1;
};
}
private String callableArityKey(
final String callableName,
final int arity) {
return callableName + "#" + arity;
}
private int safeToInt(final long value) {
if (value > Integer.MAX_VALUE) {
return Integer.MAX_VALUE;

View File

@ -3,5 +3,6 @@ package p.studio.compiler.backend.irvm;
public enum IRVMLoweringErrorCode {
LOWER_IRVM_EMPTY_EXECUTABLE_INPUT,
LOWER_IRVM_MISSING_CALLEE,
LOWER_IRVM_CALL_ARG_SLOTS_MISMATCH,
LOWER_IRVM_CALL_RET_SLOTS_MISMATCH,
}

View File

@ -65,6 +65,25 @@ public class LowerToIRVMService {
IRVMLoweringErrorCode.LOWER_IRVM_MISSING_CALLEE,
"missing callee function: " + key);
}
final var calleeFunction = ordered.get(calleeId);
if (instr.expectedArgSlots() != null
&& instr.expectedArgSlots() != calleeFunction.paramSlots()) {
throw new IRVMLoweringException(
IRVMLoweringErrorCode.LOWER_IRVM_CALL_ARG_SLOTS_MISMATCH,
"call arg_slots mismatch for %s. expected=%d actual=%d".formatted(
key,
instr.expectedArgSlots(),
calleeFunction.paramSlots()));
}
if (instr.expectedRetSlots() != null
&& instr.expectedRetSlots() != calleeFunction.returnSlots()) {
throw new IRVMLoweringException(
IRVMLoweringErrorCode.LOWER_IRVM_CALL_RET_SLOTS_MISMATCH,
"call ret_slots mismatch for %s. expected=%d actual=%d".formatted(
key,
instr.expectedRetSlots(),
calleeFunction.returnSlots()));
}
instructions.add(new IRVMInstruction(IRVMOp.CALL, calleeId));
operations.add(BytecodeEmitter.Operation.callFunc(calleeId));
}
@ -142,4 +161,3 @@ public class LowerToIRVMService {
return (moduleKey == null ? "" : moduleKey) + "::" + callableName;
}
}

View File

@ -79,6 +79,20 @@ class LowerToIRVMServiceTest {
assertEquals(IRVMLoweringErrorCode.LOWER_IRVM_MISSING_CALLEE, thrown.code());
}
@Test
void lowerMustRejectCallArgSlotMismatch() {
final var backend = IRBackend.builder()
.executableFunctions(ReadOnlyList.from(
fn("callee", "app", ReadOnlyList.from(ret())),
fn("main", "app", ReadOnlyList.from(
callFuncWithExpected("app", "callee", 2, 0),
ret()))))
.build();
final var thrown = assertThrows(IRVMLoweringException.class, () -> new LowerToIRVMService().lower(backend));
assertEquals(IRVMLoweringErrorCode.LOWER_IRVM_CALL_ARG_SLOTS_MISMATCH, thrown.code());
}
private static IRBackendExecutableFunction fn(
final String name,
final String moduleKey,
@ -117,6 +131,22 @@ class LowerToIRVMServiceTest {
Span.none());
}
private static IRBackendExecutableFunction.Instruction callFuncWithExpected(
final String moduleKey,
final String name,
final int expectedArgSlots,
final int expectedRetSlots) {
return new IRBackendExecutableFunction.Instruction(
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
moduleKey,
name,
null,
null,
expectedArgSlots,
expectedRetSlots,
Span.none());
}
private static IRBackendExecutableFunction.Instruction callHost(
final String module,
final String name,
@ -145,4 +175,3 @@ class LowerToIRVMServiceTest {
Span.none());
}
}