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/ThinLTOCodeGenerator.cpp =================================================================== --- lib/LTO/ThinLTOCodeGenerator.cpp +++ lib/LTO/ThinLTOCodeGenerator.cpp @@ -606,6 +606,7 @@ return nullptr; } } + propagateFunctionAttrs(*CombinedIndex); return CombinedIndex; } @@ -681,6 +682,7 @@ ExportLists); auto &ImportList = ImportLists[TheModule.getModuleIdentifier()]; + thinLTOInsertFunctionAttrsForModule(TheModule, Index); crossImportIntoModule(TheModule, Index, ModuleMap, ImportList); } 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,80 @@ #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; + + bool calleesMightRecurse = std::any_of( + F->calls().begin(), F->calls().end(), + [&F](const FunctionSummary::EdgeTy &E) { + if (E.first.getGUID() == 0 || !E.first.getSummaryList().size()) + return true; // might recurse - we can't reason about external + // functions + FunctionSummary *CFS = + cast(E.first.getSummaryList().front().get()); + bool calleeRecurses = !CFS->fflags().NoRecurse; + return calleeRecurses; + }); + + if (calleesMightRecurse) + 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 +595,24 @@ 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(); + if (FS->fflags().NoRecurse) + F.setDoesNotRecurse(); + } +} + /// Fixup WeakForLinker linkages in \p TheModule based on summary analysis. void llvm::thinLTOResolveWeakForLinkerModule( Module &TheModule, const GVSummaryMapTy &DefinedGlobals) { Index: test/ThinLTO/X86/Inputs/function-attr-prop-a.ll =================================================================== --- /dev/null +++ test/ThinLTO/X86/Inputs/function-attr-prop-a.ll @@ -0,0 +1,12 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +declare i32 @b() +define i32 @a() readnone { + %1 = call i32 @b() + ret i32 %1 +} + +define i32 @c() norecurse { + ret i32 4 +} Index: test/ThinLTO/X86/functionattr-prop.ll =================================================================== --- /dev/null +++ test/ThinLTO/X86/functionattr-prop.ll @@ -0,0 +1,21 @@ +; RUN: opt -module-summary %s -o %t1.bc +; RUN: opt -module-summary %p/Inputs/function-attr-prop-a.ll -o %t2.bc +; RUN: llvm-lto -thinlto-action=thinlink -o %t3.bc %t1.bc %t2.bc +; RUN: llvm-lto -thinlto-action=import %t1.bc -thinlto-index=%t3.bc -o - | llvm-dis -o - | FileCheck %s + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +declare i32 @a() readnone +define i32 @b() { + %1 = call i32 @a() + ret i32 %1 +} + +declare i32 @c() norecurse +; CHECK: define i32 @d() #0 { +define i32 @d() { + call i32 @c() + ret i32 1; +} +; CHECK: #0 = { norecurse }