llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clangir

@llvm/pr-subscribers-clang

Author: Kenzo Tjandra (ketjandr)

<details>
<summary>Changes</summary>

This PR aims to diagnose `always_inline` and `noinline` conflicts which 
currently could pass silently through `Sema` or cause a crash (with assertions 
enabled). Changes include:

- Added `MutualExclusions&lt;[AlwaysInline, NoInline]&gt;` in `Attr.td`
- Prevent `always_inline` and `noinline` from being propagated from generic to 
specialized templates in `SemaDecl.cpp`
- Prevent inner attributes from clobbering unrelated outer attributes (e.g. 
`noinline` and `nomerge`) in both `CGStmt.cpp` and `CIRGenStmt.cpp`

Something to consider though: Should we prevent propagating conflicting 
attributes when doing explicit template specializations in general, not just 
`inline`, e.g. `hot`/`cold`, `convergent`/`noconvergent`, etc.

Fixes #<!-- -->214764

---
Full diff: https://github.com/llvm/llvm-project/pull/215173.diff


7 Files Affected:

- (modified) clang/include/clang/Basic/Attr.td (+2) 
- (modified) clang/lib/CIR/CodeGen/CIRGenStmt.cpp (+7-2) 
- (modified) clang/lib/CodeGen/CGStmt.cpp (+10-4) 
- (modified) clang/lib/Sema/SemaDecl.cpp (+5-2) 
- (modified) clang/test/CIR/CodeGen/callsite-inline-attributes.cpp (+8) 
- (added) clang/test/CodeGen/attr-noinline-always-inline.cpp (+49) 
- (added) clang/test/SemaCXX/attr-noinline-always-inline-conflict.cpp (+37) 


``````````diff
diff --git a/clang/include/clang/Basic/Attr.td 
b/clang/include/clang/Basic/Attr.td
index d874ccb1b8653..0bf81c312640f 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -2423,6 +2423,8 @@ def NoInline : DeclOrStmtAttr {
   let SimpleHandler = 1;
 }
 
