Skip to content

Commit e683539

Browse files
committed
fix: C++ empty record with align break aarch64 AAPCS64
1 parent 4f80c06 commit e683539

File tree

7 files changed

+38
-12
lines changed

7 files changed

+38
-12
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ C++ Specific Potentially Breaking Changes
110110
ABI Changes in This Version
111111
---------------------------
112112

113+
- Let C++ empty record fall through to the standard argument-handling path instead of
114+
always pass a single ``i8`` according to aarch64 AAPCS rules.
115+
113116
AST Dumping Potentially Breaking Changes
114117
----------------------------------------
115118
- How nested name specifiers are dumped and printed changes, keeping track of clang AST changes.

clang/lib/CodeGen/Targets/AArch64.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,12 @@ ABIArgInfo AArch64ABIInfo::classifyArgumentType(QualType Ty, bool IsVariadicFn,
422422
}
423423

424424
// Empty records:
425+
// AAPCS64 does not say that empty records are ignored as arguments,
426+
// but other compilers do so in certain situations, and we copy that behavior.
427+
// Those situations are in fact language-mode-specific, which seems really
428+
// unfortunate, but it's something we just have to accept. If this doesn't
429+
// apply, just fall through to the standard argument-handling path.
430+
// Darwin overrides the psABI here to ignore all empty records in all modes.
425431
uint64_t Size = getContext().getTypeSize(Ty);
426432
bool IsEmpty = isEmptyRecord(getContext(), Ty, true);
427433
if (!Ty->isSVESizelessBuiltinType() && (IsEmpty || Size == 0)) {
@@ -434,9 +440,6 @@ ABIArgInfo AArch64ABIInfo::classifyArgumentType(QualType Ty, bool IsVariadicFn,
434440
// behaviour here.
435441
if (Size == 0)
436442
return ABIArgInfo::getIgnore();
437-
438-
// Otherwise, they are passed as if they have a size of 1 byte.
439-
return ABIArgInfo::getDirect(llvm::Type::getInt8Ty(getVMContext()));
440443
}
441444

442445
// Homogeneous Floating-point Aggregates (HFAs) need to be expanded.

clang/test/CodeGen/AArch64/args.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,29 @@ struct Empty {};
1717

1818
// DARWIN: define{{.*}} i32 @empty_arg(i32 noundef %a)
1919
// C: define{{.*}} i32 @empty_arg(i32 noundef %a)
20-
// CXX: define{{.*}} i32 @empty_arg(i8 %e.coerce, i32 noundef %a)
20+
// CXX: define{{.*}} i32 @empty_arg(i64 %e.coerce, i32 noundef %a)
2121
EXTERNC int empty_arg(struct Empty e, int a) {
2222
return a;
2323
}
2424

25+
// CXX: define{{.*}} i32 @empty_align8_arg(i64 %a.coerce, i32 noundef %b)
26+
struct EmptyAlign8 { int __attribute__((aligned(8))) : 0; };
27+
EXTERNC int empty_align8_arg(struct EmptyAlign8 a, int b) {
28+
return b;
29+
}
30+
31+
// CXX: define{{.*}} i32 @empty_align16_arg(i128 %a.coerce, i32 noundef %b)
32+
struct EmptyAlign16 { long long int __attribute__((aligned(16))) : 0; };
33+
EXTERNC int empty_align16_arg(struct EmptyAlign16 a, int b) {
34+
return b;
35+
}
36+
37+
// CXX: define{{.*}} i32 @empty_align32_arg(ptr dead_on_return noundef %a, i32 noundef %b)
38+
struct EmptyAlign32 { long long int __attribute__((aligned(32))) : 0; };
39+
EXTERNC int empty_align32_arg(struct EmptyAlign32 a, int b) {
40+
return b;
41+
}
42+
2543
// DARWIN: define{{.*}} void @empty_ret()
2644
// C: define{{.*}} void @empty_ret()
2745
// CXX: define{{.*}} void @empty_ret()

clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -575,19 +575,21 @@ void TSpp_align16(SSpp_align16 s) { *s.a.x = 1; }
575575
struct Sempty {
576576
};
577577
// CHECK-A64-LABEL: define dso_local void @_Z6Tempty6Sempty(
578-
// CHECK-A64-SAME: i8 [[S_COERCE:%.*]]) #[[ATTR0]] {
578+
// CHECK-A64-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0]] {
579579
// CHECK-A64-NEXT: [[ENTRY:.*:]]
580580
// CHECK-A64-NEXT: [[S:%.*]] = alloca [[STRUCT_SEMPTY:%.*]], align 1
581581
// CHECK-A64-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SEMPTY]], ptr [[S]], i32 0, i32 0
582-
// CHECK-A64-NEXT: store i8 [[S_COERCE]], ptr [[COERCE_DIVE]], align 1
582+
// CHECK-A64-NEXT: [[COERCE_VAL_II:%.*]] = trunc i64 [[S_COERCE]] to i8
583+
// CHECK-A64-NEXT: store i8 [[COERCE_VAL_II]], ptr [[COERCE_DIVE]], align 1
583584
// CHECK-A64-NEXT: ret void
584585
//
585586
// CHECK-A64_32-LABEL: define void @_Z6Tempty6Sempty(
586-
// CHECK-A64_32-SAME: i8 [[S_COERCE:%.*]]) #[[ATTR0]] {
587+
// CHECK-A64_32-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0]] {
587588
// CHECK-A64_32-NEXT: [[ENTRY:.*:]]
588589
// CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[STRUCT_SEMPTY:%.*]], align 1
589590
// CHECK-A64_32-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SEMPTY]], ptr [[S]], i32 0, i32 0
590-
// CHECK-A64_32-NEXT: store i8 [[S_COERCE]], ptr [[COERCE_DIVE]], align 1
591+
// CHECK-A64_32-NEXT: [[COERCE_VAL_II:%.*]] = trunc i64 [[S_COERCE]] to i8
592+
// CHECK-A64_32-NEXT: store i8 [[COERCE_VAL_II]], ptr [[COERCE_DIVE]], align 1
591593
// CHECK-A64_32-NEXT: ret void
592594
//
593595
void Tempty(Sempty s) { }

clang/test/CodeGen/arm64-microsoft-arguments.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ S4 f4() {
5757

5858
// Pass and return from instance method called from instance method.
5959
// CHECK: define {{.*}} void @{{.*}}bar@Q1{{.*}}(ptr {{[^,]*}} %this, ptr dead_on_unwind inreg noalias writable sret(%class.P1) align 1 %agg.result)
60-
// CHECK: call void {{.*}}foo@P1{{.*}}(ptr noundef{{[^,]*}} %ref.tmp, ptr dead_on_unwind inreg writable sret(%class.P1) align 1 %agg.result, i8 %0)
60+
// CHECK: call void {{.*}}foo@P1{{.*}}(ptr noundef{{[^,]*}} %ref.tmp, ptr dead_on_unwind inreg writable sret(%class.P1) align 1 %agg.result, i64 %coerce.val.ii)
6161

6262
class P1 {
6363
public:
@@ -76,7 +76,7 @@ P1 Q1::bar() {
7676

7777
// Pass and return from instance method called from free function.
7878
// CHECK: define {{.*}} void {{.*}}bar{{.*}}()
79-
// CHECK: call void {{.*}}foo@P2{{.*}}(ptr noundef{{[^,]*}} %ref.tmp, ptr dead_on_unwind inreg writable sret(%class.P2) align 1 %retval, i8 %0)
79+
// CHECK: call void {{.*}}foo@P2{{.*}}(ptr noundef{{[^,]*}} %ref.tmp, ptr dead_on_unwind inreg writable sret(%class.P2) align 1 %retval, i64 %coerce.val.ii)
8080
class P2 {
8181
public:
8282
P2 foo(P2 x);
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// RUN: %clang_cc1 -triple arm64-none-linux -emit-llvm -w -o - %s | FileCheck -check-prefix=PCS %s
22

3-
// PCS: define{{.*}} void @{{.*}}(i8 %a
3+
// PCS: define{{.*}} void @{{.*}}(i64 %a.coerce)
44
struct s0 {};
55
void f0(s0 a) {}

clang/test/CodeGenCXX/arm64-darwinpcs.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ void test_extensions(bool a, char b, short c) {}
77

88
struct Empty {};
99
void test_empty(Empty e) {}
10-
// CHECK: define{{.*}} void @_Z10test_empty5Empty(i8
10+
// CHECK: define{{.*}} void @_Z10test_empty5Empty(i64 %e.coerce)
1111
// CHECK-DARWIN: define{{.*}} void @_Z10test_empty5Empty()
1212

1313
struct HFA {

0 commit comments

Comments
 (0)