Index: include/llvm/Transforms/IPO/FunctionImport.h =================================================================== --- include/llvm/Transforms/IPO/FunctionImport.h +++ include/llvm/Transforms/IPO/FunctionImport.h @@ -102,6 +102,15 @@ ModuleSummaryIndex &Index, const DenseSet &GUIDPreservedSymbols); +/// Propagate function attributes for function summaries along the index's +/// callgraph during thinlink +void propagateFunctionAttrs(ModuleSummaryIndex &Index); + +/// Inserts the FunctionAttr flags from the Index (ReadNone, ReadOnly, +/// NoRecurse, NoAlias) into \p TheModule. +void thinLTOInsertFunctionAttrsForModule(Module &TheModule, + const ModuleSummaryIndex &Index); + /// Compute the set of summaries needed for a ThinLTO backend compilation of /// \p ModulePath. // Index: lib/LTO/LTO.cpp =================================================================== --- lib/LTO/LTO.cpp +++ lib/LTO/LTO.cpp @@ -1048,6 +1048,7 @@ if (!ModuleToDefinedGVSummaries.count(Mod.first)) ModuleToDefinedGVSummaries.try_emplace(Mod.first); + propagateFunctionAttrs(ThinLTO.CombinedIndex); StringMap ImportLists( ThinLTO.ModuleMap.size()); StringMap ExportLists( Index: lib/LTO/LTOBackend.cpp =================================================================== --- lib/LTO/LTOBackend.cpp +++ lib/LTO/LTOBackend.cpp @@ -415,6 +415,7 @@ renameModuleForThinLTO(Mod, CombinedIndex); thinLTOResolveWeakForLinkerModule(Mod, DefinedGlobals); + thinLTOInsertFunctionAttrsForModule(Mod, CombinedIndex); if (Conf.PostPromoteModuleHook && !Conf.PostPromoteModuleHook(Task, Mod)) return Error::success(); Index: lib/Transforms/IPO/FunctionImport.cpp =================================================================== --- lib/Transforms/IPO/FunctionImport.cpp +++ lib/Transforms/IPO/FunctionImport.cpp @@ -13,6 +13,7 @@ #include "llvm/Transforms/IPO/FunctionImport.h" +#include "llvm/ADT/SCCIterator.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringSet.h" @@ -40,6 +41,12 @@ STATISTIC(NumImportedModules, "Number of modules imported from"); STATISTIC(NumDeadSymbols, "Number of dead stripped symbols in index"); STATISTIC(NumLiveSymbols, "Number of live symbols in index"); +STATISTIC(NumReadOnly, + "Number of of functions marked readonly from summary propagation"); +STATISTIC(NumReadNone, + "Number of of functions marked readnone from summary propagation"); +STATISTIC(NumNoRecurse, + "Number of of functions marked norecurse from summary propagation"); /// Limit on instruction count of imported functions. static cl::opt ImportInstrLimit( @@ -410,6 +417,77 @@ #endif } +/// Propagates ReadOnly/ReadNone and NoRecurse attributes throughout +/// the Index (uses same method as FunctionAttrs pass) +void llvm::propagateFunctionAttrs(ModuleSummaryIndex &Index) { + auto addReadAttrs = [](std::vector &SCCNodes) { + bool ReadsMemory = false; + for (FunctionSummary *F : SCCNodes) { + if (!(F->fflags().ReadOnly || F->fflags().ReadNone)) + return false; // may write memory + if (F->fflags().ReadOnly) + ReadsMemory = true; + } + + bool MadeChange = false; + for (FunctionSummary *F : SCCNodes) { + if (F->fflags().ReadNone || (F->fflags().ReadOnly && ReadsMemory)) + continue; // flags are already correct + MadeChange = true; + if (ReadsMemory) { + NumReadOnly++; + F->fflags().ReadOnly = true; + F->fflags().ReadNone = false; + } else { + NumReadNone++; + F->fflags().ReadOnly = false; + F->fflags().ReadNone = true; + } + } + return MadeChange; + }; + + // TODO: implement addNoAliasAttrs once + // there's more information about the return type in the summary + + auto addNoRecurseAttrs = [](std::vector &SCCNodes) { + if (SCCNodes.size() != 1) + return false; + + FunctionSummary *F = SCCNodes.front(); + + if (F->getOriginalName() == 0 || F->fflags().NoRecurse) + return false; + + if (std::any_of( + F->calls().begin(), F->calls().end(), + [&F](const FunctionSummary::EdgeTy &E) { + if (E.first.getGUID() == 0 || !E.first.getSummaryList().size()) + return false; + FunctionSummary *CFS = + cast(E.first.getSummaryList().front().get()); + return E.first.getGUID() == F->getOriginalName() || + CFS->fflags().NoRecurse; + })) + return false; + + if (!F->fflags().NoRecurse) { + F->fflags().NoRecurse = true; + NumNoRecurse++; + return true; + } + return false; + }; + + // Call propagation functions on each SCC in the Index + for (scc_iterator I = scc_begin(&Index); !I.isAtEnd(); + ++I) { + std::vector Nodes(*I); + bool Changed = addReadAttrs(Nodes); + Changed |= addNoRecurseAttrs(Nodes); + } +} + void llvm::computeDeadSymbols( ModuleSummaryIndex &Index, const DenseSet &GUIDPreservedSymbols) { @@ -514,6 +592,22 @@ return std::error_code(); } +/// Insert function attributes in the Index back into the \p TheModule. +void llvm::thinLTOInsertFunctionAttrsForModule( + Module &TheModule, const ModuleSummaryIndex &Index) { + for (Function &F : TheModule) { + GlobalValueSummary *GV; + if (!(GV = + Index.findSummaryInModule(F.getGUID(), F.getParent()->getName()))) + continue; // skip functions that aren't in the index + FunctionSummary *FS = cast(GV); + if (FS->fflags().ReadNone) + F.setDoesNotAccessMemory(); + if (FS->fflags().ReadOnly) + F.setOnlyReadsMemory(); + } +} + /// Fixup WeakForLinker linkages in \p TheModule based on summary analysis. void llvm::thinLTOResolveWeakForLinkerModule( Module &TheModule, const GVSummaryMapTy &DefinedGlobals) {