Thanks to visit codestin.com
Credit goes to llvm.org

LLVM 22.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
49 bool SelectInlineAsmMemoryOperand(const SDValue &Op,
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 }
109 case CSKYISD::BITCAST_TO_LOHI:
110 IsSelected = selectBITCAST_TO_LOHI(N);
111 break;
112 case ISD::INLINEASM:
113 case ISD::INLINEASM_BR:
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;
127 InlineAsm::Flag Flag;
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
return SDValue()
static SDValue createGPRPairNode(SelectionDAG &DAG, SDValue V)
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
AMDGPU Register Bank Select
static SDValue InvertCarryFlag(const CSKYSubtarget *Subtarget, SelectionDAG *DAG, SDLoc Dl, SDValue OldCarry)
#define DEBUG_TYPE
#define op(i)
const size_t AbstractManglingParser< Derived, Alloc >::NumOps
const AbstractManglingParser< Derived, Alloc >::OperatorInfo AbstractManglingParser< Derived, Alloc >::Ops[]
Promote Memory to Register
Definition Mem2Reg.cpp:110
#define T1
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition PassSupport.h:56
#define PASS_NAME
bool hasFPUv2DoubleFloat() const
FunctionPass class - This class is used to implement most global optimizations.
Definition Pass.h:314
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
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.
EVT getValueType(unsigned ResNo) const
Return the type of a specified result.
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 runOnMachineFunction(MachineFunction &mf)
This is used to represent a portion of an LLVM function in a low-level Data Dependence DAG representa...
LLVM_ABI 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)
void push_back(const T &Elt)
Changed
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
@ GLOBAL_OFFSET_TABLE
The address of the GOT.
Definition ISDOpcodes.h:103
@ UADDO_CARRY
Carry-using nodes for multiple precision addition and subtraction.
Definition ISDOpcodes.h:323
Flag
These should be considered private to the implementation of the MCInstrDesc class.
This is an optimization pass for GlobalISel generic memory operations.
LLVM_ABI bool isNullConstant(SDValue V)
Returns true if V is a constant integer zero.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:649
CodeGenOptLevel
Code generation optimization level.
Definition CodeGen.h:82
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
DWARFExpression::Operation Op
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:565
LLVM_ABI 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