use crate::*;
use core::ops::ControlFlow;
impl LocalVariable {
fn addr(&self, fp: u32) -> usize {
(fp as i32 + self.frame_offset as i32) as usize
}
}
struct CallFrame {
frame_length: u16,
module_delta: u8,
parameter_size: u8,
}
impl CallFrame {
pub fn from_bits(bits: u32) -> CallFrame {
unsafe { core::mem::transmute(bits) }
}
pub fn into_bits(self) -> u32 {
unsafe { core::mem::transmute(self) }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InvokeError {
ParamLenMismatch,
ParamTypeMismatch,
StackOverflow,
}
impl Engine {
fn call_impl_enter_module(&mut self, f_ref: WasmRef) -> Result<(), InterpreterBreak> {
let module_delta = if f_ref.module == self.module {
0
} else {
let delta = f_ref.module.0.wrapping_sub(self.module.0);
self.module = f_ref.module;
self.memory = self.store.get_memory(self.module).clone();
self.table = self.store.get_table(self.module).clone();
delta
};
self.call_impl(module_delta, f_ref.index)
}
fn call_impl(&mut self, module_delta: u8, index: u16) -> Result<(), InterpreterBreak> {
let m = &self.store.modules()[self.module.0 as usize];
let f = &m.functions[index as usize];
let required_stack_space = f.stack_usage as usize + 2 + f.local_size as usize;
if self.stack.len() < self.sp + required_stack_space {
return Err(InterpreterBreak::Trap(TrapReason::StackOverflow));
}
let frame_length = (self.sp - self.fp as usize) as u32;
assert!(frame_length <= 0xFFFF);
let frame = CallFrame {
frame_length: frame_length as u16,
module_delta,
parameter_size: f.parameter_size,
};
self.stack.write_u32(self.sp, frame.into_bits());
self.stack.write_u32(self.sp + 1, self.pc.0);
self.fp = self.sp as u32;
for i in 0..(f.local_size as usize) {
self.stack.write_u32(self.sp + 2 + i, 0);
}
self.sp += 2 + f.local_size as usize;
self.pc = f.expr.0;
self.jumped = true;
Ok(())
}
pub fn invoke(&mut self, f_ref: WasmRef, params: &[Value]) -> Result<(), InvokeError> {
assert_eq!(self.pc, JumpTarget::SENTINEL);
assert_eq!(self.sp, 0);
assert_eq!(self.fp, 0);
let m = &self.store.modules()[f_ref.module.0 as usize];
let f = &m.functions[f_ref.index as usize];
let ty = &m.types[f.ty.0 as usize];
if ty.params.len() != params.len() {
return Err(InvokeError::ParamLenMismatch);
}
for (pi, pd) in params.iter().zip(&ty.params) {
match (*pi, *pd) {
(Value::I32(v), ValType::I32) => {
self.stack.write_u32(self.sp, v as u32);
self.sp += 1;
}
(Value::I64(v), ValType::I64) => {
self.stack.write_u64(self.sp, v as u64);
self.sp += 2;
}
(Value::F32(v), ValType::F32) => {
self.stack.write_f32(self.sp, v);
self.sp += 1;
}
(Value::F64(v), ValType::F64) => {
self.stack.write_f64(self.sp, v);
self.sp += 2;
}
_ => {
return Err(InvokeError::ParamTypeMismatch);
}
}
}
self.module = f_ref.module;
self.memory = self.store.get_memory(self.module).clone();
self.table = self.store.get_table(self.module).clone();
self.call_impl(0, f_ref.index)
.map_err(|_| InvokeError::StackOverflow)?;
self.jumped = false;
self.result = None;
Ok(())
}
}
pub struct Interpreter;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TrapReason {
Unreachable,
Host,
DivideByZero,
InvalidTableIndex,
InvalidTableFunctionType,
UninitializedTableElement,
GlobalGetFailed,
GlobalSetFailed,
OutOfMemory,
MemoryRefNotUnique,
MemoryOutOfBounds,
StackOverflow,
UnrepresentableResult,
IntegerOverflow,
BadConversionToInteger,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InterpreterBreak {
Finished,
Trap(TrapReason),
Pause,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum InterpreterResult {
Finished,
Trap(TrapReason),
Pause,
OutOfFuel,
ReaderError(IrReaderError),
}
impl Interpreter {
fn br_impl(
&self,
label_pc_offset: JumpOffset,
addr: LabelTarget,
state: &mut Engine,
) -> Result<(), InterpreterBreak> {
if addr.is_sentinel() {
return self.return_(addr.arity() as u8, state);
}
let depth = addr.depth() as usize;
state.sp -= depth;
match addr.arity() {
LabelArity::None => {}
LabelArity::I32 => {
let w = state.stack.read_u32(state.sp + depth - 1);
state.stack.write_u32(state.sp, w);
state.sp += 1;
}
LabelArity::I64 => {
let w1 = state.stack.read_u32(state.sp + depth - 2);
let w2 = state.stack.read_u32(state.sp + depth - 1);
state.stack.write_u32(state.sp, w1);
state.stack.write_u32(state.sp + 1, w2);
state.sp += 2;
}
}
state.pc += label_pc_offset;
state.pc += addr.jump();
state.jumped = true;
Ok(())
}
}
pub trait InterpreterRunner {
fn run(
&self,
code: &[Box<TextPage>],
state: &mut Engine,
n_instructions: usize,
) -> InterpreterResult;
}
impl<T: IrVisitor<State = Engine, Error = InterpreterBreak>> InterpreterRunner for T {
fn run(
&self,
code: &[Box<TextPage>],
state: &mut T::State,
n_instructions: usize,
) -> InterpreterResult {
let reader = IrReader::new(code);
for _ in 0..n_instructions {
let mut pc = state.pc;
if pc == JumpTarget::SENTINEL {
return InterpreterResult::Finished;
}
let i_res = reader.visit_instruction(state, &mut pc, self);
if state.jumped {
state.jumped = false;
} else {
state.pc = pc;
}
match i_res {
Ok(_) => {}
Err(InterpreterBreak::Trap(trap_reason)) => {
state.sp = 0;
state.pc = JumpTarget::SENTINEL;
state.fp = 0;
state.jumped = false;
return InterpreterResult::Trap(trap_reason);
}
Err(InterpreterBreak::Pause) => return InterpreterResult::Pause,
Err(InterpreterBreak::Finished) => return InterpreterResult::Finished,
}
}
InterpreterResult::OutOfFuel
}
}
impl From<MemoryError> for InterpreterBreak {
fn from(e: MemoryError) -> Self {
match e {
MemoryError::OutOfBounds => InterpreterBreak::Trap(TrapReason::MemoryOutOfBounds),
MemoryError::OutOfMemory => InterpreterBreak::Trap(TrapReason::OutOfMemory),
_ => unreachable!(),
}
}
}
impl From<HostFunctionBreak> for InterpreterBreak {
fn from(err: HostFunctionBreak) -> Self {
match err {
HostFunctionBreak::Trap => InterpreterBreak::Trap(TrapReason::Host),
HostFunctionBreak::Pause => InterpreterBreak::Pause,
}
}
}
impl From<TrapReason> for InterpreterBreak {
fn from(err: TrapReason) -> Self {
InterpreterBreak::Trap(err)
}
}
macro_rules! instruction {
($name:ident, f32 -> f32, $f:ident, $( $t:tt )*) => {
fn $name(&self, state: &mut Self::State) -> Result<(), Self::Error> {
let $f = state.stack.read_f32(state.sp - 1);
state.stack.write_f32(state.sp - 1, $($t)*);
Ok(())
}
};
($name:ident, i32 -> i32, $i:ident, $( $t:tt )*) => {
fn $name(&self, state: &mut Self::State) -> Result<(), Self::Error> {
let $i = state.stack.read_u32(state.sp - 1) as i32;
state.stack.write_u32(state.sp - 1, ($($t)*) as u32);
Ok(())
}
};
($name:ident, f64 -> f64, $f:ident, $( $t:tt )*) => {
fn $name(&self, state: &mut Self::State) -> Result<(), Self::Error> {
let $f = state.stack.read_f64(state.sp - 2);
state.stack.write_f64(state.sp - 2, $($t)*);
Ok(())
}
};
($name:ident, i64 -> i64, $i:ident, $( $t:tt )*) => {
fn $name(&self, state: &mut Self::State) -> Result<(), Self::Error> {
let $i = state.stack.read_u64(state.sp - 2) as i64;
state.stack.write_u64(state.sp - 2, ($($t)*) as u64);
Ok(())
}
};
($name:ident, f32, f32 -> f32, $a:ident, $b:ident, $( $t:tt )*) => {
fn $name(&self, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 1;
let $b = state.stack.read_f32(state.sp);
let $a = state.stack.read_f32(state.sp - 1);
state.stack.write_f32(state.sp - 1, $($t)*);
Ok(())
}
};
($name:ident, i32, i32 -> i32, $a:ident, $b:ident, $( $t:tt )*) => {
fn $name(&self, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 1;
let $b = state.stack.read_u32(state.sp) as i32;
let $a = state.stack.read_u32(state.sp - 1) as i32;
state.stack.write_u32(state.sp - 1, ($($t)*) as u32);
Ok(())
}
};
($name:ident, i32 -> bool, $i:ident, $( $t:tt )*) => {
fn $name(&self, state: &mut Self::State) -> Result<(), Self::Error> {
let $i = state.stack.read_u32(state.sp - 1) as i32;
state.stack.write_u32(state.sp - 1, if $($t)* { 1 } else { 0 });
Ok(())
}
};
($name:ident, i64 -> bool, $i:ident, $( $t:tt )*) => {
fn $name(&self, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 1;
let $i = state.stack.read_u64(state.sp - 1) as i64;
state.stack.write_u32(state.sp - 1, if $($t)* { 1 } else { 0 });
Ok(())
}
};
($name:ident, i32, i32 -> bool, $a:ident, $b:ident, $( $t:tt )*) => {
fn $name(&self, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 1;
let $b = state.stack.read_u32(state.sp) as i32;
let $a = state.stack.read_u32(state.sp - 1) as i32;
state.stack.write_u32(state.sp - 1, if $($t)* { 1 } else { 0 });
Ok(())
}
};
($name:ident, f32, f32 -> bool, $a:ident, $b:ident, $( $t:tt )*) => {
fn $name(&self, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 1;
let $b = state.stack.read_f32(state.sp);
let $a = state.stack.read_f32(state.sp - 1);
state.stack.write_u32(state.sp - 1, if $($t)* { 1 } else { 0 });
Ok(())
}
};
($name:ident, f64, f64 -> f64, $a:ident, $b:ident, $( $t:tt )*) => {
fn $name(&self, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 2;
let $b = state.stack.read_f64(state.sp);
let $a = state.stack.read_f64(state.sp - 2);
state.stack.write_f64(state.sp - 2, $($t)*);
Ok(())
}
};
($name:ident, i64, i64 -> i64, $a:ident, $b:ident, $( $t:tt )*) => {
fn $name(&self, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 2;
let $b = state.stack.read_u64(state.sp) as i64;
let $a = state.stack.read_u64(state.sp - 2) as i64;
state.stack.write_u64(state.sp - 2, ($($t)*) as u64);
Ok(())
}
};
($name:ident, i64, i64 -> bool, $a:ident, $b:ident, $( $t:tt )*) => {
fn $name(&self, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 3;
let $b = state.stack.read_u64(state.sp + 1) as i64;
let $a = state.stack.read_u64(state.sp - 1) as i64;
state.stack.write_u32(state.sp - 1, if $($t)* { 1 } else { 0 });
Ok(())
}
};
($name:ident, f64, f64 -> bool, $a:ident, $b:ident, $( $t:tt )*) => {
fn $name(&self, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 3;
let $b = state.stack.read_f64(state.sp + 1);
let $a = state.stack.read_f64(state.sp - 1);
state.stack.write_u32(state.sp - 1, if $($t)* { 1 } else { 0 });
Ok(())
}
};
}
impl BaseVisitor for Interpreter {
type Error = InterpreterBreak;
type State = Engine;
fn unreachable(&self, _: &mut Self::State) -> Result<(), Self::Error> {
Err(InterpreterBreak::Trap(TrapReason::Unreachable))
}
fn nop(&self, _: &mut Self::State) -> Result<(), Self::Error> {
Ok(())
}
fn i32_load(&self, m: MemArg, state: &mut Self::State) -> Result<(), Self::Error> {
let addr = Memory::effective_address(state.stack.read_u32(state.sp - 1), m.offset)?;
let val = state.memory.load_u32(addr)?;
state.stack.write_u32(state.sp - 1, val);
Ok(())
}
fn i64_load(&self, m: MemArg, state: &mut Self::State) -> Result<(), Self::Error> {
let addr = Memory::effective_address(state.stack.read_u32(state.sp - 1), m.offset)?;
let val = state.memory.load_u64(addr)?;
state.stack.write_u64(state.sp - 1, val);
state.sp += 1;
Ok(())
}
fn f32_load(&self, m: MemArg, state: &mut Self::State) -> Result<(), Self::Error> {
let addr = Memory::effective_address(state.stack.read_u32(state.sp - 1), m.offset)?;
let val = state.memory.load_u32(addr)?;
state.stack.write_u32(state.sp - 1, val);
Ok(())
}
fn f64_load(&self, m: MemArg, state: &mut Self::State) -> Result<(), Self::Error> {
let addr = Memory::effective_address(state.stack.read_u32(state.sp - 1), m.offset)?;
let val = state.memory.load_u64(addr)?;
state.stack.write_u64(state.sp - 1, val);
state.sp += 1;
Ok(())
}
fn i32_load8_s(&self, m: MemArg, state: &mut Self::State) -> Result<(), Self::Error> {
let addr = Memory::effective_address(state.stack.read_u32(state.sp - 1), m.offset)?;
let val = state.memory.load_u8(addr)? as i8 as i32;
state.stack.write_u32(state.sp - 1, val as u32);
Ok(())
}
fn i32_load8_u(&self, m: MemArg, state: &mut Self::State) -> Result<(), Self::Error> {
let addr = Memory::effective_address(state.stack.read_u32(state.sp - 1), m.offset)?;
let val = state.memory.load_u8(addr)? as u32;
state.stack.write_u32(state.sp - 1, val);
Ok(())
}
fn i32_load16_s(&self, m: MemArg, state: &mut Self::State) -> Result<(), Self::Error> {
let addr = Memory::effective_address(state.stack.read_u32(state.sp - 1), m.offset)?;
let val = state.memory.load_u16(addr)? as i16 as i32;
state.stack.write_u32(state.sp - 1, val as u32);
Ok(())
}
fn i32_load16_u(&self, m: MemArg, state: &mut Self::State) -> Result<(), Self::Error> {
let addr = Memory::effective_address(state.stack.read_u32(state.sp - 1), m.offset)?;
let val = state.memory.load_u16(addr)? as u32;
state.stack.write_u32(state.sp - 1, val);
Ok(())
}
fn i64_load8_s(&self, m: MemArg, state: &mut Self::State) -> Result<(), Self::Error> {
let addr = Memory::effective_address(state.stack.read_u32(state.sp - 1), m.offset)?;
let val = state.memory.load_u8(addr)? as i8 as i64 as u64;
state.stack.write_u64(state.sp - 1, val);
state.sp += 1;
Ok(())
}
fn i64_load8_u(&self, m: MemArg, state: &mut Self::State) -> Result<(), Self::Error> {
let addr = Memory::effective_address(state.stack.read_u32(state.sp - 1), m.offset)?;
let val = state.memory.load_u8(addr)? as u64;
state.stack.write_u64(state.sp - 1, val);
state.sp += 1;
Ok(())
}
fn i64_load16_s(&self, m: MemArg, state: &mut Self::State) -> Result<(), Self::Error> {
let addr = Memory::effective_address(state.stack.read_u32(state.sp - 1), m.offset)?;
let val = state.memory.load_u16(addr)? as i16 as i64 as u64;
state.stack.write_u64(state.sp - 1, val);
state.sp += 1;
Ok(())
}
fn i64_load16_u(&self, m: MemArg, state: &mut Self::State) -> Result<(), Self::Error> {
let addr = Memory::effective_address(state.stack.read_u32(state.sp - 1), m.offset)?;
let val = state.memory.load_u16(addr)? as u64;
state.stack.write_u64(state.sp - 1, val);
state.sp += 1;
Ok(())
}
fn i64_load32_s(&self, m: MemArg, state: &mut Self::State) -> Result<(), Self::Error> {
let addr = Memory::effective_address(state.stack.read_u32(state.sp - 1), m.offset)?;
let val = state.memory.load_u32(addr)? as i32 as i64 as u64;
state.stack.write_u64(state.sp - 1, val);
state.sp += 1;
Ok(())
}
fn i64_load32_u(&self, m: MemArg, state: &mut Self::State) -> Result<(), Self::Error> {
let addr = Memory::effective_address(state.stack.read_u32(state.sp - 1), m.offset)?;
let val = state.memory.load_u32(addr)? as u64;
state.stack.write_u64(state.sp - 1, val);
state.sp += 1;
Ok(())
}
fn i32_store(&self, m: MemArg, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 2;
let val = state.stack.read_u32(state.sp + 1);
let addr = Memory::effective_address(state.stack.read_u32(state.sp), m.offset)?;
state.memory.store_u32(addr, val)?;
Ok(())
}
fn i64_store(&self, m: MemArg, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 3;
let val = state.stack.read_u64(state.sp + 1);
let addr = Memory::effective_address(state.stack.read_u32(state.sp), m.offset)?;
state.memory.store_u64(addr, val)?;
Ok(())
}
fn f32_store(&self, m: MemArg, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 2;
let val = state.stack.read_u32(state.sp + 1);
let addr = Memory::effective_address(state.stack.read_u32(state.sp), m.offset)?;
state.memory.store_u32(addr, val)?;
Ok(())
}
fn f64_store(&self, m: MemArg, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 3;
let val = state.stack.read_u64(state.sp + 1);
let addr = Memory::effective_address(state.stack.read_u32(state.sp), m.offset)?;
state.memory.store_u64(addr, val)?;
Ok(())
}
fn i32_store8(&self, m: MemArg, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 2;
let val = state.stack.read_u32(state.sp + 1) as u8;
let addr = Memory::effective_address(state.stack.read_u32(state.sp), m.offset)?;
state.memory.store_u8(addr, val)?;
Ok(())
}
fn i32_store16(&self, m: MemArg, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 2;
let val = state.stack.read_u32(state.sp + 1) as u16;
let addr = Memory::effective_address(state.stack.read_u32(state.sp), m.offset)?;
state.memory.store_u16(addr, val)?;
Ok(())
}
fn i64_store8(&self, m: MemArg, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 3;
let val = state.stack.read_u64(state.sp + 1) as u8;
let addr = Memory::effective_address(state.stack.read_u32(state.sp), m.offset)?;
state.memory.store_u8(addr, val)?;
Ok(())
}
fn i64_store16(&self, m: MemArg, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 3;
let val = state.stack.read_u64(state.sp + 1) as u16;
let addr = Memory::effective_address(state.stack.read_u32(state.sp), m.offset)?;
state.memory.store_u16(addr, val)?;
Ok(())
}
fn i64_store32(&self, m: MemArg, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 3;
let val = state.stack.read_u64(state.sp + 1) as u32;
let addr = Memory::effective_address(state.stack.read_u32(state.sp), m.offset)?;
state.memory.store_u32(addr, val)?;
Ok(())
}
fn memory_size(&self, state: &mut Self::State) -> Result<(), Self::Error> {
state.stack.write_u32(state.sp, state.memory.size());
state.sp += 1;
Ok(())
}
fn memory_grow(&self, state: &mut Self::State) -> Result<(), Self::Error> {
let n = state.stack.read_u32(state.sp - 1);
state.clear_memory();
let memory = state.store.get_memory_mut(state.module);
let result: Result<u32, MemoryError> = if memory.is_zero() {
Err(MemoryError::OutOfMemory)
} else if let Some(memory) = memory.get_mut() {
memory.grow(n)
} else {
return Err(InterpreterBreak::Trap(TrapReason::MemoryRefNotUnique));
};
state.memory = memory.clone();
match result {
Ok(old_size) => {
state.stack.write_u32(state.sp - 1, old_size);
}
Err(_) => {
state.stack.write_u32(state.sp - 1, 0xFFFF_FFFF);
}
}
Ok(())
}
fn i32_const(&self, n: i32, state: &mut Self::State) -> Result<(), Self::Error> {
state.stack.write_u32(state.sp, n as u32);
state.sp += 1;
Ok(())
}
fn i64_const(&self, n: i64, state: &mut Self::State) -> Result<(), Self::Error> {
state.stack.write_u64(state.sp, n as u64);
state.sp += 2;
Ok(())
}
fn f32_const(&self, z: f32, state: &mut Self::State) -> Result<(), Self::Error> {
state.stack.write_f32(state.sp, z);
state.sp += 1;
Ok(())
}
fn f64_const(&self, z: f64, state: &mut Self::State) -> Result<(), Self::Error> {
state.stack.write_f64(state.sp, z);
state.sp += 2;
Ok(())
}
instruction!(i32_eqz, i32 -> bool, i, i == 0);
instruction!(i32_eq, i32, i32 -> bool, a, b, a == b);
instruction!(i32_ne, i32, i32 -> bool, a, b, a != b);
instruction!(i32_lt_s, i32, i32 -> bool, a, b, a < b);
instruction!(i32_lt_u, i32, i32 -> bool, a, b, (a as u32) < (b as u32));
instruction!(i32_gt_s, i32, i32 -> bool, a, b, a > b);
instruction!(i32_gt_u, i32, i32 -> bool, a, b, (a as u32) > (b as u32));
instruction!(i32_le_s, i32, i32 -> bool, a, b, a <= b);
instruction!(i32_le_u, i32, i32 -> bool, a, b, (a as u32) <= (b as u32));
instruction!(i32_ge_s, i32, i32 -> bool, a, b, a >= b);
instruction!(i32_ge_u, i32, i32 -> bool, a, b, (a as u32) >= (b as u32));
instruction!(i64_eqz, i64 -> bool, i, i == 0);
instruction!(i64_eq, i64, i64 -> bool, a, b, a == b);
instruction!(i64_ne, i64, i64 -> bool, a, b, a != b);
instruction!(i64_lt_s, i64, i64 -> bool, a, b, a < b);
instruction!(i64_lt_u, i64, i64 -> bool, a, b, (a as u64) < (b as u64));
instruction!(i64_gt_s, i64, i64 -> bool, a, b, a > b);
instruction!(i64_gt_u, i64, i64 -> bool, a, b, (a as u64) > (b as u64));
instruction!(i64_le_s, i64, i64 -> bool, a, b, a <= b);
instruction!(i64_le_u, i64, i64 -> bool, a, b, (a as u64) <= (b as u64));
instruction!(i64_ge_s, i64, i64 -> bool, a, b, a >= b);
instruction!(i64_ge_u, i64, i64 -> bool, a, b, (a as u64) >= (b as u64));
instruction!(f32_eq, f32, f32 -> bool, a, b, a == b);
instruction!(f32_ne, f32, f32 -> bool, a, b, a != b);
instruction!(f32_lt, f32, f32 -> bool, a, b, a < b);
instruction!(f32_gt, f32, f32 -> bool, a, b, a > b);
instruction!(f32_le, f32, f32 -> bool, a, b, a <= b);
instruction!(f32_ge, f32, f32 -> bool, a, b, a >= b);
instruction!(f64_eq, f64, f64 -> bool, a, b, a == b);
instruction!(f64_ne, f64, f64 -> bool, a, b, a != b);
instruction!(f64_lt, f64, f64 -> bool, a, b, a < b);
instruction!(f64_gt, f64, f64 -> bool, a, b, a > b);
instruction!(f64_le, f64, f64 -> bool, a, b, a <= b);
instruction!(f64_ge, f64, f64 -> bool, a, b, a >= b);
instruction!(i32_clz, i32 -> i32, i, i.leading_zeros() as i32);
instruction!(i32_ctz, i32 -> i32, i, i.trailing_zeros() as i32);
instruction!(i32_popcnt, i32 -> i32, i, i.count_ones() as i32);
instruction!(i32_add, i32, i32 -> i32, a, b, a.wrapping_add(b));
instruction!(i32_sub, i32, i32 -> i32, a, b, a.wrapping_sub(b));
instruction!(i32_mul, i32, i32 -> i32, a, b, a.wrapping_mul(b));
instruction!(i32_div_s, i32, i32 -> i32, a, b, {
if b == 0 {
return Err(TrapReason::DivideByZero.into());
}
if a == i32::MIN && b == -1 {
return Err(TrapReason::IntegerOverflow.into());
}
a / b
});
instruction!(i32_div_u, i32, i32 -> i32, a, b, {
if b == 0 {
return Err(InterpreterBreak::Trap(TrapReason::DivideByZero))
} else {
((a as u32) / (b as u32)) as i32
}
});
instruction!(i32_rem_s, i32, i32 -> i32, a, b, {
if b == 0 {
return Err(InterpreterBreak::Trap(TrapReason::DivideByZero))
} else {
a.wrapping_rem(b)
}
});
instruction!(i32_rem_u, i32, i32 -> i32, a, b, {
if b == 0 {
return Err(InterpreterBreak::Trap(TrapReason::DivideByZero))
} else {
(a as u32).wrapping_rem(b as u32) as i32
}
});
instruction!(i32_and, i32, i32 -> i32, a, b, a & b);
instruction!(i32_or, i32, i32 -> i32, a, b, a | b);
instruction!(i32_xor, i32, i32 -> i32, a, b, a ^ b);
instruction!(i32_shl, i32, i32 -> i32, a, b, a.wrapping_shl(b as u32));
instruction!(i32_shr_s, i32, i32 -> i32, a, b, a.wrapping_shr(b as u32));
instruction!(i32_shr_u, i32, i32 -> i32, a, b, (a as u32).wrapping_shr(b as u32) as i32);
instruction!(i32_rotl, i32, i32 -> i32, a, b, a.rotate_left(b as u32));
instruction!(i32_rotr, i32, i32 -> i32, a, b, a.rotate_right(b as u32));
instruction!(i64_clz, i64 -> i64, i, i.leading_zeros() as i64);
instruction!(i64_ctz, i64 -> i64, i, i.trailing_zeros() as i64);
instruction!(i64_popcnt, i64 -> i64, i, i.count_ones() as i64);
instruction!(i64_add, i64, i64 -> i64, a, b, a.wrapping_add(b));
instruction!(i64_sub, i64, i64 -> i64, a, b, a.wrapping_sub(b));
instruction!(i64_mul, i64, i64 -> i64, a, b, a.wrapping_mul(b));
instruction!(i64_div_s, i64, i64 -> i64, a, b, {
if b == 0 {
return Err(TrapReason::DivideByZero.into());
}
if a == i64::MIN && b == -1 {
return Err(TrapReason::IntegerOverflow.into());
}
a / b
});
instruction!(i64_div_u, i64, i64 -> i64, a, b, {
if b == 0 {
return Err(InterpreterBreak::Trap(TrapReason::DivideByZero))
} else {
(a as u64).wrapping_div(b as u64) as i64
}
});
instruction!(i64_rem_s, i64, i64 -> i64, a, b, {
if b == 0 {
return Err(InterpreterBreak::Trap(TrapReason::DivideByZero))
} else {
a.wrapping_rem(b)
}
});
instruction!(i64_rem_u, i64, i64 -> i64, a, b, {
if b == 0 {
return Err(InterpreterBreak::Trap(TrapReason::DivideByZero))
} else {
(a as u64).wrapping_rem(b as u64) as i64
}
});
instruction!(i64_and, i64, i64 -> i64, a, b, a & b);
instruction!(i64_or, i64, i64 -> i64, a, b, a | b);
instruction!(i64_xor, i64, i64 -> i64, a, b, a ^ b);
instruction!(i64_shl, i64, i64 -> i64, a, b, a.wrapping_shl(b as u32));
instruction!(i64_shr_s, i64, i64 -> i64, a, b, a.wrapping_shr(b as u32));
instruction!(i64_shr_u, i64, i64 -> i64, a, b, (a as u64).wrapping_shr(b as u32) as i64);
instruction!(i64_rotl, i64, i64 -> i64, a, b, a.rotate_left(b as u32));
instruction!(i64_rotr, i64, i64 -> i64, a, b, a.rotate_right(b as u32));
instruction!(f32_abs, f32 -> f32, f, libm::fabsf(f));
instruction!(f32_neg, f32 -> f32, f, -f);
instruction!(f32_ceil, f32 -> f32, f, {
if f.is_nan() {
f32::NAN
} else {
libm::ceilf(f)
}
});
instruction!(f32_floor, f32 -> f32, f, {
if f.is_nan() {
f32::NAN
} else {
libm::floorf(f)
}
});
instruction!(f32_trunc, f32 -> f32, f, {
if f.is_nan() {
f32::NAN
} else {
libm::truncf(f)
}
});
instruction!(f32_nearest, f32 -> f32, f, {
if f.is_nan() {
f32::NAN
} else {
libm::rintf(f)
}
});
instruction!(f32_sqrt, f32 -> f32, f, libm::sqrtf(f));
instruction!(f32_add, f32, f32 -> f32, a, b, a + b);
instruction!(f32_sub, f32, f32 -> f32, a, b, a - b);
instruction!(f32_mul, f32, f32 -> f32, a, b, a * b);
instruction!(f32_div, f32, f32 -> f32, a, b, a / b);
instruction!(f32_min, f32, f32 -> f32, a, b, {
if a.is_nan() || b.is_nan() {
f32::NAN
} else if a == 0.0 && b == 0.0 {
if a.to_bits() >> 31 == 1 {
a
} else {
b
}
} else {
a.min(b)
}
});
instruction!(f32_max, f32, f32 -> f32, a, b, {
if a.is_nan() || b.is_nan() {
f32::NAN
} else if a == 0.0 && b == 0.0 {
if a.to_bits() >> 31 == 1 {
b
} else {
a
}
} else {
a.max(b)
}
});
instruction!(f32_copysign, f32, f32 -> f32, a, b, libm::copysignf(a, b));
instruction!(f64_abs, f64 -> f64, f, libm::fabs(f));
instruction!(f64_neg, f64 -> f64, f, -f);
instruction!(f64_ceil, f64 -> f64, f, {
if f.is_nan() {
f64::NAN
} else {
libm::ceil(f)
}
});
instruction!(f64_floor, f64 -> f64, f, {
if f.is_nan() {
f64::NAN
} else {
libm::floor(f)
}
});
instruction!(f64_trunc, f64 -> f64, f, {
if f.is_nan() {
f64::NAN
} else {
libm::trunc(f)
}
});
instruction!(f64_nearest, f64 -> f64, f, {
if f.is_nan() {
f64::NAN
} else {
libm::rint(f)
}
});
instruction!(f64_sqrt, f64 -> f64, f, libm::sqrt(f));
instruction!(f64_add, f64, f64 -> f64, a, b, a + b);
instruction!(f64_sub, f64, f64 -> f64, a, b, a - b);
instruction!(f64_mul, f64, f64 -> f64, a, b, a * b);
instruction!(f64_div, f64, f64 -> f64, a, b, a / b);
instruction!(f64_min, f64, f64 -> f64, a, b, {
if a.is_nan() || b.is_nan() {
f64::NAN
} else if a == 0.0 && b == 0.0 {
if a.to_bits() >> 63 == 1 {
a
} else {
b
}
} else {
a.min(b)
}
});
instruction!(f64_max, f64, f64 -> f64, a, b, {
if a.is_nan() || b.is_nan() {
f64::NAN
} else if a == 0.0 && b == 0.0 {
if a.to_bits() >> 63 == 1 {
b
} else {
a
}
} else {
a.max(b)
}
});
instruction!(f64_copysign, f64, f64 -> f64, a, b, libm::copysign(a, b));
fn i32_wrap_i64(&self, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 1;
Ok(())
}
fn i32_trunc_f32_s(&self, state: &mut Self::State) -> Result<(), Self::Error> {
let f = state.stack.read_f32(state.sp - 1);
if f.is_infinite() {
return Err(TrapReason::UnrepresentableResult.into());
}
if f.is_nan() {
return Err(TrapReason::BadConversionToInteger.into());
}
if f >= 2147483648.0f32 || f <= -2147483904.0f32 {
return Err(TrapReason::UnrepresentableResult.into());
}
state.stack.write_u32(state.sp - 1, f as i32 as u32);
Ok(())
}
fn i32_trunc_f32_u(&self, state: &mut Self::State) -> Result<(), Self::Error> {
let f = state.stack.read_f32(state.sp - 1);
if f.is_infinite() {
return Err(TrapReason::UnrepresentableResult.into());
}
if f.is_nan() {
return Err(TrapReason::BadConversionToInteger.into());
}
if f >= 4294967296.0f32 || f <= -1.0f32 {
return Err(TrapReason::UnrepresentableResult.into());
}
state.stack.write_u32(state.sp - 1, f as u32);
Ok(())
}
fn i32_trunc_f64_s(&self, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 1;
let f = state.stack.read_f64(state.sp - 1);
if f.is_infinite() {
return Err(TrapReason::UnrepresentableResult.into());
}
if f.is_nan() {
return Err(TrapReason::BadConversionToInteger.into());
}
if f >= 2147483648.0f64 || f <= -2147483649.0f64 {
return Err(TrapReason::UnrepresentableResult.into());
}
state.stack.write_u32(state.sp - 1, f as i32 as u32);
Ok(())
}
fn i32_trunc_f64_u(&self, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 1;
let f = state.stack.read_f64(state.sp - 1);
if f.is_infinite() {
return Err(TrapReason::UnrepresentableResult.into());
}
if f.is_nan() {
return Err(TrapReason::BadConversionToInteger.into());
}
if f >= 4294967296.0f64 || f <= -1.0f64 {
return Err(TrapReason::UnrepresentableResult.into());
}
state.stack.write_u32(state.sp - 1, f as u32);
Ok(())
}
fn i64_extend_i32_s(&self, state: &mut Self::State) -> Result<(), Self::Error> {
let i = state.stack.read_u32(state.sp - 1) as i32;
let extended = i as i64 as u64;
state.stack.write_u64(state.sp - 1, extended);
state.sp += 1;
Ok(())
}
fn i64_extend_i32_u(&self, state: &mut Self::State) -> Result<(), Self::Error> {
state.stack.write_u32(state.sp, 0);
state.sp += 1;
Ok(())
}
fn i64_trunc_f32_s(&self, state: &mut Self::State) -> Result<(), Self::Error> {
let f = state.stack.read_f32(state.sp - 1);
if f.is_infinite() {
return Err(TrapReason::UnrepresentableResult.into());
}
if f.is_nan() {
return Err(TrapReason::BadConversionToInteger.into());
}
if f >= 9223372036854775808.0f32 || f <= -9223373136366403584.0f32 {
return Err(TrapReason::UnrepresentableResult.into());
}
let i = f as i64 as u64;
state.stack.write_u64(state.sp - 1, i);
state.sp += 1;
Ok(())
}
fn i64_trunc_f32_u(&self, state: &mut Self::State) -> Result<(), Self::Error> {
let f = state.stack.read_f32(state.sp - 1);
if f.is_infinite() {
return Err(TrapReason::UnrepresentableResult.into());
}
if f.is_nan() {
return Err(TrapReason::BadConversionToInteger.into());
}
if f >= 18446744073709551616.0f32 || f <= -1.0f32 {
return Err(TrapReason::UnrepresentableResult.into());
}
let u = f as u64;
state.stack.write_u64(state.sp - 1, u);
state.sp += 1;
Ok(())
}
fn i64_trunc_f64_s(&self, state: &mut Self::State) -> Result<(), Self::Error> {
let f = state.stack.read_f64(state.sp - 2);
if f.is_infinite() {
return Err(TrapReason::UnrepresentableResult.into());
}
if f.is_nan() {
return Err(TrapReason::BadConversionToInteger.into());
}
if f >= 9223372036854775808.0f64 || f <= -9223372036854777856.0f64 {
return Err(TrapReason::UnrepresentableResult.into());
}
let i = f as i64 as u64;
state.stack.write_u64(state.sp - 2, i);
Ok(())
}
fn i64_trunc_f64_u(&self, state: &mut Self::State) -> Result<(), Self::Error> {
let f = state.stack.read_f64(state.sp - 2);
if f.is_infinite() {
return Err(TrapReason::UnrepresentableResult.into());
}
if f.is_nan() {
return Err(TrapReason::BadConversionToInteger.into());
}
if f >= 18446744073709551616.0f64 || f <= -1.0f64 {
return Err(TrapReason::UnrepresentableResult.into());
}
let u = f as u64;
state.stack.write_u64(state.sp - 2, u);
Ok(())
}
fn f32_convert_i32_s(&self, state: &mut Self::State) -> Result<(), Self::Error> {
let i = state.stack.read_u32(state.sp - 1) as i32;
state.stack.write_f32(state.sp - 1, i as f32);
Ok(())
}
fn f32_convert_i32_u(&self, state: &mut Self::State) -> Result<(), Self::Error> {
let u = state.stack.read_u32(state.sp - 1);
state.stack.write_f32(state.sp - 1, u as f32);
Ok(())
}
fn f32_convert_i64_s(&self, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 1;
let i = state.stack.read_u64(state.sp - 1) as i64;
state.stack.write_f32(state.sp - 1, i as f32);
Ok(())
}
fn f32_convert_i64_u(&self, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 1;
let u = state.stack.read_u64(state.sp - 1);
state.stack.write_f32(state.sp - 1, u as f32);
Ok(())
}
fn f32_demote_f64(&self, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 1;
let f = state.stack.read_f64(state.sp - 1);
state.stack.write_f32(state.sp - 1, f as f32);
Ok(())
}
fn f64_convert_i32_s(&self, state: &mut Self::State) -> Result<(), Self::Error> {
let i = state.stack.read_u32(state.sp - 1) as i32;
state.stack.write_f64(state.sp - 1, i as f64);
state.sp += 1;
Ok(())
}
fn f64_convert_i32_u(&self, state: &mut Self::State) -> Result<(), Self::Error> {
let u = state.stack.read_u32(state.sp - 1);
state.stack.write_f64(state.sp - 1, u as f64);
state.sp += 1;
Ok(())
}
fn f64_convert_i64_s(&self, state: &mut Self::State) -> Result<(), Self::Error> {
let i = state.stack.read_u64(state.sp - 2) as i64;
state.stack.write_f64(state.sp - 2, i as f64);
Ok(())
}
fn f64_convert_i64_u(&self, state: &mut Self::State) -> Result<(), Self::Error> {
let u = state.stack.read_u64(state.sp - 2);
state.stack.write_f64(state.sp - 2, u as f64);
Ok(())
}
fn f64_promote_f32(&self, state: &mut Self::State) -> Result<(), Self::Error> {
let f = state.stack.read_f32(state.sp - 1);
state.stack.write_f64(state.sp - 1, f as f64);
state.sp += 1;
Ok(())
}
}
impl IrVisitor for Interpreter {
fn drop(&self, ty: ValType, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= match ty {
ValType::I32 | ValType::F32 => 1,
ValType::I64 | ValType::F64 => 2,
};
Ok(())
}
fn select(&self, ty: ValType, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 1;
let c = state.stack.read_u32(state.sp);
match ty {
ValType::I32 | ValType::F32 => {
if c != 0 {
state.sp -= 1;
} else {
let val2 = state.stack.read_u32(state.sp - 1);
state.stack.write_u32(state.sp - 2, val2);
state.sp -= 1;
}
}
ValType::I64 | ValType::F64 => {
if c != 0 {
state.sp -= 2;
} else {
let val2 = state.stack.read_u64(state.sp - 2);
state.stack.write_u64(state.sp - 4, val2);
state.sp -= 2;
}
}
}
Ok(())
}
fn if_(&self, false_address: LabelTarget, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 1;
let v = state.stack.read_u32(state.sp);
if v == 0 {
state.pc += JumpOffset::offset(1);
state.pc += false_address.jump();
state.jumped = true;
}
Ok(())
}
fn br(&self, addr: LabelTarget, state: &mut Self::State) -> Result<(), Self::Error> {
self.br_impl(JumpOffset::offset(1), addr, state)
}
fn br_if(&self, true_address: LabelTarget, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 1;
let v = state.stack.read_u32(state.sp);
if v != 0 {
self.br_impl(JumpOffset::offset(1), true_address, state)?;
}
Ok(())
}
fn br_table(
&self,
n: u32,
cases: impl FnOnce(u32) -> LabelTarget,
state: &mut Self::State,
) -> Result<(), Self::Error> {
state.sp -= 1;
let v = state.stack.read_u32(state.sp);
let label = cases(v);
let op_offset = if n >= 0xFF { 2 } else { 1 };
if v < n {
self.br_impl(JumpOffset::offset(op_offset + (2 * v as i32)), label, state)?;
} else {
self.br_impl(JumpOffset::offset(op_offset + (2 * n as i32)), label, state)?;
}
Ok(())
}
fn return_(&self, return_size: u8, state: &mut Self::State) -> Result<(), Self::Error> {
let return_size = return_size as usize;
let fp = state.fp as usize;
let return_pc = JumpTarget(state.stack.read_u32(state.fp as usize + 1));
let call_frame = CallFrame::from_bits(state.stack.read_u32(state.fp as usize));
let return_fp = (fp as u32) - (call_frame.frame_length as u32);
let parameter_start = fp - (call_frame.parameter_size as usize);
for i in 0..return_size {
let val = state.stack.read_u32(state.sp - return_size + i);
state.stack.write_u32(parameter_start + i, val);
}
state.fp = return_fp;
if call_frame.module_delta != 0 {
let restore_module = state.module.0.wrapping_sub(call_frame.module_delta);
state.module = ModuleRef(restore_module);
state.memory = state.store.get_memory(state.module).clone();
state.table = state.store.get_table(state.module).clone();
}
if return_pc == JumpTarget::SENTINEL {
state.sp = parameter_start;
state.pc = JumpTarget::SENTINEL;
state.jumped = true;
let return_value = match return_size {
0 => RawValue::from_64(0),
1 => RawValue::from_32(state.stack.read_u32(state.sp)),
2 => RawValue::from_64(state.stack.read_u64(state.sp)),
_ => unreachable!(),
};
state.result = Some(return_value);
Err(InterpreterBreak::Finished)
} else {
state.sp = parameter_start + return_size;
state.pc = return_pc + 2; state.jumped = true;
Ok(())
}
}
fn call(&self, x: u16, state: &mut Self::State) -> Result<(), Self::Error> {
state.call_impl(0, x)
}
fn call_host(
&self,
module: HostModuleRef,
x: u16,
state: &mut Self::State,
) -> Result<(), Self::Error> {
let m = &state.store.host_modules()[module.0 as usize];
let f = &m.functions[x as usize];
let mut sv: StaticVec<Value, 9> = StaticVec::new();
state.sp -= f.param_size();
let mut offset = 0;
for p_ty in f.params().iter() {
match p_ty {
ValType::I32 => {
sv.push(Value::I32(state.stack.read_u32(state.sp + offset) as i32))
.unwrap();
offset += 1;
}
ValType::I64 => {
sv.push(Value::I64(state.stack.read_u64(state.sp + offset) as i64))
.unwrap();
offset += 2;
}
ValType::F32 => {
sv.push(Value::F32(state.stack.read_f32(state.sp + offset)))
.unwrap();
offset += 1;
}
ValType::F64 => {
sv.push(Value::F64(state.stack.read_f64(state.sp + offset)))
.unwrap();
offset += 2;
}
}
}
match f.call(state, &sv) {
ControlFlow::Continue(v) => {
match v {
None => {}
Some(Value::I32(i)) => {
state.stack.write_u32(state.sp, i as u32);
state.sp += 1;
}
Some(Value::I64(i)) => {
state.stack.write_u64(state.sp, i as u64);
state.sp += 2;
}
Some(Value::F32(f)) => {
state.stack.write_f32(state.sp, f);
state.sp += 1;
}
Some(Value::F64(f)) => {
state.stack.write_f64(state.sp, f);
state.sp += 2;
}
}
Ok(())
}
ControlFlow::Break(b) => Err(b.into()),
}
}
fn call_extern(
&self,
module_ref: ModuleRef,
x: u16,
state: &mut Self::State,
) -> Result<(), Self::Error> {
state.call_impl_enter_module(WasmRef {
module: module_ref,
index: x,
})
}
fn call_indirect(&self, x: TypeIdx, state: &mut Self::State) -> Result<(), Self::Error> {
state.sp -= 1;
let i = state.stack.read_u32(state.sp) as usize;
let m = &state.store.modules()[state.module.0 as usize];
let f_expected = &m.types[x.0 as usize];
match state.table.get(i) {
None => Err(InterpreterBreak::Trap(TrapReason::InvalidTableIndex)),
Some(TableElement::Func { module, index }) => {
let m = &state.store.modules()[module.0 as usize];
let f = &m.functions[*index as usize];
let f_actual = &m.types[f.ty.0 as usize];
if f_actual.params != f_expected.params || f_actual.returns != f_expected.returns {
return Err(InterpreterBreak::Trap(TrapReason::InvalidTableFunctionType));
}
state.call_impl_enter_module(WasmRef {
module: *module,
index: *index,
})
}
Some(TableElement::Host { module, index }) => {
let m = &state.store.host_modules()[module.0 as usize];
let f = &m.functions[*index as usize];
if f.params() != f_expected.params[..] || f.returns() != f_expected.returns[..] {
return Err(InterpreterBreak::Trap(TrapReason::InvalidTableFunctionType));
}
self.call_host(*module, *index, state)
}
Some(TableElement::Uninitialized) => Err(InterpreterBreak::Trap(
TrapReason::UninitializedTableElement,
)),
}
}
fn local_get(&self, l: LocalVariable, state: &mut Self::State) -> Result<(), Self::Error> {
let local_addr = l.addr(state.fp);
match l.ty {
ValType::I32 | ValType::F32 => {
let val = state.stack.read_u32(local_addr);
state.stack.write_u32(state.sp, val);
state.sp += 1;
}
ValType::I64 | ValType::F64 => {
let val = state.stack.read_u64(local_addr);
state.stack.write_u64(state.sp, val);
state.sp += 2;
}
}
Ok(())
}
fn local_set(&self, l: LocalVariable, state: &mut Self::State) -> Result<(), Self::Error> {
let local_addr = l.addr(state.fp);
match l.ty {
ValType::I32 | ValType::F32 => {
state.sp -= 1;
let val = state.stack.read_u32(state.sp);
state.stack.write_u32(local_addr, val);
}
ValType::I64 | ValType::F64 => {
state.sp -= 2;
let val = state.stack.read_u64(state.sp);
state.stack.write_u64(local_addr, val);
}
}
Ok(())
}
fn local_tee(&self, l: LocalVariable, state: &mut Self::State) -> Result<(), Self::Error> {
let local_addr = l.addr(state.fp);
match l.ty {
ValType::I32 | ValType::F32 => {
let val = state.stack.read_u32(state.sp - 1);
state.stack.write_u32(local_addr, val);
}
ValType::I64 | ValType::F64 => {
let val = state.stack.read_u64(state.sp - 2);
state.stack.write_u64(local_addr, val);
}
}
Ok(())
}
fn global_get(&self, idx: u16, state: &mut Self::State) -> Result<(), Self::Error> {
let m = &state.store.modules()[state.module.0 as usize];
let g = &m.globals[idx as usize];
match g.type_.ty {
ValType::I32 | ValType::F32 => {
let val = g.value.read_32();
state.stack.write_u32(state.sp, val);
state.sp += 1;
}
ValType::I64 | ValType::F64 => {
let val = g.value.read_64();
state.stack.write_u64(state.sp, val);
state.sp += 2;
}
}
Ok(())
}
fn global_get_host(
&self,
module: HostModuleRef,
index: u16,
state: &mut Self::State,
) -> Result<(), Self::Error> {
let m = &state.store.host_modules()[module.0 as usize];
match m.globals[index as usize]
.value
.read()
.or(Err(InterpreterBreak::Trap(TrapReason::GlobalGetFailed)))?
{
Value::I32(i) => {
state.stack.write_u32(state.sp, i as u32);
state.sp += 1;
}
Value::I64(i) => {
state.stack.write_u64(state.sp, i as u64);
state.sp += 2;
}
Value::F32(f) => {
state.stack.write_f32(state.sp, f);
state.sp += 1;
}
Value::F64(f) => {
state.stack.write_f64(state.sp, f);
state.sp += 2;
}
};
Ok(())
}
fn global_set(&self, idx: u16, state: &mut Self::State) -> Result<(), Self::Error> {
let m = &mut state.store.modules_mut()[state.module.0 as usize];
let g = &mut m.globals[idx as usize];
match g.type_.ty {
ValType::I32 | ValType::F32 => {
state.sp -= 1;
let val = state.stack.read_u32(state.sp);
g.value.write_32(val);
}
ValType::I64 | ValType::F64 => {
state.sp -= 2;
let val = state.stack.read_u64(state.sp);
g.value.write_64(val);
}
}
Ok(())
}
fn global_set_host(
&self,
module: HostModuleRef,
index: u16,
state: &mut Self::State,
) -> Result<(), Self::Error> {
let m = &state.store.host_modules()[module.0 as usize];
let g = &m.globals[index as usize];
match g.value.ty() {
ValType::I32 => {
state.sp -= 1;
let val = state.stack.read_u32(state.sp) as i32;
g.value.write(Value::I32(val))
}
ValType::I64 => {
state.sp -= 2;
let val = state.stack.read_u64(state.sp) as i64;
g.value.write(Value::I64(val))
}
ValType::F32 => {
state.sp -= 1;
let f = state.stack.read_f32(state.sp);
g.value.write(Value::F32(f))
}
ValType::F64 => {
state.sp -= 2;
let f = state.stack.read_f64(state.sp);
g.value.write(Value::F64(f))
}
}
.or(Err(InterpreterBreak::Trap(TrapReason::GlobalSetFailed)))
}
fn global_get_extern(
&self,
module: ModuleRef,
index: u16,
state: &mut Self::State,
) -> Result<(), Self::Error> {
let current = state.module;
state.module = module;
let res = self.global_get(index, state);
state.module = current;
res
}
fn global_set_extern(
&self,
module: ModuleRef,
index: u16,
state: &mut Self::State,
) -> Result<(), Self::Error> {
let current = state.module;
state.module = module;
let res = self.global_set(index, state);
state.module = current;
res
}
}
#[cfg(test)]
#[path = "interpreter_tests.rs"]
mod interpreter_tests;
#[cfg(kani)]
mod kani_proofs {
use super::*;
#[kani::proof]
fn proof_callframe_transmute_roundtrip() {
let frame_length: u16 = kani::any();
let module_delta: u8 = kani::any();
let parameter_size: u8 = kani::any();
let frame = CallFrame {
frame_length,
module_delta,
parameter_size,
};
let bits = frame.into_bits();
let decoded = CallFrame::from_bits(bits);
assert_eq!(
decoded.frame_length, frame_length,
"frame_length must round-trip correctly"
);
assert_eq!(
decoded.module_delta, module_delta,
"module_delta must round-trip correctly"
);
assert_eq!(
decoded.parameter_size, parameter_size,
"parameter_size must round-trip correctly"
);
}
#[kani::proof]
fn proof_callframe_deterministic() {
let frame_length: u16 = kani::any();
let module_delta: u8 = kani::any();
let parameter_size: u8 = kani::any();
let frame1 = CallFrame {
frame_length,
module_delta,
parameter_size,
};
let frame2 = CallFrame {
frame_length,
module_delta,
parameter_size,
};
let bits1 = frame1.into_bits();
let bits2 = frame2.into_bits();
assert_eq!(bits1, bits2, "Same inputs must produce same bit pattern");
}
#[kani::proof]
fn proof_callframe_layout() {
assert_eq!(
core::mem::size_of::<CallFrame>(),
4,
"CallFrame must be exactly 4 bytes"
);
let frame = CallFrame {
frame_length: 0x1234,
module_delta: 0x56,
parameter_size: 0x78,
};
let bits = frame.into_bits();
let decoded = CallFrame::from_bits(bits);
assert_eq!(decoded.frame_length, 0x1234);
assert_eq!(decoded.module_delta, 0x56);
assert_eq!(decoded.parameter_size, 0x78);
}
}