Index: llvm/trunk/include/llvm/IR/ModuleSummaryIndex.h =================================================================== --- llvm/trunk/include/llvm/IR/ModuleSummaryIndex.h +++ llvm/trunk/include/llvm/IR/ModuleSummaryIndex.h @@ -478,13 +478,17 @@ TypeCheckedLoadConstVCalls; }; - /// Function attribute flags. Used to track if a function accesses memory, - /// recurses or aliases. + /// Flags specific to function summaries. struct FFlags { + // Function attribute flags. Used to track if a function accesses memory, + // recurses or aliases. unsigned ReadNone : 1; unsigned ReadOnly : 1; unsigned NoRecurse : 1; unsigned ReturnDoesNotAlias : 1; + + // Indicate if the global value cannot be inlined. + unsigned NoInline : 1; }; /// Create an empty FunctionSummary (with specified call edges). @@ -511,8 +515,7 @@ /// during the initial compile step when the summary index is first built. unsigned InstCount; - /// Function attribute flags. Used to track if a function accesses memory, - /// recurses or aliases. + /// Function summary specific flags. FFlags FunFlags; /// List of call edge pairs from this function. @@ -546,7 +549,7 @@ return GVS->getSummaryKind() == FunctionKind; } - /// Get function attribute flags. + /// Get function summary flags. FFlags fflags() const { return FunFlags; } /// Get the instruction count recorded for this function. Index: llvm/trunk/include/llvm/Transforms/IPO/FunctionImport.h =================================================================== --- llvm/trunk/include/llvm/Transforms/IPO/FunctionImport.h +++ llvm/trunk/include/llvm/Transforms/IPO/FunctionImport.h @@ -56,10 +56,14 @@ // to find at least one summary for the GUID that is global or a local // in the referenced module for direct calls. LocalLinkageNotInModule, - // This corresponse to the NotEligibleToImport being set on the summary, + // This corresponds to the NotEligibleToImport being set on the summary, // which can happen in a few different cases (e.g. local that can't be // renamed or promoted because it is referenced on a llvm*.used variable). - NotEligible + NotEligible, + // This corresponds to NoInline being set on the function summary, + // which will happen if it is known that the inliner will not be able + // to inline the function (e.g. it is marked with a NoInline attribute). + NoInline }; /// Information optionally tracked for candidates the importer decided Index: llvm/trunk/lib/Analysis/ModuleSummaryAnalysis.cpp =================================================================== --- llvm/trunk/lib/Analysis/ModuleSummaryAnalysis.cpp +++ llvm/trunk/lib/Analysis/ModuleSummaryAnalysis.cpp @@ -350,20 +350,18 @@ bool NonRenamableLocal = isNonRenamableLocal(F); bool NotEligibleForImport = - NonRenamableLocal || HasInlineAsmMaybeReferencingInternal || - // Inliner doesn't handle variadic functions. - // FIXME: refactor this to use the same code that inliner is using. - F.isVarArg() || - // Don't try to import functions with noinline attribute. - F.getAttributes().hasFnAttribute(Attribute::NoInline); + NonRenamableLocal || HasInlineAsmMaybeReferencingInternal; GlobalValueSummary::GVFlags Flags(F.getLinkage(), NotEligibleForImport, /* Live = */ false, F.isDSOLocal()); FunctionSummary::FFlags FunFlags{ F.hasFnAttribute(Attribute::ReadNone), F.hasFnAttribute(Attribute::ReadOnly), - F.hasFnAttribute(Attribute::NoRecurse), - F.returnDoesNotAlias(), - }; + F.hasFnAttribute(Attribute::NoRecurse), F.returnDoesNotAlias(), + // Inliner doesn't handle variadic functions. + // FIXME: refactor this to use the same code that inliner is using. + F.isVarArg() || + // Don't try to import functions with noinline attribute. + F.getAttributes().hasFnAttribute(Attribute::NoInline)}; auto FuncSummary = llvm::make_unique( Flags, NumInsts, FunFlags, RefEdges.takeVector(), CallGraphEdges.takeVector(), TypeTests.takeVector(), @@ -478,7 +476,8 @@ F->hasFnAttribute(Attribute::ReadNone), F->hasFnAttribute(Attribute::ReadOnly), F->hasFnAttribute(Attribute::NoRecurse), - F->returnDoesNotAlias()}, + F->returnDoesNotAlias(), + /* NoInline = */ false}, ArrayRef{}, ArrayRef{}, ArrayRef{}, ArrayRef{}, Index: llvm/trunk/lib/AsmParser/LLLexer.cpp =================================================================== --- llvm/trunk/lib/AsmParser/LLLexer.cpp +++ llvm/trunk/lib/AsmParser/LLLexer.cpp @@ -740,6 +740,7 @@ KEYWORD(readOnly); KEYWORD(noRecurse); KEYWORD(returnDoesNotAlias); + KEYWORD(noInline); KEYWORD(calls); KEYWORD(callee); KEYWORD(hotness); Index: llvm/trunk/lib/AsmParser/LLParser.cpp =================================================================== --- llvm/trunk/lib/AsmParser/LLParser.cpp +++ llvm/trunk/lib/AsmParser/LLParser.cpp @@ -7714,6 +7714,7 @@ /// := 'funcFlags' ':' '(' ['readNone' ':' Flag]? /// [',' 'readOnly' ':' Flag]? [',' 'noRecurse' ':' Flag]? /// [',' 'returnDoesNotAlias' ':' Flag]? ')' +/// [',' 'noInline' ':' Flag]? ')' bool LLParser::ParseOptionalFFlags(FunctionSummary::FFlags &FFlags) { assert(Lex.getKind() == lltok::kw_funcFlags); Lex.Lex(); @@ -7749,6 +7750,12 @@ return true; FFlags.ReturnDoesNotAlias = Val; break; + case lltok::kw_noInline: + Lex.Lex(); + if (ParseToken(lltok::colon, "expected ':'") || ParseFlag(Val)) + return true; + FFlags.NoInline = Val; + break; default: return Error(Lex.getLoc(), "expected function flag type"); } Index: llvm/trunk/lib/AsmParser/LLToken.h =================================================================== --- llvm/trunk/lib/AsmParser/LLToken.h +++ llvm/trunk/lib/AsmParser/LLToken.h @@ -369,6 +369,7 @@ kw_readOnly, kw_noRecurse, kw_returnDoesNotAlias, + kw_noInline, kw_calls, kw_callee, kw_hotness, Index: llvm/trunk/lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- llvm/trunk/lib/Bitcode/Reader/BitcodeReader.cpp +++ llvm/trunk/lib/Bitcode/Reader/BitcodeReader.cpp @@ -876,6 +876,7 @@ Flags.ReadOnly = (RawFlags >> 1) & 0x1; Flags.NoRecurse = (RawFlags >> 2) & 0x1; Flags.ReturnDoesNotAlias = (RawFlags >> 3) & 0x1; + Flags.NoInline = (RawFlags >> 4) & 0x1; return Flags; } Index: llvm/trunk/lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- llvm/trunk/lib/Bitcode/Writer/BitcodeWriter.cpp +++ llvm/trunk/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -971,6 +971,7 @@ RawFlags |= (Flags.ReadOnly << 1); RawFlags |= (Flags.NoRecurse << 2); RawFlags |= (Flags.ReturnDoesNotAlias << 3); + RawFlags |= (Flags.NoInline << 4); return RawFlags; } Index: llvm/trunk/lib/IR/AsmWriter.cpp =================================================================== --- llvm/trunk/lib/IR/AsmWriter.cpp +++ llvm/trunk/lib/IR/AsmWriter.cpp @@ -2871,6 +2871,7 @@ Out << ", readOnly: " << FFlags.ReadOnly; Out << ", noRecurse: " << FFlags.NoRecurse; Out << ", returnDoesNotAlias: " << FFlags.ReturnDoesNotAlias; + Out << ", noInline: " << FFlags.NoInline; Out << ")"; } if (!FS->calls().empty()) { Index: llvm/trunk/lib/IR/ModuleSummaryIndex.cpp =================================================================== --- llvm/trunk/lib/IR/ModuleSummaryIndex.cpp +++ llvm/trunk/lib/IR/ModuleSummaryIndex.cpp @@ -182,8 +182,9 @@ static std::string fflagsToString(FunctionSummary::FFlags F) { auto FlagValue = [](unsigned V) { return V ? '1' : '0'; }; - char FlagRep[] = {FlagValue(F.ReadNone), FlagValue(F.ReadOnly), - FlagValue(F.NoRecurse), FlagValue(F.ReturnDoesNotAlias), 0}; + char FlagRep[] = {FlagValue(F.ReadNone), FlagValue(F.ReadOnly), + FlagValue(F.NoRecurse), FlagValue(F.ReturnDoesNotAlias), + FlagValue(F.NoInline), 0}; return FlagRep; } Index: llvm/trunk/lib/Transforms/IPO/FunctionImport.cpp =================================================================== --- llvm/trunk/lib/Transforms/IPO/FunctionImport.cpp +++ llvm/trunk/lib/Transforms/IPO/FunctionImport.cpp @@ -237,11 +237,19 @@ return false; } + // Skip if it isn't legal to import (e.g. may reference unpromotable + // locals). if (Summary->notEligibleToImport()) { Reason = FunctionImporter::ImportFailureReason::NotEligible; return false; } + // Don't bother importing if we can't inline it anyway. + if (Summary->fflags().NoInline) { + Reason = FunctionImporter::ImportFailureReason::NoInline; + return false; + } + return true; }); if (It == CalleeSummaryList.end()) @@ -318,6 +326,8 @@ return "LocalLinkageNotInModule"; case FunctionImporter::ImportFailureReason::NotEligible: return "NotEligible"; + case FunctionImporter::ImportFailureReason::NoInline: + return "NoInline"; } llvm_unreachable("invalid reason"); } Index: llvm/trunk/test/Assembler/thinlto-summary.ll =================================================================== --- llvm/trunk/test/Assembler/thinlto-summary.ll +++ llvm/trunk/test/Assembler/thinlto-summary.ll @@ -81,8 +81,8 @@ ; CHECK: ^13 = gv: (guid: 12, summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0)))) ; CHECK: ^14 = gv: (guid: 13, summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 1)))) ; CHECK: ^15 = gv: (guid: 14, summaries: (function: (module: ^1, flags: (linkage: external, notEligibleToImport: 1, live: 1, dsoLocal: 0), insts: 1))) -; CHECK: ^16 = gv: (guid: 15, summaries: (function: (module: ^1, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1, funcFlags: (readNone: 1, readOnly: 0, noRecurse: 1, returnDoesNotAlias: 0)))) -; CHECK: ^17 = gv: (guid: 16, summaries: (function: (module: ^1, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1, funcFlags: (readNone: 0, readOnly: 1, noRecurse: 0, returnDoesNotAlias: 1), calls: ((callee: ^15))))) +; CHECK: ^16 = gv: (guid: 15, summaries: (function: (module: ^1, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1, funcFlags: (readNone: 1, readOnly: 0, noRecurse: 1, returnDoesNotAlias: 0, noInline: 0)))) +; CHECK: ^17 = gv: (guid: 16, summaries: (function: (module: ^1, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1, funcFlags: (readNone: 0, readOnly: 1, noRecurse: 0, returnDoesNotAlias: 1, noInline: 0), calls: ((callee: ^15))))) ; CHECK: ^18 = gv: (guid: 17, summaries: (alias: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 1), aliasee: ^14))) ; CHECK: ^19 = gv: (guid: 18, summaries: (function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 4, typeIdInfo: (typeTests: (^24, ^26))))) ; CHECK: ^20 = gv: (guid: 19, summaries: (function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 8, typeIdInfo: (typeTestAssumeVCalls: (vFuncId: (^27, offset: 16)))))) Index: llvm/trunk/test/Bitcode/thinlto-function-summary.ll =================================================================== --- llvm/trunk/test/Bitcode/thinlto-function-summary.ll +++ llvm/trunk/test/Bitcode/thinlto-function-summary.ll @@ -20,7 +20,7 @@ ; BC-NEXT: