clang 20.0.0 (based on r547379) from build 12806354. Bug: http://b/379133546 Test: N/A Change-Id: I2eb8938af55d809de674be63cb30cf27e801862b Upstream-Commit: ad834e67b1105d15ef907f6255d4c96e8e733f57
98 lines
3.3 KiB
C++
98 lines
3.3 KiB
C++
//===-- 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 <vector>
|
|
|
|
namespace llvm {
|
|
// Visitor class that finds indirect calls or instructions that gives vtable
|
|
// value, depending on Type.
|
|
struct PGOIndirectCallVisitor : public InstVisitor<PGOIndirectCallVisitor> {
|
|
enum class InstructionType {
|
|
kIndirectCall = 0,
|
|
kVTableVal = 1,
|
|
};
|
|
std::vector<CallBase *> IndirectCalls;
|
|
std::vector<Instruction *> 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");
|
|
if (!CB->isIndirectCall())
|
|
return nullptr;
|
|
|
|
LoadInst *LI = dyn_cast<LoadInst>(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<Instruction>(VTablePtr))
|
|
return cast<Instruction>(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<CallBase *> findIndirectCalls(Function &F) {
|
|
PGOIndirectCallVisitor ICV(
|
|
PGOIndirectCallVisitor::InstructionType::kIndirectCall);
|
|
ICV.visit(F);
|
|
return ICV.IndirectCalls;
|
|
}
|
|
|
|
inline std::vector<Instruction *> findVTableAddrs(Function &F) {
|
|
PGOIndirectCallVisitor ICV(
|
|
PGOIndirectCallVisitor::InstructionType::kVTableVal);
|
|
ICV.visit(F);
|
|
return ICV.ProfiledAddresses;
|
|
}
|
|
|
|
} // namespace llvm
|
|
|
|
#endif
|