big refactor

This commit is contained in:
Chris Fallin 2022-11-01 20:43:47 -07:00
parent 1da150823d
commit a538d10167
11 changed files with 561 additions and 504 deletions

View file

@ -1,4 +1,6 @@
/*
use crate::backend::binaryen;
use crate::entity::EntityRef;
use crate::ir::*;
use crate::Operator;
use fxhash::FxHashMap;
@ -14,9 +16,9 @@ pub(crate) fn generate_body(
let mut ctx = ElabCtx::new(body, into_mod);
// For each block, generate an expr.
let mut block_exprs: FxHashMap<BlockId, binaryen::Expression> = FxHashMap::default();
for block in body.blocks() {
let exprs = body[block]
let mut block_exprs: FxHashMap<Block, binaryen::Expression> = FxHashMap::default();
for (block_id, block) in body.blocks.entries() {
let exprs = block
.insts
.iter()
.flat_map(|&inst| {
@ -24,61 +26,32 @@ pub(crate) fn generate_body(
ctx.elaborate_value(into_mod, inst)
})
.collect::<Vec<binaryen::Expression>>();
block_exprs.insert(block, binaryen::Expression::block(into_mod, &exprs[..]));
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<BlockId, binaryen::RelooperBlock> = FxHashMap::default();
let mut relooper_blocks: FxHashMap<Block, binaryen::RelooperBlock> = 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);
let expr = relooper.construct(entry.unwrap(), index_var.index());
(ctx.new_locals, expr)
}
#[derive(Clone, Debug)]
struct ElabCtx<'a> {
body: &'a FunctionBody,
op_result_locals: FxHashMap<(Value, usize), LocalId>,
block_param_locals: FxHashMap<(BlockId, usize), LocalId>,
op_result_locals: FxHashMap<(Value, usize), Local>,
block_param_locals: FxHashMap<(Block, usize), Local>,
new_locals: Vec<Type>,
}
impl<'a> ElabCtx<'a> {
fn new(body: &'a FunctionBody, into_mod: &mut binaryen::Module) -> ElabCtx<'a> {
// Create locals for each blockparam.
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 {}
}
// 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);
}
_ => {}
}
}
this
todo!()
}
fn elaborate_value(
@ -86,9 +59,10 @@ impl<'a> ElabCtx<'a> {
into_mod: &binaryen::Module,
value: Value,
) -> Option<binaryen::Expression> {
/*
let value = self.body.resolve_alias(value);
match &self.body[value] {
match &self.body.values[value] {
&ValueDef::Operator(op, ref args, ref tys) => {
// Get expressions for each arg.
let args = args.iter().map(|&arg| self.get_val_local(arg));
@ -113,11 +87,13 @@ impl<'a> ElabCtx<'a> {
}
_ => None,
}
*/
todo!()
}
fn get_val_local(&self, value: Value) -> LocalId {
match &self.body[value] {
&ValueDef::Arg(idx, _) => idx as LocalId,
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()
}
@ -130,8 +106,8 @@ impl<'a> ElabCtx<'a> {
}
}
fn new_local(&mut self, ty: Type) -> LocalId {
let index = (self.body.locals.len() + self.new_locals.len()) as LocalId;
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);
index
}
@ -145,19 +121,18 @@ impl<'a> ElabCtx<'a> {
todo!()
}
fn local_ty(&self, local: LocalId) -> Type {
let index = local as usize;
fn local_ty(&self, local: Local) -> Type {
self.body
.locals
.get(index)
.get(local)
.copied()
.unwrap_or_else(|| self.new_locals[index - self.body.locals.len()])
.unwrap_or_else(|| self.new_locals[local.index() - self.body.locals.len()])
}
}
pub(crate) fn create_new_func(
module: &Module,
sig: SignatureId,
sig: Signature,
body: &FunctionBody,
into_mod: &mut binaryen::Module,
body_expr: binaryen::Expression,
@ -170,10 +145,11 @@ pub(crate) fn create_new_func(
sig.params.iter().copied(),
sig.returns.iter().copied(),
body.locals
.iter()
.values()
.copied()
.skip(body.n_params)
.chain(new_locals.into_iter()),
body_expr,
);
}
*/

View file

