LLVM 24.0.0git
PGOCtxProfLowering.cpp
Go to the documentation of this file.
1//===- PGOCtxProfLowering.cpp - Contextual PGO Instr. Lowering ------------===//
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
12#include "llvm/ADT/STLExtras.h"
13#include "llvm/Analysis/CFG.h"
15#include "llvm/IR/Analysis.h"
16#include "llvm/IR/Constants.h"
18#include "llvm/IR/GlobalValue.h"
19#include "llvm/IR/IRBuilder.h"
20#include "llvm/IR/InstrTypes.h"
23#include "llvm/IR/Module.h"
24#include "llvm/IR/PassManager.h"
27#include <utility>
28
29using namespace llvm;
30
31#define DEBUG_TYPE "ctx-instr-lower"
32
34 const cl::LibraryOptionsContext &LibraryOptions) {
35 return !LibraryOptions.get<InstrumentationOptions>()
36 .profile_context_root.empty();
37}
38
39// the names of symbols we expect in compiler-rt. Using a namespace for
40// readability.
42static auto StartCtx = "__llvm_ctx_profile_start_context";
43static auto ReleaseCtx = "__llvm_ctx_profile_release_context";
44static auto GetCtx = "__llvm_ctx_profile_get_context";
45static auto ExpectedCalleeTLS = "__llvm_ctx_profile_expected_callee";
46static auto CallsiteTLS = "__llvm_ctx_profile_callsite";
47} // namespace CompilerRtAPINames
48
49namespace {
50// The lowering logic and state.
51class CtxInstrumentationLowerer final {
52 Module &M;
54 Type *ContextNodeTy = nullptr;
55 StructType *FunctionDataTy = nullptr;
56
57 DenseSet<const Function *> ContextRootSet;
58 Function *StartCtx = nullptr;
59 Function *GetCtx = nullptr;
60 Function *ReleaseCtx = nullptr;
61 GlobalVariable *ExpectedCalleeTLS = nullptr;
62 GlobalVariable *CallsiteInfoTLS = nullptr;
63 Constant *CannotBeRootInitializer = nullptr;
64
65public:
66 CtxInstrumentationLowerer(Module &M, ModuleAnalysisManager &MAM);
67 // return true if lowering happened (i.e. a change was made)
68 bool lowerFunction(Function &F);
69};
70
71// llvm.instrprof.increment[.step] captures the total number of counters as one
72// of its parameters, and llvm.instrprof.callsite captures the total number of
73// callsites. Those values are the same for instances of those intrinsics in
74// this function. Find the first instance of each and return them.
75std::pair<uint32_t, uint32_t> getNumCountersAndCallsites(const Function &F) {
76 uint32_t NumCounters = 0;
77 uint32_t NumCallsites = 0;
78 for (const auto &BB : F) {
79 for (const auto &I : BB) {
80 if (const auto *Incr = dyn_cast<InstrProfIncrementInst>(&I)) {
81 uint32_t V =
82 static_cast<uint32_t>(Incr->getNumCounters()->getZExtValue());
83 assert((!NumCounters || V == NumCounters) &&
84 "expected all llvm.instrprof.increment[.step] intrinsics to "
85 "have the same total nr of counters parameter");
86 NumCounters = V;
87 } else if (const auto *CSIntr = dyn_cast<InstrProfCallsite>(&I)) {
88 uint32_t V =
89 static_cast<uint32_t>(CSIntr->getNumCounters()->getZExtValue());
90 assert((!NumCallsites || V == NumCallsites) &&
91 "expected all llvm.instrprof.callsite intrinsics to have the "
92 "same total nr of callsites parameter");
93 NumCallsites = V;
94 }
95#ifdef NDEBUG
96 if (NumCounters && NumCallsites)
97 return std::make_pair(NumCounters, NumCallsites);
98#endif
99 }
100 }
101 return {NumCounters, NumCallsites};
102}
103
104void emitUnsupportedRootError(const Function &F, StringRef Reason) {
105 F.getContext().emitError("[ctxprof] The function " + F.getName() +
106 " was indicated as context root but " + Reason +
107 ", which is not supported.");
108}
109} // namespace
110
111// set up tie-in with compiler-rt.
112// NOTE!!!
113// These have to match compiler-rt/lib/ctx_profile/CtxInstrProfiling.h
114CtxInstrumentationLowerer::CtxInstrumentationLowerer(Module &M,
116 : M(M), MAM(MAM) {
117 auto *PointerTy = PointerType::get(M.getContext(), 0);
118 auto *SanitizerMutexType = Type::getInt8Ty(M.getContext());
119 auto *I32Ty = Type::getInt32Ty(M.getContext());
120 auto *I64Ty = Type::getInt64Ty(M.getContext());
121
122#define _PTRDECL(_, __) PointerTy,
123#define _VOLATILE_PTRDECL(_, __) PointerTy,
124#define _CONTEXT_ROOT PointerTy,
125#define _MUTEXDECL(_) SanitizerMutexType,
126
127 FunctionDataTy = StructType::get(
128 M.getContext(), {CTXPROF_FUNCTION_DATA(_PTRDECL, _CONTEXT_ROOT,
129 _VOLATILE_PTRDECL, _MUTEXDECL)});
130#undef _PTRDECL
131#undef _CONTEXT_ROOT
132#undef _VOLATILE_PTRDECL
133#undef _MUTEXDECL
134
135#define _PTRDECL(_, __) Constant::getNullValue(PointerTy),
136#define _VOLATILE_PTRDECL(_, __) _PTRDECL(_, __)
137#define _MUTEXDECL(_) Constant::getNullValue(SanitizerMutexType),
138#define _CONTEXT_ROOT \
139 Constant::getIntegerValue( \
140 PointerTy, \
141 APInt(M.getDataLayout().getPointerTypeSizeInBits(PointerTy), 1U)),
142 CannotBeRootInitializer = ConstantStruct::get(
145#undef _PTRDECL
146#undef _CONTEXT_ROOT
147#undef _VOLATILE_PTRDECL
148#undef _MUTEXDECL
149
150 // The Context header.
151 ContextNodeTy = StructType::get(M.getContext(), {
152 I64Ty, /*Guid*/
153 PointerTy, /*Next*/
154 I32Ty, /*NumCounters*/
155 I32Ty, /*NumCallsites*/
156 });
157
158 const InstrumentationOptions &Opts = getInstrumentationOptions(M);
159 // Define a global for each entrypoint. We'll reuse the entrypoint's name
160 // as prefix. We assume the entrypoint names to be unique.
161 for (StringRef Fname : Opts.profile_context_root) {
162 if (const auto *F = M.getFunction(Fname)) {
163 if (F->isDeclaration())
164 continue;
165 ContextRootSet.insert(F);
166 for (const auto &BB : *F)
167 for (const auto &I : BB)
168 if (const auto *CB = dyn_cast<CallBase>(&I))
169 if (CB->isMustTailCall())
170 emitUnsupportedRootError(*F, "it features musttail calls");
171 }
172 }
173
174 // Declare the functions we will call.
176 M.getOrInsertFunction(
178 FunctionType::get(PointerTy,
179 {PointerTy, /*FunctionData*/
180 I64Ty, /*Guid*/ I32Ty,
181 /*NumCounters*/ I32Ty /*NumCallsites*/},
182 false))
183 .getCallee());
185 M.getOrInsertFunction(CompilerRtAPINames::GetCtx,
186 FunctionType::get(PointerTy,
187 {PointerTy, /*FunctionData*/
188 PointerTy, /*Callee*/
189 I64Ty, /*Guid*/
190 I32Ty, /*NumCounters*/
191 I32Ty}, /*NumCallsites*/
192 false))
193 .getCallee());
195 M.getOrInsertFunction(CompilerRtAPINames::ReleaseCtx,
196 FunctionType::get(Type::getVoidTy(M.getContext()),
197 {
198 PointerTy, /*FunctionData*/
199 },
200 false))
201 .getCallee());
202
203 // Declare the TLSes we will need to use.
204 CallsiteInfoTLS =
205 new GlobalVariable(M, PointerTy, false, GlobalValue::ExternalLinkage,
207 CallsiteInfoTLS->setThreadLocal(true);
208 CallsiteInfoTLS->setVisibility(llvm::GlobalValue::HiddenVisibility);
210 new GlobalVariable(M, PointerTy, false, GlobalValue::ExternalLinkage,
212 ExpectedCalleeTLS->setThreadLocal(true);
214}
215
218 CtxInstrumentationLowerer Lowerer(M, MAM);
219 bool Changed = false;
220 for (auto &F : M)
221 Changed |= Lowerer.lowerFunction(F);
223}
224
225bool CtxInstrumentationLowerer::lowerFunction(Function &F) {
226 if (F.isDeclaration())
227 return false;
228
229 // Probably pointless to try to do anything here, unlikely to be
230 // performance-affecting.
231 if (!llvm::canReturn(F)) {
232 for (auto &BB : F)
233 for (auto &I : make_early_inc_range(BB))
235 I.eraseFromParent();
236 if (ContextRootSet.contains(&F))
237 emitUnsupportedRootError(F, "it does not return");
238 return true;
239 }
240
241 auto &FAM = MAM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
242 auto &ORE = FAM.getResult<OptimizationRemarkEmitterAnalysis>(F);
243
244 Value *Guid = nullptr;
245 auto [NumCounters, NumCallsites] = getNumCountersAndCallsites(F);
246
247 Value *Context = nullptr;
248 Value *RealContext = nullptr;
249
250 StructType *ThisContextType = nullptr;
251 Value *TheRootFunctionData = nullptr;
252 Value *ExpectedCalleeTLSAddr = nullptr;
253 Value *CallsiteInfoTLSAddr = nullptr;
254 const bool HasMusttail = [&F]() {
255 for (auto &BB : F)
256 for (auto &I : BB)
257 if (auto *CB = dyn_cast<CallBase>(&I))
258 if (CB->isMustTailCall())
259 return true;
260 return false;
261 }();
262
263 if (HasMusttail && ContextRootSet.contains(&F)) {
264 F.getContext().emitError(
265 "[ctx_prof] A function with musttail calls was explicitly requested as "
266 "root. That is not supported because we cannot instrument a return "
267 "instruction to release the context: " +
268 F.getName());
269 return false;
270 }
271 auto &Head = F.getEntryBlock();
272 for (auto &I : Head) {
273 // Find the increment intrinsic in the entry basic block.
274 if (auto *Mark = dyn_cast<InstrProfIncrementInst>(&I)) {
275 assert(Mark->getIndex()->isZero());
276
277 IRBuilder<> Builder(Mark);
278 Guid = Builder.getInt64(cast<Function>(*Mark->getNameValue()).getGUID());
279 // The type of the context of this function is now knowable since we have
280 // NumCallsites and NumCounters. We declare it here because it's more
281 // convenient - we have the Builder.
282 ThisContextType = StructType::get(
283 F.getContext(),
284 {ContextNodeTy, ArrayType::get(Builder.getInt64Ty(), NumCounters),
285 ArrayType::get(Builder.getPtrTy(), NumCallsites)});
286 // Figure out which way we obtain the context object for this function -
287 // if it's an entrypoint, then we call StartCtx, otherwise GetCtx. In the
288 // former case, we also set TheRootFunctionData since we need to release
289 // it at the end (plus it can be used to know if we have an entrypoint or
290 // a regular function). Don't set a name, they end up taking a lot of
291 // space and we don't need them.
292
293 // Zero-initialize the FunctionData, except for functions that have
294 // musttail calls. There, we set the CtxRoot field to 1, which will be
295 // treated as a "can't be set as root".
296 TheRootFunctionData = new GlobalVariable(
297 M, FunctionDataTy, false, GlobalVariable::InternalLinkage,
298 HasMusttail ? CannotBeRootInitializer
299 : Constant::getNullValue(FunctionDataTy));
300
301 if (ContextRootSet.contains(&F)) {
302 Context = Builder.CreateCall(
303 StartCtx, {TheRootFunctionData, Guid, Builder.getInt32(NumCounters),
304 Builder.getInt32(NumCallsites)});
305 ORE.emit(
306 [&] { return OptimizationRemark(DEBUG_TYPE, "Entrypoint", &F); });
307 } else {
308 Context = Builder.CreateCall(GetCtx, {TheRootFunctionData, &F, Guid,
309 Builder.getInt32(NumCounters),
310 Builder.getInt32(NumCallsites)});
311 ORE.emit([&] {
312 return OptimizationRemark(DEBUG_TYPE, "RegularFunction", &F);
313 });
314 }
315 // The context could be scratch.
316 auto *CtxAsInt = Builder.CreatePtrToInt(Context, Builder.getInt64Ty());
317 if (NumCallsites > 0) {
318 // Figure out which index of the TLS 2-element buffers to use.
319 // Scratch context => we use index == 1. Real contexts => index == 0.
320 auto *Index = Builder.CreateAnd(CtxAsInt, Builder.getInt64(1));
321 // The GEPs corresponding to that index, in the respective TLS.
322 ExpectedCalleeTLSAddr = Builder.CreateGEP(
323 PointerType::getUnqual(F.getContext()),
324 Builder.CreateThreadLocalAddress(ExpectedCalleeTLS), {Index});
325 CallsiteInfoTLSAddr = Builder.CreateGEP(
326 Builder.getInt32Ty(),
327 Builder.CreateThreadLocalAddress(CallsiteInfoTLS), {Index});
328 }
329 // Because the context pointer may have LSB set (to indicate scratch),
330 // clear it for the value we use as base address for the counter vector.
331 // This way, if later we want to have "real" (not clobbered) buffers
332 // acting as scratch, the lowering (at least this part of it that deals
333 // with counters) stays the same.
334 RealContext = Builder.CreateIntToPtr(
335 Builder.CreateAnd(CtxAsInt, Builder.getInt64(-2)),
336 PointerType::getUnqual(F.getContext()));
337 I.eraseFromParent();
338 break;
339 }
340 }
341 if (!Context) {
342 ORE.emit([&] {
343 return OptimizationRemarkMissed(DEBUG_TYPE, "Skip", &F)
344 << "Function doesn't have instrumentation, skipping";
345 });
346 return false;
347 }
348
349 bool ContextWasReleased = false;
350 for (auto &BB : F) {
351 for (auto &I : llvm::make_early_inc_range(BB)) {
352 if (auto *Instr = dyn_cast<InstrProfCntrInstBase>(&I)) {
353 IRBuilder<> Builder(Instr);
354 switch (Instr->getIntrinsicID()) {
355 case llvm::Intrinsic::instrprof_increment:
356 case llvm::Intrinsic::instrprof_increment_step: {
357 // Increments (or increment-steps) are just a typical load - increment
358 // - store in the RealContext.
359 auto *AsStep = cast<InstrProfIncrementInst>(Instr);
360 auto *GEP = Builder.CreateGEP(
361 ThisContextType, RealContext,
362 {Builder.getInt32(0), Builder.getInt32(1), AsStep->getIndex()});
363 Builder.CreateStore(
364 Builder.CreateAdd(Builder.CreateLoad(Builder.getInt64Ty(), GEP),
365 AsStep->getStep()),
366 GEP);
367 } break;
368 case llvm::Intrinsic::instrprof_callsite:
369 // callsite lowering: write the called value in the expected callee
370 // TLS we treat the TLS as volatile because of signal handlers and to
371 // avoid these being moved away from the callsite they decorate.
372 auto *CSIntrinsic = dyn_cast<InstrProfCallsite>(Instr);
373 Builder.CreateStore(CSIntrinsic->getCallee(), ExpectedCalleeTLSAddr,
374 true);
375 // write the GEP of the slot in the sub-contexts portion of the
376 // context in TLS. Now, here, we use the actual Context value - as
377 // returned from compiler-rt - which may have the LSB set if the
378 // Context was scratch. Since the header of the context object and
379 // then the values are all 8-aligned (or, really, insofar as we care,
380 // they are even) - if the context is scratch (meaning, an odd value),
381 // so will the GEP. This is important because this is then visible to
382 // compiler-rt which will produce scratch contexts for callers that
383 // have a scratch context.
384 Builder.CreateStore(
385 Builder.CreateGEP(ThisContextType, Context,
386 {Builder.getInt32(0), Builder.getInt32(2),
387 CSIntrinsic->getIndex()}),
388 CallsiteInfoTLSAddr, true);
389 break;
390 }
391 I.eraseFromParent();
392 } else if (!HasMusttail && isa<ReturnInst>(I)) {
393 // Remember to release the context if we are an entrypoint.
394 IRBuilder<> Builder(&I);
395 Builder.CreateCall(ReleaseCtx, {TheRootFunctionData});
396 ContextWasReleased = true;
397 }
398 }
399 }
400 if (!HasMusttail && !ContextWasReleased)
402 "[ctx_prof] A function that doesn't have musttail calls was "
403 "instrumented but it has no `ret` "
404 "instructions above which to release the context: " +
405 F.getName());
406 return true;
407}
408
411 bool Changed = false;
412 for (auto &F : M) {
413 if (F.isDeclaration())
414 continue;
415 if (F.hasFnAttribute(Attribute::NoInline))
416 continue;
417 if (!F.isWeakForLinker())
418 continue;
419
420 if (F.hasFnAttribute(Attribute::AlwaysInline))
421 F.removeFnAttr(Attribute::AlwaysInline);
422
423 F.addFnAttr(Attribute::NoInline);
424 Changed = true;
425 }
426 if (Changed)
428 return PreservedAnalyses::all();
429}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
This file contains the declarations for the subclasses of Constant, which represent the different fla...
#define CTXPROF_FUNCTION_DATA(PTRDECL, CONTEXT_PTR, VOLATILE_PTRDECL, MUTEXDECL)
The internal structure of FunctionData.
#define DEBUG_TYPE
Hexagon Common GEP
Module.h This file contains the declarations for the Module class.
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
#define _MUTEXDECL(_)
#define _VOLATILE_PTRDECL(_, __)
#define _PTRDECL(_, __)
#define _CONTEXT_ROOT
FunctionAnalysisManager FAM
ModuleAnalysisManager MAM
This file contains some templates that are useful if you are working with the STL at all.
static LLVM_ABI Constant * get(StructType *T, ArrayRef< Constant * > V)
This is an important base class in LLVM.
Definition Constant.h:44
static LLVM_ABI Constant * getNullValue(Type *Ty)
Constructor to create a '0' constant of arbitrary type.
Implements a dense probed hash-table based set.
Definition DenseSet.h:281
@ HiddenVisibility
The GV is hidden.
Definition GlobalValue.h:69
@ InternalLinkage
Rename collisions when linking (static functions).
Definition GlobalValue.h:60
@ ExternalLinkage
Externally visible function.
Definition GlobalValue.h:53
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2909
LLVM_ABI void emitError(const Instruction *I, const Twine &ErrorStr)
emitError - Emit an error message to the currently installed error handler with optional location inf...
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:68
LLVM_ABI PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM)
Diagnostic information for missed-optimization remarks.
Diagnostic information for applied optimization remarks.
static LLVM_ABI bool isCtxIRPGOInstrEnabled(const cl::LibraryOptionsContext &LibraryOptions)
LLVM_ABI PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM)
static PointerType * getUnqual(LLVMContext &C)
This constructs an opaque pointer to an object in the default address space (address space zero).
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses none()
Convenience factory function for the empty preserved set.
Definition Analysis.h:115
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
Class to represent struct types.
static LLVM_ABI StructType * get(LLVMContext &Context, ArrayRef< Type * > Elements, bool isPacked=false)
This static method is the primary way to create a literal StructType.
Definition Type.cpp:467
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
LLVM Value Representation.
Definition Value.h:75
LLVMContext & getContext() const
All values hold a context through their type.
Definition Value.h:260
Changed
Pass manager infrastructure for declaring and invalidating analyses.
NodeAddr< InstrNode * > Instr
Definition RDFGraph.h:389
This is an optimization pass for GlobalISel generic memory operations.
const InstrumentationOptions & getInstrumentationOptions(const LLVMContext &C)
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
Definition STLExtras.h:649
InnerAnalysisManagerProxy< FunctionAnalysisManager, Module > FunctionAnalysisManagerModuleProxy
Provide the FunctionAnalysisManager to Module proxy.
void * PointerTy
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
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
AnalysisManager< Module > ModuleAnalysisManager
Convenience typedef for the Module analysis manager.
Definition MIRParser.h:39
LLVM_ABI bool canReturn(const Function &F)
Return true if there is at least a path through which F can return, false if there is no such path.
Definition CFG.cpp:405