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 | #include "llvm/Support/raw_ostream.h" |
19 | |
20 | using namespace llvm; |
21 | |
22 | #define DEBUG_TYPE "loongarch-isel" |
23 | #define PASS_NAME "LoongArch DAG->DAG Pattern Instruction Selection" |
24 | |
25 | char LoongArchDAGToDAGISelLegacy::ID; |
26 | |
27 | LoongArchDAGToDAGISelLegacy::LoongArchDAGToDAGISelLegacy( |
28 | LoongArchTargetMachine &TM) |
29 | : SelectionDAGISelLegacy(ID, std::make_unique<LoongArchDAGToDAGISel>(args&: TM)) {} |
30 | |
31 | INITIALIZE_PASS(LoongArchDAGToDAGISelLegacy, DEBUG_TYPE, PASS_NAME, false, |
32 | false) |
33 | |
34 | void LoongArchDAGToDAGISel::Select(SDNode *Node) { |
35 | // If we have a custom node, we have already selected. |
36 | if (Node->isMachineOpcode()) { |
37 | LLVM_DEBUG(dbgs() << "== " ; Node->dump(CurDAG); dbgs() << "\n" ); |
38 | Node->setNodeId(-1); |
39 | return; |
40 | } |
41 | |
42 | // Instruction Selection not handled by the auto-generated tablegen selection |
43 | // should be handled here. |
44 | unsigned Opcode = Node->getOpcode(); |
45 | MVT GRLenVT = Subtarget->getGRLenVT(); |
46 | SDLoc DL(Node); |
47 | MVT VT = Node->getSimpleValueType(ResNo: 0); |
48 | |
49 | switch (Opcode) { |
50 | default: |
51 | break; |
52 | case ISD::Constant: { |
53 | int64_t Imm = cast<ConstantSDNode>(Val: Node)->getSExtValue(); |
54 | if (Imm == 0 && VT == GRLenVT) { |
55 | SDValue New = CurDAG->getCopyFromReg(Chain: CurDAG->getEntryNode(), dl: DL, |
56 | Reg: LoongArch::R0, VT: GRLenVT); |
57 | ReplaceNode(F: Node, T: New.getNode()); |
58 | return; |
59 | } |
60 | SDNode *Result = nullptr; |
61 | SDValue SrcReg = CurDAG->getRegister(Reg: LoongArch::R0, VT: GRLenVT); |
62 | // The instructions in the sequence are handled here. |
63 | for (LoongArchMatInt::Inst &Inst : LoongArchMatInt::generateInstSeq(Val: Imm)) { |
64 | SDValue SDImm = CurDAG->getTargetConstant(Val: Inst.Imm, DL, VT: GRLenVT); |
65 | if (Inst.Opc == LoongArch::LU12I_W) |
66 | Result = CurDAG->getMachineNode(Opcode: LoongArch::LU12I_W, dl: DL, VT: GRLenVT, Op1: SDImm); |
67 | else |
68 | Result = CurDAG->getMachineNode(Opcode: Inst.Opc, dl: DL, VT: GRLenVT, Op1: SrcReg, Op2: SDImm); |
69 | SrcReg = SDValue(Result, 0); |
70 | } |
71 | |
72 | ReplaceNode(F: Node, T: Result); |
73 | return; |
74 | } |
75 | case ISD::FrameIndex: { |
76 | SDValue Imm = CurDAG->getTargetConstant(Val: 0, DL, VT: GRLenVT); |
77 | int FI = cast<FrameIndexSDNode>(Val: Node)->getIndex(); |
78 | SDValue TFI = CurDAG->getTargetFrameIndex(FI, VT); |
79 | unsigned ADDIOp = |
80 | Subtarget->is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W; |
81 | ReplaceNode(F: Node, T: CurDAG->getMachineNode(Opcode: ADDIOp, dl: DL, VT, Op1: TFI, Op2: Imm)); |
82 | return; |
83 | } |
84 | case ISD::BITCAST: { |
85 | if (VT.is128BitVector() || VT.is256BitVector()) { |
86 | ReplaceUses(F: SDValue(Node, 0), T: Node->getOperand(Num: 0)); |
87 | CurDAG->RemoveDeadNode(N: Node); |
88 | return; |
89 | } |
90 | break; |
91 | } |
92 | case ISD::BUILD_VECTOR: { |
93 | // Select appropriate [x]vrepli.[bhwd] instructions for constant splats of |
94 | // 128/256-bit when LSX/LASX is enabled. |
95 | BuildVectorSDNode *BVN = cast<BuildVectorSDNode>(Val: Node); |
96 | APInt SplatValue, SplatUndef; |
97 | unsigned SplatBitSize; |
98 | bool HasAnyUndefs; |
99 | unsigned Op; |
100 | EVT ViaVecTy; |
101 | bool Is128Vec = BVN->getValueType(ResNo: 0).is128BitVector(); |
102 | bool Is256Vec = BVN->getValueType(ResNo: 0).is256BitVector(); |
103 | |
104 | if (!Subtarget->hasExtLSX() || (!Is128Vec && !Is256Vec)) |
105 | break; |
106 | if (!BVN->isConstantSplat(SplatValue, SplatUndef, SplatBitSize, |
107 | HasAnyUndefs, MinSplatBits: 8)) |
108 | break; |
109 | |
110 | switch (SplatBitSize) { |
111 | default: |
112 | break; |
113 | case 8: |
114 | Op = Is256Vec ? LoongArch::PseudoXVREPLI_B : LoongArch::PseudoVREPLI_B; |
115 | ViaVecTy = Is256Vec ? MVT::v32i8 : MVT::v16i8; |
116 | break; |
117 | case 16: |
118 | Op = Is256Vec ? LoongArch::PseudoXVREPLI_H : LoongArch::PseudoVREPLI_H; |
119 | ViaVecTy = Is256Vec ? MVT::v16i16 : MVT::v8i16; |
120 | break; |
121 | case 32: |
122 | Op = Is256Vec ? LoongArch::PseudoXVREPLI_W : LoongArch::PseudoVREPLI_W; |
123 | ViaVecTy = Is256Vec ? MVT::v8i32 : MVT::v4i32; |
124 | break; |
125 | case 64: |
126 | Op = Is256Vec ? LoongArch::PseudoXVREPLI_D : LoongArch::PseudoVREPLI_D; |
127 | ViaVecTy = Is256Vec ? MVT::v4i64 : MVT::v2i64; |
128 | break; |
129 | } |
130 | |
131 | SDNode *Res; |
132 | // If we have a signed 10 bit integer, we can splat it directly. |
133 | if (SplatValue.isSignedIntN(N: 10)) { |
134 | SDValue Imm = CurDAG->getTargetConstant(Val: SplatValue, DL, |
135 | VT: ViaVecTy.getVectorElementType()); |
136 | Res = CurDAG->getMachineNode(Opcode: Op, dl: DL, VT: ViaVecTy, Op1: Imm); |
137 | ReplaceNode(F: Node, T: Res); |
138 | return; |
139 | } |
140 | break; |
141 | } |
142 | } |
143 | |
144 | // Select the default instruction. |
145 | SelectCode(N: Node); |
146 | } |
147 | |
148 | bool LoongArchDAGToDAGISel::SelectInlineAsmMemoryOperand( |
149 | const SDValue &Op, InlineAsm::ConstraintCode ConstraintID, |
150 | std::vector<SDValue> &OutOps) { |
151 | SDValue Base = Op; |
152 | SDValue Offset = |
153 | CurDAG->getTargetConstant(Val: 0, DL: SDLoc(Op), VT: Subtarget->getGRLenVT()); |
154 | switch (ConstraintID) { |
155 | default: |
156 | llvm_unreachable("unexpected asm memory constraint" ); |
157 | // Reg+Reg addressing. |
158 | case InlineAsm::ConstraintCode::k: |
159 | Base = Op.getOperand(i: 0); |
160 | Offset = Op.getOperand(i: 1); |
161 | break; |
162 | // Reg+simm12 addressing. |
163 | case InlineAsm::ConstraintCode::m: |
164 | if (CurDAG->isBaseWithConstantOffset(Op)) { |
165 | ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Val: Op.getOperand(i: 1)); |
166 | if (isIntN(N: 12, x: CN->getSExtValue())) { |
167 | Base = Op.getOperand(i: 0); |
168 | Offset = CurDAG->getTargetConstant(Val: CN->getZExtValue(), DL: SDLoc(Op), |
169 | VT: Op.getValueType()); |
170 | } |
171 | } |
172 | break; |
173 | // Reg+0 addressing. |
174 | case InlineAsm::ConstraintCode::ZB: |
175 | break; |
176 | // Reg+(simm14<<2) addressing. |
177 | case InlineAsm::ConstraintCode::ZC: |
178 | if (CurDAG->isBaseWithConstantOffset(Op)) { |
179 | ConstantSDNode *CN = dyn_cast<ConstantSDNode>(Val: Op.getOperand(i: 1)); |
180 | if (isIntN(N: 16, x: CN->getSExtValue()) && |
181 | isAligned(Lhs: Align(4ULL), SizeInBytes: CN->getZExtValue())) { |
182 | Base = Op.getOperand(i: 0); |
183 | Offset = CurDAG->getTargetConstant(Val: CN->getZExtValue(), DL: SDLoc(Op), |
184 | VT: Op.getValueType()); |
185 | } |
186 | } |
187 | break; |
188 | } |
189 | OutOps.push_back(x: Base); |
190 | OutOps.push_back(x: Offset); |
191 | return false; |
192 | } |
193 | |
194 | bool LoongArchDAGToDAGISel::SelectBaseAddr(SDValue Addr, SDValue &Base) { |
195 | // If this is FrameIndex, select it directly. Otherwise just let it get |
196 | // selected to a register independently. |
197 | if (auto *FIN = dyn_cast<FrameIndexSDNode>(Val&: Addr)) |
198 | Base = |
199 | CurDAG->getTargetFrameIndex(FI: FIN->getIndex(), VT: Subtarget->getGRLenVT()); |
200 | else |
201 | Base = Addr; |
202 | return true; |
203 | } |
204 | |
205 | // Fold constant addresses. |
206 | bool LoongArchDAGToDAGISel::SelectAddrConstant(SDValue Addr, SDValue &Base, |
207 | SDValue &Offset) { |
208 | SDLoc DL(Addr); |
209 | MVT VT = Addr.getSimpleValueType(); |
210 | |
211 | if (!isa<ConstantSDNode>(Val: Addr)) |
212 | return false; |
213 | |
214 | // If the constant is a simm12, we can fold the whole constant and use R0 as |
215 | // the base. |
216 | int64_t CVal = cast<ConstantSDNode>(Val&: Addr)->getSExtValue(); |
217 | if (!isInt<12>(x: CVal)) |
218 | return false; |
219 | Base = CurDAG->getRegister(Reg: LoongArch::R0, VT); |
220 | Offset = CurDAG->getTargetConstant(Val: SignExtend64<12>(x: CVal), DL, VT); |
221 | return true; |
222 | } |
223 | |
224 | bool LoongArchDAGToDAGISel::selectNonFIBaseAddr(SDValue Addr, SDValue &Base) { |
225 | // If this is FrameIndex, don't select it. |
226 | if (isa<FrameIndexSDNode>(Val: Addr)) |
227 | return false; |
228 | Base = Addr; |
229 | return true; |
230 | } |
231 | |
232 | bool LoongArchDAGToDAGISel::selectShiftMask(SDValue N, unsigned ShiftWidth, |
233 | SDValue &ShAmt) { |
234 | // Shift instructions on LoongArch only read the lower 5 or 6 bits of the |
235 | // shift amount. If there is an AND on the shift amount, we can bypass it if |
236 | // it doesn't affect any of those bits. |
237 | if (N.getOpcode() == ISD::AND && isa<ConstantSDNode>(Val: N.getOperand(i: 1))) { |
238 | const APInt &AndMask = N->getConstantOperandAPInt(Num: 1); |
239 | |
240 | // Since the max shift amount is a power of 2 we can subtract 1 to make a |
241 | // mask that covers the bits needed to represent all shift amounts. |
242 | assert(isPowerOf2_32(ShiftWidth) && "Unexpected max shift amount!" ); |
243 | APInt ShMask(AndMask.getBitWidth(), ShiftWidth - 1); |
244 | |
245 | if (ShMask.isSubsetOf(RHS: AndMask)) { |
246 | ShAmt = N.getOperand(i: 0); |
247 | return true; |
248 | } |
249 | |
250 | // SimplifyDemandedBits may have optimized the mask so try restoring any |
251 | // bits that are known zero. |
252 | KnownBits Known = CurDAG->computeKnownBits(Op: N->getOperand(Num: 0)); |
253 | if (ShMask.isSubsetOf(RHS: AndMask | Known.Zero)) { |
254 | ShAmt = N.getOperand(i: 0); |
255 | return true; |
256 | } |
257 | } else if (N.getOpcode() == LoongArchISD::BSTRPICK) { |
258 | // Similar to the above AND, if there is a BSTRPICK on the shift amount, we |
259 | // can bypass it. |
260 | assert(isPowerOf2_32(ShiftWidth) && "Unexpected max shift amount!" ); |
261 | assert(isa<ConstantSDNode>(N.getOperand(1)) && "Illegal msb operand!" ); |
262 | assert(isa<ConstantSDNode>(N.getOperand(2)) && "Illegal lsb operand!" ); |
263 | uint64_t msb = N.getConstantOperandVal(i: 1), lsb = N.getConstantOperandVal(i: 2); |
264 | if (lsb == 0 && Log2_32(Value: ShiftWidth) <= msb + 1) { |
265 | ShAmt = N.getOperand(i: 0); |
266 | return true; |
267 | } |
268 | } else if (N.getOpcode() == ISD::SUB && |
269 | isa<ConstantSDNode>(Val: N.getOperand(i: 0))) { |
270 | uint64_t Imm = N.getConstantOperandVal(i: 0); |
271 | // If we are shifting by N-X where N == 0 mod Size, then just shift by -X to |
272 | // generate a NEG instead of a SUB of a constant. |
273 | if (Imm != 0 && Imm % ShiftWidth == 0) { |
274 | SDLoc DL(N); |
275 | EVT VT = N.getValueType(); |
276 | SDValue Zero = |
277 | CurDAG->getCopyFromReg(Chain: CurDAG->getEntryNode(), dl: DL, Reg: LoongArch::R0, VT); |
278 | unsigned NegOpc = VT == MVT::i64 ? LoongArch::SUB_D : LoongArch::SUB_W; |
279 | MachineSDNode *Neg = |
280 | CurDAG->getMachineNode(Opcode: NegOpc, dl: DL, VT, Op1: Zero, Op2: N.getOperand(i: 1)); |
281 | ShAmt = SDValue(Neg, 0); |
282 | return true; |
283 | } |
284 | } |
285 | |
286 | ShAmt = N; |
287 | return true; |
288 | } |
289 | |
290 | bool LoongArchDAGToDAGISel::selectSExti32(SDValue N, SDValue &Val) { |
291 | if (N.getOpcode() == ISD::SIGN_EXTEND_INREG && |
292 | cast<VTSDNode>(Val: N.getOperand(i: 1))->getVT() == MVT::i32) { |
293 | Val = N.getOperand(i: 0); |
294 | return true; |
295 | } |
296 | if (N.getOpcode() == LoongArchISD::BSTRPICK && |
297 | N.getConstantOperandVal(i: 1) < UINT64_C(0X1F) && |
298 | N.getConstantOperandVal(i: 2) == UINT64_C(0)) { |
299 | Val = N; |
300 | return true; |
301 | } |
302 | MVT VT = N.getSimpleValueType(); |
303 | if (CurDAG->ComputeNumSignBits(Op: N) > (VT.getSizeInBits() - 32)) { |
304 | Val = N; |
305 | return true; |
306 | } |
307 | |
308 | return false; |
309 | } |
310 | |
311 | bool LoongArchDAGToDAGISel::selectZExti32(SDValue N, SDValue &Val) { |
312 | if (N.getOpcode() == ISD::AND) { |
313 | auto *C = dyn_cast<ConstantSDNode>(Val: N.getOperand(i: 1)); |
314 | if (C && C->getZExtValue() == UINT64_C(0xFFFFFFFF)) { |
315 | Val = N.getOperand(i: 0); |
316 | return true; |
317 | } |
318 | } |
319 | MVT VT = N.getSimpleValueType(); |
320 | APInt Mask = APInt::getHighBitsSet(numBits: VT.getSizeInBits(), hiBitsSet: 32); |
321 | if (CurDAG->MaskedValueIsZero(Op: N, Mask)) { |
322 | Val = N; |
323 | return true; |
324 | } |
325 | |
326 | return false; |
327 | } |
328 | |
329 | bool LoongArchDAGToDAGISel::selectVSplat(SDNode *N, APInt &Imm, |
330 | unsigned MinSizeInBits) const { |
331 | if (!Subtarget->hasExtLSX()) |
332 | return false; |
333 | |
334 | BuildVectorSDNode *Node = dyn_cast<BuildVectorSDNode>(Val: N); |
335 | |
336 | if (!Node) |
337 | return false; |
338 | |
339 | APInt SplatValue, SplatUndef; |
340 | unsigned SplatBitSize; |
341 | bool HasAnyUndefs; |
342 | |
343 | if (!Node->isConstantSplat(SplatValue, SplatUndef, SplatBitSize, HasAnyUndefs, |
344 | MinSplatBits: MinSizeInBits, /*IsBigEndian=*/isBigEndian: false)) |
345 | return false; |
346 | |
347 | Imm = SplatValue; |
348 | |
349 | return true; |
350 | } |
351 | |
352 | template <unsigned ImmBitSize, bool IsSigned> |
353 | bool LoongArchDAGToDAGISel::selectVSplatImm(SDValue N, SDValue &SplatVal) { |
354 | APInt ImmValue; |
355 | EVT EltTy = N->getValueType(ResNo: 0).getVectorElementType(); |
356 | |
357 | if (N->getOpcode() == ISD::BITCAST) |
358 | N = N->getOperand(Num: 0); |
359 | |
360 | if (selectVSplat(N: N.getNode(), Imm&: ImmValue, MinSizeInBits: EltTy.getSizeInBits()) && |
361 | ImmValue.getBitWidth() == EltTy.getSizeInBits()) { |
362 | if (IsSigned && ImmValue.isSignedIntN(N: ImmBitSize)) { |
363 | SplatVal = CurDAG->getTargetConstant(Val: ImmValue.getSExtValue(), DL: SDLoc(N), |
364 | VT: Subtarget->getGRLenVT()); |
365 | return true; |
366 | } |
367 | if (!IsSigned && ImmValue.isIntN(N: ImmBitSize)) { |
368 | SplatVal = CurDAG->getTargetConstant(Val: ImmValue.getZExtValue(), DL: SDLoc(N), |
369 | VT: Subtarget->getGRLenVT()); |
370 | return true; |
371 | } |
372 | } |
373 | |
374 | return false; |
375 | } |
376 | |
377 | bool LoongArchDAGToDAGISel::selectVSplatUimmInvPow2(SDValue N, |
378 | SDValue &SplatImm) const { |
379 | APInt ImmValue; |
380 | EVT EltTy = N->getValueType(ResNo: 0).getVectorElementType(); |
381 | |
382 | if (N->getOpcode() == ISD::BITCAST) |
383 | N = N->getOperand(Num: 0); |
384 | |
385 | if (selectVSplat(N: N.getNode(), Imm&: ImmValue, MinSizeInBits: EltTy.getSizeInBits()) && |
386 | ImmValue.getBitWidth() == EltTy.getSizeInBits()) { |
387 | int32_t Log2 = (~ImmValue).exactLogBase2(); |
388 | |
389 | if (Log2 != -1) { |
390 | SplatImm = CurDAG->getTargetConstant(Val: Log2, DL: SDLoc(N), VT: EltTy); |
391 | return true; |
392 | } |
393 | } |
394 | |
395 | return false; |
396 | } |
397 | |
398 | bool LoongArchDAGToDAGISel::selectVSplatUimmPow2(SDValue N, |
399 | SDValue &SplatImm) const { |
400 | APInt ImmValue; |
401 | EVT EltTy = N->getValueType(ResNo: 0).getVectorElementType(); |
402 | |
403 | if (N->getOpcode() == ISD::BITCAST) |
404 | N = N->getOperand(Num: 0); |
405 | |
406 | if (selectVSplat(N: N.getNode(), Imm&: ImmValue, MinSizeInBits: EltTy.getSizeInBits()) && |
407 | ImmValue.getBitWidth() == EltTy.getSizeInBits()) { |
408 | int32_t Log2 = ImmValue.exactLogBase2(); |
409 | |
410 | if (Log2 != -1) { |
411 | SplatImm = CurDAG->getTargetConstant(Val: Log2, DL: SDLoc(N), VT: EltTy); |
412 | return true; |
413 | } |
414 | } |
415 | |
416 | return false; |
417 | } |
418 | |
419 | // This pass converts a legalized DAG into a LoongArch-specific DAG, ready |
420 | // for instruction scheduling. |
421 | FunctionPass *llvm::createLoongArchISelDag(LoongArchTargetMachine &TM) { |
422 | return new LoongArchDAGToDAGISelLegacy(TM); |
423 | } |
424 | |