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