use super::ast::*;
use super::ops::{FloatDemoteOp, FloatOp, FloatPromoteOp, IntOp};
use super::runtime::*;
use super::types;
use super::values::Value;
use std::mem;
use std::rc::Rc;
pub struct Interpreter<'a> {
pub stack: Vec<Value>,
frame: StackFrame,
funcs: &'a FuncInstStore,
tables: &'a TableInstStore,
mems: &'a mut MemInstStore,
globals: &'a mut GlobalInstStore,
}
#[derive(Debug, PartialEq)]
pub enum TrapOrigin {
Unreachable,
UndefinedResult,
CallIndirectElemNotFound,
CallIndirectElemUnitialized,
CallIndirectTypesDiffer,
LoadOutOfMemory,
StoreOutOfMemory,
StackOverflow,
HostFunction(HostFunctionError),
}
#[derive(Debug, PartialEq)]
pub struct Trap {
pub origin: TrapOrigin,
}
#[derive(Debug, PartialEq)]
pub enum Control {
Continue,
Branch { nesting_levels: u32 },
Return,
}
use self::Control::*;
type IntResult = Result<Control, Trap>;
pub struct StackFrame {
module: Option<Rc<ModuleInst>>,
stack_idx: usize, nested_levels: usize,
}
const STACK_BUDGET: usize = 300;
impl StackFrame {
pub fn new(module: Option<Rc<ModuleInst>>) -> StackFrame {
StackFrame {
module,
stack_idx: 0,
nested_levels: STACK_BUDGET,
}
}
fn module(&self) -> &ModuleInst {
self.module.as_ref().unwrap()
}
pub fn push(&self, module: Option<Rc<ModuleInst>>, stack_idx: usize) -> Option<StackFrame> {
if self.nested_levels == 0 {
return None;
}
Some(StackFrame {
module,
stack_idx,
nested_levels: self.nested_levels - 1,
})
}
}
impl<'a> Interpreter<'a> {
pub fn new(
funcs: &'a FuncInstStore,
tables: &'a TableInstStore,
globals: &'a mut GlobalInstStore,
mems: &'a mut MemInstStore,
) -> Interpreter<'a> {
Interpreter {
stack: Vec::new(),
frame: StackFrame::new(None),
funcs,
tables,
globals,
mems,
}
}
pub fn push(&mut self, val: Value) {
self.stack.push(val)
}
pub fn pop(&mut self) -> Option<Value> {
self.stack.pop()
}
pub fn get_memory_mut(&mut self) -> &mut [u8] {
&mut self.mems[self.frame.module().mem_addrs[0]].data
}
fn instr(&mut self, instr: &Instr) -> IntResult {
use super::ast::Instr::*;
match *instr {
Unreachable => self.unreachable(),
Nop => self.nop(),
Block(ref result_type, ref instrs) => self.block(result_type, instrs),
Loop(_, ref instrs) => self.loop_(instrs),
If(ref result_type, ref if_instrs, ref else_instrs) => {
self.if_(result_type, if_instrs, else_instrs)
}
Br(nesting_levels) => self.branch(nesting_levels),
BrIf(nesting_levels) => self.branch_cond(nesting_levels),
BrTable(ref all_levels, default_level) => self.branch_table(all_levels, default_level),
Return => self.return_(),
Call(idx) => self.call(self.frame.module().func_addrs[idx as usize]),
CallIndirect(idx) => self.call_indirect(idx),
Drop_ => self.drop(),
Select => self.select(),
GetLocal(idx) => self.get_local(idx),
SetLocal(idx) => self.set_local(idx),
TeeLocal(idx) => self.tee_local(idx),
GetGlobal(idx) => self.get_global(idx),
SetGlobal(idx) => self.set_global(idx),
Load(ref memop) => self.load(memop),
Store(ref memop) => self.store(memop),
CurrentMemory => self.current_memory(),
GrowMemory => self.grow_memory(),
Const(c) => self.const_(c),
IUnary(t, ref op) => self.iunary(t, op),
FUnary(t, ref op) => self.funary(t, op),
IBin(t, ref op) => self.ibin(t, op),
FBin(t, ref op) => self.fbin(t, op),
ITest(t, ref op) => self.itest(t, op),
IRel(t, ref op) => self.irel(t, op),
FRel(t, ref op) => self.frel(t, op),
Convert(ref op) => self.cvtop(op),
}
}
fn unreachable(&self) -> IntResult {
Err(Trap {
origin: TrapOrigin::Unreachable,
})
}
fn nop(&self) -> IntResult {
Ok(Continue)
}
fn block(&mut self, result_type: &[types::Value], instrs: &[Instr]) -> IntResult {
let local_stack_begin = self.stack.len();
for instr in instrs {
match self.instr(instr)? {
Branch { nesting_levels } => {
return Ok(if nesting_levels == 0 {
let junk_end = self.stack.len() - result_type.len();
self.stack.drain(local_stack_begin..junk_end);
Continue
} else {
Branch {
nesting_levels: nesting_levels - 1,
}
});
}
Return => return Ok(Return), Continue => {}
}
}
Ok(Continue)
}
fn loop_(&mut self, instrs: &[Instr]) -> IntResult {
let local_stack_begin = self.stack.len();
'outer: loop {
for instr in instrs {
let res = self.instr(instr)?;
match res {
Branch { nesting_levels } => {
if nesting_levels == 0 {
self.stack.truncate(local_stack_begin);
continue 'outer;
} else {
return Ok(Branch {
nesting_levels: nesting_levels - 1,
});
}
}
Return => return Ok(Return),
Continue => {}
}
}
return Ok(Continue);
}
}
fn branch(&self, nesting_levels: u32) -> IntResult {
Ok(Branch { nesting_levels })
}
fn branch_cond(&mut self, nesting_levels: u32) -> IntResult {
match self.stack.pop().unwrap() {
Value::I32(c) => Ok(if c != 0 {
Branch { nesting_levels }
} else {
Continue
}),
_ => unreachable!(),
}
}
fn branch_table(&mut self, all_levels: &[u32], default_level: u32) -> IntResult {
match self.stack.pop().unwrap() {
Value::I32(c) => Ok(Branch {
nesting_levels: *all_levels.get(c as usize).unwrap_or(&default_level),
}),
_ => unreachable!(),
}
}
fn if_(
&mut self,
result_type: &[types::Value],
if_instrs: &[Instr],
else_instrs: &[Instr],
) -> IntResult {
let c = match self.stack.pop().unwrap() {
Value::I32(c) => c,
_ => unreachable!(),
};
Ok(if c != 0 {
self.block(result_type, if_instrs)?
} else {
self.block(result_type, else_instrs)?
})
}
fn drop(&mut self) -> IntResult {
self.stack.pop();
Ok(Continue)
}
fn select(&mut self) -> IntResult {
let b = self.stack.pop().unwrap();
let (v1, v2) = self.pop2();
match b {
Value::I32(c) => self.stack.push(if c != 0 { v1 } else { v2 }),
_ => unreachable!(),
}
Ok(Continue)
}
fn const_(&mut self, c: Value) -> IntResult {
self.stack.push(c);
Ok(Continue)
}
fn iunary(&mut self, _t: types::Int, op: &IUnOp) -> IntResult {
let v = match self.stack.pop().unwrap() {
Value::I32(c) => Value::I32(self.type_iunary(c, op)),
Value::I64(c) => Value::I64(self.type_iunary(c, op)),
_ => unreachable!(),
};
self.stack.push(v);
Ok(Continue)
}
fn type_iunary<T>(&self, v: T, op: &IUnOp) -> T
where
T: IntOp,
{
match *op {
IUnOp::Clz => v.leading_zeros(),
IUnOp::Ctz => v.trailing_zeros(),
IUnOp::Popcnt => v.count_ones(),
}
}
fn funary(&mut self, _t: types::Float, op: &FUnOp) -> IntResult {
let v = match self.stack.pop().unwrap() {
Value::F32(c) => Value::F32(self.type_funary(c, op)),
Value::F64(c) => Value::F64(self.type_funary(c, op)),
_ => unreachable!(),
};
self.stack.push(v);
Ok(Continue)
}
fn type_funary<T>(&self, v: T, op: &FUnOp) -> T
where
T: FloatOp,
{
match *op {
FUnOp::Neg => v.neg(),
FUnOp::Abs => v.abs(),
FUnOp::Ceil => v.ceil(),
FUnOp::Floor => v.floor(),
FUnOp::Trunc => v.trunc(),
FUnOp::Nearest => v.nearest(),
FUnOp::Sqrt => v.sqrt(),
}
}
fn ibin(&mut self, _t: types::Int, op: &IBinOp) -> IntResult {
let res = match self.pop2() {
(Value::I32(c1), Value::I32(c2)) => self.type_ibin(c1, c2, op).map(Value::I32),
(Value::I64(c1), Value::I64(c2)) => self.type_ibin(c1, c2, op).map(Value::I64),
_ => unreachable!(),
};
if let Some(v) = res {
self.stack.push(v);
Ok(Continue)
} else {
Err(Trap {
origin: TrapOrigin::UndefinedResult,
})
}
}
fn type_ibin<T>(&self, c1: T, c2: T, op: &IBinOp) -> Option<T>
where
T: IntOp,
{
let res = match *op {
IBinOp::Add => c1.add(c2),
IBinOp::Sub => c1.sub(c2),
IBinOp::Mul => c1.mul(c2),
IBinOp::DivS => c1.divs(c2)?,
IBinOp::DivU => c1.divu(c2)?,
IBinOp::RemS => c1.rems(c2)?,
IBinOp::RemU => c1.remu(c2)?,
IBinOp::And => c1.and(c2),
IBinOp::Or => c1.or(c2),
IBinOp::Xor => c1.xor(c2),
IBinOp::Shl => c1.shl(c2),
IBinOp::ShrS => c1.shrs(c2),
IBinOp::ShrU => c1.shru(c2),
IBinOp::Rotr => c1.rotr(c2),
IBinOp::Rotl => c1.rotl(c2),
};
Some(res)
}
fn fbin(&mut self, _t: types::Float, op: &FBinOp) -> IntResult {
let res = match self.pop2() {
(Value::F32(c1), Value::F32(c2)) => Value::F32(self.type_fbin(c1, c2, op)),
(Value::F64(c1), Value::F64(c2)) => Value::F64(self.type_fbin(c1, c2, op)),
_ => unreachable!(),
};
self.stack.push(res);
Ok(Continue)
}
fn type_fbin<T>(&self, c1: T, c2: T, op: &FBinOp) -> T
where
T: FloatOp,
{
match *op {
FBinOp::Add => c1.add(c2),
FBinOp::Sub => c1.sub(c2),
FBinOp::Mul => c1.mul(c2),
FBinOp::Div => c1.div(c2),
FBinOp::Min => c1.min(c2),
FBinOp::Max => c1.max(c2),
FBinOp::CopySign => c1.copysign(c2),
}
}
fn itest(&mut self, _t: types::Int, op: &ITestOp) -> IntResult {
let v = match self.stack.pop().unwrap() {
Value::I32(c) => Value::from_bool(self.type_itest(c, op)),
Value::I64(c) => Value::from_bool(self.type_itest(c, op)),
_ => unreachable!(),
};
self.stack.push(v);
Ok(Continue)
}
fn type_itest<T>(&self, v: T, op: &ITestOp) -> bool
where
T: IntOp,
{
match *op {
ITestOp::Eqz => v.eqz(),
}
}
fn irel(&mut self, _t: types::Int, op: &IRelOp) -> IntResult {
let res = match self.pop2() {
(Value::I32(c1), Value::I32(c2)) => Value::from_bool(self.type_irel(c1, c2, op)),
(Value::I64(c1), Value::I64(c2)) => Value::from_bool(self.type_irel(c1, c2, op)),
_ => unreachable!(),
};
self.stack.push(res);
Ok(Continue)
}
fn type_irel<T>(&self, c1: T, c2: T, op: &IRelOp) -> bool
where
T: IntOp,
{
match *op {
IRelOp::Eq_ => c1.eq(c2),
IRelOp::Ne => c1.ne(c2),
IRelOp::LtS => c1.lts(c2),
IRelOp::LtU => c1.ltu(c2),
IRelOp::GtS => c1.gts(c2),
IRelOp::GtU => c1.gtu(c2),
IRelOp::LeS => c1.les(c2),
IRelOp::LeU => c1.leu(c2),
IRelOp::GeS => c1.ges(c2),
IRelOp::GeU => c1.geu(c2),
}
}
fn frel(&mut self, _t: types::Float, op: &FRelOp) -> IntResult {
let res = match self.pop2() {
(Value::F32(c1), Value::F32(c2)) => Value::from_bool(self.type_frel(c1, c2, op)),
(Value::F64(c1), Value::F64(c2)) => Value::from_bool(self.type_frel(c1, c2, op)),
_ => unreachable!(),
};
self.stack.push(res);
Ok(Continue)
}
fn type_frel<T>(&self, c1: T, c2: T, op: &FRelOp) -> bool
where
T: FloatOp,
{
match *op {
FRelOp::Eq_ => c1.eq(c2),
FRelOp::Ne => c1.ne(c2),
FRelOp::Lt => c1.lt(c2),
FRelOp::Gt => c1.gt(c2),
FRelOp::Le => c1.le(c2),
FRelOp::Ge => c1.ge(c2),
}
}
fn cvtop(&mut self, op: &ConvertOp) -> IntResult {
use super::types::Value as tv;
use super::types::{Float, Int};
let c = self.stack.pop().unwrap();
let cls = |&op, &c| {
Some(match (op, c) {
(&ConvertOp::I32WrapI64, Value::I64(c)) => Value::I32(c.to_u32()),
(&ConvertOp::I64ExtendUI32, Value::I32(c)) => Value::I64(c.to_u64()),
(&ConvertOp::I64ExtendSI32, Value::I32(c)) => Value::from_i64(c.to_i64()),
(
&ConvertOp::Trunc {
from: Float::F32,
to: Int::I32,
signed: false,
},
Value::F32(c),
) => Value::I32(c.to_u32()?),
(
&ConvertOp::Trunc {
from: Float::F32,
to: Int::I32,
signed: true,
},
Value::F32(c),
) => Value::from_i32(c.to_i32()?),
(
&ConvertOp::Trunc {
from: Float::F32,
to: Int::I64,
signed: false,
},
Value::F32(c),
) => Value::I64(c.to_u64()?),
(
&ConvertOp::Trunc {
from: Float::F32,
to: Int::I64,
signed: true,
},
Value::F32(c),
) => Value::from_i64(c.to_i64()?),
(
&ConvertOp::Trunc {
from: Float::F64,
to: Int::I32,
signed: false,
},
Value::F64(c),
) => Value::I32(c.to_u32()?),
(
&ConvertOp::Trunc {
from: Float::F64,
to: Int::I32,
signed: true,
},
Value::F64(c),
) => Value::from_i32(c.to_i32()?),
(
&ConvertOp::Trunc {
from: Float::F64,
to: Int::I64,
signed: false,
},
Value::F64(c),
) => Value::I64(c.to_u64()?),
(
&ConvertOp::Trunc {
from: Float::F64,
to: Int::I64,
signed: true,
},
Value::F64(c),
) => Value::from_i64(c.to_i64()?),
(
&ConvertOp::Convert {
from: Int::I32,
to: Float::F32,
signed: false,
},
Value::I32(c),
) => Value::F32(c.to_uf32()),
(
&ConvertOp::Convert {
from: Int::I32,
to: Float::F32,
signed: true,
},
Value::I32(c),
) => Value::F32(c.to_if32()),
(
&ConvertOp::Convert {
from: Int::I32,
to: Float::F64,
signed: false,
},
Value::I32(c),
) => Value::F64(c.to_uf64()),
(
&ConvertOp::Convert {
from: Int::I32,
to: Float::F64,
signed: true,
},
Value::I32(c),
) => Value::F64(c.to_if64()),
(
&ConvertOp::Convert {
from: Int::I64,
to: Float::F32,
signed: false,
},
Value::I64(c),
) => Value::F32(c.to_uf32()),
(
&ConvertOp::Convert {
from: Int::I64,
to: Float::F32,
signed: true,
},
Value::I64(c),
) => Value::F32(c.to_if32()),
(
&ConvertOp::Convert {
from: Int::I64,
to: Float::F64,
signed: false,
},
Value::I64(c),
) => Value::F64(c.to_uf64()),
(
&ConvertOp::Convert {
from: Int::I64,
to: Float::F64,
signed: true,
},
Value::I64(c),
) => Value::F64(c.to_if64()),
(
&ConvertOp::Reinterpret {
from: tv::Int(Int::I32),
to: tv::Float(Float::F32),
},
Value::I32(c),
) => Value::F32(c.reinterpret()),
(
&ConvertOp::Reinterpret {
from: tv::Int(Int::I64),
to: tv::Float(Float::F64),
},
Value::I64(c),
) => Value::F64(c.reinterpret()),
(
&ConvertOp::Reinterpret {
from: tv::Float(Float::F32),
to: tv::Int(Int::I32),
},
Value::F32(c),
) => Value::I32(c.reinterpret()),
(
&ConvertOp::Reinterpret {
from: tv::Float(Float::F64),
to: tv::Int(Int::I64),
},
Value::F64(c),
) => Value::I64(c.reinterpret()),
(&ConvertOp::F32DemoteF64, Value::F64(c)) => Value::F32(c.demote()),
(&ConvertOp::F64PromoteF32, Value::F32(c)) => Value::F64(c.promote()),
_ => unreachable!(),
})
};
if let Some(v) = cls(&op, &c) {
self.stack.push(v);
Ok(Continue)
} else {
Err(Trap {
origin: TrapOrigin::UndefinedResult,
})
}
}
fn get_global(&mut self, idx: Index) -> IntResult {
self.stack
.push(self.globals[self.frame.module().global_addrs[idx as usize]].value);
Ok(Continue)
}
fn set_global(&mut self, idx: Index) -> IntResult {
let val = self.stack.pop().unwrap();
self.globals[self.frame.module().global_addrs[idx as usize]].value = val;
Ok(Continue)
}
fn get_local(&mut self, idx: Index) -> IntResult {
let val = self.stack[self.frame.stack_idx + (idx as usize)];
self.stack.push(val);
Ok(Continue)
}
fn set_local(&mut self, idx: Index) -> IntResult {
self.stack[self.frame.stack_idx + (idx as usize)] = self.stack.pop().unwrap();
Ok(Continue)
}
fn tee_local(&mut self, idx: Index) -> IntResult {
self.stack[self.frame.stack_idx + (idx as usize)] = *self.stack.last().unwrap();
Ok(Continue)
}
fn call_module(&mut self, f_inst: &ModuleFuncInst) -> IntResult {
for l in &f_inst.code.locals {
match *l {
types::Value::Int(types::Int::I32) => self.stack.push(Value::I32(0)),
types::Value::Int(types::Int::I64) => self.stack.push(Value::I64(0)),
types::Value::Float(types::Float::F32) => self.stack.push(Value::F32(0.0)),
types::Value::Float(types::Float::F64) => self.stack.push(Value::F64(0.0)),
}
}
let frame_begin = self.stack.len() - f_inst.type_.args.len() - f_inst.code.locals.len();
let new_frame = self
.frame
.push(Some(f_inst.module.clone()), frame_begin)
.ok_or(Trap {
origin: TrapOrigin::StackOverflow,
})?;
let old_frame = mem::replace(&mut self.frame, new_frame);
self.block(&f_inst.type_.result, &f_inst.code.body)?;
self.frame = old_frame;
let drain_start = frame_begin;
let drain_end = self.stack.len() - f_inst.type_.result.len();
self.stack.drain(drain_start..drain_end);
Ok(Continue)
}
fn call_host(&mut self, f_inst: &HostFuncInst) -> IntResult {
if let Some(err) = (f_inst.hostcode)(self) {
return Err(Trap {
origin: TrapOrigin::HostFunction(err),
});
}
Ok(Continue)
}
pub fn call(&mut self, f_addr: FuncAddr) -> IntResult {
match self.funcs[f_addr] {
FuncInst::Module(ref f_inst) => self.call_module(f_inst)?,
FuncInst::Host(ref f_inst) => self.call_host(f_inst)?,
};
Ok(Continue)
}
fn call_indirect(&mut self, idx: Index) -> IntResult {
let tab = &self.tables[self.frame.module().table_addrs[0]];
let type_ = &self.frame.module().types[idx as usize];
let indirect_idx = match self.stack.pop().unwrap() {
Value::I32(c) => c as usize,
_ => unreachable!(),
};
if indirect_idx >= tab.elem.len() {
return Err(Trap {
origin: TrapOrigin::CallIndirectElemNotFound,
});
}
let func_addr = match tab.elem[indirect_idx] {
Some(c) => c,
None => {
return Err(Trap {
origin: TrapOrigin::CallIndirectElemUnitialized,
});
}
};
let f = &self.funcs[func_addr];
let f_type_ = match *f {
FuncInst::Module(ref f) => &f.type_,
FuncInst::Host(ref f) => &f.type_,
};
if f_type_ != type_ {
return Err(Trap {
origin: TrapOrigin::CallIndirectTypesDiffer,
});
}
self.call(func_addr)
}
fn return_(&self) -> IntResult {
Ok(Return)
}
fn current_memory(&mut self) -> IntResult {
self.stack.push(Value::I32(
self.mems.size(self.frame.module().mem_addrs[0]) as u32
));
Ok(Continue)
}
fn grow_memory(&mut self) -> IntResult {
let new_pages = match self.stack.pop().unwrap() {
Value::I32(c) => c as usize,
_ => unreachable!(),
};
if let Some(old_size) = self.mems.grow(self.frame.module().mem_addrs[0], new_pages) {
self.stack.push(Value::I32(old_size as u32));
} else {
self.stack.push(Value::from_i32(-1));
}
Ok(Continue)
}
fn load(&mut self, memop: &LoadOp) -> IntResult {
use super::types::Value as Tv;
use super::types::{Float, Int};
let mem = &self.mems[self.frame.module().mem_addrs[0]];
let offset = match self.stack.pop().unwrap() {
Value::I32(c) => c as usize + memop.offset as usize,
_ => unreachable!(),
};
let (size_in_bits, signed) = memop.opt.unwrap_or((memop.type_.bit_width(), false));
let size_in_bytes: usize = (size_in_bits as usize) / 8;
if offset + size_in_bytes > mem.data.len() {
return Err(Trap {
origin: TrapOrigin::LoadOutOfMemory,
});
}
let bits: &[u8] = &mem.data[offset..(offset + size_in_bytes)];
let res = match (size_in_bits, signed, memop.type_) {
(8, false, Tv::Int(Int::I32)) => Value::I32(bits[0] as u32),
(8, true, Tv::Int(Int::I32)) => Value::I32(bits[0] as i8 as u32),
(16, false, Tv::Int(Int::I32)) => {
Value::I32(u16::from_le_bytes([bits[0], bits[1]]) as u32)
}
(16, true, Tv::Int(Int::I32)) => {
Value::I32(i16::from_le_bytes([bits[0], bits[1]]) as u32)
}
(32, false, Tv::Int(Int::I32)) => {
Value::I32(u32::from_le_bytes([bits[0], bits[1], bits[2], bits[3]]) as u32)
}
(32, true, Tv::Int(Int::I32)) => {
Value::I32(i32::from_le_bytes([bits[0], bits[1], bits[2], bits[3]]) as u32)
}
(8, false, Tv::Int(Int::I64)) => Value::I64(bits[0] as u64),
(8, true, Tv::Int(Int::I64)) => Value::I64(bits[0] as i8 as u64),
(16, false, Tv::Int(Int::I64)) => {
Value::I64(u16::from_le_bytes([bits[0], bits[1]]) as u64)
}
(16, true, Tv::Int(Int::I64)) => {
Value::I64(i16::from_le_bytes([bits[0], bits[1]]) as u64)
}
(32, false, Tv::Int(Int::I64)) => {
Value::I64(u32::from_le_bytes([bits[0], bits[1], bits[2], bits[3]]) as u64)
}
(32, true, Tv::Int(Int::I64)) => {
Value::I64(i32::from_le_bytes([bits[0], bits[1], bits[2], bits[3]]) as u64)
}
(64, false, Tv::Int(Int::I64)) => Value::I64(u64::from_le_bytes([
bits[0], bits[1], bits[2], bits[3], bits[4], bits[5], bits[6], bits[7],
]) as u64),
(64, true, Tv::Int(Int::I64)) => Value::I64(i64::from_le_bytes([
bits[0], bits[1], bits[2], bits[3], bits[4], bits[5], bits[6], bits[7],
]) as u64),
(32, false, Tv::Float(Float::F32)) => Value::F32(f32::from_bits(u32::from_le_bytes([
bits[0], bits[1], bits[2], bits[3],
]))),
(64, false, Tv::Float(Float::F64)) => Value::F64(f64::from_bits(u64::from_le_bytes([
bits[0], bits[1], bits[2], bits[3], bits[4], bits[5], bits[6], bits[7],
]))),
_ => unreachable!(),
};
self.stack.push(res);
Ok(Continue)
}
fn store(&mut self, memop: &StoreOp) -> IntResult {
use super::types::Value as Tv;
use super::types::{Float, Int};
let mem = &mut self.mems[self.frame.module().mem_addrs[0]];
let c = self.stack.pop().unwrap();
let offset = match self.stack.pop().unwrap() {
Value::I32(c) => c as usize + memop.offset as usize,
_ => unreachable!(),
};
let size_in_bits = memop.opt.unwrap_or_else(|| memop.type_.bit_width());
let size_in_bytes: usize = (size_in_bits as usize) / 8;
if offset + size_in_bytes > mem.data.len() {
return Err(Trap {
origin: TrapOrigin::StoreOutOfMemory,
});
}
let bits = &mut mem.data[offset..(offset + size_in_bytes)];
match (size_in_bits, memop.type_, c) {
(8, Tv::Int(Int::I32), Value::I32(c)) => bits[0] = c as u8,
(16, Tv::Int(Int::I32), Value::I32(c)) => {
let b = (c as u16).to_le_bytes();
bits[0] = b[0];
bits[1] = b[1];
}
(32, Tv::Int(Int::I32), Value::I32(c)) => {
let b = (c as u32).to_le_bytes();
bits[0] = b[0];
bits[1] = b[1];
bits[2] = b[2];
bits[3] = b[3];
}
(8, Tv::Int(Int::I64), Value::I64(c)) => bits[0] = c as u8,
(16, Tv::Int(Int::I64), Value::I64(c)) => {
let b = (c as u16).to_le_bytes();
bits[0] = b[0];
bits[1] = b[1];
}
(32, Tv::Int(Int::I64), Value::I64(c)) => {
let b = (c as u32).to_le_bytes();
bits[0] = b[0];
bits[1] = b[1];
bits[2] = b[2];
bits[3] = b[3];
}
(64, Tv::Int(Int::I64), Value::I64(c)) => {
let b = (c as u64).to_le_bytes();
bits[0] = b[0];
bits[1] = b[1];
bits[2] = b[2];
bits[3] = b[3];
bits[4] = b[4];
bits[5] = b[5];
bits[6] = b[6];
bits[7] = b[7];
}
(32, Tv::Float(Float::F32), Value::F32(c)) => {
let b = (c as f32).to_bits().to_le_bytes();
bits[0] = b[0];
bits[1] = b[1];
bits[2] = b[2];
bits[3] = b[3];
}
(64, Tv::Float(Float::F64), Value::F64(c)) => {
let b = (c as f64).to_bits().to_le_bytes();
bits[0] = b[0];
bits[1] = b[1];
bits[2] = b[2];
bits[3] = b[3];
bits[4] = b[4];
bits[5] = b[5];
bits[6] = b[6];
bits[7] = b[7];
}
_ => unreachable!(),
};
Ok(Continue)
}
fn pop2(&mut self) -> (Value, Value) {
let b = self.stack.pop().unwrap();
let a = self.stack.pop().unwrap();
(a, b)
}
}
pub fn eval_const_expr(
globals: &GlobalInstStore,
mod_globals: &[GlobalAddr],
expr: &[Instr],
) -> Value {
if expr.len() != 1 {
panic!("contant expressions must have exactly only one instruction");
}
match expr[0] {
Instr::Const(c) => c,
Instr::GetGlobal(idx) => globals[mod_globals[idx as usize]].value,
_ => panic!("not a constant expression"),
}
}