]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
gimple-fold: Fix up __builtin_clear_padding lowering [PR115527]
authorJakub Jelinek <jakub@redhat.com>
Wed, 17 Jul 2024 09:38:33 +0000 (11:38 +0200)
committerJakub Jelinek <jakub@gcc.gnu.org>
Fri, 13 Jun 2025 09:00:28 +0000 (11:00 +0200)
The builtin-clear-padding-6.c testcase fails as clear_padding_type
doesn't correctly recompute the buf->size and buf->off members after
expanding clearing of an array using a runtime loop.
buf->size should be in that case the offset after which it should continue
with next members or padding before them modulo UNITS_PER_WORD and
buf->off that offset minus buf->size.  That is what the code was doing,
but with off being the start of the loop cleared array, not its end.
So, the last hunk in gimple-fold.cc fixes that.
When adding the testcase, I've noticed that the
c-c++-common/torture/builtin-clear-padding-* tests, although clearly
written as runtime tests to test the builtins at runtime, didn't have
{ dg-do run } directive and were just compile tests because of that.
When adding that to the tests, builtin-clear-padding-1.c was already
failing without that clear_padding_type hunk too, but
builtin-clear-padding-5.c was still failing even after the change.
That is due to a bug in clear_padding_flush which the patch fixes as
well - when clear_padding_flush is called with full=true (that happens
at the end of the whole __builtin_clear_padding or on those array
padding clears done by a runtime loop), it wants to flush all the pending
padding clearings rather than just some.  If it is at the end of the whole
object, it decreases wordsize when needed to make sure the code never writes
including RMW cycles to something outside of the object:
      if ((unsigned HOST_WIDE_INT) (buf->off + i + wordsize)
          > (unsigned HOST_WIDE_INT) buf->sz)
        {
          gcc_assert (wordsize > 1);
          wordsize /= 2;
          i -= wordsize;
          continue;
        }
but if it is full==true flush in the middle, this doesn't happen, but we
still process just the buffer bytes before the current end.  If that end
is not on a wordsize boundary, e.g. on the builtin-clear-padding-5.c test
the last chunk is 2 bytes, '\0', '\xff', i is 16 and end is 18,
nonzero_last might be equal to the end - i, i.e. 2 here, but still all_ones
might be true, so in some spots we just didn't emit any clearing in that
last chunk.

2024-07-17  Jakub Jelinek  <jakub@redhat.com>

PR middle-end/115527
* gimple-fold.cc (clear_padding_flush): Introduce endsize
variable and use it instead of wordsize when comparing it against
nonzero_last.
(clear_padding_type): Increment off by sz.

* c-c++-common/torture/builtin-clear-padding-1.c: Add dg-do run
directive.
* c-c++-common/torture/builtin-clear-padding-2.c: Likewise.
* c-c++-common/torture/builtin-clear-padding-3.c: Likewise.
* c-c++-common/torture/builtin-clear-padding-4.c: Likewise.
* c-c++-common/torture/builtin-clear-padding-5.c: Likewise.
* c-c++-common/torture/builtin-clear-padding-6.c: New test.

(cherry picked from commit 8b5919bae11754f4b65a17e63663d3143f9615ac)

gcc/gimple-fold.cc
gcc/testsuite/c-c++-common/torture/builtin-clear-padding-1.c
gcc/testsuite/c-c++-common/torture/builtin-clear-padding-2.c
gcc/testsuite/c-c++-common/torture/builtin-clear-padding-3.c
gcc/testsuite/c-c++-common/torture/builtin-clear-padding-4.c
gcc/testsuite/c-c++-common/torture/builtin-clear-padding-5.c
gcc/testsuite/c-c++-common/torture/builtin-clear-padding-6.c [new file with mode: 0644]

index 010be0b2b89e8cb563346e9f5d315fd0d69d7f35..2381b0c231275171d19d43f581055dffcf259213 100644 (file)
@@ -4237,7 +4237,8 @@ clear_padding_flush (clear_padding_struct *buf, bool full)
          i -= wordsize;
          continue;
        }