+def : MutualExclusions<[AlwaysInline, NoInline]>;
+
 def NoOutline : DeclOrStmtAttr {
   let Spellings = [Clang<"no_outline">];
   let Subjects = SubjectList<[Function, ObjCMethod, Block], ErrorDiag>;
diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp 
b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
index 38b65f16197eb..48ef88cef9621 100644
--- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
@@ -90,8 +90,8 @@ mlir::LogicalResult 
CIRGenFunction::emitCompoundStmtWithoutScope(
 mlir::LogicalResult
 CIRGenFunction::emitAttributedStmt(const AttributedStmt &s) {
 
-  bool noinline = false;
-  bool alwaysinline = false;
+  bool noinline = inNoInlineAttributedStmt;
+  bool alwaysinline = inAlwaysInlineAttributedStmt;
   const CallExpr *musttail = nullptr;
 
   for (const Attr *attr : s.getAttrs()) {
@@ -108,9 +108,11 @@ CIRGenFunction::emitAttributedStmt(const AttributedStmt 
&s) {
       break;
     case attr::NoInline:
       noinline = true;
+      alwaysinline = false;
       break;
     case attr::AlwaysInline:
       alwaysinline = true;
+      noinline = false;
       break;
     case attr::MustTail: {
       const Stmt *sub = s.getSubStmt();
@@ -131,6 +133,9 @@ CIRGenFunction::emitAttributedStmt(const AttributedStmt &s) 
{
     }
   }
 
+  assert(!(alwaysinline && noinline) &&
+         "alwaysinline and noinline are mutually exclusive");
+
   SaveAndRestore save_noinline(inNoInlineAttributedStmt, noinline);
   SaveAndRestore save_alwaysinline(inAlwaysInlineAttributedStmt, alwaysinline);
 
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 73f6c3c4aff1d..828edd109a9ac 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -779,10 +779,10 @@ void CodeGenFunction::EmitLabelStmt(const LabelStmt &S) {
 }
 
 void CodeGenFunction::EmitAttributedStmt(const AttributedStmt &S) {
-  bool nomerge = false;
-  bool noinline = false;
-  bool alwaysinline = false;
-  bool noconvergent = false;
+  bool nomerge = InNoMergeAttributedStmt;
+  bool noinline = InNoInlineAttributedStmt;
+  bool alwaysinline = InAlwaysInlineAttributedStmt;
+  bool noconvergent = InNoConvergentAttributedStmt;
   StringRef amdgpuAVMode;
   HLSLControlFlowHintAttr::Spelling flattenOrBranch =
       HLSLControlFlowHintAttr::SpellingNotCalculated;
@@ -798,9 +798,11 @@ void CodeGenFunction::EmitAttributedStmt(const 
AttributedStmt &S) {
       break;
     case attr::NoInline:
       noinline = true;
+      alwaysinline = false;
       break;
     case attr::AlwaysInline:
       alwaysinline = true;
+      noinline = false;
       break;
     case attr::NoConvergent:
       noconvergent = true;
@@ -829,6 +831,10 @@ void CodeGenFunction::EmitAttributedStmt(const 
AttributedStmt &S) {
     } break;
     }
   }
+
+  assert(!(alwaysinline && noinline) &&
+         "alwaysinline and noinline are mutually exclusive");
+
   SaveAndRestore save_nomerge(InNoMergeAttributedStmt, nomerge);
   SaveAndRestore save_noinline(InNoInlineAttributedStmt, noinline);
   SaveAndRestore save_alwaysinline(InAlwaysInlineAttributedStmt, alwaysinline);
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index c5dcdee7dc5dd..ccb080af6cb26 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -3389,11 +3389,14 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl 
*Old,
     if (isa<UsedAttr>(I) || isa<RetainAttr>(I))
       continue;
 
-    if (isa<InferredNoReturnAttr>(I)) {
+    // Don't propagate inferred noreturn or conflicting inline attributes to
+    // explicit specializations.
+    if (isa<InferredNoReturnAttr>(I) || isa<AlwaysInlineAttr>(I) ||
+        isa<NoInlineAttr>(I)) {
       if (auto *FD = dyn_cast<FunctionDecl>(New);
           FD &&
           FD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
-        continue; // Don't propagate inferred noreturn attributes to explicit
+        continue;
     }
 
     if (mergeDeclAttribute(*this, New, I, LocalAMK))
diff --git a/clang/test/CIR/CodeGen/callsite-inline-attributes.cpp 
b/clang/test/CIR/CodeGen/callsite-inline-attributes.cpp
index 06422d36539c6..b4b2c2386cf6e 100644
--- a/clang/test/CIR/CodeGen/callsite-inline-attributes.cpp
+++ b/clang/test/CIR/CodeGen/callsite-inline-attributes.cpp
@@ -49,6 +49,14 @@ void caller() {
     // CIR: cir.call %{{.*}}() {inline_kind = 1 : i32}
     // LLVM: call void %{{.*}}() #[[NOINLINE]]
   }
+
+  [[clang::noinline]]
+  {
+    [[clang::always_inline]]
+    callee();
+    // CIR: cir.call @_Z6calleev() {inline_kind = 2 : i32}
+    // LLVM: call void @_Z6calleev() #[[ALWAYSINLINE]]
+  }
 }
 
 // LLVM: attributes #[[ALWAYSINLINE]] = { alwaysinline }
diff --git a/clang/test/CodeGen/attr-noinline-always-inline.cpp 
b/clang/test/CodeGen/attr-noinline-always-inline.cpp
new file mode 100644
index 0000000000000..208bf13a1d1cb
--- /dev/null
+++ b/clang/test/CodeGen/attr-noinline-always-inline.cpp
@@ -0,0 +1,49 @@
+// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s
+
+template <typename T>
+[[clang::noinline]] void foo(T) {}
+
+// Explicit specialization should not inherit noinline
+template <>
+[[clang::always_inline]] void foo<int>(int) {}
+
+void caller() {
+  foo<float>(4.2f); // expect noinline on function
+  foo<int>(42); // expect alwaysinline on function
+}
+// CHECK: define {{.*}}void @_Z3fooIiEvT_({{.*}}) #[[ALWAYSINLINE:[0-9]+]]
+// CHECK: define {{.*}}void @_Z3fooIfEvT_({{.*}}) #[[NOINLINE:[0-9]+]]
+
+// Inner function should not clobber non-conflicting attributes
+void inner_fn();
+
+void outer_fn() {
+    [[clang::noinline]]
+    {
+        [[clang::nomerge]] // unrelated to inling
+        inner_fn();
+    }
+}
+// CHECK: call void @_Z8inner_fnv() #[[NOINLINE_NOMERGE:[0-9]+]]
+
+// Inner function should clobber a conflicting attribute
+void inner_fn2();
+
+void outer_fn2() {
+    [[clang::noinline]]
+    {
+        [[clang::always_inline]]
+        inner_fn2();
+    }
+}
+// CHECK: call void @_Z9inner_fn2v() #[[ALWAYSINLINE_ONLY:[0-9]+]]
+
+// CHECK: attributes #[[ALWAYSINLINE]] = {
+// CHECK-SAME: alwaysinline
+// CHECK-NOT:  noinline
+// CHECK: attributes #[[NOINLINE]] = {
+// CHECK-SAME: noinline
+// CHECK-NOT:  alwaysinline
+
+// CHECK: attributes #[[NOINLINE_NOMERGE]] = { noinline nomerge }
+// CHECK: attributes #[[ALWAYSINLINE_ONLY]] = { alwaysinline }
diff --git a/clang/test/SemaCXX/attr-noinline-always-inline-conflict.cpp 
b/clang/test/SemaCXX/attr-noinline-always-inline-conflict.cpp
new file mode 100644
index 0000000000000..cd50bf41e6c1f
--- /dev/null
+++ b/clang/test/SemaCXX/attr-noinline-always-inline-conflict.cpp
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 %s -std=c++17 -fsyntax-only -verify
+
+void foo() {}
+
+// Statement attributes are mutually exclusive
+void caller() {
+  [[clang::noinline, clang::always_inline]] foo(); // expected-error 
{{'clang::always_inline' and 'clang::noinline' attributes are not compatible}} \
+                                                   // expected-note 
{{conflicting attribute is here}}
+
+  [[clang::always_inline, clang::noinline]] foo(); // expected-error 
{{'clang::noinline' and 'clang::always_inline' attributes are not compatible}} \
+                                                   // expected-note 
{{conflicting attribute is here}}
+}
+
+// Attributes on redeclared functions are mutually exclusive
+[[clang::noinline]] void redecl_fn(); // expected-note {{conflicting attribute 
is here}}
+[[clang::always_inline]] void redecl_fn() {} // expected-error 
{{'clang::always_inline' and 'clang::noinline' attributes are not compatible}}
+
+[[clang::always_inline]] void redecl_fn2(); // expected-note {{conflicting 
attribute is here}}
+[[clang::noinline]] void redecl_fn2() {} // expected-error {{'clang::noinline' 
and 'clang::always_inline' attributes are not compatible}}
+
+// Attributes on the same declaration are mutually exclusive
+[[clang::noinline, clang::always_inline]] void decl_fn(); // expected-error 
{{'clang::always_inline' and 'clang::noinline' attributes are not compatible}} \
+                                                          // expected-note 
{{conflicting attribute is here}}
+
+// Explicit specialization should not inherit inline attributes
+template <typename T>
+[[clang::noinline]] void tmpl_fn(T);
+
+template <>
+[[clang::always_inline]] void tmpl_fn(int); // no error expected
+
+// Check different spellings
+[[gnu::noinline]] void spelling_fn(); // expected-note {{conflicting attribute 
is here}}
+[[gnu::always_inline]] void spelling_fn() {} // expected-error 
{{'gnu::always_inline' and 'gnu::noinline' attributes are not compatible}}
+
+__attribute__((noinline)) void spelling_fn2(); // expected-note {{conflicting 
attribute is here}}
+__attribute__((always_inline)) void spelling_fn2() {} // expected-error 
{{'always_inline' and 'noinline' attributes are not compatible}}

``````````

</details>


https://github.com/llvm/llvm-project/pull/215173
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to