implements PR-08.2

This commit is contained in:
bQUARKz 2026-03-09 14:22:22 +00:00
parent 40188b67ab
commit 0490f64870
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
11 changed files with 520 additions and 23 deletions

View File

@ -19,6 +19,7 @@ import p.studio.compiler.source.identifiers.CallableShapeId;
import p.studio.compiler.source.identifiers.CallableId;
import p.studio.compiler.source.identifiers.FileId;
import p.studio.compiler.source.identifiers.IntrinsicId;
import p.studio.compiler.source.identifiers.ModuleId;
import p.studio.compiler.source.identifiers.NameId;
import p.studio.compiler.source.identifiers.TypeSurfaceId;
import p.studio.compiler.source.tables.CallableShapeTable;
@ -26,6 +27,7 @@ import p.studio.compiler.source.tables.CallableSignatureRef;
import p.studio.compiler.source.tables.CallableTable;
import p.studio.compiler.source.tables.IntrinsicReference;
import p.studio.compiler.source.tables.IntrinsicTable;
import p.studio.compiler.source.tables.ModuleReference;
import p.studio.compiler.source.tables.NameTable;
import p.studio.compiler.source.tables.TypeSurfaceTable;
import p.studio.utilities.structures.ReadOnlyList;
@ -71,7 +73,16 @@ public final class PbsFrontendCompiler {
? PbsParser.ParseMode.INTERFACE_MODULE
: PbsParser.ParseMode.ORDINARY;
final var ast = PbsParser.parse(tokens, fileId, diagnostics, parseMode);
final var irBackendFile = compileParsedFile(fileId, ast, diagnostics, sourceKind, "", hostAdmissionContext, nameTable);
final var irBackendFile = compileParsedFile(
fileId,
ast,
diagnostics,
sourceKind,
ModuleId.none(),
"",
ReadOnlyList.empty(),
hostAdmissionContext,
nameTable);
if (diagnostics.errorCount() > admissionBaseline) {
return IRBackendFile.empty(fileId);
}
@ -99,7 +110,16 @@ public final class PbsFrontendCompiler {
final DiagnosticSink diagnostics,
final SourceKind sourceKind,
final HostAdmissionContext hostAdmissionContext) {
return compileParsedFile(fileId, ast, diagnostics, sourceKind, "", hostAdmissionContext, new NameTable());
return compileParsedFile(
fileId,
ast,
diagnostics,
sourceKind,
ModuleId.none(),
"",
ReadOnlyList.empty(),
hostAdmissionContext,
new NameTable());
}
public IRBackendFile compileParsedFile(
@ -109,7 +129,16 @@ public final class PbsFrontendCompiler {
final SourceKind sourceKind,
final String moduleKey,
final HostAdmissionContext hostAdmissionContext) {
return compileParsedFile(fileId, ast, diagnostics, sourceKind, moduleKey, hostAdmissionContext, new NameTable());
return compileParsedFile(
fileId,
ast,
diagnostics,
sourceKind,
ModuleId.none(),
moduleKey,
ReadOnlyList.empty(),
hostAdmissionContext,
new NameTable());
}
public IRBackendFile compileParsedFile(
@ -117,7 +146,9 @@ public final class PbsFrontendCompiler {
final PbsAst.File ast,
final DiagnosticSink diagnostics,
final SourceKind sourceKind,
final ModuleId moduleId,
final String moduleKey,
final ReadOnlyList<ModuleReference> modulePool,
final HostAdmissionContext hostAdmissionContext,
final NameTable nameTable) {
return compileParsedFile(
@ -125,7 +156,9 @@ public final class PbsFrontendCompiler {
ast,
diagnostics,
sourceKind,
moduleId,
moduleKey,
modulePool,
hostAdmissionContext,
nameTable,
ReadOnlyList.empty(),
@ -138,12 +171,16 @@ public final class PbsFrontendCompiler {
final PbsAst.File ast,
final DiagnosticSink diagnostics,
final SourceKind sourceKind,
final ModuleId moduleId,
final String moduleKey,
final ReadOnlyList<ModuleReference> modulePool,
final HostAdmissionContext hostAdmissionContext,
final NameTable nameTable,
final ReadOnlyList<PbsAst.TopDecl> supplementalTopDecls,
final ReadOnlyList<ImportedCallableSurface> importedCallables,
final IRReservedMetadata importedReservedMetadata) {
final var effectiveModuleId = moduleId == null ? ModuleId.none() : moduleId;
final var effectiveModulePool = modulePool == null ? ReadOnlyList.<ModuleReference>empty() : modulePool;
final var effectiveNameTable = nameTable == null ? new NameTable() : nameTable;
final var effectiveSupplementalTopDecls = supplementalTopDecls == null
? ReadOnlyList.<PbsAst.TopDecl>empty()
@ -187,6 +224,7 @@ public final class PbsFrontendCompiler {
fileId,
ast,
effectiveSupplementalTopDecls,
effectiveModuleId,
moduleKey,
effectiveReservedMetadata,
effectiveNameTable,
@ -200,6 +238,7 @@ public final class PbsFrontendCompiler {
functions,
executableLowering.executableFunctions(),
reservedMetadata,
effectiveModulePool,
executableLowering.callableSignatures(),
executableLowering.intrinsicPool());
}
@ -221,12 +260,14 @@ public final class PbsFrontendCompiler {
final FileId fileId,
final PbsAst.File ast,
final ReadOnlyList<PbsAst.TopDecl> supplementalTopDecls,
final ModuleId moduleId,
final String moduleKey,
final IRReservedMetadata reservedMetadata,
final NameTable nameTable,
final DiagnosticSink diagnostics,
final ReadOnlyList<ImportedCallableSurface> importedCallables) {
final var normalizedModuleKey = moduleKey == null ? "" : moduleKey;
final var normalizedModuleId = moduleId == null ? ModuleId.none() : moduleId;
final var hostByMethodName = new HashMap<NameId, List<IRReservedMetadata.HostMethodBinding>>();
for (final var hostBinding : reservedMetadata.hostMethodBindings()) {
hostByMethodName
@ -318,6 +359,7 @@ public final class PbsFrontendCompiler {
final var declaredFn = declaredCallable.functionDecl();
final var callableShapeId = callableShapeId(declaredFn, typeSurfaceTable, callableShapeTable);
final var callableId = callableIdTable.register(
normalizedModuleId,
normalizedModuleKey,
declaredCallable.callableName(),
declaredFn.parameters().size(),
@ -333,6 +375,7 @@ public final class PbsFrontendCompiler {
}
for (final var importedCallable : importedCallables) {
final var callableId = callableIdTable.register(
importedCallable.moduleId(),
importedCallable.moduleKey(),
importedCallable.callableName(),
importedCallable.arity(),
@ -359,6 +402,7 @@ public final class PbsFrontendCompiler {
localSlotByNameId.put(nameTable.register(fn.parameters().get(paramIndex).name()), paramIndex);
}
final var loweringContext = new ExecutableLoweringContext(
normalizedModuleId,
normalizedModuleKey,
diagnostics,
nameTable,
@ -401,6 +445,7 @@ public final class PbsFrontendCompiler {
final var end = safeToInt(fn.span().getEnd());
executableFunctions.add(new IRBackendExecutableFunction(
fileId,
normalizedModuleId,
normalizedModuleKey,
callable.callableName(),
functionCallableId,
@ -921,6 +966,7 @@ public final class PbsFrontendCompiler {
final var calleeCallableId = callableCandidates.getFirst();
context.instructions().add(new IRBackendExecutableFunction.Instruction(
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
context.moduleId(),
context.moduleKey(),
calleeIdentity.primaryCallableDisplayName(),
calleeCallableId,
@ -1292,6 +1338,7 @@ public final class PbsFrontendCompiler {
}
private static final class ExecutableLoweringContext {
private final ModuleId moduleId;
private final String moduleKey;
private final DiagnosticSink diagnostics;
private final NameTable nameTable;
@ -1308,6 +1355,7 @@ public final class PbsFrontendCompiler {
private int nextLabelId = 0;
private ExecutableLoweringContext(
final ModuleId moduleId,
final String moduleKey,
final DiagnosticSink diagnostics,
final NameTable nameTable,
@ -1319,6 +1367,7 @@ public final class PbsFrontendCompiler {
final Map<CallableId, Integer> returnSlotsByCallableId,
final IntrinsicTable intrinsicIdTable,
final Map<NameId, Integer> localSlotByNameId) {
this.moduleId = moduleId == null ? ModuleId.none() : moduleId;
this.moduleKey = moduleKey;
this.diagnostics = diagnostics;
this.nameTable = nameTable;
@ -1332,6 +1381,10 @@ public final class PbsFrontendCompiler {
this.localSlotByNameId = localSlotByNameId == null ? Map.of() : localSlotByNameId;
}
private ModuleId moduleId() {
return moduleId;
}
private String moduleKey() {
return moduleKey;
}
@ -1440,11 +1493,15 @@ public final class PbsFrontendCompiler {
}
public record ImportedCallableSurface(
ModuleId moduleId,
String moduleKey,
String callableName,
int arity,
int returnSlots,
String shapeSurface) {
public ImportedCallableSurface {
moduleId = moduleId == null ? ModuleId.none() : moduleId;
}
}
private static final class ExecutableLoweringAnalysisException extends RuntimeException {

View File

@ -159,6 +159,7 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
markModulesWithLinkingErrors(diagnostics, moduleIdByFile, failedModuleIds);
final var moduleDependencyGraph = buildModuleDependencyGraph(parsedSourceFiles, moduleTable);
final var importedSemanticContexts = buildImportedSemanticContexts(parsedSourceFiles, moduleTable);
final var canonicalModulePool = emitModulePool(moduleTable);
final var compiledSourceFiles = new ArrayList<CompiledSourceFile>(parsedSourceFiles.size());
for (final var parsedSource : parsedSourceFiles) {
@ -175,7 +176,9 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
parsedSource.ast(),
diagnostics,
parsedSource.sourceKind(),
parsedSource.moduleId(),
renderModuleKey(moduleTable, parsedSource.moduleId()),
canonicalModulePool,
ctx.hostAdmissionContext(),
nameTable,
importedSemanticContext.supplementalTopDecls(),
@ -431,6 +434,7 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
importedCallables,
importedCallableKeys,
new PbsFrontendCompiler.ImportedCallableSurface(
importedModuleId,
importedModuleKey,
localName + "." + method.name(),
method.parameters().size(),
@ -444,6 +448,7 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
importedCallables,
importedCallableKeys,
new PbsFrontendCompiler.ImportedCallableSurface(
importedModuleId,
importedModuleKey,
localName,
functionDecl.parameters().size(),
@ -793,6 +798,14 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
return moduleReference.project() + ":" + String.join("/", moduleReference.pathSegments().asList());
}
private ReadOnlyList<ModuleReference> emitModulePool(final ModuleTable moduleTable) {
final var pool = new ArrayList<ModuleReference>(moduleTable.size());
for (final var moduleId : moduleTable.identifiers()) {
pool.add(moduleTable.get(moduleId));
}
return ReadOnlyList.wrap(pool);
}
private static final class MutableModuleUnit {
private final ArrayList<PbsModuleVisibilityValidator.SourceFile> sources = new ArrayList<>();
private final ArrayList<PbsModuleVisibilityValidator.BarrelFile> barrels = new ArrayList<>();

View File

@ -6,19 +6,15 @@ import p.studio.compiler.models.IRBackend;
import p.studio.compiler.models.IRBackendExecutableFunction;
import p.studio.compiler.source.Span;
import p.studio.compiler.source.identifiers.CallableId;
import p.studio.compiler.source.identifiers.ModuleId;
import p.studio.compiler.source.tables.ModuleReference;
import p.studio.utilities.structures.ReadOnlyList;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.StringJoiner;
public class LowerToIRVMService {
private static final Comparator<IRBackendExecutableFunction> FUNCTION_ORDER = Comparator
.comparing(IRBackendExecutableFunction::moduleKey)
.thenComparing(IRBackendExecutableFunction::callableName)
.thenComparingInt(function -> function.callableId().getIndex())
.thenComparingInt(IRBackendExecutableFunction::sourceStart);
private final IRVMValidator validator;
public LowerToIRVMService() {
@ -301,7 +297,23 @@ public class LowerToIRVMService {
"frontend IRBackend entrypoint declaration is missing");
}
final var sorted = new ArrayList<>(backend.getExecutableFunctions().asList());
sorted.sort(FUNCTION_ORDER);
sorted.sort((left, right) -> {
final var leftModuleKey = moduleSortKey(backend, left.moduleId(), left.moduleKey());
final var rightModuleKey = moduleSortKey(backend, right.moduleId(), right.moduleKey());
final var moduleCmp = leftModuleKey.compareTo(rightModuleKey);
if (moduleCmp != 0) {
return moduleCmp;
}
final var nameCmp = left.callableName().compareTo(right.callableName());
if (nameCmp != 0) {
return nameCmp;
}
final var idCmp = Integer.compare(left.callableId().getIndex(), right.callableId().getIndex());
if (idCmp != 0) {
return idCmp;
}
return Integer.compare(left.sourceStart(), right.sourceStart());
});
final var entrypoints = sorted.stream()
.filter(candidate -> entryPointCallableName.equals(candidate.callableName()))
.toList();
@ -328,6 +340,27 @@ public class LowerToIRVMService {
return ReadOnlyList.wrap(ordered);
}
private String moduleSortKey(
final IRBackend backend,
final ModuleId moduleId,
final String fallbackModuleKey) {
if (moduleId != null && !moduleId.isNone()) {
final var index = moduleId.getIndex();
if (index >= 0 && index < backend.getModulePool().size()) {
return canonicalModuleSortKey(backend.getModulePool().get(index));
}
}
return fallbackModuleKey == null ? "" : fallbackModuleKey;
}
private String canonicalModuleSortKey(final ModuleReference reference) {
final var joiner = new StringJoiner("/", reference.project() + ":", "");
for (final var segment : reference.pathSegments()) {
joiner.add(segment);
}
return joiner.toString();
}
private BytecodeModule.SourceSpan toBytecodeSpan(
final int fallbackFileId,
final Span span) {

View File

@ -7,7 +7,9 @@ import p.studio.compiler.source.Span;
import p.studio.compiler.source.identifiers.CallableId;
import p.studio.compiler.source.identifiers.FileId;
import p.studio.compiler.source.identifiers.IntrinsicId;
import p.studio.compiler.source.identifiers.ModuleId;
import p.studio.compiler.source.tables.IntrinsicReference;
import p.studio.compiler.source.tables.ModuleReference;
import p.studio.utilities.structures.ReadOnlyList;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -199,6 +201,27 @@ class LowerToIRVMServiceTest {
assertEquals(IRVMLoweringErrorCode.LOWER_IRVM_ENTRYPOINT_AMBIGUOUS, thrown.code());
}
@Test
void lowerMustUseModulePoolCanonicalIdentityForNonEntrypointOrdering() {
final var backend = IRBackend.builder()
.entryPointCallableName("frame")
.modulePool(ReadOnlyList.from(
new ModuleReference("app", ReadOnlyList.from("alpha")),
new ModuleReference("app", ReadOnlyList.from("zeta"))))
.executableFunctions(ReadOnlyList.from(
fnWithModule("helper_in_zeta", "legacy/zeta", 1, 21, ReadOnlyList.from(ret())),
fnWithModule("frame", "legacy/zeta", 1, 20, ReadOnlyList.from(ret())),
fnWithModule("helper_in_alpha", "legacy/alpha", 0, 22, ReadOnlyList.from(ret()))))
.build();
final var lowered = new LowerToIRVMService().lower(backend);
assertEquals(3, lowered.module().functions().size());
assertEquals("frame", lowered.module().functions().get(0).name());
assertEquals("helper_in_alpha", lowered.module().functions().get(1).name());
assertEquals("helper_in_zeta", lowered.module().functions().get(2).name());
}
private static IRBackendExecutableFunction fn(
final String name,
final String moduleKey,
@ -219,6 +242,28 @@ class LowerToIRVMServiceTest {
Span.none());
}
private static IRBackendExecutableFunction fnWithModule(
final String name,
final String moduleKey,
final int moduleId,
final int callableId,
final ReadOnlyList<IRBackendExecutableFunction.Instruction> instructions) {
return new IRBackendExecutableFunction(
new FileId(0),
new ModuleId(moduleId),
moduleKey,
name,
new CallableId(callableId),
0,
10,
0,
0,
0,
4,
instructions,
Span.none());
}
private static IRBackendExecutableFunction.Instruction ret() {
return new IRBackendExecutableFunction.Instruction(
IRBackendExecutableFunction.InstructionKind.RET,

View File

@ -1,7 +1,17 @@
package p.studio.compiler.source.identifiers;
public class ModuleId extends SourceIdentifier {
public static final ModuleId NONE = new ModuleId(-1);
public ModuleId(int id) {
super(id);
}
public static ModuleId none() {
return NONE;
}
public boolean isNone() {
return this == NONE;
}
}

View File

@ -1,14 +1,29 @@
package p.studio.compiler.source.tables;
import p.studio.compiler.source.identifiers.ModuleId;
import java.util.Objects;
public record CallableSignatureRef(
ModuleId moduleId,
String moduleKey,
String callableName,
int arity,
String typeShape) {
public CallableSignatureRef(
final String moduleKey,
final String callableName,
final int arity,
final String typeShape) {
this(ModuleId.none(), moduleKey, callableName, arity, typeShape);
}
public CallableSignatureRef {
moduleId = moduleId == null ? ModuleId.none() : moduleId;
if (!moduleId.isNone()) {
moduleId.getIndex();
}
moduleKey = moduleKey == null ? "" : moduleKey;
callableName = Objects.requireNonNull(callableName, "callableName");
typeShape = typeShape == null ? "" : typeShape;

View File

@ -1,6 +1,7 @@
package p.studio.compiler.source.tables;
import p.studio.compiler.source.identifiers.CallableId;
import p.studio.compiler.source.identifiers.ModuleId;
public class CallableTable extends InternTable<CallableId, CallableSignatureRef> implements CallableTableReader {
@ -13,6 +14,15 @@ public class CallableTable extends InternTable<CallableId, CallableSignatureRef>
final String callableName,
final int arity,
final String typeShape) {
return register(new CallableSignatureRef(moduleKey, callableName, arity, typeShape));
return register(new CallableSignatureRef(ModuleId.none(), moduleKey, callableName, arity, typeShape));
}
public CallableId register(
final ModuleId moduleId,
final String moduleKey,
final String callableName,
final int arity,
final String typeShape) {
return register(new CallableSignatureRef(moduleId, moduleKey, callableName, arity, typeShape));
}
}

View File

@ -4,14 +4,18 @@ import lombok.Builder;
import lombok.Getter;
import p.studio.compiler.source.identifiers.CallableId;
import p.studio.compiler.source.identifiers.IntrinsicId;
import p.studio.compiler.source.identifiers.ModuleId;
import p.studio.compiler.source.tables.CallableSignatureRef;
import p.studio.compiler.source.tables.CallableTable;
import p.studio.compiler.source.tables.IntrinsicReference;
import p.studio.compiler.source.tables.IntrinsicTable;
import p.studio.compiler.source.tables.ModuleReference;
import p.studio.compiler.source.tables.ModuleTable;
import p.studio.utilities.structures.ReadOnlyList;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
@Builder
@Getter
@ -23,6 +27,8 @@ public class IRBackend {
@Builder.Default
private final ReadOnlyList<IRBackendExecutableFunction> executableFunctions = ReadOnlyList.empty();
@Builder.Default
private final ReadOnlyList<ModuleReference> modulePool = ReadOnlyList.empty();
@Builder.Default
private final ReadOnlyList<CallableSignatureRef> callableSignatures = ReadOnlyList.empty();
@Builder.Default
private final ReadOnlyList<IntrinsicReference> intrinsicPool = ReadOnlyList.empty();
@ -37,6 +43,7 @@ public class IRBackend {
private String entryPointCallableName;
private final ArrayList<IRFunction> functions = new ArrayList<>();
private final ArrayList<IRBackendExecutableFunction> executableFunctions = new ArrayList<>();
private final ModuleTable moduleTable = new ModuleTable();
private final CallableTable callableTable = new CallableTable();
private final IntrinsicTable intrinsicTable = new IntrinsicTable();
private final ArrayList<IRReservedMetadata.HostMethodBinding> hostMethodBindings = new ArrayList<>();
@ -49,10 +56,11 @@ public class IRBackend {
return;
}
functions.addAll(backendFile.functions().asList());
final var callableRemap = reindexCallables(backendFile.callableSignatures());
final var moduleRemap = reindexModules(backendFile.modulePool());
final var callableRemap = reindexCallables(backendFile.callableSignatures(), moduleRemap);
final var intrinsicRemap = reindexIntrinsics(backendFile.intrinsicPool());
for (final var function : backendFile.executableFunctions()) {
executableFunctions.add(remapExecutableFunction(function, callableRemap, intrinsicRemap));
executableFunctions.add(remapExecutableFunction(function, moduleRemap, callableRemap, intrinsicRemap));
}
final var metadata = backendFile.reservedMetadata();
hostMethodBindings.addAll(metadata.hostMethodBindings().asList());
@ -68,22 +76,52 @@ public class IRBackend {
return this;
}
private CallableId[] reindexCallables(final ReadOnlyList<CallableSignatureRef> localCallableSignatures) {
private ModuleId[] reindexModules(final ReadOnlyList<ModuleReference> localModulePool) {
if (localModulePool == null || localModulePool.isEmpty()) {
return new ModuleId[0];
}
final var remap = new ModuleId[localModulePool.size()];
for (var i = 0; i < localModulePool.size(); i++) {
remap[i] = moduleTable.register(localModulePool.get(i));
}
return remap;
}
private CallableId[] reindexCallables(
final ReadOnlyList<CallableSignatureRef> localCallableSignatures,
final ModuleId[] moduleRemap) {
if (localCallableSignatures == null || localCallableSignatures.isEmpty()) {
return new CallableId[0];
}
final var remap = new CallableId[localCallableSignatures.size()];
for (var i = 0; i < localCallableSignatures.size(); i++) {
remap[i] = callableTable.register(localCallableSignatures.get(i));
final var signature = localCallableSignatures.get(i);
final var remappedModuleId = remapModuleId(
signature.moduleId(),
signature.moduleKey(),
moduleRemap,
"callable signature");
remap[i] = callableTable.register(
remappedModuleId,
signature.moduleKey(),
signature.callableName(),
signature.arity(),
signature.typeShape());
}
return remap;
}
private IRBackendExecutableFunction remapExecutableFunction(
final IRBackendExecutableFunction function,
final ModuleId[] moduleRemap,
final CallableId[] callableRemap,
final IntrinsicId[] intrinsicRemap) {
final var remappedInstructions = new ArrayList<IRBackendExecutableFunction.Instruction>(function.instructions().size());
final var remappedFunctionModuleId = remapModuleId(
function.moduleId(),
function.moduleKey(),
moduleRemap,
"function");
for (final var instruction : function.instructions()) {
final CallableId remappedCallee;
if (instruction.calleeCallableId() == null) {
@ -101,8 +139,14 @@ public class IRBackend {
} else {
remappedIntrinsic = instruction.intrinsicCall();
}
final var remappedCalleeModuleId = remapModuleId(
instruction.calleeModuleId(),
instruction.calleeModuleKey(),
moduleRemap,
"callee");
remappedInstructions.add(new IRBackendExecutableFunction.Instruction(
instruction.kind(),
remappedCalleeModuleId,
instruction.calleeModuleKey(),
instruction.calleeCallableName(),
remappedCallee,
@ -116,6 +160,7 @@ public class IRBackend {
}
return new IRBackendExecutableFunction(
function.fileId(),
remappedFunctionModuleId,
function.moduleKey(),
function.callableName(),
remapCallableId(function.callableId(), callableRemap, "function"),
@ -129,6 +174,45 @@ public class IRBackend {
function.span());
}
private ModuleId remapModuleId(
final ModuleId localModuleId,
final String moduleKey,
final ModuleId[] moduleRemap,
final String label) {
if (localModuleId != null && !localModuleId.isNone()) {
final var localIndex = localModuleId.getIndex();
if (localIndex < 0 || localIndex >= moduleRemap.length) {
throw new IllegalArgumentException("invalid local " + label + " module id: " + localModuleId);
}
return moduleRemap[localIndex];
}
final var fallbackReference = moduleReferenceFromKey(moduleKey);
if (fallbackReference != null) {
return moduleTable.register(fallbackReference);
}
return ModuleId.none();
}
private ModuleReference moduleReferenceFromKey(final String moduleKey) {
if (moduleKey == null || moduleKey.isBlank()) {
return null;
}
final var separator = moduleKey.indexOf(':');
if (separator <= 0) {
return null;
}
final var project = moduleKey.substring(0, separator).trim();
final var rawPath = moduleKey.substring(separator + 1).trim();
if (project.isBlank()) {
return null;
}
if (rawPath.isBlank()) {
return new ModuleReference(project, ReadOnlyList.empty());
}
final var segments = List.of(rawPath.split("/"));
return new ModuleReference(project, ReadOnlyList.wrap(segments));
}
private CallableId remapCallableId(
final CallableId localCallableId,
final CallableId[] callableRemap,
@ -169,6 +253,14 @@ public class IRBackend {
return ReadOnlyList.wrap(signatures);
}
private ReadOnlyList<ModuleReference> emitModulePool() {
final var modules = new ArrayList<ModuleReference>(moduleTable.size());
for (final var moduleId : moduleTable.identifiers()) {
modules.add(moduleTable.get(moduleId));
}
return ReadOnlyList.wrap(modules);
}
private ReadOnlyList<IntrinsicReference> emitIntrinsicPool() {
final var pool = new ArrayList<IntrinsicReference>(intrinsicTable.size());
for (final var intrinsicId : intrinsicTable.identifiers()) {
@ -183,6 +275,7 @@ public class IRBackend {
.entryPointCallableName(resolveEntryPointCallableName())
.functions(ReadOnlyList.wrap(functions))
.executableFunctions(ReadOnlyList.wrap(executableFunctions))
.modulePool(emitModulePool())
.callableSignatures(emitCallableSignatures())
.intrinsicPool(emitIntrinsicPool())
.reservedMetadata(new IRReservedMetadata(
@ -206,6 +299,7 @@ public class IRBackend {
sb.append("IRBackend{entrypoint=").append(entryPointCallableName)
.append(", functions=").append(functions.size())
.append(", executableFunctions=").append(executableFunctions.size())
.append(", modulePool=").append(modulePool.size())
.append(", callableSignatures=").append(callableSignatures.size())
.append(", intrinsicPool=").append(intrinsicPool.size())
.append(", hostBindings=").append(reservedMetadata.hostMethodBindings().size())

View File

@ -4,12 +4,14 @@ import p.studio.compiler.source.Span;
import p.studio.compiler.source.identifiers.CallableId;
import p.studio.compiler.source.identifiers.FileId;
import p.studio.compiler.source.identifiers.IntrinsicId;
import p.studio.compiler.source.identifiers.ModuleId;
import p.studio.utilities.structures.ReadOnlyList;
import java.util.Objects;
public record IRBackendExecutableFunction(
FileId fileId,
ModuleId moduleId,
String moduleKey,
String callableName,
CallableId callableId,
@ -22,8 +24,41 @@ public record IRBackendExecutableFunction(
ReadOnlyList<Instruction> instructions,
Span span) {
public IRBackendExecutableFunction(
final FileId fileId,
final String moduleKey,
final String callableName,
final CallableId callableId,
final int sourceStart,
final int sourceEnd,
final int paramSlots,
final int localSlots,
final int returnSlots,
final int maxStackSlots,
final ReadOnlyList<Instruction> instructions,
final Span span) {
this(
fileId,
ModuleId.none(),
moduleKey,
callableName,
callableId,
sourceStart,
sourceEnd,
paramSlots,
localSlots,
returnSlots,
maxStackSlots,
instructions,
span);
}
public IRBackendExecutableFunction {
fileId = Objects.requireNonNull(fileId, "fileId");
moduleId = moduleId == null ? ModuleId.none() : moduleId;
if (!moduleId.isNone()) {
moduleId.getIndex();
}
moduleKey = moduleKey == null ? "" : moduleKey;
callableName = Objects.requireNonNull(callableName, "callableName");
if (callableName.isBlank()) {
@ -52,6 +87,7 @@ public record IRBackendExecutableFunction(
public record Instruction(
InstructionKind kind,
ModuleId calleeModuleId,
String calleeModuleKey,
String calleeCallableName,
CallableId calleeCallableId,
@ -64,13 +100,78 @@ public record IRBackendExecutableFunction(
Span span) {
public Instruction(
final InstructionKind kind,
final ModuleId calleeModuleId,
final String calleeModuleKey,
final String calleeCallableName,
final CallableId calleeCallableId,
final HostCallMetadata hostCall,
final IntrinsicCallMetadata intrinsicCall,
final Span span) {
this(kind, calleeModuleKey, calleeCallableName, calleeCallableId, hostCall, intrinsicCall, null, null, null, null, span);
this(kind, calleeModuleId, calleeModuleKey, calleeCallableName, calleeCallableId, hostCall, intrinsicCall, null, null, null, null, span);
}
public Instruction(
final InstructionKind kind,
final ModuleId calleeModuleId,
final String calleeModuleKey,
final String calleeCallableName,
final CallableId calleeCallableId,
final HostCallMetadata hostCall,
final IntrinsicCallMetadata intrinsicCall,
final Integer expectedArgSlots,
final Integer expectedRetSlots,
final Span span) {
this(kind, calleeModuleId, calleeModuleKey, calleeCallableName, calleeCallableId, hostCall, intrinsicCall, null, null, expectedArgSlots, expectedRetSlots, span);
}
public Instruction(
final InstructionKind kind,
final ModuleId calleeModuleId,
final String calleeModuleKey,
final String calleeCallableName,
final HostCallMetadata hostCall,
final IntrinsicCallMetadata intrinsicCall,
final Span span) {
this(kind, calleeModuleId, calleeModuleKey, calleeCallableName, null, hostCall, intrinsicCall, null, null, null, null, span);
}
public Instruction(
final InstructionKind kind,
final ModuleId calleeModuleId,
final String calleeModuleKey,
final String calleeCallableName,
final HostCallMetadata hostCall,
final IntrinsicCallMetadata intrinsicCall,
final Integer expectedArgSlots,
final Integer expectedRetSlots,
final Span span) {
this(kind, calleeModuleId, calleeModuleKey, calleeCallableName, null, hostCall, intrinsicCall, null, null, expectedArgSlots, expectedRetSlots, span);
}
public Instruction(
final InstructionKind kind,
final String calleeModuleKey,
final String calleeCallableName,
final CallableId calleeCallableId,
final HostCallMetadata hostCall,
final IntrinsicCallMetadata intrinsicCall,
final String label,
final String targetLabel,
final Integer expectedArgSlots,
final Integer expectedRetSlots,
final Span span) {
this(kind, ModuleId.none(), calleeModuleKey, calleeCallableName, calleeCallableId, hostCall, intrinsicCall, label, targetLabel, expectedArgSlots, expectedRetSlots, span);
}
public Instruction(
final InstructionKind kind,
final String calleeModuleKey,
final String calleeCallableName,
final CallableId calleeCallableId,
final HostCallMetadata hostCall,
final IntrinsicCallMetadata intrinsicCall,
final Span span) {
this(kind, ModuleId.none(), calleeModuleKey, calleeCallableName, calleeCallableId, hostCall, intrinsicCall, null, null, null, null, span);
}
public Instruction(
@ -83,7 +184,7 @@ public record IRBackendExecutableFunction(
final Integer expectedArgSlots,
final Integer expectedRetSlots,
final Span span) {
this(kind, calleeModuleKey, calleeCallableName, calleeCallableId, hostCall, intrinsicCall, null, null, expectedArgSlots, expectedRetSlots, span);
this(kind, ModuleId.none(), calleeModuleKey, calleeCallableName, calleeCallableId, hostCall, intrinsicCall, null, null, expectedArgSlots, expectedRetSlots, span);
}
public Instruction(
@ -93,7 +194,7 @@ public record IRBackendExecutableFunction(
final HostCallMetadata hostCall,
final IntrinsicCallMetadata intrinsicCall,
final Span span) {
this(kind, calleeModuleKey, calleeCallableName, null, hostCall, intrinsicCall, null, null, null, null, span);
this(kind, ModuleId.none(), calleeModuleKey, calleeCallableName, null, hostCall, intrinsicCall, null, null, null, null, span);
}
public Instruction(
@ -105,12 +206,16 @@ public record IRBackendExecutableFunction(
final Integer expectedArgSlots,
final Integer expectedRetSlots,
final Span span) {
this(kind, calleeModuleKey, calleeCallableName, null, hostCall, intrinsicCall, null, null, expectedArgSlots, expectedRetSlots, span);
this(kind, ModuleId.none(), calleeModuleKey, calleeCallableName, null, hostCall, intrinsicCall, null, null, expectedArgSlots, expectedRetSlots, span);
}
public Instruction {
Objects.requireNonNull(kind, "kind");
span = span == null ? Span.none() : span;
calleeModuleId = calleeModuleId == null ? ModuleId.none() : calleeModuleId;
if (!calleeModuleId.isNone()) {
calleeModuleId.getIndex();
}
calleeModuleKey = calleeModuleKey == null ? "" : calleeModuleKey;
calleeCallableName = calleeCallableName == null ? "" : calleeCallableName;
label = label == null ? "" : label;

View File

@ -3,6 +3,7 @@ package p.studio.compiler.models;
import p.studio.compiler.source.identifiers.FileId;
import p.studio.compiler.source.tables.CallableSignatureRef;
import p.studio.compiler.source.tables.IntrinsicReference;
import p.studio.compiler.source.tables.ModuleReference;
import p.studio.utilities.structures.ReadOnlyList;
import java.util.Objects;
@ -12,6 +13,7 @@ public record IRBackendFile(
ReadOnlyList<IRFunction> functions,
ReadOnlyList<IRBackendExecutableFunction> executableFunctions,
IRReservedMetadata reservedMetadata,
ReadOnlyList<ModuleReference> modulePool,
ReadOnlyList<CallableSignatureRef> callableSignatures,
ReadOnlyList<IntrinsicReference> intrinsicPool) {
public IRBackendFile {
@ -19,6 +21,7 @@ public record IRBackendFile(
functions = functions == null ? ReadOnlyList.empty() : functions;
executableFunctions = executableFunctions == null ? ReadOnlyList.empty() : executableFunctions;
reservedMetadata = reservedMetadata == null ? IRReservedMetadata.empty() : reservedMetadata;
modulePool = modulePool == null ? ReadOnlyList.empty() : modulePool;
callableSignatures = callableSignatures == null ? ReadOnlyList.empty() : callableSignatures;
intrinsicPool = intrinsicPool == null ? ReadOnlyList.empty() : intrinsicPool;
}
@ -26,17 +29,55 @@ public record IRBackendFile(
public IRBackendFile(
final FileId fileId,
final ReadOnlyList<IRFunction> functions) {
this(fileId, functions, ReadOnlyList.empty(), IRReservedMetadata.empty(), ReadOnlyList.empty(), ReadOnlyList.empty());
this(
fileId,
functions,
ReadOnlyList.empty(),
IRReservedMetadata.empty(),
ReadOnlyList.empty(),
ReadOnlyList.empty(),
ReadOnlyList.empty());
}
public static IRBackendFile empty(final FileId fileId) {
return new IRBackendFile(fileId, ReadOnlyList.empty(), ReadOnlyList.empty(), IRReservedMetadata.empty(), ReadOnlyList.empty(), ReadOnlyList.empty());
return new IRBackendFile(
fileId,
ReadOnlyList.empty(),
ReadOnlyList.empty(),
IRReservedMetadata.empty(),
ReadOnlyList.empty(),
ReadOnlyList.empty(),
ReadOnlyList.empty());
}
public IRBackendFile(
final FileId fileId,
final ReadOnlyList<IRFunction> functions,
final IRReservedMetadata reservedMetadata) {
this(fileId, functions, ReadOnlyList.empty(), reservedMetadata, ReadOnlyList.empty(), ReadOnlyList.empty());
this(
fileId,
functions,
ReadOnlyList.empty(),
reservedMetadata,
ReadOnlyList.empty(),
ReadOnlyList.empty(),
ReadOnlyList.empty());
}
public IRBackendFile(
final FileId fileId,
final ReadOnlyList<IRFunction> functions,
final ReadOnlyList<IRBackendExecutableFunction> executableFunctions,
final IRReservedMetadata reservedMetadata,
final ReadOnlyList<CallableSignatureRef> callableSignatures,
final ReadOnlyList<IntrinsicReference> intrinsicPool) {
this(
fileId,
functions,
executableFunctions,
reservedMetadata,
ReadOnlyList.empty(),
callableSignatures,
intrinsicPool);
}
}

View File

@ -5,8 +5,10 @@ import p.studio.compiler.source.Span;
import p.studio.compiler.source.identifiers.CallableId;
import p.studio.compiler.source.identifiers.FileId;
import p.studio.compiler.source.identifiers.IntrinsicId;
import p.studio.compiler.source.identifiers.ModuleId;
import p.studio.compiler.source.tables.CallableSignatureRef;
import p.studio.compiler.source.tables.IntrinsicReference;
import p.studio.compiler.source.tables.ModuleReference;
import p.studio.utilities.structures.ReadOnlyList;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -300,4 +302,76 @@ class IRBackendExecutableContractTest {
assertEquals(21, emittedInstructionSpan.getStart());
assertEquals(34, emittedInstructionSpan.getEnd());
}
@Test
void aggregatorMustReindexModulesIntoGlobalDensePool() {
final var fileA = new IRBackendFile(
new FileId(1),
ReadOnlyList.empty(),
ReadOnlyList.from(new IRBackendExecutableFunction(
new FileId(1),
new ModuleId(0),
"app:mod/a",
"entry",
new CallableId(0),
0,
5,
0,
0,
0,
1,
ReadOnlyList.from(new IRBackendExecutableFunction.Instruction(
IRBackendExecutableFunction.InstructionKind.RET,
"",
"",
null,
null,
Span.none())),
Span.none())),
IRReservedMetadata.empty(),
ReadOnlyList.from(new ModuleReference("app", ReadOnlyList.from("mod", "a"))),
ReadOnlyList.from(new CallableSignatureRef(new ModuleId(0), "app:mod/a", "entry", 0, "() -> unit")),
ReadOnlyList.empty());
final var fileB = new IRBackendFile(
new FileId(2),
ReadOnlyList.empty(),
ReadOnlyList.from(new IRBackendExecutableFunction(
new FileId(2),
new ModuleId(0),
"app:mod/b",
"aux",
new CallableId(0),
6,
10,
0,
0,
0,
1,
ReadOnlyList.from(new IRBackendExecutableFunction.Instruction(
IRBackendExecutableFunction.InstructionKind.RET,
"",
"",
null,
null,
Span.none())),
Span.none())),
IRReservedMetadata.empty(),
ReadOnlyList.from(new ModuleReference("app", ReadOnlyList.from("mod", "b"))),
ReadOnlyList.from(new CallableSignatureRef(new ModuleId(0), "app:mod/b", "aux", 0, "() -> unit")),
ReadOnlyList.empty());
final var aggregator = IRBackend.aggregator();
aggregator.merge(fileA);
aggregator.merge(fileB);
final var backend = aggregator.emit();
assertEquals(2, backend.getModulePool().size());
assertEquals("mod", backend.getModulePool().get(0).pathSegments().get(0));
assertEquals("a", backend.getModulePool().get(0).pathSegments().get(1));
assertEquals("b", backend.getModulePool().get(1).pathSegments().get(1));
assertEquals(0, backend.getExecutableFunctions().get(0).moduleId().getIndex());
assertEquals(1, backend.getExecutableFunctions().get(1).moduleId().getIndex());
assertEquals(0, backend.getCallableSignatures().get(0).moduleId().getIndex());
assertEquals(1, backend.getCallableSignatures().get(1).moduleId().getIndex());
}
}