LLVM 20.0.0git
CSKYISelDAGToDAG.cpp
Go to the documentation of this file.
1//===-- CSKYISelDAGToDAG.cpp - A dag to dag inst selector for CSKY---------===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8//
9// This file defines an instruction selector for the CSKY target.
10//
11//===----------------------------------------------------------------------===//
12
13#include "CSKY.h"
14#include "CSKYSubtarget.h"
15#include "CSKYTargetMachine.h"
20
21using namespace llvm;
22
23#define DEBUG_TYPE "csky-isel"
24#define PASS_NAME "CSKY DAG->DAG Pattern Instruction Selection"
25
26namespace {
27class CSKYDAGToDAGISel : public SelectionDAGISel {
28 const CSKYSubtarget *Subtarget;
29
30public:
31 explicit CSKYDAGToDAGISel(CSKYTargetMachine &TM, CodeGenOptLevel OptLevel)
32 : SelectionDAGISel(TM, OptLevel) {}
33
34 bool runOnMachineFunction(MachineFunction &MF) override {
35 // Reset the subtarget each time through.
36 Subtarget = &MF.getSubtarget<CSKYSubtarget>();
38 return true;
39 }
40
41 void Select(SDNode *N) override;
42 bool selectAddCarry(SDNode *N);
43 bool selectSubCarry(SDNode *N);
44 bool selectBITCAST_TO_LOHI(SDNode *N);
45 bool selectInlineAsm(SDNode *N);
46
48
50 InlineAsm::ConstraintCode ConstraintID,
51 std::vector<SDValue> &OutOps) override;
52
53#include "CSKYGenDAGISel.inc"
54};
55
56class CSKYDAGToDAGISelLegacy : public SelectionDAGISelLegacy {
57public:
58 static char ID;
59 explicit CSKYDAGToDAGISelLegacy(CSKYTargetMachine &TM,
60 CodeGenOptLevel OptLevel)
62 ID, std::make_unique<CSKYDAGToDAGISel>(TM, OptLevel)) {}
63};
64} // namespace
65
66char CSKYDAGToDAGISelLegacy::ID = 0;
67
68INITIALIZE_PASS(CSKYDAGToDAGISelLegacy, DEBUG_TYPE, PASS_NAME, false, false)
69
70void CSKYDAGToDAGISel::Select(SDNode *N) {
71 // If we have a custom node, we have already selected
72 if (N->isMachineOpcode()) {
73 LLVM_DEBUG(dbgs() << "== "; N->dump(CurDAG); dbgs() << "\n");
74 N->setNodeId(-1);
75 return;
76 }
77
78 SDLoc Dl(N);
79 unsigned Opcode = N->getOpcode();
80 bool IsSelected = false;
81
82 switch (Opcode) {
83 default:
84 break;
86 IsSelected = selectAddCarry(N);
87 break;
89 IsSelected = selectSubCarry(N);
90 break;
92 Register GP = Subtarget->getInstrInfo()->getGlobalBaseReg(*MF);
93 ReplaceNode(N, CurDAG->getRegister(GP, N->getValueType(0)).getNode());
94
95 IsSelected = true;
96 break;
97 }
98 case ISD::FrameIndex: {
99 SDValue Imm = CurDAG->getTargetConstant(0, Dl, MVT::i32);
100 int FI = cast<FrameIndexSDNode>(N)->getIndex();
101 SDValue TFI = CurDAG->getTargetFrameIndex(FI, MVT::i32);
102 ReplaceNode(N, CurDAG->getMachineNode(Subtarget->hasE2() ? CSKY::ADDI32
103 : CSKY::ADDI16XZ,
104 Dl, MVT::i32, TFI, Imm));
105
106 IsSelected = true;
107 break;
108 }
110 IsSelected = selectBITCAST_TO_LOHI(N);
111 break;
112 case ISD::INLINEASM:
114 IsSelected = selectInlineAsm(N);
115 break;
116 }
117
118 if (IsSelected)
119 return;
120
121 // Select the default instruction.
122 SelectCode(N);
123}
124
125bool CSKYDAGToDAGISel::selectInlineAsm(SDNode *N) {
126 std::vector<SDValue> AsmNodeOperands;
128 bool Changed = false;
129 unsigned NumOps = N->getNumOperands();
130
131 // Normally, i64 data is bounded to two arbitrary GRPs for "%r" constraint.
132 // However, some instructions (e.g. mula.s32) require GPR pair.
133 // Since there is no constraint to explicitly specify a
134 // reg pair, we use GPRPair reg class for "%r" for 64-bit data.
135
136 SDLoc dl(N);
137 SDValue Glue =
138 N->getGluedNode() ? N->getOperand(NumOps - 1) : SDValue(nullptr, 0);
139
140 SmallVector<bool, 8> OpChanged;
141 // Glue node will be appended late.
142 for (unsigned i = 0, e = N->getGluedNode() ? NumOps - 1 : NumOps; i < e;
143 ++i) {
144 SDValue op = N->getOperand(i);
145 AsmNodeOperands.push_back(op);
146
148 continue;
149
150 if (const auto *C = dyn_cast<ConstantSDNode>(N->getOperand(i)))
151 Flag = InlineAsm::Flag(C->getZExtValue());
152 else
153 continue;
154
155 // Immediate operands to inline asm in the SelectionDAG are modeled with
156 // two operands. The first is a constant of value InlineAsm::Kind::Imm, and
157 // the second is a constant with the value of the immediate. If we get here
158 // and we have a Kind::Imm, skip the next operand, and continue.
159 if (Flag.isImmKind()) {
160 SDValue op = N->getOperand(++i);
161 AsmNodeOperands.push_back(op);
162 continue;
163 }
164
165 const unsigned NumRegs = Flag.getNumOperandRegisters();
166 if (NumRegs)
167 OpChanged.push_back(false);
168
169 unsigned DefIdx = 0;
170 bool IsTiedToChangedOp = false;
171 // If it's a use that is tied with a previous def, it has no
172 // reg class constraint.
173 if (Changed && Flag.isUseOperandTiedToDef(DefIdx))
174 IsTiedToChangedOp = OpChanged[DefIdx];
175
176 // Memory operands to inline asm in the SelectionDAG are modeled with two
177 // operands: a constant of value InlineAsm::Kind::Mem followed by the input
178 // operand. If we get here and we have a Kind::Mem, skip the next operand
179 // (so it doesn't get misinterpreted), and continue. We do this here because
180 // it's important to update the OpChanged array correctly before moving on.
181 if (Flag.isMemKind()) {
182 SDValue op = N->getOperand(++i);
183 AsmNodeOperands.push_back(op);
184 continue;
185 }
186
187 if (!Flag.isRegUseKind() && !Flag.isRegDefKind() &&
188 !Flag.isRegDefEarlyClobberKind())
189 continue;
190
191 unsigned RC;
192 const bool HasRC = Flag.hasRegClassConstraint(RC);
193 if ((!IsTiedToChangedOp && (!HasRC || RC != CSKY::GPRRegClassID)) ||
194 NumRegs != 2)
195 continue;
196
197 assert((i + 2 < NumOps) && "Invalid number of operands in inline asm");
198 SDValue V0 = N->getOperand(i + 1);
199 SDValue V1 = N->getOperand(i + 2);
200 unsigned Reg0 = cast<RegisterSDNode>(V0)->getReg();
201 unsigned Reg1 = cast<RegisterSDNode>(V1)->getReg();
202 SDValue PairedReg;
203 MachineRegisterInfo &MRI = MF->getRegInfo();
204
205 if (Flag.isRegDefKind() || Flag.isRegDefEarlyClobberKind()) {
206 // Replace the two GPRs with 1 GPRPair and copy values from GPRPair to
207 // the original GPRs.
208
209 Register GPVR = MRI.createVirtualRegister(&CSKY::GPRPairRegClass);
210 PairedReg = CurDAG->getRegister(GPVR, MVT::i64);
211 SDValue Chain = SDValue(N, 0);
212
213 SDNode *GU = N->getGluedUser();
214 SDValue RegCopy =
215 CurDAG->getCopyFromReg(Chain, dl, GPVR, MVT::i64, Chain.getValue(1));
216
217 // Extract values from a GPRPair reg and copy to the original GPR reg.
218 SDValue Sub0 =
219 CurDAG->getTargetExtractSubreg(CSKY::sub32_0, dl, MVT::i32, RegCopy);
220 SDValue Sub1 =
221 CurDAG->getTargetExtractSubreg(CSKY::sub32_32, dl, MVT::i32, RegCopy);
222 SDValue T0 =
223 CurDAG->getCopyToReg(Sub0, dl, Reg0, Sub0, RegCopy.getValue(1));
224 SDValue T1 = CurDAG->getCopyToReg(Sub1, dl, Reg1, Sub1, T0.getValue(1));
225
226 // Update the original glue user.
227 std::vector<SDValue> Ops(GU->op_begin(), GU->op_end() - 1);
228 Ops.push_back(T1.getValue(1));
229 CurDAG->UpdateNodeOperands(GU, Ops);
230 } else {
231 // For Kind == InlineAsm::Kind::RegUse, we first copy two GPRs into a
232 // GPRPair and then pass the GPRPair to the inline asm.
233 SDValue Chain = AsmNodeOperands[InlineAsm::Op_InputChain];
234
235 // As REG_SEQ doesn't take RegisterSDNode, we copy them first.
236 SDValue T0 =
237 CurDAG->getCopyFromReg(Chain, dl, Reg0, MVT::i32, Chain.getValue(1));
238 SDValue T1 =
239 CurDAG->getCopyFromReg(Chain, dl, Reg1, MVT::i32, T0.getValue(1));
240 SDValue Pair = SDValue(createGPRPairNode(MVT::i64, T0, T1), 0);
241
242 // Copy REG_SEQ into a GPRPair-typed VR and replace the original two
243 // i32 VRs of inline asm with it.
244 Register GPVR = MRI.createVirtualRegister(&CSKY::GPRPairRegClass);
245 PairedReg = CurDAG->getRegister(GPVR, MVT::i64);
246 Chain = CurDAG->getCopyToReg(T1, dl, GPVR, Pair, T1.getValue(1));
247
248 AsmNodeOperands[InlineAsm::Op_InputChain] = Chain;
249 Glue = Chain.getValue(1);
250 }
251
252 Changed = true;
253
254 if (PairedReg.getNode()) {
255 OpChanged[OpChanged.size() - 1] = true;
256 // TODO: maybe a setter for getNumOperandRegisters?
257 Flag = InlineAsm::Flag(Flag.getKind(), 1 /* RegNum*/);
258 if (IsTiedToChangedOp)
259 Flag.setMatchingOp(DefIdx);
260 else
261 Flag.setRegClass(CSKY::GPRPairRegClassID);
262 // Replace the current flag.
263 AsmNodeOperands[AsmNodeOperands.size() - 1] =
264 CurDAG->getTargetConstant(Flag, dl, MVT::i32);
265 // Add the new register node and skip the original two GPRs.
266 AsmNodeOperands.push_back(PairedReg);
267 // Skip the next two GPRs.
268 i += 2;
269 }
270 }
271
272 if (Glue.getNode())
273 AsmNodeOperands.push_back(Glue);
274 if (!Changed)
275 return false;
276
277 SDValue New = CurDAG->getNode(N->getOpcode(), SDLoc(N),
278 CurDAG->getVTList(MVT::Other, MVT::Glue),
279 AsmNodeOperands);
280 New->setNodeId(-1);
281 ReplaceNode(N, New.getNode());
282 return true;
283}
284
285bool CSKYDAGToDAGISel::selectBITCAST_TO_LOHI(SDNode *N) {
286 SDLoc Dl(N);
287 auto VT = N->getValueType(0);
288 auto V = N->getOperand(0);
289
290 if (!Subtarget->hasFPUv2DoubleFloat())
291 return false;
292
293 SDValue V1 = SDValue(CurDAG->getMachineNode(CSKY::FMFVRL_D, Dl, VT, V), 0);
294 SDValue V2 = SDValue(CurDAG->getMachineNode(CSKY::FMFVRH_D, Dl, VT, V), 0);
295
296 ReplaceUses(SDValue(N, 0), V1);
297 ReplaceUses(SDValue(N, 1), V2);
298 CurDAG->RemoveDeadNode(N);
299
300 return true;
301}
302
303bool CSKYDAGToDAGISel::selectAddCarry(SDNode *N) {
304 MachineSDNode *NewNode = nullptr;
305 auto Type0 = N->getValueType(0);
306 auto Type1 = N->getValueType(1);
307 auto Op0 = N->getOperand(0);
308 auto Op1 = N->getOperand(1);
309 auto Op2 = N->getOperand(2);
310
311 SDLoc Dl(N);
312
313 if (isNullConstant(Op2)) {
314 auto *CA = CurDAG->getMachineNode(
315 Subtarget->has2E3() ? CSKY::CLRC32 : CSKY::CLRC16, Dl, Type1);
316 NewNode = CurDAG->getMachineNode(
317 Subtarget->has2E3() ? CSKY::ADDC32 : CSKY::ADDC16, Dl, {Type0, Type1},
318 {Op0, Op1, SDValue(CA, 0)});
319 } else if (isOneConstant(Op2)) {
320 auto *CA = CurDAG->getMachineNode(
321 Subtarget->has2E3() ? CSKY::SETC32 : CSKY::SETC16, Dl, Type1);
322 NewNode = CurDAG->getMachineNode(
323 Subtarget->has2E3() ? CSKY::ADDC32 : CSKY::ADDC16, Dl, {Type0, Type1},
324 {Op0, Op1, SDValue(CA, 0)});
325 } else {
326 NewNode = CurDAG->getMachineNode(Subtarget->has2E3() ? CSKY::ADDC32
327 : CSKY::ADDC16,
328 Dl, {Type0, Type1}, {Op0, Op1, Op2});
329 }
330 ReplaceNode(N, NewNode);
331 return true;
332}
333
334static SDValue InvertCarryFlag(const CSKYSubtarget *Subtarget,
335 SelectionDAG *DAG, SDLoc Dl, SDValue OldCarry) {
336 auto NewCarryReg =
337 DAG->getMachineNode(Subtarget->has2E3() ? CSKY::MVCV32 : CSKY::MVCV16, Dl,
338 MVT::i32, OldCarry);
339 auto NewCarry =
340 DAG->getMachineNode(Subtarget->hasE2() ? CSKY::BTSTI32 : CSKY::BTSTI16,
341 Dl, OldCarry.getValueType(), SDValue(NewCarryReg, 0),
342 DAG->getTargetConstant(0, Dl, MVT::i32));
343 return SDValue(NewCarry, 0);
344}
345
346bool CSKYDAGToDAGISel::selectSubCarry(SDNode *N) {
347 MachineSDNode *NewNode = nullptr;
348 auto Type0 = N->getValueType(0);
349 auto Type1 = N->getValueType(1);
350 auto Op0 = N->getOperand(0);
351 auto Op1 = N->getOperand(1);
352 auto Op2 = N->getOperand(2);
353
354 SDLoc Dl(N);
355
356 if (isNullConstant(Op2)) {
357 auto *CA = CurDAG->getMachineNode(
358 Subtarget->has2E3() ? CSKY::SETC32 : CSKY::SETC16, Dl, Type1);
359 NewNode = CurDAG->getMachineNode(
360 Subtarget->has2E3() ? CSKY::SUBC32 : CSKY::SUBC16, Dl, {Type0, Type1},
361 {Op0, Op1, SDValue(CA, 0)});
362 } else if (isOneConstant(Op2)) {
363 auto *CA = CurDAG->getMachineNode(
364 Subtarget->has2E3() ? CSKY::CLRC32 : CSKY::CLRC16, Dl, Type1);
365 NewNode = CurDAG->getMachineNode(
366 Subtarget->has2E3() ? CSKY::SUBC32 : CSKY::SUBC16, Dl, {Type0, Type1},
367 {Op0, Op1, SDValue(CA, 0)});
368 } else {
369 auto CarryIn = InvertCarryFlag(Subtarget, CurDAG, Dl, Op2);
370 NewNode = CurDAG->getMachineNode(Subtarget->has2E3() ? CSKY::SUBC32
371 : CSKY::SUBC16,
372 Dl, {Type0, Type1}, {Op0, Op1, CarryIn});
373 }
374 auto CarryOut = InvertCarryFlag(Subtarget, CurDAG, Dl, SDValue(NewNode, 1));
375
376 ReplaceUses(SDValue(N, 0), SDValue(NewNode, 0));
377 ReplaceUses(SDValue(N, 1), CarryOut);
378 CurDAG->RemoveDeadNode(N);
379
380 return true;
381}
382
383SDNode *CSKYDAGToDAGISel::createGPRPairNode(EVT VT, SDValue V0, SDValue V1) {
384 SDLoc dl(V0.getNode());
385 SDValue RegClass =
386 CurDAG->getTargetConstant(CSKY::GPRPairRegClassID, dl, MVT::i32);
387 SDValue SubReg0 = CurDAG->getTargetConstant(CSKY::sub32_0, dl, MVT::i32);
388 SDValue SubReg1 = CurDAG->getTargetConstant(CSKY::sub32_32, dl, MVT::i32);
389 const SDValue Ops[] = {RegClass, V0, SubReg0, V1, SubReg1};
390 return CurDAG->getMachineNode(TargetOpcode::REG_SEQUENCE, dl, VT, Ops);
391}
392
393bool CSKYDAGToDAGISel::SelectInlineAsmMemoryOperand(
394 const SDValue &Op, const InlineAsm::ConstraintCode ConstraintID,
395 std::vector<SDValue> &OutOps) {
396 switch (ConstraintID) {
397 case InlineAsm::ConstraintCode::m:
398 // We just support simple memory operands that have a single address
399 // operand and need no special handling.
400 OutOps.push_back(Op);
401 return false;
402 default:
403 break;
404 }
405
406 return true;
407}
408
410 CodeGenOptLevel OptLevel) {
411 return new CSKYDAGToDAGISelLegacy(TM, OptLevel);
412}
unsigned const MachineRegisterInfo * MRI
static SDValue createGPRPairNode(SelectionDAG &DAG, SDValue V)
amdgpu AMDGPU Register Bank Select
static SDValue InvertCarryFlag(const CSKYSubtarget *Subtarget, SelectionDAG *DAG, SDLoc Dl, SDValue OldCarry)
#define PASS_NAME
#define DEBUG_TYPE
#define LLVM_DEBUG(X)
Definition: Debug.h:101
#define op(i)
#define T1
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition: PassSupport.h:38
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
bool hasE2() const
bool has2E3() const
This class represents an Operation in the Expression.
FunctionPass class - This class is used to implement most global optimizations.
Definition: Pass.h:310
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
MachineRegisterInfo - Keep track of information for virtual and physical registers,...
An SDNode that represents everything that will be needed to construct a MachineInstr.
Wrapper class representing virtual and physical registers.
Definition: Register.h:19
Wrapper class for IR location info (IR ordering and DebugLoc) to be passed into SDNode creation funct...
Represents one node in the SelectionDAG.
op_iterator op_end() const
op_iterator op_begin() const
Unlike LLVM values, Selection DAG nodes may return multiple values as the result of a computation.
SDNode * getNode() const
get the SDNode which holds the desired result
SDValue getValue(unsigned R) const
EVT getValueType() const
Return the ValueType of the referenced return value.
SelectionDAGISel - This is the common base class used for SelectionDAG-based pattern-matching instruc...
virtual bool SelectInlineAsmMemoryOperand(const SDValue &Op, InlineAsm::ConstraintCode ConstraintID, std::vector< SDValue > &OutOps)
SelectInlineAsmMemoryOperand - Select the specified address as a target addressing mode,...
virtual bool runOnMachineFunction(MachineFunction &mf)
This is used to represent a portion of an LLVM function in a low-level Data Dependence DAG representa...
Definition: SelectionDAG.h:226
MachineSDNode * getMachineNode(unsigned Opcode, const SDLoc &dl, EVT VT)
These are used for target selectors to create a new node with specified return type(s),...
SDValue getTargetConstant(uint64_t Val, const SDLoc &DL, EVT VT, bool isOpaque=false)
Definition: SelectionDAG.h:690
size_t size() const
Definition: SmallVector.h:91
void push_back(const T &Elt)
Definition: SmallVector.h:426
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1209
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition: CallingConv.h:24
@ C
The default llvm calling convention, compatible with C.
Definition: CallingConv.h:34
@ FrameIndex
Definition: ISDOpcodes.h:80
@ GLOBAL_OFFSET_TABLE
The address of the GOT.
Definition: ISDOpcodes.h:93
@ UADDO_CARRY
Carry-using nodes for multiple precision addition and subtraction.
Definition: ISDOpcodes.h:310
@ INLINEASM_BR
INLINEASM_BR - Branching version of inline asm. Used by asm-goto.
Definition: ISDOpcodes.h:1165
@ INLINEASM
INLINEASM - Represents an inline asm block.
Definition: ISDOpcodes.h:1162
Flag
These should be considered private to the implementation of the MCInstrDesc class.
Definition: MCInstrDesc.h:148
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
bool isNullConstant(SDValue V)
Returns true if V is a constant integer zero.
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:163
CodeGenOptLevel
Code generation optimization level.
Definition: CodeGen.h:54
bool isOneConstant(SDValue V)
Returns true if V is a constant integer one.
FunctionPass * createCSKYISelDag(CSKYTargetMachine &TM, CodeGenOptLevel OptLevel)
#define N
Extended Value Type.
Definition: ValueTypes.h:35