Index: include/llvm/IR/ModuleSummaryIndex.h =================================================================== --- include/llvm/IR/ModuleSummaryIndex.h +++ include/llvm/IR/ModuleSummaryIndex.h @@ -25,6 +25,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/Module.h" +#include "llvm/Transforms/IPO/FunctionAttrs.h" #include #include #include @@ -55,9 +56,11 @@ Critical = 4 }; HotnessType Hotness = HotnessType::Unknown; + MemoryAccessKind MemoryAccess = MemoryAccessKind::MAK_MayWrite; CalleeInfo() = default; - explicit CalleeInfo(HotnessType Hotness) : Hotness(Hotness) {} + explicit CalleeInfo(HotnessType Hotness, MemoryAccessKind A) + : Hotness(Hotness), MemoryAccess(A) {} void updateHotness(const HotnessType OtherHotness) { Hotness = std::max(Hotness, OtherHotness); @@ -287,11 +290,20 @@ std::vector Args; }; + struct FFlags { + unsigned ReadNone : 1; + unsigned ReadOnly : 1; + unsigned NoRecurse : 1; + unsigned NoAlias : 1; + }; + private: /// Number of instructions (ignoring debug instructions, e.g.) computed /// during the initial compile step when the summary index is first built. unsigned InstCount; + FFlags FunFlags; + /// List of call edge pairs from this function. std::vector CallGraphEdgeList; @@ -317,15 +329,16 @@ std::unique_ptr TIdInfo; public: - FunctionSummary(GVFlags Flags, unsigned NumInsts, std::vector Refs, - std::vector CGEdges, + FunctionSummary(GVFlags Flags, unsigned NumInsts, FFlags FunFlags, + std::vector Refs, std::vector CGEdges, std::vector TypeTests, std::vector TypeTestAssumeVCalls, std::vector TypeCheckedLoadVCalls, std::vector TypeTestAssumeConstVCalls, std::vector TypeCheckedLoadConstVCalls) : GlobalValueSummary(FunctionKind, Flags, std::move(Refs)), - InstCount(NumInsts), CallGraphEdgeList(std::move(CGEdges)) { + InstCount(NumInsts), FunFlags(FunFlags), + CallGraphEdgeList(std::move(CGEdges)) { if (!TypeTests.empty() || !TypeTestAssumeVCalls.empty() || !TypeCheckedLoadVCalls.empty() || !TypeTestAssumeConstVCalls.empty() || !TypeCheckedLoadConstVCalls.empty()) @@ -341,12 +354,27 @@ return GVS->getSummaryKind() == FunctionKind; } + FFlags &fflags() { return FunFlags; } + /// Get the instruction count recorded for this function. unsigned instCount() const { return InstCount; } /// Return the list of pairs. ArrayRef calls() const { return CallGraphEdgeList; } + bool updateCallGraphEdgeMAK(GlobalValue::GUID CalleeGUID, + MemoryAccessKind M) { + auto E = std::find_if(CallGraphEdgeList.begin(), CallGraphEdgeList.end(), + [&CalleeGUID](FunctionSummary::EdgeTy &E) -> bool { + return E.first.getGUID() == CalleeGUID; + }); + if (E != CallGraphEdgeList.end()) { + E->second.MemoryAccess = M; + return true; + } + return false; + } + /// Returns the list of type identifiers used by this function in /// llvm.type.test intrinsics other than by an llvm.assume intrinsic, /// represented as GUIDs. Index: include/llvm/IR/ModuleSummaryIndexYAML.h =================================================================== --- include/llvm/IR/ModuleSummaryIndexYAML.h +++ include/llvm/IR/ModuleSummaryIndexYAML.h @@ -206,8 +206,9 @@ GlobalValueSummary::GVFlags( static_cast(FSum.Linkage), FSum.NotEligibleToImport, FSum.Live), - 0, ArrayRef{}, ArrayRef{}, - std::move(FSum.TypeTests), std::move(FSum.TypeTestAssumeVCalls), + 0, FunctionSummary::FFlags{}, ArrayRef{}, + ArrayRef{}, std::move(FSum.TypeTests), + std::move(FSum.TypeTestAssumeVCalls), std::move(FSum.TypeCheckedLoadVCalls), std::move(FSum.TypeTestAssumeConstVCalls), std::move(FSum.TypeCheckedLoadConstVCalls))); Index: include/llvm/Transforms/IPO/FunctionAttrs.h =================================================================== --- include/llvm/Transforms/IPO/FunctionAttrs.h +++ include/llvm/Transforms/IPO/FunctionAttrs.h @@ -31,7 +31,9 @@ }; /// Returns the memory access properties of this copy of the function. -MemoryAccessKind computeFunctionBodyMemoryAccess(Function &F, AAResults &AAR); +MemoryAccessKind computeFunctionBodyMemoryAccess( + Function &F, AAResults &AAR, + std::map *CallSiteMAKs = nullptr); /// Computes function attributes in post-order over the call graph. /// Index: include/llvm/Transforms/IPO/FunctionImport.h =================================================================== --- include/llvm/Transforms/IPO/FunctionImport.h +++ include/llvm/Transforms/IPO/FunctionImport.h @@ -11,6 +11,7 @@ #define LLVM_FUNCTIONIMPORT_H #include "llvm/ADT/StringMap.h" +#include "llvm/Analysis/BasicAliasAnalysis.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/ModuleSummaryIndex.h" #include "llvm/IR/PassManager.h" @@ -102,6 +103,8 @@ ModuleSummaryIndex &Index, const DenseSet &GUIDPreservedSymbols); +void thinLinkComputeFunctionAttrs(ModuleSummaryIndex &Index); + /// Compute the set of summaries needed for a ThinLTO backend compilation of /// \p ModulePath. // @@ -123,6 +126,9 @@ EmitImportsFiles(StringRef ModulePath, StringRef OutputFilename, const FunctionImporter::ImportMapTy &ModuleImports); +void thinLTOInsertFunctionAttrsForModule(Module &TheModule, + const ModuleSummaryIndex &Index); + /// Resolve WeakForLinker values in \p TheModule based on the information /// recorded in the summaries during global summary-based analysis. void thinLTOResolveWeakForLinkerModule(Module &TheModule, Index: lib/Analysis/ModuleSummaryAnalysis.cpp =================================================================== --- lib/Analysis/ModuleSummaryAnalysis.cpp +++ lib/Analysis/ModuleSummaryAnalysis.cpp @@ -276,10 +276,16 @@ F.isVarArg(); GlobalValueSummary::GVFlags Flags(F.getLinkage(), NotEligibleForImport, /* Live = */ false); + FunctionSummary::FFlags FunFlags{ + F.hasFnAttribute(Attribute::ReadNone), + F.hasFnAttribute(Attribute::ReadOnly), + F.hasFnAttribute(Attribute::NoRecurse), + F.hasFnAttribute(Attribute::NoAlias), + }; auto FuncSummary = llvm::make_unique( - Flags, NumInsts, RefEdges.takeVector(), CallGraphEdges.takeVector(), - TypeTests.takeVector(), TypeTestAssumeVCalls.takeVector(), - TypeCheckedLoadVCalls.takeVector(), + Flags, NumInsts, FunFlags, RefEdges.takeVector(), + CallGraphEdges.takeVector(), TypeTests.takeVector(), + TypeTestAssumeVCalls.takeVector(), TypeCheckedLoadVCalls.takeVector(), TypeTestAssumeConstVCalls.takeVector(), TypeCheckedLoadConstVCalls.takeVector()); if (NonRenamableLocal) @@ -427,11 +433,16 @@ /* Live = */ true); CantBePromoted.insert(GlobalValue::getGUID(Name)); // Create the appropriate summary type. - if (isa(GV)) { + if (Function *F = dyn_cast(GV)) { std::unique_ptr Summary = llvm::make_unique( - GVFlags, 0, ArrayRef{}, - ArrayRef{}, + GVFlags, 0, + FunctionSummary::FFlags{ + F->hasFnAttribute(Attribute::ReadNone), + F->hasFnAttribute(Attribute::ReadOnly), + F->hasFnAttribute(Attribute::NoRecurse), + F->hasFnAttribute(Attribute::NoAlias)}, + ArrayRef{}, ArrayRef{}, ArrayRef{}, ArrayRef{}, ArrayRef{}, Index: lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- lib/Bitcode/Reader/BitcodeReader.cpp +++ lib/Bitcode/Reader/BitcodeReader.cpp @@ -860,6 +860,15 @@ } } +static FunctionSummary::FFlags getDecodedFFlags(uint64_t RawFlags) { + FunctionSummary::FFlags Flags; + Flags.ReadNone = RawFlags & 0x1; + Flags.ReadOnly = (RawFlags >> 1) & 0x1; + Flags.NoRecurse = (RawFlags >> 2) & 0x1; + Flags.NoAlias = (RawFlags >> 3) & 0x1; + return Flags; +} + /// Decode the flags for GlobalValue in the summary. static GlobalValueSummary::GVFlags getDecodedGVSummaryFlags(uint64_t RawFlags, uint64_t Version) { @@ -5008,13 +5017,14 @@ for (unsigned I = 0, E = Record.size(); I != E; ++I) { CalleeInfo::HotnessType Hotness = CalleeInfo::HotnessType::Unknown; ValueInfo Callee = getValueInfoFromValueId(Record[I]).first; + MemoryAccessKind MA = static_cast(Record[++I]); if (IsOldProfileFormat) { I += 1; // Skip old callsitecount field if (HasProfile) I += 1; // Skip old profilecount field } else if (HasProfile) Hotness = static_cast(Record[++I]); - Ret.push_back(FunctionSummary::EdgeTy{Callee, CalleeInfo{Hotness}}); + Ret.push_back(FunctionSummary::EdgeTy{Callee, CalleeInfo{Hotness, MA}}); } return Ret; } @@ -5098,14 +5108,15 @@ unsigned ValueID = Record[0]; uint64_t RawFlags = Record[1]; unsigned InstCount = Record[2]; - unsigned NumRefs = Record[3]; + uint64_t RawFunFlags = Record[3]; + unsigned NumRefs = Record[4]; auto Flags = getDecodedGVSummaryFlags(RawFlags, Version); // The module path string ref set in the summary must be owned by the // index's module string table. Since we don't have a module path // string table section in the per-module index, we create a single // module path string table entry with an empty (0) ID to take // ownership. - static int RefListStartIndex = 4; + static int RefListStartIndex = 5; int CallGraphEdgeStartIndex = RefListStartIndex + NumRefs; assert(Record.size() >= RefListStartIndex + NumRefs && "Record size inconsistent with number of references"); @@ -5116,8 +5127,9 @@ ArrayRef(Record).slice(CallGraphEdgeStartIndex), IsOldProfileFormat, HasProfile); auto FS = llvm::make_unique( - Flags, InstCount, std::move(Refs), std::move(Calls), - std::move(PendingTypeTests), std::move(PendingTypeTestAssumeVCalls), + Flags, InstCount, getDecodedFFlags(RawFunFlags), std::move(Refs), + std::move(Calls), std::move(PendingTypeTests), + std::move(PendingTypeTestAssumeVCalls), std::move(PendingTypeCheckedLoadVCalls), std::move(PendingTypeTestAssumeConstVCalls), std::move(PendingTypeCheckedLoadConstVCalls)); @@ -5186,9 +5198,10 @@ uint64_t ModuleId = Record[1]; uint64_t RawFlags = Record[2]; unsigned InstCount = Record[3]; - unsigned NumRefs = Record[4]; + uint64_t RawFunFlags = Record[4]; + unsigned NumRefs = Record[5]; auto Flags = getDecodedGVSummaryFlags(RawFlags, Version); - static int RefListStartIndex = 5; + static int RefListStartIndex = 6; int CallGraphEdgeStartIndex = RefListStartIndex + NumRefs; assert(Record.size() >= RefListStartIndex + NumRefs && "Record size inconsistent with number of references"); @@ -5200,8 +5213,9 @@ IsOldProfileFormat, HasProfile); ValueInfo VI = getValueInfoFromValueId(ValueID).first; auto FS = llvm::make_unique( - Flags, InstCount, std::move(Refs), std::move(Edges), - std::move(PendingTypeTests), std::move(PendingTypeTestAssumeVCalls), + Flags, InstCount, getDecodedFFlags(RawFunFlags), std::move(Refs), + std::move(Edges), std::move(PendingTypeTests), + std::move(PendingTypeTestAssumeVCalls), std::move(PendingTypeCheckedLoadVCalls), std::move(PendingTypeTestAssumeConstVCalls), std::move(PendingTypeCheckedLoadConstVCalls)); Index: lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- lib/Bitcode/Writer/BitcodeWriter.cpp +++ lib/Bitcode/Writer/BitcodeWriter.cpp @@ -880,6 +880,15 @@ return getEncodedLinkage(GV.getLinkage()); } +static uint64_t getEncodedFFlags(FunctionSummary::FFlags Flags) { + uint64_t RawFlags = 0; + RawFlags |= Flags.ReadNone; + RawFlags |= (Flags.ReadOnly << 1); + RawFlags |= (Flags.NoRecurse << 2); + RawFlags |= (Flags.NoAlias << 3); + return RawFlags; +} + // Decode the flags for GlobalValue in the summary static uint64_t getEncodedGVSummaryFlags(GlobalValueSummary::GVFlags Flags) { uint64_t RawFlags = 0; @@ -1688,7 +1697,7 @@ Record.push_back(N->isDistinct()); Record.push_back(VE.getMetadataOrNullID(N->getVariable())); Record.push_back(VE.getMetadataOrNullID(N->getExpression())); - + Stream.EmitRecord(bitc::METADATA_GLOBAL_VAR_EXPR, Record, Abbrev); Record.clear(); } @@ -3278,6 +3287,7 @@ NameVals.push_back(getEncodedGVSummaryFlags(FS->flags())); NameVals.push_back(FS->instCount()); + NameVals.push_back(getEncodedFFlags(FS->fflags())); NameVals.push_back(FS->refs().size()); for (auto &RI : FS->refs()) @@ -3286,6 +3296,7 @@ bool HasProfileData = F.getEntryCount().hasValue(); for (auto &ECI : FS->calls()) { NameVals.push_back(getValueId(ECI.first)); + NameVals.push_back(static_cast(ECI.second.MemoryAccess)); if (HasProfileData) NameVals.push_back(static_cast(ECI.second.Hotness)); } @@ -3559,6 +3570,7 @@ NameVals.push_back(Index.getModuleId(FS->modulePath())); NameVals.push_back(getEncodedGVSummaryFlags(FS->flags())); NameVals.push_back(FS->instCount()); + NameVals.push_back(getEncodedFFlags(FS->fflags())); // Fill in below NameVals.push_back(0); @@ -3570,7 +3582,7 @@ NameVals.push_back(*RefValueId); Count++; } - NameVals[4] = Count; + NameVals[5] = Count; bool HasProfileData = false; for (auto &EI : FS->calls()) { @@ -3596,6 +3608,7 @@ continue; } NameVals.push_back(*CallValueId); + NameVals.push_back(static_cast(EI.second.MemoryAccess)); if (HasProfileData) NameVals.push_back(static_cast(EI.second.Hotness)); } Index: lib/LTO/LTO.cpp =================================================================== --- lib/LTO/LTO.cpp +++ lib/LTO/LTO.cpp @@ -704,6 +704,7 @@ } computeDeadSymbols(ThinLTO.CombinedIndex, GUIDPreservedSymbols); + thinLinkComputeFunctionAttrs(ThinLTO.CombinedIndex); // Save the status of having a regularLTO combined module, as // this is needed for generating the ThinLTO Task ID, and Index: lib/LTO/LTOBackend.cpp =================================================================== --- lib/LTO/LTOBackend.cpp +++ lib/LTO/LTOBackend.cpp @@ -410,6 +410,7 @@ renameModuleForThinLTO(Mod, CombinedIndex); thinLTOResolveWeakForLinkerModule(Mod, DefinedGlobals); + thinLTOInsertFunctionAttrsForModule(Mod, CombinedIndex); if (Conf.PostPromoteModuleHook && !Conf.PostPromoteModuleHook(Task, Mod)) return Error::success(); Index: lib/Transforms/IPO/FunctionAttrs.cpp =================================================================== --- lib/Transforms/IPO/FunctionAttrs.cpp +++ lib/Transforms/IPO/FunctionAttrs.cpp @@ -60,6 +60,58 @@ typedef SmallSetVector SCCNodeSet; } +static MemoryAccessKind +analyseCallSite(Instruction *I, const SCCNodeSet &SCCNodes, AAResults &AAR) { + CallSite CS(cast(I)); + // Ignore calls to functions in the same SCC, as long as the call sites + // don't have operand bundles. Calls with operand bundles are allowed to + // have memory effects not described by the memory effects of the call + // target. + if (!CS.hasOperandBundles() && CS.getCalledFunction() && + SCCNodes.count(CS.getCalledFunction())) + return MAK_ReadNone; + FunctionModRefBehavior MRB = AAR.getModRefBehavior(CS); + + // If the call doesn't access memory, we're done. + if (!(MRB & MRI_ModRef)) + return MAK_ReadNone; + + if (!AliasAnalysis::onlyAccessesArgPointees(MRB)) { + // The call could access any memory. If that includes writes, give up. + if (MRB & MRI_Mod) + return MAK_MayWrite; + // If it reads, note it. + if (MRB & MRI_Ref) + return MAK_ReadOnly; + } + + // Check whether all pointer arguments point to local memory, and + // ignore calls that only access local memory. + for (CallSite::arg_iterator CI = CS.arg_begin(), CE = CS.arg_end(); CI != CE; + ++CI) { + Value *Arg = *CI; + if (!Arg->getType()->isPtrOrPtrVectorTy()) + return MAK_ReadNone; + + AAMDNodes AAInfo; + I->getAAMetadata(AAInfo); + MemoryLocation Loc(Arg, MemoryLocation::UnknownSize, AAInfo); + + // Skip accesses to local or constant memory as they don't impact the + // externally visible mod/ref behavior. + if (AAR.pointsToConstantMemory(Loc, /*OrLocal=*/true)) + return MAK_ReadNone; + + if (MRB & MRI_Mod) + // Writes non-local memory. Give up. + return MAK_MayWrite; + if (MRB & MRI_Ref) + // Ok, it reads non-local memory. + return MAK_ReadOnly; + } + return MAK_ReadNone; +} + /// Returns the memory access attribute for function F using AAR for AA results, /// where SCCNodes is the current SCC. /// @@ -68,9 +120,9 @@ /// result will be based only on AA results for the function declaration; it /// will be assumed that some other (perhaps less optimized) version of the /// function may be selected at link time. -static MemoryAccessKind checkFunctionMemoryAccess(Function &F, bool ThisBody, - AAResults &AAR, - const SCCNodeSet &SCCNodes) { +static MemoryAccessKind checkFunctionMemoryAccess( + Function &F, bool ThisBody, AAResults &AAR, const SCCNodeSet &SCCNodes, + std::map *CallSiteMAKs) { FunctionModRefBehavior MRB = AAR.getModRefBehavior(&F); if (MRB == FMRB_DoesNotAccessMemory) // Already perfect! @@ -93,54 +145,18 @@ // Detect these now, skipping to the next instruction if one is found. CallSite CS(cast(I)); if (CS) { - // Ignore calls to functions in the same SCC, as long as the call sites - // don't have operand bundles. Calls with operand bundles are allowed to - // have memory effects not described by the memory effects of the call - // target. - if (!CS.hasOperandBundles() && CS.getCalledFunction() && - SCCNodes.count(CS.getCalledFunction())) - continue; - FunctionModRefBehavior MRB = AAR.getModRefBehavior(CS); - - // If the call doesn't access memory, we're done. - if (!(MRB & MRI_ModRef)) - continue; - - if (!AliasAnalysis::onlyAccessesArgPointees(MRB)) { - // The call could access any memory. If that includes writes, give up. - if (MRB & MRI_Mod) - return MAK_MayWrite; - // If it reads, note it. - if (MRB & MRI_Ref) - ReadsMemory = true; + MemoryAccessKind M = analyseCallSite(I, SCCNodes, AAR); + if (CallSiteMAKs && CS.getCalledFunction()) + (*CallSiteMAKs)[CS.getCalledFunction()] = M; + switch (M) { + case MAK_MayWrite: + return MAK_MayWrite; + case MAK_ReadOnly: + ReadsMemory = true; + LLVM_FALLTHROUGH; + default: continue; } - - // Check whether all pointer arguments point to local memory, and - // ignore calls that only access local memory. - for (CallSite::arg_iterator CI = CS.arg_begin(), CE = CS.arg_end(); - CI != CE; ++CI) { - Value *Arg = *CI; - if (!Arg->getType()->isPtrOrPtrVectorTy()) - continue; - - AAMDNodes AAInfo; - I->getAAMetadata(AAInfo); - MemoryLocation Loc(Arg, MemoryLocation::UnknownSize, AAInfo); - - // Skip accesses to local or constant memory as they don't impact the - // externally visible mod/ref behavior. - if (AAR.pointsToConstantMemory(Loc, /*OrLocal=*/true)) - continue; - - if (MRB & MRI_Mod) - // Writes non-local memory. Give up. - return MAK_MayWrite; - if (MRB & MRI_Ref) - // Ok, it reads non-local memory. - ReadsMemory = true; - } - continue; } else if (LoadInst *LI = dyn_cast(I)) { // Ignore non-volatile loads from local memory. (Atomic is okay here.) if (!LI->isVolatile()) { @@ -175,9 +191,10 @@ return ReadsMemory ? MAK_ReadOnly : MAK_ReadNone; } -MemoryAccessKind llvm::computeFunctionBodyMemoryAccess(Function &F, - AAResults &AAR) { - return checkFunctionMemoryAccess(F, /*ThisBody=*/true, AAR, {}); +MemoryAccessKind llvm::computeFunctionBodyMemoryAccess( + Function &F, AAResults &AAR, + std::map *CallSiteMAKs) { + return checkFunctionMemoryAccess(F, /*ThisBody=*/true, AAR, {}, CallSiteMAKs); } /// Deduce readonly/readnone attributes for the SCC. @@ -193,8 +210,8 @@ // Non-exact function definitions may not be selected at link time, and an // alternative version that writes to memory may be selected. See the // comment on GlobalValue::isDefinitionExact for more details. - switch (checkFunctionMemoryAccess(*F, F->hasExactDefinition(), - AAR, SCCNodes)) { + switch (checkFunctionMemoryAccess(*F, F->hasExactDefinition(), AAR, + SCCNodes, nullptr)) { case MAK_MayWrite: return false; case MAK_ReadOnly: @@ -575,7 +592,7 @@ if (!isGuaranteedToTransferExecutionToSuccessor(&I)) break; } - + return Changed; } @@ -989,6 +1006,7 @@ DEBUG(dbgs() << "SCC marking " << F->getName() << " as nonnull\n"); F->addAttribute(AttributeList::ReturnIndex, Attribute::NonNull); + ++NumNonNullReturn; MadeChange = true; } Index: lib/Transforms/IPO/FunctionImport.cpp =================================================================== --- lib/Transforms/IPO/FunctionImport.cpp +++ lib/Transforms/IPO/FunctionImport.cpp @@ -29,6 +29,7 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/SourceMgr.h" +#include "llvm/Transforms/IPO/FunctionAttrs.h" #include "llvm/Transforms/IPO/Internalize.h" #include "llvm/Transforms/Utils/FunctionImportUtils.h" @@ -420,6 +421,38 @@ #endif } +void llvm::thinLinkComputeFunctionAttrs(ModuleSummaryIndex &Index) { + auto checkCalls = + [](FunctionSummary *F, + std::function Cond) { + for (auto &C : F->calls()) { + for (auto &V : C.first.getSummaryList()) { + FunctionSummary *S = cast(V.get()); + if (!Cond(S, &C.second)) + return false; + } + } + return true; + }; + + for (const auto &P : Index) { + for (auto &S : P.second.SummaryList) + if (FunctionSummary *F = dyn_cast(S.get())) { + // TODO: propagate NoRecurseTopdown + bool noRecursiveCallsInFunction = + checkCalls(F, [F](FunctionSummary *S, const CalleeInfo *C) { + if (!S->fflags().NoRecurse || + S->getOriginalName() == F->getOriginalName()) + return false; + return true; + }); + if (noRecursiveCallsInFunction) { + F->fflags().NoRecurse = true; + } + } + } +} + void llvm::computeDeadSymbols( ModuleSummaryIndex &Index, const DenseSet &GUIDPreservedSymbols) { @@ -524,6 +557,20 @@ return std::error_code(); } +void llvm::thinLTOInsertFunctionAttrsForModule( + Module &TheModule, const ModuleSummaryIndex &Index) { + for (Function &F : TheModule) { + if (!Index.findSummaryInModule(F.getGUID(), F.getParent()->getName())) + continue; // skip functions that aren't in the index + GlobalValueSummary *GV = Index.getGlobalValueSummary(F); + 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) { Index: lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp =================================================================== --- lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp +++ lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp @@ -24,6 +24,7 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/IPO.h" #include "llvm/Transforms/IPO/FunctionAttrs.h" +#include "llvm/Transforms/IPO/FunctionImport.h" #include "llvm/Transforms/Utils/Cloning.h" #include "llvm/Transforms/Utils/ModuleUtils.h" using namespace llvm; @@ -405,6 +406,44 @@ return false; } +static void computeThinMemoryAccess(Module &TheModule, + const ModuleSummaryIndex &Index, + LegacyAARGetter AARGetter) { + for (Function &F : TheModule) { + GlobalValueSummary *GV; + + if (!(GV = Index.findSummaryInModule(F.getGUID(), ""))) + continue; // skip functions that aren't in the index + + FunctionSummary *FS = cast(GV); + AAResults &R = AARGetter(F); + + bool Changed = false; + std::map CallMAKs; + switch (computeFunctionBodyMemoryAccess(F, R, &CallMAKs)) { + case MAK_MayWrite: + break; + case MAK_ReadOnly: + if (!FS->fflags().ReadOnly) { + Changed = true; + FS->fflags().ReadOnly = true; + } + break; + case MAK_ReadNone: + if (!FS->fflags().ReadNone) { + Changed = true; + FS->fflags().ReadOnly = false; + FS->fflags().ReadNone = true; + } + break; + } + + for (auto &P : CallMAKs) { + FS->updateCallGraphEdgeMAK(P.first->getGUID(), P.second); + } + } +} + void writeThinLTOBitcode(raw_ostream &OS, raw_ostream *ThinLinkOS, function_ref AARGetter, Module &M, const ModuleSummaryIndex *Index) { @@ -452,6 +491,7 @@ bool runOnModule(Module &M) override { const ModuleSummaryIndex *Index = &(getAnalysis().getIndex()); + computeThinMemoryAccess(M, *Index, LegacyAARGetter(*this)); writeThinLTOBitcode(OS, ThinLinkOS, LegacyAARGetter(*this), M, Index); return true; }