Skip to content

Commit cafb799

Browse files
committed
[clang][CodeGen] Fix MS ABI for classes with non static data members of reference type
1 parent 8c9f45e commit cafb799

File tree

3 files changed

+98
-6
lines changed

3 files changed

+98
-6
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ ABI Changes in This Version
6363
MSVC uses a different mangling for these objects, compatibility is not affected.
6464
(#GH85423).
6565

66+
- Fixed Microsoft calling convention when returning classes that have a reference type
67+
as a field. Such a class should be returned indirectly.
68+
6669
AST Dumping Potentially Breaking Changes
6770
----------------------------------------
6871

clang/lib/CodeGen/MicrosoftCXXABI.cpp

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,8 +1103,27 @@ bool MicrosoftCXXABI::hasMostDerivedReturn(GlobalDecl GD) const {
11031103
return isDeletingDtor(GD);
11041104
}
11051105

1106+
static bool fieldIsTrivialForMSVC(const FieldDecl *Field,
1107+
const ASTContext &Context) {
1108+
if (Field->getType()->isReferenceType())
1109+
return false;
1110+
1111+
const RecordType *RT =
1112+
Context.getBaseElementType(Field->getType())->getAs<RecordType>();
1113+
if (!RT)
1114+
return true;
1115+
1116+
CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl());
1117+
1118+
for (const FieldDecl *RDField : RD->fields())
1119+
if (!fieldIsTrivialForMSVC(RDField, Context))
1120+
return false;
1121+
1122+
return true;
1123+
}
1124+
11061125
static bool isTrivialForMSVC(const CXXRecordDecl *RD, QualType Ty,
1107-
CodeGenModule &CGM) {
1126+
CodeGenModule &CGM, const ASTContext &Context) {
11081127
// On AArch64, HVAs that can be passed in registers can also be returned
11091128
// in registers. (Note this is using the MSVC definition of an HVA; see
11101129
// isPermittedToBeHomogeneousAggregate().)
@@ -1122,7 +1141,8 @@ static bool isTrivialForMSVC(const CXXRecordDecl *RD, QualType Ty,
11221141
// No base classes
11231142
// No virtual functions
11241143
// Additionally, we need to ensure that there is a trivial copy assignment
1125-
// operator, a trivial destructor and no user-provided constructors.
1144+
// operator, a trivial destructor, no user-provided constructors and no
1145+
// non static data members of reference type.
11261146
if (RD->hasProtectedFields() || RD->hasPrivateFields())
11271147
return false;
11281148
if (RD->getNumBases() > 0)
@@ -1136,6 +1156,9 @@ static bool isTrivialForMSVC(const CXXRecordDecl *RD, QualType Ty,
11361156
return false;
11371157
if (RD->hasNonTrivialDestructor())
11381158
return false;
1159+
for (const FieldDecl *Field : RD->fields())
1160+
if (!fieldIsTrivialForMSVC(Field, Context))
1161+
return false;
11391162
return true;
11401163
}
11411164

@@ -1144,11 +1167,13 @@ bool MicrosoftCXXABI::classifyReturnType(CGFunctionInfo &FI) const {
11441167
if (!RD)
11451168
return false;
11461169

1147-
bool isTrivialForABI = RD->canPassInRegisters() &&
1148-
isTrivialForMSVC(RD, FI.getReturnType(), CGM);
1149-
11501170
// MSVC always returns structs indirectly from C++ instance methods.
1151-
bool isIndirectReturn = !isTrivialForABI || FI.isInstanceMethod();
1171+
bool isIndirectReturn = FI.isInstanceMethod();
1172+
if (!isIndirectReturn) {
1173+
isIndirectReturn =
1174+
!(RD->canPassInRegisters() &&
1175+
isTrivialForMSVC(RD, FI.getReturnType(), CGM, getContext()));
1176+
}
11521177

11531178
if (isIndirectReturn) {
11541179
CharUnits Align = CGM.getContext().getTypeAlignInChars(FI.getReturnType());
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// RUN: %clang_cc1 -triple x86_64-windows-msvc -ffreestanding -emit-llvm -O0 \
2+
// RUN: -x c++ -o - %s | FileCheck %s
3+
4+
int global_i = 0;
5+
6+
// Pass and return object with a reference type (pass directly, return indirectly).
7+
// CHECK: define dso_local void @"?f1@@YA?AUS1@@XZ"(ptr dead_on_unwind noalias writable sret(%struct.S1) align 8 {{.*}})
8+
// CHECK: call void @"?func1@@YA?AUS1@@U1@@Z"(ptr dead_on_unwind writable sret(%struct.S1) align 8 {{.*}}, i64 {{.*}})
9+
struct S1 {
10+
int& r;
11+
};
12+
13+
S1 func1(S1 x);
14+
S1 f1() {
15+
S1 x{ global_i };
16+
return func1(x);
17+
}
18+
19+
// Pass and return object with a reference type within an inner struct (pass directly, return indirectly).
20+
// CHECK: define dso_local void @"?f2@@YA?AUS2@@XZ"(ptr dead_on_unwind noalias writable sret(%struct.S2) align 8 {{.*}})
21+
// CHECK: call void @"?func2@@YA?AUS2@@U1@@Z"(ptr dead_on_unwind writable sret(%struct.S2) align 8 {{.*}}, i64 {{.*}})
22+
struct Inner {
23+
int& r;
24+
};
25+
26+
struct S2 {
27+
Inner i;
28+
};
29+
30+
S2 func2(S2 x);
31+
S2 f2() {
32+
S2 x{ { global_i } };
33+
return func2(x);
34+
}
35+
36+
// Pass and return object with a reference type (pass directly, return indirectly).
37+
// CHECK: define dso_local void @"?f3@@YA?AUS3@@XZ"(ptr dead_on_unwind noalias writable sret(%struct.S3) align 8 {{.*}})
38+
// CHECK: call void @"?func3@@YA?AUS3@@U1@@Z"(ptr dead_on_unwind writable sret(%struct.S3) align 8 {{.*}}, i64 {{.*}})
39+
struct S3 {
40+
const int& r;
41+
};
42+
43+
S3 func3(S3 x);
44+
S3 f3() {
45+
S3 x{ global_i };
46+
return func3(x);
47+
}
48+
49+
// Pass and return object with a reference type within an inner struct (pass directly, return indirectly).
50+
// CHECK: define dso_local void @"?f4@@YA?AUS4@@XZ"(ptr dead_on_unwind noalias writable sret(%struct.S4) align 8 {{.*}})
51+
// CHECK: call void @"?func4@@YA?AUS4@@U1@@Z"(ptr dead_on_unwind writable sret(%struct.S4) align 8 {{.*}}, i64 {{.*}})
52+
struct InnerConst {
53+
const int& r;
54+
};
55+
56+
struct S4 {
57+
InnerConst i;
58+
};
59+
60+
S4 func4(S4 x);
61+
S4 f4() {
62+
S4 x{ { global_i } };
63+
return func4(x);
64+
}

0 commit comments

Comments
 (0)