LLVM 24.0.0git
CopyProf.cpp
Go to the documentation of this file.
1//===-- CopyProf.cpp ------------------------------------------------------===//
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 LLVM IR instrumentation passes for CopyProf.
10/// It adds enter/exit callbacks to C++ special member functions, and
11/// instruments store instructions.
12///
13/// The basic idea of the CopyProf algorithm works like this:
14/// An object copy Y is made from original object X. The shadow memory
15/// corresponding to (and owned by) Y is marked as "copied". Any subsequent
16/// memory store to the memory corresponding to Y marks the shadow memory as
17/// "modified". When Y is destroyed and all of its corresponding shadow memory
18/// is marked as "copied", the object is reported as an unnecessary copy.
19///
20//===----------------------------------------------------------------------===//
21
23
26#include "llvm/IR/Attributes.h"
28#include "llvm/IR/Function.h"
29#include "llvm/IR/IRBuilder.h"
30#include "llvm/IR/Instruction.h"
32#include "llvm/IR/Module.h"
33#include "llvm/IR/PassManager.h"
37#include <array>
38#include <cstddef>
39#include <cstdint>
40
41// TODO: Convert CopyProfPass and CopyProfStoresPass to module passes so that
42// the runtime callbacks can be cached, thus avoiding repetitive symbol table
43// lookups.
44
45using namespace llvm;
46
47// Names for the module c'tor to initialize the runtime, and the runtime
48// initialization function itself.
49constexpr StringRef CopyProfModuleCtorName = "copyprof.module_ctor";
50constexpr StringRef CopyProfInitName = "__copyprof_init";
51
52// Runtime callback function names.
54 "__copyprof_ctor_enter_callback";
56 "__copyprof_ctor_exit_callback";
58 "__copyprof_copy_ctor_enter_callback";
60 "__copyprof_copy_ctor_exit_callback";
62 "__copyprof_copy_assign_op_enter_callback";
64 "__copyprof_copy_assign_op_exit_callback";
66 "__copyprof_dtor_enter_callback";
68 "__copyprof_dtor_exit_callback";
69constexpr StringRef CopyProfStoreCallbackName = "__copyprof_store_callback";
70
71// Attribute strings used by the frontend to mark special member functions.
72constexpr StringRef CopyProfCtorAttr = "copyprof-ctor";
73constexpr StringRef CopyProfCopyCtorAttr = "copyprof-copy-ctor";
74constexpr StringRef CopyProfCopyAssignAttr = "copyprof-copy-assign-op";
75constexpr StringRef CopyProfDtorAttr = "copyprof-dtor";
76
77static bool insertModuleCtor(Module &M) {
78 bool Modified = false;
81 /*InitArgTypes=*/{},
82 /*InitArgs=*/{}, [&](Function *Ctor, FunctionCallee) {
83 // Mark the ctor so it's never instrumented itself.
84 Ctor->addFnAttr(Attribute::DisableSanitizerInstrumentation);
85 appendToGlobalCtors(M, Ctor, 0);
86 Modified = true;
87 });
88 return Modified;
89}
90
91static bool isCopyProfCandidate(const Function &F) {
92 // Must not instrument functions that are explicitly disallowed for
93 // instrumentation, or naked functions.
94 if (F.isDeclaration() ||
95 F.hasFnAttribute(Attribute::DisableSanitizerInstrumentation) ||
96 F.hasFnAttribute(Attribute::Naked))
97 return false;
98
99 if (!F.hasFnAttribute(CopyProfCtorAttr) &&
100 !F.hasFnAttribute(CopyProfCopyCtorAttr) &&
101 !F.hasFnAttribute(CopyProfCopyAssignAttr) &&
102 !F.hasFnAttribute(CopyProfDtorAttr))
103 return false;
104
105 // Don't instrument a function at all if it ends in a tail call.
106 // Alternatively, the exit callback could be placed before the tail call, but
107 // that would risk missing observable side-effects needed by CopyProf to infer
108 // memory ownership (potentially leading to false positive reports).
109 // For example, if the tail would deallocate memory then CopyProf would be
110 // unable to inspect that memory and the object could be misclassified as
111 // having been unnecessarily copied. Skipping functions ending in musttail
112 // calls therefore favors false negatives over false positives.
113 for (const BasicBlock &BB : F)
114 if (BB.getTerminatingMustTailCall())
115 return false;
116
117 return true;
118}
119
121 return !F.isDeclaration() &&
122 !F.hasFnAttribute(Attribute::DisableSanitizerInstrumentation) &&
123 !F.hasFnAttribute(Attribute::Naked);
124}
125
126// Returns the object size in bytes that was stored in the given function
127// attribute during parsing in the frontend.
128static size_t getAttrValueAsInt(const Function &F, StringRef Attr) {
129 size_t IntValue = 0;
130 [[maybe_unused]] bool Success =
131 to_integer<size_t>(F.getFnAttribute(Attr).getValueAsString(), IntValue,
132 /*Base=*/10);
133 assert(Success &&
134 "Unable to parse object size from CopyProf function attribute value.");
135 return IntValue;
136}
137
138namespace {
139
140// Instruments special member functions to call into the CopyProf runtime.
141class CopyProf {
142public:
143 explicit CopyProf(Module &M);
144 bool instrumentFunction(Function &F);
145
146private:
147 void insertCallback(Function &F, size_t ObjSize, unsigned NumArgs,
148 FunctionCallee Callback, FunctionCallee ExitCallback);
149
150 Type *IntPtrTy;
151 FunctionCallee CtorEnterCallback;
152 FunctionCallee CtorExitCallback;
153 FunctionCallee CopyCtorEnterCallback;
154 FunctionCallee CopyCtorExitCallback;
155 FunctionCallee CopyAssignOpEnterCallback;
156 FunctionCallee CopyAssignOpExitCallback;
157 FunctionCallee DtorEnterCallback;
158 FunctionCallee DtorExitCallback;
159};
160
161// Late-stage pass that instruments store instructions after all optimizations
162// have run (to avoid instrumenting stores that would be eliminated).
163class CopyProfStores {
164public:
165 explicit CopyProfStores(Module &M);
166 bool instrumentFunction(Function &F);
167
168private:
169 Type *IntPtrTy;
170 FunctionCallee StoreCallback;
171};
172
173} // namespace
174
175CopyProf::CopyProf(Module &M) {
176 LLVMContext &Ctx = M.getContext();
177 IRBuilder<> IRB(Ctx);
178 IntPtrTy = IRB.getIntPtrTy(M.getDataLayout());
179 Type *PtrTy = IRB.getPtrTy();
180 Type *VoidTy = IRB.getVoidTy();
181 // CopyProf callbacks never throw exceptions.
182 AttributeList Attr;
183 Attr = Attr.addFnAttribute(Ctx, Attribute::NoUnwind);
184 CtorEnterCallback = M.getOrInsertFunction(CopyProfCtorEnterCallbackName, Attr,
185 VoidTy, PtrTy, IntPtrTy);
186 CtorExitCallback = M.getOrInsertFunction(CopyProfCtorExitCallbackName, Attr,
187 VoidTy, PtrTy, IntPtrTy);
188 CopyCtorEnterCallback = M.getOrInsertFunction(
189 CopyProfCopyCtorEnterCallbackName, Attr, VoidTy, PtrTy, PtrTy, IntPtrTy);
190 CopyCtorExitCallback = M.getOrInsertFunction(
191 CopyProfCopyCtorExitCallbackName, Attr, VoidTy, PtrTy, PtrTy, IntPtrTy);
192 CopyAssignOpEnterCallback =
193 M.getOrInsertFunction(CopyProfCopyAssignOpEnterCallbackName, Attr, VoidTy,
194 PtrTy, PtrTy, IntPtrTy);
195 CopyAssignOpExitCallback =
196 M.getOrInsertFunction(CopyProfCopyAssignOpExitCallbackName, Attr, VoidTy,
197 PtrTy, PtrTy, IntPtrTy);
198 DtorEnterCallback = M.getOrInsertFunction(CopyProfDtorEnterCallbackName, Attr,
199 VoidTy, PtrTy, IntPtrTy);
200 DtorExitCallback = M.getOrInsertFunction(CopyProfDtorExitCallbackName, Attr,
201 VoidTy, PtrTy, IntPtrTy);
202}
203
204bool CopyProf::instrumentFunction(Function &F) {
205 bool Modified = true;
206 if (F.hasFnAttribute(CopyProfCtorAttr))
207 insertCallback(F, getAttrValueAsInt(F, CopyProfCtorAttr), /*NumArgs=*/1,
208 CtorEnterCallback, CtorExitCallback);
209 else if (F.hasFnAttribute(CopyProfCopyCtorAttr))
210 insertCallback(F, getAttrValueAsInt(F, CopyProfCopyCtorAttr), /*NumArgs=*/2,
211 CopyCtorEnterCallback, CopyCtorExitCallback);
212 else if (F.hasFnAttribute(CopyProfCopyAssignAttr))
214 /*NumArgs=*/2, CopyAssignOpEnterCallback,
215 CopyAssignOpExitCallback);
216 else if (F.hasFnAttribute(CopyProfDtorAttr))
217 insertCallback(F, getAttrValueAsInt(F, CopyProfDtorAttr), /*NumArgs=*/1,
218 DtorEnterCallback, DtorExitCallback);
219 else
220 Modified = false;
221
222 return Modified;
223}
224
225void CopyProf::insertCallback(Function &F, size_t ObjSize, unsigned NumArgs,
226 FunctionCallee EntryCallback,
227 FunctionCallee ExitCallback) {
228 auto InsertCallback = [IntPtrTy = IntPtrTy, ObjSize,
229 NumArgs](Function &F, InstrumentationIRBuilder &&IRB,
230 FunctionCallee Callback) {
232 // `this` is always the first argument to a special member function, but
233 // copy c'tor / copy assignment operator will have the other `this` ptr
234 // passed as their second argument.
235 assert(NumArgs == 1 || NumArgs == 2);
236 for (unsigned I = 0; I < NumArgs; ++I)
237 Args.push_back(F.getArg(I));
238 // The last argument to the callback is the static size of the object
239 // pointed at by `this`.
240 Args.push_back(ConstantInt::get(IntPtrTy, ObjSize));
241 IRB.CreateCall(Callback, Args);
242 };
243
244 InsertCallback(
245 F,
246 InstrumentationIRBuilder{&F.getEntryBlock(),
247 F.getEntryBlock().getFirstNonPHIOrDbgOrAlloca()},
248 EntryCallback);
249 for (BasicBlock &BB : F) {
250 Instruction *Term = BB.getTerminator();
251 if (isa<ReturnInst>(Term) || isa<ResumeInst>(Term))
252 InsertCallback(F, InstrumentationIRBuilder{Term}, ExitCallback);
253 }
254}
255
256CopyProfStores::CopyProfStores(Module &M) {
257 LLVMContext &Ctx = M.getContext();
258 IRBuilder<> IRB(Ctx);
259 IntPtrTy = IRB.getIntPtrTy(M.getDataLayout());
260 Type *PtrTy = IRB.getPtrTy();
261 Type *VoidTy = IRB.getVoidTy();
262 // CopyProf callbacks never throw exceptions.
263 AttributeList Attr;
264 Attr = Attr.addFnAttribute(Ctx, Attribute::NoUnwind);
265 StoreCallback = M.getOrInsertFunction(CopyProfStoreCallbackName, Attr, VoidTy,
266 PtrTy, IntPtrTy);
267}
268
269bool CopyProfStores::instrumentFunction(Function &F) {
270 // TODO: Handle all types of memory stores (memory intrinsics, masked store
271 // intrinsics, AtomicRMW, and AtomicCmpXchg).
272 // TODO: Skip stores to alloca if only made of fundamental types, arrays
273 // thereof and (possibly) class types that are trivial and aggregate.
274 const DataLayout &DL = F.getParent()->getDataLayout();
275 SmallVector<StoreInst *, 16> ToInstrument;
276 for (BasicBlock &BB : F) {
277 for (Instruction &I : BB) {
278 if (auto *SI = dyn_cast<StoreInst>(&I);
279 SI != nullptr && SI->getPointerAddressSpace() == 0 &&
280 !SI->hasMetadata(LLVMContext::MD_nosanitize) &&
281 // Scalable vector stores have no compile-time-constant size so skip
282 // them.
283 !DL.getTypeStoreSize(SI->getValueOperand()->getType()).isScalable())
284 ToInstrument.push_back(SI);
285 }
286 }
287 if (ToInstrument.empty())
288 return false;
289
290 for (StoreInst *SI : ToInstrument) {
291 uint64_t StoredSize =
292 DL.getTypeStoreSize(SI->getValueOperand()->getType()).getFixedValue();
293 InstrumentationIRBuilder IRB(SI);
294 std::array<Value *, 2> Args = {SI->getPointerOperand(),
295 ConstantInt::get(IntPtrTy, StoredSize)};
296 IRB.CreateCall(StoreCallback, Args);
297 }
298 return true;
299}
300
303 return PreservedAnalyses::all();
304 CopyProf Impl(*F.getParent());
305 if (!Impl.instrumentFunction(F))
306 return PreservedAnalyses::all();
309 return PA;
310}
311
316
320 return PreservedAnalyses::all();
321 CopyProfStores Impl(*F.getParent());
322 if (!Impl.instrumentFunction(F))
323 return PreservedAnalyses::all();
326 return PA;
327}
#define Success
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
This file contains the simple types necessary to represent the attributes associated with functions a...
constexpr StringRef CopyProfInitName
Definition CopyProf.cpp:50
constexpr StringRef CopyProfCtorEnterCallbackName
Definition CopyProf.cpp:53
constexpr StringRef CopyProfDtorAttr
Definition CopyProf.cpp:75
constexpr StringRef CopyProfCtorAttr
Definition CopyProf.cpp:72
constexpr StringRef CopyProfDtorEnterCallbackName
Definition CopyProf.cpp:65
constexpr StringRef CopyProfCopyCtorEnterCallbackName
Definition CopyProf.cpp:57
static bool isCopyProfStoresCandidate(const Function &F)
Definition CopyProf.cpp:120
constexpr StringRef CopyProfModuleCtorName
Definition CopyProf.cpp:49
constexpr StringRef CopyProfDtorExitCallbackName
Definition CopyProf.cpp:67
static bool isCopyProfCandidate(const Function &F)
Definition CopyProf.cpp:91
constexpr StringRef CopyProfCopyAssignOpEnterCallbackName
Definition CopyProf.cpp:61
constexpr StringRef CopyProfCopyAssignOpExitCallbackName
Definition CopyProf.cpp:63
static bool insertModuleCtor(Module &M)
Definition CopyProf.cpp:77
constexpr StringRef CopyProfStoreCallbackName
Definition CopyProf.cpp:69
constexpr StringRef CopyProfCopyCtorAttr
Definition CopyProf.cpp:73
constexpr StringRef CopyProfCtorExitCallbackName
Definition CopyProf.cpp:55
constexpr StringRef CopyProfCopyCtorExitCallbackName
Definition CopyProf.cpp:59
constexpr StringRef CopyProfCopyAssignAttr
Definition CopyProf.cpp:74
static size_t getAttrValueAsInt(const Function &F, StringRef Attr)
Definition CopyProf.cpp:128
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
Machine Check Debug Module
This file defines the SmallVector class.
This file contains some functions that are useful when dealing with strings.
LLVM Basic Block Representation.
Definition BasicBlock.h:62
Represents analyses that only rely on functions' control flow.
Definition Analysis.h:73
LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM)
Definition CopyProf.cpp:301
LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM)
Definition CopyProf.cpp:317
A handy container for a FunctionType+Callee-pointer pair, which can be passed around as a single enti...
void addFnAttr(Attribute::AttrKind Kind)
Add function attributes to this function.
Definition Function.cpp:633
const MachineFunction * getParent() const
Return the MachineFunction containing this basic block.
const DataLayout & getDataLayout() const
Return the DataLayout attached to the Module associated to this MF.
LLVM_ABI PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM)
Definition CopyProf.cpp:312
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:67
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
PreservedAnalyses & preserveSet()
Mark an analysis set as preserved.
Definition Analysis.h:151
void push_back(const T &Elt)
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
static LLVM_ABI Type * getVoidTy(LLVMContext &C)
Definition Type.cpp:282
constexpr char Args[]
Key for Kernel::Metadata::mArgs.
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
Definition Transport.h:139
friend class Instruction
Iterator for Instructions in a `BasicBlock.
Definition BasicBlock.h:73
This is an optimization pass for GlobalISel generic memory operations.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
LLVM_ABI std::pair< Function *, FunctionCallee > getOrCreateSanitizerCtorAndInitFunctions(Module &M, StringRef CtorName, StringRef InitName, ArrayRef< Type * > InitArgTypes, ArrayRef< Value * > InitArgs, function_ref< void(Function *, FunctionCallee)> FunctionsCreatedCallback, StringRef VersionCheckName=StringRef(), bool Weak=false)
Creates sanitizer constructor function lazily.
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
IRBuilder(LLVMContext &, FolderTy, InserterTy, MDNode *, ArrayRef< OperandBundleDef >) -> IRBuilder< FolderTy, InserterTy >
IntPtrTy
Definition InstrProf.h:82
LLVM_ABI void appendToGlobalCtors(Module &M, Function *F, int Priority, Constant *Data=nullptr)
Append F to the list of global ctors of module M with the given Priority.
AnalysisManager< Function > FunctionAnalysisManager
Convenience typedef for the Function analysis manager.
bool to_integer(StringRef S, N &Num, unsigned Base=0)
Convert the string S to an integer of the specified type using the radix Base. If Base is 0,...
AnalysisManager< Module > ModuleAnalysisManager
Convenience typedef for the Module analysis manager.
Definition MIRParser.h:39