[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 Oct 2 21:05:22 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/7] [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/7] 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/7] 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

>From 017bfea1483ea46405eb78932822d4b914abfdb9 Mon Sep 17 00:00:00 2001
From: Henry Baba-Weiss <henry.babaweiss at gmail.com>
Date: Thu, 11 Sep 2025 18:44:36 -0700
Subject: [PATCH 4/7] fixup! [clang][CodeGen][MSVC] Return vector types from
 methods indirectly

Simplify the test by conditionally defining the platform's vector type.
---
 .../CodeGenCXX/microsoft-abi-vector-types.cpp | 75 +++++++------------
 1 file changed, 26 insertions(+), 49 deletions(-)

diff --git a/clang/test/CodeGenCXX/microsoft-abi-vector-types.cpp b/clang/test/CodeGenCXX/microsoft-abi-vector-types.cpp
index 073a35aa5edee..487bedbd11c37 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-vector-types.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-vector-types.cpp
@@ -1,15 +1,15 @@
 // RUN: %clang_cc1 -ffreestanding -emit-llvm %s -o - -triple=i686-pc-windows-msvc \
-// RUN:   | FileCheck --check-prefixes=X86 %s
+// RUN:   | FileCheck --check-prefixes=CHECK,X86 %s
 // RUN: %clang_cc1 -ffreestanding -emit-llvm %s -o - -triple=x86_64-pc-windows-msvc \
-// RUN:   | FileCheck --check-prefixes=X86 %s
+// RUN:   | FileCheck --check-prefixes=CHECK,X86 %s
 // RUN: %clang_cc1 -ffreestanding -emit-llvm %s -o - -triple=aarch64-pc-windows-msvc \
-// RUN:   | FileCheck --check-prefixes=AARCH64 %s
+// RUN:   | FileCheck --check-prefixes=CHECK,AARCH64 %s
 // RUN: %clang_cc1 -ffreestanding -emit-llvm %s -o - -triple=i686-pc-windows-msvc \
-// RUN:   -fclang-abi-compat=21 | FileCheck --check-prefixes=X86-CLANG21 %s
+// RUN:   -fclang-abi-compat=21 | FileCheck --check-prefixes=CHECK,CLANG21 %s
 // RUN: %clang_cc1 -ffreestanding -emit-llvm %s -o - -triple=x86_64-pc-windows-msvc \
-// RUN:   -fclang-abi-compat=21 | FileCheck --check-prefixes=X86-CLANG21 %s
+// RUN:   -fclang-abi-compat=21 | FileCheck --check-prefixes=CHECK,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
+// RUN:   -fclang-abi-compat=21 | FileCheck --check-prefixes=CHECK,CLANG21 %s
 
 // To match the MSVC ABI, vector types must be returned indirectly from member
 // functions on x86 and x86-64 (as long as they do not use the vectorcall
@@ -17,62 +17,39 @@
 
 #if defined(__i386__) || defined(__x86_64__)
 #include <xmmintrin.h>
-
-struct Foo {
-  __m128 method_m128();
-  __m128 __vectorcall vectorcall_method_m128();
-};
-
-__m128 Foo::method_m128() {
-  return __m128{};
-// GH104
-// X86: store <4 x float>
-// X86: ret void
-// X86-CLANG21: ret <4 x float>
-}
-
-__m128 __vectorcall Foo::vectorcall_method_m128() {
-  return __m128{};
-// X86: ret <4 x float>
-}
-
-__m128 func_m128() {
-  return __m128{};
-// X86: ret <4 x float>
-}
-
-__m128 __vectorcall vectorcall_func_m128() {
-  return __m128{};
-// X86: ret <4 x float>
-}
+#define VECTOR_TYPE __m128
 #endif
 
 #ifdef __aarch64__
 #include <arm_neon.h>
+#define VECTOR_TYPE float32x4_t
+#endif
 
 struct Foo {
-  float32x4_t method_f32x4();
-  float32x4_t __vectorcall vectorcall_method_f32x4();
+  VECTOR_TYPE method_ret_vec();
+  VECTOR_TYPE __vectorcall vectorcall_method_ret_vec();
 };
 
