blob: e4ab3354e06f814e8dbeeac07352f7fd056f082e [file] [log] [blame]
;; 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)
(stack_bytes_to_pop u32))
(Extend
(rd WritableReg)
(rn Reg)
(signed bool)
(from_bits u8)
(to_bits u8))
(AdjustSp
(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))
;; 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))
(VecAluRRRImm5
(op VecAluOpRRRImm5)
(vd WritableReg)
(vd_src Reg)
(vs2 Reg)
(imm Imm5)
(mask VecOpMasking)
(vstate VState))
(VecAluRRR
(op VecAluOpRRR)
(vd WritableReg)
(vs2 Reg)
(vs1 Reg)
(mask VecOpMasking)
(vstate VState))
(VecAluRRImm5
(op VecAluOpRRImm5)
(vd WritableReg)
(vs2 Reg)
(imm Imm5)
(mask VecOpMasking)
(vstate VState))
(VecAluRR
(op VecAluOpRR)
(vd WritableReg)
(vs Reg)
(mask VecOpMasking)
(vstate VState))
(VecAluRImm5
(op VecAluOpRImm5)
(vd WritableReg)
(imm Imm5)
(mask VecOpMasking)
(vstate VState))
(VecSetState
(rd WritableReg)
(vstate VState))
(VecLoad
(eew VecElementWidth)
(to WritableReg)
(from VecAMode)
(flags MemFlags)
(mask VecOpMasking)
(vstate VState))
(VecStore
(eew VecElementWidth)
(to VecAMode)
(from Reg)
(flags MemFlags)
(mask VecOpMasking)
(vstate VState))
))
(type FloatSelectOP (enum
(Max)
(Min)
))
(type FloatRoundOP (enum
(Nearest)
(Ceil)
(Floor)
(Trunc)
))
(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 instruction’s 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 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 Imm5 (primitive Imm5))
(type Imm20 (primitive Imm20))
(type Imm3 (primitive Imm3))
(type BranchTarget (primitive BranchTarget))
(type OptionFloatRoundingMode (primitive OptionFloatRoundingMode))
(type VecU8 (primitive VecU8))
(type AMO (primitive AMO))
(type VecMachLabel extern (enum))
;;;; Newtypes for Different Register Classes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(type XReg (primitive XReg))
(type WritableXReg (primitive WritableXReg))
(type FReg (primitive FReg))
(type WritableFReg (primitive WritableFReg))
(type VReg (primitive VReg))
(type WritableVReg (primitive WritableVReg))
;; Construct a new `XReg` from a `Reg`.
;;
;; Asserts that the register has a Integer RegClass.
(decl xreg_new (Reg) XReg)
(extern constructor xreg_new xreg_new)
(convert Reg XReg xreg_new)
;; Construct a new `WritableXReg` from a `WritableReg`.
;;
;; Asserts that the register has a Integer RegClass.
(decl writable_xreg_new (WritableReg) WritableXReg)
(extern constructor writable_xreg_new writable_xreg_new)
(convert WritableReg WritableXReg writable_xreg_new)
;; Put a value into a XReg.
;;
;; Asserts that the value goes into a XReg.
(decl put_in_xreg (Value) XReg)
(rule (put_in_xreg val) (xreg_new (put_in_reg val)))
(convert Value XReg put_in_xreg)
;; Construct an `InstOutput` out of a single XReg register.
(decl output_xreg (XReg) InstOutput)
(rule (output_xreg x) (output_reg x))
(convert XReg InstOutput output_xreg)
;; Convert a `WritableXReg` to an `XReg`.
(decl pure writable_xreg_to_xreg (WritableXReg) XReg)
(extern constructor writable_xreg_to_xreg writable_xreg_to_xreg)
(convert WritableXReg XReg writable_xreg_to_xreg)
;; Convert a `WritableXReg` to an `WritableReg`.
(decl pure writable_xreg_to_writable_reg (WritableXReg) WritableReg)
(extern constructor writable_xreg_to_writable_reg writable_xreg_to_writable_reg)
(convert WritableXReg WritableReg writable_xreg_to_writable_reg)
;; Convert a `WritableXReg` to an `Reg`.
(decl pure writable_xreg_to_reg (WritableXReg) Reg)
(rule (writable_xreg_to_reg x) (writable_xreg_to_writable_reg x))
(convert WritableXReg Reg writable_xreg_to_reg)
;; Convert an `XReg` to a `Reg`.
(decl pure xreg_to_reg (XReg) Reg)
(extern constructor xreg_to_reg xreg_to_reg)
(convert XReg Reg xreg_to_reg)
;; Convert a `XReg` to a `ValueRegs`.
(decl xreg_to_value_regs (XReg) ValueRegs)
(rule (xreg_to_value_regs x) (value_reg x))
(convert XReg ValueRegs xreg_to_reg)
;; Convert a `WritableXReg` to a `ValueRegs`.
(decl writable_xreg_to_value_regs (WritableXReg) ValueRegs)
(rule (writable_xreg_to_value_regs x) (value_reg x))
(convert WritableXReg ValueRegs writable_xreg_to_value_regs)
;; Allocates a new `WritableXReg`.
(decl temp_writable_xreg () WritableXReg)
(rule (temp_writable_xreg) (temp_writable_reg $I64))
;; Construct a new `FReg` from a `Reg`.
;;
;; Asserts that the register has a Float RegClass.
(decl freg_new (Reg) FReg)
(extern constructor freg_new freg_new)
(convert Reg FReg freg_new)
;; Construct a new `WritableFReg` from a `WritableReg`.
;;
;; Asserts that the register has a Float RegClass.
(decl writable_freg_new (WritableReg) WritableFReg)
(extern constructor writable_freg_new writable_freg_new)
(convert WritableReg WritableFReg writable_freg_new)
;; Put a value into a FReg.
;;
;; Asserts that the value goes into a FReg.
(decl put_in_freg (Value) FReg)
(rule (put_in_freg val) (freg_new (put_in_reg val)))
(convert Value FReg put_in_freg)
;; Construct an `InstOutput` out of a single FReg register.
(decl output_freg (FReg) InstOutput)
(rule (output_freg x) (output_reg x))
(convert FReg InstOutput output_freg)
;; Convert a `WritableFReg` to an `FReg`.
(decl pure writable_freg_to_freg (WritableFReg) FReg)
(extern constructor writable_freg_to_freg writable_freg_to_freg)
(convert WritableFReg FReg writable_freg_to_freg)
;; Convert a `WritableFReg` to an `WritableReg`.
(decl pure writable_freg_to_writable_reg (WritableFReg) WritableReg)
(extern constructor writable_freg_to_writable_reg writable_freg_to_writable_reg)
(convert WritableFReg WritableReg writable_freg_to_writable_reg)
;; Convert a `WritableFReg` to an `Reg`.
(decl pure writable_freg_to_reg (WritableFReg) Reg)
(rule (writable_freg_to_reg x) (writable_freg_to_writable_reg x))
(convert WritableFReg Reg writable_freg_to_reg)
;; Convert an `FReg` to a `Reg`.
(decl pure freg_to_reg (FReg) Reg)
(extern constructor freg_to_reg freg_to_reg)
(convert FReg Reg freg_to_reg)
;; Convert a `FReg` to a `ValueRegs`.
(decl freg_to_value_regs (FReg) ValueRegs)
(rule (freg_to_value_regs x) (value_reg x))
(convert FReg ValueRegs xreg_to_reg)
;; Convert a `WritableFReg` to a `ValueRegs`.
(decl writable_freg_to_value_regs (WritableFReg) ValueRegs)
(rule (writable_freg_to_value_regs x) (value_reg x))
(convert WritableFReg ValueRegs writable_freg_to_value_regs)
;; Allocates a new `WritableFReg`.
(decl temp_writable_freg () WritableFReg)
(rule (temp_writable_freg) (temp_writable_reg $F64))
;; Construct a new `VReg` from a `Reg`.
;;
;; Asserts that the register has a Vector RegClass.
(decl vreg_new (Reg) VReg)
(extern constructor vreg_new vreg_new)
(convert Reg VReg vreg_new)
;; Construct a new `WritableVReg` from a `WritableReg`.
;;
;; Asserts that the register has a Vector RegClass.
(decl writable_vreg_new (WritableReg) WritableVReg)
(extern constructor writable_vreg_new writable_vreg_new)
(convert WritableReg WritableVReg writable_vreg_new)
;; Put a value into a VReg.
;;
;; Asserts that the value goes into a VReg.
(decl put_in_vreg (Value) VReg)
(rule (put_in_vreg val) (vreg_new (put_in_reg val)))
(convert Value VReg put_in_vreg)
;; Construct an `InstOutput` out of a single VReg register.
(decl output_vreg (VReg) InstOutput)
(rule (output_vreg x) (output_reg x))
(convert VReg InstOutput output_vreg)
;; Convert a `WritableVReg` to an `VReg`.
(decl pure writable_vreg_to_vreg (WritableVReg) VReg)
(extern constructor writable_vreg_to_vreg writable_vreg_to_vreg)
(convert WritableVReg VReg writable_vreg_to_vreg)
;; Convert a `WritableVReg` to an `WritableReg`.
(decl pure writable_vreg_to_writable_reg (WritableVReg) WritableReg)
(extern constructor writable_vreg_to_writable_reg writable_vreg_to_writable_reg)
(convert WritableVReg WritableReg writable_vreg_to_writable_reg)
;; Convert a `WritableVReg` to an `Reg`.
(decl pure writable_vreg_to_reg (WritableVReg) Reg)
(rule (writable_vreg_to_reg x) (writable_vreg_to_writable_reg x))
(convert WritableVReg Reg writable_vreg_to_reg)
;; Convert an `VReg` to a `Reg`.
(decl pure vreg_to_reg (VReg) Reg)
(extern constructor vreg_to_reg vreg_to_reg)
(convert VReg Reg vreg_to_reg)
;; Convert a `VReg` to a `ValueRegs`.
(decl vreg_to_value_regs (VReg) ValueRegs)
(rule (vreg_to_value_regs x) (value_reg x))
(convert VReg ValueRegs xreg_to_reg)
;; Convert a `WritableVReg` to a `ValueRegs`.
(decl writable_vreg_to_value_regs (WritableVReg) ValueRegs)
(rule (writable_vreg_to_value_regs x) (value_reg x))
(convert WritableVReg ValueRegs writable_vreg_to_value_regs)
;; Allocates a new `WritableVReg`.
(decl temp_writable_vreg () WritableVReg)
(rule (temp_writable_vreg) (temp_writable_reg $I8X16))
;; Converters
(convert u8 i32 u8_as_i32)
(decl u8_as_i32 (u8) i32)
(extern constructor u8_as_i32 u8_as_i32)
;; 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 WritableXReg (temp_writable_xreg))
(tmp2 WritableFReg (temp_writable_freg))
(_ 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 WritableXReg (temp_writable_xreg))
(_ 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 WritableXReg (temp_writable_xreg))
(_ 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 (XReg XReg) XReg)
(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 (XReg Imm12) XReg)
(rule (rv_addi rs1 imm)
(alu_rr_imm12 (AluOPRRI.Addi) rs1 imm))
;; Helper for emitting the `sub` instruction.
;; rd ← rs1 - rs2
(decl rv_sub (XReg XReg) XReg)
(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 (XReg) XReg)
(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 (XReg XReg) XReg)
(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 (XReg Imm12) XReg)
(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 (XReg XReg) XReg)
(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 (XReg Imm12) XReg)
(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 (XReg XReg) XReg)
(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 (XReg Imm12) XReg)
(rule (rv_srai rs1 imm)
(alu_rr_imm12 (AluOPRRI.Srai) rs1 imm))
;; Helper for emitting the `or` instruction.
;; rd ← rs1 ∨ rs2
(decl rv_or (XReg XReg) XReg)
(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 (XReg Imm12) XReg)
(rule (rv_ori rs1 imm)
(alu_rr_imm12 (AluOPRRI.Ori) rs1 imm))
;; Helper for emitting the `xor` instruction.
;; rd ← rs1 ⊕ rs2
(decl rv_xor (XReg XReg) XReg)
(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 (XReg Imm12) XReg)
(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 (XReg) XReg)
(rule (rv_not rs1)
(rv_xori rs1 (imm12_const -1)))
;; Helper for emitting the `and` instruction.
;; rd ← rs1 ∧ rs2
(decl rv_and (XReg XReg) XReg)
(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 (XReg Imm12) XReg)
(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 (XReg XReg) XReg)
(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 (XReg) XReg)
(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 (XReg Imm12) XReg)
(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 (XReg) XReg)
(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 (XReg XReg) XReg)
(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 (XReg Imm12) XReg)
(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 (XReg) XReg)
(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 (XReg XReg) XReg)
(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 (XReg XReg) XReg)
(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 (XReg Imm12) XReg)
(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 (XReg XReg) XReg)
(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 (XReg Imm12) XReg)
(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 (XReg XReg) XReg)
(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 (XReg Imm12) XReg)
(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 (XReg XReg) XReg)
(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 (XReg XReg) XReg)
(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 (XReg XReg) XReg)
(rule (rv_mulhu rs1 rs2)
(alu_rrr (AluOPRRR.Mulhu) rs1 rs2))
;; Helper for emitting the `div` instruction.
;; rd ← rs1 ÷ rs2
(decl rv_div (XReg XReg) XReg)
(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 (XReg XReg) XReg)
(rule (rv_divu rs1 rs2)
(alu_rrr (AluOPRRR.DivU) rs1 rs2))
;; Helper for emitting the `rem` instruction.
;; rd ← rs1 mod rs2
(decl rv_rem (XReg XReg) XReg)
(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 (XReg XReg) XReg)
(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 (XReg XReg) XReg)
(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 (XReg XReg) XReg)
(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 (XReg XReg) XReg)
(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 (XReg XReg) XReg)
(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 (XReg XReg) XReg)
(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 FReg FReg) FReg)
(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 FReg FReg) FReg)
(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 FReg FReg) FReg)
(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 FReg FReg) FReg)
(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 FReg) FReg)
(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 FReg FReg FReg) FReg)
(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 `fmv.x.w` instruction.
(decl rv_fmvxw (FReg) XReg)
(rule (rv_fmvxw r) (fpu_rr (FpuOPRR.FmvXW) $I32 r))
;; Helper for emitting the `fmv.x.d` instruction.
(decl rv_fmvxd (FReg) XReg)
(rule (rv_fmvxd r) (fpu_rr (FpuOPRR.FmvXD) $I64 r))
;; Helper for emitting the `fmv.w.x` instruction.
(decl rv_fmvwx (XReg) FReg)
(rule (rv_fmvwx r) (fpu_rr (FpuOPRR.FmvWX) $F32 r))
;; Helper for emitting the `fmv.d.x` instruction.
(decl rv_fmvdx (XReg) FReg)
(rule (rv_fmvdx r) (fpu_rr (FpuOPRR.FmvDX) $F64 r))
;; Helper for emitting the `fcvt.d.s` ("Float Convert Double to Single") instruction.
(decl rv_fcvtds (FReg) FReg)
(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 (FReg) FReg)
(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 FReg FReg) FReg)
(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 FReg FReg) FReg)
(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 FReg) FReg)
(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 FReg FReg) FReg)
(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 FReg) FReg)
(rule (rv_fabs ty rs1) (rv_fsgnjx ty rs1 rs1))
;; Helper for emitting the `feq` ("Float Equal") instruction.
(decl rv_feq (Type FReg FReg) XReg)
(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 FReg FReg) XReg)
(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 FReg FReg) XReg)
(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 FReg FReg) XReg)
(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 FReg FReg) XReg)
(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 (XReg XReg) XReg)
(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 (XReg) XReg)
(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 (XReg Imm12) XReg)
(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 (XReg XReg) XReg)
(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 (XReg XReg) XReg)
(rule (rv_orn rs1 rs2)
(alu_rrr (AluOPRRR.Orn) rs1 rs2))
;; Helper for emitting the `clz` ("Count Leading Zero Bits") instruction.
(decl rv_clz (XReg) XReg)
(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 (XReg) XReg)
(rule (rv_clzw rs1)
(alu_rr_funct12 (AluOPRRI.Clzw) rs1))
;; Helper for emitting the `ctz` ("Count Trailing Zero Bits") instruction.
(decl rv_ctz (XReg) XReg)
(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 (XReg) XReg)
(rule (rv_ctzw rs1)
(alu_rr_funct12 (AluOPRRI.Ctzw) rs1))
;; Helper for emitting the `cpop` ("Count Population") instruction.
(decl rv_cpop (XReg) XReg)
(rule (rv_cpop rs1)
(alu_rr_funct12 (AluOPRRI.Cpop) rs1))
;; Helper for emitting the `max` instruction.
(decl rv_max (XReg XReg) XReg)
(rule (rv_max rs1 rs2)
(alu_rrr (AluOPRRR.Max) rs1 rs2))
;; Helper for emitting the `sext.b` instruction.
(decl rv_sextb (XReg) XReg)
(rule (rv_sextb rs1)
(alu_rr_imm12 (AluOPRRI.Sextb) rs1 (imm12_const 0)))
;; Helper for emitting the `sext.h` instruction.
(decl rv_sexth (XReg) XReg)
(rule (rv_sexth rs1)
(alu_rr_imm12 (AluOPRRI.Sexth) rs1 (imm12_const 0)))
;; Helper for emitting the `zext.h` instruction.
(decl rv_zexth (XReg) XReg)
(rule (rv_zexth rs1)
(alu_rr_imm12 (AluOPRRI.Zexth) rs1 (imm12_const 0)))
;; Helper for emitting the `rol` ("Rotate Left") instruction.
(decl rv_rol (XReg XReg) XReg)
(rule (rv_rol rs1 rs2)
(alu_rrr (AluOPRRR.Rol) rs1 rs2))
;; Helper for emitting the `rolw` ("Rotate Left Word") instruction.
(decl rv_rolw (XReg XReg) XReg)
(rule (rv_rolw rs1 rs2)
(alu_rrr (AluOPRRR.Rolw) rs1 rs2))
;; Helper for emitting the `ror` ("Rotate Right") instruction.
(decl rv_ror (XReg XReg) XReg)
(rule (rv_ror rs1 rs2)
(alu_rrr (AluOPRRR.Ror) rs1 rs2))
;; Helper for emitting the `rorw` ("Rotate Right Word") instruction.
(decl rv_rorw (XReg XReg) XReg)
(rule (rv_rorw rs1 rs2)
(alu_rrr (AluOPRRR.Rorw) rs1 rs2))
;; Helper for emitting the `rev8` ("Byte Reverse") instruction.
(decl rv_rev8 (XReg) XReg)
(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 (XReg) XReg)
(rule (rv_brev8 rs1)
(alu_rr_funct12 (AluOPRRI.Brev8) rs1))
;; Helper for emitting the `bseti` ("Single-Bit Set Immediate") instruction.
(decl rv_bseti (XReg Imm12) XReg)
(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 (XReg XReg) XReg)
(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 (XReg XReg) XReg)
(rule (rv_packw rs1 rs2)
(alu_rrr (AluOPRRR.Packw) rs1 rs2))
;; Generate a mask for the bit-width of the given type
(decl pure shift_mask (Type) u64)
(rule (shift_mask ty) (u64_sub (ty_bits (lane_type ty)) 1))
;; 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 u64) 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)
(decl pure partial u64_to_imm12 (u64) Imm12)
(rule (u64_to_imm12 (imm12_from_u64 n)) n)
;; Imm5 Extractors
(decl imm5_from_u64 (Imm5) u64)
(extern extractor imm5_from_u64 imm5_from_u64)
;; Construct a Imm5 from an i8
(decl pure partial imm5_from_i8 (i8) Imm5)
(extern constructor imm5_from_i8 imm5_from_i8)
;; Extractor that matches a `Value` equivalent to a replicated Imm5 on all lanes.
;; TODO(#6527): Try matching vconst here as well
(decl replicated_imm5 (Imm5) Value)
(extractor (replicated_imm5 n)
(def_inst (splat (iconst (u64_from_imm64 (imm5_from_u64 n))))))
;; UImm5 Helpers
;; Extractor that matches a `Value` equivalent to a replicated UImm5 on all lanes.
;; TODO(#6527): Try matching vconst here as well
(decl replicated_uimm5 (UImm5) Value)
(extractor (replicated_uimm5 n)
(def_inst (splat (uimm5_from_value n))))
;; Helper to go directly from a `Value`, when it's an `iconst`, to an `UImm5`.
(decl uimm5_from_value (UImm5) Value)
(extractor (uimm5_from_value n)
(iconst (u64_from_imm64 (uimm5_from_u64 n))))
;; Extract a `UImm5` from an `u8`.
(decl pure partial uimm5_from_u8 (UImm5) u8)
(extern extractor uimm5_from_u8 uimm5_from_u8)
;; Extract a `UImm5` from an `u64`.
(decl pure partial uimm5_from_u64 (UImm5) u64)
(extern extractor uimm5_from_u64 uimm5_from_u64)
;; Convert a `u64` into an `UImm5`
(decl pure partial u64_to_uimm5 (u64) UImm5)
(rule (u64_to_uimm5 (uimm5_from_u64 n)) n)
(decl uimm5_bitcast_to_imm5 (UImm5) Imm5)
(extern constructor uimm5_bitcast_to_imm5 uimm5_bitcast_to_imm5)
;; Float Helpers
;; Returns the bitpattern of the Canonical NaN for the given type.
(decl pure canonical_nan_u64 (Type) u64)
(rule (canonical_nan_u64 $F32) 0x7fc00000)
(rule (canonical_nan_u64 $F64) 0x7ff8000000000000)
(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 WritableXReg (temp_writable_xreg))
(_ 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 WritableXReg (temp_writable_xreg))
(_ 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 WritableXReg (temp_writable_xreg))
(_ 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 2 (gen_bnot (ty_scalar_float ty) x)
(let ((val FReg (value_regs_get x 0))
(x_val XReg (move_f_to_x val ty))
(inverted XReg (rv_not x_val))
(res FReg (move_x_to_f inverted (float_int_of_same_size ty))))
(value_reg res)))
(rule 1 (gen_bnot $I128 x)
(let ((lo XReg (rv_not (value_regs_get x 0)))
(hi XReg (rv_not (value_regs_get x 1))))
(value_regs lo hi)))
(rule 0 (gen_bnot (ty_int_ref_scalar_64 _) x)
(rv_not (value_regs_get x 0)))
(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_andi (XReg u64) XReg)
(rule 1 (gen_andi x (imm12_from_u64 y))
(rv_andi x y))
(rule 0 (gen_andi x y)
(rv_and x (imm $I64 y)))
(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 XReg (gen_brev8 r $I16))
(tmp2 XReg (gen_rev8 tmp))
(result XReg (rv_srli tmp2 (imm12_const 48))))
result))
(rule
(lower_bit_reverse r $I32)
(let
((tmp XReg (gen_brev8 r $I32))
(tmp2 XReg (gen_rev8 tmp))
(result XReg (rv_srli tmp2 (imm12_const 32))))
result))
(rule
(lower_bit_reverse r $I64)
(let
((tmp XReg (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 XReg (value_regs_get x 0))
(x_hi XReg (value_regs_get x 1))
;; Count both halves
(high XReg (lower_ctz $I64 x_hi))
(low XReg (lower_ctz $I64 x_lo))
;; Only add the top half if the bottom is zero
(high XReg (gen_select_reg (IntCC.Equal) x_lo (zero_reg) high (zero_reg)))
(result XReg (rv_add low high)))
(extend result (ExtendOp.Zero) $I64 $I128)))
(decl lower_clz (Type XReg) XReg)
(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 XReg (zext r ty $I64))
(count XReg (rv_clz tmp))
;; We always do the operation on the full 64-bit register, so subtract 64 from the result.
(result XReg (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 XReg (value_regs_get x 0))
(x_hi XReg (value_regs_get x 1))
;; Count both halves
(high XReg (lower_clz $I64 x_hi))
(low XReg (lower_clz $I64 x_lo))
;; Only add the bottom zeros if the top half is zero
(low XReg (gen_select_reg (IntCC.Equal) x_hi (zero_reg) low (zero_reg)))
(result XReg (rv_add high low)))
(extend result (ExtendOp.Zero) $I64 $I128)))
(decl lower_cls (Type XReg) XReg)
(rule (lower_cls ty r)
(let ((tmp XReg (sext r ty $I64))
(tmp2 XReg (gen_select_reg (IntCC.SignedLessThan) tmp (zero_reg) (rv_not tmp) tmp))
(tmp3 XReg (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 XReg (value_regs_get x 0))
(high XReg (value_regs_get x 1))
(low XReg (gen_select_reg (IntCC.SignedLessThan) high (zero_reg) (rv_not low) low))
(high XReg (gen_select_reg (IntCC.SignedLessThan) high (zero_reg) (rv_not high) high))
(tmp ValueRegs (lower_clz_i128 (value_regs low high)))
(count XReg (value_regs_get tmp 0))
(result XReg (rv_addi count (imm12_const -1))))
(extend result (ExtendOp.Zero) $I64 $I128)))
(decl gen_cltz (bool XReg Type) XReg)
(rule (gen_cltz leading rs ty)
(let ((tmp WritableXReg (temp_writable_xreg))
(step WritableXReg (temp_writable_xreg))
(sum WritableXReg (temp_writable_xreg))
(_ 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)))
(extend val (ExtendOp.Signed) ty $I64))
(rule 0 (ext_int_if_need $false val (fits_in_32 (ty_int ty)))
(extend val (ExtendOp.Zero) 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 (XReg Type Type) XReg)
(rule (zext val from_ty (fits_in_64 to_ty)) (value_regs_get (extend val (ExtendOp.Zero) from_ty to_ty) 0))
;; Performs a signed extension of the given value
(decl sext (XReg Type Type) XReg)
(rule (sext val from_ty (fits_in_64 to_ty)) (value_regs_get (extend val (ExtendOp.Signed) from_ty to_ty) 0))
(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 XReg (value_regs_get val 0))
(shift Imm12 (imm_from_bits (u64_sub 64 (ty_bits from_ty))))
(left XReg (rv_slli val shift))
(shift_op AluOPRRI (extend_shift_op extend_op))
(right XReg (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 XReg (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 XReg (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 XReg (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 XReg (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 XReg (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 XReg (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 XReg (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 XReg (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 XReg (value_regs_get val 0))
(low XReg (sext val from_ty $I64))
(high XReg (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 XReg (value_regs_get val 0))
(low XReg (zext val from_ty $I64))
(high XReg (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 XReg (alu_rrr op (value_regs_get a 0) (value_regs_get b 0)))
;; high part.
(high XReg (alu_rrr op (value_regs_get a 1) (value_regs_get b 1))))
(value_regs low high)))
(decl lower_umlhi (Type XReg XReg) XReg)
(rule 1 (lower_umlhi $I64 rs1 rs2)
(rv_mulhu rs1 rs2))
(rule (lower_umlhi ty rs1 rs2)
(let
((tmp XReg (rv_mul (zext rs1 ty $I64) (zext rs2 ty $I64))))
(rv_srli tmp (imm12_const (ty_bits ty)))))
(decl lower_smlhi (Type XReg XReg) XReg)
(rule 1
(lower_smlhi $I64 rs1 rs2)
(rv_mulh rs1 rs2))
(rule
(lower_smlhi ty rs1 rs2)
(let
((tmp XReg (rv_mul rs1 rs2)))
(rv_srli tmp (imm12_const (ty_bits ty)))))
(decl lower_rotl (Type XReg XReg) XReg)
(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 XReg XReg) XReg)
;;; 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 XReg) ValueRegs)
(extern constructor gen_shamt gen_shamt)
(decl lower_rotr (Type XReg XReg) XReg)
(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 XReg XReg) XReg)
;;;
(rule
(lower_rotr_shift ty rs amount)
(let
((x ValueRegs (gen_shamt ty amount))
(shamt XReg (value_regs_get x 0))
(len_sub_shamt XReg (value_regs_get x 1))
;;
(part1 XReg (rv_srl rs shamt))
;;
(part2 XReg (rv_sll rs len_sub_shamt))
;;
(part3 XReg (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 XReg (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 WritableXReg (temp_writable_xreg))
(step WritableXReg (temp_writable_xreg))
(sum WritableXReg (temp_writable_xreg))
(_ Unit (emit (MInst.Popcnt sum step tmp rs ty))))
(writable_reg_to_reg sum)))
(decl lower_popcnt (XReg Type) XReg)
(rule 1 (lower_popcnt rs ty)
(if-let $true (has_zbb))
(rv_cpop (zext rs ty $I64)))
(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 XReg (lower_popcnt (value_regs_get a 0) $I64))
;; high part.
(high XReg (lower_popcnt (value_regs_get a 1) $I64))
;; add toghter.
(result XReg (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 XReg (value_regs_get tmp 0))
(len_sub_shamt XReg (value_regs_get tmp 1))
;;
(low_part1 XReg (rv_sll (value_regs_get x 0) shamt))
(low_part2 XReg (rv_srl (value_regs_get x 1) len_sub_shamt))
;;; if shamt == 0 low_part2 will overflow we should zero instead.
(low_part3 XReg (gen_select_reg (IntCC.Equal) shamt (zero_reg) (zero_reg) low_part2))
(low XReg (rv_or low_part1 low_part3))
;;
(high_part1 XReg (rv_sll (value_regs_get x 1) shamt))
(high_part2 XReg (rv_srl (value_regs_get x 0) len_sub_shamt))
(high_part3 XReg (gen_select_reg (IntCC.Equal) shamt (zero_reg) (zero_reg) high_part2))
(high XReg (rv_or high_part1 high_part3))
;;
(const64 XReg (load_u64_constant 64))
(shamt_128 XReg (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 XReg (value_regs_get tmp 0))
(len_sub_shamt XReg (value_regs_get tmp 1))
;;
(low_part1 XReg (rv_srl (value_regs_get x 0) shamt))
(low_part2 XReg (rv_sll (value_regs_get x 1) len_sub_shamt))
;;; if shamt == 0 low_part2 will overflow we should zero instead.
(low_part3 XReg (gen_select_reg (IntCC.Equal) shamt (zero_reg) (zero_reg) low_part2))
(low XReg (rv_or low_part1 low_part3))
;;
(high_part1 XReg (rv_srl (value_regs_get x 1) shamt))
(high_part2 XReg (rv_sll (value_regs_get x 0) len_sub_shamt))
(high_part3 XReg (gen_select_reg (IntCC.Equal) shamt (zero_reg) (zero_reg) high_part2))
(high XReg (rv_or high_part1 high_part3))
;;
(const64 XReg (load_u64_constant 64))
(shamt_128 XReg (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 gen_amode (Reg Offset32 Type) AMode)
(extern constructor gen_amode gen_amode)
;; Generates a AMode that points to a constant in the constant pool.
(decl gen_const_amode (VCodeConstant) AMode)
(extern constructor gen_const_amode gen_const_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 WritableXReg (temp_writable_xreg))
(_ 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 WritableXReg (temp_writable_xreg))
(_ 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 gen_stack_addr (StackSlot Offset32) Reg)
(extern constructor gen_stack_addr gen_stack_addr)
;;
(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 XReg XReg 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_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 FReg bool Type Type) XReg)
(rule
(gen_fcvt_int is_sat rs is_signed in_type out_type)
(let
((result WritableReg (temp_writable_reg out_type))
(tmp WritableFReg (temp_writable_freg))
(_ Unit (emit (MInst.FcvtToInt is_sat result tmp rs is_signed in_type out_type))))
(writable_reg_to_reg result)))
;;; some float binary operation
;;; 1. need move into x reister.
;;; 2. do the operation.
;;; 3. move back.
(decl lower_float_binary (AluOPRRR FReg FReg Type) FReg)
(rule
(lower_float_binary op rs1 rs2 ty)
(let ((x_rs1 XReg (move_f_to_x rs1 ty))
(x_rs2 XReg (move_f_to_x rs2 ty))
(tmp XReg (alu_rrr op x_rs1 x_rs2)))
(move_x_to_f tmp (float_int_of_same_size 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 XReg (rv_sub (value_regs_get x 0) (value_regs_get y 0)))
;; compute borrow.
(borrow XReg (rv_sltu (value_regs_get x 0) low))
;;
(high_tmp XReg (rv_sub (value_regs_get x 1) (value_regs_get y 1)))
;;
(high XReg (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 (XReg XReg Type) ValueRegs)
(rule 1
(lower_uadd_overflow x y $I64)
(let ((tmp XReg (rv_add x y))
(test XReg (gen_icmp (IntCC.UnsignedLessThan) tmp x $I64)))
(value_regs tmp test)))
(rule
(lower_uadd_overflow x y (fits_in_32 ty))
(let ((tmp_x XReg (zext x ty $I64))
(tmp_y XReg (zext y ty $I64))
(sum XReg (rv_add tmp_x tmp_y))
(test XReg (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 (XReg Type ExtendOp) XReg)
(rule 2 (normalize_fcvt_from_int r (fits_in_16 ty) op)
(value_regs_get (extend r op ty $I64) 0))
(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) XReg)
(rule 1 (truthy_to_reg (fits_in_64 _) regs)
(value_regs_get regs 0))
(rule 0 (truthy_to_reg $I128 regs)
(let ((lo XReg (value_regs_get regs 0))
(hi XReg (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 XReg (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)
;; Generates a bitcast instruction.
;; Args are: src, src_ty, dst_ty
(decl gen_bitcast (Reg Type Type) Reg)
(rule 1 (gen_bitcast r $F32 $I32) (rv_fmvxw r))
(rule 1 (gen_bitcast r $F64 $I64) (rv_fmvxd r))
(rule 1 (gen_bitcast r $I32 $F32) (rv_fmvwx r))
(rule 1 (gen_bitcast r $I64 $F64) (rv_fmvdx r))
(rule (gen_bitcast r _ _) r)
(decl move_f_to_x (FReg Type) XReg)
(rule (move_f_to_x r $F32) (gen_bitcast r $F32 $I32))
(rule (move_f_to_x r $F64) (gen_bitcast r $F64 $I64))
(decl move_x_to_f (XReg Type) FReg)
(rule (move_x_to_f r $I32) (gen_bitcast r $I32 $F32))
(rule (move_x_to_f r $I64) (gen_bitcast r $I64 $F64))
(decl float_int_of_same_size (Type) Type)
(rule (float_int_of_same_size $F32) $I32)
(rule (float_int_of_same_size $F64) $I64)
(decl gen_rev8 (XReg) XReg)
(rule 1
(gen_rev8 rs)
(if-let $true (has_zbb))
(rv_rev8 rs))
(rule
(gen_rev8 rs)
(if-let $false (has_zbb))
(let
((rd WritableXReg (temp_writable_xreg))
(tmp WritableXReg (temp_writable_xreg))
(step WritableXReg (temp_writable_xreg))
(_ 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 WritableXReg (temp_writable_xreg))
(tmp2 WritableXReg (temp_writable_xreg))
(step WritableXReg (temp_writable_xreg))
(rd WritableXReg (temp_writable_xreg))
(_ 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 XReg XReg) XReg)
(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 gen_trapif (XReg TrapCode) InstOutput)
(rule
(gen_trapif test trap_code)
(side_effect (SideEffectNoResult.Inst (MInst.TrapIf test trap_code))))
(decl gen_trapifc (IntCC XReg XReg 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 (XReg Type) XReg)
(extern constructor shift_int_to_most_significant shift_int_to_most_significant)
;;; generate div overflow.
(decl gen_div_overflow (XReg XReg Type) InstOutput)
(rule
(gen_div_overflow rs1 rs2 ty)
(let
((r_const_neg_1 XReg (load_imm12 -1))
(r_const_min XReg (rv_slli (load_imm12 1) (imm12_const 63)))
(tmp_rs1 XReg (shift_int_to_most_significant rs1 ty))
(t1 XReg (gen_icmp (IntCC.Equal) r_const_neg_1 rs2 ty))
(t2 XReg (gen_icmp (IntCC.Equal) r_const_min tmp_rs1 ty))
(test XReg (rv_and t1 t2)))
(gen_trapif test (TrapCode.IntegerOverflow))))
(decl gen_div_by_zero (XReg) 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 (XReg XReg XReg) XReg)
(rule
(madd n m a)
(let
((t XReg (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 XReg (truthy_to_reg in_ty (normalize_cmp_value in_ty val (ExtendOp.Zero))))
(non_zero XReg (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 XReg (value_regs_get val 0))
(hi XReg (value_regs_get val 1))
(combined XReg (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 WritableXReg (temp_writable_xreg))
(_ 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 (XReg) XReg)
(rule (not x) (rv_xori x (imm_from_bits 1)))
(decl is_not_nan (Type FReg) XReg)
(rule (is_not_nan ty a) (rv_feq ty a a))
(decl ordered (Type FReg FReg) XReg)
(rule (ordered ty a b) (rv_and (is_not_nan ty a) (is_not_nan ty b)))
(type CmpResult (enum
(Result
(result XReg)
(invert bool))))
;; Wrapper for the common case when constructing comparison results. It assumes
;; that the result isn't negated.
(decl cmp_result (XReg) 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 (XReg) 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 XReg XReg) 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) XReg)
(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 FReg FReg) 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))))