diff mbox series

middle-end/77608: object size estimate with variable offsets

Message ID 20220105132058.1073416-1-siddhesh@gotplt.org
State New
Headers show
Series middle-end/77608: object size estimate with variable offsets | expand

Commit Message

Siddhesh Poyarekar Jan. 5, 2022, 1:20 p.m. UTC
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 <siddhesh@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(-)

Comments

Jakub Jelinek Jan. 10, 2022, 11:15 a.m. UTC | #1
On Wed, Jan 05, 2022 at 06:50:58PM +0530, Siddhesh Poyarekar wrote:
> --- 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,

Formatting, missing space in between , and tree sz.

Otherwise LGTM.

	Jakub
diff mbox series

Patch

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;