| 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 | |