diff options
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/parser/src/optimize.rs | 187 | ||||
| -rw-r--r-- | crates/tinywasm/src/interpreter/executor.rs | 78 | ||||
| -rw-r--r-- | crates/types/src/instructions.rs | 40 |
3 files changed, 257 insertions, 48 deletions
diff --git a/crates/parser/src/optimize.rs b/crates/parser/src/optimize.rs index c066c29..7308262 100644 --- a/crates/parser/src/optimize.rs +++ b/crates/parser/src/optimize.rs @@ -1,6 +1,37 @@ use crate::ParserOptions; use alloc::vec::Vec; -use tinywasm_types::{Instruction, WasmFunctionData}; +use tinywasm_types::{CmpOp, Instruction, WasmFunctionData}; + +fn cmp_op(instr: Instruction) -> Option<CmpOp> { + Some(match instr { + Instruction::I32Eq => CmpOp::Eq, + Instruction::I32Ne => CmpOp::Ne, + Instruction::I32LtS => CmpOp::LtS, + Instruction::I32LtU => CmpOp::LtU, + Instruction::I32GtS => CmpOp::GtS, + Instruction::I32GtU => CmpOp::GtU, + Instruction::I32LeS => CmpOp::LeS, + Instruction::I32LeU => CmpOp::LeU, + Instruction::I32GeS => CmpOp::GeS, + Instruction::I32GeU => CmpOp::GeU, + _ => return None, + }) +} + +fn inverse_cmp_op(op: CmpOp) -> CmpOp { + match op { + CmpOp::Eq => CmpOp::Ne, + CmpOp::Ne => CmpOp::Eq, + CmpOp::LtS => CmpOp::GeS, + CmpOp::LtU => CmpOp::GeU, + CmpOp::GtS => CmpOp::LeS, + CmpOp::GtU => CmpOp::LeU, + CmpOp::LeS => CmpOp::GtS, + CmpOp::LeU => CmpOp::GtU, + CmpOp::GeS => CmpOp::LtS, + CmpOp::GeU => CmpOp::LtU, + } +} pub(crate) fn optimize_instructions( mut instructions: Vec<Instruction>, @@ -30,7 +61,7 @@ fn rewrite(instructions: &mut [Instruction], self_func_addr: u32) { { instructions[read - 2] = Instruction::Nop; instructions[read - 1] = Instruction::Nop; - instructions[read] = Instruction::I32AddLocals(a, b); + instructions[read] = Instruction::AddLocalLocal32(a, b); } if read > 0 { @@ -39,15 +70,15 @@ fn rewrite(instructions: &mut [Instruction], self_func_addr: u32) { if let Instruction::LocalGet32(local) = instructions[read - 2] { instructions[read - 2] = Instruction::Nop; instructions[read - 1] = Instruction::LocalGet32(local); - instructions[read] = Instruction::I32AddConst(c); + instructions[read] = Instruction::AddConst32(c); } else { instructions[read - 1] = Instruction::Nop; - instructions[read] = Instruction::I32AddConst(c); + instructions[read] = Instruction::AddConst32(c); } } Instruction::I32Const(c) => { instructions[read - 1] = Instruction::Nop; - instructions[read] = Instruction::I32AddConst(c); + instructions[read] = Instruction::AddConst32(c); } _ => {} } @@ -60,7 +91,7 @@ fn rewrite(instructions: &mut [Instruction], self_func_addr: u32) { { instructions[read - 2] = Instruction::Nop; instructions[read - 1] = Instruction::Nop; - instructions[read] = Instruction::I64AddLocals(a, b); + instructions[read] = Instruction::AddLocalLocal64(a, b); } if read > 0 { @@ -69,15 +100,15 @@ fn rewrite(instructions: &mut [Instruction], self_func_addr: u32) { if let Instruction::LocalGet64(local) = instructions[read - 2] { instructions[read - 2] = Instruction::Nop; instructions[read - 1] = Instruction::LocalGet64(local); - instructions[read] = Instruction::I64AddConst(c); + instructions[read] = Instruction::AddConst64(c); } else { instructions[read - 1] = Instruction::Nop; - instructions[read] = Instruction::I64AddConst(c); + instructions[read] = Instruction::AddConst64(c); } } Instruction::I64Const(c) => { instructions[read - 1] = Instruction::Nop; - instructions[read] = Instruction::I64AddConst(c); + instructions[read] = Instruction::AddConst64(c); } _ => {} } @@ -90,7 +121,7 @@ fn rewrite(instructions: &mut [Instruction], self_func_addr: u32) { { instructions[read - 2] = Instruction::Nop; instructions[read - 1] = Instruction::Nop; - instructions[read] = Instruction::I64XorRotlConst(c); + instructions[read] = Instruction::XorRotlConst64(c); } } Instruction::I32Store(memarg) => { @@ -101,7 +132,7 @@ fn rewrite(instructions: &mut [Instruction], self_func_addr: u32) { { instructions[read - 2] = Instruction::Nop; instructions[read - 1] = Instruction::Nop; - instructions[read] = Instruction::I32StoreLocalLocal(memarg, addr_local, value_local); + instructions[read] = Instruction::StoreLocalLocal32(memarg, addr_local, value_local); } } Instruction::I64Store(memarg) => { @@ -112,7 +143,16 @@ fn rewrite(instructions: &mut [Instruction], self_func_addr: u32) { { instructions[read - 2] = Instruction::Nop; instructions[read - 1] = Instruction::Nop; - instructions[read] = Instruction::I64StoreLocalLocal(memarg, addr_local, value_local); + instructions[read] = Instruction::StoreLocalLocal64(memarg, addr_local, value_local); + } + } + Instruction::I32Load(memarg) => { + if read > 0 + && let Instruction::LocalGet32(addr_local) = instructions[read - 1] + && let Ok(addr_local) = u8::try_from(addr_local) + { + instructions[read - 1] = Instruction::Nop; + instructions[read] = Instruction::LoadLocal32(memarg, addr_local); } } Instruction::MemoryFill(mem) => { @@ -163,12 +203,12 @@ fn rewrite(instructions: &mut [Instruction], self_func_addr: u32) { } Instruction::I32Const(c) => { instructions[read - 1] = Instruction::Nop; - instructions[read] = Instruction::LocalSetConst32(dst, c); + instructions[read] = Instruction::SetLocalConst32(dst, c); } Instruction::F32Const(c) => { instructions[read - 1] = Instruction::Nop; instructions[read] = - Instruction::LocalSetConst32(dst, i32::from_ne_bytes(c.to_bits().to_ne_bytes())); + Instruction::SetLocalConst32(dst, i32::from_ne_bytes(c.to_bits().to_ne_bytes())); } _ => {} } @@ -176,21 +216,29 @@ fn rewrite(instructions: &mut [Instruction], self_func_addr: u32) { if read > 1 { match (instructions[read - 2], instructions[read - 1]) { - (Instruction::LocalGet32(src), Instruction::I32AddConst(c)) if src == dst => { + (Instruction::LocalGet32(src), Instruction::AddConst32(c)) if src == dst => { instructions[read - 2] = Instruction::Nop; instructions[read - 1] = Instruction::Nop; - instructions[read] = Instruction::LocalAddConst32(dst, c); + instructions[read] = Instruction::AddLocalConst32(dst, c); } (Instruction::LocalGet32(addr), Instruction::I32Load(memarg)) => { if let (Ok(addr), Ok(dst)) = (u8::try_from(addr), u8::try_from(dst)) { instructions[read - 2] = Instruction::Nop; instructions[read - 1] = Instruction::Nop; - instructions[read] = Instruction::I32LoadLocalSet(memarg, addr, dst); + instructions[read] = Instruction::LoadLocalSet32(memarg, addr, dst); } } _ => {} } } + + if read > 0 + && let Instruction::LoadLocal32(memarg, addr) = instructions[read - 1] + && let Ok(dst) = u8::try_from(dst) + { + instructions[read - 1] = Instruction::Nop; + instructions[read] = Instruction::LoadLocalSet32(memarg, addr, dst); + } } Instruction::LocalSet64(dst) => { if read > 0 { @@ -202,25 +250,25 @@ fn rewrite(instructions: &mut [Instruction], self_func_addr: u32) { } Instruction::I64Const(c) => { instructions[read - 1] = Instruction::Nop; - instructions[read] = Instruction::LocalSetConst64(dst, c); + instructions[read] = Instruction::SetLocalConst64(dst, c); } Instruction::F64Const(c) => { instructions[read - 1] = Instruction::Nop; instructions[read] = - Instruction::LocalSetConst64(dst, i64::from_ne_bytes(c.to_bits().to_ne_bytes())); + Instruction::SetLocalConst64(dst, i64::from_ne_bytes(c.to_bits().to_ne_bytes())); } _ => {} } } if read > 1 - && let (Instruction::LocalGet64(src), Instruction::I64AddConst(c)) = + && let (Instruction::LocalGet64(src), Instruction::AddConst64(c)) = (instructions[read - 2], instructions[read - 1]) && src == dst { instructions[read - 2] = Instruction::Nop; instructions[read - 1] = Instruction::Nop; - instructions[read] = Instruction::LocalAddConst64(dst, c); + instructions[read] = Instruction::AddLocalConst64(dst, c); } } Instruction::LocalSet128(dst) => { @@ -247,16 +295,24 @@ fn rewrite(instructions: &mut [Instruction], self_func_addr: u32) { { instructions[read - 2] = Instruction::Nop; instructions[read - 1] = Instruction::Nop; - instructions[read] = Instruction::I32LoadLocalTee(memarg, addr, dst); + instructions[read] = Instruction::LoadLocalTee32(memarg, addr, dst); + } + + if read > 0 + && let Instruction::LoadLocal32(memarg, addr) = instructions[read - 1] + && let Ok(dst) = u8::try_from(dst) + { + instructions[read - 1] = Instruction::Nop; + instructions[read] = Instruction::LoadLocalTee32(memarg, addr, dst); } } Instruction::LocalTee64(dst) if read > 0 => match instructions[read - 1] { Instruction::LocalGet64(src) if src == dst => { instructions[read] = Instruction::Nop; } - Instruction::I64XorRotlConst(c) => { + Instruction::XorRotlConst64(c) => { instructions[read - 1] = Instruction::Nop; - instructions[read] = Instruction::I64XorRotlConstTee(c, dst); + instructions[read] = Instruction::XorRotlConstTee64(c, dst); } _ => {} }, @@ -292,6 +348,87 @@ fn rewrite(instructions: &mut [Instruction], self_func_addr: u32) { instructions[read] = Instruction::Nop; } } + Instruction::JumpIfZero(ip) => { + if read > 0 && instructions[read - 1] == Instruction::I32Eqz { + instructions[read - 1] = Instruction::Nop; + instructions[read] = Instruction::JumpIfNonZero(ip); + continue; + } + + if read > 2 { + match (instructions[read - 2], instructions[read - 1]) { + (Instruction::I32Const(imm), cmp) => { + if read > 3 + && let Instruction::LocalGet32(local) = instructions[read - 3] + && let Some(op) = cmp_op(cmp) + { + instructions[read - 3] = Instruction::Nop; + instructions[read - 2] = Instruction::Nop; + instructions[read - 1] = Instruction::Nop; + instructions[read] = Instruction::JumpCmpLocalConst32 { + target_ip: ip, + local, + imm, + op: inverse_cmp_op(op), + }; + } + } + (Instruction::LocalGet32(right), cmp) => { + if read > 3 + && let Instruction::LocalGet32(left) = instructions[read - 3] + && let Some(op) = cmp_op(cmp) + { + instructions[read - 3] = Instruction::Nop; + instructions[read - 2] = Instruction::Nop; + instructions[read - 1] = Instruction::Nop; + instructions[read] = Instruction::JumpCmpLocalLocal32 { + target_ip: ip, + left, + right, + op: inverse_cmp_op(op), + }; + } + } + _ => {} + } + } + } + Instruction::JumpIfNonZero(ip) => { + if read > 0 && instructions[read - 1] == Instruction::I32Eqz { + instructions[read - 1] = Instruction::Nop; + instructions[read] = Instruction::JumpIfZero(ip); + continue; + } + + if read > 2 { + match (instructions[read - 2], instructions[read - 1]) { + (Instruction::I32Const(imm), cmp) => { + if read > 3 + && let Instruction::LocalGet32(local) = instructions[read - 3] + && let Some(op) = cmp_op(cmp) + { + instructions[read - 3] = Instruction::Nop; + instructions[read - 2] = Instruction::Nop; + instructions[read - 1] = Instruction::Nop; + instructions[read] = Instruction::JumpCmpLocalConst32 { target_ip: ip, local, imm, op }; + } + } + (Instruction::LocalGet32(right), cmp) => { + if read > 3 + && let Instruction::LocalGet32(left) = instructions[read - 3] + && let Some(op) = cmp_op(cmp) + { + instructions[read - 3] = Instruction::Nop; + instructions[read - 2] = Instruction::Nop; + instructions[read - 1] = Instruction::Nop; + instructions[read] = + Instruction::JumpCmpLocalLocal32 { target_ip: ip, left, right, op }; + } + } + _ => {} + } + } + } _ => {} } } @@ -330,6 +467,8 @@ fn dce(instructions: &mut Vec<Instruction>, function_data: &mut WasmFunctionData Instruction::Jump(ip) | Instruction::JumpIfZero(ip) | Instruction::JumpIfNonZero(ip) + | Instruction::JumpCmpLocalConst32 { target_ip: ip, .. } + | Instruction::JumpCmpLocalLocal32 { target_ip: ip, .. } | Instruction::BranchTable(ip, _, _) => ip, _ => return !matches!(instr, Instruction::Nop), }; diff --git a/crates/tinywasm/src/interpreter/executor.rs b/crates/tinywasm/src/interpreter/executor.rs index a6052a9..2f59349 100644 --- a/crates/tinywasm/src/interpreter/executor.rs +++ b/crates/tinywasm/src/interpreter/executor.rs @@ -148,6 +148,12 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { Jump(ip) => { self.exec_jump(*ip); continue; } JumpIfZero(ip) => if self.exec_jump_if_zero(*ip) { continue; }, JumpIfNonZero(ip) => if self.exec_jump_if_non_zero(*ip) { continue; }, + JumpCmpLocalConst32 { target_ip, local, imm, op } => { + if self.exec_jump_cmp_local_const_32(*target_ip, *local, *imm, *op) { continue; } + } + JumpCmpLocalLocal32 { target_ip, left, right, op } => { + if self.exec_jump_cmp_local_local_32(*target_ip, *left, *right, *op) { continue; } + } DropKeep { base32, keep32, base64, keep64, base128, keep128 } => { let mut base = self.cf.stack_base(); base.s32 += *base32 as u32; base.s64 += *base64 as u32; base.s128 += *base128 as u32; self.store.stack.values.truncate_keep_counts(base, ValueCounts { c32: *keep32 as u16, c64: *keep64 as u16, c128: *keep128 as u16 }); @@ -166,20 +172,21 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { LocalCopy32(from, to) => self.store.stack.values.local_set(&self.cf, *to, self.store.stack.values.local_get::<Value32>(&self.cf, *from)), LocalCopy64(from, to) => self.store.stack.values.local_set(&self.cf, *to, self.store.stack.values.local_get::<Value64>(&self.cf, *from)), LocalCopy128(from, to) => self.store.stack.values.local_set(&self.cf, *to, self.store.stack.values.local_get::<Value128>(&self.cf, *from)), - I32AddLocals(a, b) => self.store.stack.values.push(self.store.stack.values.local_get::<i32>(&self.cf, *a).wrapping_add(self.store.stack.values.local_get::<i32>(&self.cf, *b)))?, - I64AddLocals(a, b) => self.store.stack.values.push(self.store.stack.values.local_get::<i64>(&self.cf, *a).wrapping_add(self.store.stack.values.local_get::<i64>(&self.cf, *b)))?, - I32AddConst(c) => stack_op!(unary i32, |v| v.wrapping_add(*c)), - I64AddConst(c) => stack_op!(unary i64, |v| v.wrapping_add(*c)), - LocalAddConst32(local_index, c) => self.store.stack.values.local_update::<Value32>(&self.cf, *local_index, |local| *local = local.wrapping_add(*c as u32)), - LocalAddConst64(local_index, c) => self.store.stack.values.local_update::<Value64>(&self.cf, *local_index, |local| *local = local.wrapping_add(*c as u64)), - LocalSetConst32(local_index, c) => self.store.stack.values.local_set::<i32>(&self.cf, *local_index, *c), - LocalSetConst64(local_index, c) => self.store.stack.values.local_set::<i64>(&self.cf, *local_index, *c), - I32StoreLocalLocal(m, addr_local, value_local) => self.exec_store_local_local::<u32, u32, 4>(*m, *addr_local, *value_local, |v| v)?, - I64StoreLocalLocal(m, addr_local, value_local) =>self.exec_store_local_local::<i64, i64, 8>(*m, *addr_local, *value_local, |v| v)?, - I32LoadLocalTee(m, addr_local, dst_local) => self.exec_i32_load_local_tee(*m, *addr_local, *dst_local)?, - I32LoadLocalSet(m, addr_local, dst_local) => self.exec_i32_load_local_set(*m, *addr_local, *dst_local)?, - I64XorRotlConst(c) => stack_op!(binary i64, |lhs, rhs| (lhs ^ rhs).rotate_left(*c as u32)), - I64XorRotlConstTee(c, local_index) => { + AddLocalLocal32(a, b) => self.store.stack.values.push(self.store.stack.values.local_get::<i32>(&self.cf, *a).wrapping_add(self.store.stack.values.local_get::<i32>(&self.cf, *b)))?, + AddLocalLocal64(a, b) => self.store.stack.values.push(self.store.stack.values.local_get::<i64>(&self.cf, *a).wrapping_add(self.store.stack.values.local_get::<i64>(&self.cf, *b)))?, + AddConst32(c) => stack_op!(unary i32, |v| v.wrapping_add(*c)), + AddConst64(c) => stack_op!(unary i64, |v| v.wrapping_add(*c)), + AddLocalConst32(local_index, c) => self.store.stack.values.local_update::<Value32>(&self.cf, *local_index, |local| *local = local.wrapping_add(*c as u32)), + AddLocalConst64(local_index, c) => self.store.stack.values.local_update::<Value64>(&self.cf, *local_index, |local| *local = local.wrapping_add(*c as u64)), + SetLocalConst32(local_index, c) => self.store.stack.values.local_set::<i32>(&self.cf, *local_index, *c), + SetLocalConst64(local_index, c) => self.store.stack.values.local_set::<i64>(&self.cf, *local_index, *c), + StoreLocalLocal32(m, addr_local, value_local) => self.exec_store_local_local::<u32, u32, 4>(*m, *addr_local, *value_local, |v| v)?, + StoreLocalLocal64(m, addr_local, value_local) =>self.exec_store_local_local::<i64, i64, 8>(*m, *addr_local, *value_local, |v| v)?, + LoadLocal32(m, addr_local) => self.exec_i32_load_local(*m, *addr_local)?, + LoadLocalTee32(m, addr_local, dst_local) => self.exec_i32_load_local_tee(*m, *addr_local, *dst_local)?, + LoadLocalSet32(m, addr_local, dst_local) => self.exec_i32_load_local_set(*m, *addr_local, *dst_local)?, + XorRotlConst64(c) => stack_op!(binary i64, |lhs, rhs| (lhs ^ rhs).rotate_left(*c as u32)), + XorRotlConstTee64(c, local_index) => { stack_op!(binary i64, |lhs, rhs| (lhs ^ rhs).rotate_left(*c as u32)); stack_op!(local_tee i64, local_index); } @@ -691,6 +698,27 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { } #[inline(always)] + fn exec_jump_cmp_local_const_32(&mut self, target_ip: u32, local: LocalAddr, imm: i32, op: CmpOp) -> bool { + let lhs = self.store.stack.values.local_get::<i32>(&self.cf, local); + if cmp_i32(lhs, imm, op) { + self.cf.instr_ptr = target_ip; + return true; + } + false + } + + #[inline(always)] + fn exec_jump_cmp_local_local_32(&mut self, target_ip: u32, left: LocalAddr, right: LocalAddr, op: CmpOp) -> bool { + let lhs = self.store.stack.values.local_get::<i32>(&self.cf, left); + let rhs = self.store.stack.values.local_get::<i32>(&self.cf, right); + if cmp_i32(lhs, rhs, op) { + self.cf.instr_ptr = target_ip; + return true; + } + false + } + + #[inline(always)] fn exec_branch_table(&mut self, default_ip: u32, start: u32, len: u32) { let idx = self.store.stack.values.pop::<i32>(); let target_ip = if idx >= 0 && (idx as u32) < len { @@ -879,6 +907,12 @@ impl<'store, const BUDGETED: bool> Executor<'store, BUDGETED> { } #[inline(always)] + fn exec_i32_load_local(&mut self, memarg: MemoryArg, addr_local: u8) -> Result<()> { + let value = self.exec_i32_load_local_value(memarg, addr_local)?; + self.store.stack.values.push(value) + } + + #[inline(always)] fn exec_i32_load_local_tee(&mut self, memarg: MemoryArg, addr_local: u8, dst_local: u8) -> Result<()> { let value = self.exec_i32_load_local_value(memarg, addr_local)?; self.store.stack.values.local_set(&self.cf, u16::from(dst_local), value); @@ -1283,3 +1317,19 @@ impl<'store> Executor<'store, true> { } } } + +#[inline(always)] +fn cmp_i32(lhs: i32, rhs: i32, op: CmpOp) -> bool { + match op { + CmpOp::Eq => lhs == rhs, + CmpOp::Ne => lhs != rhs, + CmpOp::LtS => lhs < rhs, + CmpOp::LtU => (lhs as u32) < (rhs as u32), + CmpOp::GtS => lhs > rhs, + CmpOp::GtU => (lhs as u32) > (rhs as u32), + CmpOp::LeS => lhs <= rhs, + CmpOp::LeU => (lhs as u32) <= (rhs as u32), + CmpOp::GeS => lhs >= rhs, + CmpOp::GeU => (lhs as u32) >= (rhs as u32), + } +} diff --git a/crates/types/src/instructions.rs b/crates/types/src/instructions.rs index 7db9f79..8cfb097 100644 --- a/crates/types/src/instructions.rs +++ b/crates/types/src/instructions.rs @@ -48,6 +48,23 @@ pub enum ConstInstruction { I64Mul, } +/// An integer comparison operator, currently only used for conditional jumps. +#[derive(Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "debug", derive(Debug))] +#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] +pub enum CmpOp { + Eq, + Ne, + LtS, + LtU, + GtS, + GtU, + LeS, + LeU, + GeS, + GeU, +} + /// A WebAssembly Instruction /// /// These are our own internal bytecode instructions so they may not match the spec exactly. @@ -60,16 +77,17 @@ pub enum ConstInstruction { #[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))] pub enum Instruction { LocalCopy32(LocalAddr, LocalAddr), LocalCopy64(LocalAddr, LocalAddr), LocalCopy128(LocalAddr, LocalAddr), - I32AddLocals(LocalAddr, LocalAddr), I64AddLocals(LocalAddr, LocalAddr), - I32AddConst(i32), I64AddConst(i64), - LocalAddConst32(LocalAddr, i32), LocalAddConst64(LocalAddr, i64), - LocalSetConst32(LocalAddr, i32), LocalSetConst64(LocalAddr, i64), - I32StoreLocalLocal(MemoryArg, u8, u8), - I64StoreLocalLocal(MemoryArg, u8, u8), - I32LoadLocalTee(MemoryArg, u8, u8), - I32LoadLocalSet(MemoryArg, u8, u8), - I64XorRotlConst(i64), - I64XorRotlConstTee(i64, LocalAddr), + AddLocalLocal32(LocalAddr, LocalAddr), AddLocalLocal64(LocalAddr, LocalAddr), + AddConst32(i32), AddConst64(i64), + AddLocalConst32(LocalAddr, i32), AddLocalConst64(LocalAddr, i64), + SetLocalConst32(LocalAddr, i32), SetLocalConst64(LocalAddr, i64), + StoreLocalLocal32(MemoryArg, u8, u8), + StoreLocalLocal64(MemoryArg, u8, u8), + LoadLocal32(MemoryArg, u8), + LoadLocalTee32(MemoryArg, u8, u8), + LoadLocalSet32(MemoryArg, u8, u8), + XorRotlConst64(i64), + XorRotlConstTee64(i64, LocalAddr), // > Control Instructions (jump-oriented, lowered from structured control during parsing) // See <https://webassembly.github.io/spec/core/binary/instructions.html#control-instructions> @@ -78,6 +96,8 @@ pub enum Instruction { Jump(u32), JumpIfZero(u32), JumpIfNonZero(u32), + JumpCmpLocalConst32 { target_ip: u32, local: LocalAddr, imm: i32, op: CmpOp }, + JumpCmpLocalLocal32 { target_ip: u32, left: LocalAddr, right: LocalAddr, op: CmpOp }, DropKeep { base32: u16, keep32: u8, base64: u16, keep64: u8, base128: u16, keep128: u8 }, DropKeep32(u16, u16), DropKeep64(u16, u16), |
