This partially fixes middle-end/77608 by making __builtin_object_size
return the whole object size instead of bailing out in case the offset
in the object is variable and the maximum estimate is requested.

gcc/ChangeLog:

        PR middle-end/77608
        * tree-object-size.c (size_for_offset): New object_size_type
        parameter.  Return known whole size if offset is not constant.
        (addr_object_size, compute_builtin_object_size): Adjust.
        (plus_stmt_object_size): Defer constness check of offset to
        size_for_offset.

gcc/testsuite/ChangeLog:

        PR middle-end/77608
        * gcc.dg/builtin-object-size-1.c (test1): Add tests.
        * gcc.dg/builtin-object-size-2.c (test1): Likewise.

Signed-off-by: Siddhesh Poyarekar <siddh...@gotplt.org>
---

This applies on top of the __builtin_dynamic_object_size patchset.  It
should be possible to process trees with side effects and fully fix
77608, but I'll do that later since it would be a bit more involved.

Tested with a full bootstrap on x86_64 and ubsan bootstrap.

 gcc/testsuite/gcc.dg/builtin-object-size-1.c | 16 ++++
 gcc/testsuite/gcc.dg/builtin-object-size-2.c | 37 ++++++++++
 gcc/tree-object-size.c                       | 77 +++++++++++---------
 3 files changed, 97 insertions(+), 33 deletions(-)

diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-1.c 
b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
index 161f426ec0b..0f92713b141 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
@@ -22,6 +22,8 @@ struct A
 extern char exta[];
 extern char extb[30];
 extern struct A zerol[0];
+int off = 2;
+int off2 = -3;
 
 void
 __attribute__ ((noinline))
@@ -162,6 +164,13 @@ test1 (void *q, int x)
   if (__builtin_object_size (&extb[5], 0) != sizeof (extb) - 5)
     abort ();
 #ifdef __builtin_object_size
+  if (__builtin_object_size (&extb[off], 0) != sizeof (extb) - off)
+    abort ();
+  r = &extb[5];
+  if (__builtin_object_size (r + off, 0) != sizeof (extb) - 5 - off)
+    abort ();
+  if (__builtin_object_size (r + off2, 0) != sizeof (extb) - 5 - off2)
+    abort ();
   if (__builtin_object_size (var, 0) != x + 10)
     abort ();
   if (__builtin_object_size (var + 10, 0) != x)
@@ -169,6 +178,13 @@ test1 (void *q, int x)
   if (__builtin_object_size (&var[5], 0) != x + 5)
     abort ();
 #else
+  if (__builtin_object_size (&extb[off], 0) != sizeof (extb))
+    abort ();
+  r = &extb[5];
+  if (__builtin_object_size (r + off, 0) != sizeof (extb))
+    abort ();
+  if (__builtin_object_size (r + off2, 0) != sizeof (extb))
+    abort ();
   if (__builtin_object_size (var, 0) != (size_t) -1)
     abort ();
   if (__builtin_object_size (var + 10, 0) != (size_t) -1)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-2.c 
b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
index 2729538da17..62a5af1384a 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-2.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
@@ -24,6 +24,9 @@ extern char extb[30];
 extern struct A extc[];
 struct A zerol[0];
 
+int off = 3;
+int off2 = -3;
+
 void
 __attribute__ ((noinline))
 test1 (void *q, int x)
