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"
16 #include "MCTargetDesc/CSKYMCTargetDesc.h"
17 #include "llvm/CodeGen/MachineFrameInfo.h"
18 #include "llvm/CodeGen/SelectionDAG.h"
19 #include "llvm/CodeGen/SelectionDAGISel.h"
20
21 using namespace llvm;
22
23 #define DEBUG_TYPE "csky-isel"
24 #define PASS_NAME "CSKY DAG->DAG Pattern Instruction Selection"
25
26 namespace {
27 class CSKYDAGToDAGISel : public SelectionDAGISel {
28 const CSKYSubtarget *Subtarget;
29
30 public:
CSKYDAGToDAGISel(CSKYTargetMachine & TM,CodeGenOptLevel OptLevel)31 explicit CSKYDAGToDAGISel(CSKYTargetMachine &TM, CodeGenOptLevel OptLevel)
32 : SelectionDAGISel(TM, OptLevel) {}
33
runOnMachineFunction(MachineFunction & MF)34 bool runOnMachineFunction(MachineFunction &MF) override {
35 // Reset the subtarget each time through.
36 Subtarget = &MF.getSubtarget<CSKYSubtarget>();
37 SelectionDAGISel::runOnMachineFunction(MF);
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
47 SDNode *createGPRPairNode(EVT VT, SDValue V0, SDValue V1);
48
49 bool SelectInlineAsmMemoryOperand(const SDValue &Op,
50 InlineAsm::ConstraintCode ConstraintID,
51 std::vector<SDValue> &OutOps) override;
52
53 #include "CSKYGenDAGISel.inc"
54 };
55
56 class CSKYDAGToDAGISelLegacy : public SelectionDAGISelLegacy {
57 public:
58 static char ID;
CSKYDAGToDAGISelLegacy(CSKYTargetMachine & TM,CodeGenOptLevel OptLevel)59 explicit CSKYDAGToDAGISelLegacy(CSKYTargetMachine &TM,
60 CodeGenOptLevel OptLevel)
61 : SelectionDAGISelLegacy(
62 ID, std::make_unique<CSKYDAGToDAGISel>(TM, OptLevel)) {}
63 };
64 } // namespace
65
66 char CSKYDAGToDAGISelLegacy::ID = 0;
67
INITIALIZE_PASS(CSKYDAGToDAGISelLegacy,DEBUG_TYPE,PASS_NAME,false,false)68 INITIALIZE_PASS(CSKYDAGToDAGISelLegacy, DEBUG_TYPE, PASS_NAME, false, false)
69
70 void 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;
85 case ISD::UADDO_CARRY:
86 IsSelected = selectAddCarry(N);
87 break;
88 case ISD::USUBO_CARRY:
89 IsSelected = selectSubCarry(N);
90 break;
91 case ISD::GLOBAL_OFFSET_TABLE: {
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
selectInlineAsm(SDNode * N)125 bool 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
147 if (i < InlineAsm::Op_FirstOperand)
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
selectBITCAST_TO_LOHI(SDNode * N)285 bool 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
selectAddCarry(SDNode * N)303 bool 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
InvertCarryFlag(const CSKYSubtarget * Subtarget,SelectionDAG * DAG,SDLoc Dl,SDValue OldCarry)334 static 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
selectSubCarry(SDNode * N)346 bool 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
createGPRPairNode(EVT VT,SDValue V0,SDValue V1)383 SDNode *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
SelectInlineAsmMemoryOperand(const SDValue & Op,const InlineAsm::ConstraintCode ConstraintID,std::vector<SDValue> & OutOps)393 bool 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
createCSKYISelDag(CSKYTargetMachine & TM,CodeGenOptLevel OptLevel)409 FunctionPass *llvm::createCSKYISelDag(CSKYTargetMachine &TM,
410 CodeGenOptLevel OptLevel) {
411 return new CSKYDAGToDAGISelLegacy(TM, OptLevel);
412 }
413