Skip to content
This repository was archived by the owner on Oct 3, 2025. It is now read-only.

Commit 0302008

Browse files
chore: optimize block stack, remove EndFunc (#12)
Signed-off-by: Henry Gressmann <[email protected]>
1 parent 398b2af commit 0302008

File tree

22 files changed

+97
-75
lines changed

22 files changed

+97
-75
lines changed

.github/workflows/test.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ jobs:
4747
run: cargo +stable build --workspace
4848

4949
- name: Run tests (stable)
50-
run: cargo +stable test --workspace
50+
run: cargo +stable test --workspace && cargo +stable run --example wasm-rust all
5151

5252
- name: Run MVP testsuite
5353
run: cargo +stable test-mvp
@@ -75,7 +75,7 @@ jobs:
7575
run: cargo +nightly build --workspace --no-default-features
7676

7777
- name: Run tests (nightly, no default features)
78-
run: cargo +nightly test --workspace --no-default-features
78+
run: cargo +nightly test --workspace --no-default-features && cargo +nightly run --example wasm-rust all
7979

8080
- name: Run MVP testsuite (nightly)
8181
run: cargo +nightly test-mvp

CHANGELOG.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12+
- `Imports` and `Module` are now cloneable (#9)
13+
1214
### Changed
1315

14-
- Improved documentation and added more tests
15-
- Tests can now be run on more targets
16-
- Nightly version has been updated to fix broken builds in some cases
17-
- Enhance support for scripted language bindings by making Imports and Module cloneable
18-
- Add `aarch64-apple-darwin` and `armv7-unknown-linux-gnueabihf` targets to CI
16+
- Improved documentation and added more tests (735c7cb636edfd4704460c94a9c7d65e5bf4df48)
17+
- Tests can now be run on more targets (#11)
18+
- Nightly version has been updated to fix broken builds in some cases (#12)
19+
- Add `aarch64-apple-darwin` and `armv7-unknown-linux-gnueabihf` targets to CI (#12)
1920

2021
### Removed
2122

23+
- Removed the `EndFunc` instruction, as it was already covered by the `Return` instruction\
24+
This also fixes a weird bug that only occurred on certain nightly versions of Rust
25+
2226
## [0.5.0] - 2024-03-01
2327

2428
**All Commits**: https://github.com/explodingcamera/tinywasm/compare/v0.4.0...v0.5.0

crates/cli/src/bin.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use argh::FromArgs;
44
use args::WasmArg;
55
use color_eyre::eyre::Result;
66
use log::{debug, info};
7-
use tinywasm::{self, types::WasmValue, Module};
7+
use tinywasm::{types::WasmValue, Module};
88

99
use crate::args::to_wasm_args;
1010
mod args;

crates/parser/src/visit.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ impl<'a> wasmparser::VisitOperator<'a> for FunctionBuilder {
392392

393393
fn visit_end(&mut self) -> Self::Output {
394394
let Some(label_pointer) = self.label_ptrs.pop() else {
395-
return self.visit(Instruction::EndFunc);
395+
return self.visit(Instruction::Return);
396396
};
397397

398398
let current_instr_ptr = self.instructions.len();

crates/tinywasm/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ name="tinywasm"
1313
path="src/lib.rs"
1414

1515
[dependencies]
16-
log={version="0.4", optional=true}
16+
_log={version="0.4", optional=true, package="log"}
1717
tinywasm-parser={version="0.5.0", path="../parser", default-features=false, optional=true}
1818
tinywasm-types={version="0.5.0", path="../types", default-features=false}
1919
libm={version="0.2", default-features=false}
@@ -29,7 +29,7 @@ pretty_env_logger="0.5"
2929

3030
[features]
3131
default=["std", "parser", "logging", "archive"]
32-
logging=["log", "tinywasm-parser?/logging", "tinywasm-types/logging"]
32+
logging=["_log", "tinywasm-parser?/logging", "tinywasm-types/logging"]
3333
std=["tinywasm-parser?/std", "tinywasm-types/std"]
3434
parser=["tinywasm-parser"]
3535
unsafe=["tinywasm-types/unsafe"]

crates/tinywasm/src/imports.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ impl From<&Import> for ExternName {
191191
///
192192
/// ## Example
193193
/// ```rust
194+
/// # use _log as log;
194195
/// # fn main() -> tinywasm::Result<()> {
195196
/// use tinywasm::{Imports, Extern};
196197
/// use tinywasm::types::{ValType, TableType, MemoryType, WasmValue};

crates/tinywasm/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ extern crate alloc;
7979
// log for logging (optional).
8080
#[cfg(feature = "logging")]
8181
#[allow(clippy::single_component_path_imports)]
82-
use log;
82+
use _log as log;
8383

8484
// noop fallback if logging is disabled.
8585
#[cfg(not(feature = "logging"))]

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

Lines changed: 20 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ enum ExecResult {
8080
fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &ModuleInstance) -> Result<ExecResult> {
8181
let instrs = &cf.func_instance.0.instructions;
8282

83-
if unlikely(cf.instr_ptr >= instrs.len() || instrs.is_empty()) {
83+
if unlikely(cf.instr_ptr as usize >= instrs.len() || instrs.is_empty()) {
8484
log::error!("instr_ptr out of bounds: {} >= {}", cf.instr_ptr, instrs.len());
8585
return Err(Error::Other(format!("instr_ptr out of bounds: {} >= {}", cf.instr_ptr, instrs.len())));
8686
}
@@ -128,7 +128,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
128128
};
129129

130130
let params = stack.values.pop_n_rev(wasm_func.ty.params.len())?;
131-
let call_frame = CallFrame::new(wasm_func, func_inst.owner, params, stack.blocks.len());
131+
let call_frame = CallFrame::new(wasm_func, func_inst.owner, params, stack.blocks.len() as u32);
132132

133133
// push the call frame
134134
cf.instr_ptr += 1; // skip the call instruction
@@ -181,7 +181,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
181181
}
182182

183183
let params = stack.values.pop_n_rev(wasm_func.ty.params.len())?;
184-
let call_frame = CallFrame::new(wasm_func, func_inst.owner, params, stack.blocks.len());
184+
let call_frame = CallFrame::new(wasm_func, func_inst.owner, params, stack.blocks.len() as u32);
185185

186186
// push the call frame
187187
cf.instr_ptr += 1; // skip the call instruction
@@ -198,8 +198,8 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
198198
cf.enter_block(
199199
BlockFrame::new(
200200
cf.instr_ptr,
201-
cf.instr_ptr + *end_offset as usize,
202-
stack.values.len(),
201+
cf.instr_ptr + *end_offset,
202+
stack.values.len() as u32,
203203
BlockType::If,
204204
&args.unpack(),
205205
module,
@@ -213,26 +213,26 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
213213
// falsy value is on the top of the stack
214214
if *else_offset != 0 {
215215
let label = BlockFrame::new(
216-
cf.instr_ptr + *else_offset as usize,
217-
cf.instr_ptr + *end_offset as usize,
218-
stack.values.len(),
216+
cf.instr_ptr + *else_offset,
217+
cf.instr_ptr + *end_offset,
218+
stack.values.len() as u32,
219219
BlockType::Else,
220220
&args.unpack(),
221221
module,
222222
);
223-
cf.instr_ptr += *else_offset as usize;
223+
cf.instr_ptr += *else_offset;
224224
cf.enter_block(label, &mut stack.values, &mut stack.blocks);
225225
} else {
226-
cf.instr_ptr += *end_offset as usize;
226+
cf.instr_ptr += *end_offset;
227227
}
228228
}
229229

230230
Loop(args, end_offset) => {
231231
cf.enter_block(
232232
BlockFrame::new(
233233
cf.instr_ptr,
234-
cf.instr_ptr + *end_offset as usize,
235-
stack.values.len(),
234+
cf.instr_ptr + *end_offset,
235+
stack.values.len() as u32,
236236
BlockType::Loop,
237237
args,
238238
module,
@@ -246,8 +246,8 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
246246
cf.enter_block(
247247
BlockFrame::new(
248248
cf.instr_ptr,
249-
cf.instr_ptr + *end_offset as usize,
250-
stack.values.len(),
249+
cf.instr_ptr + *end_offset,
250+
stack.values.len() as u32,
251251
BlockType::Block,
252252
args,
253253
module,
@@ -258,7 +258,9 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
258258
}
259259

260260
BrTable(default, len) => {
261-
let instr = cf.instructions()[cf.instr_ptr + 1..cf.instr_ptr + 1 + *len as usize]
261+
let start = cf.instr_ptr + 1;
262+
let end = cf.instr_ptr + 1 + *len;
263+
let instr = cf.instructions()[start as usize..end as usize]
262264
.iter()
263265
.map(|i| match i {
264266
BrLabel(l) => Ok(*l),
@@ -294,24 +296,13 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
294296
false => return Ok(ExecResult::Call),
295297
},
296298

297-
EndFunc => {
298-
if unlikely(stack.blocks.len() != cf.block_ptr) {
299-
panic!("endfunc: block frames not empty, this should have been validated by the parser");
300-
}
301-
302-
match stack.call_stack.is_empty() {
303-
true => return Ok(ExecResult::Return),
304-
false => return Ok(ExecResult::Call),
305-
}
306-
}
307-
308299
// We're essentially using else as a EndBlockFrame instruction for if blocks
309300
Else(end_offset) => {
310301
let block =
311302
stack.blocks.pop().expect("else: no label to end, this should have been validated by the parser");
312303

313-
stack.values.truncate_keep(block.stack_ptr, block.results);
314-
cf.instr_ptr += *end_offset as usize;
304+
stack.values.truncate_keep(block.stack_ptr, block.results as u32);
305+
cf.instr_ptr += *end_offset;
315306
}
316307

317308
// remove the label from the label stack
@@ -321,7 +312,7 @@ fn exec_one(cf: &mut CallFrame, stack: &mut Stack, store: &mut Store, module: &M
321312
.pop()
322313
.expect("end blockframe: no label to end, this should have been validated by the parser");
323314

324-
stack.values.truncate_keep(block.stack_ptr, block.results);
315+
stack.values.truncate_keep(block.stack_ptr, block.results as u32);
325316
}
326317

327318
LocalGet(local_index) => stack.values.push(cf.get_local(*local_index as usize)),

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

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@ impl BlockStack {
1818

1919
#[inline]
2020
/// get the label at the given index, where 0 is the top of the stack
21-
pub(crate) fn get_relative_to(&self, index: usize, offset: usize) -> Option<&BlockFrame> {
22-
let len = self.0.len() - offset;
21+
pub(crate) fn get_relative_to(&self, index: u32, offset: u32) -> Option<&BlockFrame> {
22+
let len = (self.0.len() as u32) - offset;
2323

2424
// the vast majority of wasm functions don't use break to return
2525
if unlikely(index >= len) {
2626
return None;
2727
}
2828

29-
Some(&self.0[self.0.len() - index - 1])
29+
Some(&self.0[self.0.len() - index as usize - 1])
3030
}
3131

3232
#[inline]
@@ -36,31 +36,32 @@ impl BlockStack {
3636

3737
/// keep the top `len` blocks and discard the rest
3838
#[inline]
39-
pub(crate) fn truncate(&mut self, len: usize) {
40-
self.0.truncate(len);
39+
pub(crate) fn truncate(&mut self, len: u32) {
40+
self.0.truncate(len as usize);
4141
}
4242
}
4343

4444
#[derive(Debug, Clone)]
4545
pub(crate) struct BlockFrame {
4646
// position of the instruction pointer when the block was entered
47-
pub(crate) instr_ptr: usize,
47+
pub(crate) instr_ptr: u32,
4848
// position of the end instruction of the block
49-
pub(crate) end_instr_ptr: usize,
49+
pub(crate) end_instr_ptr: u32,
5050

5151
// position of the stack pointer when the block was entered
52-
pub(crate) stack_ptr: usize,
53-
pub(crate) results: usize,
54-
pub(crate) params: usize,
52+
pub(crate) stack_ptr: u32,
53+
54+
pub(crate) results: u8,
55+
pub(crate) params: u8,
5556
pub(crate) ty: BlockType,
5657
}
5758

5859
impl BlockFrame {
5960
#[inline]
6061
pub(crate) fn new(
61-
instr_ptr: usize,
62-
end_instr_ptr: usize,
63-
stack_ptr: usize,
62+
instr_ptr: u32,
63+
end_instr_ptr: u32,
64+
stack_ptr: u32,
6465
ty: BlockType,
6566
args: &BlockArgs,
6667
module: &ModuleInstance,
@@ -70,7 +71,7 @@ impl BlockFrame {
7071
BlockArgs::Type(_) => (0, 1),
7172
BlockArgs::FuncType(t) => {
7273
let ty = module.func_ty(*t);
73-
(ty.params.len(), ty.results.len())
74+
(ty.params.len() as u8, ty.results.len() as u8)
7475
}
7576
};
7677

crates/tinywasm/src/runtime/stack/call_stack.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ impl CallStack {
4848

4949
#[derive(Debug, Clone)]
5050
pub(crate) struct CallFrame {
51-
pub(crate) instr_ptr: usize,
52-
pub(crate) block_ptr: usize,
51+
pub(crate) instr_ptr: u32,
52+
pub(crate) block_ptr: u32,
5353
pub(crate) func_instance: (Rc<WasmFunction>, ModuleInstanceAddr),
5454
pub(crate) locals: Box<[RawWasmValue]>,
5555
}
@@ -63,7 +63,9 @@ impl CallFrame {
6363
blocks: &mut super::BlockStack,
6464
) {
6565
if block_frame.params > 0 {
66-
values.extend_from_within((block_frame.stack_ptr - block_frame.params)..block_frame.stack_ptr);
66+
let start = (block_frame.stack_ptr - block_frame.params as u32) as usize;
67+
let end = block_frame.stack_ptr as usize;
68+
values.extend_from_within(start..end);
6769
}
6870

6971
blocks.push(block_frame);
@@ -77,7 +79,7 @@ impl CallFrame {
7779
values: &mut super::ValueStack,
7880
blocks: &mut super::BlockStack,
7981
) -> Option<()> {
80-
let break_to = blocks.get_relative_to(break_to_relative as usize, self.block_ptr)?;
82+
let break_to = blocks.get_relative_to(break_to_relative, self.block_ptr)?;
8183

8284
// instr_ptr points to the label instruction, but the next step
8385
// will increment it by 1 since we're changing the "current" instr_ptr
@@ -92,7 +94,7 @@ impl CallFrame {
9294
// check if we're breaking to the loop
9395
if break_to_relative != 0 {
9496
// we also want to trim the label stack to the loop (but not including the loop)
95-
blocks.truncate(blocks.len() - break_to_relative as usize);
97+
blocks.truncate(blocks.len() as u32 - break_to_relative);
9698
return Some(());
9799
}
98100
}
@@ -106,7 +108,7 @@ impl CallFrame {
106108
self.instr_ptr = break_to.end_instr_ptr;
107109

108110
// we also want to trim the label stack, including the block
109-
blocks.truncate(blocks.len() - (break_to_relative as usize + 1));
111+
blocks.truncate(blocks.len() as u32 - (break_to_relative + 1));
110112
}
111113
}
112114

@@ -118,8 +120,8 @@ impl CallFrame {
118120
pub(crate) fn new(
119121
wasm_func_inst: Rc<WasmFunction>,
120122
owner: ModuleInstanceAddr,
121-
params: impl Iterator<Item = RawWasmValue> + ExactSizeIterator,
122-
block_ptr: usize,
123+
params: impl ExactSizeIterator<Item = RawWasmValue>,
124+
block_ptr: u32,
123125
) -> Self {
124126
let locals = {
125127
let local_types = &wasm_func_inst.locals;
@@ -150,6 +152,6 @@ impl CallFrame {
150152

151153
#[inline(always)]
152154
pub(crate) fn current_instruction(&self) -> &Instruction {
153-
&self.func_instance.0.instructions[self.instr_ptr]
155+
&self.func_instance.0.instructions[self.instr_ptr as usize]
154156
}
155157
}

0 commit comments

Comments
 (0)