- 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
39 lines
2.2 KiB
Markdown
39 lines
2.2 KiB
Markdown
# 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).
|