diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -1943,6 +1943,11 @@ This attribute indicates that the inliner should never inline this function in any situation. This attribute may not be used together with the ``alwaysinline`` attribute. +``noipa`` + Disables any interprocedural analysis that inspects the definition of this + function. Equivalent to moving this function definition to a separate, + optimizer-opaque, module. Any attributes on the function are still respected + (as they would be if they remained on a function declaration in this module). ``nomerge`` This attribute indicates that calls to this function should never be merged during optimization. For example, it will prevent tail merging otherwise diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -714,6 +714,7 @@ ATTR_KIND_MEMORY = 86, ATTR_KIND_NOFPCLASS = 87, ATTR_KIND_OPTIMIZE_FOR_DEBUGGING = 88, + ATTR_KIND_NO_INTERPROCEDURAL_ANALYSIS = 89, }; enum ComdatSelectionKindCodes { diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td --- a/llvm/include/llvm/IR/Attributes.td +++ b/llvm/include/llvm/IR/Attributes.td @@ -157,6 +157,9 @@ /// inline=never. def NoInline : EnumAttr<"noinline", [FnAttr]>; +/// Do not do interprocedural analysis or optimization including this function +def NoIPA : EnumAttr<"noipa", [FnAttr]>; + /// Function is called early and/or often, so lazy binding isn't worthwhile. def NonLazyBind : EnumAttr<"nonlazybind", [FnAttr]>; diff --git a/llvm/include/llvm/IR/GlobalValue.h b/llvm/include/llvm/IR/GlobalValue.h --- a/llvm/include/llvm/IR/GlobalValue.h +++ b/llvm/include/llvm/IR/GlobalValue.h @@ -130,7 +130,7 @@ /// Returns true if the definition of this global may be replaced by a /// differently optimized variant of the same source level function at link /// time. - bool mayBeDerefined() const { + bool mayBeDerefinedOrNoIPA() const { switch (getLinkage()) { case WeakODRLinkage: case LinkOnceODRLinkage: @@ -149,7 +149,7 @@ // nobuiltin due to attributes at call-sites. To avoid applying IPO based // on nobuiltin semantics, treat such function definitions as maybe // derefined. - return isInterposable() || isNobuiltinFnDef(); + return isInterposable() || isNobuiltinFnDef() || isNoipaFnDef(); } llvm_unreachable("Fully covered switch above!"); @@ -159,6 +159,10 @@ /// attribute. bool isNobuiltinFnDef() const; + /// Returns true if the global is a function definition with the noipa + /// attribute. + bool isNoipaFnDef() const; + protected: /// The intrinsic ID for this subclass (which must be a Function). /// @@ -483,7 +487,7 @@ /// visible variant is *a* correct implementation of the original source /// function; it just isn't the *only* correct implementation. bool isDefinitionExact() const { - return !mayBeDerefined(); + return !mayBeDerefinedOrNoIPA(); } /// Return true if this global has an exact defintion. diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -2058,6 +2058,8 @@ return Attribute::Hot; case bitc::ATTR_KIND_PRESPLIT_COROUTINE: return Attribute::PresplitCoroutine; + case bitc::ATTR_KIND_NO_INTERPROCEDURAL_ANALYSIS: + return Attribute::NoIPA; } } diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -823,6 +823,8 @@ return bitc::ATTR_KIND_MUSTPROGRESS; case Attribute::PresplitCoroutine: return bitc::ATTR_KIND_PRESPLIT_COROUTINE; + case Attribute::NoIPA: + return bitc::ATTR_KIND_NO_INTERPROCEDURAL_ANALYSIS; case Attribute::EndAttrKinds: llvm_unreachable("Can not encode end-attribute kinds marker."); case Attribute::None: diff --git a/llvm/lib/IR/Globals.cpp b/llvm/lib/IR/Globals.cpp --- a/llvm/lib/IR/Globals.cpp +++ b/llvm/lib/IR/Globals.cpp @@ -270,6 +270,13 @@ return F->hasFnAttribute(Attribute::NoBuiltin); } +bool GlobalValue::isNoipaFnDef() const { + const Function *F = dyn_cast(this); + if (!F || F->empty()) + return false; + return F->hasFnAttribute(Attribute::NoIPA); +} + bool GlobalValue::isDeclaration() const { // Globals are definitions if they have an initializer. if (const GlobalVariable *GV = dyn_cast(this)) diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -743,7 +743,7 @@ for (Function *F : SCCNodes) { // We can infer and propagate function attributes only when we know that the // definition we'll get at link time is *exactly* the definition we see now. - // For more details, see GlobalValue::mayBeDerefined. + // For more details, see GlobalValue::mayBeDerefinedOrNoIPA. if (!F->hasExactDefinition()) continue; @@ -860,7 +860,7 @@ for (Function *F : SCCNodes) { // We can infer and propagate function attributes only when we know that the // definition we'll get at link time is *exactly* the definition we see now. - // For more details, see GlobalValue::mayBeDerefined. + // For more details, see GlobalValue::mayBeDerefinedOrNoIPA. if (!F->hasExactDefinition()) continue; @@ -1111,7 +1111,7 @@ // We can infer and propagate function attributes only when we know that the // definition we'll get at link time is *exactly* the definition we see now. - // For more details, see GlobalValue::mayBeDerefined. + // For more details, see GlobalValue::mayBeDerefinedOrNoIPA. if (!F->hasExactDefinition()) return; @@ -1223,7 +1223,7 @@ // We can infer and propagate function attributes only when we know that the // definition we'll get at link time is *exactly* the definition we see now. - // For more details, see GlobalValue::mayBeDerefined. + // For more details, see GlobalValue::mayBeDerefinedOrNoIPA. if (!F->hasExactDefinition()) return; @@ -1679,7 +1679,7 @@ static bool functionWillReturn(const Function &F) { // We can infer and propagate function attributes only when we know that the // definition we'll get at link time is *exactly* the definition we see now. - // For more details, see GlobalValue::mayBeDerefined. + // For more details, see GlobalValue::mayBeDerefinedOrNoIPA. if (!F.hasExactDefinition()) return false; diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp --- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -946,6 +946,7 @@ case Attribute::OptimizeForDebugging: case Attribute::OptForFuzzing: case Attribute::OptimizeNone: + case Attribute::NoIPA: case Attribute::OptimizeForSize: case Attribute::SafeStack: case Attribute::ShadowCallStack: diff --git a/llvm/test/Bitcode/attributes.ll b/llvm/test/Bitcode/attributes.ll --- a/llvm/test/Bitcode/attributes.ll +++ b/llvm/test/Bitcode/attributes.ll @@ -517,6 +517,12 @@ ret void; } +define void @f90() noipa +; CHECK: define void @f90() [[NOIPA:#[0-9]+]] +{ + ret void +} + ; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #2 = { memory(none) } @@ -573,4 +579,5 @@ ; CHECK: attributes [[FNRETTHUNKEXTERN]] = { fn_ret_thunk_extern } ; CHECK: attributes [[SKIPPROFILE]] = { skipprofile } ; CHECK: attributes [[OPTDEBUG]] = { optdebug } +; CHECK: attributes [[NOIPA]] = { noipa } ; CHECK: attributes #[[NOBUILTIN]] = { nobuiltin } diff --git a/llvm/unittests/IR/FunctionTest.cpp b/llvm/unittests/IR/FunctionTest.cpp --- a/llvm/unittests/IR/FunctionTest.cpp +++ b/llvm/unittests/IR/FunctionTest.cpp @@ -486,4 +486,16 @@ It = F->erase(F->begin(), F->end()); EXPECT_EQ(F->size(), 0u); } + +TEST(FunctionTest, NoIPAInexact) { + LLVMContext Ctx; + std::unique_ptr M = parseIR(Ctx, R"( + define void @foo() { bb1: ret void } + define void @bar() #0 { bb1: ret void } + attributes #0 = { noipa } + )"); + EXPECT_TRUE(M->getFunction("foo")->isDefinitionExact()); + EXPECT_FALSE(M->getFunction("bar")->isDefinitionExact()); +} + } // end namespace diff --git a/llvm/utils/emacs/llvm-mode.el b/llvm/utils/emacs/llvm-mode.el --- a/llvm/utils/emacs/llvm-mode.el +++ b/llvm/utils/emacs/llvm-mode.el @@ -24,7 +24,7 @@ `(,(regexp-opt '("alwaysinline" "argmemonly" "allocsize" "builtin" "cold" "convergent" "dereferenceable" "dereferenceable_or_null" "hot" "immarg" "inaccessiblememonly" "inaccessiblemem_or_argmemonly" "inalloca" "inlinehint" "jumptable" "minsize" "mustprogress" "naked" "nobuiltin" "nonnull" "nocapture" - "nocallback" "nocf_check" "noduplicate" "nofree" "noimplicitfloat" "noinline" "nomerge" "nonlazybind" "noprofile" "noredzone" "noreturn" + "nocallback" "nocf_check" "noduplicate" "nofree" "noimplicitfloat" "noinline" "noipa" "nomerge" "nonlazybind" "noprofile" "noredzone" "noreturn" "norecurse" "nosync" "noundef" "nounwind" "nosanitize_bounds" "nosanitize_coverage" "null_pointer_is_valid" "optdebug" "optforfuzzing" "optnone" "optsize" "preallocated" "readnone" "readonly" "returned" "returns_twice" "shadowcallstack" "signext" "speculatable" "speculative_load_hardening" "ssp" "sspreq" "sspstrong" "safestack" "sanitize_address" "sanitize_hwaddress" "sanitize_memtag" "sanitize_thread" "sanitize_memory" "strictfp" "swifterror" "uwtable" "vscale_range" "willreturn" "writeonly" "zeroext") 'symbols) . font-lock-constant-face) diff --git a/llvm/utils/kate/llvm.xml b/llvm/utils/kate/llvm.xml --- a/llvm/utils/kate/llvm.xml +++ b/llvm/utils/kate/llvm.xml @@ -104,6 +104,7 @@ nofree noimplicitfloat noinline + noipa nomerge noprofile noredzone diff --git a/llvm/utils/vim/syntax/llvm.vim b/llvm/utils/vim/syntax/llvm.vim --- a/llvm/utils/vim/syntax/llvm.vim +++ b/llvm/utils/vim/syntax/llvm.vim @@ -129,6 +129,7 @@ \ nofree \ noimplicitfloat \ noinline + \ noipa \ nomerge \ nonlazybind \ nonnull diff --git a/llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml b/llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml --- a/llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml +++ b/llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml @@ -228,6 +228,7 @@ \\bnofree\\b|\ \\bnoimplicitfloat\\b|\ \\bnoinline\\b|\ + \\bnoipa\\b|\ \\bnomerge\\b|\ \\bnonlazybind\\b|\ \\bnonnull\\b|\