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 aliasees prevailing in a different module than its alias");
118 cl::desc(
"Specify the path prefix of the MemProf dot files."));
122 cl::desc(
"Export graph to dot files."));
127 cl::desc(
"Iteratively apply merging on a node to catch new callers"));
137 "memprof-dot-scope",
cl::desc(
"Scope of graph to export to dot"),
142 "Export only nodes with contexts feeding given "
143 "-memprof-dot-alloc-id"),
145 "Export only nodes with given -memprof-dot-context-id")));
149 cl::desc(
"Id of alloc to export if -memprof-dot-scope=alloc "
150 "or to highlight if -memprof-dot-scope=all"));
154 cl::desc(
"Id of context to export if -memprof-dot-scope=context or to "
155 "highlight otherwise"));
159 cl::desc(
"Dump CallingContextGraph to stdout after each stage."));
163 cl::desc(
"Perform verification checks on CallingContextGraph."));
167 cl::desc(
"Perform frequent verification checks on nodes."));
170 "memprof-import-summary",
171 cl::desc(
"Import summary to use for testing the ThinLTO backend via opt"),
177 cl::desc(
"Max depth to recursively search for missing "
178 "frames through tail calls."));
183 cl::desc(
"Allow cloning of callsites involved in recursive cycles"));
187 cl::desc(
"Allow cloning of contexts through recursive cycles"));
194 cl::desc(
"Merge clones before assigning functions"));
203 cl::desc(
"Allow cloning of contexts having recursive cycles"));
209 cl::desc(
"Minimum absolute count for promoted target to be inlinable"));
213 "enable-memprof-context-disambiguation",
cl::Hidden,
214 cl::desc(
"Enable MemProf context disambiguation"));
220 cl::desc(
"Linking with hot/cold operator new interfaces"));
225 "Require target function definition when promoting indirect calls"));
232 cl::desc(
"Number of largest cold contexts to consider important"));
236 cl::desc(
"Enables edge fixup for important contexts"));
258template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
259class CallsiteContextGraph {
261 CallsiteContextGraph() =
default;
262 CallsiteContextGraph(
const CallsiteContextGraph &) =
default;
263 CallsiteContextGraph(CallsiteContextGraph &&) =
default;
267 EmitRemark =
nullptr,
268 bool AllowExtraAnalysis =
false);
272 void identifyClones();
279 bool assignFunctions();
285 EmitRemark =
nullptr)
const;
288 const CallsiteContextGraph &CCG) {
294 const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *>;
296 const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *>;
298 void exportToDot(std::string Label)
const;
301 struct FuncInfo final
302 :
public std::pair<FuncTy *, unsigned > {
303 using Base = std::pair<FuncTy *, unsigned>;
305 FuncInfo(FuncTy *
F =
nullptr,
unsigned CloneNo = 0) :
Base(
F, CloneNo) {}
306 explicit operator bool()
const {
return this->first !=
nullptr; }
307 FuncTy *func()
const {
return this->first; }
308 unsigned cloneNo()
const {
return this->second; }
312 struct CallInfo final :
public std::pair<CallTy, unsigned > {
313 using Base = std::pair<CallTy, unsigned>;
315 CallInfo(CallTy
Call =
nullptr,
unsigned CloneNo = 0)
317 explicit operator bool()
const {
return (
bool)this->first; }
318 CallTy call()
const {
return this->first; }
319 unsigned cloneNo()
const {
return this->second; }
320 void setCloneNo(
unsigned N) { this->second =
N; }
322 if (!
operator bool()) {
328 OS <<
"\t(clone " << cloneNo() <<
")";
354 bool Recursive =
false;
381 std::vector<std::shared_ptr<ContextEdge>> CalleeEdges;
385 std::vector<std::shared_ptr<ContextEdge>> CallerEdges;
389 bool useCallerEdgesForContextInfo()
const {
394 assert(!CalleeEdges.empty() || CallerEdges.empty() || IsAllocation ||
412 for (
auto &Edge : CalleeEdges.empty() ? CallerEdges : CalleeEdges)
413 Count += Edge->getContextIds().size();
417 CalleeEdges, useCallerEdgesForContextInfo()
419 : std::vector<std::shared_ptr<ContextEdge>>());
420 for (
const auto &Edge : Edges)
427 uint8_t computeAllocType()
const {
432 CalleeEdges, useCallerEdgesForContextInfo()
434 : std::vector<std::shared_ptr<ContextEdge>>());
435 for (
const auto &Edge : Edges) {
446 bool emptyContextIds()
const {
448 CalleeEdges, useCallerEdgesForContextInfo()
450 : std::vector<std::shared_ptr<ContextEdge>>());
451 for (
const auto &Edge : Edges) {
452 if (!Edge->getContextIds().empty())
459 std::vector<ContextNode *> Clones;
462 ContextNode *CloneOf =
nullptr;
464 ContextNode(
bool IsAllocation) : IsAllocation(IsAllocation),
Call() {}
466 ContextNode(
bool IsAllocation, CallInfo
C)
467 : IsAllocation(IsAllocation),
Call(
C) {}
469 void addClone(ContextNode *Clone) {
471 CloneOf->Clones.push_back(Clone);
472 Clone->CloneOf = CloneOf;
474 Clones.push_back(Clone);
476 Clone->CloneOf =
this;
480 ContextNode *getOrigNode() {
487 unsigned int ContextId);
489 ContextEdge *findEdgeFromCallee(
const ContextNode *Callee);
490 ContextEdge *findEdgeFromCaller(
const ContextNode *Caller);
491 void eraseCalleeEdge(
const ContextEdge *Edge);
492 void eraseCallerEdge(
const ContextEdge *Edge);
494 void setCall(CallInfo
C) {
Call = std::move(
C); }
496 bool hasCall()
const {
return (
bool)
Call.call(); }
502 bool isRemoved()
const {
538 bool IsBackedge =
false;
545 : Callee(Callee), Caller(Caller), AllocTypes(
AllocType),
546 ContextIds(std::move(ContextIds)) {}
552 inline void clear() {
562 inline bool isRemoved()
const {
563 if (Callee || Caller)
584 void removeNoneTypeCalleeEdges(ContextNode *
Node);
585 void removeNoneTypeCallerEdges(ContextNode *
Node);
587 recursivelyRemoveNoneTypeCalleeEdges(ContextNode *
Node,
593 template <
class NodeT,
class IteratorT>
594 std::vector<uint64_t>
599 ContextNode *addAllocNode(CallInfo
Call,
const FuncTy *
F);
602 template <
class NodeT,
class IteratorT>
603 void addStackNodesForMIB(
607 std::map<uint64_t, uint32_t> &TotalSizeToContextIdTopNCold);
612 void updateStackNodes();
621 void fixupImportantContexts();
625 void handleCallsitesWithMultipleTargets();
628 void markBackedges();
638 bool partitionCallsByCallee(
640 std::vector<std::pair<CallInfo, ContextNode *>> &NewCallToNode);
647 std::map<const ContextNode *, const FuncTy *> NodeToCallingFunc;
654 using EdgeIter =
typename std::vector<std::shared_ptr<ContextEdge>>
::iterator;
659 struct CallContextInfo {
663 std::vector<uint64_t> StackIds;
677 void removeEdgeFromGraph(ContextEdge *Edge, EdgeIter *EI =
nullptr,
678 bool CalleeIter =
true);
686 void assignStackNodesPostOrder(
700 void propagateDuplicateContextIds(
706 void connectNewNode(ContextNode *NewNode, ContextNode *OrigNode,
714 return static_cast<const DerivedCCG *
>(
this)->getStackId(IdOrIndex);
724 calleesMatch(CallTy
Call, EdgeIter &EI,
729 const FuncTy *getCalleeFunc(CallTy
Call) {
730 return static_cast<DerivedCCG *
>(
this)->getCalleeFunc(
Call);
736 bool calleeMatchesFunc(
737 CallTy
Call,
const FuncTy *Func,
const FuncTy *CallerFunc,
738 std::vector<std::pair<CallTy, FuncTy *>> &FoundCalleeChain) {
739 return static_cast<DerivedCCG *
>(
this)->calleeMatchesFunc(
740 Call, Func, CallerFunc, FoundCalleeChain);
744 bool sameCallee(CallTy Call1, CallTy Call2) {
745 return static_cast<DerivedCCG *
>(
this)->sameCallee(Call1, Call2);
750 std::vector<uint64_t> getStackIdsWithContextNodesForCall(CallTy
Call) {
751 return static_cast<DerivedCCG *
>(
this)->getStackIdsWithContextNodesForCall(
757 return static_cast<DerivedCCG *
>(
this)->getLastStackId(
Call);
763 static_cast<DerivedCCG *
>(
this)->updateAllocationCall(
Call,
AllocType);
768 return static_cast<const DerivedCCG *
>(
this)->getAllocationCallType(
Call);
773 void updateCall(CallInfo &CallerCall, FuncInfo CalleeFunc) {
774 static_cast<DerivedCCG *
>(
this)->updateCall(CallerCall, CalleeFunc);
780 FuncInfo cloneFunctionForCallsite(
782 std::vector<CallInfo> &CallsWithMetadataInFunc,
unsigned CloneNo) {
783 return static_cast<DerivedCCG *
>(
this)->cloneFunctionForCallsite(
784 Func,
Call, CallMap, CallsWithMetadataInFunc, CloneNo);
789 std::string getLabel(
const FuncTy *Func,
const CallTy
Call,
790 unsigned CloneNo)
const {
791 return static_cast<const DerivedCCG *
>(
this)->getLabel(Func,
Call, CloneNo);
795 ContextNode *createNewNode(
bool IsAllocation,
const FuncTy *
F =
nullptr,
796 CallInfo
C = CallInfo()) {
797 NodeOwner.push_back(std::make_unique<ContextNode>(IsAllocation,
C));
798 auto *NewNode = NodeOwner.back().get();
800 NodeToCallingFunc[NewNode] =
F;
801 NewNode->NodeId = NodeOwner.size();
806 ContextNode *getNodeForInst(
const CallInfo &
C);
807 ContextNode *getNodeForAlloc(
const CallInfo &
C);
808 ContextNode *getNodeForStackId(
uint64_t StackId);
830 moveEdgeToNewCalleeClone(
const std::shared_ptr<ContextEdge> &Edge,
837 void moveEdgeToExistingCalleeClone(
const std::shared_ptr<ContextEdge> &Edge,
838 ContextNode *NewCallee,
839 bool NewClone =
false,
847 void moveCalleeEdgeToNewCaller(
const std::shared_ptr<ContextEdge> &Edge,
848 ContextNode *NewCaller);
859 void mergeNodeCalleeClones(
864 void findOtherCallersToShareMerge(
865 ContextNode *
Node, std::vector<std::shared_ptr<ContextEdge>> &CalleeEdges,
893 struct ImportantContextInfo {
895 std::vector<uint64_t> StackIds;
898 unsigned MaxLength = 0;
902 std::map<std::vector<uint64_t>, ContextNode *> StackIdsToNode;
911 void recordStackNode(std::vector<uint64_t> &StackIds, ContextNode *
Node,
925 auto Size = StackIds.size();
926 for (
auto Id : Ids) {
927 auto &Entry = ImportantContextIdInfo[Id];
928 Entry.StackIdsToNode[StackIds] =
Node;
930 if (
Size > Entry.MaxLength)
931 Entry.MaxLength =
Size;
940 std::vector<std::unique_ptr<ContextNode>> NodeOwner;
946 unsigned int LastContextId = 0;
949template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
951 typename CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode;
952template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
954 typename CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextEdge;
955template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
957 typename CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::FuncInfo;
958template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
960 typename CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::CallInfo;
963class ModuleCallsiteContextGraph
964 :
public CallsiteContextGraph<ModuleCallsiteContextGraph, Function,
967 ModuleCallsiteContextGraph(
969 llvm::function_ref<OptimizationRemarkEmitter &(
Function *)> OREGetter);
972 friend CallsiteContextGraph<ModuleCallsiteContextGraph,
Function,
977 bool calleeMatchesFunc(
979 std::vector<std::pair<Instruction *, Function *>> &FoundCalleeChain);
980 bool sameCallee(Instruction *Call1, Instruction *Call2);
981 bool findProfiledCalleeThroughTailCalls(
983 std::vector<std::pair<Instruction *, Function *>> &FoundCalleeChain,
984 bool &FoundMultipleCalleeChains);
986 std::vector<uint64_t> getStackIdsWithContextNodesForCall(Instruction *
Call);
989 void updateCall(CallInfo &CallerCall, FuncInfo CalleeFunc);
990 CallsiteContextGraph<ModuleCallsiteContextGraph,
Function,
992 cloneFunctionForCallsite(FuncInfo &Func, CallInfo &
Call,
993 DenseMap<CallInfo, CallInfo> &CallMap,
994 std::vector<CallInfo> &CallsWithMetadataInFunc,
996 std::string getLabel(
const Function *Func,
const Instruction *
Call,
997 unsigned CloneNo)
const;
1000 llvm::function_ref<OptimizationRemarkEmitter &(
Function *)> OREGetter;
1006struct IndexCall :
public PointerUnion<CallsiteInfo *, AllocInfo *> {
1007 IndexCall() : PointerUnion() {}
1008 IndexCall(std::nullptr_t) : IndexCall() {}
1009 IndexCall(CallsiteInfo *StackNode) : PointerUnion(StackNode) {}
1010 IndexCall(AllocInfo *AllocNode) : PointerUnion(AllocNode) {}
1011 IndexCall(PointerUnion PT) : PointerUnion(PT) {}
1013 IndexCall *operator->() {
return this; }
1015 void print(raw_ostream &OS)
const {
1016 PointerUnion<CallsiteInfo *, AllocInfo *>
Base = *
this;
1041class IndexCallsiteContextGraph
1042 :
public CallsiteContextGraph<IndexCallsiteContextGraph, FunctionSummary,
1045 IndexCallsiteContextGraph(
1046 ModuleSummaryIndex &Index,
1050 ~IndexCallsiteContextGraph() {
1055 for (
auto &
I : FunctionCalleesToSynthesizedCallsiteInfos) {
1057 for (
auto &Callsite :
I.second)
1058 FS->addCallsite(std::move(*Callsite.second));
1063 friend CallsiteContextGraph<IndexCallsiteContextGraph, FunctionSummary,
1067 const FunctionSummary *getCalleeFunc(IndexCall &
Call);
1068 bool calleeMatchesFunc(
1069 IndexCall &
Call,
const FunctionSummary *Func,
1070 const FunctionSummary *CallerFunc,
1071 std::vector<std::pair<IndexCall, FunctionSummary *>> &FoundCalleeChain);
1072 bool sameCallee(IndexCall &Call1, IndexCall &Call2);
1073 bool findProfiledCalleeThroughTailCalls(
1074 ValueInfo ProfiledCallee, ValueInfo CurCallee,
unsigned Depth,
1075 std::vector<std::pair<IndexCall, FunctionSummary *>> &FoundCalleeChain,
1076 bool &FoundMultipleCalleeChains);
1078 std::vector<uint64_t> getStackIdsWithContextNodesForCall(IndexCall &
Call);
1081 void updateCall(CallInfo &CallerCall, FuncInfo CalleeFunc);
1082 CallsiteContextGraph<IndexCallsiteContextGraph, FunctionSummary,
1083 IndexCall>::FuncInfo
1084 cloneFunctionForCallsite(FuncInfo &Func, CallInfo &
Call,
1085 DenseMap<CallInfo, CallInfo> &CallMap,
1086 std::vector<CallInfo> &CallsWithMetadataInFunc,
1088 std::string getLabel(
const FunctionSummary *Func,
const IndexCall &
Call,
1089 unsigned CloneNo)
const;
1090 DenseSet<GlobalValue::GUID> findAliaseeGUIDsPrevailingInDifferentModule();
1094 std::map<const FunctionSummary *, ValueInfo> FSToVIMap;
1096 const ModuleSummaryIndex &Index;
1104 DenseMap<FunctionSummary *,
1105 std::map<ValueInfo, std::unique_ptr<CallsiteInfo>>>
1106 FunctionCalleesToSynthesizedCallsiteInfos;
1117 :
public DenseMapInfo<std::pair<IndexCall, unsigned>> {};
1120 :
public DenseMapInfo<PointerUnion<CallsiteInfo *, AllocInfo *>> {};
1141template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1142bool allocTypesMatch(
1143 const std::vector<uint8_t> &InAllocTypes,
1144 const std::vector<std::shared_ptr<ContextEdge<DerivedCCG, FuncTy, CallTy>>>
1148 assert(InAllocTypes.size() == Edges.size());
1150 InAllocTypes.begin(), InAllocTypes.end(), Edges.begin(), Edges.end(),
1152 const std::shared_ptr<ContextEdge<DerivedCCG, FuncTy, CallTy>> &r) {
1156 if (l == (uint8_t)AllocationType::None ||
1157 r->AllocTypes == (uint8_t)AllocationType::None)
1159 return allocTypeToUse(l) == allocTypeToUse(r->AllocTypes);
1168template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1169bool allocTypesMatchClone(
1170 const std::vector<uint8_t> &InAllocTypes,
1171 const ContextNode<DerivedCCG, FuncTy, CallTy> *Clone) {
1172 const ContextNode<DerivedCCG, FuncTy, CallTy> *
Node = Clone->CloneOf;
1176 assert(InAllocTypes.size() ==
Node->CalleeEdges.size());
1180 for (
const auto &
E : Clone->CalleeEdges) {
1182 EdgeCalleeMap[
E->Callee] =
E->AllocTypes;
1186 for (
unsigned I = 0;
I <
Node->CalleeEdges.size();
I++) {
1187 auto Iter = EdgeCalleeMap.
find(
Node->CalleeEdges[
I]->Callee);
1189 if (Iter == EdgeCalleeMap.
end())
1197 if (allocTypeToUse(Iter->second) != allocTypeToUse(InAllocTypes[
I]))
1205template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1206typename CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode *
1207CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::getNodeForInst(
1208 const CallInfo &
C) {
1209 ContextNode *
Node = getNodeForAlloc(
C);
1213 return NonAllocationCallToContextNodeMap.lookup(
C);
1216template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1217typename CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode *
1218CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::getNodeForAlloc(
1219 const CallInfo &
C) {
1220 return AllocationCallToContextNodeMap.lookup(
C);
1223template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1224typename CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode *
1225CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::getNodeForStackId(
1227 auto StackEntryNode = StackEntryIdToContextNodeMap.find(StackId);
1228 if (StackEntryNode != StackEntryIdToContextNodeMap.end())
1229 return StackEntryNode->second;
1233template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1234void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode::
1236 unsigned int ContextId) {
1237 for (
auto &
Edge : CallerEdges) {
1238 if (
Edge->Caller == Caller) {
1240 Edge->getContextIds().insert(ContextId);
1244 std::shared_ptr<ContextEdge>
Edge = std::make_shared<ContextEdge>(
1245 this, Caller, (uint8_t)
AllocType, DenseSet<uint32_t>({ContextId}));
1246 CallerEdges.push_back(
Edge);
1250template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1251void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::removeEdgeFromGraph(
1252 ContextEdge *
Edge, EdgeIter *EI,
bool CalleeIter) {
1268 auto CalleeCallerCount =
Callee->CallerEdges.size();
1269 auto CallerCalleeCount =
Caller->CalleeEdges.size();
1274 }
else if (CalleeIter) {
1276 *EI =
Caller->CalleeEdges.erase(*EI);
1279 *EI =
Callee->CallerEdges.erase(*EI);
1281 assert(
Callee->CallerEdges.size() < CalleeCallerCount);
1282 assert(
Caller->CalleeEdges.size() < CallerCalleeCount);
1285template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1286void CallsiteContextGraph<
1287 DerivedCCG, FuncTy, CallTy>::removeNoneTypeCalleeEdges(ContextNode *Node) {
1288 for (
auto EI =
Node->CalleeEdges.begin(); EI !=
Node->CalleeEdges.end();) {
1290 if (
Edge->AllocTypes == (uint8_t)AllocationType::None) {
1292 removeEdgeFromGraph(
Edge.get(), &EI,
true);
1298template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1299void CallsiteContextGraph<
1300 DerivedCCG, FuncTy, CallTy>::removeNoneTypeCallerEdges(ContextNode *Node) {
1301 for (
auto EI =
Node->CallerEdges.begin(); EI !=
Node->CallerEdges.end();) {
1303 if (
Edge->AllocTypes == (uint8_t)AllocationType::None) {
1305 Edge->Caller->eraseCalleeEdge(
Edge.get());
1306 EI =
Node->CallerEdges.erase(EI);
1312template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1313typename CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextEdge *
1314CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode::
1315 findEdgeFromCallee(
const ContextNode *Callee) {
1316 for (
const auto &
Edge : CalleeEdges)
1317 if (
Edge->Callee == Callee)
1322template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1323typename CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextEdge *
1324CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode::
1325 findEdgeFromCaller(
const ContextNode *Caller) {
1326 for (
const auto &
Edge : CallerEdges)
1327 if (
Edge->Caller == Caller)
1332template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1333void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode::
1334 eraseCalleeEdge(
const ContextEdge *
Edge) {
1336 CalleeEdges, [
Edge](
const std::shared_ptr<ContextEdge> &CalleeEdge) {
1337 return CalleeEdge.get() ==
Edge;
1339 assert(EI != CalleeEdges.end());
1340 CalleeEdges.erase(EI);
1343template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1344void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode::
1345 eraseCallerEdge(
const ContextEdge *
Edge) {
1347 CallerEdges, [
Edge](
const std::shared_ptr<ContextEdge> &CallerEdge) {
1348 return CallerEdge.get() ==
Edge;
1350 assert(EI != CallerEdges.end());
1351 CallerEdges.erase(EI);
1354template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1355uint8_t CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::computeAllocType(
1356 DenseSet<uint32_t> &ContextIds)
const {
1358 (uint8_t)AllocationType::Cold | (uint8_t)AllocationType::NotCold;
1359 uint8_t
AllocType = (uint8_t)AllocationType::None;
1360 for (
auto Id : ContextIds) {
1361 AllocType |= (uint8_t)ContextIdToAllocationType.at(Id);
1369template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1371CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::intersectAllocTypesImpl(
1372 const DenseSet<uint32_t> &Node1Ids,
1373 const DenseSet<uint32_t> &Node2Ids)
const {
1375 (uint8_t)AllocationType::Cold | (uint8_t)AllocationType::NotCold;
1376 uint8_t
AllocType = (uint8_t)AllocationType::None;
1377 for (
auto Id : Node1Ids) {
1378 if (!Node2Ids.
count(Id))
1380 AllocType |= (uint8_t)ContextIdToAllocationType.at(Id);
1388template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1389uint8_t CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::intersectAllocTypes(
1390 const DenseSet<uint32_t> &Node1Ids,
1391 const DenseSet<uint32_t> &Node2Ids)
const {
1392 if (Node1Ids.
size() < Node2Ids.
size())
1393 return intersectAllocTypesImpl(Node1Ids, Node2Ids);
1395 return intersectAllocTypesImpl(Node2Ids, Node1Ids);
1398template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1399typename CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode *
1400CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::addAllocNode(
1401 CallInfo
Call,
const FuncTy *
F) {
1403 ContextNode *AllocNode = createNewNode(
true,
F,
Call);
1404 AllocationCallToContextNodeMap[
Call] = AllocNode;
1406 AllocNode->OrigStackOrAllocId = LastContextId;
1409 AllocNode->AllocTypes = (uint8_t)AllocationType::None;
1425template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1426template <
class NodeT,
class IteratorT>
1427void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::addStackNodesForMIB(
1428 ContextNode *AllocNode, CallStack<NodeT, IteratorT> &StackContext,
1431 std::map<uint64_t, uint32_t> &TotalSizeToContextIdTopNCold) {
1437 ContextIdToAllocationType[++LastContextId] =
AllocType;
1439 bool IsImportant =
false;
1440 if (!ContextSizeInfo.
empty()) {
1441 auto &
Entry = ContextIdToContextSizeInfos[LastContextId];
1446 for (
auto &CSI : ContextSizeInfo)
1447 TotalCold += CSI.TotalSize;
1453 TotalCold > TotalSizeToContextIdTopNCold.begin()->first) {
1456 auto IdToRemove = TotalSizeToContextIdTopNCold.begin()->second;
1457 TotalSizeToContextIdTopNCold.erase(
1458 TotalSizeToContextIdTopNCold.begin());
1459 assert(ImportantContextIdInfo.count(IdToRemove));
1460 ImportantContextIdInfo.erase(IdToRemove);
1462 TotalSizeToContextIdTopNCold[TotalCold] = LastContextId;
1466 Entry.insert(
Entry.begin(), ContextSizeInfo.begin(), ContextSizeInfo.end());
1470 AllocNode->AllocTypes |= (uint8_t)
AllocType;
1475 ContextNode *PrevNode = AllocNode;
1479 SmallSet<uint64_t, 8> StackIdSet;
1482 ContextIter != StackContext.
end(); ++ContextIter) {
1483 auto StackId = getStackId(*ContextIter);
1485 ImportantContextIdInfo[LastContextId].StackIds.push_back(StackId);
1486 ContextNode *StackNode = getNodeForStackId(StackId);
1488 StackNode = createNewNode(
false);
1489 StackEntryIdToContextNodeMap[StackId] = StackNode;
1490 StackNode->OrigStackOrAllocId = StackId;
1495 auto Ins = StackIdSet.
insert(StackId);
1497 StackNode->Recursive =
true;
1499 StackNode->AllocTypes |= (uint8_t)
AllocType;
1500 PrevNode->addOrUpdateCallerEdge(StackNode,
AllocType, LastContextId);
1501 PrevNode = StackNode;
1505template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1507CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::duplicateContextIds(
1508 const DenseSet<uint32_t> &StackSequenceContextIds,
1509 DenseMap<uint32_t, DenseSet<uint32_t>> &OldToNewContextIds) {
1510 DenseSet<uint32_t> NewContextIds;
1511 for (
auto OldId : StackSequenceContextIds) {
1512 NewContextIds.
insert(++LastContextId);
1513 OldToNewContextIds[OldId].insert(LastContextId);
1514 assert(ContextIdToAllocationType.count(OldId));
1516 ContextIdToAllocationType[LastContextId] = ContextIdToAllocationType[OldId];
1517 auto CSI = ContextIdToContextSizeInfos.find(OldId);
1518 if (CSI != ContextIdToContextSizeInfos.end())
1519 ContextIdToContextSizeInfos[LastContextId] = CSI->second;
1520 if (DotAllocContextIds.
contains(OldId))
1521 DotAllocContextIds.
insert(LastContextId);
1523 return NewContextIds;
1526template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1527void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::
1528 propagateDuplicateContextIds(
1529 const DenseMap<uint32_t, DenseSet<uint32_t>> &OldToNewContextIds) {
1531 auto GetNewIds = [&OldToNewContextIds](
const DenseSet<uint32_t> &ContextIds) {
1532 DenseSet<uint32_t> NewIds;
1533 for (
auto Id : ContextIds)
1534 if (
auto NewId = OldToNewContextIds.find(Id);
1535 NewId != OldToNewContextIds.end())
1541 auto UpdateCallers = [&](ContextNode *
Node,
1542 DenseSet<const ContextEdge *> &Visited,
1543 auto &&UpdateCallers) ->
void {
1544 for (
const auto &
Edge :
Node->CallerEdges) {
1548 ContextNode *NextNode =
Edge->Caller;
1549 DenseSet<uint32_t> NewIdsToAdd = GetNewIds(
Edge->getContextIds());
1552 if (!NewIdsToAdd.
empty()) {
1553 Edge->getContextIds().insert_range(NewIdsToAdd);
1554 UpdateCallers(NextNode, Visited, UpdateCallers);
1559 DenseSet<const ContextEdge *> Visited;
1560 for (
auto &Entry : AllocationCallToContextNodeMap) {
1562 UpdateCallers(Node, Visited, UpdateCallers);
1566template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1567void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::connectNewNode(
1568 ContextNode *NewNode, ContextNode *OrigNode,
bool TowardsCallee,
1571 DenseSet<uint32_t> RemainingContextIds) {
1573 TowardsCallee ? OrigNode->CalleeEdges : OrigNode->CallerEdges;
1574 DenseSet<uint32_t> RecursiveContextIds;
1575 DenseSet<uint32_t> AllCallerContextIds;
1580 for (
auto &CE : OrigEdges) {
1581 AllCallerContextIds.
reserve(
CE->getContextIds().size());
1582 for (
auto Id :
CE->getContextIds())
1583 if (!AllCallerContextIds.
insert(Id).second)
1584 RecursiveContextIds.
insert(Id);
1588 for (
auto EI = OrigEdges.begin(); EI != OrigEdges.end();) {
1590 DenseSet<uint32_t> NewEdgeContextIds;
1591 DenseSet<uint32_t> NotFoundContextIds;
1595 set_subtract(
Edge->getContextIds(), RemainingContextIds, NewEdgeContextIds,
1596 NotFoundContextIds);
1599 if (RecursiveContextIds.
empty()) {
1602 RemainingContextIds.
swap(NotFoundContextIds);
1612 DenseSet<uint32_t> NonRecursiveRemainingCurEdgeIds =
1614 set_subtract(RemainingContextIds, NonRecursiveRemainingCurEdgeIds);
1617 if (NewEdgeContextIds.
empty()) {
1621 if (TowardsCallee) {
1622 uint8_t NewAllocType = computeAllocType(NewEdgeContextIds);
1623 auto NewEdge = std::make_shared<ContextEdge>(
1624 Edge->Callee, NewNode, NewAllocType, std::move(NewEdgeContextIds));
1625 NewNode->CalleeEdges.push_back(NewEdge);
1626 NewEdge->Callee->CallerEdges.push_back(NewEdge);
1628 uint8_t NewAllocType = computeAllocType(NewEdgeContextIds);
1629 auto NewEdge = std::make_shared<ContextEdge>(
1630 NewNode,
Edge->Caller, NewAllocType, std::move(NewEdgeContextIds));
1631 NewNode->CallerEdges.push_back(NewEdge);
1632 NewEdge->Caller->CalleeEdges.push_back(NewEdge);
1635 if (
Edge->getContextIds().empty()) {
1636 removeEdgeFromGraph(
Edge.get(), &EI, TowardsCallee);
1643template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1645 const std::shared_ptr<ContextEdge<DerivedCCG, FuncTy, CallTy>> &Edge) {
1649 assert(!Edge->ContextIds.empty());
1652template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1654 bool CheckEdges =
true) {
1655 if (
Node->isRemoved())
1659 auto NodeContextIds =
Node->getContextIds();
1663 if (
Node->CallerEdges.size()) {
1665 Node->CallerEdges.front()->ContextIds);
1669 set_union(CallerEdgeContextIds, Edge->ContextIds);
1676 NodeContextIds == CallerEdgeContextIds ||
1679 if (
Node->CalleeEdges.size()) {
1681 Node->CalleeEdges.front()->ContextIds);
1685 set_union(CalleeEdgeContextIds, Edge->getContextIds());
1691 NodeContextIds == CalleeEdgeContextIds);
1700 for (
const auto &
E :
Node->CalleeEdges)
1706template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1707void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::
1708 assignStackNodesPostOrder(ContextNode *Node,
1709 DenseSet<const ContextNode *> &Visited,
1710 DenseMap<
uint64_t, std::vector<CallContextInfo>>
1711 &StackIdToMatchingCalls,
1712 DenseMap<CallInfo, CallInfo> &CallToMatchingCall,
1713 const DenseSet<uint32_t> &ImportantContextIds) {
1721 auto CallerEdges =
Node->CallerEdges;
1722 for (
auto &
Edge : CallerEdges) {
1724 if (
Edge->isRemoved()) {
1728 assignStackNodesPostOrder(
Edge->Caller, Visited, StackIdToMatchingCalls,
1729 CallToMatchingCall, ImportantContextIds);
1738 if (
Node->IsAllocation ||
1739 !StackIdToMatchingCalls.count(
Node->OrigStackOrAllocId))
1742 auto &Calls = StackIdToMatchingCalls[
Node->OrigStackOrAllocId];
1746 if (Calls.size() == 1) {
1747 auto &[
Call, Ids,
Func, SavedContextIds] = Calls[0];
1748 if (Ids.size() == 1) {
1749 assert(SavedContextIds.empty());
1751 assert(Node == getNodeForStackId(Ids[0]));
1752 if (
Node->Recursive)
1755 NonAllocationCallToContextNodeMap[
Call] =
Node;
1757 recordStackNode(Ids, Node,
Node->getContextIds(), ImportantContextIds);
1766 ContextNode *LastNode = getNodeForStackId(LastId);
1769 assert(LastNode == Node);
1771 ContextNode *LastNode =
Node;
1776 DenseSet<uint32_t> LastNodeContextIds = LastNode->getContextIds();
1778 [[maybe_unused]]
bool PrevIterCreatedNode =
false;
1779 bool CreatedNode =
false;
1780 for (
unsigned I = 0;
I < Calls.size();
1781 I++, PrevIterCreatedNode = CreatedNode) {
1782 CreatedNode =
false;
1783 auto &[
Call, Ids,
Func, SavedContextIds] = Calls[
I];
1786 if (SavedContextIds.empty()) {
1793 auto MatchingCall = CallToMatchingCall[
Call];
1794 if (!NonAllocationCallToContextNodeMap.contains(MatchingCall)) {
1798 assert(
I > 0 && !PrevIterCreatedNode);
1801 NonAllocationCallToContextNodeMap[MatchingCall]->MatchingCalls.push_back(
1806 assert(LastId == Ids.back());
1815 ContextNode *PrevNode = LastNode;
1819 for (
auto IdIter = Ids.rbegin() + 1; IdIter != Ids.rend(); IdIter++) {
1821 ContextNode *CurNode = getNodeForStackId(Id);
1825 assert(!CurNode->Recursive);
1827 auto *
Edge = CurNode->findEdgeFromCaller(PrevNode);
1839 if (SavedContextIds.empty()) {
1848 ContextNode *NewNode = createNewNode(
false, Func,
Call);
1849 NonAllocationCallToContextNodeMap[
Call] = NewNode;
1851 NewNode->AllocTypes = computeAllocType(SavedContextIds);
1853 ContextNode *FirstNode = getNodeForStackId(Ids[0]);
1859 connectNewNode(NewNode, FirstNode,
true, SavedContextIds);
1864 connectNewNode(NewNode, LastNode,
false, SavedContextIds);
1869 for (
auto Id : Ids) {
1870 ContextNode *CurNode = getNodeForStackId(Id);
1877 auto *PrevEdge = CurNode->findEdgeFromCallee(PrevNode);
1884 set_subtract(PrevEdge->getContextIds(), SavedContextIds);
1885 if (PrevEdge->getContextIds().empty())
1886 removeEdgeFromGraph(PrevEdge);
1891 CurNode->AllocTypes = CurNode->CalleeEdges.empty()
1892 ? (uint8_t)AllocationType::None
1893 : CurNode->computeAllocType();
1897 recordStackNode(Ids, NewNode, SavedContextIds, ImportantContextIds);
1901 for (
auto Id : Ids) {
1902 ContextNode *CurNode = getNodeForStackId(Id);
1911template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1912void CallsiteContextGraph<DerivedCCG, FuncTy,
1913 CallTy>::fixupImportantContexts() {
1914 if (ImportantContextIdInfo.empty())
1918 NumImportantContextIds = ImportantContextIdInfo.size();
1924 exportToDot(
"beforestackfixup");
1949 for (
auto &[CurContextId, Info] : ImportantContextIdInfo) {
1950 if (
Info.StackIdsToNode.empty())
1953 ContextNode *PrevNode =
nullptr;
1954 ContextNode *CurNode =
nullptr;
1955 DenseSet<const ContextEdge *> VisitedEdges;
1956 ArrayRef<uint64_t> AllStackIds(
Info.StackIds);
1959 for (
unsigned I = 0;
I < AllStackIds.size();
I++, PrevNode = CurNode) {
1963 auto LenToEnd = AllStackIds.size() -
I;
1971 auto CheckStackIds = AllStackIds.slice(
I, Len);
1972 auto EntryIt =
Info.StackIdsToNode.find(CheckStackIds);
1973 if (EntryIt ==
Info.StackIdsToNode.end())
1975 CurNode = EntryIt->second;
1992 auto *CurEdge = PrevNode->findEdgeFromCaller(CurNode);
1995 if (CurEdge->getContextIds().insert(CurContextId).second) {
1996 NumFixupEdgeIdsInserted++;
2001 NumFixupEdgesAdded++;
2002 DenseSet<uint32_t> ContextIds({CurContextId});
2003 auto AllocType = computeAllocType(ContextIds);
2004 auto NewEdge = std::make_shared<ContextEdge>(
2005 PrevNode, CurNode,
AllocType, std::move(ContextIds));
2006 PrevNode->CallerEdges.push_back(NewEdge);
2007 CurNode->CalleeEdges.push_back(NewEdge);
2009 CurEdge = NewEdge.get();
2012 VisitedEdges.
insert(CurEdge);
2015 for (
auto &
Edge : PrevNode->CallerEdges) {
2019 Edge->getContextIds().erase(CurContextId);
2027template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
2028void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::updateStackNodes() {
2036 DenseMap<uint64_t, std::vector<CallContextInfo>> StackIdToMatchingCalls;
2037 for (
auto &[Func, CallsWithMetadata] : FuncToCallsWithMetadata) {
2038 for (
auto &
Call : CallsWithMetadata) {
2040 if (AllocationCallToContextNodeMap.count(
Call))
2042 auto StackIdsWithContextNodes =
2043 getStackIdsWithContextNodesForCall(
Call.call());
2046 if (StackIdsWithContextNodes.empty())
2050 StackIdToMatchingCalls[StackIdsWithContextNodes.back()].push_back(
2051 {
Call.call(), StackIdsWithContextNodes,
Func, {}});
2061 DenseMap<uint32_t, DenseSet<uint32_t>> OldToNewContextIds;
2065 DenseMap<CallInfo, CallInfo> CallToMatchingCall;
2066 for (
auto &It : StackIdToMatchingCalls) {
2067 auto &Calls = It.getSecond();
2069 if (Calls.size() == 1) {
2070 auto &Ids = Calls[0].StackIds;
2071 if (Ids.size() == 1)
2084 DenseMap<const FuncTy *, unsigned> FuncToIndex;
2085 for (
const auto &[Idx, CallCtxInfo] :
enumerate(Calls))
2086 FuncToIndex.
insert({CallCtxInfo.Func, Idx});
2089 [&FuncToIndex](
const CallContextInfo &
A,
const CallContextInfo &
B) {
2090 return A.StackIds.size() >
B.StackIds.size() ||
2091 (
A.StackIds.size() ==
B.StackIds.size() &&
2092 (
A.StackIds <
B.StackIds ||
2093 (
A.StackIds ==
B.StackIds &&
2094 FuncToIndex[
A.Func] < FuncToIndex[
B.Func])));
2101 ContextNode *LastNode = getNodeForStackId(LastId);
2105 if (LastNode->Recursive)
2110 DenseSet<uint32_t> LastNodeContextIds = LastNode->getContextIds();
2118 DenseSet<const FuncTy *> MatchingIdsFuncSet;
2121 for (
unsigned I = 0;
I < Calls.size();
I++) {
2122 auto &[
Call, Ids,
Func, SavedContextIds] = Calls[
I];
2123 assert(SavedContextIds.empty());
2124 assert(LastId == Ids.back());
2129 if (
I > 0 && Ids != Calls[
I - 1].StackIds)
2130 MatchingIdsFuncSet.
clear();
2137 DenseSet<uint32_t> StackSequenceContextIds = LastNodeContextIds;
2139 ContextNode *PrevNode = LastNode;
2140 ContextNode *CurNode = LastNode;
2145 for (
auto IdIter = Ids.rbegin() + 1; IdIter != Ids.rend(); IdIter++) {
2147 CurNode = getNodeForStackId(Id);
2151 if (CurNode->Recursive) {
2156 auto *
Edge = CurNode->findEdgeFromCaller(PrevNode);
2177 if (StackSequenceContextIds.
empty()) {
2190 if (Ids.back() != getLastStackId(
Call)) {
2191 for (
const auto &PE : LastNode->CallerEdges) {
2192 set_subtract(StackSequenceContextIds, PE->getContextIds());
2193 if (StackSequenceContextIds.
empty())
2197 if (StackSequenceContextIds.
empty())
2209 MatchingIdsFuncSet.
insert(Func);
2216 bool DuplicateContextIds =
false;
2217 for (
unsigned J =
I + 1; J < Calls.size(); J++) {
2218 auto &CallCtxInfo = Calls[J];
2219 auto &NextIds = CallCtxInfo.StackIds;
2222 auto *NextFunc = CallCtxInfo.Func;
2223 if (NextFunc != Func) {
2226 DuplicateContextIds =
true;
2229 auto &NextCall = CallCtxInfo.Call;
2230 CallToMatchingCall[NextCall] =
Call;
2241 OldToNewContextIds.
reserve(OldToNewContextIds.
size() +
2242 StackSequenceContextIds.
size());
2245 ? duplicateContextIds(StackSequenceContextIds, OldToNewContextIds)
2246 : StackSequenceContextIds;
2247 assert(!SavedContextIds.empty());
2249 if (!DuplicateContextIds) {
2253 set_subtract(LastNodeContextIds, StackSequenceContextIds);
2254 if (LastNodeContextIds.
empty())
2261 propagateDuplicateContextIds(OldToNewContextIds);
2271 DenseSet<const ContextNode *> Visited;
2273 ImportantContextIdInfo.keys());
2274 for (
auto &Entry : AllocationCallToContextNodeMap)
2275 assignStackNodesPostOrder(
Entry.second, Visited, StackIdToMatchingCalls,
2276 CallToMatchingCall, ImportantContextIds);
2278 fixupImportantContexts();
2284uint64_t ModuleCallsiteContextGraph::getLastStackId(Instruction *
Call) {
2285 CallStack<MDNode, MDNode::op_iterator> CallsiteContext(
2287 return CallsiteContext.
back();
2290uint64_t IndexCallsiteContextGraph::getLastStackId(IndexCall &
Call) {
2292 CallStack<CallsiteInfo, SmallVector<unsigned>::const_iterator>
2295 return Index.getStackIdAtIndex(CallsiteContext.
back());
2317 auto Pos =
F.getName().find_last_of(
'.');
2320 bool Err =
F.getName().drop_front(Pos + 1).getAsInteger(10, CloneNo);
2326std::string ModuleCallsiteContextGraph::getLabel(
const Function *Func,
2327 const Instruction *
Call,
2328 unsigned CloneNo)
const {
2334std::string IndexCallsiteContextGraph::getLabel(
const FunctionSummary *Func,
2335 const IndexCall &
Call,
2336 unsigned CloneNo)
const {
2337 auto VI = FSToVIMap.find(Func);
2338 assert(VI != FSToVIMap.end());
2341 return CallerName +
" -> alloc";
2344 return CallerName +
" -> " +
2346 Callsite->Clones[CloneNo]);
2350std::vector<uint64_t>
2351ModuleCallsiteContextGraph::getStackIdsWithContextNodesForCall(
2352 Instruction *
Call) {
2353 CallStack<MDNode, MDNode::op_iterator> CallsiteContext(
2355 return getStackIdsWithContextNodes<MDNode, MDNode::op_iterator>(
2359std::vector<uint64_t>
2360IndexCallsiteContextGraph::getStackIdsWithContextNodesForCall(IndexCall &
Call) {
2362 CallStack<CallsiteInfo, SmallVector<unsigned>::const_iterator>
2364 return getStackIdsWithContextNodes<CallsiteInfo,
2365 SmallVector<unsigned>::const_iterator>(
2369template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
2370template <
class NodeT,
class IteratorT>
2371std::vector<uint64_t>
2372CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::getStackIdsWithContextNodes(
2373 CallStack<NodeT, IteratorT> &CallsiteContext) {
2374 std::vector<uint64_t> StackIds;
2375 for (
auto IdOrIndex : CallsiteContext) {
2376 auto StackId = getStackId(IdOrIndex);
2377 ContextNode *
Node = getNodeForStackId(StackId);
2380 StackIds.push_back(StackId);
2385ModuleCallsiteContextGraph::ModuleCallsiteContextGraph(
2387 llvm::function_ref<OptimizationRemarkEmitter &(
Function *)> OREGetter)
2388 :
Mod(
M), OREGetter(OREGetter) {
2392 std::map<uint64_t, uint32_t> TotalSizeToContextIdTopNCold;
2394 std::vector<CallInfo> CallsWithMetadata;
2395 for (
auto &BB :
F) {
2396 for (
auto &
I : BB) {
2399 if (
auto *MemProfMD =
I.getMetadata(LLVMContext::MD_memprof)) {
2400 CallsWithMetadata.push_back(&
I);
2401 auto *AllocNode = addAllocNode(&
I, &
F);
2402 auto *CallsiteMD =
I.getMetadata(LLVMContext::MD_callsite);
2406 for (
auto &MDOp : MemProfMD->operands()) {
2408 std::vector<ContextTotalSize> ContextSizeInfo;
2410 if (MIBMD->getNumOperands() > 2) {
2411 for (
unsigned I = 2;
I < MIBMD->getNumOperands();
I++) {
2412 MDNode *ContextSizePair =
2421 ContextSizeInfo.push_back({FullStackId, TotalSize});
2427 addStackNodesForMIB<MDNode, MDNode::op_iterator>(
2428 AllocNode, StackContext, CallsiteContext,
2430 TotalSizeToContextIdTopNCold);
2435 DotAllocContextIds = AllocNode->getContextIds();
2439 I.setMetadata(LLVMContext::MD_memprof,
nullptr);
2440 I.setMetadata(LLVMContext::MD_callsite,
nullptr);
2443 else if (
I.getMetadata(LLVMContext::MD_callsite)) {
2444 CallsWithMetadata.push_back(&
I);
2448 if (!CallsWithMetadata.empty())
2449 FuncToCallsWithMetadata[&
F] = CallsWithMetadata;
2453 dbgs() <<
"CCG before updating call stack chains:\n";
2458 exportToDot(
"prestackupdate");
2463 exportToDot(
"poststackupdate");
2465 handleCallsitesWithMultipleTargets();
2470 for (
auto &FuncEntry : FuncToCallsWithMetadata)
2471 for (
auto &
Call : FuncEntry.second)
2472 Call.call()->setMetadata(LLVMContext::MD_callsite,
nullptr);
2478IndexCallsiteContextGraph::findAliaseeGUIDsPrevailingInDifferentModule() {
2479 DenseSet<GlobalValue::GUID> AliaseeGUIDs;
2480 for (
auto &
I : Index) {
2482 for (
auto &S :
VI.getSummaryList()) {
2487 auto *AliaseeSummary = &AS->getAliasee();
2495 !isPrevailing(
VI.getGUID(), S.get()))
2500 auto AliaseeGUID = AS->getAliaseeGUID();
2502 if (!isPrevailing(AliaseeGUID, AliaseeSummary))
2503 AliaseeGUIDs.
insert(AliaseeGUID);
2506 AliaseesPrevailingInDiffModuleFromAlias += AliaseeGUIDs.
size();
2507 return AliaseeGUIDs;
2510IndexCallsiteContextGraph::IndexCallsiteContextGraph(
2511 ModuleSummaryIndex &Index,
2521 findAliaseeGUIDsPrevailingInDifferentModule();
2525 std::map<uint64_t, uint32_t> TotalSizeToContextIdTopNCold;
2530 for (
const auto &
I : Index.sortedGlobalValueSummariesRange()) {
2531 auto VI = Index.getValueInfo(
I);
2532 if (GUIDsToSkip.
contains(VI.getGUID()))
2534 for (
auto &S : VI.getSummaryList()) {
2543 !isPrevailing(VI.getGUID(), S.get()))
2548 std::vector<CallInfo> CallsWithMetadata;
2549 if (!
FS->allocs().empty()) {
2550 for (
auto &AN :
FS->mutableAllocs()) {
2555 if (AN.MIBs.empty())
2557 IndexCall AllocCall(&AN);
2558 CallsWithMetadata.push_back(AllocCall);
2559 auto *AllocNode = addAllocNode(AllocCall, FS);
2567 AN.ContextSizeInfos.size() == AN.MIBs.size());
2569 for (
auto &MIB : AN.MIBs) {
2572 std::vector<ContextTotalSize> ContextSizeInfo;
2573 if (!AN.ContextSizeInfos.empty()) {
2574 for (
auto [FullStackId, TotalSize] : AN.ContextSizeInfos[
I])
2575 ContextSizeInfo.push_back({FullStackId, TotalSize});
2577 addStackNodesForMIB<MIBInfo, SmallVector<unsigned>::const_iterator>(
2578 AllocNode, StackContext, EmptyContext, MIB.AllocType,
2579 ContextSizeInfo, TotalSizeToContextIdTopNCold);
2585 DotAllocContextIds = AllocNode->getContextIds();
2591 AN.Versions[0] = (
uint8_t)allocTypeToUse(AllocNode->AllocTypes);
2595 if (!
FS->callsites().empty())
2596 for (
auto &SN :
FS->mutableCallsites()) {
2597 IndexCall StackNodeCall(&SN);
2598 CallsWithMetadata.push_back(StackNodeCall);
2601 if (!CallsWithMetadata.empty())
2602 FuncToCallsWithMetadata[
FS] = CallsWithMetadata;
2604 if (!
FS->allocs().empty() || !
FS->callsites().empty())
2610 dbgs() <<
"CCG before updating call stack chains:\n";
2615 exportToDot(
"prestackupdate");
2620 exportToDot(
"poststackupdate");
2622 handleCallsitesWithMultipleTargets();
2627template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
2628void CallsiteContextGraph<DerivedCCG, FuncTy,
2629 CallTy>::handleCallsitesWithMultipleTargets() {
2644 std::vector<std::pair<CallInfo, ContextNode *>> NewCallToNode;
2645 for (
auto &Entry : NonAllocationCallToContextNodeMap) {
2646 auto *
Node = Entry.second;
2655 std::vector<CallInfo> AllCalls;
2656 AllCalls.reserve(
Node->MatchingCalls.size() + 1);
2657 AllCalls.push_back(
Node->Call);
2671 if (partitionCallsByCallee(
Node, AllCalls, NewCallToNode))
2674 auto It = AllCalls.begin();
2676 for (; It != AllCalls.end(); ++It) {
2679 for (
auto EI =
Node->CalleeEdges.begin(); EI !=
Node->CalleeEdges.end();
2682 if (!Edge->Callee->hasCall())
2684 assert(NodeToCallingFunc.count(Edge->Callee));
2686 if (!calleesMatch(
ThisCall.call(), EI, TailCallToContextNodeMap)) {
2695 if (
Node->Call != ThisCall) {
2696 Node->setCall(ThisCall);
2707 Node->MatchingCalls.clear();
2710 if (It == AllCalls.end()) {
2711 RemovedEdgesWithMismatchedCallees++;
2715 Node->setCall(CallInfo());
2720 for (++It; It != AllCalls.end(); ++It) {
2724 Node->MatchingCalls.push_back(ThisCall);
2733 NonAllocationCallToContextNodeMap.remove_if([](
const auto &it) {
2734 return !it.second->hasCall() || it.second->Call != it.first;
2738 for (
auto &[
Call,
Node] : NewCallToNode)
2739 NonAllocationCallToContextNodeMap[
Call] =
Node;
2743 for (
auto &[
Call,
Node] : TailCallToContextNodeMap)
2744 NonAllocationCallToContextNodeMap[
Call] =
Node;
2747template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
2748bool CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::partitionCallsByCallee(
2750 std::vector<std::pair<CallInfo, ContextNode *>> &NewCallToNode) {
2754 struct CallsWithSameCallee {
2755 std::vector<CallInfo> Calls;
2756 ContextNode *
Node =
nullptr;
2762 for (
auto ThisCall : AllCalls) {
2763 auto *
F = getCalleeFunc(
ThisCall.call());
2765 CalleeFuncToCallInfo[
F].Calls.push_back(ThisCall);
2774 for (
const auto &Edge :
Node->CalleeEdges) {
2775 if (!Edge->Callee->hasCall())
2777 const FuncTy *ProfiledCalleeFunc = NodeToCallingFunc[Edge->Callee];
2778 if (CalleeFuncToCallInfo.
contains(ProfiledCalleeFunc))
2779 CalleeNodeToCallInfo[Edge->Callee] =
2780 &CalleeFuncToCallInfo[ProfiledCalleeFunc];
2786 if (CalleeNodeToCallInfo.
empty())
2798 ContextNode *UnmatchedCalleesNode =
nullptr;
2800 bool UsedOrigNode =
false;
2805 auto CalleeEdges =
Node->CalleeEdges;
2806 for (
auto &Edge : CalleeEdges) {
2807 if (!Edge->Callee->hasCall())
2812 ContextNode *CallerNodeToUse =
nullptr;
2816 if (!CalleeNodeToCallInfo.
contains(Edge->Callee)) {
2817 if (!UnmatchedCalleesNode)
2818 UnmatchedCalleesNode =
2819 createNewNode(
false, NodeToCallingFunc[
Node]);
2820 CallerNodeToUse = UnmatchedCalleesNode;
2824 auto *Info = CalleeNodeToCallInfo[Edge->Callee];
2827 if (!UsedOrigNode) {
2830 Node->MatchingCalls.clear();
2831 UsedOrigNode =
true;
2834 createNewNode(
false, NodeToCallingFunc[
Node]);
2835 assert(!Info->Calls.empty());
2838 Info->Node->setCall(Info->Calls.front());
2844 NewCallToNode.push_back({Info->Node->Call, Info->Node});
2846 CallerNodeToUse = Info->Node;
2850 if (CallerNodeToUse ==
Node)
2853 moveCalleeEdgeToNewCaller(Edge, CallerNodeToUse);
2860 for (
auto &
I : CalleeNodeToCallInfo)
2861 removeNoneTypeCallerEdges(
I.second->Node);
2862 if (UnmatchedCalleesNode)
2863 removeNoneTypeCallerEdges(UnmatchedCalleesNode);
2864 removeNoneTypeCallerEdges(
Node);
2877 return Index.getStackIdAtIndex(IdOrIndex);
2880template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
2881bool CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::calleesMatch(
2882 CallTy
Call, EdgeIter &EI,
2883 MapVector<CallInfo, ContextNode *> &TailCallToContextNodeMap) {
2885 const FuncTy *ProfiledCalleeFunc = NodeToCallingFunc[
Edge->Callee];
2886 const FuncTy *CallerFunc = NodeToCallingFunc[
Edge->Caller];
2889 std::vector<std::pair<CallTy, FuncTy *>> FoundCalleeChain;
2890 if (!calleeMatchesFunc(
Call, ProfiledCalleeFunc, CallerFunc,
2895 if (FoundCalleeChain.empty())
2899 auto *CurEdge =
Callee->findEdgeFromCaller(Caller);
2903 CurEdge->ContextIds.insert_range(
Edge->ContextIds);
2904 CurEdge->AllocTypes |=
Edge->AllocTypes;
2909 auto NewEdge = std::make_shared<ContextEdge>(
2910 Callee, Caller,
Edge->AllocTypes,
Edge->ContextIds);
2911 Callee->CallerEdges.push_back(NewEdge);
2912 if (Caller ==
Edge->Caller) {
2916 EI =
Caller->CalleeEdges.insert(EI, NewEdge);
2919 "Iterator position not restored after insert and increment");
2921 Caller->CalleeEdges.push_back(NewEdge);
2926 auto *CurCalleeNode =
Edge->Callee;
2927 for (
auto &[NewCall, Func] : FoundCalleeChain) {
2928 ContextNode *NewNode =
nullptr;
2930 if (TailCallToContextNodeMap.
count(NewCall)) {
2931 NewNode = TailCallToContextNodeMap[NewCall];
2932 NewNode->AllocTypes |=
Edge->AllocTypes;
2934 FuncToCallsWithMetadata[
Func].push_back({NewCall});
2936 NewNode = createNewNode(
false, Func, NewCall);
2937 TailCallToContextNodeMap[NewCall] = NewNode;
2938 NewNode->AllocTypes =
Edge->AllocTypes;
2942 AddEdge(NewNode, CurCalleeNode);
2944 CurCalleeNode = NewNode;
2948 AddEdge(
Edge->Caller, CurCalleeNode);
2956 removeEdgeFromGraph(
Edge.get(), &EI,
true);
2968bool ModuleCallsiteContextGraph::findProfiledCalleeThroughTailCalls(
2970 std::vector<std::pair<Instruction *, Function *>> &FoundCalleeChain,
2971 bool &FoundMultipleCalleeChains) {
2978 FoundCalleeChain.push_back({Callsite,
F});
2993 bool FoundSingleCalleeChain =
false;
2994 for (
auto &BB : *CalleeFunc) {
2995 for (
auto &
I : BB) {
2997 if (!CB || !CB->isTailCall())
2999 auto *CalledValue = CB->getCalledOperand();
3000 auto *CalledFunction = CB->getCalledFunction();
3001 if (CalledValue && !CalledFunction) {
3002 CalledValue = CalledValue->stripPointerCasts();
3009 assert(!CalledFunction &&
3010 "Expected null called function in callsite for alias");
3013 if (!CalledFunction)
3015 if (CalledFunction == ProfiledCallee) {
3016 if (FoundSingleCalleeChain) {
3017 FoundMultipleCalleeChains =
true;
3020 FoundSingleCalleeChain =
true;
3021 FoundProfiledCalleeCount++;
3022 FoundProfiledCalleeDepth +=
Depth;
3023 if (
Depth > FoundProfiledCalleeMaxDepth)
3024 FoundProfiledCalleeMaxDepth =
Depth;
3025 SaveCallsiteInfo(&
I, CalleeFunc);
3026 }
else if (findProfiledCalleeThroughTailCalls(
3027 ProfiledCallee, CalledFunction,
Depth + 1,
3028 FoundCalleeChain, FoundMultipleCalleeChains)) {
3031 assert(!FoundMultipleCalleeChains);
3032 if (FoundSingleCalleeChain) {
3033 FoundMultipleCalleeChains =
true;
3036 FoundSingleCalleeChain =
true;
3037 SaveCallsiteInfo(&
I, CalleeFunc);
3038 }
else if (FoundMultipleCalleeChains)
3043 return FoundSingleCalleeChain;
3046const Function *ModuleCallsiteContextGraph::getCalleeFunc(Instruction *
Call) {
3048 if (!CB->getCalledOperand() || CB->isIndirectCall())
3050 auto *CalleeVal = CB->getCalledOperand()->stripPointerCasts();
3057bool ModuleCallsiteContextGraph::calleeMatchesFunc(
3059 std::vector<std::pair<Instruction *, Function *>> &FoundCalleeChain) {
3061 if (!CB->getCalledOperand() || CB->isIndirectCall())
3063 auto *CalleeVal = CB->getCalledOperand()->stripPointerCasts();
3065 if (CalleeFunc == Func)
3068 if (Alias && Alias->getAliasee() == Func)
3079 bool FoundMultipleCalleeChains =
false;
3080 if (!findProfiledCalleeThroughTailCalls(Func, CalleeVal,
Depth,
3082 FoundMultipleCalleeChains)) {
3083 LLVM_DEBUG(
dbgs() <<
"Not found through unique tail call chain: "
3084 <<
Func->getName() <<
" from " << CallerFunc->
getName()
3085 <<
" that actually called " << CalleeVal->getName()
3086 << (FoundMultipleCalleeChains
3087 ?
" (found multiple possible chains)"
3090 if (FoundMultipleCalleeChains)
3091 FoundProfiledCalleeNonUniquelyCount++;
3098bool ModuleCallsiteContextGraph::sameCallee(Instruction *Call1,
3099 Instruction *Call2) {
3101 if (!CB1->getCalledOperand() || CB1->isIndirectCall())
3103 auto *CalleeVal1 = CB1->getCalledOperand()->stripPointerCasts();
3106 if (!CB2->getCalledOperand() || CB2->isIndirectCall())
3108 auto *CalleeVal2 = CB2->getCalledOperand()->stripPointerCasts();
3110 return CalleeFunc1 == CalleeFunc2;
3113bool IndexCallsiteContextGraph::findProfiledCalleeThroughTailCalls(
3114 ValueInfo ProfiledCallee, ValueInfo CurCallee,
unsigned Depth,
3115 std::vector<std::pair<IndexCall, FunctionSummary *>> &FoundCalleeChain,
3116 bool &FoundMultipleCalleeChains) {
3122 auto CreateAndSaveCallsiteInfo = [&](ValueInfo
Callee, FunctionSummary *
FS) {
3125 if (!FunctionCalleesToSynthesizedCallsiteInfos.count(FS) ||
3126 !FunctionCalleesToSynthesizedCallsiteInfos[FS].count(Callee))
3129 FunctionCalleesToSynthesizedCallsiteInfos[
FS][
Callee] =
3130 std::make_unique<CallsiteInfo>(Callee, SmallVector<unsigned>());
3131 CallsiteInfo *NewCallsiteInfo =
3132 FunctionCalleesToSynthesizedCallsiteInfos[
FS][
Callee].get();
3133 FoundCalleeChain.push_back({NewCallsiteInfo,
FS});
3140 bool FoundSingleCalleeChain =
false;
3143 !isPrevailing(CurCallee.
getGUID(), S.get()))
3148 auto FSVI = CurCallee;
3151 FSVI = AS->getAliaseeVI();
3152 for (
auto &CallEdge :
FS->calls()) {
3153 if (!CallEdge.second.hasTailCall())
3155 if (CallEdge.first == ProfiledCallee) {
3156 if (FoundSingleCalleeChain) {
3157 FoundMultipleCalleeChains =
true;
3160 FoundSingleCalleeChain =
true;
3161 FoundProfiledCalleeCount++;
3162 FoundProfiledCalleeDepth +=
Depth;
3163 if (
Depth > FoundProfiledCalleeMaxDepth)
3164 FoundProfiledCalleeMaxDepth =
Depth;
3165 CreateAndSaveCallsiteInfo(CallEdge.first, FS);
3167 assert(!FSToVIMap.count(FS) || FSToVIMap[FS] == FSVI);
3168 FSToVIMap[
FS] = FSVI;
3169 }
else if (findProfiledCalleeThroughTailCalls(
3170 ProfiledCallee, CallEdge.first,
Depth + 1,
3171 FoundCalleeChain, FoundMultipleCalleeChains)) {
3174 assert(!FoundMultipleCalleeChains);
3175 if (FoundSingleCalleeChain) {
3176 FoundMultipleCalleeChains =
true;
3179 FoundSingleCalleeChain =
true;
3180 CreateAndSaveCallsiteInfo(CallEdge.first, FS);
3182 assert(!FSToVIMap.count(FS) || FSToVIMap[FS] == FSVI);
3183 FSToVIMap[
FS] = FSVI;
3184 }
else if (FoundMultipleCalleeChains)
3189 return FoundSingleCalleeChain;
3192const FunctionSummary *
3193IndexCallsiteContextGraph::getCalleeFunc(IndexCall &
Call) {
3195 if (
Callee.getSummaryList().empty())
3200bool IndexCallsiteContextGraph::calleeMatchesFunc(
3201 IndexCall &
Call,
const FunctionSummary *Func,
3202 const FunctionSummary *CallerFunc,
3203 std::vector<std::pair<IndexCall, FunctionSummary *>> &FoundCalleeChain) {
3207 AliasSummary *Alias =
3208 Callee.getSummaryList().empty()
3211 assert(FSToVIMap.count(Func));
3212 auto FuncVI = FSToVIMap[
Func];
3213 if (Callee == FuncVI ||
3228 bool FoundMultipleCalleeChains =
false;
3229 if (!findProfiledCalleeThroughTailCalls(
3230 FuncVI, Callee,
Depth, FoundCalleeChain, FoundMultipleCalleeChains)) {
3231 LLVM_DEBUG(
dbgs() <<
"Not found through unique tail call chain: " << FuncVI
3232 <<
" from " << FSToVIMap[CallerFunc]
3233 <<
" that actually called " << Callee
3234 << (FoundMultipleCalleeChains
3235 ?
" (found multiple possible chains)"
3238 if (FoundMultipleCalleeChains)
3239 FoundProfiledCalleeNonUniquelyCount++;
3246bool IndexCallsiteContextGraph::sameCallee(IndexCall &Call1, IndexCall &Call2) {
3249 return Callee1 == Callee2;
3252template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3253void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode::dump()
3259template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3260void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode::print(
3261 raw_ostream &OS)
const {
3262 OS <<
"Node " <<
this <<
"\n";
3266 OS <<
" (recursive)";
3268 if (!MatchingCalls.empty()) {
3269 OS <<
"\tMatchingCalls:\n";
3270 for (
auto &MatchingCall : MatchingCalls) {
3272 MatchingCall.print(OS);
3276 OS <<
"\tNodeId: " <<
NodeId <<
"\n";
3278 OS <<
"\tContextIds:";
3280 auto ContextIds = getContextIds();
3281 std::vector<uint32_t> SortedIds(ContextIds.begin(), ContextIds.end());
3282 std::sort(SortedIds.begin(), SortedIds.end());
3283 for (
auto Id : SortedIds)
3286 OS <<
"\tCalleeEdges:\n";
3287 for (
auto &
Edge : CalleeEdges)
3288 OS <<
"\t\t" << *
Edge <<
" (Callee NodeId: " <<
Edge->Callee->NodeId
3290 OS <<
"\tCallerEdges:\n";
3291 for (
auto &
Edge : CallerEdges)
3292 OS <<
"\t\t" << *
Edge <<
" (Caller NodeId: " <<
Edge->Caller->NodeId
3294 if (!Clones.empty()) {
3297 for (
auto *
C : Clones)
3298 OS <<
LS <<
C <<
" NodeId: " <<
C->NodeId;
3300 }
else if (CloneOf) {
3301 OS <<
"\tClone of " << CloneOf <<
" NodeId: " << CloneOf->NodeId <<
"\n";
3305template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3306void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextEdge::dump()
3312template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3313void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextEdge::print(
3314 raw_ostream &OS)
const {
3315 OS <<
"Edge from Callee " <<
Callee <<
" to Caller: " <<
Caller
3316 << (IsBackedge ?
" (BE)" :
"")
3318 OS <<
" ContextIds:";
3319 std::vector<uint32_t> SortedIds(ContextIds.begin(), ContextIds.end());
3320 std::sort(SortedIds.begin(), SortedIds.end());
3321 for (
auto Id : SortedIds)
3325template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3326void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::dump()
const {
3330template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3331void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::print(
3332 raw_ostream &OS)
const {
3333 OS <<
"Callsite Context Graph:\n";
3334 using GraphType =
const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *;
3336 if (
Node->isRemoved())
3343template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3344void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::printTotalSizes(
3346 function_ref<
void(StringRef, StringRef,
const Twine &)> EmitRemark)
const {
3347 using GraphType =
const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *;
3349 if (
Node->isRemoved())
3351 if (!
Node->IsAllocation)
3353 DenseSet<uint32_t> ContextIds =
Node->getContextIds();
3354 auto AllocTypeFromCall = getAllocationCallType(
Node->Call);
3355 std::vector<uint32_t> SortedIds(ContextIds.
begin(), ContextIds.
end());
3356 std::sort(SortedIds.begin(), SortedIds.end());
3357 for (
auto Id : SortedIds) {
3358 auto TypeI = ContextIdToAllocationType.find(Id);
3359 assert(TypeI != ContextIdToAllocationType.end());
3360 auto CSI = ContextIdToContextSizeInfos.find(Id);
3361 if (CSI != ContextIdToContextSizeInfos.end()) {
3362 for (
auto &Info : CSI->second) {
3365 " full allocation context " + std::to_string(
Info.FullStackId) +
3366 " with total size " + std::to_string(
Info.TotalSize) +
" is " +
3368 if (allocTypeToUse(
Node->AllocTypes) != AllocTypeFromCall)
3370 " due to cold byte percent";
3372 Msg +=
" (internal context id " + std::to_string(Id) +
")";
3384 if (allocTypeToUse(
Node->AllocTypes) != AllocTypeFromCall)
3386 " due to cold byte percent";
3388 Msg +=
" (internal context id " + std::to_string(Id) +
")";
3398template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3399void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::check()
const {
3400 using GraphType =
const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *;
3403 for (
auto &
Edge :
Node->CallerEdges)
3408template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3410 using GraphType =
const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *;
3411 using NodeRef =
const ContextNode<DerivedCCG, FuncTy, CallTy> *;
3413 using NodePtrTy = std::unique_ptr<ContextNode<DerivedCCG, FuncTy, CallTy>>;
3429 return G->NodeOwner.begin()->get();
3432 using EdgePtrTy = std::shared_ptr<ContextEdge<DerivedCCG, FuncTy, CallTy>>;
3433 static const ContextNode<DerivedCCG, FuncTy, CallTy> *
3452template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3466 using GraphType =
const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *;
3472 std::string LabelString =
3473 (
Twine(
"OrigId: ") + (
Node->IsAllocation ?
"Alloc" :
"") +
3476 LabelString +=
"\n";
3477 if (
Node->hasCall()) {
3478 auto Func =
G->NodeToCallingFunc.find(
Node);
3479 assert(Func !=
G->NodeToCallingFunc.end());
3481 G->getLabel(Func->second,
Node->Call.call(),
Node->Call.cloneNo());
3482 for (
auto &MatchingCall :
Node->MatchingCalls) {
3483 LabelString +=
"\n";
3484 LabelString +=
G->getLabel(Func->second, MatchingCall.call(),
3485 MatchingCall.cloneNo());
3488 LabelString +=
"null call";
3489 if (
Node->Recursive)
3490 LabelString +=
" (recursive)";
3492 LabelString +=
" (external)";
3498 auto ContextIds =
Node->getContextIds();
3502 bool Highlight =
false;
3511 std::string AttributeString = (
Twine(
"tooltip=\"") + getNodeId(
Node) +
" " +
3512 getContextIds(ContextIds) +
"\"")
3516 AttributeString +=
",fontsize=\"30\"";
3518 (
Twine(
",fillcolor=\"") + getColor(
Node->AllocTypes, Highlight) +
"\"")
3520 if (
Node->CloneOf) {
3521 AttributeString +=
",color=\"blue\"";
3522 AttributeString +=
",style=\"filled,bold,dashed\"";
3524 AttributeString +=
",style=\"filled\"";
3525 return AttributeString;
3530 auto &Edge = *(ChildIter.getCurrent());
3535 bool Highlight =
false;
3544 auto Color = getColor(Edge->AllocTypes, Highlight);
3545 std::string AttributeString =
3546 (
Twine(
"tooltip=\"") + getContextIds(Edge->ContextIds) +
"\"" +
3548 Twine(
",fillcolor=\"") + Color +
"\"" +
Twine(
",color=\"") + Color +
3551 if (Edge->IsBackedge)
3552 AttributeString +=
",style=\"dotted\"";
3555 AttributeString +=
",penwidth=\"2.0\",weight=\"2\"";
3556 return AttributeString;
3562 if (
Node->isRemoved())
3575 std::string IdString =
"ContextIds:";
3576 if (ContextIds.
size() < 100) {
3577 std::vector<uint32_t> SortedIds(ContextIds.
begin(), ContextIds.
end());
3578 std::sort(SortedIds.begin(), SortedIds.end());
3579 for (
auto Id : SortedIds)
3580 IdString += (
" " +
Twine(Id)).str();
3582 IdString += (
" (" + Twine(ContextIds.
size()) +
" ids)").str();
3587 static std::string getColor(uint8_t AllocTypes,
bool Highlight) {
3593 if (AllocTypes == (uint8_t)AllocationType::NotCold)
3595 return !
DoHighlight || Highlight ?
"brown1" :
"lightpink";
3596 if (AllocTypes == (uint8_t)AllocationType::Cold)
3597 return !
DoHighlight || Highlight ?
"cyan" :
"lightskyblue";
3599 ((uint8_t)AllocationType::NotCold | (uint8_t)AllocationType::Cold))
3600 return Highlight ?
"magenta" :
"mediumorchid1";
3604 static std::string getNodeId(NodeRef Node) {
3605 std::stringstream SStream;
3606 SStream << std::hex <<
"N0x" << (
unsigned long long)Node;
3607 std::string
Result = SStream.str();
3616template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3621template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3622void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::exportToDot(
3623 std::string Label)
const {
3628template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3629typename CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode *
3630CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::moveEdgeToNewCalleeClone(
3631 const std::shared_ptr<ContextEdge> &
Edge,
3632 DenseSet<uint32_t> ContextIdsToMove) {
3634 assert(NodeToCallingFunc.count(Node));
3635 ContextNode *Clone =
3636 createNewNode(
Node->IsAllocation, NodeToCallingFunc[Node],
Node->Call);
3637 Node->addClone(Clone);
3638 Clone->MatchingCalls =
Node->MatchingCalls;
3639 moveEdgeToExistingCalleeClone(
Edge, Clone,
true,
3644template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3645void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::
3646 moveEdgeToExistingCalleeClone(
const std::shared_ptr<ContextEdge> &
Edge,
3647 ContextNode *NewCallee,
bool NewClone,
3648 DenseSet<uint32_t> ContextIdsToMove) {
3651 assert(NewCallee->getOrigNode() ==
Edge->Callee->getOrigNode());
3653 bool EdgeIsRecursive =
Edge->Callee ==
Edge->Caller;
3655 ContextNode *OldCallee =
Edge->Callee;
3659 auto ExistingEdgeToNewCallee = NewCallee->findEdgeFromCaller(
Edge->Caller);
3663 if (ContextIdsToMove.
empty())
3664 ContextIdsToMove =
Edge->getContextIds();
3668 if (
Edge->getContextIds().size() == ContextIdsToMove.
size()) {
3671 NewCallee->AllocTypes |=
Edge->AllocTypes;
3673 if (ExistingEdgeToNewCallee) {
3676 ExistingEdgeToNewCallee->getContextIds().insert_range(ContextIdsToMove);
3677 ExistingEdgeToNewCallee->AllocTypes |=
Edge->AllocTypes;
3678 assert(
Edge->ContextIds == ContextIdsToMove);
3679 removeEdgeFromGraph(
Edge.get());
3682 Edge->Callee = NewCallee;
3683 NewCallee->CallerEdges.push_back(
Edge);
3685 OldCallee->eraseCallerEdge(
Edge.get());
3692 auto CallerEdgeAllocType = computeAllocType(ContextIdsToMove);
3693 if (ExistingEdgeToNewCallee) {
3696 ExistingEdgeToNewCallee->getContextIds().insert_range(ContextIdsToMove);
3697 ExistingEdgeToNewCallee->AllocTypes |= CallerEdgeAllocType;
3700 auto NewEdge = std::make_shared<ContextEdge>(
3701 NewCallee,
Edge->Caller, CallerEdgeAllocType, ContextIdsToMove);
3702 Edge->Caller->CalleeEdges.push_back(NewEdge);
3703 NewCallee->CallerEdges.push_back(NewEdge);
3707 NewCallee->AllocTypes |= CallerEdgeAllocType;
3709 Edge->AllocTypes = computeAllocType(
Edge->ContextIds);
3714 for (
auto &OldCalleeEdge : OldCallee->CalleeEdges) {
3715 ContextNode *CalleeToUse = OldCalleeEdge->Callee;
3719 if (CalleeToUse == OldCallee) {
3723 if (EdgeIsRecursive) {
3727 CalleeToUse = NewCallee;
3731 DenseSet<uint32_t> EdgeContextIdsToMove =
3733 set_subtract(OldCalleeEdge->getContextIds(), EdgeContextIdsToMove);
3734 OldCalleeEdge->AllocTypes =
3735 computeAllocType(OldCalleeEdge->getContextIds());
3742 if (
auto *NewCalleeEdge = NewCallee->findEdgeFromCallee(CalleeToUse)) {
3743 NewCalleeEdge->getContextIds().insert_range(EdgeContextIdsToMove);
3744 NewCalleeEdge->AllocTypes |= computeAllocType(EdgeContextIdsToMove);
3748 auto NewEdge = std::make_shared<ContextEdge>(
3749 CalleeToUse, NewCallee, computeAllocType(EdgeContextIdsToMove),
3750 EdgeContextIdsToMove);
3751 NewCallee->CalleeEdges.push_back(NewEdge);
3752 NewEdge->Callee->CallerEdges.push_back(NewEdge);
3756 OldCallee->AllocTypes = OldCallee->computeAllocType();
3758 assert((OldCallee->AllocTypes == (uint8_t)AllocationType::None) ==
3759 OldCallee->emptyContextIds());
3763 for (
const auto &OldCalleeEdge : OldCallee->CalleeEdges)
3766 for (
const auto &NewCalleeEdge : NewCallee->CalleeEdges)
3772template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3773void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::
3774 moveCalleeEdgeToNewCaller(
const std::shared_ptr<ContextEdge> &
Edge,
3775 ContextNode *NewCaller) {
3776 auto *OldCallee =
Edge->Callee;
3777 auto *NewCallee = OldCallee;
3780 bool Recursive =
Edge->Caller ==
Edge->Callee;
3782 NewCallee = NewCaller;
3784 ContextNode *OldCaller =
Edge->Caller;
3785 OldCaller->eraseCalleeEdge(
Edge.get());
3789 auto ExistingEdgeToNewCaller = NewCaller->findEdgeFromCallee(NewCallee);
3791 if (ExistingEdgeToNewCaller) {
3794 ExistingEdgeToNewCaller->getContextIds().insert_range(
3795 Edge->getContextIds());
3796 ExistingEdgeToNewCaller->AllocTypes |=
Edge->AllocTypes;
3797 Edge->ContextIds.clear();
3798 Edge->AllocTypes = (uint8_t)AllocationType::None;
3799 OldCallee->eraseCallerEdge(
Edge.get());
3802 Edge->Caller = NewCaller;
3803 NewCaller->CalleeEdges.push_back(
Edge);
3805 assert(NewCallee == NewCaller);
3808 Edge->Callee = NewCallee;
3809 NewCallee->CallerEdges.push_back(
Edge);
3810 OldCallee->eraseCallerEdge(
Edge.get());
3816 NewCaller->AllocTypes |=
Edge->AllocTypes;
3823 bool IsNewNode = NewCaller->CallerEdges.empty();
3832 for (
auto &OldCallerEdge : OldCaller->CallerEdges) {
3833 auto OldCallerCaller = OldCallerEdge->Caller;
3837 OldCallerEdge->getContextIds(),
Edge->getContextIds());
3838 if (OldCaller == OldCallerCaller) {
3839 OldCallerCaller = NewCaller;
3845 set_subtract(OldCallerEdge->getContextIds(), EdgeContextIdsToMove);
3846 OldCallerEdge->AllocTypes =
3847 computeAllocType(OldCallerEdge->getContextIds());
3852 auto *ExistingCallerEdge = NewCaller->findEdgeFromCaller(OldCallerCaller);
3856 if (ExistingCallerEdge) {
3857 ExistingCallerEdge->getContextIds().insert_range(EdgeContextIdsToMove);
3858 ExistingCallerEdge->AllocTypes |=
3859 computeAllocType(EdgeContextIdsToMove);
3862 auto NewEdge = std::make_shared<ContextEdge>(
3863 NewCaller, OldCallerCaller, computeAllocType(EdgeContextIdsToMove),
3864 EdgeContextIdsToMove);
3865 NewCaller->CallerEdges.push_back(NewEdge);
3866 NewEdge->Caller->CalleeEdges.push_back(NewEdge);
3871 OldCaller->AllocTypes = OldCaller->computeAllocType();
3873 assert((OldCaller->AllocTypes == (uint8_t)AllocationType::None) ==
3874 OldCaller->emptyContextIds());
3878 for (
const auto &OldCallerEdge : OldCaller->CallerEdges)
3881 for (
const auto &NewCallerEdge : NewCaller->CallerEdges)
3887template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3888void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::
3889 recursivelyRemoveNoneTypeCalleeEdges(
3890 ContextNode *Node, DenseSet<const ContextNode *> &Visited) {
3895 removeNoneTypeCalleeEdges(Node);
3897 for (
auto *Clone :
Node->Clones)
3898 recursivelyRemoveNoneTypeCalleeEdges(Clone, Visited);
3902 auto CallerEdges =
Node->CallerEdges;
3903 for (
auto &
Edge : CallerEdges) {
3905 if (
Edge->isRemoved()) {
3909 recursivelyRemoveNoneTypeCalleeEdges(
Edge->Caller, Visited);
3914template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3915void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::markBackedges() {
3920 DenseSet<const ContextNode *> Visited;
3921 DenseSet<const ContextNode *> CurrentStack;
3922 for (
auto &Entry : NonAllocationCallToContextNodeMap) {
3924 if (
Node->isRemoved())
3927 if (!
Node->CallerEdges.empty())
3929 markBackedges(Node, Visited, CurrentStack);
3935template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3936void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::markBackedges(
3937 ContextNode *Node, DenseSet<const ContextNode *> &Visited,
3938 DenseSet<const ContextNode *> &CurrentStack) {
3939 auto I = Visited.
insert(Node);
3943 for (
auto &CalleeEdge :
Node->CalleeEdges) {
3944 auto *
Callee = CalleeEdge->Callee;
3945 if (Visited.
count(Callee)) {
3948 if (CurrentStack.
count(Callee))
3949 CalleeEdge->IsBackedge =
true;
3952 CurrentStack.
insert(Callee);
3953 markBackedges(Callee, Visited, CurrentStack);
3954 CurrentStack.
erase(Callee);
3958template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3959void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::identifyClones() {
3960 DenseSet<const ContextNode *> Visited;
3961 for (
auto &Entry : AllocationCallToContextNodeMap) {
3963 identifyClones(
Entry.second, Visited,
Entry.second->getContextIds());
3966 for (
auto &Entry : AllocationCallToContextNodeMap)
3967 recursivelyRemoveNoneTypeCalleeEdges(
Entry.second, Visited);
3980template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3981void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::identifyClones(
3982 ContextNode *Node, DenseSet<const ContextNode *> &Visited,
3983 const DenseSet<uint32_t> &AllocContextIds) {
3993 if (!
Node->hasCall())
4012 auto CallerEdges =
Node->CallerEdges;
4013 for (
auto &
Edge : CallerEdges) {
4015 if (
Edge->isRemoved()) {
4021 if (
Edge->IsBackedge) {
4028 if (!Visited.
count(
Edge->Caller) && !
Edge->Caller->CloneOf) {
4029 identifyClones(
Edge->Caller, Visited, AllocContextIds);
4052 const unsigned AllocTypeCloningPriority[] = { 3, 4,
4056 [&](
const std::shared_ptr<ContextEdge> &
A,
4057 const std::shared_ptr<ContextEdge> &
B) {
4060 if (A->ContextIds.empty())
4066 if (B->ContextIds.empty())
4069 if (A->AllocTypes == B->AllocTypes)
4072 return *A->ContextIds.begin() < *B->ContextIds.begin();
4073 return AllocTypeCloningPriority[A->AllocTypes] <
4074 AllocTypeCloningPriority[B->AllocTypes];
4077 assert(
Node->AllocTypes != (uint8_t)AllocationType::None);
4079 DenseSet<uint32_t> RecursiveContextIds;
4084 DenseSet<uint32_t> AllCallerContextIds;
4085 for (
auto &CE :
Node->CallerEdges) {
4088 AllCallerContextIds.
reserve(
CE->getContextIds().size());
4089 for (
auto Id :
CE->getContextIds())
4090 if (!AllCallerContextIds.
insert(Id).second)
4091 RecursiveContextIds.
insert(Id);
4101 auto CallerEdges =
Node->CallerEdges;
4102 for (
auto &CallerEdge : CallerEdges) {
4104 if (CallerEdge->isRemoved()) {
4108 assert(CallerEdge->Callee == Node);
4117 if (!CallerEdge->Caller->hasCall())
4122 auto CallerEdgeContextsForAlloc =
4124 if (!RecursiveContextIds.
empty())
4125 CallerEdgeContextsForAlloc =
4127 if (CallerEdgeContextsForAlloc.empty())
4130 auto CallerAllocTypeForAlloc = computeAllocType(CallerEdgeContextsForAlloc);
4134 std::vector<uint8_t> CalleeEdgeAllocTypesForCallerEdge;
4135 CalleeEdgeAllocTypesForCallerEdge.reserve(
Node->CalleeEdges.size());
4136 for (
auto &CalleeEdge :
Node->CalleeEdges)
4137 CalleeEdgeAllocTypesForCallerEdge.push_back(intersectAllocTypes(
4138 CalleeEdge->getContextIds(), CallerEdgeContextsForAlloc));
4154 assert(CallerEdge->AllocTypes != (uint8_t)AllocationType::None);
4155 assert(
Node->AllocTypes != (uint8_t)AllocationType::None);
4156 if (!CallerEdge->IsBackedge &&
4157 allocTypeToUse(CallerAllocTypeForAlloc) ==
4158 allocTypeToUse(
Node->AllocTypes) &&
4159 allocTypesMatch<DerivedCCG, FuncTy, CallTy>(
4160 CalleeEdgeAllocTypesForCallerEdge,
Node->CalleeEdges)) {
4164 if (CallerEdge->IsBackedge) {
4168 DeferredBackedges++;
4181 if (CallerEdge->IsBackedge && !CallerEdge->Caller->CloneOf &&
4182 !Visited.
count(CallerEdge->Caller)) {
4183 const auto OrigIdCount = CallerEdge->getContextIds().size();
4186 identifyClones(CallerEdge->Caller, Visited, CallerEdgeContextsForAlloc);
4187 removeNoneTypeCalleeEdges(CallerEdge->Caller);
4191 bool UpdatedEdge =
false;
4192 if (OrigIdCount > CallerEdge->getContextIds().size()) {
4193 for (
auto E :
Node->CallerEdges) {
4195 if (
E->Caller->CloneOf != CallerEdge->Caller)
4199 auto CallerEdgeContextsForAllocNew =
4201 if (CallerEdgeContextsForAllocNew.empty())
4211 CallerEdgeContextsForAlloc.swap(CallerEdgeContextsForAllocNew);
4221 if (CallerEdge->isRemoved())
4231 CallerEdgeContextsForAlloc, CallerEdge->getContextIds());
4232 if (CallerEdgeContextsForAlloc.empty())
4237 CallerAllocTypeForAlloc = computeAllocType(CallerEdgeContextsForAlloc);
4238 CalleeEdgeAllocTypesForCallerEdge.clear();
4239 for (
auto &CalleeEdge :
Node->CalleeEdges) {
4240 CalleeEdgeAllocTypesForCallerEdge.push_back(intersectAllocTypes(
4241 CalleeEdge->getContextIds(), CallerEdgeContextsForAlloc));
4247 ContextNode *Clone =
nullptr;
4248 for (
auto *CurClone :
Node->Clones) {
4249 if (allocTypeToUse(CurClone->AllocTypes) !=
4250 allocTypeToUse(CallerAllocTypeForAlloc))
4257 assert(!BothSingleAlloc ||
4258 CurClone->AllocTypes == CallerAllocTypeForAlloc);
4264 if (BothSingleAlloc || allocTypesMatchClone<DerivedCCG, FuncTy, CallTy>(
4265 CalleeEdgeAllocTypesForCallerEdge, CurClone)) {
4273 moveEdgeToExistingCalleeClone(CallerEdge, Clone,
false,
4274 CallerEdgeContextsForAlloc);
4276 Clone = moveEdgeToNewCalleeClone(CallerEdge, CallerEdgeContextsForAlloc);
4279 assert(Clone->AllocTypes != (uint8_t)AllocationType::None);
4286 assert(
Node->AllocTypes != (uint8_t)AllocationType::None);
4292void ModuleCallsiteContextGraph::updateAllocationCall(
4297 "memprof", AllocTypeString);
4300 .emit(OptimizationRemark(
DEBUG_TYPE,
"MemprofAttribute",
Call.call())
4301 <<
ore::NV(
"AllocationCall",
Call.call()) <<
" in clone "
4303 <<
" marked with memprof allocation attribute "
4304 <<
ore::NV(
"Attribute", AllocTypeString));
4307void IndexCallsiteContextGraph::updateAllocationCall(CallInfo &
Call,
4311 assert(AI->Versions.size() >
Call.cloneNo());
4316ModuleCallsiteContextGraph::getAllocationCallType(
const CallInfo &
Call)
const {
4318 if (!CB->getAttributes().hasFnAttr(
"memprof"))
4319 return AllocationType::None;
4320 return CB->getAttributes().getFnAttr(
"memprof").getValueAsString() ==
"cold"
4321 ? AllocationType::Cold
4322 : AllocationType::NotCold;
4326IndexCallsiteContextGraph::getAllocationCallType(
const CallInfo &
Call)
const {
4328 assert(AI->Versions.size() >
Call.cloneNo());
4332void ModuleCallsiteContextGraph::updateCall(CallInfo &CallerCall,
4333 FuncInfo CalleeFunc) {
4334 auto *CurF = getCalleeFunc(CallerCall.call());
4335 auto NewCalleeCloneNo = CalleeFunc.cloneNo();
4342 if (CurCalleeCloneNo != NewCalleeCloneNo) {
4344 << CurCalleeCloneNo <<
" now " << NewCalleeCloneNo
4346 MismatchedCloneAssignments++;
4349 if (NewCalleeCloneNo > 0)
4350 cast<CallBase>(CallerCall.call())->setCalledFunction(CalleeFunc.func());
4351 OREGetter(CallerCall.call()->getFunction())
4352 .emit(OptimizationRemark(
DEBUG_TYPE,
"MemprofCall", CallerCall.call())
4353 <<
ore::NV(
"Call", CallerCall.call()) <<
" in clone "
4354 <<
ore::NV(
"Caller", CallerCall.call()->getFunction())
4355 <<
" assigned to call function clone "
4356 <<
ore::NV(
"Callee", CalleeFunc.func()));
4359void IndexCallsiteContextGraph::updateCall(CallInfo &CallerCall,
4360 FuncInfo CalleeFunc) {
4363 "Caller cannot be an allocation which should not have profiled calls");
4364 assert(CI->Clones.size() > CallerCall.cloneNo());
4365 auto NewCalleeCloneNo = CalleeFunc.cloneNo();
4366 auto &CurCalleeCloneNo = CI->Clones[CallerCall.cloneNo()];
4371 if (CurCalleeCloneNo != 0 && CurCalleeCloneNo != NewCalleeCloneNo) {
4373 << CurCalleeCloneNo <<
" now " << NewCalleeCloneNo
4375 MismatchedCloneAssignments++;
4377 CurCalleeCloneNo = NewCalleeCloneNo;
4389 SP->replaceLinkageName(MDName);
4393 TempDISubprogram NewDecl = Decl->
clone();
4394 NewDecl->replaceLinkageName(MDName);
4398CallsiteContextGraph<ModuleCallsiteContextGraph,
Function,
4400ModuleCallsiteContextGraph::cloneFunctionForCallsite(
4401 FuncInfo &Func, CallInfo &
Call, DenseMap<CallInfo, CallInfo> &CallMap,
4402 std::vector<CallInfo> &CallsWithMetadataInFunc,
unsigned CloneNo) {
4407 assert(!
Func.func()->getParent()->getFunction(Name));
4408 NewFunc->setName(Name);
4410 for (
auto &Inst : CallsWithMetadataInFunc) {
4412 assert(Inst.cloneNo() == 0);
4415 OREGetter(
Func.func())
4416 .emit(OptimizationRemark(
DEBUG_TYPE,
"MemprofClone",
Func.func())
4417 <<
"created clone " <<
ore::NV(
"NewFunction", NewFunc));
4418 return {NewFunc, CloneNo};
4421CallsiteContextGraph<IndexCallsiteContextGraph, FunctionSummary,
4422 IndexCall>::FuncInfo
4423IndexCallsiteContextGraph::cloneFunctionForCallsite(
4424 FuncInfo &Func, CallInfo &
Call, DenseMap<CallInfo, CallInfo> &CallMap,
4425 std::vector<CallInfo> &CallsWithMetadataInFunc,
unsigned CloneNo) {
4439 for (
auto &Inst : CallsWithMetadataInFunc) {
4441 assert(Inst.cloneNo() == 0);
4443 assert(AI->Versions.size() == CloneNo);
4446 AI->Versions.push_back(0);
4449 assert(CI && CI->Clones.size() == CloneNo);
4452 CI->Clones.push_back(0);
4454 CallMap[Inst] = {Inst.call(), CloneNo};
4456 return {
Func.func(), CloneNo};
4473template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
4474void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::mergeClones() {
4480 DenseMap<uint32_t, ContextNode *> ContextIdToAllocationNode;
4481 for (
auto &Entry : AllocationCallToContextNodeMap) {
4483 for (
auto Id :
Node->getContextIds())
4484 ContextIdToAllocationNode[
Id] =
Node->getOrigNode();
4485 for (
auto *Clone :
Node->Clones) {
4486 for (
auto Id : Clone->getContextIds())
4487 ContextIdToAllocationNode[
Id] = Clone->getOrigNode();
4494 DenseSet<const ContextNode *> Visited;
4495 for (
auto &Entry : AllocationCallToContextNodeMap) {
4498 mergeClones(Node, Visited, ContextIdToAllocationNode);
4504 auto Clones =
Node->Clones;
4505 for (
auto *Clone : Clones)
4506 mergeClones(Clone, Visited, ContextIdToAllocationNode);
4510 dbgs() <<
"CCG after merging:\n";
4514 exportToDot(
"aftermerge");
4522template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
4523void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::mergeClones(
4524 ContextNode *Node, DenseSet<const ContextNode *> &Visited,
4525 DenseMap<uint32_t, ContextNode *> &ContextIdToAllocationNode) {
4535 bool FoundUnvisited =
true;
4537 while (FoundUnvisited) {
4539 FoundUnvisited =
false;
4542 auto CallerEdges =
Node->CallerEdges;
4543 for (
auto CallerEdge : CallerEdges) {
4545 if (CallerEdge->Callee != Node)
4550 FoundUnvisited =
true;
4551 mergeClones(CallerEdge->Caller, Visited, ContextIdToAllocationNode);
4555 TotalMergeInvokes++;
4556 TotalMergeIters += Iters;
4557 if (Iters > MaxMergeIters)
4558 MaxMergeIters = Iters;
4561 mergeNodeCalleeClones(Node, Visited, ContextIdToAllocationNode);
4564template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
4565void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::mergeNodeCalleeClones(
4566 ContextNode *Node, DenseSet<const ContextNode *> &Visited,
4567 DenseMap<uint32_t, ContextNode *> &ContextIdToAllocationNode) {
4569 if (
Node->emptyContextIds())
4574 MapVector<ContextNode *, std::vector<std::shared_ptr<ContextEdge>>>
4575 OrigNodeToCloneEdges;
4576 for (
const auto &
E :
Node->CalleeEdges) {
4581 OrigNodeToCloneEdges[
Base].push_back(
E);
4587 auto CalleeCallerEdgeLessThan = [](
const std::shared_ptr<ContextEdge> &
A,
4588 const std::shared_ptr<ContextEdge> &
B) {
4589 if (
A->Callee->CallerEdges.size() !=
B->Callee->CallerEdges.size())
4590 return A->Callee->CallerEdges.size() <
B->Callee->CallerEdges.size();
4591 if (
A->Callee->CloneOf && !
B->Callee->CloneOf)
4593 else if (!
A->Callee->CloneOf &&
B->Callee->CloneOf)
4597 return *
A->ContextIds.begin() < *
B->ContextIds.begin();
4602 for (
auto Entry : OrigNodeToCloneEdges) {
4605 auto &CalleeEdges =
Entry.second;
4606 auto NumCalleeClones = CalleeEdges.size();
4608 if (NumCalleeClones == 1)
4619 DenseSet<ContextNode *> OtherCallersToShareMerge;
4620 findOtherCallersToShareMerge(Node, CalleeEdges, ContextIdToAllocationNode,
4621 OtherCallersToShareMerge);
4626 ContextNode *MergeNode =
nullptr;
4627 DenseMap<ContextNode *, unsigned> CallerToMoveCount;
4628 for (
auto CalleeEdge : CalleeEdges) {
4629 auto *OrigCallee = CalleeEdge->Callee;
4635 if (CalleeEdge->Callee->CallerEdges.size() == 1) {
4636 MergeNode = OrigCallee;
4637 NonNewMergedNodes++;
4644 if (!OtherCallersToShareMerge.
empty()) {
4645 bool MoveAllCallerEdges =
true;
4646 for (
auto CalleeCallerE : OrigCallee->CallerEdges) {
4647 if (CalleeCallerE == CalleeEdge)
4649 if (!OtherCallersToShareMerge.
contains(CalleeCallerE->Caller)) {
4650 MoveAllCallerEdges =
false;
4656 if (MoveAllCallerEdges) {
4657 MergeNode = OrigCallee;
4658 NonNewMergedNodes++;
4665 assert(MergeNode != OrigCallee);
4666 moveEdgeToExistingCalleeClone(CalleeEdge, MergeNode,
4669 MergeNode = moveEdgeToNewCalleeClone(CalleeEdge);
4674 if (!OtherCallersToShareMerge.
empty()) {
4678 auto OrigCalleeCallerEdges = OrigCallee->CallerEdges;
4679 for (
auto &CalleeCallerE : OrigCalleeCallerEdges) {
4680 if (CalleeCallerE == CalleeEdge)
4682 if (!OtherCallersToShareMerge.
contains(CalleeCallerE->Caller))
4684 CallerToMoveCount[CalleeCallerE->Caller]++;
4685 moveEdgeToExistingCalleeClone(CalleeCallerE, MergeNode,
4689 removeNoneTypeCalleeEdges(OrigCallee);
4690 removeNoneTypeCalleeEdges(MergeNode);
4708template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
4709void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::
4710 findOtherCallersToShareMerge(
4712 std::vector<std::shared_ptr<ContextEdge>> &CalleeEdges,
4713 DenseMap<uint32_t, ContextNode *> &ContextIdToAllocationNode,
4714 DenseSet<ContextNode *> &OtherCallersToShareMerge) {
4715 auto NumCalleeClones = CalleeEdges.size();
4718 DenseMap<ContextNode *, unsigned> OtherCallersToSharedCalleeEdgeCount;
4721 unsigned PossibleOtherCallerNodes = 0;
4725 if (CalleeEdges[0]->
Callee->CallerEdges.size() < 2)
4731 DenseMap<ContextEdge *, DenseSet<ContextNode *>> CalleeEdgeToAllocNodes;
4732 for (
auto CalleeEdge : CalleeEdges) {
4733 assert(CalleeEdge->Callee->CallerEdges.size() > 1);
4736 for (
auto CalleeCallerEdges : CalleeEdge->Callee->CallerEdges) {
4737 if (CalleeCallerEdges->Caller == Node) {
4738 assert(CalleeCallerEdges == CalleeEdge);
4741 OtherCallersToSharedCalleeEdgeCount[CalleeCallerEdges->Caller]++;
4744 if (OtherCallersToSharedCalleeEdgeCount[CalleeCallerEdges->Caller] ==
4746 PossibleOtherCallerNodes++;
4750 for (
auto Id : CalleeEdge->getContextIds()) {
4751 auto *
Alloc = ContextIdToAllocationNode.
lookup(Id);
4755 MissingAllocForContextId++;
4758 CalleeEdgeToAllocNodes[CalleeEdge.get()].
insert(
Alloc);
4765 for (
auto CalleeEdge : CalleeEdges) {
4766 assert(CalleeEdge->Callee->CallerEdges.size() > 1);
4768 if (!PossibleOtherCallerNodes)
4770 auto &CurCalleeAllocNodes = CalleeEdgeToAllocNodes[CalleeEdge.get()];
4772 for (
auto &CalleeCallerE : CalleeEdge->Callee->CallerEdges) {
4774 if (CalleeCallerE == CalleeEdge)
4778 if (OtherCallersToSharedCalleeEdgeCount[CalleeCallerE->Caller] !=
4783 for (
auto Id : CalleeCallerE->getContextIds()) {
4784 auto *
Alloc = ContextIdToAllocationNode.
lookup(Id);
4789 if (!CurCalleeAllocNodes.contains(
Alloc)) {
4790 OtherCallersToSharedCalleeEdgeCount[CalleeCallerE->Caller] = 0;
4791 PossibleOtherCallerNodes--;
4798 if (!PossibleOtherCallerNodes)
4803 for (
auto &[OtherCaller,
Count] : OtherCallersToSharedCalleeEdgeCount) {
4804 if (
Count != NumCalleeClones)
4806 OtherCallersToShareMerge.
insert(OtherCaller);
4851template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
4852bool CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::assignFunctions() {
4859 DenseMap<ContextNode *, FuncInfo> CallsiteToCalleeFuncCloneMap;
4863 auto RecordCalleeFuncOfCallsite = [&](ContextNode *
Caller,
4864 const FuncInfo &CalleeFunc) {
4866 CallsiteToCalleeFuncCloneMap[
Caller] = CalleeFunc;
4870 struct FuncCloneInfo {
4875 DenseMap<CallInfo, CallInfo> CallMap;
4903 DenseMap<const ContextNode *, std::map<unsigned, SmallVector<CallInfo, 0>>>
4904 UnassignedCallClones;
4908 for (
auto &[Func, CallsWithMetadata] : FuncToCallsWithMetadata) {
4909 FuncInfo OrigFunc(Func);
4914 std::vector<FuncCloneInfo> FuncCloneInfos;
4915 for (
auto &
Call : CallsWithMetadata) {
4916 ContextNode *
Node = getNodeForInst(
Call);
4920 if (!Node ||
Node->Clones.empty())
4923 "Not having a call should have prevented cloning");
4927 std::map<FuncInfo, ContextNode *> FuncCloneToCurNodeCloneMap;
4931 auto AssignCallsiteCloneToFuncClone = [&](
const FuncInfo &FuncClone,
4933 ContextNode *CallsiteClone,
4936 FuncCloneToCurNodeCloneMap[FuncClone] = CallsiteClone;
4938 assert(FuncCloneInfos.size() > FuncClone.cloneNo());
4939 DenseMap<CallInfo, CallInfo> &CallMap =
4940 FuncCloneInfos[FuncClone.cloneNo()].CallMap;
4941 CallInfo CallClone(
Call);
4942 if (
auto It = CallMap.
find(
Call); It != CallMap.
end())
4943 CallClone = It->second;
4944 CallsiteClone->setCall(CallClone);
4946 for (
auto &MatchingCall :
Node->MatchingCalls) {
4947 CallInfo CallClone(MatchingCall);
4948 if (
auto It = CallMap.
find(MatchingCall); It != CallMap.
end())
4949 CallClone = It->second;
4951 MatchingCall = CallClone;
4959 auto MoveEdgeToNewCalleeCloneAndSetUp =
4960 [&](
const std::shared_ptr<ContextEdge> &
Edge) {
4961 ContextNode *OrigCallee =
Edge->Callee;
4962 ContextNode *NewClone = moveEdgeToNewCalleeClone(
Edge);
4963 removeNoneTypeCalleeEdges(NewClone);
4964 assert(NewClone->AllocTypes != (uint8_t)AllocationType::None);
4968 if (CallsiteToCalleeFuncCloneMap.
count(OrigCallee))
4969 RecordCalleeFuncOfCallsite(
4970 NewClone, CallsiteToCalleeFuncCloneMap[OrigCallee]);
4977 std::deque<ContextNode *> ClonesWorklist;
4979 if (!
Node->emptyContextIds())
4980 ClonesWorklist.push_back(Node);
4986 unsigned NodeCloneCount = 0;
4987 while (!ClonesWorklist.empty()) {
4988 ContextNode *Clone = ClonesWorklist.front();
4989 ClonesWorklist.pop_front();
4998 if (FuncCloneInfos.size() < NodeCloneCount) {
5000 if (NodeCloneCount == 1) {
5005 Clone->CallerEdges, [&](
const std::shared_ptr<ContextEdge> &
E) {
5006 return CallsiteToCalleeFuncCloneMap.count(E->Caller);
5010 FuncCloneInfos.push_back(
5011 {OrigFunc, DenseMap<CallInfo, CallInfo>()});
5012 AssignCallsiteCloneToFuncClone(
5013 OrigFunc,
Call, Clone,
5014 AllocationCallToContextNodeMap.count(
Call));
5015 for (
auto &CE : Clone->CallerEdges) {
5017 if (!
CE->Caller->hasCall())
5019 RecordCalleeFuncOfCallsite(
CE->Caller, OrigFunc);
5029 FuncInfo PreviousAssignedFuncClone;
5031 Clone->CallerEdges, [&](
const std::shared_ptr<ContextEdge> &
E) {
5032 return CallsiteToCalleeFuncCloneMap.count(E->Caller);
5034 bool CallerAssignedToCloneOfFunc =
false;
5035 if (EI != Clone->CallerEdges.end()) {
5036 const std::shared_ptr<ContextEdge> &
Edge = *EI;
5037 PreviousAssignedFuncClone =
5038 CallsiteToCalleeFuncCloneMap[
Edge->Caller];
5039 CallerAssignedToCloneOfFunc =
true;
5044 DenseMap<CallInfo, CallInfo> NewCallMap;
5045 unsigned CloneNo = FuncCloneInfos.size();
5046 assert(CloneNo > 0 &&
"Clone 0 is the original function, which "
5047 "should already exist in the map");
5048 FuncInfo NewFuncClone = cloneFunctionForCallsite(
5049 OrigFunc,
Call, NewCallMap, CallsWithMetadata, CloneNo);
5050 FuncCloneInfos.push_back({NewFuncClone, std::move(NewCallMap)});
5051 FunctionClonesAnalysis++;
5057 if (!CallerAssignedToCloneOfFunc) {
5058 AssignCallsiteCloneToFuncClone(
5059 NewFuncClone,
Call, Clone,
5060 AllocationCallToContextNodeMap.count(
Call));
5061 for (
auto &CE : Clone->CallerEdges) {
5063 if (!
CE->Caller->hasCall())
5065 RecordCalleeFuncOfCallsite(
CE->Caller, NewFuncClone);
5077 auto CallerEdges = Clone->CallerEdges;
5078 for (
auto CE : CallerEdges) {
5080 if (
CE->isRemoved()) {
5086 if (!
CE->Caller->hasCall())
5089 if (!CallsiteToCalleeFuncCloneMap.
count(
CE->Caller) ||
5093 CallsiteToCalleeFuncCloneMap[
CE->Caller] !=
5094 PreviousAssignedFuncClone)
5097 RecordCalleeFuncOfCallsite(
CE->Caller, NewFuncClone);
5110 auto CalleeEdges =
CE->Caller->CalleeEdges;
5111 for (
auto CalleeEdge : CalleeEdges) {
5114 if (CalleeEdge->isRemoved()) {
5119 ContextNode *
Callee = CalleeEdge->Callee;
5123 if (Callee == Clone || !
Callee->hasCall())
5128 if (Callee == CalleeEdge->Caller)
5130 ContextNode *NewClone =
5131 MoveEdgeToNewCalleeCloneAndSetUp(CalleeEdge);
5134 removeNoneTypeCalleeEdges(Callee);
5142 CallInfo OrigCall(
Callee->getOrigNode()->Call);
5143 OrigCall.setCloneNo(0);
5144 DenseMap<CallInfo, CallInfo> &CallMap =
5145 FuncCloneInfos[NewFuncClone.cloneNo()].CallMap;
5147 CallInfo NewCall(CallMap[OrigCall]);
5149 NewClone->setCall(NewCall);
5151 for (
auto &MatchingCall : NewClone->MatchingCalls) {
5152 CallInfo OrigMatchingCall(MatchingCall);
5153 OrigMatchingCall.setCloneNo(0);
5155 CallInfo NewCall(CallMap[OrigMatchingCall]);
5158 MatchingCall = NewCall;
5167 auto FindFirstAvailFuncClone = [&]() {
5172 for (
auto &CF : FuncCloneInfos) {
5173 if (!FuncCloneToCurNodeCloneMap.count(CF.FuncClone))
5174 return CF.FuncClone;
5177 "Expected an available func clone for this callsite clone");
5194 std::map<FuncInfo, ContextNode *> FuncCloneToNewCallsiteCloneMap;
5195 FuncInfo FuncCloneAssignedToCurCallsiteClone;
5199 auto CloneCallerEdges = Clone->CallerEdges;
5200 for (
auto &
Edge : CloneCallerEdges) {
5204 if (
Edge->isRemoved())
5207 if (!
Edge->Caller->hasCall())
5211 if (CallsiteToCalleeFuncCloneMap.
count(
Edge->Caller)) {
5212 FuncInfo FuncCloneCalledByCaller =
5213 CallsiteToCalleeFuncCloneMap[
Edge->Caller];
5223 if ((FuncCloneToCurNodeCloneMap.count(FuncCloneCalledByCaller) &&
5224 FuncCloneToCurNodeCloneMap[FuncCloneCalledByCaller] !=
5232 (FuncCloneAssignedToCurCallsiteClone &&
5233 FuncCloneAssignedToCurCallsiteClone !=
5234 FuncCloneCalledByCaller)) {
5249 if (FuncCloneToNewCallsiteCloneMap.count(
5250 FuncCloneCalledByCaller)) {
5251 ContextNode *NewClone =
5252 FuncCloneToNewCallsiteCloneMap[FuncCloneCalledByCaller];
5253 moveEdgeToExistingCalleeClone(
Edge, NewClone);
5255 removeNoneTypeCalleeEdges(NewClone);
5258 ContextNode *NewClone = MoveEdgeToNewCalleeCloneAndSetUp(
Edge);
5259 FuncCloneToNewCallsiteCloneMap[FuncCloneCalledByCaller] =
5262 ClonesWorklist.push_back(NewClone);
5266 removeNoneTypeCalleeEdges(Clone);
5274 if (!FuncCloneAssignedToCurCallsiteClone) {
5275 FuncCloneAssignedToCurCallsiteClone = FuncCloneCalledByCaller;
5277 AssignCallsiteCloneToFuncClone(
5278 FuncCloneCalledByCaller,
Call, Clone,
5279 AllocationCallToContextNodeMap.count(
Call));
5283 assert(FuncCloneAssignedToCurCallsiteClone ==
5284 FuncCloneCalledByCaller);
5293 if (!FuncCloneAssignedToCurCallsiteClone) {
5294 FuncCloneAssignedToCurCallsiteClone = FindFirstAvailFuncClone();
5295 assert(FuncCloneAssignedToCurCallsiteClone);
5297 AssignCallsiteCloneToFuncClone(
5298 FuncCloneAssignedToCurCallsiteClone,
Call, Clone,
5299 AllocationCallToContextNodeMap.count(
Call));
5301 assert(FuncCloneToCurNodeCloneMap
5302 [FuncCloneAssignedToCurCallsiteClone] == Clone);
5304 RecordCalleeFuncOfCallsite(
Edge->Caller,
5305 FuncCloneAssignedToCurCallsiteClone);
5325 if (!FuncCloneAssignedToCurCallsiteClone) {
5326 FuncCloneAssignedToCurCallsiteClone = FindFirstAvailFuncClone();
5327 assert(FuncCloneAssignedToCurCallsiteClone &&
5328 "No available func clone for this callsite clone");
5329 AssignCallsiteCloneToFuncClone(
5330 FuncCloneAssignedToCurCallsiteClone,
Call, Clone,
5331 AllocationCallToContextNodeMap.contains(
Call));
5336 for (
const auto &PE :
Node->CalleeEdges)
5338 for (
const auto &CE :
Node->CallerEdges)
5340 for (
auto *Clone :
Node->Clones) {
5342 for (
const auto &PE : Clone->CalleeEdges)
5344 for (
const auto &CE : Clone->CallerEdges)
5350 if (FuncCloneInfos.size() < 2)
5356 for (
auto &
Call : CallsWithMetadata) {
5357 ContextNode *
Node = getNodeForInst(
Call);
5358 if (!Node || !
Node->hasCall() ||
Node->emptyContextIds())
5364 if (
Node->Clones.size() + 1 >= FuncCloneInfos.size())
5368 DenseSet<unsigned> NodeCallClones;
5369 for (
auto *
C :
Node->Clones)
5370 NodeCallClones.
insert(
C->Call.cloneNo());
5373 for (
auto &FC : FuncCloneInfos) {
5378 if (++
I == 1 || NodeCallClones.
contains(
I)) {
5383 auto &CallVector = UnassignedCallClones[
Node][
I];
5384 DenseMap<CallInfo, CallInfo> &CallMap =
FC.CallMap;
5385 if (
auto It = CallMap.
find(
Call); It != CallMap.
end()) {
5386 CallInfo CallClone = It->second;
5387 CallVector.push_back(CallClone);
5391 assert(
false &&
"Expected to find call in CallMap");
5394 for (
auto &MatchingCall :
Node->MatchingCalls) {
5395 if (
auto It = CallMap.
find(MatchingCall); It != CallMap.
end()) {
5396 CallInfo CallClone = It->second;
5397 CallVector.push_back(CallClone);
5401 assert(
false &&
"Expected to find call in CallMap");
5409 (uint8_t)AllocationType::Cold | (uint8_t)AllocationType::NotCold;
5411 auto UpdateCalls = [&](ContextNode *
Node,
5412 DenseSet<const ContextNode *> &Visited,
5413 auto &&UpdateCalls) {
5414 auto Inserted = Visited.insert(Node);
5418 for (
auto *Clone :
Node->Clones)
5419 UpdateCalls(Clone, Visited, UpdateCalls);
5421 for (
auto &
Edge :
Node->CallerEdges)
5422 UpdateCalls(
Edge->Caller, Visited, UpdateCalls);
5426 if (!
Node->hasCall() ||
Node->emptyContextIds())
5429 if (
Node->IsAllocation) {
5430 auto AT = allocTypeToUse(
Node->AllocTypes);
5436 !ContextIdToContextSizeInfos.empty()) {
5439 for (
auto Id :
Node->getContextIds()) {
5440 auto TypeI = ContextIdToAllocationType.find(Id);
5441 assert(TypeI != ContextIdToAllocationType.end());
5442 auto CSI = ContextIdToContextSizeInfos.find(Id);
5443 if (CSI != ContextIdToContextSizeInfos.end()) {
5444 for (
auto &Info : CSI->second) {
5446 if (TypeI->second == AllocationType::Cold)
5447 TotalCold +=
Info.TotalSize;
5452 AT = AllocationType::Cold;
5454 updateAllocationCall(
Node->Call, AT);
5459 if (!CallsiteToCalleeFuncCloneMap.
count(Node))
5462 auto CalleeFunc = CallsiteToCalleeFuncCloneMap[
Node];
5463 updateCall(
Node->Call, CalleeFunc);
5465 for (
auto &
Call :
Node->MatchingCalls)
5466 updateCall(
Call, CalleeFunc);
5470 if (!UnassignedCallClones.
contains(Node))
5472 DenseSet<unsigned> NodeCallClones;
5473 for (
auto *
C :
Node->Clones)
5474 NodeCallClones.
insert(
C->Call.cloneNo());
5476 auto &ClonedCalls = UnassignedCallClones[
Node];
5477 for (
auto &[CloneNo, CallVector] : ClonedCalls) {
5481 if (NodeCallClones.
contains(CloneNo))
5484 for (
auto &
Call : CallVector)
5485 updateCall(
Call, CalleeFunc);
5494 DenseSet<const ContextNode *> Visited;
5495 for (
auto &Entry : AllocationCallToContextNodeMap)
5496 UpdateCalls(
Entry.second, Visited, UpdateCalls);
5507 for (
auto &SN : FS->callsites()) {
5512 SN.Clones.size() >
I &&
5513 "Callsite summary has fewer entries than other summaries in function");
5514 if (SN.Clones.size() <=
I || !SN.Clones[
I])
5521 for (
auto &AN : FS->allocs()) {
5525 assert(AN.Versions.size() >
I &&
5526 "Alloc summary has fewer entries than other summaries in function");
5527 if (AN.Versions.size() <=
I ||
5544 NewGV->takeName(DeclGV);
5551 auto CloneFuncAliases = [&](
Function *NewF,
unsigned I) {
5552 if (!FuncToAliasMap.count(&
F))
5554 for (
auto *
A : FuncToAliasMap[&
F]) {
5556 auto *PrevA = M.getNamedAlias(AliasName);
5558 A->getType()->getPointerAddressSpace(),
5559 A->getLinkage(), AliasName, NewF);
5560 NewA->copyAttributesFrom(
A);
5562 TakeDeclNameAndReplace(PrevA, NewA);
5571 FunctionsClonedThinBackend++;
5588 for (
unsigned I = 1;
I < NumClones;
I++) {
5589 VMaps.
emplace_back(std::make_unique<ValueToValueMapTy>());
5596 FunctionCloneDuplicatesThinBackend++;
5597 auto *Func = HashToFunc[Hash];
5598 if (Func->hasAvailableExternallyLinkage()) {
5604 auto Decl = M.getOrInsertFunction(Name, Func->getFunctionType());
5606 <<
"created clone decl " <<
ore::NV(
"Decl", Decl.getCallee()));
5609 auto *PrevF = M.getFunction(Name);
5612 TakeDeclNameAndReplace(PrevF, Alias);
5614 <<
"created clone alias " <<
ore::NV(
"Alias", Alias));
5617 CloneFuncAliases(Func,
I);
5621 HashToFunc[Hash] = NewF;
5622 FunctionClonesThinBackend++;
5625 for (
auto &BB : *NewF) {
5626 for (
auto &Inst : BB) {
5627 Inst.setMetadata(LLVMContext::MD_memprof,
nullptr);
5628 Inst.setMetadata(LLVMContext::MD_callsite,
nullptr);
5633 TakeDeclNameAndReplace(PrevF, NewF);
5635 NewF->setName(Name);
5638 <<
"created clone " <<
ore::NV(
"NewFunction", NewF));
5641 CloneFuncAliases(NewF,
I);
5650 const Function *CallingFunc =
nullptr) {
5669 auto SrcFileMD =
F.getMetadata(
"thinlto_src_file");
5675 if (!SrcFileMD &&
F.isDeclaration()) {
5679 SrcFileMD = CallingFunc->getMetadata(
"thinlto_src_file");
5684 assert(SrcFileMD || OrigName ==
F.getName());
5686 StringRef SrcFile = M.getSourceFileName();
5698 if (!TheFnVI && OrigName ==
F.getName() &&
F.hasLocalLinkage() &&
5699 F.getName().contains(
'.')) {
5700 OrigName =
F.getName().rsplit(
'.').first;
5709 assert(TheFnVI ||
F.isDeclaration());
5713bool MemProfContextDisambiguation::initializeIndirectCallPromotionInfo(
5715 ICallAnalysis = std::make_unique<ICallPromotionAnalysis>();
5716 Symtab = std::make_unique<InstrProfSymtab>();
5727 if (
Error E = Symtab->create(M,
true,
false)) {
5728 std::string SymtabFailure =
toString(std::move(
E));
5729 M.getContext().emitError(
"Failed to create symtab: " + SymtabFailure);
5742 auto MIBIter = AllocNode.
MIBs.begin();
5743 for (
auto &MDOp : MemProfMD->
operands()) {
5745 auto StackIdIndexIter = MIBIter->StackIdIndices.begin();
5750 auto ContextIterBegin =
5754 (ContextIterBegin != StackContext.
end() && *ContextIterBegin == 0) ? 1
5756 for (
auto ContextIter = ContextIterBegin; ContextIter != StackContext.
end();
5761 if (LastStackContextId == *ContextIter)
5763 LastStackContextId = *ContextIter;
5764 assert(StackIdIndexIter != MIBIter->StackIdIndices.end());
5774bool MemProfContextDisambiguation::applyImport(
Module &M) {
5781 std::map<const Function *, SmallPtrSet<const GlobalAlias *, 1>>
5783 for (
auto &
A :
M.aliases()) {
5784 auto *Aliasee =
A.getAliaseeObject();
5786 FuncToAliasMap[
F].insert(&
A);
5789 if (!initializeIndirectCallPromotionInfo(M))
5796 OptimizationRemarkEmitter ORE(&
F);
5799 bool ClonesCreated =
false;
5800 unsigned NumClonesCreated = 0;
5801 auto CloneFuncIfNeeded = [&](
unsigned NumClones, FunctionSummary *
FS) {
5811 if (ClonesCreated) {
5812 assert(NumClonesCreated == NumClones);
5819 ClonesCreated =
true;
5820 NumClonesCreated = NumClones;
5823 auto CloneCallsite = [&](
const CallsiteInfo &StackNode, CallBase *CB,
5824 Function *CalledFunction, FunctionSummary *
FS) {
5826 CloneFuncIfNeeded(StackNode.
Clones.
size(), FS);
5838 if (CalledFunction != CB->getCalledOperand() &&
5839 (!GA || CalledFunction != GA->getAliaseeObject())) {
5840 SkippedCallsCloning++;
5846 auto CalleeOrigName = CalledFunction->getName();
5847 for (
unsigned J = 0; J < StackNode.
Clones.
size(); J++) {
5850 if (J > 0 && VMaps[J - 1]->
empty())
5854 if (!StackNode.
Clones[J])
5856 auto NewF =
M.getOrInsertFunction(
5858 CalledFunction->getFunctionType());
5872 ORE.emit(OptimizationRemark(
DEBUG_TYPE,
"MemprofCall", CBClone)
5873 <<
ore::NV(
"Call", CBClone) <<
" in clone "
5875 <<
" assigned to call function clone "
5876 <<
ore::NV(
"Callee", NewF.getCallee()));
5890 ImportSummary->findSummaryInModule(TheFnVI,
M.getModuleIdentifier());
5894 auto SrcModuleMD =
F.getMetadata(
"thinlto_src_module");
5896 "enable-import-metadata is needed to emit thinlto_src_module");
5897 StringRef SrcModule =
5900 if (GVS->modulePath() == SrcModule) {
5901 GVSummary = GVS.get();
5926 if (
FS->allocs().empty() &&
FS->callsites().empty())
5929 auto SI =
FS->callsites().begin();
5930 auto AI =
FS->allocs().begin();
5935 DenseMap<ValueInfo, CallsiteInfo> MapTailCallCalleeVIToCallsite;
5938 for (
auto CallsiteIt =
FS->callsites().rbegin();
5939 CallsiteIt !=
FS->callsites().rend(); CallsiteIt++) {
5940 auto &Callsite = *CallsiteIt;
5944 if (!Callsite.StackIdIndices.empty())
5946 MapTailCallCalleeVIToCallsite.
insert({Callsite.Callee, Callsite});
5955 for (
auto &BB :
F) {
5956 for (
auto &
I : BB) {
5962 auto *CalledValue = CB->getCalledOperand();
5963 auto *CalledFunction = CB->getCalledFunction();
5964 if (CalledValue && !CalledFunction) {
5965 CalledValue = CalledValue->stripPointerCasts();
5972 assert(!CalledFunction &&
5973 "Expected null called function in callsite for alias");
5977 CallStack<MDNode, MDNode::op_iterator> CallsiteContext(
5978 I.getMetadata(LLVMContext::MD_callsite));
5979 auto *MemProfMD =
I.getMetadata(LLVMContext::MD_memprof);
5985 if (CB->getAttributes().hasFnAttr(
"memprof") && !MemProfMD) {
5986 CB->getAttributes().getFnAttr(
"memprof").getValueAsString() ==
"cold"
5987 ? AllocTypeColdThinBackend++
5988 : AllocTypeNotColdThinBackend++;
5989 OrigAllocsThinBackend++;
5990 AllocVersionsThinBackend++;
5991 if (!MaxAllocVersionsThinBackend)
5992 MaxAllocVersionsThinBackend = 1;
5999 auto &AllocNode = *(AI++);
6007 CloneFuncIfNeeded(AllocNode.Versions.size(), FS);
6009 OrigAllocsThinBackend++;
6010 AllocVersionsThinBackend += AllocNode.Versions.size();
6011 if (MaxAllocVersionsThinBackend < AllocNode.Versions.size())
6012 MaxAllocVersionsThinBackend = AllocNode.Versions.size();
6022 if (AllocNode.Versions.size() == 1 &&
6025 AllocationType::NotCold ||
6027 AllocationType::None);
6028 UnclonableAllocsThinBackend++;
6034 return Type == ((uint8_t)AllocationType::NotCold |
6035 (uint8_t)AllocationType::Cold);
6039 for (
unsigned J = 0; J < AllocNode.Versions.size(); J++) {
6042 if (J > 0 && VMaps[J - 1]->
empty())
6045 if (AllocNode.Versions[J] == (uint8_t)AllocationType::None)
6048 AllocTy == AllocationType::Cold ? AllocTypeColdThinBackend++
6049 : AllocTypeNotColdThinBackend++;
6064 ORE.emit(OptimizationRemark(
DEBUG_TYPE,
"MemprofAttribute", CBClone)
6065 <<
ore::NV(
"AllocationCall", CBClone) <<
" in clone "
6067 <<
" marked with memprof allocation attribute "
6068 <<
ore::NV(
"Attribute", AllocTypeString));
6070 }
else if (!CallsiteContext.empty()) {
6071 if (!CalledFunction) {
6075 assert(!CI || !CI->isInlineAsm());
6085 recordICPInfo(CB,
FS->callsites(), SI, ICallAnalysisInfo);
6091 CloneFuncIfNeeded(NumClones, FS);
6096 assert(SI !=
FS->callsites().end());
6097 auto &StackNode = *(
SI++);
6103 for (
auto StackId : CallsiteContext) {
6105 assert(ImportSummary->getStackIdAtIndex(*StackIdIndexIter) ==
6111 CloneCallsite(StackNode, CB, CalledFunction, FS);
6113 }
else if (CB->isTailCall() && CalledFunction) {
6116 ValueInfo CalleeVI =
6118 if (CalleeVI && MapTailCallCalleeVIToCallsite.
count(CalleeVI)) {
6119 auto Callsite = MapTailCallCalleeVIToCallsite.
find(CalleeVI);
6120 assert(Callsite != MapTailCallCalleeVIToCallsite.
end());
6121 CloneCallsite(Callsite->second, CB, CalledFunction, FS);
6128 performICP(M,
FS->callsites(), VMaps, ICallAnalysisInfo, ORE);
6138 for (
auto &BB :
F) {
6139 for (
auto &
I : BB) {
6142 I.setMetadata(LLVMContext::MD_memprof,
nullptr);
6143 I.setMetadata(LLVMContext::MD_callsite,
nullptr);
6151unsigned MemProfContextDisambiguation::recordICPInfo(
6156 uint32_t NumCandidates;
6158 auto CandidateProfileData =
6159 ICallAnalysis->getPromotionCandidatesForInstruction(
6161 if (CandidateProfileData.empty())
6167 bool ICPNeeded =
false;
6168 unsigned NumClones = 0;
6169 size_t CallsiteInfoStartIndex = std::distance(AllCallsites.
begin(), SI);
6170 for (
const auto &Candidate : CandidateProfileData) {
6172 auto CalleeValueInfo =
6174 ImportSummary->getValueInfo(Candidate.Value);
6177 assert(!CalleeValueInfo ||
SI->Callee == CalleeValueInfo);
6179 auto &StackNode = *(
SI++);
6184 [](
unsigned CloneNo) { return CloneNo != 0; });
6194 ICallAnalysisInfo.
push_back({CB, CandidateProfileData.vec(), NumCandidates,
6195 TotalCount, CallsiteInfoStartIndex});
6199void MemProfContextDisambiguation::performICP(
6201 ArrayRef<std::unique_ptr<ValueToValueMapTy>> VMaps,
6203 OptimizationRemarkEmitter &ORE) {
6210 for (
auto &Info : ICallAnalysisInfo) {
6213 auto TotalCount =
Info.TotalCount;
6214 unsigned NumClones = 0;
6217 for (
auto &Candidate :
Info.CandidateProfileData) {
6228 Function *TargetFunction = Symtab->getFunction(Candidate.Value);
6229 if (TargetFunction ==
nullptr ||
6237 return OptimizationRemarkMissed(
DEBUG_TYPE,
"UnableToFindTarget", CB)
6238 <<
"Memprof cannot promote indirect call: target with md5sum "
6239 <<
ore::NV(
"target md5sum", Candidate.Value) <<
" not found";
6244 RemainingCandidates.
push_back(Candidate);
6249 const char *Reason =
nullptr;
6252 return OptimizationRemarkMissed(
DEBUG_TYPE,
"UnableToPromote", CB)
6253 <<
"Memprof cannot promote indirect call to "
6254 <<
ore::NV(
"TargetFunction", TargetFunction)
6255 <<
" with count of " <<
ore::NV(
"TotalCount", TotalCount)
6258 RemainingCandidates.
push_back(Candidate);
6267 CallBase *CBClone = CB;
6268 for (
unsigned J = 0; J < NumClones; J++) {
6271 if (J > 0 && VMaps[J - 1]->
empty())
6281 TotalCount, isSamplePGO, &ORE);
6282 auto *TargetToUse = TargetFunction;
6285 if (StackNode.
Clones[J]) {
6304 <<
ore::NV(
"Call", CBClone) <<
" in clone "
6306 <<
" promoted and assigned to call function clone "
6307 <<
ore::NV(
"Callee", TargetToUse));
6311 TotalCount -= Candidate.Count;
6315 CallBase *CBClone = CB;
6316 for (
unsigned J = 0; J < NumClones; J++) {
6319 if (J > 0 && VMaps[J - 1]->
empty())
6325 CBClone->
setMetadata(LLVMContext::MD_prof,
nullptr);
6328 if (TotalCount != 0)
6330 IPVK_IndirectCallTarget,
Info.NumCandidates);
6335template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
6336bool CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::process(
6337 function_ref<
void(StringRef, StringRef,
const Twine &)> EmitRemark,
6338 bool AllowExtraAnalysis) {
6340 dbgs() <<
"CCG before cloning:\n";
6344 exportToDot(
"postbuild");
6357 dbgs() <<
"CCG after cloning:\n";
6361 exportToDot(
"cloned");
6363 bool Changed = assignFunctions();
6366 dbgs() <<
"CCG after assigning function clones:\n";
6370 exportToDot(
"clonefuncassign");
6373 printTotalSizes(
errs(), EmitRemark);
6378bool MemProfContextDisambiguation::processModule(
6380 llvm::function_ref<OptimizationRemarkEmitter &(
Function *)> OREGetter) {
6385 return applyImport(M);
6398 ModuleCallsiteContextGraph CCG(M, OREGetter);
6401 return CCG.process();
6406 : ImportSummary(Summary), isSamplePGO(isSamplePGO) {
6411 "-memprof-dot-scope=alloc requires -memprof-dot-alloc-id");
6415 "-memprof-dot-scope=context requires -memprof-dot-context-id");
6419 "-memprof-dot-scope=all can't have both -memprof-dot-alloc-id and "
6420 "-memprof-dot-context-id");
6421 if (ImportSummary) {
6431 auto ReadSummaryFile =
6433 if (!ReadSummaryFile) {
6440 if (!ImportSummaryForTestingOrErr) {
6446 ImportSummaryForTesting = std::move(*ImportSummaryForTestingOrErr);
6447 ImportSummary = ImportSummaryForTesting.get();
6456 if (!processModule(M, OREGetter))
6475 bool AllowExtraAnalysis =
6478 IndexCallsiteContextGraph CCG(Index, isPrevailing);
6479 CCG.process(EmitRemark, AllowExtraAnalysis);
6494 for (
auto &BB :
F) {
6495 for (
auto &
I : BB) {
6499 if (CI->hasFnAttr(
"memprof")) {
6500 CI->removeFnAttr(
"memprof");
6503 if (!CI->hasMetadata(LLVMContext::MD_callsite)) {
6504 assert(!CI->hasMetadata(LLVMContext::MD_memprof));
6510 CI->setMetadata(LLVMContext::MD_memprof,
nullptr);
6511 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 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)
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.
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.
iterator find(const_arg_type_t< KeyT > Val)
size_type count(const_arg_type_t< KeyT > Val) const
Return 1 if the specified key is in the map, 0 otherwise.
bool contains(const_arg_type_t< KeyT > Val) const
Return true if the specified key is in the map, false otherwise.
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
void reserve(size_type NumEntries)
Grow the densemap so that it can contain at least NumEntries items before resizing again.
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)
void swap(DenseSetImpl &RHS)
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"))
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.
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
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...