Skip to content

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

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

    architectureStructural/system-level shape and runtime behaviourbindingsABI, FFI, WASM, and cross-language interop surfacesenhancementNew capability or improvement to existing behaviour

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions