x86_64: Ignore zero width bitfields in ABI and issue -Wpsabi warning about C zero...
authorJakub Jelinek <jakub@redhat.com>
Mon, 10 Jan 2022 16:43:23 +0000 (17:43 +0100)
committerJakub Jelinek <jakub@redhat.com>
Mon, 10 Jan 2022 16:43:23 +0000 (17:43 +0100)
For zero-width bitfields current GCC classify_argument does:
                  if (DECL_BIT_FIELD (field))
                    {
                      for (i = (int_bit_position (field)
                                + (bit_offset % 64)) / 8 / 8;
                           i < ((int_bit_position (field) + (bit_offset % 64))
                                + tree_to_shwi (DECL_SIZE (field))
                                + 63) / 8 / 8; i++)
                        classes[i]
                          = merge_classes (X86_64_INTEGER_CLASS, classes[i]);
                    }
which I think means that if the zero-width bitfields are at bit-positions
(in the toplevel aggregate) which are multiples of 64 bits doesn't do
anything, (int_bit_position (field) + (bit_offset % 64)) / 64 and
(int_bit_position (field) + (bit_offset % 64) + 63) / 64 should be equal.
But for zero-width bitfields at other bit positions it will call
merge_classes once.  Now, the typical case is that the zero width bitfield
is surrounded by some bitfields and in that case, it doesn't change
anything, but it can be sandwitched in between floats too as the testcases
show.
In C we had this behavior, in C++ previously the FE was removing the
zero-width bitfields and therefore they were ignored.
LLVM and ICC seems to ignore those bitfields both in C and C++ (== passing
struct S in SSE register rather than in GPR).

The x86-64 psABI has been recently clarified by
https://gitlab.com/x86-psABIs/x86-64-ABI/-/commit/1aa4398d26c250b252a0c4a0f777216c9a6789ec
that zero width bitfield should be always ignored.

This patch implements that and emits a warning for C for cases where the ABI
changed from GCC 11.

2022-01-10  Jakub Jelinek  <jakub@redhat.com>

PR target/102024
* config/i386/i386.c (classify_argument): Add zero_width_bitfields
argument, when seeing DECL_FIELD_CXX_ZERO_WIDTH_BIT_FIELD bitfields,
always ignore them, when seeing other zero sized bitfields, either
set zero_width_bitfields to 1 and ignore it or if equal to 2 process
it.  Pass it to recursive calls.  Add wrapper
with old arguments and diagnose ABI differences for C structures
with zero width bitfields.  Formatting fixes.

* gcc.target/i386/pr102024.c: New test.
* g++.target/i386/pr102024.C: New test.

gcc/config/i386/i386.c
gcc/testsuite/g++.target/i386/pr102024.C [new file with mode: 0644]
gcc/testsuite/gcc.target/i386/pr102024.c [new file with mode: 0644]

