This commit is contained in:
Chris Fallin 2022-11-29 01:44:19 -08:00
parent d0ecd1238a
commit 8744965705
4 changed files with 361 additions and 53 deletions

View file

@ -1,8 +1,10 @@
//! Backend: IR to Wasm.
use crate::cfg::CFGInfo;
use crate::ir::{FunctionBody, Value};
use crate::entity::EntityRef;
use crate::ir::{FunctionBody, Value, ValueDef};
use crate::passes::rpo::RPO;
use crate::Operator;
use anyhow::Result;
use std::borrow::Cow;
@ -21,6 +23,12 @@ pub struct WasmBackend<'a> {
locals: Localifier,
}
macro_rules! op {
($name:tt) => {
Some(wasm_encoder::Instruction::$name)
};
}
impl<'a> WasmBackend<'a> {
pub fn new(body: &'a FunctionBody) -> Result<WasmBackend<'a>> {
log::debug!("Backend compiling:\n{}\n", body.display_verbose("| "));
@ -66,6 +74,8 @@ impl<'a> WasmBackend<'a> {
_ => {}
}
log::debug!("Compiled to:\n{:?}\n", func);
Ok(func)
}
@ -130,15 +140,26 @@ impl<'a> WasmBackend<'a> {
}
WasmBlock::Leaf { block } => {
for &inst in &self.body.blocks[*block].insts {
self.lower_inst(inst, func);
// If this value is "owned", do nothing: it will be lowered in
// the one place it's used.
if self.trees.owner.contains_key(&inst) {
return;
}
self.lower_inst(inst, /* root = */ true, func);
}
}
WasmBlock::BlockParams { from, to } => {
debug_assert_eq!(from.len(), to.len());
for &from in from.iter() {
for (&from, &(_, to)) in from.iter().zip(to.iter()) {
if self.locals.values[to].is_empty() {
continue;
}
self.lower_value(from, func);
}
for &(_, to) in to.iter().rev() {
if self.locals.values[to].is_empty() {
continue;
}
self.lower_set_value(to, func);
}
}
@ -155,14 +176,330 @@ impl<'a> WasmBackend<'a> {
}
fn lower_value(&self, value: Value, func: &mut wasm_encoder::Function) {
todo!()
let value = self.body.resolve_alias(value);
let local = match &self.body.values[value] {
&ValueDef::BlockParam(..) | &ValueDef::Operator(..) => self.locals.values[value][0],
&ValueDef::PickOutput(orig_value, idx, _) => self.locals.values[orig_value][idx],
_ => unreachable!(),
};
func.instruction(&wasm_encoder::Instruction::LocalGet(local.index() as u32));
}
fn lower_set_value(&self, value: Value, func: &mut wasm_encoder::Function) {
todo!()
debug_assert_eq!(
self.locals.values[value].len(),
1,
"Value {} has no local",
value
);
let local = self.locals.values[value][0];
func.instruction(&wasm_encoder::Instruction::LocalSet(local.index() as u32));
}
fn lower_inst(&self, value: Value, func: &mut wasm_encoder::Function) {
todo!()
fn lower_inst(&self, value: Value, root: bool, func: &mut wasm_encoder::Function) {
match &self.body.values[value] {
&ValueDef::Operator(ref op, ref args, _) => {
for &arg in &args[..] {
if self.trees.owner.contains_key(&arg) {
self.lower_inst(arg, /* root = */ false, func);
} else {
self.lower_value(arg, func);
}
}
self.lower_op(op, func);
if root {
for &local in &self.locals.values[value] {
func.instruction(
&wasm_encoder::Instruction::LocalSet(local.index() as u32),
);
}
}
}
_ => unreachable!(),
}
}
fn lower_op(&self, op: &Operator, func: &mut wasm_encoder::Function) {
let inst = match op {
Operator::Unreachable => Some(wasm_encoder::Instruction::Unreachable),
Operator::Nop => None,
Operator::Call { function_index } => Some(wasm_encoder::Instruction::Call(
function_index.index() as u32,
)),
Operator::CallIndirect {
sig_index,
table_index,
} => Some(wasm_encoder::Instruction::CallIndirect {
ty: sig_index.index() as u32,
table: table_index.index() as u32,
}),
Operator::Return => Some(wasm_encoder::Instruction::Return),
Operator::Select => Some(wasm_encoder::Instruction::Select),
Operator::TypedSelect { ty } => Some(wasm_encoder::Instruction::TypedSelect(
wasm_encoder::ValType::from(*ty),
)),
Operator::GlobalGet { global_index } => Some(wasm_encoder::Instruction::GlobalGet(
global_index.index() as u32,
)),
Operator::GlobalSet { global_index } => Some(wasm_encoder::Instruction::GlobalSet(
global_index.index() as u32,
)),
Operator::I32Load { memory } => Some(wasm_encoder::Instruction::I32Load(
wasm_encoder::MemArg::from(*memory),
)),
Operator::I64Load { memory } => Some(wasm_encoder::Instruction::I64Load(
wasm_encoder::MemArg::from(*memory),
)),
Operator::F32Load { memory } => Some(wasm_encoder::Instruction::F32Load(
wasm_encoder::MemArg::from(*memory),
)),
Operator::F64Load { memory } => Some(wasm_encoder::Instruction::F64Load(
wasm_encoder::MemArg::from(*memory),
)),
Operator::I32Load8S { memory } => Some(wasm_encoder::Instruction::I32Load8S(
wasm_encoder::MemArg::from(*memory),
)),
Operator::I32Load8U { memory } => Some(wasm_encoder::Instruction::I32Load8U(
wasm_encoder::MemArg::from(*memory),
)),
Operator::I32Load16S { memory } => Some(wasm_encoder::Instruction::I32Load16S(
wasm_encoder::MemArg::from(*memory),
)),
Operator::I32Load16U { memory } => Some(wasm_encoder::Instruction::I32Load16U(
wasm_encoder::MemArg::from(*memory),
)),
Operator::I64Load8S { memory } => Some(wasm_encoder::Instruction::I64Load8S(
wasm_encoder::MemArg::from(*memory),
)),
Operator::I64Load8U { memory } => Some(wasm_encoder::Instruction::I64Load8U(
wasm_encoder::MemArg::from(*memory),
)),
Operator::I64Load16S { memory } => Some(wasm_encoder::Instruction::I64Load16S(
wasm_encoder::MemArg::from(*memory),
)),
Operator::I64Load16U { memory } => Some(wasm_encoder::Instruction::I64Load16U(
wasm_encoder::MemArg::from(*memory),
)),
Operator::I64Load32S { memory } => Some(wasm_encoder::Instruction::I64Load32S(
wasm_encoder::MemArg::from(*memory),
)),
Operator::I64Load32U { memory } => Some(wasm_encoder::Instruction::I64Load32U(
wasm_encoder::MemArg::from(*memory),
)),
Operator::I32Store { memory } => Some(wasm_encoder::Instruction::I32Store(
wasm_encoder::MemArg::from(*memory),
)),
Operator::I64Store { memory } => Some(wasm_encoder::Instruction::I64Store(
wasm_encoder::MemArg::from(*memory),
)),
Operator::F32Store { memory } => Some(wasm_encoder::Instruction::F32Store(
wasm_encoder::MemArg::from(*memory),
)),
Operator::F64Store { memory } => Some(wasm_encoder::Instruction::F64Store(
wasm_encoder::MemArg::from(*memory),
)),
Operator::I32Store8 { memory } => Some(wasm_encoder::Instruction::I32Store8(
wasm_encoder::MemArg::from(*memory),
)),
Operator::I32Store16 { memory } => Some(wasm_encoder::Instruction::I32Store16(
wasm_encoder::MemArg::from(*memory),
)),
Operator::I64Store8 { memory } => Some(wasm_encoder::Instruction::I64Store8(
wasm_encoder::MemArg::from(*memory),
)),
Operator::I64Store16 { memory } => Some(wasm_encoder::Instruction::I64Store16(
wasm_encoder::MemArg::from(*memory),
)),
Operator::I64Store32 { memory } => Some(wasm_encoder::Instruction::I64Store32(
wasm_encoder::MemArg::from(*memory),
)),
Operator::I32Const { value } => {
Some(wasm_encoder::Instruction::I32Const(*value as i32))
}
Operator::I64Const { value } => {
Some(wasm_encoder::Instruction::I64Const(*value as i64))
}
Operator::F32Const { value } => {
Some(wasm_encoder::Instruction::F32Const(f32::from_bits(*value)))
}
Operator::F64Const { value } => {
Some(wasm_encoder::Instruction::F64Const(f64::from_bits(*value)))
}
Operator::I32Eqz => op!(I32Eqz),
Operator::I32Eq => op!(I32Eq),
Operator::I32Ne => op!(I32Ne),
Operator::I32LtS => op!(I32LtS),
Operator::I32LtU => op!(I32LtU),
Operator::I32GtS => op!(I32GtS),
Operator::I32GtU => op!(I32GtU),
Operator::I32LeS => op!(I32LeS),
Operator::I32LeU => op!(I32LeU),
Operator::I32GeS => op!(I32GeS),
Operator::I32GeU => op!(I32GeU),
Operator::I64Eqz => op!(I64Eqz),
Operator::I64Eq => op!(I64Eq),
Operator::I64Ne => op!(I64Ne),
Operator::I64LtS => op!(I64LtS),
Operator::I64LtU => op!(I64LtU),
Operator::I64GtU => op!(I64GtU),
Operator::I64GtS => op!(I64GtS),
Operator::I64LeS => op!(I64LeS),
Operator::I64LeU => op!(I64LeU),
Operator::I64GeS => op!(I64GeS),
Operator::I64GeU => op!(I64GeU),
Operator::F32Eq => op!(F32Eq),
Operator::F32Ne => op!(F32Ne),
Operator::F32Lt => op!(F32Lt),
Operator::F32Gt => op!(F32Gt),
Operator::F32Le => op!(F32Le),
Operator::F32Ge => op!(F32Ge),
Operator::F64Eq => op!(F64Eq),
Operator::F64Ne => op!(F64Ne),
Operator::F64Lt => op!(F64Lt),
Operator::F64Gt => op!(F64Gt),
Operator::F64Le => op!(F64Le),
Operator::F64Ge => op!(F64Ge),
Operator::I32Clz => op!(I32Clz),
Operator::I32Ctz => op!(I32Ctz),
Operator::I32Popcnt => op!(I32Popcnt),
Operator::I32Add => op!(I32Add),
Operator::I32Sub => op!(I32Sub),
Operator::I32Mul => op!(I32Mul),
Operator::I32DivS => op!(I32DivS),
Operator::I32DivU => op!(I32DivU),
Operator::I32RemS => op!(I32RemS),
Operator::I32RemU => op!(I32RemU),
Operator::I32And => op!(I32And),
Operator::I32Or => op!(I32Or),
Operator::I32Xor => op!(I32Xor),
Operator::I32Shl => op!(I32Shl),
Operator::I32ShrS => op!(I32ShrS),
Operator::I32ShrU => op!(I32ShrU),
Operator::I32Rotl => op!(I32Rotl),
Operator::I32Rotr => op!(I32Rotr),
Operator::I64Clz => op!(I64Clz),
Operator::I64Ctz => op!(I64Ctz),
Operator::I64Popcnt => op!(I64Popcnt),
Operator::I64Add => op!(I64Add),
Operator::I64Sub => op!(I64Sub),
Operator::I64Mul => op!(I64Mul),
Operator::I64DivS => op!(I64DivS),
Operator::I64DivU => op!(I64DivU),
Operator::I64RemS => op!(I64RemS),
Operator::I64RemU => op!(I64RemU),
Operator::I64And => op!(I64And),
Operator::I64Or => op!(I64Or),
Operator::I64Xor => op!(I64Xor),
Operator::I64Shl => op!(I64Shl),
Operator::I64ShrS => op!(I64ShrS),
Operator::I64ShrU => op!(I64ShrU),
Operator::I64Rotl => op!(I64Rotl),
Operator::I64Rotr => op!(I64Rotr),
Operator::F32Abs => op!(F32Abs),
Operator::F32Neg => op!(F32Neg),
Operator::F32Ceil => op!(F32Ceil),
Operator::F32Floor => op!(F32Floor),
Operator::F32Trunc => op!(F32Trunc),
Operator::F32Nearest => op!(F32Nearest),
Operator::F32Sqrt => op!(F32Sqrt),
Operator::F32Add => op!(F32Add),
Operator::F32Sub => op!(F32Sub),
Operator::F32Mul => op!(F32Mul),
Operator::F32Div => op!(F32Div),
Operator::F32Min => op!(F32Min),
Operator::F32Max => op!(F32Max),
Operator::F32Copysign => op!(F32Copysign),
Operator::F64Abs => op!(F64Abs),
Operator::F64Neg => op!(F64Neg),
Operator::F64Ceil => op!(F64Ceil),
Operator::F64Floor => op!(F64Floor),
Operator::F64Trunc => op!(F64Trunc),
Operator::F64Nearest => op!(F64Nearest),
Operator::F64Sqrt => op!(F64Sqrt),
Operator::F64Add => op!(F64Add),
Operator::F64Sub => op!(F64Sub),
Operator::F64Mul => op!(F64Mul),
Operator::F64Div => op!(F64Div),
Operator::F64Min => op!(F64Min),
Operator::F64Max => op!(F64Max),
Operator::F64Copysign => op!(F64Copysign),
Operator::I32WrapI64 => op!(I32WrapI64),
Operator::I32TruncF32S => op!(I32TruncF32S),
Operator::I32TruncF32U => op!(I32TruncF32U),
Operator::I32TruncF64S => op!(I32TruncF64S),
Operator::I32TruncF64U => op!(I32TruncF64U),
Operator::I64ExtendI32S => op!(I64ExtendI32S),
Operator::I64ExtendI32U => op!(I64ExtendI32U),
Operator::I64TruncF32S => op!(I64TruncF32S),
Operator::I64TruncF32U => op!(I64TruncF32U),
Operator::I64TruncF64S => op!(I64TruncF64S),
Operator::I64TruncF64U => op!(I64TruncF64U),
Operator::F32ConvertI32S => op!(F32ConvertI32S),
Operator::F32ConvertI32U => op!(F32ConvertI32U),
Operator::F32ConvertI64S => op!(F32ConvertI64S),
Operator::F32ConvertI64U => op!(F32ConvertI64U),
Operator::F32DemoteF64 => op!(F32DemoteF64),
Operator::F64ConvertI32S => op!(F64ConvertI32S),
Operator::F64ConvertI32U => op!(F64ConvertI32U),
Operator::F64ConvertI64S => op!(F64ConvertI64S),
Operator::F64ConvertI64U => op!(F64ConvertI64U),
Operator::F64PromoteF32 => op!(F64PromoteF32),
Operator::I32Extend8S => op!(I32Extend8S),
Operator::I32Extend16S => op!(I32Extend16S),
Operator::I64Extend8S => op!(I64Extend8S),
Operator::I64Extend16S => op!(I64Extend16S),
Operator::I64Extend32S => op!(I64Extend32S),
Operator::I32TruncSatF32S => op!(I32TruncSatF32S),
Operator::I32TruncSatF32U => op!(I32TruncSatF32U),
Operator::I32TruncSatF64S => op!(I32TruncSatF64S),
Operator::I32TruncSatF64U => op!(I32TruncSatF64U),
Operator::I64TruncSatF32S => op!(I64TruncSatF32S),
Operator::I64TruncSatF32U => op!(I64TruncSatF32U),
Operator::I64TruncSatF64S => op!(I64TruncSatF64S),
Operator::I64TruncSatF64U => op!(I64TruncSatF64U),
Operator::F32ReinterpretI32 => op!(F32ReinterpretI32),
Operator::F64ReinterpretI64 => op!(F64ReinterpretI64),
Operator::I32ReinterpretF32 => op!(I32ReinterpretF32),
Operator::I64ReinterpretF64 => op!(I64ReinterpretF64),
Operator::TableGet { table_index } => Some(wasm_encoder::Instruction::TableGet(
table_index.index() as u32,
)),
Operator::TableSet { table_index } => Some(wasm_encoder::Instruction::TableSet(
table_index.index() as u32,
)),
Operator::TableGrow { table_index } => Some(wasm_encoder::Instruction::TableGrow(
table_index.index() as u32,
)),
Operator::TableSize { table_index } => Some(wasm_encoder::Instruction::TableSize(
table_index.index() as u32,
)),
Operator::MemorySize { mem } => {
Some(wasm_encoder::Instruction::MemorySize(mem.index() as u32))
}
Operator::MemoryGrow { mem } => {
Some(wasm_encoder::Instruction::MemoryGrow(mem.index() as u32))
}
};
if let Some(inst) = inst {
func.instruction(&inst);
}
}
}

View file

@ -1316,14 +1316,8 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> {
}
fn emit(&mut self, op: Operator) -> Result<()> {
let inputs = op_inputs(
self.module,
self.my_sig,
&self.body.locals,
&self.op_stack[..],
&op,
)?;
let outputs = op_outputs(self.module, &self.body.locals, &self.op_stack[..], &op)?;
let inputs = op_inputs(self.module, self.my_sig, &self.op_stack[..], &op)?;
let outputs = op_outputs(self.module, &self.op_stack[..], &op)?;
log::trace!(
"emit into block {:?}: op {:?} inputs {:?}",

View file

@ -1,7 +1,6 @@
//! Metadata on operators.
use crate::entity::EntityVec;
use crate::ir::{Local, Module, Signature, Type, Value};
use crate::ir::{Module, Signature, Type, Value};
use crate::Operator;
use anyhow::Result;
use std::borrow::Cow;
@ -9,7 +8,6 @@ use std::borrow::Cow;
pub fn op_inputs(
module: &Module,
my_sig: Signature,
my_locals: &EntityVec<Local, Type>,
op_stack: &[(Type, Value)],
op: &Operator,
) -> Result<Cow<'static, [Type]>> {
@ -27,11 +25,6 @@ pub fn op_inputs(
}
&Operator::Return => Ok(Vec::from(module.signature(my_sig).returns.clone()).into()),
&Operator::LocalSet { local_index } | &Operator::LocalTee { local_index } => {
Ok(vec![my_locals[local_index]].into())
}
&Operator::LocalGet { .. } => Ok(Cow::Borrowed(&[])),
&Operator::Select => {
let val_ty = op_stack[op_stack.len() - 2].0;
Ok(vec![val_ty, val_ty, Type::I32].into())
@ -235,7 +228,6 @@ pub fn op_inputs(
pub fn op_outputs(
module: &Module,
my_locals: &EntityVec<Local, Type>,
op_stack: &[(Type, Value)],
op: &Operator,
) -> Result<Cow<'static, [Type]>> {
@ -250,10 +242,6 @@ pub fn op_outputs(
Ok(Vec::from(module.signature(sig_index).returns.clone()).into())
}
&Operator::Return => Ok(Cow::Borrowed(&[])),
&Operator::LocalSet { .. } => Ok(Cow::Borrowed(&[])),
&Operator::LocalGet { local_index } | &Operator::LocalTee { local_index } => {
Ok(vec![my_locals[local_index]].into())
}
&Operator::Select => {
let val_ty = op_stack[op_stack.len() - 2].0;
@ -469,9 +457,6 @@ impl Operator {
&Operator::Call { .. } => &[All],
&Operator::CallIndirect { .. } => &[All],
&Operator::Return => &[Return],
&Operator::LocalSet { .. } => &[WriteLocal],
&Operator::LocalGet { .. } => &[ReadLocal],
&Operator::LocalTee { .. } => &[ReadLocal, WriteLocal],
&Operator::Select => &[],
&Operator::TypedSelect { .. } => &[],
@ -675,9 +660,6 @@ impl std::fmt::Display for Operator {
table_index,
} => write!(f, "call_indirect<{}, {}>", sig_index, table_index)?,
&Operator::Return => write!(f, "return")?,
&Operator::LocalSet { local_index, .. } => write!(f, "local_set<{}>", local_index)?,
&Operator::LocalGet { local_index, .. } => write!(f, "local_get<{}>", local_index)?,
&Operator::LocalTee { local_index, .. } => write!(f, "local_tee<{}>", local_index)?,
&Operator::Select => write!(f, "select")?,
&Operator::TypedSelect { ty } => write!(f, "typed_select<{}>", ty)?,

View file

@ -1,6 +1,6 @@
//! Operators.
use crate::{Func, Global, Local, Memory, Signature, Table, Type};
use crate::{entity::EntityRef, Func, Global, Memory, Signature, Table, Type};
use std::convert::TryFrom;
pub use wasmparser::{Ieee32, Ieee64};
@ -34,15 +34,6 @@ pub enum Operator {
table_index: Table,
},
Return,
LocalSet {
local_index: Local,
},
LocalTee {
local_index: Local,
},
LocalGet {
local_index: Local,
},
Select,
TypedSelect {
ty: Type,
@ -326,15 +317,9 @@ impl<'a, 'b> std::convert::TryFrom<&'b wasmparser::Operator<'a>> for Operator {
table_index: Table::from(table_index),
}),
&wasmparser::Operator::Return => Ok(Operator::Return),
&wasmparser::Operator::LocalSet { local_index } => Ok(Operator::LocalSet {
local_index: Local::from(local_index),
}),
&wasmparser::Operator::LocalTee { local_index } => Ok(Operator::LocalTee {
local_index: Local::from(local_index),
}),
&wasmparser::Operator::LocalGet { local_index } => Ok(Operator::LocalGet {
local_index: Local::from(local_index),
}),
&wasmparser::Operator::LocalSet { .. } => Err(()),
&wasmparser::Operator::LocalTee { .. } => Err(()),
&wasmparser::Operator::LocalGet { .. } => Err(()),
&wasmparser::Operator::Select => Ok(Operator::Select),
&wasmparser::Operator::TypedSelect { ty } => {
Ok(Operator::TypedSelect { ty: ty.into() })
@ -594,3 +579,13 @@ impl std::convert::From<wasmparser::MemArg> for MemoryArg {
}
}
}
impl std::convert::From<MemoryArg> for wasm_encoder::MemArg {
fn from(value: MemoryArg) -> wasm_encoder::MemArg {
wasm_encoder::MemArg {
offset: value.offset as u64,
align: value.align,
memory_index: value.memory.index() as u32,
}
}
}