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).

llvm-svn: 302313
This commit is contained in:
Tim Northover 2017-05-05 22:36:06 +00:00
parent ce03732ec8
commit 23bcad226c
2 changed files with 76 additions and 4 deletions

View File

@ -4890,10 +4890,16 @@ ABIArgInfo AArch64ABIInfo::classifyArgumentType(QualType Ty) const {
// 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::classifyArgumentType(QualType Ty) const {
}
// 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::classifyReturnType(QualType RetTy) const {
: 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::classifyReturnType(QualType RetTy) const {
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.

View File

@ -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;
}