diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 99aa545831240..220cb4bac0ed4 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -110,6 +110,9 @@ C++ Specific Potentially Breaking Changes ABI Changes in This Version --------------------------- +- Let C++ empty record fall through to the standard argument-handling path instead of + always pass a single ``i8`` according to aarch64 AAPCS rules. + AST Dumping Potentially Breaking Changes ---------------------------------------- - How nested name specifiers are dumped and printed changes, keeping track of clang AST changes. diff --git a/clang/lib/CodeGen/Targets/AArch64.cpp b/clang/lib/CodeGen/Targets/AArch64.cpp index d7deece232a9f..9adace69c06c6 100644 --- a/clang/lib/CodeGen/Targets/AArch64.cpp +++ b/clang/lib/CodeGen/Targets/AArch64.cpp @@ -422,6 +422,12 @@ ABIArgInfo AArch64ABIInfo::classifyArgumentType(QualType Ty, bool IsVariadicFn, } // Empty records: + // AAPCS64 does not say that empty records are ignored as arguments, + // but other compilers do so in certain situations, and we copy that behavior. + // Those situations are in fact language-mode-specific, which seems really + // unfortunate, but it's something we just have to accept. If this doesn't + // apply, just fall through to the standard argument-handling path. + // Darwin overrides the psABI here to ignore all empty records in all modes. uint64_t Size = getContext().getTypeSize(Ty); bool IsEmpty = isEmptyRecord(getContext(), Ty, true); if (!Ty->isSVESizelessBuiltinType() && (IsEmpty || Size == 0)) { @@ -434,9 +440,6 @@ ABIArgInfo AArch64ABIInfo::classifyArgumentType(QualType Ty, bool IsVariadicFn, // behaviour here. if (Size == 0) return ABIArgInfo::getIgnore(); - - // Otherwise, they are passed as if they have a size of 1 byte. - return ABIArgInfo::getDirect(llvm::Type::getInt8Ty(getVMContext())); } // Homogeneous Floating-point Aggregates (HFAs) need to be expanded. diff --git a/clang/test/CodeGen/AArch64/args.cpp b/clang/test/CodeGen/AArch64/args.cpp index 3cb62d3119ecf..c284316a5e1b4 100644 --- a/clang/test/CodeGen/AArch64/args.cpp +++ b/clang/test/CodeGen/AArch64/args.cpp @@ -17,11 +17,29 @@ struct Empty {}; // DARWIN: define{{.*}} i32 @empty_arg(i32 noundef %a) // C: define{{.*}} i32 @empty_arg(i32 noundef %a) -// CXX: define{{.*}} i32 @empty_arg(i8 %e.coerce, i32 noundef %a) +// CXX: define{{.*}} i32 @empty_arg(i64 %e.coerce, i32 noundef %a) EXTERNC int empty_arg(struct Empty e, int a) { return a; } +// CXX: define{{.*}} i32 @empty_align8_arg(i64 %a.coerce, i32 noundef %b) +struct EmptyAlign8 { int __attribute__((aligned(8))) : 0; }; +EXTERNC int empty_align8_arg(struct EmptyAlign8 a, int b) { + return b; +} + +// CXX: define{{.*}} i32 @empty_align16_arg(i128 %a.coerce, i32 noundef %b) +struct EmptyAlign16 { long long int __attribute__((aligned(16))) : 0; }; +EXTERNC int empty_align16_arg(struct EmptyAlign16 a, int b) { + return b; +} + +// CXX: define{{.*}} i32 @empty_align32_arg(ptr dead_on_return noundef %a, i32 noundef %b) +struct EmptyAlign32 { long long int __attribute__((aligned(32))) : 0; }; +EXTERNC int empty_align32_arg(struct EmptyAlign32 a, int b) { + return b; +} + // DARWIN: define{{.*}} void @empty_ret() // C: define{{.*}} void @empty_ret() // CXX: define{{.*}} void @empty_ret() diff --git a/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp b/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp index f0c9ef28201a5..97fdd0ce56c66 100644 --- a/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp +++ b/clang/test/CodeGen/AArch64/struct-coerce-using-ptr.cpp @@ -575,19 +575,21 @@ void TSpp_align16(SSpp_align16 s) { *s.a.x = 1; } struct Sempty { }; // CHECK-A64-LABEL: define dso_local void @_Z6Tempty6Sempty( -// CHECK-A64-SAME: i8 [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0]] { // CHECK-A64-NEXT: [[ENTRY:.*:]] // CHECK-A64-NEXT: [[S:%.*]] = alloca [[STRUCT_SEMPTY:%.*]], align 1 // CHECK-A64-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SEMPTY]], ptr [[S]], i32 0, i32 0 -// CHECK-A64-NEXT: store i8 [[S_COERCE]], ptr [[COERCE_DIVE]], align 1 +// CHECK-A64-NEXT: [[COERCE_VAL_II:%.*]] = trunc i64 [[S_COERCE]] to i8 +// CHECK-A64-NEXT: store i8 [[COERCE_VAL_II]], ptr [[COERCE_DIVE]], align 1 // CHECK-A64-NEXT: ret void // // CHECK-A64_32-LABEL: define void @_Z6Tempty6Sempty( -// CHECK-A64_32-SAME: i8 [[S_COERCE:%.*]]) #[[ATTR0]] { +// CHECK-A64_32-SAME: i64 [[S_COERCE:%.*]]) #[[ATTR0]] { // CHECK-A64_32-NEXT: [[ENTRY:.*:]] // CHECK-A64_32-NEXT: [[S:%.*]] = alloca [[STRUCT_SEMPTY:%.*]], align 1 // CHECK-A64_32-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_SEMPTY]], ptr [[S]], i32 0, i32 0 -// CHECK-A64_32-NEXT: store i8 [[S_COERCE]], ptr [[COERCE_DIVE]], align 1 +// CHECK-A64_32-NEXT: [[COERCE_VAL_II:%.*]] = trunc i64 [[S_COERCE]] to i8 +// CHECK-A64_32-NEXT: store i8 [[COERCE_VAL_II]], ptr [[COERCE_DIVE]], align 1 // CHECK-A64_32-NEXT: ret void // void Tempty(Sempty s) { } diff --git a/clang/test/CodeGen/arm64-microsoft-arguments.cpp b/clang/test/CodeGen/arm64-microsoft-arguments.cpp index a0a81be54325f..f7eb0cc765354 100644 --- a/clang/test/CodeGen/arm64-microsoft-arguments.cpp +++ b/clang/test/CodeGen/arm64-microsoft-arguments.cpp @@ -57,7 +57,7 @@ S4 f4() { // Pass and return from instance method called from instance method. // CHECK: define {{.*}} void @{{.*}}bar@Q1{{.*}}(ptr {{[^,]*}} %this, ptr dead_on_unwind inreg noalias writable sret(%class.P1) align 1 %agg.result) -// CHECK: call void {{.*}}foo@P1{{.*}}(ptr noundef{{[^,]*}} %ref.tmp, ptr dead_on_unwind inreg writable sret(%class.P1) align 1 %agg.result, i8 %0) +// 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) class P1 { public: @@ -76,7 +76,7 @@ P1 Q1::bar() { // Pass and return from instance method called from free function. // CHECK: define {{.*}} void {{.*}}bar{{.*}}() -// CHECK: call void {{.*}}foo@P2{{.*}}(ptr noundef{{[^,]*}} %ref.tmp, ptr dead_on_unwind inreg writable sret(%class.P2) align 1 %retval, i8 %0) +// 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) class P2 { public: P2 foo(P2 x); diff --git a/clang/test/CodeGenCXX/aarch64-arguments.cpp b/clang/test/CodeGenCXX/aarch64-arguments.cpp index ffb0cafa8882d..3206e38ad0090 100644 --- a/clang/test/CodeGenCXX/aarch64-arguments.cpp +++ b/clang/test/CodeGenCXX/aarch64-arguments.cpp @@ -1,5 +1,5 @@ // RUN: %clang_cc1 -triple arm64-none-linux -emit-llvm -w -o - %s | FileCheck -check-prefix=PCS %s -// PCS: define{{.*}} void @{{.*}}(i8 %a +// PCS: define{{.*}} void @{{.*}}(i64 %a.coerce) struct s0 {}; void f0(s0 a) {} diff --git a/clang/test/CodeGenCXX/arm64-darwinpcs.cpp b/clang/test/CodeGenCXX/arm64-darwinpcs.cpp index a0b0d9efdd4c4..ef0e2da3effac 100644 --- a/clang/test/CodeGenCXX/arm64-darwinpcs.cpp +++ b/clang/test/CodeGenCXX/arm64-darwinpcs.cpp @@ -7,7 +7,7 @@ void test_extensions(bool a, char b, short c) {} struct Empty {}; void test_empty(Empty e) {} -// CHECK: define{{.*}} void @_Z10test_empty5Empty(i8 +// CHECK: define{{.*}} void @_Z10test_empty5Empty(i64 %e.coerce) // CHECK-DARWIN: define{{.*}} void @_Z10test_empty5Empty() struct HFA {