blob: e004d5dfaeb4ca458df6518b5eb0669c68b3db6b [file] [log] [blame]
;; Instruction formats.
(type MInst
(enum
;; A no-op of zero size.
(Nop0)
(Nop4)
;; load immediate
(Lui
(rd WritableReg)
(imm Imm20))
(LoadInlineConst
(rd WritableReg)
(ty Type)
(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))
;; A CSR Reading or Writing instruction with a register source and a register destination.
(CsrReg
(op CsrRegOP)
(rd WritableReg)
(rs Reg)
(csr CSR))
;; A CSR Writing instruction with an immediate source and a register destination.
(CsrImm
(op CsrImmOP)
(rd WritableReg)
(imm UImm5)
(csr CSR))
;; 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))
;; A pseudo-instruction that moves vregs to return registers.
(Rets
(rets VecRetPair))
(Ret)
(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))
;; A direct return-call macro instruction.
(ReturnCall
(callee BoxExternalName)
(info BoxReturnCallInfo))
;; An indirect return-call macro instruction.
(ReturnCallInd
(callee Reg)
(info BoxReturnCallInfo))
;; Emits a trap with the given trap code if the comparison succeeds
(TrapIf
(rs1 Reg)
(rs2 Reg)
(cc IntCC)
(trap_code TrapCode))
(Jal
;; (rd WritableReg) don't use
(label MachLabel))
(CondBr
(taken CondBrTarget)
(not_taken CondBrTarget)
(kind IntegerCompare))
;; Load an inline symbol reference.
(LoadExtName
(rd WritableReg)
(name BoxExternalName)
(offset i64))
;; Load a TLS symbol address
(ElfTlsGetAddr
(rd WritableReg)
(name BoxExternalName))
;; 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))
(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 WritableValueRegs)
(condition IntegerCompare)
(x ValueRegs)
(y ValueRegs))
(BrTable
(index Reg)
(tmp1 WritableReg)
(tmp2 WritableReg)
(targets VecMachLabel))
;; atomic compare and set operation
(AtomicCas
(offset Reg)
(t0 WritableReg)
(dst WritableReg)
(e Reg)
(addr Reg)
(v Reg)
(ty Type))
;; an integer compare.
(Icmp
(cc IntCC)
(rd WritableReg)
(a ValueRegs)
(b ValueRegs)
(ty Type))
(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))
;; 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))
(VecAluRRRR
(op VecAluOpRRRR)
(vd WritableReg)
(vd_src Reg)
(vs2 Reg)
(vs1 Reg)
(mask VecOpMasking)
(vstate VState))
(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 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 COpcodeSpace (enum
(C0)
(C1)
(C2)
))
;; Opcodes for the CR compressed instruction format
(type CrOp (enum
(CMv)
(CAdd)
(CJr)
(CJalr)
;; c.ebreak technically isn't a CR format instruction, but it's encoding
;; lines up with this format.
(CEbreak)
))
;; Opcodes for the CA compressed instruction format
(type CaOp (enum
(CAnd)
(COr)
(CXor)
(CSub)
(CAddw)
(CSubw)
(CMul)
))
;; Opcodes for the CJ compressed instruction format
(type CjOp (enum
(CJ)
))
;; Opcodes for the CI compressed instruction format
(type CiOp (enum
(CAddi)
(CAddiw)
(CAddi16sp)
(CSlli)
(CLi)
(CLui)
(CLwsp)
(CLdsp)
(CFldsp)
))
;; Opcodes for the CIW compressed instruction format
(type CiwOp (enum
(CAddi4spn)
))
;; Opcodes for the CB compressed instruction format
(type CbOp (enum
(CSrli)
(CSrai)
(CAndi)
))
;; Opcodes for the CSS compressed instruction format
(type CssOp (enum
(CSwsp)
(CSdsp)
(CFsdsp)
))
;; Opcodes for the CS compressed instruction format
(type CsOp (enum
(CSw)
(CSd)
(CFsd)
))
;; Opcodes for the CL compressed instruction format
(type ClOp (enum
(CLw)
(CLd)
(CFld)
))
;; Opcodes for the CSZN compressed instruction format
(type CsznOp (enum
(CNot)
(CZextb)
(CZexth)
(CZextw)
(CSextb)
(CSexth)
))
;; This is a mix of all Zcb memory adressing instructions
;;
;; Technically they are split across 4 different formats.
;; But they are all very similar, so we just group them all together.
(type ZcbMemOp (enum
(CLbu)
(CLhu)
(CLh)
(CSb)
(CSh)
))
(type CsrRegOP (enum
;; Atomic Read/Write CSR
(CsrRW)
;; Atomic Read and Set Bits in CSR
(CsrRS)
;; Atomic Read and Clear Bits in CSR
(CsrRC)
))
(type CsrImmOP (enum
;; Atomic Read/Write CSR (Immediate Source)
(CsrRWI)
;; Atomic Read and Set Bits in CSR (Immediate Source)
(CsrRSI)
;; Atomic Read and Clear Bits in CSR (Immediate Source)
(CsrRCI)
))
;; Enum of the known CSR registers
(type CSR (enum
;; Floating-Point Dynamic Rounding Mode
(Frm)
))
(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)
))
(decl pure frm_bits (FRM) UImm5)
(extern constructor frm_bits frm_bits)
(convert FRM UImm5 frm_bits)
(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 BoxCallInfo (primitive BoxCallInfo))
(type BoxCallIndInfo (primitive BoxCallIndInfo))
(type BoxReturnCallInfo (primitive BoxReturnCallInfo))
(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 CondBrTarget (primitive CondBrTarget))
(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_m () bool)
(extern constructor has_m has_m)
(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 (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 `fcvt.s.w` instruction.
(decl rv_fcvtsw (XReg) FReg)
(rule (rv_fcvtsw rs1) (fpu_rr (FpuOPRR.FcvtSw) $F32 rs1))
;; Helper for emitting the `fcvt.s.wu` instruction.
(decl rv_fcvtswu (XReg) FReg)
(rule (rv_fcvtswu rs1) (fpu_rr (FpuOPRR.FcvtSwU) $F32 rs1))
;; Helper for emitting the `fcvt.d.w` instruction.
(decl rv_fcvtdw (XReg) FReg)
(rule (rv_fcvtdw rs1) (fpu_rr (FpuOPRR.FcvtDW) $F32 rs1))
;; Helper for emitting the `fcvt.d.wu` instruction.
(decl rv_fcvtdwu (XReg) FReg)
(rule (rv_fcvtdwu rs1) (fpu_rr (FpuOPRR.FcvtDWU) $F32 rs1))
;; Helper for emitting the `fcvt.s.l` instruction.
(decl rv_fcvtsl (XReg) FReg)
(rule (rv_fcvtsl rs1) (fpu_rr (FpuOPRR.FcvtSL) $F32 rs1))
;; Helper for emitting the `fcvt.s.lu` instruction.
(decl rv_fcvtslu (XReg) FReg)
(rule (rv_fcvtslu rs1) (fpu_rr (FpuOPRR.FcvtSLU) $F32 rs1))
;; Helper for emitting the `fcvt.d.l` instruction.
(decl rv_fcvtdl (XReg) FReg)
(rule (rv_fcvtdl rs1) (fpu_rr (FpuOPRR.FcvtDL) $F32 rs1))
;; Helper for emitting the `fcvt.d.lu` instruction.
(decl rv_fcvtdlu (XReg) FReg)
(rule (rv_fcvtdlu rs1) (fpu_rr (FpuOPRR.FcvtDLu) $F32 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 `cpopw` ("Count Population") instruction.
(decl rv_cpopw (XReg) XReg)
(rule (rv_cpopw rs1)
(alu_rr_funct12 (AluOPRRI.Cpopw) 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 `maxu` instruction.
(decl rv_maxu (XReg XReg) XReg)
(rule (rv_maxu rs1 rs2)
(alu_rrr (AluOPRRR.Maxu) rs1 rs2))
;; Helper for emitting the `min` instruction.
(decl rv_min (XReg XReg) XReg)
(rule (rv_min rs1 rs2)
(alu_rrr (AluOPRRR.Max) rs1 rs2))
;; Helper for emitting the `minu` instruction.
(decl rv_minu (XReg XReg) XReg)
(rule (rv_minu rs1 rs2)
(alu_rrr (AluOPRRR.Minu) 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))
;; `Zicsr` Extension Instructions
;; Helper for emitting the `csrrwi` instruction.
(decl rv_csrrwi (CSR UImm5) XReg)
(rule (rv_csrrwi csr imm)
(csr_imm (CsrImmOP.CsrRWI) csr imm))
;; This is a special case of `csrrwi` when the CSR is the `frm` CSR.
(decl rv_fsrmi (FRM) XReg)
(rule (rv_fsrmi frm) (rv_csrrwi (CSR.Frm) frm))
;; Helper for emitting the `csrw` instruction. This is a special case of
;; `csrrw` where the destination register is always `x0`.
(decl rv_csrw (CSR XReg) Unit)
(rule (rv_csrw csr rs)
(csr_reg_dst_zero (CsrRegOP.CsrRW) csr rs))
;; This is a special case of `csrw` when the CSR is the `frm` CSR.
(decl rv_fsrm (XReg) Unit)
(rule (rv_fsrm rs) (rv_csrw (CSR.Frm) rs))
;; 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))
;; Helper for generating a i64 from a pair of Imm20 and Imm12 constants
(decl i64_generate_imm (Imm20 Imm12) i64)
(extern extractor i64_generate_imm i64_generate_imm)
;; Helper for generating a i64 from a shift of a Imm20 constant with LUI
(decl i64_shift_for_lui (u64 Imm12) i64)
(extern extractor i64_shift_for_lui i64_shift_for_lui)
;; Helper for generating a i64 from a shift of a Imm20 constant
(decl i64_shift (i64 Imm12) i64)
(extern extractor i64_shift i64_shift)
;; Immediate Loading rules
;; TODO: Loading the zero reg directly causes a bunch of regalloc errors, we should look into it.
;; TODO: Load floats using `fld` instead of `ld`
(decl imm (Type u64) Reg)
;; Refs get loaded as integers.
(rule 5 (imm $R32 c) (imm $I32 c))
(rule 5 (imm $R64 c) (imm $I64 c))
;; Floats get loaded as integers and then moved into an F register.
(rule 5 (imm $F32 c) (gen_bitcast (imm $I32 c) $I32 $F32))
(rule 5 (imm $F64 c) (gen_bitcast (imm $I64 c) $I64 $F64))
;; Try to match just an imm12
(rule 4 (imm (ty_int ty) c)
(if-let (i64_generate_imm (imm20_is_zero) imm12) (i64_sextend_u64 ty c))
(rv_addi (zero_reg) imm12))
;; We can also try to load using a single LUI.
;; LUI takes a 20 bit immediate, places it on bits 13 to 32 of the register.
;; In RV64 this value is then sign extended to 64bits.
(rule 3 (imm (ty_int ty) c)
(if-let (i64_generate_imm imm20 (imm12_is_zero)) (i64_sextend_u64 ty c))
(rv_lui imm20))
;; We can combo addi + lui to represent all 32-bit immediates
;; And some 64-bit immediates as well.
(rule 2 (imm (ty_int ty) c)
(if-let (i64_generate_imm imm20 imm12) (i64_sextend_u64 ty c))
(rv_addi (rv_lui imm20) imm12))
;; If the non-zero bits of the immediate fit in 20 bits, we can use LUI + shift
(rule 1 (imm (ty_int ty) c)
(if-let (i64_shift_for_lui (imm20_from_u64 base) shift) (i64_sextend_u64 ty c))
(rv_slli (rv_lui base) shift))
;; Combine one of the above rules with a shift-left if possible, This chops off
;; all trailing zeros from the input constant and then attempts if the resulting
;; constant can itself use one of the above rules via the `i64_generate_imm`
;; matcher. This will then recurse on the above rules to materialize a smaller
;; constant which is then shifted left to create the desired constant.
(rule 0 (imm (ty_int ty) c)
(if-let (i64_shift c_shifted shift) (i64_sextend_u64 ty c)) ;; constant to make
(if-let (i64_generate_imm _ _) c_shifted) ;; can the smaller constant be made?
(rv_slli (imm ty (i64_as_u64 c_shifted)) shift))
;; Otherwise we fall back to loading the immediate from the constant pool.
(rule -1 (imm (ty_int ty) c)
(gen_load
(gen_const_amode (emit_u64_le_const c))
(LoadOP.Ld)
(mem_flags_trusted)))
;; 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)
;; 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) (i64_from_iconst (imm12_from_i64 n)))
(decl imm12_from_u64 (Imm12) u64)
(extern extractor imm12_from_u64 imm12_from_u64)
(decl imm12_from_i64 (Imm12) i64)
(extern extractor imm12_from_i64 imm12_from_i64)
(decl pure partial u64_to_imm12 (u64) Imm12)
(rule (u64_to_imm12 (imm12_from_u64 n)) n)
(decl pure imm12_is_zero () Imm12)
(extern extractor imm12_is_zero imm12_is_zero)
;; Imm20
;; Extractor that matches if a Imm20 is zero
(decl pure imm20_is_zero () Imm20)
(extern extractor imm20_is_zero imm20_is_zero)
(decl imm20_from_u64 (Imm20) u64)
(extern extractor imm20_from_u64 imm20_from_u64)
(decl imm20_from_i64 (Imm20) i64)
(extern extractor imm20_from_i64 imm20_from_i64)
;; Imm5 Extractors
(decl imm5_from_u64 (Imm5) u64)
(extern extractor imm5_from_u64 imm5_from_u64)
(decl imm5_from_i64 (Imm5) i64)
(extern extractor imm5_from_i64 imm5_from_i64)
;; Construct a Imm5 from an i8
(decl pure partial i8_to_imm5 (i8) Imm5)
(extern constructor i8_to_imm5 i8_to_imm5)
;; Constructor that matches a `Value` equivalent to a replicated Imm5 on all lanes.
(decl pure partial replicated_imm5 (Value) Imm5)
(rule (replicated_imm5 (splat (i64_from_iconst (imm5_from_i64 n)))) n)
(rule (replicated_imm5 (vconst (u128_from_constant n128)))
(if-let (u128_replicated_u64 n64) n128)
(if-let (u64_replicated_u32 n32) n64)
(if-let (u32_replicated_u16 n16) n32)
(if-let (u16_replicated_u8 n8) n16)
(if-let n (i8_to_imm5 (u8_as_i8 n8)))
n)
;; UImm5 Helpers
;; Constructor that matches a `Value` equivalent to a replicated UImm5 on all lanes.
(decl pure partial replicated_uimm5 (Value) UImm5)
(rule (replicated_uimm5 (splat (uimm5_from_value n))) n)
(rule 1 (replicated_uimm5 (vconst (u128_from_constant n128)))
(if-let (u128_replicated_u64 n64) n128)
(if-let (u64_replicated_u32 n32) n64)
(if-let (u32_replicated_u16 n16) n32)
(if-let (u16_replicated_u8 n8) n16)
(if-let (uimm5_from_u8 n) n8)
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))
;; 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))
;; Helper for emitting the `Lui` instruction.
;; TODO: This should be something like `emit_u_type`. And should share the
;; `MInst` with `auipc` since these instructions share the U-Type format.
(decl rv_lui (Imm20) XReg)
(rule (rv_lui imm)
(let ((dst WritableXReg (temp_writable_xreg))
(_ Unit (emit (MInst.Lui dst imm))))
dst))
;; Helper for emitting `MInst.CsrImm` instructions.
(decl csr_imm (CsrImmOP CSR UImm5) XReg)
(rule (csr_imm op csr imm)
(let ((dst WritableXReg (temp_writable_xreg))
(_ Unit (emit (MInst.CsrImm op dst imm csr))))
dst))
;; Helper for emitting a `MInst.CsrReg` instruction that writes the result to x0.
(decl csr_reg_dst_zero (CsrRegOP CSR XReg) Unit)
(rule (csr_reg_dst_zero op csr rs)
(emit (MInst.CsrReg op (writable_zero_reg) rs csr)))
(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 leading zeros from a i128 bit value.
;; We count both halves separately and conditionally add them if it makes sense.
(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))
;; Performs a zero extension of the given value
(decl zext (Value) XReg)
;; In the most generic case, we shift left and then shift right.
(rule 0 (zext val @ (value_type (fits_in_32 ty)))
(let ((shift Imm12 (imm_from_bits (u64_sub 64 (ty_bits ty)))))
(rv_srli (rv_slli val shift) shift)))
;; If we are zero extending a U8 we can use a `andi` instruction.
(rule 1 (zext val @ (value_type $I8))
(rv_andi val (imm12_const 0xff)))
;; 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 (zext val @ (value_type $I16))
(if-let $true (has_zbkb))
(rv_packw val (zero_reg)))
;; If we have the `zbkb` extension `pack` can be used to zero extend 32 bit registers
(rule 1 (zext val @ (value_type $I32))
(if-let $true (has_zbkb))
(rv_pack val (zero_reg)))
;; If we have the `zbb` extension we can use the dedicated `zext.h` instruction.
(rule 2 (zext val @ (value_type $I16))
(if-let $true (has_zbb))
(rv_zexth val))
;; With `zba` we have a `zext.w` instruction
(rule 2 (zext val @ (value_type $I32))
(if-let $true (has_zba))
(rv_zextw val))
;; Ignore sign extensions for values whose representation is already the full
;; register width.
(rule 3 (zext val)
(if (val_already_extended val))
val)
;; Performs a signed extension of the given value
(decl sext (Value) XReg)
;; Same base case as `zext`, shift left-then-right.
(rule 0 (sext val @ (value_type (fits_in_32 ty)))
(let ((shift Imm12 (imm_from_bits (u64_sub 64 (ty_bits ty)))))
(rv_srai (rv_slli val shift) shift)))
;; If we have the `zbb` extension we can use the dedicated `sext.b` instruction.
(rule 1 (sext val @ (value_type $I8))
(if-let $true (has_zbb))
(rv_sextb val))
;; If we have the `zbb` extension we can use the dedicated `sext.h` instruction.
(rule 1 (sext val @ (value_type $I16))
(if-let $true (has_zbb))
(rv_sexth val))
;; When signed extending from 32 to 64 bits we can use a
;; `addiw val 0`. Also known as a `sext.w`
(rule 1 (sext val @ (value_type $I32))
(rv_sextw val))
;; Ignore sign extensions for values whose representation is already the full
;; register width.
(rule 2 (sext val)
(if (val_already_extended val))
val)
;; Helper matcher for when a value's representation is already sign or zero
;; extended to the full 64-bit register representation. This is used by `zext`
;; and `sext` above to skip the extension instruction entirely in some
;; circumstances.
(decl pure partial val_already_extended (Value) bool)
(rule 0 (val_already_extended v @ (value_type $I64)) $true)
;; When extending our backend always extends to the full register width, so
;; there's no need to extend-an-extend.
(rule 1 (val_already_extended (uextend _)) $true)
(rule 1 (val_already_extended (sextend _)) $true)
;; The result of `icmp`/`fcmp` is zero or one, meaning that it's already sign
;; extended to the full register width.
(rule 1 (val_already_extended (icmp _ _ _)) $true)
(rule 1 (val_already_extended (fcmp _ _ _)) $true)
(type ExtendOp
(enum
(Zero)
(Signed)))
(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_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_xreg (cmp_eqz shamt) (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_xreg (cmp_eqz shamt) (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 (imm $I64 (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 (XReg) Reg)
(rule (gen_popcnt rs)
(let
((tmp WritableXReg (temp_writable_xreg))
(step WritableXReg (temp_writable_xreg))
(sum WritableXReg (temp_writable_xreg))
(_ Unit (emit (MInst.Popcnt sum step tmp rs $I64))))
(writable_reg_to_reg sum)))
(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_xreg (cmp_eqz shamt) (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_xreg (cmp_eqz shamt) (zero_reg) high_part2))
(high XReg (rv_or high_part1 high_part3))
;;
(const64 XReg (imm $I64 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.
(gen_select_regs
(cmp_geu shamt_128 const64)
(value_regs high low)
(value_regs 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_xreg (cmp_eqz shamt) (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_xreg (cmp_eqz shamt) (zero_reg) high_part2))
(high XReg (rv_or high_part1 high_part3))
;;
(const64 XReg (imm $I64 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.
(gen_select_regs
(cmp_geu shamt_128 const64)
(value_regs high low)
(value_regs low high)
)))
;; Generates a AMode that points to a register plus an offset.
(decl gen_reg_offset_amode (Reg i64 Type) AMode)
(extern constructor gen_reg_offset_amode gen_reg_offset_amode)
;; Generates a AMode that an offset from the stack pointer.
(decl gen_sp_offset_amode (i64 Type) AMode)
(extern constructor gen_sp_offset_amode gen_sp_offset_amode)
;; Generates a AMode that an offset from the frame pointer.
(decl gen_fp_offset_amode (i64 Type) AMode)
(extern constructor gen_fp_offset_amode gen_fp_offset_amode)
;; Generates an AMode that points to a stack slot + offset.
(decl gen_stack_slot_amode (StackSlot i64 Type) AMode)
(extern constructor gen_stack_slot_amode gen_stack_slot_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)
;; Tries to match a Value + Offset into an AMode
(decl amode (Value i32 Type) AMode)
(rule 0 (amode addr offset ty) (amode_inner addr offset ty))
;; If we are adding a constant offset with an iadd we can instead make that
;; offset part of the amode offset.
;;
;; We can't recurse into `amode` again since that could cause stack overflows.
;; See: https://github.com/bytecodealliance/wasmtime/pull/6968
(rule 1 (amode (iadd addr (iconst (simm32 y))) offset ty)
(if-let new_offset (s32_add_fallible y offset))
(amode_inner addr new_offset ty))
(rule 2 (amode (iadd (iconst (simm32 x)) addr) offset ty)
(if-let new_offset (s32_add_fallible x offset))
(amode_inner addr new_offset ty))
;; These are the normal rules for generating an AMode.
(decl amode_inner (Value i32 Type) AMode)
;; In the simplest case we just lower into a Reg+Offset
(rule 0 (amode_inner r @ (value_type (ty_addr64 _)) offset ty)
(gen_reg_offset_amode r offset ty))
;; If the value is a `get_frame_pointer`, we can just use the offset from that.
(rule 1 (amode_inner (get_frame_pointer) offset ty)
(gen_fp_offset_amode offset ty))
;; If the value is a `get_stack_pointer`, we can just use the offset from that.
(rule 1 (amode_inner (get_stack_pointer) offset ty)
(gen_sp_offset_amode offset ty))
;; Similarly if the value is a `stack_addr` we can also turn that into an sp offset.
(rule 1 (amode_inner (stack_addr ss ss_offset) amode_offset ty)
(if-let combined_offset (s32_add_fallible ss_offset amode_offset))
(gen_stack_slot_amode ss combined_offset ty))
;; Returns a canonical type for a LoadOP. We only return I64 or F64.
(decl load_op_reg_type (LoadOP) Type)
(rule 1 (load_op_reg_type (LoadOP.Fld)) $F64)
(rule 1 (load_op_reg_type (LoadOP.Flw)) $F64)
(rule 0 (load_op_reg_type _) $I64)
;; helper function to load from memory.
(decl gen_load (AMode LoadOP MemFlags) Reg)
(rule (gen_load amode op flags)
(let ((dst WritableReg (temp_writable_reg (load_op_reg_type op)))
(_ Unit (emit (MInst.Load dst op flags amode))))
dst))
;; helper function to store to memory.
(decl gen_store (AMode StoreOP MemFlags Reg) InstOutput)
(rule (gen_store amode op flags src)
(side_effect (SideEffectNoResult.Inst (MInst.Store amode op flags src))))
(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_xreg (IntegerCompare XReg XReg) XReg)
(rule 1 (gen_select_xreg (int_compare_decompose cc x y) x y)
(if-let (IntCC.UnsignedLessThan) (intcc_without_eq cc))
(if-let $true (has_zbb))
(rv_minu x y))
(rule 1 (gen_select_xreg (int_compare_decompose cc x y) x y)
(if-let (IntCC.SignedLessThan) (intcc_without_eq cc))
(if-let $true (has_zbb))
(rv_min x y))
(rule 1 (gen_select_xreg (int_compare_decompose cc x y) x y)
(if-let (IntCC.UnsignedGreaterThan) (intcc_without_eq cc))
(if-let $true (has_zbb))
(rv_maxu x y))
(rule 1 (gen_select_xreg (int_compare_decompose cc x y) x y)
(if-let (IntCC.SignedGreaterThan) (intcc_without_eq cc))
(if-let $true (has_zbb))
(rv_max x y))
(rule 0 (gen_select_xreg c x y)
(let
((dst WritableReg (temp_writable_xreg))
(_ Unit (emit (MInst.Select dst c x y))))
(writable_reg_to_reg dst)))
(decl gen_select_vreg (IntegerCompare VReg VReg) VReg)
(rule (gen_select_vreg c x y)
(let
((dst WritableReg (temp_writable_vreg))
(_ Unit (emit (MInst.Select dst c (vreg_to_reg x) (vreg_to_reg y)))))
(writable_reg_to_reg dst)))
(decl gen_select_freg (IntegerCompare FReg FReg) FReg)
(rule (gen_select_freg c x y)
(let
((dst WritableReg (temp_writable_freg))
(_ Unit (emit (MInst.Select dst c (freg_to_reg x) (freg_to_reg y)))))
(writable_reg_to_reg dst)))
(decl gen_select_regs (IntegerCompare ValueRegs ValueRegs) ValueRegs)
(rule (gen_select_regs c x y)
(let
((dst1 WritableReg (temp_writable_xreg))
(dst2 WritableReg (temp_writable_xreg))
(_ Unit (emit (MInst.Select (writable_value_regs dst1 dst2) c x y))))
(value_regs dst1 dst2)))
(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)
;;;; load extern name
(decl load_ext_name (ExternalName i64) Reg)
(extern constructor load_ext_name load_ext_name)
(decl elf_tls_get_addr (ExternalName) Reg)
(rule (elf_tls_get_addr name)
(let ((dst WritableReg (temp_writable_reg $I64))
(_ Unit (emit (MInst.ElfTlsGetAddr dst name))))
dst))
;;;;
(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))))
(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)))
;; int scalar zero regs.
(decl int_zero_reg (Type) ValueRegs)
(extern constructor int_zero_reg int_zero_reg)
;; Consume a CmpResult, producing a branch on its result.
(decl cond_br (IntegerCompare CondBrTarget CondBrTarget) SideEffectNoResult)
(rule (cond_br cmp then else)
(SideEffectNoResult.Inst
(MInst.CondBr then else cmp)))
;; Helper for emitting the `j` mnemonic, an unconditional jump to label.
(decl rv_j (MachLabel) SideEffectNoResult)
(rule (rv_j label)
(SideEffectNoResult.Inst (MInst.Jal label)))
;; Construct an IntegerCompare value.
(decl int_compare (IntCC XReg XReg) IntegerCompare)
(extern constructor int_compare int_compare)
;; Extract the components of an `IntegerCompare`
(decl int_compare_decompose (IntCC XReg XReg) IntegerCompare)
(extern extractor infallible int_compare_decompose int_compare_decompose)
(decl label_to_br_target (MachLabel) CondBrTarget)
(extern constructor label_to_br_target label_to_br_target)
(convert MachLabel CondBrTarget label_to_br_target)
(decl cmp_eqz (XReg) IntegerCompare)
(rule (cmp_eqz r) (int_compare (IntCC.Equal) r (zero_reg)))
(decl cmp_nez (XReg) IntegerCompare)
(rule (cmp_nez r) (int_compare (IntCC.NotEqual) r (zero_reg)))
(decl cmp_eq (XReg XReg) IntegerCompare)
(rule (cmp_eq rs1 rs2) (int_compare (IntCC.Equal) rs1 rs2))
(decl cmp_ne (XReg XReg) IntegerCompare)
(rule (cmp_ne rs1 rs2) (int_compare (IntCC.NotEqual) rs1 rs2))
(decl cmp_lt (XReg XReg) IntegerCompare)
(rule (cmp_lt rs1 rs2) (int_compare (IntCC.SignedLessThan) rs1 rs2))
(decl cmp_ltz (XReg) IntegerCompare)
(rule (cmp_ltz rs) (int_compare (IntCC.SignedLessThan) rs (zero_reg)))
(decl cmp_gt (XReg XReg) IntegerCompare)
(rule (cmp_gt rs1 rs2) (int_compare (IntCC.SignedGreaterThan) rs1 rs2))
(decl cmp_ge (XReg XReg) IntegerCompare)
(rule (cmp_ge rs1 rs2) (int_compare (IntCC.SignedGreaterThanOrEqual) rs1 rs2))
(decl cmp_le (XReg XReg) IntegerCompare)
(rule (cmp_le rs1 rs2) (int_compare (IntCC.SignedLessThanOrEqual) rs1 rs2))
(decl cmp_gtu (XReg XReg) IntegerCompare)
(rule (cmp_gtu rs1 rs2) (int_compare (IntCC.UnsignedGreaterThan) rs1 rs2))
(decl cmp_geu (XReg XReg) IntegerCompare)
(rule (cmp_geu rs1 rs2) (int_compare (IntCC.UnsignedGreaterThanOrEqual) rs1 rs2))
(decl cmp_ltu (XReg XReg) IntegerCompare)
(rule (cmp_ltu rs1 rs2) (int_compare (IntCC.UnsignedLessThan) rs1 rs2))
(decl cmp_leu (XReg XReg) IntegerCompare)
(rule (cmp_leu rs1 rs2) (int_compare (IntCC.UnsignedLessThanOrEqual) rs1 rs2))
;; Helper to generate an `IntegerCompare` which represents the "truthy" value of
;; the input provided.
;;
;; This is used in `Select` and `brif` for example to generate conditional
;; branches. The returned comparison, when taken, represents that `Value` is
;; nonzero. When not taken the input `Value` is zero.
(decl lower_int_compare (Value) IntegerCompare)
;; Base case - convert to a "truthy" value and compare it against zero.
;;
;; Note that non-64-bit types need to be extended since the upper bits from
;; Cranelift's point of view are undefined. Favor a zero extension for 8-bit
;; types because that's a single `andi` instruction, but favor sign-extension
;; for 16 and 32-bit types because many RISC-V which operate on the low 32-bits.
;; Additionally the base 64-bit ISA has a single instruction for sign-extending
;; from 32 to 64-bits which makes that a bit cheaper if used.
;; of registers sign-extend the results.
(rule 0 (lower_int_compare val @ (value_type (fits_in_64 _)))
(cmp_nez (sext val)))
(rule 1 (lower_int_compare val @ (value_type $I8))
(cmp_nez (zext val)))
(rule 1 (lower_int_compare val @ (value_type $I128))
(cmp_nez (rv_or (value_regs_get val 0) (value_regs_get val 1))))
;; If the input value is itself an `icmp` we can avoid generating the result of
;; the `icmp` and instead move the comparison directly into the `IntegerCompare`
;; that's returned. Note that comparisons compare full registers so
;; sign-extension according to the integer comparison performed here is
;; required.
;;
;; Also note that as a small optimization `Equal` and `NotEqual` use
;; sign-extension for 32-bit values since the same result is produced with
;; either zero-or-sign extension and many values are already sign-extended given
;; the RV64 instruction set (e.g. `addw` adds 32-bit values and sign extends),
;; theoretically resulting in more efficient codegen.
(rule 2 (lower_int_compare (maybe_uextend (icmp cc a b @ (value_type (fits_in_64 in_ty)))))
(int_compare cc (zext a) (zext b)))
(rule 3 (lower_int_compare (maybe_uextend (icmp cc a b @ (value_type (fits_in_64 in_ty)))))
(if (signed_cond_code cc))
(int_compare cc (sext a) (sext b)))
(rule 4 (lower_int_compare (maybe_uextend (icmp cc @ (IntCC.Equal) a b @ (value_type $I32))))
(int_compare cc (sext a) (sext b)))
(rule 4 (lower_int_compare (maybe_uextend (icmp cc @ (IntCC.NotEqual) a b @ (value_type $I32))))
(int_compare cc (sext a) (sext b)))
;; If the input is an `fcmp` then the `FCmp` return value is directly
;; convertible to `IntegerCompare` which can shave off an instruction from the
;; fallback lowering above.
(rule 2 (lower_int_compare (maybe_uextend (fcmp cc a @ (value_type ty) b)))
(emit_fcmp cc ty a b))
(decl partial lower_branch (Inst MachLabelSlice) Unit)
(rule (lower_branch (jump _) (single_target label))
(emit_side_effect (rv_j label)))
(rule (lower_branch (brif v _ _) (two_targets then else))
(emit_side_effect (cond_br (lower_int_compare v) then else)))
(decl lower_br_table (Reg MachLabelSlice) 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))
;; Builds an instruction sequence that traps if the comparision succeeds.
(decl gen_trapif (IntCC XReg XReg TrapCode) InstOutput)
(rule (gen_trapif cc a b trap_code)
(side_effect (SideEffectNoResult.Inst (MInst.TrapIf a b cc trap_code))))
;; Builds an instruction sequence that traps if the input is non-zero.
(decl gen_trapnz (XReg TrapCode) InstOutput)
(rule (gen_trapnz test trap_code)
(gen_trapif (IntCC.NotEqual) test (zero_reg) trap_code))
;; Builds an instruction sequence that traps if the input is zero.
(decl gen_trapz (XReg TrapCode) InstOutput)
(rule (gen_trapz test trap_code)
(gen_trapif (IntCC.Equal) test (zero_reg) trap_code))
;;;; 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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Generates either 0 if `Value` is zero or -1 otherwise.
(decl gen_bmask (Value) XReg)
(rule 0 (gen_bmask val @ (value_type (fits_in_64 _)))
(let ((non_zero XReg (rv_snez (sext val))))
(rv_neg non_zero)))
(rule 1 (gen_bmask val @ (value_type $I128))
(let ((non_zero XReg (rv_snez (rv_or (value_regs_get val 0) (value_regs_get val 1)))))
(rv_neg non_zero)))
(decl lower_bmask (Value Type) ValueRegs)
(rule 0 (lower_bmask val (fits_in_64 _))
(value_reg (gen_bmask val)))
(rule 1 (lower_bmask val $I128)
(let ((bits XReg (gen_bmask val)))
(value_regs bits bits)))
;;;; 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 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 FCmp (enum
;; The comparison succeeded if `r` is one
(One (r XReg))
;; The comparison succeeded if `r` is zero
(Zero (r XReg))
))
(decl fcmp_invert (FCmp) FCmp)
(rule (fcmp_invert (FCmp.One r)) (FCmp.Zero r))
(rule (fcmp_invert (FCmp.Zero r)) (FCmp.One r))
(decl fcmp_to_compare (FCmp) IntegerCompare)
(rule (fcmp_to_compare (FCmp.One r)) (cmp_nez r))
(rule (fcmp_to_compare (FCmp.Zero r)) (cmp_eqz r))
(convert FCmp IntegerCompare fcmp_to_compare)
;; Compare two floating point numbers and return a zero/non-zero result.
(decl emit_fcmp (FloatCC Type FReg FReg) FCmp)
;; Direct codegen for unordered comparisons is not that efficient, so invert
;; the comparison to get an ordered comparison and generate that. Then invert
;; the result to produce the final fcmp result.
(rule 0 (emit_fcmp cc ty a b)
(if-let $true (floatcc_unordered cc))
(fcmp_invert (emit_fcmp (floatcc_complement cc) ty a b)))
;; a is not nan && b is not nan
(rule 1 (emit_fcmp (FloatCC.Ordered) ty a b)
(FCmp.One (ordered ty a b)))
;; a == b
(rule 1 (emit_fcmp (FloatCC.Equal) ty a b)
(FCmp.One (rv_feq ty a b)))
;; a != b
;; == !(a == b)
(rule 1 (emit_fcmp (FloatCC.NotEqual) ty a b)
(FCmp.Zero (rv_feq ty a b)))
;; a < b || a > b
(rule 1 (emit_fcmp (FloatCC.OrderedNotEqual) ty a b)
(FCmp.One (rv_or (rv_flt ty a b) (rv_fgt ty a b))))
;; a < b
(rule 1 (emit_fcmp (FloatCC.LessThan) ty a b)
(FCmp.One (rv_flt ty a b)))
;; a <= b
(rule 1 (emit_fcmp (FloatCC.LessThanOrEqual) ty a b)
(FCmp.One (rv_fle ty a b)))
;; a > b
(rule 1 (emit_fcmp (FloatCC.GreaterThan) ty a b)
(FCmp.One (rv_fgt ty a b)))
;; a >= b
(rule 1 (emit_fcmp (FloatCC.GreaterThanOrEqual) ty a b)
(FCmp.One (rv_fge ty a b)))