Skip to content

Commit 5c1f78a

Browse files
feat: make init stack size configurable (#44)
1 parent 749ebee commit 5c1f78a

File tree

7 files changed

+176
-21
lines changed

7 files changed

+176
-21
lines changed

crates/tinywasm/src/config.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
use core::fmt;
2+
3+
/// Default initial size for the 32-bit value stack (i32, f32 values).
4+
pub const DEFAULT_VALUE_STACK_32_INIT_SIZE: usize = 32 * 1024; // 32KB
5+
6+
/// Default initial size for the 64-bit value stack (i64, f64 values).
7+
pub const DEFAULT_VALUE_STACK_64_INIT_SIZE: usize = 16 * 1024; // 16KB
8+
9+
/// Default initial size for the 128-bit value stack (v128 values).
10+
pub const DEFAULT_VALUE_STACK_128_INIT_SIZE: usize = 8 * 1024; // 8KB
11+
12+
/// Default initial size for the reference value stack (funcref, externref values).
13+
pub const DEFAULT_VALUE_STACK_REF_INIT_SIZE: usize = 1024; // 1KB
14+
15+
/// Default initial size for the block stack.
16+
pub const DEFAULT_BLOCK_STACK_INIT_SIZE: usize = 128;
17+
18+
/// Configuration for the WebAssembly interpreter's stack preallocation.
19+
///
20+
/// This struct allows you to configure how much space is preallocated for the
21+
/// different parts of the stack that the interpreter uses to store values.
22+
#[derive(Debug, Clone)]
23+
pub struct StackConfig {
24+
value_stack_32_init_size: Option<usize>,
25+
value_stack_64_init_size: Option<usize>,
26+
value_stack_128_init_size: Option<usize>,
27+
value_stack_ref_init_size: Option<usize>,
28+
block_stack_init_size: Option<usize>,
29+
}
30+
31+
impl StackConfig {
32+
/// Create a new stack configuration with default settings.
33+
pub fn new() -> Self {
34+
Self {
35+
value_stack_32_init_size: None,
36+
value_stack_64_init_size: None,
37+
value_stack_128_init_size: None,
38+
value_stack_ref_init_size: None,
39+
block_stack_init_size: None,
40+
}
41+
}
42+
43+
/// Get the initial size for the 32-bit value stack.
44+
pub fn value_stack_32_init_size(&self) -> usize {
45+
self.value_stack_32_init_size.unwrap_or(DEFAULT_VALUE_STACK_32_INIT_SIZE)
46+
}
47+
48+
/// Get the initial size for the 64-bit value stack.
49+
pub fn value_stack_64_init_size(&self) -> usize {
50+
self.value_stack_64_init_size.unwrap_or(DEFAULT_VALUE_STACK_64_INIT_SIZE)
51+
}
52+
53+
/// Get the initial size for the 128-bit value stack.
54+
pub fn value_stack_128_init_size(&self) -> usize {
55+
self.value_stack_128_init_size.unwrap_or(DEFAULT_VALUE_STACK_128_INIT_SIZE)
56+
}
57+
58+
/// Get the initial size for the reference value stack.
59+
pub fn value_stack_ref_init_size(&self) -> usize {
60+
self.value_stack_ref_init_size.unwrap_or(DEFAULT_VALUE_STACK_REF_INIT_SIZE)
61+
}
62+
63+
/// Get the initial size for the block stack.
64+
pub fn block_stack_init_size(&self) -> usize {
65+
self.block_stack_init_size.unwrap_or(DEFAULT_BLOCK_STACK_INIT_SIZE)
66+
}
67+
68+
/// Set the initial capacity for the 32-bit value stack.
69+
pub fn with_value_stack_32_init_size(mut self, capacity: usize) -> Self {
70+
self.value_stack_32_init_size = Some(capacity);
71+
self
72+
}
73+
74+
/// Set the initial capacity for the 64-bit value stack.
75+
pub fn with_value_stack_64_init_size(mut self, capacity: usize) -> Self {
76+
self.value_stack_64_init_size = Some(capacity);
77+
self
78+
}
79+
80+
/// Set the initial capacity for the 128-bit value stack.
81+
pub fn with_value_stack_128_init_size(mut self, capacity: usize) -> Self {
82+
self.value_stack_128_init_size = Some(capacity);
83+
self
84+
}
85+
86+
/// Set the initial capacity for the reference value stack.
87+
pub fn with_value_stack_ref_init_size(mut self, capacity: usize) -> Self {
88+
self.value_stack_ref_init_size = Some(capacity);
89+
self
90+
}
91+
92+
/// Set the initial capacity for the block stack.
93+
pub fn with_block_stack_init_size(mut self, capacity: usize) -> Self {
94+
self.block_stack_init_size = Some(capacity);
95+
self
96+
}
97+
}
98+
99+
impl Default for StackConfig {
100+
fn default() -> Self {
101+
Self::new()
102+
}
103+
}
104+
105+
impl fmt::Display for StackConfig {
106+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107+
write!(f, "StackConfig {{ ")?;
108+
write!(f, "value_stack_32: {}, ", self.value_stack_32_init_size())?;
109+
write!(f, "value_stack_64: {}, ", self.value_stack_64_init_size())?;
110+
write!(f, "value_stack_128: {}, ", self.value_stack_128_init_size())?;
111+
write!(f, "value_stack_ref: {}, ", self.value_stack_ref_init_size())?;
112+
write!(f, "block_stack: {} }}", self.block_stack_init_size())?;
113+
Ok(())
114+
}
115+
}

