xref: /freebsd/contrib/llvm-project/llvm/lib/Target/CSKY/CSKYISelDAGToDAG.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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