Author: tnorthover Date: Fri May 5 17:36:06 2017 New Revision: 302313 URL: http://llvm.org/viewvc/llvm-project?rev=302313&view=rev Log: AArch64: fix weird edge case in ABI.
It turns out there are some sort-of-but-not-quite empty structs that break all the rules. For example: struct SuperEmpty { int arr[0]; }; struct SortOfEmpty { struct SuperEmpty e; }; Both of these have sizeof == 0, even in C++ mode, for GCC compatibility. The first one also doesn't occupy a register when passed by value in GNU C++ mode, unlike everything else. On Darwin, we want to ignore the lot (and especially don't want to try to use an i0 as we were). Added: cfe/trunk/test/CodeGen/aarch64-args.cpp Modified: cfe/trunk/lib/CodeGen/TargetInfo.cpp Modified: cfe/trunk/lib/CodeGen/TargetInfo.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/TargetInfo.cpp?rev=302313&r1=302312&r2=302313&view=diff ============================================================================== --- cfe/trunk/lib/CodeGen/TargetInfo.cpp (original) +++ cfe/trunk/lib/CodeGen/TargetInfo.cpp Fri May 5 17:36:06 2017 @@ -4890,10 +4890,16 @@ ABIArgInfo AArch64ABIInfo::classifyArgum // Empty records are always ignored on Darwin, but actually passed in C++ mode // elsewhere for GNU compatibility. - if (isEmptyRecord(getContext(), Ty, true)) { + uint64_t Size = getContext().getTypeSize(Ty); + bool IsEmpty = isEmptyRecord(getContext(), Ty, true); + if (IsEmpty || Size == 0) { if (!getContext().getLangOpts().CPlusPlus || isDarwinPCS()) return ABIArgInfo::getIgnore(); + // GNU C mode. The only argument that gets ignored is an empty one with size + // 0. + if (IsEmpty && Size == 0) + return ABIArgInfo::getIgnore(); return ABIArgInfo::getDirect(llvm::Type::getInt8Ty(getVMContext())); } @@ -4906,7 +4912,6 @@ ABIArgInfo AArch64ABIInfo::classifyArgum } // Aggregates <= 16 bytes are passed directly in registers or on the stack. - uint64_t Size = getContext().getTypeSize(Ty); if (Size <= 128) { // On RenderScript, coerce Aggregates <= 16 bytes to an integer array of // same size and alignment. @@ -4946,7 +4951,8 @@ ABIArgInfo AArch64ABIInfo::classifyRetur : ABIArgInfo::getDirect()); } - if (isEmptyRecord(getContext(), RetTy, true)) + uint64_t Size = getContext().getTypeSize(RetTy); + if (isEmptyRecord(getContext(), RetTy, true) || Size == 0) return ABIArgInfo::getIgnore(); const Type *Base = nullptr; @@ -4956,7 +4962,6 @@ ABIArgInfo AArch64ABIInfo::classifyRetur return ABIArgInfo::getDirect(); // Aggregates <= 16 bytes are returned directly in registers or on the stack. - uint64_t Size = getContext().getTypeSize(RetTy); if (Size <= 128) { // On RenderScript, coerce Aggregates <= 16 bytes to an integer array of // same size and alignment. Added: cfe/trunk/test/CodeGen/aarch64-args.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/aarch64-args.cpp?rev=302313&view=auto ============================================================================== --- cfe/trunk/test/CodeGen/aarch64-args.cpp (added) +++ cfe/trunk/test/CodeGen/aarch64-args.cpp Fri May 5 17:36:06 2017 @@ -0,0 +1,67 @@ +// RUN: %clang_cc1 -triple arm64-apple-ios7.0 -target-abi darwinpcs -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple aarch64-linux-gnu -emit-llvm -o - -x c %s | FileCheck %s --check-prefix=CHECK-GNU-C +// RUN: %clang_cc1 -triple aarch64-linux-gnu -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-GNU-CXX + +// Empty structs are ignored for PCS purposes on Darwin and in C mode elsewhere. +// In C++ mode on ELF 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 {}; + +// CHECK: define i32 @empty_arg(i32 %a) +// CHECK-GNU-C: define i32 @empty_arg(i32 %a) +// CHECK-GNU-CXX: define i32 @empty_arg(i8 %e.coerce, i32 %a) +EXTERNC int empty_arg(struct Empty e, int a) { + return a; +} + +// CHECK: define void @empty_ret() +// CHECK-GNU-C: define void @empty_ret() +// CHECK-GNU-CXX: define void @empty_ret() +EXTERNC struct Empty empty_ret() { + 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]; +}; + +// CHECK: define i32 @super_empty_arg(i32 %a) +// CHECK-GNU-C: define i32 @super_empty_arg(i32 %a) +// CHECK-GNU-CXX: define i32 @super_empty_arg(i32 %a) +EXTERNC int super_empty_arg(struct SuperEmpty e, int a) { + return a; +} + +// This is not empty. It has 0 size but consumes a register slot for GCC. + +struct SortOfEmpty { + struct SuperEmpty e; +}; + +// CHECK: define i32 @sort_of_empty_arg(i32 %a) +// CHECK-GNU-C: define i32 @sort_of_empty_arg(i32 %a) +// CHECK-GNU-CXX: define i32 @sort_of_empty_arg(i8 %e.coerce, i32 %a) +EXTERNC int sort_of_empty_arg(struct Empty e, int a) { + return a; +} + +// CHECK: define void @sort_of_empty_ret() +// CHECK-GNU-C: define void @sort_of_empty_ret() +// CHECK-GNU-CXX: define void @sort_of_empty_ret() +EXTERNC struct SortOfEmpty sort_of_empty_ret() { + struct SortOfEmpty e; + return e; +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits