Skip to content

Commit ced3b90

Browse files
[MemProf] Change map to vector to avoid unstable iteration (#151039)
We iterate over a std::map indexed by FuncInfo, which is a pair of a pointer and a clone number. In the ThinLTO case, this isn't an issue as the function pointer always points to the same FunctionSummary object. However, for regular LTO, this is a pointer to a Function object, which is different for each clone. This will lead to unstable iteration order. This was exposed in a test case added for PR150735, which added a new instance of iteration over this map. Since these function clones are added and numbered sequentially, change this to a vector indexed by clone number, which points to a structure containing the clone FuncInfo and the call map (the old map's key and value, respectively).
1 parent c93d166 commit ced3b90

File tree

1 file changed

+29
-18
lines changed

1 file changed

+29
-18
lines changed

llvm/lib/Transforms/IPO/MemProfContextDisambiguation.cpp

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4457,14 +4457,24 @@ bool CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::assignFunctions() {
44574457
CallsiteToCalleeFuncCloneMap[Caller] = CalleeFunc;
44584458
};
44594459

4460+
// Information for a single clone of this Func.
4461+
struct FuncCloneInfo {
4462+
// The function clone.
4463+
FuncInfo FuncClone;
4464+
// Remappings of each call of interest (from original uncloned call to the
4465+
// corresponding cloned call in this function clone).
4466+
std::map<CallInfo, CallInfo> CallMap;
4467+
};
4468+
44604469
// Walk all functions for which we saw calls with memprof metadata, and handle
44614470
// cloning for each of its calls.
44624471
for (auto &[Func, CallsWithMetadata] : FuncToCallsWithMetadata) {
44634472
FuncInfo OrigFunc(Func);
4464-
// Map from each clone of OrigFunc to a map of remappings of each call of
4465-
// interest (from original uncloned call to the corresponding cloned call in
4466-
// that function clone).
4467-
std::map<FuncInfo, std::map<CallInfo, CallInfo>> FuncClonesToCallMap;
4473+
// Map from each clone number of OrigFunc to information about that function
4474+
// clone (the function clone FuncInfo and call remappings). The index into
4475+
// the vector is the clone number, as function clones are created and
4476+
// numbered sequentially.
4477+
std::vector<FuncCloneInfo> FuncCloneInfos;
44684478
for (auto &Call : CallsWithMetadata) {
44694479
ContextNode *Node = getNodeForInst(Call);
44704480
// Skip call if we do not have a node for it (all uses of its stack ids
@@ -4488,8 +4498,9 @@ bool CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::assignFunctions() {
44884498
// Record the clone of callsite node assigned to this function clone.
44894499
FuncCloneToCurNodeCloneMap[FuncClone] = CallsiteClone;
44904500

4491-
assert(FuncClonesToCallMap.count(FuncClone));
4492-
std::map<CallInfo, CallInfo> &CallMap = FuncClonesToCallMap[FuncClone];
4501+
assert(FuncCloneInfos.size() > FuncClone.cloneNo());
4502+
std::map<CallInfo, CallInfo> &CallMap =
4503+
FuncCloneInfos[FuncClone.cloneNo()].CallMap;
44934504
CallInfo CallClone(Call);
44944505
if (auto It = CallMap.find(Call); It != CallMap.end())
44954506
CallClone = It->second;
@@ -4528,10 +4539,10 @@ bool CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::assignFunctions() {
45284539
// than existing function clones, which would have been assigned to an
45294540
// earlier clone in the list (we assign callsite clones to function
45304541
// clones greedily).
4531-
if (FuncClonesToCallMap.size() < NodeCloneCount) {
4542+
if (FuncCloneInfos.size() < NodeCloneCount) {
45324543
// If this is the first callsite copy, assign to original function.
45334544
if (NodeCloneCount == 1) {
4534-
// Since FuncClonesToCallMap is empty in this case, no clones have
4545+
// Since FuncCloneInfos is empty in this case, no clones have
45354546
// been created for this function yet, and no callers should have
45364547
// been assigned a function clone for this callee node yet.
45374548
assert(llvm::none_of(
@@ -4540,7 +4551,7 @@ bool CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::assignFunctions() {
45404551
}));
45414552
// Initialize with empty call map, assign Clone to original function
45424553
// and its callers, and skip to the next clone.
4543-
FuncClonesToCallMap[OrigFunc] = {};
4554+
FuncCloneInfos.push_back({OrigFunc, {}});
45444555
AssignCallsiteCloneToFuncClone(
45454556
OrigFunc, Call, Clone,
45464557
AllocationCallToContextNodeMap.count(Call));
@@ -4572,14 +4583,14 @@ bool CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::assignFunctions() {
45724583
}
45734584

45744585
// Clone function and save it along with the CallInfo map created
4575-
// during cloning in the FuncClonesToCallMap.
4586+
// during cloning in the FuncCloneInfos.
45764587
std::map<CallInfo, CallInfo> NewCallMap;
4577-
unsigned CloneNo = FuncClonesToCallMap.size();
4588+
unsigned CloneNo = FuncCloneInfos.size();
45784589
assert(CloneNo > 0 && "Clone 0 is the original function, which "
45794590
"should already exist in the map");
45804591
FuncInfo NewFuncClone = cloneFunctionForCallsite(
45814592
OrigFunc, Call, NewCallMap, CallsWithMetadata, CloneNo);
4582-
FuncClonesToCallMap.emplace(NewFuncClone, std::move(NewCallMap));
4593+
FuncCloneInfos.push_back({NewFuncClone, std::move(NewCallMap)});
45834594
FunctionClonesAnalysis++;
45844595
Changed = true;
45854596

@@ -4681,7 +4692,7 @@ bool CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::assignFunctions() {
46814692
CallInfo OrigCall(Callee->getOrigNode()->Call);
46824693
OrigCall.setCloneNo(0);
46834694
std::map<CallInfo, CallInfo> &CallMap =
4684-
FuncClonesToCallMap[NewFuncClone];
4695+
FuncCloneInfos[NewFuncClone.cloneNo()].CallMap;
46854696
assert(CallMap.count(OrigCall));
46864697
CallInfo NewCall(CallMap[OrigCall]);
46874698
assert(NewCall);
@@ -4819,13 +4830,13 @@ bool CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::assignFunctions() {
48194830
// clone of OrigFunc for another caller during this iteration over
48204831
// its caller edges.
48214832
if (!FuncCloneAssignedToCurCallsiteClone) {
4822-
// Find first function in FuncClonesToCallMap without an assigned
4833+
// Find first function in FuncCloneInfos without an assigned
48234834
// clone of this callsite Node. We should always have one
48244835
// available at this point due to the earlier cloning when the
4825-
// FuncClonesToCallMap size was smaller than the clone number.
4826-
for (auto &CF : FuncClonesToCallMap) {
4827-
if (!FuncCloneToCurNodeCloneMap.count(CF.first)) {
4828-
FuncCloneAssignedToCurCallsiteClone = CF.first;
4836+
// FuncCloneInfos size was smaller than the clone number.
4837+
for (auto &CF : FuncCloneInfos) {
4838+
if (!FuncCloneToCurNodeCloneMap.count(CF.FuncClone)) {
4839+
FuncCloneAssignedToCurCallsiteClone = CF.FuncClone;
48294840
break;
48304841
}
48314842
}

0 commit comments

Comments
 (0)