xref: /llvm-project/llvm/lib/Transforms/Utils/EntryExitInstrumenter.cpp (revision e6f76378c20bebec85f66c1574bb6bb928a79025)
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(SizeTy, 0));
45       CallInst *Call = CallInst::Create(
46           M.getOrInsertFunction(Func,
47                                 FunctionType::get(Type::getVoidTy(C), {SizePtrTy},
48                                                   /*isVarArg=*/false)),
49           {GV}, "", 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           Intrinsic::getOrInsertDeclaration(&M, Intrinsic::returnaddress),
58           ConstantInt::get(Type::getInt32Ty(C), 0), "", InsertionPt);
59       RetAddr->setDebugLoc(DL);
60 
61       FunctionCallee Fn = M.getOrInsertFunction(
62           Func, FunctionType::get(Type::getVoidTy(C), PointerType::getUnqual(C),
63                                   false));
64       CallInst *Call = CallInst::Create(Fn, RetAddr, "", InsertionPt);
65       Call->setDebugLoc(DL);
66     } else {
67       FunctionCallee Fn = M.getOrInsertFunction(Func, Type::getVoidTy(C));
68       CallInst *Call = CallInst::Create(Fn, "", InsertionPt);
69       Call->setDebugLoc(DL);
70     }
71     return;
72   }
73 
74   if (Func == "__cyg_profile_func_enter" || Func == "__cyg_profile_func_exit") {
75     Type *ArgTypes[] = {PointerType::getUnqual(C), PointerType::getUnqual(C)};
76 
77     FunctionCallee Fn = M.getOrInsertFunction(
78         Func, FunctionType::get(Type::getVoidTy(C), ArgTypes, false));
79 
80     Instruction *RetAddr = CallInst::Create(
81         Intrinsic::getOrInsertDeclaration(&M, Intrinsic::returnaddress),
82         ArrayRef<Value *>(ConstantInt::get(Type::getInt32Ty(C), 0)), "",
83         InsertionPt);
84     RetAddr->setDebugLoc(DL);
85 
86     Value *Args[] = {&CurFn, RetAddr};
87     CallInst *Call =
88         CallInst::Create(Fn, ArrayRef<Value *>(Args), "", InsertionPt);
89     Call->setDebugLoc(DL);
90     return;
91   }
92 
93   // We only know how to call a fixed set of instrumentation functions, because
94   // they all expect different arguments, etc.
95   report_fatal_error(Twine("Unknown instrumentation function: '") + Func + "'");
96 }
97 
98 static bool runOnFunction(Function &F, bool PostInlining) {
99   // The asm in a naked function may reasonably expect the argument registers
100   // and the return address register (if present) to be live. An inserted
101   // function call will clobber these registers. Simply skip naked functions for
102   // all targets.
103   if (F.hasFnAttribute(Attribute::Naked))
104     return false;
105 
106   // available_externally functions may not have definitions external to the
107   // module (e.g. gnu::always_inline). Instrumenting them might lead to linker
108   // errors if they are optimized out. Skip them like GCC.
109   if (F.hasAvailableExternallyLinkage())
110     return false;
111 
112   StringRef EntryAttr = PostInlining ? "instrument-function-entry-inlined"
113                                      : "instrument-function-entry";
114 
115   StringRef ExitAttr = PostInlining ? "instrument-function-exit-inlined"
116                                     : "instrument-function-exit";
117 
118   StringRef EntryFunc = F.getFnAttribute(EntryAttr).getValueAsString();
119   StringRef ExitFunc = F.getFnAttribute(ExitAttr).getValueAsString();
120 
121   bool Changed = false;
122 
123   // If the attribute is specified, insert instrumentation and then "consume"
124   // the attribute so that it's not inserted again if the pass should happen to
125   // run later for some reason.
126 
127   if (!EntryFunc.empty()) {
128     DebugLoc DL;
129     if (auto SP = F.getSubprogram())
130       DL = DILocation::get(SP->getContext(), SP->getScopeLine(), 0, SP);
131 
132     insertCall(F, EntryFunc, F.begin()->getFirstInsertionPt(), DL);
133     Changed = true;
134     F.removeFnAttr(EntryAttr);
135   }
136 
137   if (!ExitFunc.empty()) {
138     for (BasicBlock &BB : F) {
139       Instruction *T = BB.getTerminator();
140       if (!isa<ReturnInst>(T))
141         continue;
142 
143       // If T is preceded by a musttail call, that's the real terminator.
144       if (CallInst *CI = BB.getTerminatingMustTailCall())
145         T = CI;
146 
147       DebugLoc DL;
148       if (DebugLoc TerminatorDL = T->getDebugLoc())
149         DL = TerminatorDL;
150       else if (auto SP = F.getSubprogram())
151         DL = DILocation::get(SP->getContext(), 0, 0, SP);
152 
153       insertCall(F, ExitFunc, T->getIterator(), DL);
154       Changed = true;
155     }
156     F.removeFnAttr(ExitAttr);
157   }
158 
159   return Changed;
160 }
161 
162 namespace {
163 struct PostInlineEntryExitInstrumenter : public FunctionPass {
164   static char ID;
165   PostInlineEntryExitInstrumenter() : FunctionPass(ID) {
166     initializePostInlineEntryExitInstrumenterPass(
167         *PassRegistry::getPassRegistry());
168   }
169   void getAnalysisUsage(AnalysisUsage &AU) const override {
170     AU.addPreserved<GlobalsAAWrapperPass>();
171     AU.addPreserved<DominatorTreeWrapperPass>();
172   }
173   bool runOnFunction(Function &F) override { return ::runOnFunction(F, true); }
174 };
175 char PostInlineEntryExitInstrumenter::ID = 0;
176 }
177 
178 INITIALIZE_PASS_BEGIN(
179     PostInlineEntryExitInstrumenter, "post-inline-ee-instrument",
180     "Instrument function entry/exit with calls to e.g. mcount() "
181     "(post inlining)",
182     false, false)
183 INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
184 INITIALIZE_PASS_END(
185     PostInlineEntryExitInstrumenter, "post-inline-ee-instrument",
186     "Instrument function entry/exit with calls to e.g. mcount() "
187     "(post inlining)",
188     false, false)
189 
190 FunctionPass *llvm::createPostInlineEntryExitInstrumenterPass() {
191   return new PostInlineEntryExitInstrumenter();
192 }
193 
194 PreservedAnalyses
195 llvm::EntryExitInstrumenterPass::run(Function &F, FunctionAnalysisManager &AM) {
196   if (!runOnFunction(F, PostInlining))
197     return PreservedAnalyses::all();
198   PreservedAnalyses PA;
199   PA.preserveSet<CFGAnalyses>();
200   return PA;
201 }
202 
203 void llvm::EntryExitInstrumenterPass::printPipeline(
204     raw_ostream &OS, function_ref<StringRef(StringRef)> MapClassName2PassName) {
205   static_cast<PassInfoMixin<llvm::EntryExitInstrumenterPass> *>(this)
206       ->printPipeline(OS, MapClassName2PassName);
207   OS << '<';
208   if (PostInlining)
209     OS << "post-inline";
210   OS << '>';
211 }
212