LLVM 24.0.0git
PGOMemOPSizeOpt.cpp
Go to the documentation of this file.
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
18#include "llvm/ADT/ArrayRef.h"
19#include "llvm/ADT/Statistic.h"
20#include "llvm/ADT/StringRef.h"
21#include "llvm/ADT/Twine.h"
26#include "llvm/IR/BasicBlock.h"
28#include "llvm/IR/Dominators.h"
29#include "llvm/IR/Function.h"
30#include "llvm/IR/IRBuilder.h"
31#include "llvm/IR/InstVisitor.h"
32#include "llvm/IR/Instruction.h"
34#include "llvm/IR/LLVMContext.h"
35#include "llvm/IR/PassManager.h"
36#include "llvm/IR/Type.h"
38#define INSTR_PROF_VALUE_PROF_MEMOP_API
41#include "llvm/Support/Debug.h"
46#include <cassert>
47#include <cstdint>
48#include <vector>
49
50using namespace llvm;
51
52#define DEBUG_TYPE "pgo-memop-opt"
53
54STATISTIC(NumOfPGOMemOPOpt, "Number of memop intrinsics optimized.");
55STATISTIC(NumOfPGOMemOPAnnotate, "Number of memop intrinsics annotated.");
56
57namespace llvm {
58
59} // end namespace llvm
60
61namespace {
62
63static const char *getMIName(const MemIntrinsic *MI) {
64 switch (MI->getIntrinsicID()) {
65 case Intrinsic::memcpy:
66 return "memcpy";
67 case Intrinsic::memmove:
68 return "memmove";
69 case Intrinsic::memset:
70 return "memset";
71 default:
72 return "unknown";
73 }
74}
75
76// A class that abstracts a memop (memcpy, memmove, memset, memcmp and bcmp).
77struct MemOp {
78 Instruction *I;
79 MemOp(MemIntrinsic *MI) : I(MI) {}
80 MemOp(CallInst *CI) : I(CI) {}
81 MemIntrinsic *asMI() { return dyn_cast<MemIntrinsic>(I); }
82 CallInst *asCI() { return cast<CallInst>(I); }
83 MemOp clone() {
84 if (auto MI = asMI())
85 return MemOp(cast<MemIntrinsic>(MI->clone()));
86 return MemOp(cast<CallInst>(asCI()->clone()));
87 }
88 Value *getLength() {
89 if (auto MI = asMI())
90 return MI->getLength();
91 return asCI()->getArgOperand(2);
92 }
93 void setLength(Value *Length) {
94 if (auto MI = asMI())
95 return MI->setLength(Length);
96 asCI()->setArgOperand(2, Length);
97 }
98 StringRef getFuncName() {
99 if (auto MI = asMI())
100 return MI->getCalledFunction()->getName();
101 return asCI()->getCalledFunction()->getName();
102 }
103 bool isMemmove() {
104 if (auto MI = asMI())
105 if (MI->getIntrinsicID() == Intrinsic::memmove)
106 return true;
107 return false;
108 }
109 bool isMemcmp(TargetLibraryInfo &TLI) {
110 return asMI() == nullptr && TLI.getLibFunc(*asCI()) == LibFunc_memcmp;
111 }
112 bool isBcmp(TargetLibraryInfo &TLI) {
113 return asMI() == nullptr && TLI.getLibFunc(*asCI()) == LibFunc_bcmp;
114 }
115 const char *getName(TargetLibraryInfo &TLI) {
116 if (auto MI = asMI())
117 return getMIName(MI);
118 LibFunc Func = TLI.getLibFunc(*asCI());
119 if (Func == LibFunc_memcmp)
120 return "memcmp";
121 if (Func == LibFunc_bcmp)
122 return "bcmp";
123 llvm_unreachable("Must be MemIntrinsic or memcmp/bcmp CallInst");
124 return nullptr;
125 }
126};
127
128class MemOPSizeOpt : public InstVisitor<MemOPSizeOpt> {
129public:
130 MemOPSizeOpt(const InstrumentationOptions &Opts, Function &Func,
131 BlockFrequencyInfo &BFI, OptimizationRemarkEmitter &ORE,
132 DominatorTree *DT, TargetLibraryInfo &TLI)
133 : Opts(Opts), Func(Func), BFI(BFI), ORE(ORE), DT(DT), TLI(TLI),
134 Changed(false) {}
135 bool isChanged() const { return Changed; }
136 void perform() {
137 WorkList.clear();
138 visit(Func);
139
140 for (auto &MO : WorkList) {
141 ++NumOfPGOMemOPAnnotate;
142 if (perform(MO)) {
143 Changed = true;
144 ++NumOfPGOMemOPOpt;
145 LLVM_DEBUG(dbgs() << "MemOP call: " << MO.getFuncName()
146 << "is Transformed.\n");
147 }
148 }
149 }
150
151 void visitMemIntrinsic(MemIntrinsic &MI) {
152 Value *Length = MI.getLength();
153 // Not perform on constant length calls.
155 return;
156 WorkList.push_back(MemOp(&MI));
157 }
158
159 void visitCallInst(CallInst &CI) {
160 LibFunc Func = TLI.getLibFunc(CI);
161 if ((Func == LibFunc_memcmp || Func == LibFunc_bcmp) &&
163 WorkList.push_back(MemOp(&CI));
164 }
165 }
166
167private:
168 const InstrumentationOptions &Opts;
169 Function &Func;
170 BlockFrequencyInfo &BFI;
171 OptimizationRemarkEmitter &ORE;
172 DominatorTree *DT;
173 TargetLibraryInfo &TLI;
174 bool Changed;
175 std::vector<MemOp> WorkList;
176 bool perform(MemOp MO);
177};
178
179static bool isProfitable(const InstrumentationOptions &Opts, uint64_t Count,
180 uint64_t TotalCount) {
181 assert(Count <= TotalCount);
182 if (Count < Opts.pgo_memop_count_threshold)
183 return false;
184 if (Count < TotalCount * Opts.pgo_memop_percent_threshold / 100)
185 return false;
186 return true;
187}
188
189static inline uint64_t getScaledCount(const InstrumentationOptions &Opts,
191 uint64_t Denom) {
192 if (!Opts.pgo_memop_scale_count)
193 return Count;
194 bool Overflowed;
195 uint64_t ScaleCount = SaturatingMultiply(Count, Num, &Overflowed);
196 return ScaleCount / Denom;
197}
198
199bool MemOPSizeOpt::perform(MemOp MO) {
200 assert(MO.I);
201 if (MO.isMemmove())
202 return false;
203 if (!Opts.pgo_memop_optimize_memcmp_bcmp &&
204 (MO.isMemcmp(TLI) || MO.isBcmp(TLI)))
205 return false;
206
207 uint32_t MaxNumVals = INSTR_PROF_NUM_BUCKETS;
208 uint64_t TotalCount;
209 auto VDs =
210 getValueProfDataFromInst(*MO.I, IPVK_MemOPSize, MaxNumVals, TotalCount);
211 if (VDs.empty())
212 return false;
213
214 uint64_t ActualCount = TotalCount;
215 uint64_t SavedTotalCount = TotalCount;
216 if (Opts.pgo_memop_scale_count) {
217 auto BBEdgeCount = BFI.getBlockProfileCount(MO.I->getParent());
218 if (!BBEdgeCount)
219 return false;
220 ActualCount = *BBEdgeCount;
221 }
222
223 LLVM_DEBUG(dbgs() << "Read one memory intrinsic profile with count "
224 << ActualCount << "\n");
226 for (auto &VD
227 : VDs) { dbgs() << " (" << VD.Value << "," << VD.Count << ")\n"; });
228
229 if (ActualCount < Opts.pgo_memop_count_threshold)
230 return false;
231 // Skip if the total value profiled count is 0, in which case we can't
232 // scale up the counts properly (and there is no profitable transformation).
233 if (TotalCount == 0)
234 return false;
235
236 TotalCount = ActualCount;
237 if (Opts.pgo_memop_scale_count)
238 LLVM_DEBUG(dbgs() << "Scale counts: numerator = " << ActualCount
239 << " denominator = " << SavedTotalCount << "\n");
240
241 // Keeping track of the count of the default case:
242 uint64_t RemainCount = TotalCount;
243 uint64_t SavedRemainCount = SavedTotalCount;
244 SmallVector<uint64_t, 16> SizeIds;
245 SmallVector<uint64_t, 16> CaseCounts;
246 uint64_t MaxCount = 0;
247 unsigned Version = 0;
248 // Default case is in the front -- save the slot here.
249 CaseCounts.push_back(0);
251 for (auto I = VDs.begin(), E = VDs.end(); I != E; ++I) {
252 auto &VD = *I;
253 int64_t V = VD.Value;
254 uint64_t C = VD.Count;
255 if (Opts.pgo_memop_scale_count)
256 C = getScaledCount(Opts, C, ActualCount, SavedTotalCount);
257
258 if (!InstrProfIsSingleValRange(V) ||
259 V > Opts.memop_value_prof_max_opt_size) {
260 RemainingVDs.push_back(VD);
261 continue;
262 }
263
264 // ValueCounts are sorted on the count. Break at the first un-profitable
265 // value.
266 if (!isProfitable(Opts, C, RemainCount)) {
267 RemainingVDs.insert(RemainingVDs.end(), I, E);
268 break;
269 }
270
271 SizeIds.push_back(V);
272 CaseCounts.push_back(C);
273 if (C > MaxCount)
274 MaxCount = C;
275
276 assert(RemainCount >= C);
277 RemainCount -= C;
278 assert(SavedRemainCount >= VD.Count);
279 SavedRemainCount -= VD.Count;
280
281 if (++Version >= Opts.pgo_memop_max_version &&
282 Opts.pgo_memop_max_version != 0) {
283 RemainingVDs.insert(RemainingVDs.end(), I + 1, E);
284 break;
285 }
286 }
287
288 if (Version == 0)
289 return false;
290
291 CaseCounts[0] = RemainCount;
292 if (RemainCount > MaxCount)
293 MaxCount = RemainCount;
294
295 uint64_t SumForOpt = TotalCount - RemainCount;
296
297 LLVM_DEBUG(dbgs() << "Optimize one memory intrinsic call to " << Version
298 << " Versions (covering " << SumForOpt << " out of "
299 << TotalCount << ")\n");
300
301 // mem_op(..., size)
302 // ==>
303 // switch (size) {
304 // case s1:
305 // mem_op(..., s1);
306 // goto merge_bb;
307 // case s2:
308 // mem_op(..., s2);
309 // goto merge_bb;
310 // ...
311 // default:
312 // mem_op(..., size);
313 // goto merge_bb;
314 // }
315 // merge_bb:
316
317 BasicBlock *BB = MO.I->getParent();
318 LLVM_DEBUG(dbgs() << "\n\n== Basic Block Before ==\n");
319 LLVM_DEBUG(dbgs() << *BB << "\n");
320 auto OrigBBFreq = BFI.getBlockFreq(BB);
321
322 BasicBlock *DefaultBB = SplitBlock(BB, MO.I, DT);
323 BasicBlock::iterator It(*MO.I);
324 ++It;
325 assert(It != DefaultBB->end());
326 BasicBlock *MergeBB = SplitBlock(DefaultBB, &(*It), DT);
327 MergeBB->setName("MemOP.Merge");
328 BFI.setBlockFreq(MergeBB, OrigBBFreq);
329 DefaultBB->setName("MemOP.Default");
330
331 DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Eager);
332 auto &Ctx = Func.getContext();
333 IRBuilder<> IRB(BB);
335 Value *SizeVar = MO.getLength();
336 SwitchInst *SI = IRB.CreateSwitch(SizeVar, DefaultBB, SizeIds.size());
337 Type *MemOpTy = MO.I->getType();
338 PHINode *PHI = nullptr;
339 if (!MemOpTy->isVoidTy()) {
340 // Insert a phi for the return values at the merge block.
341 IRBuilder<> IRBM(MergeBB->getFirstNonPHIIt());
342 PHI = IRBM.CreatePHI(MemOpTy, SizeIds.size() + 1, "MemOP.RVMerge");
343 MO.I->replaceAllUsesWith(PHI);
344 PHI->addIncoming(MO.I, DefaultBB);
345 }
346
347 // Clear the value profile data.
348 MO.I->setMetadata(LLVMContext::MD_prof, nullptr);
349 // If all promoted, we don't need the MD.prof metadata.
350 if (SavedRemainCount > 0 || Version != VDs.size()) {
351 // Otherwise we need update with the un-promoted records back.
352 annotateValueSite(*Func.getParent(), *MO.I, RemainingVDs, SavedRemainCount,
353 IPVK_MemOPSize, VDs.size());
354 }
355
356 LLVM_DEBUG(dbgs() << "\n\n== Basic Block After==\n");
357
358 std::vector<DominatorTree::UpdateType> Updates;
359 if (DT)
360 Updates.reserve(2 * SizeIds.size());
361
362 for (uint64_t SizeId : SizeIds) {
364 Ctx, Twine("MemOP.Case.") + Twine(SizeId), &Func, DefaultBB);
365 MemOp NewMO = MO.clone();
366 // Fix the argument.
367 auto *SizeType = dyn_cast<IntegerType>(NewMO.getLength()->getType());
368 assert(SizeType && "Expected integer type size argument.");
369 ConstantInt *CaseSizeId = ConstantInt::get(SizeType, SizeId);
370 NewMO.setLength(CaseSizeId);
371 NewMO.I->insertInto(CaseBB, CaseBB->end());
372 IRBuilder<> IRBCase(CaseBB);
373 IRBCase.CreateBr(MergeBB);
374 SI->addCase(CaseSizeId, CaseBB);
375 if (!MemOpTy->isVoidTy())
376 PHI->addIncoming(NewMO.I, CaseBB);
377 if (DT) {
378 Updates.push_back({DominatorTree::Insert, CaseBB, MergeBB});
379 Updates.push_back({DominatorTree::Insert, BB, CaseBB});
380 }
381 LLVM_DEBUG(dbgs() << *CaseBB << "\n");
382 }
383 DTU.applyUpdates(Updates);
384 Updates.clear();
385
386 if (MaxCount)
387 setProfMetadata(SI, CaseCounts, MaxCount);
388
389 LLVM_DEBUG(dbgs() << *BB << "\n");
390 LLVM_DEBUG(dbgs() << *DefaultBB << "\n");
391 LLVM_DEBUG(dbgs() << *MergeBB << "\n");
392
393 ORE.emit([&]() {
394 using namespace ore;
395 return OptimizationRemark(DEBUG_TYPE, "memopt-opt", MO.I)
396 << "optimized " << NV("Memop", MO.getName(TLI)) << " with count "
397 << NV("Count", SumForOpt) << " out of " << NV("Total", TotalCount)
398 << " for " << NV("Versions", Version) << " versions";
399 });
400
401 return true;
402}
403} // namespace
404
405static bool pgoMemOPSizeOptImpl(const InstrumentationOptions &Opts, Function &F,
409 if (Opts.disable_memop_opt)
410 return false;
411
412 if (F.hasOptSize())
413 return false;
414 MemOPSizeOpt MemOPSizeOpt(Opts, F, BFI, ORE, DT, TLI);
415 MemOPSizeOpt.perform();
416 return MemOPSizeOpt.isChanged();
417}
418
421 auto &BFI = FAM.getResult<BlockFrequencyAnalysis>(F);
422 auto &ORE = FAM.getResult<OptimizationRemarkEmitterAnalysis>(F);
423 auto *DT = FAM.getCachedResult<DominatorTreeAnalysis>(F);
424 auto &TLI = FAM.getResult<TargetLibraryAnalysis>(F);
425 bool Changed =
426 pgoMemOPSizeOptImpl(InstrumentationOptions::Global, F, BFI, ORE, DT, TLI);
427 if (!Changed)
428 return PreservedAnalyses::all();
429 auto PA = PreservedAnalyses();
430 PA.preserve<DominatorTreeAnalysis>();
431 return PA;
432}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
unsigned uint64_t
Rewrite undef for PHI
Function Alias Analysis false
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
#define DEBUG_TYPE
IRTranslator LLVM IR MI
This header defines various interfaces for pass management in LLVM.
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
This file provides the interface for IR based instrumentation passes ( (profile-gen,...
static bool pgoMemOPSizeOptImpl(const InstrumentationOptions &Opts, Function &F, BlockFrequencyInfo &BFI, OptimizationRemarkEmitter &ORE, DominatorTree *DT, TargetLibraryInfo &TLI)
FunctionAnalysisManager FAM
static StringRef getName(Value *V)
static void visit(BasicBlock &Start, std::function< bool(BasicBlock *)> op)
static bool isProfitable(const StableFunctionMap::StableFunctionEntries &SFS)
This file defines the 'Statistic' class, which is designed to be an easy way to expose various metric...
#define STATISTIC(VARNAME, DESC)
Definition Statistic.h:171
#define LLVM_DEBUG(...)
Definition Debug.h:119
iterator end()
Definition BasicBlock.h:459
LLVM_ABI InstListType::const_iterator getFirstNonPHIIt() const
Returns an iterator to the first instruction in this block that is not a PHINode instruction.
static BasicBlock * Create(LLVMContext &Context, const Twine &Name="", Function *Parent=nullptr, BasicBlock *InsertBefore=nullptr)
Creates a new BasicBlock.
Definition BasicBlock.h:206
InstListType::iterator iterator
Instruction iterators...
Definition BasicBlock.h:170
const Instruction * getTerminator() const LLVM_READONLY
Returns the terminator instruction; assumes that the block is well-formed.
Definition BasicBlock.h:237
Analysis pass which computes BlockFrequencyInfo.
BlockFrequencyInfo pass uses BlockFrequencyInfoImpl implementation to estimate IR basic block frequen...
LLVM_ABI void setBlockFreq(const BasicBlock *BB, BlockFrequency Freq)
LLVM_ABI std::optional< uint64_t > getBlockProfileCount(const BasicBlock *BB) const
Returns the estimated profile count of BB.
LLVM_ABI BlockFrequency getBlockFreq(const BasicBlock *BB) const
getblockFreq - Return block frequency.
Value * getArgOperand(unsigned i) const
Analysis pass which computes a DominatorTree.
Definition Dominators.h:241
Concrete subclass of DominatorTreeBase that is used to compute a normal dominator tree.
Definition Dominators.h:122
Base class for instruction visitors.
Definition InstVisitor.h:78
LLVM_ABI InstListType::iterator eraseFromParent()
This method unlinks 'this' from the containing basic block and deletes it.
This is the common base class for memset/memcpy/memmove.
The optimization diagnostic interface.
LLVM_ABI void emit(DiagnosticInfoOptimizationBase &OptDiag)
Output the remark via the diagnostic handler and to the optimization record file.
LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &MAM)
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
iterator insert(iterator I, T &&Elt)
void push_back(const T &Elt)
Analysis pass providing the TargetLibraryInfo.
Provides information about what library functions are available for the current target.
LibFunc getLibFunc(StringRef funcName) const
Searches for a particular function name.
bool isVoidTy() const
Return true if this is 'void'.
Definition Type.h:141
LLVM_ABI void setName(const Twine &Name)
Change the name of the value.
Definition Value.cpp:394
Changed
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ BasicBlock
Various leaf nodes.
Definition ISDOpcodes.h:83
DiagnosticInfoOptimizationBase::Argument NV
NodeAddr< FuncNode * > Func
Definition RDFGraph.h:393
friend class Instruction
Iterator for Instructions in a `BasicBlock.
Definition BasicBlock.h:73
This is an optimization pass for GlobalISel generic memory operations.
@ Length
Definition DWP.cpp:577
LLVM_ABI void setProfMetadata(Instruction *TI, ArrayRef< uint64_t > EdgeCounts, uint64_t MaxCount)
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
LLVM_ABI void annotateValueSite(Module &M, Instruction &Inst, const InstrProfRecord &InstrProfR, InstrProfValueKind ValueKind, uint32_t SiteIndx, uint32_t MaxMDCount=3)
Get the value profile data for value site SiteIdx from InstrProfR and annotate the instruction Inst w...
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
IRBuilder(LLVMContext &, FolderTy, InserterTy) -> IRBuilder< FolderTy, InserterTy >
LLVM_ABI SmallVector< InstrProfValueData, 4 > getValueProfDataFromInst(const Instruction &Inst, InstrProfValueKind ValueKind, uint32_t MaxNumValueData, uint64_t &TotalC, bool GetNoICPValue=false)
Extract the value profile data from Inst and returns them if Inst is annotated with value profile dat...
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
std::enable_if_t< std::is_unsigned_v< T >, T > SaturatingMultiply(T X, T Y, bool *ResultOverflowed=nullptr)
Multiply two unsigned integers, X and Y, of type T.
Definition MathExtras.h:633
LLVM_ABI BasicBlock * SplitBlock(BasicBlock *Old, BasicBlock::iterator SplitPt, DominatorTree *DT, LoopInfo *LI=nullptr, MemorySSAUpdater *MSSAU=nullptr, const Twine &BBName="")
Split the specified block at the specified instruction.
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Count
Definition InstrProf.h:145
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
AnalysisManager< Function > FunctionAnalysisManager
Convenience typedef for the Function analysis manager.
uint64_t size() const
bool isMemmove() const