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/TargetTransformInfo.h"
19 #include "llvm/CodeGen/TargetLowering.h"
20 #include "llvm/CodeGen/TargetPassConfig.h"
21 #include "llvm/CodeGen/TargetSubtargetInfo.h"
22 #include "llvm/InitializePasses.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/InstrTypes.h"
29 #include "llvm/IR/Instruction.h"
30 #include "llvm/IR/Instructions.h"
31 #include "llvm/IR/IntrinsicInst.h"
32 #include "llvm/IR/Intrinsics.h"
33 #include "llvm/IR/IntrinsicsARM.h"
34 #include "llvm/IR/IRBuilder.h"
35 #include "llvm/IR/PatternMatch.h"
36 #include "llvm/IR/Type.h"
37 #include "llvm/IR/Value.h"
38 #include "llvm/Pass.h"
39 #include "llvm/Support/Casting.h"
40 #include <algorithm>
41 #include <cassert>
42 
43 using namespace llvm;
44 
45 #define DEBUG_TYPE "mve-gather-scatter-lowering"
46 
47 cl::opt<bool> EnableMaskedGatherScatters(
48     "enable-arm-maskedgatscat", cl::Hidden, cl::init(false),
49     cl::desc("Enable the generation of masked gathers and scatters"));
50 
51 namespace {
52 
53 class MVEGatherScatterLowering : public FunctionPass {
54 public:
55   static char ID; // Pass identification, replacement for typeid
56 
57   explicit MVEGatherScatterLowering() : FunctionPass(ID) {
58     initializeMVEGatherScatterLoweringPass(*PassRegistry::getPassRegistry());
59   }
60 
61   bool runOnFunction(Function &F) override;
62 
63   StringRef getPassName() const override {
64     return "MVE gather/scatter lowering";
65   }
66 
67   void getAnalysisUsage(AnalysisUsage &AU) const override {
68     AU.setPreservesCFG();
69     AU.addRequired<TargetPassConfig>();
70     FunctionPass::getAnalysisUsage(AU);
71   }
72 
73 private:
74   // Check this is a valid gather with correct alignment
75   bool isLegalTypeAndAlignment(unsigned NumElements, unsigned ElemSize,
76                                unsigned Alignment);
77   // Check whether Ptr is hidden behind a bitcast and look through it
78   void lookThroughBitcast(Value *&Ptr);
79   // Check for a getelementptr and deduce base and offsets from it, on success
80   // returning the base directly and the offsets indirectly using the Offsets
81   // argument
82   Value *checkGEP(Value *&Offsets, Type *Ty, Value *Ptr, IRBuilder<> Builder);
83   // Compute the scale of this gather/scatter instruction
84   int computeScale(unsigned GEPElemSize, unsigned MemoryElemSize);
85 
86   bool lowerGather(IntrinsicInst *I);
87   // Create a gather from a base + vector of offsets
88   Value *tryCreateMaskedGatherOffset(IntrinsicInst *I, Value *Ptr,
89                                      Instruction *&Root, IRBuilder<> Builder);
90   // Create a gather from a vector of pointers
91   Value *tryCreateMaskedGatherBase(IntrinsicInst *I, Value *Ptr,
92                                    IRBuilder<> Builder);
93 
94   bool lowerScatter(IntrinsicInst *I);
95   // Create a scatter to a base + vector of offsets
96   Value *tryCreateMaskedScatterOffset(IntrinsicInst *I, Value *Ptr,
97                                       IRBuilder<> Builder);
98   // Create a scatter to a vector of pointers
99   Value *tryCreateMaskedScatterBase(IntrinsicInst *I, Value *Ptr,
100                                     IRBuilder<> Builder);
101 };
102 
103 } // end anonymous namespace
104 
105 char MVEGatherScatterLowering::ID = 0;
106 
107 INITIALIZE_PASS(MVEGatherScatterLowering, DEBUG_TYPE,
108                 "MVE gather/scattering lowering pass", false, false)
109 
110 Pass *llvm::createMVEGatherScatterLoweringPass() {
111   return new MVEGatherScatterLowering();
112 }
113 
114 bool MVEGatherScatterLowering::isLegalTypeAndAlignment(unsigned NumElements,
115                                                        unsigned ElemSize,
116                                                        unsigned Alignment) {
117   if (((NumElements == 4 &&
118         (ElemSize == 32 || ElemSize == 16 || ElemSize == 8)) ||
119        (NumElements == 8 && (ElemSize == 16 || ElemSize == 8)) ||
120        (NumElements == 16 && ElemSize == 8)) &&
121       ElemSize / 8 <= Alignment)
122     return true;
123   LLVM_DEBUG(dbgs() << "masked gathers/scatters: instruction does not have "
124                     << "valid alignment or vector type \n");
125   return false;
126 }
127 
128 Value *MVEGatherScatterLowering::checkGEP(Value *&Offsets, Type *Ty, Value *Ptr,
129                                           IRBuilder<> Builder) {
130   GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(Ptr);
131   if (!GEP) {
132     LLVM_DEBUG(
133         dbgs() << "masked gathers/scatters: no getelementpointer found\n");
134     return nullptr;
135   }
136   LLVM_DEBUG(dbgs() << "masked gathers/scatters: getelementpointer found."
137                     << " Looking at intrinsic for base + vector of offsets\n");
138   Value *GEPPtr = GEP->getPointerOperand();
139   if (GEPPtr->getType()->isVectorTy()) {
140     return nullptr;
141   }
142   if (GEP->getNumOperands() != 2) {
143     LLVM_DEBUG(dbgs() << "masked gathers/scatters: getelementptr with too many"
144                       << " operands. Expanding.\n");
145     return nullptr;
146   }
147   Offsets = GEP->getOperand(1);
148   // SExt offsets inside masked gathers are not permitted by the architecture;
149   // we therefore can't fold them
150   if (ZExtInst *ZextOffs = dyn_cast<ZExtInst>(Offsets))
151     Offsets = ZextOffs->getOperand(0);
152   Type *OffsType = VectorType::getInteger(cast<VectorType>(Ty));
153   // If the offset we found does not have the type the intrinsic expects,
154   // i.e., the same type as the gather (or scatter input) itself, we need to
155   // convert it (only i types) or fall back to expanding the gather
156   if (OffsType != Offsets->getType()) {
157     if (OffsType->getScalarSizeInBits() >
158         Offsets->getType()->getScalarSizeInBits()) {
159       LLVM_DEBUG(dbgs() << "masked gathers/scatters: extending offsets\n");
160       Offsets = Builder.CreateZExt(Offsets, OffsType, "");
161     } else {
162       LLVM_DEBUG(dbgs() << "masked gathers/scatters: no correct offset type."
163                         << " Can't create intrinsic.\n");
164       return nullptr;
165     }
166   }
167   // If none of the checks failed, return the gep's base pointer
168   return GEPPtr;
169 }
170 
171 void MVEGatherScatterLowering::lookThroughBitcast(Value *&Ptr) {
172   // Look through bitcast instruction if #elements is the same
173   if (auto *BitCast = dyn_cast<BitCastInst>(Ptr)) {
174     Type *BCTy = BitCast->getType();
175     Type *BCSrcTy = BitCast->getOperand(0)->getType();
176     if (BCTy->getVectorNumElements() == BCSrcTy->getVectorNumElements()) {
177       LLVM_DEBUG(
178           dbgs() << "masked gathers/scatters: looking through bitcast\n");
179       Ptr = BitCast->getOperand(0);
180     }
181   }
182 }
183 
184 int MVEGatherScatterLowering::computeScale(unsigned GEPElemSize,
185                                            unsigned MemoryElemSize) {
186   // This can be a 32bit load/store scaled by 4, a 16bit load/store scaled by 2,
187   // or a 8bit, 16bit or 32bit load/store scaled by 1
188   if (GEPElemSize == 32 && MemoryElemSize == 32)
189     return 2;
190   else if (GEPElemSize == 16 && MemoryElemSize == 16)
191     return 1;
192   else if (GEPElemSize == 8)
193     return 0;
194   LLVM_DEBUG(dbgs() << "masked gathers/scatters: incorrect scale. Can't "
195                     << "create intrinsic\n");
196   return -1;
197 }
198 
199 bool MVEGatherScatterLowering::lowerGather(IntrinsicInst *I) {
200   using namespace PatternMatch;
201   LLVM_DEBUG(dbgs() << "masked gathers: checking transform preconditions\n");
202 
203   // @llvm.masked.gather.*(Ptrs, alignment, Mask, Src0)
204   // Attempt to turn the masked gather in I into a MVE intrinsic
205   // Potentially optimising the addressing modes as we do so.
206   Type *Ty = I->getType();
207   Value *Ptr = I->getArgOperand(0);
208   unsigned Alignment = cast<ConstantInt>(I->getArgOperand(1))->getZExtValue();
209   Value *Mask = I->getArgOperand(2);
210   Value *PassThru = I->getArgOperand(3);
211 
212   if (!isLegalTypeAndAlignment(Ty->getVectorNumElements(),
213                                Ty->getScalarSizeInBits(), Alignment))
214     return false;
215   lookThroughBitcast(Ptr);
216   assert(Ptr->getType()->isVectorTy() && "Unexpected pointer type");
217 
218   IRBuilder<> Builder(I->getContext());
219   Builder.SetInsertPoint(I);
220   Builder.SetCurrentDebugLocation(I->getDebugLoc());
221 
222   Instruction *Root = I;
223   Value *Load = tryCreateMaskedGatherOffset(I, Ptr, Root, Builder);
224   if (!Load)
225     Load = tryCreateMaskedGatherBase(I, Ptr, Builder);
226   if (!Load)
227     return false;
228 
229   if (!isa<UndefValue>(PassThru) && !match(PassThru, m_Zero())) {
230     LLVM_DEBUG(dbgs() << "masked gathers: found non-trivial passthru - "
231                       << "creating select\n");
232     Load = Builder.CreateSelect(Mask, Load, PassThru);
233   }
234 
235   Root->replaceAllUsesWith(Load);
236   Root->eraseFromParent();
237   if (Root != I)
238     // If this was an extending gather, we need to get rid of the sext/zext
239     // sext/zext as well as of the gather itself
240     I->eraseFromParent();
241   LLVM_DEBUG(dbgs() << "masked gathers: successfully built masked gather\n");
242   return true;
243 }
244 
245 Value *MVEGatherScatterLowering::tryCreateMaskedGatherBase(
246     IntrinsicInst *I, Value *Ptr, IRBuilder<> Builder) {
247   using namespace PatternMatch;
248   Type *Ty = I->getType();
249   LLVM_DEBUG(dbgs() << "masked gathers: loading from vector of pointers\n");
250   if (Ty->getVectorNumElements() != 4 || Ty->getScalarSizeInBits() != 32)
251     // Can't build an intrinsic for this
252     return nullptr;
253   Value *Mask = I->getArgOperand(2);
254   if (match(Mask, m_One()))
255     return Builder.CreateIntrinsic(Intrinsic::arm_mve_vldr_gather_base,
256                                    {Ty, Ptr->getType()},
257                                    {Ptr, Builder.getInt32(0)});
258   else
259     return Builder.CreateIntrinsic(
260         Intrinsic::arm_mve_vldr_gather_base_predicated,
261         {Ty, Ptr->getType(), Mask->getType()},
262         {Ptr, Builder.getInt32(0), Mask});
263 }
264 
265 Value *MVEGatherScatterLowering::tryCreateMaskedGatherOffset(
266     IntrinsicInst *I, Value *Ptr, Instruction *&Root, IRBuilder<> Builder) {
267   using namespace PatternMatch;
268 
269   Type *OriginalTy = I->getType();
270   Type *ResultTy = OriginalTy;
271 
272   unsigned Unsigned = 1;
273   // The size of the gather was already checked in isLegalTypeAndAlignment;
274   // if it was not a full vector width an appropriate extend should follow.
275   auto *Extend = Root;
276   if (OriginalTy->getPrimitiveSizeInBits() < 128) {
277     // Only transform gathers with exactly one use
278     if (!I->hasOneUse())
279       return nullptr;
280 
281     // The correct root to replace is the not the CallInst itself, but the
282     // instruction which extends it
283     Extend = cast<Instruction>(*I->users().begin());
284     if (isa<SExtInst>(Extend)) {
285       Unsigned = 0;
286     } else if (!isa<ZExtInst>(Extend)) {
287       LLVM_DEBUG(dbgs() << "masked gathers: extend needed but not provided. "
288                         << "Expanding\n");
289       return nullptr;
290     }
291     LLVM_DEBUG(dbgs() << "masked gathers: found an extending gather\n");
292     ResultTy = Extend->getType();
293     // The final size of the gather must be a full vector width
294     if (ResultTy->getPrimitiveSizeInBits() != 128) {
295       LLVM_DEBUG(dbgs() << "masked gathers: extending from the wrong type. "
296                         << "Expanding\n");
297       return nullptr;
298     }
299   }
300 
301   Value *Offsets;
302   Value *BasePtr = checkGEP(Offsets, ResultTy, Ptr, Builder);
303   if (!BasePtr)
304     return nullptr;
305 
306   int Scale = computeScale(
307       BasePtr->getType()->getPointerElementType()->getPrimitiveSizeInBits(),
308       OriginalTy->getScalarSizeInBits());
309   if (Scale == -1)
310     return nullptr;
311   Root = Extend;
312 
313   Value *Mask = I->getArgOperand(2);
314   if (!match(Mask, m_One()))
315     return Builder.CreateIntrinsic(
316         Intrinsic::arm_mve_vldr_gather_offset_predicated,
317         {ResultTy, BasePtr->getType(), Offsets->getType(), Mask->getType()},
318         {BasePtr, Offsets, Builder.getInt32(OriginalTy->getScalarSizeInBits()),
319          Builder.getInt32(Scale), Builder.getInt32(Unsigned), Mask});
320   else
321     return Builder.CreateIntrinsic(
322         Intrinsic::arm_mve_vldr_gather_offset,
323         {ResultTy, BasePtr->getType(), Offsets->getType()},
324         {BasePtr, Offsets, Builder.getInt32(OriginalTy->getScalarSizeInBits()),
325          Builder.getInt32(Scale), Builder.getInt32(Unsigned)});
326 }
327 
328 bool MVEGatherScatterLowering::lowerScatter(IntrinsicInst *I) {
329   using namespace PatternMatch;
330   LLVM_DEBUG(dbgs() << "masked scatters: checking transform preconditions\n");
331 
332   // @llvm.masked.scatter.*(data, ptrs, alignment, mask)
333   // Attempt to turn the masked scatter in I into a MVE intrinsic
334   // Potentially optimising the addressing modes as we do so.
335   Value *Input = I->getArgOperand(0);
336   Value *Ptr = I->getArgOperand(1);
337   unsigned Alignment = cast<ConstantInt>(I->getArgOperand(2))->getZExtValue();
338   Type *Ty = Input->getType();
339 
340   if (!isLegalTypeAndAlignment(Ty->getVectorNumElements(),
341                                Ty->getScalarSizeInBits(), Alignment))
342     return false;
343   lookThroughBitcast(Ptr);
344   assert(Ptr->getType()->isVectorTy() && "Unexpected pointer type");
345 
346   IRBuilder<> Builder(I->getContext());
347   Builder.SetInsertPoint(I);
348   Builder.SetCurrentDebugLocation(I->getDebugLoc());
349 
350   Value *Store = tryCreateMaskedScatterOffset(I, Ptr, Builder);
351   if (!Store)
352     Store = tryCreateMaskedScatterBase(I, Ptr, Builder);
353   if (!Store)
354     return false;
355 
356   LLVM_DEBUG(dbgs() << "masked scatters: successfully built masked scatter\n");
357   I->replaceAllUsesWith(Store);
358   I->eraseFromParent();
359   return true;
360 }
361 
362 Value *MVEGatherScatterLowering::tryCreateMaskedScatterBase(
363     IntrinsicInst *I, Value *Ptr, IRBuilder<> Builder) {
364   using namespace PatternMatch;
365   Value *Input = I->getArgOperand(0);
366   Value *Mask = I->getArgOperand(3);
367   Type *Ty = Input->getType();
368   // Only QR variants allow truncating
369   if (!(Ty->getVectorNumElements() == 4 && Ty->getScalarSizeInBits() == 32)) {
370     // Can't build an intrinsic for this
371     return nullptr;
372   }
373   //  int_arm_mve_vstr_scatter_base(_predicated) addr, offset, data(, mask)
374   LLVM_DEBUG(dbgs() << "masked scatters: storing to a vector of pointers\n");
375   if (match(Mask, m_One()))
376     return Builder.CreateIntrinsic(Intrinsic::arm_mve_vstr_scatter_base,
377                                    {Ptr->getType(), Input->getType()},
378                                    {Ptr, Builder.getInt32(0), Input});
379   else
380     return Builder.CreateIntrinsic(
381         Intrinsic::arm_mve_vstr_scatter_base_predicated,
382         {Ptr->getType(), Input->getType(), Mask->getType()},
383         {Ptr, Builder.getInt32(0), Input, Mask});
384 }
385 
386 Value *MVEGatherScatterLowering::tryCreateMaskedScatterOffset(
387     IntrinsicInst *I, Value *Ptr, IRBuilder<> Builder) {
388   using namespace PatternMatch;
389   Value *Input = I->getArgOperand(0);
390   Value *Mask = I->getArgOperand(3);
391   Type *InputTy = Input->getType();
392   Type *MemoryTy = InputTy;
393   LLVM_DEBUG(dbgs() << "masked scatters: getelementpointer found. Storing"
394                     << " to base + vector of offsets\n");
395   // If the input has been truncated, try to integrate that trunc into the
396   // scatter instruction (we don't care about alignment here)
397   if (TruncInst *Trunc = dyn_cast<TruncInst>(Input)) {
398     Value *PreTrunc = Trunc->getOperand(0);
399     Type *PreTruncTy = PreTrunc->getType();
400     if (PreTruncTy->getPrimitiveSizeInBits() == 128) {
401       Input = PreTrunc;
402       InputTy = PreTruncTy;
403     }
404   }
405   if (InputTy->getPrimitiveSizeInBits() != 128) {
406     LLVM_DEBUG(
407         dbgs() << "masked scatters: cannot create scatters for non-standard"
408                << " input types. Expanding.\n");
409     return nullptr;
410   }
411 
412   Value *Offsets;
413   Value *BasePtr = checkGEP(Offsets, InputTy, Ptr, Builder);
414   if (!BasePtr)
415     return nullptr;
416   int Scale = computeScale(
417       BasePtr->getType()->getPointerElementType()->getPrimitiveSizeInBits(),
418       MemoryTy->getScalarSizeInBits());
419   if (Scale == -1)
420     return nullptr;
421 
422   if (!match(Mask, m_One()))
423     return Builder.CreateIntrinsic(
424         Intrinsic::arm_mve_vstr_scatter_offset_predicated,
425         {BasePtr->getType(), Offsets->getType(), Input->getType(),
426          Mask->getType()},
427         {BasePtr, Offsets, Input,
428          Builder.getInt32(MemoryTy->getScalarSizeInBits()),
429          Builder.getInt32(Scale), Mask});
430   else
431     return Builder.CreateIntrinsic(
432         Intrinsic::arm_mve_vstr_scatter_offset,
433         {BasePtr->getType(), Offsets->getType(), Input->getType()},
434         {BasePtr, Offsets, Input,
435          Builder.getInt32(MemoryTy->getScalarSizeInBits()),
436          Builder.getInt32(Scale)});
437 }
438 
439 bool MVEGatherScatterLowering::runOnFunction(Function &F) {
440   if (!EnableMaskedGatherScatters)
441     return false;
442   auto &TPC = getAnalysis<TargetPassConfig>();
443   auto &TM = TPC.getTM<TargetMachine>();
444   auto *ST = &TM.getSubtarget<ARMSubtarget>(F);
445   if (!ST->hasMVEIntegerOps())
446     return false;
447   SmallVector<IntrinsicInst *, 4> Gathers;
448   SmallVector<IntrinsicInst *, 4> Scatters;
449   for (BasicBlock &BB : F) {
450     for (Instruction &I : BB) {
451       IntrinsicInst *II = dyn_cast<IntrinsicInst>(&I);
452       if (II && II->getIntrinsicID() == Intrinsic::masked_gather)
453         Gathers.push_back(II);
454       else if (II && II->getIntrinsicID() == Intrinsic::masked_scatter)
455         Scatters.push_back(II);
456     }
457   }
458 
459   bool Changed = false;
460   for (IntrinsicInst *I : Gathers)
461     Changed |= lowerGather(I);
462   for (IntrinsicInst *I : Scatters)
463     Changed |= lowerScatter(I);
464 
465   return Changed;
466 }
467