//! This module is the central place for machine code emission. //! It defines an implementation of wasmparser's Visitor trait //! for `CodeGen`; which defines a visitor per op-code, //! which validates and dispatches to the corresponding //! machine code emitter. use crate::codegen::CodeGen; use crate::masm::{MacroAssembler, OperandSize, RegImm}; use crate::stack::Val; use wasmparser::ValType; use wasmparser::VisitOperator; impl<'a, M> CodeGen<'a, M> where M: MacroAssembler, { fn add_imm_i32(&mut self) { let val = self .context .stack .pop_i32_const() .expect("i32 constant at stack top"); let reg = self .regalloc .pop_to_reg(&mut self.context, OperandSize::S32); let dst = RegImm::reg(reg); self.context .masm .add(dst, dst, RegImm::imm(val), OperandSize::S32); self.context.stack.push(Val::reg(reg)); } fn add_i32(&mut self) { let src = self .regalloc .pop_to_reg(&mut self.context, OperandSize::S32); let dst = self .regalloc .pop_to_reg(&mut self.context, OperandSize::S32); let lhs = RegImm::reg(dst); self.context .masm .add(lhs, lhs, RegImm::reg(src), OperandSize::S32); self.regalloc.free_gpr(src); self.context.stack.push(Val::reg(dst)); } } /// A macro to define unsupported WebAssembly operators. /// /// This macro calls itself recursively; /// 1. It no-ops when matching a supported operator. /// 2. Defines the visitor function and panics when /// matching an unsupported operator. macro_rules! def_unsupported { ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => { $( def_unsupported!( emit $op fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output { $($(drop($arg);)*)? todo!(stringify!($op)) } ); )* }; (emit I32Const $($rest:tt)*) => {}; (emit I32Add $($rest:tt)*) => {}; (emit LocalGet $($rest:tt)*) => {}; (emit LocalSet $($rest:tt)*) => {}; (emit End $($rest:tt)*) => {}; (emit $unsupported:tt $($rest:tt)*) => {$($rest)*}; } impl<'a, M> VisitOperator<'a> for CodeGen<'a, M> where M: MacroAssembler, { type Output = (); fn visit_i32_const(&mut self, val: i32) { self.context.stack.push(Val::i32(val)); } fn visit_i32_add(&mut self) { let is_const = self .context .stack .peek() .expect("value at stack top") .is_i32_const(); if is_const { self.add_imm_i32(); } else { self.add_i32(); } } fn visit_end(&mut self) {} fn visit_local_get(&mut self, index: u32) { let context = &mut self.context; let slot = context .frame .get_local(index) .expect(&format!("valid local at slot = {}", index)); match slot.ty { ValType::I32 | ValType::I64 => context.stack.push(Val::local(index)), _ => panic!("Unsupported type {:?} for local", slot.ty), } } // TODO verify the case where the target local is on the stack. fn visit_local_set(&mut self, index: u32) { let context = &mut self.context; let frame = context.frame; let slot = frame .get_local(index) .expect(&format!("vald local at slot = {}", index)); let size: OperandSize = slot.ty.into(); let src = self.regalloc.pop_to_reg(context, size); let addr = context.masm.local_address(&slot); context.masm.store(RegImm::reg(src), addr, size); self.regalloc.free_gpr(src); } wasmparser::for_each_operator!(def_unsupported); } impl From for OperandSize { fn from(ty: ValType) -> OperandSize { match ty { ValType::I32 => OperandSize::S32, ValType::I64 => OperandSize::S64, ty => todo!("unsupported type {:?}", ty), } } }