xref: /freebsd/contrib/llvm-project/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp (revision 5ffd83dbcc34f10e07f6d3e968ae6365869615f4)
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