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