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/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 27 FunctionSummary FunctionSummary::ExternalNode = 28 FunctionSummary::makeDummyFunctionSummary({}); 29 bool ValueInfo::isDSOLocal() const { 30 // Need to check all summaries are local in case of hash collisions. 31 return getSummaryList().size() && 32 llvm::all_of(getSummaryList(), 33 [](const std::unique_ptr<GlobalValueSummary> &Summary) { 34 return Summary->isDSOLocal(); 35 }); 36 } 37 38 // Gets the number of immutable refs in RefEdgeList 39 unsigned FunctionSummary::immutableRefCount() const { 40 // Here we take advantage of having all readonly references 41 // located in the end of the RefEdgeList. 42 auto Refs = refs(); 43 unsigned ImmutableRefCnt = 0; 44 for (int I = Refs.size() - 1; I >= 0 && Refs[I].isReadOnly(); --I) 45 ImmutableRefCnt++; 46 return ImmutableRefCnt; 47 } 48 49 // Collect for the given module the list of function it defines 50 // (GUID -> Summary). 51 void ModuleSummaryIndex::collectDefinedFunctionsForModule( 52 StringRef ModulePath, GVSummaryMapTy &GVSummaryMap) const { 53 for (auto &GlobalList : *this) { 54 auto GUID = GlobalList.first; 55 for (auto &GlobSummary : GlobalList.second.SummaryList) { 56 auto *Summary = dyn_cast_or_null<FunctionSummary>(GlobSummary.get()); 57 if (!Summary) 58 // Ignore global variable, focus on functions 59 continue; 60 // Ignore summaries from other modules. 61 if (Summary->modulePath() != ModulePath) 62 continue; 63 GVSummaryMap[GUID] = Summary; 64 } 65 } 66 } 67 68 // Collect for each module the list of function it defines (GUID -> Summary). 69 void ModuleSummaryIndex::collectDefinedGVSummariesPerModule( 70 StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries) const { 71 for (auto &GlobalList : *this) { 72 auto GUID = GlobalList.first; 73 for (auto &Summary : GlobalList.second.SummaryList) { 74 ModuleToDefinedGVSummaries[Summary->modulePath()][GUID] = Summary.get(); 75 } 76 } 77 } 78 79 GlobalValueSummary * 80 ModuleSummaryIndex::getGlobalValueSummary(uint64_t ValueGUID, 81 bool PerModuleIndex) const { 82 auto VI = getValueInfo(ValueGUID); 83 assert(VI && "GlobalValue not found in index"); 84 assert((!PerModuleIndex || VI.getSummaryList().size() == 1) && 85 "Expected a single entry per global value in per-module index"); 86 auto &Summary = VI.getSummaryList()[0]; 87 return Summary.get(); 88 } 89 90 bool ModuleSummaryIndex::isGUIDLive(GlobalValue::GUID GUID) const { 91 auto VI = getValueInfo(GUID); 92 if (!VI) 93 return true; 94 const auto &SummaryList = VI.getSummaryList(); 95 if (SummaryList.empty()) 96 return true; 97 for (auto &I : SummaryList) 98 if (isGlobalValueLive(I.get())) 99 return true; 100 return false; 101 } 102 103 static void propagateConstantsToRefs(GlobalValueSummary *S) { 104 // If reference is not readonly then referenced summary is not 105 // readonly either. Note that: 106 // - All references from GlobalVarSummary are conservatively considered as 107 // not readonly. Tracking them properly requires more complex analysis 108 // then we have now. 109 // 110 // - AliasSummary objects have no refs at all so this function is a no-op 111 // for them. 112 for (auto &VI : S->refs()) { 113 if (VI.isReadOnly()) { 114 // We only mark refs as readonly when computing function summaries on 115 // analysis phase. 116 assert(isa<FunctionSummary>(S)); 117 continue; 118 } 119 for (auto &Ref : VI.getSummaryList()) 120 // If references to alias is not readonly then aliasee is not readonly 121 if (auto *GVS = dyn_cast<GlobalVarSummary>(Ref->getBaseObject())) 122 GVS->setReadOnly(false); 123 } 124 } 125 126 // Do the constant propagation in combined index. 127 // The goal of constant propagation is internalization of readonly 128 // variables. To determine which variables are readonly and which 129 // are not we take following steps: 130 // - During analysis we speculatively assign readonly attribute to 131 // all variables which can be internalized. When computing function 132 // summary we also assign readonly attribute to a reference if 133 // function doesn't modify referenced variable. 134 // 135 // - After computing dead symbols in combined index we do the constant 136 // propagation. During this step we clear readonly attribute from 137 // all variables which: 138 // a. are preserved or can't be imported 139 // b. referenced by any global variable initializer 140 // c. referenced by a function and reference is not readonly 141 // 142 // Internalization itself happens in the backend after import is finished 143 // See internalizeImmutableGVs. 144 void ModuleSummaryIndex::propagateConstants( 145 const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols) { 146 for (auto &P : *this) 147 for (auto &S : P.second.SummaryList) { 148 if (!isGlobalValueLive(S.get())) 149 // We don't examine references from dead objects 150 continue; 151 152 // Global variable can't be marked read only if it is not eligible 153 // to import since we need to ensure that all external references 154 // get a local (imported) copy. It also can't be marked read only 155 // if it or any alias (since alias points to the same memory) are 156 // preserved or notEligibleToImport, since either of those means 157 // there could be writes that are not visible (because preserved 158 // means it could have external to DSO writes, and notEligibleToImport 159 // means it could have writes via inline assembly leading it to be 160 // in the @llvm.*used). 161 if (auto *GVS = dyn_cast<GlobalVarSummary>(S->getBaseObject())) 162 // Here we intentionally pass S.get() not GVS, because S could be 163 // an alias. 164 if (!canImportGlobalVar(S.get()) || GUIDPreservedSymbols.count(P.first)) 165 GVS->setReadOnly(false); 166 propagateConstantsToRefs(S.get()); 167 } 168 if (llvm::AreStatisticsEnabled()) 169 for (auto &P : *this) 170 if (P.second.SummaryList.size()) 171 if (auto *GVS = dyn_cast<GlobalVarSummary>( 172 P.second.SummaryList[0]->getBaseObject())) 173 if (isGlobalValueLive(GVS) && GVS->isReadOnly()) 174 ReadOnlyLiveGVars++; 175 } 176 177 // TODO: write a graphviz dumper for SCCs (see ModuleSummaryIndex::exportToDot) 178 // then delete this function and update its tests 179 LLVM_DUMP_METHOD 180 void ModuleSummaryIndex::dumpSCCs(raw_ostream &O) { 181 for (scc_iterator<ModuleSummaryIndex *> I = 182 scc_begin<ModuleSummaryIndex *>(this); 183 !I.isAtEnd(); ++I) { 184 O << "SCC (" << utostr(I->size()) << " node" << (I->size() == 1 ? "" : "s") 185 << ") {\n"; 186 for (const ValueInfo V : *I) { 187 FunctionSummary *F = nullptr; 188 if (V.getSummaryList().size()) 189 F = cast<FunctionSummary>(V.getSummaryList().front().get()); 190 O << " " << (F == nullptr ? "External" : "") << " " << utostr(V.getGUID()) 191 << (I.hasLoop() ? " (has loop)" : "") << "\n"; 192 } 193 O << "}\n"; 194 } 195 } 196 197 namespace { 198 struct Attributes { 199 void add(const Twine &Name, const Twine &Value, 200 const Twine &Comment = Twine()); 201 void addComment(const Twine &Comment); 202 std::string getAsString() const; 203 204 std::vector<std::string> Attrs; 205 std::string Comments; 206 }; 207 208 struct Edge { 209 uint64_t SrcMod; 210 int Hotness; 211 GlobalValue::GUID Src; 212 GlobalValue::GUID Dst; 213 }; 214 } 215 216 void Attributes::add(const Twine &Name, const Twine &Value, 217 const Twine &Comment) { 218 std::string A = Name.str(); 219 A += "=\""; 220 A += Value.str(); 221 A += "\""; 222 Attrs.push_back(A); 223 addComment(Comment); 224 } 225 226 void Attributes::addComment(const Twine &Comment) { 227 if (!Comment.isTriviallyEmpty()) { 228 if (Comments.empty()) 229 Comments = " // "; 230 else 231 Comments += ", "; 232 Comments += Comment.str(); 233 } 234 } 235 236 std::string Attributes::getAsString() const { 237 if (Attrs.empty()) 238 return ""; 239 240 std::string Ret = "["; 241 for (auto &A : Attrs) 242 Ret += A + ","; 243 Ret.pop_back(); 244 Ret += "];"; 245 Ret += Comments; 246 return Ret; 247 } 248 249 static std::string linkageToString(GlobalValue::LinkageTypes LT) { 250 switch (LT) { 251 case GlobalValue::ExternalLinkage: 252 return "extern"; 253 case GlobalValue::AvailableExternallyLinkage: 254 return "av_ext"; 255 case GlobalValue::LinkOnceAnyLinkage: 256 return "linkonce"; 257 case GlobalValue::LinkOnceODRLinkage: 258 return "linkonce_odr"; 259 case GlobalValue::WeakAnyLinkage: 260 return "weak"; 261 case GlobalValue::WeakODRLinkage: 262 return "weak_odr"; 263 case GlobalValue::AppendingLinkage: 264 return "appending"; 265 case GlobalValue::InternalLinkage: 266 return "internal"; 267 case GlobalValue::PrivateLinkage: 268 return "private"; 269 case GlobalValue::ExternalWeakLinkage: 270 return "extern_weak"; 271 case GlobalValue::CommonLinkage: 272 return "common"; 273 } 274 275 return "<unknown>"; 276 } 277 278 static std::string fflagsToString(FunctionSummary::FFlags F) { 279 auto FlagValue = [](unsigned V) { return V ? '1' : '0'; }; 280 char FlagRep[] = {FlagValue(F.ReadNone), FlagValue(F.ReadOnly), 281 FlagValue(F.NoRecurse), FlagValue(F.ReturnDoesNotAlias), 282 FlagValue(F.NoInline), 0}; 283 284 return FlagRep; 285 } 286 287 // Get string representation of function instruction count and flags. 288 static std::string getSummaryAttributes(GlobalValueSummary* GVS) { 289 auto *FS = dyn_cast_or_null<FunctionSummary>(GVS); 290 if (!FS) 291 return ""; 292 293 return std::string("inst: ") + std::to_string(FS->instCount()) + 294 ", ffl: " + fflagsToString(FS->fflags()); 295 } 296 297 static std::string getNodeVisualName(GlobalValue::GUID Id) { 298 return std::string("@") + std::to_string(Id); 299 } 300 301 static std::string getNodeVisualName(const ValueInfo &VI) { 302 return VI.name().empty() ? getNodeVisualName(VI.getGUID()) : VI.name().str(); 303 } 304 305 static std::string getNodeLabel(const ValueInfo &VI, GlobalValueSummary *GVS) { 306 if (isa<AliasSummary>(GVS)) 307 return getNodeVisualName(VI); 308 309 std::string Attrs = getSummaryAttributes(GVS); 310 std::string Label = 311 getNodeVisualName(VI) + "|" + linkageToString(GVS->linkage()); 312 if (!Attrs.empty()) 313 Label += std::string(" (") + Attrs + ")"; 314 Label += "}"; 315 316 return Label; 317 } 318 319 // Write definition of external node, which doesn't have any 320 // specific module associated with it. Typically this is function 321 // or variable defined in native object or library. 322 static void defineExternalNode(raw_ostream &OS, const char *Pfx, 323 const ValueInfo &VI, GlobalValue::GUID Id) { 324 auto StrId = std::to_string(Id); 325 OS << " " << StrId << " [label=\""; 326 327 if (VI) { 328 OS << getNodeVisualName(VI); 329 } else { 330 OS << getNodeVisualName(Id); 331 } 332 OS << "\"]; // defined externally\n"; 333 } 334 335 static bool hasReadOnlyFlag(const GlobalValueSummary *S) { 336 if (auto *GVS = dyn_cast<GlobalVarSummary>(S)) 337 return GVS->isReadOnly(); 338 return false; 339 } 340 341 void ModuleSummaryIndex::exportToDot(raw_ostream &OS) const { 342 std::vector<Edge> CrossModuleEdges; 343 DenseMap<GlobalValue::GUID, std::vector<uint64_t>> NodeMap; 344 StringMap<GVSummaryMapTy> ModuleToDefinedGVS; 345 collectDefinedGVSummariesPerModule(ModuleToDefinedGVS); 346 347 // Get node identifier in form MXXX_<GUID>. The MXXX prefix is required, 348 // because we may have multiple linkonce functions summaries. 349 auto NodeId = [](uint64_t ModId, GlobalValue::GUID Id) { 350 return ModId == (uint64_t)-1 ? std::to_string(Id) 351 : std::string("M") + std::to_string(ModId) + 352 "_" + std::to_string(Id); 353 }; 354 355 auto DrawEdge = [&](const char *Pfx, uint64_t SrcMod, GlobalValue::GUID SrcId, 356 uint64_t DstMod, GlobalValue::GUID DstId, 357 int TypeOrHotness) { 358 // 0 - alias 359 // 1 - reference 360 // 2 - constant reference 361 // Other value: (hotness - 3). 362 TypeOrHotness += 3; 363 static const char *EdgeAttrs[] = { 364 " [style=dotted]; // alias", 365 " [style=dashed]; // ref", 366 " [style=dashed,color=forestgreen]; // const-ref", 367 " // call (hotness : Unknown)", 368 " [color=blue]; // call (hotness : Cold)", 369 " // call (hotness : None)", 370 " [color=brown]; // call (hotness : Hot)", 371 " [style=bold,color=red]; // call (hotness : Critical)"}; 372 373 assert(static_cast<size_t>(TypeOrHotness) < 374 sizeof(EdgeAttrs) / sizeof(EdgeAttrs[0])); 375 OS << Pfx << NodeId(SrcMod, SrcId) << " -> " << NodeId(DstMod, DstId) 376 << EdgeAttrs[TypeOrHotness] << "\n"; 377 }; 378 379 OS << "digraph Summary {\n"; 380 for (auto &ModIt : ModuleToDefinedGVS) { 381 auto ModId = getModuleId(ModIt.first()); 382 OS << " // Module: " << ModIt.first() << "\n"; 383 OS << " subgraph cluster_" << std::to_string(ModId) << " {\n"; 384 OS << " style = filled;\n"; 385 OS << " color = lightgrey;\n"; 386 OS << " label = \"" << sys::path::filename(ModIt.first()) << "\";\n"; 387 OS << " node [style=filled,fillcolor=lightblue];\n"; 388 389 auto &GVSMap = ModIt.second; 390 auto Draw = [&](GlobalValue::GUID IdFrom, GlobalValue::GUID IdTo, int Hotness) { 391 if (!GVSMap.count(IdTo)) { 392 CrossModuleEdges.push_back({ModId, Hotness, IdFrom, IdTo}); 393 return; 394 } 395 DrawEdge(" ", ModId, IdFrom, ModId, IdTo, Hotness); 396 }; 397 398 for (auto &SummaryIt : GVSMap) { 399 NodeMap[SummaryIt.first].push_back(ModId); 400 auto Flags = SummaryIt.second->flags(); 401 Attributes A; 402 if (isa<FunctionSummary>(SummaryIt.second)) { 403 A.add("shape", "record", "function"); 404 } else if (isa<AliasSummary>(SummaryIt.second)) { 405 A.add("style", "dotted,filled", "alias"); 406 A.add("shape", "box"); 407 } else { 408 A.add("shape", "Mrecord", "variable"); 409 if (Flags.Live && hasReadOnlyFlag(SummaryIt.second)) 410 A.addComment("immutable"); 411 } 412 413 auto VI = getValueInfo(SummaryIt.first); 414 A.add("label", getNodeLabel(VI, SummaryIt.second)); 415 if (!Flags.Live) 416 A.add("fillcolor", "red", "dead"); 417 else if (Flags.NotEligibleToImport) 418 A.add("fillcolor", "yellow", "not eligible to import"); 419 420 OS << " " << NodeId(ModId, SummaryIt.first) << " " << A.getAsString() 421 << "\n"; 422 } 423 OS << " // Edges:\n"; 424 425 for (auto &SummaryIt : GVSMap) { 426 auto *GVS = SummaryIt.second; 427 for (auto &R : GVS->refs()) 428 Draw(SummaryIt.first, R.getGUID(), R.isReadOnly() ? -1 : -2); 429 430 if (auto *AS = dyn_cast_or_null<AliasSummary>(SummaryIt.second)) { 431 GlobalValue::GUID AliaseeId; 432 if (AS->hasAliaseeGUID()) 433 AliaseeId = AS->getAliaseeGUID(); 434 else { 435 auto AliaseeOrigId = AS->getAliasee().getOriginalName(); 436 AliaseeId = getGUIDFromOriginalID(AliaseeOrigId); 437 if (!AliaseeId) 438 AliaseeId = AliaseeOrigId; 439 } 440 441 Draw(SummaryIt.first, AliaseeId, -3); 442 continue; 443 } 444 445 if (auto *FS = dyn_cast_or_null<FunctionSummary>(SummaryIt.second)) 446 for (auto &CGEdge : FS->calls()) 447 Draw(SummaryIt.first, CGEdge.first.getGUID(), 448 static_cast<int>(CGEdge.second.Hotness)); 449 } 450 OS << " }\n"; 451 } 452 453 OS << " // Cross-module edges:\n"; 454 for (auto &E : CrossModuleEdges) { 455 auto &ModList = NodeMap[E.Dst]; 456 if (ModList.empty()) { 457 defineExternalNode(OS, " ", getValueInfo(E.Dst), E.Dst); 458 // Add fake module to the list to draw an edge to an external node 459 // in the loop below. 460 ModList.push_back(-1); 461 } 462 for (auto DstMod : ModList) 463 // The edge representing call or ref is drawn to every module where target 464 // symbol is defined. When target is a linkonce symbol there can be 465 // multiple edges representing a single call or ref, both intra-module and 466 // cross-module. As we've already drawn all intra-module edges before we 467 // skip it here. 468 if (DstMod != E.SrcMod) 469 DrawEdge(" ", E.SrcMod, E.Src, DstMod, E.Dst, E.Hotness); 470 } 471 472 OS << "}"; 473 } 474