implements PR-19.9 conformance fixtures diagnostics and gate closure
This commit is contained in:
parent
58c5ab26d7
commit
459f5e3b87
@ -3,7 +3,7 @@
|
||||
Status: Draft v1 (Traceability Baseline)
|
||||
Applies to: backend conformance traceability for `IRBackend -> IRVM -> OptimizeIRVM -> EmitBytecode`
|
||||
|
||||
Last Updated: 2026-03-09
|
||||
Last Updated: 2026-03-26
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
@ -45,7 +45,7 @@ to concrete positive/negative test evidence and current status.
|
||||
| G20-7.2 | Jump immediates MUST resolve to u32 function-relative offsets before emission. | `LowerToIRVMServiceTest#lowerMustResolveJumpTargetsFromLabels`; `BytecodeEmitterTest#emitMustEncodeJumpOpcodesWithU32Immediate` | `LowerToIRVMServiceTest#lowerMustRejectMissingJumpTargetLabel` | pass | |
|
||||
| G20-7.3 | Jump targets MUST be instruction-boundary valid. | N/A | `IRVMValidatorTest#validateMustRejectInvalidJumpTarget` | pass | |
|
||||
| G20-7.4 | Reachable fallthrough beyond function end MUST be rejected. | N/A | `LowerToIRVMServiceTest#lowerMustRejectUnterminatedFunction`; `IRVMValidatorTest#validateMustRejectRetShapeMismatch` | pass | |
|
||||
| G20-8.1 | Qualified entrypoint function id MUST be 0. | `LowerToIRVMServiceTest#lowerMustAssignEntrypointIdZeroAndSortRemainingDeterministically`; `LowerToIRVMServiceTest#lowerMustUseQualifiedEntrypointIdentityWhenProvided` | `LowerToIRVMServiceTest#lowerMustRejectWhenEntrypointDeclarationIsMissing`; `LowerToIRVMServiceTest#lowerMustRejectMissingQualifiedEntrypointIdentity`; `LowerToIRVMServiceTest#lowerMustRejectWhenEntrypointIsAmbiguous` | pass | Entrypoint is resolved strictly by `EntrypointRef(entryPointModuleId, entryPointCallableName)`. |
|
||||
| G20-8.1 | Qualified entrypoint function id MUST be 0. | `LowerToIRVMServiceTest#lowerMustAssignEntrypointIdZeroAndSortRemainingDeterministically`; `LowerToIRVMServiceTest#lowerMustUseQualifiedEntrypointIdentityWhenProvided`; `LowerToIRVMServiceTest#lowerMustPublishSyntheticWrapperAtFunctionZeroAndKeepFinalRetInWrapperPath` | `LowerToIRVMServiceTest#lowerMustRejectWhenEntrypointDeclarationIsMissing`; `LowerToIRVMServiceTest#lowerMustRejectMissingQualifiedEntrypointIdentity`; `LowerToIRVMServiceTest#lowerMustRejectWhenEntrypointIsAmbiguous`; `LowerToIRVMServiceTest#lowerMustRejectWhenPublishedWrapperWouldNotOccupyFunctionZero` | pass | Entrypoint is resolved strictly by qualified identity; PBS wrapper publication now has dedicated zero-index coverage. |
|
||||
| G20-8.2 | Remaining function ids MUST follow deterministic moduleId-only ordering. | `LowerToIRVMServiceTest#lowerMustAssignEntrypointIdZeroAndSortRemainingDeterministically`; `LowerToIRVMServiceTest#lowerMustUseModulePoolCanonicalIdentityForNonEntrypointOrdering` | N/A | pass | Ordering key is `(moduleId -> modulePool canonical key, callable_name, source_start)`. |
|
||||
| G20-8.3 | Same admitted input graph MUST produce identical function-id assignment. | `BackendSafetyGateSUTest#fullPipelineMustProduceDeterministicBytecodeForSameInput` | N/A | pass | Artifact determinism implies stable function mapping for fixed input. |
|
||||
| G20-9.3 | `CALL_INTRINSIC` MUST remain distinct from host-binding path and resolve ids from canonical registry artifact. | `LowerToIRVMServiceTest#lowerMustMapHostAndIntrinsicCallsites`; `BackendGateIIntegrationTest#gateI_validIntrinsicPath`; `IRVMIntrinsicRegistryParityTest#registryResourceMustExposeExpectedCanonicalEntries`; `IRVMIntrinsicRegistryParityTest#registryMustStayInSyncWithRuntimeBuiltinsTable` | `IRVMValidatorTest#validateProgramMustRejectIntrinsicWithoutSignatureMetadata` | pass | Distinct operation kinds and canonical registry parity checks are enforced. |
|
||||
@ -66,7 +66,7 @@ to concrete positive/negative test evidence and current status.
|
||||
| G21-9.1 | Validation MUST include optimized-vs-non-optimized equivalence fixtures. | `OptimizeIRVMEquivalenceHarnessTest#optimizeOnOffMustPreserveObservableTraceForLoweredHostIntrinsicFixture`; `OptimizeIRVMEquivalenceHarnessTest#optimizeOnOffMustPreserveObservableTraceForConditionalJoinFixture`; `OptimizeIRVMEquivalenceHarnessTest#optimizeOnOffMustPreserveObservableTraceForSimpleLoopFixture`; `OptimizeIRVMEquivalenceHarnessTest#optimizeOnOffMustPreserveObservableTraceForLinearCallFixture` | N/A | pass | Dedicated opt on/off harness with reusable interpreter and deterministic trace assertions is in place. |
|
||||
| G21-9.2 | Validation MUST preserve known negative loader/verifier behavior. | `BackendSafetyGateSUTest#emitStageMustExposeMarshalingLinkageFailureDeterministically`; `BackendGateIIntegrationTest` rejection suite | N/A | pass | |
|
||||
| G21-9.3 | Validation MUST preserve deterministic artifact-level invariants. | `BackendSafetyGateSUTest#fullPipelineMustProduceDeterministicBytecodeForSameInput`; `BytecodeEmitterTest#emitMustRemainDeterministicAfterInterning` | N/A | pass | |
|
||||
| PBS13-12.0 | Executable backend handoff MUST satisfy addendum obligations. | `IRBackendExecutableContractTest` suite | N/A | pass | Row group `PBS13-12.x` details each obligation. |
|
||||
| PBS13-12.0 | Executable backend handoff MUST satisfy addendum obligations. | `IRBackendExecutableContractTest` suite; `PBSFrontendPhaseServiceTest#shouldSynthesizePositiveTopic19FixtureAcrossFileInitProjectInitAndFrame` | `LowerToIRVMServiceTest#lowerMustRejectWhenSyntheticWrapperEntrypointIsMissing`; `LowerToIRVMServiceTest#lowerMustRejectWhenHiddenBootGuardIsMissing`; `LowerToIRVMServiceTest#lowerMustRejectWhenSyntheticCallableOriginIsMissing` | pass | Row group `PBS13-12.x` details each obligation; topic 19 closure now includes lifecycle wrapper/guard/origin evidence. |
|
||||
| PBS13-12.1.1 | Callable identity MUST be preserved at handoff. | `IRBackendExecutableContractTest#aggregatorMustPreserveExecutableFunctionOrderDeterministically` | N/A | pass | |
|
||||
| PBS13-12.1.2 | Observable callable signature MUST be preserved at handoff. | `IRBackendExecutableContractTest#functionContractMustRejectInvalidSlotAndSpanBounds` | N/A | pass | |
|
||||
| PBS13-12.1.3 | Callable category MUST be preserved at handoff. | `PbsFrontendCompilerTest#shouldLowerExecutableFunctionsWithCallsiteCategories` | N/A | pass | |
|
||||
|
||||
@ -416,6 +416,10 @@ public final class PbsFrontendCompiler {
|
||||
return "__pbs.frame_wrapper$m" + normalizedModuleIndex(moduleId);
|
||||
}
|
||||
|
||||
public static String bootGuardGlobalName(final ModuleId moduleId) {
|
||||
return "__pbs.boot_guard$m" + normalizedModuleIndex(moduleId);
|
||||
}
|
||||
|
||||
private static int normalizedModuleIndex(final ModuleId moduleId) {
|
||||
final var normalized = moduleId == null ? ModuleId.none() : moduleId;
|
||||
return normalized.isNone() ? -1 : normalized.getIndex();
|
||||
|
||||
@ -119,6 +119,7 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
|
||||
private IRBackend synthesizePublishedLifecycle(final IRBackend baseBackend) {
|
||||
final var callableSignatures = new ArrayList<>(baseBackend.getCallableSignatures().asList());
|
||||
final var executableFunctions = new ArrayList<>(baseBackend.getExecutableFunctions().asList());
|
||||
final var globals = new ArrayList<>(baseBackend.getGlobals().asList());
|
||||
final var fileInitFunctionsByModule = new LinkedHashMap<ModuleId, List<IRBackendExecutableFunction>>();
|
||||
final var projectInitAnchorsByModule = new LinkedHashMap<ModuleId, String>();
|
||||
final var wrapperNamesByModule = new LinkedHashMap<ModuleId, String>();
|
||||
@ -151,8 +152,9 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
|
||||
entryPointModuleId = wrapperNamesByModule.keySet().iterator().next();
|
||||
entryPointCallableName = wrapperNamesByModule.get(entryPointModuleId);
|
||||
} else {
|
||||
entryPointModuleId = baseBackend.getEntryPointModuleId();
|
||||
entryPointCallableName = baseBackend.getEntryPointCallableName();
|
||||
final var legacyEntrypoint = resolveLegacyEntrypoint(baseBackend);
|
||||
entryPointModuleId = legacyEntrypoint.moduleId();
|
||||
entryPointCallableName = legacyEntrypoint.callableName();
|
||||
}
|
||||
|
||||
final var sortedModuleIds = new ArrayList<ModuleId>();
|
||||
@ -281,6 +283,17 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
|
||||
1,
|
||||
ReadOnlyList.wrap(instructions),
|
||||
frameExecutable.span()));
|
||||
|
||||
if (globals.stream().noneMatch(global ->
|
||||
moduleIdsMatch(entryPointModuleId, global.moduleId())
|
||||
&& PbsFrontendCompiler.bootGuardGlobalName(entryPointModuleId).equals(global.name()))) {
|
||||
globals.add(new p.studio.compiler.models.IRGlobal(
|
||||
frameExecutable.fileId(),
|
||||
entryPointModuleId,
|
||||
PbsFrontendCompiler.bootGuardGlobalName(entryPointModuleId),
|
||||
"bool",
|
||||
frameExecutable.span()));
|
||||
}
|
||||
}
|
||||
|
||||
return IRBackend.builder()
|
||||
@ -288,7 +301,7 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
|
||||
.entryPointModuleId(entryPointModuleId)
|
||||
.functions(baseBackend.getFunctions())
|
||||
.syntheticFunctions(baseBackend.getSyntheticFunctions())
|
||||
.globals(baseBackend.getGlobals())
|
||||
.globals(ReadOnlyList.wrap(globals))
|
||||
.executableFunctions(ReadOnlyList.wrap(executableFunctions))
|
||||
.modulePool(baseBackend.getModulePool())
|
||||
.callableSignatures(ReadOnlyList.wrap(callableSignatures))
|
||||
@ -384,6 +397,19 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
|
||||
return (int) value;
|
||||
}
|
||||
|
||||
private LegacyEntrypoint resolveLegacyEntrypoint(final IRBackend backend) {
|
||||
for (final var candidateName : List.of("frame", "main")) {
|
||||
final var candidates = backend.getExecutableFunctions().stream()
|
||||
.filter(function -> candidateName.equals(function.callableName()))
|
||||
.filter(function -> function.moduleId() != null && !function.moduleId().isNone())
|
||||
.toList();
|
||||
if (candidates.size() == 1) {
|
||||
return new LegacyEntrypoint(candidates.getFirst().moduleId(), candidateName);
|
||||
}
|
||||
}
|
||||
return new LegacyEntrypoint(backend.getEntryPointModuleId(), backend.getEntryPointCallableName());
|
||||
}
|
||||
|
||||
private Set<ModuleId> blockedModulesByDependency(
|
||||
final Set<ModuleId> failedModuleIds,
|
||||
final Map<ModuleId, Set<ModuleId>> dependenciesByModule) {
|
||||
@ -414,4 +440,9 @@ public class PBSFrontendPhaseService implements FrontendPhaseService {
|
||||
ModuleId moduleId,
|
||||
p.studio.compiler.models.IRBackendFile irBackendFile) {
|
||||
}
|
||||
|
||||
private record LegacyEntrypoint(
|
||||
ModuleId moduleId,
|
||||
String callableName) {
|
||||
}
|
||||
}
|
||||
|
||||
@ -308,6 +308,8 @@ class PBSFrontendPhaseServiceTest {
|
||||
|
||||
assertTrue(diagnostics.isEmpty(), diagnostics.stream().map(d -> d.getCode() + ":" + d.getMessage()).toList().toString());
|
||||
assertTrue(irBackend.getEntryPointCallableName().startsWith("__pbs.frame_wrapper$m"));
|
||||
assertTrue(irBackend.getGlobals().stream().anyMatch(global ->
|
||||
global.name().startsWith("__pbs.boot_guard$m")));
|
||||
final var wrapper = irBackend.getExecutableFunctions().stream()
|
||||
.filter(function -> irBackend.getEntryPointCallableName().equals(function.callableName()))
|
||||
.findFirst()
|
||||
@ -319,6 +321,73 @@ class PBSFrontendPhaseServiceTest {
|
||||
assertEquals(p.studio.compiler.models.IRBackendExecutableFunction.InstructionKind.RET, wrapper.instructions().get(3).kind());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSynthesizePositiveTopic19FixtureAcrossFileInitProjectInitAndFrame() throws IOException {
|
||||
final var projectRoot = tempDir.resolve("project-topic-19-positive");
|
||||
final var sourceRoot = projectRoot.resolve("src");
|
||||
final var modulePath = sourceRoot.resolve("game");
|
||||
Files.createDirectories(modulePath);
|
||||
|
||||
final var globalsSource = modulePath.resolve("globals.pbs");
|
||||
final var mainSource = modulePath.resolve("main.pbs");
|
||||
final var modBarrel = modulePath.resolve("mod.barrel");
|
||||
Files.writeString(globalsSource, """
|
||||
declare global SCORE: int = 0;
|
||||
|
||||
[Init]
|
||||
fn warmup() -> void { return; }
|
||||
""");
|
||||
Files.writeString(mainSource, """
|
||||
[Init]
|
||||
fn boot() -> void { return; }
|
||||
|
||||
[Frame]
|
||||
fn frame() -> void { return; }
|
||||
""");
|
||||
Files.writeString(modBarrel, """
|
||||
pub fn warmup() -> void;
|
||||
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, globalsSource, fileTable);
|
||||
registerFile(projectId, projectRoot, mainSource, 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"));
|
||||
assertTrue(irBackend.getExecutableFunctions().stream().anyMatch(function ->
|
||||
function.callableName().startsWith("__pbs.file_init$f")));
|
||||
assertTrue(irBackend.getExecutableFunctions().stream().anyMatch(function ->
|
||||
function.callableName().startsWith("__pbs.module_init$m")));
|
||||
assertTrue(irBackend.getExecutableFunctions().stream().anyMatch(function ->
|
||||
function.callableName().startsWith("__pbs.project_init$m")));
|
||||
assertTrue(irBackend.getGlobals().stream().anyMatch(global -> "SCORE".equals(global.name())));
|
||||
assertTrue(irBackend.getGlobals().stream().anyMatch(global -> global.name().startsWith("__pbs.boot_guard$m")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSkipIrLoweringForSourcesMarkedAsSdkInterface() throws IOException {
|
||||
final var projectRoot = tempDir.resolve("project-sdk-interface");
|
||||
|
||||
@ -5,6 +5,10 @@ public enum IRVMLoweringErrorCode {
|
||||
LOWER_IRVM_ENTRYPOINT_DECLARATION_MISSING,
|
||||
LOWER_IRVM_ENTRYPOINT_MISSING,
|
||||
LOWER_IRVM_ENTRYPOINT_AMBIGUOUS,
|
||||
LOWER_IRVM_SYNTHETIC_WRAPPER_ENTRYPOINT_MISSING,
|
||||
LOWER_IRVM_PUBLISHED_ENTRYPOINT_NOT_ZERO,
|
||||
LOWER_IRVM_HIDDEN_BOOT_GUARD_MISSING,
|
||||
LOWER_IRVM_SYNTHETIC_ORIGIN_MISSING,
|
||||
LOWER_IRVM_MISSING_CALLEE,
|
||||
LOWER_IRVM_INVALID_INTRINSIC_ID,
|
||||
LOWER_IRVM_CALL_ARG_SLOTS_MISMATCH,
|
||||
|
||||
@ -4,6 +4,7 @@ import p.studio.compiler.backend.bytecode.BytecodeEmitter;
|
||||
import p.studio.compiler.backend.bytecode.BytecodeModule;
|
||||
import p.studio.compiler.models.IRBackend;
|
||||
import p.studio.compiler.models.IRBackendExecutableFunction;
|
||||
import p.studio.compiler.models.IRSyntheticCallableKind;
|
||||
import p.studio.compiler.source.Span;
|
||||
import p.studio.compiler.source.identifiers.CallableId;
|
||||
import p.studio.compiler.source.identifiers.ModuleId;
|
||||
@ -38,6 +39,7 @@ public class LowerToIRVMService {
|
||||
"IRBackend has no executable functions");
|
||||
}
|
||||
|
||||
validatePbsLifecycleStructure(backend);
|
||||
final var ordered = orderFunctions(backend);
|
||||
final var funcIdByCallableId = new HashMap<CallableId, Integer>();
|
||||
for (var i = 0; i < ordered.size(); i++) {
|
||||
@ -452,6 +454,14 @@ public class LowerToIRVMService {
|
||||
entryPointModuleId.getIndex()));
|
||||
}
|
||||
final var entrypoint = entrypoints.getFirst();
|
||||
final var publishedWrapper = resolvePublishedWrapper(backend);
|
||||
if (publishedWrapper != null
|
||||
&& (!entryPointCallableName.equals(publishedWrapper.callableName())
|
||||
|| !sameModuleIdentity(entryPointModuleId, publishedWrapper.moduleId()))) {
|
||||
throw new IRVMLoweringException(
|
||||
IRVMLoweringErrorCode.LOWER_IRVM_PUBLISHED_ENTRYPOINT_NOT_ZERO,
|
||||
"published entrypoint is not function index 0 because entrypoint identity does not select the published wrapper");
|
||||
}
|
||||
final var ordered = new ArrayList<IRBackendExecutableFunction>(sorted.size());
|
||||
ordered.add(entrypoint);
|
||||
for (final var fn : sorted) {
|
||||
@ -462,6 +472,83 @@ public class LowerToIRVMService {
|
||||
return ReadOnlyList.wrap(ordered);
|
||||
}
|
||||
|
||||
private void validatePbsLifecycleStructure(final IRBackend backend) {
|
||||
if (backend.getSyntheticFunctions().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (final var syntheticFunction : backend.getSyntheticFunctions()) {
|
||||
if (syntheticFunction.origin() == null
|
||||
|| syntheticFunction.origin().fileId() == null
|
||||
|| syntheticFunction.origin().anchorCallableName().isBlank()
|
||||
|| syntheticFunction.origin().span() == null
|
||||
|| syntheticFunction.origin().span().isNone()) {
|
||||
throw new IRVMLoweringException(
|
||||
IRVMLoweringErrorCode.LOWER_IRVM_SYNTHETIC_ORIGIN_MISSING,
|
||||
"synthetic callable origin missing for " + syntheticFunction.callableName());
|
||||
}
|
||||
}
|
||||
|
||||
final var publishedWrapper = resolvePublishedWrapper(backend);
|
||||
if (publishedWrapper == null) {
|
||||
return;
|
||||
}
|
||||
final var wrapperExecutablePresent = backend.getExecutableFunctions().stream()
|
||||
.anyMatch(function -> publishedWrapper.callableName().equals(function.callableName())
|
||||
&& sameModuleIdentity(publishedWrapper.moduleId(), function.moduleId()));
|
||||
if (!wrapperExecutablePresent) {
|
||||
throw new IRVMLoweringException(
|
||||
IRVMLoweringErrorCode.LOWER_IRVM_SYNTHETIC_WRAPPER_ENTRYPOINT_MISSING,
|
||||
"synthetic wrapper entrypoint missing: " + publishedWrapper.callableName());
|
||||
}
|
||||
|
||||
final var expectedBootGuardName = bootGuardName(publishedWrapper.moduleId());
|
||||
final var bootGuardPresent = backend.getGlobals().stream()
|
||||
.anyMatch(global -> expectedBootGuardName.equals(global.name())
|
||||
&& sameModuleIdentity(publishedWrapper.moduleId(), global.moduleId()));
|
||||
if (!bootGuardPresent) {
|
||||
throw new IRVMLoweringException(
|
||||
IRVMLoweringErrorCode.LOWER_IRVM_HIDDEN_BOOT_GUARD_MISSING,
|
||||
"hidden boot guard is missing for moduleId=%d".formatted(
|
||||
publishedWrapper.moduleId() == null || publishedWrapper.moduleId().isNone()
|
||||
? -1
|
||||
: publishedWrapper.moduleId().getIndex()));
|
||||
}
|
||||
}
|
||||
|
||||
private p.studio.compiler.models.IRSyntheticFunction resolvePublishedWrapper(final IRBackend backend) {
|
||||
p.studio.compiler.models.IRSyntheticFunction publishedWrapper = null;
|
||||
for (final var syntheticFunction : backend.getSyntheticFunctions()) {
|
||||
if (syntheticFunction.kind() != IRSyntheticCallableKind.PUBLISHED_FRAME_WRAPPER) {
|
||||
continue;
|
||||
}
|
||||
if (publishedWrapper != null) {
|
||||
throw new IRVMLoweringException(
|
||||
IRVMLoweringErrorCode.LOWER_IRVM_ENTRYPOINT_AMBIGUOUS,
|
||||
"multiple published frame wrappers found in executable backend");
|
||||
}
|
||||
publishedWrapper = syntheticFunction;
|
||||
}
|
||||
return publishedWrapper;
|
||||
}
|
||||
|
||||
private String bootGuardName(final ModuleId moduleId) {
|
||||
final var normalizedModuleId = moduleId == null ? ModuleId.none() : moduleId;
|
||||
return "__pbs.boot_guard$m" + (normalizedModuleId.isNone() ? -1 : normalizedModuleId.getIndex());
|
||||
}
|
||||
|
||||
private boolean sameModuleIdentity(
|
||||
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 String moduleSortKey(
|
||||
final IRBackend backend,
|
||||
final ModuleId moduleId) {
|
||||
|
||||
@ -3,6 +3,10 @@ package p.studio.compiler.backend.irvm;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import p.studio.compiler.models.IRBackend;
|
||||
import p.studio.compiler.models.IRBackendExecutableFunction;
|
||||
import p.studio.compiler.models.IRGlobal;
|
||||
import p.studio.compiler.models.IRSyntheticCallableKind;
|
||||
import p.studio.compiler.models.IRSyntheticFunction;
|
||||
import p.studio.compiler.models.IRSyntheticOrigin;
|
||||
import p.studio.compiler.source.Span;
|
||||
import p.studio.compiler.source.identifiers.CallableId;
|
||||
import p.studio.compiler.source.identifiers.FileId;
|
||||
@ -62,6 +66,17 @@ class LowerToIRVMServiceTest {
|
||||
final var backend = IRBackend.builder()
|
||||
.entryPointCallableName("__pbs.frame_wrapper$m0")
|
||||
.entryPointModuleId(new ModuleId(0))
|
||||
.globals(ReadOnlyList.from(new IRGlobal(
|
||||
new FileId(0),
|
||||
new ModuleId(0),
|
||||
"__pbs.boot_guard$m0",
|
||||
"bool",
|
||||
Span.none())))
|
||||
.syntheticFunctions(ReadOnlyList.from(new IRSyntheticFunction(
|
||||
new ModuleId(0),
|
||||
"__pbs.frame_wrapper$m0",
|
||||
IRSyntheticCallableKind.PUBLISHED_FRAME_WRAPPER,
|
||||
new IRSyntheticOrigin(new FileId(0), "frame", new Span(new FileId(0), 1, 10)))))
|
||||
.executableFunctions(ReadOnlyList.from(
|
||||
fn("__pbs.file_init$f1", "app", 30, ReadOnlyList.from(ret())),
|
||||
fn("__pbs.module_init$m0", "app", 31, ReadOnlyList.from(
|
||||
@ -95,6 +110,105 @@ class LowerToIRVMServiceTest {
|
||||
.op());
|
||||
}
|
||||
|
||||
@Test
|
||||
void lowerMustRejectWhenSyntheticWrapperEntrypointIsMissing() {
|
||||
final var backend = IRBackend.builder()
|
||||
.entryPointCallableName("__pbs.frame_wrapper$m0")
|
||||
.entryPointModuleId(new ModuleId(0))
|
||||
.globals(ReadOnlyList.from(new IRGlobal(
|
||||
new FileId(0),
|
||||
new ModuleId(0),
|
||||
"__pbs.boot_guard$m0",
|
||||
"bool",
|
||||
Span.none())))
|
||||
.syntheticFunctions(ReadOnlyList.from(new IRSyntheticFunction(
|
||||
new ModuleId(0),
|
||||
"__pbs.frame_wrapper$m0",
|
||||
IRSyntheticCallableKind.PUBLISHED_FRAME_WRAPPER,
|
||||
new IRSyntheticOrigin(new FileId(0), "frame", new Span(new FileId(0), 1, 10)))))
|
||||
.executableFunctions(ReadOnlyList.from(
|
||||
fn("frame", "app", 20, ReadOnlyList.from(ret()))))
|
||||
.build();
|
||||
|
||||
final var thrown = assertThrows(IRVMLoweringException.class, () -> new LowerToIRVMService().lower(backend));
|
||||
assertEquals(IRVMLoweringErrorCode.LOWER_IRVM_SYNTHETIC_WRAPPER_ENTRYPOINT_MISSING, thrown.code());
|
||||
}
|
||||
|
||||
@Test
|
||||
void lowerMustRejectWhenPublishedWrapperWouldNotOccupyFunctionZero() {
|
||||
final var backend = IRBackend.builder()
|
||||
.entryPointCallableName("frame")
|
||||
.entryPointModuleId(new ModuleId(0))
|
||||
.globals(ReadOnlyList.from(new IRGlobal(
|
||||
new FileId(0),
|
||||
new ModuleId(0),
|
||||
"__pbs.boot_guard$m0",
|
||||
"bool",
|
||||
Span.none())))
|
||||
.syntheticFunctions(ReadOnlyList.from(new IRSyntheticFunction(
|
||||
new ModuleId(0),
|
||||
"__pbs.frame_wrapper$m0",
|
||||
IRSyntheticCallableKind.PUBLISHED_FRAME_WRAPPER,
|
||||
new IRSyntheticOrigin(new FileId(0), "frame", new Span(new FileId(0), 1, 10)))))
|
||||
.executableFunctions(ReadOnlyList.from(
|
||||
fn("__pbs.frame_wrapper$m0", "app", 40, ReadOnlyList.from(
|
||||
callFunc("app", "frame", 20),
|
||||
ret())),
|
||||
fn("frame", "app", 20, ReadOnlyList.from(ret()))))
|
||||
.build();
|
||||
|
||||
final var thrown = assertThrows(IRVMLoweringException.class, () -> new LowerToIRVMService().lower(backend));
|
||||
assertEquals(IRVMLoweringErrorCode.LOWER_IRVM_PUBLISHED_ENTRYPOINT_NOT_ZERO, thrown.code());
|
||||
}
|
||||
|
||||
@Test
|
||||
void lowerMustRejectWhenHiddenBootGuardIsMissing() {
|
||||
final var backend = IRBackend.builder()
|
||||
.entryPointCallableName("__pbs.frame_wrapper$m0")
|
||||
.entryPointModuleId(new ModuleId(0))
|
||||
.syntheticFunctions(ReadOnlyList.from(new IRSyntheticFunction(
|
||||
new ModuleId(0),
|
||||
"__pbs.frame_wrapper$m0",
|
||||
IRSyntheticCallableKind.PUBLISHED_FRAME_WRAPPER,
|
||||
new IRSyntheticOrigin(new FileId(0), "frame", new Span(new FileId(0), 1, 10)))))
|
||||
.executableFunctions(ReadOnlyList.from(
|
||||
fn("__pbs.frame_wrapper$m0", "app", 40, ReadOnlyList.from(
|
||||
callFunc("app", "frame", 20),
|
||||
ret())),
|
||||
fn("frame", "app", 20, ReadOnlyList.from(ret()))))
|
||||
.build();
|
||||
|
||||
final var thrown = assertThrows(IRVMLoweringException.class, () -> new LowerToIRVMService().lower(backend));
|
||||
assertEquals(IRVMLoweringErrorCode.LOWER_IRVM_HIDDEN_BOOT_GUARD_MISSING, thrown.code());
|
||||
}
|
||||
|
||||
@Test
|
||||
void lowerMustRejectWhenSyntheticCallableOriginIsMissing() {
|
||||
final var backend = IRBackend.builder()
|
||||
.entryPointCallableName("__pbs.frame_wrapper$m0")
|
||||
.entryPointModuleId(new ModuleId(0))
|
||||
.globals(ReadOnlyList.from(new IRGlobal(
|
||||
new FileId(0),
|
||||
new ModuleId(0),
|
||||
"__pbs.boot_guard$m0",
|
||||
"bool",
|
||||
Span.none())))
|
||||
.syntheticFunctions(ReadOnlyList.from(new IRSyntheticFunction(
|
||||
new ModuleId(0),
|
||||
"__pbs.frame_wrapper$m0",
|
||||
IRSyntheticCallableKind.PUBLISHED_FRAME_WRAPPER,
|
||||
new IRSyntheticOrigin(new FileId(0), "", Span.none()))))
|
||||
.executableFunctions(ReadOnlyList.from(
|
||||
fn("__pbs.frame_wrapper$m0", "app", 40, ReadOnlyList.from(
|
||||
callFunc("app", "frame", 20),
|
||||
ret())),
|
||||
fn("frame", "app", 20, ReadOnlyList.from(ret()))))
|
||||
.build();
|
||||
|
||||
final var thrown = assertThrows(IRVMLoweringException.class, () -> new LowerToIRVMService().lower(backend));
|
||||
assertEquals(IRVMLoweringErrorCode.LOWER_IRVM_SYNTHETIC_ORIGIN_MISSING, thrown.code());
|
||||
}
|
||||
|
||||
@Test
|
||||
void lowerMustMapHostAndIntrinsicCallsites() {
|
||||
final var backend = IRBackend.builder()
|
||||
|
||||
@ -13,9 +13,9 @@ import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
class BackendClaimScopeSpecTest {
|
||||
private static final String LOWERING_SPEC_RELATIVE_PATH =
|
||||
"docs/compiler/general/specs/20. IRBackend to IRVM Lowering Specification.md";
|
||||
"docs/specs/compiler/20. IRBackend to IRVM Lowering Specification.md";
|
||||
private static final String MATRIX_RELATIVE_PATH =
|
||||
"docs/compiler/general/specs/22. Backend Spec-to-Test Conformance Matrix.md";
|
||||
"docs/specs/compiler/22. Backend Spec-to-Test Conformance Matrix.md";
|
||||
private static final String DECISION_RELATIVE_PATH =
|
||||
"docs/compiler/pbs/decisions/SPAWN-YIELD v1 Claim Rescope Decision.md";
|
||||
|
||||
@ -63,7 +63,7 @@ class BackendClaimScopeSpecTest {
|
||||
private Path locateRepoRoot() {
|
||||
var cursor = Path.of(System.getProperty("user.dir")).toAbsolutePath().normalize();
|
||||
while (cursor != null) {
|
||||
final var hasDocs = Files.isDirectory(cursor.resolve("docs/compiler/general/specs"));
|
||||
final var hasDocs = Files.isDirectory(cursor.resolve("docs/specs/compiler"));
|
||||
final var hasCompiler = Files.isDirectory(cursor.resolve("prometeu-compiler"));
|
||||
if (hasDocs && hasCompiler) {
|
||||
return cursor;
|
||||
|
||||
@ -14,7 +14,7 @@ import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class BackendConformanceMatrixSpecTest {
|
||||
private static final String MATRIX_RELATIVE_PATH =
|
||||
"docs/compiler/general/specs/22. Backend Spec-to-Test Conformance Matrix.md";
|
||||
"docs/specs/compiler/22. Backend Spec-to-Test Conformance Matrix.md";
|
||||
private static final String MATRIX_HARD_GATE_ENV = "PROMETEU_MATRIX_HARD_GATE";
|
||||
private static final String CHANGED_FILES_ENV = "PROMETEU_CHANGED_FILES";
|
||||
private static final List<String> HARD_GATE_PATH_PREFIXES = List.of(
|
||||
@ -158,8 +158,9 @@ class BackendConformanceMatrixSpecTest {
|
||||
var cursor = Path.of(System.getProperty("user.dir")).toAbsolutePath().normalize();
|
||||
while (cursor != null) {
|
||||
final var hasDocs = Files.isDirectory(cursor.resolve("docs/compiler/general/specs"));
|
||||
final var hasCurrentDocs = Files.isDirectory(cursor.resolve("docs/specs/compiler"));
|
||||
final var hasCompiler = Files.isDirectory(cursor.resolve("prometeu-compiler"));
|
||||
if (hasDocs && hasCompiler) {
|
||||
if ((hasDocs || hasCurrentDocs) && hasCompiler) {
|
||||
return cursor;
|
||||
}
|
||||
cursor = cursor.getParent();
|
||||
|
||||
@ -62,7 +62,7 @@ class NoModuleKeyRegressionSpecTest {
|
||||
private Path locateRepoRoot() {
|
||||
var cursor = Path.of(System.getProperty("user.dir")).toAbsolutePath().normalize();
|
||||
while (cursor != null) {
|
||||
final var hasDocs = Files.isDirectory(cursor.resolve("docs/compiler/general/specs"));
|
||||
final var hasDocs = Files.isDirectory(cursor.resolve("docs/specs/compiler"));
|
||||
final var hasCompiler = Files.isDirectory(cursor.resolve("prometeu-compiler"));
|
||||
if (hasDocs && hasCompiler) {
|
||||
return cursor;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user