1  //=== lib/CodeGen/GlobalISel/AArch64PostLegalizerCombiner.cpp -------------===//
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 performs post-legalization combines on generic MachineInstrs.
10 //
11 // Any combine that this pass performs must preserve instruction legality.
12 // Combines unconcerned with legality should be handled by the
13 // PreLegalizerCombiner instead.
14 //
15 //===----------------------------------------------------------------------===//
16 
17 #include "AArch64TargetMachine.h"
18 #include "llvm/CodeGen/GlobalISel/Combiner.h"
19 #include "llvm/CodeGen/GlobalISel/CombinerHelper.h"
20 #include "llvm/CodeGen/GlobalISel/CombinerInfo.h"
21 #include "llvm/CodeGen/GlobalISel/GISelKnownBits.h"
22 #include "llvm/CodeGen/GlobalISel/MIPatternMatch.h"
23 #include "llvm/CodeGen/GlobalISel/Utils.h"
24 #include "llvm/CodeGen/MachineDominators.h"
25 #include "llvm/CodeGen/MachineFunctionPass.h"
26 #include "llvm/CodeGen/TargetPassConfig.h"
27 #include "llvm/Support/Debug.h"
28 
29 #define DEBUG_TYPE "aarch64-postlegalizer-combiner"
30 
31 using namespace llvm;
32 using namespace MIPatternMatch;
33 
34 /// Represents a pseudo instruction which replaces a G_SHUFFLE_VECTOR.
35 ///
36 /// Used for matching target-supported shuffles before codegen.
37 struct ShuffleVectorPseudo {
38   unsigned Opc; ///< Opcode for the instruction. (E.g. G_ZIP1)
39   Register Dst; ///< Destination register.
40   SmallVector<SrcOp, 2> SrcOps; ///< Source registers.
41   ShuffleVectorPseudo(unsigned Opc, Register Dst,
42                       std::initializer_list<SrcOp> SrcOps)
43       : Opc(Opc), Dst(Dst), SrcOps(SrcOps){};
44   ShuffleVectorPseudo() {}
45 };
46 
47 /// Check if a vector shuffle corresponds to a REV instruction with the
48 /// specified blocksize.
49 static bool isREVMask(ArrayRef<int> M, unsigned EltSize, unsigned NumElts,
50                       unsigned BlockSize) {
51   assert((BlockSize == 16 || BlockSize == 32 || BlockSize == 64) &&
52          "Only possible block sizes for REV are: 16, 32, 64");
53   assert(EltSize != 64 && "EltSize cannot be 64 for REV mask.");
54 
55   unsigned BlockElts = M[0] + 1;
56 
57   // If the first shuffle index is UNDEF, be optimistic.
58   if (M[0] < 0)
59     BlockElts = BlockSize / EltSize;
60 
61   if (BlockSize <= EltSize || BlockSize != BlockElts * EltSize)
62     return false;
63 
64   for (unsigned i = 0; i < NumElts; ++i) {
65     // Ignore undef indices.
66     if (M[i] < 0)
67       continue;
68     if (static_cast<unsigned>(M[i]) !=
69         (i - i % BlockElts) + (BlockElts - 1 - i % BlockElts))
70       return false;
71   }
72 
73   return true;
74 }
75 
76 /// Determines if \p M is a shuffle vector mask for a TRN of \p NumElts.
77 /// Whether or not G_TRN1 or G_TRN2 should be used is stored in \p WhichResult.
78 static bool isTRNMask(ArrayRef<int> M, unsigned NumElts,
79                       unsigned &WhichResult) {
80   if (NumElts % 2 != 0)
81     return false;
82   WhichResult = (M[0] == 0 ? 0 : 1);
83   for (unsigned i = 0; i < NumElts; i += 2) {
84     if ((M[i] >= 0 && static_cast<unsigned>(M[i]) != i + WhichResult) ||
85         (M[i + 1] >= 0 &&
86          static_cast<unsigned>(M[i + 1]) != i + NumElts + WhichResult))
87       return false;
88   }
89   return true;
90 }
91 
92 /// Check if a G_EXT instruction can handle a shuffle mask \p M when the vector
93 /// sources of the shuffle are different.
94 static Optional<std::pair<bool, uint64_t>> getExtMask(ArrayRef<int> M,
95                                                       unsigned NumElts) {
96   // Look for the first non-undef element.
97   auto FirstRealElt = find_if(M, [](int Elt) { return Elt >= 0; });
98   if (FirstRealElt == M.end())
99     return None;
100 
101   // Use APInt to handle overflow when calculating expected element.
102   unsigned MaskBits = APInt(32, NumElts * 2).logBase2();
103   APInt ExpectedElt = APInt(MaskBits, *FirstRealElt + 1);
104 
105   // The following shuffle indices must be the successive elements after the
106   // first real element.
107   if (any_of(
108           make_range(std::next(FirstRealElt), M.end()),
109           [&ExpectedElt](int Elt) { return Elt != ExpectedElt++ && Elt >= 0; }))
110     return None;
111 
112   // The index of an EXT is the first element if it is not UNDEF.
113   // Watch out for the beginning UNDEFs. The EXT index should be the expected
114   // value of the first element.  E.g.
115   // <-1, -1, 3, ...> is treated as <1, 2, 3, ...>.
116   // <-1, -1, 0, 1, ...> is treated as <2*NumElts-2, 2*NumElts-1, 0, 1, ...>.
117   // ExpectedElt is the last mask index plus 1.
118   uint64_t Imm = ExpectedElt.getZExtValue();
119   bool ReverseExt = false;
120 
121   // There are two difference cases requiring to reverse input vectors.
122   // For example, for vector <4 x i32> we have the following cases,
123   // Case 1: shufflevector(<4 x i32>,<4 x i32>,<-1, -1, -1, 0>)
124   // Case 2: shufflevector(<4 x i32>,<4 x i32>,<-1, -1, 7, 0>)
125   // For both cases, we finally use mask <5, 6, 7, 0>, which requires
126   // to reverse two input vectors.
127   if (Imm < NumElts)
128     ReverseExt = true;
129   else
130     Imm -= NumElts;
131   return std::make_pair(ReverseExt, Imm);
132 }
133 
134 /// Determines if \p M is a shuffle vector mask for a UZP of \p NumElts.
135 /// Whether or not G_UZP1 or G_UZP2 should be used is stored in \p WhichResult.
136 static bool isUZPMask(ArrayRef<int> M, unsigned NumElts,
137                       unsigned &WhichResult) {
138   WhichResult = (M[0] == 0 ? 0 : 1);
139   for (unsigned i = 0; i != NumElts; ++i) {
140     // Skip undef indices.
141     if (M[i] < 0)
142       continue;
143     if (static_cast<unsigned>(M[i]) != 2 * i + WhichResult)
144       return false;
145   }
146   return true;
147 }
148 
149 /// \return true if \p M is a zip mask for a shuffle vector of \p NumElts.
150 /// Whether or not G_ZIP1 or G_ZIP2 should be used is stored in \p WhichResult.
151 static bool isZipMask(ArrayRef<int> M, unsigned NumElts,
152                       unsigned &WhichResult) {
153   if (NumElts % 2 != 0)
154     return false;
155 
156   // 0 means use ZIP1, 1 means use ZIP2.
157   WhichResult = (M[0] == 0 ? 0 : 1);
158   unsigned Idx = WhichResult * NumElts / 2;
159   for (unsigned i = 0; i != NumElts; i += 2) {
160       if ((M[i] >= 0 && static_cast<unsigned>(M[i]) != Idx) ||
161           (M[i + 1] >= 0 && static_cast<unsigned>(M[i + 1]) != Idx + NumElts))
162         return false;
163     Idx += 1;
164   }
165   return true;
166 }
167 
168 /// \return true if a G_SHUFFLE_VECTOR instruction \p MI can be replaced with a
169 /// G_REV instruction. Returns the appropriate G_REV opcode in \p Opc.
170 static bool matchREV(MachineInstr &MI, MachineRegisterInfo &MRI,
171                      ShuffleVectorPseudo &MatchInfo) {
172   assert(MI.getOpcode() == TargetOpcode::G_SHUFFLE_VECTOR);
173   ArrayRef<int> ShuffleMask = MI.getOperand(3).getShuffleMask();
174   Register Dst = MI.getOperand(0).getReg();
175   Register Src = MI.getOperand(1).getReg();
176   LLT Ty = MRI.getType(Dst);
177   unsigned EltSize = Ty.getScalarSizeInBits();
178 
179   // Element size for a rev cannot be 64.
180   if (EltSize == 64)
181     return false;
182 
183   unsigned NumElts = Ty.getNumElements();
184 
185   // Try to produce G_REV64
186   if (isREVMask(ShuffleMask, EltSize, NumElts, 64)) {
187     MatchInfo = ShuffleVectorPseudo(AArch64::G_REV64, Dst, {Src});
188     return true;
189   }
190 
191   // TODO: Produce G_REV32 and G_REV16 once we have proper legalization support.
192   // This should be identical to above, but with a constant 32 and constant
193   // 16.
194   return false;
195 }
196 
197 /// \return true if a G_SHUFFLE_VECTOR instruction \p MI can be replaced with
198 /// a G_TRN1 or G_TRN2 instruction.
199 static bool matchTRN(MachineInstr &MI, MachineRegisterInfo &MRI,
200                      ShuffleVectorPseudo &MatchInfo) {
201   assert(MI.getOpcode() == TargetOpcode::G_SHUFFLE_VECTOR);
202   unsigned WhichResult;
203   ArrayRef<int> ShuffleMask = MI.getOperand(3).getShuffleMask();
204   Register Dst = MI.getOperand(0).getReg();
205   unsigned NumElts = MRI.getType(Dst).getNumElements();
206   if (!isTRNMask(ShuffleMask, NumElts, WhichResult))
207     return false;
208   unsigned Opc = (WhichResult == 0) ? AArch64::G_TRN1 : AArch64::G_TRN2;
209   Register V1 = MI.getOperand(1).getReg();
210   Register V2 = MI.getOperand(2).getReg();
211   MatchInfo = ShuffleVectorPseudo(Opc, Dst, {V1, V2});
212   return true;
213 }
214 
215 /// \return true if a G_SHUFFLE_VECTOR instruction \p MI can be replaced with
216 /// a G_UZP1 or G_UZP2 instruction.
217 ///
218 /// \param [in] MI - The shuffle vector instruction.
219 /// \param [out] MatchInfo - Either G_UZP1 or G_UZP2 on success.
220 static bool matchUZP(MachineInstr &MI, MachineRegisterInfo &MRI,
221                      ShuffleVectorPseudo &MatchInfo) {
222   assert(MI.getOpcode() == TargetOpcode::G_SHUFFLE_VECTOR);
223   unsigned WhichResult;
224   ArrayRef<int> ShuffleMask = MI.getOperand(3).getShuffleMask();
225   Register Dst = MI.getOperand(0).getReg();
226   unsigned NumElts = MRI.getType(Dst).getNumElements();
227   if (!isUZPMask(ShuffleMask, NumElts, WhichResult))
228     return false;
229   unsigned Opc = (WhichResult == 0) ? AArch64::G_UZP1 : AArch64::G_UZP2;
230   Register V1 = MI.getOperand(1).getReg();
231   Register V2 = MI.getOperand(2).getReg();
232   MatchInfo = ShuffleVectorPseudo(Opc, Dst, {V1, V2});
233   return true;
234 }
235 
236 static bool matchZip(MachineInstr &MI, MachineRegisterInfo &MRI,
237                      ShuffleVectorPseudo &MatchInfo) {
238   assert(MI.getOpcode() == TargetOpcode::G_SHUFFLE_VECTOR);
239   unsigned WhichResult;
240   ArrayRef<int> ShuffleMask = MI.getOperand(3).getShuffleMask();
241   Register Dst = MI.getOperand(0).getReg();
242   unsigned NumElts = MRI.getType(Dst).getNumElements();
243   if (!isZipMask(ShuffleMask, NumElts, WhichResult))
244     return false;
245   unsigned Opc = (WhichResult == 0) ? AArch64::G_ZIP1 : AArch64::G_ZIP2;
246   Register V1 = MI.getOperand(1).getReg();
247   Register V2 = MI.getOperand(2).getReg();
248   MatchInfo = ShuffleVectorPseudo(Opc, Dst, {V1, V2});
249   return true;
250 }
251 
252 /// Helper function for matchDup.
253 static bool matchDupFromInsertVectorElt(int Lane, MachineInstr &MI,
254                                         MachineRegisterInfo &MRI,
255                                         ShuffleVectorPseudo &MatchInfo) {
256   if (Lane != 0)
257     return false;
258 
259   // Try to match a vector splat operation into a dup instruction.
260   // We're looking for this pattern:
261   //
262   // %scalar:gpr(s64) = COPY $x0
263   // %undef:fpr(<2 x s64>) = G_IMPLICIT_DEF
264   // %cst0:gpr(s32) = G_CONSTANT i32 0
265   // %zerovec:fpr(<2 x s32>) = G_BUILD_VECTOR %cst0(s32), %cst0(s32)
266   // %ins:fpr(<2 x s64>) = G_INSERT_VECTOR_ELT %undef, %scalar(s64), %cst0(s32)
267   // %splat:fpr(<2 x s64>) = G_SHUFFLE_VECTOR %ins(<2 x s64>), %undef, %zerovec(<2 x s32>)
268   //
269   // ...into:
270   // %splat = G_DUP %scalar
271 
272   // Begin matching the insert.
273   auto *InsMI = getOpcodeDef(TargetOpcode::G_INSERT_VECTOR_ELT,
274                              MI.getOperand(1).getReg(), MRI);
275   if (!InsMI)
276     return false;
277   // Match the undef vector operand.
278   if (!getOpcodeDef(TargetOpcode::G_IMPLICIT_DEF, InsMI->getOperand(1).getReg(),
279                     MRI))
280     return false;
281 
282   // Match the index constant 0.
283   int64_t Index = 0;
284   if (!mi_match(InsMI->getOperand(3).getReg(), MRI, m_ICst(Index)) || Index)
285     return false;
286 
287   MatchInfo = ShuffleVectorPseudo(AArch64::G_DUP, MI.getOperand(0).getReg(),
288                                   {InsMI->getOperand(2).getReg()});
289   return true;
290 }
291 
292 /// Helper function for matchDup.
293 static bool matchDupFromBuildVector(int Lane, MachineInstr &MI,
294                                     MachineRegisterInfo &MRI,
295                                     ShuffleVectorPseudo &MatchInfo) {
296   assert(Lane >= 0 && "Expected positive lane?");
297   // Test if the LHS is a BUILD_VECTOR. If it is, then we can just reference the
298   // lane's definition directly.
299   auto *BuildVecMI = getOpcodeDef(TargetOpcode::G_BUILD_VECTOR,
300                                   MI.getOperand(1).getReg(), MRI);
301   if (!BuildVecMI)
302     return false;
303   Register Reg = BuildVecMI->getOperand(Lane + 1).getReg();
304   MatchInfo =
305       ShuffleVectorPseudo(AArch64::G_DUP, MI.getOperand(0).getReg(), {Reg});
306   return true;
307 }
308 
309 static bool matchDup(MachineInstr &MI, MachineRegisterInfo &MRI,
310                      ShuffleVectorPseudo &MatchInfo) {
311   assert(MI.getOpcode() == TargetOpcode::G_SHUFFLE_VECTOR);
312   auto MaybeLane = getSplatIndex(MI);
313   if (!MaybeLane)
314     return false;
315   int Lane = *MaybeLane;
316   // If this is undef splat, generate it via "just" vdup, if possible.
317   if (Lane < 0)
318     Lane = 0;
319   if (matchDupFromInsertVectorElt(Lane, MI, MRI, MatchInfo))
320     return true;
321   if (matchDupFromBuildVector(Lane, MI, MRI, MatchInfo))
322     return true;
323   return false;
324 }
325 
326 static bool matchEXT(MachineInstr &MI, MachineRegisterInfo &MRI,
327                      ShuffleVectorPseudo &MatchInfo) {
328   assert(MI.getOpcode() == TargetOpcode::G_SHUFFLE_VECTOR);
329   Register Dst = MI.getOperand(0).getReg();
330   auto ExtInfo = getExtMask(MI.getOperand(3).getShuffleMask(),
331                             MRI.getType(Dst).getNumElements());
332   if (!ExtInfo)
333     return false;
334   bool ReverseExt;
335   uint64_t Imm;
336   std::tie(ReverseExt, Imm) = *ExtInfo;
337   Register V1 = MI.getOperand(1).getReg();
338   Register V2 = MI.getOperand(2).getReg();
339   if (ReverseExt)
340     std::swap(V1, V2);
341   uint64_t ExtFactor = MRI.getType(V1).getScalarSizeInBits() / 8;
342   Imm *= ExtFactor;
343   MatchInfo = ShuffleVectorPseudo(AArch64::G_EXT, Dst, {V1, V2, Imm});
344   return true;
345 }
346 
347 /// Replace a G_SHUFFLE_VECTOR instruction with a pseudo.
348 /// \p Opc is the opcode to use. \p MI is the G_SHUFFLE_VECTOR.
349 static bool applyShuffleVectorPseudo(MachineInstr &MI,
350                                      ShuffleVectorPseudo &MatchInfo) {
351   MachineIRBuilder MIRBuilder(MI);
352   MIRBuilder.buildInstr(MatchInfo.Opc, {MatchInfo.Dst}, MatchInfo.SrcOps);
353   MI.eraseFromParent();
354   return true;
355 }
356 
357 /// Replace a G_SHUFFLE_VECTOR instruction with G_EXT.
358 /// Special-cased because the constant operand must be emitted as a G_CONSTANT
359 /// for the imported tablegen patterns to work.
360 static bool applyEXT(MachineInstr &MI, ShuffleVectorPseudo &MatchInfo) {
361   MachineIRBuilder MIRBuilder(MI);
362   // Tablegen patterns expect an i32 G_CONSTANT as the final op.
363   auto Cst =
364       MIRBuilder.buildConstant(LLT::scalar(32), MatchInfo.SrcOps[2].getImm());
365   MIRBuilder.buildInstr(MatchInfo.Opc, {MatchInfo.Dst},
366                         {MatchInfo.SrcOps[0], MatchInfo.SrcOps[1], Cst});
367   MI.eraseFromParent();
368   return true;
369 }
370 
371 #define AARCH64POSTLEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_DEPS
372 #include "AArch64GenPostLegalizeGICombiner.inc"
373 #undef AARCH64POSTLEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_DEPS
374 
375 namespace {
376 #define AARCH64POSTLEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_H
377 #include "AArch64GenPostLegalizeGICombiner.inc"
378 #undef AARCH64POSTLEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_H
379 
380 class AArch64PostLegalizerCombinerInfo : public CombinerInfo {
381   GISelKnownBits *KB;
382   MachineDominatorTree *MDT;
383 
384 public:
385   AArch64GenPostLegalizerCombinerHelperRuleConfig GeneratedRuleCfg;
386 
387   AArch64PostLegalizerCombinerInfo(bool EnableOpt, bool OptSize, bool MinSize,
388                                    GISelKnownBits *KB,
389                                    MachineDominatorTree *MDT)
390       : CombinerInfo(/*AllowIllegalOps*/ true, /*ShouldLegalizeIllegal*/ false,
391                      /*LegalizerInfo*/ nullptr, EnableOpt, OptSize, MinSize),
392         KB(KB), MDT(MDT) {
393     if (!GeneratedRuleCfg.parseCommandLineOption())
394       report_fatal_error("Invalid rule identifier");
395   }
396 
397   virtual bool combine(GISelChangeObserver &Observer, MachineInstr &MI,
398                        MachineIRBuilder &B) const override;
399 };
400 
401 bool AArch64PostLegalizerCombinerInfo::combine(GISelChangeObserver &Observer,
402                                                MachineInstr &MI,
403                                                MachineIRBuilder &B) const {
404   const auto *LI =
405       MI.getParent()->getParent()->getSubtarget().getLegalizerInfo();
406   CombinerHelper Helper(Observer, B, KB, MDT, LI);
407   AArch64GenPostLegalizerCombinerHelper Generated(GeneratedRuleCfg);
408   return Generated.tryCombineAll(Observer, MI, B, Helper);
409 }
410 
411 #define AARCH64POSTLEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_CPP
412 #include "AArch64GenPostLegalizeGICombiner.inc"
413 #undef AARCH64POSTLEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_CPP
414 
415 class AArch64PostLegalizerCombiner : public MachineFunctionPass {
416 public:
417   static char ID;
418 
419   AArch64PostLegalizerCombiner(bool IsOptNone = false);
420 
421   StringRef getPassName() const override {
422     return "AArch64PostLegalizerCombiner";
423   }
424 
425   bool runOnMachineFunction(MachineFunction &MF) override;
426   void getAnalysisUsage(AnalysisUsage &AU) const override;
427 
428 private:
429   bool IsOptNone;
430 };
431 } // end anonymous namespace
432 
433 void AArch64PostLegalizerCombiner::getAnalysisUsage(AnalysisUsage &AU) const {
434   AU.addRequired<TargetPassConfig>();
435   AU.setPreservesCFG();
436   getSelectionDAGFallbackAnalysisUsage(AU);
437   AU.addRequired<GISelKnownBitsAnalysis>();
438   AU.addPreserved<GISelKnownBitsAnalysis>();
439   if (!IsOptNone) {
440     AU.addRequired<MachineDominatorTree>();
441     AU.addPreserved<MachineDominatorTree>();
442   }
443   MachineFunctionPass::getAnalysisUsage(AU);
444 }
445 
446 AArch64PostLegalizerCombiner::AArch64PostLegalizerCombiner(bool IsOptNone)
447     : MachineFunctionPass(ID), IsOptNone(IsOptNone) {
448   initializeAArch64PostLegalizerCombinerPass(*PassRegistry::getPassRegistry());
449 }
450 
451 bool AArch64PostLegalizerCombiner::runOnMachineFunction(MachineFunction &MF) {
452   if (MF.getProperties().hasProperty(
453           MachineFunctionProperties::Property::FailedISel))
454     return false;
455   assert(MF.getProperties().hasProperty(
456              MachineFunctionProperties::Property::Legalized) &&
457          "Expected a legalized function?");
458   auto *TPC = &getAnalysis<TargetPassConfig>();
459   const Function &F = MF.getFunction();
460   bool EnableOpt =
461       MF.getTarget().getOptLevel() != CodeGenOpt::None && !skipFunction(F);
462   GISelKnownBits *KB = &getAnalysis<GISelKnownBitsAnalysis>().get(MF);
463   MachineDominatorTree *MDT =
464       IsOptNone ? nullptr : &getAnalysis<MachineDominatorTree>();
465   AArch64PostLegalizerCombinerInfo PCInfo(EnableOpt, F.hasOptSize(),
466                                           F.hasMinSize(), KB, MDT);
467   Combiner C(PCInfo, TPC);
468   return C.combineMachineInstrs(MF, /*CSEInfo*/ nullptr);
469 }
470 
471 char AArch64PostLegalizerCombiner::ID = 0;
472 INITIALIZE_PASS_BEGIN(AArch64PostLegalizerCombiner, DEBUG_TYPE,
473                       "Combine AArch64 MachineInstrs after legalization", false,
474                       false)
475 INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
476 INITIALIZE_PASS_DEPENDENCY(GISelKnownBitsAnalysis)
477 INITIALIZE_PASS_END(AArch64PostLegalizerCombiner, DEBUG_TYPE,
478                     "Combine AArch64 MachineInstrs after legalization", false,
479                     false)
480 
481 namespace llvm {
482 FunctionPass *createAArch64PostLegalizeCombiner(bool IsOptNone) {
483   return new AArch64PostLegalizerCombiner(IsOptNone);
484 }
485 } // end namespace llvm
486