https://github.com/mike-goutokuji created 
https://github.com/llvm/llvm-project/pull/203925

- Fix Clang returning vectorcall HVA/HFA types via `sret` when they are not 
C++14 aggregates (e.g. they have base classes, protected members, or 
user-provided constructors).
- MSVC returns these types in XMM registers on x86/x64 vectorcall; Clang was 
incorrectly treating them as indirect returns after applying the usual MSVC 
C++14 aggregate rules in `MicrosoftCXXABI::classifyReturnType`.
- Allow direct register returns for `__vectorcall` functions when the return 
type is a homogeneous aggregate and can still be passed in registers. This is 
analogous to the AArch64 HVA return handling added for #62223, but on x86 
vectorcall the same relaxation also applies to HFAs.

Fixes #63417

>From 84c1658eefe2c20626fe5a71d1b4a686510c26b8 Mon Sep 17 00:00:00 2001
From: Mike-Goutokuji <[email protected]>
Date: Mon, 15 Jun 2026 11:34:03 -0400
Subject: [PATCH] [clang] Fix vectorcall HVA/HFA returns on x86 MSVC ABI

- Fix Clang returning vectorcall HVA/HFA types via `sret` when they are not 
C++14 aggregates (e.g. they have base classes, protected members, or 
user-provided constructors).
- MSVC returns these types in XMM registers on x86/x64 vectorcall; Clang was 
incorrectly treating them as indirect returns after applying the usual MSVC 
C++14 aggregate rules in `MicrosoftCXXABI::classifyReturnType`.
- Allow direct register returns for `__vectorcall` functions when the return 
type is a homogeneous aggregate and can still be passed in registers.
This is analogous to the AArch64 HVA return handling added for #62223, but on 
x86 vectorcall the same relaxation also applies to HFAs.

Fixes #63417
---
 clang/lib/CodeGen/MicrosoftCXXABI.cpp         | 11 +++++++
 .../CodeGenCXX/homogeneous-aggregates.cpp     | 22 ++++++++++++++
 .../microsoft-abi-vectorcall-hva.cpp          | 30 +++++++++++++++++++
 3 files changed, 63 insertions(+)
 create mode 100644 clang/test/CodeGenCXX/microsoft-abi-vectorcall-hva.cpp

diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp 
b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
index 40c7c00d85395..b24595036a930 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -1192,6 +1192,17 @@ bool MicrosoftCXXABI::classifyReturnType(CGFunctionInfo 
&FI) const {
   bool isTrivialForABI = RD->canPassInRegisters() &&
                          isTrivialForMSVC(RD, FI.getReturnType(), CGM);
 
+  // On x86 and x64, vectorcall returns HVAs and HFAs in registers even if they
+  // are not C++14 aggregates (e.g. they have base classes), as long as they 
can
+  // still be passed in registers and qualify as homogeneous aggregates.
+  if (!isTrivialForABI && RD->canPassInRegisters() &&
+      FI.getCallingConvention() == llvm::CallingConv::X86_VectorCall) {
+    const Type *Base = nullptr;
+    uint64_t NumElts = 0;
+    if (CGM.getABIInfo().isHomogeneousAggregate(FI.getReturnType(), Base, 
NumElts))
+      isTrivialForABI = true;
+  }
+
   // MSVC always returns structs indirectly from C++ instance methods.
   bool isIndirectReturn = !isTrivialForABI || FI.isInstanceMethod();
 
diff --git a/clang/test/CodeGenCXX/homogeneous-aggregates.cpp 
b/clang/test/CodeGenCXX/homogeneous-aggregates.cpp
index 278c19b384c92..6365472fc4b3e 100644
--- a/clang/test/CodeGenCXX/homogeneous-aggregates.cpp
+++ b/clang/test/CodeGenCXX/homogeneous-aggregates.cpp
@@ -302,3 +302,25 @@ struct test2 : base2 { test2(double); protected: double 
v2;};
 test2 f(test2 *x) { return *x; }
 // WOA64: define dso_local void @"?f@pr62223@@YA?AUtest2@1@PEAU21@@Z"(ptr 
dead_on_unwind inreg noalias writable sret(%"struct.pr62223::test2") align 8 
%{{.*}}, ptr noundef %{{.*}})
 }
