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