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