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}