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/Function.h"
13#include "llvm/IR/Instructions.h"
14#include "llvm/IR/Intrinsics.h"
15#include "llvm/IR/Module.h"
16#include "llvm/IR/Type.h"
17#include "llvm/InitializePasses.h"
18#include "llvm/Pass.h"
19#include "llvm/Transforms/Utils.h"
20using namespace llvm;
21
22static void insertCall(Function &CurFn, StringRef Func,
23                       Instruction *InsertionPt, DebugLoc DL) {
24  Module &M = *InsertionPt->getParent()->getParent()->getParent();
25  LLVMContext &C = InsertionPt->getParent()->getContext();
26
27  if (Func == "mcount" ||
28      Func == ".mcount" ||
29      Func == "llvm.arm.gnu.eabi.mcount" ||
30      Func == "\01_mcount" ||
31      Func == "\01mcount" ||
32      Func == "__mcount" ||
33      Func == "_mcount" ||
34      Func == "__cyg_profile_func_enter_bare") {
35    FunctionCallee Fn = M.getOrInsertFunction(Func, Type::getVoidTy(C));
36    CallInst *Call = CallInst::Create(Fn, "", InsertionPt);
37    Call->setDebugLoc(DL);
38    return;
39  }
40
41  if (Func == "__cyg_profile_func_enter" || Func == "__cyg_profile_func_exit") {
42    Type *ArgTypes[] = {Type::getInt8PtrTy(C), Type::getInt8PtrTy(C)};
43
44    FunctionCallee Fn = M.getOrInsertFunction(
45        Func, FunctionType::get(Type::getVoidTy(C), ArgTypes, false));
46
47    Instruction *RetAddr = CallInst::Create(
48        Intrinsic::getDeclaration(&M, Intrinsic::returnaddress),
49        ArrayRef<Value *>(ConstantInt::get(Type::getInt32Ty(C), 0)), "",
50        InsertionPt);
51    RetAddr->setDebugLoc(DL);
52
53    Value *Args[] = {ConstantExpr::getBitCast(&CurFn, Type::getInt8PtrTy(C)),
54                     RetAddr};
55
56    CallInst *Call =
57        CallInst::Create(Fn, ArrayRef<Value *>(Args), "", InsertionPt);
58    Call->setDebugLoc(DL);
59    return;
60  }
61
62  // We only know how to call a fixed set of instrumentation functions, because
63  // they all expect different arguments, etc.
64  report_fatal_error(Twine("Unknown instrumentation function: '") + Func + "'");
65}
66
67static bool runOnFunction(Function &F, bool PostInlining) {
68  StringRef EntryAttr = PostInlining ? "instrument-function-entry-inlined"
69                                     : "instrument-function-entry";
70
71  StringRef ExitAttr = PostInlining ? "instrument-function-exit-inlined"
72                                    : "instrument-function-exit";
73
74  StringRef EntryFunc = F.getFnAttribute(EntryAttr).getValueAsString();
75  StringRef ExitFunc = F.getFnAttribute(ExitAttr).getValueAsString();
76
77  bool Changed = false;
78
79  // If the attribute is specified, insert instrumentation and then "consume"
80  // the attribute so that it's not inserted again if the pass should happen to
81  // run later for some reason.
82
83  if (!EntryFunc.empty()) {
84    DebugLoc DL;
85    if (auto SP = F.getSubprogram())
86      DL = DebugLoc::get(SP->getScopeLine(), 0, SP);
87
88    insertCall(F, EntryFunc, &*F.begin()->getFirstInsertionPt(), DL);
89    Changed = true;
90    F.removeAttribute(AttributeList::FunctionIndex, EntryAttr);
91  }
92
93  if (!ExitFunc.empty()) {
94    for (BasicBlock &BB : F) {
95      Instruction *T = BB.getTerminator();
96      if (!isa<ReturnInst>(T))
97        continue;
98
99      // If T is preceded by a musttail call, that's the real terminator.
100      Instruction *Prev = T->getPrevNode();
101      if (BitCastInst *BCI = dyn_cast_or_null<BitCastInst>(Prev))
102        Prev = BCI->getPrevNode();
103      if (CallInst *CI = dyn_cast_or_null<CallInst>(Prev)) {
104        if (CI->isMustTailCall())
105          T = CI;
106      }
107
108      DebugLoc DL;
109      if (DebugLoc TerminatorDL = T->getDebugLoc())
110        DL = TerminatorDL;
111      else if (auto SP = F.getSubprogram())
112        DL = DebugLoc::get(0, 0, SP);
113
114      insertCall(F, ExitFunc, T, DL);
115      Changed = true;
116    }
117    F.removeAttribute(AttributeList::FunctionIndex, ExitAttr);
118  }
119
120  return Changed;
121}
122
123namespace {
124struct EntryExitInstrumenter : public FunctionPass {
125  static char ID;
126  EntryExitInstrumenter() : FunctionPass(ID) {
127    initializeEntryExitInstrumenterPass(*PassRegistry::getPassRegistry());
128  }
129  void getAnalysisUsage(AnalysisUsage &AU) const override {
130    AU.addPreserved<GlobalsAAWrapperPass>();
131  }
132  bool runOnFunction(Function &F) override { return ::runOnFunction(F, false); }
133};
134char EntryExitInstrumenter::ID = 0;
135
136struct PostInlineEntryExitInstrumenter : public FunctionPass {
137  static char ID;
138  PostInlineEntryExitInstrumenter() : FunctionPass(ID) {
139    initializePostInlineEntryExitInstrumenterPass(
140        *PassRegistry::getPassRegistry());
141  }
142  void getAnalysisUsage(AnalysisUsage &AU) const override {
143    AU.addPreserved<GlobalsAAWrapperPass>();
144  }
145  bool runOnFunction(Function &F) override { return ::runOnFunction(F, true); }
146};
147char PostInlineEntryExitInstrumenter::ID = 0;
148}
149
150INITIALIZE_PASS(
151    EntryExitInstrumenter, "ee-instrument",
152    "Instrument function entry/exit with calls to e.g. mcount() (pre inlining)",
153    false, false)
154INITIALIZE_PASS(PostInlineEntryExitInstrumenter, "post-inline-ee-instrument",
155                "Instrument function entry/exit with calls to e.g. mcount() "
156                "(post inlining)",
157                false, false)
158
159FunctionPass *llvm::createEntryExitInstrumenterPass() {
160  return new EntryExitInstrumenter();
161}
162
163FunctionPass *llvm::createPostInlineEntryExitInstrumenterPass() {
164  return new PostInlineEntryExitInstrumenter();
165}
166
167PreservedAnalyses
168llvm::EntryExitInstrumenterPass::run(Function &F, FunctionAnalysisManager &AM) {
169  runOnFunction(F, PostInlining);
170  PreservedAnalyses PA;
171  PA.preserveSet<CFGAnalyses>();
172  return PA;
173}
174