pub struct DecodedModule {Show 30 fields
pub functions: Vec<FunctionOps>,
pub memories: Vec<WasmMemory>,
pub data_segments: Vec<(u32, Vec<u8>)>,
pub extra_memory_data_segments: Vec<(u32, u32, Vec<u8>)>,
pub multi_memory_decline: Option<String>,
pub default_memory_nonconst_data: Option<String>,
pub imports: Vec<ImportEntry>,
pub num_imported_funcs: u32,
pub func_arg_counts: Vec<u32>,
pub type_arg_counts: Vec<u32>,
pub func_ret_i64: Vec<bool>,
pub type_ret_i64: Vec<bool>,
pub func_params_i64: Vec<Vec<bool>>,
pub func_params_f32: Vec<Vec<bool>>,
pub func_params_f64: Vec<Vec<bool>>,
pub func_ret_f32: Vec<bool>,
pub func_ret_f64: Vec<bool>,
pub type_ret_f32: Vec<bool>,
pub type_ret_f64: Vec<bool>,
pub globals: Vec<WasmGlobal>,
pub elem_func_indices: Vec<u32>,
pub table_size: Option<u32>,
pub table_sizes: Vec<Option<u32>>,
pub elem_segments: Vec<ElemSegmentInfo>,
pub func_type_indices: Vec<u32>,
pub func_result_counts: Vec<u32>,
pub type_signatures: Vec<String>,
pub type_result_counts: Vec<u32>,
pub wsc_facts: Vec<WscFact>,
pub start_function: Option<u32>,
}Expand description
Decoded WASM module with functions and memory
Fields§
§functions: Vec<FunctionOps>Decoded functions
memories: Vec<WasmMemory>Linear memories
data_segments: Vec<(u32, Vec<u8>)>Data segments (offset, data) for memory initialization.
MEMORY 0 ONLY — the legacy single-memory field every existing consumer
reads; its shape and contents are unchanged by multi-memory (#406).
Segments targeting memory > 0 live in
Self::extra_memory_data_segments.
extra_memory_data_segments: Vec<(u32, u32, Vec<u8>)>VCR-MEM-002 phase 1 (#406): active const-offset data segments on
NON-DEFAULT memories, as (memory_index, offset, bytes) with
memory_index > 0. Previously these were silently dropped (memory k
shipped uninitialized while its loads compiled). Declaration order.
multi_memory_decline: Option<String>VCR-MEM-002 phase 1 (#406): Some(reason) when the module contains a
multi-memory shape decode cannot lower (e.g. an active data segment on
memory > 0 with a non-constant offset). The multi-memory compile path
must decline LOUDLY with this reason; single-memory modules never set
it.
default_memory_nonconst_data: Option<String>#851 — Some(reason) when an active data segment on MEMORY 0 has a
NON-CONSTANT offset expression: such a segment cannot be placed at
compile time and is absent from Self::data_segments (the legacy
drop, kept frozen for the ARM/RV32 paths). Recording it lets a backend
with no runtime-offset placement (aarch64) decline LOUDLY instead of
shipping the region uninitialized — the segment would otherwise be
INVISIBLE to any post-decode honesty check.
imports: Vec<ImportEntry>Import entries (module name, field name, kind)
num_imported_funcs: u32Number of imported functions (for distinguishing import calls from local calls)
func_arg_counts: Vec<u32>AAPCS integer-argument count per function, indexed by the full WASM
function index (imported functions first, then locally-defined ones).
Used by the backend to marshal call arguments into R0–R3 (issue #195).
Counts every parameter as one slot (i64/f64 over-counted — see the
backend’s set_func_arg_counts scope note).
type_arg_counts: Vec<u32>AAPCS integer-argument count per function type, indexed by type index.
Used by call_indirect, whose callee arg count comes from the static
type index (issue #195).
func_ret_i64: Vec<bool>#311: whether each function (full index, imports first) returns i64 — the call lowering must tag the result as a register PAIR (r0:r1) or the hi half is invisible to liveness and the next constant clobbers it.
type_ret_i64: Vec<bool>#311: whether each function type returns i64 (for call_indirect).
func_params_i64: Vec<Vec<bool>>#359: declared parameter widths per function (full index, imports
first): func_params_i64[f][k] is true when param k is i64/f64. The
AAPCS stack-argument path needs the declared widths — op-stream inference
can’t see an unused i64 param that still shifts the incoming-stack layout.
func_params_f32: Vec<Vec<bool>>GI-FPU-002 (#619/#369): declared f32-param mask per function (full
index, imports first): func_params_f32[f][k] is true when param k is
f32. The direct selector homes hard-float f32 args in S0..S15 (AAPCS-VFP),
which op-stream inference cannot recover for a pure-passthrough f32 param.
func_params_f64: Vec<Vec<bool>>GI-FPU-002 phase 2 (#369): declared f64-param mask per function
(full index, imports first): func_params_f64[f][k] is true when param
k is f64. Hard-float targets decline such functions loudly (the
legacy width inference treats the param as an i64 CORE pair — wrong
registers under AAPCS-VFP). Distinct from func_params_i64, which
deliberately lumps i64 and f64 for frame-layout purposes.
func_ret_f32: Vec<bool>GI-FPU-002 phase 2 (#719/#369): whether each function (full index, imports first) returns f32. The direct selector’s epilogue homes an f32 result in S0 (AAPCS-VFP); when the result value transited a core register (e.g. it came from a call that returned f32 as an integer-tagged R0), the epilogue must loudly decline rather than emit the integer R0 return (a silent miscompile — the caller reads S0). Op-stream inference cannot see a pure-passthrough f32 return, so it is carried from the declared signature.
func_ret_f64: Vec<bool>GI-FPU-002 phase 2 (#719/#369): whether each function returns f64 (D0
under AAPCS-VFP). Same epilogue-soundness role as func_ret_f32.
type_ret_f32: Vec<bool>GI-FPU-002 phase 2 (#719/#369): whether each function type returns
f32 / f64 — the call_indirect analogue of func_ret_f32/func_ret_f64
(the selector loudly declines an indirect call whose static type returns
a float this increment does not marshal, rather than tag S0/D0 as R0).
type_ret_f64: Vec<bool>See Self::type_ret_f32.
globals: Vec<WasmGlobal>Defined globals with their initializers (#237). Empty if the module has
no global section. Used by the native-pointer ABI to make a global whose
initializer is a linear-memory address (e.g. $__stack_pointer)
self-contained rather than table-relative.
elem_func_indices: Vec<u32>Function indices that populate any table via an element segment (#275).
These are the possible call_indirect targets — a function reached only
through the table is invisible to direct-call reachability, so the
whole-graph closure must treat every table entry as reachable once any
reachable function performs a call_indirect. Empty for modules with no
element section (every leaf/direct-call module), keeping output identical.
table_size: Option<u32>#642: compile-time size (in entries) of table 0 — table_sizes[0],
kept as a convenience accessor. See Self::table_sizes.
table_sizes: Vec<Option<u32>>#650: compile-time size (in entries) per table, indexed by table index
(imported tables first, then the table section, in declaration order).
A DEFINED table’s size is exact: table.grow/table.set are
unsupported ops (their functions loud-skip at decode), so nothing
synth compiles can resize or retype a table. An imported table only
yields a sound bound when its limits pin the size (max == initial);
otherwise its entry is None and the call_indirect lowering
declines (for that table AND for any later table, whose base offset
within the contiguous R11 region is then unknown).
elem_segments: Vec<ElemSegmentInfo>#642: per element segment, everything the closed-world call_indirect
type check needs. offset is the const i32 placement of an ACTIVE
segment into table table_index (None = passive/declared/non-const
offset — statically unverifiable placement); funcs are the segment’s
function indices in slot order (None = an entry was not a plain
ref.func, e.g. ref.null — statically unverifiable contents).
func_type_indices: Vec<u32>#642: type index per function, indexed by the FULL function index (imports first, then locally-defined ones).
func_result_counts: Vec<u32>#851: result (return-value) count per function, indexed by the FULL
function index (imports first). 0 = void, 1 = single result, etc.
(saturated at 255 for pathological signatures). The aarch64 direct-call
lowering needs the 0-vs-1 distinction — func_ret_i64/f32/f64 carry the
result TYPE but conflate void and i32 (both all-false), so they cannot say
whether a value is pushed back after the call.
type_signatures: Vec<String>#642: canonical structural signature per type index (params/results
rendered as a string) — used for the closed-world call_indirect type
check, which must compare SIGNATURES, not raw type indices (a module
may carry structurally-identical duplicate types).
type_result_counts: Vec<u32>#851 lane L3: result (return-value) count per FUNCTION TYPE, indexed by
type index — the call_indirect analogue of Self::func_result_counts.
An indirect call’s callee is known only by its static type, so the
0-vs-1 result distinction (does a value get pushed back?) has to come
from here; type_ret_i64/f32/f64 carry the result TYPE but conflate
void with i32 (both all-false).
wsc_facts: Vec<WscFact>VCR-PERF-002 Phase 1 (#494): proven invariants from loom’s wsc.facts
custom section, keyed by (function index, value id) — see
docs/design/wsc-facts-encoding.md (schema v1) and
crate::wsc_facts::parse_wsc_facts. FAIL-SAFE by contract (loom#231
Q4): a missing/unparseable section or unknown version yields the empty
vec, unknown fact kinds are skipped — never a decode error. Phase 1 is
ingestion only: NO codegen path consumes these yet, so emitted bytes
are unchanged whether or not a module carries the section.
start_function: Option<u32>RQ-59-STARTFN (#1046): the module’s (start ...) function index
(FULL index space — imports first), when a start section is present.
The decoder previously had NO Payload::StartSection arm at all: the
section fell through the catch-all and was discarded outright, so no
backend, path, or warning ever mentioned it — the module compiled,
exited 0, and its instantiation-time initialization (WASM Core §4.5.5:
the start function runs before any export is callable) silently never
ran. Recording it lets the compile paths refuse LOUDLY (#851/#1041
shape) until a backend actually invokes it; invocation is a capability
follow-on, not part of the #1046 fix.
Implementations§
Source§impl DecodedModule
impl DecodedModule
Sourcepub fn structural_type_class_ids(&self) -> Vec<u32>
pub fn structural_type_class_ids(&self) -> Vec<u32>
#676/#851: the STRUCTURAL signature class of every function type — structurally-equal types share one dense 1-based id (first-occurrence order over the type section); id 0 is reserved for “null slot / not statically classifiable” and therefore never equals a real class.
This is the id a call_indirect type check must compare, NOT the raw
type index: WASM type equality is STRUCTURAL, so a module carrying two
identical (param i32) (result i32) entries must let call_indirect (type 1) reach a function declared with type 0 (§4.4.8). Comparing
indices would trap where wasmtime calls.
CallIndirectGuards::type_class_ids exposes this only when the ARM
heterogeneous-table sidecar exists; the aarch64 dispatch type-checks
UNCONDITIONALLY, so it reads the ids from here.
Sourcepub fn funcref_region_class_ids(&self) -> Vec<u32>
pub fn funcref_region_class_ids(&self) -> Vec<u32>
#851 lane L3: the structural class id of every funcref-region slot, in
the SAME contiguous order as Self::funcref_region_slots — 0 for a
null slot, for a slot whose function has no known type, and for a table
whose image is not statically verifiable (all of which
funcref_region_slots already reports as None).
The aarch64 funcref table stores this id beside each slot’s branch
trampoline, so the dispatch’s cmp against the expected class id is
simultaneously the §4.4.8 TYPE check and the NULL check (id 0 matches
no expected class, which is >= 1).
Sourcepub fn call_indirect_guards(&self) -> CallIndirectGuards
pub fn call_indirect_guards(&self) -> CallIndirectGuards
#642/#650: compute the call_indirect guard inputs — per table, the
compile-time size for the runtime bounds check, the base byte offset
within the contiguous R11 region, and the per-expected-type
closed-world verdict that discharges the type check at compile time.
See CallIndirectGuards for the layout contract and soundness
argument.
Sourcepub fn funcref_region_slots(&self) -> Vec<Option<u32>>
pub fn funcref_region_slots(&self) -> Vec<Option<u32>>
#275: the STATIC image of the contiguous funcref region — one slot per
table entry across ALL tables in declaration order (the exact layout
CallIndirectGuards’ base_byte_offset contract describes), each
Some(full_function_index) for a statically-known initialized slot
and None for a null (or not statically attributable) slot. The
self-contained image builder resolves each Some to the laid-out
function address (Thumb bit set) and links every None as a ZERO
word, which the dispatch’s #664 null check / #676 id-0 compare traps.
Mirrors the reconstruction in Self::call_indirect_guards:
- stops at the first table with no compile-time size (later tables have no constant base offset, so no dispatch can reach them);
- a table whose segments are not statically verifiable (non-const
offset, non-
ref.funcentry, out-of-range write) contributes all-Noneslots — every dispatch into it declines at the lowering anyway, and a rogue read traps on the zero word rather than branch.
Trait Implementations§
Source§impl Clone for DecodedModule
impl Clone for DecodedModule
Source§fn clone(&self) -> DecodedModule
fn clone(&self) -> DecodedModule
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more