-float32x4_t Foo::method_f32x4() {
-  return float32x4_t{};
+VECTOR_TYPE Foo::method_ret_vec() {
+  return VECTOR_TYPE{};
+// GH104
+// X86: store <4 x float>
+// X86: ret void
 // AARCH64: ret <4 x float>
-// AARCH64-CLANG21: ret <4 x float>
+// CLANG21: ret <4 x float>
 }
 
-float32x4_t __vectorcall Foo::vectorcall_method_f32x4() {
-  return float32x4_t{};
-// AARCH64: ret <4 x float>
+VECTOR_TYPE __vectorcall Foo::vectorcall_method_ret_vec() {
+  return VECTOR_TYPE{};
+// CHECK: ret <4 x float>
 }
 
-float32x4_t func_f32x4() {
-  return float32x4_t{};
-// AARCH64: ret <4 x float>
+VECTOR_TYPE func_ret_vec() {
+  return VECTOR_TYPE{};
+// CHECK: ret <4 x float>
 }
 
-float32x4_t __vectorcall vectorcall_func_f32x4() {
-  return float32x4_t{};
-// AARCH64: ret <4 x float>
+VECTOR_TYPE __vectorcall vectorcall_func_ret_vec() {
+  return VECTOR_TYPE{};
+// CHECK: ret <4 x float>
 }
-#endif

>From c0f4b2256209e6c2f06b118fc4852b9e991ca7aa Mon Sep 17 00:00:00 2001
From: Henry Baba-Weiss <henry.babaweiss at gmail.com>
Date: Thu, 2 Oct 2025 20:23:52 -0700
Subject: [PATCH 5/7] amend! [clang][CodeGen][MSVC] Return vector types from
 methods indirectly

[clang][CodeGen][MSVC] Match how MSVC returns vector types from member functions

The MSVC ABI usually returns vector types directly, but on x86 and x86-64, there
seems to be a special case for C++ member functions, which return vector types
indirectly (with the exception of member functions using the `__vectorcall`
calling convention, which return vector types > 64 bits directly).

This is an ABI change and has the potential to cause backward compatibility
issues with previous Clang releases.

Fixes #104.
---
 clang/lib/CodeGen/MicrosoftCXXABI.cpp | 32 ++++++++++++++++-----------
 1 file changed, 19 insertions(+), 13 deletions(-)

diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
index e687d59fc19d8..05b132ada92a3 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -1168,26 +1168,32 @@ static bool isTrivialForMSVC(const CXXRecordDecl *RD, QualType Ty,
 }
 
 bool MicrosoftCXXABI::classifyReturnType(CGFunctionInfo &FI) const {
+  QualType RetTy = FI.getReturnType();
   bool isIndirectReturn = false;
-  if (const CXXRecordDecl *RD = FI.getReturnType()->getAsCXXRecordDecl()) {
-    bool isTrivialForABI = RD->canPassInRegisters() &&
-                           isTrivialForMSVC(RD, FI.getReturnType(), CGM);
+
+  if (const CXXRecordDecl *RD = RetTy->getAsCXXRecordDecl()) {
+    bool isTrivialForABI =
+        RD->canPassInRegisters() && isTrivialForMSVC(RD, RetTy, 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 &&
-        // Clang <= 21.0 did not do this.
-        getContext().getLangOpts().getClangABICompat() >
-            LangOptions::ClangABI::Ver21;
+  } else if (isa<VectorType>(RetTy) &&
+             getContext().getLangOpts().getClangABICompat() >
+                 LangOptions::ClangABI::Ver21) {
+    // On x86, MSVC usually returns vector types indirectly from C++ instance
+    // methods. (Clang <= 21.0 always returned vector types directly.)
+    if (CGM.getTarget().getTriple().isX86() && FI.isInstanceMethod()) {
+      // However, MSVC returns vector types > 64 bits directly from vectorcall
+      // instance methods.
+      if (FI.getCallingConvention() == llvm::CallingConv::X86_VectorCall)
+        isIndirectReturn = getContext().getTypeSize(RetTy) == 64;
+      else
+        isIndirectReturn = true;
+    }
   }
 
   if (isIndirectReturn) {
-    CharUnits Align = CGM.getContext().getTypeAlignInChars(FI.getReturnType());
+    CharUnits Align = CGM.getContext().getTypeAlignInChars(RetTy);
     FI.getReturnInfo() = ABIArgInfo::getIndirect(
         Align, /*AddrSpace=*/CGM.getDataLayout().getAllocaAddrSpace(),
         /*ByVal=*/false);

>From c6b0cd3308cc6882ebd5bbb223a25d5fef7f86b3 Mon Sep 17 00:00:00 2001
From: Henry Baba-Weiss <henry.babaweiss at gmail.com>
Date: Tue, 30 Sep 2025 22:05:35 -0700
Subject: [PATCH 6/7] fixup! [clang][CodeGen][MSVC] Return vector types from
 methods indirectly

Rework the test to also cover 64-bit vector types.
---
 .../CodeGenCXX/microsoft-abi-vector-types.cpp | 70 ++++++++++++++-----
 1 file changed, 54 insertions(+), 16 deletions(-)

diff --git a/clang/test/CodeGenCXX/microsoft-abi-vector-types.cpp b/clang/test/CodeGenCXX/microsoft-abi-vector-types.cpp
index 487bedbd11c37..c80d4674ade53 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-vector-types.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-vector-types.cpp
@@ -11,45 +11,83 @@
 // RUN: %clang_cc1 -ffreestanding -emit-llvm %s -o - -triple=aarch64-pc-windows-msvc \
 // RUN:   -fclang-abi-compat=21 | FileCheck --check-prefixes=CHECK,CLANG21 %s
 
-// To match the MSVC ABI, vector types must be returned indirectly from member
-// functions on x86 and x86-64 (as long as they do not use the vectorcall
-// calling convention), but must be returned directly everywhere else.
+// To match the MSVC ABI, vector types are usually returned directly, but on x86
+// and x86-64 they must be returned indirectly from member functions (unless
+// they use the vectorcall calling convention and the vector type is > 64 bits).
 
 #if defined(__i386__) || defined(__x86_64__)
 #include <xmmintrin.h>
-#define VECTOR_TYPE __m128
+
+#define VECTOR64_TYPE __m64
+#define VECTOR128_TYPE __m128
+
+#define VECTORCALL __vectorcall
 #endif
 
 #ifdef __aarch64__
 #include <arm_neon.h>
-#define VECTOR_TYPE float32x4_t
+
+// These were chosen such that they lower to the same types that the x86 vector
+// types lower to (e.g. int64x1_t and __m64 both lower to <1 x i64>).
+#define VECTOR64_TYPE int64x1_t
+#define VECTOR128_TYPE float32x4_t
+
+#define VECTORCALL
 #endif
 
 struct Foo {
-  VECTOR_TYPE method_ret_vec();
-  VECTOR_TYPE __vectorcall vectorcall_method_ret_vec();
+  VECTOR64_TYPE method_ret_vec64();
+  VECTOR128_TYPE method_ret_vec128();
+
+  VECTOR64_TYPE VECTORCALL vc_method_ret_vec64();
+  VECTOR128_TYPE VECTORCALL vc_method_ret_vec128();
 };
 
-VECTOR_TYPE Foo::method_ret_vec() {
-  return VECTOR_TYPE{};
-// GH104
+VECTOR64_TYPE Foo::method_ret_vec64() {
+  return VECTOR64_TYPE{};
+// X86: store <1 x i64>
+// X86: ret void
+// AARCH64: ret <1 x i64>
+// CLANG21: ret <1 x i64>
+}
+
+VECTOR128_TYPE Foo::method_ret_vec128() {
+  return VECTOR128_TYPE{};
 // X86: store <4 x float>
 // X86: ret void
 // AARCH64: ret <4 x float>
 // CLANG21: ret <4 x float>
 }
 
-VECTOR_TYPE __vectorcall Foo::vectorcall_method_ret_vec() {
-  return VECTOR_TYPE{};
+VECTOR64_TYPE VECTORCALL Foo::vc_method_ret_vec64() {
+  return VECTOR64_TYPE{};
+// X86: store <1 x i64>
+// X86: ret void
+// AARCH64: ret <1 x i64>
+// CLANG21: ret <1 x i64>
+}
+
+VECTOR128_TYPE VECTORCALL Foo::vc_method_ret_vec128() {
+  return VECTOR128_TYPE{};
 // CHECK: ret <4 x float>
 }
 
-VECTOR_TYPE func_ret_vec() {
-  return VECTOR_TYPE{};
+VECTOR64_TYPE func_ret_vec64() {
+  return VECTOR64_TYPE{};
+// CHECK: ret <1 x i64>
+}
+
+VECTOR128_TYPE func_ret_vec128() {
+  return VECTOR128_TYPE{};
 // CHECK: ret <4 x float>
 }
 
-VECTOR_TYPE __vectorcall vectorcall_func_ret_vec() {
-  return VECTOR_TYPE{};
+VECTOR64_TYPE VECTORCALL vc_func_ret_vec64() {
+  return VECTOR64_TYPE{};
+// CHECK: ret <1 x i64>
+}
+
+VECTOR128_TYPE VECTORCALL vc_func_ret_vec128() {
+  return VECTOR128_TYPE{};
 // CHECK: ret <4 x float>
 }

>From 71cd7fd8eae20ead470d1eb22c1c813b54093dce Mon Sep 17 00:00:00 2001
From: Henry Baba-Weiss <henry.babaweiss at gmail.com>
Date: Thu, 2 Oct 2025 20:47:14 -0700
Subject: [PATCH 7/7] fixup! [clang][CodeGen][MSVC] Return vector types from
 methods indirectly

Tweak wording of ABI note and update release note to say Clang now matches MSVC.
---
 clang/docs/ReleaseNotes.rst               | 10 +++++-----
 clang/include/clang/Basic/ABIVersions.def |  4 ++--
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 286a2889e3f36..36e7baff58e79 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -90,11 +90,11 @@ C++ Specific Potentially Breaking Changes
 ABI Changes in This Version
 ---------------------------
 
-- 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)
+- Fixed Microsoft calling convention to match how MSVC returns vector types from
+  C++ member functions on x86-64. 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 b8bd86f5f6aab..a08338db22992 100644
--- a/clang/include/clang/Basic/ABIVersions.def
+++ b/clang/include/clang/Basic/ABIVersions.def
@@ -129,8 +129,8 @@ 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.
+///   - Always 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.



More information about the cfe-commits mailing list