diff --git a/src/backend/binaryen.rs b/src/backend/binaryen.rs index ebc31e8..e061c3d 100644 --- a/src/backend/binaryen.rs +++ b/src/backend/binaryen.rs @@ -14,8 +14,8 @@ pub struct Expression(BinaryenModule, BinaryenExpression); #[derive(Clone, Copy, Debug)] pub struct Export(BinaryenModule, BinaryenExport); -type BinaryenIndex = u32; -type BinaryenType = usize; +pub type BinaryenIndex = u32; +pub type BinaryenType = usize; impl Module { pub fn read(data: &[u8]) -> Result { @@ -423,6 +423,8 @@ pub type BinaryenModule = *const c_void; type BinaryenFunction = *const c_void; type BinaryenExpression = *const c_void; type BinaryenExport = *const c_void; +type BinaryenRelooper = *const c_void; +type BinaryenRelooperBlock = *const c_void; #[repr(C)] struct BinaryenModuleAllocateAndWriteResult { @@ -440,6 +442,74 @@ impl Drop for BinaryenModuleAllocateAndWriteResult { } } +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) }; + Relooper(module.0, ptr) + } + + pub fn construct(self, entry: RelooperBlock, index_var: usize) -> Expression { + let module = self.0; + let expr = unsafe { RelooperRenderAndDispose(self.1, entry.0, index_var as BinaryenIndex) }; + std::mem::forget(self); + Expression(module, expr) + } + + pub fn add_block(&mut self, expr: Expression) -> RelooperBlock { + 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) }) + } +} + +impl RelooperBlock { + pub fn cond_branch(&self, to: RelooperBlock, cond: Expression, edge: Expression) { + unsafe { + RelooperAddBranch(self.0, to.0, cond.1, edge.1); + } + } + + pub fn branch(&self, to: RelooperBlock, edge: Expression) { + unsafe { + RelooperAddBranch(self.0, to.0, std::ptr::null(), edge.1); + } + } +} + +impl RelooperBlockWithSwitch { + pub fn switch(&self, to: RelooperBlock, edge: Expression, indices: &[BinaryenIndex]) { + unsafe { + RelooperAddBranchForSwitch( + self.0, + to.0, + indices.as_ptr(), + indices.len() as BinaryenIndex, + edge.1, + ); + } + } +} + +impl Drop for Relooper { + fn drop(&mut self) { + panic!("Relooper dropped without constructing/disposing"); + } +} + #[link(name = "binaryen")] extern "C" { fn BinaryenModuleRead(data: *const u8, len: usize) -> BinaryenModule; @@ -613,6 +683,32 @@ extern "C" { fn BinaryenLiteralInt64(x: i64) -> BinaryenLiteral; fn BinaryenLiteralFloat32Bits(x: i32) -> BinaryenLiteral; fn BinaryenLiteralFloat64Bits(x: i64) -> BinaryenLiteral; + + fn RelooperCreate(module: BinaryenModule) -> BinaryenRelooper; + fn RelooperRenderAndDispose( + r: BinaryenRelooper, + entry: BinaryenRelooperBlock, + labelVar: BinaryenIndex, + ) -> BinaryenExpression; + fn RelooperAddBlock(r: BinaryenRelooper, code: BinaryenExpression) -> BinaryenRelooperBlock; + fn RelooperAddBranch( + from: BinaryenRelooperBlock, + to: BinaryenRelooperBlock, + cond: BinaryenExpression, + edge_code: BinaryenExpression, + ); + fn RelooperAddBlockWithSwitch( + r: BinaryenRelooper, + code: BinaryenExpression, + selector: BinaryenExpression, + ) -> BinaryenRelooperBlock; + fn RelooperAddBranchForSwitch( + from: BinaryenRelooperBlock, + to: BinaryenRelooperBlock, + indices: *const BinaryenIndex, + n_indices: BinaryenIndex, + edge_code: BinaryenExpression, + ); } #[repr(C)] diff --git a/src/backend/lower.rs b/src/backend/lower.rs index 73d4873..8025146 100644 --- a/src/backend/lower.rs +++ b/src/backend/lower.rs @@ -1,5 +1,6 @@ use crate::backend::binaryen; use crate::ir::*; +use crate::Operator; use fxhash::FxHashMap; use wasmparser::Type; @@ -10,76 +11,148 @@ pub(crate) fn generate_body( body: &FunctionBody, into_mod: &mut binaryen::Module, ) -> (Vec, binaryen::Expression) { + let mut ctx = ElabCtx::new(body, into_mod); + // For each block, generate an expr. let mut block_exprs: FxHashMap = FxHashMap::default(); - let mut ctx = ElabCtx::new(body, into_mod); for block in body.blocks() { let exprs = body[block] .insts .iter() - .map(|&inst| { + .flat_map(|&inst| { let inst = body.resolve_alias(inst); - elaborate_value(body, into_mod, &mut ctx, inst) + ctx.elaborate_value(into_mod, inst) }) - .collect::>(); + .collect::>(); block_exprs.insert(block, binaryen::Expression::block(into_mod, &exprs[..])); } - todo!() + // 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 {} + + let index_var = ctx.new_local(Type::I32); + let expr = relooper.construct(entry.unwrap(), index_var as usize); + (ctx.new_locals, expr) } #[derive(Clone, Debug)] -struct ElabCtx { - value_to_expr: FxHashMap, - block_params: FxHashMap<(Block, usize), LocalId>, +struct ElabCtx<'a> { + body: &'a FunctionBody, + op_result_locals: FxHashMap<(Value, usize), LocalId>, + block_param_locals: FxHashMap<(BlockId, usize), LocalId>, new_locals: Vec, } -impl ElabCtx { - fn new(body: &FunctionBody, into_mod: &mut binaryen::Module) -> ElabCtx { +impl<'a> ElabCtx<'a> { + fn new(body: &'a FunctionBody, into_mod: &mut binaryen::Module) -> ElabCtx<'a> { // Create locals for each blockparam. - let mut block_params = FxHashMap::default(); - let mut next_local = body.locals.len() as LocalId; - let mut new_locals = vec![]; + let mut this = ElabCtx { + body, + op_result_locals: FxHashMap::default(), + block_param_locals: FxHashMap::default(), + new_locals: vec![], + }; + for block in body.blocks() { - for &(ty, param) in &body[block].params { - let new_local = next_local; - next_local += 1; - block_params.insert((ty, param), new_local); - new_locals.push(ty); + for &(ty, param) in &body[block].params {} + } + + // Create locals for each Operator value and each blockparam. + for (value, def) in body.values() { + match def { + &ValueDef::Operator(_, _, ref tys) => { + for (i, ty) in tys.iter().copied().enumerate() { + let local = this.new_local(ty); + this.op_result_locals.insert((value, i), local); + } + } + &ValueDef::BlockParam(block, index, ty) => { + let local = this.new_local(ty); + this.block_param_locals.insert((block, index), local); + } + _ => {} } } - ElabCtx { - value_to_expr: FxHashMap::default(), - block_params, - new_locals, + this + } + + fn elaborate_value( + &mut self, + into_mod: &binaryen::Module, + value: Value, + ) -> Option { + let value = self.body.resolve_alias(value); + + match &self.body[value] { + &ValueDef::Operator(op, ref args, ref tys) => { + // Get expressions for each arg. + let args = args.iter().map(|&arg| self.get_val_local(arg)); + // Create `get_local` expressions for each arg. + let binaryen_args = args + .map(|arg_local| into_mod.expr_local_get(arg_local, self.local_ty(arg_local))) + .collect::>(); + // Create operator. + let expr = self.create_binaryen_op(op, binaryen_args, tys); + + // Set local(s) as appropriate. + if tys.len() == 0 { + // Nothing. Create a `drop` expr that wraps the actual operator. + Some(into_mod.expr_drop(expr)) + } else if tys.len() == 1 { + // Set value directly. + let local = self.get_val_local(value); + Some(into_mod.expr_local_set(local, expr)) + } else { + todo!("support multivalue") + } + } + _ => None, } } -} -fn elaborate_value( - body: &FunctionBody, - into_mod: &binaryen::Module, - ctx: &mut ElabCtx, - value: Value, -) -> binaryen::Expression { - let value = body.resolve_alias(value); - if let Some(expr) = ctx.value_to_expr.get(&value) { - return *expr; + fn get_val_local(&self, value: Value) -> LocalId { + match &self.body[value] { + &ValueDef::Arg(idx, _) => idx as LocalId, + &ValueDef::BlockParam(block, idx, _) => { + self.block_param_locals.get(&(block, idx)).copied().unwrap() + } + &ValueDef::Operator(..) => self.op_result_locals.get(&(value, 0)).copied().unwrap(), + &ValueDef::PickOutput(value, idx, _) => { + self.op_result_locals.get(&(value, idx)).copied().unwrap() + } + &ValueDef::Alias(val) => self.get_val_local(val), + &ValueDef::Placeholder(_) => unreachable!(), + } } - match &body[value] { - &ValueDef::BlockParam(block, idx) => {} - &ValueDef::Arg(idx) => {} - &ValueDef::PickOutput(value, idx) => {} - &ValueDef::Operator(op, ref args) => {} - - &ValueDef::Alias(_) => unreachable!(), - &ValueDef::Placeholder => unreachable!(), + fn new_local(&mut self, ty: Type) -> LocalId { + let index = (self.body.locals.len() + self.new_locals.len()) as LocalId; + self.new_locals.push(ty); + index } - todo!() + fn create_binaryen_op( + &mut self, + op: Operator, + args: Vec, + tys: &[Type], + ) -> binaryen::Expression { + todo!() + } + + fn local_ty(&self, local: LocalId) -> Type { + let index = local as usize; + self.body + .locals + .get(index) + .copied() + .unwrap_or_else(|| self.new_locals[index - self.body.locals.len()]) + } } pub(crate) fn create_new_func( diff --git a/src/frontend.rs b/src/frontend.rs index 25ab17e..6612116 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -128,7 +128,7 @@ fn parse_body<'a>( for (arg_idx, &arg_ty) in module.signature(my_sig).params.iter().enumerate() { let local_idx = arg_idx as LocalId; - let value = builder.body.add_value(ValueDef::Arg(arg_idx), vec![arg_ty]); + let value = builder.body.add_value(ValueDef::Arg(arg_idx, arg_ty)); trace!("defining local {} to value {}", local_idx, value); builder.locals.declare(local_idx, arg_ty); builder.locals.set(local_idx, value); @@ -155,7 +155,7 @@ fn parse_body<'a>( assert!(builder.locals.is_sealed(block)); } for value in &builder.body.values { - assert!(value != &ValueDef::Placeholder); + assert!(!matches!(value, &ValueDef::Placeholder(_))); } trace!("Final function body:{:?}", ret); @@ -291,32 +291,30 @@ impl LocalTracker { fn create_default_value(&mut self, body: &mut FunctionBody, ty: Type) -> Value { match ty { - Type::I32 => body.add_value( - ValueDef::Operator(Operator::I32Const { value: 0 }, vec![]), + Type::I32 => body.add_value(ValueDef::Operator( + Operator::I32Const { value: 0 }, + vec![], vec![ty], - ), - Type::I64 => body.add_value( - ValueDef::Operator(Operator::I64Const { value: 0 }, vec![]), + )), + Type::I64 => body.add_value(ValueDef::Operator( + Operator::I64Const { value: 0 }, + vec![], vec![ty], - ), - Type::F32 => body.add_value( - ValueDef::Operator( - Operator::F32Const { - value: Ieee32::from_bits(0), - }, - vec![], - ), + )), + Type::F32 => body.add_value(ValueDef::Operator( + Operator::F32Const { + value: Ieee32::from_bits(0), + }, + vec![], vec![ty], - ), - Type::F64 => body.add_value( - ValueDef::Operator( - Operator::F64Const { - value: Ieee64::from_bits(0), - }, - vec![], - ), + )), + Type::F64 => body.add_value(ValueDef::Operator( + Operator::F64Const { + value: Ieee64::from_bits(0), + }, + vec![], vec![ty], - ), + )), _ => todo!("unsupported type: {:?}", ty), } } @@ -1180,7 +1178,7 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> { let value = self .body - .add_value(ValueDef::Operator(op, input_operands), outputs.clone()); + .add_value(ValueDef::Operator(op, input_operands, outputs.clone())); log::trace!(" -> value: {:?}", value); if let Some(block) = self.cur_block { @@ -1194,7 +1192,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), vec![output_ty]); + .add_value(ValueDef::PickOutput(value, i, output_ty)); self.op_stack.push((output_ty, pick)); log::trace!(" -> pick {}: {:?} ty {:?}", i, pick, output_ty); } diff --git a/src/ir.rs b/src/ir.rs index 7a3d8d0..0d455a7 100644 --- a/src/ir.rs +++ b/src/ir.rs @@ -110,9 +110,6 @@ pub struct FunctionBody { pub blocks: Vec, /// Value definitions, indexed by `Value`. pub values: Vec, - /// Types, indexed by `Value`. A single value can have multiple - /// types if multi-value (e.g. a call). - pub types: Vec>, } impl FunctionBody { @@ -134,12 +131,11 @@ impl FunctionBody { log::trace!("add_edge: from {} to {}", from, to); } - pub fn add_value(&mut self, value: ValueDef, tys: Vec) -> Value { - log::trace!("add_value: def {:?} ty {:?}", value, tys); + pub fn add_value(&mut self, value: ValueDef) -> Value { + log::trace!("add_value: def {:?}", value); let id = Value(self.values.len() as u32); log::trace!(" -> value {:?}", id); self.values.push(value.clone()); - self.types.push(tys); id } @@ -166,30 +162,31 @@ impl FunctionBody { result } - pub fn add_mutable_inst(&mut self, tys: Vec, def: ValueDef) -> Value { + pub fn add_mutable_inst(&mut self, def: ValueDef) -> Value { let value = Value(self.values.len() as u32); - self.types.push(tys); self.values.push(def); value } pub fn add_blockparam(&mut self, block: BlockId, ty: Type) -> Value { let index = self.blocks[block].params.len(); - let value = self.add_value(ValueDef::BlockParam(block, index), vec![ty]); + let value = self.add_value(ValueDef::BlockParam(block, index, ty)); self.blocks[block].params.push((ty, value)); value } pub fn add_placeholder(&mut self, ty: Type) -> Value { - self.add_mutable_inst(vec![ty], ValueDef::Placeholder) + self.add_mutable_inst(ValueDef::Placeholder(ty)) } pub fn replace_placeholder_with_blockparam(&mut self, block: BlockId, value: Value) { - assert!(self.values[value.index()] == ValueDef::Placeholder); - let ty = self.types[value.index()].get(0).cloned().unwrap(); let index = self.blocks[block].params.len(); + let ty = match &self.values[value.index()] { + &ValueDef::Placeholder(ty) => ty, + _ => unreachable!(), + }; self.blocks[block].params.push((ty, value)); - self.values[value.index()] = ValueDef::BlockParam(block, index); + self.values[value.index()] = ValueDef::BlockParam(block, index, ty); } pub fn resolve_and_update_alias(&mut self, value: Value) -> Value { @@ -295,12 +292,12 @@ impl Value { #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum ValueDef { - Arg(usize), - BlockParam(BlockId, usize), - Operator(Operator, Vec), - PickOutput(Value, usize), + Arg(usize, Type), + BlockParam(BlockId, usize, Type), + Operator(Operator, Vec, Vec), + PickOutput(Value, usize, Type), Alias(Value), - Placeholder, + Placeholder(Type), } impl ValueDef { @@ -308,14 +305,14 @@ impl ValueDef { match self { &ValueDef::Arg { .. } => {} &ValueDef::BlockParam { .. } => {} - &ValueDef::Operator(_, ref args) => { + &ValueDef::Operator(_, ref args, _) => { for &arg in args { f(arg); } } &ValueDef::PickOutput(from, ..) => f(from), &ValueDef::Alias(value) => f(value), - &ValueDef::Placeholder => {} + &ValueDef::Placeholder(_) => {} } } @@ -323,14 +320,14 @@ impl ValueDef { match self { &mut ValueDef::Arg { .. } => {} &mut ValueDef::BlockParam { .. } => {} - &mut ValueDef::Operator(_, ref mut args) => { + &mut ValueDef::Operator(_, ref mut args, _) => { for arg in args { f(arg); } } &mut ValueDef::PickOutput(ref mut from, ..) => f(from), &mut ValueDef::Alias(ref mut value) => f(value), - &mut ValueDef::Placeholder => {} + &mut ValueDef::Placeholder(_) => {} } } } diff --git a/src/use_count.rs b/src/use_count.rs index c6e04a7..4cb6d68 100644 --- a/src/use_count.rs +++ b/src/use_count.rs @@ -41,7 +41,7 @@ impl UseCountAnalysis { workqueue_set.remove(&value); match &f.values[value.index()] { &ValueDef::Alias(..) | &ValueDef::Arg(..) | &ValueDef::BlockParam(..) => {} - &ValueDef::Operator(_op, ref args) => { + &ValueDef::Operator(_op, ref args, _) => { for &arg in args { let arg = f.resolve_alias(arg); counts.add(arg); @@ -52,7 +52,7 @@ impl UseCountAnalysis { } } } - &ValueDef::PickOutput(value, _) => { + &ValueDef::PickOutput(value, _, _) => { let value = f.resolve_alias(value); counts.add(value); if counts.use_count[value.index()] == 1 { @@ -61,7 +61,7 @@ impl UseCountAnalysis { } } } - &ValueDef::Placeholder => { + &ValueDef::Placeholder(_) => { panic!("Unresolved placeholder for value {}", value); } }