+
+namespace pr113104 {
+// On x86/x64 vectorcall, both HVAs and HFAs are returned in registers even 
when
+// they are not C++14 aggregates (unlike WOA64, where only HVAs get this
+// treatment — see pr62223 above).
+struct HFA {
+  float a;
+  float b;
+};
+
+using HVA = float __attribute__((__vector_size__(16), __aligned__(16)));
+
+struct base_hfa { HFA v1; };
+struct test_hfa : base_hfa { test_hfa(double); protected: HFA v2; };
+test_hfa CC f(test_hfa *x) { return *x; }
+// X64: define dso_local x86_vectorcallcc %"struct.pr113104::test_hfa" 
@"\01_ZN8pr1131041fEPNS_8test_hfaE@@8"(ptr noundef %x)
+
+struct base_hva { HVA v1; };
+struct test_hva : base_hva { test_hva(double); protected: HVA v2; };
+test_hva CC f(test_hva *x) { return *x; }
+// X64: define dso_local x86_vectorcallcc %"struct.pr113104::test_hva" 
@"\01_ZN8pr1131041fEPNS_8test_hvaE@@8"(ptr noundef %x)
+}
diff --git a/clang/test/CodeGenCXX/microsoft-abi-vectorcall-hva.cpp 
b/clang/test/CodeGenCXX/microsoft-abi-vectorcall-hva.cpp
new file mode 100644
index 0000000000000..7739813088111
--- /dev/null
+++ b/clang/test/CodeGenCXX/microsoft-abi-vectorcall-hva.cpp
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -triple i686-pc-windows-msvc -emit-llvm -o - %s | FileCheck 
%s --check-prefix=X86
+// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -emit-llvm -o - %s | 
FileCheck %s --check-prefix=X64
+
+typedef float __m128 __attribute__((__vector_size__(16)));
+
+// HVA with base class
+struct base_hva { __m128 v; };
+struct test_hva : base_hva { test_hva(double); protected: __m128 v2; };
+
+// HFA with base class
+struct base_hfa { double v; };
+struct test_hfa : base_hfa { test_hfa(double); protected: double v2; };
+
+// 1. Vectorcall returns should be direct (not sret)
+test_hva __vectorcall ret_hva_vectorcall(test_hva *x) { return *x; }
+// X86-LABEL: define dso_local x86_vectorcallcc %struct.test_hva 
@"?ret_hva_vectorcall@@YQ?AUtest_hva@@PAU1@@Z"(ptr inreg noundef %x)
+// X64-LABEL: define dso_local x86_vectorcallcc %struct.test_hva 
@"?ret_hva_vectorcall@@YQ?AUtest_hva@@PEAU1@@Z"(ptr noundef %x)
+
+test_hfa __vectorcall ret_hfa_vectorcall(test_hfa *x) { return *x; }
+// X86-LABEL: define dso_local x86_vectorcallcc %struct.test_hfa 
@"?ret_hfa_vectorcall@@YQ?AUtest_hfa@@PAU1@@Z"(ptr inreg noundef %x)
+// X64-LABEL: define dso_local x86_vectorcallcc %struct.test_hfa 
@"?ret_hfa_vectorcall@@YQ?AUtest_hfa@@PEAU1@@Z"(ptr noundef %x)
+
+// 2. Cdecl returns should be indirect (sret) because they are not aggregates
+test_hva __cdecl ret_hva_cdecl(test_hva *x) { return *x; }
+// X86-LABEL: define dso_local void 
@"?ret_hva_cdecl@@YA?AUtest_hva@@PAU1@@Z"(ptr dead_on_unwind noalias writable 
sret(%struct.test_hva) align 16 %agg.result, ptr noundef %x)
+// X64-LABEL: define dso_local void 
@"?ret_hva_cdecl@@YA?AUtest_hva@@PEAU1@@Z"(ptr dead_on_unwind noalias writable 
sret(%struct.test_hva) align 16 %agg.result, ptr noundef %x)
+
+test_hfa __cdecl ret_hfa_cdecl(test_hfa *x) { return *x; }
+// X86-LABEL: define dso_local void 
@"?ret_hfa_cdecl@@YA?AUtest_hfa@@PAU1@@Z"(ptr dead_on_unwind noalias writable 
sret(%struct.test_hfa) align 8 %agg.result, ptr noundef %x)
+// X64-LABEL: define dso_local void 
@"?ret_hfa_cdecl@@YA?AUtest_hfa@@PEAU1@@Z"(ptr dead_on_unwind noalias writable 
sret(%struct.test_hfa) align 8 %agg.result, ptr noundef %x)

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to