Skip to content

Complete native stable generational GC and retained-heap fast paths - #24

Merged
kleeedolinux merged 29 commits into
poplanguage:masterfrom
kleeedolinux:master
Jul 14, 2026
Merged

Complete native stable generational GC and retained-heap fast paths#24
kleeedolinux merged 29 commits into
poplanguage:masterfrom
kleeedolinux:master

Conversation

@kleeedolinux

Copy link
Copy Markdown
Collaborator

Summary

Replace the native bootstrap collector with Pop Lang’s stable-token generational
collector and make real native executables exercise mature allocation, SATB
marking, bounded sweeping, memory pacing, page management, and precise object
storage.

This PR delivers the complete ABI 1 stable-generational conformance slice:

  • removes BootstrapRuntime from native program execution;
  • introduces NativeStableGenerationalConformance;
  • keeps ABI 1 managed tokens stable by placing native allocations directly in
    non-moving mature, large, or pinned domains;
  • preserves the ABI 2 requirement for moving nursery allocation and native root
    relocation;
  • adds ABI 1.11 atomic initialized-object allocation;
  • lowers LLVM class and record construction to one complete allocation instead
    of allocation followed by repeated field calls;
  • validates complete payloads and precise pointer maps before publication;
  • keeps ordinary later mutations on the checked barrier path;
  • integrates incremental SATB mature marking and bounded sweeping with native
    safe points;
  • batches empty-page reclamation and maintains cached committed-byte accounting;
  • uses scheduler/layout-indexed active mature pages with a mutator-local cursor;
  • initializes managed arrays before publication;
  • specializes stable-stage barriers where nursery edges are impossible;
  • stores small object payloads inline;
  • represents every logical payload slot with one physical machine word;
  • interprets slot contents exclusively through precise object maps;
  • uses deterministic segmented token directories for object and placement
    metadata;
  • derives directory tokens from their segment coordinates instead of storing a
    duplicate token beside every entry;
  • classifies homogeneous array stores in constant time;
  • preserves exact scalar-versus-reference behavior even when scalar bits equal
    a valid managed token;
  • adds the retained objectArray workload and checksum validation;
  • refreshes the benchmark JSON and HTML results;
  • documents the remaining heap bottlenecks in ROADMAP.md.

The implementation does not scalar-replace or fuse the retained managed graph.
objectArray still creates 200,000 distinct managed objects, retains them
through a managed-reference array, reads every array element and object field,
and validates the 20000100000 checksum.

The branch also adds LIST.md, a documentation-only inventory of proposed
first-party Pop Lang batteries. It does not change GC or language semantics.

Performance

The original native retained-object path measured approximately 408 ms on the
development host.

Successive changes in this PR reduced the checksum-validated objectArray
workload to:

  • approximately 38.0 ms after initialized allocation, active mature pages,
    barrier specialization, and segmented metadata;
  • 34.223 ms median across 50 measured samples after compact one-word payloads
    and token-derived directory entries;
  • 4.896 ms for Go in the same 50-sample comparison.

The checked-in broad benchmark snapshot reports:

  • Pop Lang allocationChurn: approximately 0.770 ms;
  • Pop Lang objectArray: approximately 32.145 ms.

That snapshot contains one sample per runtime and is published as dashboard
data, not used as the primary regression claim. Performance numbers are
machine-local evidence rather than portable language guarantees.

Architecture traceability

  • Authorizing architecture section or ADR:
    • architecture/15-garbage-collector-architecture.md
      • page-described objects;
      • thread-local allocation buffers;
      • publication safety;
      • SATB marking;
      • page-centric allocation and marking;
      • side metadata;
      • barrier specialization;
      • LLVM allocation and barrier fast paths;
      • production allocation performance gates.
    • ADR 0008: concurrent generational garbage collector.
    • ADR 0038: modular portable runtime implementation.
    • ADR 0039: relocating nursery root and backend contract.
    • ADR 0059: native stable-token generational transition.
    • ADR 0060: atomic initialized-object allocation.
  • New or changed public contract:
    • native ABI advances to version 1.11;
    • adds the closed AllocateObjectInitialized PLRI operation;
    • adds pop_rt_allocate_initialized_object;
    • adds NativeStableGenerationalConformance;
    • native ABI 1 execution now uses stable mature generational allocation rather
      than the bootstrap collector;
    • no Pop Lang source syntax or language semantics change.
  • Architecture documents, examples, or terminology updated:
    • runtime and ABI architecture;
    • implementation roadmaps;
    • garbage collector architecture;
    • ADR 0059 and ADR 0060;
    • collector, native runtime, and native ABI contributor documentation;
    • benchmark documentation;
    • root ROADMAP.md, including concrete remaining heap problems.

