• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     LoadInst *LI = dyn_cast<LoadInst>(CB->getCalledOperand());
41 
42     if (LI != nullptr) {
43       Value *FuncPtr = LI->getPointerOperand(); // GEP (or bitcast)
44       Value *VTablePtr = FuncPtr->stripInBoundsConstantOffsets();
45       // FIXME: Add support in the frontend so LLVM type intrinsics are
46       // emitted without LTO. This way, added intrinsics could filter
47       // non-vtable instructions and reduce instrumentation overhead.
48       // Since a non-vtable profiled address is not within the address
49       // range of vtable objects, it's stored as zero in indexed profiles.
50       // A pass that looks up symbol with an zero hash will (almost) always
51       // find nullptr and skip the actual transformation (e.g., comparison
52       // of symbols). So the performance overhead from non-vtable profiled
53       // address is negligible if exists at all. Comparing loaded address
54       // with symbol address guarantees correctness.
55       if (VTablePtr != nullptr && isa<Instruction>(VTablePtr))
56         return cast<Instruction>(VTablePtr);
57     }
58     return nullptr;
59   }
60 
visitCallBasePGOIndirectCallVisitor61   void visitCallBase(CallBase &Call) {
62     if (Call.isIndirectCall()) {
63       IndirectCalls.push_back(&Call);
64 
65       if (Type != InstructionType::kVTableVal)
66         return;
67 
68       Instruction *VPtr =
69           PGOIndirectCallVisitor::tryGetVTableInstruction(&Call);
70       if (VPtr)
71         ProfiledAddresses.push_back(VPtr);
72     }
73   }
74 
75 private:
76   InstructionType Type;
77 };
78 
findIndirectCalls(Function & F)79 inline std::vector<CallBase *> findIndirectCalls(Function &F) {
80   PGOIndirectCallVisitor ICV(
81       PGOIndirectCallVisitor::InstructionType::kIndirectCall);
82   ICV.visit(F);
83   return ICV.IndirectCalls;
84 }
85 
findVTableAddrs(Function & F)86 inline std::vector<Instruction *> findVTableAddrs(Function &F) {
87   PGOIndirectCallVisitor ICV(
88       PGOIndirectCallVisitor::InstructionType::kVTableVal);
89   ICV.visit(F);
90   return ICV.ProfiledAddresses;
91 }
92 
93 } // namespace llvm
94 
95 #endif
96