implements PR-O4.1
This commit is contained in:
parent
bbcb65b048
commit
d4f6d96437
@ -16,6 +16,8 @@ import p.studio.compiler.pbs.semantics.PbsSemanticsErrors;
|
|||||||
import p.studio.compiler.source.diagnostics.DiagnosticSink;
|
import p.studio.compiler.source.diagnostics.DiagnosticSink;
|
||||||
import p.studio.compiler.source.diagnostics.DiagnosticPhase;
|
import p.studio.compiler.source.diagnostics.DiagnosticPhase;
|
||||||
import p.studio.compiler.source.identifiers.FileId;
|
import p.studio.compiler.source.identifiers.FileId;
|
||||||
|
import p.studio.compiler.source.tables.CallableSignatureRef;
|
||||||
|
import p.studio.compiler.source.tables.CallableTable;
|
||||||
import p.studio.compiler.source.tables.IntrinsicTable;
|
import p.studio.compiler.source.tables.IntrinsicTable;
|
||||||
import p.studio.utilities.structures.ReadOnlyList;
|
import p.studio.utilities.structures.ReadOnlyList;
|
||||||
|
|
||||||
@ -121,13 +123,18 @@ public final class PbsFrontendCompiler {
|
|||||||
? ReadOnlyList.empty()
|
? ReadOnlyList.empty()
|
||||||
: lowerFunctions(fileId, ast);
|
: lowerFunctions(fileId, ast);
|
||||||
final var loweringErrorBaseline = diagnostics.errorCount();
|
final var loweringErrorBaseline = diagnostics.errorCount();
|
||||||
final ReadOnlyList<IRBackendExecutableFunction> executableFunctions = sourceKind == SourceKind.SDK_INTERFACE
|
final var executableLowering = sourceKind == SourceKind.SDK_INTERFACE
|
||||||
? ReadOnlyList.empty()
|
? new ExecutableLoweringResult(ReadOnlyList.empty(), ReadOnlyList.empty())
|
||||||
: lowerExecutableFunctions(fileId, ast, moduleKey, reservedMetadata, diagnostics);
|
: lowerExecutableFunctions(fileId, ast, moduleKey, reservedMetadata, diagnostics);
|
||||||
if (diagnostics.errorCount() > loweringErrorBaseline) {
|
if (diagnostics.errorCount() > loweringErrorBaseline) {
|
||||||
return IRBackendFile.empty(fileId);
|
return IRBackendFile.empty(fileId);
|
||||||
}
|
}
|
||||||
return new IRBackendFile(fileId, functions, executableFunctions, reservedMetadata);
|
return new IRBackendFile(
|
||||||
|
fileId,
|
||||||
|
functions,
|
||||||
|
executableLowering.executableFunctions(),
|
||||||
|
reservedMetadata,
|
||||||
|
executableLowering.callableSignatures());
|
||||||
}
|
}
|
||||||
|
|
||||||
private ReadOnlyList<IRFunction> lowerFunctions(final FileId fileId, final PbsAst.File ast) {
|
private ReadOnlyList<IRFunction> lowerFunctions(final FileId fileId, final PbsAst.File ast) {
|
||||||
@ -143,12 +150,13 @@ public final class PbsFrontendCompiler {
|
|||||||
return ReadOnlyList.wrap(functions);
|
return ReadOnlyList.wrap(functions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ReadOnlyList<IRBackendExecutableFunction> lowerExecutableFunctions(
|
private ExecutableLoweringResult lowerExecutableFunctions(
|
||||||
final FileId fileId,
|
final FileId fileId,
|
||||||
final PbsAst.File ast,
|
final PbsAst.File ast,
|
||||||
final String moduleKey,
|
final String moduleKey,
|
||||||
final IRReservedMetadata reservedMetadata,
|
final IRReservedMetadata reservedMetadata,
|
||||||
final DiagnosticSink diagnostics) {
|
final DiagnosticSink diagnostics) {
|
||||||
|
final var normalizedModuleKey = moduleKey == null ? "" : moduleKey;
|
||||||
final var hostByMethodName = new HashMap<String, IRReservedMetadata.HostMethodBinding>();
|
final var hostByMethodName = new HashMap<String, IRReservedMetadata.HostMethodBinding>();
|
||||||
for (final var hostBinding : reservedMetadata.hostMethodBindings()) {
|
for (final var hostBinding : reservedMetadata.hostMethodBindings()) {
|
||||||
hostByMethodName.put(hostBinding.sourceMethodName(), hostBinding);
|
hostByMethodName.put(hostBinding.sourceMethodName(), hostBinding);
|
||||||
@ -159,21 +167,32 @@ public final class PbsFrontendCompiler {
|
|||||||
intrinsicByMethodName.put(intrinsicSurface.sourceMethodName(), intrinsicSurface);
|
intrinsicByMethodName.put(intrinsicSurface.sourceMethodName(), intrinsicSurface);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final var returnSlotsByCallableAndArity = new HashMap<String, Integer>();
|
final var callableIdTable = new CallableTable();
|
||||||
|
final var callableIdsByNameAndArity = new HashMap<String, List<Integer>>();
|
||||||
|
final var callableIdByDeclaration = new HashMap<PbsAst.FunctionDecl, Integer>();
|
||||||
|
final var returnSlotsByCallableId = new HashMap<Integer, Integer>();
|
||||||
final var intrinsicIdTable = new IntrinsicTable();
|
final var intrinsicIdTable = new IntrinsicTable();
|
||||||
for (final var declaredFn : ast.functions()) {
|
for (final var declaredFn : ast.functions()) {
|
||||||
final var key = callableArityKey(declaredFn.name(), declaredFn.parameters().size());
|
final var callableId = callableIdTable.register(
|
||||||
|
normalizedModuleKey,
|
||||||
|
declaredFn.name(),
|
||||||
|
declaredFn.parameters().size(),
|
||||||
|
callableShapeKey(declaredFn))
|
||||||
|
.getId();
|
||||||
|
callableIdByDeclaration.put(declaredFn, callableId);
|
||||||
|
callableIdsByNameAndArity
|
||||||
|
.computeIfAbsent(callableArityKey(declaredFn.name(), declaredFn.parameters().size()), ignored -> new ArrayList<>())
|
||||||
|
.add(callableId);
|
||||||
final var retSlots = returnSlotsFor(declaredFn);
|
final var retSlots = returnSlotsFor(declaredFn);
|
||||||
if (returnSlotsByCallableAndArity.containsKey(key)
|
returnSlotsByCallableId.put(callableId, retSlots);
|
||||||
&& !returnSlotsByCallableAndArity.get(key).equals(retSlots)) {
|
|
||||||
returnSlotsByCallableAndArity.put(key, null);
|
|
||||||
} else {
|
|
||||||
returnSlotsByCallableAndArity.put(key, retSlots);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final var executableFunctions = new ArrayList<IRBackendExecutableFunction>(ast.functions().size());
|
final var executableFunctions = new ArrayList<IRBackendExecutableFunction>(ast.functions().size());
|
||||||
for (final var fn : ast.functions()) {
|
for (final var fn : ast.functions()) {
|
||||||
|
final var functionCallableId = callableIdByDeclaration.get(fn);
|
||||||
|
if (functionCallableId == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
final var instructions = new ArrayList<IRBackendExecutableFunction.Instruction>();
|
final var instructions = new ArrayList<IRBackendExecutableFunction.Instruction>();
|
||||||
final var callsites = new ArrayList<PbsAst.CallExpr>();
|
final var callsites = new ArrayList<PbsAst.CallExpr>();
|
||||||
collectCallsFromBlock(fn.body(), callsites);
|
collectCallsFromBlock(fn.body(), callsites);
|
||||||
@ -230,14 +249,37 @@ public final class PbsFrontendCompiler {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final var candidateCallableIds = callableIdsByNameAndArity.get(callableArityKey(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();
|
||||||
instructions.add(new IRBackendExecutableFunction.Instruction(
|
instructions.add(new IRBackendExecutableFunction.Instruction(
|
||||||
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
|
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
|
||||||
moduleKey == null ? "" : moduleKey,
|
normalizedModuleKey,
|
||||||
calleeName,
|
calleeName,
|
||||||
|
calleeCallableId,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
callExpr.arguments().size(),
|
callExpr.arguments().size(),
|
||||||
returnSlotsByCallableAndArity.get(callableArityKey(calleeName, callExpr.arguments().size())),
|
returnSlotsByCallableId.get(calleeCallableId),
|
||||||
callExpr.span()));
|
callExpr.span()));
|
||||||
}
|
}
|
||||||
instructions.add(new IRBackendExecutableFunction.Instruction(
|
instructions.add(new IRBackendExecutableFunction.Instruction(
|
||||||
@ -246,6 +288,7 @@ public final class PbsFrontendCompiler {
|
|||||||
"",
|
"",
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
fn.span()));
|
fn.span()));
|
||||||
|
|
||||||
final var returnSlots = returnSlotsFor(fn);
|
final var returnSlots = returnSlotsFor(fn);
|
||||||
@ -253,8 +296,9 @@ public final class PbsFrontendCompiler {
|
|||||||
final var end = safeToInt(fn.span().getEnd());
|
final var end = safeToInt(fn.span().getEnd());
|
||||||
executableFunctions.add(new IRBackendExecutableFunction(
|
executableFunctions.add(new IRBackendExecutableFunction(
|
||||||
fileId,
|
fileId,
|
||||||
moduleKey == null ? "" : moduleKey,
|
normalizedModuleKey,
|
||||||
fn.name(),
|
fn.name(),
|
||||||
|
functionCallableId,
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
fn.parameters().size(),
|
fn.parameters().size(),
|
||||||
@ -264,7 +308,13 @@ public final class PbsFrontendCompiler {
|
|||||||
ReadOnlyList.wrap(instructions),
|
ReadOnlyList.wrap(instructions),
|
||||||
fn.span()));
|
fn.span()));
|
||||||
}
|
}
|
||||||
return ReadOnlyList.wrap(executableFunctions);
|
final var callableSignatures = new ArrayList<CallableSignatureRef>(callableIdTable.size());
|
||||||
|
for (final var callableId : callableIdTable.identifiers()) {
|
||||||
|
callableSignatures.add(callableIdTable.get(callableId));
|
||||||
|
}
|
||||||
|
return new ExecutableLoweringResult(
|
||||||
|
ReadOnlyList.wrap(executableFunctions),
|
||||||
|
ReadOnlyList.wrap(callableSignatures));
|
||||||
}
|
}
|
||||||
|
|
||||||
private int returnSlotsFor(final PbsAst.FunctionDecl functionDecl) {
|
private int returnSlotsFor(final PbsAst.FunctionDecl functionDecl) {
|
||||||
@ -280,6 +330,60 @@ public final class PbsFrontendCompiler {
|
|||||||
return callableName + "#" + arity;
|
return callableName + "#" + arity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String callableShapeKey(final PbsAst.FunctionDecl functionDecl) {
|
||||||
|
final var builder = new StringBuilder();
|
||||||
|
builder.append('(');
|
||||||
|
for (var i = 0; i < functionDecl.parameters().size(); i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
builder.append(',');
|
||||||
|
}
|
||||||
|
builder.append(typeKey(functionDecl.parameters().get(i).typeRef()));
|
||||||
|
}
|
||||||
|
builder.append(")->");
|
||||||
|
builder.append(outputShapeKey(functionDecl.returnKind(), functionDecl.returnType(), functionDecl.resultErrorType()));
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String outputShapeKey(
|
||||||
|
final PbsAst.ReturnKind returnKind,
|
||||||
|
final PbsAst.TypeRef returnType,
|
||||||
|
final PbsAst.TypeRef resultErrorType) {
|
||||||
|
return switch (returnKind) {
|
||||||
|
case INFERRED_UNIT, EXPLICIT_UNIT -> "unit";
|
||||||
|
case PLAIN -> typeKey(returnType);
|
||||||
|
case RESULT -> "result<" + typeKey(resultErrorType) + ">" + typeKey(returnType);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private String typeKey(final PbsAst.TypeRef typeRef) {
|
||||||
|
final var unwrapped = unwrapGroup(typeRef);
|
||||||
|
if (unwrapped == null) {
|
||||||
|
return "unit";
|
||||||
|
}
|
||||||
|
return switch (unwrapped.kind()) {
|
||||||
|
case SIMPLE -> "simple:" + unwrapped.name();
|
||||||
|
case SELF -> "self";
|
||||||
|
case OPTIONAL -> "optional(" + typeKey(unwrapped.inner()) + ")";
|
||||||
|
case UNIT -> "unit";
|
||||||
|
case GROUP -> typeKey(unwrapped.inner());
|
||||||
|
case NAMED_TUPLE -> "tuple(" + unwrapped.fields().stream()
|
||||||
|
.map(field -> typeKey(field.typeRef()))
|
||||||
|
.reduce((left, right) -> left + "," + right)
|
||||||
|
.orElse("") + ")";
|
||||||
|
case ERROR -> "error";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private PbsAst.TypeRef unwrapGroup(final PbsAst.TypeRef typeRef) {
|
||||||
|
if (typeRef == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (typeRef.kind() != PbsAst.TypeRefKind.GROUP) {
|
||||||
|
return typeRef;
|
||||||
|
}
|
||||||
|
return unwrapGroup(typeRef.inner());
|
||||||
|
}
|
||||||
|
|
||||||
private int safeToInt(final long value) {
|
private int safeToInt(final long value) {
|
||||||
if (value > Integer.MAX_VALUE) {
|
if (value > Integer.MAX_VALUE) {
|
||||||
return Integer.MAX_VALUE;
|
return Integer.MAX_VALUE;
|
||||||
@ -443,4 +547,9 @@ public final class PbsFrontendCompiler {
|
|||||||
default -> null;
|
default -> null;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private record ExecutableLoweringResult(
|
||||||
|
ReadOnlyList<IRBackendExecutableFunction> executableFunctions,
|
||||||
|
ReadOnlyList<CallableSignatureRef> callableSignatures) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,7 @@ public class LowerToIRVMService {
|
|||||||
private static final Comparator<IRBackendExecutableFunction> FUNCTION_ORDER = Comparator
|
private static final Comparator<IRBackendExecutableFunction> FUNCTION_ORDER = Comparator
|
||||||
.comparing(IRBackendExecutableFunction::moduleKey)
|
.comparing(IRBackendExecutableFunction::moduleKey)
|
||||||
.thenComparing(IRBackendExecutableFunction::callableName)
|
.thenComparing(IRBackendExecutableFunction::callableName)
|
||||||
|
.thenComparingInt(IRBackendExecutableFunction::callableId)
|
||||||
.thenComparingInt(IRBackendExecutableFunction::sourceStart);
|
.thenComparingInt(IRBackendExecutableFunction::sourceStart);
|
||||||
|
|
||||||
private final IRVMValidator validator;
|
private final IRVMValidator validator;
|
||||||
@ -35,11 +36,14 @@ public class LowerToIRVMService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final var ordered = orderFunctions(backend.getExecutableFunctions());
|
final var ordered = orderFunctions(backend.getExecutableFunctions());
|
||||||
final var funcIdByModuleAndName = new HashMap<String, Integer>();
|
final var funcIdByCallableId = new HashMap<Integer, Integer>();
|
||||||
for (var i = 0; i < ordered.size(); i++) {
|
for (var i = 0; i < ordered.size(); i++) {
|
||||||
final var fn = ordered.get(i);
|
final var fn = ordered.get(i);
|
||||||
final var key = callableKey(fn.moduleKey(), fn.callableName());
|
if (funcIdByCallableId.putIfAbsent(fn.callableId(), i) != null) {
|
||||||
funcIdByModuleAndName.putIfAbsent(key, i);
|
throw new IRVMLoweringException(
|
||||||
|
IRVMLoweringErrorCode.LOWER_IRVM_MISSING_CALLEE,
|
||||||
|
"duplicate callable id in executable set: " + fn.callableId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final var loweredFunctions = new ArrayList<IRVMFunction>(ordered.size());
|
final var loweredFunctions = new ArrayList<IRVMFunction>(ordered.size());
|
||||||
@ -66,12 +70,16 @@ public class LowerToIRVMService {
|
|||||||
functionPc += IRVMOp.RET.immediateSize() + 2;
|
functionPc += IRVMOp.RET.immediateSize() + 2;
|
||||||
}
|
}
|
||||||
case CALL_FUNC -> {
|
case CALL_FUNC -> {
|
||||||
final var key = callableKey(instr.calleeModuleKey(), instr.calleeCallableName());
|
if (instr.calleeCallableId() == null) {
|
||||||
final var calleeId = funcIdByModuleAndName.get(key);
|
throw new IRVMLoweringException(
|
||||||
|
IRVMLoweringErrorCode.LOWER_IRVM_MISSING_CALLEE,
|
||||||
|
"missing callee callable id");
|
||||||
|
}
|
||||||
|
final var calleeId = funcIdByCallableId.get(instr.calleeCallableId());
|
||||||
if (calleeId == null) {
|
if (calleeId == null) {
|
||||||
throw new IRVMLoweringException(
|
throw new IRVMLoweringException(
|
||||||
IRVMLoweringErrorCode.LOWER_IRVM_MISSING_CALLEE,
|
IRVMLoweringErrorCode.LOWER_IRVM_MISSING_CALLEE,
|
||||||
"missing callee function: " + key);
|
"missing callee function for callable_id=" + instr.calleeCallableId());
|
||||||
}
|
}
|
||||||
final var calleeFunction = ordered.get(calleeId);
|
final var calleeFunction = ordered.get(calleeId);
|
||||||
if (instr.expectedArgSlots() != null
|
if (instr.expectedArgSlots() != null
|
||||||
@ -79,7 +87,7 @@ public class LowerToIRVMService {
|
|||||||
throw new IRVMLoweringException(
|
throw new IRVMLoweringException(
|
||||||
IRVMLoweringErrorCode.LOWER_IRVM_CALL_ARG_SLOTS_MISMATCH,
|
IRVMLoweringErrorCode.LOWER_IRVM_CALL_ARG_SLOTS_MISMATCH,
|
||||||
"call arg_slots mismatch for %s. expected=%d actual=%d".formatted(
|
"call arg_slots mismatch for %s. expected=%d actual=%d".formatted(
|
||||||
key,
|
instr.calleeCallableName(),
|
||||||
instr.expectedArgSlots(),
|
instr.expectedArgSlots(),
|
||||||
calleeFunction.paramSlots()));
|
calleeFunction.paramSlots()));
|
||||||
}
|
}
|
||||||
@ -88,7 +96,7 @@ public class LowerToIRVMService {
|
|||||||
throw new IRVMLoweringException(
|
throw new IRVMLoweringException(
|
||||||
IRVMLoweringErrorCode.LOWER_IRVM_CALL_RET_SLOTS_MISMATCH,
|
IRVMLoweringErrorCode.LOWER_IRVM_CALL_RET_SLOTS_MISMATCH,
|
||||||
"call ret_slots mismatch for %s. expected=%d actual=%d".formatted(
|
"call ret_slots mismatch for %s. expected=%d actual=%d".formatted(
|
||||||
key,
|
instr.calleeCallableName(),
|
||||||
instr.expectedRetSlots(),
|
instr.expectedRetSlots(),
|
||||||
calleeFunction.returnSlots()));
|
calleeFunction.returnSlots()));
|
||||||
}
|
}
|
||||||
@ -213,12 +221,6 @@ public class LowerToIRVMService {
|
|||||||
return ReadOnlyList.wrap(ordered);
|
return ReadOnlyList.wrap(ordered);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String callableKey(
|
|
||||||
final String moduleKey,
|
|
||||||
final String callableName) {
|
|
||||||
return (moduleKey == null ? "" : moduleKey) + "::" + callableName;
|
|
||||||
}
|
|
||||||
|
|
||||||
private BytecodeModule.SourceSpan toBytecodeSpan(
|
private BytecodeModule.SourceSpan toBytecodeSpan(
|
||||||
final int fallbackFileId,
|
final int fallbackFileId,
|
||||||
final Span span) {
|
final Span span) {
|
||||||
|
|||||||
@ -17,10 +17,10 @@ class LowerToIRVMServiceTest {
|
|||||||
void lowerMustAssignEntrypointIdZeroAndSortRemainingDeterministically() {
|
void lowerMustAssignEntrypointIdZeroAndSortRemainingDeterministically() {
|
||||||
final var backend = IRBackend.builder()
|
final var backend = IRBackend.builder()
|
||||||
.executableFunctions(ReadOnlyList.from(
|
.executableFunctions(ReadOnlyList.from(
|
||||||
fn("aux", "app", ReadOnlyList.from(
|
fn("aux", "app", 11, ReadOnlyList.from(
|
||||||
callFunc("app", "main"),
|
callFunc("app", "main", 10),
|
||||||
ret())),
|
ret())),
|
||||||
fn("main", "app", ReadOnlyList.from(
|
fn("main", "app", 10, ReadOnlyList.from(
|
||||||
ret()))))
|
ret()))))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ class LowerToIRVMServiceTest {
|
|||||||
void lowerMustMapHostAndIntrinsicCallsites() {
|
void lowerMustMapHostAndIntrinsicCallsites() {
|
||||||
final var backend = IRBackend.builder()
|
final var backend = IRBackend.builder()
|
||||||
.executableFunctions(ReadOnlyList.from(
|
.executableFunctions(ReadOnlyList.from(
|
||||||
fn("main", "app", ReadOnlyList.from(
|
fn("main", "app", 10, ReadOnlyList.from(
|
||||||
callHost("gfx", "draw_pixel", 1, 2, 0),
|
callHost("gfx", "draw_pixel", 1, 2, 0),
|
||||||
callIntrinsic("core.color.pack", 1, 0x2001),
|
callIntrinsic("core.color.pack", 1, 0x2001),
|
||||||
ret()))))
|
ret()))))
|
||||||
@ -59,8 +59,8 @@ class LowerToIRVMServiceTest {
|
|||||||
void lowerMustRejectUnterminatedFunction() {
|
void lowerMustRejectUnterminatedFunction() {
|
||||||
final var backend = IRBackend.builder()
|
final var backend = IRBackend.builder()
|
||||||
.executableFunctions(ReadOnlyList.from(
|
.executableFunctions(ReadOnlyList.from(
|
||||||
fn("main", "app", ReadOnlyList.from(
|
fn("main", "app", 10, ReadOnlyList.from(
|
||||||
callFunc("app", "main")))))
|
callFunc("app", "main", 10)))))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
final var thrown = assertThrows(IRVMValidationException.class, () -> new LowerToIRVMService().lower(backend));
|
final var thrown = assertThrows(IRVMValidationException.class, () -> new LowerToIRVMService().lower(backend));
|
||||||
@ -71,8 +71,8 @@ class LowerToIRVMServiceTest {
|
|||||||
void lowerMustRejectMissingCallee() {
|
void lowerMustRejectMissingCallee() {
|
||||||
final var backend = IRBackend.builder()
|
final var backend = IRBackend.builder()
|
||||||
.executableFunctions(ReadOnlyList.from(
|
.executableFunctions(ReadOnlyList.from(
|
||||||
fn("main", "app", ReadOnlyList.from(
|
fn("main", "app", 10, ReadOnlyList.from(
|
||||||
callFunc("app", "missing"),
|
callFunc("app", "missing", 77),
|
||||||
ret()))))
|
ret()))))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@ -84,9 +84,9 @@ class LowerToIRVMServiceTest {
|
|||||||
void lowerMustRejectCallArgSlotMismatch() {
|
void lowerMustRejectCallArgSlotMismatch() {
|
||||||
final var backend = IRBackend.builder()
|
final var backend = IRBackend.builder()
|
||||||
.executableFunctions(ReadOnlyList.from(
|
.executableFunctions(ReadOnlyList.from(
|
||||||
fn("callee", "app", ReadOnlyList.from(ret())),
|
fn("callee", "app", 20, ReadOnlyList.from(ret())),
|
||||||
fn("main", "app", ReadOnlyList.from(
|
fn("main", "app", 10, ReadOnlyList.from(
|
||||||
callFuncWithExpected("app", "callee", 2, 0),
|
callFuncWithExpected("app", "callee", 20, 2, 0),
|
||||||
ret()))))
|
ret()))))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ class LowerToIRVMServiceTest {
|
|||||||
void lowerMustResolveJumpTargetsFromLabels() {
|
void lowerMustResolveJumpTargetsFromLabels() {
|
||||||
final var backend = IRBackend.builder()
|
final var backend = IRBackend.builder()
|
||||||
.executableFunctions(ReadOnlyList.from(
|
.executableFunctions(ReadOnlyList.from(
|
||||||
fn("main", "app", ReadOnlyList.from(
|
fn("main", "app", 10, ReadOnlyList.from(
|
||||||
label("entry"),
|
label("entry"),
|
||||||
jmp("exit"),
|
jmp("exit"),
|
||||||
label("exit"),
|
label("exit"),
|
||||||
@ -117,7 +117,7 @@ class LowerToIRVMServiceTest {
|
|||||||
void lowerMustRejectMissingJumpTargetLabel() {
|
void lowerMustRejectMissingJumpTargetLabel() {
|
||||||
final var backend = IRBackend.builder()
|
final var backend = IRBackend.builder()
|
||||||
.executableFunctions(ReadOnlyList.from(
|
.executableFunctions(ReadOnlyList.from(
|
||||||
fn("main", "app", ReadOnlyList.from(
|
fn("main", "app", 10, ReadOnlyList.from(
|
||||||
jmp("missing"),
|
jmp("missing"),
|
||||||
ret()))))
|
ret()))))
|
||||||
.build();
|
.build();
|
||||||
@ -129,11 +129,13 @@ class LowerToIRVMServiceTest {
|
|||||||
private static IRBackendExecutableFunction fn(
|
private static IRBackendExecutableFunction fn(
|
||||||
final String name,
|
final String name,
|
||||||
final String moduleKey,
|
final String moduleKey,
|
||||||
|
final int callableId,
|
||||||
final ReadOnlyList<IRBackendExecutableFunction.Instruction> instructions) {
|
final ReadOnlyList<IRBackendExecutableFunction.Instruction> instructions) {
|
||||||
return new IRBackendExecutableFunction(
|
return new IRBackendExecutableFunction(
|
||||||
new FileId(0),
|
new FileId(0),
|
||||||
moduleKey,
|
moduleKey,
|
||||||
name,
|
name,
|
||||||
|
callableId,
|
||||||
0,
|
0,
|
||||||
10,
|
10,
|
||||||
0,
|
0,
|
||||||
@ -154,11 +156,15 @@ class LowerToIRVMServiceTest {
|
|||||||
Span.none());
|
Span.none());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IRBackendExecutableFunction.Instruction callFunc(final String moduleKey, final String name) {
|
private static IRBackendExecutableFunction.Instruction callFunc(
|
||||||
|
final String moduleKey,
|
||||||
|
final String name,
|
||||||
|
final int calleeCallableId) {
|
||||||
return new IRBackendExecutableFunction.Instruction(
|
return new IRBackendExecutableFunction.Instruction(
|
||||||
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
|
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
|
||||||
moduleKey,
|
moduleKey,
|
||||||
name,
|
name,
|
||||||
|
calleeCallableId,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
Span.none());
|
Span.none());
|
||||||
@ -167,12 +173,14 @@ class LowerToIRVMServiceTest {
|
|||||||
private static IRBackendExecutableFunction.Instruction callFuncWithExpected(
|
private static IRBackendExecutableFunction.Instruction callFuncWithExpected(
|
||||||
final String moduleKey,
|
final String moduleKey,
|
||||||
final String name,
|
final String name,
|
||||||
|
final int calleeCallableId,
|
||||||
final int expectedArgSlots,
|
final int expectedArgSlots,
|
||||||
final int expectedRetSlots) {
|
final int expectedRetSlots) {
|
||||||
return new IRBackendExecutableFunction.Instruction(
|
return new IRBackendExecutableFunction.Instruction(
|
||||||
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
|
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
|
||||||
moduleKey,
|
moduleKey,
|
||||||
name,
|
name,
|
||||||
|
calleeCallableId,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
expectedArgSlots,
|
expectedArgSlots,
|
||||||
@ -215,6 +223,7 @@ class LowerToIRVMServiceTest {
|
|||||||
"",
|
"",
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
label,
|
label,
|
||||||
"",
|
"",
|
||||||
null,
|
null,
|
||||||
@ -229,6 +238,7 @@ class LowerToIRVMServiceTest {
|
|||||||
"",
|
"",
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
"",
|
"",
|
||||||
target,
|
target,
|
||||||
null,
|
null,
|
||||||
|
|||||||
@ -178,6 +178,7 @@ class BackendGateIIntegrationTest {
|
|||||||
new FileId(1),
|
new FileId(1),
|
||||||
"app/main",
|
"app/main",
|
||||||
name,
|
name,
|
||||||
|
1,
|
||||||
0,
|
0,
|
||||||
10,
|
10,
|
||||||
0,
|
0,
|
||||||
|
|||||||
@ -29,6 +29,7 @@ class BackendSafetyGateSUTest {
|
|||||||
new FileId(1),
|
new FileId(1),
|
||||||
"app",
|
"app",
|
||||||
"main",
|
"main",
|
||||||
|
1,
|
||||||
0,
|
0,
|
||||||
10,
|
10,
|
||||||
0,
|
0,
|
||||||
@ -40,6 +41,7 @@ class BackendSafetyGateSUTest {
|
|||||||
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
|
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
|
||||||
"app",
|
"app",
|
||||||
"missing",
|
"missing",
|
||||||
|
99,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
0,
|
0,
|
||||||
@ -112,6 +114,7 @@ class BackendSafetyGateSUTest {
|
|||||||
new FileId(1),
|
new FileId(1),
|
||||||
"app",
|
"app",
|
||||||
"main",
|
"main",
|
||||||
|
1,
|
||||||
0,
|
0,
|
||||||
10,
|
10,
|
||||||
0,
|
0,
|
||||||
|
|||||||
@ -38,6 +38,7 @@ class LowerToIRVMPipelineStageTest {
|
|||||||
new FileId(0),
|
new FileId(0),
|
||||||
"app",
|
"app",
|
||||||
"main",
|
"main",
|
||||||
|
1,
|
||||||
0,
|
0,
|
||||||
10,
|
10,
|
||||||
0,
|
0,
|
||||||
|
|||||||
@ -0,0 +1,13 @@
|
|||||||
|
package p.studio.compiler.source.identifiers;
|
||||||
|
|
||||||
|
public class CallableId extends SourceIdentifier {
|
||||||
|
public static final CallableId NONE = new CallableId(-1);
|
||||||
|
|
||||||
|
public CallableId(final int id) {
|
||||||
|
super(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CallableId none() {
|
||||||
|
return NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
package p.studio.compiler.source.tables;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record CallableSignatureRef(
|
||||||
|
String moduleKey,
|
||||||
|
String callableName,
|
||||||
|
int arity,
|
||||||
|
String typeShape) {
|
||||||
|
|
||||||
|
public CallableSignatureRef {
|
||||||
|
moduleKey = moduleKey == null ? "" : moduleKey;
|
||||||
|
callableName = Objects.requireNonNull(callableName, "callableName");
|
||||||
|
typeShape = typeShape == null ? "" : typeShape;
|
||||||
|
if (callableName.isBlank()) {
|
||||||
|
throw new IllegalArgumentException("callableName must not be blank");
|
||||||
|
}
|
||||||
|
if (arity < 0) {
|
||||||
|
throw new IllegalArgumentException("arity must be non-negative");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
package p.studio.compiler.source.tables;
|
||||||
|
|
||||||
|
import p.studio.compiler.source.identifiers.CallableId;
|
||||||
|
|
||||||
|
public class CallableTable extends InternTable<CallableId, CallableSignatureRef> implements CallableTableReader {
|
||||||
|
|
||||||
|
public CallableTable() {
|
||||||
|
super(CallableId::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CallableId register(
|
||||||
|
final String moduleKey,
|
||||||
|
final String callableName,
|
||||||
|
final int arity,
|
||||||
|
final String typeShape) {
|
||||||
|
return register(new CallableSignatureRef(moduleKey, callableName, arity, typeShape));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
package p.studio.compiler.source.tables;
|
||||||
|
|
||||||
|
import p.studio.compiler.source.identifiers.CallableId;
|
||||||
|
|
||||||
|
public interface CallableTableReader extends InternTableReader<CallableId, CallableSignatureRef> {
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package p.studio.compiler.source.tables;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
|
class CallableTableTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldInternEqualCallableSignaturesToSameIdentifier() {
|
||||||
|
final var table = new CallableTable();
|
||||||
|
|
||||||
|
final var first = table.register("app/main", "draw", 2, "(simple:int,simple:int)->unit");
|
||||||
|
final var second = table.register("app/main", "draw", 2, "(simple:int,simple:int)->unit");
|
||||||
|
final var third = table.register("app/main", "draw", 1, "(simple:int)->unit");
|
||||||
|
|
||||||
|
assertEquals(first, second);
|
||||||
|
assertEquals(2, table.size());
|
||||||
|
assertEquals(1, table.get(third).arity());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRejectInvalidCallableSignatureReferenceContract() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> new CallableSignatureRef("app/main", "", 1, "(unit)->unit"));
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> new CallableSignatureRef("app/main", "draw", -1, "(unit)->unit"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,6 +2,8 @@ package p.studio.compiler.models;
|
|||||||
|
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import p.studio.compiler.source.tables.CallableSignatureRef;
|
||||||
|
import p.studio.compiler.source.tables.CallableTable;
|
||||||
import p.studio.utilities.structures.ReadOnlyList;
|
import p.studio.utilities.structures.ReadOnlyList;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -15,6 +17,8 @@ public class IRBackend {
|
|||||||
@Builder.Default
|
@Builder.Default
|
||||||
private final ReadOnlyList<IRBackendExecutableFunction> executableFunctions = ReadOnlyList.empty();
|
private final ReadOnlyList<IRBackendExecutableFunction> executableFunctions = ReadOnlyList.empty();
|
||||||
@Builder.Default
|
@Builder.Default
|
||||||
|
private final ReadOnlyList<CallableSignatureRef> callableSignatures = ReadOnlyList.empty();
|
||||||
|
@Builder.Default
|
||||||
private final IRReservedMetadata reservedMetadata = IRReservedMetadata.empty();
|
private final IRReservedMetadata reservedMetadata = IRReservedMetadata.empty();
|
||||||
|
|
||||||
public static IRBackendAggregator aggregator() {
|
public static IRBackendAggregator aggregator() {
|
||||||
@ -24,6 +28,7 @@ public class IRBackend {
|
|||||||
public static final class IRBackendAggregator {
|
public static final class IRBackendAggregator {
|
||||||
private final ArrayList<IRFunction> functions = new ArrayList<>();
|
private final ArrayList<IRFunction> functions = new ArrayList<>();
|
||||||
private final ArrayList<IRBackendExecutableFunction> executableFunctions = new ArrayList<>();
|
private final ArrayList<IRBackendExecutableFunction> executableFunctions = new ArrayList<>();
|
||||||
|
private final CallableTable callableTable = new CallableTable();
|
||||||
private final ArrayList<IRReservedMetadata.HostMethodBinding> hostMethodBindings = new ArrayList<>();
|
private final ArrayList<IRReservedMetadata.HostMethodBinding> hostMethodBindings = new ArrayList<>();
|
||||||
private final ArrayList<IRReservedMetadata.BuiltinTypeSurface> builtinTypeSurfaces = new ArrayList<>();
|
private final ArrayList<IRReservedMetadata.BuiltinTypeSurface> builtinTypeSurfaces = new ArrayList<>();
|
||||||
private final ArrayList<IRReservedMetadata.BuiltinConstSurface> builtinConstSurfaces = new ArrayList<>();
|
private final ArrayList<IRReservedMetadata.BuiltinConstSurface> builtinConstSurfaces = new ArrayList<>();
|
||||||
@ -34,7 +39,10 @@ public class IRBackend {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
functions.addAll(backendFile.functions().asList());
|
functions.addAll(backendFile.functions().asList());
|
||||||
executableFunctions.addAll(backendFile.executableFunctions().asList());
|
final var callableRemap = reindexCallables(backendFile.callableSignatures());
|
||||||
|
for (final var function : backendFile.executableFunctions()) {
|
||||||
|
executableFunctions.add(remapExecutableFunction(function, callableRemap));
|
||||||
|
}
|
||||||
final var metadata = backendFile.reservedMetadata();
|
final var metadata = backendFile.reservedMetadata();
|
||||||
hostMethodBindings.addAll(metadata.hostMethodBindings().asList());
|
hostMethodBindings.addAll(metadata.hostMethodBindings().asList());
|
||||||
builtinTypeSurfaces.addAll(metadata.builtinTypeSurfaces().asList());
|
builtinTypeSurfaces.addAll(metadata.builtinTypeSurfaces().asList());
|
||||||
@ -42,11 +50,80 @@ public class IRBackend {
|
|||||||
requiredCapabilities.addAll(metadata.requiredCapabilities().asList());
|
requiredCapabilities.addAll(metadata.requiredCapabilities().asList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int[] reindexCallables(final ReadOnlyList<CallableSignatureRef> localCallableSignatures) {
|
||||||
|
if (localCallableSignatures == null || localCallableSignatures.isEmpty()) {
|
||||||
|
return new int[0];
|
||||||
|
}
|
||||||
|
final var remap = new int[localCallableSignatures.size()];
|
||||||
|
for (var i = 0; i < localCallableSignatures.size(); i++) {
|
||||||
|
remap[i] = callableTable.register(localCallableSignatures.get(i)).getId();
|
||||||
|
}
|
||||||
|
return remap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IRBackendExecutableFunction remapExecutableFunction(
|
||||||
|
final IRBackendExecutableFunction function,
|
||||||
|
final int[] callableRemap) {
|
||||||
|
final var remappedInstructions = new ArrayList<IRBackendExecutableFunction.Instruction>(function.instructions().size());
|
||||||
|
for (final var instruction : function.instructions()) {
|
||||||
|
final Integer remappedCallee;
|
||||||
|
if (instruction.calleeCallableId() == null) {
|
||||||
|
remappedCallee = null;
|
||||||
|
} else {
|
||||||
|
remappedCallee = remapCallableId(instruction.calleeCallableId(), callableRemap, "callee");
|
||||||
|
}
|
||||||
|
remappedInstructions.add(new IRBackendExecutableFunction.Instruction(
|
||||||
|
instruction.kind(),
|
||||||
|
instruction.calleeModuleKey(),
|
||||||
|
instruction.calleeCallableName(),
|
||||||
|
remappedCallee,
|
||||||
|
instruction.hostCall(),
|
||||||
|
instruction.intrinsicCall(),
|
||||||
|
instruction.label(),
|
||||||
|
instruction.targetLabel(),
|
||||||
|
instruction.expectedArgSlots(),
|
||||||
|
instruction.expectedRetSlots(),
|
||||||
|
instruction.span()));
|
||||||
|
}
|
||||||
|
return new IRBackendExecutableFunction(
|
||||||
|
function.fileId(),
|
||||||
|
function.moduleKey(),
|
||||||
|
function.callableName(),
|
||||||
|
remapCallableId(function.callableId(), callableRemap, "function"),
|
||||||
|
function.sourceStart(),
|
||||||
|
function.sourceEnd(),
|
||||||
|
function.paramSlots(),
|
||||||
|
function.localSlots(),
|
||||||
|
function.returnSlots(),
|
||||||
|
function.maxStackSlots(),
|
||||||
|
ReadOnlyList.wrap(remappedInstructions),
|
||||||
|
function.span());
|
||||||
|
}
|
||||||
|
|
||||||
|
private int remapCallableId(
|
||||||
|
final int localCallableId,
|
||||||
|
final int[] callableRemap,
|
||||||
|
final String label) {
|
||||||
|
if (localCallableId < 0 || localCallableId >= callableRemap.length) {
|
||||||
|
throw new IllegalArgumentException("invalid local " + label + " callable id: " + localCallableId);
|
||||||
|
}
|
||||||
|
return callableRemap[localCallableId];
|
||||||
|
}
|
||||||
|
|
||||||
|
private ReadOnlyList<CallableSignatureRef> emitCallableSignatures() {
|
||||||
|
final var signatures = new ArrayList<CallableSignatureRef>(callableTable.size());
|
||||||
|
for (final var callableId : callableTable.identifiers()) {
|
||||||
|
signatures.add(callableTable.get(callableId));
|
||||||
|
}
|
||||||
|
return ReadOnlyList.wrap(signatures);
|
||||||
|
}
|
||||||
|
|
||||||
public IRBackend emit() {
|
public IRBackend emit() {
|
||||||
return IRBackend
|
return IRBackend
|
||||||
.builder()
|
.builder()
|
||||||
.functions(ReadOnlyList.wrap(functions))
|
.functions(ReadOnlyList.wrap(functions))
|
||||||
.executableFunctions(ReadOnlyList.wrap(executableFunctions))
|
.executableFunctions(ReadOnlyList.wrap(executableFunctions))
|
||||||
|
.callableSignatures(emitCallableSignatures())
|
||||||
.reservedMetadata(new IRReservedMetadata(
|
.reservedMetadata(new IRReservedMetadata(
|
||||||
ReadOnlyList.wrap(hostMethodBindings),
|
ReadOnlyList.wrap(hostMethodBindings),
|
||||||
ReadOnlyList.wrap(builtinTypeSurfaces),
|
ReadOnlyList.wrap(builtinTypeSurfaces),
|
||||||
@ -61,6 +138,7 @@ public class IRBackend {
|
|||||||
final var sb = new StringBuilder();
|
final var sb = new StringBuilder();
|
||||||
sb.append("IRBackend{functions=").append(functions.size())
|
sb.append("IRBackend{functions=").append(functions.size())
|
||||||
.append(", executableFunctions=").append(executableFunctions.size())
|
.append(", executableFunctions=").append(executableFunctions.size())
|
||||||
|
.append(", callableSignatures=").append(callableSignatures.size())
|
||||||
.append(", hostBindings=").append(reservedMetadata.hostMethodBindings().size())
|
.append(", hostBindings=").append(reservedMetadata.hostMethodBindings().size())
|
||||||
.append(", builtinTypes=").append(reservedMetadata.builtinTypeSurfaces().size())
|
.append(", builtinTypes=").append(reservedMetadata.builtinTypeSurfaces().size())
|
||||||
.append(", builtinConsts=").append(reservedMetadata.builtinConstSurfaces().size())
|
.append(", builtinConsts=").append(reservedMetadata.builtinConstSurfaces().size())
|
||||||
|
|||||||
@ -10,6 +10,7 @@ public record IRBackendExecutableFunction(
|
|||||||
FileId fileId,
|
FileId fileId,
|
||||||
String moduleKey,
|
String moduleKey,
|
||||||
String callableName,
|
String callableName,
|
||||||
|
int callableId,
|
||||||
int sourceStart,
|
int sourceStart,
|
||||||
int sourceEnd,
|
int sourceEnd,
|
||||||
int paramSlots,
|
int paramSlots,
|
||||||
@ -26,6 +27,9 @@ public record IRBackendExecutableFunction(
|
|||||||
if (callableName.isBlank()) {
|
if (callableName.isBlank()) {
|
||||||
throw new IllegalArgumentException("callableName must not be blank");
|
throw new IllegalArgumentException("callableName must not be blank");
|
||||||
}
|
}
|
||||||
|
if (callableId < 0) {
|
||||||
|
throw new IllegalArgumentException("callableId must be non-negative");
|
||||||
|
}
|
||||||
if (sourceStart < 0 || sourceEnd < 0 || sourceEnd < sourceStart) {
|
if (sourceStart < 0 || sourceEnd < 0 || sourceEnd < sourceStart) {
|
||||||
throw new IllegalArgumentException("invalid source span bounds");
|
throw new IllegalArgumentException("invalid source span bounds");
|
||||||
}
|
}
|
||||||
@ -46,6 +50,7 @@ public record IRBackendExecutableFunction(
|
|||||||
InstructionKind kind,
|
InstructionKind kind,
|
||||||
String calleeModuleKey,
|
String calleeModuleKey,
|
||||||
String calleeCallableName,
|
String calleeCallableName,
|
||||||
|
Integer calleeCallableId,
|
||||||
HostCallMetadata hostCall,
|
HostCallMetadata hostCall,
|
||||||
IntrinsicCallMetadata intrinsicCall,
|
IntrinsicCallMetadata intrinsicCall,
|
||||||
String label,
|
String label,
|
||||||
@ -57,10 +62,34 @@ public record IRBackendExecutableFunction(
|
|||||||
final InstructionKind kind,
|
final InstructionKind kind,
|
||||||
final String calleeModuleKey,
|
final String calleeModuleKey,
|
||||||
final String calleeCallableName,
|
final String calleeCallableName,
|
||||||
|
final Integer calleeCallableId,
|
||||||
final HostCallMetadata hostCall,
|
final HostCallMetadata hostCall,
|
||||||
final IntrinsicCallMetadata intrinsicCall,
|
final IntrinsicCallMetadata intrinsicCall,
|
||||||
final Span span) {
|
final Span span) {
|
||||||
this(kind, calleeModuleKey, calleeCallableName, hostCall, intrinsicCall, null, null, null, null, span);
|
this(kind, calleeModuleKey, calleeCallableName, calleeCallableId, hostCall, intrinsicCall, null, null, null, null, span);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instruction(
|
||||||
|
final InstructionKind kind,
|
||||||
|
final String calleeModuleKey,
|
||||||
|
final String calleeCallableName,
|
||||||
|
final Integer calleeCallableId,
|
||||||
|
final HostCallMetadata hostCall,
|
||||||
|
final IntrinsicCallMetadata intrinsicCall,
|
||||||
|
final Integer expectedArgSlots,
|
||||||
|
final Integer expectedRetSlots,
|
||||||
|
final Span span) {
|
||||||
|
this(kind, calleeModuleKey, calleeCallableName, calleeCallableId, hostCall, intrinsicCall, null, null, expectedArgSlots, expectedRetSlots, span);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instruction(
|
||||||
|
final InstructionKind kind,
|
||||||
|
final String calleeModuleKey,
|
||||||
|
final String calleeCallableName,
|
||||||
|
final HostCallMetadata hostCall,
|
||||||
|
final IntrinsicCallMetadata intrinsicCall,
|
||||||
|
final Span span) {
|
||||||
|
this(kind, calleeModuleKey, calleeCallableName, null, hostCall, intrinsicCall, null, null, null, null, span);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Instruction(
|
public Instruction(
|
||||||
@ -72,7 +101,7 @@ public record IRBackendExecutableFunction(
|
|||||||
final Integer expectedArgSlots,
|
final Integer expectedArgSlots,
|
||||||
final Integer expectedRetSlots,
|
final Integer expectedRetSlots,
|
||||||
final Span span) {
|
final Span span) {
|
||||||
this(kind, calleeModuleKey, calleeCallableName, hostCall, intrinsicCall, null, null, expectedArgSlots, expectedRetSlots, span);
|
this(kind, calleeModuleKey, calleeCallableName, null, hostCall, intrinsicCall, null, null, expectedArgSlots, expectedRetSlots, span);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Instruction {
|
public Instruction {
|
||||||
@ -82,6 +111,9 @@ public record IRBackendExecutableFunction(
|
|||||||
calleeCallableName = calleeCallableName == null ? "" : calleeCallableName;
|
calleeCallableName = calleeCallableName == null ? "" : calleeCallableName;
|
||||||
label = label == null ? "" : label;
|
label = label == null ? "" : label;
|
||||||
targetLabel = targetLabel == null ? "" : targetLabel;
|
targetLabel = targetLabel == null ? "" : targetLabel;
|
||||||
|
if (calleeCallableId != null && calleeCallableId < 0) {
|
||||||
|
throw new IllegalArgumentException("calleeCallableId must be non-negative");
|
||||||
|
}
|
||||||
if (expectedArgSlots != null && expectedArgSlots < 0) {
|
if (expectedArgSlots != null && expectedArgSlots < 0) {
|
||||||
throw new IllegalArgumentException("expectedArgSlots must be non-negative");
|
throw new IllegalArgumentException("expectedArgSlots must be non-negative");
|
||||||
}
|
}
|
||||||
@ -90,8 +122,8 @@ public record IRBackendExecutableFunction(
|
|||||||
}
|
}
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case CALL_FUNC -> {
|
case CALL_FUNC -> {
|
||||||
if (calleeCallableName.isBlank()) {
|
if (calleeCallableId == null) {
|
||||||
throw new IllegalArgumentException("CALL_FUNC requires calleeCallableName");
|
throw new IllegalArgumentException("CALL_FUNC requires calleeCallableId");
|
||||||
}
|
}
|
||||||
if (hostCall != null || intrinsicCall != null) {
|
if (hostCall != null || intrinsicCall != null) {
|
||||||
throw new IllegalArgumentException("CALL_FUNC must not carry host or intrinsic metadata");
|
throw new IllegalArgumentException("CALL_FUNC must not carry host or intrinsic metadata");
|
||||||
@ -101,6 +133,9 @@ public record IRBackendExecutableFunction(
|
|||||||
if (hostCall == null) {
|
if (hostCall == null) {
|
||||||
throw new IllegalArgumentException("CALL_HOST requires hostCall metadata");
|
throw new IllegalArgumentException("CALL_HOST requires hostCall metadata");
|
||||||
}
|
}
|
||||||
|
if (calleeCallableId != null) {
|
||||||
|
throw new IllegalArgumentException("CALL_HOST must not carry calleeCallableId");
|
||||||
|
}
|
||||||
if (intrinsicCall != null) {
|
if (intrinsicCall != null) {
|
||||||
throw new IllegalArgumentException("CALL_HOST must not carry intrinsic metadata");
|
throw new IllegalArgumentException("CALL_HOST must not carry intrinsic metadata");
|
||||||
}
|
}
|
||||||
@ -109,12 +144,15 @@ public record IRBackendExecutableFunction(
|
|||||||
if (intrinsicCall == null) {
|
if (intrinsicCall == null) {
|
||||||
throw new IllegalArgumentException("CALL_INTRINSIC requires intrinsic metadata");
|
throw new IllegalArgumentException("CALL_INTRINSIC requires intrinsic metadata");
|
||||||
}
|
}
|
||||||
|
if (calleeCallableId != null) {
|
||||||
|
throw new IllegalArgumentException("CALL_INTRINSIC must not carry calleeCallableId");
|
||||||
|
}
|
||||||
if (hostCall != null) {
|
if (hostCall != null) {
|
||||||
throw new IllegalArgumentException("CALL_INTRINSIC must not carry host metadata");
|
throw new IllegalArgumentException("CALL_INTRINSIC must not carry host metadata");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case HALT, RET -> {
|
case HALT, RET -> {
|
||||||
if (!calleeCallableName.isBlank() || hostCall != null || intrinsicCall != null) {
|
if (!calleeCallableName.isBlank() || calleeCallableId != null || hostCall != null || intrinsicCall != null) {
|
||||||
throw new IllegalArgumentException(kind + " must not carry callsite metadata");
|
throw new IllegalArgumentException(kind + " must not carry callsite metadata");
|
||||||
}
|
}
|
||||||
if (expectedArgSlots != null || expectedRetSlots != null) {
|
if (expectedArgSlots != null || expectedRetSlots != null) {
|
||||||
@ -127,6 +165,7 @@ public record IRBackendExecutableFunction(
|
|||||||
}
|
}
|
||||||
if (!targetLabel.isBlank()
|
if (!targetLabel.isBlank()
|
||||||
|| !calleeCallableName.isBlank()
|
|| !calleeCallableName.isBlank()
|
||||||
|
|| calleeCallableId != null
|
||||||
|| hostCall != null
|
|| hostCall != null
|
||||||
|| intrinsicCall != null
|
|| intrinsicCall != null
|
||||||
|| expectedArgSlots != null
|
|| expectedArgSlots != null
|
||||||
@ -140,6 +179,7 @@ public record IRBackendExecutableFunction(
|
|||||||
}
|
}
|
||||||
if (!label.isBlank()
|
if (!label.isBlank()
|
||||||
|| !calleeCallableName.isBlank()
|
|| !calleeCallableName.isBlank()
|
||||||
|
|| calleeCallableId != null
|
||||||
|| hostCall != null
|
|| hostCall != null
|
||||||
|| intrinsicCall != null
|
|| intrinsicCall != null
|
||||||
|| expectedArgSlots != null
|
|| expectedArgSlots != null
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package p.studio.compiler.models;
|
package p.studio.compiler.models;
|
||||||
|
|
||||||
import p.studio.compiler.source.identifiers.FileId;
|
import p.studio.compiler.source.identifiers.FileId;
|
||||||
|
import p.studio.compiler.source.tables.CallableSignatureRef;
|
||||||
import p.studio.utilities.structures.ReadOnlyList;
|
import p.studio.utilities.structures.ReadOnlyList;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -9,28 +10,30 @@ public record IRBackendFile(
|
|||||||
FileId fileId,
|
FileId fileId,
|
||||||
ReadOnlyList<IRFunction> functions,
|
ReadOnlyList<IRFunction> functions,
|
||||||
ReadOnlyList<IRBackendExecutableFunction> executableFunctions,
|
ReadOnlyList<IRBackendExecutableFunction> executableFunctions,
|
||||||
IRReservedMetadata reservedMetadata) {
|
IRReservedMetadata reservedMetadata,
|
||||||
|
ReadOnlyList<CallableSignatureRef> callableSignatures) {
|
||||||
public IRBackendFile {
|
public IRBackendFile {
|
||||||
fileId = Objects.requireNonNull(fileId, "fileId");
|
fileId = Objects.requireNonNull(fileId, "fileId");
|
||||||
functions = functions == null ? ReadOnlyList.empty() : functions;
|
functions = functions == null ? ReadOnlyList.empty() : functions;
|
||||||
executableFunctions = executableFunctions == null ? ReadOnlyList.empty() : executableFunctions;
|
executableFunctions = executableFunctions == null ? ReadOnlyList.empty() : executableFunctions;
|
||||||
reservedMetadata = reservedMetadata == null ? IRReservedMetadata.empty() : reservedMetadata;
|
reservedMetadata = reservedMetadata == null ? IRReservedMetadata.empty() : reservedMetadata;
|
||||||
|
callableSignatures = callableSignatures == null ? ReadOnlyList.empty() : callableSignatures;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IRBackendFile(
|
public IRBackendFile(
|
||||||
final FileId fileId,
|
final FileId fileId,
|
||||||
final ReadOnlyList<IRFunction> functions) {
|
final ReadOnlyList<IRFunction> functions) {
|
||||||
this(fileId, functions, ReadOnlyList.empty(), IRReservedMetadata.empty());
|
this(fileId, functions, ReadOnlyList.empty(), IRReservedMetadata.empty(), ReadOnlyList.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IRBackendFile empty(final FileId fileId) {
|
public static IRBackendFile empty(final FileId fileId) {
|
||||||
return new IRBackendFile(fileId, ReadOnlyList.empty(), ReadOnlyList.empty(), IRReservedMetadata.empty());
|
return new IRBackendFile(fileId, ReadOnlyList.empty(), ReadOnlyList.empty(), IRReservedMetadata.empty(), ReadOnlyList.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
public IRBackendFile(
|
public IRBackendFile(
|
||||||
final FileId fileId,
|
final FileId fileId,
|
||||||
final ReadOnlyList<IRFunction> functions,
|
final ReadOnlyList<IRFunction> functions,
|
||||||
final IRReservedMetadata reservedMetadata) {
|
final IRReservedMetadata reservedMetadata) {
|
||||||
this(fileId, functions, ReadOnlyList.empty(), reservedMetadata);
|
this(fileId, functions, ReadOnlyList.empty(), reservedMetadata, ReadOnlyList.empty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package p.studio.compiler.models;
|
|||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import p.studio.compiler.source.Span;
|
import p.studio.compiler.source.Span;
|
||||||
import p.studio.compiler.source.identifiers.FileId;
|
import p.studio.compiler.source.identifiers.FileId;
|
||||||
|
import p.studio.compiler.source.tables.CallableSignatureRef;
|
||||||
import p.studio.utilities.structures.ReadOnlyList;
|
import p.studio.utilities.structures.ReadOnlyList;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
@ -25,6 +26,7 @@ class IRBackendExecutableContractTest {
|
|||||||
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
|
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
|
||||||
"app/main",
|
"app/main",
|
||||||
"foo",
|
"foo",
|
||||||
|
7,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
Span.none());
|
Span.none());
|
||||||
@ -37,6 +39,7 @@ class IRBackendExecutableContractTest {
|
|||||||
new FileId(1),
|
new FileId(1),
|
||||||
"app",
|
"app",
|
||||||
"main",
|
"main",
|
||||||
|
1,
|
||||||
10,
|
10,
|
||||||
5,
|
5,
|
||||||
0,
|
0,
|
||||||
@ -50,6 +53,7 @@ class IRBackendExecutableContractTest {
|
|||||||
new FileId(1),
|
new FileId(1),
|
||||||
"app",
|
"app",
|
||||||
"main",
|
"main",
|
||||||
|
1,
|
||||||
0,
|
0,
|
||||||
10,
|
10,
|
||||||
0,
|
0,
|
||||||
@ -67,6 +71,7 @@ class IRBackendExecutableContractTest {
|
|||||||
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
|
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
|
||||||
"app/main",
|
"app/main",
|
||||||
"foo",
|
"foo",
|
||||||
|
1,
|
||||||
new IRBackendExecutableFunction.HostCallMetadata("gfx", "draw", 1, 0, 0),
|
new IRBackendExecutableFunction.HostCallMetadata("gfx", "draw", 1, 0, 0),
|
||||||
null,
|
null,
|
||||||
Span.none()));
|
Span.none()));
|
||||||
@ -87,6 +92,7 @@ class IRBackendExecutableContractTest {
|
|||||||
"",
|
"",
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
null,
|
null,
|
||||||
@ -99,6 +105,7 @@ class IRBackendExecutableContractTest {
|
|||||||
"",
|
"",
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
null,
|
null,
|
||||||
@ -116,6 +123,7 @@ class IRBackendExecutableContractTest {
|
|||||||
"app/main",
|
"app/main",
|
||||||
"entry",
|
"entry",
|
||||||
0,
|
0,
|
||||||
|
0,
|
||||||
10,
|
10,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
@ -127,9 +135,11 @@ class IRBackendExecutableContractTest {
|
|||||||
"",
|
"",
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
Span.none())),
|
Span.none())),
|
||||||
Span.none())),
|
Span.none())),
|
||||||
IRReservedMetadata.empty());
|
IRReservedMetadata.empty(),
|
||||||
|
ReadOnlyList.from(new CallableSignatureRef("app/main", "entry", 0, "() -> unit")));
|
||||||
final var fileB = new IRBackendFile(
|
final var fileB = new IRBackendFile(
|
||||||
new FileId(2),
|
new FileId(2),
|
||||||
ReadOnlyList.empty(),
|
ReadOnlyList.empty(),
|
||||||
@ -137,6 +147,7 @@ class IRBackendExecutableContractTest {
|
|||||||
new FileId(2),
|
new FileId(2),
|
||||||
"app/main",
|
"app/main",
|
||||||
"aux",
|
"aux",
|
||||||
|
0,
|
||||||
11,
|
11,
|
||||||
20,
|
20,
|
||||||
0,
|
0,
|
||||||
@ -149,9 +160,11 @@ class IRBackendExecutableContractTest {
|
|||||||
"",
|
"",
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
Span.none())),
|
Span.none())),
|
||||||
Span.none())),
|
Span.none())),
|
||||||
IRReservedMetadata.empty());
|
IRReservedMetadata.empty(),
|
||||||
|
ReadOnlyList.from(new CallableSignatureRef("app/main", "aux", 0, "() -> unit")));
|
||||||
|
|
||||||
final var aggregator = IRBackend.aggregator();
|
final var aggregator = IRBackend.aggregator();
|
||||||
aggregator.merge(fileA);
|
aggregator.merge(fileA);
|
||||||
@ -161,5 +174,6 @@ class IRBackendExecutableContractTest {
|
|||||||
assertEquals(2, backend.getExecutableFunctions().size());
|
assertEquals(2, backend.getExecutableFunctions().size());
|
||||||
assertEquals("entry", backend.getExecutableFunctions().get(0).callableName());
|
assertEquals("entry", backend.getExecutableFunctions().get(0).callableName());
|
||||||
assertEquals("aux", backend.getExecutableFunctions().get(1).callableName());
|
assertEquals("aux", backend.getExecutableFunctions().get(1).callableName());
|
||||||
|
assertEquals(2, backend.getCallableSignatures().size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user