https://github.com/ostannard updated https://github.com/llvm/llvm-project/pull/124762
>From 4d883f068c5061e60e90e51d149a361920b431d2 Mon Sep 17 00:00:00 2001 From: Oliver Stannard <oliver.stann...@arm.com> Date: Thu, 12 Dec 2024 15:29:31 +0000 Subject: [PATCH 1/7] [ARM] Empty structs are 1-byte for C++ ABI For C++ (but not C), empty structs should be passed to functions as if they are a 1 byte object with 1 byte alignment. This is defined in Arm's CPPABI32: https://github.com/ARM-software/abi-aa/blob/main/cppabi32/cppabi32.rst For the purposes of parameter passing in AAPCS32, a parameter whose type is an empty class shall be treated as if its type were an aggregate with a single member of type unsigned byte. The AArch64 equivalent of this has an exception for structs containing an array of size zero, I've kept that logic for ARM. I've not found a reason for this exception, but I've checked that GCC does have the same behaviour for ARM as it does for AArch64. The AArch64 version has an Apple ABI with different rules, which ignores empty structs in both C and C++. This is documented at https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms. The ARM equivalent of that appears to be AAPCS16_VFP, used for WatchOS, but I can't find any documentation for that ABI, so I'm not sure what rules it should follow. For now I've left it following the AArch64 Apple rules. --- clang/lib/CodeGen/Targets/ARM.cpp | 43 ++++++++-- clang/test/CodeGen/arm-empty-args.cpp | 119 ++++++++++++++++++++++++++ 2 files changed, 156 insertions(+), 6 deletions(-) create mode 100644 clang/test/CodeGen/arm-empty-args.cpp diff --git a/clang/lib/CodeGen/Targets/ARM.cpp b/clang/lib/CodeGen/Targets/ARM.cpp index 2d858fa2f3c3a3..b243ccacc2155d 100644 --- a/clang/lib/CodeGen/Targets/ARM.cpp +++ b/clang/lib/CodeGen/Targets/ARM.cpp @@ -71,6 +71,7 @@ class ARMABIInfo : public ABIInfo { unsigned functionCallConv) const; ABIArgInfo classifyHomogeneousAggregate(QualType Ty, const Type *Base, uint64_t Members) const; + bool shouldIgnoreEmptyArg(QualType Ty) const; ABIArgInfo coerceIllegalVector(QualType Ty) const; bool isIllegalVectorType(QualType Ty) const; bool containsAnyFP16Vectors(QualType Ty) const; @@ -328,6 +329,26 @@ ABIArgInfo ARMABIInfo::classifyHomogeneousAggregate(QualType Ty, return ABIArgInfo::getDirect(nullptr, 0, nullptr, false, Align); } +bool ARMABIInfo::shouldIgnoreEmptyArg(QualType Ty) const { + uint64_t Size = getContext().getTypeSize(Ty); + assert((isEmptyRecord(getContext(), Ty, true) || Size == 0) && + "Arg is not empty"); + + // Empty records are ignored in C mode, and in C++ on WatchOS. + if (!getContext().getLangOpts().CPlusPlus || + getABIKind() == ARMABIKind::AAPCS16_VFP) + return true; + + // In C++ mode, arguments which have sizeof() == 0 are ignored. This is not a + // situation which is defined by any C++ standard or ABI, but this matches + // GCC's de facto ABI. + if (Size == 0) + return true; + + // Otherwise, they are passed as if they have a size of 1 byte. + return false; +} + ABIArgInfo ARMABIInfo::classifyArgumentType(QualType Ty, bool isVariadic, unsigned functionCallConv) const { // 6.1.2.1 The following argument types are VFP CPRCs: @@ -366,9 +387,15 @@ ABIArgInfo ARMABIInfo::classifyArgumentType(QualType Ty, bool isVariadic, return getNaturalAlignIndirect(Ty, RAA == CGCXXABI::RAA_DirectInMemory); } - // Ignore empty records. - if (isEmptyRecord(getContext(), Ty, true)) - return ABIArgInfo::getIgnore(); + // Empty records are either ignored completely or passed as if they were a + // 1-byte object, depending on the ABI and language standard. + if (isEmptyRecord(getContext(), Ty, true) || + getContext().getTypeSize(Ty) == 0) { + if (shouldIgnoreEmptyArg(Ty)) + return ABIArgInfo::getIgnore(); + else + return ABIArgInfo::getDirect(llvm::Type::getInt8Ty(getVMContext())); + } if (IsAAPCS_VFP) { // Homogeneous Aggregates need to be expanded when we can fit the aggregate @@ -588,7 +615,8 @@ ABIArgInfo ARMABIInfo::classifyReturnType(QualType RetTy, bool isVariadic, // Otherwise this is an AAPCS variant. - if (isEmptyRecord(getContext(), RetTy, true)) + if (isEmptyRecord(getContext(), RetTy, true) || + getContext().getTypeSize(RetTy) == 0) return ABIArgInfo::getIgnore(); // Check for homogeneous aggregates with AAPCS-VFP. @@ -752,10 +780,13 @@ RValue ARMABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, CharUnits SlotSize = CharUnits::fromQuantity(4); // Empty records are ignored for parameter passing purposes. - if (isEmptyRecord(getContext(), Ty, true)) + uint64_t Size = getContext().getTypeSize(Ty); + bool IsEmpty = isEmptyRecord(getContext(), Ty, true); + if ((IsEmpty || Size == 0) && shouldIgnoreEmptyArg(Ty)) return Slot.asRValue(); - CharUnits TySize = getContext().getTypeSizeInChars(Ty); + CharUnits TySize = + std::max(getContext().getTypeSizeInChars(Ty), CharUnits::fromQuantity(1)); CharUnits TyAlignForABI = getContext().getTypeUnadjustedAlignInChars(Ty); // Use indirect if size of the illegal vector is bigger than 16 bytes. diff --git a/clang/test/CodeGen/arm-empty-args.cpp b/clang/test/CodeGen/arm-empty-args.cpp new file mode 100644 index 00000000000000..5770ce160749f7 --- /dev/null +++ b/clang/test/CodeGen/arm-empty-args.cpp @@ -0,0 +1,119 @@ +// RUN: %clang_cc1 -triple armv7a-linux-gnueabi -emit-llvm -o - -x c %s | FileCheck %s --check-prefixes=CHECK,C +// RUN: %clang_cc1 -triple armv7a-linux-gnueabi -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,CXX +// RUN: %clang_cc1 -triple thumbv7k-apple-watchos2.0 -target-abi aapcs16 -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,WATCHOS + +// Empty structs are ignored for PCS purposes on WatchOS and in C mode +// elsewhere. In C++ mode they consume a register slot though. Functions are +// slightly bigger than minimal to make confirmation against actual GCC +// behaviour easier. + +#if __cplusplus +#define EXTERNC extern "C" +#else +#define EXTERNC +#endif + +struct Empty {}; + +// C: define{{.*}} i32 @empty_arg(i32 noundef %a) +// CXX: define{{.*}} i32 @empty_arg(i8 %e.coerce, i32 noundef %a) +// WATCHOS: define{{.*}} i32 @empty_arg(i32 noundef %a) +EXTERNC int empty_arg(struct Empty e, int a) { + return a; +} + +// C: define{{.*}} void @empty_ret() +// CXX: define{{.*}} void @empty_ret() +// WATCHOS: define{{.*}} void @empty_ret() +EXTERNC struct Empty empty_ret(void) { + struct Empty e; + return e; +} + +// However, what counts as "empty" is a baroque mess. This is super-empty, it's +// ignored even in C++ mode. It also has sizeof == 0, violating C++, but that's +// legacy for you: + +struct SuperEmpty { + int arr[0]; +}; + +// C: define{{.*}} i32 @super_empty_arg(i32 noundef %a) +// CXX: define{{.*}} i32 @super_empty_arg(i32 noundef %a) +// WATCHOS: define{{.*}} i32 @super_empty_arg(i32 noundef %a) +EXTERNC int super_empty_arg(struct SuperEmpty e, int a) { + return a; +} + +struct SortOfEmpty { + struct SuperEmpty e; +}; + +// C: define{{.*}} i32 @sort_of_empty_arg(i32 noundef %a) +// CXX: define{{.*}} i32 @sort_of_empty_arg(i8 %e.coerce, i32 noundef %a) +// WATCHOS: define{{.*}} i32 @sort_of_empty_arg(i32 noundef %a) +EXTERNC int sort_of_empty_arg(struct Empty e, int a) { + return a; +} + +// C: define{{.*}} void @sort_of_empty_ret() +// CXX: define{{.*}} void @sort_of_empty_ret() +// WATCHOS: define{{.*}} void @sort_of_empty_ret() +EXTERNC struct SortOfEmpty sort_of_empty_ret(void) { + struct SortOfEmpty e; + return e; +} + +#include <stdarg.h> + +// va_arg matches the above rules, consuming an incoming argument in cases +// where one would be passed, and not doing so when the argument should be +// ignored. + +EXTERNC int empty_arg_variadic(int a, ...) { +// CHECK-LABEL: @empty_arg_variadic( +// C: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 +// C-NOT: {{ getelementptr }} +// CXX: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 +// CXX: %argp.next2 = getelementptr inbounds i8, ptr %argp.cur1, i32 4 +// WATCHOS: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 +// WATCHOS-NOT: {{ getelementptr }} + va_list vl; + va_start(vl, a); + struct Empty b = va_arg(vl, struct Empty); + int c = va_arg(vl, int); + va_end(vl); + return c; +} + +EXTERNC int super_empty_arg_variadic(int a, ...) { +// CHECK-LABEL: @super_empty_arg_variadic( +// C: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 +// C-NOT: {{ getelementptr }} +// CXX: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 +// CXX-NOT: {{ getelementptr }} +// WATCHOS: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 +// WATCHOS-NOT: {{ getelementptr }} + va_list vl; + va_start(vl, a); + struct SuperEmpty b = va_arg(vl, struct SuperEmpty); + int c = va_arg(vl, int); + va_end(vl); + return c; +} + +EXTERNC int sort_of_empty_arg_variadic(int a, ...) { +// CHECK-LABEL: @sort_of_empty_arg_variadic( +// C: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 +// C-NOT: {{ getelementptr }} +// CXX: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 +// CXX-NOT: {{ getelementptr }} +// WATCHOS: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 +// WATCHOS-NOT: {{ getelementptr }} + va_list vl; + va_start(vl, a); + struct SortOfEmpty b = va_arg(vl, struct SortOfEmpty); + int c = va_arg(vl, int); + va_end(vl); + return c; +} >From 441052fb17caa896aa82b69b738c5e30bf77ff3b Mon Sep 17 00:00:00 2001 From: Oliver Stannard <oliver.stann...@arm.com> Date: Wed, 29 Jan 2025 15:59:27 +0000 Subject: [PATCH 2/7] Add the -fclang-abi-compat=20 option --- clang/include/clang/Basic/LangOptions.h | 5 +++++ clang/lib/Frontend/CompilerInvocation.cpp | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index 949c8f5d448bcf..df7a5beb070ac5 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -247,6 +247,11 @@ class LangOptionsBase { /// function templates. Ver19, + /// Attempt to be ABI-compatible with code generated by Clang 20.x. + /// This causes clang to: + /// - Nothing yet + Ver20, + /// Conform to the underlying platform's C and C++ ABIs as closely /// as we can. Latest diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 3bf124e4827be9..1d3a49aaddf68a 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -3893,6 +3893,9 @@ void CompilerInvocationBase::GenerateLangArgs(const LangOptions &Opts, case LangOptions::ClangABI::Ver19: GenerateArg(Consumer, OPT_fclang_abi_compat_EQ, "19.0"); break; + case LangOptions::ClangABI::Ver20: + GenerateArg(Consumer, OPT_fclang_abi_compat_EQ, "20.0"); + break; case LangOptions::ClangABI::Latest: break; } @@ -4439,6 +4442,8 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.setClangABICompat(LangOptions::ClangABI::Ver18); else if (Major <= 19) Opts.setClangABICompat(LangOptions::ClangABI::Ver19); + else if (Major <= 20) + Opts.setClangABICompat(LangOptions::ClangABI::Ver20); } else if (Ver != "latest") { Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << A->getValue(); >From f0eef1b65638d928edf3cb894c6f89bb562482c9 Mon Sep 17 00:00:00 2001 From: Oliver Stannard <oliver.stann...@arm.com> Date: Wed, 29 Jan 2025 15:59:54 +0000 Subject: [PATCH 3/7] Allow reverting ABI change with -fclang-abi-compat=20 --- clang/include/clang/Basic/LangOptions.h | 3 ++- clang/lib/CodeGen/Targets/ARM.cpp | 5 +++++ clang/test/CodeGen/arm-empty-args.cpp | 12 ++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index df7a5beb070ac5..e5aea7ab289fc4 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -249,7 +249,8 @@ class LangOptionsBase { /// Attempt to be ABI-compatible with code generated by Clang 20.x. /// This causes clang to: - /// - Nothing yet + /// - Ignore empty struct arguments in C++ mode for AArch64, instead of + /// passing them as if they had a size of 1 byte. Ver20, /// Conform to the underlying platform's C and C++ ABIs as closely diff --git a/clang/lib/CodeGen/Targets/ARM.cpp b/clang/lib/CodeGen/Targets/ARM.cpp index b243ccacc2155d..a7964d94806a4e 100644 --- a/clang/lib/CodeGen/Targets/ARM.cpp +++ b/clang/lib/CodeGen/Targets/ARM.cpp @@ -345,6 +345,11 @@ bool ARMABIInfo::shouldIgnoreEmptyArg(QualType Ty) const { if (Size == 0) return true; + // Clang 20.0 and earlier always ignored empty struct arguments in C++ mode. + if (getContext().getLangOpts().getClangABICompat() <= + LangOptions::ClangABI::Ver20) + return true; + // Otherwise, they are passed as if they have a size of 1 byte. return false; } diff --git a/clang/test/CodeGen/arm-empty-args.cpp b/clang/test/CodeGen/arm-empty-args.cpp index 5770ce160749f7..bce65e43a36508 100644 --- a/clang/test/CodeGen/arm-empty-args.cpp +++ b/clang/test/CodeGen/arm-empty-args.cpp @@ -1,5 +1,6 @@ // RUN: %clang_cc1 -triple armv7a-linux-gnueabi -emit-llvm -o - -x c %s | FileCheck %s --check-prefixes=CHECK,C // RUN: %clang_cc1 -triple armv7a-linux-gnueabi -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,CXX +// RUN: %clang_cc1 -triple armv7a-linux-gnueabi -emit-llvm -o - %s -fclang-abi-compat=20 | FileCheck %s --check-prefixes=CHECK,CXXCLANG20 // RUN: %clang_cc1 -triple thumbv7k-apple-watchos2.0 -target-abi aapcs16 -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,WATCHOS // Empty structs are ignored for PCS purposes on WatchOS and in C mode @@ -17,6 +18,7 @@ struct Empty {}; // C: define{{.*}} i32 @empty_arg(i32 noundef %a) // CXX: define{{.*}} i32 @empty_arg(i8 %e.coerce, i32 noundef %a) +// CXXCLANG20: define{{.*}} i32 @empty_arg(i32 noundef %a) // WATCHOS: define{{.*}} i32 @empty_arg(i32 noundef %a) EXTERNC int empty_arg(struct Empty e, int a) { return a; @@ -24,6 +26,7 @@ EXTERNC int empty_arg(struct Empty e, int a) { // C: define{{.*}} void @empty_ret() // CXX: define{{.*}} void @empty_ret() +// CXXCLANG20: define{{.*}} void @empty_ret() // WATCHOS: define{{.*}} void @empty_ret() EXTERNC struct Empty empty_ret(void) { struct Empty e; @@ -40,6 +43,7 @@ struct SuperEmpty { // C: define{{.*}} i32 @super_empty_arg(i32 noundef %a) // CXX: define{{.*}} i32 @super_empty_arg(i32 noundef %a) +// CXXCLANG20: define{{.*}} i32 @super_empty_arg(i32 noundef %a) // WATCHOS: define{{.*}} i32 @super_empty_arg(i32 noundef %a) EXTERNC int super_empty_arg(struct SuperEmpty e, int a) { return a; @@ -51,6 +55,7 @@ struct SortOfEmpty { // C: define{{.*}} i32 @sort_of_empty_arg(i32 noundef %a) // CXX: define{{.*}} i32 @sort_of_empty_arg(i8 %e.coerce, i32 noundef %a) +// CXXCLANG20: define{{.*}} i32 @sort_of_empty_arg(i32 noundef %a) // WATCHOS: define{{.*}} i32 @sort_of_empty_arg(i32 noundef %a) EXTERNC int sort_of_empty_arg(struct Empty e, int a) { return a; @@ -58,6 +63,7 @@ EXTERNC int sort_of_empty_arg(struct Empty e, int a) { // C: define{{.*}} void @sort_of_empty_ret() // CXX: define{{.*}} void @sort_of_empty_ret() +// CXXCLANG20: define{{.*}} void @sort_of_empty_ret() // WATCHOS: define{{.*}} void @sort_of_empty_ret() EXTERNC struct SortOfEmpty sort_of_empty_ret(void) { struct SortOfEmpty e; @@ -76,6 +82,8 @@ EXTERNC int empty_arg_variadic(int a, ...) { // C-NOT: {{ getelementptr }} // CXX: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 // CXX: %argp.next2 = getelementptr inbounds i8, ptr %argp.cur1, i32 4 +// CXXCLANG20: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 +// CXXCLANG20-NOT: {{ getelementptr }} // WATCHOS: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 // WATCHOS-NOT: {{ getelementptr }} va_list vl; @@ -92,6 +100,8 @@ EXTERNC int super_empty_arg_variadic(int a, ...) { // C-NOT: {{ getelementptr }} // CXX: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 // CXX-NOT: {{ getelementptr }} +// CXXCLANG20: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 +// CXXCLANG20-NOT: {{ getelementptr }} // WATCHOS: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 // WATCHOS-NOT: {{ getelementptr }} va_list vl; @@ -108,6 +118,8 @@ EXTERNC int sort_of_empty_arg_variadic(int a, ...) { // C-NOT: {{ getelementptr }} // CXX: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 // CXX-NOT: {{ getelementptr }} +// CXXCLANG20: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 +// CXXCLANG20-NOT: {{ getelementptr }} // WATCHOS: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 // WATCHOS-NOT: {{ getelementptr }} va_list vl; >From 9bcf7b2b3adf45fb2100de67941342ad3ead6933 Mon Sep 17 00:00:00 2001 From: Oliver Stannard <oliver.stann...@arm.com> Date: Wed, 29 Jan 2025 16:00:10 +0000 Subject: [PATCH 4/7] Revert unneeded change --- clang/lib/CodeGen/Targets/ARM.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clang/lib/CodeGen/Targets/ARM.cpp b/clang/lib/CodeGen/Targets/ARM.cpp index a7964d94806a4e..33648300cebdff 100644 --- a/clang/lib/CodeGen/Targets/ARM.cpp +++ b/clang/lib/CodeGen/Targets/ARM.cpp @@ -790,8 +790,7 @@ RValue ARMABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, if ((IsEmpty || Size == 0) && shouldIgnoreEmptyArg(Ty)) return Slot.asRValue(); - CharUnits TySize = - std::max(getContext().getTypeSizeInChars(Ty), CharUnits::fromQuantity(1)); + CharUnits TySize = getContext().getTypeSizeInChars(Ty); CharUnits TyAlignForABI = getContext().getTypeUnadjustedAlignInChars(Ty); // Use indirect if size of the illegal vector is bigger than 16 bytes. >From e3965825077119e3b65683b23ccc81b91e312b02 Mon Sep 17 00:00:00 2001 From: Oliver Stannard <oliver.stann...@arm.com> Date: Wed, 29 Jan 2025 16:01:05 +0000 Subject: [PATCH 5/7] Add release note --- clang/docs/ReleaseNotes.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index b63bd366cfe884..b53f912f417c47 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -1144,6 +1144,12 @@ Arm and AArch64 Support * FUJITSU-MONAKA (fujitsu-monaka) +- The AArch64 calling convention for empty structs in C++ mode was changed to + pass them as if they have a size of 1 byte, matching the AAPCS64 + specification and GCC's implementation. The previous behaviour of ignoring + the argument can be restored using the -fclang-abi-compat=20 (or earlier) + option. + Android Support ^^^^^^^^^^^^^^^ >From 665877e608214a376ebd32f265a6512d588ccf2b Mon Sep 17 00:00:00 2001 From: Oliver Stannard <oliver.stann...@arm.com> Date: Wed, 29 Jan 2025 21:47:03 +0000 Subject: [PATCH 6/7] Assume this will be backported to llvm-19 --- clang/docs/ReleaseNotes.rst | 2 +- clang/include/clang/Basic/LangOptions.h | 8 ++------ clang/lib/CodeGen/Targets/ARM.cpp | 4 ++-- clang/lib/Frontend/CompilerInvocation.cpp | 5 ----- clang/test/CodeGen/arm-empty-args.cpp | 24 +++++++++++------------ 5 files changed, 17 insertions(+), 26 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index b53f912f417c47..eb7cce684a4de6 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -1147,7 +1147,7 @@ Arm and AArch64 Support - The AArch64 calling convention for empty structs in C++ mode was changed to pass them as if they have a size of 1 byte, matching the AAPCS64 specification and GCC's implementation. The previous behaviour of ignoring - the argument can be restored using the -fclang-abi-compat=20 (or earlier) + the argument can be restored using the -fclang-abi-compat=19 (or earlier) option. Android Support diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index e5aea7ab289fc4..b99cd25bab0131 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -245,13 +245,9 @@ class LangOptionsBase { /// construction vtable because it hasn't added 'type' as a substitution. /// - Skip mangling enclosing class templates of member-like friend /// function templates. - Ver19, - - /// Attempt to be ABI-compatible with code generated by Clang 20.x. - /// This causes clang to: - /// - Ignore empty struct arguments in C++ mode for AArch64, instead of + /// - Ignore empty struct arguments in C++ mode for AArch64, instead of /// passing them as if they had a size of 1 byte. - Ver20, + Ver19, /// Conform to the underlying platform's C and C++ ABIs as closely /// as we can. diff --git a/clang/lib/CodeGen/Targets/ARM.cpp b/clang/lib/CodeGen/Targets/ARM.cpp index 33648300cebdff..47e31ceeaf2943 100644 --- a/clang/lib/CodeGen/Targets/ARM.cpp +++ b/clang/lib/CodeGen/Targets/ARM.cpp @@ -345,9 +345,9 @@ bool ARMABIInfo::shouldIgnoreEmptyArg(QualType Ty) const { if (Size == 0) return true; - // Clang 20.0 and earlier always ignored empty struct arguments in C++ mode. + // Clang 19.0 and earlier always ignored empty struct arguments in C++ mode. if (getContext().getLangOpts().getClangABICompat() <= - LangOptions::ClangABI::Ver20) + LangOptions::ClangABI::Ver19) return true; // Otherwise, they are passed as if they have a size of 1 byte. diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 1d3a49aaddf68a..3bf124e4827be9 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -3893,9 +3893,6 @@ void CompilerInvocationBase::GenerateLangArgs(const LangOptions &Opts, case LangOptions::ClangABI::Ver19: GenerateArg(Consumer, OPT_fclang_abi_compat_EQ, "19.0"); break; - case LangOptions::ClangABI::Ver20: - GenerateArg(Consumer, OPT_fclang_abi_compat_EQ, "20.0"); - break; case LangOptions::ClangABI::Latest: break; } @@ -4442,8 +4439,6 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.setClangABICompat(LangOptions::ClangABI::Ver18); else if (Major <= 19) Opts.setClangABICompat(LangOptions::ClangABI::Ver19); - else if (Major <= 20) - Opts.setClangABICompat(LangOptions::ClangABI::Ver20); } else if (Ver != "latest") { Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << A->getValue(); diff --git a/clang/test/CodeGen/arm-empty-args.cpp b/clang/test/CodeGen/arm-empty-args.cpp index bce65e43a36508..4e61c78b73ab92 100644 --- a/clang/test/CodeGen/arm-empty-args.cpp +++ b/clang/test/CodeGen/arm-empty-args.cpp @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -triple armv7a-linux-gnueabi -emit-llvm -o - -x c %s | FileCheck %s --check-prefixes=CHECK,C // RUN: %clang_cc1 -triple armv7a-linux-gnueabi -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,CXX -// RUN: %clang_cc1 -triple armv7a-linux-gnueabi -emit-llvm -o - %s -fclang-abi-compat=20 | FileCheck %s --check-prefixes=CHECK,CXXCLANG20 +// RUN: %clang_cc1 -triple armv7a-linux-gnueabi -emit-llvm -o - %s -fclang-abi-compat=19 | FileCheck %s --check-prefixes=CHECK,CXXCLANG19 // RUN: %clang_cc1 -triple thumbv7k-apple-watchos2.0 -target-abi aapcs16 -emit-llvm -o - %s | FileCheck %s --check-prefixes=CHECK,WATCHOS // Empty structs are ignored for PCS purposes on WatchOS and in C mode @@ -18,7 +18,7 @@ struct Empty {}; // C: define{{.*}} i32 @empty_arg(i32 noundef %a) // CXX: define{{.*}} i32 @empty_arg(i8 %e.coerce, i32 noundef %a) -// CXXCLANG20: define{{.*}} i32 @empty_arg(i32 noundef %a) +// CXXCLANG19: define{{.*}} i32 @empty_arg(i32 noundef %a) // WATCHOS: define{{.*}} i32 @empty_arg(i32 noundef %a) EXTERNC int empty_arg(struct Empty e, int a) { return a; @@ -26,7 +26,7 @@ EXTERNC int empty_arg(struct Empty e, int a) { // C: define{{.*}} void @empty_ret() // CXX: define{{.*}} void @empty_ret() -// CXXCLANG20: define{{.*}} void @empty_ret() +// CXXCLANG19: define{{.*}} void @empty_ret() // WATCHOS: define{{.*}} void @empty_ret() EXTERNC struct Empty empty_ret(void) { struct Empty e; @@ -43,7 +43,7 @@ struct SuperEmpty { // C: define{{.*}} i32 @super_empty_arg(i32 noundef %a) // CXX: define{{.*}} i32 @super_empty_arg(i32 noundef %a) -// CXXCLANG20: define{{.*}} i32 @super_empty_arg(i32 noundef %a) +// CXXCLANG19: define{{.*}} i32 @super_empty_arg(i32 noundef %a) // WATCHOS: define{{.*}} i32 @super_empty_arg(i32 noundef %a) EXTERNC int super_empty_arg(struct SuperEmpty e, int a) { return a; @@ -55,7 +55,7 @@ struct SortOfEmpty { // C: define{{.*}} i32 @sort_of_empty_arg(i32 noundef %a) // CXX: define{{.*}} i32 @sort_of_empty_arg(i8 %e.coerce, i32 noundef %a) -// CXXCLANG20: define{{.*}} i32 @sort_of_empty_arg(i32 noundef %a) +// CXXCLANG19: define{{.*}} i32 @sort_of_empty_arg(i32 noundef %a) // WATCHOS: define{{.*}} i32 @sort_of_empty_arg(i32 noundef %a) EXTERNC int sort_of_empty_arg(struct Empty e, int a) { return a; @@ -63,7 +63,7 @@ EXTERNC int sort_of_empty_arg(struct Empty e, int a) { // C: define{{.*}} void @sort_of_empty_ret() // CXX: define{{.*}} void @sort_of_empty_ret() -// CXXCLANG20: define{{.*}} void @sort_of_empty_ret() +// CXXCLANG19: define{{.*}} void @sort_of_empty_ret() // WATCHOS: define{{.*}} void @sort_of_empty_ret() EXTERNC struct SortOfEmpty sort_of_empty_ret(void) { struct SortOfEmpty e; @@ -82,8 +82,8 @@ EXTERNC int empty_arg_variadic(int a, ...) { // C-NOT: {{ getelementptr }} // CXX: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 // CXX: %argp.next2 = getelementptr inbounds i8, ptr %argp.cur1, i32 4 -// CXXCLANG20: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 -// CXXCLANG20-NOT: {{ getelementptr }} +// CXXCLANG19: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 +// CXXCLANG19-NOT: {{ getelementptr }} // WATCHOS: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 // WATCHOS-NOT: {{ getelementptr }} va_list vl; @@ -100,8 +100,8 @@ EXTERNC int super_empty_arg_variadic(int a, ...) { // C-NOT: {{ getelementptr }} // CXX: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 // CXX-NOT: {{ getelementptr }} -// CXXCLANG20: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 -// CXXCLANG20-NOT: {{ getelementptr }} +// CXXCLANG19: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 +// CXXCLANG19-NOT: {{ getelementptr }} // WATCHOS: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 // WATCHOS-NOT: {{ getelementptr }} va_list vl; @@ -118,8 +118,8 @@ EXTERNC int sort_of_empty_arg_variadic(int a, ...) { // C-NOT: {{ getelementptr }} // CXX: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 // CXX-NOT: {{ getelementptr }} -// CXXCLANG20: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 -// CXXCLANG20-NOT: {{ getelementptr }} +// CXXCLANG19: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 +// CXXCLANG19-NOT: {{ getelementptr }} // WATCHOS: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 // WATCHOS-NOT: {{ getelementptr }} va_list vl; >From 363adc6a2a4d34dbb760b37a39b0d768d04e65d1 Mon Sep 17 00:00:00 2001 From: Oliver Stannard <oliver.stann...@arm.com> Date: Thu, 30 Jan 2025 08:52:01 +0000 Subject: [PATCH 7/7] Correct architecture in release notes --- clang/docs/ReleaseNotes.rst | 9 ++++----- clang/include/clang/Basic/LangOptions.h | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index eb7cce684a4de6..34334e4fcb67d4 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -1144,11 +1144,10 @@ Arm and AArch64 Support * FUJITSU-MONAKA (fujitsu-monaka) -- The AArch64 calling convention for empty structs in C++ mode was changed to - pass them as if they have a size of 1 byte, matching the AAPCS64 - specification and GCC's implementation. The previous behaviour of ignoring - the argument can be restored using the -fclang-abi-compat=19 (or earlier) - option. +- The ARM calling convention for empty structs in C++ mode was changed to pass + them as if they have a size of 1 byte, matching the AAPCS32 specification and + GCC's implementation. The previous behaviour of ignoring the argument can be + restored using the -fclang-abi-compat=19 (or earlier) option. Android Support ^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index b99cd25bab0131..aa0ce8a110faff 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -245,7 +245,7 @@ class LangOptionsBase { /// construction vtable because it hasn't added 'type' as a substitution. /// - Skip mangling enclosing class templates of member-like friend /// function templates. - /// - Ignore empty struct arguments in C++ mode for AArch64, instead of + /// - Ignore empty struct arguments in C++ mode for ARM, instead of /// passing them as if they had a size of 1 byte. Ver19, _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits