1 use super::ControlStackFrame;
2 use crate::{
3     Result,
4     abi::{ABIOperand, ABIResults, RetArea, vmctx},
5     bail,
6     codegen::{BranchState, CodeGenError, CodeGenPhase, Emission, Prologue},
7     ensure,
8     frame::Frame,
9     isa::reg::RegClass,
10     masm::{
11         ExtractLaneKind, Imm, IntScratch, MacroAssembler, MemMoveDirection, OperandSize, RegImm,
12         ReplaceLaneKind, SPOffset, ShiftKind, StackSlot,
13     },
14     reg::{Reg, WritableReg, writable},
15     regalloc::RegAlloc,
16     stack::{Stack, TypedReg, Val},
17 };
18 use wasmparser::{Ieee32, Ieee64};
19 use wasmtime_environ::{VMOffsets, WasmHeapType, WasmValType};
20 
21 /// The code generation context.
22 /// The code generation context is made up of three
23 /// essential data structures:
24 ///
25 /// * The register allocator, in charge of keeping the inventory of register
26 ///   availability.
27 /// * The value stack, which keeps track of the state of the values
28 ///   after each operation.
29 /// * The current function's frame.
30 ///
31 /// These data structures normally require cooperating with each other
32 /// to perform most of the operations needed during the code
33 /// generation process. The code generation context should
34 /// be generally used as the single entry point to access
35 /// the compound functionality provided by its elements.
36 pub(crate) struct CodeGenContext<'a, P: CodeGenPhase> {
37     /// The register allocator.
38     pub regalloc: RegAlloc,
39     /// The value stack.
40     pub stack: Stack,
41     /// The current function's frame.
42     pub frame: Frame<P>,
43     /// Reachability state.
44     pub reachable: bool,
45     /// A reference to the VMOffsets.
46     pub vmoffsets: &'a VMOffsets<u8>,
47 }
48 
49 impl<'a> CodeGenContext<'a, Emission> {
50     /// Prepares arguments for emitting an i32 shift operation.
i32_shift<M>(&mut self, masm: &mut M, kind: ShiftKind) -> Result<()> where M: MacroAssembler,51     pub fn i32_shift<M>(&mut self, masm: &mut M, kind: ShiftKind) -> Result<()>
52     where
53         M: MacroAssembler,
54     {
55         let top = self
56             .stack
57             .peek()
58             .ok_or_else(|| CodeGenError::missing_values_in_stack())?;
59 
60         if top.is_i32_const() {
61             let val = self
62                 .stack
63                 .pop_i32_const()
64                 .ok_or_else(|| CodeGenError::missing_values_in_stack())?;
65             let typed_reg = self.pop_to_reg(masm, None)?;
66             masm.shift_ir(
67                 writable!(typed_reg.reg),
68                 Imm::i32(val),
69                 typed_reg.reg,
70                 kind,
71                 OperandSize::S32,
72             )?;
73             self.stack.push(typed_reg.into());
74         } else {
75             masm.shift(self, kind, OperandSize::S32)?;
76         }
77         Ok(())
78     }
79 
80     /// Prepares arguments for emitting an i64 binary operation.
i64_shift<M>(&mut self, masm: &mut M, kind: ShiftKind) -> Result<()> where M: MacroAssembler,81     pub fn i64_shift<M>(&mut self, masm: &mut M, kind: ShiftKind) -> Result<()>
82     where
83         M: MacroAssembler,
84     {
85         let top = self
86             .stack
87             .peek()
88             .ok_or_else(|| CodeGenError::missing_values_in_stack())?;
89         if top.is_i64_const() {
90             let val = self
91                 .stack
92                 .pop_i64_const()
93                 .ok_or_else(|| CodeGenError::missing_values_in_stack())?;
94             let typed_reg = self.pop_to_reg(masm, None)?;
95             masm.shift_ir(
96                 writable!(typed_reg.reg),
97                 Imm::i64(val),
98                 typed_reg.reg,
99                 kind,
100                 OperandSize::S64,
101             )?;
102             self.stack.push(typed_reg.into());
103         } else {
104             masm.shift(self, kind, OperandSize::S64)?;
105         };
106 
107         Ok(())
108     }
109 }
110 
111 impl<'a> CodeGenContext<'a, Prologue> {
112     /// Create a new code generation context.
new( regalloc: RegAlloc, stack: Stack, frame: Frame<Prologue>, vmoffsets: &'a VMOffsets<u8>, ) -> Self113     pub fn new(
114         regalloc: RegAlloc,
115         stack: Stack,
116         frame: Frame<Prologue>,
117         vmoffsets: &'a VMOffsets<u8>,
118     ) -> Self {
119         Self {
120             regalloc,
121             stack,
122             frame,
123             reachable: true,
124             vmoffsets,
125         }
126     }
127 
128     /// Prepares the frame for the [`Emission`] code generation phase.
for_emission(self) -> CodeGenContext<'a, Emission>129     pub fn for_emission(self) -> CodeGenContext<'a, Emission> {
130         CodeGenContext {
131             regalloc: self.regalloc,
132             stack: self.stack,
133             reachable: self.reachable,
134             vmoffsets: self.vmoffsets,
135             frame: self.frame.for_emission(),
136         }
137     }
138 }
139 
140 impl<'a> CodeGenContext<'a, Emission> {
141     /// Request a specific register to the register allocator,
142     /// spilling if not available.
reg<M: MacroAssembler>(&mut self, named: Reg, masm: &mut M) -> Result<Reg>143     pub fn reg<M: MacroAssembler>(&mut self, named: Reg, masm: &mut M) -> Result<Reg> {
144         self.regalloc.reg(named, |regalloc| {
145             Self::spill_impl(&mut self.stack, regalloc, &self.frame, masm)
146         })
147     }
148 
149     /// Allocate a register for the given WebAssembly type.
reg_for_type<M: MacroAssembler>( &mut self, ty: WasmValType, masm: &mut M, ) -> Result<Reg>150     pub fn reg_for_type<M: MacroAssembler>(
151         &mut self,
152         ty: WasmValType,
153         masm: &mut M,
154     ) -> Result<Reg> {
155         use WasmValType::*;
156         match ty {
157             I32 | I64 => self.reg_for_class(RegClass::Int, masm),
158             F32 | F64 => self.reg_for_class(RegClass::Float, masm),
159             // All of our supported architectures use the float registers for vector operations.
160             V128 => self.reg_for_class(RegClass::Float, masm),
161             Ref(rt) => match rt.heap_type {
162                 WasmHeapType::Func | WasmHeapType::Extern => {
163                     self.reg_for_class(RegClass::Int, masm)
164                 }
165                 _ => bail!(CodeGenError::unsupported_wasm_type()),
166             },
167         }
168     }
169 
170     /// Request the register allocator to provide the next available
171     /// register of the specified class.
reg_for_class<M: MacroAssembler>( &mut self, class: RegClass, masm: &mut M, ) -> Result<Reg>172     pub fn reg_for_class<M: MacroAssembler>(
173         &mut self,
174         class: RegClass,
175         masm: &mut M,
176     ) -> Result<Reg> {
177         self.regalloc.reg_for_class(class, &mut |regalloc| {
178             Self::spill_impl(&mut self.stack, regalloc, &self.frame, masm)
179         })
180     }
181 
182     /// Convenience wrapper around `CodeGenContext::reg_for_class`, to
183     /// request the next available general purpose register.
any_gpr<M: MacroAssembler>(&mut self, masm: &mut M) -> Result<Reg>184     pub fn any_gpr<M: MacroAssembler>(&mut self, masm: &mut M) -> Result<Reg> {
185         self.reg_for_class(RegClass::Int, masm)
186     }
187 
188     /// Convenience wrapper around `CodeGenContext::reg_for_class`, to
189     /// request the next available floating point register.
any_fpr<M: MacroAssembler>(&mut self, masm: &mut M) -> Result<Reg>190     pub fn any_fpr<M: MacroAssembler>(&mut self, masm: &mut M) -> Result<Reg> {
191         self.reg_for_class(RegClass::Float, masm)
192     }
193 
194     /// Executes the provided function, guaranteeing that the specified set of
195     /// registers, if any, remain unallocatable throughout the function's
196     /// execution.
without<'r, T, M, F>( &mut self, regs: impl IntoIterator<Item = &'r Reg> + Copy, masm: &mut M, mut f: F, ) -> Result<T> where M: MacroAssembler, F: FnMut(&mut Self, &mut M) -> T,197     pub fn without<'r, T, M, F>(
198         &mut self,
199         regs: impl IntoIterator<Item = &'r Reg> + Copy,
200         masm: &mut M,
201         mut f: F,
202     ) -> Result<T>
203     where
204         M: MacroAssembler,
205         F: FnMut(&mut Self, &mut M) -> T,
206     {
207         for r in regs {
208             self.reg(*r, masm)?;
209         }
210 
211         let result = f(self, masm);
212 
213         for r in regs {
214             self.free_reg(*r);
215         }
216 
217         Ok(result)
218     }
219 
220     /// Free the given register.
free_reg(&mut self, reg: impl Into<Reg>)221     pub fn free_reg(&mut self, reg: impl Into<Reg>) {
222         let reg: Reg = reg.into();
223         self.regalloc.free(reg);
224     }
225 
226     /// Loads the stack top value into the next available register, if
227     /// it isn't already one; spilling if there are no registers
228     /// available.  Optionally the caller may specify a specific
229     /// destination register.
230     /// When a named register is requested and it's not at the top of the
231     /// stack a move from register to register might happen, in which case
232     /// the source register will be freed.
pop_to_reg<M: MacroAssembler>( &mut self, masm: &mut M, named: Option<Reg>, ) -> Result<TypedReg>233     pub fn pop_to_reg<M: MacroAssembler>(
234         &mut self,
235         masm: &mut M,
236         named: Option<Reg>,
237     ) -> Result<TypedReg> {
238         let typed_reg = if let Some(dst) = named {
239             self.stack.pop_named_reg(dst)
240         } else {
241             self.stack.pop_reg()
242         };
243 
244         if let Some(dst) = typed_reg {
245             return Ok(dst);
246         }
247 
248         let val = self.stack.pop().expect("a value at stack top");
249         let reg = if let Some(r) = named {
250             self.reg(r, masm)?
251         } else {
252             self.reg_for_type(val.ty(), masm)?
253         };
254 
255         if val.is_mem() {
256             let mem = val.unwrap_mem();
257             let curr_offset = masm.sp_offset()?.as_u32();
258             let slot_offset = mem.slot.offset.as_u32();
259             ensure!(
260                 curr_offset == slot_offset,
261                 CodeGenError::invalid_sp_offset(),
262             );
263             masm.pop(writable!(reg), val.ty().try_into()?)?;
264         } else {
265             self.move_val_to_reg(&val, reg, masm)?;
266             // Free the source value if it is a register.
267             if val.is_reg() {
268                 self.free_reg(val.unwrap_reg());
269             }
270         }
271 
272         Ok(TypedReg::new(val.ty(), reg))
273     }
274 
275     /// Pops the value stack top and stores it at the specified address.
pop_to_addr<M: MacroAssembler>(&mut self, masm: &mut M, addr: M::Address) -> Result<()>276     pub fn pop_to_addr<M: MacroAssembler>(&mut self, masm: &mut M, addr: M::Address) -> Result<()> {
277         let val = self.stack.pop().expect("a value at stack top");
278         let ty = val.ty();
279         let size: OperandSize = ty.try_into()?;
280         match val {
281             Val::Reg(tr) => {
282                 masm.store(tr.reg.into(), addr, size)?;
283                 self.free_reg(tr.reg);
284             }
285             Val::I32(v) => masm.store(RegImm::i32(v), addr, size)?,
286             Val::I64(v) => masm.store(RegImm::i64(v), addr, size)?,
287             Val::F32(v) => masm.store(RegImm::f32(v.bits()), addr, size)?,
288             Val::F64(v) => masm.store(RegImm::f64(v.bits()), addr, size)?,
289             Val::V128(v) => masm.store(RegImm::v128(v), addr, size)?,
290             Val::Local(local) => {
291                 let slot = self.frame.get_wasm_local(local.index);
292                 let local_addr = masm.local_address(&slot)?;
293                 masm.with_scratch::<IntScratch, _>(|masm, scratch| {
294                     masm.load(local_addr, scratch.writable(), size)?;
295                     masm.store(scratch.inner().into(), addr, size)
296                 })?;
297             }
298             Val::Memory(_) => {
299                 masm.with_scratch_for(ty, |masm, scratch| {
300                     masm.pop(scratch.writable(), size)?;
301                     masm.store(scratch.inner().into(), addr, size)
302                 })?;
303             }
304         }
305 
306         Ok(())
307     }
308 
309     /// Move a stack value to the given register.
move_val_to_reg<M: MacroAssembler>( &self, src: &Val, dst: Reg, masm: &mut M, ) -> Result<()>310     pub fn move_val_to_reg<M: MacroAssembler>(
311         &self,
312         src: &Val,
313         dst: Reg,
314         masm: &mut M,
315     ) -> Result<()> {
316         let size: OperandSize = src.ty().try_into()?;
317         match src {
318             Val::Reg(tr) => masm.mov(writable!(dst), RegImm::reg(tr.reg), size),
319             Val::I32(imm) => masm.mov(writable!(dst), RegImm::i32(*imm), size),
320             Val::I64(imm) => masm.mov(writable!(dst), RegImm::i64(*imm), size),
321             Val::F32(imm) => masm.mov(writable!(dst), RegImm::f32(imm.bits()), size),
322             Val::F64(imm) => masm.mov(writable!(dst), RegImm::f64(imm.bits()), size),
323             Val::V128(imm) => masm.mov(writable!(dst), RegImm::v128(*imm), size),
324             Val::Local(local) => {
325                 let slot = self.frame.get_wasm_local(local.index);
326                 let addr = masm.local_address(&slot)?;
327                 masm.load(addr, writable!(dst), size)
328             }
329             Val::Memory(mem) => {
330                 let addr = masm.address_from_sp(mem.slot.offset)?;
331                 masm.load(addr, writable!(dst), size)
332             }
333         }
334     }
335 
336     /// Prepares arguments for emitting a unary operation.
337     ///
338     /// The `emit` function returns the `TypedReg` to put on the value stack.
unop<F, M>(&mut self, masm: &mut M, emit: F) -> Result<()> where F: FnOnce(&mut M, Reg) -> Result<TypedReg>, M: MacroAssembler,339     pub fn unop<F, M>(&mut self, masm: &mut M, emit: F) -> Result<()>
340     where
341         F: FnOnce(&mut M, Reg) -> Result<TypedReg>,
342         M: MacroAssembler,
343     {
344         let typed_reg = self.pop_to_reg(masm, None)?;
345         let dst = emit(masm, typed_reg.reg)?;
346         self.stack.push(dst.into());
347 
348         Ok(())
349     }
350 
351     /// Prepares arguments for emitting a binary operation.
352     ///
353     /// The `emit` function returns the `TypedReg` to put on the value stack.
binop<F, M>(&mut self, masm: &mut M, size: OperandSize, emit: F) -> Result<()> where F: FnOnce(&mut M, Reg, Reg, OperandSize) -> Result<TypedReg>, M: MacroAssembler,354     pub fn binop<F, M>(&mut self, masm: &mut M, size: OperandSize, emit: F) -> Result<()>
355     where
356         F: FnOnce(&mut M, Reg, Reg, OperandSize) -> Result<TypedReg>,
357         M: MacroAssembler,
358     {
359         let src = self.pop_to_reg(masm, None)?;
360         let dst = self.pop_to_reg(masm, None)?;
361         let dst = emit(masm, dst.reg, src.reg, size)?;
362         self.free_reg(src);
363         self.stack.push(dst.into());
364 
365         Ok(())
366     }
367 
368     /// Prepares arguments for emitting an f32 or f64 comparison operation.
float_cmp_op<F, M>(&mut self, masm: &mut M, size: OperandSize, emit: F) -> Result<()> where F: FnOnce(&mut M, Reg, Reg, Reg, OperandSize) -> Result<()>, M: MacroAssembler,369     pub fn float_cmp_op<F, M>(&mut self, masm: &mut M, size: OperandSize, emit: F) -> Result<()>
370     where
371         F: FnOnce(&mut M, Reg, Reg, Reg, OperandSize) -> Result<()>,
372         M: MacroAssembler,
373     {
374         let src2 = self.pop_to_reg(masm, None)?;
375         let src1 = self.pop_to_reg(masm, None)?;
376         let dst = self.any_gpr(masm)?;
377         emit(masm, dst, src1.reg, src2.reg, size)?;
378         self.free_reg(src1);
379         self.free_reg(src2);
380 
381         let dst = match size {
382             // Float comparison operators are defined as
383             // [f64 f64] -> i32
384             // https://webassembly.github.io/spec/core/appendix/index-instructions.html
385             OperandSize::S32 | OperandSize::S64 => TypedReg::i32(dst),
386             OperandSize::S8 | OperandSize::S16 | OperandSize::S128 => {
387                 bail!(CodeGenError::unexpected_operand_size())
388             }
389         };
390         self.stack.push(dst.into());
391 
392         Ok(())
393     }
394 
395     /// Prepares arguments for emitting an i32 binary operation.
396     ///
397     /// The `emit` function returns the `TypedReg` to put on the value stack.
i32_binop<F, M>(&mut self, masm: &mut M, mut emit: F) -> Result<()> where F: FnMut(&mut M, Reg, RegImm, OperandSize) -> Result<TypedReg>, M: MacroAssembler,398     pub fn i32_binop<F, M>(&mut self, masm: &mut M, mut emit: F) -> Result<()>
399     where
400         F: FnMut(&mut M, Reg, RegImm, OperandSize) -> Result<TypedReg>,
401         M: MacroAssembler,
402     {
403         match self.pop_i32_const() {
404             Some(val) => {
405                 let typed_reg = self.pop_to_reg(masm, None)?;
406                 let dst = emit(masm, typed_reg.reg, RegImm::i32(val), OperandSize::S32)?;
407                 self.stack.push(dst.into());
408             }
409             None => self.binop(masm, OperandSize::S32, |masm, dst, src, size| {
410                 emit(masm, dst, src.into(), size)
411             })?,
412         }
413         Ok(())
414     }
415 
416     /// Prepares arguments for emitting an i64 binary operation.
417     ///
418     /// The `emit` function returns the `TypedReg` to put on the value stack.
i64_binop<F, M>(&mut self, masm: &mut M, emit: F) -> Result<()> where F: FnOnce(&mut M, Reg, RegImm, OperandSize) -> Result<TypedReg>, M: MacroAssembler,419     pub fn i64_binop<F, M>(&mut self, masm: &mut M, emit: F) -> Result<()>
420     where
421         F: FnOnce(&mut M, Reg, RegImm, OperandSize) -> Result<TypedReg>,
422         M: MacroAssembler,
423     {
424         match self.pop_i64_const() {
425             Some(val) => {
426                 let typed_reg = self.pop_to_reg(masm, None)?;
427                 let dst = emit(masm, typed_reg.reg, RegImm::i64(val), OperandSize::S64)?;
428                 self.stack.push(dst.into());
429             }
430             None => self.binop(masm, OperandSize::S64, |masm, dst, src, size| {
431                 emit(masm, dst, src.into(), size)
432             })?,
433         }
434         Ok(())
435     }
436 
437     /// Returns the i32 const on top of the stack or None if there isn't one.
pop_i32_const(&mut self) -> Option<i32>438     pub fn pop_i32_const(&mut self) -> Option<i32> {
439         let top = self.stack.peek().expect("value at stack top");
440 
441         if top.is_i32_const() {
442             let val = self
443                 .stack
444                 .pop_i32_const()
445                 .expect("i32 const value at stack top");
446             Some(val)
447         } else {
448             None
449         }
450     }
451 
452     /// Returns the i64 const on top of the stack or None if there isn't one.
pop_i64_const(&mut self) -> Option<i64>453     pub fn pop_i64_const(&mut self) -> Option<i64> {
454         let top = self.stack.peek().expect("value at stack top");
455 
456         if top.is_i64_const() {
457             let val = self
458                 .stack
459                 .pop_i64_const()
460                 .expect("i64 const value at stack top");
461             Some(val)
462         } else {
463             None
464         }
465     }
466 
467     /// Returns the f32 const on top of the stack or None if there isn't one.
pop_f32_const(&mut self) -> Option<Ieee32>468     pub fn pop_f32_const(&mut self) -> Option<Ieee32> {
469         let top = self.stack.peek().expect("value at stack top");
470 
471         if top.is_f32_const() {
472             let val = self
473                 .stack
474                 .pop_f32_const()
475                 .expect("f32 const value at stack top");
476             Some(val)
477         } else {
478             None
479         }
480     }
481 
482     /// Returns the f64 const on top of the stack or None if there isn't one.
pop_f64_const(&mut self) -> Option<Ieee64>483     pub fn pop_f64_const(&mut self) -> Option<Ieee64> {
484         let top = self.stack.peek().expect("value at stack top");
485 
486         if top.is_f64_const() {
487             let val = self
488                 .stack
489                 .pop_f64_const()
490                 .expect("f64 const value at stack top");
491             Some(val)
492         } else {
493             None
494         }
495     }
496 
497     /// Prepares arguments for emitting a convert operation.
convert_op<F, M>(&mut self, masm: &mut M, dst_ty: WasmValType, emit: F) -> Result<()> where F: FnOnce(&mut M, Reg, Reg, OperandSize) -> Result<()>, M: MacroAssembler,498     pub fn convert_op<F, M>(&mut self, masm: &mut M, dst_ty: WasmValType, emit: F) -> Result<()>
499     where
500         F: FnOnce(&mut M, Reg, Reg, OperandSize) -> Result<()>,
501         M: MacroAssembler,
502     {
503         let src = self.pop_to_reg(masm, None)?;
504         let dst = self.reg_for_type(dst_ty, masm)?;
505         let dst_size = match dst_ty {
506             WasmValType::I32 => OperandSize::S32,
507             WasmValType::I64 => OperandSize::S64,
508             WasmValType::F32 => OperandSize::S32,
509             WasmValType::F64 => OperandSize::S64,
510             WasmValType::V128 => bail!(CodeGenError::unsupported_wasm_type()),
511             WasmValType::Ref(_) => bail!(CodeGenError::unsupported_wasm_type()),
512         };
513 
514         emit(masm, dst, src.into(), dst_size)?;
515 
516         self.free_reg(src);
517         self.stack.push(TypedReg::new(dst_ty, dst).into());
518         Ok(())
519     }
520 
521     /// Prepares arguments for emitting a convert operation with a temporary
522     /// register.
convert_op_with_tmp_reg<F, M>( &mut self, masm: &mut M, dst_ty: WasmValType, tmp_reg_class: RegClass, emit: F, ) -> Result<()> where F: FnOnce(&mut M, Reg, Reg, Reg, OperandSize) -> Result<()>, M: MacroAssembler,523     pub fn convert_op_with_tmp_reg<F, M>(
524         &mut self,
525         masm: &mut M,
526         dst_ty: WasmValType,
527         tmp_reg_class: RegClass,
528         emit: F,
529     ) -> Result<()>
530     where
531         F: FnOnce(&mut M, Reg, Reg, Reg, OperandSize) -> Result<()>,
532         M: MacroAssembler,
533     {
534         let tmp_gpr = self.reg_for_class(tmp_reg_class, masm)?;
535         self.convert_op(masm, dst_ty, |masm, dst, src, dst_size| {
536             emit(masm, dst, src, tmp_gpr, dst_size)
537         })?;
538         self.free_reg(tmp_gpr);
539         Ok(())
540     }
541 
542     /// Prepares arguments for emitting an extract lane operation.
extract_lane_op<F, M>( &mut self, masm: &mut M, kind: ExtractLaneKind, emit: F, ) -> Result<()> where F: FnOnce(&mut M, Reg, WritableReg, ExtractLaneKind) -> Result<()>, M: MacroAssembler,543     pub fn extract_lane_op<F, M>(
544         &mut self,
545         masm: &mut M,
546         kind: ExtractLaneKind,
547         emit: F,
548     ) -> Result<()>
549     where
550         F: FnOnce(&mut M, Reg, WritableReg, ExtractLaneKind) -> Result<()>,
551         M: MacroAssembler,
552     {
553         let src = self.pop_to_reg(masm, None)?;
554         let dst = writable!(match kind {
555             ExtractLaneKind::I8x16S
556             | ExtractLaneKind::I8x16U
557             | ExtractLaneKind::I16x8S
558             | ExtractLaneKind::I16x8U
559             | ExtractLaneKind::I32x4
560             | ExtractLaneKind::I64x2 => self.any_gpr(masm)?,
561             ExtractLaneKind::F32x4 | ExtractLaneKind::F64x2 => src.reg,
562         });
563 
564         emit(masm, src.reg, dst, kind)?;
565 
566         match kind {
567             ExtractLaneKind::I8x16S
568             | ExtractLaneKind::I8x16U
569             | ExtractLaneKind::I16x8S
570             | ExtractLaneKind::I16x8U
571             | ExtractLaneKind::I32x4
572             | ExtractLaneKind::I64x2 => self.free_reg(src),
573             _ => (),
574         }
575 
576         let dst = dst.to_reg();
577         let dst = match kind {
578             ExtractLaneKind::I8x16S
579             | ExtractLaneKind::I8x16U
580             | ExtractLaneKind::I16x8S
581             | ExtractLaneKind::I16x8U
582             | ExtractLaneKind::I32x4 => TypedReg::i32(dst),
583             ExtractLaneKind::I64x2 => TypedReg::i64(dst),
584             ExtractLaneKind::F32x4 => TypedReg::f32(dst),
585             ExtractLaneKind::F64x2 => TypedReg::f64(dst),
586         };
587 
588         self.stack.push(Val::Reg(dst));
589         Ok(())
590     }
591 
592     /// Prepares arguments for emitting a replace lane operation.
replace_lane_op<F, M>( &mut self, masm: &mut M, kind: ReplaceLaneKind, emit: F, ) -> Result<()> where F: FnOnce(&mut M, RegImm, WritableReg, ReplaceLaneKind) -> Result<()>, M: MacroAssembler,593     pub fn replace_lane_op<F, M>(
594         &mut self,
595         masm: &mut M,
596         kind: ReplaceLaneKind,
597         emit: F,
598     ) -> Result<()>
599     where
600         F: FnOnce(&mut M, RegImm, WritableReg, ReplaceLaneKind) -> Result<()>,
601         M: MacroAssembler,
602     {
603         let src = match kind {
604             ReplaceLaneKind::I8x16 | ReplaceLaneKind::I16x8 | ReplaceLaneKind::I32x4 => {
605                 self.pop_i32_const().map(RegImm::i32)
606             }
607             ReplaceLaneKind::I64x2 => self.pop_i64_const().map(RegImm::i64),
608             ReplaceLaneKind::F32x4 => self.pop_f32_const().map(|v| RegImm::f32(v.bits())),
609             ReplaceLaneKind::F64x2 => self.pop_f64_const().map(|v| RegImm::f64(v.bits())),
610         }
611         .map_or_else(
612             || Ok(RegImm::reg(self.pop_to_reg(masm, None)?.into())),
613             Ok::<_, crate::Error>,
614         )?;
615 
616         let dst = self.pop_to_reg(masm, None)?;
617 
618         emit(masm, src, writable!(dst.into()), kind)?;
619 
620         if let RegImm::Reg(reg) = src {
621             self.free_reg(reg);
622         }
623         self.stack.push(dst.into());
624 
625         Ok(())
626     }
627 
628     /// Drops the last `n` elements of the stack, calling the provided
629     /// function for each `n` stack value.
630     /// The values are dropped in top-to-bottom order.
drop_last<F>(&mut self, last: usize, mut f: F) -> Result<()> where F: FnMut(&mut RegAlloc, &Val) -> Result<()>,631     pub fn drop_last<F>(&mut self, last: usize, mut f: F) -> Result<()>
632     where
633         F: FnMut(&mut RegAlloc, &Val) -> Result<()>,
634     {
635         if last > 0 {
636             let len = self.stack.len();
637             ensure!(last <= len, CodeGenError::unexpected_value_stack_index(),);
638             let truncate = self.stack.len() - last;
639             let stack_mut = self.stack.inner_mut();
640 
641             // Invoke the callback in top-to-bottom order.
642             for v in stack_mut[truncate..].into_iter().rev() {
643                 f(&mut self.regalloc, v)?
644             }
645             stack_mut.truncate(truncate);
646         }
647 
648         Ok(())
649     }
650 
651     /// Convenience wrapper around [`Self::spill_callback`].
652     ///
653     /// This function exists for cases in which triggering an unconditional
654     /// spill is needed, like before entering control flow.
spill<M: MacroAssembler>(&mut self, masm: &mut M) -> Result<()>655     pub fn spill<M: MacroAssembler>(&mut self, masm: &mut M) -> Result<()> {
656         Self::spill_impl(&mut self.stack, &mut self.regalloc, &self.frame, masm)
657     }
658 
659     /// Prepares the compiler to branch to the given destination
660     /// frame.
661     ///  This process involves:
662     /// * Balancing the machine stack pointer and value stack by
663     ///   popping it to match the destination branch.
664     /// * Updating the reachability state.
665     /// * Marking the destination frame as a destination target.
br<M, F, B>( &mut self, dest: &mut ControlStackFrame, masm: &mut M, mut maybe_pop_results: F, ) -> Result<()> where M: MacroAssembler, F: FnMut(&mut M, &mut Self, &mut ControlStackFrame) -> Result<()>, B: BranchState,666     pub fn br<M, F, B>(
667         &mut self,
668         dest: &mut ControlStackFrame,
669         masm: &mut M,
670         mut maybe_pop_results: F,
671     ) -> Result<()>
672     where
673         M: MacroAssembler,
674         F: FnMut(&mut M, &mut Self, &mut ControlStackFrame) -> Result<()>,
675         B: BranchState,
676     {
677         let state = dest.stack_state();
678         let target_offset = state.target_offset;
679         let base_offset = state.base_offset;
680         let results_size = dest.results::<M>()?.size();
681 
682         maybe_pop_results(masm, self, dest)?;
683         // After calling `maybe_pop_results`, the stack pointer plus
684         // any result space needed, must be greater or equal to the
685         // destination frame base stack pointer offset.
686         //
687         // We check
688         //   current_sp + results >= base_offset
689         // as opposed to
690         //   current_sp >= base_offset
691         //
692         // To:
693         //  - Verify that `maybe_pop_results` popped exactly the right
694         //    amount relative to the base offset.
695         //  - Accommodate for multi-branch cases (i.e., `br_table`) in which
696         //    result handling happens only once and _could_ happen outside of
697         //    `maybe_pop_results` callback.
698         //
699         //
700         // Ensuring that the current stack pointer offset plus any
701         // result space is equal to or greater than the target branch
702         // base offset is the the most deterministic check at branch
703         // emission time since we can be certain that the base offset
704         // is the value recorded when a new control frame was pushed,
705         // upon which the expected target offset is calculated.
706         ensure!(
707             (masm.sp_offset()?.as_u32() + results_size) >= base_offset.as_u32(),
708             CodeGenError::invalid_sp_offset()
709         );
710 
711         // At jump sites, the machine stack might be left unbalanced,
712         // due to register spills.
713         // The following snippet, pops the stack pointer to ensure
714         // that it is correctly placed according to the expectations
715         // of the destination branch.
716         //
717         // Note that in most branch cases (`return`, ` br`) the stack
718         // pointer will be already balanced, by virtue of calling
719         // [`ControlStackFrame::pop_abi_results`] through the
720         // callback.
721         //
722         // More generally speaking the current stack pointer will be
723         // less than the destination frame stack pointer offset in
724         // cases in which the top value in the value stack is a memory
725         // entry which needs to be popped into the return location
726         // according to the ABI (a register for single value returns
727         // and a memory slot for 1+ returns).
728         //
729         // Stack balancing is mostly required for WebAssembly
730         // instructions that deal with multiple destination branches
731         // (e.g., `br_table`) or fall-through scenarios (e.g.,
732         // `br_if`). In order to ensure that multi-value returns are
733         // handled correctly we ensure that correct placing of stack
734         // results by emitting a [`MacroAssembler::memmove`]
735         // instruction, prior to claiming any excess stack space.
736         //
737         // Depending on the branch state, the compiler might enter in an
738         // unreachable state; instead of immediately truncating the value stack
739         // to the expected length of the destination branch, we let the
740         // reachability analysis code decide what should happen with the length
741         // of the value stack once reachability is actually restored. At that
742         // point, the right stack pointer offset will also be restored, which
743         // should match the contents of the value stack.
744         if dest.unbalanced::<M>(masm)? {
745             masm.memmove(
746                 masm.sp_offset()?,
747                 target_offset,
748                 results_size,
749                 MemMoveDirection::LowToHigh,
750             )?;
751         }
752         masm.ensure_sp_for_jump(target_offset)?;
753         dest.set_as_target();
754         masm.jmp(*dest.label())?;
755         if B::unreachable_state_after_emission() {
756             self.reachable = false;
757         }
758         Ok(())
759     }
760 
761     /// Push the ABI representation of the results stack.
push_abi_results<M, F>( &mut self, results: &ABIResults, masm: &mut M, mut calculate_ret_area: F, ) -> Result<()> where M: MacroAssembler, F: FnMut(&ABIResults, &mut CodeGenContext<Emission>, &mut M) -> Option<RetArea>,762     pub fn push_abi_results<M, F>(
763         &mut self,
764         results: &ABIResults,
765         masm: &mut M,
766         mut calculate_ret_area: F,
767     ) -> Result<()>
768     where
769         M: MacroAssembler,
770         F: FnMut(&ABIResults, &mut CodeGenContext<Emission>, &mut M) -> Option<RetArea>,
771     {
772         let area = results
773             .on_stack()
774             .then(|| calculate_ret_area(&results, self, masm).unwrap());
775 
776         for operand in results.operands().iter() {
777             match operand {
778                 ABIOperand::Reg { reg, ty, .. } => {
779                     ensure!(
780                         self.regalloc.reg_available(*reg),
781                         CodeGenError::expected_register_to_be_available(),
782                     );
783 
784                     let typed_reg = TypedReg::new(*ty, self.reg(*reg, masm)?);
785                     self.stack.push(typed_reg.into());
786                 }
787                 ABIOperand::Stack { ty, offset, size } => match area.unwrap() {
788                     RetArea::SP(sp_offset) => {
789                         let slot =
790                             StackSlot::new(SPOffset::from_u32(sp_offset.as_u32() - offset), *size);
791                         self.stack.push(Val::mem(*ty, slot));
792                     }
793                     // This function is only expected to be called when dealing
794                     // with control flow and when calling functions; as a
795                     // callee, only [Self::pop_abi_results] is needed when
796                     // finalizing the function compilation.
797                     _ => bail!(CodeGenError::unexpected_function_call()),
798                 },
799             }
800         }
801 
802         Ok(())
803     }
804 
805     /// Truncates the value stack to the specified target.
806     /// This function is intended to only be used when restoring the code
807     /// generation's reachability state, when handling an unreachable end or
808     /// else.
truncate_stack_to(&mut self, target: usize) -> Result<()>809     pub fn truncate_stack_to(&mut self, target: usize) -> Result<()> {
810         if self.stack.len() > target {
811             self.drop_last(self.stack.len() - target, |regalloc, val| match val {
812                 Val::Reg(tr) => Ok(regalloc.free(tr.reg)),
813                 _ => Ok(()),
814             })
815         } else {
816             Ok(())
817         }
818     }
819 
820     /// Load the [VMContext] pointer into the designated pinned register.
load_vmctx<M>(&mut self, masm: &mut M) -> Result<()> where M: MacroAssembler,821     pub fn load_vmctx<M>(&mut self, masm: &mut M) -> Result<()>
822     where
823         M: MacroAssembler,
824     {
825         let addr = masm.local_address(&self.frame.vmctx_slot())?;
826         masm.load_ptr(addr, writable!(vmctx!(M)))
827     }
828 
829     /// Spill locals and registers to memory.
830     // TODO: optimize the spill range;
831     // At any point in the program, the stack might already contain memory
832     // entries; we could effectively ignore that range; only focusing on the
833     // range that contains spillable values.
spill_impl<M: MacroAssembler>( stack: &mut Stack, regalloc: &mut RegAlloc, frame: &Frame<Emission>, masm: &mut M, ) -> Result<()>834     fn spill_impl<M: MacroAssembler>(
835         stack: &mut Stack,
836         regalloc: &mut RegAlloc,
837         frame: &Frame<Emission>,
838         masm: &mut M,
839     ) -> Result<()> {
840         for v in stack.inner_mut() {
841             match v {
842                 Val::Reg(r) => {
843                     let slot = masm.push(r.reg, r.ty.try_into()?)?;
844                     regalloc.free(r.reg);
845                     *v = Val::mem(r.ty, slot);
846                 }
847                 Val::Local(local) => {
848                     let slot = frame.get_wasm_local(local.index);
849                     let addr = masm.local_address(&slot)?;
850                     masm.with_scratch_for(slot.ty, |masm, scratch| {
851                         masm.load(addr, scratch.writable(), slot.ty.try_into()?)?;
852                         let stack_slot = masm.push(scratch.inner(), slot.ty.try_into()?)?;
853                         *v = Val::mem(slot.ty, stack_slot);
854                         wasmtime_environ::error::Ok(())
855                     })?;
856                 }
857                 _ => {}
858             }
859         }
860 
861         Ok(())
862     }
863 
864     /// Prepares for emitting a binary operation where four 64-bit operands are
865     /// used to produce two 64-bit operands, e.g. a 128-bit binop.
binop128<F, M>(&mut self, masm: &mut M, emit: F) -> Result<()> where F: FnOnce(&mut M, Reg, Reg, Reg, Reg) -> Result<(TypedReg, TypedReg)>, M: MacroAssembler,866     pub fn binop128<F, M>(&mut self, masm: &mut M, emit: F) -> Result<()>
867     where
868         F: FnOnce(&mut M, Reg, Reg, Reg, Reg) -> Result<(TypedReg, TypedReg)>,
869         M: MacroAssembler,
870     {
871         let rhs_hi = self.pop_to_reg(masm, None)?;
872         let rhs_lo = self.pop_to_reg(masm, None)?;
873         let lhs_hi = self.pop_to_reg(masm, None)?;
874         let lhs_lo = self.pop_to_reg(masm, None)?;
875         let (lo, hi) = emit(masm, lhs_lo.reg, lhs_hi.reg, rhs_lo.reg, rhs_hi.reg)?;
876         self.free_reg(rhs_hi);
877         self.free_reg(rhs_lo);
878         self.stack.push(lo.into());
879         self.stack.push(hi.into());
880 
881         Ok(())
882     }
883 
884     /// Prepares to emit a vector `all_true` operation.
v128_all_true_op<F, M>(&mut self, masm: &mut M, emit: F) -> Result<()> where F: FnOnce(&mut M, Reg, Reg) -> Result<()>, M: MacroAssembler,885     pub fn v128_all_true_op<F, M>(&mut self, masm: &mut M, emit: F) -> Result<()>
886     where
887         F: FnOnce(&mut M, Reg, Reg) -> Result<()>,
888         M: MacroAssembler,
889     {
890         let src = self.pop_to_reg(masm, None)?;
891         let dst = self.any_gpr(masm)?;
892         emit(masm, src.reg, dst)?;
893         self.free_reg(src);
894         self.stack.push(TypedReg::i32(dst).into());
895 
896         Ok(())
897     }
898 
899     /// Prepares to emit a vector `bitmask` operation.
v128_bitmask_op<F, M>(&mut self, masm: &mut M, emit: F) -> Result<()> where F: FnOnce(&mut M, Reg, Reg) -> Result<()>, M: MacroAssembler,900     pub fn v128_bitmask_op<F, M>(&mut self, masm: &mut M, emit: F) -> Result<()>
901     where
902         F: FnOnce(&mut M, Reg, Reg) -> Result<()>,
903         M: MacroAssembler,
904     {
905         let src = self.pop_to_reg(masm, None)?;
906         let dst = self.any_gpr(masm)?;
907         emit(masm, src.reg, dst)?;
908         self.free_reg(src);
909         self.stack.push(TypedReg::i32(dst).into());
910 
911         Ok(())
912     }
913 
914     /// Pops a register from the stack and then immediately frees it. Used to
915     /// discard values from the last operation, for example.
pop_and_free<M: MacroAssembler>(&mut self, masm: &mut M) -> Result<()>916     pub fn pop_and_free<M: MacroAssembler>(&mut self, masm: &mut M) -> Result<()> {
917         let reg = self.pop_to_reg(masm, None)?;
918         self.free_reg(reg.reg);
919         Ok(())
920     }
921 }
922