xref: /freebsd/contrib/llvm-project/llvm/lib/Target/CSKY/CSKYISelDAGToDAG.cpp (revision 2e3507c25e42292b45a5482e116d278f5515d04d)
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, CodeGenOpt::Level 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, unsigned ConstraintID,
52                                     std::vector<SDValue> &OutOps) override;
53 
54 #include "CSKYGenDAGISel.inc"
55 };
56 } // namespace
57 
58 char CSKYDAGToDAGISel::ID = 0;
59 
60 INITIALIZE_PASS(CSKYDAGToDAGISel, DEBUG_TYPE, PASS_NAME, false, false)
61 
62 void CSKYDAGToDAGISel::Select(SDNode *N) {
63   // If we have a custom node, we have already selected
64   if (N->isMachineOpcode()) {
65     LLVM_DEBUG(dbgs() << "== "; N->dump(CurDAG); dbgs() << "\n");
66     N->setNodeId(-1);
67     return;
68   }
69 
70   SDLoc Dl(N);
71   unsigned Opcode = N->getOpcode();
72   bool IsSelected = false;
73 
74   switch (Opcode) {
75   default:
76     break;
77   case ISD::UADDO_CARRY:
78     IsSelected = selectAddCarry(N);
79     break;
80   case ISD::USUBO_CARRY:
81     IsSelected = selectSubCarry(N);
82     break;
83   case ISD::GLOBAL_OFFSET_TABLE: {
84     Register GP = Subtarget->getInstrInfo()->getGlobalBaseReg(*MF);
85     ReplaceNode(N, CurDAG->getRegister(GP, N->getValueType(0)).getNode());
86 
87     IsSelected = true;
88     break;
89   }
90   case ISD::FrameIndex: {
91     SDValue Imm = CurDAG->getTargetConstant(0, Dl, MVT::i32);
92     int FI = cast<FrameIndexSDNode>(N)->getIndex();
93     SDValue TFI = CurDAG->getTargetFrameIndex(FI, MVT::i32);
94     ReplaceNode(N, CurDAG->getMachineNode(Subtarget->hasE2() ? CSKY::ADDI32
95                                                              : CSKY::ADDI16XZ,
96                                           Dl, MVT::i32, TFI, Imm));
97 
98     IsSelected = true;
99     break;
100   }
101   case CSKYISD::BITCAST_TO_LOHI:
102     IsSelected = selectBITCAST_TO_LOHI(N);
103     break;
104   case ISD::INLINEASM:
105   case ISD::INLINEASM_BR:
106     IsSelected = selectInlineAsm(N);
107     break;
108   }
109 
110   if (IsSelected)
111     return;
112 
113   // Select the default instruction.
114   SelectCode(N);
115 }
116 
117 bool CSKYDAGToDAGISel::selectInlineAsm(SDNode *N) {
118   std::vector<SDValue> AsmNodeOperands;
119   unsigned Flag, Kind;
120   bool Changed = false;
121   unsigned NumOps = N->getNumOperands();
122 
123   // Normally, i64 data is bounded to two arbitrary GRPs for "%r" constraint.
124   // However, some instructions (e.g. mula.s32) require GPR pair.
125   // Since there is no constraint to explicitly specify a
126   // reg pair, we use GPRPair reg class for "%r" for 64-bit data.
127 
128   SDLoc dl(N);
129   SDValue Glue =
130       N->getGluedNode() ? N->getOperand(NumOps - 1) : SDValue(nullptr, 0);
131 
132   SmallVector<bool, 8> OpChanged;
133   // Glue node will be appended late.
134   for (unsigned i = 0, e = N->getGluedNode() ? NumOps - 1 : NumOps; i < e;
135        ++i) {
136     SDValue op = N->getOperand(i);
137     AsmNodeOperands.push_back(op);
138 
139     if (i < InlineAsm::Op_FirstOperand)
140       continue;
141 
142     if (ConstantSDNode *C = dyn_cast<ConstantSDNode>(N->getOperand(i))) {
143       Flag = C->getZExtValue();
144       Kind = InlineAsm::getKind(Flag);
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 (Kind == InlineAsm::Kind_Imm) {
153       SDValue op = N->getOperand(++i);
154       AsmNodeOperands.push_back(op);
155       continue;
156     }
157 
158     unsigned NumRegs = InlineAsm::getNumOperandRegisters(Flag);
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 && InlineAsm::isUseOperandTiedToDef(Flag, 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 (so
172     // 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 (Kind == InlineAsm::Kind_Mem) {
175       SDValue op = N->getOperand(++i);
176       AsmNodeOperands.push_back(op);
177       continue;
178     }
179 
180     if (Kind != InlineAsm::Kind_RegUse && Kind != InlineAsm::Kind_RegDef &&
181         Kind != InlineAsm::Kind_RegDefEarlyClobber)
182       continue;
183 
184     unsigned RC;
185     bool HasRC = InlineAsm::hasRegClassConstraint(Flag, 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 (Kind == InlineAsm::Kind_RegDef ||
199         Kind == InlineAsm::Kind_RegDefEarlyClobber) {
200       // Replace the two GPRs with 1 GPRPair and copy values from GPRPair to
201       // the original GPRs.
202 
203       Register GPVR = MRI.createVirtualRegister(&CSKY::GPRPairRegClass);
204       PairedReg = CurDAG->getRegister(GPVR, MVT::i64);
205       SDValue Chain = SDValue(N, 0);
206 
207       SDNode *GU = N->getGluedUser();
208       SDValue RegCopy =
209           CurDAG->getCopyFromReg(Chain, dl, GPVR, MVT::i64, Chain.getValue(1));
210 
211       // Extract values from a GPRPair reg and copy to the original GPR reg.
212       SDValue Sub0 =
213           CurDAG->getTargetExtractSubreg(CSKY::sub32_0, dl, MVT::i32, RegCopy);
214       SDValue Sub1 =
215           CurDAG->getTargetExtractSubreg(CSKY::sub32_32, dl, MVT::i32, RegCopy);
216       SDValue T0 =
217           CurDAG->getCopyToReg(Sub0, dl, Reg0, Sub0, RegCopy.getValue(1));
218       SDValue T1 = CurDAG->getCopyToReg(Sub1, dl, Reg1, Sub1, T0.getValue(1));
219 
220       // Update the original glue user.
221       std::vector<SDValue> Ops(GU->op_begin(), GU->op_end() - 1);
222       Ops.push_back(T1.getValue(1));
223       CurDAG->UpdateNodeOperands(GU, Ops);
224     } else {
225       // For Kind  == InlineAsm::Kind_RegUse, we first copy two GPRs into a
226       // GPRPair and then pass the GPRPair to the inline asm.
227       SDValue Chain = AsmNodeOperands[InlineAsm::Op_InputChain];
228 
229       // As REG_SEQ doesn't take RegisterSDNode, we copy them first.
230       SDValue T0 =
231           CurDAG->getCopyFromReg(Chain, dl, Reg0, MVT::i32, Chain.getValue(1));
232       SDValue T1 =
233           CurDAG->getCopyFromReg(Chain, dl, Reg1, MVT::i32, T0.getValue(1));
234       SDValue Pair = SDValue(createGPRPairNode(MVT::i64, T0, T1), 0);
235 
236       // Copy REG_SEQ into a GPRPair-typed VR and replace the original two
237       // i32 VRs of inline asm with it.
238       Register GPVR = MRI.createVirtualRegister(&CSKY::GPRPairRegClass);
239       PairedReg = CurDAG->getRegister(GPVR, MVT::i64);
240       Chain = CurDAG->getCopyToReg(T1, dl, GPVR, Pair, T1.getValue(1));
241 
242       AsmNodeOperands[InlineAsm::Op_InputChain] = Chain;
243       Glue = Chain.getValue(1);
244     }
245 
246     Changed = true;
247 
248     if (PairedReg.getNode()) {
249       OpChanged[OpChanged.size() - 1] = true;
250       Flag = InlineAsm::getFlagWord(Kind, 1 /* RegNum*/);
251       if (IsTiedToChangedOp)
252         Flag = InlineAsm::getFlagWordForMatchingOp(Flag, DefIdx);
253       else
254         Flag = InlineAsm::getFlagWordForRegClass(Flag, 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, unsigned ConstraintID, std::vector<SDValue> &OutOps) {
388   switch (ConstraintID) {
389   case InlineAsm::Constraint_m:
390     // We just support simple memory operands that have a single address
391     // operand and need no special handling.
392     OutOps.push_back(Op);
393     return false;
394   default:
395     break;
396   }
397 
398   return true;
399 }
400 
401 FunctionPass *llvm::createCSKYISelDag(CSKYTargetMachine &TM,
402                                       CodeGenOpt::Level OptLevel) {
403   return new CSKYDAGToDAGISel(TM, OptLevel);
404 }
405