xref: /freebsd-src/contrib/llvm-project/llvm/lib/Target/ARM/MVEGatherScatterLowering.cpp (revision 4652422eb477731f284b1345afeefef7f269da50)
1480093f4SDimitry Andric //===- MVEGatherScatterLowering.cpp - Gather/Scatter lowering -------------===//
2480093f4SDimitry Andric //
3480093f4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4480093f4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5480093f4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6480093f4SDimitry Andric //
7480093f4SDimitry Andric //===----------------------------------------------------------------------===//
8480093f4SDimitry Andric //
9480093f4SDimitry Andric /// This pass custom lowers llvm.gather and llvm.scatter instructions to
10480093f4SDimitry Andric /// arm.mve.gather and arm.mve.scatter intrinsics, optimising the code to
11480093f4SDimitry Andric /// produce a better final result as we go.
12480093f4SDimitry Andric //
13480093f4SDimitry Andric //===----------------------------------------------------------------------===//
14480093f4SDimitry Andric 
15480093f4SDimitry Andric #include "ARM.h"
16480093f4SDimitry Andric #include "ARMBaseInstrInfo.h"
17480093f4SDimitry Andric #include "ARMSubtarget.h"
185ffd83dbSDimitry Andric #include "llvm/Analysis/LoopInfo.h"
19480093f4SDimitry Andric #include "llvm/Analysis/TargetTransformInfo.h"
20480093f4SDimitry Andric #include "llvm/CodeGen/TargetLowering.h"
21480093f4SDimitry Andric #include "llvm/CodeGen/TargetPassConfig.h"
22480093f4SDimitry Andric #include "llvm/CodeGen/TargetSubtargetInfo.h"
23480093f4SDimitry Andric #include "llvm/InitializePasses.h"
24480093f4SDimitry Andric #include "llvm/IR/BasicBlock.h"
25480093f4SDimitry Andric #include "llvm/IR/Constant.h"
26480093f4SDimitry Andric #include "llvm/IR/Constants.h"
27480093f4SDimitry Andric #include "llvm/IR/DerivedTypes.h"
28480093f4SDimitry Andric #include "llvm/IR/Function.h"
29480093f4SDimitry Andric #include "llvm/IR/InstrTypes.h"
30480093f4SDimitry Andric #include "llvm/IR/Instruction.h"
31480093f4SDimitry Andric #include "llvm/IR/Instructions.h"
32480093f4SDimitry Andric #include "llvm/IR/IntrinsicInst.h"
33480093f4SDimitry Andric #include "llvm/IR/Intrinsics.h"
34480093f4SDimitry Andric #include "llvm/IR/IntrinsicsARM.h"
35480093f4SDimitry Andric #include "llvm/IR/IRBuilder.h"
36480093f4SDimitry Andric #include "llvm/IR/PatternMatch.h"
37480093f4SDimitry Andric #include "llvm/IR/Type.h"
38480093f4SDimitry Andric #include "llvm/IR/Value.h"
39480093f4SDimitry Andric #include "llvm/Pass.h"
40480093f4SDimitry Andric #include "llvm/Support/Casting.h"
415ffd83dbSDimitry Andric #include "llvm/Transforms/Utils/Local.h"
42480093f4SDimitry Andric #include <algorithm>
43480093f4SDimitry Andric #include <cassert>
44480093f4SDimitry Andric 
45480093f4SDimitry Andric using namespace llvm;
46480093f4SDimitry Andric 
47e8d8bef9SDimitry Andric #define DEBUG_TYPE "arm-mve-gather-scatter-lowering"
48480093f4SDimitry Andric 
49480093f4SDimitry Andric cl::opt<bool> EnableMaskedGatherScatters(
50e8d8bef9SDimitry Andric     "enable-arm-maskedgatscat", cl::Hidden, cl::init(true),
51480093f4SDimitry Andric     cl::desc("Enable the generation of masked gathers and scatters"));
52480093f4SDimitry Andric 
53480093f4SDimitry Andric namespace {
54480093f4SDimitry Andric 
55480093f4SDimitry Andric class MVEGatherScatterLowering : public FunctionPass {
56480093f4SDimitry Andric public:
57480093f4SDimitry Andric   static char ID; // Pass identification, replacement for typeid
58480093f4SDimitry Andric 
59480093f4SDimitry Andric   explicit MVEGatherScatterLowering() : FunctionPass(ID) {
60480093f4SDimitry Andric     initializeMVEGatherScatterLoweringPass(*PassRegistry::getPassRegistry());
61480093f4SDimitry Andric   }
62480093f4SDimitry Andric 
63480093f4SDimitry Andric   bool runOnFunction(Function &F) override;
64480093f4SDimitry Andric 
65480093f4SDimitry Andric   StringRef getPassName() const override {
66480093f4SDimitry Andric     return "MVE gather/scatter lowering";
67480093f4SDimitry Andric   }
68480093f4SDimitry Andric 
69480093f4SDimitry Andric   void getAnalysisUsage(AnalysisUsage &AU) const override {
70480093f4SDimitry Andric     AU.setPreservesCFG();
71480093f4SDimitry Andric     AU.addRequired<TargetPassConfig>();
725ffd83dbSDimitry Andric     AU.addRequired<LoopInfoWrapperPass>();
73480093f4SDimitry Andric     FunctionPass::getAnalysisUsage(AU);
74480093f4SDimitry Andric   }
75480093f4SDimitry Andric 
76480093f4SDimitry Andric private:
775ffd83dbSDimitry Andric   LoopInfo *LI = nullptr;
785ffd83dbSDimitry Andric 
79480093f4SDimitry Andric   // Check this is a valid gather with correct alignment
80480093f4SDimitry Andric   bool isLegalTypeAndAlignment(unsigned NumElements, unsigned ElemSize,
815ffd83dbSDimitry Andric                                Align Alignment);
82480093f4SDimitry Andric   // Check whether Ptr is hidden behind a bitcast and look through it
83480093f4SDimitry Andric   void lookThroughBitcast(Value *&Ptr);
84480093f4SDimitry Andric   // Check for a getelementptr and deduce base and offsets from it, on success
85480093f4SDimitry Andric   // returning the base directly and the offsets indirectly using the Offsets
86480093f4SDimitry Andric   // argument
87e8d8bef9SDimitry Andric   Value *checkGEP(Value *&Offsets, FixedVectorType *Ty, GetElementPtrInst *GEP,
885ffd83dbSDimitry Andric                   IRBuilder<> &Builder);
895ffd83dbSDimitry Andric   // Compute the scale of this gather/scatter instruction
905ffd83dbSDimitry Andric   int computeScale(unsigned GEPElemSize, unsigned MemoryElemSize);
915ffd83dbSDimitry Andric   // If the value is a constant, or derived from constants via additions
925ffd83dbSDimitry Andric   // and multilications, return its numeric value
935ffd83dbSDimitry Andric   Optional<int64_t> getIfConst(const Value *V);
945ffd83dbSDimitry Andric   // If Inst is an add instruction, check whether one summand is a
955ffd83dbSDimitry Andric   // constant. If so, scale this constant and return it together with
965ffd83dbSDimitry Andric   // the other summand.
975ffd83dbSDimitry Andric   std::pair<Value *, int64_t> getVarAndConst(Value *Inst, int TypeScale);
98480093f4SDimitry Andric 
995ffd83dbSDimitry Andric   Value *lowerGather(IntrinsicInst *I);
100480093f4SDimitry Andric   // Create a gather from a base + vector of offsets
101480093f4SDimitry Andric   Value *tryCreateMaskedGatherOffset(IntrinsicInst *I, Value *Ptr,
1025ffd83dbSDimitry Andric                                      Instruction *&Root, IRBuilder<> &Builder);
103480093f4SDimitry Andric   // Create a gather from a vector of pointers
104480093f4SDimitry Andric   Value *tryCreateMaskedGatherBase(IntrinsicInst *I, Value *Ptr,
1055ffd83dbSDimitry Andric                                    IRBuilder<> &Builder, int64_t Increment = 0);
1065ffd83dbSDimitry Andric   // Create an incrementing gather from a vector of pointers
1075ffd83dbSDimitry Andric   Value *tryCreateMaskedGatherBaseWB(IntrinsicInst *I, Value *Ptr,
1085ffd83dbSDimitry Andric                                      IRBuilder<> &Builder,
1095ffd83dbSDimitry Andric                                      int64_t Increment = 0);
1105ffd83dbSDimitry Andric 
1115ffd83dbSDimitry Andric   Value *lowerScatter(IntrinsicInst *I);
1125ffd83dbSDimitry Andric   // Create a scatter to a base + vector of offsets
1135ffd83dbSDimitry Andric   Value *tryCreateMaskedScatterOffset(IntrinsicInst *I, Value *Offsets,
1145ffd83dbSDimitry Andric                                       IRBuilder<> &Builder);
1155ffd83dbSDimitry Andric   // Create a scatter to a vector of pointers
1165ffd83dbSDimitry Andric   Value *tryCreateMaskedScatterBase(IntrinsicInst *I, Value *Ptr,
1175ffd83dbSDimitry Andric                                     IRBuilder<> &Builder,
1185ffd83dbSDimitry Andric                                     int64_t Increment = 0);
1195ffd83dbSDimitry Andric   // Create an incrementing scatter from a vector of pointers
1205ffd83dbSDimitry Andric   Value *tryCreateMaskedScatterBaseWB(IntrinsicInst *I, Value *Ptr,
1215ffd83dbSDimitry Andric                                       IRBuilder<> &Builder,
1225ffd83dbSDimitry Andric                                       int64_t Increment = 0);
1235ffd83dbSDimitry Andric 
1245ffd83dbSDimitry Andric   // QI gathers and scatters can increment their offsets on their own if
1255ffd83dbSDimitry Andric   // the increment is a constant value (digit)
1265ffd83dbSDimitry Andric   Value *tryCreateIncrementingGatScat(IntrinsicInst *I, Value *BasePtr,
1275ffd83dbSDimitry Andric                                       Value *Ptr, GetElementPtrInst *GEP,
1285ffd83dbSDimitry Andric                                       IRBuilder<> &Builder);
1295ffd83dbSDimitry Andric   // QI gathers/scatters can increment their offsets on their own if the
1305ffd83dbSDimitry Andric   // increment is a constant value (digit) - this creates a writeback QI
1315ffd83dbSDimitry Andric   // gather/scatter
1325ffd83dbSDimitry Andric   Value *tryCreateIncrementingWBGatScat(IntrinsicInst *I, Value *BasePtr,
1335ffd83dbSDimitry Andric                                         Value *Ptr, unsigned TypeScale,
1345ffd83dbSDimitry Andric                                         IRBuilder<> &Builder);
135e8d8bef9SDimitry Andric 
136e8d8bef9SDimitry Andric   // Optimise the base and offsets of the given address
137e8d8bef9SDimitry Andric   bool optimiseAddress(Value *Address, BasicBlock *BB, LoopInfo *LI);
138e8d8bef9SDimitry Andric   // Try to fold consecutive geps together into one
139e8d8bef9SDimitry Andric   Value *foldGEP(GetElementPtrInst *GEP, Value *&Offsets, IRBuilder<> &Builder);
1405ffd83dbSDimitry Andric   // Check whether these offsets could be moved out of the loop they're in
1415ffd83dbSDimitry Andric   bool optimiseOffsets(Value *Offsets, BasicBlock *BB, LoopInfo *LI);
1425ffd83dbSDimitry Andric   // Pushes the given add out of the loop
1435ffd83dbSDimitry Andric   void pushOutAdd(PHINode *&Phi, Value *OffsSecondOperand, unsigned StartIndex);
1445ffd83dbSDimitry Andric   // Pushes the given mul out of the loop
1455ffd83dbSDimitry Andric   void pushOutMul(PHINode *&Phi, Value *IncrementPerRound,
1465ffd83dbSDimitry Andric                   Value *OffsSecondOperand, unsigned LoopIncrement,
1475ffd83dbSDimitry Andric                   IRBuilder<> &Builder);
148480093f4SDimitry Andric };
149480093f4SDimitry Andric 
150480093f4SDimitry Andric } // end anonymous namespace
151480093f4SDimitry Andric 
152480093f4SDimitry Andric char MVEGatherScatterLowering::ID = 0;
153480093f4SDimitry Andric 
154480093f4SDimitry Andric INITIALIZE_PASS(MVEGatherScatterLowering, DEBUG_TYPE,
155480093f4SDimitry Andric                 "MVE gather/scattering lowering pass", false, false)
156480093f4SDimitry Andric 
157480093f4SDimitry Andric Pass *llvm::createMVEGatherScatterLoweringPass() {
158480093f4SDimitry Andric   return new MVEGatherScatterLowering();
159480093f4SDimitry Andric }
160480093f4SDimitry Andric 
161480093f4SDimitry Andric bool MVEGatherScatterLowering::isLegalTypeAndAlignment(unsigned NumElements,
162480093f4SDimitry Andric                                                        unsigned ElemSize,
1635ffd83dbSDimitry Andric                                                        Align Alignment) {
1645ffd83dbSDimitry Andric   if (((NumElements == 4 &&
1655ffd83dbSDimitry Andric         (ElemSize == 32 || ElemSize == 16 || ElemSize == 8)) ||
1665ffd83dbSDimitry Andric        (NumElements == 8 && (ElemSize == 16 || ElemSize == 8)) ||
167480093f4SDimitry Andric        (NumElements == 16 && ElemSize == 8)) &&
1685ffd83dbSDimitry Andric       Alignment >= ElemSize / 8)
169480093f4SDimitry Andric     return true;
1705ffd83dbSDimitry Andric   LLVM_DEBUG(dbgs() << "masked gathers/scatters: instruction does not have "
1715ffd83dbSDimitry Andric                     << "valid alignment or vector type \n");
172480093f4SDimitry Andric   return false;
173480093f4SDimitry Andric }
174480093f4SDimitry Andric 
175e8d8bef9SDimitry Andric static bool checkOffsetSize(Value *Offsets, unsigned TargetElemCount) {
176e8d8bef9SDimitry Andric   // Offsets that are not of type <N x i32> are sign extended by the
177e8d8bef9SDimitry Andric   // getelementptr instruction, and MVE gathers/scatters treat the offset as
178e8d8bef9SDimitry Andric   // unsigned. Thus, if the element size is smaller than 32, we can only allow
179e8d8bef9SDimitry Andric   // positive offsets - i.e., the offsets are not allowed to be variables we
180e8d8bef9SDimitry Andric   // can't look into.
181e8d8bef9SDimitry Andric   // Additionally, <N x i32> offsets have to either originate from a zext of a
182e8d8bef9SDimitry Andric   // vector with element types smaller or equal the type of the gather we're
183e8d8bef9SDimitry Andric   // looking at, or consist of constants that we can check are small enough
184e8d8bef9SDimitry Andric   // to fit into the gather type.
185e8d8bef9SDimitry Andric   // Thus we check that 0 < value < 2^TargetElemSize.
186e8d8bef9SDimitry Andric   unsigned TargetElemSize = 128 / TargetElemCount;
187e8d8bef9SDimitry Andric   unsigned OffsetElemSize = cast<FixedVectorType>(Offsets->getType())
188e8d8bef9SDimitry Andric                                 ->getElementType()
189e8d8bef9SDimitry Andric                                 ->getScalarSizeInBits();
190e8d8bef9SDimitry Andric   if (OffsetElemSize != TargetElemSize || OffsetElemSize != 32) {
191e8d8bef9SDimitry Andric     Constant *ConstOff = dyn_cast<Constant>(Offsets);
192e8d8bef9SDimitry Andric     if (!ConstOff)
193e8d8bef9SDimitry Andric       return false;
194e8d8bef9SDimitry Andric     int64_t TargetElemMaxSize = (1ULL << TargetElemSize);
195e8d8bef9SDimitry Andric     auto CheckValueSize = [TargetElemMaxSize](Value *OffsetElem) {
196e8d8bef9SDimitry Andric       ConstantInt *OConst = dyn_cast<ConstantInt>(OffsetElem);
197e8d8bef9SDimitry Andric       if (!OConst)
198e8d8bef9SDimitry Andric         return false;
199e8d8bef9SDimitry Andric       int SExtValue = OConst->getSExtValue();
200e8d8bef9SDimitry Andric       if (SExtValue >= TargetElemMaxSize || SExtValue < 0)
201e8d8bef9SDimitry Andric         return false;
202e8d8bef9SDimitry Andric       return true;
203e8d8bef9SDimitry Andric     };
204e8d8bef9SDimitry Andric     if (isa<FixedVectorType>(ConstOff->getType())) {
205e8d8bef9SDimitry Andric       for (unsigned i = 0; i < TargetElemCount; i++) {
206e8d8bef9SDimitry Andric         if (!CheckValueSize(ConstOff->getAggregateElement(i)))
207e8d8bef9SDimitry Andric           return false;
208e8d8bef9SDimitry Andric       }
209e8d8bef9SDimitry Andric     } else {
210e8d8bef9SDimitry Andric       if (!CheckValueSize(ConstOff))
211e8d8bef9SDimitry Andric         return false;
212e8d8bef9SDimitry Andric     }
213e8d8bef9SDimitry Andric   }
214e8d8bef9SDimitry Andric   return true;
215e8d8bef9SDimitry Andric }
216e8d8bef9SDimitry Andric 
217e8d8bef9SDimitry Andric Value *MVEGatherScatterLowering::checkGEP(Value *&Offsets, FixedVectorType *Ty,
2185ffd83dbSDimitry Andric                                           GetElementPtrInst *GEP,
2195ffd83dbSDimitry Andric                                           IRBuilder<> &Builder) {
220480093f4SDimitry Andric   if (!GEP) {
2215ffd83dbSDimitry Andric     LLVM_DEBUG(
2225ffd83dbSDimitry Andric         dbgs() << "masked gathers/scatters: no getelementpointer found\n");
223480093f4SDimitry Andric     return nullptr;
224480093f4SDimitry Andric   }
2255ffd83dbSDimitry Andric   LLVM_DEBUG(dbgs() << "masked gathers/scatters: getelementpointer found."
2265ffd83dbSDimitry Andric                     << " Looking at intrinsic for base + vector of offsets\n");
227480093f4SDimitry Andric   Value *GEPPtr = GEP->getPointerOperand();
228e8d8bef9SDimitry Andric   Offsets = GEP->getOperand(1);
229e8d8bef9SDimitry Andric   if (GEPPtr->getType()->isVectorTy() ||
230e8d8bef9SDimitry Andric       !isa<FixedVectorType>(Offsets->getType()))
231480093f4SDimitry Andric     return nullptr;
232e8d8bef9SDimitry Andric 
233480093f4SDimitry Andric   if (GEP->getNumOperands() != 2) {
2345ffd83dbSDimitry Andric     LLVM_DEBUG(dbgs() << "masked gathers/scatters: getelementptr with too many"
235480093f4SDimitry Andric                       << " operands. Expanding.\n");
236480093f4SDimitry Andric     return nullptr;
237480093f4SDimitry Andric   }
238480093f4SDimitry Andric   Offsets = GEP->getOperand(1);
239e8d8bef9SDimitry Andric   unsigned OffsetsElemCount =
240e8d8bef9SDimitry Andric       cast<FixedVectorType>(Offsets->getType())->getNumElements();
2415ffd83dbSDimitry Andric   // Paranoid check whether the number of parallel lanes is the same
242e8d8bef9SDimitry Andric   assert(Ty->getNumElements() == OffsetsElemCount);
243e8d8bef9SDimitry Andric 
244e8d8bef9SDimitry Andric   ZExtInst *ZextOffs = dyn_cast<ZExtInst>(Offsets);
245e8d8bef9SDimitry Andric   if (ZextOffs)
246480093f4SDimitry Andric     Offsets = ZextOffs->getOperand(0);
247e8d8bef9SDimitry Andric   FixedVectorType *OffsetType = cast<FixedVectorType>(Offsets->getType());
248e8d8bef9SDimitry Andric 
249e8d8bef9SDimitry Andric   // If the offsets are already being zext-ed to <N x i32>, that relieves us of
250e8d8bef9SDimitry Andric   // having to make sure that they won't overflow.
251e8d8bef9SDimitry Andric   if (!ZextOffs || cast<FixedVectorType>(ZextOffs->getDestTy())
252e8d8bef9SDimitry Andric                            ->getElementType()
253e8d8bef9SDimitry Andric                            ->getScalarSizeInBits() != 32)
254e8d8bef9SDimitry Andric     if (!checkOffsetSize(Offsets, OffsetsElemCount))
255480093f4SDimitry Andric       return nullptr;
2565ffd83dbSDimitry Andric 
257e8d8bef9SDimitry Andric   // The offset sizes have been checked; if any truncating or zext-ing is
258e8d8bef9SDimitry Andric   // required to fix them, do that now
2595ffd83dbSDimitry Andric   if (Ty != Offsets->getType()) {
260e8d8bef9SDimitry Andric     if ((Ty->getElementType()->getScalarSizeInBits() <
261e8d8bef9SDimitry Andric          OffsetType->getElementType()->getScalarSizeInBits())) {
262e8d8bef9SDimitry Andric       Offsets = Builder.CreateTrunc(Offsets, Ty);
2635ffd83dbSDimitry Andric     } else {
264e8d8bef9SDimitry Andric       Offsets = Builder.CreateZExt(Offsets, VectorType::getInteger(Ty));
265480093f4SDimitry Andric     }
266480093f4SDimitry Andric   }
267480093f4SDimitry Andric   // If none of the checks failed, return the gep's base pointer
2685ffd83dbSDimitry Andric   LLVM_DEBUG(dbgs() << "masked gathers/scatters: found correct offsets\n");
269480093f4SDimitry Andric   return GEPPtr;
270480093f4SDimitry Andric }
271480093f4SDimitry Andric 
272480093f4SDimitry Andric void MVEGatherScatterLowering::lookThroughBitcast(Value *&Ptr) {
273480093f4SDimitry Andric   // Look through bitcast instruction if #elements is the same
274480093f4SDimitry Andric   if (auto *BitCast = dyn_cast<BitCastInst>(Ptr)) {
2755ffd83dbSDimitry Andric     auto *BCTy = cast<FixedVectorType>(BitCast->getType());
2765ffd83dbSDimitry Andric     auto *BCSrcTy = cast<FixedVectorType>(BitCast->getOperand(0)->getType());
2775ffd83dbSDimitry Andric     if (BCTy->getNumElements() == BCSrcTy->getNumElements()) {
2785ffd83dbSDimitry Andric       LLVM_DEBUG(
2795ffd83dbSDimitry Andric           dbgs() << "masked gathers/scatters: looking through bitcast\n");
280480093f4SDimitry Andric       Ptr = BitCast->getOperand(0);
281480093f4SDimitry Andric     }
282480093f4SDimitry Andric   }
283480093f4SDimitry Andric }
284480093f4SDimitry Andric 
2855ffd83dbSDimitry Andric int MVEGatherScatterLowering::computeScale(unsigned GEPElemSize,
2865ffd83dbSDimitry Andric                                            unsigned MemoryElemSize) {
2875ffd83dbSDimitry Andric   // This can be a 32bit load/store scaled by 4, a 16bit load/store scaled by 2,
2885ffd83dbSDimitry Andric   // or a 8bit, 16bit or 32bit load/store scaled by 1
2895ffd83dbSDimitry Andric   if (GEPElemSize == 32 && MemoryElemSize == 32)
2905ffd83dbSDimitry Andric     return 2;
2915ffd83dbSDimitry Andric   else if (GEPElemSize == 16 && MemoryElemSize == 16)
2925ffd83dbSDimitry Andric     return 1;
2935ffd83dbSDimitry Andric   else if (GEPElemSize == 8)
2945ffd83dbSDimitry Andric     return 0;
2955ffd83dbSDimitry Andric   LLVM_DEBUG(dbgs() << "masked gathers/scatters: incorrect scale. Can't "
2965ffd83dbSDimitry Andric                     << "create intrinsic\n");
2975ffd83dbSDimitry Andric   return -1;
2985ffd83dbSDimitry Andric }
2995ffd83dbSDimitry Andric 
3005ffd83dbSDimitry Andric Optional<int64_t> MVEGatherScatterLowering::getIfConst(const Value *V) {
3015ffd83dbSDimitry Andric   const Constant *C = dyn_cast<Constant>(V);
3025ffd83dbSDimitry Andric   if (C != nullptr)
3035ffd83dbSDimitry Andric     return Optional<int64_t>{C->getUniqueInteger().getSExtValue()};
3045ffd83dbSDimitry Andric   if (!isa<Instruction>(V))
3055ffd83dbSDimitry Andric     return Optional<int64_t>{};
3065ffd83dbSDimitry Andric 
3075ffd83dbSDimitry Andric   const Instruction *I = cast<Instruction>(V);
3085ffd83dbSDimitry Andric   if (I->getOpcode() == Instruction::Add ||
3095ffd83dbSDimitry Andric               I->getOpcode() == Instruction::Mul) {
3105ffd83dbSDimitry Andric     Optional<int64_t> Op0 = getIfConst(I->getOperand(0));
3115ffd83dbSDimitry Andric     Optional<int64_t> Op1 = getIfConst(I->getOperand(1));
3125ffd83dbSDimitry Andric     if (!Op0 || !Op1)
3135ffd83dbSDimitry Andric       return Optional<int64_t>{};
3145ffd83dbSDimitry Andric     if (I->getOpcode() == Instruction::Add)
3155ffd83dbSDimitry Andric       return Optional<int64_t>{Op0.getValue() + Op1.getValue()};
3165ffd83dbSDimitry Andric     if (I->getOpcode() == Instruction::Mul)
3175ffd83dbSDimitry Andric       return Optional<int64_t>{Op0.getValue() * Op1.getValue()};
3185ffd83dbSDimitry Andric   }
3195ffd83dbSDimitry Andric   return Optional<int64_t>{};
3205ffd83dbSDimitry Andric }
3215ffd83dbSDimitry Andric 
3225ffd83dbSDimitry Andric std::pair<Value *, int64_t>
3235ffd83dbSDimitry Andric MVEGatherScatterLowering::getVarAndConst(Value *Inst, int TypeScale) {
3245ffd83dbSDimitry Andric   std::pair<Value *, int64_t> ReturnFalse =
3255ffd83dbSDimitry Andric       std::pair<Value *, int64_t>(nullptr, 0);
3265ffd83dbSDimitry Andric   // At this point, the instruction we're looking at must be an add or we
3275ffd83dbSDimitry Andric   // bail out
3285ffd83dbSDimitry Andric   Instruction *Add = dyn_cast<Instruction>(Inst);
3295ffd83dbSDimitry Andric   if (Add == nullptr || Add->getOpcode() != Instruction::Add)
3305ffd83dbSDimitry Andric     return ReturnFalse;
3315ffd83dbSDimitry Andric 
3325ffd83dbSDimitry Andric   Value *Summand;
3335ffd83dbSDimitry Andric   Optional<int64_t> Const;
3345ffd83dbSDimitry Andric   // Find out which operand the value that is increased is
3355ffd83dbSDimitry Andric   if ((Const = getIfConst(Add->getOperand(0))))
3365ffd83dbSDimitry Andric     Summand = Add->getOperand(1);
3375ffd83dbSDimitry Andric   else if ((Const = getIfConst(Add->getOperand(1))))
3385ffd83dbSDimitry Andric     Summand = Add->getOperand(0);
3395ffd83dbSDimitry Andric   else
3405ffd83dbSDimitry Andric     return ReturnFalse;
3415ffd83dbSDimitry Andric 
3425ffd83dbSDimitry Andric   // Check that the constant is small enough for an incrementing gather
3435ffd83dbSDimitry Andric   int64_t Immediate = Const.getValue() << TypeScale;
3445ffd83dbSDimitry Andric   if (Immediate > 512 || Immediate < -512 || Immediate % 4 != 0)
3455ffd83dbSDimitry Andric     return ReturnFalse;
3465ffd83dbSDimitry Andric 
3475ffd83dbSDimitry Andric   return std::pair<Value *, int64_t>(Summand, Immediate);
3485ffd83dbSDimitry Andric }
3495ffd83dbSDimitry Andric 
3505ffd83dbSDimitry Andric Value *MVEGatherScatterLowering::lowerGather(IntrinsicInst *I) {
351480093f4SDimitry Andric   using namespace PatternMatch;
352480093f4SDimitry Andric   LLVM_DEBUG(dbgs() << "masked gathers: checking transform preconditions\n");
353480093f4SDimitry Andric 
354480093f4SDimitry Andric   // @llvm.masked.gather.*(Ptrs, alignment, Mask, Src0)
355480093f4SDimitry Andric   // Attempt to turn the masked gather in I into a MVE intrinsic
356480093f4SDimitry Andric   // Potentially optimising the addressing modes as we do so.
3575ffd83dbSDimitry Andric   auto *Ty = cast<FixedVectorType>(I->getType());
358480093f4SDimitry Andric   Value *Ptr = I->getArgOperand(0);
3595ffd83dbSDimitry Andric   Align Alignment = cast<ConstantInt>(I->getArgOperand(1))->getAlignValue();
360480093f4SDimitry Andric   Value *Mask = I->getArgOperand(2);
361480093f4SDimitry Andric   Value *PassThru = I->getArgOperand(3);
362480093f4SDimitry Andric 
3635ffd83dbSDimitry Andric   if (!isLegalTypeAndAlignment(Ty->getNumElements(), Ty->getScalarSizeInBits(),
3645ffd83dbSDimitry Andric                                Alignment))
3655ffd83dbSDimitry Andric     return nullptr;
366480093f4SDimitry Andric   lookThroughBitcast(Ptr);
367480093f4SDimitry Andric   assert(Ptr->getType()->isVectorTy() && "Unexpected pointer type");
368480093f4SDimitry Andric 
369480093f4SDimitry Andric   IRBuilder<> Builder(I->getContext());
370480093f4SDimitry Andric   Builder.SetInsertPoint(I);
371480093f4SDimitry Andric   Builder.SetCurrentDebugLocation(I->getDebugLoc());
3725ffd83dbSDimitry Andric 
3735ffd83dbSDimitry Andric   Instruction *Root = I;
3745ffd83dbSDimitry Andric   Value *Load = tryCreateMaskedGatherOffset(I, Ptr, Root, Builder);
375480093f4SDimitry Andric   if (!Load)
376480093f4SDimitry Andric     Load = tryCreateMaskedGatherBase(I, Ptr, Builder);
377480093f4SDimitry Andric   if (!Load)
3785ffd83dbSDimitry Andric     return nullptr;
379480093f4SDimitry Andric 
380480093f4SDimitry Andric   if (!isa<UndefValue>(PassThru) && !match(PassThru, m_Zero())) {
381480093f4SDimitry Andric     LLVM_DEBUG(dbgs() << "masked gathers: found non-trivial passthru - "
382480093f4SDimitry Andric                       << "creating select\n");
383480093f4SDimitry Andric     Load = Builder.CreateSelect(Mask, Load, PassThru);
384480093f4SDimitry Andric   }
385480093f4SDimitry Andric 
3865ffd83dbSDimitry Andric   Root->replaceAllUsesWith(Load);
3875ffd83dbSDimitry Andric   Root->eraseFromParent();
3885ffd83dbSDimitry Andric   if (Root != I)
3895ffd83dbSDimitry Andric     // If this was an extending gather, we need to get rid of the sext/zext
3905ffd83dbSDimitry Andric     // sext/zext as well as of the gather itself
391480093f4SDimitry Andric     I->eraseFromParent();
3925ffd83dbSDimitry Andric 
3935ffd83dbSDimitry Andric   LLVM_DEBUG(dbgs() << "masked gathers: successfully built masked gather\n");
3945ffd83dbSDimitry Andric   return Load;
395480093f4SDimitry Andric }
396480093f4SDimitry Andric 
3975ffd83dbSDimitry Andric Value *MVEGatherScatterLowering::tryCreateMaskedGatherBase(IntrinsicInst *I,
3985ffd83dbSDimitry Andric                                                            Value *Ptr,
3995ffd83dbSDimitry Andric                                                            IRBuilder<> &Builder,
4005ffd83dbSDimitry Andric                                                            int64_t Increment) {
401480093f4SDimitry Andric   using namespace PatternMatch;
4025ffd83dbSDimitry Andric   auto *Ty = cast<FixedVectorType>(I->getType());
403480093f4SDimitry Andric   LLVM_DEBUG(dbgs() << "masked gathers: loading from vector of pointers\n");
4045ffd83dbSDimitry Andric   if (Ty->getNumElements() != 4 || Ty->getScalarSizeInBits() != 32)
405480093f4SDimitry Andric     // Can't build an intrinsic for this
406480093f4SDimitry Andric     return nullptr;
407480093f4SDimitry Andric   Value *Mask = I->getArgOperand(2);
408480093f4SDimitry Andric   if (match(Mask, m_One()))
409480093f4SDimitry Andric     return Builder.CreateIntrinsic(Intrinsic::arm_mve_vldr_gather_base,
410480093f4SDimitry Andric                                    {Ty, Ptr->getType()},
4115ffd83dbSDimitry Andric                                    {Ptr, Builder.getInt32(Increment)});
412480093f4SDimitry Andric   else
413480093f4SDimitry Andric     return Builder.CreateIntrinsic(
414480093f4SDimitry Andric         Intrinsic::arm_mve_vldr_gather_base_predicated,
415480093f4SDimitry Andric         {Ty, Ptr->getType(), Mask->getType()},
4165ffd83dbSDimitry Andric         {Ptr, Builder.getInt32(Increment), Mask});
4175ffd83dbSDimitry Andric }
4185ffd83dbSDimitry Andric 
4195ffd83dbSDimitry Andric Value *MVEGatherScatterLowering::tryCreateMaskedGatherBaseWB(
4205ffd83dbSDimitry Andric     IntrinsicInst *I, Value *Ptr, IRBuilder<> &Builder, int64_t Increment) {
4215ffd83dbSDimitry Andric   using namespace PatternMatch;
4225ffd83dbSDimitry Andric   auto *Ty = cast<FixedVectorType>(I->getType());
4235ffd83dbSDimitry Andric   LLVM_DEBUG(
4245ffd83dbSDimitry Andric       dbgs()
4255ffd83dbSDimitry Andric       << "masked gathers: loading from vector of pointers with writeback\n");
4265ffd83dbSDimitry Andric   if (Ty->getNumElements() != 4 || Ty->getScalarSizeInBits() != 32)
4275ffd83dbSDimitry Andric     // Can't build an intrinsic for this
4285ffd83dbSDimitry Andric     return nullptr;
4295ffd83dbSDimitry Andric   Value *Mask = I->getArgOperand(2);
4305ffd83dbSDimitry Andric   if (match(Mask, m_One()))
4315ffd83dbSDimitry Andric     return Builder.CreateIntrinsic(Intrinsic::arm_mve_vldr_gather_base_wb,
4325ffd83dbSDimitry Andric                                    {Ty, Ptr->getType()},
4335ffd83dbSDimitry Andric                                    {Ptr, Builder.getInt32(Increment)});
4345ffd83dbSDimitry Andric   else
4355ffd83dbSDimitry Andric     return Builder.CreateIntrinsic(
4365ffd83dbSDimitry Andric         Intrinsic::arm_mve_vldr_gather_base_wb_predicated,
4375ffd83dbSDimitry Andric         {Ty, Ptr->getType(), Mask->getType()},
4385ffd83dbSDimitry Andric         {Ptr, Builder.getInt32(Increment), Mask});
439480093f4SDimitry Andric }
440480093f4SDimitry Andric 
441480093f4SDimitry Andric Value *MVEGatherScatterLowering::tryCreateMaskedGatherOffset(
4425ffd83dbSDimitry Andric     IntrinsicInst *I, Value *Ptr, Instruction *&Root, IRBuilder<> &Builder) {
443480093f4SDimitry Andric   using namespace PatternMatch;
4445ffd83dbSDimitry Andric 
4455ffd83dbSDimitry Andric   Type *OriginalTy = I->getType();
4465ffd83dbSDimitry Andric   Type *ResultTy = OriginalTy;
4475ffd83dbSDimitry Andric 
4485ffd83dbSDimitry Andric   unsigned Unsigned = 1;
4495ffd83dbSDimitry Andric   // The size of the gather was already checked in isLegalTypeAndAlignment;
4505ffd83dbSDimitry Andric   // if it was not a full vector width an appropriate extend should follow.
4515ffd83dbSDimitry Andric   auto *Extend = Root;
4525ffd83dbSDimitry Andric   if (OriginalTy->getPrimitiveSizeInBits() < 128) {
4535ffd83dbSDimitry Andric     // Only transform gathers with exactly one use
4545ffd83dbSDimitry Andric     if (!I->hasOneUse())
455480093f4SDimitry Andric       return nullptr;
456480093f4SDimitry Andric 
4575ffd83dbSDimitry Andric     // The correct root to replace is not the CallInst itself, but the
4585ffd83dbSDimitry Andric     // instruction which extends it
4595ffd83dbSDimitry Andric     Extend = cast<Instruction>(*I->users().begin());
4605ffd83dbSDimitry Andric     if (isa<SExtInst>(Extend)) {
4615ffd83dbSDimitry Andric       Unsigned = 0;
4625ffd83dbSDimitry Andric     } else if (!isa<ZExtInst>(Extend)) {
4635ffd83dbSDimitry Andric       LLVM_DEBUG(dbgs() << "masked gathers: extend needed but not provided. "
4645ffd83dbSDimitry Andric                         << "Expanding\n");
465480093f4SDimitry Andric       return nullptr;
466480093f4SDimitry Andric     }
4675ffd83dbSDimitry Andric     LLVM_DEBUG(dbgs() << "masked gathers: found an extending gather\n");
4685ffd83dbSDimitry Andric     ResultTy = Extend->getType();
4695ffd83dbSDimitry Andric     // The final size of the gather must be a full vector width
4705ffd83dbSDimitry Andric     if (ResultTy->getPrimitiveSizeInBits() != 128) {
4715ffd83dbSDimitry Andric       LLVM_DEBUG(dbgs() << "masked gathers: extending from the wrong type. "
4725ffd83dbSDimitry Andric                         << "Expanding\n");
4735ffd83dbSDimitry Andric       return nullptr;
4745ffd83dbSDimitry Andric     }
4755ffd83dbSDimitry Andric   }
4765ffd83dbSDimitry Andric 
4775ffd83dbSDimitry Andric   GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(Ptr);
4785ffd83dbSDimitry Andric   Value *Offsets;
479e8d8bef9SDimitry Andric   Value *BasePtr =
480e8d8bef9SDimitry Andric       checkGEP(Offsets, cast<FixedVectorType>(ResultTy), GEP, Builder);
4815ffd83dbSDimitry Andric   if (!BasePtr)
4825ffd83dbSDimitry Andric     return nullptr;
4835ffd83dbSDimitry Andric   // Check whether the offset is a constant increment that could be merged into
4845ffd83dbSDimitry Andric   // a QI gather
4855ffd83dbSDimitry Andric   Value *Load = tryCreateIncrementingGatScat(I, BasePtr, Offsets, GEP, Builder);
4865ffd83dbSDimitry Andric   if (Load)
4875ffd83dbSDimitry Andric     return Load;
4885ffd83dbSDimitry Andric 
4895ffd83dbSDimitry Andric   int Scale = computeScale(
4905ffd83dbSDimitry Andric       BasePtr->getType()->getPointerElementType()->getPrimitiveSizeInBits(),
4915ffd83dbSDimitry Andric       OriginalTy->getScalarSizeInBits());
4925ffd83dbSDimitry Andric   if (Scale == -1)
4935ffd83dbSDimitry Andric     return nullptr;
4945ffd83dbSDimitry Andric   Root = Extend;
495480093f4SDimitry Andric 
496480093f4SDimitry Andric   Value *Mask = I->getArgOperand(2);
497480093f4SDimitry Andric   if (!match(Mask, m_One()))
498480093f4SDimitry Andric     return Builder.CreateIntrinsic(
499480093f4SDimitry Andric         Intrinsic::arm_mve_vldr_gather_offset_predicated,
5005ffd83dbSDimitry Andric         {ResultTy, BasePtr->getType(), Offsets->getType(), Mask->getType()},
5015ffd83dbSDimitry Andric         {BasePtr, Offsets, Builder.getInt32(OriginalTy->getScalarSizeInBits()),
5025ffd83dbSDimitry Andric          Builder.getInt32(Scale), Builder.getInt32(Unsigned), Mask});
503480093f4SDimitry Andric   else
504480093f4SDimitry Andric     return Builder.CreateIntrinsic(
505480093f4SDimitry Andric         Intrinsic::arm_mve_vldr_gather_offset,
5065ffd83dbSDimitry Andric         {ResultTy, BasePtr->getType(), Offsets->getType()},
5075ffd83dbSDimitry Andric         {BasePtr, Offsets, Builder.getInt32(OriginalTy->getScalarSizeInBits()),
5085ffd83dbSDimitry Andric          Builder.getInt32(Scale), Builder.getInt32(Unsigned)});
5095ffd83dbSDimitry Andric }
5105ffd83dbSDimitry Andric 
5115ffd83dbSDimitry Andric Value *MVEGatherScatterLowering::lowerScatter(IntrinsicInst *I) {
5125ffd83dbSDimitry Andric   using namespace PatternMatch;
5135ffd83dbSDimitry Andric   LLVM_DEBUG(dbgs() << "masked scatters: checking transform preconditions\n");
5145ffd83dbSDimitry Andric 
5155ffd83dbSDimitry Andric   // @llvm.masked.scatter.*(data, ptrs, alignment, mask)
5165ffd83dbSDimitry Andric   // Attempt to turn the masked scatter in I into a MVE intrinsic
5175ffd83dbSDimitry Andric   // Potentially optimising the addressing modes as we do so.
5185ffd83dbSDimitry Andric   Value *Input = I->getArgOperand(0);
5195ffd83dbSDimitry Andric   Value *Ptr = I->getArgOperand(1);
5205ffd83dbSDimitry Andric   Align Alignment = cast<ConstantInt>(I->getArgOperand(2))->getAlignValue();
5215ffd83dbSDimitry Andric   auto *Ty = cast<FixedVectorType>(Input->getType());
5225ffd83dbSDimitry Andric 
5235ffd83dbSDimitry Andric   if (!isLegalTypeAndAlignment(Ty->getNumElements(), Ty->getScalarSizeInBits(),
5245ffd83dbSDimitry Andric                                Alignment))
5255ffd83dbSDimitry Andric     return nullptr;
5265ffd83dbSDimitry Andric 
5275ffd83dbSDimitry Andric   lookThroughBitcast(Ptr);
5285ffd83dbSDimitry Andric   assert(Ptr->getType()->isVectorTy() && "Unexpected pointer type");
5295ffd83dbSDimitry Andric 
5305ffd83dbSDimitry Andric   IRBuilder<> Builder(I->getContext());
5315ffd83dbSDimitry Andric   Builder.SetInsertPoint(I);
5325ffd83dbSDimitry Andric   Builder.SetCurrentDebugLocation(I->getDebugLoc());
5335ffd83dbSDimitry Andric 
5345ffd83dbSDimitry Andric   Value *Store = tryCreateMaskedScatterOffset(I, Ptr, Builder);
5355ffd83dbSDimitry Andric   if (!Store)
5365ffd83dbSDimitry Andric     Store = tryCreateMaskedScatterBase(I, Ptr, Builder);
5375ffd83dbSDimitry Andric   if (!Store)
5385ffd83dbSDimitry Andric     return nullptr;
5395ffd83dbSDimitry Andric 
5405ffd83dbSDimitry Andric   LLVM_DEBUG(dbgs() << "masked scatters: successfully built masked scatter\n");
5415ffd83dbSDimitry Andric   I->eraseFromParent();
5425ffd83dbSDimitry Andric   return Store;
5435ffd83dbSDimitry Andric }
5445ffd83dbSDimitry Andric 
5455ffd83dbSDimitry Andric Value *MVEGatherScatterLowering::tryCreateMaskedScatterBase(
5465ffd83dbSDimitry Andric     IntrinsicInst *I, Value *Ptr, IRBuilder<> &Builder, int64_t Increment) {
5475ffd83dbSDimitry Andric   using namespace PatternMatch;
5485ffd83dbSDimitry Andric   Value *Input = I->getArgOperand(0);
5495ffd83dbSDimitry Andric   auto *Ty = cast<FixedVectorType>(Input->getType());
5505ffd83dbSDimitry Andric   // Only QR variants allow truncating
5515ffd83dbSDimitry Andric   if (!(Ty->getNumElements() == 4 && Ty->getScalarSizeInBits() == 32)) {
5525ffd83dbSDimitry Andric     // Can't build an intrinsic for this
5535ffd83dbSDimitry Andric     return nullptr;
5545ffd83dbSDimitry Andric   }
5555ffd83dbSDimitry Andric   Value *Mask = I->getArgOperand(3);
5565ffd83dbSDimitry Andric   //  int_arm_mve_vstr_scatter_base(_predicated) addr, offset, data(, mask)
5575ffd83dbSDimitry Andric   LLVM_DEBUG(dbgs() << "masked scatters: storing to a vector of pointers\n");
5585ffd83dbSDimitry Andric   if (match(Mask, m_One()))
5595ffd83dbSDimitry Andric     return Builder.CreateIntrinsic(Intrinsic::arm_mve_vstr_scatter_base,
5605ffd83dbSDimitry Andric                                    {Ptr->getType(), Input->getType()},
5615ffd83dbSDimitry Andric                                    {Ptr, Builder.getInt32(Increment), Input});
5625ffd83dbSDimitry Andric   else
5635ffd83dbSDimitry Andric     return Builder.CreateIntrinsic(
5645ffd83dbSDimitry Andric         Intrinsic::arm_mve_vstr_scatter_base_predicated,
5655ffd83dbSDimitry Andric         {Ptr->getType(), Input->getType(), Mask->getType()},
5665ffd83dbSDimitry Andric         {Ptr, Builder.getInt32(Increment), Input, Mask});
5675ffd83dbSDimitry Andric }
5685ffd83dbSDimitry Andric 
5695ffd83dbSDimitry Andric Value *MVEGatherScatterLowering::tryCreateMaskedScatterBaseWB(
5705ffd83dbSDimitry Andric     IntrinsicInst *I, Value *Ptr, IRBuilder<> &Builder, int64_t Increment) {
5715ffd83dbSDimitry Andric   using namespace PatternMatch;
5725ffd83dbSDimitry Andric   Value *Input = I->getArgOperand(0);
5735ffd83dbSDimitry Andric   auto *Ty = cast<FixedVectorType>(Input->getType());
5745ffd83dbSDimitry Andric   LLVM_DEBUG(
5755ffd83dbSDimitry Andric       dbgs()
5765ffd83dbSDimitry Andric       << "masked scatters: storing to a vector of pointers with writeback\n");
5775ffd83dbSDimitry Andric   if (Ty->getNumElements() != 4 || Ty->getScalarSizeInBits() != 32)
5785ffd83dbSDimitry Andric     // Can't build an intrinsic for this
5795ffd83dbSDimitry Andric     return nullptr;
5805ffd83dbSDimitry Andric   Value *Mask = I->getArgOperand(3);
5815ffd83dbSDimitry Andric   if (match(Mask, m_One()))
5825ffd83dbSDimitry Andric     return Builder.CreateIntrinsic(Intrinsic::arm_mve_vstr_scatter_base_wb,
5835ffd83dbSDimitry Andric                                    {Ptr->getType(), Input->getType()},
5845ffd83dbSDimitry Andric                                    {Ptr, Builder.getInt32(Increment), Input});
5855ffd83dbSDimitry Andric   else
5865ffd83dbSDimitry Andric     return Builder.CreateIntrinsic(
5875ffd83dbSDimitry Andric         Intrinsic::arm_mve_vstr_scatter_base_wb_predicated,
5885ffd83dbSDimitry Andric         {Ptr->getType(), Input->getType(), Mask->getType()},
5895ffd83dbSDimitry Andric         {Ptr, Builder.getInt32(Increment), Input, Mask});
5905ffd83dbSDimitry Andric }
5915ffd83dbSDimitry Andric 
5925ffd83dbSDimitry Andric Value *MVEGatherScatterLowering::tryCreateMaskedScatterOffset(
5935ffd83dbSDimitry Andric     IntrinsicInst *I, Value *Ptr, IRBuilder<> &Builder) {
5945ffd83dbSDimitry Andric   using namespace PatternMatch;
5955ffd83dbSDimitry Andric   Value *Input = I->getArgOperand(0);
5965ffd83dbSDimitry Andric   Value *Mask = I->getArgOperand(3);
5975ffd83dbSDimitry Andric   Type *InputTy = Input->getType();
5985ffd83dbSDimitry Andric   Type *MemoryTy = InputTy;
5995ffd83dbSDimitry Andric   LLVM_DEBUG(dbgs() << "masked scatters: getelementpointer found. Storing"
6005ffd83dbSDimitry Andric                     << " to base + vector of offsets\n");
6015ffd83dbSDimitry Andric   // If the input has been truncated, try to integrate that trunc into the
6025ffd83dbSDimitry Andric   // scatter instruction (we don't care about alignment here)
6035ffd83dbSDimitry Andric   if (TruncInst *Trunc = dyn_cast<TruncInst>(Input)) {
6045ffd83dbSDimitry Andric     Value *PreTrunc = Trunc->getOperand(0);
6055ffd83dbSDimitry Andric     Type *PreTruncTy = PreTrunc->getType();
6065ffd83dbSDimitry Andric     if (PreTruncTy->getPrimitiveSizeInBits() == 128) {
6075ffd83dbSDimitry Andric       Input = PreTrunc;
6085ffd83dbSDimitry Andric       InputTy = PreTruncTy;
6095ffd83dbSDimitry Andric     }
6105ffd83dbSDimitry Andric   }
6115ffd83dbSDimitry Andric   if (InputTy->getPrimitiveSizeInBits() != 128) {
6125ffd83dbSDimitry Andric     LLVM_DEBUG(
6135ffd83dbSDimitry Andric         dbgs() << "masked scatters: cannot create scatters for non-standard"
6145ffd83dbSDimitry Andric                << " input types. Expanding.\n");
6155ffd83dbSDimitry Andric     return nullptr;
6165ffd83dbSDimitry Andric   }
6175ffd83dbSDimitry Andric 
6185ffd83dbSDimitry Andric   GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(Ptr);
6195ffd83dbSDimitry Andric   Value *Offsets;
620e8d8bef9SDimitry Andric   Value *BasePtr =
621e8d8bef9SDimitry Andric       checkGEP(Offsets, cast<FixedVectorType>(InputTy), GEP, Builder);
6225ffd83dbSDimitry Andric   if (!BasePtr)
6235ffd83dbSDimitry Andric     return nullptr;
6245ffd83dbSDimitry Andric   // Check whether the offset is a constant increment that could be merged into
6255ffd83dbSDimitry Andric   // a QI gather
6265ffd83dbSDimitry Andric   Value *Store =
6275ffd83dbSDimitry Andric       tryCreateIncrementingGatScat(I, BasePtr, Offsets, GEP, Builder);
6285ffd83dbSDimitry Andric   if (Store)
6295ffd83dbSDimitry Andric     return Store;
6305ffd83dbSDimitry Andric   int Scale = computeScale(
6315ffd83dbSDimitry Andric       BasePtr->getType()->getPointerElementType()->getPrimitiveSizeInBits(),
6325ffd83dbSDimitry Andric       MemoryTy->getScalarSizeInBits());
6335ffd83dbSDimitry Andric   if (Scale == -1)
6345ffd83dbSDimitry Andric     return nullptr;
6355ffd83dbSDimitry Andric 
6365ffd83dbSDimitry Andric   if (!match(Mask, m_One()))
6375ffd83dbSDimitry Andric     return Builder.CreateIntrinsic(
6385ffd83dbSDimitry Andric         Intrinsic::arm_mve_vstr_scatter_offset_predicated,
6395ffd83dbSDimitry Andric         {BasePtr->getType(), Offsets->getType(), Input->getType(),
6405ffd83dbSDimitry Andric          Mask->getType()},
6415ffd83dbSDimitry Andric         {BasePtr, Offsets, Input,
6425ffd83dbSDimitry Andric          Builder.getInt32(MemoryTy->getScalarSizeInBits()),
6435ffd83dbSDimitry Andric          Builder.getInt32(Scale), Mask});
6445ffd83dbSDimitry Andric   else
6455ffd83dbSDimitry Andric     return Builder.CreateIntrinsic(
6465ffd83dbSDimitry Andric         Intrinsic::arm_mve_vstr_scatter_offset,
6475ffd83dbSDimitry Andric         {BasePtr->getType(), Offsets->getType(), Input->getType()},
6485ffd83dbSDimitry Andric         {BasePtr, Offsets, Input,
6495ffd83dbSDimitry Andric          Builder.getInt32(MemoryTy->getScalarSizeInBits()),
6505ffd83dbSDimitry Andric          Builder.getInt32(Scale)});
6515ffd83dbSDimitry Andric }
6525ffd83dbSDimitry Andric 
6535ffd83dbSDimitry Andric Value *MVEGatherScatterLowering::tryCreateIncrementingGatScat(
6545ffd83dbSDimitry Andric     IntrinsicInst *I, Value *BasePtr, Value *Offsets, GetElementPtrInst *GEP,
6555ffd83dbSDimitry Andric     IRBuilder<> &Builder) {
6565ffd83dbSDimitry Andric   FixedVectorType *Ty;
6575ffd83dbSDimitry Andric   if (I->getIntrinsicID() == Intrinsic::masked_gather)
6585ffd83dbSDimitry Andric     Ty = cast<FixedVectorType>(I->getType());
6595ffd83dbSDimitry Andric   else
6605ffd83dbSDimitry Andric     Ty = cast<FixedVectorType>(I->getArgOperand(0)->getType());
6615ffd83dbSDimitry Andric   // Incrementing gathers only exist for v4i32
6625ffd83dbSDimitry Andric   if (Ty->getNumElements() != 4 ||
6635ffd83dbSDimitry Andric       Ty->getScalarSizeInBits() != 32)
6645ffd83dbSDimitry Andric     return nullptr;
6655ffd83dbSDimitry Andric   Loop *L = LI->getLoopFor(I->getParent());
6665ffd83dbSDimitry Andric   if (L == nullptr)
6675ffd83dbSDimitry Andric     // Incrementing gathers are not beneficial outside of a loop
6685ffd83dbSDimitry Andric     return nullptr;
6695ffd83dbSDimitry Andric   LLVM_DEBUG(dbgs() << "masked gathers/scatters: trying to build incrementing "
6705ffd83dbSDimitry Andric                        "wb gather/scatter\n");
6715ffd83dbSDimitry Andric 
6725ffd83dbSDimitry Andric   // The gep was in charge of making sure the offsets are scaled correctly
6735ffd83dbSDimitry Andric   // - calculate that factor so it can be applied by hand
6745ffd83dbSDimitry Andric   DataLayout DT = I->getParent()->getParent()->getParent()->getDataLayout();
6755ffd83dbSDimitry Andric   int TypeScale =
6765ffd83dbSDimitry Andric       computeScale(DT.getTypeSizeInBits(GEP->getOperand(0)->getType()),
6775ffd83dbSDimitry Andric                    DT.getTypeSizeInBits(GEP->getType()) /
6785ffd83dbSDimitry Andric                        cast<FixedVectorType>(GEP->getType())->getNumElements());
6795ffd83dbSDimitry Andric   if (TypeScale == -1)
6805ffd83dbSDimitry Andric     return nullptr;
6815ffd83dbSDimitry Andric 
6825ffd83dbSDimitry Andric   if (GEP->hasOneUse()) {
6835ffd83dbSDimitry Andric     // Only in this case do we want to build a wb gather, because the wb will
6845ffd83dbSDimitry Andric     // change the phi which does affect other users of the gep (which will still
6855ffd83dbSDimitry Andric     // be using the phi in the old way)
6865ffd83dbSDimitry Andric     Value *Load =
6875ffd83dbSDimitry Andric         tryCreateIncrementingWBGatScat(I, BasePtr, Offsets, TypeScale, Builder);
6885ffd83dbSDimitry Andric     if (Load != nullptr)
6895ffd83dbSDimitry Andric       return Load;
6905ffd83dbSDimitry Andric   }
6915ffd83dbSDimitry Andric   LLVM_DEBUG(dbgs() << "masked gathers/scatters: trying to build incrementing "
6925ffd83dbSDimitry Andric                        "non-wb gather/scatter\n");
6935ffd83dbSDimitry Andric 
6945ffd83dbSDimitry Andric   std::pair<Value *, int64_t> Add = getVarAndConst(Offsets, TypeScale);
6955ffd83dbSDimitry Andric   if (Add.first == nullptr)
6965ffd83dbSDimitry Andric     return nullptr;
6975ffd83dbSDimitry Andric   Value *OffsetsIncoming = Add.first;
6985ffd83dbSDimitry Andric   int64_t Immediate = Add.second;
6995ffd83dbSDimitry Andric 
7005ffd83dbSDimitry Andric   // Make sure the offsets are scaled correctly
7015ffd83dbSDimitry Andric   Instruction *ScaledOffsets = BinaryOperator::Create(
7025ffd83dbSDimitry Andric       Instruction::Shl, OffsetsIncoming,
7035ffd83dbSDimitry Andric       Builder.CreateVectorSplat(Ty->getNumElements(), Builder.getInt32(TypeScale)),
7045ffd83dbSDimitry Andric       "ScaledIndex", I);
7055ffd83dbSDimitry Andric   // Add the base to the offsets
7065ffd83dbSDimitry Andric   OffsetsIncoming = BinaryOperator::Create(
7075ffd83dbSDimitry Andric       Instruction::Add, ScaledOffsets,
7085ffd83dbSDimitry Andric       Builder.CreateVectorSplat(
7095ffd83dbSDimitry Andric           Ty->getNumElements(),
7105ffd83dbSDimitry Andric           Builder.CreatePtrToInt(
7115ffd83dbSDimitry Andric               BasePtr,
7125ffd83dbSDimitry Andric               cast<VectorType>(ScaledOffsets->getType())->getElementType())),
7135ffd83dbSDimitry Andric       "StartIndex", I);
7145ffd83dbSDimitry Andric 
7155ffd83dbSDimitry Andric   if (I->getIntrinsicID() == Intrinsic::masked_gather)
7165ffd83dbSDimitry Andric     return cast<IntrinsicInst>(
7175ffd83dbSDimitry Andric         tryCreateMaskedGatherBase(I, OffsetsIncoming, Builder, Immediate));
7185ffd83dbSDimitry Andric   else
7195ffd83dbSDimitry Andric     return cast<IntrinsicInst>(
7205ffd83dbSDimitry Andric         tryCreateMaskedScatterBase(I, OffsetsIncoming, Builder, Immediate));
7215ffd83dbSDimitry Andric }
7225ffd83dbSDimitry Andric 
7235ffd83dbSDimitry Andric Value *MVEGatherScatterLowering::tryCreateIncrementingWBGatScat(
7245ffd83dbSDimitry Andric     IntrinsicInst *I, Value *BasePtr, Value *Offsets, unsigned TypeScale,
7255ffd83dbSDimitry Andric     IRBuilder<> &Builder) {
7265ffd83dbSDimitry Andric   // Check whether this gather's offset is incremented by a constant - if so,
7275ffd83dbSDimitry Andric   // and the load is of the right type, we can merge this into a QI gather
7285ffd83dbSDimitry Andric   Loop *L = LI->getLoopFor(I->getParent());
7295ffd83dbSDimitry Andric   // Offsets that are worth merging into this instruction will be incremented
7305ffd83dbSDimitry Andric   // by a constant, thus we're looking for an add of a phi and a constant
7315ffd83dbSDimitry Andric   PHINode *Phi = dyn_cast<PHINode>(Offsets);
7325ffd83dbSDimitry Andric   if (Phi == nullptr || Phi->getNumIncomingValues() != 2 ||
7335ffd83dbSDimitry Andric       Phi->getParent() != L->getHeader() || Phi->getNumUses() != 2)
7345ffd83dbSDimitry Andric     // No phi means no IV to write back to; if there is a phi, we expect it
7355ffd83dbSDimitry Andric     // to have exactly two incoming values; the only phis we are interested in
7365ffd83dbSDimitry Andric     // will be loop IV's and have exactly two uses, one in their increment and
7375ffd83dbSDimitry Andric     // one in the gather's gep
7385ffd83dbSDimitry Andric     return nullptr;
7395ffd83dbSDimitry Andric 
7405ffd83dbSDimitry Andric   unsigned IncrementIndex =
7415ffd83dbSDimitry Andric       Phi->getIncomingBlock(0) == L->getLoopLatch() ? 0 : 1;
7425ffd83dbSDimitry Andric   // Look through the phi to the phi increment
7435ffd83dbSDimitry Andric   Offsets = Phi->getIncomingValue(IncrementIndex);
7445ffd83dbSDimitry Andric 
7455ffd83dbSDimitry Andric   std::pair<Value *, int64_t> Add = getVarAndConst(Offsets, TypeScale);
7465ffd83dbSDimitry Andric   if (Add.first == nullptr)
7475ffd83dbSDimitry Andric     return nullptr;
7485ffd83dbSDimitry Andric   Value *OffsetsIncoming = Add.first;
7495ffd83dbSDimitry Andric   int64_t Immediate = Add.second;
7505ffd83dbSDimitry Andric   if (OffsetsIncoming != Phi)
7515ffd83dbSDimitry Andric     // Then the increment we are looking at is not an increment of the
7525ffd83dbSDimitry Andric     // induction variable, and we don't want to do a writeback
7535ffd83dbSDimitry Andric     return nullptr;
7545ffd83dbSDimitry Andric 
7555ffd83dbSDimitry Andric   Builder.SetInsertPoint(&Phi->getIncomingBlock(1 - IncrementIndex)->back());
7565ffd83dbSDimitry Andric   unsigned NumElems =
7575ffd83dbSDimitry Andric       cast<FixedVectorType>(OffsetsIncoming->getType())->getNumElements();
7585ffd83dbSDimitry Andric 
7595ffd83dbSDimitry Andric   // Make sure the offsets are scaled correctly
7605ffd83dbSDimitry Andric   Instruction *ScaledOffsets = BinaryOperator::Create(
7615ffd83dbSDimitry Andric       Instruction::Shl, Phi->getIncomingValue(1 - IncrementIndex),
7625ffd83dbSDimitry Andric       Builder.CreateVectorSplat(NumElems, Builder.getInt32(TypeScale)),
7635ffd83dbSDimitry Andric       "ScaledIndex", &Phi->getIncomingBlock(1 - IncrementIndex)->back());
7645ffd83dbSDimitry Andric   // Add the base to the offsets
7655ffd83dbSDimitry Andric   OffsetsIncoming = BinaryOperator::Create(
7665ffd83dbSDimitry Andric       Instruction::Add, ScaledOffsets,
7675ffd83dbSDimitry Andric       Builder.CreateVectorSplat(
7685ffd83dbSDimitry Andric           NumElems,
7695ffd83dbSDimitry Andric           Builder.CreatePtrToInt(
7705ffd83dbSDimitry Andric               BasePtr,
7715ffd83dbSDimitry Andric               cast<VectorType>(ScaledOffsets->getType())->getElementType())),
7725ffd83dbSDimitry Andric       "StartIndex", &Phi->getIncomingBlock(1 - IncrementIndex)->back());
7735ffd83dbSDimitry Andric   // The gather is pre-incrementing
7745ffd83dbSDimitry Andric   OffsetsIncoming = BinaryOperator::Create(
7755ffd83dbSDimitry Andric       Instruction::Sub, OffsetsIncoming,
7765ffd83dbSDimitry Andric       Builder.CreateVectorSplat(NumElems, Builder.getInt32(Immediate)),
7775ffd83dbSDimitry Andric       "PreIncrementStartIndex",
7785ffd83dbSDimitry Andric       &Phi->getIncomingBlock(1 - IncrementIndex)->back());
7795ffd83dbSDimitry Andric   Phi->setIncomingValue(1 - IncrementIndex, OffsetsIncoming);
7805ffd83dbSDimitry Andric 
7815ffd83dbSDimitry Andric   Builder.SetInsertPoint(I);
7825ffd83dbSDimitry Andric 
7835ffd83dbSDimitry Andric   Value *EndResult;
7845ffd83dbSDimitry Andric   Value *NewInduction;
7855ffd83dbSDimitry Andric   if (I->getIntrinsicID() == Intrinsic::masked_gather) {
7865ffd83dbSDimitry Andric     // Build the incrementing gather
7875ffd83dbSDimitry Andric     Value *Load = tryCreateMaskedGatherBaseWB(I, Phi, Builder, Immediate);
7885ffd83dbSDimitry Andric     // One value to be handed to whoever uses the gather, one is the loop
7895ffd83dbSDimitry Andric     // increment
7905ffd83dbSDimitry Andric     EndResult = Builder.CreateExtractValue(Load, 0, "Gather");
7915ffd83dbSDimitry Andric     NewInduction = Builder.CreateExtractValue(Load, 1, "GatherIncrement");
7925ffd83dbSDimitry Andric   } else {
7935ffd83dbSDimitry Andric     // Build the incrementing scatter
7945ffd83dbSDimitry Andric     NewInduction = tryCreateMaskedScatterBaseWB(I, Phi, Builder, Immediate);
7955ffd83dbSDimitry Andric     EndResult = NewInduction;
7965ffd83dbSDimitry Andric   }
7975ffd83dbSDimitry Andric   Instruction *AddInst = cast<Instruction>(Offsets);
7985ffd83dbSDimitry Andric   AddInst->replaceAllUsesWith(NewInduction);
7995ffd83dbSDimitry Andric   AddInst->eraseFromParent();
8005ffd83dbSDimitry Andric   Phi->setIncomingValue(IncrementIndex, NewInduction);
8015ffd83dbSDimitry Andric 
8025ffd83dbSDimitry Andric   return EndResult;
8035ffd83dbSDimitry Andric }
8045ffd83dbSDimitry Andric 
8055ffd83dbSDimitry Andric void MVEGatherScatterLowering::pushOutAdd(PHINode *&Phi,
8065ffd83dbSDimitry Andric                                           Value *OffsSecondOperand,
8075ffd83dbSDimitry Andric                                           unsigned StartIndex) {
8085ffd83dbSDimitry Andric   LLVM_DEBUG(dbgs() << "masked gathers/scatters: optimising add instruction\n");
8095ffd83dbSDimitry Andric   Instruction *InsertionPoint =
8105ffd83dbSDimitry Andric         &cast<Instruction>(Phi->getIncomingBlock(StartIndex)->back());
8115ffd83dbSDimitry Andric   // Initialize the phi with a vector that contains a sum of the constants
8125ffd83dbSDimitry Andric   Instruction *NewIndex = BinaryOperator::Create(
8135ffd83dbSDimitry Andric       Instruction::Add, Phi->getIncomingValue(StartIndex), OffsSecondOperand,
8145ffd83dbSDimitry Andric       "PushedOutAdd", InsertionPoint);
8155ffd83dbSDimitry Andric   unsigned IncrementIndex = StartIndex == 0 ? 1 : 0;
8165ffd83dbSDimitry Andric 
8175ffd83dbSDimitry Andric   // Order such that start index comes first (this reduces mov's)
8185ffd83dbSDimitry Andric   Phi->addIncoming(NewIndex, Phi->getIncomingBlock(StartIndex));
8195ffd83dbSDimitry Andric   Phi->addIncoming(Phi->getIncomingValue(IncrementIndex),
8205ffd83dbSDimitry Andric                    Phi->getIncomingBlock(IncrementIndex));
8215ffd83dbSDimitry Andric   Phi->removeIncomingValue(IncrementIndex);
8225ffd83dbSDimitry Andric   Phi->removeIncomingValue(StartIndex);
8235ffd83dbSDimitry Andric }
8245ffd83dbSDimitry Andric 
8255ffd83dbSDimitry Andric void MVEGatherScatterLowering::pushOutMul(PHINode *&Phi,
8265ffd83dbSDimitry Andric                                           Value *IncrementPerRound,
8275ffd83dbSDimitry Andric                                           Value *OffsSecondOperand,
8285ffd83dbSDimitry Andric                                           unsigned LoopIncrement,
8295ffd83dbSDimitry Andric                                           IRBuilder<> &Builder) {
8305ffd83dbSDimitry Andric   LLVM_DEBUG(dbgs() << "masked gathers/scatters: optimising mul instruction\n");
8315ffd83dbSDimitry Andric 
8325ffd83dbSDimitry Andric   // Create a new scalar add outside of the loop and transform it to a splat
8335ffd83dbSDimitry Andric   // by which loop variable can be incremented
8345ffd83dbSDimitry Andric   Instruction *InsertionPoint = &cast<Instruction>(
8355ffd83dbSDimitry Andric         Phi->getIncomingBlock(LoopIncrement == 1 ? 0 : 1)->back());
8365ffd83dbSDimitry Andric 
8375ffd83dbSDimitry Andric   // Create a new index
8385ffd83dbSDimitry Andric   Value *StartIndex = BinaryOperator::Create(
8395ffd83dbSDimitry Andric       Instruction::Mul, Phi->getIncomingValue(LoopIncrement == 1 ? 0 : 1),
8405ffd83dbSDimitry Andric       OffsSecondOperand, "PushedOutMul", InsertionPoint);
8415ffd83dbSDimitry Andric 
8425ffd83dbSDimitry Andric   Instruction *Product =
8435ffd83dbSDimitry Andric       BinaryOperator::Create(Instruction::Mul, IncrementPerRound,
8445ffd83dbSDimitry Andric                              OffsSecondOperand, "Product", InsertionPoint);
8455ffd83dbSDimitry Andric   // Increment NewIndex by Product instead of the multiplication
8465ffd83dbSDimitry Andric   Instruction *NewIncrement = BinaryOperator::Create(
8475ffd83dbSDimitry Andric       Instruction::Add, Phi, Product, "IncrementPushedOutMul",
8485ffd83dbSDimitry Andric       cast<Instruction>(Phi->getIncomingBlock(LoopIncrement)->back())
8495ffd83dbSDimitry Andric           .getPrevNode());
8505ffd83dbSDimitry Andric 
8515ffd83dbSDimitry Andric   Phi->addIncoming(StartIndex,
8525ffd83dbSDimitry Andric                    Phi->getIncomingBlock(LoopIncrement == 1 ? 0 : 1));
8535ffd83dbSDimitry Andric   Phi->addIncoming(NewIncrement, Phi->getIncomingBlock(LoopIncrement));
8545ffd83dbSDimitry Andric   Phi->removeIncomingValue((unsigned)0);
8555ffd83dbSDimitry Andric   Phi->removeIncomingValue((unsigned)0);
8565ffd83dbSDimitry Andric }
8575ffd83dbSDimitry Andric 
8585ffd83dbSDimitry Andric // Check whether all usages of this instruction are as offsets of
8595ffd83dbSDimitry Andric // gathers/scatters or simple arithmetics only used by gathers/scatters
8605ffd83dbSDimitry Andric static bool hasAllGatScatUsers(Instruction *I) {
8615ffd83dbSDimitry Andric   if (I->hasNUses(0)) {
8625ffd83dbSDimitry Andric     return false;
8635ffd83dbSDimitry Andric   }
8645ffd83dbSDimitry Andric   bool Gatscat = true;
8655ffd83dbSDimitry Andric   for (User *U : I->users()) {
8665ffd83dbSDimitry Andric     if (!isa<Instruction>(U))
8675ffd83dbSDimitry Andric       return false;
8685ffd83dbSDimitry Andric     if (isa<GetElementPtrInst>(U) ||
8695ffd83dbSDimitry Andric         isGatherScatter(dyn_cast<IntrinsicInst>(U))) {
8705ffd83dbSDimitry Andric       return Gatscat;
8715ffd83dbSDimitry Andric     } else {
8725ffd83dbSDimitry Andric       unsigned OpCode = cast<Instruction>(U)->getOpcode();
8735ffd83dbSDimitry Andric       if ((OpCode == Instruction::Add || OpCode == Instruction::Mul) &&
8745ffd83dbSDimitry Andric           hasAllGatScatUsers(cast<Instruction>(U))) {
8755ffd83dbSDimitry Andric         continue;
8765ffd83dbSDimitry Andric       }
8775ffd83dbSDimitry Andric       return false;
8785ffd83dbSDimitry Andric     }
8795ffd83dbSDimitry Andric   }
8805ffd83dbSDimitry Andric   return Gatscat;
8815ffd83dbSDimitry Andric }
8825ffd83dbSDimitry Andric 
8835ffd83dbSDimitry Andric bool MVEGatherScatterLowering::optimiseOffsets(Value *Offsets, BasicBlock *BB,
8845ffd83dbSDimitry Andric                                                LoopInfo *LI) {
8855ffd83dbSDimitry Andric   LLVM_DEBUG(dbgs() << "masked gathers/scatters: trying to optimize\n");
8865ffd83dbSDimitry Andric   // Optimise the addresses of gathers/scatters by moving invariant
8875ffd83dbSDimitry Andric   // calculations out of the loop
8885ffd83dbSDimitry Andric   if (!isa<Instruction>(Offsets))
8895ffd83dbSDimitry Andric     return false;
8905ffd83dbSDimitry Andric   Instruction *Offs = cast<Instruction>(Offsets);
8915ffd83dbSDimitry Andric   if (Offs->getOpcode() != Instruction::Add &&
8925ffd83dbSDimitry Andric       Offs->getOpcode() != Instruction::Mul)
8935ffd83dbSDimitry Andric     return false;
8945ffd83dbSDimitry Andric   Loop *L = LI->getLoopFor(BB);
8955ffd83dbSDimitry Andric   if (L == nullptr)
8965ffd83dbSDimitry Andric     return false;
8975ffd83dbSDimitry Andric   if (!Offs->hasOneUse()) {
8985ffd83dbSDimitry Andric     if (!hasAllGatScatUsers(Offs))
8995ffd83dbSDimitry Andric       return false;
9005ffd83dbSDimitry Andric   }
9015ffd83dbSDimitry Andric 
9025ffd83dbSDimitry Andric   // Find out which, if any, operand of the instruction
9035ffd83dbSDimitry Andric   // is a phi node
9045ffd83dbSDimitry Andric   PHINode *Phi;
9055ffd83dbSDimitry Andric   int OffsSecondOp;
9065ffd83dbSDimitry Andric   if (isa<PHINode>(Offs->getOperand(0))) {
9075ffd83dbSDimitry Andric     Phi = cast<PHINode>(Offs->getOperand(0));
9085ffd83dbSDimitry Andric     OffsSecondOp = 1;
9095ffd83dbSDimitry Andric   } else if (isa<PHINode>(Offs->getOperand(1))) {
9105ffd83dbSDimitry Andric     Phi = cast<PHINode>(Offs->getOperand(1));
9115ffd83dbSDimitry Andric     OffsSecondOp = 0;
9125ffd83dbSDimitry Andric   } else {
9135ffd83dbSDimitry Andric     bool Changed = true;
9145ffd83dbSDimitry Andric     if (isa<Instruction>(Offs->getOperand(0)) &&
9155ffd83dbSDimitry Andric         L->contains(cast<Instruction>(Offs->getOperand(0))))
9165ffd83dbSDimitry Andric       Changed |= optimiseOffsets(Offs->getOperand(0), BB, LI);
9175ffd83dbSDimitry Andric     if (isa<Instruction>(Offs->getOperand(1)) &&
9185ffd83dbSDimitry Andric         L->contains(cast<Instruction>(Offs->getOperand(1))))
9195ffd83dbSDimitry Andric       Changed |= optimiseOffsets(Offs->getOperand(1), BB, LI);
9205ffd83dbSDimitry Andric     if (!Changed) {
9215ffd83dbSDimitry Andric       return false;
9225ffd83dbSDimitry Andric     } else {
9235ffd83dbSDimitry Andric       if (isa<PHINode>(Offs->getOperand(0))) {
9245ffd83dbSDimitry Andric         Phi = cast<PHINode>(Offs->getOperand(0));
9255ffd83dbSDimitry Andric         OffsSecondOp = 1;
9265ffd83dbSDimitry Andric       } else if (isa<PHINode>(Offs->getOperand(1))) {
9275ffd83dbSDimitry Andric         Phi = cast<PHINode>(Offs->getOperand(1));
9285ffd83dbSDimitry Andric         OffsSecondOp = 0;
9295ffd83dbSDimitry Andric       } else {
9305ffd83dbSDimitry Andric         return false;
9315ffd83dbSDimitry Andric       }
9325ffd83dbSDimitry Andric     }
9335ffd83dbSDimitry Andric   }
9345ffd83dbSDimitry Andric   // A phi node we want to perform this function on should be from the
9355ffd83dbSDimitry Andric   // loop header, and shouldn't have more than 2 incoming values
9365ffd83dbSDimitry Andric   if (Phi->getParent() != L->getHeader() ||
9375ffd83dbSDimitry Andric       Phi->getNumIncomingValues() != 2)
9385ffd83dbSDimitry Andric     return false;
9395ffd83dbSDimitry Andric 
9405ffd83dbSDimitry Andric   // The phi must be an induction variable
9415ffd83dbSDimitry Andric   int IncrementingBlock = -1;
9425ffd83dbSDimitry Andric 
9435ffd83dbSDimitry Andric   for (int i = 0; i < 2; i++)
944e8d8bef9SDimitry Andric     if (auto *Op = dyn_cast<Instruction>(Phi->getIncomingValue(i)))
9455ffd83dbSDimitry Andric       if (Op->getOpcode() == Instruction::Add &&
9465ffd83dbSDimitry Andric           (Op->getOperand(0) == Phi || Op->getOperand(1) == Phi))
9475ffd83dbSDimitry Andric         IncrementingBlock = i;
9485ffd83dbSDimitry Andric   if (IncrementingBlock == -1)
9495ffd83dbSDimitry Andric     return false;
9505ffd83dbSDimitry Andric 
9515ffd83dbSDimitry Andric   Instruction *IncInstruction =
9525ffd83dbSDimitry Andric       cast<Instruction>(Phi->getIncomingValue(IncrementingBlock));
9535ffd83dbSDimitry Andric 
9545ffd83dbSDimitry Andric   // If the phi is not used by anything else, we can just adapt it when
9555ffd83dbSDimitry Andric   // replacing the instruction; if it is, we'll have to duplicate it
9565ffd83dbSDimitry Andric   PHINode *NewPhi;
9575ffd83dbSDimitry Andric   Value *IncrementPerRound = IncInstruction->getOperand(
9585ffd83dbSDimitry Andric       (IncInstruction->getOperand(0) == Phi) ? 1 : 0);
9595ffd83dbSDimitry Andric 
9605ffd83dbSDimitry Andric   // Get the value that is added to/multiplied with the phi
9615ffd83dbSDimitry Andric   Value *OffsSecondOperand = Offs->getOperand(OffsSecondOp);
9625ffd83dbSDimitry Andric 
963*4652422eSDimitry Andric   if (IncrementPerRound->getType() != OffsSecondOperand->getType() ||
964*4652422eSDimitry Andric       !L->isLoopInvariant(OffsSecondOperand))
9655ffd83dbSDimitry Andric     // Something has gone wrong, abort
9665ffd83dbSDimitry Andric     return false;
9675ffd83dbSDimitry Andric 
9685ffd83dbSDimitry Andric   // Only proceed if the increment per round is a constant or an instruction
9695ffd83dbSDimitry Andric   // which does not originate from within the loop
9705ffd83dbSDimitry Andric   if (!isa<Constant>(IncrementPerRound) &&
9715ffd83dbSDimitry Andric       !(isa<Instruction>(IncrementPerRound) &&
9725ffd83dbSDimitry Andric         !L->contains(cast<Instruction>(IncrementPerRound))))
9735ffd83dbSDimitry Andric     return false;
9745ffd83dbSDimitry Andric 
9755ffd83dbSDimitry Andric   if (Phi->getNumUses() == 2) {
9765ffd83dbSDimitry Andric     // No other users -> reuse existing phi (One user is the instruction
9775ffd83dbSDimitry Andric     // we're looking at, the other is the phi increment)
9785ffd83dbSDimitry Andric     if (IncInstruction->getNumUses() != 1) {
9795ffd83dbSDimitry Andric       // If the incrementing instruction does have more users than
9805ffd83dbSDimitry Andric       // our phi, we need to copy it
9815ffd83dbSDimitry Andric       IncInstruction = BinaryOperator::Create(
9825ffd83dbSDimitry Andric           Instruction::BinaryOps(IncInstruction->getOpcode()), Phi,
9835ffd83dbSDimitry Andric           IncrementPerRound, "LoopIncrement", IncInstruction);
9845ffd83dbSDimitry Andric       Phi->setIncomingValue(IncrementingBlock, IncInstruction);
9855ffd83dbSDimitry Andric     }
9865ffd83dbSDimitry Andric     NewPhi = Phi;
9875ffd83dbSDimitry Andric   } else {
9885ffd83dbSDimitry Andric     // There are other users -> create a new phi
9895ffd83dbSDimitry Andric     NewPhi = PHINode::Create(Phi->getType(), 0, "NewPhi", Phi);
9905ffd83dbSDimitry Andric     std::vector<Value *> Increases;
9915ffd83dbSDimitry Andric     // Copy the incoming values of the old phi
9925ffd83dbSDimitry Andric     NewPhi->addIncoming(Phi->getIncomingValue(IncrementingBlock == 1 ? 0 : 1),
9935ffd83dbSDimitry Andric                         Phi->getIncomingBlock(IncrementingBlock == 1 ? 0 : 1));
9945ffd83dbSDimitry Andric     IncInstruction = BinaryOperator::Create(
9955ffd83dbSDimitry Andric         Instruction::BinaryOps(IncInstruction->getOpcode()), NewPhi,
9965ffd83dbSDimitry Andric         IncrementPerRound, "LoopIncrement", IncInstruction);
9975ffd83dbSDimitry Andric     NewPhi->addIncoming(IncInstruction,
9985ffd83dbSDimitry Andric                         Phi->getIncomingBlock(IncrementingBlock));
9995ffd83dbSDimitry Andric     IncrementingBlock = 1;
10005ffd83dbSDimitry Andric   }
10015ffd83dbSDimitry Andric 
10025ffd83dbSDimitry Andric   IRBuilder<> Builder(BB->getContext());
10035ffd83dbSDimitry Andric   Builder.SetInsertPoint(Phi);
10045ffd83dbSDimitry Andric   Builder.SetCurrentDebugLocation(Offs->getDebugLoc());
10055ffd83dbSDimitry Andric 
10065ffd83dbSDimitry Andric   switch (Offs->getOpcode()) {
10075ffd83dbSDimitry Andric   case Instruction::Add:
10085ffd83dbSDimitry Andric     pushOutAdd(NewPhi, OffsSecondOperand, IncrementingBlock == 1 ? 0 : 1);
10095ffd83dbSDimitry Andric     break;
10105ffd83dbSDimitry Andric   case Instruction::Mul:
10115ffd83dbSDimitry Andric     pushOutMul(NewPhi, IncrementPerRound, OffsSecondOperand, IncrementingBlock,
10125ffd83dbSDimitry Andric                Builder);
10135ffd83dbSDimitry Andric     break;
10145ffd83dbSDimitry Andric   default:
10155ffd83dbSDimitry Andric     return false;
10165ffd83dbSDimitry Andric   }
10175ffd83dbSDimitry Andric   LLVM_DEBUG(
10185ffd83dbSDimitry Andric       dbgs() << "masked gathers/scatters: simplified loop variable add/mul\n");
10195ffd83dbSDimitry Andric 
10205ffd83dbSDimitry Andric   // The instruction has now been "absorbed" into the phi value
10215ffd83dbSDimitry Andric   Offs->replaceAllUsesWith(NewPhi);
10225ffd83dbSDimitry Andric   if (Offs->hasNUses(0))
10235ffd83dbSDimitry Andric     Offs->eraseFromParent();
10245ffd83dbSDimitry Andric   // Clean up the old increment in case it's unused because we built a new
10255ffd83dbSDimitry Andric   // one
10265ffd83dbSDimitry Andric   if (IncInstruction->hasNUses(0))
10275ffd83dbSDimitry Andric     IncInstruction->eraseFromParent();
10285ffd83dbSDimitry Andric 
10295ffd83dbSDimitry Andric   return true;
1030480093f4SDimitry Andric }
1031480093f4SDimitry Andric 
1032e8d8bef9SDimitry Andric static Value *CheckAndCreateOffsetAdd(Value *X, Value *Y, Value *GEP,
1033e8d8bef9SDimitry Andric                                       IRBuilder<> &Builder) {
1034e8d8bef9SDimitry Andric   // Splat the non-vector value to a vector of the given type - if the value is
1035e8d8bef9SDimitry Andric   // a constant (and its value isn't too big), we can even use this opportunity
1036e8d8bef9SDimitry Andric   // to scale it to the size of the vector elements
1037e8d8bef9SDimitry Andric   auto FixSummands = [&Builder](FixedVectorType *&VT, Value *&NonVectorVal) {
1038e8d8bef9SDimitry Andric     ConstantInt *Const;
1039e8d8bef9SDimitry Andric     if ((Const = dyn_cast<ConstantInt>(NonVectorVal)) &&
1040e8d8bef9SDimitry Andric         VT->getElementType() != NonVectorVal->getType()) {
1041e8d8bef9SDimitry Andric       unsigned TargetElemSize = VT->getElementType()->getPrimitiveSizeInBits();
1042e8d8bef9SDimitry Andric       uint64_t N = Const->getZExtValue();
1043e8d8bef9SDimitry Andric       if (N < (unsigned)(1 << (TargetElemSize - 1))) {
1044e8d8bef9SDimitry Andric         NonVectorVal = Builder.CreateVectorSplat(
1045e8d8bef9SDimitry Andric             VT->getNumElements(), Builder.getIntN(TargetElemSize, N));
1046e8d8bef9SDimitry Andric         return;
1047e8d8bef9SDimitry Andric       }
1048e8d8bef9SDimitry Andric     }
1049e8d8bef9SDimitry Andric     NonVectorVal =
1050e8d8bef9SDimitry Andric         Builder.CreateVectorSplat(VT->getNumElements(), NonVectorVal);
1051e8d8bef9SDimitry Andric   };
1052e8d8bef9SDimitry Andric 
1053e8d8bef9SDimitry Andric   FixedVectorType *XElType = dyn_cast<FixedVectorType>(X->getType());
1054e8d8bef9SDimitry Andric   FixedVectorType *YElType = dyn_cast<FixedVectorType>(Y->getType());
1055e8d8bef9SDimitry Andric   // If one of X, Y is not a vector, we have to splat it in order
1056e8d8bef9SDimitry Andric   // to add the two of them.
1057e8d8bef9SDimitry Andric   if (XElType && !YElType) {
1058e8d8bef9SDimitry Andric     FixSummands(XElType, Y);
1059e8d8bef9SDimitry Andric     YElType = cast<FixedVectorType>(Y->getType());
1060e8d8bef9SDimitry Andric   } else if (YElType && !XElType) {
1061e8d8bef9SDimitry Andric     FixSummands(YElType, X);
1062e8d8bef9SDimitry Andric     XElType = cast<FixedVectorType>(X->getType());
1063e8d8bef9SDimitry Andric   }
1064e8d8bef9SDimitry Andric   assert(XElType && YElType && "Unknown vector types");
1065e8d8bef9SDimitry Andric   // Check that the summands are of compatible types
1066e8d8bef9SDimitry Andric   if (XElType != YElType) {
1067e8d8bef9SDimitry Andric     LLVM_DEBUG(dbgs() << "masked gathers/scatters: incompatible gep offsets\n");
1068e8d8bef9SDimitry Andric     return nullptr;
1069e8d8bef9SDimitry Andric   }
1070e8d8bef9SDimitry Andric 
1071e8d8bef9SDimitry Andric   if (XElType->getElementType()->getScalarSizeInBits() != 32) {
1072e8d8bef9SDimitry Andric     // Check that by adding the vectors we do not accidentally
1073e8d8bef9SDimitry Andric     // create an overflow
1074e8d8bef9SDimitry Andric     Constant *ConstX = dyn_cast<Constant>(X);
1075e8d8bef9SDimitry Andric     Constant *ConstY = dyn_cast<Constant>(Y);
1076e8d8bef9SDimitry Andric     if (!ConstX || !ConstY)
1077e8d8bef9SDimitry Andric       return nullptr;
1078e8d8bef9SDimitry Andric     unsigned TargetElemSize = 128 / XElType->getNumElements();
1079e8d8bef9SDimitry Andric     for (unsigned i = 0; i < XElType->getNumElements(); i++) {
1080e8d8bef9SDimitry Andric       ConstantInt *ConstXEl =
1081e8d8bef9SDimitry Andric           dyn_cast<ConstantInt>(ConstX->getAggregateElement(i));
1082e8d8bef9SDimitry Andric       ConstantInt *ConstYEl =
1083e8d8bef9SDimitry Andric           dyn_cast<ConstantInt>(ConstY->getAggregateElement(i));
1084e8d8bef9SDimitry Andric       if (!ConstXEl || !ConstYEl ||
1085e8d8bef9SDimitry Andric           ConstXEl->getZExtValue() + ConstYEl->getZExtValue() >=
1086e8d8bef9SDimitry Andric               (unsigned)(1 << (TargetElemSize - 1)))
1087e8d8bef9SDimitry Andric         return nullptr;
1088e8d8bef9SDimitry Andric     }
1089e8d8bef9SDimitry Andric   }
1090e8d8bef9SDimitry Andric 
1091e8d8bef9SDimitry Andric   Value *Add = Builder.CreateAdd(X, Y);
1092e8d8bef9SDimitry Andric 
1093e8d8bef9SDimitry Andric   FixedVectorType *GEPType = cast<FixedVectorType>(GEP->getType());
1094e8d8bef9SDimitry Andric   if (checkOffsetSize(Add, GEPType->getNumElements()))
1095e8d8bef9SDimitry Andric     return Add;
1096e8d8bef9SDimitry Andric   else
1097e8d8bef9SDimitry Andric     return nullptr;
1098e8d8bef9SDimitry Andric }
1099e8d8bef9SDimitry Andric 
1100e8d8bef9SDimitry Andric Value *MVEGatherScatterLowering::foldGEP(GetElementPtrInst *GEP,
1101e8d8bef9SDimitry Andric                                          Value *&Offsets,
1102e8d8bef9SDimitry Andric                                          IRBuilder<> &Builder) {
1103e8d8bef9SDimitry Andric   Value *GEPPtr = GEP->getPointerOperand();
1104e8d8bef9SDimitry Andric   Offsets = GEP->getOperand(1);
1105e8d8bef9SDimitry Andric   // We only merge geps with constant offsets, because only for those
1106e8d8bef9SDimitry Andric   // we can make sure that we do not cause an overflow
1107e8d8bef9SDimitry Andric   if (!isa<Constant>(Offsets))
1108e8d8bef9SDimitry Andric     return nullptr;
1109e8d8bef9SDimitry Andric   GetElementPtrInst *BaseGEP;
1110e8d8bef9SDimitry Andric   if ((BaseGEP = dyn_cast<GetElementPtrInst>(GEPPtr))) {
1111e8d8bef9SDimitry Andric     // Merge the two geps into one
1112e8d8bef9SDimitry Andric     Value *BaseBasePtr = foldGEP(BaseGEP, Offsets, Builder);
1113e8d8bef9SDimitry Andric     if (!BaseBasePtr)
1114e8d8bef9SDimitry Andric       return nullptr;
1115e8d8bef9SDimitry Andric     Offsets =
1116e8d8bef9SDimitry Andric         CheckAndCreateOffsetAdd(Offsets, GEP->getOperand(1), GEP, Builder);
1117e8d8bef9SDimitry Andric     if (Offsets == nullptr)
1118e8d8bef9SDimitry Andric       return nullptr;
1119e8d8bef9SDimitry Andric     return BaseBasePtr;
1120e8d8bef9SDimitry Andric   }
1121e8d8bef9SDimitry Andric   return GEPPtr;
1122e8d8bef9SDimitry Andric }
1123e8d8bef9SDimitry Andric 
1124e8d8bef9SDimitry Andric bool MVEGatherScatterLowering::optimiseAddress(Value *Address, BasicBlock *BB,
1125e8d8bef9SDimitry Andric                                                LoopInfo *LI) {
1126e8d8bef9SDimitry Andric   GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(Address);
1127e8d8bef9SDimitry Andric   if (!GEP)
1128e8d8bef9SDimitry Andric     return false;
1129e8d8bef9SDimitry Andric   bool Changed = false;
1130e8d8bef9SDimitry Andric   if (GEP->hasOneUse() &&
1131e8d8bef9SDimitry Andric       dyn_cast<GetElementPtrInst>(GEP->getPointerOperand())) {
1132e8d8bef9SDimitry Andric     IRBuilder<> Builder(GEP->getContext());
1133e8d8bef9SDimitry Andric     Builder.SetInsertPoint(GEP);
1134e8d8bef9SDimitry Andric     Builder.SetCurrentDebugLocation(GEP->getDebugLoc());
1135e8d8bef9SDimitry Andric     Value *Offsets;
1136e8d8bef9SDimitry Andric     Value *Base = foldGEP(GEP, Offsets, Builder);
1137e8d8bef9SDimitry Andric     // We only want to merge the geps if there is a real chance that they can be
1138e8d8bef9SDimitry Andric     // used by an MVE gather; thus the offset has to have the correct size
1139e8d8bef9SDimitry Andric     // (always i32 if it is not of vector type) and the base has to be a
1140e8d8bef9SDimitry Andric     // pointer.
1141e8d8bef9SDimitry Andric     if (Offsets && Base && Base != GEP) {
1142e8d8bef9SDimitry Andric       PointerType *BaseType = cast<PointerType>(Base->getType());
1143e8d8bef9SDimitry Andric       GetElementPtrInst *NewAddress = GetElementPtrInst::Create(
1144e8d8bef9SDimitry Andric           BaseType->getPointerElementType(), Base, Offsets, "gep.merged", GEP);
1145e8d8bef9SDimitry Andric       GEP->replaceAllUsesWith(NewAddress);
1146e8d8bef9SDimitry Andric       GEP = NewAddress;
1147e8d8bef9SDimitry Andric       Changed = true;
1148e8d8bef9SDimitry Andric     }
1149e8d8bef9SDimitry Andric   }
1150e8d8bef9SDimitry Andric   Changed |= optimiseOffsets(GEP->getOperand(1), GEP->getParent(), LI);
1151e8d8bef9SDimitry Andric   return Changed;
1152e8d8bef9SDimitry Andric }
1153e8d8bef9SDimitry Andric 
1154480093f4SDimitry Andric bool MVEGatherScatterLowering::runOnFunction(Function &F) {
1155480093f4SDimitry Andric   if (!EnableMaskedGatherScatters)
1156480093f4SDimitry Andric     return false;
1157480093f4SDimitry Andric   auto &TPC = getAnalysis<TargetPassConfig>();
1158480093f4SDimitry Andric   auto &TM = TPC.getTM<TargetMachine>();
1159480093f4SDimitry Andric   auto *ST = &TM.getSubtarget<ARMSubtarget>(F);
1160480093f4SDimitry Andric   if (!ST->hasMVEIntegerOps())
1161480093f4SDimitry Andric     return false;
11625ffd83dbSDimitry Andric   LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
1163480093f4SDimitry Andric   SmallVector<IntrinsicInst *, 4> Gathers;
11645ffd83dbSDimitry Andric   SmallVector<IntrinsicInst *, 4> Scatters;
11655ffd83dbSDimitry Andric 
11665ffd83dbSDimitry Andric   bool Changed = false;
11675ffd83dbSDimitry Andric 
1168480093f4SDimitry Andric   for (BasicBlock &BB : F) {
1169*4652422eSDimitry Andric     Changed |= SimplifyInstructionsInBlock(&BB);
1170*4652422eSDimitry Andric 
1171480093f4SDimitry Andric     for (Instruction &I : BB) {
1172480093f4SDimitry Andric       IntrinsicInst *II = dyn_cast<IntrinsicInst>(&I);
1173e8d8bef9SDimitry Andric       if (II && II->getIntrinsicID() == Intrinsic::masked_gather &&
1174e8d8bef9SDimitry Andric           isa<FixedVectorType>(II->getType())) {
1175480093f4SDimitry Andric         Gathers.push_back(II);
1176e8d8bef9SDimitry Andric         Changed |= optimiseAddress(II->getArgOperand(0), II->getParent(), LI);
1177e8d8bef9SDimitry Andric       } else if (II && II->getIntrinsicID() == Intrinsic::masked_scatter &&
1178e8d8bef9SDimitry Andric                  isa<FixedVectorType>(II->getArgOperand(0)->getType())) {
11795ffd83dbSDimitry Andric         Scatters.push_back(II);
1180e8d8bef9SDimitry Andric         Changed |= optimiseAddress(II->getArgOperand(1), II->getParent(), LI);
11815ffd83dbSDimitry Andric       }
1182480093f4SDimitry Andric     }
1183480093f4SDimitry Andric   }
11845ffd83dbSDimitry Andric   for (unsigned i = 0; i < Gathers.size(); i++) {
11855ffd83dbSDimitry Andric     IntrinsicInst *I = Gathers[i];
11865ffd83dbSDimitry Andric     Value *L = lowerGather(I);
11875ffd83dbSDimitry Andric     if (L == nullptr)
11885ffd83dbSDimitry Andric       continue;
1189480093f4SDimitry Andric 
11905ffd83dbSDimitry Andric     // Get rid of any now dead instructions
11915ffd83dbSDimitry Andric     SimplifyInstructionsInBlock(cast<Instruction>(L)->getParent());
11925ffd83dbSDimitry Andric     Changed = true;
11935ffd83dbSDimitry Andric   }
1194480093f4SDimitry Andric 
11955ffd83dbSDimitry Andric   for (unsigned i = 0; i < Scatters.size(); i++) {
11965ffd83dbSDimitry Andric     IntrinsicInst *I = Scatters[i];
11975ffd83dbSDimitry Andric     Value *S = lowerScatter(I);
11985ffd83dbSDimitry Andric     if (S == nullptr)
11995ffd83dbSDimitry Andric       continue;
12005ffd83dbSDimitry Andric 
12015ffd83dbSDimitry Andric     // Get rid of any now dead instructions
12025ffd83dbSDimitry Andric     SimplifyInstructionsInBlock(cast<Instruction>(S)->getParent());
12035ffd83dbSDimitry Andric     Changed = true;
12045ffd83dbSDimitry Andric   }
12055ffd83dbSDimitry Andric   return Changed;
1206480093f4SDimitry Andric }
1207