soroban_wasmi/engine/
config.rs

1use super::{EnforcedLimits, StackLimits};
2use crate::core::UntypedVal;
3use core::{mem::size_of, num::NonZeroU64};
4use wasmparser::WasmFeatures;
5
6/// The default amount of stacks kept in the cache at most.
7const DEFAULT_CACHED_STACKS: usize = 2;
8
9/// Configuration for an [`Engine`].
10///
11/// [`Engine`]: [`crate::Engine`]
12#[derive(Debug, Copy, Clone)]
13pub struct Config {
14    /// The limits set on the value stack and call stack.
15    stack_limits: StackLimits,
16    /// The amount of Wasm stacks to keep in cache at most.
17    cached_stacks: usize,
18    /// Is `true` if the `mutable-global` Wasm proposal is enabled.
19    mutable_global: bool,
20    /// Is `true` if the `sign-extension` Wasm proposal is enabled.
21    sign_extension: bool,
22    /// Is `true` if the `saturating-float-to-int` Wasm proposal is enabled.
23    saturating_float_to_int: bool,
24    /// Is `true` if the [`multi-value`] Wasm proposal is enabled.
25    multi_value: bool,
26    /// Is `true` if the [`bulk-memory`] Wasm proposal is enabled.
27    bulk_memory: bool,
28    /// Is `true` if the [`reference-types`] Wasm proposal is enabled.
29    reference_types: bool,
30    /// Is `true` if the [`tail-call`] Wasm proposal is enabled.
31    tail_call: bool,
32    /// Is `true` if the [`extended-const`] Wasm proposal is enabled.
33    extended_const: bool,
34    /// Is `true` if Wasm instructions on `f32` and `f64` types are allowed.
35    floats: bool,
36    /// Is `true` if Wasmi executions shall consume fuel.
37    consume_fuel: bool,
38    /// Is `true` if Wasmi shall ignore Wasm custom sections when parsing Wasm modules.
39    ignore_custom_sections: bool,
40    /// The configured fuel costs of all Wasmi bytecode instructions.
41    fuel_costs: FuelCosts,
42    /// The mode of Wasm to Wasmi bytecode compilation.
43    compilation_mode: CompilationMode,
44    /// Enforced limits for Wasm module parsing and compilation.
45    limits: EnforcedLimits,
46}
47
48/// Type storing all kinds of fuel costs of instructions.
49#[derive(Debug, Copy, Clone)]
50pub struct FuelCosts {
51    /// The base fuel costs for all instructions.
52    base: u64,
53    /// The register copies that can be performed per unit of fuel.
54    copies_per_fuel: NonZeroU64,
55    /// The bytes that can be copied per unit of fuel.
56    bytes_per_fuel: NonZeroU64,
57}
58
59impl FuelCosts {
60    /// Returns the base fuel costs for all Wasmi IR instructions.
61    pub fn base(&self) -> u64 {
62        self.base
63    }
64
65    /// Returns the base fuel costs for all Wasmi IR entity related instructions.
66    pub fn entity(&self) -> u64 {
67        // Note: For simplicity we currently simply use base costs.
68        self.base
69    }
70
71    /// Returns the base fuel costs for all Wasmi IR load instructions.
72    pub fn load(&self) -> u64 {
73        // Note: For simplicity we currently simply use base costs.
74        self.base
75    }
76
77    /// Returns the base fuel costs for all Wasmi IR store instructions.
78    pub fn store(&self) -> u64 {
79        // Note: For simplicity we currently simply use base costs.
80        self.base
81    }
82
83    /// Returns the base fuel costs for all Wasmi IR call instructions.
84    pub fn call(&self) -> u64 {
85        // Note: For simplicity we currently simply use base costs.
86        self.base
87    }
88
89    /// Returns the number of register copies performed per unit of fuel.
90    fn copies_per_fuel(&self) -> NonZeroU64 {
91        self.copies_per_fuel
92    }
93
94    /// Returns the number of byte copies performed per unit of fuel.
95    fn bytes_per_fuel(&self) -> NonZeroU64 {
96        self.bytes_per_fuel
97    }
98
99    /// Returns the fuel costs for `len_copies` register copies in Wasmi IR.
100    ///
101    /// # Note
102    ///
103    /// Registers are copied for the following Wasmi IR instructions:
104    ///
105    /// - calls (parameter passing)
106    /// - `copy_span`
107    /// - `copy_many`
108    /// - `return_span`
109    /// - `return_many`
110    /// - `table.grow` (+ variants)
111    /// - `table.copy` (+ variants)
112    /// - `table.fill` (+ variants)
113    /// - `table.init` (+ variants)
114    pub fn fuel_for_copies(&self, len_copies: u64) -> u64 {
115        Self::costs_per(len_copies, self.copies_per_fuel())
116    }
117
118    /// Returns the fuel costs for `len_copies` register copies in Wasmi IR.
119    ///
120    /// # Note
121    ///
122    /// Registers are copied for the following Wasmi IR instructions:
123    ///
124    /// - `memory.grow`
125    /// - `memory.copy`
126    /// - `memory.fill`
127    /// - `memory.init`
128    pub fn fuel_for_bytes(&self, len_bytes: u64) -> u64 {
129        Self::costs_per(len_bytes, self.bytes_per_fuel())
130    }
131
132    /// Returns the fuel consumption of the amount of items with costs per items.
133    fn costs_per(len_items: u64, items_per_fuel: NonZeroU64) -> u64 {
134        len_items / items_per_fuel
135    }
136}
137
138impl Default for FuelCosts {
139    fn default() -> Self {
140        let bytes_per_fuel = 64;
141        let bytes_per_register = size_of::<UntypedVal>() as u64;
142        let registers_per_fuel = bytes_per_fuel / bytes_per_register;
143        Self {
144            base: 1,
145            copies_per_fuel: NonZeroU64::new(registers_per_fuel)
146                .unwrap_or_else(|| panic!("invalid zero value for copies_per_fuel value")),
147            bytes_per_fuel: NonZeroU64::new(bytes_per_fuel)
148                .unwrap_or_else(|| panic!("invalid zero value for copies_per_fuel value")),
149        }
150    }
151}
152
153/// The chosen mode of Wasm to Wasmi bytecode compilation.
154#[derive(Debug, Default, Copy, Clone)]
155pub enum CompilationMode {
156    /// The Wasm code is compiled eagerly to Wasmi bytecode.
157    #[default]
158    Eager,
159    /// The Wasm code is validated eagerly and translated lazily on first use.
160    LazyTranslation,
161    /// The Wasm code is validated and translated lazily on first use.
162    ///
163    /// # Note
164    ///
165    /// This mode must not be used if the result of Wasm execution
166    /// must be deterministic amongst multiple Wasm implementations.
167    Lazy,
168}
169
170impl Default for Config {
171    fn default() -> Self {
172        Self {
173            stack_limits: StackLimits::default(),
174            cached_stacks: DEFAULT_CACHED_STACKS,
175            mutable_global: true,
176            sign_extension: true,
177            saturating_float_to_int: true,
178            multi_value: true,
179            bulk_memory: true,
180            reference_types: true,
181            tail_call: true,
182            extended_const: true,
183            floats: true,
184            consume_fuel: false,
185            ignore_custom_sections: false,
186            fuel_costs: FuelCosts::default(),
187            compilation_mode: CompilationMode::default(),
188            limits: EnforcedLimits::default(),
189        }
190    }
191}
192
193impl Config {
194    /// Sets the [`StackLimits`] for the [`Config`].
195    pub fn set_stack_limits(&mut self, stack_limits: StackLimits) -> &mut Self {
196        self.stack_limits = stack_limits;
197        self
198    }
199
200    /// Returns the [`StackLimits`] of the [`Config`].
201    pub(super) fn stack_limits(&self) -> StackLimits {
202        self.stack_limits
203    }
204
205    /// Sets the maximum amount of cached stacks for reuse for the [`Config`].
206    ///
207    /// # Note
208    ///
209    /// Defaults to 2.
210    pub fn set_cached_stacks(&mut self, amount: usize) -> &mut Self {
211        self.cached_stacks = amount;
212        self
213    }
214
215    /// Returns the maximum amount of cached stacks for reuse of the [`Config`].
216    pub(super) fn cached_stacks(&self) -> usize {
217        self.cached_stacks
218    }
219
220    /// Enable or disable the [`mutable-global`] Wasm proposal for the [`Config`].
221    ///
222    /// # Note
223    ///
224    /// Enabled by default.
225    ///
226    /// [`mutable-global`]: https://github.com/WebAssembly/mutable-global
227    pub fn wasm_mutable_global(&mut self, enable: bool) -> &mut Self {
228        self.mutable_global = enable;
229        self
230    }
231
232    /// Enable or disable the [`sign-extension`] Wasm proposal for the [`Config`].
233    ///
234    /// # Note
235    ///
236    /// Enabled by default.
237    ///
238    /// [`sign-extension`]: https://github.com/WebAssembly/sign-extension-ops
239    pub fn wasm_sign_extension(&mut self, enable: bool) -> &mut Self {
240        self.sign_extension = enable;
241        self
242    }
243
244    /// Enable or disable the [`saturating-float-to-int`] Wasm proposal for the [`Config`].
245    ///
246    /// # Note
247    ///
248    /// Enabled by default.
249    ///
250    /// [`saturating-float-to-int`]:
251    /// https://github.com/WebAssembly/nontrapping-float-to-int-conversions
252    pub fn wasm_saturating_float_to_int(&mut self, enable: bool) -> &mut Self {
253        self.saturating_float_to_int = enable;
254        self
255    }
256
257    /// Enable or disable the [`multi-value`] Wasm proposal for the [`Config`].
258    ///
259    /// # Note
260    ///
261    /// Enabled by default.
262    ///
263    /// [`multi-value`]: https://github.com/WebAssembly/multi-value
264    pub fn wasm_multi_value(&mut self, enable: bool) -> &mut Self {
265        self.multi_value = enable;
266        self
267    }
268
269    /// Enable or disable the [`bulk-memory`] Wasm proposal for the [`Config`].
270    ///
271    /// # Note
272    ///
273    /// Enabled by default.
274    ///
275    /// [`bulk-memory`]: https://github.com/WebAssembly/bulk-memory-operations
276    pub fn wasm_bulk_memory(&mut self, enable: bool) -> &mut Self {
277        self.bulk_memory = enable;
278        self
279    }
280
281    /// Enable or disable the [`reference-types`] Wasm proposal for the [`Config`].
282    ///
283    /// # Note
284    ///
285    /// Enabled by default.
286    ///
287    /// [`reference-types`]: https://github.com/WebAssembly/reference-types
288    pub fn wasm_reference_types(&mut self, enable: bool) -> &mut Self {
289        self.reference_types = enable;
290        self
291    }
292
293    /// Enable or disable the [`tail-call`] Wasm proposal for the [`Config`].
294    ///
295    /// # Note
296    ///
297    /// Enabled by default.
298    ///
299    /// [`tail-call`]: https://github.com/WebAssembly/tail-call
300    pub fn wasm_tail_call(&mut self, enable: bool) -> &mut Self {
301        self.tail_call = enable;
302        self
303    }
304
305    /// Enable or disable the [`extended-const`] Wasm proposal for the [`Config`].
306    ///
307    /// # Note
308    ///
309    /// Enabled by default.
310    ///
311    /// [`extended-const`]: https://github.com/WebAssembly/extended-const
312    pub fn wasm_extended_const(&mut self, enable: bool) -> &mut Self {
313        self.extended_const = enable;
314        self
315    }
316
317    /// Enable or disable Wasm floating point (`f32` and `f64`) instructions and types.
318    ///
319    /// Enabled by default.
320    pub fn floats(&mut self, enable: bool) -> &mut Self {
321        self.floats = enable;
322        self
323    }
324
325    /// Configures whether Wasmi will consume fuel during execution to either halt execution as desired.
326    ///
327    /// # Note
328    ///
329    /// This configuration can be used to make Wasmi instrument its internal bytecode
330    /// so that it consumes fuel as it executes. Once an execution runs out of fuel
331    /// a [`TrapCode::OutOfFuel`](crate::core::TrapCode::OutOfFuel) trap is raised.
332    /// This way users can deterministically halt or yield the execution of WebAssembly code.
333    ///
334    /// - Use [`Store::set_fuel`](crate::Store::set_fuel) to set the remaining fuel of the [`Store`] before
335    ///   executing some code as the [`Store`] start with no fuel.
336    /// - Use [`Caller::set_fuel`](crate::Caller::set_fuel) to update the remaining fuel when executing host functions.
337    ///
338    /// Disabled by default.
339    ///
340    /// [`Store`]: crate::Store
341    /// [`Engine`]: crate::Engine
342    pub fn consume_fuel(&mut self, enable: bool) -> &mut Self {
343        self.consume_fuel = enable;
344        self
345    }
346
347    /// Returns `true` if the [`Config`] enables fuel consumption by the [`Engine`].
348    ///
349    /// [`Engine`]: crate::Engine
350    pub(crate) fn get_consume_fuel(&self) -> bool {
351        self.consume_fuel
352    }
353
354    /// Configures whether Wasmi will ignore custom sections when parsing Wasm modules.
355    ///
356    /// Default value: `false`
357    pub fn ignore_custom_sections(&mut self, enable: bool) -> &mut Self {
358        self.ignore_custom_sections = enable;
359        self
360    }
361
362    /// Returns `true` if the [`Config`] mandates to ignore Wasm custom sections when parsing Wasm modules.
363    pub(crate) fn get_ignore_custom_sections(&self) -> bool {
364        self.ignore_custom_sections
365    }
366
367    /// Returns the configured [`FuelCosts`].
368    pub(crate) fn fuel_costs(&self) -> &FuelCosts {
369        &self.fuel_costs
370    }
371
372    /// Sets the [`CompilationMode`] used for the [`Engine`].
373    ///
374    /// By default [`CompilationMode::Eager`] is used.
375    ///
376    /// [`Engine`]: crate::Engine
377    pub fn compilation_mode(&mut self, mode: CompilationMode) -> &mut Self {
378        self.compilation_mode = mode;
379        self
380    }
381
382    /// Returns the [`CompilationMode`] used for the [`Engine`].
383    ///
384    /// [`Engine`]: crate::Engine
385    pub(super) fn get_compilation_mode(&self) -> CompilationMode {
386        self.compilation_mode
387    }
388
389    /// Sets the [`EnforcedLimits`] enforced by the [`Engine`] for Wasm module parsing and compilation.
390    ///
391    /// By default no limits are enforced.
392    ///
393    /// [`Engine`]: crate::Engine
394    pub fn enforced_limits(&mut self, limits: EnforcedLimits) -> &mut Self {
395        self.limits = limits;
396        self
397    }
398
399    /// Returns the [`EnforcedLimits`] used for the [`Engine`].
400    ///
401    /// [`Engine`]: crate::Engine
402    pub(crate) fn get_enforced_limits(&self) -> &EnforcedLimits {
403        &self.limits
404    }
405
406    pub fn set_fuel_costs(&mut self, costs: FuelCosts) -> &mut Self {
407        self.fuel_costs = costs;
408        self
409    }
410
411    /// Returns the [`WasmFeatures`] represented by the [`Config`].
412    pub(crate) fn wasm_features(&self) -> WasmFeatures {
413        WasmFeatures {
414            multi_value: self.multi_value,
415            mutable_global: self.mutable_global,
416            saturating_float_to_int: self.saturating_float_to_int,
417            sign_extension: self.sign_extension,
418            bulk_memory: self.bulk_memory,
419            reference_types: self.reference_types,
420            tail_call: self.tail_call,
421            extended_const: self.extended_const,
422            floats: self.floats,
423            component_model: false,
424            simd: false,
425            relaxed_simd: false,
426            threads: false,
427            multi_memory: false,
428            exceptions: false,
429            memory64: false,
430            memory_control: false,
431        }
432    }
433}