prometeu-studio/docs/specs/compiler/20. IRBackend to IRVM Lowering Specification.md
bQUARKz e809ce63d3
All checks were successful
JaCoCo Coverage #### Project Overview No changes detected, that affect the code coverage. * Line Coverage: 62.10% (17811/28682) * Branch Coverage: 52.81% (6878/13025) * Lines of Code: 28682 * Cyclomatic Complexity: 11494 #### Quality Gates Summary Output truncated.
Test / Build skipped: 15, passed: 654
Intrepid/Prometeu/Studio/pipeline/head This commit looks good
Intrepid/Prometeu/Studio/pipeline/pr-master This commit looks good
implements PLN-0129
Add ArchUnit guards for Java multi-frontend boundaries in a test-only
module. Identify PBS by frontend-module origin, allow only the registry
as composition root, and lock the wording in specs 19, 20, and 22.

Housekeep DSC-0064 with LSN-0066.
2026-09-19 03:32:43 +01:00

15 KiB

IRBackend to IRVM Lowering Specification

Status: Draft v1 (Backend Baseline)
Applies to: executable-backend lowering from the common IRBackend handoff into IRVM

1. Purpose

This document defines the normative lowering contract from executable IRBackend to IRVM.

Its purpose is to make backend lowering deterministic, reviewable, and compatible with runtime ISA/verification authority.

IRBackend is the common frontend-to-backend executable handoff. It is emitted by frontends and consumed by common backend stages. Backend lowering MUST NOT require PBS AST nodes, PBS tokens, PBS parser structures, PBS semantic objects, PBS editorial objects, or equivalent language-owned objects from future frontends.

IRVM is an internal compiler representation. It MUST be consumed by common compiler optimization and PBX emission stages, and it MUST NOT cross the compiler/runtime boundary or become an input, dependency, public type, or operational concept of the runtime loader, verifier, or PVM. PBX is the only executable artifact boundary between compiler and runtime.

2. Scope

This document defines:

  • required backend preconditions before IRBackend -> IRVM starts,
  • allowed language-neutral IRBackend contract concepts at the backend boundary,
  • IRVM structural obligations needed before optimization/emission,
  • deterministic function-id assignment and control-flow lowering obligations,
  • callsite lowering obligations across function, host-backed, and VM-owned intrinsic paths,
  • mandatory compiler-side structural pre-verification before bytecode emission,
  • and deterministic rejection obligations for backend-originated failures.

This document does not define:

  • binary PBX layout details,
  • loader patching internals,
  • runtime execution internals,
  • one mandatory optimizer implementation,
  • or frontend-specific lowering rules before IRBackend emission.

3. Authority and Precedence

Normative precedence:

  1. Runtime authority (docs/specs/hardware/topics/chapter-2.md, chapter-3.md, chapter-9.md, chapter-12.md, chapter-16.md)
  2. Bytecode authority (docs/specs/bytecode/ISA_CORE.md)
  3. This document
  4. Frontend-specific lowering specifications, including docs/specs/compiler-languages/pbs/13. Lowering IRBackend Specification.md

If a rule here conflicts with higher-precedence authorities, it is invalid.

4. Normative Inputs

This document depends on:

  • docs/specs/compiler-languages/pbs/6.1. Intrinsics and Builtin Types Specification.md
  • docs/specs/compiler-languages/pbs/6.2. Host ABI Binding and Loader Resolution Specification.md
  • frontend-specific lowering specifications that emit the common IRBackend handoff
  • 15. Bytecode and PBX Mapping Specification.md
  • 21. IRVM Optimization Pipeline Specification.md

4.1 Common IRBackend Contract

At this boundary, IRBackend may contain only language-neutral compiler concepts required by executable backend lowering:

  1. source identity and spans,
  2. modules and module identifiers,
  3. callable identities, signatures, names, arity, and type-shape surfaces,
  4. executable functions,
  5. instruction kinds and instruction metadata,
  6. globals and global origins,
  7. synthetic executable functions and synthetic origins,
  8. host-call metadata,
  9. intrinsic metadata,
  10. reserved metadata required for backend lowering,
  11. required runtime or platform capabilities,
  12. and an optional typed IRLifecycleDeclaration used as lifecycle provenance after common assembly.

The common backend contract MUST express these concepts as backend obligations, not as PBS syntax or PBS semantic rules. When a concept currently originates from PBS, the backend contract still owns only the language-neutral obligation carried by IRBackend.

4.3 IRLifecycleDeclaration

IRLifecycleDeclaration is the typed, immutable, serializable-by-design lifecycle provenance carried by IRBackend.

An executable handoff MUST contain exactly one declaration before common assembly. A deliberately non-executable handoff MAY omit it; absence MUST NOT synthesize a default main or published wrapper.

The declaration MUST represent, using ordered collections and typed ids:

  1. modules in explicit dependency order,
  2. each module's ordered file-init fragment references,
  3. an optional project-init callable reference,
  4. and exactly one frame-root callable reference.

