This commit is contained in:
Chris Fallin 2022-11-01 02:26:21 -07:00
parent 589729566f
commit 1da150823d
5 changed files with 258 additions and 94 deletions

View file

@ -14,8 +14,8 @@ pub struct Expression(BinaryenModule, BinaryenExpression);
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct Export(BinaryenModule, BinaryenExport); pub struct Export(BinaryenModule, BinaryenExport);
type BinaryenIndex = u32; pub type BinaryenIndex = u32;
type BinaryenType = usize; pub type BinaryenType = usize;
impl Module { impl Module {
pub fn read(data: &[u8]) -> Result<Module> { pub fn read(data: &[u8]) -> Result<Module> {
@ -423,6 +423,8 @@ pub type BinaryenModule = *const c_void;
type BinaryenFunction = *const c_void; type BinaryenFunction = *const c_void;
type BinaryenExpression = *const c_void; type BinaryenExpression = *const c_void;
type BinaryenExport = *const c_void; type BinaryenExport = *const c_void;
type BinaryenRelooper = *const c_void;
type BinaryenRelooperBlock = *const c_void;
#[repr(C)] #[repr(C)]
struct BinaryenModuleAllocateAndWriteResult { 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")] #[link(name = "binaryen")]
extern "C" { extern "C" {
fn BinaryenModuleRead(data: *const u8, len: usize) -> BinaryenModule; fn BinaryenModuleRead(data: *const u8, len: usize) -> BinaryenModule;
@ -613,6 +683,32 @@ extern "C" {
fn BinaryenLiteralInt64(x: i64) -> BinaryenLiteral; fn BinaryenLiteralInt64(x: i64) -> BinaryenLiteral;
fn BinaryenLiteralFloat32Bits(x: i32) -> BinaryenLiteral; fn BinaryenLiteralFloat32Bits(x: i32) -> BinaryenLiteral;
fn BinaryenLiteralFloat64Bits(x: i64) -> 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)] #[repr(C)]

View file

@ -1,5 +1,6 @@
use crate::backend::binaryen; use crate::backend::binaryen;
use crate::ir::*; use crate::ir::*;
use crate::Operator;
use fxhash::FxHashMap; use fxhash::FxHashMap;
use wasmparser::Type; use wasmparser::Type;
@ -10,78 +11,150 @@ pub(crate) fn generate_body(
body: &FunctionBody, body: &FunctionBody,
into_mod: &mut binaryen::Module, into_mod: &mut binaryen::Module,
) -> (Vec<Type>, binaryen::Expression) { ) -> (Vec<Type>, binaryen::Expression) {
let mut ctx = ElabCtx::new(body, into_mod);
// For each block, generate an expr. // For each block, generate an expr.
let mut block_exprs: FxHashMap<BlockId, binaryen::Expression> = FxHashMap::default(); let mut block_exprs: FxHashMap<BlockId, binaryen::Expression> = FxHashMap::default();
let mut ctx = ElabCtx::new(body, into_mod);
for block in body.blocks() { for block in body.blocks() {
let exprs = body[block] let exprs = body[block]
.insts .insts
.iter() .iter()
.map(|&inst| { .flat_map(|&inst| {
let inst = body.resolve_alias(inst); let inst = body.resolve_alias(inst);
elaborate_value(body, into_mod, &mut ctx, inst) ctx.elaborate_value(into_mod, inst)
}) })
.collect::<Vec<_>>(); .collect::<Vec<binaryen::Expression>>();
block_exprs.insert(block, binaryen::Expression::block(into_mod, &exprs[..])); 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<BlockId, 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);
(ctx.new_locals, expr)
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
struct ElabCtx { struct ElabCtx<'a> {
value_to_expr: FxHashMap<Value, binaryen::Expression>, body: &'a FunctionBody,
block_params: FxHashMap<(Block, usize), LocalId>, op_result_locals: FxHashMap<(Value, usize), LocalId>,
block_param_locals: FxHashMap<(BlockId, usize), LocalId>,
new_locals: Vec<Type>, new_locals: Vec<Type>,
} }
impl ElabCtx { impl<'a> ElabCtx<'a> {
fn new(body: &FunctionBody, into_mod: &mut binaryen::Module) -> ElabCtx { fn new(body: &'a FunctionBody, into_mod: &mut binaryen::Module) -> ElabCtx<'a> {
// Create locals for each blockparam. // Create locals for each blockparam.
let mut block_params = FxHashMap::default(); let mut this = ElabCtx {
let mut next_local = body.locals.len() as LocalId; body,
let mut new_locals = vec![]; op_result_locals: FxHashMap::default(),
block_param_locals: FxHashMap::default(),
new_locals: vec![],
};
for block in body.blocks() { for block in body.blocks() {
for &(ty, param) in &body[block].params { for &(ty, param) in &body[block].params {}
let new_local = next_local; }
next_local += 1;
block_params.insert((ty, param), new_local); // Create locals for each Operator value and each blockparam.
new_locals.push(ty); 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 { this
value_to_expr: FxHashMap::default(),
block_params,
new_locals,
}
}
} }
fn elaborate_value( fn elaborate_value(
body: &FunctionBody, &mut self,
into_mod: &binaryen::Module, into_mod: &binaryen::Module,
ctx: &mut ElabCtx,
value: Value, value: Value,
) -> Option<binaryen::Expression> {
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::<Vec<_>>();
// 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 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!(),
}
}
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
}
fn create_binaryen_op(
&mut self,
op: Operator,
args: Vec<binaryen::Expression>,
tys: &[Type],
) -> binaryen::Expression { ) -> binaryen::Expression {
let value = body.resolve_alias(value);
if let Some(expr) = ctx.value_to_expr.get(&value) {
return *expr;
}
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!(),
}
todo!() 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( pub(crate) fn create_new_func(
module: &Module, module: &Module,
sig: SignatureId, sig: SignatureId,

View file

@ -128,7 +128,7 @@ fn parse_body<'a>(
for (arg_idx, &arg_ty) in module.signature(my_sig).params.iter().enumerate() { for (arg_idx, &arg_ty) in module.signature(my_sig).params.iter().enumerate() {
let local_idx = arg_idx as LocalId; 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); trace!("defining local {} to value {}", local_idx, value);
builder.locals.declare(local_idx, arg_ty); builder.locals.declare(local_idx, arg_ty);
builder.locals.set(local_idx, value); builder.locals.set(local_idx, value);
@ -155,7 +155,7 @@ fn parse_body<'a>(
assert!(builder.locals.is_sealed(block)); assert!(builder.locals.is_sealed(block));
} }
for value in &builder.body.values { for value in &builder.body.values {
assert!(value != &ValueDef::Placeholder); assert!(!matches!(value, &ValueDef::Placeholder(_)));
} }
trace!("Final function body:{:?}", ret); trace!("Final function body:{:?}", ret);
@ -291,32 +291,30 @@ impl LocalTracker {
fn create_default_value(&mut self, body: &mut FunctionBody, ty: Type) -> Value { fn create_default_value(&mut self, body: &mut FunctionBody, ty: Type) -> Value {
match ty { match ty {
Type::I32 => body.add_value( Type::I32 => body.add_value(ValueDef::Operator(
ValueDef::Operator(Operator::I32Const { value: 0 }, vec![]), Operator::I32Const { value: 0 },
vec![],
vec![ty], vec![ty],
), )),
Type::I64 => body.add_value( Type::I64 => body.add_value(ValueDef::Operator(
ValueDef::Operator(Operator::I64Const { value: 0 }, vec![]), Operator::I64Const { value: 0 },
vec![],
vec![ty], vec![ty],
), )),
Type::F32 => body.add_value( Type::F32 => body.add_value(ValueDef::Operator(
ValueDef::Operator(
Operator::F32Const { Operator::F32Const {
value: Ieee32::from_bits(0), value: Ieee32::from_bits(0),
}, },
vec![], vec![],
),
vec![ty], vec![ty],
), )),
Type::F64 => body.add_value( Type::F64 => body.add_value(ValueDef::Operator(
ValueDef::Operator(
Operator::F64Const { Operator::F64Const {
value: Ieee64::from_bits(0), value: Ieee64::from_bits(0),
}, },
vec![], vec![],
),
vec![ty], vec![ty],
), )),
_ => todo!("unsupported type: {:?}", ty), _ => todo!("unsupported type: {:?}", ty),
} }
} }
@ -1180,7 +1178,7 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> {
let value = self let value = self
.body .body
.add_value(ValueDef::Operator(op, input_operands), outputs.clone()); .add_value(ValueDef::Operator(op, input_operands, outputs.clone()));
log::trace!(" -> value: {:?}", value); log::trace!(" -> value: {:?}", value);
if let Some(block) = self.cur_block { 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() { for (i, output_ty) in outputs.into_iter().enumerate() {
let pick = self let pick = self
.body .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)); self.op_stack.push((output_ty, pick));
log::trace!(" -> pick {}: {:?} ty {:?}", i, pick, output_ty); log::trace!(" -> pick {}: {:?} ty {:?}", i, pick, output_ty);
} }

View file

@ -110,9 +110,6 @@ pub struct FunctionBody {
pub blocks: Vec<Block>, pub blocks: Vec<Block>,
/// Value definitions, indexed by `Value`. /// Value definitions, indexed by `Value`.
pub values: Vec<ValueDef>, pub values: Vec<ValueDef>,
/// Types, indexed by `Value`. A single value can have multiple
/// types if multi-value (e.g. a call).
pub types: Vec<Vec<Type>>,
} }
impl FunctionBody { impl FunctionBody {
@ -134,12 +131,11 @@ impl FunctionBody {
log::trace!("add_edge: from {} to {}", from, to); log::trace!("add_edge: from {} to {}", from, to);
} }
pub fn add_value(&mut self, value: ValueDef, tys: Vec<Type>) -> Value { pub fn add_value(&mut self, value: ValueDef) -> Value {
log::trace!("add_value: def {:?} ty {:?}", value, tys); log::trace!("add_value: def {:?}", value);
let id = Value(self.values.len() as u32); let id = Value(self.values.len() as u32);
log::trace!(" -> value {:?}", id); log::trace!(" -> value {:?}", id);
self.values.push(value.clone()); self.values.push(value.clone());
self.types.push(tys);
id id
} }
@ -166,30 +162,31 @@ impl FunctionBody {
result result
} }
pub fn add_mutable_inst(&mut self, tys: Vec<Type>, def: ValueDef) -> Value { pub fn add_mutable_inst(&mut self, def: ValueDef) -> Value {
let value = Value(self.values.len() as u32); let value = Value(self.values.len() as u32);
self.types.push(tys);
self.values.push(def); self.values.push(def);
value value
} }
pub fn add_blockparam(&mut self, block: BlockId, ty: Type) -> Value { pub fn add_blockparam(&mut self, block: BlockId, ty: Type) -> Value {
let index = self.blocks[block].params.len(); 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)); self.blocks[block].params.push((ty, value));
value value
} }
pub fn add_placeholder(&mut self, ty: Type) -> 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) { 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 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.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 { pub fn resolve_and_update_alias(&mut self, value: Value) -> Value {
@ -295,12 +292,12 @@ impl Value {
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum ValueDef { pub enum ValueDef {
Arg(usize), Arg(usize, Type),
BlockParam(BlockId, usize), BlockParam(BlockId, usize, Type),
Operator(Operator, Vec<Value>), Operator(Operator, Vec<Value>, Vec<Type>),
PickOutput(Value, usize), PickOutput(Value, usize, Type),
Alias(Value), Alias(Value),
Placeholder, Placeholder(Type),
} }
impl ValueDef { impl ValueDef {
@ -308,14 +305,14 @@ impl ValueDef {
match self { match self {
&ValueDef::Arg { .. } => {} &ValueDef::Arg { .. } => {}
&ValueDef::BlockParam { .. } => {} &ValueDef::BlockParam { .. } => {}
&ValueDef::Operator(_, ref args) => { &ValueDef::Operator(_, ref args, _) => {
for &arg in args { for &arg in args {
f(arg); f(arg);
} }
} }
&ValueDef::PickOutput(from, ..) => f(from), &ValueDef::PickOutput(from, ..) => f(from),
&ValueDef::Alias(value) => f(value), &ValueDef::Alias(value) => f(value),
&ValueDef::Placeholder => {} &ValueDef::Placeholder(_) => {}
} }
} }
@ -323,14 +320,14 @@ impl ValueDef {
match self { match self {
&mut ValueDef::Arg { .. } => {} &mut ValueDef::Arg { .. } => {}
&mut ValueDef::BlockParam { .. } => {} &mut ValueDef::BlockParam { .. } => {}
&mut ValueDef::Operator(_, ref mut args) => { &mut ValueDef::Operator(_, ref mut args, _) => {
for arg in args { for arg in args {
f(arg); f(arg);
} }
} }
&mut ValueDef::PickOutput(ref mut from, ..) => f(from), &mut ValueDef::PickOutput(ref mut from, ..) => f(from),
&mut ValueDef::Alias(ref mut value) => f(value), &mut ValueDef::Alias(ref mut value) => f(value),
&mut ValueDef::Placeholder => {} &mut ValueDef::Placeholder(_) => {}
} }
} }
} }

View file

@ -41,7 +41,7 @@ impl UseCountAnalysis {
workqueue_set.remove(&value); workqueue_set.remove(&value);
match &f.values[value.index()] { match &f.values[value.index()] {
&ValueDef::Alias(..) | &ValueDef::Arg(..) | &ValueDef::BlockParam(..) => {} &ValueDef::Alias(..) | &ValueDef::Arg(..) | &ValueDef::BlockParam(..) => {}
&ValueDef::Operator(_op, ref args) => { &ValueDef::Operator(_op, ref args, _) => {
for &arg in args { for &arg in args {
let arg = f.resolve_alias(arg); let arg = f.resolve_alias(arg);
counts.add(arg); counts.add(arg);
@ -52,7 +52,7 @@ impl UseCountAnalysis {
} }
} }
} }
&ValueDef::PickOutput(value, _) => { &ValueDef::PickOutput(value, _, _) => {
let value = f.resolve_alias(value); let value = f.resolve_alias(value);
counts.add(value); counts.add(value);
if counts.use_count[value.index()] == 1 { if counts.use_count[value.index()] == 1 {
@ -61,7 +61,7 @@ impl UseCountAnalysis {
} }
} }
} }
&ValueDef::Placeholder => { &ValueDef::Placeholder(_) => {
panic!("Unresolved placeholder for value {}", value); panic!("Unresolved placeholder for value {}", value);
} }
} }