//===-- IndirectCallVisitor.h - indirect call visitor ---------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements defines a visitor class and a helper function that find // all indirect call-sites in a function. #ifndef LLVM_ANALYSIS_INDIRECTCALLVISITOR_H #define LLVM_ANALYSIS_INDIRECTCALLVISITOR_H #include "llvm/IR/InstVisitor.h" #include namespace llvm { // Visitor class that finds indirect calls or instructions that gives vtable // value, depending on Type. struct PGOIndirectCallVisitor : public InstVisitor { enum class InstructionType { kIndirectCall = 0, kVTableVal = 1, }; std::vector IndirectCalls; std::vector ProfiledAddresses; PGOIndirectCallVisitor(InstructionType Type) : Type(Type) {} // Given an indirect call instruction, try to find the the following pattern // // %vtable = load ptr, ptr %obj // %vfn = getelementptr inbounds ptr, ptr %vtable, i64 1 // %2 = load ptr, ptr %vfn // $call = tail call i32 %2 // // A heuristic is used to find the address feeding instructions. static Instruction *tryGetVTableInstruction(CallBase *CB) { assert(CB != nullptr && "Caller guaranteed"); LoadInst *LI = dyn_cast(CB->getCalledOperand()); if (LI != nullptr) { Value *FuncPtr = LI->getPointerOperand(); // GEP (or bitcast) Value *VTablePtr = FuncPtr->stripInBoundsConstantOffsets(); // FIXME: Add support in the frontend so LLVM type intrinsics are // emitted without LTO. This way, added intrinsics could filter // non-vtable instructions and reduce instrumentation overhead. // Since a non-vtable profiled address is not within the address // range of vtable objects, it's stored as zero in indexed profiles. // A pass that looks up symbol with an zero hash will (almost) always // find nullptr and skip the actual transformation (e.g., comparison // of symbols). So the performance overhead from non-vtable profiled // address is negligible if exists at all. Comparing loaded address // with symbol address guarantees correctness. if (VTablePtr != nullptr && isa(VTablePtr)) return cast(VTablePtr); } return nullptr; } void visitCallBase(CallBase &Call) { if (Call.isIndirectCall()) { IndirectCalls.push_back(&Call); if (Type != InstructionType::kVTableVal) return; Instruction *VPtr = PGOIndirectCallVisitor::tryGetVTableInstruction(&Call); if (VPtr) ProfiledAddresses.push_back(VPtr); } } private: InstructionType Type; }; inline std::vector findIndirectCalls(Function &F) { PGOIndirectCallVisitor ICV( PGOIndirectCallVisitor::InstructionType::kIndirectCall); ICV.visit(F); return ICV.IndirectCalls; } inline std::vector findVTableAddrs(Function &F) { PGOIndirectCallVisitor ICV( PGOIndirectCallVisitor::InstructionType::kVTableVal); ICV.visit(F); return ICV.ProfiledAddresses; } } // namespace llvm #endif