working roundtrip of hello world?

This commit is contained in:
Chris Fallin 2021-12-24 23:02:20 -08:00
parent 1b67d25efb
commit 5e3edc1de4
8 changed files with 112 additions and 377 deletions

View file

@ -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

View file

@ -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;

View file

@ -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);
}
}
}

View file

@ -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);

View file

@ -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
View file

@ -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)
}
}
}
}

View file

@ -13,6 +13,7 @@ mod frontend;
mod ir;
mod op_traits;
mod ops;
mod use_count;
pub use ir::*;
pub use ops::Operator;

View file

@ -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 {