https://github.com/trungnt2910 updated https://github.com/llvm/llvm-project/pull/185282
>From f261399901d7c0141f4aac7029f92848ff22ed81 Mon Sep 17 00:00:00 2001 From: Trung Nguyen <[email protected]> Date: Mon, 9 Mar 2026 01:28:03 +1100 Subject: [PATCH] [clang] Add support for MSVC force inline attrs Add support for `[[msvc::forceinline]]` and `[[msvc::forceinline_calls]]`. `[[msvc::forceinline]]` is equivalent to Microsoft's `__forceinline` when placed before a function declaration. Unlike `__forceinline`, `[[msvc::forceinline]]` works with lambdas. `[[msvc::forceinline_calls]]` is simliar to `[[clang::always_inline]]` but only works on statements. Both are implemented as aliases of `[[clang::always_inline]]` with special checks. Fixes #186539. --- clang/docs/ReleaseNotes.rst | 5 +++ clang/include/clang/Basic/Attr.td | 10 ++++- .../clang/Basic/DiagnosticSemaKinds.td | 5 +++ clang/lib/Sema/SemaDeclAttr.cpp | 12 ++++++ clang/lib/Sema/SemaStmtAttr.cpp | 12 +++++- clang/test/CodeGen/attr-ms-forceinline.cpp | 33 ++++++++++++++++ clang/test/Sema/attr-ms-forceinline.c | 32 ++++++++++++++++ clang/test/Sema/attr-ms-forceinline.cpp | 38 +++++++++++++++++++ 8 files changed, 144 insertions(+), 3 deletions(-) create mode 100644 clang/test/CodeGen/attr-ms-forceinline.cpp create mode 100644 clang/test/Sema/attr-ms-forceinline.c create mode 100644 clang/test/Sema/attr-ms-forceinline.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 3341ec17c87cd..7f6049b77fa7e 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -252,6 +252,11 @@ Attribute Changes in Clang sound because any writer must hold all capabilities, so holding any one prevents concurrent writes. +- Added support for ``[[msvc::forceinline]]`` for functions and + ``[[msvc::forceinline_calls]]`` for statements. Both are aliases to + ``[[clang::always_inline]]`` with additional checks to ensure that they + are only accepted in places where MSVC also does. + Improvements to Clang's diagnostics ----------------------------------- - ``-Wunused-but-set-variable`` now diagnoses file-scope variables with diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 8d716bd05b6ab..328e70b3ed900 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -934,9 +934,15 @@ def AlignNatural : InheritableAttr { def AlwaysInline : DeclOrStmtAttr { let Spellings = [GCC<"always_inline">, CXX11<"clang", "always_inline">, - C23<"clang", "always_inline">, CustomKeyword<"__forceinline">]; + C23<"clang", "always_inline">, CustomKeyword<"__forceinline">, + CXX11<"msvc", "forceinline">, C23<"msvc", "forceinline">, + CXX11<"msvc", "forceinline_calls">, C23<"msvc", "forceinline_calls">]; let Accessors = [Accessor<"isClangAlwaysInline", [CXX11<"clang", "always_inline">, - C23<"clang", "always_inline">]>]; + C23<"clang", "always_inline">]>, + Accessor<"isMSVCForceInline", [CXX11<"msvc", "forceinline">, + C23<"msvc", "forceinline">]>, + Accessor<"isMSVCForceInlineCalls", [CXX11<"msvc", "forceinline_calls">, + C23<"msvc", "forceinline_calls">]>]; let Subjects = SubjectList<[Function, Stmt], WarnDiag, "functions and statements">; let Documentation = [AlwaysInlineDocs]; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 6b9fa4a257397..603e91d598f79 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3277,6 +3277,11 @@ def warn_function_attribute_ignored_in_stmt : Warning< "use '%0' on statements">, InGroup<IgnoredAttributes>; +def warn_stmt_attribute_ignored_in_function : Warning< + "attribute is ignored on this function as it only applies to statements; " + "use '%0' for functions">, + InGroup<IgnoredAttributes>; + def err_musttail_needs_trivial_args : Error< "tail call requires that the return value, all parameters, and any " "temporaries created by the expression are trivially destructible">; diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 63a70e4b77bc4..8a856215a9627 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -5181,6 +5181,18 @@ OptimizeNoneAttr *Sema::mergeOptimizeNoneAttr(Decl *D, } static void handleAlwaysInlineAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + AlwaysInlineAttr AIA(S.Context, AL); + if (!S.getLangOpts().MicrosoftExt && + (AIA.isMSVCForceInline() || AIA.isMSVCForceInlineCalls())) { + S.Diag(AL.getLoc(), diag::warn_attribute_ignored) << AL; + return; + } + if (AIA.isMSVCForceInlineCalls()) { + S.Diag(AL.getLoc(), diag::warn_stmt_attribute_ignored_in_function) + << "[[msvc::forceinline]]"; + return; + } + if (AlwaysInlineAttr *Inline = S.mergeAlwaysInlineAttr(D, AL, AL.getAttrName())) D->addAttr(Inline); diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp index f7de98a3e5cf0..58d5332565d10 100644 --- a/clang/lib/Sema/SemaStmtAttr.cpp +++ b/clang/lib/Sema/SemaStmtAttr.cpp @@ -314,7 +314,17 @@ static Attr *handleNoInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A, static Attr *handleAlwaysInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A, SourceRange Range) { AlwaysInlineAttr AIA(S.Context, A); - if (!AIA.isClangAlwaysInline()) { + if (!S.getLangOpts().MicrosoftExt && + (AIA.isMSVCForceInline() || AIA.isMSVCForceInlineCalls())) { + S.Diag(St->getBeginLoc(), diag::warn_attribute_ignored) << A; + return nullptr; + } + if (AIA.isMSVCForceInline()) { + S.Diag(St->getBeginLoc(), diag::warn_function_attribute_ignored_in_stmt) + << "[[msvc::forceinline_calls]]"; + return nullptr; + } + if (!AIA.isClangAlwaysInline() && !AIA.isMSVCForceInlineCalls()) { S.Diag(St->getBeginLoc(), diag::warn_function_attribute_ignored_in_stmt) << "[[clang::always_inline]]"; return nullptr; diff --git a/clang/test/CodeGen/attr-ms-forceinline.cpp b/clang/test/CodeGen/attr-ms-forceinline.cpp new file mode 100644 index 0000000000000..795e6705f7826 --- /dev/null +++ b/clang/test/CodeGen/attr-ms-forceinline.cpp @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 -std=c++23 -disable-llvm-passes -emit-llvm %s -triple x86_64-pc-windows-msvc -fms-extensions -o - | FileCheck -check-prefix=CHECK-MSVC %s +// RUN: %clang_cc1 -std=c++23 -disable-llvm-passes -emit-llvm %s -triple x86_64-unknown-linux-gnu -fms-extensions -o - | FileCheck -check-prefix=CHECK-LINUX %s + +// CHECK-MSVC: define {{.*}} @"?foo@@YAXXZ"() #[[ATTR_FOO:[0-9]+]] +// CHECK-LINUX: define {{.*}} @_Z3foov() #[[ATTR_FOO:[0-9]+]] +// Add another attribute to prevent overlapping sets. +[[msvc::forceinline, gnu::hot]] void foo() {} + +void bar(); + +void call_lambda() { + auto lambda = [] [[msvc::forceinline]] () { bar(); }; + lambda(); +} + +// CHECK-MSVC: define internal void @"??R<lambda_0>@?0??call_lambda@@YAXXZ@QEBA?A?<auto>@@XZ"{{.*}} #[[ATTR_LAMBDA:[0-9]+]] +// CHECK-LINUX: define internal void @"_ZZ11call_lambdavENK3$_0clEv"{{.*}} #[[ATTR_LAMBDA:[0-9]+]] + +void call_bar() { +// CHECK-MSVC-LABEL: define {{.*}} @"?call_bar@@YAXXZ"() +// CHECK-MSVC: call void @"?bar@@YAXXZ"() #[[ATTR_CALLSITE:[0-9]+]] +// CHECK-LINUX-LABEL: define {{.*}} @_Z8call_barv() +// CHECK-LINUX: call void @_Z3barv() #[[ATTR_CALLSITE:[0-9]+]] + [[msvc::forceinline_calls]] bar(); +} + +// CHECK-MSVC-DAG: attributes #[[ATTR_FOO]] = { alwaysinline hot {{.*}}} +// CHECK-MSVC-DAG: attributes #[[ATTR_LAMBDA]] = { alwaysinline {{.*}}} +// CHECK-MSVC-DAG: attributes #[[ATTR_CALLSITE]] = { alwaysinline } + +// CHECK-LINUX-DAG: attributes #[[ATTR_FOO]] = { alwaysinline hot {{.*}}} +// CHECK-LINUX-DAG: attributes #[[ATTR_LAMBDA]] = { alwaysinline {{.*}}} +// CHECK-LINUX-DAG: attributes #[[ATTR_CALLSITE]] = { alwaysinline } diff --git a/clang/test/Sema/attr-ms-forceinline.c b/clang/test/Sema/attr-ms-forceinline.c new file mode 100644 index 0000000000000..1da92e89f2178 --- /dev/null +++ b/clang/test/Sema/attr-ms-forceinline.c @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -verify=ms-ext -fms-extensions -fsyntax-only -std=c23 %s +// RUN: %clang_cc1 -verify=no-ms-ext -fsyntax-only -std=c23 %s + +void foo(void); + +[[msvc::forceinline]] void func(void) {} +// no-ms-ext-warning@-1 {{'msvc::forceinline' attribute ignored}} + +void stmt_forceinline(void) { + [[msvc::forceinline]] func(); + // ms-ext-warning@-1 {{attribute is ignored on this statement as it only applies to functions; use '[[msvc::forceinline_calls]]' on statements}} + // no-ms-ext-warning@-2 {{'msvc::forceinline' attribute ignored}} +} + +[[msvc::forceinline_calls]] void func2(void) {} +// ms-ext-warning@-1 {{attribute is ignored on this function as it only applies to statements; use '[[msvc::forceinline]]' for functions}} +// no-ms-ext-warning@-2 {{'msvc::forceinline_calls' attribute ignored}} + +void stmt_forceinline_calls(void) { + [[msvc::forceinline_calls]] foo(); + // no-ms-ext-warning@-1 {{'msvc::forceinline_calls' attribute ignored}} +} + +[[msvc::forceinline(0)]] void func3(void); +// ms-ext-error@-1 {{'msvc::forceinline' attribute takes no arguments}} +// no-ms-ext-error@-2 {{'msvc::forceinline' attribute takes no arguments}} + +void func4(void) { + [[msvc::forceinline_calls("foo")]] foo(); + // ms-ext-error@-1 {{'msvc::forceinline_calls' attribute takes no arguments}} + // no-ms-ext-error@-2 {{'msvc::forceinline_calls' attribute takes no arguments}} +} diff --git a/clang/test/Sema/attr-ms-forceinline.cpp b/clang/test/Sema/attr-ms-forceinline.cpp new file mode 100644 index 0000000000000..5e1d2b3eeee85 --- /dev/null +++ b/clang/test/Sema/attr-ms-forceinline.cpp @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -verify=ms-ext -fms-extensions -fsyntax-only -std=c++23 %s +// RUN: %clang_cc1 -verify=no-ms-ext -fsyntax-only -std=c++23 %s + +void foo(); + +[[msvc::forceinline]] void func() {} +// no-ms-ext-warning@-1 {{'msvc::forceinline' attribute ignored}} + +void lambda_func() { + auto l = [] [[msvc::forceinline]] () {}; + // no-ms-ext-warning@-1 {{'msvc::forceinline' attribute ignored}} + l(); +} + +void stmt_forceinline() { + [[msvc::forceinline]] func(); + // ms-ext-warning@-1 {{attribute is ignored on this statement as it only applies to functions; use '[[msvc::forceinline_calls]]' on statements}} + // no-ms-ext-warning@-2 {{'msvc::forceinline' attribute ignored}} +} + +[[msvc::forceinline_calls]] void func2() {} +// ms-ext-warning@-1 {{attribute is ignored on this function as it only applies to statements; use '[[msvc::forceinline]]' for functions}} +// no-ms-ext-warning@-2 {{'msvc::forceinline_calls' attribute ignored}} + +void stmt_forceinline_calls() { + [[msvc::forceinline_calls]] foo(); + // no-ms-ext-warning@-1 {{'msvc::forceinline_calls' attribute ignored}} +} + +[[msvc::forceinline(0)]] void func3(); +// ms-ext-error@-1 {{'msvc::forceinline' attribute takes no arguments}} +// no-ms-ext-error@-2 {{'msvc::forceinline' attribute takes no arguments}} + +void func4() { + [[msvc::forceinline_calls("foo")]] foo(); + // ms-ext-error@-1 {{'msvc::forceinline_calls' attribute takes no arguments}} + // no-ms-ext-error@-2 {{'msvc::forceinline_calls' attribute takes no arguments}} +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
