10b57cec5SDimitry Andric //===-- RISCVISelDAGToDAG.cpp - A dag to dag inst selector for RISCV ------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This file defines an instruction selector for the RISCV target. 100b57cec5SDimitry Andric // 110b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 120b57cec5SDimitry Andric 13*5ffd83dbSDimitry Andric #include "RISCVISelDAGToDAG.h" 140b57cec5SDimitry Andric #include "MCTargetDesc/RISCVMCTargetDesc.h" 150b57cec5SDimitry Andric #include "Utils/RISCVMatInt.h" 160b57cec5SDimitry Andric #include "llvm/CodeGen/MachineFrameInfo.h" 17*5ffd83dbSDimitry Andric #include "llvm/Support/Alignment.h" 180b57cec5SDimitry Andric #include "llvm/Support/Debug.h" 190b57cec5SDimitry Andric #include "llvm/Support/MathExtras.h" 200b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 21*5ffd83dbSDimitry Andric 220b57cec5SDimitry Andric using namespace llvm; 230b57cec5SDimitry Andric 240b57cec5SDimitry Andric #define DEBUG_TYPE "riscv-isel" 250b57cec5SDimitry Andric 260b57cec5SDimitry Andric void RISCVDAGToDAGISel::PostprocessISelDAG() { 270b57cec5SDimitry Andric doPeepholeLoadStoreADDI(); 280b57cec5SDimitry Andric } 290b57cec5SDimitry Andric 300b57cec5SDimitry Andric static SDNode *selectImm(SelectionDAG *CurDAG, const SDLoc &DL, int64_t Imm, 310b57cec5SDimitry Andric MVT XLenVT) { 320b57cec5SDimitry Andric RISCVMatInt::InstSeq Seq; 330b57cec5SDimitry Andric RISCVMatInt::generateInstSeq(Imm, XLenVT == MVT::i64, Seq); 340b57cec5SDimitry Andric 358bcb0991SDimitry Andric SDNode *Result = nullptr; 360b57cec5SDimitry Andric SDValue SrcReg = CurDAG->getRegister(RISCV::X0, XLenVT); 370b57cec5SDimitry Andric for (RISCVMatInt::Inst &Inst : Seq) { 380b57cec5SDimitry Andric SDValue SDImm = CurDAG->getTargetConstant(Inst.Imm, DL, XLenVT); 390b57cec5SDimitry Andric if (Inst.Opc == RISCV::LUI) 400b57cec5SDimitry Andric Result = CurDAG->getMachineNode(RISCV::LUI, DL, XLenVT, SDImm); 410b57cec5SDimitry Andric else 420b57cec5SDimitry Andric Result = CurDAG->getMachineNode(Inst.Opc, DL, XLenVT, SrcReg, SDImm); 430b57cec5SDimitry Andric 440b57cec5SDimitry Andric // Only the first instruction has X0 as its source. 450b57cec5SDimitry Andric SrcReg = SDValue(Result, 0); 460b57cec5SDimitry Andric } 470b57cec5SDimitry Andric 480b57cec5SDimitry Andric return Result; 490b57cec5SDimitry Andric } 500b57cec5SDimitry Andric 510b57cec5SDimitry Andric // Returns true if the Node is an ISD::AND with a constant argument. If so, 520b57cec5SDimitry Andric // set Mask to that constant value. 530b57cec5SDimitry Andric static bool isConstantMask(SDNode *Node, uint64_t &Mask) { 540b57cec5SDimitry Andric if (Node->getOpcode() == ISD::AND && 550b57cec5SDimitry Andric Node->getOperand(1).getOpcode() == ISD::Constant) { 560b57cec5SDimitry Andric Mask = cast<ConstantSDNode>(Node->getOperand(1))->getZExtValue(); 570b57cec5SDimitry Andric return true; 580b57cec5SDimitry Andric } 590b57cec5SDimitry Andric return false; 600b57cec5SDimitry Andric } 610b57cec5SDimitry Andric 620b57cec5SDimitry Andric void RISCVDAGToDAGISel::Select(SDNode *Node) { 630b57cec5SDimitry Andric // If we have a custom node, we have already selected. 640b57cec5SDimitry Andric if (Node->isMachineOpcode()) { 650b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "== "; Node->dump(CurDAG); dbgs() << "\n"); 660b57cec5SDimitry Andric Node->setNodeId(-1); 670b57cec5SDimitry Andric return; 680b57cec5SDimitry Andric } 690b57cec5SDimitry Andric 700b57cec5SDimitry Andric // Instruction Selection not handled by the auto-generated tablegen selection 710b57cec5SDimitry Andric // should be handled here. 720b57cec5SDimitry Andric unsigned Opcode = Node->getOpcode(); 730b57cec5SDimitry Andric MVT XLenVT = Subtarget->getXLenVT(); 740b57cec5SDimitry Andric SDLoc DL(Node); 750b57cec5SDimitry Andric EVT VT = Node->getValueType(0); 760b57cec5SDimitry Andric 770b57cec5SDimitry Andric switch (Opcode) { 78*5ffd83dbSDimitry Andric case ISD::ADD: { 79*5ffd83dbSDimitry Andric // Optimize (add r, imm) to (addi (addi r, imm0) imm1) if applicable. The 80*5ffd83dbSDimitry Andric // immediate must be in specific ranges and have a single use. 81*5ffd83dbSDimitry Andric if (auto *ConstOp = dyn_cast<ConstantSDNode>(Node->getOperand(1))) { 82*5ffd83dbSDimitry Andric if (!(ConstOp->hasOneUse())) 83*5ffd83dbSDimitry Andric break; 84*5ffd83dbSDimitry Andric // The imm must be in range [-4096,-2049] or [2048,4094]. 85*5ffd83dbSDimitry Andric int64_t Imm = ConstOp->getSExtValue(); 86*5ffd83dbSDimitry Andric if (!(-4096 <= Imm && Imm <= -2049) && !(2048 <= Imm && Imm <= 4094)) 87*5ffd83dbSDimitry Andric break; 88*5ffd83dbSDimitry Andric // Break the imm to imm0+imm1. 89*5ffd83dbSDimitry Andric SDLoc DL(Node); 90*5ffd83dbSDimitry Andric EVT VT = Node->getValueType(0); 91*5ffd83dbSDimitry Andric const SDValue ImmOp0 = CurDAG->getTargetConstant(Imm - Imm / 2, DL, VT); 92*5ffd83dbSDimitry Andric const SDValue ImmOp1 = CurDAG->getTargetConstant(Imm / 2, DL, VT); 93*5ffd83dbSDimitry Andric auto *NodeAddi0 = CurDAG->getMachineNode(RISCV::ADDI, DL, VT, 94*5ffd83dbSDimitry Andric Node->getOperand(0), ImmOp0); 95*5ffd83dbSDimitry Andric auto *NodeAddi1 = CurDAG->getMachineNode(RISCV::ADDI, DL, VT, 96*5ffd83dbSDimitry Andric SDValue(NodeAddi0, 0), ImmOp1); 97*5ffd83dbSDimitry Andric ReplaceNode(Node, NodeAddi1); 98*5ffd83dbSDimitry Andric return; 99*5ffd83dbSDimitry Andric } 100*5ffd83dbSDimitry Andric break; 101*5ffd83dbSDimitry Andric } 1020b57cec5SDimitry Andric case ISD::Constant: { 1030b57cec5SDimitry Andric auto ConstNode = cast<ConstantSDNode>(Node); 1040b57cec5SDimitry Andric if (VT == XLenVT && ConstNode->isNullValue()) { 1050b57cec5SDimitry Andric SDValue New = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), SDLoc(Node), 1060b57cec5SDimitry Andric RISCV::X0, XLenVT); 1070b57cec5SDimitry Andric ReplaceNode(Node, New.getNode()); 1080b57cec5SDimitry Andric return; 1090b57cec5SDimitry Andric } 1100b57cec5SDimitry Andric int64_t Imm = ConstNode->getSExtValue(); 1110b57cec5SDimitry Andric if (XLenVT == MVT::i64) { 1120b57cec5SDimitry Andric ReplaceNode(Node, selectImm(CurDAG, SDLoc(Node), Imm, XLenVT)); 1130b57cec5SDimitry Andric return; 1140b57cec5SDimitry Andric } 1150b57cec5SDimitry Andric break; 1160b57cec5SDimitry Andric } 1170b57cec5SDimitry Andric case ISD::FrameIndex: { 1180b57cec5SDimitry Andric SDValue Imm = CurDAG->getTargetConstant(0, DL, XLenVT); 1190b57cec5SDimitry Andric int FI = cast<FrameIndexSDNode>(Node)->getIndex(); 1200b57cec5SDimitry Andric SDValue TFI = CurDAG->getTargetFrameIndex(FI, VT); 1210b57cec5SDimitry Andric ReplaceNode(Node, CurDAG->getMachineNode(RISCV::ADDI, DL, VT, TFI, Imm)); 1220b57cec5SDimitry Andric return; 1230b57cec5SDimitry Andric } 1240b57cec5SDimitry Andric case ISD::SRL: { 1250b57cec5SDimitry Andric if (!Subtarget->is64Bit()) 1260b57cec5SDimitry Andric break; 1270b57cec5SDimitry Andric SDValue Op0 = Node->getOperand(0); 1280b57cec5SDimitry Andric SDValue Op1 = Node->getOperand(1); 1290b57cec5SDimitry Andric uint64_t Mask; 1300b57cec5SDimitry Andric // Match (srl (and val, mask), imm) where the result would be a 1310b57cec5SDimitry Andric // zero-extended 32-bit integer. i.e. the mask is 0xffffffff or the result 1320b57cec5SDimitry Andric // is equivalent to this (SimplifyDemandedBits may have removed lower bits 1330b57cec5SDimitry Andric // from the mask that aren't necessary due to the right-shifting). 1340b57cec5SDimitry Andric if (Op1.getOpcode() == ISD::Constant && 1350b57cec5SDimitry Andric isConstantMask(Op0.getNode(), Mask)) { 1360b57cec5SDimitry Andric uint64_t ShAmt = cast<ConstantSDNode>(Op1.getNode())->getZExtValue(); 1370b57cec5SDimitry Andric 1380b57cec5SDimitry Andric if ((Mask | maskTrailingOnes<uint64_t>(ShAmt)) == 0xffffffff) { 1390b57cec5SDimitry Andric SDValue ShAmtVal = 1400b57cec5SDimitry Andric CurDAG->getTargetConstant(ShAmt, SDLoc(Node), XLenVT); 1410b57cec5SDimitry Andric CurDAG->SelectNodeTo(Node, RISCV::SRLIW, XLenVT, Op0.getOperand(0), 1420b57cec5SDimitry Andric ShAmtVal); 1430b57cec5SDimitry Andric return; 1440b57cec5SDimitry Andric } 1450b57cec5SDimitry Andric } 1460b57cec5SDimitry Andric break; 1470b57cec5SDimitry Andric } 1480b57cec5SDimitry Andric case RISCVISD::READ_CYCLE_WIDE: 1490b57cec5SDimitry Andric assert(!Subtarget->is64Bit() && "READ_CYCLE_WIDE is only used on riscv32"); 1500b57cec5SDimitry Andric 1510b57cec5SDimitry Andric ReplaceNode(Node, CurDAG->getMachineNode(RISCV::ReadCycleWide, DL, MVT::i32, 1520b57cec5SDimitry Andric MVT::i32, MVT::Other, 1530b57cec5SDimitry Andric Node->getOperand(0))); 1540b57cec5SDimitry Andric return; 1550b57cec5SDimitry Andric } 1560b57cec5SDimitry Andric 1570b57cec5SDimitry Andric // Select the default instruction. 1580b57cec5SDimitry Andric SelectCode(Node); 1590b57cec5SDimitry Andric } 1600b57cec5SDimitry Andric 1610b57cec5SDimitry Andric bool RISCVDAGToDAGISel::SelectInlineAsmMemoryOperand( 1620b57cec5SDimitry Andric const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) { 1630b57cec5SDimitry Andric switch (ConstraintID) { 1640b57cec5SDimitry Andric case InlineAsm::Constraint_m: 1650b57cec5SDimitry Andric // We just support simple memory operands that have a single address 1660b57cec5SDimitry Andric // operand and need no special handling. 1670b57cec5SDimitry Andric OutOps.push_back(Op); 1680b57cec5SDimitry Andric return false; 1690b57cec5SDimitry Andric case InlineAsm::Constraint_A: 1700b57cec5SDimitry Andric OutOps.push_back(Op); 1710b57cec5SDimitry Andric return false; 1720b57cec5SDimitry Andric default: 1730b57cec5SDimitry Andric break; 1740b57cec5SDimitry Andric } 1750b57cec5SDimitry Andric 1760b57cec5SDimitry Andric return true; 1770b57cec5SDimitry Andric } 1780b57cec5SDimitry Andric 1790b57cec5SDimitry Andric bool RISCVDAGToDAGISel::SelectAddrFI(SDValue Addr, SDValue &Base) { 1800b57cec5SDimitry Andric if (auto FIN = dyn_cast<FrameIndexSDNode>(Addr)) { 1810b57cec5SDimitry Andric Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), Subtarget->getXLenVT()); 1820b57cec5SDimitry Andric return true; 1830b57cec5SDimitry Andric } 1840b57cec5SDimitry Andric return false; 1850b57cec5SDimitry Andric } 1860b57cec5SDimitry Andric 1870b57cec5SDimitry Andric // Merge an ADDI into the offset of a load/store instruction where possible. 188*5ffd83dbSDimitry Andric // (load (addi base, off1), off2) -> (load base, off1+off2) 189*5ffd83dbSDimitry Andric // (store val, (addi base, off1), off2) -> (store val, base, off1+off2) 190*5ffd83dbSDimitry Andric // This is possible when off1+off2 fits a 12-bit immediate. 1910b57cec5SDimitry Andric void RISCVDAGToDAGISel::doPeepholeLoadStoreADDI() { 1920b57cec5SDimitry Andric SelectionDAG::allnodes_iterator Position(CurDAG->getRoot().getNode()); 1930b57cec5SDimitry Andric ++Position; 1940b57cec5SDimitry Andric 1950b57cec5SDimitry Andric while (Position != CurDAG->allnodes_begin()) { 1960b57cec5SDimitry Andric SDNode *N = &*--Position; 1970b57cec5SDimitry Andric // Skip dead nodes and any non-machine opcodes. 1980b57cec5SDimitry Andric if (N->use_empty() || !N->isMachineOpcode()) 1990b57cec5SDimitry Andric continue; 2000b57cec5SDimitry Andric 2010b57cec5SDimitry Andric int OffsetOpIdx; 2020b57cec5SDimitry Andric int BaseOpIdx; 2030b57cec5SDimitry Andric 2040b57cec5SDimitry Andric // Only attempt this optimisation for I-type loads and S-type stores. 2050b57cec5SDimitry Andric switch (N->getMachineOpcode()) { 2060b57cec5SDimitry Andric default: 2070b57cec5SDimitry Andric continue; 2080b57cec5SDimitry Andric case RISCV::LB: 2090b57cec5SDimitry Andric case RISCV::LH: 2100b57cec5SDimitry Andric case RISCV::LW: 2110b57cec5SDimitry Andric case RISCV::LBU: 2120b57cec5SDimitry Andric case RISCV::LHU: 2130b57cec5SDimitry Andric case RISCV::LWU: 2140b57cec5SDimitry Andric case RISCV::LD: 2150b57cec5SDimitry Andric case RISCV::FLW: 2160b57cec5SDimitry Andric case RISCV::FLD: 2170b57cec5SDimitry Andric BaseOpIdx = 0; 2180b57cec5SDimitry Andric OffsetOpIdx = 1; 2190b57cec5SDimitry Andric break; 2200b57cec5SDimitry Andric case RISCV::SB: 2210b57cec5SDimitry Andric case RISCV::SH: 2220b57cec5SDimitry Andric case RISCV::SW: 2230b57cec5SDimitry Andric case RISCV::SD: 2240b57cec5SDimitry Andric case RISCV::FSW: 2250b57cec5SDimitry Andric case RISCV::FSD: 2260b57cec5SDimitry Andric BaseOpIdx = 1; 2270b57cec5SDimitry Andric OffsetOpIdx = 2; 2280b57cec5SDimitry Andric break; 2290b57cec5SDimitry Andric } 2300b57cec5SDimitry Andric 231*5ffd83dbSDimitry Andric if (!isa<ConstantSDNode>(N->getOperand(OffsetOpIdx))) 2320b57cec5SDimitry Andric continue; 2330b57cec5SDimitry Andric 2340b57cec5SDimitry Andric SDValue Base = N->getOperand(BaseOpIdx); 2350b57cec5SDimitry Andric 2360b57cec5SDimitry Andric // If the base is an ADDI, we can merge it in to the load/store. 2370b57cec5SDimitry Andric if (!Base.isMachineOpcode() || Base.getMachineOpcode() != RISCV::ADDI) 2380b57cec5SDimitry Andric continue; 2390b57cec5SDimitry Andric 2400b57cec5SDimitry Andric SDValue ImmOperand = Base.getOperand(1); 241*5ffd83dbSDimitry Andric uint64_t Offset2 = N->getConstantOperandVal(OffsetOpIdx); 2420b57cec5SDimitry Andric 2430b57cec5SDimitry Andric if (auto Const = dyn_cast<ConstantSDNode>(ImmOperand)) { 244*5ffd83dbSDimitry Andric int64_t Offset1 = Const->getSExtValue(); 245*5ffd83dbSDimitry Andric int64_t CombinedOffset = Offset1 + Offset2; 246*5ffd83dbSDimitry Andric if (!isInt<12>(CombinedOffset)) 247*5ffd83dbSDimitry Andric continue; 248*5ffd83dbSDimitry Andric ImmOperand = CurDAG->getTargetConstant(CombinedOffset, SDLoc(ImmOperand), 249*5ffd83dbSDimitry Andric ImmOperand.getValueType()); 2500b57cec5SDimitry Andric } else if (auto GA = dyn_cast<GlobalAddressSDNode>(ImmOperand)) { 251*5ffd83dbSDimitry Andric // If the off1 in (addi base, off1) is a global variable's address (its 252*5ffd83dbSDimitry Andric // low part, really), then we can rely on the alignment of that variable 253*5ffd83dbSDimitry Andric // to provide a margin of safety before off1 can overflow the 12 bits. 254*5ffd83dbSDimitry Andric // Check if off2 falls within that margin; if so off1+off2 can't overflow. 255*5ffd83dbSDimitry Andric const DataLayout &DL = CurDAG->getDataLayout(); 256*5ffd83dbSDimitry Andric Align Alignment = GA->getGlobal()->getPointerAlignment(DL); 257*5ffd83dbSDimitry Andric if (Offset2 != 0 && Alignment <= Offset2) 258*5ffd83dbSDimitry Andric continue; 259*5ffd83dbSDimitry Andric int64_t Offset1 = GA->getOffset(); 260*5ffd83dbSDimitry Andric int64_t CombinedOffset = Offset1 + Offset2; 2610b57cec5SDimitry Andric ImmOperand = CurDAG->getTargetGlobalAddress( 2620b57cec5SDimitry Andric GA->getGlobal(), SDLoc(ImmOperand), ImmOperand.getValueType(), 263*5ffd83dbSDimitry Andric CombinedOffset, GA->getTargetFlags()); 264*5ffd83dbSDimitry Andric } else if (auto CP = dyn_cast<ConstantPoolSDNode>(ImmOperand)) { 265*5ffd83dbSDimitry Andric // Ditto. 266*5ffd83dbSDimitry Andric Align Alignment = CP->getAlign(); 267*5ffd83dbSDimitry Andric if (Offset2 != 0 && Alignment <= Offset2) 268*5ffd83dbSDimitry Andric continue; 269*5ffd83dbSDimitry Andric int64_t Offset1 = CP->getOffset(); 270*5ffd83dbSDimitry Andric int64_t CombinedOffset = Offset1 + Offset2; 271*5ffd83dbSDimitry Andric ImmOperand = CurDAG->getTargetConstantPool( 272*5ffd83dbSDimitry Andric CP->getConstVal(), ImmOperand.getValueType(), CP->getAlign(), 273*5ffd83dbSDimitry Andric CombinedOffset, CP->getTargetFlags()); 2740b57cec5SDimitry Andric } else { 2750b57cec5SDimitry Andric continue; 2760b57cec5SDimitry Andric } 2770b57cec5SDimitry Andric 2780b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "Folding add-immediate into mem-op:\nBase: "); 2790b57cec5SDimitry Andric LLVM_DEBUG(Base->dump(CurDAG)); 2800b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "\nN: "); 2810b57cec5SDimitry Andric LLVM_DEBUG(N->dump(CurDAG)); 2820b57cec5SDimitry Andric LLVM_DEBUG(dbgs() << "\n"); 2830b57cec5SDimitry Andric 2840b57cec5SDimitry Andric // Modify the offset operand of the load/store. 2850b57cec5SDimitry Andric if (BaseOpIdx == 0) // Load 2860b57cec5SDimitry Andric CurDAG->UpdateNodeOperands(N, Base.getOperand(0), ImmOperand, 2870b57cec5SDimitry Andric N->getOperand(2)); 2880b57cec5SDimitry Andric else // Store 2890b57cec5SDimitry Andric CurDAG->UpdateNodeOperands(N, N->getOperand(0), Base.getOperand(0), 2900b57cec5SDimitry Andric ImmOperand, N->getOperand(3)); 2910b57cec5SDimitry Andric 2920b57cec5SDimitry Andric // The add-immediate may now be dead, in which case remove it. 2930b57cec5SDimitry Andric if (Base.getNode()->use_empty()) 2940b57cec5SDimitry Andric CurDAG->RemoveDeadNode(Base.getNode()); 2950b57cec5SDimitry Andric } 2960b57cec5SDimitry Andric } 2970b57cec5SDimitry Andric 2980b57cec5SDimitry Andric // This pass converts a legalized DAG into a RISCV-specific DAG, ready 2990b57cec5SDimitry Andric // for instruction scheduling. 3000b57cec5SDimitry Andric FunctionPass *llvm::createRISCVISelDag(RISCVTargetMachine &TM) { 3010b57cec5SDimitry Andric return new RISCVDAGToDAGISel(TM); 3020b57cec5SDimitry Andric } 303