Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[InstrFDO][TypeProf] Implement binary instrumentation and profile read/write #66825

Merged
merged 44 commits into from
Apr 1, 2024
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
361f8f6
[IRPGO][ValueProfile] Instrument virtual table address that could be …
mingmingl-llvm Sep 17, 2023
cb0246f
Address feebacks on the code. Questions will be followed up shortly.
mingmingl-llvm Sep 29, 2023
3c6ca04
In InstrProf.cpp, add option -enable-vtable-type-profiling to flag co…
mingmingl-llvm Oct 2, 2023
3780bd9
A few fixes:
mingmingl-llvm Oct 2, 2023
9255867
resolve feedbacks on code
mingmingl-llvm Oct 4, 2023
8d8a45a
Rebase onto main (a fresh commit on Oct27) and add the following changes
mingmingl-llvm Oct 28, 2023
82fac8e
address feedback
mingmingl-llvm Nov 1, 2023
d54d059
small changes:
mingmingl-llvm Nov 1, 2023
ebf4c1d
pick up upstream change; sync main branch with upstream and 'git merg…
mingmingl-llvm Nov 7, 2023
52143e9
update clang-format version to 117.0.4 released binary and run clang-…
mingmingl-llvm Nov 7, 2023
2350d7f
The changes:
mingmingl-llvm Nov 7, 2023
af34929
Update main branch of my llvm fork. Run 'git merge main' and resolve …
mingmingl-llvm Nov 9, 2023
fcf92af
resolve feedback
mingmingl-llvm Nov 13, 2023
f5d12b5
merge upstream main changes and resolve conflict
mingmingl-llvm Nov 16, 2023
17d941c
Merge branch 'main' into vtable
mingmingl-llvm Nov 16, 2023
c000e64
run clang-format over modified files
mingmingl-llvm Nov 16, 2023
5b09e43
merge main branch changes
mingmingl-llvm Nov 25, 2023
313eb10
undo clang-format on unchanged lines
mingmingl-llvm Nov 26, 2023
abcbc6d
Changes:
mingmingl-llvm Dec 3, 2023
8c725ef
fix three test failures related with zlib usage
mingmingl-llvm Dec 6, 2023
1050a6b
apply git-format change
mingmingl-llvm Dec 11, 2023
9f01a30
run 'git merge main', resolve conflicts and run clang-format on the diff
mingmingl-llvm Dec 14, 2023
a9deaec
Polish tests
mingmingl-llvm Dec 14, 2023
66efde8
In raw profile reader, read multi-byte data using 'swap'. The 'swap' …
mingmingl-llvm Dec 30, 2023
9ec0784
undo git-formatted lines and follow existing style
mingmingl-llvm Jan 2, 2024
0d9abe5
run 'git merge main'
mingmingl-llvm Jan 2, 2024
8240524
one-liner fix for tools/llvm-profdata/raw-64-bits-le.test
mingmingl-llvm Jan 2, 2024
60cd296
fix two test failures. They are overlooked when running 'git merge main'
mingmingl-llvm Jan 2, 2024
a7633ad
run 'git merge main'
mingmingl-llvm Feb 27, 2024
a61c8a4
A few changes after 'git merge main':
mingmingl-llvm Feb 27, 2024
4dbe23e
Changes
mingmingl-llvm Feb 28, 2024
000d818
apply clang-format change
mingmingl-llvm Feb 28, 2024
c57422b
simple clean-ups
mingmingl-llvm Mar 2, 2024
df6eadf
For helper function needsComdatForCounter, limit the first parameter …
mingmingl-llvm Mar 3, 2024
c30888c
Merge branch 'main' into vtable
mingmingl-llvm Mar 4, 2024
4a9e6c9
unify 'maybeSetComdat'
mingmingl-llvm Mar 4, 2024
61e8292
resolve review feedback
mingmingl-llvm Mar 6, 2024
fc1d2be
resolve comment
mingmingl-llvm Mar 26, 2024
d8967d2
merge main branch and resolve conflicts
mingmingl-llvm Mar 26, 2024
27239f0
Merge branch 'main' into vtable
mingmingl-llvm Mar 28, 2024
0158523
Changes:
mingmingl-llvm Mar 28, 2024
2abdfe3
remove InstrProfSymtab::getGlobalVariable method; it's not used in th…
mingmingl-llvm Mar 28, 2024
7ea9217
resolve comments
mingmingl-llvm Mar 29, 2024
0a0c364
Changes:
mingmingl-llvm Mar 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 57 additions & 5 deletions llvm/include/llvm/Analysis/IndirectCallVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,75 @@
#include <vector>

