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