This commit is contained in:
Chris Fallin 2021-11-14 00:00:34 -08:00
parent 3e21bb7411
commit 62fbb238d7
6 changed files with 141 additions and 24 deletions

View file

@ -26,3 +26,9 @@ name = "parse_ir"
path = "fuzz_targets/parse_ir.rs" path = "fuzz_targets/parse_ir.rs"
test = false test = false
doc = false doc = false
[[bin]]
name = "roundtrip"
path = "fuzz_targets/roundtrip.rs"
test = false
doc = false

View file

@ -0,0 +1,11 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use waffle::Module;
fuzz_target!(|module: wasm_smith::Module| {
let _ = env_logger::try_init();
let orig_bytes = module.to_bytes();
let parsed_module = Module::from_wasm_bytes(&orig_bytes[..]).unwrap();
let _ = parsed_module.to_wasm_bytes();
});

54
src/backend.rs Normal file
View file

@ -0,0 +1,54 @@
//! IR-to-Wasm transform.
use crate::ir::*;
use fxhash::{FxHashMap, FxHashSet};
pub fn treeify_function(func: &mut FunctionBody) -> FxHashSet<(BlockId, InstId)> {
// First, count uses of all values.
let mut uses: FxHashMap<(BlockId, InstId, usize), usize> = FxHashMap::default();
for block in &func.blocks {
for inst in &block.insts {
for input in &inst.inputs {
match input {
&Operand::Value(value_id) => {
if let ValueKind::Inst(src_block, src_inst, idx) =
&func.values[value_id].kind
{
*uses.entry((*src_block, *src_inst, *idx)).or_insert(0) += 1;
}
}
_ => {}
}
}
}
for arg in block.terminator.args() {
match arg {
Operand::Value(value_id) => {
if let ValueKind::Inst(src_block, src_inst, idx) = &func.values[value_id].kind {
*uses.entry((*src_block, *src_inst, *idx)).or_insert(0) += 1;
}
}
_ => {}
}
}
}
// Next, treeify all insts with only one use.
let mut single_use_insts: FxHashSet<(BlockId, InstId)> = FxHashSet::default();
for (block_idx, block) in func.blocks.iter().enumerate() {
for (inst_idx, inst) in block.insts.iter().enumerate() {
let all_one_use = (0..inst.outputs.len()).all(|output| {
uses.get(&(block_idx, inst_idx, output))
.cloned()
.unwrap_or(0)
<= 1
});
if all_one_use {
single_use_insts.insert((block_idx, inst_idx));
}
}
}
single_use_insts
}

View file

