xref: /wasmtime-44.0.1/winch/codegen/src/masm.rs (revision f9f8a4df)
1 use crate::abi::{self, align_to, LocalSlot};
2 use crate::codegen::{ptr_type_from_ptr_size, CodeGenContext, TableData};
3 use crate::isa::reg::Reg;
4 use cranelift_codegen::{ir::LibCall, Final, MachBufferFinalized, MachLabel};
5 use std::{fmt::Debug, ops::Range};
6 use wasmtime_environ::PtrSize;
7 
8 pub(crate) use cranelift_codegen::ir::TrapCode;
9 
10 #[derive(Eq, PartialEq)]
11 pub(crate) enum DivKind {
12     /// Signed division.
13     Signed,
14     /// Unsigned division.
15     Unsigned,
16 }
17 
18 /// Remainder kind.
19 pub(crate) enum RemKind {
20     /// Signed remainder.
21     Signed,
22     /// Unsigned remainder.
23     Unsigned,
24 }
25 
26 /// Representation of the stack pointer offset.
27 #[derive(Copy, Clone, Eq, PartialEq, Debug, PartialOrd, Ord)]
28 pub struct SPOffset(u32);
29 
30 impl SPOffset {
31     pub fn from_u32(offs: u32) -> Self {
32         Self(offs)
33     }
34 
35     pub fn as_u32(&self) -> u32 {
36         self.0
37     }
38 }
39 
40 /// A stack slot.
41 #[derive(Debug, Clone, Copy, Eq, PartialEq)]
42 pub struct StackSlot {
43     /// The location of the slot, relative to the stack pointer.
44     pub offset: SPOffset,
45     /// The size of the slot, in bytes.
46     pub size: u32,
47 }
48 
49 impl StackSlot {
50     pub fn new(offs: SPOffset, size: u32) -> Self {
51         Self { offset: offs, size }
52     }
53 }
54 
55 /// Kinds of integer binary comparison in WebAssembly. The [`MacroAssembler`]
56 /// implementation for each ISA is responsible for emitting the correct
57 /// sequence of instructions when lowering to machine code.
58 #[derive(Debug, Clone, Copy, Eq, PartialEq)]
59 pub(crate) enum IntCmpKind {
60     /// Equal.
61     Eq,
62     /// Not equal.
63     Ne,
64     /// Signed less than.
65     LtS,
66     /// Unsigned less than.
67     LtU,
68     /// Signed greater than.
69     GtS,
70     /// Unsigned greater than.
71     GtU,
72     /// Signed less than or equal.
73     LeS,
74     /// Unsigned less than or equal.
75     LeU,
76     /// Signed greater than or equal.
77     GeS,
78     /// Unsigned greater than or equal.
79     GeU,
80 }
81 
82 /// Kinds of float binary comparison in WebAssembly. The [`MacroAssembler`]
83 /// implementation for each ISA is responsible for emitting the correct
84 /// sequence of instructions when lowering code.
85 #[derive(Debug)]
86 pub(crate) enum FloatCmpKind {
87     /// Equal.
88     Eq,
89     /// Not equal.
90     Ne,
91     /// Less than.
92     Lt,
93     /// Greater than.
94     Gt,
95     /// Less than or equal.
96     Le,
97     /// Greater than or equal.
98     Ge,
99 }
100 
101 /// Kinds of shifts in WebAssembly.The [`masm`] implementation for each ISA is
102 /// responsible for emitting the correct sequence of instructions when
103 /// lowering to machine code.
104 pub(crate) enum ShiftKind {
105     /// Left shift.
106     Shl,
107     /// Signed right shift.
108     ShrS,
109     /// Unsigned right shift.
110     ShrU,
111     /// Left rotate.
112     Rotl,
113     /// Right rotate.
114     Rotr,
115 }
116 
117 /// Operand size, in bits.
118 #[derive(Copy, Debug, Clone, Eq, PartialEq)]
119 pub(crate) enum OperandSize {
120     /// 32 bits.
121     S32,
122     /// 64 bits.
123     S64,
124     /// 128 bits.
125     S128,
126 }
127 
128 impl OperandSize {
129     /// The number of bits in the operand.
130     pub fn num_bits(&self) -> i32 {
131         match self {
132             OperandSize::S32 => 32,
133             OperandSize::S64 => 64,
134             OperandSize::S128 => 128,
135         }
136     }
137 
138     /// The number of bytes in the operand.
139     pub fn bytes(&self) -> u32 {
140         match self {
141             Self::S32 => 4,
142             Self::S64 => 8,
143             Self::S128 => 16,
144         }
145     }
146 
147     /// The binary logarithm of the number of bits in the operand.
148     pub fn log2(&self) -> u8 {
149         match self {
150             OperandSize::S32 => 5,
151             OperandSize::S64 => 6,
152             OperandSize::S128 => 7,
153         }
154     }
155 
156     /// Create an [`OperandSize`]  from the given number of bytes.
157     pub fn from_bytes(bytes: u8) -> Self {
158         use OperandSize::*;
159         match bytes {
160             4 => S32,
161             8 => S64,
162             16 => S128,
163             _ => panic!("Invalid bytes {} for OperandSize", bytes),
164         }
165     }
166 }
167 
168 /// An abstraction over a register or immediate.
169 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
170 pub(crate) enum RegImm {
171     /// A register.
172     Reg(Reg),
173     /// A tagged immediate argument.
174     Imm(Imm),
175 }
176 
177 /// An tagged representation of an immediate.
178 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
179 pub(crate) enum Imm {
180     /// I32 immediate.
181     I32(u32),
182     /// I64 immediate.
183     I64(u64),
184     /// F32 immediate.
185     F32(u32),
186     /// F64 immediate.
187     F64(u64),
188 }
189 
190 impl Imm {
191     /// Create a new I64 immediate.
192     pub fn i64(val: i64) -> Self {
193         Self::I64(val as u64)
194     }
195 
196     /// Create a new I32 immediate.
197     pub fn i32(val: i32) -> Self {
198         Self::I32(val as u32)
199     }
200 
201     /// Create a new F32 immediate.
202     // Temporary until support for f32.const is added.
203     #[allow(dead_code)]
204     pub fn f32(bits: u32) -> Self {
205         Self::F32(bits)
206     }
207 
208     /// Create a new F64 immediate.
209     // Temporary until support for f64.const is added.
210     #[allow(dead_code)]
211     pub fn f64(bits: u64) -> Self {
212         Self::F64(bits)
213     }
214 
215     /// Convert the immediate to i32, if possible.
216     pub fn to_i32(&self) -> Option<i32> {
217         match self {
218             Self::I32(v) => Some(*v as i32),
219             Self::I64(v) => i32::try_from(*v as i64).ok(),
220             _ => None,
221         }
222     }
223 }
224 
225 #[derive(Copy, Clone, Debug)]
226 pub(crate) enum CalleeKind {
227     /// A function call to a raw address.
228     Indirect(Reg),
229     /// A function call to a local function.
230     Direct(u32),
231     /// Call to a well known LibCall.
232     Known(LibCall),
233 }
234 
235 impl CalleeKind {
236     /// Creates a callee kind from a register.
237     pub fn indirect(reg: Reg) -> Self {
238         Self::Indirect(reg)
239     }
240 
241     /// Creates a direct callee kind from a function index.
242     pub fn direct(index: u32) -> Self {
243         Self::Direct(index)
244     }
245 
246     /// Creates a known callee kind from a libcall.
247     pub fn known(call: LibCall) -> Self {
248         Self::Known(call)
249     }
250 }
251 
252 impl RegImm {
253     /// Register constructor.
254     pub fn reg(r: Reg) -> Self {
255         RegImm::Reg(r)
256     }
257 
258     /// I64 immediate constructor.
259     pub fn i64(val: i64) -> Self {
260         RegImm::Imm(Imm::i64(val))
261     }
262 
263     /// I32 immediate constructor.
264     pub fn i32(val: i32) -> Self {
265         RegImm::Imm(Imm::i32(val))
266     }
267 
268     /// F32 immediate, stored using its bits representation.
269     // Temporary until support for f32.const is added.
270     #[allow(dead_code)]
271     pub fn f32(bits: u32) -> Self {
272         RegImm::Imm(Imm::f32(bits))
273     }
274 
275     /// F64 immediate, stored using its bits representation.
276     // Temporary until support for f64.const is added.
277     #[allow(dead_code)]
278     pub fn f64(bits: u64) -> Self {
279         RegImm::Imm(Imm::f64(bits))
280     }
281 }
282 
283 impl From<Reg> for RegImm {
284     fn from(r: Reg) -> Self {
285         Self::Reg(r)
286     }
287 }
288 
289 pub enum RoundingMode {
290     Nearest,
291     Up,
292     Down,
293     Zero,
294 }
295 
296 /// Generic MacroAssembler interface used by the code generation.
297 ///
298 /// The MacroAssembler trait aims to expose an interface, high-level enough,
299 /// so that each ISA can provide its own lowering to machine code. For example,
300 /// for WebAssembly operators that don't have a direct mapping to a machine
301 /// a instruction, the interface defines a signature matching the WebAssembly
302 /// operator, allowing each implementation to lower such operator entirely.
303 /// This approach attributes more responsibility to the MacroAssembler, but frees
304 /// the caller from concerning about assembling the right sequence of
305 /// instructions at the operator callsite.
306 ///
307 /// The interface defaults to a three-argument form for binary operations;
308 /// this allows a natural mapping to instructions for RISC architectures,
309 /// that use three-argument form.
310 /// This approach allows for a more general interface that can be restricted
311 /// where needed, in the case of architectures that use a two-argument form.
312 
313 pub(crate) trait MacroAssembler {
314     /// The addressing mode.
315     type Address: Copy + Debug;
316 
317     /// The pointer representation of the target ISA,
318     /// used to access information from [`VMOffsets`].
319     type Ptr: PtrSize;
320 
321     /// The ABI details of the target.
322     type ABI: abi::ABI;
323 
324     /// Emit the function prologue.
325     fn prologue(&mut self);
326 
327     /// Emit the function epilogue.
328     fn epilogue(&mut self, locals_size: u32);
329 
330     /// Reserve stack space.
331     fn reserve_stack(&mut self, bytes: u32);
332 
333     /// Free stack space.
334     fn free_stack(&mut self, bytes: u32);
335 
336     /// Reset the stack pointer to the given offset;
337     ///
338     /// Used to reset the stack pointer to a given offset
339     /// when dealing with unreachable code.
340     fn reset_stack_pointer(&mut self, offset: SPOffset);
341 
342     /// Get the address of a local slot.
343     fn local_address(&mut self, local: &LocalSlot) -> Self::Address;
344 
345     /// Loads the address of the table element at a given index. Returns the
346     /// address of the table element using the provided register as base.
347     fn table_elem_address(
348         &mut self,
349         index: Reg,
350         base: Reg,
351         table_data: &TableData,
352         context: &mut CodeGenContext,
353     ) -> Self::Address;
354 
355     /// Retrieves the size of the table, pushing the result to the value stack.
356     fn table_size(&mut self, table_data: &TableData, context: &mut CodeGenContext);
357 
358     /// Constructs an address with an offset that is relative to the
359     /// current position of the stack pointer (e.g. [sp + (sp_offset -
360     /// offset)].
361     fn address_from_sp(&self, offset: SPOffset) -> Self::Address;
362 
363     /// Constructs an address with an offset that is absolute to the
364     /// current position of the stack pointer (e.g. [sp + offset].
365     fn address_at_sp(&self, offset: SPOffset) -> Self::Address;
366 
367     /// Alias for [`Self::address_at_reg`] using the VMContext register as
368     /// a base. The VMContext register is derived from the ABI type that is
369     /// associated to the MacroAssembler.
370     fn address_at_vmctx(&self, offset: u32) -> Self::Address;
371 
372     /// Construct an address that is absolute to the current position
373     /// of the given register.
374     fn address_at_reg(&self, reg: Reg, offset: u32) -> Self::Address;
375 
376     /// Emit a function call to either a local or external function.
377     fn call(&mut self, stack_args_size: u32, f: impl FnMut(&mut Self) -> CalleeKind) -> u32;
378 
379     /// Get stack pointer offset.
380     fn sp_offset(&self) -> SPOffset;
381 
382     /// Perform a stack store.
383     fn store(&mut self, src: RegImm, dst: Self::Address, size: OperandSize);
384 
385     /// Perform a stack load.
386     fn load(&mut self, src: Self::Address, dst: Reg, size: OperandSize);
387 
388     /// Alias for `MacroAssembler::load` with the operand size corresponding
389     /// to the pointer size of the target.
390     fn load_ptr(&mut self, src: Self::Address, dst: Reg);
391 
392     /// Loads the effective address into destination.
393     fn load_addr(&mut self, _src: Self::Address, _dst: Reg, _size: OperandSize);
394 
395     /// Alias for `MacroAssembler::store` with the operand size corresponding
396     /// to the pointer size of the target.
397     fn store_ptr(&mut self, src: Reg, dst: Self::Address);
398 
399     /// Pop a value from the machine stack into the given register.
400     fn pop(&mut self, dst: Reg, size: OperandSize);
401 
402     /// Perform a move.
403     fn mov(&mut self, src: RegImm, dst: Reg, size: OperandSize);
404 
405     /// Perform a conditional move.
406     fn cmov(&mut self, src: Reg, dst: Reg, cc: IntCmpKind, size: OperandSize);
407 
408     /// Performs a memory move of bytes from src to dest.
409     /// Bytes are moved in blocks of 8 bytes, where possible.
410     fn memmove(&mut self, src: SPOffset, dst: SPOffset, bytes: u32) {
411         debug_assert!(dst.as_u32() < src.as_u32());
412         // At least 4 byte aligned.
413         debug_assert!(bytes % 4 == 0);
414         let mut remaining = bytes;
415         let word_bytes = <Self::ABI as abi::ABI>::word_bytes();
416         let scratch = <Self::ABI as abi::ABI>::scratch_reg();
417         let ptr_size: OperandSize = ptr_type_from_ptr_size(word_bytes as u8).into();
418 
419         let mut dst_offs = dst.as_u32() - bytes;
420         let mut src_offs = src.as_u32() - bytes;
421 
422         while remaining >= word_bytes {
423             remaining -= word_bytes;
424             dst_offs += word_bytes;
425             src_offs += word_bytes;
426 
427             self.load(
428                 self.address_from_sp(SPOffset::from_u32(src_offs)),
429                 scratch,
430                 ptr_size,
431             );
432             self.store(
433                 scratch.into(),
434                 self.address_from_sp(SPOffset::from_u32(dst_offs)),
435                 ptr_size,
436             );
437         }
438 
439         if remaining > 0 {
440             let half_word = word_bytes / 2;
441             let ptr_size = OperandSize::from_bytes(half_word as u8);
442             debug_assert!(remaining == half_word);
443             dst_offs += half_word;
444             src_offs += half_word;
445 
446             self.load(
447                 self.address_from_sp(SPOffset::from_u32(src_offs)),
448                 scratch,
449                 ptr_size,
450             );
451             self.store(
452                 scratch.into(),
453                 self.address_from_sp(SPOffset::from_u32(dst_offs)),
454                 ptr_size,
455             );
456         }
457     }
458 
459     /// Perform add operation.
460     fn add(&mut self, dst: Reg, lhs: Reg, rhs: RegImm, size: OperandSize);
461 
462     /// Perform subtraction operation.
463     fn sub(&mut self, dst: Reg, lhs: Reg, rhs: RegImm, size: OperandSize);
464 
465     /// Perform multiplication operation.
466     fn mul(&mut self, dst: Reg, lhs: Reg, rhs: RegImm, size: OperandSize);
467 
468     /// Perform a floating point add operation.
469     fn float_add(&mut self, dst: Reg, lhs: Reg, rhs: Reg, size: OperandSize);
470 
471     /// Perform a floating point subtraction operation.
472     fn float_sub(&mut self, dst: Reg, lhs: Reg, rhs: Reg, size: OperandSize);
473 
474     /// Perform a floating point multiply operation.
475     fn float_mul(&mut self, dst: Reg, lhs: Reg, rhs: Reg, size: OperandSize);
476 
477     /// Perform a floating point divide operation.
478     fn float_div(&mut self, dst: Reg, lhs: Reg, rhs: Reg, size: OperandSize);
479 
480     /// Perform a floating point minimum operation. In x86, this will emit
481     /// multiple instructions.
482     fn float_min(&mut self, dst: Reg, lhs: Reg, rhs: Reg, size: OperandSize);
483 
484     /// Perform a floating point maximum operation. In x86, this will emit
485     /// multiple instructions.
486     fn float_max(&mut self, dst: Reg, lhs: Reg, rhs: Reg, size: OperandSize);
487 
488     /// Perform a floating point copysign operation. In x86, this will emit
489     /// multiple instructions.
490     fn float_copysign(&mut self, dst: Reg, lhs: Reg, rhs: Reg, size: OperandSize);
491 
492     /// Perform a floating point abs operation.
493     fn float_abs(&mut self, dst: Reg, size: OperandSize);
494 
495     /// Perform a floating point negation operation.
496     fn float_neg(&mut self, dst: Reg, size: OperandSize);
497 
498     /// Perform a floating point floor operation.
499     fn float_round(&mut self, mode: RoundingMode, context: &mut CodeGenContext, size: OperandSize);
500 
501     /// Perform a floating point square root operation.
502     fn float_sqrt(&mut self, dst: Reg, src: Reg, size: OperandSize);
503 
504     /// Perform logical and operation.
505     fn and(&mut self, dst: Reg, lhs: Reg, rhs: RegImm, size: OperandSize);
506 
507     /// Perform logical or operation.
508     fn or(&mut self, dst: Reg, lhs: Reg, rhs: RegImm, size: OperandSize);
509 
510     /// Perform logical exclusive or operation.
511     fn xor(&mut self, dst: Reg, lhs: Reg, rhs: RegImm, size: OperandSize);
512 
513     /// Perform a shift operation.
514     /// Shift is special in that some architectures have specific expectations
515     /// regarding the location of the instruction arguments. To free the
516     /// caller from having to deal with the architecture specific constraints
517     /// we give this function access to the code generation context, allowing
518     /// each implementation to decide the lowering path.
519     fn shift(&mut self, context: &mut CodeGenContext, kind: ShiftKind, size: OperandSize);
520 
521     /// Perform division operation.
522     /// Division is special in that some architectures have specific
523     /// expectations regarding the location of the instruction
524     /// arguments and regarding the location of the quotient /
525     /// remainder. To free the caller from having to deal with the
526     /// architecure specific contraints we give this function access
527     /// to the code generation context, allowing each implementation
528     /// to decide the lowering path.  For cases in which division is a
529     /// unconstrained binary operation, the caller can decide to use
530     /// the `CodeGenContext::i32_binop` or `CodeGenContext::i64_binop`
531     /// functions.
532     fn div(&mut self, context: &mut CodeGenContext, kind: DivKind, size: OperandSize);
533 
534     /// Calculate remainder.
535     fn rem(&mut self, context: &mut CodeGenContext, kind: RemKind, size: OperandSize);
536 
537     /// Compare src and dst and put the result in dst.
538     fn cmp(&mut self, src: RegImm, dest: Reg, size: OperandSize);
539 
540     /// Compare src and dst and put the result in dst.
541     /// This function will potentially emit a series of instructions.
542     fn cmp_with_set(&mut self, src: RegImm, dst: Reg, kind: IntCmpKind, size: OperandSize);
543 
544     /// Compare floats in src1 and src2 and put the result in dst.
545     /// In x86, this will emit multiple instructions.
546     fn float_cmp_with_set(
547         &mut self,
548         src1: Reg,
549         src2: Reg,
550         dst: Reg,
551         kind: FloatCmpKind,
552         size: OperandSize,
553     );
554 
555     /// Count the number of leading zeroes in src and put the result in dst.
556     /// In x64, this will emit multiple instructions if the `has_lzcnt` flag is
557     /// false.
558     fn clz(&mut self, src: Reg, dst: Reg, size: OperandSize);
559 
560     /// Count the number of trailing zeroes in src and put the result in dst.masm
561     /// In x64, this will emit multiple instructions if the `has_tzcnt` flag is
562     /// false.
563     fn ctz(&mut self, src: Reg, dst: Reg, size: OperandSize);
564 
565     /// Push the register to the stack, returning the stack slot metadata.
566     // NB
567     // The stack alignment should not be assumed after any call to `push`,
568     // unless explicitly aligned otherwise.  Typically, stack alignment is
569     // maintained at call sites and during the execution of
570     // epilogues.
571     fn push(&mut self, src: Reg, size: OperandSize) -> StackSlot;
572 
573     /// Finalize the assembly and return the result.
574     fn finalize(self) -> MachBufferFinalized<Final>;
575 
576     /// Zero a particular register.
577     fn zero(&mut self, reg: Reg);
578 
579     /// Count the number of 1 bits in src and put the result in dst. In x64,
580     /// this will emit multiple instructions if the `has_popcnt` flag is false.
581     fn popcnt(&mut self, context: &mut CodeGenContext, size: OperandSize);
582 
583     /// Zero a given memory range.
584     ///
585     /// The default implementation divides the given memory range
586     /// into word-sized slots. Then it unrolls a series of store
587     /// instructions, effectively assigning zero to each slot.
588     fn zero_mem_range(&mut self, mem: &Range<u32>) {
589         let word_size = <Self::ABI as abi::ABI>::word_bytes();
590         if mem.is_empty() {
591             return;
592         }
593 
594         let start = if mem.start % word_size == 0 {
595             mem.start
596         } else {
597             // Ensure that the start of the range is at least 4-byte aligned.
598             assert!(mem.start % 4 == 0);
599             let start = align_to(mem.start, word_size);
600             let addr: Self::Address = self.local_address(&LocalSlot::i32(start));
601             self.store(RegImm::i32(0), addr, OperandSize::S32);
602             // Ensure that the new start of the range, is word-size aligned.
603             assert!(start % word_size == 0);
604             start
605         };
606 
607         let end = align_to(mem.end, word_size);
608         let slots = (end - start) / word_size;
609 
610         if slots == 1 {
611             let slot = LocalSlot::i64(start + word_size);
612             let addr: Self::Address = self.local_address(&slot);
613             self.store(RegImm::i64(0), addr, OperandSize::S64);
614         } else {
615             // TODO
616             // Add an upper bound to this generation;
617             // given a considerably large amount of slots
618             // this will be inefficient.
619             let zero = <Self::ABI as abi::ABI>::scratch_reg();
620             self.zero(zero);
621             let zero = RegImm::reg(zero);
622 
623             for step in (start..end).into_iter().step_by(word_size as usize) {
624                 let slot = LocalSlot::i64(step + word_size);
625                 let addr: Self::Address = self.local_address(&slot);
626                 self.store(zero, addr, OperandSize::S64);
627             }
628         }
629     }
630 
631     /// Generate a label.
632     fn get_label(&mut self) -> MachLabel;
633 
634     /// Bind the given label at the current code offset.
635     fn bind(&mut self, label: MachLabel);
636 
637     /// Conditional branch.
638     ///
639     /// Performs a comparison between the two operands,
640     /// and immediately after emits a jump to the given
641     /// label destination if the condition is met.
642     fn branch(
643         &mut self,
644         kind: IntCmpKind,
645         lhs: RegImm,
646         rhs: Reg,
647         taken: MachLabel,
648         size: OperandSize,
649     );
650 
651     /// Emits and unconditional jump to the given label.
652     fn jmp(&mut self, target: MachLabel);
653 
654     /// Emits a jump table sequence. The default label is specified as
655     /// the last element of the targets slice.
656     fn jmp_table(&mut self, targets: &[MachLabel], index: Reg, tmp: Reg);
657 
658     /// Emit an unreachable code trap.
659     fn unreachable(&mut self);
660 
661     /// Traps if the condition code is met.
662     fn trapif(&mut self, cc: IntCmpKind, code: TrapCode);
663 
664     /// Trap if the source register is zero.
665     fn trapz(&mut self, src: Reg, code: TrapCode);
666 
667     /// Ensures that the stack pointer is correctly positioned before an unconditional
668     /// jump according to the requirements of the destination target.
669     fn ensure_sp_for_jump(&mut self, target: SPOffset) {
670         let bytes = self
671             .sp_offset()
672             .as_u32()
673             .checked_sub(target.as_u32())
674             .unwrap_or(0);
675         if bytes > 0 {
676             self.free_stack(bytes);
677         }
678     }
679 }
680