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");
return;
}
InterpResult::Trap => {
InterpResult::Trap(..) => {
// Silently reject.
log::trace!("Rejecting due to trap in orig");
return;

View file

@ -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())

View file

@ -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);

View file

@ -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;

View file

@ -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);
}

View file

@ -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);

View file

@ -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::<Vec<_>>();
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::<Vec<_>>()
.join(", "),
tys.iter()
self.body.type_pool[*tys]
.iter()
.map(|arg| format!("{}", arg))
.collect::<Vec<_>>()
.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::<Vec<_>>()
.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::<Vec<_>>()
.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::<Vec<_>>()
.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::<Vec<_>>();
let tys = tys.iter().map(|&ty| format!("{}", ty)).collect::<Vec<_>>();
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::<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 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::<Vec<_>>();
writeln!(f, "{} trace {}, {}", self.1, id, args.join(", "))?;
let args = self.body.arg_pool[*args]
.iter()
.map(|&v| format!("{}", v))
.collect::<Vec<_>>();
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, "}}")?;

View file

@ -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<Block, BlockDef>,
/// Value definitions, indexed by `Value`.
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.
pub value_blocks: PerEntity<Value, Block>,
/// 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<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) {
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));
}
}

View file

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

View file

@ -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<Value>, Vec<Type>),
PickOutput(Value, usize, Type),
BlockParam(Block, u32, Type),
Operator(Operator, ListRef<Value>, ListRef<Type>),
PickOutput(Value, u32, Type),
Alias(Value),
Placeholder(Type),
Trace(usize, Vec<Value>),
Trace(usize, ListRef<Value>),
#[default]
None,
}
impl ValueDef {
pub fn ty(&self) -> Option<Type> {
pub fn ty(&self, types: &ListPool<Type>) -> Option<Type> {
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<Type>) -> &'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<F: FnMut(Value)>(&self, mut f: F) {
pub fn visit_uses<F: FnMut(Value)>(&self, arg_pool: &ListPool<Value>, 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<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 {
&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);
}
}

View file

@ -14,6 +14,7 @@ mod ir;
mod op_traits;
mod ops;
pub mod passes;
pub mod pool;
mod scoped_map;
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 {
type Error = ();

View file

@ -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();
}

View file

@ -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, ..) => {

View file

@ -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);
}
}
}

View file

@ -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) => {

View file

@ -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(..) => {}
}

View file

@ -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::<Vec<_>>(),
body.arg_pool
.from_iter(data.params.iter().map(|&(_, param)| param)),
);
let value = body.values.push(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
}
}