1 //===- MVEGatherScatterLowering.cpp - Gather/Scatter lowering -------------===//
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 pass custom lowers llvm.gather and llvm.scatter instructions to
10 /// arm.mve.gather and arm.mve.scatter intrinsics, optimising the code to
11 /// produce a better final result as we go.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "ARM.h"
16 #include "ARMBaseInstrInfo.h"
17 #include "ARMSubtarget.h"
18 #include "llvm/Analysis/LoopInfo.h"
19 #include "llvm/Analysis/TargetTransformInfo.h"
20 #include "llvm/CodeGen/TargetLowering.h"
21 #include "llvm/CodeGen/TargetPassConfig.h"
22 #include "llvm/CodeGen/TargetSubtargetInfo.h"
23 #include "llvm/IR/BasicBlock.h"
24 #include "llvm/IR/Constant.h"
25 #include "llvm/IR/Constants.h"
26 #include "llvm/IR/DerivedTypes.h"
27 #include "llvm/IR/Function.h"
28 #include "llvm/IR/IRBuilder.h"
29 #include "llvm/IR/InstrTypes.h"
30 #include "llvm/IR/Instruction.h"
31 #include "llvm/IR/Instructions.h"
32 #include "llvm/IR/IntrinsicInst.h"
33 #include "llvm/IR/Intrinsics.h"
34 #include "llvm/IR/IntrinsicsARM.h"
35 #include "llvm/IR/PatternMatch.h"
36 #include "llvm/IR/Type.h"
37 #include "llvm/IR/Value.h"
38 #include "llvm/InitializePasses.h"
39 #include "llvm/Pass.h"
40 #include "llvm/Support/Casting.h"
41 #include "llvm/Transforms/Utils/Local.h"
42 #include <algorithm>
43 #include <cassert>
44 
45 using namespace llvm;
46 
47 #define DEBUG_TYPE "mve-gather-scatter-lowering"
48 
49 cl::opt<bool> EnableMaskedGatherScatters(
50     "enable-arm-maskedgatscat", cl::Hidden, cl::init(false),
51     cl::desc("Enable the generation of masked gathers and scatters"));
52 
53 namespace {
54 
55 class MVEGatherScatterLowering : public FunctionPass {
56 public:
57   static char ID; // Pass identification, replacement for typeid
58 
59   explicit MVEGatherScatterLowering() : FunctionPass(ID) {
60     initializeMVEGatherScatterLoweringPass(*PassRegistry::getPassRegistry());
61   }
62 
63   bool runOnFunction(Function &F) override;
64 
65   StringRef getPassName() const override {
66     return "MVE gather/scatter lowering";
67   }
68 
69   void getAnalysisUsage(AnalysisUsage &AU) const override {
70     AU.setPreservesCFG();
71     AU.addRequired<TargetPassConfig>();
72     AU.addRequired<LoopInfoWrapperPass>();
73     FunctionPass::getAnalysisUsage(AU);
74   }
75 
76 private:
77   // Check this is a valid gather with correct alignment
78   bool isLegalTypeAndAlignment(unsigned NumElements, unsigned ElemSize,
79                                unsigned Alignment);
80   // Check whether Ptr is hidden behind a bitcast and look through it
81   void lookThroughBitcast(Value *&Ptr);
82   // Check for a getelementptr and deduce base and offsets from it, on success
83   // returning the base directly and the offsets indirectly using the Offsets
84   // argument
85   Value *checkGEP(Value *&Offsets, Type *Ty, Value *Ptr, IRBuilder<> &Builder);
86   // Compute the scale of this gather/scatter instruction
87   int computeScale(unsigned GEPElemSize, unsigned MemoryElemSize);
88 
89   Value *lowerGather(IntrinsicInst *I);
90   // Create a gather from a base + vector of offsets
91   Value *tryCreateMaskedGatherOffset(IntrinsicInst *I, Value *Ptr,
92                                      Instruction *&Root, IRBuilder<> &Builder);
93   // Create a gather from a vector of pointers
94   Value *tryCreateMaskedGatherBase(IntrinsicInst *I, Value *Ptr,
95                                    IRBuilder<> &Builder);
96 
97   Value *lowerScatter(IntrinsicInst *I);
98   // Create a scatter to a base + vector of offsets
99   Value *tryCreateMaskedScatterOffset(IntrinsicInst *I, Value *Offsets,
100                                       IRBuilder<> &Builder);
101   // Create a scatter to a vector of pointers
102   Value *tryCreateMaskedScatterBase(IntrinsicInst *I, Value *Ptr,
103                                     IRBuilder<> &Builder);
104 
105   // Check whether these offsets could be moved out of the loop they're in
106   bool optimiseOffsets(Value *Offsets, BasicBlock *BB, LoopInfo *LI);
107   // Pushes the given add out of the loop
108   void pushOutAdd(PHINode *&Phi, Value *OffsSecondOperand, unsigned StartIndex);
109   // Pushes the given mul out of the loop
110   void pushOutMul(PHINode *&Phi, Value *IncrementPerRound,
111                   Value *OffsSecondOperand, unsigned LoopIncrement,
112                   IRBuilder<> &Builder);
113 };
114 
115 } // end anonymous namespace
116 
117 char MVEGatherScatterLowering::ID = 0;
118 
119 INITIALIZE_PASS(MVEGatherScatterLowering, DEBUG_TYPE,
120                 "MVE gather/scattering lowering pass", false, false)
121 
122 Pass *llvm::createMVEGatherScatterLoweringPass() {
123   return new MVEGatherScatterLowering();
124 }
125 
126 bool MVEGatherScatterLowering::isLegalTypeAndAlignment(unsigned NumElements,
127                                                        unsigned ElemSize,
128                                                        unsigned Alignment) {
129   if (((NumElements == 4 &&
130         (ElemSize == 32 || ElemSize == 16 || ElemSize == 8)) ||
131        (NumElements == 8 && (ElemSize == 16 || ElemSize == 8)) ||
132        (NumElements == 16 && ElemSize == 8)) &&
133       ElemSize / 8 <= Alignment)
134     return true;
135   LLVM_DEBUG(dbgs() << "masked gathers/scatters: instruction does not have "
136                     << "valid alignment or vector type \n");
137   return false;
138 }
139 
140 Value *MVEGatherScatterLowering::checkGEP(Value *&Offsets, Type *Ty, Value *Ptr,
141                                           IRBuilder<> &Builder) {
142   GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(Ptr);
143   if (!GEP) {
144     LLVM_DEBUG(
145         dbgs() << "masked gathers/scatters: no getelementpointer found\n");
146     return nullptr;
147   }
148   LLVM_DEBUG(dbgs() << "masked gathers/scatters: getelementpointer found."
149                     << " Looking at intrinsic for base + vector of offsets\n");
150   Value *GEPPtr = GEP->getPointerOperand();
151   if (GEPPtr->getType()->isVectorTy()) {
152     return nullptr;
153   }
154   if (GEP->getNumOperands() != 2) {
155     LLVM_DEBUG(dbgs() << "masked gathers/scatters: getelementptr with too many"
156                       << " operands. Expanding.\n");
157     return nullptr;
158   }
159   Offsets = GEP->getOperand(1);
160   // Paranoid check whether the number of parallel lanes is the same
161   assert(cast<VectorType>(Ty)->getNumElements() ==
162          cast<VectorType>(Offsets->getType())->getNumElements());
163   // Only <N x i32> offsets can be integrated into an arm gather, any smaller
164   // type would have to be sign extended by the gep - and arm gathers can only
165   // zero extend. Additionally, the offsets do have to originate from a zext of
166   // a vector with element types smaller or equal the type of the gather we're
167   // looking at
168   if (Offsets->getType()->getScalarSizeInBits() != 32)
169     return nullptr;
170   if (ZExtInst *ZextOffs = dyn_cast<ZExtInst>(Offsets))
171     Offsets = ZextOffs->getOperand(0);
172   else if (!(cast<VectorType>(Offsets->getType())->getNumElements() == 4 &&
173              Offsets->getType()->getScalarSizeInBits() == 32))
174     return nullptr;
175 
176   if (Ty != Offsets->getType()) {
177     if ((Ty->getScalarSizeInBits() <
178          Offsets->getType()->getScalarSizeInBits())) {
179       LLVM_DEBUG(dbgs() << "masked gathers/scatters: no correct offset type."
180                         << " Can't create intrinsic.\n");
181       return nullptr;
182     } else {
183       Offsets = Builder.CreateZExt(
184           Offsets, VectorType::getInteger(cast<VectorType>(Ty)));
185     }
186   }
187   // If none of the checks failed, return the gep's base pointer
188   LLVM_DEBUG(dbgs() << "masked gathers/scatters: found correct offsets\n");
189   return GEPPtr;
190 }
191 
192 void MVEGatherScatterLowering::lookThroughBitcast(Value *&Ptr) {
193   // Look through bitcast instruction if #elements is the same
194   if (auto *BitCast = dyn_cast<BitCastInst>(Ptr)) {
195     auto *BCTy = cast<VectorType>(BitCast->getType());
196     auto *BCSrcTy = cast<VectorType>(BitCast->getOperand(0)->getType());
197     if (BCTy->getNumElements() == BCSrcTy->getNumElements()) {
198       LLVM_DEBUG(
199           dbgs() << "masked gathers/scatters: looking through bitcast\n");
200       Ptr = BitCast->getOperand(0);
201     }
202   }
203 }
204 
205 int MVEGatherScatterLowering::computeScale(unsigned GEPElemSize,
206                                            unsigned MemoryElemSize) {
207   // This can be a 32bit load/store scaled by 4, a 16bit load/store scaled by 2,
208   // or a 8bit, 16bit or 32bit load/store scaled by 1
209   if (GEPElemSize == 32 && MemoryElemSize == 32)
210     return 2;
211   else if (GEPElemSize == 16 && MemoryElemSize == 16)
212     return 1;
213   else if (GEPElemSize == 8)
214     return 0;
215   LLVM_DEBUG(dbgs() << "masked gathers/scatters: incorrect scale. Can't "
216                     << "create intrinsic\n");
217   return -1;
218 }
219 
220 Value *MVEGatherScatterLowering::lowerGather(IntrinsicInst *I) {
221   using namespace PatternMatch;
222   LLVM_DEBUG(dbgs() << "masked gathers: checking transform preconditions\n");
223 
224   // @llvm.masked.gather.*(Ptrs, alignment, Mask, Src0)
225   // Attempt to turn the masked gather in I into a MVE intrinsic
226   // Potentially optimising the addressing modes as we do so.
227   auto *Ty = cast<VectorType>(I->getType());
228   Value *Ptr = I->getArgOperand(0);
229   unsigned Alignment = cast<ConstantInt>(I->getArgOperand(1))->getZExtValue();
230   Value *Mask = I->getArgOperand(2);
231   Value *PassThru = I->getArgOperand(3);
232 
233   if (!isLegalTypeAndAlignment(Ty->getNumElements(), Ty->getScalarSizeInBits(),
234                                Alignment))
235     return nullptr;
236   lookThroughBitcast(Ptr);
237   assert(Ptr->getType()->isVectorTy() && "Unexpected pointer type");
238 
239   IRBuilder<> Builder(I->getContext());
240   Builder.SetInsertPoint(I);
241   Builder.SetCurrentDebugLocation(I->getDebugLoc());
242 
243   Instruction *Root = I;
244   Value *Load = tryCreateMaskedGatherOffset(I, Ptr, Root, Builder);
245   if (!Load)
246     Load = tryCreateMaskedGatherBase(I, Ptr, Builder);
247   if (!Load)
248     return nullptr;
249 
250   if (!isa<UndefValue>(PassThru) && !match(PassThru, m_Zero())) {
251     LLVM_DEBUG(dbgs() << "masked gathers: found non-trivial passthru - "
252                       << "creating select\n");
253     Load = Builder.CreateSelect(Mask, Load, PassThru);
254   }
255 
256   Root->replaceAllUsesWith(Load);
257   Root->eraseFromParent();
258   if (Root != I)
259     // If this was an extending gather, we need to get rid of the sext/zext
260     // sext/zext as well as of the gather itself
261     I->eraseFromParent();
262 
263   LLVM_DEBUG(dbgs() << "masked gathers: successfully built masked gather\n");
264   return Load;
265 }
266 
267 Value *MVEGatherScatterLowering::tryCreateMaskedGatherBase(IntrinsicInst *I,
268                                                            Value *Ptr,
269                                                            IRBuilder<> &Builder) {
270   using namespace PatternMatch;
271   auto *Ty = cast<VectorType>(I->getType());
272   LLVM_DEBUG(dbgs() << "masked gathers: loading from vector of pointers\n");
273   if (Ty->getNumElements() != 4 || Ty->getScalarSizeInBits() != 32)
274     // Can't build an intrinsic for this
275     return nullptr;
276   Value *Mask = I->getArgOperand(2);
277   if (match(Mask, m_One()))
278     return Builder.CreateIntrinsic(Intrinsic::arm_mve_vldr_gather_base,
279                                    {Ty, Ptr->getType()},
280                                    {Ptr, Builder.getInt32(0)});
281   else
282     return Builder.CreateIntrinsic(
283         Intrinsic::arm_mve_vldr_gather_base_predicated,
284         {Ty, Ptr->getType(), Mask->getType()},
285         {Ptr, Builder.getInt32(0), Mask});
286 }
287 
288 Value *MVEGatherScatterLowering::tryCreateMaskedGatherOffset(
289     IntrinsicInst *I, Value *Ptr, Instruction *&Root, IRBuilder<> &Builder) {
290   using namespace PatternMatch;
291 
292   Type *OriginalTy = I->getType();
293   Type *ResultTy = OriginalTy;
294 
295   unsigned Unsigned = 1;
296   // The size of the gather was already checked in isLegalTypeAndAlignment;
297   // if it was not a full vector width an appropriate extend should follow.
298   auto *Extend = Root;
299   if (OriginalTy->getPrimitiveSizeInBits() < 128) {
300     // Only transform gathers with exactly one use
301     if (!I->hasOneUse())
302       return nullptr;
303 
304     // The correct root to replace is not the CallInst itself, but the
305     // instruction which extends it
306     Extend = cast<Instruction>(*I->users().begin());
307     if (isa<SExtInst>(Extend)) {
308       Unsigned = 0;
309     } else if (!isa<ZExtInst>(Extend)) {
310       LLVM_DEBUG(dbgs() << "masked gathers: extend needed but not provided. "
311                         << "Expanding\n");
312       return nullptr;
313     }
314     LLVM_DEBUG(dbgs() << "masked gathers: found an extending gather\n");
315     ResultTy = Extend->getType();
316     // The final size of the gather must be a full vector width
317     if (ResultTy->getPrimitiveSizeInBits() != 128) {
318       LLVM_DEBUG(dbgs() << "masked gathers: extending from the wrong type. "
319                         << "Expanding\n");
320       return nullptr;
321     }
322   }
323 
324   Value *Offsets;
325   Value *BasePtr = checkGEP(Offsets, ResultTy, Ptr, Builder);
326   if (!BasePtr)
327     return nullptr;
328 
329   int Scale = computeScale(
330       BasePtr->getType()->getPointerElementType()->getPrimitiveSizeInBits(),
331       OriginalTy->getScalarSizeInBits());
332   if (Scale == -1)
333     return nullptr;
334   Root = Extend;
335 
336   Value *Mask = I->getArgOperand(2);
337   if (!match(Mask, m_One()))
338     return Builder.CreateIntrinsic(
339         Intrinsic::arm_mve_vldr_gather_offset_predicated,
340         {ResultTy, BasePtr->getType(), Offsets->getType(), Mask->getType()},
341         {BasePtr, Offsets, Builder.getInt32(OriginalTy->getScalarSizeInBits()),
342          Builder.getInt32(Scale), Builder.getInt32(Unsigned), Mask});
343   else
344     return Builder.CreateIntrinsic(
345         Intrinsic::arm_mve_vldr_gather_offset,
346         {ResultTy, BasePtr->getType(), Offsets->getType()},
347         {BasePtr, Offsets, Builder.getInt32(OriginalTy->getScalarSizeInBits()),
348          Builder.getInt32(Scale), Builder.getInt32(Unsigned)});
349 }
350 
351 Value *MVEGatherScatterLowering::lowerScatter(IntrinsicInst *I) {
352   using namespace PatternMatch;
353   LLVM_DEBUG(dbgs() << "masked scatters: checking transform preconditions\n");
354 
355   // @llvm.masked.scatter.*(data, ptrs, alignment, mask)
356   // Attempt to turn the masked scatter in I into a MVE intrinsic
357   // Potentially optimising the addressing modes as we do so.
358   Value *Input = I->getArgOperand(0);
359   Value *Ptr = I->getArgOperand(1);
360   unsigned Alignment = cast<ConstantInt>(I->getArgOperand(2))->getZExtValue();
361   auto *Ty = cast<VectorType>(Input->getType());
362 
363   if (!isLegalTypeAndAlignment(Ty->getNumElements(), Ty->getScalarSizeInBits(),
364                                Alignment))
365     return nullptr;
366 
367   lookThroughBitcast(Ptr);
368   assert(Ptr->getType()->isVectorTy() && "Unexpected pointer type");
369 
370   IRBuilder<> Builder(I->getContext());
371   Builder.SetInsertPoint(I);
372   Builder.SetCurrentDebugLocation(I->getDebugLoc());
373 
374   Value *Store = tryCreateMaskedScatterOffset(I, Ptr, Builder);
375   if (!Store)
376     Store = tryCreateMaskedScatterBase(I, Ptr, Builder);
377   if (!Store)
378     return nullptr;
379 
380   LLVM_DEBUG(dbgs() << "masked scatters: successfully built masked scatter\n");
381   I->replaceAllUsesWith(Store);
382   I->eraseFromParent();
383   return Store;
384 }
385 
386 Value *MVEGatherScatterLowering::tryCreateMaskedScatterBase(
387     IntrinsicInst *I, Value *Ptr, IRBuilder<> &Builder) {
388   using namespace PatternMatch;
389   Value *Input = I->getArgOperand(0);
390   Value *Mask = I->getArgOperand(3);
391   auto *Ty = cast<VectorType>(Input->getType());
392   // Only QR variants allow truncating
393   if (!(Ty->getNumElements() == 4 && Ty->getScalarSizeInBits() == 32)) {
394     // Can't build an intrinsic for this
395     return nullptr;
396   }
397   //  int_arm_mve_vstr_scatter_base(_predicated) addr, offset, data(, mask)
398   LLVM_DEBUG(dbgs() << "masked scatters: storing to a vector of pointers\n");
399   if (match(Mask, m_One()))
400     return Builder.CreateIntrinsic(Intrinsic::arm_mve_vstr_scatter_base,
401                                    {Ptr->getType(), Input->getType()},
402                                    {Ptr, Builder.getInt32(0), Input});
403   else
404     return Builder.CreateIntrinsic(
405         Intrinsic::arm_mve_vstr_scatter_base_predicated,
406         {Ptr->getType(), Input->getType(), Mask->getType()},
407         {Ptr, Builder.getInt32(0), Input, Mask});
408 }
409 
410 Value *MVEGatherScatterLowering::tryCreateMaskedScatterOffset(
411     IntrinsicInst *I, Value *Ptr, IRBuilder<> &Builder) {
412   using namespace PatternMatch;
413   Value *Input = I->getArgOperand(0);
414   Value *Mask = I->getArgOperand(3);
415   Type *InputTy = Input->getType();
416   Type *MemoryTy = InputTy;
417   LLVM_DEBUG(dbgs() << "masked scatters: getelementpointer found. Storing"
418                     << " to base + vector of offsets\n");
419   // If the input has been truncated, try to integrate that trunc into the
420   // scatter instruction (we don't care about alignment here)
421   if (TruncInst *Trunc = dyn_cast<TruncInst>(Input)) {
422     Value *PreTrunc = Trunc->getOperand(0);
423     Type *PreTruncTy = PreTrunc->getType();
424     if (PreTruncTy->getPrimitiveSizeInBits() == 128) {
425       Input = PreTrunc;
426       InputTy = PreTruncTy;
427     }
428   }
429   if (InputTy->getPrimitiveSizeInBits() != 128) {
430     LLVM_DEBUG(
431         dbgs() << "masked scatters: cannot create scatters for non-standard"
432                << " input types. Expanding.\n");
433     return nullptr;
434   }
435 
436   Value *Offsets;
437   Value *BasePtr = checkGEP(Offsets, InputTy, Ptr, Builder);
438   if (!BasePtr)
439     return nullptr;
440   int Scale = computeScale(
441       BasePtr->getType()->getPointerElementType()->getPrimitiveSizeInBits(),
442       MemoryTy->getScalarSizeInBits());
443   if (Scale == -1)
444     return nullptr;
445 
446   if (!match(Mask, m_One()))
447     return Builder.CreateIntrinsic(
448         Intrinsic::arm_mve_vstr_scatter_offset_predicated,
449         {BasePtr->getType(), Offsets->getType(), Input->getType(),
450          Mask->getType()},
451         {BasePtr, Offsets, Input,
452          Builder.getInt32(MemoryTy->getScalarSizeInBits()),
453          Builder.getInt32(Scale), Mask});
454   else
455     return Builder.CreateIntrinsic(
456         Intrinsic::arm_mve_vstr_scatter_offset,
457         {BasePtr->getType(), Offsets->getType(), Input->getType()},
458         {BasePtr, Offsets, Input,
459          Builder.getInt32(MemoryTy->getScalarSizeInBits()),
460          Builder.getInt32(Scale)});
461 }
462 
463 void MVEGatherScatterLowering::pushOutAdd(PHINode *&Phi,
464                                           Value *OffsSecondOperand,
465                                           unsigned StartIndex) {
466   LLVM_DEBUG(dbgs() << "masked gathers/scatters: optimising add instruction\n");
467   Instruction *InsertionPoint =
468         &cast<Instruction>(Phi->getIncomingBlock(StartIndex)->back());
469   // Initialize the phi with a vector that contains a sum of the constants
470   Instruction *NewIndex = BinaryOperator::Create(
471       Instruction::Add, Phi->getIncomingValue(StartIndex), OffsSecondOperand,
472       "PushedOutAdd", InsertionPoint);
473   unsigned IncrementIndex = StartIndex == 0 ? 1 : 0;
474 
475   // Order such that start index comes first (this reduces mov's)
476   Phi->addIncoming(NewIndex, Phi->getIncomingBlock(StartIndex));
477   Phi->addIncoming(Phi->getIncomingValue(IncrementIndex),
478                    Phi->getIncomingBlock(IncrementIndex));
479   Phi->removeIncomingValue(IncrementIndex);
480   Phi->removeIncomingValue(StartIndex);
481 }
482 
483 void MVEGatherScatterLowering::pushOutMul(PHINode *&Phi,
484                                           Value *IncrementPerRound,
485                                           Value *OffsSecondOperand,
486                                           unsigned LoopIncrement,
487                                           IRBuilder<> &Builder) {
488   LLVM_DEBUG(dbgs() << "masked gathers/scatters: optimising mul instruction\n");
489 
490   // Create a new scalar add outside of the loop and transform it to a splat
491   // by which loop variable can be incremented
492   Instruction *InsertionPoint = &cast<Instruction>(
493         Phi->getIncomingBlock(LoopIncrement == 1 ? 0 : 1)->back());
494 
495   // Create a new index
496   Value *StartIndex = BinaryOperator::Create(
497       Instruction::Mul, Phi->getIncomingValue(LoopIncrement == 1 ? 0 : 1),
498       OffsSecondOperand, "PushedOutMul", InsertionPoint);
499 
500   Instruction *Product =
501       BinaryOperator::Create(Instruction::Mul, IncrementPerRound,
502                              OffsSecondOperand, "Product", InsertionPoint);
503   // Increment NewIndex by Product instead of the multiplication
504   Instruction *NewIncrement = BinaryOperator::Create(
505       Instruction::Add, Phi, Product, "IncrementPushedOutMul",
506       cast<Instruction>(Phi->getIncomingBlock(LoopIncrement)->back())
507           .getPrevNode());
508 
509   Phi->addIncoming(StartIndex,
510                    Phi->getIncomingBlock(LoopIncrement == 1 ? 0 : 1));
511   Phi->addIncoming(NewIncrement, Phi->getIncomingBlock(LoopIncrement));
512   Phi->removeIncomingValue((unsigned)0);
513   Phi->removeIncomingValue((unsigned)0);
514   return;
515 }
516 
517 // Return true if the given intrinsic is a gather or scatter
518 static bool isGatherScatter(IntrinsicInst *IntInst) {
519   if (IntInst == nullptr)
520     return false;
521   unsigned IntrinsicID = IntInst->getIntrinsicID();
522   return (IntrinsicID == Intrinsic::masked_gather ||
523           IntrinsicID == Intrinsic::arm_mve_vldr_gather_base ||
524           IntrinsicID == Intrinsic::arm_mve_vldr_gather_base_predicated ||
525           IntrinsicID == Intrinsic::arm_mve_vldr_gather_base_wb ||
526           IntrinsicID == Intrinsic::arm_mve_vldr_gather_base_wb_predicated ||
527           IntrinsicID == Intrinsic::arm_mve_vldr_gather_offset ||
528           IntrinsicID == Intrinsic::arm_mve_vldr_gather_offset_predicated ||
529           IntrinsicID == Intrinsic::masked_scatter ||
530           IntrinsicID == Intrinsic::arm_mve_vstr_scatter_base ||
531           IntrinsicID == Intrinsic::arm_mve_vstr_scatter_base_predicated ||
532           IntrinsicID == Intrinsic::arm_mve_vstr_scatter_base_wb ||
533           IntrinsicID == Intrinsic::arm_mve_vstr_scatter_base_wb_predicated ||
534           IntrinsicID == Intrinsic::arm_mve_vstr_scatter_offset ||
535           IntrinsicID == Intrinsic::arm_mve_vstr_scatter_offset_predicated);
536 }
537 
538 // Check whether all usages of this instruction are as offsets of
539 // gathers/scatters or simple arithmetics only used by gathers/scatters
540 static bool hasAllGatScatUsers(Instruction *I) {
541   if (I->hasNUses(0)) {
542     return false;
543   }
544   bool Gatscat = true;
545   for (User *U : I->users()) {
546     if (!isa<Instruction>(U))
547       return false;
548     if (isa<GetElementPtrInst>(U) ||
549         isGatherScatter(dyn_cast<IntrinsicInst>(U))) {
550       return Gatscat;
551     } else {
552       unsigned OpCode = cast<Instruction>(U)->getOpcode();
553       if ((OpCode == Instruction::Add || OpCode == Instruction::Mul) &&
554           hasAllGatScatUsers(cast<Instruction>(U))) {
555         continue;
556       }
557       return false;
558     }
559   }
560   return Gatscat;
561 }
562 
563 bool MVEGatherScatterLowering::optimiseOffsets(Value *Offsets, BasicBlock *BB,
564                                                LoopInfo *LI) {
565   LLVM_DEBUG(dbgs() << "masked gathers/scatters: trying to optimize\n");
566   // Optimise the addresses of gathers/scatters by moving invariant
567   // calculations out of the loop
568   if (!isa<Instruction>(Offsets))
569     return false;
570   Instruction *Offs = cast<Instruction>(Offsets);
571   if (Offs->getOpcode() != Instruction::Add &&
572       Offs->getOpcode() != Instruction::Mul)
573     return false;
574   Loop *L = LI->getLoopFor(BB);
575   if (L == nullptr)
576     return false;
577   if (!Offs->hasOneUse()) {
578     if (!hasAllGatScatUsers(Offs))
579       return false;
580   }
581 
582   // Find out which, if any, operand of the instruction
583   // is a phi node
584   PHINode *Phi;
585   int OffsSecondOp;
586   if (isa<PHINode>(Offs->getOperand(0))) {
587     Phi = cast<PHINode>(Offs->getOperand(0));
588     OffsSecondOp = 1;
589   } else if (isa<PHINode>(Offs->getOperand(1))) {
590     Phi = cast<PHINode>(Offs->getOperand(1));
591     OffsSecondOp = 0;
592   } else {
593     bool Changed = true;
594     if (isa<Instruction>(Offs->getOperand(0)) &&
595         L->contains(cast<Instruction>(Offs->getOperand(0))))
596       Changed |= optimiseOffsets(Offs->getOperand(0), BB, LI);
597     if (isa<Instruction>(Offs->getOperand(1)) &&
598         L->contains(cast<Instruction>(Offs->getOperand(1))))
599       Changed |= optimiseOffsets(Offs->getOperand(1), BB, LI);
600     if (!Changed) {
601       return false;
602     } else {
603       if (isa<PHINode>(Offs->getOperand(0))) {
604         Phi = cast<PHINode>(Offs->getOperand(0));
605         OffsSecondOp = 1;
606       } else if (isa<PHINode>(Offs->getOperand(1))) {
607         Phi = cast<PHINode>(Offs->getOperand(1));
608         OffsSecondOp = 0;
609       } else {
610         return false;
611       }
612     }
613   }
614   // A phi node we want to perform this function on should be from the
615   // loop header, and shouldn't have more than 2 incoming values
616   if (Phi->getParent() != L->getHeader() ||
617       Phi->getNumIncomingValues() != 2)
618     return false;
619 
620   // The phi must be an induction variable
621   Instruction *Op;
622   int IncrementingBlock = -1;
623 
624   for (int i = 0; i < 2; i++)
625     if ((Op = dyn_cast<Instruction>(Phi->getIncomingValue(i))) != nullptr)
626       if (Op->getOpcode() == Instruction::Add &&
627           (Op->getOperand(0) == Phi || Op->getOperand(1) == Phi))
628         IncrementingBlock = i;
629   if (IncrementingBlock == -1)
630     return false;
631 
632   Instruction *IncInstruction =
633       cast<Instruction>(Phi->getIncomingValue(IncrementingBlock));
634 
635   // If the phi is not used by anything else, we can just adapt it when
636   // replacing the instruction; if it is, we'll have to duplicate it
637   PHINode *NewPhi;
638   Value *IncrementPerRound = IncInstruction->getOperand(
639       (IncInstruction->getOperand(0) == Phi) ? 1 : 0);
640 
641   // Get the value that is added to/multiplied with the phi
642   Value *OffsSecondOperand = Offs->getOperand(OffsSecondOp);
643 
644   if (IncrementPerRound->getType() != OffsSecondOperand->getType())
645     // Something has gone wrong, abort
646     return false;
647 
648   // Only proceed if the increment per round is a constant or an instruction
649   // which does not originate from within the loop
650   if (!isa<Constant>(IncrementPerRound) &&
651       !(isa<Instruction>(IncrementPerRound) &&
652         !L->contains(cast<Instruction>(IncrementPerRound))))
653     return false;
654 
655   if (Phi->getNumUses() == 2) {
656     // No other users -> reuse existing phi (One user is the instruction
657     // we're looking at, the other is the phi increment)
658     if (IncInstruction->getNumUses() != 1) {
659       // If the incrementing instruction does have more users than
660       // our phi, we need to copy it
661       IncInstruction = BinaryOperator::Create(
662           Instruction::BinaryOps(IncInstruction->getOpcode()), Phi,
663           IncrementPerRound, "LoopIncrement", IncInstruction);
664       Phi->setIncomingValue(IncrementingBlock, IncInstruction);
665     }
666     NewPhi = Phi;
667   } else {
668     // There are other users -> create a new phi
669     NewPhi = PHINode::Create(Phi->getType(), 0, "NewPhi", Phi);
670     std::vector<Value *> Increases;
671     // Copy the incoming values of the old phi
672     NewPhi->addIncoming(Phi->getIncomingValue(IncrementingBlock == 1 ? 0 : 1),
673                         Phi->getIncomingBlock(IncrementingBlock == 1 ? 0 : 1));
674     IncInstruction = BinaryOperator::Create(
675         Instruction::BinaryOps(IncInstruction->getOpcode()), NewPhi,
676         IncrementPerRound, "LoopIncrement", IncInstruction);
677     NewPhi->addIncoming(IncInstruction,
678                         Phi->getIncomingBlock(IncrementingBlock));
679     IncrementingBlock = 1;
680   }
681 
682   IRBuilder<> Builder(BB->getContext());
683   Builder.SetInsertPoint(Phi);
684   Builder.SetCurrentDebugLocation(Offs->getDebugLoc());
685 
686   switch (Offs->getOpcode()) {
687   case Instruction::Add:
688     pushOutAdd(NewPhi, OffsSecondOperand, IncrementingBlock == 1 ? 0 : 1);
689     break;
690   case Instruction::Mul:
691     pushOutMul(NewPhi, IncrementPerRound, OffsSecondOperand, IncrementingBlock,
692                Builder);
693     break;
694   default:
695     return false;
696   }
697   LLVM_DEBUG(
698       dbgs() << "masked gathers/scatters: simplified loop variable add/mul\n");
699 
700   // The instruction has now been "absorbed" into the phi value
701   Offs->replaceAllUsesWith(NewPhi);
702   if (Offs->hasNUses(0))
703     Offs->eraseFromParent();
704   // Clean up the old increment in case it's unused because we built a new
705   // one
706   if (IncInstruction->hasNUses(0))
707     IncInstruction->eraseFromParent();
708 
709   return true;
710 }
711 
712 bool MVEGatherScatterLowering::runOnFunction(Function &F) {
713   if (!EnableMaskedGatherScatters)
714     return false;
715   auto &TPC = getAnalysis<TargetPassConfig>();
716   auto &TM = TPC.getTM<TargetMachine>();
717   auto *ST = &TM.getSubtarget<ARMSubtarget>(F);
718   if (!ST->hasMVEIntegerOps())
719     return false;
720   SmallVector<IntrinsicInst *, 4> Gathers;
721   SmallVector<IntrinsicInst *, 4> Scatters;
722   LoopInfo &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
723 
724   for (BasicBlock &BB : F) {
725     for (Instruction &I : BB) {
726       IntrinsicInst *II = dyn_cast<IntrinsicInst>(&I);
727       if (II && II->getIntrinsicID() == Intrinsic::masked_gather)
728         Gathers.push_back(II);
729       else if (II && II->getIntrinsicID() == Intrinsic::masked_scatter)
730         Scatters.push_back(II);
731     }
732   }
733 
734   bool Changed = false;
735   for (unsigned i = 0; i < Gathers.size(); i++) {
736     IntrinsicInst *I = Gathers[i];
737     if (isa<GetElementPtrInst>(I->getArgOperand(0)))
738       optimiseOffsets(cast<Instruction>(I->getArgOperand(0))->getOperand(1),
739                       I->getParent(), &LI);
740     Value *L = lowerGather(I);
741     if (L == nullptr)
742       continue;
743     // Get rid of any now dead instructions
744     SimplifyInstructionsInBlock(cast<Instruction>(L)->getParent());
745     Changed = true;
746   }
747 
748   for (unsigned i = 0; i < Scatters.size(); i++) {
749     IntrinsicInst *I = Scatters[i];
750     if (isa<GetElementPtrInst>(I->getArgOperand(1)))
751       optimiseOffsets(cast<Instruction>(I->getArgOperand(1))->getOperand(1),
752                       I->getParent(), &LI);
753     Value *S = lowerScatter(I);
754     if (S == nullptr)
755       continue;
756     // Get rid of any now dead instructions
757     SimplifyInstructionsInBlock(cast<Instruction>(S)->getParent());
758     Changed = true;
759   }
760   return Changed;
761 }
762