//===-- RISCVInstrInfoSFB.td - Pseudos for SFB -------------*- tablegen -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file describes the pseudos for SFB (Short Forward Branch). // //===----------------------------------------------------------------------===// // Represents the Opcode for a branch instruction, used by SFB pseudos def bcc_opcode : RISCVOp { let OperandType = "OPERAND_BCC_OPCODE"; } def sfb_rhs : RegisterOperand { let OperandNamespace = "RISCVOp"; let OperandType = "OPERAND_SFB_RHS"; } // cond -> bcc_opcode for reg, reg branches def CCtoRISCVBCC : SDNodeXFormget(); RISCVCC::CondCode RvCC = getRISCVCCForIntCC(CC); RvCC = RISCVCC::getInverseBranchCondition(RvCC); unsigned BccOpcode = RISCVCC::getBrCond(RvCC); return CurDAG->getTargetConstant(BccOpcode, SDLoc(N), MVT::i32); }]>; // cond -> bcc_opcode (QC_E_BccI) def CCtoQC_E_BccI : SDNodeXFormget(); RISCVCC::CondCode RvCC = getRISCVCCForIntCC(CC); RvCC = RISCVCC::getInverseBranchCondition(RvCC); unsigned SelectOpc; switch (RvCC) { case RISCVCC::COND_LTU: case RISCVCC::COND_GEU: SelectOpc = RISCV::Select_GPRNoX0_Using_CC_UImm16NonZero_QC; break; default: // EQ/NE/LT/GE treated as signed condition codes SelectOpc = RISCV::Select_GPRNoX0_Using_CC_SImm16NonZero_QC; break; } unsigned BccOpcode = RISCVCC::getBrCond(RvCC, SelectOpc); return CurDAG->getTargetConstant(BccOpcode, SDLoc(N), MVT::i32); }]>; // cond -> bcc_opcode (QC_BeqI) def CCtoQC_BccI : SDNodeXFormget(); RISCVCC::CondCode RvCC = getRISCVCCForIntCC(CC); RvCC = RISCVCC::getInverseBranchCondition(RvCC); unsigned SelectOpc; switch (RvCC) { case RISCVCC::COND_LTU: case RISCVCC::COND_GEU: SelectOpc = RISCV::Select_GPRNoX0_Using_CC_UImm5NonZero_QC; break; default: // EQ/NE/LT/GE treated as signed condition codes SelectOpc = RISCV::Select_GPRNoX0_Using_CC_SImm5NonZero_QC; break; } unsigned BccOpcode = RISCVCC::getBrCond(RvCC, SelectOpc); return CurDAG->getTargetConstant(BccOpcode, SDLoc(N), MVT::i32); }]>; // For each of the short forward branch pseudos, corresponding code for // getting correct size of the pseduo is needed in getInstSizeInBytes. let Predicates = [HasShortForwardBranchIALU], isSelect = 1, Constraints = "$dst = $falsev", isCommutable = 1 in { // This instruction moves $truev to $dst when the condition is true. It will // be expanded to control flow in RISCVExpandPseudoInsts. def PseudoCCMOVGPR : Pseudo<(outs GPR:$dst), (ins GPR:$falsev, GPR:$truev, bcc_opcode:$bcc, GPR:$lhs, sfb_rhs:$rhs), []>, Sched<[WriteSFB, ReadSFBALU, ReadSFBALU, ReadSFBJmp, ReadSFBJmp]>; } let Predicates = [HasShortForwardBranchIALU] in def : Pat<(riscv_selectcc (XLenVT GPR:$lhs), GPR:$rhs, cond:$cc, (XLenVT GPR:$truev), GPR:$falsev), (PseudoCCMOVGPR GPR:$falsev, GPR:$truev, (CCtoRISCVBCC $cc), GPR:$lhs, GPR:$rhs)>; // This should always expand to a branch+c.mv so the size is 6 or 4 if the // branch is compressible. let Predicates = [HasConditionalMoveFusion, NoShortForwardBranch], Constraints = "$dst = $falsev", isCommutable = 1 in { // This instruction moves $truev to $dst when the condition is true. It will // be expanded to control flow in RISCVExpandPseudoInsts. // We use GPRNoX0 because c.mv cannot encode X0. def PseudoCCMOVGPRNoX0 : Pseudo<(outs GPRNoX0:$dst), (ins GPRNoX0:$falsev, GPRNoX0:$truev, bcc_opcode:$bcc, GPR:$lhs, sfb_rhs:$rhs), []>, Sched<[]>; } let Predicates = [HasConditionalMoveFusion, NoShortForwardBranch] in def :Pat<(riscv_selectcc (XLenVT GPR:$lhs), GPR:$rhs, cond:$cc, (XLenVT GPRNoX0:$truev), GPRNoX0:$falsev), (PseudoCCMOVGPRNoX0 GPRNoX0:$falsev, GPRNoX0:$truev, (CCtoRISCVBCC $cc), GPR:$lhs, GPR:$rhs )>; class SFBALU_rr : Pseudo<(outs GPR:$dst), (ins GPR:$falsev, GPR:$rs1, GPR:$rs2, bcc_opcode:$bcc, GPR:$lhs, sfb_rhs:$rhs), []>, Sched<[WriteSFB, ReadSFBALU, ReadSFBALU, ReadSFBALU, ReadSFBJmp, ReadSFBJmp]> { let hasSideEffects = 0; let mayLoad = 0; let mayStore = 0; let Constraints = "$dst = $falsev"; } class SFBALU_ri : Pseudo<(outs GPR:$dst), (ins GPR:$falsev, GPR:$rs1, simm12_lo:$imm, bcc_opcode:$bcc, GPR:$lhs, sfb_rhs:$rhs), []>, Sched<[WriteSFB, ReadSFBALU, ReadSFBALU, ReadSFBJmp, ReadSFBJmp]> { let hasSideEffects = 0; let mayLoad = 0; let mayStore = 0; let Constraints = "$dst = $falsev"; } class SFBLUI : Pseudo<(outs GPR:$dst), (ins GPR:$falsev, uimm20_lui:$imm, bcc_opcode:$bcc, GPR:$lhs, sfb_rhs:$rhs), []> { let hasSideEffects = 0; let mayLoad = 0; let mayStore = 0; let Constraints = "$dst = $falsev"; } class SFBLoad : Pseudo<(outs GPR:$dst), (ins GPR:$falsev, GPR:$rs1, simm12_lo:$imm, bcc_opcode:$bcc, GPR:$lhs, sfb_rhs:$rhs), []> { let hasSideEffects = 0; let mayLoad = 1; let mayStore = 0; let Constraints = "$dst = $falsev"; } class SFBShift_ri : Pseudo<(outs GPR:$dst), (ins GPR:$falsev, GPR:$rs1, uimmlog2xlen:$imm, bcc_opcode:$bcc, GPR:$lhs, sfb_rhs:$rhs), []>, Sched<[WriteSFB, ReadSFBALU, ReadSFBALU, ReadSFBJmp, ReadSFBJmp]> { let hasSideEffects = 0; let mayLoad = 0; let mayStore = 0; let Constraints = "$dst = $falsev"; } class SFBShiftW_ri : Pseudo<(outs GPR:$dst), (ins GPR:$falsev, GPR:$rs1, uimm5:$imm, bcc_opcode:$bcc, GPR:$lhs, sfb_rhs:$rhs), []>, Sched<[WriteSFB, ReadSFBALU, ReadSFBALU, ReadSFBJmp, ReadSFBJmp]> { let hasSideEffects = 0; let mayLoad = 0; let mayStore = 0; let Constraints = "$dst = $falsev"; } // Conditional binops, that updates update $dst to (op rs1, rs2) when condition // is true. Returns $falsev otherwise. Selected by optimizeSelect. // TODO: Can we use DefaultOperands on the regular binop to accomplish this more // like how ARM does predication? let Predicates = [HasShortForwardBranchIALU] in { def PseudoCCADD : SFBALU_rr; def PseudoCCSUB : SFBALU_rr; def PseudoCCSLL : SFBALU_rr; def PseudoCCSRL : SFBALU_rr; def PseudoCCSRA : SFBALU_rr; def PseudoCCAND : SFBALU_rr; def PseudoCCOR : SFBALU_rr; def PseudoCCXOR : SFBALU_rr; def PseudoCCADDI : SFBALU_ri; def PseudoCCANDI : SFBALU_ri; def PseudoCCORI : SFBALU_ri; def PseudoCCXORI : SFBALU_ri; def PseudoCCLUI : SFBLUI; def PseudoCCSLLI : SFBShift_ri; def PseudoCCSRLI : SFBShift_ri; def PseudoCCSRAI : SFBShift_ri; // RV64I instructions def PseudoCCADDW : SFBALU_rr; def PseudoCCSUBW : SFBALU_rr; def PseudoCCSLLW : SFBALU_rr; def PseudoCCSRLW : SFBALU_rr; def PseudoCCSRAW : SFBALU_rr; def PseudoCCADDIW : SFBALU_ri; def PseudoCCSLLIW : SFBShiftW_ri; def PseudoCCSRLIW : SFBShiftW_ri; def PseudoCCSRAIW : SFBShiftW_ri; // Zbb/Zbkb instructions def PseudoCCANDN : SFBALU_rr; def PseudoCCORN : SFBALU_rr; def PseudoCCXNOR : SFBALU_rr; } // imm -> bcc_opcode for reg, reg branches def RISCVCCtoRISCVBCC : SDNodeXForm(N)->getZExtValue(); auto RvCC = static_cast(CCImm); RvCC = RISCVCC::getInverseBranchCondition(RvCC); unsigned BccOpcode = RISCVCC::getBrCond(RvCC); return CurDAG->getTargetConstant(BccOpcode, SDLoc(N), MVT::i32); }]>; let Predicates = [HasShortForwardBranchIALU] in def : Pat<(XLenVT (abs GPR:$rs1)), (PseudoCCSUB (XLenVT GPR:$rs1), (XLenVT X0), (XLenVT GPR:$rs1), (RISCVCCtoRISCVBCC (i32 /* COND_LT */ 2)),(XLenVT GPR:$rs1), (XLenVT X0))>; let Predicates = [HasShortForwardBranchIALU, IsRV64] in def : Pat<(sext_inreg (abs 33signbits_node:$rs1), i32), (PseudoCCSUBW (i64 GPR:$rs1), (i64 X0), (i64 GPR:$rs1), (RISCVCCtoRISCVBCC (i32 /* COND_LT */ 2)), (i64 GPR:$rs1), (i64 X0))>; let Predicates = [HasShortForwardBranchIMinMax] in { def PseudoCCMAX : SFBALU_rr; def PseudoCCMIN : SFBALU_rr; def PseudoCCMAXU : SFBALU_rr; def PseudoCCMINU : SFBALU_rr; } let Predicates = [HasShortForwardBranchIMul] in def PseudoCCMUL : SFBALU_rr; let Predicates = [HasShortForwardBranchILoad] in { def PseudoCCLB : SFBLoad; def PseudoCCLH : SFBLoad; def PseudoCCLW : SFBLoad; def PseudoCCLHU : SFBLoad; def PseudoCCLBU : SFBLoad; def PseudoCCLWU : SFBLoad; def PseudoCCLD : SFBLoad; }