Runtime Interface (PLRI) & Native ABI
Relevant source files
The following files were used as context for generating this wiki page:
- architecture/08.1-closed-design-questions.md
- crates/tools/architecture-tests/src/tests.rs
- architecture/19-architecture-conformance-and-regression-policy.md
- AGENTS.md
- architecture/decisions/0028-toolchain-distribution-and-popup-management.md
- crates/libraries/README.md
- architecture/decisions/0018-rust-workspace-and-crate-boundaries.md
The Pop Lang Runtime Interface (PLRI) serves as a versioned, backend-neutral contract between generated code and runtime services. It ensures that native and future virtual machine (VM) backends preserve identical object, initialization, visibility, metadata, error, and garbage collection (GC) semantics. The PLRI facilitates communication between the compiler backends (such as LLVM or a MIR interpreter) and the underlying runtime implementation.
The Native ABI defines the low-level vocabulary and calling conventions used by the runtime. It encompasses the precise concurrent generational GC model, which includes a moving nursery, safe points, and generational barriers. This architectural boundary allows for a modular runtime ownership structure where the PLRI interface remains an implementation-independent leaf contract, separate from the native host facade and the specific collector implementation.
Sources: AGENTS.md:329-335, crates/tools/architecture-tests/src/tests.rs:434-440, architecture/decisions/0028-toolchain-distribution-and-popup-management.md:86-90
Architectural Components and Boundaries
The runtime is partitioned into focused ownership crates to maintain strict dependency directions and prevent implementation details from leaking into portable layers.
This diagram illustrates the dependency flow between the compiler backend and the modular runtime crates.
Component Responsibilities
| Crate | Responsibility | Constraints |
|---|---|---|
pop-runtime-interface | Implementation-independent leaf contract (PLRI). | Must not contain native details like extern "C" or std::ffi. |
pop-runtime-native-abi | Defines pop_rt_ operations and native calling conventions. | Confined to native detail; does not depend on the collector. |
pop-runtime-collector | Portable GC implementation (precise concurrent generational). | Depends on PLRI; must not contain native detail or ABI dependencies. |
pop-runtime-native | The host facade linking all components. | Depends on interface, collector, and ABI. |
Sources: crates/tools/architecture-tests/src/tests.rs:440-520, architecture/decisions/0018-rust-workspace-and-crate-boundaries.md:35-45
Garbage Collection and Relocation
The Pop GC is a precise concurrent generational collector. It features a moving nursery and a mostly non-moving mature heap. The PLRI defines the contract for relocating managed references, which is critical for supporting a moving collector.
Relocation Conformance
The PLRI enforces a relocation contract through specific IDs and publication mechanisms.
- SchedulerId: A canonical identity for scheduler workers.
- TaskFrameRootId: Identifies roots within task frames.
- RootPublication: A mechanism for safe-point relocation conformance.
Sequence showing the interaction during a GC relocation event.
Sources: crates/tools/architecture-tests/src/tests.rs:445-470, architecture/08.1-closed-design-questions.md:155-165, architecture/19-architecture-conformance-and-regression-policy.md:144-150
Native ABI Versioning and Coexistence
The Native ABI supports multiple versions to allow for architectural evolution while maintaining stability. ABI versions define safe-point behaviors and root management strategies.
ABI Version Characteristics
- ABI 1.x: Uses stable roots; cannot silently satisfy the moving nursery profile required by higher versions.
- ABI 1.12: Introduces compiler-created task frames and separates cancellation source authority.
- ABI 2.0: Implements
pop_rt_gc_safe_point_v2with mandatory post-call LLVM reload aliases and writable root arrays.
Sources: architecture/08.1-closed-design-questions.md:215-230, architecture/19-architecture-conformance-and-regression-policy.md:165-170
Foundation Library Integration
The toolchain provides two foundational Bubbles that interact with the PLRI: Pop.Internal and Pop.Standard. Native-backed adapters in these libraries use a specific macro to bridge Pop declarations to native code.
The #[poplib] Attribute
Native transitions for Standard or Internal functions are managed through the #[poplib] attribute. This ensures that the Rust ABI matches the accepted Pop metadata without requiring runtime registration or string-based dispatch.
#[poplib(
bubble = Standard,
namespace = "Pop",
name = "print",
parameters(Int),
results(),
effects(AmbientIo),
)]
pub extern "C" fn pop_std_print_int(value: i64) {
// Native implementation logic
}
Example of a native adapter using the poplib attribute to bind to the PLRI.
Sources: crates/libraries/README.md:129-145, architecture/08.1-closed-design-questions.md:205-212, crates/tools/architecture-tests/src/tests.rs:645-660
Conformance and Safety
Architecture conformance is a binding baseline. The PLRI and Native ABI are protected against "Lua regressions"—the accidental reintroduction of dynamic, table-centered semantics.
Key invariants include:
- Precise Roots: Native stack frames and runtime structures must use precise roots and stack maps.
- Backend Neutrality: Canonical MIR describes runtime operations without being tied to a specific backend like LLVM.
- Identity Stability: While physical tokens (references) may change due to GC relocation, Pop object identity remains stable.
Sources: architecture/19-architecture-conformance-and-regression-policy.md:10-25, architecture/08.1-closed-design-questions.md:215-220, AGENTS.md:330-335