]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
tree-optimization/117355: object size for PHI nodes with negative offsets
authorSiddhesh Poyarekar <siddhesh@gotplt.org>
Wed, 20 Nov 2024 03:51:31 +0000 (22:51 -0500)
committerSiddhesh Poyarekar <siddhesh@gotplt.org>
Fri, 22 Nov 2024 20:25:09 +0000 (15:25 -0500)
When the object size estimate is returned for a PHI node, it is the
maximum possible value, which is fine in isolation.  When combined with
negative offsets however, it may sometimes end up in zero size because
the resultant size was larger than the wholesize, leading
size_for_offset to conclude that there's a potential underflow.  Fix
this by allowing a non-strict mode to size_for_offset, which
conservatively returns the size (or wholesize) in case of a negative
offset.

gcc/ChangeLog:

PR tree-optimization/117355
* tree-object-size.cc (size_for_offset): New argument STRICT,
return SZ if it is set to false.
(plus_stmt_object_size): Adjust call to SIZE_FOR_OFFSET.

gcc/testsuite/ChangeLog:

PR tree-optimization/117355
* g++.dg/ext/builtin-object-size2.C (test9): New test.
(main): Call it.
* gcc.dg/builtin-object-size-3.c (test10): Adjust expected size.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
gcc/testsuite/g++.dg/ext/builtin-object-size2.C
gcc/testsuite/gcc.dg/builtin-object-size-3.c
gcc/tree-object-size.cc

index 7a8f4e6273320b433399dd9013f73b231be64cf1..45401b5a9c13fb08ab6ba898be8471e6dd6c3a91 100644 (file)
@@ -406,6 +406,32 @@ test8 (union F *f)
     FAIL ();
 }
 
+// PR117355
+#define STR "bbbbbbbbbbbbbbbbbbbbbbbbbbb"
+
+void
+__attribute__ ((noinline))
+test9 (void)
+{
+  char line[256];
+  const char *p = STR;
+  const char *q = p + sizeof (STR) - 1;
+
+  char *q1 = line;
+  for (const char *p1 = p; p1 < q;)
+    {
+      *q1++ = *p1++;
+
+      if (p1 < q && (*q1++ = *p1++) != '\0')
+       {
+         if (__builtin_object_size (q1 - 2, 0) == 0)
+           __builtin_abort ();
+         if (__builtin_object_size (q1 - 2, 1) == 0)
+           __builtin_abort ();
+       }
+    }
+}
+
 int
 main (void)
 {
@@ -430,5 +456,6 @@ main (void)
   union F f, *fp = &f;
   __asm ("" : "+r" (fp));
   test8 (fp);
+  test9 ();
   DONE ();
 }
index ec2c62c96401316799167afea8bbfaf014eb7def..e0c967e003f60ca1eba43213e99e2a615dd3a46b 100644 (file)
@@ -619,7 +619,7 @@ test10 (void)
          if (__builtin_object_size (p - 3, 2) != sizeof (buf) - i + 3)
            FAIL ();
 #else
-         if (__builtin_object_size (p - 3, 2) != 0)
+         if (__builtin_object_size (p - 3, 2) != 3)
            FAIL ();
 #endif
          break;
index 09aad88498eabe6d64fe682a8850d6d1b337c894..6413ebcca37c72f2a20cd2a8620438fd637647c7 100644 (file)
@@ -344,7 +344,8 @@ init_offset_limit (void)
    be positive and hence, be within OFFSET_LIMIT for valid offsets.  */
 
 static tree
-size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE)
+size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE,
+                bool strict = true)
 {
   gcc_checking_assert (types_compatible_p (TREE_TYPE (sz), sizetype));
 
@@ -377,9 +378,17 @@ size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE)
        return sz;
 
       /* Negative or too large offset even after adjustment, cannot be within
-        bounds of an object.  */
+        bounds of an object.  The exception here is when the base object size
+        has been overestimated (e.g. through PHI nodes or a COND_EXPR) and the
+        adjusted offset remains negative.  If the caller wants to be
+        permissive, return the base size.  */
       if (compare_tree_int (offset, offset_limit) > 0)
-       return size_zero_node;
+       {
+         if (strict)
+           return size_zero_node;
+         else
+           return sz;
+       }
     }
 
   return size_binop (MINUS_EXPR, size_binop (MAX_EXPR, sz, offset), offset);
@@ -1521,16 +1530,23 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
          addr_object_size (osi, op0, object_size_type, &bytes, &wholesize);
        }
 
+      bool pos_offset = (size_valid_p (op1, 0)
+                        && compare_tree_int (op1, offset_limit) <= 0);
+
       /* size_for_offset doesn't make sense for -1 size, but it does for size 0
         since the wholesize could be non-zero and a negative offset could give
         a non-zero size.  */
       if (size_unknown_p (bytes, 0))
        ;
+      /* In the static case, We want SIZE_FOR_OFFSET to go a bit easy on us if
+        it sees a negative offset since BYTES could have been
+        overestimated.  */
       else if ((object_size_type & OST_DYNAMIC)
               || bytes != wholesize
-              || (size_valid_p (op1, object_size_type)
-                  && compare_tree_int (op1, offset_limit) <= 0))
-       bytes = size_for_offset (bytes, op1, wholesize);
+              || pos_offset)
+       bytes = size_for_offset (bytes, op1, wholesize,
+                                ((object_size_type & OST_DYNAMIC)
+                                 || pos_offset));
       /* In the static case, with a negative offset, the best estimate for
         minimum size is size_unknown but for maximum size, the wholesize is a
         better estimate than size_unknown.  */