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