[clang] [clang] Fix vectorcall HVA/HFA returns on x86 MSVC ABI (PR #203925)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 15 08:53:08 PDT 2026
https://github.com/mike-goutokuji updated https://github.com/llvm/llvm-project/pull/203925
>From ff296ce3f10fe0732e7f1786017d56841968327f Mon Sep 17 00:00:00 2001
From: Mike-Goutokuji <gfunni234 at gmail.com>
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 | 12 ++++++++
.../CodeGenCXX/homogeneous-aggregates.cpp | 22 ++++++++++++++
.../microsoft-abi-vectorcall-hva.cpp | 30 +++++++++++++++++++
3 files changed, 64 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..42a33028d5a88 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -1192,6 +1192,18 @@ 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 at pr62223@@YA?AUtest2 at 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)
More information about the cfe-commits
mailing list