[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:
@@ -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">;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user