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; 28 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. 38 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 63 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 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 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