From dab18103ce66a5993180a293d60eb5d53fab52a9 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Tue, 18 Apr 2023 17:31:17 -0700 Subject: [PATCH] Refactor to store args and types in common list pool. --- fuzz/fuzz_targets/opt_diff.rs | 2 +- src/backend/localify.rs | 6 +- src/backend/mod.rs | 8 +- src/backend/treeify.rs | 6 +- src/frontend.rs | 36 +++++---- src/interp.rs | 22 +++--- src/ir/display.rs | 139 ++++++++++++++++++++-------------- src/ir/func.rs | 43 +++++++++-- src/ir/module.rs | 2 +- src/ir/value.rs | 39 +++++----- src/lib.rs | 1 + src/ops.rs | 5 ++ src/passes/basic_opt.rs | 35 ++++++--- src/passes/maxssa.rs | 14 ++-- src/passes/remove_phis.rs | 4 +- src/passes/resolve_aliases.rs | 8 +- src/passes/ssa.rs | 11 +-- src/passes/trace.rs | 6 +- src/pool.rs | 64 ++++++++++++++++ 19 files changed, 296 insertions(+), 155 deletions(-) create mode 100644 src/pool.rs diff --git a/fuzz/fuzz_targets/opt_diff.rs b/fuzz/fuzz_targets/opt_diff.rs index 6610053..cee3e11 100644 --- a/fuzz/fuzz_targets/opt_diff.rs +++ b/fuzz/fuzz_targets/opt_diff.rs @@ -38,7 +38,7 @@ fuzz_target!( log::trace!("Rejecting due to timeout in orig"); return; } - InterpResult::Trap => { + InterpResult::Trap(..) => { // Silently reject. log::trace!("Rejecting due to trap in orig"); return; diff --git a/src/backend/localify.rs b/src/backend/localify.rs index 8b1008a..82e9122 100644 --- a/src/backend/localify.rs +++ b/src/backend/localify.rs @@ -90,13 +90,13 @@ impl<'a, V: Visitor> BlockVisitor<'a, V> { } fn visit_inst(&mut self, value: Value, root: bool) { // If this is an instruction... - if let ValueDef::Operator(_, ref args, _) = &self.body.values[value] { + if let ValueDef::Operator(_, args, _) = &self.body.values[value] { // If root, we need to process the def. if root { self.visitor.visit_def(value); } // Handle uses. - for &arg in args { + for &arg in &self.body.arg_pool[*args] { self.visit_use(arg); } } @@ -284,7 +284,7 @@ impl<'a> Context<'a> { // allocate a new one. let mut allocs = smallvec![]; let expiring = expiring.entry(range.end).or_insert_with(|| smallvec![]); - for &ty in self.body.values[value].tys() { + for &ty in self.body.values[value].tys(&self.body.type_pool) { let local = freelist .get_mut(&ty) .and_then(|v| v.pop()) diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 994b692..5046953 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -185,7 +185,9 @@ impl<'a> WasmFuncBackend<'a> { } else { let local = match &self.body.values[value] { &ValueDef::BlockParam(..) | &ValueDef::Operator(..) => self.locals.values[value][0], - &ValueDef::PickOutput(orig_value, idx, _) => self.locals.values[orig_value][idx], + &ValueDef::PickOutput(orig_value, idx, _) => { + self.locals.values[orig_value][idx as usize] + } _ => unreachable!(), }; func.instruction(&wasm_encoder::Instruction::LocalGet(local.index() as u32)); @@ -206,8 +208,8 @@ impl<'a> WasmFuncBackend<'a> { fn lower_inst(&self, value: Value, root: bool, func: &mut wasm_encoder::Function) { log::trace!("lower_inst: value {} root {}", value, root); match &self.body.values[value] { - &ValueDef::Operator(ref op, ref args, ref tys) => { - for &arg in &args[..] { + &ValueDef::Operator(ref op, args, tys) => { + for &arg in &self.body.arg_pool[args] { let arg = self.body.resolve_alias(arg); if self.trees.owner.contains_key(&arg) || self.trees.remat.contains(&arg) { log::trace!(" -> arg {} is owned", arg); diff --git a/src/backend/treeify.rs b/src/backend/treeify.rs index 550bc8b..ab87295 100644 --- a/src/backend/treeify.rs +++ b/src/backend/treeify.rs @@ -43,10 +43,10 @@ impl Trees { for (value, def) in body.values.entries() { match def { - &ValueDef::Operator(op, ref args, _) => { + &ValueDef::Operator(op, args, _) => { // Ignore operators with invalid args: these must // always be unreachable. - if args.iter().any(|arg| arg.is_invalid()) { + if body.arg_pool[args].iter().any(|arg| arg.is_invalid()) { continue; } // If this is an always-rematerialized operator, @@ -62,7 +62,7 @@ impl Trees { // in the arg slot. Otherwise if owned already // somewhere else, undo that and put in // `multi_use`. - for (i, &arg) in args.iter().enumerate() { + for (i, &arg) in body.arg_pool[args].iter().enumerate() { let arg = body.resolve_alias(arg); if multi_use.contains(&arg) { continue; diff --git a/src/frontend.rs b/src/frontend.rs index a0e1ce2..4b66d7a 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -7,6 +7,7 @@ use crate::errors::FrontendError; use crate::ir::*; use crate::op_traits::{op_inputs, op_outputs}; use crate::ops::Operator; +use crate::pool::ListRef; use addr2line::gimli; use anyhow::{bail, Result}; use fxhash::{FxHashMap, FxHashSet}; @@ -656,26 +657,27 @@ impl LocalTracker { ty: Type, at_block: Block, ) -> Value { + let types = body.single_type_list(ty); let val = match ty { Type::I32 => body.add_value(ValueDef::Operator( Operator::I32Const { value: 0 }, - vec![], - vec![ty], + ListRef::default(), + types, )), Type::I64 => body.add_value(ValueDef::Operator( Operator::I64Const { value: 0 }, - vec![], - vec![ty], + ListRef::default(), + types, )), Type::F32 => body.add_value(ValueDef::Operator( Operator::F32Const { value: 0 }, - vec![], - vec![ty], + ListRef::default(), + types, )), Type::F64 => body.add_value(ValueDef::Operator( Operator::F64Const { value: 0 }, - vec![], - vec![ty], + ListRef::default(), + types, )), _ => todo!("unsupported type: {:?}", ty), }; @@ -1684,19 +1686,25 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> { let n_outputs = outputs.len(); - let mut input_operands = vec![]; - for &input in inputs.into_iter().rev() { + let input_operands = self.body.arg_pool.allocate(inputs.len(), Value::invalid()); + let args = &mut self.body.arg_pool[input_operands]; + for (i, &input) in inputs.into_iter().enumerate().rev() { let (stack_top_ty, stack_top) = self.op_stack.pop().unwrap(); assert_eq!(stack_top_ty, input); - input_operands.push(stack_top); + args[i] = stack_top; } - input_operands.reverse(); log::trace!(" -> operands: {:?}", input_operands); log::trace!(" -> ty {:?}", outputs); + let outputs_list = if n_outputs == 1 { + self.body.single_type_list(outputs[0]) + } else { + self.body.type_pool.from_iter(outputs.iter().cloned()) + }; + let value = self .body - .add_value(ValueDef::Operator(op, input_operands, outputs.to_vec())); + .add_value(ValueDef::Operator(op, input_operands, outputs_list)); log::trace!(" -> value: {:?}", value); if self.reachable { @@ -1711,7 +1719,7 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> { for (i, &output_ty) in outputs.into_iter().enumerate() { let pick = self .body - .add_value(ValueDef::PickOutput(value, i, output_ty)); + .add_value(ValueDef::PickOutput(value, i as u32, output_ty)); if self.reachable { self.body.append_to_block(self.cur_block, pick); } diff --git a/src/interp.rs b/src/interp.rs index 5c68687..b6657e1 100644 --- a/src/interp.rs +++ b/src/interp.rs @@ -133,10 +133,10 @@ impl InterpContext { &ValueDef::Alias(_) => smallvec![], &ValueDef::PickOutput(val, idx, _) => { let val = body.resolve_alias(val); - smallvec![frame.values.get(&val).unwrap()[idx]] + smallvec![frame.values.get(&val).unwrap()[idx as usize]] } - &ValueDef::Operator(Operator::Call { function_index }, ref args, _) => { - let args = args + &ValueDef::Operator(Operator::Call { function_index }, args, _) => { + let args = body.arg_pool[args] .iter() .map(|&arg| { let arg = body.resolve_alias(arg); @@ -151,12 +151,8 @@ impl InterpContext { _ => return result, } } - &ValueDef::Operator( - Operator::CallIndirect { table_index, .. }, - ref args, - _, - ) => { - let args = args + &ValueDef::Operator(Operator::CallIndirect { table_index, .. }, args, _) => { + let args = body.arg_pool[args] .iter() .map(|&arg| { let arg = body.resolve_alias(arg); @@ -173,8 +169,8 @@ impl InterpContext { _ => return result, } } - &ValueDef::Operator(ref op, ref args, _) => { - let args = args + &ValueDef::Operator(ref op, args, _) => { + let args = body.arg_pool[args] .iter() .map(|&arg| { let arg = body.resolve_alias(arg); @@ -200,9 +196,9 @@ impl InterpContext { }; smallvec![result] } - &ValueDef::Trace(id, ref args) => { + &ValueDef::Trace(id, args) => { if let Some(handler) = self.trace_handler.as_ref() { - let args = args + let args = body.arg_pool[args] .iter() .map(|&arg| { let arg = body.resolve_alias(arg); diff --git a/src/ir/display.rs b/src/ir/display.rs index af3e24f..42178ae 100644 --- a/src/ir/display.rs +++ b/src/ir/display.rs @@ -5,24 +5,24 @@ use crate::entity::EntityRef; use std::collections::HashMap; use std::fmt::{Display, Formatter, Result as FmtResult}; -pub struct FunctionBodyDisplay<'a>( - pub(crate) &'a FunctionBody, - pub(crate) &'a str, - pub(crate) bool, - pub(crate) Option<&'a Module<'a>>, -); +pub struct FunctionBodyDisplay<'a> { + pub(crate) body: &'a FunctionBody, + pub(crate) indent: &'a str, + pub(crate) verbose: bool, + pub(crate) module: Option<&'a Module<'a>>, +} impl<'a> Display for FunctionBodyDisplay<'a> { fn fmt(&self, f: &mut Formatter) -> FmtResult { let arg_tys = self - .0 + .body .locals .values() - .take(self.0.n_params) + .take(self.body.n_params) .map(|&ty| format!("{}", ty)) .collect::>(); let ret_tys = self - .0 + .body .rets .iter() .map(|&ty| format!("{}", ty)) @@ -30,51 +30,63 @@ impl<'a> Display for FunctionBodyDisplay<'a> { writeln!( f, "{}function({}) -> {} {{", - self.1, + self.indent, arg_tys.join(", "), ret_tys.join(", ") )?; - let verbose = self.2; - for (value, value_def) in self.0.values.entries() { + for (value, value_def) in self.body.values.entries() { match value_def { - ValueDef::Operator(op, args, tys) if verbose => writeln!( + ValueDef::Operator(op, args, tys) if self.verbose => writeln!( f, "{} {} = {} {} # {}", - self.1, + self.indent, value, op, - args.iter() + self.body.arg_pool[*args] + .iter() .map(|arg| format!("{}", arg)) .collect::>() .join(", "), - tys.iter() + self.body.type_pool[*tys] + .iter() .map(|arg| format!("{}", arg)) .collect::>() .join(", ") )?, - ValueDef::BlockParam(block, idx, ty) if verbose => writeln!( + ValueDef::BlockParam(block, idx, ty) if self.verbose => writeln!( f, "{} {} = blockparam {}, {} # {}", - self.1, value, block, idx, ty + self.indent, value, block, idx, ty )?, ValueDef::Alias(alias_target) => { - if verbose { - writeln!(f, "{} {} = {}", self.1, value, alias_target)? + if self.verbose { + writeln!(f, "{} {} = {}", self.indent, value, alias_target)? } } ValueDef::PickOutput(val, idx, ty) => { - writeln!(f, "{} {} = {}.{} # {}", self.1, value, val, idx, ty)? + writeln!(f, "{} {} = {}.{} # {}", self.indent, value, val, idx, ty)? } ValueDef::Placeholder(ty) => { - writeln!(f, "{} {} = placeholder # {}", self.1, value, ty)? + writeln!(f, "{} {} = placeholder # {}", self.indent, value, ty)? } + ValueDef::Trace(id, args) => writeln!( + f, + "{} trace {}, {}", + self.indent, + id, + self.body.arg_pool[*args] + .iter() + .map(|arg| format!("{}", arg)) + .collect::>() + .join(", ") + )?, ValueDef::None => panic!(), _ => {} } } - for (block_id, block) in self.0.blocks.entries() { + for (block_id, block) in self.body.blocks.entries() { let block_params = block .params .iter() @@ -83,7 +95,7 @@ impl<'a> Display for FunctionBodyDisplay<'a> { writeln!( f, "{} {}({}): # {}", - self.1, + self.indent, block_id, block_params.join(", "), block.desc @@ -91,43 +103,49 @@ impl<'a> Display for FunctionBodyDisplay<'a> { writeln!( f, "{} # preds: {}", - self.1, + self.indent, block .preds .iter() - .map(|pred| format!("{} ({})", pred, self.0.blocks[*pred].desc)) + .map(|pred| format!("{} ({})", pred, self.body.blocks[*pred].desc)) .collect::>() .join(", ") )?; writeln!( f, "{} # succs: {}", - self.1, + self.indent, block .succs .iter() - .map(|succ| format!("{} ({})", succ, self.0.blocks[*succ].desc)) + .map(|succ| format!("{} ({})", succ, self.body.blocks[*succ].desc)) .collect::>() .join(", ") )?; for (_, param) in &block.params { - if let Some(local) = self.0.value_locals[*param] { - writeln!(f, "{} # {}: {}", self.1, param, local)?; + if let Some(local) = self.body.value_locals[*param] { + writeln!(f, "{} # {}: {}", self.indent, param, local)?; } } for &inst in &block.insts { - if let Some(local) = self.0.value_locals[inst] { - writeln!(f, "{} # {}: {}", self.1, inst, local)?; + if let Some(local) = self.body.value_locals[inst] { + writeln!(f, "{} # {}: {}", self.indent, inst, local)?; } - match &self.0.values[inst] { + match &self.body.values[inst] { ValueDef::Operator(op, args, tys) => { - let args = args.iter().map(|&v| format!("{}", v)).collect::>(); - let tys = tys.iter().map(|&ty| format!("{}", ty)).collect::>(); - let loc = if self.0.source_locs[inst] != SourceLoc::invalid() - && self.3.is_some() + let args = self.body.arg_pool[*args] + .iter() + .map(|&v| format!("{}", v)) + .collect::>(); + let tys = self.body.type_pool[*tys] + .iter() + .map(|&ty| format!("{}", ty)) + .collect::>(); + let loc = if self.body.source_locs[inst] != SourceLoc::invalid() + && self.module.is_some() { - let module = self.3.as_ref().unwrap(); - let loc = self.0.source_locs[inst]; + let module = self.module.as_ref().unwrap(); + let loc = self.body.source_locs[inst]; let data = &module.debug.source_locs[loc]; let filename = &module.debug.source_files[data.file]; format!("@{} {}:{}:{}", loc, filename, data.line, data.col) @@ -137,7 +155,7 @@ impl<'a> Display for FunctionBodyDisplay<'a> { writeln!( f, "{} {} = {} {} # {} {}", - self.1, + self.indent, inst, op, args.join(", "), @@ -146,37 +164,42 @@ impl<'a> Display for FunctionBodyDisplay<'a> { )?; } ValueDef::PickOutput(val, idx, ty) => { - writeln!(f, "{} {} = {}.{} # {}", self.1, inst, val, idx, ty)?; + writeln!(f, "{} {} = {}.{} # {}", self.indent, inst, val, idx, ty)?; } ValueDef::Alias(val) => { - writeln!(f, "{} {} = {}", self.1, inst, val)?; + writeln!(f, "{} {} = {}", self.indent, inst, val)?; } ValueDef::Trace(id, args) => { - let args = args.iter().map(|&v| format!("{}", v)).collect::>(); - writeln!(f, "{} trace {}, {}", self.1, id, args.join(", "))?; + let args = self.body.arg_pool[*args] + .iter() + .map(|&v| format!("{}", v)) + .collect::>(); + writeln!(f, "{} trace {}, {}", self.indent, id, args.join(", "))?; } _ => unreachable!(), } } - writeln!(f, "{} {}", self.1, block.terminator)?; + writeln!(f, "{} {}", self.indent, block.terminator)?; } - writeln!(f, "{}}}", self.1)?; + writeln!(f, "{}}}", self.indent)?; Ok(()) } } -pub struct ModuleDisplay<'a>(pub(crate) &'a Module<'a>); +pub struct ModuleDisplay<'a> { + pub(crate) module: &'a Module<'a>, +} impl<'a> Display for ModuleDisplay<'a> { fn fmt(&self, f: &mut Formatter) -> FmtResult { writeln!(f, "module {{")?; - if let Some(func) = self.0.start_func { + if let Some(func) = self.module.start_func { writeln!(f, " start = {}", func)?; } let mut sig_strs = HashMap::new(); - for (sig, sig_data) in self.0.signatures.entries() { + for (sig, sig_data) in self.module.signatures.entries() { let arg_tys = sig_data .params .iter() @@ -191,14 +214,14 @@ impl<'a> Display for ModuleDisplay<'a> { sig_strs.insert(sig, sig_str.clone()); writeln!(f, " {}: {}", sig, sig_str)?; } - for (global, global_data) in self.0.globals.entries() { + for (global, global_data) in self.module.globals.entries() { writeln!( f, " {}: {:?} # {}", global, global_data.value, global_data.ty )?; } - for (table, table_data) in self.0.tables.entries() { + for (table, table_data) in self.module.tables.entries() { writeln!(f, " {}: {}", table, table_data.ty)?; if let Some(funcs) = &table_data.func_elements { for (i, &func) in funcs.iter().enumerate() { @@ -206,7 +229,7 @@ impl<'a> Display for ModuleDisplay<'a> { } } } - for (memory, memory_data) in self.0.memories.entries() { + for (memory, memory_data) in self.module.memories.entries() { writeln!( f, " {}: initial {} max {:?}", @@ -222,17 +245,17 @@ impl<'a> Display for ModuleDisplay<'a> { )?; } } - for import in &self.0.imports { + for import in &self.module.imports { writeln!( f, " import \"{}\".\"{}\": {}", import.module, import.name, import.kind )?; } - for export in &self.0.exports { + for export in &self.module.exports { writeln!(f, " export \"{}\": {}", export.name, export.kind)?; } - for (func, func_decl) in self.0.funcs.entries() { + for (func, func_decl) in self.module.funcs.entries() { match func_decl { FuncDecl::Body(sig, name, body) => { writeln!( @@ -243,7 +266,7 @@ impl<'a> Display for ModuleDisplay<'a> { sig, sig_strs.get(&sig).unwrap() )?; - writeln!(f, "{}", body.display(" ", Some(self.0)))?; + writeln!(f, "{}", body.display(" ", Some(self.module)))?; } FuncDecl::Lazy(sig, name, reader) => { writeln!( @@ -282,14 +305,14 @@ impl<'a> Display for ModuleDisplay<'a> { } } } - for (loc, loc_data) in self.0.debug.source_locs.entries() { + for (loc, loc_data) in self.module.debug.source_locs.entries() { writeln!( f, " {} = {} line {} column {}", loc, loc_data.file, loc_data.line, loc_data.col )?; } - for (file, file_name) in self.0.debug.source_files.entries() { + for (file, file_name) in self.module.debug.source_files.entries() { writeln!(f, " {} = \"{}\"", file, file_name)?; } writeln!(f, "}}")?; diff --git a/src/ir/func.rs b/src/ir/func.rs index acecf9e..32117e0 100644 --- a/src/ir/func.rs +++ b/src/ir/func.rs @@ -4,7 +4,9 @@ use crate::cfg::CFGInfo; use crate::entity::{EntityRef, EntityVec, PerEntity}; use crate::frontend::parse_body; use crate::ir::SourceLoc; +use crate::pool::{ListPool, ListRef}; use anyhow::Result; +use fxhash::FxHashMap; use std::collections::HashSet; /// A declaration of a function: there is one `FuncDecl` per `Func` @@ -124,6 +126,12 @@ pub struct FunctionBody { pub blocks: EntityVec, /// Value definitions, indexed by `Value`. pub values: EntityVec, + /// Pool of types for ValueDefs' result type lists. + pub type_pool: ListPool, + /// Deduplication for type-lists of single types. + pub single_type_dedup: FxHashMap>, + /// Pool of values for ValueDefs' arg lists. + pub arg_pool: ListPool, /// Blocks in which values are computed. Each may be `Block::invalid()` if not placed. pub value_blocks: PerEntity, /// Wasm locals that values correspond to, if any. @@ -142,7 +150,7 @@ impl FunctionBody { let mut values = EntityVec::default(); let mut value_blocks = PerEntity::default(); for (i, &arg_ty) in locals.values().enumerate() { - let value = values.push(ValueDef::BlockParam(entry, i, arg_ty)); + let value = values.push(ValueDef::BlockParam(entry, i as u32, arg_ty)); blocks[entry].params.push((arg_ty, value)); value_blocks[value] = entry; } @@ -153,6 +161,9 @@ impl FunctionBody { entry, blocks, values, + type_pool: ListPool::default(), + arg_pool: ListPool::default(), + single_type_dedup: FxHashMap::default(), value_blocks, value_locals: PerEntity::default(), source_locs: PerEntity::default(), @@ -178,6 +189,14 @@ impl FunctionBody { id } + pub fn single_type_list(&mut self, ty: Type) -> ListRef { + let type_pool = &mut self.type_pool; + *self + .single_type_dedup + .entry(ty) + .or_insert_with(|| type_pool.single(ty)) + } + pub fn add_edge(&mut self, from: Block, to: Block) { let succ_pos = self.blocks[from].succs.len(); let pred_pos = self.blocks[to].preds.len(); @@ -284,7 +303,7 @@ impl FunctionBody { pub fn add_blockparam(&mut self, block: Block, ty: Type) -> Value { let index = self.blocks[block].params.len(); - let value = self.add_value(ValueDef::BlockParam(block, index, ty)); + let value = self.add_value(ValueDef::BlockParam(block, index as u32, ty)); self.blocks[block].params.push((ty, value)); self.value_blocks[value] = block; value @@ -301,7 +320,7 @@ impl FunctionBody { _ => unreachable!(), }; self.blocks[block].params.push((ty, value)); - self.values[value] = ValueDef::BlockParam(block, index, ty); + self.values[value] = ValueDef::BlockParam(block, index as u32, ty); } pub fn mark_value_as_local(&mut self, value: Value, local: Local) { @@ -342,7 +361,12 @@ impl FunctionBody { indent: &'a str, module: Option<&'a Module>, ) -> FunctionBodyDisplay<'a> { - FunctionBodyDisplay(self, indent, /* verbose = */ false, module) + FunctionBodyDisplay { + body: self, + indent, + verbose: false, + module, + } } pub fn display_verbose<'a>( @@ -350,7 +374,12 @@ impl FunctionBody { indent: &'a str, module: Option<&'a Module>, ) -> FunctionBodyDisplay<'a> { - FunctionBodyDisplay(self, indent, /* verbose = */ true, module) + FunctionBodyDisplay { + body: self, + indent, + verbose: true, + module, + } } pub fn validate(&self) -> anyhow::Result<()> { @@ -419,8 +448,8 @@ impl FunctionBody { for (i, &inst) in block_def.insts.iter().enumerate() { match &self.values[inst] { - &ValueDef::Operator(_, ref args, _) => { - for &arg in args { + &ValueDef::Operator(_, args, _) => { + for &arg in &self.arg_pool[args] { visit_use(arg, Some(i), Some(inst)); } } diff --git a/src/ir/module.rs b/src/ir/module.rs index 3e1b014..05795db 100644 --- a/src/ir/module.rs +++ b/src/ir/module.rs @@ -236,6 +236,6 @@ impl<'a> Module<'a> { where 'b: 'a, { - ModuleDisplay(self) + ModuleDisplay { module: self } } } diff --git a/src/ir/value.rs b/src/ir/value.rs index 0a88431..67072fe 100644 --- a/src/ir/value.rs +++ b/src/ir/value.rs @@ -1,24 +1,25 @@ use super::{Block, Type, Value}; +use crate::pool::{ListPool, ListRef}; use crate::Operator; #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] pub enum ValueDef { - BlockParam(Block, usize, Type), - Operator(Operator, Vec, Vec), - PickOutput(Value, usize, Type), + BlockParam(Block, u32, Type), + Operator(Operator, ListRef, ListRef), + PickOutput(Value, u32, Type), Alias(Value), Placeholder(Type), - Trace(usize, Vec), + Trace(usize, ListRef), #[default] None, } impl ValueDef { - pub fn ty(&self) -> Option { + pub fn ty(&self, types: &ListPool) -> Option { match self { &ValueDef::BlockParam(_, _, ty) => Some(ty), - &ValueDef::Operator(_, _, ref tys) if tys.len() == 0 => None, - &ValueDef::Operator(_, _, ref tys) if tys.len() == 1 => Some(tys[0]), + &ValueDef::Operator(_, _, tys) if tys.len() == 0 => None, + &ValueDef::Operator(_, _, tys) if tys.len() == 1 => Some(types[tys][0]), &ValueDef::PickOutput(_, _, ty) => Some(ty), &ValueDef::Placeholder(ty) => Some(ty), &ValueDef::Trace(_, _) => None, @@ -26,9 +27,9 @@ impl ValueDef { } } - pub fn tys(&self) -> &[Type] { + pub fn tys<'a>(&'a self, types: &'a ListPool) -> &'a [Type] { match self { - &ValueDef::Operator(_, _, ref tys) => &tys[..], + &ValueDef::Operator(_, _, tys) => &types[tys], &ValueDef::BlockParam(_, _, ref ty) | &ValueDef::PickOutput(_, _, ref ty) | &ValueDef::Placeholder(ref ty) => std::slice::from_ref(ty), @@ -36,19 +37,19 @@ impl ValueDef { } } - pub fn visit_uses(&self, mut f: F) { + pub fn visit_uses(&self, arg_pool: &ListPool, mut f: F) { match self { &ValueDef::BlockParam { .. } => {} - &ValueDef::Operator(_, ref args, _) => { - for &arg in args { + &ValueDef::Operator(_, args, _) => { + for &arg in &arg_pool[args] { f(arg); } } &ValueDef::PickOutput(from, ..) => f(from), &ValueDef::Alias(value) => f(value), &ValueDef::Placeholder(_) => {} - &ValueDef::Trace(_, ref args) => { - for &arg in args { + &ValueDef::Trace(_, args) => { + for &arg in &arg_pool[args] { f(arg); } } @@ -56,19 +57,19 @@ impl ValueDef { } } - pub fn update_uses(&mut self, mut f: F) { + pub fn update_uses(&mut self, arg_pool: &mut ListPool, mut f: F) { match self { &mut ValueDef::BlockParam { .. } => {} - &mut ValueDef::Operator(_, ref mut args, _) => { - for arg in args { + &mut ValueDef::Operator(_, args, _) => { + for arg in &mut arg_pool[args] { f(arg); } } &mut ValueDef::PickOutput(ref mut from, ..) => f(from), &mut ValueDef::Alias(ref mut value) => f(value), &mut ValueDef::Placeholder(_) => {} - &mut ValueDef::Trace(_, ref mut args) => { - for arg in args { + &mut ValueDef::Trace(_, args) => { + for arg in &mut arg_pool[args] { f(arg); } } diff --git a/src/lib.rs b/src/lib.rs index 13623f1..a5b038f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,7 @@ mod ir; mod op_traits; mod ops; pub mod passes; +pub mod pool; mod scoped_map; pub use errors::*; diff --git a/src/ops.rs b/src/ops.rs index 295380d..ea7851d 100644 --- a/src/ops.rs +++ b/src/ops.rs @@ -297,6 +297,11 @@ pub enum Operator { }, } +#[test] +fn op_size() { + assert_eq!(std::mem::size_of::(), 16); +} + impl<'a, 'b> std::convert::TryFrom<&'b wasmparser::Operator<'a>> for Operator { type Error = (); diff --git a/src/passes/basic_opt.rs b/src/passes/basic_opt.rs index 54433ac..78fe981 100644 --- a/src/passes/basic_opt.rs +++ b/src/passes/basic_opt.rs @@ -4,6 +4,7 @@ use crate::cfg::CFGInfo; use crate::interp::{const_eval, ConstVal}; use crate::ir::*; use crate::passes::dom_pass::{dom_pass, DomtreePass}; +use crate::pool::ListRef; use crate::scoped_map::ScopedMap; use crate::Operator; @@ -48,10 +49,24 @@ impl GVNPass { i += 1; if value_is_pure(inst, body) { let mut value = body.values[inst].clone(); - value.update_uses(|val| *val = body.resolve_and_update_alias(*val)); + + match &mut value { + &mut ValueDef::Operator(_, args, _) | &mut ValueDef::Trace(_, args) => { + for i in 0..args.len() { + let val = body.arg_pool[args][i]; + let val = body.resolve_and_update_alias(val); + body.arg_pool[args][i] = val; + } + } + &mut ValueDef::PickOutput(ref mut val, ..) => { + let updated = body.resolve_and_update_alias(*val); + *val = updated; + } + _ => {} + } if let ValueDef::Operator(op, args, ..) = &value { - let arg_values = args + let arg_values = body.arg_pool[*args] .iter() .map(|&arg| match body.values[arg] { ValueDef::Operator(Operator::I32Const { value }, _, _) => { @@ -74,32 +89,32 @@ impl GVNPass { Some(ConstVal::I32(val)) => { value = ValueDef::Operator( Operator::I32Const { value: val }, - vec![], - vec![Type::I32], + ListRef::default(), + body.single_type_list(Type::I32), ); body.values[inst] = value.clone(); } Some(ConstVal::I64(val)) => { value = ValueDef::Operator( Operator::I64Const { value: val }, - vec![], - vec![Type::I64], + ListRef::default(), + body.single_type_list(Type::I64), ); body.values[inst] = value.clone(); } Some(ConstVal::F32(val)) => { value = ValueDef::Operator( Operator::F32Const { value: val }, - vec![], - vec![Type::F32], + ListRef::default(), + body.single_type_list(Type::F32), ); body.values[inst] = value.clone(); } Some(ConstVal::F64(val)) => { value = ValueDef::Operator( Operator::F64Const { value: val }, - vec![], - vec![Type::F64], + ListRef::default(), + body.single_type_list(Type::F64), ); body.values[inst] = value.clone(); } diff --git a/src/passes/maxssa.rs b/src/passes/maxssa.rs index bed6caf..80d60dd 100644 --- a/src/passes/maxssa.rs +++ b/src/passes/maxssa.rs @@ -48,8 +48,8 @@ impl MaxSSAPass { let mut uses = BTreeSet::default(); for &inst in &body.blocks[block].insts { match &body.values[inst] { - &ValueDef::Operator(_, ref args, _) => { - for &arg in args { + &ValueDef::Operator(_, args, _) | &ValueDef::Trace(_, args) => { + for &arg in &body.arg_pool[args] { let arg = body.resolve_alias(arg); uses.insert(arg); } @@ -81,7 +81,7 @@ impl MaxSSAPass { self.new_args[block].push(value); // Create a placeholder value. - let ty = body.values[value].ty().unwrap(); + let ty = body.values[value].ty(&body.type_pool).unwrap(); let blockparam = body.add_blockparam(block, ty); self.value_map.insert((block, value), blockparam); @@ -147,9 +147,11 @@ impl MaxSSAPass { let inst = body.blocks[block].insts[i]; let mut def = std::mem::take(&mut body.values[inst]); match &mut def { - ValueDef::Operator(_, args, _) => { - for arg in args { - *arg = resolve(body, *arg); + ValueDef::Operator(_, args, _) | ValueDef::Trace(_, args) => { + for i in 0..args.len() { + let val = body.arg_pool[*args][i]; + let val = resolve(body, val); + body.arg_pool[*args][i] = val; } } ValueDef::PickOutput(value, ..) => { diff --git a/src/passes/remove_phis.rs b/src/passes/remove_phis.rs index 9a5d692..65e679b 100644 --- a/src/passes/remove_phis.rs +++ b/src/passes/remove_phis.rs @@ -99,8 +99,8 @@ pub fn run(func: &mut FunctionBody, cfg: &CFGInfo) { // Renumber blockparam values. for (i, &(_, param)) in func.blocks[block].params.iter().enumerate() { - let ty = func.values[param].ty().unwrap(); - func.values[param] = ValueDef::BlockParam(block, i, ty); + let ty = func.values[param].ty(&func.type_pool).unwrap(); + func.values[param] = ValueDef::BlockParam(block, i as u32, ty); } } } diff --git a/src/passes/resolve_aliases.rs b/src/passes/resolve_aliases.rs index 5aa64c3..1501d34 100644 --- a/src/passes/resolve_aliases.rs +++ b/src/passes/resolve_aliases.rs @@ -10,9 +10,11 @@ pub fn run(body: &mut FunctionBody) { for value in body.values.iter() { let mut value_def = std::mem::take(&mut body.values[value]); match &mut value_def { - ValueDef::Operator(_op, args, _tys) => { - for arg in args { - *arg = body.resolve_alias(*arg); + ValueDef::Operator(_, args, _) | ValueDef::Trace(_, args) => { + for i in 0..args.len() { + let val = body.arg_pool[*args][i]; + let val = body.resolve_and_update_alias(val); + body.arg_pool[*args][i] = val; } } ValueDef::PickOutput(val, _idx, _ty) => { diff --git a/src/passes/ssa.rs b/src/passes/ssa.rs index 0965c83..00c3342 100644 --- a/src/passes/ssa.rs +++ b/src/passes/ssa.rs @@ -44,7 +44,7 @@ pub fn run(body: &FunctionBody, cfg: &CFGInfo) { match &body.values[param] { &ValueDef::BlockParam(param_block, param_idx, _) => { assert_eq!(param_block, block); - assert_eq!(param_idx, i); + assert_eq!(param_idx, i as u32); } _ => panic!( "Bad blockparam value for param {} of {} ({}): {:?}", @@ -55,19 +55,14 @@ pub fn run(body: &FunctionBody, cfg: &CFGInfo) { for &inst in &data.insts { match &body.values[inst] { - &ValueDef::Operator(_, ref args, _) => { - for &arg in args { + &ValueDef::Operator(_, args, _) | &ValueDef::Trace(_, args) => { + for &arg in &body.arg_pool[args] { validate(arg); } } &ValueDef::PickOutput(val, _, _) => { validate(val); } - &ValueDef::Trace(_, ref args) => { - for &arg in args { - validate(arg); - } - } &ValueDef::Alias(..) => {} &ValueDef::None | &ValueDef::Placeholder(_) | &ValueDef::BlockParam(..) => {} } diff --git a/src/passes/trace.rs b/src/passes/trace.rs index 8c5f9d0..6d6c851 100644 --- a/src/passes/trace.rs +++ b/src/passes/trace.rs @@ -7,10 +7,8 @@ pub fn run(body: &mut FunctionBody) { for (block, data) in body.blocks.entries_mut() { let value = ValueDef::Trace( block.index(), - data.params - .iter() - .map(|&(_, param)| param) - .collect::>(), + body.arg_pool + .from_iter(data.params.iter().map(|&(_, param)| param)), ); let value = body.values.push(value); data.insts.insert(0, value); diff --git a/src/pool.rs b/src/pool.rs new file mode 100644 index 0000000..58dcd2a --- /dev/null +++ b/src/pool.rs @@ -0,0 +1,64 @@ +//! Pooled list data structure. + +use std::convert::TryFrom; +use std::default::Default; +use std::fmt::Debug; +use std::marker::PhantomData; +use std::ops::{Index, IndexMut}; + +#[derive(Clone, Debug)] +pub struct ListPool { + storage: Vec, +} + +impl Default for ListPool { + fn default() -> Self { + ListPool { storage: vec![] } + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub struct ListRef(u32, u32, PhantomData); + +impl Default for ListRef { + fn default() -> Self { + ListRef(0, 0, PhantomData) + } +} + +impl ListPool { + pub fn from_iter>(&mut self, iter: I) -> ListRef { + let start = u32::try_from(self.storage.len()).unwrap(); + self.storage.extend(iter); + let end = u32::try_from(self.storage.len()).unwrap(); + ListRef(start, end, PhantomData) + } + pub fn single(&mut self, value: T) -> ListRef { + self.from_iter(std::iter::once(value)) + } + pub fn allocate(&mut self, size: usize, initial: T) -> ListRef { + self.from_iter(std::iter::repeat(initial).take(size)) + } +} + +impl Index> for ListPool { + type Output = [T]; + fn index(&self, index: ListRef) -> &[T] { + &self.storage[index.0 as usize..index.1 as usize] + } +} + +impl IndexMut> for ListPool { + fn index_mut(&mut self, index: ListRef) -> &mut [T] { + &mut self.storage[index.0 as usize..index.1 as usize] + } +} + +impl ListRef { + pub fn len(&self) -> usize { + (self.1 - self.0) as usize + } + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +}