Skip to content

Commit 2c049c1

Browse files
committed
[clang] Return larger CXX records in memory
We incorrectly return CXX records in AVX registers when they should be returned in memory. This is violation of x86-64 psABI. Detailed discussion is here: https://groups.google.com/g/x86-64-abi/c/BjOOyihHuqg/m/KurXdUcWAgAJ
1 parent d444558 commit 2c049c1

File tree

4 files changed

+47
-0
lines changed

4 files changed

+47
-0
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ C++ Specific Potentially Breaking Changes
4343
ABI Changes in This Version
4444
---------------------------
4545

46+
- Return larger CXX records in memory instead of using AVX registers. Code compiled with older clang will be incompatible with newer version of the clang unless -fclang-abi-compat=20 is provided. (#GH120670)
47+
4648
AST Dumping Potentially Breaking Changes
4749
----------------------------------------
4850

clang/include/clang/Basic/LangOptions.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,11 @@ class LangOptionsBase {
248248
/// function templates.
249249
Ver19,
250250

251+
/// Attempt to be ABI-compatible with code generated by Clang 20.0.x.
252+
/// This causes clang to:
253+
/// - Incorrectly return C++ records in AVX registers on x86_64.
254+
Ver20,
255+
251256
/// Conform to the underlying platform's C and C++ ABIs as closely
252257
/// as we can.
253258
Latest

clang/lib/CodeGen/Targets/X86.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1334,6 +1334,16 @@ class X86_64ABIInfo : public ABIInfo {
13341334
return T.isOSLinux() || T.isOSNetBSD();
13351335
}
13361336

1337+
bool returnCXXRecordGreaterThan128InMem() const {
1338+
// Clang <= 20.0 did not do this.
1339+
if (getContext().getLangOpts().getClangABICompat() <=
1340+
LangOptions::ClangABI::Ver20)
1341+
return false;
1342+
1343+
const llvm::Triple &T = getTarget().getTriple();
1344+
return T.isOSLinux() || T.isOSNetBSD();
1345+
}
1346+
13371347
X86AVXABILevel AVXLevel;
13381348
// Some ABIs (e.g. X32 ABI and Native Client OS) use 32 bit pointers on
13391349
// 64-bit hardware.
@@ -2067,6 +2077,13 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, Class &Lo,
20672077
classify(I.getType(), Offset, FieldLo, FieldHi, isNamedArg);
20682078
Lo = merge(Lo, FieldLo);
20692079
Hi = merge(Hi, FieldHi);
2080+
if (returnCXXRecordGreaterThan128InMem() &&
2081+
(Size > 128 && (Size != getContext().getTypeSize(I.getType()) ||
2082+
Size > getNativeVectorSizeForAVXABI(AVXLevel)))) {
2083+
// The only case a 256(or 512)-bit wide vector could be used to return
2084+
// is when CXX record contains a single 256(or 512)-bit element.
2085+
Lo = Memory;
2086+
}
20702087
if (Lo == Memory || Hi == Memory) {
20712088
postMerge(Size, Lo, Hi);
20722089
return;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: %clang %s -S --target=x86_64-unknown-linux-gnu -emit-llvm -O2 -march=x86-64-v3 -o - | FileCheck %s
2+
3+
using UInt64x2 = unsigned long long __attribute__((__vector_size__(16), may_alias));
4+
5+
template<int id>
6+
struct XMM1 {
7+
UInt64x2 x;
8+
};
9+
10+
struct XMM2 : XMM1<0>, XMM1<1> {
11+
};
12+
13+
// CHECK: define{{.*}} @_Z3foov({{.*}} [[ARG:%.*]]){{.*}}
14+
// CHECK-NEXT: entry:
15+
// CHECK-NEXT: store {{.*}}, ptr [[ARG]]{{.*}}
16+
// CHECK-NEXT: [[TMP1:%.*]] = getelementptr {{.*}}, ptr [[ARG]]{{.*}}
17+
// CHECK-NEXT: store {{.*}}, ptr [[TMP1]]{{.*}}
18+
XMM2 foo() {
19+
XMM2 result;
20+
((XMM1<0>*)&result)->x = UInt64x2{1, 2};
21+
((XMM1<1>*)&result)->x = UInt64x2{3, 4};
22+
return result;
23+
}

0 commit comments

Comments
 (0)