1;; Instruction formats.
2(type MInst
3  (enum
4    ;; A no-op of zero size.
5    (Nop0)
6    (Nop4)
7
8    ;; load immediate
9    (Lui
10      (rd WritableReg)
11      (imm Imm20))
12
13    (LoadInlineConst
14      (rd WritableReg)
15      (ty Type)
16      (imm u64))
17
18     (Auipc
19      (rd WritableReg)
20      (imm Imm20))
21
22    (Fli
23      (width FpuOPWidth)
24      (imm FliConstant)
25      (rd WritableReg))
26
27    ;; An ALU operation with one register sources and a register destination.
28    (FpuRR
29      (alu_op FpuOPRR)
30      (width FpuOPWidth)
31      (frm FRM)
32      (rd WritableReg)
33      (rs Reg))
34
35
36    ;; An ALU operation with two register sources and a register destination.
37    (AluRRR
38      (alu_op AluOPRRR)
39      (rd WritableReg)
40      (rs1 Reg)
41      (rs2 Reg))
42
43    ;; An ALU operation with two register sources and a register destination.
44    (FpuRRR
45      (alu_op FpuOPRRR)
46      (width FpuOPWidth)
47      (frm FRM)
48      (rd WritableReg)
49      (rs1 Reg)
50      (rs2 Reg))
51
52    ;; An ALU operation with three register sources and a register destination.
53    (FpuRRRR
54      (alu_op FpuOPRRRR)
55      (width FpuOPWidth)
56      (frm FRM)
57      (rd WritableReg)
58      (rs1 Reg)
59      (rs2 Reg)
60      (rs3 Reg))
61
62    ;; An ALU operation with a register source and an immediate-12 source, and a register
63    ;; destination.
64    (AluRRImm12
65      (alu_op AluOPRRI)
66      (rd WritableReg)
67      (rs Reg)
68      (imm12 Imm12))
69
70    ;; A CSR Reading or Writing instruction with a register source and a register destination.
71    (CsrReg
72      (op CsrRegOP)
73      (rd WritableReg)
74      (rs Reg)
75      (csr CSR))
76
77    ;; A CSR Writing instruction with an immediate source and a register destination.
78    (CsrImm
79      (op CsrImmOP)
80      (rd WritableReg)
81      (imm UImm5)
82      (csr CSR))
83
84    ;; An load
85    (Load
86      (rd WritableReg)
87      (op LoadOP)
88      (flags MemFlags)
89      (from AMode))
90    ;; An Store
91    (Store
92      (to AMode)
93      (op StoreOP)
94      (flags MemFlags)
95      (src Reg))
96
97    ;; A pseudo-instruction that captures register arguments in vregs.
98    (Args
99      (args VecArgPair))
100
101    ;; A pseudo-instruction that moves vregs to return registers.
102    (Rets
103      (rets VecRetPair))
104
105    (Ret)
106
107     (Extend
108      (rd WritableReg)
109      (rn Reg)
110      (signed bool)
111      (from_bits u8)
112      (to_bits u8))
113
114    ;; A machine direct-call instruction.
115    (Call (info BoxCallInfo))
116
117    ;; A machine indirect-call instruction.
118    (CallInd (info BoxCallIndInfo))
119
120    ;; A direct return-call macro instruction.
121    (ReturnCall (info BoxReturnCallInfo))
122
123    ;; An indirect return-call macro instruction.
124    (ReturnCallInd (info BoxReturnCallIndInfo))
125
126    ;; Emits a trap with the given trap code if the comparison succeeds
127    (TrapIf
128      (rs1 Reg)
129      (rs2 Reg)
130      (cc IntCC)
131      (trap_code TrapCode))
132
133    (Jal
134      ;; (rd WritableReg) don't use
135      (label MachLabel))
136
137    (CondBr
138      (taken CondBrTarget)
139      (not_taken CondBrTarget)
140      (kind IntegerCompare))
141
142    ;; Load an inline symbol reference.
143    (LoadExtNameGot
144      (rd WritableReg)
145      (name BoxExternalName))
146    (LoadExtNameNear
147      (rd WritableReg)
148      (name BoxExternalName)
149      (offset i64))
150    (LoadExtNameFar
151      (rd WritableReg)
152      (name BoxExternalName)
153      (offset i64))
154
155    ;; Load a TLS symbol address
156    (ElfTlsGetAddr
157      (rd WritableReg)
158      (name BoxExternalName))
159
160    ;; Load address referenced by `mem` into `rd`.
161    (LoadAddr
162      (rd WritableReg)
163      (mem AMode))
164
165    ;; A MOV instruction. These are encoded as OrR's (AluRRR form) but we
166    ;; keep them separate at the `Inst` level for better pretty-printing
167    ;; and faster `is_move()` logic.
168    (Mov
169      (rd WritableReg)
170      (rm Reg)
171      (ty Type))
172
173    ;; A MOV instruction, but where the source register is a non-allocatable
174    ;; PReg. It's important that the register be non-allocatable, as regalloc2
175    ;; will not see it as used.
176    (MovFromPReg
177      (rd WritableReg)
178      (rm PReg))
179
180    (Fence
181      (pred FenceReq)
182      (succ FenceReq))
183
184    (EBreak)
185
186    ;; An instruction guaranteed to always be undefined and to trigger an illegal instruction at
187    ;; runtime.
188    (Udf
189      (trap_code TrapCode))
190    ;; a jump and link register operation
191    (Jalr
192      ;;Plain unconditional jumps (assembler pseudo-op J) are encoded as a JAL with rd=x0.
193      (rd WritableReg)
194      (base Reg)
195      (offset Imm12))
196
197    ;; atomic operations.
198    (Atomic
199      (op AtomicOP)
200      (rd WritableReg)
201      (addr Reg)
202      (src Reg)
203      (amo AMO))
204    ;; an atomic store
205    (AtomicStore
206      (src Reg)
207      (ty Type)
208      (p Reg))
209    ;; an atomic load.
210    (AtomicLoad
211      (rd WritableReg)
212      (ty Type)
213      (p Reg))
214
215    ;; an atomic nand need using loop to implement.
216    (AtomicRmwLoop
217      (offset Reg)
218      (op AtomicRmwOp)
219      (dst WritableReg)
220      (ty Type)
221      (p Reg)
222      (x Reg)
223      (t0 WritableReg))
224
225    ;; select x or y base on condition
226    (Select
227      (dst WritableValueRegs)
228      (condition IntegerCompare)
229      (x ValueRegs)
230      (y ValueRegs))
231
232    (BrTable
233      (index Reg)
234      (tmp1 WritableReg)
235      (tmp2 WritableReg)
236      (targets VecMachLabel))
237
238    ;; atomic compare and set operation
239    (AtomicCas
240      (offset Reg)
241      (t0 WritableReg)
242      (dst WritableReg)
243      (e Reg)
244      (addr Reg)
245      (v Reg)
246      (ty Type))
247
248    (RawData (data VecU8))
249
250    ;; An unwind pseudo-instruction.
251       (Unwind
252        (inst UnwindInst))
253
254    ;; A dummy use, useful to keep a value alive.
255       (DummyUse
256        (reg Reg))
257
258    ;; A pseudoinstruction that loads the address of a label.
259    (LabelAddress (dst WritableReg)
260                  (label MachLabel))
261
262    ;; popcnt  if target doesn't support extension B
263    ;; use iteration to implement.
264    (Popcnt
265      (sum WritableReg)
266      (step WritableReg)
267      (tmp WritableReg)
268      (rs Reg)
269      (ty Type))
270
271    ;;; counting leading or trailing zeros.
272    (Cltz
273      ;; leading or trailing.
274      (leading bool)
275      (sum WritableReg)
276      (step WritableReg)
277      (tmp WritableReg)
278      (rs Reg)
279      (ty Type))
280
281    (Brev8
282      (rs Reg)
283      (ty Type)
284      (step WritableReg)
285      (tmp WritableReg)
286      (tmp2 WritableReg)
287      (rd WritableReg))
288    (StackProbeLoop
289      (guard_size u32)
290      (probe_count u32)
291      (tmp WritableReg))
292
293    (VecAluRRRR
294      (op VecAluOpRRRR)
295      (vd WritableReg)
296      (vd_src Reg)
297      (vs2 Reg)
298      (vs1 Reg)
299      (mask VecOpMasking)
300      (vstate VState))
301
302    (VecAluRRRImm5
303      (op VecAluOpRRRImm5)
304      (vd WritableReg)
305      (vd_src Reg)
306      (vs2 Reg)
307      (imm Imm5)
308      (mask VecOpMasking)
309      (vstate VState))
310
311    (VecAluRRR
312      (op VecAluOpRRR)
313      (vd WritableReg)
314      (vs2 Reg)
315      (vs1 Reg)
316      (mask VecOpMasking)
317      (vstate VState))
318
319    (VecAluRRImm5
320      (op VecAluOpRRImm5)
321      (vd WritableReg)
322      (vs2 Reg)
323      (imm Imm5)
324      (mask VecOpMasking)
325      (vstate VState))
326
327    (VecAluRR
328      (op VecAluOpRR)
329      (vd WritableReg)
330      (vs Reg)
331      (mask VecOpMasking)
332      (vstate VState))
333
334    (VecAluRImm5
335      (op VecAluOpRImm5)
336      (vd WritableReg)
337      (imm Imm5)
338      (mask VecOpMasking)
339      (vstate VState))
340
341    (VecSetState
342      (rd WritableReg)
343      (vstate VState))
344
345    (VecLoad
346      (eew VecElementWidth)
347      (to WritableReg)
348      (from VecAMode)
349      (flags MemFlags)
350      (mask VecOpMasking)
351      (vstate VState))
352
353    (VecStore
354      (eew VecElementWidth)
355      (to VecAMode)
356      (from Reg)
357      (flags MemFlags)
358      (mask VecOpMasking)
359      (vstate VState))
360
361    (EmitIsland
362     ;; The needed space before the next deadline.
363     (needed_space u32))
364
365    ;; A pseudoinstruction that serves as a sequence point.
366    (SequencePoint)
367))
368
369(type AtomicOP (enum
370  (LrW)
371  (ScW)
372  (AmoswapW)
373  (AmoaddW)
374  (AmoxorW)
375  (AmoandW)
376  (AmoorW)
377  (AmominW)
378  (AmomaxW)
379  (AmominuW)
380  (AmomaxuW)
381  (LrD)
382  (ScD)
383  (AmoswapD)
384  (AmoaddD)
385  (AmoxorD)
386  (AmoandD)
387  (AmoorD)
388  (AmominD)
389  (AmomaxD)
390  (AmominuD)
391  (AmomaxuD)
392))
393
394(type FpuOPRRRR (enum
395  (Fmadd)
396  (Fmsub)
397  (Fnmsub)
398  (Fnmadd)
399))
400
401(type FClassResult (enum
402  ;;0 rs1 is −∞.
403  (NegInfinite)
404  ;; 1 rs1 is a negative normal number.
405  (NegNormal)
406  ;; 2 rs1 is a negative subnormal number.
407  (NegSubNormal)
408  ;; 3 rs1 is −0.
409  (NegZero)
410  ;; 4 rs1 is +0.
411  (PosZero)
412  ;; 5 rs1 is a positive subnormal number.
413  (PosSubNormal)
414  ;; 6 rs1 is a positive normal number.
415  (PosNormal)
416  ;; 7 rs1 is +∞.
417  (PosInfinite)
418  ;; 8 rs1 is a signaling NaN.
419  (SNaN)
420  ;; 9 rs1 is a quiet NaN.
421  (QNaN)
422))
423
424(type FliConstant (primitive FliConstant))
425
426(type FpuOPWidth (enum
427  (S)
428  (D)
429  (H)
430  (Q)
431))
432
433(decl pure fpu_op_width_from_ty (Type) FpuOPWidth)
434(extern constructor fpu_op_width_from_ty fpu_op_width_from_ty)
435(convert Type FpuOPWidth fpu_op_width_from_ty)
436
437(type FpuOPRR (enum
438  (Fsqrt) ;; fsqrt.{fmt}
439  (Fclass) ;; fclass.{fmt}
440  (FcvtWFmt) ;; fcvt.w.{fmt}
441  (FcvtWuFmt) ;; fcvt.wu.{fmt}
442  (FcvtLFmt) ;; fcvt.l.{fmt}
443  (FcvtLuFmt) ;; fcvt.lu.{fmt}
444  (FcvtFmtW) ;; fcvt.{fmt}.w
445  (FcvtFmtWu) ;; fcvt.{fmt}.wu
446  (FcvtFmtL) ;; fcvt.{fmt}.l
447  (FcvtFmtLu) ;; fcvt.{fmt}.lu
448  (FmvXFmt) ;; fmv.x.{fmt}
449  (FmvFmtX) ;; fmv.{fmt}.x
450  (FcvtSD) ;; fcvt.s.d
451  (FcvtDS) ;; fcvt.d.s
452
453  ;; Zfa Extension
454  (Fround) ;; fround.{fmt}
455))
456
457(type LoadOP (enum
458  (Lb)
459  (Lh)
460  (Lw)
461  (Lbu)
462  (Lhu)
463  (Lwu)
464  (Ld)
465  (Flh)
466  (Flw)
467  (Fld)
468))
469
470(type StoreOP (enum
471  (Sb)
472  (Sh)
473  (Sw)
474  (Sd)
475  (Fsh)
476  (Fsw)
477  (Fsd)
478))
479
480(type AluOPRRR (enum
481  ;; base set
482  (Add)
483  (Sub)
484  (Sll)
485  (Slt)
486  (SltU)
487  (Sgt)
488  (Sgtu)
489  (Xor)
490  (Srl)
491  (Sra)
492  (Or)
493  (And)
494
495  ;; RV64I Base Instruction Set (in addition to RV32I)
496  (Addw)
497  (Subw)
498  (Sllw)
499  (Srlw)
500  (Sraw)
501
502
503  ;;RV32M Standard Extension
504  (Mul)
505  (Mulh)
506  (Mulhsu)
507  (Mulhu)
508  (Div)
509  (DivU)
510  (Rem)
511  (RemU)
512
513  ;; RV64M Standard Extension (in addition to RV32M)
514  (Mulw)
515  (Divw)
516  (Divuw)
517  (Remw)
518  (Remuw)
519
520  ;; Zba: Address Generation Instructions
521  (Adduw)
522  (Sh1add)
523  (Sh1adduw)
524  (Sh2add)
525  (Sh2adduw)
526  (Sh3add)
527  (Sh3adduw)
528
529  ;; Zbb: Bit Manipulation Instructions
530  (Andn)
531  (Orn)
532  (Xnor)
533  (Max)
534  (Maxu)
535  (Min)
536  (Minu)
537  (Rol)
538  (Rolw)
539  (Ror)
540  (Rorw)
541
542  ;; Zbs: Single-bit instructions
543  (Bclr)
544  (Bext)
545  (Binv)
546  (Bset)
547
548  ;; Zbc: Carry-less multiplication
549  (Clmul)
550  (Clmulh)
551  (Clmulr)
552
553  ;; Zbkb: Bit-manipulation for Cryptography
554  (Pack)
555  (Packw)
556  (Packh)
557
558  ;; ZiCond: Integer Conditional Operations
559  (CzeroEqz)
560  (CzeroNez)
561))
562
563
564(type FpuOPRRR (enum
565  (Fadd)
566  (Fsub)
567  (Fmul)
568  (Fdiv)
569  (Fsgnj)
570  (Fsgnjn)
571  (Fsgnjx)
572  (Fmin)
573  (Fmax)
574  (Feq)
575  (Flt)
576  (Fle)
577
578  ;; Zfa Extension
579  (Fminm)
580  (Fmaxm)
581))
582
583
584
585(type AluOPRRI (enum
586  ;; Base ISA
587  (Addi)
588  (Slti)
589  (SltiU)
590  (Xori)
591  (Ori)
592  (Andi)
593  (Slli)
594  (Srli)
595  (Srai)
596  (Addiw)
597  (Slliw)
598  (SrliW)
599  (Sraiw)
600
601  ;; Zba: Address Generation Instructions
602  (SlliUw)
603
604  ;; Zbb: Bit Manipulation Instructions
605  (Clz)
606  (Clzw)
607  (Ctz)
608  (Ctzw)
609  (Cpop)
610  (Cpopw)
611  (Sextb)
612  (Sexth)
613  (Zexth)
614  (Rori)
615  (Roriw)
616  (Rev8)
617  (Brev8)
618  (Orcb)
619
620  ;; Zbs: Single-bit instructions
621  (Bclri)
622  (Bexti)
623  (Binvi)
624  (Bseti)
625))
626
627(type COpcodeSpace (enum
628  (C0)
629  (C1)
630  (C2)
631))
632
633;; Opcodes for the CR compressed instruction format
634(type CrOp (enum
635  (CMv)
636  (CAdd)
637  (CJr)
638  (CJalr)
639  ;; c.ebreak technically isn't a CR format instruction, but it's encoding
640  ;; lines up with this format.
641  (CEbreak)
642))
643
644;; Opcodes for the CA compressed instruction format
645(type CaOp (enum
646  (CAnd)
647  (COr)
648  (CXor)
649  (CSub)
650  (CAddw)
651  (CSubw)
652  (CMul)
653))
654
655;; Opcodes for the CJ compressed instruction format
656(type CjOp (enum
657  (CJ)
658))
659
660;; Opcodes for the CI compressed instruction format
661(type CiOp (enum
662  (CAddi)
663  (CAddiw)
664  (CAddi16sp)
665  (CSlli)
666  (CLi)
667  (CLui)
668  (CLwsp)
669  (CLdsp)
670  (CFldsp)
671))
672
673;; Opcodes for the CIW compressed instruction format
674(type CiwOp (enum
675  (CAddi4spn)
676))
677
678;; Opcodes for the CB compressed instruction format
679(type CbOp (enum
680  (CSrli)
681  (CSrai)
682  (CAndi)
683))
684
685;; Opcodes for the CSS compressed instruction format
686(type CssOp (enum
687  (CSwsp)
688  (CSdsp)
689  (CFsdsp)
690))
691
692;; Opcodes for the CS compressed instruction format
693(type CsOp (enum
694  (CSw)
695  (CSd)
696  (CFsd)
697))
698
699;; Opcodes for the CL compressed instruction format
700(type ClOp (enum
701  (CLw)
702  (CLd)
703  (CFld)
704))
705
706;; Opcodes for the CSZN compressed instruction format
707(type CsznOp (enum
708  (CNot)
709  (CZextb)
710  (CZexth)
711  (CZextw)
712  (CSextb)
713  (CSexth)
714))
715
716;; This is a mix of all Zcb memory addressing instructions
717;;
718;; Technically they are split across 4 different formats.
719;; But they are all very similar, so we just group them all together.
720(type ZcbMemOp (enum
721  (CLbu)
722  (CLhu)
723  (CLh)
724  (CSb)
725  (CSh)
726))
727
728
729(type CsrRegOP (enum
730  ;; Atomic Read/Write CSR
731  (CsrRW)
732  ;; Atomic Read and Set Bits in CSR
733  (CsrRS)
734  ;; Atomic Read and Clear Bits in CSR
735  (CsrRC)
736))
737
738(type CsrImmOP (enum
739  ;; Atomic Read/Write CSR (Immediate Source)
740  (CsrRWI)
741  ;; Atomic Read and Set Bits in CSR (Immediate Source)
742  (CsrRSI)
743  ;; Atomic Read and Clear Bits in CSR (Immediate Source)
744  (CsrRCI)
745))
746
747;; Enum of the known CSR registers
748(type CSR (enum
749  ;; Floating-Point Dynamic Rounding Mode
750  (Frm)
751))
752
753
754(type FRM (enum
755  ;; Round to Nearest, ties to Even
756  (RNE)
757  ;; Round towards Zero
758  (RTZ)
759  ;;  Round Down (towards −∞)
760  (RDN)
761  ;; Round Up (towards +∞)
762  (RUP)
763  ;; Round to Nearest, ties to Max Magnitude
764  (RMM)
765  ;; In instruction’s rm field, selects dynamic rounding mode;
766  ;;In Rounding Mode register, Invalid.
767  (Fcsr)
768))
769
770(decl pure frm_bits (FRM) UImm5)
771(extern constructor frm_bits frm_bits)
772(convert FRM UImm5 frm_bits)
773
774(type FFlagsException (enum
775  ;; Invalid Operation
776  (NV)
777  ;; Divide by Zero
778  (DZ)
779  ;; Overflow
780  (OF)
781  ;; Underflow
782  (UF)
783  ;; Inexact
784  (NX)
785))
786
787;;;; input output read write
788;;;; SI SO SR SW
789;;;; PI PO PR PW
790;;;; lowest four bit are used.
791(type FenceReq (primitive u8))
792
793(type BoxCallInfo (primitive BoxCallInfo))
794(type BoxCallIndInfo (primitive BoxCallIndInfo))
795(type BoxReturnCallInfo (primitive BoxReturnCallInfo))
796(type BoxReturnCallIndInfo (primitive BoxReturnCallIndInfo))
797(type IntegerCompare (primitive IntegerCompare))
798(type AMode (primitive AMode))
799(type OptionReg (primitive OptionReg))
800(type OptionImm12 (primitive OptionImm12))
801(type OptionUimm5 (primitive OptionUimm5))
802(type Imm12 (primitive Imm12))
803(type UImm5 (primitive UImm5))
804(type Imm5 (primitive Imm5))
805(type Imm20 (primitive Imm20))
806(type Imm3 (primitive Imm3))
807(type CondBrTarget (primitive CondBrTarget))
808(type VecU8 (primitive VecU8))
809(type AMO (primitive AMO))
810(type VecMachLabel extern (enum))
811
812
813;;;; Newtypes for Different Register Classes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
814
815(type XReg (primitive XReg))
816(type WritableXReg (primitive WritableXReg))
817(type FReg (primitive FReg))
818(type WritableFReg (primitive WritableFReg))
819(type VReg (primitive VReg))
820(type WritableVReg (primitive WritableVReg))
821
822;; Construct a new `XReg` from a `Reg`.
823;;
824;; Asserts that the register has a Integer RegClass.
825(decl xreg_new (Reg) XReg)
826(extern constructor xreg_new xreg_new)
827(convert Reg XReg xreg_new)
828
829;; Construct a new `WritableXReg` from a `WritableReg`.
830;;
831;; Asserts that the register has a Integer RegClass.
832(decl writable_xreg_new (WritableReg) WritableXReg)
833(extern constructor writable_xreg_new writable_xreg_new)
834(convert WritableReg WritableXReg writable_xreg_new)
835
836;; Put a value into a XReg.
837;;
838;; Asserts that the value goes into a XReg.
839(decl put_in_xreg (Value) XReg)
840(rule (put_in_xreg val) (xreg_new (put_in_reg val)))
841(convert Value XReg put_in_xreg)
842
843;; Construct an `InstOutput` out of a single XReg register.
844(decl output_xreg (XReg) InstOutput)
845(rule (output_xreg x) (output_reg x))
846(convert XReg InstOutput output_xreg)
847
848;; Convert a `WritableXReg` to an `XReg`.
849(decl pure writable_xreg_to_xreg (WritableXReg) XReg)
850(extern constructor writable_xreg_to_xreg writable_xreg_to_xreg)
851(convert WritableXReg XReg writable_xreg_to_xreg)
852
853;; Convert a `WritableXReg` to an `WritableReg`.
854(decl pure writable_xreg_to_writable_reg (WritableXReg) WritableReg)
855(extern constructor writable_xreg_to_writable_reg writable_xreg_to_writable_reg)
856(convert WritableXReg WritableReg writable_xreg_to_writable_reg)
857
858;; Convert a `WritableXReg` to an `Reg`.
859(decl pure writable_xreg_to_reg (WritableXReg) Reg)
860(rule (writable_xreg_to_reg x) (writable_xreg_to_writable_reg x))
861(convert WritableXReg Reg writable_xreg_to_reg)
862
863;; Convert an `XReg` to a `Reg`.
864(decl pure xreg_to_reg (XReg) Reg)
865(extern constructor xreg_to_reg xreg_to_reg)
866(convert XReg Reg xreg_to_reg)
867
868;; Convert a `XReg` to a `ValueRegs`.
869(decl xreg_to_value_regs (XReg) ValueRegs)
870(rule (xreg_to_value_regs x) (value_reg x))
871(convert XReg ValueRegs xreg_to_reg)
872
873;; Convert a `WritableXReg` to a `ValueRegs`.
874(decl writable_xreg_to_value_regs (WritableXReg) ValueRegs)
875(rule (writable_xreg_to_value_regs x) (value_reg x))
876(convert WritableXReg ValueRegs writable_xreg_to_value_regs)
877
878;; Allocates a new `WritableXReg`.
879(decl temp_writable_xreg () WritableXReg)
880(rule (temp_writable_xreg) (temp_writable_reg $I64))
881
882
883;; Construct a new `FReg` from a `Reg`.
884;;
885;; Asserts that the register has a Float RegClass.
886(decl freg_new (Reg) FReg)
887(extern constructor freg_new freg_new)
888(convert Reg FReg freg_new)
889
890;; Construct a new `WritableFReg` from a `WritableReg`.
891;;
892;; Asserts that the register has a Float RegClass.
893(decl writable_freg_new (WritableReg) WritableFReg)
894(extern constructor writable_freg_new writable_freg_new)
895(convert WritableReg WritableFReg writable_freg_new)
896
897;; Put a value into a FReg.
898;;
899;; Asserts that the value goes into a FReg.
900(decl put_in_freg (Value) FReg)
901(rule (put_in_freg val) (freg_new (put_in_reg val)))
902(convert Value FReg put_in_freg)
903
904;; Construct an `InstOutput` out of a single FReg register.
905(decl output_freg (FReg) InstOutput)
906(rule (output_freg x) (output_reg x))
907(convert FReg InstOutput output_freg)
908
909;; Convert a `WritableFReg` to an `FReg`.
910(decl pure writable_freg_to_freg (WritableFReg) FReg)
911(extern constructor writable_freg_to_freg writable_freg_to_freg)
912(convert WritableFReg FReg writable_freg_to_freg)
913
914;; Convert a `WritableFReg` to an `WritableReg`.
915(decl pure writable_freg_to_writable_reg (WritableFReg) WritableReg)
916(extern constructor writable_freg_to_writable_reg writable_freg_to_writable_reg)
917(convert WritableFReg WritableReg writable_freg_to_writable_reg)
918
919;; Convert a `WritableFReg` to an `Reg`.
920(decl pure writable_freg_to_reg (WritableFReg) Reg)
921(rule (writable_freg_to_reg x) (writable_freg_to_writable_reg x))
922(convert WritableFReg Reg writable_freg_to_reg)
923
924;; Convert an `FReg` to a `Reg`.
925(decl pure freg_to_reg (FReg) Reg)
926(extern constructor freg_to_reg freg_to_reg)
927(convert FReg Reg freg_to_reg)
928
929;; Convert a `FReg` to a `ValueRegs`.
930(decl freg_to_value_regs (FReg) ValueRegs)
931(rule (freg_to_value_regs x) (value_reg x))
932(convert FReg ValueRegs xreg_to_reg)
933
934;; Convert a `WritableFReg` to a `ValueRegs`.
935(decl writable_freg_to_value_regs (WritableFReg) ValueRegs)
936(rule (writable_freg_to_value_regs x) (value_reg x))
937(convert WritableFReg ValueRegs writable_freg_to_value_regs)
938
939;; Allocates a new `WritableFReg`.
940(decl temp_writable_freg () WritableFReg)
941(rule (temp_writable_freg) (temp_writable_reg $F64))
942
943
944
945;; Construct a new `VReg` from a `Reg`.
946;;
947;; Asserts that the register has a Vector RegClass.
948(decl vreg_new (Reg) VReg)
949(extern constructor vreg_new vreg_new)
950(convert Reg VReg vreg_new)
951
952;; Construct a new `WritableVReg` from a `WritableReg`.
953;;
954;; Asserts that the register has a Vector RegClass.
955(decl writable_vreg_new (WritableReg) WritableVReg)
956(extern constructor writable_vreg_new writable_vreg_new)
957(convert WritableReg WritableVReg writable_vreg_new)
958
959;; Put a value into a VReg.
960;;
961;; Asserts that the value goes into a VReg.
962(decl put_in_vreg (Value) VReg)
963(rule (put_in_vreg val) (vreg_new (put_in_reg val)))
964(convert Value VReg put_in_vreg)
965
966;; Construct an `InstOutput` out of a single VReg register.
967(decl output_vreg (VReg) InstOutput)
968(rule (output_vreg x) (output_reg x))
969(convert VReg InstOutput output_vreg)
970
971;; Convert a `WritableVReg` to an `VReg`.
972(decl pure writable_vreg_to_vreg (WritableVReg) VReg)
973(extern constructor writable_vreg_to_vreg writable_vreg_to_vreg)
974(convert WritableVReg VReg writable_vreg_to_vreg)
975
976;; Convert a `WritableVReg` to an `WritableReg`.
977(decl pure writable_vreg_to_writable_reg (WritableVReg) WritableReg)
978(extern constructor writable_vreg_to_writable_reg writable_vreg_to_writable_reg)
979(convert WritableVReg WritableReg writable_vreg_to_writable_reg)
980
981;; Convert a `WritableVReg` to an `Reg`.
982(decl pure writable_vreg_to_reg (WritableVReg) Reg)
983(rule (writable_vreg_to_reg x) (writable_vreg_to_writable_reg x))
984(convert WritableVReg Reg writable_vreg_to_reg)
985
986;; Convert an `VReg` to a `Reg`.
987(decl pure vreg_to_reg (VReg) Reg)
988(extern constructor vreg_to_reg vreg_to_reg)
989(convert VReg Reg vreg_to_reg)
990
991;; Convert a `VReg` to a `ValueRegs`.
992(decl vreg_to_value_regs (VReg) ValueRegs)
993(rule (vreg_to_value_regs x) (value_reg x))
994(convert VReg ValueRegs xreg_to_reg)
995
996;; Convert a `WritableVReg` to a `ValueRegs`.
997(decl writable_vreg_to_value_regs (WritableVReg) ValueRegs)
998(rule (writable_vreg_to_value_regs x) (value_reg x))
999(convert WritableVReg ValueRegs writable_vreg_to_value_regs)
1000
1001;; Allocates a new `WritableVReg`.
1002(decl temp_writable_vreg () WritableVReg)
1003(rule (temp_writable_vreg) (temp_writable_reg $I8X16))
1004
1005
1006;; ISA Extension helpers
1007
1008(decl pure has_m () bool)
1009(extern constructor has_m has_m)
1010
1011(decl pure has_v () bool)
1012(extern constructor has_v has_v)
1013
1014(decl pure has_zfa () bool)
1015(extern constructor has_zfa has_zfa)
1016
1017(decl pure has_zfhmin () bool)
1018(extern constructor has_zfhmin has_zfhmin)
1019
1020(decl pure has_zfh () bool)
1021(extern constructor has_zfh has_zfh)
1022
1023(decl pure has_zvfh () bool)
1024(extern constructor has_zvfh has_zvfh)
1025
1026(decl pure has_zbkb () bool)
1027(extern constructor has_zbkb has_zbkb)
1028
1029(decl pure has_zba () bool)
1030(extern constructor has_zba has_zba)
1031
1032(decl pure has_zbb () bool)
1033(extern constructor has_zbb has_zbb)
1034
1035(decl pure has_zbc () bool)
1036(extern constructor has_zbc has_zbc)
1037
1038(decl pure has_zbs () bool)
1039(extern constructor has_zbs has_zbs)
1040
1041(decl pure has_zicond () bool)
1042(extern constructor has_zicond has_zicond)
1043
1044
1045;;;; Type Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1046
1047;; Helper that matches any supported type. This extractor checks the ISA flags
1048;; to determine if the type is supported.
1049(decl ty_supported (Type) Type)
1050(extern extractor ty_supported ty_supported)
1051
1052;; Helper that matches any scalar floating point type
1053
1054;; Floating point registers are large enough to hold the type
1055(decl ty_supported_float_size (Type) Type)
1056(extern extractor ty_supported_float_size ty_supported_float_size)
1057
1058;; At least basic floating point instructions like load/store are supported (e.g. Zfhmin)
1059(decl ty_supported_float_min (Type) Type)
1060(extern extractor ty_supported_float_min ty_supported_float_min)
1061
1062;; All floating point instructions are supported (e.g. Zfh)
1063(decl ty_supported_float_full (Type) Type)
1064(extern extractor ty_supported_float_full ty_supported_float_full)
1065
1066;; Helper that matches any supported vector type
1067(decl ty_supported_vec (Type) Type)
1068(extern extractor ty_supported_vec ty_supported_vec)
1069
1070;; Helper that matches types which are stored in a pair of integer registers (I128 and F128)
1071(decl ty_reg_pair (Type) Type)
1072(extern extractor ty_reg_pair ty_reg_pair)
1073
1074
1075;;;; Instruction Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1076
1077;; RV32I Base Integer Instruction Set
1078
1079;; Helper for emitting the `add` instruction.
1080;; rd ← rs1 + rs2
1081(decl rv_add (XReg XReg) XReg)
1082(rule (rv_add rs1 rs2)
1083  (alu_rrr (AluOPRRR.Add) rs1 rs2))
1084
1085;; Helper for emitting the `addi` ("Add Immediate") instruction.
1086;; rd ← rs1 + sext(imm)
1087(decl rv_addi (XReg Imm12) XReg)
1088(rule (rv_addi rs1 imm)
1089  (alu_rr_imm12 (AluOPRRI.Addi) rs1 imm))
1090
1091;; Helper for emitting the `sub` instruction.
1092;; rd ← rs1 - rs2
1093(decl rv_sub (XReg XReg) XReg)
1094(rule (rv_sub rs1 rs2)
1095  (alu_rrr (AluOPRRR.Sub) rs1 rs2))
1096
1097;; Helper for emitting the `neg` instruction.
1098;; This instruction is a mnemonic for `sub rd, zero, rs1`.
1099(decl rv_neg (XReg) XReg)
1100(rule (rv_neg rs1)
1101  (alu_rrr (AluOPRRR.Sub) (zero_reg) rs1))
1102
1103;; Helper for emitting the `sll` ("Shift Left Logical") instruction.
1104;; rd ← rs1 << rs2
1105(decl rv_sll (XReg XReg) XReg)
1106(rule (rv_sll rs1 rs2)
1107  (alu_rrr (AluOPRRR.Sll) rs1 rs2))
1108
1109;; Helper for emitting the `slli` ("Shift Left Logical Immediate") instruction.
1110;; rd ← rs1 << uext(imm)
1111(decl rv_slli (XReg Imm12) XReg)
1112(rule (rv_slli rs1 imm)
1113  (alu_rr_imm12 (AluOPRRI.Slli) rs1 imm))
1114
1115;; Helper for emitting the `srl` ("Shift Right Logical") instruction.
1116;; rd ← rs1 >> rs2
1117(decl rv_srl (XReg XReg) XReg)
1118(rule (rv_srl rs1 rs2)
1119  (alu_rrr (AluOPRRR.Srl) rs1 rs2))
1120
1121;; Helper for emitting the `srli` ("Shift Right Logical Immediate") instruction.
1122;; rd ← rs1 >> uext(imm)
1123(decl rv_srli (XReg Imm12) XReg)
1124(rule (rv_srli rs1 imm)
1125  (alu_rr_imm12 (AluOPRRI.Srli) rs1 imm))
1126
1127;; Helper for emitting the `sra` ("Shift Right Arithmetic") instruction.
1128;; rd ← rs1 >> rs2
1129(decl rv_sra (XReg XReg) XReg)
1130(rule (rv_sra rs1 rs2)
1131  (alu_rrr (AluOPRRR.Sra) rs1 rs2))
1132
1133;; Helper for emitting the `srai` ("Shift Right Arithmetic Immediate") instruction.
1134;; rd ← rs1 >> uext(imm)
1135(decl rv_srai (XReg Imm12) XReg)
1136(rule (rv_srai rs1 imm)
1137  (alu_rr_imm12 (AluOPRRI.Srai) rs1 imm))
1138
1139;; Helper for emitting the `or` instruction.
1140;; rd ← rs1 ∨ rs2
1141(decl rv_or (XReg XReg) XReg)
1142(rule (rv_or rs1 rs2)
1143  (alu_rrr (AluOPRRR.Or) rs1 rs2))
1144
1145;; Helper for emitting the `ori` ("Or Immediate") instruction.
1146;; rd ← rs1 ∨ uext(imm)
1147(decl rv_ori (XReg Imm12) XReg)
1148(rule (rv_ori rs1 imm)
1149  (alu_rr_imm12 (AluOPRRI.Ori) rs1 imm))
1150
1151;; Helper for emitting the `xor` instruction.
1152;; rd ← rs1 ⊕ rs2
1153(decl rv_xor (XReg XReg) XReg)
1154(rule (rv_xor rs1 rs2)
1155  (alu_rrr (AluOPRRR.Xor) rs1 rs2))
1156
1157;; Helper for emitting the `xori` ("Exclusive Or Immediate") instruction.
1158;; rd ← rs1 ⊕ uext(imm)
1159(decl rv_xori (XReg Imm12) XReg)
1160(rule (rv_xori rs1 imm)
1161  (alu_rr_imm12 (AluOPRRI.Xori) rs1 imm))
1162
1163;; Helper for emitting the `not` instruction.
1164;; This instruction is a mnemonic for `xori rd, rs1, -1`.
1165(decl rv_not (XReg) XReg)
1166(rule (rv_not rs1)
1167  (rv_xori rs1 (imm12_const -1)))
1168
1169;; Helper for emitting the `and` instruction.
1170;; rd ← rs1 ∧ rs2
1171(decl rv_and (XReg XReg) XReg)
1172(rule (rv_and rs1 rs2)
1173  (alu_rrr (AluOPRRR.And) rs1 rs2))
1174
1175;; Helper for emitting the `andi` ("And Immediate") instruction.
1176;; rd ← rs1 ∧ uext(imm)
1177(decl rv_andi (XReg Imm12) XReg)
1178(rule (rv_andi rs1 imm)
1179  (alu_rr_imm12 (AluOPRRI.Andi) rs1 imm))
1180
1181;; Helper for emitting the `slt` ("Set Less Than") instruction.
1182;; rd ← rs1 < rs2
1183(decl rv_slt (XReg XReg) XReg)
1184(rule (rv_slt rs1 rs2)
1185  (alu_rrr (AluOPRRR.Slt) rs1 rs2))
1186
1187;; Helper for emitting the `sltu` ("Set Less Than Unsigned") instruction.
1188;; rd ← rs1 < rs2
1189(decl rv_sltu (XReg XReg) XReg)
1190(rule (rv_sltu rs1 rs2)
1191  (alu_rrr (AluOPRRR.SltU) rs1 rs2))
1192
1193;; Helper for emitting the `snez` instruction.
1194;; This instruction is a mnemonic for `sltu rd, zero, rs`.
1195(decl rv_snez (XReg) XReg)
1196(rule (rv_snez rs1)
1197  (rv_sltu (zero_reg) rs1))
1198
1199;; Helper for emitting the `slti` ("Set Less Than Immediate") instruction.
1200;; rd ← rs1 < imm
1201(decl rv_slti (XReg Imm12) XReg)
1202(rule (rv_slti rs1 imm)
1203  (alu_rr_imm12 (AluOPRRI.Slti) rs1 imm))
1204
1205;; Helper for emitting the `sltiu` ("Set Less Than Immediate Unsigned") instruction.
1206;; rd ← rs1 < imm
1207(decl rv_sltiu (XReg Imm12) XReg)
1208(rule (rv_sltiu rs1 imm)
1209  (alu_rr_imm12 (AluOPRRI.SltiU) rs1 imm))
1210
1211;; Helper for emitting the `seqz` instruction.
1212;; This instruction is a mnemonic for `sltiu rd, rs, 1`.
1213(decl rv_seqz (XReg) XReg)
1214(rule (rv_seqz rs1)
1215  (rv_sltiu rs1 (imm12_const 1)))
1216
1217
1218;; RV64I Base Integer Instruction Set
1219;; Unlike RV32I instructions these are only present in the 64bit ISA
1220
1221;; Helper for emitting the `addw` ("Add Word") instruction.
1222;; rd ← sext32(rs1) + sext32(rs2)
1223(decl rv_addw (XReg XReg) XReg)
1224(rule (rv_addw rs1 rs2)
1225  (alu_rrr (AluOPRRR.Addw) rs1 rs2))
1226
1227;; Helper for emitting the `addiw` ("Add Word Immediate") instruction.
1228;; rd ← sext32(rs1) + imm
1229(decl rv_addiw (XReg Imm12) XReg)
1230(rule (rv_addiw rs1 imm)
1231  (alu_rr_imm12 (AluOPRRI.Addiw) rs1 imm))
1232
1233;; Helper for emitting the `sext.w` ("Sign Extend Word") instruction.
1234;; This instruction is a mnemonic for `addiw rd, rs, zero`.
1235(decl rv_sextw (XReg) XReg)
1236(rule (rv_sextw rs1)
1237  (rv_addiw rs1 (imm12_const 0)))
1238
1239;; Helper for emitting the `subw` ("Subtract Word") instruction.
1240;; rd ← sext32(rs1) - sext32(rs2)
1241(decl rv_subw (XReg XReg) XReg)
1242(rule (rv_subw rs1 rs2)
1243  (alu_rrr (AluOPRRR.Subw) rs1 rs2))
1244
1245;; Helper for emitting the `sllw` ("Shift Left Logical Word") instruction.
1246;; rd ← sext32(uext32(rs1) << rs2)
1247(decl rv_sllw (XReg XReg) XReg)
1248(rule (rv_sllw rs1 rs2)
1249  (alu_rrr (AluOPRRR.Sllw) rs1 rs2))
1250
1251;; Helper for emitting the `slliw` ("Shift Left Logical Immediate Word") instruction.
1252;; rd ← sext32(uext32(rs1) << imm)
1253(decl rv_slliw (XReg Imm12) XReg)
1254(rule (rv_slliw rs1 imm)
1255  (alu_rr_imm12 (AluOPRRI.Slliw) rs1 imm))
1256
1257;; Helper for emitting the `srlw` ("Shift Right Logical Word") instruction.
1258;; rd ← sext32(uext32(rs1) >> rs2)
1259(decl rv_srlw (XReg XReg) XReg)
1260(rule (rv_srlw rs1 rs2)
1261  (alu_rrr (AluOPRRR.Srlw) rs1 rs2))
1262
1263;; Helper for emitting the `srliw` ("Shift Right Logical Immediate Word") instruction.
1264;; rd ← sext32(uext32(rs1) >> imm)
1265(decl rv_srliw (XReg Imm12) XReg)
1266(rule (rv_srliw rs1 imm)
1267  (alu_rr_imm12 (AluOPRRI.SrliW) rs1 imm))
1268
1269;; Helper for emitting the `sraw` ("Shift Right Arithmetic Word") instruction.
1270;; rd ← sext32(rs1 >> rs2)
1271(decl rv_sraw (XReg XReg) XReg)
1272(rule (rv_sraw rs1 rs2)
1273  (alu_rrr (AluOPRRR.Sraw) rs1 rs2))
1274
1275;; Helper for emitting the `sraiw` ("Shift Right Arithmetic Immediate Word") instruction.
1276;; rd ← sext32(rs1 >> imm)
1277(decl rv_sraiw (XReg Imm12) XReg)
1278(rule (rv_sraiw rs1 imm)
1279  (alu_rr_imm12 (AluOPRRI.Sraiw) rs1 imm))
1280
1281
1282;; RV32M Extension
1283;; TODO: Enable these instructions only when we have the M extension
1284
1285;; Helper for emitting the `mul` instruction.
1286;; rd ← rs1 × rs2
1287(decl rv_mul (XReg XReg) XReg)
1288(rule (rv_mul rs1 rs2)
1289  (alu_rrr (AluOPRRR.Mul) rs1 rs2))
1290
1291;; Helper for emitting the `mulh` ("Multiply High Signed Signed") instruction.
1292;; rd ← (sext(rs1) × sext(rs2)) » xlen
1293(decl rv_mulh (XReg XReg) XReg)
1294(rule (rv_mulh rs1 rs2)
1295  (alu_rrr (AluOPRRR.Mulh) rs1 rs2))
1296
1297;; Helper for emitting the `mulhu` ("Multiply High Unsigned Unsigned") instruction.
1298;; rd ← (uext(rs1) × uext(rs2)) » xlen
1299(decl rv_mulhu (XReg XReg) XReg)
1300(rule (rv_mulhu rs1 rs2)
1301  (alu_rrr (AluOPRRR.Mulhu) rs1 rs2))
1302
1303;; Helper for emitting the `div` instruction.
1304;; rd ← rs1 ÷ rs2
1305(decl rv_div (XReg XReg) XReg)
1306(rule (rv_div rs1 rs2)
1307  (alu_rrr (AluOPRRR.Div) rs1 rs2))
1308
1309;; Helper for emitting the `divu` ("Divide Unsigned") instruction.
1310;; rd ← rs1 ÷ rs2
1311(decl rv_divu (XReg XReg) XReg)
1312(rule (rv_divu rs1 rs2)
1313  (alu_rrr (AluOPRRR.DivU) rs1 rs2))
1314
1315;; Helper for emitting the `rem` instruction.
1316;; rd ← rs1 mod rs2
1317(decl rv_rem (XReg XReg) XReg)
1318(rule (rv_rem rs1 rs2)
1319  (alu_rrr (AluOPRRR.Rem) rs1 rs2))
1320
1321;; Helper for emitting the `remu` ("Remainder Unsigned") instruction.
1322;; rd ← rs1 mod rs2
1323(decl rv_remu (XReg XReg) XReg)
1324(rule (rv_remu rs1 rs2)
1325  (alu_rrr (AluOPRRR.RemU) rs1 rs2))
1326
1327;; RV64M Extension
1328;; TODO: Enable these instructions only when we have the M extension
1329
1330;; Helper for emitting the `mulw` ("Multiply Word") instruction.
1331;; rd ← uext32(rs1) × uext32(rs2)
1332(decl rv_mulw (XReg XReg) XReg)
1333(rule (rv_mulw rs1 rs2)
1334  (alu_rrr (AluOPRRR.Mulw) rs1 rs2))
1335
1336;; Helper for emitting the `divw` ("Divide Word") instruction.
1337;; rd ← sext32(rs1) ÷ sext32(rs2)
1338(decl rv_divw (XReg XReg) XReg)
1339(rule (rv_divw rs1 rs2)
1340  (alu_rrr (AluOPRRR.Divw) rs1 rs2))
1341
1342;; Helper for emitting the `divuw` ("Divide Unsigned Word") instruction.
1343;; rd ← uext32(rs1) ÷ uext32(rs2)
1344(decl rv_divuw (XReg XReg) XReg)
1345(rule (rv_divuw rs1 rs2)
1346  (alu_rrr (AluOPRRR.Divuw) rs1 rs2))
1347
1348;; Helper for emitting the `remw` ("Remainder Word") instruction.
1349;; rd ← sext32(rs1) mod sext32(rs2)
1350(decl rv_remw (XReg XReg) XReg)
1351(rule (rv_remw rs1 rs2)
1352  (alu_rrr (AluOPRRR.Remw) rs1 rs2))
1353
1354;; Helper for emitting the `remuw` ("Remainder Unsigned Word") instruction.
1355;; rd ← uext32(rs1) mod uext32(rs2)
1356(decl rv_remuw (XReg XReg) XReg)
1357(rule (rv_remuw rs1 rs2)
1358  (alu_rrr (AluOPRRR.Remuw) rs1 rs2))
1359
1360
1361;; F and D Extensions
1362;; TODO: Enable these instructions only when we have the F or D extensions
1363
1364;; Helper for emitting the `fadd` instruction.
1365(decl rv_fadd (Type FRM FReg FReg) FReg)
1366(rule (rv_fadd ty frm rs1 rs2) (fpu_rrr (FpuOPRRR.Fadd) ty frm rs1 rs2))
1367
1368;; Helper for emitting the `fsub` instruction.
1369(decl rv_fsub (Type FRM FReg FReg) FReg)
1370(rule (rv_fsub ty frm rs1 rs2) (fpu_rrr (FpuOPRRR.Fsub) ty frm rs1 rs2))
1371
1372;; Helper for emitting the `fmul` instruction.
1373(decl rv_fmul (Type FRM FReg FReg) FReg)
1374(rule (rv_fmul ty frm rs1 rs2) (fpu_rrr (FpuOPRRR.Fmul) ty frm rs1 rs2))
1375
1376;; Helper for emitting the `fdiv` instruction.
1377(decl rv_fdiv (Type FRM FReg FReg) FReg)
1378(rule (rv_fdiv ty frm rs1 rs2) (fpu_rrr (FpuOPRRR.Fdiv) ty frm rs1 rs2))
1379
1380;; Helper for emitting the `fsqrt` instruction.
1381(decl rv_fsqrt (Type FRM FReg) FReg)
1382(rule (rv_fsqrt ty frm rs1) (fpu_rr (FpuOPRR.Fsqrt) ty frm rs1))
1383
1384;; Helper for emitting the `fmadd` instruction.
1385(decl rv_fmadd (Type FRM FReg FReg FReg) FReg)
1386(rule (rv_fmadd ty frm rs1 rs2 rs3) (fpu_rrrr (FpuOPRRRR.Fmadd) ty frm rs1 rs2 rs3))
1387
1388;; Helper for emitting the `fmsub` instruction.
1389(decl rv_fmsub (Type FRM FReg FReg FReg) FReg)
1390(rule (rv_fmsub ty frm rs1 rs2 rs3) (fpu_rrrr (FpuOPRRRR.Fmsub) ty frm rs1 rs2 rs3))
1391
1392;; Helper for emitting the `fnmadd` instruction.
1393(decl rv_fnmadd (Type FRM FReg FReg FReg) FReg)
1394(rule (rv_fnmadd ty frm rs1 rs2 rs3) (fpu_rrrr (FpuOPRRRR.Fnmadd) ty frm rs1 rs2 rs3))
1395
1396;; Helper for emitting the `fnmsub` instruction.
1397(decl rv_fnmsub (Type FRM FReg FReg FReg) FReg)
1398(rule (rv_fnmsub ty frm rs1 rs2 rs3) (fpu_rrrr (FpuOPRRRR.Fnmsub) ty frm rs1 rs2 rs3))
1399
1400;; Helper for emitting the `fmv.x.h` instruction.
1401(decl rv_fmvxh (FReg) XReg)
1402(rule (rv_fmvxh r) (fpu_rr_int (FpuOPRR.FmvXFmt) $F16 (FRM.RNE) r))
1403
1404;; Helper for emitting the `fmv.x.w` instruction.
1405(decl rv_fmvxw (FReg) XReg)
1406(rule (rv_fmvxw r) (fpu_rr_int (FpuOPRR.FmvXFmt) $F32 (FRM.RNE) r))
1407
1408;; Helper for emitting the `fmv.x.d` instruction.
1409(decl rv_fmvxd (FReg) XReg)
1410(rule (rv_fmvxd r) (fpu_rr_int (FpuOPRR.FmvXFmt) $F64 (FRM.RNE) r))
1411
1412;; Helper for emitting the `fmv.h.x` instruction.
1413(decl rv_fmvhx (XReg) FReg)
1414(rule (rv_fmvhx r) (fpu_rr (FpuOPRR.FmvFmtX) $F16 (FRM.RNE) r))
1415
1416;; Helper for emitting the `fmv.w.x` instruction.
1417(decl rv_fmvwx (XReg) FReg)
1418(rule (rv_fmvwx r) (fpu_rr (FpuOPRR.FmvFmtX) $F32 (FRM.RNE) r))
1419
1420;; Helper for emitting the `fmv.d.x` instruction.
1421(decl rv_fmvdx (XReg) FReg)
1422(rule (rv_fmvdx r) (fpu_rr (FpuOPRR.FmvFmtX) $F64 (FRM.RNE) r))
1423
1424;; Helper for emitting the `fcvt.d.s` ("Float Convert Double to Single") instruction.
1425(decl rv_fcvtds (FReg) FReg)
1426(rule (rv_fcvtds rs1) (fpu_rr (FpuOPRR.FcvtDS) $F64 (FRM.RNE) rs1))
1427
1428;; Helper for emitting the `fcvt.s.d` ("Float Convert Single to Double") instruction.
1429(decl rv_fcvtsd (FRM FReg) FReg)
1430(rule (rv_fcvtsd frm rs1) (fpu_rr (FpuOPRR.FcvtSD) $F32 frm rs1))
1431
1432;; Helper for emitting the `fcvt.s.w` instruction.
1433(decl rv_fcvtsw (FRM XReg) FReg)
1434(rule (rv_fcvtsw frm rs1) (fpu_rr (FpuOPRR.FcvtFmtW) $F32 frm rs1))
1435
1436;; Helper for emitting the `fcvt.s.wu` instruction.
1437(decl rv_fcvtswu (FRM XReg) FReg)
1438(rule (rv_fcvtswu frm rs1) (fpu_rr (FpuOPRR.FcvtFmtWu) $F32 frm rs1))
1439
1440;; Helper for emitting the `fcvt.d.w` instruction.
1441(decl rv_fcvtdw (XReg) FReg)
1442(rule (rv_fcvtdw rs1) (fpu_rr (FpuOPRR.FcvtFmtW) $F64 (FRM.RNE) rs1))
1443
1444;; Helper for emitting the `fcvt.d.wu` instruction.
1445(decl rv_fcvtdwu (XReg) FReg)
1446(rule (rv_fcvtdwu rs1) (fpu_rr (FpuOPRR.FcvtFmtWu) $F64 (FRM.RNE) rs1))
1447
1448;; Helper for emitting the `fcvt.s.l` instruction.
1449(decl rv_fcvtsl (FRM XReg) FReg)
1450(rule (rv_fcvtsl frm rs1) (fpu_rr (FpuOPRR.FcvtFmtL) $F32 frm rs1))
1451
1452;; Helper for emitting the `fcvt.s.lu` instruction.
1453(decl rv_fcvtslu (FRM XReg) FReg)
1454(rule (rv_fcvtslu frm rs1) (fpu_rr (FpuOPRR.FcvtFmtLu) $F32 frm rs1))
1455
1456;; Helper for emitting the `fcvt.d.l` instruction.
1457(decl rv_fcvtdl (FRM XReg) FReg)
1458(rule (rv_fcvtdl frm rs1) (fpu_rr (FpuOPRR.FcvtFmtL) $F64 frm rs1))
1459
1460;; Helper for emitting the `fcvt.d.lu` instruction.
1461(decl rv_fcvtdlu (FRM XReg) FReg)
1462(rule (rv_fcvtdlu frm rs1) (fpu_rr (FpuOPRR.FcvtFmtLu) $F64 frm rs1))
1463
1464;; Helper for emitting the `fcvt.w.s` instruction.
1465(decl rv_fcvtws (FRM FReg) XReg)
1466(rule (rv_fcvtws frm rs1) (fpu_rr_int (FpuOPRR.FcvtWFmt) $F32 frm rs1))
1467
1468;; Helper for emitting the `fcvt.l.s` instruction.
1469(decl rv_fcvtls (FRM FReg) XReg)
1470(rule (rv_fcvtls frm rs1) (fpu_rr_int (FpuOPRR.FcvtLFmt) $F32 frm rs1))
1471
1472;; Helper for emitting the `fcvt.wu.s` instruction.
1473(decl rv_fcvtwus (FRM FReg) XReg)
1474(rule (rv_fcvtwus frm rs1) (fpu_rr_int (FpuOPRR.FcvtWuFmt) $F32 frm rs1))
1475
1476;; Helper for emitting the `fcvt.lu.s` instruction.
1477(decl rv_fcvtlus (FRM FReg) XReg)
1478(rule (rv_fcvtlus frm rs1) (fpu_rr_int (FpuOPRR.FcvtLuFmt) $F32 frm rs1))
1479
1480;; Helper for emitting the `fcvt.w.d` instruction.
1481(decl rv_fcvtwd (FRM FReg) XReg)
1482(rule (rv_fcvtwd frm rs1) (fpu_rr_int (FpuOPRR.FcvtWFmt) $F64 frm rs1))
1483
1484;; Helper for emitting the `fcvt.l.d` instruction.
1485(decl rv_fcvtld (FRM FReg) XReg)
1486(rule (rv_fcvtld frm rs1) (fpu_rr_int (FpuOPRR.FcvtLFmt) $F64 frm rs1))
1487
1488;; Helper for emitting the `fcvt.wu.d` instruction.
1489(decl rv_fcvtwud (FRM FReg) XReg)
1490(rule (rv_fcvtwud frm rs1) (fpu_rr_int (FpuOPRR.FcvtWuFmt) $F64 frm rs1))
1491
1492;; Helper for emitting the `fcvt.lu.d` instruction.
1493(decl rv_fcvtlud (FRM FReg) XReg)
1494(rule (rv_fcvtlud frm rs1) (fpu_rr_int (FpuOPRR.FcvtLuFmt) $F64 frm rs1))
1495
1496;; Helper for emitting the `fcvt.w.*` instructions.
1497(decl rv_fcvtw (Type FRM FReg) XReg)
1498(rule (rv_fcvtw $F32 frm rs1) (rv_fcvtws frm rs1))
1499(rule (rv_fcvtw $F64 frm rs1) (rv_fcvtwd frm rs1))
1500
1501;; Helper for emitting the `fcvt.l.*` instructions.
1502(decl rv_fcvtl (Type FRM FReg) XReg)
1503(rule (rv_fcvtl $F32 frm rs1) (rv_fcvtls frm rs1))
1504(rule (rv_fcvtl $F64 frm rs1) (rv_fcvtld frm rs1))
1505
1506;; Helper for emitting the `fcvt.wu.*` instructions.
1507(decl rv_fcvtwu (Type FRM FReg) XReg)
1508(rule (rv_fcvtwu $F32 frm rs1) (rv_fcvtwus frm rs1))
1509(rule (rv_fcvtwu $F64 frm rs1) (rv_fcvtwud frm rs1))
1510
1511;; Helper for emitting the `fcvt.lu.*` instructions.
1512(decl rv_fcvtlu (Type FRM FReg) XReg)
1513(rule (rv_fcvtlu $F32 frm rs1) (rv_fcvtlus frm rs1))
1514(rule (rv_fcvtlu $F64 frm rs1) (rv_fcvtlud frm rs1))
1515
1516;; Helper for emitting the `fsgnj` ("Floating Point Sign Injection") instruction.
1517;; The output of this instruction is `rs1` with the sign bit from `rs2`
1518;; This implements the `copysign` operation
1519(decl rv_fsgnj (Type FReg FReg) FReg)
1520(rule (rv_fsgnj ty rs1 rs2) (fpu_rrr (FpuOPRRR.Fsgnj) ty (FRM.RNE) rs1 rs2))
1521
1522;; Helper for emitting the `fsgnjn` ("Floating Point Sign Injection Negated") instruction.
1523;; The output of this instruction is `rs1` with the negated sign bit from `rs2`
1524;; When `rs1 == rs2` this implements the `neg` operation
1525(decl rv_fsgnjn (Type FReg FReg) FReg)
1526(rule (rv_fsgnjn ty rs1 rs2) (fpu_rrr (FpuOPRRR.Fsgnjn) ty (FRM.RTZ) rs1 rs2))
1527
1528;; Helper for emitting the `fneg` ("Floating Point Negate") instruction.
1529;; This instruction is a mnemonic for `fsgnjn rd, rs1, rs1`
1530(decl rv_fneg (Type FReg) FReg)
1531(rule (rv_fneg ty rs1) (rv_fsgnjn ty rs1 rs1))
1532
1533;; Helper for emitting the `fsgnjx` ("Floating Point Sign Injection Exclusive") instruction.
1534;; The output of this instruction is `rs1` with the XOR of the sign bits from `rs1` and `rs2`.
1535;; When `rs1 == rs2` this implements `fabs`
1536(decl rv_fsgnjx (Type FReg FReg) FReg)
1537(rule (rv_fsgnjx ty rs1 rs2) (fpu_rrr (FpuOPRRR.Fsgnjx) ty (FRM.RDN) rs1 rs2))
1538
1539;; Helper for emitting the `fabs` ("Floating Point Absolute") instruction.
1540;; This instruction is a mnemonic for `fsgnjx rd, rs1, rs1`
1541(decl rv_fabs (Type FReg) FReg)
1542(rule (rv_fabs ty rs1) (rv_fsgnjx ty rs1 rs1))
1543
1544;; Helper for emitting the `feq` ("Float Equal") instruction.
1545(decl rv_feq (Type FReg FReg) XReg)
1546(rule (rv_feq ty rs1 rs2) (fpu_rrr_int (FpuOPRRR.Feq) ty (FRM.RDN) rs1 rs2))
1547
1548;; Helper for emitting the `flt` ("Float Less Than") instruction.
1549(decl rv_flt (Type FReg FReg) XReg)
1550(rule (rv_flt ty rs1 rs2) (fpu_rrr_int (FpuOPRRR.Flt) ty (FRM.RTZ) rs1 rs2))
1551
1552;; Helper for emitting the `fle` ("Float Less Than or Equal") instruction.
1553(decl rv_fle (Type FReg FReg) XReg)
1554(rule (rv_fle ty rs1 rs2) (fpu_rrr_int (FpuOPRRR.Fle) ty (FRM.RNE) rs1 rs2))
1555
1556;; Helper for emitting the `fgt` ("Float Greater Than") instruction.
1557;; Note: The arguments are reversed
1558(decl rv_fgt (Type FReg FReg) XReg)
1559(rule (rv_fgt ty rs1 rs2) (rv_flt ty rs2 rs1))
1560
1561;; Helper for emitting the `fge` ("Float Greater Than or Equal") instruction.
1562;; Note: The arguments are reversed
1563(decl rv_fge (Type FReg FReg) XReg)
1564(rule (rv_fge ty rs1 rs2) (rv_fle ty rs2 rs1))
1565
1566;; Helper for emitting the `fmin` instruction.
1567(decl rv_fmin (Type FReg FReg) FReg)
1568(rule (rv_fmin ty rs1 rs2) (fpu_rrr (FpuOPRRR.Fmin) ty (FRM.RNE) rs1 rs2))
1569
1570;; Helper for emitting the `fmax` instruction.
1571(decl rv_fmax (Type FReg FReg) FReg)
1572(rule (rv_fmax ty rs1 rs2) (fpu_rrr (FpuOPRRR.Fmax) ty (FRM.RTZ) rs1 rs2))
1573
1574;; `Zfa` Extension Instructions
1575
1576;; Helper for emitting the `fminm` instruction.
1577(decl rv_fminm (Type FReg FReg) FReg)
1578(rule (rv_fminm ty rs1 rs2) (fpu_rrr (FpuOPRRR.Fminm) ty (FRM.RDN) rs1 rs2))
1579
1580;; Helper for emitting the `fmaxm` instruction.
1581(decl rv_fmaxm (Type FReg FReg) FReg)
1582(rule (rv_fmaxm ty rs1 rs2) (fpu_rrr (FpuOPRRR.Fmaxm) ty (FRM.RUP) rs1 rs2))
1583
1584;; Helper for emitting the `fround` instruction.
1585(decl rv_fround (Type FRM FReg) FReg)
1586(rule (rv_fround ty frm rs) (fpu_rr (FpuOPRR.Fround) ty frm rs))
1587
1588;; Helper for emitting the `fli` instruction.
1589(decl rv_fli (Type FliConstant) FReg)
1590(rule (rv_fli ty imm)
1591      (let ((dst WritableFReg (temp_writable_freg))
1592            (_ Unit (emit (MInst.Fli ty
1593                                     imm
1594                                     dst))))
1595        dst))
1596
1597;; `Zba` Extension Instructions
1598
1599;; Helper for emitting the `adduw` ("Add Unsigned Word") instruction.
1600;; rd ← uext32(rs1) + uext32(rs2)
1601(decl rv_adduw (XReg XReg) XReg)
1602(rule (rv_adduw rs1 rs2)
1603  (alu_rrr (AluOPRRR.Adduw) rs1 rs2))
1604
1605;; Helper for emitting the `zext.w` ("Zero Extend Word") instruction.
1606;; This instruction is a mnemonic for `adduw rd, rs1, zero`.
1607;; rd ← uext32(rs1)
1608(decl rv_zextw (XReg) XReg)
1609(rule (rv_zextw rs1)
1610  (rv_adduw rs1 (zero_reg)))
1611
1612;; Helper for emitting the `slli.uw` ("Shift Left Logical Immediate Unsigned Word") instruction.
1613;; rd ← uext32(rs1) << imm
1614(decl rv_slliuw (XReg Imm12) XReg)
1615(rule (rv_slliuw rs1 imm)
1616  (alu_rr_imm12 (AluOPRRI.SlliUw) rs1 imm))
1617
1618
1619;; `Zbb` Extension Instructions
1620
1621;; Helper for emitting the `andn` ("And Negated") instruction.
1622;; rd ← rs1 ∧ ~(rs2)
1623(decl rv_andn (XReg XReg) XReg)
1624(rule (rv_andn rs1 rs2)
1625  (if-let true (has_zbb))
1626  (alu_rrr (AluOPRRR.Andn) rs1 rs2))
1627(rule (rv_andn rs1 rs2)
1628  (if-let false (has_zbb))
1629  (rv_and rs1 (rv_not rs2)))
1630
1631;; Helper for emitting the `orn` ("Or Negated") instruction.
1632;; rd ← rs1 ∨ ~(rs2)
1633(decl rv_orn (XReg XReg) XReg)
1634(rule (rv_orn rs1 rs2)
1635  (alu_rrr (AluOPRRR.Orn) rs1 rs2))
1636
1637;; Helper for emitting the `xnor` ("Exclusive NOR") instruction.
1638;; rd ← ~(rs1 ^ rs2)
1639(decl rv_xnor (XReg XReg) XReg)
1640(rule (rv_xnor rs1 rs2)
1641  (alu_rrr (AluOPRRR.Xnor) rs1 rs2))
1642
1643;; Helper for emitting the `clz` ("Count Leading Zero Bits") instruction.
1644(decl rv_clz (XReg) XReg)
1645(rule (rv_clz rs1)
1646  (alu_rr_funct12 (AluOPRRI.Clz) rs1))
1647
1648;; Helper for emitting the `clzw` ("Count Leading Zero Bits in Word") instruction.
1649(decl rv_clzw (XReg) XReg)
1650(rule (rv_clzw rs1)
1651  (alu_rr_funct12 (AluOPRRI.Clzw) rs1))
1652
1653;; Helper for emitting the `ctz` ("Count Trailing Zero Bits") instruction.
1654(decl rv_ctz (XReg) XReg)
1655(rule (rv_ctz rs1)
1656  (alu_rr_funct12 (AluOPRRI.Ctz) rs1))
1657
1658;; Helper for emitting the `ctzw` ("Count Trailing Zero Bits in Word") instruction.
1659(decl rv_ctzw (XReg) XReg)
1660(rule (rv_ctzw rs1)
1661  (alu_rr_funct12 (AluOPRRI.Ctzw) rs1))
1662
1663;; Helper for emitting the `cpop` ("Count Population") instruction.
1664(decl rv_cpop (XReg) XReg)
1665(rule (rv_cpop rs1)
1666  (alu_rr_funct12 (AluOPRRI.Cpop) rs1))
1667
1668;; Helper for emitting the `cpopw` ("Count Population") instruction.
1669(decl rv_cpopw (XReg) XReg)
1670(rule (rv_cpopw rs1)
1671  (alu_rr_funct12 (AluOPRRI.Cpopw) rs1))
1672
1673;; Helper for emitting the `max` instruction.
1674(decl rv_max (XReg XReg) XReg)
1675(rule (rv_max rs1 rs2)
1676  (alu_rrr (AluOPRRR.Max) rs1 rs2))
1677
1678;; Helper for emitting the `maxu` instruction.
1679(decl rv_maxu (XReg XReg) XReg)
1680(rule (rv_maxu rs1 rs2)
1681  (alu_rrr (AluOPRRR.Maxu) rs1 rs2))
1682
1683;; Helper for emitting the `min` instruction.
1684(decl rv_min (XReg XReg) XReg)
1685(rule (rv_min rs1 rs2)
1686  (alu_rrr (AluOPRRR.Min) rs1 rs2))
1687
1688;; Helper for emitting the `minu` instruction.
1689(decl rv_minu (XReg XReg) XReg)
1690(rule (rv_minu rs1 rs2)
1691  (alu_rrr (AluOPRRR.Minu) rs1 rs2))
1692
1693;; Helper for emitting the `sext.b` instruction.
1694(decl rv_sextb (XReg) XReg)
1695(rule (rv_sextb rs1)
1696  (alu_rr_imm12 (AluOPRRI.Sextb) rs1 (imm12_const 0)))
1697
1698;; Helper for emitting the `sext.h` instruction.
1699(decl rv_sexth (XReg) XReg)
1700(rule (rv_sexth rs1)
1701  (alu_rr_imm12 (AluOPRRI.Sexth) rs1 (imm12_const 0)))
1702
1703;; Helper for emitting the `zext.h` instruction.
1704(decl rv_zexth (XReg) XReg)
1705(rule (rv_zexth rs1)
1706  (alu_rr_imm12 (AluOPRRI.Zexth) rs1 (imm12_const 0)))
1707
1708;; Helper for emitting the `rol` ("Rotate Left") instruction.
1709(decl rv_rol (XReg XReg) XReg)
1710(rule (rv_rol rs1 rs2)
1711  (alu_rrr (AluOPRRR.Rol) rs1 rs2))
1712
1713;; Helper for emitting the `rolw` ("Rotate Left Word") instruction.
1714(decl rv_rolw (XReg XReg) XReg)
1715(rule (rv_rolw rs1 rs2)
1716  (alu_rrr (AluOPRRR.Rolw) rs1 rs2))
1717
1718;; Helper for emitting the `ror` ("Rotate Right") instruction.
1719(decl rv_ror (XReg XReg) XReg)
1720(rule (rv_ror rs1 rs2)
1721  (alu_rrr (AluOPRRR.Ror) rs1 rs2))
1722
1723;; Helper for emitting the `rorw` ("Rotate Right Word") instruction.
1724(decl rv_rorw (XReg XReg) XReg)
1725(rule (rv_rorw rs1 rs2)
1726  (alu_rrr (AluOPRRR.Rorw) rs1 rs2))
1727
1728;; Helper for emitting the `rori` ("Rotate Right") instruction.
1729(decl rv_rori (XReg Imm12) XReg)
1730(rule (rv_rori rs1 rs2)
1731  (alu_rr_imm12 (AluOPRRI.Rori) rs1 rs2))
1732
1733;; Helper for emitting the `roriw` ("Rotate Right Word") instruction.
1734(decl rv_roriw (XReg Imm12) XReg)
1735(rule (rv_roriw rs1 rs2)
1736  (alu_rr_imm12 (AluOPRRI.Roriw) rs1 rs2))
1737
1738;; Helper for emitting the `rev8` ("Byte Reverse") instruction.
1739(decl rv_rev8 (XReg) XReg)
1740(rule (rv_rev8 rs1)
1741  (alu_rr_funct12 (AluOPRRI.Rev8) rs1))
1742
1743;; Helper for emitting the `brev8` ("Bit Reverse Inside Bytes") instruction.
1744;; TODO: This instruction is mentioned in some older versions of the
1745;; spec, but has since disappeared, we should follow up on this.
1746;; It probably was renamed to `rev.b` which seems to be the closest match.
1747(decl rv_brev8 (XReg) XReg)
1748(rule (rv_brev8 rs1)
1749  (alu_rr_funct12 (AluOPRRI.Brev8) rs1))
1750
1751;; `Zbs` Extension Instructions
1752
1753(decl rv_bclr (XReg XReg) XReg)
1754(rule (rv_bclr rs1 rs2)
1755  (alu_rrr (AluOPRRR.Bclr) rs1 rs2))
1756
1757(decl rv_bclri (XReg Imm12) XReg)
1758(rule (rv_bclri rs1 imm)
1759  (alu_rr_imm12 (AluOPRRI.Bclri) rs1 imm))
1760
1761(decl rv_bext (XReg XReg) XReg)
1762(rule (rv_bext rs1 rs2)
1763  (alu_rrr (AluOPRRR.Bext) rs1 rs2))
1764
1765(decl rv_bexti (XReg Imm12) XReg)
1766(rule (rv_bexti rs1 imm)
1767  (alu_rr_imm12 (AluOPRRI.Bexti) rs1 imm))
1768
1769(decl rv_binv (XReg XReg) XReg)
1770(rule (rv_binv rs1 rs2)
1771  (alu_rrr (AluOPRRR.Binv) rs1 rs2))
1772
1773(decl rv_binvi (XReg Imm12) XReg)
1774(rule (rv_binvi rs1 imm)
1775  (alu_rr_imm12 (AluOPRRI.Binvi) rs1 imm))
1776
1777(decl rv_bset (XReg XReg) XReg)
1778(rule (rv_bset rs1 rs2)
1779  (alu_rrr (AluOPRRR.Bset) rs1 rs2))
1780
1781;; Helper for emitting the `bseti` ("Single-Bit Set Immediate") instruction.
1782(decl rv_bseti (XReg Imm12) XReg)
1783(rule (rv_bseti rs1 imm)
1784  (alu_rr_imm12 (AluOPRRI.Bseti) rs1 imm))
1785
1786;; `Zbkb` Extension Instructions
1787
1788;; Helper for emitting the `pack` ("Pack low halves of registers") instruction.
1789(decl rv_pack (XReg XReg) XReg)
1790(rule (rv_pack rs1 rs2)
1791  (alu_rrr (AluOPRRR.Pack) rs1 rs2))
1792
1793;; Helper for emitting the `packw` ("Pack low 16-bits of registers") instruction.
1794(decl rv_packw (XReg XReg) XReg)
1795(rule (rv_packw rs1 rs2)
1796  (alu_rrr (AluOPRRR.Packw) rs1 rs2))
1797
1798;; `ZiCond` Extension Instructions
1799
1800;; Helper for emitting the `czero.eqz` ("Conditional zero, if condition is equal to zero") instruction.
1801;; RS1 is the data source
1802;; RS2 is the condition
1803;;
1804;; rd = (rs2 == 0) ? 0 : rs1
1805(decl rv_czero_eqz (XReg XReg) XReg)
1806(rule (rv_czero_eqz rs1 rs2)
1807  (alu_rrr (AluOPRRR.CzeroEqz) rs1 rs2))
1808
1809;; Helper for emitting the `czero.nez` ("Conditional zero, if condition is nonzero") instruction.
1810;; RS1 is the data source
1811;; RS2 is the condition
1812;;
1813;; rd = (rs2 != 0) ? 0 : rs1
1814(decl rv_czero_nez (XReg XReg) XReg)
1815(rule (rv_czero_nez rs1 rs2)
1816  (alu_rrr (AluOPRRR.CzeroNez) rs1 rs2))
1817
1818
1819;; `Zicsr` Extension Instructions
1820
1821;; Helper for emitting the `csrrwi` instruction.
1822(decl rv_csrrwi (CSR UImm5) XReg)
1823(rule (rv_csrrwi csr imm)
1824  (csr_imm (CsrImmOP.CsrRWI) csr imm))
1825
1826;; This is a special case of `csrrwi` when the CSR is the `frm` CSR.
1827(decl rv_fsrmi (FRM) XReg)
1828(rule (rv_fsrmi frm) (rv_csrrwi (CSR.Frm) frm))
1829
1830
1831;; Helper for emitting the `csrw` instruction. This is a special case of
1832;; `csrrw` where the destination register is always `x0`.
1833(decl rv_csrw (CSR XReg) Unit)
1834(rule (rv_csrw csr rs)
1835  (csr_reg_dst_zero (CsrRegOP.CsrRW) csr rs))
1836
1837;; This is a special case of `csrw` when the CSR is the `frm` CSR.
1838(decl rv_fsrm (XReg) Unit)
1839(rule (rv_fsrm rs) (rv_csrw (CSR.Frm) rs))
1840
1841
1842
1843
1844
1845
1846;; Helper for generating a FliConstant from a u64 constant
1847(decl pure partial fli_constant_from_u64 (Type u64) FliConstant)
1848(extern constructor fli_constant_from_u64 fli_constant_from_u64)
1849
1850;; Helper for generating a FliConstant from a u64 negated constant
1851(decl pure partial fli_constant_from_negated_u64 (Type u64) FliConstant)
1852(extern constructor fli_constant_from_negated_u64 fli_constant_from_negated_u64)
1853
1854;; Helper for generating a i64 from a pair of Imm20 and Imm12 constants
1855(decl i64_generate_imm (Imm20 Imm12) i64)
1856(extern extractor i64_generate_imm i64_generate_imm)
1857
1858;; Helper for generating a i64 from a shift of a Imm20 constant with LUI
1859(decl i64_shift_for_lui (u64 Imm12) i64)
1860(extern extractor i64_shift_for_lui i64_shift_for_lui)
1861
1862;; Helper for generating a i64 from a shift of a Imm20 constant
1863(decl i64_shift (i64 Imm12) i64)
1864(extern extractor i64_shift i64_shift)
1865
1866(decl pure has_fli_for_type (Type) bool)
1867(rule 2 (has_fli_for_type $F16) (if-let true (has_zfh)) (has_zfa))
1868(rule 1 (has_fli_for_type $F16) (if-let true (has_zvfh)) (has_zfa))
1869(rule (has_fli_for_type $F16) false)
1870(rule (has_fli_for_type $F32) (has_zfa))
1871(rule (has_fli_for_type $F64) (has_zfa))
1872
1873;; Immediate Loading rules
1874;; TODO: Loading the zero reg directly causes a bunch of regalloc errors, we should look into it.
1875;; TODO: Load floats using `fld` instead of `ld`
1876;;
1877;; Recursion: bounded since either float cases are reduced to integers, or the
1878;; shift case reduces to a smaller constant.
1879(decl rec imm (Type u64) Reg)
1880
1881;; Special-case 0.0 for floats to use the `(zero_reg)` directly.
1882;; See #7162 for why this doesn't fall out of the rules below.
1883(rule 9 (imm (ty_supported_float_min ty) 0) (gen_bitcast (zero_reg) (float_int_of_same_size ty) ty))
1884
1885;; If Zfa is enabled, we can load certain constants with the `fli` instruction.
1886(rule 8 (imm (ty_supported_float_size ty) imm)
1887  (if-let true (has_fli_for_type ty))
1888  (if-let const (fli_constant_from_u64 ty imm))
1889  (rv_fli ty const))
1890
1891;; It is beneficial to load the negated constant with `fli` and then negate it
1892;; in a register.
1893;;
1894;; For f64's this saves one instruction, and for f32's it avoids
1895;; having to allocate an integer register, reducing integer register pressure.
1896(rule 7 (imm (ty_supported_float_full ty) imm)
1897  (if-let true (has_fli_for_type ty))
1898  (if-let const (fli_constant_from_negated_u64 ty imm))
1899  (rv_fneg ty (rv_fli ty const)))
1900
1901;; Otherwise floats get loaded as integers and then moved into an F register.
1902(rule 6 (imm (ty_supported_float_min ty) c) (gen_bitcast (imm (float_int_of_same_size ty) c) (float_int_of_same_size ty) ty))
1903;; NaN-box the constant when 16-bit `fmv` is unavailable.
1904(rule 5 (imm (ty_supported_float_size $F16) c) (gen_bitcast (imm $I32 (u64_or c 0xffff0000)) $I32 $F32))
1905
1906;; Try to match just an imm12
1907(rule 4 (imm (ty_int ty) c)
1908  (if-let (i64_generate_imm (imm20_is_zero) imm12) (i64_sextend_u64 ty c))
1909  (rv_addi (zero_reg) imm12))
1910
1911;; We can also try to load using a single LUI.
1912;; LUI takes a 20 bit immediate, places it on bits 13 to 32 of the register.
1913;; In RV64 this value is then sign extended to 64bits.
1914(rule 3 (imm (ty_int ty) c)
1915  (if-let (i64_generate_imm imm20 (imm12_is_zero)) (i64_sextend_u64 ty c))
1916  (rv_lui imm20))
1917
1918;; We can combo addi + lui to represent all 32-bit immediates
1919;; And some 64-bit immediates as well.
1920(rule 2 (imm (ty_int ty) c)
1921  (if-let (i64_generate_imm imm20 imm12) (i64_sextend_u64 ty c))
1922  (rv_addi (rv_lui imm20) imm12))
1923
1924;; If the non-zero bits of the immediate fit in 20 bits, we can use LUI + shift
1925(rule 1 (imm (ty_int ty) c)
1926  (if-let (i64_shift_for_lui (imm20_from_u64 base) shift) (i64_sextend_u64 ty c))
1927  (rv_slli (rv_lui base) shift))
1928
1929;; Combine one of the above rules with a shift-left if possible, This chops off
1930;; all trailing zeros from the input constant and then attempts if the resulting
1931;; constant can itself use one of the above rules via the `i64_generate_imm`
1932;; matcher. This will then recurse on the above rules to materialize a smaller
1933;; constant which is then shifted left to create the desired constant.
1934(rule 0 (imm (ty_int ty) c)
1935  (if-let (i64_shift c_shifted shift) (i64_sextend_u64 ty c))  ;; constant to make
1936  (if-let (i64_generate_imm _ _) c_shifted)                    ;; can the smaller constant be made?
1937  (rv_slli (imm ty (i64_cast_unsigned c_shifted)) shift))
1938
1939;; Otherwise we fall back to loading the immediate from the constant pool.
1940(rule -1 (imm (ty_int ty) c)
1941  (gen_load
1942    (gen_const_amode (emit_u64_le_const c))
1943    (LoadOP.Ld)
1944    (mem_flags_trusted)))
1945
1946;; Imm12 Rules
1947
1948(decl pure imm12_zero () Imm12)
1949(rule (imm12_zero) (imm12_const 0))
1950
1951(decl pure imm12_const (i32) Imm12)
1952(extern constructor imm12_const imm12_const)
1953
1954(decl load_imm12 (i32) Reg)
1955(rule
1956  (load_imm12 x)
1957  (rv_addi (zero_reg) (imm12_const x)))
1958
1959;; for load immediate
1960(decl imm_from_bits (u64) Imm12)
1961(extern constructor imm_from_bits imm_from_bits)
1962
1963(decl imm_from_neg_bits (i64) Imm12)
1964(extern constructor imm_from_neg_bits imm_from_neg_bits)
1965
1966(decl imm12_const_add (i32 i32) Imm12)
1967(extern constructor imm12_const_add imm12_const_add)
1968
1969;; Performs a fallible add of the `Imm12` value and the 32-bit value provided.
1970(decl pure partial imm12_add (Imm12 i32) Imm12)
1971(extern constructor imm12_add imm12_add)
1972
1973(decl imm12_and (Imm12 u64) Imm12)
1974(extern constructor imm12_and imm12_and)
1975
1976;; Imm12 Extractors
1977
1978;; Helper to go directly from a `Value`, when it's an `iconst`, to an `Imm12`.
1979(decl imm12_from_value (Imm12) Value)
1980(extractor (imm12_from_value n) (i64_from_iconst (imm12_from_i64 n)))
1981
1982;; Conceptually the same as `imm12_from_value`, but tries negating the constant
1983;; value (first sign-extending to handle narrow widths).
1984(decl pure partial imm12_from_negated_value (Value) Imm12)
1985(rule
1986  (imm12_from_negated_value (has_type ty (iconst _ n)))
1987  (if-let (imm12_from_u64 imm) (i64_cast_unsigned (i64_wrapping_neg (i64_sextend_imm64 ty n))))
1988  imm)
1989
1990(decl imm12_from_u64 (Imm12) u64)
1991(extern extractor imm12_from_u64 imm12_from_u64)
1992
1993(decl imm12_from_i64 (Imm12) i64)
1994(extern extractor imm12_from_i64 imm12_from_i64)
1995
1996(decl pure partial u64_to_imm12 (u64) Imm12)
1997(rule (u64_to_imm12 (imm12_from_u64 n)) n)
1998
1999(decl pure imm12_is_zero () Imm12)
2000(extern extractor imm12_is_zero imm12_is_zero)
2001
2002;; Imm20
2003
2004;; Extractor that matches if a Imm20 is zero
2005(decl pure imm20_is_zero () Imm20)
2006(extern extractor imm20_is_zero imm20_is_zero)
2007
2008(decl imm20_from_u64 (Imm20) u64)
2009(extern extractor imm20_from_u64 imm20_from_u64)
2010
2011(decl imm20_from_i64 (Imm20) i64)
2012(extern extractor imm20_from_i64 imm20_from_i64)
2013
2014
2015;; Imm5 Extractors
2016
2017(decl imm5_from_u64 (Imm5) u64)
2018(extern extractor imm5_from_u64 imm5_from_u64)
2019
2020(decl imm5_from_i64 (Imm5) i64)
2021(extern extractor imm5_from_i64 imm5_from_i64)
2022
2023;; Construct a Imm5 from an i8
2024(decl pure partial i8_to_imm5 (i8) Imm5)
2025(extern constructor i8_to_imm5 i8_to_imm5)
2026
2027;; Helper to go directly from a `Value` to an `Imm5`.
2028(decl imm5_from_value (Imm5) Value)
2029(extractor (imm5_from_value n) (i64_from_iconst (imm5_from_i64 n)))
2030
2031;; Like imm5_from_value, but first negates the `Value`.
2032(decl pure partial imm5_from_negated_value (Value) Imm5)
2033(rule (imm5_from_negated_value (has_type ty (iconst _ n)))
2034  (if-let (imm5_from_i64 imm) (i64_wrapping_neg (i64_sextend_imm64 ty n)))
2035  imm)
2036
2037;; Constructor that matches a `Value` equivalent to a replicated Imm5 on all lanes.
2038(decl pure partial replicated_imm5 (Value) Imm5)
2039(rule (replicated_imm5 (splat _ (imm5_from_value n))) n)
2040(rule (replicated_imm5 (vconst _ (u128_from_constant n128)))
2041  (if-let (u128_replicated_u64 n64) n128)
2042  (if-let (u64_replicated_u32 n32) n64)
2043  (if-let (u32_replicated_u16 n16) n32)
2044  (if-let (u16_replicated_u8 n8) n16)
2045  (if-let n (i8_to_imm5 (u8_cast_signed n8)))
2046  n)
2047
2048;; Like replicated_imm5, but first negates the `Value`.
2049(decl pure partial negated_replicated_imm5 (Value) Imm5)
2050(rule (negated_replicated_imm5 (splat _ n))
2051  (if-let imm5 (imm5_from_negated_value n))
2052  imm5)
2053(rule (negated_replicated_imm5 (vconst _ (u128_from_constant n128)))
2054  (if-let (u128_replicated_u64 n64) n128)
2055  (if-let (u64_replicated_u32 n32) n64)
2056  (if-let (u32_replicated_u16 n16) n32)
2057  (if-let (u16_replicated_u8 n8) n16)
2058  (if-let n (i8_to_imm5 (i8_wrapping_neg (u8_cast_signed n8))))
2059  n)
2060
2061;; UImm5 Helpers
2062
2063;; Constructor that matches a `Value` equivalent to a replicated UImm5 on all lanes.
2064(decl pure partial replicated_uimm5 (Value) UImm5)
2065(rule (replicated_uimm5 (splat _ (uimm5_from_value n))) n)
2066(rule 1 (replicated_uimm5 (vconst _ (u128_from_constant n128)))
2067  (if-let (u128_replicated_u64 n64) n128)
2068  (if-let (u64_replicated_u32 n32) n64)
2069  (if-let (u32_replicated_u16 n16) n32)
2070  (if-let (u16_replicated_u8 n8) n16)
2071  (if-let (uimm5_from_u8 n) n8)
2072  n)
2073
2074;; Helper to go directly from a `Value`, when it's an `iconst`, to an `UImm5`.
2075(decl uimm5_from_value (UImm5) Value)
2076(extractor (uimm5_from_value n)
2077  (iconst _ (u64_from_imm64 (uimm5_from_u64 n))))
2078
2079;; Extract a `UImm5` from an `u8`.
2080(decl pure partial uimm5_from_u8 (UImm5) u8)
2081(extern extractor uimm5_from_u8 uimm5_from_u8)
2082
2083;; Extract a `UImm5` from an `u64`.
2084(decl pure partial uimm5_from_u64 (UImm5) u64)
2085(extern extractor uimm5_from_u64 uimm5_from_u64)
2086
2087;; Convert a `u64` into an `UImm5`
2088(decl pure partial u64_to_uimm5 (u64) UImm5)
2089(rule (u64_to_uimm5 (uimm5_from_u64 n)) n)
2090
2091(decl uimm5_bitcast_to_imm5 (UImm5) Imm5)
2092(extern constructor uimm5_bitcast_to_imm5 uimm5_bitcast_to_imm5)
2093
2094;; Float Helpers
2095
2096;; Returns the bitpattern of the Canonical NaN for the given type.
2097(decl pure canonical_nan_u64 (Type) u64)
2098(rule (canonical_nan_u64 $F32) 0x7fc00000)
2099(rule (canonical_nan_u64 $F64) 0x7ff8000000000000)
2100
2101;; Helper for emitting `MInst.FpuRR` instructions.
2102(decl fpu_rr (FpuOPRR Type FRM Reg) FReg)
2103(rule (fpu_rr op ty frm src)
2104      (let ((dst WritableFReg (temp_writable_freg))
2105            (_ Unit (emit (MInst.FpuRR op ty frm dst src))))
2106        dst))
2107
2108;; Similar to fpu_rr but with an integer destination register
2109(decl fpu_rr_int (FpuOPRR Type FRM Reg) XReg)
2110(rule (fpu_rr_int op ty frm src)
2111      (let ((dst WritableXReg (temp_writable_xreg))
2112            (_ Unit (emit (MInst.FpuRR op ty frm dst src))))
2113        dst))
2114
2115;; Helper for emitting `MInst.AluRRR` instructions.
2116(decl alu_rrr (AluOPRRR Reg Reg) Reg)
2117(rule (alu_rrr op src1 src2)
2118      (let ((dst WritableXReg (temp_writable_xreg))
2119            (_ Unit (emit (MInst.AluRRR op dst src1 src2))))
2120        dst))
2121
2122;; Helper for emitting `MInst.FpuRRR` instructions.
2123(decl fpu_rrr (FpuOPRRR Type FRM Reg Reg) FReg)
2124(rule (fpu_rrr op ty frm src1 src2)
2125      (let ((dst WritableFReg (temp_writable_freg))
2126            (_ Unit (emit (MInst.FpuRRR op ty frm dst src1 src2))))
2127        dst))
2128
2129;; Similar to fpu_rrr but with an integer destination register
2130(decl fpu_rrr_int (FpuOPRRR Type FRM Reg Reg) XReg)
2131(rule (fpu_rrr_int op ty frm src1 src2)
2132      (let ((dst WritableXReg (temp_writable_xreg))
2133            (_ Unit (emit (MInst.FpuRRR op ty frm dst src1 src2))))
2134        dst))
2135
2136;; Helper for emitting `MInst.FpuRRRR` instructions.
2137(decl fpu_rrrr (FpuOPRRRR Type FRM Reg Reg Reg) FReg)
2138(rule (fpu_rrrr op ty frm src1 src2 src3)
2139      (let ((dst WritableFReg (temp_writable_freg))
2140            (_ Unit (emit (MInst.FpuRRRR op ty frm dst src1 src2 src3))))
2141        dst))
2142
2143
2144;; Helper for emitting `MInst.AluRRImm12` instructions.
2145(decl alu_rr_imm12 (AluOPRRI Reg Imm12) Reg)
2146(rule (alu_rr_imm12 op src imm)
2147      (let ((dst WritableXReg (temp_writable_xreg))
2148            (_ Unit (emit (MInst.AluRRImm12 op dst src imm))))
2149        dst))
2150
2151;; some instruction use imm12 as funct12.
2152;; so we don't need the imm12 parameter.
2153(decl alu_rr_funct12 (AluOPRRI Reg) Reg)
2154(rule (alu_rr_funct12 op src)
2155      (let ((dst WritableXReg (temp_writable_xreg))
2156            (_ Unit (emit (MInst.AluRRImm12 op dst src (imm12_zero)))))
2157        dst))
2158
2159;; Helper for emitting the `Lui` instruction.
2160;; TODO: This should be something like `emit_u_type`. And should share the
2161;; `MInst` with `auipc` since these instructions share the U-Type format.
2162(decl rv_lui (Imm20) XReg)
2163(rule (rv_lui imm)
2164      (let ((dst WritableXReg (temp_writable_xreg))
2165            (_ Unit (emit (MInst.Lui dst imm))))
2166        dst))
2167
2168;; Helper for emitting `MInst.CsrImm` instructions.
2169(decl csr_imm (CsrImmOP CSR UImm5) XReg)
2170(rule (csr_imm op csr imm)
2171      (let ((dst WritableXReg (temp_writable_xreg))
2172            (_ Unit (emit (MInst.CsrImm op dst imm csr))))
2173        dst))
2174
2175;; Helper for emitting a `MInst.CsrReg` instruction that writes the result to x0.
2176(decl csr_reg_dst_zero (CsrRegOP CSR XReg) Unit)
2177(rule (csr_reg_dst_zero op csr rs)
2178      (emit (MInst.CsrReg op (writable_zero_reg) rs csr)))
2179
2180
2181
2182(decl select_addi (Type) AluOPRRI)
2183(rule 1 (select_addi (fits_in_32 ty)) (AluOPRRI.Addiw))
2184(rule (select_addi (fits_in_64 ty)) (AluOPRRI.Addi))
2185
2186
2187(decl gen_andi (XReg u64) XReg)
2188(rule 1 (gen_andi x (imm12_from_u64 y))
2189  (rv_andi x y))
2190
2191(rule 0 (gen_andi x y)
2192  (rv_and x (imm $I64 y)))
2193
2194
2195(decl gen_or (Type ValueRegs ValueRegs) ValueRegs)
2196(rule 1 (gen_or $I128 x y)
2197  (value_regs
2198    (rv_or (value_regs_get x 0) (value_regs_get y 0))
2199    (rv_or (value_regs_get x 1) (value_regs_get y 1))))
2200
2201(rule 0 (gen_or (fits_in_64 _) x y)
2202  (rv_or (value_regs_get x 0) (value_regs_get y 0)))
2203
2204
2205(decl lower_ctz (Type Reg) Reg)
2206(rule (lower_ctz ty x)
2207  (gen_cltz false x ty))
2208
2209(rule 1 (lower_ctz (fits_in_16 ty) x)
2210  (if-let true (has_zbb))
2211  (let ((tmp Reg (gen_bseti x (ty_bits ty))))
2212    (rv_ctzw tmp)))
2213
2214(rule 2 (lower_ctz $I32 x)
2215  (if-let true (has_zbb))
2216  (rv_ctzw x))
2217
2218(rule 2 (lower_ctz $I64 x)
2219  (if-let true (has_zbb))
2220  (rv_ctz x))
2221
2222;; Count leading zeros from a i128 bit value.
2223;; We count both halves separately and conditionally add them if it makes sense.
2224
2225(decl gen_cltz (bool XReg Type) XReg)
2226(rule (gen_cltz leading rs ty)
2227  (let ((tmp WritableXReg (temp_writable_xreg))
2228        (step WritableXReg (temp_writable_xreg))
2229        (sum WritableXReg (temp_writable_xreg))
2230        (_ Unit (emit (MInst.Cltz leading sum step tmp rs ty))))
2231    sum))
2232
2233;; Performs a zero extension of the given value
2234(decl zext (Value) XReg)
2235
2236;; In the most generic case, we shift left and then shift right.
2237(rule 0 (zext val @ (value_type (fits_in_32 ty)))
2238  (let ((shift Imm12 (imm_from_bits (u64_wrapping_sub 64 (ty_bits ty)))))
2239    (rv_srli (rv_slli val shift) shift)))
2240
2241;; If we are zero extending a U8 we can use a `andi` instruction.
2242(rule 1 (zext val @ (value_type $I8))
2243  (rv_andi val (imm12_const 0xff)))
2244
2245;; No point in trying to use `packh` here to zero extend 8 bit values
2246;; since we can just use `andi` instead which is part of the base ISA.
2247
2248;; If we have the `zbkb` extension `packw` can be used to zero extend 16 bit values
2249(rule 1 (zext val @ (value_type $I16))
2250  (if-let true (has_zbkb))
2251  (rv_packw val (zero_reg)))
2252
2253;; If we have the `zbkb` extension `pack` can be used to zero extend 32 bit registers
2254(rule 1 (zext val @ (value_type $I32))
2255  (if-let true (has_zbkb))
2256  (rv_pack val (zero_reg)))
2257
2258;; If we have the `zbb` extension we can use the dedicated `zext.h` instruction.
2259(rule 2 (zext val @ (value_type $I16))
2260  (if-let true (has_zbb))
2261  (rv_zexth val))
2262
2263;; With `zba` we have a `zext.w` instruction
2264(rule 2 (zext val @ (value_type $I32))
2265  (if-let true (has_zba))
2266  (rv_zextw val))
2267
2268;; Ignore sign extensions for values whose representation is already the full
2269;; register width.
2270(rule 3 (zext val)
2271  (if (val_already_extended (ExtendOp.Zero) val))
2272  val)
2273
2274;; Performs a signed extension of the given value
2275(decl sext (Value) XReg)
2276
2277;; Same base case as `zext`, shift left-then-right.
2278(rule 0 (sext val @ (value_type (fits_in_32 ty)))
2279  (let ((shift Imm12 (imm_from_bits (u64_wrapping_sub 64 (ty_bits ty)))))
2280    (rv_srai (rv_slli val shift) shift)))
2281
2282;; If we have the `zbb` extension we can use the dedicated `sext.b` instruction.
2283(rule 1 (sext val @ (value_type $I8))
2284  (if-let true (has_zbb))
2285  (rv_sextb val))
2286
2287;; If we have the `zbb` extension we can use the dedicated `sext.h` instruction.
2288(rule 1 (sext val @ (value_type $I16))
2289  (if-let true (has_zbb))
2290  (rv_sexth val))
2291
2292;; When signed extending from 32 to 64 bits we can use a
2293;; `addiw val 0`. Also known as a `sext.w`
2294(rule 1 (sext val @ (value_type $I32))
2295  (rv_sextw val))
2296
2297;; Ignore sign extensions for values whose representation is already the full
2298;; register width.
2299(rule 2 (sext val)
2300  (if (val_already_extended (ExtendOp.Signed) val))
2301  val)
2302
2303;; Helper matcher for when a value's representation is already sign or zero
2304;; extended to the full 64-bit register representation. This is used by `zext`
2305;; and `sext` above to skip the extension instruction entirely in some
2306;; circumstances.
2307(decl pure partial val_already_extended (ExtendOp Value) bool)
2308(rule 0 (val_already_extended _ v @ (value_type $I64)) true)
2309
2310;; When extending our backend always extends to the full register width, so
2311;; there's no need to extend-an-extend.
2312(rule 1 (val_already_extended (ExtendOp.Zero) (uextend _ _)) true)
2313(rule 1 (val_already_extended (ExtendOp.Signed) (sextend _ _)) true)
2314
2315;; The result of `icmp`/`fcmp` is zero or one, meaning that it's already sign
2316;; extended to the full register width.
2317(rule 1 (val_already_extended _ (icmp _ _ _ _)) true)
2318(rule 1 (val_already_extended _ (fcmp _ _ _ _)) true)
2319
2320;; The lowering for these operations always sign-extend their results due to the
2321;; use of the `*w` instructions in RV64I. Note that this requires that the
2322;; extension is from 32 to 64, 16/8-bit operations are explicitly excluded here.
2323;; There are no native instructions for the 16/8 bit operations so they must
2324;; fall through to actual sign extension above.
2325(rule 1 (val_already_extended (ExtendOp.Signed) (has_type $I32 (ishl _ _ _))) true)
2326(rule 1 (val_already_extended (ExtendOp.Signed) (has_type $I32 (ushr _ _ _))) true)
2327(rule 1 (val_already_extended (ExtendOp.Signed) (has_type $I32 (sshr _ _ _))) true)
2328(rule 1 (val_already_extended (ExtendOp.Signed) (has_type $I32 (iadd _ _ _))) true)
2329(rule 1 (val_already_extended (ExtendOp.Signed) (has_type $I32 (isub _ _ _))) true)
2330
2331(type ExtendOp
2332  (enum
2333    (Zero)
2334    (Signed)))
2335
2336(decl lower_b128_binary (AluOPRRR ValueRegs ValueRegs) ValueRegs)
2337(rule
2338  (lower_b128_binary op a b)
2339  (let
2340    ( ;; low part.
2341      (low XReg (alu_rrr op (value_regs_get a 0) (value_regs_get b 0)))
2342      ;; high part.
2343      (high XReg (alu_rrr op (value_regs_get a 1) (value_regs_get b 1))))
2344    (value_regs low high)))
2345
2346(decl lower_smlhi (Type XReg XReg) XReg)
2347(rule 1
2348  (lower_smlhi $I64 rs1 rs2)
2349  (rv_mulh rs1 rs2))
2350
2351(rule
2352  (lower_smlhi ty rs1 rs2)
2353  (let
2354    ((tmp XReg (rv_mul rs1 rs2)))
2355    (rv_srli tmp (imm12_const (ty_bits ty)))))
2356
2357;;;; construct shift amount.rotl on i128 will use shift to implement. So can call this function.
2358;;;; this will return shift amount and (ty_bits - "shift amount")
2359;;;; if ty_bits is greater than 64 like i128, then shmat will fallback to 64.because We are 64 bit platform.
2360(decl gen_shamt (Type XReg) ValueRegs)
2361(extern constructor gen_shamt gen_shamt)
2362
2363;; bseti: Set a single bit in a register, indexed by a constant.
2364(decl gen_bseti (Reg u64) Reg)
2365(rule (gen_bseti val bit)
2366  (if-let false (has_zbs))
2367  (if-let false (u64_lt_eq bit 12))
2368  (let ((const XReg (imm $I64 (u64_wrapping_shl 1 (u64_unwrap_into_u32 bit)))))
2369    (rv_or val const)))
2370
2371(rule (gen_bseti val bit)
2372  (if-let false (has_zbs))
2373  (if-let true (u64_lt_eq bit 12))
2374  (rv_ori val (imm12_const (u32_cast_signed (u32_wrapping_shl 1 (u64_unwrap_into_u32 bit))))))
2375
2376(rule (gen_bseti val bit)
2377  (if-let true (has_zbs))
2378  (rv_bseti val (imm12_const (u32_cast_signed (u64_unwrap_into_u32 bit)))))
2379
2380
2381(decl gen_popcnt (XReg) Reg)
2382(rule (gen_popcnt rs)
2383  (let
2384    ((tmp WritableXReg (temp_writable_xreg))
2385      (step WritableXReg (temp_writable_xreg))
2386      (sum WritableXReg (temp_writable_xreg))
2387      (_ Unit (emit (MInst.Popcnt sum step tmp rs $I64))))
2388    (writable_reg_to_reg sum)))
2389
2390;; Generates a AMode that points to a register plus an offset.
2391(decl gen_reg_offset_amode (Reg i64) AMode)
2392(extern constructor gen_reg_offset_amode gen_reg_offset_amode)
2393
2394;; Generates a AMode that an offset from the stack pointer.
2395(decl gen_sp_offset_amode (i64) AMode)
2396(extern constructor gen_sp_offset_amode gen_sp_offset_amode)
2397
2398;; Generates a AMode that an offset from the frame pointer.
2399(decl gen_fp_offset_amode (i64) AMode)
2400(extern constructor gen_fp_offset_amode gen_fp_offset_amode)
2401
2402;; Generates an AMode that points to a stack slot + offset.
2403(decl gen_stack_slot_amode (StackSlot i64) AMode)
2404(extern constructor gen_stack_slot_amode gen_stack_slot_amode)
2405
2406;; Generates a AMode that points to a constant in the constant pool.
2407(decl gen_const_amode (VCodeConstant) AMode)
2408(extern constructor gen_const_amode gen_const_amode)
2409
2410
2411
2412;; Tries to match a Value + Offset into an AMode
2413(decl amode (Value i32) AMode)
2414(rule 0 (amode addr offset) (amode_inner addr offset))
2415
2416;; If we are adding a constant offset with an iadd we can instead make that
2417;; offset part of the amode offset.
2418;;
2419;; We can't recurse into `amode` again since that could cause stack overflows.
2420;; See: https://github.com/bytecodealliance/wasmtime/pull/6968
2421(rule 1 (amode (iadd _ addr (i32_from_iconst y)) offset)
2422  (if-let new_offset (i32_checked_add y offset))
2423  (amode_inner addr new_offset))
2424(rule 2 (amode (iadd _ (i32_from_iconst x) addr) offset)
2425  (if-let new_offset (i32_checked_add x offset))
2426  (amode_inner addr new_offset))
2427
2428
2429;; These are the normal rules for generating an AMode.
2430(decl amode_inner (Value i32) AMode)
2431
2432;; In the simplest case we just lower into a Reg+Offset
2433(rule 0 (amode_inner r @ (value_type (ty_addr64 _)) offset)
2434  (gen_reg_offset_amode r offset))
2435
2436;; If the value is a `get_frame_pointer`, we can just use the offset from that.
2437(rule 1 (amode_inner (get_frame_pointer _) offset)
2438  (gen_fp_offset_amode offset))
2439
2440;; If the value is a `get_stack_pointer`, we can just use the offset from that.
2441(rule 1 (amode_inner (get_stack_pointer _) offset)
2442  (gen_sp_offset_amode offset))
2443
2444;; Similarly if the value is a `stack_addr` we can also turn that into an sp offset.
2445(rule 1 (amode_inner (stack_addr _ ss ss_offset) amode_offset)
2446  (if-let combined_offset (i32_checked_add ss_offset amode_offset))
2447  (gen_stack_slot_amode ss combined_offset))
2448
2449
2450;; Helpers for sinkable loads ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2451
2452;; RISC-V doesen't really have sinkable loads. But the regular load instructions
2453;; sign / zero extend their results to 64 bits. So we can pretend they are
2454;; an extend instruction with a sinkable load. This allows us to have better
2455;; lowerings on these cases.
2456
2457;; Extract a sinkable instruction from a value operand.
2458(decl sinkable_inst (Inst) Value)
2459(extern extractor sinkable_inst sinkable_inst)
2460
2461;; Matches a sinkable load.
2462(decl sinkable_load (Inst Type MemFlags Value Offset32) Value)
2463(extractor (sinkable_load inst ty flags addr offset)
2464           (and
2465              (load _ (little_or_native_endian flags) addr offset)
2466              (sinkable_inst (has_type ty inst))))
2467
2468;; Returns a canonical type for a LoadOP. We only return I64 or F64.
2469(decl load_op_reg_type (LoadOP) Type)
2470(rule 1 (load_op_reg_type (LoadOP.Fld)) $F64)
2471(rule 1 (load_op_reg_type (LoadOP.Flw)) $F64)
2472(rule 1 (load_op_reg_type (LoadOP.Flh)) $F64)
2473(rule 0 (load_op_reg_type _) $I64)
2474
2475;; Helper constructor to build a load instruction.
2476;;
2477;; Recursion: recursive rule can only match once, since it matches on
2478;; `LoadOP.Flh` and emits `LoadOP.Lh`.
2479(decl rec gen_load (AMode LoadOP MemFlags) Reg)
2480(rule (gen_load amode op flags)
2481  (let ((dst WritableReg (temp_writable_reg (load_op_reg_type op)))
2482      (_ Unit (emit (MInst.Load dst op flags amode))))
2483    dst))
2484(rule 1 (gen_load amode (LoadOP.Flh) flags)
2485  (if-let false (has_zfhmin))
2486  (gen_bitcast (gen_load amode (LoadOP.Lh) flags) $I16 $F16))
2487
2488;; Similar to `gen_load` but marks `Inst` as sunk at the current point.
2489;;
2490;; This is only useful for load op's that perform some additional computation
2491;; such as extending the loaded value.
2492(decl gen_sunk_load (Inst AMode LoadOP MemFlags) Reg)
2493(rule (gen_sunk_load inst amode op flags)
2494  (let ((_ Unit (sink_inst inst)))
2495        (gen_load amode op flags)))
2496
2497
2498;; Helper constructor to build a store instruction.
2499;;
2500;; This helper contains a special-case for zero constants stored to memory to
2501;; directly store the `zero` register to memory. See #7162 for some discussion
2502;; on why this doesn't just fall out.
2503(decl gen_store (AMode MemFlags Value) InstOutput)
2504(rule 2 (gen_store amode flags val @ (value_type $F16))
2505  (if-let false (has_zfhmin))
2506  (rv_store amode (StoreOP.Sh) flags (gen_bitcast val $F16 $I16)))
2507(rule 1 (gen_store amode flags val @ (value_type ty))
2508  (if-let (u64_from_iconst 0) val)
2509  (rv_store amode (store_op ty) flags (zero_reg)))
2510(rule 0 (gen_store amode flags val @ (value_type ty))
2511  (rv_store amode (store_op ty) flags val))
2512
2513;; Emit a raw instruction to store a register into memory.
2514;;
2515;; Note that the `src` operand must have the correct type for the `op`
2516;; specified.
2517(decl rv_store (AMode StoreOP MemFlags Reg) InstOutput)
2518(rule (rv_store amode op flags src)
2519  (side_effect (SideEffectNoResult.Inst (MInst.Store amode op flags src))))
2520
2521
2522
2523
2524(decl valid_atomic_transaction (Type) Type)
2525(extern extractor valid_atomic_transaction valid_atomic_transaction)
2526
2527;;helper function.
2528;;construct an atomic instruction.
2529(decl gen_atomic (AtomicOP Reg Reg AMO) Reg)
2530(rule
2531  (gen_atomic op addr src amo)
2532  (let
2533    ((tmp WritableXReg (temp_writable_xreg))
2534      (_ Unit (emit (MInst.Atomic op tmp addr src amo))))
2535    tmp))
2536
2537;; helper function
2538(decl get_atomic_rmw_op (Type AtomicRmwOp) AtomicOP)
2539(rule
2540  (get_atomic_rmw_op $I32 (AtomicRmwOp.Add))
2541  (AtomicOP.AmoaddW))
2542(rule
2543  (get_atomic_rmw_op $I64 (AtomicRmwOp.Add))
2544  (AtomicOP.AmoaddD))
2545
2546(rule
2547  (get_atomic_rmw_op $I32 (AtomicRmwOp.And))
2548  (AtomicOP.AmoandW))
2549
2550(rule
2551  (get_atomic_rmw_op $I64 (AtomicRmwOp.And))
2552  (AtomicOP.AmoandD))
2553
2554(rule
2555  (get_atomic_rmw_op $I32 (AtomicRmwOp.Or))
2556  (AtomicOP.AmoorW))
2557
2558(rule
2559  (get_atomic_rmw_op $I64 (AtomicRmwOp.Or))
2560  (AtomicOP.AmoorD))
2561
2562(rule
2563  (get_atomic_rmw_op $I32 (AtomicRmwOp.Smax))
2564  (AtomicOP.AmomaxW))
2565
2566(rule
2567  (get_atomic_rmw_op $I64 (AtomicRmwOp.Smax))
2568  (AtomicOP.AmomaxD))
2569
2570(rule
2571  (get_atomic_rmw_op $I32 (AtomicRmwOp.Smin))
2572  (AtomicOP.AmominW))
2573
2574(rule
2575  (get_atomic_rmw_op $I64 (AtomicRmwOp.Smin))
2576  (AtomicOP.AmominD))
2577
2578(rule
2579  (get_atomic_rmw_op $I32 (AtomicRmwOp.Umax))
2580  (AtomicOP.AmomaxuW)
2581)
2582
2583(rule
2584  (get_atomic_rmw_op $I64 (AtomicRmwOp.Umax))
2585  (AtomicOP.AmomaxuD))
2586
2587(rule
2588  (get_atomic_rmw_op $I32 (AtomicRmwOp.Umin))
2589  (AtomicOP.AmominuW))
2590
2591(rule
2592  (get_atomic_rmw_op $I64 (AtomicRmwOp.Umin))
2593  (AtomicOP.AmominuD))
2594
2595(rule
2596  (get_atomic_rmw_op $I32 (AtomicRmwOp.Xchg))
2597  (AtomicOP.AmoswapW))
2598
2599(rule
2600  (get_atomic_rmw_op $I64 (AtomicRmwOp.Xchg))
2601  (AtomicOP.AmoswapD))
2602
2603(rule
2604  (get_atomic_rmw_op $I32 (AtomicRmwOp.Xor))
2605  (AtomicOP.AmoxorW))
2606
2607(rule
2608  (get_atomic_rmw_op $I64 (AtomicRmwOp.Xor))
2609  (AtomicOP.AmoxorD))
2610
2611(decl atomic_amo () AMO)
2612(extern constructor atomic_amo atomic_amo)
2613
2614
2615(decl gen_atomic_load (Reg Type) Reg)
2616(rule
2617  (gen_atomic_load p ty)
2618  (let
2619    ((tmp WritableXReg (temp_writable_xreg))
2620      (_ Unit (emit (MInst.AtomicLoad tmp ty p))))
2621    (writable_reg_to_reg tmp)))
2622
2623;;;
2624(decl gen_atomic_store (Reg Type Reg) InstOutput)
2625(rule
2626  (gen_atomic_store p ty src)
2627  (side_effect (SideEffectNoResult.Inst (MInst.AtomicStore src ty p)))
2628)
2629
2630
2631;; Rounds a FReg by converting the value into an integer and back with a specified
2632;; float rounding mode.
2633(decl float_round_fcvt (Type FRM FReg) FReg)
2634(rule (float_round_fcvt $F32 frm rs) (rv_fcvtsw frm (rv_fcvtws frm rs)))
2635(rule (float_round_fcvt $F64 frm rs) (rv_fcvtdl frm (rv_fcvtld frm rs)))
2636
2637(decl gen_float_round (FRM FReg Type) FReg)
2638(rule 0 (gen_float_round frm rs ty)
2639  (let (
2640        ;; if rs is NaN/+-Infinity/+-Zero or if the exponent is larger than # of bits
2641        ;; in mantissa, the result is the same as src, check for these cases first.
2642        (max FReg (imm ty (float_int_max ty)))
2643        (abs FReg (rv_fabs ty rs))
2644        (exact XReg (rv_flt ty abs max))
2645
2646        ;; Manually round the value using the fcvt instructions
2647        ;; to move the value to an integer register and back.
2648        (fcvt FReg (float_round_fcvt ty frm rs))
2649        ;; Restore the sign bit from the initial value.
2650        (rounded FReg (rv_fsgnj ty fcvt rs))
2651
2652        ;; We want to return a arithmetic nan if the input is a canonical nan.
2653        ;; Convert them by adding 0.0 to the input.
2654        (float_zero FReg (gen_bitcast (zero_reg) (float_int_of_same_size ty) ty))
2655        (corrected_nan FReg (rv_fadd ty (FRM.RNE) rs float_zero)))
2656
2657    ;; Check if the value cannot be rounded exactly and return the source input if so
2658    (gen_select_freg (cmp_eqz exact) corrected_nan rounded)))
2659
2660;; With Zfa we can use the dedicated `fround` instruction.
2661(rule 1 (gen_float_round frm rs ty)
2662  (if-let true (has_zfa))
2663  (rv_fround ty frm rs))
2664
2665
2666
2667(decl gen_stack_addr (StackSlot Offset32) Reg)
2668(extern constructor gen_stack_addr gen_stack_addr)
2669
2670; Recursion: bounded by only matching when one of the inputs is a zero register,
2671; but not both.
2672(decl rec gen_select_xreg (IntegerCompare XReg XReg) XReg)
2673
2674(rule 6 (gen_select_xreg (int_compare_decompose cc x y) x y)
2675  (if-let (IntCC.UnsignedLessThan) (intcc_without_eq cc))
2676  (if-let true (has_zbb))
2677  (rv_minu x y))
2678
2679(rule 6 (gen_select_xreg (int_compare_decompose cc x y) x y)
2680  (if-let (IntCC.SignedLessThan) (intcc_without_eq cc))
2681  (if-let true (has_zbb))
2682  (rv_min x y))
2683
2684(rule 6 (gen_select_xreg (int_compare_decompose cc x y) x y)
2685  (if-let (IntCC.UnsignedGreaterThan) (intcc_without_eq cc))
2686  (if-let true (has_zbb))
2687  (rv_maxu x y))
2688
2689(rule 6 (gen_select_xreg (int_compare_decompose cc x y) x y)
2690  (if-let (IntCC.SignedGreaterThan) (intcc_without_eq cc))
2691  (if-let true (has_zbb))
2692  (rv_max x y))
2693
2694;; Rotate Zero Reg to the right. This allows us to write fewer rules
2695;; below when matching the zero register
2696;;
2697;; Additionally prevent this rule from recursing infinitely by only
2698;; matching when one of the inputs is the zero register, but not both.
2699
2700(rule 5 (gen_select_xreg (int_compare_decompose cc a @ (zero_reg) b @ (non_zero_reg)) x y)
2701  (if-let true (has_zicond))
2702  (gen_select_xreg (int_compare (intcc_swap_args cc) b a) x y))
2703
2704(rule 4 (gen_select_xreg c @ (int_compare_decompose cc a b) x @ (zero_reg) y @ (non_zero_reg))
2705  (if-let true (has_zicond))
2706  (gen_select_xreg (int_compare (intcc_complement cc) a b) y x))
2707
2708(rule 3 (gen_select_xreg (int_compare_decompose (IntCC.Equal) c (zero_reg)) x (zero_reg))
2709  (if-let true (has_zicond))
2710  (rv_czero_nez x c))
2711
2712(rule 3 (gen_select_xreg (int_compare_decompose (IntCC.NotEqual) c (zero_reg)) x (zero_reg))
2713  (if-let true (has_zicond))
2714  (rv_czero_eqz x c))
2715
2716(rule 2 (gen_select_xreg (int_compare_decompose (IntCC.Equal) c (zero_reg)) x y)
2717  (if-let true (has_zicond))
2718  (rv_or
2719    (rv_czero_nez x c)
2720    (rv_czero_eqz y c)))
2721
2722(rule 2 (gen_select_xreg (int_compare_decompose (IntCC.NotEqual) c (zero_reg)) x y)
2723  (if-let true (has_zicond))
2724  (rv_or
2725    (rv_czero_eqz x c)
2726    (rv_czero_nez y c)))
2727
2728;; It is still beneficial to emit the full compare instruction, and then the 3 instruction
2729;; select using zicond, so do that here as a last resort.
2730(rule 1 (gen_select_xreg compare x y)
2731  (if-let true (has_zicond))
2732  (gen_select_xreg (cmp_nez (lower_int_compare compare)) x y))
2733
2734;; In the base case we emit a conditional branch and a few moves.
2735
2736(rule 0 (gen_select_xreg c x y)
2737  (let
2738    ((dst WritableReg (temp_writable_xreg))
2739     (_ Unit (emit (MInst.Select dst c x y))))
2740    (writable_reg_to_reg dst)))
2741
2742
2743(decl gen_select_vreg (IntegerCompare VReg VReg) VReg)
2744(rule (gen_select_vreg c x y)
2745  (let
2746    ((dst WritableReg (temp_writable_vreg))
2747     (_ Unit (emit (MInst.Select dst c (vreg_to_reg x) (vreg_to_reg y)))))
2748    (writable_reg_to_reg dst)))
2749(decl gen_select_freg (IntegerCompare FReg FReg) FReg)
2750(rule (gen_select_freg c x y)
2751  (let
2752    ((dst WritableReg (temp_writable_freg))
2753     (_ Unit (emit (MInst.Select dst c (freg_to_reg x) (freg_to_reg y)))))
2754    (writable_reg_to_reg dst)))
2755(decl gen_select_regs (IntegerCompare ValueRegs ValueRegs) ValueRegs)
2756(rule (gen_select_regs c x y)
2757  (let
2758    ((dst1 WritableReg (temp_writable_xreg))
2759     (dst2 WritableReg (temp_writable_xreg))
2760     (_ Unit (emit (MInst.Select (writable_value_regs dst1 dst2) c x y))))
2761    (value_regs dst1 dst2)))
2762
2763(decl udf (TrapCode) InstOutput)
2764(rule
2765  (udf code)
2766  (side_effect (SideEffectNoResult.Inst (MInst.Udf code))))
2767
2768(decl load_op (Type) LoadOP)
2769(extern constructor load_op load_op)
2770
2771(decl store_op (Type) StoreOP)
2772(extern constructor store_op store_op)
2773
2774
2775;;;; load extern name
2776(decl load_ext_name (ExternalName i64 RelocDistance) Reg)
2777(rule (load_ext_name name offset _dist)
2778  (if-let true (is_pic))
2779  (rv_add (load_ext_name_got name) (imm $I64 (i64_cast_unsigned offset))))
2780(rule 1 (load_ext_name name 0 _dist)
2781  (if-let true (is_pic))
2782  (load_ext_name_got name))
2783(rule (load_ext_name name offset (RelocDistance.Near))
2784  (if-let false (is_pic))
2785  (load_ext_name_near name offset))
2786(rule (load_ext_name name offset (RelocDistance.Far))
2787  (if-let false (is_pic))
2788  (load_ext_name_far name offset))
2789
2790(decl pure is_pic () bool)
2791(extern constructor is_pic is_pic)
2792
2793;; Helper for emitting `MInst.LoadExtNameGot` instructions.
2794(decl load_ext_name_got (BoxExternalName) Reg)
2795(rule (load_ext_name_got extname)
2796      (let ((dst WritableReg (temp_writable_reg $I64))
2797            (_ Unit (emit (MInst.LoadExtNameGot dst extname))))
2798        dst))
2799
2800;; Helper for emitting `MInst.LoadExtNameNear` instructions.
2801(decl load_ext_name_near (BoxExternalName i64) Reg)
2802(rule (load_ext_name_near extname offset)
2803      (let ((dst WritableReg (temp_writable_reg $I64))
2804            (_ Unit (emit (MInst.LoadExtNameNear dst extname offset))))
2805        dst))
2806
2807;; Helper for emitting `MInst.LoadExtNameFar` instructions.
2808(decl load_ext_name_far (BoxExternalName i64) Reg)
2809(rule (load_ext_name_far extname offset)
2810      (let ((dst WritableReg (temp_writable_reg $I64))
2811            (_ Unit (emit (MInst.LoadExtNameFar dst extname offset))))
2812        dst))
2813
2814(decl elf_tls_get_addr (ExternalName) Reg)
2815(rule (elf_tls_get_addr name)
2816      (let ((dst WritableReg (temp_writable_reg $I64))
2817            (_ Unit (emit (MInst.ElfTlsGetAddr dst name))))
2818        dst))
2819
2820;;; some float binary operation
2821;;; 1. need move into x register.
2822;;; 2. do the operation.
2823;;; 3. move back.
2824(decl lower_float_binary (AluOPRRR FReg FReg Type) FReg)
2825(rule
2826  (lower_float_binary op rs1 rs2 ty)
2827  (let ((x_rs1 XReg (move_f_to_x rs1 ty))
2828        (x_rs2 XReg (move_f_to_x rs2 ty))
2829        (tmp XReg (alu_rrr op x_rs1 x_rs2)))
2830    (move_x_to_f tmp ty)))
2831
2832
2833(decl sub_i128 (ValueRegs ValueRegs) ValueRegs)
2834(rule
2835  (sub_i128 x y )
2836  (let (
2837      ;; low part.
2838      (low XReg (rv_sub (value_regs_get x 0) (value_regs_get y 0)))
2839      ;; compute borrow.
2840      (borrow XReg (rv_sltu (value_regs_get x 0) low))
2841      ;;
2842      (high_tmp XReg (rv_sub (value_regs_get x 1) (value_regs_get y 1)))
2843      ;;
2844      (high XReg (rv_sub high_tmp borrow)))
2845    (value_regs low high)))
2846
2847;; Consume a CmpResult, producing a branch on its result.
2848(decl cond_br (IntegerCompare CondBrTarget CondBrTarget) SideEffectNoResult)
2849(rule (cond_br cmp then else)
2850      (SideEffectNoResult.Inst
2851        (MInst.CondBr then else cmp)))
2852
2853;; Helper for emitting the `j` mnemonic, an unconditional jump to label.
2854(decl rv_j (MachLabel) SideEffectNoResult)
2855(rule (rv_j label)
2856  (SideEffectNoResult.Inst (MInst.Jal label)))
2857
2858;; Construct an IntegerCompare value.
2859(decl int_compare (IntCC XReg XReg) IntegerCompare)
2860(extern constructor int_compare int_compare)
2861
2862;; Extract the components of an `IntegerCompare`
2863(decl int_compare_decompose (IntCC XReg XReg) IntegerCompare)
2864(extern extractor infallible int_compare_decompose int_compare_decompose)
2865
2866(decl label_to_br_target (MachLabel) CondBrTarget)
2867(extern constructor label_to_br_target label_to_br_target)
2868(convert MachLabel CondBrTarget label_to_br_target)
2869
2870(decl cmp_eqz (XReg) IntegerCompare)
2871(rule (cmp_eqz r) (int_compare (IntCC.Equal) r (zero_reg)))
2872
2873(decl cmp_nez (XReg) IntegerCompare)
2874(rule (cmp_nez r) (int_compare (IntCC.NotEqual) r (zero_reg)))
2875
2876(decl cmp_eq (XReg XReg) IntegerCompare)
2877(rule (cmp_eq rs1 rs2) (int_compare (IntCC.Equal) rs1 rs2))
2878
2879(decl cmp_ne (XReg XReg) IntegerCompare)
2880(rule (cmp_ne rs1 rs2) (int_compare (IntCC.NotEqual) rs1 rs2))
2881
2882(decl cmp_lt (XReg XReg) IntegerCompare)
2883(rule (cmp_lt rs1 rs2) (int_compare (IntCC.SignedLessThan) rs1 rs2))
2884
2885(decl cmp_ltz (XReg) IntegerCompare)
2886(rule (cmp_ltz rs) (int_compare (IntCC.SignedLessThan) rs (zero_reg)))
2887
2888(decl cmp_gt (XReg XReg) IntegerCompare)
2889(rule (cmp_gt rs1 rs2) (int_compare (IntCC.SignedGreaterThan) rs1 rs2))
2890
2891(decl cmp_ge (XReg XReg) IntegerCompare)
2892(rule (cmp_ge rs1 rs2) (int_compare (IntCC.SignedGreaterThanOrEqual) rs1 rs2))
2893
2894(decl cmp_le (XReg XReg) IntegerCompare)
2895(rule (cmp_le rs1 rs2) (int_compare (IntCC.SignedLessThanOrEqual) rs1 rs2))
2896
2897(decl cmp_gtu (XReg XReg) IntegerCompare)
2898(rule (cmp_gtu rs1 rs2) (int_compare (IntCC.UnsignedGreaterThan) rs1 rs2))
2899
2900(decl cmp_geu (XReg XReg) IntegerCompare)
2901(rule (cmp_geu rs1 rs2) (int_compare (IntCC.UnsignedGreaterThanOrEqual) rs1 rs2))
2902
2903(decl cmp_ltu (XReg XReg) IntegerCompare)
2904(rule (cmp_ltu rs1 rs2) (int_compare (IntCC.UnsignedLessThan) rs1 rs2))
2905
2906(decl cmp_leu (XReg XReg) IntegerCompare)
2907(rule (cmp_leu rs1 rs2) (int_compare (IntCC.UnsignedLessThanOrEqual) rs1 rs2))
2908
2909;; Helper to generate an `IntegerCompare` which represents the "truthy" value of
2910;; the input provided.
2911;;
2912;; This is used in `Select` and `brif` for example to generate conditional
2913;; branches. The returned comparison, when taken, represents that `Value` is
2914;; nonzero. When not taken the input `Value` is zero.
2915(decl is_nonzero_cmp (Value) IntegerCompare)
2916
2917;; Base case - convert to a "truthy" value and compare it against zero.
2918;;
2919;; Note that non-64-bit types need to be extended since the upper bits from
2920;; Cranelift's point of view are undefined. Favor a zero extension for 8-bit
2921;; types because that's a single `andi` instruction, but favor sign-extension
2922;; for 16 and 32-bit types because many RISC-V which operate on the low 32-bits.
2923;; Additionally the base 64-bit ISA has a single instruction for sign-extending
2924;; from 32 to 64-bits which makes that a bit cheaper if used.
2925;; of registers sign-extend the results.
2926(rule 0 (is_nonzero_cmp val @ (value_type (fits_in_64 _)))
2927  (cmp_nez (sext val)))
2928(rule 1 (is_nonzero_cmp val @ (value_type $I8))
2929  (cmp_nez (zext val)))
2930(rule 1 (is_nonzero_cmp val @ (value_type $I128))
2931  (cmp_nez (rv_or (value_regs_get val 0) (value_regs_get val 1))))
2932
2933;; If the input value is itself an `icmp` or `fcmp` we can avoid generating the
2934;; result of the comparison and instead move the comparison directly into the
2935;; `IntegerCompare` that's returned.
2936(rule 2 (is_nonzero_cmp (maybe_uextend (icmp _ cc a b @ (value_type (fits_in_64 _)))))
2937  (icmp_to_int_compare cc a b))
2938(rule 2 (is_nonzero_cmp (maybe_uextend (fcmp _ cc a @ (value_type ty) b)))
2939  (fcmp_to_float_compare cc ty a b))
2940
2941;; Creates an `IntegerCompare` from an `icmp` node's parts. This will extend
2942;; values as necessary to their full register width to perform the
2943;; comparison. The returned `IntegerCompare` is suitable to use in conditional
2944;; branches for example.
2945;;
2946;; Note that this should ideally only be used when the `IntegerCompare` returned
2947;; is fed into a branch. If `IntegerCompare` is materialized this will miss out
2948;; on optimizations to compare against constants using some native instructions.
2949(decl icmp_to_int_compare (IntCC Value Value) IntegerCompare)
2950(rule 0 (icmp_to_int_compare cc a b @ (value_type (fits_in_64 in_ty)))
2951  (int_compare cc (put_value_in_reg_for_icmp cc a) (put_value_in_reg_for_icmp cc b)))
2952(rule 1 (icmp_to_int_compare cc a b @ (value_type $I128))
2953  (cmp_nez (lower_icmp_i128 cc a b)))
2954
2955;; Places a `Value` into a full register width to prepare for a comparison
2956;; using `IntCC`.
2957;;
2958;; This is largely a glorified means of choosing sign-extension or
2959;; zero-extension for the `Value` input.
2960(decl put_value_in_reg_for_icmp (IntCC Value) XReg)
2961
2962;; Base cases, use the `cc` to determine whether to zero or sign extend.
2963(rule 0 (put_value_in_reg_for_icmp cc val)
2964  (zext val))
2965(rule 1 (put_value_in_reg_for_icmp cc val)
2966  (if (signed_cond_code cc))
2967  (sext val))
2968
2969;; For equality and inequality favor sign extension since it's generally
2970;; easier to perform sign extension on RV64 via native instructions. For 8-bit
2971;; types though use zero-extension since that's a single instruction `and`.
2972(rule 2 (put_value_in_reg_for_icmp (IntCC.Equal) val @ (value_type (fits_in_64 _)))
2973  (sext val))
2974(rule 2 (put_value_in_reg_for_icmp (IntCC.NotEqual) val @ (value_type (fits_in_64 _)))
2975  (sext val))
2976(rule 3 (put_value_in_reg_for_icmp (IntCC.Equal) val @ (value_type $I8))
2977  (zext val))
2978(rule 3 (put_value_in_reg_for_icmp (IntCC.NotEqual) val @ (value_type $I8))
2979  (zext val))
2980
2981;; As a special case use `x0` directly if a constant is 0.
2982(rule 4 (put_value_in_reg_for_icmp _ (i64_from_iconst 0))
2983  (zero_reg))
2984
2985
2986(decl partial lower_branch (Inst MachLabelSlice) Unit)
2987(rule (lower_branch (jump _) (single_target label))
2988      (emit_side_effect (rv_j label)))
2989
2990(rule (lower_branch (brif v _ _) (two_targets then else))
2991  (emit_side_effect (cond_br (is_nonzero_cmp v) then else)))
2992
2993(decl lower_br_table (Reg MachLabelSlice) Unit)
2994(extern constructor lower_br_table lower_br_table)
2995
2996(rule (lower_branch (br_table index _) targets)
2997  (lower_br_table index targets))
2998
2999(decl load_ra () Reg)
3000(extern constructor load_ra load_ra)
3001
3002
3003;; Generates a bitcast instruction.
3004;; Args are: src, src_ty, dst_ty
3005;;
3006;; Recursion: only recursive rule matches on vec-to-float, and emits vec-to-int
3007;; and int-to-float bitcasts, so this can only recurse once.
3008(decl rec gen_bitcast (Reg Type Type) Reg)
3009
3010(rule 9 (gen_bitcast r (ty_supported_float_size $F16) (ty_supported_vec _)) (if-let false (has_zvfh)) (rv_vfmv_sf r (vstate_from_type $F32)))
3011(rule 8 (gen_bitcast r (ty_supported_vec ty) (ty_supported_float_size $F16)) (if-let false (has_zvfh)) (gen_bitcast (gen_bitcast r ty $I16) $I16 $F16))
3012(rule 7 (gen_bitcast r (ty_supported_float_min src_ty) (ty_supported_vec _)) (rv_vfmv_sf r src_ty))
3013(rule 6 (gen_bitcast r (ty_supported_vec _) (ty_supported_float_min dst_ty)) (rv_vfmv_fs r dst_ty))
3014
3015(rule 5 (gen_bitcast r (ty_int_ref_scalar_64 src_ty) (ty_supported_vec _)) (rv_vmv_sx r src_ty))
3016(rule 4 (gen_bitcast r (ty_supported_vec _) (ty_int_ref_scalar_64 dst_ty)) (rv_vmv_xs r dst_ty))
3017(rule 3 (gen_bitcast r (ty_supported_float_min $F16) $I16) (rv_fmvxh r))
3018(rule 2 (gen_bitcast r (ty_supported_float_size $F16) $I16) (rv_fmvxw r))
3019(rule 2 (gen_bitcast r (ty_supported_float_size $F32) $I32) (rv_fmvxw r))
3020(rule 2 (gen_bitcast r (ty_supported_float_size $F64) $I64) (rv_fmvxd r))
3021(rule 1 (gen_bitcast r $I16 (ty_supported_float_min $F16)) (rv_fmvhx r))
3022;; Smaller float types are NaN-boxed inside of larger floating point types on RISC-V, meaning the
3023;; smaller float is stored in the lower bits of the larger float and all the other bits are set to 1.
3024;; This is done automatically by the correctly-sized `fmv` instructions but needs to be done manually
3025;; here when using a 32-bit `fmv` for a 16-bit float.
3026(rule 0 (gen_bitcast r $I16 (ty_supported_float_size $F16)) (rv_fmvwx (rv_or r (imm $I32 0xffff0000))))
3027(rule 0 (gen_bitcast r $I32 (ty_supported_float_size $F32)) (rv_fmvwx r))
3028(rule 0 (gen_bitcast r $I64 (ty_supported_float_size $F64)) (rv_fmvdx r))
3029(rule -1 (gen_bitcast r (ty_supported_float_size _) (ty_supported_float_size _)) r)
3030(rule -2 (gen_bitcast r (ty_int_ref_scalar_64 _) (ty_int_ref_scalar_64 _)) r)
3031(rule -3 (gen_bitcast r (ty_supported_vec _) (ty_supported_vec _)) r)
3032
3033(decl move_f_to_x (FReg Type) XReg)
3034(rule (move_f_to_x r ty) (gen_bitcast r ty (float_int_of_same_size ty)))
3035
3036(decl move_x_to_f (XReg Type) FReg)
3037(rule (move_x_to_f r ty) (gen_bitcast r (float_int_of_same_size ty) ty))
3038
3039(decl float_int_of_same_size (Type) Type)
3040(rule (float_int_of_same_size $F16) $I16)
3041(rule (float_int_of_same_size $F32) $I32)
3042(rule (float_int_of_same_size $F64) $I64)
3043
3044
3045(decl gen_brev8 (Reg Type) Reg)
3046(rule 1
3047  (gen_brev8 rs _)
3048  (if-let true (has_zbkb))
3049  (rv_brev8 rs))
3050(rule
3051  (gen_brev8 rs ty)
3052  (if-let false (has_zbkb))
3053  (let
3054    ((tmp WritableXReg (temp_writable_xreg))
3055      (tmp2 WritableXReg (temp_writable_xreg))
3056      (step WritableXReg (temp_writable_xreg))
3057      (rd WritableXReg (temp_writable_xreg))
3058      (_ Unit (emit (MInst.Brev8 rs ty step tmp tmp2 rd))))
3059    (writable_reg_to_reg rd)))
3060
3061;; Negates x
3062;; Equivalent to 0 - x
3063(decl neg (Type ValueRegs) ValueRegs)
3064(rule 1 (neg (fits_in_64 (ty_int ty)) val)
3065  (value_reg
3066    (rv_neg (value_regs_get val 0))))
3067
3068(rule 2 (neg $I128 val)
3069  (sub_i128 (value_regs_zero) val))
3070
3071
3072;; Builds an instruction sequence that traps if the comparison succeeds.
3073(decl gen_trapif (IntCC XReg XReg TrapCode) InstOutput)
3074(rule (gen_trapif cc a b trap_code)
3075  (side_effect (SideEffectNoResult.Inst (MInst.TrapIf a b cc trap_code))))
3076
3077;; Builds an instruction sequence that traps if the input is non-zero.
3078(decl gen_trapnz (XReg TrapCode) InstOutput)
3079(rule (gen_trapnz test trap_code)
3080  (gen_trapif (IntCC.NotEqual) test (zero_reg) trap_code))
3081
3082;; Builds an instruction sequence that traps if the input is zero.
3083(decl gen_trapz (XReg TrapCode) InstOutput)
3084(rule (gen_trapz test trap_code)
3085  (gen_trapif (IntCC.Equal) test (zero_reg) trap_code))
3086
3087;; Converts bool to the corresponding {in,}equality condition
3088(type ZeroCond
3089      (enum
3090       Zero
3091       NonZero))
3092
3093(decl zero_cond_to_cc (ZeroCond) IntCC)
3094(rule (zero_cond_to_cc (ZeroCond.Zero)) (IntCC.Equal))
3095(rule (zero_cond_to_cc (ZeroCond.NonZero)) (IntCC.NotEqual))
3096
3097;; Builds an instruction sequence for wide trapz/nz
3098(decl gen_trapif_val_i128 (ZeroCond ValueRegs TrapCode) InstOutput)
3099(rule (gen_trapif_val_i128 zero_cond value trap_code)
3100  (let ((lo XReg (value_regs_get value 0))
3101        (hi XReg (value_regs_get value 1))
3102        (test XReg (rv_or hi lo)))
3103      (gen_trapif (zero_cond_to_cc zero_cond) test (zero_reg) trap_code)))
3104
3105;;;; Helpers for Emitting Calls ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3106
3107(decl gen_call_info (Sig ExternalName CallArgList CallRetList OptionTryCallInfo bool) BoxCallInfo)
3108(extern constructor gen_call_info gen_call_info)
3109
3110(decl gen_call_ind_info (Sig Reg CallArgList CallRetList OptionTryCallInfo) BoxCallIndInfo)
3111(extern constructor gen_call_ind_info gen_call_ind_info)
3112
3113(decl gen_return_call_info (Sig ExternalName CallArgList) BoxReturnCallInfo)
3114(extern constructor gen_return_call_info gen_return_call_info)
3115
3116(decl gen_return_call_ind_info (Sig Reg CallArgList) BoxReturnCallIndInfo)
3117(extern constructor gen_return_call_ind_info gen_return_call_ind_info)
3118
3119;; Helper for creating `MInst.Call` instructions.
3120(decl call_impl (BoxCallInfo) SideEffectNoResult)
3121(rule (call_impl info)
3122      (SideEffectNoResult.Inst (MInst.Call info)))
3123
3124;; Helper for creating `MInst.CallInd` instructions.
3125(decl call_ind_impl (BoxCallIndInfo) SideEffectNoResult)
3126(rule (call_ind_impl info)
3127      (SideEffectNoResult.Inst (MInst.CallInd info)))
3128
3129;; Helper for creating `MInst.ReturnCall` instructions.
3130(decl return_call_impl (BoxReturnCallInfo) SideEffectNoResult)
3131(rule (return_call_impl info)
3132      (SideEffectNoResult.Inst (MInst.ReturnCall info)))
3133
3134;; Helper for creating `MInst.ReturnCallInd` instructions.
3135(decl return_call_ind_impl (BoxReturnCallIndInfo) SideEffectNoResult)
3136(rule (return_call_ind_impl info)
3137      (SideEffectNoResult.Inst (MInst.ReturnCallInd info)))
3138
3139
3140;;; this is trying to imitate aarch64 `madd` instruction.
3141(decl madd (XReg XReg XReg) XReg)
3142(rule
3143  (madd n m a)
3144  (let
3145    ((t XReg (rv_mul n m)))
3146    (rv_add t a)))
3147
3148;;;; Helpers for bmask ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3149
3150;; Generates either 0 if `Value` is zero or -1 otherwise.
3151(decl gen_bmask (Value) XReg)
3152
3153;; Base cases: use `snez` after a sign extension to ensure that the entire
3154;; register is defined. For i128 we test both the upper and lower half.
3155(rule 0 (gen_bmask val @ (value_type (fits_in_64 _)))
3156  (let ((non_zero XReg (rv_snez (sext val))))
3157    (rv_neg non_zero)))
3158(rule 1 (gen_bmask val @ (value_type $I128))
3159  (let ((non_zero XReg (rv_snez (rv_or (value_regs_get val 0) (value_regs_get val 1)))))
3160    (rv_neg non_zero)))
3161
3162;; If the input value is an `icmp` or an `fcmp` directly then the `snez` can
3163;; be omitted because the result of the icmp or fcmp is a 0 or 1 directly. This
3164;; means we can go straight to the `neg` instruction to produce the final
3165;; result.
3166(rule 2 (gen_bmask val @ (maybe_uextend (icmp _ _ _ _))) (rv_neg val))
3167(rule 2 (gen_bmask val @ (maybe_uextend (fcmp _ _ _ _))) (rv_neg val))
3168
3169(decl lower_bmask (Value Type) ValueRegs)
3170(rule 0 (lower_bmask val (fits_in_64 _))
3171  (value_reg (gen_bmask val)))
3172(rule 1 (lower_bmask val $I128)
3173  (let ((bits XReg (gen_bmask val)))
3174    (value_regs bits bits)))
3175
3176;;;; Helpers for physical registers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3177
3178(decl gen_mov_from_preg (PReg) Reg)
3179
3180(rule
3181  (gen_mov_from_preg rm)
3182  (let ((rd WritableXReg (temp_writable_xreg))
3183        (_ Unit (emit (MInst.MovFromPReg rd rm))))
3184    rd))
3185
3186(decl fp_reg () PReg)
3187(extern constructor fp_reg fp_reg)
3188
3189(decl sp_reg () PReg)
3190(extern constructor sp_reg sp_reg)
3191
3192;; Extractor that matches all registers, except the zero register
3193(decl non_zero_reg () XReg)
3194(extern extractor non_zero_reg is_non_zero_reg)
3195
3196;; Helper for creating the zero register.
3197(decl zero_reg () XReg)
3198(extern constructor zero_reg zero_reg)
3199(extern extractor zero_reg is_zero_reg)
3200
3201(decl value_regs_zero () ValueRegs)
3202(rule (value_regs_zero)
3203  (value_regs (imm $I64 0) (imm $I64 0)))
3204
3205(decl writable_zero_reg () WritableReg)
3206(extern constructor writable_zero_reg writable_zero_reg)
3207
3208
3209;;;; Helpers for floating point comparisons ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3210
3211(type FloatCompare (enum
3212  ;; The comparison succeeded if `r` is one
3213  (One (r XReg))
3214  ;; The comparison succeeded if `r` is zero
3215  (Zero (r XReg))
3216))
3217
3218(decl float_compare_invert (FloatCompare) FloatCompare)
3219(rule (float_compare_invert (FloatCompare.One r)) (FloatCompare.Zero r))
3220(rule (float_compare_invert (FloatCompare.Zero r)) (FloatCompare.One r))
3221
3222(decl float_to_int_compare (FloatCompare) IntegerCompare)
3223(rule (float_to_int_compare (FloatCompare.One r)) (cmp_nez r))
3224(rule (float_to_int_compare (FloatCompare.Zero r)) (cmp_eqz r))
3225(convert FloatCompare IntegerCompare float_to_int_compare)
3226
3227;; Compare two floating point numbers and return a zero/non-zero result.
3228;;
3229;; Recursion: at most once to convert unordered comparisons into ordered comparisons.
3230(decl rec fcmp_to_float_compare (FloatCC Type FReg FReg) FloatCompare)
3231
3232;; Direct codegen for unordered comparisons is not that efficient, so invert
3233;; the comparison to get an ordered comparison and generate that. Then invert
3234;; the result to produce the final fcmp result.
3235(rule 0 (fcmp_to_float_compare cc ty a b)
3236  (if-let true (floatcc_unordered cc))
3237  (float_compare_invert (fcmp_to_float_compare (floatcc_complement cc) ty a b)))
3238
3239;; a is not nan && b is not nan
3240(rule 1 (fcmp_to_float_compare (FloatCC.Ordered) ty a b)
3241  (FloatCompare.One (rv_and (is_not_nan ty a) (is_not_nan ty b))))
3242
3243(decl is_not_nan (Type FReg) XReg)
3244(rule (is_not_nan ty a) (rv_feq ty a a))
3245
3246;; a == b
3247(rule 1 (fcmp_to_float_compare (FloatCC.Equal) ty a b)
3248  (FloatCompare.One (rv_feq ty a b)))
3249
3250;; a != b
3251;; == !(a == b)
3252(rule 1 (fcmp_to_float_compare (FloatCC.NotEqual) ty a b)
3253  (FloatCompare.Zero (rv_feq ty a b)))
3254
3255;; a < b || a > b
3256(rule 1 (fcmp_to_float_compare (FloatCC.OrderedNotEqual) ty a b)
3257  (FloatCompare.One (rv_or (rv_flt ty a b) (rv_fgt ty a b))))
3258
3259;; a < b
3260(rule 1 (fcmp_to_float_compare (FloatCC.LessThan) ty a b)
3261  (FloatCompare.One (rv_flt ty a b)))
3262
3263;; a <= b
3264(rule 1 (fcmp_to_float_compare (FloatCC.LessThanOrEqual) ty a b)
3265  (FloatCompare.One (rv_fle ty a b)))
3266
3267;; a > b
3268(rule 1 (fcmp_to_float_compare (FloatCC.GreaterThan) ty a b)
3269  (FloatCompare.One (rv_fgt ty a b)))
3270
3271;; a >= b
3272(rule 1 (fcmp_to_float_compare (FloatCC.GreaterThanOrEqual) ty a b)
3273  (FloatCompare.One (rv_fge ty a b)))
3274
3275
3276;; Helper for creating an `LabelAddress` instruction.
3277(decl rv64_label_address (MachLabel) Reg)
3278(rule (rv64_label_address label)
3279      (let ((dst WritableReg (temp_writable_reg $I64))
3280            (_ Unit (emit (MInst.LabelAddress dst label))))
3281        dst))
3282
3283;; Helper for creating a `SequencePoint` instruction.
3284(decl rv64_sequence_point () SideEffectNoResult)
3285(rule (rv64_sequence_point)
3286      (SideEffectNoResult.Inst (MInst.SequencePoint)))
3287