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