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