summaryrefslogtreecommitdiff
path: root/crates/parser/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/parser/src')
-rw-r--r--crates/parser/src/lib.rs46
-rw-r--r--crates/parser/src/macros.rs117
-rw-r--r--crates/parser/src/module.rs15
-rw-r--r--crates/parser/src/optimize.rs794
-rw-r--r--crates/parser/src/visit.rs8
5 files changed, 785 insertions, 195 deletions
diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs
index 7b3dfa2..949afa2 100644
--- a/crates/parser/src/lib.rs
+++ b/crates/parser/src/lib.rs
@@ -47,11 +47,22 @@ pub use tinywasm_types::Module;
pub struct ParserOptions {
/// Whether to optimize local memory allocation by skipping allocation of unused local memories.
pub optimize_local_memory_allocation: bool,
+ /// Whether to run the peephole rewrite optimizer.
+ pub optimize_rewrite: bool,
+ /// Whether to remove `Nop` and `MergeBarrier` instructions after rewriting.
+ pub optimize_remove_nop: bool,
+ /// Whether to invert conditional branches over an unconditional jump.
+ pub optimize_branch_inversion: bool,
}
impl Default for ParserOptions {
fn default() -> Self {
- Self { optimize_local_memory_allocation: true }
+ Self {
+ optimize_local_memory_allocation: true,
+ optimize_rewrite: true,
+ optimize_remove_nop: true,
+ optimize_branch_inversion: false,
+ }
}
}
@@ -66,6 +77,39 @@ impl ParserOptions {
pub const fn optimize_local_memory_allocation(&self) -> bool {
self.optimize_local_memory_allocation
}
+
+ /// Enable or disable the peephole rewrite optimizer.
+ pub const fn with_rewrite_optimization(mut self, enabled: bool) -> Self {
+ self.optimize_rewrite = enabled;
+ self
+ }
+
+ /// Returns whether the peephole rewrite optimizer is enabled.
+ pub const fn optimize_rewrite(&self) -> bool {
+ self.optimize_rewrite
+ }
+
+ /// Enable or disable `Nop`/`MergeBarrier` removal after rewriting.
+ pub const fn with_nop_removal_optimization(mut self, enabled: bool) -> Self {
+ self.optimize_remove_nop = enabled;
+ self
+ }
+
+ /// Returns whether `Nop`/`MergeBarrier` removal is enabled.
+ pub const fn optimize_remove_nop(&self) -> bool {
+ self.optimize_remove_nop
+ }
+
+ /// Enable or disable the optimization that inverts conditional branches over an unconditional jump.
+ pub const fn with_branch_inversion_optimization(mut self, enabled: bool) -> Self {
+ self.optimize_branch_inversion = enabled;
+ self
+ }
+
+ /// Returns whether conditional branch inversion optimization is enabled.
+ pub const fn optimize_branch_inversion(&self) -> bool {
+ self.optimize_branch_inversion
+ }
}
/// A WebAssembly parser
diff --git a/crates/parser/src/macros.rs b/crates/parser/src/macros.rs
index 97f0080..a6eab61 100644
--- a/crates/parser/src/macros.rs
+++ b/crates/parser/src/macros.rs
@@ -216,5 +216,120 @@ pub(crate) mod optimize {
};
}
- pub(crate) use {replace, rewrite};
+ macro_rules! define_local_source_resolver {
+ (
+ $name:ident,
+ get = $get:ident,
+ tee = $tee:ident,
+ set = $set:ident,
+ copy = $copy:ident,
+ binop_local_local_tee = $lltee:ident,
+ binop_local_local_set = $llset:ident,
+ binop_local_const_tee = $lctee:ident,
+ binop_local_const_set = $lcset:ident
+ $(, load_local_tee = $loadtee:ident, load_local_set = $loadset:ident)?
+ ) => {
+ fn $name(instrs: &mut [Instruction], read: usize, instr: Instruction) -> Option<(Instruction, u16)> {
+ Some(match instr {
+ Instruction::$get(local) => (Instruction::Nop, local),
+ Instruction::$tee(local) => {
+ let replacement = if let Some([(prev_idx, Instruction::$get(src))]) = previous_non_nop::<1>(instrs, read) {
+ instrs[prev_idx] = Instruction::Nop;
+ if src == local { Instruction::Nop } else { Instruction::$copy(src, local) }
+ } else {
+ Instruction::$set(local)
+ };
+ (replacement, local)
+ }
+ Instruction::$lltee(op, a, b, local) => (Instruction::$llset(op, a, b, local), local),
+ Instruction::$lctee(op, src, c, local) => (Instruction::$lcset(op, src, c, local), local),
+ $(Instruction::$loadtee(memarg, addr, local) => (Instruction::$loadset(memarg, addr, local), local.into()),)?
+ _ => return None,
+ })
+ }
+ };
+ }
+
+ macro_rules! fold_local_binop {
+ (
+ $instrs:ident, $read:expr, $dst:expr,
+ source = $source:ident,
+ op = $op:ident,
+ const = $const:ident,
+ local_local = $local_local:ident,
+ local_const = $local_const:expr
+ ) => {{
+ if let Some([(lhs_idx, lhs_src), (rhs_idx, rhs_src), (op_idx, raw_op)]) =
+ previous_non_nop::<3>($instrs, $read)
+ && let Some((lhs_instr, lhs)) = $source($instrs, lhs_idx, lhs_src)
+ && let Some(op) = $op(raw_op)
+ {
+ if let Some((rhs_instr, rhs)) = $source($instrs, rhs_idx, rhs_src) {
+ $instrs[lhs_idx] = lhs_instr;
+ $instrs[rhs_idx] = rhs_instr;
+ $instrs[op_idx] = Instruction::Nop;
+ $instrs[$read] = Instruction::$local_local(op, lhs, rhs, $dst);
+ } else if let Some(imm) = $const(rhs_src, raw_op) {
+ $instrs[lhs_idx] = lhs_instr;
+ $instrs[rhs_idx] = Instruction::Nop;
+ $instrs[op_idx] = Instruction::Nop;
+ $instrs[$read] = $local_const($dst, lhs, op, imm);
+ }
+ }
+ }};
+ }
+
+ macro_rules! rewrite_local_set_direct {
+ (
+ $instrs:ident, $read:ident, $dst:expr,
+ get = $get:ident,
+ copy = $copy:ident,
+ binop_local_local = $ll:ident,
+ binop_local_local_set = $llset:ident,
+ binop_local_const = $lc:ident,
+ binop_local_const_set = $lcset:expr
+ $(, const_instr = $const_instr:ident, set_local_const = $set_local_const:ident)?
+ ) => {{
+ rewrite!($instrs, $read, [$get(src)] => if src == $dst { Instruction::Nop } else { Instruction::$copy(src, $dst) });
+ $(rewrite!($instrs, $read, [$const_instr(c)] => Instruction::$set_local_const($dst, c));)?
+ rewrite!($instrs, $read, [$ll(op, a, b)] => Instruction::$llset(op, a, b, $dst));
+ rewrite!($instrs, $read, [$lc(op, src, c)] => { replace!($instrs, $read, 1 => $lcset($dst, src, op, c)); });
+ }};
+ }
+
+ macro_rules! rewrite_local_tee_direct {
+ (
+ $instrs:ident, $read:ident, $dst:expr,
+ get = $get:ident,
+ binop_local_local = $ll:ident,
+ binop_local_local_tee = $lltee:ident,
+ binop_local_const = $lc:ident,
+ binop_local_const_tee = $lctee:ident
+ ) => {{
+ rewrite!($instrs, $read, [$get(src)] if (src == $dst) => [Instruction::$get(src), Instruction::Nop]);
+ rewrite!($instrs, $read, [$ll(op, a, b)] => Instruction::$lltee(op, a, b, $dst));
+ rewrite!($instrs, $read, [$lc(op, src, c)] => Instruction::$lctee(op, src, c, $dst));
+ }};
+ }
+
+ macro_rules! rewrite_drop_tee_direct {
+ (
+ $instrs:ident, $read:ident,
+ tee = $tee:ident,
+ set = $set:ident,
+ binop_local_local_tee = $lltee:ident,
+ binop_local_local_set = $llset:ident,
+ binop_local_const_tee = $lctee:ident,
+ binop_local_const_set = $lcset:ident
+ ) => {{
+ rewrite!($instrs, $read, [$tee(local)] => [Instruction::$set(local), Instruction::Nop]);
+ rewrite!($instrs, $read, [$lltee(op, a, b, dst)] => Instruction::$llset(op, a, b, dst));
+ rewrite!($instrs, $read, [$lctee(op, src, c, dst)] => Instruction::$lcset(op, src, c, dst));
+ }};
+ }
+
+ pub(crate) use {
+ define_local_source_resolver, fold_local_binop, replace, rewrite, rewrite_drop_tee_direct,
+ rewrite_local_set_direct, rewrite_local_tee_direct,
+ };
}
diff --git a/crates/parser/src/module.rs b/crates/parser/src/module.rs
index 164127f..548280e 100644
--- a/crates/parser/src/module.rs
+++ b/crates/parser/src/module.rs
@@ -200,17 +200,26 @@ impl ModuleReader {
{
let ty = self.func_types.get(ty_idx as usize).expect("No func type for func, this is a bug").clone();
let params = ValueCounts::from_iter(ty.params());
+ let results = ValueCounts::from_iter(ty.results());
let self_func = (imported_func_count + func_idx) as u32;
let local_mem_alloc =
optimize_local_memory_allocation && local_memory_allocation != LocalMemoryAllocation::Eager;
- let optimized =
- optimize::optimize_instructions(instructions, &mut data, self_func, import_mem_count, local_mem_alloc);
+ let optimized = optimize::optimize_instructions(
+ instructions,
+ &mut data,
+ options,
+ self_func,
+ import_mem_count,
+ local_mem_alloc,
+ );
if optimized.uses_local_memory {
local_memory_allocation = LocalMemoryAllocation::Eager;
}
- funcs.push(WasmFunction { instructions: optimized.instructions.into(), data, locals, params, ty }.into());
+ funcs.push(
+ WasmFunction { instructions: optimized.instructions.into(), data, locals, params, results, ty }.into(),
+ );
}
Ok(ModuleInner {
diff --git a/crates/parser/src/optimize.rs b/crates/parser/src/optimize.rs
index 51d0944..baaaf66 100644
--- a/crates/parser/src/optimize.rs
+++ b/crates/parser/src/optimize.rs
@@ -1,6 +1,7 @@
+use crate::ParserOptions;
use crate::macros::optimize::*;
use alloc::vec::Vec;
-use tinywasm_types::{BinOp, BinOp128, CmpOp, Instruction, WasmFunctionData};
+use tinywasm_types::{BinOp, BinOp128, CmpOp, ConstIdx, Instruction, WasmFunctionData};
pub(crate) struct OptimizeResult {
pub(crate) instructions: Vec<Instruction>,
@@ -10,12 +11,27 @@ pub(crate) struct OptimizeResult {
pub(crate) fn optimize_instructions(
mut instructions: Vec<Instruction>,
function_data: &mut WasmFunctionData,
+ options: &ParserOptions,
self_func_addr: u32,
imported_memory_count: u32,
track_local_memory_usage: bool,
) -> OptimizeResult {
- let uses_local_memory = rewrite(&mut instructions, self_func_addr, imported_memory_count, track_local_memory_usage);
- remove_nop(&mut instructions, function_data);
+ let uses_local_memory = if options.optimize_rewrite() {
+ rewrite(
+ &mut instructions,
+ self_func_addr,
+ imported_memory_count,
+ track_local_memory_usage,
+ options.optimize_branch_inversion(),
+ )
+ } else {
+ track_local_memory_usage
+ && instructions.iter().any(|instr| instr.memory_addr().is_some_and(|mem| mem >= imported_memory_count))
+ };
+
+ if options.optimize_remove_nop() {
+ remove_nop(&mut instructions, function_data);
+ }
OptimizeResult { instructions, uses_local_memory }
}
@@ -24,6 +40,7 @@ fn rewrite(
self_func_addr: u32,
imported_memory_count: u32,
track_local_memory_usage: bool,
+ optimize_branch_inversion: bool,
) -> bool {
use Instruction::*;
let mut uses_local_memory = false;
@@ -38,71 +55,71 @@ fn rewrite(
instr @ (I32Add | I32Mul | I32And | I32Or | I32Xor) => {
let Some(op) = int_bin_op_32(instr) else { unreachable!() };
rewrite!(instrs, i, [LocalGet32(a), LocalGet32(b)] => BinOpLocalLocal32(op, a, b));
- rewrite!(instrs, i, [LocalGet32(local), I32Const(c)] => BinOpLocalConst32(op, local, c));
- rewrite!(instrs, i, [I32Const(c), LocalGet32(local)] => BinOpLocalConst32(op, local, c));
+ rewrite!(instrs, i, [LocalGet32(local), Const32(c)] => BinOpLocalConst32(op, local, c));
+ rewrite!(instrs, i, [Const32(c), LocalGet32(local)] => BinOpLocalConst32(op, local, c));
if matches!(op, BinOp::IAdd) {
- rewrite!(instrs, i, [I32Const(c)] => AddConst32(c));
+ rewrite!(instrs, i, [Const32(c)] => AddConst32(c));
}
}
instr @ (I32Sub | I32Shl | I32ShrS | I32ShrU | I32Rotl | I32Rotr) => {
let Some(op) = int_bin_op_32(instr) else { unreachable!() };
rewrite!(instrs, i, [LocalGet32(a), LocalGet32(b)] => BinOpLocalLocal32(op, a, b));
- rewrite!(instrs, i, [LocalGet32(local), I32Const(c)] => BinOpLocalConst32(op, local, c));
+ rewrite!(instrs, i, [LocalGet32(local), Const32(c)] => BinOpLocalConst32(op, local, c));
}
instr @ (I64Add | I64Mul | I64And | I64Or | I64Xor) => {
let Some(op) = int_bin_op_64(instr) else { unreachable!() };
rewrite!(instrs, i, [LocalGet64(a), LocalGet64(b)] => BinOpLocalLocal64(op, a, b));
- rewrite!(instrs, i, [LocalGet64(local), I64Const(c)] => BinOpLocalConst64(op, local, c));
- rewrite!(instrs, i, [I64Const(c), LocalGet64(local)] => BinOpLocalConst64(op, local, c));
+ rewrite!(instrs, i, [LocalGet64(local), Const64(c)] => BinOpLocalConst64(op, local, c));
+ rewrite!(instrs, i, [Const64(c), LocalGet64(local)] => BinOpLocalConst64(op, local, c));
if matches!(op, BinOp::IAdd) {
- rewrite!(instrs, i, [I64Const(c)] => AddConst64(c));
+ rewrite!(instrs, i, [Const64(c)] => AddConst64(c));
}
}
instr @ (I64Sub | I64Shl | I64ShrS | I64ShrU | I64Rotl | I64Rotr) => {
let Some(op) = int_bin_op_64(instr) else { unreachable!() };
rewrite!(instrs, i, [LocalGet64(a), LocalGet64(b)] => BinOpLocalLocal64(op, a, b));
- rewrite!(instrs, i, [LocalGet64(local), I64Const(c)] => BinOpLocalConst64(op, local, c));
+ rewrite!(instrs, i, [LocalGet64(local), Const64(c)] => BinOpLocalConst64(op, local, c));
}
instr @ (F32Add | F32Mul | F32Min | F32Max) => {
let Some(op) = float_bin_op_32(instr) else { unreachable!() };
rewrite!(instrs, i, [LocalGet32(a), LocalGet32(b)] => BinOpLocalLocal32(op, a, b));
- rewrite!(instrs, i, [LocalGet32(local), F32Const(c)] => BinOpLocalConst32(op, local, f32_const_bits(c)));
- rewrite!(instrs, i, [F32Const(c), LocalGet32(local)] => BinOpLocalConst32(op, local, f32_const_bits(c)));
+ rewrite!(instrs, i, [LocalGet32(local), Const32(c)] => BinOpLocalConst32(op, local, c));
+ rewrite!(instrs, i, [Const32(c), LocalGet32(local)] => BinOpLocalConst32(op, local, c));
}
instr @ (F32Sub | F32Div | F32Copysign) => {
let Some(op) = float_bin_op_32(instr) else { unreachable!() };
rewrite!(instrs, i, [LocalGet32(a), LocalGet32(b)] => BinOpLocalLocal32(op, a, b));
- rewrite!(instrs, i, [LocalGet32(local), F32Const(c)] => BinOpLocalConst32(op, local, f32_const_bits(c)));
+ rewrite!(instrs, i, [LocalGet32(local), Const32(c)] => BinOpLocalConst32(op, local, c));
}
instr @ (F64Add | F64Mul | F64Min | F64Max) => {
let Some(op) = float_bin_op_64(instr) else { unreachable!() };
rewrite!(instrs, i, [LocalGet64(a), LocalGet64(b)] => BinOpLocalLocal64(op, a, b));
- rewrite!(instrs, i, [LocalGet64(local), F64Const(c)] => BinOpLocalConst64(op, local, f64_const_bits(c)));
- rewrite!(instrs, i, [F64Const(c), LocalGet64(local)] => BinOpLocalConst64(op, local, f64_const_bits(c)));
+ rewrite!(instrs, i, [LocalGet64(local), Const64(c)] => BinOpLocalConst64(op, local, c));
+ rewrite!(instrs, i, [Const64(c), LocalGet64(local)] => BinOpLocalConst64(op, local, c));
}
instr @ (F64Sub | F64Div | F64Copysign) => {
let Some(op) = float_bin_op_64(instr) else { unreachable!() };
rewrite!(instrs, i, [LocalGet64(a), LocalGet64(b)] => BinOpLocalLocal64(op, a, b));
- rewrite!(instrs, i, [LocalGet64(local), F64Const(c)] => BinOpLocalConst64(op, local, f64_const_bits(c)));
+ rewrite!(instrs, i, [LocalGet64(local), Const64(c)] => BinOpLocalConst64(op, local, c));
}
instr @ (V128And | V128Or | V128Xor | I64x2Add | I64x2Mul) => {
let Some(op) = bin_op_128(instr) else { unreachable!() };
rewrite!(instrs, i, [LocalGet128(a), LocalGet128(b)] => BinOpLocalLocal128(op, a, b));
- rewrite!(instrs, i, [LocalGet128(local), V128Const(c)] => BinOpLocalConst128(op, local, c));
- rewrite!(instrs, i, [V128Const(c), LocalGet128(local)] => BinOpLocalConst128(op, local, c));
+ rewrite!(instrs, i, [LocalGet128(local), Const128(c)] => BinOpLocalConst128(op, local, c));
+ rewrite!(instrs, i, [Const128(c), LocalGet128(local)] => BinOpLocalConst128(op, local, c));
}
V128AndNot => {
rewrite!(instrs, i, [LocalGet128(a), LocalGet128(b)] => BinOpLocalLocal128(BinOp128::AndNot, a, b));
- rewrite!(instrs, i, [LocalGet128(local), V128Const(c)] => BinOpLocalConst128(BinOp128::AndNot, local, c));
+ rewrite!(instrs, i, [LocalGet128(local), Const128(c)] => BinOpLocalConst128(BinOp128::AndNot, local, c));
}
- I32Store(memarg) => {
+ I32Store(memarg) | F32Store(memarg) => {
rewrite!(instrs, i,
[LocalGet32(addr_local), LocalGet32(value_local)] if
(let (Ok(addr_local), Ok(value_local)) = (u8::try_from(addr_local), u8::try_from(value_local))) =>
StoreLocalLocal32(memarg, addr_local, value_local)
);
}
- I64Store(memarg) => {
+ I64Store(memarg) | F64Store(memarg) => {
rewrite!(instrs, i,
[LocalGet32(addr_local), LocalGet64(value_local)] if
(let (Ok(addr_local), Ok(value_local)) = (u8::try_from(addr_local), u8::try_from(value_local))) =>
@@ -116,48 +133,48 @@ fn rewrite(
StoreLocalLocal128(memarg, addr_local, value_local)
);
}
- I32Load(memarg) => {
+ I32Load(memarg) | F32Load(memarg) => {
rewrite!(instrs, i,
[LocalGet32(addr_local)] if (let Ok(addr_local) = u8::try_from(addr_local)) =>
LoadLocal32(memarg, addr_local)
);
}
MemoryFill(mem) => {
- rewrite!(instrs, i, [I32Const(val), I32Const(size)] => MemoryFillImm(mem, val as u8, size))
+ rewrite!(instrs, i, [Const32(val), Const32(size)] => MemoryFillImm(mem, val as u8, size))
}
LocalGet32(dst) => rewrite!(instrs, i, [LocalSet32(src)] if (src == dst) => [LocalTee32(src), Nop]),
LocalGet64(dst) => rewrite!(instrs, i, [LocalSet64(src)] if (src == dst) => [LocalTee64(src), Nop]),
LocalGet128(dst) => rewrite!(instrs, i, [LocalSet128(src)] if (src == dst) => [LocalTee128(src), Nop]),
LocalSet32(dst) => {
- if let Some([(lhs_idx, lhs_src), (rhs_idx, rhs_src), (op_idx, raw_op)]) = previous_non_nop_3(instrs, i)
- && let Some((lhs_instr, lhs)) = stack_source_local_32(lhs_src)
- && let Some(op) = scalar_bin_op_32(raw_op)
- {
- if let Some((rhs_instr, rhs)) = stack_source_local_32(rhs_src) {
- instrs[lhs_idx] = lhs_instr;
- instrs[rhs_idx] = rhs_instr;
- instrs[op_idx] = Nop;
- instrs[i] = BinOpLocalLocalSet32(op, lhs, rhs, dst);
- } else if let Some(imm) = scalar_const_32(rhs_src, raw_op) {
- instrs[lhs_idx] = lhs_instr;
- instrs[rhs_idx] = Nop;
- instrs[op_idx] = Nop;
- instrs[i] = match (dst == lhs, op) {
- (true, BinOp::IAdd) => IncLocal32(dst, imm),
- (true, BinOp::ISub) => IncLocal32(dst, imm.wrapping_neg()),
- _ => BinOpLocalConstSet32(op, lhs, imm, dst),
- };
+ fold_local_binop!(
+ instrs, i, dst,
+ source = resolve_local_source_32,
+ op = scalar_bin_op_32,
+ const = scalar_const_32,
+ local_local = BinOpLocalLocalSet32,
+ local_const = |dst, lhs, op, imm| match (dst == lhs, op) {
+ (true, BinOp::IAdd) => Instruction::IncLocal32(dst, imm),
+ (true, BinOp::ISub) => Instruction::IncLocal32(dst, imm.wrapping_neg()),
+ _ => Instruction::BinOpLocalConstSet32(op, lhs, imm, dst),
}
- }
- rewrite!(instrs, i, [LocalGet32(src)] => if src == dst { Nop } else { LocalCopy32(src, dst) });
- rewrite!(instrs, i, [I32Const(c)] => SetLocalConst32(dst, c));
- rewrite!(instrs, i, [F32Const(c)] => SetLocalConst32(dst, i32::from_ne_bytes(c.to_bits().to_ne_bytes())));
- rewrite!(instrs, i, [BinOpLocalLocal32(op, a, b)] => BinOpLocalLocalSet32(op, a, b, dst));
- rewrite!(instrs, i, [BinOpLocalConst32(op, src, c)] => match (dst == src, op) {
- (true, BinOp::IAdd) => IncLocal32(dst, c),
- (true, BinOp::ISub) => IncLocal32(dst, c.wrapping_neg()),
- _ => BinOpLocalConstSet32(op, src, c, dst),
- });
+ );
+ rewrite_local_set_direct!(
+ instrs,
+ i,
+ dst,
+ get = LocalGet32,
+ copy = LocalCopy32,
+ binop_local_local = BinOpLocalLocal32,
+ binop_local_local_set = BinOpLocalLocalSet32,
+ binop_local_const = BinOpLocalConst32,
+ binop_local_const_set = |dst, src, op, c| match (dst == src, op) {
+ (true, BinOp::IAdd) => IncLocal32(dst, c),
+ (true, BinOp::ISub) => IncLocal32(dst, c.wrapping_neg()),
+ _ => BinOpLocalConstSet32(op, src, c, dst),
+ },
+ const_instr = Const32,
+ set_local_const = SetLocalConst32
+ );
rewrite!(instrs, i, [LoadLocal32(memarg, addr)] if (let Ok(dst) = u8::try_from(dst)) => LoadLocalSet32(memarg, addr, dst));
rewrite!(instrs, i,
[LocalGet32(addr), I32Load(memarg)] if
@@ -166,32 +183,58 @@ fn rewrite(
);
}
LocalSet64(dst) => {
- rewrite!(instrs, i, [LocalGet64(src)] => if src == dst { Nop } else { LocalCopy64(src, dst) });
- rewrite!(instrs, i,
- [LocalTee64(src), I64Const(c), instr] if (let Some(op) = int_bin_op_64(instr)) =>
- [LocalSet64(src), Nop, Nop, match (dst == src, op) {
+ fold_local_binop!(
+ instrs, i, dst,
+ source = resolve_local_source_64,
+ op = scalar_bin_op_64,
+ const = scalar_const_64,
+ local_local = BinOpLocalLocalSet64,
+ local_const = |dst, lhs, op, imm| match (dst == lhs, op) {
+ (true, BinOp::IAdd) => Instruction::IncLocal64(dst, imm),
+ (true, BinOp::ISub) => Instruction::IncLocal64(dst, imm.wrapping_neg()),
+ _ => Instruction::BinOpLocalConstSet64(op, lhs, imm, dst),
+ }
+ );
+ rewrite_local_set_direct!(
+ instrs,
+ i,
+ dst,
+ get = LocalGet64,
+ copy = LocalCopy64,
+ binop_local_local = BinOpLocalLocal64,
+ binop_local_local_set = BinOpLocalLocalSet64,
+ binop_local_const = BinOpLocalConst64,
+ binop_local_const_set = |dst, src, op, c| match (dst == src, op) {
(true, BinOp::IAdd) => IncLocal64(dst, c),
(true, BinOp::ISub) => IncLocal64(dst, c.wrapping_neg()),
_ => BinOpLocalConstSet64(op, src, c, dst),
- }]
+ },
+ const_instr = Const64,
+ set_local_const = SetLocalConst64
);
- rewrite!(instrs, i,
- [LocalTee64(src), F64Const(c), instr] if (let Some(op) = float_bin_op_64(instr)) =>
- [LocalSet64(src), Nop, Nop, BinOpLocalConstSet64(op, src, f64_const_bits(c), dst)]
- );
- rewrite!(instrs, i, [I64Const(c)] => SetLocalConst64(dst, c));
- rewrite!(instrs, i, [F64Const(c)] => SetLocalConst64(dst, i64::from_ne_bytes(c.to_bits().to_ne_bytes())));
- rewrite!(instrs, i, [BinOpLocalLocal64(op, a, b)] => BinOpLocalLocalSet64(op, a, b, dst));
- rewrite!(instrs, i, [BinOpLocalConst64(op, src, c)] => match (dst == src, op) {
- (true, BinOp::IAdd) => IncLocal64(dst, c),
- (true, BinOp::ISub) => IncLocal64(dst, c.wrapping_neg()),
- _ => BinOpLocalConstSet64(op, src, c, dst),
- });
}
LocalSet128(dst) => {
- rewrite!(instrs, i, [LocalGet128(src)] => if src == dst { Nop } else { LocalCopy128(src, dst) });
- rewrite!(instrs, i, [BinOpLocalLocal128(op, a, b)] => BinOpLocalLocalSet128(op, a, b, dst));
- rewrite!(instrs, i, [BinOpLocalConst128(op, src, c)] => BinOpLocalConstSet128(op, src, c, dst));
+ fold_local_binop!(
+ instrs, i, dst,
+ source = resolve_local_source_128,
+ op = bin_op_128,
+ const = const_128,
+ local_local = BinOpLocalLocalSet128,
+ local_const = |dst, lhs, op, imm| Instruction::BinOpLocalConstSet128(op, lhs, imm, dst)
+ );
+ rewrite_local_set_direct!(
+ instrs,
+ i,
+ dst,
+ get = LocalGet128,
+ copy = LocalCopy128,
+ binop_local_local = BinOpLocalLocal128,
+ binop_local_local_set = BinOpLocalLocalSet128,
+ binop_local_const = BinOpLocalConst128,
+ binop_local_const_set = |dst, src, op, c| BinOpLocalConstSet128(op, src, c, dst),
+ const_instr = Const128,
+ set_local_const = SetLocalConst128
+ );
rewrite!(instrs, i,
[LocalGet32(addr), V128Load(memarg)] if
(let (Ok(addr), Ok(dst)) = (u8::try_from(addr), u8::try_from(dst))) =>
@@ -199,153 +242,353 @@ fn rewrite(
);
}
LocalTee32(dst) => {
- if let Some([(lhs_idx, lhs_src), (rhs_idx, rhs_src), (op_idx, raw_op)]) = previous_non_nop_3(instrs, i)
- && let Some((lhs_instr, lhs)) = stack_source_local_32(lhs_src)
- && let Some(op) = scalar_bin_op_32(raw_op)
- {
- if let Some((rhs_instr, rhs)) = stack_source_local_32(rhs_src) {
- instrs[lhs_idx] = lhs_instr;
- instrs[rhs_idx] = rhs_instr;
- instrs[op_idx] = Nop;
- instrs[i] = BinOpLocalLocalTee32(op, lhs, rhs, dst);
- } else if let Some(imm) = scalar_const_32(rhs_src, raw_op) {
- instrs[lhs_idx] = lhs_instr;
- instrs[rhs_idx] = Nop;
- instrs[op_idx] = Nop;
- instrs[i] = BinOpLocalConstTee32(op, lhs, imm, dst);
- }
- }
- rewrite!(instrs, i, [LocalGet32(src)] if (src == dst) => [LocalGet32(src), Nop]);
- rewrite!(instrs, i, [BinOpLocalLocal32(op, a, b)] => BinOpLocalLocalTee32(op, a, b, dst));
- rewrite!(instrs, i, [BinOpLocalConst32(op, src, c)] => BinOpLocalConstTee32(op, src, c, dst));
- rewrite!(instrs, i, [I32Const(c), I32And] => AndConstTee32(c, dst));
- rewrite!(instrs, i, [I32Const(c), I32Sub] => SubConstTee32(c, dst));
+ fold_local_binop!(
+ instrs, i, dst,
+ source = resolve_local_source_32,
+ op = scalar_bin_op_32,
+ const = scalar_const_32,
+ local_local = BinOpLocalLocalTee32,
+ local_const = |dst, lhs, op, imm| Instruction::BinOpLocalConstTee32(op, lhs, imm, dst)
+ );
+ rewrite_local_tee_direct!(
+ instrs,
+ i,
+ dst,
+ get = LocalGet32,
+ binop_local_local = BinOpLocalLocal32,
+ binop_local_local_tee = BinOpLocalLocalTee32,
+ binop_local_const = BinOpLocalConst32,
+ binop_local_const_tee = BinOpLocalConstTee32
+ );
+ rewrite!(instrs, i, [Const32(c), I32And] => AndConstTee32(c, dst));
+ rewrite!(instrs, i, [Const32(c), I32Sub] => SubConstTee32(c, dst));
rewrite!(instrs, i,
[LocalGet32(addr), I32Load(memarg)] if
(let (Ok(addr), Ok(dst)) = (u8::try_from(addr), u8::try_from(dst))) =>
LoadLocalTee32(memarg, addr, dst)
);
rewrite!(instrs, i,
+ [LocalGet32(addr), F32Load(memarg)] if
+ (let (Ok(addr), Ok(dst)) = (u8::try_from(addr), u8::try_from(dst))) =>
+ LoadLocalTee32(memarg, addr, dst)
+ );
+ rewrite!(instrs, i,
[LoadLocal32(memarg, addr)] if (let Ok(dst) = u8::try_from(dst)) =>
LoadLocalTee32(memarg, addr, dst)
);
}
LocalTee64(dst) => {
- rewrite!(instrs, i, [LocalGet64(src)] if (src == dst) => [LocalGet64(src), Nop]);
- rewrite!(instrs, i, [BinOpLocalLocal64(op, a, b)] => BinOpLocalLocalTee64(op, a, b, dst));
- rewrite!(instrs, i, [BinOpLocalConst64(op, src, c)] => BinOpLocalConstTee64(op, src, c, dst));
- rewrite!(instrs, i, [I64Const(c), I64And] => AndConstTee64(c, dst));
- rewrite!(instrs, i, [I64Const(c), I64Sub] => SubConstTee64(c, dst));
+ fold_local_binop!(
+ instrs, i, dst,
+ source = resolve_local_source_64,
+ op = scalar_bin_op_64,
+ const = scalar_const_64,
+ local_local = BinOpLocalLocalTee64,
+ local_const = |dst, lhs, op, imm| Instruction::BinOpLocalConstTee64(op, lhs, imm, dst)
+ );
+ rewrite_local_tee_direct!(
+ instrs,
+ i,
+ dst,
+ get = LocalGet64,
+ binop_local_local = BinOpLocalLocal64,
+ binop_local_local_tee = BinOpLocalLocalTee64,
+ binop_local_const = BinOpLocalConst64,
+ binop_local_const_tee = BinOpLocalConstTee64
+ );
+ rewrite!(instrs, i, [Const64(c), I64And] => AndConstTee64(c, dst));
+ rewrite!(instrs, i, [Const64(c), I64Sub] => SubConstTee64(c, dst));
}
LocalTee128(dst) => {
- rewrite!(instrs, i, [LocalGet128(src)] if (src == dst) => [LocalGet128(src), Nop]);
- rewrite!(instrs, i, [BinOpLocalLocal128(op, a, b)] => BinOpLocalLocalTee128(op, a, b, dst));
- rewrite!(instrs, i, [BinOpLocalConst128(op, src, c)] => BinOpLocalConstTee128(op, src, c, dst));
+ fold_local_binop!(
+ instrs, i, dst,
+ source = resolve_local_source_128,
+ op = bin_op_128,
+ const = const_128,
+ local_local = BinOpLocalLocalTee128,
+ local_const = |dst, lhs, op, imm| Instruction::BinOpLocalConstTee128(op, lhs, imm, dst)
+ );
+ rewrite_local_tee_direct!(
+ instrs,
+ i,
+ dst,
+ get = LocalGet128,
+ binop_local_local = BinOpLocalLocal128,
+ binop_local_local_tee = BinOpLocalLocalTee128,
+ binop_local_const = BinOpLocalConst128,
+ binop_local_const_tee = BinOpLocalConstTee128
+ );
rewrite!(instrs, i,
[LocalGet32(addr), V128Load(memarg)] if
(let (Ok(addr), Ok(dst)) = (u8::try_from(addr), u8::try_from(dst))) =>
LoadLocalTee128(memarg, addr, dst)
);
}
- Drop32 => {
- rewrite!(instrs, i, [LocalTee32(local)] => [LocalSet32(local), Nop]);
- rewrite!(instrs, i, [BinOpLocalLocalTee32(op, a, b, dst)] => BinOpLocalLocalSet32(op, a, b, dst));
- rewrite!(instrs, i, [BinOpLocalConstTee32(op, src, c, dst)] => BinOpLocalConstSet32(op, src, c, dst));
- }
- Drop64 => {
- rewrite!(instrs, i, [LocalTee64(local)] => [LocalSet64(local), Nop]);
- rewrite!(instrs, i, [BinOpLocalLocalTee64(op, a, b, dst)] => BinOpLocalLocalSet64(op, a, b, dst));
- rewrite!(instrs, i, [BinOpLocalConstTee64(op, src, c, dst)] => BinOpLocalConstSet64(op, src, c, dst));
- }
- Drop128 => {
- rewrite!(instrs, i, [LocalTee128(local)] => [LocalSet128(local), Nop]);
- rewrite!(instrs, i, [BinOpLocalLocalTee128(op, a, b, dst)] => BinOpLocalLocalSet128(op, a, b, dst));
- rewrite!(instrs, i, [BinOpLocalConstTee128(op, src, c, dst)] => BinOpLocalConstSet128(op, src, c, dst));
+ Drop32 => rewrite_drop_tee_direct!(
+ instrs,
+ i,
+ tee = LocalTee32,
+ set = LocalSet32,
+ binop_local_local_tee = BinOpLocalLocalTee32,
+ binop_local_local_set = BinOpLocalLocalSet32,
+ binop_local_const_tee = BinOpLocalConstTee32,
+ binop_local_const_set = BinOpLocalConstSet32
+ ),
+ Drop64 => rewrite_drop_tee_direct!(
+ instrs,
+ i,
+ tee = LocalTee64,
+ set = LocalSet64,
+ binop_local_local_tee = BinOpLocalLocalTee64,
+ binop_local_local_set = BinOpLocalLocalSet64,
+ binop_local_const_tee = BinOpLocalConstTee64,
+ binop_local_const_set = BinOpLocalConstSet64
+ ),
+ Drop128 => rewrite_drop_tee_direct!(
+ instrs,
+ i,
+ tee = LocalTee128,
+ set = LocalSet128,
+ binop_local_local_tee = BinOpLocalLocalTee128,
+ binop_local_local_set = BinOpLocalLocalSet128,
+ binop_local_const_tee = BinOpLocalConstTee128,
+ binop_local_const_set = BinOpLocalConstSet128
+ ),
+ Jump(ip) => {
+ let target = resolve_jump_target(instrs, ip);
+ if target == next_non_nop(instrs, i + 1) as u32 {
+ instrs[i] = Nop;
+ } else if target != ip {
+ instrs[i] = Jump(target);
+ }
}
JumpIfZero(ip) => {
+ let target = resolve_jump_target(instrs, ip);
+ rewrite!(instrs, i, [LocalGet32(local), I32Eqz] => {
+ replace!(instrs, i, 2 => [Nop, Nop, JumpIfLocalNonZero32 { target_ip: target, local }]);
+ continue;
+ });
rewrite!(instrs, i, [I32Eqz] => {
- replace!(instrs, i, 1 => [Nop, JumpIfNonZero32(ip)]);
+ replace!(instrs, i, 1 => [Nop, JumpIfNonZero32(target)]);
+ continue;
+ });
+ rewrite!(instrs, i, [LocalGet32(local)] => JumpIfLocalZero32 { target_ip: target, local });
+ rewrite!(instrs, i, [LocalGet64(local), I64Eqz] => {
+ replace!(instrs, i, 2 => [Nop, Nop, JumpIfLocalNonZero64 { target_ip: target, local }]);
continue;
});
rewrite!(instrs, i, [I64Eqz] => {
- replace!(instrs, i, 1 => [Nop, JumpIfNonZero64(ip)]);
+ replace!(instrs, i, 1 => [Nop, JumpIfNonZero64(target)]);
continue;
});
rewrite!(instrs, i,
- [LocalGet32(local), I32Const(imm), cmp] if (let Some(op) = cmp_op(cmp)) =>
- JumpCmpLocalConst32 { target_ip: ip, local, imm, op: inverse_cmp_op(op) }
+ [LocalGet32(local), Const32(imm), cmp] if (let Some(op) = cmp_op(cmp)) =>
+ match (imm, inverse_cmp_op(op)) {
+ (0, CmpOp::Eq) => JumpIfLocalZero32 { target_ip: target, local },
+ (0, CmpOp::Ne) => JumpIfLocalNonZero32 { target_ip: target, local },
+ (imm, op) => JumpCmpLocalConst32 { target_ip: target, local, imm, op },
+ }
);
rewrite!(instrs, i,
- [LocalGet64(local), I64Const(imm), cmp] if
+ [LocalGet64(local), Const64(imm), cmp] if
(let Some(op) = cmp_op_64(cmp) && let Ok(imm) = i32::try_from(imm)) =>
- JumpCmpLocalConst64 { target_ip: ip, local, imm, op: inverse_cmp_op(op) }
+ match (imm, inverse_cmp_op(op)) {
+ (0, CmpOp::Eq) => JumpIfLocalZero64 { target_ip: target, local },
+ (0, CmpOp::Ne) => JumpIfLocalNonZero64 { target_ip: target, local },
+ (imm, op) => JumpCmpLocalConst64 { target_ip: target, local, imm, op },
+ }
);
rewrite!(instrs, i,
[LocalGet32(left), LocalGet32(right), cmp] if (let Some(op) = cmp_op(cmp)) =>
- JumpCmpLocalLocal32 { target_ip: ip, left, right, op: inverse_cmp_op(op) }
+ JumpCmpLocalLocal32 { target_ip: target, left, right, op: inverse_cmp_op(op) }
);
rewrite!(instrs, i,
[LocalGet64(left), LocalGet64(right), cmp] if (let Some(op) = cmp_op_64(cmp)) =>
- JumpCmpLocalLocal64 { target_ip: ip, left, right, op: inverse_cmp_op(op) }
+ JumpCmpLocalLocal64 { target_ip: target, left, right, op: inverse_cmp_op(op) }
);
- rewrite!(instrs, i, [I32Const(imm), cmp] if (let Some(op) = cmp_op(cmp)) => match (imm, inverse_cmp_op(op)) {
- (0, CmpOp::Eq) => JumpIfZero32(ip),
- (0, CmpOp::Ne) => JumpIfNonZero32(ip),
- (imm, op) => JumpCmpStackConst32 { target_ip: ip, imm, op },
+ rewrite!(instrs, i, [Const32(imm), cmp] if (let Some(op) = cmp_op(cmp)) => match (imm, inverse_cmp_op(op)) {
+ (0, CmpOp::Eq) => JumpIfZero32(target),
+ (0, CmpOp::Ne) => JumpIfNonZero32(target),
+ (imm, op) => JumpCmpStackConst32 { target_ip: target, imm, op },
});
- rewrite!(instrs, i, [I64Const(imm), cmp] if (let Some(op) = cmp_op_64(cmp)) => match (imm, inverse_cmp_op(op)) {
- (0, CmpOp::Eq) => JumpIfZero64(ip),
- (0, CmpOp::Ne) => JumpIfNonZero64(ip),
- (imm, op) => JumpCmpStackConst64 { target_ip: ip, imm, op },
+ rewrite!(instrs, i, [Const64(imm), cmp] if (let Some(op) = cmp_op_64(cmp)) => match (imm, inverse_cmp_op(op)) {
+ (0, CmpOp::Eq) => JumpIfZero64(target),
+ (0, CmpOp::Ne) => JumpIfNonZero64(target),
+ (imm, op) => JumpCmpStackConst64 { target_ip: target, imm, op },
});
+ if optimize_branch_inversion {
+ invert_conditional_over_jump(instrs, i);
+ }
+ canonicalize_jump_like(instrs, i);
+ if let JumpIfZero(current) = &mut instrs[i] {
+ *current = target;
+ }
}
JumpIfNonZero(ip) => {
+ let target = resolve_jump_target(instrs, ip);
+ rewrite!(instrs, i, [LocalGet32(local), I32Eqz] => {
+ replace!(instrs, i, 2 => [Nop, Nop, JumpIfLocalZero32 { target_ip: target, local }]);
+ continue;
+ });
rewrite!(instrs, i, [I32Eqz] => {
- replace!(instrs, i, 1 => [Nop, JumpIfZero32(ip)]);
+ replace!(instrs, i, 1 => [Nop, JumpIfZero32(target)]);
+ continue;
+ });
+ rewrite!(instrs, i, [LocalGet32(local)] => JumpIfLocalNonZero32 { target_ip: target, local });
+ rewrite!(instrs, i, [LocalGet64(local), I64Eqz] => {
+ replace!(instrs, i, 2 => [Nop, Nop, JumpIfLocalZero64 { target_ip: target, local }]);
continue;
});
rewrite!(instrs, i, [I64Eqz] => {
- replace!(instrs, i, 1 => [Nop, JumpIfZero64(ip)]);
+ replace!(instrs, i, 1 => [Nop, JumpIfZero64(target)]);
continue;
});
rewrite!(instrs, i,
- [LocalGet32(local), I32Const(imm), cmp] if (let Some(op) = cmp_op(cmp)) =>
- JumpCmpLocalConst32 { target_ip: ip, local, imm, op }
+ [LocalGet32(local), Const32(imm), cmp] if (let Some(op) = cmp_op(cmp)) =>
+ match (imm, op) {
+ (0, CmpOp::Eq) => JumpIfLocalZero32 { target_ip: target, local },
+ (0, CmpOp::Ne) => JumpIfLocalNonZero32 { target_ip: target, local },
+ (imm, op) => JumpCmpLocalConst32 { target_ip: target, local, imm, op },
+ }
);
rewrite!(instrs, i,
- [LocalGet64(local), I64Const(imm), cmp] if
+ [LocalGet64(local), Const64(imm), cmp] if
(let Some(op) = cmp_op_64(cmp) && let Ok(imm) = i32::try_from(imm)) =>
- JumpCmpLocalConst64 { target_ip: ip, local, imm, op }
+ match (imm, op) {
+ (0, CmpOp::Eq) => JumpIfLocalZero64 { target_ip: target, local },
+ (0, CmpOp::Ne) => JumpIfLocalNonZero64 { target_ip: target, local },
+ (imm, op) => JumpCmpLocalConst64 { target_ip: target, local, imm, op },
+ }
);
rewrite!(instrs, i,
[LocalGet32(left), LocalGet32(right), cmp] if (let Some(op) = cmp_op(cmp)) =>
- JumpCmpLocalLocal32 { target_ip: ip, left, right, op }
+ JumpCmpLocalLocal32 { target_ip: target, left, right, op }
);
rewrite!(instrs, i,
[LocalGet64(left), LocalGet64(right), cmp] if (let Some(op) = cmp_op_64(cmp)) =>
- JumpCmpLocalLocal64 { target_ip: ip, left, right, op }
+ JumpCmpLocalLocal64 { target_ip: target, left, right, op }
);
- rewrite!(instrs, i, [I32Const(imm), cmp] if (let Some(op) = cmp_op(cmp)) => match (imm, op) {
- (0, CmpOp::Eq) => JumpIfZero32(ip),
- (0, CmpOp::Ne) => JumpIfNonZero32(ip),
- (imm, op) => JumpCmpStackConst32 { target_ip: ip, imm, op },
+ rewrite!(instrs, i, [Const32(imm), cmp] if (let Some(op) = cmp_op(cmp)) => match (imm, op) {
+ (0, CmpOp::Eq) => JumpIfZero32(target),
+ (0, CmpOp::Ne) => JumpIfNonZero32(target),
+ (imm, op) => JumpCmpStackConst32 { target_ip: target, imm, op },
});
- rewrite!(instrs, i, [I64Const(imm), cmp] if (let Some(op) = cmp_op_64(cmp)) => match (imm, op) {
- (0, CmpOp::Eq) => JumpIfZero64(ip),
- (0, CmpOp::Ne) => JumpIfNonZero64(ip),
- (imm, op) => JumpCmpStackConst64 { target_ip: ip, imm, op },
+ rewrite!(instrs, i, [Const64(imm), cmp] if (let Some(op) = cmp_op_64(cmp)) => match (imm, op) {
+ (0, CmpOp::Eq) => JumpIfZero64(target),
+ (0, CmpOp::Ne) => JumpIfNonZero64(target),
+ (imm, op) => JumpCmpStackConst64 { target_ip: target, imm, op },
});
+ if optimize_branch_inversion {
+ invert_conditional_over_jump(instrs, i);
+ }
+ canonicalize_jump_like(instrs, i);
+ if let JumpIfNonZero(current) = &mut instrs[i] {
+ *current = target;
+ }
+ }
+ JumpIfZero32(ip) => {
+ let target = resolve_jump_target(instrs, ip);
+ rewrite!(instrs, i, [LocalGet32(local)] => JumpIfLocalZero32 { target_ip: target, local });
+ if optimize_branch_inversion {
+ invert_conditional_over_jump(instrs, i);
+ }
+ canonicalize_jump_like(instrs, i);
+ if let JumpIfZero32(current) = &mut instrs[i] {
+ *current = target;
+ }
+ }
+ JumpIfNonZero32(ip) => {
+ let target = resolve_jump_target(instrs, ip);
+ rewrite!(instrs, i, [LocalGet32(local)] => JumpIfLocalNonZero32 { target_ip: target, local });
+ if optimize_branch_inversion {
+ invert_conditional_over_jump(instrs, i);
+ }
+ canonicalize_jump_like(instrs, i);
+ if let JumpIfNonZero32(current) = &mut instrs[i] {
+ *current = target;
+ }
+ }
+ JumpIfZero64(ip) => {
+ let target = resolve_jump_target(instrs, ip);
+ rewrite!(instrs, i, [LocalGet64(local)] => JumpIfLocalZero64 { target_ip: target, local });
+ if optimize_branch_inversion {
+ invert_conditional_over_jump(instrs, i);
+ }
+ canonicalize_jump_like(instrs, i);
+ if let JumpIfZero64(current) = &mut instrs[i] {
+ *current = target;
+ }
+ }
+ JumpIfNonZero64(ip) => {
+ let target = resolve_jump_target(instrs, ip);
+ rewrite!(instrs, i, [LocalGet64(local)] => JumpIfLocalNonZero64 { target_ip: target, local });
+ if optimize_branch_inversion {
+ invert_conditional_over_jump(instrs, i);
+ }
+ canonicalize_jump_like(instrs, i);
+ if let JumpIfNonZero64(current) = &mut instrs[i] {
+ *current = target;
+ }
+ }
+ JumpCmpStackConst32 { target_ip, imm: 0, op } => {
+ match op {
+ CmpOp::Eq => instrs[i] = JumpIfZero32(target_ip),
+ CmpOp::Ne => instrs[i] = JumpIfNonZero32(target_ip),
+ _ => {}
+ }
+ if optimize_branch_inversion {
+ invert_conditional_over_jump(instrs, i);
+ }
+ canonicalize_jump_like(instrs, i);
+ }
+ JumpCmpStackConst64 { target_ip, imm: 0, op } => {
+ match op {
+ CmpOp::Eq => instrs[i] = JumpIfZero64(target_ip),
+ CmpOp::Ne => instrs[i] = JumpIfNonZero64(target_ip),
+ _ => {}
+ }
+ if optimize_branch_inversion {
+ invert_conditional_over_jump(instrs, i);
+ }
+ canonicalize_jump_like(instrs, i);
+ }
+ JumpCmpLocalConst32 { target_ip, local, imm: 0, op } => {
+ match op {
+ CmpOp::Eq => instrs[i] = JumpIfLocalZero32 { target_ip, local },
+ CmpOp::Ne => instrs[i] = JumpIfLocalNonZero32 { target_ip, local },
+ _ => {}
+ }
+ if optimize_branch_inversion {
+ invert_conditional_over_jump(instrs, i);
+ }
+ canonicalize_jump_like(instrs, i);
+ }
+ JumpCmpLocalConst64 { target_ip, local, imm: 0, op } => {
+ match op {
+ CmpOp::Eq => instrs[i] = JumpIfLocalZero64 { target_ip, local },
+ CmpOp::Ne => instrs[i] = JumpIfLocalNonZero64 { target_ip, local },
+ _ => {}
+ }
+ if optimize_branch_inversion {
+ invert_conditional_over_jump(instrs, i);
+ }
+ canonicalize_jump_like(instrs, i);
+ }
+ JumpCmpStackConst32 { .. }
+ | JumpCmpStackConst64 { .. }
+ | JumpCmpLocalConst32 { .. }
+ | JumpCmpLocalConst64 { .. }
+ | JumpCmpLocalLocal32 { .. }
+ | JumpCmpLocalLocal64 { .. }
+ | JumpIfLocalZero32 { .. }
+ | JumpIfLocalNonZero32 { .. }
+ | JumpIfLocalZero64 { .. }
+ | JumpIfLocalNonZero64 { .. } => {
+ if optimize_branch_inversion {
+ invert_conditional_over_jump(instrs, i);
+ }
+ canonicalize_jump_like(instrs, i);
}
- JumpCmpStackConst32 { target_ip, imm: 0, op } => match op {
- CmpOp::Eq => instrs[i] = JumpIfZero32(target_ip),
- CmpOp::Ne => instrs[i] = JumpIfNonZero32(target_ip),
- _ => {}
- },
- JumpCmpStackConst64 { target_ip, imm: 0, op } => match op {
- CmpOp::Eq => instrs[i] = JumpIfZero64(target_ip),
- CmpOp::Ne => instrs[i] = JumpIfNonZero64(target_ip),
- _ => {}
- },
_ => {}
}
@@ -437,31 +680,71 @@ fn scalar_bin_op_32(instr: Instruction) -> Option<BinOp> {
int_bin_op_32(instr).or_else(|| float_bin_op_32(instr))
}
-fn stack_source_local_32(instr: Instruction) -> Option<(Instruction, u16)> {
- Some(match instr {
- Instruction::LocalGet32(local) => (Instruction::Nop, local),
- Instruction::LocalTee32(local) => (Instruction::LocalSet32(local), local),
- Instruction::BinOpLocalLocalTee32(op, a, b, local) => {
- (Instruction::BinOpLocalLocalSet32(op, a, b, local), local)
- }
- Instruction::BinOpLocalConstTee32(op, src, c, local) => {
- (Instruction::BinOpLocalConstSet32(op, src, c, local), local)
- }
- Instruction::LoadLocalTee32(memarg, addr, local) => {
- (Instruction::LoadLocalSet32(memarg, addr, local), local.into())
- }
- _ => return None,
- })
+fn scalar_bin_op_64(instr: Instruction) -> Option<BinOp> {
+ int_bin_op_64(instr).or_else(|| float_bin_op_64(instr))
}
fn scalar_const_32(instr: Instruction, op_instr: Instruction) -> Option<i32> {
match instr {
- Instruction::I32Const(c) if int_bin_op_32(op_instr).is_some() => Some(c),
- Instruction::F32Const(c) if float_bin_op_32(op_instr).is_some() => Some(f32_const_bits(c)),
+ Instruction::Const32(c) if int_bin_op_32(op_instr).is_some() || float_bin_op_32(op_instr).is_some() => Some(c),
+ _ => None,
+ }
+}
+
+fn scalar_const_64(instr: Instruction, op_instr: Instruction) -> Option<i64> {
+ match instr {
+ Instruction::Const64(c) if int_bin_op_64(op_instr).is_some() || float_bin_op_64(op_instr).is_some() => Some(c),
_ => None,
}
}
+fn const_128(instr: Instruction, op_instr: Instruction) -> Option<ConstIdx> {
+ match instr {
+ Instruction::Const128(c) if bin_op_128(op_instr).is_some() => Some(c),
+ _ => None,
+ }
+}
+
+define_local_source_resolver!(
+ resolve_local_source_32,
+ get = LocalGet32,
+ tee = LocalTee32,
+ set = LocalSet32,
+ copy = LocalCopy32,
+ binop_local_local_tee = BinOpLocalLocalTee32,
+ binop_local_local_set = BinOpLocalLocalSet32,
+ binop_local_const_tee = BinOpLocalConstTee32,
+ binop_local_const_set = BinOpLocalConstSet32,
+ load_local_tee = LoadLocalTee32,
+ load_local_set = LoadLocalSet32
+);
+
+define_local_source_resolver!(
+ resolve_local_source_64,
+ get = LocalGet64,
+ tee = LocalTee64,
+ set = LocalSet64,
+ copy = LocalCopy64,
+ binop_local_local_tee = BinOpLocalLocalTee64,
+ binop_local_local_set = BinOpLocalLocalSet64,
+ binop_local_const_tee = BinOpLocalConstTee64,
+ binop_local_const_set = BinOpLocalConstSet64
+);
+
+define_local_source_resolver!(
+ resolve_local_source_128,
+ get = LocalGet128,
+ tee = LocalTee128,
+ set = LocalSet128,
+ copy = LocalCopy128,
+ binop_local_local_tee = BinOpLocalLocalTee128,
+ binop_local_local_set = BinOpLocalLocalSet128,
+ binop_local_const_tee = BinOpLocalConstTee128,
+ binop_local_const_set = BinOpLocalConstSet128,
+ load_local_tee = LoadLocalTee128,
+ load_local_set = LoadLocalSet128
+);
+
fn bin_op_128(instr: Instruction) -> Option<BinOp128> {
Some(match instr {
Instruction::V128And => BinOp128::And,
@@ -474,14 +757,6 @@ fn bin_op_128(instr: Instruction) -> Option<BinOp128> {
})
}
-fn f32_const_bits(value: f32) -> i32 {
- i32::from_ne_bytes(value.to_bits().to_ne_bytes())
-}
-
-fn f64_const_bits(value: f64) -> i64 {
- i64::from_ne_bytes(value.to_bits().to_ne_bytes())
-}
-
fn cmp_op_64(instr: Instruction) -> Option<CmpOp> {
Some(match instr {
Instruction::I64Eq => CmpOp::Eq,
@@ -513,8 +788,8 @@ fn inverse_cmp_op(op: CmpOp) -> CmpOp {
}
}
-fn previous_non_nop_3(instrs: &[Instruction], read: usize) -> Option<[(usize, Instruction); 3]> {
- let mut out = [(0usize, Instruction::Nop); 3];
+fn previous_non_nop<const N: usize>(instrs: &[Instruction], read: usize) -> Option<[(usize, Instruction); N]> {
+ let mut out = [(0usize, Instruction::Nop); N];
let mut filled = 0usize;
for idx in (0..read).rev() {
@@ -526,9 +801,9 @@ fn previous_non_nop_3(instrs: &[Instruction], read: usize) -> Option<[(usize, In
continue;
}
- out[2 - filled] = (idx, instr);
+ out[N - 1 - filled] = (idx, instr);
filled += 1;
- if filled == 3 {
+ if filled == N {
return Some(out);
}
}
@@ -536,6 +811,149 @@ fn previous_non_nop_3(instrs: &[Instruction], read: usize) -> Option<[(usize, In
None
}
+fn next_non_nop(instrs: &[Instruction], mut idx: usize) -> usize {
+ while idx < instrs.len() && matches!(instrs[idx], Instruction::Nop | Instruction::MergeBarrier) {
+ idx += 1;
+ }
+ idx
+}
+
+fn resolve_jump_target(instrs: &[Instruction], target: u32) -> u32 {
+ let mut idx = next_non_nop(instrs, target as usize);
+ let mut steps = 0usize;
+
+ while idx < instrs.len() && steps < instrs.len() {
+ match instrs[idx] {
+ Instruction::Jump(next) => {
+ idx = next_non_nop(instrs, next as usize);
+ steps += 1;
+ }
+ _ => break,
+ }
+ }
+
+ idx as u32
+}
+
+fn jump_target(instr: Instruction) -> Option<u32> {
+ Some(match instr {
+ Instruction::Jump(ip)
+ | Instruction::JumpIfZero(ip)
+ | Instruction::JumpIfNonZero(ip)
+ | Instruction::JumpIfZero32(ip)
+ | Instruction::JumpIfNonZero32(ip)
+ | Instruction::JumpIfZero64(ip)
+ | Instruction::JumpIfNonZero64(ip) => ip,
+ Instruction::JumpCmpStackConst32 { target_ip, .. }
+ | Instruction::JumpCmpStackConst64 { target_ip, .. }
+ | Instruction::JumpIfLocalZero32 { target_ip, .. }
+ | Instruction::JumpIfLocalNonZero32 { target_ip, .. }
+ | Instruction::JumpIfLocalZero64 { target_ip, .. }
+ | Instruction::JumpIfLocalNonZero64 { target_ip, .. }
+ | Instruction::JumpCmpLocalConst32 { target_ip, .. }
+ | Instruction::JumpCmpLocalConst64 { target_ip, .. }
+ | Instruction::JumpCmpLocalLocal32 { target_ip, .. }
+ | Instruction::JumpCmpLocalLocal64 { target_ip, .. } => target_ip,
+ _ => return None,
+ })
+}
+
+fn set_jump_target(instr: &mut Instruction, target: u32) {
+ match instr {
+ Instruction::Jump(ip)
+ | Instruction::JumpIfZero(ip)
+ | Instruction::JumpIfNonZero(ip)
+ | Instruction::JumpIfZero32(ip)
+ | Instruction::JumpIfNonZero32(ip)
+ | Instruction::JumpIfZero64(ip)
+ | Instruction::JumpIfNonZero64(ip)
+ | Instruction::JumpCmpStackConst32 { target_ip: ip, .. }
+ | Instruction::JumpCmpStackConst64 { target_ip: ip, .. }
+ | Instruction::JumpIfLocalZero32 { target_ip: ip, .. }
+ | Instruction::JumpIfLocalNonZero32 { target_ip: ip, .. }
+ | Instruction::JumpIfLocalZero64 { target_ip: ip, .. }
+ | Instruction::JumpIfLocalNonZero64 { target_ip: ip, .. }
+ | Instruction::JumpCmpLocalConst32 { target_ip: ip, .. }
+ | Instruction::JumpCmpLocalConst64 { target_ip: ip, .. }
+ | Instruction::JumpCmpLocalLocal32 { target_ip: ip, .. }
+ | Instruction::JumpCmpLocalLocal64 { target_ip: ip, .. } => *ip = target,
+ _ => {}
+ }
+}
+
+fn invert_jump(instr: Instruction, target: u32) -> Option<Instruction> {
+ Some(match instr {
+ Instruction::JumpCmpStackConst32 { imm, op, .. } => {
+ Instruction::JumpCmpStackConst32 { target_ip: target, imm, op: inverse_cmp_op(op) }
+ }
+ Instruction::JumpCmpStackConst64 { imm, op, .. } => {
+ Instruction::JumpCmpStackConst64 { target_ip: target, imm, op: inverse_cmp_op(op) }
+ }
+ Instruction::JumpCmpLocalConst32 { local, imm, op, .. } => {
+ Instruction::JumpCmpLocalConst32 { target_ip: target, local, imm, op: inverse_cmp_op(op) }
+ }
+ Instruction::JumpCmpLocalConst64 { local, imm, op, .. } => {
+ Instruction::JumpCmpLocalConst64 { target_ip: target, local, imm, op: inverse_cmp_op(op) }
+ }
+ Instruction::JumpCmpLocalLocal32 { left, right, op, .. } => {
+ Instruction::JumpCmpLocalLocal32 { target_ip: target, left, right, op: inverse_cmp_op(op) }
+ }
+ Instruction::JumpCmpLocalLocal64 { left, right, op, .. } => {
+ Instruction::JumpCmpLocalLocal64 { target_ip: target, left, right, op: inverse_cmp_op(op) }
+ }
+ _ => return None,
+ })
+}
+
+fn canonicalize_jump_like(instrs: &mut [Instruction], idx: usize) {
+ let Some(target) = jump_target(instrs[idx]) else {
+ return;
+ };
+
+ let target = resolve_jump_target(instrs, target);
+ if matches!(instrs[idx], Instruction::Jump(_)) && target == next_non_nop(instrs, idx + 1) as u32 {
+ instrs[idx] = Instruction::Nop;
+ } else {
+ set_jump_target(&mut instrs[idx], target);
+ }
+}
+
+fn invert_conditional_over_jump(instrs: &mut [Instruction], idx: usize) {
+ let Some(target) = jump_target(instrs[idx]) else {
+ return;
+ };
+ if matches!(instrs[idx], Instruction::Jump(_)) {
+ return;
+ }
+
+ let target_idx = next_non_nop(instrs, target as usize);
+ if target_idx >= instrs.len() || target_idx <= idx + 1 {
+ return;
+ }
+
+ let Some(jump_idx) = ((idx + 1)..target_idx)
+ .rev()
+ .find(|&candidate| !matches!(instrs[candidate], Instruction::Nop | Instruction::MergeBarrier))
+ else {
+ return;
+ };
+
+ let Instruction::Jump(exit_target) = instrs[jump_idx] else {
+ return;
+ };
+ if next_non_nop(instrs, jump_idx + 1) != target_idx {
+ return;
+ }
+
+ let exit_target = resolve_jump_target(instrs, exit_target);
+ let Some(inverted) = invert_jump(instrs[idx], exit_target) else {
+ return;
+ };
+
+ instrs[idx] = inverted;
+ instrs[jump_idx] = Instruction::Nop;
+}
+
fn remove_nop(instructions: &mut Vec<Instruction>, function_data: &mut WasmFunctionData) {
let old_len = instructions.len();
if old_len == 0 {
@@ -574,6 +992,10 @@ fn remove_nop(instructions: &mut Vec<Instruction>, function_data: &mut WasmFunct
| Instruction::JumpIfNonZero32(ip)
| Instruction::JumpIfZero64(ip)
| Instruction::JumpIfNonZero64(ip)
+ | Instruction::JumpIfLocalZero32 { target_ip: ip, .. }
+ | Instruction::JumpIfLocalNonZero32 { target_ip: ip, .. }
+ | Instruction::JumpIfLocalZero64 { target_ip: ip, .. }
+ | Instruction::JumpIfLocalNonZero64 { target_ip: ip, .. }
| Instruction::JumpCmpStackConst32 { target_ip: ip, .. }
| Instruction::JumpCmpStackConst64 { target_ip: ip, .. }
| Instruction::JumpCmpLocalConst32 { target_ip: ip, .. }
diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs
index 5e1ca15..9a094ed 100644
--- a/crates/parser/src/visit.rs
+++ b/crates/parser/src/visit.rs
@@ -108,7 +108,7 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild
define_operands! {
// basic instructions
- visit_global_get(GlobalGet, u32), visit_i32_const(I32Const, i32), visit_i64_const(I64Const, i64), visit_return(Return),
+ visit_global_get(GlobalGet, u32), visit_i32_const(Const32, i32), visit_i64_const(Const64, i64), visit_return(Return),
visit_call(Call, u32), visit_call_indirect(CallIndirect, u32, u32), visit_return_call_indirect(ReturnCallIndirect, u32, u32),
visit_return_call(ReturnCall, u32), visit_memory_size(MemorySize, u32), visit_memory_grow(MemoryGrow, u32), visit_unreachable(Unreachable),
visit_nop(Nop), visit_i32_eqz(I32Eqz), visit_i32_eq(I32Eq), visit_i32_ne(I32Ne), visit_i32_lt_s(I32LtS), visit_i32_lt_u(I32LtU),
@@ -360,11 +360,11 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild
}
fn visit_f32_const(&mut self, val: wasmparser::Ieee32) -> Self::Output {
- self.instructions.push(Instruction::F32Const(f32::from_bits(val.bits())));
+ self.instructions.push(Instruction::Const32(i32::from_ne_bytes(val.bits().to_ne_bytes())));
}
fn visit_f64_const(&mut self, val: wasmparser::Ieee64) -> Self::Output {
- self.instructions.push(Instruction::F64Const(f64::from_bits(val.bits())));
+ self.instructions.push(Instruction::Const64(i64::from_ne_bytes(val.bits().to_ne_bytes())));
}
fn visit_table_copy(&mut self, dst_table: u32, src_table: u32) -> Self::Output {
@@ -485,7 +485,7 @@ impl<R: WasmModuleResources> wasmparser::VisitSimdOperator<'_> for FunctionBuild
}
fn visit_v128_const(&mut self, value: wasmparser::V128) -> Self::Output {
- self.instructions.push(Instruction::V128Const(self.data.v128_constants.len() as u32));
+ self.instructions.push(Instruction::Const128(self.data.v128_constants.len() as u32));
self.data.v128_constants.push(*value.bytes());
}
}