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:
30   explicit CSKYDAGToDAGISel(CSKYTargetMachine &TM) : SelectionDAGISel(TM) {}
31 
32   StringRef getPassName() const override {
33     return "CSKY DAG->DAG Pattern Instruction Selection";
34   }
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, unsigned ConstraintID,
52                                     std::vector<SDValue> &OutOps) override;
53 
54 #include "CSKYGenDAGISel.inc"
55 };
56 } // namespace
57 
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 
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 
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 
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 
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 
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 
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 
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 
397 FunctionPass *llvm::createCSKYISelDag(CSKYTargetMachine &TM) {
398   return new CSKYDAGToDAGISel(TM);
399 }
400