LLVM 19.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 static char ID;
32
33 explicit CSKYDAGToDAGISel(CSKYTargetMachine &TM, CodeGenOptLevel OptLevel)
34 : SelectionDAGISel(ID, TM, OptLevel) {}
35
36 bool runOnMachineFunction(MachineFunction &MF) override {
37 // Reset the subtarget each time through.
38 Subtarget = &MF.getSubtarget<CSKYSubtarget>();
40 return true;
41 }
42
43 void Select(SDNode *N) override;
44 bool selectAddCarry(SDNode *N);
45 bool selectSubCarry(SDNode *N);
46 bool selectBITCAST_TO_LOHI(SDNode *N);
47 bool selectInlineAsm(SDNode *N);
48
50
52 InlineAsm::ConstraintCode ConstraintID,
53 std::vector<SDValue> &OutOps) override;
54
55#include "CSKYGenDAGISel.inc"
56};
57} // namespace
58
59char CSKYDAGToDAGISel::ID = 0;
60
61INITIALIZE_PASS(CSKYDAGToDAGISel, DEBUG_TYPE, PASS_NAME, false, false)
62
63void CSKYDAGToDAGISel::Select(SDNode *N) {
64 // If we have a custom node, we have already selected
65 if (N->isMachineOpcode()) {
66 LLVM_DEBUG(dbgs() << "== "; N->dump(CurDAG); dbgs() << "\n");
67 N->setNodeId(-1);
68 return;
69 }
70
71 SDLoc Dl(N);
72 unsigned Opcode = N->getOpcode();
73 bool IsSelected = false;
74
75 switch (Opcode) {
76 default:
77 break;
79 IsSelected = selectAddCarry(N);
80 break;
82 IsSelected = selectSubCarry(N);
83 break;
85 Register GP = Subtarget->getInstrInfo()->getGlobalBaseReg(*MF);
86 ReplaceNode(N, CurDAG->getRegister(GP, N->getValueType(0)).getNode());
87
88 IsSelected = true;
89 break;
90 }
91 case ISD::FrameIndex: {
92 SDValue Imm = CurDAG->getTargetConstant(0, Dl, MVT::i32);
93 int FI = cast<FrameIndexSDNode>(N)->getIndex();
94 SDValue TFI = CurDAG->getTargetFrameIndex(FI, MVT::i32);
95 ReplaceNode(N, CurDAG->getMachineNode(Subtarget->hasE2() ? CSKY::ADDI32
96 : CSKY::ADDI16XZ,
97 Dl, MVT::i32, TFI, Imm));
98
99 IsSelected = true;
100 break;
101 }
103 IsSelected = selectBITCAST_TO_LOHI(N);
104 break;
105 case ISD::INLINEASM:
107 IsSelected = selectInlineAsm(N);
108 break;
109 }
110
111 if (IsSelected)
112 return;
113
114 // Select the default instruction.
115 SelectCode(N);
116}
117
118bool CSKYDAGToDAGISel::selectInlineAsm(SDNode *N) {
119 std::vector<SDValue> AsmNodeOperands;
121 bool Changed = false;
122 unsigned NumOps = N->getNumOperands();
123
124 // Normally, i64 data is bounded to two arbitrary GRPs for "%r" constraint.
125 // However, some instructions (e.g. mula.s32) require GPR pair.
126 // Since there is no constraint to explicitly specify a
127 // reg pair, we use GPRPair reg class for "%r" for 64-bit data.
128
129 SDLoc dl(N);
130 SDValue Glue =
131 N->getGluedNode() ? N->getOperand(NumOps - 1) : SDValue(nullptr, 0);
132
133 SmallVector<bool, 8> OpChanged;
134 // Glue node will be appended late.
135 for (unsigned i = 0, e = N->getGluedNode() ? NumOps - 1 : NumOps; i < e;
136 ++i) {
137 SDValue op = N->getOperand(i);
138 AsmNodeOperands.push_back(op);
139
141 continue;
142
143 if (const auto *C = dyn_cast<ConstantSDNode>(N->getOperand(i)))
144 Flag = InlineAsm::Flag(C->getZExtValue());
145 else
146 continue;
147
148 // Immediate operands to inline asm in the SelectionDAG are modeled with
149 // two operands. The first is a constant of value InlineAsm::Kind::Imm, and
150 // the second is a constant with the value of the immediate. If we get here
151 // and we have a Kind::Imm, skip the next operand, and continue.
152 if (Flag.isImmKind()) {
153 SDValue op = N->getOperand(++i);
154 AsmNodeOperands.push_back(op);
155 continue;
156 }
157
158 const unsigned NumRegs = Flag.getNumOperandRegisters();
159 if (NumRegs)
160 OpChanged.push_back(false);
161
162 unsigned DefIdx = 0;
163 bool IsTiedToChangedOp = false;
164 // If it's a use that is tied with a previous def, it has no
165 // reg class constraint.
166 if (Changed && Flag.isUseOperandTiedToDef(DefIdx))
167 IsTiedToChangedOp = OpChanged[DefIdx];
168
169 // Memory operands to inline asm in the SelectionDAG are modeled with two
170 // operands: a constant of value InlineAsm::Kind::Mem followed by the input
171 // operand. If we get here and we have a Kind::Mem, skip the next operand
172 // (so it doesn't get misinterpreted), and continue. We do this here because
173 // it's important to update the OpChanged array correctly before moving on.
174 if (Flag.isMemKind()) {
175 SDValue op = N->getOperand(++i);
176 AsmNodeOperands.push_back(op);
177 continue;
178 }
179
180 if (!Flag.isRegUseKind() && !Flag.isRegDefKind() &&
181 !Flag.isRegDefEarlyClobberKind())
182 continue;
183
184 unsigned RC;
185 const bool HasRC = Flag.hasRegClassConstraint(RC);
186 if ((!IsTiedToChangedOp && (!HasRC || RC != CSKY::GPRRegClassID)) ||
187 NumRegs != 2)
188 continue;
189
190 assert((i + 2 < NumOps) && "Invalid number of operands in inline asm");
191 SDValue V0 = N->getOperand(i + 1);
192 SDValue V1 = N->getOperand(i + 2);
193 unsigned Reg0 = cast<RegisterSDNode>(V0)->getReg();
194 unsigned Reg1 = cast<RegisterSDNode>(V1)->getReg();
195 SDValue PairedReg;
196 MachineRegisterInfo &MRI = MF->getRegInfo();
197
198 if (Flag.isRegDefKind() || Flag.isRegDefEarlyClobberKind()) {
199 // Replace the two GPRs with 1 GPRPair and copy values from GPRPair to
200 // the original GPRs.
201
202 Register GPVR = MRI.createVirtualRegister(&CSKY::GPRPairRegClass);
203 PairedReg = CurDAG->getRegister(GPVR, MVT::i64);
204 SDValue Chain = SDValue(N, 0);
205
206 SDNode *GU = N->getGluedUser();
207 SDValue RegCopy =
208 CurDAG->getCopyFromReg(Chain, dl, GPVR, MVT::i64, Chain.getValue(1));
209
210 // Extract values from a GPRPair reg and copy to the original GPR reg.
211 SDValue Sub0 =
212 CurDAG->getTargetExtractSubreg(CSKY::sub32_0, dl, MVT::i32, RegCopy);
213 SDValue Sub1 =
214 CurDAG->getTargetExtractSubreg(CSKY::sub32_32, dl, MVT::i32, RegCopy);
215 SDValue T0 =
216 CurDAG->getCopyToReg(Sub0, dl, Reg0, Sub0, RegCopy.getValue(1));
217 SDValue T1 = CurDAG->getCopyToReg(Sub1, dl, Reg1, Sub1, T0.getValue(1));
218
219 // Update the original glue user.
220 std::vector<SDValue> Ops(GU->op_begin(), GU->op_end() - 1);
221 Ops.push_back(T1.getValue(1));
222 CurDAG->UpdateNodeOperands(GU, Ops);
223 } else {
224 // For Kind == InlineAsm::Kind::RegUse, we first copy two GPRs into a
225 // GPRPair and then pass the GPRPair to the inline asm.
226 SDValue Chain = AsmNodeOperands[InlineAsm::Op_InputChain];
227
228 // As REG_SEQ doesn't take RegisterSDNode, we copy them first.
229 SDValue T0 =
230 CurDAG->getCopyFromReg(Chain, dl, Reg0, MVT::i32, Chain.getValue(1));
231 SDValue T1 =
232 CurDAG->getCopyFromReg(Chain, dl, Reg1, MVT::i32, T0.getValue(1));
233 SDValue Pair = SDValue(createGPRPairNode(MVT::i64, T0, T1), 0);
234
235 // Copy REG_SEQ into a GPRPair-typed VR and replace the original two
236 // i32 VRs of inline asm with it.
237 Register GPVR = MRI.createVirtualRegister(&CSKY::GPRPairRegClass);
238 PairedReg = CurDAG->getRegister(GPVR, MVT::i64);
239 Chain = CurDAG->getCopyToReg(T1, dl, GPVR, Pair, T1.getValue(1));
240
241 AsmNodeOperands[InlineAsm::Op_InputChain] = Chain;
242 Glue = Chain.getValue(1);
243 }
244
245 Changed = true;
246
247 if (PairedReg.getNode()) {
248 OpChanged[OpChanged.size() - 1] = true;
249 // TODO: maybe a setter for getNumOperandRegisters?
250 Flag = InlineAsm::Flag(Flag.getKind(), 1 /* RegNum*/);
251 if (IsTiedToChangedOp)
252 Flag.setMatchingOp(DefIdx);
253 else
254 Flag.setRegClass(CSKY::GPRPairRegClassID);
255 // Replace the current flag.
256 AsmNodeOperands[AsmNodeOperands.size() - 1] =
257 CurDAG->getTargetConstant(Flag, dl, MVT::i32);
258 // Add the new register node and skip the original two GPRs.
259 AsmNodeOperands.push_back(PairedReg);
260 // Skip the next two GPRs.
261 i += 2;
262 }
263 }
264
265 if (Glue.getNode())
266 AsmNodeOperands.push_back(Glue);
267 if (!Changed)
268 return false;
269
270 SDValue New = CurDAG->getNode(N->getOpcode(), SDLoc(N),
271 CurDAG->getVTList(MVT::Other, MVT::Glue),
272 AsmNodeOperands);
273 New->setNodeId(-1);
274 ReplaceNode(N, New.getNode());
275 return true;
276}
277
278bool CSKYDAGToDAGISel::selectBITCAST_TO_LOHI(SDNode *N) {
279 SDLoc Dl(N);
280 auto VT = N->getValueType(0);
281 auto V = N->getOperand(0);
282
283 if (!Subtarget->hasFPUv2DoubleFloat())
284 return false;
285
286 SDValue V1 = SDValue(CurDAG->getMachineNode(CSKY::FMFVRL_D, Dl, VT, V), 0);
287 SDValue V2 = SDValue(CurDAG->getMachineNode(CSKY::FMFVRH_D, Dl, VT, V), 0);
288
289 ReplaceUses(SDValue(N, 0), V1);
290 ReplaceUses(SDValue(N, 1), V2);
291 CurDAG->RemoveDeadNode(N);
292
293 return true;
294}
295
296bool CSKYDAGToDAGISel::selectAddCarry(SDNode *N) {
297 MachineSDNode *NewNode = nullptr;
298 auto Type0 = N->getValueType(0);
299 auto Type1 = N->getValueType(1);
300 auto Op0 = N->getOperand(0);
301 auto Op1 = N->getOperand(1);
302 auto Op2 = N->getOperand(2);
303
304 SDLoc Dl(N);
305
306 if (isNullConstant(Op2)) {
307 auto *CA = CurDAG->getMachineNode(
308 Subtarget->has2E3() ? CSKY::CLRC32 : CSKY::CLRC16, Dl, Type1);
309 NewNode = CurDAG->getMachineNode(
310 Subtarget->has2E3() ? CSKY::ADDC32 : CSKY::ADDC16, Dl, {Type0, Type1},
311 {Op0, Op1, SDValue(CA, 0)});
312 } else if (isOneConstant(Op2)) {
313 auto *CA = CurDAG->getMachineNode(
314 Subtarget->has2E3() ? CSKY::SETC32 : CSKY::SETC16, Dl, Type1);
315 NewNode = CurDAG->getMachineNode(
316 Subtarget->has2E3() ? CSKY::ADDC32 : CSKY::ADDC16, Dl, {Type0, Type1},
317 {Op0, Op1, SDValue(CA, 0)});
318 } else {
319 NewNode = CurDAG->getMachineNode(Subtarget->has2E3() ? CSKY::ADDC32
320 : CSKY::ADDC16,
321 Dl, {Type0, Type1}, {Op0, Op1, Op2});
322 }
323 ReplaceNode(N, NewNode);
324 return true;
325}
326
327static SDValue InvertCarryFlag(const CSKYSubtarget *Subtarget,
328 SelectionDAG *DAG, SDLoc Dl, SDValue OldCarry) {
329 auto NewCarryReg =
330 DAG->getMachineNode(Subtarget->has2E3() ? CSKY::MVCV32 : CSKY::MVCV16, Dl,
331 MVT::i32, OldCarry);
332 auto NewCarry =
333 DAG->getMachineNode(Subtarget->hasE2() ? CSKY::BTSTI32 : CSKY::BTSTI16,
334 Dl, OldCarry.getValueType(), SDValue(NewCarryReg, 0),
335 DAG->getTargetConstant(0, Dl, MVT::i32));
336 return SDValue(NewCarry, 0);
337}
338
339bool CSKYDAGToDAGISel::selectSubCarry(SDNode *N) {
340 MachineSDNode *NewNode = nullptr;
341 auto Type0 = N->getValueType(0);
342 auto Type1 = N->getValueType(1);
343 auto Op0 = N->getOperand(0);
344 auto Op1 = N->getOperand(1);
345 auto Op2 = N->getOperand(2);
346
347 SDLoc Dl(N);
348
349 if (isNullConstant(Op2)) {
350 auto *CA = CurDAG->getMachineNode(
351 Subtarget->has2E3() ? CSKY::SETC32 : CSKY::SETC16, Dl, Type1);
352 NewNode = CurDAG->getMachineNode(
353 Subtarget->has2E3() ? CSKY::SUBC32 : CSKY::SUBC16, Dl, {Type0, Type1},
354 {Op0, Op1, SDValue(CA, 0)});
355 } else if (isOneConstant(Op2)) {
356 auto *CA = CurDAG->getMachineNode(
357 Subtarget->has2E3() ? CSKY::CLRC32 : CSKY::CLRC16, Dl, Type1);
358 NewNode = CurDAG->getMachineNode(
359 Subtarget->has2E3() ? CSKY::SUBC32 : CSKY::SUBC16, Dl, {Type0, Type1},
360 {Op0, Op1, SDValue(CA, 0)});
361 } else {
362 auto CarryIn = InvertCarryFlag(Subtarget, CurDAG, Dl, Op2);
363 NewNode = CurDAG->getMachineNode(Subtarget->has2E3() ? CSKY::SUBC32
364 : CSKY::SUBC16,
365 Dl, {Type0, Type1}, {Op0, Op1, CarryIn});
366 }
367 auto CarryOut = InvertCarryFlag(Subtarget, CurDAG, Dl, SDValue(NewNode, 1));
368
369 ReplaceUses(SDValue(N, 0), SDValue(NewNode, 0));
370 ReplaceUses(SDValue(N, 1), CarryOut);
371 CurDAG->RemoveDeadNode(N);
372
373 return true;
374}
375
376SDNode *CSKYDAGToDAGISel::createGPRPairNode(EVT VT, SDValue V0, SDValue V1) {
377 SDLoc dl(V0.getNode());
378 SDValue RegClass =
379 CurDAG->getTargetConstant(CSKY::GPRPairRegClassID, dl, MVT::i32);
380 SDValue SubReg0 = CurDAG->getTargetConstant(CSKY::sub32_0, dl, MVT::i32);
381 SDValue SubReg1 = CurDAG->getTargetConstant(CSKY::sub32_32, dl, MVT::i32);
382 const SDValue Ops[] = {RegClass, V0, SubReg0, V1, SubReg1};
383 return CurDAG->getMachineNode(TargetOpcode::REG_SEQUENCE, dl, VT, Ops);
384}
385
386bool CSKYDAGToDAGISel::SelectInlineAsmMemoryOperand(
387 const SDValue &Op, const InlineAsm::ConstraintCode ConstraintID,
388 std::vector<SDValue> &OutOps) {
389 switch (ConstraintID) {
390 case InlineAsm::ConstraintCode::m:
391 // We just support simple memory operands that have a single address
392 // operand and need no special handling.
393 OutOps.push_back(Op);
394 return false;
395 default:
396 break;
397 }
398
399 return true;
400}
401
403 CodeGenOptLevel OptLevel) {
404 return new CSKYDAGToDAGISel(TM, OptLevel);
405}
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
const char LLVMTargetMachineRef TM
#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:311
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,...
bool runOnMachineFunction(MachineFunction &MF) override
runOnMachineFunction - This method must be overloaded to perform the desired machine code transformat...
This is used to represent a portion of an LLVM function in a low-level Data Dependence DAG representa...
Definition: SelectionDAG.h:225
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:676
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:87
@ UADDO_CARRY
Carry-using nodes for multiple precision addition and subtraction.
Definition: ISDOpcodes.h:303
@ INLINEASM_BR
INLINEASM_BR - Branching version of inline asm. Used by asm-goto.
Definition: ISDOpcodes.h:1097
@ INLINEASM
INLINEASM - Represents an inline asm block.
Definition: ISDOpcodes.h:1094
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:34