index c4ed82d..fb9b626 100644 (file)
@@ -2065,7 +2065,8 @@ merge_classes (enum x86_64_reg_class class1, enum x86_64_reg_class class2)
 
 static int
 classify_argument (machine_mode mode, const_tree type,
-                  enum x86_64_reg_class classes[MAX_CLASSES], int bit_offset)
+                  enum x86_64_reg_class classes[MAX_CLASSES], int bit_offset,
+                  int &zero_width_bitfields)
 {
   HOST_WIDE_INT bytes
     = mode == BLKmode ? int_size_in_bytes (type) : (int) GET_MODE_SIZE (mode);
@@ -2123,6 +2124,16 @@ classify_argument (machine_mode mode, const_tree type,
                     misaligned integers.  */
                  if (DECL_BIT_FIELD (field))
                    {
+                     if (integer_zerop (DECL_SIZE (field)))
+                       {
+                         if (DECL_FIELD_CXX_ZERO_WIDTH_BIT_FIELD (field))
+                           continue;
+                         if (zero_width_bitfields != 2)
+                           {
+                             zero_width_bitfields = 1;
+                             continue;
+                           }
+                       }
                      for (i = (int_bit_position (field)
                                + (bit_offset % 64)) / 8 / 8;
                           i < ((int_bit_position (field) + (bit_offset % 64))
@@ -2160,7 +2171,8 @@ classify_argument (machine_mode mode, const_tree type,
                      num = classify_argument (TYPE_MODE (type), type,
                                               subclasses,
                                               (int_bit_position (field)
-                                               + bit_offset) % 512);
+                                               + bit_offset) % 512,
+                                              zero_width_bitfields);
                      if (!num)
                        return 0;
                      pos = (int_bit_position (field)
@@ -2178,7 +2190,8 @@ classify_argument (machine_mode mode, const_tree type,
          {
            int num;
            num = classify_argument (TYPE_MODE (TREE_TYPE (type)),
-                                    TREE_TYPE (type), subclasses, bit_offset);
+                                    TREE_TYPE (type), subclasses, bit_offset,
+                                    zero_width_bitfields);
            if (!num)
              return 0;
 
@@ -2211,7 +2224,7 @@ classify_argument (machine_mode mode, const_tree type,
 
                  num = classify_argument (TYPE_MODE (TREE_TYPE (field)),
                                           TREE_TYPE (field), subclasses,
-                                          bit_offset);
+                                          bit_offset, zero_width_bitfields);
                  if (!num)
                    return 0;
                  for (i = 0; i < num && i < words; i++)
@@ -2231,7 +2244,7 @@ classify_argument (machine_mode mode, const_tree type,
             X86_64_SSEUP_CLASS, everything should be passed in
             memory.  */
          if (classes[0] != X86_64_SSE_CLASS)
-             return 0;
+           return 0;
 
          for (i = 1; i < words; i++)
            if (classes[i] != X86_64_SSEUP_CLASS)
@@ -2257,8 +2270,8 @@ classify_argument (machine_mode mode, const_tree type,
              classes[i] = X86_64_SSE_CLASS;
            }
 
-         /*  If X86_64_X87UP_CLASS isn't preceded by X86_64_X87_CLASS,
-              everything should be passed in memory.  */
+         /* If X86_64_X87UP_CLASS isn't preceded by X86_64_X87_CLASS,
+            everything should be passed in memory.  */
          if (classes[i] == X86_64_X87UP_CLASS
              && (classes[i - 1] != X86_64_X87_CLASS))
            {
@@ -2487,6 +2500,44 @@ classify_argument (machine_mode mode, const_tree type,
     }
 }
 
+/* Wrapper around classify_argument with the extra zero_width_bitfields
+   argument, to diagnose GCC 12.1 ABI differences for C.  */
+
+static int
+classify_argument (machine_mode mode, const_tree type,
+                  enum x86_64_reg_class classes[MAX_CLASSES], int bit_offset)
+{
+  int zero_width_bitfields = 0;
+  static bool warned = false;
+  int n = classify_argument (mode, type, classes, bit_offset,
+                            zero_width_bitfields);
+  if (!zero_width_bitfields || warned || !warn_psabi)
+    return n;
+  enum x86_64_reg_class alt_classes[MAX_CLASSES];
+  zero_width_bitfields = 2;
+  if (classify_argument (mode, type, alt_classes, bit_offset,
+                        zero_width_bitfields) != n)
+    zero_width_bitfields = 3;
+  else
+    for (int i = 0; i < n; i++)
+      if (classes[i] != alt_classes[i])
+       {
+         zero_width_bitfields = 3;
+         break;
+       }
+  if (zero_width_bitfields == 3)
+    {
+      warned = true;
+      const char *url
+       = CHANGES_ROOT_URL "gcc-12/changes.html#zero_width_bitfields";
+
+      inform (input_location,
+             "the ABI of passing C structures with zero-width bit-fields"
+             " has changed in GCC %{12.1%}", url);
+    }
+  return n;
+}
+
 /* Examine the argument and return set number of register required in each
    class.  Return true iff parameter should be passed in memory.  */
 
diff --git a/gcc/testsuite/g++.target/i386/pr102024.C b/gcc/testsuite/g++.target/i386/pr102024.C
new file mode 100644 (file)
index 0000000..0fbc026
--- /dev/null
@@ -0,0 +1,12 @@
+// PR target/102024
+// { dg-do compile }
+
+struct S { float a; int : 0; float b; };
+void foo (struct S x);
+
+void
+bar (void)
+{
+  struct S s = { 0.0f, 0.0f };
+  foo (s);     // { dg-bogus "the ABI of passing C structures with zero-width bit-fields has changed in GCC 12.1" }
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr102024.c b/gcc/testsuite/gcc.target/i386/pr102024.c
new file mode 100644 (file)
index 0000000..703195b
--- /dev/null
@@ -0,0 +1,12 @@
+/* PR target/102024 */
+/* { dg-do compile } */
+
+struct S { float a; int : 0; float b; };
+void foo (struct S x);
+
+void
+bar (void)
+{
+  struct S s = { 0.0f, 0.0f };
+  foo (s);     /* { dg-message "the ABI of passing C structures with zero-width bit-fields has changed in GCC 12.1" "" { target { ! ia32 } } } */
+}