Files
wasmtime/cranelift/codegen/src/isa/riscv64/inst.isle
Afonso Bordado 60e4a00413 riscv64: Initial SIMD Vector Implementation (#6240)
* riscv64: Remove unused code

* riscv64: Add vector types

* riscv64: Initial Vector ABI Load/Stores

* riscv64: Vector Loads/Stores

* riscv64: Fix `vsetvli` encoding error

* riscv64: Add SIMD `iadd` runtests

* riscv64: Rename `VecSew`

The SEW name is correct, but only for VType. We also use this type
in loads/stores as the Efective Element Width, so the name isn't
quite correct in that case.

* ci: Add V extension to RISC-V QEMU

* riscv64: Misc Cleanups

* riscv64: Check V extension in `load`/`store` for SIMD

* riscv64: Fix `sumop` doc comment

* cranelift: Fix comment typo

* riscv64: Add convert for VType and VecElementWidth

* riscv64: Remove VecElementWidth converter
2023-04-20 21:54:43 +00:00

2745 lines
74 KiB
Common Lisp
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;; Instruction formats.
(type MInst
(enum
;; A no-op of zero size.
(Nop0)
(Nop4)
;; load immediate
(Lui
(rd WritableReg)
(imm Imm20))
(LoadConst32
(rd WritableReg)
(imm u32))
(LoadConst64
(rd WritableReg)
(imm u64))
(Auipc
(rd WritableReg)
(imm Imm20))
;; An ALU operation with one register sources and a register destination.
(FpuRR
(alu_op FpuOPRR)
(frm OptionFloatRoundingMode)
(rd WritableReg)
(rs Reg))
;; An ALU operation with two register sources and a register destination.
(AluRRR
(alu_op AluOPRRR)
(rd WritableReg)
(rs1 Reg)
(rs2 Reg))
;; An ALU operation with two register sources and a register destination.
(FpuRRR
(alu_op FpuOPRRR)
(frm OptionFloatRoundingMode)
(rd WritableReg)
(rs1 Reg)
(rs2 Reg))
;; An ALU operation with three register sources and a register destination.
(FpuRRRR
(alu_op FpuOPRRRR)
(frm OptionFloatRoundingMode)
(rd WritableReg)
(rs1 Reg)
(rs2 Reg)
(rs3 Reg))
;; An ALU operation with a register source and an immediate-12 source, and a register
;; destination.
(AluRRImm12
(alu_op AluOPRRI)
(rd WritableReg)
(rs Reg)
(imm12 Imm12))
;; An load
(Load
(rd WritableReg)
(op LoadOP)
(flags MemFlags)
(from AMode))
;; An Store
(Store
(to AMode)
(op StoreOP)
(flags MemFlags)
(src Reg))
;; A pseudo-instruction that captures register arguments in vregs.
(Args
(args VecArgPair))
(Ret (rets VecRetPair))
(Extend
(rd WritableReg)
(rn Reg)
(signed bool)
(from_bits u8)
(to_bits u8))
(AjustSp
(amount i64))
(Call
(info BoxCallInfo))
;; A machine indirect-call instruction.
(CallInd
(info BoxCallIndInfo))
(TrapIf
(test Reg)
(trap_code TrapCode))
;; use a simple compare to decide to cause trap or not.
(TrapIfC
(rs1 Reg)
(rs2 Reg)
(cc IntCC)
(trap_code TrapCode))
(Jal
;; (rd WritableReg) don't use
(dest BranchTarget))
(CondBr
(taken BranchTarget)
(not_taken BranchTarget)
(kind IntegerCompare))
;; Load an inline symbol reference.
(LoadExtName
(rd WritableReg)
(name BoxExternalName)
(offset i64))
;; Load address referenced by `mem` into `rd`.
(LoadAddr
(rd WritableReg)
(mem AMode))
;; Marker, no-op in generated code: SP "virtual offset" is adjusted. This
;; controls how AMode::NominalSPOffset args are lowered.
(VirtualSPOffsetAdj
(amount i64))
;; A MOV instruction. These are encoded as OrR's (AluRRR form) but we
;; keep them separate at the `Inst` level for better pretty-printing
;; and faster `is_move()` logic.
(Mov
(rd WritableReg)
(rm Reg)
(ty Type))
;; A MOV instruction, but where the source register is a non-allocatable
;; PReg. It's important that the register be non-allocatable, as regalloc2
;; will not see it as used.
(MovFromPReg
(rd WritableReg)
(rm PReg))
(Fence
(pred FenceReq)
(succ FenceReq))
(FenceI)
(ECall)
(EBreak)
;; An instruction guaranteed to always be undefined and to trigger an illegal instruction at
;; runtime.
(Udf
(trap_code TrapCode))
;; a jump and link register operation
(Jalr
;;Plain unconditional jumps (assembler pseudo-op J) are encoded as a JAL with rd=x0.
(rd WritableReg)
(base Reg)
(offset Imm12))
;; atomic operations.
(Atomic
(op AtomicOP)
(rd WritableReg)
(addr Reg)
(src Reg)
(amo AMO))
;; an atomic store
(AtomicStore
(src Reg)
(ty Type)
(p Reg))
;; an atomic load.
(AtomicLoad
(rd WritableReg)
(ty Type)
(p Reg))
;; an atomic nand need using loop to implement.
(AtomicRmwLoop
(offset Reg)
(op AtomicRmwOp)
(dst WritableReg)
(ty Type)
(p Reg)
(x Reg)
(t0 WritableReg))
;; select x or y base on condition
(Select
(dst VecWritableReg)
(ty Type)
(condition Reg)
(x ValueRegs)
(y ValueRegs))
(BrTable
(index Reg)
(tmp1 WritableReg)
(tmp2 WritableReg)
(targets VecBranchTarget))
;; atomic compare and set operation
(AtomicCas
(offset Reg)
(t0 WritableReg)
(dst WritableReg)
(e Reg)
(addr Reg)
(v Reg)
(ty Type))
;; select x or y base on op_code
(IntSelect
(op IntSelectOP)
(dst VecWritableReg)
(x ValueRegs)
(y ValueRegs)
(ty Type))
;; risc-v csr operations.
(Csr
(csr_op CsrOP)
(rd WritableReg)
(rs OptionReg)
(imm OptionUimm5)
(csr CsrAddress))
;; an integer compare.
(Icmp
(cc IntCC)
(rd WritableReg)
(a ValueRegs)
(b ValueRegs)
(ty Type))
;; select a reg base on condition.
;; very useful because in lowering stage we can not have condition branch.
(SelectReg
(rd WritableReg)
(rs1 Reg)
(rs2 Reg)
(condition IntegerCompare))
;;
(FcvtToInt
(is_sat bool)
(rd WritableReg)
(tmp WritableReg) ;; a float register to load bounds.
(rs Reg)
(is_signed bool)
(in_type Type)
(out_type Type))
(RawData (data VecU8))
;; An unwind pseudo-instruction.
(Unwind
(inst UnwindInst))
;; A dummy use, useful to keep a value alive.
(DummyUse
(reg Reg))
;;;
(FloatRound
(op FloatRoundOP)
(rd WritableReg)
(int_tmp WritableReg)
(f_tmp WritableReg)
(rs Reg)
(ty Type))
;;;; FMax
(FloatSelect
(op FloatSelectOP)
(rd WritableReg)
;; a integer register
(tmp WritableReg)
(rs1 Reg)
(rs2 Reg)
(ty Type))
(FloatSelectPseudo
(op FloatSelectOP)
(rd WritableReg)
;; a integer register
(tmp WritableReg)
(rs1 Reg)
(rs2 Reg)
(ty Type))
;; popcnt if target doesn't support extension B
;; use iteration to implement.
(Popcnt
(sum WritableReg)
(step WritableReg)
(tmp WritableReg)
(rs Reg)
(ty Type))
;;; counting leading or trailing zeros.
(Cltz
;; leading or trailing.
(leading bool)
(sum WritableReg)
(step WritableReg)
(tmp WritableReg)
(rs Reg)
(ty Type))
;; Byte-reverse register
(Rev8
(rs Reg)
(step WritableReg)
(tmp WritableReg)
(rd WritableReg))
;;
(Brev8
(rs Reg)
(ty Type)
(step WritableReg)
(tmp WritableReg)
(tmp2 WritableReg)
(rd WritableReg))
(StackProbeLoop
(guard_size u32)
(probe_count u32)
(tmp WritableReg))
(VecAluRRR
(op VecAluOpRRR)
(vd WritableReg)
(vs1 Reg)
(vs2 Reg)
(vstate VState))
(VecSetState
(rd WritableReg)
(vstate VState))
(VecLoad
(eew VecElementWidth)
(to WritableReg)
(from VecAMode)
(flags MemFlags)
(vstate VState))
(VecStore
(eew VecElementWidth)
(to VecAMode)
(from Reg)
(flags MemFlags)
(vstate VState))
))
(type FloatSelectOP (enum
(Max)
(Min)
))
(type FloatRoundOP (enum
(Nearest)
(Ceil)
(Floor)
(Trunc)
))
(type CsrOP (enum
(Csrrw)
(Csrrs)
(Csrrc)
(Csrrwi)
(Csrrsi)
(Csrrci)
))
(type IntSelectOP (enum
(Smax)
(Umax)
(Smin)
(Umin)
))
(type AtomicOP (enum
(LrW)
(ScW)
(AmoswapW)
(AmoaddW)
(AmoxorW)
(AmoandW)
(AmoorW)
(AmominW)
(AmomaxW)
(AmominuW)
(AmomaxuW)
(LrD)
(ScD)
(AmoswapD)
(AmoaddD)
(AmoxorD)
(AmoandD)
(AmoorD)
(AmominD)
(AmomaxD)
(AmominuD)
(AmomaxuD)
))
(type FpuOPRRRR (enum
;; float32
(FmaddS)
(FmsubS)
(FnmsubS)
(FnmaddS)
;; float64
(FmaddD)
(FmsubD)
(FnmsubD)
(FnmaddD)
))
(type FClassResult (enum
;;0 rs1 is −∞.
(NegInfinite)
;; 1 rs1 is a negative normal number.
(NegNormal)
;; 2 rs1 is a negative subnormal number.
(NegSubNormal)
;; 3 rs1 is 0.
(NegZero)
;; 4 rs1 is +0.
(PosZero)
;; 5 rs1 is a positive subnormal number.
(PosSubNormal)
;; 6 rs1 is a positive normal number.
(PosNormal)
;; 7 rs1 is +∞.
(PosInfinite)
;; 8 rs1 is a signaling NaN.
(SNaN)
;; 9 rs1 is a quiet NaN.
(QNaN)
))
(type FpuOPRR (enum
;; RV32F Standard Extension
(FsqrtS)
(FcvtWS)
(FcvtWuS)
(FmvXW)
(FclassS)
(FcvtSw)
(FcvtSwU)
(FmvWX)
;; RV64F Standard Extension (in addition to RV32F)
(FcvtLS)
(FcvtLuS)
(FcvtSL)
(FcvtSLU)
;; RV64D Standard Extension (in addition to RV32D)
(FcvtLD)
(FcvtLuD)
(FmvXD)
(FcvtDL)
(FcvtDLu)
(FmvDX)
;; RV32D Standard Extension
(FsqrtD)
(FcvtSD)
(FcvtDS)
(FclassD)
(FcvtWD)
(FcvtWuD)
(FcvtDW)
(FcvtDWU)
;; bitmapip
))
(type LoadOP (enum
(Lb)
(Lh)
(Lw)
(Lbu)
(Lhu)
(Lwu)
(Ld)
(Flw)
(Fld)
))
(type StoreOP (enum
(Sb)
(Sh)
(Sw)
(Sd)
(Fsw)
(Fsd)
))
(type AluOPRRR (enum
;; base set
(Add)
(Sub)
(Sll)
(Slt)
(SltU)
(Sgt)
(Sgtu)
(Xor)
(Srl)
(Sra)
(Or)
(And)
;; RV64I Base Instruction Set (in addition to RV32I)
(Addw)
(Subw)
(Sllw)
(Srlw)
(Sraw)
;;RV32M Standard Extension
(Mul)
(Mulh)
(Mulhsu)
(Mulhu)
(Div)
(DivU)
(Rem)
(RemU)
;; RV64M Standard Extension (in addition to RV32M)
(Mulw)
(Divw)
(Divuw)
(Remw)
(Remuw)
;; Zba: Address Generation Instructions
(Adduw)
(Sh1add)
(Sh1adduw)
(Sh2add)
(Sh2adduw)
(Sh3add)
(Sh3adduw)
;; Zbb: Bit Manipulation Instructions
(Andn)
(Orn)
(Xnor)
(Max)
(Maxu)
(Min)
(Minu)
(Rol)
(Rolw)
(Ror)
(Rorw)
;; Zbs: Single-bit instructions
(Bclr)
(Bext)
(Binv)
(Bset)
;; Zbc: Carry-less multiplication
(Clmul)
(Clmulh)
(Clmulr)
;; Zbkb: Bit-manipulation for Cryptography
(Pack)
(Packw)
(Packh)
))
(type FpuOPRRR (enum
;; RV32F Standard Extension
(FaddS)
(FsubS)
(FmulS)
(FdivS)
(FsgnjS)
(FsgnjnS)
(FsgnjxS)
(FminS)
(FmaxS)
(FeqS)
(FltS)
(FleS)
;; RV32D Standard Extension
(FaddD)
(FsubD)
(FmulD)
(FdivD)
(FsgnjD)
(FsgnjnD)
(FsgnjxD)
(FminD)
(FmaxD)
(FeqD)
(FltD)
(FleD)
))
(type AluOPRRI (enum
;; Base ISA
(Addi)
(Slti)
(SltiU)
(Xori)
(Ori)
(Andi)
(Slli)
(Srli)
(Srai)
(Addiw)
(Slliw)
(SrliW)
(Sraiw)
;; Zba: Address Generation Instructions
(SlliUw)
;; Zbb: Bit Manipulation Instructions
(Clz)
(Clzw)
(Ctz)
(Ctzw)
(Cpop)
(Cpopw)
(Sextb)
(Sexth)
(Zexth)
(Rori)
(Roriw)
(Rev8)
(Brev8)
(Orcb)
;; Zbs: Single-bit instructions
(Bclri)
(Bexti)
(Binvi)
(Bseti)
))
(type FRM (enum
;; Round to Nearest, ties to Even
(RNE)
;; Round towards Zero
(RTZ)
;; Round Down (towards −∞)
(RDN)
;; Round Up (towards +∞)
(RUP)
;; Round to Nearest, ties to Max Magnitude
(RMM)
;; In instructions rm field, selects dynamic rounding mode;
;;In Rounding Mode register, Invalid.
(Fcsr)
))
(type FFlagsException (enum
;; Invalid Operation
(NV)
;; Divide by Zero
(DZ)
;; Overflow
(OF)
;; Underflow
(UF)
;; Inexact
(NX)
))
;;;; input output read write
;;;; SI SO SR SW
;;;; PI PO PR PW
;;;; lowest four bit are used.
(type FenceReq (primitive u8))
(type FenceFm (enum
(None)
(Tso)
))
(type VecBranchTarget (primitive VecBranchTarget))
(type BoxCallInfo (primitive BoxCallInfo))
(type BoxCallIndInfo (primitive BoxCallIndInfo))
(type IntegerCompare (primitive IntegerCompare))
(type AMode (primitive AMode))
(type OptionReg (primitive OptionReg))
(type OptionImm12 (primitive OptionImm12))
(type OptionUimm5 (primitive OptionUimm5))
(type Imm12 (primitive Imm12))
(type UImm5 (primitive UImm5))
(type Imm20 (primitive Imm20))
(type Imm3 (primitive Imm3))
(type BranchTarget (primitive BranchTarget))
(type CsrAddress (primitive CsrAddress))
(type OptionFloatRoundingMode (primitive OptionFloatRoundingMode))
(type VecU8 (primitive VecU8))
(type AMO (primitive AMO))
(type VecMachLabel extern (enum))
;; Converters
(convert u8 i32 u8_as_i32)
(decl u8_as_i32 (u8) i32)
(extern constructor u8_as_i32 u8_as_i32)
(decl convert_valueregs_reg (ValueRegs) Reg)
(rule (convert_valueregs_reg x)
(value_regs_get x 0))
(convert ValueRegs Reg convert_valueregs_reg)
;; ISA Extension helpers
(decl pure has_v () bool)
(extern constructor has_v has_v)
(decl pure has_zbkb () bool)
(extern constructor has_zbkb has_zbkb)
(decl pure has_zba () bool)
(extern constructor has_zba has_zba)
(decl pure has_zbb () bool)
(extern constructor has_zbb has_zbb)
(decl pure has_zbc () bool)
(extern constructor has_zbc has_zbc)
(decl pure has_zbs () bool)
(extern constructor has_zbs has_zbs)
(decl gen_float_round (FloatRoundOP Reg Type) Reg)
(rule
(gen_float_round op rs ty)
(let
((rd WritableReg (temp_writable_reg ty))
(tmp WritableReg (temp_writable_reg $I64))
(tmp2 WritableReg (temp_writable_reg $F64))
(_ Unit (emit (MInst.FloatRound op rd tmp tmp2 rs ty))))
(writable_reg_to_reg rd)))
(decl gen_float_select_pseudo (FloatSelectOP Reg Reg Type) Reg)
(rule
(gen_float_select_pseudo op x y ty)
(let
((rd WritableReg (temp_writable_reg ty))
(tmp WritableReg (temp_writable_reg $I64))
(_ Unit (emit (MInst.FloatSelectPseudo op rd tmp x y ty))))
(writable_reg_to_reg rd)))
(decl gen_float_select (FloatSelectOP Reg Reg Type) Reg)
(rule
(gen_float_select op x y ty)
(let
((rd WritableReg (temp_writable_reg ty))
(tmp WritableReg (temp_writable_reg $I64))
(_ Unit (emit (MInst.FloatSelect op rd tmp x y ty))))
(writable_reg_to_reg rd)))
;;;; Instruction Helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; RV32I Base Integer Instruction Set
;; Helper for emitting the `add` instruction.
;; rd ← rs1 + rs2
(decl rv_add (Reg Reg) Reg)
(rule (rv_add rs1 rs2)
(alu_rrr (AluOPRRR.Add) rs1 rs2))
;; Helper for emitting the `addi` ("Add Immediate") instruction.
;; rd ← rs1 + sext(imm)
(decl rv_addi (Reg Imm12) Reg)
(rule (rv_addi rs1 imm)
(alu_rr_imm12 (AluOPRRI.Addi) rs1 imm))
;; Helper for emitting the `sub` instruction.
;; rd ← rs1 - rs2
(decl rv_sub (Reg Reg) Reg)
(rule (rv_sub rs1 rs2)
(alu_rrr (AluOPRRR.Sub) rs1 rs2))
;; Helper for emitting the `neg` instruction.
;; This instruction is a mnemonic for `sub rd, zero, rs1`.
(decl rv_neg (Reg) Reg)
(rule (rv_neg rs1)
(alu_rrr (AluOPRRR.Sub) (zero_reg) rs1))
;; Helper for emitting the `sll` ("Shift Left Logical") instruction.
;; rd ← rs1 << rs2
(decl rv_sll (Reg Reg) Reg)
(rule (rv_sll rs1 rs2)
(alu_rrr (AluOPRRR.Sll) rs1 rs2))
;; Helper for emitting the `slli` ("Shift Left Logical Immediate") instruction.
;; rd ← rs1 << uext(imm)
(decl rv_slli (Reg Imm12) Reg)
(rule (rv_slli rs1 imm)
(alu_rr_imm12 (AluOPRRI.Slli) rs1 imm))
;; Helper for emitting the `srl` ("Shift Right Logical") instruction.
;; rd ← rs1 >> rs2
(decl rv_srl (Reg Reg) Reg)
(rule (rv_srl rs1 rs2)
(alu_rrr (AluOPRRR.Srl) rs1 rs2))
;; Helper for emitting the `srli` ("Shift Right Logical Immediate") instruction.
;; rd ← rs1 >> uext(imm)
(decl rv_srli (Reg Imm12) Reg)
(rule (rv_srli rs1 imm)
(alu_rr_imm12 (AluOPRRI.Srli) rs1 imm))
;; Helper for emitting the `sra` ("Shift Right Arithmetic") instruction.
;; rd ← rs1 >> rs2
(decl rv_sra (Reg Reg) Reg)
(rule (rv_sra rs1 rs2)
(alu_rrr (AluOPRRR.Sra) rs1 rs2))
;; Helper for emitting the `srai` ("Shift Right Arithmetic Immediate") instruction.
;; rd ← rs1 >> uext(imm)
(decl rv_srai (Reg Imm12) Reg)
(rule (rv_srai rs1 imm)
(alu_rr_imm12 (AluOPRRI.Srai) rs1 imm))
;; Helper for emitting the `or` instruction.
;; rd ← rs1 rs2
(decl rv_or (Reg Reg) Reg)
(rule (rv_or rs1 rs2)
(alu_rrr (AluOPRRR.Or) rs1 rs2))
;; Helper for emitting the `ori` ("Or Immediate") instruction.
;; rd ← rs1 uext(imm)
(decl rv_ori (Reg Imm12) Reg)
(rule (rv_ori rs1 imm)
(alu_rr_imm12 (AluOPRRI.Ori) rs1 imm))
;; Helper for emitting the `xor` instruction.
;; rd ← rs1 ⊕ rs2
(decl rv_xor (Reg Reg) Reg)
(rule (rv_xor rs1 rs2)
(alu_rrr (AluOPRRR.Xor) rs1 rs2))
;; Helper for emitting the `xori` ("Exlusive Or Immediate") instruction.
;; rd ← rs1 ⊕ uext(imm)
(decl rv_xori (Reg Imm12) Reg)
(rule (rv_xori rs1 imm)
(alu_rr_imm12 (AluOPRRI.Xori) rs1 imm))
;; Helper for emitting the `not` instruction.
;; This instruction is a mnemonic for `xori rd, rs1, -1`.
(decl rv_not (Reg) Reg)
(rule (rv_not rs1)
(rv_xori rs1 (imm12_const -1)))
;; Helper for emitting the `and` instruction.
;; rd ← rs1 ∧ rs2
(decl rv_and (Reg Reg) Reg)
(rule (rv_and rs1 rs2)
(alu_rrr (AluOPRRR.And) rs1 rs2))
;; Helper for emitting the `andi` ("And Immediate") instruction.
;; rd ← rs1 ∧ uext(imm)
(decl rv_andi (Reg Imm12) Reg)
(rule (rv_andi rs1 imm)
(alu_rr_imm12 (AluOPRRI.Andi) rs1 imm))
;; Helper for emitting the `sltu` ("Set Less Than Unsigned") instruction.
;; rd ← rs1 < rs2
(decl rv_sltu (Reg Reg) Reg)
(rule (rv_sltu rs1 rs2)
(alu_rrr (AluOPRRR.SltU) rs1 rs2))
;; Helper for emitting the `snez` instruction.
;; This instruction is a mnemonic for `sltu rd, zero, rs`.
(decl rv_snez (Reg) Reg)
(rule (rv_snez rs1)
(rv_sltu (zero_reg) rs1))
;; Helper for emiting the `sltiu` ("Set Less Than Immediate Unsigned") instruction.
;; rd ← rs1 < imm
(decl rv_sltiu (Reg Imm12) Reg)
(rule (rv_sltiu rs1 imm)
(alu_rr_imm12 (AluOPRRI.SltiU) rs1 imm))
;; Helper for emitting the `seqz` instruction.
;; This instruction is a mnemonic for `sltiu rd, rs, 1`.
(decl rv_seqz (Reg) Reg)
(rule (rv_seqz rs1)
(rv_sltiu rs1 (imm12_const 1)))
;; RV64I Base Integer Instruction Set
;; Unlike RV32I instructions these are only present in the 64bit ISA
;; Helper for emitting the `addw` ("Add Word") instruction.
;; rd ← sext32(rs1) + sext32(rs2)
(decl rv_addw (Reg Reg) Reg)
(rule (rv_addw rs1 rs2)
(alu_rrr (AluOPRRR.Addw) rs1 rs2))
;; Helper for emitting the `addiw` ("Add Word Immediate") instruction.
;; rd ← sext32(rs1) + imm
(decl rv_addiw (Reg Imm12) Reg)
(rule (rv_addiw rs1 imm)
(alu_rr_imm12 (AluOPRRI.Addiw) rs1 imm))
;; Helper for emitting the `sext.w` ("Sign Extend Word") instruction.
;; This instruction is a mnemonic for `addiw rd, rs, zero`.
(decl rv_sextw (Reg) Reg)
(rule (rv_sextw rs1)
(rv_addiw rs1 (imm12_const 0)))
;; Helper for emitting the `subw` ("Subtract Word") instruction.
;; rd ← sext32(rs1) - sext32(rs2)
(decl rv_subw (Reg Reg) Reg)
(rule (rv_subw rs1 rs2)
(alu_rrr (AluOPRRR.Subw) rs1 rs2))
;; Helper for emitting the `sllw` ("Shift Left Logical Word") instruction.
;; rd ← sext32(uext32(rs1) << rs2)
(decl rv_sllw (Reg Reg) Reg)
(rule (rv_sllw rs1 rs2)
(alu_rrr (AluOPRRR.Sllw) rs1 rs2))
;; Helper for emitting the `slliw` ("Shift Left Logical Immediate Word") instruction.
;; rd ← sext32(uext32(rs1) << imm)
(decl rv_slliw (Reg Imm12) Reg)
(rule (rv_slliw rs1 imm)
(alu_rr_imm12 (AluOPRRI.Slliw) rs1 imm))
;; Helper for emitting the `srlw` ("Shift Right Logical Word") instruction.
;; rd ← sext32(uext32(rs1) >> rs2)
(decl rv_srlw (Reg Reg) Reg)
(rule (rv_srlw rs1 rs2)
(alu_rrr (AluOPRRR.Srlw) rs1 rs2))
;; Helper for emitting the `srliw` ("Shift Right Logical Immediate Word") instruction.
;; rd ← sext32(uext32(rs1) >> imm)
(decl rv_srliw (Reg Imm12) Reg)
(rule (rv_srliw rs1 imm)
(alu_rr_imm12 (AluOPRRI.SrliW) rs1 imm))
;; Helper for emitting the `sraw` ("Shift Right Arithmetic Word") instruction.
;; rd ← sext32(rs1 >> rs2)
(decl rv_sraw (Reg Reg) Reg)
(rule (rv_sraw rs1 rs2)
(alu_rrr (AluOPRRR.Sraw) rs1 rs2))
;; Helper for emitting the `sraiw` ("Shift Right Arithmetic Immediate Word") instruction.
;; rd ← sext32(rs1 >> imm)
(decl rv_sraiw (Reg Imm12) Reg)
(rule (rv_sraiw rs1 imm)
(alu_rr_imm12 (AluOPRRI.Sraiw) rs1 imm))
;; RV32M Extension
;; TODO: Enable these instructions only when we have the M extension
;; Helper for emitting the `mul` instruction.
;; rd ← rs1 × rs2
(decl rv_mul (Reg Reg) Reg)
(rule (rv_mul rs1 rs2)
(alu_rrr (AluOPRRR.Mul) rs1 rs2))
;; Helper for emitting the `mulh` ("Multiply High Signed Signed") instruction.
;; rd ← (sext(rs1) × sext(rs2)) » xlen
(decl rv_mulh (Reg Reg) Reg)
(rule (rv_mulh rs1 rs2)
(alu_rrr (AluOPRRR.Mulh) rs1 rs2))
;; Helper for emitting the `mulhu` ("Multiply High Unsigned Unsigned") instruction.
;; rd ← (uext(rs1) × uext(rs2)) » xlen
(decl rv_mulhu (Reg Reg) Reg)
(rule (rv_mulhu rs1 rs2)
(alu_rrr (AluOPRRR.Mulhu) rs1 rs2))
;; Helper for emitting the `div` instruction.
;; rd ← rs1 ÷ rs2
(decl rv_div (Reg Reg) Reg)
(rule (rv_div rs1 rs2)
(alu_rrr (AluOPRRR.Div) rs1 rs2))
;; Helper for emitting the `divu` ("Divide Unsigned") instruction.
;; rd ← rs1 ÷ rs2
(decl rv_divu (Reg Reg) Reg)
(rule (rv_divu rs1 rs2)
(alu_rrr (AluOPRRR.DivU) rs1 rs2))
;; Helper for emitting the `rem` instruction.
;; rd ← rs1 mod rs2
(decl rv_rem (Reg Reg) Reg)
(rule (rv_rem rs1 rs2)
(alu_rrr (AluOPRRR.Rem) rs1 rs2))
;; Helper for emitting the `remu` ("Remainder Unsigned") instruction.
;; rd ← rs1 mod rs2
(decl rv_remu (Reg Reg) Reg)
(rule (rv_remu rs1 rs2)
(alu_rrr (AluOPRRR.RemU) rs1 rs2))
;; RV64M Extension
;; TODO: Enable these instructions only when we have the M extension
;; Helper for emitting the `mulw` ("Multiply Word") instruction.
;; rd ← uext32(rs1) × uext32(rs2)
(decl rv_mulw (Reg Reg) Reg)
(rule (rv_mulw rs1 rs2)
(alu_rrr (AluOPRRR.Mulw) rs1 rs2))
;; Helper for emitting the `divw` ("Divide Word") instruction.
;; rd ← sext32(rs1) ÷ sext32(rs2)
(decl rv_divw (Reg Reg) Reg)
(rule (rv_divw rs1 rs2)
(alu_rrr (AluOPRRR.Divw) rs1 rs2))
;; Helper for emitting the `divuw` ("Divide Unsigned Word") instruction.
;; rd ← uext32(rs1) ÷ uext32(rs2)
(decl rv_divuw (Reg Reg) Reg)
(rule (rv_divuw rs1 rs2)
(alu_rrr (AluOPRRR.Divuw) rs1 rs2))
;; Helper for emitting the `remw` ("Remainder Word") instruction.
;; rd ← sext32(rs1) mod sext32(rs2)
(decl rv_remw (Reg Reg) Reg)
(rule (rv_remw rs1 rs2)
(alu_rrr (AluOPRRR.Remw) rs1 rs2))
;; Helper for emitting the `remuw` ("Remainder Unsigned Word") instruction.
;; rd ← uext32(rs1) mod uext32(rs2)
(decl rv_remuw (Reg Reg) Reg)
(rule (rv_remuw rs1 rs2)
(alu_rrr (AluOPRRR.Remuw) rs1 rs2))
;; F and D Extensions
;; TODO: Enable these instructions only when we have the F or D extensions
;; Helper for emitting the `fadd` instruction.
(decl rv_fadd (Type Reg Reg) Reg)
(rule (rv_fadd $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FaddS) $F32 rs1 rs2))
(rule (rv_fadd $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FaddD) $F64 rs1 rs2))
;; Helper for emitting the `fsub` instruction.
(decl rv_fsub (Type Reg Reg) Reg)
(rule (rv_fsub $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FsubS) $F32 rs1 rs2))
(rule (rv_fsub $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FsubD) $F64 rs1 rs2))
;; Helper for emitting the `fmul` instruction.
(decl rv_fmul (Type Reg Reg) Reg)
(rule (rv_fmul $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FmulS) $F32 rs1 rs2))
(rule (rv_fmul $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FmulD) $F64 rs1 rs2))
;; Helper for emitting the `fdiv` instruction.
(decl rv_fdiv (Type Reg Reg) Reg)
(rule (rv_fdiv $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FdivS) $F32 rs1 rs2))
(rule (rv_fdiv $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FdivD) $F64 rs1 rs2))
;; Helper for emitting the `fsqrt` instruction.
(decl rv_fsqrt (Type Reg) Reg)
(rule (rv_fsqrt $F32 rs1) (fpu_rr (FpuOPRR.FsqrtS) $F32 rs1))
(rule (rv_fsqrt $F64 rs1) (fpu_rr (FpuOPRR.FsqrtD) $F64 rs1))
;; Helper for emitting the `fmadd` instruction.
(decl rv_fmadd (Type Reg Reg Reg) Reg)
(rule (rv_fmadd $F32 rs1 rs2 rs3) (fpu_rrrr (FpuOPRRRR.FmaddS) $F32 rs1 rs2 rs3))
(rule (rv_fmadd $F64 rs1 rs2 rs3) (fpu_rrrr (FpuOPRRRR.FmaddD) $F64 rs1 rs2 rs3))
;; Helper for emitting the `fcvt.d.s` ("Float Convert Double to Single") instruction.
(decl rv_fcvtds (Reg) Reg)
(rule (rv_fcvtds rs1) (fpu_rr (FpuOPRR.FcvtDS) $F32 rs1))
;; Helper for emitting the `fcvt.s.d` ("Float Convert Single to Double") instruction.
(decl rv_fcvtsd (Reg) Reg)
(rule (rv_fcvtsd rs1) (fpu_rr (FpuOPRR.FcvtSD) $F64 rs1))
;; Helper for emitting the `fsgnj` ("Floating Point Sign Injection") instruction.
;; The output of this instruction is `rs1` with the sign bit from `rs2`
;; This implements the `copysign` operation
(decl rv_fsgnj (Type Reg Reg) Reg)
(rule (rv_fsgnj $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FsgnjS) $F32 rs1 rs2))
(rule (rv_fsgnj $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FsgnjD) $F64 rs1 rs2))
;; Helper for emitting the `fsgnjn` ("Floating Point Sign Injection Negated") instruction.
;; The output of this instruction is `rs1` with the negated sign bit from `rs2`
;; When `rs1 == rs2` this implements the `neg` operation
(decl rv_fsgnjn (Type Reg Reg) Reg)
(rule (rv_fsgnjn $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FsgnjnS) $F32 rs1 rs2))
(rule (rv_fsgnjn $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FsgnjnD) $F64 rs1 rs2))
;; Helper for emitting the `fneg` ("Floating Point Negate") instruction.
;; This instruction is a mnemonic for `fsgnjn rd, rs1, rs1`
(decl rv_fneg (Type Reg) Reg)
(rule (rv_fneg ty rs1) (rv_fsgnjn ty rs1 rs1))
;; Helper for emitting the `fsgnjx` ("Floating Point Sign Injection Exclusive") instruction.
;; The output of this instruction is `rs1` with the XOR of the sign bits from `rs1` and `rs2`.
;; When `rs1 == rs2` this implements `fabs`
(decl rv_fsgnjx (Type Reg Reg) Reg)
(rule (rv_fsgnjx $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FsgnjxS) $F32 rs1 rs2))
(rule (rv_fsgnjx $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FsgnjxD) $F64 rs1 rs2))
;; Helper for emitting the `fabs` ("Floating Point Absolute") instruction.
;; This instruction is a mnemonic for `fsgnjx rd, rs1, rs1`
(decl rv_fabs (Type Reg) Reg)
(rule (rv_fabs ty rs1) (rv_fsgnjx ty rs1 rs1))
;; Helper for emitting the `feq` ("Float Equal") instruction.
(decl rv_feq (Type Reg Reg) Reg)
(rule (rv_feq $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FeqS) $I64 rs1 rs2))
(rule (rv_feq $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FeqD) $I64 rs1 rs2))
;; Helper for emitting the `flt` ("Float Less Than") instruction.
(decl rv_flt (Type Reg Reg) Reg)
(rule (rv_flt $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FltS) $I64 rs1 rs2))
(rule (rv_flt $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FltD) $I64 rs1 rs2))
;; Helper for emitting the `fle` ("Float Less Than or Equal") instruction.
(decl rv_fle (Type Reg Reg) Reg)
(rule (rv_fle $F32 rs1 rs2) (fpu_rrr (FpuOPRRR.FleS) $I64 rs1 rs2))
(rule (rv_fle $F64 rs1 rs2) (fpu_rrr (FpuOPRRR.FleD) $I64 rs1 rs2))
;; Helper for emitting the `fgt` ("Float Greater Than") instruction.
;; Note: The arguments are reversed
(decl rv_fgt (Type Reg Reg) Reg)
(rule (rv_fgt ty rs1 rs2) (rv_flt ty rs2 rs1))
;; Helper for emitting the `fge` ("Float Greater Than or Equal") instruction.
;; Note: The arguments are reversed
(decl rv_fge (Type Reg Reg) Reg)
(rule (rv_fge ty rs1 rs2) (rv_fle ty rs2 rs1))
;; `Zba` Extension Instructions
;; Helper for emitting the `adduw` ("Add Unsigned Word") instruction.
;; rd ← uext32(rs1) + uext32(rs2)
(decl rv_adduw (Reg Reg) Reg)
(rule (rv_adduw rs1 rs2)
(alu_rrr (AluOPRRR.Adduw) rs1 rs2))
;; Helper for emitting the `zext.w` ("Zero Extend Word") instruction.
;; This instruction is a mnemonic for `adduw rd, rs1, zero`.
;; rd ← uext32(rs1)
(decl rv_zextw (Reg) Reg)
(rule (rv_zextw rs1)
(rv_adduw rs1 (zero_reg)))
;; Helper for emitting the `slli.uw` ("Shift Left Logical Immediate Unsigned Word") instruction.
;; rd ← uext32(rs1) << imm
(decl rv_slliuw (Reg Imm12) Reg)
(rule (rv_slliuw rs1 imm)
(alu_rr_imm12 (AluOPRRI.SlliUw) rs1 imm))
;; `Zbb` Extension Instructions
;; Helper for emitting the `andn` ("And Negated") instruction.
;; rd ← rs1 ∧ ~(rs2)
(decl rv_andn (Reg Reg) Reg)
(rule (rv_andn rs1 rs2)
(alu_rrr (AluOPRRR.Andn) rs1 rs2))
;; Helper for emitting the `orn` ("Or Negated") instruction.
;; rd ← rs1 ~(rs2)
(decl rv_orn (Reg Reg) Reg)
(rule (rv_orn rs1 rs2)
(alu_rrr (AluOPRRR.Orn) rs1 rs2))
;; Helper for emitting the `clz` ("Count Leading Zero Bits") instruction.
(decl rv_clz (Reg) Reg)
(rule (rv_clz rs1)
(alu_rr_funct12 (AluOPRRI.Clz) rs1))
;; Helper for emitting the `clzw` ("Count Leading Zero Bits in Word") instruction.
(decl rv_clzw (Reg) Reg)
(rule (rv_clzw rs1)
(alu_rr_funct12 (AluOPRRI.Clzw) rs1))
;; Helper for emitting the `ctz` ("Count Trailing Zero Bits") instruction.
(decl rv_ctz (Reg) Reg)
(rule (rv_ctz rs1)
(alu_rr_funct12 (AluOPRRI.Ctz) rs1))
;; Helper for emitting the `ctzw` ("Count Trailing Zero Bits in Word") instruction.
(decl rv_ctzw (Reg) Reg)
(rule (rv_ctzw rs1)
(alu_rr_funct12 (AluOPRRI.Ctzw) rs1))
;; Helper for emitting the `cpop` ("Count Population") instruction.
(decl rv_cpop (Reg) Reg)
(rule (rv_cpop rs1)
(alu_rr_funct12 (AluOPRRI.Cpop) rs1))
;; Helper for emitting the `max` instruction.
(decl rv_max (Reg Reg) Reg)
(rule (rv_max rs1 rs2)
(alu_rrr (AluOPRRR.Max) rs1 rs2))
;; Helper for emitting the `sext.b` instruction.
(decl rv_sextb (Reg) Reg)
(rule (rv_sextb rs1)
(alu_rr_imm12 (AluOPRRI.Sextb) rs1 (imm12_const 0)))
;; Helper for emitting the `sext.h` instruction.
(decl rv_sexth (Reg) Reg)
(rule (rv_sexth rs1)
(alu_rr_imm12 (AluOPRRI.Sexth) rs1 (imm12_const 0)))
;; Helper for emitting the `zext.h` instruction.
(decl rv_zexth (Reg) Reg)
(rule (rv_zexth rs1)
(alu_rr_imm12 (AluOPRRI.Zexth) rs1 (imm12_const 0)))
;; Helper for emitting the `rol` ("Rotate Left") instruction.
(decl rv_rol (Reg Reg) Reg)
(rule (rv_rol rs1 rs2)
(alu_rrr (AluOPRRR.Rol) rs1 rs2))
;; Helper for emitting the `rolw` ("Rotate Left Word") instruction.
(decl rv_rolw (Reg Reg) Reg)
(rule (rv_rolw rs1 rs2)
(alu_rrr (AluOPRRR.Rolw) rs1 rs2))
;; Helper for emitting the `ror` ("Rotate Right") instruction.
(decl rv_ror (Reg Reg) Reg)
(rule (rv_ror rs1 rs2)
(alu_rrr (AluOPRRR.Ror) rs1 rs2))
;; Helper for emitting the `rorw` ("Rotate Right Word") instruction.
(decl rv_rorw (Reg Reg) Reg)
(rule (rv_rorw rs1 rs2)
(alu_rrr (AluOPRRR.Rorw) rs1 rs2))
;; Helper for emitting the `rev8` ("Byte Reverse") instruction.
(decl rv_rev8 (Reg) Reg)
(rule (rv_rev8 rs1)
(alu_rr_funct12 (AluOPRRI.Rev8) rs1))
;; Helper for emitting the `brev8` ("Bit Reverse Inside Bytes") instruction.
;; TODO: This instruction is mentioned in some older versions of the
;; spec, but has since disappeared, we should follow up on this.
;; It probably was renamed to `rev.b` which seems to be the closest match.
(decl rv_brev8 (Reg) Reg)
(rule (rv_brev8 rs1)
(alu_rr_funct12 (AluOPRRI.Brev8) rs1))
;; Helper for emitting the `bseti` ("Single-Bit Set Immediate") instruction.
(decl rv_bseti (Reg Imm12) Reg)
(rule (rv_bseti rs1 imm)
(alu_rr_imm12 (AluOPRRI.Bseti) rs1 imm))
;; `Zbkb` Extension Instructions
;; Helper for emitting the `pack` ("Pack low halves of registers") instruction.
(decl rv_pack (Reg Reg) Reg)
(rule (rv_pack rs1 rs2)
(alu_rrr (AluOPRRR.Pack) rs1 rs2))
;; Helper for emitting the `packw` ("Pack low 16-bits of registers") instruction.
(decl rv_packw (Reg Reg) Reg)
(rule (rv_packw rs1 rs2)
(alu_rrr (AluOPRRR.Packw) rs1 rs2))
;; for load immediate
(decl imm (Type u64) Reg)
(extern constructor imm imm)
;; Imm12 Rules
(decl pure imm12_zero () Imm12)
(rule
(imm12_zero)
(imm12_const 0))
(decl pure imm12_const (i32) Imm12)
(extern constructor imm12_const imm12_const)
(decl load_imm12 (i32) Reg)
(rule
(load_imm12 x)
(rv_addi (zero_reg) (imm12_const x)))
;; for load immediate
(decl imm_from_bits (u64) Imm12)
(extern constructor imm_from_bits imm_from_bits)
(decl imm_from_neg_bits (i64) Imm12)
(extern constructor imm_from_neg_bits imm_from_neg_bits)
(decl imm12_const_add (i32 i32) Imm12)
(extern constructor imm12_const_add imm12_const_add)
(decl imm12_and (Imm12 i32) Imm12)
(extern constructor imm12_and imm12_and)
;; Helper for get negative of Imm12
(decl neg_imm12 (Imm12) Imm12)
(extern constructor neg_imm12 neg_imm12)
;; Imm12 Extractors
;; Helper to go directly from a `Value`, when it's an `iconst`, to an `Imm12`.
(decl imm12_from_value (Imm12) Value)
(extractor
(imm12_from_value n)
(def_inst (iconst (u64_from_imm64 (imm12_from_u64 n)))))
(decl imm12_from_u64 (Imm12) u64)
(extern extractor imm12_from_u64 imm12_from_u64)
;; Float Helpers
(decl gen_default_frm () OptionFloatRoundingMode)
(extern constructor gen_default_frm gen_default_frm)
;; Helper for emitting `MInst.FpuRR` instructions.
(decl fpu_rr (FpuOPRR Type Reg) Reg)
(rule (fpu_rr op ty src)
(let ((dst WritableReg (temp_writable_reg ty))
(_ Unit (emit (MInst.FpuRR op (gen_default_frm) dst src))))
dst))
;; Helper for emitting `MInst.AluRRR` instructions.
(decl alu_rrr (AluOPRRR Reg Reg) Reg)
(rule (alu_rrr op src1 src2)
(let ((dst WritableReg (temp_writable_reg $I64))
(_ Unit (emit (MInst.AluRRR op dst src1 src2))))
dst))
(decl pack_float_rounding_mode (FRM) OptionFloatRoundingMode)
(extern constructor pack_float_rounding_mode pack_float_rounding_mode)
;; Helper for emitting `MInst.AluRRR` instructions.
(decl fpu_rrr (FpuOPRRR Type Reg Reg) Reg)
(rule (fpu_rrr op ty src1 src2)
(let ((dst WritableReg (temp_writable_reg ty))
(_ Unit (emit (MInst.FpuRRR op (gen_default_frm) dst src1 src2))))
dst))
;; Helper for emitting `MInst.FpuRRRR` instructions.
(decl fpu_rrrr (FpuOPRRRR Type Reg Reg Reg) Reg)
(rule (fpu_rrrr op ty src1 src2 src3)
(let ((dst WritableReg (temp_writable_reg ty))
(_ Unit (emit (MInst.FpuRRRR op (gen_default_frm) dst src1 src2 src3))))
dst))
;; Helper for emitting `MInst.AluRRImm12` instructions.
(decl alu_rr_imm12 (AluOPRRI Reg Imm12) Reg)
(rule (alu_rr_imm12 op src imm)
(let ((dst WritableReg (temp_writable_reg $I64))
(_ Unit (emit (MInst.AluRRImm12 op dst src imm))))
dst))
;; some instruction use imm12 as funct12.
;; so we don't need the imm12 paramter.
(decl alu_rr_funct12 (AluOPRRI Reg) Reg)
(rule (alu_rr_funct12 op src)
(let ((dst WritableReg (temp_writable_reg $I64))
(_ Unit (emit (MInst.AluRRImm12 op dst src (imm12_zero)))))
dst))
(decl select_addi (Type) AluOPRRI)
(rule 1 (select_addi (fits_in_32 ty)) (AluOPRRI.Addiw))
(rule (select_addi (fits_in_64 ty)) (AluOPRRI.Addi))
(decl gen_bnot (Type ValueRegs) ValueRegs)
(rule 1 (gen_bnot $I128 x)
(let ((lo Reg (rv_not (value_regs_get x 0)))
(hi Reg (rv_not (value_regs_get x 1))))
(value_regs lo hi)))
(rule 0 (gen_bnot (fits_in_64 _) x)
(rv_not x))
(decl gen_and (Type ValueRegs ValueRegs) ValueRegs)
(rule 1 (gen_and $I128 x y)
(value_regs
(rv_and (value_regs_get x 0) (value_regs_get y 0))
(rv_and (value_regs_get x 1) (value_regs_get y 1))))
(rule 0 (gen_and (fits_in_64 _) x y)
(rv_and (value_regs_get x 0) (value_regs_get y 0)))
(decl gen_or (Type ValueRegs ValueRegs) ValueRegs)
(rule 1 (gen_or $I128 x y)
(value_regs
(rv_or (value_regs_get x 0) (value_regs_get y 0))
(rv_or (value_regs_get x 1) (value_regs_get y 1))))
(rule 0 (gen_or (fits_in_64 _) x y)
(rv_or (value_regs_get x 0) (value_regs_get y 0)))
(decl lower_bit_reverse (Reg Type) Reg)
(rule
(lower_bit_reverse r $I8)
(gen_brev8 r $I8))
(rule
(lower_bit_reverse r $I16)
(let
((tmp Reg (gen_brev8 r $I16))
(tmp2 Reg (gen_rev8 tmp))
(result Reg (rv_srli tmp2 (imm12_const 48))))
result))
(rule
(lower_bit_reverse r $I32)
(let
((tmp Reg (gen_brev8 r $I32))
(tmp2 Reg (gen_rev8 tmp))
(result Reg (rv_srli tmp2 (imm12_const 32))))
result))
(rule
(lower_bit_reverse r $I64)
(let
((tmp Reg (gen_rev8 r)))
(gen_brev8 tmp $I64)))
(decl lower_ctz (Type Reg) Reg)
(rule (lower_ctz ty x)
(gen_cltz $false x ty))
(rule 1 (lower_ctz (fits_in_16 ty) x)
(if-let $true (has_zbb))
(let ((tmp Reg (gen_bseti x (ty_bits ty))))
(rv_ctzw tmp)))
(rule 2 (lower_ctz $I32 x)
(if-let $true (has_zbb))
(rv_ctzw x))
(rule 2 (lower_ctz $I64 x)
(if-let $true (has_zbb))
(rv_ctz x))
;; Count trailing zeros from a i128 bit value.
;; We count both halves separately and conditionally add them if it makes sense.
(decl lower_ctz_128 (ValueRegs) ValueRegs)
(rule (lower_ctz_128 x)
(let ((x_lo Reg (value_regs_get x 0))
(x_hi Reg (value_regs_get x 1))
;; Count both halves
(high Reg (lower_ctz $I64 x_hi))
(low Reg (lower_ctz $I64 x_lo))
;; Only add the top half if the bottom is zero
(high Reg (gen_select_reg (IntCC.Equal) x_lo (zero_reg) high (zero_reg)))
(result Reg (rv_add low high)))
(zext result $I64 $I128)))
(decl lower_clz (Type Reg) Reg)
(rule (lower_clz ty rs)
(gen_cltz $true rs ty))
(rule 1 (lower_clz (fits_in_16 ty) r)
(if-let $true (has_zbb))
(let ((tmp Reg (zext r ty $I64))
(count Reg (rv_clz tmp))
;; We always do the operation on the full 64-bit register, so subtract 64 from the result.
(result Reg (rv_addi count (imm12_const_add (ty_bits ty) -64))))
result))
(rule 2 (lower_clz $I32 r)
(if-let $true (has_zbb))
(rv_clzw r))
(rule 2 (lower_clz $I64 r)
(if-let $true (has_zbb))
(rv_clz r))
;; Count leading zeros from a i128 bit value.
;; We count both halves separately and conditionally add them if it makes sense.
(decl lower_clz_i128 (ValueRegs) ValueRegs)
(rule (lower_clz_i128 x)
(let ((x_lo Reg (value_regs_get x 0))
(x_hi Reg (value_regs_get x 1))
;; Count both halves
(high Reg (lower_clz $I64 x_hi))
(low Reg (lower_clz $I64 x_lo))
;; Only add the bottom zeros if the top half is zero
(low Reg (gen_select_reg (IntCC.Equal) x_hi (zero_reg) low (zero_reg)))
(result Reg (rv_add high low)))
(zext result $I64 $I128)))
(decl lower_cls (Type Reg) Reg)
(rule (lower_cls ty r)
(let ((tmp Reg (ext_int_if_need $true r ty))
(tmp2 Reg (gen_select_reg (IntCC.SignedLessThan) tmp (zero_reg) (rv_not tmp) tmp))
(tmp3 Reg (lower_clz ty tmp2)))
(rv_addi tmp3 (imm12_const -1))))
;; If the sign bit is set, we count the leading zeros of the inverted value.
;; Otherwise we can just count the leading zeros of the original value.
;; Subtract 1 since the sign bit does not count.
(decl lower_cls_i128 (ValueRegs) ValueRegs)
(rule (lower_cls_i128 x)
(let ((low Reg (value_regs_get x 0))
(high Reg (value_regs_get x 1))
(low Reg (gen_select_reg (IntCC.SignedLessThan) high (zero_reg) (rv_not low) low))
(high Reg (gen_select_reg (IntCC.SignedLessThan) high (zero_reg) (rv_not high) high))
(tmp ValueRegs (lower_clz_i128 (value_regs low high)))
(count Reg (value_regs_get tmp 0))
(result Reg (rv_addi count (imm12_const -1))))
(zext result $I64 $I128)))
(decl gen_cltz (bool Reg Type) Reg)
(rule (gen_cltz leading rs ty)
(let ((tmp WritableReg (temp_writable_reg $I64))
(step WritableReg (temp_writable_reg $I64))
(sum WritableReg (temp_writable_reg $I64))
(_ Unit (emit (MInst.Cltz leading sum step tmp rs ty))))
sum))
;; Extends an integer if it is smaller than 64 bits.
(decl ext_int_if_need (bool ValueRegs Type) ValueRegs)
;;; For values smaller than 64 bits, we need to extend them to 64 bits
(rule 0 (ext_int_if_need $true val (fits_in_32 (ty_int ty)))
(sext val ty $I64))
(rule 0 (ext_int_if_need $false val (fits_in_32 (ty_int ty)))
(zext val ty $I64))
;; If the value is larger than one machine register, we don't need to do anything
(rule 1 (ext_int_if_need _ r $I64) r)
(rule 2 (ext_int_if_need _ r $I128) r)
;; Performs a zero extension of the given value
(decl zext (ValueRegs Type Type) ValueRegs)
(rule (zext val from_ty to_ty) (extend val (ExtendOp.Zero) from_ty to_ty))
;; Performs a signed extension of the given value
(decl sext (ValueRegs Type Type) ValueRegs)
(rule (sext val from_ty to_ty) (extend val (ExtendOp.Signed) from_ty to_ty))
(type ExtendOp
(enum
(Zero)
(Signed)))
;; Performs either a sign or zero extension of the given value
(decl extend (ValueRegs ExtendOp Type Type) ValueRegs)
;;; Generic Rules Extending to I64
(decl pure extend_shift_op (ExtendOp) AluOPRRI)
(rule (extend_shift_op (ExtendOp.Zero)) (AluOPRRI.Srli))
(rule (extend_shift_op (ExtendOp.Signed)) (AluOPRRI.Srai))
;; In the most generic case, we shift left and then shift right.
;; The type of right shift is determined by the extend op.
(rule 0 (extend val extend_op (fits_in_32 from_ty) (fits_in_64 to_ty))
(let ((val Reg (value_regs_get val 0))
(shift Imm12 (imm_from_bits (u64_sub 64 (ty_bits from_ty))))
(left Reg (rv_slli val shift))
(shift_op AluOPRRI (extend_shift_op extend_op))
(right Reg (alu_rr_imm12 shift_op left shift)))
right))
;; If we are zero extending a U8 we can use a `andi` instruction.
(rule 1 (extend val (ExtendOp.Zero) $I8 (fits_in_64 to_ty))
(let ((val Reg (value_regs_get val 0)))
(rv_andi val (imm12_const 255))))
;; When signed extending from 32 to 64 bits we can use a
;; `addiw val 0`. Also known as a `sext.w`
(rule 1 (extend val (ExtendOp.Signed) $I32 $I64)
(let ((val Reg (value_regs_get val 0)))
(rv_sextw val)))
;; No point in trying to use `packh` here to zero extend 8 bit values
;; since we can just use `andi` instead which is part of the base ISA.
;; If we have the `zbkb` extension `packw` can be used to zero extend 16 bit values
(rule 1 (extend val (ExtendOp.Zero) $I16 (fits_in_64 _))
(if-let $true (has_zbkb))
(let ((val Reg (value_regs_get val 0)))
(rv_packw val (zero_reg))))
;; If we have the `zbkb` extension `pack` can be used to zero extend 32 bit registers
(rule 1 (extend val (ExtendOp.Zero) $I32 $I64)
(if-let $true (has_zbkb))
(let ((val Reg (value_regs_get val 0)))
(rv_pack val (zero_reg))))
;; If we have the `zbb` extension we can use the dedicated `sext.b` instruction.
(rule 1 (extend val (ExtendOp.Signed) $I8 (fits_in_64 _))
(if-let $true (has_zbb))
(let ((val Reg (value_regs_get val 0)))
(rv_sextb val)))
;; If we have the `zbb` extension we can use the dedicated `sext.h` instruction.
(rule 1 (extend val (ExtendOp.Signed) $I16 (fits_in_64 _))
(if-let $true (has_zbb))
(let ((val Reg (value_regs_get val 0)))
(rv_sexth val)))
;; If we have the `zbb` extension we can use the dedicated `zext.h` instruction.
(rule 2 (extend val (ExtendOp.Zero) $I16 (fits_in_64 _))
(if-let $true (has_zbb))
(let ((val Reg (value_regs_get val 0)))
(rv_zexth val)))
;; With `zba` we have a `zext.w` instruction
(rule 2 (extend val (ExtendOp.Zero) $I32 $I64)
(if-let $true (has_zba))
(let ((val Reg (value_regs_get val 0)))
(rv_zextw val)))
;;; Signed rules extending to I128
;; Extend the bottom part, and extract the sign bit from the bottom as the top
(rule 3 (extend val (ExtendOp.Signed) (fits_in_64 from_ty) $I128)
(let ((val Reg (value_regs_get val 0))
(low Reg (extend val (ExtendOp.Signed) from_ty $I64))
(high Reg (rv_srai low (imm12_const 63))))
(value_regs low high)))
;;; Unsigned rules extending to I128
;; Extend the bottom register to I64 and then just zero out the top half.
(rule 3 (extend val (ExtendOp.Zero) (fits_in_64 from_ty) $I128)
(let ((val Reg (value_regs_get val 0))
(low Reg (extend val (ExtendOp.Zero) from_ty $I64))
(high Reg (load_u64_constant 0)))
(value_regs low high)))
;; Catch all rule for ignoring extensions of the same type.
(rule 4 (extend val _ ty ty) val)
(decl lower_b128_binary (AluOPRRR ValueRegs ValueRegs) ValueRegs)
(rule
(lower_b128_binary op a b)
(let
( ;; low part.
(low Reg (alu_rrr op (value_regs_get a 0) (value_regs_get b 0)))
;; high part.
(high Reg (alu_rrr op (value_regs_get a 1) (value_regs_get b 1))))
(value_regs low high)))
(decl lower_umlhi (Type Reg Reg) Reg)
(rule 1
(lower_umlhi $I64 rs1 rs2)
(rv_mulhu rs1 rs2))
(rule
(lower_umlhi ty rs1 rs2)
(let
((tmp Reg (rv_mul (ext_int_if_need $false rs1 ty) (ext_int_if_need $false rs2 ty))))
(rv_srli tmp (imm12_const (ty_bits ty)))))
(decl lower_smlhi (Type Reg Reg) Reg)
(rule 1
(lower_smlhi $I64 rs1 rs2)
(rv_mulh rs1 rs2))
(rule
(lower_smlhi ty rs1 rs2)
(let
((tmp Reg (rv_mul rs1 rs2)))
(rv_srli tmp (imm12_const (ty_bits ty)))))
(decl lower_rotl (Type Reg Reg) Reg)
(rule 1
(lower_rotl $I64 rs amount)
(if-let $true (has_zbb))
(rv_rol rs amount))
(rule
(lower_rotl $I64 rs amount)
(if-let $false (has_zbb))
(lower_rotl_shift $I64 rs amount))
(rule 1
(lower_rotl $I32 rs amount)
(if-let $true (has_zbb))
(rv_rolw rs amount))
(rule
(lower_rotl $I32 rs amount)
(if-let $false (has_zbb))
(lower_rotl_shift $I32 rs amount))
(rule -1
(lower_rotl ty rs amount)
(lower_rotl_shift ty rs amount))
;;; using shift to implement rotl.
(decl lower_rotl_shift (Type Reg Reg) Reg)
;;; for I8 and I16 ...
(rule
(lower_rotl_shift ty rs amount)
(let
((x ValueRegs (gen_shamt ty amount))
(shamt Reg (value_regs_get x 0))
(len_sub_shamt Reg (value_regs_get x 1))
;;
(part1 Reg (rv_sll rs shamt))
;;
(part2 Reg (rv_srl rs len_sub_shamt))
(part3 Reg (gen_select_reg (IntCC.Equal) shamt (zero_reg) (zero_reg) part2)))
(rv_or part1 part3)))
;;;; construct shift amount.rotl on i128 will use shift to implement. So can call this function.
;;;; this will return shift amount and (ty_bits - "shift amount")
;;;; if ty_bits is greater than 64 like i128, then shmat will fallback to 64.because We are 64 bit platform.
(decl gen_shamt (Type Reg) ValueRegs)
(extern constructor gen_shamt gen_shamt)
(decl lower_rotr (Type Reg Reg) Reg)
(rule 1
(lower_rotr $I64 rs amount)
(if-let $true (has_zbb))
(rv_ror rs amount))
(rule
(lower_rotr $I64 rs amount)
(if-let $false (has_zbb))
(lower_rotr_shift $I64 rs amount))
(rule 1
(lower_rotr $I32 rs amount)
(if-let $true (has_zbb))
(rv_rorw rs amount))
(rule
(lower_rotr $I32 rs amount)
(if-let $false (has_zbb))
(lower_rotr_shift $I32 rs amount))
(rule -1
(lower_rotr ty rs amount)
(lower_rotr_shift ty rs amount))
(decl lower_rotr_shift (Type Reg Reg) Reg)
;;;
(rule
(lower_rotr_shift ty rs amount)
(let
((x ValueRegs (gen_shamt ty amount))
(shamt Reg (value_regs_get x 0))
(len_sub_shamt Reg (value_regs_get x 1))
;;
(part1 Reg (rv_srl rs shamt))
;;
(part2 Reg (rv_sll rs len_sub_shamt))
;;
(part3 Reg (gen_select_reg (IntCC.Equal) shamt (zero_reg) (zero_reg) part2)))
(rv_or part1 part3)))
;; bseti: Set a single bit in a register, indexed by a constant.
(decl gen_bseti (Reg u64) Reg)
(rule (gen_bseti val bit)
(if-let $false (has_zbs))
(if-let $false (u64_le bit 12))
(let ((const Reg (load_u64_constant (u64_shl 1 bit))))
(rv_or val const)))
(rule (gen_bseti val bit)
(if-let $false (has_zbs))
(if-let $true (u64_le bit 12))
(rv_ori val (imm12_const (u64_as_i32 (u64_shl 1 bit)))))
(rule (gen_bseti val bit)
(if-let $true (has_zbs))
(rv_bseti val (imm12_const (u64_as_i32 bit))))
(decl gen_popcnt (Reg Type) Reg)
(rule
(gen_popcnt rs ty)
(let
((tmp WritableReg (temp_writable_reg $I64))
(step WritableReg (temp_writable_reg $I64))
(sum WritableReg (temp_writable_reg $I64))
(_ Unit (emit (MInst.Popcnt sum step tmp rs ty))))
(writable_reg_to_reg sum)))
(decl lower_popcnt (Reg Type) Reg)
(rule 1 (lower_popcnt rs ty)
(if-let $true (has_zbb))
(rv_cpop (ext_int_if_need $false rs ty)))
(rule (lower_popcnt rs ty)
(if-let $false (has_zbb))
(gen_popcnt rs ty))
(decl lower_popcnt_i128 (ValueRegs) ValueRegs)
(rule
(lower_popcnt_i128 a)
(let
( ;; low part.
(low Reg (lower_popcnt (value_regs_get a 0) $I64))
;; high part.
(high Reg (lower_popcnt (value_regs_get a 1) $I64))
;; add toghter.
(result Reg (rv_add low high)))
(value_regs result (load_u64_constant 0))))
(decl lower_i128_rotl (ValueRegs ValueRegs) ValueRegs)
(rule
(lower_i128_rotl x y)
(let
((tmp ValueRegs (gen_shamt $I128 (value_regs_get y 0)))
(shamt Reg (value_regs_get tmp 0))
(len_sub_shamt Reg (value_regs_get tmp 1))
;;
(low_part1 Reg (rv_sll (value_regs_get x 0) shamt))
(low_part2 Reg (rv_srl (value_regs_get x 1) len_sub_shamt))
;;; if shamt == 0 low_part2 will overflow we should zero instead.
(low_part3 Reg (gen_select_reg (IntCC.Equal) shamt (zero_reg) (zero_reg) low_part2))
(low Reg (rv_or low_part1 low_part3))
;;
(high_part1 Reg (rv_sll (value_regs_get x 1) shamt))
(high_part2 Reg (rv_srl (value_regs_get x 0) len_sub_shamt))
(high_part3 Reg (gen_select_reg (IntCC.Equal) shamt (zero_reg) (zero_reg) high_part2))
(high Reg (rv_or high_part1 high_part3))
;;
(const64 Reg (load_u64_constant 64))
(shamt_128 Reg (rv_andi (value_regs_get y 0) (imm12_const 127))))
;; right now we only rotate less than 64 bits.
;; if shamt is greater than or equal 64 , we should switch low and high.
(value_regs
(gen_select_reg (IntCC.UnsignedGreaterThanOrEqual) shamt_128 const64 high low)
(gen_select_reg (IntCC.UnsignedGreaterThanOrEqual) shamt_128 const64 low high)
)))
(decl lower_i128_rotr (ValueRegs ValueRegs) ValueRegs)
(rule
(lower_i128_rotr x y)
(let
((tmp ValueRegs (gen_shamt $I128 (value_regs_get y 0)))
(shamt Reg (value_regs_get tmp 0))
(len_sub_shamt Reg (value_regs_get tmp 1))
;;
(low_part1 Reg (rv_srl (value_regs_get x 0) shamt))
(low_part2 Reg (rv_sll (value_regs_get x 1) len_sub_shamt))
;;; if shamt == 0 low_part2 will overflow we should zero instead.
(low_part3 Reg (gen_select_reg (IntCC.Equal) shamt (zero_reg) (zero_reg) low_part2))
(low Reg (rv_or low_part1 low_part3))
;;
(high_part1 Reg (rv_srl (value_regs_get x 1) shamt))
(high_part2 Reg (rv_sll (value_regs_get x 0) len_sub_shamt))
(high_part3 Reg (gen_select_reg (IntCC.Equal) shamt (zero_reg) (zero_reg) high_part2))
(high Reg (rv_or high_part1 high_part3))
;;
(const64 Reg (load_u64_constant 64))
(shamt_128 Reg (rv_andi (value_regs_get y 0) (imm12_const 127))))
;; right now we only rotate less than 64 bits.
;; if shamt is greater than or equal 64 , we should switch low and high.
(value_regs
(gen_select_reg (IntCC.UnsignedGreaterThanOrEqual) shamt_128 const64 high low)
(gen_select_reg (IntCC.UnsignedGreaterThanOrEqual) shamt_128 const64 low high)
)))
(decl lower_i128_ishl (ValueRegs ValueRegs) ValueRegs)
(rule
(lower_i128_ishl x y)
(let
((tmp ValueRegs (gen_shamt $I128 (value_regs_get y 0)))
(shamt Reg (value_regs_get tmp 0))
(len_sub_shamt Reg (value_regs_get tmp 1))
;;
(low Reg (rv_sll (value_regs_get x 0) shamt))
;; high part.
(high_part1 Reg (rv_srl (value_regs_get x 0) len_sub_shamt))
(high_part2 Reg (gen_select_reg (IntCC.Equal) shamt (zero_reg) (zero_reg) high_part1))
;;
(high_part3 Reg (rv_sll (value_regs_get x 1) shamt))
(high Reg (rv_or high_part2 high_part3 ))
;;
(const64 Reg (load_u64_constant 64))
(shamt_128 Reg (rv_andi (value_regs_get y 0) (imm12_const 127))))
(value_regs
(gen_select_reg (IntCC.UnsignedGreaterThanOrEqual) shamt_128 const64 (zero_reg) low)
(gen_select_reg (IntCC.UnsignedGreaterThanOrEqual) shamt_128 const64 low high))))
(decl lower_i128_ushr (ValueRegs ValueRegs) ValueRegs)
(rule
(lower_i128_ushr x y)
(let
((tmp ValueRegs (gen_shamt $I128 (value_regs_get y 0)))
(shamt Reg (value_regs_get tmp 0))
(len_sub_shamt Reg (value_regs_get tmp 1))
;; low part.
(low_part1 Reg (rv_sll (value_regs_get x 1) len_sub_shamt))
(low_part2 Reg (gen_select_reg (IntCC.Equal) shamt (zero_reg) (zero_reg) low_part1))
;;
(low_part3 Reg (rv_srl (value_regs_get x 0) shamt))
(low Reg (rv_or low_part2 low_part3 ))
;;
(const64 Reg (load_u64_constant 64))
;;
(high Reg (rv_srl (value_regs_get x 1) shamt))
(shamt_128 Reg (rv_andi (value_regs_get y 0) (imm12_const 127))))
(value_regs
(gen_select_reg (IntCC.UnsignedGreaterThanOrEqual) shamt_128 const64 high low)
(gen_select_reg (IntCC.UnsignedGreaterThanOrEqual) shamt_128 const64 (zero_reg) high))))
(decl lower_i128_sshr (ValueRegs ValueRegs) ValueRegs)
(rule
(lower_i128_sshr x y)
(let
((tmp ValueRegs (gen_shamt $I128 (value_regs_get y 0)))
(shamt Reg (value_regs_get tmp 0))
(len_sub_shamt Reg (value_regs_get tmp 1))
;; low part.
(low_part1 Reg (rv_sll (value_regs_get x 1) len_sub_shamt))
(low_part2 Reg (gen_select_reg (IntCC.Equal) shamt (zero_reg) (zero_reg) low_part1))
;;
(low_part3 Reg (rv_srl (value_regs_get x 0) shamt))
(low Reg (rv_or low_part2 low_part3 ))
;;
(const64 Reg (load_u64_constant 64))
;;
(high Reg (rv_sra (value_regs_get x 1) shamt))
;;
(const_neg_1 Reg (load_imm12 -1))
;;
(high_replacement Reg (gen_select_reg (IntCC.SignedLessThan) (value_regs_get x 1) (zero_reg) const_neg_1 (zero_reg)))
(const64 Reg (load_u64_constant 64))
(shamt_128 Reg (rv_andi (value_regs_get y 0) (imm12_const 127))))
(value_regs
(gen_select_reg (IntCC.UnsignedGreaterThanOrEqual) shamt_128 const64 high low)
(gen_select_reg (IntCC.UnsignedGreaterThanOrEqual) shamt_128 const64 high_replacement high))))
(decl gen_amode (Reg Offset32 Type) AMode)
(extern constructor gen_amode gen_amode)
(decl offset32_imm (i32) Offset32)
(extern constructor offset32_imm offset32_imm)
;; helper function to load from memory.
(decl gen_load (Reg Offset32 LoadOP MemFlags Type) Reg)
(rule
(gen_load p offset op flags ty)
(let
((tmp WritableReg (temp_writable_reg ty))
(_ Unit (emit (MInst.Load tmp op flags (gen_amode p offset $I64)))))
tmp))
(decl gen_load_128 (Reg Offset32 MemFlags) ValueRegs)
(rule
(gen_load_128 p offset flags)
(let
((low Reg (gen_load p offset (LoadOP.Ld) flags $I64))
(high Reg (gen_load p (offset32_add offset 8) (LoadOP.Ld) flags $I64)))
(value_regs low high)))
(decl default_memflags () MemFlags)
(extern constructor default_memflags default_memflags)
(decl offset32_add (Offset32 i64) Offset32)
(extern constructor offset32_add offset32_add)
;; helper function to store to memory.
(decl gen_store (Reg Offset32 StoreOP MemFlags Reg) InstOutput)
(rule
(gen_store base offset op flags src)
(side_effect (SideEffectNoResult.Inst (MInst.Store (gen_amode base offset $I64) op flags src)))
)
(decl gen_store_128 (Reg Offset32 MemFlags ValueRegs) InstOutput)
(rule
(gen_store_128 p offset flags src)
(side_effect
(SideEffectNoResult.Inst2
(MInst.Store (gen_amode p offset $I64) (StoreOP.Sd) flags (value_regs_get src 0))
(MInst.Store (gen_amode p (offset32_add offset 8) $I64) (StoreOP.Sd) flags (value_regs_get src 1)))))
(decl valid_atomic_transaction (Type) Type)
(extern extractor valid_atomic_transaction valid_atomic_transaction)
;;helper function.
;;construct an atomic instruction.
(decl gen_atomic (AtomicOP Reg Reg AMO) Reg)
(rule
(gen_atomic op addr src amo)
(let
((tmp WritableReg (temp_writable_reg $I64))
(_ Unit (emit (MInst.Atomic op tmp addr src amo))))
tmp))
;; helper function
(decl get_atomic_rmw_op (Type AtomicRmwOp) AtomicOP)
(rule
(get_atomic_rmw_op $I32 (AtomicRmwOp.Add))
(AtomicOP.AmoaddW))
(rule
(get_atomic_rmw_op $I64 (AtomicRmwOp.Add))
(AtomicOP.AmoaddD))
(rule
(get_atomic_rmw_op $I32 (AtomicRmwOp.And))
(AtomicOP.AmoandW))
(rule
(get_atomic_rmw_op $I64 (AtomicRmwOp.And))
(AtomicOP.AmoandD))
(rule
(get_atomic_rmw_op $I32 (AtomicRmwOp.Or))
(AtomicOP.AmoorW))
(rule
(get_atomic_rmw_op $I64 (AtomicRmwOp.Or))
(AtomicOP.AmoorD))
(rule
(get_atomic_rmw_op $I32 (AtomicRmwOp.Smax))
(AtomicOP.AmomaxW))
(rule
(get_atomic_rmw_op $I64 (AtomicRmwOp.Smax))
(AtomicOP.AmomaxD))
(rule
(get_atomic_rmw_op $I32 (AtomicRmwOp.Smin))
(AtomicOP.AmominW))
(rule
(get_atomic_rmw_op $I64 (AtomicRmwOp.Smin))
(AtomicOP.AmominD))
(rule
(get_atomic_rmw_op $I32 (AtomicRmwOp.Umax))
(AtomicOP.AmomaxuW)
)
(rule
(get_atomic_rmw_op $I64 (AtomicRmwOp.Umax))
(AtomicOP.AmomaxuD))
(rule
(get_atomic_rmw_op $I32 (AtomicRmwOp.Umin))
(AtomicOP.AmominuW))
(rule
(get_atomic_rmw_op $I64 (AtomicRmwOp.Umin))
(AtomicOP.AmominuD))
(rule
(get_atomic_rmw_op $I32 (AtomicRmwOp.Xchg))
(AtomicOP.AmoswapW))
(rule
(get_atomic_rmw_op $I64 (AtomicRmwOp.Xchg))
(AtomicOP.AmoswapD))
(rule
(get_atomic_rmw_op $I32 (AtomicRmwOp.Xor))
(AtomicOP.AmoxorW))
(rule
(get_atomic_rmw_op $I64 (AtomicRmwOp.Xor))
(AtomicOP.AmoxorD))
(decl atomic_amo () AMO)
(extern constructor atomic_amo atomic_amo)
(decl gen_atomic_load (Reg Type) Reg)
(rule
(gen_atomic_load p ty)
(let
((tmp WritableReg (temp_writable_reg $I64))
(_ Unit (emit (MInst.AtomicLoad tmp ty p))))
(writable_reg_to_reg tmp)))
;;;
(decl gen_atomic_store (Reg Type Reg) InstOutput)
(rule
(gen_atomic_store p ty src)
(side_effect (SideEffectNoResult.Inst (MInst.AtomicStore src ty p)))
)
(decl move_f_to_x (Reg Type) Reg)
(extern constructor move_f_to_x move_f_to_x)
(decl move_x_to_f (Reg Type) Reg)
(extern constructor move_x_to_f move_x_to_f)
(decl gen_stack_addr (StackSlot Offset32) Reg )
(extern constructor gen_stack_addr gen_stack_addr)
;;; generate a move and reinterprete the data
;; parameter is "rs" "in_type" "out_type"
(decl gen_moves (ValueRegs Type Type) ValueRegs)
(extern constructor gen_moves gen_moves)
;;
(decl gen_select (Type Reg ValueRegs ValueRegs) ValueRegs)
(rule
(gen_select ty c x y)
(let
((dst VecWritableReg (alloc_vec_writable ty))
;;
(reuslt VecWritableReg (vec_writable_clone dst))
(_ Unit (emit (MInst.Select dst ty c x y))))
(vec_writable_to_regs reuslt)))
;; Parameters are "intcc compare_a compare_b rs1 rs2".
(decl gen_select_reg (IntCC Reg Reg Reg Reg) Reg)
(extern constructor gen_select_reg gen_select_reg)
;; load a constant into reg.
(decl load_u64_constant (u64) Reg)
(extern constructor load_u64_constant load_u64_constant)
;;; clone WritableReg
;;; if not rust compiler will complain about use moved value.
(decl vec_writable_clone (VecWritableReg) VecWritableReg)
(extern constructor vec_writable_clone vec_writable_clone)
(decl vec_writable_to_regs (VecWritableReg) ValueRegs)
(extern constructor vec_writable_to_regs vec_writable_to_regs)
(decl alloc_vec_writable (Type) VecWritableReg)
(extern constructor alloc_vec_writable alloc_vec_writable)
(decl gen_bitselect (Type Reg Reg Reg) Reg)
(rule
(gen_bitselect ty c x y)
(let
((tmp_x Reg (rv_and c x))
;;;inverse condition
(c_inverse Reg (rv_not c))
;;;get all y part.
(tmp_y Reg (rv_and c_inverse y))
;;;get reuslt.
(result Reg (rv_or tmp_x tmp_y)))
result))
(decl gen_int_select (Type IntSelectOP ValueRegs ValueRegs) ValueRegs)
(rule
(gen_int_select ty op x y)
(let
( ;;;
(dst VecWritableReg (alloc_vec_writable ty))
;;;
(_ Unit (emit (MInst.IntSelect op (vec_writable_clone dst) x y ty))))
(vec_writable_to_regs dst)))
(decl udf (TrapCode) InstOutput)
(rule
(udf code)
(side_effect (SideEffectNoResult.Inst (MInst.Udf code))))
(decl load_op (Type) LoadOP)
(extern constructor load_op load_op)
(decl store_op (Type) StoreOP)
(extern constructor store_op store_op)
;; bool is "is_signed"
(decl int_load_op (bool u8) LoadOP)
(rule
(int_load_op $false 8)
(LoadOP.Lbu))
(rule
(int_load_op $true 8)
(LoadOP.Lb))
(rule
(int_load_op $false 16)
(LoadOP.Lhu))
(rule
(int_load_op $true 16)
(LoadOP.Lh))
(rule
(int_load_op $false 32)
(LoadOP.Lwu))
(rule
(int_load_op $true 32)
(LoadOP.Lw))
(rule
(int_load_op _ 64)
(LoadOP.Ld))
;;;; load extern name
(decl load_ext_name (ExternalName i64) Reg)
(extern constructor load_ext_name load_ext_name)
(decl int_convert_2_float_op (Type bool Type) FpuOPRR)
(extern constructor int_convert_2_float_op int_convert_2_float_op)
;;;;
(decl gen_fcvt_int (bool Reg bool Type Type) Reg)
(rule
(gen_fcvt_int is_sat rs is_signed in_type out_type)
(let
((result WritableReg (temp_writable_reg out_type))
(tmp WritableReg (temp_writable_reg $F64))
(_ Unit (emit (MInst.FcvtToInt is_sat result tmp rs is_signed in_type out_type))))
result))
;;; some float binary operation
;;; 1. need move into x reister.
;;; 2. do the operation.
;;; 3. move back.
(decl lower_float_binary (AluOPRRR Reg Reg Type) Reg)
(rule
(lower_float_binary op rs1 rs2 ty)
(let
((x_rs1 Reg (move_f_to_x rs1 ty))
(x_rs2 Reg (move_f_to_x rs2 ty))
;;;
(tmp Reg (alu_rrr op x_rs1 x_rs2)))
;;; move back.
(move_x_to_f tmp ty)))
;;;;
(decl lower_float_bnot (Reg Type) Reg)
(rule
(lower_float_bnot x ty)
(let
(;; move to x register.
(tmp Reg (move_f_to_x x ty))
;; inverse all bits.
(tmp2 Reg (rv_not tmp)))
;; move back to float register.
(move_x_to_f tmp2 ty)))
;;; lower icmp
(decl lower_icmp (IntCC ValueRegs ValueRegs Type) Reg)
(rule 1 (lower_icmp cc x y ty)
(if (signed_cond_code cc))
(gen_icmp cc (ext_int_if_need $true x ty) (ext_int_if_need $true y ty) ty))
(rule (lower_icmp cc x y ty)
(gen_icmp cc (ext_int_if_need $false x ty) (ext_int_if_need $false y ty) ty))
(decl i128_sub (ValueRegs ValueRegs) ValueRegs)
(rule
(i128_sub x y )
(let
(;; low part.
(low Reg (rv_sub (value_regs_get x 0) (value_regs_get y 0)))
;; compute borrow.
(borrow Reg (rv_sltu (value_regs_get x 0) low))
;;
(high_tmp Reg (rv_sub (value_regs_get x 1) (value_regs_get y 1)))
;;
(high Reg (rv_sub high_tmp borrow)))
(value_regs low high)))
;;; Returns the sum in the first register, and the overflow test in the second.
(decl lower_uadd_overflow (Reg Reg Type) ValueRegs)
(rule 1
(lower_uadd_overflow x y $I64)
(let ((tmp Reg (rv_add x y))
(test Reg (gen_icmp (IntCC.UnsignedLessThan) tmp x $I64)))
(value_regs tmp test)))
(rule
(lower_uadd_overflow x y (fits_in_32 ty))
(let ((tmp_x Reg (ext_int_if_need $false x ty))
(tmp_y Reg (ext_int_if_need $false y ty))
(sum Reg (rv_add tmp_x tmp_y))
(test Reg (rv_srli sum (imm12_const (ty_bits ty)))))
(value_regs sum test)))
(decl label_to_br_target (MachLabel) BranchTarget)
(extern constructor label_to_br_target label_to_br_target)
(decl gen_jump (MachLabel) MInst)
(rule
(gen_jump v)
(MInst.Jal (label_to_br_target v)))
(decl vec_label_get (VecMachLabel u8) MachLabel )
(extern constructor vec_label_get vec_label_get)
(decl partial lower_branch (Inst VecMachLabel) Unit)
(rule (lower_branch (jump _) targets )
(emit_side_effect (SideEffectNoResult.Inst (gen_jump (vec_label_get targets 0)))))
;;; cc a b targets Type
(decl lower_br_icmp (IntCC ValueRegs ValueRegs VecMachLabel Type) Unit)
(extern constructor lower_br_icmp lower_br_icmp)
;; int scalar zero regs.
(decl int_zero_reg (Type) ValueRegs)
(extern constructor int_zero_reg int_zero_reg)
(decl lower_cond_br (IntCC ValueRegs VecMachLabel Type) Unit)
(extern constructor lower_cond_br lower_cond_br)
(decl intcc_to_extend_op (IntCC) ExtendOp)
(extern constructor intcc_to_extend_op intcc_to_extend_op)
;; Normalize a value for comparision.
;;
;; This ensures that types smaller than a register don't accidentally
;; pass undefined high bits when being compared as a full register.
(decl normalize_cmp_value (Type ValueRegs ExtendOp) ValueRegs)
(rule 1 (normalize_cmp_value (fits_in_32 ity) r op)
(extend r op ity $I64))
(rule (normalize_cmp_value $I64 r _) r)
(rule (normalize_cmp_value $I128 r _) r)
(decl normalize_fcvt_from_int (ValueRegs Type ExtendOp) ValueRegs)
(rule 2 (normalize_fcvt_from_int r (fits_in_16 ty) op)
(extend r op ty $I64))
(rule 1 (normalize_fcvt_from_int r _ _)
r)
;; Convert a truthy value, possibly of more than one register (an
;; I128), to one register. If narrower than 64 bits, must have already
;; been masked (e.g. by `normalize_cmp_value`).
(decl truthy_to_reg (Type ValueRegs) Reg)
(rule 1 (truthy_to_reg (fits_in_64 _) regs)
(value_regs_get regs 0))
(rule 0 (truthy_to_reg $I128 regs)
(let ((lo Reg (value_regs_get regs 0))
(hi Reg (value_regs_get regs 1)))
(rv_or lo hi)))
;; Default behavior for branching based on an input value.
(rule
(lower_branch (brif v @ (value_type ty) _ _) targets)
(lower_cond_br (IntCC.NotEqual) (normalize_cmp_value ty v (ExtendOp.Zero)) targets ty))
;; Special case for SI128 to reify the comparison value and branch on it.
(rule 2
(lower_branch (brif v @ (value_type $I128) _ _) targets)
(let ((zero ValueRegs (value_regs (zero_reg) (zero_reg)))
(cmp Reg (gen_icmp (IntCC.NotEqual) v zero $I128)))
(lower_cond_br (IntCC.NotEqual) cmp targets $I64)))
;; Branching on the result of an icmp
(rule 1
(lower_branch (brif (maybe_uextend (icmp cc a @ (value_type ty) b)) _ _) targets)
(lower_br_icmp cc a b targets ty))
;; Branching on the result of an fcmp
(rule 1
(lower_branch (brif (maybe_uextend (fcmp cc a @ (value_type ty) b)) _ _) targets)
(if-let $true (floatcc_unordered cc))
(let ((then BranchTarget (label_to_br_target (vec_label_get targets 0)))
(else BranchTarget (label_to_br_target (vec_label_get targets 1))))
(emit_side_effect (cond_br (emit_fcmp (floatcc_inverse cc) ty a b) else then))))
(rule 1
(lower_branch (brif (maybe_uextend (fcmp cc a @ (value_type ty) b)) _ _) targets)
(if-let $false (floatcc_unordered cc))
(let ((then BranchTarget (label_to_br_target (vec_label_get targets 0)))
(else BranchTarget (label_to_br_target (vec_label_get targets 1))))
(emit_side_effect (cond_br (emit_fcmp cc ty a b) then else))))
;;;
(decl lower_br_table (Reg VecMachLabel) Unit)
(extern constructor lower_br_table lower_br_table)
(rule
(lower_branch (br_table index _) targets)
(lower_br_table index targets))
(decl load_ra () Reg)
(extern constructor load_ra load_ra)
(decl gen_rev8 (Reg) Reg)
(rule 1
(gen_rev8 rs)
(if-let $true (has_zbb))
(rv_rev8 rs))
(rule
(gen_rev8 rs)
(if-let $false (has_zbb))
(let
((rd WritableReg (temp_writable_reg $I64))
(tmp WritableReg (temp_writable_reg $I64))
(step WritableReg (temp_writable_reg $I64))
(_ Unit (emit (MInst.Rev8 rs step tmp rd))))
(writable_reg_to_reg rd)))
(decl gen_brev8 (Reg Type) Reg)
(rule 1
(gen_brev8 rs _)
(if-let $true (has_zbkb))
(rv_brev8 rs))
(rule
(gen_brev8 rs ty)
(if-let $false (has_zbkb))
(let
((tmp WritableReg (temp_writable_reg $I64))
(tmp2 WritableReg (temp_writable_reg $I64))
(step WritableReg (temp_writable_reg $I64))
(rd WritableReg (temp_writable_reg $I64))
(_ Unit (emit (MInst.Brev8 rs ty step tmp tmp2 rd))))
(writable_reg_to_reg rd)))
;; Negates x
;; Equivalent to 0 - x
(decl neg (Type ValueRegs) ValueRegs)
(rule 1 (neg (fits_in_64 (ty_int ty)) val)
(value_reg
(rv_neg (value_regs_get val 0))))
(rule 2 (neg $I128 val)
(i128_sub (value_regs_zero) val))
;; Selects the greatest of two registers as signed values.
(decl max (Type Reg Reg) Reg)
(rule (max (fits_in_64 (ty_int ty)) x y)
(if-let $true (has_zbb))
(rv_max x y))
(rule (max (fits_in_64 (ty_int ty)) x y)
(if-let $false (has_zbb))
(gen_select_reg (IntCC.SignedGreaterThan) x y x y))
(decl lower_iabs (Type Reg) Reg)
; I64 and lower
; Generate the following code:
; sext.{b,h,w} a0, a0
; neg a1, a0
; max a0, a0, a1
(rule (lower_iabs (fits_in_64 ty) val)
(let ((extended Reg (ext_int_if_need $true val ty))
(negated Reg (neg $I64 extended)))
(max $I64 extended negated)))
(decl gen_trapif (Reg TrapCode) InstOutput)
(rule
(gen_trapif test trap_code)
(side_effect (SideEffectNoResult.Inst (MInst.TrapIf test trap_code))))
(decl gen_trapifc (IntCC Reg Reg TrapCode) InstOutput)
(rule
(gen_trapifc cc a b trap_code)
(side_effect (SideEffectNoResult.Inst (MInst.TrapIfC a b cc trap_code))))
(decl shift_int_to_most_significant (Reg Type) Reg)
(extern constructor shift_int_to_most_significant shift_int_to_most_significant)
;;; generate div overflow.
(decl gen_div_overflow (Reg Reg Type) InstOutput)
(rule
(gen_div_overflow rs1 rs2 ty)
(let
((r_const_neg_1 Reg (load_imm12 -1))
(r_const_min Reg (rv_slli (load_imm12 1) (imm12_const 63)))
(tmp_rs1 Reg (shift_int_to_most_significant rs1 ty))
(t1 Reg (gen_icmp (IntCC.Equal) r_const_neg_1 rs2 ty))
(t2 Reg (gen_icmp (IntCC.Equal) r_const_min tmp_rs1 ty))
(test Reg (rv_and t1 t2)))
(gen_trapif test (TrapCode.IntegerOverflow))))
(decl gen_div_by_zero (Reg) InstOutput)
(rule
(gen_div_by_zero r)
(gen_trapifc (IntCC.Equal) (zero_reg) r (TrapCode.IntegerDivisionByZero)))
;;;; Helpers for Emitting Calls ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(decl gen_call (SigRef ExternalName RelocDistance ValueSlice) InstOutput)
(extern constructor gen_call gen_call)
(decl gen_call_indirect (SigRef Value ValueSlice) InstOutput)
(extern constructor gen_call_indirect gen_call_indirect)
;;; this is trying to imitate aarch64 `madd` instruction.
(decl madd (Reg Reg Reg) Reg)
(rule
(madd n m a)
(let
((t Reg (rv_mul n m)))
(rv_add t a)))
;;;; Helpers for bmask ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(decl lower_bmask (Type Type ValueRegs) ValueRegs)
;; Produces -1 if the 64-bit value is non-zero, and 0 otherwise.
;; If the type is smaller than 64 bits, we need to mask off the
;; high bits.
(rule
0
(lower_bmask (fits_in_64 _) (fits_in_64 in_ty) val)
(let ((input Reg (normalize_cmp_value in_ty val (ExtendOp.Zero)))
(non_zero Reg (rv_snez input)))
(value_reg (rv_neg non_zero))))
;; Bitwise-or the two registers that make up the 128-bit value, then recurse as
;; though it was a 64-bit value.
(rule
1
(lower_bmask (fits_in_64 ty) $I128 val)
(let ((lo Reg (value_regs_get val 0))
(hi Reg (value_regs_get val 1))
(combined Reg (rv_or lo hi)))
(lower_bmask ty $I64 (value_reg combined))))
;; Conversion of one 64-bit value to a 128-bit one. Duplicate the result of the
;; bmask of the 64-bit value into both result registers of the i128.
(rule
2
(lower_bmask $I128 (fits_in_64 in_ty) val)
(let ((res ValueRegs (lower_bmask $I64 in_ty val)))
(value_regs (value_regs_get res 0) (value_regs_get res 0))))
;; Conversion of one 64-bit value to a 128-bit one. Duplicate the result of
;; bmasking the 128-bit value to a 64-bit value into both registers of the
;; 128-bit result.
(rule
3
(lower_bmask $I128 $I128 val)
(let ((res ValueRegs (lower_bmask $I64 $I128 val)))
(value_regs (value_regs_get res 0) (value_regs_get res 0))))
;;;; Helpers for physical registers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(decl gen_mov_from_preg (PReg) Reg)
(rule
(gen_mov_from_preg rm)
(let ((rd WritableReg (temp_writable_reg $I64))
(_ Unit (emit (MInst.MovFromPReg rd rm))))
rd))
(decl fp_reg () PReg)
(extern constructor fp_reg fp_reg)
(decl sp_reg () PReg)
(extern constructor sp_reg sp_reg)
;; Helper for creating the zero register.
(decl zero_reg () Reg)
(extern constructor zero_reg zero_reg)
(decl value_regs_zero () ValueRegs)
(rule (value_regs_zero)
(value_regs (imm $I64 0) (imm $I64 0)))
(decl writable_zero_reg () WritableReg)
(extern constructor writable_zero_reg writable_zero_reg)
;;;; Helpers for floating point comparisons ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(decl not (Reg) Reg)
(rule (not x) (rv_xori x (imm_from_bits 1)))
(decl is_not_nan (Type Reg) Reg)
(rule (is_not_nan ty a) (rv_feq ty a a))
(decl ordered (Type Reg Reg) Reg)
(rule (ordered ty a b) (rv_and (is_not_nan ty a) (is_not_nan ty b)))
(type CmpResult (enum
(Result
(result Reg)
(invert bool))))
;; Wrapper for the common case when constructing comparison results. It assumes
;; that the result isn't negated.
(decl cmp_result (Reg) CmpResult)
(rule (cmp_result result) (CmpResult.Result result $false))
;; Wrapper for the case where it's more convenient to construct the negated
;; version of the comparison.
(decl cmp_result_invert (Reg) CmpResult)
(rule (cmp_result_invert result) (CmpResult.Result result $true))
;; Consume a CmpResult, producing a branch on its result.
(decl cond_br (CmpResult BranchTarget BranchTarget) SideEffectNoResult)
(rule (cond_br cmp then else)
(SideEffectNoResult.Inst
(MInst.CondBr then else (cmp_integer_compare cmp))))
;; Construct an IntegerCompare value.
(decl int_compare (IntCC Reg Reg) IntegerCompare)
(extern constructor int_compare int_compare)
;; Convert a comparison into a branch test.
(decl cmp_integer_compare (CmpResult) IntegerCompare)
(rule
(cmp_integer_compare (CmpResult.Result res $false))
(int_compare (IntCC.NotEqual) res (zero_reg)))
(rule
(cmp_integer_compare (CmpResult.Result res $true))
(int_compare (IntCC.Equal) res (zero_reg)))
;; Convert a comparison into a boolean value.
(decl cmp_value (CmpResult) Reg)
(rule (cmp_value (CmpResult.Result res $false)) res)
(rule (cmp_value (CmpResult.Result res $true)) (not res))
;; Compare two floating point numbers and return a zero/non-zero result.
(decl emit_fcmp (FloatCC Type Reg Reg) CmpResult)
;; a is not nan && b is not nan
(rule
(emit_fcmp (FloatCC.Ordered) ty a b)
(cmp_result (ordered ty a b)))
;; a is nan || b is nan
;; == !(a is not nan && b is not nan)
(rule
(emit_fcmp (FloatCC.Unordered) ty a b)
(cmp_result_invert (ordered ty a b)))
;; a == b
(rule
(emit_fcmp (FloatCC.Equal) ty a b)
(cmp_result (rv_feq ty a b)))
;; a != b
;; == !(a == b)
(rule
(emit_fcmp (FloatCC.NotEqual) ty a b)
(cmp_result_invert (rv_feq ty a b)))
;; a < b || a > b
(rule
(emit_fcmp (FloatCC.OrderedNotEqual) ty a b)
(cmp_result (rv_or (rv_flt ty a b) (rv_fgt ty a b))))
;; !(ordered a b) || a == b
(rule
(emit_fcmp (FloatCC.UnorderedOrEqual) ty a b)
(cmp_result (rv_or (not (ordered ty a b)) (rv_feq ty a b))))
;; a < b
(rule
(emit_fcmp (FloatCC.LessThan) ty a b)
(cmp_result (rv_flt ty a b)))
;; a <= b
(rule
(emit_fcmp (FloatCC.LessThanOrEqual) ty a b)
(cmp_result (rv_fle ty a b)))
;; a > b
(rule
(emit_fcmp (FloatCC.GreaterThan) ty a b)
(cmp_result (rv_fgt ty a b)))
;; a >= b
(rule
(emit_fcmp (FloatCC.GreaterThanOrEqual) ty a b)
(cmp_result (rv_fge ty a b)))
;; !(ordered a b) || a < b
;; == !(ordered a b && a >= b)
(rule
(emit_fcmp (FloatCC.UnorderedOrLessThan) ty a b)
(cmp_result_invert (rv_and (ordered ty a b) (rv_fge ty a b))))
;; !(ordered a b) || a <= b
;; == !(ordered a b && a > b)
(rule
(emit_fcmp (FloatCC.UnorderedOrLessThanOrEqual) ty a b)
(cmp_result_invert (rv_and (ordered ty a b) (rv_fgt ty a b))))
;; !(ordered a b) || a > b
;; == !(ordered a b && a <= b)
(rule
(emit_fcmp (FloatCC.UnorderedOrGreaterThan) ty a b)
(cmp_result_invert (rv_and (ordered ty a b) (rv_fle ty a b))))
;; !(ordered a b) || a >= b
;; == !(ordered a b && a < b)
(rule
(emit_fcmp (FloatCC.UnorderedOrGreaterThanOrEqual) ty a b)
(cmp_result_invert (rv_and (ordered ty a b) (rv_flt ty a b))))