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->Caller->NodeId < B->Caller->NodeId;
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)
4591 return A->Callee->NodeId <
B->Callee->NodeId;
4596 for (
auto Entry : OrigNodeToCloneEdges) {
4599 auto &CalleeEdges =
Entry.second;
4600 auto NumCalleeClones = CalleeEdges.size();
4602 if (NumCalleeClones == 1)
4613 DenseSet<ContextNode *> OtherCallersToShareMerge;
4614 findOtherCallersToShareMerge(Node, CalleeEdges, ContextIdToAllocationNode,
4615 OtherCallersToShareMerge);
4620 ContextNode *MergeNode =
nullptr;
4621 DenseMap<ContextNode *, unsigned> CallerToMoveCount;
4622 for (
auto CalleeEdge : CalleeEdges) {
4623 auto *OrigCallee = CalleeEdge->Callee;
4629 if (CalleeEdge->Callee->CallerEdges.size() == 1) {
4630 MergeNode = OrigCallee;
4631 NonNewMergedNodes++;
4638 if (!OtherCallersToShareMerge.
empty()) {
4639 bool MoveAllCallerEdges =
true;
4640 for (
auto CalleeCallerE : OrigCallee->CallerEdges) {
4641 if (CalleeCallerE == CalleeEdge)
4643 if (!OtherCallersToShareMerge.
contains(CalleeCallerE->Caller)) {
4644 MoveAllCallerEdges =
false;
4650 if (MoveAllCallerEdges) {
4651 MergeNode = OrigCallee;
4652 NonNewMergedNodes++;
4659 assert(MergeNode != OrigCallee);
4660 moveEdgeToExistingCalleeClone(CalleeEdge, MergeNode,
4663 MergeNode = moveEdgeToNewCalleeClone(CalleeEdge);
4668 if (!OtherCallersToShareMerge.
empty()) {
4672 auto OrigCalleeCallerEdges = OrigCallee->CallerEdges;
4673 for (
auto &CalleeCallerE : OrigCalleeCallerEdges) {
4674 if (CalleeCallerE == CalleeEdge)
4676 if (!OtherCallersToShareMerge.
contains(CalleeCallerE->Caller))
4678 CallerToMoveCount[CalleeCallerE->Caller]++;
4679 moveEdgeToExistingCalleeClone(CalleeCallerE, MergeNode,
4683 removeNoneTypeCalleeEdges(OrigCallee);
4684 removeNoneTypeCalleeEdges(MergeNode);
4702template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
4703void CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::
4704 findOtherCallersToShareMerge(
4706 std::vector<std::shared_ptr<ContextEdge>> &CalleeEdges,
4707 DenseMap<uint32_t, ContextNode *> &ContextIdToAllocationNode,
4708 DenseSet<ContextNode *> &OtherCallersToShareMerge) {
4709 auto NumCalleeClones = CalleeEdges.size();
4712 DenseMap<ContextNode *, unsigned> OtherCallersToSharedCalleeEdgeCount;
4715 unsigned PossibleOtherCallerNodes = 0;
4719 if (CalleeEdges[0]->
Callee->CallerEdges.size() < 2)
4725 DenseMap<ContextEdge *, DenseSet<ContextNode *>> CalleeEdgeToAllocNodes;
4726 for (
auto CalleeEdge : CalleeEdges) {
4727 assert(CalleeEdge->Callee->CallerEdges.size() > 1);
4730 for (
auto CalleeCallerEdges : CalleeEdge->Callee->CallerEdges) {
4731 if (CalleeCallerEdges->Caller == Node) {
4732 assert(CalleeCallerEdges == CalleeEdge);
4735 OtherCallersToSharedCalleeEdgeCount[CalleeCallerEdges->Caller]++;
4738 if (OtherCallersToSharedCalleeEdgeCount[CalleeCallerEdges->Caller] ==
4740 PossibleOtherCallerNodes++;
4744 for (
auto Id : CalleeEdge->getContextIds()) {
4745 auto *
Alloc = ContextIdToAllocationNode.
lookup(Id);
4749 MissingAllocForContextId++;
4752 CalleeEdgeToAllocNodes[CalleeEdge.get()].
insert(
Alloc);
4759 for (
auto CalleeEdge : CalleeEdges) {
4760 assert(CalleeEdge->Callee->CallerEdges.size() > 1);
4762 if (!PossibleOtherCallerNodes)
4764 auto &CurCalleeAllocNodes = CalleeEdgeToAllocNodes[CalleeEdge.get()];
4766 for (
auto &CalleeCallerE : CalleeEdge->Callee->CallerEdges) {
4768 if (CalleeCallerE == CalleeEdge)
4772 if (OtherCallersToSharedCalleeEdgeCount[CalleeCallerE->Caller] !=
4777 for (
auto Id : CalleeCallerE->getContextIds()) {
4778 auto *
Alloc = ContextIdToAllocationNode.
lookup(Id);
4783 if (!CurCalleeAllocNodes.contains(
Alloc)) {
4784 OtherCallersToSharedCalleeEdgeCount[CalleeCallerE->Caller] = 0;
4785 PossibleOtherCallerNodes--;
4792 if (!PossibleOtherCallerNodes)
4797 for (
auto &[OtherCaller,
Count] : OtherCallersToSharedCalleeEdgeCount) {
4798 if (
Count != NumCalleeClones)
4800 OtherCallersToShareMerge.
insert(OtherCaller);
4845template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
4846bool CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::assignFunctions() {
4853 DenseMap<ContextNode *, FuncInfo> CallsiteToCalleeFuncCloneMap;
4857 auto RecordCalleeFuncOfCallsite = [&](ContextNode *
Caller,
4858 const FuncInfo &CalleeFunc) {
4860 CallsiteToCalleeFuncCloneMap[
Caller] = CalleeFunc;
4864 struct FuncCloneInfo {
4869 DenseMap<CallInfo, CallInfo> CallMap;
4897 DenseMap<const ContextNode *, std::map<unsigned, SmallVector<CallInfo, 0>>>
4898 UnassignedCallClones;
4902 for (
auto &[Func, CallsWithMetadata] : FuncToCallsWithMetadata) {
4903 FuncInfo OrigFunc(Func);
4908 std::vector<FuncCloneInfo> FuncCloneInfos;
4909 for (
auto &
Call : CallsWithMetadata) {
4910 ContextNode *
Node = getNodeForInst(
Call);
4914 if (!Node ||
Node->Clones.empty())
4917 "Not having a call should have prevented cloning");
4921 std::map<FuncInfo, ContextNode *> FuncCloneToCurNodeCloneMap;
4925 auto AssignCallsiteCloneToFuncClone = [&](
const FuncInfo &FuncClone,
4927 ContextNode *CallsiteClone,
4930 FuncCloneToCurNodeCloneMap[FuncClone] = CallsiteClone;
4932 assert(FuncCloneInfos.size() > FuncClone.cloneNo());
4933 DenseMap<CallInfo, CallInfo> &CallMap =
4934 FuncCloneInfos[FuncClone.cloneNo()].CallMap;
4935 CallInfo CallClone(
Call);
4936 if (
auto It = CallMap.
find(
Call); It != CallMap.
end())
4937 CallClone = It->second;
4938 CallsiteClone->setCall(CallClone);
4940 for (
auto &MatchingCall :
Node->MatchingCalls) {
4941 CallInfo CallClone(MatchingCall);
4942 if (
auto It = CallMap.
find(MatchingCall); It != CallMap.
end())
4943 CallClone = It->second;
4945 MatchingCall = CallClone;
4953 auto MoveEdgeToNewCalleeCloneAndSetUp =
4954 [&](
const std::shared_ptr<ContextEdge> &
Edge) {
4955 ContextNode *OrigCallee =
Edge->Callee;
4956 ContextNode *NewClone = moveEdgeToNewCalleeClone(
Edge);
4957 removeNoneTypeCalleeEdges(NewClone);
4958 assert(NewClone->AllocTypes != (uint8_t)AllocationType::None);
4962 if (CallsiteToCalleeFuncCloneMap.
count(OrigCallee))
4963 RecordCalleeFuncOfCallsite(
4964 NewClone, CallsiteToCalleeFuncCloneMap[OrigCallee]);
4971 std::deque<ContextNode *> ClonesWorklist;
4973 if (!
Node->emptyContextIds())
4974 ClonesWorklist.push_back(Node);
4980 unsigned NodeCloneCount = 0;
4981 while (!ClonesWorklist.empty()) {
4982 ContextNode *Clone = ClonesWorklist.front();
4983 ClonesWorklist.pop_front();
4992 if (FuncCloneInfos.size() < NodeCloneCount) {
4994 if (NodeCloneCount == 1) {
4999 Clone->CallerEdges, [&](
const std::shared_ptr<ContextEdge> &
E) {
5000 return CallsiteToCalleeFuncCloneMap.count(E->Caller);
5004 FuncCloneInfos.push_back(
5005 {OrigFunc, DenseMap<CallInfo, CallInfo>()});
5006 AssignCallsiteCloneToFuncClone(
5007 OrigFunc,
Call, Clone,
5008 AllocationCallToContextNodeMap.count(
Call));
5009 for (
auto &CE : Clone->CallerEdges) {
5011 if (!
CE->Caller->hasCall())
5013 RecordCalleeFuncOfCallsite(
CE->Caller, OrigFunc);
5023 FuncInfo PreviousAssignedFuncClone;
5025 Clone->CallerEdges, [&](
const std::shared_ptr<ContextEdge> &
E) {
5026 return CallsiteToCalleeFuncCloneMap.count(E->Caller);
5028 bool CallerAssignedToCloneOfFunc =
false;
5029 if (EI != Clone->CallerEdges.end()) {
5030 const std::shared_ptr<ContextEdge> &
Edge = *EI;
5031 PreviousAssignedFuncClone =
5032 CallsiteToCalleeFuncCloneMap[
Edge->Caller];
5033 CallerAssignedToCloneOfFunc =
true;
5038 DenseMap<CallInfo, CallInfo> NewCallMap;
5039 unsigned CloneNo = FuncCloneInfos.size();
5040 assert(CloneNo > 0 &&
"Clone 0 is the original function, which "
5041 "should already exist in the map");
5042 FuncInfo NewFuncClone = cloneFunctionForCallsite(
5043 OrigFunc,
Call, NewCallMap, CallsWithMetadata, CloneNo);
5044 FuncCloneInfos.push_back({NewFuncClone, std::move(NewCallMap)});
5045 FunctionClonesAnalysis++;
5051 if (!CallerAssignedToCloneOfFunc) {
5052 AssignCallsiteCloneToFuncClone(
5053 NewFuncClone,
Call, Clone,
5054 AllocationCallToContextNodeMap.count(
Call));
5055 for (
auto &CE : Clone->CallerEdges) {
5057 if (!
CE->Caller->hasCall())
5059 RecordCalleeFuncOfCallsite(
CE->Caller, NewFuncClone);
5071 auto CallerEdges = Clone->CallerEdges;
5072 for (
auto CE : CallerEdges) {
5074 if (
CE->isRemoved()) {
5080 if (!
CE->Caller->hasCall())
5083 if (!CallsiteToCalleeFuncCloneMap.
count(
CE->Caller) ||
5087 CallsiteToCalleeFuncCloneMap[
CE->Caller] !=
5088 PreviousAssignedFuncClone)
5091 RecordCalleeFuncOfCallsite(
CE->Caller, NewFuncClone);
5104 auto CalleeEdges =
CE->Caller->CalleeEdges;
5105 for (
auto CalleeEdge : CalleeEdges) {
5108 if (CalleeEdge->isRemoved()) {
5113 ContextNode *
Callee = CalleeEdge->Callee;
5117 if (Callee == Clone || !
Callee->hasCall())
5122 if (Callee == CalleeEdge->Caller)
5124 ContextNode *NewClone =
5125 MoveEdgeToNewCalleeCloneAndSetUp(CalleeEdge);
5128 removeNoneTypeCalleeEdges(Callee);
5136 CallInfo OrigCall(
Callee->getOrigNode()->Call);
5137 OrigCall.setCloneNo(0);
5138 DenseMap<CallInfo, CallInfo> &CallMap =
5139 FuncCloneInfos[NewFuncClone.cloneNo()].CallMap;
5141 CallInfo NewCall(CallMap[OrigCall]);
5143 NewClone->setCall(NewCall);
5145 for (
auto &MatchingCall : NewClone->MatchingCalls) {
5146 CallInfo OrigMatchingCall(MatchingCall);
5147 OrigMatchingCall.setCloneNo(0);
5149 CallInfo NewCall(CallMap[OrigMatchingCall]);
5152 MatchingCall = NewCall;
5161 auto FindFirstAvailFuncClone = [&]() {
5166 for (
auto &CF : FuncCloneInfos) {
5167 if (!FuncCloneToCurNodeCloneMap.count(CF.FuncClone))
5168 return CF.FuncClone;
5171 "Expected an available func clone for this callsite clone");
5188 std::map<FuncInfo, ContextNode *> FuncCloneToNewCallsiteCloneMap;
5189 FuncInfo FuncCloneAssignedToCurCallsiteClone;
5193 auto CloneCallerEdges = Clone->CallerEdges;
5194 for (
auto &
Edge : CloneCallerEdges) {
5198 if (
Edge->isRemoved())
5201 if (!
Edge->Caller->hasCall())
5205 if (CallsiteToCalleeFuncCloneMap.
count(
Edge->Caller)) {
5206 FuncInfo FuncCloneCalledByCaller =
5207 CallsiteToCalleeFuncCloneMap[
Edge->Caller];
5217 if ((FuncCloneToCurNodeCloneMap.count(FuncCloneCalledByCaller) &&
5218 FuncCloneToCurNodeCloneMap[FuncCloneCalledByCaller] !=
5226 (FuncCloneAssignedToCurCallsiteClone &&
5227 FuncCloneAssignedToCurCallsiteClone !=
5228 FuncCloneCalledByCaller)) {
5243 if (FuncCloneToNewCallsiteCloneMap.count(
5244 FuncCloneCalledByCaller)) {
5245 ContextNode *NewClone =
5246 FuncCloneToNewCallsiteCloneMap[FuncCloneCalledByCaller];
5247 moveEdgeToExistingCalleeClone(
Edge, NewClone);
5249 removeNoneTypeCalleeEdges(NewClone);
5252 ContextNode *NewClone = MoveEdgeToNewCalleeCloneAndSetUp(
Edge);
5253 FuncCloneToNewCallsiteCloneMap[FuncCloneCalledByCaller] =
5256 ClonesWorklist.push_back(NewClone);
5260 removeNoneTypeCalleeEdges(Clone);
5268 if (!FuncCloneAssignedToCurCallsiteClone) {
5269 FuncCloneAssignedToCurCallsiteClone = FuncCloneCalledByCaller;
5271 AssignCallsiteCloneToFuncClone(
5272 FuncCloneCalledByCaller,
Call, Clone,
5273 AllocationCallToContextNodeMap.count(
Call));
5277 assert(FuncCloneAssignedToCurCallsiteClone ==
5278 FuncCloneCalledByCaller);
5287 if (!FuncCloneAssignedToCurCallsiteClone) {
5288 FuncCloneAssignedToCurCallsiteClone = FindFirstAvailFuncClone();
5289 assert(FuncCloneAssignedToCurCallsiteClone);
5291 AssignCallsiteCloneToFuncClone(
5292 FuncCloneAssignedToCurCallsiteClone,
Call, Clone,
5293 AllocationCallToContextNodeMap.count(
Call));
5295 assert(FuncCloneToCurNodeCloneMap
5296 [FuncCloneAssignedToCurCallsiteClone] == Clone);
5298 RecordCalleeFuncOfCallsite(
Edge->Caller,
5299 FuncCloneAssignedToCurCallsiteClone);
5319 if (!FuncCloneAssignedToCurCallsiteClone) {
5320 FuncCloneAssignedToCurCallsiteClone = FindFirstAvailFuncClone();
5321 assert(FuncCloneAssignedToCurCallsiteClone &&
5322 "No available func clone for this callsite clone");
5323 AssignCallsiteCloneToFuncClone(
5324 FuncCloneAssignedToCurCallsiteClone,
Call, Clone,
5325 AllocationCallToContextNodeMap.contains(
Call));
5330 for (
const auto &PE :
Node->CalleeEdges)
5332 for (
const auto &CE :
Node->CallerEdges)
5334 for (
auto *Clone :
Node->Clones) {
5336 for (
const auto &PE : Clone->CalleeEdges)
5338 for (
const auto &CE : Clone->CallerEdges)
5344 if (FuncCloneInfos.size() < 2)
5350 for (
auto &
Call : CallsWithMetadata) {
5351 ContextNode *
Node = getNodeForInst(
Call);
5352 if (!Node || !
Node->hasCall() ||
Node->emptyContextIds())
5358 if (
Node->Clones.size() + 1 >= FuncCloneInfos.size())
5362 DenseSet<unsigned> NodeCallClones;
5363 for (
auto *
C :
Node->Clones)
5364 NodeCallClones.
insert(
C->Call.cloneNo());
5367 for (
auto &FC : FuncCloneInfos) {
5372 if (++
I == 1 || NodeCallClones.
contains(
I)) {
5377 auto &CallVector = UnassignedCallClones[
Node][
I];
5378 DenseMap<CallInfo, CallInfo> &CallMap =
FC.CallMap;
5379 if (
auto It = CallMap.
find(
Call); It != CallMap.
end()) {
5380 CallInfo CallClone = It->second;
5381 CallVector.push_back(CallClone);
5385 assert(
false &&
"Expected to find call in CallMap");
5388 for (
auto &MatchingCall :
Node->MatchingCalls) {
5389 if (
auto It = CallMap.
find(MatchingCall); It != CallMap.
end()) {
5390 CallInfo CallClone = It->second;
5391 CallVector.push_back(CallClone);
5395 assert(
false &&
"Expected to find call in CallMap");
5403 (uint8_t)AllocationType::Cold | (uint8_t)AllocationType::NotCold;
5405 auto UpdateCalls = [&](ContextNode *
Node,
5406 DenseSet<const ContextNode *> &Visited,
5407 auto &&UpdateCalls) {
5408 auto Inserted = Visited.insert(Node);
5412 for (
auto *Clone :
Node->Clones)
5413 UpdateCalls(Clone, Visited, UpdateCalls);
5415 for (
auto &
Edge :
Node->CallerEdges)
5416 UpdateCalls(
Edge->Caller, Visited, UpdateCalls);
5420 if (!
Node->hasCall() ||
Node->emptyContextIds())
5423 if (
Node->IsAllocation) {
5424 auto AT = allocTypeToUse(
Node->AllocTypes);
5430 !ContextIdToContextSizeInfos.empty()) {
5433 for (
auto Id :
Node->getContextIds()) {
5434 auto TypeI = ContextIdToAllocationType.find(Id);
5435 assert(TypeI != ContextIdToAllocationType.end());
5436 auto CSI = ContextIdToContextSizeInfos.find(Id);
5437 if (CSI != ContextIdToContextSizeInfos.end()) {
5438 for (
auto &Info : CSI->second) {
5440 if (TypeI->second == AllocationType::Cold)
5441 TotalCold +=
Info.TotalSize;
5446 AT = AllocationType::Cold;
5448 updateAllocationCall(
Node->Call, AT);
5453 if (!CallsiteToCalleeFuncCloneMap.
count(Node))
5456 auto CalleeFunc = CallsiteToCalleeFuncCloneMap[
Node];
5457 updateCall(
Node->Call, CalleeFunc);
5459 for (
auto &
Call :
Node->MatchingCalls)
5460 updateCall(
Call, CalleeFunc);
5464 if (!UnassignedCallClones.
contains(Node))
5466 DenseSet<unsigned> NodeCallClones;
5467 for (
auto *
C :
Node->Clones)
5468 NodeCallClones.
insert(
C->Call.cloneNo());
5470 auto &ClonedCalls = UnassignedCallClones[
Node];
5471 for (
auto &[CloneNo, CallVector] : ClonedCalls) {
5475 if (NodeCallClones.
contains(CloneNo))
5478 for (
auto &
Call : CallVector)
5479 updateCall(
Call, CalleeFunc);
5488 DenseSet<const ContextNode *> Visited;
5489 for (
auto &Entry : AllocationCallToContextNodeMap)
5490 UpdateCalls(
Entry.second, Visited, UpdateCalls);
5501 for (
auto &SN : FS->callsites()) {
5506 SN.Clones.size() >
I &&
5507 "Callsite summary has fewer entries than other summaries in function");
5508 if (SN.Clones.size() <=
I || !SN.Clones[
I])
5515 for (
auto &AN : FS->allocs()) {
5519 assert(AN.Versions.size() >
I &&
5520 "Alloc summary has fewer entries than other summaries in function");
5521 if (AN.Versions.size() <=
I ||
5538 NewGV->takeName(DeclGV);
5545 auto CloneFuncAliases = [&](
Function *NewF,
unsigned I) {
5546 if (!FuncToAliasMap.count(&
F))
5548 for (
auto *
A : FuncToAliasMap[&
F]) {
5550 auto *PrevA = M.getNamedAlias(AliasName);
5552 A->getType()->getPointerAddressSpace(),
5553 A->getLinkage(), AliasName, NewF);
5554 NewA->copyAttributesFrom(
A);
5556 TakeDeclNameAndReplace(PrevA, NewA);
5565 FunctionsClonedThinBackend++;
5582 for (
unsigned I = 1;
I < NumClones;
I++) {
5583 VMaps.
emplace_back(std::make_unique<ValueToValueMapTy>());
5590 FunctionCloneDuplicatesThinBackend++;
5591 auto *Func = HashToFunc[Hash];
5592 if (Func->hasAvailableExternallyLinkage()) {
5598 auto Decl = M.getOrInsertFunction(Name, Func->getFunctionType());
5600 <<
"created clone decl " <<
ore::NV(
"Decl", Decl.getCallee()));
5603 auto *PrevF = M.getFunction(Name);
5606 TakeDeclNameAndReplace(PrevF, Alias);
5608 <<
"created clone alias " <<
ore::NV(
"Alias", Alias));
5611 CloneFuncAliases(Func,
I);
5615 HashToFunc[Hash] = NewF;
5616 FunctionClonesThinBackend++;
5619 for (
auto &BB : *NewF) {
5620 for (
auto &Inst : BB) {
5621 Inst.setMetadata(LLVMContext::MD_memprof,
nullptr);
5622 Inst.setMetadata(LLVMContext::MD_callsite,
nullptr);
5627 TakeDeclNameAndReplace(PrevF, NewF);
5629 NewF->setName(Name);
5632 <<
"created clone " <<
ore::NV(
"NewFunction", NewF));
5635 CloneFuncAliases(NewF,
I);
5644 const Function *CallingFunc =
nullptr) {
5663 auto SrcFileMD =
F.getMetadata(
"thinlto_src_file");
5669 if (!SrcFileMD &&
F.isDeclaration()) {
5673 SrcFileMD = CallingFunc->getMetadata(
"thinlto_src_file");
5678 assert(SrcFileMD || OrigName ==
F.getName());
5680 StringRef SrcFile = M.getSourceFileName();
5692 if (!TheFnVI && OrigName ==
F.getName() &&
F.hasLocalLinkage() &&
5693 F.getName().contains(
'.')) {
5694 OrigName =
F.getName().rsplit(
'.').first;
5703 assert(TheFnVI ||
F.isDeclaration());
5707bool MemProfContextDisambiguation::initializeIndirectCallPromotionInfo(
5709 ICallAnalysis = std::make_unique<ICallPromotionAnalysis>();
5710 Symtab = std::make_unique<InstrProfSymtab>();
5721 if (
Error E = Symtab->create(M,
true,
false)) {
5722 std::string SymtabFailure =
toString(std::move(
E));
5723 M.getContext().emitError(
"Failed to create symtab: " + SymtabFailure);
5736 auto MIBIter = AllocNode.
MIBs.begin();
5737 for (
auto &MDOp : MemProfMD->
operands()) {
5739 auto StackIdIndexIter = MIBIter->StackIdIndices.begin();
5744 auto ContextIterBegin =
5748 (ContextIterBegin != StackContext.
end() && *ContextIterBegin == 0) ? 1
5750 for (
auto ContextIter = ContextIterBegin; ContextIter != StackContext.
end();
5755 if (LastStackContextId == *ContextIter)
5757 LastStackContextId = *ContextIter;
5758 assert(StackIdIndexIter != MIBIter->StackIdIndices.end());
5768bool MemProfContextDisambiguation::applyImport(
Module &M) {
5775 std::map<const Function *, SmallPtrSet<const GlobalAlias *, 1>>
5777 for (
auto &
A :
M.aliases()) {
5778 auto *Aliasee =
A.getAliaseeObject();
5780 FuncToAliasMap[
F].insert(&
A);
5783 if (!initializeIndirectCallPromotionInfo(M))
5790 OptimizationRemarkEmitter ORE(&
F);
5793 bool ClonesCreated =
false;
5794 unsigned NumClonesCreated = 0;
5795 auto CloneFuncIfNeeded = [&](
unsigned NumClones, FunctionSummary *
FS) {
5805 if (ClonesCreated) {
5806 assert(NumClonesCreated == NumClones);
5813 ClonesCreated =
true;
5814 NumClonesCreated = NumClones;
5817 auto CloneCallsite = [&](
const CallsiteInfo &StackNode, CallBase *CB,
5818 Function *CalledFunction, FunctionSummary *
FS) {
5820 CloneFuncIfNeeded(StackNode.
Clones.
size(), FS);
5832 if (CalledFunction != CB->getCalledOperand() &&
5833 (!GA || CalledFunction != GA->getAliaseeObject())) {
5834 SkippedCallsCloning++;
5840 auto CalleeOrigName = CalledFunction->getName();
5841 for (
unsigned J = 0; J < StackNode.
Clones.
size(); J++) {
5844 if (J > 0 && VMaps[J - 1]->
empty())
5848 if (!StackNode.
Clones[J])
5850 auto NewF =
M.getOrInsertFunction(
5852 CalledFunction->getFunctionType());
5866 ORE.emit(OptimizationRemark(
DEBUG_TYPE,
"MemprofCall", CBClone)
5867 <<
ore::NV(
"Call", CBClone) <<
" in clone "
5869 <<
" assigned to call function clone "
5870 <<
ore::NV(
"Callee", NewF.getCallee()));
5884 ImportSummary->findSummaryInModule(TheFnVI,
M.getModuleIdentifier());
5888 auto SrcModuleMD =
F.getMetadata(
"thinlto_src_module");
5890 "enable-import-metadata is needed to emit thinlto_src_module");
5891 StringRef SrcModule =
5894 if (GVS->modulePath() == SrcModule) {
5895 GVSummary = GVS.get();
5920 if (
FS->allocs().empty() &&
FS->callsites().empty())
5923 auto SI =
FS->callsites().begin();
5924 auto AI =
FS->allocs().begin();
5929 DenseMap<ValueInfo, CallsiteInfo> MapTailCallCalleeVIToCallsite;
5932 for (
auto CallsiteIt =
FS->callsites().rbegin();
5933 CallsiteIt !=
FS->callsites().rend(); CallsiteIt++) {
5934 auto &Callsite = *CallsiteIt;
5938 if (!Callsite.StackIdIndices.empty())
5940 MapTailCallCalleeVIToCallsite.
insert({Callsite.Callee, Callsite});
5949 for (
auto &BB :
F) {
5950 for (
auto &
I : BB) {
5956 auto *CalledValue = CB->getCalledOperand();
5957 auto *CalledFunction = CB->getCalledFunction();
5958 if (CalledValue && !CalledFunction) {
5959 CalledValue = CalledValue->stripPointerCasts();
5966 assert(!CalledFunction &&
5967 "Expected null called function in callsite for alias");
5971 CallStack<MDNode, MDNode::op_iterator> CallsiteContext(
5972 I.getMetadata(LLVMContext::MD_callsite));
5973 auto *MemProfMD =
I.getMetadata(LLVMContext::MD_memprof);
5979 if (CB->getAttributes().hasFnAttr(
"memprof") && !MemProfMD) {
5980 CB->getAttributes().getFnAttr(
"memprof").getValueAsString() ==
"cold"
5981 ? AllocTypeColdThinBackend++
5982 : AllocTypeNotColdThinBackend++;
5983 OrigAllocsThinBackend++;
5984 AllocVersionsThinBackend++;
5985 if (!MaxAllocVersionsThinBackend)
5986 MaxAllocVersionsThinBackend = 1;
5993 auto &AllocNode = *(AI++);
6001 CloneFuncIfNeeded(AllocNode.Versions.size(), FS);
6003 OrigAllocsThinBackend++;
6004 AllocVersionsThinBackend += AllocNode.Versions.size();
6005 if (MaxAllocVersionsThinBackend < AllocNode.Versions.size())
6006 MaxAllocVersionsThinBackend = AllocNode.Versions.size();
6016 if (AllocNode.Versions.size() == 1 &&
6019 AllocationType::NotCold ||
6021 AllocationType::None);
6022 UnclonableAllocsThinBackend++;
6028 return Type == ((uint8_t)AllocationType::NotCold |
6029 (uint8_t)AllocationType::Cold);
6033 for (
unsigned J = 0; J < AllocNode.Versions.size(); J++) {
6036 if (J > 0 && VMaps[J - 1]->
empty())
6039 if (AllocNode.Versions[J] == (uint8_t)AllocationType::None)
6042 AllocTy == AllocationType::Cold ? AllocTypeColdThinBackend++
6043 : AllocTypeNotColdThinBackend++;
6058 ORE.emit(OptimizationRemark(
DEBUG_TYPE,
"MemprofAttribute", CBClone)
6059 <<
ore::NV(
"AllocationCall", CBClone) <<
" in clone "
6061 <<
" marked with memprof allocation attribute "
6062 <<
ore::NV(
"Attribute", AllocTypeString));
6064 }
else if (!CallsiteContext.empty()) {
6065 if (!CalledFunction) {
6069 assert(!CI || !CI->isInlineAsm());
6079 recordICPInfo(CB,
FS->callsites(), SI, ICallAnalysisInfo);
6085 CloneFuncIfNeeded(NumClones, FS);
6090 assert(SI !=
FS->callsites().end());
6091 auto &StackNode = *(
SI++);
6097 for (
auto StackId : CallsiteContext) {
6099 assert(ImportSummary->getStackIdAtIndex(*StackIdIndexIter) ==
6105 CloneCallsite(StackNode, CB, CalledFunction, FS);
6107 }
else if (CB->isTailCall() && CalledFunction) {
6110 ValueInfo CalleeVI =
6112 if (CalleeVI && MapTailCallCalleeVIToCallsite.
count(CalleeVI)) {
6113 auto Callsite = MapTailCallCalleeVIToCallsite.
find(CalleeVI);
6114 assert(Callsite != MapTailCallCalleeVIToCallsite.
end());
6115 CloneCallsite(Callsite->second, CB, CalledFunction, FS);
6122 performICP(M,
FS->callsites(), VMaps, ICallAnalysisInfo, ORE);
6132 for (
auto &BB :
F) {
6133 for (
auto &
I : BB) {
6136 I.setMetadata(LLVMContext::MD_memprof,
nullptr);
6137 I.setMetadata(LLVMContext::MD_callsite,
nullptr);
6145unsigned MemProfContextDisambiguation::recordICPInfo(
6150 uint32_t NumCandidates;
6152 auto CandidateProfileData =
6153 ICallAnalysis->getPromotionCandidatesForInstruction(
6155 if (CandidateProfileData.empty())
6161 bool ICPNeeded =
false;
6162 unsigned NumClones = 0;
6163 size_t CallsiteInfoStartIndex = std::distance(AllCallsites.
begin(), SI);
6164 for (
const auto &Candidate : CandidateProfileData) {
6166 auto CalleeValueInfo =
6168 ImportSummary->getValueInfo(Candidate.Value);
6171 assert(!CalleeValueInfo ||
SI->Callee == CalleeValueInfo);
6173 auto &StackNode = *(
SI++);
6178 [](
unsigned CloneNo) { return CloneNo != 0; });
6188 ICallAnalysisInfo.
push_back({CB, CandidateProfileData.vec(), NumCandidates,
6189 TotalCount, CallsiteInfoStartIndex});
6193void MemProfContextDisambiguation::performICP(
6195 ArrayRef<std::unique_ptr<ValueToValueMapTy>> VMaps,
6197 OptimizationRemarkEmitter &ORE) {
6204 for (
auto &Info : ICallAnalysisInfo) {
6207 auto TotalCount =
Info.TotalCount;
6208 unsigned NumClones = 0;
6211 for (
auto &Candidate :
Info.CandidateProfileData) {
6222 Function *TargetFunction = Symtab->getFunction(Candidate.Value);
6223 if (TargetFunction ==
nullptr ||
6231 return OptimizationRemarkMissed(
DEBUG_TYPE,
"UnableToFindTarget", CB)
6232 <<
"Memprof cannot promote indirect call: target with md5sum "
6233 <<
ore::NV(
"target md5sum", Candidate.Value) <<
" not found";
6238 RemainingCandidates.
push_back(Candidate);
6243 const char *Reason =
nullptr;
6246 return OptimizationRemarkMissed(
DEBUG_TYPE,
"UnableToPromote", CB)
6247 <<
"Memprof cannot promote indirect call to "
6248 <<
ore::NV(
"TargetFunction", TargetFunction)
6249 <<
" with count of " <<
ore::NV(
"TotalCount", TotalCount)
6252 RemainingCandidates.
push_back(Candidate);
6261 CallBase *CBClone = CB;
6262 for (
unsigned J = 0; J < NumClones; J++) {
6265 if (J > 0 && VMaps[J - 1]->
empty())
6275 TotalCount, isSamplePGO, &ORE);
6276 auto *TargetToUse = TargetFunction;
6279 if (StackNode.
Clones[J]) {
6298 <<
ore::NV(
"Call", CBClone) <<
" in clone "
6300 <<
" promoted and assigned to call function clone "
6301 <<
ore::NV(
"Callee", TargetToUse));
6305 TotalCount -= Candidate.Count;
6309 CallBase *CBClone = CB;
6310 for (
unsigned J = 0; J < NumClones; J++) {
6313 if (J > 0 && VMaps[J - 1]->
empty())
6319 CBClone->
setMetadata(LLVMContext::MD_prof,
nullptr);
6322 if (TotalCount != 0)
6324 IPVK_IndirectCallTarget,
Info.NumCandidates);
6329template <
typename DerivedCCG,
typename FuncTy,
typename CallTy>
6330bool CallsiteContextGraph<DerivedCCG, FuncTy, CallTy>::process(
6331 function_ref<
void(StringRef, StringRef,
const Twine &)> EmitRemark,
6332 bool AllowExtraAnalysis) {
6334 dbgs() <<
"CCG before cloning:\n";
6338 exportToDot(
"postbuild");
6351 dbgs() <<
"CCG after cloning:\n";
6355 exportToDot(
"cloned");
6357 bool Changed = assignFunctions();
6360 dbgs() <<
"CCG after assigning function clones:\n";
6364 exportToDot(
"clonefuncassign");
6367 printTotalSizes(
errs(), EmitRemark);
6372bool MemProfContextDisambiguation::processModule(
6374 llvm::function_ref<OptimizationRemarkEmitter &(
Function *)> OREGetter) {
6379 return applyImport(M);
6392 ModuleCallsiteContextGraph CCG(M, OREGetter);
6395 return CCG.process();
6400 : ImportSummary(Summary), isSamplePGO(isSamplePGO) {
6405 "-memprof-dot-scope=alloc requires -memprof-dot-alloc-id");
6409 "-memprof-dot-scope=context requires -memprof-dot-context-id");
6413 "-memprof-dot-scope=all can't have both -memprof-dot-alloc-id and "
6414 "-memprof-dot-context-id");
6415 if (ImportSummary) {
6425 auto ReadSummaryFile =
6427 if (!ReadSummaryFile) {
6434 if (!ImportSummaryForTestingOrErr) {
6440 ImportSummaryForTesting = std::move(*ImportSummaryForTestingOrErr);
6441 ImportSummary = ImportSummaryForTesting.get();
6450 if (!processModule(M, OREGetter))
6469 bool AllowExtraAnalysis =
6472 IndexCallsiteContextGraph CCG(Index, isPrevailing);
6473 CCG.process(EmitRemark, AllowExtraAnalysis);
6488 for (
auto &BB :
F) {
6489 for (
auto &
I : BB) {
6493 if (CI->hasFnAttr(
"memprof")) {
6494 CI->removeFnAttr(
"memprof");
6497 if (!CI->hasMetadata(LLVMContext::MD_callsite)) {
6498 assert(!CI->hasMetadata(LLVMContext::MD_memprof));
6504 CI->setMetadata(LLVMContext::MD_memprof,
nullptr);
6505 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...