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

Commit 366ef54

Browse files
feat: fully working block and loop implementations
Signed-off-by: Henry Gressmann <mail@henrygressmann.de>
1 parent 6e8ea78 commit 366ef54

File tree

8 files changed

+174
-116
lines changed

8 files changed

+174
-116
lines changed

crates/parser/src/conversion.rs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use alloc::{boxed::Box, format, string::ToString, vec::Vec};
2+
use log::info;
23
use tinywasm_types::{BlockArgs, Export, ExternalKind, FuncType, Instruction, MemArg, ValType};
34
use wasmparser::{FuncValidator, ValidatorResources};
45

@@ -113,9 +114,11 @@ pub fn process_operators<'a>(
113114
mut validator: FuncValidator<ValidatorResources>,
114115
) -> Result<Box<[Instruction]>> {
115116
let mut instructions = Vec::new();
116-
let mut last_label_pointer = Option::None;
117+
let mut labels_ptrs = Vec::new();
117118

118119
for (i, op) in ops.enumerate() {
120+
info!("op: {:?}", op);
121+
119122
let op = op?;
120123
validator.op(offset, &op)?;
121124
offset += 1;
@@ -134,27 +137,26 @@ pub fn process_operators<'a>(
134137
Unreachable => Instruction::Unreachable,
135138
Nop => Instruction::Nop,
136139
Block { blockty } => {
137-
last_label_pointer = Some(i);
140+
labels_ptrs.push(instructions.len());
138141
Instruction::Block(convert_blocktype(blockty), 0)
139142
}
140143
Loop { blockty } => {
141-
last_label_pointer = Some(i);
144+
labels_ptrs.push(instructions.len());
142145
Instruction::Loop(convert_blocktype(blockty), 0)
143146
}
144147
If { blockty } => {
145-
last_label_pointer = Some(i);
148+
labels_ptrs.push(instructions.len());
146149
Instruction::If(convert_blocktype(blockty), 0)
147150
}
148151
End => {
149-
if let Some(label_pointer) = last_label_pointer {
152+
if let Some(label_pointer) = labels_ptrs.pop() {
150153
// last_label_pointer is Some if we're ending a block
151154
match instructions[label_pointer] {
152155
Instruction::Block(_, ref mut end)
153156
| Instruction::Loop(_, ref mut end)
154157
| Instruction::Else(ref mut end)
155158
| Instruction::If(_, ref mut end) => {
156159
*end = i + 1; // Set the end position to be one after the End instruction
157-
last_label_pointer = None;
158160
}
159161
_ => {
160162
return Err(crate::ParseError::UnsupportedOperator(
@@ -170,7 +172,7 @@ pub fn process_operators<'a>(
170172
}
171173
}
172174
Else => {
173-
let Some(label_pointer) = last_label_pointer else {
175+
let Some(label_pointer) = labels_ptrs.pop() else {
174176
return Err(crate::ParseError::UnsupportedOperator(
175177
"Expected to end an if block, but the last label was None".to_string(),
176178
));
@@ -187,7 +189,6 @@ pub fn process_operators<'a>(
187189
}
188190
}
189191

190-
last_label_pointer = Some(i);
191192
Instruction::Else(0)
192193
}
193194
Br { relative_depth } => Instruction::Br(relative_depth),
@@ -369,6 +370,13 @@ pub fn process_operators<'a>(
369370
instructions.push(res);
370371
}
371372

373+
if !labels_ptrs.is_empty() {
374+
panic!(
375+
"last_label_pointer should be None after processing all instructions: {:?}",
376+
labels_ptrs
377+
);
378+
}
379+
372380
validator.finish(offset)?;
373381

374382
Ok(instructions.into_boxed_slice())

crates/tinywasm/src/error.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ pub enum Error {
3939
/// The stack is empty
4040
StackUnderflow,
4141

42-
/// The block stack is empty
43-
BlockStackUnderflow,
42+
/// The label stack is empty
43+
LabelStackUnderflow,
4444

4545
/// The call stack is empty
4646
CallStackEmpty,
@@ -63,7 +63,7 @@ impl Display for Error {
6363
Self::Other(message) => write!(f, "unknown error: {}", message),
6464
Self::UnsupportedFeature(feature) => write!(f, "unsupported feature: {}", feature),
6565
Self::FuncDidNotReturn => write!(f, "function did not return"),
66-
Self::BlockStackUnderflow => write!(f, "block stack underflow"),
66+
Self::LabelStackUnderflow => write!(f, "label stack underflow"),
6767
Self::StackUnderflow => write!(f, "stack underflow"),
6868
Self::CallStackEmpty => write!(f, "call stack empty"),
6969
Self::InvalidStore => write!(f, "invalid store"),

crates/tinywasm/src/runtime/executor/macros.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ macro_rules! div_instr {
3131
/// Less than signed instruction
3232
macro_rules! lts_instr {
3333
($ty:ty, $stack:ident) => {{
34-
let [b, a] = $stack.values.pop_n_const::<2>()?;
34+
let [a, b] = $stack.values.pop_n_const::<2>()?;
3535
let a: $ty = a.into();
3636
let b: $ty = b.into();
3737
$stack.values.push(((a < b) as i32).into());
@@ -58,9 +58,20 @@ macro_rules! eq_instr {
5858
}};
5959
}
6060

61+
/// Greater or equal than signed instruction
62+
macro_rules! ges_instr {
63+
($ty:ty, $stack:ident) => {{
64+
let [a, b] = $stack.values.pop_n_const::<2>()?;
65+
let a: $ty = a.into();
66+
let b: $ty = b.into();
67+
$stack.values.push(((a >= b) as i32).into());
68+
}};
69+
}
70+
6171
pub(super) use add_instr;
6272
pub(super) use div_instr;
6373
pub(super) use eq_instr;
74+
pub(super) use ges_instr;
6475
pub(super) use lts_instr;
6576
pub(super) use mul_instr;
6677
pub(super) use sub_instr;

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

Lines changed: 27 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use super::{DefaultRuntime, Stack};
22
use crate::{
33
log::debug,
4-
runtime::{BlockFrame, BlockFrameInner, RawWasmValue},
4+
runtime::{BlockType, LabelFrame, RawWasmValue},
55
CallFrame, Error, ModuleInstance, Result, Store,
66
};
77
use alloc::vec::Vec;
8+
use log::info;
89
use tinywasm_types::{BlockArgs, Instruction};
910

1011
mod macros;
@@ -64,6 +65,9 @@ enum ExecResult {
6465
Trap(crate::Trap),
6566
}
6667

68+
/// Run a single step of the interpreter
69+
/// A seperate function is used so later, we can more easily implement
70+
/// a step-by-step debugger (using generators once they're stable?)
6771
#[inline]
6872
fn exec_one(
6973
cf: &mut CallFrame,
@@ -74,6 +78,9 @@ fn exec_one(
7478
module: &ModuleInstance,
7579
) -> Result<ExecResult> {
7680
use tinywasm_types::Instruction::*;
81+
82+
info!("ptr: {} instr: {:?}", cf.instr_ptr, instr);
83+
7784
match instr {
7885
Nop => { /* do nothing */ }
7986
Unreachable => return Ok(ExecResult::Trap(crate::Trap::Unreachable)), // we don't need to include the call frame here because it's already on the stack
@@ -117,23 +124,23 @@ fn exec_one(
117124
}
118125

119126
Loop(args, end_offset) => {
120-
cf.block_frames.push(BlockFrame {
127+
cf.labels.push(LabelFrame {
121128
instr_ptr: cf.instr_ptr,
122129
end_instr_ptr: cf.instr_ptr + *end_offset,
123130
stack_ptr: stack.values.len(),
124131
args: *args,
125-
block: BlockFrameInner::Loop,
132+
ty: BlockType::Loop,
126133
});
127134
stack.values.block_args(*args)?;
128135
}
129136

130137
Block(args, end_offset) => {
131-
cf.block_frames.push(BlockFrame {
138+
cf.labels.push(LabelFrame {
132139
instr_ptr: cf.instr_ptr,
133140
end_instr_ptr: cf.instr_ptr + *end_offset,
134141
stack_ptr: stack.values.len(),
135142
args: *args,
136-
block: BlockFrameInner::Block,
143+
ty: BlockType::Block,
137144
});
138145
stack.values.block_args(*args)?;
139146
}
@@ -163,7 +170,7 @@ fn exec_one(
163170
}
164171

165172
EndFunc => {
166-
if cf.block_frames.len() > 0 {
173+
if cf.labels.len() > 0 {
167174
panic!("endfunc: block frames not empty, this should have been validated by the parser");
168175
}
169176

@@ -178,44 +185,24 @@ fn exec_one(
178185
}
179186

180187
EndBlockFrame => {
181-
let blocks = &mut cf.block_frames;
188+
let blocks = &mut cf.labels;
189+
190+
// remove the label from the label stack
182191
let Some(block) = blocks.pop() else {
183-
panic!("end: no block to end, this should have been validated by the parser");
192+
panic!("end: no label to end, this should have been validated by the parser");
184193
};
185194

186-
debug!("end, blocks: {:?}", blocks);
187-
debug!(" instr_ptr: {}", cf.instr_ptr);
188-
189195
let res: &[RawWasmValue] = match block.args {
190196
BlockArgs::Empty => &[],
191197
BlockArgs::Type(_t) => todo!(),
192198
BlockArgs::FuncType(_t) => todo!(),
193199
};
194200

195-
match block.block {
196-
BlockFrameInner::Loop => {
197-
debug!("end(loop): continue loop");
201+
// trim the lable's stack from the stack
202+
stack.values.trim(block.stack_ptr);
198203

199-
// remove the loop values from the stack
200-
stack.values.trim(block.stack_ptr);
201-
202-
// set the instruction pointer to the start of the loop
203-
cf.instr_ptr = block.instr_ptr;
204-
205-
// push the loop back onto the stack
206-
blocks.push(block);
207-
}
208-
BlockFrameInner::Block => {
209-
// remove the block values from the stack
210-
stack.values.trim(block.stack_ptr);
211-
212-
// push the block result values to the stack
213-
stack.values.extend(res.iter().copied());
214-
}
215-
_ => {
216-
panic!("end: unimplemented block type end: {:?}", block.block);
217-
}
218-
}
204+
// push the block result values to the stack
205+
stack.values.extend(res.iter().copied());
219206
}
220207

221208
LocalGet(local_index) => {
@@ -244,17 +231,16 @@ fn exec_one(
244231
F32Sub => sub_instr!(f32, stack),
245232
F64Sub => sub_instr!(f64, stack),
246233

247-
I32LtS => {
248-
let [b, a] = stack.values.pop_n_const::<2>()?;
249-
let a: i32 = a.into();
250-
let b: i32 = b.into();
251-
stack.values.push(((a < b) as i32).into());
252-
debug!("i32.lt_s: {} < {} = {}", a, b, a < b);
253-
}
234+
I32LtS => lts_instr!(i32, stack),
254235
I64LtS => lts_instr!(i64, stack),
255236
F32Lt => lts_instr!(f32, stack),
256237
F64Lt => lts_instr!(f64, stack),
257238

239+
I32GeS => ges_instr!(i32, stack),
240+
I64GeS => ges_instr!(i64, stack),
241+
F32Ge => ges_instr!(f32, stack),
242+
F64Ge => ges_instr!(f64, stack),
243+
258244
I32DivS => div_instr!(i32, stack),
259245
I64DivS => div_instr!(i64, stack),
260246
F32Div => div_instr!(f32, stack),

crates/tinywasm/src/runtime/stack.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ mod call_stack;
33
mod value_stack;
44

55
use self::{call_stack::CallStack, value_stack::ValueStack};
6-
pub(crate) use blocks::{BlockFrame, BlockFrameInner};
6+
pub(crate) use blocks::{BlockType, LabelFrame};
77
pub(crate) use call_stack::CallFrame;
88

99
/// A WebAssembly Stack

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

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,33 @@ use log::info;
33
use tinywasm_types::BlockArgs;
44

55
#[derive(Debug, Default, Clone)]
6-
pub(crate) struct Blocks(Vec<BlockFrame>);
6+
pub(crate) struct Labels(Vec<LabelFrame>);
77

8-
impl Blocks {
8+
impl Labels {
99
pub(crate) fn len(&self) -> usize {
1010
self.0.len()
1111
}
1212

1313
#[inline]
14-
pub(crate) fn push(&mut self, block: BlockFrame) {
14+
pub(crate) fn push(&mut self, block: LabelFrame) {
1515
self.0.push(block);
1616
}
1717

18+
#[inline]
19+
pub(crate) fn top(&self) -> Option<&LabelFrame> {
20+
self.0.last()
21+
}
22+
1823
#[inline]
1924
/// get the block at the given index, where 0 is the top of the stack
20-
pub(crate) fn get_relative_to_top(&self, index: usize) -> Option<&BlockFrame> {
25+
pub(crate) fn get_relative_to_top(&self, index: usize) -> Option<&LabelFrame> {
2126
info!("get block: {}", index);
2227
info!("blocks: {:?}", self.0);
2328
self.0.get(self.0.len() - index - 1)
2429
}
2530

2631
#[inline]
27-
pub(crate) fn pop(&mut self) -> Option<BlockFrame> {
32+
pub(crate) fn pop(&mut self) -> Option<LabelFrame> {
2833
self.0.pop()
2934
}
3035

@@ -36,7 +41,7 @@ impl Blocks {
3641
}
3742

3843
#[derive(Debug, Clone)]
39-
pub(crate) struct BlockFrame {
44+
pub(crate) struct LabelFrame {
4045
// position of the instruction pointer when the block was entered
4146
pub(crate) instr_ptr: usize,
4247
// position of the end instruction of the block
@@ -45,12 +50,12 @@ pub(crate) struct BlockFrame {
4550
// position of the stack pointer when the block was entered
4651
pub(crate) stack_ptr: usize,
4752
pub(crate) args: BlockArgs,
48-
pub(crate) block: BlockFrameInner,
53+
pub(crate) ty: BlockType,
4954
}
5055

5156
#[derive(Debug, Copy, Clone)]
5257
#[allow(dead_code)]
53-
pub(crate) enum BlockFrameInner {
58+
pub(crate) enum BlockType {
5459
Loop,
5560
If,
5661
Else,

0 commit comments

Comments
 (0)