Skip to content

Commit

Permalink
wasmtime: Make table lazy-init configurable (#8531)
Browse files Browse the repository at this point in the history
* wasmtime: Make table lazy-init configurable

Lazy initialization of tables has trade-offs that we haven't explored in
a while. Making it configurable makes it easier to test the effects of
these trade-offs on a variety of WebAssembly programs, and allows
embedders to decide whether the trade-offs are worth-while for their use
cases.

* Review comments
  • Loading branch information
jameysharp authored May 14, 2024
1 parent 9b9a796 commit 7ea9201
Show file tree
Hide file tree
Showing 12 changed files with 392 additions and 57 deletions.
9 changes: 9 additions & 0 deletions crates/cli-flags/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ wasmtime_option_group! {
/// linear memories.
pub guard_before_linear_memory: Option<bool>,

/// Whether to initialize tables lazily, so that instantiation is
/// fast but indirect calls are a little slower. If no, tables are
/// initialized eagerly from any active element segments that apply to
/// them during instantiation. (default: yes)
pub table_lazy_init: Option<bool>,

/// Enable the pooling allocator, in place of the on-demand allocator.
pub pooling_allocator: Option<bool>,

Expand Down Expand Up @@ -548,6 +554,9 @@ impl CommonOptions {
if let Some(enable) = self.opts.guard_before_linear_memory {
config.guard_before_linear_memory(enable);
}
if let Some(enable) = self.opts.table_lazy_init {
config.table_lazy_init(enable);
}

// If fuel has been configured, set the `consume fuel` flag on the config.
if self.wasm.fuel.is_some() {
Expand Down
34 changes: 26 additions & 8 deletions crates/cranelift/src/func_environ.rs
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,7 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
table_index: TableIndex,
index: ir::Value,
cold_blocks: bool,
lazy_init: bool,
) -> ir::Value {
let pointer_type = self.pointer_type();
self.ensure_table_exists(builder.func, table_index);
Expand All @@ -833,6 +834,11 @@ impl<'module_environment> FuncEnvironment<'module_environment> {
self.isa.flags().enable_table_access_spectre_mitigation(),
);
let value = builder.ins().load(pointer_type, flags, table_entry_addr, 0);

if !lazy_init {
return value;
}

// Mask off the "initialized bit". See documentation on
// FUNCREF_INIT_BIT in crates/environ/src/ref_bits.rs for more
// details. Note that `FUNCREF_MASK` has type `usize` which may not be
Expand Down Expand Up @@ -1354,11 +1360,14 @@ impl<'a, 'func, 'module_env> Call<'a, 'func, 'module_env> {
cold_blocks: bool,
) -> WasmResult<Option<(ir::Value, ir::Value)>> {
// Get the funcref pointer from the table.
let table = &self.env.module.table_plans[table_index];
let TableStyle::CallerChecksSignature { lazy_init } = table.style;
let funcref_ptr = self.env.get_or_init_func_ref_table_elem(
self.builder,
table_index,
callee,
cold_blocks,
lazy_init,
);

// If necessary, check the signature.
Expand Down Expand Up @@ -1407,7 +1416,7 @@ impl<'a, 'func, 'module_env> Call<'a, 'func, 'module_env> {

// Generate a rustc compile error here if more styles are added in
// the future as the following code is tailored to just this style.
let TableStyle::CallerChecksSignature = table.style;
let TableStyle::CallerChecksSignature { .. } = table.style;

// Test if a type check is necessary for this table. If this table is a
// table of typed functions and that type matches `ty_index`, then
Expand Down Expand Up @@ -1752,9 +1761,14 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m

// Function types.
WasmHeapTopType::Func => match plan.style {
TableStyle::CallerChecksSignature => {
Ok(self.get_or_init_func_ref_table_elem(builder, table_index, index, false))
}
TableStyle::CallerChecksSignature { lazy_init } => Ok(self
.get_or_init_func_ref_table_elem(
builder,
table_index,
index,
false,
lazy_init,
)),
},
}
}
Expand Down Expand Up @@ -1805,7 +1819,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
// Function types.
WasmHeapTopType::Func => {
match plan.style {
TableStyle::CallerChecksSignature => {
TableStyle::CallerChecksSignature { lazy_init } => {
let (elem_addr, flags) = table_data.prepare_table_addr(
builder,
index,
Expand All @@ -1815,9 +1829,13 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
// Set the "initialized bit". See doc-comment on
// `FUNCREF_INIT_BIT` in
// crates/environ/src/ref_bits.rs for details.
let value_with_init_bit = builder
.ins()
.bor_imm(value, Imm64::from(FUNCREF_INIT_BIT as i64));
let value_with_init_bit = if lazy_init {
builder
.ins()
.bor_imm(value, Imm64::from(FUNCREF_INIT_BIT as i64))
} else {
value
};
builder
.ins()
.store(flags, value_with_init_bit, elem_addr, 0);
Expand Down
12 changes: 9 additions & 3 deletions crates/environ/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,13 +315,19 @@ pub trait InitMemory {
#[derive(Debug, Clone, Hash, Serialize, Deserialize)]
pub enum TableStyle {
/// Signatures are stored in the table and checked in the caller.
CallerChecksSignature,
CallerChecksSignature {
/// Whether this table is initialized lazily and requires an
/// initialization check on every access.
lazy_init: bool,
},
}

impl TableStyle {
/// Decide on an implementation style for the given `Table`.
pub fn for_table(_table: Table, _tunables: &Tunables) -> Self {
Self::CallerChecksSignature
pub fn for_table(_table: Table, tunables: &Tunables) -> Self {
Self::CallerChecksSignature {
lazy_init: tunables.table_lazy_init,
}
}
}

Expand Down
7 changes: 7 additions & 0 deletions crates/environ/src/tunables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ pub struct Tunables {
/// beginning of the allocation in addition to the end.
pub guard_before_linear_memory: bool,

/// Whether to initialize tables lazily, so that instantiation is fast but
/// indirect calls are a little slower. If false, tables are initialized
/// eagerly from any active element segments that apply to them during
/// instantiation.
pub table_lazy_init: bool,

/// Indicates whether an address map from compiled native code back to wasm
/// offsets in the original file is generated.
pub generate_address_map: bool,
Expand Down Expand Up @@ -113,6 +119,7 @@ impl Tunables {
epoch_interruption: false,
static_memory_bound_is_maximum: false,
guard_before_linear_memory: true,
table_lazy_init: true,
generate_address_map: true,
debug_adapter_modules: false,
relaxed_simd_deterministic: false,
Expand Down
4 changes: 4 additions & 0 deletions crates/fuzzing/src/generators/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,9 @@ impl Config {
}

cfg.cranelift_pcc(pcc);

// Eager init is currently only supported on Cranelift, not Winch.
cfg.table_lazy_init(self.wasmtime.table_lazy_init);
}

self.wasmtime.async_config.configure(&mut cfg);
Expand Down Expand Up @@ -483,6 +486,7 @@ pub struct WasmtimeConfig {
cache_call_indirects: bool,
/// The maximum number of call-indirect cache slots.
max_call_indirect_cache_slots: usize,
table_lazy_init: bool,

/// Whether or not fuzzing should enable PCC.
pcc: bool,
Expand Down
4 changes: 3 additions & 1 deletion crates/wasmtime/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -807,7 +807,9 @@ impl FunctionIndices {
// Attempt to convert table initializer segments to
// FuncTable representation where possible, to enable
// table lazy init.
translation.try_func_table_init();
if engine.tunables().table_lazy_init {
translation.try_func_table_init();
}

let funcs: PrimaryMap<DefinedFuncIndex, CompiledFunctionInfo> =
wasm_functions_for_module(&mut wasm_functions, module)
Expand Down
19 changes: 19 additions & 0 deletions crates/wasmtime/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ struct ConfigTunables {
epoch_interruption: Option<bool>,
static_memory_bound_is_maximum: Option<bool>,
guard_before_linear_memory: Option<bool>,
table_lazy_init: Option<bool>,
generate_address_map: Option<bool>,
debug_adapter_modules: Option<bool>,
relaxed_simd_deterministic: Option<bool>,
Expand Down Expand Up @@ -1541,6 +1542,19 @@ impl Config {
self
}

/// Indicates whether to initialize tables lazily, so that instantiation
/// is fast but indirect calls are a little slower. If false, tables
/// are initialized eagerly during instantiation from any active element
/// segments that apply to them.
///
/// ## Default
///
/// This value defaults to `true`.
pub fn table_lazy_init(&mut self, table_lazy_init: bool) -> &mut Self {
self.tunables.table_lazy_init = Some(table_lazy_init);
self
}

/// Configure the version information used in serialized and deserialzied [`crate::Module`]s.
/// This effects the behavior of [`crate::Module::serialize()`], as well as
/// [`crate::Module::deserialize()`] and related functions.
Expand Down Expand Up @@ -1810,6 +1824,7 @@ impl Config {
epoch_interruption
static_memory_bound_is_maximum
guard_before_linear_memory
table_lazy_init
generate_address_map
debug_adapter_modules
relaxed_simd_deterministic
Expand All @@ -1830,6 +1845,10 @@ impl Config {
if tunables.winch_callable && tunables.tail_callable {
bail!("Winch does not support the WebAssembly tail call proposal");
}

if tunables.winch_callable && !tunables.table_lazy_init {
bail!("Winch requires the table-lazy-init configuration option");
}
}

if tunables.static_memory_offset_guard_size < tunables.dynamic_memory_offset_guard_size {
Expand Down
2 changes: 2 additions & 0 deletions crates/wasmtime/src/engine/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ impl Metadata<'_> {
epoch_interruption,
static_memory_bound_is_maximum,
guard_before_linear_memory,
table_lazy_init,
relaxed_simd_deterministic,
tail_callable,
winch_callable,
Expand Down Expand Up @@ -415,6 +416,7 @@ impl Metadata<'_> {
other.guard_before_linear_memory,
"guard before linear memory",
)?;
Self::check_bool(table_lazy_init, other.table_lazy_init, "table lazy init")?;
Self::check_bool(
relaxed_simd_deterministic,
other.relaxed_simd_deterministic,
Expand Down
Loading

0 comments on commit 7ea9201

Please sign in to comment.