References MUST use ModuleId, FileId, CallableId, a typed role, and source attribution. The public shape MUST NOT expose maps, sets, callbacks, compiler services, or frontend-owned types.

AssembleLifecyclePipelineStage MUST run after FrontendPhasePipelineStage and before LowerToIRVMPipelineStage. LifecycleAssemblerService is the only owner of derived lifecycle artifacts: module init, project-init wrapper, published frame wrapper, physical entrypoint, and hidden boot guard. Those artifacts MUST use deterministic neutral internal identities. Common code MUST NOT discover roles from __pbs.* prefixes or depend on types from the PBS frontend module (prometeu-frontend-pbs). The package p.studio.compiler.pbs is not the complete boundary.

The declaration MUST remain available on the assembled IRBackend as typed provenance. Downstream inspection MUST use roles, ids, and origins rather than synthetic names.

Project init MUST be absent from file-init fragments and MUST execute exactly once, after all module initializers and before the frame root. Bootstrap failure MUST abort without retry. The boot guard MUST prevent a second bootstrap after successful completion. The published wrapper owns the final frame return.

The common backend contract MUST NOT expose or require:

  1. PbsExpression, PbsStatement, PbsToken, or equivalent PBS-owned syntax objects,
  2. PBS parser cursors, parse contexts, parse nodes, or token kinds,
  3. PBS semantic validator internals,
  4. PBS editorial or language-service objects,
  5. or any type from the PBS frontend module (prometeu-frontend-pbs) or an equivalent language frontend module as part of the public backend handoff.

4.2 Serializable-by-Design Contract Shape

The public IRBackend handoff MUST remain serializable by design. This requirement does not select a wire format, schema language, RPC mechanism, process boundary, plugin runtime, or external frontend transport. It only constrains the public data shape so those choices remain possible later without redesigning the handoff.

Public IRBackend contract types MUST be modelable as an acyclic, deterministic data graph made of:

  1. primitive values and strings,
  2. enums,
  3. immutable value objects,
  4. ordered lists,
  5. nullable or optional scalar fields only where absence is part of the contract,
  6. explicit identifier values for cross-references,
  7. and source attribution values such as file ids and spans.

The public handoff MUST NOT expose, require, or rely on:

  1. callbacks, lambdas, service objects, registries, visitors, or live compiler services,
  2. frontend AST, parser, token, semantic, or editorial objects,
  3. mutable global state as part of interpretation,
  4. object identity equality as a semantic key,
  5. unordered collections where iteration order affects output,
  6. cyclic references between public handoff objects,
  7. lazy values whose result depends on current process state,
  8. or a selected wire format.

Cross-object references MUST use explicit ids or stable symbolic keys. Existing table-scoped identifiers such as FileId, ModuleId, CallableId, and IntrinsicId are valid handoff values when their table scope is explicit and deterministic. New typed ids SHOULD be introduced only when a concrete public handoff field would otherwise depend on textual identity, object identity, or a direct object reference.

Ordered collections MUST preserve deterministic order. Any map-like data that becomes part of the handoff MUST either be represented as an ordered list of entries or define deterministic key ordering before it reaches the public contract.

Initial enforcement for this contract SHOULD be audit and guardrail based: specs, reflection tests over public handoff surfaces, deterministic ordering tests, and targeted fixes for concrete leaks. A serializer or schema implementation is not required for conformance to this subsection.

5. Backend Entry Preconditions

Lowering from IRBackend to IRVM may start only when:

  1. frontend admission to IRBackend is complete for the active slice,
  2. common lifecycle assembly has already materialized derived artifacts for an executable handoff,
  3. required callsite categories (CALL_FUNC/CALL_HOST/CALL_INTRINSIC) are available,
  4. required canonical host/intrinsic metadata is available for admitted callsites,
  5. dependency-scoped fail-fast exclusions have already been applied at the IRBackend boundary,
  6. compiler-selected published-wrapper entrypoint identity is present and unambiguous,
  7. and target vm_profile is selected deterministically.

Declaration validation, role uniqueness, explicit order, and rejection of frontend-preassembled derived artifacts belong to LifecycleAssemblerService. IRBackend -> IRVM MUST keep only backend preconditions on the assembled graph, such as published-wrapper identity, synthetic origin presence, and hidden boot-guard presence. It MUST NOT reconstruct lifecycle order from callable names.

Those common structural diagnostics are compiler-general. Classification, code families, and dual language/platform reporting are owned by docs/specs/compiler/19. Verification and Safety Checks Specification.md section 11. They MUST NOT be catalogued as PBS-facing required coverage.

6. IRVM Model Obligations

IRVM v1 is a quasi-ISA backend form:

  1. it may contain Core ISA ops and internal extension ops (IRVM_EXT),
  2. each IRVM_EXT must declare structural metadata equivalent to opcode-spec needs:
    • pops,
    • pushes,
    • is_branch,
    • is_terminator,
  3. and IRVM_EXT must be eliminable before bytecode emission.

