xref: /freebsd/contrib/llvm-project/llvm/include/llvm/Analysis/IndirectCallVisitor.h (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1 //===-- IndirectCallVisitor.h - indirect call visitor ---------------------===//
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 defines a visitor class and a helper function that find
10 // all indirect call-sites in a function.
11 
12 #ifndef LLVM_ANALYSIS_INDIRECTCALLVISITOR_H
13 #define LLVM_ANALYSIS_INDIRECTCALLVISITOR_H
14 
15 #include "llvm/IR/InstVisitor.h"
16 #include <vector>
17 
18 namespace llvm {
19 // Visitor class that finds indirect calls or instructions that gives vtable
20 // value, depending on Type.
21 struct PGOIndirectCallVisitor : public InstVisitor<PGOIndirectCallVisitor> {
22   enum class InstructionType {
23     kIndirectCall = 0,
24     kVTableVal = 1,
25   };
26   std::vector<CallBase *> IndirectCalls;
27   std::vector<Instruction *> ProfiledAddresses;
PGOIndirectCallVisitorPGOIndirectCallVisitor28   PGOIndirectCallVisitor(InstructionType Type) : Type(Type) {}
29 
30   // Given an indirect call instruction, try to find the the following pattern
31   //
32   // %vtable = load ptr, ptr %obj
33   // %vfn = getelementptr inbounds ptr, ptr %vtable, i64 1
34   // %2 = load ptr, ptr %vfn
35   // $call = tail call i32 %2
36   //
37   // A heuristic is used to find the address feeding instructions.
tryGetVTableInstructionPGOIndirectCallVisitor38   static Instruction *tryGetVTableInstruction(CallBase *CB) {
39     assert(CB != nullptr && "Caller guaranteed");
40     if (!CB->isIndirectCall())
41       return nullptr;
42 
43     LoadInst *LI = dyn_cast<LoadInst>(CB->getCalledOperand());
44     if (LI != nullptr) {
45       Value *FuncPtr = LI->getPointerOperand(); // GEP (or bitcast)
46       Value *VTablePtr = FuncPtr->stripInBoundsConstantOffsets();
47       // FIXME: Add support in the frontend so LLVM type intrinsics are
48       // emitted without LTO. This way, added intrinsics could filter
49       // non-vtable instructions and reduce instrumentation overhead.
50       // Since a non-vtable profiled address is not within the address
51       // range of vtable objects, it's stored as zero in indexed profiles.
52       // A pass that looks up symbol with an zero hash will (almost) always
53       // find nullptr and skip the actual transformation (e.g., comparison
54       // of symbols). So the performance overhead from non-vtable profiled
55       // address is negligible if exists at all. Comparing loaded address
56       // with symbol address guarantees correctness.
57       if (VTablePtr != nullptr && isa<Instruction>(VTablePtr))
58         return cast<Instruction>(VTablePtr);
59     }
60     return nullptr;
61   }
62 
visitCallBasePGOIndirectCallVisitor63   void visitCallBase(CallBase &Call) {
64     if (Call.isIndirectCall()) {
65       IndirectCalls.push_back(&Call);
66 
67       if (Type != InstructionType::kVTableVal)
68         return;
69 
70       Instruction *VPtr =
71           PGOIndirectCallVisitor::tryGetVTableInstruction(&Call);
72       if (VPtr)
73         ProfiledAddresses.push_back(VPtr);
74     }
75   }
76 
77 private:
78   InstructionType Type;
79 };
80 
findIndirectCalls(Function & F)81 inline std::vector<CallBase *> findIndirectCalls(Function &F) {
82   PGOIndirectCallVisitor ICV(
83       PGOIndirectCallVisitor::InstructionType::kIndirectCall);
84   ICV.visit(F);
85   return ICV.IndirectCalls;
86 }
87 
findVTableAddrs(Function & F)88 inline std::vector<Instruction *> findVTableAddrs(Function &F) {
89   PGOIndirectCallVisitor ICV(
90       PGOIndirectCallVisitor::InstructionType::kVTableVal);
91   ICV.visit(F);
92   return ICV.ProfiledAddresses;
93 }
94 
95 } // namespace llvm
96 
97 #endif
98