Refactor to store args and types in common list pool.

This commit is contained in:
Chris Fallin 2023-04-18 17:31:17 -07:00
parent 4fd635ef7f
commit dab18103ce
19 changed files with 296 additions and 155 deletions

View file

@ -38,7 +38,7 @@ fuzz_target!(
log::trace!("Rejecting due to timeout in orig"); log::trace!("Rejecting due to timeout in orig");
return; return;
} }
InterpResult::Trap => { InterpResult::Trap(..) => {
// Silently reject. // Silently reject.
log::trace!("Rejecting due to trap in orig"); log::trace!("Rejecting due to trap in orig");
return; return;

View file

@ -90,13 +90,13 @@ impl<'a, V: Visitor> BlockVisitor<'a, V> {
} }
fn visit_inst(&mut self, value: Value, root: bool) { fn visit_inst(&mut self, value: Value, root: bool) {
// If this is an instruction... // 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, we need to process the def.
if root { if root {
self.visitor.visit_def(value); self.visitor.visit_def(value);
} }
// Handle uses. // Handle uses.
for &arg in args { for &arg in &self.body.arg_pool[*args] {
self.visit_use(arg); self.visit_use(arg);
} }
} }
@ -284,7 +284,7 @@ impl<'a> Context<'a> {
// allocate a new one. // allocate a new one.
let mut allocs = smallvec![]; let mut allocs = smallvec![];
let expiring = expiring.entry(range.end).or_insert_with(|| 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 let local = freelist
.get_mut(&ty) .get_mut(&ty)
.and_then(|v| v.pop()) .and_then(|v| v.pop())

View file

@ -185,7 +185,9 @@ impl<'a> WasmFuncBackend<'a> {
} else { } else {
let local = match &self.body.values[value] { let local = match &self.body.values[value] {
&ValueDef::BlockParam(..) | &ValueDef::Operator(..) => self.locals.values[value][0], &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!(), _ => unreachable!(),
}; };
func.instruction(&wasm_encoder::Instruction::LocalGet(local.index() as u32)); 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) { fn lower_inst(&self, value: Value, root: bool, func: &mut wasm_encoder::Function) {
log::trace!("lower_inst: value {} root {}", value, root); log::trace!("lower_inst: value {} root {}", value, root);
match &self.body.values[value] { match &self.body.values[value] {
&ValueDef::Operator(ref op, ref args, ref tys) => { &ValueDef::Operator(ref op, args, tys) => {
for &arg in &args[..] { for &arg in &self.body.arg_pool[args] {
let arg = self.body.resolve_alias(arg); let arg = self.body.resolve_alias(arg);
if self.trees.owner.contains_key(&arg) || self.trees.remat.contains(&arg) { if self.trees.owner.contains_key(&arg) || self.trees.remat.contains(&arg) {
log::trace!(" -> arg {} is owned", arg); log::trace!(" -> arg {} is owned", arg);

View file

@ -43,10 +43,10 @@ impl Trees {
for (value, def) in body.values.entries() { for (value, def) in body.values.entries() {
match def { match def {
&ValueDef::Operator(op, ref args, _) => { &ValueDef::Operator(op, args, _) => {
// Ignore operators with invalid args: these must // Ignore operators with invalid args: these must
// always be unreachable. // always be unreachable.
if args.iter().any(|arg| arg.is_invalid()) { if body.arg_pool[args].iter().any(|arg| arg.is_invalid()) {
continue; continue;
} }
// If this is an always-rematerialized operator, // If this is an always-rematerialized operator,
@ -62,7 +62,7 @@ impl Trees {
// in the arg slot. Otherwise if owned already // in the arg slot. Otherwise if owned already
// somewhere else, undo that and put in // somewhere else, undo that and put in
// `multi_use`. // `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); let arg = body.resolve_alias(arg);
if multi_use.contains(&arg) { if multi_use.contains(&arg) {
continue; continue;

View file

@ -7,6 +7,7 @@ use crate::errors::FrontendError;
use crate::ir::*; use crate::ir::*;
use crate::op_traits::{op_inputs, op_outputs}; use crate::op_traits::{op_inputs, op_outputs};
use crate::ops::Operator; use crate::ops::Operator;
use crate::pool::ListRef;
use addr2line::gimli; use addr2line::gimli;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use fxhash::{FxHashMap, FxHashSet}; use fxhash::{FxHashMap, FxHashSet};
@ -656,26 +657,27 @@ impl LocalTracker {
ty: Type, ty: Type,
at_block: Block, at_block: Block,
) -> Value { ) -> Value {
let types = body.single_type_list(ty);
let val = match ty { let val = match ty {
Type::I32 => body.add_value(ValueDef::Operator( Type::I32 => body.add_value(ValueDef::Operator(
Operator::I32Const { value: 0 }, Operator::I32Const { value: 0 },
vec![], ListRef::default(),
vec![ty], types,
)), )),
Type::I64 => body.add_value(ValueDef::Operator( Type::I64 => body.add_value(ValueDef::Operator(
Operator::I64Const { value: 0 }, Operator::I64Const { value: 0 },
vec![], ListRef::default(),
vec![ty], types,
)), )),
Type::F32 => body.add_value(ValueDef::Operator( Type::F32 => body.add_value(ValueDef::Operator(
Operator::F32Const { value: 0 }, Operator::F32Const { value: 0 },
vec![], ListRef::default(),
vec![ty], types,
)), )),
Type::F64 => body.add_value(ValueDef::Operator( Type::F64 => body.add_value(ValueDef::Operator(
Operator::F64Const { value: 0 }, Operator::F64Const { value: 0 },
vec![], ListRef::default(),
vec![ty], types,
)), )),
_ => todo!("unsupported type: {:?}", ty), _ => todo!("unsupported type: {:?}", ty),
}; };
@ -1684,19 +1686,25 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> {
let n_outputs = outputs.len(); let n_outputs = outputs.len();
let mut input_operands = vec![]; let input_operands = self.body.arg_pool.allocate(inputs.len(), Value::invalid());
for &input in inputs.into_iter().rev() { 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(); let (stack_top_ty, stack_top) = self.op_stack.pop().unwrap();
assert_eq!(stack_top_ty, input); 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!(" -> operands: {:?}", input_operands);
log::trace!(" -> ty {:?}", outputs); 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 let value = self
.body .body
.add_value(ValueDef::Operator(op, input_operands, outputs.to_vec())); .add_value(ValueDef::Operator(op, input_operands, outputs_list));
log::trace!(" -> value: {:?}", value); log::trace!(" -> value: {:?}", value);
if self.reachable { if self.reachable {
@ -1711,7 +1719,7 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> {
for (i, &output_ty) in outputs.into_iter().enumerate() { for (i, &output_ty) in outputs.into_iter().enumerate() {
let pick = self let pick = self
.body .body
.add_value(ValueDef::PickOutput(value, i, output_ty)); .add_value(ValueDef::PickOutput(value, i as u32, output_ty));
if self.reachable { if self.reachable {
self.body.append_to_block(self.cur_block, pick); self.body.append_to_block(self.cur_block, pick);
} }

View file

@ -133,10 +133,10 @@ impl InterpContext {
&ValueDef::Alias(_) => smallvec![], &ValueDef::Alias(_) => smallvec![],
&ValueDef::PickOutput(val, idx, _) => { &ValueDef::PickOutput(val, idx, _) => {
let val = body.resolve_alias(val); 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, _) => { &ValueDef::Operator(Operator::Call { function_index }, args, _) => {
let args = args let args = body.arg_pool[args]
.iter() .iter()
.map(|&arg| { .map(|&arg| {
let arg = body.resolve_alias(arg); let arg = body.resolve_alias(arg);
@ -151,12 +151,8 @@ impl InterpContext {
_ => return result, _ => return result,
} }
} }
&ValueDef::Operator( &ValueDef::Operator(Operator::CallIndirect { table_index, .. }, args, _) => {
Operator::CallIndirect { table_index, .. }, let args = body.arg_pool[args]
ref args,
_,
) => {
let args = args
.iter() .iter()
.map(|&arg| { .map(|&arg| {
let arg = body.resolve_alias(arg); let arg = body.resolve_alias(arg);
@ -173,8 +169,8 @@ impl InterpContext {
_ => return result, _ => return result,
} }
} }
&ValueDef::Operator(ref op, ref args, _) => { &ValueDef::Operator(ref op, args, _) => {
let args = args let args = body.arg_pool[args]
.iter() .iter()
.map(|&arg| { .map(|&arg| {
let arg = body.resolve_alias(arg); let arg = body.resolve_alias(arg);
@ -200,9 +196,9 @@ impl InterpContext {
}; };
smallvec![result] smallvec![result]
} }
&ValueDef::Trace(id, ref args) => { &ValueDef::Trace(id, args) => {
if let Some(handler) = self.trace_handler.as_ref() { if let Some(handler) = self.trace_handler.as_ref() {
let args = args let args = body.arg_pool[args]
.iter() .iter()
.map(|&arg| { .map(|&arg| {
let arg = body.resolve_alias(arg); let arg = body.resolve_alias(arg);

View file

@ -5,24 +5,24 @@ use crate::entity::EntityRef;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::{Display, Formatter, Result as FmtResult}; use std::fmt::{Display, Formatter, Result as FmtResult};
pub struct FunctionBodyDisplay<'a>( pub struct FunctionBodyDisplay<'a> {
pub(crate) &'a FunctionBody, pub(crate) body: &'a FunctionBody,
pub(crate) &'a str, pub(crate) indent: &'a str,
pub(crate) bool, pub(crate) verbose: bool,
pub(crate) Option<&'a Module<'a>>, pub(crate) module: Option<&'a Module<'a>>,
); }
impl<'a> Display for FunctionBodyDisplay<'a> { impl<'a> Display for FunctionBodyDisplay<'a> {
fn fmt(&self, f: &mut Formatter) -> FmtResult { fn fmt(&self, f: &mut Formatter) -> FmtResult {
let arg_tys = self let arg_tys = self
.0 .body
.locals .locals
.values() .values()
.take(self.0.n_params) .take(self.body.n_params)
.map(|&ty| format!("{}", ty)) .map(|&ty| format!("{}", ty))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let ret_tys = self let ret_tys = self
.0 .body
.rets .rets
.iter() .iter()
.map(|&ty| format!("{}", ty)) .map(|&ty| format!("{}", ty))
@ -30,51 +30,63 @@ impl<'a> Display for FunctionBodyDisplay<'a> {
writeln!( writeln!(
f, f,
"{}function({}) -> {} {{", "{}function({}) -> {} {{",
self.1, self.indent,
arg_tys.join(", "), arg_tys.join(", "),
ret_tys.join(", ") ret_tys.join(", ")
)?; )?;
let verbose = self.2; for (value, value_def) in self.body.values.entries() {
for (value, value_def) in self.0.values.entries() {
match value_def { match value_def {
ValueDef::Operator(op, args, tys) if verbose => writeln!( ValueDef::Operator(op, args, tys) if self.verbose => writeln!(
f, f,
"{} {} = {} {} # {}", "{} {} = {} {} # {}",
self.1, self.indent,
value, value,
op, op,
args.iter() self.body.arg_pool[*args]
.iter()
.map(|arg| format!("{}", arg)) .map(|arg| format!("{}", arg))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", "), .join(", "),
tys.iter() self.body.type_pool[*tys]
.iter()
.map(|arg| format!("{}", arg)) .map(|arg| format!("{}", arg))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", ") .join(", ")
)?, )?,
ValueDef::BlockParam(block, idx, ty) if verbose => writeln!( ValueDef::BlockParam(block, idx, ty) if self.verbose => writeln!(
f, f,
"{} {} = blockparam {}, {} # {}", "{} {} = blockparam {}, {} # {}",
self.1, value, block, idx, ty self.indent, value, block, idx, ty
)?, )?,
ValueDef::Alias(alias_target) => { ValueDef::Alias(alias_target) => {
if verbose { if self.verbose {
writeln!(f, "{} {} = {}", self.1, value, alias_target)? writeln!(f, "{} {} = {}", self.indent, value, alias_target)?
} }
} }
ValueDef::PickOutput(val, idx, ty) => { ValueDef::PickOutput(val, idx, ty) => {
writeln!(f, "{} {} = {}.{} # {}", self.1, value, val, idx, ty)? writeln!(f, "{} {} = {}.{} # {}", self.indent, value, val, idx, ty)?
} }
ValueDef::Placeholder(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::<Vec<_>>()
.join(", ")
)?,
ValueDef::None => panic!(), 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 let block_params = block
.params .params
.iter() .iter()
@ -83,7 +95,7 @@ impl<'a> Display for FunctionBodyDisplay<'a> {
writeln!( writeln!(
f, f,
"{} {}({}): # {}", "{} {}({}): # {}",
self.1, self.indent,
block_id, block_id,
block_params.join(", "), block_params.join(", "),
block.desc block.desc
@ -91,43 +103,49 @@ impl<'a> Display for FunctionBodyDisplay<'a> {
writeln!( writeln!(
f, f,
"{} # preds: {}", "{} # preds: {}",
self.1, self.indent,
block block
.preds .preds
.iter() .iter()
.map(|pred| format!("{} ({})", pred, self.0.blocks[*pred].desc)) .map(|pred| format!("{} ({})", pred, self.body.blocks[*pred].desc))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", ") .join(", ")
)?; )?;
writeln!( writeln!(
f, f,
"{} # succs: {}", "{} # succs: {}",
self.1, self.indent,
block block
.succs .succs
.iter() .iter()
.map(|succ| format!("{} ({})", succ, self.0.blocks[*succ].desc)) .map(|succ| format!("{} ({})", succ, self.body.blocks[*succ].desc))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", ") .join(", ")
)?; )?;
for (_, param) in &block.params { for (_, param) in &block.params {
if let Some(local) = self.0.value_locals[*param] { if let Some(local) = self.body.value_locals[*param] {
writeln!(f, "{} # {}: {}", self.1, param, local)?; writeln!(f, "{} # {}: {}", self.indent, param, local)?;
} }
} }
for &inst in &block.insts { for &inst in &block.insts {
if let Some(local) = self.0.value_locals[inst] { if let Some(local) = self.body.value_locals[inst] {
writeln!(f, "{} # {}: {}", self.1, inst, local)?; writeln!(f, "{} # {}: {}", self.indent, inst, local)?;
} }
match &self.0.values[inst] { match &self.body.values[inst] {
ValueDef::Operator(op, args, tys) => { ValueDef::Operator(op, args, tys) => {
let args = args.iter().map(|&v| format!("{}", v)).collect::<Vec<_>>(); let args = self.body.arg_pool[*args]
let tys = tys.iter().map(|&ty| format!("{}", ty)).collect::<Vec<_>>(); .iter()
let loc = if self.0.source_locs[inst] != SourceLoc::invalid() .map(|&v| format!("{}", v))
&& self.3.is_some() .collect::<Vec<_>>();
let tys = self.body.type_pool[*tys]
.iter()
.map(|&ty| format!("{}", ty))
.collect::<Vec<_>>();
let loc = if self.body.source_locs[inst] != SourceLoc::invalid()
&& self.module.is_some()
{ {
let module = self.3.as_ref().unwrap(); let module = self.module.as_ref().unwrap();
let loc = self.0.source_locs[inst]; let loc = self.body.source_locs[inst];
let data = &module.debug.source_locs[loc]; let data = &module.debug.source_locs[loc];
let filename = &module.debug.source_files[data.file]; let filename = &module.debug.source_files[data.file];
format!("@{} {}:{}:{}", loc, filename, data.line, data.col) format!("@{} {}:{}:{}", loc, filename, data.line, data.col)
@ -137,7 +155,7 @@ impl<'a> Display for FunctionBodyDisplay<'a> {
writeln!( writeln!(
f, f,
"{} {} = {} {} # {} {}", "{} {} = {} {} # {} {}",
self.1, self.indent,
inst, inst,
op, op,
args.join(", "), args.join(", "),
@ -146,37 +164,42 @@ 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.indent, inst, val, idx, ty)?;
} }
ValueDef::Alias(val) => { ValueDef::Alias(val) => {
writeln!(f, "{} {} = {}", self.1, inst, val)?; writeln!(f, "{} {} = {}", self.indent, inst, val)?;
} }
ValueDef::Trace(id, args) => { ValueDef::Trace(id, args) => {
let args = args.iter().map(|&v| format!("{}", v)).collect::<Vec<_>>(); let args = self.body.arg_pool[*args]
writeln!(f, "{} trace {}, {}", self.1, id, args.join(", "))?; .iter()
.map(|&v| format!("{}", v))
.collect::<Vec<_>>();
writeln!(f, "{} trace {}, {}", self.indent, id, args.join(", "))?;
} }
_ => unreachable!(), _ => unreachable!(),
} }
} }
writeln!(f, "{} {}", self.1, block.terminator)?; writeln!(f, "{} {}", self.indent, block.terminator)?;
} }
writeln!(f, "{}}}", self.1)?; writeln!(f, "{}}}", self.indent)?;
Ok(()) 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> { impl<'a> Display for ModuleDisplay<'a> {
fn fmt(&self, f: &mut Formatter) -> FmtResult { fn fmt(&self, f: &mut Formatter) -> FmtResult {
writeln!(f, "module {{")?; writeln!(f, "module {{")?;
if let Some(func) = self.0.start_func { if let Some(func) = self.module.start_func {
writeln!(f, " start = {}", func)?; writeln!(f, " start = {}", func)?;
} }
let mut sig_strs = HashMap::new(); 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 let arg_tys = sig_data
.params .params
.iter() .iter()
@ -191,14 +214,14 @@ impl<'a> Display for ModuleDisplay<'a> {
sig_strs.insert(sig, sig_str.clone()); sig_strs.insert(sig, sig_str.clone());
writeln!(f, " {}: {}", sig, sig_str)?; writeln!(f, " {}: {}", sig, sig_str)?;
} }
for (global, global_data) in self.0.globals.entries() { for (global, global_data) in self.module.globals.entries() {
writeln!( writeln!(
f, f,
" {}: {:?} # {}", " {}: {:?} # {}",
global, global_data.value, global_data.ty 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)?; writeln!(f, " {}: {}", table, table_data.ty)?;
if let Some(funcs) = &table_data.func_elements { if let Some(funcs) = &table_data.func_elements {
for (i, &func) in funcs.iter().enumerate() { 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!( writeln!(
f, f,
" {}: initial {} max {:?}", " {}: 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!( writeln!(
f, f,
" import \"{}\".\"{}\": {}", " import \"{}\".\"{}\": {}",
import.module, import.name, import.kind 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)?; 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 { match func_decl {
FuncDecl::Body(sig, name, body) => { FuncDecl::Body(sig, name, body) => {
writeln!( writeln!(
@ -243,7 +266,7 @@ impl<'a> Display for ModuleDisplay<'a> {
sig, sig,
sig_strs.get(&sig).unwrap() sig_strs.get(&sig).unwrap()
)?; )?;
writeln!(f, "{}", body.display(" ", Some(self.0)))?; writeln!(f, "{}", body.display(" ", Some(self.module)))?;
} }
FuncDecl::Lazy(sig, name, reader) => { FuncDecl::Lazy(sig, name, reader) => {
writeln!( 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!( writeln!(
f, f,
" {} = {} line {} column {}", " {} = {} line {} column {}",
loc, loc_data.file, loc_data.line, loc_data.col 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, " {} = \"{}\"", file, file_name)?;
} }
writeln!(f, "}}")?; writeln!(f, "}}")?;

View file

@ -4,7 +4,9 @@ use crate::cfg::CFGInfo;
use crate::entity::{EntityRef, EntityVec, PerEntity}; use crate::entity::{EntityRef, EntityVec, PerEntity};
use crate::frontend::parse_body; use crate::frontend::parse_body;
use crate::ir::SourceLoc; use crate::ir::SourceLoc;
use crate::pool::{ListPool, ListRef};
use anyhow::Result; use anyhow::Result;
use fxhash::FxHashMap;
use std::collections::HashSet; use std::collections::HashSet;
/// A declaration of a function: there is one `FuncDecl` per `Func` /// A declaration of a function: there is one `FuncDecl` per `Func`
@ -124,6 +126,12 @@ pub struct FunctionBody {
pub blocks: EntityVec<Block, BlockDef>, pub blocks: EntityVec<Block, BlockDef>,
/// Value definitions, indexed by `Value`. /// Value definitions, indexed by `Value`.
pub values: EntityVec<Value, ValueDef>, pub values: EntityVec<Value, ValueDef>,
/// Pool of types for ValueDefs' result type lists.
pub type_pool: ListPool<Type>,
/// Deduplication for type-lists of single types.
pub single_type_dedup: FxHashMap<Type, ListRef<Type>>,
/// Pool of values for ValueDefs' arg lists.
pub arg_pool: ListPool<Value>,
/// Blocks in which values are computed. Each may be `Block::invalid()` if not placed. /// Blocks in which values are computed. Each may be `Block::invalid()` if not placed.
pub value_blocks: PerEntity<Value, Block>, pub value_blocks: PerEntity<Value, Block>,
/// Wasm locals that values correspond to, if any. /// Wasm locals that values correspond to, if any.
@ -142,7 +150,7 @@ impl FunctionBody {
let mut values = EntityVec::default(); let mut values = EntityVec::default();
let mut value_blocks = PerEntity::default(); let mut value_blocks = PerEntity::default();
for (i, &arg_ty) in locals.values().enumerate() { 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)); blocks[entry].params.push((arg_ty, value));
value_blocks[value] = entry; value_blocks[value] = entry;
} }
@ -153,6 +161,9 @@ impl FunctionBody {
entry, entry,
blocks, blocks,
values, values,
type_pool: ListPool::default(),
arg_pool: ListPool::default(),
single_type_dedup: FxHashMap::default(),
value_blocks, value_blocks,
value_locals: PerEntity::default(), value_locals: PerEntity::default(),
source_locs: PerEntity::default(), source_locs: PerEntity::default(),
@ -178,6 +189,14 @@ impl FunctionBody {
id id
} }
pub fn single_type_list(&mut self, ty: Type) -> ListRef<Type> {
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) { pub fn add_edge(&mut self, from: Block, to: Block) {
let succ_pos = self.blocks[from].succs.len(); let succ_pos = self.blocks[from].succs.len();
let pred_pos = self.blocks[to].preds.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 { pub fn add_blockparam(&mut self, block: Block, ty: Type) -> Value {
let index = self.blocks[block].params.len(); 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.blocks[block].params.push((ty, value));
self.value_blocks[value] = block; self.value_blocks[value] = block;
value value
@ -301,7 +320,7 @@ impl FunctionBody {
_ => unreachable!(), _ => unreachable!(),
}; };
self.blocks[block].params.push((ty, value)); 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) { pub fn mark_value_as_local(&mut self, value: Value, local: Local) {
@ -342,7 +361,12 @@ impl FunctionBody {
indent: &'a str, indent: &'a str,
module: Option<&'a Module>, module: Option<&'a Module>,
) -> FunctionBodyDisplay<'a> { ) -> FunctionBodyDisplay<'a> {
FunctionBodyDisplay(self, indent, /* verbose = */ false, module) FunctionBodyDisplay {
body: self,
indent,
verbose: false,
module,
}
} }
pub fn display_verbose<'a>( pub fn display_verbose<'a>(
@ -350,7 +374,12 @@ impl FunctionBody {
indent: &'a str, indent: &'a str,
module: Option<&'a Module>, module: Option<&'a Module>,
) -> FunctionBodyDisplay<'a> { ) -> FunctionBodyDisplay<'a> {
FunctionBodyDisplay(self, indent, /* verbose = */ true, module) FunctionBodyDisplay {
body: self,
indent,
verbose: true,
module,
}
} }
pub fn validate(&self) -> anyhow::Result<()> { pub fn validate(&self) -> anyhow::Result<()> {
@ -419,8 +448,8 @@ impl FunctionBody {
for (i, &inst) in block_def.insts.iter().enumerate() { for (i, &inst) in block_def.insts.iter().enumerate() {
match &self.values[inst] { match &self.values[inst] {
&ValueDef::Operator(_, ref args, _) => { &ValueDef::Operator(_, args, _) => {
for &arg in args { for &arg in &self.arg_pool[args] {
visit_use(arg, Some(i), Some(inst)); visit_use(arg, Some(i), Some(inst));
} }
} }

View file

@ -236,6 +236,6 @@ impl<'a> Module<'a> {
where where
'b: 'a, 'b: 'a,
{ {
ModuleDisplay(self) ModuleDisplay { module: self }
} }
} }

View file

@ -1,24 +1,25 @@
use super::{Block, Type, Value}; use super::{Block, Type, Value};
use crate::pool::{ListPool, ListRef};
use crate::Operator; use crate::Operator;
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub enum ValueDef { pub enum ValueDef {
BlockParam(Block, usize, Type), BlockParam(Block, u32, Type),
Operator(Operator, Vec<Value>, Vec<Type>), Operator(Operator, ListRef<Value>, ListRef<Type>),
PickOutput(Value, usize, Type), PickOutput(Value, u32, Type),
Alias(Value), Alias(Value),
Placeholder(Type), Placeholder(Type),
Trace(usize, Vec<Value>), Trace(usize, ListRef<Value>),
#[default] #[default]
None, None,
} }
impl ValueDef { impl ValueDef {
pub fn ty(&self) -> Option<Type> { pub fn ty(&self, types: &ListPool<Type>) -> Option<Type> {
match self { match self {
&ValueDef::BlockParam(_, _, ty) => Some(ty), &ValueDef::BlockParam(_, _, ty) => Some(ty),
&ValueDef::Operator(_, _, ref tys) if tys.len() == 0 => None, &ValueDef::Operator(_, _, tys) if tys.len() == 0 => None,
&ValueDef::Operator(_, _, ref tys) if tys.len() == 1 => Some(tys[0]), &ValueDef::Operator(_, _, tys) if tys.len() == 1 => Some(types[tys][0]),
&ValueDef::PickOutput(_, _, ty) => Some(ty), &ValueDef::PickOutput(_, _, ty) => Some(ty),
&ValueDef::Placeholder(ty) => Some(ty), &ValueDef::Placeholder(ty) => Some(ty),
&ValueDef::Trace(_, _) => None, &ValueDef::Trace(_, _) => None,
@ -26,9 +27,9 @@ impl ValueDef {
} }
} }
pub fn tys(&self) -> &[Type] { pub fn tys<'a>(&'a self, types: &'a ListPool<Type>) -> &'a [Type] {
match self { match self {
&ValueDef::Operator(_, _, ref tys) => &tys[..], &ValueDef::Operator(_, _, tys) => &types[tys],
&ValueDef::BlockParam(_, _, ref ty) &ValueDef::BlockParam(_, _, ref ty)
| &ValueDef::PickOutput(_, _, ref ty) | &ValueDef::PickOutput(_, _, ref ty)
| &ValueDef::Placeholder(ref ty) => std::slice::from_ref(ty), | &ValueDef::Placeholder(ref ty) => std::slice::from_ref(ty),
@ -36,19 +37,19 @@ impl ValueDef {
} }
} }
pub fn visit_uses<F: FnMut(Value)>(&self, mut f: F) { pub fn visit_uses<F: FnMut(Value)>(&self, arg_pool: &ListPool<Value>, mut f: F) {
match self { match self {
&ValueDef::BlockParam { .. } => {} &ValueDef::BlockParam { .. } => {}
&ValueDef::Operator(_, ref args, _) => { &ValueDef::Operator(_, args, _) => {
for &arg in args { for &arg in &arg_pool[args] {
f(arg); f(arg);
} }
} }
&ValueDef::PickOutput(from, ..) => f(from), &ValueDef::PickOutput(from, ..) => f(from),
&ValueDef::Alias(value) => f(value), &ValueDef::Alias(value) => f(value),
&ValueDef::Placeholder(_) => {} &ValueDef::Placeholder(_) => {}
&ValueDef::Trace(_, ref args) => { &ValueDef::Trace(_, args) => {
for &arg in args { for &arg in &arg_pool[args] {
f(arg); f(arg);
} }
} }
@ -56,19 +57,19 @@ impl ValueDef {
} }
} }
pub fn update_uses<F: FnMut(&mut Value)>(&mut self, mut f: F) { pub fn update_uses<F: FnMut(&mut Value)>(&mut self, arg_pool: &mut ListPool<Value>, mut f: F) {
match self { match self {
&mut ValueDef::BlockParam { .. } => {} &mut ValueDef::BlockParam { .. } => {}
&mut ValueDef::Operator(_, ref mut args, _) => { &mut ValueDef::Operator(_, args, _) => {
for arg in args { for arg in &mut arg_pool[args] {
f(arg); f(arg);
} }
} }
&mut ValueDef::PickOutput(ref mut from, ..) => f(from), &mut ValueDef::PickOutput(ref mut from, ..) => f(from),
&mut ValueDef::Alias(ref mut value) => f(value), &mut ValueDef::Alias(ref mut value) => f(value),
&mut ValueDef::Placeholder(_) => {} &mut ValueDef::Placeholder(_) => {}
&mut ValueDef::Trace(_, ref mut args) => { &mut ValueDef::Trace(_, args) => {
for arg in args { for arg in &mut arg_pool[args] {
f(arg); f(arg);
} }
} }

View file

@ -14,6 +14,7 @@ mod ir;
mod op_traits; mod op_traits;
mod ops; mod ops;
pub mod passes; pub mod passes;
pub mod pool;
mod scoped_map; mod scoped_map;
pub use errors::*; pub use errors::*;

View file

@ -297,6 +297,11 @@ pub enum Operator {
}, },
} }
#[test]
fn op_size() {
assert_eq!(std::mem::size_of::<Operator>(), 16);
}
impl<'a, 'b> std::convert::TryFrom<&'b wasmparser::Operator<'a>> for Operator { impl<'a, 'b> std::convert::TryFrom<&'b wasmparser::Operator<'a>> for Operator {
type Error = (); type Error = ();

View file

@ -4,6 +4,7 @@ use crate::cfg::CFGInfo;
use crate::interp::{const_eval, ConstVal}; use crate::interp::{const_eval, ConstVal};
use crate::ir::*; use crate::ir::*;
use crate::passes::dom_pass::{dom_pass, DomtreePass}; use crate::passes::dom_pass::{dom_pass, DomtreePass};
use crate::pool::ListRef;
use crate::scoped_map::ScopedMap; use crate::scoped_map::ScopedMap;
use crate::Operator; use crate::Operator;
@ -48,10 +49,24 @@ impl GVNPass {
i += 1; i += 1;
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_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 { if let ValueDef::Operator(op, args, ..) = &value {
let arg_values = args let arg_values = body.arg_pool[*args]
.iter() .iter()
.map(|&arg| match body.values[arg] { .map(|&arg| match body.values[arg] {
ValueDef::Operator(Operator::I32Const { value }, _, _) => { ValueDef::Operator(Operator::I32Const { value }, _, _) => {
@ -74,32 +89,32 @@ impl GVNPass {
Some(ConstVal::I32(val)) => { Some(ConstVal::I32(val)) => {
value = ValueDef::Operator( value = ValueDef::Operator(
Operator::I32Const { value: val }, Operator::I32Const { value: val },
vec![], ListRef::default(),
vec![Type::I32], body.single_type_list(Type::I32),
); );
body.values[inst] = value.clone(); body.values[inst] = value.clone();
} }
Some(ConstVal::I64(val)) => { Some(ConstVal::I64(val)) => {
value = ValueDef::Operator( value = ValueDef::Operator(
Operator::I64Const { value: val }, Operator::I64Const { value: val },
vec![], ListRef::default(),
vec![Type::I64], body.single_type_list(Type::I64),
); );
body.values[inst] = value.clone(); body.values[inst] = value.clone();
} }
Some(ConstVal::F32(val)) => { Some(ConstVal::F32(val)) => {
value = ValueDef::Operator( value = ValueDef::Operator(
Operator::F32Const { value: val }, Operator::F32Const { value: val },
vec![], ListRef::default(),
vec![Type::F32], body.single_type_list(Type::F32),
); );
body.values[inst] = value.clone(); body.values[inst] = value.clone();
} }
Some(ConstVal::F64(val)) => { Some(ConstVal::F64(val)) => {
value = ValueDef::Operator( value = ValueDef::Operator(
Operator::F64Const { value: val }, Operator::F64Const { value: val },
vec![], ListRef::default(),
vec![Type::F64], body.single_type_list(Type::F64),
); );
body.values[inst] = value.clone(); body.values[inst] = value.clone();
} }

View file

@ -48,8 +48,8 @@ impl MaxSSAPass {
let mut uses = BTreeSet::default(); let mut uses = BTreeSet::default();
for &inst in &body.blocks[block].insts { for &inst in &body.blocks[block].insts {
match &body.values[inst] { match &body.values[inst] {
&ValueDef::Operator(_, ref args, _) => { &ValueDef::Operator(_, args, _) | &ValueDef::Trace(_, args) => {
for &arg in args { for &arg in &body.arg_pool[args] {
let arg = body.resolve_alias(arg); let arg = body.resolve_alias(arg);
uses.insert(arg); uses.insert(arg);
} }
@ -81,7 +81,7 @@ impl MaxSSAPass {
self.new_args[block].push(value); self.new_args[block].push(value);
// Create a placeholder 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); let blockparam = body.add_blockparam(block, ty);
self.value_map.insert((block, value), blockparam); self.value_map.insert((block, value), blockparam);
@ -147,9 +147,11 @@ impl MaxSSAPass {
let inst = body.blocks[block].insts[i]; let inst = body.blocks[block].insts[i];
let mut def = std::mem::take(&mut body.values[inst]); let mut def = std::mem::take(&mut body.values[inst]);
match &mut def { match &mut def {
ValueDef::Operator(_, args, _) => { ValueDef::Operator(_, args, _) | ValueDef::Trace(_, args) => {
for arg in args { for i in 0..args.len() {
*arg = resolve(body, *arg); let val = body.arg_pool[*args][i];
let val = resolve(body, val);
body.arg_pool[*args][i] = val;
} }
} }
ValueDef::PickOutput(value, ..) => { ValueDef::PickOutput(value, ..) => {

View file

@ -99,8 +99,8 @@ pub fn run(func: &mut FunctionBody, cfg: &CFGInfo) {
// Renumber blockparam values. // Renumber blockparam values.
for (i, &(_, param)) in func.blocks[block].params.iter().enumerate() { for (i, &(_, param)) in func.blocks[block].params.iter().enumerate() {
let ty = func.values[param].ty().unwrap(); let ty = func.values[param].ty(&func.type_pool).unwrap();
func.values[param] = ValueDef::BlockParam(block, i, ty); func.values[param] = ValueDef::BlockParam(block, i as u32, ty);
} }
} }
} }

View file

@ -10,9 +10,11 @@ pub fn run(body: &mut FunctionBody) {
for value in body.values.iter() { for value in body.values.iter() {
let mut value_def = std::mem::take(&mut body.values[value]); let mut value_def = std::mem::take(&mut body.values[value]);
match &mut value_def { match &mut value_def {
ValueDef::Operator(_op, args, _tys) => { ValueDef::Operator(_, args, _) | ValueDef::Trace(_, args) => {
for arg in args { for i in 0..args.len() {
*arg = body.resolve_alias(*arg); 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) => { ValueDef::PickOutput(val, _idx, _ty) => {

View file

@ -44,7 +44,7 @@ pub fn run(body: &FunctionBody, cfg: &CFGInfo) {
match &body.values[param] { match &body.values[param] {
&ValueDef::BlockParam(param_block, param_idx, _) => { &ValueDef::BlockParam(param_block, param_idx, _) => {
assert_eq!(param_block, block); assert_eq!(param_block, block);
assert_eq!(param_idx, i); assert_eq!(param_idx, i as u32);
} }
_ => panic!( _ => panic!(
"Bad blockparam value for param {} of {} ({}): {:?}", "Bad blockparam value for param {} of {} ({}): {:?}",
@ -55,19 +55,14 @@ pub fn run(body: &FunctionBody, cfg: &CFGInfo) {
for &inst in &data.insts { for &inst in &data.insts {
match &body.values[inst] { match &body.values[inst] {
&ValueDef::Operator(_, ref args, _) => { &ValueDef::Operator(_, args, _) | &ValueDef::Trace(_, args) => {
for &arg in args { for &arg in &body.arg_pool[args] {
validate(arg); validate(arg);
} }
} }
&ValueDef::PickOutput(val, _, _) => { &ValueDef::PickOutput(val, _, _) => {
validate(val); validate(val);
} }
&ValueDef::Trace(_, ref args) => {
for &arg in args {
validate(arg);
}
}
&ValueDef::Alias(..) => {} &ValueDef::Alias(..) => {}
&ValueDef::None | &ValueDef::Placeholder(_) | &ValueDef::BlockParam(..) => {} &ValueDef::None | &ValueDef::Placeholder(_) | &ValueDef::BlockParam(..) => {}
} }

View file

@ -7,10 +7,8 @@ pub fn run(body: &mut FunctionBody) {
for (block, data) in body.blocks.entries_mut() { for (block, data) in body.blocks.entries_mut() {
let value = ValueDef::Trace( let value = ValueDef::Trace(
block.index(), block.index(),
data.params body.arg_pool
.iter() .from_iter(data.params.iter().map(|&(_, param)| param)),
.map(|&(_, param)| param)
.collect::<Vec<_>>(),
); );
let value = body.values.push(value); let value = body.values.push(value);
data.insts.insert(0, value); data.insts.insert(0, value);

64
src/pool.rs Normal file
View file

@ -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<T: Clone + Debug> {
storage: Vec<T>,
}
impl<T: Clone + Debug> Default for ListPool<T> {
fn default() -> Self {
ListPool { storage: vec![] }
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct ListRef<T>(u32, u32, PhantomData<T>);
impl<T> Default for ListRef<T> {
fn default() -> Self {
ListRef(0, 0, PhantomData)
}
}
impl<T: Clone + Debug> ListPool<T> {
pub fn from_iter<I: Iterator<Item = T>>(&mut self, iter: I) -> ListRef<T> {
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<T> {
self.from_iter(std::iter::once(value))
}
pub fn allocate(&mut self, size: usize, initial: T) -> ListRef<T> {
self.from_iter(std::iter::repeat(initial).take(size))
}
}
impl<T: Clone + Debug> Index<ListRef<T>> for ListPool<T> {
type Output = [T];
fn index(&self, index: ListRef<T>) -> &[T] {
&self.storage[index.0 as usize..index.1 as usize]
}
}
impl<T: Clone + Debug> IndexMut<ListRef<T>> for ListPool<T> {
fn index_mut(&mut self, index: ListRef<T>) -> &mut [T] {
&mut self.storage[index.0 as usize..index.1 as usize]
}
}
impl<T> ListRef<T> {
pub fn len(&self) -> usize {
(self.1 - self.0) as usize
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}