Skip to main content

DecodedModule

Struct DecodedModule 

Source
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: u32

Number 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>§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

Source

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.

Source

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

Source

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.

Source

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 CallIndirectGuardsbase_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.func entry, out-of-range write) contributes all-None slots — 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

Source§

fn clone(&self) -> DecodedModule

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for DecodedModule

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.