Skip to content

Access-site carrier: how does the post-codegen verifier map (i32.load offset=N) back to (region_id, field_id)? #78

@hyperpolymath

Description

@hyperpolymath

Discovered while pre-staging the L2 codec for proposal 0001 (#76, refs #34). Pre-stage PR: #77.

Problem

Proposal 0001 defines typedwasm.regions as a region/field schema carrier. With it, the verifier knows that region X exists and field Y has type Z and is at offset O within X. What it does not know is which (region_id, field_id) any individual wasm memory access is supposed to be against.

The source-level checker has no problem: it sees region.get \$r .f directly in the AST and resolves $r → region, .f → field at compile time. After codegen, that statement is some sequence ending in i32.load offset=N align=A — and the ($r, .f) annotation is gone. The verifier can re-derive which region's offset window N falls into only if the producer tells it which base pointer (region instance) was loaded, and a single function may touch several regions.

So proposal 0001 as written is sufficient for L2-the-spec (regions/fields exist) but insufficient for L2-the-enforcement (every typed access resolves to a declared region/field).

This is orthogonal to the schema carrier — the schema says what regions exist; the access-site carrier says which one each access targets.

Options

A. Per-instruction access-site carrier section

A new section typedwasm.access-sites mapping (func_idx, instruction_byte_offset_in_function) → (region_id, field_id).

Pros:

  • Lets the verifier check every typed load/store against the region/field's declared type without dictating codegen shape.
  • Composes cleanly with the proposal-0001 schema carrier.
  • Producers already know this mapping at codegen time — emitting it is cheap.

Cons:

  • A third section to spec, version, and cross-impl test.
  • The per-instruction offset is fragile under any post-codegen rewrite (wasm-opt, snip, gc). Producers must emit *after* any such pass, or the offsets desync.
  • Adds maybe 8-12 bytes per typed access. On a typical AffineScript module that's not nothing — measurement needed before committing.

B. ABI convention via named imports

Typed loads/stores route through a fixed-name imported function shape: __typedwasm_load_\$region_\$field / __typedwasm_store_\$region_\$field. The verifier inspects Operator::Call targets and matches the import name against the schema.

Pros:

  • No new carrier section; everything piggybacks on standard wasm imports.
  • Stable across wasm-opt and friends — names are first-class.
  • Trivially debug-friendly (the imports show up in any tool).

Cons:

  • Constrains codegen — producers can't inline typed accesses as raw i32.load even when they want to (perf-critical paths).
  • Pollutes the import surface; ABI-conscious producers will hate this.
  • Names have to be globally agreed (escaping rules for unusual region/field characters).

C. Hybrid

Default to inline i32.load with a B-style named-import *opt-in* for producers that want it; the per-instruction access-site carrier (A) is the universal fallback the verifier always understands.

Pros: producers choose their poison.

Cons: verifier has to handle both shapes; two-codepath bug surface.

D. Skip L2-the-enforcement, ship L2-the-spec

Accept that proposal 0001 gives the verifier enough to validate *region/field schema agreement* between modules (essentially: "do A's exports cover B's imports?") and to feed L13 / L15 / a hypothetical future static-analysis layer, but not enough to bind individual loads/stores. L2-the-enforcement waits on a separate proposal.

Pros: ships the schema carrier sooner with no scope creep.

Cons: "L2 enforcement on emitted wasm" is then still a partial story; users may be surprised.

Recommendation

A (per-instruction access-site carrier) as a follow-up proposal, with a tiny prototype emitter on the AffineScript side (or a hand-rolled test fixture) to measure the size overhead before formally adopting. Reject B in v1 because constraining codegen shape is a big concession and the perf hit on hot paths could be substantial.

Ask

. Decide between A / B / C / D for the v1 milestone.
. If A: file a follow-up proposal in docs/proposals/0002-...adoc and gate the L2 verifier pass (the verify_region_binding that PR #77 deliberately omits) on its acceptance.
. Either way: update proposal 0001 with an open question pointing at this issue so future readers don't trip on the same gap.

Refs

🤖 Generated with Claude Code

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