r302313 - AArch64: fix weird edge case in ABI.

Tim Northover via cfe-commits cfe-commits at lists.llvm.org
Fri May 5 15:36:07 PDT 2017


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




More information about the cfe-commits mailing list