1*0fca6ea1SDimitry Andric //===- CombinerHelperVectorOps.cpp-----------------------------------------===//
2*0fca6ea1SDimitry Andric //
3*0fca6ea1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0fca6ea1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0fca6ea1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0fca6ea1SDimitry Andric //
7*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===//
8*0fca6ea1SDimitry Andric //
9*0fca6ea1SDimitry Andric // This file implements CombinerHelper for G_EXTRACT_VECTOR_ELT,
10*0fca6ea1SDimitry Andric // G_INSERT_VECTOR_ELT, and G_VSCALE
11*0fca6ea1SDimitry Andric //
12*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===//
13*0fca6ea1SDimitry Andric #include "llvm/CodeGen/GlobalISel/CombinerHelper.h"
14*0fca6ea1SDimitry Andric #include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h"
15*0fca6ea1SDimitry Andric #include "llvm/CodeGen/GlobalISel/LegalizerHelper.h"
16*0fca6ea1SDimitry Andric #include "llvm/CodeGen/GlobalISel/LegalizerInfo.h"
17*0fca6ea1SDimitry Andric #include "llvm/CodeGen/GlobalISel/MIPatternMatch.h"
18*0fca6ea1SDimitry Andric #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
19*0fca6ea1SDimitry Andric #include "llvm/CodeGen/GlobalISel/Utils.h"
20*0fca6ea1SDimitry Andric #include "llvm/CodeGen/LowLevelTypeUtils.h"
21*0fca6ea1SDimitry Andric #include "llvm/CodeGen/MachineOperand.h"
22*0fca6ea1SDimitry Andric #include "llvm/CodeGen/MachineRegisterInfo.h"
23*0fca6ea1SDimitry Andric #include "llvm/CodeGen/TargetLowering.h"
24*0fca6ea1SDimitry Andric #include "llvm/CodeGen/TargetOpcodes.h"
25*0fca6ea1SDimitry Andric #include "llvm/Support/Casting.h"
26*0fca6ea1SDimitry Andric #include <optional>
27*0fca6ea1SDimitry Andric
28*0fca6ea1SDimitry Andric #define DEBUG_TYPE "gi-combiner"
29*0fca6ea1SDimitry Andric
30*0fca6ea1SDimitry Andric using namespace llvm;
31*0fca6ea1SDimitry Andric using namespace MIPatternMatch;
32*0fca6ea1SDimitry Andric
matchExtractVectorElement(MachineInstr & MI,BuildFnTy & MatchInfo)33*0fca6ea1SDimitry Andric bool CombinerHelper::matchExtractVectorElement(MachineInstr &MI,
34*0fca6ea1SDimitry Andric BuildFnTy &MatchInfo) {
35*0fca6ea1SDimitry Andric GExtractVectorElement *Extract = cast<GExtractVectorElement>(&MI);
36*0fca6ea1SDimitry Andric
37*0fca6ea1SDimitry Andric Register Dst = Extract->getReg(0);
38*0fca6ea1SDimitry Andric Register Vector = Extract->getVectorReg();
39*0fca6ea1SDimitry Andric Register Index = Extract->getIndexReg();
40*0fca6ea1SDimitry Andric LLT DstTy = MRI.getType(Dst);
41*0fca6ea1SDimitry Andric LLT VectorTy = MRI.getType(Vector);
42*0fca6ea1SDimitry Andric
43*0fca6ea1SDimitry Andric // The vector register can be def'd by various ops that have vector as its
44*0fca6ea1SDimitry Andric // type. They can all be used for constant folding, scalarizing,
45*0fca6ea1SDimitry Andric // canonicalization, or combining based on symmetry.
46*0fca6ea1SDimitry Andric //
47*0fca6ea1SDimitry Andric // vector like ops
48*0fca6ea1SDimitry Andric // * build vector
49*0fca6ea1SDimitry Andric // * build vector trunc
50*0fca6ea1SDimitry Andric // * shuffle vector
51*0fca6ea1SDimitry Andric // * splat vector
52*0fca6ea1SDimitry Andric // * concat vectors
53*0fca6ea1SDimitry Andric // * insert/extract vector element
54*0fca6ea1SDimitry Andric // * insert/extract subvector
55*0fca6ea1SDimitry Andric // * vector loads
56*0fca6ea1SDimitry Andric // * scalable vector loads
57*0fca6ea1SDimitry Andric //
58*0fca6ea1SDimitry Andric // compute like ops
59*0fca6ea1SDimitry Andric // * binary ops
60*0fca6ea1SDimitry Andric // * unary ops
61*0fca6ea1SDimitry Andric // * exts and truncs
62*0fca6ea1SDimitry Andric // * casts
63*0fca6ea1SDimitry Andric // * fneg
64*0fca6ea1SDimitry Andric // * select
65*0fca6ea1SDimitry Andric // * phis
66*0fca6ea1SDimitry Andric // * cmps
67*0fca6ea1SDimitry Andric // * freeze
68*0fca6ea1SDimitry Andric // * bitcast
69*0fca6ea1SDimitry Andric // * undef
70*0fca6ea1SDimitry Andric
71*0fca6ea1SDimitry Andric // We try to get the value of the Index register.
72*0fca6ea1SDimitry Andric std::optional<ValueAndVReg> MaybeIndex =
73*0fca6ea1SDimitry Andric getIConstantVRegValWithLookThrough(Index, MRI);
74*0fca6ea1SDimitry Andric std::optional<APInt> IndexC = std::nullopt;
75*0fca6ea1SDimitry Andric
76*0fca6ea1SDimitry Andric if (MaybeIndex)
77*0fca6ea1SDimitry Andric IndexC = MaybeIndex->Value;
78*0fca6ea1SDimitry Andric
79*0fca6ea1SDimitry Andric // Fold extractVectorElement(Vector, TOOLARGE) -> undef
80*0fca6ea1SDimitry Andric if (IndexC && VectorTy.isFixedVector() &&
81*0fca6ea1SDimitry Andric IndexC->uge(VectorTy.getNumElements()) &&
82*0fca6ea1SDimitry Andric isLegalOrBeforeLegalizer({TargetOpcode::G_IMPLICIT_DEF, {DstTy}})) {
83*0fca6ea1SDimitry Andric // For fixed-length vectors, it's invalid to extract out-of-range elements.
84*0fca6ea1SDimitry Andric MatchInfo = [=](MachineIRBuilder &B) { B.buildUndef(Dst); };
85*0fca6ea1SDimitry Andric return true;
86*0fca6ea1SDimitry Andric }
87*0fca6ea1SDimitry Andric
88*0fca6ea1SDimitry Andric return false;
89*0fca6ea1SDimitry Andric }
90*0fca6ea1SDimitry Andric
matchExtractVectorElementWithDifferentIndices(const MachineOperand & MO,BuildFnTy & MatchInfo)91*0fca6ea1SDimitry Andric bool CombinerHelper::matchExtractVectorElementWithDifferentIndices(
92*0fca6ea1SDimitry Andric const MachineOperand &MO, BuildFnTy &MatchInfo) {
93*0fca6ea1SDimitry Andric MachineInstr *Root = getDefIgnoringCopies(MO.getReg(), MRI);
94*0fca6ea1SDimitry Andric GExtractVectorElement *Extract = cast<GExtractVectorElement>(Root);
95*0fca6ea1SDimitry Andric
96*0fca6ea1SDimitry Andric //
97*0fca6ea1SDimitry Andric // %idx1:_(s64) = G_CONSTANT i64 1
98*0fca6ea1SDimitry Andric // %idx2:_(s64) = G_CONSTANT i64 2
99*0fca6ea1SDimitry Andric // %insert:_(<2 x s32>) = G_INSERT_VECTOR_ELT_ELT %bv(<2 x s32>),
100*0fca6ea1SDimitry Andric // %value(s32), %idx2(s64) %extract:_(s32) = G_EXTRACT_VECTOR_ELT %insert(<2
101*0fca6ea1SDimitry Andric // x s32>), %idx1(s64)
102*0fca6ea1SDimitry Andric //
103*0fca6ea1SDimitry Andric // -->
104*0fca6ea1SDimitry Andric //
105*0fca6ea1SDimitry Andric // %insert:_(<2 x s32>) = G_INSERT_VECTOR_ELT_ELT %bv(<2 x s32>),
106*0fca6ea1SDimitry Andric // %value(s32), %idx2(s64) %extract:_(s32) = G_EXTRACT_VECTOR_ELT %bv(<2 x
107*0fca6ea1SDimitry Andric // s32>), %idx1(s64)
108*0fca6ea1SDimitry Andric //
109*0fca6ea1SDimitry Andric //
110*0fca6ea1SDimitry Andric
111*0fca6ea1SDimitry Andric Register Index = Extract->getIndexReg();
112*0fca6ea1SDimitry Andric
113*0fca6ea1SDimitry Andric // We try to get the value of the Index register.
114*0fca6ea1SDimitry Andric std::optional<ValueAndVReg> MaybeIndex =
115*0fca6ea1SDimitry Andric getIConstantVRegValWithLookThrough(Index, MRI);
116*0fca6ea1SDimitry Andric std::optional<APInt> IndexC = std::nullopt;
117*0fca6ea1SDimitry Andric
118*0fca6ea1SDimitry Andric if (!MaybeIndex)
119*0fca6ea1SDimitry Andric return false;
120*0fca6ea1SDimitry Andric else
121*0fca6ea1SDimitry Andric IndexC = MaybeIndex->Value;
122*0fca6ea1SDimitry Andric
123*0fca6ea1SDimitry Andric Register Vector = Extract->getVectorReg();
124*0fca6ea1SDimitry Andric
125*0fca6ea1SDimitry Andric GInsertVectorElement *Insert =
126*0fca6ea1SDimitry Andric getOpcodeDef<GInsertVectorElement>(Vector, MRI);
127*0fca6ea1SDimitry Andric if (!Insert)
128*0fca6ea1SDimitry Andric return false;
129*0fca6ea1SDimitry Andric
130*0fca6ea1SDimitry Andric Register Dst = Extract->getReg(0);
131*0fca6ea1SDimitry Andric
132*0fca6ea1SDimitry Andric std::optional<ValueAndVReg> MaybeInsertIndex =
133*0fca6ea1SDimitry Andric getIConstantVRegValWithLookThrough(Insert->getIndexReg(), MRI);
134*0fca6ea1SDimitry Andric
135*0fca6ea1SDimitry Andric if (MaybeInsertIndex && MaybeInsertIndex->Value != *IndexC) {
136*0fca6ea1SDimitry Andric // There is no one-use check. We have to keep the insert. When both Index
137*0fca6ea1SDimitry Andric // registers are constants and not equal, we can look into the Vector
138*0fca6ea1SDimitry Andric // register of the insert.
139*0fca6ea1SDimitry Andric MatchInfo = [=](MachineIRBuilder &B) {
140*0fca6ea1SDimitry Andric B.buildExtractVectorElement(Dst, Insert->getVectorReg(), Index);
141*0fca6ea1SDimitry Andric };
142*0fca6ea1SDimitry Andric return true;
143*0fca6ea1SDimitry Andric }
144*0fca6ea1SDimitry Andric
145*0fca6ea1SDimitry Andric return false;
146*0fca6ea1SDimitry Andric }
147*0fca6ea1SDimitry Andric
matchExtractVectorElementWithBuildVector(const MachineOperand & MO,BuildFnTy & MatchInfo)148*0fca6ea1SDimitry Andric bool CombinerHelper::matchExtractVectorElementWithBuildVector(
149*0fca6ea1SDimitry Andric const MachineOperand &MO, BuildFnTy &MatchInfo) {
150*0fca6ea1SDimitry Andric MachineInstr *Root = getDefIgnoringCopies(MO.getReg(), MRI);
151*0fca6ea1SDimitry Andric GExtractVectorElement *Extract = cast<GExtractVectorElement>(Root);
152*0fca6ea1SDimitry Andric
153*0fca6ea1SDimitry Andric //
154*0fca6ea1SDimitry Andric // %zero:_(s64) = G_CONSTANT i64 0
155*0fca6ea1SDimitry Andric // %bv:_(<2 x s32>) = G_BUILD_VECTOR %arg1(s32), %arg2(s32)
156*0fca6ea1SDimitry Andric // %extract:_(s32) = G_EXTRACT_VECTOR_ELT %bv(<2 x s32>), %zero(s64)
157*0fca6ea1SDimitry Andric //
158*0fca6ea1SDimitry Andric // -->
159*0fca6ea1SDimitry Andric //
160*0fca6ea1SDimitry Andric // %extract:_(32) = COPY %arg1(s32)
161*0fca6ea1SDimitry Andric //
162*0fca6ea1SDimitry Andric //
163*0fca6ea1SDimitry Andric //
164*0fca6ea1SDimitry Andric // %bv:_(<2 x s32>) = G_BUILD_VECTOR %arg1(s32), %arg2(s32)
165*0fca6ea1SDimitry Andric // %extract:_(s32) = G_EXTRACT_VECTOR_ELT %bv(<2 x s32>), %opaque(s64)
166*0fca6ea1SDimitry Andric //
167*0fca6ea1SDimitry Andric // -->
168*0fca6ea1SDimitry Andric //
169*0fca6ea1SDimitry Andric // %bv:_(<2 x s32>) = G_BUILD_VECTOR %arg1(s32), %arg2(s32)
170*0fca6ea1SDimitry Andric // %extract:_(s32) = G_EXTRACT_VECTOR_ELT %bv(<2 x s32>), %opaque(s64)
171*0fca6ea1SDimitry Andric //
172*0fca6ea1SDimitry Andric
173*0fca6ea1SDimitry Andric Register Vector = Extract->getVectorReg();
174*0fca6ea1SDimitry Andric
175*0fca6ea1SDimitry Andric // We expect a buildVector on the Vector register.
176*0fca6ea1SDimitry Andric GBuildVector *Build = getOpcodeDef<GBuildVector>(Vector, MRI);
177*0fca6ea1SDimitry Andric if (!Build)
178*0fca6ea1SDimitry Andric return false;
179*0fca6ea1SDimitry Andric
180*0fca6ea1SDimitry Andric LLT VectorTy = MRI.getType(Vector);
181*0fca6ea1SDimitry Andric
182*0fca6ea1SDimitry Andric // There is a one-use check. There are more combines on build vectors.
183*0fca6ea1SDimitry Andric EVT Ty(getMVTForLLT(VectorTy));
184*0fca6ea1SDimitry Andric if (!MRI.hasOneNonDBGUse(Build->getReg(0)) ||
185*0fca6ea1SDimitry Andric !getTargetLowering().aggressivelyPreferBuildVectorSources(Ty))
186*0fca6ea1SDimitry Andric return false;
187*0fca6ea1SDimitry Andric
188*0fca6ea1SDimitry Andric Register Index = Extract->getIndexReg();
189*0fca6ea1SDimitry Andric
190*0fca6ea1SDimitry Andric // If the Index is constant, then we can extract the element from the given
191*0fca6ea1SDimitry Andric // offset.
192*0fca6ea1SDimitry Andric std::optional<ValueAndVReg> MaybeIndex =
193*0fca6ea1SDimitry Andric getIConstantVRegValWithLookThrough(Index, MRI);
194*0fca6ea1SDimitry Andric if (!MaybeIndex)
195*0fca6ea1SDimitry Andric return false;
196*0fca6ea1SDimitry Andric
197*0fca6ea1SDimitry Andric // We now know that there is a buildVector def'd on the Vector register and
198*0fca6ea1SDimitry Andric // the index is const. The combine will succeed.
199*0fca6ea1SDimitry Andric
200*0fca6ea1SDimitry Andric Register Dst = Extract->getReg(0);
201*0fca6ea1SDimitry Andric
202*0fca6ea1SDimitry Andric MatchInfo = [=](MachineIRBuilder &B) {
203*0fca6ea1SDimitry Andric B.buildCopy(Dst, Build->getSourceReg(MaybeIndex->Value.getZExtValue()));
204*0fca6ea1SDimitry Andric };
205*0fca6ea1SDimitry Andric
206*0fca6ea1SDimitry Andric return true;
207*0fca6ea1SDimitry Andric }
208*0fca6ea1SDimitry Andric
matchExtractVectorElementWithBuildVectorTrunc(const MachineOperand & MO,BuildFnTy & MatchInfo)209*0fca6ea1SDimitry Andric bool CombinerHelper::matchExtractVectorElementWithBuildVectorTrunc(
210*0fca6ea1SDimitry Andric const MachineOperand &MO, BuildFnTy &MatchInfo) {
211*0fca6ea1SDimitry Andric MachineInstr *Root = getDefIgnoringCopies(MO.getReg(), MRI);
212*0fca6ea1SDimitry Andric GExtractVectorElement *Extract = cast<GExtractVectorElement>(Root);
213*0fca6ea1SDimitry Andric
214*0fca6ea1SDimitry Andric //
215*0fca6ea1SDimitry Andric // %zero:_(s64) = G_CONSTANT i64 0
216*0fca6ea1SDimitry Andric // %bv:_(<2 x s32>) = G_BUILD_VECTOR_TRUNC %arg1(s64), %arg2(s64)
217*0fca6ea1SDimitry Andric // %extract:_(s32) = G_EXTRACT_VECTOR_ELT %bv(<2 x s32>), %zero(s64)
218*0fca6ea1SDimitry Andric //
219*0fca6ea1SDimitry Andric // -->
220*0fca6ea1SDimitry Andric //
221*0fca6ea1SDimitry Andric // %extract:_(32) = G_TRUNC %arg1(s64)
222*0fca6ea1SDimitry Andric //
223*0fca6ea1SDimitry Andric //
224*0fca6ea1SDimitry Andric //
225*0fca6ea1SDimitry Andric // %bv:_(<2 x s32>) = G_BUILD_VECTOR_TRUNC %arg1(s64), %arg2(s64)
226*0fca6ea1SDimitry Andric // %extract:_(s32) = G_EXTRACT_VECTOR_ELT %bv(<2 x s32>), %opaque(s64)
227*0fca6ea1SDimitry Andric //
228*0fca6ea1SDimitry Andric // -->
229*0fca6ea1SDimitry Andric //
230*0fca6ea1SDimitry Andric // %bv:_(<2 x s32>) = G_BUILD_VECTOR_TRUNC %arg1(s64), %arg2(s64)
231*0fca6ea1SDimitry Andric // %extract:_(s32) = G_EXTRACT_VECTOR_ELT %bv(<2 x s32>), %opaque(s64)
232*0fca6ea1SDimitry Andric //
233*0fca6ea1SDimitry Andric
234*0fca6ea1SDimitry Andric Register Vector = Extract->getVectorReg();
235*0fca6ea1SDimitry Andric
236*0fca6ea1SDimitry Andric // We expect a buildVectorTrunc on the Vector register.
237*0fca6ea1SDimitry Andric GBuildVectorTrunc *Build = getOpcodeDef<GBuildVectorTrunc>(Vector, MRI);
238*0fca6ea1SDimitry Andric if (!Build)
239*0fca6ea1SDimitry Andric return false;
240*0fca6ea1SDimitry Andric
241*0fca6ea1SDimitry Andric LLT VectorTy = MRI.getType(Vector);
242*0fca6ea1SDimitry Andric
243*0fca6ea1SDimitry Andric // There is a one-use check. There are more combines on build vectors.
244*0fca6ea1SDimitry Andric EVT Ty(getMVTForLLT(VectorTy));
245*0fca6ea1SDimitry Andric if (!MRI.hasOneNonDBGUse(Build->getReg(0)) ||
246*0fca6ea1SDimitry Andric !getTargetLowering().aggressivelyPreferBuildVectorSources(Ty))
247*0fca6ea1SDimitry Andric return false;
248*0fca6ea1SDimitry Andric
249*0fca6ea1SDimitry Andric Register Index = Extract->getIndexReg();
250*0fca6ea1SDimitry Andric
251*0fca6ea1SDimitry Andric // If the Index is constant, then we can extract the element from the given
252*0fca6ea1SDimitry Andric // offset.
253*0fca6ea1SDimitry Andric std::optional<ValueAndVReg> MaybeIndex =
254*0fca6ea1SDimitry Andric getIConstantVRegValWithLookThrough(Index, MRI);
255*0fca6ea1SDimitry Andric if (!MaybeIndex)
256*0fca6ea1SDimitry Andric return false;
257*0fca6ea1SDimitry Andric
258*0fca6ea1SDimitry Andric // We now know that there is a buildVectorTrunc def'd on the Vector register
259*0fca6ea1SDimitry Andric // and the index is const. The combine will succeed.
260*0fca6ea1SDimitry Andric
261*0fca6ea1SDimitry Andric Register Dst = Extract->getReg(0);
262*0fca6ea1SDimitry Andric LLT DstTy = MRI.getType(Dst);
263*0fca6ea1SDimitry Andric LLT SrcTy = MRI.getType(Build->getSourceReg(0));
264*0fca6ea1SDimitry Andric
265*0fca6ea1SDimitry Andric // For buildVectorTrunc, the inputs are truncated.
266*0fca6ea1SDimitry Andric if (!isLegalOrBeforeLegalizer({TargetOpcode::G_TRUNC, {DstTy, SrcTy}}))
267*0fca6ea1SDimitry Andric return false;
268*0fca6ea1SDimitry Andric
269*0fca6ea1SDimitry Andric MatchInfo = [=](MachineIRBuilder &B) {
270*0fca6ea1SDimitry Andric B.buildTrunc(Dst, Build->getSourceReg(MaybeIndex->Value.getZExtValue()));
271*0fca6ea1SDimitry Andric };
272*0fca6ea1SDimitry Andric
273*0fca6ea1SDimitry Andric return true;
274*0fca6ea1SDimitry Andric }
275*0fca6ea1SDimitry Andric
matchExtractVectorElementWithShuffleVector(const MachineOperand & MO,BuildFnTy & MatchInfo)276*0fca6ea1SDimitry Andric bool CombinerHelper::matchExtractVectorElementWithShuffleVector(
277*0fca6ea1SDimitry Andric const MachineOperand &MO, BuildFnTy &MatchInfo) {
278*0fca6ea1SDimitry Andric GExtractVectorElement *Extract =
279*0fca6ea1SDimitry Andric cast<GExtractVectorElement>(getDefIgnoringCopies(MO.getReg(), MRI));
280*0fca6ea1SDimitry Andric
281*0fca6ea1SDimitry Andric //
282*0fca6ea1SDimitry Andric // %zero:_(s64) = G_CONSTANT i64 0
283*0fca6ea1SDimitry Andric // %sv:_(<4 x s32>) = G_SHUFFLE_SHUFFLE %arg1(<4 x s32>), %arg2(<4 x s32>),
284*0fca6ea1SDimitry Andric // shufflemask(0, 0, 0, 0)
285*0fca6ea1SDimitry Andric // %extract:_(s32) = G_EXTRACT_VECTOR_ELT %sv(<4 x s32>), %zero(s64)
286*0fca6ea1SDimitry Andric //
287*0fca6ea1SDimitry Andric // -->
288*0fca6ea1SDimitry Andric //
289*0fca6ea1SDimitry Andric // %zero1:_(s64) = G_CONSTANT i64 0
290*0fca6ea1SDimitry Andric // %extract:_(s32) = G_EXTRACT_VECTOR_ELT %arg1(<4 x s32>), %zero1(s64)
291*0fca6ea1SDimitry Andric //
292*0fca6ea1SDimitry Andric //
293*0fca6ea1SDimitry Andric //
294*0fca6ea1SDimitry Andric //
295*0fca6ea1SDimitry Andric // %three:_(s64) = G_CONSTANT i64 3
296*0fca6ea1SDimitry Andric // %sv:_(<4 x s32>) = G_SHUFFLE_SHUFFLE %arg1(<4 x s32>), %arg2(<4 x s32>),
297*0fca6ea1SDimitry Andric // shufflemask(0, 0, 0, -1)
298*0fca6ea1SDimitry Andric // %extract:_(s32) = G_EXTRACT_VECTOR_ELT %sv(<4 x s32>), %three(s64)
299*0fca6ea1SDimitry Andric //
300*0fca6ea1SDimitry Andric // -->
301*0fca6ea1SDimitry Andric //
302*0fca6ea1SDimitry Andric // %extract:_(s32) = G_IMPLICIT_DEF
303*0fca6ea1SDimitry Andric //
304*0fca6ea1SDimitry Andric //
305*0fca6ea1SDimitry Andric //
306*0fca6ea1SDimitry Andric //
307*0fca6ea1SDimitry Andric //
308*0fca6ea1SDimitry Andric // %sv:_(<4 x s32>) = G_SHUFFLE_SHUFFLE %arg1(<4 x s32>), %arg2(<4 x s32>),
309*0fca6ea1SDimitry Andric // shufflemask(0, 0, 0, -1)
310*0fca6ea1SDimitry Andric // %extract:_(s32) = G_EXTRACT_VECTOR_ELT %sv(<4 x s32>), %opaque(s64)
311*0fca6ea1SDimitry Andric //
312*0fca6ea1SDimitry Andric // -->
313*0fca6ea1SDimitry Andric //
314*0fca6ea1SDimitry Andric // %sv:_(<4 x s32>) = G_SHUFFLE_SHUFFLE %arg1(<4 x s32>), %arg2(<4 x s32>),
315*0fca6ea1SDimitry Andric // shufflemask(0, 0, 0, -1)
316*0fca6ea1SDimitry Andric // %extract:_(s32) = G_EXTRACT_VECTOR_ELT %sv(<4 x s32>), %opaque(s64)
317*0fca6ea1SDimitry Andric //
318*0fca6ea1SDimitry Andric
319*0fca6ea1SDimitry Andric // We try to get the value of the Index register.
320*0fca6ea1SDimitry Andric std::optional<ValueAndVReg> MaybeIndex =
321*0fca6ea1SDimitry Andric getIConstantVRegValWithLookThrough(Extract->getIndexReg(), MRI);
322*0fca6ea1SDimitry Andric if (!MaybeIndex)
323*0fca6ea1SDimitry Andric return false;
324*0fca6ea1SDimitry Andric
325*0fca6ea1SDimitry Andric GShuffleVector *Shuffle =
326*0fca6ea1SDimitry Andric cast<GShuffleVector>(getDefIgnoringCopies(Extract->getVectorReg(), MRI));
327*0fca6ea1SDimitry Andric
328*0fca6ea1SDimitry Andric ArrayRef<int> Mask = Shuffle->getMask();
329*0fca6ea1SDimitry Andric
330*0fca6ea1SDimitry Andric unsigned Offset = MaybeIndex->Value.getZExtValue();
331*0fca6ea1SDimitry Andric int SrcIdx = Mask[Offset];
332*0fca6ea1SDimitry Andric
333*0fca6ea1SDimitry Andric LLT Src1Type = MRI.getType(Shuffle->getSrc1Reg());
334*0fca6ea1SDimitry Andric // At the IR level a <1 x ty> shuffle vector is valid, but we want to extract
335*0fca6ea1SDimitry Andric // from a vector.
336*0fca6ea1SDimitry Andric assert(Src1Type.isVector() && "expected to extract from a vector");
337*0fca6ea1SDimitry Andric unsigned LHSWidth = Src1Type.isVector() ? Src1Type.getNumElements() : 1;
338*0fca6ea1SDimitry Andric
339*0fca6ea1SDimitry Andric // Note that there is no one use check.
340*0fca6ea1SDimitry Andric Register Dst = Extract->getReg(0);
341*0fca6ea1SDimitry Andric LLT DstTy = MRI.getType(Dst);
342*0fca6ea1SDimitry Andric
343*0fca6ea1SDimitry Andric if (SrcIdx < 0 &&
344*0fca6ea1SDimitry Andric isLegalOrBeforeLegalizer({TargetOpcode::G_IMPLICIT_DEF, {DstTy}})) {
345*0fca6ea1SDimitry Andric MatchInfo = [=](MachineIRBuilder &B) { B.buildUndef(Dst); };
346*0fca6ea1SDimitry Andric return true;
347*0fca6ea1SDimitry Andric }
348*0fca6ea1SDimitry Andric
349*0fca6ea1SDimitry Andric // If the legality check failed, then we still have to abort.
350*0fca6ea1SDimitry Andric if (SrcIdx < 0)
351*0fca6ea1SDimitry Andric return false;
352*0fca6ea1SDimitry Andric
353*0fca6ea1SDimitry Andric Register NewVector;
354*0fca6ea1SDimitry Andric
355*0fca6ea1SDimitry Andric // We check in which vector and at what offset to look through.
356*0fca6ea1SDimitry Andric if (SrcIdx < (int)LHSWidth) {
357*0fca6ea1SDimitry Andric NewVector = Shuffle->getSrc1Reg();
358*0fca6ea1SDimitry Andric // SrcIdx unchanged
359*0fca6ea1SDimitry Andric } else { // SrcIdx >= LHSWidth
360*0fca6ea1SDimitry Andric NewVector = Shuffle->getSrc2Reg();
361*0fca6ea1SDimitry Andric SrcIdx -= LHSWidth;
362*0fca6ea1SDimitry Andric }
363*0fca6ea1SDimitry Andric
364*0fca6ea1SDimitry Andric LLT IdxTy = MRI.getType(Extract->getIndexReg());
365*0fca6ea1SDimitry Andric LLT NewVectorTy = MRI.getType(NewVector);
366*0fca6ea1SDimitry Andric
367*0fca6ea1SDimitry Andric // We check the legality of the look through.
368*0fca6ea1SDimitry Andric if (!isLegalOrBeforeLegalizer(
369*0fca6ea1SDimitry Andric {TargetOpcode::G_EXTRACT_VECTOR_ELT, {DstTy, NewVectorTy, IdxTy}}) ||
370*0fca6ea1SDimitry Andric !isConstantLegalOrBeforeLegalizer({IdxTy}))
371*0fca6ea1SDimitry Andric return false;
372*0fca6ea1SDimitry Andric
373*0fca6ea1SDimitry Andric // We look through the shuffle vector.
374*0fca6ea1SDimitry Andric MatchInfo = [=](MachineIRBuilder &B) {
375*0fca6ea1SDimitry Andric auto Idx = B.buildConstant(IdxTy, SrcIdx);
376*0fca6ea1SDimitry Andric B.buildExtractVectorElement(Dst, NewVector, Idx);
377*0fca6ea1SDimitry Andric };
378*0fca6ea1SDimitry Andric
379*0fca6ea1SDimitry Andric return true;
380*0fca6ea1SDimitry Andric }
381*0fca6ea1SDimitry Andric
matchInsertVectorElementOOB(MachineInstr & MI,BuildFnTy & MatchInfo)382*0fca6ea1SDimitry Andric bool CombinerHelper::matchInsertVectorElementOOB(MachineInstr &MI,
383*0fca6ea1SDimitry Andric BuildFnTy &MatchInfo) {
384*0fca6ea1SDimitry Andric GInsertVectorElement *Insert = cast<GInsertVectorElement>(&MI);
385*0fca6ea1SDimitry Andric
386*0fca6ea1SDimitry Andric Register Dst = Insert->getReg(0);
387*0fca6ea1SDimitry Andric LLT DstTy = MRI.getType(Dst);
388*0fca6ea1SDimitry Andric Register Index = Insert->getIndexReg();
389*0fca6ea1SDimitry Andric
390*0fca6ea1SDimitry Andric if (!DstTy.isFixedVector())
391*0fca6ea1SDimitry Andric return false;
392*0fca6ea1SDimitry Andric
393*0fca6ea1SDimitry Andric std::optional<ValueAndVReg> MaybeIndex =
394*0fca6ea1SDimitry Andric getIConstantVRegValWithLookThrough(Index, MRI);
395*0fca6ea1SDimitry Andric
396*0fca6ea1SDimitry Andric if (MaybeIndex && MaybeIndex->Value.uge(DstTy.getNumElements()) &&
397*0fca6ea1SDimitry Andric isLegalOrBeforeLegalizer({TargetOpcode::G_IMPLICIT_DEF, {DstTy}})) {
398*0fca6ea1SDimitry Andric MatchInfo = [=](MachineIRBuilder &B) { B.buildUndef(Dst); };
399*0fca6ea1SDimitry Andric return true;
400*0fca6ea1SDimitry Andric }
401*0fca6ea1SDimitry Andric
402*0fca6ea1SDimitry Andric return false;
403*0fca6ea1SDimitry Andric }
404*0fca6ea1SDimitry Andric
matchAddOfVScale(const MachineOperand & MO,BuildFnTy & MatchInfo)405*0fca6ea1SDimitry Andric bool CombinerHelper::matchAddOfVScale(const MachineOperand &MO,
406*0fca6ea1SDimitry Andric BuildFnTy &MatchInfo) {
407*0fca6ea1SDimitry Andric GAdd *Add = cast<GAdd>(MRI.getVRegDef(MO.getReg()));
408*0fca6ea1SDimitry Andric GVScale *LHSVScale = cast<GVScale>(MRI.getVRegDef(Add->getLHSReg()));
409*0fca6ea1SDimitry Andric GVScale *RHSVScale = cast<GVScale>(MRI.getVRegDef(Add->getRHSReg()));
410*0fca6ea1SDimitry Andric
411*0fca6ea1SDimitry Andric Register Dst = Add->getReg(0);
412*0fca6ea1SDimitry Andric
413*0fca6ea1SDimitry Andric if (!MRI.hasOneNonDBGUse(LHSVScale->getReg(0)) ||
414*0fca6ea1SDimitry Andric !MRI.hasOneNonDBGUse(RHSVScale->getReg(0)))
415*0fca6ea1SDimitry Andric return false;
416*0fca6ea1SDimitry Andric
417*0fca6ea1SDimitry Andric MatchInfo = [=](MachineIRBuilder &B) {
418*0fca6ea1SDimitry Andric B.buildVScale(Dst, LHSVScale->getSrc() + RHSVScale->getSrc());
419*0fca6ea1SDimitry Andric };
420*0fca6ea1SDimitry Andric
421*0fca6ea1SDimitry Andric return true;
422*0fca6ea1SDimitry Andric }
423*0fca6ea1SDimitry Andric
matchMulOfVScale(const MachineOperand & MO,BuildFnTy & MatchInfo)424*0fca6ea1SDimitry Andric bool CombinerHelper::matchMulOfVScale(const MachineOperand &MO,
425*0fca6ea1SDimitry Andric BuildFnTy &MatchInfo) {
426*0fca6ea1SDimitry Andric GMul *Mul = cast<GMul>(MRI.getVRegDef(MO.getReg()));
427*0fca6ea1SDimitry Andric GVScale *LHSVScale = cast<GVScale>(MRI.getVRegDef(Mul->getLHSReg()));
428*0fca6ea1SDimitry Andric
429*0fca6ea1SDimitry Andric std::optional<APInt> MaybeRHS = getIConstantVRegVal(Mul->getRHSReg(), MRI);
430*0fca6ea1SDimitry Andric if (!MaybeRHS)
431*0fca6ea1SDimitry Andric return false;
432*0fca6ea1SDimitry Andric
433*0fca6ea1SDimitry Andric Register Dst = MO.getReg();
434*0fca6ea1SDimitry Andric
435*0fca6ea1SDimitry Andric if (!MRI.hasOneNonDBGUse(LHSVScale->getReg(0)))
436*0fca6ea1SDimitry Andric return false;
437*0fca6ea1SDimitry Andric
438*0fca6ea1SDimitry Andric MatchInfo = [=](MachineIRBuilder &B) {
439*0fca6ea1SDimitry Andric B.buildVScale(Dst, LHSVScale->getSrc() * *MaybeRHS);
440*0fca6ea1SDimitry Andric };
441*0fca6ea1SDimitry Andric
442*0fca6ea1SDimitry Andric return true;
443*0fca6ea1SDimitry Andric }
444*0fca6ea1SDimitry Andric
matchSubOfVScale(const MachineOperand & MO,BuildFnTy & MatchInfo)445*0fca6ea1SDimitry Andric bool CombinerHelper::matchSubOfVScale(const MachineOperand &MO,
446*0fca6ea1SDimitry Andric BuildFnTy &MatchInfo) {
447*0fca6ea1SDimitry Andric GSub *Sub = cast<GSub>(MRI.getVRegDef(MO.getReg()));
448*0fca6ea1SDimitry Andric GVScale *RHSVScale = cast<GVScale>(MRI.getVRegDef(Sub->getRHSReg()));
449*0fca6ea1SDimitry Andric
450*0fca6ea1SDimitry Andric Register Dst = MO.getReg();
451*0fca6ea1SDimitry Andric LLT DstTy = MRI.getType(Dst);
452*0fca6ea1SDimitry Andric
453*0fca6ea1SDimitry Andric if (!MRI.hasOneNonDBGUse(RHSVScale->getReg(0)) ||
454*0fca6ea1SDimitry Andric !isLegalOrBeforeLegalizer({TargetOpcode::G_ADD, DstTy}))
455*0fca6ea1SDimitry Andric return false;
456*0fca6ea1SDimitry Andric
457*0fca6ea1SDimitry Andric MatchInfo = [=](MachineIRBuilder &B) {
458*0fca6ea1SDimitry Andric auto VScale = B.buildVScale(DstTy, -RHSVScale->getSrc());
459*0fca6ea1SDimitry Andric B.buildAdd(Dst, Sub->getLHSReg(), VScale, Sub->getFlags());
460*0fca6ea1SDimitry Andric };
461*0fca6ea1SDimitry Andric
462*0fca6ea1SDimitry Andric return true;
463*0fca6ea1SDimitry Andric }
464*0fca6ea1SDimitry Andric
matchShlOfVScale(const MachineOperand & MO,BuildFnTy & MatchInfo)465*0fca6ea1SDimitry Andric bool CombinerHelper::matchShlOfVScale(const MachineOperand &MO,
466*0fca6ea1SDimitry Andric BuildFnTy &MatchInfo) {
467*0fca6ea1SDimitry Andric GShl *Shl = cast<GShl>(MRI.getVRegDef(MO.getReg()));
468*0fca6ea1SDimitry Andric GVScale *LHSVScale = cast<GVScale>(MRI.getVRegDef(Shl->getSrcReg()));
469*0fca6ea1SDimitry Andric
470*0fca6ea1SDimitry Andric std::optional<APInt> MaybeRHS = getIConstantVRegVal(Shl->getShiftReg(), MRI);
471*0fca6ea1SDimitry Andric if (!MaybeRHS)
472*0fca6ea1SDimitry Andric return false;
473*0fca6ea1SDimitry Andric
474*0fca6ea1SDimitry Andric Register Dst = MO.getReg();
475*0fca6ea1SDimitry Andric LLT DstTy = MRI.getType(Dst);
476*0fca6ea1SDimitry Andric
477*0fca6ea1SDimitry Andric if (!MRI.hasOneNonDBGUse(LHSVScale->getReg(0)) ||
478*0fca6ea1SDimitry Andric !isLegalOrBeforeLegalizer({TargetOpcode::G_VSCALE, DstTy}))
479*0fca6ea1SDimitry Andric return false;
480*0fca6ea1SDimitry Andric
481*0fca6ea1SDimitry Andric MatchInfo = [=](MachineIRBuilder &B) {
482*0fca6ea1SDimitry Andric B.buildVScale(Dst, LHSVScale->getSrc().shl(*MaybeRHS));
483*0fca6ea1SDimitry Andric };
484*0fca6ea1SDimitry Andric
485*0fca6ea1SDimitry Andric return true;
486*0fca6ea1SDimitry Andric }
487