[flang-commits] [flang] [llvm-profdata] Do not create numerical strings for MD5 function names read from a Sample Profile. (PR #66164)
William Junda Huang via flang-commits
flang-commits at lists.llvm.org
Sat Oct 7 18:19:34 PDT 2023
https://github.com/huangjd updated https://github.com/llvm/llvm-project/pull/66164
>From dcaa197c8b4e80fba83fc3905b0488a44fe89cc5 Mon Sep 17 00:00:00 2001
From: William Huang <williamjhuang at google.com>
Date: Fri, 15 Sep 2023 23:08:46 +0000
Subject: [PATCH 1/9] Rebase with main
---
.../include/llvm/ProfileData/ProfileFuncRef.h | 225 ++++++++++++++++++
llvm/include/llvm/ProfileData/SampleProf.h | 130 +++++-----
.../llvm/ProfileData/SampleProfReader.h | 27 +--
.../llvm/ProfileData/SampleProfWriter.h | 12 +-
.../llvm/Transforms/IPO/ProfiledCallGraph.h | 24 +-
.../Transforms/IPO/SampleContextTracker.h | 27 ++-
llvm/lib/ProfileData/SampleProf.cpp | 25 +-
llvm/lib/ProfileData/SampleProfReader.cpp | 76 +++---
llvm/lib/ProfileData/SampleProfWriter.cpp | 30 ++-
llvm/lib/Target/X86/X86InsertPrefetch.cpp | 3 +-
.../Transforms/IPO/SampleContextTracker.cpp | 64 ++---
llvm/lib/Transforms/IPO/SampleProfile.cpp | 90 ++++---
llvm/tools/llvm-profdata/llvm-profdata.cpp | 16 +-
llvm/tools/llvm-profgen/CSPreInliner.cpp | 8 +-
llvm/tools/llvm-profgen/CSPreInliner.h | 7 +-
llvm/tools/llvm-profgen/ProfileGenerator.cpp | 19 +-
llvm/tools/llvm-profgen/ProfiledBinary.cpp | 15 +-
llvm/tools/llvm-profgen/ProfiledBinary.h | 20 +-
llvm/unittests/ProfileData/SampleProfTest.cpp | 45 ++--
19 files changed, 555 insertions(+), 308 deletions(-)
create mode 100644 llvm/include/llvm/ProfileData/ProfileFuncRef.h
diff --git a/llvm/include/llvm/ProfileData/ProfileFuncRef.h b/llvm/include/llvm/ProfileData/ProfileFuncRef.h
new file mode 100644
index 000000000000000..60be6232372b8c6
--- /dev/null
+++ b/llvm/include/llvm/ProfileData/ProfileFuncRef.h
@@ -0,0 +1,225 @@
+//===--- ProfileFuncRef.h - Sample profile function name ---*- C++ -*-===//
+//
+// 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 defines the StringRefOrHashCode class. It is to represent function
+// names in a sample profile, which can be in one of two forms - either a
+// regular string, or a 64-bit hash code.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_PROFILEDATA_PROFILEFUNCREF_H
+#define LLVM_PROFILEDATA_PROFILEFUNCREF_H
+
+#include "llvm/ADT/DenseMapInfo.h"
+#include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/MD5.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cstdint>
+
+namespace llvm {
+namespace sampleprof {
+
+/// This class represents a function name that is read from a sample profile. It
+/// comes with two forms: a string or a hash code. For efficient storage, a
+/// sample profile may store function names as 64-bit MD5 values, so when
+/// reading the profile, this class can represnet them without converting it to
+/// a string first.
+/// When representing a hash code, we utilize the Length field to store it, and
+/// Data is set to null. When representing a string, it is same as StringRef,
+/// and can be pointer-casted as one.
+/// We disallow implicit cast to StringRef because there are too many instances
+/// that it may cause break the code, such as using it in a StringMap.
+class ProfileFuncRef {
+
+ const char *Data = nullptr;
+
+ /// Use uint64_t instead of size_t so that it can also hold a MD5 value.
+ uint64_t Length = 0;
+
+ /// Extension to memcmp to handle hash code representation. If both are hash
+ /// values, Lhs and Rhs are both null, function returns 0 (and needs an extra
+ /// comparison using getIntValue). If only one is hash code, it is considered
+ /// less than the StringRef one. Otherwise perform normal string comparison.
+ static int compareMemory(const char *Lhs, const char *Rhs, uint64_t Length) {
+ if (Lhs == Rhs)
+ return 0;
+ if (!Lhs)
+ return -1;
+ if (!Rhs)
+ return 1;
+ return ::memcmp(Lhs, Rhs, (size_t)Length);
+ }
+
+public:
+ ProfileFuncRef() = default;
+
+ /// Constructor from a StringRef.
+ explicit ProfileFuncRef(StringRef Str)
+ : Data(Str.data()), Length(Str.size()) {}
+
+ /// Constructor from a hash code.
+ explicit ProfileFuncRef(uint64_t HashCode)
+ : Data(nullptr), Length(HashCode) {
+ assert(HashCode != 0);
+ }
+
+ /// Constructor from a string. Check if Str is a number, which is generated by
+ /// converting a MD5 sample profile to a format that does not support MD5, and
+ /// if so, convert the numerical string to a hash code first. We assume that
+ /// no function name (from a profile) can be a pure number.
+ explicit ProfileFuncRef(const std::string &Str)
+ : Data(Str.data()), Length(Str.size()) {
+ // Only need to check for base 10 digits, fail faster if otherwise.
+ if (Str.length() > 0 && isdigit(Str[0]) &&
+ !StringRef(Str).getAsInteger(10, Length))
+ Data = nullptr;
+ }
+
+ /// Check for equality. Similar to StringRef::equals, but will also cover for
+ /// the case where one or both are hash codes. Comparing their int values are
+ /// sufficient. A hash code ProfileFuncName is considered not equal to a
+ /// StringRef ProfileFuncName regardless of actual contents.
+ bool equals(const ProfileFuncRef &Other) const {
+ return Length == Other.Length &&
+ compareMemory(Data, Other.Data, Length) == 0;
+ }
+
+ /// Total order comparison. If both ProfileFuncName are StringRef, this is the
+ /// same as StringRef::compare. If one of them is StringRef, it is considered
+ /// greater than the hash code ProfileFuncName. Otherwise this is the the
+ /// same as comparing their int values.
+ int compare(const ProfileFuncRef &Other) const {
+ auto Res = compareMemory(Data, Other.Data, std::min(Length, Other.Length));
+ if (Res != 0)
+ return Res;
+ if (Length == Other.Length)
+ return 0;
+ return Length < Other.Length ? -1 : 1;
+ }
+
+ /// Convert to a string, usually for output purpose.
+ std::string str() const {
+ if (Data)
+ return std::string(Data, Length);
+ if (Length != 0)
+ return std::to_string(Length);
+ return std::string();
+ }
+
+ /// Convert to StringRef, a backing buffer must be provided in case this
+ /// object represents a hash code, which will be converted to a string into
+ /// the buffer. If this object represents a StringRef, the buffer is not used.
+ StringRef stringRef(std::string &Buffer) const {
+ if (Data)
+ return StringRef(Data, Length);
+ if (Length != 0) {
+ Buffer = std::to_string(Length);
+ return Buffer;
+ }
+ return StringRef();
+ }
+
+ friend raw_ostream &operator<<(raw_ostream &OS, const ProfileFuncRef &Obj);
+
+ /// Get hash code of this object. Returns this object's hash code if it is
+ /// already representing one, otherwise returns the MD5 of its string content.
+ /// Note that it is not the same as std::hash because we want to keep the
+ /// consistency that the same sample profile function in string form or MD5
+ /// form has the same hash code.
+ uint64_t getHashCode() const {
+ if (Data)
+ return MD5Hash(StringRef(Data, Length));
+ return Length;
+ }
+
+ bool empty() const { return Length == 0; }
+
+ /// Check if this object represents a StringRef, or a hash code.
+ bool isStringRef() const { return Data != nullptr; }
+};
+
+inline bool operator==(const ProfileFuncRef &LHS, const ProfileFuncRef &RHS) {
+ return LHS.equals(RHS);
+}
+
+inline bool operator!=(const ProfileFuncRef &LHS, const ProfileFuncRef &RHS) {
+ return !LHS.equals(RHS);
+}
+
+inline bool operator<(const ProfileFuncRef &LHS, const ProfileFuncRef &RHS) {
+ return LHS.compare(RHS) < 0;
+}
+
+inline bool operator<=(const ProfileFuncRef &LHS, const ProfileFuncRef &RHS) {
+ return LHS.compare(RHS) <= 0;
+}
+
+inline bool operator>(const ProfileFuncRef &LHS, const ProfileFuncRef &RHS) {
+ return LHS.compare(RHS) > 0;
+}
+
+inline bool operator>=(const ProfileFuncRef &LHS, const ProfileFuncRef &RHS) {
+ return LHS.compare(RHS) >= 0;
+}
+
+inline raw_ostream &operator<<(raw_ostream &OS, const ProfileFuncRef &Obj) {
+ if (Obj.Data)
+ return OS << StringRef(Obj.Data, Obj.Length);
+ if (Obj.Length != 0)
+ return OS << Obj.Length;
+ return OS;
+}
+
+inline uint64_t MD5Hash(const sampleprof::ProfileFuncRef &Obj) {
+ return Obj.getHashCode();
+}
+
+inline hash_code hash_value(const sampleprof::ProfileFuncRef &Obj) {
+ return Obj.getHashCode();
+}
+
+} // end namespace sampleprof
+
+/// Template specialization for ProfileFuncName so that it can be used in LLVM
+/// map containers.
+template <> struct DenseMapInfo<sampleprof::ProfileFuncRef, void> {
+
+ static inline sampleprof::ProfileFuncRef getEmptyKey() {
+ return sampleprof::ProfileFuncRef(~0ULL);
+ }
+
+ static inline sampleprof::ProfileFuncRef getTombstoneKey() {
+ return sampleprof::ProfileFuncRef(~1ULL);
+ }
+
+ static unsigned getHashValue(const sampleprof::ProfileFuncRef &Val) {
+ return Val.getHashCode();
+ }
+
+ static bool isEqual(const sampleprof::ProfileFuncRef &LHS,
+ const sampleprof::ProfileFuncRef &RHS) {
+ return LHS == RHS;
+ }
+};
+
+} // end namespace llvm
+
+namespace std {
+
+/// Template specialization for ProfileFuncName so that it can be used in STL
+/// containers.
+template <> struct hash<llvm::sampleprof::ProfileFuncRef> {
+ size_t operator()(const llvm::sampleprof::ProfileFuncRef &Val) const {
+ return Val.getHashCode();
+ }
+};
+
+} // end namespace std
+
+#endif // LLVM_PROFILEDATA_PROFILEFUNCREF_H
diff --git a/llvm/include/llvm/ProfileData/SampleProf.h b/llvm/include/llvm/ProfileData/SampleProf.h
index 9b2416e39b6cc9a..cae38de7dfb9835 100644
--- a/llvm/include/llvm/ProfileData/SampleProf.h
+++ b/llvm/include/llvm/ProfileData/SampleProf.h
@@ -21,6 +21,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalValue.h"
+#include "llvm/ProfileData/ProfileFuncRef.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorOr.h"
@@ -111,12 +112,10 @@ static inline uint64_t SPMagic(SampleProfileFormat Format = SPF_Binary) {
/// Get the proper representation of a string according to whether the
/// current Format uses MD5 to represent the string.
-static inline StringRef getRepInFormat(StringRef Name, bool UseMD5,
- std::string &GUIDBuf) {
+static inline ProfileFuncRef getRepInFormat(StringRef Name, bool UseMD5) {
if (Name.empty() || !UseMD5)
- return Name;
- GUIDBuf = std::to_string(Function::getGUID(Name));
- return GUIDBuf;
+ return ProfileFuncRef(Name);
+ return ProfileFuncRef(Function::getGUID(Name));
}
static inline uint64_t SPVersion() { return 103; }
@@ -305,6 +304,10 @@ struct LineLocation {
return LineOffset != O.LineOffset || Discriminator != O.Discriminator;
}
+ uint64_t getHashCode() const {
+ return ((uint64_t) Discriminator << 32) | LineOffset;
+ }
+
uint32_t LineOffset;
uint32_t Discriminator;
};
@@ -318,14 +321,6 @@ struct LineLocationHash {
raw_ostream &operator<<(raw_ostream &OS, const LineLocation &Loc);
-static inline uint64_t hashFuncName(StringRef F) {
- // If function name is already MD5 string, do not hash again.
- uint64_t Hash;
- if (F.getAsInteger(10, Hash))
- Hash = MD5Hash(F);
- return Hash;
-}
-
/// Representation of a single sample record.
///
/// A sample record is represented by a positive integer value, which
@@ -338,7 +333,7 @@ static inline uint64_t hashFuncName(StringRef F) {
/// will be a list of one or more functions.
class SampleRecord {
public:
- using CallTarget = std::pair<StringRef, uint64_t>;
+ using CallTarget = std::pair<ProfileFuncRef, uint64_t>;
struct CallTargetComparator {
bool operator()(const CallTarget &LHS, const CallTarget &RHS) const {
if (LHS.second != RHS.second)
@@ -349,7 +344,7 @@ class SampleRecord {
};
using SortedCallTargetSet = std::set<CallTarget, CallTargetComparator>;
- using CallTargetMap = StringMap<uint64_t>;
+ using CallTargetMap = std::unordered_map<ProfileFuncRef, uint64_t>;
SampleRecord() = default;
/// Increment the number of samples for this record by \p S.
@@ -378,7 +373,7 @@ class SampleRecord {
///
/// Sample counts accumulate using saturating arithmetic, to avoid wrapping
/// around unsigned integers.
- sampleprof_error addCalledTarget(StringRef F, uint64_t S,
+ sampleprof_error addCalledTarget(ProfileFuncRef F, uint64_t S,
uint64_t Weight = 1) {
uint64_t &TargetSamples = CallTargets[F];
bool Overflowed;
@@ -390,7 +385,7 @@ class SampleRecord {
/// Remove called function from the call target map. Return the target sample
/// count of the called function.
- uint64_t removeCalledTarget(StringRef F) {
+ uint64_t removeCalledTarget(ProfileFuncRef F) {
uint64_t Count = 0;
auto I = CallTargets.find(F);
if (I != CallTargets.end()) {
@@ -476,12 +471,12 @@ enum ContextAttributeMask {
// Represents a context frame with function name and line location
struct SampleContextFrame {
- StringRef FuncName;
+ ProfileFuncRef FuncName;
LineLocation Location;
SampleContextFrame() : Location(0, 0) {}
- SampleContextFrame(StringRef FuncName, LineLocation Location)
+ SampleContextFrame(ProfileFuncRef FuncName, LineLocation Location)
: FuncName(FuncName), Location(Location) {}
bool operator==(const SampleContextFrame &That) const {
@@ -502,6 +497,12 @@ struct SampleContextFrame {
}
return OContextStr.str();
}
+
+ uint64_t getHashCode() const {
+ uint64_t NameHash = FuncName.getHashCode();
+ uint64_t LocId = Location.getHashCode();
+ return NameHash + (LocId << 5) + LocId;
+ }
};
static inline hash_code hash_value(const SampleContextFrame &arg) {
@@ -535,6 +536,9 @@ class SampleContext {
SampleContext(StringRef Name)
: Name(Name), State(UnknownContext), Attributes(ContextNone) {}
+ SampleContext(ProfileFuncRef Name)
+ : Name(Name), State(UnknownContext), Attributes(ContextNone) {}
+
SampleContext(SampleContextFrames Context,
ContextStateMask CState = RawContext)
: Attributes(ContextNone) {
@@ -555,7 +559,7 @@ class SampleContext {
bool HasContext = ContextStr.startswith("[");
if (!HasContext) {
State = UnknownContext;
- Name = ContextStr;
+ Name = ProfileFuncRef(ContextStr);
} else {
CSNameTable.emplace_back();
SampleContextFrameVector &Context = CSNameTable.back();
@@ -572,7 +576,7 @@ class SampleContext {
ContextStr = ContextStr.substr(1, ContextStr.size() - 2);
StringRef ContextRemain = ContextStr;
StringRef ChildContext;
- StringRef CalleeName;
+ ProfileFuncRef CalleeName;
while (!ContextRemain.empty()) {
auto ContextSplit = ContextRemain.split(" @ ");
ChildContext = ContextSplit.first;
@@ -585,11 +589,12 @@ class SampleContext {
// Decode context string for a frame to get function name and location.
// `ContextStr` is in the form of `FuncName:StartLine.Discriminator`.
- static void decodeContextString(StringRef ContextStr, StringRef &FName,
+ static void decodeContextString(StringRef ContextStr,
+ ProfileFuncRef &FName,
LineLocation &LineLoc) {
// Get function name
auto EntrySplit = ContextStr.split(':');
- FName = EntrySplit.first;
+ FName = ProfileFuncRef(EntrySplit.first);
LineLoc = {0, 0};
if (!EntrySplit.second.empty()) {
@@ -616,7 +621,7 @@ class SampleContext {
void clearState(ContextStateMask S) { State &= (uint32_t)~S; }
bool hasContext() const { return State != UnknownContext; }
bool isBaseContext() const { return FullContext.size() == 1; }
- StringRef getName() const { return Name; }
+ ProfileFuncRef getName() const { return Name; }
SampleContextFrames getContextFrames() const { return FullContext; }
static std::string getContextString(SampleContextFrames Context,
@@ -641,14 +646,11 @@ class SampleContext {
uint64_t getHashCode() const {
if (hasContext())
return hash_value(getContextFrames());
-
- // For non-context function name, use its MD5 as hash value, so that it is
- // consistent with the profile map's key.
- return hashFuncName(getName());
+ return getName().getHashCode();
}
/// Set the name of the function and clear the current context.
- void setName(StringRef FunctionName) {
+ void setName(ProfileFuncRef FunctionName) {
Name = FunctionName;
FullContext = SampleContextFrames();
State = UnknownContext;
@@ -713,7 +715,7 @@ class SampleContext {
private:
/// Mangled name of the function.
- StringRef Name;
+ ProfileFuncRef Name;
// Full context including calling context and leaf function name
SampleContextFrames FullContext;
// State of the associated sample profile
@@ -736,7 +738,7 @@ class SampleProfileReaderItaniumRemapper;
using BodySampleMap = std::map<LineLocation, SampleRecord>;
// NOTE: Using a StringMap here makes parsed profiles consume around 17% more
// memory, which is *very* significant for large profiles.
-using FunctionSamplesMap = std::map<std::string, FunctionSamples, std::less<>>;
+using FunctionSamplesMap = std::map<ProfileFuncRef, FunctionSamples>;
using CallsiteSampleMap = std::map<LineLocation, FunctionSamplesMap>;
using LocToLocMap =
std::unordered_map<LineLocation, LineLocation, LineLocationHash>;
@@ -788,14 +790,16 @@ class FunctionSamples {
sampleprof_error addCalledTargetSamples(uint32_t LineOffset,
uint32_t Discriminator,
- StringRef FName, uint64_t Num,
+ ProfileFuncRef FName,
+ uint64_t Num,
uint64_t Weight = 1) {
return BodySamples[LineLocation(LineOffset, Discriminator)].addCalledTarget(
FName, Num, Weight);
}
sampleprof_error addSampleRecord(LineLocation Location,
- const SampleRecord &SampleRecord, uint64_t Weight = 1) {
+ const SampleRecord &SampleRecord,
+ uint64_t Weight = 1) {
return BodySamples[Location].merge(SampleRecord, Weight);
}
@@ -803,7 +807,7 @@ class FunctionSamples {
// the number of body samples actually decreased.
uint64_t removeCalledTargetAndBodySample(uint32_t LineOffset,
uint32_t Discriminator,
- StringRef FName) {
+ ProfileFuncRef FName) {
uint64_t Count = 0;
auto I = BodySamples.find(LineLocation(LineOffset, Discriminator));
if (I != BodySamples.end()) {
@@ -1046,16 +1050,16 @@ class FunctionSamples {
};
if (isDeclaration(SymbolMap.lookup(getFuncName()))) {
// Add to the import list only when it's defined out of module.
- S.insert(getGUID(getName()));
+ S.insert(getGUID());
}
// Import hot CallTargets, which may not be available in IR because full
// profile annotation cannot be done until backend compilation in ThinLTO.
for (const auto &BS : BodySamples)
for (const auto &TS : BS.second.getCallTargets())
- if (TS.getValue() > Threshold) {
- const Function *Callee = SymbolMap.lookup(getFuncName(TS.getKey()));
+ if (TS.second > Threshold) {
+ const Function *Callee = SymbolMap.lookup(getFuncName(TS.first));
if (isDeclaration(Callee))
- S.insert(getGUID(TS.getKey()));
+ S.insert(TS.first.getHashCode());
}
for (const auto &CS : CallsiteSamples)
for (const auto &NameFS : CS.second)
@@ -1063,10 +1067,12 @@ class FunctionSamples {
}
/// Set the name of the function.
- void setName(StringRef FunctionName) { Context.setName(FunctionName); }
+ void setName(ProfileFuncRef FunctionName) {
+ Context.setName(FunctionName);
+ }
/// Return the function name.
- StringRef getName() const { return Context.getName(); }
+ ProfileFuncRef getName() const { return Context.getName(); }
/// Return the original function name.
StringRef getFuncName() const { return getFuncName(getName()); }
@@ -1133,12 +1139,12 @@ class FunctionSamples {
/// translate \p Name in current FunctionSamples into its original name
/// by looking up in the function map GUIDToFuncNameMap.
/// If the original name doesn't exist in the map, return empty StringRef.
- StringRef getFuncName(StringRef Name) const {
+ StringRef getFuncName(ProfileFuncRef Name) const {
if (!UseMD5)
- return Name;
+ return reinterpret_cast<StringRef &>(Name);
assert(GUIDToFuncNameMap && "GUIDToFuncNameMap needs to be populated first");
- return GUIDToFuncNameMap->lookup(std::stoull(Name.data()));
+ return GUIDToFuncNameMap->lookup(Name.getHashCode());
}
/// Returns the line offset to the start line of the subprogram.
@@ -1154,7 +1160,9 @@ class FunctionSamples {
/// Returns a unique hash code for a combination of a callsite location and
/// the callee function name.
- static uint64_t getCallSiteHash(StringRef CalleeName,
+ /// Guarantee MD5 and non-MD5 representation of the same function results in
+ /// the same hash.
+ static uint64_t getCallSiteHash(ProfileFuncRef CalleeName,
const LineLocation &Callsite);
/// Get the FunctionSamples of the inline instance where DIL originates
@@ -1195,16 +1203,15 @@ class FunctionSamples {
/// all the function symbols defined or declared in current module.
DenseMap<uint64_t, StringRef> *GUIDToFuncNameMap = nullptr;
- // Assume the input \p Name is a name coming from FunctionSamples itself.
- // If UseMD5 is true, the name is already a GUID and we
- // don't want to return the GUID of GUID.
- static uint64_t getGUID(StringRef Name) {
- return UseMD5 ? std::stoull(Name.data()) : Function::getGUID(Name);
+ /// Return the GUID of the context's name. If the context is already using
+ /// MD5, don't hash it again.
+ uint64_t getGUID() const {
+ return getName().getHashCode();
}
// Find all the names in the current FunctionSamples including names in
// all the inline instances and names of call targets.
- void findAllNames(DenseSet<StringRef> &NameSet) const;
+ void findAllNames(DenseSet<ProfileFuncRef> &NameSet) const;
bool operator==(const FunctionSamples &Other) const {
return (GUIDToFuncNameMap == Other.GUIDToFuncNameMap ||
@@ -1221,9 +1228,6 @@ class FunctionSamples {
return !(*this == Other);
}
- template <typename T>
- const T &getKey() const;
-
private:
/// CFG hash value for the function.
uint64_t FunctionHash = 0;
@@ -1287,9 +1291,8 @@ class FunctionSamples {
const LocToLocMap *IRToProfileLocationMap = nullptr;
};
-template <>
-inline const SampleContext &FunctionSamples::getKey<SampleContext>() const {
- return getContext();
+static inline ProfileFuncRef getRepInFormat(StringRef Name) {
+ return getRepInFormat(Name, FunctionSamples::UseMD5);
}
raw_ostream &operator<<(raw_ostream &OS, const FunctionSamples &FS);
@@ -1398,11 +1401,6 @@ class SampleProfileMap
Ctx);
}
- // Overloaded find() to lookup a function by name.
- iterator find(StringRef Fname) {
- return base_type::find(hashFuncName(Fname));
- }
-
size_t erase(const SampleContext &Ctx) {
return HashKeyMap<std::unordered_map, SampleContext, FunctionSamples>::
erase(Ctx);
@@ -1473,7 +1471,7 @@ class ProfileConverter {
// profile.
void convertCSProfiles();
struct FrameNode {
- FrameNode(StringRef FName = StringRef(),
+ FrameNode(ProfileFuncRef FName = ProfileFuncRef(),
FunctionSamples *FSamples = nullptr,
LineLocation CallLoc = {0, 0})
: FuncName(FName), FuncSamples(FSamples), CallSiteLoc(CallLoc){};
@@ -1481,14 +1479,14 @@ class ProfileConverter {
// Map line+discriminator location to child frame
std::map<uint64_t, FrameNode> AllChildFrames;
// Function name for current frame
- StringRef FuncName;
+ ProfileFuncRef FuncName;
// Function Samples for current frame
FunctionSamples *FuncSamples;
// Callsite location in parent context
LineLocation CallSiteLoc;
FrameNode *getOrCreateChildFrame(const LineLocation &CallSite,
- StringRef CalleeName);
+ ProfileFuncRef CalleeName);
};
static void flattenProfile(SampleProfileMap &ProfileMap,
@@ -1623,7 +1621,9 @@ using namespace sampleprof;
template <> struct DenseMapInfo<SampleContext> {
static inline SampleContext getEmptyKey() { return SampleContext(); }
- static inline SampleContext getTombstoneKey() { return SampleContext("@"); }
+ static inline SampleContext getTombstoneKey() {
+ return SampleContext(ProfileFuncRef(~1ULL));
+ }
static unsigned getHashValue(const SampleContext &Val) {
return Val.getHashCode();
diff --git a/llvm/include/llvm/ProfileData/SampleProfReader.h b/llvm/include/llvm/ProfileData/SampleProfReader.h
index a5b1df3ef550b2b..df30722cb1b5bc3 100644
--- a/llvm/include/llvm/ProfileData/SampleProfReader.h
+++ b/llvm/include/llvm/ProfileData/SampleProfReader.h
@@ -409,13 +409,13 @@ class SampleProfileReader {
/// Return the samples collected for function \p F.
FunctionSamples *getSamplesFor(StringRef Fname) {
- auto It = Profiles.find(Fname);
+ auto It = Profiles.find(ProfileFuncRef(Fname));
if (It != Profiles.end())
return &It->second;
if (Remapper) {
if (auto NameInProfile = Remapper->lookUpNameInProfile(Fname)) {
- auto It = Profiles.find(*NameInProfile);
+ auto It = Profiles.find(ProfileFuncRef(*NameInProfile));
if (It != Profiles.end())
return &It->second;
}
@@ -474,7 +474,7 @@ class SampleProfileReader {
/// It includes all the names that have samples either in outline instance
/// or inline instance.
- virtual std::vector<StringRef> *getNameTable() { return nullptr; }
+ virtual std::vector<ProfileFuncRef> *getNameTable() { return nullptr; }
virtual bool dumpSectionInfo(raw_ostream &OS = dbgs()) { return false; };
/// Return whether names in the profile are all MD5 numbers.
@@ -508,10 +508,6 @@ class SampleProfileReader {
/// Memory buffer holding the profile file.
std::unique_ptr<MemoryBuffer> Buffer;
- /// Extra name buffer holding names created on demand.
- /// This should only be needed for md5 profiles.
- std::unordered_set<std::string> MD5NameBuffer;
-
/// Profile summary information.
std::unique_ptr<ProfileSummary> Summary;
@@ -595,7 +591,9 @@ class SampleProfileReaderBinary : public SampleProfileReader {
/// It includes all the names that have samples either in outline instance
/// or inline instance.
- std::vector<StringRef> *getNameTable() override { return &NameTable; }
+ std::vector<ProfileFuncRef> *getNameTable() override {
+ return &NameTable;
+ }
protected:
/// Read a numeric value of type T from the profile.
@@ -637,7 +635,7 @@ class SampleProfileReaderBinary : public SampleProfileReader {
std::error_code readNameTable();
/// Read a string indirectly via the name table. Optionally return the index.
- ErrorOr<StringRef> readStringFromTable(size_t *RetIdx = nullptr);
+ ErrorOr<ProfileFuncRef> readStringFromTable(size_t *RetIdx = nullptr);
/// Read a context indirectly via the CSNameTable. Optionally return the
/// index.
@@ -654,16 +652,7 @@ class SampleProfileReaderBinary : public SampleProfileReader {
const uint8_t *End = nullptr;
/// Function name table.
- std::vector<StringRef> NameTable;
-
- /// If MD5 is used in NameTable section, the section saves uint64_t data.
- /// The uint64_t data has to be converted to a string and then the string
- /// will be used to initialize StringRef in NameTable.
- /// Note NameTable contains StringRef so it needs another buffer to own
- /// the string data. MD5StringBuf serves as the string buffer that is
- /// referenced by NameTable (vector of StringRef). We make sure
- /// the lifetime of MD5StringBuf is not shorter than that of NameTable.
- std::vector<std::string> MD5StringBuf;
+ std::vector<ProfileFuncRef> NameTable;
/// The starting address of fixed length MD5 name table section.
const uint8_t *MD5NameMemStart = nullptr;
diff --git a/llvm/include/llvm/ProfileData/SampleProfWriter.h b/llvm/include/llvm/ProfileData/SampleProfWriter.h
index 1f19283ea1dd0a8..3eff17e5e5f0900 100644
--- a/llvm/include/llvm/ProfileData/SampleProfWriter.h
+++ b/llvm/include/llvm/ProfileData/SampleProfWriter.h
@@ -196,20 +196,20 @@ class SampleProfileWriterBinary : public SampleProfileWriter {
std::error_code writeSample(const FunctionSamples &S) override;
protected:
- virtual MapVector<StringRef, uint32_t> &getNameTable() { return NameTable; }
+ virtual MapVector<ProfileFuncRef, uint32_t> &getNameTable() { return NameTable; }
virtual std::error_code writeMagicIdent(SampleProfileFormat Format);
virtual std::error_code writeNameTable();
std::error_code writeHeader(const SampleProfileMap &ProfileMap) override;
std::error_code writeSummary();
virtual std::error_code writeContextIdx(const SampleContext &Context);
- std::error_code writeNameIdx(StringRef FName);
+ std::error_code writeNameIdx(ProfileFuncRef FName);
std::error_code writeBody(const FunctionSamples &S);
- inline void stablizeNameTable(MapVector<StringRef, uint32_t> &NameTable,
- std::set<StringRef> &V);
+ inline void stablizeNameTable(MapVector<ProfileFuncRef, uint32_t> &NameTable,
+ std::set<ProfileFuncRef> &V);
- MapVector<StringRef, uint32_t> NameTable;
+ MapVector<ProfileFuncRef, uint32_t> NameTable;
- void addName(StringRef FName);
+ void addName(ProfileFuncRef FName);
virtual void addContext(const SampleContext &Context);
void addNames(const FunctionSamples &S);
diff --git a/llvm/include/llvm/Transforms/IPO/ProfiledCallGraph.h b/llvm/include/llvm/Transforms/IPO/ProfiledCallGraph.h
index bc8360a80bc02bd..b575058614211e5 100644
--- a/llvm/include/llvm/Transforms/IPO/ProfiledCallGraph.h
+++ b/llvm/include/llvm/Transforms/IPO/ProfiledCallGraph.h
@@ -53,9 +53,10 @@ struct ProfiledCallGraphNode {
using iterator = edges::iterator;
using const_iterator = edges::const_iterator;
- ProfiledCallGraphNode(StringRef FName = StringRef()) : Name(FName) {}
+ ProfiledCallGraphNode(ProfileFuncRef FName = ProfileFuncRef()) : Name(FName)
+ {}
- StringRef Name;
+ ProfileFuncRef Name;
edges Edges;
};
@@ -85,7 +86,7 @@ class ProfiledCallGraph {
std::queue<ContextTrieNode *> Queue;
for (auto &Child : ContextTracker.getRootContext().getAllChildContext()) {
ContextTrieNode *Callee = &Child.second;
- addProfiledFunction(ContextTracker.getFuncNameFor(Callee));
+ addProfiledFunction(Callee->getFuncName());
Queue.push(Callee);
}
@@ -102,7 +103,7 @@ class ProfiledCallGraph {
// context-based one, which may in turn block context-based inlining.
for (auto &Child : Caller->getAllChildContext()) {
ContextTrieNode *Callee = &Child.second;
- addProfiledFunction(ContextTracker.getFuncNameFor(Callee));
+ addProfiledFunction(Callee->getFuncName());
Queue.push(Callee);
// Fetch edge weight from the profile.
@@ -123,8 +124,7 @@ class ProfiledCallGraph {
Weight = std::max(CallsiteCount, CalleeEntryCount);
}
- addProfiledCall(ContextTracker.getFuncNameFor(Caller),
- ContextTracker.getFuncNameFor(Callee), Weight);
+ addProfiledCall(Caller->getFuncName(), Callee->getFuncName(), Weight);
}
}
@@ -137,7 +137,7 @@ class ProfiledCallGraph {
iterator end() { return Root.Edges.end(); }
ProfiledCallGraphNode *getEntryNode() { return &Root; }
- void addProfiledFunction(StringRef Name) {
+ void addProfiledFunction(ProfileFuncRef Name) {
if (!ProfiledFunctions.count(Name)) {
// Link to synthetic root to make sure every node is reachable
// from root. This does not affect SCC order.
@@ -147,7 +147,7 @@ class ProfiledCallGraph {
}
private:
- void addProfiledCall(StringRef CallerName, StringRef CalleeName,
+ void addProfiledCall(ProfileFuncRef CallerName, ProfileFuncRef CalleeName,
uint64_t Weight = 0) {
assert(ProfiledFunctions.count(CallerName));
auto CalleeIt = ProfiledFunctions.find(CalleeName);
@@ -168,19 +168,19 @@ class ProfiledCallGraph {
}
void addProfiledCalls(const FunctionSamples &Samples) {
- addProfiledFunction(Samples.getFuncName());
+ addProfiledFunction(Samples.getName());
for (const auto &Sample : Samples.getBodySamples()) {
for (const auto &[Target, Frequency] : Sample.second.getCallTargets()) {
addProfiledFunction(Target);
- addProfiledCall(Samples.getFuncName(), Target, Frequency);
+ addProfiledCall(Samples.getName(), Target, Frequency);
}
}
for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) {
for (const auto &InlinedSamples : CallsiteSamples.second) {
addProfiledFunction(InlinedSamples.first);
- addProfiledCall(Samples.getFuncName(), InlinedSamples.first,
+ addProfiledCall(Samples.getName(), InlinedSamples.first,
InlinedSamples.second.getHeadSamplesEstimate());
addProfiledCalls(InlinedSamples.second);
}
@@ -206,7 +206,7 @@ class ProfiledCallGraph {
}
ProfiledCallGraphNode Root;
- StringMap<ProfiledCallGraphNode> ProfiledFunctions;
+ std::unordered_map<ProfileFuncRef, ProfiledCallGraphNode> ProfiledFunctions;
};
} // end namespace sampleprof
diff --git a/llvm/include/llvm/Transforms/IPO/SampleContextTracker.h b/llvm/include/llvm/Transforms/IPO/SampleContextTracker.h
index 347dac1e9684db1..270dd25672b9f36 100644
--- a/llvm/include/llvm/Transforms/IPO/SampleContextTracker.h
+++ b/llvm/include/llvm/Transforms/IPO/SampleContextTracker.h
@@ -35,20 +35,20 @@ class Instruction;
class ContextTrieNode {
public:
ContextTrieNode(ContextTrieNode *Parent = nullptr,
- StringRef FName = StringRef(),
+ ProfileFuncRef FName = ProfileFuncRef(),
FunctionSamples *FSamples = nullptr,
LineLocation CallLoc = {0, 0})
: ParentContext(Parent), FuncName(FName), FuncSamples(FSamples),
CallSiteLoc(CallLoc){};
ContextTrieNode *getChildContext(const LineLocation &CallSite,
- StringRef ChildName);
+ ProfileFuncRef ChildName);
ContextTrieNode *getHottestChildContext(const LineLocation &CallSite);
ContextTrieNode *getOrCreateChildContext(const LineLocation &CallSite,
- StringRef ChildName,
+ ProfileFuncRef ChildName,
bool AllowCreate = true);
- void removeChildContext(const LineLocation &CallSite, StringRef ChildName);
+ void removeChildContext(const LineLocation &CallSite, ProfileFuncRef ChildName);
std::map<uint64_t, ContextTrieNode> &getAllChildContext();
- StringRef getFuncName() const;
+ ProfileFuncRef getFuncName() const;
FunctionSamples *getFunctionSamples() const;
void setFunctionSamples(FunctionSamples *FSamples);
std::optional<uint32_t> getFunctionSize() const;
@@ -68,7 +68,7 @@ class ContextTrieNode {
ContextTrieNode *ParentContext;
// Function name for current context
- StringRef FuncName;
+ ProfileFuncRef FuncName;
// Function Samples for current context
FunctionSamples *FuncSamples;
@@ -118,7 +118,8 @@ class SampleContextTracker {
FunctionSamples *getBaseSamplesFor(const Function &Func,
bool MergeContext = true);
// Query base profile for a given function by name.
- FunctionSamples *getBaseSamplesFor(StringRef Name, bool MergeContext = true);
+ FunctionSamples *getBaseSamplesFor(ProfileFuncRef Name,
+ bool MergeContext = true);
// Retrieve the context trie node for given profile context
ContextTrieNode *getContextFor(const SampleContext &Context);
// Get real function name for a given trie node.
@@ -129,7 +130,7 @@ class SampleContextTracker {
void markContextSamplesInlined(const FunctionSamples *InlinedSamples);
ContextTrieNode &getRootContext();
void promoteMergeContextSamplesTree(const Instruction &Inst,
- StringRef CalleeName);
+ ProfileFuncRef CalleeName);
// Create a merged conext-less profile map.
void createContextLessProfileMap(SampleProfileMap &ContextLessProfiles);
@@ -140,7 +141,7 @@ class SampleContextTracker {
return nullptr;
return I->second;
}
- StringMap<ContextSamplesTy> &getFuncToCtxtProfiles() {
+ std::unordered_map<ProfileFuncRef, ContextSamplesTy> &getFuncToCtxtProfiles() {
return FuncToCtxtProfiles;
}
@@ -189,9 +190,9 @@ class SampleContextTracker {
private:
ContextTrieNode *getContextFor(const DILocation *DIL);
ContextTrieNode *getCalleeContextFor(const DILocation *DIL,
- StringRef CalleeName);
- ContextTrieNode *getTopLevelContextNode(StringRef FName);
- ContextTrieNode &addTopLevelContextNode(StringRef FName);
+ ProfileFuncRef CalleeName);
+ ContextTrieNode *getTopLevelContextNode(ProfileFuncRef FName);
+ ContextTrieNode &addTopLevelContextNode(ProfileFuncRef FName);
ContextTrieNode &promoteMergeContextSamplesTree(ContextTrieNode &NodeToPromo);
void mergeContextNode(ContextTrieNode &FromNode, ContextTrieNode &ToNode);
ContextTrieNode &
@@ -204,7 +205,7 @@ class SampleContextTracker {
ProfileToNodeMap[FSample] = Node;
}
// Map from function name to context profiles (excluding base profile)
- StringMap<ContextSamplesTy> FuncToCtxtProfiles;
+ std::unordered_map<ProfileFuncRef, ContextSamplesTy> FuncToCtxtProfiles;
// Map from current FunctionSample to the belonged context trie.
std::unordered_map<const FunctionSamples *, ContextTrieNode *>
diff --git a/llvm/lib/ProfileData/SampleProf.cpp b/llvm/lib/ProfileData/SampleProf.cpp
index b14dc01be236245..a3de3c8e7400f0a 100644
--- a/llvm/lib/ProfileData/SampleProf.cpp
+++ b/llvm/lib/ProfileData/SampleProf.cpp
@@ -121,7 +121,7 @@ sampleprof_error SampleRecord::merge(const SampleRecord &Other,
sampleprof_error Result;
Result = addSamples(Other.getSamples(), Weight);
for (const auto &I : Other.getCallTargets()) {
- MergeResult(Result, addCalledTarget(I.first(), I.second, Weight));
+ MergeResult(Result, addCalledTarget(I.first, I.second, Weight));
}
return Result;
}
@@ -234,9 +234,9 @@ LineLocation FunctionSamples::getCallSiteIdentifier(const DILocation *DIL,
}
}
-uint64_t FunctionSamples::getCallSiteHash(StringRef CalleeName,
+uint64_t FunctionSamples::getCallSiteHash(ProfileFuncRef CalleeName,
const LineLocation &Callsite) {
- uint64_t NameHash = std::hash<std::string>{}(CalleeName.str());
+ uint64_t NameHash = CalleeName.getHashCode();
uint64_t LocId =
(((uint64_t)Callsite.LineOffset) << 32) | Callsite.Discriminator;
return NameHash + (LocId << 5) + LocId;
@@ -268,11 +268,11 @@ const FunctionSamples *FunctionSamples::findFunctionSamples(
return FS;
}
-void FunctionSamples::findAllNames(DenseSet<StringRef> &NameSet) const {
+void FunctionSamples::findAllNames(DenseSet<ProfileFuncRef> &NameSet) const {
NameSet.insert(getName());
for (const auto &BS : BodySamples)
for (const auto &TS : BS.second.getCallTargets())
- NameSet.insert(TS.getKey());
+ NameSet.insert(TS.first);
for (const auto &CS : CallsiteSamples) {
for (const auto &NameFS : CS.second) {
@@ -287,18 +287,15 @@ const FunctionSamples *FunctionSamples::findFunctionSamplesAt(
SampleProfileReaderItaniumRemapper *Remapper) const {
CalleeName = getCanonicalFnName(CalleeName);
- std::string CalleeGUID;
- CalleeName = getRepInFormat(CalleeName, UseMD5, CalleeGUID);
-
auto iter = CallsiteSamples.find(mapIRLocToProfileLoc(Loc));
if (iter == CallsiteSamples.end())
return nullptr;
- auto FS = iter->second.find(CalleeName);
+ auto FS = iter->second.find(getRepInFormat(CalleeName));
if (FS != iter->second.end())
return &FS->second;
if (Remapper) {
if (auto NameInProfile = Remapper->lookUpNameInProfile(CalleeName)) {
- auto FS = iter->second.find(*NameInProfile);
+ auto FS = iter->second.find(getRepInFormat(*NameInProfile));
if (FS != iter->second.end())
return &FS->second;
}
@@ -422,7 +419,7 @@ void ProfileSymbolList::dump(raw_ostream &OS) const {
ProfileConverter::FrameNode *
ProfileConverter::FrameNode::getOrCreateChildFrame(const LineLocation &CallSite,
- StringRef CalleeName) {
+ ProfileFuncRef CalleeName) {
uint64_t Hash = FunctionSamples::getCallSiteHash(CalleeName, CallSite);
auto It = AllChildFrames.find(Hash);
if (It != AllChildFrames.end()) {
@@ -468,7 +465,7 @@ void ProfileConverter::convertCSProfiles(ProfileConverter::FrameNode &Node) {
if (!ChildProfile)
continue;
SampleContext OrigChildContext = ChildProfile->getContext();
- hash_code OrigChildContextHash = OrigChildContext.getHashCode();
+ uint64_t OrigChildContextHash = OrigChildContext.getHashCode();
// Reset the child context to be contextless.
ChildProfile->getContext().setName(OrigChildContext.getName());
if (NodeProfile) {
@@ -484,7 +481,7 @@ void ProfileConverter::convertCSProfiles(ProfileConverter::FrameNode &Node) {
NodeProfile->removeTotalSamples(Count);
}
- hash_code NewChildProfileHash(0);
+ uint64_t NewChildProfileHash = 0;
// Separate child profile to be a standalone profile, if the current parent
// profile doesn't exist. This is a duplicating operation when the child
// profile is already incorporated into the parent which is still useful and
@@ -498,7 +495,7 @@ void ProfileConverter::convertCSProfiles(ProfileConverter::FrameNode &Node) {
ProfileMap[ChildProfile->getContext()].merge(*ChildProfile);
NewChildProfileHash = ChildProfile->getContext().getHashCode();
auto &SamplesMap = NodeProfile->functionSamplesAt(ChildNode.CallSiteLoc);
- SamplesMap[ChildProfile->getName().str()].getContext().setAttribute(
+ SamplesMap[ChildProfile->getName()].getContext().setAttribute(
ContextDuplicatedIntoBase);
}
diff --git a/llvm/lib/ProfileData/SampleProfReader.cpp b/llvm/lib/ProfileData/SampleProfReader.cpp
index 6d355cdc41840b4..86306c14a048da4 100644
--- a/llvm/lib/ProfileData/SampleProfReader.cpp
+++ b/llvm/lib/ProfileData/SampleProfReader.cpp
@@ -91,7 +91,7 @@ static void dumpFunctionProfileJson(const FunctionSamples &S,
JOS.attributeArray("calls", [&] {
for (const auto &J : CallTargets) {
JOS.object([&] {
- JOS.attribute("function", J.first);
+ JOS.attribute("function", J.first.str());
JOS.attribute("samples", J.second);
});
}
@@ -117,7 +117,7 @@ static void dumpFunctionProfileJson(const FunctionSamples &S,
};
JOS.object([&] {
- JOS.attribute("name", S.getName());
+ JOS.attribute("name", S.getName().str());
JOS.attribute("total", S.getTotalSamples());
if (TopLevel)
JOS.attribute("head", S.getHeadSamples());
@@ -392,8 +392,8 @@ std::error_code SampleProfileReaderText::readImpl() {
switch (LineTy) {
case LineType::CallSiteProfile: {
FunctionSamples &FSamples = InlineStack.back()->functionSamplesAt(
- LineLocation(LineOffset, Discriminator))[std::string(FName)];
- FSamples.setName(FName);
+ LineLocation(LineOffset, Discriminator))[ProfileFuncRef(FName)];
+ FSamples.setName(ProfileFuncRef(FName));
MergeResult(Result, FSamples.addTotalSamples(NumSamples));
InlineStack.push_back(&FSamples);
DepthMetadata = 0;
@@ -406,7 +406,8 @@ std::error_code SampleProfileReaderText::readImpl() {
FunctionSamples &FProfile = *InlineStack.back();
for (const auto &name_count : TargetCountMap) {
MergeResult(Result, FProfile.addCalledTargetSamples(
- LineOffset, Discriminator, name_count.first,
+ LineOffset, Discriminator,
+ ProfileFuncRef(name_count.first),
name_count.second));
}
MergeResult(Result, FProfile.addBodySamples(LineOffset, Discriminator,
@@ -523,24 +524,21 @@ inline ErrorOr<size_t> SampleProfileReaderBinary::readStringIndex(T &Table) {
return *Idx;
}
-ErrorOr<StringRef>
+ErrorOr<ProfileFuncRef>
SampleProfileReaderBinary::readStringFromTable(size_t *RetIdx) {
auto Idx = readStringIndex(NameTable);
if (std::error_code EC = Idx.getError())
return EC;
- // Lazy loading, if the string has not been materialized from memory storing
- // MD5 values, then it is default initialized with the null pointer. This can
- // only happen when using fixed length MD5, that bounds check is performed
- // while parsing the name table to ensure MD5NameMemStart points to an array
- // with enough MD5 entries.
- StringRef &SR = NameTable[*Idx];
- if (!SR.data()) {
+ // If using fixed length MD5, just read directly from the pointer to the name
+ // table.
+ ProfileFuncRef &SR = NameTable[*Idx];
+ if (SR.empty()) {
assert(MD5NameMemStart);
using namespace support;
uint64_t FID = endian::read<uint64_t, little, unaligned>(
MD5NameMemStart + (*Idx) * sizeof(uint64_t));
- SR = MD5StringBuf.emplace_back(std::to_string(FID));
+ SR = ProfileFuncRef(FID);
}
if (RetIdx)
*RetIdx = *Idx;
@@ -662,7 +660,7 @@ SampleProfileReaderBinary::readProfile(FunctionSamples &FProfile) {
uint32_t DiscriminatorVal = (*Discriminator) & getDiscriminatorMask();
FunctionSamples &CalleeProfile = FProfile.functionSamplesAt(
- LineLocation(*LineOffset, DiscriminatorVal))[std::string(*FName)];
+ LineLocation(*LineOffset, DiscriminatorVal))[*FName];
CalleeProfile.setName(*FName);
if (std::error_code EC = readProfile(CalleeProfile))
return EC;
@@ -902,12 +900,16 @@ std::error_code SampleProfileReaderExtBinaryBase::readFuncProfiles() {
for (const auto &NameOffset : FuncOffsetList) {
const auto &FContext = NameOffset.first;
auto FName = FContext.getName();
+ StringRef FNameString;
+ if (!useMD5()) {
+ FNameString = reinterpret_cast<StringRef &>(FName);
+ }
// For function in the current module, keep its farthest ancestor
// context. This can be used to load itself and its child and
// sibling contexts.
- if ((useMD5() && FuncGuidsToUse.count(std::stoull(FName.data()))) ||
- (!useMD5() && (FuncsToUse.count(FName) ||
- (Remapper && Remapper->exist(FName))))) {
+ if ((useMD5() && FuncGuidsToUse.count(FName.getHashCode())) ||
+ (!useMD5() && (FuncsToUse.count(FNameString) ||
+ (Remapper && Remapper->exist(FNameString))))) {
if (!CommonContext || !CommonContext->IsPrefixOf(FContext))
CommonContext = &FContext;
}
@@ -937,7 +939,8 @@ std::error_code SampleProfileReaderExtBinaryBase::readFuncProfiles() {
for (auto NameOffset : FuncOffsetList) {
SampleContext FContext(NameOffset.first);
auto FuncName = FContext.getName();
- if (!FuncsToUse.count(FuncName) && !Remapper->exist(FuncName))
+ StringRef FuncNameStr = reinterpret_cast<StringRef &>(FuncName);
+ if (!FuncsToUse.count(FuncNameStr) && !Remapper->exist(FuncNameStr))
continue;
const uint8_t *FuncProfileAddr = Start + NameOffset.second;
if (std::error_code EC = readFuncProfile(FuncProfileAddr))
@@ -1069,8 +1072,6 @@ std::error_code SampleProfileReaderBinary::readNameTable() {
// tables mixing string and MD5, all of them have to be normalized to use MD5,
// because optimization passes can only handle either type.
bool UseMD5 = useMD5();
- if (UseMD5)
- MD5StringBuf.reserve(MD5StringBuf.size() + *Size);
NameTable.clear();
NameTable.reserve(*Size);
@@ -1089,12 +1090,12 @@ std::error_code SampleProfileReaderBinary::readNameTable() {
if (std::error_code EC = Name.getError())
return EC;
if (UseMD5) {
- uint64_t FID = hashFuncName(*Name);
+ ProfileFuncRef FID(*Name);
if (!ProfileIsCS)
- MD5SampleContextTable.emplace_back(FID);
- NameTable.emplace_back(MD5StringBuf.emplace_back(std::to_string(FID)));
+ MD5SampleContextTable.emplace_back(FID.getHashCode());
+ NameTable.emplace_back(FID);
} else
- NameTable.push_back(*Name);
+ NameTable.push_back(ProfileFuncRef(*Name));
}
if (!ProfileIsCS)
MD5SampleContextStart = MD5SampleContextTable.data();
@@ -1121,7 +1122,6 @@ SampleProfileReaderExtBinaryBase::readNameTableSec(bool IsMD5,
// index has been read before by checking whether the element in the
// NameTable is empty, meanwhile readStringIndex can do the boundary
// check using the size of NameTable.
- MD5StringBuf.reserve(MD5StringBuf.size() + *Size);
NameTable.clear();
NameTable.resize(*Size);
MD5NameMemStart = Data;
@@ -1137,7 +1137,6 @@ SampleProfileReaderExtBinaryBase::readNameTableSec(bool IsMD5,
if (std::error_code EC = Size.getError())
return EC;
- MD5StringBuf.reserve(MD5StringBuf.size() + *Size);
NameTable.clear();
NameTable.reserve(*Size);
if (!ProfileIsCS)
@@ -1148,7 +1147,7 @@ SampleProfileReaderExtBinaryBase::readNameTableSec(bool IsMD5,
return EC;
if (!ProfileIsCS)
support::endian::write64le(&MD5SampleContextTable[I], *FID);
- NameTable.emplace_back(MD5StringBuf.emplace_back(std::to_string(*FID)));
+ NameTable.emplace_back(ProfileFuncRef(*FID));
}
if (!ProfileIsCS)
MD5SampleContextStart = MD5SampleContextTable.data();
@@ -1250,7 +1249,7 @@ SampleProfileReaderExtBinaryBase::readFuncMetadata(bool ProfileHasAttribute,
CalleeProfile = const_cast<FunctionSamples *>(
&FProfile->functionSamplesAt(LineLocation(
*LineOffset,
- *Discriminator))[std::string(FContext.getName())]);
+ *Discriminator))[FContext.getName()]);
}
if (std::error_code EC =
readFuncMetadata(ProfileHasAttribute, CalleeProfile))
@@ -1660,7 +1659,7 @@ std::error_code SampleProfileReaderGCC::readOneFunctionProfile(
// body, there will be identical replicated profiles for the
// original function. In this case, we simply not bother updating
// the profile of the original function.
- FProfile = &Profiles[Name];
+ FProfile = &Profiles[ProfileFuncRef(Name)];
FProfile->addHeadSamples(HeadCount);
if (FProfile->getTotalSamples() > 0)
Update = false;
@@ -1672,9 +1671,9 @@ std::error_code SampleProfileReaderGCC::readOneFunctionProfile(
uint32_t LineOffset = Offset >> 16;
uint32_t Discriminator = Offset & 0xffff;
FProfile = &CallerProfile->functionSamplesAt(
- LineLocation(LineOffset, Discriminator))[std::string(Name)];
+ LineLocation(LineOffset, Discriminator))[ProfileFuncRef(Name)];
}
- FProfile->setName(Name);
+ FProfile->setName(ProfileFuncRef(Name));
for (uint32_t I = 0; I < NumPosCounts; ++I) {
uint32_t Offset;
@@ -1730,7 +1729,8 @@ std::error_code SampleProfileReaderGCC::readOneFunctionProfile(
if (Update)
FProfile->addCalledTargetSamples(LineOffset, Discriminator,
- TargetName, TargetCount);
+ ProfileFuncRef(TargetName),
+ TargetCount);
}
}
@@ -1791,11 +1791,13 @@ void SampleProfileReaderItaniumRemapper::applyRemapping(LLVMContext &Ctx) {
// We will need to remap the entire context string.
assert(Remappings && "should be initialized while creating remapper");
for (auto &Sample : Reader.getProfiles()) {
- DenseSet<StringRef> NamesInSample;
+ DenseSet<ProfileFuncRef> NamesInSample;
Sample.second.findAllNames(NamesInSample);
- for (auto &Name : NamesInSample)
- if (auto Key = Remappings->insert(Name))
- NameMap.insert({Key, Name});
+ for (auto &Name : NamesInSample) {
+ StringRef NameStr = reinterpret_cast<StringRef &>(Name);
+ if (auto Key = Remappings->insert(NameStr))
+ NameMap.insert({Key, NameStr});
+ }
}
RemappingApplied = true;
diff --git a/llvm/lib/ProfileData/SampleProfWriter.cpp b/llvm/lib/ProfileData/SampleProfWriter.cpp
index f418dbe71057095..1d5c811331cc9bf 100644
--- a/llvm/lib/ProfileData/SampleProfWriter.cpp
+++ b/llvm/lib/ProfileData/SampleProfWriter.cpp
@@ -348,7 +348,7 @@ std::error_code SampleProfileWriterExtBinaryBase::writeNameTable() {
return SampleProfileWriterBinary::writeNameTable();
auto &OS = *OutputStream;
- std::set<StringRef> V;
+ std::set<ProfileFuncRef> V;
stablizeNameTable(NameTable, V);
// Write out the MD5 name table. We wrote unencoded MD5 so reader can
@@ -357,7 +357,7 @@ std::error_code SampleProfileWriterExtBinaryBase::writeNameTable() {
encodeULEB128(NameTable.size(), OS);
support::endian::Writer Writer(OS, support::little);
for (auto N : V)
- Writer.write(hashFuncName(N));
+ Writer.write(N.getHashCode());
return sampleprof_error::success;
}
@@ -371,10 +371,14 @@ std::error_code SampleProfileWriterExtBinaryBase::writeNameTableSection(
// If NameTable contains ".__uniq." suffix, set SecFlagUniqSuffix flag
// so compiler won't strip the suffix during profile matching after
// seeing the flag in the profile.
- for (const auto &I : NameTable) {
- if (I.first.contains(FunctionSamples::UniqSuffix)) {
- addSectionFlag(SecNameTable, SecNameTableFlags::SecFlagUniqSuffix);
- break;
+ // Original names are unavailable if using MD5, so this option has no use.
+ if (!UseMD5) {
+ std::string Dummy;
+ for (const auto &I : NameTable) {
+ if (I.first.stringRef(Dummy).contains(FunctionSamples::UniqSuffix)) {
+ addSectionFlag(SecNameTable, SecNameTableFlags::SecFlagUniqSuffix);
+ break;
+ }
}
}
@@ -632,7 +636,7 @@ SampleProfileWriterBinary::writeContextIdx(const SampleContext &Context) {
return writeNameIdx(Context.getName());
}
-std::error_code SampleProfileWriterBinary::writeNameIdx(StringRef FName) {
+std::error_code SampleProfileWriterBinary::writeNameIdx(ProfileFuncRef FName) {
auto &NTable = getNameTable();
const auto &Ret = NTable.find(FName);
if (Ret == NTable.end())
@@ -641,7 +645,7 @@ std::error_code SampleProfileWriterBinary::writeNameIdx(StringRef FName) {
return sampleprof_error::success;
}
-void SampleProfileWriterBinary::addName(StringRef FName) {
+void SampleProfileWriterBinary::addName(ProfileFuncRef FName) {
auto &NTable = getNameTable();
NTable.insert(std::make_pair(FName, 0));
}
@@ -655,7 +659,7 @@ void SampleProfileWriterBinary::addNames(const FunctionSamples &S) {
for (const auto &I : S.getBodySamples()) {
const SampleRecord &Sample = I.second;
for (const auto &J : Sample.getCallTargets())
- addName(J.first());
+ addName(J.first);
}
// Recursively add all the names for inlined callsites.
@@ -679,18 +683,18 @@ void SampleProfileWriterExtBinaryBase::addContext(
}
void SampleProfileWriterBinary::stablizeNameTable(
- MapVector<StringRef, uint32_t> &NameTable, std::set<StringRef> &V) {
+ MapVector<ProfileFuncRef, uint32_t> &NameTable, std::set<ProfileFuncRef> &V) {
// Sort the names to make NameTable deterministic.
for (const auto &I : NameTable)
V.insert(I.first);
int i = 0;
- for (const StringRef &N : V)
+ for (const ProfileFuncRef &N : V)
NameTable[N] = i++;
}
std::error_code SampleProfileWriterBinary::writeNameTable() {
auto &OS = *OutputStream;
- std::set<StringRef> V;
+ std::set<ProfileFuncRef> V;
stablizeNameTable(NameTable, V);
// Write out the name table.
@@ -836,7 +840,7 @@ std::error_code SampleProfileWriterBinary::writeBody(const FunctionSamples &S) {
encodeULEB128(Sample.getSamples(), OS);
encodeULEB128(Sample.getCallTargets().size(), OS);
for (const auto &J : Sample.getSortedCallTargets()) {
- StringRef Callee = J.first;
+ auto Callee = J.first;
uint64_t CalleeSamples = J.second;
if (std::error_code EC = writeNameIdx(Callee))
return EC;
diff --git a/llvm/lib/Target/X86/X86InsertPrefetch.cpp b/llvm/lib/Target/X86/X86InsertPrefetch.cpp
index 29ae05bf0c94673..a7d74275670d521 100644
--- a/llvm/lib/Target/X86/X86InsertPrefetch.cpp
+++ b/llvm/lib/Target/X86/X86InsertPrefetch.cpp
@@ -125,7 +125,8 @@ bool X86InsertPrefetch::findPrefetchInfo(const FunctionSamples *TopSamples,
// Convert serialized prefetch hints into PrefetchInfo objects, and populate
// the Prefetches vector.
for (const auto &S_V : *T) {
- StringRef Name = S_V.getKey();
+ std::string Buffer;
+ StringRef Name = S_V.first.stringRef(Buffer);
if (Name.consume_front(SerializedPrefetchPrefix)) {
int64_t D = static_cast<int64_t>(S_V.second);
unsigned IID = 0;
diff --git a/llvm/lib/Transforms/IPO/SampleContextTracker.cpp b/llvm/lib/Transforms/IPO/SampleContextTracker.cpp
index 79bee8362ef8a9c..7bc692e4332fe0f 100644
--- a/llvm/lib/Transforms/IPO/SampleContextTracker.cpp
+++ b/llvm/lib/Transforms/IPO/SampleContextTracker.cpp
@@ -29,7 +29,7 @@ using namespace sampleprof;
namespace llvm {
ContextTrieNode *ContextTrieNode::getChildContext(const LineLocation &CallSite,
- StringRef CalleeName) {
+ ProfileFuncRef CalleeName) {
if (CalleeName.empty())
return getHottestChildContext(CallSite);
@@ -104,7 +104,7 @@ SampleContextTracker::moveContextSamples(ContextTrieNode &ToNodeParent,
}
void ContextTrieNode::removeChildContext(const LineLocation &CallSite,
- StringRef CalleeName) {
+ ProfileFuncRef CalleeName) {
uint64_t Hash = FunctionSamples::getCallSiteHash(CalleeName, CallSite);
// Note this essentially calls dtor and destroys that child context
AllChildContext.erase(Hash);
@@ -114,7 +114,7 @@ std::map<uint64_t, ContextTrieNode> &ContextTrieNode::getAllChildContext() {
return AllChildContext;
}
-StringRef ContextTrieNode::getFuncName() const { return FuncName; }
+ProfileFuncRef ContextTrieNode::getFuncName() const { return FuncName; }
FunctionSamples *ContextTrieNode::getFunctionSamples() const {
return FuncSamples;
@@ -178,7 +178,7 @@ void ContextTrieNode::dumpTree() {
}
ContextTrieNode *ContextTrieNode::getOrCreateChildContext(
- const LineLocation &CallSite, StringRef CalleeName, bool AllowCreate) {
+ const LineLocation &CallSite, ProfileFuncRef CalleeName, bool AllowCreate) {
uint64_t Hash = FunctionSamples::getCallSiteHash(CalleeName, CallSite);
auto It = AllChildContext.find(Hash);
if (It != AllChildContext.end()) {
@@ -232,14 +232,12 @@ SampleContextTracker::getCalleeContextSamplesFor(const CallBase &Inst,
return nullptr;
CalleeName = FunctionSamples::getCanonicalFnName(CalleeName);
- // Convert real function names to MD5 names, if the input profile is
- // MD5-based.
- std::string FGUID;
- CalleeName = getRepInFormat(CalleeName, FunctionSamples::UseMD5, FGUID);
+
+ ProfileFuncRef FName = getRepInFormat(CalleeName);
// For indirect call, CalleeName will be empty, in which case the context
// profile for callee with largest total samples will be returned.
- ContextTrieNode *CalleeContext = getCalleeContextFor(DIL, CalleeName);
+ ContextTrieNode *CalleeContext = getCalleeContextFor(DIL, FName);
if (CalleeContext) {
FunctionSamples *FSamples = CalleeContext->getFunctionSamples();
LLVM_DEBUG(if (FSamples) {
@@ -305,27 +303,23 @@ SampleContextTracker::getContextSamplesFor(const SampleContext &Context) {
SampleContextTracker::ContextSamplesTy &
SampleContextTracker::getAllContextSamplesFor(const Function &Func) {
StringRef CanonName = FunctionSamples::getCanonicalFnName(Func);
- return FuncToCtxtProfiles[CanonName];
+ return FuncToCtxtProfiles[getRepInFormat(CanonName)];
}
SampleContextTracker::ContextSamplesTy &
SampleContextTracker::getAllContextSamplesFor(StringRef Name) {
- return FuncToCtxtProfiles[Name];
+ return FuncToCtxtProfiles[getRepInFormat(Name)];
}
FunctionSamples *SampleContextTracker::getBaseSamplesFor(const Function &Func,
bool MergeContext) {
StringRef CanonName = FunctionSamples::getCanonicalFnName(Func);
- return getBaseSamplesFor(CanonName, MergeContext);
+ return getBaseSamplesFor(getRepInFormat(CanonName), MergeContext);
}
-FunctionSamples *SampleContextTracker::getBaseSamplesFor(StringRef Name,
+FunctionSamples *SampleContextTracker::getBaseSamplesFor(ProfileFuncRef Name,
bool MergeContext) {
LLVM_DEBUG(dbgs() << "Getting base profile for function: " << Name << "\n");
- // Convert real function names to MD5 names, if the input profile is
- // MD5-based.
- std::string FGUID;
- Name = getRepInFormat(Name, FunctionSamples::UseMD5, FGUID);
// Base profile is top-level node (child of root node), so try to retrieve
// existing top-level node for given function first. If it exists, it could be
@@ -373,7 +367,7 @@ void SampleContextTracker::markContextSamplesInlined(
ContextTrieNode &SampleContextTracker::getRootContext() { return RootContext; }
void SampleContextTracker::promoteMergeContextSamplesTree(
- const Instruction &Inst, StringRef CalleeName) {
+ const Instruction &Inst, ProfileFuncRef CalleeName) {
LLVM_DEBUG(dbgs() << "Promoting and merging context tree for instr: \n"
<< Inst << "\n");
// Get the caller context for the call instruction, we don't use callee
@@ -457,10 +451,11 @@ SampleContextTracker::getContextString(ContextTrieNode *Node) const {
void SampleContextTracker::dump() { RootContext.dumpTree(); }
StringRef SampleContextTracker::getFuncNameFor(ContextTrieNode *Node) const {
+ std::string Dummy;
if (!FunctionSamples::UseMD5)
- return Node->getFuncName();
+ return Node->getFuncName().stringRef(Dummy);
assert(GUIDToFuncNameMap && "GUIDToFuncNameMap needs to be populated first");
- return GUIDToFuncNameMap->lookup(std::stoull(Node->getFuncName().data()));
+ return GUIDToFuncNameMap->lookup(Node->getFuncName().getHashCode());
}
ContextTrieNode *
@@ -470,7 +465,7 @@ SampleContextTracker::getContextFor(const SampleContext &Context) {
ContextTrieNode *
SampleContextTracker::getCalleeContextFor(const DILocation *DIL,
- StringRef CalleeName) {
+ ProfileFuncRef CalleeName) {
assert(DIL && "Expect non-null location");
ContextTrieNode *CallContext = getContextFor(DIL);
@@ -485,7 +480,7 @@ SampleContextTracker::getCalleeContextFor(const DILocation *DIL,
ContextTrieNode *SampleContextTracker::getContextFor(const DILocation *DIL) {
assert(DIL && "Expect non-null location");
- SmallVector<std::pair<LineLocation, StringRef>, 10> S;
+ SmallVector<std::pair<LineLocation, ProfileFuncRef>, 10> S;
// Use C++ linkage name if possible.
const DILocation *PrevDIL = DIL;
@@ -494,7 +489,8 @@ ContextTrieNode *SampleContextTracker::getContextFor(const DILocation *DIL) {
if (Name.empty())
Name = PrevDIL->getScope()->getSubprogram()->getName();
S.push_back(
- std::make_pair(FunctionSamples::getCallSiteIdentifier(DIL), Name));
+ std::make_pair(FunctionSamples::getCallSiteIdentifier(DIL),
+ getRepInFormat(Name)));
PrevDIL = DIL;
}
@@ -503,24 +499,14 @@ ContextTrieNode *SampleContextTracker::getContextFor(const DILocation *DIL) {
StringRef RootName = PrevDIL->getScope()->getSubprogram()->getLinkageName();
if (RootName.empty())
RootName = PrevDIL->getScope()->getSubprogram()->getName();
- S.push_back(std::make_pair(LineLocation(0, 0), RootName));
-
- // Convert real function names to MD5 names, if the input profile is
- // MD5-based.
- std::list<std::string> MD5Names;
- if (FunctionSamples::UseMD5) {
- for (auto &Location : S) {
- MD5Names.emplace_back();
- getRepInFormat(Location.second, FunctionSamples::UseMD5, MD5Names.back());
- Location.second = MD5Names.back();
- }
- }
+ S.push_back(std::make_pair(LineLocation(0, 0),
+ getRepInFormat(RootName)));
ContextTrieNode *ContextNode = &RootContext;
int I = S.size();
while (--I >= 0 && ContextNode) {
LineLocation &CallSite = S[I].first;
- StringRef CalleeName = S[I].second;
+ ProfileFuncRef CalleeName = S[I].second;
ContextNode = ContextNode->getChildContext(CallSite, CalleeName);
}
@@ -553,12 +539,14 @@ SampleContextTracker::getOrCreateContextPath(const SampleContext &Context,
return ContextNode;
}
-ContextTrieNode *SampleContextTracker::getTopLevelContextNode(StringRef FName) {
+ContextTrieNode *
+SampleContextTracker::getTopLevelContextNode(ProfileFuncRef FName) {
assert(!FName.empty() && "Top level node query must provide valid name");
return RootContext.getChildContext(LineLocation(0, 0), FName);
}
-ContextTrieNode &SampleContextTracker::addTopLevelContextNode(StringRef FName) {
+ContextTrieNode &
+SampleContextTracker::addTopLevelContextNode(ProfileFuncRef FName) {
assert(!getTopLevelContextNode(FName) && "Node to add must not exist");
return *RootContext.getOrCreateChildContext(LineLocation(0, 0), FName);
}
diff --git a/llvm/lib/Transforms/IPO/SampleProfile.cpp b/llvm/lib/Transforms/IPO/SampleProfile.cpp
index 67a2e167b6bf7dd..e89913e2795349d 100644
--- a/llvm/lib/Transforms/IPO/SampleProfile.cpp
+++ b/llvm/lib/Transforms/IPO/SampleProfile.cpp
@@ -424,7 +424,7 @@ struct CandidateComparer {
return LCS->getBodySamples().size() > RCS->getBodySamples().size();
// Tie breaker using GUID so we have stable/deterministic inlining order
- return LCS->getGUID(LCS->getName()) < RCS->getGUID(RCS->getName());
+ return LCS->getGUID() < RCS->getGUID();
}
};
@@ -467,7 +467,7 @@ class SampleProfileMatcher {
private:
FunctionSamples *getFlattenedSamplesFor(const Function &F) {
StringRef CanonFName = FunctionSamples::getCanonicalFnName(F);
- auto It = FlattenedProfiles.find(CanonFName);
+ auto It = FlattenedProfiles.find(ProfileFuncRef(CanonFName));
if (It != FlattenedProfiles.end())
return &It->second;
return nullptr;
@@ -475,18 +475,21 @@ class SampleProfileMatcher {
void runOnFunction(const Function &F);
void findIRAnchors(const Function &F,
std::map<LineLocation, StringRef> &IRAnchors);
- void findProfileAnchors(const FunctionSamples &FS,
- std::map<LineLocation, StringSet<>> &ProfileAnchors);
+ void findProfileAnchors(
+ const FunctionSamples &FS,
+ std::map<LineLocation, std::unordered_set<ProfileFuncRef>>
+ &ProfileAnchors);
void countMismatchedSamples(const FunctionSamples &FS);
void countProfileMismatches(
const Function &F, const FunctionSamples &FS,
const std::map<LineLocation, StringRef> &IRAnchors,
- const std::map<LineLocation, StringSet<>> &ProfileAnchors);
+ const std::map<LineLocation, std::unordered_set<ProfileFuncRef>>
+ &ProfileAnchors);
void countProfileCallsiteMismatches(
const FunctionSamples &FS,
const std::map<LineLocation, StringRef> &IRAnchors,
- const std::map<LineLocation, StringSet<>> &ProfileAnchors,
-
+ const std::map<LineLocation, std::unordered_set<ProfileFuncRef>>
+ &ProfileAnchors,
uint64_t &FuncMismatchedCallsites, uint64_t &FuncProfiledCallsites);
LocToLocMap &getIRToProfileLocationMap(const Function &F) {
auto Ret = FuncMappings.try_emplace(
@@ -497,7 +500,8 @@ class SampleProfileMatcher {
void distributeIRToProfileLocationMap(FunctionSamples &FS);
void runStaleProfileMatching(
const Function &F, const std::map<LineLocation, StringRef> &IRAnchors,
- const std::map<LineLocation, StringSet<>> &ProfileAnchors,
+ const std::map<LineLocation, std::unordered_set<ProfileFuncRef>>
+ &ProfileAnchors,
LocToLocMap &IRToProfileLocationMap);
};
@@ -615,7 +619,7 @@ class SampleProfileLoader final : public SampleProfileLoaderBaseImpl<Function> {
// All the Names used in FunctionSamples including outline function
// names, inline instance names and call target names.
- StringSet<> NamesInProfile;
+ std::unordered_set<ProfileFuncRef> NamesInProfile;
// For symbol in profile symbol list, whether to regard their profiles
// to be accurate. It is mainly decided by existance of profile symbol
@@ -760,8 +764,7 @@ SampleProfileLoader::findIndirectCallFunctionSamples(
assert(L && R && "Expect non-null FunctionSamples");
if (L->getHeadSamplesEstimate() != R->getHeadSamplesEstimate())
return L->getHeadSamplesEstimate() > R->getHeadSamplesEstimate();
- return FunctionSamples::getGUID(L->getName()) <
- FunctionSamples::getGUID(R->getName());
+ return L->getGUID() < R->getGUID();
};
if (FunctionSamples::ProfileIsCS) {
@@ -1081,7 +1084,7 @@ void SampleProfileLoader::findExternalInlineCandidate(
// just add the direct GUID and move on
if (!Samples) {
InlinedGUIDs.insert(
- FunctionSamples::getGUID(CB->getCalledFunction()->getName()));
+ Function::getGUID(CB->getCalledFunction()->getName()));
return;
}
// Otherwise, drop the threshold to import everything that we can
@@ -1127,17 +1130,17 @@ void SampleProfileLoader::findExternalInlineCandidate(
Function *Func = SymbolMap.lookup(Name);
// Add to the import list only when it's defined out of module.
if (!Func || Func->isDeclaration())
- InlinedGUIDs.insert(FunctionSamples::getGUID(CalleeSample->getName()));
+ InlinedGUIDs.insert(CalleeSample->getGUID());
// Import hot CallTargets, which may not be available in IR because full
// profile annotation cannot be done until backend compilation in ThinLTO.
for (const auto &BS : CalleeSample->getBodySamples())
for (const auto &TS : BS.second.getCallTargets())
- if (TS.getValue() > Threshold) {
- StringRef CalleeName = CalleeSample->getFuncName(TS.getKey());
+ if (TS.second > Threshold) {
+ StringRef CalleeName = CalleeSample->getFuncName(TS.first);
const Function *Callee = SymbolMap.lookup(CalleeName);
if (!Callee || Callee->isDeclaration())
- InlinedGUIDs.insert(FunctionSamples::getGUID(TS.getKey()));
+ InlinedGUIDs.insert(TS.first.getHashCode());
}
// Import hot child context profile associted with callees. Note that this
@@ -1644,7 +1647,7 @@ GetSortedValueDataFromCallTargets(const SampleRecord::CallTargetMap &M) {
SmallVector<InstrProfValueData, 2> R;
for (const auto &I : SampleRecord::SortCallTargets(M)) {
R.emplace_back(
- InstrProfValueData{FunctionSamples::getGUID(I.first), I.second});
+ InstrProfValueData{I.first.getHashCode(), I.second});
}
return R;
}
@@ -1871,7 +1874,8 @@ SampleProfileLoader::buildProfiledCallGraph(Module &M) {
for (Function &F : M) {
if (F.isDeclaration() || !F.hasFnAttribute("use-sample-profile"))
continue;
- ProfiledCG->addProfiledFunction(FunctionSamples::getCanonicalFnName(F));
+ ProfiledCG->addProfiledFunction(
+ getRepInFormat(FunctionSamples::getCanonicalFnName(F)));
}
return ProfiledCG;
@@ -1962,7 +1966,10 @@ SampleProfileLoader::buildFunctionOrder(Module &M, LazyCallGraph &CG) {
Range = *SI;
}
for (auto *Node : Range) {
- Function *F = SymbolMap.lookup(Node->Name);
+ Function *F = SymbolMap.lookup(
+ FunctionSamples::UseMD5
+ ? GUIDToFuncNameMap.lookup(Node->Name.getHashCode())
+ : reinterpret_cast<StringRef &>(Node->Name));
if (F && !F->isDeclaration() && F->hasFnAttribute("use-sample-profile"))
FunctionOrderList.push_back(F);
}
@@ -2177,7 +2184,7 @@ void SampleProfileMatcher::findIRAnchors(
}
void SampleProfileMatcher::countMismatchedSamples(const FunctionSamples &FS) {
- const auto *FuncDesc = ProbeManager->getDesc(FS.getName());
+ const auto *FuncDesc = ProbeManager->getDesc(FS.getGUID());
// Skip the function that is external or renamed.
if (!FuncDesc)
return;
@@ -2194,7 +2201,8 @@ void SampleProfileMatcher::countMismatchedSamples(const FunctionSamples &FS) {
void SampleProfileMatcher::countProfileMismatches(
const Function &F, const FunctionSamples &FS,
const std::map<LineLocation, StringRef> &IRAnchors,
- const std::map<LineLocation, StringSet<>> &ProfileAnchors) {
+ const std::map<LineLocation, std::unordered_set<ProfileFuncRef>>
+ &ProfileAnchors) {
[[maybe_unused]] bool IsFuncHashMismatch = false;
if (FunctionSamples::ProfileIsProbeBased) {
TotalFuncHashSamples += FS.getTotalSamples();
@@ -2228,7 +2236,8 @@ void SampleProfileMatcher::countProfileMismatches(
void SampleProfileMatcher::countProfileCallsiteMismatches(
const FunctionSamples &FS,
const std::map<LineLocation, StringRef> &IRAnchors,
- const std::map<LineLocation, StringSet<>> &ProfileAnchors,
+ const std::map<LineLocation, std::unordered_set<ProfileFuncRef>>
+ &ProfileAnchors,
uint64_t &FuncMismatchedCallsites, uint64_t &FuncProfiledCallsites) {
// Check if there are any callsites in the profile that does not match to any
@@ -2263,7 +2272,7 @@ void SampleProfileMatcher::countProfileCallsiteMismatches(
// reported as mismatching.
if (IRCalleeName == UnknownIndirectCallee)
CallsiteIsMatched = true;
- else if (Callees.size() == 1 && Callees.count(IRCalleeName))
+ else if (Callees.size() == 1 && Callees.count(getRepInFormat(IRCalleeName)))
CallsiteIsMatched = true;
FuncProfiledCallsites++;
@@ -2275,9 +2284,8 @@ void SampleProfileMatcher::countProfileCallsiteMismatches(
}
}
-void SampleProfileMatcher::findProfileAnchors(
- const FunctionSamples &FS,
- std::map<LineLocation, StringSet<>> &ProfileAnchors) {
+void SampleProfileMatcher::findProfileAnchors(const FunctionSamples &FS,
+ std::map<LineLocation, std::unordered_set<ProfileFuncRef>> &ProfileAnchors) {
auto isInvalidLineOffset = [](uint32_t LineOffset) {
return LineOffset & 0x8000;
};
@@ -2287,8 +2295,9 @@ void SampleProfileMatcher::findProfileAnchors(
if (isInvalidLineOffset(Loc.LineOffset))
continue;
for (const auto &I : I.second.getCallTargets()) {
- auto Ret = ProfileAnchors.try_emplace(Loc, StringSet<>());
- Ret.first->second.insert(I.first());
+ auto Ret = ProfileAnchors.try_emplace(Loc,
+ std::unordered_set<ProfileFuncRef>());
+ Ret.first->second.insert(I.first);
}
}
@@ -2298,7 +2307,8 @@ void SampleProfileMatcher::findProfileAnchors(
continue;
const auto &CalleeMap = I.second;
for (const auto &I : CalleeMap) {
- auto Ret = ProfileAnchors.try_emplace(Loc, StringSet<>());
+ auto Ret = ProfileAnchors.try_emplace(Loc,
+ std::unordered_set<ProfileFuncRef>());
Ret.first->second.insert(I.first);
}
}
@@ -2322,21 +2332,24 @@ void SampleProfileMatcher::findProfileAnchors(
// [1, 2, 3(foo), 4, 7, 8(bar), 9]
// The output mapping: [2->3, 3->4, 5->7, 6->8, 7->9].
void SampleProfileMatcher::runStaleProfileMatching(
- const Function &F, const std::map<LineLocation, StringRef> &IRAnchors,
- const std::map<LineLocation, StringSet<>> &ProfileAnchors,
+ const Function &F,
+ const std::map<LineLocation, StringRef> &IRAnchors,
+ const std::map<LineLocation, std::unordered_set<ProfileFuncRef>>
+ &ProfileAnchors,
LocToLocMap &IRToProfileLocationMap) {
LLVM_DEBUG(dbgs() << "Run stale profile matching for " << F.getName()
<< "\n");
assert(IRToProfileLocationMap.empty() &&
"Run stale profile matching only once per function");
- StringMap<std::set<LineLocation>> CalleeToCallsitesMap;
+ std::unordered_map<ProfileFuncRef, std::set<LineLocation>>
+ CalleeToCallsitesMap;
for (const auto &I : ProfileAnchors) {
const auto &Loc = I.first;
const auto &Callees = I.second;
// Filter out possible indirect calls, use direct callee name as anchor.
if (Callees.size() == 1) {
- StringRef CalleeName = Callees.begin()->first();
+ ProfileFuncRef CalleeName = *Callees.begin();
const auto &Candidates = CalleeToCallsitesMap.try_emplace(
CalleeName, std::set<LineLocation>());
Candidates.first->second.insert(Loc);
@@ -2355,11 +2368,12 @@ void SampleProfileMatcher::runStaleProfileMatching(
for (const auto &IR : IRAnchors) {
const auto &Loc = IR.first;
- StringRef CalleeName = IR.second;
+ auto CalleeName = IR.second;
bool IsMatchedAnchor = false;
// Match the anchor location in lexical order.
if (!CalleeName.empty()) {
- auto CandidateAnchors = CalleeToCallsitesMap.find(CalleeName);
+ auto CandidateAnchors = CalleeToCallsitesMap.find(
+ getRepInFormat(CalleeName));
if (CandidateAnchors != CalleeToCallsitesMap.end() &&
!CandidateAnchors->second.empty()) {
auto CI = CandidateAnchors->second.begin();
@@ -2420,7 +2434,7 @@ void SampleProfileMatcher::runOnFunction(const Function &F) {
findIRAnchors(F, IRAnchors);
// Anchors for profile. It's a map from callsite location to a set of callee
// name.
- std::map<LineLocation, StringSet<>> ProfileAnchors;
+ std::map<LineLocation, std::unordered_set<ProfileFuncRef>> ProfileAnchors;
findProfileAnchors(*FSFlattened, ProfileAnchors);
// Detect profile mismatch for profile staleness metrics report.
@@ -2499,7 +2513,7 @@ void SampleProfileMatcher::runOnModule() {
void SampleProfileMatcher::distributeIRToProfileLocationMap(
FunctionSamples &FS) {
- const auto ProfileMappings = FuncMappings.find(FS.getName());
+ const auto ProfileMappings = FuncMappings.find(FS.getFuncName());
if (ProfileMappings != FuncMappings.end()) {
FS.setIRToProfileLocationMap(&(ProfileMappings->second));
}
@@ -2625,7 +2639,7 @@ bool SampleProfileLoader::runOnFunction(Function &F, ModuleAnalysisManager *AM)
// but not cold accumulatively...), so the outline function showing up as
// cold in sampled binary will actually not be cold after current build.
StringRef CanonName = FunctionSamples::getCanonicalFnName(F);
- if (NamesInProfile.count(CanonName))
+ if (NamesInProfile.count(getRepInFormat(CanonName)))
initialEntryCount = -1;
}
diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp
index fdb2c1405f1237f..32718482296f46b 100644
--- a/llvm/tools/llvm-profdata/llvm-profdata.cpp
+++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp
@@ -199,6 +199,12 @@ class SymbolRemapper {
StringRef New = RemappingTable.lookup(Name);
return New.empty() ? Name : New;
}
+
+ ProfileFuncRef operator()(ProfileFuncRef Name) {
+ std::string Buffer;
+ StringRef New = RemappingTable.lookup(Name.stringRef(Buffer));
+ return New.empty() ? Name : ProfileFuncRef(New);
+ }
};
}
@@ -716,7 +722,8 @@ adjustInstrProfile(std::unique_ptr<WriterContext> &WC,
auto BuildMaxSampleMapImpl = [&](const FunctionSamples &FS,
const StringRef &RootName,
auto &BuildImpl) -> void {
- const StringRef &Name = FS.getName();
+ std::string Buffer;
+ const StringRef &Name = FS.getName().stringRef(Buffer);
const StringRef *NewRootName = &RootName;
uint64_t EntrySample = FS.getHeadSamplesEstimate();
uint64_t MaxBodySample = FS.getMaxCountInside(/* SkipCallSite*/ true);
@@ -770,7 +777,8 @@ adjustInstrProfile(std::unique_ptr<WriterContext> &WC,
for (auto &PD : Reader->getProfiles()) {
sampleprof::FunctionSamples &FS = PD.second;
- BuildMaxSampleMap(FS, FS.getName());
+ std::string Buffer;
+ BuildMaxSampleMap(FS, FS.getName().stringRef(Buffer));
}
ProfileSummary InstrPS = *IPBuilder.getSummary();
@@ -896,7 +904,7 @@ remapSamples(const sampleprof::FunctionSamples &Samples,
for (const auto &Target : BodySample.second.getCallTargets()) {
Result.addCalledTargetSamples(BodySample.first.LineOffset,
MaskedDiscriminator,
- Remapper(Target.first()), Target.second);
+ Remapper(Target.first), Target.second);
}
}
for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) {
@@ -906,7 +914,7 @@ remapSamples(const sampleprof::FunctionSamples &Samples,
sampleprof::FunctionSamples Remapped =
remapSamples(Callsite.second, Remapper, Error);
MergeResult(Error,
- Target[std::string(Remapped.getName())].merge(Remapped));
+ Target[Remapped.getName()].merge(Remapped));
}
}
return Result;
diff --git a/llvm/tools/llvm-profgen/CSPreInliner.cpp b/llvm/tools/llvm-profgen/CSPreInliner.cpp
index 9421118a3bb1b50..01796f41d188a02 100644
--- a/llvm/tools/llvm-profgen/CSPreInliner.cpp
+++ b/llvm/tools/llvm-profgen/CSPreInliner.cpp
@@ -80,8 +80,8 @@ CSPreInliner::CSPreInliner(SampleContextTracker &Tracker,
ProfileInlineLimitMax = 50000;
}
-std::vector<StringRef> CSPreInliner::buildTopDownOrder() {
- std::vector<StringRef> Order;
+std::vector<ProfileFuncRef> CSPreInliner::buildTopDownOrder() {
+ std::vector<ProfileFuncRef> Order;
// Trim cold edges to get a more stable call graph. This allows for a more
// stable top-down order which in turns helps the stablity of the generated
// profile from run to run.
@@ -202,7 +202,7 @@ bool CSPreInliner::shouldInline(ProfiledInlineCandidate &Candidate) {
return (Candidate.SizeCost < SampleThreshold);
}
-void CSPreInliner::processFunction(const StringRef Name) {
+void CSPreInliner::processFunction(const ProfileFuncRef Name) {
FunctionSamples *FSamples = ContextTracker.getBaseSamplesFor(Name);
if (!FSamples)
return;
@@ -303,7 +303,7 @@ void CSPreInliner::run() {
// It also helps better compress context profile to control profile
// size, as we now only need context profile for functions going to
// be inlined.
- for (StringRef FuncName : buildTopDownOrder()) {
+ for (ProfileFuncRef FuncName : buildTopDownOrder()) {
processFunction(FuncName);
}
diff --git a/llvm/tools/llvm-profgen/CSPreInliner.h b/llvm/tools/llvm-profgen/CSPreInliner.h
index 4d848aafdab914d..85e297cddfe8544 100644
--- a/llvm/tools/llvm-profgen/CSPreInliner.h
+++ b/llvm/tools/llvm-profgen/CSPreInliner.h
@@ -57,8 +57,7 @@ struct ProfiledCandidateComparer {
// Tie breaker using GUID so we have stable/deterministic inlining order
assert(LHS.CalleeSamples && RHS.CalleeSamples &&
"Expect non-null FunctionSamples");
- return LHS.CalleeSamples->getGUID(LHS.CalleeSamples->getName()) <
- RHS.CalleeSamples->getGUID(RHS.CalleeSamples->getName());
+ return LHS.CalleeSamples->getGUID() < RHS.CalleeSamples->getGUID();
}
};
@@ -81,8 +80,8 @@ class CSPreInliner {
private:
bool getInlineCandidates(ProfiledCandidateQueue &CQueue,
const FunctionSamples *FCallerContextSamples);
- std::vector<StringRef> buildTopDownOrder();
- void processFunction(StringRef Name);
+ std::vector<ProfileFuncRef> buildTopDownOrder();
+ void processFunction(ProfileFuncRef Name);
bool shouldInline(ProfiledInlineCandidate &Candidate);
uint32_t getFuncSize(const ContextTrieNode *ContextNode);
bool UseContextCost;
diff --git a/llvm/tools/llvm-profgen/ProfileGenerator.cpp b/llvm/tools/llvm-profgen/ProfileGenerator.cpp
index d307ab465676296..f5f2bc0d800e83a 100644
--- a/llvm/tools/llvm-profgen/ProfileGenerator.cpp
+++ b/llvm/tools/llvm-profgen/ProfileGenerator.cpp
@@ -586,20 +586,21 @@ void ProfileGenerator::populateBoundarySamplesWithProbesForAllFunctions(
FunctionProfile.addCalledTargetSamples(
FrameVec.back().Location.LineOffset,
FrameVec.back().Location.Discriminator,
- CalleeName, Count);
+ ProfileFuncRef(CalleeName), Count);
}
}
}
FunctionSamples &ProfileGenerator::getLeafProfileAndAddTotalSamples(
const SampleContextFrameVector &FrameVec, uint64_t Count) {
+ std::string Buffer;
// Get top level profile
FunctionSamples *FunctionProfile =
- &getTopLevelFunctionProfile(FrameVec[0].FuncName);
+ &getTopLevelFunctionProfile(FrameVec[0].FuncName.stringRef(Buffer));
FunctionProfile->addTotalSamples(Count);
if (Binary->usePseudoProbes()) {
const auto *FuncDesc = Binary->getFuncDescForGUID(
- Function::getGUID(FunctionProfile->getName()));
+ FunctionProfile->getName().getHashCode());
FunctionProfile->setFunctionHash(FuncDesc->FuncHash);
}
@@ -619,7 +620,7 @@ FunctionSamples &ProfileGenerator::getLeafProfileAndAddTotalSamples(
FunctionProfile->addTotalSamples(Count);
if (Binary->usePseudoProbes()) {
const auto *FuncDesc = Binary->getFuncDescForGUID(
- Function::getGUID(FunctionProfile->getName()));
+ FunctionProfile->getName().getHashCode());
FunctionProfile->setFunctionHash(FuncDesc->FuncHash);
}
}
@@ -716,7 +717,7 @@ void ProfileGenerator::populateBoundarySamplesForAllFunctions(
FunctionProfile.addCalledTargetSamples(
FrameVec.back().Location.LineOffset,
getBaseDiscriminator(FrameVec.back().Location.Discriminator),
- CalleeName, Count);
+ ProfileFuncRef(CalleeName), Count);
}
// Add head samples for callee.
FunctionSamples &CalleeProfile = getTopLevelFunctionProfile(CalleeName);
@@ -899,7 +900,8 @@ void CSProfileGenerator::populateBoundarySamplesForFunction(
if (LeafLoc) {
CallerNode->getFunctionSamples()->addCalledTargetSamples(
LeafLoc->Location.LineOffset,
- getBaseDiscriminator(LeafLoc->Location.Discriminator), CalleeName,
+ getBaseDiscriminator(LeafLoc->Location.Discriminator),
+ ProfileFuncRef(CalleeName),
Count);
// Record head sample for called target(callee)
CalleeCallSite = LeafLoc->Location;
@@ -907,7 +909,8 @@ void CSProfileGenerator::populateBoundarySamplesForFunction(
}
ContextTrieNode *CalleeNode =
- CallerNode->getOrCreateChildContext(CalleeCallSite, CalleeName);
+ CallerNode->getOrCreateChildContext(CalleeCallSite,
+ ProfileFuncRef(CalleeName));
FunctionSamples *CalleeProfile = getOrCreateFunctionSamples(CalleeNode);
CalleeProfile->addHeadSamples(Count);
}
@@ -1212,7 +1215,7 @@ void CSProfileGenerator::populateBoundarySamplesWithProbes(
continue;
FunctionProfile.addCalledTargetSamples(CallProbe->getIndex(),
CallProbe->getDiscriminator(),
- CalleeName, Count);
+ ProfileFuncRef(CalleeName), Count);
}
}
diff --git a/llvm/tools/llvm-profgen/ProfiledBinary.cpp b/llvm/tools/llvm-profgen/ProfiledBinary.cpp
index 5fb51d3ac9eba78..95cb1f91bd68fe0 100644
--- a/llvm/tools/llvm-profgen/ProfiledBinary.cpp
+++ b/llvm/tools/llvm-profgen/ProfiledBinary.cpp
@@ -77,7 +77,7 @@ void BinarySizeContextTracker::addInstructionForContext(
ContextTrieNode *CurNode = &RootContext;
bool IsLeaf = true;
for (const auto &Callsite : reverse(Context)) {
- StringRef CallerName = Callsite.FuncName;
+ ProfileFuncRef CallerName = Callsite.FuncName;
LineLocation CallsiteLoc = IsLeaf ? LineLocation(0, 0) : Callsite.Location;
CurNode = CurNode->getOrCreateChildContext(CallsiteLoc, CallerName);
IsLeaf = false;
@@ -145,7 +145,8 @@ void BinarySizeContextTracker::trackInlineesOptimizedAway(
StringRef CallerName = ProbeFrame.first;
LineLocation CallsiteLoc(ProbeFrame.second, 0);
SizeContext =
- SizeContext->getOrCreateChildContext(CallsiteLoc, CallerName);
+ SizeContext->getOrCreateChildContext(CallsiteLoc,
+ ProfileFuncRef(CallerName));
}
// Add 0 size to make known.
SizeContext->addFunctionSize(0);
@@ -831,6 +832,14 @@ void ProfiledBinary::loadSymbolsFromDWARF(ObjectFile &Obj) {
if (BinaryFunctions.empty())
WithColor::warning() << "Loading of DWARF info completed, but no binary "
"functions have been retrieved.\n";
+
+
+ // Populate the hash binary function map for MD5 function name lookup. This
+ // is done after BinaryFunctions are finalized.
+ for (auto &BinaryFunction : BinaryFunctions) {
+ HashBinaryFunctions[MD5Hash(StringRef(BinaryFunction.first))] =
+ &BinaryFunction.second;
+ }
}
void ProfiledBinary::populateSymbolListFromDWARF(
@@ -882,7 +891,7 @@ SampleContextFrameVector ProfiledBinary::symbolize(const InstructionPointer &IP,
LineLocation Line(LineOffset, Discriminator);
auto It = NameStrings.insert(FunctionName.str());
- CallStack.emplace_back(*It.first, Line);
+ CallStack.emplace_back(ProfileFuncRef(*It.first), Line);
}
return CallStack;
diff --git a/llvm/tools/llvm-profgen/ProfiledBinary.h b/llvm/tools/llvm-profgen/ProfiledBinary.h
index a6d78c661cc1c31..298ec2781b38727 100644
--- a/llvm/tools/llvm-profgen/ProfiledBinary.h
+++ b/llvm/tools/llvm-profgen/ProfiledBinary.h
@@ -220,6 +220,10 @@ class ProfiledBinary {
// A map of mapping function name to BinaryFunction info.
std::unordered_map<std::string, BinaryFunction> BinaryFunctions;
+ // Lookup BinaryFunctions using the function name's MD5 hash. Needed if the
+ // profile is using MD5.
+ std::unordered_map<uint64_t, BinaryFunction *> HashBinaryFunctions;
+
// A list of binary functions that have samples.
std::unordered_set<const BinaryFunction *> ProfiledFunctions;
@@ -477,11 +481,17 @@ class ProfiledBinary {
ProfiledFunctions = Funcs;
}
- BinaryFunction *getBinaryFunction(StringRef FName) {
- auto I = BinaryFunctions.find(FName.str());
- if (I == BinaryFunctions.end())
+ BinaryFunction *getBinaryFunction(ProfileFuncRef FName) {
+ if (FName.isStringRef()) {
+ auto I = BinaryFunctions.find(FName.str());
+ if (I == BinaryFunctions.end())
+ return nullptr;
+ return &I->second;
+ }
+ auto I = HashBinaryFunctions.find(FName.getHashCode());
+ if (I == HashBinaryFunctions.end())
return nullptr;
- return &I->second;
+ return I->second;
}
uint32_t getFuncSizeForContext(const ContextTrieNode *ContextNode) {
@@ -556,7 +566,7 @@ class ProfiledBinary {
InlineContextStack.clear();
continue;
}
- InlineContextStack.emplace_back(Callsite.first,
+ InlineContextStack.emplace_back(ProfileFuncRef(Callsite.first),
LineLocation(Callsite.second, 0));
}
}
diff --git a/llvm/unittests/ProfileData/SampleProfTest.cpp b/llvm/unittests/ProfileData/SampleProfTest.cpp
index e01025440e4a56a..a13869d901bc2eb 100644
--- a/llvm/unittests/ProfileData/SampleProfTest.cpp
+++ b/llvm/unittests/ProfileData/SampleProfTest.cpp
@@ -145,7 +145,7 @@ struct SampleProfTest : ::testing::Test {
StringRef FooName("_Z3fooi");
FunctionSamples FooSamples;
- FooSamples.setName(FooName);
+ FooSamples.setName(ProfileFuncRef(FooName));
FooSamples.addTotalSamples(7711);
FooSamples.addHeadSamples(610);
FooSamples.addBodySamples(1, 0, 610);
@@ -155,43 +155,43 @@ struct SampleProfTest : ::testing::Test {
FooSamples.addBodySamples(10, 0, 605);
// Add inline instance with name "_Z3gooi".
- StringRef GooName("_Z3gooi");
+ ProfileFuncRef GooName(StringRef("_Z3gooi"));
auto &GooSamples =
- FooSamples.functionSamplesAt(LineLocation(7, 0))[GooName.str()];
+ FooSamples.functionSamplesAt(LineLocation(7, 0))[GooName];
GooSamples.setName(GooName);
GooSamples.addTotalSamples(502);
GooSamples.addBodySamples(3, 0, 502);
// Add inline instance with name "_Z3hooi".
- StringRef HooName("_Z3hooi");
+ ProfileFuncRef HooName(StringRef("_Z3hooi"));
auto &HooSamples =
- GooSamples.functionSamplesAt(LineLocation(9, 0))[HooName.str()];
+ GooSamples.functionSamplesAt(LineLocation(9, 0))[HooName];
HooSamples.setName(HooName);
HooSamples.addTotalSamples(317);
HooSamples.addBodySamples(4, 0, 317);
StringRef BarName("_Z3bari");
FunctionSamples BarSamples;
- BarSamples.setName(BarName);
+ BarSamples.setName(ProfileFuncRef(BarName));
BarSamples.addTotalSamples(20301);
BarSamples.addHeadSamples(1437);
BarSamples.addBodySamples(1, 0, 1437);
// Test how reader/writer handles unmangled names.
StringRef MconstructName("_M_construct<char *>");
StringRef StringviewName("string_view<std::allocator<char> >");
- BarSamples.addCalledTargetSamples(1, 0, MconstructName, 1000);
- BarSamples.addCalledTargetSamples(1, 0, StringviewName, 437);
+ BarSamples.addCalledTargetSamples(1, 0, ProfileFuncRef(MconstructName), 1000);
+ BarSamples.addCalledTargetSamples(1, 0, ProfileFuncRef(StringviewName), 437);
StringRef BazName("_Z3bazi");
FunctionSamples BazSamples;
- BazSamples.setName(BazName);
+ BazSamples.setName(ProfileFuncRef(BazName));
BazSamples.addTotalSamples(12557);
BazSamples.addHeadSamples(1257);
BazSamples.addBodySamples(1, 0, 12557);
StringRef BooName("_Z3booi");
FunctionSamples BooSamples;
- BooSamples.setName(BooName);
+ BooSamples.setName(ProfileFuncRef(BooName));
BooSamples.addTotalSamples(1232);
BooSamples.addHeadSamples(1);
BooSamples.addBodySamples(1, 0, 1232);
@@ -210,8 +210,8 @@ struct SampleProfTest : ::testing::Test {
if (Remap) {
FooName = "_Z4fauxi";
BarName = "_Z3barl";
- GooName = "_Z3gool";
- HooName = "_Z3hool";
+ GooName = ProfileFuncRef(StringRef("_Z3gool"));
+ HooName = ProfileFuncRef(StringRef("_Z3hool"));
}
M.getOrInsertFunction(FooName, fn_type);
@@ -245,16 +245,17 @@ struct SampleProfTest : ::testing::Test {
FunctionSamples *ReadFooSamples = Reader->getSamplesFor(FooName);
ASSERT_TRUE(ReadFooSamples != nullptr);
if (!UseMD5) {
- ASSERT_EQ("_Z3fooi", ReadFooSamples->getName());
+ ASSERT_EQ("_Z3fooi", ReadFooSamples->getName().str());
}
ASSERT_EQ(7711u, ReadFooSamples->getTotalSamples());
ASSERT_EQ(610u, ReadFooSamples->getHeadSamples());
+ std::string Buffer;
// Try to find a FunctionSamples with GooName at given callsites containing
// inline instance for GooName. Test the correct FunctionSamples can be
// found with Remapper support.
const FunctionSamples *ReadGooSamples =
- ReadFooSamples->findFunctionSamplesAt(LineLocation(7, 0), GooName,
+ ReadFooSamples->findFunctionSamplesAt(LineLocation(7, 0), GooName.stringRef(Buffer),
Reader->getRemapper());
ASSERT_TRUE(ReadGooSamples != nullptr);
ASSERT_EQ(502u, ReadGooSamples->getTotalSamples());
@@ -263,7 +264,7 @@ struct SampleProfTest : ::testing::Test {
// no inline instance for GooName. Test no FunctionSamples will be
// found with Remapper support.
const FunctionSamples *ReadGooSamplesAgain =
- ReadFooSamples->findFunctionSamplesAt(LineLocation(9, 0), GooName,
+ ReadFooSamples->findFunctionSamplesAt(LineLocation(9, 0), GooName.stringRef(Buffer),
Reader->getRemapper());
ASSERT_TRUE(ReadGooSamplesAgain == nullptr);
@@ -272,7 +273,7 @@ struct SampleProfTest : ::testing::Test {
// inline instance for HooName. Test the correct FunctionSamples can be
// found with Remapper support.
const FunctionSamples *ReadHooSamples =
- ReadGooSamples->findFunctionSamplesAt(LineLocation(9, 0), HooName,
+ ReadGooSamples->findFunctionSamplesAt(LineLocation(9, 0), HooName.stringRef(Buffer),
Reader->getRemapper());
ASSERT_TRUE(ReadHooSamples != nullptr);
ASSERT_EQ(317u, ReadHooSamples->getTotalSamples());
@@ -280,7 +281,7 @@ struct SampleProfTest : ::testing::Test {
FunctionSamples *ReadBarSamples = Reader->getSamplesFor(BarName);
ASSERT_TRUE(ReadBarSamples != nullptr);
if (!UseMD5) {
- ASSERT_EQ("_Z3bari", ReadBarSamples->getName());
+ ASSERT_EQ("_Z3bari", ReadBarSamples->getName().str());
}
ASSERT_EQ(20301u, ReadBarSamples->getTotalSamples());
ASSERT_EQ(1437u, ReadBarSamples->getHeadSamples());
@@ -305,12 +306,8 @@ struct SampleProfTest : ::testing::Test {
ASSERT_TRUE(ReadBooSamples != nullptr);
ASSERT_EQ(1232u, ReadBooSamples->getTotalSamples());
- std::string MconstructGUID;
- StringRef MconstructRep =
- getRepInFormat(MconstructName, UseMD5, MconstructGUID);
- std::string StringviewGUID;
- StringRef StringviewRep =
- getRepInFormat(StringviewName, UseMD5, StringviewGUID);
+ ProfileFuncRef MconstructRep = getRepInFormat(MconstructName, UseMD5);
+ ProfileFuncRef StringviewRep = getRepInFormat(StringviewName, UseMD5);
ASSERT_EQ(1000u, CTMap.get()[MconstructRep]);
ASSERT_EQ(437u, CTMap.get()[StringviewRep]);
@@ -333,7 +330,7 @@ struct SampleProfTest : ::testing::Test {
uint64_t TotalSamples, uint64_t HeadSamples) {
StringRef Name(Fname);
FunctionSamples FcnSamples;
- FcnSamples.setName(Name);
+ FcnSamples.setName(ProfileFuncRef(Name));
FcnSamples.addTotalSamples(TotalSamples);
FcnSamples.addHeadSamples(HeadSamples);
FcnSamples.addBodySamples(1, 0, HeadSamples);
>From 11ea8142388cdf768f536142715e43ab830cef97 Mon Sep 17 00:00:00 2001
From: William Huang <williamjhuang at google.com>
Date: Wed, 20 Sep 2023 20:30:30 +0000
Subject: [PATCH 2/9] Change ProfileFuncRef member name to be more descriptive
---
.../include/llvm/ProfileData/ProfileFuncRef.h | 45 ++++++++++---------
1 file changed, 23 insertions(+), 22 deletions(-)
diff --git a/llvm/include/llvm/ProfileData/ProfileFuncRef.h b/llvm/include/llvm/ProfileData/ProfileFuncRef.h
index 60be6232372b8c6..37a12bf27d745d3 100644
--- a/llvm/include/llvm/ProfileData/ProfileFuncRef.h
+++ b/llvm/include/llvm/ProfileData/ProfileFuncRef.h
@@ -40,7 +40,7 @@ class ProfileFuncRef {
const char *Data = nullptr;
/// Use uint64_t instead of size_t so that it can also hold a MD5 value.
- uint64_t Length = 0;
+ uint64_t LengthOrHashCode = 0;
/// Extension to memcmp to handle hash code representation. If both are hash
/// values, Lhs and Rhs are both null, function returns 0 (and needs an extra
@@ -61,11 +61,11 @@ class ProfileFuncRef {
/// Constructor from a StringRef.
explicit ProfileFuncRef(StringRef Str)
- : Data(Str.data()), Length(Str.size()) {}
+ : Data(Str.data()), LengthOrHashCode(Str.size()) {}
/// Constructor from a hash code.
explicit ProfileFuncRef(uint64_t HashCode)
- : Data(nullptr), Length(HashCode) {
+ : Data(nullptr), LengthOrHashCode(HashCode) {
assert(HashCode != 0);
}
@@ -74,10 +74,10 @@ class ProfileFuncRef {
/// if so, convert the numerical string to a hash code first. We assume that
/// no function name (from a profile) can be a pure number.
explicit ProfileFuncRef(const std::string &Str)
- : Data(Str.data()), Length(Str.size()) {
+ : Data(Str.data()), LengthOrHashCode(Str.size()) {
// Only need to check for base 10 digits, fail faster if otherwise.
if (Str.length() > 0 && isdigit(Str[0]) &&
- !StringRef(Str).getAsInteger(10, Length))
+ !StringRef(Str).getAsInteger(10, LengthOrHashCode))
Data = nullptr;
}
@@ -86,8 +86,8 @@ class ProfileFuncRef {
/// sufficient. A hash code ProfileFuncName is considered not equal to a
/// StringRef ProfileFuncName regardless of actual contents.
bool equals(const ProfileFuncRef &Other) const {
- return Length == Other.Length &&
- compareMemory(Data, Other.Data, Length) == 0;
+ return LengthOrHashCode == Other.LengthOrHashCode &&
+ compareMemory(Data, Other.Data, LengthOrHashCode) == 0;
}
/// Total order comparison. If both ProfileFuncName are StringRef, this is the
@@ -95,20 +95,21 @@ class ProfileFuncRef {
/// greater than the hash code ProfileFuncName. Otherwise this is the the
/// same as comparing their int values.
int compare(const ProfileFuncRef &Other) const {
- auto Res = compareMemory(Data, Other.Data, std::min(Length, Other.Length));
+ auto Res = compareMemory(
+ Data, Other.Data, std::min(LengthOrHashCode, Other.LengthOrHashCode));
if (Res != 0)
return Res;
- if (Length == Other.Length)
+ if (LengthOrHashCode == Other.LengthOrHashCode)
return 0;
- return Length < Other.Length ? -1 : 1;
+ return LengthOrHashCode < Other.LengthOrHashCode ? -1 : 1;
}
/// Convert to a string, usually for output purpose.
std::string str() const {
if (Data)
- return std::string(Data, Length);
- if (Length != 0)
- return std::to_string(Length);
+ return std::string(Data, LengthOrHashCode);
+ if (LengthOrHashCode != 0)
+ return std::to_string(LengthOrHashCode);
return std::string();
}
@@ -117,9 +118,9 @@ class ProfileFuncRef {
/// the buffer. If this object represents a StringRef, the buffer is not used.
StringRef stringRef(std::string &Buffer) const {
if (Data)
- return StringRef(Data, Length);
- if (Length != 0) {
- Buffer = std::to_string(Length);
+ return StringRef(Data, LengthOrHashCode);
+ if (LengthOrHashCode != 0) {
+ Buffer = std::to_string(LengthOrHashCode);
return Buffer;
}
return StringRef();
@@ -134,11 +135,11 @@ class ProfileFuncRef {
/// form has the same hash code.
uint64_t getHashCode() const {
if (Data)
- return MD5Hash(StringRef(Data, Length));
- return Length;
+ return MD5Hash(StringRef(Data, LengthOrHashCode));
+ return LengthOrHashCode;
}
- bool empty() const { return Length == 0; }
+ bool empty() const { return LengthOrHashCode == 0; }
/// Check if this object represents a StringRef, or a hash code.
bool isStringRef() const { return Data != nullptr; }
@@ -170,9 +171,9 @@ inline bool operator>=(const ProfileFuncRef &LHS, const ProfileFuncRef &RHS) {
inline raw_ostream &operator<<(raw_ostream &OS, const ProfileFuncRef &Obj) {
if (Obj.Data)
- return OS << StringRef(Obj.Data, Obj.Length);
- if (Obj.Length != 0)
- return OS << Obj.Length;
+ return OS << StringRef(Obj.Data, Obj.LengthOrHashCode);
+ if (Obj.LengthOrHashCode != 0)
+ return OS << Obj.LengthOrHashCode;
return OS;
}
>From 2579bfd4498ed7b846d4cbea399123691ea03ae7 Mon Sep 17 00:00:00 2001
From: William Huang <williamjhuang at google.com>
Date: Thu, 21 Sep 2023 21:33:55 +0000
Subject: [PATCH 3/9] Removed lazy loading of name table in sample profile
reader because now MD5 values are just being memcopied, which is fast.
---
.../include/llvm/ProfileData/ProfileFuncRef.h | 14 +++++-----
.../llvm/ProfileData/SampleProfReader.h | 3 ---
llvm/lib/ProfileData/SampleProfReader.cpp | 26 ++++++-------------
3 files changed, 15 insertions(+), 28 deletions(-)
diff --git a/llvm/include/llvm/ProfileData/ProfileFuncRef.h b/llvm/include/llvm/ProfileData/ProfileFuncRef.h
index 37a12bf27d745d3..2dd69ee3392c955 100644
--- a/llvm/include/llvm/ProfileData/ProfileFuncRef.h
+++ b/llvm/include/llvm/ProfileData/ProfileFuncRef.h
@@ -83,17 +83,17 @@ class ProfileFuncRef {
/// Check for equality. Similar to StringRef::equals, but will also cover for
/// the case where one or both are hash codes. Comparing their int values are
- /// sufficient. A hash code ProfileFuncName is considered not equal to a
- /// StringRef ProfileFuncName regardless of actual contents.
+ /// sufficient. A hash code ProfileFuncRef is considered not equal to a
+ /// StringRef ProfileFuncRef regardless of actual contents.
bool equals(const ProfileFuncRef &Other) const {
return LengthOrHashCode == Other.LengthOrHashCode &&
compareMemory(Data, Other.Data, LengthOrHashCode) == 0;
}
- /// Total order comparison. If both ProfileFuncName are StringRef, this is the
+ /// Total order comparison. If both ProfileFuncRef are StringRef, this is the
/// same as StringRef::compare. If one of them is StringRef, it is considered
- /// greater than the hash code ProfileFuncName. Otherwise this is the the
- /// same as comparing their int values.
+ /// greater than the hash code ProfileFuncRef. Otherwise this is the the same
+ /// as comparing their int values.
int compare(const ProfileFuncRef &Other) const {
auto Res = compareMemory(
Data, Other.Data, std::min(LengthOrHashCode, Other.LengthOrHashCode));
@@ -187,7 +187,7 @@ inline hash_code hash_value(const sampleprof::ProfileFuncRef &Obj) {
} // end namespace sampleprof
-/// Template specialization for ProfileFuncName so that it can be used in LLVM
+/// Template specialization for ProfileFuncRef so that it can be used in LLVM
/// map containers.
template <> struct DenseMapInfo<sampleprof::ProfileFuncRef, void> {
@@ -213,7 +213,7 @@ template <> struct DenseMapInfo<sampleprof::ProfileFuncRef, void> {
namespace std {
-/// Template specialization for ProfileFuncName so that it can be used in STL
+/// Template specialization for ProfileFuncRef so that it can be used in STL
/// containers.
template <> struct hash<llvm::sampleprof::ProfileFuncRef> {
size_t operator()(const llvm::sampleprof::ProfileFuncRef &Val) const {
diff --git a/llvm/include/llvm/ProfileData/SampleProfReader.h b/llvm/include/llvm/ProfileData/SampleProfReader.h
index df30722cb1b5bc3..0f1ad8c4660e7f2 100644
--- a/llvm/include/llvm/ProfileData/SampleProfReader.h
+++ b/llvm/include/llvm/ProfileData/SampleProfReader.h
@@ -654,9 +654,6 @@ class SampleProfileReaderBinary : public SampleProfileReader {
/// Function name table.
std::vector<ProfileFuncRef> NameTable;
- /// The starting address of fixed length MD5 name table section.
- const uint8_t *MD5NameMemStart = nullptr;
-
/// CSNameTable is used to save full context vectors. It is the backing buffer
/// for SampleContextFrames.
std::vector<SampleContextFrameVector> CSNameTable;
diff --git a/llvm/lib/ProfileData/SampleProfReader.cpp b/llvm/lib/ProfileData/SampleProfReader.cpp
index 86306c14a048da4..b539e27f0bd29ea 100644
--- a/llvm/lib/ProfileData/SampleProfReader.cpp
+++ b/llvm/lib/ProfileData/SampleProfReader.cpp
@@ -529,20 +529,9 @@ SampleProfileReaderBinary::readStringFromTable(size_t *RetIdx) {
auto Idx = readStringIndex(NameTable);
if (std::error_code EC = Idx.getError())
return EC;
-
- // If using fixed length MD5, just read directly from the pointer to the name
- // table.
- ProfileFuncRef &SR = NameTable[*Idx];
- if (SR.empty()) {
- assert(MD5NameMemStart);
- using namespace support;
- uint64_t FID = endian::read<uint64_t, little, unaligned>(
- MD5NameMemStart + (*Idx) * sizeof(uint64_t));
- SR = ProfileFuncRef(FID);
- }
if (RetIdx)
*RetIdx = *Idx;
- return SR;
+ return NameTable[*Idx];
}
ErrorOr<SampleContextFrames>
@@ -1118,13 +1107,14 @@ SampleProfileReaderExtBinaryBase::readNameTableSec(bool IsMD5,
if (Data + (*Size) * sizeof(uint64_t) > End)
return sampleprof_error::truncated;
- // Preallocate and initialize NameTable so we can check whether a name
- // index has been read before by checking whether the element in the
- // NameTable is empty, meanwhile readStringIndex can do the boundary
- // check using the size of NameTable.
NameTable.clear();
- NameTable.resize(*Size);
- MD5NameMemStart = Data;
+ NameTable.reserve(*Size);
+ for (size_t I = 0; I < *Size; ++I) {
+ using namespace support;
+ uint64_t FID = endian::read<uint64_t, little, unaligned>(
+ Data + I * sizeof(uint64_t));
+ NameTable.emplace_back(ProfileFuncRef(FID));
+ }
if (!ProfileIsCS)
MD5SampleContextStart = reinterpret_cast<const uint64_t *>(Data);
Data = Data + (*Size) * sizeof(uint64_t);
>From 61bf48716c36e65c8ce95d85474429101c7bb52d Mon Sep 17 00:00:00 2001
From: William Huang <williamjhuang at google.com>
Date: Thu, 21 Sep 2023 22:03:23 +0000
Subject: [PATCH 4/9] In ProfileFuncRef constructor check for StringRef being
MD5. This can come from a profile converted from MD5 format
---
.../include/llvm/ProfileData/ProfileFuncRef.h | 24 ++++++++-----------
llvm/tools/llvm-profgen/ProfiledBinary.cpp | 2 +-
2 files changed, 11 insertions(+), 15 deletions(-)
diff --git a/llvm/include/llvm/ProfileData/ProfileFuncRef.h b/llvm/include/llvm/ProfileData/ProfileFuncRef.h
index 2dd69ee3392c955..a989c6824c19993 100644
--- a/llvm/include/llvm/ProfileData/ProfileFuncRef.h
+++ b/llvm/include/llvm/ProfileData/ProfileFuncRef.h
@@ -59,9 +59,17 @@ class ProfileFuncRef {
public:
ProfileFuncRef() = default;
- /// Constructor from a StringRef.
+ /// Constructor from a StringRef. Check if Str is a number, which is generated
+ /// by converting a MD5 sample profile to a format that does not support MD5,
+ /// and if so, convert the numerical string to a hash code first. We assume
+ /// that no function name (from a profile) can be a pure number.
explicit ProfileFuncRef(StringRef Str)
- : Data(Str.data()), LengthOrHashCode(Str.size()) {}
+ : Data(Str.data()), LengthOrHashCode(Str.size()) {
+ // Only need to check for base 10 digits, fail faster if otherwise.
+ if (Str.size() > 0 && isdigit(Str[0]) &&
+ !Str.getAsInteger(10, LengthOrHashCode))
+ Data = nullptr;
+ }
/// Constructor from a hash code.
explicit ProfileFuncRef(uint64_t HashCode)
@@ -69,18 +77,6 @@ class ProfileFuncRef {
assert(HashCode != 0);
}
- /// Constructor from a string. Check if Str is a number, which is generated by
- /// converting a MD5 sample profile to a format that does not support MD5, and
- /// if so, convert the numerical string to a hash code first. We assume that
- /// no function name (from a profile) can be a pure number.
- explicit ProfileFuncRef(const std::string &Str)
- : Data(Str.data()), LengthOrHashCode(Str.size()) {
- // Only need to check for base 10 digits, fail faster if otherwise.
- if (Str.length() > 0 && isdigit(Str[0]) &&
- !StringRef(Str).getAsInteger(10, LengthOrHashCode))
- Data = nullptr;
- }
-
/// Check for equality. Similar to StringRef::equals, but will also cover for
/// the case where one or both are hash codes. Comparing their int values are
/// sufficient. A hash code ProfileFuncRef is considered not equal to a
diff --git a/llvm/tools/llvm-profgen/ProfiledBinary.cpp b/llvm/tools/llvm-profgen/ProfiledBinary.cpp
index 2ecff78218143d7..00354286131b595 100644
--- a/llvm/tools/llvm-profgen/ProfiledBinary.cpp
+++ b/llvm/tools/llvm-profgen/ProfiledBinary.cpp
@@ -891,7 +891,7 @@ SampleContextFrameVector ProfiledBinary::symbolize(const InstructionPointer &IP,
LineLocation Line(LineOffset, Discriminator);
auto It = NameStrings.insert(FunctionName.str());
- CallStack.emplace_back(ProfileFuncRef(*It.first), Line);
+ CallStack.emplace_back(ProfileFuncRef(StringRef(*It.first)), Line);
}
return CallStack;
>From 5cc1ae6b84361282c707a7789a512d0fd2a3b358 Mon Sep 17 00:00:00 2001
From: William Huang <williamjhuang at google.com>
Date: Mon, 25 Sep 2023 20:44:29 +0000
Subject: [PATCH 5/9] Fix bug in profile flattener
---
llvm/lib/ProfileData/SampleProf.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/ProfileData/SampleProf.cpp b/llvm/lib/ProfileData/SampleProf.cpp
index a3de3c8e7400f0a..64b844d526c2a1a 100644
--- a/llvm/lib/ProfileData/SampleProf.cpp
+++ b/llvm/lib/ProfileData/SampleProf.cpp
@@ -471,7 +471,7 @@ void ProfileConverter::convertCSProfiles(ProfileConverter::FrameNode &Node) {
if (NodeProfile) {
// Add child profile to the callsite profile map.
auto &SamplesMap = NodeProfile->functionSamplesAt(ChildNode.CallSiteLoc);
- SamplesMap.emplace(OrigChildContext.getName().str(), *ChildProfile);
+ SamplesMap.emplace(OrigChildContext.getName(), *ChildProfile);
NodeProfile->addTotalSamples(ChildProfile->getTotalSamples());
// Remove the corresponding body sample for the callsite and update the
// total weight.
>From 512ee1114f8dc5e7741a1378ad5c8b3775940e7e Mon Sep 17 00:00:00 2001
From: William Huang <williamjhuang at google.com>
Date: Tue, 26 Sep 2023 21:58:40 +0000
Subject: [PATCH 6/9] Do not use reinterpret_cast to cast ProfileFuncRef into
StringRef since they are decoupled
---
llvm/include/llvm/ProfileData/SampleProf.h | 9 +++++++--
llvm/lib/ProfileData/SampleProfReader.cpp | 8 +++++---
llvm/lib/Transforms/IPO/SampleProfile.cpp | 3 ++-
3 files changed, 14 insertions(+), 6 deletions(-)
diff --git a/llvm/include/llvm/ProfileData/SampleProf.h b/llvm/include/llvm/ProfileData/SampleProf.h
index 1604b737e336460..6c57d2d98071e3e 100644
--- a/llvm/include/llvm/ProfileData/SampleProf.h
+++ b/llvm/include/llvm/ProfileData/SampleProf.h
@@ -1142,8 +1142,13 @@ class FunctionSamples {
/// by looking up in the function map GUIDToFuncNameMap.
/// If the original name doesn't exist in the map, return empty StringRef.
StringRef getFuncName(ProfileFuncRef Name) const {
- if (!UseMD5)
- return reinterpret_cast<StringRef &>(Name);
+ std::string Buffer;
+ if (!UseMD5) {
+ // UseMD5 implies name is StringRef, in this case Buffer should not be
+ // written to.
+ assert(Name.isStringRef());
+ return Name.stringRef(Buffer);
+ }
assert(GUIDToFuncNameMap && "GUIDToFuncNameMap needs to be populated first");
return GUIDToFuncNameMap->lookup(Name.getHashCode());
diff --git a/llvm/lib/ProfileData/SampleProfReader.cpp b/llvm/lib/ProfileData/SampleProfReader.cpp
index b539e27f0bd29ea..4eaae646b94060f 100644
--- a/llvm/lib/ProfileData/SampleProfReader.cpp
+++ b/llvm/lib/ProfileData/SampleProfReader.cpp
@@ -870,6 +870,7 @@ std::error_code SampleProfileReaderExtBinaryBase::readFuncProfiles() {
}
}
+ std::string Buffer;
if (ProfileIsCS) {
assert(useFuncOffsetList());
DenseSet<uint64_t> FuncGuidsToUse;
@@ -891,7 +892,7 @@ std::error_code SampleProfileReaderExtBinaryBase::readFuncProfiles() {
auto FName = FContext.getName();
StringRef FNameString;
if (!useMD5()) {
- FNameString = reinterpret_cast<StringRef &>(FName);
+ FNameString = FName.stringRef(Buffer);
}
// For function in the current module, keep its farthest ancestor
// context. This can be used to load itself and its child and
@@ -928,7 +929,7 @@ std::error_code SampleProfileReaderExtBinaryBase::readFuncProfiles() {
for (auto NameOffset : FuncOffsetList) {
SampleContext FContext(NameOffset.first);
auto FuncName = FContext.getName();
- StringRef FuncNameStr = reinterpret_cast<StringRef &>(FuncName);
+ StringRef FuncNameStr = FuncName.stringRef(Buffer);
if (!FuncsToUse.count(FuncNameStr) && !Remapper->exist(FuncNameStr))
continue;
const uint8_t *FuncProfileAddr = Start + NameOffset.second;
@@ -1777,6 +1778,7 @@ void SampleProfileReaderItaniumRemapper::applyRemapping(LLVMContext &Ctx) {
return;
}
+ std::string Buffer;
// CSSPGO-TODO: Remapper is not yet supported.
// We will need to remap the entire context string.
assert(Remappings && "should be initialized while creating remapper");
@@ -1784,7 +1786,7 @@ void SampleProfileReaderItaniumRemapper::applyRemapping(LLVMContext &Ctx) {
DenseSet<ProfileFuncRef> NamesInSample;
Sample.second.findAllNames(NamesInSample);
for (auto &Name : NamesInSample) {
- StringRef NameStr = reinterpret_cast<StringRef &>(Name);
+ StringRef NameStr = Name.stringRef(Buffer);
if (auto Key = Remappings->insert(NameStr))
NameMap.insert({Key, NameStr});
}
diff --git a/llvm/lib/Transforms/IPO/SampleProfile.cpp b/llvm/lib/Transforms/IPO/SampleProfile.cpp
index e89913e2795349d..ac14ae86baa3561 100644
--- a/llvm/lib/Transforms/IPO/SampleProfile.cpp
+++ b/llvm/lib/Transforms/IPO/SampleProfile.cpp
@@ -1958,6 +1958,7 @@ SampleProfileLoader::buildFunctionOrder(Module &M, LazyCallGraph &CG) {
std::unique_ptr<ProfiledCallGraph> ProfiledCG = buildProfiledCallGraph(M);
scc_iterator<ProfiledCallGraph *> CGI = scc_begin(ProfiledCG.get());
+ std::string Buffer;
while (!CGI.isAtEnd()) {
auto Range = *CGI;
if (SortProfiledSCC) {
@@ -1969,7 +1970,7 @@ SampleProfileLoader::buildFunctionOrder(Module &M, LazyCallGraph &CG) {
Function *F = SymbolMap.lookup(
FunctionSamples::UseMD5
? GUIDToFuncNameMap.lookup(Node->Name.getHashCode())
- : reinterpret_cast<StringRef &>(Node->Name));
+ : Node->Name.stringRef(Buffer));
if (F && !F->isDeclaration() && F->hasFnAttribute("use-sample-profile"))
FunctionOrderList.push_back(F);
}
>From 88e85075203c47e4295f739d199b0942c4cee708 Mon Sep 17 00:00:00 2001
From: William Huang <williamjhuang at google.com>
Date: Thu, 5 Oct 2023 00:15:16 +0000
Subject: [PATCH 7/9] Rewrite some comments in ProfileFuncRef Make hash
functions consistent in SampleProf
---
.../include/llvm/ProfileData/ProfileFuncRef.h | 45 ++++++++-----------
llvm/include/llvm/ProfileData/SampleProf.h | 6 +--
llvm/lib/ProfileData/SampleProfWriter.cpp | 2 +-
3 files changed, 22 insertions(+), 31 deletions(-)
diff --git a/llvm/include/llvm/ProfileData/ProfileFuncRef.h b/llvm/include/llvm/ProfileData/ProfileFuncRef.h
index a989c6824c19993..e9b829dbb7524cd 100644
--- a/llvm/include/llvm/ProfileData/ProfileFuncRef.h
+++ b/llvm/include/llvm/ProfileData/ProfileFuncRef.h
@@ -1,15 +1,15 @@
-//===--- ProfileFuncRef.h - Sample profile function name ---*- C++ -*-===//
+//===--- ProfileFuncRef.h - Sample profile function reference ---*- C++ -*-===//
//
// 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 defines the StringRefOrHashCode class. It is to represent function
-// names in a sample profile, which can be in one of two forms - either a
-// regular string, or a 64-bit hash code.
-//
+///
+/// \file
+///
+/// Defines ProfileFuncRef class.
+///
//===----------------------------------------------------------------------===//
#ifndef LLVM_PROFILEDATA_PROFILEFUNCREF_H
@@ -25,16 +25,14 @@
namespace llvm {
namespace sampleprof {
-/// This class represents a function name that is read from a sample profile. It
-/// comes with two forms: a string or a hash code. For efficient storage, a
-/// sample profile may store function names as 64-bit MD5 values, so when
-/// reading the profile, this class can represnet them without converting it to
-/// a string first.
-/// When representing a hash code, we utilize the Length field to store it, and
-/// Data is set to null. When representing a string, it is same as StringRef,
-/// and can be pointer-casted as one.
-/// We disallow implicit cast to StringRef because there are too many instances
-/// that it may cause break the code, such as using it in a StringMap.
+/// This class represents a function that is read from a sample profile. It
+/// comes with two forms: a string or a hash code. The latter form is the 64-bit
+/// MD5 of the function name for efficient storage supported by ExtBinary
+/// profile format, and when reading the profile, this class can represent it
+/// without converting it to a string first.
+/// When representing a hash code, we utilize the LengthOrHashCode field to
+/// store it, and Name is set to null. When representing a string, it is same as
+/// StringRef.
class ProfileFuncRef {
const char *Data = nullptr;
@@ -59,21 +57,16 @@ class ProfileFuncRef {
public:
ProfileFuncRef() = default;
- /// Constructor from a StringRef. Check if Str is a number, which is generated
- /// by converting a MD5 sample profile to a format that does not support MD5,
- /// and if so, convert the numerical string to a hash code first. We assume
- /// that no function name (from a profile) can be a pure number.
+ /// Constructor from a StringRef.
explicit ProfileFuncRef(StringRef Str)
: Data(Str.data()), LengthOrHashCode(Str.size()) {
- // Only need to check for base 10 digits, fail faster if otherwise.
- if (Str.size() > 0 && isdigit(Str[0]) &&
- !Str.getAsInteger(10, LengthOrHashCode))
+ if (!Str.getAsInteger(10, LengthOrHashCode))
Data = nullptr;
}
/// Constructor from a hash code.
explicit ProfileFuncRef(uint64_t HashCode)
- : Data(nullptr), LengthOrHashCode(HashCode) {
+ : LengthOrHashCode(HashCode) {
assert(HashCode != 0);
}
@@ -173,11 +166,11 @@ inline raw_ostream &operator<<(raw_ostream &OS, const ProfileFuncRef &Obj) {
return OS;
}
-inline uint64_t MD5Hash(const sampleprof::ProfileFuncRef &Obj) {
+inline uint64_t MD5Hash(const ProfileFuncRef &Obj) {
return Obj.getHashCode();
}
-inline hash_code hash_value(const sampleprof::ProfileFuncRef &Obj) {
+inline hash_code hash_value(const ProfileFuncRef &Obj) {
return Obj.getHashCode();
}
diff --git a/llvm/include/llvm/ProfileData/SampleProf.h b/llvm/include/llvm/ProfileData/SampleProf.h
index 6c57d2d98071e3e..3e755f4d3a6f288 100644
--- a/llvm/include/llvm/ProfileData/SampleProf.h
+++ b/llvm/include/llvm/ProfileData/SampleProf.h
@@ -314,8 +314,7 @@ struct LineLocation {
struct LineLocationHash {
uint64_t operator()(const LineLocation &Loc) const {
- return std::hash<std::uint64_t>{}((((uint64_t)Loc.LineOffset) << 32) |
- Loc.Discriminator);
+ return Loc.getHashCode();
}
};
@@ -506,8 +505,7 @@ struct SampleContextFrame {
};
static inline hash_code hash_value(const SampleContextFrame &arg) {
- return hash_combine(arg.FuncName, arg.Location.LineOffset,
- arg.Location.Discriminator);
+ return arg.getHashCode();
}
using SampleContextFrameVector = SmallVector<SampleContextFrame, 1>;
diff --git a/llvm/lib/ProfileData/SampleProfWriter.cpp b/llvm/lib/ProfileData/SampleProfWriter.cpp
index 1d5c811331cc9bf..0cb2548b8444e1b 100644
--- a/llvm/lib/ProfileData/SampleProfWriter.cpp
+++ b/llvm/lib/ProfileData/SampleProfWriter.cpp
@@ -840,7 +840,7 @@ std::error_code SampleProfileWriterBinary::writeBody(const FunctionSamples &S) {
encodeULEB128(Sample.getSamples(), OS);
encodeULEB128(Sample.getCallTargets().size(), OS);
for (const auto &J : Sample.getSortedCallTargets()) {
- auto Callee = J.first;
+ ProfileFuncRef Callee = J.first;
uint64_t CalleeSamples = J.second;
if (std::error_code EC = writeNameIdx(Callee))
return EC;
>From baa563f06012e14cb8c2590231cb3a1204d2f705 Mon Sep 17 00:00:00 2001
From: William Huang <williamjhuang at google.com>
Date: Fri, 6 Oct 2023 03:19:14 +0000
Subject: [PATCH 8/9] Move HashKeyMap to a separate file because multiple
locations now use it
---
llvm/include/llvm/ProfileData/HashKeyMap.h | 129 ++++++++++++++++++
.../include/llvm/ProfileData/ProfileFuncRef.h | 7 +-
llvm/include/llvm/ProfileData/SampleProf.h | 99 ++------------
.../llvm/Transforms/IPO/ProfiledCallGraph.h | 3 +-
.../Transforms/IPO/SampleContextTracker.h | 6 +-
llvm/lib/Transforms/IPO/SampleProfile.cpp | 64 ++++-----
llvm/unittests/ProfileData/SampleProfTest.cpp | 4 +-
7 files changed, 179 insertions(+), 133 deletions(-)
create mode 100644 llvm/include/llvm/ProfileData/HashKeyMap.h
diff --git a/llvm/include/llvm/ProfileData/HashKeyMap.h b/llvm/include/llvm/ProfileData/HashKeyMap.h
new file mode 100644
index 000000000000000..b2f1bf222157b7b
--- /dev/null
+++ b/llvm/include/llvm/ProfileData/HashKeyMap.h
@@ -0,0 +1,129 @@
+//===--- HashKeyMap.h - Wrapper for maps using hash value key ---*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+///
+/// Defines HashKeyMap template.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_PROFILEDATA_HASHKEYMAP_H
+#define LLVM_PROFILEDATA_HASHKEYMAP_H
+
+#include "llvm/ADT/Hashing.h"
+#include <iterator>
+#include <utility>
+
+namespace llvm {
+
+namespace sampleprof {
+
+/// This class is a wrapper to associative container MapT<KeyT, ValueT> using
+/// the hash value of the original key as the new key. This greatly improves the
+/// performance of insert and query operations especially when hash values of
+/// keys are available a priori, and reduces memory usage if KeyT has a large
+/// size.
+/// All keys with the same hash value are considered equivalent (i.e. hash
+/// collision is silently ignored). Given such feature this class should only be
+/// used where it does not affect compilation correctness, for example, when
+/// loading a sample profile. The original key is not stored, so if the user
+/// needs to preserve it, it should be stored in the mapped type.
+/// Assuming the hashing algorithm is uniform, we use the formula
+/// 1 - Permute(n, k) / n ^ k where n is the universe size and k is number of
+/// elements chosen at random to calculate the probability of collision. With
+/// 1,000,000 entries the probability is negligible:
+/// 1 - (2^64)!/((2^64-1000000)!*(2^64)^1000000) ~= 3*10^-8.
+/// Source: https://en.wikipedia.org/wiki/Birthday_problem
+///
+/// \param MapT The underlying associative container type.
+/// \param KeyT The original key type, which requires the implementation of
+/// llvm::hash_value(KeyT).
+/// \param ValueT The original mapped type, which has the same requirement as
+/// the underlying container.
+/// \param MapTArgs Additional template parameters passed to the underlying
+/// container.
+template <template <typename, typename, typename...> typename MapT,
+ typename KeyT, typename ValueT, typename... MapTArgs>
+class HashKeyMap :
+ public MapT<decltype(hash_value(KeyT())), ValueT, MapTArgs...> {
+public:
+ using base_type = MapT<decltype(hash_value(KeyT())), ValueT, MapTArgs...>;
+ using key_type = decltype(hash_value(KeyT()));
+ using original_key_type = KeyT;
+ using mapped_type = ValueT;
+ using value_type = typename base_type::value_type;
+
+ using iterator = typename base_type::iterator;
+ using const_iterator = typename base_type::const_iterator;
+
+ template <typename... Ts>
+ std::pair<iterator, bool> try_emplace(const key_type &Hash,
+ const original_key_type &Key,
+ Ts &&...Args) {
+ assert(Hash == hash_value(Key));
+ return base_type::try_emplace(Hash, std::forward<Ts>(Args)...);
+ }
+
+ template <typename... Ts>
+ std::pair<iterator, bool> try_emplace(const original_key_type &Key,
+ Ts &&...Args) {
+ return try_emplace(hash_value(Key), Key, std::forward<Ts>(Args)...);
+ }
+
+ template <typename... Ts> std::pair<iterator, bool> emplace(Ts &&...Args) {
+ return try_emplace(std::forward<Ts>(Args)...);
+ }
+
+ mapped_type &operator[](const original_key_type &Key) {
+ return try_emplace(Key, mapped_type()).first->second;
+ }
+
+ iterator find(const original_key_type &Key) {
+ auto It = base_type::find(hash_value(Key));
+ if (It != base_type::end())
+ return It;
+ return base_type::end();
+ }
+
+ const_iterator find(const original_key_type &Key) const {
+ auto It = base_type::find(hash_value(Key));
+ if (It != base_type::end())
+ return It;
+ return base_type::end();
+ }
+
+ mapped_type lookup(const original_key_type &Key) const {
+ auto It = base_type::find(hash_value(Key));
+ if (It != base_type::end())
+ return It->second;
+ return mapped_type();
+ }
+
+ size_t count(const original_key_type &Key) const {
+ return base_type::count(hash_value(Key));
+ }
+
+ size_t erase(const original_key_type &Ctx) {
+ auto It = find(Ctx);
+ if (It != base_type::end()) {
+ base_type::erase(It);
+ return 1;
+ }
+ return 0;
+ }
+
+ iterator erase(const_iterator It) {
+ return base_type::erase(It);
+ }
+};
+
+}
+
+}
+
+#endif // LLVM_PROFILEDATA_HASHKEYMAP_H
diff --git a/llvm/include/llvm/ProfileData/ProfileFuncRef.h b/llvm/include/llvm/ProfileData/ProfileFuncRef.h
index e9b829dbb7524cd..65e4f4797024193 100644
--- a/llvm/include/llvm/ProfileData/ProfileFuncRef.h
+++ b/llvm/include/llvm/ProfileData/ProfileFuncRef.h
@@ -37,7 +37,8 @@ class ProfileFuncRef {
const char *Data = nullptr;
- /// Use uint64_t instead of size_t so that it can also hold a MD5 value.
+ // Use uint64_t instead of size_t so that it can also hold a MD5 value on
+ // 32-bit system.
uint64_t LengthOrHashCode = 0;
/// Extension to memcmp to handle hash code representation. If both are hash
@@ -60,8 +61,6 @@ class ProfileFuncRef {
/// Constructor from a StringRef.
explicit ProfileFuncRef(StringRef Str)
: Data(Str.data()), LengthOrHashCode(Str.size()) {
- if (!Str.getAsInteger(10, LengthOrHashCode))
- Data = nullptr;
}
/// Constructor from a hash code.
@@ -170,7 +169,7 @@ inline uint64_t MD5Hash(const ProfileFuncRef &Obj) {
return Obj.getHashCode();
}
-inline hash_code hash_value(const ProfileFuncRef &Obj) {
+inline uint64_t hash_value(const ProfileFuncRef &Obj) {
return Obj.getHashCode();
}
diff --git a/llvm/include/llvm/ProfileData/SampleProf.h b/llvm/include/llvm/ProfileData/SampleProf.h
index 3e755f4d3a6f288..45ba5ccc284058c 100644
--- a/llvm/include/llvm/ProfileData/SampleProf.h
+++ b/llvm/include/llvm/ProfileData/SampleProf.h
@@ -26,6 +26,7 @@
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/MathExtras.h"
+#include "llvm/ProfileData/HashKeyMap.h"
#include <algorithm>
#include <cstdint>
#include <list>
@@ -110,14 +111,6 @@ static inline uint64_t SPMagic(SampleProfileFormat Format = SPF_Binary) {
uint64_t('2') << (64 - 56) | uint64_t(Format);
}
-/// Get the proper representation of a string according to whether the
-/// current Format uses MD5 to represent the string.
-static inline ProfileFuncRef getRepInFormat(StringRef Name, bool UseMD5) {
- if (Name.empty() || !UseMD5)
- return ProfileFuncRef(Name);
- return ProfileFuncRef(Function::getGUID(Name));
-}
-
static inline uint64_t SPVersion() { return 103; }
// Section Type used by SampleProfileExtBinaryBaseReader and
@@ -1041,14 +1034,15 @@ class FunctionSamples {
/// GUID to \p S. Also traverse the BodySamples to add hot CallTarget's GUID
/// to \p S.
void findInlinedFunctions(DenseSet<GlobalValue::GUID> &S,
- const StringMap<Function *> &SymbolMap,
+ const HashKeyMap<std::unordered_map, ProfileFuncRef,
+ Function *> &SymbolMap,
uint64_t Threshold) const {
if (TotalSamples <= Threshold)
return;
auto isDeclaration = [](const Function *F) {
return !F || F->isDeclaration();
};
- if (isDeclaration(SymbolMap.lookup(getFuncName()))) {
+ if (isDeclaration(SymbolMap.lookup(getName()))) {
// Add to the import list only when it's defined out of module.
S.insert(getGUID());
}
@@ -1057,7 +1051,7 @@ class FunctionSamples {
for (const auto &BS : BodySamples)
for (const auto &TS : BS.second.getCallTargets())
if (TS.second > Threshold) {
- const Function *Callee = SymbolMap.lookup(getFuncName(TS.first));
+ const Function *Callee = SymbolMap.lookup(TS.first);
if (isDeclaration(Callee))
S.insert(TS.first.getHashCode());
}
@@ -1296,89 +1290,16 @@ class FunctionSamples {
const LocToLocMap *IRToProfileLocationMap = nullptr;
};
+/// Get the proper representation of a string according to whether the
+/// current Format uses MD5 to represent the string.
static inline ProfileFuncRef getRepInFormat(StringRef Name) {
- return getRepInFormat(Name, FunctionSamples::UseMD5);
+ if (Name.empty() || !FunctionSamples::UseMD5)
+ return ProfileFuncRef(Name);
+ return ProfileFuncRef(Function::getGUID(Name));
}
raw_ostream &operator<<(raw_ostream &OS, const FunctionSamples &FS);
-/// This class is a wrapper to associative container MapT<KeyT, ValueT> using
-/// the hash value of the original key as the new key. This greatly improves the
-/// performance of insert and query operations especially when hash values of
-/// keys are available a priori, and reduces memory usage if KeyT has a large
-/// size.
-/// All keys with the same hash value are considered equivalent (i.e. hash
-/// collision is silently ignored). Given such feature this class should only be
-/// used where it does not affect compilation correctness, for example, when
-/// loading a sample profile.
-/// Assuming the hashing algorithm is uniform, we use the formula
-/// 1 - Permute(n, k) / n ^ k where n is the universe size and k is number of
-/// elements chosen at random to calculate the probability of collision. With
-/// 1,000,000 entries the probability is negligible:
-/// 1 - (2^64)!/((2^64-1000000)!*(2^64)^1000000) ~= 3*10^-8.
-/// Source: https://en.wikipedia.org/wiki/Birthday_problem
-template <template <typename, typename, typename...> typename MapT,
- typename KeyT, typename ValueT, typename... MapTArgs>
-class HashKeyMap : public MapT<hash_code, ValueT, MapTArgs...> {
-public:
- using base_type = MapT<hash_code, ValueT, MapTArgs...>;
- using key_type = hash_code;
- using original_key_type = KeyT;
- using mapped_type = ValueT;
- using value_type = typename base_type::value_type;
-
- using iterator = typename base_type::iterator;
- using const_iterator = typename base_type::const_iterator;
-
- template <typename... Ts>
- std::pair<iterator, bool> try_emplace(const key_type &Hash,
- const original_key_type &Key,
- Ts &&...Args) {
- assert(Hash == hash_value(Key));
- return base_type::try_emplace(Hash, std::forward<Ts>(Args)...);
- }
-
- template <typename... Ts>
- std::pair<iterator, bool> try_emplace(const original_key_type &Key,
- Ts &&...Args) {
- key_type Hash = hash_value(Key);
- return try_emplace(Hash, Key, std::forward<Ts>(Args)...);
- }
-
- template <typename... Ts> std::pair<iterator, bool> emplace(Ts &&...Args) {
- return try_emplace(std::forward<Ts>(Args)...);
- }
-
- mapped_type &operator[](const original_key_type &Key) {
- return try_emplace(Key, mapped_type()).first->second;
- }
-
- iterator find(const original_key_type &Key) {
- key_type Hash = hash_value(Key);
- auto It = base_type::find(Hash);
- if (It != base_type::end())
- return It;
- return base_type::end();
- }
-
- const_iterator find(const original_key_type &Key) const {
- key_type Hash = hash_value(Key);
- auto It = base_type::find(Hash);
- if (It != base_type::end())
- return It;
- return base_type::end();
- }
-
- size_t erase(const original_key_type &Ctx) {
- auto It = find(Ctx);
- if (It != base_type::end()) {
- base_type::erase(It);
- return 1;
- }
- return 0;
- }
-};
-
/// This class provides operator overloads to the map container using MD5 as the
/// key type, so that existing code can still work in most cases using
/// SampleContext as key.
diff --git a/llvm/include/llvm/Transforms/IPO/ProfiledCallGraph.h b/llvm/include/llvm/Transforms/IPO/ProfiledCallGraph.h
index b575058614211e5..cf012dbe0f16647 100644
--- a/llvm/include/llvm/Transforms/IPO/ProfiledCallGraph.h
+++ b/llvm/include/llvm/Transforms/IPO/ProfiledCallGraph.h
@@ -206,7 +206,8 @@ class ProfiledCallGraph {
}
ProfiledCallGraphNode Root;
- std::unordered_map<ProfileFuncRef, ProfiledCallGraphNode> ProfiledFunctions;
+ HashKeyMap<std::unordered_map, ProfileFuncRef, ProfiledCallGraphNode>
+ ProfiledFunctions;
};
} // end namespace sampleprof
diff --git a/llvm/include/llvm/Transforms/IPO/SampleContextTracker.h b/llvm/include/llvm/Transforms/IPO/SampleContextTracker.h
index 270dd25672b9f36..4934f8ffb9c2487 100644
--- a/llvm/include/llvm/Transforms/IPO/SampleContextTracker.h
+++ b/llvm/include/llvm/Transforms/IPO/SampleContextTracker.h
@@ -141,7 +141,8 @@ class SampleContextTracker {
return nullptr;
return I->second;
}
- std::unordered_map<ProfileFuncRef, ContextSamplesTy> &getFuncToCtxtProfiles() {
+ HashKeyMap<std::unordered_map, ProfileFuncRef, ContextSamplesTy>
+ &getFuncToCtxtProfiles() {
return FuncToCtxtProfiles;
}
@@ -205,7 +206,8 @@ class SampleContextTracker {
ProfileToNodeMap[FSample] = Node;
}
// Map from function name to context profiles (excluding base profile)
- std::unordered_map<ProfileFuncRef, ContextSamplesTy> FuncToCtxtProfiles;
+ HashKeyMap<std::unordered_map, ProfileFuncRef, ContextSamplesTy>
+ FuncToCtxtProfiles;
// Map from current FunctionSample to the belonged context trie.
std::unordered_map<const FunctionSamples *, ContextTrieNode *>
diff --git a/llvm/lib/Transforms/IPO/SampleProfile.cpp b/llvm/lib/Transforms/IPO/SampleProfile.cpp
index ac14ae86baa3561..76732acf25a3afb 100644
--- a/llvm/lib/Transforms/IPO/SampleProfile.cpp
+++ b/llvm/lib/Transforms/IPO/SampleProfile.cpp
@@ -543,7 +543,6 @@ class SampleProfileLoader final : public SampleProfileLoaderBaseImpl<Function> {
findIndirectCallFunctionSamples(const Instruction &I, uint64_t &Sum) const;
void findExternalInlineCandidate(CallBase *CB, const FunctionSamples *Samples,
DenseSet<GlobalValue::GUID> &InlinedGUIDs,
- const StringMap<Function *> &SymbolMap,
uint64_t Threshold);
// Attempt to promote indirect call and also inline the promoted call
bool tryPromoteAndInlineCandidate(
@@ -578,7 +577,7 @@ class SampleProfileLoader final : public SampleProfileLoaderBaseImpl<Function> {
/// the function name. If the function name contains suffix, additional
/// entry is added to map from the stripped name to the function if there
/// is one-to-one mapping.
- StringMap<Function *> SymbolMap;
+ HashKeyMap<std::unordered_map, ProfileFuncRef, Function *> SymbolMap;
std::function<AssumptionCache &(Function &)> GetAC;
std::function<TargetTransformInfo &(Function &)> GetTTI;
@@ -617,9 +616,9 @@ class SampleProfileLoader final : public SampleProfileLoaderBaseImpl<Function> {
// all the function symbols defined or declared in current module.
DenseMap<uint64_t, StringRef> GUIDToFuncNameMap;
- // All the Names used in FunctionSamples including outline function
+ // GUID of all the Names used in FunctionSamples including outline function
// names, inline instance names and call target names.
- std::unordered_set<ProfileFuncRef> NamesInProfile;
+ std::unordered_set<uint64_t> GUIDNamesInProfile;
// For symbol in profile symbol list, whether to regard their profiles
// to be accurate. It is mainly decided by existance of profile symbol
@@ -974,13 +973,13 @@ bool SampleProfileLoader::tryPromoteAndInlineCandidate(
// This prevents allocating an array of zero length in callees below.
if (MaxNumPromotions == 0)
return false;
- auto CalleeFunctionName = Candidate.CalleeSamples->getFuncName();
+ auto CalleeFunctionName = Candidate.CalleeSamples->getName();
auto R = SymbolMap.find(CalleeFunctionName);
- if (R == SymbolMap.end() || !R->getValue())
+ if (R == SymbolMap.end() || !R->second)
return false;
auto &CI = *Candidate.CallInstr;
- if (!doesHistoryAllowICP(CI, R->getValue()->getName()))
+ if (!doesHistoryAllowICP(CI, R->second->getName()))
return false;
const char *Reason = "Callee function not available";
@@ -990,17 +989,17 @@ bool SampleProfileLoader::tryPromoteAndInlineCandidate(
// clone the caller first, and inline the cloned caller if it is
// recursive. As llvm does not inline recursive calls, we will
// simply ignore it instead of handling it explicitly.
- if (!R->getValue()->isDeclaration() && R->getValue()->getSubprogram() &&
- R->getValue()->hasFnAttribute("use-sample-profile") &&
- R->getValue() != &F && isLegalToPromote(CI, R->getValue(), &Reason)) {
+ if (!R->second->isDeclaration() && R->second->getSubprogram() &&
+ R->second->hasFnAttribute("use-sample-profile") &&
+ R->second != &F && isLegalToPromote(CI, R->second, &Reason)) {
// For promoted target, set its value with NOMORE_ICP_MAGICNUM count
// in the value profile metadata so the target won't be promoted again.
SmallVector<InstrProfValueData, 1> SortedCallTargets = {InstrProfValueData{
- Function::getGUID(R->getValue()->getName()), NOMORE_ICP_MAGICNUM}};
+ Function::getGUID(R->second->getName()), NOMORE_ICP_MAGICNUM}};
updateIDTMetaData(CI, SortedCallTargets, 0);
auto *DI = &pgo::promoteIndirectCall(
- CI, R->getValue(), Candidate.CallsiteCount, Sum, false, ORE);
+ CI, R->second, Candidate.CallsiteCount, Sum, false, ORE);
if (DI) {
Sum -= Candidate.CallsiteCount;
// Do not prorate the indirect callsite distribution since the original
@@ -1029,7 +1028,8 @@ bool SampleProfileLoader::tryPromoteAndInlineCandidate(
}
} else {
LLVM_DEBUG(dbgs() << "\nFailed to promote indirect call to "
- << Candidate.CalleeSamples->getFuncName() << " because "
+ << FunctionSamples::getCanonicalFnName(
+ Candidate.CallInstr->getName())<< " because "
<< Reason << "\n");
}
return false;
@@ -1074,8 +1074,7 @@ void SampleProfileLoader::emitOptimizationRemarksForInlineCandidates(
void SampleProfileLoader::findExternalInlineCandidate(
CallBase *CB, const FunctionSamples *Samples,
- DenseSet<GlobalValue::GUID> &InlinedGUIDs,
- const StringMap<Function *> &SymbolMap, uint64_t Threshold) {
+ DenseSet<GlobalValue::GUID> &InlinedGUIDs, uint64_t Threshold) {
// If ExternalInlineAdvisor(ReplayInlineAdvisor) wants to inline an external
// function make sure it's imported
@@ -1126,8 +1125,7 @@ void SampleProfileLoader::findExternalInlineCandidate(
if (!PreInline && CalleeSample->getHeadSamplesEstimate() < Threshold)
continue;
- StringRef Name = CalleeSample->getFuncName();
- Function *Func = SymbolMap.lookup(Name);
+ Function *Func = SymbolMap.lookup(CalleeSample->getName());
// Add to the import list only when it's defined out of module.
if (!Func || Func->isDeclaration())
InlinedGUIDs.insert(CalleeSample->getGUID());
@@ -1137,8 +1135,7 @@ void SampleProfileLoader::findExternalInlineCandidate(
for (const auto &BS : CalleeSample->getBodySamples())
for (const auto &TS : BS.second.getCallTargets())
if (TS.second > Threshold) {
- StringRef CalleeName = CalleeSample->getFuncName(TS.first);
- const Function *Callee = SymbolMap.lookup(CalleeName);
+ const Function *Callee = SymbolMap.lookup(TS.first);
if (!Callee || Callee->isDeclaration())
InlinedGUIDs.insert(TS.first.getHashCode());
}
@@ -1238,7 +1235,7 @@ bool SampleProfileLoader::inlineHotFunctions(
for (const auto *FS : findIndirectCallFunctionSamples(*I, Sum)) {
uint64_t SumOrigin = Sum;
if (LTOPhase == ThinOrFullLTOPhase::ThinLTOPreLink) {
- findExternalInlineCandidate(I, FS, InlinedGUIDs, SymbolMap,
+ findExternalInlineCandidate(I, FS, InlinedGUIDs,
PSI->getOrCompHotCountThreshold());
continue;
}
@@ -1259,7 +1256,7 @@ bool SampleProfileLoader::inlineHotFunctions(
}
} else if (LTOPhase == ThinOrFullLTOPhase::ThinLTOPreLink) {
findExternalInlineCandidate(I, findCalleeFunctionSamples(*I),
- InlinedGUIDs, SymbolMap,
+ InlinedGUIDs,
PSI->getOrCompHotCountThreshold());
}
}
@@ -1508,7 +1505,7 @@ bool SampleProfileLoader::inlineHotFunctionsWithPriority(
for (const auto *FS : CalleeSamples) {
// TODO: Consider disable pre-lTO ICP for MonoLTO as well
if (LTOPhase == ThinOrFullLTOPhase::ThinLTOPreLink) {
- findExternalInlineCandidate(I, FS, InlinedGUIDs, SymbolMap,
+ findExternalInlineCandidate(I, FS, InlinedGUIDs,
PSI->getOrCompHotCountThreshold());
continue;
}
@@ -1561,7 +1558,7 @@ bool SampleProfileLoader::inlineHotFunctionsWithPriority(
}
} else if (LTOPhase == ThinOrFullLTOPhase::ThinLTOPreLink) {
findExternalInlineCandidate(I, findCalleeFunctionSamples(*I),
- InlinedGUIDs, SymbolMap,
+ InlinedGUIDs,
PSI->getOrCompHotCountThreshold());
}
}
@@ -1958,7 +1955,6 @@ SampleProfileLoader::buildFunctionOrder(Module &M, LazyCallGraph &CG) {
std::unique_ptr<ProfiledCallGraph> ProfiledCG = buildProfiledCallGraph(M);
scc_iterator<ProfiledCallGraph *> CGI = scc_begin(ProfiledCG.get());
- std::string Buffer;
while (!CGI.isAtEnd()) {
auto Range = *CGI;
if (SortProfiledSCC) {
@@ -1967,10 +1963,7 @@ SampleProfileLoader::buildFunctionOrder(Module &M, LazyCallGraph &CG) {
Range = *SI;
}
for (auto *Node : Range) {
- Function *F = SymbolMap.lookup(
- FunctionSamples::UseMD5
- ? GUIDToFuncNameMap.lookup(Node->Name.getHashCode())
- : Node->Name.stringRef(Buffer));
+ Function *F = SymbolMap.lookup(Node->Name);
if (F && !F->isDeclaration() && F->hasFnAttribute("use-sample-profile"))
FunctionOrderList.push_back(F);
}
@@ -2029,9 +2022,10 @@ bool SampleProfileLoader::doInitialization(Module &M,
ProfAccForSymsInList =
ProfileAccurateForSymsInList && PSL && !ProfileSampleAccurate;
if (ProfAccForSymsInList) {
- NamesInProfile.clear();
+ GUIDNamesInProfile.clear();
if (auto NameTable = Reader->getNameTable())
- NamesInProfile.insert(NameTable->begin(), NameTable->end());
+ for (auto Name : *NameTable)
+ GUIDNamesInProfile.insert(Name.getHashCode());
CoverageTracker.setProfAccForSymsInList(true);
}
@@ -2556,10 +2550,10 @@ bool SampleProfileLoader::runOnModule(Module &M, ModuleAnalysisManager *AM,
Function *F = dyn_cast<Function>(N_F.getValue());
if (F == nullptr || OrigName.empty())
continue;
- SymbolMap[OrigName] = F;
+ SymbolMap[ProfileFuncRef(OrigName)] = F;
StringRef NewName = FunctionSamples::getCanonicalFnName(*F);
if (OrigName != NewName && !NewName.empty()) {
- auto r = SymbolMap.insert(std::make_pair(NewName, F));
+ auto r = SymbolMap.emplace(ProfileFuncRef(NewName), F);
// Failiing to insert means there is already an entry in SymbolMap,
// thus there are multiple functions that are mapped to the same
// stripped name. In this case of name conflicting, set the value
@@ -2572,11 +2566,11 @@ bool SampleProfileLoader::runOnModule(Module &M, ModuleAnalysisManager *AM,
if (Remapper) {
if (auto MapName = Remapper->lookUpNameInProfile(OrigName)) {
if (*MapName != OrigName && !MapName->empty())
- SymbolMap.insert(std::make_pair(*MapName, F));
+ SymbolMap.emplace(ProfileFuncRef(*MapName), F);
}
}
}
- assert(SymbolMap.count(StringRef()) == 0 &&
+ assert(SymbolMap.count(ProfileFuncRef()) == 0 &&
"No empty StringRef should be added in SymbolMap");
if (ReportProfileStaleness || PersistProfileStaleness ||
@@ -2640,7 +2634,7 @@ bool SampleProfileLoader::runOnFunction(Function &F, ModuleAnalysisManager *AM)
// but not cold accumulatively...), so the outline function showing up as
// cold in sampled binary will actually not be cold after current build.
StringRef CanonName = FunctionSamples::getCanonicalFnName(F);
- if (NamesInProfile.count(getRepInFormat(CanonName)))
+ if (GUIDNamesInProfile.count(Function::getGUID(CanonName)))
initialEntryCount = -1;
}
diff --git a/llvm/unittests/ProfileData/SampleProfTest.cpp b/llvm/unittests/ProfileData/SampleProfTest.cpp
index a13869d901bc2eb..382d50a78de4ece 100644
--- a/llvm/unittests/ProfileData/SampleProfTest.cpp
+++ b/llvm/unittests/ProfileData/SampleProfTest.cpp
@@ -306,8 +306,8 @@ struct SampleProfTest : ::testing::Test {
ASSERT_TRUE(ReadBooSamples != nullptr);
ASSERT_EQ(1232u, ReadBooSamples->getTotalSamples());
- ProfileFuncRef MconstructRep = getRepInFormat(MconstructName, UseMD5);
- ProfileFuncRef StringviewRep = getRepInFormat(StringviewName, UseMD5);
+ ProfileFuncRef MconstructRep = getRepInFormat(MconstructName);
+ ProfileFuncRef StringviewRep = getRepInFormat(StringviewName);
ASSERT_EQ(1000u, CTMap.get()[MconstructRep]);
ASSERT_EQ(437u, CTMap.get()[StringviewRep]);
>From 6656f28949c8ef713898727b5bc97a0d4fd3f9dd Mon Sep 17 00:00:00 2001
From: William Huang <williamjhuang at google.com>
Date: Sun, 8 Oct 2023 01:18:31 +0000
Subject: [PATCH 9/9] Fixed performance issue with String format sample profile
---
llvm/lib/Transforms/IPO/SampleProfile.cpp | 29 +++++++++++++++++------
1 file changed, 22 insertions(+), 7 deletions(-)
diff --git a/llvm/lib/Transforms/IPO/SampleProfile.cpp b/llvm/lib/Transforms/IPO/SampleProfile.cpp
index 76732acf25a3afb..124f2ed8fccb2e0 100644
--- a/llvm/lib/Transforms/IPO/SampleProfile.cpp
+++ b/llvm/lib/Transforms/IPO/SampleProfile.cpp
@@ -616,9 +616,14 @@ class SampleProfileLoader final : public SampleProfileLoaderBaseImpl<Function> {
// all the function symbols defined or declared in current module.
DenseMap<uint64_t, StringRef> GUIDToFuncNameMap;
- // GUID of all the Names used in FunctionSamples including outline function
+ // All the Names used in FunctionSamples including outline function
// names, inline instance names and call target names.
- std::unordered_set<uint64_t> GUIDNamesInProfile;
+ StringSet<> NamesInProfile;
+ // MD5 version of NamesInProfile. Either NamesInProfile or GUIDsInProfile is
+ // populated, depends on whether the profile uses MD5. Because the name table
+ // generally contains several magnitude more entries than the number of
+ // functions, we do not want to convert all names from one form to another.
+ llvm::DenseSet<uint64_t> GUIDsInProfile;
// For symbol in profile symbol list, whether to regard their profiles
// to be accurate. It is mainly decided by existance of profile symbol
@@ -2022,10 +2027,18 @@ bool SampleProfileLoader::doInitialization(Module &M,
ProfAccForSymsInList =
ProfileAccurateForSymsInList && PSL && !ProfileSampleAccurate;
if (ProfAccForSymsInList) {
- GUIDNamesInProfile.clear();
- if (auto NameTable = Reader->getNameTable())
- for (auto Name : *NameTable)
- GUIDNamesInProfile.insert(Name.getHashCode());
+ NamesInProfile.clear();
+ GUIDsInProfile.clear();
+ if (auto NameTable = Reader->getNameTable()) {
+ if (FunctionSamples::UseMD5) {
+ for (auto Name : *NameTable)
+ GUIDsInProfile.insert(Name.getHashCode());
+ } else {
+ std::string buffer;
+ for (auto Name : *NameTable)
+ NamesInProfile.insert(Name.stringRef(buffer));
+ }
+ }
CoverageTracker.setProfAccForSymsInList(true);
}
@@ -2634,7 +2647,9 @@ bool SampleProfileLoader::runOnFunction(Function &F, ModuleAnalysisManager *AM)
// but not cold accumulatively...), so the outline function showing up as
// cold in sampled binary will actually not be cold after current build.
StringRef CanonName = FunctionSamples::getCanonicalFnName(F);
- if (GUIDNamesInProfile.count(Function::getGUID(CanonName)))
+ if ((FunctionSamples::UseMD5 &&
+ GUIDsInProfile.count(Function::getGUID(CanonName))) ||
+ (!FunctionSamples::UseMD5 && NamesInProfile.count(CanonName)))
initialEntryCount = -1;
}
More information about the flang-commits
mailing list