diff --git a/src/backend/binaryen.rs b/src/backend/binaryen.rs index e061c3d..385aa8c 100644 --- a/src/backend/binaryen.rs +++ b/src/backend/binaryen.rs @@ -1,5 +1,7 @@ //! Binaryen bindings. +use crate::entity::EntityRef; +use crate::ir; use anyhow::{bail, Result}; use lazy_static::lazy_static; use libc::{c_char, c_void}; @@ -118,14 +120,14 @@ impl Function { pub fn create( module: &mut Module, - params: impl Iterator, - results: impl Iterator, - locals: impl Iterator, + params: impl Iterator, + results: impl Iterator, + locals: impl Iterator, body: Expression, ) -> Function { let params = tys_to_binaryen(params); let results = tys_to_binaryen(results); - let locals: Vec = locals.map(|ty| Type::from(ty).to_kind()).collect(); + let locals: Vec = locals.map(|ty| Type::from(ty).to_binaryen()).collect(); let ptr = unsafe { BinaryenAddFunc( module.0, @@ -140,8 +142,8 @@ impl Function { Function(module.0, ptr) } - pub fn add_local(&mut self, ty: wasmparser::Type) -> usize { - (unsafe { BinaryenFunctionAddVar(self.1, Type::from(ty).to_kind()) }) as usize + pub fn add_local(&mut self, ty: ir::Type) -> usize { + (unsafe { BinaryenFunctionAddVar(self.1, Type::from(ty).to_binaryen()) }) as usize } } @@ -264,7 +266,7 @@ pub enum Type { } impl Type { - fn from_kind(kind: BinaryenType) -> Option { + fn from_binaryen(kind: BinaryenType) -> Option { let tys = &*TYPE_IDS; if kind == tys.none_t { Some(Type::None) @@ -281,7 +283,7 @@ impl Type { } } - pub(crate) fn to_kind(&self) -> BinaryenType { + pub(crate) fn to_binaryen(&self) -> BinaryenType { let tys = &*TYPE_IDS; match self { &Type::None => tys.none_t, @@ -294,21 +296,21 @@ impl Type { } } -impl From for Type { - fn from(ty: wasmparser::Type) -> Self { +impl From for Type { + fn from(ty: ir::Type) -> Self { match ty { - wasmparser::Type::I32 => Type::I32, - wasmparser::Type::I64 => Type::I64, - wasmparser::Type::F32 => Type::F32, - wasmparser::Type::F64 => Type::F64, - wasmparser::Type::V128 => Type::V128, + ir::Type::I32 => Type::I32, + ir::Type::I64 => Type::I64, + ir::Type::F32 => Type::F32, + ir::Type::F64 => Type::F64, + ir::Type::V128 => Type::V128, _ => unimplemented!(), } } } -pub fn tys_to_binaryen(tys: impl Iterator) -> BinaryenType { - let tys: Vec = tys.map(|ty| Type::from(ty).to_kind()).collect(); +pub fn tys_to_binaryen(tys: impl Iterator) -> BinaryenType { + let tys: Vec = tys.map(|ty| Type::from(ty).to_binaryen()).collect(); unsafe { BinaryenTypeCreate(tys.as_ptr(), tys.len() as BinaryenIndex) } } @@ -322,7 +324,7 @@ fn name_to_string(name: *const c_char) -> Option { impl Expression { pub fn ty(&self) -> Type { - Type::from_kind(unsafe { BinaryenExpressionGetType(self.1) }).unwrap() + Type::from_binaryen(unsafe { BinaryenExpressionGetType(self.1) }).unwrap() } pub fn deep_clone(&self) -> Self { @@ -345,6 +347,44 @@ impl Expression { ) }) } + + pub fn block_append_child(&mut self, child: Expression) { + unsafe { + BinaryenBlockAppendChild(self.1, child.1); + } + } + + pub fn unreachable(module: &Module) -> Expression { + Expression(module.0, unsafe { BinaryenUnreachable(module.0) }) + } + + pub fn local_get(module: &Module, local: ir::Local, ty: ir::Type) -> Expression { + let local = local.index() as BinaryenIndex; + let ty = Type::from(ty).to_binaryen(); + let expr = unsafe { BinaryenLocalGet(module.0, local, ty) }; + Expression(module.0, expr) + } + + pub fn local_set(module: &Module, local: ir::Local, value: Expression) -> Expression { + let local = local.index() as BinaryenIndex; + let expr = unsafe { BinaryenLocalSet(module.0, local, value.1) }; + Expression(module.0, expr) + } + + pub fn ret(module: &Module, values: &[Expression]) -> Expression { + let expr = if values.len() == 0 { + unsafe { BinaryenReturn(module.0, std::ptr::null()) } + } else if values.len() == 1 { + unsafe { BinaryenReturn(module.0, values[0].1) } + } else { + let exprs = values.iter().map(|e| e.1).collect::>(); + let tuple = unsafe { + BinaryenTupleMake(module.0, exprs.as_ptr(), exprs.len() as BinaryenIndex) + }; + unsafe { BinaryenReturn(module.0, tuple) } + }; + Expression(module.0, expr) + } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -387,13 +427,13 @@ lazy_static! { } impl UnaryOp { - fn from_kind(kind: u32) -> UnaryOp { + fn from_binaryen(kind: u32) -> UnaryOp { UnaryOp::Other(kind) } } impl BinaryOp { - fn from_kind(kind: u32) -> BinaryOp { + fn from_binaryen(kind: u32) -> BinaryOp { let ids = &*OP_IDS; if kind == ids.i32_add { BinaryOp::I32Add @@ -447,9 +487,6 @@ pub struct Relooper(BinaryenModule, BinaryenRelooper); #[derive(Clone, Copy, PartialEq, Eq)] pub struct RelooperBlock(BinaryenRelooperBlock); -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct RelooperBlockWithSwitch(BinaryenRelooperBlock); - impl Relooper { pub fn new(module: &Module) -> Relooper { let ptr = unsafe { RelooperCreate(module.0) }; @@ -467,12 +504,8 @@ impl Relooper { RelooperBlock(unsafe { RelooperAddBlock(self.1, expr.1) }) } - pub fn add_block_with_switch( - &mut self, - expr: Expression, - sel: Expression, - ) -> RelooperBlockWithSwitch { - RelooperBlockWithSwitch(unsafe { RelooperAddBlockWithSwitch(self.1, expr.1, sel.1) }) + pub fn add_block_with_switch(&mut self, expr: Expression, sel: Expression) -> RelooperBlock { + RelooperBlock(unsafe { RelooperAddBlockWithSwitch(self.1, expr.1, sel.1) }) } } @@ -488,9 +521,7 @@ impl RelooperBlock { RelooperAddBranch(self.0, to.0, std::ptr::null(), edge.1); } } -} -impl RelooperBlockWithSwitch { pub fn switch(&self, to: RelooperBlock, edge: Expression, indices: &[BinaryenIndex]) { unsafe { RelooperAddBranchForSwitch( @@ -548,6 +579,10 @@ extern "C" { fn BinaryenBlockGetNumChildren(ptr: BinaryenExpression) -> u32; fn BinaryenBlockGetChildAt(ptr: BinaryenExpression, index: u32) -> BinaryenExpression; fn BinaryenBlockGetName(ptr: BinaryenExpression) -> *const c_char; + fn BinaryenBlockAppendChild( + ptr: BinaryenExpression, + child: BinaryenExpression, + ) -> BinaryenIndex; fn BinaryenLoopGetBody(ptr: BinaryenExpression) -> BinaryenExpression; fn BinaryenLoopGetName(ptr: BinaryenExpression) -> *const c_char; @@ -654,6 +689,12 @@ extern "C" { fn BinaryenShrSInt32() -> u32; fn BinaryenConst(module: BinaryenModule, lit: BinaryenLiteral) -> BinaryenExpression; + fn BinaryenUnreachable(module: BinaryenModule) -> BinaryenExpression; + fn BinaryenLocalGet( + module: BinaryenModule, + local: BinaryenIndex, + ty: BinaryenType, + ) -> BinaryenExpression; fn BinaryenLocalSet( module: BinaryenModule, index: u32, @@ -666,6 +707,12 @@ extern "C" { n_children: BinaryenIndex, ty: BinaryenType, ) -> BinaryenExpression; + fn BinaryenTupleMake( + module: BinaryenModule, + operands: *const BinaryenExpression, + n_operands: BinaryenIndex, + ) -> BinaryenExpression; + fn BinaryenReturn(module: BinaryenModule, expr: BinaryenExpression) -> BinaryenExpression; fn BinaryenAddFunc( module: BinaryenModule, diff --git a/src/backend/lower.rs b/src/backend/lower.rs index 309b75b..4db2ab9 100644 --- a/src/backend/lower.rs +++ b/src/backend/lower.rs @@ -1,10 +1,9 @@ -/* use crate::backend::binaryen; use crate::entity::EntityRef; use crate::ir::*; use crate::Operator; use fxhash::FxHashMap; -use wasmparser::Type; +use std::collections::BTreeMap; /// Creates a body expression for a function. Returns that expression, /// and new locals (as their types) that were created as temporaries @@ -16,36 +15,133 @@ pub(crate) fn generate_body( let mut ctx = ElabCtx::new(body, into_mod); // For each block, generate an expr. - let mut block_exprs: FxHashMap = FxHashMap::default(); + let mut block_exprs: BTreeMap = BTreeMap::default(); for (block_id, block) in body.blocks.entries() { - let exprs = block - .insts - .iter() - .flat_map(|&inst| { - let inst = body.resolve_alias(inst); - ctx.elaborate_value(into_mod, inst) - }) - .collect::>(); + let mut exprs = vec![]; + for (i, (ty, _param)) in block.params.iter().enumerate() { + let val = binaryen::Expression::local_get( + into_mod, + *ctx.block_param_next_locals.get(&(block_id, i)).unwrap(), + *ty, + ); + let set = binaryen::Expression::local_set( + into_mod, + *ctx.block_param_locals.get(&(block_id, i)).unwrap(), + val, + ); + exprs.push(set); + } + for &inst in &block.insts { + let inst = body.resolve_alias(inst); + if let Some(expr) = ctx.elaborate_value(into_mod, inst) { + exprs.push(expr); + } + } block_exprs.insert(block_id, binaryen::Expression::block(into_mod, &exprs[..])); } // Combine blocks into a single body expression, using the // relooper/stackifier support built into Binaryen. let mut relooper = binaryen::Relooper::new(into_mod); - let mut entry = None; - let mut relooper_blocks: FxHashMap = FxHashMap::default(); - for (block_id, block_expr) in block_exprs {} + + // Create the blocks. + let mut relooper_blocks: FxHashMap = + FxHashMap::default(); + for (block_id, block_expr) in &mut block_exprs { + let block = match &body.blocks[*block_id].terminator { + &Terminator::Select { value, .. } => { + let sel = ctx.get_val(value, into_mod); + relooper.add_block_with_switch(block_expr.clone(), sel) + } + _ => relooper.add_block(block_expr.clone()), + }; + relooper_blocks.insert(*block_id, (block_expr.clone(), block)); + } + + // Add edges. + for &block_id in block_exprs.keys() { + let (mut block_expr, block) = relooper_blocks.get(&block_id).unwrap().clone(); + match &body.blocks[block_id].terminator { + &Terminator::Br { ref target } => { + let (target_block, edge) = build_ssa_edge(&ctx, target, &relooper_blocks, into_mod); + block.branch(target_block, edge); + } + &Terminator::CondBr { + cond, + ref if_true, + ref if_false, + } => { + let (true_block, true_edge) = + build_ssa_edge(&ctx, if_true, &relooper_blocks, into_mod); + let (false_block, false_edge) = + build_ssa_edge(&ctx, if_false, &relooper_blocks, into_mod); + let cond = ctx.get_val(cond, into_mod); + block.cond_branch(true_block, cond, true_edge); + block.branch(false_block, false_edge); + } + &Terminator::Select { + value: _, + ref targets, + ref default, + } => { + for (i, target) in targets.iter().enumerate() { + let (target_block, edge) = + build_ssa_edge(&ctx, target, &relooper_blocks, into_mod); + block.switch(target_block, edge, &[i as u32]); + } + let (target_block, edge) = + build_ssa_edge(&ctx, default, &relooper_blocks, into_mod); + block.switch(target_block, edge, &[]); + } + &Terminator::Return { ref values } => { + let values = values + .iter() + .map(|value| ctx.get_val(*value, into_mod)) + .collect::>(); + block_expr.block_append_child(binaryen::Expression::ret(into_mod, &values[..])); + } + &Terminator::Unreachable | &Terminator::None => { + block_expr.block_append_child(binaryen::Expression::unreachable(into_mod)); + } + } + } let index_var = ctx.new_local(Type::I32); - let expr = relooper.construct(entry.unwrap(), index_var.index()); + let entry = relooper_blocks.get(&ctx.body.entry).unwrap().1.clone(); + let expr = relooper.construct(entry, index_var.index()); (ctx.new_locals, expr) } +fn build_ssa_edge( + ctx: &ElabCtx<'_>, + target: &BlockTarget, + blocks: &FxHashMap, + into_mod: &mut binaryen::Module, +) -> (binaryen::RelooperBlock, binaryen::Expression) { + // Copy all block args to the "next" locals. Build an edge block + // with these get-set pairs. + let mut sets = vec![]; + for (i, arg) in target.args.iter().enumerate() { + let value = ctx.get_val(*arg, into_mod); + let set = binaryen::Expression::local_set( + into_mod, + *ctx.block_param_next_locals.get(&(target.block, i)).unwrap(), + value, + ); + sets.push(set); + } + + let edge_block = binaryen::Expression::block(into_mod, &sets[..]); + let block = blocks.get(&target.block).unwrap().1.clone(); + (block, edge_block) +} + #[derive(Clone, Debug)] struct ElabCtx<'a> { body: &'a FunctionBody, op_result_locals: FxHashMap<(Value, usize), Local>, block_param_locals: FxHashMap<(Block, usize), Local>, + block_param_next_locals: FxHashMap<(Block, usize), Local>, new_locals: Vec, } @@ -93,7 +189,6 @@ impl<'a> ElabCtx<'a> { fn get_val_local(&self, value: Value) -> Local { match &self.body.values[value] { - &ValueDef::Arg(idx, _) => Local::new(idx), &ValueDef::BlockParam(block, idx, _) => { self.block_param_locals.get(&(block, idx)).copied().unwrap() } @@ -102,10 +197,15 @@ impl<'a> ElabCtx<'a> { self.op_result_locals.get(&(value, idx)).copied().unwrap() } &ValueDef::Alias(val) => self.get_val_local(val), - &ValueDef::Placeholder(_) => unreachable!(), + &ValueDef::Placeholder(_) | &ValueDef::None => unreachable!(), } } + fn get_val(&self, value: Value, into_mod: &mut binaryen::Module) -> binaryen::Expression { + let local = self.get_val_local(value); + binaryen::Expression::local_get(into_mod, local, self.body.values[value].ty().unwrap()) + } + fn new_local(&mut self, ty: Type) -> Local { let index = Local::new(self.body.locals.len() + self.new_locals.len()); self.new_locals.push(ty); @@ -152,4 +252,3 @@ pub(crate) fn create_new_func( body_expr, ); } -*/ diff --git a/src/ir/value.rs b/src/ir/value.rs index 8a6df73..df46141 100644 --- a/src/ir/value.rs +++ b/src/ir/value.rs @@ -13,6 +13,17 @@ pub enum ValueDef { } impl ValueDef { + pub fn ty(&self) -> 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::PickOutput(_, _, ty) => Some(ty), + &ValueDef::Placeholder(ty) => Some(ty), + _ => None, + } + } + pub fn visit_uses(&self, mut f: F) { match self { &ValueDef::BlockParam { .. } => {}