1 //===-- PGOMemOPSizeOpt.cpp - Optimizations based on value profiling ===// 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 file implements the transformation that optimizes memory intrinsics 10 // such as memcpy using the size value profile. When memory intrinsic size 11 // value profile metadata is available, a single memory intrinsic is expanded 12 // to a sequence of guarded specialized versions that are called with the 13 // hottest size(s), for later expansion into more optimal inline sequences. 14 // 15 //===----------------------------------------------------------------------===// 16 17 #include "llvm/ADT/ArrayRef.h" 18 #include "llvm/ADT/Statistic.h" 19 #include "llvm/ADT/StringRef.h" 20 #include "llvm/ADT/Twine.h" 21 #include "llvm/Analysis/BlockFrequencyInfo.h" 22 #include "llvm/Analysis/DomTreeUpdater.h" 23 #include "llvm/Analysis/GlobalsModRef.h" 24 #include "llvm/Analysis/OptimizationRemarkEmitter.h" 25 #include "llvm/IR/BasicBlock.h" 26 #include "llvm/IR/DerivedTypes.h" 27 #include "llvm/IR/Dominators.h" 28 #include "llvm/IR/Function.h" 29 #include "llvm/IR/IRBuilder.h" 30 #include "llvm/IR/InstVisitor.h" 31 #include "llvm/IR/InstrTypes.h" 32 #include "llvm/IR/Instruction.h" 33 #include "llvm/IR/Instructions.h" 34 #include "llvm/IR/LLVMContext.h" 35 #include "llvm/IR/PassManager.h" 36 #include "llvm/IR/Type.h" 37 #include "llvm/InitializePasses.h" 38 #include "llvm/Pass.h" 39 #include "llvm/PassRegistry.h" 40 #include "llvm/ProfileData/InstrProf.h" 41 #include "llvm/Support/Casting.h" 42 #include "llvm/Support/CommandLine.h" 43 #include "llvm/Support/Debug.h" 44 #include "llvm/Support/ErrorHandling.h" 45 #include "llvm/Support/MathExtras.h" 46 #include "llvm/Transforms/Instrumentation.h" 47 #include "llvm/Transforms/Instrumentation/PGOInstrumentation.h" 48 #include "llvm/Transforms/Utils/BasicBlockUtils.h" 49 #include <cassert> 50 #include <cstdint> 51 #include <vector> 52 53 using namespace llvm; 54 55 #define DEBUG_TYPE "pgo-memop-opt" 56 57 STATISTIC(NumOfPGOMemOPOpt, "Number of memop intrinsics optimized."); 58 STATISTIC(NumOfPGOMemOPAnnotate, "Number of memop intrinsics annotated."); 59 60 // The minimum call count to optimize memory intrinsic calls. 61 static cl::opt<unsigned> 62 MemOPCountThreshold("pgo-memop-count-threshold", cl::Hidden, cl::ZeroOrMore, 63 cl::init(1000), 64 cl::desc("The minimum count to optimize memory " 65 "intrinsic calls")); 66 67 // Command line option to disable memory intrinsic optimization. The default is 68 // false. This is for debug purpose. 69 static cl::opt<bool> DisableMemOPOPT("disable-memop-opt", cl::init(false), 70 cl::Hidden, cl::desc("Disable optimize")); 71 72 // The percent threshold to optimize memory intrinsic calls. 73 static cl::opt<unsigned> 74 MemOPPercentThreshold("pgo-memop-percent-threshold", cl::init(40), 75 cl::Hidden, cl::ZeroOrMore, 76 cl::desc("The percentage threshold for the " 77 "memory intrinsic calls optimization")); 78 79 // Maximum number of versions for optimizing memory intrinsic call. 80 static cl::opt<unsigned> 81 MemOPMaxVersion("pgo-memop-max-version", cl::init(3), cl::Hidden, 82 cl::ZeroOrMore, 83 cl::desc("The max version for the optimized memory " 84 " intrinsic calls")); 85 86 // Scale the counts from the annotation using the BB count value. 87 static cl::opt<bool> 88 MemOPScaleCount("pgo-memop-scale-count", cl::init(true), cl::Hidden, 89 cl::desc("Scale the memop size counts using the basic " 90 " block count value")); 91 92 // This option sets the rangge of precise profile memop sizes. 93 extern cl::opt<std::string> MemOPSizeRange; 94 95 // This option sets the value that groups large memop sizes 96 extern cl::opt<unsigned> MemOPSizeLarge; 97 98 namespace { 99 class PGOMemOPSizeOptLegacyPass : public FunctionPass { 100 public: 101 static char ID; 102 103 PGOMemOPSizeOptLegacyPass() : FunctionPass(ID) { 104 initializePGOMemOPSizeOptLegacyPassPass(*PassRegistry::getPassRegistry()); 105 } 106 107 StringRef getPassName() const override { return "PGOMemOPSize"; } 108 109 private: 110 bool runOnFunction(Function &F) override; 111 void getAnalysisUsage(AnalysisUsage &AU) const override { 112 AU.addRequired<BlockFrequencyInfoWrapperPass>(); 113 AU.addRequired<OptimizationRemarkEmitterWrapperPass>(); 114 AU.addPreserved<GlobalsAAWrapperPass>(); 115 AU.addPreserved<DominatorTreeWrapperPass>(); 116 } 117 }; 118 } // end anonymous namespace 119 120 char PGOMemOPSizeOptLegacyPass::ID = 0; 121 INITIALIZE_PASS_BEGIN(PGOMemOPSizeOptLegacyPass, "pgo-memop-opt", 122 "Optimize memory intrinsic using its size value profile", 123 false, false) 124 INITIALIZE_PASS_DEPENDENCY(BlockFrequencyInfoWrapperPass) 125 INITIALIZE_PASS_END(PGOMemOPSizeOptLegacyPass, "pgo-memop-opt", 126 "Optimize memory intrinsic using its size value profile", 127 false, false) 128 129 FunctionPass *llvm::createPGOMemOPSizeOptLegacyPass() { 130 return new PGOMemOPSizeOptLegacyPass(); 131 } 132 133 namespace { 134 class MemOPSizeOpt : public InstVisitor<MemOPSizeOpt> { 135 public: 136 MemOPSizeOpt(Function &Func, BlockFrequencyInfo &BFI, 137 OptimizationRemarkEmitter &ORE, DominatorTree *DT) 138 : Func(Func), BFI(BFI), ORE(ORE), DT(DT), Changed(false) { 139 ValueDataArray = 140 std::make_unique<InstrProfValueData[]>(MemOPMaxVersion + 2); 141 // Get the MemOPSize range information from option MemOPSizeRange, 142 getMemOPSizeRangeFromOption(MemOPSizeRange, PreciseRangeStart, 143 PreciseRangeLast); 144 } 145 bool isChanged() const { return Changed; } 146 void perform() { 147 WorkList.clear(); 148 visit(Func); 149 150 for (auto &MI : WorkList) { 151 ++NumOfPGOMemOPAnnotate; 152 if (perform(MI)) { 153 Changed = true; 154 ++NumOfPGOMemOPOpt; 155 LLVM_DEBUG(dbgs() << "MemOP call: " 156 << MI->getCalledFunction()->getName() 157 << "is Transformed.\n"); 158 } 159 } 160 } 161 162 void visitMemIntrinsic(MemIntrinsic &MI) { 163 Value *Length = MI.getLength(); 164 // Not perform on constant length calls. 165 if (dyn_cast<ConstantInt>(Length)) 166 return; 167 WorkList.push_back(&MI); 168 } 169 170 private: 171 Function &Func; 172 BlockFrequencyInfo &BFI; 173 OptimizationRemarkEmitter &ORE; 174 DominatorTree *DT; 175 bool Changed; 176 std::vector<MemIntrinsic *> WorkList; 177 // Start of the previse range. 178 int64_t PreciseRangeStart; 179 // Last value of the previse range. 180 int64_t PreciseRangeLast; 181 // The space to read the profile annotation. 182 std::unique_ptr<InstrProfValueData[]> ValueDataArray; 183 bool perform(MemIntrinsic *MI); 184 185 // This kind shows which group the value falls in. For PreciseValue, we have 186 // the profile count for that value. LargeGroup groups the values that are in 187 // range [LargeValue, +inf). NonLargeGroup groups the rest of values. 188 enum MemOPSizeKind { PreciseValue, NonLargeGroup, LargeGroup }; 189 190 MemOPSizeKind getMemOPSizeKind(int64_t Value) const { 191 if (Value == MemOPSizeLarge && MemOPSizeLarge != 0) 192 return LargeGroup; 193 if (Value == PreciseRangeLast + 1) 194 return NonLargeGroup; 195 return PreciseValue; 196 } 197 }; 198 199 static const char *getMIName(const MemIntrinsic *MI) { 200 switch (MI->getIntrinsicID()) { 201 case Intrinsic::memcpy: 202 return "memcpy"; 203 case Intrinsic::memmove: 204 return "memmove"; 205 case Intrinsic::memset: 206 return "memset"; 207 default: 208 return "unknown"; 209 } 210 } 211 212 static bool isProfitable(uint64_t Count, uint64_t TotalCount) { 213 assert(Count <= TotalCount); 214 if (Count < MemOPCountThreshold) 215 return false; 216 if (Count < TotalCount * MemOPPercentThreshold / 100) 217 return false; 218 return true; 219 } 220 221 static inline uint64_t getScaledCount(uint64_t Count, uint64_t Num, 222 uint64_t Denom) { 223 if (!MemOPScaleCount) 224 return Count; 225 bool Overflowed; 226 uint64_t ScaleCount = SaturatingMultiply(Count, Num, &Overflowed); 227 return ScaleCount / Denom; 228 } 229 230 bool MemOPSizeOpt::perform(MemIntrinsic *MI) { 231 assert(MI); 232 if (MI->getIntrinsicID() == Intrinsic::memmove) 233 return false; 234 235 uint32_t NumVals, MaxNumPromotions = MemOPMaxVersion + 2; 236 uint64_t TotalCount; 237 if (!getValueProfDataFromInst(*MI, IPVK_MemOPSize, MaxNumPromotions, 238 ValueDataArray.get(), NumVals, TotalCount)) 239 return false; 240 241 uint64_t ActualCount = TotalCount; 242 uint64_t SavedTotalCount = TotalCount; 243 if (MemOPScaleCount) { 244 auto BBEdgeCount = BFI.getBlockProfileCount(MI->getParent()); 245 if (!BBEdgeCount) 246 return false; 247 ActualCount = *BBEdgeCount; 248 } 249 250 ArrayRef<InstrProfValueData> VDs(ValueDataArray.get(), NumVals); 251 LLVM_DEBUG(dbgs() << "Read one memory intrinsic profile with count " 252 << ActualCount << "\n"); 253 LLVM_DEBUG( 254 for (auto &VD 255 : VDs) { dbgs() << " (" << VD.Value << "," << VD.Count << ")\n"; }); 256 257 if (ActualCount < MemOPCountThreshold) 258 return false; 259 // Skip if the total value profiled count is 0, in which case we can't 260 // scale up the counts properly (and there is no profitable transformation). 261 if (TotalCount == 0) 262 return false; 263 264 TotalCount = ActualCount; 265 if (MemOPScaleCount) 266 LLVM_DEBUG(dbgs() << "Scale counts: numerator = " << ActualCount 267 << " denominator = " << SavedTotalCount << "\n"); 268 269 // Keeping track of the count of the default case: 270 uint64_t RemainCount = TotalCount; 271 uint64_t SavedRemainCount = SavedTotalCount; 272 SmallVector<uint64_t, 16> SizeIds; 273 SmallVector<uint64_t, 16> CaseCounts; 274 uint64_t MaxCount = 0; 275 unsigned Version = 0; 276 // Default case is in the front -- save the slot here. 277 CaseCounts.push_back(0); 278 for (auto &VD : VDs) { 279 int64_t V = VD.Value; 280 uint64_t C = VD.Count; 281 if (MemOPScaleCount) 282 C = getScaledCount(C, ActualCount, SavedTotalCount); 283 284 // Only care precise value here. 285 if (getMemOPSizeKind(V) != PreciseValue) 286 continue; 287 288 // ValueCounts are sorted on the count. Break at the first un-profitable 289 // value. 290 if (!isProfitable(C, RemainCount)) 291 break; 292 293 SizeIds.push_back(V); 294 CaseCounts.push_back(C); 295 if (C > MaxCount) 296 MaxCount = C; 297 298 assert(RemainCount >= C); 299 RemainCount -= C; 300 assert(SavedRemainCount >= VD.Count); 301 SavedRemainCount -= VD.Count; 302 303 if (++Version > MemOPMaxVersion && MemOPMaxVersion != 0) 304 break; 305 } 306 307 if (Version == 0) 308 return false; 309 310 CaseCounts[0] = RemainCount; 311 if (RemainCount > MaxCount) 312 MaxCount = RemainCount; 313 314 uint64_t SumForOpt = TotalCount - RemainCount; 315 316 LLVM_DEBUG(dbgs() << "Optimize one memory intrinsic call to " << Version 317 << " Versions (covering " << SumForOpt << " out of " 318 << TotalCount << ")\n"); 319 320 // mem_op(..., size) 321 // ==> 322 // switch (size) { 323 // case s1: 324 // mem_op(..., s1); 325 // goto merge_bb; 326 // case s2: 327 // mem_op(..., s2); 328 // goto merge_bb; 329 // ... 330 // default: 331 // mem_op(..., size); 332 // goto merge_bb; 333 // } 334 // merge_bb: 335 336 BasicBlock *BB = MI->getParent(); 337 LLVM_DEBUG(dbgs() << "\n\n== Basic Block Before ==\n"); 338 LLVM_DEBUG(dbgs() << *BB << "\n"); 339 auto OrigBBFreq = BFI.getBlockFreq(BB); 340 341 BasicBlock *DefaultBB = SplitBlock(BB, MI, DT); 342 BasicBlock::iterator It(*MI); 343 ++It; 344 assert(It != DefaultBB->end()); 345 BasicBlock *MergeBB = SplitBlock(DefaultBB, &(*It), DT); 346 MergeBB->setName("MemOP.Merge"); 347 BFI.setBlockFreq(MergeBB, OrigBBFreq.getFrequency()); 348 DefaultBB->setName("MemOP.Default"); 349 350 DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Eager); 351 auto &Ctx = Func.getContext(); 352 IRBuilder<> IRB(BB); 353 BB->getTerminator()->eraseFromParent(); 354 Value *SizeVar = MI->getLength(); 355 SwitchInst *SI = IRB.CreateSwitch(SizeVar, DefaultBB, SizeIds.size()); 356 357 // Clear the value profile data. 358 MI->setMetadata(LLVMContext::MD_prof, nullptr); 359 // If all promoted, we don't need the MD.prof metadata. 360 if (SavedRemainCount > 0 || Version != NumVals) 361 // Otherwise we need update with the un-promoted records back. 362 annotateValueSite(*Func.getParent(), *MI, VDs.slice(Version), 363 SavedRemainCount, IPVK_MemOPSize, NumVals); 364 365 LLVM_DEBUG(dbgs() << "\n\n== Basic Block After==\n"); 366 367 std::vector<DominatorTree::UpdateType> Updates; 368 if (DT) 369 Updates.reserve(2 * SizeIds.size()); 370 371 for (uint64_t SizeId : SizeIds) { 372 BasicBlock *CaseBB = BasicBlock::Create( 373 Ctx, Twine("MemOP.Case.") + Twine(SizeId), &Func, DefaultBB); 374 Instruction *NewInst = MI->clone(); 375 // Fix the argument. 376 auto *MemI = cast<MemIntrinsic>(NewInst); 377 auto *SizeType = dyn_cast<IntegerType>(MemI->getLength()->getType()); 378 assert(SizeType && "Expected integer type size argument."); 379 ConstantInt *CaseSizeId = ConstantInt::get(SizeType, SizeId); 380 MemI->setLength(CaseSizeId); 381 CaseBB->getInstList().push_back(NewInst); 382 IRBuilder<> IRBCase(CaseBB); 383 IRBCase.CreateBr(MergeBB); 384 SI->addCase(CaseSizeId, CaseBB); 385 if (DT) { 386 Updates.push_back({DominatorTree::Insert, CaseBB, MergeBB}); 387 Updates.push_back({DominatorTree::Insert, BB, CaseBB}); 388 } 389 LLVM_DEBUG(dbgs() << *CaseBB << "\n"); 390 } 391 DTU.applyUpdates(Updates); 392 Updates.clear(); 393 394 setProfMetadata(Func.getParent(), SI, CaseCounts, MaxCount); 395 396 LLVM_DEBUG(dbgs() << *BB << "\n"); 397 LLVM_DEBUG(dbgs() << *DefaultBB << "\n"); 398 LLVM_DEBUG(dbgs() << *MergeBB << "\n"); 399 400 ORE.emit([&]() { 401 using namespace ore; 402 return OptimizationRemark(DEBUG_TYPE, "memopt-opt", MI) 403 << "optimized " << NV("Intrinsic", StringRef(getMIName(MI))) 404 << " with count " << NV("Count", SumForOpt) << " out of " 405 << NV("Total", TotalCount) << " for " << NV("Versions", Version) 406 << " versions"; 407 }); 408 409 return true; 410 } 411 } // namespace 412 413 static bool PGOMemOPSizeOptImpl(Function &F, BlockFrequencyInfo &BFI, 414 OptimizationRemarkEmitter &ORE, 415 DominatorTree *DT) { 416 if (DisableMemOPOPT) 417 return false; 418 419 if (F.hasFnAttribute(Attribute::OptimizeForSize)) 420 return false; 421 MemOPSizeOpt MemOPSizeOpt(F, BFI, ORE, DT); 422 MemOPSizeOpt.perform(); 423 return MemOPSizeOpt.isChanged(); 424 } 425 426 bool PGOMemOPSizeOptLegacyPass::runOnFunction(Function &F) { 427 BlockFrequencyInfo &BFI = 428 getAnalysis<BlockFrequencyInfoWrapperPass>().getBFI(); 429 auto &ORE = getAnalysis<OptimizationRemarkEmitterWrapperPass>().getORE(); 430 auto *DTWP = getAnalysisIfAvailable<DominatorTreeWrapperPass>(); 431 DominatorTree *DT = DTWP ? &DTWP->getDomTree() : nullptr; 432 return PGOMemOPSizeOptImpl(F, BFI, ORE, DT); 433 } 434 435 namespace llvm { 436 char &PGOMemOPSizeOptID = PGOMemOPSizeOptLegacyPass::ID; 437 438 PreservedAnalyses PGOMemOPSizeOpt::run(Function &F, 439 FunctionAnalysisManager &FAM) { 440 auto &BFI = FAM.getResult<BlockFrequencyAnalysis>(F); 441 auto &ORE = FAM.getResult<OptimizationRemarkEmitterAnalysis>(F); 442 auto *DT = FAM.getCachedResult<DominatorTreeAnalysis>(F); 443 bool Changed = PGOMemOPSizeOptImpl(F, BFI, ORE, DT); 444 if (!Changed) 445 return PreservedAnalyses::all(); 446 auto PA = PreservedAnalyses(); 447 PA.preserve<GlobalsAA>(); 448 PA.preserve<DominatorTreeAnalysis>(); 449 return PA; 450 } 451 } // namespace llvm 452