implements PR-038

This commit is contained in:
bQUARKz 2026-03-07 16:52:50 +00:00
parent 574e890c21
commit 0728637f58
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
4 changed files with 200 additions and 2 deletions

View File

@ -13,6 +13,8 @@ public class IRBackend {
@Builder.Default
private final ReadOnlyList<IRFunction> functions = ReadOnlyList.empty();
@Builder.Default
private final ReadOnlyList<IRBackendExecutableFunction> executableFunctions = ReadOnlyList.empty();
@Builder.Default
private final IRReservedMetadata reservedMetadata = IRReservedMetadata.empty();
public static IRBackendAggregator aggregator() {
@ -21,6 +23,7 @@ public class IRBackend {
public static final class IRBackendAggregator {
private final ArrayList<IRFunction> functions = new ArrayList<>();
private final ArrayList<IRBackendExecutableFunction> executableFunctions = new ArrayList<>();
private final ArrayList<IRReservedMetadata.HostMethodBinding> hostMethodBindings = new ArrayList<>();
private final ArrayList<IRReservedMetadata.BuiltinTypeSurface> builtinTypeSurfaces = new ArrayList<>();
private final ArrayList<IRReservedMetadata.BuiltinConstSurface> builtinConstSurfaces = new ArrayList<>();
@ -31,6 +34,7 @@ public class IRBackend {
return;
}
functions.addAll(backendFile.functions().asList());
executableFunctions.addAll(backendFile.executableFunctions().asList());
final var metadata = backendFile.reservedMetadata();
hostMethodBindings.addAll(metadata.hostMethodBindings().asList());
builtinTypeSurfaces.addAll(metadata.builtinTypeSurfaces().asList());
@ -42,6 +46,7 @@ public class IRBackend {
return IRBackend
.builder()
.functions(ReadOnlyList.wrap(functions))
.executableFunctions(ReadOnlyList.wrap(executableFunctions))
.reservedMetadata(new IRReservedMetadata(
ReadOnlyList.wrap(hostMethodBindings),
ReadOnlyList.wrap(builtinTypeSurfaces),
@ -55,6 +60,7 @@ public class IRBackend {
public String toString() {
final var sb = new StringBuilder();
sb.append("IRBackend{functions=").append(functions.size())
.append(", executableFunctions=").append(executableFunctions.size())
.append(", hostBindings=").append(reservedMetadata.hostMethodBindings().size())
.append(", builtinTypes=").append(reservedMetadata.builtinTypeSurfaces().size())
.append(", builtinConsts=").append(reservedMetadata.builtinConstSurfaces().size())

View File

@ -0,0 +1,93 @@
package p.studio.compiler.models;
import p.studio.compiler.source.Span;
import p.studio.compiler.source.identifiers.FileId;
import p.studio.utilities.structures.ReadOnlyList;
import java.util.Objects;
public record IRBackendExecutableFunction(
FileId fileId,
String moduleKey,
String callableName,
int sourceStart,
int sourceEnd,
int paramSlots,
int localSlots,
int returnSlots,
int maxStackSlots,
ReadOnlyList<Instruction> instructions,
Span span) {
public IRBackendExecutableFunction {
fileId = Objects.requireNonNull(fileId, "fileId");
moduleKey = moduleKey == null ? "" : moduleKey;
callableName = Objects.requireNonNull(callableName, "callableName");
instructions = instructions == null ? ReadOnlyList.empty() : instructions;
span = span == null ? Span.none() : span;
}
public record Instruction(
InstructionKind kind,
String calleeModuleKey,
String calleeCallableName,
HostCallMetadata hostCall,
IntrinsicCallMetadata intrinsicCall,
Span span) {
public Instruction {
Objects.requireNonNull(kind, "kind");
span = span == null ? Span.none() : span;
calleeModuleKey = calleeModuleKey == null ? "" : calleeModuleKey;
calleeCallableName = calleeCallableName == null ? "" : calleeCallableName;
switch (kind) {
case CALL_FUNC -> {
if (calleeCallableName.isBlank()) {
throw new IllegalArgumentException("CALL_FUNC requires calleeCallableName");
}
}
case CALL_HOST -> {
if (hostCall == null) {
throw new IllegalArgumentException("CALL_HOST requires hostCall metadata");
}
}
case CALL_INTRINSIC -> {
if (intrinsicCall == null) {
throw new IllegalArgumentException("CALL_INTRINSIC requires intrinsic metadata");
}
}
case HALT, RET -> {
}
}
}
}
public enum InstructionKind {
HALT,
RET,
CALL_FUNC,
CALL_HOST,
CALL_INTRINSIC,
}
public record HostCallMetadata(
String module,
String name,
long version,
int argSlots,
int retSlots) {
public HostCallMetadata {
module = Objects.requireNonNull(module, "module");
name = Objects.requireNonNull(name, "name");
}
}
public record IntrinsicCallMetadata(
String canonicalName,
long canonicalVersion,
int intrinsicId) {
public IntrinsicCallMetadata {
canonicalName = Objects.requireNonNull(canonicalName, "canonicalName");
}
}
}

View File

@ -8,20 +8,29 @@ import java.util.Objects;
public record IRBackendFile(
FileId fileId,
ReadOnlyList<IRFunction> functions,
ReadOnlyList<IRBackendExecutableFunction> executableFunctions,
IRReservedMetadata reservedMetadata) {
public IRBackendFile {
fileId = Objects.requireNonNull(fileId, "fileId");
functions = functions == null ? ReadOnlyList.empty() : functions;
executableFunctions = executableFunctions == null ? ReadOnlyList.empty() : executableFunctions;
reservedMetadata = reservedMetadata == null ? IRReservedMetadata.empty() : reservedMetadata;
}
public IRBackendFile(
final FileId fileId,
final ReadOnlyList<IRFunction> functions) {
this(fileId, functions, IRReservedMetadata.empty());
this(fileId, functions, ReadOnlyList.empty(), IRReservedMetadata.empty());
}
public static IRBackendFile empty(final FileId fileId) {
return new IRBackendFile(fileId, ReadOnlyList.empty(), IRReservedMetadata.empty());
return new IRBackendFile(fileId, ReadOnlyList.empty(), ReadOnlyList.empty(), IRReservedMetadata.empty());
}
public IRBackendFile(
final FileId fileId,
final ReadOnlyList<IRFunction> functions,
final IRReservedMetadata reservedMetadata) {
this(fileId, functions, ReadOnlyList.empty(), reservedMetadata);
}
}

View File

@ -0,0 +1,90 @@
package p.studio.compiler.models;
import org.junit.jupiter.api.Test;
import p.studio.compiler.source.Span;
import p.studio.compiler.source.identifiers.FileId;
import p.studio.utilities.structures.ReadOnlyList;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
class IRBackendExecutableContractTest {
@Test
void callInstructionMustRequireCategorySpecificMetadata() {
assertThrows(IllegalArgumentException.class, () -> new IRBackendExecutableFunction.Instruction(
IRBackendExecutableFunction.InstructionKind.CALL_HOST,
"",
"",
null,
null,
Span.none()));
final var callFunc = new IRBackendExecutableFunction.Instruction(
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
"app/main",
"foo",
null,
null,
Span.none());
assertEquals(IRBackendExecutableFunction.InstructionKind.CALL_FUNC, callFunc.kind());
}
@Test
void aggregatorMustPreserveExecutableFunctionOrderDeterministically() {
final var fileA = new IRBackendFile(
new FileId(1),
ReadOnlyList.empty(),
ReadOnlyList.from(new IRBackendExecutableFunction(
new FileId(1),
"app/main",
"entry",
0,
10,
0,
0,
0,
1,
ReadOnlyList.from(new IRBackendExecutableFunction.Instruction(
IRBackendExecutableFunction.InstructionKind.HALT,
"",
"",
null,
null,
Span.none())),
Span.none())),
IRReservedMetadata.empty());
final var fileB = new IRBackendFile(
new FileId(2),
ReadOnlyList.empty(),
ReadOnlyList.from(new IRBackendExecutableFunction(
new FileId(2),
"app/main",
"aux",
11,
20,
0,
0,
0,
1,
ReadOnlyList.from(new IRBackendExecutableFunction.Instruction(
IRBackendExecutableFunction.InstructionKind.RET,
"",
"",
null,
null,
Span.none())),
Span.none())),
IRReservedMetadata.empty());
final var aggregator = IRBackend.aggregator();
aggregator.merge(fileA);
aggregator.merge(fileB);
final var backend = aggregator.emit();
assertEquals(2, backend.getExecutableFunctions().size());
assertEquals("entry", backend.getExecutableFunctions().get(0).callableName());
assertEquals("aux", backend.getExecutableFunctions().get(1).callableName());
}
}