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