1 //===-- RISCVISelDAGToDAG.cpp - A dag to dag inst selector for RISCV ------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file defines an instruction selector for the RISCV target.
11 //===----------------------------------------------------------------------===//
13 #include "MCTargetDesc/RISCVMCTargetDesc.h"
15 #include "RISCVTargetMachine.h"
16 #include "Utils/RISCVMatInt.h"
17 #include "llvm/CodeGen/MachineFrameInfo.h"
18 #include "llvm/CodeGen/SelectionDAGISel.h"
19 #include "llvm/Support/Debug.h"
20 #include "llvm/Support/MathExtras.h"
21 #include "llvm/Support/raw_ostream.h"
24 #define DEBUG_TYPE "riscv-isel"
26 // RISCV-specific code to select RISCV machine instructions for
27 // SelectionDAG operations.
29 class RISCVDAGToDAGISel final
: public SelectionDAGISel
{
30 const RISCVSubtarget
*Subtarget
;
33 explicit RISCVDAGToDAGISel(RISCVTargetMachine
&TargetMachine
)
34 : SelectionDAGISel(TargetMachine
) {}
36 StringRef
getPassName() const override
{
37 return "RISCV DAG->DAG Pattern Instruction Selection";
40 bool runOnMachineFunction(MachineFunction
&MF
) override
{
41 Subtarget
= &MF
.getSubtarget
<RISCVSubtarget
>();
42 return SelectionDAGISel::runOnMachineFunction(MF
);
45 void PostprocessISelDAG() override
;
47 void Select(SDNode
*Node
) override
;
49 bool SelectInlineAsmMemoryOperand(const SDValue
&Op
, unsigned ConstraintID
,
50 std::vector
<SDValue
> &OutOps
) override
;
52 bool SelectAddrFI(SDValue Addr
, SDValue
&Base
);
54 // Include the pieces autogenerated from the target description.
55 #include "RISCVGenDAGISel.inc"
58 void doPeepholeLoadStoreADDI();
62 void RISCVDAGToDAGISel::PostprocessISelDAG() {
63 doPeepholeLoadStoreADDI();
66 static SDNode
*selectImm(SelectionDAG
*CurDAG
, const SDLoc
&DL
, int64_t Imm
,
68 RISCVMatInt::InstSeq Seq
;
69 RISCVMatInt::generateInstSeq(Imm
, XLenVT
== MVT::i64
, Seq
);
71 SDNode
*Result
= nullptr;
72 SDValue SrcReg
= CurDAG
->getRegister(RISCV::X0
, XLenVT
);
73 for (RISCVMatInt::Inst
&Inst
: Seq
) {
74 SDValue SDImm
= CurDAG
->getTargetConstant(Inst
.Imm
, DL
, XLenVT
);
75 if (Inst
.Opc
== RISCV::LUI
)
76 Result
= CurDAG
->getMachineNode(RISCV::LUI
, DL
, XLenVT
, SDImm
);
78 Result
= CurDAG
->getMachineNode(Inst
.Opc
, DL
, XLenVT
, SrcReg
, SDImm
);
80 // Only the first instruction has X0 as its source.
81 SrcReg
= SDValue(Result
, 0);
87 // Returns true if the Node is an ISD::AND with a constant argument. If so,
88 // set Mask to that constant value.
89 static bool isConstantMask(SDNode
*Node
, uint64_t &Mask
) {
90 if (Node
->getOpcode() == ISD::AND
&&
91 Node
->getOperand(1).getOpcode() == ISD::Constant
) {
92 Mask
= cast
<ConstantSDNode
>(Node
->getOperand(1))->getZExtValue();
98 void RISCVDAGToDAGISel::Select(SDNode
*Node
) {
99 // If we have a custom node, we have already selected.
100 if (Node
->isMachineOpcode()) {
101 LLVM_DEBUG(dbgs() << "== "; Node
->dump(CurDAG
); dbgs() << "\n");
106 // Instruction Selection not handled by the auto-generated tablegen selection
107 // should be handled here.
108 unsigned Opcode
= Node
->getOpcode();
109 MVT XLenVT
= Subtarget
->getXLenVT();
111 EVT VT
= Node
->getValueType(0);
114 case ISD::Constant
: {
115 auto ConstNode
= cast
<ConstantSDNode
>(Node
);
116 if (VT
== XLenVT
&& ConstNode
->isNullValue()) {
117 SDValue New
= CurDAG
->getCopyFromReg(CurDAG
->getEntryNode(), SDLoc(Node
),
119 ReplaceNode(Node
, New
.getNode());
122 int64_t Imm
= ConstNode
->getSExtValue();
123 if (XLenVT
== MVT::i64
) {
124 ReplaceNode(Node
, selectImm(CurDAG
, SDLoc(Node
), Imm
, XLenVT
));
129 case ISD::FrameIndex
: {
130 SDValue Imm
= CurDAG
->getTargetConstant(0, DL
, XLenVT
);
131 int FI
= cast
<FrameIndexSDNode
>(Node
)->getIndex();
132 SDValue TFI
= CurDAG
->getTargetFrameIndex(FI
, VT
);
133 ReplaceNode(Node
, CurDAG
->getMachineNode(RISCV::ADDI
, DL
, VT
, TFI
, Imm
));
137 if (!Subtarget
->is64Bit())
139 SDValue Op0
= Node
->getOperand(0);
140 SDValue Op1
= Node
->getOperand(1);
142 // Match (srl (and val, mask), imm) where the result would be a
143 // zero-extended 32-bit integer. i.e. the mask is 0xffffffff or the result
144 // is equivalent to this (SimplifyDemandedBits may have removed lower bits
145 // from the mask that aren't necessary due to the right-shifting).
146 if (Op1
.getOpcode() == ISD::Constant
&&
147 isConstantMask(Op0
.getNode(), Mask
)) {
148 uint64_t ShAmt
= cast
<ConstantSDNode
>(Op1
.getNode())->getZExtValue();
150 if ((Mask
| maskTrailingOnes
<uint64_t>(ShAmt
)) == 0xffffffff) {
152 CurDAG
->getTargetConstant(ShAmt
, SDLoc(Node
), XLenVT
);
153 CurDAG
->SelectNodeTo(Node
, RISCV::SRLIW
, XLenVT
, Op0
.getOperand(0),
160 case RISCVISD::READ_CYCLE_WIDE
:
161 assert(!Subtarget
->is64Bit() && "READ_CYCLE_WIDE is only used on riscv32");
163 ReplaceNode(Node
, CurDAG
->getMachineNode(RISCV::ReadCycleWide
, DL
, MVT::i32
,
164 MVT::i32
, MVT::Other
,
165 Node
->getOperand(0)));
169 // Select the default instruction.
173 bool RISCVDAGToDAGISel::SelectInlineAsmMemoryOperand(
174 const SDValue
&Op
, unsigned ConstraintID
, std::vector
<SDValue
> &OutOps
) {
175 switch (ConstraintID
) {
176 case InlineAsm::Constraint_i
:
177 case InlineAsm::Constraint_m
:
178 // We just support simple memory operands that have a single address
179 // operand and need no special handling.
180 OutOps
.push_back(Op
);
182 case InlineAsm::Constraint_A
:
183 OutOps
.push_back(Op
);
192 bool RISCVDAGToDAGISel::SelectAddrFI(SDValue Addr
, SDValue
&Base
) {
193 if (auto FIN
= dyn_cast
<FrameIndexSDNode
>(Addr
)) {
194 Base
= CurDAG
->getTargetFrameIndex(FIN
->getIndex(), Subtarget
->getXLenVT());
200 // Merge an ADDI into the offset of a load/store instruction where possible.
201 // (load (add base, off), 0) -> (load base, off)
202 // (store val, (add base, off)) -> (store val, base, off)
203 void RISCVDAGToDAGISel::doPeepholeLoadStoreADDI() {
204 SelectionDAG::allnodes_iterator
Position(CurDAG
->getRoot().getNode());
207 while (Position
!= CurDAG
->allnodes_begin()) {
208 SDNode
*N
= &*--Position
;
209 // Skip dead nodes and any non-machine opcodes.
210 if (N
->use_empty() || !N
->isMachineOpcode())
216 // Only attempt this optimisation for I-type loads and S-type stores.
217 switch (N
->getMachineOpcode()) {
243 // Currently, the load/store offset must be 0 to be considered for this
244 // peephole optimisation.
245 if (!isa
<ConstantSDNode
>(N
->getOperand(OffsetOpIdx
)) ||
246 N
->getConstantOperandVal(OffsetOpIdx
) != 0)
249 SDValue Base
= N
->getOperand(BaseOpIdx
);
251 // If the base is an ADDI, we can merge it in to the load/store.
252 if (!Base
.isMachineOpcode() || Base
.getMachineOpcode() != RISCV::ADDI
)
255 SDValue ImmOperand
= Base
.getOperand(1);
257 if (auto Const
= dyn_cast
<ConstantSDNode
>(ImmOperand
)) {
258 ImmOperand
= CurDAG
->getTargetConstant(
259 Const
->getSExtValue(), SDLoc(ImmOperand
), ImmOperand
.getValueType());
260 } else if (auto GA
= dyn_cast
<GlobalAddressSDNode
>(ImmOperand
)) {
261 ImmOperand
= CurDAG
->getTargetGlobalAddress(
262 GA
->getGlobal(), SDLoc(ImmOperand
), ImmOperand
.getValueType(),
263 GA
->getOffset(), GA
->getTargetFlags());
268 LLVM_DEBUG(dbgs() << "Folding add-immediate into mem-op:\nBase: ");
269 LLVM_DEBUG(Base
->dump(CurDAG
));
270 LLVM_DEBUG(dbgs() << "\nN: ");
271 LLVM_DEBUG(N
->dump(CurDAG
));
272 LLVM_DEBUG(dbgs() << "\n");
274 // Modify the offset operand of the load/store.
275 if (BaseOpIdx
== 0) // Load
276 CurDAG
->UpdateNodeOperands(N
, Base
.getOperand(0), ImmOperand
,
279 CurDAG
->UpdateNodeOperands(N
, N
->getOperand(0), Base
.getOperand(0),
280 ImmOperand
, N
->getOperand(3));
282 // The add-immediate may now be dead, in which case remove it.
283 if (Base
.getNode()->use_empty())
284 CurDAG
->RemoveDeadNode(Base
.getNode());
288 // This pass converts a legalized DAG into a RISCV-specific DAG, ready
289 // for instruction scheduling.
290 FunctionPass
*llvm::createRISCVISelDag(RISCVTargetMachine
&TM
) {
291 return new RISCVDAGToDAGISel(TM
);