[clang] [clang][CodeGen][MSVC] Return vector types from methods indirectly (PR #157365)
Henry Baba-Weiss via cfe-commits
cfe-commits at lists.llvm.org
Thu Sep 11 18:04:10 PDT 2025
https://github.com/henrybw updated https://github.com/llvm/llvm-project/pull/157365
>From 8f42b86f8d487b9f488a7cf7ec5d0e127f736b12 Mon Sep 17 00:00:00 2001
From: Henry Baba-Weiss <henry.babaweiss at gmail.com>
Date: Sun, 7 Sep 2025 14:16:16 -0700
Subject: [PATCH 1/3] [clang][CodeGen][MSVC] Return vector types from methods
indirectly
The MSVC ABI almost always returns vector types directly, but on x86 and x86_64,
there seems to be a special case for member functions, which return vector types
indirectly.
Fixes #104.
---
clang/docs/ReleaseNotes.rst | 3 ++
clang/lib/CodeGen/MicrosoftCXXABI.cpp | 23 +++++++-----
.../CodeGenCXX/microsoft-abi-vector-types.cpp | 36 +++++++++++++++++++
3 files changed, 53 insertions(+), 9 deletions(-)
create mode 100644 clang/test/CodeGenCXX/microsoft-abi-vector-types.cpp
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index d1ef91b7e7c14..1be917221aa2c 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -90,6 +90,9 @@ C++ Specific Potentially Breaking Changes
ABI Changes in This Version
---------------------------
+- Fixed Microsoft calling convention for returning vector types from C++ member
+ functions. Such vector types should be returned indirectly. (GH#104)
+
AST Dumping Potentially Breaking Changes
----------------------------------------
- How nested name specifiers are dumped and printed changes, keeping track of clang AST changes.
diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
index 88f0648660965..2d76eebcecd08 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -1168,15 +1168,20 @@ static bool isTrivialForMSVC(const CXXRecordDecl *RD, QualType Ty,
}
bool MicrosoftCXXABI::classifyReturnType(CGFunctionInfo &FI) const {
- const CXXRecordDecl *RD = FI.getReturnType()->getAsCXXRecordDecl();
- if (!RD)
- return false;
-
- bool isTrivialForABI = RD->canPassInRegisters() &&
- isTrivialForMSVC(RD, FI.getReturnType(), CGM);
-
- // MSVC always returns structs indirectly from C++ instance methods.
- bool isIndirectReturn = !isTrivialForABI || FI.isInstanceMethod();
+ bool isIndirectReturn = false;
+ if (const CXXRecordDecl *RD = FI.getReturnType()->getAsCXXRecordDecl()) {
+ bool isTrivialForABI = RD->canPassInRegisters() &&
+ isTrivialForMSVC(RD, FI.getReturnType(), CGM);
+
+ // MSVC always returns structs indirectly from C++ instance methods.
+ isIndirectReturn = !isTrivialForABI || FI.isInstanceMethod();
+ } else if (isa<VectorType>(FI.getReturnType())) {
+ // On x86, MSVC seems to only return vector types indirectly from non-
+ // vectorcall C++ instance methods.
+ isIndirectReturn =
+ CGM.getTarget().getTriple().isX86() && FI.isInstanceMethod() &&
+ FI.getCallingConvention() != llvm::CallingConv::X86_VectorCall;
+ }
if (isIndirectReturn) {
CharUnits Align = CGM.getContext().getTypeAlignInChars(FI.getReturnType());
diff --git a/clang/test/CodeGenCXX/microsoft-abi-vector-types.cpp b/clang/test/CodeGenCXX/microsoft-abi-vector-types.cpp
new file mode 100644
index 0000000000000..e046fb4bb3169
--- /dev/null
+++ b/clang/test/CodeGenCXX/microsoft-abi-vector-types.cpp
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -ffreestanding -emit-llvm %s -o - -triple=i686-pc-windows-msvc | FileCheck %s
+// RUN: %clang_cc1 -ffreestanding -emit-llvm %s -o - -triple=x86_64-pc-windows-msvc | FileCheck %s
+
+// To match the MSVC ABI, vector types must be returned indirectly from member
+// functions (as long as they do not use the vectorcall calling convention),
+// but must be returned directly everywhere else.
+
+#include <xmmintrin.h>
+
+struct Foo {
+ __m128 method_m128();
+ __m128 __vectorcall vectorcall_method_m128();
+};
+
+__m128 Foo::method_m128() {
+ return __m128{};
+// GH104
+// CHECK: store <4 x float>
+// CHECK: ret void
+}
+
+__m128 __vectorcall Foo::vectorcall_method_m128() {
+ return __m128{};
+// CHECK: ret <4 x float>
+}
+
+__m128 func_m128() {
+ return __m128{};
+// CHECK: ret <4 x float>
+}
+
+__m128 __vectorcall vectorcall_func_m128() {
+ return __m128{};
+// CHECK: ret <4 x float>
+}
+
>From fd8dd19556578bad4cc620fa76d4f00022269782 Mon Sep 17 00:00:00 2001
From: Henry Baba-Weiss <henry.babaweiss at gmail.com>
Date: Mon, 8 Sep 2025 16:56:50 -0700
Subject: [PATCH 2/3] amend! [clang][CodeGen][MSVC] Return vector types from
methods indirectly
[clang][CodeGen][MSVC] Return vector types from methods indirectly
The MSVC ABI almost always returns vector types directly, but on x86 and x86_64,
there seems to be a special case for member functions, which return vector types
indirectly.
This is an ABI change and has the potential to cause backward compatibility
issues with previous Clang releases.
Fixes #104.
---
clang/docs/ReleaseNotes.rst | 7 +++++--
clang/include/clang/Basic/ABIVersions.def | 6 ++++++
clang/lib/CodeGen/MicrosoftCXXABI.cpp | 5 ++++-
clang/test/CodeGenCXX/microsoft-abi-vector-types.cpp | 11 +++++++++--
4 files changed, 24 insertions(+), 5 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 1be917221aa2c..286a2889e3f36 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -90,8 +90,11 @@ C++ Specific Potentially Breaking Changes
ABI Changes in This Version
---------------------------
-- Fixed Microsoft calling convention for returning vector types from C++ member
- functions. Such vector types should be returned indirectly. (GH#104)
+- Fixed Microsoft calling convention to return vector types from C++ member
+ functions indirectly. This change resolves incompatibilities with code
+ compiled by MSVC but will introduce incompatibilities with code compiled by
+ Clang 21 and earlier versions, unless the ``-fclang-abi-compat=21`` option is
+ used. (#GH104)
AST Dumping Potentially Breaking Changes
----------------------------------------
diff --git a/clang/include/clang/Basic/ABIVersions.def b/clang/include/clang/Basic/ABIVersions.def
index f6524bc3bafb9..b8bd86f5f6aab 100644
--- a/clang/include/clang/Basic/ABIVersions.def
+++ b/clang/include/clang/Basic/ABIVersions.def
@@ -127,6 +127,12 @@ ABI_VER_MAJOR(19)
/// - Incorrectly return C++ records in AVX registers on x86_64.
ABI_VER_MAJOR(20)
+/// Attempt to be ABI-compatible with code generated by Clang 21.0.x.
+/// This causes clang to:
+/// - Return vector types directly from member functions on x86 and x86_64 on
+/// Windows, which is not compatible with the MSVC ABI.
+ABI_VER_MAJOR(21)
+
/// Conform to the underlying platform's C and C++ ABIs as closely as we can.
ABI_VER_LATEST(Latest)
diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
index 2d76eebcecd08..e687d59fc19d8 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -1180,7 +1180,10 @@ bool MicrosoftCXXABI::classifyReturnType(CGFunctionInfo &FI) const {
// vectorcall C++ instance methods.
isIndirectReturn =
CGM.getTarget().getTriple().isX86() && FI.isInstanceMethod() &&
- FI.getCallingConvention() != llvm::CallingConv::X86_VectorCall;
+ FI.getCallingConvention() != llvm::CallingConv::X86_VectorCall &&
+ // Clang <= 21.0 did not do this.
+ getContext().getLangOpts().getClangABICompat() >
+ LangOptions::ClangABI::Ver21;
}
if (isIndirectReturn) {
diff --git a/clang/test/CodeGenCXX/microsoft-abi-vector-types.cpp b/clang/test/CodeGenCXX/microsoft-abi-vector-types.cpp
index e046fb4bb3169..0fa80c1d48ee7 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-vector-types.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-vector-types.cpp
@@ -1,5 +1,11 @@
-// RUN: %clang_cc1 -ffreestanding -emit-llvm %s -o - -triple=i686-pc-windows-msvc | FileCheck %s
-// RUN: %clang_cc1 -ffreestanding -emit-llvm %s -o - -triple=x86_64-pc-windows-msvc | FileCheck %s
+// RUN: %clang_cc1 -ffreestanding -emit-llvm %s -o - -triple=i686-pc-windows-msvc \
+// RUN: | FileCheck %s
+// RUN: %clang_cc1 -ffreestanding -emit-llvm %s -o - -triple=x86_64-pc-windows-msvc \
+// RUN: | FileCheck %s
+// RUN: %clang_cc1 -ffreestanding -emit-llvm %s -o - -triple=i686-pc-windows-msvc \
+// RUN: -fclang-abi-compat=21 | FileCheck --check-prefixes=CLANG21 %s
+// RUN: %clang_cc1 -ffreestanding -emit-llvm %s -o - -triple=x86_64-pc-windows-msvc \
+// RUN: -fclang-abi-compat=21 | FileCheck --check-prefixes=CLANG21 %s
// To match the MSVC ABI, vector types must be returned indirectly from member
// functions (as long as they do not use the vectorcall calling convention),
@@ -17,6 +23,7 @@ __m128 Foo::method_m128() {
// GH104
// CHECK: store <4 x float>
// CHECK: ret void
+// CLANG21: ret <4 x float>
}
__m128 __vectorcall Foo::vectorcall_method_m128() {
>From 59701809d77f131af7d387b99d4de1b62a875b83 Mon Sep 17 00:00:00 2001
From: Henry Baba-Weiss <henry.babaweiss at gmail.com>
Date: Thu, 11 Sep 2025 17:44:42 -0700
Subject: [PATCH 3/3] fixup! [clang][CodeGen][MSVC] Return vector types from
methods indirectly
Add ARM64 coverage to the test.
---
.../CodeGenCXX/microsoft-abi-vector-types.cpp | 59 +++++++++++++++----
1 file changed, 47 insertions(+), 12 deletions(-)
diff --git a/clang/test/CodeGenCXX/microsoft-abi-vector-types.cpp b/clang/test/CodeGenCXX/microsoft-abi-vector-types.cpp
index 0fa80c1d48ee7..073a35aa5edee 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-vector-types.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-vector-types.cpp
@@ -1,16 +1,21 @@
// RUN: %clang_cc1 -ffreestanding -emit-llvm %s -o - -triple=i686-pc-windows-msvc \
-// RUN: | FileCheck %s
+// RUN: | FileCheck --check-prefixes=X86 %s
// RUN: %clang_cc1 -ffreestanding -emit-llvm %s -o - -triple=x86_64-pc-windows-msvc \
-// RUN: | FileCheck %s
+// RUN: | FileCheck --check-prefixes=X86 %s
+// RUN: %clang_cc1 -ffreestanding -emit-llvm %s -o - -triple=aarch64-pc-windows-msvc \
+// RUN: | FileCheck --check-prefixes=AARCH64 %s
// RUN: %clang_cc1 -ffreestanding -emit-llvm %s -o - -triple=i686-pc-windows-msvc \
-// RUN: -fclang-abi-compat=21 | FileCheck --check-prefixes=CLANG21 %s
+// RUN: -fclang-abi-compat=21 | FileCheck --check-prefixes=X86-CLANG21 %s
// RUN: %clang_cc1 -ffreestanding -emit-llvm %s -o - -triple=x86_64-pc-windows-msvc \
-// RUN: -fclang-abi-compat=21 | FileCheck --check-prefixes=CLANG21 %s
+// RUN: -fclang-abi-compat=21 | FileCheck --check-prefixes=X86-CLANG21 %s
+// RUN: %clang_cc1 -ffreestanding -emit-llvm %s -o - -triple=aarch64-pc-windows-msvc \
+// RUN: -fclang-abi-compat=21 | FileCheck --check-prefixes=AARCH64-CLANG21 %s
// To match the MSVC ABI, vector types must be returned indirectly from member
-// functions (as long as they do not use the vectorcall calling convention),
-// but must be returned directly everywhere else.
+// functions on x86 and x86-64 (as long as they do not use the vectorcall
+// calling convention), but must be returned directly everywhere else.
+#if defined(__i386__) || defined(__x86_64__)
#include <xmmintrin.h>
struct Foo {
@@ -21,23 +26,53 @@ struct Foo {
__m128 Foo::method_m128() {
return __m128{};
// GH104
-// CHECK: store <4 x float>
-// CHECK: ret void
-// CLANG21: ret <4 x float>
+// X86: store <4 x float>
+// X86: ret void
+// X86-CLANG21: ret <4 x float>
}
__m128 __vectorcall Foo::vectorcall_method_m128() {
return __m128{};
-// CHECK: ret <4 x float>
+// X86: ret <4 x float>
}
__m128 func_m128() {
return __m128{};
-// CHECK: ret <4 x float>
+// X86: ret <4 x float>
}
__m128 __vectorcall vectorcall_func_m128() {
return __m128{};
-// CHECK: ret <4 x float>
+// X86: ret <4 x float>
}
+#endif
+#ifdef __aarch64__
+#include <arm_neon.h>
+
+struct Foo {
+ float32x4_t method_f32x4();
+ float32x4_t __vectorcall vectorcall_method_f32x4();
+};
+
+float32x4_t Foo::method_f32x4() {
+ return float32x4_t{};
+// AARCH64: ret <4 x float>
+// AARCH64-CLANG21: ret <4 x float>
+}
+
+float32x4_t __vectorcall Foo::vectorcall_method_f32x4() {
+ return float32x4_t{};
+// AARCH64: ret <4 x float>
+}
+
+float32x4_t func_f32x4() {
+ return float32x4_t{};
+// AARCH64: ret <4 x float>
+}
+
+float32x4_t __vectorcall vectorcall_func_f32x4() {
+ return float32x4_t{};
+// AARCH64: ret <4 x float>
+}
+#endif
More information about the cfe-commits
mailing list