From 3de1a8e00ee6441da9bbcc1ffdb5333b31d75254 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Tue, 1 Nov 2022 20:51:18 -0700 Subject: [PATCH] WIP. --- src/ir.rs | 461 +---------------------------------------------- src/ir/func.rs | 351 ++++++++++++++++++++++++++++++++++++ src/ir/module.rs | 65 +++++++ src/ir/value.rs | 45 +++++ 4 files changed, 467 insertions(+), 455 deletions(-) create mode 100644 src/ir/func.rs create mode 100644 src/ir/module.rs create mode 100644 src/ir/value.rs diff --git a/src/ir.rs b/src/ir.rs index badb1a0..57cd133 100644 --- a/src/ir.rs +++ b/src/ir.rs @@ -1,11 +1,6 @@ //! Intermediate representation for Wasm. use crate::entity; -use crate::entity::{EntityRef, EntityVec}; -use crate::{frontend, Operator}; -use anyhow::Result; -use fxhash::FxHashSet; -use wasmparser::FuncType; pub use wasmparser::Type; @@ -18,453 +13,9 @@ entity!(Table, "table"); entity!(Memory, "memory"); entity!(Value, "value"); -#[derive(Clone, Debug, Default)] -pub struct Module<'a> { - orig_bytes: &'a [u8], - funcs: Vec, - signatures: Vec, - globals: Vec, - tables: Vec, - - dirty_funcs: FxHashSet, -} - -impl<'a> Module<'a> { - pub(crate) fn with_orig_bytes(orig_bytes: &'a [u8]) -> Module<'a> { - let mut m = Module::default(); - m.orig_bytes = orig_bytes; - m - } -} - -impl<'a> Module<'a> { - pub fn func<'b>(&'b self, id: Func) -> &'b FuncDecl { - &self.funcs[id.index()] - } - pub fn func_mut<'b>(&'b mut self, id: Func) -> &'b mut FuncDecl { - self.dirty_funcs.insert(id); - &mut self.funcs[id.index()] - } - pub fn signature<'b>(&'b self, id: Signature) -> &'b FuncType { - &self.signatures[id.index()] - } - pub fn global_ty(&self, id: Global) -> Type { - self.globals[id.index()] - } - pub fn table_ty(&self, id: Table) -> Type { - self.tables[id.index()] - } - - pub(crate) fn frontend_add_signature(&mut self, ty: FuncType) { - self.signatures.push(ty); - } - pub(crate) fn frontend_add_func(&mut self, body: FuncDecl) { - self.funcs.push(body); - } - pub(crate) fn frontend_add_table(&mut self, ty: Type) { - self.tables.push(ty); - } - pub(crate) fn frontend_add_global(&mut self, ty: Type) { - self.globals.push(ty); - } -} - -#[derive(Clone, Debug)] -pub enum FuncDecl { - Import(Signature), - Body(Signature, FunctionBody), -} - -impl FuncDecl { - pub fn sig(&self) -> Signature { - match self { - FuncDecl::Import(sig) => *sig, - FuncDecl::Body(sig, ..) => *sig, - } - } - - pub fn body(&self) -> Option<&FunctionBody> { - match self { - FuncDecl::Body(_, body) => Some(body), - _ => None, - } - } - - pub fn body_mut(&mut self) -> Option<&mut FunctionBody> { - match self { - FuncDecl::Body(_, body) => Some(body), - _ => None, - } - } -} - -#[derive(Clone, Debug, Default)] -pub struct FunctionBody { - /// How many parameters the function has. (Their types are the - /// first `n_params` values in `locals`.) - pub n_params: usize, - /// Return types of the function. - pub rets: Vec, - /// Local types, *including* args. - pub locals: EntityVec, - /// Entry block. - pub entry: Block, - /// Block bodies. - pub blocks: EntityVec, - /// Value definitions, indexed by `Value`. - pub values: EntityVec, -} - -impl FunctionBody { - pub fn add_block(&mut self) -> Block { - let id = self.blocks.push(BlockDef::default()); - log::trace!("add_block: block {}", id); - id - } - - 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(); - self.blocks[from].succs.push(to); - self.blocks[to].preds.push(from); - self.blocks[from].pos_in_succ_pred.push(pred_pos); - self.blocks[to].pos_in_pred_succ.push(succ_pos); - log::trace!("add_edge: from {} to {}", from, to); - } - - pub fn add_value(&mut self, value: ValueDef) -> Value { - log::trace!("add_value: def {:?}", value); - self.values.push(value) - } - - pub fn set_alias(&mut self, value: Value, to: Value) { - log::trace!("set_alias: value {:?} to {:?}", value, to); - // Resolve the `to` value through all existing aliases. - let to = self.resolve_and_update_alias(to); - // Disallow cycles. - if to == value { - panic!("Cannot create an alias cycle"); - } - self.values[value] = ValueDef::Alias(to); - } - - pub fn resolve_alias(&self, value: Value) -> Value { - let mut result = value; - loop { - if let &ValueDef::Alias(to) = &self.values[result] { - result = to; - } else { - break; - } - } - result - } - - pub fn add_mutable_inst(&mut self, def: ValueDef) -> Value { - let value = Value(self.values.len() as u32); - self.values.push(def); - value - } - - 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)); - self.blocks[block].params.push((ty, value)); - value - } - - pub fn add_placeholder(&mut self, ty: Type) -> Value { - self.add_mutable_inst(ValueDef::Placeholder(ty)) - } - - pub fn replace_placeholder_with_blockparam(&mut self, block: Block, value: Value) { - let index = self.blocks[block].params.len(); - let ty = match &self.values[value] { - &ValueDef::Placeholder(ty) => ty, - _ => unreachable!(), - }; - self.blocks[block].params.push((ty, value)); - self.values[value] = ValueDef::BlockParam(block, index, ty); - } - - pub fn resolve_and_update_alias(&mut self, value: Value) -> Value { - let to = self.resolve_alias(value); - // Short-circuit the chain, union-find-style. - if let &ValueDef::Alias(orig_to) = &self.values[value] { - if orig_to != to { - self.values[value] = ValueDef::Alias(to); - } - } - to - } - - pub fn append_to_block(&mut self, block: Block, value: Value) { - self.blocks[block].insts.push(value); - } - - pub fn end_block(&mut self, block: Block, terminator: Terminator) { - terminator.visit_successors(|succ| { - self.add_edge(block, succ); - }); - self.blocks[block].terminator = terminator; - } - - pub fn add_local(&mut self, ty: Type) -> Local { - self.locals.push(ty) - } -} - -#[derive(Clone, Debug, Default)] -pub struct BlockDef { - /// Instructions in this block. - pub insts: Vec, - /// Terminator: branch or return. - pub terminator: Terminator, - /// Successor blocks. - pub succs: Vec, - /// For each successor block, our index in its `preds` array. - pub pos_in_succ_pred: Vec, - /// Predecessor blocks. - pub preds: Vec, - /// For each predecessor block, our index in its `succs` array. - pub pos_in_pred_succ: Vec, - /// Type and Value for each blockparam. - pub params: Vec<(Type, Value)>, -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum ValueDef { - Arg(usize, Type), - BlockParam(Block, usize, Type), - Operator(Operator, Vec, Vec), - PickOutput(Value, usize, Type), - Alias(Value), - Placeholder(Type), -} - -impl ValueDef { - pub fn visit_uses(&self, mut f: F) { - match self { - &ValueDef::Arg { .. } => {} - &ValueDef::BlockParam { .. } => {} - &ValueDef::Operator(_, ref args, _) => { - for &arg in args { - f(arg); - } - } - &ValueDef::PickOutput(from, ..) => f(from), - &ValueDef::Alias(value) => f(value), - &ValueDef::Placeholder(_) => {} - } - } - - pub fn update_uses(&mut self, mut f: F) { - match self { - &mut ValueDef::Arg { .. } => {} - &mut ValueDef::BlockParam { .. } => {} - &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(_) => {} - } - } -} - -#[derive(Clone, Debug)] -pub struct BlockTarget { - pub block: Block, - pub args: Vec, -} - -#[derive(Clone, Debug)] -pub enum Terminator { - Br { - target: BlockTarget, - }, - CondBr { - cond: Value, - if_true: BlockTarget, - if_false: BlockTarget, - }, - Select { - value: Value, - targets: Vec, - default: BlockTarget, - }, - Return { - values: Vec, - }, - None, -} - -impl std::default::Default for Terminator { - fn default() -> Self { - Terminator::None - } -} - -impl Terminator { - pub fn visit_targets(&self, mut f: F) { - match self { - Terminator::Return { .. } => {} - Terminator::Br { ref target, .. } => f(target), - Terminator::CondBr { - ref if_true, - ref if_false, - .. - } => { - f(if_true); - f(if_false); - } - Terminator::Select { - ref targets, - ref default, - .. - } => { - f(default); - for target in targets { - f(target); - } - } - Terminator::None => {} - } - } - - pub fn update_targets(&mut self, mut f: F) { - match self { - Terminator::Return { .. } => {} - Terminator::Br { ref mut target, .. } => f(target), - Terminator::CondBr { - ref mut if_true, - ref mut if_false, - .. - } => { - f(if_true); - f(if_false); - } - Terminator::Select { - ref mut targets, - ref mut default, - .. - } => { - f(default); - for target in targets { - f(target); - } - } - Terminator::None => {} - } - } - - pub fn visit_target(&self, index: usize, mut f: F) { - match (index, self) { - (0, Terminator::Br { ref target, .. }) => f(target), - (0, Terminator::CondBr { ref if_true, .. }) => { - f(if_true); - } - (1, Terminator::CondBr { ref if_false, .. }) => { - f(if_false); - } - (0, Terminator::Select { ref default, .. }) => { - f(default); - } - (i, Terminator::Select { ref targets, .. }) if i <= targets.len() => { - f(&targets[i - 1]); - } - _ => panic!("out of bounds"), - } - } - - pub fn update_target(&mut self, index: usize, mut f: F) { - match (index, self) { - (0, Terminator::Br { ref mut target, .. }) => f(target), - ( - 0, - Terminator::CondBr { - ref mut if_true, .. - }, - ) => { - f(if_true); - } - ( - 1, - Terminator::CondBr { - ref mut if_false, .. - }, - ) => { - f(if_false); - } - ( - 0, - Terminator::Select { - ref mut default, .. - }, - ) => { - f(default); - } - ( - i, - Terminator::Select { - ref mut targets, .. - }, - ) if i <= targets.len() => { - f(&mut targets[i - 1]); - } - (i, this) => panic!("out of bounds: index {} term {:?}", i, this), - } - } - - pub fn visit_successors(&self, mut f: F) { - self.visit_targets(|target| f(target.block)); - } - - pub fn visit_uses(&self, mut f: F) { - self.visit_targets(|target| { - for &arg in &target.args { - f(arg); - } - }); - match self { - &Terminator::CondBr { cond, .. } => f(cond), - &Terminator::Select { value, .. } => f(value), - &Terminator::Return { ref values, .. } => { - for &value in values { - f(value); - } - } - _ => {} - } - } - - pub fn update_uses(&mut self, mut f: F) { - self.update_targets(|target| { - for arg in &mut target.args { - f(arg); - } - }); - match self { - &mut Terminator::CondBr { ref mut cond, .. } => f(cond), - &mut Terminator::Select { ref mut value, .. } => f(value), - &mut Terminator::Return { ref mut values, .. } => { - for value in values { - f(value); - } - } - _ => {} - } - } -} - -impl<'a> Module<'a> { - pub fn from_wasm_bytes(bytes: &'a [u8]) -> Result { - frontend::wasm_to_ir(bytes) - } - - pub fn to_wasm_bytes(&self) -> Result> { - todo!() - } -} +mod module; +pub use module::*; +mod func; +pub use func::*; +mod value; +pub use value::*; diff --git a/src/ir/func.rs b/src/ir/func.rs new file mode 100644 index 0000000..1238793 --- /dev/null +++ b/src/ir/func.rs @@ -0,0 +1,351 @@ +use super::{Block, Local, Signature, Value, ValueDef}; +use crate::entity::EntityVec; +use wasmparser::Type; + +#[derive(Clone, Debug)] +pub enum FuncDecl { + Import(Signature), + Body(Signature, FunctionBody), +} + +impl FuncDecl { + pub fn sig(&self) -> Signature { + match self { + FuncDecl::Import(sig) => *sig, + FuncDecl::Body(sig, ..) => *sig, + } + } + + pub fn body(&self) -> Option<&FunctionBody> { + match self { + FuncDecl::Body(_, body) => Some(body), + _ => None, + } + } + + pub fn body_mut(&mut self) -> Option<&mut FunctionBody> { + match self { + FuncDecl::Body(_, body) => Some(body), + _ => None, + } + } +} + +#[derive(Clone, Debug, Default)] +pub struct FunctionBody { + /// How many parameters the function has. (Their types are the + /// first `n_params` values in `locals`.) + pub n_params: usize, + /// Return types of the function. + pub rets: Vec, + /// Local types, *including* args. + pub locals: EntityVec, + /// Entry block. + pub entry: Block, + /// Block bodies. + pub blocks: EntityVec, + /// Value definitions, indexed by `Value`. + pub values: EntityVec, +} + +impl FunctionBody { + pub fn add_block(&mut self) -> Block { + let id = self.blocks.push(BlockDef::default()); + log::trace!("add_block: block {}", id); + id + } + + 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(); + self.blocks[from].succs.push(to); + self.blocks[to].preds.push(from); + self.blocks[from].pos_in_succ_pred.push(pred_pos); + self.blocks[to].pos_in_pred_succ.push(succ_pos); + log::trace!("add_edge: from {} to {}", from, to); + } + + pub fn add_value(&mut self, value: ValueDef) -> Value { + log::trace!("add_value: def {:?}", value); + self.values.push(value) + } + + pub fn set_alias(&mut self, value: Value, to: Value) { + log::trace!("set_alias: value {:?} to {:?}", value, to); + // Resolve the `to` value through all existing aliases. + let to = self.resolve_and_update_alias(to); + // Disallow cycles. + if to == value { + panic!("Cannot create an alias cycle"); + } + self.values[value] = ValueDef::Alias(to); + } + + pub fn resolve_alias(&self, value: Value) -> Value { + let mut result = value; + loop { + if let &ValueDef::Alias(to) = &self.values[result] { + result = to; + } else { + break; + } + } + result + } + + pub fn add_mutable_inst(&mut self, def: ValueDef) -> Value { + let value = Value(self.values.len() as u32); + self.values.push(def); + value + } + + 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)); + self.blocks[block].params.push((ty, value)); + value + } + + pub fn add_placeholder(&mut self, ty: Type) -> Value { + self.add_mutable_inst(ValueDef::Placeholder(ty)) + } + + pub fn replace_placeholder_with_blockparam(&mut self, block: Block, value: Value) { + let index = self.blocks[block].params.len(); + let ty = match &self.values[value] { + &ValueDef::Placeholder(ty) => ty, + _ => unreachable!(), + }; + self.blocks[block].params.push((ty, value)); + self.values[value] = ValueDef::BlockParam(block, index, ty); + } + + pub fn resolve_and_update_alias(&mut self, value: Value) -> Value { + let to = self.resolve_alias(value); + // Short-circuit the chain, union-find-style. + if let &ValueDef::Alias(orig_to) = &self.values[value] { + if orig_to != to { + self.values[value] = ValueDef::Alias(to); + } + } + to + } + + pub fn append_to_block(&mut self, block: Block, value: Value) { + self.blocks[block].insts.push(value); + } + + pub fn end_block(&mut self, block: Block, terminator: Terminator) { + terminator.visit_successors(|succ| { + self.add_edge(block, succ); + }); + self.blocks[block].terminator = terminator; + } + + pub fn add_local(&mut self, ty: Type) -> Local { + self.locals.push(ty) + } +} + +#[derive(Clone, Debug, Default)] +pub struct BlockDef { + /// Instructions in this block. + pub insts: Vec, + /// Terminator: branch or return. + pub terminator: Terminator, + /// Successor blocks. + pub succs: Vec, + /// For each successor block, our index in its `preds` array. + pub pos_in_succ_pred: Vec, + /// Predecessor blocks. + pub preds: Vec, + /// For each predecessor block, our index in its `succs` array. + pub pos_in_pred_succ: Vec, + /// Type and Value for each blockparam. + pub params: Vec<(Type, Value)>, +} + +#[derive(Clone, Debug)] +pub struct BlockTarget { + pub block: Block, + pub args: Vec, +} + +#[derive(Clone, Debug)] +pub enum Terminator { + Br { + target: BlockTarget, + }, + CondBr { + cond: Value, + if_true: BlockTarget, + if_false: BlockTarget, + }, + Select { + value: Value, + targets: Vec, + default: BlockTarget, + }, + Return { + values: Vec, + }, + None, +} + +impl std::default::Default for Terminator { + fn default() -> Self { + Terminator::None + } +} + +impl Terminator { + pub fn visit_targets(&self, mut f: F) { + match self { + Terminator::Return { .. } => {} + Terminator::Br { ref target, .. } => f(target), + Terminator::CondBr { + ref if_true, + ref if_false, + .. + } => { + f(if_true); + f(if_false); + } + Terminator::Select { + ref targets, + ref default, + .. + } => { + f(default); + for target in targets { + f(target); + } + } + Terminator::None => {} + } + } + + pub fn update_targets(&mut self, mut f: F) { + match self { + Terminator::Return { .. } => {} + Terminator::Br { ref mut target, .. } => f(target), + Terminator::CondBr { + ref mut if_true, + ref mut if_false, + .. + } => { + f(if_true); + f(if_false); + } + Terminator::Select { + ref mut targets, + ref mut default, + .. + } => { + f(default); + for target in targets { + f(target); + } + } + Terminator::None => {} + } + } + + pub fn visit_target(&self, index: usize, mut f: F) { + match (index, self) { + (0, Terminator::Br { ref target, .. }) => f(target), + (0, Terminator::CondBr { ref if_true, .. }) => { + f(if_true); + } + (1, Terminator::CondBr { ref if_false, .. }) => { + f(if_false); + } + (0, Terminator::Select { ref default, .. }) => { + f(default); + } + (i, Terminator::Select { ref targets, .. }) if i <= targets.len() => { + f(&targets[i - 1]); + } + _ => panic!("out of bounds"), + } + } + + pub fn update_target(&mut self, index: usize, mut f: F) { + match (index, self) { + (0, Terminator::Br { ref mut target, .. }) => f(target), + ( + 0, + Terminator::CondBr { + ref mut if_true, .. + }, + ) => { + f(if_true); + } + ( + 1, + Terminator::CondBr { + ref mut if_false, .. + }, + ) => { + f(if_false); + } + ( + 0, + Terminator::Select { + ref mut default, .. + }, + ) => { + f(default); + } + ( + i, + Terminator::Select { + ref mut targets, .. + }, + ) if i <= targets.len() => { + f(&mut targets[i - 1]); + } + (i, this) => panic!("out of bounds: index {} term {:?}", i, this), + } + } + + pub fn visit_successors(&self, mut f: F) { + self.visit_targets(|target| f(target.block)); + } + + pub fn visit_uses(&self, mut f: F) { + self.visit_targets(|target| { + for &arg in &target.args { + f(arg); + } + }); + match self { + &Terminator::CondBr { cond, .. } => f(cond), + &Terminator::Select { value, .. } => f(value), + &Terminator::Return { ref values, .. } => { + for &value in values { + f(value); + } + } + _ => {} + } + } + + pub fn update_uses(&mut self, mut f: F) { + self.update_targets(|target| { + for arg in &mut target.args { + f(arg); + } + }); + match self { + &mut Terminator::CondBr { ref mut cond, .. } => f(cond), + &mut Terminator::Select { ref mut value, .. } => f(value), + &mut Terminator::Return { ref mut values, .. } => { + for value in values { + f(value); + } + } + _ => {} + } + } +} diff --git a/src/ir/module.rs b/src/ir/module.rs new file mode 100644 index 0000000..fa8e211 --- /dev/null +++ b/src/ir/module.rs @@ -0,0 +1,65 @@ +use super::{Func, FuncDecl, Global, Signature, Table}; +use crate::entity::EntityVec; +use crate::frontend; +use anyhow::Result; +use fxhash::FxHashSet; +use wasmparser::{FuncType, Type}; + +#[derive(Clone, Debug, Default)] +pub struct Module<'a> { + orig_bytes: &'a [u8], + funcs: EntityVec, + signatures: EntityVec, + globals: EntityVec, + tables: EntityVec, + + dirty_funcs: FxHashSet, +} + +impl<'a> Module<'a> { + pub(crate) fn with_orig_bytes(orig_bytes: &'a [u8]) -> Module<'a> { + let mut m = Module::default(); + m.orig_bytes = orig_bytes; + m + } +} + +impl<'a> Module<'a> { + pub fn func<'b>(&'b self, id: Func) -> &'b FuncDecl { + &self.funcs[id] + } + pub fn func_mut<'b>(&'b mut self, id: Func) -> &'b mut FuncDecl { + self.dirty_funcs.insert(id); + &mut self.funcs[id] + } + pub fn signature<'b>(&'b self, id: Signature) -> &'b FuncType { + &self.signatures[id] + } + pub fn global_ty(&self, id: Global) -> Type { + self.globals[id] + } + pub fn table_ty(&self, id: Table) -> Type { + self.tables[id] + } + + pub(crate) fn frontend_add_signature(&mut self, ty: FuncType) { + self.signatures.push(ty); + } + pub(crate) fn frontend_add_func(&mut self, body: FuncDecl) { + self.funcs.push(body); + } + pub(crate) fn frontend_add_table(&mut self, ty: Type) { + self.tables.push(ty); + } + pub(crate) fn frontend_add_global(&mut self, ty: Type) { + self.globals.push(ty); + } + + pub fn from_wasm_bytes(bytes: &'a [u8]) -> Result { + frontend::wasm_to_ir(bytes) + } + + pub fn to_wasm_bytes(&self) -> Result> { + todo!() + } +} diff --git a/src/ir/value.rs b/src/ir/value.rs new file mode 100644 index 0000000..da90b1e --- /dev/null +++ b/src/ir/value.rs @@ -0,0 +1,45 @@ +use super::{Block, Value}; +use crate::Operator; +use wasmparser::Type; + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum ValueDef { + Arg(usize, Type), + BlockParam(Block, usize, Type), + Operator(Operator, Vec, Vec), + PickOutput(Value, usize, Type), + Alias(Value), + Placeholder(Type), +} + +impl ValueDef { + pub fn visit_uses(&self, mut f: F) { + match self { + &ValueDef::Arg { .. } => {} + &ValueDef::BlockParam { .. } => {} + &ValueDef::Operator(_, ref args, _) => { + for &arg in args { + f(arg); + } + } + &ValueDef::PickOutput(from, ..) => f(from), + &ValueDef::Alias(value) => f(value), + &ValueDef::Placeholder(_) => {} + } + } + + pub fn update_uses(&mut self, mut f: F) { + match self { + &mut ValueDef::Arg { .. } => {} + &mut ValueDef::BlockParam { .. } => {} + &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(_) => {} + } + } +}