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