1 //===-- SparcISelDAGToDAG.cpp - A dag to dag inst selector for Sparc ------===//
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 SPARC target.
11 //===----------------------------------------------------------------------===//
13 #include "SparcTargetMachine.h"
14 #include "llvm/CodeGen/MachineRegisterInfo.h"
15 #include "llvm/CodeGen/SelectionDAGISel.h"
16 #include "llvm/Support/ErrorHandling.h"
19 #define DEBUG_TYPE "sparc-isel"
20 #define PASS_NAME "SPARC DAG->DAG Pattern Instruction Selection"
22 //===----------------------------------------------------------------------===//
23 // Instruction Selector Implementation
24 //===----------------------------------------------------------------------===//
26 //===--------------------------------------------------------------------===//
27 /// SparcDAGToDAGISel - SPARC specific code to select SPARC machine
28 /// instructions for SelectionDAG operations.
31 class SparcDAGToDAGISel
: public SelectionDAGISel
{
32 /// Subtarget - Keep a pointer to the Sparc Subtarget around so that we can
33 /// make the right decision when generating code for different targets.
34 const SparcSubtarget
*Subtarget
= nullptr;
37 SparcDAGToDAGISel() = delete;
39 explicit SparcDAGToDAGISel(SparcTargetMachine
&tm
) : SelectionDAGISel(tm
) {}
41 bool runOnMachineFunction(MachineFunction
&MF
) override
{
42 Subtarget
= &MF
.getSubtarget
<SparcSubtarget
>();
43 return SelectionDAGISel::runOnMachineFunction(MF
);
46 void Select(SDNode
*N
) override
;
48 // Complex Pattern Selectors.
49 bool SelectADDRrr(SDValue N
, SDValue
&R1
, SDValue
&R2
);
50 bool SelectADDRri(SDValue N
, SDValue
&Base
, SDValue
&Offset
);
52 /// SelectInlineAsmMemoryOperand - Implement addressing mode selection for
53 /// inline asm expressions.
54 bool SelectInlineAsmMemoryOperand(const SDValue
&Op
,
55 InlineAsm::ConstraintCode ConstraintID
,
56 std::vector
<SDValue
> &OutOps
) override
;
58 // Include the pieces autogenerated from the target description.
59 #include "SparcGenDAGISel.inc"
62 SDNode
* getGlobalBaseReg();
63 bool tryInlineAsm(SDNode
*N
);
66 class SparcDAGToDAGISelLegacy
: public SelectionDAGISelLegacy
{
69 explicit SparcDAGToDAGISelLegacy(SparcTargetMachine
&tm
)
70 : SelectionDAGISelLegacy(ID
, std::make_unique
<SparcDAGToDAGISel
>(tm
)) {}
72 } // end anonymous namespace
74 char SparcDAGToDAGISelLegacy::ID
= 0;
76 INITIALIZE_PASS(SparcDAGToDAGISelLegacy
, DEBUG_TYPE
, PASS_NAME
, false, false)
78 SDNode
* SparcDAGToDAGISel::getGlobalBaseReg() {
79 Register GlobalBaseReg
= Subtarget
->getInstrInfo()->getGlobalBaseReg(MF
);
80 return CurDAG
->getRegister(GlobalBaseReg
,
81 TLI
->getPointerTy(CurDAG
->getDataLayout()))
85 bool SparcDAGToDAGISel::SelectADDRri(SDValue Addr
,
86 SDValue
&Base
, SDValue
&Offset
) {
87 if (FrameIndexSDNode
*FIN
= dyn_cast
<FrameIndexSDNode
>(Addr
)) {
88 Base
= CurDAG
->getTargetFrameIndex(
89 FIN
->getIndex(), TLI
->getPointerTy(CurDAG
->getDataLayout()));
90 Offset
= CurDAG
->getTargetConstant(0, SDLoc(Addr
), MVT::i32
);
93 if (Addr
.getOpcode() == ISD::TargetExternalSymbol
||
94 Addr
.getOpcode() == ISD::TargetGlobalAddress
||
95 Addr
.getOpcode() == ISD::TargetGlobalTLSAddress
)
96 return false; // direct calls.
98 if (Addr
.getOpcode() == ISD::ADD
) {
99 if (ConstantSDNode
*CN
= dyn_cast
<ConstantSDNode
>(Addr
.getOperand(1))) {
100 if (isInt
<13>(CN
->getSExtValue())) {
101 if (FrameIndexSDNode
*FIN
=
102 dyn_cast
<FrameIndexSDNode
>(Addr
.getOperand(0))) {
103 // Constant offset from frame ref.
104 Base
= CurDAG
->getTargetFrameIndex(
105 FIN
->getIndex(), TLI
->getPointerTy(CurDAG
->getDataLayout()));
107 Base
= Addr
.getOperand(0);
109 Offset
= CurDAG
->getSignedTargetConstant(CN
->getSExtValue(),
110 SDLoc(Addr
), MVT::i32
);
114 if (Addr
.getOperand(0).getOpcode() == SPISD::Lo
) {
115 Base
= Addr
.getOperand(1);
116 Offset
= Addr
.getOperand(0).getOperand(0);
119 if (Addr
.getOperand(1).getOpcode() == SPISD::Lo
) {
120 Base
= Addr
.getOperand(0);
121 Offset
= Addr
.getOperand(1).getOperand(0);
126 Offset
= CurDAG
->getTargetConstant(0, SDLoc(Addr
), MVT::i32
);
130 bool SparcDAGToDAGISel::SelectADDRrr(SDValue Addr
, SDValue
&R1
, SDValue
&R2
) {
131 if (Addr
.getOpcode() == ISD::FrameIndex
) return false;
132 if (Addr
.getOpcode() == ISD::TargetExternalSymbol
||
133 Addr
.getOpcode() == ISD::TargetGlobalAddress
||
134 Addr
.getOpcode() == ISD::TargetGlobalTLSAddress
)
135 return false; // direct calls.
137 if (Addr
.getOpcode() == ISD::ADD
) {
138 if (ConstantSDNode
*CN
= dyn_cast
<ConstantSDNode
>(Addr
.getOperand(1)))
139 if (isInt
<13>(CN
->getSExtValue()))
140 return false; // Let the reg+imm pattern catch this!
141 if (Addr
.getOperand(0).getOpcode() == SPISD::Lo
||
142 Addr
.getOperand(1).getOpcode() == SPISD::Lo
)
143 return false; // Let the reg+imm pattern catch this!
144 R1
= Addr
.getOperand(0);
145 R2
= Addr
.getOperand(1);
150 R2
= CurDAG
->getRegister(SP::G0
, TLI
->getPointerTy(CurDAG
->getDataLayout()));
155 // Re-assemble i64 arguments split up in SelectionDAGBuilder's
156 // visitInlineAsm / GetRegistersForValue functions.
158 // Note: This function was copied from, and is essentially identical
159 // to ARMISelDAGToDAG::SelectInlineAsm. It is very unfortunate that
160 // such hacking-up is necessary; a rethink of how inline asm operands
161 // are handled may be in order to make doing this more sane.
163 // TODO: fix inline asm support so I can simply tell it that 'i64'
164 // inputs to asm need to be allocated to the IntPair register type,
165 // and have that work. Then, delete this function.
166 bool SparcDAGToDAGISel::tryInlineAsm(SDNode
*N
){
167 std::vector
<SDValue
> AsmNodeOperands
;
168 InlineAsm::Flag Flag
;
169 bool Changed
= false;
170 unsigned NumOps
= N
->getNumOperands();
172 // Normally, i64 data is bounded to two arbitrary GPRs for "%r"
173 // constraint. However, some instructions (e.g. ldd/std) require
174 // (even/even+1) GPRs.
176 // So, here, we check for this case, and mutate the inlineasm to use
177 // a single IntPair register instead, which guarantees such even/odd
181 SDValue Glue
= N
->getGluedNode() ? N
->getOperand(NumOps
- 1) : SDValue();
183 SmallVector
<bool, 8> OpChanged
;
184 // Glue node will be appended late.
185 for(unsigned i
= 0, e
= N
->getGluedNode() ? NumOps
- 1 : NumOps
; i
< e
; ++i
) {
186 SDValue op
= N
->getOperand(i
);
187 AsmNodeOperands
.push_back(op
);
189 if (i
< InlineAsm::Op_FirstOperand
)
192 if (const auto *C
= dyn_cast
<ConstantSDNode
>(N
->getOperand(i
)))
193 Flag
= InlineAsm::Flag(C
->getZExtValue());
197 // Immediate operands to inline asm in the SelectionDAG are modeled with
198 // two operands. The first is a constant of value InlineAsm::Kind::Imm, and
199 // the second is a constant with the value of the immediate. If we get here
200 // and we have a Kind::Imm, skip the next operand, and continue.
201 if (Flag
.isImmKind()) {
202 SDValue op
= N
->getOperand(++i
);
203 AsmNodeOperands
.push_back(op
);
207 const unsigned NumRegs
= Flag
.getNumOperandRegisters();
209 OpChanged
.push_back(false);
212 bool IsTiedToChangedOp
= false;
213 // If it's a use that is tied with a previous def, it has no
214 // reg class constraint.
215 if (Changed
&& Flag
.isUseOperandTiedToDef(DefIdx
))
216 IsTiedToChangedOp
= OpChanged
[DefIdx
];
218 if (!Flag
.isRegUseKind() && !Flag
.isRegDefKind() &&
219 !Flag
.isRegDefEarlyClobberKind())
223 const bool HasRC
= Flag
.hasRegClassConstraint(RC
);
224 if ((!IsTiedToChangedOp
&& (!HasRC
|| RC
!= SP::IntRegsRegClassID
))
228 assert((i
+2 < NumOps
) && "Invalid number of operands in inline asm");
229 SDValue V0
= N
->getOperand(i
+1);
230 SDValue V1
= N
->getOperand(i
+2);
231 Register Reg0
= cast
<RegisterSDNode
>(V0
)->getReg();
232 Register Reg1
= cast
<RegisterSDNode
>(V1
)->getReg();
234 MachineRegisterInfo
&MRI
= MF
->getRegInfo();
236 if (Flag
.isRegDefKind() || Flag
.isRegDefEarlyClobberKind()) {
237 // Replace the two GPRs with 1 GPRPair and copy values from GPRPair to
238 // the original GPRs.
240 Register GPVR
= MRI
.createVirtualRegister(&SP::IntPairRegClass
);
241 PairedReg
= CurDAG
->getRegister(GPVR
, MVT::v2i32
);
242 SDValue Chain
= SDValue(N
,0);
244 SDNode
*GU
= N
->getGluedUser();
245 SDValue RegCopy
= CurDAG
->getCopyFromReg(Chain
, dl
, GPVR
, MVT::v2i32
,
248 // Extract values from a GPRPair reg and copy to the original GPR reg.
249 SDValue Sub0
= CurDAG
->getTargetExtractSubreg(SP::sub_even
, dl
, MVT::i32
,
251 SDValue Sub1
= CurDAG
->getTargetExtractSubreg(SP::sub_odd
, dl
, MVT::i32
,
253 SDValue T0
= CurDAG
->getCopyToReg(Sub0
, dl
, Reg0
, Sub0
,
254 RegCopy
.getValue(1));
255 SDValue T1
= CurDAG
->getCopyToReg(Sub1
, dl
, Reg1
, Sub1
, T0
.getValue(1));
257 // Update the original glue user.
258 std::vector
<SDValue
> Ops(GU
->op_begin(), GU
->op_end()-1);
259 Ops
.push_back(T1
.getValue(1));
260 CurDAG
->UpdateNodeOperands(GU
, Ops
);
262 // For Kind == InlineAsm::Kind::RegUse, we first copy two GPRs into a
263 // GPRPair and then pass the GPRPair to the inline asm.
264 SDValue Chain
= AsmNodeOperands
[InlineAsm::Op_InputChain
];
266 // As REG_SEQ doesn't take RegisterSDNode, we copy them first.
267 SDValue T0
= CurDAG
->getCopyFromReg(Chain
, dl
, Reg0
, MVT::i32
,
269 SDValue T1
= CurDAG
->getCopyFromReg(Chain
, dl
, Reg1
, MVT::i32
,
271 SDValue Pair
= SDValue(
272 CurDAG
->getMachineNode(
273 TargetOpcode::REG_SEQUENCE
, dl
, MVT::v2i32
,
275 CurDAG
->getTargetConstant(SP::IntPairRegClassID
, dl
,
278 CurDAG
->getTargetConstant(SP::sub_even
, dl
, MVT::i32
),
280 CurDAG
->getTargetConstant(SP::sub_odd
, dl
, MVT::i32
),
284 // Copy REG_SEQ into a GPRPair-typed VR and replace the original two
285 // i32 VRs of inline asm with it.
286 Register GPVR
= MRI
.createVirtualRegister(&SP::IntPairRegClass
);
287 PairedReg
= CurDAG
->getRegister(GPVR
, MVT::v2i32
);
288 Chain
= CurDAG
->getCopyToReg(T1
, dl
, GPVR
, Pair
, T1
.getValue(1));
290 AsmNodeOperands
[InlineAsm::Op_InputChain
] = Chain
;
291 Glue
= Chain
.getValue(1);
296 if(PairedReg
.getNode()) {
297 OpChanged
[OpChanged
.size() -1 ] = true;
298 Flag
= InlineAsm::Flag(Flag
.getKind(), 1 /* RegNum*/);
299 if (IsTiedToChangedOp
)
300 Flag
.setMatchingOp(DefIdx
);
302 Flag
.setRegClass(SP::IntPairRegClassID
);
303 // Replace the current flag.
304 AsmNodeOperands
[AsmNodeOperands
.size() -1] = CurDAG
->getTargetConstant(
306 // Add the new register node and skip the original two GPRs.
307 AsmNodeOperands
.push_back(PairedReg
);
308 // Skip the next two GPRs.
314 AsmNodeOperands
.push_back(Glue
);
318 SelectInlineAsmMemoryOperands(AsmNodeOperands
, SDLoc(N
));
320 SDValue New
= CurDAG
->getNode(N
->getOpcode(), SDLoc(N
),
321 CurDAG
->getVTList(MVT::Other
, MVT::Glue
), AsmNodeOperands
);
323 ReplaceNode(N
, New
.getNode());
327 void SparcDAGToDAGISel::Select(SDNode
*N
) {
329 if (N
->isMachineOpcode()) {
331 return; // Already selected.
334 switch (N
->getOpcode()) {
337 case ISD::INLINEASM_BR
: {
342 case SPISD::GLOBAL_BASE_REG
:
343 ReplaceNode(N
, getGlobalBaseReg());
348 // sdivx / udivx handle 64-bit divides.
349 if (N
->getValueType(0) == MVT::i64
)
351 // FIXME: should use a custom expander to expose the SRA to the dag.
352 SDValue DivLHS
= N
->getOperand(0);
353 SDValue DivRHS
= N
->getOperand(1);
355 // Set the Y register to the high-part.
357 if (N
->getOpcode() == ISD::SDIV
) {
358 TopPart
= SDValue(CurDAG
->getMachineNode(SP::SRAri
, dl
, MVT::i32
, DivLHS
,
359 CurDAG
->getTargetConstant(31, dl
, MVT::i32
)),
362 TopPart
= CurDAG
->getRegister(SP::G0
, MVT::i32
);
364 TopPart
= CurDAG
->getCopyToReg(CurDAG
->getEntryNode(), dl
, SP::Y
, TopPart
,
368 // FIXME: Handle div by immediate.
369 unsigned Opcode
= N
->getOpcode() == ISD::SDIV
? SP::SDIVrr
: SP::UDIVrr
;
370 CurDAG
->SelectNodeTo(N
, Opcode
, MVT::i32
, DivLHS
, DivRHS
, TopPart
);
379 /// SelectInlineAsmMemoryOperand - Implement addressing mode selection for
380 /// inline asm expressions.
381 bool SparcDAGToDAGISel::SelectInlineAsmMemoryOperand(
382 const SDValue
&Op
, InlineAsm::ConstraintCode ConstraintID
,
383 std::vector
<SDValue
> &OutOps
) {
385 switch (ConstraintID
) {
386 default: return true;
387 case InlineAsm::ConstraintCode::o
:
388 case InlineAsm::ConstraintCode::m
: // memory
389 if (!SelectADDRrr(Op
, Op0
, Op1
))
390 SelectADDRri(Op
, Op0
, Op1
);
394 OutOps
.push_back(Op0
);
395 OutOps
.push_back(Op1
);
399 /// createSparcISelDag - This pass converts a legalized DAG into a
400 /// SPARC-specific DAG, ready for instruction scheduling.
402 FunctionPass
*llvm::createSparcISelDag(SparcTargetMachine
&TM
) {
403 return new SparcDAGToDAGISelLegacy(TM
);