1 //! This module is the central place for machine code emission. 2 //! It defines an implementation of wasmparser's Visitor trait 3 //! for `CodeGen`; which defines a visitor per op-code, 4 //! which validates and dispatches to the corresponding 5 //! machine code emitter. 6 7 use crate::codegen::CodeGen; 8 use crate::masm::{MacroAssembler, OperandSize, RegImm}; 9 use crate::stack::Val; 10 use wasmparser::ValType; 11 use wasmparser::VisitOperator; 12 13 impl<'a, M> CodeGen<'a, M> 14 where 15 M: MacroAssembler, 16 { 17 fn add_imm_i32(&mut self) { 18 let val = self 19 .context 20 .stack 21 .pop_i32_const() 22 .expect("i32 constant at stack top"); 23 let reg = self 24 .regalloc 25 .pop_to_reg(&mut self.context, OperandSize::S32); 26 27 let dst = RegImm::reg(reg); 28 self.context 29 .masm 30 .add(dst, dst, RegImm::imm(val), OperandSize::S32); 31 self.context.stack.push(Val::reg(reg)); 32 } 33 34 fn add_i32(&mut self) { 35 let src = self 36 .regalloc 37 .pop_to_reg(&mut self.context, OperandSize::S32); 38 let dst = self 39 .regalloc 40 .pop_to_reg(&mut self.context, OperandSize::S32); 41 42 let lhs = RegImm::reg(dst); 43 self.context 44 .masm 45 .add(lhs, lhs, RegImm::reg(src), OperandSize::S32); 46 47 self.regalloc.free_gpr(src); 48 self.context.stack.push(Val::reg(dst)); 49 } 50 } 51 52 /// A macro to define unsupported WebAssembly operators. 53 /// 54 /// This macro calls itself recursively; 55 /// 1. It no-ops when matching a supported operator. 56 /// 2. Defines the visitor function and panics when 57 /// matching an unsupported operator. 58 macro_rules! def_unsupported { 59 ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => { 60 $( 61 def_unsupported!( 62 emit 63 $op 64 65 fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output { 66 $($(drop($arg);)*)? 67 todo!(stringify!($op)) 68 } 69 ); 70 )* 71 }; 72 73 (emit I32Const $($rest:tt)*) => {}; 74 (emit I32Add $($rest:tt)*) => {}; 75 (emit LocalGet $($rest:tt)*) => {}; 76 (emit LocalSet $($rest:tt)*) => {}; 77 (emit End $($rest:tt)*) => {}; 78 79 (emit $unsupported:tt $($rest:tt)*) => {$($rest)*}; 80 } 81 82 impl<'a, M> VisitOperator<'a> for CodeGen<'a, M> 83 where 84 M: MacroAssembler, 85 { 86 type Output = (); 87 88 fn visit_i32_const(&mut self, val: i32) { 89 self.context.stack.push(Val::i32(val)); 90 } 91 92 fn visit_i32_add(&mut self) { 93 let is_const = self 94 .context 95 .stack 96 .peek() 97 .expect("value at stack top") 98 .is_i32_const(); 99 100 if is_const { 101 self.add_imm_i32(); 102 } else { 103 self.add_i32(); 104 } 105 } 106 107 fn visit_end(&mut self) {} 108 109 fn visit_local_get(&mut self, index: u32) { 110 let context = &mut self.context; 111 let slot = context 112 .frame 113 .get_local(index) 114 .expect(&format!("valid local at slot = {}", index)); 115 match slot.ty { 116 ValType::I32 | ValType::I64 => context.stack.push(Val::local(index)), 117 _ => panic!("Unsupported type {:?} for local", slot.ty), 118 } 119 } 120 121 // TODO verify the case where the target local is on the stack. 122 fn visit_local_set(&mut self, index: u32) { 123 let context = &mut self.context; 124 let frame = context.frame; 125 let slot = frame 126 .get_local(index) 127 .expect(&format!("vald local at slot = {}", index)); 128 let size: OperandSize = slot.ty.into(); 129 let src = self.regalloc.pop_to_reg(context, size); 130 let addr = context.masm.local_address(&slot); 131 context.masm.store(RegImm::reg(src), addr, size); 132 self.regalloc.free_gpr(src); 133 } 134 135 wasmparser::for_each_operator!(def_unsupported); 136 } 137 138 impl From<ValType> for OperandSize { 139 fn from(ty: ValType) -> OperandSize { 140 match ty { 141 ValType::I32 => OperandSize::S32, 142 ValType::I64 => OperandSize::S64, 143 ty => todo!("unsupported type {:?}", ty), 144 } 145 } 146 } 147