namespace llvm {
// Visitor class that finds all indirect call.
// 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;
PGOIndirectCallVisitor() = default;
std::vector<Instruction *> ProfiledAddresses;
PGOIndirectCallVisitor(InstructionType Type) : Type(Type) {}

void visitCallBase(CallBase &Call) {
if (Call.isIndirectCall())
if (!Call.isIndirectCall())
return;

if (Type == InstructionType::kIndirectCall) {
IndirectCalls.push_back(&Call);
return;
}

assert(Type == InstructionType::kVTableVal && "Control flow guaranteed");

LoadInst *LI = dyn_cast<LoadInst>(Call.getCalledOperand());
// The code pattern to look for
//
// %vtable = load ptr, ptr %b
// %vfn = getelementptr inbounds ptr, ptr %vtable, i64 1
// %2 = load ptr, ptr %vfn
// %call = tail call i32 %2(ptr %b)
//
// %vtable is the vtable address value to profile, and
// %2 is the indirect call target address to profile.
if (LI != nullptr) {
Value *Ptr = LI->getPointerOperand();
Value *VTablePtr = Ptr->stripInBoundsConstantOffsets();
// This is a heuristic to find address feeding instructions.
// 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))
ProfiledAddresses.push_back(cast<Instruction>(VTablePtr));
}
}

private:
InstructionType Type;
};

