}
void SampleContextTrimmer::canonicalizeContextProfiles() {
- StringSet<> ProfilesToBeRemoved;
- // Note that StringMap order is guaranteed to be top-down order,
- // this makes sure we make room for promoted/merged context in the
- // map, before we move profiles in the map.
+ std::vector<StringRef> ProfilesToBeRemoved;
+ StringMap<FunctionSamples> ProfilesToBeAdded;
for (auto &I : ProfileMap) {
FunctionSamples &FProfile = I.second;
StringRef ContextStr = FProfile.getNameWithContext();
// Use the context string from FunctionSamples to update the keys of
// ProfileMap. They can get out of sync after context profile promotion
// through pre-inliner.
- auto Ret = ProfileMap.try_emplace(ContextStr, FProfile);
- assert(Ret.second && "Conext conflict during canonicalization");
- FProfile = Ret.first->second;
-
- // Track the context profile to remove
- ProfilesToBeRemoved.erase(ContextStr);
- ProfilesToBeRemoved.insert(I.first());
+ // Duplicate the function profile for later insertion to avoid a conflict
+ // caused by a context both to be add and to be removed. This could happen
+ // when a context is promoted to another context which is also promoted to
+ // the third context. For example, given an original context A @ B @ C that
+ // is promoted to B @ C and the original context B @ C which is promoted to
+ // just C, adding B @ C to the profile map while removing same context (but
+ // with different profiles) from the map can cause a conflict if they are
+ // not handled in a right order. This can be solved by just caching the
+ // profiles to be added.
+ auto Ret = ProfilesToBeAdded.try_emplace(ContextStr, FProfile);
+ (void)Ret;
+ assert(Ret.second && "Context conflict during canonicalization");
+ ProfilesToBeRemoved.push_back(I.first());
}
for (auto &I : ProfilesToBeRemoved) {
- ProfileMap.erase(I.first());
+ ProfileMap.erase(I);
+ }
+
+ for (auto &I : ProfilesToBeAdded) {
+ ProfileMap.try_emplace(I.first(), I.second);
}
}
using namespace llvm;
using namespace sampleprof;
-static cl::opt<bool> EnableCSPreInliner(
- "csspgo-preinliner", cl::Hidden, cl::init(false),
- cl::desc("Run a global pre-inliner to merge context profile based on "
- "estimated global top-down inline decisions"));
-
// The switches specify inline thresholds used in SampleProfileLoader inlining.
// TODO: the actual threshold to be tuned here because the size here is based
// on machine code not LLVM IR.
}
void CSPreInliner::run() {
- if (!EnableCSPreInliner)
- return;
-
#ifndef NDEBUG
auto printProfileNames = [](StringMap<FunctionSamples> &Profiles,
bool IsInput) {
cl::desc("Keep the last K frames while merging cold profile. 1 means the "
"context-less base profile"));
+static cl::opt<bool> EnableCSPreInliner(
+ "csspgo-preinliner", cl::Hidden, cl::init(false),
+ cl::desc("Run a global pre-inliner to merge context profile based on "
+ "estimated global top-down inline decisions"));
+
extern cl::opt<int> ProfileSummaryCutoffCold;
using namespace llvm;
// Run global pre-inliner to adjust/merge context profile based on estimated
// inline decisions.
- CSPreInliner(ProfileMap, HotCountThreshold, ColdCountThreshold).run();
+ if (EnableCSPreInliner)
+ CSPreInliner(ProfileMap, HotCountThreshold, ColdCountThreshold).run();
// Trim and merge cold context profile using cold threshold above;
SampleContextTrimmer(ProfileMap)