55#define DEBUG_TYPE "memprof-context-disambiguation"
58 "Number of function clones created during whole program analysis");
60 "Number of function clones created during ThinLTO backend");
62 "Number of functions that had clones created during ThinLTO backend");
64 FunctionCloneDuplicatesThinBackend,
65 "Number of function clone duplicates detected during ThinLTO backend");
66STATISTIC(AllocTypeNotCold,
"Number of not cold static allocations (possibly "
67 "cloned) during whole program analysis");
68STATISTIC(AllocTypeCold,
"Number of cold static allocations (possibly cloned) "
69 "during whole program analysis");
71 "Number of not cold static allocations (possibly cloned) during "
73STATISTIC(AllocTypeColdThinBackend,
"Number of cold static allocations "
74 "(possibly cloned) during ThinLTO backend");
76 "Number of original (not cloned) allocations with memprof profiles "
77 "during ThinLTO backend");
79 AllocVersionsThinBackend,
80 "Number of allocation versions (including clones) during ThinLTO backend");
82 "Maximum number of allocation versions created for an original "
83 "allocation during ThinLTO backend");
85 "Number of unclonable ambigous allocations during ThinLTO backend");
87 "Number of edges removed due to mismatched callees (profiled vs IR)");
89 "Number of profiled callees found via tail calls");
91 "Aggregate depth of profiled callees found via tail calls");
93 "Maximum depth of profiled callees found via tail calls");
95 "Number of profiled callees found via multiple tail call chains");
96STATISTIC(DeferredBackedges,
"Number of backedges with deferred cloning");
97STATISTIC(NewMergedNodes,
"Number of new nodes created during merging");
98STATISTIC(NonNewMergedNodes,
"Number of non new nodes used during merging");
100 "Number of missing alloc nodes for context ids");
102 "Number of calls skipped during cloning due to unexpected operand");
104 "Number of callsites assigned to call multiple non-matching clones");
105STATISTIC(TotalMergeInvokes,
"Number of merge invocations for nodes");
106STATISTIC(TotalMergeIters,
"Number of merge iterations for nodes");
107STATISTIC(MaxMergeIters,
"Max merge iterations for nodes");
108STATISTIC(NumImportantContextIds,
"Number of important context ids");
109STATISTIC(NumFixupEdgeIdsInserted,
"Number of fixup edge ids inserted");
110STATISTIC(NumFixupEdgesAdded,
"Number of fixup edges added");
111STATISTIC(NumFixedContexts,
"Number of contexts with fixed edges");
113 "Number of callsite clones without an edge to a callee's clones "
114 "assigned the callee function clone of their original node");
116 "Number of calls in function clones whose callee was taken from a "
117 "callsite clone an implicit caller requires");
119 "Number of context ids with call stacks trimmed at a callsite");
121 "Number of callsite clones without an edge to a callee's clones "
122 "left in a function clone calling a conflicting callee clone");
124 "Number of aliasees prevailing in a different module than its alias");
126 "Number of summary callsites assigned to call a cloned callee (thin "
129 "Number of summary callsites assigned to call a cloned callee with "
130 "an indirect call edge (thin link)");
132 "Number of summary indirect call edges to cloned callees marked as "
133 "requiring a declaration import (thin link)");
138 cl::desc(
"Specify the path prefix of the MemProf dot files."));
142 cl::desc(
"Export graph to dot files."));
147 cl::desc(
"Iteratively apply merging on a node to catch new callers"));
157 "memprof-dot-scope",
cl::desc(
"Scope of graph to export to dot"),
162 "Export only nodes with contexts feeding given "
163 "-memprof-dot-alloc-id"),
165 "Export only nodes with given -memprof-dot-context-id")));
169 cl::desc(
"Id of alloc to export if -memprof-dot-scope=alloc "
170 "or to highlight if -memprof-dot-scope=all"));
174 cl::desc(
"Id of context to export if -memprof-dot-scope=context or to "
175 "highlight otherwise"));
179 cl::desc(
"Dump CallingContextGraph to stdout after each stage."));
183 cl::desc(
"Perform verification checks on CallingContextGraph."));
187 cl::desc(
"Perform frequent verification checks on nodes."));
190 "memprof-import-summary",
191 cl::desc(
"Import summary to use for testing the ThinLTO backend via opt"),
197 cl::desc(
"Max depth to recursively search for missing "
198 "frames through tail calls."));
203 cl::desc(
"Allow cloning of callsites involved in recursive cycles"));
207 cl::desc(
"Allow cloning of contexts through recursive cycles"));
214 cl::desc(
"Merge clones before assigning functions"));
223 cl::desc(
"Allow cloning of contexts having recursive cycles"));
229 cl::desc(
"Minimum absolute count for promoted target to be inlinable"));
233 "enable-memprof-context-disambiguation",
cl::Hidden,
234 cl::desc(
"Enable MemProf context disambiguation"));
240 cl::desc(
"Linking with hot/cold operator new interfaces"));
245 "Require target function definition when promoting indirect calls"));
268 cl::desc(
"Import at least a declaration of the indirect call targets of "
269 "callsites assigned to call a cloned callee (needed for MemProf "
270 "ICP in the ThinLTO backend)"));
277 cl::desc(
"Number of largest cold contexts to consider important"));
281 cl::desc(
"Enables edge fixup for important contexts"));
303template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
304class CallsiteContextGraph {
306 CallsiteContextGraph() =
default;
307 CallsiteContextGraph(
const CallsiteContextGraph &) =
default;
308 CallsiteContextGraph(CallsiteContextGraph &&) =
default;
312 EmitRemark =
nullptr,
313 bool AllowExtraAnalysis =
false);
317 void identifyClones();
324 bool assignFunctions();
330 EmitRemark =
nullptr)
const;
333 const CallsiteContextGraph &CCG) {
339 const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *>;
341 const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *>;
343 void exportToDot(std::string Label)
const;
346 struct FuncInfo final
347 :
public std::pair<FuncTy *, unsigned > {
348 using Base = std::pair<FuncTy *, unsigned>;
350 FuncInfo(FuncTy *
F =
nullptr,
unsigned CloneNo = 0) :
Base(
F, CloneNo) {}
351 explicit operator bool()
const {
return this->first !=
nullptr; }
352 FuncTy *func()
const {
return this->first; }
353 unsigned cloneNo()
const {
return this->second; }
357 struct CallInfo final :
public std::pair<CallTy, unsigned > {
358 using Base = std::pair<CallTy, unsigned>;
360 CallInfo(CallTy
Call =
nullptr,
unsigned CloneNo = 0)
362 explicit operator bool()
const {
return (
bool)this->first; }
363 CallTy call()
const {
return this->first; }
364 unsigned cloneNo()
const {
return this->second; }
365 void setCloneNo(
unsigned N) { this->second =
N; }
367 if (!
operator bool()) {
373 OS <<
"\t(clone " << cloneNo() <<
")";
399 bool Recursive =
false;
426 std::vector<std::shared_ptr<ContextEdge>> CalleeEdges;
430 std::vector<std::shared_ptr<ContextEdge>> CallerEdges;
434 bool useCallerEdgesForContextInfo()
const {
439 assert(!CalleeEdges.empty() || CallerEdges.empty() || IsAllocation ||
457 for (
auto &Edge : CalleeEdges.empty() ? CallerEdges : CalleeEdges)
458 Count += Edge->getContextIds().size();
462 CalleeEdges, useCallerEdgesForContextInfo()
464 : std::vector<std::shared_ptr<ContextEdge>>());
465 for (
const auto &Edge : Edges)
472 uint8_t computeAllocType()
const {
477 CalleeEdges, useCallerEdgesForContextInfo()
479 : std::vector<std::shared_ptr<ContextEdge>>());
480 for (
const auto &Edge : Edges) {
491 bool emptyContextIds()
const {
493 CalleeEdges, useCallerEdgesForContextInfo()
495 : std::vector<std::shared_ptr<ContextEdge>>());
496 for (
const auto &Edge : Edges) {
497 if (!Edge->getContextIds().empty())
504 bool hasContextId(
uint32_t Id)
const {
506 CalleeEdges, useCallerEdgesForContextInfo()
508 : std::vector<std::shared_ptr<ContextEdge>>());
509 for (
const auto &Edge : Edges) {
510 if (Edge->getContextIds().contains(Id))
517 std::vector<ContextNode *> Clones;
520 ContextNode *CloneOf =
nullptr;
522 ContextNode(
bool IsAllocation) : IsAllocation(IsAllocation),
Call() {}
524 ContextNode(
bool IsAllocation, CallInfo
C)
525 : IsAllocation(IsAllocation),
Call(
C) {}
527 void addClone(ContextNode *Clone) {
529 CloneOf->Clones.push_back(Clone);
530 Clone->CloneOf = CloneOf;
532 Clones.push_back(Clone);
534 Clone->CloneOf =
this;
538 ContextNode *getOrigNode() {
545 unsigned int ContextId);
547 ContextEdge *findEdgeFromCallee(
const ContextNode *Callee);
548 ContextEdge *findEdgeFromCaller(
const ContextNode *Caller);
549 void eraseCalleeEdge(
const ContextEdge *Edge);
550 void eraseCallerEdge(
const ContextEdge *Edge);
552 void setCall(CallInfo
C) {
Call = std::move(
C); }
554 bool hasCall()
const {
return (
bool)
Call.call(); }
560 bool isRemoved()
const {
596 bool IsBackedge =
false;
603 : Callee(Callee), Caller(Caller), AllocTypes(
AllocType),
604 ContextIds(std::move(ContextIds)) {}
610 inline void clear() {
620 inline bool isRemoved()
const {
621 if (Callee || Caller)
642 void removeNoneTypeCalleeEdges(ContextNode *
Node);
643 void removeNoneTypeCallerEdges(ContextNode *
Node);
645 recursivelyRemoveNoneTypeCalleeEdges(ContextNode *
Node,
651 template <
class NodeT,
class IteratorT>
652 std::vector<uint64_t>
657 ContextNode *addAllocNode(CallInfo
Call,
const FuncTy *
F);
660 template <
class NodeT,
class IteratorT>
661 void addStackNodesForMIB(
665 std::map<uint64_t, uint32_t> &TotalSizeToContextIdTopNCold);
670 void updateStackNodes();
679 void fixupImportantContexts();
683 void handleCallsitesWithMultipleTargets();
686 void markBackedges();
696 bool partitionCallsByCallee(
698 std::vector<std::pair<CallInfo, ContextNode *>> &NewCallToNode);
705 std::map<const ContextNode *, const FuncTy *> NodeToCallingFunc;
712 using EdgeIter =
typename std::vector<std::shared_ptr<ContextEdge>>
::iterator;
717 struct CallContextInfo {
721 std::vector<uint64_t> StackIds;
735 void removeEdgeFromGraph(ContextEdge *Edge, EdgeIter *EI =
nullptr,
736 bool CalleeIter =
true);
744 void assignStackNodesPostOrder(
758 void propagateDuplicateContextIds(
764 void connectNewNode(ContextNode *NewNode, ContextNode *OrigNode,
771 return static_cast<const DerivedCCG *
>(
this)->getStackId(IdOrIndex);
781 calleesMatch(CallTy
Call, EdgeIter &EI,
786 const FuncTy *getCalleeFunc(CallTy
Call) {
787 return static_cast<DerivedCCG *
>(
this)->getCalleeFunc(
Call);
793 bool calleeMatchesFunc(
794 CallTy
Call,
const FuncTy *Func,
const FuncTy *CallerFunc,
795 std::vector<std::pair<CallTy, FuncTy *>> &FoundCalleeChain) {
796 return static_cast<DerivedCCG *
>(
this)->calleeMatchesFunc(
797 Call, Func, CallerFunc, FoundCalleeChain);
801 bool sameCallee(CallTy Call1, CallTy Call2) {
802 return static_cast<DerivedCCG *
>(
this)->sameCallee(Call1, Call2);
807 std::vector<uint64_t> getStackIdsWithContextNodesForCall(CallTy
Call) {
808 return static_cast<DerivedCCG *
>(
this)->getStackIdsWithContextNodesForCall(
814 return static_cast<DerivedCCG *
>(
this)->getLastStackId(
Call);
820 static_cast<DerivedCCG *
>(
this)->updateAllocationCall(
Call,
AllocType);
825 return static_cast<const DerivedCCG *
>(
this)->getAllocationCallType(
Call);
830 void updateCall(CallInfo &CallerCall, FuncInfo CalleeFunc) {
831 static_cast<DerivedCCG *
>(
this)->updateCall(CallerCall, CalleeFunc);
837 FuncInfo cloneFunctionForCallsite(
839 std::vector<CallInfo> &CallsWithMetadataInFunc,
unsigned CloneNo) {
840 return static_cast<DerivedCCG *
>(
this)->cloneFunctionForCallsite(
841 Func,
Call, CallMap, CallsWithMetadataInFunc, CloneNo);
846 std::string getLabel(
const FuncTy *Func,
const CallTy
Call,
847 unsigned CloneNo)
const {
848 return static_cast<const DerivedCCG *
>(
this)->getLabel(Func,
Call, CloneNo);
852 ContextNode *createNewNode(
bool IsAllocation,
const FuncTy *
F =
nullptr,
853 CallInfo
C = CallInfo()) {
854 NodeOwner.push_back(std::make_unique<ContextNode>(IsAllocation,
C));
855 auto *NewNode = NodeOwner.back().get();
857 NodeToCallingFunc[NewNode] =
F;
858 NewNode->NodeId = NodeOwner.size();
863 ContextNode *getNodeForInst(
const CallInfo &
C);
864 ContextNode *getNodeForAlloc(
const CallInfo &
C);
865 ContextNode *getNodeForStackId(
uint64_t StackId);
887 moveEdgeToNewCalleeClone(
const std::shared_ptr<ContextEdge> &Edge,
894 void moveEdgeToExistingCalleeClone(
const std::shared_ptr<ContextEdge> &Edge,
895 ContextNode *NewCallee,
896 bool NewClone =
false,
904 void moveCalleeEdgeToNewCaller(
const std::shared_ptr<ContextEdge> &Edge,
905 ContextNode *NewCaller);
916 void mergeNodeCalleeClones(
921 void findOtherCallersToShareMerge(
922 ContextNode *
Node, std::vector<std::shared_ptr<ContextEdge>> &CalleeEdges,
936 struct ImplicitSource {
938 const ContextNode *Source;
946 using ImplicitSourceMap =
954 void assignImplicitCallers(
957 const std::map<FuncInfo, ContextNode *> &FuncCloneToCurNodeCloneMap,
958 ImplicitSourceMap &ImplicitSources);
964 static const ContextNode *resolveImplicitSource(
const ContextNode *
Node,
965 const ImplicitSource &Src);
984 struct ImportantContextInfo {
986 std::vector<uint64_t> StackIds;
989 unsigned MaxLength = 0;
993 std::map<std::vector<uint64_t>, ContextNode *> StackIdsToNode;
1002 void recordStackNode(std::vector<uint64_t> &StackIds, ContextNode *
Node,
1016 auto Size = StackIds.size();
1017 for (
auto Id : Ids) {
1018 auto &Entry = ImportantContextIdInfo[Id];
1019 Entry.StackIdsToNode[StackIds] =
Node;
1021 if (
Size > Entry.MaxLength)
1022 Entry.MaxLength =
Size;
1031 std::vector<std::unique_ptr<ContextNode>> NodeOwner;
1037 unsigned int LastContextId = 0;
1040template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1042 typename CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode;
1043template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1045 typename CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextEdge;
1046template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1048 typename CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::FuncInfo;
1049template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1051 typename CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::CallInfo;
1054class ModuleCallsiteContextGraph
1055 :
public CallsiteContextGraph<ModuleCallsiteContextGraph, Function,
1058 ModuleCallsiteContextGraph(
1060 llvm::function_ref<OptimizationRemarkEmitter &(
Function *)> OREGetter);
1063 friend CallsiteContextGraph<ModuleCallsiteContextGraph,
Function,
1068 bool calleeMatchesFunc(
1070 std::vector<std::pair<Instruction *, Function *>> &FoundCalleeChain);
1071 bool sameCallee(Instruction *Call1, Instruction *Call2);
1072 bool findProfiledCalleeThroughTailCalls(
1074 std::vector<std::pair<Instruction *, Function *>> &FoundCalleeChain,
1075 bool &FoundMultipleCalleeChains);
1077 std::vector<uint64_t> getStackIdsWithContextNodesForCall(Instruction *
Call);
1080 void updateCall(CallInfo &CallerCall, FuncInfo CalleeFunc);
1081 CallsiteContextGraph<ModuleCallsiteContextGraph,
Function,
1083 cloneFunctionForCallsite(FuncInfo &Func, CallInfo &
Call,
1084 DenseMap<CallInfo, CallInfo> &CallMap,
1085 std::vector<CallInfo> &CallsWithMetadataInFunc,
1087 std::string getLabel(
const Function *Func,
const Instruction *
Call,
1088 unsigned CloneNo)
const;
1091 llvm::function_ref<OptimizationRemarkEmitter &(
Function *)> OREGetter;
1097struct IndexCall :
public PointerUnion<CallsiteInfo *, AllocInfo *> {
1098 IndexCall() : PointerUnion() {}
1099 IndexCall(std::nullptr_t) : IndexCall() {}
1100 IndexCall(CallsiteInfo *StackNode) : PointerUnion(StackNode) {}
1101 IndexCall(AllocInfo *AllocNode) : PointerUnion(AllocNode) {}
1102 IndexCall(PointerUnion PT) : PointerUnion(PT) {}
1104 IndexCall *operator->() {
return this; }
1106 void print(raw_ostream &OS)
const {
1107 PointerUnion<CallsiteInfo *, AllocInfo *>
Base = *
this;
1132class IndexCallsiteContextGraph
1133 :
public CallsiteContextGraph<IndexCallsiteContextGraph, FunctionSummary,
1136 IndexCallsiteContextGraph(
1137 ModuleSummaryIndex &Index,
1141 ~IndexCallsiteContextGraph() {
1146 for (
auto &
I : FunctionCalleesToSynthesizedCallsiteInfos) {
1148 for (
auto &Callsite :
I.second)
1149 FS->addCallsite(std::move(*Callsite.second));
1154 friend CallsiteContextGraph<IndexCallsiteContextGraph, FunctionSummary,
1158 const FunctionSummary *getCalleeFunc(IndexCall &
Call);
1159 bool calleeMatchesFunc(
1160 IndexCall &
Call,
const FunctionSummary *Func,
1161 const FunctionSummary *CallerFunc,
1162 std::vector<std::pair<IndexCall, FunctionSummary *>> &FoundCalleeChain);
1163 bool sameCallee(IndexCall &Call1, IndexCall &Call2);
1164 bool findProfiledCalleeThroughTailCalls(
1165 ValueInfo ProfiledCallee, ValueInfo CurCallee,
unsigned Depth,
1166 std::vector<std::pair<IndexCall, FunctionSummary *>> &FoundCalleeChain,
1167 bool &FoundMultipleCalleeChains);
1169 std::vector<uint64_t> getStackIdsWithContextNodesForCall(IndexCall &
Call);
1172 void updateCall(CallInfo &CallerCall, FuncInfo CalleeFunc);
1173 CallsiteContextGraph<IndexCallsiteContextGraph, FunctionSummary,
1174 IndexCall>::FuncInfo
1175 cloneFunctionForCallsite(FuncInfo &Func, CallInfo &
Call,
1176 DenseMap<CallInfo, CallInfo> &CallMap,
1177 std::vector<CallInfo> &CallsWithMetadataInFunc,
1179 std::string getLabel(
const FunctionSummary *Func,
const IndexCall &
Call,
1180 unsigned CloneNo)
const;
1181 DenseSet<GlobalValue::GUID> findAliaseeGUIDsPrevailingInDifferentModule();
1185 std::map<const FunctionSummary *, ValueInfo> FSToVIMap;
1187 const ModuleSummaryIndex &Index;
1195 DenseMap<FunctionSummary *,
1196 std::map<ValueInfo, std::unique_ptr<CallsiteInfo>>>
1197 FunctionCalleesToSynthesizedCallsiteInfos;
1208 :
public DenseMapInfo<std::pair<IndexCall, unsigned>> {};
1211 :
public DenseMapInfo<PointerUnion<CallsiteInfo *, AllocInfo *>> {};
1232template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1233bool allocTypesMatch(
1234 const std::vector<uint8_t> &InAllocTypes,
1235 const std::vector<std::shared_ptr<ContextEdge<DerivedCCG, FuncTy, CallTy>>>
1239 assert(InAllocTypes.size() == Edges.size());
1241 InAllocTypes.begin(), InAllocTypes.end(), Edges.begin(), Edges.end(),
1243 const std::shared_ptr<ContextEdge<DerivedCCG, FuncTy, CallTy>> &r) {
1247 if (l == (uint8_t)AllocationType::None ||
1248 r->AllocTypes == (uint8_t)AllocationType::None)
1250 return allocTypeToUse(l) == allocTypeToUse(r->AllocTypes);
1259template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1260bool allocTypesMatchClone(
1261 const std::vector<uint8_t> &InAllocTypes,
1262 const ContextNode<DerivedCCG, FuncTy, CallTy> *Clone) {
1263 const ContextNode<DerivedCCG, FuncTy, CallTy> *
Node = Clone->CloneOf;
1267 assert(InAllocTypes.size() ==
Node->CalleeEdges.size());
1271 for (
const auto &
E : Clone->CalleeEdges) {
1273 EdgeCalleeMap[
E->Callee] =
E->AllocTypes;
1277 for (
unsigned I = 0;
I <
Node->CalleeEdges.size();
I++) {
1278 auto Iter = EdgeCalleeMap.
find(
Node->CalleeEdges[
I]->Callee);
1280 if (Iter == EdgeCalleeMap.
end())
1288 if (allocTypeToUse(Iter->second) != allocTypeToUse(InAllocTypes[
I]))
1296template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1297typename CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode *
1298CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::getNodeForInst(
1299 const CallInfo &
C) {
1300 ContextNode *
Node = getNodeForAlloc(
C);
1304 return NonAllocationCallToContextNodeMap.lookup(
C);
1307template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1308typename CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode *
1309CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::getNodeForAlloc(
1310 const CallInfo &
C) {
1311 return AllocationCallToContextNodeMap.lookup(
C);
1314template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1315typename CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode *
1316CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::getNodeForStackId(
1318 auto StackEntryNode = StackEntryIdToContextNodeMap.find(StackId);
1319 if (StackEntryNode != StackEntryIdToContextNodeMap.end())
1320 return StackEntryNode->second;
1324template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1325void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode::
1327 unsigned int ContextId) {
1328 for (
auto &
Edge : CallerEdges) {
1329 if (
Edge->Caller == Caller) {
1331 Edge->getContextIds().insert(ContextId);
1335 std::shared_ptr<ContextEdge>
Edge = std::make_shared<ContextEdge>(
1336 this, Caller, (uint8_t)
AllocType, DenseSet<uint32_t>({ContextId}));
1337 CallerEdges.push_back(
Edge);
1341template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1342void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::removeEdgeFromGraph(
1343 ContextEdge *
Edge, EdgeIter *EI,
bool CalleeIter) {
1359 auto CalleeCallerCount =
Callee->CallerEdges.size();
1360 auto CallerCalleeCount =
Caller->CalleeEdges.size();
1365 }
else if (CalleeIter) {
1367 *EI =
Caller->CalleeEdges.erase(*EI);
1370 *EI =
Callee->CallerEdges.erase(*EI);
1372 assert(
Callee->CallerEdges.size() < CalleeCallerCount);
1373 assert(
Caller->CalleeEdges.size() < CallerCalleeCount);
1376template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1377void CallsiteContextGraph<
1378 DerivedCCG, FuncTy, CallTy>::removeNoneTypeCalleeEdges(ContextNode *Node) {
1379 for (
auto EI =
Node->CalleeEdges.begin(); EI !=
Node->CalleeEdges.end();) {
1381 if (
Edge->AllocTypes == (uint8_t)AllocationType::None) {
1383 removeEdgeFromGraph(
Edge.get(), &EI,
true);
1389template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1390void CallsiteContextGraph<
1391 DerivedCCG, FuncTy, CallTy>::removeNoneTypeCallerEdges(ContextNode *Node) {
1392 for (
auto EI =
Node->CallerEdges.begin(); EI !=
Node->CallerEdges.end();) {
1394 if (
Edge->AllocTypes == (uint8_t)AllocationType::None) {
1396 Edge->Caller->eraseCalleeEdge(
Edge.get());
1397 EI =
Node->CallerEdges.erase(EI);
1403template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1404typename CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextEdge *
1405CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode::
1406 findEdgeFromCallee(
const ContextNode *Callee) {
1407 for (
const auto &
Edge : CalleeEdges)
1408 if (
Edge->Callee == Callee)
1413template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1414typename CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextEdge *
1415CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode::
1416 findEdgeFromCaller(
const ContextNode *Caller) {
1417 for (
const auto &
Edge : CallerEdges)
1418 if (
Edge->Caller == Caller)
1423template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1424void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode::
1425 eraseCalleeEdge(
const ContextEdge *
Edge) {
1427 CalleeEdges, [
Edge](
const std::shared_ptr<ContextEdge> &CalleeEdge) {
1428 return CalleeEdge.get() ==
Edge;
1430 assert(EI != CalleeEdges.end());
1431 CalleeEdges.erase(EI);
1434template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1435void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode::
1436 eraseCallerEdge(
const ContextEdge *
Edge) {
1438 CallerEdges, [
Edge](
const std::shared_ptr<ContextEdge> &CallerEdge) {
1439 return CallerEdge.get() ==
Edge;
1441 assert(EI != CallerEdges.end());
1442 CallerEdges.erase(EI);
1445template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1446uint8_t CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::computeAllocType(
1447 DenseSet<uint32_t> &ContextIds)
const {
1449 (uint8_t)AllocationType::Cold | (uint8_t)AllocationType::NotCold;
1450 uint8_t
AllocType = (uint8_t)AllocationType::None;
1451 for (
auto Id : ContextIds) {
1452 AllocType |= (uint8_t)ContextIdToAllocationType.at(Id);
1460template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1462CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::intersectAllocTypesImpl(
1463 const DenseSet<uint32_t> &Node1Ids,
1464 const DenseSet<uint32_t> &Node2Ids)
const {
1466 (uint8_t)AllocationType::Cold | (uint8_t)AllocationType::NotCold;
1467 uint8_t
AllocType = (uint8_t)AllocationType::None;
1468 for (
auto Id : Node1Ids) {
1469 if (!Node2Ids.
count(Id))
1471 AllocType |= (uint8_t)ContextIdToAllocationType.at(Id);
1479template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1480uint8_t CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::intersectAllocTypes(
1481 const DenseSet<uint32_t> &Node1Ids,
1482 const DenseSet<uint32_t> &Node2Ids)
const {
1483 if (Node1Ids.
size() < Node2Ids.
size())
1484 return intersectAllocTypesImpl(Node1Ids, Node2Ids);
1486 return intersectAllocTypesImpl(Node2Ids, Node1Ids);
1489template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1490typename CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode *
1491CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::addAllocNode(
1492 CallInfo
Call,
const FuncTy *
F) {
1494 ContextNode *AllocNode = createNewNode(
true,
F,
Call);
1495 AllocationCallToContextNodeMap[
Call] = AllocNode;
1497 AllocNode->OrigStackOrAllocId = LastContextId;
1500 AllocNode->AllocTypes = (uint8_t)AllocationType::None;
1516template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1517template <
class NodeT,
class IteratorT>
1518void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::addStackNodesForMIB(
1519 ContextNode *AllocNode, CallStack<NodeT, IteratorT> &StackContext,
1522 std::map<uint64_t, uint32_t> &TotalSizeToContextIdTopNCold) {
1528 ContextIdToAllocationType[++LastContextId] =
AllocType;
1530 bool IsImportant =
false;
1531 if (!ContextSizeInfo.
empty()) {
1532 auto &
Entry = ContextIdToContextSizeInfos[LastContextId];
1537 for (
auto &CSI : ContextSizeInfo)
1538 TotalCold += CSI.TotalSize;
1544 TotalCold > TotalSizeToContextIdTopNCold.begin()->first) {
1547 auto IdToRemove = TotalSizeToContextIdTopNCold.begin()->second;
1548 TotalSizeToContextIdTopNCold.erase(
1549 TotalSizeToContextIdTopNCold.begin());
1550 assert(ImportantContextIdInfo.count(IdToRemove));
1551 ImportantContextIdInfo.erase(IdToRemove);
1553 TotalSizeToContextIdTopNCold[TotalCold] = LastContextId;
1557 Entry.insert(
Entry.begin(), ContextSizeInfo.begin(), ContextSizeInfo.end());
1561 AllocNode->AllocTypes |= (uint8_t)
AllocType;
1566 ContextNode *PrevNode = AllocNode;
1570 SmallSet<uint64_t, 8> StackIdSet;
1573 ContextIter != StackContext.
end(); ++ContextIter) {
1574 auto StackId = getStackId(*ContextIter);
1576 ImportantContextIdInfo[LastContextId].StackIds.push_back(StackId);
1577 ContextNode *StackNode = getNodeForStackId(StackId);
1579 StackNode = createNewNode(
false);
1580 StackEntryIdToContextNodeMap[StackId] = StackNode;
1581 StackNode->OrigStackOrAllocId = StackId;
1586 auto Ins = StackIdSet.
insert(StackId);
1588 StackNode->Recursive =
true;
1590 StackNode->AllocTypes |= (uint8_t)
AllocType;
1591 PrevNode->addOrUpdateCallerEdge(StackNode,
AllocType, LastContextId);
1592 PrevNode = StackNode;
1596template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1598CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::duplicateContextIds(
1599 const DenseSet<uint32_t> &StackSequenceContextIds,
1600 DenseMap<uint32_t, DenseSet<uint32_t>> &OldToNewContextIds) {
1601 DenseSet<uint32_t> NewContextIds;
1602 for (
auto OldId : StackSequenceContextIds) {
1603 NewContextIds.
insert(++LastContextId);
1604 OldToNewContextIds[OldId].insert(LastContextId);
1605 assert(ContextIdToAllocationType.count(OldId));
1607 ContextIdToAllocationType[LastContextId] = ContextIdToAllocationType[OldId];
1608 auto CSI = ContextIdToContextSizeInfos.find(OldId);
1609 if (CSI != ContextIdToContextSizeInfos.end())
1610 ContextIdToContextSizeInfos[LastContextId] = CSI->second;
1611 if (DotAllocContextIds.
contains(OldId))
1612 DotAllocContextIds.
insert(LastContextId);
1614 return NewContextIds;
1617template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1618void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::
1619 propagateDuplicateContextIds(
1620 const DenseMap<uint32_t, DenseSet<uint32_t>> &OldToNewContextIds) {
1622 auto GetNewIds = [&OldToNewContextIds](
const DenseSet<uint32_t> &ContextIds) {
1623 DenseSet<uint32_t> NewIds;
1624 for (
auto Id : ContextIds)
1625 if (
auto NewId = OldToNewContextIds.find(Id);
1626 NewId != OldToNewContextIds.end())
1632 auto UpdateCallers = [&](ContextNode *
Node,
1633 DenseSet<const ContextEdge *> &Visited,
1634 auto &&UpdateCallers) ->
void {
1635 for (
const auto &
Edge :
Node->CallerEdges) {
1639 ContextNode *NextNode =
Edge->Caller;
1640 DenseSet<uint32_t> NewIdsToAdd = GetNewIds(
Edge->getContextIds());
1643 if (!NewIdsToAdd.
empty()) {
1644 Edge->getContextIds().insert_range(NewIdsToAdd);
1645 UpdateCallers(NextNode, Visited, UpdateCallers);
1650 DenseSet<const ContextEdge *> Visited;
1651 for (
auto &Entry : AllocationCallToContextNodeMap) {
1653 UpdateCallers(Node, Visited, UpdateCallers);
1657template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1658void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::connectNewNode(
1659 ContextNode *NewNode, ContextNode *OrigNode,
bool TowardsCallee,
1660 const DenseSet<uint32_t> &ContextIds) {
1662 TowardsCallee ? OrigNode->CalleeEdges : OrigNode->CallerEdges;
1664 for (
auto EI = OrigEdges.begin(); EI != OrigEdges.end();) {
1666 DenseSet<uint32_t> NewEdgeContextIds;
1671 if (NewEdgeContextIds.
empty()) {
1675 if (TowardsCallee) {
1676 uint8_t NewAllocType = computeAllocType(NewEdgeContextIds);
1677 auto NewEdge = std::make_shared<ContextEdge>(
1678 Edge->Callee, NewNode, NewAllocType, std::move(NewEdgeContextIds));
1679 NewNode->CalleeEdges.push_back(NewEdge);
1680 NewEdge->Callee->CallerEdges.push_back(NewEdge);
1682 uint8_t NewAllocType = computeAllocType(NewEdgeContextIds);
1683 auto NewEdge = std::make_shared<ContextEdge>(
1684 NewNode,
Edge->Caller, NewAllocType, std::move(NewEdgeContextIds));
1685 NewNode->CallerEdges.push_back(NewEdge);
1686 NewEdge->Caller->CalleeEdges.push_back(NewEdge);
1689 if (
Edge->getContextIds().empty()) {
1690 removeEdgeFromGraph(
Edge.get(), &EI, TowardsCallee);
1697template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1699 const std::shared_ptr<ContextEdge<DerivedCCG, FuncTy, CallTy>> &Edge) {
1703 assert(!Edge->ContextIds.empty());
1706template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1708 bool CheckEdges =
true) {
1709 if (
Node->isRemoved())
1713 auto NodeContextIds =
Node->getContextIds();
1717 if (
Node->CallerEdges.size()) {
1719 Node->CallerEdges.front()->ContextIds);
1723 set_union(CallerEdgeContextIds, Edge->ContextIds);
1730 NodeContextIds == CallerEdgeContextIds ||
1733 if (
Node->CalleeEdges.size()) {
1735 Node->CalleeEdges.front()->ContextIds);
1739 set_union(CalleeEdgeContextIds, Edge->getContextIds());
1745 NodeContextIds == CalleeEdgeContextIds);
1754 for (
const auto &
E :
Node->CalleeEdges)
1760template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1761void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::
1762 assignStackNodesPostOrder(ContextNode *Node,
1763 DenseSet<const ContextNode *> &Visited,
1764 DenseMap<
uint64_t, std::vector<CallContextInfo>>
1765 &StackIdToMatchingCalls,
1766 DenseMap<CallInfo, CallInfo> &CallToMatchingCall,
1767 const DenseSet<uint32_t> &ImportantContextIds) {
1775 auto CallerEdges =
Node->CallerEdges;
1776 for (
auto &
Edge : CallerEdges) {
1778 if (
Edge->isRemoved()) {
1782 assignStackNodesPostOrder(
Edge->Caller, Visited, StackIdToMatchingCalls,
1783 CallToMatchingCall, ImportantContextIds);
1792 if (
Node->IsAllocation ||
1793 !StackIdToMatchingCalls.count(
Node->OrigStackOrAllocId))
1796 auto &Calls = StackIdToMatchingCalls[
Node->OrigStackOrAllocId];
1800 if (Calls.size() == 1) {
1801 auto &[
Call, Ids,
Func, SavedContextIds] = Calls[0];
1802 if (Ids.size() == 1) {
1803 assert(SavedContextIds.empty());
1805 assert(Node == getNodeForStackId(Ids[0]));
1806 if (
Node->Recursive)
1809 NonAllocationCallToContextNodeMap[
Call] =
Node;
1811 recordStackNode(Ids, Node,
Node->getContextIds(), ImportantContextIds);
1820 ContextNode *LastNode = getNodeForStackId(LastId);
1823 assert(LastNode == Node);
1825 ContextNode *LastNode =
Node;
1830 DenseSet<uint32_t> LastNodeContextIds = LastNode->getContextIds();
1832 [[maybe_unused]]
bool PrevIterCreatedNode =
false;
1833 bool CreatedNode =
false;
1834 for (
unsigned I = 0;
I < Calls.size();
1835 I++, PrevIterCreatedNode = CreatedNode) {
1836 CreatedNode =
false;
1837 auto &[
Call, Ids,
Func, SavedContextIds] = Calls[
I];
1840 if (SavedContextIds.empty()) {
1847 auto MatchingCall = CallToMatchingCall[
Call];
1848 if (!NonAllocationCallToContextNodeMap.contains(MatchingCall)) {
1852 assert(
I > 0 && !PrevIterCreatedNode);
1855 NonAllocationCallToContextNodeMap[MatchingCall]->MatchingCalls.push_back(
1860 assert(LastId == Ids.back());
1869 ContextNode *PrevNode = LastNode;
1873 for (
auto IdIter = Ids.rbegin() + 1; IdIter != Ids.rend(); IdIter++) {
1875 ContextNode *CurNode = getNodeForStackId(Id);
1879 assert(!CurNode->Recursive);
1881 auto *
Edge = CurNode->findEdgeFromCaller(PrevNode);
1893 if (SavedContextIds.empty()) {
1902 ContextNode *NewNode = createNewNode(
false, Func,
Call);
1903 NonAllocationCallToContextNodeMap[
Call] = NewNode;
1905 NewNode->AllocTypes = computeAllocType(SavedContextIds);
1907 ContextNode *FirstNode = getNodeForStackId(Ids[0]);
1913 connectNewNode(NewNode, FirstNode,
true, SavedContextIds);
1918 connectNewNode(NewNode, LastNode,
false, SavedContextIds);
1923 for (
auto Id : Ids) {
1924 ContextNode *CurNode = getNodeForStackId(Id);
1931 auto *PrevEdge = CurNode->findEdgeFromCallee(PrevNode);
1938 set_subtract(PrevEdge->getContextIds(), SavedContextIds);
1939 if (PrevEdge->getContextIds().empty())
1940 removeEdgeFromGraph(PrevEdge);
1945 CurNode->AllocTypes = CurNode->CalleeEdges.empty()
1946 ? (uint8_t)AllocationType::None
1947 : CurNode->computeAllocType();
1951 recordStackNode(Ids, NewNode, SavedContextIds, ImportantContextIds);
1955 for (
auto Id : Ids) {
1956 ContextNode *CurNode = getNodeForStackId(Id);
1965template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1966void CallsiteContextGraph<DerivedCCG, FuncTy,
1967 CallTy>::fixupImportantContexts() {
1968 if (ImportantContextIdInfo.empty())
1972 NumImportantContextIds = ImportantContextIdInfo.size();
1978 exportToDot(
"beforestackfixup");
2003 for (
auto &[CurContextId, Info] : ImportantContextIdInfo) {
2004 if (
Info.StackIdsToNode.empty())
2007 ContextNode *PrevNode =
nullptr;
2008 ContextNode *CurNode =
nullptr;
2009 DenseSet<const ContextEdge *> VisitedEdges;
2010 ArrayRef<uint64_t> AllStackIds(
Info.StackIds);
2013 for (
unsigned I = 0;
I < AllStackIds.size();
I++, PrevNode = CurNode) {
2017 auto LenToEnd = AllStackIds.size() -
I;
2025 auto CheckStackIds = AllStackIds.slice(
I, Len);
2026 auto EntryIt =
Info.StackIdsToNode.find(CheckStackIds);
2027 if (EntryIt ==
Info.StackIdsToNode.end())
2029 CurNode = EntryIt->second;
2046 auto *CurEdge = PrevNode->findEdgeFromCaller(CurNode);
2049 if (CurEdge->getContextIds().insert(CurContextId).second) {
2050 NumFixupEdgeIdsInserted++;
2055 NumFixupEdgesAdded++;
2056 DenseSet<uint32_t> ContextIds({CurContextId});
2057 auto AllocType = computeAllocType(ContextIds);
2058 auto NewEdge = std::make_shared<ContextEdge>(
2059 PrevNode, CurNode,
AllocType, std::move(ContextIds));
2060 PrevNode->CallerEdges.push_back(NewEdge);
2061 CurNode->CalleeEdges.push_back(NewEdge);
2063 CurEdge = NewEdge.get();
2066 VisitedEdges.
insert(CurEdge);
2069 for (
auto &
Edge : PrevNode->CallerEdges) {
2073 Edge->getContextIds().erase(CurContextId);
2081template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
2082void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::updateStackNodes() {
2090 DenseMap<uint64_t, std::vector<CallContextInfo>> StackIdToMatchingCalls;
2091 for (
auto &[Func, CallsWithMetadata] : FuncToCallsWithMetadata) {
2092 for (
auto &
Call : CallsWithMetadata) {
2094 if (AllocationCallToContextNodeMap.count(
Call))
2096 auto StackIdsWithContextNodes =
2097 getStackIdsWithContextNodesForCall(
Call.call());
2100 if (StackIdsWithContextNodes.empty())
2104 StackIdToMatchingCalls[StackIdsWithContextNodes.back()].push_back(
2105 {
Call.call(), StackIdsWithContextNodes,
Func, {}});
2115 DenseMap<uint32_t, DenseSet<uint32_t>> OldToNewContextIds;
2119 DenseMap<CallInfo, CallInfo> CallToMatchingCall;
2120 for (
auto &It : StackIdToMatchingCalls) {
2121 auto &Calls = It.getSecond();
2123 if (Calls.size() == 1) {
2124 auto &Ids = Calls[0].StackIds;
2125 if (Ids.size() == 1)
2138 DenseMap<const FuncTy *, unsigned> FuncToIndex;
2139 for (
const auto &[Idx, CallCtxInfo] :
enumerate(Calls))
2140 FuncToIndex.
insert({CallCtxInfo.Func,
Idx});
2143 [&FuncToIndex](
const CallContextInfo &
A,
const CallContextInfo &
B) {
2144 return A.StackIds.size() >
B.StackIds.size() ||
2145 (
A.StackIds.size() ==
B.StackIds.size() &&
2146 (
A.StackIds <
B.StackIds ||
2147 (
A.StackIds ==
B.StackIds &&
2148 FuncToIndex[
A.Func] < FuncToIndex[
B.Func])));
2155 ContextNode *LastNode = getNodeForStackId(LastId);
2159 if (LastNode->Recursive)
2164 DenseSet<uint32_t> LastNodeContextIds = LastNode->getContextIds();
2172 DenseSet<const FuncTy *> MatchingIdsFuncSet;
2175 for (
unsigned I = 0;
I < Calls.size();
I++) {
2176 auto &[
Call, Ids,
Func, SavedContextIds] = Calls[
I];
2177 assert(SavedContextIds.empty());
2178 assert(LastId == Ids.back());
2183 if (
I > 0 && Ids != Calls[
I - 1].StackIds)
2184 MatchingIdsFuncSet.
clear();
2191 DenseSet<uint32_t> StackSequenceContextIds = LastNodeContextIds;
2193 ContextNode *PrevNode = LastNode;
2194 ContextNode *CurNode = LastNode;
2199 for (
auto IdIter = Ids.rbegin() + 1; IdIter != Ids.rend(); IdIter++) {
2201 CurNode = getNodeForStackId(Id);
2205 if (CurNode->Recursive) {
2210 auto *
Edge = CurNode->findEdgeFromCaller(PrevNode);
2231 if (StackSequenceContextIds.
empty()) {
2244 if (Ids.back() != getLastStackId(
Call)) {
2245 for (
const auto &PE : LastNode->CallerEdges) {
2246 set_subtract(StackSequenceContextIds, PE->getContextIds());
2247 if (StackSequenceContextIds.
empty())
2251 if (StackSequenceContextIds.
empty())
2263 MatchingIdsFuncSet.
insert(Func);
2270 bool DuplicateContextIds =
false;
2271 for (
unsigned J =
I + 1; J < Calls.size(); J++) {
2272 auto &CallCtxInfo = Calls[J];
2273 auto &NextIds = CallCtxInfo.StackIds;
2276 auto *NextFunc = CallCtxInfo.Func;
2277 if (NextFunc != Func) {
2280 DuplicateContextIds =
true;
2283 auto &NextCall = CallCtxInfo.Call;
2284 CallToMatchingCall[NextCall] =
Call;
2295 OldToNewContextIds.
reserve(OldToNewContextIds.
size() +
2296 StackSequenceContextIds.
size());
2299 ? duplicateContextIds(StackSequenceContextIds, OldToNewContextIds)
2300 : StackSequenceContextIds;
2301 assert(!SavedContextIds.empty());
2303 if (!DuplicateContextIds) {
2307 set_subtract(LastNodeContextIds, StackSequenceContextIds);
2308 if (LastNodeContextIds.
empty())
2315 propagateDuplicateContextIds(OldToNewContextIds);
2325 DenseSet<const ContextNode *> Visited;
2327 ImportantContextIdInfo.keys());
2328 for (
auto &Entry : AllocationCallToContextNodeMap)
2329 assignStackNodesPostOrder(
Entry.second, Visited, StackIdToMatchingCalls,
2330 CallToMatchingCall, ImportantContextIds);
2332 fixupImportantContexts();
2338uint64_t ModuleCallsiteContextGraph::getLastStackId(Instruction *
Call) {
2339 CallStack<MDNode, MDNode::op_iterator> CallsiteContext(
2341 return CallsiteContext.
back();
2344uint64_t IndexCallsiteContextGraph::getLastStackId(IndexCall &
Call) {
2346 CallStack<CallsiteInfo, SmallVector<unsigned>::const_iterator>
2349 return Index.getStackIdAtIndex(CallsiteContext.
back());
2371 auto Pos =
F.getName().find_last_of(
'.');
2374 bool Err =
F.getName().drop_front(Pos + 1).getAsInteger(10, CloneNo);
2380std::string ModuleCallsiteContextGraph::getLabel(
const Function *Func,
2381 const Instruction *
Call,
2382 unsigned CloneNo)
const {
2388std::string IndexCallsiteContextGraph::getLabel(
const FunctionSummary *Func,
2389 const IndexCall &
Call,
2390 unsigned CloneNo)
const {
2391 auto VI = FSToVIMap.find(Func);
2392 assert(VI != FSToVIMap.end());
2395 return CallerName +
" -> alloc";
2398 return CallerName +
" -> " +
2400 Callsite->Clones[CloneNo]);
2404std::vector<uint64_t>
2405ModuleCallsiteContextGraph::getStackIdsWithContextNodesForCall(
2406 Instruction *
Call) {
2407 CallStack<MDNode, MDNode::op_iterator> CallsiteContext(
2409 return getStackIdsWithContextNodes<MDNode, MDNode::op_iterator>(
2413std::vector<uint64_t>
2414IndexCallsiteContextGraph::getStackIdsWithContextNodesForCall(IndexCall &
Call) {
2416 CallStack<CallsiteInfo, SmallVector<unsigned>::const_iterator>
2418 return getStackIdsWithContextNodes<CallsiteInfo,
2419 SmallVector<unsigned>::const_iterator>(
2423template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
2424template <
class NodeT,
class IteratorT>
2425std::vector<uint64_t>
2426CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::getStackIdsWithContextNodes(
2427 CallStack<NodeT, IteratorT> &CallsiteContext) {
2428 std::vector<uint64_t> StackIds;
2429 for (
auto IdOrIndex : CallsiteContext) {
2430 auto StackId = getStackId(IdOrIndex);
2431 ContextNode *
Node = getNodeForStackId(StackId);
2434 StackIds.push_back(StackId);
2439ModuleCallsiteContextGraph::ModuleCallsiteContextGraph(
2441 llvm::function_ref<OptimizationRemarkEmitter &(
Function *)> OREGetter)
2442 :
Mod(
M), OREGetter(OREGetter) {
2446 std::map<uint64_t, uint32_t> TotalSizeToContextIdTopNCold;
2448 std::vector<CallInfo> CallsWithMetadata;
2449 for (
auto &BB :
F) {
2450 for (
auto &
I : BB) {
2453 if (
auto *MemProfMD =
I.getMetadata(LLVMContext::MD_memprof)) {
2454 CallsWithMetadata.push_back(&
I);
2455 auto *AllocNode = addAllocNode(&
I, &
F);
2456 auto *CallsiteMD =
I.getMetadata(LLVMContext::MD_callsite);
2460 for (
auto &MDOp : MemProfMD->operands()) {
2462 std::vector<ContextTotalSize> ContextSizeInfo;
2464 if (MIBMD->getNumOperands() > 2) {
2465 for (
unsigned I = 2;
I < MIBMD->getNumOperands();
I++) {
2466 MDNode *ContextSizePair =
2475 ContextSizeInfo.push_back({FullStackId, TotalSize});
2481 addStackNodesForMIB<MDNode, MDNode::op_iterator>(
2482 AllocNode, StackContext, CallsiteContext,
2484 TotalSizeToContextIdTopNCold);
2489 DotAllocContextIds = AllocNode->getContextIds();
2493 I.setMetadata(LLVMContext::MD_memprof,
nullptr);
2494 I.setMetadata(LLVMContext::MD_callsite,
nullptr);
2497 else if (
I.getMetadata(LLVMContext::MD_callsite)) {
2498 CallsWithMetadata.push_back(&
I);
2502 if (!CallsWithMetadata.empty())
2503 FuncToCallsWithMetadata[&
F] = CallsWithMetadata;
2507 dbgs() <<
"CCG before updating call stack chains:\n";
2512 exportToDot(
"prestackupdate");
2517 exportToDot(
"poststackupdate");
2519 handleCallsitesWithMultipleTargets();
2524 for (
auto &FuncEntry : FuncToCallsWithMetadata)
2525 for (
auto &
Call : FuncEntry.second)
2526 Call.call()->setMetadata(LLVMContext::MD_callsite,
nullptr);
2532IndexCallsiteContextGraph::findAliaseeGUIDsPrevailingInDifferentModule() {
2533 DenseSet<GlobalValue::GUID> AliaseeGUIDs;
2534 for (
auto &
I : Index) {
2536 for (
auto &S :
VI.getSummaryList()) {
2541 auto *AliaseeSummary = &AS->getAliasee();
2549 !isPrevailing(
VI.getGUID(), S.get()))
2554 auto AliaseeGUID = AS->getAliaseeGUID();
2556 if (!isPrevailing(AliaseeGUID, AliaseeSummary))
2557 AliaseeGUIDs.
insert(AliaseeGUID);
2560 AliaseesPrevailingInDiffModuleFromAlias += AliaseeGUIDs.
size();
2561 return AliaseeGUIDs;
2564IndexCallsiteContextGraph::IndexCallsiteContextGraph(
2565 ModuleSummaryIndex &Index,
2575 findAliaseeGUIDsPrevailingInDifferentModule();
2579 std::map<uint64_t, uint32_t> TotalSizeToContextIdTopNCold;
2584 for (
const auto &
I : Index.sortedGlobalValueSummariesRange()) {
2585 auto VI = Index.getValueInfo(
I);
2586 if (GUIDsToSkip.
contains(VI.getGUID()))
2588 for (
auto &S : VI.getSummaryList()) {
2597 !isPrevailing(VI.getGUID(), S.get()))
2602 std::vector<CallInfo> CallsWithMetadata;
2603 if (!
FS->allocs().empty()) {
2604 for (
auto &AN :
FS->mutableAllocs()) {
2609 if (AN.MIBs.empty())
2611 IndexCall AllocCall(&AN);
2612 CallsWithMetadata.push_back(AllocCall);
2613 auto *AllocNode = addAllocNode(AllocCall, FS);
2621 AN.ContextSizeInfos.size() == AN.MIBs.size());
2623 for (
auto &MIB : AN.MIBs) {
2626 std::vector<ContextTotalSize> ContextSizeInfo;
2627 if (!AN.ContextSizeInfos.empty()) {
2628 for (
auto [FullStackId, TotalSize] : AN.ContextSizeInfos[
I])
2629 ContextSizeInfo.push_back({FullStackId, TotalSize});
2631 addStackNodesForMIB<MIBInfo, SmallVector<unsigned>::const_iterator>(
2632 AllocNode, StackContext, EmptyContext, MIB.AllocType,
2633 ContextSizeInfo, TotalSizeToContextIdTopNCold);
2639 DotAllocContextIds = AllocNode->getContextIds();
2645 AN.Versions[0] = (
uint8_t)allocTypeToUse(AllocNode->AllocTypes);
2649 if (!
FS->callsites().empty())
2650 for (
auto &SN :
FS->mutableCallsites()) {
2651 IndexCall StackNodeCall(&SN);
2652 CallsWithMetadata.push_back(StackNodeCall);
2655 if (!CallsWithMetadata.empty())
2656 FuncToCallsWithMetadata[
FS] = CallsWithMetadata;
2658 if (!
FS->allocs().empty() || !
FS->callsites().empty())
2664 dbgs() <<
"CCG before updating call stack chains:\n";
2669 exportToDot(
"prestackupdate");
2674 exportToDot(
"poststackupdate");
2676 handleCallsitesWithMultipleTargets();
2681template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
2682void CallsiteContextGraph<DerivedCCG, FuncTy,
2683 CallTy>::handleCallsitesWithMultipleTargets() {
2698 std::vector<std::pair<CallInfo, ContextNode *>> NewCallToNode;
2699 for (
auto &Entry : NonAllocationCallToContextNodeMap) {
2700 auto *
Node = Entry.second;
2709 std::vector<CallInfo> AllCalls;
2710 AllCalls.reserve(
Node->MatchingCalls.size() + 1);
2711 AllCalls.push_back(
Node->Call);
2725 if (partitionCallsByCallee(
Node, AllCalls, NewCallToNode))
2728 auto It = AllCalls.begin();
2730 for (; It != AllCalls.end(); ++It) {
2733 for (
auto EI =
Node->CalleeEdges.begin(); EI !=
Node->CalleeEdges.end();
2736 if (!Edge->Callee->hasCall())
2738 assert(NodeToCallingFunc.count(Edge->Callee));
2740 if (!calleesMatch(
ThisCall.call(), EI, TailCallToContextNodeMap)) {
2749 if (
Node->Call != ThisCall) {
2750 Node->setCall(ThisCall);
2761 Node->MatchingCalls.clear();
2764 if (It == AllCalls.end()) {
2765 RemovedEdgesWithMismatchedCallees++;
2769 Node->setCall(CallInfo());
2774 for (++It; It != AllCalls.end(); ++It) {
2778 Node->MatchingCalls.push_back(ThisCall);
2787 NonAllocationCallToContextNodeMap.remove_if([](
const auto &it) {
2788 return !it.second->hasCall() || it.second->Call != it.first;
2792 for (
auto &[
Call,
Node] : NewCallToNode)
2793 NonAllocationCallToContextNodeMap[
Call] =
Node;
2797 for (
auto &[
Call,
Node] : TailCallToContextNodeMap)
2798 NonAllocationCallToContextNodeMap[
Call] =
Node;
2801template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
2802bool CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::partitionCallsByCallee(
2804 std::vector<std::pair<CallInfo, ContextNode *>> &NewCallToNode) {
2808 struct CallsWithSameCallee {
2809 std::vector<CallInfo> Calls;
2810 ContextNode *
Node =
nullptr;
2816 for (
auto ThisCall : AllCalls) {
2817 auto *
F = getCalleeFunc(
ThisCall.call());
2819 CalleeFuncToCallInfo[
F].Calls.push_back(ThisCall);
2828 for (
const auto &Edge :
Node->CalleeEdges) {
2829 if (!Edge->Callee->hasCall())
2831 const FuncTy *ProfiledCalleeFunc = NodeToCallingFunc[Edge->Callee];
2832 if (CalleeFuncToCallInfo.
contains(ProfiledCalleeFunc))
2833 CalleeNodeToCallInfo[Edge->Callee] =
2834 &CalleeFuncToCallInfo[ProfiledCalleeFunc];
2840 if (CalleeNodeToCallInfo.
empty())
2852 ContextNode *UnmatchedCalleesNode =
nullptr;
2854 bool UsedOrigNode =
false;
2859 auto CalleeEdges =
Node->CalleeEdges;
2860 for (
auto &Edge : CalleeEdges) {
2861 if (!Edge->Callee->hasCall())
2866 ContextNode *CallerNodeToUse =
nullptr;
2870 if (!CalleeNodeToCallInfo.
contains(Edge->Callee)) {
2871 if (!UnmatchedCalleesNode)
2872 UnmatchedCalleesNode =
2873 createNewNode(
false, NodeToCallingFunc[
Node]);
2874 CallerNodeToUse = UnmatchedCalleesNode;
2878 auto *Info = CalleeNodeToCallInfo[Edge->Callee];
2881 if (!UsedOrigNode) {
2884 Node->MatchingCalls.clear();
2885 UsedOrigNode =
true;
2888 createNewNode(
false, NodeToCallingFunc[
Node]);
2889 assert(!Info->Calls.empty());
2892 Info->Node->setCall(Info->Calls.front());
2898 NewCallToNode.push_back({Info->Node->Call, Info->Node});
2900 CallerNodeToUse = Info->Node;
2904 if (CallerNodeToUse ==
Node)
2907 moveCalleeEdgeToNewCaller(Edge, CallerNodeToUse);
2914 for (
auto &
I : CalleeNodeToCallInfo)
2915 removeNoneTypeCallerEdges(
I.second->Node);
2916 if (UnmatchedCalleesNode)
2917 removeNoneTypeCallerEdges(UnmatchedCalleesNode);
2918 removeNoneTypeCallerEdges(
Node);
2931 return Index.getStackIdAtIndex(IdOrIndex);
2934template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
2935bool CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::calleesMatch(
2936 CallTy
Call, EdgeIter &EI,
2937 MapVector<CallInfo, ContextNode *> &TailCallToContextNodeMap) {
2939 const FuncTy *ProfiledCalleeFunc = NodeToCallingFunc[
Edge->Callee];
2940 const FuncTy *CallerFunc = NodeToCallingFunc[
Edge->Caller];
2943 std::vector<std::pair<CallTy, FuncTy *>> FoundCalleeChain;
2944 if (!calleeMatchesFunc(
Call, ProfiledCalleeFunc, CallerFunc,
2949 if (FoundCalleeChain.empty())
2953 auto *CurEdge =
Callee->findEdgeFromCaller(Caller);
2957 CurEdge->ContextIds.insert_range(
Edge->ContextIds);
2958 CurEdge->AllocTypes |=
Edge->AllocTypes;
2963 auto NewEdge = std::make_shared<ContextEdge>(
2964 Callee, Caller,
Edge->AllocTypes,
Edge->ContextIds);
2965 Callee->CallerEdges.push_back(NewEdge);
2966 if (Caller ==
Edge->Caller) {
2970 EI =
Caller->CalleeEdges.insert(EI, NewEdge);
2973 "Iterator position not restored after insert and increment");
2975 Caller->CalleeEdges.push_back(NewEdge);
2980 auto *CurCalleeNode =
Edge->Callee;
2981 for (
auto &[NewCall, Func] : FoundCalleeChain) {
2982 ContextNode *NewNode =
nullptr;
2984 if (TailCallToContextNodeMap.
count(NewCall)) {
2985 NewNode = TailCallToContextNodeMap[NewCall];
2986 NewNode->AllocTypes |=
Edge->AllocTypes;
2988 FuncToCallsWithMetadata[
Func].push_back({NewCall});
2990 NewNode = createNewNode(
false, Func, NewCall);
2991 TailCallToContextNodeMap[NewCall] = NewNode;
2992 NewNode->AllocTypes =
Edge->AllocTypes;
2996 AddEdge(NewNode, CurCalleeNode);
2998 CurCalleeNode = NewNode;
3002 AddEdge(
Edge->Caller, CurCalleeNode);
3010 removeEdgeFromGraph(
Edge.get(), &EI,
true);
3022bool ModuleCallsiteContextGraph::findProfiledCalleeThroughTailCalls(
3024 std::vector<std::pair<Instruction *, Function *>> &FoundCalleeChain,
3025 bool &FoundMultipleCalleeChains) {
3032 FoundCalleeChain.push_back({Callsite,
F});
3047 bool FoundSingleCalleeChain =
false;
3048 for (
auto &BB : *CalleeFunc) {
3049 for (
auto &
I : BB) {
3051 if (!CB || !CB->isTailCall())
3053 auto *CalledValue = CB->getCalledOperand();
3054 auto *CalledFunction = CB->getCalledFunction();
3055 if (CalledValue && !CalledFunction) {
3056 CalledValue = CalledValue->stripPointerCasts();
3063 assert(!CalledFunction &&
3064 "Expected null called function in callsite for alias");
3067 if (!CalledFunction)
3069 if (CalledFunction == ProfiledCallee) {
3070 if (FoundSingleCalleeChain) {
3071 FoundMultipleCalleeChains =
true;
3074 FoundSingleCalleeChain =
true;
3075 FoundProfiledCalleeCount++;
3076 FoundProfiledCalleeDepth +=
Depth;
3077 if (
Depth > FoundProfiledCalleeMaxDepth)
3078 FoundProfiledCalleeMaxDepth =
Depth;
3079 SaveCallsiteInfo(&
I, CalleeFunc);
3080 }
else if (findProfiledCalleeThroughTailCalls(
3081 ProfiledCallee, CalledFunction,
Depth + 1,
3082 FoundCalleeChain, FoundMultipleCalleeChains)) {
3085 assert(!FoundMultipleCalleeChains);
3086 if (FoundSingleCalleeChain) {
3087 FoundMultipleCalleeChains =
true;
3090 FoundSingleCalleeChain =
true;
3091 SaveCallsiteInfo(&
I, CalleeFunc);
3092 }
else if (FoundMultipleCalleeChains)
3097 return FoundSingleCalleeChain;
3100const Function *ModuleCallsiteContextGraph::getCalleeFunc(Instruction *
Call) {
3102 if (!CB->getCalledOperand() || CB->isIndirectCall())
3104 auto *CalleeVal = CB->getCalledOperand()->stripPointerCasts();
3111bool ModuleCallsiteContextGraph::calleeMatchesFunc(
3113 std::vector<std::pair<Instruction *, Function *>> &FoundCalleeChain) {
3115 if (!CB->getCalledOperand() || CB->isIndirectCall())
3117 auto *CalleeVal = CB->getCalledOperand()->stripPointerCasts();
3119 if (CalleeFunc == Func)
3122 if (Alias && Alias->getAliasee() == Func)
3133 bool FoundMultipleCalleeChains =
false;
3134 if (!findProfiledCalleeThroughTailCalls(Func, CalleeVal,
Depth,
3136 FoundMultipleCalleeChains)) {
3137 LLVM_DEBUG(
dbgs() <<
"Not found through unique tail call chain: "
3138 <<
Func->getName() <<
" from " << CallerFunc->
getName()
3139 <<
" that actually called " << CalleeVal->getName()
3140 << (FoundMultipleCalleeChains
3141 ?
" (found multiple possible chains)"
3144 if (FoundMultipleCalleeChains)
3145 FoundProfiledCalleeNonUniquelyCount++;
3152bool ModuleCallsiteContextGraph::sameCallee(Instruction *Call1,
3153 Instruction *Call2) {
3155 if (!CB1->getCalledOperand() || CB1->isIndirectCall())
3157 auto *CalleeVal1 = CB1->getCalledOperand()->stripPointerCasts();
3160 if (!CB2->getCalledOperand() || CB2->isIndirectCall())
3162 auto *CalleeVal2 = CB2->getCalledOperand()->stripPointerCasts();
3164 return CalleeFunc1 == CalleeFunc2;
3167bool IndexCallsiteContextGraph::findProfiledCalleeThroughTailCalls(
3168 ValueInfo ProfiledCallee, ValueInfo CurCallee,
unsigned Depth,
3169 std::vector<std::pair<IndexCall, FunctionSummary *>> &FoundCalleeChain,
3170 bool &FoundMultipleCalleeChains) {
3176 auto CreateAndSaveCallsiteInfo = [&](ValueInfo
Callee, FunctionSummary *
FS) {
3179 if (!FunctionCalleesToSynthesizedCallsiteInfos.count(FS) ||
3180 !FunctionCalleesToSynthesizedCallsiteInfos[FS].count(Callee))
3183 FunctionCalleesToSynthesizedCallsiteInfos[
FS][
Callee] =
3184 std::make_unique<CallsiteInfo>(Callee, SmallVector<unsigned>());
3185 CallsiteInfo *NewCallsiteInfo =
3186 FunctionCalleesToSynthesizedCallsiteInfos[
FS][
Callee].get();
3187 FoundCalleeChain.push_back({NewCallsiteInfo,
FS});
3194 bool FoundSingleCalleeChain =
false;
3197 !isPrevailing(CurCallee.
getGUID(), S.get()))
3202 auto FSVI = CurCallee;
3205 FSVI = AS->getAliaseeVI();
3206 for (
auto &CallEdge :
FS->calls()) {
3207 if (!CallEdge.second.hasTailCall())
3209 if (CallEdge.first == ProfiledCallee) {
3210 if (FoundSingleCalleeChain) {
3211 FoundMultipleCalleeChains =
true;
3214 FoundSingleCalleeChain =
true;
3215 FoundProfiledCalleeCount++;
3216 FoundProfiledCalleeDepth +=
Depth;
3217 if (
Depth > FoundProfiledCalleeMaxDepth)
3218 FoundProfiledCalleeMaxDepth =
Depth;
3219 CreateAndSaveCallsiteInfo(CallEdge.first, FS);
3221 assert(!FSToVIMap.count(FS) || FSToVIMap[FS] == FSVI);
3222 FSToVIMap[
FS] = FSVI;
3223 }
else if (findProfiledCalleeThroughTailCalls(
3224 ProfiledCallee, CallEdge.first,
Depth + 1,
3225 FoundCalleeChain, FoundMultipleCalleeChains)) {
3228 assert(!FoundMultipleCalleeChains);
3229 if (FoundSingleCalleeChain) {
3230 FoundMultipleCalleeChains =
true;
3233 FoundSingleCalleeChain =
true;
3234 CreateAndSaveCallsiteInfo(CallEdge.first, FS);
3236 assert(!FSToVIMap.count(FS) || FSToVIMap[FS] == FSVI);
3237 FSToVIMap[
FS] = FSVI;
3238 }
else if (FoundMultipleCalleeChains)
3243 return FoundSingleCalleeChain;
3246const FunctionSummary *
3247IndexCallsiteContextGraph::getCalleeFunc(IndexCall &
Call) {
3249 if (
Callee.getSummaryList().empty())
3254bool IndexCallsiteContextGraph::calleeMatchesFunc(
3255 IndexCall &
Call,
const FunctionSummary *Func,
3256 const FunctionSummary *CallerFunc,
3257 std::vector<std::pair<IndexCall, FunctionSummary *>> &FoundCalleeChain) {
3261 AliasSummary *Alias =
3262 Callee.getSummaryList().empty()
3265 assert(FSToVIMap.count(Func));
3266 auto FuncVI = FSToVIMap[
Func];
3267 if (Callee == FuncVI ||
3282 bool FoundMultipleCalleeChains =
false;
3283 if (!findProfiledCalleeThroughTailCalls(
3284 FuncVI, Callee,
Depth, FoundCalleeChain, FoundMultipleCalleeChains)) {
3285 LLVM_DEBUG(
dbgs() <<
"Not found through unique tail call chain: " << FuncVI
3286 <<
" from " << FSToVIMap[CallerFunc]
3287 <<
" that actually called " << Callee
3288 << (FoundMultipleCalleeChains
3289 ?
" (found multiple possible chains)"
3292 if (FoundMultipleCalleeChains)
3293 FoundProfiledCalleeNonUniquelyCount++;
3300bool IndexCallsiteContextGraph::sameCallee(IndexCall &Call1, IndexCall &Call2) {
3303 return Callee1 == Callee2;
3306template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3307void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode::dump()
3313template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3314void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode::print(
3315 raw_ostream &OS)
const {
3316 OS <<
"Node " <<
this <<
"\n";
3320 OS <<
" (recursive)";
3322 if (!MatchingCalls.empty()) {
3323 OS <<
"\tMatchingCalls:\n";
3324 for (
auto &MatchingCall : MatchingCalls) {
3326 MatchingCall.print(OS);
3330 OS <<
"\tNodeId: " <<
NodeId <<
"\n";
3332 OS <<
"\tContextIds:";
3334 auto ContextIds = getContextIds();
3335 std::vector<uint32_t> SortedIds(ContextIds.
begin(), ContextIds.
end());
3336 std::sort(SortedIds.begin(), SortedIds.end());
3337 for (
auto Id : SortedIds)
3340 OS <<
"\tCalleeEdges:\n";
3341 for (
auto &
Edge : CalleeEdges)
3342 OS <<
"\t\t" << *
Edge <<
" (Callee NodeId: " <<
Edge->Callee->NodeId
3344 OS <<
"\tCallerEdges:\n";
3345 for (
auto &
Edge : CallerEdges)
3346 OS <<
"\t\t" << *
Edge <<
" (Caller NodeId: " <<
Edge->Caller->NodeId
3348 if (!Clones.empty()) {
3351 for (
auto *
C : Clones)
3352 OS <<
LS <<
C <<
" NodeId: " <<
C->NodeId;
3354 }
else if (CloneOf) {
3355 OS <<
"\tClone of " << CloneOf <<
" NodeId: " << CloneOf->NodeId <<
"\n";
3359template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3360void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextEdge::dump()
3366template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3367void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextEdge::print(
3368 raw_ostream &OS)
const {
3369 OS <<
"Edge from Callee " <<
Callee <<
" to Caller: " <<
Caller
3370 << (IsBackedge ?
" (BE)" :
"")
3372 OS <<
" ContextIds:";
3373 std::vector<uint32_t> SortedIds(ContextIds.
begin(), ContextIds.
end());
3374 std::sort(SortedIds.begin(), SortedIds.end());
3375 for (
auto Id : SortedIds)
3379template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3380void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::dump()
const {
3384template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3385void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::print(
3386 raw_ostream &OS)
const {
3387 OS <<
"Callsite Context Graph:\n";
3388 using GraphType =
const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *;
3390 if (
Node->isRemoved())
3397template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3398void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::printTotalSizes(
3400 function_ref<
void(StringRef, StringRef,
const Twine &)> EmitRemark)
const {
3401 using GraphType =
const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *;
3403 if (
Node->isRemoved())
3405 if (!
Node->IsAllocation)
3407 DenseSet<uint32_t> ContextIds =
Node->getContextIds();
3408 auto AllocTypeFromCall = getAllocationCallType(
Node->Call);
3409 std::vector<uint32_t> SortedIds(ContextIds.
begin(), ContextIds.
end());
3410 std::sort(SortedIds.begin(), SortedIds.end());
3411 for (
auto Id : SortedIds) {
3412 auto TypeI = ContextIdToAllocationType.find(Id);
3413 assert(TypeI != ContextIdToAllocationType.end());
3414 auto CSI = ContextIdToContextSizeInfos.find(Id);
3415 if (CSI != ContextIdToContextSizeInfos.end()) {
3416 for (
auto &Info : CSI->second) {
3419 " full allocation context " + std::to_string(
Info.FullStackId) +
3420 " with total size " + std::to_string(
Info.TotalSize) +
" is " +
3422 if (allocTypeToUse(
Node->AllocTypes) != AllocTypeFromCall)
3424 " due to cold byte percent";
3426 Msg +=
" (internal context id " + std::to_string(Id) +
")";
3438 if (allocTypeToUse(
Node->AllocTypes) != AllocTypeFromCall)
3440 " due to cold byte percent";
3442 Msg +=
" (internal context id " + std::to_string(Id) +
")";
3452template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3453void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::check()
const {
3454 using GraphType =
const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *;
3457 for (
auto &
Edge :
Node->CallerEdges)
3462template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3464 using GraphType =
const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *;
3465 using NodeRef =
const ContextNode<DerivedCCG, FuncTy, CallTy> *;
3467 using NodePtrTy = std::unique_ptr<ContextNode<DerivedCCG, FuncTy, CallTy>>;
3483 return G->NodeOwner.begin()->get();
3486 using EdgePtrTy = std::shared_ptr<ContextEdge<DerivedCCG, FuncTy, CallTy>>;
3487 static const ContextNode<DerivedCCG, FuncTy, CallTy> *
3506template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3520 using GraphType =
const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *;
3526 std::string LabelString =
3527 (
Twine(
"OrigId: ") + (
Node->IsAllocation ?
"Alloc" :
"") +
3530 LabelString +=
"\n";
3531 if (
Node->hasCall()) {
3532 auto Func =
G->NodeToCallingFunc.find(
Node);
3533 assert(Func !=
G->NodeToCallingFunc.end());
3535 G->getLabel(Func->second,
Node->Call.call(),
Node->Call.cloneNo());
3536 for (
auto &MatchingCall :
Node->MatchingCalls) {
3537 LabelString +=
"\n";
3538 LabelString +=
G->getLabel(Func->second, MatchingCall.call(),
3539 MatchingCall.cloneNo());
3542 LabelString +=
"null call";
3543 if (
Node->Recursive)
3544 LabelString +=
" (recursive)";
3546 LabelString +=
" (external)";
3552 auto ContextIds =
Node->getContextIds();
3556 bool Highlight =
false;
3565 std::string AttributeString = (
Twine(
"tooltip=\"") + getNodeId(
Node) +
" " +
3566 getContextIds(ContextIds) +
"\"")
3570 AttributeString +=
",fontsize=\"30\"";
3572 (
Twine(
",fillcolor=\"") + getColor(
Node->AllocTypes, Highlight) +
"\"")
3574 if (
Node->CloneOf) {
3575 AttributeString +=
",color=\"blue\"";
3576 AttributeString +=
",style=\"filled,bold,dashed\"";
3578 AttributeString +=
",style=\"filled\"";
3579 return AttributeString;
3584 auto &Edge = *(ChildIter.getCurrent());
3589 bool Highlight =
false;
3598 auto Color = getColor(Edge->AllocTypes, Highlight);
3599 std::string AttributeString =
3600 (
Twine(
"tooltip=\"") + getContextIds(Edge->ContextIds) +
"\"" +
3602 Twine(
",fillcolor=\"") + Color +
"\"" +
Twine(
",color=\"") + Color +
3605 if (Edge->IsBackedge)
3606 AttributeString +=
",style=\"dotted\"";
3609 AttributeString +=
",penwidth=\"2.0\",weight=\"2\"";
3610 return AttributeString;
3616 if (
Node->isRemoved())
3629 std::string IdString =
"ContextIds:";
3630 if (ContextIds.
size() < 100) {
3631 std::vector<uint32_t> SortedIds(ContextIds.
begin(), ContextIds.
end());
3632 std::sort(SortedIds.begin(), SortedIds.end());
3633 for (
auto Id : SortedIds)
3634 IdString += (
" " +
Twine(Id)).str();
3636 IdString += (
" (" + Twine(ContextIds.
size()) +
" ids)").str();
3641 static std::string getColor(uint8_t AllocTypes,
bool Highlight) {
3647 if (AllocTypes == (uint8_t)AllocationType::NotCold)
3649 return !
DoHighlight || Highlight ?
"brown1" :
"lightpink";
3650 if (AllocTypes == (uint8_t)AllocationType::Cold)
3651 return !
DoHighlight || Highlight ?
"cyan" :
"lightskyblue";
3653 ((uint8_t)AllocationType::NotCold | (uint8_t)AllocationType::Cold))
3654 return Highlight ?
"magenta" :
"mediumorchid1";
3658 static std::string getNodeId(NodeRef Node) {
3659 std::stringstream SStream;
3660 SStream << std::hex <<
"N0x" << (
unsigned long long)Node;
3661 std::string
Result = SStream.str();
3670template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3675template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3676void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::exportToDot(
3677 std::string Label)
const {
3682template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3683typename CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode *
3684CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::moveEdgeToNewCalleeClone(
3685 const std::shared_ptr<ContextEdge> &
Edge,
3686 DenseSet<uint32_t> ContextIdsToMove) {
3688 assert(NodeToCallingFunc.count(Node));
3689 ContextNode *Clone =
3690 createNewNode(
Node->IsAllocation, NodeToCallingFunc[Node],
Node->Call);
3691 Node->addClone(Clone);
3692 Clone->MatchingCalls =
Node->MatchingCalls;
3693 moveEdgeToExistingCalleeClone(
Edge, Clone,
true,
3698template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3699void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::
3700 moveEdgeToExistingCalleeClone(
const std::shared_ptr<ContextEdge> &
Edge,
3701 ContextNode *NewCallee,
bool NewClone,
3702 DenseSet<uint32_t> ContextIdsToMove) {
3705 assert(NewCallee->getOrigNode() ==
Edge->Callee->getOrigNode());
3707 bool EdgeIsRecursive =
Edge->Callee ==
Edge->Caller;
3709 ContextNode *OldCallee =
Edge->Callee;
3713 auto ExistingEdgeToNewCallee = NewCallee->findEdgeFromCaller(
Edge->Caller);
3717 if (ContextIdsToMove.
empty())
3718 ContextIdsToMove =
Edge->getContextIds();
3722 if (
Edge->getContextIds().size() == ContextIdsToMove.
size()) {
3725 NewCallee->AllocTypes |=
Edge->AllocTypes;
3727 if (ExistingEdgeToNewCallee) {
3730 ExistingEdgeToNewCallee->getContextIds().insert_range(ContextIdsToMove);
3731 ExistingEdgeToNewCallee->AllocTypes |=
Edge->AllocTypes;
3732 assert(
Edge->ContextIds == ContextIdsToMove);
3733 removeEdgeFromGraph(
Edge.get());
3736 Edge->Callee = NewCallee;
3737 NewCallee->CallerEdges.push_back(
Edge);
3739 OldCallee->eraseCallerEdge(
Edge.get());
3746 auto CallerEdgeAllocType = computeAllocType(ContextIdsToMove);
3747 if (ExistingEdgeToNewCallee) {
3750 ExistingEdgeToNewCallee->getContextIds().insert_range(ContextIdsToMove);
3751 ExistingEdgeToNewCallee->AllocTypes |= CallerEdgeAllocType;
3754 auto NewEdge = std::make_shared<ContextEdge>(
3755 NewCallee,
Edge->Caller, CallerEdgeAllocType, ContextIdsToMove);
3756 Edge->Caller->CalleeEdges.push_back(NewEdge);
3757 NewCallee->CallerEdges.push_back(NewEdge);
3761 NewCallee->AllocTypes |= CallerEdgeAllocType;
3763 Edge->AllocTypes = computeAllocType(
Edge->ContextIds);
3768 for (
auto &OldCalleeEdge : OldCallee->CalleeEdges) {
3769 ContextNode *CalleeToUse = OldCalleeEdge->Callee;
3773 if (CalleeToUse == OldCallee) {
3777 if (EdgeIsRecursive) {
3781 CalleeToUse = NewCallee;
3785 DenseSet<uint32_t> EdgeContextIdsToMove =
3787 set_subtract(OldCalleeEdge->getContextIds(), EdgeContextIdsToMove);
3788 OldCalleeEdge->AllocTypes =
3789 computeAllocType(OldCalleeEdge->getContextIds());
3796 if (
auto *NewCalleeEdge = NewCallee->findEdgeFromCallee(CalleeToUse)) {
3797 NewCalleeEdge->getContextIds().insert_range(EdgeContextIdsToMove);
3798 NewCalleeEdge->AllocTypes |= computeAllocType(EdgeContextIdsToMove);
3802 auto NewEdge = std::make_shared<ContextEdge>(
3803 CalleeToUse, NewCallee, computeAllocType(EdgeContextIdsToMove),
3804 EdgeContextIdsToMove);
3805 NewCallee->CalleeEdges.push_back(NewEdge);
3806 NewEdge->Callee->CallerEdges.push_back(NewEdge);
3810 OldCallee->AllocTypes = OldCallee->computeAllocType();
3812 assert((OldCallee->AllocTypes == (uint8_t)AllocationType::None) ==
3813 OldCallee->emptyContextIds());
3817 for (
const auto &OldCalleeEdge : OldCallee->CalleeEdges)
3820 for (
const auto &NewCalleeEdge : NewCallee->CalleeEdges)
3826template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3827void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::
3828 moveCalleeEdgeToNewCaller(
const std::shared_ptr<ContextEdge> &
Edge,
3829 ContextNode *NewCaller) {
3830 auto *OldCallee =
Edge->Callee;
3831 auto *NewCallee = OldCallee;
3834 bool Recursive =
Edge->Caller ==
Edge->Callee;
3836 NewCallee = NewCaller;
3838 ContextNode *OldCaller =
Edge->Caller;
3839 OldCaller->eraseCalleeEdge(
Edge.get());
3843 auto ExistingEdgeToNewCaller = NewCaller->findEdgeFromCallee(NewCallee);
3845 if (ExistingEdgeToNewCaller) {
3848 ExistingEdgeToNewCaller->getContextIds().insert_range(
3849 Edge->getContextIds());
3850 ExistingEdgeToNewCaller->AllocTypes |=
Edge->AllocTypes;
3851 Edge->ContextIds.clear();
3852 Edge->AllocTypes = (uint8_t)AllocationType::None;
3853 OldCallee->eraseCallerEdge(
Edge.get());
3856 Edge->Caller = NewCaller;
3857 NewCaller->CalleeEdges.push_back(
Edge);
3859 assert(NewCallee == NewCaller);
3862 Edge->Callee = NewCallee;
3863 NewCallee->CallerEdges.push_back(
Edge);
3864 OldCallee->eraseCallerEdge(
Edge.get());
3870 NewCaller->AllocTypes |=
Edge->AllocTypes;
3877 bool IsNewNode = NewCaller->CallerEdges.empty();
3886 for (
auto &OldCallerEdge : OldCaller->CallerEdges) {
3887 auto OldCallerCaller = OldCallerEdge->Caller;
3891 OldCallerEdge->getContextIds(),
Edge->getContextIds());
3892 if (OldCaller == OldCallerCaller) {
3893 OldCallerCaller = NewCaller;
3899 set_subtract(OldCallerEdge->getContextIds(), EdgeContextIdsToMove);
3900 OldCallerEdge->AllocTypes =
3901 computeAllocType(OldCallerEdge->getContextIds());
3906 auto *ExistingCallerEdge = NewCaller->findEdgeFromCaller(OldCallerCaller);
3910 if (ExistingCallerEdge) {
3911 ExistingCallerEdge->getContextIds().insert_range(EdgeContextIdsToMove);
3912 ExistingCallerEdge->AllocTypes |=
3913 computeAllocType(EdgeContextIdsToMove);
3916 auto NewEdge = std::make_shared<ContextEdge>(
3917 NewCaller, OldCallerCaller, computeAllocType(EdgeContextIdsToMove),
3918 EdgeContextIdsToMove);
3919 NewCaller->CallerEdges.push_back(NewEdge);
3920 NewEdge->Caller->CalleeEdges.push_back(NewEdge);
3925 OldCaller->AllocTypes = OldCaller->computeAllocType();
3927 assert((OldCaller->AllocTypes == (uint8_t)AllocationType::None) ==
3928 OldCaller->emptyContextIds());
3932 for (
const auto &OldCallerEdge : OldCaller->CallerEdges)
3935 for (
const auto &NewCallerEdge : NewCaller->CallerEdges)
3941template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3942void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::
3943 recursivelyRemoveNoneTypeCalleeEdges(
3944 ContextNode *Node, DenseSet<const ContextNode *> &Visited) {
3949 removeNoneTypeCalleeEdges(Node);
3951 for (
auto *Clone :
Node->Clones)
3952 recursivelyRemoveNoneTypeCalleeEdges(Clone, Visited);
3956 auto CallerEdges =
Node->CallerEdges;
3957 for (
auto &
Edge : CallerEdges) {
3959 if (
Edge->isRemoved()) {
3963 recursivelyRemoveNoneTypeCalleeEdges(
Edge->Caller, Visited);
3968template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3969void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::markBackedges() {
3974 DenseSet<const ContextNode *> Visited;
3975 DenseSet<const ContextNode *> CurrentStack;
3976 for (
auto &Entry : NonAllocationCallToContextNodeMap) {
3978 if (
Node->isRemoved())
3981 if (!
Node->CallerEdges.empty())
3983 markBackedges(Node, Visited, CurrentStack);
3989template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3990void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::markBackedges(
3991 ContextNode *Node, DenseSet<const ContextNode *> &Visited,
3992 DenseSet<const ContextNode *> &CurrentStack) {
3993 auto I = Visited.
insert(Node);
3997 for (
auto &CalleeEdge :
Node->CalleeEdges) {
3998 auto *
Callee = CalleeEdge->Callee;
3999 if (Visited.
count(Callee)) {
4002 if (CurrentStack.
count(Callee))
4003 CalleeEdge->IsBackedge =
true;
4006 CurrentStack.
insert(Callee);
4007 markBackedges(Callee, Visited, CurrentStack);
4008 CurrentStack.
erase(Callee);
4012template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
4013void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::identifyClones() {
4014 DenseSet<const ContextNode *> Visited;
4015 for (
auto &Entry : AllocationCallToContextNodeMap) {
4017 identifyClones(
Entry.second, Visited,
Entry.second->getContextIds());
4020 for (
auto &Entry : AllocationCallToContextNodeMap)
4021 recursivelyRemoveNoneTypeCalleeEdges(
Entry.second, Visited);
4034template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
4035void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::identifyClones(
4036 ContextNode *Node, DenseSet<const ContextNode *> &Visited,
4037 const DenseSet<uint32_t> &AllocContextIds) {
4047 if (!
Node->hasCall())
4066 auto CallerEdges =
Node->CallerEdges;
4067 for (
auto &
Edge : CallerEdges) {
4069 if (
Edge->isRemoved()) {
4075 if (
Edge->IsBackedge) {
4082 if (!Visited.
count(
Edge->Caller) && !
Edge->Caller->CloneOf) {
4083 identifyClones(
Edge->Caller, Visited, AllocContextIds);
4106 const unsigned AllocTypeCloningPriority[] = { 3, 4,
4110 [&](
const std::shared_ptr<ContextEdge> &
A,
4111 const std::shared_ptr<ContextEdge> &
B) {
4114 if (A->ContextIds.empty())
4120 if (B->ContextIds.empty())
4123 if (A->AllocTypes == B->AllocTypes)
4126 return A->Caller->NodeId < B->Caller->NodeId;
4127 return AllocTypeCloningPriority[A->AllocTypes] <
4128 AllocTypeCloningPriority[B->AllocTypes];
4131 assert(
Node->AllocTypes != (uint8_t)AllocationType::None);
4133 DenseSet<uint32_t> RecursiveContextIds;
4138 DenseSet<uint32_t> AllCallerContextIds;
4139 for (
auto &CE :
Node->CallerEdges) {
4142 AllCallerContextIds.
reserve(
CE->getContextIds().size());
4143 for (
auto Id :
CE->getContextIds())
4144 if (!AllCallerContextIds.
insert(Id).second)
4145 RecursiveContextIds.
insert(Id);
4155 auto CallerEdges =
Node->CallerEdges;
4156 for (
auto &CallerEdge : CallerEdges) {
4158 if (CallerEdge->isRemoved()) {
4162 assert(CallerEdge->Callee == Node);
4171 if (!CallerEdge->Caller->hasCall())
4176 auto CallerEdgeContextsForAlloc =
4178 if (!RecursiveContextIds.
empty())
4179 CallerEdgeContextsForAlloc =
4181 if (CallerEdgeContextsForAlloc.empty())
4184 auto CallerAllocTypeForAlloc = computeAllocType(CallerEdgeContextsForAlloc);
4188 std::vector<uint8_t> CalleeEdgeAllocTypesForCallerEdge;
4189 CalleeEdgeAllocTypesForCallerEdge.reserve(
Node->CalleeEdges.size());
4190 for (
auto &CalleeEdge :
Node->CalleeEdges)
4191 CalleeEdgeAllocTypesForCallerEdge.push_back(intersectAllocTypes(
4192 CalleeEdge->getContextIds(), CallerEdgeContextsForAlloc));
4208 assert(CallerEdge->AllocTypes != (uint8_t)AllocationType::None);
4209 assert(
Node->AllocTypes != (uint8_t)AllocationType::None);
4210 if (!CallerEdge->IsBackedge &&
4211 allocTypeToUse(CallerAllocTypeForAlloc) ==
4212 allocTypeToUse(
Node->AllocTypes) &&
4213 allocTypesMatch<DerivedCCG, FuncTy, CallTy>(
4214 CalleeEdgeAllocTypesForCallerEdge,
Node->CalleeEdges)) {
4218 if (CallerEdge->IsBackedge) {
4222 DeferredBackedges++;
4235 if (CallerEdge->IsBackedge && !CallerEdge->Caller->CloneOf &&
4236 !Visited.
count(CallerEdge->Caller)) {
4237 const auto OrigIdCount = CallerEdge->getContextIds().size();
4240 identifyClones(CallerEdge->Caller, Visited, CallerEdgeContextsForAlloc);
4241 removeNoneTypeCalleeEdges(CallerEdge->Caller);
4245 bool UpdatedEdge =
false;
4246 if (OrigIdCount > CallerEdge->getContextIds().size()) {
4247 for (
auto E :
Node->CallerEdges) {
4249 if (
E->Caller->CloneOf != CallerEdge->Caller)
4253 auto CallerEdgeContextsForAllocNew =
4255 if (CallerEdgeContextsForAllocNew.empty())
4265 CallerEdgeContextsForAlloc.swap(CallerEdgeContextsForAllocNew);
4275 if (CallerEdge->isRemoved())
4285 CallerEdgeContextsForAlloc, CallerEdge->getContextIds());
4286 if (CallerEdgeContextsForAlloc.empty())
4291 CallerAllocTypeForAlloc = computeAllocType(CallerEdgeContextsForAlloc);
4292 CalleeEdgeAllocTypesForCallerEdge.clear();
4293 for (
auto &CalleeEdge :
Node->CalleeEdges) {
4294 CalleeEdgeAllocTypesForCallerEdge.push_back(intersectAllocTypes(
4295 CalleeEdge->getContextIds(), CallerEdgeContextsForAlloc));
4301 ContextNode *Clone =
nullptr;
4302 for (
auto *CurClone :
Node->Clones) {
4303 if (allocTypeToUse(CurClone->AllocTypes) !=
4304 allocTypeToUse(CallerAllocTypeForAlloc))
4311 assert(!BothSingleAlloc ||
4312 CurClone->AllocTypes == CallerAllocTypeForAlloc);
4318 if (BothSingleAlloc || allocTypesMatchClone<DerivedCCG, FuncTy, CallTy>(
4319 CalleeEdgeAllocTypesForCallerEdge, CurClone)) {
4327 moveEdgeToExistingCalleeClone(CallerEdge, Clone,
false,
4328 CallerEdgeContextsForAlloc);
4330 Clone = moveEdgeToNewCalleeClone(CallerEdge, CallerEdgeContextsForAlloc);
4333 assert(Clone->AllocTypes != (uint8_t)AllocationType::None);
4340 assert(
Node->AllocTypes != (uint8_t)AllocationType::None);
4346void ModuleCallsiteContextGraph::updateAllocationCall(
4351 "memprof", AllocTypeString);
4354 .emit(OptimizationRemark(
DEBUG_TYPE,
"MemprofAttribute",
Call.call())
4355 <<
ore::NV(
"AllocationCall",
Call.call()) <<
" in clone "
4357 <<
" marked with memprof allocation attribute "
4358 <<
ore::NV(
"Attribute", AllocTypeString));
4361void IndexCallsiteContextGraph::updateAllocationCall(CallInfo &
Call,
4365 assert(AI->Versions.size() >
Call.cloneNo());
4370ModuleCallsiteContextGraph::getAllocationCallType(
const CallInfo &
Call)
const {
4372 if (!CB->getAttributes().hasFnAttr(
"memprof"))
4373 return AllocationType::None;
4374 return CB->getAttributes().getFnAttr(
"memprof").getValueAsString() ==
"cold"
4375 ? AllocationType::Cold
4376 : AllocationType::NotCold;
4380IndexCallsiteContextGraph::getAllocationCallType(
const CallInfo &
Call)
const {
4382 assert(AI->Versions.size() >
Call.cloneNo());
4386void ModuleCallsiteContextGraph::updateCall(CallInfo &CallerCall,
4387 FuncInfo CalleeFunc) {
4388 auto *CurF = getCalleeFunc(CallerCall.call());
4389 auto NewCalleeCloneNo = CalleeFunc.cloneNo();
4396 if (CurCalleeCloneNo != NewCalleeCloneNo) {
4398 << CurCalleeCloneNo <<
" now " << NewCalleeCloneNo
4400 MismatchedCloneAssignments++;
4403 if (NewCalleeCloneNo > 0)
4404 cast<CallBase>(CallerCall.call())->setCalledFunction(CalleeFunc.func());
4405 OREGetter(CallerCall.call()->getFunction())
4406 .emit(OptimizationRemark(
DEBUG_TYPE,
"MemprofCall", CallerCall.call())
4407 <<
ore::NV(
"Call", CallerCall.call()) <<
" in clone "
4408 <<
ore::NV(
"Caller", CallerCall.call()->getFunction())
4409 <<
" assigned to call function clone "
4410 <<
ore::NV(
"Callee", CalleeFunc.func()));
4413void IndexCallsiteContextGraph::updateCall(CallInfo &CallerCall,
4414 FuncInfo CalleeFunc) {
4417 "Caller cannot be an allocation which should not have profiled calls");
4418 assert(CI->Clones.size() > CallerCall.cloneNo());
4419 auto NewCalleeCloneNo = CalleeFunc.cloneNo();
4420 auto &CurCalleeCloneNo = CI->Clones[CallerCall.cloneNo()];
4425 if (CurCalleeCloneNo != 0 && CurCalleeCloneNo != NewCalleeCloneNo) {
4427 << CurCalleeCloneNo <<
" now " << NewCalleeCloneNo
4429 MismatchedCloneAssignments++;
4431 CurCalleeCloneNo = NewCalleeCloneNo;
4443 SP->replaceLinkageName(MDName);
4447 TempDISubprogram NewDecl = Decl->
clone();
4448 NewDecl->replaceLinkageName(MDName);
4452CallsiteContextGraph<ModuleCallsiteContextGraph,
Function,
4454ModuleCallsiteContextGraph::cloneFunctionForCallsite(
4455 FuncInfo &Func, CallInfo &
Call, DenseMap<CallInfo, CallInfo> &CallMap,
4456 std::vector<CallInfo> &CallsWithMetadataInFunc,
unsigned CloneNo) {
4461 assert(!
Func.func()->getParent()->getFunction(Name));
4462 NewFunc->setName(Name);
4464 for (
auto &Inst : CallsWithMetadataInFunc) {
4466 assert(Inst.cloneNo() == 0);
4469 OREGetter(
Func.func())
4470 .emit(OptimizationRemark(
DEBUG_TYPE,
"MemprofClone",
Func.func())
4471 <<
"created clone " <<
ore::NV(
"NewFunction", NewFunc));
4472 return {NewFunc, CloneNo};
4475CallsiteContextGraph<IndexCallsiteContextGraph, FunctionSummary,
4476 IndexCall>::FuncInfo
4477IndexCallsiteContextGraph::cloneFunctionForCallsite(
4478 FuncInfo &Func, CallInfo &
Call, DenseMap<CallInfo, CallInfo> &CallMap,
4479 std::vector<CallInfo> &CallsWithMetadataInFunc,
unsigned CloneNo) {
4493 for (
auto &Inst : CallsWithMetadataInFunc) {
4495 assert(Inst.cloneNo() == 0);
4497 assert(AI->Versions.size() == CloneNo);
4500 AI->Versions.push_back(0);
4503 assert(CI && CI->Clones.size() == CloneNo);
4506 CI->Clones.push_back(0);
4508 CallMap[Inst] = {Inst.call(), CloneNo};
4510 return {
Func.func(), CloneNo};
4527template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
4528void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::mergeClones() {
4534 DenseMap<uint32_t, ContextNode *> ContextIdToAllocationNode;
4535 for (
auto &Entry : AllocationCallToContextNodeMap) {
4537 for (
auto Id :
Node->getContextIds())
4538 ContextIdToAllocationNode[
Id] =
Node->getOrigNode();
4539 for (
auto *Clone :
Node->Clones) {
4540 for (
auto Id : Clone->getContextIds())
4541 ContextIdToAllocationNode[
Id] = Clone->getOrigNode();
4548 DenseSet<const ContextNode *> Visited;
4549 for (
auto &Entry : AllocationCallToContextNodeMap) {
4552 mergeClones(Node, Visited, ContextIdToAllocationNode);
4558 auto Clones =
Node->Clones;
4559 for (
auto *Clone : Clones)
4560 mergeClones(Clone, Visited, ContextIdToAllocationNode);
4564 dbgs() <<
"CCG after merging:\n";
4568 exportToDot(
"aftermerge");
4576template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
4577void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::mergeClones(
4578 ContextNode *Node, DenseSet<const ContextNode *> &Visited,
4579 DenseMap<uint32_t, ContextNode *> &ContextIdToAllocationNode) {
4589 bool FoundUnvisited =
true;
4591 while (FoundUnvisited) {
4593 FoundUnvisited =
false;
4596 auto CallerEdges =
Node->CallerEdges;
4597 for (
auto CallerEdge : CallerEdges) {
4599 if (CallerEdge->Callee != Node)
4604 FoundUnvisited =
true;
4605 mergeClones(CallerEdge->Caller, Visited, ContextIdToAllocationNode);
4609 TotalMergeInvokes++;
4610 TotalMergeIters += Iters;
4611 if (Iters > MaxMergeIters)
4612 MaxMergeIters = Iters;
4615 mergeNodeCalleeClones(Node, Visited, ContextIdToAllocationNode);
4618template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
4619void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::mergeNodeCalleeClones(
4620 ContextNode *Node, DenseSet<const ContextNode *> &Visited,
4621 DenseMap<uint32_t, ContextNode *> &ContextIdToAllocationNode) {
4623 if (
Node->emptyContextIds())
4628 MapVector<ContextNode *, std::vector<std::shared_ptr<ContextEdge>>>
4629 OrigNodeToCloneEdges;
4630 for (
const auto &
E :
Node->CalleeEdges) {
4635 OrigNodeToCloneEdges[
Base].push_back(
E);
4641 auto CalleeCallerEdgeLessThan = [](
const std::shared_ptr<ContextEdge> &
A,
4642 const std::shared_ptr<ContextEdge> &
B) {
4643 if (
A->Callee->CallerEdges.size() !=
B->Callee->CallerEdges.size())
4644 return A->Callee->CallerEdges.size() <
B->Callee->CallerEdges.size();
4645 if (
A->Callee->CloneOf && !
B->Callee->CloneOf)
4647 else if (!
A->Callee->CloneOf &&
B->Callee->CloneOf)
4650 return A->Callee->NodeId <
B->Callee->NodeId;
4655 for (
auto Entry : OrigNodeToCloneEdges) {
4658 auto &CalleeEdges =
Entry.second;
4659 auto NumCalleeClones = CalleeEdges.size();
4661 if (NumCalleeClones == 1)
4672 DenseSet<ContextNode *> OtherCallersToShareMerge;
4673 findOtherCallersToShareMerge(Node, CalleeEdges, ContextIdToAllocationNode,
4674 OtherCallersToShareMerge);
4679 ContextNode *MergeNode =
nullptr;
4680 DenseMap<ContextNode *, unsigned> CallerToMoveCount;
4681 for (
auto CalleeEdge : CalleeEdges) {
4682 auto *OrigCallee = CalleeEdge->Callee;
4688 if (CalleeEdge->Callee->CallerEdges.size() == 1) {
4689 MergeNode = OrigCallee;
4690 NonNewMergedNodes++;
4697 if (!OtherCallersToShareMerge.
empty()) {
4698 bool MoveAllCallerEdges =
true;
4699 for (
auto CalleeCallerE : OrigCallee->CallerEdges) {
4700 if (CalleeCallerE == CalleeEdge)
4702 if (!OtherCallersToShareMerge.
contains(CalleeCallerE->Caller)) {
4703 MoveAllCallerEdges =
false;
4709 if (MoveAllCallerEdges) {
4710 MergeNode = OrigCallee;
4711 NonNewMergedNodes++;
4718 assert(MergeNode != OrigCallee);
4719 moveEdgeToExistingCalleeClone(CalleeEdge, MergeNode,
4722 MergeNode = moveEdgeToNewCalleeClone(CalleeEdge);
4727 if (!OtherCallersToShareMerge.
empty()) {
4731 auto OrigCalleeCallerEdges = OrigCallee->CallerEdges;
4732 for (
auto &CalleeCallerE : OrigCalleeCallerEdges) {
4733 if (CalleeCallerE == CalleeEdge)
4735 if (!OtherCallersToShareMerge.
contains(CalleeCallerE->Caller))
4737 CallerToMoveCount[CalleeCallerE->Caller]++;
4738 moveEdgeToExistingCalleeClone(CalleeCallerE, MergeNode,
4742 removeNoneTypeCalleeEdges(OrigCallee);
4743 removeNoneTypeCalleeEdges(MergeNode);
4761template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
4762void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::
4763 findOtherCallersToShareMerge(
4765 std::vector<std::shared_ptr<ContextEdge>> &CalleeEdges,
4766 DenseMap<uint32_t, ContextNode *> &ContextIdToAllocationNode,
4767 DenseSet<ContextNode *> &OtherCallersToShareMerge) {
4768 auto NumCalleeClones = CalleeEdges.size();
4771 DenseMap<ContextNode *, unsigned> OtherCallersToSharedCalleeEdgeCount;
4774 unsigned PossibleOtherCallerNodes = 0;
4778 if (CalleeEdges[0]->
Callee->CallerEdges.size() < 2)
4784 DenseMap<ContextEdge *, DenseSet<ContextNode *>> CalleeEdgeToAllocNodes;
4785 for (
auto CalleeEdge : CalleeEdges) {
4786 assert(CalleeEdge->Callee->CallerEdges.size() > 1);
4789 for (
auto CalleeCallerEdges : CalleeEdge->Callee->CallerEdges) {
4790 if (CalleeCallerEdges->Caller == Node) {
4791 assert(CalleeCallerEdges == CalleeEdge);
4794 OtherCallersToSharedCalleeEdgeCount[CalleeCallerEdges->Caller]++;
4797 if (OtherCallersToSharedCalleeEdgeCount[CalleeCallerEdges->Caller] ==
4799 PossibleOtherCallerNodes++;
4803 for (
auto Id : CalleeEdge->getContextIds()) {
4804 auto *
Alloc = ContextIdToAllocationNode.
lookup(Id);
4808 MissingAllocForContextId++;
4811 CalleeEdgeToAllocNodes[CalleeEdge.get()].
insert(
Alloc);
4818 for (
auto CalleeEdge : CalleeEdges) {
4819 assert(CalleeEdge->Callee->CallerEdges.size() > 1);
4821 if (!PossibleOtherCallerNodes)
4823 auto &CurCalleeAllocNodes = CalleeEdgeToAllocNodes[CalleeEdge.get()];
4825 for (
auto &CalleeCallerE : CalleeEdge->Callee->CallerEdges) {
4827 if (CalleeCallerE == CalleeEdge)
4831 if (OtherCallersToSharedCalleeEdgeCount[CalleeCallerE->Caller] !=
4836 for (
auto Id : CalleeCallerE->getContextIds()) {
4837 auto *
Alloc = ContextIdToAllocationNode.
lookup(Id);
4842 if (!CurCalleeAllocNodes.contains(
Alloc)) {
4843 OtherCallersToSharedCalleeEdgeCount[CalleeCallerE->Caller] = 0;
4844 PossibleOtherCallerNodes--;
4851 if (!PossibleOtherCallerNodes)
4856 for (
auto &[OtherCaller,
Count] : OtherCallersToSharedCalleeEdgeCount) {
4857 if (
Count != NumCalleeClones)
4859 OtherCallersToShareMerge.
insert(OtherCaller);
4863template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
4864const typename CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode *
4865CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::resolveImplicitSource(
4866 const ContextNode *Node,
const ImplicitSource &Src) {
4867 if (!Src.Source->emptyContextIds())
4873 if (
Node->hasContextId(Src.ContextId))
4875 for (
const ContextNode *
C :
Node->Clones)
4876 if (
C->hasContextId(Src.ContextId))
4881 assert(
false &&
"trimmed context id on no clone of its callsite node");
4923template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
4924void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::assignImplicitCallers(
4925 ContextNode *Node,
const DenseSet<uint32_t> &TrimmedContextIds,
4926 DenseMap<ContextNode *, FuncInfo> &CallsiteToCalleeFuncCloneMap,
4927 const std::map<FuncInfo, ContextNode *> &FuncCloneToCurNodeCloneMap,
4928 ImplicitSourceMap &ImplicitSources) {
4929 auto HasEdgeToNodeFamily = [&](
const ContextNode *
N) {
4931 [&](
const std::shared_ptr<ContextEdge> &
E) {
4932 return E->Callee->getOrigNode() == Node;
4966 auto ImpliesContextIds =
4967 [&](ContextNode *
K, ContextNode *
Y,
const DenseSet<uint32_t> &Ids,
4968 DenseMap<std::pair<ContextNode *, ContextNode *>, DenseSet<uint32_t>>
4970 auto &&ImpliesContextIds) ->
bool {
4971 assert(
K->getOrigNode() ==
Y->getOrigNode());
4972 DenseSet<uint32_t> &VisitedIds = Visited[{
K,
Y}];
4973 DenseSet<uint32_t> NewIds;
4975 if (VisitedIds.
insert(Id).second)
4983 DenseSet<uint32_t> IdsOnCallerEdges;
4984 for (
auto &YEdge :
Y->CallerEdges) {
4985 DenseSet<uint32_t> IdsFromZ =
4987 if (IdsFromZ.
empty())
4990 IdsFromCallers.
push_back({YEdge->Caller, std::move(IdsFromZ)});
4994 if (IdsOnCallerEdges.
size() < NewIds.
size())
4998 for (
auto &[Z, IdsFromZ] : IdsFromCallers) {
4999 for (
auto &KEdge :
K->CallerEdges) {
5000 ContextNode *
C = KEdge->Caller;
5001 if (
C == Z ||
C->getOrigNode() !=
Z->getOrigNode())
5005 DenseSet<uint32_t> IdsNotOnC;
5006 for (
auto Id : IdsFromZ)
5007 if (!
C->hasContextId(Id))
5009 if (!IdsNotOnC.
empty() &&
5010 ImpliesContextIds(
C, Z, IdsNotOnC, Visited, ImpliesContextIds))
5019 auto SameVersion = [&](
const ContextNode *
A,
const ContextNode *
B) {
5022 if (
Node->IsAllocation)
5023 return allocTypeToUse(
A->AllocTypes) == allocTypeToUse(
B->AllocTypes);
5024 auto AIt = CallsiteToCalleeFuncCloneMap.
find(
A);
5025 auto BIt = CallsiteToCalleeFuncCloneMap.
find(
B);
5026 return AIt != CallsiteToCalleeFuncCloneMap.
end() &&
5027 BIt != CallsiteToCalleeFuncCloneMap.
end() &&
5028 AIt->second == BIt->second;
5032 DenseSet<const ContextNode *> ImplicitCallers;
5037 auto ProcessCaller = [&](
const std::shared_ptr<ContextEdge> &
Edge) {
5038 ContextNode *
Y =
Edge->Caller;
5039 ContextNode *Orig =
Y->getOrigNode();
5040 if (!
Y->hasCall() || Orig->Clones.empty())
5042 DenseSet<uint32_t> TrimmedIds =
5044 if (TrimmedIds.
empty())
5046 auto YIt = CallsiteToCalleeFuncCloneMap.
find(
Y);
5047 if (YIt == CallsiteToCalleeFuncCloneMap.
end())
5050 const FuncInfo YFuncClone = YIt->second;
5053 auto RequiredIt = FuncCloneToCurNodeCloneMap.find(YFuncClone);
5054 assert(RequiredIt != FuncCloneToCurNodeCloneMap.end());
5055 const ContextNode *
Required = RequiredIt->second;
5063 auto RecordImplicitSource = [&](
const FuncInfo &
F,
const ContextNode *Src) {
5066 if (
F.cloneNo() != 0)
5067 ImplicitSources.try_emplace({
Node,
F.cloneNo()},
5068 ImplicitSource{Src, *TrimmedIds.
begin()});
5074 if (K ==
Y || !
K->hasCall() ||
K->emptyContextIds() ||
5075 HasEdgeToNodeFamily(K) || ImplicitCallers.
contains(K))
5077 DenseMap<std::pair<ContextNode *, ContextNode *>, DenseSet<uint32_t>>
5079 if (!ImpliesContextIds(K,
Y, TrimmedIds, Visited, ImpliesContextIds))
5082 ImplicitCallers.
insert(K);
5083 auto KIt = CallsiteToCalleeFuncCloneMap.
find(K);
5084 if (KIt == CallsiteToCalleeFuncCloneMap.
end()) {
5087 CallsiteToCalleeFuncCloneMap[
K] = YFuncClone;
5088 RecordImplicitSource(YFuncClone, Required);
5089 NumImplicitCallersAssigned++;
5092 const FuncInfo KFuncClone = KIt->second;
5093 if (KFuncClone == YFuncClone) {
5095 RecordImplicitSource(YFuncClone, Required);
5098 unsigned KCloneNo = KFuncClone.cloneNo();
5099 auto KNodeCloneIt = FuncCloneToCurNodeCloneMap.find(KFuncClone);
5100 if (KNodeCloneIt == FuncCloneToCurNodeCloneMap.end()) {
5102 if (KCloneNo == 0) {
5106 if (
Node->IsAllocation) {
5107 if (allocTypeToUse(
Required->AllocTypes) == AllocationType::NotCold)
5109 }
else if (
auto It = CallsiteToCalleeFuncCloneMap.
find(Required);
5110 It != CallsiteToCalleeFuncCloneMap.
end() &&
5111 It->second.cloneNo() == 0)
5117 auto [It,
Inserted] = ImplicitSources.try_emplace(
5120 NumImplicitCallersSourced++;
5121 if (SameVersion(It->second.Source, Required))
5124 }
else if (SameVersion(KNodeCloneIt->second, Required)) {
5127 RecordImplicitSource(KFuncClone, KNodeCloneIt->second);
5131 NumImplicitCallersUnresolved++;
5137 for (
auto &
Edge :
Node->CallerEdges)
5138 ProcessCaller(
Edge);
5139 for (
auto *Clone :
Node->Clones)
5140 for (
auto &
Edge : Clone->CallerEdges)
5141 ProcessCaller(
Edge);
5185template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
5186bool CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::assignFunctions() {
5194 DenseSet<uint32_t> TrimmedContextIds;
5195 for (
auto &NodePtr : NodeOwner) {
5196 ContextNode *
N = NodePtr.get();
5197 if (
N->IsAllocation ||
N->CalleeEdges.empty() ||
5198 (
N->CallerEdges.empty() &&
N->Clones.empty() && !
N->CloneOf))
5203 DenseSet<uint32_t> CallerIds;
5204 for (
auto &
E :
N->CallerEdges)
5206 for (
auto &CalleeEdge :
N->CalleeEdges)
5207 for (
auto Id : CalleeEdge->getContextIds())
5209 TrimmedContextIds.
insert(Id);
5211 NumTrimmedContextIds += TrimmedContextIds.
size();
5215 DenseMap<ContextNode *, FuncInfo> CallsiteToCalleeFuncCloneMap;
5219 auto RecordCalleeFuncOfCallsite = [&](ContextNode *
Caller,
5220 const FuncInfo &CalleeFunc) {
5222 CallsiteToCalleeFuncCloneMap[
Caller] = CalleeFunc;
5226 struct FuncCloneInfo {
5231 DenseMap<CallInfo, CallInfo> CallMap;
5267 struct UnassignedCallCloneInfo {
5269 const ContextNode *
Source =
nullptr;
5271 DenseMap<const ContextNode *, std::map<unsigned, UnassignedCallCloneInfo>>
5272 UnassignedCallClones;
5276 for (
auto &[Func, CallsWithMetadata] : FuncToCallsWithMetadata) {
5277 FuncInfo OrigFunc(Func);
5282 std::vector<FuncCloneInfo> FuncCloneInfos;
5289 std::vector<unsigned> FuncCloneParent;
5292 ImplicitSourceMap ImplicitSources;
5299 struct OrigCallsInfo {
5303 DenseMap<const ContextNode *, OrigCallsInfo> OrigNodeToOrigCalls;
5304 for (
auto &
Call : CallsWithMetadata) {
5305 ContextNode *
Node = getNodeForInst(
Call);
5307 OrigNodeToOrigCalls[
Node] = {
Call,
Node->MatchingCalls};
5309 for (
auto &
Call : CallsWithMetadata) {
5310 ContextNode *
Node = getNodeForInst(
Call);
5314 if (!Node ||
Node->Clones.empty())
5317 "Not having a call should have prevented cloning");
5321 std::map<FuncInfo, ContextNode *> FuncCloneToCurNodeCloneMap;
5325 auto AssignCallsiteCloneToFuncClone = [&](
const FuncInfo &FuncClone,
5327 ContextNode *CallsiteClone,
5330 FuncCloneToCurNodeCloneMap[FuncClone] = CallsiteClone;
5332 assert(FuncCloneInfos.size() > FuncClone.cloneNo());
5333 DenseMap<CallInfo, CallInfo> &CallMap =
5334 FuncCloneInfos[FuncClone.cloneNo()].CallMap;
5335 CallInfo CallClone(
Call);
5336 if (
auto It = CallMap.
find(
Call); It != CallMap.
end())
5337 CallClone = It->second;
5338 CallsiteClone->setCall(CallClone);
5344 CallsiteClone->MatchingCalls.clear();
5345 for (
const CallInfo &OrigMatchingCall :
5346 OrigNodeToOrigCalls[Node].MatchingCalls) {
5347 CallInfo CallClone(OrigMatchingCall);
5348 if (
auto It = CallMap.
find(OrigMatchingCall); It != CallMap.
end())
5349 CallClone = It->second;
5350 CallsiteClone->MatchingCalls.push_back(CallClone);
5358 auto MoveEdgeToNewCalleeCloneAndSetUp =
5359 [&](
const std::shared_ptr<ContextEdge> &
Edge) {
5360 ContextNode *OrigCallee =
Edge->Callee;
5361 ContextNode *NewClone = moveEdgeToNewCalleeClone(
Edge);
5362 removeNoneTypeCalleeEdges(NewClone);
5363 assert(NewClone->AllocTypes != (uint8_t)AllocationType::None);
5367 if (CallsiteToCalleeFuncCloneMap.
count(OrigCallee))
5368 RecordCalleeFuncOfCallsite(
5369 NewClone, CallsiteToCalleeFuncCloneMap[OrigCallee]);
5376 std::deque<ContextNode *> ClonesWorklist;
5378 if (!
Node->emptyContextIds())
5379 ClonesWorklist.push_back(Node);
5385 unsigned NodeCloneCount = 0;
5386 while (!ClonesWorklist.empty()) {
5387 ContextNode *Clone = ClonesWorklist.front();
5388 ClonesWorklist.pop_front();
5397 if (FuncCloneInfos.size() < NodeCloneCount) {
5399 if (NodeCloneCount == 1) {
5404 Clone->CallerEdges, [&](
const std::shared_ptr<ContextEdge> &
E) {
5405 return CallsiteToCalleeFuncCloneMap.count(E->Caller);
5409 FuncCloneInfos.push_back(
5410 {OrigFunc, DenseMap<CallInfo, CallInfo>()});
5411 FuncCloneParent.push_back(0);
5412 AssignCallsiteCloneToFuncClone(
5413 OrigFunc,
Call, Clone,
5414 AllocationCallToContextNodeMap.count(
Call));
5415 for (
auto &CE : Clone->CallerEdges) {
5417 if (!
CE->Caller->hasCall())
5419 RecordCalleeFuncOfCallsite(
CE->Caller, OrigFunc);
5429 FuncInfo PreviousAssignedFuncClone;
5431 Clone->CallerEdges, [&](
const std::shared_ptr<ContextEdge> &
E) {
5432 return CallsiteToCalleeFuncCloneMap.count(E->Caller);
5434 bool CallerAssignedToCloneOfFunc =
false;
5435 if (EI != Clone->CallerEdges.end()) {
5436 const std::shared_ptr<ContextEdge> &
Edge = *EI;
5437 PreviousAssignedFuncClone =
5438 CallsiteToCalleeFuncCloneMap[
Edge->Caller];
5439 CallerAssignedToCloneOfFunc =
true;
5444 DenseMap<CallInfo, CallInfo> NewCallMap;
5445 unsigned CloneNo = FuncCloneInfos.size();
5446 assert(CloneNo > 0 &&
"Clone 0 is the original function, which "
5447 "should already exist in the map");
5448 FuncInfo NewFuncClone = cloneFunctionForCallsite(
5449 OrigFunc,
Call, NewCallMap, CallsWithMetadata, CloneNo);
5450 FuncCloneInfos.push_back({NewFuncClone, std::move(NewCallMap)});
5453 FuncCloneParent.push_back(CallerAssignedToCloneOfFunc
5454 ? PreviousAssignedFuncClone.cloneNo()
5456 FunctionClonesAnalysis++;
5462 if (!CallerAssignedToCloneOfFunc) {
5463 AssignCallsiteCloneToFuncClone(
5464 NewFuncClone,
Call, Clone,
5465 AllocationCallToContextNodeMap.count(
Call));
5466 for (
auto &CE : Clone->CallerEdges) {
5468 if (!
CE->Caller->hasCall())
5470 RecordCalleeFuncOfCallsite(
CE->Caller, NewFuncClone);
5482 auto CallerEdges = Clone->CallerEdges;
5483 for (
auto CE : CallerEdges) {
5485 if (
CE->isRemoved()) {
5491 if (!
CE->Caller->hasCall())
5494 if (!CallsiteToCalleeFuncCloneMap.
count(
CE->Caller) ||
5498 CallsiteToCalleeFuncCloneMap[
CE->Caller] !=
5499 PreviousAssignedFuncClone)
5502 RecordCalleeFuncOfCallsite(
CE->Caller, NewFuncClone);
5515 auto CalleeEdges =
CE->Caller->CalleeEdges;
5516 for (
auto CalleeEdge : CalleeEdges) {
5519 if (CalleeEdge->isRemoved()) {
5524 ContextNode *
Callee = CalleeEdge->Callee;
5528 if (Callee == Clone || !
Callee->hasCall())
5533 if (Callee == CalleeEdge->Caller)
5535 ContextNode *NewClone =
5536 MoveEdgeToNewCalleeCloneAndSetUp(CalleeEdge);
5539 removeNoneTypeCalleeEdges(Callee);
5552 auto &OrigCalls = OrigNodeToOrigCalls[
Callee->getOrigNode()];
5553 DenseMap<CallInfo, CallInfo> &CallMap =
5554 FuncCloneInfos[NewFuncClone.cloneNo()].CallMap;
5556 CallInfo NewCall(CallMap[OrigCalls.Call]);
5558 NewClone->setCall(NewCall);
5560 NewClone->MatchingCalls.clear();
5561 for (
auto &OrigMatchingCall : OrigCalls.MatchingCalls) {
5563 CallInfo NewCall(CallMap[OrigMatchingCall]);
5565 NewClone->MatchingCalls.push_back(NewCall);
5574 auto FindFirstAvailFuncClone = [&]() {
5579 for (
auto &CF : FuncCloneInfos) {
5580 if (!FuncCloneToCurNodeCloneMap.count(CF.FuncClone))
5581 return CF.FuncClone;
5584 "Expected an available func clone for this callsite clone");
5601 std::map<FuncInfo, ContextNode *> FuncCloneToNewCallsiteCloneMap;
5602 FuncInfo FuncCloneAssignedToCurCallsiteClone;
5606 auto CloneCallerEdges = Clone->CallerEdges;
5607 for (
auto &
Edge : CloneCallerEdges) {
5611 if (
Edge->isRemoved())
5614 if (!
Edge->Caller->hasCall())
5618 if (CallsiteToCalleeFuncCloneMap.
count(
Edge->Caller)) {
5619 FuncInfo FuncCloneCalledByCaller =
5620 CallsiteToCalleeFuncCloneMap[
Edge->Caller];
5630 if ((FuncCloneToCurNodeCloneMap.count(FuncCloneCalledByCaller) &&
5631 FuncCloneToCurNodeCloneMap[FuncCloneCalledByCaller] !=
5639 (FuncCloneAssignedToCurCallsiteClone &&
5640 FuncCloneAssignedToCurCallsiteClone !=
5641 FuncCloneCalledByCaller)) {
5656 if (FuncCloneToNewCallsiteCloneMap.count(
5657 FuncCloneCalledByCaller)) {
5658 ContextNode *NewClone =
5659 FuncCloneToNewCallsiteCloneMap[FuncCloneCalledByCaller];
5660 moveEdgeToExistingCalleeClone(
Edge, NewClone);
5662 removeNoneTypeCalleeEdges(NewClone);
5665 ContextNode *NewClone = MoveEdgeToNewCalleeCloneAndSetUp(
Edge);
5666 FuncCloneToNewCallsiteCloneMap[FuncCloneCalledByCaller] =
5669 ClonesWorklist.push_back(NewClone);
5673 removeNoneTypeCalleeEdges(Clone);
5681 if (!FuncCloneAssignedToCurCallsiteClone) {
5682 FuncCloneAssignedToCurCallsiteClone = FuncCloneCalledByCaller;
5684 AssignCallsiteCloneToFuncClone(
5685 FuncCloneCalledByCaller,
Call, Clone,
5686 AllocationCallToContextNodeMap.count(
Call));
5690 assert(FuncCloneAssignedToCurCallsiteClone ==
5691 FuncCloneCalledByCaller);
5700 if (!FuncCloneAssignedToCurCallsiteClone) {
5701 FuncCloneAssignedToCurCallsiteClone = FindFirstAvailFuncClone();
5702 assert(FuncCloneAssignedToCurCallsiteClone);
5704 AssignCallsiteCloneToFuncClone(
5705 FuncCloneAssignedToCurCallsiteClone,
Call, Clone,
5706 AllocationCallToContextNodeMap.count(
Call));
5708 assert(FuncCloneToCurNodeCloneMap
5709 [FuncCloneAssignedToCurCallsiteClone] == Clone);
5711 RecordCalleeFuncOfCallsite(
Edge->Caller,
5712 FuncCloneAssignedToCurCallsiteClone);
5732 if (!FuncCloneAssignedToCurCallsiteClone) {
5733 FuncCloneAssignedToCurCallsiteClone = FindFirstAvailFuncClone();
5734 assert(FuncCloneAssignedToCurCallsiteClone &&
5735 "No available func clone for this callsite clone");
5736 AssignCallsiteCloneToFuncClone(
5737 FuncCloneAssignedToCurCallsiteClone,
Call, Clone,
5738 AllocationCallToContextNodeMap.contains(
Call));
5741 assignImplicitCallers(Node, TrimmedContextIds,
5742 CallsiteToCalleeFuncCloneMap,
5743 FuncCloneToCurNodeCloneMap, ImplicitSources);
5746 for (
const auto &PE :
Node->CalleeEdges)
5748 for (
const auto &CE :
Node->CallerEdges)
5750 for (
auto *Clone :
Node->Clones) {
5752 for (
const auto &PE : Clone->CalleeEdges)
5754 for (
const auto &CE : Clone->CallerEdges)
5760 if (FuncCloneInfos.size() < 2)
5766 for (
auto &
Call : CallsWithMetadata) {
5767 ContextNode *
Node = getNodeForInst(
Call);
5768 if (!Node || !
Node->hasCall() ||
Node->emptyContextIds())
5773 DenseMap<unsigned, const ContextNode *> FuncCloneToNodeClone;
5774 FuncCloneToNodeClone[
Node->Call.cloneNo()] =
Node;
5782 for (
auto *
C :
Node->Clones)
5783 if (!
C->emptyContextIds() ||
5784 !ImplicitSources.contains({Node, C->Call.cloneNo()}))
5785 FuncCloneToNodeClone[
C->Call.cloneNo()] =
C;
5788 if (FuncCloneToNodeClone.
size() >= FuncCloneInfos.size())
5791 for (
auto &FC : FuncCloneInfos) {
5792 unsigned I =
FC.FuncClone.cloneNo();
5795 if (
I == 0 || FuncCloneToNodeClone.
contains(
I)) {
5803 auto &
Info = UnassignedCallClones[
Node][
I];
5804 auto &CallVector =
Info.Calls;
5805 for (
unsigned P =
I;;
P = FuncCloneParent[
P]) {
5806 if (
auto It = ImplicitSources.find({Node, P});
5807 It != ImplicitSources.end()) {
5808 Info.Source = resolveImplicitSource(Node, It->second);
5811 if (
auto It = FuncCloneToNodeClone.
find(
P);
5812 It != FuncCloneToNodeClone.
end()) {
5813 Info.Source = It->second;
5819 DenseMap<CallInfo, CallInfo> &CallMap =
FC.CallMap;
5820 if (
auto It = CallMap.
find(
Call); It != CallMap.
end()) {
5821 CallInfo CallClone = It->second;
5822 CallVector.push_back(CallClone);
5826 assert(
false &&
"Expected to find call in CallMap");
5832 for (
const CallInfo &OrigMatchingCall :
5833 OrigNodeToOrigCalls[Node].MatchingCalls) {
5834 if (
auto It = CallMap.
find(OrigMatchingCall); It != CallMap.
end()) {
5835 CallInfo CallClone = It->second;
5836 CallVector.push_back(CallClone);
5840 assert(
false &&
"Expected to find call in CallMap");
5848 (uint8_t)AllocationType::Cold | (uint8_t)AllocationType::NotCold;
5850 auto UpdateCalls = [&](ContextNode *
Node,
5851 DenseSet<const ContextNode *> &Visited,
5852 auto &&UpdateCalls) {
5853 auto Inserted = Visited.insert(Node);
5857 for (
auto *Clone :
Node->Clones)
5858 UpdateCalls(Clone, Visited, UpdateCalls);
5860 for (
auto &
Edge :
Node->CallerEdges)
5861 UpdateCalls(
Edge->Caller, Visited, UpdateCalls);
5865 if (!
Node->hasCall() ||
Node->emptyContextIds())
5871 auto GetUnassignedCallClones = [&](ContextNode *
Node) {
5874 auto UnassignedIt = UnassignedCallClones.
find(Node);
5875 if (UnassignedIt == UnassignedCallClones.
end())
5877 DenseSet<unsigned> NodeCallClones;
5878 for (
auto *
C :
Node->Clones)
5879 if (!
C->emptyContextIds())
5880 NodeCallClones.
insert(
C->Call.cloneNo());
5881 for (
auto &[CloneNo, Info] : UnassignedIt->second) {
5885 if (NodeCallClones.
contains(CloneNo))
5892 if (
Node->IsAllocation) {
5893 auto GetAllocTypeToUse = [&](
const ContextNode *
Node) {
5894 auto AT = allocTypeToUse(
Node->AllocTypes);
5900 !ContextIdToContextSizeInfos.empty()) {
5903 for (
auto Id :
Node->getContextIds()) {
5904 auto TypeI = ContextIdToAllocationType.find(Id);
5905 assert(TypeI != ContextIdToAllocationType.end());
5906 auto CSI = ContextIdToContextSizeInfos.find(Id);
5907 if (CSI != ContextIdToContextSizeInfos.end()) {
5908 for (
auto &Info : CSI->second) {
5910 if (TypeI->second == AllocationType::Cold)
5911 TotalCold +=
Info.TotalSize;
5916 AT = AllocationType::Cold;
5920 updateAllocationCall(
Node->Call, GetAllocTypeToUse(Node));
5924 for (
auto &[Source, Calls] : GetUnassignedCallClones(Node)) {
5925 auto AT = GetAllocTypeToUse(Source);
5926 for (
auto &
Call : Calls)
5927 updateAllocationCall(
Call, AT);
5932 auto CalleeFuncIt = CallsiteToCalleeFuncCloneMap.
find(Node);
5933 if (CalleeFuncIt != CallsiteToCalleeFuncCloneMap.
end()) {
5934 auto &CalleeFunc = CalleeFuncIt->second;
5935 updateCall(
Node->Call, CalleeFunc);
5937 for (
auto &
Call :
Node->MatchingCalls)
5938 updateCall(
Call, CalleeFunc);
5944 for (
auto &[Source, Calls] : GetUnassignedCallClones(Node)) {
5945 auto It = CallsiteToCalleeFuncCloneMap.
find(Source);
5947 if (It == CallsiteToCalleeFuncCloneMap.
end())
5949 if (It == CallsiteToCalleeFuncCloneMap.
end())
5951 for (
auto &
Call : Calls)
5952 updateCall(
Call, It->second);
5961 DenseSet<const ContextNode *> Visited;
5962 for (
auto &Entry : AllocationCallToContextNodeMap)
5963 UpdateCalls(
Entry.second, Visited, UpdateCalls);
5974 for (
auto &SN : FS->callsites()) {
5979 SN.Clones.size() >
I &&
5980 "Callsite summary has fewer entries than other summaries in function");
5981 if (SN.Clones.size() <=
I || !SN.Clones[
I])
5988 for (
auto &AN : FS->allocs()) {
5992 assert(AN.Versions.size() >
I &&
5993 "Alloc summary has fewer entries than other summaries in function");
5994 if (AN.Versions.size() <=
I ||
6011 NewGV->takeName(DeclGV);
6018 auto CloneFuncAliases = [&](
Function *NewF,
unsigned I) {
6019 if (!FuncToAliasMap.count(&
F))
6021 for (
auto *
A : FuncToAliasMap[&
F]) {
6023 auto *PrevA = M.getNamedAlias(AliasName);
6025 A->getType()->getPointerAddressSpace(),
6026 A->getLinkage(), AliasName, NewF);
6027 NewA->copyAttributesFrom(
A);
6029 TakeDeclNameAndReplace(PrevA, NewA);
6038 FunctionsClonedThinBackend++;
6055 for (
unsigned I = 1;
I < NumClones;
I++) {
6056 VMaps.
emplace_back(std::make_unique<ValueToValueMapTy>());
6063 FunctionCloneDuplicatesThinBackend++;
6064 auto *Func = HashToFunc[Hash];
6065 if (Func->hasAvailableExternallyLinkage()) {
6071 auto Decl = M.getOrInsertFunction(Name, Func->getFunctionType());
6073 <<
"created clone decl " <<
ore::NV(
"Decl", Decl.getCallee()));
6076 auto *PrevF = M.getFunction(Name);
6079 TakeDeclNameAndReplace(PrevF, Alias);
6081 <<
"created clone alias " <<
ore::NV(
"Alias", Alias));
6084 CloneFuncAliases(Func,
I);
6088 HashToFunc[Hash] = NewF;
6089 FunctionClonesThinBackend++;
6092 for (
auto &BB : *NewF) {
6093 for (
auto &Inst : BB) {
6094 Inst.setMetadata(LLVMContext::MD_memprof,
nullptr);
6095 Inst.setMetadata(LLVMContext::MD_callsite,
nullptr);
6100 TakeDeclNameAndReplace(PrevF, NewF);
6102 NewF->setName(Name);
6105 <<
"created clone " <<
ore::NV(
"NewFunction", NewF));
6108 CloneFuncAliases(NewF,
I);
6117 const Function *CallingFunc =
nullptr) {
6136 auto SrcFileMD =
F.getMetadata(
"thinlto_src_file");
6142 if (!SrcFileMD &&
F.isDeclaration()) {
6146 SrcFileMD = CallingFunc->getMetadata(
"thinlto_src_file");
6151 assert(SrcFileMD || OrigName ==
F.getName());
6153 StringRef SrcFile = M.getSourceFileName();
6165 if (!TheFnVI && OrigName ==
F.getName() &&
F.hasLocalLinkage() &&
6166 F.getName().contains(
'.')) {
6167 OrigName =
F.getName().rsplit(
'.').first;
6176 assert(TheFnVI ||
F.isDeclaration());
6180bool MemProfContextDisambiguation::initializeIndirectCallPromotionInfo(
6182 ICallAnalysis = std::make_unique<ICallPromotionAnalysis>();
6183 Symtab = std::make_unique<InstrProfSymtab>();
6194 if (
Error E = Symtab->create(M,
true,
false)) {
6195 std::string SymtabFailure =
toString(std::move(
E));
6196 M.getContext().emitError(
"Failed to create symtab: " + SymtabFailure);
6209 auto MIBIter = AllocNode.
MIBs.begin();
6210 for (
auto &MDOp : MemProfMD->
operands()) {
6219 if (MIBIter->StackIdIndices.empty()) {
6223 auto StackIdIndexIter = MIBIter->StackIdIndices.begin();
6228 auto ContextIterBegin =
6232 (ContextIterBegin != StackContext.
end() && *ContextIterBegin == 0) ? 1
6234 for (
auto ContextIter = ContextIterBegin; ContextIter != StackContext.
end();
6239 if (LastStackContextId == *ContextIter)
6241 LastStackContextId = *ContextIter;
6242 assert(StackIdIndexIter != MIBIter->StackIdIndices.end());
6252bool MemProfContextDisambiguation::applyImport(
Module &M) {
6259 std::map<const Function *, SmallPtrSet<const GlobalAlias *, 1>>
6261 for (
auto &
A :
M.aliases()) {
6262 auto *Aliasee =
A.getAliaseeObject();
6264 FuncToAliasMap[
F].insert(&
A);
6267 if (!initializeIndirectCallPromotionInfo(M))
6274 OptimizationRemarkEmitter ORE(&
F);
6277 bool ClonesCreated =
false;
6278 unsigned NumClonesCreated = 0;
6279 auto CloneFuncIfNeeded = [&](
unsigned NumClones, FunctionSummary *
FS) {
6289 if (ClonesCreated) {
6290 assert(NumClonesCreated == NumClones);
6297 ClonesCreated =
true;
6298 NumClonesCreated = NumClones;
6301 auto CloneCallsite = [&](
const CallsiteInfo &StackNode, CallBase *CB,
6302 Function *CalledFunction, FunctionSummary *
FS) {
6304 CloneFuncIfNeeded(StackNode.
Clones.
size(), FS);
6316 if (CalledFunction != CB->getCalledOperand() &&
6317 (!GA || CalledFunction != GA->getAliaseeObject())) {
6318 SkippedCallsCloning++;
6324 auto CalleeOrigName = CalledFunction->getName();
6325 for (
unsigned J = 0; J < StackNode.
Clones.
size(); J++) {
6328 if (J > 0 && VMaps[J - 1]->
empty())
6332 if (!StackNode.
Clones[J])
6334 auto NewF =
M.getOrInsertFunction(
6336 CalledFunction->getFunctionType());
6350 ORE.emit(OptimizationRemark(
DEBUG_TYPE,
"MemprofCall", CBClone)
6351 <<
ore::NV(
"Call", CBClone) <<
" in clone "
6353 <<
" assigned to call function clone "
6354 <<
ore::NV(
"Callee", NewF.getCallee()));
6368 ImportSummary->findSummaryInModule(TheFnVI,
M.getModuleIdentifier());
6372 auto SrcModuleMD =
F.getMetadata(
"thinlto_src_module");
6374 "enable-import-metadata is needed to emit thinlto_src_module");
6375 StringRef SrcModule =
6378 if (GVS->modulePath() == SrcModule) {
6379 GVSummary = GVS.get();
6404 if (
FS->allocs().empty() &&
FS->callsites().empty())
6407 auto SI =
FS->callsites().begin();
6408 auto AI =
FS->allocs().begin();
6413 DenseMap<ValueInfo, CallsiteInfo> MapTailCallCalleeVIToCallsite;
6416 for (
auto CallsiteIt =
FS->callsites().rbegin();
6417 CallsiteIt !=
FS->callsites().rend(); CallsiteIt++) {
6418 auto &Callsite = *CallsiteIt;
6422 if (!Callsite.StackIdIndices.empty())
6424 MapTailCallCalleeVIToCallsite.
insert({Callsite.Callee, Callsite});
6433 for (
auto &BB :
F) {
6434 for (
auto &
I : BB) {
6440 auto *CalledValue = CB->getCalledOperand();
6441 auto *CalledFunction = CB->getCalledFunction();
6442 if (CalledValue && !CalledFunction) {
6443 CalledValue = CalledValue->stripPointerCasts();
6450 assert(!CalledFunction &&
6451 "Expected null called function in callsite for alias");
6455 CallStack<MDNode, MDNode::op_iterator> CallsiteContext(
6456 I.getMetadata(LLVMContext::MD_callsite));
6457 auto *MemProfMD =
I.getMetadata(LLVMContext::MD_memprof);
6463 if (CB->getAttributes().hasFnAttr(
"memprof") && !MemProfMD) {
6464 CB->getAttributes().getFnAttr(
"memprof").getValueAsString() ==
"cold"
6465 ? AllocTypeColdThinBackend++
6466 : AllocTypeNotColdThinBackend++;
6467 OrigAllocsThinBackend++;
6468 AllocVersionsThinBackend++;
6469 if (!MaxAllocVersionsThinBackend)
6470 MaxAllocVersionsThinBackend = 1;
6477 auto &AllocNode = *(AI++);
6485 CloneFuncIfNeeded(AllocNode.Versions.size(), FS);
6487 OrigAllocsThinBackend++;
6488 AllocVersionsThinBackend += AllocNode.Versions.size();
6489 if (MaxAllocVersionsThinBackend < AllocNode.Versions.size())
6490 MaxAllocVersionsThinBackend = AllocNode.Versions.size();
6500 if (AllocNode.Versions.size() == 1 &&
6503 AllocationType::NotCold ||
6505 AllocationType::None);
6506 UnclonableAllocsThinBackend++;
6512 return Type == ((uint8_t)AllocationType::NotCold |
6513 (uint8_t)AllocationType::Cold);
6517 for (
unsigned J = 0; J < AllocNode.Versions.size(); J++) {
6520 if (J > 0 && VMaps[J - 1]->
empty())
6523 if (AllocNode.Versions[J] == (uint8_t)AllocationType::None)
6526 AllocTy == AllocationType::Cold ? AllocTypeColdThinBackend++
6527 : AllocTypeNotColdThinBackend++;
6542 ORE.emit(OptimizationRemark(
DEBUG_TYPE,
"MemprofAttribute", CBClone)
6543 <<
ore::NV(
"AllocationCall", CBClone) <<
" in clone "
6545 <<
" marked with memprof allocation attribute "
6546 <<
ore::NV(
"Attribute", AllocTypeString));
6548 }
else if (!CallsiteContext.empty()) {
6549 if (!CalledFunction) {
6553 assert(!CI || !CI->isInlineAsm());
6563 recordICPInfo(CB,
FS->callsites(), SI, ICallAnalysisInfo);
6569 CloneFuncIfNeeded(NumClones, FS);
6574 assert(SI !=
FS->callsites().end());
6575 auto &StackNode = *(
SI++);
6581 for (
auto StackId : CallsiteContext) {
6583 assert(ImportSummary->getStackIdAtIndex(*StackIdIndexIter) ==
6589 CloneCallsite(StackNode, CB, CalledFunction, FS);
6591 }
else if (CB->isTailCall() && CalledFunction) {
6594 ValueInfo CalleeVI =
6596 if (CalleeVI && MapTailCallCalleeVIToCallsite.
count(CalleeVI)) {
6597 auto Callsite = MapTailCallCalleeVIToCallsite.
find(CalleeVI);
6598 assert(Callsite != MapTailCallCalleeVIToCallsite.
end());
6599 CloneCallsite(Callsite->second, CB, CalledFunction, FS);
6606 performICP(M,
FS->callsites(), VMaps, ICallAnalysisInfo, ORE);
6616 for (
auto &BB :
F) {
6617 for (
auto &
I : BB) {
6620 I.setMetadata(LLVMContext::MD_memprof,
nullptr);
6621 I.setMetadata(LLVMContext::MD_callsite,
nullptr);
6629unsigned MemProfContextDisambiguation::recordICPInfo(
6634 uint32_t NumCandidates;
6636 auto CandidateProfileData =
6637 ICallAnalysis->getPromotionCandidatesForInstruction(
6639 if (CandidateProfileData.empty())
6645 bool ICPNeeded =
false;
6646 unsigned NumClones = 0;
6647 size_t CallsiteInfoStartIndex = std::distance(AllCallsites.
begin(), SI);
6648 for (
const auto &Candidate : CandidateProfileData) {
6650 auto CalleeValueInfo =
6652 ImportSummary->getValueInfo(Candidate.Value);
6658 assert(!CalleeValueInfo ||
SI->Callee == CalleeValueInfo ||
6660 SI->Callee.getGUID() ==
6661 ImportSummary->getGUIDFromOriginalID(Candidate.Value)));
6663 auto &StackNode = *(
SI++);
6668 [](
unsigned CloneNo) { return CloneNo != 0; });
6678 ICallAnalysisInfo.
push_back({CB, CandidateProfileData.vec(), NumCandidates,
6679 TotalCount, CallsiteInfoStartIndex});
6683void MemProfContextDisambiguation::performICP(
6685 ArrayRef<std::unique_ptr<ValueToValueMapTy>> VMaps,
6687 OptimizationRemarkEmitter &ORE) {
6694 for (
auto &Info : ICallAnalysisInfo) {
6697 auto TotalCount =
Info.TotalCount;
6698 unsigned NumClones = 0;
6701 for (
auto &Candidate :
Info.CandidateProfileData) {
6712 Function *TargetFunction = Symtab->getFunction(Candidate.Value);
6721 if (!TargetFunction && StackNode.
Callee &&
6724 if (TargetFunction ==
nullptr ||
6732 return OptimizationRemarkMissed(
DEBUG_TYPE,
"UnableToFindTarget", CB)
6733 <<
"Memprof cannot promote indirect call: target with md5sum "
6734 <<
ore::NV(
"target md5sum", Candidate.Value) <<
" not found";
6739 RemainingCandidates.
push_back(Candidate);
6744 const char *Reason =
nullptr;
6747 return OptimizationRemarkMissed(
DEBUG_TYPE,
"UnableToPromote", CB)
6748 <<
"Memprof cannot promote indirect call to "
6749 <<
ore::NV(
"TargetFunction", TargetFunction)
6750 <<
" with count of " <<
ore::NV(
"TotalCount", TotalCount)
6753 RemainingCandidates.
push_back(Candidate);
6762 CallBase *CBClone = CB;
6763 for (
unsigned J = 0; J < NumClones; J++) {
6766 if (J > 0 && VMaps[J - 1]->
empty())
6776 TotalCount, isSamplePGO, &ORE);
6777 auto *TargetToUse = TargetFunction;
6780 if (StackNode.
Clones[J]) {
6799 <<
ore::NV(
"Call", CBClone) <<
" in clone "
6801 <<
" promoted and assigned to call function clone "
6802 <<
ore::NV(
"Callee", TargetToUse));
6806 TotalCount -= Candidate.Count;
6810 CallBase *CBClone = CB;
6811 for (
unsigned J = 0; J < NumClones; J++) {
6814 if (J > 0 && VMaps[J - 1]->
empty())
6820 CBClone->
setMetadata(LLVMContext::MD_prof,
nullptr);
6823 if (TotalCount != 0)
6825 IPVK_IndirectCallTarget,
Info.NumCandidates);
6830template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
6831bool CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::process(
6832 function_ref<
void(StringRef, StringRef,
const Twine &)> EmitRemark,
6833 bool AllowExtraAnalysis) {
6835 dbgs() <<
"CCG before cloning:\n";
6839 exportToDot(
"postbuild");
6852 dbgs() <<
"CCG after cloning:\n";
6856 exportToDot(
"cloned");
6858 bool Changed = assignFunctions();
6861 dbgs() <<
"CCG after assigning function clones:\n";
6865 exportToDot(
"clonefuncassign");
6868 printTotalSizes(
errs(), EmitRemark);
6873bool MemProfContextDisambiguation::processModule(
6875 llvm::function_ref<OptimizationRemarkEmitter &(
Function *)> OREGetter) {
6880 return applyImport(M);
6893 ModuleCallsiteContextGraph CCG(M, OREGetter);
6896 return CCG.process();
6901 : ImportSummary(Summary), isSamplePGO(isSamplePGO) {
6906 "-memprof-dot-scope=alloc requires -memprof-dot-alloc-id");
6910 "-memprof-dot-scope=context requires -memprof-dot-context-id");
6914 "-memprof-dot-scope=all can't have both -memprof-dot-alloc-id and "
6915 "-memprof-dot-context-id");
6916 if (ImportSummary) {
6926 auto ReadSummaryFile =
6928 if (!ReadSummaryFile) {
6935 if (!ImportSummaryForTestingOrErr) {
6941 ImportSummaryForTesting = std::move(*ImportSummaryForTestingOrErr);
6942 ImportSummary = ImportSummaryForTesting.get();
6951 if (!processModule(M, OREGetter))
6974 for (
auto &
I : Index) {
6975 auto VI = Index.getValueInfo(
I);
6976 for (
auto &S : VI.getSummaryList()) {
6980 !isPrevailing(VI.getGUID(), S.get()))
6983 if (!FS || FS->callsites().empty())
6985 CalleeToEdge.
clear();
6986 for (
auto &CI : FS->callsites()) {
6987 if (!CI.Callee ||
llvm::all_of(CI.Clones, [](
unsigned CloneNo) {
6988 return CloneNo == 0;
6995 NumClonedCalleeCallsites++;
6997 if (CalleeToEdge.
empty())
6998 for (
auto &[CalleeVI, Edge] : FS->mutableCalls())
6999 CalleeToEdge[CalleeVI] = &Edge;
7000 auto It = CalleeToEdge.
find(CI.Callee);
7001 if (It == CalleeToEdge.
end())
7008 if (!Edge->hasIndirectCall())
7010 NumClonedCalleeIndirectCallsites++;
7011 if (!Edge->requiresDeclImport()) {
7012 Edge->setRequiresDeclImport(
true);
7013 NumClonedCalleeEdgesMarkedRequiresDeclImport++;
7034 bool AllowExtraAnalysis =
7039 IndexCallsiteContextGraph CCG(Index, isPrevailing);
7040 Changed = CCG.process(EmitRemark, AllowExtraAnalysis);
7063 for (
auto &BB :
F) {
7064 for (
auto &
I : BB) {
7068 if (CI->hasFnAttr(
"memprof")) {
7069 CI->removeFnAttr(
"memprof");
7072 if (!CI->hasMetadata(LLVMContext::MD_callsite)) {
7073 assert(!CI->hasMetadata(LLVMContext::MD_memprof));
7079 CI->setMetadata(LLVMContext::MD_memprof,
nullptr);
7080 CI->setMetadata(LLVMContext::MD_callsite,
nullptr);
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
Unify divergent function exit nodes
static void print(raw_ostream &Out, object::Archive::Kind Kind, T Val)
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
#define clEnumValN(ENUMVAL, FLAGNAME, DESC)
This file defines the DenseMap class.
This file defines the DenseSet and SmallDenseSet classes.
Module.h This file contains the declarations for the Module class.
Machine Check Debug Module
This file implements a map that provides insertion order iteration.
static cl::opt< unsigned > TailCallSearchDepth("memprof-tail-call-search-depth", cl::init(5), cl::Hidden, cl::desc("Max depth to recursively search for missing " "frames through tail calls."))
uint64_t ComputeHash(const FunctionSummary *FS, unsigned I)
static cl::opt< DotScope > DotGraphScope("memprof-dot-scope", cl::desc("Scope of graph to export to dot"), cl::Hidden, cl::init(DotScope::All), cl::values(clEnumValN(DotScope::All, "all", "Export full callsite graph"), clEnumValN(DotScope::Alloc, "alloc", "Export only nodes with contexts feeding given " "-memprof-dot-alloc-id"), clEnumValN(DotScope::Context, "context", "Export only nodes with given -memprof-dot-context-id")))
static cl::opt< bool > DoMergeIteration("memprof-merge-iteration", cl::init(true), cl::Hidden, cl::desc("Iteratively apply merging on a node to catch new callers"))
static bool isMemProfClone(const Function &F)
static cl::opt< unsigned > AllocIdForDot("memprof-dot-alloc-id", cl::init(0), cl::Hidden, cl::desc("Id of alloc to export if -memprof-dot-scope=alloc " "or to highlight if -memprof-dot-scope=all"))
static void updateClonedCalleeEdgesForImport(ModuleSummaryIndex &Index, llvm::function_ref< bool(GlobalValue::GUID, const GlobalValueSummary *)> isPrevailing)
static cl::opt< unsigned > ContextIdForDot("memprof-dot-context-id", cl::init(0), cl::Hidden, cl::desc("Id of context to export if -memprof-dot-scope=context or to " "highlight otherwise"))
static cl::opt< bool > ExportToDot("memprof-export-to-dot", cl::init(false), cl::Hidden, cl::desc("Export graph to dot files."))
static void checkEdge(const std::shared_ptr< ContextEdge< DerivedCCG, FuncTy, CallTy > > &Edge)
static cl::opt< bool > AllowRecursiveCallsites("memprof-allow-recursive-callsites", cl::init(true), cl::Hidden, cl::desc("Allow cloning of callsites involved in recursive cycles"))
bool checkColdOrNotCold(uint8_t AllocType)
static ValueInfo findValueInfoForFunc(const Function &F, const Module &M, const ModuleSummaryIndex *ImportSummary, const Function *CallingFunc=nullptr)
static cl::opt< bool > CloneRecursiveContexts("memprof-clone-recursive-contexts", cl::init(true), cl::Hidden, cl::desc("Allow cloning of contexts through recursive cycles"))
static std::string getAllocTypeString(uint8_t AllocTypes)
bool DOTGraphTraits< constCallsiteContextGraph< DerivedCCG, FuncTy, CallTy > * >::DoHighlight
static unsigned getMemProfCloneNum(const Function &F)
static cl::opt< unsigned > MemProfICPNoInlineThreshold("memprof-icp-noinline-threshold", cl::init(0), cl::Hidden, cl::desc("Minimum absolute count for promoted target to be inlinable"))
static SmallVector< std::unique_ptr< ValueToValueMapTy >, 4 > createFunctionClones(Function &F, unsigned NumClones, Module &M, OptimizationRemarkEmitter &ORE, std::map< const Function *, SmallPtrSet< const GlobalAlias *, 1 > > &FuncToAliasMap, FunctionSummary *FS)
static cl::opt< bool > VerifyCCG("memprof-verify-ccg", cl::init(false), cl::Hidden, cl::desc("Perform verification checks on CallingContextGraph."))
static void checkNode(const ContextNode< DerivedCCG, FuncTy, CallTy > *Node, bool CheckEdges=true)
static cl::opt< bool > MergeClones("memprof-merge-clones", cl::init(true), cl::Hidden, cl::desc("Merge clones before assigning functions"))
static std::string getMemProfFuncName(Twine Base, unsigned CloneNo)
static cl::opt< std::string > MemProfImportSummary("memprof-import-summary", cl::desc("Import summary to use for testing the ThinLTO backend via opt"), cl::Hidden)
static const std::string MemProfCloneSuffix
static void updateSubprogramLinkageName(Function *NewFunc, StringRef Name)
static cl::opt< bool > AllowRecursiveContexts("memprof-allow-recursive-contexts", cl::init(true), cl::Hidden, cl::desc("Allow cloning of contexts having recursive cycles"))
static cl::opt< std::string > DotFilePathPrefix("memprof-dot-file-path-prefix", cl::init(""), cl::Hidden, cl::value_desc("filename"), cl::desc("Specify the path prefix of the MemProf dot files."))
static cl::opt< bool > VerifyNodes("memprof-verify-nodes", cl::init(false), cl::Hidden, cl::desc("Perform frequent verification checks on nodes."))
static void checkAllocContextIds(const AllocInfo &AllocNode, const MDNode *MemProfMD, const CallStack< MDNode, MDNode::op_iterator > &CallsiteContext, const ModuleSummaryIndex *ImportSummary)
static cl::opt< bool > DumpCCG("memprof-dump-ccg", cl::init(false), cl::Hidden, cl::desc("Dump CallingContextGraph to stdout after each stage."))
This is the interface to build a ModuleSummaryIndex for a module.
ModuleSummaryIndex.h This file contains the declarations the classes that hold the module index and s...
if(auto Err=PB.parsePassPipeline(MPM, Passes)) return wrap(std MPM run * Mod
FunctionAnalysisManager FAM
std::pair< BasicBlock *, BasicBlock * > Edge
This file defines generic set operations that may be used on set's of different types,...
This file defines the SmallPtrSet class.
This file defines the SmallSet class.
This file defines the SmallVector class.
This file defines the 'Statistic' class, which is designed to be an easy way to expose various metric...
#define STATISTIC(VARNAME, DESC)
static TableGen::Emitter::Opt Y("gen-skeleton-entry", EmitSkeleton, "Generate example skeleton entry")
void print(OutputBuffer &OB) const
ValueInfo getAliaseeVI() const
PassT::Result & getResult(IRUnitT &IR, ExtraArgTs... ExtraArgs)
Get the result of an analysis pass for a given IR unit.
Represent a constant reference to an array (0 or more elements consecutively in memory),...
bool empty() const
Check if the array is empty.
static LLVM_ABI Attribute get(LLVMContext &Context, AttrKind Kind, uint64_t Val=0)
Return a uniquified Attribute object.
void addFnAttr(Attribute::AttrKind Kind)
Adds the attribute to the function.
void setCalledOperand(Value *V)
Subprogram description. Uses SubclassData1.
bool contains(const_arg_type_t< KeyT > Val) const
Return true if the specified key is in the map, false otherwise.
size_type count(const_arg_type_t< KeyT > Val) const
Return 1 if the specified key is in the map, 0 otherwise.
iterator find(const_arg_type_t< KeyT > Val)
void reserve(size_type NumEntries)
Grow the DenseMap so that it can contain at least NumEntries items before resizing again.
ValueT lookup(const_arg_type_t< KeyT > Val) const
Return the entry for the specified key, or a default constructed value if no such entry exists.
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
Implements a dense probed hash-table based set.
Function summary information to aid decisions and implementation of importing.
FunctionType * getFunctionType() const
Returns the FunctionType for me.
DISubprogram * getSubprogram() const
Get the attached subprogram.
const Function & getFunction() const
LLVMContext & getContext() const
getContext - Return a reference to the LLVMContext associated with this function.
static LLVM_ABI GlobalAlias * create(Type *Ty, unsigned AddressSpace, LinkageTypes Linkage, const Twine &Name, Constant *Aliasee, Module *Parent)
If a parent module is specified, the alias is automatically inserted into the end of the specified mo...
Function and variable summary information to aid decisions and implementation of importing.
static LLVM_ABI GUID getGUIDAssumingExternalLinkage(StringRef GlobalName)
Return a 64-bit global unique ID constructed from the name of a global symbol.
static bool isLocalLinkage(LinkageTypes Linkage)
LLVM_ABI bool isDeclaration() const
Return true if the primary definition of this global value is outside of the current translation unit...
uint64_t GUID
Declare a type to represent a global unique identifier for a global value.
Module * getParent()
Get the module that this global value is contained inside of...
LLVM_ABI void eraseFromParent()
This method unlinks 'this' from the containing module and deletes it.
static LLVM_ABI std::string getGlobalIdentifier(StringRef Name, GlobalValue::LinkageTypes Linkage, StringRef FileName)
Return the modified name for a global value suitable to be used as the key for a global lookup (e....
bool isWeakForLinker() const
@ InternalLinkage
Rename collisions when linking (static functions).
LLVM_ABI const Function * getFunction() const
Return the function this instruction belongs to.
MDNode * getMetadata(unsigned KindID) const
Get the metadata of given kind attached to this Instruction.
LLVM_ABI void setMetadata(unsigned KindID, MDNode *Node)
Set the metadata of the specified kind to the specified node.
This is an important class for using LLVM in a threaded context.
const MDOperand & getOperand(unsigned I) const
ArrayRef< MDOperand > operands() const
unsigned getNumOperands() const
Return number of MDNode operands.
LLVM_ABI TempMDNode clone() const
Create a (temporary) clone of this.
static std::enable_if_t< std::is_base_of< MDNode, T >::value, T * > replaceWithUniqued(std::unique_ptr< T, TempMDNodeDeleter > N)
Replace a temporary node with a uniqued one.
static LLVM_ABI MDString * get(LLVMContext &Context, StringRef Str)
This class implements a map that also provides access to all stored values in a deterministic order.
size_type count(const KeyT &Key) const
LLVM_ABI MemProfContextDisambiguation(const ModuleSummaryIndex *Summary=nullptr, bool isSamplePGO=false)
LLVM_ABI PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM)
LLVM_ABI PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM)
static ErrorOr< std::unique_ptr< MemoryBuffer > > getFile(const Twine &Filename, bool IsText=false, bool RequiresNullTerminator=true, bool IsVolatile=false, std::optional< Align > Alignment=std::nullopt)
Open the specified file as a MemoryBuffer, returning a new MemoryBuffer if successful,...
Class to hold module path string table and global value map, and encapsulate methods for operating on...
static StringRef getOriginalNameBeforePromote(StringRef Name)
Helper to obtain the unpromoted name for a global value (or the original name if not promoted).
ValueInfo getValueInfo(const GlobalValueSummaryMapTy::value_type &R) const
Return a ValueInfo for the index value_type (convenient when iterating index).
uint64_t getStackIdAtIndex(unsigned Index) const
A Module instance is used to store all the information related to an LLVM module.
LLVMContext & getContext() const
Get the global data context.
A NodeSet contains a set of SUnit DAG nodes with additional information that assigns a priority to th...
A discriminated union of two or more pointer types, with the discriminator in the low bits of the poi...
A set of analyses that are preserved following a run of a transformation pass.
static PreservedAnalyses none()
Convenience factory function for the empty preserved set.
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
A class that wrap the SHA1 algorithm.
LLVM_ABI void update(ArrayRef< uint8_t > Data)
Digest more data.
LLVM_ABI std::array< uint8_t, 20 > result()
Return the current raw 160-bits SHA1 for the digested data since the last call to init().
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
std::pair< const_iterator, bool > insert(const T &V)
insert - Insert an element into the set if it isn't already there.
reference emplace_back(ArgTypes &&... Args)
void reserve(size_type N)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Represent a constant reference to a string, i.e.
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
LLVM_ABI StringRef getName() const
Return a constant reference to the value's name.
std::pair< iterator, bool > insert(const ValueT &V)
bool contains(const_arg_type_t< ValueT > V) const
Check if the set contains the given element.
bool erase(const ValueT &V)
void insert_range(Range &&R)
size_type count(const_arg_type_t< ValueT > V) const
Return 1 if the specified key is in the set, 0 otherwise.
void reserve(size_t Size)
Grow the DenseSet so that it can contain at least NumEntries items before resizing again.
An efficient, type-erasing, non-owning reference to a callable.
Helper class to iterate through stack ids in both metadata (memprof MIB and callsite) and the corresp...
CallStackIterator beginAfterSharedPrefix(const CallStack &Other)
CallStackIterator end() const
This class implements an extremely fast bulk output stream that can only output to a stream.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ CE
Windows NT (Windows on ARM)
ValuesClass values(OptsTy... Options)
Helper to build a ValuesClass by forwarding a variable number of arguments as an initializer list to ...
initializer< Ty > init(const Ty &Val)
std::enable_if_t< detail::IsValidPointer< X, Y >::value, X * > dyn_extract(Y &&MD)
Extract a Value from Metadata, if any.
LLVM_ABI AllocationType getMIBAllocType(const MDNode *MIB)
Returns the allocation type from an MIB metadata node.
LLVM_ABI bool metadataMayIncludeContextSizeInfo()
Whether the alloc memprof metadata may include context size info for some MIBs (but possibly not all)...
LLVM_ABI bool hasSingleAllocType(uint8_t AllocTypes)
True if the AllocTypes bitmask contains just a single type.
LLVM_ABI std::string getAllocTypeAttributeString(AllocationType Type)
Returns the string to use in attributes with the given type.
LLVM_ABI MDNode * getMIBStackNode(const MDNode *MIB)
Returns the stack node from an MIB metadata node.
LLVM_ABI void removeAnyExistingAmbiguousAttribute(CallBase *CB)
Removes any existing "ambiguous" memprof attribute.
DiagnosticInfoOptimizationBase::Argument NV
LLVM_ABI CallBase & promoteIndirectCall(CallBase &CB, Function *F, uint64_t Count, uint64_t TotalCount, bool AttachProfToDirectCall, OptimizationRemarkEmitter *ORE)
NodeAddr< NodeBase * > Node
NodeAddr< FuncNode * > Func
friend class Instruction
Iterator for Instructions in a `BasicBlock.
uint64_t read64le(const void *P)
void write32le(void *P, uint32_t V)
This is an optimization pass for GlobalISel generic memory operations.
cl::opt< unsigned > MinClonedColdBytePercent("memprof-cloning-cold-threshold", cl::init(100), cl::Hidden, cl::desc("Min percent of cold bytes to hint alloc cold during cloning"))
auto drop_begin(T &&RangeOrContainer, size_t N=1)
Return a range covering RangeOrContainer with the first N elements excluded.
void dump(const SparseBitVector< ElementSize > &LHS, raw_ostream &out)
LLVM_ABI void logAllUnhandledErrors(Error E, raw_ostream &OS, Twine ErrorBanner={})
Log all errors (if any) in E to OS.
void stable_sort(R &&Range)
cl::opt< bool > MemProfReportHintedSizes("memprof-report-hinted-sizes", cl::init(false), cl::Hidden, cl::desc("Report total allocation sizes of hinted allocations"))
bool all_of(R &&range, UnaryPredicate P)
Provide wrappers to std::all_of which take ranges instead of having to pass begin/end explicitly.
LLVM_ABI bool isLegalToPromote(const CallBase &CB, Function *Callee, const char **FailureReason=nullptr)
Return true if the given indirect call site can be made to call Callee.
Printable print(const GCNRegPressure &RP, const GCNSubtarget *ST=nullptr, unsigned DynamicVGPRBlockSize=0)
auto enumerate(FirstRange &&First, RestRanges &&...Rest)
Given two or more input ranges, returns a new range whose values are tuples (A, B,...
void set_intersect(S1Ty &S1, const S2Ty &S2)
set_intersect(A, B) - Compute A := A ^ B Identical to set_intersection, except that it works on set<>...
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
LLVM_ABI bool mayHaveMemprofSummary(const CallBase *CB)
Returns true if the instruction could have memprof metadata, used to ensure consistency between summa...
constexpr from_range_t from_range
static cl::opt< bool > MemProfRequireDefinitionForPromotion("memprof-require-definition-for-promotion", cl::init(false), cl::Hidden, cl::desc("Require target function definition when promoting indirect calls"))
auto dyn_cast_if_present(const Y &Val)
dyn_cast_if_present<X> - Functionally identical to dyn_cast, except that a null (or none in the case ...
cl::opt< unsigned > MemProfTopNImportant("memprof-top-n-important", cl::init(10), cl::Hidden, cl::desc("Number of largest cold contexts to consider important"))
bool set_is_subset(const S1Ty &S1, const S2Ty &S2)
set_is_subset(A, B) - Return true iff A in B
void append_range(Container &C, Range &&R)
Wrapper function to append range R to container C.
static cl::opt< bool > MemProfImportClonedIndirectTargets("memprof-import-cloned-indirect-targets", cl::init(true), cl::Hidden, cl::desc("Import at least a declaration of the indirect call targets of " "callsites assigned to call a cloned callee (needed for MemProf " "ICP in the ThinLTO backend)"))
void set_subtract(S1Ty &S1, const S2Ty &S2)
set_subtract(A, B) - Compute A := A - B
InnerAnalysisManagerProxy< FunctionAnalysisManager, Module > FunctionAnalysisManagerModuleProxy
Provide the FunctionAnalysisManager to Module proxy.
raw_ostream & WriteGraph(raw_ostream &O, const GraphType &G, bool ShortNames=false, const Twine &Title="")
bool set_intersects(const S1Ty &S1, const S2Ty &S2)
set_intersects(A, B) - Return true iff A ^ B is non empty
detail::concat_range< ValueT, RangeTs... > concat(RangeTs &&...Ranges)
Returns a concatenated range across two or more ranges.
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
LLVM_ABI Expected< std::unique_ptr< ModuleSummaryIndex > > getModuleSummaryIndex(MemoryBufferRef Buffer)
Parse the specified bitcode buffer, returning the module summary index.
auto dyn_cast_or_null(const Y &Val)
bool any_of(R &&range, UnaryPredicate P)
Provide wrappers to std::any_of which take ranges instead of having to pass begin/end explicitly.
LLVM_ABI void annotateValueSite(Module &M, Instruction &Inst, const InstrProfRecord &InstrProfR, InstrProfValueKind ValueKind, uint32_t SiteIndx, uint32_t MaxMDCount=3)
Get the value profile data for value site SiteIdx from InstrProfR and annotate the instruction Inst w...
cl::opt< unsigned > MaxSummaryIndirectEdges("module-summary-max-indirect-edges", cl::init(0), cl::Hidden, cl::desc("Max number of summary edges added from " "indirect call profile metadata"))
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
bool none_of(R &&Range, UnaryPredicate P)
Provide wrappers to std::none_of which take ranges instead of having to pass begin/end explicitly.
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
bool set_union(S1Ty &S1, const S2Ty &S2)
set_union(A, B) - Compute A := A u B, return whether A changed.
cl::opt< bool > SupportsHotColdNew
Indicate we are linking with an allocator that supports hot/cold operator new interfaces.
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
S1Ty set_intersection(const S1Ty &S1, const S2Ty &S2)
set_intersection(A, B) - Return A ^ B
LLVM_ABI raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
cl::opt< bool > EnableMemProfContextDisambiguation
Enable MemProf context disambiguation for thin link.
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Count
S1Ty set_difference(const S1Ty &S1, const S2Ty &S2)
set_difference(A, B) - Return A - B
raw_ostream & operator<<(raw_ostream &OS, const APFixedPoint &FX)
Expected< T > errorOrToExpected(ErrorOr< T > &&EO)
Convert an ErrorOr<T> to an Expected<T>.
ArrayRef(const T &OneElt) -> ArrayRef< T >
std::string toString(const APInt &I, unsigned Radix, bool Signed, bool formatAsCLiteral=false, bool UpperCase=true, bool InsertSeparators=false)
ValueMap< const Value *, WeakTrackingVH > ValueToValueMapTy
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
auto find_if(R &&Range, UnaryPredicate P)
Provide wrappers to std::find_if which take ranges instead of having to pass begin/end explicitly.
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
LLVM_ABI Function * CloneFunction(Function *F, ValueToValueMapTy &VMap, ClonedCodeInfo *CodeInfo=nullptr)
Return a copy of the specified function and add it to that function's module.
AnalysisManager< Module > ModuleAnalysisManager
Convenience typedef for the Module analysis manager.
cl::opt< bool > MemProfFixupImportant("memprof-fixup-important", cl::init(true), cl::Hidden, cl::desc("Enables edge fixup for important contexts"))
DOTGraphTraits(bool IsSimple=false)
typename GTraits::NodeRef NodeRef
static std::string getEdgeAttributes(NodeRef, ChildIteratorType ChildIter, GraphType G)
const CallsiteContextGraph< DerivedCCG, FuncTy, CallTy > * GraphType
typename GTraits::ChildIteratorType ChildIteratorType
static std::string getNodeAttributes(NodeRef Node, GraphType G)
static bool isNodeHidden(NodeRef Node, GraphType G)
static std::string getNodeLabel(NodeRef Node, GraphType G)
GraphTraits< GraphType > GTraits
static NodeRef getNode(const NodePtrTy &P)
static const ContextNode< DerivedCCG, FuncTy, CallTy > * GetCallee(const EdgePtrTy &P)
static ChildIteratorType child_end(NodeRef N)
std::unique_ptr< ContextNode< DerivedCCG, FuncTy, CallTy > > NodePtrTy
mapped_iterator< typename std::vector< std::shared_ptr< ContextEdge< DerivedCCG, FuncTy, CallTy > > >::const_iterator, decltype(&GetCallee)> ChildIteratorType
const CallsiteContextGraph< DerivedCCG, FuncTy, CallTy > * GraphType
const ContextNode< DerivedCCG, FuncTy, CallTy > * NodeRef
mapped_iterator< typename std::vector< NodePtrTy >::const_iterator, decltype(&getNode)> nodes_iterator
static ChildIteratorType child_begin(NodeRef N)
static NodeRef getEntryNode(GraphType G)
static nodes_iterator nodes_begin(GraphType G)
static nodes_iterator nodes_end(GraphType G)
std::shared_ptr< ContextEdge< DerivedCCG, FuncTy, CallTy > > EdgePtrTy
Summary of memprof metadata on allocations.
std::vector< MIBInfo > MIBs
Class to accumulate and hold information about a callee.
SmallVector< unsigned > StackIdIndices
SmallVector< unsigned > Clones
DOTGraphTraits - Template class that can be specialized to customize how graphs are converted to 'dot...
DefaultDOTGraphTraits(bool simple=false)
An information struct used to provide DenseMap with the various necessary components for a given valu...
typename GraphType::UnknownGraphTypeError NodeRef
Struct that holds a reference to a particular GUID in a global value summary.
ArrayRef< std::unique_ptr< GlobalValueSummary > > getSummaryList() const
GlobalValue::GUID getGUID() const
PointerUnion< CallsiteInfo *, AllocInfo * > SimpleType
static SimpleType getSimplifiedValue(IndexCall &Val)
const PointerUnion< CallsiteInfo *, AllocInfo * > SimpleType
static SimpleType getSimplifiedValue(const IndexCall &Val)
Define a template that can be specialized by smart pointers to reflect the fact that they are automat...