diff options
| author | Henry <mail@henrygressmann.de> | 2026-04-24 00:13:23 +0200 |
|---|---|---|
| committer | Henry <mail@henrygressmann.de> | 2026-04-24 00:13:23 +0200 |
| commit | 19038bfea6a65e2aad89ec6a8fa51c41da35b219 (patch) | |
| tree | b4f33812059e60f55a6f85a78789b83476269908 /crates/parser | |
| parent | 5b318177aa4f79246e772c5fcef6ee4bac5bdaa0 (diff) | |
feat: new binopt superinstructions
Signed-off-by: Henry <mail@henrygressmann.de>
Diffstat (limited to 'crates/parser')
| -rw-r--r-- | crates/parser/src/optimize.rs | 362 | ||||
| -rw-r--r-- | crates/parser/src/visit.rs | 12 |
2 files changed, 329 insertions, 45 deletions
diff --git a/crates/parser/src/optimize.rs b/crates/parser/src/optimize.rs index beb944d..51d0944 100644 --- a/crates/parser/src/optimize.rs +++ b/crates/parser/src/optimize.rs @@ -1,6 +1,6 @@ use crate::macros::optimize::*; use alloc::vec::Vec; -use tinywasm_types::{CmpOp, Instruction, WasmFunctionData}; +use tinywasm_types::{BinOp, BinOp128, CmpOp, Instruction, WasmFunctionData}; pub(crate) struct OptimizeResult { pub(crate) instructions: Vec<Instruction>, @@ -35,17 +35,66 @@ fn rewrite( LocalCopy128(a, b) if a == b => instrs[i] = Nop, Call(addr) if addr == self_func_addr => instrs[i] = CallSelf, ReturnCall(addr) if addr == self_func_addr => instrs[i] = ReturnCallSelf, - I32Add => { - rewrite!(instrs, i, [LocalGet32(a), LocalGet32(b)] => AddLocalLocal32(a, b)); - rewrite!(instrs, i, [LocalGet32(local), I32Const(c)] => [ Nop, LocalGet32(local), AddConst32(c)]); - rewrite!(instrs, i, [I32Const(c)] => AddConst32(c)); + 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)); + if matches!(op, BinOp::IAdd) { + rewrite!(instrs, i, [I32Const(c)] => AddConst32(c)); + } } - I64Add => { - rewrite!(instrs, i, [LocalGet64(a), LocalGet64(b)] => AddLocalLocal64(a, b)); - rewrite!(instrs, i, [LocalGet64(local), I64Const(c)] => [ Nop, LocalGet64(local), AddConst64(c)]); - rewrite!(instrs, i, [I64Const(c)] => AddConst64(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)); + } + 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)); + if matches!(op, BinOp::IAdd) { + rewrite!(instrs, i, [I64Const(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)); + } + 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))); + } + 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))); + } + 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))); + } + 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))); + } + 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)); + } + 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)); } - I64Rotl => rewrite!(instrs, i, [I64Xor, I64Const(c)] => XorRotlConst64(c)), I32Store(memarg) => { rewrite!(instrs, i, [LocalGet32(addr_local), LocalGet32(value_local)] if @@ -80,11 +129,35 @@ fn rewrite( 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), + }; + } + } 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, [AddLocalLocal32(a, b)] => AddLocalLocalSet32(a, b, dst)); - rewrite!(instrs, i, [LocalGet32(src), AddConst32(c)] if (src == dst) => AddLocalConst32(dst, c)); + 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!(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 @@ -94,16 +167,31 @@ fn rewrite( } LocalSet64(dst) => { rewrite!(instrs, i, [LocalGet64(src)] => if src == dst { Nop } else { LocalCopy64(src, 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, [AddLocalLocal64(a, b)] => AddLocalLocalSet64(a, b, dst)); rewrite!(instrs, i, - [LocalGet64(src), AddConst64(c)] if (src == dst) => - AddLocalConst64(dst, c) + [LocalTee64(src), I64Const(c), instr] if (let Some(op) = int_bin_op_64(instr)) => + [LocalSet64(src), Nop, Nop, match (dst == src, op) { + (true, BinOp::IAdd) => IncLocal64(dst, c), + (true, BinOp::ISub) => IncLocal64(dst, c.wrapping_neg()), + _ => BinOpLocalConstSet64(op, src, c, dst), + }] ); + 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)); rewrite!(instrs, i, [LocalGet32(addr), V128Load(memarg)] if (let (Ok(addr), Ok(dst)) = (u8::try_from(addr), u8::try_from(dst))) => @@ -111,7 +199,25 @@ 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)); rewrite!(instrs, i, @@ -126,24 +232,43 @@ fn rewrite( } 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)); - rewrite!(instrs, i, [XorRotlConst64(c)] => XorRotlConstTee64(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)); 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]), - Drop64 => rewrite!(instrs, i, [LocalTee64(local)] => [LocalSet64(local), Nop]), - Drop128 => rewrite!(instrs, i, [LocalTee128(local)] => [LocalSet128(local), Nop]), + 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)); + } JumpIfZero(ip) => { rewrite!(instrs, i, [I32Eqz] => { - replace!(instrs, i, 1 => [Nop, JumpIfNonZero(ip)]); + replace!(instrs, i, 1 => [Nop, JumpIfNonZero32(ip)]); + continue; + }); + rewrite!(instrs, i, [I64Eqz] => { + replace!(instrs, i, 1 => [Nop, JumpIfNonZero64(ip)]); continue; }); rewrite!(instrs, i, @@ -163,16 +288,24 @@ fn rewrite( [LocalGet64(left), LocalGet64(right), cmp] if (let Some(op) = cmp_op_64(cmp)) => JumpCmpLocalLocal64 { target_ip: ip, left, right, op: inverse_cmp_op(op) } ); - rewrite!(instrs, i, [I32Const(imm), cmp] if (let Some(op) = cmp_op(cmp)) => - JumpCmpStackConst32 { target_ip: ip, imm, op: inverse_cmp_op(op) } - ); - rewrite!(instrs, i, [I64Const(imm), cmp] if (let Some(op) = cmp_op_64(cmp)) => - JumpCmpStackConst64 { target_ip: ip, imm, 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, [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 }, + }); } JumpIfNonZero(ip) => { rewrite!(instrs, i, [I32Eqz] => { - replace!(instrs, i, 1 => [Nop, JumpIfZero(ip)]); + replace!(instrs, i, 1 => [Nop, JumpIfZero32(ip)]); + continue; + }); + rewrite!(instrs, i, [I64Eqz] => { + replace!(instrs, i, 1 => [Nop, JumpIfZero64(ip)]); continue; }); rewrite!(instrs, i, @@ -192,13 +325,27 @@ fn rewrite( [LocalGet64(left), LocalGet64(right), cmp] if (let Some(op) = cmp_op_64(cmp)) => JumpCmpLocalLocal64 { target_ip: ip, left, right, op } ); - rewrite!(instrs, i, [I32Const(imm), cmp] if (let Some(op) = cmp_op(cmp)) => - JumpCmpStackConst32 { target_ip: ip, imm, op } - ); - rewrite!(instrs, i, [I64Const(imm), cmp] if (let Some(op) = cmp_op_64(cmp)) => - JumpCmpStackConst64 { target_ip: ip, imm, 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, [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 }, + }); } + 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), + _ => {} + }, _ => {} } @@ -226,6 +373,115 @@ fn cmp_op(instr: Instruction) -> Option<CmpOp> { }) } +fn int_bin_op_32(instr: Instruction) -> Option<BinOp> { + Some(match instr { + Instruction::I32Add => BinOp::IAdd, + Instruction::I32Sub => BinOp::ISub, + Instruction::I32Mul => BinOp::IMul, + Instruction::I32And => BinOp::IAnd, + Instruction::I32Or => BinOp::IOr, + Instruction::I32Xor => BinOp::IXor, + Instruction::I32Shl => BinOp::IShl, + Instruction::I32ShrS => BinOp::IShrS, + Instruction::I32ShrU => BinOp::IShrU, + Instruction::I32Rotl => BinOp::IRotl, + Instruction::I32Rotr => BinOp::IRotr, + _ => return None, + }) +} + +fn int_bin_op_64(instr: Instruction) -> Option<BinOp> { + Some(match instr { + Instruction::I64Add => BinOp::IAdd, + Instruction::I64Sub => BinOp::ISub, + Instruction::I64Mul => BinOp::IMul, + Instruction::I64And => BinOp::IAnd, + Instruction::I64Or => BinOp::IOr, + Instruction::I64Xor => BinOp::IXor, + Instruction::I64Shl => BinOp::IShl, + Instruction::I64ShrS => BinOp::IShrS, + Instruction::I64ShrU => BinOp::IShrU, + Instruction::I64Rotl => BinOp::IRotl, + Instruction::I64Rotr => BinOp::IRotr, + _ => return None, + }) +} + +fn float_bin_op_32(instr: Instruction) -> Option<BinOp> { + Some(match instr { + Instruction::F32Add => BinOp::FAdd, + Instruction::F32Sub => BinOp::FSub, + Instruction::F32Mul => BinOp::FMul, + Instruction::F32Div => BinOp::FDiv, + Instruction::F32Min => BinOp::FMin, + Instruction::F32Max => BinOp::FMax, + Instruction::F32Copysign => BinOp::FCopysign, + _ => return None, + }) +} + +fn float_bin_op_64(instr: Instruction) -> Option<BinOp> { + Some(match instr { + Instruction::F64Add => BinOp::FAdd, + Instruction::F64Sub => BinOp::FSub, + Instruction::F64Mul => BinOp::FMul, + Instruction::F64Div => BinOp::FDiv, + Instruction::F64Min => BinOp::FMin, + Instruction::F64Max => BinOp::FMax, + Instruction::F64Copysign => BinOp::FCopysign, + _ => return None, + }) +} + +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_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)), + _ => None, + } +} + +fn bin_op_128(instr: Instruction) -> Option<BinOp128> { + Some(match instr { + Instruction::V128And => BinOp128::And, + Instruction::V128AndNot => BinOp128::AndNot, + Instruction::V128Or => BinOp128::Or, + Instruction::V128Xor => BinOp128::Xor, + Instruction::I64x2Add => BinOp128::I64x2Add, + Instruction::I64x2Mul => BinOp128::I64x2Mul, + _ => return None, + }) +} + +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, @@ -257,6 +513,29 @@ 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]; + let mut filled = 0usize; + + for idx in (0..read).rev() { + let instr = instrs[idx]; + if matches!(instr, Instruction::MergeBarrier) { + return None; + } + if matches!(instr, Instruction::Nop) { + continue; + } + + out[2 - filled] = (idx, instr); + filled += 1; + if filled == 3 { + return Some(out); + } + } + + None +} + fn remove_nop(instructions: &mut Vec<Instruction>, function_data: &mut WasmFunctionData) { let old_len = instructions.len(); if old_len == 0 { @@ -266,7 +545,8 @@ fn remove_nop(instructions: &mut Vec<Instruction>, function_data: &mut WasmFunct let mut removed_before = Vec::with_capacity(old_len + 1); removed_before.push(0u32); instructions.iter().for_each(|instr| { - let removed = removed_before.last().copied().unwrap_or(0) + u32::from(matches!(instr, Instruction::Nop)); + let removed = removed_before.last().copied().unwrap_or(0) + + u32::from(matches!(instr, Instruction::Nop | Instruction::MergeBarrier)); removed_before.push(removed); }); @@ -290,6 +570,10 @@ fn remove_nop(instructions: &mut Vec<Instruction>, function_data: &mut WasmFunct 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::JumpCmpLocalConst32 { target_ip: ip, .. } @@ -297,16 +581,16 @@ fn remove_nop(instructions: &mut Vec<Instruction>, function_data: &mut WasmFunct | Instruction::JumpCmpLocalLocal32 { target_ip: ip, .. } | Instruction::JumpCmpLocalLocal64 { target_ip: ip, .. } | Instruction::BranchTable(ip, _, _) => ip, - _ => return !matches!(instr, Instruction::Nop), + _ => return !matches!(instr, Instruction::Nop | Instruction::MergeBarrier), }; let old_target = *ip as usize; if old_target > old_len { - return !matches!(instr, Instruction::Nop); + return !matches!(instr, Instruction::Nop | Instruction::MergeBarrier); } *ip -= removed_before[old_target]; debug_assert!(*ip < compacted_len, "remapped jump target points past end of function"); - !matches!(instr, Instruction::Nop) + !matches!(instr, Instruction::Nop | Instruction::MergeBarrier) }); } diff --git a/crates/parser/src/visit.rs b/crates/parser/src/visit.rs index 5c99df2..5e1ca15 100644 --- a/crates/parser/src/visit.rs +++ b/crates/parser/src/visit.rs @@ -240,8 +240,8 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild } fn visit_loop(&mut self, _ty: wasmparser::BlockType) -> Self::Output { - if !matches!(self.instructions.last(), Some(Instruction::Nop)) { - self.instructions.push(Instruction::Nop); // add nop to ensure that no superinstruction can be merged across block boundaries + if !matches!(self.instructions.last(), Some(Instruction::Nop | Instruction::MergeBarrier)) { + self.instructions.push(Instruction::MergeBarrier); // prevent superinstructions from merging across block boundaries } let start_ip = self.instructions.len(); self.ctx_stack.push(LoweringCtx { kind: BlockKind::Loop, has_else: false, start_ip, branch_jumps: Vec::new() }); @@ -266,8 +266,8 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild ctx.has_else = true; ctx.branch_jumps.push(jump_ip); self.patch_jump_if_zero(cond_jump_ip, self.instructions.len()); - if !matches!(self.instructions.last(), Some(Instruction::Nop)) { - self.instructions.push(Instruction::Nop); // add nop to ensure that no superinstruction can be merged across block boundaries + if !matches!(self.instructions.last(), Some(Instruction::Nop | Instruction::MergeBarrier)) { + self.instructions.push(Instruction::MergeBarrier); // prevent superinstructions from merging across block boundaries } }; }; @@ -276,8 +276,8 @@ impl<'a, R: WasmModuleResources> wasmparser::VisitOperator<'a> for FunctionBuild fn visit_end(&mut self) -> Self::Output { if let Some(ctx) = self.ctx_stack.pop() { self.patch_end_jumps(ctx, self.instructions.len()); - if !matches!(self.instructions.last(), Some(Instruction::Nop)) { - self.instructions.push(Instruction::Nop); // add nop to ensure that no superinstruction can be merged across block boundaries + if !matches!(self.instructions.last(), Some(Instruction::Nop | Instruction::MergeBarrier)) { + self.instructions.push(Instruction::MergeBarrier); // prevent superinstructions from merging across block boundaries } } else { self.instructions.push(Instruction::Return); |
