forked from OSchip/llvm-project
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:
parent
ce03732ec8
commit
23bcad226c
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue