1 //===----- SVEIntrinsicOpts - SVE ACLE Intrinsics Opts --------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // Performs general IR level optimizations on SVE intrinsics. 11 // 12 // This pass performs the following optimizations: 13 // 14 // - removes unnecessary reinterpret intrinsics 15 // (llvm.aarch64.sve.convert.[to|from].svbool), e.g: 16 // %1 = @llvm.aarch64.sve.convert.to.svbool.nxv4i1(<vscale x 4 x i1> %a) 17 // %2 = @llvm.aarch64.sve.convert.from.svbool.nxv4i1(<vscale x 16 x i1> %1) 18 // 19 // - removes unnecessary ptrue intrinsics (llvm.aarch64.sve.ptrue), e.g: 20 // %1 = @llvm.aarch64.sve.ptrue.nxv4i1(i32 31) 21 // %2 = @llvm.aarch64.sve.ptrue.nxv8i1(i32 31) 22 // ; (%1 can be replaced with a reinterpret of %2) 23 // 24 // - optimizes ptest intrinsics and phi instructions where the operands are 25 // being needlessly converted to and from svbool_t. 26 // 27 //===----------------------------------------------------------------------===// 28 29 #include "Utils/AArch64BaseInfo.h" 30 #include "llvm/ADT/PostOrderIterator.h" 31 #include "llvm/ADT/SetVector.h" 32 #include "llvm/IR/Constants.h" 33 #include "llvm/IR/Dominators.h" 34 #include "llvm/IR/IRBuilder.h" 35 #include "llvm/IR/Instructions.h" 36 #include "llvm/IR/IntrinsicInst.h" 37 #include "llvm/IR/IntrinsicsAArch64.h" 38 #include "llvm/IR/LLVMContext.h" 39 #include "llvm/IR/PatternMatch.h" 40 #include "llvm/InitializePasses.h" 41 #include "llvm/Support/Debug.h" 42 43 using namespace llvm; 44 using namespace llvm::PatternMatch; 45 46 #define DEBUG_TYPE "aarch64-sve-intrinsic-opts" 47 48 namespace llvm { 49 void initializeSVEIntrinsicOptsPass(PassRegistry &); 50 } 51 52 namespace { 53 struct SVEIntrinsicOpts : public ModulePass { 54 static char ID; // Pass identification, replacement for typeid 55 SVEIntrinsicOpts() : ModulePass(ID) { 56 initializeSVEIntrinsicOptsPass(*PassRegistry::getPassRegistry()); 57 } 58 59 bool runOnModule(Module &M) override; 60 void getAnalysisUsage(AnalysisUsage &AU) const override; 61 62 private: 63 static IntrinsicInst *isReinterpretToSVBool(Value *V); 64 65 bool coalescePTrueIntrinsicCalls(BasicBlock &BB, 66 SmallSetVector<IntrinsicInst *, 4> &PTrues); 67 bool optimizePTrueIntrinsicCalls(SmallSetVector<Function *, 4> &Functions); 68 69 /// Operates at the instruction-scope. I.e., optimizations are applied local 70 /// to individual instructions. 71 static bool optimizeIntrinsic(Instruction *I); 72 bool optimizeIntrinsicCalls(SmallSetVector<Function *, 4> &Functions); 73 74 /// Operates at the function-scope. I.e., optimizations are applied local to 75 /// the functions themselves. 76 bool optimizeFunctions(SmallSetVector<Function *, 4> &Functions); 77 78 static bool optimizeConvertFromSVBool(IntrinsicInst *I); 79 static bool optimizePTest(IntrinsicInst *I); 80 static bool optimizeVectorMul(IntrinsicInst *I); 81 82 static bool processPhiNode(IntrinsicInst *I); 83 }; 84 } // end anonymous namespace 85 86 void SVEIntrinsicOpts::getAnalysisUsage(AnalysisUsage &AU) const { 87 AU.addRequired<DominatorTreeWrapperPass>(); 88 AU.setPreservesCFG(); 89 } 90 91 char SVEIntrinsicOpts::ID = 0; 92 static const char *name = "SVE intrinsics optimizations"; 93 INITIALIZE_PASS_BEGIN(SVEIntrinsicOpts, DEBUG_TYPE, name, false, false) 94 INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass); 95 INITIALIZE_PASS_END(SVEIntrinsicOpts, DEBUG_TYPE, name, false, false) 96 97 namespace llvm { 98 ModulePass *createSVEIntrinsicOptsPass() { return new SVEIntrinsicOpts(); } 99 } // namespace llvm 100 101 /// Returns V if it's a cast from <n x 16 x i1> (aka svbool_t), nullptr 102 /// otherwise. 103 IntrinsicInst *SVEIntrinsicOpts::isReinterpretToSVBool(Value *V) { 104 IntrinsicInst *I = dyn_cast<IntrinsicInst>(V); 105 if (!I) 106 return nullptr; 107 108 if (I->getIntrinsicID() != Intrinsic::aarch64_sve_convert_to_svbool) 109 return nullptr; 110 111 return I; 112 } 113 114 /// Checks if a ptrue intrinsic call is promoted. The act of promoting a 115 /// ptrue will introduce zeroing. For example: 116 /// 117 /// %1 = <vscale x 4 x i1> call @llvm.aarch64.sve.ptrue.nxv4i1(i32 31) 118 /// %2 = <vscale x 16 x i1> call @llvm.aarch64.sve.convert.to.svbool.nxv4i1(<vscale x 4 x i1> %1) 119 /// %3 = <vscale x 8 x i1> call @llvm.aarch64.sve.convert.from.svbool.nxv8i1(<vscale x 16 x i1> %2) 120 /// 121 /// %1 is promoted, because it is converted: 122 /// 123 /// <vscale x 4 x i1> => <vscale x 16 x i1> => <vscale x 8 x i1> 124 /// 125 /// via a sequence of the SVE reinterpret intrinsics convert.{to,from}.svbool. 126 bool isPTruePromoted(IntrinsicInst *PTrue) { 127 // Find all users of this intrinsic that are calls to convert-to-svbool 128 // reinterpret intrinsics. 129 SmallVector<IntrinsicInst *, 4> ConvertToUses; 130 for (User *User : PTrue->users()) { 131 if (match(User, m_Intrinsic<Intrinsic::aarch64_sve_convert_to_svbool>())) { 132 ConvertToUses.push_back(cast<IntrinsicInst>(User)); 133 } 134 } 135 136 // If no such calls were found, this is ptrue is not promoted. 137 if (ConvertToUses.empty()) 138 return false; 139 140 // Otherwise, try to find users of the convert-to-svbool intrinsics that are 141 // calls to the convert-from-svbool intrinsic, and would result in some lanes 142 // being zeroed. 143 const auto *PTrueVTy = cast<ScalableVectorType>(PTrue->getType()); 144 for (IntrinsicInst *ConvertToUse : ConvertToUses) { 145 for (User *User : ConvertToUse->users()) { 146 auto *IntrUser = dyn_cast<IntrinsicInst>(User); 147 if (IntrUser && IntrUser->getIntrinsicID() == 148 Intrinsic::aarch64_sve_convert_from_svbool) { 149 const auto *IntrUserVTy = cast<ScalableVectorType>(IntrUser->getType()); 150 151 // Would some lanes become zeroed by the conversion? 152 if (IntrUserVTy->getElementCount().getKnownMinValue() > 153 PTrueVTy->getElementCount().getKnownMinValue()) 154 // This is a promoted ptrue. 155 return true; 156 } 157 } 158 } 159 160 // If no matching calls were found, this is not a promoted ptrue. 161 return false; 162 } 163 164 /// Attempts to coalesce ptrues in a basic block. 165 bool SVEIntrinsicOpts::coalescePTrueIntrinsicCalls( 166 BasicBlock &BB, SmallSetVector<IntrinsicInst *, 4> &PTrues) { 167 if (PTrues.size() <= 1) 168 return false; 169 170 // Find the ptrue with the most lanes. 171 auto *MostEncompassingPTrue = *std::max_element( 172 PTrues.begin(), PTrues.end(), [](auto *PTrue1, auto *PTrue2) { 173 auto *PTrue1VTy = cast<ScalableVectorType>(PTrue1->getType()); 174 auto *PTrue2VTy = cast<ScalableVectorType>(PTrue2->getType()); 175 return PTrue1VTy->getElementCount().getKnownMinValue() < 176 PTrue2VTy->getElementCount().getKnownMinValue(); 177 }); 178 179 // Remove the most encompassing ptrue, as well as any promoted ptrues, leaving 180 // behind only the ptrues to be coalesced. 181 PTrues.remove(MostEncompassingPTrue); 182 PTrues.remove_if([](auto *PTrue) { return isPTruePromoted(PTrue); }); 183 184 // Hoist MostEncompassingPTrue to the start of the basic block. It is always 185 // safe to do this, since ptrue intrinsic calls are guaranteed to have no 186 // predecessors. 187 MostEncompassingPTrue->moveBefore(BB, BB.getFirstInsertionPt()); 188 189 LLVMContext &Ctx = BB.getContext(); 190 IRBuilder<> Builder(Ctx); 191 Builder.SetInsertPoint(&BB, ++MostEncompassingPTrue->getIterator()); 192 193 auto *MostEncompassingPTrueVTy = 194 cast<VectorType>(MostEncompassingPTrue->getType()); 195 auto *ConvertToSVBool = Builder.CreateIntrinsic( 196 Intrinsic::aarch64_sve_convert_to_svbool, {MostEncompassingPTrueVTy}, 197 {MostEncompassingPTrue}); 198 199 for (auto *PTrue : PTrues) { 200 auto *PTrueVTy = cast<VectorType>(PTrue->getType()); 201 202 Builder.SetInsertPoint(&BB, ++ConvertToSVBool->getIterator()); 203 auto *ConvertFromSVBool = 204 Builder.CreateIntrinsic(Intrinsic::aarch64_sve_convert_from_svbool, 205 {PTrueVTy}, {ConvertToSVBool}); 206 PTrue->replaceAllUsesWith(ConvertFromSVBool); 207 PTrue->eraseFromParent(); 208 } 209 210 return true; 211 } 212 213 /// The goal of this function is to remove redundant calls to the SVE ptrue 214 /// intrinsic in each basic block within the given functions. 215 /// 216 /// SVE ptrues have two representations in LLVM IR: 217 /// - a logical representation -- an arbitrary-width scalable vector of i1s, 218 /// i.e. <vscale x N x i1>. 219 /// - a physical representation (svbool, <vscale x 16 x i1>) -- a 16-element 220 /// scalable vector of i1s, i.e. <vscale x 16 x i1>. 221 /// 222 /// The SVE ptrue intrinsic is used to create a logical representation of an SVE 223 /// predicate. Suppose that we have two SVE ptrue intrinsic calls: P1 and P2. If 224 /// P1 creates a logical SVE predicate that is at least as wide as the logical 225 /// SVE predicate created by P2, then all of the bits that are true in the 226 /// physical representation of P2 are necessarily also true in the physical 227 /// representation of P1. P1 'encompasses' P2, therefore, the intrinsic call to 228 /// P2 is redundant and can be replaced by an SVE reinterpret of P1 via 229 /// convert.{to,from}.svbool. 230 /// 231 /// Currently, this pass only coalesces calls to SVE ptrue intrinsics 232 /// if they match the following conditions: 233 /// 234 /// - the call to the intrinsic uses either the SV_ALL or SV_POW2 patterns. 235 /// SV_ALL indicates that all bits of the predicate vector are to be set to 236 /// true. SV_POW2 indicates that all bits of the predicate vector up to the 237 /// largest power-of-two are to be set to true. 238 /// - the result of the call to the intrinsic is not promoted to a wider 239 /// predicate. In this case, keeping the extra ptrue leads to better codegen 240 /// -- coalescing here would create an irreducible chain of SVE reinterprets 241 /// via convert.{to,from}.svbool. 242 /// 243 /// EXAMPLE: 244 /// 245 /// %1 = <vscale x 8 x i1> ptrue(i32 SV_ALL) 246 /// ; Logical: <1, 1, 1, 1, 1, 1, 1, 1> 247 /// ; Physical: <1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0> 248 /// ... 249 /// 250 /// %2 = <vscale x 4 x i1> ptrue(i32 SV_ALL) 251 /// ; Logical: <1, 1, 1, 1> 252 /// ; Physical: <1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0> 253 /// ... 254 /// 255 /// Here, %2 can be replaced by an SVE reinterpret of %1, giving, for instance: 256 /// 257 /// %1 = <vscale x 8 x i1> ptrue(i32 i31) 258 /// %2 = <vscale x 16 x i1> convert.to.svbool(<vscale x 8 x i1> %1) 259 /// %3 = <vscale x 4 x i1> convert.from.svbool(<vscale x 16 x i1> %2) 260 /// 261 bool SVEIntrinsicOpts::optimizePTrueIntrinsicCalls( 262 SmallSetVector<Function *, 4> &Functions) { 263 bool Changed = false; 264 265 for (auto *F : Functions) { 266 for (auto &BB : *F) { 267 SmallSetVector<IntrinsicInst *, 4> SVAllPTrues; 268 SmallSetVector<IntrinsicInst *, 4> SVPow2PTrues; 269 270 // For each basic block, collect the used ptrues and try to coalesce them. 271 for (Instruction &I : BB) { 272 if (I.use_empty()) 273 continue; 274 275 auto *IntrI = dyn_cast<IntrinsicInst>(&I); 276 if (!IntrI || IntrI->getIntrinsicID() != Intrinsic::aarch64_sve_ptrue) 277 continue; 278 279 const auto PTruePattern = 280 cast<ConstantInt>(IntrI->getOperand(0))->getZExtValue(); 281 282 if (PTruePattern == AArch64SVEPredPattern::all) 283 SVAllPTrues.insert(IntrI); 284 if (PTruePattern == AArch64SVEPredPattern::pow2) 285 SVPow2PTrues.insert(IntrI); 286 } 287 288 Changed |= coalescePTrueIntrinsicCalls(BB, SVAllPTrues); 289 Changed |= coalescePTrueIntrinsicCalls(BB, SVPow2PTrues); 290 } 291 } 292 293 return Changed; 294 } 295 296 /// The function will remove redundant reinterprets casting in the presence 297 /// of the control flow 298 bool SVEIntrinsicOpts::processPhiNode(IntrinsicInst *X) { 299 300 SmallVector<Instruction *, 32> Worklist; 301 auto RequiredType = X->getType(); 302 303 auto *PN = dyn_cast<PHINode>(X->getArgOperand(0)); 304 assert(PN && "Expected Phi Node!"); 305 306 // Don't create a new Phi unless we can remove the old one. 307 if (!PN->hasOneUse()) 308 return false; 309 310 for (Value *IncValPhi : PN->incoming_values()) { 311 auto *Reinterpret = isReinterpretToSVBool(IncValPhi); 312 if (!Reinterpret || 313 RequiredType != Reinterpret->getArgOperand(0)->getType()) 314 return false; 315 } 316 317 // Create the new Phi 318 LLVMContext &Ctx = PN->getContext(); 319 IRBuilder<> Builder(Ctx); 320 Builder.SetInsertPoint(PN); 321 PHINode *NPN = Builder.CreatePHI(RequiredType, PN->getNumIncomingValues()); 322 Worklist.push_back(PN); 323 324 for (unsigned I = 0; I < PN->getNumIncomingValues(); I++) { 325 auto *Reinterpret = cast<Instruction>(PN->getIncomingValue(I)); 326 NPN->addIncoming(Reinterpret->getOperand(0), PN->getIncomingBlock(I)); 327 Worklist.push_back(Reinterpret); 328 } 329 330 // Cleanup Phi Node and reinterprets 331 X->replaceAllUsesWith(NPN); 332 X->eraseFromParent(); 333 334 for (auto &I : Worklist) 335 if (I->use_empty()) 336 I->eraseFromParent(); 337 338 return true; 339 } 340 341 bool SVEIntrinsicOpts::optimizePTest(IntrinsicInst *I) { 342 IntrinsicInst *Op1 = dyn_cast<IntrinsicInst>(I->getArgOperand(0)); 343 IntrinsicInst *Op2 = dyn_cast<IntrinsicInst>(I->getArgOperand(1)); 344 345 if (Op1 && Op2 && 346 Op1->getIntrinsicID() == Intrinsic::aarch64_sve_convert_to_svbool && 347 Op2->getIntrinsicID() == Intrinsic::aarch64_sve_convert_to_svbool && 348 Op1->getArgOperand(0)->getType() == Op2->getArgOperand(0)->getType()) { 349 350 Value *Ops[] = {Op1->getArgOperand(0), Op2->getArgOperand(0)}; 351 Type *Tys[] = {Op1->getArgOperand(0)->getType()}; 352 Module *M = I->getParent()->getParent()->getParent(); 353 354 auto Fn = Intrinsic::getDeclaration(M, I->getIntrinsicID(), Tys); 355 auto CI = CallInst::Create(Fn, Ops, I->getName(), I); 356 357 I->replaceAllUsesWith(CI); 358 I->eraseFromParent(); 359 if (Op1->use_empty()) 360 Op1->eraseFromParent(); 361 if (Op1 != Op2 && Op2->use_empty()) 362 Op2->eraseFromParent(); 363 364 return true; 365 } 366 367 return false; 368 } 369 370 bool SVEIntrinsicOpts::optimizeVectorMul(IntrinsicInst *I) { 371 assert((I->getIntrinsicID() == Intrinsic::aarch64_sve_mul || 372 I->getIntrinsicID() == Intrinsic::aarch64_sve_fmul) && 373 "Unexpected opcode"); 374 375 auto *OpPredicate = I->getOperand(0); 376 auto *OpMultiplicand = I->getOperand(1); 377 auto *OpMultiplier = I->getOperand(2); 378 379 // Return true if a given instruction is an aarch64_sve_dup_x intrinsic call 380 // with a unit splat value, false otherwise. 381 auto IsUnitDupX = [](auto *I) { 382 auto *IntrI = dyn_cast<IntrinsicInst>(I); 383 if (!IntrI || IntrI->getIntrinsicID() != Intrinsic::aarch64_sve_dup_x) 384 return false; 385 386 auto *SplatValue = IntrI->getOperand(0); 387 return match(SplatValue, m_FPOne()) || match(SplatValue, m_One()); 388 }; 389 390 // Return true if a given instruction is an aarch64_sve_dup intrinsic call 391 // with a unit splat value, false otherwise. 392 auto IsUnitDup = [](auto *I) { 393 auto *IntrI = dyn_cast<IntrinsicInst>(I); 394 if (!IntrI || IntrI->getIntrinsicID() != Intrinsic::aarch64_sve_dup) 395 return false; 396 397 auto *SplatValue = IntrI->getOperand(2); 398 return match(SplatValue, m_FPOne()) || match(SplatValue, m_One()); 399 }; 400 401 bool Changed = true; 402 403 // The OpMultiplier variable should always point to the dup (if any), so 404 // swap if necessary. 405 if (IsUnitDup(OpMultiplicand) || IsUnitDupX(OpMultiplicand)) 406 std::swap(OpMultiplier, OpMultiplicand); 407 408 if (IsUnitDupX(OpMultiplier)) { 409 // [f]mul pg (dupx 1) %n => %n 410 I->replaceAllUsesWith(OpMultiplicand); 411 I->eraseFromParent(); 412 Changed = true; 413 } else if (IsUnitDup(OpMultiplier)) { 414 // [f]mul pg (dup pg 1) %n => %n 415 auto *DupInst = cast<IntrinsicInst>(OpMultiplier); 416 auto *DupPg = DupInst->getOperand(1); 417 // TODO: this is naive. The optimization is still valid if DupPg 418 // 'encompasses' OpPredicate, not only if they're the same predicate. 419 if (OpPredicate == DupPg) { 420 I->replaceAllUsesWith(OpMultiplicand); 421 I->eraseFromParent(); 422 Changed = true; 423 } 424 } 425 426 // If an instruction was optimized out then it is possible that some dangling 427 // instructions are left. 428 if (Changed) { 429 auto *OpPredicateInst = dyn_cast<Instruction>(OpPredicate); 430 auto *OpMultiplierInst = dyn_cast<Instruction>(OpMultiplier); 431 if (OpMultiplierInst && OpMultiplierInst->use_empty()) 432 OpMultiplierInst->eraseFromParent(); 433 if (OpPredicateInst && OpPredicateInst->use_empty()) 434 OpPredicateInst->eraseFromParent(); 435 } 436 437 return Changed; 438 } 439 440 bool SVEIntrinsicOpts::optimizeConvertFromSVBool(IntrinsicInst *I) { 441 assert(I->getIntrinsicID() == Intrinsic::aarch64_sve_convert_from_svbool && 442 "Unexpected opcode"); 443 444 // If the reinterpret instruction operand is a PHI Node 445 if (isa<PHINode>(I->getArgOperand(0))) 446 return processPhiNode(I); 447 448 SmallVector<Instruction *, 32> CandidatesForRemoval; 449 Value *Cursor = I->getOperand(0), *EarliestReplacement = nullptr; 450 451 const auto *IVTy = cast<VectorType>(I->getType()); 452 453 // Walk the chain of conversions. 454 while (Cursor) { 455 // If the type of the cursor has fewer lanes than the final result, zeroing 456 // must take place, which breaks the equivalence chain. 457 const auto *CursorVTy = cast<VectorType>(Cursor->getType()); 458 if (CursorVTy->getElementCount().getKnownMinValue() < 459 IVTy->getElementCount().getKnownMinValue()) 460 break; 461 462 // If the cursor has the same type as I, it is a viable replacement. 463 if (Cursor->getType() == IVTy) 464 EarliestReplacement = Cursor; 465 466 auto *IntrinsicCursor = dyn_cast<IntrinsicInst>(Cursor); 467 468 // If this is not an SVE conversion intrinsic, this is the end of the chain. 469 if (!IntrinsicCursor || !(IntrinsicCursor->getIntrinsicID() == 470 Intrinsic::aarch64_sve_convert_to_svbool || 471 IntrinsicCursor->getIntrinsicID() == 472 Intrinsic::aarch64_sve_convert_from_svbool)) 473 break; 474 475 CandidatesForRemoval.insert(CandidatesForRemoval.begin(), IntrinsicCursor); 476 Cursor = IntrinsicCursor->getOperand(0); 477 } 478 479 // If no viable replacement in the conversion chain was found, there is 480 // nothing to do. 481 if (!EarliestReplacement) 482 return false; 483 484 I->replaceAllUsesWith(EarliestReplacement); 485 I->eraseFromParent(); 486 487 while (!CandidatesForRemoval.empty()) { 488 Instruction *Candidate = CandidatesForRemoval.pop_back_val(); 489 if (Candidate->use_empty()) 490 Candidate->eraseFromParent(); 491 } 492 return true; 493 } 494 495 bool SVEIntrinsicOpts::optimizeIntrinsic(Instruction *I) { 496 IntrinsicInst *IntrI = dyn_cast<IntrinsicInst>(I); 497 if (!IntrI) 498 return false; 499 500 switch (IntrI->getIntrinsicID()) { 501 case Intrinsic::aarch64_sve_convert_from_svbool: 502 return optimizeConvertFromSVBool(IntrI); 503 case Intrinsic::aarch64_sve_fmul: 504 case Intrinsic::aarch64_sve_mul: 505 return optimizeVectorMul(IntrI); 506 case Intrinsic::aarch64_sve_ptest_any: 507 case Intrinsic::aarch64_sve_ptest_first: 508 case Intrinsic::aarch64_sve_ptest_last: 509 return optimizePTest(IntrI); 510 default: 511 return false; 512 } 513 514 return true; 515 } 516 517 bool SVEIntrinsicOpts::optimizeIntrinsicCalls( 518 SmallSetVector<Function *, 4> &Functions) { 519 bool Changed = false; 520 for (auto *F : Functions) { 521 DominatorTree *DT = &getAnalysis<DominatorTreeWrapperPass>(*F).getDomTree(); 522 523 // Traverse the DT with an rpo walk so we see defs before uses, allowing 524 // simplification to be done incrementally. 525 BasicBlock *Root = DT->getRoot(); 526 ReversePostOrderTraversal<BasicBlock *> RPOT(Root); 527 for (auto *BB : RPOT) 528 for (Instruction &I : make_early_inc_range(*BB)) 529 Changed |= optimizeIntrinsic(&I); 530 } 531 return Changed; 532 } 533 534 bool SVEIntrinsicOpts::optimizeFunctions( 535 SmallSetVector<Function *, 4> &Functions) { 536 bool Changed = false; 537 538 Changed |= optimizePTrueIntrinsicCalls(Functions); 539 Changed |= optimizeIntrinsicCalls(Functions); 540 541 return Changed; 542 } 543 544 bool SVEIntrinsicOpts::runOnModule(Module &M) { 545 bool Changed = false; 546 SmallSetVector<Function *, 4> Functions; 547 548 // Check for SVE intrinsic declarations first so that we only iterate over 549 // relevant functions. Where an appropriate declaration is found, store the 550 // function(s) where it is used so we can target these only. 551 for (auto &F : M.getFunctionList()) { 552 if (!F.isDeclaration()) 553 continue; 554 555 switch (F.getIntrinsicID()) { 556 case Intrinsic::aarch64_sve_convert_from_svbool: 557 case Intrinsic::aarch64_sve_ptest_any: 558 case Intrinsic::aarch64_sve_ptest_first: 559 case Intrinsic::aarch64_sve_ptest_last: 560 case Intrinsic::aarch64_sve_ptrue: 561 case Intrinsic::aarch64_sve_mul: 562 case Intrinsic::aarch64_sve_fmul: 563 for (User *U : F.users()) 564 Functions.insert(cast<Instruction>(U)->getFunction()); 565 break; 566 default: 567 break; 568 } 569 } 570 571 if (!Functions.empty()) 572 Changed |= optimizeFunctions(Functions); 573 574 return Changed; 575 } 576