This commit is contained in:
Chris Fallin 2022-11-03 01:23:33 -07:00
parent 3ca0ca7f7a
commit 754492c860

View file

@ -5,6 +5,7 @@ use crate::ir::*;
use crate::op_traits::is_pure; use crate::op_traits::is_pure;
use crate::passes::dom_pass::{dom_pass, DomtreePass}; use crate::passes::dom_pass::{dom_pass, DomtreePass};
use crate::scoped_map::ScopedMap; use crate::scoped_map::ScopedMap;
use crate::Operator;
pub fn gvn(body: &mut FunctionBody, cfg: &CFGInfo) { pub fn gvn(body: &mut FunctionBody, cfg: &CFGInfo) {
dom_pass::<GVNPass>(body, cfg, &mut GVNPass::default()); dom_pass::<GVNPass>(body, cfg, &mut GVNPass::default());
@ -33,6 +34,150 @@ fn value_is_pure(value: Value, body: &FunctionBody) -> bool {
} }
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
enum ConstVal {
I32(u32),
I64(u64),
None,
}
fn const_eval(op: &Operator, vals: &[ConstVal]) -> Option<ConstVal> {
match (op, vals) {
(Operator::I32Const { value }, []) => Some(ConstVal::I32(*value as u32)),
(Operator::I64Const { value }, []) => Some(ConstVal::I64(*value as u64)),
(Operator::I32Eqz, [ConstVal::I32(a)]) => Some(ConstVal::I32(if *a == 0 { 1 } else { 0 })),
(Operator::I32Eq, [ConstVal::I32(a), ConstVal::I32(b)]) => {
Some(ConstVal::I32(if a == b { 1 } else { 0 }))
}
(Operator::I32Ne, [ConstVal::I32(a), ConstVal::I32(b)]) => {
Some(ConstVal::I32(if a != b { 1 } else { 0 }))
}
(Operator::I32LtS, [ConstVal::I32(a), ConstVal::I32(b)]) => {
Some(ConstVal::I32(if (*a as i32) < (*b as i32) { 1 } else { 0 }))
}
(Operator::I32LtU, [ConstVal::I32(a), ConstVal::I32(b)]) => {
Some(ConstVal::I32(if a < b { 1 } else { 0 }))
}
(Operator::I32GtS, [ConstVal::I32(a), ConstVal::I32(b)]) => {
Some(ConstVal::I32(if (*a as i32) > (*b as i32) { 1 } else { 0 }))
}
(Operator::I32GtU, [ConstVal::I32(a), ConstVal::I32(b)]) => {
Some(ConstVal::I32(if a > b { 1 } else { 0 }))
}
(Operator::I32LeS, [ConstVal::I32(a), ConstVal::I32(b)]) => {
Some(ConstVal::I32(if (*a as i32) <= (*b as i32) {
1
} else {
0
}))
}
(Operator::I32LeU, [ConstVal::I32(a), ConstVal::I32(b)]) => {
Some(ConstVal::I32(if a <= b { 1 } else { 0 }))
}
(Operator::I32GeS, [ConstVal::I32(a), ConstVal::I32(b)]) => {
Some(ConstVal::I32(if (*a as i32) >= (*b as i32) {
1
} else {
0
}))
}
(Operator::I32GeU, [ConstVal::I32(a), ConstVal::I32(b)]) => {
Some(ConstVal::I32(if a >= b { 1 } else { 0 }))
}
(Operator::I64Eqz, [ConstVal::I64(a)]) => Some(ConstVal::I64(if *a == 0 { 1 } else { 0 })),
(Operator::I64Eq, [ConstVal::I64(a), ConstVal::I64(b)]) => {
Some(ConstVal::I64(if a == b { 1 } else { 0 }))
}
(Operator::I64Ne, [ConstVal::I64(a), ConstVal::I64(b)]) => {
Some(ConstVal::I64(if a != b { 1 } else { 0 }))
}
(Operator::I64LtS, [ConstVal::I64(a), ConstVal::I64(b)]) => {
Some(ConstVal::I64(if (*a as i64) < (*b as i64) { 1 } else { 0 }))
}
(Operator::I64LtU, [ConstVal::I64(a), ConstVal::I64(b)]) => {
Some(ConstVal::I64(if a < b { 1 } else { 0 }))
}
(Operator::I64GtS, [ConstVal::I64(a), ConstVal::I64(b)]) => {
Some(ConstVal::I64(if (*a as i64) > (*b as i64) { 1 } else { 0 }))
}
(Operator::I64GtU, [ConstVal::I64(a), ConstVal::I64(b)]) => {
Some(ConstVal::I64(if a > b { 1 } else { 0 }))
}
(Operator::I64LeS, [ConstVal::I64(a), ConstVal::I64(b)]) => {
Some(ConstVal::I64(if (*a as i64) <= (*b as i64) {
1
} else {
0
}))
}
(Operator::I64LeU, [ConstVal::I64(a), ConstVal::I64(b)]) => {
Some(ConstVal::I64(if a <= b { 1 } else { 0 }))
}
(Operator::I64GeS, [ConstVal::I64(a), ConstVal::I64(b)]) => {
Some(ConstVal::I64(if (*a as i64) >= (*b as i64) {
1
} else {
0
}))
}
(Operator::I64GeU, [ConstVal::I64(a), ConstVal::I64(b)]) => {
Some(ConstVal::I64(if a >= b { 1 } else { 0 }))
}
(Operator::I32Add, [ConstVal::I32(a), ConstVal::I32(b)]) => {
Some(ConstVal::I32(a.wrapping_add(*b)))
}
(Operator::I32Sub, [ConstVal::I32(a), ConstVal::I32(b)]) => {
Some(ConstVal::I32(a.wrapping_sub(*b)))
}
(Operator::I32Mul, [ConstVal::I32(a), ConstVal::I32(b)]) => {
Some(ConstVal::I32(a.wrapping_mul(*b)))
}
(Operator::I32And, [ConstVal::I32(a), ConstVal::I32(b)]) => Some(ConstVal::I32(a & b)),
(Operator::I32Or, [ConstVal::I32(a), ConstVal::I32(b)]) => Some(ConstVal::I32(a | b)),
(Operator::I32Xor, [ConstVal::I32(a), ConstVal::I32(b)]) => Some(ConstVal::I32(a ^ b)),
(Operator::I32Shl, [ConstVal::I32(a), ConstVal::I32(b)]) => Some(ConstVal::I32(a << b)),
(Operator::I32ShrS, [ConstVal::I32(a), ConstVal::I32(b)]) => {
Some(ConstVal::I32(((*a as i32) >> (*b as i32)) as u32))
}
(Operator::I32ShrU, [ConstVal::I32(a), ConstVal::I32(b)]) => Some(ConstVal::I32(a >> b)),
(Operator::I64Add, [ConstVal::I64(a), ConstVal::I64(b)]) => {
Some(ConstVal::I64(a.wrapping_add(*b)))
}
(Operator::I64Sub, [ConstVal::I64(a), ConstVal::I64(b)]) => {
Some(ConstVal::I64(a.wrapping_sub(*b)))
}
(Operator::I64Mul, [ConstVal::I64(a), ConstVal::I64(b)]) => {
Some(ConstVal::I64(a.wrapping_mul(*b)))
}
(Operator::I64And, [ConstVal::I64(a), ConstVal::I64(b)]) => Some(ConstVal::I64(a & b)),
(Operator::I64Or, [ConstVal::I64(a), ConstVal::I64(b)]) => Some(ConstVal::I64(a | b)),
(Operator::I64Xor, [ConstVal::I64(a), ConstVal::I64(b)]) => Some(ConstVal::I64(a ^ b)),
(Operator::I64Shl, [ConstVal::I64(a), ConstVal::I64(b)]) => Some(ConstVal::I64(a << b)),
(Operator::I64ShrS, [ConstVal::I64(a), ConstVal::I64(b)]) => {
Some(ConstVal::I64(((*a as i64) >> (*b as i64)) as u64))
}
(Operator::I64ShrU, [ConstVal::I64(a), ConstVal::I64(b)]) => Some(ConstVal::I64(a >> b)),
(Operator::I32Extend8S, [ConstVal::I32(a)]) => Some(ConstVal::I32(*a as i8 as i32 as u32)),
(Operator::I32Extend16S, [ConstVal::I32(a)]) => {
Some(ConstVal::I32(*a as i16 as i32 as u32))
}
(Operator::I64Extend8S, [ConstVal::I64(a)]) => Some(ConstVal::I64(*a as i8 as i64 as u64)),
(Operator::I64Extend16S, [ConstVal::I64(a)]) => {
Some(ConstVal::I64(*a as i16 as i64 as u64))
}
(Operator::I64Extend32S, [ConstVal::I64(a)]) => {
Some(ConstVal::I64(*a as i32 as i64 as u64))
}
_ => None,
}
}
impl GVNPass { impl GVNPass {
fn optimize(&mut self, block: Block, body: &mut FunctionBody) { fn optimize(&mut self, block: Block, body: &mut FunctionBody) {
let mut i = 0; let mut i = 0;
@ -42,13 +187,50 @@ impl GVNPass {
if value_is_pure(inst, body) { if value_is_pure(inst, body) {
let mut value = body.values[inst].clone(); let mut value = body.values[inst].clone();
value.update_uses(|val| *val = body.resolve_alias(*val)); value.update_uses(|val| *val = body.resolve_alias(*val));
if let ValueDef::Operator(op, args, ..) = &value {
let arg_values = args
.iter()
.map(|&arg| match body.values[arg] {
ValueDef::Operator(Operator::I32Const { value }, _, _) => {
ConstVal::I32(value as u32)
}
ValueDef::Operator(Operator::I64Const { value }, _, _) => {
ConstVal::I64(value as u64)
}
_ => ConstVal::None,
})
.collect::<Vec<_>>();
let const_val = const_eval(op, &arg_values[..]);
match const_val {
Some(ConstVal::I32(val)) => {
value = ValueDef::Operator(
Operator::I32Const { value: val as i32 },
vec![],
vec![Type::I32],
);
body.values[inst] = value.clone();
}
Some(ConstVal::I64(val)) => {
value = ValueDef::Operator(
Operator::I64Const { value: val as i64 },
vec![],
vec![Type::I64],
);
body.values[inst] = value.clone();
}
_ => {}
}
}
if let Some(value) = self.map.get(&value) { if let Some(value) = self.map.get(&value) {
body.set_alias(inst, *value); body.set_alias(inst, *value);
i -= 1; i -= 1;
body.blocks[block].insts.remove(i); body.blocks[block].insts.remove(i);
} else { continue;
self.map.insert(value, inst);
} }
self.map.insert(value, inst);
} }
} }
} }