1117 lines
42 KiB
Rust
1117 lines
42 KiB
Rust
//! A small WebAssembly interpreter for the wasm backend's tests: it decodes and runs the
|
|
//! subset the backend emits (MVP numerics, sign-extension, saturating truncation, bulk memory,
|
|
//! multi-value, exceptions with try_table, a SIMD128 subset), checks the section layout and
|
|
//! traps on what it does not know. Test code only: no third-party runtime.
|
|
|
|
#![allow(dead_code)]
|
|
|
|
use std::collections::HashMap;
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
pub enum Trap {
|
|
Trap(String),
|
|
Exception,
|
|
}
|
|
|
|
fn t<T>(m: &str) -> Result<T, Trap> {
|
|
Err(Trap::Trap(m.to_string()))
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct FType {
|
|
pub params: Vec<u8>,
|
|
pub results: Vec<u8>,
|
|
}
|
|
|
|
pub type HostFn = Box<dyn FnMut(&mut Vec<u8>, &[u64]) -> Result<Vec<u64>, Trap>>;
|
|
|
|
pub struct Func {
|
|
ty: u32,
|
|
locals: Vec<u8>,
|
|
code: std::rc::Rc<Vec<u8>>,
|
|
/// block/loop/if/try_table start (after the opcode) -> (else pc or 0, end pc)
|
|
ctl: std::rc::Rc<HashMap<usize, (usize, usize)>>,
|
|
}
|
|
|
|
pub struct Instance {
|
|
pub types: Vec<FType>,
|
|
pub imports: Vec<(String, String, u32)>,
|
|
pub funcs: Vec<Func>,
|
|
pub table: Vec<Option<u32>>,
|
|
pub mem: Vec<u8>,
|
|
/// memory64 (limits flag 0x04): addresses are i64
|
|
pub mem64: bool,
|
|
pub globals: Vec<u64>,
|
|
pub exports: HashMap<String, (u8, u32)>,
|
|
pub host: Vec<Option<HostFn>>,
|
|
pub steps: u64,
|
|
pub max_steps: u64,
|
|
depth: u32,
|
|
}
|
|
|
|
struct Rd<'a> {
|
|
b: &'a [u8],
|
|
p: usize,
|
|
}
|
|
|
|
impl<'a> Rd<'a> {
|
|
fn u8(&mut self) -> Result<u8, Trap> {
|
|
let v = *self.b.get(self.p).ok_or(Trap::Trap("eof".into()))?;
|
|
self.p += 1;
|
|
Ok(v)
|
|
}
|
|
fn u(&mut self) -> Result<u64, Trap> {
|
|
let mut r = 0u64;
|
|
let mut s = 0;
|
|
loop {
|
|
let b = self.u8()?;
|
|
r |= ((b & 0x7f) as u64) << s;
|
|
s += 7;
|
|
if b & 0x80 == 0 {
|
|
return Ok(r);
|
|
}
|
|
}
|
|
}
|
|
fn s(&mut self) -> Result<i64, Trap> {
|
|
let mut r = 0i64;
|
|
let mut s = 0;
|
|
loop {
|
|
let b = self.u8()?;
|
|
r |= ((b & 0x7f) as i64) << s;
|
|
s += 7;
|
|
if b & 0x80 == 0 {
|
|
if s < 64 && b & 0x40 != 0 {
|
|
r |= -1i64 << s;
|
|
}
|
|
return Ok(r);
|
|
}
|
|
}
|
|
}
|
|
fn bytes(&mut self, n: usize) -> Result<&'a [u8], Trap> {
|
|
let r = self.b.get(self.p..self.p + n).ok_or(Trap::Trap("eof".into()))?;
|
|
self.p += n;
|
|
Ok(r)
|
|
}
|
|
fn name(&mut self) -> Result<String, Trap> {
|
|
let n = self.u()? as usize;
|
|
Ok(String::from_utf8_lossy(self.bytes(n)?).to_string())
|
|
}
|
|
}
|
|
|
|
/// Skips one instruction's immediates (after its opcode `op`).
|
|
fn skip_imm(r: &mut Rd, op: u8) -> Result<(), Trap> {
|
|
match op {
|
|
0x02 | 0x03 | 0x04 | 0x06 => {
|
|
r.s()?;
|
|
}
|
|
0x1F => {
|
|
r.s()?;
|
|
let n = r.u()?;
|
|
for _ in 0..n {
|
|
let k = r.u8()?;
|
|
if k < 2 {
|
|
r.u()?;
|
|
}
|
|
r.u()?;
|
|
}
|
|
}
|
|
0x08 | 0x0C | 0x0D | 0x10 | 0x20 | 0x21 | 0x22 | 0x23 | 0x24 => {
|
|
r.u()?;
|
|
}
|
|
0x0E => {
|
|
let n = r.u()?;
|
|
for _ in 0..=n {
|
|
r.u()?;
|
|
}
|
|
}
|
|
0x11 => {
|
|
r.u()?;
|
|
r.u()?;
|
|
}
|
|
0x28..=0x3E => {
|
|
r.u()?;
|
|
r.u()?;
|
|
}
|
|
0x3F | 0x40 => {
|
|
r.u8()?;
|
|
}
|
|
0x41 | 0x42 => {
|
|
r.s()?;
|
|
}
|
|
0x43 => {
|
|
r.bytes(4)?;
|
|
}
|
|
0x44 => {
|
|
r.bytes(8)?;
|
|
}
|
|
0xFC => {
|
|
let k = r.u()?;
|
|
match k {
|
|
8 => {
|
|
r.u()?;
|
|
r.u8()?;
|
|
}
|
|
9 => {
|
|
r.u()?;
|
|
}
|
|
10 => {
|
|
r.u8()?;
|
|
r.u8()?;
|
|
}
|
|
11 => {
|
|
r.u8()?;
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
0xFD => {
|
|
let k = r.u()?;
|
|
match k {
|
|
0 | 11 => {
|
|
r.u()?;
|
|
r.u()?;
|
|
}
|
|
12 | 13 => {
|
|
r.bytes(16)?;
|
|
}
|
|
21..=34 => {
|
|
r.u8()?;
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn ctl_map(code: &[u8], start: usize) -> Result<HashMap<usize, (usize, usize)>, Trap> {
|
|
let mut m = HashMap::new();
|
|
let mut st: Vec<(usize, usize)> = Vec::new();
|
|
let mut r = Rd { b: code, p: start };
|
|
while r.p < code.len() {
|
|
let op = r.u8()?;
|
|
let here = r.p;
|
|
match op {
|
|
0x02 | 0x03 | 0x04 | 0x1F | 0x06 => {
|
|
st.push((here, 0));
|
|
}
|
|
0x05 | 0x19 => {
|
|
st.last_mut().ok_or(Trap::Trap("else".into()))?.1 = here;
|
|
}
|
|
0x0B => {
|
|
if let Some((s, e)) = st.pop() {
|
|
m.insert(s, (e, here));
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
skip_imm(&mut r, op)?;
|
|
}
|
|
Ok(m)
|
|
}
|
|
|
|
impl Instance {
|
|
pub fn new(bytes: &[u8]) -> Result<Instance, Trap> {
|
|
let mut r = Rd { b: bytes, p: 0 };
|
|
if r.bytes(8)? != [0, 0x61, 0x73, 0x6D, 1, 0, 0, 0] {
|
|
return t("magic");
|
|
}
|
|
let mut inst = Instance { types: vec![], imports: vec![], funcs: vec![], table: vec![], mem: vec![], mem64: false, globals: vec![], exports: HashMap::new(), host: vec![], steps: 0, max_steps: 50_000_000, depth: 0 };
|
|
let mut fty: Vec<u32> = vec![];
|
|
let mut last_id = 0u8;
|
|
let mut datas: Vec<(Option<u64>, Vec<u8>)> = vec![];
|
|
let order = |id: u8| -> u8 {
|
|
match id {
|
|
1 => 1,
|
|
2 => 2,
|
|
3 => 3,
|
|
4 => 4,
|
|
5 => 5,
|
|
13 => 6,
|
|
6 => 7,
|
|
7 => 8,
|
|
8 => 9,
|
|
9 => 10,
|
|
12 => 11,
|
|
10 => 12,
|
|
11 => 13,
|
|
_ => 0,
|
|
}
|
|
};
|
|
while r.p < bytes.len() {
|
|
let id = r.u8()?;
|
|
let n = r.u()? as usize;
|
|
let body = r.bytes(n)?;
|
|
if id != 0 {
|
|
if order(id) <= last_id {
|
|
return t(&format!("section {} out of order", id));
|
|
}
|
|
last_id = order(id);
|
|
}
|
|
let mut s = Rd { b: body, p: 0 };
|
|
match id {
|
|
0 => {}
|
|
1 => {
|
|
for _ in 0..s.u()? {
|
|
if s.u8()? != 0x60 {
|
|
return t("functype");
|
|
}
|
|
let np = s.u()? as usize;
|
|
let params = s.bytes(np)?.to_vec();
|
|
let nr = s.u()? as usize;
|
|
let results = s.bytes(nr)?.to_vec();
|
|
inst.types.push(FType { params, results });
|
|
}
|
|
}
|
|
2 => {
|
|
for _ in 0..s.u()? {
|
|
let m = s.name()?;
|
|
let f = s.name()?;
|
|
let k = s.u8()?;
|
|
let ti = s.u()? as u32;
|
|
if k != 0 {
|
|
return t("import kind");
|
|
}
|
|
inst.imports.push((m, f, ti));
|
|
inst.host.push(None);
|
|
}
|
|
}
|
|
3 => {
|
|
for _ in 0..s.u()? {
|
|
fty.push(s.u()? as u32);
|
|
}
|
|
}
|
|
4 => {
|
|
s.u()?;
|
|
s.u8()?;
|
|
s.u8()?;
|
|
let n = s.u()? as usize;
|
|
inst.table = vec![None; n];
|
|
}
|
|
5 => {
|
|
s.u()?;
|
|
let fl = s.u8()?;
|
|
if fl & !0x04 != 0 {
|
|
return t("memory limits flags");
|
|
}
|
|
inst.mem64 = fl & 0x04 != 0;
|
|
let pages = s.u()? as usize;
|
|
inst.mem = vec![0; pages * 65536];
|
|
}
|
|
13 => {}
|
|
6 => {
|
|
for _ in 0..s.u()? {
|
|
s.u8()?;
|
|
s.u8()?;
|
|
let op = s.u8()?;
|
|
let v = s.s()?;
|
|
s.u8()?;
|
|
inst.globals.push(if op == 0x41 { v as i32 as u32 as u64 } else { v as u64 });
|
|
}
|
|
}
|
|
7 => {
|
|
for _ in 0..s.u()? {
|
|
let n = s.name()?;
|
|
let k = s.u8()?;
|
|
let i = s.u()? as u32;
|
|
inst.exports.insert(n, (k, i));
|
|
}
|
|
}
|
|
9 => {
|
|
for _ in 0..s.u()? {
|
|
if s.u()? != 0 {
|
|
return t("elem flags");
|
|
}
|
|
s.u8()?;
|
|
let off = s.s()? as usize;
|
|
s.u8()?;
|
|
for k in 0..s.u()? as usize {
|
|
let f = s.u()? as u32;
|
|
inst.table[off + k] = Some(f);
|
|
}
|
|
}
|
|
}
|
|
12 => {}
|
|
10 => {
|
|
let n = s.u()? as usize;
|
|
if n != fty.len() {
|
|
return t("code count");
|
|
}
|
|
for k in 0..n {
|
|
let sz = s.u()? as usize;
|
|
let fb = s.bytes(sz)?;
|
|
let mut q = Rd { b: fb, p: 0 };
|
|
let mut locals = vec![];
|
|
for _ in 0..q.u()? {
|
|
let c = q.u()?;
|
|
let ty = q.u8()?;
|
|
for _ in 0..c {
|
|
locals.push(ty);
|
|
}
|
|
}
|
|
let code = fb.to_vec();
|
|
let ctl = ctl_map(&code, q.p)?;
|
|
let start = q.p;
|
|
let mut ctl = ctl;
|
|
ctl.insert(usize::MAX, (start, 0));
|
|
inst.funcs.push(Func { ty: fty[k], locals, code: std::rc::Rc::new(code), ctl: std::rc::Rc::new(ctl) });
|
|
}
|
|
}
|
|
11 => {
|
|
for _ in 0..s.u()? {
|
|
let fl = s.u()?;
|
|
let addr = if fl == 0 {
|
|
let op = s.u8()?;
|
|
let v = s.s()?;
|
|
s.u8()?;
|
|
Some(if op == 0x41 { v as i32 as u32 as u64 } else { v as u64 })
|
|
} else {
|
|
None
|
|
};
|
|
let n = s.u()? as usize;
|
|
datas.push((addr, s.bytes(n)?.to_vec()));
|
|
}
|
|
}
|
|
_ => return t(&format!("section {}", id)),
|
|
}
|
|
}
|
|
for (a, d) in datas {
|
|
if let Some(a) = a {
|
|
let a = a as usize;
|
|
if a + d.len() > inst.mem.len() {
|
|
return t("data out of memory");
|
|
}
|
|
inst.mem[a..a + d.len()].copy_from_slice(&d);
|
|
}
|
|
}
|
|
Ok(inst)
|
|
}
|
|
|
|
pub fn bind(&mut self, m: &str, f: &str, h: HostFn) {
|
|
for (k, (mm, ff, _)) in self.imports.iter().enumerate() {
|
|
if mm == m && ff == f {
|
|
self.host[k] = Some(h);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn call_export(&mut self, name: &str, args: &[u64]) -> Result<Vec<u64>, Trap> {
|
|
let (k, i) = *self.exports.get(name).ok_or(Trap::Trap(format!("no export {}", name)))?;
|
|
if k != 0 {
|
|
return t("not a function");
|
|
}
|
|
let mut st: Vec<u128> = args.iter().map(|a| *a as u128).collect();
|
|
self.invoke(i, &mut st)?;
|
|
Ok(st.into_iter().map(|v| v as u64).collect())
|
|
}
|
|
|
|
fn ftype(&self, f: u32) -> FType {
|
|
let ni = self.imports.len() as u32;
|
|
if f < ni {
|
|
self.types[self.imports[f as usize].2 as usize].clone()
|
|
} else {
|
|
self.types[self.funcs[(f - ni) as usize].ty as usize].clone()
|
|
}
|
|
}
|
|
|
|
fn invoke(&mut self, f: u32, st: &mut Vec<u128>) -> Result<(), Trap> {
|
|
let ty = self.ftype(f);
|
|
let np = ty.params.len();
|
|
if st.len() < np {
|
|
return t("stack underflow at call");
|
|
}
|
|
let args: Vec<u128> = st.split_off(st.len() - np);
|
|
let ni = self.imports.len() as u32;
|
|
if f < ni {
|
|
let mut h = self.host[f as usize].take().ok_or(Trap::Trap(format!("unbound import {}.{}", self.imports[f as usize].0, self.imports[f as usize].1)))?;
|
|
let a64: Vec<u64> = args.iter().map(|a| *a as u64).collect();
|
|
let r = h(&mut self.mem, &a64);
|
|
self.host[f as usize] = Some(h);
|
|
let r = r?;
|
|
if r.len() != ty.results.len() {
|
|
return t("host result count");
|
|
}
|
|
st.extend(r.into_iter().map(|v| v as u128));
|
|
return Ok(());
|
|
}
|
|
self.depth += 1;
|
|
if self.depth > 3000 {
|
|
self.depth -= 1;
|
|
return t("call stack exhausted");
|
|
}
|
|
let r = self.run(f - ni, args);
|
|
self.depth -= 1;
|
|
let res = r?;
|
|
st.extend(res);
|
|
Ok(())
|
|
}
|
|
|
|
/// An address operand (i32, or i64 under memory64) as an index.
|
|
fn addr(&self, v: u128) -> u64 {
|
|
if self.mem64 { v as u64 } else { v as u64 as u32 as u64 }
|
|
}
|
|
|
|
fn ea(&self, base: u128, off: u64, n: usize) -> Result<usize, Trap> {
|
|
let a = self.addr(base).checked_add(off).ok_or(Trap::Trap("address overflow".into()))? as usize;
|
|
if a.checked_add(n).map_or(true, |e| e > self.mem.len()) {
|
|
return t("out of bounds memory access");
|
|
}
|
|
Ok(a)
|
|
}
|
|
|
|
fn run(&mut self, fi: u32, args: Vec<u128>) -> Result<Vec<u128>, Trap> {
|
|
let ty = self.types[self.funcs[fi as usize].ty as usize].clone();
|
|
let mut locals: Vec<u128> = args;
|
|
locals.extend(std::iter::repeat(0).take(self.funcs[fi as usize].locals.len()));
|
|
let code = self.funcs[fi as usize].code.clone();
|
|
let ctl = self.funcs[fi as usize].ctl.clone();
|
|
self.exec(&code, &ctl, &mut locals, ty.results.len())
|
|
}
|
|
|
|
fn exec(&mut self, code: &[u8], ctl: &HashMap<usize, (usize, usize)>, l: &mut Vec<u128>, nres: usize) -> Result<Vec<u128>, Trap> {
|
|
// label: (kind 0 block/if/try 1 loop, branch target pc, stack height, try catch label or MAX)
|
|
struct Lab {
|
|
lp: bool,
|
|
target: usize,
|
|
h: usize,
|
|
catch: Option<u32>,
|
|
/// legacy try: the catch_all body's pc
|
|
legacy: usize,
|
|
}
|
|
let mut labs: Vec<Lab> = Vec::new();
|
|
let mut st: Vec<u128> = Vec::new();
|
|
let mut r = Rd { b: code, p: ctl[&usize::MAX].0 };
|
|
macro_rules! pop {
|
|
() => {
|
|
st.pop().ok_or(Trap::Trap("stack underflow".into()))?
|
|
};
|
|
}
|
|
macro_rules! bin64 {
|
|
($f:expr) => {{
|
|
let b = pop!() as u64;
|
|
let a = pop!() as u64;
|
|
let f: fn(u64, u64) -> Result<u64, Trap> = $f;
|
|
st.push(f(a, b)? as u128);
|
|
}};
|
|
}
|
|
macro_rules! bin32 {
|
|
($f:expr) => {{
|
|
let b = pop!() as u32;
|
|
let a = pop!() as u32;
|
|
let f: fn(u32, u32) -> Result<u32, Trap> = $f;
|
|
st.push(f(a, b)? as u128);
|
|
}};
|
|
}
|
|
macro_rules! f64b {
|
|
($f:expr) => {{
|
|
let b = f64::from_bits(pop!() as u64);
|
|
let a = f64::from_bits(pop!() as u64);
|
|
let f: fn(f64, f64) -> f64 = $f;
|
|
st.push(f(a, b).to_bits() as u128);
|
|
}};
|
|
}
|
|
macro_rules! f32b {
|
|
($f:expr) => {{
|
|
let b = f32::from_bits(pop!() as u32);
|
|
let a = f32::from_bits(pop!() as u32);
|
|
let f: fn(f32, f32) -> f32 = $f;
|
|
st.push(f(a, b).to_bits() as u128);
|
|
}};
|
|
}
|
|
macro_rules! f64c {
|
|
($f:expr) => {{
|
|
let b = f64::from_bits(pop!() as u64);
|
|
let a = f64::from_bits(pop!() as u64);
|
|
let f: fn(f64, f64) -> bool = $f;
|
|
st.push(f(a, b) as u128);
|
|
}};
|
|
}
|
|
macro_rules! f32c {
|
|
($f:expr) => {{
|
|
let b = f32::from_bits(pop!() as u32);
|
|
let a = f32::from_bits(pop!() as u32);
|
|
let f: fn(f32, f32) -> bool = $f;
|
|
st.push(f(a, b) as u128);
|
|
}};
|
|
}
|
|
// branch to label depth d
|
|
macro_rules! branch {
|
|
($d:expr) => {{
|
|
let d = $d as usize;
|
|
if d >= labs.len() {
|
|
// the function body's label: return
|
|
let res = st.split_off(st.len() - nres);
|
|
return Ok(res);
|
|
}
|
|
let k = labs.len() - 1 - d;
|
|
let lab = &labs[k];
|
|
st.truncate(lab.h);
|
|
r.p = lab.target;
|
|
if lab.lp {
|
|
labs.truncate(k + 1);
|
|
} else {
|
|
labs.truncate(k);
|
|
}
|
|
}};
|
|
}
|
|
loop {
|
|
self.steps += 1;
|
|
if self.steps > self.max_steps {
|
|
return t("step limit");
|
|
}
|
|
if r.p >= code.len() {
|
|
return t("ran off the code");
|
|
}
|
|
let op = r.u8()?;
|
|
let here = r.p;
|
|
match op {
|
|
0x00 => return t("unreachable"),
|
|
0x01 => {}
|
|
0x02 | 0x03 | 0x04 => {
|
|
let bt = r.s()?;
|
|
if bt != -64 {
|
|
return t("block type");
|
|
}
|
|
let (els, end) = ctl[&here];
|
|
if op == 0x04 {
|
|
let c = pop!() as u32;
|
|
labs.push(Lab { lp: false, target: end, h: st.len(), catch: None, legacy: 0 });
|
|
if c == 0 {
|
|
if els != 0 {
|
|
r.p = els;
|
|
} else {
|
|
r.p = end;
|
|
labs.pop();
|
|
}
|
|
}
|
|
} else if op == 0x03 {
|
|
labs.push(Lab { lp: true, target: r.p, h: st.len(), catch: None, legacy: 0 });
|
|
} else {
|
|
labs.push(Lab { lp: false, target: end, h: st.len(), catch: None, legacy: 0 });
|
|
}
|
|
}
|
|
0x1F => {
|
|
let bt = r.s()?;
|
|
if bt != -64 {
|
|
return t("try block type");
|
|
}
|
|
let n = r.u()?;
|
|
let mut catch = None;
|
|
for _ in 0..n {
|
|
let k = r.u8()?;
|
|
if k != 2 {
|
|
return t("only catch_all");
|
|
}
|
|
catch = Some(r.u()? as u32);
|
|
}
|
|
let (_, end) = ctl[&here];
|
|
labs.push(Lab { lp: false, target: end, h: st.len(), catch, legacy: 0 });
|
|
}
|
|
0x06 => {
|
|
if r.s()? != -64 {
|
|
return t("try block type");
|
|
}
|
|
let (cat, end) = ctl[&here];
|
|
if cat == 0 {
|
|
return t("try without catch_all");
|
|
}
|
|
labs.push(Lab { lp: false, target: end, h: st.len(), catch: None, legacy: cat });
|
|
}
|
|
0x05 | 0x19 => {
|
|
// end of the then arm / try body: skip to after end
|
|
let lab = labs.pop().ok_or(Trap::Trap("else".into()))?;
|
|
r.p = lab.target;
|
|
}
|
|
0x0B => {
|
|
if labs.pop().is_none() {
|
|
let res = st.split_off(st.len().checked_sub(nres).ok_or(Trap::Trap("results".into()))?);
|
|
return Ok(res);
|
|
}
|
|
}
|
|
0x08 => {
|
|
r.u()?;
|
|
// find the innermost try in this frame
|
|
let e = Trap::Exception;
|
|
match labs.iter().rposition(|x| x.catch.is_some() || x.legacy != 0) {
|
|
Some(k) if labs[k].legacy != 0 => {
|
|
let h = labs[k].h;
|
|
let cat = labs[k].legacy;
|
|
labs.truncate(k + 1);
|
|
labs[k].legacy = 0;
|
|
st.truncate(h);
|
|
r.p = cat;
|
|
}
|
|
Some(k) => {
|
|
let c = labs[k].catch.unwrap();
|
|
let h = labs[k].h;
|
|
labs.truncate(k);
|
|
st.truncate(h);
|
|
branch!(c);
|
|
}
|
|
None => return Err(e),
|
|
}
|
|
}
|
|
0x0C => {
|
|
let d = r.u()?;
|
|
branch!(d);
|
|
}
|
|
0x0D => {
|
|
let d = r.u()?;
|
|
if pop!() as u32 != 0 {
|
|
branch!(d);
|
|
}
|
|
}
|
|
0x0E => {
|
|
let n = r.u()? as usize;
|
|
let mut v = Vec::with_capacity(n + 1);
|
|
for _ in 0..=n {
|
|
v.push(r.u()?);
|
|
}
|
|
let i = pop!() as u32 as usize;
|
|
let d = if i < n { v[i] } else { v[n] };
|
|
branch!(d);
|
|
}
|
|
0x0F => {
|
|
let res = st.split_off(st.len() - nres);
|
|
return Ok(res);
|
|
}
|
|
0x10 | 0x11 => {
|
|
let callee = if op == 0x10 {
|
|
r.u()? as u32
|
|
} else {
|
|
let ti = r.u()? as u32;
|
|
r.u()?;
|
|
let i = pop!() as u32 as usize;
|
|
let f = *self.table.get(i).ok_or(Trap::Trap("table index".into()))?;
|
|
let f = f.ok_or(Trap::Trap("null table entry".into()))?;
|
|
if self.ftype(f) != self.types[ti as usize] {
|
|
return t("indirect call signature mismatch");
|
|
}
|
|
f
|
|
};
|
|
match self.invoke(callee, &mut st) {
|
|
Ok(()) => {}
|
|
Err(Trap::Exception) => match labs.iter().rposition(|x| x.catch.is_some() || x.legacy != 0) {
|
|
Some(k) if labs[k].legacy != 0 => {
|
|
let h = labs[k].h;
|
|
let cat = labs[k].legacy;
|
|
labs.truncate(k + 1);
|
|
labs[k].legacy = 0;
|
|
st.truncate(h);
|
|
r.p = cat;
|
|
}
|
|
Some(k) => {
|
|
let c = labs[k].catch.unwrap();
|
|
let h = labs[k].h;
|
|
labs.truncate(k);
|
|
st.truncate(h);
|
|
branch!(c);
|
|
}
|
|
None => return Err(Trap::Exception),
|
|
},
|
|
Err(e) => return Err(e),
|
|
}
|
|
}
|
|
0x1A => {
|
|
pop!();
|
|
}
|
|
0x1B => {
|
|
let c = pop!() as u32;
|
|
let b = pop!();
|
|
let a = pop!();
|
|
st.push(if c != 0 { a } else { b });
|
|
}
|
|
0x20 => {
|
|
let i = r.u()? as usize;
|
|
st.push(l[i]);
|
|
}
|
|
0x21 => {
|
|
let i = r.u()? as usize;
|
|
l[i] = pop!();
|
|
}
|
|
0x22 => {
|
|
let i = r.u()? as usize;
|
|
l[i] = *st.last().ok_or(Trap::Trap("tee".into()))?;
|
|
}
|
|
0x23 => {
|
|
let i = r.u()? as usize;
|
|
st.push(self.globals[i] as u128);
|
|
}
|
|
0x24 => {
|
|
let i = r.u()? as usize;
|
|
self.globals[i] = pop!() as u64;
|
|
}
|
|
0x28..=0x35 => {
|
|
let _al = r.u()?;
|
|
let off = r.u()?;
|
|
let base = pop!();
|
|
let n = match op {
|
|
0x28 | 0x2A | 0x34 | 0x35 => 4,
|
|
0x29 | 0x2B => 8,
|
|
0x2C | 0x2D | 0x30 | 0x31 => 1,
|
|
_ => 2,
|
|
};
|
|
let a = self.ea(base, off, n)?;
|
|
let mut w = [0u8; 8];
|
|
w[..n].copy_from_slice(&self.mem[a..a + n]);
|
|
let u = u64::from_le_bytes(w);
|
|
let v: u64 = match op {
|
|
0x2C => u as u8 as i8 as i32 as u32 as u64,
|
|
0x2E => u as u16 as i16 as i32 as u32 as u64,
|
|
0x30 => u as u8 as i8 as i64 as u64,
|
|
0x32 => u as u16 as i16 as i64 as u64,
|
|
0x34 => u as u32 as i32 as i64 as u64,
|
|
_ => u,
|
|
};
|
|
st.push(v as u128);
|
|
}
|
|
0x36..=0x3E => {
|
|
let _al = r.u()?;
|
|
let off = r.u()?;
|
|
let v = pop!() as u64;
|
|
let base = pop!();
|
|
let n = match op {
|
|
0x36 | 0x38 | 0x3E => 4,
|
|
0x37 | 0x39 => 8,
|
|
0x3A | 0x3C => 1,
|
|
_ => 2,
|
|
};
|
|
let a = self.ea(base, off, n)?;
|
|
self.mem[a..a + n].copy_from_slice(&v.to_le_bytes()[..n]);
|
|
}
|
|
0x41 => st.push(r.s()? as i32 as u32 as u128),
|
|
0x42 => st.push(r.s()? as u64 as u128),
|
|
0x43 => {
|
|
let b = r.bytes(4)?;
|
|
st.push(u32::from_le_bytes([b[0], b[1], b[2], b[3]]) as u128);
|
|
}
|
|
0x44 => {
|
|
let b = r.bytes(8)?;
|
|
let mut w = [0u8; 8];
|
|
w.copy_from_slice(b);
|
|
st.push(u64::from_le_bytes(w) as u128);
|
|
}
|
|
0x45 => {
|
|
let a = pop!() as u32;
|
|
st.push((a == 0) as u128);
|
|
}
|
|
0x46..=0x4F => bin32!(match op {
|
|
0x46 => |a, b| Ok((a == b) as u32),
|
|
0x47 => |a, b| Ok((a != b) as u32),
|
|
0x48 => |a, b| Ok(((a as i32) < (b as i32)) as u32),
|
|
0x49 => |a, b| Ok((a < b) as u32),
|
|
0x4A => |a, b| Ok((a as i32 > b as i32) as u32),
|
|
0x4B => |a, b| Ok((a > b) as u32),
|
|
0x4C => |a, b| Ok((a as i32 <= b as i32) as u32),
|
|
0x4D => |a, b| Ok((a <= b) as u32),
|
|
0x4E => |a, b| Ok((a as i32 >= b as i32) as u32),
|
|
_ => |a, b| Ok((a >= b) as u32),
|
|
}),
|
|
0x50 => {
|
|
let a = pop!() as u64;
|
|
st.push((a == 0) as u128);
|
|
}
|
|
0x51..=0x5A => bin64!(match op {
|
|
0x51 => |a, b| Ok((a == b) as u64),
|
|
0x52 => |a, b| Ok((a != b) as u64),
|
|
0x53 => |a, b| Ok(((a as i64) < (b as i64)) as u64),
|
|
0x54 => |a, b| Ok((a < b) as u64),
|
|
0x55 => |a, b| Ok((a as i64 > b as i64) as u64),
|
|
0x56 => |a, b| Ok((a > b) as u64),
|
|
0x57 => |a, b| Ok((a as i64 <= b as i64) as u64),
|
|
0x58 => |a, b| Ok((a <= b) as u64),
|
|
0x59 => |a, b| Ok((a as i64 >= b as i64) as u64),
|
|
_ => |a, b| Ok((a >= b) as u64),
|
|
}),
|
|
0x5B..=0x60 => f32c!(match op {
|
|
0x5B => |a, b| a == b,
|
|
0x5C => |a, b| a != b,
|
|
0x5D => |a, b| a < b,
|
|
0x5E => |a, b| a > b,
|
|
0x5F => |a, b| a <= b,
|
|
_ => |a, b| a >= b,
|
|
}),
|
|
0x61..=0x66 => f64c!(match op {
|
|
0x61 => |a, b| a == b,
|
|
0x62 => |a, b| a != b,
|
|
0x63 => |a, b| a < b,
|
|
0x64 => |a, b| a > b,
|
|
0x65 => |a, b| a <= b,
|
|
_ => |a, b| a >= b,
|
|
}),
|
|
0x67..=0x69 => {
|
|
let a = pop!() as u32;
|
|
st.push(match op {
|
|
0x67 => a.leading_zeros(),
|
|
0x68 => a.trailing_zeros(),
|
|
_ => a.count_ones(),
|
|
} as u128);
|
|
}
|
|
0x6A..=0x78 => bin32!(match op {
|
|
0x6A => |a, b| Ok(a.wrapping_add(b)),
|
|
0x6B => |a, b| Ok(a.wrapping_sub(b)),
|
|
0x6C => |a, b| Ok(a.wrapping_mul(b)),
|
|
0x6D => |a, b| if b == 0 || (a == 0x8000_0000 && b == u32::MAX) { t("int div") } else { Ok((a as i32 / b as i32) as u32) },
|
|
0x6E => |a, b| if b == 0 { t("int div") } else { Ok(a / b) },
|
|
0x6F => |a, b| if b == 0 { t("int div") } else { Ok((a as i32).wrapping_rem(b as i32) as u32) },
|
|
0x70 => |a, b| if b == 0 { t("int div") } else { Ok(a % b) },
|
|
0x71 => |a, b| Ok(a & b),
|
|
0x72 => |a, b| Ok(a | b),
|
|
0x73 => |a, b| Ok(a ^ b),
|
|
0x74 => |a, b| Ok(a << (b & 31)),
|
|
0x75 => |a, b| Ok(((a as i32) >> (b & 31)) as u32),
|
|
0x76 => |a, b| Ok(a >> (b & 31)),
|
|
0x77 => |a, b| Ok(a.rotate_left(b & 31)),
|
|
_ => |a, b| Ok(a.rotate_right(b & 31)),
|
|
}),
|
|
0x79..=0x7B => {
|
|
let a = pop!() as u64;
|
|
st.push(match op {
|
|
0x79 => a.leading_zeros(),
|
|
0x7A => a.trailing_zeros(),
|
|
_ => a.count_ones(),
|
|
} as u128);
|
|
}
|
|
0x7C..=0x8A => bin64!(match op {
|
|
0x7C => |a, b| Ok(a.wrapping_add(b)),
|
|
0x7D => |a, b| Ok(a.wrapping_sub(b)),
|
|
0x7E => |a, b| Ok(a.wrapping_mul(b)),
|
|
0x7F => |a, b| if b == 0 || (a == 1 << 63 && b == u64::MAX) { t("int div") } else { Ok((a as i64 / b as i64) as u64) },
|
|
0x80 => |a, b| if b == 0 { t("int div") } else { Ok(a / b) },
|
|
0x81 => |a, b| if b == 0 { t("int div") } else { Ok((a as i64).wrapping_rem(b as i64) as u64) },
|
|
0x82 => |a, b| if b == 0 { t("int div") } else { Ok(a % b) },
|
|
0x83 => |a, b| Ok(a & b),
|
|
0x84 => |a, b| Ok(a | b),
|
|
0x85 => |a, b| Ok(a ^ b),
|
|
0x86 => |a, b| Ok(a << (b & 63)),
|
|
0x87 => |a, b| Ok(((a as i64) >> (b & 63)) as u64),
|
|
0x88 => |a, b| Ok(a >> (b & 63)),
|
|
0x89 => |a, b| Ok(a.rotate_left((b & 63) as u32)),
|
|
_ => |a, b| Ok(a.rotate_right((b & 63) as u32)),
|
|
}),
|
|
0x8B..=0x91 => {
|
|
let a = f32::from_bits(pop!() as u32);
|
|
let v = match op {
|
|
0x8B => a.abs(),
|
|
0x8C => -a,
|
|
0x8D => a.ceil(),
|
|
0x8E => a.floor(),
|
|
0x8F => a.trunc(),
|
|
0x90 => a.round_ties_even(),
|
|
_ => a.sqrt(),
|
|
};
|
|
st.push(v.to_bits() as u128);
|
|
}
|
|
0x92..=0x98 => f32b!(match op {
|
|
0x92 => |a, b| a + b,
|
|
0x93 => |a, b| a - b,
|
|
0x94 => |a, b| a * b,
|
|
0x95 => |a, b| a / b,
|
|
0x96 => |a, b| if a.is_nan() || b.is_nan() { f32::NAN } else if a == b { if a.is_sign_negative() { a } else { b } } else { a.min(b) },
|
|
0x97 => |a, b| if a.is_nan() || b.is_nan() { f32::NAN } else if a == b { if a.is_sign_positive() { a } else { b } } else { a.max(b) },
|
|
_ => |a, b| a.copysign(b),
|
|
}),
|
|
0x99..=0x9F => {
|
|
let a = f64::from_bits(pop!() as u64);
|
|
let v = match op {
|
|
0x99 => a.abs(),
|
|
0x9A => -a,
|
|
0x9B => a.ceil(),
|
|
0x9C => a.floor(),
|
|
0x9D => a.trunc(),
|
|
0x9E => a.round_ties_even(),
|
|
_ => a.sqrt(),
|
|
};
|
|
st.push(v.to_bits() as u128);
|
|
}
|
|
0xA0..=0xA6 => f64b!(match op {
|
|
0xA0 => |a, b| a + b,
|
|
0xA1 => |a, b| a - b,
|
|
0xA2 => |a, b| a * b,
|
|
0xA3 => |a, b| a / b,
|
|
0xA4 => |a, b| if a.is_nan() || b.is_nan() { f64::NAN } else if a == b { if a.is_sign_negative() { a } else { b } } else { a.min(b) },
|
|
0xA5 => |a, b| if a.is_nan() || b.is_nan() { f64::NAN } else if a == b { if a.is_sign_positive() { a } else { b } } else { a.max(b) },
|
|
_ => |a, b| a.copysign(b),
|
|
}),
|
|
0xA7 => {
|
|
let a = pop!() as u64;
|
|
st.push(a as u32 as u128);
|
|
}
|
|
0xAC => {
|
|
let a = pop!() as u32;
|
|
st.push(a as i32 as i64 as u64 as u128);
|
|
}
|
|
0xAD => {
|
|
let a = pop!() as u32;
|
|
st.push(a as u128);
|
|
}
|
|
0xB2..=0xBA => {
|
|
let a = pop!();
|
|
let v: u64 = match op {
|
|
0xB2 => (a as u32 as i32 as f32).to_bits() as u64,
|
|
0xB3 => (a as u32 as f32).to_bits() as u64,
|
|
0xB4 => (a as u64 as i64 as f32).to_bits() as u64,
|
|
0xB5 => (a as u64 as f32).to_bits() as u64,
|
|
0xB6 => (f64::from_bits(a as u64) as f32).to_bits() as u64,
|
|
0xB7 => (a as u32 as i32 as f64).to_bits(),
|
|
0xB8 => (a as u32 as f64).to_bits(),
|
|
0xB9 => (a as u64 as i64 as f64).to_bits(),
|
|
_ => (a as u64 as f64).to_bits(),
|
|
};
|
|
st.push(v as u128);
|
|
}
|
|
0xBB => {
|
|
let a = f32::from_bits(pop!() as u32);
|
|
st.push((a as f64).to_bits() as u128);
|
|
}
|
|
0xBC | 0xBE => {
|
|
let a = pop!() as u32;
|
|
st.push(a as u128);
|
|
}
|
|
0xBD | 0xBF => {
|
|
let a = pop!() as u64;
|
|
st.push(a as u128);
|
|
}
|
|
0xC0..=0xC4 => {
|
|
let a = pop!();
|
|
let v: u64 = match op {
|
|
0xC0 => a as u8 as i8 as i32 as u32 as u64,
|
|
0xC1 => a as u16 as i16 as i32 as u32 as u64,
|
|
0xC2 => a as u8 as i8 as i64 as u64,
|
|
0xC3 => a as u16 as i16 as i64 as u64,
|
|
_ => a as u32 as i32 as i64 as u64,
|
|
};
|
|
st.push(v as u128);
|
|
}
|
|
0xFC => {
|
|
let k = r.u()?;
|
|
match k {
|
|
0..=7 => {
|
|
let a = pop!();
|
|
let x = if k & 2 == 0 { f32::from_bits(a as u32) as f64 } else { f64::from_bits(a as u64) };
|
|
let v: u64 = match k {
|
|
0 | 2 => x as i32 as u32 as u64,
|
|
1 | 3 => x as u32 as u64,
|
|
4 | 6 => x as i64 as u64,
|
|
_ => x as u64,
|
|
};
|
|
st.push(v as u128);
|
|
}
|
|
10 => {
|
|
r.u8()?;
|
|
r.u8()?;
|
|
let n = self.addr(pop!()) as usize;
|
|
let s = self.addr(pop!()) as usize;
|
|
let d = self.addr(pop!()) as usize;
|
|
if s + n > self.mem.len() || d + n > self.mem.len() {
|
|
return t("out of bounds memory access");
|
|
}
|
|
self.mem.copy_within(s..s + n, d);
|
|
}
|
|
11 => {
|
|
r.u8()?;
|
|
let n = self.addr(pop!()) as usize;
|
|
let v = pop!() as u8;
|
|
let d = self.addr(pop!()) as usize;
|
|
if d + n > self.mem.len() {
|
|
return t("out of bounds memory access");
|
|
}
|
|
self.mem[d..d + n].fill(v);
|
|
}
|
|
_ => return t(&format!("0xFC {}", k)),
|
|
}
|
|
}
|
|
0xFD => {
|
|
let k = r.u()?;
|
|
let lanes32 = |v: u128| -> [u32; 4] { [v as u32, (v >> 32) as u32, (v >> 64) as u32, (v >> 96) as u32] };
|
|
let from32 = |a: [u32; 4]| -> u128 { a[0] as u128 | (a[1] as u128) << 32 | (a[2] as u128) << 64 | (a[3] as u128) << 96 };
|
|
match k {
|
|
0 => {
|
|
r.u()?;
|
|
let off = r.u()?;
|
|
let base = pop!();
|
|
let a = self.ea(base, off, 16)?;
|
|
let mut w = [0u8; 16];
|
|
w.copy_from_slice(&self.mem[a..a + 16]);
|
|
st.push(u128::from_le_bytes(w));
|
|
}
|
|
11 => {
|
|
r.u()?;
|
|
let off = r.u()?;
|
|
let v = pop!();
|
|
let base = pop!();
|
|
let a = self.ea(base, off, 16)?;
|
|
self.mem[a..a + 16].copy_from_slice(&v.to_le_bytes());
|
|
}
|
|
12 => {
|
|
let b = r.bytes(16)?;
|
|
let mut w = [0u8; 16];
|
|
w.copy_from_slice(b);
|
|
st.push(u128::from_le_bytes(w));
|
|
}
|
|
17 | 19 => {
|
|
let a = pop!() as u32;
|
|
st.push(from32([a; 4]));
|
|
}
|
|
27 | 31 => {
|
|
let ln = r.u8()? as usize;
|
|
let v = pop!();
|
|
st.push(lanes32(v)[ln] as u128);
|
|
}
|
|
28 | 32 => {
|
|
let ln = r.u8()? as usize;
|
|
let s = pop!() as u32;
|
|
let v = pop!();
|
|
let mut a = lanes32(v);
|
|
a[ln] = s;
|
|
st.push(from32(a));
|
|
}
|
|
78..=81 => {
|
|
let b = pop!();
|
|
let a = pop!();
|
|
st.push(match k {
|
|
78 => a & b,
|
|
79 => a & !b,
|
|
80 => a | b,
|
|
_ => a ^ b,
|
|
});
|
|
}
|
|
82 => {
|
|
let c = pop!();
|
|
let b = pop!();
|
|
let a = pop!();
|
|
st.push((a & c) | (b & !c));
|
|
}
|
|
83 => {
|
|
let a = pop!();
|
|
st.push((a != 0) as u128);
|
|
}
|
|
174 | 177 | 181 | 228..=231 => {
|
|
let b = lanes32(pop!());
|
|
let a = lanes32(pop!());
|
|
let mut o = [0u32; 4];
|
|
for i in 0..4 {
|
|
let (x, y) = (a[i], b[i]);
|
|
let (fx, fy) = (f32::from_bits(x), f32::from_bits(y));
|
|
o[i] = match k {
|
|
174 => x.wrapping_add(y),
|
|
177 => x.wrapping_sub(y),
|
|
181 => x.wrapping_mul(y),
|
|
228 => (fx + fy).to_bits(),
|
|
229 => (fx - fy).to_bits(),
|
|
230 => (fx * fy).to_bits(),
|
|
_ => (fx / fy).to_bits(),
|
|
};
|
|
}
|
|
st.push(from32(o));
|
|
}
|
|
_ => return t(&format!("simd op {}", k)),
|
|
}
|
|
}
|
|
_ => return t(&format!("opcode {:#x}", op)),
|
|
}
|
|
}
|
|
}
|
|
}
|