[llvm] [IR] Make optnone imply noinline (PR #228169)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Oct 1 10:51:08 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-ir
Author: David Blaikie (dwblaikie)
<details>
<summary>Changes</summary>
Previously the verifier required every `optnone` function to also carry
`noinline`. Instead, make `optnone` imply `noinline` so the pairing is no
longer necessary.
To avoid each user having to remember to check both attributes, add
`Function::isNoInline()` (true for `noinline` or `optnone`) and make
`CallBase::isNoInline()` defer to it for the callee. Existing direct
queries of `Attribute::NoInline` on functions are migrated to the helper.
The verifier now explicitly rejects `alwaysinline` + `optnone`, which was
previously only enforced transitively via the `noinline` requirement.
ForceFunctionAttrs and llvm-reduce no longer need to keep the two
attributes paired.
---
Patch is 21.70 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/228169.diff
24 Files Affected:
- (modified) llvm/docs/LangRef.md (+2-2)
- (modified) llvm/docs/ReleaseNotes.md (+5)
- (modified) llvm/include/llvm/IR/Attributes.td (+2)
- (modified) llvm/include/llvm/IR/Function.h (+7)
- (modified) llvm/include/llvm/IR/InstrTypes.h (+4-2)
- (modified) llvm/lib/Analysis/InlineCost.cpp (+1-1)
- (modified) llvm/lib/Analysis/ModuleSummaryAnalysis.cpp (+1-2)
- (modified) llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp (+1-1)
- (modified) llvm/lib/CodeGen/SafeStack.cpp (+1-2)
- (modified) llvm/lib/IR/Instructions.cpp (+8)
- (modified) llvm/lib/IR/Verifier.cpp (+3-2)
- (modified) llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp (+1-1)
- (modified) llvm/lib/Transforms/Coroutines/CoroSplit.cpp (+3-3)
- (modified) llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp (+1-15)
- (modified) llvm/lib/Transforms/IPO/FunctionSpecialization.cpp (+1-2)
- (modified) llvm/lib/Transforms/IPO/HotColdSplitting.cpp (+1-1)
- (modified) llvm/lib/Transforms/IPO/OpenMPOpt.cpp (+1-2)
- (modified) llvm/lib/Transforms/IPO/PartialInlining.cpp (+1-1)
- (modified) llvm/lib/Transforms/Instrumentation/PGOCtxProfLowering.cpp (+1-1)
- (modified) llvm/test/Transforms/ForcedFunctionAttrs/forced.ll (+17-15)
- (modified) llvm/test/Transforms/Inline/inline-optnone.ll (+18)
- (added) llvm/test/Verifier/optnone.ll (+17)
- (modified) llvm/test/tools/llvm-reduce/reduce-attributes-optnone-noinline.ll (+3-5)
- (modified) llvm/tools/llvm-reduce/deltas/ReduceAttributes.cpp (-15)
``````````diff
diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md
index ee7068b2125cc..06aa5443d67d5 100644
--- a/llvm/docs/LangRef.md
+++ b/llvm/docs/LangRef.md
@@ -2598,8 +2598,8 @@ fn -> other_fn -> other_fn ; fn is norecurse
attribute; this attribute is also incompatible
with the `minsize`, `optsize`, and `optdebug` attributes.
- This attribute requires the `noinline` attribute to be specified on
- the function as well, so the function is never inlined into any caller.
+ This attribute implies `noinline`, so the function is never inlined into
+ any caller; the `noinline` attribute need not be specified as well.
Only functions with the `alwaysinline` attribute are valid
candidates for inlining into the body of this function.
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index f66c80ecea9a2..f65e651ace07b 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -52,6 +52,11 @@ Makes programs 10x faster by doing Special New Thing.
### Changes to the LLVM IR
+* The `optnone` function attribute now implies `noinline`, and no longer
+ requires `noinline` to be present on the same function. Use
+ `Function::isNoInline()` or `CallBase::isNoInline()` to query whether a
+ function or call should not be inlined.
+
* LLVM now assigns persistent print IDs to metadata nodes. Reusing these IDs
avoids repeated module-wide scans to rebuild metadata numbering, which can
significantly speed up debug and pass printing on large modules. Keeping
diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td
index c43b499061ddc..9070b619d7da6 100644
--- a/llvm/include/llvm/IR/Attributes.td
+++ b/llvm/include/llvm/IR/Attributes.td
@@ -218,6 +218,8 @@ def DeadOnReturn : IntAttr<"dead_on_return", IntersectMin, [ParamAttr]>;
def NoImplicitFloat : EnumAttr<"noimplicitfloat", IntersectPreserve, [FnAttr]>;
/// inline=never.
+/// Note: optnone implies noinline. Use Function::isNoInline() or
+/// CallBase::isNoInline() rather than querying this attribute directly.
def NoInline : EnumAttr<"noinline", IntersectPreserve, [FnAttr]>;
/// nooutline
diff --git a/llvm/include/llvm/IR/Function.h b/llvm/include/llvm/IR/Function.h
index 17fa5efd01a9d..900739c88702a 100644
--- a/llvm/include/llvm/IR/Function.h
+++ b/llvm/include/llvm/IR/Function.h
@@ -685,6 +685,13 @@ class LLVM_ABI Function : public GlobalObject, public ilist_node<Function> {
/// Do not optimize this function (-O0).
bool hasOptNone() const { return hasFnAttribute(Attribute::OptimizeNone); }
+ /// Determine if this function should not be inlined. This is the case if the
+ /// function has the noinline attribute, or the optnone attribute (which
+ /// implies noinline). Prefer this over querying Attribute::NoInline directly.
+ bool isNoInline() const {
+ return hasFnAttribute(Attribute::NoInline) || hasOptNone();
+ }
+
/// Determine whether interprocedural transforms may rewrite this function's
/// signature.
bool canChangeSignature() const {
diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h
index 5f7df6a4eb6f8..fb8851a4637c5 100644
--- a/llvm/include/llvm/IR/InstrTypes.h
+++ b/llvm/include/llvm/IR/InstrTypes.h
@@ -1979,8 +1979,10 @@ class CallBase : public Instruction {
/// Determine if the call requires strict floating point semantics.
bool isStrictFP() const { return hasFnAttr(Attribute::StrictFP); }
- /// Return true if the call should not be inlined.
- bool isNoInline() const { return hasFnAttr(Attribute::NoInline); }
+ /// Return true if the call should not be inlined, either because the call
+ /// site is marked noinline or because the callee should not be inlined (see
+ /// Function::isNoInline).
+ LLVM_ABI bool isNoInline() const;
void setIsNoInline() { addFnAttr(Attribute::NoInline); }
LLVM_ABI MemoryEffects getMemoryEffects() const;
diff --git a/llvm/lib/Analysis/InlineCost.cpp b/llvm/lib/Analysis/InlineCost.cpp
index 0a540c1b8fb67..2c7f7aefffe08 100644
--- a/llvm/lib/Analysis/InlineCost.cpp
+++ b/llvm/lib/Analysis/InlineCost.cpp
@@ -3248,7 +3248,7 @@ std::optional<InlineResult> llvm::getAttributeBasedInliningDecision(
return InlineResult::failure("interposable");
// Don't inline functions marked noinline.
- if (Callee->hasFnAttribute(Attribute::NoInline))
+ if (Callee->isNoInline())
return InlineResult::failure("noinline function attribute");
// Don't inline call sites marked noinline.
diff --git a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
index bb5fd4eefb7f9..9ecda26f71c5d 100644
--- a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
+++ b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
@@ -744,8 +744,7 @@ static void computeFunctionSummary(
F.hasFnAttribute(Attribute::NoRecurse), F.returnDoesNotAlias(),
// FIXME: refactor this to use the same code that inliner is using.
// Don't try to import functions with noinline attribute.
- F.getAttributes().hasFnAttr(Attribute::NoInline),
- F.hasFnAttribute(Attribute::AlwaysInline),
+ F.isNoInline(), F.hasFnAttribute(Attribute::AlwaysInline),
F.hasFnAttribute(Attribute::NoUnwind), MayThrow, HasUnknownCall,
mustBeUnreachableFunction(F)};
std::vector<FunctionSummary::ParamAccess> ParamAccesses;
diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
index 84ea5e349f01d..d54bf092875ba 100644
--- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
@@ -1183,7 +1183,7 @@ void CodeViewDebug::emitDebugInfoForFunction(const Function *GV,
ProcFlags |= ProcSymFlags::HasFP;
if (GV->hasFnAttribute(Attribute::NoReturn))
ProcFlags |= ProcSymFlags::IsNoReturn;
- if (GV->hasFnAttribute(Attribute::NoInline))
+ if (GV->isNoInline())
ProcFlags |= ProcSymFlags::IsNoInline;
OS.emitInt8(static_cast<uint8_t>(ProcFlags));
// Emit the function display name as a null-terminated string.
diff --git a/llvm/lib/CodeGen/SafeStack.cpp b/llvm/lib/CodeGen/SafeStack.cpp
index 75a8762cc7090..630831c1f74de 100644
--- a/llvm/lib/CodeGen/SafeStack.cpp
+++ b/llvm/lib/CodeGen/SafeStack.cpp
@@ -727,8 +727,7 @@ bool SafeStack::ShouldInlinePointerAddress(CallInst &CI) {
if (CI.hasFnAttr(Attribute::AlwaysInline) &&
isInlineViable(*Callee).isSuccess())
return true;
- if (Callee->isInterposable() || Callee->hasFnAttribute(Attribute::NoInline) ||
- CI.isNoInline())
+ if (Callee->isInterposable() || CI.isNoInline())
return false;
return true;
}
diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp
index fa91cb79a63f9..05943fab2219b 100644
--- a/llvm/lib/IR/Instructions.cpp
+++ b/llvm/lib/IR/Instructions.cpp
@@ -460,6 +460,14 @@ bool CallBase::paramHasNonNullAttr(unsigned ArgNo,
return false;
}
+bool CallBase::isNoInline() const {
+ if (Attrs.hasFnAttr(Attribute::NoInline))
+ return true;
+ if (const auto *F = dyn_cast<Function>(getCalledOperand()))
+ return F->isNoInline();
+ return false;
+}
+
bool CallBase::hasFnAttrOnCalledFunction(Attribute::AttrKind Kind) const {
if (auto *F = dyn_cast<Function>(getCalledOperand()))
return F->getAttributes().hasFnAttr(Kind);
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 2502d574dd992..d723f7a856639 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -2542,8 +2542,9 @@ void Verifier::verifyFunctionAttrs(FunctionType *FT, AttributeList Attrs,
"Attributes 'noinline and alwaysinline' are incompatible!", V);
if (Attrs.hasFnAttr(Attribute::OptimizeNone)) {
- Check(Attrs.hasFnAttr(Attribute::NoInline),
- "Attribute 'optnone' requires 'noinline'!", V);
+ // optnone implies noinline, so it is incompatible with alwaysinline.
+ Check(!Attrs.hasFnAttr(Attribute::AlwaysInline),
+ "Attributes 'alwaysinline and optnone' are incompatible!", V);
Check(!Attrs.hasFnAttr(Attribute::OptimizeForSize),
"Attributes 'optsize and optnone' are incompatible!", V);
diff --git a/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp b/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp
index 330704d0f8857..1b7fc2983b5a7 100644
--- a/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp
@@ -68,7 +68,7 @@ static uint32_t getFunctionControl(const Function &F,
uint32_t FuncControl = static_cast<uint32_t>(SPIRV::FunctionControl::None);
- if (F.hasFnAttribute(Attribute::AttrKind::NoInline))
+ if (F.isNoInline())
FuncControl |= static_cast<uint32_t>(SPIRV::FunctionControl::DontInline);
else if (F.hasFnAttribute(Attribute::AttrKind::AlwaysInline))
FuncControl |= static_cast<uint32_t>(SPIRV::FunctionControl::Inline);
diff --git a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
index db8f835a5cedf..972f951bc042d 100644
--- a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
@@ -2104,9 +2104,9 @@ static void doSplitCoroutine(Function &F, SmallVectorImpl<Function *> &Clones,
bool isNoSuspendCoroutine = Shape.CoroSuspends.empty();
- bool shouldCreateNoAllocVariant =
- !isNoSuspendCoroutine && Shape.ABI == coro::ABI::Switch &&
- hasSafeElideCaller(F) && !F.hasFnAttribute(llvm::Attribute::NoInline);
+ bool shouldCreateNoAllocVariant = !isNoSuspendCoroutine &&
+ Shape.ABI == coro::ABI::Switch &&
+ hasSafeElideCaller(F) && !F.isNoInline();
if (Shape.ABI == coro::ABI::Switch)
Shape.SwitchLowering.HasCoroElideNoAllocVariant =
shouldCreateNoAllocVariant;
diff --git a/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp b/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp
index 3e7e467fc042a..c1ea5efb4c52a 100644
--- a/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/ForceFunctionAttrs.cpp
@@ -77,17 +77,6 @@ static bool hasConflictingFnAttr(Attribute::AttrKind Kind, Function &F) {
}
}
-static void addRequiredFnAttrs(Attribute::AttrKind Kind, Function &F) {
- if (Kind == Attribute::OptimizeNone && !F.hasFnAttribute(Attribute::NoInline))
- F.addFnAttr(Attribute::NoInline);
-}
-
-static bool wouldRemoveRequiredFnAttr(Attribute::AttrKind Kind, Function &F) {
- if (Kind == Attribute::NoInline && F.hasFnAttribute(Attribute::OptimizeNone))
- return true;
- return false;
-}
-
/// If F has any forced attributes given on the command line, add them.
/// If F has any forced remove attributes given on the command line, remove
/// them. When both force and force-remove are given to a function, the latter
@@ -116,14 +105,12 @@ static void forceAttributes(Function &F) {
if (Kind == Attribute::None || F.hasFnAttribute(Kind) ||
hasConflictingFnAttr(Kind, F))
continue;
- addRequiredFnAttrs(Kind, F);
F.addFnAttr(Kind);
}
for (const auto &S : ForceRemoveAttributes) {
auto Kind = ParseFunctionAndAttr(S);
- if (Kind == Attribute::None || !F.hasFnAttribute(Kind) ||
- wouldRemoveRequiredFnAttr(Kind, F))
+ if (Kind == Attribute::None || !F.hasFnAttribute(Kind))
continue;
F.removeFnAttr(Kind);
}
@@ -166,7 +153,6 @@ PreservedAnalyses ForceFunctionAttrsPass::run(Module &M,
!hasConflictingFnAttr(AttrKind, *Func)) {
// TODO: There could be string attributes without a value, we should
// support those, too.
- addRequiredFnAttrs(AttrKind, *Func);
Func->addFnAttr(AttrKind);
Changed = true;
} else
diff --git a/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp b/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp
index 38c31c4cc2499..348dd53f9fb06 100644
--- a/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp
@@ -687,8 +687,7 @@ bool FunctionSpecializer::run() {
// When specializing literal constants is enabled, always require functions
// to be larger than MinFunctionSize, to prevent excessive specialization.
const bool RequireMinSize =
- !ForceSpecialization &&
- (SpecializeLiteralConstant || !F.hasFnAttribute(Attribute::NoInline));
+ !ForceSpecialization && (SpecializeLiteralConstant || !F.isNoInline());
// If the code metrics reveal that we shouldn't duplicate the function,
// or if the code size implies that this function is easy to get inlined,
diff --git a/llvm/lib/Transforms/IPO/HotColdSplitting.cpp b/llvm/lib/Transforms/IPO/HotColdSplitting.cpp
index 97183c76a079b..cadb52744734f 100644
--- a/llvm/lib/Transforms/IPO/HotColdSplitting.cpp
+++ b/llvm/lib/Transforms/IPO/HotColdSplitting.cpp
@@ -258,7 +258,7 @@ bool HotColdSplitting::shouldOutlineFrom(const Function &F) const {
if (F.hasFnAttribute(Attribute::AlwaysInline))
return false;
- if (F.hasFnAttribute(Attribute::NoInline))
+ if (F.isNoInline())
return false;
// A function marked `noreturn` may contain unreachable terminators: these
diff --git a/llvm/lib/Transforms/IPO/OpenMPOpt.cpp b/llvm/lib/Transforms/IPO/OpenMPOpt.cpp
index 7bf628ce27fa8..d7929606debb2 100644
--- a/llvm/lib/Transforms/IPO/OpenMPOpt.cpp
+++ b/llvm/lib/Transforms/IPO/OpenMPOpt.cpp
@@ -6042,8 +6042,7 @@ PreservedAnalyses OpenMPOptPass::run(Module &M, ModuleAnalysisManager &AM) {
// Optionally inline device functions for potentially better performance.
if (AlwaysInlineDeviceFunctions && isOpenMPDevice(M))
for (Function &F : M)
- if (!F.isDeclaration() && !Kernels.contains(&F) &&
- !F.hasFnAttribute(Attribute::NoInline))
+ if (!F.isDeclaration() && !Kernels.contains(&F) && !F.isNoInline())
F.addFnAttr(Attribute::AlwaysInline);
if (PrintModuleAfterOptimizations)
diff --git a/llvm/lib/Transforms/IPO/PartialInlining.cpp b/llvm/lib/Transforms/IPO/PartialInlining.cpp
index 2d45578071d89..21ed6417d8ca2 100644
--- a/llvm/lib/Transforms/IPO/PartialInlining.cpp
+++ b/llvm/lib/Transforms/IPO/PartialInlining.cpp
@@ -1238,7 +1238,7 @@ std::pair<bool, Function *> PartialInlinerImpl::unswitchFunction(Function &F) {
if (F.hasFnAttribute(Attribute::AlwaysInline))
return {false, nullptr};
- if (F.hasFnAttribute(Attribute::NoInline))
+ if (F.isNoInline())
return {false, nullptr};
if (PSI.isFunctionEntryCold(&F))
diff --git a/llvm/lib/Transforms/Instrumentation/PGOCtxProfLowering.cpp b/llvm/lib/Transforms/Instrumentation/PGOCtxProfLowering.cpp
index e031b0b38f075..e2187b7d50b40 100644
--- a/llvm/lib/Transforms/Instrumentation/PGOCtxProfLowering.cpp
+++ b/llvm/lib/Transforms/Instrumentation/PGOCtxProfLowering.cpp
@@ -416,7 +416,7 @@ PreservedAnalyses NoinlineNonPrevailing::run(Module &M,
for (auto &F : M) {
if (F.isDeclaration())
continue;
- if (F.hasFnAttribute(Attribute::NoInline))
+ if (F.isNoInline())
continue;
if (!F.isWeakForLinker())
continue;
diff --git a/llvm/test/Transforms/ForcedFunctionAttrs/forced.ll b/llvm/test/Transforms/ForcedFunctionAttrs/forced.ll
index 3c1497e407a21..b7547f57c117e 100644
--- a/llvm/test/Transforms/ForcedFunctionAttrs/forced.ll
+++ b/llvm/test/Transforms/ForcedFunctionAttrs/forced.ll
@@ -84,8 +84,8 @@ attributes #5 = { noinline optnone }
; When passing an attribute to be removed without specifying a function,
; the attribute should be removed from all functions in the module that
-; have it, unless doing so would create invalid IR (e.g. `optnone` requires
-; `noinline`).
+; have it. optnone implies (but does not require) noinline, so noinline can be
+; removed from optnone functions.
; CHECK-REMOVE-ALL: define void @foo() {
; CHECK-REMOVE-ALL: define void @goo() {
; CHECK-REMOVE-ALL: define void @hoo() #0 {
@@ -97,7 +97,7 @@ attributes #5 = { noinline optnone }
; CHECK-REMOVE-ALL-DAG: attributes #1 = { optsize }
; CHECK-REMOVE-ALL-DAG: attributes #2 = { minsize }
; CHECK-REMOVE-ALL-DAG: attributes #3 = { optdebug }
-; CHECK-REMOVE-ALL-DAG: attributes #4 = { noinline optnone }
+; CHECK-REMOVE-ALL-DAG: attributes #4 = { optnone }
; When forcing alwaysinline on all functions, it should not be added to
; functions that already have noinline or optnone (would produce invalid IR).
@@ -132,19 +132,21 @@ attributes #5 = { noinline optnone }
; CHECK-NOINLINE-ALL-DAG: attributes #5 = { noinline optnone }
; When forcing optnone on all functions, it should not be added to functions
-; that already have alwaysinline, optsize, minsize, or optdebug.
+; that already have alwaysinline, optsize, minsize, or optdebug. optnone implies
+; noinline, so noinline is not added alongside it.
; CHECK-OPTNONE-ALL: define void @foo() #0 {
-; CHECK-OPTNONE-ALL: define void @goo() #0 {
-; CHECK-OPTNONE-ALL: define void @hoo() #1 {
-; CHECK-OPTNONE-ALL: define void @zoo() #2 {
-; CHECK-OPTNONE-ALL: define void @bar() #3 {
-; CHECK-OPTNONE-ALL: define void @baz() #4 {
-; CHECK-OPTNONE-ALL: define void @qoo() #0 {
-; CHECK-OPTNONE-ALL-DAG: attributes #0 = { noinline optnone }
-; CHECK-OPTNONE-ALL-DAG: attributes #1 = { alwaysinline }
-; CHECK-OPTNONE-ALL-DAG: attributes #2 = { optsize }
-; CHECK-OPTNONE-ALL-DAG: attributes #3 = { minsize }
-; CHECK-OPTNONE-ALL-DAG: attributes #4 = { optdebug }
+; CHECK-OPTNONE-ALL: define void @goo() #1 {
+; CHECK-OPTNONE-ALL: define void @hoo() #2 {
+; CHECK-OPTNONE-ALL: define void @zoo() #3 {
+; CHECK-OPTNONE-ALL: define void @bar() #4 {
+; CHECK-OPTNONE-ALL: define void @baz() #5 {
+; CHECK-OPTNONE-ALL: define void @qoo() #1 {
+; CHECK-OPTNONE-ALL-DAG: attributes #0 = { optnone }
+; CHECK-OPTNONE-ALL-DAG: attributes #1 = { noinline optnone }
+; CHECK-OPTNONE-ALL-DAG: attributes #2 = { alwaysinline }
+; CHECK-OPTNONE-ALL-DAG: attributes #3 = { optsize }
+; CHECK-OPTNONE-ALL-DAG: attributes #4 = { minsize }
+; CHECK-OPTNONE-ALL-DAG: attributes #5 = { optdebug }
; When forcing minsize on all functions, it should not be added to functions
; that already have optnone or optdebug.
diff --git a/llvm/test/Transforms/Inline/inline-optnone.ll b/llvm/test/Transforms/Inline/inline-optnone.ll
index b8f9a23a3e708..424cf3528977e 100644
--- a/llvm/test/Transforms/Inline/inline-optnone.ll
+++ b/llvm/test/Transforms/Inline/inline-optnone.ll
@@ -47,7 +47,25 @@ entry:
; CHECK-NOT: call i32 @simpleFunction(i32 6)
; CHECK: ret
+; optnone implies noinline, even without an explicit noinline attribute.
+define i32 @OptnoneOnlyFunction(i32 %a) #3 {
+entry:
+ %add = add i32 %a, %a
+ ret i32 %add
+}
+
+define i32 @baz(i32 %a) #1 {
+entry:
+ %0 = tail call i32 @OptnoneOnlyFunction(i32 5)
+ ret i32 %0
+}
+
+; CHECK-LABEL: @baz
+; CHECK: call i32 @OptnoneOnlyFunction(i32 5)
+; CHECK: ret
+
attributes #0 = { alwaysinline nounwind readnone uwtable }
attributes #1 = { nounwind readnone uwtable }
attributes #2 = { nounwind noinline optnone readnone uwtable }
+attributes #3 = { nounwind optnone readnone uwtable }
diff --git a/llvm/test/Verifier/optnone.ll b/llvm/test/Verifier/optnone.ll
new file mode 100644
index 0000000000000..67044ad364406
--- /dev/null
+++ b/llvm/test/Verifier/optnone.ll
@@ -0,0 +1,17 @@
+; RUN: not llvm-as < %s -o /dev/null 2>&1 | FileCheck %s
+
+; optnone implies noinline, so noinline is not required alongside it.
+; CHECK-NOT: optnone_only
+define void @optnone_only() optnone {
+ ret void
+}
+
+; CHECK-NOT: optnone_noinline
+define void @optnone_noinline() noinline optnone {
+ ret void
+}
+
+; CHECK: Attributes 'alwaysinline and optnone' are incompatible!
+define void @optnone_alwaysinline() alwaysinline optnone {
+ ret void
+}
diff --git a/llvm/test/tools/llvm-reduce/reduce-attributes-optnone-noinline.ll b/llvm/test/tools/llvm-reduce/reduce-attributes-optnone-noinline.ll
index 6229a3f9c2d80..1049701840389 100644
--- a/llvm/test/tools/llvm-reduce/reduce-attributes-optnone-noinline.ll
+++ b/llvm/test/tools/llvm-reduce/reduce-attributes-optnone-noinline.ll
@@ -5,9 +5,8 @@
; RUN: FileCheck --check-prefix=RESULT-OPTNONE %s < %t.1
-; Make sure this doesn't hit the "Attribute 'optnone' requires
-; 'noinline'!" verifier error. optnone can be dropped separately from
-; noinline, but removing noinline requires removing the pair together.
+; optnone implies noinline (but does not require it), so optnone and noinline
+; can each be dropped independently of the other.
...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/228169
More information about the llvm-commits
mailing list