1 //===-- ModuleSummaryIndex.cpp - Module Summary Index ---------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file implements the module index and summary classes for the
10 // IR library.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "llvm/IR/ModuleSummaryIndex.h"
15 #include "llvm/ADT/SCCIterator.h"
16 #include "llvm/ADT/Statistic.h"
17 #include "llvm/ADT/StringMap.h"
18 #include "llvm/Support/CommandLine.h"
19 #include "llvm/Support/Path.h"
20 #include "llvm/Support/raw_ostream.h"
21 using namespace llvm;
22 
23 #define DEBUG_TYPE "module-summary-index"
24 
25 STATISTIC(ReadOnlyLiveGVars,
26           "Number of live global variables marked read only");
27 STATISTIC(WriteOnlyLiveGVars,
28           "Number of live global variables marked write only");
29 
30 static cl::opt<bool> PropagateAttrs("propagate-attrs", cl::init(true),
31                                     cl::Hidden,
32                                     cl::desc("Propagate attributes in index"));
33 
34 static cl::opt<bool> ImportConstantsWithRefs(
35     "import-constants-with-refs", cl::init(true), cl::Hidden,
36     cl::desc("Import constant global variables with references"));
37 
38 constexpr uint32_t FunctionSummary::ParamAccess::RangeWidth;
39 
40 FunctionSummary FunctionSummary::ExternalNode =
41     FunctionSummary::makeDummyFunctionSummary({});
42 
getELFVisibility() const43 GlobalValue::VisibilityTypes ValueInfo::getELFVisibility() const {
44   bool HasProtected = false;
45   for (const auto &S : make_pointee_range(getSummaryList())) {
46     if (S.getVisibility() == GlobalValue::HiddenVisibility)
47       return GlobalValue::HiddenVisibility;
48     if (S.getVisibility() == GlobalValue::ProtectedVisibility)
49       HasProtected = true;
50   }
51   return HasProtected ? GlobalValue::ProtectedVisibility
52                       : GlobalValue::DefaultVisibility;
53 }
54 
isDSOLocal(bool WithDSOLocalPropagation) const55 bool ValueInfo::isDSOLocal(bool WithDSOLocalPropagation) const {
56   // With DSOLocal propagation done, the flag in evey summary is the same.
57   // Check the first one is enough.
58   return WithDSOLocalPropagation
59              ? getSummaryList().size() && getSummaryList()[0]->isDSOLocal()
60              : getSummaryList().size() &&
61                    llvm::all_of(
62                        getSummaryList(),
63                        [](const std::unique_ptr<GlobalValueSummary> &Summary) {
64                          return Summary->isDSOLocal();
65                        });
66 }
67 
canAutoHide() const68 bool ValueInfo::canAutoHide() const {
69   // Can only auto hide if all copies are eligible to auto hide.
70   return getSummaryList().size() &&
71          llvm::all_of(getSummaryList(),
72                       [](const std::unique_ptr<GlobalValueSummary> &Summary) {
73                         return Summary->canAutoHide();
74                       });
75 }
76 
77 // Gets the number of readonly and writeonly refs in RefEdgeList
specialRefCounts() const78 std::pair<unsigned, unsigned> FunctionSummary::specialRefCounts() const {
79   // Here we take advantage of having all readonly and writeonly references
80   // located in the end of the RefEdgeList.
81   auto Refs = refs();
82   unsigned RORefCnt = 0, WORefCnt = 0;
83   int I;
84   for (I = Refs.size() - 1; I >= 0 && Refs[I].isWriteOnly(); --I)
85     WORefCnt++;
86   for (; I >= 0 && Refs[I].isReadOnly(); --I)
87     RORefCnt++;
88   return {RORefCnt, WORefCnt};
89 }
90 
91 constexpr uint64_t ModuleSummaryIndex::BitcodeSummaryVersion;
92 
getFlags() const93 uint64_t ModuleSummaryIndex::getFlags() const {
94   uint64_t Flags = 0;
95   if (withGlobalValueDeadStripping())
96     Flags |= 0x1;
97   if (skipModuleByDistributedBackend())
98     Flags |= 0x2;
99   if (hasSyntheticEntryCounts())
100     Flags |= 0x4;
101   if (enableSplitLTOUnit())
102     Flags |= 0x8;
103   if (partiallySplitLTOUnits())
104     Flags |= 0x10;
105   if (withAttributePropagation())
106     Flags |= 0x20;
107   if (withDSOLocalPropagation())
108     Flags |= 0x40;
109   return Flags;
110 }
111 
setFlags(uint64_t Flags)112 void ModuleSummaryIndex::setFlags(uint64_t Flags) {
113   assert(Flags <= 0x7f && "Unexpected bits in flag");
114   // 1 bit: WithGlobalValueDeadStripping flag.
115   // Set on combined index only.
116   if (Flags & 0x1)
117     setWithGlobalValueDeadStripping();
118   // 1 bit: SkipModuleByDistributedBackend flag.
119   // Set on combined index only.
120   if (Flags & 0x2)
121     setSkipModuleByDistributedBackend();
122   // 1 bit: HasSyntheticEntryCounts flag.
123   // Set on combined index only.
124   if (Flags & 0x4)
125     setHasSyntheticEntryCounts();
126   // 1 bit: DisableSplitLTOUnit flag.
127   // Set on per module indexes. It is up to the client to validate
128   // the consistency of this flag across modules being linked.
129   if (Flags & 0x8)
130     setEnableSplitLTOUnit();
131   // 1 bit: PartiallySplitLTOUnits flag.
132   // Set on combined index only.
133   if (Flags & 0x10)
134     setPartiallySplitLTOUnits();
135   // 1 bit: WithAttributePropagation flag.
136   // Set on combined index only.
137   if (Flags & 0x20)
138     setWithAttributePropagation();
139   // 1 bit: WithDSOLocalPropagation flag.
140   // Set on combined index only.
141   if (Flags & 0x40)
142     setWithDSOLocalPropagation();
143 }
144 
145 // Collect for the given module the list of function it defines
146 // (GUID -> Summary).
collectDefinedFunctionsForModule(StringRef ModulePath,GVSummaryMapTy & GVSummaryMap) const147 void ModuleSummaryIndex::collectDefinedFunctionsForModule(
148     StringRef ModulePath, GVSummaryMapTy &GVSummaryMap) const {
149   for (auto &GlobalList : *this) {
150     auto GUID = GlobalList.first;
151     for (auto &GlobSummary : GlobalList.second.SummaryList) {
152       auto *Summary = dyn_cast_or_null<FunctionSummary>(GlobSummary.get());
153       if (!Summary)
154         // Ignore global variable, focus on functions
155         continue;
156       // Ignore summaries from other modules.
157       if (Summary->modulePath() != ModulePath)
158         continue;
159       GVSummaryMap[GUID] = Summary;
160     }
161   }
162 }
163 
164 GlobalValueSummary *
getGlobalValueSummary(uint64_t ValueGUID,bool PerModuleIndex) const165 ModuleSummaryIndex::getGlobalValueSummary(uint64_t ValueGUID,
166                                           bool PerModuleIndex) const {
167   auto VI = getValueInfo(ValueGUID);
168   assert(VI && "GlobalValue not found in index");
169   assert((!PerModuleIndex || VI.getSummaryList().size() == 1) &&
170          "Expected a single entry per global value in per-module index");
171   auto &Summary = VI.getSummaryList()[0];
172   return Summary.get();
173 }
174 
isGUIDLive(GlobalValue::GUID GUID) const175 bool ModuleSummaryIndex::isGUIDLive(GlobalValue::GUID GUID) const {
176   auto VI = getValueInfo(GUID);
177   if (!VI)
178     return true;
179   const auto &SummaryList = VI.getSummaryList();
180   if (SummaryList.empty())
181     return true;
182   for (auto &I : SummaryList)
183     if (isGlobalValueLive(I.get()))
184       return true;
185   return false;
186 }
187 
188 static void
propagateAttributesToRefs(GlobalValueSummary * S,DenseSet<ValueInfo> & MarkedNonReadWriteOnly)189 propagateAttributesToRefs(GlobalValueSummary *S,
190                           DenseSet<ValueInfo> &MarkedNonReadWriteOnly) {
191   // If reference is not readonly or writeonly then referenced summary is not
192   // read/writeonly either. Note that:
193   // - All references from GlobalVarSummary are conservatively considered as
194   //   not readonly or writeonly. Tracking them properly requires more complex
195   //   analysis then we have now.
196   //
197   // - AliasSummary objects have no refs at all so this function is a no-op
198   //   for them.
199   for (auto &VI : S->refs()) {
200     assert(VI.getAccessSpecifier() == 0 || isa<FunctionSummary>(S));
201     if (!VI.getAccessSpecifier()) {
202       if (!MarkedNonReadWriteOnly.insert(VI).second)
203         continue;
204     } else if (MarkedNonReadWriteOnly.contains(VI))
205       continue;
206     for (auto &Ref : VI.getSummaryList())
207       // If references to alias is not read/writeonly then aliasee
208       // is not read/writeonly
209       if (auto *GVS = dyn_cast<GlobalVarSummary>(Ref->getBaseObject())) {
210         if (!VI.isReadOnly())
211           GVS->setReadOnly(false);
212         if (!VI.isWriteOnly())
213           GVS->setWriteOnly(false);
214       }
215   }
216 }
217 
218 // Do the access attribute and DSOLocal propagation in combined index.
219 // The goal of attribute propagation is internalization of readonly (RO)
220 // or writeonly (WO) variables. To determine which variables are RO or WO
221 // and which are not we take following steps:
222 // - During analysis we speculatively assign readonly and writeonly
223 //   attribute to all variables which can be internalized. When computing
224 //   function summary we also assign readonly or writeonly attribute to a
225 //   reference if function doesn't modify referenced variable (readonly)
226 //   or doesn't read it (writeonly).
227 //
228 // - After computing dead symbols in combined index we do the attribute
229 //   and DSOLocal propagation. During this step we:
230 //   a. clear RO and WO attributes from variables which are preserved or
231 //      can't be imported
232 //   b. clear RO and WO attributes from variables referenced by any global
233 //      variable initializer
234 //   c. clear RO attribute from variable referenced by a function when
235 //      reference is not readonly
236 //   d. clear WO attribute from variable referenced by a function when
237 //      reference is not writeonly
238 //   e. clear IsDSOLocal flag in every summary if any of them is false.
239 //
240 //   Because of (c, d) we don't internalize variables read by function A
241 //   and modified by function B.
242 //
243 // Internalization itself happens in the backend after import is finished
244 // See internalizeGVsAfterImport.
propagateAttributes(const DenseSet<GlobalValue::GUID> & GUIDPreservedSymbols)245 void ModuleSummaryIndex::propagateAttributes(
246     const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols) {
247   if (!PropagateAttrs)
248     return;
249   DenseSet<ValueInfo> MarkedNonReadWriteOnly;
250   for (auto &P : *this) {
251     bool IsDSOLocal = true;
252     for (auto &S : P.second.SummaryList) {
253       if (!isGlobalValueLive(S.get())) {
254         // computeDeadSymbols should have marked all copies live. Note that
255         // it is possible that there is a GUID collision between internal
256         // symbols with the same name in different files of the same name but
257         // not enough distinguishing path. Because computeDeadSymbols should
258         // conservatively mark all copies live we can assert here that all are
259         // dead if any copy is dead.
260         assert(llvm::none_of(
261             P.second.SummaryList,
262             [&](const std::unique_ptr<GlobalValueSummary> &Summary) {
263               return isGlobalValueLive(Summary.get());
264             }));
265         // We don't examine references from dead objects
266         break;
267       }
268 
269       // Global variable can't be marked read/writeonly if it is not eligible
270       // to import since we need to ensure that all external references get
271       // a local (imported) copy. It also can't be marked read/writeonly if
272       // it or any alias (since alias points to the same memory) are preserved
273       // or notEligibleToImport, since either of those means there could be
274       // writes (or reads in case of writeonly) that are not visible (because
275       // preserved means it could have external to DSO writes or reads, and
276       // notEligibleToImport means it could have writes or reads via inline
277       // assembly leading it to be in the @llvm.*used).
278       if (auto *GVS = dyn_cast<GlobalVarSummary>(S->getBaseObject()))
279         // Here we intentionally pass S.get() not GVS, because S could be
280         // an alias. We don't analyze references here, because we have to
281         // know exactly if GV is readonly to do so.
282         if (!canImportGlobalVar(S.get(), /* AnalyzeRefs */ false) ||
283             GUIDPreservedSymbols.count(P.first)) {
284           GVS->setReadOnly(false);
285           GVS->setWriteOnly(false);
286         }
287       propagateAttributesToRefs(S.get(), MarkedNonReadWriteOnly);
288 
289       // If the flag from any summary is false, the GV is not DSOLocal.
290       IsDSOLocal &= S->isDSOLocal();
291     }
292     if (!IsDSOLocal)
293       // Mark the flag in all summaries false so that we can do quick check
294       // without going through the whole list.
295       for (const std::unique_ptr<GlobalValueSummary> &Summary :
296            P.second.SummaryList)
297         Summary->setDSOLocal(false);
298   }
299   setWithAttributePropagation();
300   setWithDSOLocalPropagation();
301   if (llvm::AreStatisticsEnabled())
302     for (auto &P : *this)
303       if (P.second.SummaryList.size())
304         if (auto *GVS = dyn_cast<GlobalVarSummary>(
305                 P.second.SummaryList[0]->getBaseObject()))
306           if (isGlobalValueLive(GVS)) {
307             if (GVS->maybeReadOnly())
308               ReadOnlyLiveGVars++;
309             if (GVS->maybeWriteOnly())
310               WriteOnlyLiveGVars++;
311           }
312 }
313 
canImportGlobalVar(GlobalValueSummary * S,bool AnalyzeRefs) const314 bool ModuleSummaryIndex::canImportGlobalVar(GlobalValueSummary *S,
315                                             bool AnalyzeRefs) const {
316   auto HasRefsPreventingImport = [this](const GlobalVarSummary *GVS) {
317     // We don't analyze GV references during attribute propagation, so
318     // GV with non-trivial initializer can be marked either read or
319     // write-only.
320     // Importing definiton of readonly GV with non-trivial initializer
321     // allows us doing some extra optimizations (like converting indirect
322     // calls to direct).
323     // Definition of writeonly GV with non-trivial initializer should also
324     // be imported. Not doing so will result in:
325     // a) GV internalization in source module (because it's writeonly)
326     // b) Importing of GV declaration to destination module as a result
327     //    of promotion.
328     // c) Link error (external declaration with internal definition).
329     // However we do not promote objects referenced by writeonly GV
330     // initializer by means of converting it to 'zeroinitializer'
331     return !(ImportConstantsWithRefs && GVS->isConstant()) &&
332            !isReadOnly(GVS) && !isWriteOnly(GVS) && GVS->refs().size();
333   };
334   auto *GVS = cast<GlobalVarSummary>(S->getBaseObject());
335 
336   // Global variable with non-trivial initializer can be imported
337   // if it's readonly. This gives us extra opportunities for constant
338   // folding and converting indirect calls to direct calls. We don't
339   // analyze GV references during attribute propagation, because we
340   // don't know yet if it is readonly or not.
341   return !GlobalValue::isInterposableLinkage(S->linkage()) &&
342          !S->notEligibleToImport() &&
343          (!AnalyzeRefs || !HasRefsPreventingImport(GVS));
344 }
345 
346 // TODO: write a graphviz dumper for SCCs (see ModuleSummaryIndex::exportToDot)
347 // then delete this function and update its tests
348 LLVM_DUMP_METHOD
dumpSCCs(raw_ostream & O)349 void ModuleSummaryIndex::dumpSCCs(raw_ostream &O) {
350   for (scc_iterator<ModuleSummaryIndex *> I =
351            scc_begin<ModuleSummaryIndex *>(this);
352        !I.isAtEnd(); ++I) {
353     O << "SCC (" << utostr(I->size()) << " node" << (I->size() == 1 ? "" : "s")
354       << ") {\n";
355     for (const ValueInfo &V : *I) {
356       FunctionSummary *F = nullptr;
357       if (V.getSummaryList().size())
358         F = cast<FunctionSummary>(V.getSummaryList().front().get());
359       O << " " << (F == nullptr ? "External" : "") << " " << utostr(V.getGUID())
360         << (I.hasCycle() ? " (has cycle)" : "") << "\n";
361     }
362     O << "}\n";
363   }
364 }
365 
366 namespace {
367 struct Attributes {
368   void add(const Twine &Name, const Twine &Value,
369            const Twine &Comment = Twine());
370   void addComment(const Twine &Comment);
371   std::string getAsString() const;
372 
373   std::vector<std::string> Attrs;
374   std::string Comments;
375 };
376 
377 struct Edge {
378   uint64_t SrcMod;
379   int Hotness;
380   GlobalValue::GUID Src;
381   GlobalValue::GUID Dst;
382 };
383 }
384 
add(const Twine & Name,const Twine & Value,const Twine & Comment)385 void Attributes::add(const Twine &Name, const Twine &Value,
386                      const Twine &Comment) {
387   std::string A = Name.str();
388   A += "=\"";
389   A += Value.str();
390   A += "\"";
391   Attrs.push_back(A);
392   addComment(Comment);
393 }
394 
addComment(const Twine & Comment)395 void Attributes::addComment(const Twine &Comment) {
396   if (!Comment.isTriviallyEmpty()) {
397     if (Comments.empty())
398       Comments = " // ";
399     else
400       Comments += ", ";
401     Comments += Comment.str();
402   }
403 }
404 
getAsString() const405 std::string Attributes::getAsString() const {
406   if (Attrs.empty())
407     return "";
408 
409   std::string Ret = "[";
410   for (auto &A : Attrs)
411     Ret += A + ",";
412   Ret.pop_back();
413   Ret += "];";
414   Ret += Comments;
415   return Ret;
416 }
417 
linkageToString(GlobalValue::LinkageTypes LT)418 static std::string linkageToString(GlobalValue::LinkageTypes LT) {
419   switch (LT) {
420   case GlobalValue::ExternalLinkage:
421     return "extern";
422   case GlobalValue::AvailableExternallyLinkage:
423     return "av_ext";
424   case GlobalValue::LinkOnceAnyLinkage:
425     return "linkonce";
426   case GlobalValue::LinkOnceODRLinkage:
427     return "linkonce_odr";
428   case GlobalValue::WeakAnyLinkage:
429     return "weak";
430   case GlobalValue::WeakODRLinkage:
431     return "weak_odr";
432   case GlobalValue::AppendingLinkage:
433     return "appending";
434   case GlobalValue::InternalLinkage:
435     return "internal";
436   case GlobalValue::PrivateLinkage:
437     return "private";
438   case GlobalValue::ExternalWeakLinkage:
439     return "extern_weak";
440   case GlobalValue::CommonLinkage:
441     return "common";
442   }
443 
444   return "<unknown>";
445 }
446 
fflagsToString(FunctionSummary::FFlags F)447 static std::string fflagsToString(FunctionSummary::FFlags F) {
448   auto FlagValue = [](unsigned V) { return V ? '1' : '0'; };
449   char FlagRep[] = {FlagValue(F.ReadNone),     FlagValue(F.ReadOnly),
450                     FlagValue(F.NoRecurse),    FlagValue(F.ReturnDoesNotAlias),
451                     FlagValue(F.NoInline), FlagValue(F.AlwaysInline), 0};
452 
453   return FlagRep;
454 }
455 
456 // Get string representation of function instruction count and flags.
getSummaryAttributes(GlobalValueSummary * GVS)457 static std::string getSummaryAttributes(GlobalValueSummary* GVS) {
458   auto *FS = dyn_cast_or_null<FunctionSummary>(GVS);
459   if (!FS)
460     return "";
461 
462   return std::string("inst: ") + std::to_string(FS->instCount()) +
463          ", ffl: " + fflagsToString(FS->fflags());
464 }
465 
getNodeVisualName(GlobalValue::GUID Id)466 static std::string getNodeVisualName(GlobalValue::GUID Id) {
467   return std::string("@") + std::to_string(Id);
468 }
469 
getNodeVisualName(const ValueInfo & VI)470 static std::string getNodeVisualName(const ValueInfo &VI) {
471   return VI.name().empty() ? getNodeVisualName(VI.getGUID()) : VI.name().str();
472 }
473 
getNodeLabel(const ValueInfo & VI,GlobalValueSummary * GVS)474 static std::string getNodeLabel(const ValueInfo &VI, GlobalValueSummary *GVS) {
475   if (isa<AliasSummary>(GVS))
476     return getNodeVisualName(VI);
477 
478   std::string Attrs = getSummaryAttributes(GVS);
479   std::string Label =
480       getNodeVisualName(VI) + "|" + linkageToString(GVS->linkage());
481   if (!Attrs.empty())
482     Label += std::string(" (") + Attrs + ")";
483   Label += "}";
484 
485   return Label;
486 }
487 
488 // Write definition of external node, which doesn't have any
489 // specific module associated with it. Typically this is function
490 // or variable defined in native object or library.
defineExternalNode(raw_ostream & OS,const char * Pfx,const ValueInfo & VI,GlobalValue::GUID Id)491 static void defineExternalNode(raw_ostream &OS, const char *Pfx,
492                                const ValueInfo &VI, GlobalValue::GUID Id) {
493   auto StrId = std::to_string(Id);
494   OS << "  " << StrId << " [label=\"";
495 
496   if (VI) {
497     OS << getNodeVisualName(VI);
498   } else {
499     OS << getNodeVisualName(Id);
500   }
501   OS << "\"]; // defined externally\n";
502 }
503 
hasReadOnlyFlag(const GlobalValueSummary * S)504 static bool hasReadOnlyFlag(const GlobalValueSummary *S) {
505   if (auto *GVS = dyn_cast<GlobalVarSummary>(S))
506     return GVS->maybeReadOnly();
507   return false;
508 }
509 
hasWriteOnlyFlag(const GlobalValueSummary * S)510 static bool hasWriteOnlyFlag(const GlobalValueSummary *S) {
511   if (auto *GVS = dyn_cast<GlobalVarSummary>(S))
512     return GVS->maybeWriteOnly();
513   return false;
514 }
515 
hasConstantFlag(const GlobalValueSummary * S)516 static bool hasConstantFlag(const GlobalValueSummary *S) {
517   if (auto *GVS = dyn_cast<GlobalVarSummary>(S))
518     return GVS->isConstant();
519   return false;
520 }
521 
exportToDot(raw_ostream & OS,const DenseSet<GlobalValue::GUID> & GUIDPreservedSymbols) const522 void ModuleSummaryIndex::exportToDot(
523     raw_ostream &OS,
524     const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols) const {
525   std::vector<Edge> CrossModuleEdges;
526   DenseMap<GlobalValue::GUID, std::vector<uint64_t>> NodeMap;
527   using GVSOrderedMapTy = std::map<GlobalValue::GUID, GlobalValueSummary *>;
528   std::map<StringRef, GVSOrderedMapTy> ModuleToDefinedGVS;
529   collectDefinedGVSummariesPerModule(ModuleToDefinedGVS);
530 
531   // Get node identifier in form MXXX_<GUID>. The MXXX prefix is required,
532   // because we may have multiple linkonce functions summaries.
533   auto NodeId = [](uint64_t ModId, GlobalValue::GUID Id) {
534     return ModId == (uint64_t)-1 ? std::to_string(Id)
535                                  : std::string("M") + std::to_string(ModId) +
536                                        "_" + std::to_string(Id);
537   };
538 
539   auto DrawEdge = [&](const char *Pfx, uint64_t SrcMod, GlobalValue::GUID SrcId,
540                       uint64_t DstMod, GlobalValue::GUID DstId,
541                       int TypeOrHotness) {
542     // 0 - alias
543     // 1 - reference
544     // 2 - constant reference
545     // 3 - writeonly reference
546     // Other value: (hotness - 4).
547     TypeOrHotness += 4;
548     static const char *EdgeAttrs[] = {
549         " [style=dotted]; // alias",
550         " [style=dashed]; // ref",
551         " [style=dashed,color=forestgreen]; // const-ref",
552         " [style=dashed,color=violetred]; // writeOnly-ref",
553         " // call (hotness : Unknown)",
554         " [color=blue]; // call (hotness : Cold)",
555         " // call (hotness : None)",
556         " [color=brown]; // call (hotness : Hot)",
557         " [style=bold,color=red]; // call (hotness : Critical)"};
558 
559     assert(static_cast<size_t>(TypeOrHotness) <
560            sizeof(EdgeAttrs) / sizeof(EdgeAttrs[0]));
561     OS << Pfx << NodeId(SrcMod, SrcId) << " -> " << NodeId(DstMod, DstId)
562        << EdgeAttrs[TypeOrHotness] << "\n";
563   };
564 
565   OS << "digraph Summary {\n";
566   for (auto &ModIt : ModuleToDefinedGVS) {
567     auto ModId = getModuleId(ModIt.first);
568     OS << "  // Module: " << ModIt.first << "\n";
569     OS << "  subgraph cluster_" << std::to_string(ModId) << " {\n";
570     OS << "    style = filled;\n";
571     OS << "    color = lightgrey;\n";
572     OS << "    label = \"" << sys::path::filename(ModIt.first) << "\";\n";
573     OS << "    node [style=filled,fillcolor=lightblue];\n";
574 
575     auto &GVSMap = ModIt.second;
576     auto Draw = [&](GlobalValue::GUID IdFrom, GlobalValue::GUID IdTo, int Hotness) {
577       if (!GVSMap.count(IdTo)) {
578         CrossModuleEdges.push_back({ModId, Hotness, IdFrom, IdTo});
579         return;
580       }
581       DrawEdge("    ", ModId, IdFrom, ModId, IdTo, Hotness);
582     };
583 
584     for (auto &SummaryIt : GVSMap) {
585       NodeMap[SummaryIt.first].push_back(ModId);
586       auto Flags = SummaryIt.second->flags();
587       Attributes A;
588       if (isa<FunctionSummary>(SummaryIt.second)) {
589         A.add("shape", "record", "function");
590       } else if (isa<AliasSummary>(SummaryIt.second)) {
591         A.add("style", "dotted,filled", "alias");
592         A.add("shape", "box");
593       } else {
594         A.add("shape", "Mrecord", "variable");
595         if (Flags.Live && hasReadOnlyFlag(SummaryIt.second))
596           A.addComment("immutable");
597         if (Flags.Live && hasWriteOnlyFlag(SummaryIt.second))
598           A.addComment("writeOnly");
599         if (Flags.Live && hasConstantFlag(SummaryIt.second))
600           A.addComment("constant");
601       }
602       if (Flags.Visibility)
603         A.addComment("visibility");
604       if (Flags.DSOLocal)
605         A.addComment("dsoLocal");
606       if (Flags.CanAutoHide)
607         A.addComment("canAutoHide");
608       if (GUIDPreservedSymbols.count(SummaryIt.first))
609         A.addComment("preserved");
610 
611       auto VI = getValueInfo(SummaryIt.first);
612       A.add("label", getNodeLabel(VI, SummaryIt.second));
613       if (!Flags.Live)
614         A.add("fillcolor", "red", "dead");
615       else if (Flags.NotEligibleToImport)
616         A.add("fillcolor", "yellow", "not eligible to import");
617 
618       OS << "    " << NodeId(ModId, SummaryIt.first) << " " << A.getAsString()
619          << "\n";
620     }
621     OS << "    // Edges:\n";
622 
623     for (auto &SummaryIt : GVSMap) {
624       auto *GVS = SummaryIt.second;
625       for (auto &R : GVS->refs())
626         Draw(SummaryIt.first, R.getGUID(),
627              R.isWriteOnly() ? -1 : (R.isReadOnly() ? -2 : -3));
628 
629       if (auto *AS = dyn_cast_or_null<AliasSummary>(SummaryIt.second)) {
630         Draw(SummaryIt.first, AS->getAliaseeGUID(), -4);
631         continue;
632       }
633 
634       if (auto *FS = dyn_cast_or_null<FunctionSummary>(SummaryIt.second))
635         for (auto &CGEdge : FS->calls())
636           Draw(SummaryIt.first, CGEdge.first.getGUID(),
637                static_cast<int>(CGEdge.second.Hotness));
638     }
639     OS << "  }\n";
640   }
641 
642   OS << "  // Cross-module edges:\n";
643   for (auto &E : CrossModuleEdges) {
644     auto &ModList = NodeMap[E.Dst];
645     if (ModList.empty()) {
646       defineExternalNode(OS, "  ", getValueInfo(E.Dst), E.Dst);
647       // Add fake module to the list to draw an edge to an external node
648       // in the loop below.
649       ModList.push_back(-1);
650     }
651     for (auto DstMod : ModList)
652       // The edge representing call or ref is drawn to every module where target
653       // symbol is defined. When target is a linkonce symbol there can be
654       // multiple edges representing a single call or ref, both intra-module and
655       // cross-module. As we've already drawn all intra-module edges before we
656       // skip it here.
657       if (DstMod != E.SrcMod)
658         DrawEdge("  ", E.SrcMod, E.Src, DstMod, E.Dst, E.Hotness);
659   }
660 
661   OS << "}";
662 }
663