This commit is contained in:
Chris Fallin 2022-11-17 23:56:44 -08:00
parent 91a2c11f67
commit c2db0ad4b9
No known key found for this signature in database
GPG key ID: 31649E4FE65EB465
3 changed files with 206 additions and 49 deletions

View file

@ -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<Item = wasmparser::Type>,
results: impl Iterator<Item = wasmparser::Type>,
locals: impl Iterator<Item = wasmparser::Type>,
params: impl Iterator<Item = ir::Type>,
results: impl Iterator<Item = ir::Type>,
locals: impl Iterator<Item = ir::Type>,
body: Expression,
) -> Function {
let params = tys_to_binaryen(params);
let results = tys_to_binaryen(results);
let locals: Vec<BinaryenType> = locals.map(|ty| Type::from(ty).to_kind()).collect();
let locals: Vec<BinaryenType> = 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<Type> {
fn from_binaryen(kind: BinaryenType) -> Option<Type> {
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<wasmparser::Type> for Type {
fn from(ty: wasmparser::Type) -> Self {
impl From<ir::Type> 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<Item = wasmparser::Type>) -> BinaryenType {
let tys: Vec<BinaryenType> = tys.map(|ty| Type::from(ty).to_kind()).collect();
pub fn tys_to_binaryen(tys: impl Iterator<Item = ir::Type>) -> BinaryenType {
let tys: Vec<BinaryenType> = 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<String> {
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::<Vec<_>>();
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,

View file

@ -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<Block, binaryen::Expression> = FxHashMap::default();
let mut block_exprs: BTreeMap<Block, binaryen::Expression> = 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::<Vec<binaryen::Expression>>();
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<Block, binaryen::RelooperBlock> = FxHashMap::default();
for (block_id, block_expr) in block_exprs {}
// Create the blocks.
let mut relooper_blocks: FxHashMap<Block, (binaryen::Expression, binaryen::RelooperBlock)> =
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::<Vec<_>>();
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<Block, (binaryen::Expression, binaryen::RelooperBlock)>,
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<Type>,
}
@ -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,
);
}
*/

View file

@ -13,6 +13,17 @@ pub enum ValueDef {
}
impl ValueDef {
pub fn ty(&self) -> Option<Type> {
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<F: FnMut(Value)>(&self, mut f: F) {
match self {
&ValueDef::BlockParam { .. } => {}