diff --git a/src/backend/mod.rs b/src/backend/mod.rs index c9d954d..77aafcc 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -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> { 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); + } } } diff --git a/src/frontend.rs b/src/frontend.rs index b12d2d3..3a298a4 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -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 {:?}", diff --git a/src/op_traits.rs b/src/op_traits.rs index 4e51e91..7330644 100644 --- a/src/op_traits.rs +++ b/src/op_traits.rs @@ -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, op_stack: &[(Type, Value)], op: &Operator, ) -> Result> { @@ -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, op_stack: &[(Type, Value)], op: &Operator, ) -> Result> { @@ -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)?, diff --git a/src/ops.rs b/src/ops.rs index 9dc3eef..c63984c 100644 --- a/src/ops.rs +++ b/src/ops.rs @@ -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 for MemoryArg { } } } + +impl std::convert::From 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, + } + } +}