1//===- AllocToken.cpp - Allocation token instrumentation ------------------===//
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 AllocToken, an instrumentation pass that
10// replaces allocation calls with token-enabled versions.
11//
12//===----------------------------------------------------------------------===//
13
14#include "llvm/Transforms/Instrumentation/AllocToken.h"
15#include "llvm/ADT/DenseMap.h"
16#include "llvm/ADT/SmallPtrSet.h"
17#include "llvm/ADT/SmallVector.h"
18#include "llvm/ADT/Statistic.h"
19#include "llvm/ADT/StringExtras.h"
20#include "llvm/ADT/StringRef.h"
21#include "llvm/Analysis/MemoryBuiltins.h"
22#include "llvm/Analysis/OptimizationRemarkEmitter.h"
23#include "llvm/Analysis/TargetLibraryInfo.h"
24#include "llvm/IR/Analysis.h"
25#include "llvm/IR/Attributes.h"
26#include "llvm/IR/Constants.h"
27#include "llvm/IR/DerivedTypes.h"
28#include "llvm/IR/Function.h"
29#include "llvm/IR/GlobalValue.h"
30#include "llvm/IR/IRBuilder.h"
31#include "llvm/IR/InstIterator.h"
32#include "llvm/IR/InstrTypes.h"
33#include "llvm/IR/Instructions.h"
34#include "llvm/IR/IntrinsicInst.h"
35#include "llvm/IR/Metadata.h"
36#include "llvm/IR/Module.h"
37#include "llvm/IR/PassManager.h"
38#include "llvm/IR/Type.h"
39#include "llvm/Support/AllocToken.h"
40#include "llvm/Support/Casting.h"
41#include "llvm/Support/CommandLine.h"
42#include "llvm/Support/Compiler.h"
43#include "llvm/Support/ErrorHandling.h"
44#include "llvm/Support/RandomNumberGenerator.h"
45#include "llvm/Support/SipHash.h"
46#include "llvm/Support/raw_ostream.h"
47#include <cassert>
48#include <cstddef>
49#include <cstdint>
50#include <limits>
51#include <memory>
52#include <optional>
53#include <string>
54#include <utility>
55#include <variant>
56
57using namespace llvm;
58using TokenMode = AllocTokenMode;
59
60#define DEBUG_TYPE "alloc-token"
61
62namespace {
63
64//===--- Command-line options ---------------------------------------------===//
65
66cl::opt<std::string> ClFuncPrefix("alloc-token-prefix",
67 cl::desc("The allocation function prefix"),
68 cl::Hidden, cl::init(Val: "__alloc_token_"));
69
70cl::opt<uint64_t>
71 ClMaxTokens("alloc-token-max",
72 cl::desc("Maximum number of tokens (0 = target SIZE_MAX)"),
73 cl::Hidden, cl::init(Val: 0));
74
75cl::opt<bool>
76 ClFastABI("alloc-token-fast-abi",
77 cl::desc("The token ID is encoded in the function name"),
78 cl::Hidden, cl::init(Val: false));
79
80// Instrument libcalls only by default - compatible allocators only need to take
81// care of providing standard allocation functions. With extended coverage, also
82// instrument non-libcall allocation function calls with !alloc_token
83// metadata.
84cl::opt<bool>
85 ClExtended("alloc-token-extended",
86 cl::desc("Extend coverage to custom allocation functions"),
87 cl::Hidden, cl::init(Val: false));
88
89// C++ defines ::operator new (and variants) as replaceable (vs. standard
90// library versions), which are nobuiltin, and are therefore not covered by
91// isAllocationFn(). Cover by default, as users of AllocToken are already
92// required to provide token-aware allocation functions (no defaults).
93cl::opt<bool> ClCoverReplaceableNew("alloc-token-cover-replaceable-new",
94 cl::desc("Cover replaceable operator new"),
95 cl::Hidden, cl::init(Val: true));
96
97cl::opt<uint64_t> ClFallbackToken(
98 "alloc-token-fallback",
99 cl::desc("The default fallback token where none could be determined"),
100 cl::Hidden, cl::init(Val: 0));
101
102//===--- Statistics -------------------------------------------------------===//
103
104STATISTIC(NumFunctionsModified, "Functions modified");
105STATISTIC(NumAllocationsInstrumented, "Allocations instrumented");
106
107//===----------------------------------------------------------------------===//
108
109/// Returns the !alloc_token metadata if available.
110///
111/// Expected format is: !{<type-name>, <contains-pointer>}
112MDNode *getAllocTokenMetadata(const CallBase &CB) {
113 MDNode *Ret = nullptr;
114 if (auto *II = dyn_cast<IntrinsicInst>(Val: &CB);
115 II && II->getIntrinsicID() == Intrinsic::alloc_token_id) {
116 auto *MDV = cast<MetadataAsValue>(Val: II->getArgOperand(i: 0));
117 Ret = cast<MDNode>(Val: MDV->getMetadata());
118 // If the intrinsic has an empty MDNode, type inference failed.
119 if (Ret->getNumOperands() == 0)
120 return nullptr;
121 } else {
122 Ret = CB.getMetadata(KindID: LLVMContext::MD_alloc_token);
123 if (!Ret)
124 return nullptr;
125 }
126 assert(Ret->getNumOperands() == 2 && "bad !alloc_token");
127 assert(isa<MDString>(Ret->getOperand(0)));
128 assert(isa<ConstantAsMetadata>(Ret->getOperand(1)));
129 return Ret;
130}
131
132bool containsPointer(const MDNode *MD) {
133 ConstantAsMetadata *C = cast<ConstantAsMetadata>(Val: MD->getOperand(I: 1));
134 auto *CI = cast<ConstantInt>(Val: C->getValue());
135 return CI->getValue().getBoolValue();
136}
137
138class ModeBase {
139public:
140 explicit ModeBase(const IntegerType &TokenTy, uint64_t MaxTokens)
141 : MaxTokens(MaxTokens ? MaxTokens : TokenTy.getBitMask()) {
142 assert(MaxTokens <= TokenTy.getBitMask());
143 }
144
145protected:
146 uint64_t boundedToken(uint64_t Val) const {
147 assert(MaxTokens != 0);
148 return Val % MaxTokens;
149 }
150
151 const uint64_t MaxTokens;
152};
153
154/// Implementation for TokenMode::Increment.
155class IncrementMode : public ModeBase {
156public:
157 using ModeBase::ModeBase;
158
159 uint64_t operator()(const CallBase &CB, OptimizationRemarkEmitter &) {
160 return boundedToken(Val: Counter++);
161 }
162
163private:
164 uint64_t Counter = 0;
165};
166
167/// Implementation for TokenMode::Random.
168class RandomMode : public ModeBase {
169public:
170 RandomMode(const IntegerType &TokenTy, uint64_t MaxTokens,
171 std::unique_ptr<RandomNumberGenerator> RNG)
172 : ModeBase(TokenTy, MaxTokens), RNG(std::move(RNG)) {}
173 uint64_t operator()(const CallBase &CB, OptimizationRemarkEmitter &) {
174 return boundedToken(Val: (*RNG)());
175 }
176
177private:
178 std::unique_ptr<RandomNumberGenerator> RNG;
179};
180
181/// Implementation for TokenMode::TypeHash. The implementation ensures
182/// hashes are stable across different compiler invocations. Uses SipHash as the
183/// hash function.
184class TypeHashMode : public ModeBase {
185public:
186 using ModeBase::ModeBase;
187
188 uint64_t operator()(const CallBase &CB, OptimizationRemarkEmitter &ORE) {
189
190 if (MDNode *N = getAllocTokenMetadata(CB)) {
191 MDString *S = cast<MDString>(Val: N->getOperand(I: 0));
192 AllocTokenMetadata Metadata{.TypeName: S->getString(), .ContainsPointer: containsPointer(MD: N)};
193 if (auto Token = getAllocToken(Mode: TokenMode::TypeHash, Metadata, MaxTokens))
194 return *Token;
195 }
196 // Fallback.
197 remarkNoMetadata(CB, ORE);
198 return ClFallbackToken;
199 }
200
201protected:
202 /// Remark that there was no precise type information.
203 static void remarkNoMetadata(const CallBase &CB,
204 OptimizationRemarkEmitter &ORE) {
205 ORE.emit(RemarkBuilder: [&] {
206 ore::NV FuncNV("Function", CB.getParent()->getParent());
207 const Function *Callee = CB.getCalledFunction();
208 ore::NV CalleeNV("Callee", Callee ? Callee->getName() : "<unknown>");
209 return OptimizationRemark(DEBUG_TYPE, "NoAllocToken", &CB)
210 << "Call to '" << CalleeNV << "' in '" << FuncNV
211 << "' without source-level type token";
212 });
213 }
214};
215
216/// Implementation for TokenMode::TypeHashPointerSplit.
217class TypeHashPointerSplitMode : public TypeHashMode {
218public:
219 using TypeHashMode::TypeHashMode;
220
221 uint64_t operator()(const CallBase &CB, OptimizationRemarkEmitter &ORE) {
222 if (MDNode *N = getAllocTokenMetadata(CB)) {
223 MDString *S = cast<MDString>(Val: N->getOperand(I: 0));
224 AllocTokenMetadata Metadata{.TypeName: S->getString(), .ContainsPointer: containsPointer(MD: N)};
225 if (auto Token = getAllocToken(Mode: TokenMode::TypeHashPointerSplit, Metadata,
226 MaxTokens))
227 return *Token;
228 }
229 // Pick the fallback token (ClFallbackToken), which by default is 0, meaning
230 // it'll fall into the pointer-less bucket. Override by setting
231 // -alloc-token-fallback if that is the wrong choice.
232 remarkNoMetadata(CB, ORE);
233 return ClFallbackToken;
234 }
235};
236
237// Apply opt overrides and module flags.
238static AllocTokenOptions resolveOptions(AllocTokenOptions Opts,
239 const Module &M) {
240 auto IntModuleFlagOrNull = [&](StringRef Key) {
241 return mdconst::extract_or_null<ConstantInt>(MD: M.getModuleFlag(Key));
242 };
243
244 if (auto *S = dyn_cast_or_null<MDString>(Val: M.getModuleFlag(Key: "alloc-token-mode")))
245 if (auto Mode = getAllocTokenModeFromString(Name: S->getString()))
246 Opts.Mode = *Mode;
247 if (auto *Val = IntModuleFlagOrNull("alloc-token-max"))
248 Opts.MaxTokens = Val->getZExtValue();
249 if (auto *Val = IntModuleFlagOrNull("alloc-token-fast-abi"))
250 Opts.FastABI |= Val->isOne();
251 if (auto *Val = IntModuleFlagOrNull("alloc-token-extended"))
252 Opts.Extended |= Val->isOne();
253
254 // Allow overriding options from command line options.
255 if (ClMaxTokens.getNumOccurrences())
256 Opts.MaxTokens = ClMaxTokens;
257 if (ClFastABI.getNumOccurrences())
258 Opts.FastABI = ClFastABI;
259 if (ClExtended.getNumOccurrences())
260 Opts.Extended = ClExtended;
261
262 return Opts;
263}
264
265class AllocToken {
266public:
267 explicit AllocToken(AllocTokenOptions Opts, Module &M,
268 ModuleAnalysisManager &MAM)
269 : Options(resolveOptions(Opts: std::move(Opts), M)), Mod(M),
270 FAM(MAM.getResult<FunctionAnalysisManagerModuleProxy>(IR&: M).getManager()),
271 Mode(IncrementMode(*IntPtrTy, Options.MaxTokens)) {
272 switch (Options.Mode) {
273 case TokenMode::Increment:
274 break;
275 case TokenMode::Random:
276 Mode.emplace<RandomMode>(args&: *IntPtrTy, args: Options.MaxTokens,
277 args: M.createRNG(DEBUG_TYPE));
278 break;
279 case TokenMode::TypeHash:
280 Mode.emplace<TypeHashMode>(args&: *IntPtrTy, args: Options.MaxTokens);
281 break;
282 case TokenMode::TypeHashPointerSplit:
283 Mode.emplace<TypeHashPointerSplitMode>(args&: *IntPtrTy, args: Options.MaxTokens);
284 break;
285 }
286 }
287
288 bool instrumentFunction(Function &F);
289
290private:
291 /// Returns the LibFunc (or NotLibFunc) if this call should be instrumented.
292 std::optional<LibFunc>
293 shouldInstrumentCall(const CallBase &CB, const TargetLibraryInfo &TLI) const;
294
295 /// Returns true for functions that are eligible for instrumentation.
296 static bool isInstrumentableLibFunc(LibFunc Func, const CallBase &CB,
297 const TargetLibraryInfo &TLI);
298
299 /// Returns true for isAllocationFn() functions that we should ignore.
300 static bool ignoreInstrumentableLibFunc(LibFunc Func);
301
302 /// Replace a call/invoke with a call/invoke to the allocation function
303 /// with token ID.
304 bool replaceAllocationCall(CallBase *CB, LibFunc Func,
305 OptimizationRemarkEmitter &ORE,
306 const TargetLibraryInfo &TLI);
307
308 /// Return replacement function for a LibFunc that takes a token ID.
309 FunctionCallee getTokenAllocFunction(const CallBase &CB, uint64_t TokenID,
310 LibFunc OriginalFunc);
311
312 /// Lower alloc_token_* intrinsics.
313 void replaceIntrinsicInst(IntrinsicInst *II, OptimizationRemarkEmitter &ORE);
314
315 /// Return the token ID from metadata in the call.
316 uint64_t getToken(const CallBase &CB, OptimizationRemarkEmitter &ORE) {
317 return std::visit(visitor: [&](auto &&Mode) { return Mode(CB, ORE); }, variants&: Mode);
318 }
319
320 const AllocTokenOptions Options;
321 Module &Mod;
322 IntegerType *IntPtrTy = Mod.getDataLayout().getIntPtrType(C&: Mod.getContext());
323 FunctionAnalysisManager &FAM;
324 // Cache for replacement functions.
325 DenseMap<std::pair<LibFunc, uint64_t>, FunctionCallee> TokenAllocFunctions;
326 // Selected mode.
327 std::variant<IncrementMode, RandomMode, TypeHashMode,
328 TypeHashPointerSplitMode>
329 Mode;
330};
331
332bool AllocToken::instrumentFunction(Function &F) {
333 // Do not apply any instrumentation for naked functions.
334 if (F.hasFnAttribute(Kind: Attribute::Naked))
335 return false;
336 // Don't touch available_externally functions, their actual body is elsewhere.
337 if (F.getLinkage() == GlobalValue::AvailableExternallyLinkage)
338 return false;
339
340 SmallVector<std::pair<CallBase *, LibFunc>, 4> AllocCalls;
341 SmallVector<IntrinsicInst *, 4> IntrinsicInsts;
342
343 // Only instrument functions that have the sanitize_alloc_token attribute.
344 const bool InstrumentFunction =
345 F.hasFnAttribute(Kind: Attribute::SanitizeAllocToken) &&
346 !F.hasFnAttribute(Kind: Attribute::DisableSanitizerInstrumentation);
347
348 // Get TLI only when required.
349 const TargetLibraryInfo *TLI =
350 InstrumentFunction ? &FAM.getResult<TargetLibraryAnalysis>(IR&: F) : nullptr;
351
352 // Collect all allocation calls to avoid iterator invalidation.
353 for (Instruction &I : instructions(F)) {
354 // Collect all alloc_token_* intrinsics.
355 if (auto *II = dyn_cast<IntrinsicInst>(Val: &I);
356 II && II->getIntrinsicID() == Intrinsic::alloc_token_id) {
357 IntrinsicInsts.emplace_back(Args&: II);
358 continue;
359 }
360
361 if (!InstrumentFunction)
362 continue;
363
364 auto *CB = dyn_cast<CallBase>(Val: &I);
365 if (!CB)
366 continue;
367 if (std::optional<LibFunc> Func = shouldInstrumentCall(CB: *CB, TLI: *TLI))
368 AllocCalls.emplace_back(Args&: CB, Args&: Func.value());
369 }
370
371 // Return early to avoid unnecessarily instantiating the ORE.
372 if (AllocCalls.empty() && IntrinsicInsts.empty())
373 return false;
374
375 auto &ORE = FAM.getResult<OptimizationRemarkEmitterAnalysis>(IR&: F);
376 bool Modified = false;
377
378 for (auto &[CB, Func] : AllocCalls)
379 Modified |= replaceAllocationCall(CB, Func, ORE, TLI: *TLI);
380
381 for (auto *II : IntrinsicInsts) {
382 replaceIntrinsicInst(II, ORE);
383 Modified = true;
384 }
385
386 if (Modified)
387 NumFunctionsModified++;
388
389 return Modified;
390}
391
392std::optional<LibFunc>
393AllocToken::shouldInstrumentCall(const CallBase &CB,
394 const TargetLibraryInfo &TLI) const {
395 const Function *Callee = CB.getCalledFunction();
396 if (!Callee)
397 return std::nullopt;
398
399 // Ignore nobuiltin of the CallBase, so that we can cover nobuiltin libcalls
400 // if requested via isInstrumentableLibFunc(). Note that isAllocationFn() is
401 // returning false for nobuiltin calls.
402 LibFunc Func;
403 if (TLI.getLibFunc(FDecl: *Callee, F&: Func)) {
404 if (isInstrumentableLibFunc(Func, CB, TLI))
405 return Func;
406 } else if (Options.Extended && CB.getMetadata(KindID: LLVMContext::MD_alloc_token)) {
407 return NotLibFunc;
408 }
409
410 return std::nullopt;
411}
412
413bool AllocToken::isInstrumentableLibFunc(LibFunc Func, const CallBase &CB,
414 const TargetLibraryInfo &TLI) {
415 if (ignoreInstrumentableLibFunc(Func))
416 return false;
417
418 if (isAllocationFn(V: &CB, TLI: &TLI))
419 return true;
420
421 switch (Func) {
422 // These libfuncs don't return normal pointers, and are therefore not handled
423 // by isAllocationFn().
424 case LibFunc_posix_memalign:
425 case LibFunc_size_returning_new:
426 case LibFunc_size_returning_new_hot_cold:
427 case LibFunc_size_returning_new_aligned:
428 case LibFunc_size_returning_new_aligned_hot_cold:
429 return true;
430
431 // See comment above ClCoverReplaceableNew.
432 case LibFunc_Znwj:
433 case LibFunc_ZnwjRKSt9nothrow_t:
434 case LibFunc_ZnwjSt11align_val_t:
435 case LibFunc_ZnwjSt11align_val_tRKSt9nothrow_t:
436 case LibFunc_Znwm:
437 case LibFunc_Znwm12__hot_cold_t:
438 case LibFunc_ZnwmRKSt9nothrow_t:
439 case LibFunc_ZnwmRKSt9nothrow_t12__hot_cold_t:
440 case LibFunc_ZnwmSt11align_val_t:
441 case LibFunc_ZnwmSt11align_val_t12__hot_cold_t:
442 case LibFunc_ZnwmSt11align_val_tRKSt9nothrow_t:
443 case LibFunc_ZnwmSt11align_val_tRKSt9nothrow_t12__hot_cold_t:
444 case LibFunc_Znaj:
445 case LibFunc_ZnajRKSt9nothrow_t:
446 case LibFunc_ZnajSt11align_val_t:
447 case LibFunc_ZnajSt11align_val_tRKSt9nothrow_t:
448 case LibFunc_Znam:
449 case LibFunc_Znam12__hot_cold_t:
450 case LibFunc_ZnamRKSt9nothrow_t:
451 case LibFunc_ZnamRKSt9nothrow_t12__hot_cold_t:
452 case LibFunc_ZnamSt11align_val_t:
453 case LibFunc_ZnamSt11align_val_t12__hot_cold_t:
454 case LibFunc_ZnamSt11align_val_tRKSt9nothrow_t:
455 case LibFunc_ZnamSt11align_val_tRKSt9nothrow_t12__hot_cold_t:
456 return ClCoverReplaceableNew;
457
458 default:
459 return false;
460 }
461}
462
463bool AllocToken::ignoreInstrumentableLibFunc(LibFunc Func) {
464 switch (Func) {
465 case LibFunc_strdup:
466 case LibFunc_dunder_strdup:
467 case LibFunc_strndup:
468 case LibFunc_dunder_strndup:
469 return true;
470 default:
471 return false;
472 }
473}
474
475bool AllocToken::replaceAllocationCall(CallBase *CB, LibFunc Func,
476 OptimizationRemarkEmitter &ORE,
477 const TargetLibraryInfo &TLI) {
478 uint64_t TokenID = getToken(CB: *CB, ORE);
479
480 FunctionCallee TokenAlloc = getTokenAllocFunction(CB: *CB, TokenID, OriginalFunc: Func);
481 if (!TokenAlloc)
482 return false;
483 NumAllocationsInstrumented++;
484
485 if (Options.FastABI) {
486 assert(TokenAlloc.getFunctionType()->getNumParams() == CB->arg_size());
487 CB->setCalledFunction(TokenAlloc);
488 return true;
489 }
490
491 IRBuilder<> IRB(CB);
492 // Original args.
493 SmallVector<Value *, 4> NewArgs{CB->args()};
494 // Add token ID, truncated to IntPtrTy width.
495 NewArgs.push_back(Elt: ConstantInt::get(Ty: IntPtrTy, V: TokenID));
496 assert(TokenAlloc.getFunctionType()->getNumParams() == NewArgs.size());
497
498 // Preserve invoke vs call semantics for exception handling.
499 CallBase *NewCall;
500 if (auto *II = dyn_cast<InvokeInst>(Val: CB)) {
501 NewCall = IRB.CreateInvoke(Callee: TokenAlloc, NormalDest: II->getNormalDest(),
502 UnwindDest: II->getUnwindDest(), Args: NewArgs);
503 } else {
504 NewCall = IRB.CreateCall(Callee: TokenAlloc, Args: NewArgs);
505 cast<CallInst>(Val: NewCall)->setTailCall(CB->isTailCall());
506 }
507 NewCall->setCallingConv(CB->getCallingConv());
508 NewCall->copyMetadata(SrcInst: *CB);
509 NewCall->setAttributes(CB->getAttributes());
510
511 // Replace all uses and delete the old call.
512 CB->replaceAllUsesWith(V: NewCall);
513 CB->eraseFromParent();
514 return true;
515}
516
517FunctionCallee AllocToken::getTokenAllocFunction(const CallBase &CB,
518 uint64_t TokenID,
519 LibFunc OriginalFunc) {
520 std::optional<std::pair<LibFunc, uint64_t>> Key;
521 if (OriginalFunc != NotLibFunc) {
522 Key = std::make_pair(x&: OriginalFunc, y: Options.FastABI ? TokenID : 0);
523 auto It = TokenAllocFunctions.find(Val: *Key);
524 if (It != TokenAllocFunctions.end())
525 return It->second;
526 }
527
528 const Function *Callee = CB.getCalledFunction();
529 if (!Callee)
530 return FunctionCallee();
531 const FunctionType *OldFTy = Callee->getFunctionType();
532 if (OldFTy->isVarArg())
533 return FunctionCallee();
534 // Copy params, and append token ID type.
535 Type *RetTy = OldFTy->getReturnType();
536 SmallVector<Type *, 4> NewParams{OldFTy->params()};
537 std::string TokenAllocName = ClFuncPrefix;
538 if (Options.FastABI)
539 TokenAllocName += utostr(X: TokenID) + "_";
540 else
541 NewParams.push_back(Elt: IntPtrTy); // token ID
542 TokenAllocName += Callee->getName();
543 FunctionType *NewFTy = FunctionType::get(Result: RetTy, Params: NewParams, isVarArg: false);
544 AttributeList NewAttrs = Callee->getAttributes();
545 FunctionCallee TokenAlloc =
546 Mod.getOrInsertFunction(Name: TokenAllocName, T: NewFTy, AttributeList: NewAttrs);
547
548 if (Key.has_value())
549 TokenAllocFunctions[*Key] = TokenAlloc;
550 return TokenAlloc;
551}
552
553void AllocToken::replaceIntrinsicInst(IntrinsicInst *II,
554 OptimizationRemarkEmitter &ORE) {
555 assert(II->getIntrinsicID() == Intrinsic::alloc_token_id);
556
557 uint64_t TokenID = getToken(CB: *II, ORE);
558 Value *V = ConstantInt::get(Ty: IntPtrTy, V: TokenID);
559 II->replaceAllUsesWith(V);
560 II->eraseFromParent();
561}
562
563} // namespace
564
565AllocTokenPass::AllocTokenPass(AllocTokenOptions Opts)
566 : Options(std::move(Opts)) {}
567
568PreservedAnalyses AllocTokenPass::run(Module &M, ModuleAnalysisManager &MAM) {
569 AllocToken Pass(Options, M, MAM);
570 bool Modified = false;
571
572 for (Function &F : M) {
573 if (F.empty())
574 continue; // declaration
575 Modified |= Pass.instrumentFunction(F);
576 }
577
578 return Modified ? PreservedAnalyses::none().preserveSet<CFGAnalyses>()
579 : PreservedAnalyses::all();
580}
581