This commit is contained in:
Chris Fallin 2022-11-03 00:20:58 -07:00
parent 43bdb36952
commit 2a47a77cdc
5 changed files with 139 additions and 76 deletions

View file

@ -119,6 +119,9 @@ impl<'a> Display for FunctionBodyDisplay<'a> {
ValueDef::PickOutput(val, idx, ty) => { ValueDef::PickOutput(val, idx, ty) => {
writeln!(f, "{} {} = {}.{} # {}", self.1, inst, val, idx, ty)?; writeln!(f, "{} {} = {}.{} # {}", self.1, inst, val, idx, ty)?;
} }
ValueDef::Alias(val) => {
writeln!(f, "{} {} = {}", self.1, inst, val)?;
}
_ => unreachable!(), _ => unreachable!(),
} }
} }

View file

@ -209,6 +209,8 @@ impl<'a> Module<'a> {
for func_decl in module.funcs.values_mut() { for func_decl in module.funcs.values_mut() {
if let Some(body) = func_decl.body_mut() { if let Some(body) = func_decl.body_mut() {
crate::passes::rpo::run(body); crate::passes::rpo::run(body);
let cfg = crate::cfg::CFGInfo::new(body);
crate::passes::basic_opt::gvn(body, &cfg);
crate::passes::resolve_aliases::run(body); crate::passes::resolve_aliases::run(body);
} }
} }

View file

@ -456,26 +456,26 @@ pub enum SideEffect {
All, All,
} }
pub fn op_effects(op: &Operator) -> Result<Cow<'static, [SideEffect]>> { pub fn op_effects(op: &Operator) -> Cow<'static, [SideEffect]> {
use SideEffect::*; use SideEffect::*;
match op { match op {
&Operator::Unreachable => Ok(Cow::Borrowed(&[Trap])), &Operator::Unreachable => Cow::Borrowed(&[Trap]),
&Operator::Nop => Ok(Cow::Borrowed(&[])), &Operator::Nop => Cow::Borrowed(&[]),
&Operator::Call { .. } => Ok(Cow::Borrowed(&[All])), &Operator::Call { .. } => Cow::Borrowed(&[All]),
&Operator::CallIndirect { .. } => Ok(Cow::Borrowed(&[All])), &Operator::CallIndirect { .. } => Cow::Borrowed(&[All]),
&Operator::Return => Ok(Cow::Borrowed(&[Return])), &Operator::Return => Cow::Borrowed(&[Return]),
&Operator::LocalSet { local_index, .. } => Ok(vec![WriteLocal(local_index)].into()), &Operator::LocalSet { local_index, .. } => vec![WriteLocal(local_index)].into(),
&Operator::LocalGet { local_index, .. } => Ok(vec![ReadLocal(local_index)].into()), &Operator::LocalGet { local_index, .. } => vec![ReadLocal(local_index)].into(),
&Operator::LocalTee { local_index, .. } => { &Operator::LocalTee { local_index, .. } => {
Ok(vec![ReadLocal(local_index), WriteLocal(local_index)].into()) vec![ReadLocal(local_index), WriteLocal(local_index)].into()
} }
&Operator::Select => Ok(Cow::Borrowed(&[])), &Operator::Select => Cow::Borrowed(&[]),
&Operator::TypedSelect { .. } => Ok(Cow::Borrowed(&[])), &Operator::TypedSelect { .. } => Cow::Borrowed(&[]),
&Operator::GlobalGet { global_index, .. } => Ok(vec![ReadGlobal(global_index)].into()), &Operator::GlobalGet { global_index, .. } => vec![ReadGlobal(global_index)].into(),
&Operator::GlobalSet { global_index, .. } => Ok(vec![WriteGlobal(global_index)].into()), &Operator::GlobalSet { global_index, .. } => vec![WriteGlobal(global_index)].into(),
Operator::I32Load { .. } Operator::I32Load { .. }
| Operator::I32Load8S { .. } | Operator::I32Load8S { .. }
@ -490,7 +490,7 @@ pub fn op_effects(op: &Operator) -> Result<Cow<'static, [SideEffect]>> {
| Operator::I64Load32S { .. } | Operator::I64Load32S { .. }
| Operator::I64Load32U { .. } | Operator::I64Load32U { .. }
| Operator::F32Load { .. } | Operator::F32Load { .. }
| Operator::F64Load { .. } => Ok(Cow::Borrowed(&[Trap, ReadMem])), | Operator::F64Load { .. } => Cow::Borrowed(&[Trap, ReadMem]),
Operator::I32Store { .. } Operator::I32Store { .. }
| Operator::I64Store { .. } | Operator::I64Store { .. }
@ -500,12 +500,12 @@ pub fn op_effects(op: &Operator) -> Result<Cow<'static, [SideEffect]>> {
| Operator::I32Store16 { .. } | Operator::I32Store16 { .. }
| Operator::I64Store8 { .. } | Operator::I64Store8 { .. }
| Operator::I64Store16 { .. } | Operator::I64Store16 { .. }
| Operator::I64Store32 { .. } => Ok(Cow::Borrowed(&[Trap, WriteMem])), | Operator::I64Store32 { .. } => Cow::Borrowed(&[Trap, WriteMem]),
Operator::I32Const { .. } Operator::I32Const { .. }
| Operator::I64Const { .. } | Operator::I64Const { .. }
| Operator::F32Const { .. } | Operator::F32Const { .. }
| Operator::F64Const { .. } => Ok(Cow::Borrowed(&[])), | Operator::F64Const { .. } => Cow::Borrowed(&[]),
Operator::I32Eqz Operator::I32Eqz
| Operator::I32Eq | Operator::I32Eq
@ -540,7 +540,7 @@ pub fn op_effects(op: &Operator) -> Result<Cow<'static, [SideEffect]>> {
| Operator::F64Lt | Operator::F64Lt
| Operator::F64Gt | Operator::F64Gt
| Operator::F64Le | Operator::F64Le
| Operator::F64Ge => Ok(Cow::Borrowed(&[])), | Operator::F64Ge => Cow::Borrowed(&[]),
Operator::I32Clz Operator::I32Clz
| Operator::I32Ctz | Operator::I32Ctz
@ -555,10 +555,10 @@ pub fn op_effects(op: &Operator) -> Result<Cow<'static, [SideEffect]>> {
| Operator::I32ShrS | Operator::I32ShrS
| Operator::I32ShrU | Operator::I32ShrU
| Operator::I32Rotl | Operator::I32Rotl
| Operator::I32Rotr => Ok(Cow::Borrowed(&[])), | Operator::I32Rotr => Cow::Borrowed(&[]),
Operator::I32DivS | Operator::I32DivU | Operator::I32RemS | Operator::I32RemU => { Operator::I32DivS | Operator::I32DivU | Operator::I32RemS | Operator::I32RemU => {
Ok(Cow::Borrowed(&[Trap])) Cow::Borrowed(&[Trap])
} }
Operator::I64Clz Operator::I64Clz
@ -574,10 +574,10 @@ pub fn op_effects(op: &Operator) -> Result<Cow<'static, [SideEffect]>> {
| Operator::I64ShrS | Operator::I64ShrS
| Operator::I64ShrU | Operator::I64ShrU
| Operator::I64Rotl | Operator::I64Rotl
| Operator::I64Rotr => Ok(Cow::Borrowed(&[])), | Operator::I64Rotr => Cow::Borrowed(&[]),
Operator::I64DivS | Operator::I64DivU | Operator::I64RemS | Operator::I64RemU => { Operator::I64DivS | Operator::I64DivU | Operator::I64RemS | Operator::I64RemU => {
Ok(Cow::Borrowed(&[Trap])) Cow::Borrowed(&[Trap])
} }
Operator::F32Abs Operator::F32Abs
@ -593,7 +593,7 @@ pub fn op_effects(op: &Operator) -> Result<Cow<'static, [SideEffect]>> {
| Operator::F32Div | Operator::F32Div
| Operator::F32Min | Operator::F32Min
| Operator::F32Max | Operator::F32Max
| Operator::F32Copysign => Ok(Cow::Borrowed(&[])), | Operator::F32Copysign => Cow::Borrowed(&[]),
Operator::F64Abs Operator::F64Abs
| Operator::F64Neg | Operator::F64Neg
@ -608,55 +608,59 @@ pub fn op_effects(op: &Operator) -> Result<Cow<'static, [SideEffect]>> {
| Operator::F64Div | Operator::F64Div
| Operator::F64Min | Operator::F64Min
| Operator::F64Max | Operator::F64Max
| Operator::F64Copysign => Ok(Cow::Borrowed(&[])), | Operator::F64Copysign => Cow::Borrowed(&[]),
Operator::I32WrapI64 => Ok(Cow::Borrowed(&[])), Operator::I32WrapI64 => Cow::Borrowed(&[]),
Operator::I32TruncF32S => Ok(Cow::Borrowed(&[Trap])), Operator::I32TruncF32S => Cow::Borrowed(&[Trap]),
Operator::I32TruncF32U => Ok(Cow::Borrowed(&[Trap])), Operator::I32TruncF32U => Cow::Borrowed(&[Trap]),
Operator::I32TruncF64S => Ok(Cow::Borrowed(&[Trap])), Operator::I32TruncF64S => Cow::Borrowed(&[Trap]),
Operator::I32TruncF64U => Ok(Cow::Borrowed(&[Trap])), Operator::I32TruncF64U => Cow::Borrowed(&[Trap]),
Operator::I64ExtendI32S => Ok(Cow::Borrowed(&[])), Operator::I64ExtendI32S => Cow::Borrowed(&[]),
Operator::I64ExtendI32U => Ok(Cow::Borrowed(&[])), Operator::I64ExtendI32U => Cow::Borrowed(&[]),
Operator::I64TruncF32S => Ok(Cow::Borrowed(&[Trap])), Operator::I64TruncF32S => Cow::Borrowed(&[Trap]),
Operator::I64TruncF32U => Ok(Cow::Borrowed(&[Trap])), Operator::I64TruncF32U => Cow::Borrowed(&[Trap]),
Operator::I64TruncF64S => Ok(Cow::Borrowed(&[Trap])), Operator::I64TruncF64S => Cow::Borrowed(&[Trap]),
Operator::I64TruncF64U => Ok(Cow::Borrowed(&[Trap])), Operator::I64TruncF64U => Cow::Borrowed(&[Trap]),
Operator::F32ConvertI32S => Ok(Cow::Borrowed(&[])), Operator::F32ConvertI32S => Cow::Borrowed(&[]),
Operator::F32ConvertI32U => Ok(Cow::Borrowed(&[])), Operator::F32ConvertI32U => Cow::Borrowed(&[]),
Operator::F32ConvertI64S => Ok(Cow::Borrowed(&[])), Operator::F32ConvertI64S => Cow::Borrowed(&[]),
Operator::F32ConvertI64U => Ok(Cow::Borrowed(&[])), Operator::F32ConvertI64U => Cow::Borrowed(&[]),
Operator::F32DemoteF64 => Ok(Cow::Borrowed(&[])), Operator::F32DemoteF64 => Cow::Borrowed(&[]),
Operator::F64ConvertI32S => Ok(Cow::Borrowed(&[])), Operator::F64ConvertI32S => Cow::Borrowed(&[]),
Operator::F64ConvertI32U => Ok(Cow::Borrowed(&[])), Operator::F64ConvertI32U => Cow::Borrowed(&[]),
Operator::F64ConvertI64S => Ok(Cow::Borrowed(&[])), Operator::F64ConvertI64S => Cow::Borrowed(&[]),
Operator::F64ConvertI64U => Ok(Cow::Borrowed(&[])), Operator::F64ConvertI64U => Cow::Borrowed(&[]),
Operator::F64PromoteF32 => Ok(Cow::Borrowed(&[])), Operator::F64PromoteF32 => Cow::Borrowed(&[]),
Operator::I32Extend8S => Ok(Cow::Borrowed(&[])), Operator::I32Extend8S => Cow::Borrowed(&[]),
Operator::I32Extend16S => Ok(Cow::Borrowed(&[])), Operator::I32Extend16S => Cow::Borrowed(&[]),
Operator::I64Extend8S => Ok(Cow::Borrowed(&[])), Operator::I64Extend8S => Cow::Borrowed(&[]),
Operator::I64Extend16S => Ok(Cow::Borrowed(&[])), Operator::I64Extend16S => Cow::Borrowed(&[]),
Operator::I64Extend32S => Ok(Cow::Borrowed(&[])), Operator::I64Extend32S => Cow::Borrowed(&[]),
Operator::I32TruncSatF32S => Ok(Cow::Borrowed(&[])), Operator::I32TruncSatF32S => Cow::Borrowed(&[]),
Operator::I32TruncSatF32U => Ok(Cow::Borrowed(&[])), Operator::I32TruncSatF32U => Cow::Borrowed(&[]),
Operator::I32TruncSatF64S => Ok(Cow::Borrowed(&[])), Operator::I32TruncSatF64S => Cow::Borrowed(&[]),
Operator::I32TruncSatF64U => Ok(Cow::Borrowed(&[])), Operator::I32TruncSatF64U => Cow::Borrowed(&[]),
Operator::I64TruncSatF32S => Ok(Cow::Borrowed(&[])), Operator::I64TruncSatF32S => Cow::Borrowed(&[]),
Operator::I64TruncSatF32U => Ok(Cow::Borrowed(&[])), Operator::I64TruncSatF32U => Cow::Borrowed(&[]),
Operator::I64TruncSatF64S => Ok(Cow::Borrowed(&[])), Operator::I64TruncSatF64S => Cow::Borrowed(&[]),
Operator::I64TruncSatF64U => Ok(Cow::Borrowed(&[])), Operator::I64TruncSatF64U => Cow::Borrowed(&[]),
Operator::F32ReinterpretI32 => Ok(Cow::Borrowed(&[])), Operator::F32ReinterpretI32 => Cow::Borrowed(&[]),
Operator::F64ReinterpretI64 => Ok(Cow::Borrowed(&[])), Operator::F64ReinterpretI64 => Cow::Borrowed(&[]),
Operator::I32ReinterpretF32 => Ok(Cow::Borrowed(&[])), Operator::I32ReinterpretF32 => Cow::Borrowed(&[]),
Operator::I64ReinterpretF64 => Ok(Cow::Borrowed(&[])), Operator::I64ReinterpretF64 => Cow::Borrowed(&[]),
Operator::TableGet { table_index, .. } => Ok(vec![ReadTable(*table_index), Trap].into()), Operator::TableGet { table_index, .. } => vec![ReadTable(*table_index), Trap].into(),
Operator::TableSet { table_index, .. } => Ok(vec![WriteTable(*table_index), Trap].into()), Operator::TableSet { table_index, .. } => vec![WriteTable(*table_index), Trap].into(),
Operator::TableGrow { table_index, .. } => Ok(vec![WriteTable(*table_index), Trap].into()), Operator::TableGrow { table_index, .. } => vec![WriteTable(*table_index), Trap].into(),
Operator::TableSize { table_index, .. } => Ok(vec![ReadTable(*table_index)].into()), Operator::TableSize { table_index, .. } => vec![ReadTable(*table_index)].into(),
Operator::MemorySize { .. } => Ok(Cow::Borrowed(&[ReadMem])), Operator::MemorySize { .. } => Cow::Borrowed(&[ReadMem]),
Operator::MemoryGrow { .. } => Ok(Cow::Borrowed(&[WriteMem, Trap])), Operator::MemoryGrow { .. } => Cow::Borrowed(&[WriteMem, Trap]),
} }
} }
pub fn is_pure(op: &Operator) -> bool {
op_effects(op).is_empty()
}
impl std::fmt::Display for Operator { impl std::fmt::Display for Operator {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self { match self {

View file

@ -1,10 +1,55 @@
//! Basic optimizations: GVN and constant-propagation/folding. //! Basic optimizations: GVN and constant-propagation/folding.
use crate::ir::*;
use crate::passes::dom_pass;
use crate::scoped_map::ScopedMap;
use crate::cfg::CFGInfo; use crate::cfg::CFGInfo;
use crate::ir::*;
use crate::op_traits::is_pure;
use crate::passes::dom_pass::{dom_pass, DomtreePass};
use crate::scoped_map::ScopedMap;
pub fn gvn(body: &mut FunctionBody, cfg: &CFGInfo) { pub fn gvn(body: &mut FunctionBody, cfg: &CFGInfo) {
let mut map: ScopedMap<ValueDef, Value> = ScopedMap::new(); dom_pass::<GVNPass>(body, cfg, &mut GVNPass::default());
}
#[derive(Clone, Debug, Default)]
struct GVNPass {
map: ScopedMap<ValueDef, Value>,
}
impl DomtreePass for GVNPass {
fn enter(&mut self, block: Block, body: &mut FunctionBody) {
self.map.push_level();
self.optimize(block, body);
}
fn leave(&mut self, _block: Block, _body: &mut FunctionBody) {
self.map.pop_level();
}
}
fn value_is_pure(value: Value, body: &FunctionBody) -> bool {
match body.values[value] {
ValueDef::Operator(op, ..) if is_pure(&op) => true,
_ => false,
}
}
impl GVNPass {
fn optimize(&mut self, block: Block, body: &mut FunctionBody) {
let mut i = 0;
while i < body.blocks[block].insts.len() {
let inst = body.blocks[block].insts[i];
i += 1;
if value_is_pure(inst, body) {
let mut value = body.values[inst].clone();
value.update_uses(|val| *val = body.resolve_alias(*val));
if let Some(value) = self.map.get(&value) {
body.set_alias(inst, *value);
i -= 1;
body.blocks[block].insts.remove(i);
} else {
self.map.insert(value, inst);
}
}
}
}
} }

View file

@ -1,21 +1,30 @@
//! Scoped hashmap. //! Scoped hashmap.
use fxhash::FxHashMap; use fxhash::FxHashMap;
use std::fmt::Debug;
use std::hash::Hash; use std::hash::Hash;
pub struct ScopedMap<K: Hash + Eq, V> { #[derive(Clone, Debug)]
pub struct ScopedMap<K: Hash + Eq + Clone + Debug, V: Clone + Debug> {
map: FxHashMap<K, ScopedMapEntry<V>>, map: FxHashMap<K, ScopedMapEntry<V>>,
gen: u32, gen: u32,
gen_by_level: Vec<u32>, gen_by_level: Vec<u32>,
} }
struct ScopedMapEntry<V> { impl<K: Hash + Eq + Clone + Debug, V: Clone + Debug> std::default::Default for ScopedMap<K, V> {
fn default() -> Self {
ScopedMap::new()
}
}
#[derive(Clone, Debug)]
struct ScopedMapEntry<V: Clone + Debug> {
gen: u32, gen: u32,
level: u32, level: u32,
value: V, value: V,
} }
impl<K: Hash + Eq, V> ScopedMap<K, V> { impl<K: Hash + Eq + Clone + Debug, V: Clone + Debug> ScopedMap<K, V> {
pub fn new() -> ScopedMap<K, V> { pub fn new() -> ScopedMap<K, V> {
ScopedMap { ScopedMap {
map: FxHashMap::default(), map: FxHashMap::default(),
@ -37,8 +46,8 @@ impl<K: Hash + Eq, V> ScopedMap<K, V> {
self.map.insert( self.map.insert(
k, k,
ScopedMapEntry { ScopedMapEntry {
gen: self.gen, gen: *self.gen_by_level.last().unwrap(),
level: self.gen_by_level.len() as u32, level: (self.gen_by_level.len() - 1) as u32,
value: v, value: v,
}, },
); );