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

Commit 6e8ea78

Browse files
feat: new new instruction types for blocks/ends
Signed-off-by: Henry Gressmann <mail@henrygressmann.de>
1 parent 97fb3cc commit 6e8ea78

File tree

9 files changed

+247
-112
lines changed

9 files changed

+247
-112
lines changed

Cargo.lock

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/parser/src/conversion.rs

Lines changed: 61 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,22 +31,17 @@ pub(crate) fn convert_module_code(
3131
) -> Result<CodeSection> {
3232
let locals_reader = func.get_locals_reader()?;
3333
let count = locals_reader.get_count();
34-
34+
let pos = locals_reader.original_position();
3535
let mut locals = Vec::with_capacity(count as usize);
3636

3737
for (i, local) in locals_reader.into_iter().enumerate() {
3838
let local = local?;
39-
validator.define_locals(i, local.0, local.1)?;
40-
39+
validator.define_locals(pos + i, local.0, local.1)?;
4140
for _ in 0..local.0 {
4241
locals.push(convert_valtype(&local.1));
4342
}
4443
}
4544

46-
if locals.len() != count as usize {
47-
return Err(crate::ParseError::Other("Invalid local index".to_string()));
48-
}
49-
5045
let body_reader = func.get_operators_reader()?;
5146
let body = process_operators(body_reader.original_position(), body_reader.into_iter(), validator)?;
5247

@@ -118,8 +113,9 @@ pub fn process_operators<'a>(
118113
mut validator: FuncValidator<ValidatorResources>,
119114
) -> Result<Box<[Instruction]>> {
120115
let mut instructions = Vec::new();
116+
let mut last_label_pointer = Option::None;
121117

122-
for op in ops {
118+
for (i, op) in ops.enumerate() {
123119
let op = op?;
124120
validator.op(offset, &op)?;
125121
offset += 1;
@@ -137,11 +133,63 @@ pub fn process_operators<'a>(
137133
}
138134
Unreachable => Instruction::Unreachable,
139135
Nop => Instruction::Nop,
140-
Block { blockty } => Instruction::Block(convert_blocktype(blockty)),
141-
Loop { blockty } => Instruction::Loop(convert_blocktype(blockty)),
142-
If { blockty } => Instruction::If(convert_blocktype(blockty)),
143-
Else => Instruction::Else,
144-
End => Instruction::End,
136+
Block { blockty } => {
137+
last_label_pointer = Some(i);
138+
Instruction::Block(convert_blocktype(blockty), 0)
139+
}
140+
Loop { blockty } => {
141+
last_label_pointer = Some(i);
142+
Instruction::Loop(convert_blocktype(blockty), 0)
143+
}
144+
If { blockty } => {
145+
last_label_pointer = Some(i);
146+
Instruction::If(convert_blocktype(blockty), 0)
147+
}
148+
End => {
149+
if let Some(label_pointer) = last_label_pointer {
150+
// last_label_pointer is Some if we're ending a block
151+
match instructions[label_pointer] {
152+
Instruction::Block(_, ref mut end)
153+
| Instruction::Loop(_, ref mut end)
154+
| Instruction::Else(ref mut end)
155+
| Instruction::If(_, ref mut end) => {
156+
*end = i + 1; // Set the end position to be one after the End instruction
157+
last_label_pointer = None;
158+
}
159+
_ => {
160+
return Err(crate::ParseError::UnsupportedOperator(
161+
"Expected to end a block, but the last label was not a block".to_string(),
162+
))
163+
}
164+
}
165+
166+
Instruction::EndBlockFrame
167+
} else {
168+
// last_label_pointer is None if we're ending the function
169+
Instruction::EndFunc
170+
}
171+
}
172+
Else => {
173+
let Some(label_pointer) = last_label_pointer else {
174+
return Err(crate::ParseError::UnsupportedOperator(
175+
"Expected to end an if block, but the last label was None".to_string(),
176+
));
177+
};
178+
179+
match instructions[label_pointer] {
180+
Instruction::If(_, ref mut end) => {
181+
*end = i + 1; // Set the end position to be one after the Else instruction
182+
}
183+
_ => {
184+
return Err(crate::ParseError::UnsupportedOperator(
185+
"Expected to end an if block, but the last label was not an if".to_string(),
186+
));
187+
}
188+
}
189+
190+
last_label_pointer = Some(i);
191+
Instruction::Else(0)
192+
}
145193
Br { relative_depth } => Instruction::Br(relative_depth),
146194
BrIf { relative_depth } => Instruction::BrIf(relative_depth),
147195
Return => Instruction::Return,

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

Lines changed: 1 addition & 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 [a, b] = $stack.values.pop_n_const::<2>()?;
34+
let [b, a] = $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());

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

Lines changed: 65 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use super::{DefaultRuntime, Stack};
22
use crate::{
33
log::debug,
4-
runtime::{BlockFrame, BlockFrameType, RawWasmValue},
4+
runtime::{BlockFrame, BlockFrameInner, RawWasmValue},
55
CallFrame, Error, ModuleInstance, Result, Store,
66
};
77
use alloc::vec::Vec;
@@ -116,12 +116,24 @@ fn exec_one(
116116
return Ok(ExecResult::Call);
117117
}
118118

119-
Loop(args) => {
120-
cf.blocks.push(BlockFrame {
119+
Loop(args, end_offset) => {
120+
cf.block_frames.push(BlockFrame {
121121
instr_ptr: cf.instr_ptr,
122+
end_instr_ptr: cf.instr_ptr + *end_offset,
122123
stack_ptr: stack.values.len(),
123124
args: *args,
124-
ty: BlockFrameType::Loop,
125+
block: BlockFrameInner::Loop,
126+
});
127+
stack.values.block_args(*args)?;
128+
}
129+
130+
Block(args, end_offset) => {
131+
cf.block_frames.push(BlockFrame {
132+
instr_ptr: cf.instr_ptr,
133+
end_instr_ptr: cf.instr_ptr + *end_offset,
134+
stack_ptr: stack.values.len(),
135+
args: *args,
136+
block: BlockFrameInner::Block,
125137
});
126138
stack.values.block_args(*args)?;
127139
}
@@ -144,42 +156,64 @@ fn exec_one(
144156
Br(v) => cf.break_to(*v, &mut stack.values)?,
145157
BrIf(v) => {
146158
let val: i32 = stack.values.pop().ok_or(Error::StackUnderflow)?.into();
159+
debug!("br_if: {}", val);
147160
if val > 0 {
148161
cf.break_to(*v, &mut stack.values)?
149162
};
150163
}
151-
End => {
152-
let blocks = &mut cf.blocks;
164+
165+
EndFunc => {
166+
if cf.block_frames.len() > 0 {
167+
panic!("endfunc: block frames not empty, this should have been validated by the parser");
168+
}
169+
170+
if stack.call_stack.is_empty() {
171+
debug!("end: no block to end and no parent call frame, returning");
172+
return Ok(ExecResult::Return);
173+
} else {
174+
debug!("end: no block to end, returning to parent call frame");
175+
*cf = stack.call_stack.pop()?;
176+
return Ok(ExecResult::Call);
177+
}
178+
}
179+
180+
EndBlockFrame => {
181+
let blocks = &mut cf.block_frames;
153182
let Some(block) = blocks.pop() else {
154-
if stack.call_stack.is_empty() {
155-
debug!("end: no block to end and no parent call frame, returning");
156-
return Ok(ExecResult::Return);
157-
} else {
158-
debug!("end: no block to end, returning to parent call frame");
159-
*cf = stack.call_stack.pop()?;
160-
return Ok(ExecResult::Call);
161-
}
183+
panic!("end: no block to end, this should have been validated by the parser");
162184
};
185+
163186
debug!("end, blocks: {:?}", blocks);
164187
debug!(" instr_ptr: {}", cf.instr_ptr);
165188

166-
match block.ty {
167-
BlockFrameType::Loop => {
168-
debug!("end(loop): break loop");
169-
let res: &[RawWasmValue] = match block.args {
170-
BlockArgs::Empty => &[],
171-
BlockArgs::Type(_t) => todo!(),
172-
BlockArgs::FuncType(_t) => todo!(),
173-
};
189+
let res: &[RawWasmValue] = match block.args {
190+
BlockArgs::Empty => &[],
191+
BlockArgs::Type(_t) => todo!(),
192+
BlockArgs::FuncType(_t) => todo!(),
193+
};
194+
195+
match block.block {
196+
BlockFrameInner::Loop => {
197+
debug!("end(loop): continue loop");
174198

175199
// remove the loop values from the stack
176200
stack.values.trim(block.stack_ptr);
177201

178-
// push the loop result values to the stack
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
179213
stack.values.extend(res.iter().copied());
180214
}
181215
_ => {
182-
panic!("end: unimplemented block type end: {:?}", block.ty);
216+
panic!("end: unimplemented block type end: {:?}", block.block);
183217
}
184218
}
185219
}
@@ -210,7 +244,13 @@ fn exec_one(
210244
F32Sub => sub_instr!(f32, stack),
211245
F64Sub => sub_instr!(f64, stack),
212246

213-
I32LtS => lts_instr!(i32, stack),
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+
}
214254
I64LtS => lts_instr!(i64, stack),
215255
F32Lt => lts_instr!(f32, stack),
216256
F64Lt => lts_instr!(f64, 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, BlockFrameType};
6+
pub(crate) use blocks::{BlockFrame, BlockFrameInner};
77
pub(crate) use call_stack::CallFrame;
88

99
/// A WebAssembly Stack

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

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,18 @@ use tinywasm_types::BlockArgs;
66
pub(crate) struct Blocks(Vec<BlockFrame>);
77

88
impl Blocks {
9+
pub(crate) fn len(&self) -> usize {
10+
self.0.len()
11+
}
12+
913
#[inline]
1014
pub(crate) fn push(&mut self, block: BlockFrame) {
1115
self.0.push(block);
1216
}
1317

1418
#[inline]
1519
/// get the block at the given index, where 0 is the top of the stack
16-
pub(crate) fn get(&self, index: usize) -> Option<&BlockFrame> {
20+
pub(crate) fn get_relative_to_top(&self, index: usize) -> Option<&BlockFrame> {
1721
info!("get block: {}", index);
1822
info!("blocks: {:?}", self.0);
1923
self.0.get(self.0.len() - index - 1)
@@ -24,26 +28,29 @@ impl Blocks {
2428
self.0.pop()
2529
}
2630

27-
/// remove all blocks after the given index
31+
/// keep the top `len` blocks and discard the rest
2832
#[inline]
29-
pub(crate) fn trim(&mut self, index: usize) {
30-
self.0.truncate(index + 1);
33+
pub(crate) fn trim(&mut self, len: usize) {
34+
self.0.truncate(len);
3135
}
3236
}
3337

3438
#[derive(Debug, Clone)]
3539
pub(crate) struct BlockFrame {
36-
// where to resume execution when the block is broken
40+
// position of the instruction pointer when the block was entered
3741
pub(crate) instr_ptr: usize,
42+
// position of the end instruction of the block
43+
pub(crate) end_instr_ptr: usize,
44+
3845
// position of the stack pointer when the block was entered
3946
pub(crate) stack_ptr: usize,
4047
pub(crate) args: BlockArgs,
41-
pub(crate) ty: BlockFrameType,
48+
pub(crate) block: BlockFrameInner,
4249
}
4350

4451
#[derive(Debug, Copy, Clone)]
4552
#[allow(dead_code)]
46-
pub(crate) enum BlockFrameType {
53+
pub(crate) enum BlockFrameInner {
4754
Loop,
4855
If,
4956
Else,

0 commit comments

Comments
 (0)