crates/tinywasm/src/func.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ impl FuncHandle {
6363

6464
// 7. Push the frame f to the call stack
6565
// & 8. Push the values to the stack (Not needed since the call frame owns the values)
66-
let mut stack = Stack::new(call_frame);
66+
let mut stack = Stack::new(call_frame, &store.config);
6767

6868
// 9. Invoke the function instance
6969
let runtime = store.runtime();

crates/tinywasm/src/interpreter/stack/block_stack.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
1-
use crate::unlikely;
1+
use crate::{StackConfig, unlikely};
22
use alloc::vec::Vec;
33

44
use crate::interpreter::values::{StackHeight, StackLocation};
55

66
#[derive(Debug)]
77
pub(crate) struct BlockStack(Vec<BlockFrame>);
88

9-
impl Default for BlockStack {
10-
fn default() -> Self {
11-
Self(Vec::with_capacity(128))
9+
impl BlockStack {
10+
pub(crate) fn new(config: &StackConfig) -> Self {
11+
Self(Vec::with_capacity(config.block_stack_init_size()))
1212
}
13-
}
1413

15-
impl BlockStack {
1614
#[inline(always)]
1715
pub(crate) fn len(&self) -> usize {
1816
self.0.len()

crates/tinywasm/src/interpreter/stack/mod.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ pub(crate) use block_stack::{BlockFrame, BlockStack, BlockType};
66
pub(crate) use call_stack::{CallFrame, CallStack, Locals};
77
pub(crate) use value_stack::ValueStack;
88

9+
use crate::StackConfig;
10+
911
/// A WebAssembly Stack
1012
#[derive(Debug)]
1113
pub(crate) struct Stack {
@@ -15,7 +17,11 @@ pub(crate) struct Stack {
1517
}
1618

1719
impl Stack {
18-
pub(crate) fn new(call_frame: CallFrame) -> Self {
19-
Self { values: ValueStack::new(), blocks: BlockStack::default(), call_stack: CallStack::new(call_frame) }
20+
pub(crate) fn new(call_frame: CallFrame, config: &StackConfig) -> Self {
21+
Self {
22+
values: ValueStack::new(config),
23+
blocks: BlockStack::new(config),
24+
call_stack: CallStack::new(call_frame),
25+
}
2026
}
2127
}

crates/tinywasm/src/interpreter/stack/value_stack.rs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
use alloc::vec::Vec;
22
use tinywasm_types::{ExternRef, FuncRef, ValType, ValueCounts, ValueCountsSmall, WasmValue};
33

4-
use crate::{Result, interpreter::*};
4+
use crate::{Result, StackConfig, interpreter::*};
55

66
use super::Locals;
7-
pub(crate) const STACK_32_SIZE: usize = 1024 * 32;
8-
pub(crate) const STACK_64_SIZE: usize = 1024 * 16;
9-
pub(crate) const STACK_128_SIZE: usize = 1024 * 8;
10-
pub(crate) const STACK_REF_SIZE: usize = 1024;
117

128
#[derive(Debug)]
139
pub(crate) struct ValueStack {
@@ -18,12 +14,12 @@ pub(crate) struct ValueStack {
1814
}
1915

2016
impl ValueStack {
21-
pub(crate) fn new() -> Self {
17+
pub(crate) fn new(config: &StackConfig) -> Self {
2218
Self {
23-
stack_32: Vec::with_capacity(STACK_32_SIZE),
24-
stack_64: Vec::with_capacity(STACK_64_SIZE),
25-
stack_128: Vec::with_capacity(STACK_128_SIZE),
26-
stack_ref: Vec::with_capacity(STACK_REF_SIZE),
19+
stack_32: Vec::with_capacity(config.value_stack_32_init_size()),
20+
stack_64: Vec::with_capacity(config.value_stack_64_init_size()),
21+
stack_128: Vec::with_capacity(config.value_stack_128_init_size()),
22+
stack_ref: Vec::with_capacity(config.value_stack_ref_init_size()),
2723
}
2824
}
2925

crates/tinywasm/src/lib.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,29 @@
6969
//! and other modules to be linked into the module when it is instantiated.
7070
//!
7171
//! See the [`Imports`] documentation for more information.
72+
//!
73+
//! ## Runtime Configuration
74+
//!
75+
//! For resource-constrained targets, you can configure the initial memory allocation:
76+
//!
77+
//! ```rust
78+
//! use tinywasm::{Store, StackConfig};
79+
//!
80+
//! // Create a store with minimal initial allocation (90% reduction in pre-allocated memory)
81+
//! let config = StackConfig::new()
82+
//! .with_value_stack_32_init_size(1024) // 1KB instead of 32KB
83+
//! .with_value_stack_64_init_size(512) // 512B instead of 16KB
84+
//! .with_value_stack_128_init_size(256) // 256B instead of 8KB
85+
//! .with_value_stack_ref_init_size(128) // 128B instead of 1KB
86+
87+
//! .with_block_stack_init_size(32); // 32 instead of 128
88+
//! let store = Store::with_config(config);
89+
//!
90+
//! // Or create a partial configuration (only override what you need)
91+
//! let config = StackConfig::new()
92+
//! .with_value_stack_32_init_size(2048); // Only override 32-bit stack size
93+
//! let store = Store::with_config(config);
94+
//! ```
7295
7396
mod std;
7497
extern crate alloc;
@@ -110,6 +133,10 @@ mod store;
110133
pub mod interpreter;
111134
pub use interpreter::InterpreterRuntime;
112135

136+
/// Configuration for the WebAssembly interpreter's stack preallocation.
137+
pub mod config;
138+
pub use config::StackConfig;
139+
113140
#[cfg(feature = "parser")]
114141
/// Re-export of [`tinywasm_parser`]. Requires `parser` feature.
115142
pub mod parser {

crates/tinywasm/src/store/mod.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use core::sync::atomic::{AtomicUsize, Ordering};
44
use tinywasm_types::*;
55

66
use crate::interpreter::{self, InterpreterRuntime, TinyWasmValue};
7-
use crate::{Error, Function, ModuleInstance, Result, Trap, cold};
7+
use crate::{Error, Function, ModuleInstance, Result, StackConfig, Trap, cold};
88

99
mod data;
1010
mod element;
@@ -33,6 +33,7 @@ pub struct Store {
3333

3434
pub(crate) data: StoreData,
3535
pub(crate) runtime: Runtime,
36+
pub(crate) config: StackConfig,
3637
}
3738

3839
impl Debug for Store {
@@ -57,6 +58,12 @@ impl Store {
5758
Self::default()
5859
}
5960

61+
/// Create a new store with the given stack configuration
62+
pub fn with_config(config: StackConfig) -> Self {
63+
let id = STORE_ID.fetch_add(1, Ordering::Relaxed);
64+
Self { id, module_instances: Vec::new(), data: StoreData::default(), runtime: Runtime::Default, config }
65+
}
66+
6067
/// Get a module instance by the internal id
6168
pub fn get_module_instance(&self, addr: ModuleInstanceAddr) -> Option<&ModuleInstance> {
6269
self.module_instances.get(addr as usize)
@@ -83,7 +90,13 @@ impl PartialEq for Store {
8390
impl Default for Store {
8491
fn default() -> Self {
8592
let id = STORE_ID.fetch_add(1, Ordering::Relaxed);
86-
Self { id, module_instances: Vec::new(), data: StoreData::default(), runtime: Runtime::Default }
93+
Self {
94+
id,
95+
module_instances: Vec::new(),
96+
data: StoreData::default(),
97+
runtime: Runtime::Default,
98+
config: StackConfig::default(),
99+
}
87100
}
88101
}
89102

0 commit comments

Comments
 (0)