Files
hakorune/docs/RESOLVER_API.md
Selfhosting Dev 2a9aa5368d harness(llvm/py): fix PHI/dominance via Resolver-only; per-pred localization and constant GEPs; stabilize Main.esc_json/1, dirname/1, node_json/3; docs: add NYASH_LLVM_TRACE_FINAL and Resolver-only invariants
- Resolver-only reads across BBs; remove vmap fallbacks
- Create PHIs at block start; insert casts in preds before terminators
- Re-materialize int in preds to satisfy dominance (add/zext/trunc)
- Use constant GEP for method strings to avoid order dependency
- Order non-PHI lowering to preserve producer→consumer dominance
- Update docs: RESOLVER_API.md, LLVM_HARNESS.md
- compare_harness_on_off: ON/OFF exits match; linking green
2025-09-13 19:49:03 +09:00

2.2 KiB

Resolver API (Minimal i64 Prototype)

Goals

  • Centralize "ValueId → current-block value" resolution.
  • Guarantee dominance by localizing values at the start of the block (before non-PHI).
  • De-duplicate per (block, value) to avoid redundant PHIs/casts.

Design

  • Resolver-only reads: lowerers must fetch cross-block values via Resolver (ban direct vmap.get for reads across BBs).
  • Resolver keeps small per-function caches keyed by (BasicBlockId, ValueId).
  • resolve_i64(...) returns an i64-typed value, inserting a PHI at the beginning of the current block and wiring incoming from predecessor end-snapshots (sealed SSA). Any required casts are inserted in predecessor blocks just before their terminators to preserve dominance.
  • resolve_ptr(...) returns an i8* value localized to the current block; integer handles are bridged via inttoptr.
  • resolve_f64(...) returns an f64 value localized to the current block; ints bridged via sitofp.
  • Internally uses sealed snapshots (block_end_values) to avoid referencing values that do not dominate the use.

Usage (planned wiring)

  • Create let mut resolver = instructions::Resolver::new(); at function lowering start.
  • Replace all integer value fetches in lowerers with resolver.resolve_i64(...).
  • Keep builder insertion discipline via BuilderCursor.

Ban: Direct vmap.get(..) for cross-BB reads

  • Direct reads from vmap are allowed only for values defined in the same block (after they are created).
  • For any value that may come from a predecessor, always go through Resolver.
  • CI guard: keep rg "vmap\.get\(" src/backend/llvm at zero for instruction paths (Resolver-only).

Next

  • Migrate remaining localize_to_i64 call sites to the resolver.
  • Enforce vmap direct access ban in lowerers (Resolver-only for reads).

Tracing

  • NYASH_LLVM_TRACE_PHI=1: log PHI creation/wiring in the Rust/inkwell path.
  • NYASH_LLVM_TRACE_FINAL=1: in the Python/llvmlite harness, trace selected final calls (e.g., Main.node_json/3, Main.esc_json/1) to correlate ON/OFF outputs during parity checks.

Acceptance tie-in

  • Combined with LoopForm: dispatch-only PHI + resolver-based value access → dominance violations drop to zero (A2.5).