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
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>();
39 SelectionDAGISel::runOnMachineFunction(MF);
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
49 SDNode *createGPRPairNode(EVT VT, SDValue V0, SDValue V1);
50
51 bool SelectInlineAsmMemoryOperand(const SDValue &Op,
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;
78 case ISD::UADDO_CARRY:
79 IsSelected = selectAddCarry(N);
80 break;
81 case ISD::USUBO_CARRY:
82 IsSelected = selectSubCarry(N);
83 break;
84 case ISD::GLOBAL_OFFSET_TABLE: {
85 Register GP = Subtarget->getInstrInfo()->getGlobalBaseReg(MF&: *MF);
86 ReplaceNode(F: N, T: CurDAG->getRegister(Reg: GP, VT: N->getValueType(ResNo: 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>(Val: N)->getIndex();
94 SDValue TFI = CurDAG->getTargetFrameIndex(FI, MVT::VT: 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 }
102 case CSKYISD::BITCAST_TO_LOHI:
103 IsSelected = selectBITCAST_TO_LOHI(N);
104 break;
105 case ISD::INLINEASM:
106 case ISD::INLINEASM_BR:
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;
120 InlineAsm::Flag Flag;
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(Num: 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(Num: i);
138 AsmNodeOperands.push_back(x: op);
139
140 if (i < InlineAsm::Op_FirstOperand)
141 continue;
142
143 if (const auto *C = dyn_cast<ConstantSDNode>(Val: N->getOperand(Num: 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(Num: ++i);
154 AsmNodeOperands.push_back(x: op);
155 continue;
156 }
157
158 const unsigned NumRegs = Flag.getNumOperandRegisters();
159 if (NumRegs)
160 OpChanged.push_back(Elt: 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(Idx&: 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(Num: ++i);
176 AsmNodeOperands.push_back(x: 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(Num: i + 1);
192 SDValue V1 = N->getOperand(Num: i + 2);
193 unsigned Reg0 = cast<RegisterSDNode>(Val&: V0)->getReg();
194 unsigned Reg1 = cast<RegisterSDNode>(Val&: 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(Reg: GPVR, MVT::VT: 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(R: 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(Chain: Sub0, dl, Reg: Reg0, N: Sub0, Glue: RegCopy.getValue(R: 1));
217 SDValue T1 = CurDAG->getCopyToReg(Chain: Sub1, dl, Reg: Reg1, N: Sub1, Glue: T0.getValue(R: 1));
218
219 // Update the original glue user.
220 std::vector<SDValue> Ops(GU->op_begin(), GU->op_end() - 1);
221 Ops.push_back(x: T1.getValue(R: 1));
222 CurDAG->UpdateNodeOperands(N: 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(R: 1));
231 SDValue T1 =
232 CurDAG->getCopyFromReg(Chain, dl, Reg1, MVT::i32, T0.getValue(R: 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(Reg: GPVR, MVT::VT: i64);
239 Chain = CurDAG->getCopyToReg(Chain: T1, dl, Reg: GPVR, N: Pair, Glue: T1.getValue(R: 1));
240
241 AsmNodeOperands[InlineAsm::Op_InputChain] = Chain;
242 Glue = Chain.getValue(R: 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(x: PairedReg);
260 // Skip the next two GPRs.
261 i += 2;
262 }
263 }
264
265 if (Glue.getNode())
266 AsmNodeOperands.push_back(x: 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(F: N, T: New.getNode());
275 return true;
276}
277
278bool CSKYDAGToDAGISel::selectBITCAST_TO_LOHI(SDNode *N) {
279 SDLoc Dl(N);
280 auto VT = N->getValueType(ResNo: 0);
281 auto V = N->getOperand(Num: 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(F: SDValue(N, 0), T: V1);
290 ReplaceUses(F: SDValue(N, 1), T: 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(ResNo: 0);
299 auto Type1 = N->getValueType(ResNo: 1);
300 auto Op0 = N->getOperand(Num: 0);
301 auto Op1 = N->getOperand(Num: 1);
302 auto Op2 = N->getOperand(Num: 2);
303
304 SDLoc Dl(N);
305
306 if (isNullConstant(V: 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(V: 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(F: N, T: 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(ResNo: 0);
342 auto Type1 = N->getValueType(ResNo: 1);
343 auto Op0 = N->getOperand(Num: 0);
344 auto Op1 = N->getOperand(Num: 1);
345 auto Op2 = N->getOperand(Num: 2);
346
347 SDLoc Dl(N);
348
349 if (isNullConstant(V: 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(V: 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, DAG: CurDAG, Dl, OldCarry: 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, DAG: CurDAG, Dl, OldCarry: SDValue(NewNode, 1));
368
369 ReplaceUses(F: SDValue(N, 0), T: SDValue(NewNode, 0));
370 ReplaceUses(F: SDValue(N, 1), T: 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(Opcode: 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(x: Op);
394 return false;
395 default:
396 break;
397 }
398
399 return true;
400}
401
402FunctionPass *llvm::createCSKYISelDag(CSKYTargetMachine &TM,
403 CodeGenOptLevel OptLevel) {
404 return new CSKYDAGToDAGISel(TM, OptLevel);
405}
406

source code of llvm/lib/Target/CSKY/CSKYISelDAGToDAG.cpp