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