xref: /freebsd/contrib/llvm-project/llvm/lib/IR/ModuleSummaryIndex.cpp (revision 5e801ac66d24704442eba426ed13c3effb8a34e7)
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 
43 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 
55 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 
68 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
78 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 
93 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 
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).
147 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 *
165 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 
175 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
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.
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         // computeDeadSymbolsAndUpdateIndirectCalls should have marked all
255         // copies live. Note that it is possible that there is a GUID collision
256         // between internal symbols with the same name in different files of the
257         // same name but not enough distinguishing path. Because
258         // computeDeadSymbolsAndUpdateIndirectCalls should conservatively mark
259         // all copies live we can assert here that all are dead if any copy is
260         // dead.
261         assert(llvm::none_of(
262             P.second.SummaryList,
263             [&](const std::unique_ptr<GlobalValueSummary> &Summary) {
264               return isGlobalValueLive(Summary.get());
265             }));
266         // We don't examine references from dead objects
267         break;
268       }
269 
270       // Global variable can't be marked read/writeonly if it is not eligible
271       // to import since we need to ensure that all external references get
272       // a local (imported) copy. It also can't be marked read/writeonly if
273       // it or any alias (since alias points to the same memory) are preserved
274       // or notEligibleToImport, since either of those means there could be
275       // writes (or reads in case of writeonly) that are not visible (because
276       // preserved means it could have external to DSO writes or reads, and
277       // notEligibleToImport means it could have writes or reads via inline
278       // assembly leading it to be in the @llvm.*used).
279       if (auto *GVS = dyn_cast<GlobalVarSummary>(S->getBaseObject()))
280         // Here we intentionally pass S.get() not GVS, because S could be
281         // an alias. We don't analyze references here, because we have to
282         // know exactly if GV is readonly to do so.
283         if (!canImportGlobalVar(S.get(), /* AnalyzeRefs */ false) ||
284             GUIDPreservedSymbols.count(P.first)) {
285           GVS->setReadOnly(false);
286           GVS->setWriteOnly(false);
287         }
288       propagateAttributesToRefs(S.get(), MarkedNonReadWriteOnly);
289 
290       // If the flag from any summary is false, the GV is not DSOLocal.
291       IsDSOLocal &= S->isDSOLocal();
292     }
293     if (!IsDSOLocal)
294       // Mark the flag in all summaries false so that we can do quick check
295       // without going through the whole list.
296       for (const std::unique_ptr<GlobalValueSummary> &Summary :
297            P.second.SummaryList)
298         Summary->setDSOLocal(false);
299   }
300   setWithAttributePropagation();
301   setWithDSOLocalPropagation();
302   if (llvm::AreStatisticsEnabled())
303     for (auto &P : *this)
304       if (P.second.SummaryList.size())
305         if (auto *GVS = dyn_cast<GlobalVarSummary>(
306                 P.second.SummaryList[0]->getBaseObject()))
307           if (isGlobalValueLive(GVS)) {
308             if (GVS->maybeReadOnly())
309               ReadOnlyLiveGVars++;
310             if (GVS->maybeWriteOnly())
311               WriteOnlyLiveGVars++;
312           }
313 }
314 
315 bool ModuleSummaryIndex::canImportGlobalVar(GlobalValueSummary *S,
316                                             bool AnalyzeRefs) const {
317   auto HasRefsPreventingImport = [this](const GlobalVarSummary *GVS) {
318     // We don't analyze GV references during attribute propagation, so
319     // GV with non-trivial initializer can be marked either read or
320     // write-only.
321     // Importing definiton of readonly GV with non-trivial initializer
322     // allows us doing some extra optimizations (like converting indirect
323     // calls to direct).
324     // Definition of writeonly GV with non-trivial initializer should also
325     // be imported. Not doing so will result in:
326     // a) GV internalization in source module (because it's writeonly)
327     // b) Importing of GV declaration to destination module as a result
328     //    of promotion.
329     // c) Link error (external declaration with internal definition).
330     // However we do not promote objects referenced by writeonly GV
331     // initializer by means of converting it to 'zeroinitializer'
332     return !(ImportConstantsWithRefs && GVS->isConstant()) &&
333            !isReadOnly(GVS) && !isWriteOnly(GVS) && GVS->refs().size();
334   };
335   auto *GVS = cast<GlobalVarSummary>(S->getBaseObject());
336 
337   // Global variable with non-trivial initializer can be imported
338   // if it's readonly. This gives us extra opportunities for constant
339   // folding and converting indirect calls to direct calls. We don't
340   // analyze GV references during attribute propagation, because we
341   // don't know yet if it is readonly or not.
342   return !GlobalValue::isInterposableLinkage(S->linkage()) &&
343          !S->notEligibleToImport() &&
344          (!AnalyzeRefs || !HasRefsPreventingImport(GVS));
345 }
346 
347 // TODO: write a graphviz dumper for SCCs (see ModuleSummaryIndex::exportToDot)
348 // then delete this function and update its tests
349 LLVM_DUMP_METHOD
350 void ModuleSummaryIndex::dumpSCCs(raw_ostream &O) {
351   for (scc_iterator<ModuleSummaryIndex *> I =
352            scc_begin<ModuleSummaryIndex *>(this);
353        !I.isAtEnd(); ++I) {
354     O << "SCC (" << utostr(I->size()) << " node" << (I->size() == 1 ? "" : "s")
355       << ") {\n";
356     for (const ValueInfo &V : *I) {
357       FunctionSummary *F = nullptr;
358       if (V.getSummaryList().size())
359         F = cast<FunctionSummary>(V.getSummaryList().front().get());
360       O << " " << (F == nullptr ? "External" : "") << " " << utostr(V.getGUID())
361         << (I.hasCycle() ? " (has cycle)" : "") << "\n";
362     }
363     O << "}\n";
364   }
365 }
366 
367 namespace {
368 struct Attributes {
369   void add(const Twine &Name, const Twine &Value,
370            const Twine &Comment = Twine());
371   void addComment(const Twine &Comment);
372   std::string getAsString() const;
373 
374   std::vector<std::string> Attrs;
375   std::string Comments;
376 };
377 
378 struct Edge {
379   uint64_t SrcMod;
380   int Hotness;
381   GlobalValue::GUID Src;
382   GlobalValue::GUID Dst;
383 };
384 }
385 
386 void Attributes::add(const Twine &Name, const Twine &Value,
387                      const Twine &Comment) {
388   std::string A = Name.str();
389   A += "=\"";
390   A += Value.str();
391   A += "\"";
392   Attrs.push_back(A);
393   addComment(Comment);
394 }
395 
396 void Attributes::addComment(const Twine &Comment) {
397   if (!Comment.isTriviallyEmpty()) {
398     if (Comments.empty())
399       Comments = " // ";
400     else
401       Comments += ", ";
402     Comments += Comment.str();
403   }
404 }
405 
406 std::string Attributes::getAsString() const {
407   if (Attrs.empty())
408     return "";
409 
410   std::string Ret = "[";
411   for (auto &A : Attrs)
412     Ret += A + ",";
413   Ret.pop_back();
414   Ret += "];";
415   Ret += Comments;
416   return Ret;
417 }
418 
419 static std::string linkageToString(GlobalValue::LinkageTypes LT) {
420   switch (LT) {
421   case GlobalValue::ExternalLinkage:
422     return "extern";
423   case GlobalValue::AvailableExternallyLinkage:
424     return "av_ext";
425   case GlobalValue::LinkOnceAnyLinkage:
426     return "linkonce";
427   case GlobalValue::LinkOnceODRLinkage:
428     return "linkonce_odr";
429   case GlobalValue::WeakAnyLinkage:
430     return "weak";
431   case GlobalValue::WeakODRLinkage:
432     return "weak_odr";
433   case GlobalValue::AppendingLinkage:
434     return "appending";
435   case GlobalValue::InternalLinkage:
436     return "internal";
437   case GlobalValue::PrivateLinkage:
438     return "private";
439   case GlobalValue::ExternalWeakLinkage:
440     return "extern_weak";
441   case GlobalValue::CommonLinkage:
442     return "common";
443   }
444 
445   return "<unknown>";
446 }
447 
448 static std::string fflagsToString(FunctionSummary::FFlags F) {
449   auto FlagValue = [](unsigned V) { return V ? '1' : '0'; };
450   char FlagRep[] = {FlagValue(F.ReadNone),  FlagValue(F.ReadOnly),
451                     FlagValue(F.NoRecurse), FlagValue(F.ReturnDoesNotAlias),
452                     FlagValue(F.NoInline),  FlagValue(F.AlwaysInline),
453                     FlagValue(F.NoUnwind),  FlagValue(F.MayThrow),
454                     FlagValue(F.HasUnknownCall), 0};
455 
456   return FlagRep;
457 }
458 
459 // Get string representation of function instruction count and flags.
460 static std::string getSummaryAttributes(GlobalValueSummary* GVS) {
461   auto *FS = dyn_cast_or_null<FunctionSummary>(GVS);
462   if (!FS)
463     return "";
464 
465   return std::string("inst: ") + std::to_string(FS->instCount()) +
466          ", ffl: " + fflagsToString(FS->fflags());
467 }
468 
469 static std::string getNodeVisualName(GlobalValue::GUID Id) {
470   return std::string("@") + std::to_string(Id);
471 }
472 
473 static std::string getNodeVisualName(const ValueInfo &VI) {
474   return VI.name().empty() ? getNodeVisualName(VI.getGUID()) : VI.name().str();
475 }
476 
477 static std::string getNodeLabel(const ValueInfo &VI, GlobalValueSummary *GVS) {
478   if (isa<AliasSummary>(GVS))
479     return getNodeVisualName(VI);
480 
481   std::string Attrs = getSummaryAttributes(GVS);
482   std::string Label =
483       getNodeVisualName(VI) + "|" + linkageToString(GVS->linkage());
484   if (!Attrs.empty())
485     Label += std::string(" (") + Attrs + ")";
486   Label += "}";
487 
488   return Label;
489 }
490 
491 // Write definition of external node, which doesn't have any
492 // specific module associated with it. Typically this is function
493 // or variable defined in native object or library.
494 static void defineExternalNode(raw_ostream &OS, const char *Pfx,
495                                const ValueInfo &VI, GlobalValue::GUID Id) {
496   auto StrId = std::to_string(Id);
497   OS << "  " << StrId << " [label=\"";
498 
499   if (VI) {
500     OS << getNodeVisualName(VI);
501   } else {
502     OS << getNodeVisualName(Id);
503   }
504   OS << "\"]; // defined externally\n";
505 }
506 
507 static bool hasReadOnlyFlag(const GlobalValueSummary *S) {
508   if (auto *GVS = dyn_cast<GlobalVarSummary>(S))
509     return GVS->maybeReadOnly();
510   return false;
511 }
512 
513 static bool hasWriteOnlyFlag(const GlobalValueSummary *S) {
514   if (auto *GVS = dyn_cast<GlobalVarSummary>(S))
515     return GVS->maybeWriteOnly();
516   return false;
517 }
518 
519 static bool hasConstantFlag(const GlobalValueSummary *S) {
520   if (auto *GVS = dyn_cast<GlobalVarSummary>(S))
521     return GVS->isConstant();
522   return false;
523 }
524 
525 void ModuleSummaryIndex::exportToDot(
526     raw_ostream &OS,
527     const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols) const {
528   std::vector<Edge> CrossModuleEdges;
529   DenseMap<GlobalValue::GUID, std::vector<uint64_t>> NodeMap;
530   using GVSOrderedMapTy = std::map<GlobalValue::GUID, GlobalValueSummary *>;
531   std::map<StringRef, GVSOrderedMapTy> ModuleToDefinedGVS;
532   collectDefinedGVSummariesPerModule(ModuleToDefinedGVS);
533 
534   // Get node identifier in form MXXX_<GUID>. The MXXX prefix is required,
535   // because we may have multiple linkonce functions summaries.
536   auto NodeId = [](uint64_t ModId, GlobalValue::GUID Id) {
537     return ModId == (uint64_t)-1 ? std::to_string(Id)
538                                  : std::string("M") + std::to_string(ModId) +
539                                        "_" + std::to_string(Id);
540   };
541 
542   auto DrawEdge = [&](const char *Pfx, uint64_t SrcMod, GlobalValue::GUID SrcId,
543                       uint64_t DstMod, GlobalValue::GUID DstId,
544                       int TypeOrHotness) {
545     // 0 - alias
546     // 1 - reference
547     // 2 - constant reference
548     // 3 - writeonly reference
549     // Other value: (hotness - 4).
550     TypeOrHotness += 4;
551     static const char *EdgeAttrs[] = {
552         " [style=dotted]; // alias",
553         " [style=dashed]; // ref",
554         " [style=dashed,color=forestgreen]; // const-ref",
555         " [style=dashed,color=violetred]; // writeOnly-ref",
556         " // call (hotness : Unknown)",
557         " [color=blue]; // call (hotness : Cold)",
558         " // call (hotness : None)",
559         " [color=brown]; // call (hotness : Hot)",
560         " [style=bold,color=red]; // call (hotness : Critical)"};
561 
562     assert(static_cast<size_t>(TypeOrHotness) <
563            sizeof(EdgeAttrs) / sizeof(EdgeAttrs[0]));
564     OS << Pfx << NodeId(SrcMod, SrcId) << " -> " << NodeId(DstMod, DstId)
565        << EdgeAttrs[TypeOrHotness] << "\n";
566   };
567 
568   OS << "digraph Summary {\n";
569   for (auto &ModIt : ModuleToDefinedGVS) {
570     auto ModId = getModuleId(ModIt.first);
571     OS << "  // Module: " << ModIt.first << "\n";
572     OS << "  subgraph cluster_" << std::to_string(ModId) << " {\n";
573     OS << "    style = filled;\n";
574     OS << "    color = lightgrey;\n";
575     OS << "    label = \"" << sys::path::filename(ModIt.first) << "\";\n";
576     OS << "    node [style=filled,fillcolor=lightblue];\n";
577 
578     auto &GVSMap = ModIt.second;
579     auto Draw = [&](GlobalValue::GUID IdFrom, GlobalValue::GUID IdTo, int Hotness) {
580       if (!GVSMap.count(IdTo)) {
581         CrossModuleEdges.push_back({ModId, Hotness, IdFrom, IdTo});
582         return;
583       }
584       DrawEdge("    ", ModId, IdFrom, ModId, IdTo, Hotness);
585     };
586 
587     for (auto &SummaryIt : GVSMap) {
588       NodeMap[SummaryIt.first].push_back(ModId);
589       auto Flags = SummaryIt.second->flags();
590       Attributes A;
591       if (isa<FunctionSummary>(SummaryIt.second)) {
592         A.add("shape", "record", "function");
593       } else if (isa<AliasSummary>(SummaryIt.second)) {
594         A.add("style", "dotted,filled", "alias");
595         A.add("shape", "box");
596       } else {
597         A.add("shape", "Mrecord", "variable");
598         if (Flags.Live && hasReadOnlyFlag(SummaryIt.second))
599           A.addComment("immutable");
600         if (Flags.Live && hasWriteOnlyFlag(SummaryIt.second))
601           A.addComment("writeOnly");
602         if (Flags.Live && hasConstantFlag(SummaryIt.second))
603           A.addComment("constant");
604       }
605       if (Flags.Visibility)
606         A.addComment("visibility");
607       if (Flags.DSOLocal)
608         A.addComment("dsoLocal");
609       if (Flags.CanAutoHide)
610         A.addComment("canAutoHide");
611       if (GUIDPreservedSymbols.count(SummaryIt.first))
612         A.addComment("preserved");
613 
614       auto VI = getValueInfo(SummaryIt.first);
615       A.add("label", getNodeLabel(VI, SummaryIt.second));
616       if (!Flags.Live)
617         A.add("fillcolor", "red", "dead");
618       else if (Flags.NotEligibleToImport)
619         A.add("fillcolor", "yellow", "not eligible to import");
620 
621       OS << "    " << NodeId(ModId, SummaryIt.first) << " " << A.getAsString()
622          << "\n";
623     }
624     OS << "    // Edges:\n";
625 
626     for (auto &SummaryIt : GVSMap) {
627       auto *GVS = SummaryIt.second;
628       for (auto &R : GVS->refs())
629         Draw(SummaryIt.first, R.getGUID(),
630              R.isWriteOnly() ? -1 : (R.isReadOnly() ? -2 : -3));
631 
632       if (auto *AS = dyn_cast_or_null<AliasSummary>(SummaryIt.second)) {
633         Draw(SummaryIt.first, AS->getAliaseeGUID(), -4);
634         continue;
635       }
636 
637       if (auto *FS = dyn_cast_or_null<FunctionSummary>(SummaryIt.second))
638         for (auto &CGEdge : FS->calls())
639           Draw(SummaryIt.first, CGEdge.first.getGUID(),
640                static_cast<int>(CGEdge.second.Hotness));
641     }
642     OS << "  }\n";
643   }
644 
645   OS << "  // Cross-module edges:\n";
646   for (auto &E : CrossModuleEdges) {
647     auto &ModList = NodeMap[E.Dst];
648     if (ModList.empty()) {
649       defineExternalNode(OS, "  ", getValueInfo(E.Dst), E.Dst);
650       // Add fake module to the list to draw an edge to an external node
651       // in the loop below.
652       ModList.push_back(-1);
653     }
654     for (auto DstMod : ModList)
655       // The edge representing call or ref is drawn to every module where target
656       // symbol is defined. When target is a linkonce symbol there can be
657       // multiple edges representing a single call or ref, both intra-module and
658       // cross-module. As we've already drawn all intra-module edges before we
659       // skip it here.
660       if (DstMod != E.SrcMod)
661         DrawEdge("  ", E.SrcMod, E.Src, DstMod, E.Dst, E.Hotness);
662   }
663 
664   OS << "}";
665 }
666