xref: /freebsd/contrib/llvm-project/llvm/lib/Target/CSKY/CSKYISelDAGToDAG.cpp (revision 6580f5c38dd5b01aeeaed16b370f1a12423437f0)
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:
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 
59 char CSKYDAGToDAGISel::ID = 0;
60 
61 INITIALIZE_PASS(CSKYDAGToDAGISel, DEBUG_TYPE, PASS_NAME, false, false)
62 
63 void 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);
86     ReplaceNode(N, CurDAG->getRegister(GP, N->getValueType(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>(N)->getIndex();
94     SDValue TFI = CurDAG->getTargetFrameIndex(FI, MVT::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 
118 bool 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(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(i);
138     AsmNodeOperands.push_back(op);
139 
140     if (i < InlineAsm::Op_FirstOperand)
141       continue;
142 
143     if (const auto *C = dyn_cast<ConstantSDNode>(N->getOperand(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(++i);
154       AsmNodeOperands.push_back(op);
155       continue;
156     }
157 
158     const unsigned NumRegs = Flag.getNumOperandRegisters();
159     if (NumRegs)
160       OpChanged.push_back(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(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(++i);
176       AsmNodeOperands.push_back(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(i + 1);
192     SDValue V1 = N->getOperand(i + 2);
193     unsigned Reg0 = cast<RegisterSDNode>(V0)->getReg();
194     unsigned Reg1 = cast<RegisterSDNode>(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(GPVR, MVT::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(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(Sub0, dl, Reg0, Sub0, RegCopy.getValue(1));
217       SDValue T1 = CurDAG->getCopyToReg(Sub1, dl, Reg1, Sub1, T0.getValue(1));
218 
219       // Update the original glue user.
220       std::vector<SDValue> Ops(GU->op_begin(), GU->op_end() - 1);
221       Ops.push_back(T1.getValue(1));
222       CurDAG->UpdateNodeOperands(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(1));
231       SDValue T1 =
232           CurDAG->getCopyFromReg(Chain, dl, Reg1, MVT::i32, T0.getValue(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(GPVR, MVT::i64);
239       Chain = CurDAG->getCopyToReg(T1, dl, GPVR, Pair, T1.getValue(1));
240 
241       AsmNodeOperands[InlineAsm::Op_InputChain] = Chain;
242       Glue = Chain.getValue(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(PairedReg);
260       // Skip the next two GPRs.
261       i += 2;
262     }
263   }
264 
265   if (Glue.getNode())
266     AsmNodeOperands.push_back(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(N, New.getNode());
275   return true;
276 }
277 
278 bool CSKYDAGToDAGISel::selectBITCAST_TO_LOHI(SDNode *N) {
279   SDLoc Dl(N);
280   auto VT = N->getValueType(0);
281   auto V = N->getOperand(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(SDValue(N, 0), V1);
290   ReplaceUses(SDValue(N, 1), V2);
291   CurDAG->RemoveDeadNode(N);
292 
293   return true;
294 }
295 
296 bool CSKYDAGToDAGISel::selectAddCarry(SDNode *N) {
297   MachineSDNode *NewNode = nullptr;
298   auto Type0 = N->getValueType(0);
299   auto Type1 = N->getValueType(1);
300   auto Op0 = N->getOperand(0);
301   auto Op1 = N->getOperand(1);
302   auto Op2 = N->getOperand(2);
303 
304   SDLoc Dl(N);
305 
306   if (isNullConstant(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(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(N, NewNode);
324   return true;
325 }
326 
327 static 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 
339 bool CSKYDAGToDAGISel::selectSubCarry(SDNode *N) {
340   MachineSDNode *NewNode = nullptr;
341   auto Type0 = N->getValueType(0);
342   auto Type1 = N->getValueType(1);
343   auto Op0 = N->getOperand(0);
344   auto Op1 = N->getOperand(1);
345   auto Op2 = N->getOperand(2);
346 
347   SDLoc Dl(N);
348 
349   if (isNullConstant(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(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, CurDAG, Dl, 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, CurDAG, Dl, SDValue(NewNode, 1));
368 
369   ReplaceUses(SDValue(N, 0), SDValue(NewNode, 0));
370   ReplaceUses(SDValue(N, 1), CarryOut);
371   CurDAG->RemoveDeadNode(N);
372 
373   return true;
374 }
375 
376 SDNode *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(TargetOpcode::REG_SEQUENCE, dl, VT, Ops);
384 }
385 
386 bool 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(Op);
394     return false;
395   default:
396     break;
397   }
398 
399   return true;
400 }
401 
402 FunctionPass *llvm::createCSKYISelDag(CSKYTargetMachine &TM,
403                                       CodeGenOptLevel OptLevel) {
404   return new CSKYDAGToDAGISel(TM, OptLevel);
405 }
406