1*0b57cec5SDimitry Andric //===-- RISCVISelDAGToDAG.cpp - A dag to dag inst selector for RISCV ------===// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric // 9*0b57cec5SDimitry Andric // This file defines an instruction selector for the RISCV target. 10*0b57cec5SDimitry Andric // 11*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 12*0b57cec5SDimitry Andric 13*0b57cec5SDimitry Andric #include "MCTargetDesc/RISCVMCTargetDesc.h" 14*0b57cec5SDimitry Andric #include "RISCV.h" 15*0b57cec5SDimitry Andric #include "RISCVTargetMachine.h" 16*0b57cec5SDimitry Andric #include "Utils/RISCVMatInt.h" 17*0b57cec5SDimitry Andric #include "llvm/CodeGen/MachineFrameInfo.h" 18*0b57cec5SDimitry Andric #include "llvm/CodeGen/SelectionDAGISel.h" 19*0b57cec5SDimitry Andric #include "llvm/Support/Debug.h" 20*0b57cec5SDimitry Andric #include "llvm/Support/MathExtras.h" 21*0b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 22*0b57cec5SDimitry Andric using namespace llvm; 23*0b57cec5SDimitry Andric 24*0b57cec5SDimitry Andric #define DEBUG_TYPE "riscv-isel" 25*0b57cec5SDimitry Andric 26*0b57cec5SDimitry Andric // RISCV-specific code to select RISCV machine instructions for 27*0b57cec5SDimitry Andric // SelectionDAG operations. 28*0b57cec5SDimitry Andric namespace { 29*0b57cec5SDimitry Andric class RISCVDAGToDAGISel final : public SelectionDAGISel { 30*0b57cec5SDimitry Andric const RISCVSubtarget *Subtarget; 31*0b57cec5SDimitry Andric 32*0b57cec5SDimitry Andric public: 33*0b57cec5SDimitry Andric explicit RISCVDAGToDAGISel(RISCVTargetMachine &TargetMachine) 34*0b57cec5SDimitry Andric : SelectionDAGISel(TargetMachine) {} 35*0b57cec5SDimitry Andric 36*0b57cec5SDimitry Andric StringRef getPassName() const override { 37*0b57cec5SDimitry Andric return "RISCV DAG->DAG Pattern Instruction Selection"; 38*0b57cec5SDimitry Andric } 39*0b57cec5SDimitry Andric 40*0b57cec5SDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override { 41*0b57cec5SDimitry Andric Subtarget = &MF.getSubtarget<RISCVSubtarget>(); 42*0b57cec5SDimitry Andric return SelectionDAGISel::runOnMachineFunction(MF); 43*0b57cec5SDimitry Andric } 44*0b57cec5SDimitry Andric 45*0b57cec5SDimitry Andric void PostprocessISelDAG() override; 46*0b57cec5SDimitry Andric 47*0b57cec5SDimitry Andric void Select(SDNode *Node) override; 48*0b57cec5SDimitry Andric 49*0b57cec5SDimitry Andric bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, 50*0b57cec5SDimitry Andric std::vector<SDValue> &OutOps) override; 51*0b57cec5SDimitry Andric 52*0b57cec5SDimitry Andric bool SelectAddrFI(SDValue Addr, SDValue &Base); 53*0b57cec5SDimitry Andric 54*0b57cec5SDimitry Andric // Include the pieces autogenerated from the target description. 55*0b57cec5SDimitry Andric #include "RISCVGenDAGISel.inc" 56*0b57cec5SDimitry Andric 57*0b57cec5SDimitry Andric private: 58*0b57cec5SDimitry Andric void doPeepholeLoadStoreADDI(); 59*0b57cec5SDimitry Andric }; 60*0b57cec5SDimitry Andric } 61*0b57cec5SDimitry Andric 62*0b57cec5SDimitry Andric void RISCVDAGToDAGISel::PostprocessISelDAG() { 63*0b57cec5SDimitry Andric doPeepholeLoadStoreADDI(); 64*0b57cec5SDimitry Andric } 65*0b57cec5SDimitry Andric 66*0b57cec5SDimitry Andric static SDNode *selectImm(SelectionDAG *CurDAG, const SDLoc &DL, int64_t Imm, 67*0b57cec5SDimitry Andric MVT XLenVT) { 68*0b57cec5SDimitry Andric RISCVMatInt::InstSeq Seq; 69*0b57cec5SDimitry Andric RISCVMatInt::generateInstSeq(Imm, XLenVT == MVT::i64, Seq); 70*0b57cec5SDimitry Andric 71*0b57cec5SDimitry Andric SDNode *Result; 72*0b57cec5SDimitry Andric SDValue SrcReg = CurDAG->getRegister(RISCV::X0, XLenVT); 73*0b57cec5SDimitry Andric for (RISCVMatInt::Inst &Inst : Seq) { 74*0b57cec5SDimitry Andric SDValue SDImm = CurDAG->getTargetConstant(Inst.Imm, DL, XLenVT); 75*0b57cec5SDimitry Andric if (Inst.Opc == RISCV::LUI) 76*0b57cec5SDimitry Andric Result = CurDAG->getMachineNode(RISCV::LUI, DL, XLenVT, SDImm); 77*0b57cec5SDimitry Andric else 78*0b57cec5SDimitry Andric Result = CurDAG->getMachineNode(Inst.Opc, DL, XLenVT, SrcReg, SDImm); 79*0b57cec5SDimitry Andric 80*0b57cec5SDimitry Andric // Only the first instruction has X0 as its source. 81*0b57cec5SDimitry Andric SrcReg = SDValue(Result, 0); 82*0b57cec5SDimitry Andric } 83*0b57cec5SDimitry Andric 84*0b57cec5SDimitry Andric return Result; 85*0b57cec5SDimitry Andric } 86*0b57cec5SDimitry Andric 87*0b57cec5SDimitry Andric // Returns true if the Node is an ISD::AND with a constant argument. If so, 88*0b57cec5SDimitry Andric // set Mask to that constant value. 89*0b57cec5SDimitry Andric static bool isConstantMask(SDNode *Node, uint64_t &Mask) { 90*0b57cec5SDimitry Andric if (Node->getOpcode() == ISD::AND && 91*0b57cec5SDimitry Andric Node->getOperand(1).getOpcode() == ISD::Constant) { 92*0b57cec5SDimitry Andric Mask = cast<ConstantSDNode>(Node->getOperand(1))->getZExtValue(); 93*0b57cec5SDimitry Andric return true; 94*0b57cec5SDimitry Andric } 95*0b57cec5SDimitry Andric return false; 96*0b57cec5SDimitry Andric } 97*0b57cec5SDimitry Andric 98*0b57cec5SDimitry Andric void RISCVDAGToDAGISel::Select(SDNode *Node) { 99*0b57cec5SDimitry Andric // If we have a custom node, we have already selected. 100*0b57cec5SDimitry Andric if (Node->isMachineOpcode()) { 101*0b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "== "; Node->dump(CurDAG); dbgs() << "\n"); 102*0b57cec5SDimitry Andric Node->setNodeId(-1); 103*0b57cec5SDimitry Andric return; 104*0b57cec5SDimitry Andric } 105*0b57cec5SDimitry Andric 106*0b57cec5SDimitry Andric // Instruction Selection not handled by the auto-generated tablegen selection 107*0b57cec5SDimitry Andric // should be handled here. 108*0b57cec5SDimitry Andric unsigned Opcode = Node->getOpcode(); 109*0b57cec5SDimitry Andric MVT XLenVT = Subtarget->getXLenVT(); 110*0b57cec5SDimitry Andric SDLoc DL(Node); 111*0b57cec5SDimitry Andric EVT VT = Node->getValueType(0); 112*0b57cec5SDimitry Andric 113*0b57cec5SDimitry Andric switch (Opcode) { 114*0b57cec5SDimitry Andric case ISD::Constant: { 115*0b57cec5SDimitry Andric auto ConstNode = cast<ConstantSDNode>(Node); 116*0b57cec5SDimitry Andric if (VT == XLenVT && ConstNode->isNullValue()) { 117*0b57cec5SDimitry Andric SDValue New = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), SDLoc(Node), 118*0b57cec5SDimitry Andric RISCV::X0, XLenVT); 119*0b57cec5SDimitry Andric ReplaceNode(Node, New.getNode()); 120*0b57cec5SDimitry Andric return; 121*0b57cec5SDimitry Andric } 122*0b57cec5SDimitry Andric int64_t Imm = ConstNode->getSExtValue(); 123*0b57cec5SDimitry Andric if (XLenVT == MVT::i64) { 124*0b57cec5SDimitry Andric ReplaceNode(Node, selectImm(CurDAG, SDLoc(Node), Imm, XLenVT)); 125*0b57cec5SDimitry Andric return; 126*0b57cec5SDimitry Andric } 127*0b57cec5SDimitry Andric break; 128*0b57cec5SDimitry Andric } 129*0b57cec5SDimitry Andric case ISD::FrameIndex: { 130*0b57cec5SDimitry Andric SDValue Imm = CurDAG->getTargetConstant(0, DL, XLenVT); 131*0b57cec5SDimitry Andric int FI = cast<FrameIndexSDNode>(Node)->getIndex(); 132*0b57cec5SDimitry Andric SDValue TFI = CurDAG->getTargetFrameIndex(FI, VT); 133*0b57cec5SDimitry Andric ReplaceNode(Node, CurDAG->getMachineNode(RISCV::ADDI, DL, VT, TFI, Imm)); 134*0b57cec5SDimitry Andric return; 135*0b57cec5SDimitry Andric } 136*0b57cec5SDimitry Andric case ISD::SRL: { 137*0b57cec5SDimitry Andric if (!Subtarget->is64Bit()) 138*0b57cec5SDimitry Andric break; 139*0b57cec5SDimitry Andric SDValue Op0 = Node->getOperand(0); 140*0b57cec5SDimitry Andric SDValue Op1 = Node->getOperand(1); 141*0b57cec5SDimitry Andric uint64_t Mask; 142*0b57cec5SDimitry Andric // Match (srl (and val, mask), imm) where the result would be a 143*0b57cec5SDimitry Andric // zero-extended 32-bit integer. i.e. the mask is 0xffffffff or the result 144*0b57cec5SDimitry Andric // is equivalent to this (SimplifyDemandedBits may have removed lower bits 145*0b57cec5SDimitry Andric // from the mask that aren't necessary due to the right-shifting). 146*0b57cec5SDimitry Andric if (Op1.getOpcode() == ISD::Constant && 147*0b57cec5SDimitry Andric isConstantMask(Op0.getNode(), Mask)) { 148*0b57cec5SDimitry Andric uint64_t ShAmt = cast<ConstantSDNode>(Op1.getNode())->getZExtValue(); 149*0b57cec5SDimitry Andric 150*0b57cec5SDimitry Andric if ((Mask | maskTrailingOnes<uint64_t>(ShAmt)) == 0xffffffff) { 151*0b57cec5SDimitry Andric SDValue ShAmtVal = 152*0b57cec5SDimitry Andric CurDAG->getTargetConstant(ShAmt, SDLoc(Node), XLenVT); 153*0b57cec5SDimitry Andric CurDAG->SelectNodeTo(Node, RISCV::SRLIW, XLenVT, Op0.getOperand(0), 154*0b57cec5SDimitry Andric ShAmtVal); 155*0b57cec5SDimitry Andric return; 156*0b57cec5SDimitry Andric } 157*0b57cec5SDimitry Andric } 158*0b57cec5SDimitry Andric break; 159*0b57cec5SDimitry Andric } 160*0b57cec5SDimitry Andric case RISCVISD::READ_CYCLE_WIDE: 161*0b57cec5SDimitry Andric assert(!Subtarget->is64Bit() && "READ_CYCLE_WIDE is only used on riscv32"); 162*0b57cec5SDimitry Andric 163*0b57cec5SDimitry Andric ReplaceNode(Node, CurDAG->getMachineNode(RISCV::ReadCycleWide, DL, MVT::i32, 164*0b57cec5SDimitry Andric MVT::i32, MVT::Other, 165*0b57cec5SDimitry Andric Node->getOperand(0))); 166*0b57cec5SDimitry Andric return; 167*0b57cec5SDimitry Andric } 168*0b57cec5SDimitry Andric 169*0b57cec5SDimitry Andric // Select the default instruction. 170*0b57cec5SDimitry Andric SelectCode(Node); 171*0b57cec5SDimitry Andric } 172*0b57cec5SDimitry Andric 173*0b57cec5SDimitry Andric bool RISCVDAGToDAGISel::SelectInlineAsmMemoryOperand( 174*0b57cec5SDimitry Andric const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) { 175*0b57cec5SDimitry Andric switch (ConstraintID) { 176*0b57cec5SDimitry Andric case InlineAsm::Constraint_i: 177*0b57cec5SDimitry Andric case InlineAsm::Constraint_m: 178*0b57cec5SDimitry Andric // We just support simple memory operands that have a single address 179*0b57cec5SDimitry Andric // operand and need no special handling. 180*0b57cec5SDimitry Andric OutOps.push_back(Op); 181*0b57cec5SDimitry Andric return false; 182*0b57cec5SDimitry Andric case InlineAsm::Constraint_A: 183*0b57cec5SDimitry Andric OutOps.push_back(Op); 184*0b57cec5SDimitry Andric return false; 185*0b57cec5SDimitry Andric default: 186*0b57cec5SDimitry Andric break; 187*0b57cec5SDimitry Andric } 188*0b57cec5SDimitry Andric 189*0b57cec5SDimitry Andric return true; 190*0b57cec5SDimitry Andric } 191*0b57cec5SDimitry Andric 192*0b57cec5SDimitry Andric bool RISCVDAGToDAGISel::SelectAddrFI(SDValue Addr, SDValue &Base) { 193*0b57cec5SDimitry Andric if (auto FIN = dyn_cast<FrameIndexSDNode>(Addr)) { 194*0b57cec5SDimitry Andric Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), Subtarget->getXLenVT()); 195*0b57cec5SDimitry Andric return true; 196*0b57cec5SDimitry Andric } 197*0b57cec5SDimitry Andric return false; 198*0b57cec5SDimitry Andric } 199*0b57cec5SDimitry Andric 200*0b57cec5SDimitry Andric // Merge an ADDI into the offset of a load/store instruction where possible. 201*0b57cec5SDimitry Andric // (load (add base, off), 0) -> (load base, off) 202*0b57cec5SDimitry Andric // (store val, (add base, off)) -> (store val, base, off) 203*0b57cec5SDimitry Andric void RISCVDAGToDAGISel::doPeepholeLoadStoreADDI() { 204*0b57cec5SDimitry Andric SelectionDAG::allnodes_iterator Position(CurDAG->getRoot().getNode()); 205*0b57cec5SDimitry Andric ++Position; 206*0b57cec5SDimitry Andric 207*0b57cec5SDimitry Andric while (Position != CurDAG->allnodes_begin()) { 208*0b57cec5SDimitry Andric SDNode *N = &*--Position; 209*0b57cec5SDimitry Andric // Skip dead nodes and any non-machine opcodes. 210*0b57cec5SDimitry Andric if (N->use_empty() || !N->isMachineOpcode()) 211*0b57cec5SDimitry Andric continue; 212*0b57cec5SDimitry Andric 213*0b57cec5SDimitry Andric int OffsetOpIdx; 214*0b57cec5SDimitry Andric int BaseOpIdx; 215*0b57cec5SDimitry Andric 216*0b57cec5SDimitry Andric // Only attempt this optimisation for I-type loads and S-type stores. 217*0b57cec5SDimitry Andric switch (N->getMachineOpcode()) { 218*0b57cec5SDimitry Andric default: 219*0b57cec5SDimitry Andric continue; 220*0b57cec5SDimitry Andric case RISCV::LB: 221*0b57cec5SDimitry Andric case RISCV::LH: 222*0b57cec5SDimitry Andric case RISCV::LW: 223*0b57cec5SDimitry Andric case RISCV::LBU: 224*0b57cec5SDimitry Andric case RISCV::LHU: 225*0b57cec5SDimitry Andric case RISCV::LWU: 226*0b57cec5SDimitry Andric case RISCV::LD: 227*0b57cec5SDimitry Andric case RISCV::FLW: 228*0b57cec5SDimitry Andric case RISCV::FLD: 229*0b57cec5SDimitry Andric BaseOpIdx = 0; 230*0b57cec5SDimitry Andric OffsetOpIdx = 1; 231*0b57cec5SDimitry Andric break; 232*0b57cec5SDimitry Andric case RISCV::SB: 233*0b57cec5SDimitry Andric case RISCV::SH: 234*0b57cec5SDimitry Andric case RISCV::SW: 235*0b57cec5SDimitry Andric case RISCV::SD: 236*0b57cec5SDimitry Andric case RISCV::FSW: 237*0b57cec5SDimitry Andric case RISCV::FSD: 238*0b57cec5SDimitry Andric BaseOpIdx = 1; 239*0b57cec5SDimitry Andric OffsetOpIdx = 2; 240*0b57cec5SDimitry Andric break; 241*0b57cec5SDimitry Andric } 242*0b57cec5SDimitry Andric 243*0b57cec5SDimitry Andric // Currently, the load/store offset must be 0 to be considered for this 244*0b57cec5SDimitry Andric // peephole optimisation. 245*0b57cec5SDimitry Andric if (!isa<ConstantSDNode>(N->getOperand(OffsetOpIdx)) || 246*0b57cec5SDimitry Andric N->getConstantOperandVal(OffsetOpIdx) != 0) 247*0b57cec5SDimitry Andric continue; 248*0b57cec5SDimitry Andric 249*0b57cec5SDimitry Andric SDValue Base = N->getOperand(BaseOpIdx); 250*0b57cec5SDimitry Andric 251*0b57cec5SDimitry Andric // If the base is an ADDI, we can merge it in to the load/store. 252*0b57cec5SDimitry Andric if (!Base.isMachineOpcode() || Base.getMachineOpcode() != RISCV::ADDI) 253*0b57cec5SDimitry Andric continue; 254*0b57cec5SDimitry Andric 255*0b57cec5SDimitry Andric SDValue ImmOperand = Base.getOperand(1); 256*0b57cec5SDimitry Andric 257*0b57cec5SDimitry Andric if (auto Const = dyn_cast<ConstantSDNode>(ImmOperand)) { 258*0b57cec5SDimitry Andric ImmOperand = CurDAG->getTargetConstant( 259*0b57cec5SDimitry Andric Const->getSExtValue(), SDLoc(ImmOperand), ImmOperand.getValueType()); 260*0b57cec5SDimitry Andric } else if (auto GA = dyn_cast<GlobalAddressSDNode>(ImmOperand)) { 261*0b57cec5SDimitry Andric ImmOperand = CurDAG->getTargetGlobalAddress( 262*0b57cec5SDimitry Andric GA->getGlobal(), SDLoc(ImmOperand), ImmOperand.getValueType(), 263*0b57cec5SDimitry Andric GA->getOffset(), GA->getTargetFlags()); 264*0b57cec5SDimitry Andric } else { 265*0b57cec5SDimitry Andric continue; 266*0b57cec5SDimitry Andric } 267*0b57cec5SDimitry Andric 268*0b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "Folding add-immediate into mem-op:\nBase: "); 269*0b57cec5SDimitry Andric LLVM_DEBUG(Base->dump(CurDAG)); 270*0b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "\nN: "); 271*0b57cec5SDimitry Andric LLVM_DEBUG(N->dump(CurDAG)); 272*0b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "\n"); 273*0b57cec5SDimitry Andric 274*0b57cec5SDimitry Andric // Modify the offset operand of the load/store. 275*0b57cec5SDimitry Andric if (BaseOpIdx == 0) // Load 276*0b57cec5SDimitry Andric CurDAG->UpdateNodeOperands(N, Base.getOperand(0), ImmOperand, 277*0b57cec5SDimitry Andric N->getOperand(2)); 278*0b57cec5SDimitry Andric else // Store 279*0b57cec5SDimitry Andric CurDAG->UpdateNodeOperands(N, N->getOperand(0), Base.getOperand(0), 280*0b57cec5SDimitry Andric ImmOperand, N->getOperand(3)); 281*0b57cec5SDimitry Andric 282*0b57cec5SDimitry Andric // The add-immediate may now be dead, in which case remove it. 283*0b57cec5SDimitry Andric if (Base.getNode()->use_empty()) 284*0b57cec5SDimitry Andric CurDAG->RemoveDeadNode(Base.getNode()); 285*0b57cec5SDimitry Andric } 286*0b57cec5SDimitry Andric } 287*0b57cec5SDimitry Andric 288*0b57cec5SDimitry Andric // This pass converts a legalized DAG into a RISCV-specific DAG, ready 289*0b57cec5SDimitry Andric // for instruction scheduling. 290*0b57cec5SDimitry Andric FunctionPass *llvm::createRISCVISelDag(RISCVTargetMachine &TM) { 291*0b57cec5SDimitry Andric return new RISCVDAGToDAGISel(TM); 292*0b57cec5SDimitry Andric } 293