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