Verification

  • Tests were added or updated before implementation where behavior changed.
  • Positive behavior is covered.
  • Negative/rejection boundaries are covered.
  • Convention, consistency, and regression coverage is present where relevant.
  • Cross-backend or differential coverage is present where relevant.
  • cargo fmt --all -- --check
  • cargo check --workspace --all-targets
  • cargo test --workspace --all-targets
  • cargo clippy --workspace --all-targets -- -D warnings

Additional completed verification:

  • full pop-runtime-collector test suite;
  • 21 native ABI tests;
  • native stable-generational allocation, collection, roots, pins, strings,
    tables, lists, ranges, and iteration tests;
  • LLVM lowering and executable initialized-object coverage;
  • collector strict clippy with -D warnings;
  • 15 architecture-regression tests;
  • benchmark harness tests;
  • changed-document relative-link validation;
  • checksum validation before benchmark warmups and samples;
  • rebuilt release native runtime before final measurements;
  • 50-sample objectArray Pop Lang/Go comparison.

If a check was not run, explain why:

The final workspace-wide test and workspace-wide clippy commands were started
but interrupted before completion. Focused collector, native ABI, LLVM,
architecture, benchmark, formatting, workspace checking, and collector clippy
verification passed. CI should run the two unchecked workspace-wide commands.

Review notes

  • No dynamic typing, runtime string lookup, broad reflection, or universal-table behavior was introduced.
  • HIR/MIR remain backend-neutral.
  • No generated artifacts, dependency caches, credentials, or editor files are included.
  • This is ready for technical review.

The tracked benchmark JSON and HTML reports are intentionally refreshed
benchmark artifacts. No build output, dependency cache, credentials, or editor
state is included. The untracked popbook/ directory is not part of this PR.

Compatibility

Newly generated ABI 1.11 programs require a runtime archive that exports
pop_rt_allocate_initialized_object.

Already-generated ABI 1.10 programs remain compatible with their corresponding
ABI 1.10 runtime. This PR does not silently select moving native allocation for
ABI 1 code.

Known limitations

This PR completes the stable native generational/conformance stage. It does not
claim that ProductionConcurrentGenerational is complete.

Remaining problems are recorded in ROADMAP.md:

  • heap changes are not yet guarded by one mandatory combined churn/retained
    performance gate;
  • logical pages still do not own physical object payloads;
  • allocation and access still pass through a process-global runtime mutex;
  • allocation layouts are rebuilt or cloned at runtime instead of using static
    compiler-emitted descriptors;
  • reference stores still carry avoidable generic barrier overhead;
  • ABI 1 cannot rewrite and reload relocated native roots;
  • native scheduler and concurrent mature-collector integration remain
    incomplete;
  • retained-object performance remains above the staged 25 ms and 12 ms targets.

Moving nursery allocation and selective evacuation remain disabled for native
ABI 1. They may be enabled only after ABI 2 writable-root reloads, forced native
relocation, stale-token rejection, stack/register/coroutine/unwind/FFI coverage,
and cross-backend tests pass.

The current worker path performs bounded parallel collector work but joins each
slice before returning to the mutator. It is not yet fully mutator-concurrent
collection.

The experimental C backend remains outside the production GC parity target.

Admit object, array, table, and pin placement against committed pages
before mutating the nursery. Keep emergency and evacuation reserves
inside the hard limit and include typed non-heap usage in admission.