IRVM must preserve, per function:

  1. param_slots,
  2. local_slots,
  3. return_slots,
  4. max_stack_slots,
  5. function entry mapping and deterministic identity.

7. Control-Flow Lowering Obligations

Control-flow lowering must satisfy:

  1. symbolic labels are allowed internally in lowering,
  2. final jump immediates must be resolved to u32 offsets relative to function start before emission,
  3. jump targets must be instruction-boundary-valid within function ranges,
  4. and reachable fallthrough beyond function end without terminator is rejected.

8. Deterministic Function-ID Assignment

Function indexing must be deterministic:

  1. the compiler-selected published wrapper is the physical entrypoint,
  2. the published wrapper function id is 0,
  3. remaining functions are ordered by (moduleId -> modulePool canonical key, callable_name, source_start),
  4. manifest-owned or name-only entrypoint fallback is forbidden,
  5. and identical admitted input graphs must produce identical function-id assignments.

For executable lowering:

  • the declared frame-root callable remains the logical frame root,
  • the assembled published wrapper is the physical entrypoint,
  • the wrapper path must contain the final frame return,
  • and lowering must preserve that distinction through IRVM.

PBS [Init]/[Frame] markers are a frontend source of the declaration. The backend-owned obligation is the neutral requirement that the compiler-selected published wrapper be the physical entrypoint and that lifecycle provenance remain explicit in IRBackend through typed roles and ids. Neutral synthetic names are internal and are not a public ABI.

9. Callsite Lowering Obligations

9.1 Function calls

CALL_FUNC lowers to CALL <func_id> with valid index and arity-consistent signature contracts.

9.2 Host-backed calls

CALL_HOST lowers to pre-load HOSTCALL <sysc_index>, not raw SYSCALL.

When a host-backed callsite carries backend-owned symbolic asset lowering metadata:

  1. the backend must validate the symbolic asset operand against the backend-owned admitted surface,
  2. the backend must resolve the symbolic identity to canonical runtime-facing asset_id,
  3. and the final host-backed lowering path must consume the resolved asset_id, not the original symbolic address.

9.3 VM-owned intrinsic calls

CALL_INTRINSIC lowers to VM-owned intrinsic path and must remain distinct from host-binding paths.

Final intrinsic id resolution rules:

  1. compiler must resolve final intrinsic ids from the canonical ISA-scoped intrinsic registry artifact,
  2. compiler-local hardcoded maps or runtime-source parsing must not be authoritative identity sources,
  3. runtime and compiler consumers must validate parity against the same canonical artifact in strict CI mode.

9.4 Closure and coroutine constraints (v1)

  1. closure calls with unknown static callee are rejected in v1,
  2. SPAWN and YIELD are outside the executable core-v1 claim scope and MUST be rejected as unsupported forms in v1 backend admission,
  3. SPAWN argument-count and YIELD stack-shape invariants remain reserved for a future profile that explicitly admits these opcodes,
  4. and any profile that admits SPAWN/YIELD MUST publish dedicated conformance evidence before claiming support.

10. Mandatory Compiler-Side Pre-Verification

Before bytecode emission, backend must run structural pre-verification on lowered IRVM (after resolving to Core-ISA-compatible form), covering at minimum:

  1. jump target validity,
  2. stack-depth join consistency,
  3. underflow/overflow against declared limits,
  4. return-shape consistency,
  5. function-id validity,
  6. callsite arity consistency,
  7. and structural validity of host/intrinsic call forms.
  8. and structural validity of backend-owned symbolic-to-operational rewrites such as Addressable -> asset_id.

For host-backed asset calls in v1, this validation includes backend-owned symbolic-to-operational rewrites selected by preserved host metadata. PBS currently emits this designation from metadata such as [AssetLowering(param = N)], but final operational resolution is a common backend obligation.

This pre-verification does not replace runtime verifier authority.

11. Deterministic Rejection Policy

When backend lowering fails at this boundary:

  1. rejection must be deterministic,
  2. diagnostics identity and phase must remain stable,
  3. and source attribution must be preserved whenever the failure remains source-attributable and user-actionable.

12. Explicit Deferrals

The following are intentionally deferred:

  • optimizer pass catalog and tuning policy,
  • richer internal IR analysis models beyond v1 obligations,
  • advanced closure/lifetime optimizations beyond v1 constraints,
  • and executable support for SPAWN/YIELD in core-v1.

13. Non-Goals

  • Defining one in-memory IR implementation graph.
  • Redefining runtime verifier/loader internals.
  • Defining PBX binary chunk policy in this document.

14. Exit Criteria

This document is healthy when:

  1. IRBackend -> IRVM obligations are explicit and testable,
  2. deterministic function/call/control-flow lowering rules are explicit,
  3. mandatory pre-verification boundary is explicit,
  4. common backend obligations are not described as owned by PBS,
  5. frontend-specific lowering specs only own emission into IRBackend,
  6. and scope boundaries with optimization/emission specs are explicit.