@@ -151,6 +154,23 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&extb[5], 1) != sizeof (extb) - 5)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (&extb[off], 1) != sizeof (extb) - off)
+    abort ();
+  r = &extb[5];
+  if (__builtin_object_size (r + off, 1) != sizeof (extb) - 5 - off)
+    abort ();
+  if (__builtin_object_size (r + off2, 0) != sizeof (extb) - 5 - off2)
+    abort ();
+#else
+  if (__builtin_object_size (&extb[off], 1) != sizeof (extb))
+    abort ();
+  r = &extb[5];
+  if (__builtin_object_size (r + off, 1) != sizeof (extb))
+    abort ();
+  if (__builtin_object_size (r + off2, 0) != sizeof (extb))
+    abort ();
+#endif
   if (__builtin_object_size (extc, 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (extc + 10, 1) != (size_t) -1)
@@ -200,6 +220,23 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&vara[7].c[7], 1) != sizeof (vara[0].c) - 7)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (&vara[5].c[off], 1) != sizeof (vara[0].c) - off)
+    abort ();
+  r = &vara[1].c[5];
+  if (__builtin_object_size (r + off, 1) != sizeof (vara[0].c) - 5 - off)
+    abort ();
+  if (__builtin_object_size (r + off2, 1) != sizeof (vara[0].c) - 5 - off2)
+    abort ();
+#else
+  if (__builtin_object_size (&vara[5].c[off], 1) != sizeof (vara[0].c))
+    abort ();
+  r = &vara[1].c[5];
+  if (__builtin_object_size (r + off, 1) != sizeof (vara[0].c))
+    abort ();
+  if (__builtin_object_size (r + off2, 1) != sizeof (vara[0].c))
+    abort ();
+#endif
   if (__builtin_object_size (zerol, 1) != 0)
     abort ();
   if (__builtin_object_size (&zerol, 1) != 0)
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index bf45adc7307..b5cb637f207 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -347,10 +347,21 @@ 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 (int object_size_type,tree sz, tree offset,
+                tree wholesize = NULL_TREE)
 {
   gcc_checking_assert (types_compatible_p (TREE_TYPE (sz), sizetype));
 
+  /* When maximum size is requested and the offset is non-constant, return the
+     whole size instead of bailing out in __builtin_object_size.  */
+  if (!(object_size_type & (OST_MINIMUM | OST_DYNAMIC))
+      && TREE_CODE (offset) != INTEGER_CST)
+    {
+      if (wholesize)
+       sz = wholesize;
+      offset = size_zero_node;
+    }
+
   /* For negative offsets, if we have a distinct WHOLESIZE, use it to get a net
      offset from the whole object.  */
   if (wholesize && wholesize != sz
@@ -547,7 +558,8 @@ addr_object_size (struct object_size_info *osi, const_tree 
ptr,
            sz = wholesize = size_unknown (object_size_type);
        }
       if (!size_unknown_p (sz, object_size_type))
-       sz = size_for_offset (sz, TREE_OPERAND (pt_var, 1), wholesize);
+       sz = size_for_offset (object_size_type, sz, TREE_OPERAND (pt_var, 1),
+                             wholesize);
 
       if (!size_unknown_p (sz, object_size_type)
          && (TREE_CODE (sz) != INTEGER_CST
@@ -695,7 +707,7 @@ addr_object_size (struct object_size_info *osi, const_tree 
ptr,
        var_size = pt_var_size;
       bytes = compute_object_offset (TREE_OPERAND (ptr, 0), var);
       if (bytes != error_mark_node)
-       bytes = size_for_offset (var_size, bytes);
+       bytes = size_for_offset (object_size_type, var_size, bytes);
       if (var != pt_var
          && pt_var_size
          && TREE_CODE (pt_var) == MEM_REF
@@ -704,7 +716,7 @@ addr_object_size (struct object_size_info *osi, const_tree 
ptr,
          tree bytes2 = compute_object_offset (TREE_OPERAND (ptr, 0), pt_var);
          if (bytes2 != error_mark_node)
            {
-             bytes2 = size_for_offset (pt_var_size, bytes2);
+             bytes2 = size_for_offset (object_size_type, pt_var_size, bytes2);
              bytes = size_binop (MIN_EXPR, bytes, bytes2);
            }
        }
@@ -1058,7 +1070,7 @@ compute_builtin_object_size (tree ptr, int 
object_size_type,
                  && compute_builtin_object_size (ptr, object_size_type,
                                                  psize))
                {
-                 *psize = size_for_offset (*psize, offset);
+                 *psize = size_for_offset (object_size_type, *psize, offset);
                  return true;
                }
            }
@@ -1323,43 +1335,42 @@ plus_stmt_object_size (struct object_size_info *osi, 
tree var, gimple *stmt)
     return false;
 
   /* Handle PTR + OFFSET here.  */
-  if (((object_size_type & OST_DYNAMIC) || TREE_CODE (op1) == INTEGER_CST)
-      && (TREE_CODE (op0) == SSA_NAME
-         || TREE_CODE (op0) == ADDR_EXPR))
+  if (TREE_CODE (op0) == SSA_NAME)
     {
-      if (TREE_CODE (op0) == SSA_NAME)
-       {
-         if (osi->pass == 0)
-           collect_object_sizes_for (osi, op0);
+      if (osi->pass == 0)
+       collect_object_sizes_for (osi, op0);
 
-         bytes = object_sizes_get (osi, SSA_NAME_VERSION (op0));
-         wholesize = object_sizes_get (osi, SSA_NAME_VERSION (op0), true);
-         reexamine = bitmap_bit_p (osi->reexamine, SSA_NAME_VERSION (op0));
-       }
-      else
-       {
-         /* op0 will be ADDR_EXPR here.  We should never come here during
-            reexamination.  */
-         gcc_checking_assert (osi->pass == 0);
-         addr_object_size (osi, op0, object_size_type, &bytes, &wholesize);
-       }
-
-      /* In the first pass, do not compute size for offset if either the
-        maximum size is unknown or the minimum size is not initialized yet;
-        the latter indicates a dependency loop and will be resolved in
-        subsequent passes.  We attempt to compute offset for 0 minimum size
-        too because a negative offset could be within bounds of WHOLESIZE,
-        giving a non-zero result for VAR.  */
-      if (osi->pass != 0 || !size_unknown_p (bytes, 0))
-       bytes = size_for_offset (bytes, op1, wholesize);
+      bytes = object_sizes_get (osi, SSA_NAME_VERSION (op0));
+      wholesize = object_sizes_get (osi, SSA_NAME_VERSION (op0), true);
+      reexamine = bitmap_bit_p (osi->reexamine, SSA_NAME_VERSION (op0));
+    }
+  else if (TREE_CODE (op0) == ADDR_EXPR)
+    {
+      /* op0 will be ADDR_EXPR here.  We should never come here during
+        reexamination.  */
+      gcc_checking_assert (osi->pass == 0);
+      addr_object_size (osi, op0, object_size_type, &bytes, &wholesize);
     }
   else
-    bytes = wholesize = size_unknown (object_size_type);
+    {
+      bytes = wholesize = size_unknown (object_size_type);
+      goto out;
+    }
+
+  /* In the first pass, do not compute size for offset if either the
+     maximum size is unknown or the minimum size is not initialized yet;
+     the latter indicates a dependency loop and will be resolved in
+     subsequent passes.  We attempt to compute offset for 0 minimum size
+     too because a negative offset could be within bounds of WHOLESIZE,
+     giving a non-zero result for VAR.  */
+  if (osi->pass != 0 || !size_unknown_p (bytes, 0))
+    bytes = size_for_offset (object_size_type, bytes, op1, wholesize);
 
   if (!size_valid_p (bytes, object_size_type)
       || !size_valid_p (wholesize, object_size_type))
     bytes = wholesize = size_unknown (object_size_type);
 
+out:
   if (object_sizes_set (osi, varno, bytes, wholesize))
     osi->changed = true;
   return reexamine;
-- 
2.33.1

Reply via email to