wasm_interp: Don't need to store block depth in CallStack any more

This commit is contained in:
Brian Carroll 2022-11-26 10:58:16 +00:00
parent 0bd1bced68
commit 3346ef9c9c
No known key found for this signature in database
GPG Key ID: 5C7B2EC4101703C0
3 changed files with 22 additions and 45 deletions

View File

@ -11,7 +11,7 @@ use crate::ValueStack;
#[derive(Debug)]
pub struct CallStack<'a> {
/// return addresses and nested block depths (one entry per frame)
return_addrs_and_block_depths: Vec<'a, (u32, u32)>,
return_addrs: Vec<'a, u32>,
/// frame offsets into the `locals`, `is_float`, and `is_64` vectors (one entry per frame)
frame_offsets: Vec<'a, u32>,
/// base size of the value stack before executing (one entry per frame)
@ -37,7 +37,7 @@ Not clear if this would be better! Stack access pattern is pretty cache-friendly
impl<'a> CallStack<'a> {
pub fn new(arena: &'a Bump) -> Self {
CallStack {
return_addrs_and_block_depths: Vec::with_capacity_in(256, arena),
return_addrs: Vec::with_capacity_in(256, arena),
frame_offsets: Vec::with_capacity_in(256, arena),
value_stack_bases: Vec::with_capacity_in(256, arena),
locals_data: Vec::with_capacity_in(16 * 256, arena),
@ -50,14 +50,12 @@ impl<'a> CallStack<'a> {
pub fn push_frame(
&mut self,
return_addr: u32,
return_block_depth: u32,
arg_type_bytes: &[u8],
value_stack: &mut ValueStack<'a>,
code_bytes: &[u8],
pc: &mut usize,
) {
self.return_addrs_and_block_depths
.push((return_addr, return_block_depth));
self.return_addrs.push(return_addr);
let frame_offset = self.is_64.len();
self.frame_offsets.push(frame_offset as u32);
let mut total = 0;
@ -92,13 +90,13 @@ impl<'a> CallStack<'a> {
}
/// On returning from a Wasm call, drop its locals and retrieve the return address
pub fn pop_frame(&mut self) -> Option<(u32, u32)> {
pub fn pop_frame(&mut self) -> Option<u32> {
let frame_offset = self.frame_offsets.pop()? as usize;
self.value_stack_bases.pop()?;
self.locals_data.truncate(frame_offset);
self.is_64.truncate(frame_offset);
self.is_64.truncate(frame_offset);
self.return_addrs_and_block_depths.pop()
self.return_addrs.pop()
}
pub fn get_local(&self, local_index: u32) -> Value {
@ -180,13 +178,13 @@ mod tests {
// Push a other few frames before the test frame, just to make the scenario more typical.
[(1u32, ValueType::I32)].serialize(&mut buffer);
call_stack.push_frame(0x11111, 0, &[], &mut vs, &buffer, &mut cursor);
call_stack.push_frame(0x11111, &[], &mut vs, &buffer, &mut cursor);
[(2u32, ValueType::I32)].serialize(&mut buffer);
call_stack.push_frame(0x22222, 0, &[], &mut vs, &buffer, &mut cursor);
call_stack.push_frame(0x22222, &[], &mut vs, &buffer, &mut cursor);
[(3u32, ValueType::I32)].serialize(&mut buffer);
call_stack.push_frame(0x33333, 0, &[], &mut vs, &buffer, &mut cursor);
call_stack.push_frame(0x33333, &[], &mut vs, &buffer, &mut cursor);
// Create a test call frame with local variables of every type
[
@ -196,7 +194,7 @@ mod tests {
(1u32, ValueType::F64),
]
.serialize(&mut buffer);
call_stack.push_frame(RETURN_ADDR, 0, &[], &mut vs, &buffer, &mut cursor);
call_stack.push_frame(RETURN_ADDR, &[], &mut vs, &buffer, &mut cursor);
}
#[test]
@ -223,7 +221,7 @@ mod tests {
test_get_set(&mut call_stack, 14, Value::F64(f64::MIN));
test_get_set(&mut call_stack, 14, Value::F64(f64::MAX));
assert_eq!(call_stack.pop_frame(), Some((RETURN_ADDR, 0)));
assert_eq!(call_stack.pop_frame(), Some(RETURN_ADDR));
}
#[test]

View File

@ -23,7 +23,6 @@ pub struct ExecutionState<'a> {
pub value_stack: ValueStack<'a>,
pub globals: Vec<'a, Value>,
pub program_counter: usize,
block_depth: u32,
block_loop_addrs: Vec<'a, Option<u32>>,
import_signatures: Vec<'a, u32>,
debug_string: Option<String>,
@ -41,7 +40,6 @@ impl<'a> ExecutionState<'a> {
value_stack: ValueStack::new(arena),
globals: Vec::from_iter_in(globals, arena),
program_counter,
block_depth: 0,
block_loop_addrs: Vec::new_in(arena),
import_signatures: Vec::new_in(arena),
debug_string: Some(String::new()),
@ -123,7 +121,6 @@ impl<'a> ExecutionState<'a> {
let mut call_stack = CallStack::new(arena);
call_stack.push_frame(
0, // return_addr
0, // return_block_depth
arg_type_bytes,
&mut value_stack,
&module.code.bytes,
@ -142,7 +139,6 @@ impl<'a> ExecutionState<'a> {
value_stack,
globals,
program_counter,
block_depth: 0,
block_loop_addrs: Vec::new_in(arena),
import_signatures,
debug_string,
@ -158,17 +154,16 @@ impl<'a> ExecutionState<'a> {
}
fn do_return(&mut self) -> Action {
if let Some((return_addr, block_depth)) = self.call_stack.pop_frame() {
if let Some(return_addr) = self.call_stack.pop_frame() {
if self.call_stack.is_empty() {
// We just popped the stack frame for the entry function. Terminate the program.
Action::Break
} else {
self.program_counter = return_addr as usize;
self.block_depth = block_depth;
Action::Continue
}
} else {
// We should never get here with real programs but maybe in tests. Terminate the program.
// We should never get here with real programs, but maybe in tests. Terminate the program.
Action::Break
}
}
@ -212,7 +207,6 @@ impl<'a> ExecutionState<'a> {
match maybe_loop {
Some((block_index, addr)) => {
self.block_loop_addrs.truncate(block_index + 1);
self.block_depth = self.block_loop_addrs.len() as u32;
self.program_counter = addr as usize;
}
None => {
@ -221,11 +215,12 @@ impl<'a> ExecutionState<'a> {
}
}
// Break to an outer block, going forward in the program
fn break_forward(&mut self, relative_blocks_outward: u32, module: &WasmModule<'a>) {
use OpCode::*;
let mut depth = self.block_depth;
let target_block_depth = self.block_depth - relative_blocks_outward - 1;
let mut depth = self.block_loop_addrs.len();
let target_block_depth = depth - (relative_blocks_outward + 1) as usize;
loop {
let skipped_op = OpCode::from(module.code.bytes[self.program_counter]);
OpCode::skip_bytes(&module.code.bytes, &mut self.program_counter).unwrap();
@ -242,10 +237,7 @@ impl<'a> ExecutionState<'a> {
_ => {}
}
}
while self.block_depth > depth {
self.block_depth -= 1;
self.block_loop_addrs.pop().unwrap();
}
self.block_loop_addrs.truncate(target_block_depth);
}
pub fn execute_next_instruction(&mut self, module: &WasmModule<'a>) -> Action {
@ -272,22 +264,19 @@ impl<'a> ExecutionState<'a> {
NOP => {}
BLOCK => {
self.fetch_immediate_u32(module); // blocktype (ignored)
self.block_depth += 1;
self.block_loop_addrs.push(None);
}
LOOP => {
self.fetch_immediate_u32(module); // blocktype (ignored)
self.block_depth += 1;
self.block_loop_addrs
.push(Some(self.program_counter as u32));
}
IF => {
self.fetch_immediate_u32(module); // blocktype (ignored)
let condition = self.value_stack.pop_i32();
self.block_depth += 1;
self.block_loop_addrs.push(None);
if condition == 0 {
let mut depth = self.block_depth;
let mut depth = self.block_loop_addrs.len();
loop {
let skipped_op = OpCode::from(module.code.bytes[self.program_counter]);
OpCode::skip_bytes(&module.code.bytes, &mut self.program_counter).unwrap();
@ -299,7 +288,7 @@ impl<'a> ExecutionState<'a> {
depth -= 1;
}
ELSE => {
if depth == self.block_depth {
if depth == self.block_loop_addrs.len() {
break;
}
}
@ -315,11 +304,11 @@ impl<'a> ExecutionState<'a> {
self.do_break(0, module, op_code);
}
END => {
if self.block_depth == 0 {
if self.block_loop_addrs.is_empty() {
// implicit RETURN at end of function
action = self.do_return();
} else {
self.block_depth -= 1;
self.block_loop_addrs.pop().unwrap();
}
}
BR => {
@ -364,14 +353,10 @@ impl<'a> ExecutionState<'a> {
let return_addr = self.program_counter as u32;
self.program_counter = module.code.function_offsets[index] as usize;
let return_block_depth = self.block_depth;
self.block_depth = 0;
let _function_byte_length =
u32::parse((), &module.code.bytes, &mut self.program_counter).unwrap();
self.call_stack.push_frame(
return_addr,
return_block_depth,
arg_type_bytes,
&mut self.value_stack,
&module.code.bytes,

View File

@ -92,7 +92,6 @@ fn test_loop_help(end: i32, expected: i32) {
let mut state = default_state(&arena);
state.call_stack.push_frame(
0,
0,
&[],
&mut state.value_stack,
@ -160,7 +159,6 @@ fn test_if_else_help(condition: i32, expected: i32) {
let mut state = default_state(&arena);
state.call_stack.push_frame(
0,
0,
&[],
&mut state.value_stack,
@ -249,7 +247,6 @@ fn test_br() {
buf.push(OpCode::END as u8);
state.call_stack.push_frame(
0,
0,
&[],
&mut state.value_stack,
@ -347,7 +344,6 @@ fn test_br_if_help(condition: i32, expected: i32) {
buf.push(OpCode::END as u8);
state.call_stack.push_frame(
0,
0,
&[],
&mut state.value_stack,
@ -451,7 +447,6 @@ fn test_br_table_help(condition: i32, expected: i32) {
println!("{:02x?}", buf);
state.call_stack.push_frame(
0,
0,
&[],
&mut state.value_stack,
@ -586,7 +581,7 @@ fn test_set_get_local() {
.serialize(&mut buffer);
state
.call_stack
.push_frame(0x1234, 0, &[], &mut vs, &buffer, &mut cursor);
.push_frame(0x1234, &[], &mut vs, &buffer, &mut cursor);
module.code.bytes.push(OpCode::I32CONST as u8);
module.code.bytes.encode_i32(12345);
@ -621,7 +616,7 @@ fn test_tee_get_local() {
.serialize(&mut buffer);
state
.call_stack
.push_frame(0x1234, 0, &[], &mut vs, &buffer, &mut cursor);
.push_frame(0x1234, &[], &mut vs, &buffer, &mut cursor);
module.code.bytes.push(OpCode::I32CONST as u8);
module.code.bytes.encode_i32(12345);
@ -1209,7 +1204,6 @@ fn test_i32_compare_help(op: OpCode, x: i32, y: i32, expected: bool) {
let mut state = default_state(&arena);
state.call_stack.push_frame(
0,
0,
&[],
&mut state.value_stack,