Skip to content

codegen: emit typedwasm.access-sites carrier (typed-wasm proposal 0002 Gate 6) #462

@hyperpolymath

Description

@hyperpolymath

Context

typed-wasm proposal 0002 (access-site carrier) reached [review] 2026-05-30 via hyperpolymath/typed-wasm PR #104. The codec + verifier pass + spec doc landed in:

Promoting proposal 0002 to [accepted] requires §Gate 6: cross-repo codegen issues filed against producers. Reviewed and signed off via #449; this issue is the codegen counterpart.

Wire format

Per typed-wasm/docs/proposals/0002-access-site-carrier.adoc §"Wire format":

u16le         version                (= 1)
u32le_leb128  entry_count
for each entry (in producer-emission order):
    u32le_leb128  func_idx                       (index into wasm function section)
    u32le_leb128  instruction_byte_offset        (offset within function body bytecode;
                                                  FIRST byte of typed access opcode)
    u32le_leb128  region_id                      (foreign key into typedwasm.regions
                                                  region table)
    u32le_leb128  field_id                       (foreign key into that region's
                                                  field table)

Custom-section name: typedwasm.access-sites (companion to typedwasm.regions and typedwasm.ownership).

Producer obligations (proposal 0002 §"Producer obligations")

  1. Emit AFTER any post-codegen wasm rewrite (wasm-opt, snip, wasm-gc, custom passes). The instruction_byte_offset field is fragile — any pass that reorders, removes, or inserts instructions desyncs the offset space. Emitting before such a pass produces an unverifiable module; the verifier flags AccessSiteMisalignment.
  2. Companion typedwasm.regions is mandatory — the region_id / field_id fields are dangling pointers otherwise. v1 verifier rejects access-sites without regions with MissingDependentCarrier (now enforced by typed-wasm#109).
  3. Stable region indices across both sections (proposal 0001 §"Producer obligations" ¶2).
  4. All-or-nothing per module: entry for EVERY typed access in EVERY function, OR for NONE. Partial emission triggers AccessSiteUnbound on unlisted accesses.
  5. NOT emit entries for non-typed accesses (e.g., raw memory the producer chose to leave outside the type system). An entry is a producer assertion that the access IS typed.

Where this lands in affinescript

The AffineScript codegen pipeline emits typedwasm.ownership today at lib/codegen.ml (build_ownership_section, called at codegen.ml:~2778). The new section emit point is the same vicinity, alongside the regions/capabilities emit points to be added per proposal 0001 codegen issue:

  • New helper: build_access_sites_section : (int * int * int * int) list -> bytes (mirroring the existing build_section shape; LEB128 per field).
  • Hook into the codegen pass: every typed access (region.get $r .f, region.set $r .f, …) records its emission position. The hook needs the post-rewrite byte offset within the function body, so the access-list construction has to happen AFTER wasm-opt (or whatever final-pass lib/codegen.ml runs).
  • Emit typedwasm.access-sites AFTER typedwasm.regions in the section order (matching the verifier's two-section dependency, though section order is technically free).

Tw_section.encode dedup intersects here

#444 extracts Tw_section.Encode from the existing 2-copy build_section pattern in preparation for proposal 0001's regions + capabilities sections. Adding access-sites makes it 4 sections × 2 copies = 8 encoders without the dedup — even more reason to do #444 first, or fold this work into the same Tw_section.Encode module:

module Tw_section : sig
  module Encode : sig
    val ownership     : Tw_ownership_section.t      -> bytes
    val regions       : Tw_regions_section.t        -> bytes  (* from #444's prep *)
    val capabilities  : Tw_capabilities_section.t   -> bytes  (* from #444's prep *)
    val access_sites  : (int * int * int * int) list -> bytes  (* this issue *)
  end
  module Decode : sig
    (* symmetric *)
  end
end

Acceptance

  • Tw_section.Encode.access_sites and .Decode.access_sites implemented (round-trip property test passes).
  • Codegen pass emits typedwasm.access-sites AFTER post-codegen rewrite, with one entry per typed access.
  • Region IDs match typedwasm.regions (cross-section consistency).
  • All-or-nothing: a module either emits a complete access-sites section or omits the section entirely (the source-language toggle is producer-defined; recommend tying it to the --emit-typedwasm-regions flag).
  • End-to-end smoke: emit an .affine test program with multiple typed accesses; feed the resulting .wasm to typed-wasm-verify (with unstable-l2); confirm verify_access_sites_from_module returns Ok(vec![]).
  • Fixture: at least one .affine test that exercises access-sites alongside regions; commit the expected .wasm and typedwasm.access-sites bytes for regression detection.

Sequencing

This issue is OPEN but not blocking the typed-wasm [accepted] flip — per the proposal 0001 / 0002 acceptance criteria, "cross-repo codegen issues filed" is a Gate-6-satisfied state, not "codegen shipped." Sequence after #444 (encoder dedup) lands.

Related

  • typed-wasm#34 (proposal 0001 umbrella) / typed-wasm#78 (proposal 0002 RFC)
  • typed-wasm#106 ([review] → [accepted] roadmap tracker)
  • affinescript#444 (Tw_section encoder dedup — prerequisite cleanup)
  • affinescript#449 (proposal 0002 review request — satisfied)
  • ephapax companion issue (filed concurrently)

🤖 Filed 2026-05-30 to satisfy proposal 0002 §Gate 6 producer-side acceptance criterion.

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