xref: /wasmtime-44.0.1/winch/codegen/src/masm.rs (revision 392df4a8)
1 use crate::abi::{self, align_to, scratch, LocalSlot};
2 use crate::codegen::{CodeGenContext, Emission, FuncEnv};
3 use crate::isa::{
4     reg::{writable, Reg, WritableReg},
5     CallingConvention,
6 };
7 use cranelift_codegen::{
8     binemit::CodeOffset,
9     ir::{Endianness, LibCall, MemFlags, RelSourceLoc, SourceLoc, UserExternalNameRef},
10     Final, MachBufferFinalized, MachLabel,
11 };
12 use std::{fmt::Debug, ops::Range};
13 use wasmtime_environ::PtrSize;
14 
15 pub(crate) use cranelift_codegen::ir::TrapCode;
16 
17 #[derive(Eq, PartialEq)]
18 pub(crate) enum DivKind {
19     /// Signed division.
20     Signed,
21     /// Unsigned division.
22     Unsigned,
23 }
24 
25 /// Remainder kind.
26 #[derive(Copy, Clone)]
27 pub(crate) enum RemKind {
28     /// Signed remainder.
29     Signed,
30     /// Unsigned remainder.
31     Unsigned,
32 }
33 
34 impl RemKind {
35     pub fn is_signed(&self) -> bool {
36         matches!(self, Self::Signed)
37     }
38 }
39 
40 #[derive(Eq, PartialEq)]
41 pub(crate) enum MulWideKind {
42     Signed,
43     Unsigned,
44 }
45 
46 /// The direction to perform the memory move.
47 #[derive(Debug, Clone, Eq, PartialEq)]
48 pub(crate) enum MemMoveDirection {
49     /// From high memory addresses to low memory addresses.
50     /// Invariant: the source location is closer to the FP than the destination
51     /// location, which will be closer to the SP.
52     HighToLow,
53     /// From low memory addresses to high memory addresses.
54     /// Invariant: the source location is closer to the SP than the destination
55     /// location, which will be closer to the FP.
56     LowToHigh,
57 }
58 
59 /// Classifies how to treat float-to-int conversions.
60 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
61 pub(crate) enum TruncKind {
62     /// Saturating conversion. If the source value is greater than the maximum
63     /// value of the destination type, the result is clamped to the
64     /// destination maximum value.
65     Checked,
66     /// An exception is raised if the source value is greater than the maximum
67     /// value of the destination type.
68     Unchecked,
69 }
70 
71 impl TruncKind {
72     /// Returns true if the truncation kind is checked.
73     pub(crate) fn is_checked(&self) -> bool {
74         *self == TruncKind::Checked
75     }
76 }
77 
78 /// Representation of the stack pointer offset.
79 #[derive(Copy, Clone, Eq, PartialEq, Debug, PartialOrd, Ord, Default)]
80 pub struct SPOffset(u32);
81 
82 impl SPOffset {
83     pub fn from_u32(offs: u32) -> Self {
84         Self(offs)
85     }
86 
87     pub fn as_u32(&self) -> u32 {
88         self.0
89     }
90 }
91 
92 /// A stack slot.
93 #[derive(Debug, Clone, Copy, Eq, PartialEq)]
94 pub struct StackSlot {
95     /// The location of the slot, relative to the stack pointer.
96     pub offset: SPOffset,
97     /// The size of the slot, in bytes.
98     pub size: u32,
99 }
100 
101 impl StackSlot {
102     pub fn new(offs: SPOffset, size: u32) -> Self {
103         Self { offset: offs, size }
104     }
105 }
106 
107 /// Kinds of integer binary comparison in WebAssembly. The [`MacroAssembler`]
108 /// implementation for each ISA is responsible for emitting the correct
109 /// sequence of instructions when lowering to machine code.
110 #[derive(Debug, Clone, Copy, Eq, PartialEq)]
111 pub(crate) enum IntCmpKind {
112     /// Equal.
113     Eq,
114     /// Not equal.
115     Ne,
116     /// Signed less than.
117     LtS,
118     /// Unsigned less than.
119     LtU,
120     /// Signed greater than.
121     GtS,
122     /// Unsigned greater than.
123     GtU,
124     /// Signed less than or equal.
125     LeS,
126     /// Unsigned less than or equal.
127     LeU,
128     /// Signed greater than or equal.
129     GeS,
130     /// Unsigned greater than or equal.
131     GeU,
132 }
133 
134 /// Kinds of float binary comparison in WebAssembly. The [`MacroAssembler`]
135 /// implementation for each ISA is responsible for emitting the correct
136 /// sequence of instructions when lowering code.
137 #[derive(Debug)]
138 pub(crate) enum FloatCmpKind {
139     /// Equal.
140     Eq,
141     /// Not equal.
142     Ne,
143     /// Less than.
144     Lt,
145     /// Greater than.
146     Gt,
147     /// Less than or equal.
148     Le,
149     /// Greater than or equal.
150     Ge,
151 }
152 
153 /// Kinds of shifts in WebAssembly.The [`masm`] implementation for each ISA is
154 /// responsible for emitting the correct sequence of instructions when
155 /// lowering to machine code.
156 #[derive(Debug, Clone, Copy, Eq, PartialEq)]
157 pub(crate) enum ShiftKind {
158     /// Left shift.
159     Shl,
160     /// Signed right shift.
161     ShrS,
162     /// Unsigned right shift.
163     ShrU,
164     /// Left rotate.
165     Rotl,
166     /// Right rotate.
167     Rotr,
168 }
169 
170 /// Kinds of extends in WebAssembly. Each MacroAssembler implementation
171 /// is responsible for emitting the correct sequence of instructions when
172 /// lowering to machine code.
173 #[derive(Copy, Clone)]
174 pub(crate) enum ExtendKind {
175     /// Sign extends i32 to i64.
176     I64ExtendI32S,
177     /// Zero extends i32 to i64.
178     I64ExtendI32U,
179     // Sign extends the 8 least significant bits to 32 bits.
180     I32Extend8S,
181     // Sign extends the 16 least significant bits to 32 bits.
182     I32Extend16S,
183     /// Sign extends the 8 least significant bits to 64 bits.
184     I64Extend8S,
185     /// Sign extends the 16 least significant bits to 64 bits.
186     I64Extend16S,
187     /// Sign extends the 32 least significant bits to 64 bits.
188     I64Extend32S,
189 }
190 
191 impl ExtendKind {
192     pub fn signed(&self) -> bool {
193         if let Self::I64ExtendI32U = self {
194             false
195         } else {
196             true
197         }
198     }
199 
200     pub fn from_bits(&self) -> u8 {
201         match self {
202             Self::I64ExtendI32S | Self::I64ExtendI32U | Self::I64Extend32S => 32,
203             Self::I32Extend8S | Self::I64Extend8S => 8,
204             Self::I32Extend16S | Self::I64Extend16S => 16,
205         }
206     }
207 
208     pub fn to_bits(&self) -> u8 {
209         match self {
210             Self::I64ExtendI32S
211             | Self::I64ExtendI32U
212             | Self::I64Extend8S
213             | Self::I64Extend16S
214             | Self::I64Extend32S => 64,
215             Self::I32Extend8S | Self::I32Extend16S => 32,
216         }
217     }
218 }
219 
220 /// Operand size, in bits.
221 #[derive(Copy, Debug, Clone, Eq, PartialEq)]
222 pub(crate) enum OperandSize {
223     /// 8 bits.
224     S8,
225     /// 16 bits.
226     S16,
227     /// 32 bits.
228     S32,
229     /// 64 bits.
230     S64,
231     /// 128 bits.
232     S128,
233 }
234 
235 impl OperandSize {
236     /// The number of bits in the operand.
237     pub fn num_bits(&self) -> u8 {
238         match self {
239             OperandSize::S8 => 8,
240             OperandSize::S16 => 16,
241             OperandSize::S32 => 32,
242             OperandSize::S64 => 64,
243             OperandSize::S128 => 128,
244         }
245     }
246 
247     /// The number of bytes in the operand.
248     pub fn bytes(&self) -> u32 {
249         match self {
250             Self::S8 => 1,
251             Self::S16 => 2,
252             Self::S32 => 4,
253             Self::S64 => 8,
254             Self::S128 => 16,
255         }
256     }
257 
258     /// The binary logarithm of the number of bits in the operand.
259     pub fn log2(&self) -> u8 {
260         match self {
261             OperandSize::S8 => 3,
262             OperandSize::S16 => 4,
263             OperandSize::S32 => 5,
264             OperandSize::S64 => 6,
265             OperandSize::S128 => 7,
266         }
267     }
268 
269     /// Create an [`OperandSize`]  from the given number of bytes.
270     pub fn from_bytes(bytes: u8) -> Self {
271         use OperandSize::*;
272         match bytes {
273             4 => S32,
274             8 => S64,
275             16 => S128,
276             _ => panic!("Invalid bytes {bytes} for OperandSize"),
277         }
278     }
279 }
280 
281 /// An abstraction over a register or immediate.
282 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
283 pub(crate) enum RegImm {
284     /// A register.
285     Reg(Reg),
286     /// A tagged immediate argument.
287     Imm(Imm),
288 }
289 
290 /// An tagged representation of an immediate.
291 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
292 pub(crate) enum Imm {
293     /// I32 immediate.
294     I32(u32),
295     /// I64 immediate.
296     I64(u64),
297     /// F32 immediate.
298     F32(u32),
299     /// F64 immediate.
300     F64(u64),
301     /// V128 immediate.
302     V128(i128),
303 }
304 
305 impl Imm {
306     /// Create a new I64 immediate.
307     pub fn i64(val: i64) -> Self {
308         Self::I64(val as u64)
309     }
310 
311     /// Create a new I32 immediate.
312     pub fn i32(val: i32) -> Self {
313         Self::I32(val as u32)
314     }
315 
316     /// Create a new F32 immediate.
317     pub fn f32(bits: u32) -> Self {
318         Self::F32(bits)
319     }
320 
321     /// Create a new F64 immediate.
322     pub fn f64(bits: u64) -> Self {
323         Self::F64(bits)
324     }
325 
326     /// Create a new V128 immediate.
327     pub fn v128(bits: i128) -> Self {
328         Self::V128(bits)
329     }
330 
331     /// Convert the immediate to i32, if possible.
332     pub fn to_i32(&self) -> Option<i32> {
333         match self {
334             Self::I32(v) => Some(*v as i32),
335             Self::I64(v) => i32::try_from(*v as i64).ok(),
336             _ => None,
337         }
338     }
339 
340     /// Returns true if the [`Imm`] is float.
341     pub fn is_float(&self) -> bool {
342         match self {
343             Self::F32(_) | Self::F64(_) => true,
344             _ => false,
345         }
346     }
347 
348     /// Get the operand size of the immediate.
349     pub fn size(&self) -> OperandSize {
350         match self {
351             Self::I32(_) | Self::F32(_) => OperandSize::S32,
352             Self::I64(_) | Self::F64(_) => OperandSize::S64,
353             Self::V128(_) => OperandSize::S128,
354         }
355     }
356 }
357 
358 /// The location of the [VMcontext] used for function calls.
359 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
360 pub(crate) enum VMContextLoc {
361     /// Dynamic, stored in the given register.
362     Reg(Reg),
363     /// The pinned [VMContext] register.
364     Pinned,
365 }
366 
367 /// The maximum number of context arguments currently used across the compiler.
368 pub(crate) const MAX_CONTEXT_ARGS: usize = 2;
369 
370 /// Out-of-band special purpose arguments used for function call emission.
371 ///
372 /// We cannot rely on the value stack for these values given that inserting
373 /// register or memory values at arbitrary locations of the value stack has the
374 /// potential to break the stack ordering principle, which states that older
375 /// values must always precede newer values, effectively simulating the order of
376 /// values in the machine stack.
377 /// The [ContextArgs] are meant to be resolved at every callsite; in some cases
378 /// it might be possible to construct it early on, but given that it might
379 /// contain allocatable registers, it's preferred to construct it in
380 /// [FnCall::emit].
381 #[derive(Clone, Debug)]
382 pub(crate) enum ContextArgs {
383     /// No context arguments required. This is used for libcalls that don't
384     /// require any special context arguments. For example builtin functions
385     /// that perform float calculations.
386     None,
387     /// A single context argument is required; the current pinned [VMcontext]
388     /// register must be passed as the first argument of the function call.
389     VMContext([VMContextLoc; 1]),
390     /// The callee and caller context arguments are required. In this case, the
391     /// callee context argument is usually stored into an allocatable register
392     /// and the caller is always the current pinned [VMContext] pointer.
393     CalleeAndCallerVMContext([VMContextLoc; MAX_CONTEXT_ARGS]),
394 }
395 
396 impl ContextArgs {
397     /// Construct an empty [ContextArgs].
398     pub fn none() -> Self {
399         Self::None
400     }
401 
402     /// Construct a [ContextArgs] declaring the usage of the pinned [VMContext]
403     /// register as both the caller and callee context arguments.
404     pub fn pinned_callee_and_caller_vmctx() -> Self {
405         Self::CalleeAndCallerVMContext([VMContextLoc::Pinned, VMContextLoc::Pinned])
406     }
407 
408     /// Construct a [ContextArgs] that declares the usage of the pinned
409     /// [VMContext] register as the only context argument.
410     pub fn pinned_vmctx() -> Self {
411         Self::VMContext([VMContextLoc::Pinned])
412     }
413 
414     /// Construct a [ContextArgs] that declares a dynamic callee context and the
415     /// pinned [VMContext] register as the context arguments.
416     pub fn with_callee_and_pinned_caller(callee_vmctx: Reg) -> Self {
417         Self::CalleeAndCallerVMContext([VMContextLoc::Reg(callee_vmctx), VMContextLoc::Pinned])
418     }
419 
420     /// Get the length of the [ContextArgs].
421     pub fn len(&self) -> usize {
422         self.as_slice().len()
423     }
424 
425     /// Get a slice of the context arguments.
426     pub fn as_slice(&self) -> &[VMContextLoc] {
427         match self {
428             Self::None => &[],
429             Self::VMContext(a) => a.as_slice(),
430             Self::CalleeAndCallerVMContext(a) => a.as_slice(),
431         }
432     }
433 }
434 
435 #[derive(Copy, Clone, Debug)]
436 pub(crate) enum CalleeKind {
437     /// A function call to a raw address.
438     Indirect(Reg),
439     /// A function call to a local function.
440     Direct(UserExternalNameRef),
441     /// Call to a well known LibCall.
442     LibCall(LibCall),
443 }
444 
445 impl CalleeKind {
446     /// Creates a callee kind from a register.
447     pub fn indirect(reg: Reg) -> Self {
448         Self::Indirect(reg)
449     }
450 
451     /// Creates a direct callee kind from a function name.
452     pub fn direct(name: UserExternalNameRef) -> Self {
453         Self::Direct(name)
454     }
455 
456     /// Creates a known callee kind from a libcall.
457     pub fn libcall(call: LibCall) -> Self {
458         Self::LibCall(call)
459     }
460 }
461 
462 impl RegImm {
463     /// Register constructor.
464     pub fn reg(r: Reg) -> Self {
465         RegImm::Reg(r)
466     }
467 
468     /// I64 immediate constructor.
469     pub fn i64(val: i64) -> Self {
470         RegImm::Imm(Imm::i64(val))
471     }
472 
473     /// I32 immediate constructor.
474     pub fn i32(val: i32) -> Self {
475         RegImm::Imm(Imm::i32(val))
476     }
477 
478     /// F32 immediate, stored using its bits representation.
479     pub fn f32(bits: u32) -> Self {
480         RegImm::Imm(Imm::f32(bits))
481     }
482 
483     /// F64 immediate, stored using its bits representation.
484     pub fn f64(bits: u64) -> Self {
485         RegImm::Imm(Imm::f64(bits))
486     }
487 
488     /// V128 immediate.
489     pub fn v128(bits: i128) -> Self {
490         RegImm::Imm(Imm::v128(bits))
491     }
492 }
493 
494 impl From<Reg> for RegImm {
495     fn from(r: Reg) -> Self {
496         Self::Reg(r)
497     }
498 }
499 
500 #[derive(Debug)]
501 pub enum RoundingMode {
502     Nearest,
503     Up,
504     Down,
505     Zero,
506 }
507 
508 /// Memory flags for trusted loads/stores.
509 pub const TRUSTED_FLAGS: MemFlags = MemFlags::trusted();
510 
511 /// Flags used for WebAssembly loads / stores.
512 /// Untrusted by default so we don't set `no_trap`.
513 /// We also ensure that the endianness is the right one for WebAssembly.
514 pub const UNTRUSTED_FLAGS: MemFlags = MemFlags::new().with_endianness(Endianness::Little);
515 
516 /// Generic MacroAssembler interface used by the code generation.
517 ///
518 /// The MacroAssembler trait aims to expose an interface, high-level enough,
519 /// so that each ISA can provide its own lowering to machine code. For example,
520 /// for WebAssembly operators that don't have a direct mapping to a machine
521 /// a instruction, the interface defines a signature matching the WebAssembly
522 /// operator, allowing each implementation to lower such operator entirely.
523 /// This approach attributes more responsibility to the MacroAssembler, but frees
524 /// the caller from concerning about assembling the right sequence of
525 /// instructions at the operator callsite.
526 ///
527 /// The interface defaults to a three-argument form for binary operations;
528 /// this allows a natural mapping to instructions for RISC architectures,
529 /// that use three-argument form.
530 /// This approach allows for a more general interface that can be restricted
531 /// where needed, in the case of architectures that use a two-argument form.
532 
533 pub(crate) trait MacroAssembler {
534     /// The addressing mode.
535     type Address: Copy + Debug;
536 
537     /// The pointer representation of the target ISA,
538     /// used to access information from [`VMOffsets`].
539     type Ptr: PtrSize;
540 
541     /// The ABI details of the target.
542     type ABI: abi::ABI;
543 
544     /// Emit the function prologue.
545     fn prologue(&mut self, vmctx: Reg) {
546         self.frame_setup();
547         self.check_stack(vmctx);
548     }
549 
550     /// Generate the frame setup sequence.
551     fn frame_setup(&mut self);
552 
553     /// Generate the frame restore sequence.
554     fn frame_restore(&mut self);
555 
556     /// Emit a stack check.
557     fn check_stack(&mut self, vmctx: Reg);
558 
559     /// Emit the function epilogue.
560     fn epilogue(&mut self) {
561         self.frame_restore();
562     }
563 
564     /// Reserve stack space.
565     fn reserve_stack(&mut self, bytes: u32);
566 
567     /// Free stack space.
568     fn free_stack(&mut self, bytes: u32);
569 
570     /// Reset the stack pointer to the given offset;
571     ///
572     /// Used to reset the stack pointer to a given offset
573     /// when dealing with unreachable code.
574     fn reset_stack_pointer(&mut self, offset: SPOffset);
575 
576     /// Get the address of a local slot.
577     fn local_address(&mut self, local: &LocalSlot) -> Self::Address;
578 
579     /// Constructs an address with an offset that is relative to the
580     /// current position of the stack pointer (e.g. [sp + (sp_offset -
581     /// offset)].
582     fn address_from_sp(&self, offset: SPOffset) -> Self::Address;
583 
584     /// Constructs an address with an offset that is absolute to the
585     /// current position of the stack pointer (e.g. [sp + offset].
586     fn address_at_sp(&self, offset: SPOffset) -> Self::Address;
587 
588     /// Alias for [`Self::address_at_reg`] using the VMContext register as
589     /// a base. The VMContext register is derived from the ABI type that is
590     /// associated to the MacroAssembler.
591     fn address_at_vmctx(&self, offset: u32) -> Self::Address;
592 
593     /// Construct an address that is absolute to the current position
594     /// of the given register.
595     fn address_at_reg(&self, reg: Reg, offset: u32) -> Self::Address;
596 
597     /// Emit a function call to either a local or external function.
598     fn call(
599         &mut self,
600         stack_args_size: u32,
601         f: impl FnMut(&mut Self) -> (CalleeKind, CallingConvention),
602     ) -> u32;
603 
604     /// Get stack pointer offset.
605     fn sp_offset(&self) -> SPOffset;
606 
607     /// Perform a stack store.
608     fn store(&mut self, src: RegImm, dst: Self::Address, size: OperandSize);
609 
610     /// Alias for `MacroAssembler::store` with the operand size corresponding
611     /// to the pointer size of the target.
612     fn store_ptr(&mut self, src: Reg, dst: Self::Address);
613 
614     /// Perform a WebAssembly store.
615     /// A WebAssembly store introduces several additional invariants compared to
616     /// [Self::store], more precisely, it can implicitly trap, in certain
617     /// circumstances, even if explicit bounds checks are elided, in that sense,
618     /// we consider this type of load as untrusted. It can also differ with
619     /// regards to the endianness depending on the target ISA. For this reason,
620     /// [Self::wasm_store], should be explicitly used when emitting WebAssembly
621     /// stores.
622     fn wasm_store(&mut self, src: Reg, dst: Self::Address, size: OperandSize);
623 
624     /// Perform a zero-extended stack load.
625     fn load(&mut self, src: Self::Address, dst: WritableReg, size: OperandSize);
626 
627     /// Perform a WebAssembly load.
628     /// A WebAssembly load introduces several additional invariants compared to
629     /// [Self::load], more precisely, it can implicitly trap, in certain
630     /// circumstances, even if explicit bounds checks are elided, in that sense,
631     /// we consider this type of load as untrusted. It can also differ with
632     /// regards to the endianness depending on the target ISA. For this reason,
633     /// [Self::wasm_load], should be explicitly used when emitting WebAssembly
634     /// loads.
635     fn wasm_load(
636         &mut self,
637         src: Self::Address,
638         dst: WritableReg,
639         size: OperandSize,
640         kind: Option<ExtendKind>,
641     );
642 
643     /// Alias for `MacroAssembler::load` with the operand size corresponding
644     /// to the pointer size of the target.
645     fn load_ptr(&mut self, src: Self::Address, dst: WritableReg);
646 
647     /// Loads the effective address into destination.
648     fn load_addr(&mut self, _src: Self::Address, _dst: WritableReg, _size: OperandSize);
649 
650     /// Pop a value from the machine stack into the given register.
651     fn pop(&mut self, dst: WritableReg, size: OperandSize);
652 
653     /// Perform a move.
654     fn mov(&mut self, dst: WritableReg, src: RegImm, size: OperandSize);
655 
656     /// Perform a conditional move.
657     fn cmov(&mut self, dst: WritableReg, src: Reg, cc: IntCmpKind, size: OperandSize);
658 
659     /// Performs a memory move of bytes from src to dest.
660     /// Bytes are moved in blocks of 8 bytes, where possible.
661     fn memmove(&mut self, src: SPOffset, dst: SPOffset, bytes: u32, direction: MemMoveDirection) {
662         match direction {
663             MemMoveDirection::LowToHigh => debug_assert!(dst.as_u32() < src.as_u32()),
664             MemMoveDirection::HighToLow => debug_assert!(dst.as_u32() > src.as_u32()),
665         }
666         // At least 4 byte aligned.
667         debug_assert!(bytes % 4 == 0);
668         let mut remaining = bytes;
669         let word_bytes = <Self::ABI as abi::ABI>::word_bytes();
670         let scratch = scratch!(Self);
671 
672         let mut dst_offs = dst.as_u32() - bytes;
673         let mut src_offs = src.as_u32() - bytes;
674 
675         let word_bytes = word_bytes as u32;
676         while remaining >= word_bytes {
677             remaining -= word_bytes;
678             dst_offs += word_bytes;
679             src_offs += word_bytes;
680 
681             self.load_ptr(
682                 self.address_from_sp(SPOffset::from_u32(src_offs)),
683                 writable!(scratch),
684             );
685             self.store_ptr(
686                 scratch.into(),
687                 self.address_from_sp(SPOffset::from_u32(dst_offs)),
688             );
689         }
690 
691         if remaining > 0 {
692             let half_word = word_bytes / 2;
693             let ptr_size = OperandSize::from_bytes(half_word as u8);
694             debug_assert!(remaining == half_word);
695             dst_offs += half_word;
696             src_offs += half_word;
697 
698             self.load(
699                 self.address_from_sp(SPOffset::from_u32(src_offs)),
700                 writable!(scratch),
701                 ptr_size,
702             );
703             self.store(
704                 scratch.into(),
705                 self.address_from_sp(SPOffset::from_u32(dst_offs)),
706                 ptr_size,
707             );
708         }
709     }
710 
711     /// Perform add operation.
712     fn add(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize);
713 
714     /// Perform a checked unsigned integer addition, emitting the provided trap
715     /// if the addition overflows.
716     fn checked_uadd(
717         &mut self,
718         dst: WritableReg,
719         lhs: Reg,
720         rhs: RegImm,
721         size: OperandSize,
722         trap: TrapCode,
723     );
724 
725     /// Perform subtraction operation.
726     fn sub(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize);
727 
728     /// Perform multiplication operation.
729     fn mul(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize);
730 
731     /// Perform a floating point add operation.
732     fn float_add(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize);
733 
734     /// Perform a floating point subtraction operation.
735     fn float_sub(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize);
736 
737     /// Perform a floating point multiply operation.
738     fn float_mul(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize);
739 
740     /// Perform a floating point divide operation.
741     fn float_div(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize);
742 
743     /// Perform a floating point minimum operation. In x86, this will emit
744     /// multiple instructions.
745     fn float_min(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize);
746 
747     /// Perform a floating point maximum operation. In x86, this will emit
748     /// multiple instructions.
749     fn float_max(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize);
750 
751     /// Perform a floating point copysign operation. In x86, this will emit
752     /// multiple instructions.
753     fn float_copysign(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize);
754 
755     /// Perform a floating point abs operation.
756     fn float_abs(&mut self, dst: WritableReg, size: OperandSize);
757 
758     /// Perform a floating point negation operation.
759     fn float_neg(&mut self, dst: WritableReg, size: OperandSize);
760 
761     /// Perform a floating point floor operation.
762     fn float_round<F: FnMut(&mut FuncEnv<Self::Ptr>, &mut CodeGenContext<Emission>, &mut Self)>(
763         &mut self,
764         mode: RoundingMode,
765         env: &mut FuncEnv<Self::Ptr>,
766         context: &mut CodeGenContext<Emission>,
767         size: OperandSize,
768         fallback: F,
769     );
770 
771     /// Perform a floating point square root operation.
772     fn float_sqrt(&mut self, dst: WritableReg, src: Reg, size: OperandSize);
773 
774     /// Perform logical and operation.
775     fn and(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize);
776 
777     /// Perform logical or operation.
778     fn or(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize);
779 
780     /// Perform logical exclusive or operation.
781     fn xor(&mut self, dst: WritableReg, lhs: Reg, rhs: RegImm, size: OperandSize);
782 
783     /// Perform a shift operation between a register and an immediate.
784     fn shift_ir(
785         &mut self,
786         dst: WritableReg,
787         imm: u64,
788         lhs: Reg,
789         kind: ShiftKind,
790         size: OperandSize,
791     );
792 
793     /// Perform a shift operation between two registers.
794     /// This case is special in that some architectures have specific expectations
795     /// regarding the location of the instruction arguments. To free the
796     /// caller from having to deal with the architecture specific constraints
797     /// we give this function access to the code generation context, allowing
798     /// each implementation to decide the lowering path.
799     fn shift(&mut self, context: &mut CodeGenContext<Emission>, kind: ShiftKind, size: OperandSize);
800 
801     /// Perform division operation.
802     /// Division is special in that some architectures have specific
803     /// expectations regarding the location of the instruction
804     /// arguments and regarding the location of the quotient /
805     /// remainder. To free the caller from having to deal with the
806     /// architecture specific constraints we give this function access
807     /// to the code generation context, allowing each implementation
808     /// to decide the lowering path.  For cases in which division is a
809     /// unconstrained binary operation, the caller can decide to use
810     /// the `CodeGenContext::i32_binop` or `CodeGenContext::i64_binop`
811     /// functions.
812     fn div(&mut self, context: &mut CodeGenContext<Emission>, kind: DivKind, size: OperandSize);
813 
814     /// Calculate remainder.
815     fn rem(&mut self, context: &mut CodeGenContext<Emission>, kind: RemKind, size: OperandSize);
816 
817     /// Compares `src1` against `src2` for the side effect of setting processor
818     /// flags.
819     ///
820     /// Note that `src1` is the left-hand-side of the comparison and `src2` is
821     /// the right-hand-side, so if testing `a < b` then `src1 == a` and
822     /// `src2 == b`
823     fn cmp(&mut self, src1: Reg, src2: RegImm, size: OperandSize);
824 
825     /// Compare src and dst and put the result in dst.
826     /// This function will potentially emit a series of instructions.
827     ///
828     /// The initial value in `dst` is the left-hand-side of the comparison and
829     /// the initial value in `src` is the right-hand-side of the comparison.
830     /// That means for `a < b` then `dst == a` and `src == b`.
831     fn cmp_with_set(&mut self, dst: WritableReg, src: RegImm, kind: IntCmpKind, size: OperandSize);
832 
833     /// Compare floats in src1 and src2 and put the result in dst.
834     /// In x86, this will emit multiple instructions.
835     fn float_cmp_with_set(
836         &mut self,
837         dst: WritableReg,
838         src1: Reg,
839         src2: Reg,
840         kind: FloatCmpKind,
841         size: OperandSize,
842     );
843 
844     /// Count the number of leading zeroes in src and put the result in dst.
845     /// In x64, this will emit multiple instructions if the `has_lzcnt` flag is
846     /// false.
847     fn clz(&mut self, dst: WritableReg, src: Reg, size: OperandSize);
848 
849     /// Count the number of trailing zeroes in src and put the result in dst.masm
850     /// In x64, this will emit multiple instructions if the `has_tzcnt` flag is
851     /// false.
852     fn ctz(&mut self, dst: WritableReg, src: Reg, size: OperandSize);
853 
854     /// Push the register to the stack, returning the stack slot metadata.
855     // NB
856     // The stack alignment should not be assumed after any call to `push`,
857     // unless explicitly aligned otherwise.  Typically, stack alignment is
858     // maintained at call sites and during the execution of
859     // epilogues.
860     fn push(&mut self, src: Reg, size: OperandSize) -> StackSlot;
861 
862     /// Finalize the assembly and return the result.
863     fn finalize(self, base: Option<SourceLoc>) -> MachBufferFinalized<Final>;
864 
865     /// Zero a particular register.
866     fn zero(&mut self, reg: WritableReg);
867 
868     /// Count the number of 1 bits in src and put the result in dst. In x64,
869     /// this will emit multiple instructions if the `has_popcnt` flag is false.
870     fn popcnt(&mut self, context: &mut CodeGenContext<Emission>, size: OperandSize);
871 
872     /// Converts an i64 to an i32 by discarding the high 32 bits.
873     fn wrap(&mut self, dst: WritableReg, src: Reg);
874 
875     /// Extends an integer of a given size to a larger size.
876     fn extend(&mut self, dst: WritableReg, src: Reg, kind: ExtendKind);
877 
878     /// Emits one or more instructions to perform a signed truncation of a
879     /// float into an integer.
880     fn signed_truncate(
881         &mut self,
882         dst: WritableReg,
883         src: Reg,
884         src_size: OperandSize,
885         dst_size: OperandSize,
886         kind: TruncKind,
887     );
888 
889     /// Emits one or more instructions to perform an unsigned truncation of a
890     /// float into an integer.
891     fn unsigned_truncate(
892         &mut self,
893         dst: WritableReg,
894         src: Reg,
895         tmp_fpr: Reg,
896         src_size: OperandSize,
897         dst_size: OperandSize,
898         kind: TruncKind,
899     );
900 
901     /// Emits one or more instructions to perform a signed convert of an
902     /// integer into a float.
903     fn signed_convert(
904         &mut self,
905         dst: WritableReg,
906         src: Reg,
907         src_size: OperandSize,
908         dst_size: OperandSize,
909     );
910 
911     /// Emits one or more instructions to perform an unsigned convert of an
912     /// integer into a float.
913     fn unsigned_convert(
914         &mut self,
915         dst: WritableReg,
916         src: Reg,
917         tmp_gpr: Reg,
918         src_size: OperandSize,
919         dst_size: OperandSize,
920     );
921 
922     /// Reinterpret a float as an integer.
923     fn reinterpret_float_as_int(&mut self, dst: WritableReg, src: Reg, size: OperandSize);
924 
925     /// Reinterpret an integer as a float.
926     fn reinterpret_int_as_float(&mut self, dst: WritableReg, src: Reg, size: OperandSize);
927 
928     /// Demote an f64 to an f32.
929     fn demote(&mut self, dst: WritableReg, src: Reg);
930 
931     /// Promote an f32 to an f64.
932     fn promote(&mut self, dst: WritableReg, src: Reg);
933 
934     /// Zero a given memory range.
935     ///
936     /// The default implementation divides the given memory range
937     /// into word-sized slots. Then it unrolls a series of store
938     /// instructions, effectively assigning zero to each slot.
939     fn zero_mem_range(&mut self, mem: &Range<u32>) {
940         let word_size = <Self::ABI as abi::ABI>::word_bytes() as u32;
941         if mem.is_empty() {
942             return;
943         }
944 
945         let start = if mem.start % word_size == 0 {
946             mem.start
947         } else {
948             // Ensure that the start of the range is at least 4-byte aligned.
949             assert!(mem.start % 4 == 0);
950             let start = align_to(mem.start, word_size);
951             let addr: Self::Address = self.local_address(&LocalSlot::i32(start));
952             self.store(RegImm::i32(0), addr, OperandSize::S32);
953             // Ensure that the new start of the range, is word-size aligned.
954             assert!(start % word_size == 0);
955             start
956         };
957 
958         let end = align_to(mem.end, word_size);
959         let slots = (end - start) / word_size;
960 
961         if slots == 1 {
962             let slot = LocalSlot::i64(start + word_size);
963             let addr: Self::Address = self.local_address(&slot);
964             self.store(RegImm::i64(0), addr, OperandSize::S64);
965         } else {
966             // TODO
967             // Add an upper bound to this generation;
968             // given a considerably large amount of slots
969             // this will be inefficient.
970             let zero = scratch!(Self);
971             self.zero(writable!(zero));
972             let zero = RegImm::reg(zero);
973 
974             for step in (start..end).into_iter().step_by(word_size as usize) {
975                 let slot = LocalSlot::i64(step + word_size);
976                 let addr: Self::Address = self.local_address(&slot);
977                 self.store(zero, addr, OperandSize::S64);
978             }
979         }
980     }
981 
982     /// Generate a label.
983     fn get_label(&mut self) -> MachLabel;
984 
985     /// Bind the given label at the current code offset.
986     fn bind(&mut self, label: MachLabel);
987 
988     /// Conditional branch.
989     ///
990     /// Performs a comparison between the two operands,
991     /// and immediately after emits a jump to the given
992     /// label destination if the condition is met.
993     fn branch(
994         &mut self,
995         kind: IntCmpKind,
996         lhs: Reg,
997         rhs: RegImm,
998         taken: MachLabel,
999         size: OperandSize,
1000     );
1001 
1002     /// Emits and unconditional jump to the given label.
1003     fn jmp(&mut self, target: MachLabel);
1004 
1005     /// Emits a jump table sequence. The default label is specified as
1006     /// the last element of the targets slice.
1007     fn jmp_table(&mut self, targets: &[MachLabel], index: Reg, tmp: Reg);
1008 
1009     /// Emit an unreachable code trap.
1010     fn unreachable(&mut self);
1011 
1012     /// Emit an unconditional trap.
1013     fn trap(&mut self, code: TrapCode);
1014 
1015     /// Traps if the condition code is met.
1016     fn trapif(&mut self, cc: IntCmpKind, code: TrapCode);
1017 
1018     /// Trap if the source register is zero.
1019     fn trapz(&mut self, src: Reg, code: TrapCode);
1020 
1021     /// Ensures that the stack pointer is correctly positioned before an unconditional
1022     /// jump according to the requirements of the destination target.
1023     fn ensure_sp_for_jump(&mut self, target: SPOffset) {
1024         let bytes = self
1025             .sp_offset()
1026             .as_u32()
1027             .checked_sub(target.as_u32())
1028             .unwrap_or(0);
1029         if bytes > 0 {
1030             self.free_stack(bytes);
1031         }
1032     }
1033 
1034     /// Mark the start of a source location returning the machine code offset
1035     /// and the relative source code location.
1036     fn start_source_loc(&mut self, loc: RelSourceLoc) -> (CodeOffset, RelSourceLoc);
1037 
1038     /// Mark the end of a source location.
1039     fn end_source_loc(&mut self);
1040 
1041     /// The current offset, in bytes from the beginning of the function.
1042     fn current_code_offset(&self) -> CodeOffset;
1043 
1044     /// Performs a 128-bit addition
1045     fn add128(
1046         &mut self,
1047         dst_lo: WritableReg,
1048         dst_hi: WritableReg,
1049         lhs_lo: Reg,
1050         lhs_hi: Reg,
1051         rhs_lo: Reg,
1052         rhs_hi: Reg,
1053     );
1054 
1055     /// Performs a 128-bit subtraction
1056     fn sub128(
1057         &mut self,
1058         dst_lo: WritableReg,
1059         dst_hi: WritableReg,
1060         lhs_lo: Reg,
1061         lhs_hi: Reg,
1062         rhs_lo: Reg,
1063         rhs_hi: Reg,
1064     );
1065 
1066     /// Performs a widening multiplication from two 64-bit operands into a
1067     /// 128-bit result.
1068     ///
1069     /// Note that some platforms require special handling of registers in this
1070     /// instruction (e.g. x64) so full access to `CodeGenContext` is provided.
1071     fn mul_wide(&mut self, context: &mut CodeGenContext<Emission>, kind: MulWideKind);
1072 }
1073