pub struct CompileConfig {Show 51 fields
pub opt_level: u8,
pub target: TargetSpec,
pub bounds_check: bool,
pub safety_bounds: SafetyBounds,
pub hardware: String,
pub no_optimize: bool,
pub loom_compat: bool,
pub num_imports: u32,
pub func_arg_counts: Vec<u32>,
pub func_result_counts: Vec<u32>,
pub type_arg_counts: Vec<u32>,
pub relocatable: bool,
pub self_contained_funcref_table: bool,
pub linmem_base: u32,
pub native_pointer_abi: bool,
pub linear_memory_bytes: u32,
pub memory_pages: Vec<u32>,
pub stack_pointer_global: Option<(u32, i32)>,
pub func_ret_i64: Vec<bool>,
pub type_ret_i64: Vec<bool>,
pub global_widths: Vec<u32>,
pub func_params_i64: Vec<Vec<bool>>,
pub current_func_params_i64: Vec<bool>,
pub func_params_f32: Vec<Vec<bool>>,
pub current_func_params_f32: Vec<bool>,
pub func_params_f64: Vec<Vec<bool>>,
pub current_func_params_f64: Vec<bool>,
pub current_func_ret_f32: bool,
pub current_func_ret_f64: 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 current_func_param_count: Option<u32>,
pub current_func_index: Option<u32>,
pub current_func_block_arity: Vec<(u8, u8)>,
pub volatile_segments: Vec<VolatileRange>,
pub wcet_hints: Option<WcetHints>,
pub wsc_facts: Vec<WscFact>,
pub current_func_facts: Vec<WscFact>,
pub fact_div_zero_elide: Vec<usize>,
pub fact_div_ovf_elide: Vec<usize>,
pub fact_mem_bounds_elide: Vec<usize>,
pub proven_safe_mem_elide: Vec<usize>,
pub call_indirect_guards: CallIndirectGuards,
pub type_result_counts: Vec<u32>,
pub type_class_ids: Vec<u32>,
pub a64_substrate_emitted: bool,
pub num_imported_globals: u32,
pub global_mutable: Vec<bool>,
pub rv32_globals_emitted: bool,
}Expand description
Configuration for a compilation run
Fields§
§opt_level: u8Optimization level (0 = none, 1 = fast, 2 = default, 3 = aggressive)
target: TargetSpecTarget specification
bounds_check: boolLegacy: enable software bounds checking for memory operations.
Deprecated in favor of safety_bounds. When set, equivalent to
SafetyBounds::Software. Kept for backwards compatibility with
callers that haven’t migrated yet.
safety_bounds: SafetyBoundsPhase-1 unified safety-bounds knob. If bounds_check is true and
this is None, the legacy field wins (back-compat). If both are set,
safety_bounds wins.
hardware: StringHardware profile name (e.g. “nrf52840”, “stm32f407”)
no_optimize: boolSkip optimization passes (direct instruction selection)
loom_compat: boolUse Loom-compatible optimization preset
num_imports: u32Number of imported functions (calls to indices below this use Meld dispatch)
func_arg_counts: Vec<u32>AAPCS integer-argument count per function, indexed by full WASM function
index (imports first, then locals). Lets Call marshal the right number
of operand-stack values into R0–R3 (issue #195). Empty = pass no args
(pre-#195 behaviour).
func_result_counts: Vec<u32>#851: result (return-value) count per function, indexed by full WASM
function index (imports first). 0 = void, 1 = one value. The AArch64
direct-call lowering needs the 0-vs-1 distinction to decide whether to
push the x0 result — func_ret_i64/f32/f64 carry the result TYPE but
conflate void and i32. Empty on backends/paths that do not lower calls
this way (byte-invisible there).
type_arg_counts: Vec<u32>AAPCS integer-argument count per function type, indexed by type index.
Used by call_indirect (issue #195).
relocatable: boolProduce relocatable (ET_REL) host-link output. When set, the backend
uses the direct instruction selector (select_with_stack) rather than
the optimized path: the optimizer materializes an absolute linear-
memory base (0x20000100) and does not preserve caller-saved registers
across calls, both wrong for a host-linked object where the linmem base
is supplied via fp at runtime and callees follow AAPCS. Imports are
also emitted as direct func_N BLs (resolved to the wasm field name)
instead of __meld_dispatch_import. (#197 — follow-up to #188/#171.)
self_contained_funcref_table: bool#275: the SELF-CONTAINED Thumb-2 --cortex-m image path lowers
call_indirect through a flash-resident funcref table addressed
PC-RELATIVE (an LdrSym literal-pool pointer to
FUNC_TABLE_SYMBOL) — NEVER through R11, which is the linear-memory
base (the v0.42 #717 collision). Set by the CLI ONLY when the image
builder that emits and patches that table
(build_multi_func_cortex_m_elf) will run: Cortex-M family, not
--relocatable, no imported functions. Every other self-contained
configuration keeps the loud #275 decline. Default false.
linmem_base: u32#687 (--stack-layout=low): the absolute linear-memory base the
OPTIMIZED ARM path materializes into user code. Defaults to
OPTIMIZED_LINMEM_BASE (0x2000_0100, byte-identical to every
pre-#687 compile). Under the low stack layout the CLI shifts it up by
the reserved stack size so const-address loads/stores land in the moved
linear memory instead of the stack region. Only the optimized
(non-relocatable) path consumes it — the direct selector is R11/fp
- relative and follows the startup’s R11 init instead.
native_pointer_abi: bool#237: emit wasm function-static data as a base-independent .data
section (__synth_wasm_data) addressed via MOVW/MOVT symbol relocations,
so a host-pointer drop-in (linmem base = 0 for native *ptr derefs)
doesn’t mis-resolve the statics. Off by default — only the leaves’
base-relative [R11+const] path is used unless explicitly requested.
linear_memory_bytes: u32#237: wasm linear-memory minimum size in bytes — the full static-data
extent (initialized (data) segments plus the zero-init/BSS region).
Under native_pointer_abi, a const memory address below this is a wasm
static → symbol-relative; any address beyond it is a runtime host pointer
→ [R11=0 + addr].
memory_pages: Vec<u32>VCR-MEM-002 phase 1 (#406): initial size in 64 KiB pages of EACH linear
memory, indexed by memory index. Consulted only by the multi-memory
lowering arms (loads/stores wrapped in WasmOp::MultiMemory,
memory.size/grow with a non-zero index) — memory-0 lowering never
reads it, so single-memory output is byte-identical whether it is set
or empty. Empty (the default) means “no multi-memory context”: any
multi-memory op then declines loudly.
stack_pointer_global: Option<(u32, i32)>#237: the wasm stack-pointer global as (index, init_value), if the
module has one. Under native_pointer_abi the backend register-promotes
it: global.get materializes __synth_wasm_data + init (the real stack
top) and the init value doubles as the static-data base that separates
pointer consts (>= init) from frame-size scalars (< init).
func_ret_i64: Vec<bool>#311: per-function (full index) / per-type “returns i64” — the call lowering must tag i64 results as a register pair or the hi half is invisible to liveness.
type_ret_i64: Vec<bool>§global_widths: Vec<u32>#643: byte width of each defined global’s storage slot, indexed by
global index — 4 for i32/f32, 8 for i64/f64, 16 for v128 (from the
module’s global section). The globals table is laid out by SUMMING
these widths: an i64 global needs a register-PAIR store/load at
[R9, off]/[R9, off+4], and every later global’s offset shifts.
Empty ⇒ every global assumed 4 bytes (the legacy idx * 4 layout;
hand-built op streams and i32-only modules are byte-identical).
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 of function f 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). The source of truth — a per-function driver loop
(compile_module / the CLI loop) indexes it by func.index and copies
the slice into [current_func_params_i64] before each compile_function.
Empty → every param assumed i32 (the legacy path; keeps every function
with <=4 params, or all-i32 params, byte-identical).
current_func_params_i64: Vec<bool>#359: declared parameter widths of the function CURRENTLY being compiled
— current_func_params_i64[k] is true when param k is i64/f64. Set per
function (a cheap clone of the config) from [func_params_i64] by the
driver loop, because compile_function is shared across backends and
carries no function index. Empty → assume i32.
func_params_f32: Vec<Vec<bool>>GI-FPU-002 (#619/#369): per-function declared f32-param mask (full index,
imports first). The driver copies func_params_f32[f] into
[current_func_params_f32] before each compile_function. Empty ⇒
all-non-f32 (byte-identical to before).
current_func_params_f32: Vec<bool>GI-FPU-002: declared f32-param mask of the function CURRENTLY being
compiled — current_func_params_f32[k] is true when param k is f32.
Set per function from [func_params_f32], mirroring
[current_func_params_i64]. Empty ⇒ no f32 params.
func_params_f64: Vec<Vec<bool>>GI-FPU-002 phase 2 (#369): per-function declared f64-param mask (full index, imports first) and the CURRENT function’s slice. Hard-float targets decline f64-param functions loudly — the legacy width inference treats an f64 param as an i64 CORE-register pair, which reads the wrong registers under AAPCS-VFP (the caller put it in a D-register). Empty ⇒ no f64 params (byte-identical legacy path).
current_func_params_f64: Vec<bool>See func_params_f64.
current_func_ret_f32: boolGI-FPU-002 phase 2 (#719/#369): whether the function CURRENTLY being
compiled returns f32. Set per function from the decoder’s func_ret_f32.
The direct selector’s epilogue uses it to loudly decline a result that
reaches the return in a core register instead of an S-register (a call
that returned f32 as integer-tagged R0 would otherwise be a silent
miscompile — the AAPCS-VFP caller reads S0). false for hand-built op
streams / non-f32 returns (byte-identical to before).
current_func_ret_f64: boolGI-FPU-002 phase 2 (#719/#369): whether the function CURRENTLY being compiled returns f64 (D0 under AAPCS-VFP). Same epilogue-soundness role.
func_ret_f32: Vec<bool>GI-FPU-002 phase 2 (#719/#369): per-function (full index, imports first)
“returns f32/f64” tables. The direct selector declines a call to an
f32/f64-returning callee LOUDLY at the call site — the result arrives in
S0/D0 (AAPCS-VFP), which this increment does not marshal into the operand
stack; tagging it as an integer R0 would be a silent miscompile. Also the
source for [current_func_ret_f32]/[current_func_ret_f64] in the
per-function driver loops. Empty ⇒ callees assumed non-float-returning
(hand-built op streams; byte-identical legacy behaviour).
func_ret_f64: Vec<bool>See func_ret_f32.
type_ret_f32: Vec<bool>GI-FPU-002 phase 2 (#719/#369): per-type “returns f32/f64” — the
call_indirect analogue of func_ret_f32.
type_ret_f64: Vec<bool>See type_ret_f32.
current_func_param_count: Option<u32>#457: DECLARED parameter count of the function CURRENTLY being compiled,
from the module’s type section (func_arg_counts[func.index]). Set per
function by the driver loops like [current_func_params_i64].
The backends otherwise INFER the param count from local-access patterns
(count_params: a local whose first access is a read is assumed to be a
param) — which cannot distinguish a param from a read-before-write
non-param local. WASM zero-initializes non-param locals, so such a local
must read 0; the inference instead homed it in a parameter register and
read caller garbage (#457).
When this is Some(declared), every backend uses
min(referenced_locals(ops), declared)
— the highest index the body touches, clamped by the signature. That is
EXACT in both directions: a genuine non-param local can never be
mistaken for a param (the clamp), and a param can never be demoted to a
local (the max over ALL accesses, reads and writes alike). The earlier
rule capped the READ-FIRST inference instead, which demoted a
conditionally-written param and produced an uninitialised-frame-slot
read on ARM and RISC-V and a zero-init local on AArch64 (#970/#851).
None → declared signature unknown (hand-built op streams, direct
compile_function callers) → pure inference, the legacy behaviour.
HONEST RESIDUAL (#970, unchanged from #851): on that path a write-first
index is genuinely AMBIGUOUS — a param whose incoming value is dead, or
a non-param local — and both readings can be wrong. The read-first rule
keeps the #457 behaviour rather than reading caller garbage for a
zero-init local. The CLI always supplies a declared count.
current_func_index: Option<u32>(#778 phase 4 / #49) The WASM index of the function CURRENTLY being compiled,
so the WCET pass can identify this function’s OWN func_<idx> self-call label
(a self-recursive BL func_N where N == this index) and prove/decline the
self-recursion depth. Set per function by the driver loop (like
[current_func_params_i64]). None → unknown (hand-built op streams, direct
compile_function callers) → no self-recursion certificate is attempted.
current_func_block_arity: Vec<(u8, u8)>#509: blocktype-arity side-table of the function CURRENTLY being compiled
— (param_count, result_count) of the k-th Block/Loop/If in its op
stream (ordinal-keyed; see FunctionOps::block_arity). Set per function
by the driver loop (like [current_func_params_i64]). The direct selector
uses it to land a value carried by br/br_if/br_table in the target
block’s designated result register instead of dropping it. Empty → every
block treated as void (the legacy lowering; hand-built op streams).
volatile_segments: Vec<VolatileRange>#543 Phase 1 — integrator-marked volatile linear-memory segments (the DMA
transfer window). Each range [base, base+len) names a region of the fused
linear memory that an EXTERNAL agent (the DMA engine, modelled by gale as a
Component-Model own<buffer> handoff — gale decision DD-DMA-REGION-001,
gale#124) rewrites out-of-band. Loads and stores whose address falls inside
a marked range must eventually be treated as VOLATILE: not cached, hoisted,
or reordered across the transfer boundary.
PHASE-2 CONTRACT (implemented — issue #543): the optimizer’s address-caching passes HONOR these ranges. Consumption points:
- the #468 base-CSE / const-address-fold
(
optimizer_bridge::plan_base_cse, DEFAULT-ON, opt-outSYNTH_BASE_CSE=0): a const-address access whose 4-byte window intersects a marked range is EXCLUDED from the fold set — it keeps its verbatim per-access materialize-and-access codegen, while accesses outside the range still fold; - const-CSE (
liveness::apply_const_csewired inarm_backend.rs, DEFAULT-ON, opt-outSYNTH_CONST_CSE=0; the former bridge-level inline cache is retired, #242): declines WHOLESALE while any range is marked — a cached constant cannot be classified address-vs-data at that level, so the conservative stance for statically-unknown addressing is to re-materialize every constant at each occurrence.
Passes that only touch SP-relative frame slots (stack-reload forwarding,
frame-slot DCE, spill re-choice) are unaffected by design: these ranges
are LINEAR-MEMORY addresses, and frame slots are never linmem. Nothing on
the pipeline deletes, forwards, or reorders a linear-memory access (IR CSE
deliberately never CSEs MemLoads; DCE removes only unreachable blocks),
so every marked access is issued verbatim, in program order.
Empty (the default): zero behavior change by construction — every gate
reduces to the pre-#543 path, so the emitted .text is byte-identical
with or without this code (the frozen-codegen gate holds). See rivet
VCR-DMA-001.
wcet_hints: Option<WcetHints>#778 phase 2 — the parsed --wcet-hints file (UNTRUSTED per-function
loop-bound hints, the scry seam). Consulted ONLY by the WCET sidecar
computation over the final instruction stream; NEVER by codegen — the
emitted bytes are byte-identical with or without hints. Every hint is
soundly verified before use and rejected with a machine reason
otherwise.
wsc_facts: Vec<WscFact>VCR-PERF-002 Phase 1 (#494) — proven invariants forwarded by loom in
the wsc.facts custom section (encoding:
docs/design/wsc-facts-encoding.md; program:
docs/design/proof-carrying-specialization.md), whole-module table
keyed by (func_index, value_id). The compile driver copies the
current function’s slice into current_func_facts (the
func_params_i64 → current_func_params_i64 pattern), because
compile_function carries no function index.
PHASE-1 CONTRACT: threaded but NOT consumed — no codegen path reads
facts, so emitted bytes are unchanged whether or not the module
carries the section (locked by wsc_facts_ingestion_494.rs). Phase 2
turns each fact into a premise for a flag-gated (SYNTH_FACT_SPEC),
per-elision ordeal-validated specialization; the facts-absent compile
stays byte-identical by construction (empty ⇒ every gate vacuous).
current_func_facts: Vec<WscFact>VCR-PERF-002 Phase 1 (#494): the wsc.facts invariants of the function
CURRENTLY being compiled (fact.func_index == func.index), set per
function by the driver loops like current_func_params_i64. This is
the field a Phase-2 selector pass will read its premises from. Empty →
no facts → no specialization may ever fire (the fail-safe default).
fact_div_zero_elide: Vec<usize>VCR-PERF-002 Phase 2b (#494, divisor-nonzero): op indices (into the op
stream passed to compile_function) of div/rem ops whose
DIVIDE-BY-ZERO trap guard is proven dead — the fact-spec pass
discharged UNSAT(P ∧ divisor == 0) per site through the
certificate-checked ordeal solver BEFORE the driver set this field.
Consumed by the ARM direct selector (select_with_stack); every other
path ignores it (guards stay — sound). Empty (the default) ⇒ every
guard is emitted, byte-identical to today.
fact_div_ovf_elide: Vec<usize>VCR-PERF-002 Phase 2b (#494): op indices of div_s ops whose
INT_MIN / -1 OVERFLOW trap guard is proven dead — a SEPARATE
obligation (UNSAT(P ∧ dividend == INT_MIN ∧ divisor == -1)). A
divisor-nonzero fact alone NEVER lands here: divisor ≠ 0 does not
exclude -1 (#633/#634 two-guard distinction). Empty ⇒ guard emitted.
fact_mem_bounds_elide: Vec<usize>#494 bounds-elision (#390 guard_bool): op indices of i32 memory
accesses whose --safety-bounds software inline guard is proven dead
— the fact-spec pass discharged
UNSAT(P ∧ trap_mem_oob(zext64(index) + offset, size, min_memory_bytes)) per site through the certificate-checked ordeal
solver BEFORE the driver set this field (ordeal 0.9.1 trap_mem_oob
shape, wraparound-safe 64-bit extension). Consumed by the ARM direct
selector (select_with_stack); every other path ignores it (guards
stay — sound). Empty (the default) ⇒ every guard is emitted,
byte-identical to today.
proven_safe_mem_elide: Vec<usize>VCR-MEM-004 (#901): op indices of linear-memory accesses whose
--safety-bounds software inline guard is elided on an EXTERNAL proof
— scry’s sound abstract interpretation proved the access in-bounds
against the memory’s guaranteed minimum size, and the verdict file
cleared every fail-closed gate (crate::proven_safe::ingest:
module_sha256 bound to the exact bytes being compiled,
memory_min_bytes equal to this module’s declared floor, and each
entry’s (func, pc) key validated against the decoded operator at
that index).
Kept SEPARATE from CompileConfig::fact_mem_bounds_elide on purpose:
the two strip the same guard at the same consumption point, but on
different AUTHORITIES (a per-site ordeal certificate vs a whole-module
external AI), and the synth-proven-safe-elisions-v1 attestation
records which one covered each site. The ARM backend unions them.
Empty (the default) ⇒ every guard is emitted, byte-identical to today.
call_indirect_guards: CallIndirectGuards#642: call_indirect guard inputs — the compile-time table size for
the runtime bounds check and the per-expected-type closed-world type
verdicts — computed from the decoded module by
crate::wasm_decoder::DecodedModule::call_indirect_guards and set by
the driver loops. The default (table_size: None, empty verdicts)
DECLINES every call_indirect lowering: an unchecked indirect branch
is never emitted (WASM Core §4.4.8 requires OOB/type-mismatch traps).
type_result_counts: Vec<u32>#851 lane L3: result count per FUNCTION TYPE (see
crate::wasm_decoder::DecodedModule::type_result_counts). The aarch64
call_indirect lowering needs the 0-vs-1 result distinction for a callee
it knows only by its static type.
type_class_ids: Vec<u32>#851 lane L3: the STRUCTURAL signature class id per function type (see
crate::wasm_decoder::DecodedModule::structural_type_class_ids). The
aarch64 call_indirect type check compares this, not the raw type index
— WASM type equality is structural. Distinct from
call_indirect_guards.type_class_ids, which the ARM path populates only
when its heterogeneous-table sidecar exists.
a64_substrate_emitted: bool#851 lane L3, aarch64 only — the driver has EMITTED the module-level
substrate the globals and call_indirect lowerings address: the .data
globals image (__synth_globals) and the .text funcref table
(__synth_func_table), both produced by
synth_backend_aarch64::substrate::plan.
FAIL-SAFE BY DEFAULT (false): the aarch64 selector LOUD-DECLINES
global.get/global.set/call_indirect unless this is set, so a driver
that compiles function bodies but never emits the regions cannot ship
code addressing a symbol that does not exist. Set only on the two paths
that call plan() and place its output in the object.
num_imported_globals: u32RQ-63-RVGLOBAL (#242): how many globals the module IMPORTS. The op
stream’s global.get/global.set index space is imports-FIRST, while
CompileConfig::global_widths / CompileConfig::global_mutable
are indexed by DEFINED global (the decoder’s WasmGlobal::index), so a
backend maps an op index i to defined global i - num_imported_globals
and must decline i < num_imported_globals (an imported global’s value
arrives at instantiation, which a synth-emitted region cannot bind).
global_mutable: Vec<bool>RQ-63-RVGLOBAL (#242): per DEFINED global, its declared mutability.
Indexed like CompileConfig::global_widths. A validated module never
global.sets an immutable global, but the RV32 lowering declines it
anyway rather than write through a const — defence in depth, since
the decoder does not run the wasm validator. Empty ⇒ every global is
treated as mutable (hand-built op streams).
rv32_globals_emitted: boolRQ-63-RVGLOBAL (#242): whether the driver WILL place the RV32 globals
region (__synth_globals, a synth-emitted .data image carrying every
defined global’s decoded initializer — synth_backend_riscv::globals)
in the object it assembles. FAIL-SAFE BY DEFAULT (false): the RV32
selector LOUD-DECLINES global.get/global.set unless this is set,
so a driver that compiles function bodies but never emits the region
cannot ship code relocating against a symbol nothing defines (the
#1102 dangling-reference class). The aarch64 a64_substrate_emitted
contract, ported.
Implementations§
Source§impl CompileConfig
impl CompileConfig
Sourcepub fn effective_safety_bounds(&self) -> SafetyBounds
pub fn effective_safety_bounds(&self) -> SafetyBounds
Resolve the effective safety-bounds setting, honouring the legacy
bounds_check field as a fallback. Used by backends to pick the
inline-check shape.
Trait Implementations§
Source§impl Clone for CompileConfig
impl Clone for CompileConfig
Source§fn clone(&self) -> CompileConfig
fn clone(&self) -> CompileConfig
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more