-      for (size_t j = i; j < i + wordsize && j < end; j++)
+      size_t endsize = end - i > wordsize ? wordsize : end - i;
+      for (size_t j = i; j < i + endsize; j++)
        {
          if (buf->buf[j])
            {
@@ -4266,12 +4267,12 @@ clear_padding_flush (clear_padding_struct *buf, bool full)
       if (padding_bytes)
        {
          if (nonzero_first == 0
-             && nonzero_last == wordsize
+             && nonzero_last == endsize
              && all_ones)
            {
              /* All bits are padding and we had some padding
                 before too.  Just extend it.  */
-             padding_bytes += wordsize;
+             padding_bytes += endsize;
              continue;
            }
          if (all_ones && nonzero_first == 0)
@@ -4311,7 +4312,7 @@ clear_padding_flush (clear_padding_struct *buf, bool full)
       if (nonzero_first == wordsize)
        /* All bits in a word are 0, there are no padding bits.  */
        continue;
-      if (all_ones && nonzero_last == wordsize)
+      if (all_ones && nonzero_last == endsize)
        {
          /* All bits between nonzero_first and end of word are padding
             bits, start counting padding_bytes.  */
@@ -4353,7 +4354,7 @@ clear_padding_flush (clear_padding_struct *buf, bool full)
                  j = k;
                }
            }
-         if (nonzero_last == wordsize)
+         if (nonzero_last == endsize)
            padding_bytes = nonzero_last - zero_last;
          continue;
        }
@@ -4804,6 +4805,7 @@ clear_padding_type (clear_padding_struct *buf, tree type,
          buf->off = 0;
          buf->size = 0;
          clear_padding_emit_loop (buf, elttype, end, for_auto_init);
+         off += sz;
          buf->base = base;
          buf->sz = prev_sz;
          buf->align = prev_align;
index 6b01a5614b53ee4a8779021a48f19f245295fe58..f739963cc4df7951e25bdaa4685b1a05a4e22522 100644 (file)
@@ -1,4 +1,5 @@
 /* PR libstdc++/88101 */
+/* { dg-do run } */
 
 int i1, i2;
 long double l1, l2;
index 1188bc07ed2746e61d212510e04fcd1488a0ef09..099f202ebc75e6850f3041f60d6143b8ca7428f9 100644 (file)
@@ -1,4 +1,5 @@
 /* PR libstdc++/88101 */
+/* { dg-do run } */
 
 typedef int T __attribute__((aligned (16384)));
 struct S { char a; short b; long double c; T d; T e; long long f; };
index edb7c8eb555ae7765b133bc0f63b3fdb905cf2cf..27bf8f6dd7347c48c67f80ef97faf5a67dad5077 100644 (file)
@@ -1,4 +1,5 @@
 /* PR libstdc++/88101 */
+/* { dg-do run } */
 
 union V { char a; signed char b; unsigned char c; };
 struct T { char a; int b; union V c; };
index 9cb202fc690b5372564fc4c659c43667ef8400f9..ebc589327387d808caaf0023c260c63700a500a6 100644 (file)
@@ -1,6 +1,6 @@
-/* { dg-require-effective-target alloca } */
-
 /* PR libstdc++/88101 */
+/* { dg-do run } */
+/* { dg-require-effective-target alloca } */
 
 struct S { char a; short b; char c; };
 
index d5dbafe1c748e57261bc6e9f45b7d8d2f0c53ca8..3f7d40fddf8c2be95290ef5c7a125a160c1b7f73 100644 (file)
@@ -1,4 +1,5 @@
 /* PR libstdc++/88101 */
+/* { dg-do run } */
 
 struct S { char a; short b; char c; } s1[24], s2[24];
 struct T { char a; long long b; char c; struct S d[3]; long long e; char f; } t1, t2;
diff --git a/gcc/testsuite/c-c++-common/torture/builtin-clear-padding-6.c b/gcc/testsuite/c-c++-common/torture/builtin-clear-padding-6.c
new file mode 100644 (file)
index 0000000..db1f00e
--- /dev/null
@@ -0,0 +1,28 @@
+/* PR middle-end/115527 */
+/* { dg-do run } */
+
+struct T { struct S { double a; signed char b; long c; } d[3]; int e; } t1, t2;
+
+__attribute__((noipa)) void
+foo (struct T *t)
+{
+  for (int i = 0; i < 3; ++i)
+    {
+      t->d[i].a = 1. + 3 * i;
+      t->d[i].b = 2 + 3 * i;
+      t->d[i].c = 3 + 3 * i;
+    }
+  t->e = 10;
+}
+
+int
+main ()
+{
+  __builtin_memset (&t2, -1, sizeof (t2));
+  foo (&t1);
+  foo (&t2);
+  __builtin_clear_padding (&t2);
+  if (__builtin_memcmp (&t1, &t2, sizeof (t1)))
+    __builtin_abort ();
+  return 0;
+}