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