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