Drive adaptive collection targets, bounded mature-cycle assists, empty
page return, deterministic out-of-memory failures, and saturating
pressure, debt, domain, and reserve telemetry. Keep the runtime labeled
as conformance-only until concurrent workers and relocating backends land.
Track typed mutator execution states and require exact once-only state\npublication before a collector phase can advance. Keep heap-sized work out\nof the coordinator and expose deterministic transition telemetry.\n\nDocument the standalone coordinator separately from the remaining scheduler\nintegration and background worker work.
Run exact object-map scans on persistent host worker threads and return\nresults in deterministic sequence order before collector-owned mutation. Use\nbounded per-worker queues, dispatch sweep work, expose telemetry, and join all\nthreads during shutdown.\n\nKeep the runtime profile at relocation conformance until epochs, workers, and\nnative scheduler transitions are integrated for true mutator concurrency.
Scan immutable mature-card snapshots on the bounded worker pool before a\nminor evacuation. Install the exact young-reference result as collecting\nsafe-point roots so relocation preserves mature-to-young reachability without\nrescanning those cards on the collector owner.\n\nKeep concurrent mutator refinement open; this slice is parallel only inside\nthe already collecting safe point.
Replace the heap-sized unreachable-object inventory at the mark/sweep\ntransition with an ordered cursor that examines at most the configured work\nbudget per slice. Treat mature allocations during sweeping as live for the\nactive cycle and retain deterministic worker dispatch.\n\nThis removes full-heap transition work without claiming concurrent page\nreclamation.
Track scheduler-local, isolated, and shared ownership independently from\ngeneration, placement, allocation class, and pin state. Publish complete local\ngraphs transactionally into shared ownership and reject shared-to-local edges\nbefore any barrier or heap mutation.\n\nPreserve pinned and large-object placement as distinct memory-domain facts and\nleave isolated transfer and scheduler-indexed heaps for their own contracts.
Construct isolated regions only after proving one external owner and\nrejecting other handles, pins, stack roots, and incoming object edges. Keep\nisolated placement and accounting distinct, protect the owner capability, and\ntransfer scheduler ownership without copying object identity or graph edges.\n\nProvide explicit dissolution back to scheduler-local mature ownership so region\nlifetimes and owner handles do not become permanent leaks.
Give each scheduler independent TLAB cursors, page ownership metadata, minor\nrequests, and nursery evacuation scope. Preserve other schedulers' young\nobjects and tokens during a local collection and reject direct local edges\nacross scheduler ownership domains.\n\nKeep parallel scheduler execution and parallel evacuation as separate remaining\nproduction work.
Allocate scheduler-owned arena objects through typed bump storage with\ndisjoint scalar, same-arena, and managed-reference slots. Keep managed targets\nas precise relocating roots, reject cross-arena and cross-scheduler edges, and\nbulk-release all objects and roots on close.\n\nAccount arena bytes against the global hard limit before mutation and expose\nlifecycle, allocation, peak-byte, and bulk-reclamation telemetry.
ADR 0057 accepts Actor as a standard/platform root and Cluster as an
optional official Package, but the canonical public inventory and catalog
still omitted both names. This left the completed standard-foundation
roadmap inconsistent with its architecture conformance snapshot.

Record both roots in the owning catalog and implementation phase while
keeping them planned and outside the frozen prelude and API baseline.
Scanning a large pointer array as one mark work item could monopolize a
safe point or collector worker. Worker marking also cloned each complete
allocation even when only a bounded part of its layout was needed.

Split pointer-dense layouts into precise slot-range continuations. Schedule
only one continuation at a time, interleave it with ordinary mark work, and
send workers only the selected slot values. Preserve SATB and post-scan
barriers across chunk boundaries and skip field tracing for pointer-free
large objects.

Expose cumulative chunk telemetry and keep the implementation roadmaps in
sync with the verified collector behavior.
Selective evacuation needs exact pin metadata, but the generational
runtime only retained opaque pin roots. It could not distinguish multiple
handles for one object or report how long a pin constrained relocation.

Track scoped handles and unique pinned objects separately. Measure active
and completed lifetimes in deterministic safe-point units and report each
long-lived handle once through runtime-private telemetry. Preflight the pin
token and placement without work proportional to heap size, and keep failed
pin or unpin operations telemetry-atomic.

