[Clang] Add support for [[msvc::noinline]] attribute. (#91720)

Fixes #90941.
Add support for ``[[msvc::noinline]]`` attribute, which is actually an
alias of ``[[clang::noinline]]``.
This commit is contained in:
Xu Zhang
2024-05-28 21:29:31 +08:00
committed by GitHub
parent 6e1a04247d
commit 8995ccc446
4 changed files with 86 additions and 5 deletions

View File

@@ -2025,9 +2025,12 @@ def Convergent : InheritableAttr {
def NoInline : DeclOrStmtAttr {
let Spellings = [CustomKeyword<"__noinline__">, GCC<"noinline">,
CXX11<"clang", "noinline">, C23<"clang", "noinline">,
CXX11<"msvc", "noinline">, C23<"msvc", "noinline">,
Declspec<"noinline">];
let Accessors = [Accessor<"isClangNoInline", [CXX11<"clang", "noinline">,
C23<"clang", "noinline">]>];
let Accessors = [Accessor<"isStmtNoInline", [CXX11<"clang", "noinline">,
C23<"clang", "noinline">,
CXX11<"msvc", "noinline">,
C23<"msvc", "noinline">]>];
let Documentation = [NoInlineDocs];
let Subjects = SubjectList<[Function, Stmt], WarnDiag,
"functions and statements">;

View File

@@ -285,7 +285,7 @@ bool Sema::CheckAlwaysInlineAttr(const Stmt *OrigSt, const Stmt *CurSt,
static Attr *handleNoInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A,
SourceRange Range) {
NoInlineAttr NIA(S.Context, A);
if (!NIA.isClangNoInline()) {
if (!NIA.isStmtNoInline()) {
S.Diag(St->getBeginLoc(), diag::warn_function_attribute_ignored_in_stmt)
<< "[[clang::noinline]]";
return nullptr;

View File

@@ -9,6 +9,7 @@ static int baz(int x) {
}
[[clang::noinline]] bool noi() { }
[[msvc::noinline]] bool ms_noi() { return true; }
void foo(int i) {
[[clang::noinline]] bar();
@@ -39,6 +40,31 @@ void foo(int i) {
// CHECK: call noundef zeroext i1 @_Z3barv()
}
void ms_noi_check(int i) {
[[msvc::noinline]] bar();
// CHECK: call noundef zeroext i1 @_Z3barv() #[[NOINLINEATTR:[0-9]+]]
[[msvc::noinline]] i = baz(i);
// CHECK: call noundef i32 @_ZL3bazi({{.*}}) #[[NOINLINEATTR]]
[[msvc::noinline]] (i = 4, bar());
// CHECK: call noundef zeroext i1 @_Z3barv() #[[NOINLINEATTR]]
[[msvc::noinline]] (void)(bar());
// CHECK: call noundef zeroext i1 @_Z3barv() #[[NOINLINEATTR]]
[[msvc::noinline]] f(bar(), bar());
// CHECK: call noundef zeroext i1 @_Z3barv() #[[NOINLINEATTR]]
// CHECK: call noundef zeroext i1 @_Z3barv() #[[NOINLINEATTR]]
// CHECK: call void @_Z1fbb({{.*}}) #[[NOINLINEATTR]]
[[msvc::noinline]] [] { bar(); bar(); }(); // noinline only applies to the anonymous function call
// CHECK: call void @"_ZZ12ms_noi_checkiENK3$_0clEv"(ptr {{[^,]*}} %ref.tmp) #[[NOINLINEATTR]]
[[msvc::noinline]] for (bar(); bar(); bar()) {}
// CHECK: call noundef zeroext i1 @_Z3barv() #[[NOINLINEATTR]]
// CHECK: call noundef zeroext i1 @_Z3barv() #[[NOINLINEATTR]]
// CHECK: call noundef zeroext i1 @_Z3barv() #[[NOINLINEATTR]]
[[msvc::noinline]] ms_noi();
// CHECK: call noundef zeroext i1 @_Z6ms_noiv()
ms_noi();
// CHECK: call noundef zeroext i1 @_Z6ms_noiv()
}
struct S {
friend bool operator==(const S &LHS, const S &RHS);
};
@@ -50,6 +76,12 @@ void func(const S &s1, const S &s2) {
bool b;
[[clang::noinline]] b = s1 == s2;
// CHECK: call noundef zeroext i1 @_ZeqRK1SS1_({{.*}}) #[[NOINLINEATTR]]
[[msvc::noinline]]g(s1 == s2);
// CHECK: call noundef zeroext i1 @_ZeqRK1SS1_({{.*}}) #[[NOINLINEATTR]]
// CHECK: call void @_Z1gb({{.*}}) #[[NOINLINEATTR]]
[[msvc::noinline]] b = s1 == s2;
// CHECK: call noundef zeroext i1 @_ZeqRK1SS1_({{.*}}) #[[NOINLINEATTR]]
}
// CHECK: attributes #[[NOINLINEATTR]] = { noinline }

View File

@@ -2,9 +2,9 @@
int bar();
// expected-note@+1{{conflicting attribute is here}}
// expected-note@+1 2 {{conflicting attribute is here}}
[[gnu::always_inline]] void always_inline_fn(void) { }
// expected-note@+1{{conflicting attribute is here}}
// expected-note@+1 2 {{conflicting attribute is here}}
[[gnu::flatten]] void flatten_fn(void) { }
[[gnu::noinline]] void noinline_fn(void) { }
@@ -25,7 +25,21 @@ void foo() {
__attribute__((noinline)) bar(); // expected-warning {{attribute is ignored on this statement as it only applies to functions; use '[[clang::noinline]]' on statements}}
}
void ms_noi_check() {
[[msvc::noinline]] bar();
[[msvc::noinline(0)]] bar(); // expected-error {{'noinline' attribute takes no arguments}}
int x;
[[msvc::noinline]] x = 0; // expected-warning {{'noinline' attribute is ignored because there exists no call expression inside the statement}}
[[msvc::noinline]] { asm("nop"); } // expected-warning {{'noinline' attribute is ignored because there exists no call expression inside the statement}}
[[msvc::noinline]] label: x = 1; // expected-warning {{'noinline' attribute only applies to functions and statements}}
[[msvc::noinline]] always_inline_fn(); // expected-warning {{statement attribute 'noinline' has higher precedence than function attribute 'always_inline'}}
[[msvc::noinline]] flatten_fn(); // expected-warning {{statement attribute 'noinline' has higher precedence than function attribute 'flatten'}}
[[msvc::noinline]] noinline_fn();
}
[[clang::noinline]] static int i = bar(); // expected-warning {{'noinline' attribute only applies to functions and statements}}
[[msvc::noinline]] static int j = bar(); // expected-warning {{'noinline' attribute only applies to functions and statements}}
// This used to crash the compiler.
template<int D>
@@ -69,7 +83,39 @@ int variadic_baz(int x) {
[[clang::noinline]] return non_dependent(x) + (dependent<D>(x) + ...);
}
template<int D> [[clang::always_inline]]
int qux(int x) { // #QUX
// expected-warning@+2{{statement attribute 'noinline' has higher precedence than function attribute 'always_inline'}}
// expected-note@#NO_DEP{{conflicting attribute is here}}
[[msvc::noinline]] non_dependent(x);
if constexpr (D>0) {
// expected-warning@+6{{statement attribute 'noinline' has higher precedence than function attribute 'always_inline'}}
// expected-note@#NO_DEP{{conflicting attribute is here}}
// expected-warning@+4 3{{statement attribute 'noinline' has higher precedence than function attribute 'always_inline'}}
// expected-note@#QUX 3{{conflicting attribute is here}}
// expected-note@#QUX_INST 3{{in instantiation}}
// expected-note@+1 3{{in instantiation}}
[[msvc::noinline]] return non_dependent(x), qux<D-1>(x + 1);
}
return x;
}
// We can't suppress if there is a variadic involved.
template<int ... D>
int variadic_qux(int x) {
// Diagnoses NO_DEP 2x, once during phase 1, the second during instantiation.
// Dianoses DEP 3x, once per variadic expansion.
// expected-warning@+5 2{{statement attribute 'noinline' has higher precedence than function attribute 'always_inline'}}
// expected-note@#NO_DEP 2{{conflicting attribute is here}}
// expected-warning@+3 3{{statement attribute 'noinline' has higher precedence than function attribute 'always_inline'}}
// expected-note@#DEP 3{{conflicting attribute is here}}
// expected-note@#QUX_VARIADIC_INST{{in instantiation}}
[[msvc::noinline]] return non_dependent(x) + (dependent<D>(x) + ...);
}
void use() {
baz<3>(0); // #BAZ_INST
variadic_baz<0, 1, 2>(0); // #VARIADIC_INST
qux<3>(0); // #QUX_INST
variadic_qux<0, 1, 2>(0); // #QUX_VARIADIC_INST
}