working roundtrip of hello world?
This commit is contained in:
parent
1b67d25efb
commit
5e3edc1de4
|
@ -4,7 +4,6 @@ use super::{Locations, SerializedBlockTarget, SerializedBody, SerializedOperator
|
|||
use crate::{ops::ty_to_valty, FunctionBody};
|
||||
use std::borrow::Cow;
|
||||
use wasm_encoder::BlockType;
|
||||
use wasmparser::Type;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Wasm {
|
||||
|
@ -12,59 +11,23 @@ pub struct Wasm {
|
|||
pub locals: Vec<wasm_encoder::ValType>,
|
||||
}
|
||||
|
||||
struct WasmContext<'a, FT: FuncTypeSink> {
|
||||
struct WasmContext<'a> {
|
||||
wasm: &'a mut Wasm,
|
||||
func_type_sink: &'a mut FT,
|
||||
}
|
||||
|
||||
pub trait FuncTypeSink {
|
||||
fn add_signature(&mut self, params: Vec<Type>, results: Vec<Type>) -> u32;
|
||||
}
|
||||
|
||||
impl<'a, FT: FuncTypeSink> WasmContext<'a, FT> {
|
||||
fn create_type(&mut self, params: Vec<Type>, results: Vec<Type>) -> u32 {
|
||||
self.func_type_sink.add_signature(params, results)
|
||||
}
|
||||
|
||||
fn find_fallthrough_ty<'b>(
|
||||
&mut self,
|
||||
params: &[Type],
|
||||
mut targets: impl Iterator<Item = &'b SerializedBlockTarget>,
|
||||
) -> u32 {
|
||||
let fallthrough_rets = targets
|
||||
.find_map(|target| match target {
|
||||
SerializedBlockTarget::Fallthrough(tys, ..) => Some(&tys[..]),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or(&[]);
|
||||
|
||||
self.create_type(params.to_vec(), fallthrough_rets.to_vec())
|
||||
}
|
||||
|
||||
impl<'a> WasmContext<'a> {
|
||||
fn translate(&mut self, op: &SerializedOperator, locations: &Locations) {
|
||||
log::trace!("translate: {:?}", op);
|
||||
match op {
|
||||
SerializedOperator::StartBlock {
|
||||
ref params,
|
||||
ref results,
|
||||
..
|
||||
} => {
|
||||
let ty =
|
||||
self.create_type(params.iter().map(|&(ty, _)| ty).collect(), results.clone());
|
||||
self.wasm.operators.push(wasm_encoder::Instruction::Block(
|
||||
BlockType::FunctionType(ty),
|
||||
));
|
||||
}
|
||||
SerializedOperator::StartLoop {
|
||||
ref params,
|
||||
ref results,
|
||||
..
|
||||
} => {
|
||||
let ty =
|
||||
self.create_type(params.iter().map(|&(ty, _)| ty).collect(), results.clone());
|
||||
SerializedOperator::StartBlock { .. } => {
|
||||
self.wasm
|
||||
.operators
|
||||
.push(wasm_encoder::Instruction::Loop(BlockType::FunctionType(ty)));
|
||||
.push(wasm_encoder::Instruction::Block(BlockType::Empty));
|
||||
}
|
||||
SerializedOperator::StartLoop { .. } => {
|
||||
self.wasm
|
||||
.operators
|
||||
.push(wasm_encoder::Instruction::Loop(BlockType::Empty));
|
||||
}
|
||||
SerializedOperator::End => {
|
||||
self.wasm.operators.push(wasm_encoder::Instruction::End);
|
||||
|
@ -84,37 +47,29 @@ impl<'a, FT: FuncTypeSink> WasmContext<'a, FT> {
|
|||
ref if_true,
|
||||
ref if_false,
|
||||
} => {
|
||||
let fallthrough_ty =
|
||||
self.find_fallthrough_ty(&[], [if_true, if_false].iter().map(|&targ| targ));
|
||||
self.wasm
|
||||
.operators
|
||||
.push(wasm_encoder::Instruction::If(BlockType::FunctionType(
|
||||
fallthrough_ty,
|
||||
)));
|
||||
.push(wasm_encoder::Instruction::If(BlockType::Empty));
|
||||
self.translate_target(1, if_true, locations);
|
||||
self.wasm.operators.push(wasm_encoder::Instruction::Else);
|
||||
self.translate_target(1, if_false, locations);
|
||||
self.wasm.operators.push(wasm_encoder::Instruction::End);
|
||||
}
|
||||
SerializedOperator::BrTable {
|
||||
ref index_ops,
|
||||
ref targets,
|
||||
ref default,
|
||||
} => {
|
||||
let fallthrough_ty = self.find_fallthrough_ty(
|
||||
&[Type::I32],
|
||||
targets.iter().chain(std::iter::once(default)),
|
||||
);
|
||||
self.wasm.operators.push(wasm_encoder::Instruction::Block(
|
||||
wasm_encoder::BlockType::FunctionType(fallthrough_ty),
|
||||
));
|
||||
let index_ty = self.create_type(vec![Type::I32], vec![]);
|
||||
for _ in 0..(targets.len() + 1) {
|
||||
for _ in 0..(targets.len() + 2) {
|
||||
self.wasm.operators.push(wasm_encoder::Instruction::Block(
|
||||
wasm_encoder::BlockType::FunctionType(index_ty),
|
||||
wasm_encoder::BlockType::Empty,
|
||||
));
|
||||
}
|
||||
|
||||
let br_table_targets = (1..=targets.len()).map(|i| i as u32).collect::<Vec<_>>();
|
||||
for op in index_ops {
|
||||
self.translate(op, locations);
|
||||
}
|
||||
self.wasm.operators.push(wasm_encoder::Instruction::BrTable(
|
||||
Cow::Owned(br_table_targets),
|
||||
0,
|
||||
|
@ -158,7 +113,7 @@ impl<'a, FT: FuncTypeSink> WasmContext<'a, FT> {
|
|||
) {
|
||||
log::trace!("translate_target: {:?}", target);
|
||||
match target {
|
||||
&SerializedBlockTarget::Fallthrough(_, ref ops) => {
|
||||
&SerializedBlockTarget::Fallthrough(ref ops) => {
|
||||
for op in ops {
|
||||
self.translate(op, locations);
|
||||
}
|
||||
|
@ -168,7 +123,7 @@ impl<'a, FT: FuncTypeSink> WasmContext<'a, FT> {
|
|||
.push(wasm_encoder::Instruction::Br((extra_blocks - 1) as u32));
|
||||
}
|
||||
}
|
||||
&SerializedBlockTarget::Branch(branch, _, ref ops) => {
|
||||
&SerializedBlockTarget::Branch(branch, ref ops) => {
|
||||
for op in ops {
|
||||
self.translate(op, locations);
|
||||
}
|
||||
|
@ -180,12 +135,7 @@ impl<'a, FT: FuncTypeSink> WasmContext<'a, FT> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn produce_func_wasm<FT: FuncTypeSink>(
|
||||
f: &FunctionBody,
|
||||
body: &SerializedBody,
|
||||
locations: &Locations,
|
||||
ft: &mut FT,
|
||||
) -> Wasm {
|
||||
pub fn produce_func_wasm(f: &FunctionBody, body: &SerializedBody, locations: &Locations) -> Wasm {
|
||||
let mut wasm = Wasm {
|
||||
operators: vec![],
|
||||
locals: vec![],
|
||||
|
@ -195,13 +145,14 @@ pub fn produce_func_wasm<FT: FuncTypeSink>(
|
|||
wasm.locals
|
||||
.extend(locations.new_locals.iter().map(|ty| ty_to_valty(*ty)));
|
||||
|
||||
let mut ctx = WasmContext {
|
||||
wasm: &mut wasm,
|
||||
func_type_sink: ft,
|
||||
};
|
||||
let mut ctx = WasmContext { wasm: &mut wasm };
|
||||
for operator in &body.operators {
|
||||
ctx.translate(operator, locations);
|
||||
}
|
||||
// There is always an explicit Return before this point. This
|
||||
// allows us to avoid matching the return types in our stack
|
||||
// discipline / outer block type.
|
||||
wasm.operators.push(wasm_encoder::Instruction::Unreachable);
|
||||
wasm.operators.push(wasm_encoder::Instruction::End);
|
||||
|
||||
wasm
|
||||
|
|
|
@ -2,10 +2,6 @@
|
|||
|
||||
mod structured;
|
||||
pub use structured::*;
|
||||
mod use_count;
|
||||
pub use use_count::*;
|
||||
mod schedule;
|
||||
pub use schedule::*;
|
||||
mod serialize;
|
||||
pub use serialize::*;
|
||||
mod locations;
|
||||
|
|
|
@ -3,16 +3,11 @@
|
|||
//! in Wasm function body. Contains everything needed to emit Wasm
|
||||
//! except for value locations (and corresponding local spill/reloads).
|
||||
|
||||
use super::{
|
||||
structured::{BlockOrder, BlockOrderEntry},
|
||||
Schedule, UseCountAnalysis,
|
||||
};
|
||||
use super::structured::{BlockOrder, BlockOrderEntry};
|
||||
use crate::{
|
||||
cfg::CFGInfo, op_traits::op_rematerialize, BlockId, FunctionBody, Operator, Terminator, Value,
|
||||
ValueDef,
|
||||
};
|
||||
use fxhash::FxHashSet;
|
||||
use wasmparser::Type;
|
||||
|
||||
/// A Wasm function body with a serialized sequence of operators that
|
||||
/// mirror Wasm opcodes in every way *except* for locals corresponding
|
||||
|
@ -25,21 +20,17 @@ pub struct SerializedBody {
|
|||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum SerializedBlockTarget {
|
||||
Fallthrough(Vec<Type>, Vec<SerializedOperator>),
|
||||
Branch(usize, Vec<Type>, Vec<SerializedOperator>),
|
||||
Fallthrough(Vec<SerializedOperator>),
|
||||
Branch(usize, Vec<SerializedOperator>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum SerializedOperator {
|
||||
StartBlock {
|
||||
header: BlockId,
|
||||
params: Vec<(Type, Value)>,
|
||||
results: Vec<Type>,
|
||||
},
|
||||
StartLoop {
|
||||
header: BlockId,
|
||||
params: Vec<(Type, Value)>,
|
||||
results: Vec<Type>,
|
||||
},
|
||||
Br(SerializedBlockTarget),
|
||||
BrIf {
|
||||
|
@ -47,6 +38,7 @@ pub enum SerializedOperator {
|
|||
if_false: SerializedBlockTarget,
|
||||
},
|
||||
BrTable {
|
||||
index_ops: Vec<SerializedOperator>,
|
||||
targets: Vec<SerializedBlockTarget>,
|
||||
default: SerializedBlockTarget,
|
||||
},
|
||||
|
@ -86,9 +78,13 @@ impl SerializedOperator {
|
|||
if_false.visit_value_locals(r, w);
|
||||
}
|
||||
&SerializedOperator::BrTable {
|
||||
ref index_ops,
|
||||
ref default,
|
||||
ref targets,
|
||||
} => {
|
||||
for index_op in index_ops {
|
||||
index_op.visit_value_locals(r, w);
|
||||
}
|
||||
default.visit_value_locals(r, w);
|
||||
for target in targets {
|
||||
target.visit_value_locals(r, w);
|
||||
|
@ -100,12 +96,7 @@ impl SerializedOperator {
|
|||
&SerializedOperator::Set(v, i) | &SerializedOperator::Tee(v, i) => {
|
||||
w(v, i);
|
||||
}
|
||||
&SerializedOperator::StartBlock { ref params, .. }
|
||||
| &SerializedOperator::StartLoop { ref params, .. } => {
|
||||
for &(_, value) in params {
|
||||
w(value, 0);
|
||||
}
|
||||
}
|
||||
&SerializedOperator::StartBlock { .. } | &SerializedOperator::StartLoop { .. } => {}
|
||||
&SerializedOperator::GetArg(..)
|
||||
| &SerializedOperator::Operator(..)
|
||||
| &SerializedOperator::End => {}
|
||||
|
@ -120,8 +111,8 @@ impl SerializedBlockTarget {
|
|||
w: &mut W,
|
||||
) {
|
||||
match self {
|
||||
&SerializedBlockTarget::Branch(_, _, ref ops)
|
||||
| &SerializedBlockTarget::Fallthrough(_, ref ops) => {
|
||||
&SerializedBlockTarget::Branch(_, ref ops)
|
||||
| &SerializedBlockTarget::Fallthrough(ref ops) => {
|
||||
for op in ops {
|
||||
op.visit_value_locals(r, w);
|
||||
}
|
||||
|
@ -133,38 +124,21 @@ impl SerializedBlockTarget {
|
|||
struct SerializedBodyContext<'a> {
|
||||
f: &'a FunctionBody,
|
||||
cfg: &'a CFGInfo,
|
||||
uses: &'a UseCountAnalysis,
|
||||
schedule: &'a Schedule,
|
||||
operators: Vec<SerializedOperator>,
|
||||
}
|
||||
|
||||
impl SerializedBody {
|
||||
pub fn compute(f: &FunctionBody, cfg: &CFGInfo, order: &BlockOrder) -> SerializedBody {
|
||||
let uses = UseCountAnalysis::compute(f);
|
||||
let schedule = Schedule::compute(f, cfg, &uses);
|
||||
|
||||
if log::log_enabled!(log::Level::Trace) {
|
||||
log::trace!("values:");
|
||||
for value in 0..f.values.len() {
|
||||
log::trace!(" * v{}: {:?}", value, f.values[value]);
|
||||
}
|
||||
log::trace!("schedule:");
|
||||
for value in 0..schedule.location.len() {
|
||||
log::trace!(" * v{}: {:?}", value, schedule.location[value]);
|
||||
}
|
||||
|
||||
for block in 0..f.blocks.len() {
|
||||
log::trace!("block{}:", block);
|
||||
log::trace!(
|
||||
" -> at top: {:?}",
|
||||
schedule.compute_at_top_of_block.get(&block)
|
||||
);
|
||||
for &inst in &f.blocks[block].insts {
|
||||
log::trace!(" -> toplevel: v{}", inst.index());
|
||||
log::trace!(
|
||||
" -> after: {:?}",
|
||||
schedule.compute_after_value.get(&inst)
|
||||
);
|
||||
log::trace!(" -> v{}", inst.index());
|
||||
}
|
||||
log::trace!(" -> terminator: {:?}", f.blocks[block].terminator);
|
||||
}
|
||||
|
@ -173,8 +147,6 @@ impl SerializedBody {
|
|||
let mut ctx = SerializedBodyContext {
|
||||
f,
|
||||
cfg,
|
||||
uses: &uses,
|
||||
schedule: &schedule,
|
||||
operators: vec![],
|
||||
};
|
||||
for entry in &order.entries {
|
||||
|
@ -189,25 +161,18 @@ impl SerializedBody {
|
|||
impl<'a> SerializedBodyContext<'a> {
|
||||
fn compute_entry(&mut self, entry: &BlockOrderEntry) {
|
||||
match entry {
|
||||
&BlockOrderEntry::StartBlock(header, ref params, ref results)
|
||||
| &BlockOrderEntry::StartLoop(header, ref params, ref results) => {
|
||||
&BlockOrderEntry::StartBlock(header) | &BlockOrderEntry::StartLoop(header) => {
|
||||
let is_loop = match entry {
|
||||
&BlockOrderEntry::StartLoop(..) => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if is_loop {
|
||||
self.operators.push(SerializedOperator::StartLoop {
|
||||
header,
|
||||
params: params.clone(),
|
||||
results: results.clone(),
|
||||
});
|
||||
self.operators
|
||||
.push(SerializedOperator::StartLoop { header });
|
||||
} else {
|
||||
self.operators.push(SerializedOperator::StartBlock {
|
||||
header,
|
||||
params: params.clone(),
|
||||
results: results.clone(),
|
||||
});
|
||||
self.operators
|
||||
.push(SerializedOperator::StartBlock { header });
|
||||
}
|
||||
}
|
||||
&BlockOrderEntry::End => {
|
||||
|
@ -216,24 +181,12 @@ impl<'a> SerializedBodyContext<'a> {
|
|||
&BlockOrderEntry::BasicBlock(block, ref targets) => {
|
||||
log::trace!("BlockOrderEntry: block{}", block);
|
||||
|
||||
// Capture BlockParams.
|
||||
for &(_, param) in self.f.blocks[block].params.iter().rev() {
|
||||
self.operators.push(SerializedOperator::Set(param, 0));
|
||||
}
|
||||
|
||||
// Schedule ops. First handle the compute-at-top ones.
|
||||
if let Some(compute_at_top) = self.schedule.compute_at_top_of_block.get(&block) {
|
||||
self.schedule_ops(None, &compute_at_top[..]);
|
||||
}
|
||||
|
||||
// Next schedule all toplevels, and values ready to
|
||||
// schedule after each one.
|
||||
// Compute insts' values in sequence.
|
||||
for &inst in &self.f.blocks[block].insts {
|
||||
if let Some(after) = self.schedule.compute_after_value.get(&inst) {
|
||||
self.schedule_ops(Some(inst), &after[..]);
|
||||
} else {
|
||||
self.schedule_ops(Some(inst), &[]);
|
||||
}
|
||||
let mut rev_ops = vec![];
|
||||
self.emit_inst(inst, &mut rev_ops);
|
||||
rev_ops.reverse();
|
||||
self.operators.extend(rev_ops);
|
||||
}
|
||||
|
||||
// For each BlockOrderTarget, compute a SerializedBlockTarget.
|
||||
|
@ -241,21 +194,26 @@ impl<'a> SerializedBodyContext<'a> {
|
|||
.iter()
|
||||
.map(|target| {
|
||||
log::trace!("target: {:?}", target);
|
||||
|
||||
let mut rev_ops = vec![];
|
||||
|
||||
// Store into block param values.
|
||||
for &(_, value) in &self.f.blocks[target.target].params {
|
||||
rev_ops.push(SerializedOperator::Set(value, 0));
|
||||
}
|
||||
|
||||
// Load from branch operator's args.
|
||||
for &value in target.args.iter().rev() {
|
||||
let value = self.f.resolve_alias(value);
|
||||
self.push_value(value, &mut rev_ops);
|
||||
}
|
||||
|
||||
rev_ops.reverse();
|
||||
let tys: Vec<Type> = self.f.blocks[target.target]
|
||||
.params
|
||||
.iter()
|
||||
.map(|&(ty, _)| ty)
|
||||
.collect();
|
||||
log::trace!(" -> ops: {:?}", rev_ops);
|
||||
|
||||
match target.relative_branch {
|
||||
Some(branch) => SerializedBlockTarget::Branch(branch, tys, rev_ops),
|
||||
None => SerializedBlockTarget::Fallthrough(tys, rev_ops),
|
||||
Some(branch) => SerializedBlockTarget::Branch(branch, rev_ops),
|
||||
None => SerializedBlockTarget::Fallthrough(rev_ops),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
@ -286,9 +244,11 @@ impl<'a> SerializedBodyContext<'a> {
|
|||
let value = self.f.resolve_alias(value);
|
||||
self.push_value(value, &mut rev_ops);
|
||||
rev_ops.reverse();
|
||||
self.operators.extend(rev_ops);
|
||||
self.operators
|
||||
.push(SerializedOperator::BrTable { targets, default });
|
||||
self.operators.push(SerializedOperator::BrTable {
|
||||
index_ops: rev_ops,
|
||||
targets,
|
||||
default,
|
||||
});
|
||||
}
|
||||
&Terminator::Return { ref values, .. } => {
|
||||
let mut rev_ops = vec![];
|
||||
|
@ -308,44 +268,8 @@ impl<'a> SerializedBodyContext<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn schedule_ops(&mut self, toplevel: Option<Value>, values: &[Value]) {
|
||||
log::trace!("schedule_ops: toplevel {:?} values {:?}", toplevel, values);
|
||||
// Work backward, generating values in the appropriate order
|
||||
// on the stack if single-use.
|
||||
let mut rev_ops = vec![];
|
||||
let mut to_compute = values
|
||||
.iter()
|
||||
.chain(toplevel.iter())
|
||||
.cloned()
|
||||
.collect::<FxHashSet<_>>();
|
||||
for &value in values.iter().rev() {
|
||||
self.schedule_op(
|
||||
value,
|
||||
&mut rev_ops,
|
||||
/* leave_value_on_stack = */ false,
|
||||
&mut to_compute,
|
||||
);
|
||||
}
|
||||
if let Some(toplevel) = toplevel {
|
||||
self.schedule_op(
|
||||
toplevel,
|
||||
&mut rev_ops,
|
||||
/* leave_value_on_stack = */ false,
|
||||
&mut to_compute,
|
||||
);
|
||||
}
|
||||
rev_ops.reverse();
|
||||
log::trace!(
|
||||
"schedule_ops: toplevel {:?} values {:?} -> ops {:?}",
|
||||
toplevel,
|
||||
values,
|
||||
rev_ops
|
||||
);
|
||||
self.operators.extend(rev_ops.into_iter());
|
||||
}
|
||||
|
||||
fn push_value(&mut self, v: Value, rev_ops: &mut Vec<SerializedOperator>) {
|
||||
let v = self.f.resolve_alias(v);
|
||||
match &self.f.values[v.index()] {
|
||||
&ValueDef::PickOutput(v, i) => {
|
||||
rev_ops.push(SerializedOperator::Get(v, i));
|
||||
|
@ -362,22 +286,8 @@ impl<'a> SerializedBodyContext<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn schedule_op(
|
||||
&mut self,
|
||||
op: Value,
|
||||
rev_ops: &mut Vec<SerializedOperator>,
|
||||
leave_value_on_stack: bool,
|
||||
to_compute: &mut FxHashSet<Value>,
|
||||
) {
|
||||
let op = self.f.resolve_alias(op);
|
||||
if !to_compute.remove(&op) {
|
||||
if leave_value_on_stack {
|
||||
self.push_value(op, rev_ops);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let (operator, operands) = match &self.f.values[op.index()] {
|
||||
fn emit_inst(&mut self, inst: Value, rev_ops: &mut Vec<SerializedOperator>) {
|
||||
let (operator, operands) = match &self.f.values[inst.index()] {
|
||||
&ValueDef::Operator(op, ref operands) => (op, operands),
|
||||
_ => {
|
||||
return;
|
||||
|
@ -386,15 +296,8 @@ impl<'a> SerializedBodyContext<'a> {
|
|||
|
||||
// We're generating ops in reverse order. So we must first
|
||||
// store value.
|
||||
for i in 0..self.f.types[op.index()].len() {
|
||||
if !leave_value_on_stack {
|
||||
rev_ops.push(SerializedOperator::Set(op, i));
|
||||
} else {
|
||||
assert_eq!(i, 0);
|
||||
if self.uses.use_count[op.index()] > 1 {
|
||||
rev_ops.push(SerializedOperator::Tee(op, i));
|
||||
}
|
||||
}
|
||||
for i in 0..self.f.types[inst.index()].len() {
|
||||
rev_ops.push(SerializedOperator::Set(inst, i));
|
||||
}
|
||||
|
||||
rev_ops.push(SerializedOperator::Operator(operator));
|
||||
|
@ -402,24 +305,7 @@ impl<'a> SerializedBodyContext<'a> {
|
|||
// Now push the args in reverse order.
|
||||
for &arg in operands.iter().rev() {
|
||||
let arg = self.f.resolve_alias(arg);
|
||||
match &self.f.values[arg.index()] {
|
||||
&ValueDef::Operator(op, ..) => {
|
||||
if op_rematerialize(&op) {
|
||||
rev_ops.push(SerializedOperator::Operator(op));
|
||||
} else if self.uses.use_count[arg.index()] == 1
|
||||
&& self.f.types[arg.index()].len() == 1
|
||||
{
|
||||
self.schedule_op(
|
||||
arg, rev_ops, /* leave_on_stack = */ true, to_compute,
|
||||
);
|
||||
} else {
|
||||
self.push_value(arg, rev_ops);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.push_value(arg, rev_ops);
|
||||
}
|
||||
}
|
||||
self.push_value(arg, rev_ops);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -284,16 +284,8 @@ pub struct BlockOrder {
|
|||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum BlockOrderEntry {
|
||||
StartBlock(
|
||||
BlockId,
|
||||
Vec<(wasmparser::Type, Value)>,
|
||||
Vec<wasmparser::Type>,
|
||||
),
|
||||
StartLoop(
|
||||
BlockId,
|
||||
Vec<(wasmparser::Type, Value)>,
|
||||
Vec<wasmparser::Type>,
|
||||
),
|
||||
StartBlock(BlockId),
|
||||
StartLoop(BlockId),
|
||||
End,
|
||||
BasicBlock(BlockId, Vec<BlockOrderTarget>),
|
||||
}
|
||||
|
@ -310,15 +302,7 @@ impl BlockOrder {
|
|||
pub fn compute(f: &FunctionBody, cfg: &CFGInfo, wasm_region: &WasmRegion) -> BlockOrder {
|
||||
let mut target_stack = vec![];
|
||||
let mut entries = vec![];
|
||||
Self::generate_region(
|
||||
f,
|
||||
cfg,
|
||||
&mut target_stack,
|
||||
&mut entries,
|
||||
wasm_region,
|
||||
None,
|
||||
true,
|
||||
);
|
||||
Self::generate_region(f, cfg, &mut target_stack, &mut entries, wasm_region, None);
|
||||
log::trace!("entries: {:?}", entries);
|
||||
BlockOrder { entries }
|
||||
}
|
||||
|
@ -330,7 +314,6 @@ impl BlockOrder {
|
|||
entries: &mut Vec<BlockOrderEntry>,
|
||||
region: &WasmRegion,
|
||||
fallthrough: Option<BlockId>,
|
||||
toplevel: bool,
|
||||
) {
|
||||
log::trace!(
|
||||
"BlockOrder::generate_region: stack {:?} region {:?} fallthrough {:?}",
|
||||
|
@ -353,23 +336,11 @@ impl BlockOrder {
|
|||
if let Some(target) = target {
|
||||
target_stack.push(target);
|
||||
}
|
||||
let params = f.blocks[header].params.clone();
|
||||
let results = if toplevel {
|
||||
f.rets.clone()
|
||||
} else {
|
||||
match fallthrough {
|
||||
Some(fallthrough) => f.blocks[fallthrough]
|
||||
.params
|
||||
.iter()
|
||||
.map(|(ty, _)| *ty)
|
||||
.collect(),
|
||||
None => vec![],
|
||||
}
|
||||
};
|
||||
|
||||
if is_loop {
|
||||
entries.push(BlockOrderEntry::StartLoop(header, params, results));
|
||||
entries.push(BlockOrderEntry::StartLoop(header));
|
||||
} else {
|
||||
entries.push(BlockOrderEntry::StartBlock(header, params, results));
|
||||
entries.push(BlockOrderEntry::StartBlock(header));
|
||||
}
|
||||
|
||||
for i in 0..subregions.len() {
|
||||
|
@ -379,15 +350,7 @@ impl BlockOrder {
|
|||
} else {
|
||||
Some(subregions[i + 1].header())
|
||||
};
|
||||
Self::generate_region(
|
||||
f,
|
||||
cfg,
|
||||
target_stack,
|
||||
entries,
|
||||
subregion,
|
||||
fallthrough,
|
||||
false,
|
||||
);
|
||||
Self::generate_region(f, cfg, target_stack, entries, subregion, fallthrough);
|
||||
}
|
||||
|
||||
entries.push(BlockOrderEntry::End);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
use std::convert::TryFrom;
|
||||
|
||||
use crate::ir::*;
|
||||
use crate::op_traits::{op_effects, op_inputs, op_outputs};
|
||||
use crate::op_traits::{op_inputs, op_outputs};
|
||||
use crate::ops::Operator;
|
||||
use anyhow::{bail, Result};
|
||||
use fxhash::{FxHashMap, FxHashSet};
|
||||
|
@ -1181,21 +1181,13 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> {
|
|||
log::trace!(" -> operands: {:?}", input_operands);
|
||||
log::trace!(" -> ty {:?}", outputs);
|
||||
|
||||
let side_effects = !op_effects(&op).unwrap().is_empty();
|
||||
|
||||
let value = if side_effects {
|
||||
self.body
|
||||
.add_mutable_inst(outputs.clone(), ValueDef::Operator(op, input_operands))
|
||||
} else {
|
||||
self.body
|
||||
.add_value(ValueDef::Operator(op, input_operands), outputs.clone())
|
||||
};
|
||||
let value = self
|
||||
.body
|
||||
.add_value(ValueDef::Operator(op, input_operands), outputs.clone());
|
||||
log::trace!(" -> value: {:?}", value);
|
||||
|
||||
if let Some(block) = self.cur_block {
|
||||
if side_effects {
|
||||
self.body.blocks[block].insts.push(value);
|
||||
}
|
||||
self.body.blocks[block].insts.push(value);
|
||||
}
|
||||
|
||||
if n_outputs == 1 {
|
||||
|
|
111
src/ir.rs
111
src/ir.rs
|
@ -1,19 +1,14 @@
|
|||
//! Intermediate representation for Wasm.
|
||||
|
||||
use crate::{
|
||||
backend::{
|
||||
produce_func_wasm, BlockOrder, FuncTypeSink, Locations, LoopNest, SerializedBody,
|
||||
WasmRegion,
|
||||
},
|
||||
backend::{produce_func_wasm, BlockOrder, Locations, LoopNest, SerializedBody, WasmRegion},
|
||||
cfg::CFGInfo,
|
||||
frontend,
|
||||
ops::ty_to_valty,
|
||||
Operator,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use fxhash::FxHashMap;
|
||||
use rayon::prelude::*;
|
||||
use std::collections::hash_map::Entry;
|
||||
use wasmparser::{FuncType, SectionReader, Type};
|
||||
|
||||
pub type SignatureId = usize;
|
||||
|
@ -57,9 +52,7 @@ pub struct FunctionBody {
|
|||
pub rets: Vec<Type>,
|
||||
pub locals: Vec<Type>,
|
||||
pub blocks: Vec<Block>,
|
||||
/// Sea-of-nodes representation.
|
||||
pub values: Vec<ValueDef>,
|
||||
value_dedup: FxHashMap<ValueDef, Value>,
|
||||
/// A single value can have multiple types if multi-value (e.g. a
|
||||
/// call).
|
||||
pub types: Vec</* Value, */ Vec<Type>>,
|
||||
|
@ -86,17 +79,10 @@ impl FunctionBody {
|
|||
|
||||
pub fn add_value(&mut self, value: ValueDef, tys: Vec<Type>) -> Value {
|
||||
log::trace!("add_value: def {:?} ty {:?}", value, tys);
|
||||
let id = match self.value_dedup.entry(value.clone()) {
|
||||
Entry::Occupied(o) => *o.get(),
|
||||
Entry::Vacant(v) => {
|
||||
let id = Value(self.values.len() as u32);
|
||||
self.values.push(value.clone());
|
||||
self.types.push(tys);
|
||||
v.insert(id);
|
||||
id
|
||||
}
|
||||
};
|
||||
let id = Value(self.values.len() as u32);
|
||||
log::trace!(" -> value {:?}", id);
|
||||
self.values.push(value.clone());
|
||||
self.types.push(tys);
|
||||
id
|
||||
}
|
||||
|
||||
|
@ -480,10 +466,10 @@ impl<'a> Module<'a> {
|
|||
|
||||
pub fn to_wasm_bytes(&self) -> Vec<u8> {
|
||||
// Do most of the compilation in parallel: up to the
|
||||
// serialized (pre-regalloc) body and the regalloc
|
||||
// results. Only the "final parts assembly" needs to be
|
||||
// serialized because it can add function signatures.
|
||||
let compiled: Vec<(u32, &FunctionBody, SerializedBody, Locations)> = self
|
||||
// generation of the function that we emit into the final code
|
||||
// seciton in order. Only the "final parts assembly" needs to
|
||||
// be serialized.
|
||||
let compiled: Vec<(u32, wasm_encoder::Function)> = self
|
||||
.funcs
|
||||
.par_iter()
|
||||
.filter_map(|func| match func {
|
||||
|
@ -496,41 +482,40 @@ impl<'a> Module<'a> {
|
|||
log::trace!("serialized: {:?}", serialized);
|
||||
let locations = Locations::compute(body, &serialized);
|
||||
log::trace!("locations: {:?}", locations);
|
||||
Some((sig as u32, body, serialized, locations))
|
||||
let func_body = produce_func_wasm(body, &serialized, &locations);
|
||||
log::trace!("body: {:?}", func_body);
|
||||
|
||||
let mut locals: Vec<(u32, wasm_encoder::ValType)> = vec![];
|
||||
for local_ty in func_body.locals {
|
||||
if locals.len() > 0 && locals.last().unwrap().1 == local_ty {
|
||||
locals.last_mut().unwrap().0 += 1;
|
||||
} else {
|
||||
locals.push((1, local_ty));
|
||||
}
|
||||
}
|
||||
let mut func = wasm_encoder::Function::new(locals);
|
||||
|
||||
for inst in func_body.operators {
|
||||
func.instruction(&inst);
|
||||
}
|
||||
|
||||
Some((sig as u32, func))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Build the final code section and function-type section.
|
||||
let mut signatures = SignatureAdder::new(&self);
|
||||
let mut code_section = wasm_encoder::CodeSection::new();
|
||||
let mut func_section = wasm_encoder::FunctionSection::new();
|
||||
for (sig, body, serialized, locations) in compiled {
|
||||
let func_body = produce_func_wasm(body, &serialized, &locations, &mut signatures);
|
||||
log::trace!("body: {:?}", func_body);
|
||||
|
||||
let mut locals: Vec<(u32, wasm_encoder::ValType)> = vec![];
|
||||
for local_ty in func_body.locals {
|
||||
if locals.len() > 0 && locals.last().unwrap().1 == local_ty {
|
||||
locals.last_mut().unwrap().0 += 1;
|
||||
} else {
|
||||
locals.push((1, local_ty));
|
||||
}
|
||||
}
|
||||
let mut func = wasm_encoder::Function::new(locals);
|
||||
|
||||
for inst in func_body.operators {
|
||||
func.instruction(&inst);
|
||||
}
|
||||
|
||||
func_section.function(sig);
|
||||
for (sig, func) in compiled {
|
||||
func_section.function(sig as u32);
|
||||
code_section.function(&func);
|
||||
}
|
||||
|
||||
// Build the final function-signature (type) section.
|
||||
let mut type_section = wasm_encoder::TypeSection::new();
|
||||
for sig in &signatures.signatures {
|
||||
for sig in &self.signatures {
|
||||
let params: Vec<wasm_encoder::ValType> =
|
||||
sig.params.iter().map(|&ty| ty_to_valty(ty)).collect();
|
||||
let returns: Vec<wasm_encoder::ValType> =
|
||||
|
@ -612,41 +597,3 @@ impl<'a> Module<'a> {
|
|||
module.finish()
|
||||
}
|
||||
}
|
||||
|
||||
struct SignatureAdder {
|
||||
signatures: Vec<FuncType>,
|
||||
signature_dedup: FxHashMap<FuncType, u32>,
|
||||
}
|
||||
|
||||
impl SignatureAdder {
|
||||
fn new(module: &Module<'_>) -> Self {
|
||||
let signature_dedup: FxHashMap<FuncType, u32> = module
|
||||
.signatures
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, sig)| (sig.clone(), idx as u32))
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
signatures: module.signatures.clone(),
|
||||
signature_dedup,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FuncTypeSink for SignatureAdder {
|
||||
fn add_signature(&mut self, params: Vec<Type>, results: Vec<Type>) -> u32 {
|
||||
let ft = wasmparser::FuncType {
|
||||
params: params.into_boxed_slice(),
|
||||
returns: results.into_boxed_slice(),
|
||||
};
|
||||
match self.signature_dedup.entry(ft.clone()) {
|
||||
Entry::Occupied(o) => *o.get(),
|
||||
Entry::Vacant(v) => {
|
||||
let idx = self.signatures.len() as u32;
|
||||
self.signatures.push(ft);
|
||||
*v.insert(idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ mod frontend;
|
|||
mod ir;
|
||||
mod op_traits;
|
||||
mod ops;
|
||||
mod use_count;
|
||||
|
||||
pub use ir::*;
|
||||
pub use ops::Operator;
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
//! Use-count analysis.
|
||||
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use crate::{FunctionBody, Value, ValueDef};
|
||||
use fxhash::FxHashSet;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UseCountAnalysis {
|
Loading…
Reference in a new issue