Update the collector and implementation roadmaps with the verified pinning
boundary.
Reject noncanonical compatibility identities, namespace roots, prelude
tier mismatches, and documentation path traversal before the frozen
Standard baseline reaches name resolution. Bound total input, entry
count, and individual rows so malformed metadata cannot consume
unbounded work.

Record the completed ADR 0058 consistency gate in the release roadmap
and bootstrap documentation.
Track physical regions independently from page numbering so allocation domains
and scheduler-local owners cannot mix. Report exact live, committed,
fragmented, pinned, and reference-slot facts while shared regions follow
the mature mark and sweep lifecycle.

Choose only positive-benefit shared candidates within a fixed region bound
and the protected evacuation reserve. Keep selected regions out of
allocation pools and exclude pinned and large-object spaces before later
forwarding and relocation work.
Copy every object in a selected shared region into compact monomorphic destination pages and rewrite precise fields, stack roots, strong handles, and card metadata before invalidating stale tokens.\n\nStage placement and heap updates together so malformed roots or reserve exhaustion cannot expose a partial relocation. Retire source regions through quarantine and record peak committed memory for the evacuation slice.
Avoid constructing root publications at native safe points when no collection is pending while still validating every published handle. Materialize bulk-initialized arrays in one allocation pass instead of zeroing and filling the same storage separately.\n\nRecord the checksum-equivalent churn and retained-object host baselines in the roadmap. The measurements identify repeated native ABI locking and handle access as the next optimization target.
Attach the persistent bounded worker pool after runtime configuration and use it to rewrite internal references in collector-staged evacuation copies. Preserve deterministic result ordering and keep the final external-edge, root, handle, card, and placement update failure-atomic on the collector.\n\nRecord worker evacuation telemetry and reject duplicate worker-pool attachment so custom runtimes can enable the same bounded path safely.
Capture the host-only Pop and Go checkpoint immediately after the evacuation worker slice. Record the rejected bootstrap access experiment as well so later optimization work targets the native ABI and storage boundary instead of repeating a measured regression.
Replace isolated worker channels with bounded owner-FIFO queues that let idle collectors steal from a peer's opposite end. Keep queue locks independent so marking, card refinement, sweeping, and evacuation do not serialize on one scheduling mutex.\n\nContinue to sort completed jobs by submission sequence before collector-owned mutation, report completed steals, and join every worker during shutdown.
Integrate the typed epoch coordinator with the generational runtime so a requested major cycle cannot trace or dispatch worker jobs before every registered managed mutator publishes a validated precise-root snapshot. Defer nursery relocation while the handshake retains physical tokens and record the completed boundary in the implementation roadmaps.
Replace the native bootstrap composition with the ABI 1 stable-token
generational collector authorized by ADR 0059. Keep nursery movement and
evacuation gated on writable roots in ABI 2.

Batch mature-page reclamation, index active allocation pages, and bulk
initialize scalar arrays. Scalar-replace safe read-only loop-local arrays
so allocation-churn programs do not pay for unobservable heap objects.
Publish class and record payloads atomically through native ABI 1.11. This removes initializer barrier calls while preserving precise maps and failure-atomic publication.\n\nAdopt span-local cursors, cached memory accounting, inline small payloads, and deterministic arena-indexed token metadata. These changes remove ordered-map scans and per-object payload allocations from the retained-object fast path without scalar-replacing the managed graph.\n\nThe checksum-validated objectArray median falls from about 408 ms to 38 ms on the development host. Direct page-backed access and inline barriers remain production performance work.
Store each logical payload slot as one untagged machine word and let the exact object map define its interpretation. This preserves precise tracing while halving retained slot storage.

Derive managed tokens from segmented directory coordinates instead of storing them beside every entry. Classify homogeneous array stores from their allocation descriptor so managed arrays keep constant-time checked access.

This reduces the checksum-validated retained-object median from about 38.0 ms to 34.223 ms on the development host, while leaving direct page-backed access and common-path mutex removal as the next optimization boundary.
@kleeedolinux
kleeedolinux merged commit a4d7cfc into poplanguage:master Jul 14, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant