aboutsummaryrefslogtreecommitdiff
path: root/llvm/include/llvm/ProfileData/SampleProf.h
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/include/llvm/ProfileData/SampleProf.h')
-rw-r--r--llvm/include/llvm/ProfileData/SampleProf.h151
1 files changed, 114 insertions, 37 deletions
diff --git a/llvm/include/llvm/ProfileData/SampleProf.h b/llvm/include/llvm/ProfileData/SampleProf.h
index c45ace9e68c1..2f71bbc6bbbe 100644
--- a/llvm/include/llvm/ProfileData/SampleProf.h
+++ b/llvm/include/llvm/ProfileData/SampleProf.h
@@ -177,19 +177,29 @@ enum class SecNameTableFlags : uint32_t {
SecFlagMD5Name = (1 << 0),
// Store MD5 in fixed length instead of ULEB128 so NameTable can be
// accessed like an array.
- SecFlagFixedLengthMD5 = (1 << 1)
+ SecFlagFixedLengthMD5 = (1 << 1),
+ // Profile contains ".__uniq." suffix name. Compiler shouldn't strip
+ // the suffix when doing profile matching when seeing the flag.
+ SecFlagUniqSuffix = (1 << 2)
};
enum class SecProfSummaryFlags : uint32_t {
SecFlagInValid = 0,
/// SecFlagPartial means the profile is for common/shared code.
/// The common profile is usually merged from profiles collected
/// from running other targets.
- SecFlagPartial = (1 << 0)
+ SecFlagPartial = (1 << 0),
+ /// SecFlagContext means this is context-sensitive profile for
+ /// CSSPGO
+ SecFlagFullContext = (1 << 1),
+ /// SecFlagFSDiscriminator means this profile uses flow-sensitive
+ /// discriminators.
+ SecFlagFSDiscriminator = (1 << 2)
};
enum class SecFuncMetadataFlags : uint32_t {
SecFlagInvalid = 0,
SecFlagIsProbeBased = (1 << 0),
+ SecFlagHasAttribute = (1 << 1)
};
// Verify section specific flag is used for the correct section.
@@ -347,16 +357,19 @@ public:
return SortedTargets;
}
- /// Merge the samples in \p Other into this record.
- /// Optionally scale sample counts by \p Weight.
- sampleprof_error merge(const SampleRecord &Other, uint64_t Weight = 1) {
- sampleprof_error Result = addSamples(Other.getSamples(), Weight);
- for (const auto &I : Other.getCallTargets()) {
- MergeResult(Result, addCalledTarget(I.first(), I.second, Weight));
+ /// Prorate call targets by a distribution factor.
+ static const CallTargetMap adjustCallTargets(const CallTargetMap &Targets,
+ float DistributionFactor) {
+ CallTargetMap AdjustedTargets;
+ for (const auto &I : Targets) {
+ AdjustedTargets[I.first()] = I.second * DistributionFactor;
}
- return Result;
+ return AdjustedTargets;
}
+ /// Merge the samples in \p Other into this record.
+ /// Optionally scale sample counts by \p Weight.
+ sampleprof_error merge(const SampleRecord &Other, uint64_t Weight = 1);
void print(raw_ostream &OS, unsigned Indent) const;
void dump() const;
@@ -376,6 +389,13 @@ enum ContextStateMask {
MergedContext = 0x8 // Profile for context merged into base profile
};
+// Attribute of context associated with FunctionSamples
+enum ContextAttributeMask {
+ ContextNone = 0x0,
+ ContextWasInlined = 0x1, // Leaf of context was inlined in previous build
+ ContextShouldBeInlined = 0x2, // Leaf of context should be inlined
+};
+
// Sample context for FunctionSamples. It consists of the calling context,
// the function name and context state. Internally sample context is represented
// using StringRef, which is also the input for constructing a `SampleContext`.
@@ -387,9 +407,9 @@ enum ContextStateMask {
// `_Z8funcLeafi`
class SampleContext {
public:
- SampleContext() : State(UnknownContext) {}
- SampleContext(StringRef ContextStr,
- ContextStateMask CState = UnknownContext) {
+ SampleContext() : State(UnknownContext), Attributes(ContextNone) {}
+ SampleContext(StringRef ContextStr, ContextStateMask CState = UnknownContext)
+ : Attributes(ContextNone) {
setContext(ContextStr, CState);
}
@@ -410,6 +430,22 @@ public:
return ContextStr.split(" @ ");
}
+ // Reconstruct a new context with the last k frames, return the context-less
+ // name if K = 1
+ StringRef getContextWithLastKFrames(uint32_t K) {
+ if (K == 1)
+ return getNameWithoutContext();
+
+ size_t I = FullContext.size();
+ while (K--) {
+ I = FullContext.find_last_of(" @ ", I);
+ if (I == StringRef::npos)
+ return FullContext;
+ I -= 2;
+ }
+ return FullContext.slice(I + 3, StringRef::npos);
+ }
+
// 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,
@@ -434,12 +470,16 @@ public:
}
operator StringRef() const { return FullContext; }
+ bool hasAttribute(ContextAttributeMask A) { return Attributes & (uint32_t)A; }
+ void setAttribute(ContextAttributeMask A) { Attributes |= (uint32_t)A; }
+ uint32_t getAllAttributes() { return Attributes; }
+ void setAllAttributes(uint32_t A) { Attributes = A; }
bool hasState(ContextStateMask S) { return State & (uint32_t)S; }
void setState(ContextStateMask S) { State |= (uint32_t)S; }
void clearState(ContextStateMask S) { State &= (uint32_t)~S; }
bool hasContext() const { return State != UnknownContext; }
bool isBaseContext() const { return CallingContext.empty(); }
- StringRef getName() const { return Name; }
+ StringRef getNameWithoutContext() const { return Name; }
StringRef getCallingContext() const { return CallingContext; }
StringRef getNameWithContext() const { return FullContext; }
@@ -488,6 +528,8 @@ private:
StringRef CallingContext;
// State of the associated sample profile
uint32_t State;
+ // Attribute of the associated sample profile
+ uint32_t Attributes;
};
class FunctionSamples;
@@ -543,28 +585,22 @@ public:
FName, Num, Weight);
}
+ sampleprof_error addBodySamplesForProbe(uint32_t Index, uint64_t Num,
+ uint64_t Weight = 1) {
+ SampleRecord S;
+ S.addSamples(Num, Weight);
+ return BodySamples[LineLocation(Index, 0)].merge(S, Weight);
+ }
+
/// Return the number of samples collected at the given location.
/// Each location is specified by \p LineOffset and \p Discriminator.
/// If the location is not found in profile, return error.
ErrorOr<uint64_t> findSamplesAt(uint32_t LineOffset,
uint32_t Discriminator) const {
const auto &ret = BodySamples.find(LineLocation(LineOffset, Discriminator));
- if (ret == BodySamples.end()) {
- // For CSSPGO, in order to conserve profile size, we no longer write out
- // locations profile for those not hit during training, so we need to
- // treat them as zero instead of error here.
- if (ProfileIsCS)
- return 0;
- return std::error_code();
- // A missing counter for a probe likely means the probe was not executed.
- // Treat it as a zero count instead of an unknown count to help edge
- // weight inference.
- if (FunctionSamples::ProfileIsProbeBased)
- return 0;
+ if (ret == BodySamples.end())
return std::error_code();
- } else {
- return ret->second.getSamples();
- }
+ return ret->second.getSamples();
}
/// Returns the call target map collected at a given location.
@@ -676,7 +712,8 @@ public:
Name = Other.getName();
if (!GUIDToFuncNameMap)
GUIDToFuncNameMap = Other.GUIDToFuncNameMap;
-
+ if (Context.getNameWithContext().empty())
+ Context = Other.getContext();
if (FunctionHash == 0) {
// Set the function hash code for the target profile.
FunctionHash = Other.getFunctionHash();
@@ -711,14 +748,15 @@ public:
/// corresponding function is no less than \p Threshold, add its corresponding
/// GUID to \p S. Also traverse the BodySamples to add hot CallTarget's GUID
/// to \p S.
- void findInlinedFunctions(DenseSet<GlobalValue::GUID> &S, const Module *M,
+ void findInlinedFunctions(DenseSet<GlobalValue::GUID> &S,
+ const StringMap<Function *> &SymbolMap,
uint64_t Threshold) const {
if (TotalSamples <= Threshold)
return;
auto isDeclaration = [](const Function *F) {
return !F || F->isDeclaration();
};
- if (isDeclaration(M->getFunction(getFuncName()))) {
+ if (isDeclaration(SymbolMap.lookup(getFuncName()))) {
// Add to the import list only when it's defined out of module.
S.insert(getGUID(Name));
}
@@ -727,13 +765,13 @@ public:
for (const auto &BS : BodySamples)
for (const auto &TS : BS.second.getCallTargets())
if (TS.getValue() > Threshold) {
- const Function *Callee = M->getFunction(getFuncName(TS.getKey()));
+ const Function *Callee = SymbolMap.lookup(getFuncName(TS.getKey()));
if (isDeclaration(Callee))
S.insert(getGUID(TS.getKey()));
}
for (const auto &CS : CallsiteSamples)
for (const auto &NameFS : CS.second)
- NameFS.second.findInlinedFunctions(S, M, Threshold);
+ NameFS.second.findInlinedFunctions(S, SymbolMap, Threshold);
}
/// Set the name of the function.
@@ -762,17 +800,31 @@ public:
return getCanonicalFnName(F.getName(), Attr);
}
- static StringRef getCanonicalFnName(StringRef FnName, StringRef Attr = "") {
- static const char *knownSuffixes[] = { ".llvm.", ".part." };
+ /// Name suffixes which canonicalization should handle to avoid
+ /// profile mismatch.
+ static constexpr const char *LLVMSuffix = ".llvm.";
+ static constexpr const char *PartSuffix = ".part.";
+ static constexpr const char *UniqSuffix = ".__uniq.";
+
+ static StringRef getCanonicalFnName(StringRef FnName,
+ StringRef Attr = "selected") {
+ // Note the sequence of the suffixes in the knownSuffixes array matters.
+ // If suffix "A" is appended after the suffix "B", "A" should be in front
+ // of "B" in knownSuffixes.
+ const char *knownSuffixes[] = {LLVMSuffix, PartSuffix, UniqSuffix};
if (Attr == "" || Attr == "all") {
return FnName.split('.').first;
} else if (Attr == "selected") {
StringRef Cand(FnName);
for (const auto &Suf : knownSuffixes) {
StringRef Suffix(Suf);
+ // If the profile contains ".__uniq." suffix, don't strip the
+ // suffix for names in the IR.
+ if (Suffix == UniqSuffix && FunctionSamples::HasUniqSuffix)
+ continue;
auto It = Cand.rfind(Suffix);
if (It == StringRef::npos)
- return Cand;
+ continue;
auto Dit = Cand.rfind('.');
if (Dit == It + Suffix.size() - 1)
Cand = Cand.substr(0, It);
@@ -797,7 +849,7 @@ public:
if (!UseMD5)
return Name;
- assert(GUIDToFuncNameMap && "GUIDToFuncNameMap needs to be popluated first");
+ assert(GUIDToFuncNameMap && "GUIDToFuncNameMap needs to be populated first");
return GUIDToFuncNameMap->lookup(std::stoull(Name.data()));
}
@@ -839,6 +891,12 @@ public:
/// Whether the profile uses MD5 to represent string.
static bool UseMD5;
+ /// Whether the profile contains any ".__uniq." suffix in a name.
+ static bool HasUniqSuffix;
+
+ /// If this profile uses flow sensitive discriminators.
+ static bool ProfileIsFS;
+
/// GUIDToFuncNameMap saves the mapping from GUID to the symbol name, for
/// all the function symbols defined or declared in current module.
DenseMap<uint64_t, StringRef> *GUIDToFuncNameMap = nullptr;
@@ -926,6 +984,25 @@ private:
SamplesWithLocList V;
};
+/// SampleContextTrimmer impelements helper functions to trim, merge cold
+/// context profiles. It also supports context profile canonicalization to make
+/// sure ProfileMap's key is consistent with FunctionSample's name/context.
+class SampleContextTrimmer {
+public:
+ SampleContextTrimmer(StringMap<FunctionSamples> &Profiles)
+ : ProfileMap(Profiles){};
+ // Trim and merge cold context profile when requested.
+ void trimAndMergeColdContextProfiles(uint64_t ColdCountThreshold,
+ bool TrimColdContext,
+ bool MergeColdContext,
+ uint32_t ColdContextFrameLength);
+ // Canonicalize context profile name and attributes.
+ void canonicalizeContextProfiles();
+
+private:
+ StringMap<FunctionSamples> &ProfileMap;
+};
+
/// ProfileSymbolList records the list of function symbols shown up
/// in the binary used to generate the profile. It is useful to
/// to discriminate a function being so cold as not to shown up