1;; Instruction formats.
2(type MInst
3      (enum
4       ;; A no-op of zero size.
5       (Nop0)
6
7       ;; A no-op that is one instruction large.
8       (Nop4)
9
10       ;; An ALU operation with two register sources and a register destination.
11       (AluRRR
12        (alu_op ALUOp)
13        (size OperandSize)
14        (rd WritableReg)
15        (rn Reg)
16        (rm Reg))
17
18       ;; An ALU operation with three register sources and a register destination.
19       (AluRRRR
20        (alu_op ALUOp3)
21        (size OperandSize)
22        (rd WritableReg)
23        (rn Reg)
24        (rm Reg)
25        (ra Reg))
26
27       ;; An ALU operation with a register source and an immediate-12 source, and a register
28       ;; destination.
29       (AluRRImm12
30        (alu_op ALUOp)
31        (size OperandSize)
32        (rd WritableReg)
33        (rn Reg)
34        (imm12 Imm12))
35
36       ;; An ALU operation with a register source and an immediate-logic source, and a register destination.
37       (AluRRImmLogic
38        (alu_op ALUOp)
39        (size OperandSize)
40        (rd WritableReg)
41        (rn Reg)
42        (imml ImmLogic))
43
44       ;; An ALU operation with a register source and an immediate-shiftamt source, and a register destination.
45       (AluRRImmShift
46        (alu_op ALUOp)
47        (size OperandSize)
48        (rd WritableReg)
49        (rn Reg)
50        (immshift ImmShift))
51
52       ;; An ALU operation with two register sources, one of which can be shifted, and a register
53       ;; destination.
54       (AluRRRShift
55        (alu_op ALUOp)
56        (size OperandSize)
57        (rd WritableReg)
58        (rn Reg)
59        (rm Reg)
60        (shiftop ShiftOpAndAmt))
61
62       ;; An ALU operation with two register sources, one of which can be {zero,sign}-extended and
63       ;; shifted, and a register destination.
64       (AluRRRExtend
65        (alu_op ALUOp)
66        (size OperandSize)
67        (rd WritableReg)
68        (rn Reg)
69        (rm Reg)
70        (extendop ExtendOp))
71
72       ;; A bit op instruction with a single register source.
73       (BitRR
74        (op BitOp)
75        (size OperandSize)
76        (rd WritableReg)
77        (rn Reg))
78
79       ;; An unsigned (zero-extending) 8-bit load.
80       (ULoad8
81        (rd WritableReg)
82        (mem AMode)
83        (flags MemFlags))
84
85       ;; A signed (sign-extending) 8-bit load.
86       (SLoad8
87        (rd WritableReg)
88        (mem AMode)
89        (flags MemFlags))
90
91       ;; An unsigned (zero-extending) 16-bit load.
92       (ULoad16
93        (rd WritableReg)
94        (mem AMode)
95        (flags MemFlags))
96
97       ;; A signed (sign-extending) 16-bit load.
98       (SLoad16
99        (rd WritableReg)
100        (mem AMode)
101        (flags MemFlags))
102
103       ;; An unsigned (zero-extending) 32-bit load.
104       (ULoad32
105        (rd WritableReg)
106        (mem AMode)
107        (flags MemFlags))
108
109       ;; A signed (sign-extending) 32-bit load.
110       (SLoad32
111        (rd WritableReg)
112        (mem AMode)
113        (flags MemFlags))
114
115       ;; A 64-bit load.
116       (ULoad64
117        (rd WritableReg)
118        (mem AMode)
119        (flags MemFlags))
120
121       ;; An 8-bit store.
122       (Store8
123        (rd Reg)
124        (mem AMode)
125        (flags MemFlags))
126
127       ;; A 16-bit store.
128       (Store16
129        (rd Reg)
130        (mem AMode)
131        (flags MemFlags))
132
133       ;; A 32-bit store.
134       (Store32
135        (rd Reg)
136        (mem AMode)
137        (flags MemFlags))
138
139       ;; A 64-bit store.
140       (Store64
141        (rd Reg)
142        (mem AMode)
143        (flags MemFlags))
144
145       ;; A store of a pair of registers.
146       (StoreP64
147        (rt Reg)
148        (rt2 Reg)
149        (mem PairAMode)
150        (flags MemFlags))
151
152       ;; A load of a pair of registers.
153       (LoadP64
154        (rt WritableReg)
155        (rt2 WritableReg)
156        (mem PairAMode)
157        (flags MemFlags))
158
159       ;; A MOV instruction. These are encoded as ORR's (AluRRR form).
160       ;; The 32-bit version zeroes the top 32 bits of the
161       ;; destination, which is effectively an alias for an unsigned
162       ;; 32-to-64-bit extension.
163       (Mov
164        (size OperandSize)
165        (rd WritableReg)
166        (rm Reg))
167
168       ;; Like `Move` but with a particular `PReg` source (for implementing CLIF
169       ;; instructions like `get_stack_pointer`).
170       (MovFromPReg
171        (rd WritableReg)
172        (rm PReg))
173
174       ;; Like `Move` but with a particular `PReg` destination (for
175       ;; implementing CLIF instructions like `set_pinned_reg`).
176       (MovToPReg
177        (rd PReg)
178        (rm Reg))
179
180       ;; A MOV[Z,N] with a 16-bit immediate.
181       (MovWide
182        (op MoveWideOp)
183        (rd WritableReg)
184        (imm MoveWideConst)
185        (size OperandSize))
186
187       ;; A MOVK with a 16-bit immediate. Modifies its register; we
188       ;; model this with a separate input `rn` and output `rd` virtual
189       ;; register, with a regalloc constraint to tie them together.
190       (MovK
191        (rd WritableReg)
192        (rn Reg)
193        (imm MoveWideConst)
194        (size OperandSize))
195
196
197       ;; A sign- or zero-extend operation.
198       (Extend
199        (rd WritableReg)
200        (rn Reg)
201        (signed bool)
202        (from_bits u8)
203        (to_bits u8))
204
205       ;; A conditional-select operation.
206       (CSel
207        (rd WritableReg)
208        (cond Cond)
209        (rn Reg)
210        (rm Reg))
211
212       ;; A conditional-select negation operation.
213       (CSNeg
214        (rd WritableReg)
215        (cond Cond)
216        (rn Reg)
217        (rm Reg))
218
219       ;; A conditional-set operation.
220       (CSet
221        (rd WritableReg)
222        (cond Cond))
223
224       ;; A conditional-set-mask operation.
225       (CSetm
226        (rd WritableReg)
227        (cond Cond))
228
229       ;; A conditional comparison with a second register.
230       (CCmp
231        (size OperandSize)
232        (rn Reg)
233        (rm Reg)
234        (nzcv NZCV)
235        (cond Cond))
236
237       ;; A conditional comparison with an immediate.
238       (CCmpImm
239        (size OperandSize)
240        (rn Reg)
241        (imm UImm5)
242        (nzcv NZCV)
243        (cond Cond))
244
245       ;; A synthetic insn, which is a load-linked store-conditional loop, that has the overall
246       ;; effect of atomically modifying a memory location in a particular way.  Because we have
247       ;; no way to explain to the regalloc about earlyclobber registers, this instruction has
248       ;; completely fixed operand registers, and we rely on the RA's coalescing to remove copies
249       ;; in the surrounding code to the extent it can. Load- and store-exclusive instructions,
250       ;; with acquire-release semantics, are used to access memory. The operand conventions are:
251       ;;
252       ;; x25   (rd) address
253       ;; x26   (rd) second operand for `op`
254       ;; x27   (wr) old value
255       ;; x24   (wr) scratch reg; value afterwards has no meaning
256       ;; x28   (wr) scratch reg; value afterwards has no meaning
257       (AtomicRMWLoop
258        (ty Type) ;; I8, I16, I32 or I64
259        (op AtomicRMWLoopOp)
260        (flags MemFlags)
261        (addr Reg)
262        (operand Reg)
263        (oldval WritableReg)
264        (scratch1 WritableReg)
265        (scratch2 WritableReg))
266
267       ;; Similar to AtomicRMWLoop, a compare-and-swap operation implemented using a load-linked
268       ;; store-conditional loop, with acquire-release semantics.
269       ;; Note that the operand conventions, although very similar to AtomicRMWLoop, are different:
270       ;;
271       ;; x25   (rd) address
272       ;; x26   (rd) expected value
273       ;; x28   (rd) replacement value
274       ;; x27   (wr) old value
275       ;; x24   (wr) scratch reg; value afterwards has no meaning
276       (AtomicCASLoop
277        (ty Type) ;; I8, I16, I32 or I64
278        (flags MemFlags)
279        (addr Reg)
280        (expected Reg)
281        (replacement Reg)
282        (oldval WritableReg)
283        (scratch WritableReg))
284
285       ;; An atomic read-modify-write operation. These instructions require the
286       ;; Large System Extension (LSE) ISA support (FEAT_LSE). The instructions have
287       ;; acquire-release semantics.
288       (AtomicRMW
289         (op AtomicRMWOp)
290         (rs Reg)
291         (rt WritableReg)
292         (rn Reg)
293         (ty Type)
294         (flags MemFlags))
295
296       ;; An atomic compare-and-swap operation. These instructions require the
297       ;; Large System Extension (LSE) ISA support (FEAT_LSE). The instructions have
298       ;; acquire-release semantics.
299       (AtomicCAS
300         ;; `rd` is really `rs` in the encoded instruction (so `rd` == `rs`); we separate
301         ;; them here to have separate use and def vregs for regalloc.
302         (rd WritableReg)
303         (rs Reg)
304         (rt Reg)
305         (rn Reg)
306         (ty Type)
307         (flags MemFlags))
308
309       ;; Read `access_ty` bits from address `rt`, either 8, 16, 32 or 64-bits, and put
310       ;; it in `rn`, optionally zero-extending to fill a word or double word result.
311       ;; This instruction is sequentially consistent.
312       (LoadAcquire
313        (access_ty Type) ;; I8, I16, I32 or I64
314        (rt WritableReg)
315        (rn Reg)
316        (flags MemFlags))
317
318       ;; Write the lowest `ty` bits of `rt` to address `rn`.
319       ;; This instruction is sequentially consistent.
320       (StoreRelease
321        (access_ty Type) ;; I8, I16, I32 or I64
322        (rt Reg)
323        (rn Reg)
324        (flags MemFlags))
325
326       ;; A memory fence.  This must provide ordering to ensure that, at a minimum, neither loads
327       ;; nor stores may move forwards or backwards across the fence.  Currently emitted as "dmb
328       ;; ish".  This instruction is sequentially consistent.
329       (Fence)
330
331       ;; Consumption of speculative data barrier.
332       (Csdb)
333
334       ;; FPU 32-bit move.
335       (FpuMove32
336         (rd WritableReg)
337         (rn Reg))
338
339       ;; FPU move. Note that this is distinct from a vector-register
340       ;; move; moving just 64 bits seems to be significantly faster.
341       (FpuMove64
342        (rd WritableReg)
343        (rn Reg))
344
345       ;; Vector register move.
346       (FpuMove128
347        (rd WritableReg)
348        (rn Reg))
349
350       ;; Move to scalar from a vector element.
351       (FpuMoveFromVec
352        (rd WritableReg)
353        (rn Reg)
354        (idx u8)
355        (size VectorSize))
356
357       ;; Zero-extend a SIMD & FP scalar to the full width of a vector register.
358       ;; 16-bit scalars require half-precision floating-point support (FEAT_FP16).
359       (FpuExtend
360        (rd WritableReg)
361        (rn Reg)
362        (size ScalarSize))
363
364       ;; 1-op FPU instruction.
365       (FpuRR
366        (fpu_op FPUOp1)
367        (size ScalarSize)
368        (rd WritableReg)
369        (rn Reg))
370
371       ;; 2-op FPU instruction.
372       (FpuRRR
373        (fpu_op FPUOp2)
374        (size ScalarSize)
375        (rd WritableReg)
376        (rn Reg)
377        (rm Reg))
378
379       (FpuRRI
380        (fpu_op FPUOpRI)
381        (rd WritableReg)
382        (rn Reg))
383
384       ;; Variant of FpuRRI that modifies its `rd`, and so we name the
385       ;; input state `ri` (for "input") and constrain the two
386       ;; together.
387       (FpuRRIMod
388        (fpu_op FPUOpRIMod)
389        (rd WritableReg)
390        (ri Reg)
391        (rn Reg))
392
393
394       ;; 3-op FPU instruction.
395       ;; 16-bit scalars require half-precision floating-point support (FEAT_FP16).
396       (FpuRRRR
397        (fpu_op FPUOp3)
398        (size ScalarSize)
399        (rd WritableReg)
400        (rn Reg)
401        (rm Reg)
402        (ra Reg))
403
404       ;; FPU comparison.
405       (FpuCmp
406        (size ScalarSize)
407        (rn Reg)
408        (rm Reg))
409
410       ;; Floating-point load, half-precision (16 bit).
411       (FpuLoad16
412        (rd WritableReg)
413        (mem AMode)
414        (flags MemFlags))
415
416       ;; Floating-point store, half-precision (16 bit).
417       (FpuStore16
418        (rd Reg)
419        (mem AMode)
420        (flags MemFlags))
421
422       ;; Floating-point load, single-precision (32 bit).
423       (FpuLoad32
424        (rd WritableReg)
425        (mem AMode)
426        (flags MemFlags))
427
428       ;; Floating-point store, single-precision (32 bit).
429       (FpuStore32
430        (rd Reg)
431        (mem AMode)
432        (flags MemFlags))
433
434       ;; Floating-point load, double-precision (64 bit).
435       (FpuLoad64
436        (rd WritableReg)
437        (mem AMode)
438        (flags MemFlags))
439
440       ;; Floating-point store, double-precision (64 bit).
441       (FpuStore64
442        (rd Reg)
443        (mem AMode)
444        (flags MemFlags))
445
446       ;; Floating-point/vector load, 128 bit.
447       (FpuLoad128
448        (rd WritableReg)
449        (mem AMode)
450        (flags MemFlags))
451
452       ;; Floating-point/vector store, 128 bit.
453       (FpuStore128
454        (rd Reg)
455        (mem AMode)
456        (flags MemFlags))
457
458       ;; A load of a pair of floating-point registers, double precision (64-bit).
459       (FpuLoadP64
460        (rt WritableReg)
461        (rt2 WritableReg)
462        (mem PairAMode)
463        (flags MemFlags))
464
465       ;; A store of a pair of floating-point registers, double precision (64-bit).
466       (FpuStoreP64
467        (rt Reg)
468        (rt2 Reg)
469        (mem PairAMode)
470        (flags MemFlags))
471
472       ;; A load of a pair of floating-point registers, 128-bit.
473       (FpuLoadP128
474        (rt WritableReg)
475        (rt2 WritableReg)
476        (mem PairAMode)
477        (flags MemFlags))
478
479       ;; A store of a pair of floating-point registers, 128-bit.
480       (FpuStoreP128
481        (rt Reg)
482        (rt2 Reg)
483        (mem PairAMode)
484        (flags MemFlags))
485
486       ;; Conversion: FP -> integer.
487       (FpuToInt
488        (op FpuToIntOp)
489        (rd WritableReg)
490        (rn Reg))
491
492       ;; Conversion: integer -> FP.
493       (IntToFpu
494        (op IntToFpuOp)
495        (rd WritableReg)
496        (rn Reg))
497
498       ;; FP conditional select, 16 bit.
499       ;; Requires FEAT_FP16.
500       (FpuCSel16
501        (rd WritableReg)
502        (rn Reg)
503        (rm Reg)
504        (cond Cond))
505
506       ;; FP conditional select, 32 bit.
507       (FpuCSel32
508        (rd WritableReg)
509        (rn Reg)
510        (rm Reg)
511        (cond Cond))
512
513       ;; FP conditional select, 64 bit.
514       (FpuCSel64
515        (rd WritableReg)
516        (rn Reg)
517        (rm Reg)
518        (cond Cond))
519
520       ;; Round to integer.
521       (FpuRound
522        (op FpuRoundMode)
523        (rd WritableReg)
524        (rn Reg))
525
526       ;; Move from a GPR to a vector register.  The scalar value is parked in the lowest lane
527       ;; of the destination, and all other lanes are zeroed out. Currently 16-, 32- and 64-bit
528       ;; transactions are supported. 16-bit moves require FEAT_FP16.
529       (MovToFpu
530        (rd WritableReg)
531        (rn Reg)
532        (size ScalarSize))
533
534       ;; Loads a floating-point immediate.
535       (FpuMoveFPImm
536        (rd WritableReg)
537        (imm ASIMDFPModImm)
538        (size ScalarSize))
539
540       ;; Move to a vector element from a GPR.
541       (MovToVec
542        (rd WritableReg)
543        (ri Reg)
544        (rn Reg)
545        (idx u8)
546        (size VectorSize))
547
548       ;; Unsigned move from a vector element to a GPR.
549       (MovFromVec
550        (rd WritableReg)
551        (rn Reg)
552        (idx u8)
553        (size ScalarSize))
554
555       ;; Signed move from a vector element to a GPR.
556       (MovFromVecSigned
557        (rd WritableReg)
558        (rn Reg)
559        (idx u8)
560        (size VectorSize)
561        (scalar_size OperandSize))
562
563       ;; Duplicate general-purpose register to vector.
564       (VecDup
565        (rd WritableReg)
566        (rn Reg)
567        (size VectorSize))
568
569       ;; Duplicate scalar to vector.
570       (VecDupFromFpu
571        (rd WritableReg)
572        (rn Reg)
573        (size VectorSize)
574        (lane u8))
575
576       ;; Duplicate FP immediate to vector.
577       (VecDupFPImm
578        (rd WritableReg)
579        (imm ASIMDFPModImm)
580        (size VectorSize))
581
582       ;; Duplicate immediate to vector.
583       (VecDupImm
584        (rd WritableReg)
585        (imm ASIMDMovModImm)
586        (invert bool)
587        (size VectorSize))
588
589       ;; Vector extend.
590       (VecExtend
591        (t VecExtendOp)
592        (rd WritableReg)
593        (rn Reg)
594        (high_half bool)
595        (lane_size ScalarSize))
596
597       ;; Move vector element to another vector element.
598       (VecMovElement
599        (rd WritableReg)
600        (ri Reg)
601        (rn Reg)
602        (dest_idx u8)
603        (src_idx u8)
604        (size VectorSize))
605
606       ;; Vector widening operation.
607       (VecRRLong
608        (op VecRRLongOp)
609        (rd WritableReg)
610        (rn Reg)
611        (high_half bool))
612
613       ;; Vector narrowing operation -- low half.
614       (VecRRNarrowLow
615        (op VecRRNarrowOp)
616        (rd WritableReg)
617        (rn Reg)
618        (lane_size ScalarSize))
619
620       ;; Vector narrowing operation -- high half.
621       (VecRRNarrowHigh
622        (op VecRRNarrowOp)
623        (rd WritableReg)
624        (ri Reg)
625        (rn Reg)
626        (lane_size ScalarSize))
627
628       ;; 1-operand vector instruction that operates on a pair of elements.
629       (VecRRPair
630        (op VecPairOp)
631        (rd WritableReg)
632        (rn Reg))
633
634       ;; 2-operand vector instruction that produces a result with twice the
635       ;; lane width and half the number of lanes.
636       (VecRRRLong
637        (alu_op VecRRRLongOp)
638        (rd WritableReg)
639        (rn Reg)
640        (rm Reg)
641        (high_half bool))
642
643       ;; 2-operand vector instruction that produces a result with
644       ;; twice the lane width and half the number of lanes. Variant
645       ;; that modifies `rd` (so takes its initial state as `ri`).
646       (VecRRRLongMod
647        (alu_op VecRRRLongModOp)
648        (rd WritableReg)
649        (ri Reg)
650        (rn Reg)
651        (rm Reg)
652        (high_half bool))
653
654       ;; 1-operand vector instruction that extends elements of the input
655       ;; register and operates on a pair of elements. The output lane width
656       ;; is double that of the input.
657       (VecRRPairLong
658        (op VecRRPairLongOp)
659        (rd WritableReg)
660        (rn Reg))
661
662       ;; A vector ALU op.
663       (VecRRR
664        (alu_op VecALUOp)
665        (rd WritableReg)
666        (rn Reg)
667        (rm Reg)
668        (size VectorSize))
669
670       ;; A vector ALU op modifying a source register.
671       (VecRRRMod
672        (alu_op VecALUModOp)
673        (rd WritableReg)
674        (ri Reg)
675        (rn Reg)
676        (rm Reg)
677        (size VectorSize))
678
679       ;; A vector ALU op modifying a source register.
680       (VecFmlaElem
681        (alu_op VecALUModOp)
682        (rd WritableReg)
683        (ri Reg)
684        (rn Reg)
685        (rm Reg)
686        (size VectorSize)
687        (idx u8))
688
689       ;; Vector two register miscellaneous instruction.
690       (VecMisc
691        (op VecMisc2)
692        (rd WritableReg)
693        (rn Reg)
694        (size VectorSize))
695
696       ;; Vector instruction across lanes.
697       (VecLanes
698        (op VecLanesOp)
699        (rd WritableReg)
700        (rn Reg)
701        (size VectorSize))
702
703       ;; Vector shift by immediate Shift Left (immediate), Unsigned Shift Right (immediate)
704       ;; Signed Shift Right (immediate).  These are somewhat unusual in that, for right shifts,
705       ;; the allowed range of `imm` values is 1 to lane-size-in-bits, inclusive.  A zero
706       ;; right-shift cannot be encoded.  Left shifts are "normal", though, having valid `imm`
707       ;; values from 0 to lane-size-in-bits - 1 inclusive.
708       (VecShiftImm
709        (op VecShiftImmOp)
710        (rd WritableReg)
711        (rn Reg)
712        (size VectorSize)
713        (imm u8))
714
715       ;; Destructive vector shift by immediate.
716       (VecShiftImmMod
717        (op VecShiftImmModOp)
718        (rd WritableReg)
719        (ri Reg)
720        (rn Reg)
721        (size VectorSize)
722        (imm u8))
723
724       ;; Vector extract - create a new vector, being the concatenation of the lowest `imm4` bytes
725       ;; of `rm` followed by the uppermost `16 - imm4` bytes of `rn`.
726       (VecExtract
727        (rd WritableReg)
728        (rn Reg)
729        (rm Reg)
730        (imm4 u8))
731
732       ;; Table vector lookup - single register table. The table
733       ;; consists of 8-bit elements and is stored in `rn`, while `rm`
734       ;; contains 8-bit element indices. This variant emits `TBL`,
735       ;; which sets elements that correspond to out-of-range indices
736       ;; (greater than 15) to 0.
737       (VecTbl
738        (rd WritableReg)
739        (rn Reg)
740        (rm Reg))
741
742       ;; Table vector lookup - single register table. The table
743       ;; consists of 8-bit elements and is stored in `rn`, while `rm`
744       ;; contains 8-bit element indices. This variant emits `TBX`,
745       ;; which leaves elements that correspond to out-of-range indices
746       ;; (greater than 15) unmodified. Hence, it takes an input vreg in
747       ;; `ri` that is constrained to the same allocation as `rd`.
748       (VecTblExt
749        (rd WritableReg)
750        (ri Reg)
751        (rn Reg)
752        (rm Reg))
753
754       ;; Table vector lookup - two register table. The table consists
755       ;; of 8-bit elements and is stored in `rn` and `rn2`, while
756       ;; `rm` contains 8-bit element indices. The table registers
757       ;; `rn` and `rn2` must have consecutive numbers modulo 32, that
758       ;; is v31 and v0 (in that order) are consecutive registers.
759       ;; This variant emits `TBL`, which sets out-of-range results to
760       ;; 0.
761       (VecTbl2
762        (rd WritableReg)
763        (rn Reg)
764        (rn2 Reg)
765        (rm Reg))
766
767       ;; Table vector lookup - two register table. The table consists
768       ;; of 8-bit elements and is stored in `rn` and `rn2`, while
769       ;; `rm` contains 8-bit element indices. The table registers
770       ;; `rn` and `rn2` must have consecutive numbers modulo 32, that
771       ;; is v31 and v0 (in that order) are consecutive registers.
772       ;; This variant emits `TBX`, which leaves out-of-range results
773       ;; unmodified, hence takes the initial state of the result
774       ;; register in vreg `ri`.
775       (VecTbl2Ext
776        (rd WritableReg)
777        (ri Reg)
778        (rn Reg)
779        (rn2 Reg)
780        (rm Reg))
781
782       ;; Load an element and replicate to all lanes of a vector.
783       (VecLoadReplicate
784        (rd WritableReg)
785        (rn Reg)
786        (size VectorSize)
787        (flags MemFlags))
788
789       ;; Vector conditional select, 128 bit.  A synthetic instruction, which generates a 4-insn
790       ;; control-flow diamond.
791       (VecCSel
792        (rd WritableReg)
793        (rn Reg)
794        (rm Reg)
795        (cond Cond))
796
797       ;; Move to the NZCV flags (actually a `MSR NZCV, Xn` insn).
798       (MovToNZCV
799        (rn Reg))
800
801       ;; Move from the NZCV flags (actually a `MRS Xn, NZCV` insn).
802       (MovFromNZCV
803        (rd WritableReg))
804
805       ;; A machine call instruction. N.B.: this allows only a +/- 128MB offset (it uses a relocation
806       ;; of type `Reloc::Arm64Call`); if the destination distance is not `RelocDistance::Near`, the
807       ;; code should use a `LoadExtName` / `CallInd` sequence instead, allowing an arbitrary 64-bit
808       ;; target.
809       (Call (info BoxCallInfo))
810
811       ;; A machine indirect-call instruction.
812       (CallInd (info BoxCallIndInfo))
813
814       ;; A return-call macro instruction.
815       (ReturnCall (info BoxReturnCallInfo))
816
817       ;; An indirect return-call macro instruction.
818       (ReturnCallInd (info BoxReturnCallIndInfo))
819
820       ;; A pseudo-instruction that captures register arguments in vregs.
821       (Args
822        (args VecArgPair))
823
824       ;; A pseudo-instruction that moves vregs to return registers.
825       (Rets
826        (rets VecRetPair))
827
828       ;; ---- branches (exactly one must appear at end of BB) ----
829
830       ;; A machine return instruction.
831       (Ret)
832
833       ;; A machine return instruction with pointer authentication using SP as the
834       ;; modifier. This instruction requires pointer authentication support
835       ;; (FEAT_PAuth) unless `is_hint` is true, in which case it is equivalent to
836       ;; the combination of a no-op and a return instruction on platforms without
837       ;; the relevant support.
838       (AuthenticatedRet
839        (key APIKey)
840        (is_hint bool))
841
842       ;; An unconditional branch.
843       (Jump
844        (dest BranchTarget))
845
846       ;; A conditional branch. Contains two targets; at emission time, both are emitted, but
847       ;; the MachBuffer knows to truncate the trailing branch if fallthrough. We optimize the
848       ;; choice of taken/not_taken (inverting the branch polarity as needed) based on the
849       ;; fallthrough at the time of lowering.
850       (CondBr
851        (taken BranchTarget)
852        (not_taken BranchTarget)
853        (kind CondBrKind))
854
855       ;; A conditional branch which tests the `bit` of `rn` and branches
856       ;; depending on `kind`.
857       (TestBitAndBranch
858        (kind TestBitAndBranchKind)
859        (taken BranchTarget)
860        (not_taken BranchTarget)
861        (rn Reg)
862        (bit u8))
863
864       ;; A conditional trap: execute a `udf` if the condition is true. This is
865       ;; one VCode instruction because it uses embedded control flow; it is
866       ;; logically a single-in, single-out region, but needs to appear as one
867       ;; unit to the register allocator.
868       ;;
869       ;; The `CondBrKind` gives the conditional-branch condition that will
870       ;; *execute* the embedded `Inst`. (In the emitted code, we use the inverse
871       ;; of this condition in a branch that skips the trap instruction.)
872       (TrapIf
873        (kind CondBrKind)
874        (trap_code TrapCode))
875
876       ;; An indirect branch through a register, augmented with set of all
877       ;; possible successors.
878       (IndirectBr
879        (rn Reg)
880        (targets VecMachLabel))
881
882       ;; A "break" instruction, used for e.g. traps and debug breakpoints.
883       (Brk)
884
885       ;; An instruction guaranteed to always be undefined and to trigger an illegal instruction at
886       ;; runtime.
887       (Udf
888        (trap_code TrapCode))
889
890       ;; Compute the address (using a PC-relative offset) of a memory location, using the `ADR`
891       ;; instruction. Note that we take a simple offset, not a `MemLabel`, here, because `Adr` is
892       ;; only used for now in fixed lowering sequences with hardcoded offsets. In the future we may
893       ;; need full `MemLabel` support.
894       (Adr
895        (rd WritableReg)
896        ;; Offset in range -2^20 .. 2^20.
897        (off i32))
898
899       ;; Compute the address (using a PC-relative offset) of a 4KB page.
900       (Adrp
901        (rd WritableReg)
902        (off i32))
903
904       ;; Raw 32-bit word, used for inline constants and jump-table entries.
905       (Word4
906        (data u32))
907
908       ;; Raw 64-bit word, used for inline constants.
909       (Word8
910        (data u64))
911
912       ;; Jump-table sequence, as one compound instruction (see note in lower_inst.rs for rationale).
913       (JTSequence
914        (default MachLabel)
915        (targets BoxVecMachLabel)
916        (ridx Reg)
917        (rtmp1 WritableReg)
918        (rtmp2 WritableReg))
919
920       ;; Load an inline symbol reference.
921       (LoadExtNameGot
922        (rd WritableReg)
923        (name BoxExternalName))
924       (LoadExtNameNear
925        (rd WritableReg)
926        (name BoxExternalName)
927        (offset i64))
928       (LoadExtNameFar
929        (rd WritableReg)
930        (name BoxExternalName)
931        (offset i64))
932
933       ;; Load address referenced by `mem` into `rd`.
934       (LoadAddr
935        (rd WritableReg)
936        (mem AMode))
937
938       ;; Pointer authentication code for instruction address with modifier in SP;
939       ;; equivalent to a no-op if Pointer authentication (FEAT_PAuth) is not
940       ;; supported.
941       (Paci
942        (key APIKey))
943
944       ;; Strip pointer authentication code from instruction address in LR;
945       ;; equivalent to a no-op if Pointer authentication (FEAT_PAuth) is not
946       ;; supported.
947       (Xpaclri)
948
949       ;; Branch target identification; equivalent to a no-op if Branch Target
950       ;; Identification (FEAT_BTI) is not supported.
951       (Bti
952        (targets BranchTargetType))
953
954       ;; Meta-insn, no-op in generated code: emit constant/branch veneer island
955       ;; at this point (with a guard jump around it) if less than the needed
956       ;; space is available before the next branch deadline. See the `MachBuffer`
957       ;; implementation in `machinst/buffer.rs` for the overall algorithm. In
958       ;; brief, we retain a set of "pending/unresolved label references" from
959       ;; branches as we scan forward through instructions to emit machine code;
960       ;; if we notice we're about to go out of range on an unresolved reference,
961       ;; we stop, emit a bunch of "veneers" (branches in a form that has a longer
962       ;; range, e.g. a 26-bit-offset unconditional jump), and point the original
963       ;; label references to those. This is an "island" because it comes in the
964       ;; middle of the code.
965       ;;
966       ;; This meta-instruction is a necessary part of the logic that determines
967       ;; where to place islands. Ordinarily, we want to place them between basic
968       ;; blocks, so we compute the worst-case size of each block, and emit the
969       ;; island before starting a block if we would exceed a deadline before the
970       ;; end of the block. However, some sequences (such as an inline jumptable)
971       ;; are variable-length and not accounted for by this logic; so these
972       ;; lowered sequences include an `EmitIsland` to trigger island generation
973       ;; where necessary.
974       (EmitIsland
975        ;; The needed space before the next deadline.
976        (needed_space CodeOffset))
977
978       ;; A call to the `ElfTlsGetAddr` libcall. Returns address of TLS symbol in x0.
979       (ElfTlsGetAddr
980        (symbol BoxExternalName)
981        (rd WritableReg)
982        (tmp WritableReg))
983
984       (MachOTlsGetAddr
985        (symbol ExternalName)
986        (rd WritableReg))
987
988       ;; An unwind pseudo-instruction.
989       (Unwind
990        (inst UnwindInst))
991
992       ;; A dummy use, useful to keep a value alive.
993       (DummyUse
994        (reg Reg))
995
996       ;; A pseudoinstruction that loads the address of a label.
997       (LabelAddress (dst WritableReg)
998                     (label MachLabel))
999
1000       ;; A pseudoinstruction that serves as a sequence point.
1001       (SequencePoint)
1002
1003       ;; Emits an inline stack probe loop.
1004       ;;
1005       ;; Note that this is emitted post-regalloc so `start` and `end` can be
1006       ;; temporary registers such as the spilltmp and tmp2 registers. This also
1007       ;; means that the internal codegen can't use these registers.
1008       (StackProbeLoop (start WritableReg)
1009                       (end Reg)
1010                       (step Imm12))))
1011
1012(model ALUOp (enum
1013      (Add #x00) ;; 0
1014      (Sub #x01)
1015      (Orr #x02)
1016      (OrrNot #x03)
1017      (And #x04)
1018      (AndNot #x05)
1019      (Eor #x06)
1020      (EorNot #x07)
1021      (SubS #x08)
1022      (SDiv #x09)
1023      (UDiv #x0a)
1024      (Extr #x0b)
1025      (Lsr #x0c)
1026      (Asr #x0d)
1027      (Lsl #x0e)))
1028
1029;; An ALU operation. This can be paired with several instruction formats
1030;; below (see `Inst`) in any combination.
1031(type ALUOp
1032  (enum
1033    (Add)
1034    (Sub)
1035    (Orr)
1036    (OrrNot)
1037    (And)
1038    (AndS)
1039    (AndNot)
1040    ;; XOR (AArch64 calls this "EOR")
1041    (Eor)
1042    ;; XNOR (AArch64 calls this "EOR-NOT")
1043    (EorNot)
1044    ;; Add, setting flags
1045    (AddS)
1046    ;; Sub, setting flags
1047    (SubS)
1048    ;; Signed multiply, high-word result
1049    (SMulH)
1050    ;; Unsigned multiply, high-word result
1051    (UMulH)
1052    (SDiv)
1053    (UDiv)
1054    (Extr)
1055    (Lsr)
1056    (Asr)
1057    (Lsl)
1058    ;; Add with carry
1059    (Adc)
1060    ;; Add with carry, settings flags
1061    (AdcS)
1062    ;; Subtract with carry
1063    (Sbc)
1064    ;; Subtract with carry, settings flags
1065    (SbcS)
1066))
1067
1068;; An ALU operation with three arguments.
1069(type ALUOp3
1070  (enum
1071    ;; Multiply-add
1072    (MAdd)
1073    ;; Multiply-sub
1074    (MSub)
1075    ;; Unsigned-Multiply-add
1076    (UMAddL)
1077    ;; Signed-Multiply-add
1078    (SMAddL)
1079))
1080
1081(type MoveWideOp
1082  (enum
1083    (MovZ)
1084    (MovN)
1085))
1086
1087(type UImm5 (primitive UImm5))
1088(model Imm12 (type (bv 24)))
1089(type Imm12 (primitive Imm12))
1090(model ImmLogic (type (bv 64)))
1091(type ImmLogic (primitive ImmLogic))
1092(model ImmShift (type (bv 6)))
1093(type ImmShift (primitive ImmShift))
1094(model ShiftOpAndAmt (type (bv 16)))
1095(type ShiftOpAndAmt (primitive ShiftOpAndAmt))
1096(model MoveWideConst (type (bv 16)))
1097(type MoveWideConst (primitive MoveWideConst))
1098(type NZCV (primitive NZCV))
1099(type ASIMDFPModImm (primitive ASIMDFPModImm))
1100(type ASIMDMovModImm (primitive ASIMDMovModImm))
1101(type SImm7Scaled (primitive SImm7Scaled))
1102
1103(type BoxCallInfo (primitive BoxCallInfo))
1104(type BoxCallIndInfo (primitive BoxCallIndInfo))
1105(type BoxReturnCallInfo (primitive BoxReturnCallInfo))
1106(type BoxReturnCallIndInfo (primitive BoxReturnCallIndInfo))
1107(type CondBrKind (primitive CondBrKind))
1108(type BranchTarget (primitive BranchTarget))
1109(type BoxJTSequenceInfo (primitive BoxJTSequenceInfo))
1110(type CodeOffset (primitive CodeOffset))
1111(type VecMachLabel extern (enum))
1112
1113(model ExtendOp (enum
1114    (UXTB #b000)
1115    (UXTH #b001)
1116    (UXTW #b010)
1117    (UXTX #b011)
1118    (SXTB #b100)
1119    (SXTH #b101)
1120    (SXTW #b110)
1121    (SXTX #b111)
1122))
1123
1124(type ExtendOp extern
1125  (enum
1126    (UXTB)
1127    (UXTH)
1128    (UXTW)
1129    (UXTX)
1130    (SXTB)
1131    (SXTH)
1132    (SXTW)
1133    (SXTX)
1134))
1135
1136;; An operation on the bits of a register. This can be paired with several instruction formats
1137;; below (see `Inst`) in any combination.
1138(type BitOp
1139  (enum
1140    ;; Bit reverse
1141    (RBit)
1142    (Clz)
1143    (Cls)
1144    ;; Byte reverse
1145    (Rev16)
1146    (Rev32)
1147    (Rev64)
1148))
1149
1150(type MemLabel extern (enum))
1151(type SImm9 extern (enum))
1152(type UImm12Scaled extern (enum))
1153
1154;; An addressing mode specified for a load/store operation.
1155(type AMode
1156      (enum
1157        ;;
1158        ;; Real ARM64 addressing modes:
1159        ;;
1160        ;; "post-indexed" mode as per AArch64 docs: postincrement reg after
1161        ;; address computation.
1162        ;; Specialized here to SP so we don't have to emit regalloc metadata.
1163        (SPPostIndexed
1164         (simm9 SImm9))
1165
1166        ;; "pre-indexed" mode as per AArch64 docs: preincrement reg before
1167        ;; address computation.
1168        ;; Specialized here to SP so we don't have to emit regalloc metadata.
1169        (SPPreIndexed
1170         (simm9 SImm9))
1171
1172        ;; N.B.: RegReg, RegScaled, and RegScaledExtended all correspond to
1173        ;; what the ISA calls the "register offset" addressing mode. We split
1174        ;; out several options here for more ergonomic codegen.
1175        ;;
1176        ;; Register plus register offset.
1177        (RegReg
1178         (rn Reg)
1179         (rm Reg))
1180
1181        ;; Register plus register offset, scaled by type's size.
1182        (RegScaled
1183         (rn Reg)
1184         (rm Reg))
1185
1186        ;; Register plus register offset, scaled by type's size, with index
1187        ;; sign- or zero-extended first.
1188        (RegScaledExtended
1189         (rn Reg)
1190         (rm Reg)
1191         (extendop ExtendOp))
1192
1193        ;; Register plus register offset, with index sign- or zero-extended
1194        ;; first.
1195        (RegExtended
1196         (rn Reg)
1197         (rm Reg)
1198         (extendop ExtendOp))
1199
1200        ;; Unscaled signed 9-bit immediate offset from reg.
1201        (Unscaled
1202         (rn Reg)
1203         (simm9 SImm9))
1204
1205        ;; Scaled (by size of a type) unsigned 12-bit immediate offset from reg.
1206        (UnsignedOffset
1207         (rn Reg)
1208         (uimm12 UImm12Scaled))
1209
1210        ;; virtual addressing modes that are lowered at emission time:
1211        ;;
1212        ;; Reference to a "label": e.g., a symbol.
1213        (Label
1214         (label MemLabel))
1215
1216        ;; Arbitrary offset from a register. Converted to generation of large
1217        ;; offsets with multiple instructions as necessary during code emission.
1218        (RegOffset
1219         (rn Reg)
1220         (off i64))
1221
1222        ;; Offset from the stack pointer.
1223        (SPOffset
1224         (off i64))
1225
1226        ;; Offset from the frame pointer.
1227        (FPOffset
1228         (off i64))
1229
1230        ;; A reference to a constant which is placed outside of the function's
1231        ;; body, typically at the end.
1232        (Const
1233          (addr VCodeConstant))
1234
1235        ;; Offset from the beginning of the argument area to the argument
1236        ;; referenced. This can only be determined when the function has been
1237        ;; processed fully, as the size of the argument area after the prologue
1238        ;; is only known once all return_call instructions in the function body
1239        ;; have been processed.
1240        (IncomingArg
1241          (off i64))
1242
1243        ;; Offset into the slot area of the stack, which lies just above the
1244        ;; outgoing argument area that's setup by the function prologue.
1245        ;; At emission time, this is converted to `SPOffset` with a fixup added to
1246        ;; the offset constant. The fixup is a running value that is tracked as
1247        ;; emission iterates through instructions in linear order, and can be
1248        ;; adjusted up and down with [Inst::VirtualSPOffsetAdj].
1249        ;;
1250        ;; The standard ABI is in charge of handling this (by emitting the
1251        ;; adjustment meta-instructions). See the diagram in the documentation
1252        ;; for [crate::isa::aarch64::abi](the ABI module) for more details.
1253        (SlotOffset
1254         (off i64))))
1255
1256;; A memory argument to a load/store-pair.
1257(type PairAMode (enum
1258  ;; Signed, scaled 7-bit offset from a register.
1259  (SignedOffset
1260    (reg Reg)
1261    (simm7 SImm7Scaled))
1262
1263  ;; Pre-increment register before address computation.
1264  (SPPreIndexed (simm7 SImm7Scaled))
1265
1266  ;; Post-increment register after address computation.
1267  (SPPostIndexed (simm7 SImm7Scaled))
1268))
1269
1270(type FPUOpRI extern (enum))
1271(type FPUOpRIMod extern (enum))
1272
1273(model OperandSize
1274      (enum (Size32 32)
1275            (Size64 64)))
1276
1277(type OperandSize extern
1278      (enum Size32
1279            Size64))
1280
1281(type TestBitAndBranchKind (enum (Z) (NZ)))
1282
1283;; Helper for calculating the `OperandSize` corresponding to a type
1284(spec (operand_size ty)
1285      (provide
1286            (= result (if (<= ty 32) 32 64)))
1287      (require
1288            (or (= ty 8) (= ty 16) (= ty 32) (= ty 64))))
1289(instantiate operand_size
1290    ((args Int) (ret Int) (canon (bv 8)))
1291    ((args Int) (ret Int) (canon (bv 16)))
1292    ((args Int) (ret Int) (canon (bv 32)))
1293    ((args Int) (ret Int) (canon (bv 64)))
1294)
1295(decl operand_size (Type) OperandSize)
1296(rule operand_size_32 1 (operand_size (fits_in_32 _ty)) (OperandSize.Size32))
1297(rule operand_size_64 (operand_size (fits_in_64 _ty)) (OperandSize.Size64))
1298
1299(model ScalarSize
1300      (enum (Size8 8)
1301            (Size16 16)
1302            (Size32 32)
1303            (Size64 64)
1304            (Size128 128)))
1305
1306;; Difference (32 - ty), useful for narrow calculations with 32-bit
1307;; instructions.
1308(decl diff_from_32 (Type) u8)
1309(rule (diff_from_32 $I8) 24)
1310(rule (diff_from_32 $I16) 16)
1311
1312(type ScalarSize extern
1313      (enum Size8
1314            Size16
1315            Size32
1316            Size64
1317            Size128))
1318
1319;; Helper for calculating the `ScalarSize` corresponding to a type
1320(decl scalar_size (Type) ScalarSize)
1321
1322(rule (scalar_size $I8) (ScalarSize.Size8))
1323(rule (scalar_size $I16) (ScalarSize.Size16))
1324(rule (scalar_size $I32) (ScalarSize.Size32))
1325(rule (scalar_size $I64) (ScalarSize.Size64))
1326(rule (scalar_size $I128) (ScalarSize.Size128))
1327
1328(rule (scalar_size $F32) (ScalarSize.Size32))
1329(rule (scalar_size $F64) (ScalarSize.Size64))
1330
1331;; Helper for calculating the `ScalarSize` lane type from vector type
1332(decl lane_size (Type) ScalarSize)
1333(rule 1 (lane_size (multi_lane 8 _)) (ScalarSize.Size8))
1334(rule 1 (lane_size (multi_lane 16 _)) (ScalarSize.Size16))
1335(rule 1 (lane_size (multi_lane 32 _)) (ScalarSize.Size32))
1336(rule 1 (lane_size (multi_lane 64 _)) (ScalarSize.Size64))
1337(rule (lane_size (dynamic_lane 8 _)) (ScalarSize.Size8))
1338(rule (lane_size (dynamic_lane 16 _)) (ScalarSize.Size16))
1339(rule (lane_size (dynamic_lane 32 _)) (ScalarSize.Size32))
1340(rule (lane_size (dynamic_lane 64 _)) (ScalarSize.Size64))
1341
1342;; Helper for extracting the size of a lane from the input `VectorSize`
1343(decl pure vector_lane_size (VectorSize) ScalarSize)
1344(rule (vector_lane_size (VectorSize.Size8x16)) (ScalarSize.Size8))
1345(rule (vector_lane_size (VectorSize.Size8x8))  (ScalarSize.Size8))
1346(rule (vector_lane_size (VectorSize.Size16x8)) (ScalarSize.Size16))
1347(rule (vector_lane_size (VectorSize.Size16x4)) (ScalarSize.Size16))
1348(rule (vector_lane_size (VectorSize.Size32x4)) (ScalarSize.Size32))
1349(rule (vector_lane_size (VectorSize.Size32x2)) (ScalarSize.Size32))
1350(rule (vector_lane_size (VectorSize.Size64x2)) (ScalarSize.Size64))
1351
1352(model Cond
1353      (enum (Lo #x03)
1354            (Hi #x08)
1355            (Lt #x0b)
1356            (Gt #x0c)))
1357
1358(type Cond extern
1359  (enum
1360    (Eq)
1361    (Ne)
1362    (Hs)
1363    (Lo)
1364    (Mi)
1365    (Pl)
1366    (Vs)
1367    (Vc)
1368    (Hi)
1369    (Ls)
1370    (Ge)
1371    (Lt)
1372    (Gt)
1373    (Le)
1374    (Al)
1375    (Nv)
1376))
1377
1378(model VectorSize
1379  (enum
1380    (Size8x8 #x00)
1381    (Size8x16 #x01)
1382    (Size16x4 #x02)
1383    (Size16x8 #x03)
1384    (Size32x2 #x04)
1385    (Size32x4 #x05)
1386    (Size64x2 #x06)))
1387
1388(type VectorSize extern
1389  (enum
1390    (Size8x8)
1391    (Size8x16)
1392    (Size16x4)
1393    (Size16x8)
1394    (Size32x2)
1395    (Size32x4)
1396    (Size64x2)
1397))
1398
1399;; Helper for calculating the `VectorSize` corresponding to a type
1400(decl vector_size (Type) VectorSize)
1401(rule 1 (vector_size (multi_lane 8 8)) (VectorSize.Size8x8))
1402(rule 1 (vector_size (multi_lane 8 16)) (VectorSize.Size8x16))
1403(rule 1 (vector_size (multi_lane 16 4)) (VectorSize.Size16x4))
1404(rule 1 (vector_size (multi_lane 16 8)) (VectorSize.Size16x8))
1405(rule 1 (vector_size (multi_lane 32 2)) (VectorSize.Size32x2))
1406(rule 1 (vector_size (multi_lane 32 4)) (VectorSize.Size32x4))
1407(rule 1 (vector_size (multi_lane 64 2)) (VectorSize.Size64x2))
1408(rule (vector_size (dynamic_lane 8 8)) (VectorSize.Size8x8))
1409(rule (vector_size (dynamic_lane 8 16)) (VectorSize.Size8x16))
1410(rule (vector_size (dynamic_lane 16 4)) (VectorSize.Size16x4))
1411(rule (vector_size (dynamic_lane 16 8)) (VectorSize.Size16x8))
1412(rule (vector_size (dynamic_lane 32 2)) (VectorSize.Size32x2))
1413(rule (vector_size (dynamic_lane 32 4)) (VectorSize.Size32x4))
1414(rule (vector_size (dynamic_lane 64 2)) (VectorSize.Size64x2))
1415
1416;; Helper for converting the `ScalarSize` of a float value to the corresponding `VectorSize`
1417(decl float_vector_size_in_64 (Type) VectorSize)
1418(rule (float_vector_size_in_64 $F16) (VectorSize.Size16x4))
1419(rule (float_vector_size_in_64 $F32) (VectorSize.Size32x2))
1420(rule (float_vector_size_in_64 $F64) (VectorSize.Size8x8))
1421
1422;; A floating-point unit (FPU) operation with one arg.
1423(type FPUOp1
1424  (enum
1425    (Abs)
1426    (Neg)
1427    (Sqrt)
1428    (Cvt32To64)
1429    (Cvt64To32)
1430))
1431
1432;; A floating-point unit (FPU) operation with two args.
1433(type FPUOp2
1434  (enum
1435    (Add)
1436    (Sub)
1437    (Mul)
1438    (Div)
1439    (Max)
1440    (Min)
1441))
1442
1443;; A floating-point unit (FPU) operation with three args.
1444(type FPUOp3
1445  (enum
1446    ;; Multiply-add
1447    (MAdd)
1448    ;; Multiply-sub
1449    (MSub)
1450    ;; Negated fused Multiply-add
1451    (NMAdd)
1452    ;; Negated fused Multiply-sub
1453    (NMSub)
1454))
1455
1456;; A conversion from an FP to an integer value.
1457(type FpuToIntOp
1458  (enum
1459    (F32ToU32)
1460    (F32ToI32)
1461    (F32ToU64)
1462    (F32ToI64)
1463    (F64ToU32)
1464    (F64ToI32)
1465    (F64ToU64)
1466    (F64ToI64)
1467))
1468
1469;; A conversion from an integer to an FP value.
1470(type IntToFpuOp
1471  (enum
1472    (U32ToF32)
1473    (I32ToF32)
1474    (U32ToF64)
1475    (I32ToF64)
1476    (U64ToF32)
1477    (I64ToF32)
1478    (U64ToF64)
1479    (I64ToF64)
1480))
1481
1482;; Modes for FP rounding ops: round down (floor) or up (ceil), or toward zero (trunc), or to
1483;; nearest, and for 32- or 64-bit FP values.
1484(type FpuRoundMode
1485  (enum
1486    (Minus32)
1487    (Minus64)
1488    (Plus32)
1489    (Plus64)
1490    (Zero32)
1491    (Zero64)
1492    (Nearest32)
1493    (Nearest64)
1494))
1495
1496;; Type of vector element extensions.
1497(type VecExtendOp
1498  (enum
1499    ;; Signed extension
1500    (Sxtl)
1501    ;; Unsigned extension
1502    (Uxtl)
1503))
1504
1505;; A vector ALU operation.
1506(type VecALUOp
1507  (enum
1508    ;; Signed saturating add
1509    (Sqadd)
1510    ;; Unsigned saturating add
1511    (Uqadd)
1512    ;; Signed saturating subtract
1513    (Sqsub)
1514    ;; Unsigned saturating subtract
1515    (Uqsub)
1516    ;; Compare bitwise equal
1517    (Cmeq)
1518    ;; Compare signed greater than or equal
1519    (Cmge)
1520    ;; Compare signed greater than
1521    (Cmgt)
1522    ;; Compare unsigned higher
1523    (Cmhs)
1524    ;; Compare unsigned higher or same
1525    (Cmhi)
1526    ;; Floating-point compare equal
1527    (Fcmeq)
1528    ;; Floating-point compare greater than
1529    (Fcmgt)
1530    ;; Floating-point compare greater than or equal
1531    (Fcmge)
1532    ;; Bitwise and
1533    (And)
1534    ;; Bitwise bit clear
1535    (Bic)
1536    ;; Bitwise inclusive or
1537    (Orr)
1538    ;; Bitwise inclusive or not
1539    (Orn)
1540    ;; Bitwise exclusive or
1541    (Eor)
1542    ;; Unsigned maximum pairwise
1543    (Umaxp)
1544    ;; Add
1545    (Add)
1546    ;; Subtract
1547    (Sub)
1548    ;; Multiply
1549    (Mul)
1550    ;; Signed shift left
1551    (Sshl)
1552    ;; Unsigned shift left
1553    (Ushl)
1554    ;; Unsigned minimum
1555    (Umin)
1556    ;; Signed minimum
1557    (Smin)
1558    ;; Unsigned maximum
1559    (Umax)
1560    ;; Signed maximum
1561    (Smax)
1562    ;; Unsigned rounding halving add
1563    (Urhadd)
1564    ;; Floating-point add
1565    (Fadd)
1566    ;; Floating-point subtract
1567    (Fsub)
1568    ;; Floating-point divide
1569    (Fdiv)
1570    ;; Floating-point maximum
1571    (Fmax)
1572    ;; Floating-point minimum
1573    (Fmin)
1574    ;; Floating-point multiply
1575    (Fmul)
1576    ;; Add pairwise
1577    (Addp)
1578    ;; Zip vectors (primary) [meaning, high halves]
1579    (Zip1)
1580    ;; Zip vectors (secondary)
1581    (Zip2)
1582    ;; Signed saturating rounding doubling multiply returning high half
1583    (Sqrdmulh)
1584    ;; Unzip vectors (primary)
1585    (Uzp1)
1586    ;; Unzip vectors (secondary)
1587    (Uzp2)
1588    ;; Transpose vectors (primary)
1589    (Trn1)
1590    ;; Transpose vectors (secondary)
1591    (Trn2)
1592))
1593
1594;; A Vector ALU operation which modifies a source register.
1595(type VecALUModOp
1596  (enum
1597    ;; Bitwise select
1598    (Bsl)
1599    ;; Floating-point fused multiply-add vectors
1600    (Fmla)
1601    ;; Floating-point fused multiply-subtract vectors
1602    (Fmls)
1603))
1604
1605;; A Vector miscellaneous operation with two registers.
1606(type VecMisc2
1607  (enum
1608    ;; Bitwise NOT
1609    (Not)
1610    ;; Negate
1611    (Neg)
1612    ;; Absolute value
1613    (Abs)
1614    ;; Floating-point absolute value
1615    (Fabs)
1616    ;; Floating-point negate
1617    (Fneg)
1618    ;; Floating-point square root
1619    (Fsqrt)
1620    ;; Reverse elements in 16-bit lanes
1621    (Rev16)
1622    ;; Reverse elements in 32-bit lanes
1623    (Rev32)
1624    ;; Reverse elements in 64-bit doublewords
1625    (Rev64)
1626    ;; Floating-point convert to signed integer, rounding toward zero
1627    (Fcvtzs)
1628    ;; Floating-point convert to unsigned integer, rounding toward zero
1629    (Fcvtzu)
1630    ;; Signed integer convert to floating-point
1631    (Scvtf)
1632    ;; Unsigned integer convert to floating-point
1633    (Ucvtf)
1634    ;; Floating point round to integral, rounding towards nearest
1635    (Frintn)
1636    ;; Floating point round to integral, rounding towards zero
1637    (Frintz)
1638    ;; Floating point round to integral, rounding towards minus infinity
1639    (Frintm)
1640    ;; Floating point round to integral, rounding towards plus infinity
1641    (Frintp)
1642    ;; Population count per byte
1643    (Cnt)
1644    ;; Compare bitwise equal to 0
1645    (Cmeq0)
1646    ;; Compare signed greater than or equal to 0
1647    (Cmge0)
1648    ;; Compare signed greater than 0
1649    (Cmgt0)
1650    ;; Compare signed less than or equal to 0
1651    (Cmle0)
1652    ;; Compare signed less than 0
1653    (Cmlt0)
1654    ;; Floating point compare equal to 0
1655    (Fcmeq0)
1656    ;; Floating point compare greater than or equal to 0
1657    (Fcmge0)
1658    ;; Floating point compare greater than 0
1659    (Fcmgt0)
1660    ;; Floating point compare less than or equal to 0
1661    (Fcmle0)
1662    ;; Floating point compare less than 0
1663    (Fcmlt0)
1664))
1665
1666;; A vector widening operation with one argument.
1667(type VecRRLongOp
1668  (enum
1669    ;; Floating-point convert to higher precision long, 16-bit elements
1670    (Fcvtl16)
1671    ;; Floating-point convert to higher precision long, 32-bit elements
1672    (Fcvtl32)
1673    ;; Shift left long (by element size), 8-bit elements
1674    (Shll8)
1675    ;; Shift left long (by element size), 16-bit elements
1676    (Shll16)
1677    ;; Shift left long (by element size), 32-bit elements
1678    (Shll32)
1679))
1680
1681;; A vector narrowing operation with one argument.
1682(type VecRRNarrowOp
1683  (enum
1684    ;; Extract narrow.
1685    (Xtn)
1686    ;; Signed saturating extract narrow.
1687    (Sqxtn)
1688    ;; Signed saturating extract unsigned narrow.
1689    (Sqxtun)
1690    ;; Unsigned saturating extract narrow.
1691    (Uqxtn)
1692    ;; Floating-point convert to lower precision narrow.
1693    (Fcvtn)
1694))
1695
1696(type VecRRRLongOp
1697  (enum
1698    ;; Signed multiply long.
1699    (Smull8)
1700    (Smull16)
1701    (Smull32)
1702    ;; Unsigned multiply long.
1703    (Umull8)
1704    (Umull16)
1705    (Umull32)
1706))
1707
1708(type VecRRRLongModOp
1709  (enum
1710    ;; Unsigned multiply add long
1711    (Umlal8)
1712    (Umlal16)
1713    (Umlal32)
1714))
1715
1716;; A vector operation on a pair of elements with one register.
1717(type VecPairOp
1718  (enum
1719    ;; Add pair of elements
1720    (Addp)
1721))
1722
1723;; 1-operand vector instruction that extends elements of the input register
1724;; and operates on a pair of elements.
1725(type VecRRPairLongOp
1726  (enum
1727    ;; Sign extend and add pair of elements
1728    (Saddlp8)
1729    (Saddlp16)
1730    ;; Unsigned extend and add pair of elements
1731    (Uaddlp8)
1732    (Uaddlp16)
1733))
1734
1735;; An operation across the lanes of vectors.
1736(type VecLanesOp
1737  (enum
1738    ;; Integer addition across a vector
1739    (Addv)
1740    ;; Unsigned minimum across a vector
1741    (Uminv)
1742))
1743
1744;; A shift-by-immediate operation on each lane of a vector.
1745(type VecShiftImmOp
1746  (enum
1747    ;; Unsigned shift left
1748    (Shl)
1749    ;; Unsigned shift right
1750    (Ushr)
1751    ;; Signed shift right
1752    (Sshr)
1753))
1754
1755;; Destructive shift-by-immediate operation on each lane of a vector.
1756(type VecShiftImmModOp
1757  (enum
1758    ;; Shift left and insert
1759    (Sli)
1760))
1761
1762;; Atomic read-modify-write operations with acquire-release semantics
1763(type AtomicRMWOp
1764  (enum
1765    (Add)
1766    (Clr)
1767    (Eor)
1768    (Set)
1769    (Smax)
1770    (Smin)
1771    (Umax)
1772    (Umin)
1773    (Swp)
1774))
1775
1776;; Atomic read-modify-write operations, with acquire-release semantics,
1777;; implemented with a loop.
1778(type AtomicRMWLoopOp
1779  (enum
1780    (Add)
1781    (Sub)
1782    (And)
1783    (Nand)
1784    (Eor)
1785    (Orr)
1786    (Smax)
1787    (Smin)
1788    (Umax)
1789    (Umin)
1790    (Xchg)
1791))
1792
1793;; Keys for instruction address PACs
1794(type APIKey
1795  (enum
1796    ;; API key A with the modifier of SP
1797    (ASP)
1798    ;; API key B with the modifier of SP
1799    (BSP)
1800    ;; API key A with the modifier of zero
1801    (AZ)
1802    ;; API key B with the modifier of zero
1803    (BZ)
1804))
1805
1806;; Branch target types
1807(type BranchTargetType
1808  (enum
1809    (None)
1810    (C)
1811    (J)
1812    (JC)
1813))
1814
1815;; Extractors for target features ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1816(decl pure partial sign_return_address_disabled () Unit)
1817(extern constructor sign_return_address_disabled sign_return_address_disabled)
1818
1819(decl use_lse () Inst)
1820(extern extractor use_lse use_lse)
1821
1822(decl pure use_fp16 () bool)
1823(extern constructor use_fp16 use_fp16)
1824
1825;; Extractor helpers for various immediate constants ;;;;;;;;;;;;;;;;;;;;;;;;;;
1826
1827(decl pure partial move_wide_const_from_u64 (Type u64) MoveWideConst)
1828(extern constructor move_wide_const_from_u64 move_wide_const_from_u64)
1829
1830(decl pure partial move_wide_const_from_inverted_u64 (Type u64) MoveWideConst)
1831(extern constructor move_wide_const_from_inverted_u64 move_wide_const_from_inverted_u64)
1832
1833(decl pure partial imm_logic_from_u64 (Type u64) ImmLogic)
1834(extern constructor imm_logic_from_u64 imm_logic_from_u64)
1835
1836(decl pure partial imm_size_from_type (Type) u16)
1837(extern constructor imm_size_from_type imm_size_from_type)
1838
1839(decl pure partial imm_logic_from_imm64 (Type Imm64) ImmLogic)
1840(extern constructor imm_logic_from_imm64 imm_logic_from_imm64)
1841
1842(spec (imm_shift_from_imm64 ty x)
1843      (provide (= result (extract 5 0 (bvand x (bvsub (int2bv 64 ty) #x0000000000000001)))))
1844      (require (bvult (bvand x (bvsub (int2bv 64 ty)  #x0000000000000001)) #x0000000000000040)))
1845
1846(decl pure partial imm_shift_from_imm64 (Type Imm64) ImmShift)
1847(extern constructor imm_shift_from_imm64 imm_shift_from_imm64)
1848
1849(decl imm_shift_from_u8 (u8) ImmShift)
1850(extern constructor imm_shift_from_u8 imm_shift_from_u8)
1851
1852(spec (imm12_from_u64 imm12)
1853      (provide (= result (zero_ext 64 imm12)))
1854      (require
1855            ; REVIEW(mbm): correct formulation of imm12?
1856            (or
1857                  (= imm12 (bvand imm12 #x000fff))
1858                  (= imm12 (bvand imm12 #xfff000))
1859            )
1860      )
1861)
1862(decl imm12_from_u64 (Imm12) u64)
1863(extern extractor imm12_from_u64 imm12_from_u64)
1864
1865(decl u8_into_uimm5 (u8) UImm5)
1866(extern constructor u8_into_uimm5 u8_into_uimm5)
1867
1868(spec (u8_into_imm12 arg)
1869      (provide (= result (zero_ext 24 arg))))
1870(decl u8_into_imm12 (u8) Imm12)
1871(extern constructor u8_into_imm12 u8_into_imm12)
1872
1873(spec (u64_into_imm_logic ty a)
1874      (provide (= result a))
1875      (require (or (= ty 32) (= ty 64))))
1876(decl u64_into_imm_logic (Type u64) ImmLogic)
1877(extern constructor u64_into_imm_logic u64_into_imm_logic)
1878
1879(decl branch_target (MachLabel) BranchTarget)
1880(extern constructor branch_target branch_target)
1881(convert MachLabel BranchTarget branch_target)
1882
1883(decl targets_jt_space (BoxVecMachLabel) CodeOffset)
1884(extern constructor targets_jt_space targets_jt_space)
1885
1886;; Calculate the minimum floating-point bound for a conversion to floating
1887;; point from an integer type.
1888;; Accepts whether the output is signed, the size of the input
1889;; floating point type in bits, and the size of the output integer type
1890;; in bits.
1891(decl min_fp_value (bool u8 u8) Reg)
1892(extern constructor min_fp_value min_fp_value)
1893
1894;; Calculate the maximum floating-point bound for a conversion to floating
1895;; point from an integer type.
1896;; Accepts whether the output is signed, the size of the input
1897;; floating point type in bits, and the size of the output integer type
1898;; in bits.
1899(decl max_fp_value (bool u8 u8) Reg)
1900(extern constructor max_fp_value max_fp_value)
1901
1902;; Constructs an FPUOpRI.Ushr* given the size in bits of the value (or lane)
1903;; and the amount to shift by.
1904(decl fpu_op_ri_ushr (u8 u8) FPUOpRI)
1905(extern constructor fpu_op_ri_ushr fpu_op_ri_ushr)
1906
1907;; Constructs an FPUOpRIMod.Sli* given the size in bits of the value (or lane)
1908;; and the amount to shift by.
1909(decl fpu_op_ri_sli (u8 u8) FPUOpRIMod)
1910(extern constructor fpu_op_ri_sli fpu_op_ri_sli)
1911
1912(decl pure partial lshr_from_u64 (Type u64) ShiftOpAndAmt)
1913(extern constructor lshr_from_u64 lshr_from_u64)
1914
1915(spec (lshl_from_imm64 ty a)
1916    (provide (= result (concat #x0e (extract 7 0 a))))
1917    (require (= (extract 63 8 a) #b00000000000000000000000000000000000000000000000000000000)))
1918(decl pure partial lshl_from_imm64 (Type Imm64) ShiftOpAndAmt)
1919(extern constructor lshl_from_imm64 lshl_from_imm64)
1920
1921(decl pure partial lshl_from_u64 (Type u64) ShiftOpAndAmt)
1922(extern constructor lshl_from_u64 lshl_from_u64)
1923
1924(decl pure partial ashr_from_u64 (Type u64) ShiftOpAndAmt)
1925(extern constructor ashr_from_u64 ashr_from_u64)
1926
1927(decl integral_ty (Type) Type)
1928(extern extractor integral_ty integral_ty)
1929
1930(decl valid_atomic_transaction (Type) Type)
1931(extern extractor valid_atomic_transaction valid_atomic_transaction)
1932
1933(decl pure partial is_zero_simm9 (SImm9) Unit)
1934(extern constructor is_zero_simm9 is_zero_simm9)
1935
1936(decl pure partial is_zero_uimm12 (UImm12Scaled) Unit)
1937(extern constructor is_zero_uimm12 is_zero_uimm12)
1938
1939;; Helper to go directly from a `Value`, when it's an `iconst`, to an `Imm12`.
1940; REVIEW(mbm): is imm12_from_value spec correct?
1941; NOTE(mbm): compare with https://github.com/avanhatt/wasmtime/blob/94ccb9d4d55a479893cb04bc796ec620ed24cee2/cranelift/codegen/src/isa/aarch64/inst.isle#L1867-L1874
1942(spec (imm12_from_value imm12)
1943      (provide
1944            ; REVIEW(mbm): zero_ext vs conv_to?
1945            (= result (conv_to (widthof result) (zero_ext 64 imm12)))
1946            (= imm12 (conv_to (widthof imm12) (zero_ext 64 result)))
1947      )
1948      (require
1949            ; REVIEW(mbm): correct formulation of imm12?
1950            (or
1951                  (= imm12 (bvand imm12 #x000fff))
1952                  (= imm12 (bvand imm12 #xfff000))
1953            )
1954      )
1955)
1956(decl imm12_from_value (Imm12) Value)
1957(extractor
1958  (imm12_from_value n)
1959  (iconst (u64_from_imm64 (imm12_from_u64 n))))
1960;; Conceptually the same as `imm12_from_value`, but tries negating the constant
1961;; value (first sign-extending to handle narrow widths).
1962(spec (imm12_from_negated_value arg)
1963      (provide
1964            (= (bvneg (sign_ext 64 arg)) (zero_ext 64 result))
1965      )
1966      (require
1967            ; REVIEW(mbm): correct formulation of imm12?
1968            (or
1969                  (= result (bvand result #x000fff))
1970                  (= result (bvand result #xfff000))
1971            )
1972      )
1973)
1974
1975(instantiate imm12_from_negated_value
1976    ((args (bv 8)) (ret (bv 24)) (canon (bv 8)))
1977    ((args (bv 16)) (ret (bv 24)) (canon (bv 16)))
1978    ((args (bv 32)) (ret (bv 24)) (canon (bv 32)))
1979    ((args (bv 64)) (ret (bv 24)) (canon (bv 64)))
1980)
1981(decl pure partial imm12_from_negated_value (Value) Imm12)
1982(rule imm12_from_negated_value
1983  (imm12_from_negated_value (has_type ty (iconst n)))
1984  (if-let (imm12_from_u64 imm) (i64_cast_unsigned (i64_checked_neg (i64_sextend_imm64 ty n))))
1985  imm)
1986
1987;; Helper type to represent a value and an extend operation fused together.
1988(model ExtendedValue (type (bv 67)))
1989(type ExtendedValue extern (enum))
1990;; Only including the i8 to i32 opcodes, based on the impl of extended_value_from_value
1991(spec (extended_value_from_value x)
1992  (provide
1993    (switch (extract 66 64 x)
1994      ((ExtendOp.UXTB) (= (extract 63 0 x) (zero_ext 64 (extract 7 0 (zero_ext 64 result)))))
1995      ((ExtendOp.UXTH) (= (extract 63 0 x) (zero_ext 64 (extract 15 0 (zero_ext 64 result)))))
1996      ((ExtendOp.UXTW) (= (extract 63 0 x) (zero_ext 64 (extract 31 0 (zero_ext 64 result)))))
1997      ((ExtendOp.SXTB) (= (extract 63 0 x) (sign_ext 64 (extract 7 0 (zero_ext 64 result)))))
1998      ((ExtendOp.SXTH) (= (extract 63 0 x) (sign_ext 64 (extract 15 0 (zero_ext 64 result)))))
1999      ((ExtendOp.SXTW) (= (extract 63 0 x) (sign_ext 64 (extract 31 0 (zero_ext 64 result)))))))
2000  (require
2001    (bvult (extract 66 64 x) #b110)
2002    (not (= (extract 66 64 x) #b011))
2003    (= result (conv_to (widthof result) x))
2004    (or (= 8 (widthof result)) (= 16 (widthof result)) (= 32 (widthof result)))))
2005(decl extended_value_from_value (ExtendedValue) Value)
2006(extern extractor extended_value_from_value extended_value_from_value)
2007
2008;; Constructors used to poke at the fields of an `ExtendedValue`.
2009(decl put_extended_in_reg (ExtendedValue) Reg)
2010(extern constructor put_extended_in_reg put_extended_in_reg)
2011(decl get_extended_op (ExtendedValue) ExtendOp)
2012(extern constructor get_extended_op get_extended_op)
2013
2014(decl nzcv (bool bool bool bool) NZCV)
2015(extern constructor nzcv nzcv)
2016
2017(decl cond_br_zero (Reg OperandSize) CondBrKind)
2018(extern constructor cond_br_zero cond_br_zero)
2019
2020(decl cond_br_not_zero (Reg OperandSize) CondBrKind)
2021(extern constructor cond_br_not_zero cond_br_not_zero)
2022
2023(decl cond_br_cond (Cond) CondBrKind)
2024(extern constructor cond_br_cond cond_br_cond)
2025
2026;; Instruction creation helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2027
2028;; Helper for creating the zero register.
2029(spec (zero_reg) (provide (= result #x0000000000000000)))
2030(decl zero_reg () Reg)
2031(extern constructor zero_reg zero_reg)
2032
2033(decl fp_reg () Reg)
2034(extern constructor fp_reg fp_reg)
2035
2036(decl stack_reg () Reg)
2037(extern constructor stack_reg stack_reg)
2038
2039(decl writable_link_reg () WritableReg)
2040(extern constructor writable_link_reg writable_link_reg)
2041
2042(decl writable_zero_reg () WritableReg)
2043(extern constructor writable_zero_reg writable_zero_reg)
2044
2045(decl value_regs_zero () ValueRegs)
2046(rule (value_regs_zero)
2047      (value_regs
2048            (imm $I64 (ImmExtend.Zero) 0)
2049            (imm $I64 (ImmExtend.Zero) 0)))
2050
2051
2052;; Helper for emitting `MInst.Mov` instructions.
2053(decl mov (Reg Type) Reg)
2054(rule (mov src ty)
2055      (let ((dst WritableReg (temp_writable_reg $I64))
2056            (_ Unit (emit (MInst.Mov (operand_size ty) dst src))))
2057        dst))
2058
2059;; Helper for emitting `MInst.MovZ` instructions.
2060(decl movz (MoveWideConst OperandSize) Reg)
2061(rule (movz imm size)
2062      (let ((dst WritableReg (temp_writable_reg $I64))
2063            (_ Unit (emit (MInst.MovWide (MoveWideOp.MovZ) dst imm size))))
2064        dst))
2065
2066;; Helper for emitting `MInst.MovN` instructions.
2067(decl movn (MoveWideConst OperandSize) Reg)
2068(rule (movn imm size)
2069      (let ((dst WritableReg (temp_writable_reg $I64))
2070            (_ Unit (emit (MInst.MovWide (MoveWideOp.MovN) dst imm size))))
2071        dst))
2072
2073;; Helper for emitting `MInst.AluRRImmLogic` instructions.
2074(decl alu_rr_imm_logic (ALUOp Type Reg ImmLogic) Reg)
2075(rule (alu_rr_imm_logic op ty src imm)
2076      (let ((dst WritableReg (temp_writable_reg $I64))
2077            (_ Unit (emit (MInst.AluRRImmLogic op (operand_size ty) dst src imm))))
2078        dst))
2079
2080;; Helper for emitting `MInst.AluRRImmShift` instructions.
2081(spec (alu_rr_imm_shift op t a b)
2082    (provide
2083      (= result (switch op
2084              ((ALUOp.Lsr)
2085                (if (<= t 32)
2086                    (conv_to 64 (bvlshr (extract 31 0 a) (bvand (bvsub (int2bv 32 32) #x00000001) (extract 31 0 (zero_ext 64 b)))))
2087                    (bvlshr a (bvand (bvsub (int2bv 64 64) #x0000000000000001) (zero_ext 64 b)))))
2088              ((ALUOp.Asr)
2089                (if (<= t 32)
2090                    (conv_to 64 (bvashr (extract 31 0 a) (bvand (bvsub (int2bv 32 32) #x00000001) (extract 31 0 (zero_ext 64 b)))))
2091                    (bvashr a (bvand (bvsub (int2bv 64 64) #x0000000000000001) (zero_ext 64 b)))))
2092              ((ALUOp.Lsl)
2093                (if (<= t 32)
2094                    (conv_to 64 (bvshl (extract 31 0 a) (bvand (bvsub (int2bv 32 32) #x00000001) (extract 31 0 (zero_ext 64 b)))))
2095                    (bvshl a (bvand (bvsub (int2bv 64 64) #x0000000000000001) (zero_ext 64 b))))))))
2096    (require
2097      (or (= op (ALUOp.Lsr)) (= op (ALUOp.Asr)) (= op (ALUOp.Lsl)))
2098      (or (= t 8) (= t 16) (= t 32) (= t 64))))
2099(decl alu_rr_imm_shift (ALUOp Type Reg ImmShift) Reg)
2100(rule (alu_rr_imm_shift op ty src imm)
2101      (let ((dst WritableReg (temp_writable_reg $I64))
2102            (_ Unit (emit (MInst.AluRRImmShift op (operand_size ty) dst src imm))))
2103        dst))
2104
2105;; Helper for emitting `MInst.AluRRR` instructions.
2106(spec (alu_rrr op t a b)
2107    (provide
2108      (= result (switch op
2109              ((ALUOp.Lsr)
2110                (if (<= t 32)
2111                    (conv_to 64 (bvlshr (extract 31 0 a) (bvand (bvsub (int2bv 32 32) #x00000001) (extract 31 0 b))))
2112                    (bvlshr a (bvand (bvsub (int2bv 64 64) #x0000000000000001) b))))
2113              ((ALUOp.Asr)
2114                (if (<= t 32)
2115                    (conv_to 64 (bvashr (extract 31 0 a) (bvand (bvsub (int2bv 32 32) #x00000001) (extract 31 0 b))))
2116                    (bvashr a (bvand (bvsub (int2bv 64 64) #x0000000000000001) b))))
2117              ((ALUOp.Lsl)
2118                (if (<= t 32)
2119                    (conv_to 64 (bvshl (extract 31 0 a) (bvand (bvsub (int2bv 32 32) #x00000001) (extract 31 0 b))))
2120                    (bvshl a (bvand (bvsub (int2bv 64 64) #x0000000000000001) b)))))))
2121    (require
2122      (or (= op (ALUOp.Lsr)) (= op (ALUOp.Asr)) (= op (ALUOp.Lsl)))
2123      (or (= t 8) (= t 16) (= t 32) (= t 64))))
2124(decl alu_rrr (ALUOp Type Reg Reg) Reg)
2125(rule (alu_rrr op ty src1 src2)
2126      (let ((dst WritableReg (temp_writable_reg $I64))
2127            (_ Unit (emit (MInst.AluRRR op (operand_size ty) dst src1 src2))))
2128        dst))
2129
2130;; Helper for emitting `MInst.VecRRR` instructions.
2131(decl vec_rrr (VecALUOp Reg Reg VectorSize) Reg)
2132(rule (vec_rrr op src1 src2 size)
2133      (let ((dst WritableReg (temp_writable_reg $I8X16))
2134            (_ Unit (emit (MInst.VecRRR op dst src1 src2 size))))
2135        dst))
2136
2137;; Helper for emitting `MInst.FpuRR` instructions.
2138(decl fpu_rr (FPUOp1 Reg ScalarSize) Reg)
2139(rule (fpu_rr op src size)
2140      (let ((dst WritableReg (temp_writable_reg $F64))
2141            (_ Unit (emit (MInst.FpuRR op size dst src))))
2142       dst))
2143
2144;; Helper for emitting `MInst.VecRRRMod` instructions which use three registers,
2145;; one of which is both source and output.
2146(decl vec_rrr_mod (VecALUModOp Reg Reg Reg VectorSize) Reg)
2147(rule (vec_rrr_mod op src1 src2 src3 size)
2148      (let ((dst WritableReg (temp_writable_reg $I8X16))
2149            (_1 Unit (emit (MInst.VecRRRMod op dst src1 src2 src3 size))))
2150        dst))
2151
2152;; Helper for emitting `MInst.VecFmlaElem` instructions which use three registers,
2153;; one of which is both source and output.
2154(decl vec_fmla_elem (VecALUModOp Reg Reg Reg VectorSize u8) Reg)
2155(rule (vec_fmla_elem op src1 src2 src3 size idx)
2156      (let ((dst WritableReg (temp_writable_reg $I8X16))
2157            (_1 Unit (emit (MInst.VecFmlaElem op dst src1 src2 src3 size idx))))
2158        dst))
2159
2160(decl fpu_rri (FPUOpRI Reg) Reg)
2161(rule (fpu_rri op src)
2162      (let ((dst WritableReg (temp_writable_reg $F64))
2163            (_ Unit (emit (MInst.FpuRRI op dst src))))
2164        dst))
2165
2166(decl fpu_rri_mod (FPUOpRIMod Reg Reg) Reg)
2167(rule (fpu_rri_mod op dst_src src)
2168      (let ((dst WritableReg (temp_writable_reg $F64))
2169            (_ Unit (emit (MInst.FpuRRIMod op dst dst_src src))))
2170        dst))
2171
2172;; Helper for emitting `MInst.FpuRRR` instructions.
2173(decl fpu_rrr (FPUOp2 Reg Reg ScalarSize) Reg)
2174(rule (fpu_rrr op src1 src2 size)
2175      (let ((dst WritableReg (temp_writable_reg $F64))
2176            (_ Unit (emit (MInst.FpuRRR op size dst src1 src2))))
2177        dst))
2178
2179;; Helper for emitting `MInst.FpuRRRR` instructions.
2180(decl fpu_rrrr (FPUOp3 ScalarSize Reg Reg Reg) Reg)
2181(rule (fpu_rrrr size op src1 src2 src3)
2182      (let ((dst WritableReg (temp_writable_reg $F64))
2183            (_ Unit (emit (MInst.FpuRRRR size op dst src1 src2 src3))))
2184        dst))
2185
2186;; Helper for emitting `MInst.FpuCmp` instructions.
2187(decl fpu_cmp (ScalarSize Reg Reg) ProducesFlags)
2188(rule (fpu_cmp size rn rm)
2189      (ProducesFlags.ProducesFlagsSideEffect
2190       (MInst.FpuCmp size rn rm)))
2191
2192;; Helper for emitting `MInst.VecLanes` instructions.
2193(decl vec_lanes (VecLanesOp Reg VectorSize) Reg)
2194(rule (vec_lanes op src size)
2195      (let ((dst WritableReg (temp_writable_reg $I8X16))
2196            (_ Unit (emit (MInst.VecLanes op dst src size))))
2197        dst))
2198
2199;; Helper for emitting `MInst.VecShiftImm` instructions.
2200(decl vec_shift_imm (VecShiftImmOp u8 Reg VectorSize) Reg)
2201(rule (vec_shift_imm op imm src size)
2202      (let ((dst WritableReg (temp_writable_reg $I8X16))
2203            (_ Unit (emit (MInst.VecShiftImm op dst src size imm))))
2204        dst))
2205
2206;; Helper for emitting `MInst.VecDup` instructions.
2207(decl vec_dup (Reg VectorSize) Reg)
2208(rule (vec_dup src size)
2209      (let ((dst WritableReg (temp_writable_reg $I8X16))
2210            (_ Unit (emit (MInst.VecDup dst src size))))
2211        dst))
2212
2213;; Helper for emitting `MInst.VecDupFromFpu` instructions.
2214(decl vec_dup_from_fpu (Reg VectorSize u8) Reg)
2215(rule (vec_dup_from_fpu src size lane)
2216      (let ((dst WritableReg (temp_writable_reg $I8X16))
2217            (_ Unit (emit (MInst.VecDupFromFpu dst src size lane))))
2218        dst))
2219
2220;; Helper for emitting `MInst.VecDupImm` instructions.
2221(decl vec_dup_imm (ASIMDMovModImm bool VectorSize) Reg)
2222(rule (vec_dup_imm imm invert size)
2223      (let ((dst WritableReg (temp_writable_reg $I8X16))
2224            (_ Unit (emit (MInst.VecDupImm dst imm invert size))))
2225        dst))
2226
2227;; Helper for emitting `MInst.AluRRImm12` instructions.
2228(decl alu_rr_imm12 (ALUOp Type Reg Imm12) Reg)
2229(rule (alu_rr_imm12 op ty src imm)
2230      (let ((dst WritableReg (temp_writable_reg $I64))
2231            (_ Unit (emit (MInst.AluRRImm12 op (operand_size ty) dst src imm))))
2232        dst))
2233
2234;; Helper for emitting `MInst.AluRRRShift` instructions.
2235(decl alu_rrr_shift (ALUOp Type Reg Reg ShiftOpAndAmt) Reg)
2236(rule (alu_rrr_shift op ty src1 src2 shift)
2237      (let ((dst WritableReg (temp_writable_reg $I64))
2238            (_ Unit (emit (MInst.AluRRRShift op (operand_size ty) dst src1 src2 shift))))
2239        dst))
2240
2241;; Helper for emitting `cmp` instructions, setting flags, with a right-shifted
2242;; second operand register.
2243(decl cmp_rr_shift (OperandSize Reg Reg u64) ProducesFlags)
2244(rule (cmp_rr_shift size src1 src2 shift_amount)
2245      (if-let shift (lshr_from_u64 $I64 shift_amount))
2246      (ProducesFlags.ProducesFlagsSideEffect
2247       (MInst.AluRRRShift (ALUOp.SubS) size (writable_zero_reg)
2248        src1 src2 shift)))
2249
2250;; Helper for emitting `cmp` instructions, setting flags, with an arithmetic right-shifted
2251;; second operand register.
2252(decl cmp_rr_shift_asr (OperandSize Reg Reg u64) ProducesFlags)
2253(rule (cmp_rr_shift_asr size src1 src2 shift_amount)
2254      (if-let shift (ashr_from_u64 $I64 shift_amount))
2255      (ProducesFlags.ProducesFlagsSideEffect
2256       (MInst.AluRRRShift (ALUOp.SubS) size (writable_zero_reg)
2257        src1 src2 shift)))
2258
2259;; Helper for emitting `MInst.AluRRRExtend` instructions.
2260(decl alu_rrr_extend (ALUOp Type Reg Reg ExtendOp) Reg)
2261(rule (alu_rrr_extend op ty src1 src2 extend)
2262      (let ((dst WritableReg (temp_writable_reg $I64))
2263            (_ Unit (emit (MInst.AluRRRExtend op (operand_size ty) dst src1 src2 extend))))
2264        dst))
2265
2266;; Same as `alu_rrr_extend`, but takes an `ExtendedValue` packed "pair" instead
2267;; of a `Reg` and an `ExtendOp`.
2268(decl alu_rr_extend_reg (ALUOp Type Reg ExtendedValue) Reg)
2269(rule (alu_rr_extend_reg op ty src1 extended_reg)
2270      (let ((src2 Reg (put_extended_in_reg extended_reg))
2271            (extend ExtendOp (get_extended_op extended_reg)))
2272        (alu_rrr_extend op ty src1 src2 extend)))
2273
2274;; Helper for emitting `MInst.AluRRRR` instructions.
2275(decl alu_rrrr (ALUOp3 Type Reg Reg Reg) Reg)
2276(rule (alu_rrrr op ty src1 src2 src3)
2277      (let ((dst WritableReg (temp_writable_reg $I64))
2278            (_ Unit (emit (MInst.AluRRRR op (operand_size ty) dst src1 src2 src3))))
2279        dst))
2280
2281;; Helper for emitting paired `MInst.AluRRR` instructions
2282(decl alu_rrr_with_flags_paired (Type Reg Reg ALUOp) ProducesFlags)
2283(rule (alu_rrr_with_flags_paired ty src1 src2 alu_op)
2284      (let ((dst WritableReg (temp_writable_reg $I64)))
2285        (ProducesFlags.ProducesFlagsReturnsResultWithConsumer
2286         (MInst.AluRRR alu_op (operand_size ty) dst src1 src2)
2287         dst)))
2288
2289;; Should only be used for AdcS and SbcS
2290(decl alu_rrr_with_flags_chained (Type Reg Reg ALUOp) ConsumesAndProducesFlags)
2291(rule (alu_rrr_with_flags_chained ty src1 src2 alu_op)
2292      (let ((dst WritableReg (temp_writable_reg $I64)))
2293        (ConsumesAndProducesFlags.ReturnsReg
2294         (MInst.AluRRR alu_op (operand_size ty) dst src1 src2)
2295         dst)))
2296
2297;; Helper for emitting `MInst.BitRR` instructions.
2298(decl bit_rr (BitOp Type Reg) Reg)
2299(rule (bit_rr op ty src)
2300      (let ((dst WritableReg (temp_writable_reg $I64))
2301            (_ Unit (emit (MInst.BitRR op (operand_size ty) dst src))))
2302        dst))
2303
2304;; Helper for emitting `adds` instructions.
2305(decl add_with_flags_paired (Type Reg Reg) ProducesFlags)
2306(rule (add_with_flags_paired ty src1 src2)
2307      (let ((dst WritableReg (temp_writable_reg $I64)))
2308        (ProducesFlags.ProducesFlagsReturnsResultWithConsumer
2309         (MInst.AluRRR (ALUOp.AddS) (operand_size ty) dst src1 src2)
2310         dst)))
2311
2312;; Helper for emitting `adc` instructions.
2313(decl adc_paired (Type Reg Reg) ConsumesFlags)
2314(rule (adc_paired ty src1 src2)
2315      (let ((dst WritableReg (temp_writable_reg $I64)))
2316        (ConsumesFlags.ConsumesFlagsReturnsResultWithProducer
2317         (MInst.AluRRR (ALUOp.Adc) (operand_size ty) dst src1 src2)
2318         dst)))
2319
2320;; Helper for emitting `subs` instructions.
2321(decl sub_with_flags_paired (Type Reg Reg) ProducesFlags)
2322(rule (sub_with_flags_paired ty src1 src2)
2323      (let ((dst WritableReg (temp_writable_reg $I64)))
2324        (ProducesFlags.ProducesFlagsReturnsResultWithConsumer
2325         (MInst.AluRRR (ALUOp.SubS) (operand_size ty) dst src1 src2)
2326         dst)))
2327
2328;; Helper for materializing a boolean value into a register from
2329;; flags.
2330(decl materialize_bool_result (Cond) ConsumesFlags)
2331(rule (materialize_bool_result cond)
2332      (let ((dst WritableReg (temp_writable_reg $I64)))
2333        (ConsumesFlags.ConsumesFlagsReturnsReg
2334         (MInst.CSet dst cond)
2335         dst)))
2336
2337(decl cmn_imm (OperandSize Reg Imm12) ProducesFlags)
2338(rule (cmn_imm size src1 src2)
2339      (ProducesFlags.ProducesFlagsSideEffect
2340       (MInst.AluRRImm12 (ALUOp.AddS) size (writable_zero_reg)
2341        src1 src2)))
2342
2343(spec (cmp ty x y)
2344  (provide (= result (subs ty x y)))
2345  (require
2346     (or (= ty 32) (= ty 64))))
2347(decl cmp (OperandSize Reg Reg) ProducesFlags)
2348(rule (cmp size src1 src2)
2349      (ProducesFlags.ProducesFlagsSideEffect
2350       (MInst.AluRRR (ALUOp.SubS) size (writable_zero_reg)
2351        src1 src2)))
2352
2353(spec (cmp_imm ty x y)
2354  (provide (= result (subs ty x (zero_ext 64 y))))
2355  (require (or (= ty 32) (= ty 64))))
2356(decl cmp_imm (OperandSize Reg Imm12) ProducesFlags)
2357(rule (cmp_imm size src1 src2)
2358      (ProducesFlags.ProducesFlagsSideEffect
2359       (MInst.AluRRImm12 (ALUOp.SubS) size (writable_zero_reg)
2360        src1 src2)))
2361
2362(decl cmp64_imm (Reg Imm12) ProducesFlags)
2363(rule (cmp64_imm src1 src2)
2364      (cmp_imm (OperandSize.Size64) src1 src2))
2365
2366(spec (cmp_extend ty x y extend)
2367  (provide
2368    (= result
2369       (subs ty x
2370         (switch extend
2371           ((ExtendOp.UXTB) (zero_ext 64 (extract 7 0 y)))
2372           ((ExtendOp.UXTH) (zero_ext 64 (extract 15 0 y)))
2373           ((ExtendOp.UXTW) (zero_ext 64 (extract 31 0 y)))
2374           ((ExtendOp.UXTX) (zero_ext 64 (extract 63 0 y)))
2375           ((ExtendOp.SXTB) (sign_ext 64 (extract 7 0 y)))
2376           ((ExtendOp.SXTH) (sign_ext 64 (extract 15 0 y)))
2377           ((ExtendOp.SXTW) (sign_ext 64 (extract 31 0 y)))
2378           ((ExtendOp.SXTX) (sign_ext 64 (extract 63 0 y)))))))
2379  (require (or (= ty 32) (= ty 64))))
2380(decl cmp_extend (OperandSize Reg Reg ExtendOp) ProducesFlags)
2381(rule (cmp_extend size src1 src2 extend)
2382      (ProducesFlags.ProducesFlagsSideEffect
2383       (MInst.AluRRRExtend (ALUOp.SubS) size (writable_zero_reg)
2384        src1 src2 extend)))
2385
2386;; Helper for emitting `sbc` instructions.
2387(decl sbc_paired (Type Reg Reg) ConsumesFlags)
2388(rule (sbc_paired ty src1 src2)
2389      (let ((dst WritableReg (temp_writable_reg $I64)))
2390        (ConsumesFlags.ConsumesFlagsReturnsResultWithProducer
2391         (MInst.AluRRR (ALUOp.Sbc) (operand_size ty) dst src1 src2)
2392         dst)))
2393
2394;; Helper for emitting `MInst.VecMisc` instructions.
2395(decl vec_misc (VecMisc2 Reg VectorSize) Reg)
2396(rule (vec_misc op src size)
2397      (let ((dst WritableReg (temp_writable_reg $I8X16))
2398            (_ Unit (emit (MInst.VecMisc op dst src size))))
2399        dst))
2400
2401;; Helper for emitting `MInst.VecTbl` instructions.
2402(decl vec_tbl (Reg Reg) Reg)
2403(rule (vec_tbl rn rm)
2404      (let ((dst WritableReg (temp_writable_reg $I8X16))
2405            (_ Unit (emit (MInst.VecTbl dst rn rm))))
2406        dst))
2407
2408(decl vec_tbl_ext (Reg Reg Reg) Reg)
2409(rule (vec_tbl_ext ri rn rm)
2410      (let ((dst WritableReg (temp_writable_reg $I8X16))
2411            (_ Unit (emit (MInst.VecTblExt dst ri rn rm))))
2412        dst))
2413
2414;; Helper for emitting `MInst.VecTbl2` instructions.
2415(decl vec_tbl2 (Reg Reg Reg Type) Reg)
2416(rule (vec_tbl2 rn rn2 rm ty)
2417      (let (
2418            (dst WritableReg (temp_writable_reg $I8X16))
2419            (_ Unit (emit (MInst.VecTbl2 dst rn rn2 rm)))
2420        )
2421        dst))
2422
2423;; Helper for emitting `MInst.VecTbl2Ext` instructions.
2424(decl vec_tbl2_ext (Reg Reg Reg Reg Type) Reg)
2425(rule (vec_tbl2_ext ri rn rn2 rm ty)
2426      (let (
2427            (dst WritableReg (temp_writable_reg $I8X16))
2428            (_ Unit (emit (MInst.VecTbl2Ext dst ri rn rn2 rm)))
2429        )
2430        dst))
2431
2432;; Helper for emitting `MInst.VecRRRLong` instructions.
2433(decl vec_rrr_long (VecRRRLongOp Reg Reg bool) Reg)
2434(rule (vec_rrr_long op src1 src2 high_half)
2435      (let ((dst WritableReg (temp_writable_reg $I8X16))
2436            (_ Unit (emit (MInst.VecRRRLong op dst src1 src2 high_half))))
2437        dst))
2438
2439;; Helper for emitting `MInst.VecRRPairLong` instructions.
2440(decl vec_rr_pair_long (VecRRPairLongOp Reg) Reg)
2441(rule (vec_rr_pair_long op src)
2442      (let ((dst WritableReg (temp_writable_reg $I8X16))
2443            (_ Unit (emit (MInst.VecRRPairLong op dst src))))
2444        dst))
2445
2446;; Helper for emitting `MInst.VecRRRLongMod` instructions.
2447(decl vec_rrrr_long (VecRRRLongModOp Reg Reg Reg bool) Reg)
2448(rule (vec_rrrr_long op src1 src2 src3 high_half)
2449      (let ((dst WritableReg (temp_writable_reg $I8X16))
2450            (_ Unit (emit (MInst.VecRRRLongMod op dst src1 src2 src3 high_half))))
2451        dst))
2452
2453;; Helper for emitting `MInst.VecRRNarrow` instructions.
2454(decl vec_rr_narrow_low (VecRRNarrowOp Reg ScalarSize) Reg)
2455(rule (vec_rr_narrow_low op src size)
2456      (let ((dst WritableReg (temp_writable_reg $I8X16))
2457            (_ Unit (emit (MInst.VecRRNarrowLow op dst src size))))
2458        dst))
2459
2460;; Helper for emitting `MInst.VecRRNarrow` instructions which update the
2461;; high half of the destination register.
2462(decl vec_rr_narrow_high (VecRRNarrowOp Reg Reg ScalarSize) Reg)
2463(rule (vec_rr_narrow_high op mod src size)
2464      (let ((dst WritableReg (temp_writable_reg $I8X16))
2465            (_ Unit (emit (MInst.VecRRNarrowHigh op dst mod src size))))
2466        dst))
2467
2468;; Helper for emitting `MInst.VecRRLong` instructions.
2469(decl vec_rr_long (VecRRLongOp Reg bool) Reg)
2470(rule (vec_rr_long op src high_half)
2471      (let ((dst WritableReg (temp_writable_reg $I8X16))
2472            (_ Unit (emit (MInst.VecRRLong op dst src high_half))))
2473        dst))
2474
2475;; Helper for emitting `MInst.FpuCSel16` / `MInst.FpuCSel32` / `MInst.FpuCSel64`
2476;; instructions.
2477;;
2478;; Recursion: may recurse once to downgrade from F16 to F32 when FP16 is not enabled.
2479(decl rec fpu_csel (Type Cond Reg Reg) ConsumesFlags)
2480(rule (fpu_csel $F16 cond if_true if_false)
2481        (fpu_csel $F32 cond if_true if_false))
2482
2483(rule 1 (fpu_csel $F16 cond if_true if_false)
2484        (if-let true (use_fp16))
2485        (let ((dst WritableReg (temp_writable_reg $F16)))
2486          (ConsumesFlags.ConsumesFlagsReturnsReg
2487           (MInst.FpuCSel16 dst if_true if_false cond)
2488           dst)))
2489
2490(rule (fpu_csel $F32 cond if_true if_false)
2491      (let ((dst WritableReg (temp_writable_reg $F32)))
2492        (ConsumesFlags.ConsumesFlagsReturnsReg
2493         (MInst.FpuCSel32 dst if_true if_false cond)
2494         dst)))
2495
2496(rule (fpu_csel $F64 cond if_true if_false)
2497      (let ((dst WritableReg (temp_writable_reg $F64)))
2498        (ConsumesFlags.ConsumesFlagsReturnsReg
2499         (MInst.FpuCSel64 dst if_true if_false cond)
2500         dst)))
2501
2502;; Helper for emitting `MInst.VecCSel` instructions.
2503(decl vec_csel (Cond Reg Reg) ConsumesFlags)
2504(rule (vec_csel cond if_true if_false)
2505      (let ((dst WritableReg (temp_writable_reg $I8X16)))
2506        (ConsumesFlags.ConsumesFlagsReturnsReg
2507         (MInst.VecCSel dst if_true if_false cond)
2508         dst)))
2509
2510;; Helper for emitting `MInst.FpuRound` instructions.
2511(decl fpu_round (FpuRoundMode Reg) Reg)
2512(rule (fpu_round op rn)
2513      (let ((dst WritableReg (temp_writable_reg $F64))
2514            (_ Unit (emit (MInst.FpuRound op dst rn))))
2515        dst))
2516
2517;; Helper for emitting `MInst.FpuMove64` and `MInst.FpuMove128` instructions.
2518(decl fpu_move (Type Reg) Reg)
2519(rule (fpu_move _ src)
2520      (let ((dst WritableReg (temp_writable_reg $I8X16))
2521            (_ Unit (emit (MInst.FpuMove128 dst src))))
2522        dst))
2523(rule 1 (fpu_move (fits_in_64 _) src)
2524      (let ((dst WritableReg (temp_writable_reg $F64))
2525            (_ Unit (emit (MInst.FpuMove64 dst src))))
2526        dst))
2527
2528;; Helper for emitting `MInst.MovToFpu` instructions.
2529;;
2530;; Recursion: may recurse once to downgrade from F16 to F32 when FP16 is not enabled.
2531(spec (mov_to_fpu x s)
2532  (provide (= result (zero_ext 64 (conv_to s x)))))
2533(decl rec mov_to_fpu (Reg ScalarSize) Reg)
2534(rule (mov_to_fpu x size)
2535      (let ((dst WritableReg (temp_writable_reg $I8X16))
2536            (_ Unit (emit (MInst.MovToFpu dst x size))))
2537        dst))
2538(rule 1 (mov_to_fpu x (ScalarSize.Size16))
2539        (if-let false (use_fp16))
2540        (mov_to_fpu x (ScalarSize.Size32)))
2541
2542;; Helper for emitting `MInst.FpuMoveFPImm` instructions.
2543(decl fpu_move_fp_imm (ASIMDFPModImm ScalarSize) Reg)
2544(rule (fpu_move_fp_imm imm size)
2545      (let ((dst WritableReg (temp_writable_reg $I8X16))
2546            (_ Unit (emit (MInst.FpuMoveFPImm dst imm size))))
2547        dst))
2548
2549;; Helper for emitting `MInst.MovToVec` instructions.
2550(decl mov_to_vec (Reg Reg u8 VectorSize) Reg)
2551(rule (mov_to_vec src1 src2 lane size)
2552      (let ((dst WritableReg (temp_writable_reg $I8X16))
2553            (_ Unit (emit (MInst.MovToVec dst src1 src2 lane size))))
2554        dst))
2555
2556;; Helper for emitting `MInst.VecMovElement` instructions.
2557(decl mov_vec_elem (Reg Reg u8 u8 VectorSize) Reg)
2558(rule (mov_vec_elem src1 src2 dst_idx src_idx size)
2559      (let ((dst WritableReg (temp_writable_reg $I8X16))
2560            (_ Unit (emit (MInst.VecMovElement dst src1 src2 dst_idx src_idx size))))
2561        dst))
2562
2563;; Helper for emitting `MInst.MovFromVec` instructions.
2564(spec (mov_from_vec x i s)
2565  (provide
2566    (= result
2567       (switch s
2568         (8
2569           (switch i
2570             (#x00 (zero_ext 64 (extract 7 0 x)))
2571             (#x01 (zero_ext 64 (extract 15 8 x)))
2572             (#x02 (zero_ext 64 (extract 23 16 x)))
2573             (#x03 (zero_ext 64 (extract 31 24 x)))
2574             (#x04 (zero_ext 64 (extract 39 32 x)))
2575             (#x05 (zero_ext 64 (extract 47 40 x)))
2576             (#x06 (zero_ext 64 (extract 55 48 x)))
2577             (#x07 (zero_ext 64 (extract 63 56 x)))))
2578         (16
2579           (switch i
2580             (#x00 (zero_ext 64 (extract 15 0 x)))
2581             (#x01 (zero_ext 64 (extract 31 16 x)))
2582             (#x03 (zero_ext 64 (extract 47 32 x)))
2583             (#x04 (zero_ext 64 (extract 63 48 x)))))
2584         (32
2585           (switch i
2586             (#x00 (zero_ext 64 (extract 31 0 x)))
2587             (#x01 (zero_ext 64 (extract 63 32 x)))))))))
2588(decl mov_from_vec (Reg u8 ScalarSize) Reg)
2589(rule (mov_from_vec rn idx size)
2590      (let ((dst WritableReg (temp_writable_reg $I64))
2591            (_ Unit (emit (MInst.MovFromVec dst rn idx size))))
2592        dst))
2593
2594;; Helper for emitting `MInst.MovFromVecSigned` instructions.
2595(decl mov_from_vec_signed (Reg u8 VectorSize OperandSize) Reg)
2596(rule (mov_from_vec_signed rn idx size scalar_size)
2597      (let ((dst WritableReg (temp_writable_reg $I64))
2598            (_ Unit (emit (MInst.MovFromVecSigned dst rn idx size scalar_size))))
2599        dst))
2600
2601(decl fpu_move_from_vec (Reg u8 VectorSize) Reg)
2602(rule (fpu_move_from_vec rn idx size)
2603      (let ((dst WritableReg (temp_writable_reg $I8X16))
2604            (_ Unit (emit (MInst.FpuMoveFromVec dst rn idx size))))
2605        dst))
2606
2607;; Helper for emitting `MInst.Extend` instructions.
2608(spec (extend a b c d)
2609  (provide
2610    (if b
2611        (= result (sign_ext (bv2int d) (conv_to (bv2int c) a)))
2612        (= result (zero_ext (bv2int d) (conv_to (bv2int c) a))))))
2613(decl extend (Reg bool u8 u8) Reg)
2614(rule (extend rn signed from_bits to_bits)
2615      (let ((dst WritableReg (temp_writable_reg $I64))
2616            (_ Unit (emit (MInst.Extend dst rn signed from_bits to_bits))))
2617        dst))
2618
2619;; Helper for emitting `MInst.FpuExtend` instructions.
2620(decl fpu_extend (Reg ScalarSize) Reg)
2621(rule (fpu_extend src size)
2622      (let ((dst WritableReg (temp_writable_reg $F32X4))
2623            (_ Unit (emit (MInst.FpuExtend dst src size))))
2624        dst))
2625
2626;; Helper for emitting `MInst.VecExtend` instructions.
2627(decl vec_extend (VecExtendOp Reg bool ScalarSize) Reg)
2628(rule (vec_extend op src high_half size)
2629      (let ((dst WritableReg (temp_writable_reg $I8X16))
2630            (_ Unit (emit (MInst.VecExtend op dst src high_half size))))
2631        dst))
2632
2633;; Helper for emitting `MInst.VecExtract` instructions.
2634(decl vec_extract (Reg Reg u8) Reg)
2635(rule (vec_extract src1 src2 idx)
2636      (let ((dst WritableReg (temp_writable_reg $I8X16))
2637            (_ Unit (emit (MInst.VecExtract dst src1 src2 idx))))
2638        dst))
2639
2640;; Helper for emitting `MInst.LoadAcquire` instructions.
2641(decl load_acquire (Type MemFlags Reg) Reg)
2642(rule (load_acquire ty flags addr)
2643      (let ((dst WritableReg (temp_writable_reg $I64))
2644            (_ Unit (emit (MInst.LoadAcquire ty dst addr flags))))
2645        dst))
2646
2647;; Helper for emitting `MInst.StoreRelease` instructions.
2648(decl store_release (Type MemFlags Reg Reg) SideEffectNoResult)
2649(rule (store_release ty flags src addr)
2650      (SideEffectNoResult.Inst (MInst.StoreRelease ty src addr flags)))
2651
2652;; Helper for generating a `tst` instruction.
2653;;
2654;; Produces a `ProducesFlags` rather than a register or emitted instruction
2655;; which must be paired with `with_flags*` helpers.
2656(decl tst_imm (Type Reg ImmLogic) ProducesFlags)
2657(rule (tst_imm ty reg imm)
2658      (ProducesFlags.ProducesFlagsSideEffect
2659       (MInst.AluRRImmLogic (ALUOp.AndS)
2660                            (operand_size ty)
2661                            (writable_zero_reg)
2662                            reg
2663                            imm)))
2664
2665;; Helper for generating a `CSel` instruction.
2666;;
2667;; Note that this doesn't actually emit anything, instead it produces a
2668;; `ConsumesFlags` instruction which must be consumed with `with_flags*`
2669;; helpers.
2670(decl csel (Cond Reg Reg) ConsumesFlags)
2671(rule (csel cond if_true if_false)
2672      (let ((dst WritableReg (temp_writable_reg $I64)))
2673        (ConsumesFlags.ConsumesFlagsReturnsReg
2674         (MInst.CSel dst cond if_true if_false)
2675         dst)))
2676
2677;; Helper for constructing `cset` instructions.
2678(decl cset (Cond) ConsumesFlags)
2679(rule (cset cond)
2680      (let ((dst WritableReg (temp_writable_reg $I64)))
2681        (ConsumesFlags.ConsumesFlagsReturnsReg (MInst.CSet dst cond) dst)))
2682
2683;; Helper for constructing `cset` instructions, when the flags producer will
2684;; also return a value.
2685(decl cset_paired (Cond) ConsumesFlags)
2686(rule (cset_paired cond)
2687      (let ((dst WritableReg (temp_writable_reg $I64)))
2688        (ConsumesFlags.ConsumesFlagsReturnsResultWithProducer (MInst.CSet dst cond) dst)))
2689
2690;; Helper for constructing `csetm` instructions.
2691(decl csetm (Cond) ConsumesFlags)
2692(rule (csetm cond)
2693      (let ((dst WritableReg (temp_writable_reg $I64)))
2694        (ConsumesFlags.ConsumesFlagsReturnsReg (MInst.CSetm dst cond) dst)))
2695
2696;; Helper for generating a `CSNeg` instruction.
2697;;
2698;; Note that this doesn't actually emit anything, instead it produces a
2699;; `ConsumesFlags` instruction which must be consumed with `with_flags*`
2700;; helpers.
2701(decl csneg (Cond Reg Reg) ConsumesFlags)
2702(rule (csneg cond if_true if_false)
2703      (let ((dst WritableReg (temp_writable_reg $I64)))
2704        (ConsumesFlags.ConsumesFlagsReturnsReg
2705         (MInst.CSNeg dst cond if_true if_false)
2706         dst)))
2707
2708;; Helper for generating `MInst.CCmp` instructions.
2709;; Creates a new `ProducesFlags` from the supplied `ProducesFlags` followed
2710;; immediately by the `MInst.CCmp` instruction.
2711(decl ccmp (OperandSize Reg Reg NZCV Cond ProducesFlags) ProducesFlags)
2712(rule (ccmp size rn rm nzcv cond inst_input)
2713      (produces_flags_concat inst_input (ProducesFlags.ProducesFlagsSideEffect (MInst.CCmp size rn rm nzcv cond))))
2714
2715;; Helper for generating `MInst.CCmpImm` instructions.
2716(decl ccmp_imm (OperandSize Reg UImm5 NZCV Cond) ConsumesFlags)
2717(rule 1 (ccmp_imm size rn imm nzcv cond)
2718      (let ((dst WritableReg (temp_writable_reg $I64)))
2719        (ConsumesFlags.ConsumesFlagsTwiceReturnsValueRegs
2720         (MInst.CCmpImm size rn imm nzcv cond)
2721         (MInst.CSet dst cond)
2722         (value_reg dst))))
2723
2724;; Helpers for generating `add` instructions.
2725(spec (add ty a b)
2726  (provide
2727    (= result
2728       (if (<= ty 32)
2729           (conv_to 64 (bvadd (extract 31 0 a) (extract 31 0 b)))
2730           (bvadd a b)))))
2731(decl add (Type Reg Reg) Reg)
2732(rule (add ty x y) (alu_rrr (ALUOp.Add) ty x y))
2733
2734(spec (add_imm ty a b)
2735  (provide
2736    (= result
2737       (if (<= ty 32)
2738           (conv_to 64 (bvadd (extract 31 0 a) (zero_ext 32 b)))
2739           (bvadd a (zero_ext 64 b)))))
2740  (require
2741      (or
2742            (= b (bvand b #x000fff))
2743            (= b (bvand b #xfff000)))))
2744(decl add_imm (Type Reg Imm12) Reg)
2745(rule (add_imm ty x y) (alu_rr_imm12 (ALUOp.Add) ty x y))
2746
2747(spec (add_extend ty x y)
2748    (provide
2749      (= result
2750         (if (<= ty 32)
2751            (conv_to 64 (bvadd (extract 31 0 x)
2752            (switch (extract 66 64 y)
2753                  ((ExtendOp.UXTB) (zero_ext 32 (extract 7 0 y)))
2754                  ((ExtendOp.UXTH) (zero_ext 32 (extract 15 0 y)))
2755                  ((ExtendOp.UXTW) (zero_ext 32 (extract 31 0 y)))
2756                  ((ExtendOp.UXTX) (zero_ext 32 (extract 31 0 y)))
2757                  ((ExtendOp.SXTB) (sign_ext 32 (extract 7 0 y)))
2758                  ((ExtendOp.SXTH) (sign_ext 32 (extract 15 0 y)))
2759                  ((ExtendOp.SXTW) (sign_ext 32 (extract 31 0 y)))
2760                  ((ExtendOp.SXTX) (sign_ext 32 (extract 31 0 y))))))
2761            (bvadd x
2762            (switch (extract 66 64 y)
2763                  ((ExtendOp.UXTB) (zero_ext 64 (extract 7 0 y)))
2764                  ((ExtendOp.UXTH) (zero_ext 64 (extract 15 0 y)))
2765                  ((ExtendOp.UXTW) (zero_ext 64 (extract 31 0 y)))
2766                  ((ExtendOp.UXTX) (zero_ext 64 (extract 63 0 y)))
2767                  ((ExtendOp.SXTB) (sign_ext 64 (extract 7 0 y)))
2768                  ((ExtendOp.SXTH) (sign_ext 64 (extract 15 0 y)))
2769                  ((ExtendOp.SXTW) (sign_ext 64 (extract 31 0 y)))
2770                  ((ExtendOp.SXTX) (sign_ext 64 (extract 63 0 y)))))))))
2771(decl add_extend (Type Reg ExtendedValue) Reg)
2772(rule (add_extend ty x y) (alu_rr_extend_reg (ALUOp.Add) ty x y))
2773
2774(decl add_extend_op (Type Reg Reg ExtendOp) Reg)
2775(rule (add_extend_op ty x y extend) (alu_rrr_extend (ALUOp.Add) ty x y extend))
2776
2777(spec (add_shift ty a b shift)
2778  (provide
2779    (= result (if (<= ty 32)
2780      (conv_to 64 (bvadd (extract 31 0 a)
2781      (switch (extract 15 8 shift)
2782        ((ALUOp.Lsl) (bvshl (extract 31 0 b) (zero_ext 32 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift)))))
2783        ((ALUOp.Lsr) (bvlshr (extract 31 0 b) (zero_ext 32 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift)))))
2784        ((ALUOp.Asr) (bvashr (extract 31 0 b) (zero_ext 32 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))))))
2785      (bvadd a
2786      (switch (extract 15 8 shift)
2787        ((ALUOp.Lsl) (bvshl b (zero_ext 64 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift)))))
2788        ((ALUOp.Lsr) (bvlshr b (zero_ext 64 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift)))))
2789        ((ALUOp.Asr) (bvashr b (zero_ext 64 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift)))))))))))
2790(decl add_shift (Type Reg Reg ShiftOpAndAmt) Reg)
2791(rule (add_shift ty x y z) (alu_rrr_shift (ALUOp.Add) ty x y z))
2792
2793(decl add_vec (Reg Reg VectorSize) Reg)
2794(rule (add_vec x y size) (vec_rrr (VecALUOp.Add) x y size))
2795
2796;; Helpers for generating `sub` instructions.
2797(spec (sub ty a b)
2798  (provide
2799    (= result
2800       (if (<= ty 32)
2801           (conv_to 64 (bvsub (extract 31 0 a) (extract 31 0 b)))
2802           (bvsub a b)))))
2803(decl sub (Type Reg Reg) Reg)
2804(rule (sub ty x y) (alu_rrr (ALUOp.Sub) ty x y))
2805
2806(spec (sub_imm ty a b)
2807  (provide
2808    (= result
2809       (if (<= ty 32)
2810           (conv_to 64 (bvsub (extract 31 0 a) (zero_ext 32 b)))
2811           (bvsub a (zero_ext 64 b)))))
2812  (require
2813      (or
2814            (= b (bvand b #x000fff))
2815            (= b (bvand b #xfff000)))))
2816(decl sub_imm (Type Reg Imm12) Reg)
2817(rule (sub_imm ty x y) (alu_rr_imm12 (ALUOp.Sub) ty x y))
2818
2819(spec (sub_extend ty x y)
2820    (provide
2821      (= result
2822         (if (<= ty 32)
2823         (conv_to 64 (bvsub (extract 31 0 x)
2824            (switch (extract 66 64 y)
2825              ((ExtendOp.UXTB) (zero_ext 32 (extract 7 0 y)))
2826              ((ExtendOp.UXTH) (zero_ext 32 (extract 15 0 y)))
2827              ((ExtendOp.UXTW) (zero_ext 32 (extract 31 0 y)))
2828              ((ExtendOp.UXTX) (zero_ext 32 (extract 31 0 y)))
2829              ((ExtendOp.SXTB) (sign_ext 32 (extract 7 0 y)))
2830              ((ExtendOp.SXTH) (sign_ext 32 (extract 15 0 y)))
2831              ((ExtendOp.SXTW) (sign_ext 32 (extract 31 0 y)))
2832              ((ExtendOp.SXTX) (sign_ext 32 (extract 31 0 y))))))
2833         (bvsub x
2834            (switch (extract 66 64 y)
2835              ((ExtendOp.UXTB) (zero_ext 64 (extract 7 0 y)))
2836              ((ExtendOp.UXTH) (zero_ext 64 (extract 15 0 y)))
2837              ((ExtendOp.UXTW) (zero_ext 64 (extract 31 0 y)))
2838              ((ExtendOp.UXTX) (zero_ext 64 (extract 63 0 y)))
2839              ((ExtendOp.SXTB) (sign_ext 64 (extract 7 0 y)))
2840              ((ExtendOp.SXTH) (sign_ext 64 (extract 15 0 y)))
2841              ((ExtendOp.SXTW) (sign_ext 64 (extract 31 0 y)))
2842              ((ExtendOp.SXTX) (sign_ext 64 (extract 63 0 y)))))))))
2843(decl sub_extend (Type Reg ExtendedValue) Reg)
2844(rule (sub_extend ty x y) (alu_rr_extend_reg (ALUOp.Sub) ty x y))
2845
2846(spec (sub_shift ty a b shift)
2847  (provide
2848    (= result (if (<= ty 32)
2849      (conv_to 64 (bvsub (extract 31 0 a) (switch (extract 15 8 shift)
2850        ((ALUOp.Lsl) (bvshl (extract 31 0 b) (zero_ext 32 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift)))))
2851        ((ALUOp.Lsr) (bvlshr (extract 31 0 b) (zero_ext 32 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift)))))
2852        ((ALUOp.Asr) (bvashr (extract 31 0 b) (zero_ext 32 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))))))
2853      (bvsub a (switch (extract 15 8 shift)
2854        ((ALUOp.Lsl) (bvshl b (zero_ext 64 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift)))))
2855        ((ALUOp.Lsr) (bvlshr b (zero_ext 64 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift)))))
2856        ((ALUOp.Asr) (bvashr b (zero_ext 64 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift)))))))))))
2857(decl sub_shift (Type Reg Reg ShiftOpAndAmt) Reg)
2858(rule (sub_shift ty x y z) (alu_rrr_shift (ALUOp.Sub) ty x y z))
2859
2860(decl sub_vec (Reg Reg VectorSize) Reg)
2861(rule (sub_vec x y size) (vec_rrr (VecALUOp.Sub) x y size))
2862
2863(decl sub_i128 (ValueRegs ValueRegs) ValueRegs)
2864(rule (sub_i128 x y)
2865      (let
2866          ;; Get the high/low registers for `x`.
2867          ((x_regs ValueRegs x)
2868           (x_lo Reg (value_regs_get x_regs 0))
2869           (x_hi Reg (value_regs_get x_regs 1))
2870
2871           ;; Get the high/low registers for `y`.
2872           (y_regs ValueRegs y)
2873           (y_lo Reg (value_regs_get y_regs 0))
2874           (y_hi Reg (value_regs_get y_regs 1)))
2875        ;; the actual subtraction is `subs` followed by `sbc` which comprises
2876        ;; the low/high bits of the result
2877        (with_flags
2878          (sub_with_flags_paired $I64 x_lo y_lo)
2879          (sbc_paired $I64 x_hi y_hi))))
2880
2881;; Helpers for generating `madd` instructions.
2882(spec (madd ty a b c)
2883  (provide
2884    (= result
2885       (if (<= ty 32)
2886           (conv_to 64 (bvadd (extract 31 0 c) (bvmul (extract 31 0 a) (extract 31 0 b))))
2887           (bvadd c (bvmul a b))))))
2888(decl madd (Type Reg Reg Reg) Reg)
2889(rule (madd ty x y z) (alu_rrrr (ALUOp3.MAdd) ty x y z))
2890
2891;; Helpers for generating `msub` instructions.
2892(spec (msub ty a b c)
2893  (provide
2894    (= result
2895       (if (<= ty 32)
2896           (conv_to 64 (bvsub (extract 31 0 c) (bvmul (extract 31 0 a) (extract 31 0 b))))
2897           (bvsub c (bvmul a b))))))
2898(decl msub (Type Reg Reg Reg) Reg)
2899(rule (msub ty x y z) (alu_rrrr (ALUOp3.MSub) ty x y z))
2900
2901;; Helpers for generating `umaddl` instructions
2902(decl umaddl (Reg Reg Reg) Reg)
2903(rule (umaddl x y z) (alu_rrrr (ALUOp3.UMAddL) $I32 x y z))
2904
2905;; Helpers for generating `smaddl` instructions
2906(decl smaddl (Reg Reg Reg) Reg)
2907(rule (smaddl x y z) (alu_rrrr (ALUOp3.SMAddL) $I32 x y z))
2908
2909;; Helper for generating `uqadd` instructions.
2910(decl uqadd (Reg Reg VectorSize) Reg)
2911(rule (uqadd x y size) (vec_rrr (VecALUOp.Uqadd) x y size))
2912
2913;; Helper for generating `sqadd` instructions.
2914(decl sqadd (Reg Reg VectorSize) Reg)
2915(rule (sqadd x y size) (vec_rrr (VecALUOp.Sqadd) x y size))
2916
2917;; Helper for generating `uqsub` instructions.
2918(decl uqsub (Reg Reg VectorSize) Reg)
2919(rule (uqsub x y size) (vec_rrr (VecALUOp.Uqsub) x y size))
2920
2921;; Helper for generating `sqsub` instructions.
2922(decl sqsub (Reg Reg VectorSize) Reg)
2923(rule (sqsub x y size) (vec_rrr (VecALUOp.Sqsub) x y size))
2924
2925;; Helper for generating `umulh` instructions.
2926(decl umulh (Type Reg Reg) Reg)
2927(rule (umulh ty x y) (alu_rrr (ALUOp.UMulH) ty x y))
2928
2929;; Helper for generating `smulh` instructions.
2930(decl smulh (Type Reg Reg) Reg)
2931(rule (smulh ty x y) (alu_rrr (ALUOp.SMulH) ty x y))
2932
2933;; Helper for generating `mul` instructions.
2934(decl mul (Reg Reg VectorSize) Reg)
2935(rule (mul x y size) (vec_rrr (VecALUOp.Mul) x y size))
2936
2937;; Helper for generating `neg` instructions.
2938(decl neg (Reg VectorSize) Reg)
2939(rule (neg x size) (vec_misc (VecMisc2.Neg) x size))
2940
2941;; Helper for generating `rev16` instructions.
2942(decl rev16 (Reg VectorSize) Reg)
2943(rule (rev16 x size) (vec_misc (VecMisc2.Rev16) x size))
2944
2945;; Helper for generating `rev32` instructions.
2946(decl rev32 (Reg VectorSize) Reg)
2947(rule (rev32 x size) (vec_misc (VecMisc2.Rev32) x size))
2948
2949;; Helper for generating `rev64` instructions.
2950(decl rev64 (Reg VectorSize) Reg)
2951(rule (rev64 x size) (vec_misc (VecMisc2.Rev64) x size))
2952
2953;; Helper for generating `xtn` instructions.
2954(decl xtn (Reg ScalarSize) Reg)
2955(rule (xtn x size) (vec_rr_narrow_low (VecRRNarrowOp.Xtn) x size))
2956
2957;; Helper for generating `fcvtn` instructions.
2958(decl fcvtn (Reg ScalarSize) Reg)
2959(rule (fcvtn x size) (vec_rr_narrow_low (VecRRNarrowOp.Fcvtn) x size))
2960
2961;; Helper for generating `sqxtn` instructions.
2962(decl sqxtn (Reg ScalarSize) Reg)
2963(rule (sqxtn x size) (vec_rr_narrow_low (VecRRNarrowOp.Sqxtn) x size))
2964
2965;; Helper for generating `sqxtn2` instructions.
2966(decl sqxtn2 (Reg Reg ScalarSize) Reg)
2967(rule (sqxtn2 x y size) (vec_rr_narrow_high (VecRRNarrowOp.Sqxtn) x y size))
2968
2969;; Helper for generating `sqxtun` instructions.
2970(decl sqxtun (Reg ScalarSize) Reg)
2971(rule (sqxtun x size) (vec_rr_narrow_low (VecRRNarrowOp.Sqxtun) x size))
2972
2973;; Helper for generating `sqxtun2` instructions.
2974(decl sqxtun2 (Reg Reg ScalarSize) Reg)
2975(rule (sqxtun2 x y size) (vec_rr_narrow_high (VecRRNarrowOp.Sqxtun) x y size))
2976
2977;; Helper for generating `uqxtn` instructions.
2978(decl uqxtn (Reg ScalarSize) Reg)
2979(rule (uqxtn x size) (vec_rr_narrow_low (VecRRNarrowOp.Uqxtn) x size))
2980
2981;; Helper for generating `uqxtn2` instructions.
2982(decl uqxtn2 (Reg Reg ScalarSize) Reg)
2983(rule (uqxtn2 x y size) (vec_rr_narrow_high (VecRRNarrowOp.Uqxtn) x y size))
2984
2985;; Helper for generating `fence` instructions.
2986(decl aarch64_fence () SideEffectNoResult)
2987(rule (aarch64_fence)
2988      (SideEffectNoResult.Inst (MInst.Fence)))
2989
2990;; Helper for generating `csdb` instructions.
2991(decl csdb () SideEffectNoResult)
2992(rule (csdb)
2993      (SideEffectNoResult.Inst (MInst.Csdb)))
2994
2995;; Helper for generating `brk` instructions, hinted as being debug traps.
2996(decl brk () SideEffectNoResult)
2997(rule (brk)
2998      (SideEffectNoResult.Inst (MInst.Brk)))
2999
3000;; Helper for generating `addp` instructions.
3001(spec (addp x y s)
3002  (provide
3003    (= result
3004       (switch s
3005         (#x00 (concat
3006         (bvadd (extract 55 48 x) (extract 63 56 x))
3007         (bvadd (extract 39 32 x) (extract 47 40 x))
3008         (bvadd (extract 23 16 x) (extract 31 24 x))
3009         (bvadd (extract 7   0 x) (extract 15  8 x))
3010         (bvadd (extract 55 48 y) (extract 63 56 y))
3011         (bvadd (extract 39 32 y) (extract 47 40 y))
3012         (bvadd (extract 23 16 y) (extract 31 24 y))
3013         (bvadd (extract 7   0 y) (extract 15  8 y))))
3014         (#x01 (concat
3015         (bvadd (extract 47 32 x) (extract 63 48 x))
3016         (bvadd (extract 15  0 x) (extract 31 16 x))
3017         (bvadd (extract 47 32 y) (extract 63 48 y))
3018         (bvadd (extract 15  0 y) (extract 31 16 y))))
3019         (#x02 (concat
3020         (bvadd (extract 31  0 x) (extract 63 32 x))
3021         (bvadd (extract 31  0 y) (extract 63 32 y)))))))
3022  (require (or (= s #x00) (= s #x01) (= s #x02))))
3023(decl addp (Reg Reg VectorSize) Reg)
3024(rule (addp x y size) (vec_rrr (VecALUOp.Addp) x y size))
3025
3026;; Helper for generating `zip1` instructions.
3027(decl zip1 (Reg Reg VectorSize) Reg)
3028(rule (zip1 x y size) (vec_rrr (VecALUOp.Zip1) x y size))
3029
3030;; Helper for generating vector `abs` instructions.
3031(decl vec_abs (Reg VectorSize) Reg)
3032(rule (vec_abs x size) (vec_misc (VecMisc2.Abs) x size))
3033
3034;; Helper for generating instruction sequences to calculate a scalar absolute
3035;; value.
3036(spec (abs s x)
3037  (provide
3038    (= result
3039       (if (= s 32)
3040           (conv_to 64
3041                    (if (bvsge (extract 31 0 x) #x00000000)
3042                        (extract 31 0 x)
3043                        (bvneg (extract 31 0 x))))
3044           (if (bvsge x #x0000000000000000) x (bvneg x)))))
3045  (require (or (= s 32) (= s 64))))
3046(decl abs (OperandSize Reg) Reg)
3047(rule (abs size x)
3048      (value_regs_get (with_flags (cmp_imm size x (u8_into_imm12 0))
3049                                  (csneg (Cond.Gt) x x)) 0))
3050
3051;; Helper for generating `addv` instructions.
3052(spec (addv x s)
3053  (provide
3054    (= result
3055       (switch s
3056         (#x00 (zero_ext 64
3057         (bvadd (extract 7   0 x)
3058         (bvadd (extract 15  8 x)
3059         (bvadd (extract 23 16 x)
3060         (bvadd (extract 31 24 x)
3061         (bvadd (extract 39 32 x)
3062         (bvadd (extract 47 40 x)
3063         (bvadd (extract 55 48 x)
3064         (extract 63 56 x))))))))))
3065         (#x01 (zero_ext 64
3066         (bvadd (extract 15 0 x)
3067         (bvadd (extract 31 16 x)
3068         (bvadd (extract 47 32 x)
3069         (extract 63 48 x))))))
3070         (#x02 (zero_ext 64
3071         (bvadd (extract 31 0 x)
3072         (extract 63 32 x)))))))
3073  (require (or (= s #x00) (or (= s #x01) (= s #x02)))))
3074(decl addv (Reg VectorSize) Reg)
3075(rule (addv x size) (vec_lanes (VecLanesOp.Addv) x size))
3076
3077;; Helper for generating `shll32` instructions.
3078(decl shll32 (Reg bool) Reg)
3079(rule (shll32 x high_half) (vec_rr_long (VecRRLongOp.Shll32) x high_half))
3080
3081;; Helpers for generating `addlp` instructions.
3082
3083(decl saddlp8 (Reg) Reg)
3084(rule (saddlp8 x) (vec_rr_pair_long (VecRRPairLongOp.Saddlp8) x))
3085
3086(decl saddlp16 (Reg) Reg)
3087(rule (saddlp16 x) (vec_rr_pair_long (VecRRPairLongOp.Saddlp16) x))
3088
3089(decl uaddlp8 (Reg) Reg)
3090(rule (uaddlp8 x) (vec_rr_pair_long (VecRRPairLongOp.Uaddlp8) x))
3091
3092(decl uaddlp16 (Reg) Reg)
3093(rule (uaddlp16 x) (vec_rr_pair_long (VecRRPairLongOp.Uaddlp16) x))
3094
3095;; Helper for generating `umlal32` instructions.
3096(decl umlal32 (Reg Reg Reg bool) Reg)
3097(rule (umlal32 x y z high_half) (vec_rrrr_long (VecRRRLongModOp.Umlal32) x y z high_half))
3098
3099;; Helper for generating `smull8` instructions.
3100(decl smull8 (Reg Reg bool) Reg)
3101(rule (smull8 x y high_half) (vec_rrr_long (VecRRRLongOp.Smull8) x y high_half))
3102
3103;; Helper for generating `umull8` instructions.
3104(decl umull8 (Reg Reg bool) Reg)
3105(rule (umull8 x y high_half) (vec_rrr_long (VecRRRLongOp.Umull8) x y high_half))
3106
3107;; Helper for generating `smull16` instructions.
3108(decl smull16 (Reg Reg bool) Reg)
3109(rule (smull16 x y high_half) (vec_rrr_long (VecRRRLongOp.Smull16) x y high_half))
3110
3111;; Helper for generating `umull16` instructions.
3112(decl umull16 (Reg Reg bool) Reg)
3113(rule (umull16 x y high_half) (vec_rrr_long (VecRRRLongOp.Umull16) x y high_half))
3114
3115;; Helper for generating `smull32` instructions.
3116(decl smull32 (Reg Reg bool) Reg)
3117(rule (smull32 x y high_half) (vec_rrr_long (VecRRRLongOp.Smull32) x y high_half))
3118
3119;; Helper for generating `umull32` instructions.
3120(decl umull32 (Reg Reg bool) Reg)
3121(rule (umull32 x y high_half) (vec_rrr_long (VecRRRLongOp.Umull32) x y high_half))
3122
3123;; Helper for generating `asr` instructions.
3124(decl asr (Type Reg Reg) Reg)
3125(rule (asr ty x y) (alu_rrr (ALUOp.Asr) ty x y))
3126
3127(decl asr_imm (Type Reg ImmShift) Reg)
3128(rule (asr_imm ty x imm) (alu_rr_imm_shift (ALUOp.Asr) ty x imm))
3129
3130;; Helper for generating `lsr` instructions.
3131(spec (lsr ty a b)
3132  (provide
3133    (= result
3134       (switch ty
3135         (32 (conv_to 64 (bvlshr (extract 31 0 a) (extract 31 0 b))))
3136         (64 (bvlshr a b))))))
3137(decl lsr (Type Reg Reg) Reg)
3138(rule (lsr ty x y) (alu_rrr (ALUOp.Lsr) ty x y))
3139
3140(spec (lsr_imm ty a b)
3141  (provide
3142    (= result
3143       (switch ty
3144         (32 (conv_to 64 (bvlshr (extract 31 0 a) (zero_ext 32 b))))
3145         (64 (bvlshr a (zero_ext 64 b)))))))
3146(decl lsr_imm (Type Reg ImmShift) Reg)
3147(rule (lsr_imm ty x imm) (alu_rr_imm_shift (ALUOp.Lsr) ty x imm))
3148
3149;; Helper for generating `lsl` instructions.
3150(spec (lsl ty a b)
3151  (provide
3152    (= result
3153       (switch ty
3154         (32 (conv_to 64 (bvshl (extract 31 0 a) (extract 31 0 b))))
3155         (64 (bvshl a b))))))
3156(decl lsl (Type Reg Reg) Reg)
3157(rule (lsl ty x y) (alu_rrr (ALUOp.Lsl) ty x y))
3158
3159(spec (lsl_imm ty a b)
3160  (provide
3161    (= result
3162       (switch ty
3163         (32 (conv_to 64 (bvshl (extract 31 0 a) (zero_ext 32 b))))
3164         (64 (bvshl a (zero_ext 64 b)))))))
3165(decl lsl_imm (Type Reg ImmShift) Reg)
3166(rule (lsl_imm ty x imm) (alu_rr_imm_shift (ALUOp.Lsl) ty x imm))
3167
3168;; Helper for generating `udiv` instructions.
3169(spec (a64_udiv ty a b)
3170  (provide
3171    (= result
3172       (if (<= ty 32)
3173           (conv_to 64 (bvudiv (extract 31 0 a) (extract 31 0 b)))
3174           (bvudiv a b)))))
3175(decl a64_udiv (Type Reg Reg) Reg)
3176(rule (a64_udiv ty x y) (alu_rrr (ALUOp.UDiv) ty x y))
3177
3178;; Helper for generating `sdiv` instructions.
3179(spec (a64_sdiv ty a b)
3180  (provide
3181    (= result
3182       (if (<= ty 32)
3183           (conv_to 64 (bvsdiv (extract 31 0 a) (extract 31 0 b)))
3184           (bvsdiv a b)))))
3185(decl a64_sdiv (Type Reg Reg) Reg)
3186(rule (a64_sdiv ty x y) (alu_rrr (ALUOp.SDiv) ty x y))
3187
3188;; Helper for generating `not` instructions.
3189(decl not (Reg VectorSize) Reg)
3190(rule (not x size) (vec_misc (VecMisc2.Not) x size))
3191
3192;; Helpers for generating `orr_not` instructions.
3193(spec (orr_not ty a b)
3194  (provide
3195    (= result
3196       (if (<= ty 32)
3197           (conv_to 64 (bvor (extract 31 0 a) (bvnot (extract 31 0 b))))
3198           (bvor a (bvnot b))))))
3199(decl orr_not (Type Reg Reg) Reg)
3200(rule (orr_not ty x y) (alu_rrr (ALUOp.OrrNot) ty x y))
3201
3202(spec (orr_not_shift ty a b shift)
3203  (provide
3204    (= result (if (<= ty 32)
3205      (conv_to 64 (bvor a (bvnot (bvshl b (zero_ext 64 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift)))))))
3206      (bvor a (bvnot (bvshl b (zero_ext 64 (bvand (bvsub (int2bv 8 ty) #x01) (extract 7 0 shift))))))))))
3207(decl orr_not_shift (Type Reg Reg ShiftOpAndAmt) Reg)
3208(rule (orr_not_shift ty x y shift) (alu_rrr_shift (ALUOp.OrrNot) ty x y shift))
3209
3210;; Helpers for generating `orr` instructions.
3211(spec (orr ty a b)
3212  (provide
3213    (= result
3214       (if (<= ty 32)
3215         (conv_to 64 (bvor (extract 31 0 a) (extract 31 0 b)))
3216         (bvor a b))))
3217  (require (or (= ty 8) (= ty 16) (= ty 32) (= ty 64))))
3218(decl orr (Type Reg Reg) Reg)
3219(rule (orr ty x y) (alu_rrr (ALUOp.Orr) ty x y))
3220
3221(spec (orr_imm ty x y)
3222  (provide
3223    (= result
3224       (switch ty
3225         (32 (conv_to 64 (bvor (extract 31 0 x) (extract 31 0 y))))
3226         (64 (bvor x (zero_ext 64 y))))))
3227  (require
3228      (or
3229            (= y (bvand y #x0000000000000fff))
3230            (= y (bvand y #x0000000000fff000)))))
3231(decl orr_imm (Type Reg ImmLogic) Reg)
3232(rule (orr_imm ty x y) (alu_rr_imm_logic (ALUOp.Orr) ty x y))
3233
3234(decl orr_shift (Type Reg Reg ShiftOpAndAmt) Reg)
3235(rule (orr_shift ty x y shift) (alu_rrr_shift (ALUOp.Orr) ty x y shift))
3236
3237(decl orr_vec (Reg Reg VectorSize) Reg)
3238(rule (orr_vec x y size) (vec_rrr (VecALUOp.Orr) x y size))
3239
3240(decl orn_vec (Reg Reg VectorSize) Reg)
3241(rule (orn_vec x y size) (vec_rrr (VecALUOp.Orn) x y size))
3242
3243;; Helpers for generating `and` instructions.
3244(spec (and_reg ty a b)
3245  (provide
3246    (= result
3247       (if (<= ty 32)
3248         (conv_to 64 (bvand (extract 31 0 a) (extract 31 0 b)))
3249         (bvand a b))))
3250  (require (or (= ty 8) (= ty 16) (= ty 32) (= ty 64))))
3251(decl and_reg (Type Reg Reg) Reg)
3252(rule (and_reg ty x y) (alu_rrr (ALUOp.And) ty x y))
3253
3254(spec (and_imm ty x y)
3255  (provide
3256    (= result
3257       (switch ty
3258         (32 (conv_to 64 (bvand (extract 31 0 x) (extract 31 0 y))))
3259         (64 (bvand x (zero_ext 64 y))))))
3260  (require
3261      (or
3262            (= y (bvand y #x0000000000000fff))
3263            (= y (bvand y #x0000000000fff000)))))
3264(decl and_imm (Type Reg ImmLogic) Reg)
3265(rule (and_imm ty x y) (alu_rr_imm_logic (ALUOp.And) ty x y))
3266
3267(decl and_vec (Reg Reg VectorSize) Reg)
3268(rule (and_vec x y size) (vec_rrr (VecALUOp.And) x y size))
3269
3270;; Helpers for generating `eor` instructions.
3271(decl eor (Type Reg Reg) Reg)
3272(rule (eor ty x y) (alu_rrr (ALUOp.Eor) ty x y))
3273
3274(decl eor_vec (Reg Reg VectorSize) Reg)
3275(rule (eor_vec x y size) (vec_rrr (VecALUOp.Eor) x y size))
3276
3277;; Helpers for generating `bic` instructions.
3278(spec (bic ty a b)
3279  (provide
3280    (= result
3281       (if (<= ty 32)
3282         (conv_to 64 (bvand (extract 31 0 a) (bvnot (extract 31 0 b))))
3283         (bvand a (bvnot b))
3284       )
3285    ))
3286  (require (or (= ty 8) (= ty 16) (= ty 32) (= ty 64))))
3287(decl bic (Type Reg Reg) Reg)
3288(rule (bic ty x y) (alu_rrr (ALUOp.AndNot) ty x y))
3289
3290(decl bic_vec (Reg Reg VectorSize) Reg)
3291(rule (bic_vec x y size) (vec_rrr (VecALUOp.Bic) x y size))
3292
3293;; Helpers for generating `sshl` instructions.
3294(decl sshl (Reg Reg VectorSize) Reg)
3295(rule (sshl x y size) (vec_rrr (VecALUOp.Sshl) x y size))
3296
3297;; Helpers for generating `ushl` instructions.
3298(decl ushl (Reg Reg VectorSize) Reg)
3299(rule (ushl x y size) (vec_rrr (VecALUOp.Ushl) x y size))
3300
3301;; Helpers for generating `ushl` instructions.
3302(decl ushl_vec_imm (Reg u8 VectorSize) Reg)
3303(rule (ushl_vec_imm x amt size) (vec_shift_imm (VecShiftImmOp.Shl) amt x size))
3304
3305;; Helpers for generating `ushr` instructions.
3306(decl ushr_vec_imm (Reg u8 VectorSize) Reg)
3307(rule (ushr_vec_imm x amt size) (vec_shift_imm (VecShiftImmOp.Ushr) amt x size))
3308
3309;; Helpers for generating `sshr` instructions.
3310(decl sshr_vec_imm (Reg u8 VectorSize) Reg)
3311(rule (sshr_vec_imm x amt size) (vec_shift_imm (VecShiftImmOp.Sshr) amt x size))
3312
3313;; Helpers for generating `rotr` instructions.
3314;;
3315;; Note that the `Extr` opcode is used here as `rotr` is an alias for that
3316;; instruction where two operands are the same register.
3317(spec (a64_rotr ty x y)
3318  (provide
3319    (= result
3320       (if (= ty 32)
3321           (zero_ext 64 (rotr (extract 31 0 x) (extract 31 0 y)))
3322           (rotr x y))))
3323  (require (or (= ty 32) (= ty 64))))
3324(decl a64_rotr (Type Reg Reg) Reg)
3325(rule (a64_rotr ty x y) (alu_rrr (ALUOp.Extr) ty x y))
3326
3327(spec (a64_rotr_imm ty x y)
3328  (provide
3329    (= result
3330       (if (= ty 32)
3331           (zero_ext 64 (rotr (extract 31 0 x) (zero_ext 32 y)))
3332           (rotr x (zero_ext 64 y)))))
3333  (require (or (= ty 32) (= ty 64))))
3334(decl a64_rotr_imm (Type Reg ImmShift) Reg)
3335(rule (a64_rotr_imm ty x y) (alu_rr_imm_shift (ALUOp.Extr) ty x y))
3336
3337;; Helpers for generating `extr` instructions
3338(decl a64_extr (Type Reg Reg ImmShift) Reg)
3339(rule (a64_extr ty x y shift) (alu_rrr_shift (ALUOp.Extr) ty x y (a64_extr_imm ty shift)))
3340(decl a64_extr_imm (Type ImmShift) ShiftOpAndAmt)
3341(extern constructor a64_extr_imm a64_extr_imm)
3342
3343;; Helpers for generating `rbit` instructions.
3344(spec (rbit ty a)
3345  (provide
3346    (= result
3347       (if (= ty 32)
3348           (conv_to 64 (rev (extract 31 0 a)))
3349           (rev a))))
3350  (require (or (= ty 32) (= ty 64))))
3351(decl rbit (Type Reg) Reg)
3352(rule (rbit ty x) (bit_rr (BitOp.RBit) ty x))
3353
3354;; Helpers for generating `clz` instructions.
3355(spec (a64_clz ty a)
3356  (provide
3357    (= result
3358       (if (= ty 32)
3359           (conv_to 64 (clz (extract 31 0 a)))
3360           (clz a))))
3361  (require (or (= ty 32) (= ty 64))))
3362(decl a64_clz (Type Reg) Reg)
3363(rule (a64_clz ty x) (bit_rr (BitOp.Clz) ty x))
3364
3365;; Helpers for generating `cls` instructions.
3366(spec (a64_cls ty a)
3367  (provide
3368    (= result
3369       (if (= ty 32)
3370           (conv_to 64 (cls (extract 31 0 a)))
3371           (cls a))))
3372  (require (or (= ty 32) (= ty 64))))
3373(decl a64_cls (Type Reg) Reg)
3374(rule (a64_cls ty x) (bit_rr (BitOp.Cls) ty x))
3375
3376;; Helpers for generating `rev` instructions
3377
3378(decl a64_rev16 (Type Reg) Reg)
3379(rule (a64_rev16 ty x) (bit_rr (BitOp.Rev16) ty x))
3380
3381(decl a64_rev32 (Type Reg) Reg)
3382(rule (a64_rev32 ty x) (bit_rr (BitOp.Rev32) ty x))
3383
3384(decl a64_rev64 (Type Reg) Reg)
3385(rule (a64_rev64 ty x) (bit_rr (BitOp.Rev64) ty x))
3386
3387;; Helpers for generating `eon` instructions.
3388
3389(decl eon (Type Reg Reg) Reg)
3390(rule (eon ty x y) (alu_rrr (ALUOp.EorNot) ty x y))
3391
3392;; Helpers for generating `cnt` instructions.
3393(spec (vec_cnt x s)
3394  (provide
3395    (= result
3396       (switch s
3397         ((VectorSize.Size8x8)
3398           (concat
3399             (popcnt (extract 63 56 x))
3400             (popcnt (extract 55 48 x))
3401             (popcnt (extract 47 40 x))
3402             (popcnt (extract 39 32 x))
3403             (popcnt (extract 31 24 x))
3404             (popcnt (extract 23 16 x))
3405             (popcnt (extract 15  8 x))
3406             (popcnt (extract 7   0 x))))
3407         ((VectorSize.Size16x4) result)
3408         ((VectorSize.Size32x2) result))))
3409  (require
3410    (or (= s (VectorSize.Size8x8)) (= s (VectorSize.Size16x4)) (= s (VectorSize.Size32x2)))))
3411(decl vec_cnt (Reg VectorSize) Reg)
3412(rule (vec_cnt x size) (vec_misc (VecMisc2.Cnt) x size))
3413
3414;; Helpers for generating a `bsl` instruction.
3415
3416(decl bsl (Type Reg Reg Reg) Reg)
3417(rule (bsl ty c x y)
3418      (vec_rrr_mod (VecALUModOp.Bsl) c x y (vector_size ty)))
3419
3420;; Helper for generating a `udf` instruction.
3421
3422(decl udf (TrapCode) SideEffectNoResult)
3423(rule (udf trap_code)
3424      (SideEffectNoResult.Inst (MInst.Udf trap_code)))
3425
3426;; Helpers for generating various load instructions, with varying
3427;; widths and sign/zero-extending properties.
3428(decl aarch64_uload8 (AMode MemFlags) Reg)
3429(spec (aarch64_uload8 amode flags)
3430      (provide (= result (zero_ext 32 (load_effect flags 8 amode))))
3431      (require (= 32 (widthof result))))
3432(rule (aarch64_uload8 amode flags)
3433      (let ((dst WritableReg (temp_writable_reg $I64))
3434            (_ Unit (emit (MInst.ULoad8 dst amode flags))))
3435        dst))
3436(decl aarch64_sload8 (AMode MemFlags) Reg)
3437(rule (aarch64_sload8 amode flags)
3438      (let ((dst WritableReg (temp_writable_reg $I64))
3439            (_ Unit (emit (MInst.SLoad8 dst amode flags))))
3440        dst))
3441(decl aarch64_uload16 (AMode MemFlags) Reg)
3442(spec (aarch64_uload16 amode flags)
3443      (provide (= result (zero_ext 32 (load_effect flags 16 amode))))
3444      (require (= 32 (widthof result))))
3445(rule (aarch64_uload16 amode flags)
3446      (let ((dst WritableReg (temp_writable_reg $I64))
3447            (_ Unit (emit (MInst.ULoad16 dst amode flags))))
3448        dst))
3449(decl aarch64_sload16 (AMode MemFlags) Reg)
3450(rule (aarch64_sload16 amode flags)
3451      (let ((dst WritableReg (temp_writable_reg $I64))
3452            (_ Unit (emit (MInst.SLoad16 dst amode flags))))
3453        dst))
3454(decl aarch64_uload32 (AMode MemFlags) Reg)
3455(spec (aarch64_uload32 amode flags)
3456      (provide (= result (load_effect flags 32 amode)))
3457      (require (= 32 (widthof result))))
3458(rule (aarch64_uload32 amode flags)
3459      (let ((dst WritableReg (temp_writable_reg $I64))
3460            (_ Unit (emit (MInst.ULoad32 dst amode flags))))
3461        dst))
3462(decl aarch64_sload32 (AMode MemFlags) Reg)
3463(rule (aarch64_sload32 amode flags)
3464      (let ((dst WritableReg (temp_writable_reg $I64))
3465            (_ Unit (emit (MInst.SLoad32 dst amode flags))))
3466        dst))
3467(decl aarch64_uload64 (AMode MemFlags) Reg)
3468(spec (aarch64_uload64 amode flags)
3469      (provide (= result (load_effect flags 64 amode)))
3470      (require (= 64 (widthof result))))
3471(rule (aarch64_uload64 amode flags)
3472      (let ((dst WritableReg (temp_writable_reg $I64))
3473            (_ Unit (emit (MInst.ULoad64 dst amode flags))))
3474        dst))
3475(decl aarch64_fpuload16 (AMode MemFlags) Reg)
3476(rule (aarch64_fpuload16 amode flags)
3477      (let ((dst WritableReg (temp_writable_reg $F64))
3478            (_ Unit (emit (MInst.FpuLoad16 dst amode flags))))
3479        dst))
3480(decl aarch64_fpuload32 (AMode MemFlags) Reg)
3481(rule (aarch64_fpuload32 amode flags)
3482      (let ((dst WritableReg (temp_writable_reg $F64))
3483            (_ Unit (emit (MInst.FpuLoad32 dst amode flags))))
3484        dst))
3485(decl aarch64_fpuload64 (AMode MemFlags) Reg)
3486(rule (aarch64_fpuload64 amode flags)
3487      (let ((dst WritableReg (temp_writable_reg $F64))
3488            (_ Unit (emit (MInst.FpuLoad64 dst amode flags))))
3489        dst))
3490(decl aarch64_fpuload128 (AMode MemFlags) Reg)
3491(rule (aarch64_fpuload128 amode flags)
3492      (let ((dst WritableReg (temp_writable_reg $F64X2))
3493            (_ Unit (emit (MInst.FpuLoad128 dst amode flags))))
3494        dst))
3495(decl aarch64_loadp64 (PairAMode MemFlags) ValueRegs)
3496(rule (aarch64_loadp64 amode flags)
3497      (let ((dst1 WritableReg (temp_writable_reg $I64))
3498            (dst2 WritableReg (temp_writable_reg $I64))
3499            (_ Unit (emit (MInst.LoadP64 dst1 dst2 amode flags))))
3500        (value_regs dst1 dst2)))
3501
3502;; Helpers for generating various store instructions with varying
3503;; widths.
3504(decl aarch64_store8 (AMode MemFlags Reg) SideEffectNoResult)
3505(spec (aarch64_store8 amode flags val)
3506      (provide (= result (store_effect flags 8 (extract 7 0 val) amode))))
3507(rule (aarch64_store8 amode flags val)
3508      (SideEffectNoResult.Inst (MInst.Store8 val amode flags)))
3509(decl aarch64_store16 (AMode MemFlags Reg) SideEffectNoResult)
3510(spec (aarch64_store16 amode flags val)
3511      (provide (= result (store_effect flags 16 (extract 15 0 val) amode))))
3512(rule (aarch64_store16 amode flags val)
3513      (SideEffectNoResult.Inst (MInst.Store16 val amode flags)))
3514(decl aarch64_store32 (AMode MemFlags Reg) SideEffectNoResult)
3515(spec (aarch64_store32 amode flags val)
3516      (provide (= result (store_effect flags 32 (extract 31 0 val) amode))))
3517(rule (aarch64_store32 amode flags val)
3518      (SideEffectNoResult.Inst (MInst.Store32 val amode flags)))
3519(decl aarch64_store64 (AMode MemFlags Reg) SideEffectNoResult)
3520(spec (aarch64_store64 amode flags val)
3521      (provide (= result (store_effect flags 64 val amode))))
3522(rule (aarch64_store64 amode flags val)
3523      (SideEffectNoResult.Inst (MInst.Store64 val amode flags)))
3524(decl aarch64_fpustore16 (AMode MemFlags Reg) SideEffectNoResult)
3525(rule (aarch64_fpustore16 amode flags val)
3526      (SideEffectNoResult.Inst (MInst.FpuStore16 val amode flags)))
3527(decl aarch64_fpustore32 (AMode MemFlags Reg) SideEffectNoResult)
3528(rule (aarch64_fpustore32 amode flags val)
3529      (SideEffectNoResult.Inst (MInst.FpuStore32 val amode flags)))
3530(decl aarch64_fpustore64 (AMode MemFlags Reg) SideEffectNoResult)
3531(rule (aarch64_fpustore64 amode flags val)
3532      (SideEffectNoResult.Inst (MInst.FpuStore64 val amode flags)))
3533(decl aarch64_fpustore128 (AMode MemFlags Reg) SideEffectNoResult)
3534(rule (aarch64_fpustore128 amode flags val)
3535      (SideEffectNoResult.Inst (MInst.FpuStore128 val amode flags)))
3536(decl aarch64_storep64 (PairAMode MemFlags Reg Reg) SideEffectNoResult)
3537(rule (aarch64_storep64 amode flags val1 val2)
3538      (SideEffectNoResult.Inst (MInst.StoreP64 val1 val2 amode flags)))
3539
3540;; Helper for generating a `trapif` instruction.
3541
3542(decl trap_if (ProducesFlags TrapCode Cond) InstOutput)
3543(rule (trap_if flags trap_code cond)
3544      (side_effect
3545       (with_flags_side_effect flags
3546        (ConsumesFlags.ConsumesFlagsSideEffect
3547         (MInst.TrapIf (cond_br_cond cond) trap_code)))))
3548
3549;; Helpers for lowering `trapz` and `trapnz`.
3550(type ZeroCond
3551      (enum
3552       Zero
3553       NonZero))
3554
3555(decl zero_cond_to_cond_br (ZeroCond Reg OperandSize) CondBrKind)
3556(rule (zero_cond_to_cond_br (ZeroCond.Zero) reg size)
3557      (cond_br_zero reg size))
3558
3559(rule (zero_cond_to_cond_br (ZeroCond.NonZero) reg size)
3560      (cond_br_not_zero reg size))
3561
3562(decl trap_if_val (ZeroCond Value TrapCode) InstOutput)
3563(rule (trap_if_val zero_cond val @ (value_type (fits_in_64 _)) trap_code)
3564      (let ((reg Reg (put_in_reg_zext64 val)))
3565      (side_effect
3566       (SideEffectNoResult.Inst
3567        (MInst.TrapIf (zero_cond_to_cond_br zero_cond reg (operand_size $I64)) trap_code)))))
3568
3569(rule -1 (trap_if_val zero_cond val @ (value_type $I128) trap_code)
3570  (let ((c ValueRegs (put_in_regs val))
3571        (c_lo Reg (value_regs_get c 0))
3572        (c_hi Reg (value_regs_get c 1))
3573        (c_test Reg (orr $I64 c_lo c_hi)))
3574      (side_effect
3575       (SideEffectNoResult.Inst
3576        (MInst.TrapIf (zero_cond_to_cond_br zero_cond c_test (operand_size $I64)) trap_code)))))
3577
3578;; Immediate value helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3579
3580;; Type of extension performed by an immediate helper
3581(model ImmExtend
3582  (enum
3583    (Sign #b0)
3584    (Zero #b1)))
3585(type ImmExtend
3586  (enum
3587    (Sign)
3588    (Zero)))
3589
3590;; Arguments:
3591;; * Immediate type
3592;; * Way to extend the immediate value to the full width of the destination
3593;;   register
3594;; * Immediate value - only the bits that fit within the type are used and
3595;;   extended, while the rest are ignored
3596;;
3597;; Note that, unlike the convention in the AArch64 backend, this helper leaves
3598;; all bits in the destination register in a defined state, i.e. smaller types
3599;; such as `I8` are either sign- or zero-extended.
3600(spec (imm ty ext x)
3601  (provide
3602    (= result
3603       (switch ty
3604         (8 (if (= ext #b1) (zero_ext 64 (extract 7 0 x)) (sign_ext 64 (extract 7 0 x))))
3605         (16 (if (= ext #b1) (zero_ext 64 (extract 15 0 x)) (sign_ext 64 (extract 15 0 x))))
3606         (32 (if (= ext #b1) (zero_ext 64 (extract 32 0 x)) (sign_ext 64 (extract 32 0 x))))
3607         (64 x))))
3608  (require (or (= ty 8) (= ty 16) (= ty 32) (= ty 64))))
3609(instantiate imm
3610    ((args Int (bv 64)) (ret (bv 64)) (canon (bv 8)))
3611    ((args Int (bv 64)) (ret (bv 64)) (canon (bv 16)))
3612    ((args Int (bv 64)) (ret (bv 64)) (canon (bv 32)))
3613    ((args Int (bv 64)) (ret (bv 64)) (canon (bv 64)))
3614)
3615(decl imm (Type ImmExtend u64) Reg)
3616
3617;; Move wide immediate instructions; to simplify, we only match when we
3618;; are zero-extending the value.
3619(rule 3 (imm (integral_ty ty) (ImmExtend.Zero) k)
3620      (if-let n (move_wide_const_from_u64 ty k))
3621      (add_range_fact
3622       (movz n (operand_size ty))
3623       64 k k))
3624(rule 2 (imm (integral_ty (ty_32_or_64 ty)) (ImmExtend.Zero) k)
3625      (if-let n (move_wide_const_from_inverted_u64 ty k))
3626      (add_range_fact
3627       (movn n (operand_size ty))
3628       64 k k))
3629
3630;; Weird logical-instruction immediate in ORI using zero register; to simplify,
3631;; we only match when we are zero-extending the value.
3632(rule 1 (imm (integral_ty ty) (ImmExtend.Zero) k)
3633      (if-let n (imm_logic_from_u64 ty k))
3634      (if-let m (imm_size_from_type ty))
3635      (add_range_fact
3636       (orr_imm ty (zero_reg) n)
3637       m k k))
3638
3639(decl load_constant_full (Type ImmExtend OperandSize u64) Reg)
3640(extern constructor load_constant_full load_constant_full)
3641
3642;; Fallback for integral 32-bit constants
3643(rule (imm (fits_in_32 (integral_ty ty)) extend n)
3644      (load_constant_full ty extend (operand_size $I32) n))
3645
3646;; Fallback for integral 64-bit constants
3647(rule -1 (imm (integral_ty $I64) extend n)
3648      (load_constant_full $I64 extend (operand_size $I64) n))
3649
3650
3651;; Sign extension helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3652
3653;; Place a `Value` into a register, sign extending it to 32-bits
3654(spec (put_in_reg_sext32 arg)
3655  (provide
3656    (= result
3657       (if (<= (widthof arg) 32)
3658           (conv_to 64 (sign_ext 32 arg))
3659           (conv_to 64 arg)))))
3660(decl put_in_reg_sext32 (Value) Reg)
3661(rule -1 (put_in_reg_sext32 val @ (value_type (fits_in_32 ty)))
3662      (extend val true (ty_bits ty) 32))
3663
3664;; 32/64-bit passthrough.
3665(rule (put_in_reg_sext32 val @ (value_type $I32)) val)
3666(rule (put_in_reg_sext32 val @ (value_type $I64)) val)
3667
3668;; Place a `Value` into a register, zero extending it to 32-bits
3669(spec (put_in_reg_zext32 arg)
3670  (provide
3671    (= result
3672       (if (<= (widthof arg) 32)
3673           (conv_to 64 (zero_ext 32 arg))
3674           (conv_to 64 arg)))))
3675(decl put_in_reg_zext32 (Value) Reg)
3676(rule -1 (put_in_reg_zext32 val @ (value_type (fits_in_32 ty)))
3677      (extend val false (ty_bits ty) 32))
3678
3679;; 32/64-bit passthrough.
3680(rule (put_in_reg_zext32 val @ (value_type $I32)) val)
3681(rule (put_in_reg_zext32 val @ (value_type $I64)) val)
3682
3683;; Place a `Value` into a register, sign extending it to 64-bits
3684(spec (put_in_reg_sext64 x)
3685      (provide (= (sign_ext 64 x) result)))
3686(decl put_in_reg_sext64 (Value) Reg)
3687(rule 1 (put_in_reg_sext64 val @ (value_type (fits_in_32 ty)))
3688      (extend val true (ty_bits ty) 64))
3689
3690;; 64-bit passthrough.
3691(rule (put_in_reg_sext64 val @ (value_type $I64)) val)
3692
3693;; Place a `Value` into a register, zero extending it to 64-bits
3694(spec (put_in_reg_zext64 x)
3695      (provide (= result (zero_ext 64 x))))
3696(decl put_in_reg_zext64 (Value) Reg)
3697(rule 1 (put_in_reg_zext64 val @ (value_type (fits_in_32 ty)))
3698      (extend val false (ty_bits ty) 64))
3699
3700;; 64-bit passthrough.
3701(rule (put_in_reg_zext64 val @ (value_type $I64)) val)
3702
3703;; Misc instruction helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3704
3705(decl trap_if_zero_divisor (Reg OperandSize) Reg)
3706(rule (trap_if_zero_divisor reg size)
3707      (let ((_ Unit (emit (MInst.TrapIf (cond_br_zero reg size ) (trap_code_division_by_zero)))))
3708        reg))
3709
3710(decl size_from_ty (Type) OperandSize)
3711(rule 1 (size_from_ty (fits_in_32 _ty)) (OperandSize.Size32))
3712(rule (size_from_ty $I64) (OperandSize.Size64))
3713
3714;; Check for signed overflow. The only case is min_value / -1.
3715;; The following checks must be done in 32-bit or 64-bit, depending
3716;; on the input type. For 8- and 16- bit, the check for x == min_value
3717;; must use a possibly-shifted value, xcheck, to overflow as expected.
3718(decl trap_if_div_overflow (Type Reg Reg Reg) Reg)
3719(rule (trap_if_div_overflow ty xcheck x y)
3720      (let (
3721          ;; Check RHS is -1.
3722          (_ Unit (emit (MInst.AluRRImm12 (ALUOp.AddS) (operand_size ty) (writable_zero_reg) y (u8_into_imm12 1))))
3723
3724          ;; Check LHS is min_value, by subtracting 1 from the possibly-shifted
3725          ;; value and branching if there is overflow.
3726          (_ Unit (emit (MInst.CCmpImm (size_from_ty ty)
3727                                       xcheck
3728                                       (u8_into_uimm5 1)
3729                                       (nzcv false false false false)
3730                                       (Cond.Eq))))
3731          (_ Unit (emit (MInst.TrapIf (cond_br_cond (Cond.Vs))
3732                                      (trap_code_integer_overflow))))
3733        )
3734        x))
3735
3736;; In the cases narrower than a register width, subtracting 1 from the
3737;; min_value will not cause overflow (e.g., I8's min_value of -128 stored in
3738;; a 32-bit register produces -129 with no overflow). However, if we left shift
3739;; x by (32 - ty), we then produce the 32-bit min_value for the respective min
3740;; values of I8 and I16.
3741;; E.g., I8's 0x00000080 left-shifted by 24 is 0x80000000, which overflows.
3742(decl intmin_check (Type Reg) Reg)
3743(rule intmin_check_fits_in_16 (intmin_check (fits_in_16 ty) x)
3744      (alu_rr_imm_shift (ALUOp.Lsl) ty x (imm_shift_from_u8 (diff_from_32 ty))))
3745
3746;; In the I32 or I64 case, checking x itself against the min_value is fine.
3747(rule -1 (intmin_check ty x) x)
3748
3749;; Check for unsigned overflow.
3750(decl trap_if_overflow (ProducesFlags TrapCode) Reg)
3751(rule (trap_if_overflow producer tc)
3752      (with_flags_reg
3753        producer
3754        (ConsumesFlags.ConsumesFlagsSideEffect
3755          (MInst.TrapIf (cond_br_cond (Cond.Hs)) tc))))
3756
3757(decl sink_atomic_load (Inst) Reg)
3758(rule (sink_atomic_load x @ (atomic_load _ addr))
3759      (let ((_ Unit (sink_inst x)))
3760           (put_in_reg addr)))
3761
3762;; Helper for generating either an `AluRRR`, `AluRRRShift`, or `AluRRImmLogic`
3763;; instruction depending on the input. Note that this requires that the `ALUOp`
3764;; specified is commutative.
3765(spec (alu_rs_imm_logic_commutative op t a b)
3766    (provide
3767      (= result
3768         (conv_to 64
3769           (switch op
3770             ((ALUOp.Orr) (bvor a b))
3771             ((ALUOp.And) (bvand a b))
3772             ((ALUOp.Eor) (bvxor a b)))))))
3773(decl alu_rs_imm_logic_commutative (ALUOp Type Value Value) Reg)
3774
3775;; Base case of operating on registers.
3776(rule -1 (alu_rs_imm_logic_commutative op ty x y)
3777      (alu_rrr op ty x y))
3778
3779;; Special cases for when one operand is a constant.
3780(rule (alu_rs_imm_logic_commutative op ty x (iconst k))
3781      (if-let imm (imm_logic_from_imm64 ty k))
3782      (alu_rr_imm_logic op ty x imm))
3783(rule 1 (alu_rs_imm_logic_commutative op ty (iconst k) x)
3784      (if-let imm (imm_logic_from_imm64 ty k))
3785      (alu_rr_imm_logic op ty x imm))
3786
3787;; Special cases for when one operand is shifted left by a constant.
3788(rule (alu_rs_imm_logic_commutative op ty x (ishl y (iconst k)))
3789      (if-let amt (lshl_from_imm64 ty k))
3790      (alu_rrr_shift op ty x y amt))
3791(rule 1 (alu_rs_imm_logic_commutative op ty (ishl x (iconst k)) y)
3792      (if-let amt (lshl_from_imm64 ty k))
3793      (alu_rrr_shift op ty y x amt))
3794
3795;; Same as `alu_rs_imm_logic_commutative` above, except that it doesn't require
3796;; that the operation is commutative.
3797(spec (alu_rs_imm_logic op t a b)
3798    (provide
3799      (= result
3800         (conv_to 64
3801           (switch op
3802             ((ALUOp.OrrNot) (bvor a (bvnot b)))
3803             ((ALUOp.EorNot) (bvxor a (bvnot b)))
3804             ((ALUOp.AndNot) (bvand a (bvnot b))))))))
3805(decl alu_rs_imm_logic (ALUOp Type Value Value) Reg)
3806(rule -1 (alu_rs_imm_logic op ty x y)
3807      (alu_rrr op ty x y))
3808(rule (alu_rs_imm_logic op ty x (iconst k))
3809      (if-let imm (imm_logic_from_imm64 ty k))
3810      (alu_rr_imm_logic op ty x imm))
3811(rule (alu_rs_imm_logic op ty x (ishl y (iconst k)))
3812      (if-let amt (lshl_from_imm64 ty k))
3813      (alu_rrr_shift op ty x y amt))
3814
3815;; Helper for generating i128 bitops which simply do the same operation to the
3816;; hi/lo registers.
3817;;
3818;; TODO: Support immlogic here
3819(decl i128_alu_bitop (ALUOp Type Value Value) ValueRegs)
3820(rule (i128_alu_bitop op ty x y)
3821      (let (
3822          (x_regs ValueRegs (put_in_regs x))
3823          (x_lo Reg (value_regs_get x_regs 0))
3824          (x_hi Reg (value_regs_get x_regs 1))
3825          (y_regs ValueRegs (put_in_regs y))
3826          (y_lo Reg (value_regs_get y_regs 0))
3827          (y_hi Reg (value_regs_get y_regs 1))
3828        )
3829        (value_regs
3830          (alu_rrr op ty x_lo y_lo)
3831          (alu_rrr op ty x_hi y_hi))))
3832
3833;; Helper for emitting `MInst.VecLoadReplicate` instructions.
3834(decl ld1r (Reg VectorSize MemFlags) Reg)
3835(rule (ld1r src size flags)
3836      (let ((dst WritableReg (temp_writable_reg $I8X16))
3837            (_ Unit (emit (MInst.VecLoadReplicate dst src size flags))))
3838        dst))
3839
3840(decl pure is_pic () bool)
3841(extern constructor is_pic is_pic)
3842
3843;; Helper loading an external name into a register via `MInst.LoadExt*`
3844(decl load_ext_name (BoxExternalName i64 RelocDistance) Reg)
3845
3846;; When `is_pic` is true all names are referenced through the GOT. Note that
3847;; the relocation is applied to the address of the GOT itself so the offset
3848;; requested must be manually added in (or skipped if it's 0).
3849(rule (load_ext_name name offset _)
3850  (if-let true (is_pic))
3851  (add $I64 (load_ext_name_got name) (imm $I64 (ImmExtend.Zero) (i64_cast_unsigned offset))))
3852(rule 1 (load_ext_name name 0 _)
3853  (if-let true (is_pic))
3854  (load_ext_name_got name))
3855
3856;; Relocations that are "near" do an `adr;add` combo.
3857(rule 1 (load_ext_name name offset (RelocDistance.Near))
3858  (if-let false (is_pic))
3859  (load_ext_name_near name offset))
3860
3861;; Relocations that are "far" do an `Abs8` relocation.
3862(rule 1 (load_ext_name name offset (RelocDistance.Far))
3863  (if-let false (is_pic))
3864  (load_ext_name_far name offset))
3865
3866;; Helper for emitting `MInst.LoadExtNameGot` instructions.
3867(decl load_ext_name_got (BoxExternalName) Reg)
3868(rule (load_ext_name_got extname)
3869      (let ((dst WritableReg (temp_writable_reg $I64))
3870            (_ Unit (emit (MInst.LoadExtNameGot dst extname))))
3871        dst))
3872
3873;; Helper for emitting `MInst.LoadExtNameNear` instructions.
3874(decl load_ext_name_near (BoxExternalName i64) Reg)
3875(rule (load_ext_name_near extname offset)
3876      (let ((dst WritableReg (temp_writable_reg $I64))
3877            (_ Unit (emit (MInst.LoadExtNameNear dst extname offset))))
3878        dst))
3879
3880;; Helper for emitting `MInst.LoadExtNameFar` instructions.
3881(decl load_ext_name_far (BoxExternalName i64) Reg)
3882(rule (load_ext_name_far extname offset)
3883      (let ((dst WritableReg (temp_writable_reg $I64))
3884            (_ Unit (emit (MInst.LoadExtNameFar dst extname offset))))
3885        dst))
3886
3887;; Lower the address of a load or a store.
3888;;
3889;; This will create an `AMode` representing the address of the `Value` provided
3890;; at runtime plus the immediate offset `i32` provided. The `Type` here is used
3891;; to represent the size of the value being loaded or stored for offset scaling
3892;; if necessary.
3893;;
3894;; Note that this is broken up into two phases. In the first phase this attempts
3895;; to find constants within the `val` provided and fold them in to the `offset`
3896;; provided. Afterwards though the `amode_no_more_iconst` helper is used at
3897;; which pointer constants are no longer pattern-matched and instead only
3898;; various modes are generated. This in theory would not be necessary with
3899;; mid-end optimizations that fold constants into load/store immediate offsets
3900;; instead, but for now each backend needs to do this.
3901(decl amode (Type Value i32) AMode)
3902(spec (amode ty val offset)
3903    (provide (= result (bvadd val (sign_ext 64 offset))))
3904    (require (= 64 (widthof val))))
3905
3906(rule 0 (amode ty val offset)
3907        (amode_no_more_iconst ty val offset))
3908(rule 1 (amode ty (iadd x (i32_from_iconst y)) offset)
3909        (if-let new_offset (i32_checked_add y offset))
3910        (amode_no_more_iconst ty x new_offset))
3911(rule 2 (amode ty (iadd (i32_from_iconst x) y) offset)
3912        (if-let new_offset (i32_checked_add x offset))
3913        (amode_no_more_iconst ty y new_offset))
3914
3915(rule 3
3916      (amode ty (stack_addr slot offset1) offset2)
3917      (AMode.SlotOffset
3918       (abi_stackslot_offset_into_slot_region slot offset1 offset2)))
3919
3920(decl amode_no_more_iconst (Type Value i32) AMode)
3921;; Base case: move the `offset` into a register and add it to `val` via the
3922;; amode
3923(rule 0 (amode_no_more_iconst ty val offset)
3924        (AMode.RegReg val (imm $I64 (ImmExtend.Zero) (i64_cast_unsigned offset))))
3925
3926;; Optimize cases where the `offset` provided fits into a immediates of
3927;; various kinds of addressing modes.
3928(rule 1 (amode_no_more_iconst ty val offset)
3929        (if-let simm9 (simm9_from_i64 offset))
3930        (AMode.Unscaled val simm9))
3931(rule 2 (amode_no_more_iconst ty val offset)
3932        (if-let uimm12 (uimm12_scaled_from_i64 offset ty))
3933        (AMode.UnsignedOffset val uimm12))
3934
3935;; Optimizations where addition can fold some operations into the `amode`.
3936;;
3937;; Note that here these take higher priority than constants because an
3938;; add-of-extend can be folded into an amode, representing 2 otherwise emitted
3939;; instructions. Constants on the other hand added to the amode represent only
3940;; a single instruction folded in, so fewer instructions should be generated
3941;; with these higher priority than the rules above.
3942(rule 3 (amode_no_more_iconst ty (iadd x y) offset)
3943        (AMode.RegReg (amode_add x offset) y))
3944(rule 4 (amode_no_more_iconst ty (iadd x (uextend y @ (value_type $I32))) offset)
3945        (AMode.RegExtended (amode_add x offset) y (ExtendOp.UXTW)))
3946(rule 4 (amode_no_more_iconst ty (iadd x (sextend y @ (value_type $I32))) offset)
3947        (AMode.RegExtended (amode_add x offset) y (ExtendOp.SXTW)))
3948(rule 5 (amode_no_more_iconst ty (iadd (uextend x @ (value_type $I32)) y) offset)
3949        (AMode.RegExtended (amode_add y offset) x (ExtendOp.UXTW)))
3950(rule 5 (amode_no_more_iconst ty (iadd (sextend x @ (value_type $I32)) y) offset)
3951        (AMode.RegExtended (amode_add y offset) x (ExtendOp.SXTW)))
3952
3953;; `RegScaled*` rules where this matches an addition of an "index register" to a
3954;; base register. The index register is shifted by the size of the type loaded
3955;; in bytes to enable this mode matching.
3956;;
3957;; Note that this can additionally bundle an extending operation but the
3958;; extension must happen before the shift. This will pattern-match the shift
3959;; first and then if that succeeds afterwards try to find an extend.
3960(rule 6 (amode_no_more_iconst ty (iadd x (ishl y (iconst (u64_from_imm64 n)))) offset)
3961        (if-let true (u64_eq (ty_bytes ty) (u64_wrapping_shl 1 (shift_masked_imm ty n))))
3962        (amode_reg_scaled (amode_add x offset) y))
3963(rule 7 (amode_no_more_iconst ty (iadd (ishl y (iconst (u64_from_imm64 n))) x) offset)
3964        (if-let true (u64_eq (ty_bytes ty) (u64_wrapping_shl 1 (shift_masked_imm ty n))))
3965        (amode_reg_scaled (amode_add x offset) y))
3966
3967(decl amode_reg_scaled (Reg Value) AMode)
3968(rule 0 (amode_reg_scaled base index)
3969        (AMode.RegScaled base index))
3970(rule 1 (amode_reg_scaled base (uextend index @ (value_type $I32)))
3971        (AMode.RegScaledExtended base index (ExtendOp.UXTW)))
3972(rule 1 (amode_reg_scaled base (sextend index @ (value_type $I32)))
3973        (AMode.RegScaledExtended base index (ExtendOp.SXTW)))
3974
3975;; Helper to add a 32-bit signed immediate to the register provided. This will
3976;; select an appropriate `add` instruction to use.
3977(decl amode_add (Reg i32) Reg)
3978(rule 0 (amode_add x y)
3979        (add $I64 x (imm $I64 (ImmExtend.Zero) (i64_cast_unsigned y))))
3980(rule 1 (amode_add x y)
3981        (if-let (imm12_from_u64 imm12) (i64_cast_unsigned y))
3982        (add_imm $I64 x imm12))
3983(rule 2 (amode_add x 0) x)
3984
3985;; Creates a `PairAMode` for the `Value` provided plus the `i32` constant
3986;; offset provided.
3987(decl pair_amode (Value i32) PairAMode)
3988
3989;; Base case where `val` and `offset` are combined with an `add`
3990(rule 0 (pair_amode val offset)
3991        (if-let simm7 (simm7_scaled_from_i64 0 $I64))
3992        (PairAMode.SignedOffset (amode_add val offset) simm7))
3993
3994;; Optimization when `offset` can fit into a `SImm7Scaled`.
3995(rule 1 (pair_amode val offset)
3996        (if-let simm7 (simm7_scaled_from_i64 offset $I64))
3997        (PairAMode.SignedOffset val simm7))
3998
3999(decl pure partial simm7_scaled_from_i64 (i64 Type) SImm7Scaled)
4000(extern constructor simm7_scaled_from_i64 simm7_scaled_from_i64)
4001
4002(decl pure partial uimm12_scaled_from_i64 (i64 Type) UImm12Scaled)
4003(extern constructor uimm12_scaled_from_i64 uimm12_scaled_from_i64)
4004
4005(decl pure partial simm9_from_i64 (i64) SImm9)
4006(extern constructor simm9_from_i64 simm9_from_i64)
4007
4008
4009(decl sink_load_into_addr (Type Inst) Reg)
4010(rule (sink_load_into_addr ty x @ (load _ addr (offset32 offset)))
4011      (let ((_ Unit (sink_inst x)))
4012        (add_imm_to_addr addr (i64_cast_unsigned offset))))
4013
4014(decl add_imm_to_addr (Reg u64) Reg)
4015(rule 2 (add_imm_to_addr val 0) val)
4016(rule 1 (add_imm_to_addr val (imm12_from_u64 imm)) (add_imm $I64 val imm))
4017(rule 0 (add_imm_to_addr val offset) (add $I64 val (imm $I64 (ImmExtend.Zero) offset)))
4018
4019;; Lower a constant f16.
4020;;
4021;; Note that we must make sure that all bits outside the lowest 16 are set to 0
4022;; because this function is also used to load wider constants (that have zeros
4023;; in their most significant bits).
4024;;
4025;; Recursion: forms cycle with `constant_f32`. Invokes 32-bit case when FP16 is not supported.
4026(decl rec constant_f16 (u16) Reg)
4027(rule 3 (constant_f16 n)
4028        (if-let false (use_fp16))
4029        (constant_f32 n))
4030(rule 2 (constant_f16 0)
4031        (vec_dup_imm (asimd_mov_mod_imm_zero (ScalarSize.Size32))
4032                     false
4033                     (VectorSize.Size32x2)))
4034(rule 1 (constant_f16 n)
4035        (if-let imm (asimd_fp_mod_imm_from_u64 n (ScalarSize.Size16)))
4036        (fpu_move_fp_imm imm (ScalarSize.Size16)))
4037(rule (constant_f16 n)
4038      (mov_to_fpu (imm $I16 (ImmExtend.Zero) n) (ScalarSize.Size16)))
4039
4040;; Lower a constant f32.
4041;;
4042;; Note that we must make sure that all bits outside the lowest 32 are set to 0
4043;; because this function is also used to load wider constants (that have zeros
4044;; in their most significant bits).
4045;;
4046;; Recursion: forms cycle with `constant_f16`. Invokes 16-bit case when FP16 is supported.
4047(decl rec constant_f32 (u32) Reg)
4048(rule 3 (constant_f32 0)
4049        (vec_dup_imm (asimd_mov_mod_imm_zero (ScalarSize.Size32))
4050                     false
4051                     (VectorSize.Size32x2)))
4052(rule 2 (constant_f32 n)
4053        (if-let imm (asimd_fp_mod_imm_from_u64 n (ScalarSize.Size32)))
4054        (fpu_move_fp_imm imm (ScalarSize.Size32)))
4055(rule 1 (constant_f32 (u16_from_u32 n))
4056        (if-let true (use_fp16))
4057        (constant_f16 n))
4058(rule (constant_f32 n)
4059      (mov_to_fpu (imm $I32 (ImmExtend.Zero) n) (ScalarSize.Size32)))
4060
4061;; Lower a constant f64.
4062;;
4063;; Note that we must make sure that all bits outside the lowest 64 are set to 0
4064;; because this function is also used to load wider constants (that have zeros
4065;; in their most significant bits).
4066;; TODO: Treat as half of a 128 bit vector and consider replicated patterns.
4067;; Scalar MOVI might also be an option.
4068(decl constant_f64 (u64) Reg)
4069(rule 4 (constant_f64 0)
4070        (vec_dup_imm (asimd_mov_mod_imm_zero (ScalarSize.Size32))
4071                     false
4072                     (VectorSize.Size32x2)))
4073(rule 3 (constant_f64 n)
4074        (if-let imm (asimd_fp_mod_imm_from_u64 n (ScalarSize.Size64)))
4075        (fpu_move_fp_imm imm (ScalarSize.Size64)))
4076(rule 2 (constant_f64 (u32_from_u64 n))
4077        (constant_f32 n))
4078(rule 1 (constant_f64 (u64_low32_bits_unset n))
4079        (mov_to_fpu (imm $I64 (ImmExtend.Zero) n) (ScalarSize.Size64)))
4080(rule (constant_f64 n)
4081      (fpu_load64 (AMode.Const (emit_u64_le_const n)) (mem_flags_trusted)))
4082
4083;; Tests whether the low 32 bits in the input are all zero.
4084(decl u64_low32_bits_unset (u64) u64)
4085(extern extractor u64_low32_bits_unset u64_low32_bits_unset)
4086
4087;; Lower a constant f128.
4088(decl constant_f128 (u128) Reg)
4089(rule 3 (constant_f128 0)
4090        (vec_dup_imm (asimd_mov_mod_imm_zero (ScalarSize.Size8))
4091                     false
4092                     (VectorSize.Size8x16)))
4093
4094;; If the upper 64-bits are all zero then defer to `constant_f64`.
4095(rule 2 (constant_f128 (u64_from_u128 n)) (constant_f64 n))
4096
4097;; If the low half of the u128 equals the high half then delegate to the splat
4098;; logic as a splat of a 64-bit value.
4099(rule 1 (constant_f128 (u128_replicated_u64 n))
4100        (splat_const n (VectorSize.Size64x2)))
4101
4102;; Base case is to load the constant from memory.
4103(rule (constant_f128 n)
4104      (fpu_load128 (AMode.Const (emit_u128_le_const n)) (mem_flags_trusted)))
4105
4106;; Lower a vector splat with a constant parameter.
4107;;
4108;; The 64-bit input here only uses the low bits for the lane size in
4109;; `VectorSize` and all other bits are ignored.
4110;;
4111;; Recursion: bounded since the recursive call always reduces lane size.
4112(decl rec splat_const (u64 VectorSize) Reg)
4113
4114;; If the splat'd constant can itself be reduced in size then attempt to do so
4115;; as it will make it easier to create the immediates in the instructions below.
4116(rule 5 (splat_const (u64_replicated_u32 n) (VectorSize.Size64x2))
4117        (splat_const n (VectorSize.Size32x4)))
4118(rule 5 (splat_const (u32_replicated_u16 n) (VectorSize.Size32x4))
4119        (splat_const n (VectorSize.Size16x8)))
4120(rule 5 (splat_const (u32_replicated_u16 n) (VectorSize.Size32x2))
4121        (splat_const n (VectorSize.Size16x4)))
4122(rule 5 (splat_const (u16_replicated_u8 n) (VectorSize.Size16x8))
4123        (splat_const n (VectorSize.Size8x16)))
4124(rule 5 (splat_const (u16_replicated_u8 n) (VectorSize.Size16x4))
4125        (splat_const n (VectorSize.Size8x8)))
4126
4127;; Special cases for `vec_dup_imm` instructions where the input is either
4128;; negated or not.
4129(rule 4 (splat_const n size)
4130        (if-let imm (asimd_mov_mod_imm_from_u64 n (vector_lane_size size)))
4131        (vec_dup_imm imm false size))
4132(rule 3 (splat_const n size)
4133        (if-let imm (asimd_mov_mod_imm_from_u64 (u64_not n) (vector_lane_size size)))
4134        (vec_dup_imm imm true size))
4135
4136;; Special case a 32-bit splat where an immediate can be created by
4137;; concatenating the 32-bit constant into a 64-bit value
4138(rule 2 (splat_const n (VectorSize.Size32x4))
4139        (if-let imm (asimd_mov_mod_imm_from_u64 (u64_or n (u64_wrapping_shl n 32)) (ScalarSize.Size64)))
4140        (vec_dup_imm imm false (VectorSize.Size64x2)))
4141(rule 2 (splat_const n (VectorSize.Size32x2))
4142        (if-let imm (asimd_mov_mod_imm_from_u64 (u64_or n (u64_wrapping_shl n 32)) (ScalarSize.Size64)))
4143        (fpu_extend (vec_dup_imm imm false (VectorSize.Size64x2)) (ScalarSize.Size64)))
4144
4145(rule 1 (splat_const n size)
4146        (if-let true (vec_dup_fp_imm_supports_lane_size (vector_lane_size size)))
4147        (if-let imm (asimd_fp_mod_imm_from_u64 n (vector_lane_size size)))
4148        (vec_dup_fp_imm imm size))
4149
4150(decl pure vec_dup_fp_imm_supports_lane_size (ScalarSize) bool)
4151(rule 1 (vec_dup_fp_imm_supports_lane_size (ScalarSize.Size32)) true)
4152(rule 1 (vec_dup_fp_imm_supports_lane_size (ScalarSize.Size64)) true)
4153(rule (vec_dup_fp_imm_supports_lane_size _) false)
4154
4155;; The base case for splat is to use `vec_dup` with the immediate loaded into a
4156;; register.
4157(rule (splat_const n size)
4158      (vec_dup (imm $I64 (ImmExtend.Zero) n) size))
4159
4160;; Lower a FloatCC to a Cond.
4161(decl fp_cond_code (FloatCC) Cond)
4162;; TODO: Port lower_fp_condcode() to ISLE.
4163(extern constructor fp_cond_code fp_cond_code)
4164
4165;; Lower an integer cond code.
4166(spec (cond_code a) (provide (= a result)))
4167(decl cond_code (IntCC) Cond)
4168;; TODO: Port lower_condcode() to ISLE.
4169(extern constructor cond_code cond_code)
4170
4171;; Invert a condition code.
4172(decl invert_cond (Cond) Cond)
4173;; TODO: Port cond.invert() to ISLE.
4174(extern constructor invert_cond invert_cond)
4175
4176;; Generate comparison to zero operator from input condition code
4177(decl float_cc_cmp_zero_to_vec_misc_op (FloatCC) VecMisc2)
4178(extern constructor float_cc_cmp_zero_to_vec_misc_op float_cc_cmp_zero_to_vec_misc_op)
4179
4180(decl float_cc_cmp_zero_to_vec_misc_op_swap (FloatCC) VecMisc2)
4181(extern constructor float_cc_cmp_zero_to_vec_misc_op_swap float_cc_cmp_zero_to_vec_misc_op_swap)
4182
4183;; Match valid generic compare to zero cases
4184(decl fcmp_zero_cond (FloatCC) FloatCC)
4185(extern extractor fcmp_zero_cond fcmp_zero_cond)
4186
4187;; Match not equal compare to zero separately as it requires two output instructions
4188(decl fcmp_zero_cond_not_eq (FloatCC) FloatCC)
4189(extern extractor fcmp_zero_cond_not_eq fcmp_zero_cond_not_eq)
4190
4191;; Helper for generating float compare to zero instructions where 2nd argument is zero
4192(decl float_cmp_zero (FloatCC Reg VectorSize) Reg)
4193(rule (float_cmp_zero cond rn size)
4194      (vec_misc (float_cc_cmp_zero_to_vec_misc_op cond) rn size))
4195
4196;; Helper for generating float compare to zero instructions in case where 1st argument is zero
4197(decl float_cmp_zero_swap (FloatCC Reg VectorSize) Reg)
4198(rule (float_cmp_zero_swap cond rn size)
4199      (vec_misc (float_cc_cmp_zero_to_vec_misc_op_swap cond) rn size))
4200
4201;; Helper for generating float compare equal to zero instruction
4202(decl fcmeq0 (Reg VectorSize) Reg)
4203(rule (fcmeq0 rn size)
4204      (vec_misc (VecMisc2.Fcmeq0) rn size))
4205
4206;; Generate comparison to zero operator from input condition code
4207(decl int_cc_cmp_zero_to_vec_misc_op (IntCC) VecMisc2)
4208(extern constructor int_cc_cmp_zero_to_vec_misc_op int_cc_cmp_zero_to_vec_misc_op)
4209
4210(decl int_cc_cmp_zero_to_vec_misc_op_swap (IntCC) VecMisc2)
4211(extern constructor int_cc_cmp_zero_to_vec_misc_op_swap int_cc_cmp_zero_to_vec_misc_op_swap)
4212
4213;; Match valid generic compare to zero cases
4214(decl icmp_zero_cond (IntCC) IntCC)
4215(extern extractor icmp_zero_cond icmp_zero_cond)
4216
4217;; Match not equal compare to zero separately as it requires two output instructions
4218(decl icmp_zero_cond_not_eq (IntCC) IntCC)
4219(extern extractor icmp_zero_cond_not_eq icmp_zero_cond_not_eq)
4220
4221;; Helper for generating int compare to zero instructions where 2nd argument is zero
4222(decl int_cmp_zero (IntCC Reg VectorSize) Reg)
4223(rule (int_cmp_zero cond rn size)
4224      (vec_misc (int_cc_cmp_zero_to_vec_misc_op cond) rn size))
4225
4226;; Helper for generating int compare to zero instructions in case where 1st argument is zero
4227(decl int_cmp_zero_swap (IntCC Reg VectorSize) Reg)
4228(rule (int_cmp_zero_swap cond rn size)
4229      (vec_misc (int_cc_cmp_zero_to_vec_misc_op_swap cond) rn size))
4230
4231;; Helper for generating int compare equal to zero instruction
4232(decl cmeq0 (Reg VectorSize) Reg)
4233(rule (cmeq0 rn size)
4234      (vec_misc (VecMisc2.Cmeq0) rn size))
4235
4236;; Helper for emitting `MInst.AtomicRMW` instructions.
4237(decl lse_atomic_rmw (AtomicRMWOp Value Reg Type MemFlags) Reg)
4238(rule (lse_atomic_rmw op p r_arg2 ty flags)
4239      (let (
4240          (r_addr Reg p)
4241          (dst WritableReg (temp_writable_reg ty))
4242          (_ Unit (emit (MInst.AtomicRMW op r_arg2 dst r_addr ty flags)))
4243        )
4244        dst))
4245
4246;; Helper for emitting `MInst.AtomicCAS` instructions.
4247(decl lse_atomic_cas (Reg Reg Reg Type MemFlags) Reg)
4248(rule (lse_atomic_cas addr expect replace ty flags)
4249      (let (
4250            (dst WritableReg (temp_writable_reg ty))
4251            (_ Unit (emit (MInst.AtomicCAS dst expect replace addr ty flags)))
4252          )
4253          dst))
4254
4255;; Helper for emitting `MInst.AtomicRMWLoop` instructions.
4256;; - Make sure that both args are in virtual regs, since in effect
4257;; we have to do a parallel copy to get them safely to the AtomicRMW input
4258;; regs, and that's not guaranteed safe if either is in a real reg.
4259;; - Move the args to the preordained AtomicRMW input regs
4260;; - And finally, copy the preordained AtomicRMW output reg to its destination.
4261(decl atomic_rmw_loop (AtomicRMWLoopOp Reg Reg Type MemFlags) Reg)
4262(rule (atomic_rmw_loop op addr operand ty flags)
4263      (let ((dst WritableReg (temp_writable_reg $I64))
4264            (scratch1 WritableReg (temp_writable_reg $I64))
4265            (scratch2 WritableReg (temp_writable_reg $I64))
4266            (_ Unit (emit (MInst.AtomicRMWLoop ty op flags addr operand dst scratch1 scratch2))))
4267        dst))
4268
4269;; Helper for emitting `MInst.AtomicCASLoop` instructions.
4270;; This is very similar to, but not identical to, the AtomicRmw case.  Note
4271;; that the AtomicCASLoop sequence does its own masking, so we don't need to worry
4272;; about zero-extending narrow (I8/I16/I32) values here.
4273;; Make sure that all three args are in virtual regs.  See corresponding comment
4274;; for `atomic_rmw_loop` above.
4275(decl atomic_cas_loop (Reg Reg Reg Type MemFlags) Reg)
4276(rule (atomic_cas_loop addr expect replace ty flags)
4277      (let ((dst WritableReg (temp_writable_reg $I64))
4278            (scratch WritableReg (temp_writable_reg $I64))
4279            (_ Unit (emit (MInst.AtomicCASLoop ty flags addr expect replace dst scratch))))
4280        dst))
4281
4282;; Helper for emitting `MInst.MovPReg` instructions.
4283(decl mov_from_preg (PReg) Reg)
4284(rule (mov_from_preg src)
4285      (let ((dst WritableReg (temp_writable_reg $I64))
4286            (_ Unit (emit (MInst.MovFromPReg dst src))))
4287        dst))
4288
4289(decl mov_to_preg (PReg Reg) SideEffectNoResult)
4290(rule (mov_to_preg dst src)
4291      (SideEffectNoResult.Inst (MInst.MovToPReg dst src)))
4292
4293(decl preg_sp () PReg)
4294(extern constructor preg_sp preg_sp)
4295
4296(decl preg_fp () PReg)
4297(extern constructor preg_fp preg_fp)
4298
4299(decl preg_link () PReg)
4300(extern constructor preg_link preg_link)
4301
4302(decl preg_pinned () PReg)
4303(extern constructor preg_pinned preg_pinned)
4304
4305(decl aarch64_sp () Reg)
4306(rule (aarch64_sp)
4307      (mov_from_preg (preg_sp)))
4308
4309(decl aarch64_fp () Reg)
4310(rule (aarch64_fp)
4311      (mov_from_preg (preg_fp)))
4312
4313(decl aarch64_link () Reg)
4314(rule 1 (aarch64_link)
4315      (if (preserve_frame_pointers))
4316      (if (sign_return_address_disabled))
4317      (let ((dst WritableReg (temp_writable_reg $I64))
4318            ;; Even though LR is not an allocatable register, whether it
4319            ;; contains the return address for the current function is
4320            ;; unknown at this point. For example, this operation may come
4321            ;; immediately after a call, in which case LR would not have a
4322            ;; valid value. That's why we must obtain the return address from
4323            ;; the frame record that corresponds to the current subroutine on
4324            ;; the stack; the presence of the record is guaranteed by the
4325            ;; `preserve_frame_pointers` setting.
4326            (addr AMode (AMode.FPOffset 8))
4327            (_ Unit (emit (MInst.ULoad64 dst addr (mem_flags_trusted)))))
4328           dst))
4329
4330(rule (aarch64_link)
4331      (if (preserve_frame_pointers))
4332      ;; Similarly to the rule above, we must load the return address from the
4333      ;; the frame record. Furthermore, we can use LR as a scratch register
4334      ;; because the function will set it to the return address immediately
4335      ;; before returning.
4336      (let ((addr AMode (AMode.FPOffset 8))
4337            (lr WritableReg (writable_link_reg))
4338            (_ Unit (emit (MInst.ULoad64 lr addr (mem_flags_trusted))))
4339            (_ Unit (emit (MInst.Xpaclri))))
4340           (mov_from_preg (preg_link))))
4341
4342;; Helper for getting the maximum shift amount for a type.
4343
4344(decl max_shift (Type) u8)
4345(rule (max_shift $F64) 63)
4346(rule (max_shift $F32) 31)
4347
4348;; Helper for generating `fcopysign` instruction sequences.
4349
4350(decl fcopy_sign (Reg Reg Type) Reg)
4351(rule 1 (fcopy_sign x y (ty_scalar_float ty))
4352      (let ((dst WritableReg (temp_writable_reg $F64))
4353            (tmp Reg (fpu_rri (fpu_op_ri_ushr (ty_bits ty) (max_shift ty)) y))
4354            (_ Unit (emit (MInst.FpuRRIMod (fpu_op_ri_sli (ty_bits ty) (max_shift ty)) dst x tmp))))
4355       dst))
4356(rule (fcopy_sign x y ty @ (multi_lane _ _))
4357      (let ((dst WritableReg (temp_writable_reg $I8X16))
4358            (tmp Reg (ushr_vec_imm y (max_shift (lane_type ty)) (vector_size ty)))
4359            (_ Unit (emit (MInst.VecShiftImmMod (VecShiftImmModOp.Sli) dst x tmp (vector_size ty) (max_shift (lane_type ty))))))
4360       dst))
4361
4362;; Helpers for generating `MInst.FpuToInt` instructions.
4363
4364(decl fpu_to_int_nan_check (ScalarSize Reg) Reg)
4365(rule (fpu_to_int_nan_check size src)
4366      (let ((r ValueRegs
4367                  (with_flags (fpu_cmp size src src)
4368                   (ConsumesFlags.ConsumesFlagsReturnsReg
4369                    (MInst.TrapIf (cond_br_cond (Cond.Vs))
4370                        (trap_code_bad_conversion_to_integer))
4371                    src))))
4372       (value_regs_get r 0)))
4373
4374;; Checks that the value is not less than the minimum bound,
4375;; accepting a boolean (whether the type is signed), input type,
4376;; output type, and registers containing the source and minimum bound.
4377(decl fpu_to_int_underflow_check (bool Type Type Reg Reg) Reg)
4378(rule (fpu_to_int_underflow_check true $F32 (fits_in_16 out_ty) src min)
4379      (let ((r ValueRegs
4380                  (with_flags (fpu_cmp (ScalarSize.Size32) src min)
4381                   (ConsumesFlags.ConsumesFlagsReturnsReg
4382                    (MInst.TrapIf (cond_br_cond (Cond.Le))
4383                        (trap_code_integer_overflow))
4384                    src))))
4385       (value_regs_get r 0)))
4386(rule (fpu_to_int_underflow_check true $F64 (fits_in_32 out_ty) src min)
4387      (let ((r ValueRegs
4388                  (with_flags (fpu_cmp (ScalarSize.Size64) src min)
4389                   (ConsumesFlags.ConsumesFlagsReturnsReg
4390                    (MInst.TrapIf (cond_br_cond (Cond.Le))
4391                        (trap_code_integer_overflow))
4392                    src))))
4393       (value_regs_get r 0)))
4394(rule -1 (fpu_to_int_underflow_check true in_ty _out_ty src min)
4395      (let ((r ValueRegs
4396                  (with_flags (fpu_cmp (scalar_size in_ty) src min)
4397                   (ConsumesFlags.ConsumesFlagsReturnsReg
4398                    (MInst.TrapIf (cond_br_cond (Cond.Lt))
4399                        (trap_code_integer_overflow))
4400                    src))))
4401       (value_regs_get r 0)))
4402(rule (fpu_to_int_underflow_check false in_ty _out_ty src min)
4403      (let ((r ValueRegs
4404                  (with_flags (fpu_cmp (scalar_size in_ty) src min)
4405                   (ConsumesFlags.ConsumesFlagsReturnsReg
4406                    (MInst.TrapIf (cond_br_cond (Cond.Le))
4407                        (trap_code_integer_overflow))
4408                    src))))
4409       (value_regs_get r 0)))
4410
4411(decl fpu_to_int_overflow_check (ScalarSize Reg Reg) Reg)
4412(rule (fpu_to_int_overflow_check size src max)
4413      (let ((r ValueRegs
4414                  (with_flags (fpu_cmp size src max)
4415                   (ConsumesFlags.ConsumesFlagsReturnsReg
4416                    (MInst.TrapIf (cond_br_cond (Cond.Ge))
4417                        (trap_code_integer_overflow))
4418                    src))))
4419       (value_regs_get r 0)))
4420
4421;; Emits the appropriate instruction sequence to convert a
4422;; floating-point value to an integer, trapping if the value
4423;; is a NaN or does not fit in the target type.
4424;; Accepts the specific conversion op, the source register,
4425;; whether the input is signed, and finally the input and output
4426;; types.
4427(decl fpu_to_int_cvt (FpuToIntOp Reg bool Type Type) Reg)
4428(rule (fpu_to_int_cvt op src signed in_ty out_ty)
4429      (let ((size ScalarSize (scalar_size in_ty))
4430            (in_bits u8 (ty_bits in_ty))
4431            (out_bits u8 (ty_bits out_ty))
4432            (src Reg (fpu_to_int_nan_check size src))
4433            (min Reg (min_fp_value signed in_bits out_bits))
4434            (src Reg (fpu_to_int_underflow_check signed in_ty out_ty src min))
4435            (max Reg (max_fp_value signed in_bits out_bits))
4436            (src Reg (fpu_to_int_overflow_check size src max)))
4437       (fpu_to_int op src)))
4438
4439;; Emits the appropriate instruction sequence to convert a
4440;; floating-point value to an integer, saturating if the value
4441;; does not fit in the target type.
4442;; Accepts the specific conversion op, the source register,
4443;; whether the input is signed, and finally the output type.
4444(decl fpu_to_int_cvt_sat (FpuToIntOp Reg bool Type) Reg)
4445(rule 1 (fpu_to_int_cvt_sat op src _ $I64)
4446      (fpu_to_int op src))
4447(rule 1 (fpu_to_int_cvt_sat op src _ $I32)
4448      (fpu_to_int op src))
4449(rule (fpu_to_int_cvt_sat op src false (fits_in_16 out_ty))
4450      (let ((result Reg (fpu_to_int op src))
4451            (max Reg (imm out_ty (ImmExtend.Zero) (ty_mask out_ty))))
4452       (with_flags_reg
4453        (cmp (OperandSize.Size32) result max)
4454        (csel (Cond.Hi) max result))))
4455(rule (fpu_to_int_cvt_sat op src true (fits_in_16 out_ty))
4456      (let ((result Reg (fpu_to_int op src))
4457            (max Reg (signed_max out_ty))
4458            (min Reg (signed_min out_ty))
4459            (result Reg (with_flags_reg
4460                         (cmp (operand_size out_ty) result max)
4461                         (csel (Cond.Gt) max result)))
4462            (result Reg (with_flags_reg
4463                         (cmp (operand_size out_ty) result min)
4464                         (csel (Cond.Lt) min result))))
4465       result))
4466
4467(decl signed_min (Type) Reg)
4468(rule (signed_min $I8) (imm $I8 (ImmExtend.Sign) 0x80))
4469(rule (signed_min $I16) (imm $I16 (ImmExtend.Sign) 0x8000))
4470
4471(decl signed_max (Type) Reg)
4472(rule (signed_max $I8) (imm $I8 (ImmExtend.Sign) 0x7F))
4473(rule (signed_max $I16) (imm $I16 (ImmExtend.Sign) 0x7FFF))
4474
4475(decl fpu_to_int (FpuToIntOp Reg) Reg)
4476(rule (fpu_to_int op src)
4477      (let ((dst WritableReg (temp_writable_reg $I64))
4478            (_ Unit (emit (MInst.FpuToInt op dst src))))
4479       dst))
4480
4481;; Helper for generating `MInst.IntToFpu` instructions.
4482
4483(decl int_to_fpu (IntToFpuOp Reg) Reg)
4484(rule (int_to_fpu op src)
4485      (let ((dst WritableReg (temp_writable_reg $I8X16))
4486            (_ Unit (emit (MInst.IntToFpu op dst src))))
4487       dst))
4488
4489;;;; Helpers for Emitting Calls ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4490
4491(decl gen_call_info (Sig ExternalName CallArgList CallRetList OptionTryCallInfo bool) BoxCallInfo)
4492(extern constructor gen_call_info gen_call_info)
4493
4494(decl gen_call_ind_info (Sig Reg CallArgList CallRetList OptionTryCallInfo) BoxCallIndInfo)
4495(extern constructor gen_call_ind_info gen_call_ind_info)
4496
4497(decl gen_return_call_info (Sig ExternalName CallArgList) BoxReturnCallInfo)
4498(extern constructor gen_return_call_info gen_return_call_info)
4499
4500(decl gen_return_call_ind_info (Sig Reg CallArgList) BoxReturnCallIndInfo)
4501(extern constructor gen_return_call_ind_info gen_return_call_ind_info)
4502
4503;; Helper for creating `MInst.Call` instructions.
4504(decl call_impl (BoxCallInfo) SideEffectNoResult)
4505(rule (call_impl info)
4506      (SideEffectNoResult.Inst (MInst.Call info)))
4507
4508;; Helper for creating `MInst.CallInd` instructions.
4509(decl call_ind_impl (BoxCallIndInfo) SideEffectNoResult)
4510(rule (call_ind_impl info)
4511      (SideEffectNoResult.Inst (MInst.CallInd info)))
4512
4513;; Helper for creating `MInst.ReturnCall` instructions.
4514(decl return_call_impl (BoxReturnCallInfo) SideEffectNoResult)
4515(rule (return_call_impl info)
4516      (SideEffectNoResult.Inst (MInst.ReturnCall info)))
4517
4518;; Helper for creating `MInst.ReturnCallInd` instructions.
4519(decl return_call_ind_impl (BoxReturnCallIndInfo) SideEffectNoResult)
4520(rule (return_call_ind_impl info)
4521      (SideEffectNoResult.Inst (MInst.ReturnCallInd info)))
4522
4523;; Helpers for pinned register manipulation.
4524
4525(decl write_pinned_reg (Reg) SideEffectNoResult)
4526(rule (write_pinned_reg val)
4527      (mov_to_preg (preg_pinned) val))
4528
4529;; Helpers for stackslot effective address generation.
4530
4531(decl compute_stack_addr (StackSlot Offset32) Reg)
4532(rule (compute_stack_addr stack_slot offset)
4533      (let ((dst WritableReg (temp_writable_reg $I64))
4534           (_ Unit (emit (abi_stackslot_addr dst stack_slot offset))))
4535        dst))
4536
4537;; Helper for emitting instruction sequences to perform a vector comparison.
4538
4539(decl vec_cmp_vc (Reg Reg VectorSize) Reg)
4540(rule (vec_cmp_vc rn rm size)
4541      (let ((dst Reg (vec_rrr (VecALUOp.Fcmeq) rn rn size))
4542            (tmp Reg (vec_rrr (VecALUOp.Fcmeq) rm rm size))
4543            (dst Reg (vec_rrr (VecALUOp.And) dst tmp size)))
4544       dst))
4545
4546(decl vec_cmp (Reg Reg Type Cond) Reg)
4547
4548;; Floating point Vs / Vc
4549(rule (vec_cmp rn rm ty (Cond.Vc))
4550      (if (ty_vector_float ty))
4551      (vec_cmp_vc rn rm (vector_size ty)))
4552(rule (vec_cmp rn rm ty (Cond.Vs))
4553      (if (ty_vector_float ty))
4554      (let ((tmp Reg (vec_cmp_vc rn rm (vector_size ty))))
4555       (vec_misc (VecMisc2.Not) tmp (vector_size ty))))
4556
4557;; 'Less than' operations are implemented by swapping the order of
4558;; operands and using the 'greater than' instructions.
4559;; 'Not equal' is implemented with 'equal' and inverting the result.
4560
4561;; Floating-point
4562(rule (vec_cmp rn rm ty (Cond.Eq))
4563      (if (ty_vector_float ty))
4564      (vec_rrr (VecALUOp.Fcmeq) rn rm (vector_size ty)))
4565(rule (vec_cmp rn rm ty (Cond.Ne))
4566      (if (ty_vector_float ty))
4567      (let ((tmp Reg (vec_rrr (VecALUOp.Fcmeq) rn rm (vector_size ty))))
4568       (vec_misc (VecMisc2.Not) tmp (vector_size ty))))
4569(rule (vec_cmp rn rm ty (Cond.Ge))
4570      (if (ty_vector_float ty))
4571      (vec_rrr (VecALUOp.Fcmge) rn rm (vector_size ty)))
4572(rule (vec_cmp rn rm ty (Cond.Gt))
4573      (if (ty_vector_float ty))
4574      (vec_rrr (VecALUOp.Fcmgt) rn rm (vector_size ty)))
4575;; Floating-point swapped-operands
4576(rule (vec_cmp rn rm ty (Cond.Mi))
4577      (if (ty_vector_float ty))
4578      (vec_rrr (VecALUOp.Fcmgt) rm rn (vector_size ty)))
4579(rule (vec_cmp rn rm ty (Cond.Ls))
4580      (if (ty_vector_float ty))
4581      (vec_rrr (VecALUOp.Fcmge) rm rn (vector_size ty)))
4582
4583;; Integer
4584(rule 1 (vec_cmp rn rm ty (Cond.Eq))
4585      (if (ty_vector_not_float ty))
4586      (vec_rrr (VecALUOp.Cmeq) rn rm (vector_size ty)))
4587(rule 1 (vec_cmp rn rm ty (Cond.Ne))
4588      (if (ty_vector_not_float ty))
4589      (let ((tmp Reg (vec_rrr (VecALUOp.Cmeq) rn rm (vector_size ty))))
4590       (vec_misc (VecMisc2.Not) tmp (vector_size ty))))
4591(rule 1 (vec_cmp rn rm ty (Cond.Ge))
4592      (if (ty_vector_not_float ty))
4593      (vec_rrr (VecALUOp.Cmge) rn rm (vector_size ty)))
4594(rule 1 (vec_cmp rn rm ty (Cond.Gt))
4595      (if (ty_vector_not_float ty))
4596      (vec_rrr (VecALUOp.Cmgt) rn rm (vector_size ty)))
4597(rule (vec_cmp rn rm ty (Cond.Hs))
4598      (if (ty_vector_not_float ty))
4599      (vec_rrr (VecALUOp.Cmhs) rn rm (vector_size ty)))
4600(rule (vec_cmp rn rm ty (Cond.Hi))
4601      (if (ty_vector_not_float ty))
4602      (vec_rrr (VecALUOp.Cmhi) rn rm (vector_size ty)))
4603;; Integer swapped-operands
4604(rule (vec_cmp rn rm ty (Cond.Le))
4605      (if (ty_vector_not_float ty))
4606      (vec_rrr (VecALUOp.Cmge) rm rn (vector_size ty)))
4607(rule (vec_cmp rn rm ty (Cond.Lt))
4608      (if (ty_vector_not_float ty))
4609      (vec_rrr (VecALUOp.Cmgt) rm rn (vector_size ty)))
4610(rule 1 (vec_cmp rn rm ty (Cond.Ls))
4611      (if (ty_vector_not_float ty))
4612      (vec_rrr (VecALUOp.Cmhs) rm rn (vector_size ty)))
4613(rule (vec_cmp rn rm ty (Cond.Lo))
4614      (if (ty_vector_not_float ty))
4615      (vec_rrr (VecALUOp.Cmhi) rm rn (vector_size ty)))
4616
4617;; Helper for determining if any value in a vector is true.
4618;; This operation is implemented by using umaxp to create a scalar value, which
4619;; is then compared against zero.
4620;;
4621;; umaxp vn.4s, vm.4s, vm.4s
4622;; mov xm, vn.d[0]
4623;; cmp xm, #0
4624(decl vanytrue (Reg Type) ProducesFlags)
4625(rule 1 (vanytrue src (ty_vec128 ty))
4626      (let ((src Reg (vec_rrr (VecALUOp.Umaxp) src src (VectorSize.Size32x4)))
4627            (src Reg (mov_from_vec src 0 (ScalarSize.Size64))))
4628       (cmp_imm (OperandSize.Size64) src (u8_into_imm12 0))))
4629(rule (vanytrue src ty)
4630      (if (ty_vec64 ty))
4631      (let ((src Reg (mov_from_vec src 0 (ScalarSize.Size64))))
4632       (cmp_imm (OperandSize.Size64) src (u8_into_imm12 0))))
4633
4634;;;; TLS Values ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4635
4636;; Helper for emitting ElfTlsGetAddr.
4637(decl elf_tls_get_addr (ExternalName) Reg)
4638(rule (elf_tls_get_addr name)
4639      (let ((dst WritableReg (temp_writable_reg $I64))
4640            (tmp WritableReg (temp_writable_reg $I64))
4641            (_ Unit (emit (MInst.ElfTlsGetAddr (box_external_name name) dst tmp))))
4642        dst))
4643
4644(decl macho_tls_get_addr (ExternalName) Reg)
4645(rule (macho_tls_get_addr name)
4646      (let ((dst WritableReg (temp_writable_reg $I64))
4647            (_ Unit (emit (MInst.MachOTlsGetAddr name dst))))
4648        dst))
4649
4650;; A tuple of `ProducesFlags` and `IntCC`.
4651(type FlagsAndCC (enum (FlagsAndCC (flags ProducesFlags)
4652                                   (cc IntCC))))
4653
4654(spec (flags_and_cc flags cc)
4655      (provide
4656            (= result (concat (extract 67 64 flags) cc)))
4657      (require
4658            (or
4659                  (= cc (IntCC.Equal))
4660                  (= cc (IntCC.NotEqual))
4661                  (= cc (IntCC.UnsignedGreaterThanOrEqual))
4662                  (= cc (IntCC.UnsignedGreaterThan))
4663                  (= cc (IntCC.UnsignedLessThanOrEqual))
4664                  (= cc (IntCC.UnsignedLessThan))
4665                  (= cc (IntCC.SignedGreaterThanOrEqual))
4666                  (= cc (IntCC.SignedGreaterThan))
4667                  (= cc (IntCC.SignedLessThanOrEqual))
4668                  (= cc (IntCC.SignedLessThan)))))
4669;; Helper constructor for `FlagsAndCC`.
4670(decl flags_and_cc (ProducesFlags IntCC) FlagsAndCC)
4671(rule (flags_and_cc flags cc) (FlagsAndCC.FlagsAndCC flags cc))
4672
4673(spec (flags_and_cc_to_bool a)
4674      (provide
4675         (= result
4676        (switch (extract 7 0 a)
4677          ((IntCC.Equal) (if (= (extract 10 10 a) #b1) #x01 #x00))
4678          ((IntCC.NotEqual) (if (= (extract 10 10 a) #b0) #x01 #x00))
4679          ((IntCC.SignedGreaterThan) (if (and (= (extract 10 10 a) #b0) (= (extract 11 11 a) (extract 8 8 a))) #x01 #x00))
4680          ((IntCC.SignedGreaterThanOrEqual) (if (= (extract 11 11 a) (extract 8 8 a)) #x01 #x00))
4681          ((IntCC.SignedLessThan) (if (not (= (extract 11 11 a) (extract 8 8 a))) #x01 #x00))
4682          ((IntCC.SignedLessThanOrEqual) (if (or (= (extract 10 10 a) #b1) (not (= (extract 11 11 a) (extract 8 8 a)))) #x01 #x00))
4683          ((IntCC.UnsignedGreaterThan) (if (and (= (extract 9 9 a) #b1) (= (extract 10 10 a) #b0)) #x01 #x00))
4684          ((IntCC.UnsignedGreaterThanOrEqual) (if (= (extract 9 9 a) #b1) #x01 #x00))
4685          ((IntCC.UnsignedLessThan) (if (= (extract 9 9 a) #b0) #x01 #x00))
4686          ((IntCC.UnsignedLessThanOrEqual) (if (or (= (extract 9 9 a) #b0) (= (extract 10 10 a) #b1)) #x01 #x00)))))
4687      (require
4688            (or
4689                  (= (extract 7 0 a) (IntCC.Equal))
4690                  (= (extract 7 0 a) (IntCC.NotEqual))
4691                  (= (extract 7 0 a) (IntCC.UnsignedGreaterThanOrEqual))
4692                  (= (extract 7 0 a) (IntCC.UnsignedGreaterThan))
4693                  (= (extract 7 0 a) (IntCC.UnsignedLessThanOrEqual))
4694                  (= (extract 7 0 a) (IntCC.UnsignedLessThan))
4695                  (= (extract 7 0 a) (IntCC.SignedGreaterThanOrEqual))
4696                  (= (extract 7 0 a) (IntCC.SignedGreaterThan))
4697                  (= (extract 7 0 a) (IntCC.SignedLessThanOrEqual))
4698                  (= (extract 7 0 a) (IntCC.SignedLessThan)))))
4699;; Materialize a `FlagsAndCC` into a boolean `ValueRegs`.
4700(decl flags_and_cc_to_bool (FlagsAndCC) ValueRegs)
4701(rule (flags_and_cc_to_bool (FlagsAndCC.FlagsAndCC flags cc))
4702      (with_flags flags (materialize_bool_result (cond_code cc))))
4703
4704;; Get the `ProducesFlags` out of a `FlagsAndCC`.
4705(decl flags_and_cc_flags (FlagsAndCC) ProducesFlags)
4706(rule (flags_and_cc_flags (FlagsAndCC.FlagsAndCC flags _cc)) flags)
4707
4708;; Get the `IntCC` out of a `FlagsAndCC`.
4709(decl flags_and_cc_cc (FlagsAndCC) IntCC)
4710(rule (flags_and_cc_cc (FlagsAndCC.FlagsAndCC _flags cc)) cc)
4711
4712;; Helpers for lowering `icmp` sequences.
4713;; `lower_icmp` contains shared functionality for lowering `icmp`
4714;; sequences, which `lower_icmp_into_{reg,flags}` extend from.
4715(spec (lower_icmp c x y in_ty)
4716      (provide
4717            (= result
4718               (concat
4719                  (extract 67 64
4720                    (if (or (= c (IntCC.SignedGreaterThanOrEqual))
4721                            (= c (IntCC.SignedGreaterThan))
4722                            (= c (IntCC.SignedLessThanOrEqual))
4723                            (= c (IntCC.SignedLessThan)))
4724                        (if (<= in_ty 32)
4725                            (subs 32 (sign_ext 64 x) (sign_ext 64 y))
4726                            (subs 64 (sign_ext 64 x) (sign_ext 64 y)))
4727                        (if (<= in_ty 32)
4728                            (subs 32 (zero_ext 64 x) (zero_ext 64 y))
4729                            (subs 64 (zero_ext 64 x) (zero_ext 64 y)))))
4730                  c)))
4731      (require
4732            (or
4733                (= c (IntCC.Equal))
4734                (= c (IntCC.NotEqual))
4735                (= c (IntCC.UnsignedGreaterThanOrEqual))
4736                (= c (IntCC.UnsignedGreaterThan))
4737                (= c (IntCC.UnsignedLessThanOrEqual))
4738                (= c (IntCC.UnsignedLessThan))
4739                (= c (IntCC.SignedGreaterThanOrEqual))
4740                (= c (IntCC.SignedGreaterThan))
4741                (= c (IntCC.SignedLessThanOrEqual))
4742                (= c (IntCC.SignedLessThan)))
4743            (or (= in_ty 8)
4744                (= in_ty 16)
4745                (= in_ty 32)
4746                (= in_ty 64))
4747            (= in_ty (widthof x))
4748            (= in_ty (widthof y))))
4749(instantiate lower_icmp
4750    ((args (bv 8) (bv 8) (bv 8) Int) (ret (bv 12)) (canon (bv 8)))
4751    ((args (bv 8) (bv 16) (bv 16) Int) (ret (bv 12)) (canon (bv 16)))
4752    ((args (bv 8) (bv 32) (bv 32) Int) (ret (bv 12)) (canon (bv 32)))
4753    ((args (bv 8) (bv 64) (bv 64) Int) (ret (bv 12)) (canon (bv 64)))
4754)
4755(decl lower_icmp (IntCC Value Value Type) FlagsAndCC)
4756
4757(spec (lower_icmp_into_reg c x y in_ty out_ty)
4758      (provide
4759            (= result
4760               (switch c
4761                 ((IntCC.Equal) (if (= x y) #x01 #x00))
4762                 ((IntCC.NotEqual) (if (not (= x y)) #x01 #x00))
4763                 ((IntCC.SignedGreaterThan) (if (bvsgt x y) #x01 #x00))
4764                 ((IntCC.SignedGreaterThanOrEqual) (if (bvsge x y) #x01 #x00))
4765                 ((IntCC.SignedLessThan) (if (bvslt x y) #x01 #x00))
4766                 ((IntCC.SignedLessThanOrEqual) (if (bvsle x y) #x01 #x00))
4767                 ((IntCC.UnsignedGreaterThan) (if (bvugt x y) #x01 #x00))
4768                 ((IntCC.UnsignedGreaterThanOrEqual) (if (bvuge x y) #x01 #x00))
4769                 ((IntCC.UnsignedLessThan) (if (bvult x y) #x01 #x00))
4770                 ((IntCC.UnsignedLessThanOrEqual) (if (bvule x y) #x01 #x00)))))
4771      (require
4772            (or
4773                (= c (IntCC.Equal))
4774                (= c (IntCC.NotEqual))
4775                (= c (IntCC.UnsignedGreaterThanOrEqual))
4776                (= c (IntCC.UnsignedGreaterThan))
4777                (= c (IntCC.UnsignedLessThanOrEqual))
4778                (= c (IntCC.UnsignedLessThan))
4779                (= c (IntCC.SignedGreaterThanOrEqual))
4780                (= c (IntCC.SignedGreaterThan))
4781                (= c (IntCC.SignedLessThanOrEqual))
4782                (= c (IntCC.SignedLessThan)))
4783            (or (= in_ty 8)
4784                (= in_ty 16)
4785                (= in_ty 32)
4786                (= in_ty 64))
4787            (= in_ty (widthof x))
4788            (= in_ty (widthof y))
4789            (= out_ty 8)))
4790(instantiate lower_icmp_into_reg
4791    ((args (bv 8) (bv 8) (bv 8) Int Int) (ret (bv 8)) (canon (bv 8)))
4792    ((args (bv 8) (bv 16) (bv 16) Int Int) (ret (bv 8)) (canon (bv 16)))
4793    ((args (bv 8) (bv 32) (bv 32) Int Int) (ret (bv 8)) (canon (bv 32)))
4794    ((args (bv 8) (bv 64) (bv 64) Int Int) (ret (bv 8)) (canon (bv 64)))
4795)
4796(decl lower_icmp_into_reg (IntCC Value Value Type Type) ValueRegs)
4797(decl lower_icmp_into_flags (IntCC Value Value Type) FlagsAndCC)
4798
4799(spec (lower_icmp_const c x y in_ty)
4800      (provide
4801            (= result
4802               (concat (extract 67 64
4803                              (if (or (= c (IntCC.SignedGreaterThanOrEqual))
4804                                      (= c (IntCC.SignedGreaterThan))
4805                                      (= c (IntCC.SignedLessThanOrEqual))
4806                                      (= c (IntCC.SignedLessThan)))
4807                                    (if (<= in_ty 32)
4808                                          (subs 32 (sign_ext 64 x) y)
4809                                          (subs 64 (sign_ext 64 x) y))
4810                                    (if (<= in_ty 32)
4811                                          (subs 32 (zero_ext 64 x) y)
4812                                          (subs 64 (zero_ext 64 x) y))))
4813                       c)))
4814       (require
4815            (or
4816                (= c (IntCC.Equal))
4817                (= c (IntCC.NotEqual))
4818                (= c (IntCC.UnsignedGreaterThanOrEqual))
4819                (= c (IntCC.UnsignedGreaterThan))
4820                (= c (IntCC.UnsignedLessThanOrEqual))
4821                (= c (IntCC.UnsignedLessThan))
4822                (= c (IntCC.SignedGreaterThanOrEqual))
4823                (= c (IntCC.SignedGreaterThan))
4824                (= c (IntCC.SignedLessThanOrEqual))
4825                (= c (IntCC.SignedLessThan)))
4826            (or (= in_ty 32) (= in_ty 64))
4827            (= in_ty (widthof x))))
4828(instantiate lower_icmp_const
4829    ((args (bv 8) (bv 8) (bv 64) Int) (ret (bv 12)) (canon (bv 8)))
4830    ((args (bv 8) (bv 16) (bv 64) Int) (ret (bv 12)) (canon (bv 16)))
4831    ((args (bv 8) (bv 32) (bv 64) Int) (ret (bv 12)) (canon (bv 32)))
4832    ((args (bv 8) (bv 64) (bv 64) Int) (ret (bv 12)) (canon (bv 64)))
4833)
4834(decl lower_icmp_const (IntCC Value u64 Type) FlagsAndCC)
4835;; For most cases, `lower_icmp_into_flags` is the same as `lower_icmp`,
4836;; except for some I128 cases (see below).
4837(rule -1 (lower_icmp_into_flags cond x y ty) (lower_icmp cond x y ty))
4838
4839;; Vectors.
4840;; `icmp` into flags for vectors is invalid.
4841(rule 1 (lower_icmp_into_reg cond x y in_ty @ (multi_lane _ _) _out_ty)
4842      (let ((cond Cond (cond_code cond))
4843            (rn Reg (put_in_reg x))
4844            (rm Reg (put_in_reg y)))
4845       (vec_cmp rn rm in_ty cond)))
4846
4847;; Determines the appropriate extend op given the value type and the given ArgumentExtension.
4848(spec (lower_extend_op ty b)
4849      (provide
4850            (= result
4851               (switch ty
4852                       (8 (switch b ((ArgumentExtension.Sext) (ExtendOp.SXTB))
4853                                    ((ArgumentExtension.Uext) (ExtendOp.UXTB))))
4854                       (16 (switch b ((ArgumentExtension.Sext) (ExtendOp.SXTH))
4855                                     ((ArgumentExtension.Uext) (ExtendOp.UXTH)))))))
4856      (require (or (= ty 8) (= ty 16) (= ty 32) (= ty 64))))
4857(decl lower_extend_op (Type ArgumentExtension) ExtendOp)
4858(rule (lower_extend_op $I8 (ArgumentExtension.Sext)) (ExtendOp.SXTB))
4859(rule (lower_extend_op $I16 (ArgumentExtension.Sext)) (ExtendOp.SXTH))
4860(rule (lower_extend_op $I8 (ArgumentExtension.Uext)) (ExtendOp.UXTB))
4861(rule (lower_extend_op $I16 (ArgumentExtension.Uext)) (ExtendOp.UXTH))
4862
4863;; Integers <= 64-bits.
4864(rule lower_icmp_into_reg_8_16_32_64 -2 (lower_icmp_into_reg cond rn rm in_ty out_ty)
4865      (if (ty_int_ref_scalar_64 in_ty))
4866      (let ((cc Cond (cond_code cond)))
4867        (flags_and_cc_to_bool (lower_icmp cond rn rm in_ty))))
4868
4869(rule lower_icmp_8_16_signed 1 (lower_icmp cond rn rm (fits_in_16 ty))
4870      (if (signed_cond_code cond))
4871      (let ((rn Reg (put_in_reg_sext32 rn)))
4872      (flags_and_cc (cmp_extend (operand_size ty) rn rm (lower_extend_op ty (ArgumentExtension.Sext))) cond)))
4873(rule lower_icmp_8_16_unsigned_imm -1 (lower_icmp cond rn (imm12_from_value rm) (fits_in_16 ty))
4874      (let ((rn Reg (put_in_reg_zext32 rn)))
4875      (flags_and_cc (cmp_imm (operand_size ty) rn rm) cond)))
4876(rule lower_icmp_8_16_unsigned -2 (lower_icmp cond rn rm (fits_in_16 ty))
4877      (let ((rn Reg (put_in_reg_zext32 rn)))
4878      (flags_and_cc (cmp_extend (operand_size ty) rn rm (lower_extend_op ty (ArgumentExtension.Uext))) cond)))
4879(rule lower_icmp_32_64_const -3 (lower_icmp cond rn (u64_from_iconst c) ty)
4880      (if (ty_int_ref_scalar_64 ty))
4881      (lower_icmp_const cond rn c ty))
4882(rule lower_icmp_32_64 -4 (lower_icmp cond rn rm ty)
4883      (if (ty_int_ref_scalar_64 ty))
4884      (flags_and_cc (cmp (operand_size ty) rn rm) cond))
4885
4886;; We get better encodings when testing against an immediate that's even instead
4887;; of odd, so rewrite comparisons to use even immediates:
4888;;
4889;;         A >= B + 1
4890;;     ==> A - 1 >= B
4891;;     ==> A > B
4892(rule lower_icmp_const_32_64_ugte (lower_icmp_const (IntCC.UnsignedGreaterThanOrEqual) a b ty)
4893      (if (ty_int_ref_scalar_64 ty))
4894      (if-let true (u64_is_odd b))
4895      (if-let (imm12_from_u64 imm) (u64_wrapping_sub b 1))
4896  (flags_and_cc (cmp_imm (operand_size ty) a imm) (IntCC.UnsignedGreaterThan)))
4897
4898(rule lower_icmp_const_32_64_sgte (lower_icmp_const (IntCC.SignedGreaterThanOrEqual) a b ty)
4899      (if (ty_int_ref_scalar_64 ty))
4900      (if-let true (u64_is_odd b))
4901      (if-let (imm12_from_u64 imm) (u64_wrapping_sub b 1))
4902  (flags_and_cc (cmp_imm (operand_size ty) a imm) (IntCC.SignedGreaterThan)))
4903
4904(rule lower_icmp_const_32_64_imm -1 (lower_icmp_const cond rn (imm12_from_u64 c) ty)
4905      (if (ty_int_ref_scalar_64 ty))
4906  (flags_and_cc (cmp_imm (operand_size ty) rn c) cond))
4907(rule lower_icmp_const_32_64 -2 (lower_icmp_const cond rn c ty)
4908      (if (ty_int_ref_scalar_64 ty))
4909  (flags_and_cc (cmp (operand_size ty) rn (imm ty (ImmExtend.Zero) c)) cond))
4910
4911
4912;; 128-bit integers.
4913(rule (lower_icmp_into_reg cond @ (IntCC.Equal) rn rm $I128 $I8)
4914      (let ((cc Cond (cond_code cond)))
4915       (flags_and_cc_to_bool
4916        (lower_icmp cond rn rm $I128))))
4917(rule (lower_icmp_into_reg cond @ (IntCC.NotEqual) rn rm $I128 $I8)
4918      (let ((cc Cond (cond_code cond)))
4919       (flags_and_cc_to_bool
4920        (lower_icmp cond rn rm $I128))))
4921
4922;; cmp lhs_lo, rhs_lo
4923;; ccmp lhs_hi, rhs_hi, #0, eq
4924(decl lower_icmp_i128_eq_ne (Value Value) ProducesFlags)
4925(rule (lower_icmp_i128_eq_ne lhs rhs)
4926      (let ((lhs ValueRegs (put_in_regs lhs))
4927            (rhs ValueRegs (put_in_regs rhs))
4928            (lhs_lo Reg (value_regs_get lhs 0))
4929            (lhs_hi Reg (value_regs_get lhs 1))
4930            (rhs_lo Reg (value_regs_get rhs 0))
4931            (rhs_hi Reg (value_regs_get rhs 1))
4932            (cmp_inst ProducesFlags (cmp (OperandSize.Size64) lhs_lo rhs_lo)))
4933       (ccmp (OperandSize.Size64) lhs_hi rhs_hi
4934        (nzcv false false false false) (Cond.Eq) cmp_inst)))
4935
4936(rule (lower_icmp (IntCC.Equal) lhs rhs $I128)
4937      (flags_and_cc (lower_icmp_i128_eq_ne lhs rhs) (IntCC.Equal)))
4938(rule (lower_icmp (IntCC.NotEqual) lhs rhs $I128)
4939      (flags_and_cc (lower_icmp_i128_eq_ne lhs rhs) (IntCC.NotEqual)))
4940
4941;; cmp      lhs_lo, rhs_lo
4942;; cset     tmp1, unsigned_cond
4943;; cmp      lhs_hi, rhs_hi
4944;; cset     tmp2, cond
4945;; csel     dst, tmp1, tmp2, eq
4946(rule -1 (lower_icmp_into_reg cond lhs rhs $I128 $I8)
4947      (let ((unsigned_cond Cond (cond_code (intcc_unsigned cond)))
4948            (cond Cond (cond_code cond))
4949            (lhs ValueRegs (put_in_regs lhs))
4950            (rhs ValueRegs (put_in_regs rhs))
4951            (lhs_lo Reg (value_regs_get lhs 0))
4952            (lhs_hi Reg (value_regs_get lhs 1))
4953            (rhs_lo Reg (value_regs_get rhs 0))
4954            (rhs_hi Reg (value_regs_get rhs 1))
4955            (tmp1 Reg (with_flags_reg (cmp (OperandSize.Size64) lhs_lo rhs_lo)
4956                                      (materialize_bool_result unsigned_cond))))
4957        (with_flags (cmp (OperandSize.Size64) lhs_hi rhs_hi)
4958                    (lower_icmp_i128_consumer cond tmp1))))
4959
4960(decl lower_icmp_i128_consumer (Cond Reg) ConsumesFlags)
4961(rule (lower_icmp_i128_consumer cond tmp1)
4962      (let ((tmp2 WritableReg (temp_writable_reg $I64))
4963            (dst WritableReg (temp_writable_reg $I64)))
4964       (ConsumesFlags.ConsumesFlagsTwiceReturnsValueRegs
4965        (MInst.CSet tmp2 cond)
4966        (MInst.CSel dst (Cond.Eq) tmp1 tmp2)
4967        (value_reg dst))))
4968
4969; Recursion: bounded since recursive calls reduce type width (128-bit to 64-bit).
4970(decl rec lower_bmask (Type Type ValueRegs) ValueRegs)
4971
4972
4973;; For conversions that exactly fit a register, we can use csetm.
4974;;
4975;; cmp   val, #0
4976;; csetm res, ne
4977(rule 0
4978      (lower_bmask (fits_in_64 _) (ty_32_or_64 in_ty) val)
4979      (with_flags_reg
4980        (cmp_imm (operand_size in_ty) (value_regs_get val 0) (u8_into_imm12 0))
4981        (csetm (Cond.Ne))))
4982
4983;; For conversions from a 128-bit value into a 64-bit or smaller one, we or the
4984;; two registers of the 128-bit value together, and then recurse with the
4985;; combined value as a 64-bit test.
4986;;
4987;; orr   val, lo, hi
4988;; cmp   val, #0
4989;; csetm res, ne
4990(rule 1
4991      (lower_bmask (fits_in_64 ty) $I128 val)
4992      (let ((lo Reg (value_regs_get val 0))
4993            (hi Reg (value_regs_get val 1))
4994            (combined Reg (orr $I64 lo hi)))
4995        (lower_bmask ty $I64 (value_reg combined))))
4996
4997;; For converting from any type into i128, duplicate the result of
4998;; converting to i64.
4999(rule 2
5000      (lower_bmask $I128 in_ty val)
5001      (let ((res ValueRegs (lower_bmask $I64 in_ty val))
5002            (res Reg (value_regs_get res 0)))
5003        (value_regs res res)))
5004
5005;; For conversions smaller than a register, we need to mask off the high bits, and then
5006;; we can recurse into the general case.
5007;;
5008;; and   tmp, val, #ty_mask
5009;; cmp   tmp, #0
5010;; csetm res, ne
5011(rule 3
5012      (lower_bmask out_ty (fits_in_16 in_ty) val)
5013      ; This if-let can't fail due to ty_mask always producing 8/16 consecutive 1s.
5014      (if-let mask_bits (imm_logic_from_u64 $I32 (ty_mask in_ty)))
5015      (let ((masked Reg (and_imm $I32 (value_regs_get val 0) mask_bits)))
5016        (lower_bmask out_ty $I32 masked)))
5017
5018;; Exceptional `lower_icmp_into_flags` rules.
5019;; We need to guarantee that the flags for `cond` are correct, so we
5020;; compare `dst` with 1.
5021(rule (lower_icmp_into_flags cond @ (IntCC.SignedGreaterThanOrEqual) lhs rhs $I128)
5022      (let ((dst ValueRegs (lower_icmp_into_reg cond lhs rhs $I128 $I8))
5023            (dst Reg (value_regs_get dst 0))
5024            (tmp Reg (imm $I64 (ImmExtend.Sign) 1))) ;; mov tmp, #1
5025        (flags_and_cc (cmp (OperandSize.Size64) dst tmp) cond)))
5026(rule (lower_icmp_into_flags cond @ (IntCC.UnsignedGreaterThanOrEqual) lhs rhs $I128)
5027      (let ((dst ValueRegs (lower_icmp_into_reg cond lhs rhs $I128 $I8))
5028            (dst Reg (value_regs_get dst 0))
5029            (tmp Reg (imm $I64 (ImmExtend.Zero) 1)))
5030        (flags_and_cc (cmp (OperandSize.Size64) dst tmp) cond)))
5031(rule (lower_icmp_into_flags cond @ (IntCC.SignedLessThanOrEqual) lhs rhs $I128)
5032      (let ((dst ValueRegs (lower_icmp_into_reg cond lhs rhs $I128 $I8))
5033            (dst Reg (value_regs_get dst 0))
5034            (tmp Reg (imm $I64 (ImmExtend.Sign) 1)))
5035       (flags_and_cc (cmp (OperandSize.Size64) tmp dst) cond)))
5036(rule (lower_icmp_into_flags cond @ (IntCC.UnsignedLessThanOrEqual) lhs rhs $I128)
5037      (let ((dst ValueRegs (lower_icmp_into_reg cond lhs rhs $I128 $I8))
5038            (dst Reg (value_regs_get dst 0))
5039            (tmp Reg (imm $I64 (ImmExtend.Zero) 1)))
5040        (flags_and_cc (cmp (OperandSize.Size64) tmp dst) cond)))
5041;; For strict comparisons, we compare with 0.
5042(rule (lower_icmp_into_flags cond @ (IntCC.SignedGreaterThan) lhs rhs $I128)
5043      (let ((dst ValueRegs (lower_icmp_into_reg cond lhs rhs $I128 $I8))
5044            (dst Reg (value_regs_get dst 0)))
5045        (flags_and_cc (cmp (OperandSize.Size64) dst (zero_reg)) cond)))
5046(rule (lower_icmp_into_flags cond @ (IntCC.UnsignedGreaterThan) lhs rhs $I128)
5047      (let ((dst ValueRegs (lower_icmp_into_reg cond lhs rhs $I128 $I8))
5048            (dst Reg (value_regs_get dst 0)))
5049        (flags_and_cc (cmp (OperandSize.Size64) dst (zero_reg)) cond)))
5050(rule (lower_icmp_into_flags cond @ (IntCC.SignedLessThan) lhs rhs $I128)
5051      (let ((dst ValueRegs (lower_icmp_into_reg cond lhs rhs $I128 $I8))
5052            (dst Reg (value_regs_get dst 0)))
5053       (flags_and_cc (cmp (OperandSize.Size64) (zero_reg) dst) cond)))
5054(rule (lower_icmp_into_flags cond @ (IntCC.UnsignedLessThan) lhs rhs $I128)
5055      (let ((dst ValueRegs (lower_icmp_into_reg cond lhs rhs $I128 $I8))
5056            (dst Reg (value_regs_get dst 0)))
5057       (flags_and_cc (cmp (OperandSize.Size64) (zero_reg) dst) cond)))
5058
5059;; Helpers for generating select instruction sequences.
5060(decl lower_select (ProducesFlags Cond Type Value Value) ValueRegs)
5061(rule 2 (lower_select flags cond (ty_scalar_float (fits_in_64 ty)) rn rm)
5062      (with_flags flags (fpu_csel ty cond rn rm)))
5063(rule 4 (lower_select flags cond $F128 rn rm)
5064      (with_flags flags (vec_csel cond rn rm)))
5065(rule 3 (lower_select flags cond (ty_vec128 ty) rn rm)
5066      (with_flags flags (vec_csel cond rn rm)))
5067(rule (lower_select flags cond ty rn rm)
5068      (if (ty_vec64 ty))
5069      (with_flags flags (fpu_csel $F64 cond rn rm)))
5070(rule 4 (lower_select flags cond $I128 rn rm)
5071      (let ((dst_lo WritableReg (temp_writable_reg $I64))
5072            (dst_hi WritableReg (temp_writable_reg $I64))
5073            (rn ValueRegs (put_in_regs rn))
5074            (rm ValueRegs (put_in_regs rm))
5075            (rn_lo Reg (value_regs_get rn 0))
5076            (rn_hi Reg (value_regs_get rn 1))
5077            (rm_lo Reg (value_regs_get rm 0))
5078            (rm_hi Reg (value_regs_get rm 1)))
5079       (with_flags flags
5080        (ConsumesFlags.ConsumesFlagsTwiceReturnsValueRegs
5081         (MInst.CSel dst_lo cond rn_lo rm_lo)
5082         (MInst.CSel dst_hi cond rn_hi rm_hi)
5083         (value_regs dst_lo dst_hi)))))
5084(rule 1 (lower_select flags cond ty rn rm)
5085      (if (ty_int_ref_scalar_64 ty))
5086      (with_flags flags (csel cond rn rm)))
5087
5088;; Helper for emitting `MInst.Jump` instructions.
5089(decl aarch64_jump (BranchTarget) SideEffectNoResult)
5090(rule (aarch64_jump target)
5091      (SideEffectNoResult.Inst (MInst.Jump target)))
5092
5093;; Helper for emitting `MInst.JTSequence` instructions.
5094;; Emit the compound instruction that does:
5095;;
5096;; b.hs default
5097;; csel rB, xzr, rIndex, hs
5098;; csdb
5099;; adr rA, jt
5100;; ldrsw rB, [rA, rB, uxtw #2]
5101;; add rA, rA, rB
5102;; br rA
5103;; [jt entries]
5104;;
5105;; This must be *one* instruction in the vcode because
5106;; we cannot allow regalloc to insert any spills/fills
5107;; in the middle of the sequence; otherwise, the ADR's
5108;; PC-rel offset to the jumptable would be incorrect.
5109;; (The alternative is to introduce a relocation pass
5110;; for inlined jumptables, which is much worse, IMHO.)
5111(decl jt_sequence (Reg MachLabel BoxVecMachLabel) ConsumesFlags)
5112(rule (jt_sequence ridx default targets)
5113      (let ((rtmp1 WritableReg (temp_writable_reg $I64))
5114            (rtmp2 WritableReg (temp_writable_reg $I64)))
5115       (ConsumesFlags.ConsumesFlagsSideEffect
5116        (MInst.JTSequence default targets ridx rtmp1 rtmp2))))
5117
5118;; Helper for emitting `MInst.CondBr` instructions.
5119(decl cond_br (BranchTarget BranchTarget CondBrKind) ConsumesFlags)
5120(rule (cond_br taken not_taken kind)
5121      (ConsumesFlags.ConsumesFlagsSideEffect
5122       (MInst.CondBr taken not_taken kind)))
5123
5124;; Helper for emitting `MInst.TestBitAndBranch` instructions.
5125(decl test_branch (TestBitAndBranchKind BranchTarget BranchTarget Reg u8) SideEffectNoResult)
5126(rule (test_branch kind taken not_taken rn bit)
5127      (SideEffectNoResult.Inst (MInst.TestBitAndBranch kind taken not_taken rn bit)))
5128
5129;; Helper for emitting `tbnz` instructions.
5130(decl tbnz (BranchTarget BranchTarget Reg u8) SideEffectNoResult)
5131(rule (tbnz taken not_taken rn bit)
5132      (test_branch (TestBitAndBranchKind.NZ) taken not_taken rn bit))
5133
5134;; Helper for emitting `tbz` instructions.
5135(decl tbz (BranchTarget BranchTarget Reg u8) SideEffectNoResult)
5136(rule (tbz taken not_taken rn bit)
5137      (test_branch (TestBitAndBranchKind.Z) taken not_taken rn bit))
5138
5139;; Helper for emitting `MInst.MovToNZCV` instructions.
5140(decl mov_to_nzcv (Reg) ProducesFlags)
5141(rule (mov_to_nzcv rn)
5142      (ProducesFlags.ProducesFlagsSideEffect
5143       (MInst.MovToNZCV rn)))
5144
5145;; Helper for emitting `MInst.EmitIsland` instructions.
5146(decl emit_island (CodeOffset) SideEffectNoResult)
5147(rule (emit_island needed_space)
5148      (SideEffectNoResult.Inst
5149       (MInst.EmitIsland needed_space)))
5150
5151;; Helper for emitting `br_table` sequences.
5152(decl br_table_impl (u64 Reg MachLabel BoxVecMachLabel) Unit)
5153(rule (br_table_impl (imm12_from_u64 jt_size) ridx default targets)
5154      (emit_side_effect (with_flags_side_effect
5155           (cmp_imm (OperandSize.Size32) ridx jt_size)
5156           (jt_sequence ridx default targets))))
5157(rule -1 (br_table_impl jt_size ridx default targets)
5158      (let ((jt_size Reg (imm $I64 (ImmExtend.Zero) jt_size)))
5159       (emit_side_effect (with_flags_side_effect
5160            (cmp (OperandSize.Size32) ridx jt_size)
5161            (jt_sequence ridx default targets)))))
5162
5163;; Helper for emitting the `uzp1` instruction
5164(decl vec_uzp1 (Reg Reg VectorSize) Reg)
5165(rule (vec_uzp1 rn rm size) (vec_rrr (VecALUOp.Uzp1) rn rm size))
5166
5167;; Helper for emitting the `uzp2` instruction
5168(decl vec_uzp2 (Reg Reg VectorSize) Reg)
5169(rule (vec_uzp2 rn rm size) (vec_rrr (VecALUOp.Uzp2) rn rm size))
5170
5171;; Helper for emitting the `zip1` instruction
5172(decl vec_zip1 (Reg Reg VectorSize) Reg)
5173(rule (vec_zip1 rn rm size) (vec_rrr (VecALUOp.Zip1) rn rm size))
5174
5175;; Helper for emitting the `zip2` instruction
5176(decl vec_zip2 (Reg Reg VectorSize) Reg)
5177(rule (vec_zip2 rn rm size) (vec_rrr (VecALUOp.Zip2) rn rm size))
5178
5179;; Helper for emitting the `trn1` instruction
5180(decl vec_trn1 (Reg Reg VectorSize) Reg)
5181(rule (vec_trn1 rn rm size) (vec_rrr (VecALUOp.Trn1) rn rm size))
5182
5183;; Helper for emitting the `trn2` instruction
5184(decl vec_trn2 (Reg Reg VectorSize) Reg)
5185(rule (vec_trn2 rn rm size) (vec_rrr (VecALUOp.Trn2) rn rm size))
5186
5187;; Helper for creating a zero value `ASIMDMovModImm` immediate.
5188(decl asimd_mov_mod_imm_zero (ScalarSize) ASIMDMovModImm)
5189(extern constructor asimd_mov_mod_imm_zero asimd_mov_mod_imm_zero)
5190
5191;; Helper for fallibly creating an `ASIMDMovModImm` immediate from its parts.
5192(decl pure partial asimd_mov_mod_imm_from_u64 (u64 ScalarSize) ASIMDMovModImm)
5193(extern constructor asimd_mov_mod_imm_from_u64 asimd_mov_mod_imm_from_u64)
5194
5195;; Helper for fallibly creating an `ASIMDFPModImm` immediate from its parts.
5196(decl pure partial asimd_fp_mod_imm_from_u64 (u64 ScalarSize) ASIMDFPModImm)
5197(extern constructor asimd_fp_mod_imm_from_u64 asimd_fp_mod_imm_from_u64)
5198
5199;; Helper for creating a `VecDupFPImm` instruction
5200(decl vec_dup_fp_imm (ASIMDFPModImm VectorSize) Reg)
5201(rule (vec_dup_fp_imm imm size)
5202      (let ((dst WritableReg (temp_writable_reg $I8X16))
5203            (_ Unit (emit (MInst.VecDupFPImm dst imm size))))
5204       dst))
5205
5206;; Helper for creating a `FpuLoad64` instruction
5207(decl fpu_load64 (AMode MemFlags) Reg)
5208(rule (fpu_load64 amode flags)
5209      (let ((dst WritableReg (temp_writable_reg $I8X16))
5210            (_ Unit (emit (MInst.FpuLoad64 dst amode flags))))
5211       dst))
5212
5213;; Helper for creating a `FpuLoad128` instruction
5214(decl fpu_load128 (AMode MemFlags) Reg)
5215(rule (fpu_load128 amode flags)
5216      (let ((dst WritableReg (temp_writable_reg $I8X16))
5217            (_ Unit (emit (MInst.FpuLoad128 dst amode flags))))
5218       dst))
5219
5220;; Helper for creating an `LabelAddress` instruction.
5221(decl a64_label_address (MachLabel) Reg)
5222(rule (a64_label_address label)
5223      (let ((dst WritableReg (temp_writable_reg $I64))
5224            (_ Unit (emit (MInst.LabelAddress dst label))))
5225        dst))
5226
5227;; Helper for creating a `SequencePoint` instruction.
5228(decl a64_sequence_point () SideEffectNoResult)
5229(rule (a64_sequence_point)
5230      (SideEffectNoResult.Inst (MInst.SequencePoint)))
5231