Skip to content

Commit 14ea80b

Browse files
committed
[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.
1 parent 6806349 commit 14ea80b

File tree

4 files changed

+125
-10
lines changed

4 files changed

+125
-10
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,12 @@ ABI Changes in This Version
124124
---------------------------
125125
- Fix AArch64 argument passing for C++ empty classes with large explicitly specified alignment.
126126

127+
- Fixed Microsoft calling convention to match how MSVC returns vector types from
128+
C++ member functions on x86/x86-64. This change resolves incompatibilities with
129+
code compiled by MSVC but will introduce incompatibilities with code compiled
130+
by Clang 21 and earlier versions, unless the ``-fclang-abi-compat=21`` option
131+
is used. (#GH104)
132+
127133
AST Dumping Potentially Breaking Changes
128134
----------------------------------------
129135
- How nested name specifiers are dumped and printed changes, keeping track of clang AST changes.

clang/include/clang/Basic/ABIVersions.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ ABI_VER_MAJOR(20)
133133
/// compatible with scalar deleting destructors emitted by MSVC for the
134134
/// cases when the class whose destructor is being emitted defines
135135
/// operator delete.
136+
/// - Always return vector types directly from member functions on x86 and
137+
/// x86_64 on Windows, which is not compatible with the MSVC ABI.
136138
ABI_VER_MAJOR(21)
137139

138140
/// Conform to the underlying platform's C and C++ ABIs as closely as we can.

clang/lib/CodeGen/MicrosoftCXXABI.cpp

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,18 +1177,32 @@ static bool isTrivialForMSVC(const CXXRecordDecl *RD, QualType Ty,
11771177
}
11781178

11791179
bool MicrosoftCXXABI::classifyReturnType(CGFunctionInfo &FI) const {
1180-
const CXXRecordDecl *RD = FI.getReturnType()->getAsCXXRecordDecl();
1181-
if (!RD)
1182-
return false;
1183-
1184-
bool isTrivialForABI = RD->canPassInRegisters() &&
1185-
isTrivialForMSVC(RD, FI.getReturnType(), CGM);
1186-
1187-
// MSVC always returns structs indirectly from C++ instance methods.
1188-
bool isIndirectReturn = !isTrivialForABI || FI.isInstanceMethod();
1180+
QualType RetTy = FI.getReturnType();
1181+
bool isIndirectReturn = false;
1182+
1183+
if (const CXXRecordDecl *RD = RetTy->getAsCXXRecordDecl()) {
1184+
bool isTrivialForABI =
1185+
RD->canPassInRegisters() && isTrivialForMSVC(RD, RetTy, CGM);
1186+
1187+
// MSVC always returns structs indirectly from C++ instance methods.
1188+
isIndirectReturn = !isTrivialForABI || FI.isInstanceMethod();
1189+
} else if (isa<VectorType>(RetTy) &&
1190+
getContext().getLangOpts().getClangABICompat() >
1191+
LangOptions::ClangABI::Ver21) {
1192+
// On x86, MSVC usually returns vector types indirectly from C++ instance
1193+
// methods. (Clang <= 21.0 always returned vector types directly.)
1194+
if (CGM.getTarget().getTriple().isX86() && FI.isInstanceMethod()) {
1195+
// However, MSVC returns vector types > 64 bits directly from vectorcall
1196+
// instance methods.
1197+
if (FI.getCallingConvention() == llvm::CallingConv::X86_VectorCall)
1198+
isIndirectReturn = getContext().getTypeSize(RetTy) == 64;
1199+
else
1200+
isIndirectReturn = true;
1201+
}
1202+
}
11891203

11901204
if (isIndirectReturn) {
1191-
CharUnits Align = CGM.getContext().getTypeAlignInChars(FI.getReturnType());
1205+
CharUnits Align = CGM.getContext().getTypeAlignInChars(RetTy);
11921206
FI.getReturnInfo() = ABIArgInfo::getIndirect(
11931207
Align, /*AddrSpace=*/CGM.getDataLayout().getAllocaAddrSpace(),
11941208
/*ByVal=*/false);
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// RUN: %clang_cc1 -ffreestanding -emit-llvm %s -o - -triple=i686-pc-windows-msvc \
2+
// RUN: | FileCheck --check-prefixes=CHECK,X86 %s
3+
// RUN: %clang_cc1 -ffreestanding -emit-llvm %s -o - -triple=x86_64-pc-windows-msvc \
4+
// RUN: | FileCheck --check-prefixes=CHECK,X86 %s
5+
// RUN: %clang_cc1 -ffreestanding -emit-llvm %s -o - -triple=aarch64-pc-windows-msvc \
6+
// RUN: | FileCheck --check-prefixes=CHECK,AARCH64 %s
7+
// RUN: %clang_cc1 -ffreestanding -emit-llvm %s -o - -triple=i686-pc-windows-msvc \
8+
// RUN: -fclang-abi-compat=21 | FileCheck --check-prefixes=CHECK,CLANG21 %s
9+
// RUN: %clang_cc1 -ffreestanding -emit-llvm %s -o - -triple=x86_64-pc-windows-msvc \
10+
// RUN: -fclang-abi-compat=21 | FileCheck --check-prefixes=CHECK,CLANG21 %s
11+
// RUN: %clang_cc1 -ffreestanding -emit-llvm %s -o - -triple=aarch64-pc-windows-msvc \
12+
// RUN: -fclang-abi-compat=21 | FileCheck --check-prefixes=CHECK,CLANG21 %s
13+
14+
// To match the MSVC ABI, vector types are usually returned directly, but on x86
15+
// and x86-64 they must be returned indirectly from member functions (unless
16+
// they use the vectorcall calling convention and the vector type is > 64 bits).
17+
18+
#if defined(__i386__) || defined(__x86_64__)
19+
#include <xmmintrin.h>
20+
21+
#define VECTOR64_TYPE __m64
22+
#define VECTOR128_TYPE __m128
23+
24+
#define VECTORCALL __vectorcall
25+
#endif
26+
27+
#ifdef __aarch64__
28+
#include <arm_neon.h>
29+
30+
// These were chosen such that they lower to the same types that the x86 vector
31+
// types lower to (e.g. int64x1_t and __m64 both lower to <1 x i64>).
32+
#define VECTOR64_TYPE int64x1_t
33+
#define VECTOR128_TYPE float32x4_t
34+
35+
#define VECTORCALL
36+
#endif
37+
38+
struct Foo {
39+
VECTOR64_TYPE method_ret_vec64();
40+
VECTOR128_TYPE method_ret_vec128();
41+
42+
VECTOR64_TYPE VECTORCALL vc_method_ret_vec64();
43+
VECTOR128_TYPE VECTORCALL vc_method_ret_vec128();
44+
};
45+
46+
VECTOR64_TYPE Foo::method_ret_vec64() {
47+
return VECTOR64_TYPE{};
48+
// X86: store <1 x i64>
49+
// X86: ret void
50+
// AARCH64: ret <1 x i64>
51+
// CLANG21: ret <1 x i64>
52+
}
53+
54+
VECTOR128_TYPE Foo::method_ret_vec128() {
55+
return VECTOR128_TYPE{};
56+
// X86: store <4 x float>
57+
// X86: ret void
58+
// AARCH64: ret <4 x float>
59+
// CLANG21: ret <4 x float>
60+
}
61+
62+
VECTOR64_TYPE VECTORCALL Foo::vc_method_ret_vec64() {
63+
return VECTOR64_TYPE{};
64+
// X86: store <1 x i64>
65+
// X86: ret void
66+
// AARCH64: ret <1 x i64>
67+
// CLANG21: ret <1 x i64>
68+
}
69+
70+
VECTOR128_TYPE VECTORCALL Foo::vc_method_ret_vec128() {
71+
return VECTOR128_TYPE{};
72+
// CHECK: ret <4 x float>
73+
}
74+
75+
VECTOR64_TYPE func_ret_vec64() {
76+
return VECTOR64_TYPE{};
77+
// CHECK: ret <1 x i64>
78+
}
79+
80+
VECTOR128_TYPE func_ret_vec128() {
81+
return VECTOR128_TYPE{};
82+
// CHECK: ret <4 x float>
83+
}
84+
85+
VECTOR64_TYPE VECTORCALL vc_func_ret_vec64() {
86+
return VECTOR64_TYPE{};
87+
// CHECK: ret <1 x i64>
88+
}
89+
90+
VECTOR128_TYPE VECTORCALL vc_func_ret_vec128() {
91+
return VECTOR128_TYPE{};
92+
// CHECK: ret <4 x float>
93+
}

0 commit comments

Comments
 (0)