mirror of
https://github.com/fluencelabs/parity-wasm
synced 2025-06-20 18:26:54 +00:00
wabt interpreter tests
This commit is contained in:
@ -47,3 +47,6 @@ mod table;
|
||||
mod utils;
|
||||
mod value;
|
||||
mod variable;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
@ -1,6 +1,7 @@
|
||||
use std::mem;
|
||||
use std::ops;
|
||||
use std::u32;
|
||||
use std::fmt::Display;
|
||||
use elements::{Opcode, BlockType, FunctionType};
|
||||
use interpreter::Error;
|
||||
use interpreter::module::{ModuleInstance, ItemIndex};
|
||||
@ -37,8 +38,10 @@ pub enum InstructionOutcome {
|
||||
RunInstruction,
|
||||
/// Continue with next instruction.
|
||||
RunNextInstruction,
|
||||
/// Pop given number of stack frames.
|
||||
PopFrame(usize),
|
||||
/// Branch to given frame.
|
||||
Branch(usize),
|
||||
/// End current frame.
|
||||
End,
|
||||
/// Return from current function block.
|
||||
Return,
|
||||
}
|
||||
@ -46,7 +49,9 @@ pub enum InstructionOutcome {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BlockFrame {
|
||||
// A label for reference from branch instructions.
|
||||
position: usize,
|
||||
branch_position: usize,
|
||||
// A label for reference from end instructions.
|
||||
end_position: usize,
|
||||
// A limit integer value, which is an index into the value stack indicating where to reset it to on a branch to that label.
|
||||
value_limit: usize,
|
||||
// A signature, which is a block signature type indicating the number and types of result values of the region.
|
||||
@ -266,13 +271,13 @@ impl Interpreter {
|
||||
|
||||
fn run_block(context: &mut FunctionContext, block_type: BlockType, body: &[Opcode]) -> Result<InstructionOutcome, Error> {
|
||||
let frame_position = context.position + 1;
|
||||
context.push_frame(frame_position, block_type.clone())?;
|
||||
context.push_frame(frame_position, frame_position, block_type.clone())?;
|
||||
Interpreter::execute_block(context, body)
|
||||
}
|
||||
|
||||
fn run_loop(context: &mut FunctionContext, block_type: BlockType, body: &[Opcode]) -> Result<InstructionOutcome, Error> {
|
||||
let frame_position = context.position;
|
||||
context.push_frame(frame_position, block_type.clone())?;
|
||||
context.push_frame(frame_position, frame_position + 1, block_type.clone())?;
|
||||
Interpreter::execute_block(context, body)
|
||||
}
|
||||
|
||||
@ -287,7 +292,7 @@ impl Interpreter {
|
||||
|
||||
if begin_index != end_index {
|
||||
let frame_position = context.position + 1;
|
||||
context.push_frame(frame_position, block_type.clone())?;
|
||||
context.push_frame(frame_position, frame_position, block_type.clone())?;
|
||||
Interpreter::execute_block(context, &body[begin_index..end_index])
|
||||
} else {
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
@ -295,20 +300,20 @@ impl Interpreter {
|
||||
}
|
||||
|
||||
fn run_else(context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
|
||||
Ok(InstructionOutcome::PopFrame(0))
|
||||
Ok(InstructionOutcome::End)
|
||||
}
|
||||
|
||||
fn run_end(context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
|
||||
Ok(InstructionOutcome::PopFrame(0))
|
||||
Ok(InstructionOutcome::End)
|
||||
}
|
||||
|
||||
fn run_br(context: &mut FunctionContext, label_idx: u32) -> Result<InstructionOutcome, Error> {
|
||||
Ok(InstructionOutcome::PopFrame(label_idx as usize))
|
||||
Ok(InstructionOutcome::Branch(label_idx as usize))
|
||||
}
|
||||
|
||||
fn run_br_if(context: &mut FunctionContext, label_idx: u32) -> Result<InstructionOutcome, Error> {
|
||||
if context.value_stack_mut().pop_as()? {
|
||||
Ok(InstructionOutcome::PopFrame(label_idx as usize))
|
||||
Ok(InstructionOutcome::Branch(label_idx as usize))
|
||||
} else {
|
||||
Ok(InstructionOutcome::RunNextInstruction)
|
||||
}
|
||||
@ -316,7 +321,7 @@ impl Interpreter {
|
||||
|
||||
fn run_br_table(context: &mut FunctionContext, table: &Vec<u32>, default: u32) -> Result<InstructionOutcome, Error> {
|
||||
let index: u32 = context.value_stack_mut().pop_as()?;
|
||||
Ok(InstructionOutcome::PopFrame(table.get(index as usize).cloned().unwrap_or(default) as usize))
|
||||
Ok(InstructionOutcome::Branch(table.get(index as usize).cloned().unwrap_or(default) as usize))
|
||||
}
|
||||
|
||||
fn run_return(context: &mut FunctionContext) -> Result<InstructionOutcome, Error> {
|
||||
@ -491,7 +496,7 @@ impl Interpreter {
|
||||
}
|
||||
|
||||
fn run_lt<T>(context: &mut FunctionContext) -> Result<InstructionOutcome, Error>
|
||||
where RuntimeValue: TryInto<T, Error>, T: PartialOrd<T> {
|
||||
where RuntimeValue: TryInto<T, Error>, T: PartialOrd<T> + Display {
|
||||
context
|
||||
.value_stack_mut()
|
||||
.pop_pair_as::<T>()
|
||||
@ -835,14 +840,19 @@ impl Interpreter {
|
||||
match Interpreter::run_instruction(context, instruction)? {
|
||||
InstructionOutcome::RunInstruction => (),
|
||||
InstructionOutcome::RunNextInstruction => context.position += 1,
|
||||
InstructionOutcome::PopFrame(index) => {
|
||||
context.pop_frame()?;
|
||||
InstructionOutcome::Branch(index) => {
|
||||
if index != 0 {
|
||||
return Ok(InstructionOutcome::PopFrame(index - 1));
|
||||
context.discard_frame()?;
|
||||
return Ok(InstructionOutcome::Branch(index - 1));
|
||||
} else {
|
||||
context.pop_frame(true)?;
|
||||
return Ok(InstructionOutcome::RunInstruction);
|
||||
}
|
||||
},
|
||||
InstructionOutcome::End => {
|
||||
context.pop_frame(false)?;
|
||||
return Ok(InstructionOutcome::RunInstruction);
|
||||
},
|
||||
InstructionOutcome::Return => return Ok(InstructionOutcome::Return),
|
||||
}
|
||||
}
|
||||
@ -859,7 +869,7 @@ impl<'a> FunctionContext<'a> {
|
||||
locals: args,
|
||||
position: 0,
|
||||
};
|
||||
context.push_frame(body.len() - 1, match function.return_type() {
|
||||
context.push_frame(body.len() - 1, body.len() - 1, match function.return_type() {
|
||||
Some(value_type) => BlockType::Value(value_type),
|
||||
None => BlockType::NoResult,
|
||||
})?;
|
||||
@ -903,15 +913,21 @@ impl<'a> FunctionContext<'a> {
|
||||
&self.frame_stack
|
||||
}
|
||||
|
||||
pub fn push_frame(&mut self, position: usize, signature: BlockType) -> Result<(), Error> {
|
||||
pub fn push_frame(&mut self, branch_position: usize, end_position: usize, signature: BlockType) -> Result<(), Error> {
|
||||
self.frame_stack.push(BlockFrame {
|
||||
position: position,
|
||||
branch_position: branch_position,
|
||||
end_position: end_position,
|
||||
value_limit: self.value_stack.len(),
|
||||
signature: signature,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn pop_frame(&mut self) -> Result<(), Error> {
|
||||
pub fn discard_frame(&mut self) -> Result<(), Error> {
|
||||
self.frame_stack.pop()
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
pub fn pop_frame(&mut self, is_branch: bool) -> Result<(), Error> {
|
||||
let frame = self.frame_stack.pop()?;
|
||||
if frame.value_limit > self.value_stack.len() {
|
||||
return Err(Error::Stack("invalid stack len".into()));
|
||||
@ -922,7 +938,7 @@ impl<'a> FunctionContext<'a> {
|
||||
BlockType::NoResult => None,
|
||||
};
|
||||
self.value_stack.resize(frame.value_limit, RuntimeValue::I32(0));
|
||||
self.position = frame.position;
|
||||
self.position = if is_branch { frame.branch_position } else { frame.end_position };
|
||||
if let Some(frame_value) = frame_value {
|
||||
self.value_stack.push(frame_value)?;
|
||||
}
|
||||
@ -934,7 +950,8 @@ impl<'a> FunctionContext<'a> {
|
||||
impl BlockFrame {
|
||||
pub fn invalid() -> Self {
|
||||
BlockFrame {
|
||||
position: usize::max_value(),
|
||||
branch_position: usize::max_value(),
|
||||
end_position: usize::max_value(),
|
||||
value_limit: usize::max_value(),
|
||||
signature: BlockType::NoResult,
|
||||
}
|
||||
@ -950,158 +967,3 @@ fn effective_address(offset: u32, align: u32) -> Result<u32, Error> {
|
||||
.ok_or(Error::Interpreter("invalid memory alignment".into()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Weak;
|
||||
use elements::{Module, ValueType, Opcodes, Opcode, BlockType, FunctionType};
|
||||
use interpreter::Error;
|
||||
use interpreter::module::ModuleInstance;
|
||||
use interpreter::runner::{Interpreter, FunctionContext};
|
||||
use interpreter::value::{RuntimeValue, TryInto};
|
||||
use interpreter::variable::{VariableInstance, VariableType};
|
||||
|
||||
fn run_function_i32(body: &Opcodes, arg: i32) -> Result<i32, Error> {
|
||||
let ftype = FunctionType::new(vec![ValueType::I32], Some(ValueType::I32));
|
||||
let module = ModuleInstance::new(Weak::default(), Module::default()).unwrap();
|
||||
let mut context = FunctionContext::new(&module, 1024, 1024, &ftype, body.elements(), vec![
|
||||
VariableInstance::new(true, VariableType::I32, RuntimeValue::I32(arg)).unwrap()
|
||||
])?;
|
||||
Interpreter::run_function(&mut context, body.elements())
|
||||
.map(|v| v.unwrap().try_into().unwrap())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trap() {
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::Unreachable, // trap
|
||||
Opcode::End]);
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap_err(), Error::Trap("programmatic".into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nop() {
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::Nop, // nop
|
||||
Opcode::I32Const(20), // 20
|
||||
Opcode::Nop, // nop
|
||||
Opcode::End]);
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_then() {
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::I32Const(20), // 20
|
||||
Opcode::GetLocal(0), // read argument
|
||||
Opcode::If(BlockType::Value(ValueType::I32), // if argument != 0
|
||||
Opcodes::new(vec![
|
||||
Opcode::I32Const(10), // 10
|
||||
Opcode::End, // end
|
||||
])),
|
||||
Opcode::End]);
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 20);
|
||||
assert_eq!(run_function_i32(&body, 1).unwrap(), 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_then_else() {
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::GetLocal(0), // read argument
|
||||
Opcode::If(BlockType::Value(ValueType::I32), // if argument != 0
|
||||
Opcodes::new(vec![
|
||||
Opcode::I32Const(10), // 10
|
||||
Opcode::Else, // else
|
||||
Opcode::I32Const(20), // 20
|
||||
Opcode::End, // end
|
||||
])),
|
||||
Opcode::End]);
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 20);
|
||||
assert_eq!(run_function_i32(&body, 1).unwrap(), 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn return_from_if() {
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::GetLocal(0), // read argument
|
||||
Opcode::If(BlockType::Value(ValueType::I32), // if argument != 0
|
||||
Opcodes::new(vec![
|
||||
Opcode::I32Const(20), // 20
|
||||
Opcode::Return, // return
|
||||
Opcode::End,
|
||||
])),
|
||||
Opcode::I32Const(10), // 10
|
||||
Opcode::End]);
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 10);
|
||||
assert_eq!(run_function_i32(&body, 1).unwrap(), 20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block() {
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::Block(BlockType::Value(ValueType::I32), // mark block
|
||||
Opcodes::new(vec![
|
||||
Opcode::I32Const(10), // 10
|
||||
Opcode::End,
|
||||
])),
|
||||
Opcode::End]);
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loop_block() {
|
||||
// TODO: test
|
||||
/*
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::I32Const(2), // 2
|
||||
Opcode::Loop(BlockType::Value(ValueType::I32), // start loop
|
||||
Opcodes::new(vec![
|
||||
Opcode::GetLocal(0), // read argument
|
||||
Opcode::I32Const(1), // 1
|
||||
Opcode::I32Sub, // argument--
|
||||
Opcode::If(BlockType::Value(ValueType::I32), // if argument != 0
|
||||
Opcodes::new(vec![
|
||||
Opcode::I32Const(2), // 2
|
||||
Opcode::I32Mul, // prev_val * 2
|
||||
Opcode::Br(1), // branch to loop
|
||||
Opcode::End, // end (if)
|
||||
])),
|
||||
Opcode::End, // end (loop)
|
||||
])),
|
||||
Opcode::End]); // end (fun)
|
||||
|
||||
assert_eq!(run_function_i32(&body, 2).unwrap(), 10);
|
||||
*/
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn branch() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn branch_if() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn branch_table() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drop() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select() {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ use interpreter::Error;
|
||||
use interpreter::value::{RuntimeValue, TryInto};
|
||||
|
||||
/// Stack with limit.
|
||||
#[derive(Debug)]
|
||||
pub struct StackWithLimit<T> where T: Clone {
|
||||
/// Stack values.
|
||||
values: VecDeque<T>,
|
||||
|
1
src/interpreter/tests/mod.rs
Normal file
1
src/interpreter/tests/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
mod wabt;
|
579
src/interpreter/tests/wabt.rs
Normal file
579
src/interpreter/tests/wabt.rs
Normal file
@ -0,0 +1,579 @@
|
||||
///! Tests from https://github.com/WebAssembly/wabt/tree/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp
|
||||
|
||||
use std::sync::Weak;
|
||||
use elements::{Module, ValueType, Opcodes, Opcode, BlockType, FunctionType};
|
||||
use interpreter::Error;
|
||||
use interpreter::module::ModuleInstance;
|
||||
use interpreter::runner::{Interpreter, FunctionContext};
|
||||
use interpreter::value::{RuntimeValue, TryInto};
|
||||
use interpreter::variable::{VariableInstance, VariableType};
|
||||
|
||||
fn run_function_i32(body: &Opcodes, arg: i32) -> Result<i32, Error> {
|
||||
let ftype = FunctionType::new(vec![ValueType::I32], Some(ValueType::I32));
|
||||
let module = ModuleInstance::new(Weak::default(), Module::default()).unwrap();
|
||||
let mut context = FunctionContext::new(&module, 1024, 1024, &ftype, body.elements(), vec![
|
||||
VariableInstance::new(true, VariableType::I32, RuntimeValue::I32(arg)).unwrap(), // arg
|
||||
VariableInstance::new(true, VariableType::I32, RuntimeValue::I32(0)).unwrap(), // local1
|
||||
VariableInstance::new(true, VariableType::I32, RuntimeValue::I32(0)).unwrap(), // local2
|
||||
])?;
|
||||
Interpreter::run_function(&mut context, body.elements())
|
||||
.map(|v| v.unwrap().try_into().unwrap())
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/unreachable.txt
|
||||
#[test]
|
||||
fn unreachable() {
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::Unreachable, // trap
|
||||
Opcode::End]);
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap_err(), Error::Trap("programmatic".into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nop() {
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::Nop, // nop
|
||||
Opcode::I32Const(1), // [1]
|
||||
Opcode::Nop, // nop
|
||||
Opcode::End]);
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 1);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/expr-block.txt
|
||||
#[test]
|
||||
fn expr_block() {
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::Block(BlockType::Value(ValueType::I32), // mark block
|
||||
Opcodes::new(vec![
|
||||
Opcode::I32Const(10), // [10]
|
||||
Opcode::Drop,
|
||||
Opcode::I32Const(1), // [1]
|
||||
Opcode::End,
|
||||
])),
|
||||
Opcode::End]);
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 1);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/loop.txt
|
||||
#[test]
|
||||
fn loop_test() {
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::Loop(BlockType::NoResult, // loop
|
||||
Opcodes::new(vec![
|
||||
Opcode::GetLocal(1), // [local1]
|
||||
Opcode::GetLocal(0), // [local1, arg]
|
||||
Opcode::I32Add, // [arg + local1]
|
||||
Opcode::SetLocal(1), // [] + local1 = arg + local1
|
||||
Opcode::GetLocal(0), // [arg]
|
||||
Opcode::I32Const(1), // [arg, 1]
|
||||
Opcode::I32Add, // [arg + 1]
|
||||
Opcode::SetLocal(0), // [] + arg = arg + 1
|
||||
Opcode::GetLocal(0), // [arg]
|
||||
Opcode::I32Const(5), // [arg, 5]
|
||||
Opcode::I32LtS, // [arg < 5]
|
||||
Opcode::If(BlockType::NoResult,
|
||||
Opcodes::new(vec![
|
||||
Opcode::Br(1), // break loop
|
||||
Opcode::End,
|
||||
])),
|
||||
Opcode::End])), // end loop
|
||||
Opcode::GetLocal(1), // [local1]
|
||||
Opcode::End]);
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 10);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/if.txt#L3
|
||||
#[test]
|
||||
fn if_1() {
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::I32Const(0), // [0]
|
||||
Opcode::SetLocal(0), // [] + arg = 0
|
||||
Opcode::I32Const(1), // [1]
|
||||
Opcode::If(BlockType::NoResult, // if 1
|
||||
Opcodes::new(vec![
|
||||
Opcode::GetLocal(0), // [arg]
|
||||
Opcode::I32Const(1), // [arg, 1]
|
||||
Opcode::I32Add, // [arg + 1]
|
||||
Opcode::SetLocal(0), // [] + arg = arg + 1
|
||||
Opcode::End, // end if
|
||||
])),
|
||||
Opcode::I32Const(0), // [0]
|
||||
Opcode::If(BlockType::NoResult, // if 0
|
||||
Opcodes::new(vec![
|
||||
Opcode::GetLocal(0), // [arg]
|
||||
Opcode::I32Const(1), // [arg, 1]
|
||||
Opcode::I32Add, // [arg + 1]
|
||||
Opcode::SetLocal(0), // [] + arg = arg + 1
|
||||
Opcode::End, // end if
|
||||
])),
|
||||
Opcode::GetLocal(0), // [arg]
|
||||
Opcode::End]);
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 1);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/if.txt#L23
|
||||
#[test]
|
||||
fn if_2() {
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::I32Const(1), // [1]
|
||||
Opcode::If(BlockType::NoResult, // if 1
|
||||
Opcodes::new(vec![
|
||||
Opcode::I32Const(1), // [1]
|
||||
Opcode::SetLocal(0), // [] + arg = 1
|
||||
Opcode::Else, // else
|
||||
Opcode::I32Const(2), // [2]
|
||||
Opcode::SetLocal(0), // [] + arg = 2
|
||||
Opcode::End, // end if
|
||||
])),
|
||||
Opcode::I32Const(0), // [0]
|
||||
Opcode::If(BlockType::NoResult, // if 0
|
||||
Opcodes::new(vec![
|
||||
Opcode::I32Const(4), // [4]
|
||||
Opcode::SetLocal(1), // [] + local1 = 4
|
||||
Opcode::Else, // else
|
||||
Opcode::I32Const(8), // [8]
|
||||
Opcode::SetLocal(1), // [] + local1 = 8
|
||||
Opcode::End, // end if
|
||||
])),
|
||||
Opcode::GetLocal(0), // [arg]
|
||||
Opcode::GetLocal(1), // [arg, local1]
|
||||
Opcode::I32Add, // [arg + local1]
|
||||
Opcode::End]);
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 9);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/expr-if.txt
|
||||
#[test]
|
||||
fn expr_if() {
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::GetLocal(0), // [arg]
|
||||
Opcode::I32Const(0), // [arg, 0]
|
||||
Opcode::I32Eq, // [arg == 0]
|
||||
Opcode::If(BlockType::Value(ValueType::I32), // if arg == 0
|
||||
Opcodes::new(vec![
|
||||
Opcode::I32Const(1), // [1]
|
||||
Opcode::Else, // else
|
||||
Opcode::I32Const(2), // [2]
|
||||
Opcode::End, // end if
|
||||
])),
|
||||
Opcode::End]);
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 1);
|
||||
assert_eq!(run_function_i32(&body, 1).unwrap(), 2);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/br.txt#L4
|
||||
#[test]
|
||||
fn br_0() {
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::Block(BlockType::NoResult, // mark block
|
||||
Opcodes::new(vec![
|
||||
Opcode::I32Const(1), // [1]
|
||||
Opcode::If(BlockType::NoResult, // if 1
|
||||
Opcodes::new(vec![
|
||||
Opcode::Br(1), // break from block
|
||||
Opcode::End, // end if
|
||||
])),
|
||||
Opcode::I32Const(1), // [1]
|
||||
Opcode::SetLocal(0), // [] + arg = 1
|
||||
Opcode::End, // end block
|
||||
])),
|
||||
Opcode::I32Const(1), // [1]
|
||||
Opcode::SetLocal(1), // [] + local1 = 1
|
||||
Opcode::GetLocal(0), // [arg]
|
||||
Opcode::I32Const(0), // [arg, 0]
|
||||
Opcode::I32Eq, // [arg == 0]
|
||||
Opcode::GetLocal(1), // [arg == 0, local1]
|
||||
Opcode::I32Const(1), // [arg == 0, local1, 1]
|
||||
Opcode::I32Eq, // [arg == 0, local1 == 1]
|
||||
Opcode::I32Add, // [arg == 0 + local1 == 1]
|
||||
Opcode::End]);
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 2);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/br.txt#L26
|
||||
#[test]
|
||||
fn br_1() {
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::Block(BlockType::NoResult, // block1
|
||||
Opcodes::new(vec![
|
||||
Opcode::Block(BlockType::NoResult, // block2
|
||||
Opcodes::new(vec![
|
||||
Opcode::I32Const(1), // [1]
|
||||
Opcode::If(BlockType::NoResult, // if 1
|
||||
Opcodes::new(vec![
|
||||
Opcode::Br(2), // break from block2
|
||||
Opcode::End, // end if
|
||||
])),
|
||||
Opcode::I32Const(1), // [1]
|
||||
Opcode::SetLocal(0), // [] + arg = 1
|
||||
Opcode::End, // end (block2)
|
||||
])),
|
||||
Opcode::I32Const(1), // [1]
|
||||
Opcode::SetLocal(1), // [] + local1 = 1
|
||||
Opcode::End, // end (block1)
|
||||
])),
|
||||
Opcode::I32Const(1), // [1]
|
||||
Opcode::SetLocal(2), // [] + local2 = 1
|
||||
Opcode::GetLocal(0), // [arg]
|
||||
Opcode::I32Const(0), // [arg, 0]
|
||||
Opcode::I32Eq, // [arg == 0]
|
||||
Opcode::GetLocal(1), // [arg == 0, local1]
|
||||
Opcode::I32Const(0), // [arg == 0, local1, 0]
|
||||
Opcode::I32Eq, // [arg == 0, local1 == 0]
|
||||
Opcode::I32Add, // [arg == 0 + local1 == 0]
|
||||
Opcode::GetLocal(2), // [arg == 0 + local1 == 0, local2]
|
||||
Opcode::I32Const(1), // [arg == 0 + local1 == 0, local2, 1]
|
||||
Opcode::I32Eq, // [arg == 0 + local1 == 0, local2 == 1]
|
||||
Opcode::I32Add, // [arg == 0 + local1 == 0 + local2 == 1]
|
||||
Opcode::End]);
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 3);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/br.txt#L56
|
||||
#[test]
|
||||
fn br_2() {
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::Block(BlockType::NoResult, // block1
|
||||
Opcodes::new(vec![
|
||||
Opcode::Block(BlockType::NoResult, // block2
|
||||
Opcodes::new(vec![
|
||||
Opcode::I32Const(1), // [1]
|
||||
Opcode::If(BlockType::NoResult, // if 1
|
||||
Opcodes::new(vec![
|
||||
Opcode::Br(2), // break from block2
|
||||
Opcode::End, // end if
|
||||
])),
|
||||
Opcode::I32Const(1), // [1]
|
||||
Opcode::Return, // return 1
|
||||
Opcode::End, // end (block2)
|
||||
])),
|
||||
Opcode::End, // end (block1)
|
||||
])),
|
||||
Opcode::I32Const(2), // [2]
|
||||
Opcode::Return, // return 2
|
||||
Opcode::End]);
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 2);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/br.txt#L71
|
||||
#[test]
|
||||
fn br_3() {
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::Block(BlockType::NoResult, // block1
|
||||
Opcodes::new(vec![
|
||||
Opcode::Loop(BlockType::NoResult, // loop
|
||||
Opcodes::new(vec![
|
||||
Opcode::GetLocal(0), // [arg]
|
||||
Opcode::I32Const(1), // [arg, 1]
|
||||
Opcode::I32Add, // [arg + 1]
|
||||
Opcode::SetLocal(0), // [] + arg = arg + 1
|
||||
Opcode::GetLocal(0), // [arg]
|
||||
Opcode::I32Const(5), // [arg, 5]
|
||||
Opcode::I32GeS, // [5 >= arg]
|
||||
Opcode::If(BlockType::NoResult, // if 5 >= arg
|
||||
Opcodes::new(vec![
|
||||
Opcode::Br(2), // break from block1
|
||||
Opcode::End, // end
|
||||
])),
|
||||
Opcode::GetLocal(0), // [arg]
|
||||
Opcode::I32Const(4), // [arg, 4]
|
||||
Opcode::I32Eq, // [arg == 4]
|
||||
Opcode::If(BlockType::NoResult, // if arg == 4
|
||||
Opcodes::new(vec![
|
||||
Opcode::Br(1), // break from loop
|
||||
Opcode::End, // end
|
||||
])),
|
||||
Opcode::GetLocal(0), // [arg]
|
||||
Opcode::SetLocal(1), // [] + local1 = arg
|
||||
Opcode::Br(0), // continue loop
|
||||
Opcode::End, // end (loop)
|
||||
])),
|
||||
Opcode::End, // end (block1)
|
||||
])),
|
||||
Opcode::GetLocal(1), // [local1]
|
||||
Opcode::Return, // return local1
|
||||
Opcode::End]);
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 3);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/expr-br.txt
|
||||
#[test]
|
||||
fn expr_br() {
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::Block(BlockType::Value(ValueType::I32), // block1
|
||||
Opcodes::new(vec![
|
||||
Opcode::GetLocal(0), // [arg]
|
||||
Opcode::I32Const(0), // [arg, 0]
|
||||
Opcode::I32Eq, // [arg == 0]
|
||||
Opcode::If(BlockType::NoResult, // if arg == 0
|
||||
Opcodes::new(vec![
|
||||
Opcode::I32Const(1), // [1]
|
||||
Opcode::Br(1), // break from block1
|
||||
Opcode::End, // end (if)
|
||||
])),
|
||||
Opcode::I32Const(2), // [2]
|
||||
Opcode::End, // end (block1)
|
||||
])),
|
||||
Opcode::End]);
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 1);
|
||||
assert_eq!(run_function_i32(&body, 1).unwrap(), 2);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/brif.txt
|
||||
#[test]
|
||||
fn brif() {
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::Block(BlockType::NoResult, // block1
|
||||
Opcodes::new(vec![
|
||||
Opcode::GetLocal(0), // [arg]
|
||||
Opcode::BrIf(0), // if arg != 0: break from block1
|
||||
Opcode::I32Const(1), // [1]
|
||||
Opcode::Return, // return 1
|
||||
Opcode::End, // end (block1)
|
||||
])),
|
||||
Opcode::I32Const(2), // [2]
|
||||
Opcode::Return, // return 2
|
||||
Opcode::End]);
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 1);
|
||||
assert_eq!(run_function_i32(&body, 1).unwrap(), 2);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/brif-loop.txt
|
||||
#[test]
|
||||
fn brif_loop() {
|
||||
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/expr-brif.txt
|
||||
#[test]
|
||||
fn expr_brif() {
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::Loop(BlockType::NoResult, // loop
|
||||
Opcodes::new(vec![
|
||||
Opcode::GetLocal(1), // [local1]
|
||||
Opcode::I32Const(1), // [local1, 1]
|
||||
Opcode::I32Add, // [local1 + 1]
|
||||
Opcode::SetLocal(1), // [] + local1 = local1 + 1
|
||||
Opcode::GetLocal(1), // [local1]
|
||||
Opcode::GetLocal(0), // [local1, local0]
|
||||
Opcode::I32LtS, // [local1 < local0]
|
||||
Opcode::BrIf(0), // if local1 < local0: break from loop
|
||||
Opcode::End, // end (loop)
|
||||
])),
|
||||
Opcode::GetLocal(1), // [local1]
|
||||
Opcode::End]);
|
||||
|
||||
assert_eq!(run_function_i32(&body, 3).unwrap(), 3);
|
||||
assert_eq!(run_function_i32(&body, 10).unwrap(), 10);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/brtable.txt
|
||||
#[test]
|
||||
fn brtable() {
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::Block(BlockType::NoResult, // block3
|
||||
Opcodes::new(vec![
|
||||
Opcode::Block(BlockType::NoResult, // block2
|
||||
Opcodes::new(vec![
|
||||
Opcode::Block(BlockType::NoResult, // block1
|
||||
Opcodes::new(vec![
|
||||
Opcode::Block(BlockType::NoResult, // block0
|
||||
Opcodes::new(vec![
|
||||
Opcode::GetLocal(0), // [arg]
|
||||
Opcode::BrTable(vec![0, 1, 2], 3), // br_table
|
||||
Opcode::End, // end (block0)
|
||||
])),
|
||||
Opcode::I32Const(0), // [0]
|
||||
Opcode::Return, // return 0
|
||||
Opcode::End, // end (block1)
|
||||
])),
|
||||
Opcode::I32Const(1), // [1]
|
||||
Opcode::Return, // return 1
|
||||
Opcode::End, // end (block2)
|
||||
])),
|
||||
Opcode::End, // end (block3)
|
||||
])),
|
||||
Opcode::I32Const(2), // [2]
|
||||
Opcode::Return, // return 2
|
||||
Opcode::End]);
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 0);
|
||||
assert_eq!(run_function_i32(&body, 1).unwrap(), 1);
|
||||
assert_eq!(run_function_i32(&body, 2).unwrap(), 2);
|
||||
assert_eq!(run_function_i32(&body, 3).unwrap(), 2);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/return.txt
|
||||
#[test]
|
||||
fn return_test() {
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::GetLocal(0),
|
||||
Opcode::I32Const(0),
|
||||
Opcode::I32Eq,
|
||||
Opcode::If(BlockType::NoResult,
|
||||
Opcodes::new(vec![
|
||||
Opcode::I32Const(1),
|
||||
Opcode::Return,
|
||||
Opcode::End,
|
||||
])),
|
||||
Opcode::GetLocal(0),
|
||||
Opcode::I32Const(1),
|
||||
Opcode::I32Eq,
|
||||
Opcode::If(BlockType::NoResult,
|
||||
Opcodes::new(vec![
|
||||
Opcode::I32Const(2),
|
||||
Opcode::Return,
|
||||
Opcode::End,
|
||||
])),
|
||||
Opcode::I32Const(3),
|
||||
Opcode::Return,
|
||||
Opcode::End]);
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 1);
|
||||
assert_eq!(run_function_i32(&body, 1).unwrap(), 2);
|
||||
assert_eq!(run_function_i32(&body, 5).unwrap(), 3);
|
||||
}
|
||||
|
||||
/// https://github.com/WebAssembly/wabt/blob/8e1f6031e9889ba770c7be4a9b084da5f14456a0/test/interp/return-void.txt
|
||||
#[test]
|
||||
fn return_void() {
|
||||
// TODO: linear memory required
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
#[test]
|
||||
fn basics_loop() {
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::I32Const(2), // [2]
|
||||
Opcode::SetLocal(1), // [] + local1 = 2
|
||||
Opcode::Block(BlockType::NoResult, // add block label to exit from loop. TODO: is it the correct pattern?
|
||||
Opcodes::new(vec![
|
||||
Opcode::Loop(BlockType::NoResult, // start loop
|
||||
Opcodes::new(vec![
|
||||
Opcode::GetLocal(0), // [local0]
|
||||
Opcode::I32Const(1), // [local0, 1]
|
||||
Opcode::I32Sub, // [local0 - 1]
|
||||
Opcode::SetLocal(0), // [] + local0 = local0 - 1
|
||||
Opcode::GetLocal(0), // [local0]
|
||||
Opcode::If(BlockType::NoResult, // if local0 != 0
|
||||
Opcodes::new(vec![
|
||||
Opcode::GetLocal(1), // [local1]
|
||||
Opcode::I32Const(2), // [local1, 2]
|
||||
Opcode::I32Mul, // [local1 * 2]
|
||||
Opcode::SetLocal(1), // [] + local1 = local1 * 2
|
||||
Opcode::Else, // else
|
||||
Opcode::Br(2), // exit from loop (2 = if + loop)
|
||||
Opcode::End, // end (if)
|
||||
])),
|
||||
Opcode::End, // end (loop)
|
||||
])),
|
||||
Opcode::End, // end (block)
|
||||
])),
|
||||
Opcode::GetLocal(1), // [local1]
|
||||
Opcode::End]); // end (fun)
|
||||
|
||||
assert_eq!(run_function_i32(&body, 2).unwrap(), 4);
|
||||
assert_eq!(run_function_i32(&body, 8).unwrap(), 256);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn basics_if_then() {
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::I32Const(20), // 20
|
||||
Opcode::GetLocal(0), // read argument
|
||||
Opcode::If(BlockType::Value(ValueType::I32), // if argument != 0
|
||||
Opcodes::new(vec![
|
||||
Opcode::I32Const(10), // 10
|
||||
Opcode::End, // end
|
||||
])),
|
||||
Opcode::End]);
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 20);
|
||||
assert_eq!(run_function_i32(&body, 1).unwrap(), 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basics_if_then_else() {
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::GetLocal(0), // read argument
|
||||
Opcode::If(BlockType::Value(ValueType::I32), // if argument != 0
|
||||
Opcodes::new(vec![
|
||||
Opcode::I32Const(10), // 10
|
||||
Opcode::Else, // else
|
||||
Opcode::I32Const(20), // 20
|
||||
Opcode::End, // end
|
||||
])),
|
||||
Opcode::End]);
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 20);
|
||||
assert_eq!(run_function_i32(&body, 1).unwrap(), 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basics_return() {
|
||||
let body = Opcodes::new(vec![
|
||||
Opcode::GetLocal(0), // read argument
|
||||
Opcode::If(BlockType::Value(ValueType::I32), // if argument != 0
|
||||
Opcodes::new(vec![
|
||||
Opcode::I32Const(20), // 20
|
||||
Opcode::Return, // return
|
||||
Opcode::End,
|
||||
])),
|
||||
Opcode::I32Const(10), // 10
|
||||
Opcode::End]);
|
||||
|
||||
assert_eq!(run_function_i32(&body, 0).unwrap(), 10);
|
||||
assert_eq!(run_function_i32(&body, 1).unwrap(), 20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn branch_if() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn branch_table() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drop() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select() {
|
||||
// TODO
|
||||
}*/
|
Reference in New Issue
Block a user