use super::{fusable_cmp_of, Translator};
use crate::module::op::{CmpKind, Op, Slot};
use crate::Result;
#[derive(Copy, Clone)]
enum Fold {
Comm(fn(Slot, Slot, i32) -> Op),
Rhs(fn(Slot, Slot, i32) -> Op),
Sub,
Cmp(CmpKind),
}
impl Fold {
fn build(self, dst: Slot, src: Slot, k: i32, swapped: bool) -> Option<Op> {
Some(match self {
Fold::Comm(op) => op(dst, src, k),
Fold::Rhs(op) if !swapped => op(dst, src, k),
Fold::Sub if !swapped => Op::I32AddImm(dst, src, k.wrapping_neg()),
Fold::Cmp(kind) => Op::I32CmpImm {
kind: if swapped { kind.mirror() } else { kind },
dst,
lhs: src,
imm: k,
},
Fold::Rhs(_) | Fold::Sub => return None,
})
}
}
macro_rules! lower_fold {
($($visit:ident => $variant:ident, $fold:expr;)*) => {
$(
pub(in crate::module::compile) fn $visit(&mut self) -> Result<()> {
if self.reachable && !self.fold($fold) {
self.binop(Op::$variant);
}
Ok(())
}
)*
};
}
impl Translator<'_> {
lower_fold! {
visit_i32_add => I32Add, Fold::Comm(Op::I32AddImm);
visit_i32_sub => I32Sub, Fold::Sub;
visit_i32_mul => I32Mul, Fold::Comm(Op::I32MulImm);
visit_i32_and => I32And, Fold::Comm(Op::I32AndImm);
visit_i32_or => I32Or, Fold::Comm(Op::I32OrImm);
visit_i32_xor => I32Xor, Fold::Comm(Op::I32XorImm);
visit_i32_shl => I32Shl, Fold::Rhs(Op::I32ShlImm);
visit_i32_shr_s => I32ShrS, Fold::Rhs(Op::I32ShrSImm);
visit_i32_shr_u => I32ShrU, Fold::Rhs(Op::I32ShrUImm);
visit_i32_eq => I32Eq, Fold::Cmp(CmpKind::Eq);
visit_i32_ne => I32Ne, Fold::Cmp(CmpKind::Ne);
visit_i32_lt_s => I32LtS, Fold::Cmp(CmpKind::LtS);
visit_i32_lt_u => I32LtU, Fold::Cmp(CmpKind::LtU);
visit_i32_gt_s => I32GtS, Fold::Cmp(CmpKind::GtS);
visit_i32_gt_u => I32GtU, Fold::Cmp(CmpKind::GtU);
visit_i32_le_s => I32LeS, Fold::Cmp(CmpKind::LeS);
visit_i32_le_u => I32LeU, Fold::Cmp(CmpKind::LeU);
visit_i32_ge_s => I32GeS, Fold::Cmp(CmpKind::GeS);
visit_i32_ge_u => I32GeU, Fold::Cmp(CmpKind::GeU);
}
#[inline]
fn fold(&mut self, fold: Fold) -> bool {
match self.pending_const {
Some((konst, k)) => self.fold_const(fold, konst, k),
None => false,
}
}
fn fold_const(&mut self, fold: Fold, konst: Slot, k: i32) -> bool {
let (lhs, rhs) = (self.operand(1), self.operand(0));
let (src, swapped) = if rhs == konst {
(lhs, false)
} else if lhs == konst {
(rhs, true)
} else {
return false;
};
let dst = self.height_slot(2);
let Some(op) = fold.build(dst, src, k, swapped) else {
return false;
};
let Some(offset) = self.unemit_const(konst, k) else {
return false;
};
self.produce(2, dst);
let cmp = fusable_cmp_of(&op);
self.emit_result(op, dst);
self.fusable_cmp = cmp;
if self.retain_offsets {
if let Some(last) = self.code.offsets.last_mut() {
*last = offset;
}
}
true
}
}