Repository navigation
codegen: emit typedwasm.access-sites carrier (typed-wasm ADR-0003) #251
Description
Context
typed-wasm proposals 0001 + 0002 reached `[accepted]` 2026-05-30 and were
promoted to typed-wasm ADR-0002 (multi-producer carrier sections) +
ADR-0003 (`typedwasm.access-sites` access-site carrier).
The access-site carrier maps each typed memory load/store instruction's
`(function_index, instruction_byte_offset)` to the resolved
`(region_id, field_id)` it intended. Without it the verifier can
re-derive regions/fields exist (via `typedwasm.regions`) but cannot
re-check that any given `i32.load offset=N` was supposed to target a
specific declared field.
`ephapax-wasm` already emits `typedwasm.ownership` (lib.rs ~line 1277;
custom-section assembly ~line 4577). The access-sites carrier is the
producer-side counterpart to typed-wasm PR #109's
`verify_access_sites_from_module` pass.
The AffineScript counterpart is tracked at affinescript#462. Filing
this issue per typed-wasm proposal 0001 Appendix B
"Producer-readiness checklist" — both shipping producers need
access-sites emission for v1 stabilisation.
(This issue was originally classifier-blocked in an earlier session;
owner directed filing now.)
What needs to land
Encoder (~25 LOC)
A new helper in the same module that hosts the `typedwasm.ownership`
encoder (currently inline in `src/ephapax-wasm/src/lib.rs` around line
1277). Wire format per typed-wasm proposal 0002 §"Wire format":
```
u16le version (= ACCESS_SITES_VERSION = 1)
u32le entry_count
for each entry (strictly ordered by (func_idx, byte_offset)):
u32le func_idx
u32le instruction_byte_offset
u32le region_id
u32le field_id
```
Encoding option B (LEB128 per field) per typed-wasm proposal 0002
§"Wire format" measurement (~5B/access, ~1.1% module overhead).
Custom-section assembly (~5 LOC)
Add the new section to the custom-sections list at
`src/ephapax-wasm/src/lib.rs` line ~4577 alongside
`typedwasm.ownership`, gated by "any typed access emitted".
Blocker: threading `(region_id, field_id)` from typecheck to codegen
This is the bulk of the work. Today the IR/codegen pipeline has typed
access at the type level — `ephapax-typing` knows the resolved
`(region, field)` at typecheck time, but by the time codegen emits
`i32.load offset=N` / `i32.store offset=N` (~lines 1351–1475 in
lib.rs), the semantic `(region, field)` annotation is gone.
To emit access-sites, codegen needs `(region_id, field_id)` available
at every emission site. Three options:
- IR annotation — add resolved `(region_id, field_id)` to the
typed-access IR node, populated by `ephapax-typing`, consumed by
`ephapax-wasm`. Likely cleanest. - Side-table — `ephapax-typing` produces a `Map<expr_id,
(region_id, field_id)>` that `ephapax-wasm` consults during
emission. Less invasive to the IR shape. - Re-resolve at codegen — duplicate typechecker resolution
logic in codegen. Code smell; avoid unless options 1/2 are
blocked.
Test coverage
New isolation test mirroring `typedwasm.ownership` round-trip
coverage (the existing test pattern in
`src/ephapax-wasm/tests/` covers ownership; the access-sites version
should construct a module, emit, verify parse+lookup).
Complexity assessment
Multi-file refactor, blocked on the typecheck → codegen
threading. The encoder + custom-section assembly is a single-day
slice; the threading is the bulk of the work and touches
`src/ephapax-typing/` (or wherever the resolution happens) +
`src/ephapax-wasm/src/lib.rs` field-emission sites.
Related
- typed-wasm ADR-0003: `docs/decisions/0003-access-site-carrier.adoc`
- typed-wasm proposal 0002 (canonical wire spec):
`docs/proposals/0002-access-site-carrier.adoc` - typed-wasm PR Wip/v2 grammar 2026 05 16 #109 — verifier pass `verify_access_sites_from_module`
- affinescript#462 — AffineScript counterpart
- ephapax PR cleanup(codegen): remove dead Codegen fields region_stack + bump_ptr (+ orphaned RegionInfo) #250 — dead-code cleanup (separate item from same
Appendix B checklist)
Activity
hyperpolymath commentedon May 30, 2026
Handoff brief now available
Self-contained handoff brief for this work landed at typed-wasm
docs/developer/handoff-typed-access-sites-codegen.adoc(filed via typed-wasm#122). Covers both this issue and the AffineScript counterpart affinescript#462, since the blocker (typecheck → codegen threading of(region_id, field_id)) is shared.The brief includes:
src/ephapax-wasm/src/lib.rs~lines 1277 + 4577)Direct link: https://github.com/hyperpolymath/typed-wasm/blob/main/docs/developer/handoff-typed-access-sites-codegen.adoc
hyperpolymath commentedon May 30, 2026
Design analysis (Claude, 2026-05-31)
Mapped the producer-side pipeline and read the typed-wasm
handoff brief
(
docs/developer/handoff-typed-access-sites-codegen.adoc)before starting implementation. Two scope items that warrant
owner confirmation before charging in:
1. Threading-option choice is owner-gated AND cross-repo
The handoff explicitly says: "The owner has not selected
between 1 / 2 / 3...propose one and confirm before
implementing" and "Both producers must adopt the SAME
threading option...Do not let one repo ship IR-annotation
while the other ships side-table; the carrier wire format is
shared so the producer-internal mechanism should be too where
reasonable."
So this decision lives across ephapax#251 + affinescript#462
and needs to be made once, not twice.
My recommendation: option 1 (IR annotation).
— no second pass walking a side-table, no risk of side-table
vs IR drift.
(region_id, field_id)becomes part ofthe typed-IR contract, so any future codegen path (e.g. a
cranelift backend, or a future Ephapax → Idris2 ABI emitter)
inherits the annotation automatically.
exists (today:
TupleIndex { tuple, index },StringNew,and
Derefof aBorrow; future: any record/fieldprojection once records land); plumbing through the
typechecker is one populate-site per access kind.
Option 2 (side-table) would be tempting if the AST were
externally stable (it isn't), or if the typechecker output were
already pluggable (it isn't — typechecker writes back into the
same AST). Option 3 (re-resolve at codegen) the handoff
explicitly flags as a code smell.
2. Companion
typedwasm.regionsis a prerequisiteEphapax emits
typedwasm.ownershiptoday(
src/ephapax-wasm/src/lib.rs~1277 + ~4577) but NOTtypedwasm.regions. The verifier rejects access-sites withoutthe companion regions section (
MissingDependentCarrier,AccessSiteError::MissingDependentCarrieratcrates/typed-wasm-verify/src/lib.rs:180).So #251 should either:
typedwasm.regions(region schema = list of field declarations per region) as a
prerequisite, OR
#251aregions-carrieremission,
#251b(this one, renamed) access-sites emission.I'd recommend (b) — separate concerns, separate PRs,
threading-option decision is shared between them, regions
carrier ships first because access-sites depends on it for
verifier acceptance.
3. Field IDs must be cross-module schema-stable
Handoff §"Coordination notes" item 3: "If the typechecker
assigns field IDs from a per-module counter, the cross-module
case will fail. Confirm the ID scheme is shared-schema-aware
before relying on it."
Ephapax doesn't yet have cross-module imports, but the ID
assignment scheme baked in now will outlive that constraint.
Recommend: stable hash of
(region_name, field_name, field_ty_serialized)rather than a per-module counter, socross-module reuse "just works" when imports land (Phase 0
Track A region-decl grammar v0 just shipped — this becomes
relevant soon).
Proposed next step
Owner decides:
parity);
#251afor regionscarrier as prerequisite, keep
#251for access-sites);stability).
Then I'll pick up implementation. Parking the in-flight
implementation branch until these are confirmed.
feat(codegen): emit typedwasm.regions + access-sites carriers (closes #…
test(seam): cross-verifier conformance for the access-sites carrier (#…