Skip to content

codegen: emit typedwasm.access-sites carrier (typed-wasm ADR-0003) #251

Closed

Description

@hyperpolymath

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:

  1. IR annotation — add resolved `(region_id, field_id)` to the
    typed-access IR node, populated by `ephapax-typing`, consumed by
    `ephapax-wasm`. Likely cleanest.
  2. 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.
  3. 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

Activity

  1. hyperpolymath commented on May 30, 2026

    @hyperpolymath
    OwnerAuthor

    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:

    • Three threading options (IR annotation / side-table / re-resolve at codegen) with trade-offs
    • Encoder spec (~25 LOC, mechanical, per ADR-0003 wire format)
    • Assembly site references in both producers (Ephapax: src/ephapax-wasm/src/lib.rs ~lines 1277 + 4577)
    • Coordination note: both producers should adopt the SAME threading option for parity
    • Sequencing recommendation: decide threading with owner first, prototype in one repo (Ephapax recommended for faster lint-driven iteration), mirror in the other

    Direct link: https://github.com/hyperpolymath/typed-wasm/blob/main/docs/developer/handoff-typed-access-sites-codegen.adoc

  2. hyperpolymath commented on May 30, 2026

    @hyperpolymath
    OwnerAuthor

    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).

    • Elegance: keeps the resolution + the consumer in lockstep
      — no second pass walking a side-table, no risk of side-table
      vs IR drift.
    • Correctness: the (region_id, field_id) becomes part of
      the typed-IR contract, so any future codegen path (e.g. a
      cranelift backend, or a future Ephapax → Idris2 ABI emitter)
      inherits the annotation automatically.
    • Cost: one new field on whatever typed-access IR node
      exists (today: TupleIndex { tuple, index }, StringNew,
      and Deref of a Borrow; future: any record/field
      projection 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.regions is a prerequisite

    Ephapax emits typedwasm.ownership today
    (src/ephapax-wasm/src/lib.rs ~1277 + ~4577) but NOT
    typedwasm.regions. The verifier rejects access-sites without
    the companion regions section (MissingDependentCarrier,
    AccessSiteError::MissingDependentCarrier at
    crates/typed-wasm-verify/src/lib.rs:180).

    So #251 should either:

    • (a) scope-bump to include emitting typedwasm.regions
      (region schema = list of field declarations per region) as a
      prerequisite, OR
    • (b) split into companion issues: #251a regions-carrier
      emission, #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, so
    cross-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:

    • threading option (recommend 1, contingent on AffineScript
      parity);
    • scope split (recommend split: file #251a for regions
      carrier as prerequisite, keep #251 for access-sites);
    • field-ID scheme (recommend stable-hash for cross-module
      stability).

    Then I'll pick up implementation. Parking the in-flight
    implementation branch until these are confirmed.

  3. added 2 commits that reference this issue on Jul 7, 2026
    fc32791
    b5d49a7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions