[clang] [Clang] Diagnose conflict between always_inline/noinline attributes (PR #215173)
Erich Keane via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 14 17:06:09 PDT 2026
https://github.com/erichkeane updated https://github.com/llvm/llvm-project/pull/215173
>From d3d3d4d502fc8728ecccd1e1dc283af7dd52d5d4 Mon Sep 17 00:00:00 2001
From: Kenzo <kenzoeugeniotjandra at gmail.com>
Date: Sun, 9 Aug 2026 22:19:40 -0400
Subject: [PATCH 1/4] [Clang] Diagnose conflict between always_inline/noinline
attributes
---
clang/include/clang/Basic/Attr.td | 2 +
clang/lib/CIR/CodeGen/CIRGenStmt.cpp | 9 +++-
clang/lib/CodeGen/CGStmt.cpp | 14 ++++--
clang/lib/Sema/SemaDecl.cpp | 7 ++-
.../CodeGen/attr-noinline-always-inline.cpp | 49 +++++++++++++++++++
.../attr-noinline-always-inline-conflict.cpp | 37 ++++++++++++++
6 files changed, 110 insertions(+), 8 deletions(-)
create mode 100644 clang/test/CodeGen/attr-noinline-always-inline.cpp
create mode 100644 clang/test/SemaCXX/attr-noinline-always-inline-conflict.cpp
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 7d66e42700eef..342e2dc595761 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 39919cc28981a..f7de3ece4ad1e 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 18f1e87d3245d..5f3baea41c2d2 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -782,10 +782,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;
@@ -801,9 +801,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;
@@ -832,6 +834,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 032737c7a191d..38db391a7e5a1 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/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}}
>From 9c7f81eaf5e3b5092a9988b4abcd11ec571a60be Mon Sep 17 00:00:00 2001
From: Kenzo <kenzoeugeniotjandra at gmail.com>
Date: Mon, 10 Aug 2026 20:07:15 -0400
Subject: [PATCH 2/4] [Clang] Add tests for CIRGen inlining, add more CGStmt
inling tests
---
.../CodeGen/callsite-inline-attributes.cpp | 8 ++++
.../CodeGen/attr-noinline-always-inline.cpp | 37 ++++++++++++-------
clang/test/Sema/attr-noinline.cpp | 34 +++++++++++++++++
.../attr-noinline-always-inline-conflict.cpp | 37 -------------------
4 files changed, 66 insertions(+), 50 deletions(-)
delete mode 100644 clang/test/SemaCXX/attr-noinline-always-inline-conflict.cpp
diff --git a/clang/test/CIR/CodeGen/callsite-inline-attributes.cpp b/clang/test/CIR/CodeGen/callsite-inline-attributes.cpp
index 13a4fcfbb690c..aefa67d6926e2 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 = #cir.inline_kind<no_inline>}
// 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
index 208bf13a1d1cb..35d3132468a00 100644
--- a/clang/test/CodeGen/attr-noinline-always-inline.cpp
+++ b/clang/test/CodeGen/attr-noinline-always-inline.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s
template <typename T>
[[clang::noinline]] void foo(T) {}
@@ -18,25 +18,36 @@ void caller() {
void inner_fn();
void outer_fn() {
- [[clang::noinline]]
- {
- [[clang::nomerge]] // unrelated to inling
- inner_fn();
- }
+ [[clang::noinline]]
+ {
+ [[clang::nomerge]] // unrelated to inlining
+ 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();
- }
+ [[clang::nomerge]]
+ {
+ [[clang::noinline]] // unrelated to nomerge
+ inner_fn2();
+ }
+}
+// CHECK: call void @_Z9inner_fn2v() #[[NOINLINE_NOMERGE]]
+
+// Inner function should clobber a conflicting attribute
+void inner_fn3();
+
+void outer_fn3() {
+ [[clang::noinline]]
+ {
+ [[clang::always_inline]]
+ inner_fn3();
+ }
}
-// CHECK: call void @_Z9inner_fn2v() #[[ALWAYSINLINE_ONLY:[0-9]+]]
+// CHECK: call void @_Z9inner_fn3v() #[[ALWAYSINLINE_ONLY:[0-9]+]]
// CHECK: attributes #[[ALWAYSINLINE]] = {
// CHECK-SAME: alwaysinline
diff --git a/clang/test/Sema/attr-noinline.cpp b/clang/test/Sema/attr-noinline.cpp
index d6d48321d6604..b16ee660602d0 100644
--- a/clang/test/Sema/attr-noinline.cpp
+++ b/clang/test/Sema/attr-noinline.cpp
@@ -119,3 +119,37 @@ void use() {
qux<3>(0); // #QUX_INST
variadic_qux<0, 1, 2>(0); // #QUX_VARIADIC_INST
}
+
+// Statement attributes are mutually exclusive
+void caller() {
+ [[clang::noinline, clang::always_inline]] bar(); // expected-error {{'clang::always_inline' and 'clang::noinline' attributes are not compatible}} \
+ // expected-note {{conflicting attribute is here}}
+
+ [[clang::always_inline, clang::noinline]] bar(); // 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}}
diff --git a/clang/test/SemaCXX/attr-noinline-always-inline-conflict.cpp b/clang/test/SemaCXX/attr-noinline-always-inline-conflict.cpp
deleted file mode 100644
index cd50bf41e6c1f..0000000000000
--- a/clang/test/SemaCXX/attr-noinline-always-inline-conflict.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-// 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}}
>From aa97e882137df7041b8e4b598b29fcc179d5a78f Mon Sep 17 00:00:00 2001
From: Kenzo <kenzoeugeniotjandra at gmail.com>
Date: Tue, 11 Aug 2026 19:54:21 -0400
Subject: [PATCH 3/4] [Clang] Add tests for bare template specializations, and
noconvergent/alwaysinline attributes
---
.../CodeGen/attr-noinline-always-inline.cpp | 42 ++++++++++++++++---
1 file changed, 37 insertions(+), 5 deletions(-)
diff --git a/clang/test/CodeGen/attr-noinline-always-inline.cpp b/clang/test/CodeGen/attr-noinline-always-inline.cpp
index 35d3132468a00..ee7fa86a85dd7 100644
--- a/clang/test/CodeGen/attr-noinline-always-inline.cpp
+++ b/clang/test/CodeGen/attr-noinline-always-inline.cpp
@@ -1,17 +1,22 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -O1 -disable-llvm-passes -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s
template <typename T>
[[clang::noinline]] void foo(T) {}
-// Explicit specialization should not inherit noinline
+// Explicit specializations should not inherit noinline
template <>
[[clang::always_inline]] void foo<int>(int) {}
+template <>
+void foo<long>(long) {}
+
void caller() {
foo<float>(4.2f); // expect noinline on function
foo<int>(42); // expect alwaysinline on function
+ foo<long>(42L); // expect no noinline attribute
}
// CHECK: define {{.*}}void @_Z3fooIiEvT_({{.*}}) #[[ALWAYSINLINE:[0-9]+]]
+// CHECK: define {{.*}}void @_Z3fooIlEvT_({{.*}}) #[[BARE:[0-9]+]]
// CHECK: define {{.*}}void @_Z3fooIfEvT_({{.*}}) #[[NOINLINE:[0-9]+]]
// Inner function should not clobber non-conflicting attributes
@@ -37,21 +42,48 @@ void outer_fn2() {
}
// CHECK: call void @_Z9inner_fn2v() #[[NOINLINE_NOMERGE]]
-// Inner function should clobber a conflicting attribute
+[[clang::convergent]]
void inner_fn3();
void outer_fn3() {
- [[clang::noinline]]
+ [[clang::always_inline]]
{
- [[clang::always_inline]]
+ [[clang::noconvergent]] // unrelated to inlining
inner_fn3();
}
}
// CHECK: call void @_Z9inner_fn3v() #[[ALWAYSINLINE_ONLY:[0-9]+]]
+[[clang::convergent]]
+void inner_fn4();
+
+void outer_fn4() {
+ [[clang::noconvergent]]
+ {
+ [[clang::always_inline]] // unrelated to noconvergent
+ inner_fn4();
+ }
+}
+// CHECK: call void @_Z9inner_fn4v() #[[ALWAYSINLINE_ONLY]]
+
+// Inner function should clobber a conflicting attribute
+void inner_fn5();
+
+void outer_fn5() {
+ [[clang::noinline]]
+ {
+ [[clang::always_inline]]
+ inner_fn5();
+ }
+}
+// CHECK: call void @_Z9inner_fn5v() #[[ALWAYSINLINE_ONLY]]
+
// CHECK: attributes #[[ALWAYSINLINE]] = {
// CHECK-SAME: alwaysinline
// CHECK-NOT: noinline
+// CHECK: attributes #[[BARE]] = {
+// CHECK-NOT: alwaysinline
+// CHECK-NOT: noinline
// CHECK: attributes #[[NOINLINE]] = {
// CHECK-SAME: noinline
// CHECK-NOT: alwaysinline
>From 2e6a3ec29fdbca4fb0d7cc7ece8b751e9f6d318c Mon Sep 17 00:00:00 2001
From: Kenzo <kenzoeugeniotjandra at gmail.com>
Date: Thu, 13 Aug 2026 22:32:41 -0400
Subject: [PATCH 4/4] [Clang] Ensure all AttributedStmt attributes are
preserved, added tests for HLSL branching
---
clang/lib/CIR/CodeGen/CIRGenStmt.cpp | 2 +-
clang/lib/CodeGen/CGStmt.cpp | 7 +++---
.../test/CodeGenHLSL/HLSLControlFlowHint.hlsl | 22 +++++++++++++++++++
3 files changed, 26 insertions(+), 5 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
index f7de3ece4ad1e..28c82794614d5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
@@ -92,7 +92,7 @@ CIRGenFunction::emitAttributedStmt(const AttributedStmt &s) {
bool noinline = inNoInlineAttributedStmt;
bool alwaysinline = inAlwaysInlineAttributedStmt;
- const CallExpr *musttail = nullptr;
+ const CallExpr *musttail = mustTailCall;
for (const Attr *attr : s.getAttrs()) {
switch (attr->getKind()) {
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 5f3baea41c2d2..bf6e6eb50f555 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -786,10 +786,9 @@ void CodeGenFunction::EmitAttributedStmt(const AttributedStmt &S) {
bool noinline = InNoInlineAttributedStmt;
bool alwaysinline = InAlwaysInlineAttributedStmt;
bool noconvergent = InNoConvergentAttributedStmt;
- StringRef amdgpuAVMode;
- HLSLControlFlowHintAttr::Spelling flattenOrBranch =
- HLSLControlFlowHintAttr::SpellingNotCalculated;
- const CallExpr *musttail = nullptr;
+ StringRef amdgpuAVMode = AMDGPUAvailableVisibleMode;
+ HLSLControlFlowHintAttr::Spelling flattenOrBranch = HLSLControlFlowAttr;
+ const CallExpr *musttail = MustTailCall;
const AtomicAttr *AA = nullptr;
for (const auto *A : S.getAttrs()) {
diff --git a/clang/test/CodeGenHLSL/HLSLControlFlowHint.hlsl b/clang/test/CodeGenHLSL/HLSLControlFlowHint.hlsl
index 6737cd3ee78ba..09ae698d3d8c0 100644
--- a/clang/test/CodeGenHLSL/HLSLControlFlowHint.hlsl
+++ b/clang/test/CodeGenHLSL/HLSLControlFlowHint.hlsl
@@ -44,5 +44,27 @@ export int test_no_attr(int X){
return resp;
}
+// CHECK-LABEL: test_nested_branch_preserved
+// CHECK: br i1 %{{.*}}, label %if.then, label %if.end{{.*}}, !hlsl.controlflow.hint [[HINT_BRANCH]]
+// CHECK: br i1 %{{.*}}, label %if.then{{.*}}, label %if.end{{.*}}, !hlsl.controlflow.hint [[HINT_BRANCH]]
+void foo(int);
+export void test_nested_branch_preserved(int X, int Y) {
+ [branch] if (X) {
+ [[clang::noinline]] // unrelated to branch
+ if (Y) foo(Y);
+ }
+}
+
+// CHECK-LABEL: test_nested_flatten_preserved
+// CHECK: br i1 %{{.*}}, label %if.then, label %if.end{{.*}}, !hlsl.controlflow.hint [[HINT_FLATTEN]]
+// CHECK: br i1 %{{.*}}, label %if.then{{.*}}, label %if.end{{.*}}, !hlsl.controlflow.hint [[HINT_FLATTEN]]
+void bar(int);
+export void test_nested_flatten_preserved(int X, int Y) {
+ [flatten] if (X) {
+ [[clang::noinline]] // unrelated to flatten
+ if (Y) bar(Y);
+ }
+}
+
//CHECK: [[HINT_BRANCH]] = !{!"hlsl.controlflow.hint", i32 1}
//CHECK: [[HINT_FLATTEN]] = !{!"hlsl.controlflow.hint", i32 2}
More information about the cfe-commits
mailing list