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