719 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements the module index and summary classes for the10// 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/Support/CommandLine.h"18#include "llvm/Support/Path.h"19#include "llvm/Support/raw_ostream.h"20using namespace llvm;21 22#define DEBUG_TYPE "module-summary-index"23 24STATISTIC(ReadOnlyLiveGVars,25 "Number of live global variables marked read only");26STATISTIC(WriteOnlyLiveGVars,27 "Number of live global variables marked write only");28 29static cl::opt<bool> PropagateAttrs("propagate-attrs", cl::init(true),30 cl::Hidden,31 cl::desc("Propagate attributes in index"));32 33static cl::opt<bool> ImportConstantsWithRefs(34 "import-constants-with-refs", cl::init(true), cl::Hidden,35 cl::desc("Import constant global variables with references"));36 37FunctionSummary FunctionSummary::ExternalNode =38 FunctionSummary::makeDummyFunctionSummary(39 SmallVector<FunctionSummary::EdgeTy, 0>());40 41GlobalValue::VisibilityTypes ValueInfo::getELFVisibility() const {42 bool HasProtected = false;43 for (const auto &S : make_pointee_range(getSummaryList())) {44 if (S.getVisibility() == GlobalValue::HiddenVisibility)45 return GlobalValue::HiddenVisibility;46 if (S.getVisibility() == GlobalValue::ProtectedVisibility)47 HasProtected = true;48 }49 return HasProtected ? GlobalValue::ProtectedVisibility50 : GlobalValue::DefaultVisibility;51}52 53bool ValueInfo::isDSOLocal(bool WithDSOLocalPropagation) const {54 // With DSOLocal propagation done, the flag in evey summary is the same.55 // Check the first one is enough.56 return WithDSOLocalPropagation57 ? getSummaryList().size() && getSummaryList()[0]->isDSOLocal()58 : getSummaryList().size() &&59 llvm::all_of(60 getSummaryList(),61 [](const std::unique_ptr<GlobalValueSummary> &Summary) {62 return Summary->isDSOLocal();63 });64}65 66bool ValueInfo::canAutoHide() const {67 // Can only auto hide if all copies are eligible to auto hide.68 return getSummaryList().size() &&69 llvm::all_of(getSummaryList(),70 [](const std::unique_ptr<GlobalValueSummary> &Summary) {71 return Summary->canAutoHide();72 });73}74 75// Gets the number of readonly and writeonly refs in RefEdgeList76std::pair<unsigned, unsigned> FunctionSummary::specialRefCounts() const {77 // Here we take advantage of having all readonly and writeonly references78 // located in the end of the RefEdgeList.79 auto Refs = refs();80 unsigned RORefCnt = 0, WORefCnt = 0;81 int I;82 for (I = Refs.size() - 1; I >= 0 && Refs[I].isWriteOnly(); --I)83 WORefCnt++;84 for (; I >= 0 && Refs[I].isReadOnly(); --I)85 RORefCnt++;86 return {RORefCnt, WORefCnt};87}88 89uint64_t ModuleSummaryIndex::getFlags() const {90 uint64_t Flags = 0;91 // Flags & 0x4 is reserved. DO NOT REUSE.92 if (withGlobalValueDeadStripping())93 Flags |= 0x1;94 if (skipModuleByDistributedBackend())95 Flags |= 0x2;96 if (enableSplitLTOUnit())97 Flags |= 0x8;98 if (partiallySplitLTOUnits())99 Flags |= 0x10;100 if (withAttributePropagation())101 Flags |= 0x20;102 if (withDSOLocalPropagation())103 Flags |= 0x40;104 if (withWholeProgramVisibility())105 Flags |= 0x80;106 if (withSupportsHotColdNew())107 Flags |= 0x100;108 if (hasUnifiedLTO())109 Flags |= 0x200;110 if (withInternalizeAndPromote())111 Flags |= 0x400;112 return Flags;113}114 115void ModuleSummaryIndex::setFlags(uint64_t Flags) {116 assert(Flags <= 0x7ff && "Unexpected bits in flag");117 // 1 bit: WithGlobalValueDeadStripping flag.118 // Set on combined index only.119 if (Flags & 0x1)120 setWithGlobalValueDeadStripping();121 // 1 bit: SkipModuleByDistributedBackend flag.122 // Set on combined index only.123 if (Flags & 0x2)124 setSkipModuleByDistributedBackend();125 // Flags & 0x4 is reserved. DO NOT REUSE.126 // 1 bit: DisableSplitLTOUnit flag.127 // Set on per module indexes. It is up to the client to validate128 // 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 // 1 bit: WithWholeProgramVisibility flag.144 // Set on combined index only.145 if (Flags & 0x80)146 setWithWholeProgramVisibility();147 // 1 bit: WithSupportsHotColdNew flag.148 // Set on combined index only.149 if (Flags & 0x100)150 setWithSupportsHotColdNew();151 // 1 bit: WithUnifiedLTO flag.152 // Set on combined index only.153 if (Flags & 0x200)154 setUnifiedLTO();155 // 1 bit: WithInternalizeAndPromote flag.156 // Set on combined index only.157 if (Flags & 0x400)158 setWithInternalizeAndPromote();159}160 161// Collect for the given module the list of function it defines162// (GUID -> Summary).163void ModuleSummaryIndex::collectDefinedFunctionsForModule(164 StringRef ModulePath, GVSummaryMapTy &GVSummaryMap) const {165 for (auto &GlobalList : *this) {166 auto GUID = GlobalList.first;167 for (auto &GlobSummary : GlobalList.second.getSummaryList()) {168 auto *Summary = dyn_cast_or_null<FunctionSummary>(GlobSummary.get());169 if (!Summary)170 // Ignore global variable, focus on functions171 continue;172 // Ignore summaries from other modules.173 if (Summary->modulePath() != ModulePath)174 continue;175 GVSummaryMap[GUID] = Summary;176 }177 }178}179 180GlobalValueSummary *181ModuleSummaryIndex::getGlobalValueSummary(uint64_t ValueGUID,182 bool PerModuleIndex) const {183 auto VI = getValueInfo(ValueGUID);184 assert(VI && "GlobalValue not found in index");185 assert((!PerModuleIndex || VI.getSummaryList().size() == 1) &&186 "Expected a single entry per global value in per-module index");187 auto &Summary = VI.getSummaryList()[0];188 return Summary.get();189}190 191bool ModuleSummaryIndex::isGUIDLive(GlobalValue::GUID GUID) const {192 auto VI = getValueInfo(GUID);193 if (!VI)194 return true;195 const auto &SummaryList = VI.getSummaryList();196 if (SummaryList.empty())197 return true;198 for (auto &I : SummaryList)199 if (isGlobalValueLive(I.get()))200 return true;201 return false;202}203 204static void205propagateAttributesToRefs(GlobalValueSummary *S,206 DenseSet<ValueInfo> &MarkedNonReadWriteOnly) {207 // If reference is not readonly or writeonly then referenced summary is not208 // read/writeonly either. Note that:209 // - All references from GlobalVarSummary are conservatively considered as210 // not readonly or writeonly. Tracking them properly requires more complex211 // analysis then we have now.212 //213 // - AliasSummary objects have no refs at all so this function is a no-op214 // for them.215 for (auto &VI : S->refs()) {216 assert(VI.getAccessSpecifier() == 0 || isa<FunctionSummary>(S));217 if (!VI.getAccessSpecifier()) {218 if (!MarkedNonReadWriteOnly.insert(VI).second)219 continue;220 } else if (MarkedNonReadWriteOnly.contains(VI))221 continue;222 for (auto &Ref : VI.getSummaryList())223 // If references to alias is not read/writeonly then aliasee224 // is not read/writeonly225 if (auto *GVS = dyn_cast<GlobalVarSummary>(Ref->getBaseObject())) {226 if (!VI.isReadOnly())227 GVS->setReadOnly(false);228 if (!VI.isWriteOnly())229 GVS->setWriteOnly(false);230 }231 }232}233 234// Do the access attribute and DSOLocal propagation in combined index.235// The goal of attribute propagation is internalization of readonly (RO)236// or writeonly (WO) variables. To determine which variables are RO or WO237// and which are not we take following steps:238// - During analysis we speculatively assign readonly and writeonly239// attribute to all variables which can be internalized. When computing240// function summary we also assign readonly or writeonly attribute to a241// reference if function doesn't modify referenced variable (readonly)242// or doesn't read it (writeonly).243//244// - After computing dead symbols in combined index we do the attribute245// and DSOLocal propagation. During this step we:246// a. clear RO and WO attributes from variables which are preserved or247// can't be imported248// b. clear RO and WO attributes from variables referenced by any global249// variable initializer250// c. clear RO attribute from variable referenced by a function when251// reference is not readonly252// d. clear WO attribute from variable referenced by a function when253// reference is not writeonly254// e. clear IsDSOLocal flag in every summary if any of them is false.255//256// Because of (c, d) we don't internalize variables read by function A257// and modified by function B.258//259// Internalization itself happens in the backend after import is finished260// See internalizeGVsAfterImport.261void ModuleSummaryIndex::propagateAttributes(262 const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols) {263 if (!PropagateAttrs)264 return;265 DenseSet<ValueInfo> MarkedNonReadWriteOnly;266 for (auto &P : *this) {267 bool IsDSOLocal = true;268 for (auto &S : P.second.getSummaryList()) {269 if (!isGlobalValueLive(S.get())) {270 // computeDeadSymbolsAndUpdateIndirectCalls should have marked all271 // copies live. Note that it is possible that there is a GUID collision272 // between internal symbols with the same name in different files of the273 // same name but not enough distinguishing path. Because274 // computeDeadSymbolsAndUpdateIndirectCalls should conservatively mark275 // all copies live we can assert here that all are dead if any copy is276 // dead.277 assert(llvm::none_of(278 P.second.getSummaryList(),279 [&](const std::unique_ptr<GlobalValueSummary> &Summary) {280 return isGlobalValueLive(Summary.get());281 }));282 // We don't examine references from dead objects283 break;284 }285 286 // Global variable can't be marked read/writeonly if it is not eligible287 // to import since we need to ensure that all external references get288 // a local (imported) copy. It also can't be marked read/writeonly if289 // it or any alias (since alias points to the same memory) are preserved290 // or notEligibleToImport, since either of those means there could be291 // writes (or reads in case of writeonly) that are not visible (because292 // preserved means it could have external to DSO writes or reads, and293 // notEligibleToImport means it could have writes or reads via inline294 // assembly leading it to be in the @llvm.*used).295 if (auto *GVS = dyn_cast<GlobalVarSummary>(S->getBaseObject()))296 // Here we intentionally pass S.get() not GVS, because S could be297 // an alias. We don't analyze references here, because we have to298 // know exactly if GV is readonly to do so.299 if (!canImportGlobalVar(S.get(), /* AnalyzeRefs */ false) ||300 GUIDPreservedSymbols.count(P.first)) {301 GVS->setReadOnly(false);302 GVS->setWriteOnly(false);303 }304 propagateAttributesToRefs(S.get(), MarkedNonReadWriteOnly);305 306 // If the flag from any summary is false, the GV is not DSOLocal.307 IsDSOLocal &= S->isDSOLocal();308 }309 if (!IsDSOLocal)310 // Mark the flag in all summaries false so that we can do quick check311 // without going through the whole list.312 for (const std::unique_ptr<GlobalValueSummary> &Summary :313 P.second.getSummaryList())314 Summary->setDSOLocal(false);315 }316 setWithAttributePropagation();317 setWithDSOLocalPropagation();318 if (llvm::AreStatisticsEnabled())319 for (auto &P : *this)320 if (P.second.getSummaryList().size())321 if (auto *GVS = dyn_cast<GlobalVarSummary>(322 P.second.getSummaryList()[0]->getBaseObject()))323 if (isGlobalValueLive(GVS)) {324 if (GVS->maybeReadOnly())325 ReadOnlyLiveGVars++;326 if (GVS->maybeWriteOnly())327 WriteOnlyLiveGVars++;328 }329}330 331bool ModuleSummaryIndex::canImportGlobalVar(const GlobalValueSummary *S,332 bool AnalyzeRefs) const {333 bool CanImportDecl;334 return canImportGlobalVar(S, AnalyzeRefs, CanImportDecl);335}336 337bool ModuleSummaryIndex::canImportGlobalVar(const GlobalValueSummary *S,338 bool AnalyzeRefs,339 bool &CanImportDecl) const {340 auto HasRefsPreventingImport = [this](const GlobalVarSummary *GVS) {341 // We don't analyze GV references during attribute propagation, so342 // GV with non-trivial initializer can be marked either read or343 // write-only.344 // Importing definiton of readonly GV with non-trivial initializer345 // allows us doing some extra optimizations (like converting indirect346 // calls to direct).347 // Definition of writeonly GV with non-trivial initializer should also348 // be imported. Not doing so will result in:349 // a) GV internalization in source module (because it's writeonly)350 // b) Importing of GV declaration to destination module as a result351 // of promotion.352 // c) Link error (external declaration with internal definition).353 // However we do not promote objects referenced by writeonly GV354 // initializer by means of converting it to 'zeroinitializer'355 return !(ImportConstantsWithRefs && GVS->isConstant()) &&356 !isReadOnly(GVS) && !isWriteOnly(GVS) && GVS->refs().size();357 };358 auto *GVS = cast<GlobalVarSummary>(S->getBaseObject());359 360 const bool nonInterposable =361 !GlobalValue::isInterposableLinkage(S->linkage());362 const bool eligibleToImport = !S->notEligibleToImport();363 364 // It's correct to import a global variable only when it is not interposable365 // and eligible to import.366 CanImportDecl = (nonInterposable && eligibleToImport);367 368 // Global variable with non-trivial initializer can be imported369 // if it's readonly. This gives us extra opportunities for constant370 // folding and converting indirect calls to direct calls. We don't371 // analyze GV references during attribute propagation, because we372 // don't know yet if it is readonly or not.373 return nonInterposable && eligibleToImport &&374 (!AnalyzeRefs || !HasRefsPreventingImport(GVS));375}376 377// TODO: write a graphviz dumper for SCCs (see ModuleSummaryIndex::exportToDot)378// then delete this function and update its tests379LLVM_DUMP_METHOD380void ModuleSummaryIndex::dumpSCCs(raw_ostream &O) {381 for (scc_iterator<ModuleSummaryIndex *> I =382 scc_begin<ModuleSummaryIndex *>(this);383 !I.isAtEnd(); ++I) {384 O << "SCC (" << utostr(I->size()) << " node" << (I->size() == 1 ? "" : "s")385 << ") {\n";386 for (const ValueInfo &V : *I) {387 FunctionSummary *F = nullptr;388 if (V.getSummaryList().size())389 F = cast<FunctionSummary>(V.getSummaryList().front().get());390 O << " " << (F == nullptr ? "External" : "") << " " << utostr(V.getGUID())391 << (I.hasCycle() ? " (has cycle)" : "") << "\n";392 }393 O << "}\n";394 }395}396 397namespace {398struct Attributes {399 void add(const Twine &Name, const Twine &Value,400 const Twine &Comment = Twine());401 void addComment(const Twine &Comment);402 std::string getAsString() const;403 404 std::vector<std::string> Attrs;405 std::string Comments;406};407 408struct Edge {409 uint64_t SrcMod;410 int Hotness;411 GlobalValue::GUID Src;412 GlobalValue::GUID Dst;413};414} // namespace415 416void Attributes::add(const Twine &Name, const Twine &Value,417 const Twine &Comment) {418 std::string A = Name.str();419 A += "=\"";420 A += Value.str();421 A += "\"";422 Attrs.push_back(A);423 addComment(Comment);424}425 426void Attributes::addComment(const Twine &Comment) {427 if (!Comment.isTriviallyEmpty()) {428 if (Comments.empty())429 Comments = " // ";430 else431 Comments += ", ";432 Comments += Comment.str();433 }434}435 436std::string Attributes::getAsString() const {437 if (Attrs.empty())438 return "";439 440 std::string Ret = "[";441 for (auto &A : Attrs)442 Ret += A + ",";443 Ret.pop_back();444 Ret += "];";445 Ret += Comments;446 return Ret;447}448 449static std::string linkageToString(GlobalValue::LinkageTypes LT) {450 switch (LT) {451 case GlobalValue::ExternalLinkage:452 return "extern";453 case GlobalValue::AvailableExternallyLinkage:454 return "av_ext";455 case GlobalValue::LinkOnceAnyLinkage:456 return "linkonce";457 case GlobalValue::LinkOnceODRLinkage:458 return "linkonce_odr";459 case GlobalValue::WeakAnyLinkage:460 return "weak";461 case GlobalValue::WeakODRLinkage:462 return "weak_odr";463 case GlobalValue::AppendingLinkage:464 return "appending";465 case GlobalValue::InternalLinkage:466 return "internal";467 case GlobalValue::PrivateLinkage:468 return "private";469 case GlobalValue::ExternalWeakLinkage:470 return "extern_weak";471 case GlobalValue::CommonLinkage:472 return "common";473 }474 475 return "<unknown>";476}477 478static std::string fflagsToString(FunctionSummary::FFlags F) {479 auto FlagValue = [](unsigned V) { return V ? '1' : '0'; };480 char FlagRep[] = {FlagValue(F.ReadNone),481 FlagValue(F.ReadOnly),482 FlagValue(F.NoRecurse),483 FlagValue(F.ReturnDoesNotAlias),484 FlagValue(F.NoInline),485 FlagValue(F.AlwaysInline),486 FlagValue(F.NoUnwind),487 FlagValue(F.MayThrow),488 FlagValue(F.HasUnknownCall),489 FlagValue(F.MustBeUnreachable),490 0};491 492 return FlagRep;493}494 495// Get string representation of function instruction count and flags.496static std::string getSummaryAttributes(GlobalValueSummary* GVS) {497 auto *FS = dyn_cast_or_null<FunctionSummary>(GVS);498 if (!FS)499 return "";500 501 return std::string("inst: ") + std::to_string(FS->instCount()) +502 ", ffl: " + fflagsToString(FS->fflags());503}504 505static std::string getNodeVisualName(GlobalValue::GUID Id) {506 return std::string("@") + std::to_string(Id);507}508 509static std::string getNodeVisualName(const ValueInfo &VI) {510 return VI.name().empty() ? getNodeVisualName(VI.getGUID()) : VI.name().str();511}512 513static std::string getNodeLabel(const ValueInfo &VI, GlobalValueSummary *GVS) {514 if (isa<AliasSummary>(GVS))515 return getNodeVisualName(VI);516 517 std::string Attrs = getSummaryAttributes(GVS);518 std::string Label =519 getNodeVisualName(VI) + "|" + linkageToString(GVS->linkage());520 if (!Attrs.empty())521 Label += std::string(" (") + Attrs + ")";522 Label += "}";523 524 return Label;525}526 527// Write definition of external node, which doesn't have any528// specific module associated with it. Typically this is function529// or variable defined in native object or library.530static void defineExternalNode(raw_ostream &OS, const char *Pfx,531 const ValueInfo &VI, GlobalValue::GUID Id) {532 auto StrId = std::to_string(Id);533 OS << " " << StrId << " [label=\"";534 535 if (VI) {536 OS << getNodeVisualName(VI);537 } else {538 OS << getNodeVisualName(Id);539 }540 OS << "\"]; // defined externally\n";541}542 543static bool hasReadOnlyFlag(const GlobalValueSummary *S) {544 if (auto *GVS = dyn_cast<GlobalVarSummary>(S))545 return GVS->maybeReadOnly();546 return false;547}548 549static bool hasWriteOnlyFlag(const GlobalValueSummary *S) {550 if (auto *GVS = dyn_cast<GlobalVarSummary>(S))551 return GVS->maybeWriteOnly();552 return false;553}554 555static bool hasConstantFlag(const GlobalValueSummary *S) {556 if (auto *GVS = dyn_cast<GlobalVarSummary>(S))557 return GVS->isConstant();558 return false;559}560 561void ModuleSummaryIndex::exportToDot(562 raw_ostream &OS,563 const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols) const {564 std::vector<Edge> CrossModuleEdges;565 DenseMap<GlobalValue::GUID, std::vector<uint64_t>> NodeMap;566 using GVSOrderedMapTy = std::map<GlobalValue::GUID, GlobalValueSummary *>;567 std::map<StringRef, GVSOrderedMapTy> ModuleToDefinedGVS;568 collectDefinedGVSummariesPerModule(ModuleToDefinedGVS);569 570 // Assign an id to each module path for use in graph labels. Since the571 // StringMap iteration order isn't guaranteed, order by path string before572 // assigning ids.573 std::vector<StringRef> ModulePaths;574 for (auto &[ModPath, _] : modulePaths())575 ModulePaths.push_back(ModPath);576 llvm::sort(ModulePaths);577 DenseMap<StringRef, uint64_t> ModuleIdMap;578 for (auto &ModPath : ModulePaths)579 ModuleIdMap.try_emplace(ModPath, ModuleIdMap.size());580 581 // Get node identifier in form MXXX_<GUID>. The MXXX prefix is required,582 // because we may have multiple linkonce functions summaries.583 auto NodeId = [](uint64_t ModId, GlobalValue::GUID Id) {584 return ModId == (uint64_t)-1 ? std::to_string(Id)585 : std::string("M") + std::to_string(ModId) +586 "_" + std::to_string(Id);587 };588 589 auto DrawEdge = [&](const char *Pfx, uint64_t SrcMod, GlobalValue::GUID SrcId,590 uint64_t DstMod, GlobalValue::GUID DstId,591 int TypeOrHotness) {592 // 0 - alias593 // 1 - reference594 // 2 - constant reference595 // 3 - writeonly reference596 // Other value: (hotness - 4).597 TypeOrHotness += 4;598 static const char *EdgeAttrs[] = {599 " [style=dotted]; // alias",600 " [style=dashed]; // ref",601 " [style=dashed,color=forestgreen]; // const-ref",602 " [style=dashed,color=violetred]; // writeOnly-ref",603 " // call (hotness : Unknown)",604 " [color=blue]; // call (hotness : Cold)",605 " // call (hotness : None)",606 " [color=brown]; // call (hotness : Hot)",607 " [style=bold,color=red]; // call (hotness : Critical)"};608 609 assert(static_cast<size_t>(TypeOrHotness) < std::size(EdgeAttrs));610 OS << Pfx << NodeId(SrcMod, SrcId) << " -> " << NodeId(DstMod, DstId)611 << EdgeAttrs[TypeOrHotness] << "\n";612 };613 614 OS << "digraph Summary {\n";615 for (auto &ModIt : ModuleToDefinedGVS) {616 // Will be empty for a just built per-module index, which doesn't setup a617 // module paths table. In that case use 0 as the module id.618 assert(ModuleIdMap.count(ModIt.first) || ModuleIdMap.empty());619 auto ModId = ModuleIdMap.empty() ? 0 : ModuleIdMap[ModIt.first];620 OS << " // Module: " << ModIt.first << "\n";621 OS << " subgraph cluster_" << std::to_string(ModId) << " {\n";622 OS << " style = filled;\n";623 OS << " color = lightgrey;\n";624 OS << " label = \"" << sys::path::filename(ModIt.first) << "\";\n";625 OS << " node [style=filled,fillcolor=lightblue];\n";626 627 auto &GVSMap = ModIt.second;628 auto Draw = [&](GlobalValue::GUID IdFrom, GlobalValue::GUID IdTo, int Hotness) {629 if (!GVSMap.count(IdTo)) {630 CrossModuleEdges.push_back({ModId, Hotness, IdFrom, IdTo});631 return;632 }633 DrawEdge(" ", ModId, IdFrom, ModId, IdTo, Hotness);634 };635 636 for (auto &SummaryIt : GVSMap) {637 NodeMap[SummaryIt.first].push_back(ModId);638 auto Flags = SummaryIt.second->flags();639 Attributes A;640 if (isa<FunctionSummary>(SummaryIt.second)) {641 A.add("shape", "record", "function");642 } else if (isa<AliasSummary>(SummaryIt.second)) {643 A.add("style", "dotted,filled", "alias");644 A.add("shape", "box");645 } else {646 A.add("shape", "Mrecord", "variable");647 if (Flags.Live && hasReadOnlyFlag(SummaryIt.second))648 A.addComment("immutable");649 if (Flags.Live && hasWriteOnlyFlag(SummaryIt.second))650 A.addComment("writeOnly");651 if (Flags.Live && hasConstantFlag(SummaryIt.second))652 A.addComment("constant");653 }654 if (Flags.Visibility)655 A.addComment("visibility");656 if (Flags.DSOLocal)657 A.addComment("dsoLocal");658 if (Flags.CanAutoHide)659 A.addComment("canAutoHide");660 if (Flags.ImportType == GlobalValueSummary::ImportKind::Definition)661 A.addComment("definition");662 else if (Flags.ImportType == GlobalValueSummary::ImportKind::Declaration)663 A.addComment("declaration");664 if (GUIDPreservedSymbols.count(SummaryIt.first))665 A.addComment("preserved");666 667 auto VI = getValueInfo(SummaryIt.first);668 A.add("label", getNodeLabel(VI, SummaryIt.second));669 if (!Flags.Live)670 A.add("fillcolor", "red", "dead");671 else if (Flags.NotEligibleToImport)672 A.add("fillcolor", "yellow", "not eligible to import");673 674 OS << " " << NodeId(ModId, SummaryIt.first) << " " << A.getAsString()675 << "\n";676 }677 OS << " // Edges:\n";678 679 for (auto &SummaryIt : GVSMap) {680 auto *GVS = SummaryIt.second;681 for (auto &R : GVS->refs())682 Draw(SummaryIt.first, R.getGUID(),683 R.isWriteOnly() ? -1 : (R.isReadOnly() ? -2 : -3));684 685 if (auto *AS = dyn_cast_or_null<AliasSummary>(SummaryIt.second)) {686 Draw(SummaryIt.first, AS->getAliaseeGUID(), -4);687 continue;688 }689 690 if (auto *FS = dyn_cast_or_null<FunctionSummary>(SummaryIt.second))691 for (auto &CGEdge : FS->calls())692 Draw(SummaryIt.first, CGEdge.first.getGUID(),693 static_cast<int>(CGEdge.second.Hotness));694 }695 OS << " }\n";696 }697 698 OS << " // Cross-module edges:\n";699 for (auto &E : CrossModuleEdges) {700 auto &ModList = NodeMap[E.Dst];701 if (ModList.empty()) {702 defineExternalNode(OS, " ", getValueInfo(E.Dst), E.Dst);703 // Add fake module to the list to draw an edge to an external node704 // in the loop below.705 ModList.push_back(-1);706 }707 for (auto DstMod : ModList)708 // The edge representing call or ref is drawn to every module where target709 // symbol is defined. When target is a linkonce symbol there can be710 // multiple edges representing a single call or ref, both intra-module and711 // cross-module. As we've already drawn all intra-module edges before we712 // skip it here.713 if (DstMod != E.SrcMod)714 DrawEdge(" ", E.SrcMod, E.Src, DstMod, E.Dst, E.Hotness);715 }716 717 OS << "}";718}719