xref: /freebsd/contrib/llvm-project/llvm/lib/IR/ModuleSummaryIndex.cpp (revision 0b57cec536236d46e3dba9bd041533462f33dbb7)
1*0b57cec5SDimitry Andric //===-- ModuleSummaryIndex.cpp - Module Summary Index ---------------------===//
2*0b57cec5SDimitry Andric //
3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0b57cec5SDimitry Andric //
7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
8*0b57cec5SDimitry Andric //
9*0b57cec5SDimitry Andric // This file implements the module index and summary classes for the
10*0b57cec5SDimitry Andric // IR library.
11*0b57cec5SDimitry Andric //
12*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
13*0b57cec5SDimitry Andric 
14*0b57cec5SDimitry Andric #include "llvm/IR/ModuleSummaryIndex.h"
15*0b57cec5SDimitry Andric #include "llvm/ADT/SCCIterator.h"
16*0b57cec5SDimitry Andric #include "llvm/ADT/Statistic.h"
17*0b57cec5SDimitry Andric #include "llvm/ADT/StringMap.h"
18*0b57cec5SDimitry Andric #include "llvm/Support/Path.h"
19*0b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
20*0b57cec5SDimitry Andric using namespace llvm;
21*0b57cec5SDimitry Andric 
22*0b57cec5SDimitry Andric #define DEBUG_TYPE "module-summary-index"
23*0b57cec5SDimitry Andric 
24*0b57cec5SDimitry Andric STATISTIC(ReadOnlyLiveGVars,
25*0b57cec5SDimitry Andric           "Number of live global variables marked read only");
26*0b57cec5SDimitry Andric STATISTIC(WriteOnlyLiveGVars,
27*0b57cec5SDimitry Andric           "Number of live global variables marked write only");
28*0b57cec5SDimitry Andric 
29*0b57cec5SDimitry Andric FunctionSummary FunctionSummary::ExternalNode =
30*0b57cec5SDimitry Andric     FunctionSummary::makeDummyFunctionSummary({});
31*0b57cec5SDimitry Andric 
32*0b57cec5SDimitry Andric bool ValueInfo::isDSOLocal() const {
33*0b57cec5SDimitry Andric   // Need to check all summaries are local in case of hash collisions.
34*0b57cec5SDimitry Andric   return getSummaryList().size() &&
35*0b57cec5SDimitry Andric          llvm::all_of(getSummaryList(),
36*0b57cec5SDimitry Andric                       [](const std::unique_ptr<GlobalValueSummary> &Summary) {
37*0b57cec5SDimitry Andric                         return Summary->isDSOLocal();
38*0b57cec5SDimitry Andric                       });
39*0b57cec5SDimitry Andric }
40*0b57cec5SDimitry Andric 
41*0b57cec5SDimitry Andric bool ValueInfo::canAutoHide() const {
42*0b57cec5SDimitry Andric   // Can only auto hide if all copies are eligible to auto hide.
43*0b57cec5SDimitry Andric   return getSummaryList().size() &&
44*0b57cec5SDimitry Andric          llvm::all_of(getSummaryList(),
45*0b57cec5SDimitry Andric                       [](const std::unique_ptr<GlobalValueSummary> &Summary) {
46*0b57cec5SDimitry Andric                         return Summary->canAutoHide();
47*0b57cec5SDimitry Andric                       });
48*0b57cec5SDimitry Andric }
49*0b57cec5SDimitry Andric 
50*0b57cec5SDimitry Andric // Gets the number of readonly and writeonly refs in RefEdgeList
51*0b57cec5SDimitry Andric std::pair<unsigned, unsigned> FunctionSummary::specialRefCounts() const {
52*0b57cec5SDimitry Andric   // Here we take advantage of having all readonly and writeonly references
53*0b57cec5SDimitry Andric   // located in the end of the RefEdgeList.
54*0b57cec5SDimitry Andric   auto Refs = refs();
55*0b57cec5SDimitry Andric   unsigned RORefCnt = 0, WORefCnt = 0;
56*0b57cec5SDimitry Andric   int I;
57*0b57cec5SDimitry Andric   for (I = Refs.size() - 1; I >= 0 && Refs[I].isWriteOnly(); --I)
58*0b57cec5SDimitry Andric     WORefCnt++;
59*0b57cec5SDimitry Andric   for (; I >= 0 && Refs[I].isReadOnly(); --I)
60*0b57cec5SDimitry Andric     RORefCnt++;
61*0b57cec5SDimitry Andric   return {RORefCnt, WORefCnt};
62*0b57cec5SDimitry Andric }
63*0b57cec5SDimitry Andric 
64*0b57cec5SDimitry Andric // Collect for the given module the list of function it defines
65*0b57cec5SDimitry Andric // (GUID -> Summary).
66*0b57cec5SDimitry Andric void ModuleSummaryIndex::collectDefinedFunctionsForModule(
67*0b57cec5SDimitry Andric     StringRef ModulePath, GVSummaryMapTy &GVSummaryMap) const {
68*0b57cec5SDimitry Andric   for (auto &GlobalList : *this) {
69*0b57cec5SDimitry Andric     auto GUID = GlobalList.first;
70*0b57cec5SDimitry Andric     for (auto &GlobSummary : GlobalList.second.SummaryList) {
71*0b57cec5SDimitry Andric       auto *Summary = dyn_cast_or_null<FunctionSummary>(GlobSummary.get());
72*0b57cec5SDimitry Andric       if (!Summary)
73*0b57cec5SDimitry Andric         // Ignore global variable, focus on functions
74*0b57cec5SDimitry Andric         continue;
75*0b57cec5SDimitry Andric       // Ignore summaries from other modules.
76*0b57cec5SDimitry Andric       if (Summary->modulePath() != ModulePath)
77*0b57cec5SDimitry Andric         continue;
78*0b57cec5SDimitry Andric       GVSummaryMap[GUID] = Summary;
79*0b57cec5SDimitry Andric     }
80*0b57cec5SDimitry Andric   }
81*0b57cec5SDimitry Andric }
82*0b57cec5SDimitry Andric 
83*0b57cec5SDimitry Andric GlobalValueSummary *
84*0b57cec5SDimitry Andric ModuleSummaryIndex::getGlobalValueSummary(uint64_t ValueGUID,
85*0b57cec5SDimitry Andric                                           bool PerModuleIndex) const {
86*0b57cec5SDimitry Andric   auto VI = getValueInfo(ValueGUID);
87*0b57cec5SDimitry Andric   assert(VI && "GlobalValue not found in index");
88*0b57cec5SDimitry Andric   assert((!PerModuleIndex || VI.getSummaryList().size() == 1) &&
89*0b57cec5SDimitry Andric          "Expected a single entry per global value in per-module index");
90*0b57cec5SDimitry Andric   auto &Summary = VI.getSummaryList()[0];
91*0b57cec5SDimitry Andric   return Summary.get();
92*0b57cec5SDimitry Andric }
93*0b57cec5SDimitry Andric 
94*0b57cec5SDimitry Andric bool ModuleSummaryIndex::isGUIDLive(GlobalValue::GUID GUID) const {
95*0b57cec5SDimitry Andric   auto VI = getValueInfo(GUID);
96*0b57cec5SDimitry Andric   if (!VI)
97*0b57cec5SDimitry Andric     return true;
98*0b57cec5SDimitry Andric   const auto &SummaryList = VI.getSummaryList();
99*0b57cec5SDimitry Andric   if (SummaryList.empty())
100*0b57cec5SDimitry Andric     return true;
101*0b57cec5SDimitry Andric   for (auto &I : SummaryList)
102*0b57cec5SDimitry Andric     if (isGlobalValueLive(I.get()))
103*0b57cec5SDimitry Andric       return true;
104*0b57cec5SDimitry Andric   return false;
105*0b57cec5SDimitry Andric }
106*0b57cec5SDimitry Andric 
107*0b57cec5SDimitry Andric static void propagateAttributesToRefs(GlobalValueSummary *S) {
108*0b57cec5SDimitry Andric   // If reference is not readonly or writeonly then referenced summary is not
109*0b57cec5SDimitry Andric   // read/writeonly either. Note that:
110*0b57cec5SDimitry Andric   // - All references from GlobalVarSummary are conservatively considered as
111*0b57cec5SDimitry Andric   //   not readonly or writeonly. Tracking them properly requires more complex
112*0b57cec5SDimitry Andric   //   analysis then we have now.
113*0b57cec5SDimitry Andric   //
114*0b57cec5SDimitry Andric   // - AliasSummary objects have no refs at all so this function is a no-op
115*0b57cec5SDimitry Andric   //   for them.
116*0b57cec5SDimitry Andric   for (auto &VI : S->refs()) {
117*0b57cec5SDimitry Andric     assert(VI.getAccessSpecifier() == 0 || isa<FunctionSummary>(S));
118*0b57cec5SDimitry Andric     for (auto &Ref : VI.getSummaryList())
119*0b57cec5SDimitry Andric       // If references to alias is not read/writeonly then aliasee
120*0b57cec5SDimitry Andric       // is not read/writeonly
121*0b57cec5SDimitry Andric       if (auto *GVS = dyn_cast<GlobalVarSummary>(Ref->getBaseObject())) {
122*0b57cec5SDimitry Andric         if (!VI.isReadOnly())
123*0b57cec5SDimitry Andric           GVS->setReadOnly(false);
124*0b57cec5SDimitry Andric         if (!VI.isWriteOnly())
125*0b57cec5SDimitry Andric           GVS->setWriteOnly(false);
126*0b57cec5SDimitry Andric       }
127*0b57cec5SDimitry Andric   }
128*0b57cec5SDimitry Andric }
129*0b57cec5SDimitry Andric 
130*0b57cec5SDimitry Andric // Do the access attribute propagation in combined index.
131*0b57cec5SDimitry Andric // The goal of attribute propagation is internalization of readonly (RO)
132*0b57cec5SDimitry Andric // or writeonly (WO) variables. To determine which variables are RO or WO
133*0b57cec5SDimitry Andric // and which are not we take following steps:
134*0b57cec5SDimitry Andric // - During analysis we speculatively assign readonly and writeonly
135*0b57cec5SDimitry Andric //   attribute to all variables which can be internalized. When computing
136*0b57cec5SDimitry Andric //   function summary we also assign readonly or writeonly attribute to a
137*0b57cec5SDimitry Andric //   reference if function doesn't modify referenced variable (readonly)
138*0b57cec5SDimitry Andric //   or doesn't read it (writeonly).
139*0b57cec5SDimitry Andric //
140*0b57cec5SDimitry Andric // - After computing dead symbols in combined index we do the attribute
141*0b57cec5SDimitry Andric //   propagation. During this step we:
142*0b57cec5SDimitry Andric //   a. clear RO and WO attributes from variables which are preserved or
143*0b57cec5SDimitry Andric //      can't be imported
144*0b57cec5SDimitry Andric //   b. clear RO and WO attributes from variables referenced by any global
145*0b57cec5SDimitry Andric //      variable initializer
146*0b57cec5SDimitry Andric //   c. clear RO attribute from variable referenced by a function when
147*0b57cec5SDimitry Andric //      reference is not readonly
148*0b57cec5SDimitry Andric //   d. clear WO attribute from variable referenced by a function when
149*0b57cec5SDimitry Andric //      reference is not writeonly
150*0b57cec5SDimitry Andric //
151*0b57cec5SDimitry Andric //   Because of (c, d) we don't internalize variables read by function A
152*0b57cec5SDimitry Andric //   and modified by function B.
153*0b57cec5SDimitry Andric //
154*0b57cec5SDimitry Andric // Internalization itself happens in the backend after import is finished
155*0b57cec5SDimitry Andric // See internalizeGVsAfterImport.
156*0b57cec5SDimitry Andric void ModuleSummaryIndex::propagateAttributes(
157*0b57cec5SDimitry Andric     const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols) {
158*0b57cec5SDimitry Andric   for (auto &P : *this)
159*0b57cec5SDimitry Andric     for (auto &S : P.second.SummaryList) {
160*0b57cec5SDimitry Andric       if (!isGlobalValueLive(S.get()))
161*0b57cec5SDimitry Andric         // We don't examine references from dead objects
162*0b57cec5SDimitry Andric         continue;
163*0b57cec5SDimitry Andric 
164*0b57cec5SDimitry Andric       // Global variable can't be marked read/writeonly if it is not eligible
165*0b57cec5SDimitry Andric       // to import since we need to ensure that all external references get
166*0b57cec5SDimitry Andric       // a local (imported) copy. It also can't be marked read/writeonly if
167*0b57cec5SDimitry Andric       // it or any alias (since alias points to the same memory) are preserved
168*0b57cec5SDimitry Andric       // or notEligibleToImport, since either of those means there could be
169*0b57cec5SDimitry Andric       // writes (or reads in case of writeonly) that are not visible (because
170*0b57cec5SDimitry Andric       // preserved means it could have external to DSO writes or reads, and
171*0b57cec5SDimitry Andric       // notEligibleToImport means it could have writes or reads via inline
172*0b57cec5SDimitry Andric       // assembly leading it to be in the @llvm.*used).
173*0b57cec5SDimitry Andric       if (auto *GVS = dyn_cast<GlobalVarSummary>(S->getBaseObject()))
174*0b57cec5SDimitry Andric         // Here we intentionally pass S.get() not GVS, because S could be
175*0b57cec5SDimitry Andric         // an alias.
176*0b57cec5SDimitry Andric         if (!canImportGlobalVar(S.get()) ||
177*0b57cec5SDimitry Andric             GUIDPreservedSymbols.count(P.first)) {
178*0b57cec5SDimitry Andric           GVS->setReadOnly(false);
179*0b57cec5SDimitry Andric           GVS->setWriteOnly(false);
180*0b57cec5SDimitry Andric         }
181*0b57cec5SDimitry Andric       propagateAttributesToRefs(S.get());
182*0b57cec5SDimitry Andric     }
183*0b57cec5SDimitry Andric   if (llvm::AreStatisticsEnabled())
184*0b57cec5SDimitry Andric     for (auto &P : *this)
185*0b57cec5SDimitry Andric       if (P.second.SummaryList.size())
186*0b57cec5SDimitry Andric         if (auto *GVS = dyn_cast<GlobalVarSummary>(
187*0b57cec5SDimitry Andric                 P.second.SummaryList[0]->getBaseObject()))
188*0b57cec5SDimitry Andric           if (isGlobalValueLive(GVS)) {
189*0b57cec5SDimitry Andric             if (GVS->maybeReadOnly())
190*0b57cec5SDimitry Andric               ReadOnlyLiveGVars++;
191*0b57cec5SDimitry Andric             if (GVS->maybeWriteOnly())
192*0b57cec5SDimitry Andric               WriteOnlyLiveGVars++;
193*0b57cec5SDimitry Andric           }
194*0b57cec5SDimitry Andric }
195*0b57cec5SDimitry Andric 
196*0b57cec5SDimitry Andric // TODO: write a graphviz dumper for SCCs (see ModuleSummaryIndex::exportToDot)
197*0b57cec5SDimitry Andric // then delete this function and update its tests
198*0b57cec5SDimitry Andric LLVM_DUMP_METHOD
199*0b57cec5SDimitry Andric void ModuleSummaryIndex::dumpSCCs(raw_ostream &O) {
200*0b57cec5SDimitry Andric   for (scc_iterator<ModuleSummaryIndex *> I =
201*0b57cec5SDimitry Andric            scc_begin<ModuleSummaryIndex *>(this);
202*0b57cec5SDimitry Andric        !I.isAtEnd(); ++I) {
203*0b57cec5SDimitry Andric     O << "SCC (" << utostr(I->size()) << " node" << (I->size() == 1 ? "" : "s")
204*0b57cec5SDimitry Andric       << ") {\n";
205*0b57cec5SDimitry Andric     for (const ValueInfo V : *I) {
206*0b57cec5SDimitry Andric       FunctionSummary *F = nullptr;
207*0b57cec5SDimitry Andric       if (V.getSummaryList().size())
208*0b57cec5SDimitry Andric         F = cast<FunctionSummary>(V.getSummaryList().front().get());
209*0b57cec5SDimitry Andric       O << " " << (F == nullptr ? "External" : "") << " " << utostr(V.getGUID())
210*0b57cec5SDimitry Andric         << (I.hasLoop() ? " (has loop)" : "") << "\n";
211*0b57cec5SDimitry Andric     }
212*0b57cec5SDimitry Andric     O << "}\n";
213*0b57cec5SDimitry Andric   }
214*0b57cec5SDimitry Andric }
215*0b57cec5SDimitry Andric 
216*0b57cec5SDimitry Andric namespace {
217*0b57cec5SDimitry Andric struct Attributes {
218*0b57cec5SDimitry Andric   void add(const Twine &Name, const Twine &Value,
219*0b57cec5SDimitry Andric            const Twine &Comment = Twine());
220*0b57cec5SDimitry Andric   void addComment(const Twine &Comment);
221*0b57cec5SDimitry Andric   std::string getAsString() const;
222*0b57cec5SDimitry Andric 
223*0b57cec5SDimitry Andric   std::vector<std::string> Attrs;
224*0b57cec5SDimitry Andric   std::string Comments;
225*0b57cec5SDimitry Andric };
226*0b57cec5SDimitry Andric 
227*0b57cec5SDimitry Andric struct Edge {
228*0b57cec5SDimitry Andric   uint64_t SrcMod;
229*0b57cec5SDimitry Andric   int Hotness;
230*0b57cec5SDimitry Andric   GlobalValue::GUID Src;
231*0b57cec5SDimitry Andric   GlobalValue::GUID Dst;
232*0b57cec5SDimitry Andric };
233*0b57cec5SDimitry Andric }
234*0b57cec5SDimitry Andric 
235*0b57cec5SDimitry Andric void Attributes::add(const Twine &Name, const Twine &Value,
236*0b57cec5SDimitry Andric                      const Twine &Comment) {
237*0b57cec5SDimitry Andric   std::string A = Name.str();
238*0b57cec5SDimitry Andric   A += "=\"";
239*0b57cec5SDimitry Andric   A += Value.str();
240*0b57cec5SDimitry Andric   A += "\"";
241*0b57cec5SDimitry Andric   Attrs.push_back(A);
242*0b57cec5SDimitry Andric   addComment(Comment);
243*0b57cec5SDimitry Andric }
244*0b57cec5SDimitry Andric 
245*0b57cec5SDimitry Andric void Attributes::addComment(const Twine &Comment) {
246*0b57cec5SDimitry Andric   if (!Comment.isTriviallyEmpty()) {
247*0b57cec5SDimitry Andric     if (Comments.empty())
248*0b57cec5SDimitry Andric       Comments = " // ";
249*0b57cec5SDimitry Andric     else
250*0b57cec5SDimitry Andric       Comments += ", ";
251*0b57cec5SDimitry Andric     Comments += Comment.str();
252*0b57cec5SDimitry Andric   }
253*0b57cec5SDimitry Andric }
254*0b57cec5SDimitry Andric 
255*0b57cec5SDimitry Andric std::string Attributes::getAsString() const {
256*0b57cec5SDimitry Andric   if (Attrs.empty())
257*0b57cec5SDimitry Andric     return "";
258*0b57cec5SDimitry Andric 
259*0b57cec5SDimitry Andric   std::string Ret = "[";
260*0b57cec5SDimitry Andric   for (auto &A : Attrs)
261*0b57cec5SDimitry Andric     Ret += A + ",";
262*0b57cec5SDimitry Andric   Ret.pop_back();
263*0b57cec5SDimitry Andric   Ret += "];";
264*0b57cec5SDimitry Andric   Ret += Comments;
265*0b57cec5SDimitry Andric   return Ret;
266*0b57cec5SDimitry Andric }
267*0b57cec5SDimitry Andric 
268*0b57cec5SDimitry Andric static std::string linkageToString(GlobalValue::LinkageTypes LT) {
269*0b57cec5SDimitry Andric   switch (LT) {
270*0b57cec5SDimitry Andric   case GlobalValue::ExternalLinkage:
271*0b57cec5SDimitry Andric     return "extern";
272*0b57cec5SDimitry Andric   case GlobalValue::AvailableExternallyLinkage:
273*0b57cec5SDimitry Andric     return "av_ext";
274*0b57cec5SDimitry Andric   case GlobalValue::LinkOnceAnyLinkage:
275*0b57cec5SDimitry Andric     return "linkonce";
276*0b57cec5SDimitry Andric   case GlobalValue::LinkOnceODRLinkage:
277*0b57cec5SDimitry Andric     return "linkonce_odr";
278*0b57cec5SDimitry Andric   case GlobalValue::WeakAnyLinkage:
279*0b57cec5SDimitry Andric     return "weak";
280*0b57cec5SDimitry Andric   case GlobalValue::WeakODRLinkage:
281*0b57cec5SDimitry Andric     return "weak_odr";
282*0b57cec5SDimitry Andric   case GlobalValue::AppendingLinkage:
283*0b57cec5SDimitry Andric     return "appending";
284*0b57cec5SDimitry Andric   case GlobalValue::InternalLinkage:
285*0b57cec5SDimitry Andric     return "internal";
286*0b57cec5SDimitry Andric   case GlobalValue::PrivateLinkage:
287*0b57cec5SDimitry Andric     return "private";
288*0b57cec5SDimitry Andric   case GlobalValue::ExternalWeakLinkage:
289*0b57cec5SDimitry Andric     return "extern_weak";
290*0b57cec5SDimitry Andric   case GlobalValue::CommonLinkage:
291*0b57cec5SDimitry Andric     return "common";
292*0b57cec5SDimitry Andric   }
293*0b57cec5SDimitry Andric 
294*0b57cec5SDimitry Andric   return "<unknown>";
295*0b57cec5SDimitry Andric }
296*0b57cec5SDimitry Andric 
297*0b57cec5SDimitry Andric static std::string fflagsToString(FunctionSummary::FFlags F) {
298*0b57cec5SDimitry Andric   auto FlagValue = [](unsigned V) { return V ? '1' : '0'; };
299*0b57cec5SDimitry Andric   char FlagRep[] = {FlagValue(F.ReadNone),     FlagValue(F.ReadOnly),
300*0b57cec5SDimitry Andric                     FlagValue(F.NoRecurse),    FlagValue(F.ReturnDoesNotAlias),
301*0b57cec5SDimitry Andric                     FlagValue(F.NoInline), 0};
302*0b57cec5SDimitry Andric 
303*0b57cec5SDimitry Andric   return FlagRep;
304*0b57cec5SDimitry Andric }
305*0b57cec5SDimitry Andric 
306*0b57cec5SDimitry Andric // Get string representation of function instruction count and flags.
307*0b57cec5SDimitry Andric static std::string getSummaryAttributes(GlobalValueSummary* GVS) {
308*0b57cec5SDimitry Andric   auto *FS = dyn_cast_or_null<FunctionSummary>(GVS);
309*0b57cec5SDimitry Andric   if (!FS)
310*0b57cec5SDimitry Andric     return "";
311*0b57cec5SDimitry Andric 
312*0b57cec5SDimitry Andric   return std::string("inst: ") + std::to_string(FS->instCount()) +
313*0b57cec5SDimitry Andric          ", ffl: " + fflagsToString(FS->fflags());
314*0b57cec5SDimitry Andric }
315*0b57cec5SDimitry Andric 
316*0b57cec5SDimitry Andric static std::string getNodeVisualName(GlobalValue::GUID Id) {
317*0b57cec5SDimitry Andric   return std::string("@") + std::to_string(Id);
318*0b57cec5SDimitry Andric }
319*0b57cec5SDimitry Andric 
320*0b57cec5SDimitry Andric static std::string getNodeVisualName(const ValueInfo &VI) {
321*0b57cec5SDimitry Andric   return VI.name().empty() ? getNodeVisualName(VI.getGUID()) : VI.name().str();
322*0b57cec5SDimitry Andric }
323*0b57cec5SDimitry Andric 
324*0b57cec5SDimitry Andric static std::string getNodeLabel(const ValueInfo &VI, GlobalValueSummary *GVS) {
325*0b57cec5SDimitry Andric   if (isa<AliasSummary>(GVS))
326*0b57cec5SDimitry Andric     return getNodeVisualName(VI);
327*0b57cec5SDimitry Andric 
328*0b57cec5SDimitry Andric   std::string Attrs = getSummaryAttributes(GVS);
329*0b57cec5SDimitry Andric   std::string Label =
330*0b57cec5SDimitry Andric       getNodeVisualName(VI) + "|" + linkageToString(GVS->linkage());
331*0b57cec5SDimitry Andric   if (!Attrs.empty())
332*0b57cec5SDimitry Andric     Label += std::string(" (") + Attrs + ")";
333*0b57cec5SDimitry Andric   Label += "}";
334*0b57cec5SDimitry Andric 
335*0b57cec5SDimitry Andric   return Label;
336*0b57cec5SDimitry Andric }
337*0b57cec5SDimitry Andric 
338*0b57cec5SDimitry Andric // Write definition of external node, which doesn't have any
339*0b57cec5SDimitry Andric // specific module associated with it. Typically this is function
340*0b57cec5SDimitry Andric // or variable defined in native object or library.
341*0b57cec5SDimitry Andric static void defineExternalNode(raw_ostream &OS, const char *Pfx,
342*0b57cec5SDimitry Andric                                const ValueInfo &VI, GlobalValue::GUID Id) {
343*0b57cec5SDimitry Andric   auto StrId = std::to_string(Id);
344*0b57cec5SDimitry Andric   OS << "  " << StrId << " [label=\"";
345*0b57cec5SDimitry Andric 
346*0b57cec5SDimitry Andric   if (VI) {
347*0b57cec5SDimitry Andric     OS << getNodeVisualName(VI);
348*0b57cec5SDimitry Andric   } else {
349*0b57cec5SDimitry Andric     OS << getNodeVisualName(Id);
350*0b57cec5SDimitry Andric   }
351*0b57cec5SDimitry Andric   OS << "\"]; // defined externally\n";
352*0b57cec5SDimitry Andric }
353*0b57cec5SDimitry Andric 
354*0b57cec5SDimitry Andric static bool hasReadOnlyFlag(const GlobalValueSummary *S) {
355*0b57cec5SDimitry Andric   if (auto *GVS = dyn_cast<GlobalVarSummary>(S))
356*0b57cec5SDimitry Andric     return GVS->maybeReadOnly();
357*0b57cec5SDimitry Andric   return false;
358*0b57cec5SDimitry Andric }
359*0b57cec5SDimitry Andric 
360*0b57cec5SDimitry Andric static bool hasWriteOnlyFlag(const GlobalValueSummary *S) {
361*0b57cec5SDimitry Andric   if (auto *GVS = dyn_cast<GlobalVarSummary>(S))
362*0b57cec5SDimitry Andric     return GVS->maybeWriteOnly();
363*0b57cec5SDimitry Andric   return false;
364*0b57cec5SDimitry Andric }
365*0b57cec5SDimitry Andric 
366*0b57cec5SDimitry Andric void ModuleSummaryIndex::exportToDot(raw_ostream &OS) const {
367*0b57cec5SDimitry Andric   std::vector<Edge> CrossModuleEdges;
368*0b57cec5SDimitry Andric   DenseMap<GlobalValue::GUID, std::vector<uint64_t>> NodeMap;
369*0b57cec5SDimitry Andric   using GVSOrderedMapTy = std::map<GlobalValue::GUID, GlobalValueSummary *>;
370*0b57cec5SDimitry Andric   std::map<StringRef, GVSOrderedMapTy> ModuleToDefinedGVS;
371*0b57cec5SDimitry Andric   collectDefinedGVSummariesPerModule(ModuleToDefinedGVS);
372*0b57cec5SDimitry Andric 
373*0b57cec5SDimitry Andric   // Get node identifier in form MXXX_<GUID>. The MXXX prefix is required,
374*0b57cec5SDimitry Andric   // because we may have multiple linkonce functions summaries.
375*0b57cec5SDimitry Andric   auto NodeId = [](uint64_t ModId, GlobalValue::GUID Id) {
376*0b57cec5SDimitry Andric     return ModId == (uint64_t)-1 ? std::to_string(Id)
377*0b57cec5SDimitry Andric                                  : std::string("M") + std::to_string(ModId) +
378*0b57cec5SDimitry Andric                                        "_" + std::to_string(Id);
379*0b57cec5SDimitry Andric   };
380*0b57cec5SDimitry Andric 
381*0b57cec5SDimitry Andric   auto DrawEdge = [&](const char *Pfx, uint64_t SrcMod, GlobalValue::GUID SrcId,
382*0b57cec5SDimitry Andric                       uint64_t DstMod, GlobalValue::GUID DstId,
383*0b57cec5SDimitry Andric                       int TypeOrHotness) {
384*0b57cec5SDimitry Andric     // 0 - alias
385*0b57cec5SDimitry Andric     // 1 - reference
386*0b57cec5SDimitry Andric     // 2 - constant reference
387*0b57cec5SDimitry Andric     // 3 - writeonly reference
388*0b57cec5SDimitry Andric     // Other value: (hotness - 4).
389*0b57cec5SDimitry Andric     TypeOrHotness += 4;
390*0b57cec5SDimitry Andric     static const char *EdgeAttrs[] = {
391*0b57cec5SDimitry Andric         " [style=dotted]; // alias",
392*0b57cec5SDimitry Andric         " [style=dashed]; // ref",
393*0b57cec5SDimitry Andric         " [style=dashed,color=forestgreen]; // const-ref",
394*0b57cec5SDimitry Andric         " [style=dashed,color=violetred]; // writeOnly-ref",
395*0b57cec5SDimitry Andric         " // call (hotness : Unknown)",
396*0b57cec5SDimitry Andric         " [color=blue]; // call (hotness : Cold)",
397*0b57cec5SDimitry Andric         " // call (hotness : None)",
398*0b57cec5SDimitry Andric         " [color=brown]; // call (hotness : Hot)",
399*0b57cec5SDimitry Andric         " [style=bold,color=red]; // call (hotness : Critical)"};
400*0b57cec5SDimitry Andric 
401*0b57cec5SDimitry Andric     assert(static_cast<size_t>(TypeOrHotness) <
402*0b57cec5SDimitry Andric            sizeof(EdgeAttrs) / sizeof(EdgeAttrs[0]));
403*0b57cec5SDimitry Andric     OS << Pfx << NodeId(SrcMod, SrcId) << " -> " << NodeId(DstMod, DstId)
404*0b57cec5SDimitry Andric        << EdgeAttrs[TypeOrHotness] << "\n";
405*0b57cec5SDimitry Andric   };
406*0b57cec5SDimitry Andric 
407*0b57cec5SDimitry Andric   OS << "digraph Summary {\n";
408*0b57cec5SDimitry Andric   for (auto &ModIt : ModuleToDefinedGVS) {
409*0b57cec5SDimitry Andric     auto ModId = getModuleId(ModIt.first);
410*0b57cec5SDimitry Andric     OS << "  // Module: " << ModIt.first << "\n";
411*0b57cec5SDimitry Andric     OS << "  subgraph cluster_" << std::to_string(ModId) << " {\n";
412*0b57cec5SDimitry Andric     OS << "    style = filled;\n";
413*0b57cec5SDimitry Andric     OS << "    color = lightgrey;\n";
414*0b57cec5SDimitry Andric     OS << "    label = \"" << sys::path::filename(ModIt.first) << "\";\n";
415*0b57cec5SDimitry Andric     OS << "    node [style=filled,fillcolor=lightblue];\n";
416*0b57cec5SDimitry Andric 
417*0b57cec5SDimitry Andric     auto &GVSMap = ModIt.second;
418*0b57cec5SDimitry Andric     auto Draw = [&](GlobalValue::GUID IdFrom, GlobalValue::GUID IdTo, int Hotness) {
419*0b57cec5SDimitry Andric       if (!GVSMap.count(IdTo)) {
420*0b57cec5SDimitry Andric         CrossModuleEdges.push_back({ModId, Hotness, IdFrom, IdTo});
421*0b57cec5SDimitry Andric         return;
422*0b57cec5SDimitry Andric       }
423*0b57cec5SDimitry Andric       DrawEdge("    ", ModId, IdFrom, ModId, IdTo, Hotness);
424*0b57cec5SDimitry Andric     };
425*0b57cec5SDimitry Andric 
426*0b57cec5SDimitry Andric     for (auto &SummaryIt : GVSMap) {
427*0b57cec5SDimitry Andric       NodeMap[SummaryIt.first].push_back(ModId);
428*0b57cec5SDimitry Andric       auto Flags = SummaryIt.second->flags();
429*0b57cec5SDimitry Andric       Attributes A;
430*0b57cec5SDimitry Andric       if (isa<FunctionSummary>(SummaryIt.second)) {
431*0b57cec5SDimitry Andric         A.add("shape", "record", "function");
432*0b57cec5SDimitry Andric       } else if (isa<AliasSummary>(SummaryIt.second)) {
433*0b57cec5SDimitry Andric         A.add("style", "dotted,filled", "alias");
434*0b57cec5SDimitry Andric         A.add("shape", "box");
435*0b57cec5SDimitry Andric       } else {
436*0b57cec5SDimitry Andric         A.add("shape", "Mrecord", "variable");
437*0b57cec5SDimitry Andric         if (Flags.Live && hasReadOnlyFlag(SummaryIt.second))
438*0b57cec5SDimitry Andric           A.addComment("immutable");
439*0b57cec5SDimitry Andric         if (Flags.Live && hasWriteOnlyFlag(SummaryIt.second))
440*0b57cec5SDimitry Andric           A.addComment("writeOnly");
441*0b57cec5SDimitry Andric       }
442*0b57cec5SDimitry Andric       if (Flags.DSOLocal)
443*0b57cec5SDimitry Andric         A.addComment("dsoLocal");
444*0b57cec5SDimitry Andric       if (Flags.CanAutoHide)
445*0b57cec5SDimitry Andric         A.addComment("canAutoHide");
446*0b57cec5SDimitry Andric 
447*0b57cec5SDimitry Andric       auto VI = getValueInfo(SummaryIt.first);
448*0b57cec5SDimitry Andric       A.add("label", getNodeLabel(VI, SummaryIt.second));
449*0b57cec5SDimitry Andric       if (!Flags.Live)
450*0b57cec5SDimitry Andric         A.add("fillcolor", "red", "dead");
451*0b57cec5SDimitry Andric       else if (Flags.NotEligibleToImport)
452*0b57cec5SDimitry Andric         A.add("fillcolor", "yellow", "not eligible to import");
453*0b57cec5SDimitry Andric 
454*0b57cec5SDimitry Andric       OS << "    " << NodeId(ModId, SummaryIt.first) << " " << A.getAsString()
455*0b57cec5SDimitry Andric          << "\n";
456*0b57cec5SDimitry Andric     }
457*0b57cec5SDimitry Andric     OS << "    // Edges:\n";
458*0b57cec5SDimitry Andric 
459*0b57cec5SDimitry Andric     for (auto &SummaryIt : GVSMap) {
460*0b57cec5SDimitry Andric       auto *GVS = SummaryIt.second;
461*0b57cec5SDimitry Andric       for (auto &R : GVS->refs())
462*0b57cec5SDimitry Andric         Draw(SummaryIt.first, R.getGUID(),
463*0b57cec5SDimitry Andric              R.isWriteOnly() ? -1 : (R.isReadOnly() ? -2 : -3));
464*0b57cec5SDimitry Andric 
465*0b57cec5SDimitry Andric       if (auto *AS = dyn_cast_or_null<AliasSummary>(SummaryIt.second)) {
466*0b57cec5SDimitry Andric         Draw(SummaryIt.first, AS->getAliaseeGUID(), -4);
467*0b57cec5SDimitry Andric         continue;
468*0b57cec5SDimitry Andric       }
469*0b57cec5SDimitry Andric 
470*0b57cec5SDimitry Andric       if (auto *FS = dyn_cast_or_null<FunctionSummary>(SummaryIt.second))
471*0b57cec5SDimitry Andric         for (auto &CGEdge : FS->calls())
472*0b57cec5SDimitry Andric           Draw(SummaryIt.first, CGEdge.first.getGUID(),
473*0b57cec5SDimitry Andric                static_cast<int>(CGEdge.second.Hotness));
474*0b57cec5SDimitry Andric     }
475*0b57cec5SDimitry Andric     OS << "  }\n";
476*0b57cec5SDimitry Andric   }
477*0b57cec5SDimitry Andric 
478*0b57cec5SDimitry Andric   OS << "  // Cross-module edges:\n";
479*0b57cec5SDimitry Andric   for (auto &E : CrossModuleEdges) {
480*0b57cec5SDimitry Andric     auto &ModList = NodeMap[E.Dst];
481*0b57cec5SDimitry Andric     if (ModList.empty()) {
482*0b57cec5SDimitry Andric       defineExternalNode(OS, "  ", getValueInfo(E.Dst), E.Dst);
483*0b57cec5SDimitry Andric       // Add fake module to the list to draw an edge to an external node
484*0b57cec5SDimitry Andric       // in the loop below.
485*0b57cec5SDimitry Andric       ModList.push_back(-1);
486*0b57cec5SDimitry Andric     }
487*0b57cec5SDimitry Andric     for (auto DstMod : ModList)
488*0b57cec5SDimitry Andric       // The edge representing call or ref is drawn to every module where target
489*0b57cec5SDimitry Andric       // symbol is defined. When target is a linkonce symbol there can be
490*0b57cec5SDimitry Andric       // multiple edges representing a single call or ref, both intra-module and
491*0b57cec5SDimitry Andric       // cross-module. As we've already drawn all intra-module edges before we
492*0b57cec5SDimitry Andric       // skip it here.
493*0b57cec5SDimitry Andric       if (DstMod != E.SrcMod)
494*0b57cec5SDimitry Andric         DrawEdge("  ", E.SrcMod, E.Src, DstMod, E.Dst, E.Hotness);
495*0b57cec5SDimitry Andric   }
496*0b57cec5SDimitry Andric 
497*0b57cec5SDimitry Andric   OS << "}";
498*0b57cec5SDimitry Andric }
499