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