1 //=- LoongArchISelDAGToDAG.cpp - A dag to dag inst selector for LoongArch -===//
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 LoongArch target.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "LoongArchISelDAGToDAG.h"
14 #include "LoongArchISelLowering.h"
15 #include "MCTargetDesc/LoongArchMCTargetDesc.h"
16 #include "MCTargetDesc/LoongArchMatInt.h"
17 #include "llvm/Support/KnownBits.h"
18 
19 using namespace llvm;
20 
21 #define DEBUG_TYPE "loongarch-isel"
22 #define PASS_NAME "LoongArch DAG->DAG Pattern Instruction Selection"
23 
24 char LoongArchDAGToDAGISel::ID;
25 
26 INITIALIZE_PASS(LoongArchDAGToDAGISel, DEBUG_TYPE, PASS_NAME, false, false)
27 
28 void LoongArchDAGToDAGISel::Select(SDNode *Node) {
29   // If we have a custom node, we have already selected.
30   if (Node->isMachineOpcode()) {
31     LLVM_DEBUG(dbgs() << "== "; Node->dump(CurDAG); dbgs() << "\n");
32     Node->setNodeId(-1);
33     return;
34   }
35 
36   // Instruction Selection not handled by the auto-generated tablegen selection
37   // should be handled here.
38   unsigned Opcode = Node->getOpcode();
39   MVT GRLenVT = Subtarget->getGRLenVT();
40   SDLoc DL(Node);
41   MVT VT = Node->getSimpleValueType(0);
42 
43   switch (Opcode) {
44   default:
45     break;
46   case ISD::Constant: {
47     int64_t Imm = cast<ConstantSDNode>(Node)->getSExtValue();
48     if (Imm == 0 && VT == GRLenVT) {
49       SDValue New = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), DL,
50                                            LoongArch::R0, GRLenVT);
51       ReplaceNode(Node, New.getNode());
52       return;
53     }
54     SDNode *Result = nullptr;
55     SDValue SrcReg = CurDAG->getRegister(LoongArch::R0, GRLenVT);
56     // The instructions in the sequence are handled here.
57     for (LoongArchMatInt::Inst &Inst : LoongArchMatInt::generateInstSeq(Imm)) {
58       SDValue SDImm = CurDAG->getTargetConstant(Inst.Imm, DL, GRLenVT);
59       if (Inst.Opc == LoongArch::LU12I_W)
60         Result = CurDAG->getMachineNode(LoongArch::LU12I_W, DL, GRLenVT, SDImm);
61       else
62         Result = CurDAG->getMachineNode(Inst.Opc, DL, GRLenVT, SrcReg, SDImm);
63       SrcReg = SDValue(Result, 0);
64     }
65 
66     ReplaceNode(Node, Result);
67     return;
68   }
69   case ISD::FrameIndex: {
70     SDValue Imm = CurDAG->getTargetConstant(0, DL, GRLenVT);
71     int FI = cast<FrameIndexSDNode>(Node)->getIndex();
72     SDValue TFI = CurDAG->getTargetFrameIndex(FI, VT);
73     unsigned ADDIOp =
74         Subtarget->is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W;
75     ReplaceNode(Node, CurDAG->getMachineNode(ADDIOp, DL, VT, TFI, Imm));
76     return;
77   }
78     // TODO: Add selection nodes needed later.
79   }
80 
81   // Select the default instruction.
82   SelectCode(Node);
83 }
84 
85 bool LoongArchDAGToDAGISel::SelectInlineAsmMemoryOperand(
86     const SDValue &Op, unsigned ConstraintID, std::vector<SDValue> &OutOps) {
87   SDValue Base = Op;
88   SDValue Offset =
89       CurDAG->getTargetConstant(0, SDLoc(Op), Subtarget->getGRLenVT());
90   switch (ConstraintID) {
91   default:
92     llvm_unreachable("unexpected asm memory constraint");
93   // Reg+Reg addressing.
94   case InlineAsm::Constraint_k:
95     Base = Op.getOperand(0);
96     Offset = Op.getOperand(1);
97     break;
98   // Reg+simm12 addressing.
99   case InlineAsm::Constraint_m:
100     if (CurDAG->isBaseWithConstantOffset(Op)) {
101       ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Op.getOperand(1));
102       if (isIntN(12, CN->getSExtValue())) {
103         Base = Op.getOperand(0);
104         Offset = CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(Op),
105                                            Op.getValueType());
106       }
107     }
108     break;
109   // Reg+0 addressing.
110   case InlineAsm::Constraint_ZB:
111     break;
112   // Reg+(simm14<<2) addressing.
113   case InlineAsm::Constraint_ZC:
114     if (CurDAG->isBaseWithConstantOffset(Op)) {
115       ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Op.getOperand(1));
116       if (isIntN(16, CN->getSExtValue()) &&
117           isAligned(Align(4ULL), CN->getZExtValue())) {
118         Base = Op.getOperand(0);
119         Offset = CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(Op),
120                                            Op.getValueType());
121       }
122     }
123     break;
124   }
125   OutOps.push_back(Base);
126   OutOps.push_back(Offset);
127   return false;
128 }
129 
130 bool LoongArchDAGToDAGISel::SelectBaseAddr(SDValue Addr, SDValue &Base) {
131   // If this is FrameIndex, select it directly. Otherwise just let it get
132   // selected to a register independently.
133   if (auto *FIN = dyn_cast<FrameIndexSDNode>(Addr))
134     Base =
135         CurDAG->getTargetFrameIndex(FIN->getIndex(), Subtarget->getGRLenVT());
136   else
137     Base = Addr;
138   return true;
139 }
140 
141 // Fold constant addresses.
142 bool LoongArchDAGToDAGISel::SelectAddrConstant(SDValue Addr, SDValue &Base,
143                                                SDValue &Offset) {
144   SDLoc DL(Addr);
145   MVT VT = Addr.getSimpleValueType();
146 
147   if (!isa<ConstantSDNode>(Addr))
148     return false;
149 
150   // If the constant is a simm12, we can fold the whole constant and use R0 as
151   // the base.
152   int64_t CVal = cast<ConstantSDNode>(Addr)->getSExtValue();
153   if (!isInt<12>(CVal))
154     return false;
155   Base = CurDAG->getRegister(LoongArch::R0, VT);
156   Offset = CurDAG->getTargetConstant(SignExtend64<12>(CVal), DL, VT);
157   return true;
158 }
159 
160 bool LoongArchDAGToDAGISel::selectNonFIBaseAddr(SDValue Addr, SDValue &Base) {
161   // If this is FrameIndex, don't select it.
162   if (isa<FrameIndexSDNode>(Addr))
163     return false;
164   Base = Addr;
165   return true;
166 }
167 
168 bool LoongArchDAGToDAGISel::selectShiftMask(SDValue N, unsigned ShiftWidth,
169                                             SDValue &ShAmt) {
170   // Shift instructions on LoongArch only read the lower 5 or 6 bits of the
171   // shift amount. If there is an AND on the shift amount, we can bypass it if
172   // it doesn't affect any of those bits.
173   if (N.getOpcode() == ISD::AND && isa<ConstantSDNode>(N.getOperand(1))) {
174     const APInt &AndMask = N->getConstantOperandAPInt(1);
175 
176     // Since the max shift amount is a power of 2 we can subtract 1 to make a
177     // mask that covers the bits needed to represent all shift amounts.
178     assert(isPowerOf2_32(ShiftWidth) && "Unexpected max shift amount!");
179     APInt ShMask(AndMask.getBitWidth(), ShiftWidth - 1);
180 
181     if (ShMask.isSubsetOf(AndMask)) {
182       ShAmt = N.getOperand(0);
183       return true;
184     }
185 
186     // SimplifyDemandedBits may have optimized the mask so try restoring any
187     // bits that are known zero.
188     KnownBits Known = CurDAG->computeKnownBits(N->getOperand(0));
189     if (ShMask.isSubsetOf(AndMask | Known.Zero)) {
190       ShAmt = N.getOperand(0);
191       return true;
192     }
193   } else if (N.getOpcode() == LoongArchISD::BSTRPICK) {
194     // Similar to the above AND, if there is a BSTRPICK on the shift amount, we
195     // can bypass it.
196     assert(isPowerOf2_32(ShiftWidth) && "Unexpected max shift amount!");
197     assert(isa<ConstantSDNode>(N.getOperand(1)) && "Illegal msb operand!");
198     assert(isa<ConstantSDNode>(N.getOperand(2)) && "Illegal lsb operand!");
199     uint64_t msb = N.getConstantOperandVal(1), lsb = N.getConstantOperandVal(2);
200     if (lsb == 0 && Log2_32(ShiftWidth) <= msb + 1) {
201       ShAmt = N.getOperand(0);
202       return true;
203     }
204   } else if (N.getOpcode() == ISD::SUB &&
205              isa<ConstantSDNode>(N.getOperand(0))) {
206     uint64_t Imm = N.getConstantOperandVal(0);
207     // If we are shifting by N-X where N == 0 mod Size, then just shift by -X to
208     // generate a NEG instead of a SUB of a constant.
209     if (Imm != 0 && Imm % ShiftWidth == 0) {
210       SDLoc DL(N);
211       EVT VT = N.getValueType();
212       SDValue Zero =
213           CurDAG->getCopyFromReg(CurDAG->getEntryNode(), DL, LoongArch::R0, VT);
214       unsigned NegOpc = VT == MVT::i64 ? LoongArch::SUB_D : LoongArch::SUB_W;
215       MachineSDNode *Neg =
216           CurDAG->getMachineNode(NegOpc, DL, VT, Zero, N.getOperand(1));
217       ShAmt = SDValue(Neg, 0);
218       return true;
219     }
220   }
221 
222   ShAmt = N;
223   return true;
224 }
225 
226 bool LoongArchDAGToDAGISel::selectSExti32(SDValue N, SDValue &Val) {
227   if (N.getOpcode() == ISD::SIGN_EXTEND_INREG &&
228       cast<VTSDNode>(N.getOperand(1))->getVT() == MVT::i32) {
229     Val = N.getOperand(0);
230     return true;
231   }
232   if (N.getOpcode() == LoongArchISD::BSTRPICK &&
233       N.getConstantOperandVal(1) < UINT64_C(0X1F) &&
234       N.getConstantOperandVal(2) == UINT64_C(0)) {
235     Val = N;
236     return true;
237   }
238   MVT VT = N.getSimpleValueType();
239   if (CurDAG->ComputeNumSignBits(N) > (VT.getSizeInBits() - 32)) {
240     Val = N;
241     return true;
242   }
243 
244   return false;
245 }
246 
247 bool LoongArchDAGToDAGISel::selectZExti32(SDValue N, SDValue &Val) {
248   if (N.getOpcode() == ISD::AND) {
249     auto *C = dyn_cast<ConstantSDNode>(N.getOperand(1));
250     if (C && C->getZExtValue() == UINT64_C(0xFFFFFFFF)) {
251       Val = N.getOperand(0);
252       return true;
253     }
254   }
255   MVT VT = N.getSimpleValueType();
256   APInt Mask = APInt::getHighBitsSet(VT.getSizeInBits(), 32);
257   if (CurDAG->MaskedValueIsZero(N, Mask)) {
258     Val = N;
259     return true;
260   }
261 
262   return false;
263 }
264 
265 // This pass converts a legalized DAG into a LoongArch-specific DAG, ready
266 // for instruction scheduling.
267 FunctionPass *llvm::createLoongArchISelDag(LoongArchTargetMachine &TM) {
268   return new LoongArchDAGToDAGISel(TM);
269 }
270