[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