1 | //===- EntryExitInstrumenter.cpp - Function Entry/Exit 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 | #include "llvm/Transforms/Utils/EntryExitInstrumenter.h" |
10 | #include "llvm/Analysis/GlobalsModRef.h" |
11 | #include "llvm/IR/DebugInfoMetadata.h" |
12 | #include "llvm/IR/Dominators.h" |
13 | #include "llvm/IR/Function.h" |
14 | #include "llvm/IR/Instructions.h" |
15 | #include "llvm/IR/Intrinsics.h" |
16 | #include "llvm/IR/Module.h" |
17 | #include "llvm/IR/Type.h" |
18 | #include "llvm/InitializePasses.h" |
19 | #include "llvm/TargetParser/Triple.h" |
20 | #include "llvm/Pass.h" |
21 | #include "llvm/Transforms/Utils.h" |
22 | |
23 | using namespace llvm; |
24 | |
25 | static void insertCall(Function &CurFn, StringRef Func, |
26 | BasicBlock::iterator InsertionPt, DebugLoc DL) { |
27 | Module &M = *InsertionPt->getParent()->getParent()->getParent(); |
28 | LLVMContext &C = InsertionPt->getParent()->getContext(); |
29 | |
30 | if (Func == "mcount" || |
31 | Func == ".mcount" || |
32 | Func == "llvm.arm.gnu.eabi.mcount" || |
33 | Func == "\01_mcount" || |
34 | Func == "\01mcount" || |
35 | Func == "__mcount" || |
36 | Func == "_mcount" || |
37 | Func == "__cyg_profile_func_enter_bare" ) { |
38 | Triple TargetTriple(M.getTargetTriple()); |
39 | if (TargetTriple.isOSAIX() && Func == "__mcount" ) { |
40 | Type *SizeTy = M.getDataLayout().getIntPtrType(C); |
41 | Type *SizePtrTy = PointerType::getUnqual(C); |
42 | GlobalVariable *GV = new GlobalVariable(M, SizeTy, /*isConstant=*/false, |
43 | GlobalValue::InternalLinkage, |
44 | ConstantInt::get(Ty: SizeTy, V: 0)); |
45 | CallInst *Call = CallInst::Create( |
46 | Func: M.getOrInsertFunction(Name: Func, |
47 | T: FunctionType::get(Result: Type::getVoidTy(C), Params: {SizePtrTy}, |
48 | /*isVarArg=*/false)), |
49 | Args: {GV}, NameStr: "" , InsertBefore: InsertionPt); |
50 | Call->setDebugLoc(DL); |
51 | } else if (TargetTriple.isRISCV() || TargetTriple.isAArch64() || |
52 | TargetTriple.isLoongArch()) { |
53 | // On RISC-V, AArch64, and LoongArch, the `_mcount` function takes |
54 | // `__builtin_return_address(0)` as an argument since |
55 | // `__builtin_return_address(1)` is not available on these platforms. |
56 | Instruction *RetAddr = CallInst::Create( |
57 | Func: Intrinsic::getOrInsertDeclaration(M: &M, id: Intrinsic::returnaddress), |
58 | Args: ConstantInt::get(Ty: Type::getInt32Ty(C), V: 0), NameStr: "" , InsertBefore: InsertionPt); |
59 | RetAddr->setDebugLoc(DL); |
60 | |
61 | FunctionCallee Fn = M.getOrInsertFunction( |
62 | Name: Func, T: FunctionType::get(Result: Type::getVoidTy(C), Params: PointerType::getUnqual(C), |
63 | isVarArg: false)); |
64 | CallInst *Call = CallInst::Create(Func: Fn, Args: RetAddr, NameStr: "" , InsertBefore: InsertionPt); |
65 | Call->setDebugLoc(DL); |
66 | } else if (TargetTriple.isSystemZ()) { |
67 | // skip insertion for `mcount` on SystemZ. This will be handled later in |
68 | // `emitPrologue`. Add custom attribute to denote this. |
69 | CurFn.addFnAttr( |
70 | Attr: llvm::Attribute::get(Context&: C, Kind: "systemz-instrument-function-entry" , Val: Func)); |
71 | } else { |
72 | FunctionCallee Fn = M.getOrInsertFunction(Name: Func, RetTy: Type::getVoidTy(C)); |
73 | CallInst *Call = CallInst::Create(Func: Fn, NameStr: "" , InsertBefore: InsertionPt); |
74 | Call->setDebugLoc(DL); |
75 | } |
76 | return; |
77 | } |
78 | |
79 | if (Func == "__cyg_profile_func_enter" || Func == "__cyg_profile_func_exit" ) { |
80 | Type *ArgTypes[] = {PointerType::getUnqual(C), PointerType::getUnqual(C)}; |
81 | |
82 | FunctionCallee Fn = M.getOrInsertFunction( |
83 | Name: Func, T: FunctionType::get(Result: Type::getVoidTy(C), Params: ArgTypes, isVarArg: false)); |
84 | |
85 | Instruction *RetAddr = CallInst::Create( |
86 | Func: Intrinsic::getOrInsertDeclaration(M: &M, id: Intrinsic::returnaddress), |
87 | Args: ArrayRef<Value *>(ConstantInt::get(Ty: Type::getInt32Ty(C), V: 0)), NameStr: "" , |
88 | InsertBefore: InsertionPt); |
89 | RetAddr->setDebugLoc(DL); |
90 | |
91 | Value *Args[] = {&CurFn, RetAddr}; |
92 | CallInst *Call = |
93 | CallInst::Create(Func: Fn, Args: ArrayRef<Value *>(Args), NameStr: "" , InsertBefore: InsertionPt); |
94 | Call->setDebugLoc(DL); |
95 | return; |
96 | } |
97 | |
98 | // We only know how to call a fixed set of instrumentation functions, because |
99 | // they all expect different arguments, etc. |
100 | report_fatal_error(reason: Twine("Unknown instrumentation function: '" ) + Func + "'" ); |
101 | } |
102 | |
103 | static bool runOnFunction(Function &F, bool PostInlining) { |
104 | // The asm in a naked function may reasonably expect the argument registers |
105 | // and the return address register (if present) to be live. An inserted |
106 | // function call will clobber these registers. Simply skip naked functions for |
107 | // all targets. |
108 | if (F.hasFnAttribute(Kind: Attribute::Naked)) |
109 | return false; |
110 | |
111 | // available_externally functions may not have definitions external to the |
112 | // module (e.g. gnu::always_inline). Instrumenting them might lead to linker |
113 | // errors if they are optimized out. Skip them like GCC. |
114 | if (F.hasAvailableExternallyLinkage()) |
115 | return false; |
116 | |
117 | StringRef EntryAttr = PostInlining ? "instrument-function-entry-inlined" |
118 | : "instrument-function-entry" ; |
119 | |
120 | StringRef ExitAttr = PostInlining ? "instrument-function-exit-inlined" |
121 | : "instrument-function-exit" ; |
122 | |
123 | StringRef EntryFunc = F.getFnAttribute(Kind: EntryAttr).getValueAsString(); |
124 | StringRef ExitFunc = F.getFnAttribute(Kind: ExitAttr).getValueAsString(); |
125 | |
126 | bool Changed = false; |
127 | |
128 | // If the attribute is specified, insert instrumentation and then "consume" |
129 | // the attribute so that it's not inserted again if the pass should happen to |
130 | // run later for some reason. |
131 | |
132 | if (!EntryFunc.empty()) { |
133 | DebugLoc DL; |
134 | if (auto SP = F.getSubprogram()) |
135 | DL = DILocation::get(Context&: SP->getContext(), Line: SP->getScopeLine(), Column: 0, Scope: SP); |
136 | |
137 | insertCall(CurFn&: F, Func: EntryFunc, InsertionPt: F.begin()->getFirstInsertionPt(), DL); |
138 | Changed = true; |
139 | F.removeFnAttr(Kind: EntryAttr); |
140 | } |
141 | |
142 | if (!ExitFunc.empty()) { |
143 | for (BasicBlock &BB : F) { |
144 | Instruction *T = BB.getTerminator(); |
145 | if (!isa<ReturnInst>(Val: T)) |
146 | continue; |
147 | |
148 | // If T is preceded by a musttail call, that's the real terminator. |
149 | if (CallInst *CI = BB.getTerminatingMustTailCall()) |
150 | T = CI; |
151 | |
152 | DebugLoc DL; |
153 | if (DebugLoc TerminatorDL = T->getDebugLoc()) |
154 | DL = TerminatorDL; |
155 | else if (auto SP = F.getSubprogram()) |
156 | DL = DILocation::get(Context&: SP->getContext(), Line: 0, Column: 0, Scope: SP); |
157 | |
158 | insertCall(CurFn&: F, Func: ExitFunc, InsertionPt: T->getIterator(), DL); |
159 | Changed = true; |
160 | } |
161 | F.removeFnAttr(Kind: ExitAttr); |
162 | } |
163 | |
164 | return Changed; |
165 | } |
166 | |
167 | namespace { |
168 | struct PostInlineEntryExitInstrumenter : public FunctionPass { |
169 | static char ID; |
170 | PostInlineEntryExitInstrumenter() : FunctionPass(ID) { |
171 | initializePostInlineEntryExitInstrumenterPass( |
172 | *PassRegistry::getPassRegistry()); |
173 | } |
174 | void getAnalysisUsage(AnalysisUsage &AU) const override { |
175 | AU.addPreserved<GlobalsAAWrapperPass>(); |
176 | AU.addPreserved<DominatorTreeWrapperPass>(); |
177 | } |
178 | bool runOnFunction(Function &F) override { return ::runOnFunction(F, PostInlining: true); } |
179 | }; |
180 | char PostInlineEntryExitInstrumenter::ID = 0; |
181 | } |
182 | |
183 | INITIALIZE_PASS_BEGIN( |
184 | PostInlineEntryExitInstrumenter, "post-inline-ee-instrument" , |
185 | "Instrument function entry/exit with calls to e.g. mcount() " |
186 | "(post inlining)" , |
187 | false, false) |
188 | INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) |
189 | INITIALIZE_PASS_END( |
190 | PostInlineEntryExitInstrumenter, "post-inline-ee-instrument" , |
191 | "Instrument function entry/exit with calls to e.g. mcount() " |
192 | "(post inlining)" , |
193 | false, false) |
194 | |
195 | FunctionPass *llvm::createPostInlineEntryExitInstrumenterPass() { |
196 | return new PostInlineEntryExitInstrumenter(); |
197 | } |
198 | |
199 | PreservedAnalyses |
200 | llvm::EntryExitInstrumenterPass::run(Function &F, FunctionAnalysisManager &AM) { |
201 | if (!runOnFunction(F, PostInlining)) |
202 | return PreservedAnalyses::all(); |
203 | PreservedAnalyses PA; |
204 | PA.preserveSet<CFGAnalyses>(); |
205 | return PA; |
206 | } |
207 | |
208 | void llvm::EntryExitInstrumenterPass::printPipeline( |
209 | raw_ostream &OS, function_ref<StringRef(StringRef)> MapClassName2PassName) { |
210 | static_cast<PassInfoMixin<llvm::EntryExitInstrumenterPass> *>(this) |
211 | ->printPipeline(OS, MapClassName2PassName); |
212 | OS << '<'; |
213 | if (PostInlining) |
214 | OS << "post-inline" ; |
215 | OS << '>'; |
216 | } |
217 | |