implements PR-19.8 published wrapper entrypoint lowering
This commit is contained in:
parent
c61b9db786
commit
58c5ab26d7
@ -11,7 +11,6 @@ public class PBSDefinitions {
|
|||||||
.languageId("pbs")
|
.languageId("pbs")
|
||||||
.allowedExtensions(ReadOnlySet.from("pbs", "barrel"))
|
.allowedExtensions(ReadOnlySet.from("pbs", "barrel"))
|
||||||
.sourceRoots(ReadOnlySet.from("src"))
|
.sourceRoots(ReadOnlySet.from("src"))
|
||||||
.entryPointCallableName("frame")
|
|
||||||
.stdlibVersions(List.of(FrontendSpec.Stdlib.asDefault(1)))
|
.stdlibVersions(List.of(FrontendSpec.Stdlib.asDefault(1)))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package p.studio.compiler.pbs;
|
package p.studio.compiler.pbs;
|
||||||
|
|
||||||
import p.studio.compiler.messages.HostAdmissionContext;
|
import p.studio.compiler.messages.HostAdmissionContext;
|
||||||
|
import p.studio.compiler.models.IRBackendExecutableFunction;
|
||||||
import p.studio.compiler.models.IRBackendFile;
|
import p.studio.compiler.models.IRBackendFile;
|
||||||
import p.studio.compiler.models.IRFunction;
|
import p.studio.compiler.models.IRFunction;
|
||||||
import p.studio.compiler.models.IRGlobal;
|
import p.studio.compiler.models.IRGlobal;
|
||||||
@ -16,9 +17,12 @@ import p.studio.compiler.pbs.parser.PbsParser;
|
|||||||
import p.studio.compiler.pbs.semantics.PbsDeclarationSemanticsValidator;
|
import p.studio.compiler.pbs.semantics.PbsDeclarationSemanticsValidator;
|
||||||
import p.studio.compiler.pbs.semantics.PbsFlowSemanticsValidator;
|
import p.studio.compiler.pbs.semantics.PbsFlowSemanticsValidator;
|
||||||
import p.studio.compiler.pbs.semantics.PbsLifecycleSemanticsValidator;
|
import p.studio.compiler.pbs.semantics.PbsLifecycleSemanticsValidator;
|
||||||
|
import p.studio.compiler.source.Span;
|
||||||
import p.studio.compiler.source.diagnostics.DiagnosticSink;
|
import p.studio.compiler.source.diagnostics.DiagnosticSink;
|
||||||
|
import p.studio.compiler.source.identifiers.CallableId;
|
||||||
import p.studio.compiler.source.identifiers.FileId;
|
import p.studio.compiler.source.identifiers.FileId;
|
||||||
import p.studio.compiler.source.identifiers.ModuleId;
|
import p.studio.compiler.source.identifiers.ModuleId;
|
||||||
|
import p.studio.compiler.source.tables.CallableSignatureRef;
|
||||||
import p.studio.compiler.source.tables.ModuleReference;
|
import p.studio.compiler.source.tables.ModuleReference;
|
||||||
import p.studio.compiler.source.tables.NameTable;
|
import p.studio.compiler.source.tables.NameTable;
|
||||||
import p.studio.utilities.structures.ReadOnlyList;
|
import p.studio.utilities.structures.ReadOnlyList;
|
||||||
@ -172,15 +176,27 @@ public final class PbsFrontendCompiler {
|
|||||||
if (diagnostics.errorCount() > loweringErrorBaseline) {
|
if (diagnostics.errorCount() > loweringErrorBaseline) {
|
||||||
return IRBackendFile.empty(fileId);
|
return IRBackendFile.empty(fileId);
|
||||||
}
|
}
|
||||||
|
final var lifecycleArtifacts = scanLifecycleArtifacts(ast);
|
||||||
|
final var executableFunctions = new ArrayList<>(executableLowering.executableFunctions().asList());
|
||||||
|
final var callableSignatures = new ArrayList<>(executableLowering.callableSignatures().asList());
|
||||||
|
final var fileInitExecutable = lowerFileInitExecutable(
|
||||||
|
fileId,
|
||||||
|
effectiveModuleId,
|
||||||
|
lifecycleArtifacts,
|
||||||
|
executableFunctions,
|
||||||
|
callableSignatures);
|
||||||
|
if (fileInitExecutable != null) {
|
||||||
|
executableFunctions.add(fileInitExecutable);
|
||||||
|
}
|
||||||
return new IRBackendFile(
|
return new IRBackendFile(
|
||||||
fileId,
|
fileId,
|
||||||
functions,
|
functions,
|
||||||
syntheticFunctions,
|
syntheticFunctions,
|
||||||
globals,
|
globals,
|
||||||
executableLowering.executableFunctions(),
|
ReadOnlyList.wrap(executableFunctions),
|
||||||
reservedMetadata,
|
reservedMetadata,
|
||||||
effectiveModulePool,
|
effectiveModulePool,
|
||||||
executableLowering.callableSignatures(),
|
ReadOnlyList.wrap(callableSignatures),
|
||||||
executableLowering.intrinsicPool());
|
executableLowering.intrinsicPool());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,6 +262,125 @@ public final class PbsFrontendCompiler {
|
|||||||
final ModuleId moduleId,
|
final ModuleId moduleId,
|
||||||
final PbsAst.File ast) {
|
final PbsAst.File ast) {
|
||||||
final var syntheticFunctions = new ArrayList<IRSyntheticFunction>();
|
final var syntheticFunctions = new ArrayList<IRSyntheticFunction>();
|
||||||
|
final var lifecycleArtifacts = scanLifecycleArtifacts(ast);
|
||||||
|
final var initDecl = lifecycleArtifacts.initDecl();
|
||||||
|
final var frameDecl = lifecycleArtifacts.frameDecl();
|
||||||
|
final var firstGlobalDecl = lifecycleArtifacts.firstGlobalDecl();
|
||||||
|
|
||||||
|
final var fileInitAnchorSpan = initDecl != null ? initDecl.span() : firstGlobalDecl == null ? null : firstGlobalDecl.span();
|
||||||
|
final var fileInitAnchorName = initDecl != null ? initDecl.name() : firstGlobalDecl == null ? "" : firstGlobalDecl.name();
|
||||||
|
if (fileInitAnchorSpan != null) {
|
||||||
|
syntheticFunctions.add(new IRSyntheticFunction(
|
||||||
|
moduleId,
|
||||||
|
fileInitCallableName(fileId),
|
||||||
|
IRSyntheticCallableKind.FILE_INIT_FRAGMENT,
|
||||||
|
new IRSyntheticOrigin(fileId, fileInitAnchorName, fileInitAnchorSpan)));
|
||||||
|
syntheticFunctions.add(new IRSyntheticFunction(
|
||||||
|
moduleId,
|
||||||
|
moduleInitCallableName(moduleId),
|
||||||
|
IRSyntheticCallableKind.MODULE_INIT,
|
||||||
|
new IRSyntheticOrigin(fileId, fileInitAnchorName, fileInitAnchorSpan)));
|
||||||
|
}
|
||||||
|
if (initDecl != null && frameDecl != null) {
|
||||||
|
syntheticFunctions.add(new IRSyntheticFunction(
|
||||||
|
moduleId,
|
||||||
|
projectInitCallableName(moduleId),
|
||||||
|
IRSyntheticCallableKind.PROJECT_INIT,
|
||||||
|
new IRSyntheticOrigin(fileId, initDecl.name(), initDecl.span())));
|
||||||
|
}
|
||||||
|
if (frameDecl != null) {
|
||||||
|
syntheticFunctions.add(new IRSyntheticFunction(
|
||||||
|
moduleId,
|
||||||
|
frameWrapperCallableName(moduleId),
|
||||||
|
IRSyntheticCallableKind.PUBLISHED_FRAME_WRAPPER,
|
||||||
|
new IRSyntheticOrigin(fileId, frameDecl.name(), frameDecl.span())));
|
||||||
|
}
|
||||||
|
return ReadOnlyList.wrap(syntheticFunctions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IRBackendExecutableFunction lowerFileInitExecutable(
|
||||||
|
final FileId fileId,
|
||||||
|
final ModuleId moduleId,
|
||||||
|
final PbsLifecycleArtifacts lifecycleArtifacts,
|
||||||
|
final ArrayList<IRBackendExecutableFunction> executableFunctions,
|
||||||
|
final ArrayList<CallableSignatureRef> callableSignatures) {
|
||||||
|
if (lifecycleArtifacts.firstGlobalDecl() == null
|
||||||
|
&& lifecycleArtifacts.initDecl() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final var syntheticCallableName = fileInitCallableName(fileId);
|
||||||
|
final var syntheticCallableId = new CallableId(callableSignatures.size());
|
||||||
|
callableSignatures.add(new CallableSignatureRef(moduleId, syntheticCallableName, 0, "() -> unit"));
|
||||||
|
|
||||||
|
final var instructions = new ArrayList<IRBackendExecutableFunction.Instruction>();
|
||||||
|
final var moduleInitSource = lifecycleArtifacts.initDecl();
|
||||||
|
if (moduleInitSource != null && moduleInitSource != lifecycleArtifacts.frameDecl()) {
|
||||||
|
final var initExecutable = resolveLifecycleExecutable(executableFunctions, moduleId, moduleInitSource.name());
|
||||||
|
if (initExecutable != null) {
|
||||||
|
instructions.add(new IRBackendExecutableFunction.Instruction(
|
||||||
|
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
|
||||||
|
moduleId,
|
||||||
|
initExecutable.callableName(),
|
||||||
|
initExecutable.callableId(),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
moduleInitSource.span()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
instructions.add(new IRBackendExecutableFunction.Instruction(
|
||||||
|
IRBackendExecutableFunction.InstructionKind.RET,
|
||||||
|
"",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
lifecycleArtifacts.anchorSpan()));
|
||||||
|
final var span = lifecycleArtifacts.anchorSpan();
|
||||||
|
return new IRBackendExecutableFunction(
|
||||||
|
fileId,
|
||||||
|
moduleId,
|
||||||
|
syntheticCallableName,
|
||||||
|
syntheticCallableId,
|
||||||
|
safeToInt(span.getStart()),
|
||||||
|
safeToInt(span.getEnd()),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
ReadOnlyList.wrap(instructions),
|
||||||
|
span);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IRBackendExecutableFunction resolveLifecycleExecutable(
|
||||||
|
final ArrayList<IRBackendExecutableFunction> executableFunctions,
|
||||||
|
final ModuleId moduleId,
|
||||||
|
final String callableName) {
|
||||||
|
for (final var executable : executableFunctions) {
|
||||||
|
if (!callableName.equals(executable.callableName())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!moduleIdsMatch(moduleId, executable.moduleId())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return executable;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean moduleIdsMatch(
|
||||||
|
final ModuleId left,
|
||||||
|
final ModuleId right) {
|
||||||
|
final var normalizedLeft = left == null ? ModuleId.none() : left;
|
||||||
|
final var normalizedRight = right == null ? ModuleId.none() : right;
|
||||||
|
if (normalizedLeft.isNone() && normalizedRight.isNone()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return !normalizedLeft.isNone()
|
||||||
|
&& !normalizedRight.isNone()
|
||||||
|
&& normalizedLeft.getIndex() == normalizedRight.getIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
private PbsLifecycleArtifacts scanLifecycleArtifacts(final PbsAst.File ast) {
|
||||||
PbsAst.FunctionDecl initDecl = null;
|
PbsAst.FunctionDecl initDecl = null;
|
||||||
PbsAst.FunctionDecl frameDecl = null;
|
PbsAst.FunctionDecl frameDecl = null;
|
||||||
PbsAst.GlobalDecl firstGlobalDecl = null;
|
PbsAst.GlobalDecl firstGlobalDecl = null;
|
||||||
@ -262,36 +397,56 @@ public final class PbsFrontendCompiler {
|
|||||||
firstGlobalDecl = globalDecl;
|
firstGlobalDecl = globalDecl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return new PbsLifecycleArtifacts(initDecl, frameDecl, firstGlobalDecl);
|
||||||
final var fileInitAnchorSpan = initDecl != null ? initDecl.span() : firstGlobalDecl == null ? null : firstGlobalDecl.span();
|
|
||||||
final var fileInitAnchorName = initDecl != null ? initDecl.name() : firstGlobalDecl == null ? "" : firstGlobalDecl.name();
|
|
||||||
if (fileInitAnchorSpan != null) {
|
|
||||||
syntheticFunctions.add(new IRSyntheticFunction(
|
|
||||||
moduleId,
|
|
||||||
"__pbs.file_init",
|
|
||||||
IRSyntheticCallableKind.FILE_INIT_FRAGMENT,
|
|
||||||
new IRSyntheticOrigin(fileId, fileInitAnchorName, fileInitAnchorSpan)));
|
|
||||||
syntheticFunctions.add(new IRSyntheticFunction(
|
|
||||||
moduleId,
|
|
||||||
"__pbs.module_init",
|
|
||||||
IRSyntheticCallableKind.MODULE_INIT,
|
|
||||||
new IRSyntheticOrigin(fileId, fileInitAnchorName, fileInitAnchorSpan)));
|
|
||||||
}
|
}
|
||||||
if (initDecl != null && frameDecl != null) {
|
|
||||||
syntheticFunctions.add(new IRSyntheticFunction(
|
private static String fileInitCallableName(final FileId fileId) {
|
||||||
moduleId,
|
return "__pbs.file_init$f" + fileId.getId();
|
||||||
"__pbs.project_init",
|
}
|
||||||
IRSyntheticCallableKind.PROJECT_INIT,
|
|
||||||
new IRSyntheticOrigin(fileId, initDecl.name(), initDecl.span())));
|
public static String moduleInitCallableName(final ModuleId moduleId) {
|
||||||
|
return "__pbs.module_init$m" + normalizedModuleIndex(moduleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String projectInitCallableName(final ModuleId moduleId) {
|
||||||
|
return "__pbs.project_init$m" + normalizedModuleIndex(moduleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String frameWrapperCallableName(final ModuleId moduleId) {
|
||||||
|
return "__pbs.frame_wrapper$m" + normalizedModuleIndex(moduleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int normalizedModuleIndex(final ModuleId moduleId) {
|
||||||
|
final var normalized = moduleId == null ? ModuleId.none() : moduleId;
|
||||||
|
return normalized.isNone() ? -1 : normalized.getIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int safeToInt(final long value) {
|
||||||
|
if (value > Integer.MAX_VALUE) {
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
if (value < Integer.MIN_VALUE) {
|
||||||
|
return Integer.MIN_VALUE;
|
||||||
|
}
|
||||||
|
return (int) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private record PbsLifecycleArtifacts(
|
||||||
|
PbsAst.FunctionDecl initDecl,
|
||||||
|
PbsAst.FunctionDecl frameDecl,
|
||||||
|
PbsAst.GlobalDecl firstGlobalDecl) {
|
||||||
|
private Span anchorSpan() {
|
||||||
|
if (initDecl != null) {
|
||||||
|
return initDecl.span();
|
||||||
}
|
}
|
||||||
if (frameDecl != null) {
|
if (frameDecl != null) {
|
||||||
syntheticFunctions.add(new IRSyntheticFunction(
|
return frameDecl.span();
|
||||||
moduleId,
|
}
|
||||||
"__pbs.frame_wrapper",
|
if (firstGlobalDecl != null) {
|
||||||
IRSyntheticCallableKind.PUBLISHED_FRAME_WRAPPER,
|
return firstGlobalDecl.span();
|
||||||
new IRSyntheticOrigin(fileId, frameDecl.name(), frameDecl.span())));
|
}
|
||||||
|
return Span.none();
|
||||||
}
|
}
|
||||||
return ReadOnlyList.wrap(syntheticFunctions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IRReservedMetadata mergeReservedMetadata(
|
private IRReservedMetadata mergeReservedMetadata(
|
||||||
|
|||||||
@ -1,18 +1,24 @@
|
|||||||
package p.studio.compiler.services;
|
package p.studio.compiler.services;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import p.studio.compiler.PBSDefinitions;
|
|
||||||
import p.studio.compiler.messages.BuildingIssueSink;
|
import p.studio.compiler.messages.BuildingIssueSink;
|
||||||
import p.studio.compiler.messages.FrontendPhaseContext;
|
import p.studio.compiler.messages.FrontendPhaseContext;
|
||||||
import p.studio.compiler.models.IRBackend;
|
import p.studio.compiler.models.IRBackend;
|
||||||
|
import p.studio.compiler.models.IRBackendExecutableFunction;
|
||||||
|
import p.studio.compiler.models.IRSyntheticCallableKind;
|
||||||
import p.studio.compiler.pbs.PbsFrontendCompiler;
|
import p.studio.compiler.pbs.PbsFrontendCompiler;
|
||||||
import p.studio.compiler.pbs.PbsReservedMetadataExtractor;
|
import p.studio.compiler.pbs.PbsReservedMetadataExtractor;
|
||||||
import p.studio.compiler.pbs.stdlib.InterfaceModuleLoader;
|
import p.studio.compiler.pbs.stdlib.InterfaceModuleLoader;
|
||||||
import p.studio.compiler.pbs.stdlib.ResourceStdlibEnvironmentResolver;
|
import p.studio.compiler.pbs.stdlib.ResourceStdlibEnvironmentResolver;
|
||||||
import p.studio.compiler.pbs.stdlib.StdlibEnvironmentResolver;
|
import p.studio.compiler.pbs.stdlib.StdlibEnvironmentResolver;
|
||||||
|
import p.studio.compiler.source.Span;
|
||||||
import p.studio.compiler.source.diagnostics.DiagnosticSink;
|
import p.studio.compiler.source.diagnostics.DiagnosticSink;
|
||||||
|
import p.studio.compiler.source.identifiers.CallableId;
|
||||||
|
import p.studio.compiler.source.identifiers.FileId;
|
||||||
import p.studio.compiler.source.identifiers.ModuleId;
|
import p.studio.compiler.source.identifiers.ModuleId;
|
||||||
|
import p.studio.compiler.source.tables.CallableSignatureRef;
|
||||||
import p.studio.utilities.logs.LogAggregator;
|
import p.studio.utilities.logs.LogAggregator;
|
||||||
|
import p.studio.utilities.structures.ReadOnlyList;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@ -100,29 +106,282 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
|
|||||||
final Map<ModuleId, Set<ModuleId>> moduleDependencyGraph) {
|
final Map<ModuleId, Set<ModuleId>> moduleDependencyGraph) {
|
||||||
final var irBackendAggregator = IRBackend.aggregator();
|
final var irBackendAggregator = IRBackend.aggregator();
|
||||||
final var blockedModuleIds = blockedModulesByDependency(failedModuleIds, moduleDependencyGraph);
|
final var blockedModuleIds = blockedModulesByDependency(failedModuleIds, moduleDependencyGraph);
|
||||||
final var entryPointCallableName = PBSDefinitions.PBS.getEntryPointCallableName();
|
|
||||||
final var entryPointModuleCandidates = new LinkedHashSet<ModuleId>();
|
|
||||||
|
|
||||||
for (final var compiledSource : compiledSourceFiles) {
|
for (final var compiledSource : compiledSourceFiles) {
|
||||||
if (blockedModuleIds.contains(compiledSource.moduleId())) {
|
if (blockedModuleIds.contains(compiledSource.moduleId())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
irBackendAggregator.merge(compiledSource.irBackendFile());
|
irBackendAggregator.merge(compiledSource.irBackendFile());
|
||||||
for (final var executable : compiledSource.irBackendFile().executableFunctions()) {
|
}
|
||||||
if (!entryPointCallableName.equals(executable.callableName())) {
|
return synthesizePublishedLifecycle(irBackendAggregator.emit());
|
||||||
|
}
|
||||||
|
|
||||||
|
private IRBackend synthesizePublishedLifecycle(final IRBackend baseBackend) {
|
||||||
|
final var callableSignatures = new ArrayList<>(baseBackend.getCallableSignatures().asList());
|
||||||
|
final var executableFunctions = new ArrayList<>(baseBackend.getExecutableFunctions().asList());
|
||||||
|
final var fileInitFunctionsByModule = new LinkedHashMap<ModuleId, List<IRBackendExecutableFunction>>();
|
||||||
|
final var projectInitAnchorsByModule = new LinkedHashMap<ModuleId, String>();
|
||||||
|
final var wrapperNamesByModule = new LinkedHashMap<ModuleId, String>();
|
||||||
|
final var frameAnchorsByModule = new LinkedHashMap<ModuleId, String>();
|
||||||
|
|
||||||
|
for (final var executable : executableFunctions) {
|
||||||
|
if (!executable.callableName().startsWith("__pbs.file_init$")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (executable.moduleId() != null && !executable.moduleId().isNone()) {
|
fileInitFunctionsByModule
|
||||||
entryPointModuleCandidates.add(executable.moduleId());
|
.computeIfAbsent(normalizeModuleId(executable.moduleId()), ignored -> new ArrayList<>())
|
||||||
|
.add(executable);
|
||||||
|
}
|
||||||
|
for (final var syntheticFunction : baseBackend.getSyntheticFunctions()) {
|
||||||
|
final var moduleId = normalizeModuleId(syntheticFunction.moduleId());
|
||||||
|
switch (syntheticFunction.kind()) {
|
||||||
|
case PROJECT_INIT -> projectInitAnchorsByModule.put(moduleId, syntheticFunction.origin().anchorCallableName());
|
||||||
|
case PUBLISHED_FRAME_WRAPPER -> {
|
||||||
|
wrapperNamesByModule.put(moduleId, syntheticFunction.callableName());
|
||||||
|
frameAnchorsByModule.put(moduleId, syntheticFunction.origin().anchorCallableName());
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
irBackendAggregator.entryPointCallableName(entryPointCallableName);
|
final ModuleId entryPointModuleId;
|
||||||
if (entryPointModuleCandidates.size() == 1) {
|
final String entryPointCallableName;
|
||||||
irBackendAggregator.entryPointModuleId(entryPointModuleCandidates.iterator().next());
|
if (wrapperNamesByModule.size() == 1) {
|
||||||
|
entryPointModuleId = wrapperNamesByModule.keySet().iterator().next();
|
||||||
|
entryPointCallableName = wrapperNamesByModule.get(entryPointModuleId);
|
||||||
|
} else {
|
||||||
|
entryPointModuleId = baseBackend.getEntryPointModuleId();
|
||||||
|
entryPointCallableName = baseBackend.getEntryPointCallableName();
|
||||||
}
|
}
|
||||||
return irBackendAggregator.emit();
|
|
||||||
|
final var sortedModuleIds = new ArrayList<ModuleId>();
|
||||||
|
sortedModuleIds.addAll(fileInitFunctionsByModule.keySet());
|
||||||
|
for (final var moduleId : wrapperNamesByModule.keySet()) {
|
||||||
|
if (!sortedModuleIds.contains(moduleId)) {
|
||||||
|
sortedModuleIds.add(moduleId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sortedModuleIds.sort(Comparator.comparing(moduleId -> moduleSortKey(baseBackend, moduleId)));
|
||||||
|
|
||||||
|
final var moduleInitCallableIds = new LinkedHashMap<ModuleId, CallableId>();
|
||||||
|
for (final var moduleId : sortedModuleIds) {
|
||||||
|
final var moduleInitName = PbsFrontendCompiler.moduleInitCallableName(moduleId);
|
||||||
|
final var fileInitFunctions = new ArrayList<>(fileInitFunctionsByModule.getOrDefault(moduleId, List.of()));
|
||||||
|
if (fileInitFunctions.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fileInitFunctions.sort(Comparator.comparingInt(fn -> fn.fileId().getId()));
|
||||||
|
|
||||||
|
final var callableId = new CallableId(callableSignatures.size());
|
||||||
|
callableSignatures.add(new CallableSignatureRef(moduleId, moduleInitName, 0, "() -> unit"));
|
||||||
|
moduleInitCallableIds.put(moduleId, callableId);
|
||||||
|
|
||||||
|
final var instructions = new ArrayList<IRBackendExecutableFunction.Instruction>();
|
||||||
|
var anchorSpan = fileInitFunctions.getFirst().span();
|
||||||
|
var anchorFileId = fileInitFunctions.getFirst().fileId();
|
||||||
|
for (final var fileInitFunction : fileInitFunctions) {
|
||||||
|
instructions.add(callInstruction(fileInitFunction, fileInitFunction.span()));
|
||||||
|
anchorSpan = fileInitFunction.span();
|
||||||
|
anchorFileId = fileInitFunction.fileId();
|
||||||
|
}
|
||||||
|
instructions.add(retInstruction(anchorSpan));
|
||||||
|
executableFunctions.add(new IRBackendExecutableFunction(
|
||||||
|
anchorFileId,
|
||||||
|
moduleId,
|
||||||
|
moduleInitName,
|
||||||
|
callableId,
|
||||||
|
safeToInt(anchorSpan.getStart()),
|
||||||
|
safeToInt(anchorSpan.getEnd()),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
ReadOnlyList.wrap(instructions),
|
||||||
|
anchorSpan));
|
||||||
|
}
|
||||||
|
|
||||||
|
CallableId projectInitCallableId = null;
|
||||||
|
if (!entryPointModuleId.isNone()) {
|
||||||
|
final var projectInitAnchor = projectInitAnchorsByModule.get(entryPointModuleId);
|
||||||
|
final var projectInitTarget = resolveExecutable(executableFunctions, entryPointModuleId, projectInitAnchor);
|
||||||
|
if (projectInitTarget != null) {
|
||||||
|
final var projectInitName = PbsFrontendCompiler.projectInitCallableName(entryPointModuleId);
|
||||||
|
projectInitCallableId = new CallableId(callableSignatures.size());
|
||||||
|
callableSignatures.add(new CallableSignatureRef(entryPointModuleId, projectInitName, 0, "() -> unit"));
|
||||||
|
executableFunctions.add(new IRBackendExecutableFunction(
|
||||||
|
projectInitTarget.fileId(),
|
||||||
|
entryPointModuleId,
|
||||||
|
projectInitName,
|
||||||
|
projectInitCallableId,
|
||||||
|
projectInitTarget.sourceStart(),
|
||||||
|
projectInitTarget.sourceEnd(),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
ReadOnlyList.from(
|
||||||
|
callInstruction(projectInitTarget, projectInitTarget.span()),
|
||||||
|
retInstruction(projectInitTarget.span())),
|
||||||
|
projectInitTarget.span()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final var frameExecutable = resolveExecutable(
|
||||||
|
executableFunctions,
|
||||||
|
entryPointModuleId,
|
||||||
|
frameAnchorsByModule.get(entryPointModuleId));
|
||||||
|
if (frameExecutable != null
|
||||||
|
&& entryPointCallableName != null
|
||||||
|
&& !entryPointCallableName.isBlank()
|
||||||
|
&& !entryPointModuleId.isNone()) {
|
||||||
|
final var wrapperCallableId = new CallableId(callableSignatures.size());
|
||||||
|
callableSignatures.add(new CallableSignatureRef(entryPointModuleId, entryPointCallableName, 0, "() -> unit"));
|
||||||
|
final var instructions = new ArrayList<IRBackendExecutableFunction.Instruction>();
|
||||||
|
for (final var moduleId : sortedModuleIds) {
|
||||||
|
final var moduleInitCallableId = moduleInitCallableIds.get(moduleId);
|
||||||
|
if (moduleInitCallableId == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
instructions.add(new IRBackendExecutableFunction.Instruction(
|
||||||
|
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
|
||||||
|
moduleId,
|
||||||
|
PbsFrontendCompiler.moduleInitCallableName(moduleId),
|
||||||
|
moduleInitCallableId,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
frameExecutable.span()));
|
||||||
|
}
|
||||||
|
if (projectInitCallableId != null) {
|
||||||
|
instructions.add(new IRBackendExecutableFunction.Instruction(
|
||||||
|
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
|
||||||
|
entryPointModuleId,
|
||||||
|
PbsFrontendCompiler.projectInitCallableName(entryPointModuleId),
|
||||||
|
projectInitCallableId,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
frameExecutable.span()));
|
||||||
|
}
|
||||||
|
instructions.add(callInstruction(frameExecutable, frameExecutable.span()));
|
||||||
|
instructions.add(retInstruction(frameExecutable.span()));
|
||||||
|
executableFunctions.add(new IRBackendExecutableFunction(
|
||||||
|
frameExecutable.fileId(),
|
||||||
|
entryPointModuleId,
|
||||||
|
entryPointCallableName,
|
||||||
|
wrapperCallableId,
|
||||||
|
frameExecutable.sourceStart(),
|
||||||
|
frameExecutable.sourceEnd(),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
ReadOnlyList.wrap(instructions),
|
||||||
|
frameExecutable.span()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return IRBackend.builder()
|
||||||
|
.entryPointCallableName(entryPointCallableName)
|
||||||
|
.entryPointModuleId(entryPointModuleId)
|
||||||
|
.functions(baseBackend.getFunctions())
|
||||||
|
.syntheticFunctions(baseBackend.getSyntheticFunctions())
|
||||||
|
.globals(baseBackend.getGlobals())
|
||||||
|
.executableFunctions(ReadOnlyList.wrap(executableFunctions))
|
||||||
|
.modulePool(baseBackend.getModulePool())
|
||||||
|
.callableSignatures(ReadOnlyList.wrap(callableSignatures))
|
||||||
|
.intrinsicPool(baseBackend.getIntrinsicPool())
|
||||||
|
.reservedMetadata(baseBackend.getReservedMetadata())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private IRBackendExecutableFunction resolveExecutable(
|
||||||
|
final ArrayList<IRBackendExecutableFunction> executableFunctions,
|
||||||
|
final ModuleId moduleId,
|
||||||
|
final String callableName) {
|
||||||
|
if (callableName == null || callableName.isBlank()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (final var executable : executableFunctions) {
|
||||||
|
if (!callableName.equals(executable.callableName())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!moduleIdsMatch(moduleId, executable.moduleId())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return executable;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IRBackendExecutableFunction.Instruction callInstruction(
|
||||||
|
final IRBackendExecutableFunction callee,
|
||||||
|
final Span span) {
|
||||||
|
return new IRBackendExecutableFunction.Instruction(
|
||||||
|
IRBackendExecutableFunction.InstructionKind.CALL_FUNC,
|
||||||
|
callee.moduleId(),
|
||||||
|
callee.callableName(),
|
||||||
|
callee.callableId(),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
callee.paramSlots(),
|
||||||
|
callee.returnSlots(),
|
||||||
|
span);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IRBackendExecutableFunction.Instruction retInstruction(final Span span) {
|
||||||
|
return new IRBackendExecutableFunction.Instruction(
|
||||||
|
IRBackendExecutableFunction.InstructionKind.RET,
|
||||||
|
"",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
span);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ModuleId normalizeModuleId(final ModuleId moduleId) {
|
||||||
|
return moduleId == null ? ModuleId.none() : moduleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean moduleIdsMatch(
|
||||||
|
final ModuleId left,
|
||||||
|
final ModuleId right) {
|
||||||
|
final var normalizedLeft = normalizeModuleId(left);
|
||||||
|
final var normalizedRight = normalizeModuleId(right);
|
||||||
|
if (normalizedLeft.isNone() && normalizedRight.isNone()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return !normalizedLeft.isNone()
|
||||||
|
&& !normalizedRight.isNone()
|
||||||
|
&& normalizedLeft.getIndex() == normalizedRight.getIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String moduleSortKey(
|
||||||
|
final IRBackend backend,
|
||||||
|
final ModuleId moduleId) {
|
||||||
|
if (moduleId != null && !moduleId.isNone()) {
|
||||||
|
final var index = moduleId.getIndex();
|
||||||
|
if (index >= 0 && index < backend.getModulePool().size()) {
|
||||||
|
final var moduleReference = backend.getModulePool().get(index);
|
||||||
|
final var joiner = new StringJoiner("/", moduleReference.project() + ":", "");
|
||||||
|
for (final var segment : moduleReference.pathSegments()) {
|
||||||
|
joiner.add(segment);
|
||||||
|
}
|
||||||
|
return joiner.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private int safeToInt(final long value) {
|
||||||
|
if (value > Integer.MAX_VALUE) {
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
if (value < Integer.MIN_VALUE) {
|
||||||
|
return Integer.MIN_VALUE;
|
||||||
|
}
|
||||||
|
return (int) value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<ModuleId> blockedModulesByDependency(
|
private Set<ModuleId> blockedModulesByDependency(
|
||||||
|
|||||||
@ -254,8 +254,69 @@ class PBSFrontendPhaseServiceTest {
|
|||||||
BuildingIssueSink.empty());
|
BuildingIssueSink.empty());
|
||||||
|
|
||||||
assertTrue(diagnostics.isEmpty());
|
assertTrue(diagnostics.isEmpty());
|
||||||
assertEquals("frame", irBackend.getEntryPointCallableName());
|
|
||||||
assertEquals(2, irBackend.getFunctions().size());
|
assertEquals(2, irBackend.getFunctions().size());
|
||||||
|
assertEquals(2, irBackend.getExecutableFunctions().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldPublishSyntheticFrameWrapperAsEntrypointWhenLifecycleMarkersExist() throws IOException {
|
||||||
|
final var projectRoot = tempDir.resolve("project-lifecycle-wrapper");
|
||||||
|
final var sourceRoot = projectRoot.resolve("src");
|
||||||
|
final var modulePath = sourceRoot.resolve("game");
|
||||||
|
Files.createDirectories(modulePath);
|
||||||
|
|
||||||
|
final var sourceFile = modulePath.resolve("source.pbs");
|
||||||
|
final var modBarrel = modulePath.resolve("mod.barrel");
|
||||||
|
Files.writeString(sourceFile, """
|
||||||
|
declare global SCORE: int = 0;
|
||||||
|
|
||||||
|
[Init]
|
||||||
|
fn boot() -> void { return; }
|
||||||
|
|
||||||
|
[Frame]
|
||||||
|
fn frame() -> void { return; }
|
||||||
|
""");
|
||||||
|
Files.writeString(modBarrel, """
|
||||||
|
pub fn boot() -> void;
|
||||||
|
pub fn frame() -> void;
|
||||||
|
pub global SCORE;
|
||||||
|
""");
|
||||||
|
|
||||||
|
final var projectTable = new ProjectTable();
|
||||||
|
final var fileTable = new FileTable(1);
|
||||||
|
final var projectId = projectTable.register(ProjectDescriptor.builder()
|
||||||
|
.rootPath(projectRoot)
|
||||||
|
.name("core")
|
||||||
|
.version("1.0.0")
|
||||||
|
.sourceRoots(ReadOnlyList.wrap(List.of(sourceRoot)))
|
||||||
|
.build());
|
||||||
|
|
||||||
|
registerFile(projectId, projectRoot, sourceFile, fileTable);
|
||||||
|
registerFile(projectId, projectRoot, modBarrel, fileTable);
|
||||||
|
|
||||||
|
final var ctx = new FrontendPhaseContext(
|
||||||
|
projectTable,
|
||||||
|
fileTable,
|
||||||
|
new BuildStack(ReadOnlyList.wrap(List.of(projectId))));
|
||||||
|
final var diagnostics = DiagnosticSink.empty();
|
||||||
|
|
||||||
|
final var irBackend = new PBSFrontendPhaseService().compile(
|
||||||
|
ctx,
|
||||||
|
diagnostics,
|
||||||
|
LogAggregator.empty(),
|
||||||
|
BuildingIssueSink.empty());
|
||||||
|
|
||||||
|
assertTrue(diagnostics.isEmpty(), diagnostics.stream().map(d -> d.getCode() + ":" + d.getMessage()).toList().toString());
|
||||||
|
assertTrue(irBackend.getEntryPointCallableName().startsWith("__pbs.frame_wrapper$m"));
|
||||||
|
final var wrapper = irBackend.getExecutableFunctions().stream()
|
||||||
|
.filter(function -> irBackend.getEntryPointCallableName().equals(function.callableName()))
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow();
|
||||||
|
assertEquals(4, wrapper.instructions().size());
|
||||||
|
assertTrue(wrapper.instructions().get(0).calleeCallableName().startsWith("__pbs.module_init$m"));
|
||||||
|
assertTrue(wrapper.instructions().get(1).calleeCallableName().startsWith("__pbs.project_init$m"));
|
||||||
|
assertEquals("frame", wrapper.instructions().get(2).calleeCallableName());
|
||||||
|
assertEquals(p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.RET, wrapper.instructions().get(3).kind());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@ -57,6 +57,44 @@ class LowerToIRVMServiceTest {
|
|||||||
assertEquals(0, lowered.module().functions().get(1).instructions().get(0).immediate());
|
assertEquals(0, lowered.module().functions().get(1).instructions().get(0).immediate());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void lowerMustPublishSyntheticWrapperAtFunctionZeroAndKeepFinalRetInWrapperPath() {
|
||||||
|
final var backend = IRBackend.builder()
|
||||||
|
.entryPointCallableName("__pbs.frame_wrapper$m0")
|
||||||
|
.entryPointModuleId(new ModuleId(0))
|
||||||
|
.executableFunctions(ReadOnlyList.from(
|
||||||
|
fn("__pbs.file_init$f1", "app", 30, ReadOnlyList.from(ret())),
|
||||||
|
fn("__pbs.module_init$m0", "app", 31, ReadOnlyList.from(
|
||||||
|
callFunc("app", "__pbs.file_init$f1", 30),
|
||||||
|
ret())),
|
||||||
|
fn("__pbs.project_init$m0", "app", 32, ReadOnlyList.from(
|
||||||
|
callFunc("app", "boot", 10),
|
||||||
|
ret())),
|
||||||
|
fn("boot", "app", 10, ReadOnlyList.from(ret())),
|
||||||
|
fn("frame", "app", 20, ReadOnlyList.from(ret())),
|
||||||
|
fn("__pbs.frame_wrapper$m0", "app", 40, ReadOnlyList.from(
|
||||||
|
callFunc("app", "__pbs.module_init$m0", 31),
|
||||||
|
callFunc("app", "__pbs.project_init$m0", 32),
|
||||||
|
callFunc("app", "frame", 20),
|
||||||
|
ret()))))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final var lowered = new LowerToIRVMService().lower(backend);
|
||||||
|
|
||||||
|
assertEquals("__pbs.frame_wrapper$m0", lowered.module().functions().get(0).name());
|
||||||
|
assertEquals(IRVMOp.CALL, lowered.module().functions().get(0).instructions().get(0).op());
|
||||||
|
assertEquals(IRVMOp.CALL, lowered.module().functions().get(0).instructions().get(1).op());
|
||||||
|
assertEquals(IRVMOp.CALL, lowered.module().functions().get(0).instructions().get(2).op());
|
||||||
|
assertEquals(IRVMOp.RET, lowered.module().functions().get(0).instructions().get(3).op());
|
||||||
|
assertEquals(IRVMOp.RET, lowered.module().functions().stream()
|
||||||
|
.filter(function -> "frame".equals(function.name()))
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow()
|
||||||
|
.instructions()
|
||||||
|
.getLast()
|
||||||
|
.op());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void lowerMustMapHostAndIntrinsicCallsites() {
|
void lowerMustMapHostAndIntrinsicCallsites() {
|
||||||
final var backend = IRBackend.builder()
|
final var backend = IRBackend.builder()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user