// Helper function that finds all indirect call sites.
inline std::vector<CallBase *> findIndirectCalls(Function &F) {
PGOIndirectCallVisitor ICV;
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
154 changes: 135 additions & 19 deletions llvm/include/llvm/ProfileData/InstrProf.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/BitmaskEnum.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
Expand Down Expand Up @@ -89,6 +90,9 @@ inline StringRef getInstrProfValueProfMemOpFuncName() {
/// Return the name prefix of variables containing instrumented function names.
inline StringRef getInstrProfNameVarPrefix() { return "__profn_"; }

/// Return the name prefix of variables containing virtual table profile data.
inline StringRef getInstrProfVTableVarPrefix() { return "__profvt_"; }

/// Return the name prefix of variables containing per-function control data.
inline StringRef getInstrProfDataVarPrefix() { return "__profd_"; }

Expand All @@ -106,9 +110,9 @@ inline StringRef getInstrProfVNodesVarName() { return "__llvm_prf_vnodes"; }

/// Return the name of the variable holding the strings (possibly compressed)
/// of all function's PGO names.
inline StringRef getInstrProfNamesVarName() {
return "__llvm_prf_nm";
}
inline StringRef getInstrProfNamesVarName() { return "__llvm_prf_nm"; }

inline StringRef getInstrProfVTableNamesVarName() { return "__llvm_prf_vnm"; }

/// Return the name of a covarage mapping variable (internal linkage)
/// for each instrumented source module. Such variables are allocated
Expand Down Expand Up @@ -140,7 +144,8 @@ inline StringRef getInstrProfRegFuncName() {
return "__llvm_profile_register_function";
}

/// Return the name of the runtime interface that registers the PGO name strings.
/// Return the name of the runtime interface that registers the PGO name
/// strings.
inline StringRef getInstrProfNamesRegFuncName() {
return "__llvm_profile_register_names_function";
}
Expand Down Expand Up @@ -246,6 +251,9 @@ Error collectGlobalObjectNameStrings(ArrayRef<std::string> NameStrs,
Error collectPGOFuncNameStrings(ArrayRef<GlobalVariable *> NameVars,
std::string &Result, bool doCompression = true);

Error collectVTableStrings(ArrayRef<GlobalVariable *> VTables,
std::string &Result, bool doCompression);

/// Check if INSTR_PROF_RAW_VERSION_VAR is defined. This global is only being
/// set in IR PGO compilation.
bool isIRPGOFlagSet(const Module *M);
Expand Down Expand Up @@ -288,14 +296,16 @@ inline StringRef getPGOFuncNameMetadataName() { return "PGOFuncName"; }
/// Return the PGOFuncName meta data associated with a function.
MDNode *getPGOFuncNameMetadata(const Function &F);

std::string getPGOName(const GlobalVariable &V, bool InLTO = false);

/// Create the PGOFuncName meta data if PGOFuncName is different from
/// function's raw name. This should only apply to internal linkage functions
/// declared by users only.
void createPGOFuncNameMetadata(Function &F, StringRef PGOFuncName);

/// Check if we can use Comdat for profile variables. This will eliminate
/// the duplicated profile variables for Comdat functions.
bool needsComdatForCounter(const Function &F, const Module &M);
bool needsComdatForCounter(const GlobalObject &GV, const Module &M);

/// An enum describing the attributes of an instrumented profile.
enum class InstrProfKind {
Expand Down Expand Up @@ -431,23 +441,37 @@ class InstrProfSymtab {
using AddrHashMap = std::vector<std::pair<uint64_t, uint64_t>>;

private:
struct VTableProfData {
uint64_t StartAddr;
uint64_t EndAddr;
uint64_t MD5Hash;
};
StringRef Data;
uint64_t Address = 0;
// Unique name strings.
// Unique name strings. Used to ensure entries in MD5NameMap (a vector that's
// going to be sorted) has unique MD5 keys in the first place.
StringSet<> NameTab;
// Records the unique virtual table names. This is used by InstrProfWriter to
// write out an on-disk chained hash table of virtual table names.
// InstrProfWriter stores per function profile data (keyed by function names)
// so it doesn't use a StringSet for function names.
StringSet<> VTableNames;
// A map from MD5 keys to function name strings.
std::vector<std::pair<uint64_t, StringRef>> MD5NameMap;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth it to simplify this to use DenseMap too in a separate patch.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. i'll send out another patch later.

// A map from MD5 keys to virtual table definitions. Only populated when
// building the Symtab from a module.
DenseMap<uint64_t, GlobalVariable *> MD5VTableMap;
// A map from MD5 keys to function define. We only populate this map
// when build the Symtab from a Module.
std::vector<std::pair<uint64_t, Function *>> MD5FuncMap;
// A map from function runtime address to function name MD5 hash.
// This map is only populated and used by raw instr profile reader.
AddrHashMap AddrToMD5Map;
// This vector is only populated and used by raw instr profile reader.
std::vector<VTableProfData> VTableProfDataArray;
bool Sorted = false;

static StringRef getExternalSymbol() {
return "** External Symbol **";
}
static StringRef getExternalSymbol() { return "** External Symbol **"; }

// Returns the canonial name of the given PGOName. In a canonical name, all
// suffixes that begins with "." except ".__uniq." are stripped.
Expand Down Expand Up @@ -481,9 +505,19 @@ class InstrProfSymtab {

/// \c NameStrings is a string composed of one of more sub-strings
/// encoded in the format described in \c collectPGOFuncNameStrings.
/// This method is a wrapper to \c readPGOFuncNameStrings method.
/// This method is a wrapper to \c readAndDecodeStrings method.
Error create(StringRef NameStrings);

/// Initialize symtab states with function names and vtable names. \c
/// FuncNameStrings is a string composed of one or more encoded function name
/// strings, and \c VTableNameStrings composes of one or more encoded vtable
/// names. This interface is solely used by raw profile reader.
Error create(StringRef FuncNameStrings, StringRef VTableNameStrings);

/// Initialize 'this' with the set of vtable names encoded in
/// \c CompressedVTableNames.
Error initVTableNamesFromCompressedStrings(StringRef CompressedVTableNames);

/// This interface is used by reader of CoverageMapping test
/// format.
inline Error create(StringRef D, uint64_t BaseAddr);
Expand All @@ -496,32 +530,69 @@ class InstrProfSymtab {

/// Create InstrProfSymtab from a set of names iteratable from
/// \p IterRange. This interface is used by IndexedProfReader.
template <typename NameIterRange> Error create(const NameIterRange &IterRange);

/// Update the symtab by adding \p FuncName to the table. This interface
/// is used by the raw and text profile readers.
Error addFuncName(StringRef FuncName) {
if (FuncName.empty())
template <typename NameIterRange>
Error create(const NameIterRange &IterRange);

/// Create InstrProfSymtab from a set of function names and vtable
/// names iteratable from \p IterRange. This interface is used by
/// IndexedProfReader.
template <typename FuncNameIterRange, typename VTableNameIterRange>
Error create(const FuncNameIterRange &FuncIterRange,
const VTableNameIterRange &VTableIterRange);

Error addSymbolName(StringRef SymbolName) {
if (SymbolName.empty())
return make_error<InstrProfError>(instrprof_error::malformed,
"function name is empty");
auto Ins = NameTab.insert(FuncName);
"symbol name is empty");

// Insert into NameTab so that MD5NameMap (a vector that will be sorted)
// won't have duplicated entries in the first place.
auto Ins = NameTab.insert(SymbolName);
if (Ins.second) {
MD5NameMap.push_back(std::make_pair(
IndexedInstrProf::ComputeHash(FuncName), Ins.first->getKey()));
IndexedInstrProf::ComputeHash(SymbolName), Ins.first->getKey()));
Sorted = false;
}
return Error::success();
}

/// The method name is kept since there are many callers.
/// It just forwards to 'addSymbolName'.
Error addFuncName(StringRef FuncName) { return addSymbolName(FuncName); }

/// Adds VTableName as a known symbol, and inserts it to a map that
/// tracks all vtable names.
Error addVTableName(StringRef VTableName) {
if (Error E = addSymbolName(VTableName))
return E;

// Record VTableName. InstrProfWriter uses this set. The comment around
// class member explains why.
VTableNames.insert(VTableName);
return Error::success();
}

const StringSet<> &getVTableNames() const { return VTableNames; }

/// Map a function address to its name's MD5 hash. This interface
/// is only used by the raw profiler reader.
void mapAddress(uint64_t Addr, uint64_t MD5Val) {
AddrToMD5Map.push_back(std::make_pair(Addr, MD5Val));
}

/// Map the address range (i.e., [start_address, end_address)) of a variable
/// to its names' MD5 hash. This interface is only used by the raw profile
/// reader.
void mapVTableAddress(uint64_t StartAddr, uint64_t EndAddr, uint64_t MD5Val) {
VTableProfDataArray.push_back(VTableProfData{StartAddr, EndAddr, MD5Val});
}

/// Return a function's hash, or 0, if the function isn't in this SymTab.
uint64_t getFunctionHashFromAddress(uint64_t Address);

/// Return a vtable's hash, or 0 if the vtable doesn't exist in this SymTab.
uint64_t getVTableHashFromAddress(uint64_t Address);

/// Return function's PGO name from the function name's symbol
/// address in the object file. If an error occurs, return
/// an empty string.
Expand All @@ -543,6 +614,8 @@ class InstrProfSymtab {

/// Return function from the name's md5 hash. Return nullptr if not found.
inline Function *getFunction(uint64_t FuncMD5Hash);
// Return vtable from the name's MD5 hash. Return nullptr if not found.
inline GlobalVariable *getGlobalVariable(uint64_t GlobalVariableMD5Hash);

/// Return the name section data.
inline StringRef getNameData() const { return Data; }
Expand All @@ -567,6 +640,24 @@ Error InstrProfSymtab::create(const NameIterRange &IterRange) {
return Error::success();
}

template <typename FuncNameIterRange, typename VTableNameIterRange>
Error InstrProfSymtab::create(const FuncNameIterRange &FuncIterRange,
const VTableNameIterRange &VTableIterRange) {
// Iterate elements by StringRef rather than by const reference.
// StringRef is small enough, so the loop is efficient whether
// element in the range is std::string or StringRef.
for (StringRef Name : FuncIterRange)
if (Error E = addFuncName(Name))
return E;

for (StringRef VTableName : VTableIterRange)
if (Error E = addVTableName(VTableName))
return E;

finalizeSymtab();
return Error::success();
}

void InstrProfSymtab::finalizeSymtab() {
if (Sorted)
return;
Expand All @@ -575,6 +666,19 @@ void InstrProfSymtab::finalizeSymtab() {
llvm::sort(AddrToMD5Map, less_first());
AddrToMD5Map.erase(std::unique(AddrToMD5Map.begin(), AddrToMD5Map.end()),
AddrToMD5Map.end());
// VTable object address ranges should not overlap; so sort by either
// beginning address or end address is fine.
llvm::sort(VTableProfDataArray,
[](const VTableProfData &LHS, const VTableProfData &RHS) {
return LHS.StartAddr < RHS.StartAddr;
});
VTableProfDataArray.erase(
std::unique(VTableProfDataArray.begin(), VTableProfDataArray.end(),
[](const VTableProfData &LHS, const VTableProfData &RHS) {
return LHS.StartAddr == RHS.StartAddr &&
LHS.EndAddr == RHS.EndAddr;
}),
VTableProfDataArray.end());
Sorted = true;
}

Expand Down Expand Up @@ -605,6 +709,16 @@ Function* InstrProfSymtab::getFunction(uint64_t FuncMD5Hash) {
return nullptr;
}

GlobalVariable *
InstrProfSymtab::getGlobalVariable(uint64_t GlobalVariableMD5Hash) {
auto Iter = MD5VTableMap.find(GlobalVariableMD5Hash);
// Iter->second should not be nullptr.
// Skip an assert since 'MD5VTableMap' is an internal state.
if (Iter != MD5VTableMap.end())
return Iter->second;
return nullptr;
}

// To store the sums of profile count values, or the percentage of
// the sums of the total count values.
struct CountSumOrPercent {
Expand Down Expand Up @@ -870,6 +984,8 @@ struct InstrProfRecord {
return ValueData->IndirectCallSites;
case IPVK_MemOPSize:
return ValueData->MemOPSizes;
case IPVK_VTableTarget:
return ValueData->VTableTargets;
default:
llvm_unreachable("Unknown value kind!");
}
Expand Down
6 changes: 6 additions & 0 deletions llvm/include/llvm/ProfileData/InstrProfReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,12 @@ class InstrProfReaderIndex : public InstrProfReaderIndexBase {
InstrProfKind getProfileKind() const override;

Error populateSymtab(InstrProfSymtab &Symtab) override {
// FIXME: the create method calls 'finalizeSymtab' and sorts a bunch of
// arrays/maps. Since there are other data sources other than 'HashTable' to
// populate a symtab, it might make sense to have something like this
// 1. Let each data source populate Symtab and init the arrays/maps without
// calling 'finalizeSymtab'
// 2. Call 'finalizeSymtab' once to get all arrays/maps sorted if needed.
return Symtab.create(HashTable->keys());
}
};
Expand Down
Loading
Loading