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