@ -12,18 +12,19 @@
// TR-06-33870
// https://www.cs.rice.edu/~keith/EMBED/dom.pdf
use crate::ir::{BlockId, INVALID_BLOCK};
use crate::entity::{EntityRef, PerEntity};
use crate::ir::Block;
// Helper
fn merge_sets(
idom: &[BlockId], // map from BlockId to BlockId
block_to_rpo: &[Option<u32>],
mut node1: BlockId,
mut node2: BlockId,
) -> BlockId {
idom: &PerEntity<Block, Block>, // map from Block to Block
block_to_rpo: &PerEntity<Block, Option<u32>>,
mut node1: Block,
mut node2: Block,
) -> Block {
while node1 != node2 {
if node1 == INVALID_BLOCK || node2 == INVALID_BLOCK {
return INVALID_BLOCK;
if node1.is_invalid() || node2.is_invalid() {
return Block::invalid();
}
let rpo1 = block_to_rpo[node1].unwrap();
let rpo2 = block_to_rpo[node2].unwrap();
@ -37,22 +38,20 @@ fn merge_sets(
node1
}
pub fn calculate<'a, PredFn: Fn(BlockId) -> &'a [BlockId]>(
num_blocks: usize,
pub fn calculate<'a, PredFn: Fn(Block) -> &'a [Block]>(
preds: PredFn,
post_ord: &[BlockId],
start: BlockId,
) -> Vec<BlockId> {
post_ord: &[Block],
start: Block,
) -> PerEntity<Block, Block> {
// We have post_ord, which is the postorder sequence.
// Compute maps from RPO to block number and vice-versa.
let mut block_to_rpo = vec![None; num_blocks];
block_to_rpo.resize(num_blocks, None);
let mut block_to_rpo: PerEntity<Block, Option<u32>> = PerEntity::default();
for (i, rpo_block) in post_ord.iter().rev().enumerate() {
block_to_rpo[*rpo_block] = Some(i as u32);
}
let mut idom = vec![INVALID_BLOCK; num_blocks];
let mut idom: PerEntity<Block, Block> = PerEntity::default();
// The start node must have itself as a parent.
idom[start] = start;
@ -64,7 +63,7 @@ pub fn calculate<'a, PredFn: Fn(BlockId) -> &'a [BlockId]>(
for &node in post_ord.iter().rev() {
let rponum = block_to_rpo[node].unwrap();
let mut parent = INVALID_BLOCK;
let mut parent = Block::invalid();
for &pred in preds(node).iter() {
let pred_rpo = match block_to_rpo[pred] {
Some(r) => r,
@ -79,19 +78,19 @@ pub fn calculate<'a, PredFn: Fn(BlockId) -> &'a [BlockId]>(
}
}
if parent != INVALID_BLOCK {
if parent != Block::invalid() {
for &pred in preds(node).iter() {
if pred == parent {
continue;
}
if idom[pred] == INVALID_BLOCK {
if idom[pred] == Block::invalid() {
continue;
}
parent = merge_sets(&idom, &block_to_rpo[..], parent, pred);
parent = merge_sets(&idom, &block_to_rpo, parent, pred);
}
}
if parent != INVALID_BLOCK && parent != idom[node] {
if parent != Block::invalid() && parent != idom[node] {
idom[node] = parent;
changed = true;
}
@ -100,17 +99,17 @@ pub fn calculate<'a, PredFn: Fn(BlockId) -> &'a [BlockId]>(
// Now set the start node's dominator-tree parent to "invalid";
// this allows the loop in `dominates` to terminate.
idom[start] = INVALID_BLOCK;
idom[start] = Block::invalid();
idom
}
pub fn dominates(idom: &[BlockId], a: BlockId, mut b: BlockId) -> bool {
pub fn dominates(idom: &PerEntity<Block, Block>, a: Block, mut b: Block) -> bool {
loop {
if a == b {
return true;
}
if b == INVALID_BLOCK {
if b.is_invalid() {
return false;
}
b = idom[b];

View file

@ -3,7 +3,8 @@
// Borrowed from regalloc2's cfg.rs, which is also Apache-2.0 with
// LLVM exception.
use crate::ir::{BlockId, FunctionBody, Terminator};
use crate::entity::PerEntity;
use crate::ir::{Block, FunctionBody, Terminator};
use smallvec::SmallVec;
pub mod domtree;
@ -11,53 +12,55 @@ pub mod postorder;
#[derive(Clone, Debug)]
pub struct CFGInfo {
/// Entry block.
pub entry: Block,
/// Predecessors for each block.
pub block_preds: Vec</* BlockId, */ SmallVec<[BlockId; 4]>>,
pub block_preds: PerEntity<Block, SmallVec<[Block; 4]>>,
/// Successors for each block.
pub block_succs: Vec</* BlockId, */ SmallVec<[BlockId; 4]>>,
pub block_succs: PerEntity<Block, SmallVec<[Block; 4]>>,
/// Blocks that end in return.
pub return_blocks: Vec<BlockId>,
pub return_blocks: Vec<Block>,
/// Postorder traversal of blocks.
pub postorder: Vec<BlockId>,
pub postorder: Vec<Block>,
/// Position of each block in postorder, if reachable.
pub postorder_pos: Vec</* BlockId, */ Option<usize>>,
pub postorder_pos: PerEntity<Block, Option<usize>>,
/// Domtree parents, indexed by block.
pub domtree: Vec<BlockId>,
pub domtree: PerEntity<Block, Block>,
}
impl CFGInfo {
pub fn new(f: &FunctionBody) -> CFGInfo {
let mut block_preds = vec![SmallVec::new(); f.blocks.len()];
let mut block_succs = vec![SmallVec::new(); f.blocks.len()];
for block in 0..f.blocks.len() {
f.blocks[block].terminator.visit_successors(|succ| {
let mut block_preds: PerEntity<Block, SmallVec<[Block; 4]>> = PerEntity::default();
let mut block_succs: PerEntity<Block, SmallVec<[Block; 4]>> = PerEntity::default();
for (block, block_def) in f.blocks.entries() {
block_def.terminator.visit_successors(|succ| {
block_preds[succ].push(block);
block_succs[block].push(succ);
});
}
let mut return_blocks = vec![];
for block in 0..f.blocks.len() {
if let Terminator::Return { .. } = &f.blocks[block].terminator {
return_blocks.push(block);
for (block_id, block) in f.blocks.entries() {
if let Terminator::Return { .. } = &block.terminator {
return_blocks.push(block_id);
}
}
let postorder = postorder::calculate(f.blocks.len(), 0, |block| &block_succs[block]);
let postorder = postorder::calculate(f.entry, |block| &block_succs[block]);
let mut postorder_pos = vec![None; f.blocks.len()];
let mut postorder_pos = PerEntity::default();
for (i, block) in postorder.iter().enumerate() {
postorder_pos[*block] = Some(i);
}
let domtree = domtree::calculate(
f.blocks.len(),
|block| &&block_preds[block],
&postorder[..],
0,
f.entry,
);
CFGInfo {
entry: f.entry,
block_preds,
block_succs,
return_blocks,
@ -67,37 +70,33 @@ impl CFGInfo {
}
}
pub fn len(&self) -> usize {
self.block_succs.len()
pub fn dominates(&self, a: Block, b: Block) -> bool {
domtree::dominates(&self.domtree, a, b)
}
pub fn dominates(&self, a: BlockId, b: BlockId) -> bool {
domtree::dominates(&self.domtree[..], a, b)
}
pub fn succs(&self, block: BlockId) -> &[BlockId] {
pub fn succs(&self, block: Block) -> &[Block] {
&self.block_succs[block]
}
pub fn preds(&self, block: BlockId) -> &[BlockId] {
pub fn preds(&self, block: Block) -> &[Block] {
&self.block_preds[block]
}
pub fn pred_count_with_entry(&self, block: BlockId) -> usize {
let is_entry = block == 0;
pub fn pred_count_with_entry(&self, block: Block) -> usize {
let is_entry = block == self.entry;
self.preds(block).len() + if is_entry { 1 } else { 0 }
}
pub fn succ_count_with_return(&self, block: BlockId) -> usize {
pub fn succ_count_with_return(&self, block: Block) -> usize {
let is_return = self.return_blocks.binary_search(&block).is_ok();
self.succs(block).len() + if is_return { 1 } else { 0 }
}
pub fn rpo(&self) -> Vec<BlockId> {
pub fn rpo(&self) -> Vec<Block> {
self.postorder.iter().cloned().rev().collect()
}
pub fn rpo_pos(&self, block: BlockId) -> Option<usize> {
pub fn rpo_pos(&self, block: Block) -> Option<usize> {
self.postorder_pos[block].map(|fwd_pos| self.postorder.len() - 1 - fwd_pos)
}
}

View file

@ -3,24 +3,23 @@
// Borrowed from regalloc2's postorder.rs, which is also Apache-2.0
// with LLVM-exception.
use crate::ir::BlockId;
use crate::entity::PerEntity;
use crate::ir::Block;
use smallvec::{smallvec, SmallVec};
pub fn calculate<'a, SuccFn: Fn(BlockId) -> &'a [BlockId]>(
num_blocks: usize,
entry: BlockId,
pub fn calculate<'a, SuccFn: Fn(Block) -> &'a [Block]>(
entry: Block,
succ_blocks: SuccFn,
) -> Vec<BlockId> {
) -> Vec<Block> {
let mut ret = vec![];
// State: visited-block map, and explicit DFS stack.
let mut visited = vec![];
visited.resize(num_blocks, false);
let mut visited: PerEntity<Block, bool> = PerEntity::default();
#[derive(Debug)]
struct State<'a> {
block: BlockId,
succs: &'a [BlockId],
block: Block,
succs: &'a [Block],
next_succ: usize,
}
let mut stack: SmallVec<[State; 64]> = smallvec![];

152
src/entity.rs Normal file
View file

@ -0,0 +1,152 @@
//! Type-safe indices and indexed containers.
use std::default::Default;
use std::fmt::Debug;
use std::hash::Hash;
use std::marker::PhantomData;
use std::ops::{Index, IndexMut};
pub trait EntityRef: Clone + Copy + PartialEq + Eq + PartialOrd + Ord + Hash {
fn new(value: usize) -> Self;
fn index(self) -> usize;
fn invalid() -> Self;
fn is_valid(self) -> bool {
self != Self::invalid()
}
fn is_invalid(self) -> bool {
self == Self::invalid()
}
}
#[macro_export]
macro_rules! entity {
($name:tt, $prefix:tt) => {
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct $name(u32);
impl crate::entity::EntityRef for $name {
fn new(value: usize) -> Self {
use std::convert::TryFrom;
let value = u32::try_from(value).unwrap();
debug_assert!(value != u32::MAX);
Self(value)
}
fn index(self) -> usize {
self.0 as usize
}
fn invalid() -> Self {
Self(u32::MAX)
}
}
impl std::convert::From<u32> for $name {
fn from(val: u32) -> Self {
<Self as crate::entity::EntityRef>::new(val as usize)
}
}
impl std::default::Default for $name {
fn default() -> Self {
<Self as crate::entity::EntityRef>::invalid()
}
}
impl std::fmt::Debug for $name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}{}", $prefix, self.0)
}
}
impl std::fmt::Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}{}", $prefix, self.0)
}
}
};
}
#[derive(Clone, Debug)]
pub struct EntityVec<Idx: EntityRef, T: Clone + Debug>(Vec<T>, PhantomData<Idx>);
impl<Idx: EntityRef, T: Clone + Debug> std::default::Default for EntityVec<Idx, T> {
fn default() -> Self {
Self(vec![], PhantomData)
}
}
impl<Idx: EntityRef, T: Clone + Debug> EntityVec<Idx, T> {
pub fn push(&mut self, t: T) -> Idx {
let idx = Idx::new(self.0.len());
self.0.push(t);
idx
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn iter(&self) -> impl Iterator<Item = Idx> {
(0..self.0.len()).map(|index| Idx::new(index))
}
pub fn values(&self) -> impl Iterator<Item = &T> {
self.0.iter()
}
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut T> {
self.0.iter_mut()
}
pub fn entries(&self) -> impl Iterator<Item = (Idx, &T)> {
self.0
.iter()
.enumerate()
.map(|(index, t)| (Idx::new(index), t))
}
pub fn entries_mut(&mut self) -> impl Iterator<Item = (Idx, &mut T)> {
self.0
.iter_mut()
.enumerate()
.map(|(index, t)| (Idx::new(index), t))
}
pub fn get(&self, idx: Idx) -> Option<&T> {
self.0.get(idx.index())
}
pub fn get_mut(&mut self, idx: Idx) -> Option<&mut T> {
self.0.get_mut(idx.index())
}
}
impl<Idx: EntityRef, T: Clone + Debug> Index<Idx> for EntityVec<Idx, T> {
type Output = T;
fn index(&self, idx: Idx) -> &T {
&self.0[idx.index()]
}
}
impl<Idx: EntityRef, T: Clone + Debug> IndexMut<Idx> for EntityVec<Idx, T> {
fn index_mut(&mut self, idx: Idx) -> &mut T {
&mut self.0[idx.index()]
}
}
#[derive(Clone, Debug, Default)]
pub struct PerEntity<Idx: EntityRef, T: Clone + Debug + Default>(Vec<T>, PhantomData<Idx>, T);
impl<Idx: EntityRef, T: Clone + Debug + Default> Index<Idx> for PerEntity<Idx, T> {
type Output = T;
fn index(&self, idx: Idx) -> &T {
self.0.get(idx.index()).unwrap_or(&self.2)
}
}
impl<Idx: EntityRef, T: Clone + Debug + Default> IndexMut<Idx> for PerEntity<Idx, T> {
fn index_mut(&mut self, idx: Idx) -> &mut T {
if idx.index() >= self.0.len() {
self.0.resize(idx.index() + 1, T::default());
}
&mut self.0[idx.index()]
}
}

View file

@ -2,14 +2,14 @@
#![allow(dead_code)]
use std::convert::TryFrom;
use crate::entity::EntityRef;
use crate::ir::*;
use crate::op_traits::{op_inputs, op_outputs};
use crate::ops::Operator;
use anyhow::{bail, Result};
use fxhash::{FxHashMap, FxHashSet};
use log::trace;
use std::convert::TryFrom;
use wasmparser::{
Ieee32, Ieee64, ImportSectionEntryType, Parser, Payload, Type, TypeDef, TypeOrFuncType,
};
@ -45,7 +45,7 @@ fn handle_payload<'a>(
for _ in 0..reader.get_count() {
match reader.read()?.ty {
ImportSectionEntryType::Function(sig_idx) => {
module.frontend_add_func(FuncDecl::Import(sig_idx as SignatureId));
module.frontend_add_func(FuncDecl::Import(Signature::from(sig_idx)));
*next_func += 1;
}
ImportSectionEntryType::Global(ty) => {
@ -72,12 +72,12 @@ fn handle_payload<'a>(
}
Payload::FunctionSection(mut reader) => {
for _ in 0..reader.get_count() {
let sig_idx = reader.read()? as SignatureId;
let sig_idx = Signature::from(reader.read()?);
module.frontend_add_func(FuncDecl::Body(sig_idx, FunctionBody::default()));
}
}
Payload::CodeSectionEntry(body) => {
let func_idx = *next_func;
let func_idx = Func::new(*next_func);
*next_func += 1;
let my_sig = module.func(func_idx).sig();
@ -94,7 +94,7 @@ fn handle_payload<'a>(
fn parse_body<'a>(
module: &'a Module,
my_sig: SignatureId,
my_sig: Signature,
body: wasmparser::FunctionBody,
) -> Result<FunctionBody> {
let mut ret: FunctionBody = FunctionBody::default();
@ -123,11 +123,12 @@ fn parse_body<'a>(
);
let mut builder = FunctionBodyBuilder::new(module, my_sig, &mut ret);
builder.locals.seal_block_preds(0, &mut builder.body);
builder.locals.start_block(0);
let entry = Block::new(0);
builder.locals.seal_block_preds(entry, &mut builder.body);
builder.locals.start_block(entry);
for (arg_idx, &arg_ty) in module.signature(my_sig).params.iter().enumerate() {
let local_idx = arg_idx as LocalId;
let local_idx = Local::new(arg_idx);
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);
@ -135,9 +136,9 @@ fn parse_body<'a>(
}
let n_args = module.signature(my_sig).params.len();
for (offset, local_ty) in locals.into_iter().enumerate() {
let local_idx = (n_args + offset) as u32;
builder.locals.declare(local_idx, local_ty);
for (offset, local_ty) in locals.values().enumerate() {
let local_idx = Local::new(n_args + offset);
builder.locals.declare(local_idx, *local_ty);
}
let ops = body.get_operators_reader()?;
@ -150,12 +151,12 @@ fn parse_body<'a>(
builder.handle_op(wasmparser::Operator::Return)?;
}
for block in 0..builder.body.blocks.len() {
for block in builder.body.blocks.iter() {
log::trace!("checking if block is sealed: {}", block);
assert!(builder.locals.is_sealed(block));
debug_assert!(builder.locals.is_sealed(block));
}
for value in &builder.body.values {
assert!(!matches!(value, &ValueDef::Placeholder(_)));
for value in builder.body.values.values() {
debug_assert!(!matches!(value, &ValueDef::Placeholder(_)));
}
trace!("Final function body:{:?}", ret);
@ -166,26 +167,26 @@ fn parse_body<'a>(
#[derive(Debug, Clone, Default)]
struct LocalTracker {
/// Types of locals, as declared.
types: FxHashMap<LocalId, Type>,
types: FxHashMap<Local, Type>,
/// The current block.
cur_block: Option<BlockId>,
cur_block: Option<Block>,
/// Is the given block sealed?
block_sealed: FxHashSet<BlockId>,
block_sealed: FxHashSet<Block>,
/// The local-to-value mapping at the start of a block.
block_start: FxHashMap<BlockId, FxHashMap<LocalId, Value>>,
block_start: FxHashMap<Block, FxHashMap<Local, Value>>,
/// The local-to-value mapping at the end of a block.
block_end: FxHashMap<BlockId, FxHashMap<LocalId, Value>>,
in_cur_block: FxHashMap<LocalId, Value>,
incomplete_phis: FxHashMap<BlockId, Vec<(LocalId, Value)>>,
block_end: FxHashMap<Block, FxHashMap<Local, Value>>,
in_cur_block: FxHashMap<Local, Value>,
incomplete_phis: FxHashMap<Block, Vec<(Local, Value)>>,
}
impl LocalTracker {
pub fn declare(&mut self, local: LocalId, ty: Type) {
pub fn declare(&mut self, local: Local, ty: Type) {
let was_present = self.types.insert(local, ty).is_some();
assert!(!was_present);
}
pub fn start_block(&mut self, block: BlockId) {
pub fn start_block(&mut self, block: Block) {
self.finish_block();
log::trace!("start_block: block {}", block);
self.cur_block = Some(block);
@ -200,7 +201,7 @@ impl LocalTracker {
self.cur_block = None;
}
pub fn seal_block_preds(&mut self, block: BlockId, body: &mut FunctionBody) {
pub fn seal_block_preds(&mut self, block: Block, body: &mut FunctionBody) {
log::trace!("seal_block_preds: block {}", block);
let not_sealed = self.block_sealed.insert(block);
assert!(not_sealed);
@ -213,23 +214,18 @@ impl LocalTracker {
}
}
fn is_sealed(&self, block: BlockId) -> bool {
fn is_sealed(&self, block: Block) -> bool {
self.block_sealed.contains(&block)
}
pub fn set(&mut self, local: LocalId, value: Value) {
pub fn set(&mut self, local: Local, value: Value) {
log::trace!("set: local {} value {:?}", local, value);
self.in_cur_block.insert(local, value);
}
fn get_in_block(
&mut self,
body: &mut FunctionBody,
at_block: BlockId,
local: LocalId,
) -> Value {
fn get_in_block(&mut self, body: &mut FunctionBody, at_block: Block, local: Local) -> Value {
log::trace!("get_in_block: at_block {} local {}", at_block, local);
let ty = body.locals[local as usize];
let ty = body.locals[local];
if self.cur_block == Some(at_block) {
if let Some(&value) = self.in_cur_block.get(&local) {
@ -279,12 +275,11 @@ impl LocalTracker {
}
}
pub fn get(&mut self, body: &mut FunctionBody, local: LocalId) -> Value {
pub fn get(&mut self, body: &mut FunctionBody, local: Local) -> Value {
if let Some(block) = self.cur_block {
assert!((local as usize) < body.locals.len());
self.get_in_block(body, block, local)
} else {
let ty = body.locals[local as usize];
let ty = body.locals[local];
self.create_default_value(body, ty)
}
}
@ -322,8 +317,8 @@ impl LocalTracker {
fn compute_blockparam(
&mut self,
body: &mut FunctionBody,
block: BlockId,
local: LocalId,
block: Block,
local: Local,
value: Value,
) {
log::trace!(
@ -400,10 +395,10 @@ impl LocalTracker {
#[derive(Debug)]
struct FunctionBodyBuilder<'a, 'b> {
module: &'b Module<'a>,
my_sig: SignatureId,
my_sig: Signature,
body: &'b mut FunctionBody,
locals: LocalTracker,
cur_block: Option<BlockId>,
cur_block: Option<Block>,
ctrl_stack: Vec<Frame>,
op_stack: Vec<(Type, Value)>,
}
@ -412,28 +407,28 @@ struct FunctionBodyBuilder<'a, 'b> {
enum Frame {
Block {
start_depth: usize,
out: BlockId,
out: Block,
params: Vec<Type>,
results: Vec<Type>,
},
Loop {
start_depth: usize,
header: BlockId,
out: BlockId,
header: Block,
out: Block,
params: Vec<Type>,
results: Vec<Type>,
},
If {
start_depth: usize,
out: BlockId,
el: BlockId,
out: Block,
el: Block,
param_values: Vec<(Type, Value)>,
params: Vec<Type>,
results: Vec<Type>,
},
Else {
start_depth: usize,
out: BlockId,
out: Block,
params: Vec<Type>,
results: Vec<Type>,
},
@ -458,7 +453,7 @@ impl Frame {
}
}
fn br_target(&self) -> BlockId {
fn br_target(&self) -> Block {
match self {
Frame::Block { out, .. } => *out,
Frame::Loop { header, .. } => *header,
@ -466,7 +461,7 @@ impl Frame {
}
}
fn out(&self) -> BlockId {
fn out(&self) -> Block {
match self {
Frame::Block { out, .. }
| Frame::Loop { out, .. }
@ -495,15 +490,15 @@ impl Frame {
}
impl<'a, 'b> FunctionBodyBuilder<'a, 'b> {
fn new(module: &'b Module<'a>, my_sig: SignatureId, body: &'b mut FunctionBody) -> Self {
body.blocks.push(Block::default());
fn new(module: &'b Module<'a>, my_sig: Signature, body: &'b mut FunctionBody) -> Self {
body.blocks.push(BlockDef::default());
let mut ret = Self {
module,
my_sig,
body,
ctrl_stack: vec![],
op_stack: vec![],
cur_block: Some(0),
cur_block: Some(Block::new(0)),
locals: LocalTracker::default(),
};
@ -559,22 +554,25 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> {
}
wasmparser::Operator::LocalGet { local_index } => {
let ty = self.body.locals[*local_index as usize];
let value = self.locals.get(&mut self.body, *local_index);
let local_index = Local::from(*local_index);
let ty = self.body.locals[local_index];
let value = self.locals.get(&mut self.body, local_index);
self.op_stack.push((ty, value));
}
wasmparser::Operator::LocalSet { local_index } => {
let local_index = Local::from(*local_index);
let (_, value) = self.op_stack.pop().unwrap();
if self.cur_block.is_some() {
self.locals.set(*local_index, value);
self.locals.set(local_index, value);
}
}
wasmparser::Operator::LocalTee { local_index } => {
let local_index = Local::from(*local_index);
let (_ty, value) = *self.op_stack.last().unwrap();
if self.cur_block.is_some() {
self.locals.set(*local_index, value);
self.locals.set(local_index, value);
}
}
@ -998,7 +996,7 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> {
Ok(())
}
fn add_block_params(&mut self, block: BlockId, tys: &[Type]) {
fn add_block_params(&mut self, block: Block, tys: &[Type]) {
log::trace!("add_block_params: block {} tys {:?}", block, tys);
for &ty in tys {
self.body.add_blockparam(block, ty);
@ -1010,7 +1008,7 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> {
TypeOrFuncType::Type(Type::EmptyBlockType) => (vec![], vec![]),
TypeOrFuncType::Type(ret_ty) => (vec![], vec![ret_ty]),
TypeOrFuncType::FuncType(sig_idx) => {
let sig = &self.module.signature(sig_idx as SignatureId);
let sig = &self.module.signature(Signature::from(sig_idx));
(
Vec::from(sig.params.clone()),
Vec::from(sig.returns.clone()),
@ -1023,7 +1021,7 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> {
&self.ctrl_stack[self.ctrl_stack.len() - 1 - relative_depth as usize]
}
fn emit_branch(&mut self, target: BlockId, args: &[Value]) {
fn emit_branch(&mut self, target: Block, args: &[Value]) {
log::trace!(
"emit_branch: cur_block {:?} target {} args {:?}",
self.cur_block,
@ -1045,9 +1043,9 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> {
fn emit_cond_branch(
&mut self,
cond: Value,
if_true: BlockId,
if_true: Block,
if_true_args: &[Value],
if_false: BlockId,
if_false: Block,
if_false_args: &[Value],
) {
log::trace!(
@ -1083,8 +1081,8 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> {
fn emit_br_table(
&mut self,
index: Value,
default_target: BlockId,
indexed_targets: &[BlockId],
default_target: Block,
indexed_targets: &[Block],
args: &[Value],
) {
log::trace!(
@ -1151,11 +1149,11 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> {
let inputs = op_inputs(
self.module,
self.my_sig,
&self.body.locals[..],
&self.body.locals,
&self.op_stack[..],
&op,
)?;
let outputs = op_outputs(self.module, &self.body.locals[..], &self.op_stack[..], &op)?;
let outputs = op_outputs(self.module, &self.body.locals, &self.op_stack[..], &op)?;
log::trace!(
"emit into block {:?}: op {:?} inputs {:?}",

196
src/ir.rs
View file

@ -1,21 +1,22 @@
//! Intermediate representation for Wasm.
use crate::{backend, backend::binaryen};
use crate::entity;
use crate::entity::{EntityRef, EntityVec};
use crate::{frontend, Operator};
use anyhow::Result;
use fxhash::FxHashSet;
use wasmparser::{FuncType, Type};
use wasmparser::FuncType;
pub type SignatureId = usize;
pub type FuncId = usize;
pub type BlockId = usize;
pub type InstId = usize;
pub type LocalId = u32;
pub type GlobalId = u32;
pub type TableId = u32;
pub type MemoryId = u32;
pub use wasmparser::Type;
pub const INVALID_BLOCK: BlockId = usize::MAX;
entity!(Signature, "sig");
entity!(Func, "func");
entity!(Block, "block");
entity!(Local, "local");
entity!(Global, "global");
entity!(Table, "table");
entity!(Memory, "memory");
entity!(Value, "value");
#[derive(Clone, Debug, Default)]
pub struct Module<'a> {
@ -25,7 +26,7 @@ pub struct Module<'a> {
globals: Vec<Type>,
tables: Vec<Type>,
dirty_funcs: FxHashSet<FuncId>,
dirty_funcs: FxHashSet<Func>,
}
impl<'a> Module<'a> {
@ -37,21 +38,21 @@ impl<'a> Module<'a> {
}
impl<'a> Module<'a> {
pub fn func<'b>(&'b self, id: FuncId) -> &'b FuncDecl {
&self.funcs[id]
pub fn func<'b>(&'b self, id: Func) -> &'b FuncDecl {
&self.funcs[id.index()]
}
pub fn func_mut<'b>(&'b mut self, id: FuncId) -> &'b mut FuncDecl {
pub fn func_mut<'b>(&'b mut self, id: Func) -> &'b mut FuncDecl {
self.dirty_funcs.insert(id);
&mut self.funcs[id]
&mut self.funcs[id.index()]
}
pub fn signature<'b>(&'b self, id: SignatureId) -> &'b FuncType {
&self.signatures[id]
pub fn signature<'b>(&'b self, id: Signature) -> &'b FuncType {
&self.signatures[id.index()]
}
pub fn global_ty(&self, id: GlobalId) -> Type {
self.globals[id as usize]
pub fn global_ty(&self, id: Global) -> Type {
self.globals[id.index()]
}
pub fn table_ty(&self, id: TableId) -> Type {
self.tables[id as usize]
pub fn table_ty(&self, id: Table) -> Type {
self.tables[id.index()]
}
pub(crate) fn frontend_add_signature(&mut self, ty: FuncType) {
@ -70,12 +71,12 @@ impl<'a> Module<'a> {
#[derive(Clone, Debug)]
pub enum FuncDecl {
Import(SignatureId),
Body(SignatureId, FunctionBody),
Import(Signature),
Body(Signature, FunctionBody),
}
impl FuncDecl {
pub fn sig(&self) -> SignatureId {
pub fn sig(&self) -> Signature {
match self {
FuncDecl::Import(sig) => *sig,
FuncDecl::Body(sig, ..) => *sig,
@ -105,23 +106,23 @@ pub struct FunctionBody {
/// Return types of the function.
pub rets: Vec<Type>,
/// Local types, *including* args.
pub locals: Vec<Type>,
/// Block bodies, indexed by `BlockId`.
pub blocks: Vec<Block>,
pub locals: EntityVec<Local, Type>,
/// Entry block.
pub entry: Block,
/// Block bodies.
pub blocks: EntityVec<Block, BlockDef>,
/// Value definitions, indexed by `Value`.
pub values: Vec<ValueDef>,
pub values: EntityVec<Value, ValueDef>,
}
impl FunctionBody {
pub fn add_block(&mut self) -> BlockId {
let id = self.blocks.len();
self.blocks.push(Block::default());
self.blocks[id].id = id;
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: BlockId, to: BlockId) {
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);
@ -133,10 +134,7 @@ impl FunctionBody {
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());
id
self.values.push(value)
}
pub fn set_alias(&mut self, value: Value, to: Value) {
@ -147,13 +145,13 @@ impl FunctionBody {
if to == value {
panic!("Cannot create an alias cycle");
}
self.values[value.index()] = ValueDef::Alias(to);
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.index()] {
if let &ValueDef::Alias(to) = &self.values[result] {
result = to;
} else {
break;
@ -168,7 +166,7 @@ impl FunctionBody {
value
}
pub fn add_blockparam(&mut self, block: BlockId, ty: Type) -> 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));
@ -179,121 +177,65 @@ impl FunctionBody {
self.add_mutable_inst(ValueDef::Placeholder(ty))
}
pub fn replace_placeholder_with_blockparam(&mut self, block: BlockId, value: Value) {
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.index()] {
let ty = match &self.values[value] {
&ValueDef::Placeholder(ty) => ty,
_ => unreachable!(),
};
self.blocks[block].params.push((ty, value));
self.values[value.index()] = ValueDef::BlockParam(block, index, ty);
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.index()] {
if let &ValueDef::Alias(orig_to) = &self.values[value] {
if orig_to != to {
self.values[value.index()] = ValueDef::Alias(to);
self.values[value] = ValueDef::Alias(to);
}
}
to
}
pub fn append_to_block(&mut self, block: BlockId, value: Value) {
pub fn append_to_block(&mut self, block: Block, value: Value) {
self.blocks[block].insts.push(value);
}
pub fn end_block(&mut self, block: BlockId, terminator: Terminator) {
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) -> LocalId {
let id = self.locals.len() as LocalId;
self.locals.push(ty);
id
}
pub fn values<'a>(&'a self) -> impl Iterator<Item = (Value, &'a ValueDef)> + 'a {
self.values
.iter()
.enumerate()
.map(|(idx, value_def)| (Value(idx as u32), value_def))
}
pub fn blocks(&self) -> impl Iterator<Item = BlockId> {
(0..self.blocks.len()).into_iter()
}
}
impl std::ops::Index<Value> for FunctionBody {
type Output = ValueDef;
fn index(&self, index: Value) -> &ValueDef {
&self.values[index.0 as usize]
}
}
impl std::ops::IndexMut<Value> for FunctionBody {
fn index_mut(&mut self, index: Value) -> &mut ValueDef {
&mut self.values[index.0 as usize]
}
}
impl std::ops::Index<BlockId> for FunctionBody {
type Output = Block;
fn index(&self, index: BlockId) -> &Block {
&self.blocks[index]
}
}
impl std::ops::IndexMut<BlockId> for FunctionBody {
fn index_mut(&mut self, index: BlockId) -> &mut Block {
&mut self.blocks[index]
pub fn add_local(&mut self, ty: Type) -> Local {
self.locals.push(ty)
}
}
#[derive(Clone, Debug, Default)]
pub struct Block {
pub id: BlockId,
/// Side-effecting values from the sea-of-nodes that are computed, in order.
pub struct BlockDef {
/// Instructions in this block.
pub insts: Vec<Value>,
/// Terminator: branch or return.
pub terminator: Terminator,
/// Successor blocks.
pub succs: Vec<BlockId>,
pub succs: Vec<Block>,
/// For each successor block, our index in its `preds` array.
pub pos_in_succ_pred: Vec<usize>,
/// Predecessor blocks.
pub preds: Vec<BlockId>,
pub preds: Vec<Block>,
/// For each predecessor block, our index in its `succs` array.
pub pos_in_pred_succ: Vec<usize>,
/// Type and Value for each blockparam.
pub params: Vec<(Type, Value)>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Value(u32);
impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "v{}", self.0)
}
}
impl Value {
pub fn index(self) -> usize {
self.0 as usize
}
pub fn from_index(value: usize) -> Value {
Self(value as u32)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum ValueDef {
Arg(usize, Type),
BlockParam(BlockId, usize, Type),
BlockParam(Block, usize, Type),
Operator(Operator, Vec<Value>, Vec<Type>),
PickOutput(Value, usize, Type),
Alias(Value),
@ -334,7 +276,7 @@ impl ValueDef {
#[derive(Clone, Debug)]
pub struct BlockTarget {
pub block: BlockId,
pub block: Block,
pub args: Vec<Value>,
}
@ -476,7 +418,7 @@ impl Terminator {
}
}
pub fn visit_successors<F: FnMut(BlockId)>(&self, mut f: F) {
pub fn visit_successors<F: FnMut(Block)>(&self, mut f: F) {
self.visit_targets(|target| f(target.block));
}
@ -523,32 +465,6 @@ impl<'a> Module<'a> {
}
pub fn to_wasm_bytes(&self) -> Result<Vec<u8>> {
let mut binaryen_module = binaryen::Module::read(self.orig_bytes)?;
for new_func_idx in self.funcs.len()..binaryen_module.num_funcs() {
let sig = self.func(new_func_idx).sig();
let body = self.func(new_func_idx).body().unwrap();
let (new_locals, binaryen_expr) =
backend::lower::generate_body(body, &mut binaryen_module);
backend::lower::create_new_func(
self,
sig,
body,
&mut binaryen_module,
binaryen_expr,
new_locals,
);
}
for &func in &self.dirty_funcs {
if let Some(body) = self.func(func).body() {
let mut binaryen_func = binaryen_module.func(func);
let (new_locals, binaryen_expr) =
backend::lower::generate_body(body, &mut binaryen_module);
for ty in new_locals {
binaryen_func.add_local(ty);
}
binaryen_func.set_body(binaryen_expr);
}
}
binaryen_module.write()
todo!()
}
}

View file

@ -7,11 +7,11 @@ pub use wasmparser;
mod backend;
mod cfg;
mod entity;
mod frontend;
mod ir;
mod op_traits;
mod ops;
mod use_count;
pub use ir::*;
pub use ops::Operator;

View file

@ -1,14 +1,15 @@
//! Metadata on operators.
use crate::ir::{Module, SignatureId, Value};
use crate::entity::EntityVec;
use crate::ir::{Global, Local, Module, Signature, Table, Value};
use crate::Operator;
use anyhow::Result;
use wasmparser::Type;
pub fn op_inputs(
module: &Module,
my_sig: SignatureId,
my_locals: &[Type],
my_sig: Signature,
my_locals: &EntityVec<Local, Type>,
op_stack: &[(Type, Value)],
op: &Operator,
) -> Result<Vec<Type>> {
@ -19,15 +20,15 @@ pub fn op_inputs(
let sig = module.func(function_index).sig();
Ok(Vec::from(module.signature(sig).params.clone()))
}
&Operator::CallIndirect { index, .. } => {
let mut params = module.signature(index).params.to_vec();
&Operator::CallIndirect { sig_index, .. } => {
let mut params = module.signature(sig_index).params.to_vec();
params.push(Type::I32);
Ok(params)
}
&Operator::Return => Ok(Vec::from(module.signature(my_sig).returns.clone())),
&Operator::LocalSet { local_index } | &Operator::LocalTee { local_index } => {
Ok(vec![my_locals[local_index as usize]])
Ok(vec![my_locals[local_index]])
}
&Operator::LocalGet { .. } => Ok(vec![]),
@ -216,7 +217,7 @@ pub fn op_inputs(
Operator::I32ReinterpretF32 => Ok(vec![Type::F32]),
Operator::I64ReinterpretF64 => Ok(vec![Type::F64]),
Operator::TableGet { .. } => Ok(vec![Type::I32]),
Operator::TableSet { table } => Ok(vec![Type::I32, module.table_ty(*table)]),
Operator::TableSet { table_index } => Ok(vec![Type::I32, module.table_ty(*table_index)]),
Operator::TableGrow { .. } => Ok(vec![Type::I32]),
Operator::TableSize { .. } => Ok(vec![]),
Operator::MemorySize { .. } => Ok(vec![]),
@ -226,7 +227,7 @@ pub fn op_inputs(
pub fn op_outputs(
module: &Module,
my_locals: &[Type],
my_locals: &EntityVec<Local, Type>,
op_stack: &[(Type, Value)],
op: &Operator,
) -> Result<Vec<Type>> {
@ -237,13 +238,13 @@ pub fn op_outputs(
let sig = module.func(function_index).sig();
Ok(Vec::from(module.signature(sig).returns.clone()))
}
&Operator::CallIndirect { index, .. } => {
Ok(Vec::from(module.signature(index).returns.clone()))
&Operator::CallIndirect { sig_index, .. } => {
Ok(Vec::from(module.signature(sig_index).returns.clone()))
}
&Operator::Return => Ok(vec![]),
&Operator::LocalSet { .. } => Ok(vec![]),
&Operator::LocalGet { local_index } | &Operator::LocalTee { local_index } => {
Ok(vec![my_locals[local_index as usize]])
Ok(vec![my_locals[local_index]])
}
&Operator::Select => {
@ -425,7 +426,7 @@ pub fn op_outputs(
Operator::F64ReinterpretI64 => Ok(vec![Type::F64]),
Operator::I32ReinterpretF32 => Ok(vec![Type::I32]),
Operator::I64ReinterpretF64 => Ok(vec![Type::I64]),
Operator::TableGet { table } => Ok(vec![module.table_ty(*table)]),
Operator::TableGet { table_index } => Ok(vec![module.table_ty(*table_index)]),
Operator::TableSet { .. } => Ok(vec![]),
Operator::TableGrow { .. } => Ok(vec![]),
Operator::TableSize { .. } => Ok(vec![Type::I32]),
@ -439,12 +440,12 @@ pub enum SideEffect {
Trap,
ReadMem,
WriteMem,
ReadGlobal(usize),
WriteGlobal(usize),
ReadTable(usize),
WriteTable(usize),
ReadLocal(usize),
WriteLocal(usize),
ReadGlobal(Global),
WriteGlobal(Global),
ReadTable(Table),
WriteTable(Table),
ReadLocal(Local),
WriteLocal(Local),
Return,
All,
}
@ -459,17 +460,16 @@ pub fn op_effects(op: &Operator) -> Result<Vec<SideEffect>> {
&Operator::Call { .. } => Ok(vec![All]),
&Operator::CallIndirect { .. } => Ok(vec![All]),
&Operator::Return => Ok(vec![Return]),
&Operator::LocalSet { local_index, .. } => Ok(vec![WriteLocal(local_index as usize)]),
&Operator::LocalGet { local_index, .. } => Ok(vec![ReadLocal(local_index as usize)]),
&Operator::LocalTee { local_index, .. } => Ok(vec![
ReadLocal(local_index as usize),
WriteLocal(local_index as usize),
]),
&Operator::LocalSet { local_index, .. } => Ok(vec![WriteLocal(local_index)]),
&Operator::LocalGet { local_index, .. } => Ok(vec![ReadLocal(local_index)]),
&Operator::LocalTee { local_index, .. } => {
Ok(vec![ReadLocal(local_index), WriteLocal(local_index)])
}
&Operator::Select => Ok(vec![]),
&Operator::TypedSelect { .. } => Ok(vec![]),
&Operator::GlobalGet { global_index, .. } => Ok(vec![ReadGlobal(global_index as usize)]),
&Operator::GlobalSet { global_index, .. } => Ok(vec![WriteGlobal(global_index as usize)]),
&Operator::GlobalGet { global_index, .. } => Ok(vec![ReadGlobal(global_index)]),
&Operator::GlobalSet { global_index, .. } => Ok(vec![WriteGlobal(global_index)]),
Operator::I32Load { .. }
| Operator::I32Load8S { .. }
@ -642,10 +642,10 @@ pub fn op_effects(op: &Operator) -> Result<Vec<SideEffect>> {
Operator::F64ReinterpretI64 => Ok(vec![]),
Operator::I32ReinterpretF32 => Ok(vec![]),
Operator::I64ReinterpretF64 => Ok(vec![]),
Operator::TableGet { table, .. } => Ok(vec![ReadTable(*table as usize), Trap]),
Operator::TableSet { table, .. } => Ok(vec![WriteTable(*table as usize), Trap]),
Operator::TableGrow { table, .. } => Ok(vec![WriteTable(*table as usize), Trap]),
Operator::TableSize { table, .. } => Ok(vec![ReadTable(*table as usize)]),
Operator::TableGet { table_index, .. } => Ok(vec![ReadTable(*table_index), Trap]),
Operator::TableSet { table_index, .. } => Ok(vec![WriteTable(*table_index), Trap]),
Operator::TableGrow { table_index, .. } => Ok(vec![WriteTable(*table_index), Trap]),
Operator::TableSize { table_index, .. } => Ok(vec![ReadTable(*table_index)]),
Operator::MemorySize { .. } => Ok(vec![ReadMem]),
Operator::MemoryGrow { .. } => Ok(vec![WriteMem, Trap]),
}

View file

@ -2,13 +2,13 @@
use wasmparser::{Ieee32, Ieee64, MemoryImmediate, Type};
use crate::{FuncId, GlobalId, LocalId, MemoryId, TableId};
use crate::{Func, Global, Local, Memory, Signature, Table};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Memory {
pub struct MemoryArg {
pub align: u8,
pub offset: u64,
pub memory: MemoryId,
pub memory: Memory,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@ -16,46 +16,117 @@ pub enum Operator {
Unreachable,
Nop,
Call { function_index: FuncId },
CallIndirect { index: FuncId, table_index: TableId },
Call {
function_index: Func,
},
CallIndirect {
sig_index: Signature,
table_index: Table,
},
Return,
LocalSet { local_index: LocalId },
LocalTee { local_index: LocalId },
LocalGet { local_index: LocalId },
LocalSet {
local_index: Local,
},
LocalTee {
local_index: Local,
},
LocalGet {
local_index: Local,
},
Select,
TypedSelect { ty: Type },
GlobalGet { global_index: GlobalId },
GlobalSet { global_index: GlobalId },
TypedSelect {
ty: Type,
},
GlobalGet {
global_index: Global,
},
GlobalSet {
global_index: Global,
},
I32Load { memory: Memory },
I64Load { memory: Memory },
F32Load { memory: Memory },
F64Load { memory: Memory },
I32Load8S { memory: Memory },
I32Load8U { memory: Memory },
I32Load16S { memory: Memory },
I32Load16U { memory: Memory },
I64Load8S { memory: Memory },
I64Load8U { memory: Memory },
I64Load16S { memory: Memory },
I64Load16U { memory: Memory },
I64Load32S { memory: Memory },
I64Load32U { memory: Memory },
I32Load {
memory: MemoryArg,
},
I64Load {
memory: MemoryArg,
},
F32Load {
memory: MemoryArg,
},
F64Load {
memory: MemoryArg,
},
I32Load8S {
memory: MemoryArg,
},
I32Load8U {
memory: MemoryArg,
},
I32Load16S {
memory: MemoryArg,
},
I32Load16U {
memory: MemoryArg,
},
I64Load8S {
memory: MemoryArg,
},
I64Load8U {
memory: MemoryArg,
},
I64Load16S {
memory: MemoryArg,
},
I64Load16U {
memory: MemoryArg,
},
I64Load32S {
memory: MemoryArg,
},
I64Load32U {
memory: MemoryArg,
},
I32Store { memory: Memory },
I64Store { memory: Memory },
F32Store { memory: Memory },
F64Store { memory: Memory },
I32Store8 { memory: Memory },
I32Store16 { memory: Memory },
I64Store8 { memory: Memory },
I64Store16 { memory: Memory },
I64Store32 { memory: Memory },
I32Store {
memory: MemoryArg,
},
I64Store {
memory: MemoryArg,
},
F32Store {
memory: MemoryArg,
},
F64Store {
memory: MemoryArg,
},
I32Store8 {
memory: MemoryArg,
},
I32Store16 {
memory: MemoryArg,
},
I64Store8 {
memory: MemoryArg,
},
I64Store16 {
memory: MemoryArg,
},
I64Store32 {
memory: MemoryArg,
},
I32Const { value: i32 },
I64Const { value: i64 },
F32Const { value: Ieee32 },
F64Const { value: Ieee64 },
I32Const {
value: i32,
},
I64Const {
value: i64,
},
F32Const {
value: Ieee32,
},
F64Const {
value: Ieee64,
},
I32Eqz,
I32Eq,
@ -206,12 +277,24 @@ pub enum Operator {
F64ReinterpretI64,
I32ReinterpretF32,
I64ReinterpretF64,
TableGet { table: TableId },
TableSet { table: TableId },
TableGrow { table: TableId },
TableSize { table: TableId },
MemorySize { mem: MemoryId },
MemoryGrow { mem: MemoryId },
TableGet {
table_index: Table,
},
TableSet {
table_index: Table,
},
TableGrow {
table_index: Table,
},
TableSize {
table_index: Table,
},
MemorySize {
mem: Memory,
},
MemoryGrow {
mem: Memory,
},
}
impl<'a, 'b> std::convert::TryFrom<&'b wasmparser::Operator<'a>> for Operator {
@ -222,32 +305,32 @@ impl<'a, 'b> std::convert::TryFrom<&'b wasmparser::Operator<'a>> for Operator {
&wasmparser::Operator::Unreachable => Ok(Operator::Unreachable),
&wasmparser::Operator::Nop => Ok(Operator::Nop),
&wasmparser::Operator::Call { function_index } => Ok(Operator::Call {
function_index: function_index as usize,
function_index: Func::from(function_index),
}),
&wasmparser::Operator::CallIndirect {
index, table_index, ..
} => Ok(Operator::CallIndirect {
sig_index: Signature::from(index),
table_index: Table::from(table_index),
}),
&wasmparser::Operator::CallIndirect { index, table_index } => {
Ok(Operator::CallIndirect {
index: index as usize,
table_index,
})
}
&wasmparser::Operator::Return => Ok(Operator::Return),
&wasmparser::Operator::LocalSet { local_index } => {
Ok(Operator::LocalSet { local_index })
}
&wasmparser::Operator::LocalTee { local_index } => {
Ok(Operator::LocalTee { local_index })
}
&wasmparser::Operator::LocalGet { local_index } => {
Ok(Operator::LocalGet { local_index })
}
&wasmparser::Operator::LocalSet { local_index } => Ok(Operator::LocalSet {
local_index: Local::from(local_index),
}),
&wasmparser::Operator::LocalTee { local_index } => Ok(Operator::LocalTee {
local_index: Local::from(local_index),
}),
&wasmparser::Operator::LocalGet { local_index } => Ok(Operator::LocalGet {
local_index: Local::from(local_index),
}),
&wasmparser::Operator::Select => Ok(Operator::Select),
&wasmparser::Operator::TypedSelect { ty } => Ok(Operator::TypedSelect { ty }),
&wasmparser::Operator::GlobalGet { global_index } => {
Ok(Operator::GlobalGet { global_index })
}
&wasmparser::Operator::GlobalSet { global_index } => {
Ok(Operator::GlobalSet { global_index })
}
&wasmparser::Operator::GlobalGet { global_index } => Ok(Operator::GlobalGet {
global_index: Global::from(global_index),
}),
&wasmparser::Operator::GlobalSet { global_index } => Ok(Operator::GlobalSet {
global_index: Global::from(global_index),
}),
&wasmparser::Operator::I32Load { memarg } => Ok(Operator::I32Load {
memory: memarg.into(),
}),
@ -457,23 +540,35 @@ impl<'a, 'b> std::convert::TryFrom<&'b wasmparser::Operator<'a>> for Operator {
&wasmparser::Operator::F64ReinterpretI64 => Ok(Operator::F64ReinterpretI64),
&wasmparser::Operator::I32ReinterpretF32 => Ok(Operator::I32ReinterpretF32),
&wasmparser::Operator::I64ReinterpretF64 => Ok(Operator::I64ReinterpretF64),
&wasmparser::Operator::TableGet { table } => Ok(Operator::TableGet { table }),
&wasmparser::Operator::TableSet { table } => Ok(Operator::TableSet { table }),
&wasmparser::Operator::TableGrow { table } => Ok(Operator::TableGrow { table }),
&wasmparser::Operator::TableSize { table } => Ok(Operator::TableSize { table }),
&wasmparser::Operator::MemorySize { mem, .. } => Ok(Operator::MemorySize { mem }),
&wasmparser::Operator::MemoryGrow { mem, .. } => Ok(Operator::MemoryGrow { mem }),
&wasmparser::Operator::TableGet { table } => Ok(Operator::TableGet {
table_index: Table::from(table),
}),
&wasmparser::Operator::TableSet { table } => Ok(Operator::TableSet {
table_index: Table::from(table),
}),
&wasmparser::Operator::TableGrow { table } => Ok(Operator::TableGrow {
table_index: Table::from(table),
}),
&wasmparser::Operator::TableSize { table } => Ok(Operator::TableSize {
table_index: Table::from(table),
}),
&wasmparser::Operator::MemorySize { mem, .. } => Ok(Operator::MemorySize {
mem: Memory::from(mem),
}),
&wasmparser::Operator::MemoryGrow { mem, .. } => Ok(Operator::MemoryGrow {
mem: Memory::from(mem),
}),
_ => Err(()),
}
}
}
impl std::convert::From<MemoryImmediate> for Memory {
fn from(value: MemoryImmediate) -> Memory {
Memory {
impl std::convert::From<MemoryImmediate> for MemoryArg {
fn from(value: MemoryImmediate) -> MemoryArg {
MemoryArg {
align: value.align,
offset: value.offset,
memory: value.memory as MemoryId,
memory: Memory::from(value.memory),
}
}
}

View file

@ -1,77 +0,0 @@
//! Use-count analysis.
use crate::{FunctionBody, Value, ValueDef};
use fxhash::FxHashSet;
use std::collections::VecDeque;
#[derive(Clone, Debug)]
pub struct UseCountAnalysis {
pub(crate) toplevel: FxHashSet<Value>,
pub(crate) use_count: Vec</* Value, */ usize>,
}
impl UseCountAnalysis {
pub(crate) fn compute(f: &FunctionBody) -> UseCountAnalysis {
let n_values = f.values.len();
let mut counts = UseCountAnalysis {
use_count: vec![0; n_values],
toplevel: FxHashSet::default(),
};
let mut workqueue = VecDeque::new();
let mut workqueue_set = FxHashSet::default();
for block in 0..f.blocks.len() {
for &value in &f.blocks[block].insts {
let value = f.resolve_alias(value);
counts.add(value);
if workqueue_set.insert(value) {
workqueue.push_back(value);
}
counts.toplevel.insert(value);
}
f.blocks[block].terminator.visit_uses(|value| {
let value = f.resolve_alias(value);
counts.add(value);
if workqueue_set.insert(value) {
workqueue.push_back(value);
}
});
while let Some(value) = workqueue.pop_front() {
workqueue_set.remove(&value);
match &f.values[value.index()] {
&ValueDef::Alias(..) | &ValueDef::Arg(..) | &ValueDef::BlockParam(..) => {}
&ValueDef::Operator(_op, ref args, _) => {
for &arg in args {
let arg = f.resolve_alias(arg);
counts.add(arg);
if counts.use_count[arg.index()] == 1 {
if workqueue_set.insert(arg) {
workqueue.push_back(arg);
}
}
}
}
&ValueDef::PickOutput(value, _, _) => {
let value = f.resolve_alias(value);
counts.add(value);
if counts.use_count[value.index()] == 1 {
if workqueue_set.insert(value) {
workqueue.push_back(value);
}
}
}
&ValueDef::Placeholder(_) => {
panic!("Unresolved placeholder for value {}", value);
}
}
}
}
counts
}
fn add(&mut self, value: Value) {
self.use_count[value.index()] += 1;
}
}