@ -14,6 +14,7 @@ use wasmparser::{
pub fn wasm_to_ir(bytes: &[u8]) -> Result<Module<'_>> { pub fn wasm_to_ir(bytes: &[u8]) -> Result<Module<'_>> {
let mut module = Module::default(); let mut module = Module::default();
module.orig_bytes = bytes;
let parser = Parser::new(0); let parser = Parser::new(0);
let mut next_func = 0; let mut next_func = 0;
for payload in parser.parse_all(bytes) { for payload in parser.parse_all(bytes) {
@ -770,7 +771,7 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> {
&self.ctrl_stack[self.ctrl_stack.len() - 1 - relative_depth as usize] &self.ctrl_stack[self.ctrl_stack.len() - 1 - relative_depth as usize]
} }
fn fill_block_params_with_locals(&mut self, target: BlockId, args: &mut Vec<Operand<'a>>) { fn fill_block_params_with_locals(&mut self, target: BlockId, args: &mut Vec<Operand>) {
if !self.block_param_locals.contains_key(&target) { if !self.block_param_locals.contains_key(&target) {
let mut keys: Vec<LocalId> = self.locals.keys().cloned().collect(); let mut keys: Vec<LocalId> = self.locals.keys().cloned().collect();
keys.sort(); keys.sort();
@ -789,7 +790,7 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> {
fn emit_branch(&mut self, target: BlockId, args: &[ValueId]) { fn emit_branch(&mut self, target: BlockId, args: &[ValueId]) {
if let Some(block) = self.cur_block { if let Some(block) = self.cur_block {
let mut args: Vec<Operand<'a>> = args.iter().map(|&val| Operand::value(val)).collect(); let mut args: Vec<Operand> = args.iter().map(|&val| Operand::value(val)).collect();
self.fill_block_params_with_locals(target, &mut args); self.fill_block_params_with_locals(target, &mut args);
let target = BlockTarget { let target = BlockTarget {
block: target, block: target,
@ -840,7 +841,7 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> {
args: &[ValueId], args: &[ValueId],
) { ) {
if let Some(block) = self.cur_block { if let Some(block) = self.cur_block {
let args: Vec<Operand<'a>> = args.iter().map(|&arg| Operand::value(arg)).collect(); let args: Vec<Operand> = args.iter().map(|&arg| Operand::value(arg)).collect();
let targets = indexed_targets let targets = indexed_targets
.iter() .iter()
.map(|&block| { .map(|&block| {

View file

@ -1,6 +1,6 @@
//! Intermediate representation for Wasm. //! Intermediate representation for Wasm.
use crate::frontend; use crate::{backend, frontend};
use anyhow::Result; use anyhow::Result;
use wasmparser::{FuncType, Operator, Type}; use wasmparser::{FuncType, Operator, Type};
@ -15,6 +15,7 @@ pub const NO_VALUE: ValueId = usize::MAX;
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct Module<'a> { pub struct Module<'a> {
pub orig_bytes: &'a [u8],
pub funcs: Vec<FuncDecl<'a>>, pub funcs: Vec<FuncDecl<'a>>,
pub signatures: Vec<FuncType>, pub signatures: Vec<FuncType>,
pub globals: Vec<Type>, pub globals: Vec<Type>,
@ -61,28 +62,26 @@ pub enum ValueKind {
pub struct Block<'a> { pub struct Block<'a> {
pub params: Vec<Type>, pub params: Vec<Type>,
pub insts: Vec<Inst<'a>>, pub insts: Vec<Inst<'a>>,
pub terminator: Terminator<'a>, pub terminator: Terminator,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Inst<'a> { pub struct Inst<'a> {
pub operator: Operator<'a>, pub operator: Operator<'a>,
pub outputs: Vec<ValueId>, pub outputs: Vec<ValueId>,
pub inputs: Vec<Operand<'a>>, pub inputs: Vec<Operand>,
} }
#[derive(Clone, Debug)] #[derive(Clone, Copy, Debug)]
pub enum Operand<'a> { pub enum Operand {
/// An SSA value. /// An SSA value.
Value(ValueId), Value(ValueId),
/// Tree-ified instructions for Wasm emission.
Sub(Box<Inst<'a>>),
/// Undef values are produced when code is unreachable and thus /// Undef values are produced when code is unreachable and thus
/// removed/never executed. /// removed/never executed.
Undef, Undef,
} }
impl<'a> Operand<'a> { impl Operand {
pub fn value(value: ValueId) -> Self { pub fn value(value: ValueId) -> Self {
if value == NO_VALUE { if value == NO_VALUE {
Operand::Undef Operand::Undef
@ -93,45 +92,90 @@ impl<'a> Operand<'a> {
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct BlockTarget<'a> { pub struct BlockTarget {
pub block: BlockId, pub block: BlockId,
pub args: Vec<Operand<'a>>, pub args: Vec<Operand>,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Terminator<'a> { pub enum Terminator {
Br { Br {
target: BlockTarget<'a>, target: BlockTarget,
}, },
CondBr { CondBr {
cond: Operand<'a>, cond: Operand,
if_true: BlockTarget<'a>, if_true: BlockTarget,
if_false: BlockTarget<'a>, if_false: BlockTarget,
}, },
Select { Select {
value: Operand<'a>, value: Operand,
targets: Vec<BlockTarget<'a>>, targets: Vec<BlockTarget>,
default: BlockTarget<'a>, default: BlockTarget,
}, },
Return { Return {
values: Vec<Operand<'a>>, values: Vec<Operand>,
}, },
None, None,
} }
impl<'a> std::default::Default for Terminator<'a> { impl std::default::Default for Terminator {
fn default() -> Self { fn default() -> Self {
Terminator::None Terminator::None
} }
} }
impl Terminator {
pub fn args(&self) -> Vec<Operand> {
match self {
Terminator::Br { target } => target.args.clone(),
Terminator::CondBr {
cond,
if_true,
if_false,
} => {
let mut ret = vec![*cond];
ret.extend(if_true.args.iter().cloned());
ret.extend(if_false.args.iter().cloned());
ret
}
Terminator::Select {
value,
targets,
default,
} => {
let mut ret = vec![*value];
for target in targets {
ret.extend(target.args.iter().cloned());
}
ret.extend(default.args.clone());
ret
}
Terminator::Return { values } => values.clone(),
Terminator::None => vec![],
}
}
}
impl<'a> Module<'a> { impl<'a> Module<'a> {
pub fn from_wasm_bytes(bytes: &'a [u8]) -> Result<Self> { pub fn from_wasm_bytes(bytes: &'a [u8]) -> Result<Self> {
frontend::wasm_to_ir(bytes) frontend::wasm_to_ir(bytes)
} }
pub fn to_wasm_bytes(mut self) -> Vec<u8> {
// TODO
for func in &mut self.funcs {
match func {
&mut FuncDecl::Body(_, ref mut body) => {
let _deleted_insts = backend::treeify_function(body);
}
_ => {}
}
}
self.orig_bytes.to_vec()
}
} }
impl<'a> Terminator<'a> { impl Terminator {
pub fn successors(&self) -> Vec<BlockId> { pub fn successors(&self) -> Vec<BlockId> {
match self { match self {
Terminator::Return { .. } => vec![], Terminator::Return { .. } => vec![],

View file

@ -7,6 +7,7 @@
pub use wasm_encoder; pub use wasm_encoder;
pub use wasmparser; pub use wasmparser;
mod backend;
mod dataflow; mod dataflow;
mod frontend; mod frontend;
mod ir; mod ir;