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;
1593 set_subtract(
Edge->getContextIds(), RemainingContextIds, NewEdgeContextIds);
1595 if (NewEdgeContextIds.
empty()) {
1601 if (RecursiveContextIds.
empty()) {
1612 DenseSet<uint32_t> NonRecursiveRemainingCurEdgeIds =
1614 set_subtract(RemainingContextIds, NonRecursiveRemainingCurEdgeIds);
1616 if (TowardsCallee) {
1617 uint8_t NewAllocType = computeAllocType(NewEdgeContextIds);
1618 auto NewEdge = std::make_shared<ContextEdge>(
1619 Edge->Callee, NewNode, NewAllocType, std::move(NewEdgeContextIds));
1620 NewNode->CalleeEdges.push_back(NewEdge);
1621 NewEdge->Callee->CallerEdges.push_back(NewEdge);
1623 uint8_t NewAllocType = computeAllocType(NewEdgeContextIds);
1624 auto NewEdge = std::make_shared<ContextEdge>(
1625 NewNode,
Edge->Caller, NewAllocType, std::move(NewEdgeContextIds));
1626 NewNode->CallerEdges.push_back(NewEdge);
1627 NewEdge->Caller->CalleeEdges.push_back(NewEdge);
1630 if (
Edge->getContextIds().empty()) {
1631 removeEdgeFromGraph(
Edge.get(), &EI, TowardsCallee);
1638template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1640 const std::shared_ptr<ContextEdge<DerivedCCG, FuncTy, CallTy>> &Edge) {
1644 assert(!Edge->ContextIds.empty());
1647template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1649 bool CheckEdges =
true) {
1650 if (
Node->isRemoved())
1654 auto NodeContextIds =
Node->getContextIds();
1658 if (
Node->CallerEdges.size()) {
1660 Node->CallerEdges.front()->ContextIds);
1664 set_union(CallerEdgeContextIds, Edge->ContextIds);
1671 NodeContextIds == CallerEdgeContextIds ||
1674 if (
Node->CalleeEdges.size()) {
1676 Node->CalleeEdges.front()->ContextIds);
1680 set_union(CalleeEdgeContextIds, Edge->getContextIds());
1686 NodeContextIds == CalleeEdgeContextIds);
1695 for (
const auto &
E :
Node->CalleeEdges)
1701template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1702void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::
1703 assignStackNodesPostOrder(ContextNode *Node,
1704 DenseSet<const ContextNode *> &Visited,
1705 DenseMap<
uint64_t, std::vector<CallContextInfo>>
1706 &StackIdToMatchingCalls,
1707 DenseMap<CallInfo, CallInfo> &CallToMatchingCall,
1708 const DenseSet<uint32_t> &ImportantContextIds) {
1716 auto CallerEdges =
Node->CallerEdges;
1717 for (
auto &
Edge : CallerEdges) {
1719 if (
Edge->isRemoved()) {
1723 assignStackNodesPostOrder(
Edge->Caller, Visited, StackIdToMatchingCalls,
1724 CallToMatchingCall, ImportantContextIds);
1733 if (
Node->IsAllocation ||
1734 !StackIdToMatchingCalls.count(
Node->OrigStackOrAllocId))
1737 auto &Calls = StackIdToMatchingCalls[
Node->OrigStackOrAllocId];
1741 if (Calls.size() == 1) {
1742 auto &[
Call, Ids,
Func, SavedContextIds] = Calls[0];
1743 if (Ids.size() == 1) {
1744 assert(SavedContextIds.empty());
1746 assert(Node == getNodeForStackId(Ids[0]));
1747 if (
Node->Recursive)
1750 NonAllocationCallToContextNodeMap[
Call] =
Node;
1752 recordStackNode(Ids, Node,
Node->getContextIds(), ImportantContextIds);
1761 ContextNode *LastNode = getNodeForStackId(LastId);
1764 assert(LastNode == Node);
1766 ContextNode *LastNode =
Node;
1771 DenseSet<uint32_t> LastNodeContextIds = LastNode->getContextIds();
1773 [[maybe_unused]]
bool PrevIterCreatedNode =
false;
1774 bool CreatedNode =
false;
1775 for (
unsigned I = 0;
I < Calls.size();
1776 I++, PrevIterCreatedNode = CreatedNode) {
1777 CreatedNode =
false;
1778 auto &[
Call, Ids,
Func, SavedContextIds] = Calls[
I];
1781 if (SavedContextIds.empty()) {
1788 auto MatchingCall = CallToMatchingCall[
Call];
1789 if (!NonAllocationCallToContextNodeMap.contains(MatchingCall)) {
1793 assert(
I > 0 && !PrevIterCreatedNode);
1796 NonAllocationCallToContextNodeMap[MatchingCall]->MatchingCalls.push_back(
1801 assert(LastId == Ids.back());
1810 ContextNode *PrevNode = LastNode;
1814 for (
auto IdIter = Ids.rbegin() + 1; IdIter != Ids.rend(); IdIter++) {
1816 ContextNode *CurNode = getNodeForStackId(Id);
1820 assert(!CurNode->Recursive);
1822 auto *
Edge = CurNode->findEdgeFromCaller(PrevNode);
1834 if (SavedContextIds.empty()) {
1843 ContextNode *NewNode = createNewNode(
false, Func,
Call);
1844 NonAllocationCallToContextNodeMap[
Call] = NewNode;
1846 NewNode->AllocTypes = computeAllocType(SavedContextIds);
1848 ContextNode *FirstNode = getNodeForStackId(Ids[0]);
1854 connectNewNode(NewNode, FirstNode,
true, SavedContextIds);
1859 connectNewNode(NewNode, LastNode,
false, SavedContextIds);
1864 for (
auto Id : Ids) {
1865 ContextNode *CurNode = getNodeForStackId(Id);
1872 auto *PrevEdge = CurNode->findEdgeFromCallee(PrevNode);
1879 set_subtract(PrevEdge->getContextIds(), SavedContextIds);
1880 if (PrevEdge->getContextIds().empty())
1881 removeEdgeFromGraph(PrevEdge);
1886 CurNode->AllocTypes = CurNode->CalleeEdges.empty()
1887 ? (uint8_t)AllocationType::None
1888 : CurNode->computeAllocType();
1892 recordStackNode(Ids, NewNode, SavedContextIds, ImportantContextIds);
1896 for (
auto Id : Ids) {
1897 ContextNode *CurNode = getNodeForStackId(Id);
1906template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
1907void CallsiteContextGraph<DerivedCCG, FuncTy,
1908 CallTy>::fixupImportantContexts() {
1909 if (ImportantContextIdInfo.empty())
1913 NumImportantContextIds = ImportantContextIdInfo.size();
1919 exportToDot(
"beforestackfixup");
1944 for (
auto &[CurContextId, Info] : ImportantContextIdInfo) {
1945 if (
Info.StackIdsToNode.empty())
1948 ContextNode *PrevNode =
nullptr;
1949 ContextNode *CurNode =
nullptr;
1950 DenseSet<const ContextEdge *> VisitedEdges;
1951 ArrayRef<uint64_t> AllStackIds(
Info.StackIds);
1954 for (
unsigned I = 0;
I < AllStackIds.size();
I++, PrevNode = CurNode) {
1958 auto LenToEnd = AllStackIds.size() -
I;
1966 auto CheckStackIds = AllStackIds.slice(
I, Len);
1967 auto EntryIt =
Info.StackIdsToNode.find(CheckStackIds);
1968 if (EntryIt ==
Info.StackIdsToNode.end())
1970 CurNode = EntryIt->second;
1987 auto *CurEdge = PrevNode->findEdgeFromCaller(CurNode);
1990 if (CurEdge->getContextIds().insert(CurContextId).second) {
1991 NumFixupEdgeIdsInserted++;
1996 NumFixupEdgesAdded++;
1997 DenseSet<uint32_t> ContextIds({CurContextId});
1998 auto AllocType = computeAllocType(ContextIds);
1999 auto NewEdge = std::make_shared<ContextEdge>(
2000 PrevNode, CurNode,
AllocType, std::move(ContextIds));
2001 PrevNode->CallerEdges.push_back(NewEdge);
2002 CurNode->CalleeEdges.push_back(NewEdge);
2004 CurEdge = NewEdge.get();
2007 VisitedEdges.
insert(CurEdge);
2010 for (
auto &
Edge : PrevNode->CallerEdges) {
2014 Edge->getContextIds().erase(CurContextId);
2022template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
2023void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::updateStackNodes() {
2031 DenseMap<uint64_t, std::vector<CallContextInfo>> StackIdToMatchingCalls;
2032 for (
auto &[Func, CallsWithMetadata] : FuncToCallsWithMetadata) {
2033 for (
auto &
Call : CallsWithMetadata) {
2035 if (AllocationCallToContextNodeMap.count(
Call))
2037 auto StackIdsWithContextNodes =
2038 getStackIdsWithContextNodesForCall(
Call.call());
2041 if (StackIdsWithContextNodes.empty())
2045 StackIdToMatchingCalls[StackIdsWithContextNodes.back()].push_back(
2046 {
Call.call(), StackIdsWithContextNodes,
Func, {}});
2056 DenseMap<uint32_t, DenseSet<uint32_t>> OldToNewContextIds;
2060 DenseMap<CallInfo, CallInfo> CallToMatchingCall;
2061 for (
auto &It : StackIdToMatchingCalls) {
2062 auto &Calls = It.getSecond();
2064 if (Calls.size() == 1) {
2065 auto &Ids = Calls[0].StackIds;
2066 if (Ids.size() == 1)
2079 DenseMap<const FuncTy *, unsigned> FuncToIndex;
2080 for (
const auto &[Idx, CallCtxInfo] :
enumerate(Calls))
2081 FuncToIndex.
insert({CallCtxInfo.Func, Idx});
2084 [&FuncToIndex](
const CallContextInfo &
A,
const CallContextInfo &
B) {
2085 return A.StackIds.size() >
B.StackIds.size() ||
2086 (
A.StackIds.size() ==
B.StackIds.size() &&
2087 (
A.StackIds <
B.StackIds ||
2088 (
A.StackIds ==
B.StackIds &&
2089 FuncToIndex[
A.Func] < FuncToIndex[
B.Func])));
2096 ContextNode *LastNode = getNodeForStackId(LastId);
2100 if (LastNode->Recursive)
2105 DenseSet<uint32_t> LastNodeContextIds = LastNode->getContextIds();
2113 DenseSet<const FuncTy *> MatchingIdsFuncSet;
2116 for (
unsigned I = 0;
I < Calls.size();
I++) {
2117 auto &[
Call, Ids,
Func, SavedContextIds] = Calls[
I];
2118 assert(SavedContextIds.empty());
2119 assert(LastId == Ids.back());
2124 if (
I > 0 && Ids != Calls[
I - 1].StackIds)
2125 MatchingIdsFuncSet.
clear();
2132 DenseSet<uint32_t> StackSequenceContextIds = LastNodeContextIds;
2134 ContextNode *PrevNode = LastNode;
2135 ContextNode *CurNode = LastNode;
2140 for (
auto IdIter = Ids.rbegin() + 1; IdIter != Ids.rend(); IdIter++) {
2142 CurNode = getNodeForStackId(Id);
2146 if (CurNode->Recursive) {
2151 auto *
Edge = CurNode->findEdgeFromCaller(PrevNode);
2172 if (StackSequenceContextIds.
empty()) {
2185 if (Ids.back() != getLastStackId(
Call)) {
2186 for (
const auto &PE : LastNode->CallerEdges) {
2187 set_subtract(StackSequenceContextIds, PE->getContextIds());
2188 if (StackSequenceContextIds.
empty())
2192 if (StackSequenceContextIds.
empty())
2204 MatchingIdsFuncSet.
insert(Func);
2211 bool DuplicateContextIds =
false;
2212 for (
unsigned J =
I + 1; J < Calls.size(); J++) {
2213 auto &CallCtxInfo = Calls[J];
2214 auto &NextIds = CallCtxInfo.StackIds;
2217 auto *NextFunc = CallCtxInfo.Func;
2218 if (NextFunc != Func) {
2221 DuplicateContextIds =
true;
2224 auto &NextCall = CallCtxInfo.Call;
2225 CallToMatchingCall[NextCall] =
Call;
2236 OldToNewContextIds.
reserve(OldToNewContextIds.
size() +
2237 StackSequenceContextIds.
size());
2240 ? duplicateContextIds(StackSequenceContextIds, OldToNewContextIds)
2241 : StackSequenceContextIds;
2242 assert(!SavedContextIds.empty());
2244 if (!DuplicateContextIds) {
2248 set_subtract(LastNodeContextIds, StackSequenceContextIds);
2249 if (LastNodeContextIds.
empty())
2256 propagateDuplicateContextIds(OldToNewContextIds);
2266 DenseSet<const ContextNode *> Visited;
2268 ImportantContextIdInfo.keys());
2269 for (
auto &Entry : AllocationCallToContextNodeMap)
2270 assignStackNodesPostOrder(
Entry.second, Visited, StackIdToMatchingCalls,
2271 CallToMatchingCall, ImportantContextIds);
2273 fixupImportantContexts();
2279uint64_t ModuleCallsiteContextGraph::getLastStackId(Instruction *
Call) {
2280 CallStack<MDNode, MDNode::op_iterator> CallsiteContext(
2282 return CallsiteContext.
back();
2285uint64_t IndexCallsiteContextGraph::getLastStackId(IndexCall &
Call) {
2287 CallStack<CallsiteInfo, SmallVector<unsigned>::const_iterator>
2290 return Index.getStackIdAtIndex(CallsiteContext.
back());
2312 auto Pos =
F.getName().find_last_of(
'.');
2315 bool Err =
F.getName().drop_front(Pos + 1).getAsInteger(10, CloneNo);
2321std::string ModuleCallsiteContextGraph::getLabel(
const Function *Func,
2322 const Instruction *
Call,
2323 unsigned CloneNo)
const {
2329std::string IndexCallsiteContextGraph::getLabel(
const FunctionSummary *Func,
2330 const IndexCall &
Call,
2331 unsigned CloneNo)
const {
2332 auto VI = FSToVIMap.find(Func);
2333 assert(VI != FSToVIMap.end());
2336 return CallerName +
" -> alloc";
2339 return CallerName +
" -> " +
2341 Callsite->Clones[CloneNo]);
2345std::vector<uint64_t>
2346ModuleCallsiteContextGraph::getStackIdsWithContextNodesForCall(
2347 Instruction *
Call) {
2348 CallStack<MDNode, MDNode::op_iterator> CallsiteContext(
2350 return getStackIdsWithContextNodes<MDNode, MDNode::op_iterator>(
2354std::vector<uint64_t>
2355IndexCallsiteContextGraph::getStackIdsWithContextNodesForCall(IndexCall &
Call) {
2357 CallStack<CallsiteInfo, SmallVector<unsigned>::const_iterator>
2359 return getStackIdsWithContextNodes<CallsiteInfo,
2360 SmallVector<unsigned>::const_iterator>(
2364template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
2365template <
class NodeT,
class IteratorT>
2366std::vector<uint64_t>
2367CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::getStackIdsWithContextNodes(
2368 CallStack<NodeT, IteratorT> &CallsiteContext) {
2369 std::vector<uint64_t> StackIds;
2370 for (
auto IdOrIndex : CallsiteContext) {
2371 auto StackId = getStackId(IdOrIndex);
2372 ContextNode *
Node = getNodeForStackId(StackId);
2375 StackIds.push_back(StackId);
2380ModuleCallsiteContextGraph::ModuleCallsiteContextGraph(
2382 llvm::function_ref<OptimizationRemarkEmitter &(
Function *)> OREGetter)
2383 :
Mod(
M), OREGetter(OREGetter) {
2387 std::map<uint64_t, uint32_t> TotalSizeToContextIdTopNCold;
2389 std::vector<CallInfo> CallsWithMetadata;
2390 for (
auto &BB :
F) {
2391 for (
auto &
I : BB) {
2394 if (
auto *MemProfMD =
I.getMetadata(LLVMContext::MD_memprof)) {
2395 CallsWithMetadata.push_back(&
I);
2396 auto *AllocNode = addAllocNode(&
I, &
F);
2397 auto *CallsiteMD =
I.getMetadata(LLVMContext::MD_callsite);
2401 for (
auto &MDOp : MemProfMD->operands()) {
2403 std::vector<ContextTotalSize> ContextSizeInfo;
2405 if (MIBMD->getNumOperands() > 2) {
2406 for (
unsigned I = 2;
I < MIBMD->getNumOperands();
I++) {
2407 MDNode *ContextSizePair =
2416 ContextSizeInfo.push_back({FullStackId, TotalSize});
2422 addStackNodesForMIB<MDNode, MDNode::op_iterator>(
2423 AllocNode, StackContext, CallsiteContext,
2425 TotalSizeToContextIdTopNCold);
2430 DotAllocContextIds = AllocNode->getContextIds();
2434 I.setMetadata(LLVMContext::MD_memprof,
nullptr);
2435 I.setMetadata(LLVMContext::MD_callsite,
nullptr);
2438 else if (
I.getMetadata(LLVMContext::MD_callsite)) {
2439 CallsWithMetadata.push_back(&
I);
2443 if (!CallsWithMetadata.empty())
2444 FuncToCallsWithMetadata[&
F] = CallsWithMetadata;
2448 dbgs() <<
"CCG before updating call stack chains:\n";
2453 exportToDot(
"prestackupdate");
2458 exportToDot(
"poststackupdate");
2460 handleCallsitesWithMultipleTargets();
2465 for (
auto &FuncEntry : FuncToCallsWithMetadata)
2466 for (
auto &
Call : FuncEntry.second)
2467 Call.call()->setMetadata(LLVMContext::MD_callsite,
nullptr);
2473IndexCallsiteContextGraph::findAliaseeGUIDsPrevailingInDifferentModule() {
2474 DenseSet<GlobalValue::GUID> AliaseeGUIDs;
2475 for (
auto &
I : Index) {
2477 for (
auto &S :
VI.getSummaryList()) {
2482 auto *AliaseeSummary = &AS->getAliasee();
2490 !isPrevailing(
VI.getGUID(), S.get()))
2495 auto AliaseeGUID = AS->getAliaseeGUID();
2497 if (!isPrevailing(AliaseeGUID, AliaseeSummary))
2498 AliaseeGUIDs.
insert(AliaseeGUID);
2501 AliaseesPrevailingInDiffModuleFromAlias += AliaseeGUIDs.
size();
2502 return AliaseeGUIDs;
2505IndexCallsiteContextGraph::IndexCallsiteContextGraph(
2506 ModuleSummaryIndex &Index,
2516 findAliaseeGUIDsPrevailingInDifferentModule();
2520 std::map<uint64_t, uint32_t> TotalSizeToContextIdTopNCold;
2525 for (
const auto &
I : Index.sortedGlobalValueSummariesRange()) {
2526 auto VI = Index.getValueInfo(
I);
2527 if (GUIDsToSkip.
contains(VI.getGUID()))
2529 for (
auto &S : VI.getSummaryList()) {
2538 !isPrevailing(VI.getGUID(), S.get()))
2543 std::vector<CallInfo> CallsWithMetadata;
2544 if (!
FS->allocs().empty()) {
2545 for (
auto &AN :
FS->mutableAllocs()) {
2550 if (AN.MIBs.empty())
2552 IndexCall AllocCall(&AN);
2553 CallsWithMetadata.push_back(AllocCall);
2554 auto *AllocNode = addAllocNode(AllocCall, FS);
2562 AN.ContextSizeInfos.size() == AN.MIBs.size());
2564 for (
auto &MIB : AN.MIBs) {
2567 std::vector<ContextTotalSize> ContextSizeInfo;
2568 if (!AN.ContextSizeInfos.empty()) {
2569 for (
auto [FullStackId, TotalSize] : AN.ContextSizeInfos[
I])
2570 ContextSizeInfo.push_back({FullStackId, TotalSize});
2572 addStackNodesForMIB<MIBInfo, SmallVector<unsigned>::const_iterator>(
2573 AllocNode, StackContext, EmptyContext, MIB.AllocType,
2574 ContextSizeInfo, TotalSizeToContextIdTopNCold);
2580 DotAllocContextIds = AllocNode->getContextIds();
2586 AN.Versions[0] = (
uint8_t)allocTypeToUse(AllocNode->AllocTypes);
2590 if (!
FS->callsites().empty())
2591 for (
auto &SN :
FS->mutableCallsites()) {
2592 IndexCall StackNodeCall(&SN);
2593 CallsWithMetadata.push_back(StackNodeCall);
2596 if (!CallsWithMetadata.empty())
2597 FuncToCallsWithMetadata[
FS] = CallsWithMetadata;
2599 if (!
FS->allocs().empty() || !
FS->callsites().empty())
2605 dbgs() <<
"CCG before updating call stack chains:\n";
2610 exportToDot(
"prestackupdate");
2615 exportToDot(
"poststackupdate");
2617 handleCallsitesWithMultipleTargets();
2622template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
2623void CallsiteContextGraph<DerivedCCG, FuncTy,
2624 CallTy>::handleCallsitesWithMultipleTargets() {
2639 std::vector<std::pair<CallInfo, ContextNode *>> NewCallToNode;
2640 for (
auto &Entry : NonAllocationCallToContextNodeMap) {
2641 auto *
Node = Entry.second;
2650 std::vector<CallInfo> AllCalls;
2651 AllCalls.reserve(
Node->MatchingCalls.size() + 1);
2652 AllCalls.push_back(
Node->Call);
2666 if (partitionCallsByCallee(
Node, AllCalls, NewCallToNode))
2669 auto It = AllCalls.begin();
2671 for (; It != AllCalls.end(); ++It) {
2674 for (
auto EI =
Node->CalleeEdges.begin(); EI !=
Node->CalleeEdges.end();
2677 if (!Edge->Callee->hasCall())
2679 assert(NodeToCallingFunc.count(Edge->Callee));
2681 if (!calleesMatch(
ThisCall.call(), EI, TailCallToContextNodeMap)) {
2690 if (
Node->Call != ThisCall) {
2691 Node->setCall(ThisCall);
2702 Node->MatchingCalls.clear();
2705 if (It == AllCalls.end()) {
2706 RemovedEdgesWithMismatchedCallees++;
2710 Node->setCall(CallInfo());
2715 for (++It; It != AllCalls.end(); ++It) {
2719 Node->MatchingCalls.push_back(ThisCall);
2728 NonAllocationCallToContextNodeMap.remove_if([](
const auto &it) {
2729 return !it.second->hasCall() || it.second->Call != it.first;
2733 for (
auto &[
Call,
Node] : NewCallToNode)
2734 NonAllocationCallToContextNodeMap[
Call] =
Node;
2738 for (
auto &[
Call,
Node] : TailCallToContextNodeMap)
2739 NonAllocationCallToContextNodeMap[
Call] =
Node;
2742template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
2743bool CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::partitionCallsByCallee(
2745 std::vector<std::pair<CallInfo, ContextNode *>> &NewCallToNode) {
2749 struct CallsWithSameCallee {
2750 std::vector<CallInfo> Calls;
2751 ContextNode *
Node =
nullptr;
2757 for (
auto ThisCall : AllCalls) {
2758 auto *
F = getCalleeFunc(
ThisCall.call());
2760 CalleeFuncToCallInfo[
F].Calls.push_back(ThisCall);
2769 for (
const auto &Edge :
Node->CalleeEdges) {
2770 if (!Edge->Callee->hasCall())
2772 const FuncTy *ProfiledCalleeFunc = NodeToCallingFunc[Edge->Callee];
2773 if (CalleeFuncToCallInfo.
contains(ProfiledCalleeFunc))
2774 CalleeNodeToCallInfo[Edge->Callee] =
2775 &CalleeFuncToCallInfo[ProfiledCalleeFunc];
2781 if (CalleeNodeToCallInfo.
empty())
2793 ContextNode *UnmatchedCalleesNode =
nullptr;
2795 bool UsedOrigNode =
false;
2800 auto CalleeEdges =
Node->CalleeEdges;
2801 for (
auto &Edge : CalleeEdges) {
2802 if (!Edge->Callee->hasCall())
2807 ContextNode *CallerNodeToUse =
nullptr;
2811 if (!CalleeNodeToCallInfo.
contains(Edge->Callee)) {
2812 if (!UnmatchedCalleesNode)
2813 UnmatchedCalleesNode =
2814 createNewNode(
false, NodeToCallingFunc[
Node]);
2815 CallerNodeToUse = UnmatchedCalleesNode;
2819 auto *Info = CalleeNodeToCallInfo[Edge->Callee];
2822 if (!UsedOrigNode) {
2825 Node->MatchingCalls.clear();
2826 UsedOrigNode =
true;
2829 createNewNode(
false, NodeToCallingFunc[
Node]);
2830 assert(!Info->Calls.empty());
2833 Info->Node->setCall(Info->Calls.front());
2839 NewCallToNode.push_back({Info->Node->Call, Info->Node});
2841 CallerNodeToUse = Info->Node;
2845 if (CallerNodeToUse ==
Node)
2848 moveCalleeEdgeToNewCaller(Edge, CallerNodeToUse);
2855 for (
auto &
I : CalleeNodeToCallInfo)
2856 removeNoneTypeCallerEdges(
I.second->Node);
2857 if (UnmatchedCalleesNode)
2858 removeNoneTypeCallerEdges(UnmatchedCalleesNode);
2859 removeNoneTypeCallerEdges(
Node);
2872 return Index.getStackIdAtIndex(IdOrIndex);
2875template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
2876bool CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::calleesMatch(
2877 CallTy
Call, EdgeIter &EI,
2878 MapVector<CallInfo, ContextNode *> &TailCallToContextNodeMap) {
2880 const FuncTy *ProfiledCalleeFunc = NodeToCallingFunc[
Edge->Callee];
2881 const FuncTy *CallerFunc = NodeToCallingFunc[
Edge->Caller];
2884 std::vector<std::pair<CallTy, FuncTy *>> FoundCalleeChain;
2885 if (!calleeMatchesFunc(
Call, ProfiledCalleeFunc, CallerFunc,
2890 if (FoundCalleeChain.empty())
2894 auto *CurEdge =
Callee->findEdgeFromCaller(Caller);
2898 CurEdge->ContextIds.insert_range(
Edge->ContextIds);
2899 CurEdge->AllocTypes |=
Edge->AllocTypes;
2904 auto NewEdge = std::make_shared<ContextEdge>(
2905 Callee, Caller,
Edge->AllocTypes,
Edge->ContextIds);
2906 Callee->CallerEdges.push_back(NewEdge);
2907 if (Caller ==
Edge->Caller) {
2911 EI =
Caller->CalleeEdges.insert(EI, NewEdge);
2914 "Iterator position not restored after insert and increment");
2916 Caller->CalleeEdges.push_back(NewEdge);
2921 auto *CurCalleeNode =
Edge->Callee;
2922 for (
auto &[NewCall, Func] : FoundCalleeChain) {
2923 ContextNode *NewNode =
nullptr;
2925 if (TailCallToContextNodeMap.
count(NewCall)) {
2926 NewNode = TailCallToContextNodeMap[NewCall];
2927 NewNode->AllocTypes |=
Edge->AllocTypes;
2929 FuncToCallsWithMetadata[
Func].push_back({NewCall});
2931 NewNode = createNewNode(
false, Func, NewCall);
2932 TailCallToContextNodeMap[NewCall] = NewNode;
2933 NewNode->AllocTypes =
Edge->AllocTypes;
2937 AddEdge(NewNode, CurCalleeNode);
2939 CurCalleeNode = NewNode;
2943 AddEdge(
Edge->Caller, CurCalleeNode);
2951 removeEdgeFromGraph(
Edge.get(), &EI,
true);
2963bool ModuleCallsiteContextGraph::findProfiledCalleeThroughTailCalls(
2965 std::vector<std::pair<Instruction *, Function *>> &FoundCalleeChain,
2966 bool &FoundMultipleCalleeChains) {
2973 FoundCalleeChain.push_back({Callsite,
F});
2988 bool FoundSingleCalleeChain =
false;
2989 for (
auto &BB : *CalleeFunc) {
2990 for (
auto &
I : BB) {
2992 if (!CB || !CB->isTailCall())
2994 auto *CalledValue = CB->getCalledOperand();
2995 auto *CalledFunction = CB->getCalledFunction();
2996 if (CalledValue && !CalledFunction) {
2997 CalledValue = CalledValue->stripPointerCasts();
3004 assert(!CalledFunction &&
3005 "Expected null called function in callsite for alias");
3008 if (!CalledFunction)
3010 if (CalledFunction == ProfiledCallee) {
3011 if (FoundSingleCalleeChain) {
3012 FoundMultipleCalleeChains =
true;
3015 FoundSingleCalleeChain =
true;
3016 FoundProfiledCalleeCount++;
3017 FoundProfiledCalleeDepth +=
Depth;
3018 if (
Depth > FoundProfiledCalleeMaxDepth)
3019 FoundProfiledCalleeMaxDepth =
Depth;
3020 SaveCallsiteInfo(&
I, CalleeFunc);
3021 }
else if (findProfiledCalleeThroughTailCalls(
3022 ProfiledCallee, CalledFunction,
Depth + 1,
3023 FoundCalleeChain, FoundMultipleCalleeChains)) {
3026 assert(!FoundMultipleCalleeChains);
3027 if (FoundSingleCalleeChain) {
3028 FoundMultipleCalleeChains =
true;
3031 FoundSingleCalleeChain =
true;
3032 SaveCallsiteInfo(&
I, CalleeFunc);
3033 }
else if (FoundMultipleCalleeChains)
3038 return FoundSingleCalleeChain;
3041const Function *ModuleCallsiteContextGraph::getCalleeFunc(Instruction *
Call) {
3043 if (!CB->getCalledOperand() || CB->isIndirectCall())
3045 auto *CalleeVal = CB->getCalledOperand()->stripPointerCasts();
3052bool ModuleCallsiteContextGraph::calleeMatchesFunc(
3054 std::vector<std::pair<Instruction *, Function *>> &FoundCalleeChain) {
3056 if (!CB->getCalledOperand() || CB->isIndirectCall())
3058 auto *CalleeVal = CB->getCalledOperand()->stripPointerCasts();
3060 if (CalleeFunc == Func)
3063 if (Alias && Alias->getAliasee() == Func)
3074 bool FoundMultipleCalleeChains =
false;
3075 if (!findProfiledCalleeThroughTailCalls(Func, CalleeVal,
Depth,
3077 FoundMultipleCalleeChains)) {
3078 LLVM_DEBUG(
dbgs() <<
"Not found through unique tail call chain: "
3079 <<
Func->getName() <<
" from " << CallerFunc->
getName()
3080 <<
" that actually called " << CalleeVal->getName()
3081 << (FoundMultipleCalleeChains
3082 ?
" (found multiple possible chains)"
3085 if (FoundMultipleCalleeChains)
3086 FoundProfiledCalleeNonUniquelyCount++;
3093bool ModuleCallsiteContextGraph::sameCallee(Instruction *Call1,
3094 Instruction *Call2) {
3096 if (!CB1->getCalledOperand() || CB1->isIndirectCall())
3098 auto *CalleeVal1 = CB1->getCalledOperand()->stripPointerCasts();
3101 if (!CB2->getCalledOperand() || CB2->isIndirectCall())
3103 auto *CalleeVal2 = CB2->getCalledOperand()->stripPointerCasts();
3105 return CalleeFunc1 == CalleeFunc2;
3108bool IndexCallsiteContextGraph::findProfiledCalleeThroughTailCalls(
3109 ValueInfo ProfiledCallee, ValueInfo CurCallee,
unsigned Depth,
3110 std::vector<std::pair<IndexCall, FunctionSummary *>> &FoundCalleeChain,
3111 bool &FoundMultipleCalleeChains) {
3117 auto CreateAndSaveCallsiteInfo = [&](ValueInfo
Callee, FunctionSummary *
FS) {
3120 if (!FunctionCalleesToSynthesizedCallsiteInfos.count(FS) ||
3121 !FunctionCalleesToSynthesizedCallsiteInfos[FS].count(Callee))
3124 FunctionCalleesToSynthesizedCallsiteInfos[
FS][
Callee] =
3125 std::make_unique<CallsiteInfo>(Callee, SmallVector<unsigned>());
3126 CallsiteInfo *NewCallsiteInfo =
3127 FunctionCalleesToSynthesizedCallsiteInfos[
FS][
Callee].get();
3128 FoundCalleeChain.push_back({NewCallsiteInfo,
FS});
3135 bool FoundSingleCalleeChain =
false;
3138 !isPrevailing(CurCallee.
getGUID(), S.get()))
3143 auto FSVI = CurCallee;
3146 FSVI = AS->getAliaseeVI();
3147 for (
auto &CallEdge :
FS->calls()) {
3148 if (!CallEdge.second.hasTailCall())
3150 if (CallEdge.first == ProfiledCallee) {
3151 if (FoundSingleCalleeChain) {
3152 FoundMultipleCalleeChains =
true;
3155 FoundSingleCalleeChain =
true;
3156 FoundProfiledCalleeCount++;
3157 FoundProfiledCalleeDepth +=
Depth;
3158 if (
Depth > FoundProfiledCalleeMaxDepth)
3159 FoundProfiledCalleeMaxDepth =
Depth;
3160 CreateAndSaveCallsiteInfo(CallEdge.first, FS);
3162 assert(!FSToVIMap.count(FS) || FSToVIMap[FS] == FSVI);
3163 FSToVIMap[
FS] = FSVI;
3164 }
else if (findProfiledCalleeThroughTailCalls(
3165 ProfiledCallee, CallEdge.first,
Depth + 1,
3166 FoundCalleeChain, FoundMultipleCalleeChains)) {
3169 assert(!FoundMultipleCalleeChains);
3170 if (FoundSingleCalleeChain) {
3171 FoundMultipleCalleeChains =
true;
3174 FoundSingleCalleeChain =
true;
3175 CreateAndSaveCallsiteInfo(CallEdge.first, FS);
3177 assert(!FSToVIMap.count(FS) || FSToVIMap[FS] == FSVI);
3178 FSToVIMap[
FS] = FSVI;
3179 }
else if (FoundMultipleCalleeChains)
3184 return FoundSingleCalleeChain;
3187const FunctionSummary *
3188IndexCallsiteContextGraph::getCalleeFunc(IndexCall &
Call) {
3190 if (
Callee.getSummaryList().empty())
3195bool IndexCallsiteContextGraph::calleeMatchesFunc(
3196 IndexCall &
Call,
const FunctionSummary *Func,
3197 const FunctionSummary *CallerFunc,
3198 std::vector<std::pair<IndexCall, FunctionSummary *>> &FoundCalleeChain) {
3202 AliasSummary *Alias =
3203 Callee.getSummaryList().empty()
3206 assert(FSToVIMap.count(Func));
3207 auto FuncVI = FSToVIMap[
Func];
3208 if (Callee == FuncVI ||
3223 bool FoundMultipleCalleeChains =
false;
3224 if (!findProfiledCalleeThroughTailCalls(
3225 FuncVI, Callee,
Depth, FoundCalleeChain, FoundMultipleCalleeChains)) {
3226 LLVM_DEBUG(
dbgs() <<
"Not found through unique tail call chain: " << FuncVI
3227 <<
" from " << FSToVIMap[CallerFunc]
3228 <<
" that actually called " << Callee
3229 << (FoundMultipleCalleeChains
3230 ?
" (found multiple possible chains)"
3233 if (FoundMultipleCalleeChains)
3234 FoundProfiledCalleeNonUniquelyCount++;
3241bool IndexCallsiteContextGraph::sameCallee(IndexCall &Call1, IndexCall &Call2) {
3244 return Callee1 == Callee2;
3247template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3248void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode::dump()
3254template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3255void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode::print(
3256 raw_ostream &OS)
const {
3257 OS <<
"Node " <<
this <<
"\n";
3261 OS <<
" (recursive)";
3263 if (!MatchingCalls.empty()) {
3264 OS <<
"\tMatchingCalls:\n";
3265 for (
auto &MatchingCall : MatchingCalls) {
3267 MatchingCall.print(OS);
3271 OS <<
"\tNodeId: " <<
NodeId <<
"\n";
3273 OS <<
"\tContextIds:";
3275 auto ContextIds = getContextIds();
3276 std::vector<uint32_t> SortedIds(ContextIds.begin(), ContextIds.end());
3277 std::sort(SortedIds.begin(), SortedIds.end());
3278 for (
auto Id : SortedIds)
3281 OS <<
"\tCalleeEdges:\n";
3282 for (
auto &
Edge : CalleeEdges)
3283 OS <<
"\t\t" << *
Edge <<
" (Callee NodeId: " <<
Edge->Callee->NodeId
3285 OS <<
"\tCallerEdges:\n";
3286 for (
auto &
Edge : CallerEdges)
3287 OS <<
"\t\t" << *
Edge <<
" (Caller NodeId: " <<
Edge->Caller->NodeId
3289 if (!Clones.empty()) {
3292 for (
auto *
C : Clones)
3293 OS <<
LS <<
C <<
" NodeId: " <<
C->NodeId;
3295 }
else if (CloneOf) {
3296 OS <<
"\tClone of " << CloneOf <<
" NodeId: " << CloneOf->NodeId <<
"\n";
3300template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3301void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextEdge::dump()
3307template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3308void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextEdge::print(
3309 raw_ostream &OS)
const {
3310 OS <<
"Edge from Callee " <<
Callee <<
" to Caller: " <<
Caller
3311 << (IsBackedge ?
" (BE)" :
"")
3313 OS <<
" ContextIds:";
3314 std::vector<uint32_t> SortedIds(ContextIds.begin(), ContextIds.end());
3315 std::sort(SortedIds.begin(), SortedIds.end());
3316 for (
auto Id : SortedIds)
3320template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3321void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::dump()
const {
3325template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3326void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::print(
3327 raw_ostream &OS)
const {
3328 OS <<
"Callsite Context Graph:\n";
3329 using GraphType =
const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *;
3331 if (
Node->isRemoved())
3338template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3339void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::printTotalSizes(
3341 function_ref<
void(StringRef, StringRef,
const Twine &)> EmitRemark)
const {
3342 using GraphType =
const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *;
3344 if (
Node->isRemoved())
3346 if (!
Node->IsAllocation)
3348 DenseSet<uint32_t> ContextIds =
Node->getContextIds();
3349 auto AllocTypeFromCall = getAllocationCallType(
Node->Call);
3350 std::vector<uint32_t> SortedIds(ContextIds.
begin(), ContextIds.
end());
3351 std::sort(SortedIds.begin(), SortedIds.end());
3352 for (
auto Id : SortedIds) {
3353 auto TypeI = ContextIdToAllocationType.find(Id);
3354 assert(TypeI != ContextIdToAllocationType.end());
3355 auto CSI = ContextIdToContextSizeInfos.find(Id);
3356 if (CSI != ContextIdToContextSizeInfos.end()) {
3357 for (
auto &Info : CSI->second) {
3360 " full allocation context " + std::to_string(
Info.FullStackId) +
3361 " with total size " + std::to_string(
Info.TotalSize) +
" is " +
3363 if (allocTypeToUse(
Node->AllocTypes) != AllocTypeFromCall)
3365 " due to cold byte percent";
3367 Msg +=
" (internal context id " + std::to_string(Id) +
")";
3379 if (allocTypeToUse(
Node->AllocTypes) != AllocTypeFromCall)
3381 " due to cold byte percent";
3383 Msg +=
" (internal context id " + std::to_string(Id) +
")";
3393template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3394void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::check()
const {
3395 using GraphType =
const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *;
3398 for (
auto &
Edge :
Node->CallerEdges)
3403template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3405 using GraphType =
const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *;
3406 using NodeRef =
const ContextNode<DerivedCCG, FuncTy, CallTy> *;
3408 using NodePtrTy = std::unique_ptr<ContextNode<DerivedCCG, FuncTy, CallTy>>;
3424 return G->NodeOwner.begin()->get();
3427 using EdgePtrTy = std::shared_ptr<ContextEdge<DerivedCCG, FuncTy, CallTy>>;
3428 static const ContextNode<DerivedCCG, FuncTy, CallTy> *
3447template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3461 using GraphType =
const CallsiteContextGraph<DerivedCCG, FuncTy, CallTy> *;
3467 std::string LabelString =
3468 (
Twine(
"OrigId: ") + (
Node->IsAllocation ?
"Alloc" :
"") +
3471 LabelString +=
"\n";
3472 if (
Node->hasCall()) {
3473 auto Func =
G->NodeToCallingFunc.find(
Node);
3474 assert(Func !=
G->NodeToCallingFunc.end());
3476 G->getLabel(Func->second,
Node->Call.call(),
Node->Call.cloneNo());
3477 for (
auto &MatchingCall :
Node->MatchingCalls) {
3478 LabelString +=
"\n";
3479 LabelString +=
G->getLabel(Func->second, MatchingCall.call(),
3480 MatchingCall.cloneNo());
3483 LabelString +=
"null call";
3484 if (
Node->Recursive)
3485 LabelString +=
" (recursive)";
3487 LabelString +=
" (external)";
3493 auto ContextIds =
Node->getContextIds();
3497 bool Highlight =
false;
3506 std::string AttributeString = (
Twine(
"tooltip=\"") + getNodeId(
Node) +
" " +
3507 getContextIds(ContextIds) +
"\"")
3511 AttributeString +=
",fontsize=\"30\"";
3513 (
Twine(
",fillcolor=\"") + getColor(
Node->AllocTypes, Highlight) +
"\"")
3515 if (
Node->CloneOf) {
3516 AttributeString +=
",color=\"blue\"";
3517 AttributeString +=
",style=\"filled,bold,dashed\"";
3519 AttributeString +=
",style=\"filled\"";
3520 return AttributeString;
3525 auto &Edge = *(ChildIter.getCurrent());
3530 bool Highlight =
false;
3539 auto Color = getColor(Edge->AllocTypes, Highlight);
3540 std::string AttributeString =
3541 (
Twine(
"tooltip=\"") + getContextIds(Edge->ContextIds) +
"\"" +
3543 Twine(
",fillcolor=\"") + Color +
"\"" +
Twine(
",color=\"") + Color +
3546 if (Edge->IsBackedge)
3547 AttributeString +=
",style=\"dotted\"";
3550 AttributeString +=
",penwidth=\"2.0\",weight=\"2\"";
3551 return AttributeString;
3557 if (
Node->isRemoved())
3570 std::string IdString =
"ContextIds:";
3571 if (ContextIds.
size() < 100) {
3572 std::vector<uint32_t> SortedIds(ContextIds.
begin(), ContextIds.
end());
3573 std::sort(SortedIds.begin(), SortedIds.end());
3574 for (
auto Id : SortedIds)
3575 IdString += (
" " +
Twine(Id)).str();
3577 IdString += (
" (" + Twine(ContextIds.
size()) +
" ids)").str();
3582 static std::string getColor(uint8_t AllocTypes,
bool Highlight) {
3588 if (AllocTypes == (uint8_t)AllocationType::NotCold)
3590 return !
DoHighlight || Highlight ?
"brown1" :
"lightpink";
3591 if (AllocTypes == (uint8_t)AllocationType::Cold)
3592 return !
DoHighlight || Highlight ?
"cyan" :
"lightskyblue";
3594 ((uint8_t)AllocationType::NotCold | (uint8_t)AllocationType::Cold))
3595 return Highlight ?
"magenta" :
"mediumorchid1";
3599 static std::string getNodeId(NodeRef Node) {
3600 std::stringstream SStream;
3601 SStream << std::hex <<
"N0x" << (
unsigned long long)Node;
3602 std::string
Result = SStream.str();
3611template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3616template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3617void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::exportToDot(
3618 std::string Label)
const {
3623template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3624typename CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::ContextNode *
3625CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::moveEdgeToNewCalleeClone(
3626 const std::shared_ptr<ContextEdge> &
Edge,
3627 DenseSet<uint32_t> ContextIdsToMove) {
3629 assert(NodeToCallingFunc.count(Node));
3630 ContextNode *Clone =
3631 createNewNode(
Node->IsAllocation, NodeToCallingFunc[Node],
Node->Call);
3632 Node->addClone(Clone);
3633 Clone->MatchingCalls =
Node->MatchingCalls;
3634 moveEdgeToExistingCalleeClone(
Edge, Clone,
true,
3639template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3640void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::
3641 moveEdgeToExistingCalleeClone(
const std::shared_ptr<ContextEdge> &
Edge,
3642 ContextNode *NewCallee,
bool NewClone,
3643 DenseSet<uint32_t> ContextIdsToMove) {
3646 assert(NewCallee->getOrigNode() ==
Edge->Callee->getOrigNode());
3648 bool EdgeIsRecursive =
Edge->Callee ==
Edge->Caller;
3650 ContextNode *OldCallee =
Edge->Callee;
3654 auto ExistingEdgeToNewCallee = NewCallee->findEdgeFromCaller(
Edge->Caller);
3658 if (ContextIdsToMove.
empty())
3659 ContextIdsToMove =
Edge->getContextIds();
3663 if (
Edge->getContextIds().size() == ContextIdsToMove.
size()) {
3666 NewCallee->AllocTypes |=
Edge->AllocTypes;
3668 if (ExistingEdgeToNewCallee) {
3671 ExistingEdgeToNewCallee->getContextIds().insert_range(ContextIdsToMove);
3672 ExistingEdgeToNewCallee->AllocTypes |=
Edge->AllocTypes;
3673 assert(
Edge->ContextIds == ContextIdsToMove);
3674 removeEdgeFromGraph(
Edge.get());
3677 Edge->Callee = NewCallee;
3678 NewCallee->CallerEdges.push_back(
Edge);
3680 OldCallee->eraseCallerEdge(
Edge.get());
3687 auto CallerEdgeAllocType = computeAllocType(ContextIdsToMove);
3688 if (ExistingEdgeToNewCallee) {
3691 ExistingEdgeToNewCallee->getContextIds().insert_range(ContextIdsToMove);
3692 ExistingEdgeToNewCallee->AllocTypes |= CallerEdgeAllocType;
3695 auto NewEdge = std::make_shared<ContextEdge>(
3696 NewCallee,
Edge->Caller, CallerEdgeAllocType, ContextIdsToMove);
3697 Edge->Caller->CalleeEdges.push_back(NewEdge);
3698 NewCallee->CallerEdges.push_back(NewEdge);
3702 NewCallee->AllocTypes |= CallerEdgeAllocType;
3704 Edge->AllocTypes = computeAllocType(
Edge->ContextIds);
3709 for (
auto &OldCalleeEdge : OldCallee->CalleeEdges) {
3710 ContextNode *CalleeToUse = OldCalleeEdge->Callee;
3714 if (CalleeToUse == OldCallee) {
3718 if (EdgeIsRecursive) {
3722 CalleeToUse = NewCallee;
3726 DenseSet<uint32_t> EdgeContextIdsToMove =
3728 set_subtract(OldCalleeEdge->getContextIds(), EdgeContextIdsToMove);
3729 OldCalleeEdge->AllocTypes =
3730 computeAllocType(OldCalleeEdge->getContextIds());
3737 if (
auto *NewCalleeEdge = NewCallee->findEdgeFromCallee(CalleeToUse)) {
3738 NewCalleeEdge->getContextIds().insert_range(EdgeContextIdsToMove);
3739 NewCalleeEdge->AllocTypes |= computeAllocType(EdgeContextIdsToMove);
3743 auto NewEdge = std::make_shared<ContextEdge>(
3744 CalleeToUse, NewCallee, computeAllocType(EdgeContextIdsToMove),
3745 EdgeContextIdsToMove);
3746 NewCallee->CalleeEdges.push_back(NewEdge);
3747 NewEdge->Callee->CallerEdges.push_back(NewEdge);
3751 OldCallee->AllocTypes = OldCallee->computeAllocType();
3753 assert((OldCallee->AllocTypes == (uint8_t)AllocationType::None) ==
3754 OldCallee->emptyContextIds());
3758 for (
const auto &OldCalleeEdge : OldCallee->CalleeEdges)
3761 for (
const auto &NewCalleeEdge : NewCallee->CalleeEdges)
3767template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3768void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::
3769 moveCalleeEdgeToNewCaller(
const std::shared_ptr<ContextEdge> &
Edge,
3770 ContextNode *NewCaller) {
3771 auto *OldCallee =
Edge->Callee;
3772 auto *NewCallee = OldCallee;
3775 bool Recursive =
Edge->Caller ==
Edge->Callee;
3777 NewCallee = NewCaller;
3779 ContextNode *OldCaller =
Edge->Caller;
3780 OldCaller->eraseCalleeEdge(
Edge.get());
3784 auto ExistingEdgeToNewCaller = NewCaller->findEdgeFromCallee(NewCallee);
3786 if (ExistingEdgeToNewCaller) {
3789 ExistingEdgeToNewCaller->getContextIds().insert_range(
3790 Edge->getContextIds());
3791 ExistingEdgeToNewCaller->AllocTypes |=
Edge->AllocTypes;
3792 Edge->ContextIds.clear();
3793 Edge->AllocTypes = (uint8_t)AllocationType::None;
3794 OldCallee->eraseCallerEdge(
Edge.get());
3797 Edge->Caller = NewCaller;
3798 NewCaller->CalleeEdges.push_back(
Edge);
3800 assert(NewCallee == NewCaller);
3803 Edge->Callee = NewCallee;
3804 NewCallee->CallerEdges.push_back(
Edge);
3805 OldCallee->eraseCallerEdge(
Edge.get());
3811 NewCaller->AllocTypes |=
Edge->AllocTypes;
3818 bool IsNewNode = NewCaller->CallerEdges.empty();
3827 for (
auto &OldCallerEdge : OldCaller->CallerEdges) {
3828 auto OldCallerCaller = OldCallerEdge->Caller;
3832 OldCallerEdge->getContextIds(),
Edge->getContextIds());
3833 if (OldCaller == OldCallerCaller) {
3834 OldCallerCaller = NewCaller;
3840 set_subtract(OldCallerEdge->getContextIds(), EdgeContextIdsToMove);
3841 OldCallerEdge->AllocTypes =
3842 computeAllocType(OldCallerEdge->getContextIds());
3847 auto *ExistingCallerEdge = NewCaller->findEdgeFromCaller(OldCallerCaller);
3851 if (ExistingCallerEdge) {
3852 ExistingCallerEdge->getContextIds().insert_range(EdgeContextIdsToMove);
3853 ExistingCallerEdge->AllocTypes |=
3854 computeAllocType(EdgeContextIdsToMove);
3857 auto NewEdge = std::make_shared<ContextEdge>(
3858 NewCaller, OldCallerCaller, computeAllocType(EdgeContextIdsToMove),
3859 EdgeContextIdsToMove);
3860 NewCaller->CallerEdges.push_back(NewEdge);
3861 NewEdge->Caller->CalleeEdges.push_back(NewEdge);
3866 OldCaller->AllocTypes = OldCaller->computeAllocType();
3868 assert((OldCaller->AllocTypes == (uint8_t)AllocationType::None) ==
3869 OldCaller->emptyContextIds());
3873 for (
const auto &OldCallerEdge : OldCaller->CallerEdges)
3876 for (
const auto &NewCallerEdge : NewCaller->CallerEdges)
3882template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3883void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::
3884 recursivelyRemoveNoneTypeCalleeEdges(
3885 ContextNode *Node, DenseSet<const ContextNode *> &Visited) {
3890 removeNoneTypeCalleeEdges(Node);
3892 for (
auto *Clone :
Node->Clones)
3893 recursivelyRemoveNoneTypeCalleeEdges(Clone, Visited);
3897 auto CallerEdges =
Node->CallerEdges;
3898 for (
auto &
Edge : CallerEdges) {
3900 if (
Edge->isRemoved()) {
3904 recursivelyRemoveNoneTypeCalleeEdges(
Edge->Caller, Visited);
3909template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3910void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::markBackedges() {
3915 DenseSet<const ContextNode *> Visited;
3916 DenseSet<const ContextNode *> CurrentStack;
3917 for (
auto &Entry : NonAllocationCallToContextNodeMap) {
3919 if (
Node->isRemoved())
3922 if (!
Node->CallerEdges.empty())
3924 markBackedges(Node, Visited, CurrentStack);
3930template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3931void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::markBackedges(
3932 ContextNode *Node, DenseSet<const ContextNode *> &Visited,
3933 DenseSet<const ContextNode *> &CurrentStack) {
3934 auto I = Visited.
insert(Node);
3938 for (
auto &CalleeEdge :
Node->CalleeEdges) {
3939 auto *
Callee = CalleeEdge->Callee;
3940 if (Visited.
count(Callee)) {
3943 if (CurrentStack.
count(Callee))
3944 CalleeEdge->IsBackedge =
true;
3947 CurrentStack.
insert(Callee);
3948 markBackedges(Callee, Visited, CurrentStack);
3949 CurrentStack.
erase(Callee);
3953template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3954void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::identifyClones() {
3955 DenseSet<const ContextNode *> Visited;
3956 for (
auto &Entry : AllocationCallToContextNodeMap) {
3958 identifyClones(
Entry.second, Visited,
Entry.second->getContextIds());
3961 for (
auto &Entry : AllocationCallToContextNodeMap)
3962 recursivelyRemoveNoneTypeCalleeEdges(
Entry.second, Visited);
3975template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
3976void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::identifyClones(
3977 ContextNode *Node, DenseSet<const ContextNode *> &Visited,
3978 const DenseSet<uint32_t> &AllocContextIds) {
3988 if (!
Node->hasCall())
4007 auto CallerEdges =
Node->CallerEdges;
4008 for (
auto &
Edge : CallerEdges) {
4010 if (
Edge->isRemoved()) {
4016 if (
Edge->IsBackedge) {
4023 if (!Visited.
count(
Edge->Caller) && !
Edge->Caller->CloneOf) {
4024 identifyClones(
Edge->Caller, Visited, AllocContextIds);
4047 const unsigned AllocTypeCloningPriority[] = { 3, 4,
4051 [&](
const std::shared_ptr<ContextEdge> &
A,
4052 const std::shared_ptr<ContextEdge> &
B) {
4055 if (A->ContextIds.empty())
4061 if (B->ContextIds.empty())
4064 if (A->AllocTypes == B->AllocTypes)
4067 return *A->ContextIds.begin() < *B->ContextIds.begin();
4068 return AllocTypeCloningPriority[A->AllocTypes] <
4069 AllocTypeCloningPriority[B->AllocTypes];
4072 assert(
Node->AllocTypes != (uint8_t)AllocationType::None);
4074 DenseSet<uint32_t> RecursiveContextIds;
4079 DenseSet<uint32_t> AllCallerContextIds;
4080 for (
auto &CE :
Node->CallerEdges) {
4083 AllCallerContextIds.
reserve(
CE->getContextIds().size());
4084 for (
auto Id :
CE->getContextIds())
4085 if (!AllCallerContextIds.
insert(Id).second)
4086 RecursiveContextIds.
insert(Id);
4096 auto CallerEdges =
Node->CallerEdges;
4097 for (
auto &CallerEdge : CallerEdges) {
4099 if (CallerEdge->isRemoved()) {
4103 assert(CallerEdge->Callee == Node);
4112 if (!CallerEdge->Caller->hasCall())
4117 auto CallerEdgeContextsForAlloc =
4119 if (!RecursiveContextIds.
empty())
4120 CallerEdgeContextsForAlloc =
4122 if (CallerEdgeContextsForAlloc.empty())
4125 auto CallerAllocTypeForAlloc = computeAllocType(CallerEdgeContextsForAlloc);
4129 std::vector<uint8_t> CalleeEdgeAllocTypesForCallerEdge;
4130 CalleeEdgeAllocTypesForCallerEdge.reserve(
Node->CalleeEdges.size());
4131 for (
auto &CalleeEdge :
Node->CalleeEdges)
4132 CalleeEdgeAllocTypesForCallerEdge.push_back(intersectAllocTypes(
4133 CalleeEdge->getContextIds(), CallerEdgeContextsForAlloc));
4149 assert(CallerEdge->AllocTypes != (uint8_t)AllocationType::None);
4150 assert(
Node->AllocTypes != (uint8_t)AllocationType::None);
4151 if (!CallerEdge->IsBackedge &&
4152 allocTypeToUse(CallerAllocTypeForAlloc) ==
4153 allocTypeToUse(
Node->AllocTypes) &&
4154 allocTypesMatch<DerivedCCG, FuncTy, CallTy>(
4155 CalleeEdgeAllocTypesForCallerEdge,
Node->CalleeEdges)) {
4159 if (CallerEdge->IsBackedge) {
4163 DeferredBackedges++;
4176 if (CallerEdge->IsBackedge && !CallerEdge->Caller->CloneOf &&
4177 !Visited.
count(CallerEdge->Caller)) {
4178 const auto OrigIdCount = CallerEdge->getContextIds().size();
4181 identifyClones(CallerEdge->Caller, Visited, CallerEdgeContextsForAlloc);
4182 removeNoneTypeCalleeEdges(CallerEdge->Caller);
4186 bool UpdatedEdge =
false;
4187 if (OrigIdCount > CallerEdge->getContextIds().size()) {
4188 for (
auto E :
Node->CallerEdges) {
4190 if (
E->Caller->CloneOf != CallerEdge->Caller)
4194 auto CallerEdgeContextsForAllocNew =
4196 if (CallerEdgeContextsForAllocNew.empty())
4206 CallerEdgeContextsForAlloc.swap(CallerEdgeContextsForAllocNew);
4216 if (CallerEdge->isRemoved())
4226 CallerEdgeContextsForAlloc, CallerEdge->getContextIds());
4227 if (CallerEdgeContextsForAlloc.empty())
4232 CallerAllocTypeForAlloc = computeAllocType(CallerEdgeContextsForAlloc);
4233 CalleeEdgeAllocTypesForCallerEdge.clear();
4234 for (
auto &CalleeEdge :
Node->CalleeEdges) {
4235 CalleeEdgeAllocTypesForCallerEdge.push_back(intersectAllocTypes(
4236 CalleeEdge->getContextIds(), CallerEdgeContextsForAlloc));
4242 ContextNode *Clone =
nullptr;
4243 for (
auto *CurClone :
Node->Clones) {
4244 if (allocTypeToUse(CurClone->AllocTypes) !=
4245 allocTypeToUse(CallerAllocTypeForAlloc))
4252 assert(!BothSingleAlloc ||
4253 CurClone->AllocTypes == CallerAllocTypeForAlloc);
4259 if (BothSingleAlloc || allocTypesMatchClone<DerivedCCG, FuncTy, CallTy>(
4260 CalleeEdgeAllocTypesForCallerEdge, CurClone)) {
4268 moveEdgeToExistingCalleeClone(CallerEdge, Clone,
false,
4269 CallerEdgeContextsForAlloc);
4271 Clone = moveEdgeToNewCalleeClone(CallerEdge, CallerEdgeContextsForAlloc);
4274 assert(Clone->AllocTypes != (uint8_t)AllocationType::None);
4281 assert(
Node->AllocTypes != (uint8_t)AllocationType::None);
4287void ModuleCallsiteContextGraph::updateAllocationCall(
4292 "memprof", AllocTypeString);
4295 .emit(OptimizationRemark(
DEBUG_TYPE,
"MemprofAttribute",
Call.call())
4296 <<
ore::NV(
"AllocationCall",
Call.call()) <<
" in clone "
4298 <<
" marked with memprof allocation attribute "
4299 <<
ore::NV(
"Attribute", AllocTypeString));
4302void IndexCallsiteContextGraph::updateAllocationCall(CallInfo &
Call,
4306 assert(AI->Versions.size() >
Call.cloneNo());
4311ModuleCallsiteContextGraph::getAllocationCallType(
const CallInfo &
Call)
const {
4313 if (!CB->getAttributes().hasFnAttr(
"memprof"))
4314 return AllocationType::None;
4315 return CB->getAttributes().getFnAttr(
"memprof").getValueAsString() ==
"cold"
4316 ? AllocationType::Cold
4317 : AllocationType::NotCold;
4321IndexCallsiteContextGraph::getAllocationCallType(
const CallInfo &
Call)
const {
4323 assert(AI->Versions.size() >
Call.cloneNo());
4327void ModuleCallsiteContextGraph::updateCall(CallInfo &CallerCall,
4328 FuncInfo CalleeFunc) {
4329 auto *CurF = getCalleeFunc(CallerCall.call());
4330 auto NewCalleeCloneNo = CalleeFunc.cloneNo();
4337 if (CurCalleeCloneNo != NewCalleeCloneNo) {
4339 << CurCalleeCloneNo <<
" now " << NewCalleeCloneNo
4341 MismatchedCloneAssignments++;
4344 if (NewCalleeCloneNo > 0)
4345 cast<CallBase>(CallerCall.call())->setCalledFunction(CalleeFunc.func());
4346 OREGetter(CallerCall.call()->getFunction())
4347 .emit(OptimizationRemark(
DEBUG_TYPE,
"MemprofCall", CallerCall.call())
4348 <<
ore::NV(
"Call", CallerCall.call()) <<
" in clone "
4349 <<
ore::NV(
"Caller", CallerCall.call()->getFunction())
4350 <<
" assigned to call function clone "
4351 <<
ore::NV(
"Callee", CalleeFunc.func()));
4354void IndexCallsiteContextGraph::updateCall(CallInfo &CallerCall,
4355 FuncInfo CalleeFunc) {
4358 "Caller cannot be an allocation which should not have profiled calls");
4359 assert(CI->Clones.size() > CallerCall.cloneNo());
4360 auto NewCalleeCloneNo = CalleeFunc.cloneNo();
4361 auto &CurCalleeCloneNo = CI->Clones[CallerCall.cloneNo()];
4366 if (CurCalleeCloneNo != 0 && CurCalleeCloneNo != NewCalleeCloneNo) {
4368 << CurCalleeCloneNo <<
" now " << NewCalleeCloneNo
4370 MismatchedCloneAssignments++;
4372 CurCalleeCloneNo = NewCalleeCloneNo;
4384 SP->replaceLinkageName(MDName);
4388 TempDISubprogram NewDecl = Decl->
clone();
4389 NewDecl->replaceLinkageName(MDName);
4393CallsiteContextGraph<ModuleCallsiteContextGraph,
Function,
4395ModuleCallsiteContextGraph::cloneFunctionForCallsite(
4396 FuncInfo &Func, CallInfo &
Call, DenseMap<CallInfo, CallInfo> &CallMap,
4397 std::vector<CallInfo> &CallsWithMetadataInFunc,
unsigned CloneNo) {
4402 assert(!
Func.func()->getParent()->getFunction(Name));
4403 NewFunc->setName(Name);
4405 for (
auto &Inst : CallsWithMetadataInFunc) {
4407 assert(Inst.cloneNo() == 0);
4410 OREGetter(
Func.func())
4411 .emit(OptimizationRemark(
DEBUG_TYPE,
"MemprofClone",
Func.func())
4412 <<
"created clone " <<
ore::NV(
"NewFunction", NewFunc));
4413 return {NewFunc, CloneNo};
4416CallsiteContextGraph<IndexCallsiteContextGraph, FunctionSummary,
4417 IndexCall>::FuncInfo
4418IndexCallsiteContextGraph::cloneFunctionForCallsite(
4419 FuncInfo &Func, CallInfo &
Call, DenseMap<CallInfo, CallInfo> &CallMap,
4420 std::vector<CallInfo> &CallsWithMetadataInFunc,
unsigned CloneNo) {
4434 for (
auto &Inst : CallsWithMetadataInFunc) {
4436 assert(Inst.cloneNo() == 0);
4438 assert(AI->Versions.size() == CloneNo);
4441 AI->Versions.push_back(0);
4444 assert(CI && CI->Clones.size() == CloneNo);
4447 CI->Clones.push_back(0);
4449 CallMap[Inst] = {Inst.call(), CloneNo};
4451 return {
Func.func(), CloneNo};
4468template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
4469void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::mergeClones() {
4475 DenseMap<uint32_t, ContextNode *> ContextIdToAllocationNode;
4476 for (
auto &Entry : AllocationCallToContextNodeMap) {
4478 for (
auto Id :
Node->getContextIds())
4479 ContextIdToAllocationNode[
Id] =
Node->getOrigNode();
4480 for (
auto *Clone :
Node->Clones) {
4481 for (
auto Id : Clone->getContextIds())
4482 ContextIdToAllocationNode[
Id] = Clone->getOrigNode();
4489 DenseSet<const ContextNode *> Visited;
4490 for (
auto &Entry : AllocationCallToContextNodeMap) {
4493 mergeClones(Node, Visited, ContextIdToAllocationNode);
4499 auto Clones =
Node->Clones;
4500 for (
auto *Clone : Clones)
4501 mergeClones(Clone, Visited, ContextIdToAllocationNode);
4505 dbgs() <<
"CCG after merging:\n";
4509 exportToDot(
"aftermerge");
4517template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
4518void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::mergeClones(
4519 ContextNode *Node, DenseSet<const ContextNode *> &Visited,
4520 DenseMap<uint32_t, ContextNode *> &ContextIdToAllocationNode) {
4530 bool FoundUnvisited =
true;
4532 while (FoundUnvisited) {
4534 FoundUnvisited =
false;
4537 auto CallerEdges =
Node->CallerEdges;
4538 for (
auto CallerEdge : CallerEdges) {
4540 if (CallerEdge->Callee != Node)
4545 FoundUnvisited =
true;
4546 mergeClones(CallerEdge->Caller, Visited, ContextIdToAllocationNode);
4550 TotalMergeInvokes++;
4551 TotalMergeIters += Iters;
4552 if (Iters > MaxMergeIters)
4553 MaxMergeIters = Iters;
4556 mergeNodeCalleeClones(Node, Visited, ContextIdToAllocationNode);
4559template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
4560void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::mergeNodeCalleeClones(
4561 ContextNode *Node, DenseSet<const ContextNode *> &Visited,
4562 DenseMap<uint32_t, ContextNode *> &ContextIdToAllocationNode) {
4564 if (
Node->emptyContextIds())
4569 MapVector<ContextNode *, std::vector<std::shared_ptr<ContextEdge>>>
4570 OrigNodeToCloneEdges;
4571 for (
const auto &
E :
Node->CalleeEdges) {
4576 OrigNodeToCloneEdges[
Base].push_back(
E);
4582 auto CalleeCallerEdgeLessThan = [](
const std::shared_ptr<ContextEdge> &
A,
4583 const std::shared_ptr<ContextEdge> &
B) {
4584 if (
A->Callee->CallerEdges.size() !=
B->Callee->CallerEdges.size())
4585 return A->Callee->CallerEdges.size() <
B->Callee->CallerEdges.size();
4586 if (
A->Callee->CloneOf && !
B->Callee->CloneOf)
4588 else if (!
A->Callee->CloneOf &&
B->Callee->CloneOf)
4592 return *
A->ContextIds.begin() < *
B->ContextIds.begin();
4597 for (
auto Entry : OrigNodeToCloneEdges) {
4600 auto &CalleeEdges =
Entry.second;
4601 auto NumCalleeClones = CalleeEdges.size();
4603 if (NumCalleeClones == 1)
4614 DenseSet<ContextNode *> OtherCallersToShareMerge;
4615 findOtherCallersToShareMerge(Node, CalleeEdges, ContextIdToAllocationNode,
4616 OtherCallersToShareMerge);
4621 ContextNode *MergeNode =
nullptr;
4622 DenseMap<ContextNode *, unsigned> CallerToMoveCount;
4623 for (
auto CalleeEdge : CalleeEdges) {
4624 auto *OrigCallee = CalleeEdge->Callee;
4630 if (CalleeEdge->Callee->CallerEdges.size() == 1) {
4631 MergeNode = OrigCallee;
4632 NonNewMergedNodes++;
4639 if (!OtherCallersToShareMerge.
empty()) {
4640 bool MoveAllCallerEdges =
true;
4641 for (
auto CalleeCallerE : OrigCallee->CallerEdges) {
4642 if (CalleeCallerE == CalleeEdge)
4644 if (!OtherCallersToShareMerge.
contains(CalleeCallerE->Caller)) {
4645 MoveAllCallerEdges =
false;
4651 if (MoveAllCallerEdges) {
4652 MergeNode = OrigCallee;
4653 NonNewMergedNodes++;
4660 assert(MergeNode != OrigCallee);
4661 moveEdgeToExistingCalleeClone(CalleeEdge, MergeNode,
4664 MergeNode = moveEdgeToNewCalleeClone(CalleeEdge);
4669 if (!OtherCallersToShareMerge.
empty()) {
4673 auto OrigCalleeCallerEdges = OrigCallee->CallerEdges;
4674 for (
auto &CalleeCallerE : OrigCalleeCallerEdges) {
4675 if (CalleeCallerE == CalleeEdge)
4677 if (!OtherCallersToShareMerge.
contains(CalleeCallerE->Caller))
4679 CallerToMoveCount[CalleeCallerE->Caller]++;
4680 moveEdgeToExistingCalleeClone(CalleeCallerE, MergeNode,
4684 removeNoneTypeCalleeEdges(OrigCallee);
4685 removeNoneTypeCalleeEdges(MergeNode);
4703template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
4704void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::
4705 findOtherCallersToShareMerge(
4707 std::vector<std::shared_ptr<ContextEdge>> &CalleeEdges,
4708 DenseMap<uint32_t, ContextNode *> &ContextIdToAllocationNode,
4709 DenseSet<ContextNode *> &OtherCallersToShareMerge) {
4710 auto NumCalleeClones = CalleeEdges.size();
4713 DenseMap<ContextNode *, unsigned> OtherCallersToSharedCalleeEdgeCount;
4716 unsigned PossibleOtherCallerNodes = 0;
4720 if (CalleeEdges[0]->
Callee->CallerEdges.size() < 2)
4726 DenseMap<ContextEdge *, DenseSet<ContextNode *>> CalleeEdgeToAllocNodes;
4727 for (
auto CalleeEdge : CalleeEdges) {
4728 assert(CalleeEdge->Callee->CallerEdges.size() > 1);
4731 for (
auto CalleeCallerEdges : CalleeEdge->Callee->CallerEdges) {
4732 if (CalleeCallerEdges->Caller == Node) {
4733 assert(CalleeCallerEdges == CalleeEdge);
4736 OtherCallersToSharedCalleeEdgeCount[CalleeCallerEdges->Caller]++;
4739 if (OtherCallersToSharedCalleeEdgeCount[CalleeCallerEdges->Caller] ==
4741 PossibleOtherCallerNodes++;
4745 for (
auto Id : CalleeEdge->getContextIds()) {
4746 auto *
Alloc = ContextIdToAllocationNode.
lookup(Id);
4750 MissingAllocForContextId++;
4753 CalleeEdgeToAllocNodes[CalleeEdge.get()].
insert(
Alloc);
4760 for (
auto CalleeEdge : CalleeEdges) {
4761 assert(CalleeEdge->Callee->CallerEdges.size() > 1);
4763 if (!PossibleOtherCallerNodes)
4765 auto &CurCalleeAllocNodes = CalleeEdgeToAllocNodes[CalleeEdge.get()];
4767 for (
auto &CalleeCallerE : CalleeEdge->Callee->CallerEdges) {
4769 if (CalleeCallerE == CalleeEdge)
4773 if (OtherCallersToSharedCalleeEdgeCount[CalleeCallerE->Caller] !=
4778 for (
auto Id : CalleeCallerE->getContextIds()) {
4779 auto *
Alloc = ContextIdToAllocationNode.
lookup(Id);
4784 if (!CurCalleeAllocNodes.contains(
Alloc)) {
4785 OtherCallersToSharedCalleeEdgeCount[CalleeCallerE->Caller] = 0;
4786 PossibleOtherCallerNodes--;
4793 if (!PossibleOtherCallerNodes)
4798 for (
auto &[OtherCaller,
Count] : OtherCallersToSharedCalleeEdgeCount) {
4799 if (
Count != NumCalleeClones)
4801 OtherCallersToShareMerge.
insert(OtherCaller);
4846template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
4847bool CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::assignFunctions() {
4854 DenseMap<ContextNode *, FuncInfo> CallsiteToCalleeFuncCloneMap;
4858 auto RecordCalleeFuncOfCallsite = [&](ContextNode *
Caller,
4859 const FuncInfo &CalleeFunc) {
4861 CallsiteToCalleeFuncCloneMap[
Caller] = CalleeFunc;
4865 struct FuncCloneInfo {
4870 DenseMap<CallInfo, CallInfo> CallMap;
4898 DenseMap<const ContextNode *, std::map<unsigned, SmallVector<CallInfo, 0>>>
4899 UnassignedCallClones;
4903 for (
auto &[Func, CallsWithMetadata] : FuncToCallsWithMetadata) {
4904 FuncInfo OrigFunc(Func);
4909 std::vector<FuncCloneInfo> FuncCloneInfos;
4910 for (
auto &
Call : CallsWithMetadata) {
4911 ContextNode *
Node = getNodeForInst(
Call);
4915 if (!Node ||
Node->Clones.empty())
4918 "Not having a call should have prevented cloning");
4922 std::map<FuncInfo, ContextNode *> FuncCloneToCurNodeCloneMap;
4926 auto AssignCallsiteCloneToFuncClone = [&](
const FuncInfo &FuncClone,
4928 ContextNode *CallsiteClone,
4931 FuncCloneToCurNodeCloneMap[FuncClone] = CallsiteClone;
4933 assert(FuncCloneInfos.size() > FuncClone.cloneNo());
4934 DenseMap<CallInfo, CallInfo> &CallMap =
4935 FuncCloneInfos[FuncClone.cloneNo()].CallMap;
4936 CallInfo CallClone(
Call);
4937 if (
auto It = CallMap.
find(
Call); It != CallMap.
end())
4938 CallClone = It->second;
4939 CallsiteClone->setCall(CallClone);
4941 for (
auto &MatchingCall :
Node->MatchingCalls) {
4942 CallInfo CallClone(MatchingCall);
4943 if (
auto It = CallMap.
find(MatchingCall); It != CallMap.
end())
4944 CallClone = It->second;
4946 MatchingCall = CallClone;
4954 auto MoveEdgeToNewCalleeCloneAndSetUp =
4955 [&](
const std::shared_ptr<ContextEdge> &
Edge) {
4956 ContextNode *OrigCallee =
Edge->Callee;
4957 ContextNode *NewClone = moveEdgeToNewCalleeClone(
Edge);
4958 removeNoneTypeCalleeEdges(NewClone);
4959 assert(NewClone->AllocTypes != (uint8_t)AllocationType::None);
4963 if (CallsiteToCalleeFuncCloneMap.
count(OrigCallee))
4964 RecordCalleeFuncOfCallsite(
4965 NewClone, CallsiteToCalleeFuncCloneMap[OrigCallee]);
4972 std::deque<ContextNode *> ClonesWorklist;
4974 if (!
Node->emptyContextIds())
4975 ClonesWorklist.push_back(Node);
4981 unsigned NodeCloneCount = 0;
4982 while (!ClonesWorklist.empty()) {
4983 ContextNode *Clone = ClonesWorklist.front();
4984 ClonesWorklist.pop_front();
4993 if (FuncCloneInfos.size() < NodeCloneCount) {
4995 if (NodeCloneCount == 1) {
5000 Clone->CallerEdges, [&](
const std::shared_ptr<ContextEdge> &
E) {
5001 return CallsiteToCalleeFuncCloneMap.count(E->Caller);
5005 FuncCloneInfos.push_back(
5006 {OrigFunc, DenseMap<CallInfo, CallInfo>()});
5007 AssignCallsiteCloneToFuncClone(
5008 OrigFunc,
Call, Clone,
5009 AllocationCallToContextNodeMap.count(
Call));
5010 for (
auto &CE : Clone->CallerEdges) {
5012 if (!
CE->Caller->hasCall())
5014 RecordCalleeFuncOfCallsite(
CE->Caller, OrigFunc);
5024 FuncInfo PreviousAssignedFuncClone;
5026 Clone->CallerEdges, [&](
const std::shared_ptr<ContextEdge> &
E) {
5027 return CallsiteToCalleeFuncCloneMap.count(E->Caller);
5029 bool CallerAssignedToCloneOfFunc =
false;
5030 if (EI != Clone->CallerEdges.end()) {
5031 const std::shared_ptr<ContextEdge> &
Edge = *EI;
5032 PreviousAssignedFuncClone =
5033 CallsiteToCalleeFuncCloneMap[
Edge->Caller];
5034 CallerAssignedToCloneOfFunc =
true;
5039 DenseMap<CallInfo, CallInfo> NewCallMap;
5040 unsigned CloneNo = FuncCloneInfos.size();
5041 assert(CloneNo > 0 &&
"Clone 0 is the original function, which "
5042 "should already exist in the map");
5043 FuncInfo NewFuncClone = cloneFunctionForCallsite(
5044 OrigFunc,
Call, NewCallMap, CallsWithMetadata, CloneNo);
5045 FuncCloneInfos.push_back({NewFuncClone, std::move(NewCallMap)});
5046 FunctionClonesAnalysis++;
5052 if (!CallerAssignedToCloneOfFunc) {
5053 AssignCallsiteCloneToFuncClone(
5054 NewFuncClone,
Call, Clone,
5055 AllocationCallToContextNodeMap.count(
Call));
5056 for (
auto &CE : Clone->CallerEdges) {
5058 if (!
CE->Caller->hasCall())
5060 RecordCalleeFuncOfCallsite(
CE->Caller, NewFuncClone);
5072 auto CallerEdges = Clone->CallerEdges;
5073 for (
auto CE : CallerEdges) {
5075 if (
CE->isRemoved()) {
5081 if (!
CE->Caller->hasCall())
5084 if (!CallsiteToCalleeFuncCloneMap.
count(
CE->Caller) ||
5088 CallsiteToCalleeFuncCloneMap[
CE->Caller] !=
5089 PreviousAssignedFuncClone)
5092 RecordCalleeFuncOfCallsite(
CE->Caller, NewFuncClone);
5105 auto CalleeEdges =
CE->Caller->CalleeEdges;
5106 for (
auto CalleeEdge : CalleeEdges) {
5109 if (CalleeEdge->isRemoved()) {
5114 ContextNode *
Callee = CalleeEdge->Callee;
5118 if (Callee == Clone || !
Callee->hasCall())
5123 if (Callee == CalleeEdge->Caller)
5125 ContextNode *NewClone =
5126 MoveEdgeToNewCalleeCloneAndSetUp(CalleeEdge);
5129 removeNoneTypeCalleeEdges(Callee);
5137 CallInfo OrigCall(
Callee->getOrigNode()->Call);
5138 OrigCall.setCloneNo(0);
5139 DenseMap<CallInfo, CallInfo> &CallMap =
5140 FuncCloneInfos[NewFuncClone.cloneNo()].CallMap;
5142 CallInfo NewCall(CallMap[OrigCall]);
5144 NewClone->setCall(NewCall);
5146 for (
auto &MatchingCall : NewClone->MatchingCalls) {
5147 CallInfo OrigMatchingCall(MatchingCall);
5148 OrigMatchingCall.setCloneNo(0);
5150 CallInfo NewCall(CallMap[OrigMatchingCall]);
5153 MatchingCall = NewCall;
5162 auto FindFirstAvailFuncClone = [&]() {
5167 for (
auto &CF : FuncCloneInfos) {
5168 if (!FuncCloneToCurNodeCloneMap.count(CF.FuncClone))
5169 return CF.FuncClone;
5172 "Expected an available func clone for this callsite clone");
5189 std::map<FuncInfo, ContextNode *> FuncCloneToNewCallsiteCloneMap;
5190 FuncInfo FuncCloneAssignedToCurCallsiteClone;
5194 auto CloneCallerEdges = Clone->CallerEdges;
5195 for (
auto &
Edge : CloneCallerEdges) {
5199 if (
Edge->isRemoved())
5202 if (!
Edge->Caller->hasCall())
5206 if (CallsiteToCalleeFuncCloneMap.
count(
Edge->Caller)) {
5207 FuncInfo FuncCloneCalledByCaller =
5208 CallsiteToCalleeFuncCloneMap[
Edge->Caller];
5218 if ((FuncCloneToCurNodeCloneMap.count(FuncCloneCalledByCaller) &&
5219 FuncCloneToCurNodeCloneMap[FuncCloneCalledByCaller] !=
5227 (FuncCloneAssignedToCurCallsiteClone &&
5228 FuncCloneAssignedToCurCallsiteClone !=
5229 FuncCloneCalledByCaller)) {
5244 if (FuncCloneToNewCallsiteCloneMap.count(
5245 FuncCloneCalledByCaller)) {
5246 ContextNode *NewClone =
5247 FuncCloneToNewCallsiteCloneMap[FuncCloneCalledByCaller];
5248 moveEdgeToExistingCalleeClone(
Edge, NewClone);
5250 removeNoneTypeCalleeEdges(NewClone);
5253 ContextNode *NewClone = MoveEdgeToNewCalleeCloneAndSetUp(
Edge);
5254 FuncCloneToNewCallsiteCloneMap[FuncCloneCalledByCaller] =
5257 ClonesWorklist.push_back(NewClone);
5261 removeNoneTypeCalleeEdges(Clone);
5269 if (!FuncCloneAssignedToCurCallsiteClone) {
5270 FuncCloneAssignedToCurCallsiteClone = FuncCloneCalledByCaller;
5272 AssignCallsiteCloneToFuncClone(
5273 FuncCloneCalledByCaller,
Call, Clone,
5274 AllocationCallToContextNodeMap.count(
Call));
5278 assert(FuncCloneAssignedToCurCallsiteClone ==
5279 FuncCloneCalledByCaller);
5288 if (!FuncCloneAssignedToCurCallsiteClone) {
5289 FuncCloneAssignedToCurCallsiteClone = FindFirstAvailFuncClone();
5290 assert(FuncCloneAssignedToCurCallsiteClone);
5292 AssignCallsiteCloneToFuncClone(
5293 FuncCloneAssignedToCurCallsiteClone,
Call, Clone,
5294 AllocationCallToContextNodeMap.count(
Call));
5296 assert(FuncCloneToCurNodeCloneMap
5297 [FuncCloneAssignedToCurCallsiteClone] == Clone);
5299 RecordCalleeFuncOfCallsite(
Edge->Caller,
5300 FuncCloneAssignedToCurCallsiteClone);
5320 if (!FuncCloneAssignedToCurCallsiteClone) {
5321 FuncCloneAssignedToCurCallsiteClone = FindFirstAvailFuncClone();
5322 assert(FuncCloneAssignedToCurCallsiteClone &&
5323 "No available func clone for this callsite clone");
5324 AssignCallsiteCloneToFuncClone(
5325 FuncCloneAssignedToCurCallsiteClone,
Call, Clone,
5326 AllocationCallToContextNodeMap.contains(
Call));
5331 for (
const auto &PE :
Node->CalleeEdges)
5333 for (
const auto &CE :
Node->CallerEdges)
5335 for (
auto *Clone :
Node->Clones) {
5337 for (
const auto &PE : Clone->CalleeEdges)
5339 for (
const auto &CE : Clone->CallerEdges)
5345 if (FuncCloneInfos.size() < 2)
5351 for (
auto &
Call : CallsWithMetadata) {
5352 ContextNode *
Node = getNodeForInst(
Call);
5353 if (!Node || !
Node->hasCall() ||
Node->emptyContextIds())
5359 if (
Node->Clones.size() + 1 >= FuncCloneInfos.size())
5363 DenseSet<unsigned> NodeCallClones;
5364 for (
auto *
C :
Node->Clones)
5365 NodeCallClones.
insert(
C->Call.cloneNo());
5368 for (
auto &FC : FuncCloneInfos) {
5373 if (++
I == 1 || NodeCallClones.
contains(
I)) {
5378 auto &CallVector = UnassignedCallClones[
Node][
I];
5379 DenseMap<CallInfo, CallInfo> &CallMap =
FC.CallMap;
5380 if (
auto It = CallMap.
find(
Call); It != CallMap.
end()) {
5381 CallInfo CallClone = It->second;
5382 CallVector.push_back(CallClone);
5386 assert(
false &&
"Expected to find call in CallMap");
5389 for (
auto &MatchingCall :
Node->MatchingCalls) {
5390 if (
auto It = CallMap.
find(MatchingCall); It != CallMap.
end()) {
5391 CallInfo CallClone = It->second;
5392 CallVector.push_back(CallClone);
5396 assert(
false &&
"Expected to find call in CallMap");
5404 (uint8_t)AllocationType::Cold | (uint8_t)AllocationType::NotCold;
5406 auto UpdateCalls = [&](ContextNode *
Node,
5407 DenseSet<const ContextNode *> &Visited,
5408 auto &&UpdateCalls) {
5409 auto Inserted = Visited.insert(Node);
5413 for (
auto *Clone :
Node->Clones)
5414 UpdateCalls(Clone, Visited, UpdateCalls);
5416 for (
auto &
Edge :
Node->CallerEdges)
5417 UpdateCalls(
Edge->Caller, Visited, UpdateCalls);
5421 if (!
Node->hasCall() ||
Node->emptyContextIds())
5424 if (
Node->IsAllocation) {
5425 auto AT = allocTypeToUse(
Node->AllocTypes);
5431 !ContextIdToContextSizeInfos.empty()) {
5434 for (
auto Id :
Node->getContextIds()) {
5435 auto TypeI = ContextIdToAllocationType.find(Id);
5436 assert(TypeI != ContextIdToAllocationType.end());
5437 auto CSI = ContextIdToContextSizeInfos.find(Id);
5438 if (CSI != ContextIdToContextSizeInfos.end()) {
5439 for (
auto &Info : CSI->second) {
5441 if (TypeI->second == AllocationType::Cold)
5442 TotalCold +=
Info.TotalSize;
5447 AT = AllocationType::Cold;
5449 updateAllocationCall(
Node->Call, AT);
5454 if (!CallsiteToCalleeFuncCloneMap.
count(Node))
5457 auto CalleeFunc = CallsiteToCalleeFuncCloneMap[
Node];
5458 updateCall(
Node->Call, CalleeFunc);
5460 for (
auto &
Call :
Node->MatchingCalls)
5461 updateCall(
Call, CalleeFunc);
5465 if (!UnassignedCallClones.
contains(Node))
5467 DenseSet<unsigned> NodeCallClones;
5468 for (
auto *
C :
Node->Clones)
5469 NodeCallClones.
insert(
C->Call.cloneNo());
5471 auto &ClonedCalls = UnassignedCallClones[
Node];
5472 for (
auto &[CloneNo, CallVector] : ClonedCalls) {
5476 if (NodeCallClones.
contains(CloneNo))
5479 for (
auto &
Call : CallVector)
5480 updateCall(
Call, CalleeFunc);
5489 DenseSet<const ContextNode *> Visited;
5490 for (
auto &Entry : AllocationCallToContextNodeMap)
5491 UpdateCalls(
Entry.second, Visited, UpdateCalls);
5502 for (
auto &SN : FS->callsites()) {
5507 SN.Clones.size() >
I &&
5508 "Callsite summary has fewer entries than other summaries in function");
5509 if (SN.Clones.size() <=
I || !SN.Clones[
I])
5516 for (
auto &AN : FS->allocs()) {
5520 assert(AN.Versions.size() >
I &&
5521 "Alloc summary has fewer entries than other summaries in function");
5522 if (AN.Versions.size() <=
I ||
5539 NewGV->takeName(DeclGV);
5546 auto CloneFuncAliases = [&](
Function *NewF,
unsigned I) {
5547 if (!FuncToAliasMap.count(&
F))
5549 for (
auto *
A : FuncToAliasMap[&
F]) {
5551 auto *PrevA = M.getNamedAlias(AliasName);
5553 A->getType()->getPointerAddressSpace(),
5554 A->getLinkage(), AliasName, NewF);
5555 NewA->copyAttributesFrom(
A);
5557 TakeDeclNameAndReplace(PrevA, NewA);
5566 FunctionsClonedThinBackend++;
5583 for (
unsigned I = 1;
I < NumClones;
I++) {
5584 VMaps.
emplace_back(std::make_unique<ValueToValueMapTy>());
5591 FunctionCloneDuplicatesThinBackend++;
5592 auto *Func = HashToFunc[Hash];
5593 if (Func->hasAvailableExternallyLinkage()) {
5599 auto Decl = M.getOrInsertFunction(Name, Func->getFunctionType());
5601 <<
"created clone decl " <<
ore::NV(
"Decl", Decl.getCallee()));
5604 auto *PrevF = M.getFunction(Name);
5607 TakeDeclNameAndReplace(PrevF, Alias);
5609 <<
"created clone alias " <<
ore::NV(
"Alias", Alias));
5612 CloneFuncAliases(Func,
I);
5616 HashToFunc[Hash] = NewF;
5617 FunctionClonesThinBackend++;
5620 for (
auto &BB : *NewF) {
5621 for (
auto &Inst : BB) {
5622 Inst.setMetadata(LLVMContext::MD_memprof,
nullptr);
5623 Inst.setMetadata(LLVMContext::MD_callsite,
nullptr);
5628 TakeDeclNameAndReplace(PrevF, NewF);
5630 NewF->setName(Name);
5633 <<
"created clone " <<
ore::NV(
"NewFunction", NewF));
5636 CloneFuncAliases(NewF,
I);
5645 const Function *CallingFunc =
nullptr) {
5664 auto SrcFileMD =
F.getMetadata(
"thinlto_src_file");
5670 if (!SrcFileMD &&
F.isDeclaration()) {
5674 SrcFileMD = CallingFunc->getMetadata(
"thinlto_src_file");
5679 assert(SrcFileMD || OrigName ==
F.getName());
5681 StringRef SrcFile = M.getSourceFileName();
5693 if (!TheFnVI && OrigName ==
F.getName() &&
F.hasLocalLinkage() &&
5694 F.getName().contains(
'.')) {
5695 OrigName =
F.getName().rsplit(
'.').first;
5704 assert(TheFnVI ||
F.isDeclaration());
5708bool MemProfContextDisambiguation::initializeIndirectCallPromotionInfo(
5710 ICallAnalysis = std::make_unique<ICallPromotionAnalysis>();
5711 Symtab = std::make_unique<InstrProfSymtab>();
5722 if (
Error E = Symtab->create(M,
true,
false)) {
5723 std::string SymtabFailure =
toString(std::move(
E));
5724 M.getContext().emitError(
"Failed to create symtab: " + SymtabFailure);
5737 auto MIBIter = AllocNode.
MIBs.begin();
5738 for (
auto &MDOp : MemProfMD->
operands()) {
5740 auto StackIdIndexIter = MIBIter->StackIdIndices.begin();
5745 auto ContextIterBegin =
5749 (ContextIterBegin != StackContext.
end() && *ContextIterBegin == 0) ? 1
5751 for (
auto ContextIter = ContextIterBegin; ContextIter != StackContext.
end();
5756 if (LastStackContextId == *ContextIter)
5758 LastStackContextId = *ContextIter;
5759 assert(StackIdIndexIter != MIBIter->StackIdIndices.end());
5769bool MemProfContextDisambiguation::applyImport(
Module &M) {
5776 std::map<const Function *, SmallPtrSet<const GlobalAlias *, 1>>
5778 for (
auto &
A :
M.aliases()) {
5779 auto *Aliasee =
A.getAliaseeObject();
5781 FuncToAliasMap[
F].insert(&
A);
5784 if (!initializeIndirectCallPromotionInfo(M))
5791 OptimizationRemarkEmitter ORE(&
F);
5794 bool ClonesCreated =
false;
5795 unsigned NumClonesCreated = 0;
5796 auto CloneFuncIfNeeded = [&](
unsigned NumClones, FunctionSummary *
FS) {
5806 if (ClonesCreated) {
5807 assert(NumClonesCreated == NumClones);
5814 ClonesCreated =
true;
5815 NumClonesCreated = NumClones;
5818 auto CloneCallsite = [&](
const CallsiteInfo &StackNode, CallBase *CB,
5819 Function *CalledFunction, FunctionSummary *
FS) {
5821 CloneFuncIfNeeded(StackNode.
Clones.
size(), FS);
5833 if (CalledFunction != CB->getCalledOperand() &&
5834 (!GA || CalledFunction != GA->getAliaseeObject())) {
5835 SkippedCallsCloning++;
5841 auto CalleeOrigName = CalledFunction->getName();
5842 for (
unsigned J = 0; J < StackNode.
Clones.
size(); J++) {
5845 if (J > 0 && VMaps[J - 1]->
empty())
5849 if (!StackNode.
Clones[J])
5851 auto NewF =
M.getOrInsertFunction(
5853 CalledFunction->getFunctionType());
5867 ORE.emit(OptimizationRemark(
DEBUG_TYPE,
"MemprofCall", CBClone)
5868 <<
ore::NV(
"Call", CBClone) <<
" in clone "
5870 <<
" assigned to call function clone "
5871 <<
ore::NV(
"Callee", NewF.getCallee()));
5885 ImportSummary->findSummaryInModule(TheFnVI,
M.getModuleIdentifier());
5889 auto SrcModuleMD =
F.getMetadata(
"thinlto_src_module");
5891 "enable-import-metadata is needed to emit thinlto_src_module");
5892 StringRef SrcModule =
5895 if (GVS->modulePath() == SrcModule) {
5896 GVSummary = GVS.get();
5921 if (
FS->allocs().empty() &&
FS->callsites().empty())
5924 auto SI =
FS->callsites().begin();
5925 auto AI =
FS->allocs().begin();
5930 DenseMap<ValueInfo, CallsiteInfo> MapTailCallCalleeVIToCallsite;
5933 for (
auto CallsiteIt =
FS->callsites().rbegin();
5934 CallsiteIt !=
FS->callsites().rend(); CallsiteIt++) {
5935 auto &Callsite = *CallsiteIt;
5939 if (!Callsite.StackIdIndices.empty())
5941 MapTailCallCalleeVIToCallsite.
insert({Callsite.Callee, Callsite});
5950 for (
auto &BB :
F) {
5951 for (
auto &
I : BB) {
5957 auto *CalledValue = CB->getCalledOperand();
5958 auto *CalledFunction = CB->getCalledFunction();
5959 if (CalledValue && !CalledFunction) {
5960 CalledValue = CalledValue->stripPointerCasts();
5967 assert(!CalledFunction &&
5968 "Expected null called function in callsite for alias");
5972 CallStack<MDNode, MDNode::op_iterator> CallsiteContext(
5973 I.getMetadata(LLVMContext::MD_callsite));
5974 auto *MemProfMD =
I.getMetadata(LLVMContext::MD_memprof);
5980 if (CB->getAttributes().hasFnAttr(
"memprof") && !MemProfMD) {
5981 CB->getAttributes().getFnAttr(
"memprof").getValueAsString() ==
"cold"
5982 ? AllocTypeColdThinBackend++
5983 : AllocTypeNotColdThinBackend++;
5984 OrigAllocsThinBackend++;
5985 AllocVersionsThinBackend++;
5986 if (!MaxAllocVersionsThinBackend)
5987 MaxAllocVersionsThinBackend = 1;
5994 auto &AllocNode = *(AI++);
6002 CloneFuncIfNeeded(AllocNode.Versions.size(), FS);
6004 OrigAllocsThinBackend++;
6005 AllocVersionsThinBackend += AllocNode.Versions.size();
6006 if (MaxAllocVersionsThinBackend < AllocNode.Versions.size())
6007 MaxAllocVersionsThinBackend = AllocNode.Versions.size();
6017 if (AllocNode.Versions.size() == 1 &&
6020 AllocationType::NotCold ||
6022 AllocationType::None);
6023 UnclonableAllocsThinBackend++;
6029 return Type == ((uint8_t)AllocationType::NotCold |
6030 (uint8_t)AllocationType::Cold);
6034 for (
unsigned J = 0; J < AllocNode.Versions.size(); J++) {
6037 if (J > 0 && VMaps[J - 1]->
empty())
6040 if (AllocNode.Versions[J] == (uint8_t)AllocationType::None)
6043 AllocTy == AllocationType::Cold ? AllocTypeColdThinBackend++
6044 : AllocTypeNotColdThinBackend++;
6059 ORE.emit(OptimizationRemark(
DEBUG_TYPE,
"MemprofAttribute", CBClone)
6060 <<
ore::NV(
"AllocationCall", CBClone) <<
" in clone "
6062 <<
" marked with memprof allocation attribute "
6063 <<
ore::NV(
"Attribute", AllocTypeString));
6065 }
else if (!CallsiteContext.empty()) {
6066 if (!CalledFunction) {
6070 assert(!CI || !CI->isInlineAsm());
6080 recordICPInfo(CB,
FS->callsites(), SI, ICallAnalysisInfo);
6086 CloneFuncIfNeeded(NumClones, FS);
6091 assert(SI !=
FS->callsites().end());
6092 auto &StackNode = *(
SI++);
6098 for (
auto StackId : CallsiteContext) {
6100 assert(ImportSummary->getStackIdAtIndex(*StackIdIndexIter) ==
6106 CloneCallsite(StackNode, CB, CalledFunction, FS);
6108 }
else if (CB->isTailCall() && CalledFunction) {
6111 ValueInfo CalleeVI =
6113 if (CalleeVI && MapTailCallCalleeVIToCallsite.
count(CalleeVI)) {
6114 auto Callsite = MapTailCallCalleeVIToCallsite.
find(CalleeVI);
6115 assert(Callsite != MapTailCallCalleeVIToCallsite.
end());
6116 CloneCallsite(Callsite->second, CB, CalledFunction, FS);
6123 performICP(M,
FS->callsites(), VMaps, ICallAnalysisInfo, ORE);
6133 for (
auto &BB :
F) {
6134 for (
auto &
I : BB) {
6137 I.setMetadata(LLVMContext::MD_memprof,
nullptr);
6138 I.setMetadata(LLVMContext::MD_callsite,
nullptr);
6146unsigned MemProfContextDisambiguation::recordICPInfo(
6151 uint32_t NumCandidates;
6153 auto CandidateProfileData =
6154 ICallAnalysis->getPromotionCandidatesForInstruction(
6156 if (CandidateProfileData.empty())
6162 bool ICPNeeded =
false;
6163 unsigned NumClones = 0;
6164 size_t CallsiteInfoStartIndex = std::distance(AllCallsites.
begin(), SI);
6165 for (
const auto &Candidate : CandidateProfileData) {
6167 auto CalleeValueInfo =
6169 ImportSummary->getValueInfo(Candidate.Value);
6172 assert(!CalleeValueInfo ||
SI->Callee == CalleeValueInfo);
6174 auto &StackNode = *(
SI++);
6179 [](
unsigned CloneNo) { return CloneNo != 0; });
6189 ICallAnalysisInfo.
push_back({CB, CandidateProfileData.vec(), NumCandidates,
6190 TotalCount, CallsiteInfoStartIndex});
6194void MemProfContextDisambiguation::performICP(
6196 ArrayRef<std::unique_ptr<ValueToValueMapTy>> VMaps,
6198 OptimizationRemarkEmitter &ORE) {
6205 for (
auto &Info : ICallAnalysisInfo) {
6208 auto TotalCount =
Info.TotalCount;
6209 unsigned NumClones = 0;
6212 for (
auto &Candidate :
Info.CandidateProfileData) {
6223 Function *TargetFunction = Symtab->getFunction(Candidate.Value);
6224 if (TargetFunction ==
nullptr ||
6232 return OptimizationRemarkMissed(
DEBUG_TYPE,
"UnableToFindTarget", CB)
6233 <<
"Memprof cannot promote indirect call: target with md5sum "
6234 <<
ore::NV(
"target md5sum", Candidate.Value) <<
" not found";
6239 RemainingCandidates.
push_back(Candidate);
6244 const char *Reason =
nullptr;
6247 return OptimizationRemarkMissed(
DEBUG_TYPE,
"UnableToPromote", CB)
6248 <<
"Memprof cannot promote indirect call to "
6249 <<
ore::NV(
"TargetFunction", TargetFunction)
6250 <<
" with count of " <<
ore::NV(
"TotalCount", TotalCount)
6253 RemainingCandidates.
push_back(Candidate);
6262 CallBase *CBClone = CB;
6263 for (
unsigned J = 0; J < NumClones; J++) {
6266 if (J > 0 && VMaps[J - 1]->
empty())
6276 TotalCount, isSamplePGO, &ORE);
6277 auto *TargetToUse = TargetFunction;
6280 if (StackNode.
Clones[J]) {
6299 <<
ore::NV(
"Call", CBClone) <<
" in clone "
6301 <<
" promoted and assigned to call function clone "
6302 <<
ore::NV(
"Callee", TargetToUse));
6306 TotalCount -= Candidate.Count;
6310 CallBase *CBClone = CB;
6311 for (
unsigned J = 0; J < NumClones; J++) {
6314 if (J > 0 && VMaps[J - 1]->
empty())
6320 CBClone->
setMetadata(LLVMContext::MD_prof,
nullptr);
6323 if (TotalCount != 0)
6325 IPVK_IndirectCallTarget,
Info.NumCandidates);
6330template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
6331bool CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::process(
6332 function_ref<
void(StringRef, StringRef,
const Twine &)> EmitRemark,
6333 bool AllowExtraAnalysis) {
6335 dbgs() <<
"CCG before cloning:\n";
6339 exportToDot(
"postbuild");
6352 dbgs() <<
"CCG after cloning:\n";
6356 exportToDot(
"cloned");
6358 bool Changed = assignFunctions();
6361 dbgs() <<
"CCG after assigning function clones:\n";
6365 exportToDot(
"clonefuncassign");
6368 printTotalSizes(
errs(), EmitRemark);
6373bool MemProfContextDisambiguation::processModule(
6375 llvm::function_ref<OptimizationRemarkEmitter &(
Function *)> OREGetter) {
6380 return applyImport(M);
6393 ModuleCallsiteContextGraph CCG(M, OREGetter);
6396 return CCG.process();
6401 : ImportSummary(Summary), isSamplePGO(isSamplePGO) {
6406 "-memprof-dot-scope=alloc requires -memprof-dot-alloc-id");
6410 "-memprof-dot-scope=context requires -memprof-dot-context-id");
6414 "-memprof-dot-scope=all can't have both -memprof-dot-alloc-id and "
6415 "-memprof-dot-context-id");
6416 if (ImportSummary) {
6426 auto ReadSummaryFile =
6428 if (!ReadSummaryFile) {
6435 if (!ImportSummaryForTestingOrErr) {
6441 ImportSummaryForTesting = std::move(*ImportSummaryForTestingOrErr);
6442 ImportSummary = ImportSummaryForTesting.get();
6451 if (!processModule(M, OREGetter))
6470 bool AllowExtraAnalysis =
6473 IndexCallsiteContextGraph CCG(Index, isPrevailing);
6474 CCG.process(EmitRemark, AllowExtraAnalysis);
6489 for (
auto &BB :
F) {
6490 for (
auto &
I : BB) {
6494 if (CI->hasFnAttr(
"memprof")) {
6495 CI->removeFnAttr(
"memprof");
6498 if (!CI->hasMetadata(LLVMContext::MD_callsite)) {
6499 assert(!CI->hasMetadata(LLVMContext::MD_memprof));
6505 CI->setMetadata(LLVMContext::MD_memprof,
nullptr);
6506 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)
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...