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