make various parts of basic_opt optional
This commit is contained in:
parent
fb9a00978f
commit
63616b502a
|
@ -2,7 +2,7 @@
|
|||
use libfuzzer_sys::fuzz_target;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
use waffle::{FrontendOptions, Module};
|
||||
use waffle::{FrontendOptions, Module, OptOptions};
|
||||
|
||||
fuzz_target!(|module: waffle::fuzzing::ArbitraryModule| {
|
||||
let module = module.0;
|
||||
|
@ -37,7 +37,7 @@ fuzz_target!(|module: waffle::fuzzing::ArbitraryModule| {
|
|||
let mut parsed_module =
|
||||
Module::from_wasm_bytes(&orig_bytes[..], &FrontendOptions::default()).unwrap();
|
||||
parsed_module.expand_all_funcs().unwrap();
|
||||
parsed_module.per_func_body(|body| body.optimize());
|
||||
parsed_module.per_func_body(|body| body.optimize(&OptOptions::default()));
|
||||
let roundtrip_bytes = parsed_module.to_wasm_bytes().unwrap();
|
||||
|
||||
if let Ok(filename) = std::env::var("FUZZ_DUMP_WASM") {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#![no_main]
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
use waffle::{FrontendOptions, InterpContext, InterpResult, Module};
|
||||
use waffle::{FrontendOptions, InterpContext, InterpResult, Module, OptOptions};
|
||||
|
||||
fuzz_target!(|module: waffle::fuzzing::ArbitraryModule| {
|
||||
let module = module.0;
|
||||
|
@ -48,7 +48,7 @@ fuzz_target!(|module: waffle::fuzzing::ArbitraryModule| {
|
|||
}
|
||||
|
||||
let mut opt_module = parsed_module.clone();
|
||||
opt_module.per_func_body(|body| body.optimize());
|
||||
parsed_module.per_func_body(|body| body.optimize(&OptOptions::default()));
|
||||
opt_module.per_func_body(|body| body.convert_to_max_ssa(None));
|
||||
|
||||
let mut opt_ctx = InterpContext::new(&opt_module).unwrap();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#![no_main]
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
use waffle::{FrontendError, FrontendOptions, Module};
|
||||
use waffle::{FrontendError, FrontendOptions, Module, OptOptions};
|
||||
|
||||
fuzz_target!(|module: wasm_smith::Module| {
|
||||
let _ = env_logger::try_init();
|
||||
|
@ -26,6 +26,6 @@ fuzz_target!(|module: wasm_smith::Module| {
|
|||
}
|
||||
};
|
||||
parsed_module.expand_all_funcs().unwrap();
|
||||
parsed_module.per_func_body(|body| body.optimize());
|
||||
parsed_module.per_func_body(|body| body.optimize(&OptOptions::default()));
|
||||
let _ = parsed_module.to_wasm_bytes();
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@ use log::debug;
|
|||
use std::path::PathBuf;
|
||||
use structopt::StructOpt;
|
||||
use waffle::InterpContext;
|
||||
use waffle::{entity::EntityRef, FrontendOptions, Func, Module};
|
||||
use waffle::{entity::EntityRef, FrontendOptions, Func, Module, OptOptions};
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
#[structopt(name = "waffle-util", about = "WAFFLE utility.")]
|
||||
|
@ -64,7 +64,7 @@ enum Command {
|
|||
fn apply_options(opts: &Options, module: &mut Module) -> Result<()> {
|
||||
module.expand_all_funcs()?;
|
||||
if opts.basic_opts {
|
||||
module.per_func_body(|body| body.optimize());
|
||||
module.per_func_body(|body| body.optimize(&OptOptions::default()));
|
||||
}
|
||||
if opts.max_ssa {
|
||||
module.per_func_body(|body| body.convert_to_max_ssa(None));
|
||||
|
|
|
@ -4,6 +4,7 @@ use crate::cfg::CFGInfo;
|
|||
use crate::entity::{EntityRef, EntityVec, PerEntity};
|
||||
use crate::frontend::parse_body;
|
||||
use crate::ir::SourceLoc;
|
||||
use crate::passes::basic_opt::OptOptions;
|
||||
use crate::pool::{ListPool, ListRef};
|
||||
use crate::{Func, Table};
|
||||
use anyhow::Result;
|
||||
|
@ -49,10 +50,10 @@ impl<'a> FuncDecl<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn optimize(&mut self) {
|
||||
pub fn optimize(&mut self, opts: &OptOptions) {
|
||||
match self {
|
||||
FuncDecl::Body(_, _, body) => {
|
||||
body.optimize();
|
||||
body.optimize(opts);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -171,11 +172,9 @@ impl FunctionBody {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn optimize(&mut self) {
|
||||
pub fn optimize(&mut self, opts: &OptOptions) {
|
||||
let cfg = crate::cfg::CFGInfo::new(self);
|
||||
crate::passes::remove_phis::run(self, &cfg);
|
||||
crate::passes::basic_opt::gvn(self, &cfg);
|
||||
crate::passes::remove_phis::run(self, &cfg);
|
||||
crate::passes::basic_opt::basic_opt(self, &cfg, opts);
|
||||
crate::passes::empty_blocks::run(self);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,5 +24,7 @@ pub use ops::{Ieee32, Ieee64, MemoryArg, Operator};
|
|||
mod interp;
|
||||
pub use interp::*;
|
||||
|
||||
pub use passes::basic_opt::OptOptions;
|
||||
|
||||
#[cfg(feature = "fuzzing")]
|
||||
pub mod fuzzing;
|
||||
|
|
|
@ -4,7 +4,6 @@ pub mod basic_opt;
|
|||
pub mod dom_pass;
|
||||
pub mod empty_blocks;
|
||||
pub mod maxssa;
|
||||
pub mod remove_phis;
|
||||
pub mod resolve_aliases;
|
||||
pub mod ssa;
|
||||
pub mod trace;
|
||||
|
|
|
@ -9,24 +9,47 @@ use crate::scoped_map::ScopedMap;
|
|||
use crate::Operator;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
pub fn gvn(body: &mut FunctionBody, cfg: &CFGInfo) {
|
||||
dom_pass::<GVNPass>(
|
||||
body,
|
||||
cfg,
|
||||
&mut GVNPass {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OptOptions {
|
||||
pub gvn: bool,
|
||||
pub cprop: bool,
|
||||
pub redundant_blockparams: bool,
|
||||
}
|
||||
|
||||
impl std::default::Default for OptOptions {
|
||||
fn default() -> Self {
|
||||
OptOptions {
|
||||
gvn: true,
|
||||
cprop: true,
|
||||
redundant_blockparams: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn basic_opt(body: &mut FunctionBody, cfg: &CFGInfo, options: &OptOptions) {
|
||||
loop {
|
||||
let mut pass = BasicOptPass {
|
||||
map: ScopedMap::default(),
|
||||
cfg,
|
||||
},
|
||||
);
|
||||
options,
|
||||
changed: false,
|
||||
};
|
||||
dom_pass::<BasicOptPass>(body, cfg, &mut pass);
|
||||
if !pass.changed {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct GVNPass<'a> {
|
||||
struct BasicOptPass<'a> {
|
||||
map: ScopedMap<ValueDef, Value>,
|
||||
cfg: &'a CFGInfo,
|
||||
options: &'a OptOptions,
|
||||
changed: bool,
|
||||
}
|
||||
|
||||
impl<'a> DomtreePass for GVNPass<'a> {
|
||||
impl<'a> DomtreePass for BasicOptPass<'a> {
|
||||
fn enter(&mut self, block: Block, body: &mut FunctionBody) {
|
||||
self.map.push_level();
|
||||
self.optimize(block, body);
|
||||
|
@ -82,9 +105,9 @@ fn remove_all_from_vec<T: Clone>(v: &mut Vec<T>, indices: &[usize]) {
|
|||
v.truncate(out);
|
||||
}
|
||||
|
||||
impl<'a> GVNPass<'a> {
|
||||
impl<'a> BasicOptPass<'a> {
|
||||
fn optimize(&mut self, block: Block, body: &mut FunctionBody) {
|
||||
if block != body.entry {
|
||||
if self.options.redundant_blockparams && block != body.entry {
|
||||
// Pass over blockparams, checking all inputs. If all inputs
|
||||
// resolve to the same SSA value, remove the blockparam and
|
||||
// make it an alias of that value. If all inputs resolve to
|
||||
|
@ -126,6 +149,10 @@ impl<'a> GVNPass<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
if !const_insts_to_insert.is_empty() || !blockparams_to_remove.is_empty() {
|
||||
self.changed = true;
|
||||
}
|
||||
|
||||
for inst in const_insts_to_insert {
|
||||
body.blocks[block].insts.insert(0, inst);
|
||||
}
|
||||
|
@ -154,71 +181,81 @@ impl<'a> GVNPass<'a> {
|
|||
&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;
|
||||
let new_val = body.resolve_and_update_alias(val);
|
||||
body.arg_pool[args][i] = new_val;
|
||||
self.changed |= new_val != val;
|
||||
}
|
||||
}
|
||||
&mut ValueDef::PickOutput(ref mut val, ..) => {
|
||||
let updated = body.resolve_and_update_alias(*val);
|
||||
*val = updated;
|
||||
self.changed |= updated != *val;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Try to constant-propagate.
|
||||
if let ValueDef::Operator(op, args, ..) = &value {
|
||||
let arg_values = body.arg_pool[*args]
|
||||
.iter()
|
||||
.map(|&arg| value_is_const(arg, body))
|
||||
.collect::<Vec<_>>();
|
||||
let const_val = const_eval(op, &arg_values[..], None);
|
||||
match const_val {
|
||||
Some(ConstVal::I32(val)) => {
|
||||
value = ValueDef::Operator(
|
||||
Operator::I32Const { value: val },
|
||||
ListRef::default(),
|
||||
body.single_type_list(Type::I32),
|
||||
);
|
||||
body.values[inst] = value.clone();
|
||||
if self.options.cprop {
|
||||
if let ValueDef::Operator(op, args, ..) = &value {
|
||||
let arg_values = body.arg_pool[*args]
|
||||
.iter()
|
||||
.map(|&arg| value_is_const(arg, body))
|
||||
.collect::<Vec<_>>();
|
||||
let const_val = const_eval(op, &arg_values[..], None);
|
||||
match const_val {
|
||||
Some(ConstVal::I32(val)) => {
|
||||
value = ValueDef::Operator(
|
||||
Operator::I32Const { value: val },
|
||||
ListRef::default(),
|
||||
body.single_type_list(Type::I32),
|
||||
);
|
||||
body.values[inst] = value.clone();
|
||||
self.changed = true;
|
||||
}
|
||||
Some(ConstVal::I64(val)) => {
|
||||
value = ValueDef::Operator(
|
||||
Operator::I64Const { value: val },
|
||||
ListRef::default(),
|
||||
body.single_type_list(Type::I64),
|
||||
);
|
||||
body.values[inst] = value.clone();
|
||||
self.changed = true;
|
||||
}
|
||||
Some(ConstVal::F32(val)) => {
|
||||
value = ValueDef::Operator(
|
||||
Operator::F32Const { value: val },
|
||||
ListRef::default(),
|
||||
body.single_type_list(Type::F32),
|
||||
);
|
||||
body.values[inst] = value.clone();
|
||||
self.changed = true;
|
||||
}
|
||||
Some(ConstVal::F64(val)) => {
|
||||
value = ValueDef::Operator(
|
||||
Operator::F64Const { value: val },
|
||||
ListRef::default(),
|
||||
body.single_type_list(Type::F64),
|
||||
);
|
||||
body.values[inst] = value.clone();
|
||||
self.changed = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Some(ConstVal::I64(val)) => {
|
||||
value = ValueDef::Operator(
|
||||
Operator::I64Const { value: val },
|
||||
ListRef::default(),
|
||||
body.single_type_list(Type::I64),
|
||||
);
|
||||
body.values[inst] = value.clone();
|
||||
}
|
||||
Some(ConstVal::F32(val)) => {
|
||||
value = ValueDef::Operator(
|
||||
Operator::F32Const { value: val },
|
||||
ListRef::default(),
|
||||
body.single_type_list(Type::F32),
|
||||
);
|
||||
body.values[inst] = value.clone();
|
||||
}
|
||||
Some(ConstVal::F64(val)) => {
|
||||
value = ValueDef::Operator(
|
||||
Operator::F64Const { value: val },
|
||||
ListRef::default(),
|
||||
body.single_type_list(Type::F64),
|
||||
);
|
||||
body.values[inst] = value.clone();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// GVN: look for already-existing copies of this
|
||||
// value.
|
||||
if let Some(value) = self.map.get(&value) {
|
||||
body.set_alias(inst, *value);
|
||||
i -= 1;
|
||||
body.blocks[block].insts.remove(i);
|
||||
continue;
|
||||
if self.options.gvn {
|
||||
// GVN: look for already-existing copies of this
|
||||
// value.
|
||||
if let Some(value) = self.map.get(&value) {
|
||||
body.set_alias(inst, *value);
|
||||
i -= 1;
|
||||
body.blocks[block].insts.remove(i);
|
||||
self.changed = true;
|
||||
continue;
|
||||
}
|
||||
self.map.insert(value, inst);
|
||||
}
|
||||
|
||||
self.map.insert(value, inst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,109 +0,0 @@
|
|||
//! Remove-useless-phis (blockparams) pass.
|
||||
|
||||
use crate::cfg::CFGInfo;
|
||||
use crate::ir::*;
|
||||
|
||||
fn all_equal(mut vals: impl Iterator<Item = Value>) -> Option<Value> {
|
||||
match vals.next() {
|
||||
Some(val) if vals.all(|other_val| other_val == val) => Some(val),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn delete_indices<T: Copy>(vec: &mut Vec<T>, indices: &[usize]) {
|
||||
let mut out = 0;
|
||||
let mut indices_idx = 0;
|
||||
for i in 0..vec.len() {
|
||||
if indices_idx < indices.len() && indices[indices_idx] == i {
|
||||
indices_idx += 1;
|
||||
// Deleted!
|
||||
} else {
|
||||
if out < i {
|
||||
vec[out] = vec[i];
|
||||
}
|
||||
out += 1;
|
||||
}
|
||||
}
|
||||
if out < vec.len() {
|
||||
vec.truncate(out);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(func: &mut FunctionBody, cfg: &CFGInfo) {
|
||||
// For every block, collect the arg-lists of preds. If a given
|
||||
// blockparam has all the same values for an arg, replace the
|
||||
// blockparam value with an alias to that one value, and then
|
||||
// remove it from the blockparams and target-lists of all preds.
|
||||
|
||||
log::trace!(
|
||||
"remove_phis: running on func:\n{}\n",
|
||||
func.display_verbose("| ", None)
|
||||
);
|
||||
|
||||
let mut deleted = vec![];
|
||||
for &block in cfg.rpo.values() {
|
||||
// Skip the entry block -- we can't remove any args, because
|
||||
// there is also an implicit in-edge from the function entry
|
||||
// with arguments.
|
||||
if block == func.entry {
|
||||
continue;
|
||||
}
|
||||
|
||||
deleted.clear();
|
||||
|
||||
// Gather arg-lists from each pred's terminator.
|
||||
let mut arglists = vec![];
|
||||
for (i, &pred) in func.blocks[block].preds.iter().enumerate() {
|
||||
let pos = func.blocks[block].pos_in_pred_succ[i];
|
||||
func.blocks[pred].terminator.visit_target(pos, |target| {
|
||||
assert_eq!(target.block, block);
|
||||
assert_eq!(target.args.len(), func.blocks[block].params.len());
|
||||
arglists.push(target.args.clone());
|
||||
});
|
||||
}
|
||||
|
||||
// For each arg-position, check if all args are the same. If
|
||||
// so, rewrite value and mark index as deleted.
|
||||
for i in 0..func.blocks[block].params.len() {
|
||||
let blockparam = func.blocks[block].params[i].1;
|
||||
let same = all_equal(
|
||||
arglists
|
||||
.iter()
|
||||
.map(|arglist| func.resolve_alias(arglist[i])),
|
||||
);
|
||||
if let Some(val) = same {
|
||||
if val != blockparam {
|
||||
log::trace!(
|
||||
"deleting blockparam {} from block {}: now {}",
|
||||
blockparam,
|
||||
block,
|
||||
val
|
||||
);
|
||||
func.values[blockparam] = ValueDef::Alias(val);
|
||||
deleted.push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If anything was deleted, remove the appropriate indices in
|
||||
// the func's blockparams list and in targets' arg lists.
|
||||
if !deleted.is_empty() {
|
||||
delete_indices(&mut func.blocks[block].params, &deleted[..]);
|
||||
for i in 0..func.blocks[block].preds.len() {
|
||||
let pred = func.blocks[block].preds[i];
|
||||
let pos = func.blocks[block].pos_in_pred_succ[i];
|
||||
func.blocks[pred].terminator.update_target(pos, |target| {
|
||||
delete_indices(&mut target.args, &deleted[..]);
|
||||
});
|
||||
}
|
||||
|
||||
// Renumber blockparam values.
|
||||
for (i, &(_, param)) in func.blocks[block].params.iter().enumerate() {
|
||||
let ty = func.values[param].ty(&func.type_pool).unwrap();
|
||||
func.values[param] = ValueDef::BlockParam(block, i as u32, ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log::trace!("remove_phis: done:\n{}\n", func.display_verbose("| ", None));
|
||||
}
|
Loading…
Reference in a new issue