Skip to content

docs: GC soundness proof — ref counting is complete for IC#42

Open
gavlooth wants to merge 6 commits intoHigherOrderCO:mainfrom
gavlooth:docs/gc-soundness-proof
Open

docs: GC soundness proof — ref counting is complete for IC#42
gavlooth wants to merge 6 commits intoHigherOrderCO:mainfrom
gavlooth:docs/gc-soundness-proof

Conversation

@gavlooth
Copy link
Copy Markdown

Summary

This PR adds a formal argument proving that reference counting alone is sufficient for complete garbage collection in HVM4, without cycle detection or tracing GC.

Key Points

  1. DAG Invariant: The heap is always a DAG because nodes can only reference previously-allocated nodes (monotonic allocation order)

  2. No Cycles Possible: A cycle A₁ → A₂ → ... → A₁ would require t(A₁) > t(A₁), a contradiction

  3. Recursion is Unfolding: Recursive definitions like @Y create infinite chains, not circular references

  4. DUP/SUP is Acyclic: Superposition nodes reference the original (older) computation

Implications

  • Ref counting + epoch batching = complete, pauseless GC
  • No mark phase, no tracing, no stop-the-world
  • Memory management falls out of the IC reduction rules

Files Changed

  • docs/GC_SOUNDNESS.md: Full proof with lemmas, theorem, and discussion of edge cases (recursion, DUP/SUP, FFI)

Introduces two features that enable iterative HVM4 programs (like
Bellman-Ford with early termination) to run without exhausting heap:

1. @compact(term): Normalizes term to SNF, then deep-copies the result
   tree to fresh heap positions. Safe to call from any evaluation context
   (inside WNF, nested in expressions) because it never modifies existing
   heap data. Enables iterative algorithms to shed accumulated evaluation
   intermediates between rounds.

2. Per-thread, size-segregated free lists (sizes 1-16): Freed heap blocks
   are recycled via LIFO free lists, checked first by heap_alloc before
   bump-allocating. heap_free calls added to all interaction handlers
   (APP-LAM, DUP-NOD, DUP-SUP, MAT-CTR, MAT-NUM, OP2, EQL, etc.).
   Disabled in multi-threaded mode to avoid cross-thread races.

Also includes OP2-NUM fast path (skip frame push when both operands are
already NUM) and dup_nod split allocation (3 separate allocs instead of
one block, enabling free-list reuse of smaller sizes).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Analyze BOOK terms after parsing to compute program hints (node count,
SUP/DUP presence, max depth, arity stats). Use hints to right-size all
runtime buffers instead of hardcoded compile-time capacities — reducing
memory footprint from GBs to KBs for small programs while still
handling large ones via dynamic growth.

Key changes:
- analyze/hints.c: O(N) post-parse analysis producing HvmHints
- data/wsq.c: resizable Chase-Lev deque (WsqArray indirection, 2x growth)
- data/wspq.c: dynamic bracket count and per-bucket capacity
- data/uset.c: sized initialization from hints instead of HEAP_CAP
- data/elastic_ring.c: ouroboros double-map ring buffer with elastic
  growth/shrink via memfd_create (Linux) / shm_open (macOS)
- eval/normalize.c, eval/collapse.c, cnf/_.c: hint-based buffer sizing
- main.c: -v flag for hints output, --test-ring for ring self-test

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Standalone data structure for future Hybrid SIV + Ring work queues.
Dense swap-compacted array with stable IDs, separate data/ID capacity
growth, and O(1) push/erase/valid/get operations. Self-test via
--test-siv flag.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Leverage fd data persistence across ftruncate+remap: on growth, existing
data at fd offsets [0, old_cap) survives the extend. Only the wrapped
prefix (if any) needs a memcpy within the mapping. On shrink, compact
live data to offset 0 with a single memmove before truncating.

Removes the malloc/free allocation overhead from the resize hot path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
elastic_ring.c:
- Update header comment to reflect zero-copy growth (was stale)
- Add thread-safety notes for future MT integration
- Document byte-level alignment contract
- Enforce cap/4 threshold in ring_shrink (was documented but not checked)
- Add tests: shrink with wrapped data, cap/4 threshold rejection,
  grow when empty, direct double-map mirror verification

siv.c:
- Document u32 ID space exhaustion (4B push limit)
- Add thread-safety contract for SIV+Ring thief pattern
  (release/acquire barriers, erase-during-read race)
- Add tests: garbage ID rejection (SIV_INVALID, near-max, beyond
  next_id, erased), independent id_cap vs data_cap growth across
  3 push-erase rounds, erase-last-element (no-swap path)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

2 participants