diff mbox series

[10/10] tree-object-size: Handle dynamic offsets

Message ID 20211109190137.1107736-11-siddhesh@gotplt.org
State New
Headers show
Series __builtin_dynamic_object_size | expand

Commit Message

Siddhesh Poyarekar Nov. 9, 2021, 7:01 p.m. UTC
Compute whole sizes for objects to allow offsets to be negative for
dynamic object sizes.  This way, the returned object size would be
precise even for negative offsets as long as the offset is within bounds
of the object.

gcc/ChangeLog:

	* tree-object-size.c (addr_object_size, expr_object_size,
	plus_stmt_object_size, cond_expr_object_size,
	phi_dynamic_object_size, parm_object_size): Add new wholesize
	argument and handle whole sizes.
	(object_whole_sizes): New vector array.
	(object_sizes_grow, object_sizes_release, object_sizes_get,
	object_sizes_set, object_sizes_initialize): Add new wholesize
	argument to manage object_whole_sizes arrays.
	(size_for_offset): Get offset from wholesize if available.
	(estimate_size): Handle COMPOUND_EXPR.
	(gimplify_size_expressions): Also remove the whole size SSA.
	(bundle_whole_size): New function.
	(ssa_object_size): Remove function.
	(collect_object_sizes_for): Return a tree and accept new
	wholesize argument.
	(init_object_sizes): Allocate object_whole_sizes.
	(fini_object_sizes): Free object_whole_sizes.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-dynamic-object-size-0.c: Add new tests.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 .../gcc.dg/builtin-dynamic-object-size-0.c    |  87 ++++
 gcc/tree-object-size.c                        | 423 ++++++++++++------
 2 files changed, 383 insertions(+), 127 deletions(-)
diff mbox series

Patch

diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
index a1db63b2d45..3b38b915593 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -273,6 +273,25 @@  test_substring (size_t sz, size_t off)
   return __builtin_dynamic_object_size (&str[off], 0);
 }
 
+size_t
+__attribute__ ((noinline))
+test_substring_ptrplus (size_t sz, size_t off)
+{
+  int str[sz];
+
+  return __builtin_dynamic_object_size (str + off, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_substring_ptrplus2 (size_t sz, size_t off, size_t off2)
+{
+  int str[sz];
+  int *ptr = &str[off];
+
+  return __builtin_dynamic_object_size (ptr + off2, 0);
+}
+
 size_t
 __attribute__ ((access (__read_write__, 1, 2)))
 test_parmsz_simple (void *obj, size_t sz)
@@ -280,6 +299,40 @@  test_parmsz_simple (void *obj, size_t sz)
   return __builtin_dynamic_object_size (obj, 0);
 }
 
+size_t
+__attribute__ ((noinline))
+__attribute__ ((access (__read_write__, 1, 2)))
+test_parmsz (void *obj, size_t sz, size_t off)
+{
+  return __builtin_dynamic_object_size (obj + off, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+__attribute__ ((access (__read_write__, 1, 2)))
+test_parmsz_scale (int *obj, size_t sz, size_t off)
+{
+  return __builtin_dynamic_object_size (obj + off, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+__attribute__ ((access (__read_write__, 1, 2)))
+test_loop (int *obj, size_t sz, size_t start, size_t end, int incr)
+{
+  int *ptr = obj + start;
+
+  for (int i = start; i != end; i = i + incr)
+    {
+      ptr = ptr + incr;
+      if (__builtin_dynamic_object_size (ptr, 0) == 0)
+	return 0;
+    }
+
+  return __builtin_dynamic_object_size (ptr, 0);
+}
+
+
 unsigned nfails = 0;
 
 #define FAIL() ({ \
@@ -357,6 +410,14 @@  main (int argc, char **argv)
   size_t objsz = 0;
   if (test_dynarray_struct_subobj2 (42, 4, &objsz) != objsz - 4 - 12)
     FAIL ();
+  if (test_substring_ptrplus (128, 4) != (128 - 4) * sizeof (int))
+    FAIL ();
+  if (test_substring_ptrplus (128, 142) != 0)
+    FAIL ();
+  if (test_substring_ptrplus2 (128, 4, 4) != (128 - 8) * sizeof (int))
+    FAIL ();
+  if (test_substring_ptrplus2 (128, 4, -3) != (128 - 1) * sizeof (int))
+    FAIL ();
   if (test_dynarray_cond (0) != 16)
     FAIL ();
   if (test_dynarray_cond (1) != 8)
@@ -368,6 +429,32 @@  main (int argc, char **argv)
   if (test_parmsz_simple (argv[0], __builtin_strlen (argv[0]) + 1)
       != __builtin_strlen (argv[0]) + 1)
     FAIL ();
+  if (test_parmsz (argv[0], __builtin_strlen (argv[0]) + 1, -1) != 0)
+    FAIL ();
+  if (test_parmsz (argv[0], __builtin_strlen (argv[0]) + 1, 0)
+      != __builtin_strlen (argv[0]) + 1)
+    FAIL ();
+  if (test_parmsz (argv[0], __builtin_strlen (argv[0]) + 1,
+		   __builtin_strlen (argv[0])) != 1)
+    FAIL ();
+  if (test_parmsz (argv[0], __builtin_strlen (argv[0]) + 1,
+		   __builtin_strlen (argv[0]) + 2) != 0)
+    FAIL ();
+  int in[42];
+  if (test_parmsz_scale (in, 42, 2) != 40 * sizeof (int))
+    FAIL ();
+  if (test_loop (in, 42, 0, 32, 1) != 10 * sizeof (int))
+    FAIL ();
+  if (test_loop (in, 42, 32, -1, -1) != 0)
+    FAIL ();
+  if (test_loop (in, 42, 32, 10, -1) != 32 * sizeof (int))
+    FAIL ();
+  if (test_loop (in, 42, 42, 0, -1) != 42 * sizeof (int))
+    FAIL ();
+  if (test_loop (in, 42, 44, 0, -1) != 0)
+    FAIL ();
+  if (test_loop (in, 42, 20, 52, 1) != 0)
+    FAIL ();
 
   if (nfails > 0)
     __builtin_abort ();
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index 865fc3feea5..d82937de6ba 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -57,14 +57,14 @@  enum
 
 static tree compute_object_offset (const_tree, const_tree);
 static bool addr_object_size (struct object_size_info *,
-			      const_tree, int, tree *);
+			      const_tree, int, tree *, tree *t = NULL);
 static tree alloc_object_size (const gcall *, int);
 static tree pass_through_call (const gcall *);
-static void collect_object_sizes_for (struct object_size_info *, tree);
-static tree expr_object_size (struct object_size_info *, tree);
-static tree ssa_object_size (struct object_size_info *, tree, tree);
-static tree plus_stmt_object_size (struct object_size_info *, gimple *);
-static tree cond_expr_object_size (struct object_size_info *, gimple *);
+static tree collect_object_sizes_for (struct object_size_info *, tree,
+				      tree *t = NULL);
+static tree expr_object_size (struct object_size_info *, tree, tree *);
+static tree plus_stmt_object_size (struct object_size_info *, tree, tree *);
+static tree cond_expr_object_size (struct object_size_info *, tree, tree *);
 static void init_offset_limit (void);
 
 /* object_sizes[0] is upper bound for number of bytes till the end of
@@ -90,6 +90,9 @@  static void init_offset_limit (void);
    - Its value in object_sizes is an expression that evaluates to the size.  */
 static vec<tree> object_sizes[OST_END];
 
+/* Whole size expressions are only needed for dynamic object sizes.  */
+static vec<tree> object_whole_sizes[OST_DYNAMIC];
+
 /* Bitmaps what object sizes have been computed already.  */
 static bitmap computed[OST_END];
 
@@ -149,21 +152,35 @@  size_unknown (int object_size_type)
   return size_int (unknown (object_size_type));
 }
 
-/* Grow object_sizes[OBJECT_SIZE_TYPE] to num_ssa_names.  */
+/* Grow object_sizes[OBJECT_SIZE_TYPE] and if WHOLE is true,
+   object_whole_sizes[OBJECT_SIZE_TYPE]  to num_ssa_names.  */
 
 static inline void
-object_sizes_grow (int object_size_type)
+object_sizes_grow (int object_size_type, bool whole = false)
 {
   if (num_ssa_names > object_sizes[object_size_type].length ())
     object_sizes[object_size_type].safe_grow (num_ssa_names, true);
+  if (whole)
+    {
+      gcc_assert (object_size_type & OST_DYNAMIC);
+      object_size_type &= ~OST_DYNAMIC;
+      if (num_ssa_names > object_whole_sizes[object_size_type].length ())
+	object_whole_sizes[object_size_type].safe_grow (num_ssa_names, true);
+    }
 }
 
 /* Release object_sizes[OBJECT_SIZE_TYPE].  */
 
 static inline void
-object_sizes_release (int object_size_type)
+object_sizes_release (int object_size_type, bool whole = false)
 {
   object_sizes[object_size_type].release ();
+  if (whole)
+    {
+      gcc_assert (object_size_type & OST_DYNAMIC);
+      object_size_type &= ~OST_DYNAMIC;
+      object_whole_sizes[object_size_type].release ();
+    }
 }
 
 /* Return true if object_sizes[OBJECT_SIZE_TYPE][VARNO] is unknown.  */
@@ -178,8 +195,16 @@  object_sizes_unknown_p (int object_size_type, unsigned varno)
 /* Return size for VARNO corresponding to OSI.  */
 
 static inline tree
-object_sizes_get (struct object_size_info *osi, unsigned varno)
+object_sizes_get (struct object_size_info *osi, unsigned varno,
+		  bool whole = false)
 {
+  if (whole)
+    {
+      int object_size_type = osi->object_size_type;
+      gcc_assert (object_size_type & OST_DYNAMIC);
+      object_size_type &= ~OST_DYNAMIC;
+      return object_whole_sizes[object_size_type][varno];
+    }
   return object_sizes[osi->object_size_type][varno];
 }
 
@@ -187,22 +212,38 @@  object_sizes_get (struct object_size_info *osi, unsigned varno)
 
 static inline void
 object_sizes_initialize (struct object_size_info *osi, unsigned varno,
-			 tree val = NULL_TREE)
+			 tree val, bool whole = false)
 {
   int object_size_type = osi->object_size_type;
 
-  if (!val)
-    val = size_initval (object_size_type);
   object_sizes[object_size_type][varno] = val;
+  if (whole)
+    {
+      object_size_type = osi->object_size_type;
+      gcc_assert (object_size_type & OST_DYNAMIC);
+      object_size_type &= ~OST_DYNAMIC;
+      object_whole_sizes[object_size_type][varno] = val;
+    }
 }
 
 /* Set size for VARNO corresponding to OSI to VAL if it is the new minimum or
    maximum.  */
 
 static inline void
-object_sizes_set (struct object_size_info *osi, unsigned varno, tree val)
+object_sizes_set (struct object_size_info *osi, unsigned varno, tree val,
+		  bool whole = false)
 {
   int object_size_type = osi->object_size_type;
+
+  if (whole)
+    {
+      int object_size_type = osi->object_size_type;
+      gcc_assert (object_size_type & OST_DYNAMIC);
+      object_size_type &= ~OST_DYNAMIC;
+      object_whole_sizes[object_size_type][varno] = val;
+      return;
+    }
+
   tree curval = object_sizes[object_size_type][varno];
 
   /* Object size is set at most twice, once to put in an SSA name to resolve
@@ -239,11 +280,21 @@  init_offset_limit (void)
   offset_limit /= 2;
 }
 
-/* Bytes at end of the object with SZ from offset OFFSET. */
+/* Bytes at end of the object with SZ from offset OFFSET.  If WHOLESIZE is
+   present, use it to allow negative OFFSET to the extent that it does not
+   underflow WHOLESIZE.  */
 
 static tree
-size_for_offset (tree sz, tree offset)
+size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE)
 {
+  if (wholesize)
+    {
+      offset = size_binop (PLUS_EXPR, size_binop (MAX_EXPR, wholesize, sz),
+			   offset);
+      offset = size_binop (MINUS_EXPR, offset, sz);
+      sz = wholesize;
+    }
+
   return size_binop (MINUS_EXPR, size_binop (MAX_EXPR, sz, offset), offset);
 }
 
@@ -360,7 +411,7 @@  decl_init_size (tree decl, bool min)
 
 static bool
 addr_object_size (struct object_size_info *osi, const_tree ptr,
-		  int object_size_type, tree *psize)
+		  int object_size_type, tree *psize, tree *wholesize)
 {
   tree pt_var, pt_var_size = NULL_TREE, var_size, bytes;
 
@@ -369,6 +420,8 @@  addr_object_size (struct object_size_info *osi, const_tree ptr,
   /* Set to unknown and overwrite just before returning if the size
      could be determined.  */
   *psize = size_unknown (object_size_type);
+  if (wholesize)
+    *wholesize = size_unknown (object_size_type);
 
   pt_var = TREE_OPERAND (ptr, 0);
   while (handled_component_p (pt_var))
@@ -391,21 +444,13 @@  addr_object_size (struct object_size_info *osi, const_tree ptr,
       else
 	{
 	  tree var = TREE_OPERAND (pt_var, 0);
-	  collect_object_sizes_for (osi, var);
-	  if (bitmap_bit_p (computed[object_size_type],
-			    SSA_NAME_VERSION (var)))
-	    sz = object_sizes_get (osi, SSA_NAME_VERSION (var));
-	  else
+	  sz = collect_object_sizes_for (osi, var);
+	  if (!bitmap_bit_p (computed[object_size_type],
+			     SSA_NAME_VERSION (var)))
 	    sz = size_unknown (object_size_type);
 	}
       if (!size_unknown_p (sz, object_size_type))
-	{
-	  tree offset = TREE_OPERAND (pt_var, 1);
-	  if (TREE_CODE (offset) != INTEGER_CST)
-	    sz = size_unknown (object_size_type);
-	  else
-	    sz = size_for_offset (sz, offset);
-	}
+	sz = size_for_offset (sz, TREE_OPERAND (pt_var, 1));
 
       if (!size_unknown_p (sz, object_size_type)
 	  && (TREE_CODE (sz) != INTEGER_CST
@@ -570,6 +615,8 @@  addr_object_size (struct object_size_info *osi, const_tree ptr,
 
   if (size_known_p (bytes, object_size_type))
     {
+      if ((object_size_type & OST_DYNAMIC) && wholesize)
+	*wholesize = object_size_type & OST_SUBOBJECT ? bytes : pt_var_size;
       *psize = bytes;
       return true;
     }
@@ -766,6 +813,8 @@  estimate_size (object_size_info *osi, tree size, bitmap *visitlog = NULL)
 	    }
 	  return size_binop (code, ret, off);
 	}
+    case COMPOUND_EXPR:
+      return estimate_size (osi, TREE_OPERAND (size, 1), visitlog);
     case INTEGER_CST:
     default:
       return size;
@@ -943,6 +992,13 @@  gimplify_size_expressions (object_size_info *osi)
 	      if (stmt)
 		{
 		  gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
+		  remove_phi_node (&gsi, false);
+
+		  /* Also remove the whole size SSA.  */
+		  stmt = SSA_NAME_DEF_STMT (object_sizes_get (osi, varno,
+							      true));
+		  gcc_checking_assert (stmt);
+		  gsi = gsi_for_stmt (stmt);
 		  remove_phi_node (&gsi, true);
 		}
 	      bitmap_set_bit (tempsize_free, i);
@@ -1043,7 +1099,7 @@  compute_builtin_object_size (tree ptr, int object_size_type,
       bitmap_iterator bi;
       unsigned int i;
 
-      object_sizes_grow (object_size_type);
+      object_sizes_grow (object_size_type, object_size_type & OST_DYNAMIC);
       if (dump_file)
 	{
 	  fprintf (dump_file, "Computing %s %s%sobject size for ",
@@ -1142,10 +1198,52 @@  make_or_get_tempsize (struct object_size_info *osi, unsigned varno)
   return ssa;
 }
 
+static tree
+bundle_whole_size (struct object_size_info *osi, tree var, tree res,
+		   tree *wholesize)
+{
+  int object_size_type = osi->object_size_type;
+
+  if (!(object_size_type & OST_DYNAMIC)
+      || size_unknown_p (res, object_size_type))
+    return res;
+
+  tree cur = NULL_TREE;
+
+  /* Get the temp SSA name if we created one for a dependency loop.  */
+  if (TREE_CODE (var) == SSA_NAME)
+    cur = object_sizes_get (osi, SSA_NAME_VERSION (var), true);
+
+  /* If we need to gimplify WHOLERES, fold it into a compound expression
+     along with RES so that their statements are emitted together.  */
+  if (cur && TREE_CODE (cur) == SSA_NAME)
+    {
+      unsigned wno = SSA_NAME_VERSION (cur);
+      if (bitmap_bit_p (osi->reexamine, wno))
+	bitmap_clear_bit (osi->reexamine, wno);
+      else
+	cur = NULL_TREE;
+    }
+  else if (!is_gimple_variable (*wholesize)
+	   && TREE_CODE (*wholesize) != INTEGER_CST)
+    cur = make_ssa_name (sizetype);
+  else
+    cur = NULL_TREE;
+
+  if (cur)
+    {
+      res = size_binop (COMPOUND_EXPR,
+			size_binop (MODIFY_EXPR, cur, *wholesize),
+			res);
+      *wholesize = cur;
+    }
+  return res;
+}
+
 /* Compute object_sizes for PTR, defined to VALUE, which is not an SSA_NAME.  */
 
 static tree
-expr_object_size (struct object_size_info *osi, tree value)
+expr_object_size (struct object_size_info *osi, tree value, tree *wholesize)
 {
   int object_size_type = osi->object_size_type;
   tree bytes;
@@ -1153,14 +1251,17 @@  expr_object_size (struct object_size_info *osi, tree value)
   if (TREE_CODE (value) == WITH_SIZE_EXPR)
     value = TREE_OPERAND (value, 0);
 
-  /* Pointer variables should have been handled by ssa_object_size.  */
+  /* Pointer variables should have been handled by
+     collect_object_sizes_for.  */
   gcc_assert (TREE_CODE (value) != SSA_NAME
 	      || !POINTER_TYPE_P (TREE_TYPE (value)));
 
   if (TREE_CODE (value) == ADDR_EXPR
-      && addr_object_size (osi, value, object_size_type, &bytes))
-    return bytes;
+      && addr_object_size (osi, value, object_size_type, &bytes, wholesize))
+    return bundle_whole_size (osi, value, bytes, wholesize);
 
+  if (wholesize)
+    *wholesize = size_unknown (object_size_type);
   return size_unknown (object_size_type);
 }
 
@@ -1168,16 +1269,20 @@  expr_object_size (struct object_size_info *osi, tree value)
 /* Compute object_sizes for PTR, defined to the result of a call.  */
 
 static tree
-call_object_size (struct object_size_info *osi, gcall *call)
+call_object_size (struct object_size_info *osi, tree var, tree *wholesize)
 {
   int object_size_type = osi->object_size_type;
+  gcall *call = as_a <gcall *> (SSA_NAME_DEF_STMT (var));
 
   gcc_assert (is_gimple_call (call));
 
   tree bytes = alloc_object_size (call, object_size_type);
 
+  if (wholesize)
+    *wholesize = bytes;
+
   if (size_known_p (bytes, object_size_type))
-    return bytes;
+    return bundle_whole_size (osi, var, bytes, wholesize);
 
   return  size_unknown (object_size_type);
 }
@@ -1186,44 +1291,25 @@  call_object_size (struct object_size_info *osi, gcall *call)
 /* Compute object_sizes for PTR, defined to an unknown value.  */
 
 static tree
-unknown_object_size (struct object_size_info *osi)
+unknown_object_size (struct object_size_info *osi, tree *wholesize)
 {
   int object_size_type = osi->object_size_type;
+  if (wholesize)
+    *wholesize = size_unknown (object_size_type);
   return size_unknown (object_size_type);
 }
 
 
-/* Return object size of ORIG + OFFSET.  */
-
-static tree
-ssa_object_size (struct object_size_info *osi, tree orig, tree offset)
-{
-  int object_size_type = osi->object_size_type;
-  tree orig_bytes;
-
-  if (compare_tree_int (offset, offset_limit) >= 0)
-    return size_unknown (object_size_type);
-
-  collect_object_sizes_for (osi, orig);
-
-  orig_bytes = object_sizes_get (osi, SSA_NAME_VERSION (orig));
-  if (!size_unknown_p (orig_bytes, object_size_type)
-      && !integer_zerop (offset))
-    orig_bytes = size_for_offset (orig_bytes, offset);
-
-  return orig_bytes;
-}
-
-
 /* Compute object_sizes for VAR, defined to the result of an assignment
    with operator POINTER_PLUS_EXPR.  */
 
 static tree
-plus_stmt_object_size (struct object_size_info *osi, gimple *stmt)
+plus_stmt_object_size (struct object_size_info *osi, tree var, tree *wholesize)
 {
   int object_size_type = osi->object_size_type;
   tree bytes;
   tree op0, op1;
+  gimple *stmt = SSA_NAME_DEF_STMT (var);
 
   if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
     {
@@ -1240,29 +1326,24 @@  plus_stmt_object_size (struct object_size_info *osi, gimple *stmt)
   else
     gcc_unreachable ();
 
-  /* Handle PTR + OFFSET here.  */
-  if (TREE_CODE (op1) == INTEGER_CST
-      && (TREE_CODE (op0) == SSA_NAME
-	  || TREE_CODE (op0) == ADDR_EXPR))
-    {
-      if (TREE_CODE (op0) == SSA_NAME)
-	bytes = ssa_object_size (osi, op0, op1);
-      else
-	{
-          /* op0 will be ADDR_EXPR here.  */
-	  addr_object_size (osi, op0, object_size_type, &bytes);
-	  if (size_unknown_p (bytes, object_size_type))
-	    ;
-	  else if (compare_tree_int (op1, offset_limit) > 0)
-	    bytes = size_unknown (object_size_type);
-	  else if (!integer_zerop (op1))
-	    bytes = size_for_offset (bytes, op1);
-	}
-    }
+  if (!(object_size_type & OST_DYNAMIC)
+      && (TREE_CODE (op1) != INTEGER_CST
+	  || compare_tree_int (op1, offset_limit) > 0))
+    return size_unknown (object_size_type);
+
+  if (TREE_CODE (op0) == SSA_NAME)
+    bytes = collect_object_sizes_for (osi, op0, wholesize);
+  else if (TREE_CODE (op0) == ADDR_EXPR)
+    addr_object_size (osi, op0, object_size_type, &bytes, wholesize);
   else
     bytes = size_unknown (object_size_type);
 
-  return bytes;
+  if (!size_unknown_p (bytes, object_size_type))
+    bytes = size_for_offset (bytes, op1, wholesize ? *wholesize : NULL_TREE);
+  else if (wholesize)
+    *wholesize = size_unknown (object_size_type);
+
+  return bundle_whole_size (osi, var, bytes, wholesize);
 }
 
 
@@ -1270,34 +1351,48 @@  plus_stmt_object_size (struct object_size_info *osi, gimple *stmt)
    a COND_EXPR.  */
 
 static tree
-cond_expr_object_size (struct object_size_info *osi, gimple *stmt)
+cond_expr_object_size (struct object_size_info *osi, tree var, tree *wholesize)
 {
   tree then_, else_;
   int object_size_type = osi->object_size_type;
-  tree thenbytes, elsebytes;
+  tree thenbytes, elsebytes, thenwhole, elsewhole;
+  gimple *stmt = SSA_NAME_DEF_STMT (var);
 
   gcc_assert (gimple_assign_rhs_code (stmt) == COND_EXPR);
 
+  if (!(object_size_type & OST_DYNAMIC))
+    wholesize = NULL;
+
   then_ = gimple_assign_rhs2 (stmt);
   else_ = gimple_assign_rhs3 (stmt);
 
   if (TREE_CODE (then_) == SSA_NAME)
-    thenbytes = ssa_object_size (osi, then_, size_int (0));
+    thenbytes = collect_object_sizes_for (osi, then_, wholesize);
   else
-    thenbytes = expr_object_size (osi, then_);
+    thenbytes = expr_object_size (osi, then_, wholesize);
+
+  thenwhole = wholesize ? *wholesize : NULL_TREE;
 
   if (TREE_CODE (else_) == SSA_NAME)
-    elsebytes = ssa_object_size (osi, else_, size_int (0));
+    elsebytes = collect_object_sizes_for (osi, else_, wholesize);
   else
-    elsebytes = expr_object_size (osi, else_);
+    elsebytes = expr_object_size (osi, else_, wholesize);
+
+  elsewhole = wholesize ? *wholesize : NULL_TREE;
 
   if (size_unknown_p (thenbytes, object_size_type)
       || size_unknown_p (elsebytes, object_size_type))
     return size_unknown (object_size_type);
 
   if (object_size_type & OST_DYNAMIC)
-    return fold_build3 (COND_EXPR, sizetype, gimple_assign_rhs1 (stmt),
-			thenbytes, elsebytes);
+    {
+      gcc_checking_assert (elsewhole && thenwhole);
+      *wholesize = fold_build3 (COND_EXPR, sizetype, gimple_assign_rhs1 (stmt),
+				thenwhole, elsewhole);
+      tree res = fold_build3 (COND_EXPR, sizetype, gimple_assign_rhs1 (stmt),
+			      thenbytes, elsebytes);
+      return bundle_whole_size (osi, var, res, wholesize);
+    }
 
   return size_binop (OST_TREE_CODE (object_size_type), thenbytes, elsebytes);
 }
@@ -1316,9 +1411,9 @@  phi_object_size (struct object_size_info *osi, gimple *stmt)
       tree phires;
 
       if (TREE_CODE (rhs) == SSA_NAME)
-	phires = ssa_object_size (osi, rhs, size_int (0));
+	phires = collect_object_sizes_for (osi, rhs);
       else
-	phires = expr_object_size (osi, rhs);
+	phires = expr_object_size (osi, rhs, NULL);
 
       res = size_binop (OST_TREE_CODE (object_size_type), res, phires);
 
@@ -1329,42 +1424,69 @@  phi_object_size (struct object_size_info *osi, gimple *stmt)
 }
 
 static tree
-phi_dynamic_object_size (struct object_size_info *osi, tree var)
+phi_dynamic_object_size (struct object_size_info *osi, tree var,
+			 tree *wholesize)
 {
   int object_size_type = osi->object_size_type;
   unsigned int varno = SSA_NAME_VERSION (var);
   gimple *stmt = SSA_NAME_DEF_STMT (var);
   unsigned i, num_args = gimple_phi_num_args (stmt);
-  tree res;
+  tree res, wholeres;
 
-  vec<tree> sizes;
+  vec<tree> sizes, wholesizes;
   sizes.create (0);
+  wholesizes.create (0);
   sizes.safe_grow (num_args);
+  wholesizes.safe_grow (num_args);
 
   /* Bail out if the size of any of the PHI arguments cannot be
      determined.  */
   for (i = 0; i < num_args; i++)
     {
       tree rhs = gimple_phi_arg_def (stmt, i);
-      tree sz;
+      tree sz, cur_whole;
 
       if (TREE_CODE (rhs) != SSA_NAME)
-	sz = expr_object_size (osi, rhs);
+	sz = expr_object_size (osi, rhs, &cur_whole);
       else
-	sz = ssa_object_size (osi, rhs, size_int (0));
+	sz = collect_object_sizes_for (osi, rhs, &cur_whole);
 
-      if (size_unknown_p (sz, object_size_type))
+      if (size_unknown_p (sz, object_size_type)
+	  || size_unknown_p (cur_whole, object_size_type))
 	break;
 
+      if (!is_gimple_variable (cur_whole)
+	  && TREE_CODE (cur_whole) != INTEGER_CST)
+	{
+	  tree tmp = make_ssa_name (sizetype);
+	  tmp = size_binop (MODIFY_EXPR, tmp, cur_whole);
+	  sz = size_binop (COMPOUND_EXPR, tmp, sz);
+	  cur_whole = tmp;
+	}
       sizes[i] = sz;
+      wholesizes[i] = cur_whole;
     }
 
   if (i == num_args)
     {
       res = make_or_get_tempsize (osi, varno);
+      wholeres = object_sizes_get (osi, varno, true);
+      if (TREE_CODE (wholeres) != SSA_NAME)
+	{
+	  gcc_checking_assert (TREE_CODE (wholeres) == INTEGER_CST);
+	  gcc_checking_assert (compare_tree_int (wholeres,
+						 initval (object_size_type))
+			       == 0);
+	  wholeres = make_ssa_name (sizetype);
+	}
+      else
+	/* We don't reexamine whole sizes on their own.  */
+	bitmap_clear_bit (osi->reexamine, SSA_NAME_VERSION (wholeres));
+
       bitmap_set_bit (osi->phiresults, SSA_NAME_VERSION (res));
       object_sizes_initialize (osi, SSA_NAME_VERSION (res), res);
 
+      gphi *wholephi = create_phi_node (wholeres, gimple_bb (stmt));
       gphi *phi = create_phi_node (res, gimple_bb (stmt));
       gphi *obj_phi =  as_a <gphi *> (stmt);
 
@@ -1377,6 +1499,11 @@  phi_dynamic_object_size (struct object_size_info *osi, tree var)
 	      sizes[i] = ssa;
 	    }
 
+	  /* We're guaranteed to get gimple variables for whole sizes through
+	     expr_object_size and collect_object_sizes_for.  */
+	  add_phi_arg (wholephi, wholesizes[i],
+		       gimple_phi_arg_edge (obj_phi, i),
+		       gimple_phi_arg_location (obj_phi, i));
 	  add_phi_arg (phi, sizes[i],
 		       gimple_phi_arg_edge (obj_phi, i),
 		       gimple_phi_arg_location (obj_phi, i));
@@ -1387,20 +1514,26 @@  phi_dynamic_object_size (struct object_size_info *osi, tree var)
 	  print_generic_expr (dump_file, var, dump_flags);
 	  fprintf (dump_file, ": PHI Node with result: ");
 	  print_gimple_stmt (dump_file, phi, dump_flags);
+	  fprintf (dump_file, " and wholephi: ");
+	  print_gimple_stmt (dump_file, wholephi, dump_flags);
 	}
+      *wholesize = wholeres;
     }
   else
-    res = size_unknown (object_size_type);
+    {
+      res = size_unknown (object_size_type);
+      *wholesize = size_unknown (object_size_type);
+    }
 
   sizes.release ();
-
+  wholesizes.release ();
   return res;
 }
 
 /* Find size of an object passed as a parameter to the function.  */
 
 static tree
-parm_object_size (struct object_size_info *osi, tree var)
+parm_object_size (struct object_size_info *osi, tree var, tree *wholesize)
 {
   int object_size_type = osi->object_size_type;
   tree parm = SSA_NAME_VAR (var);
@@ -1414,7 +1547,7 @@  parm_object_size (struct object_size_info *osi, tree var)
     }
 
   if (!(object_size_type & OST_DYNAMIC) || !POINTER_TYPE_P (TREE_TYPE (parm)))
-    return expr_object_size (osi, parm);
+    return expr_object_size (osi, parm, wholesize);
 
   /* Look for access attribute.  */
   rdwr_map rdwr_idx;
@@ -1422,6 +1555,7 @@  parm_object_size (struct object_size_info *osi, tree var)
   tree fndecl = cfun->decl;
   const attr_access *access = get_parm_access (rdwr_idx, parm, fndecl);
   tree typesize = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (parm)));
+  tree sz = NULL_TREE;
 
   if (access && access->sizarg != UINT_MAX)
     {
@@ -1436,17 +1570,21 @@  parm_object_size (struct object_size_info *osi, tree var)
 
       if (arg != NULL_TREE && INTEGRAL_TYPE_P (TREE_TYPE (arg)))
 	{
-	  tree sz = get_or_create_ssa_default_def (cfun, arg);
+	  sz = get_or_create_ssa_default_def (cfun, arg);
 	  if (sz != NULL_TREE)
 	    {
 	      sz = fold_convert (sizetype, sz);
 	      if (typesize)
 		sz = size_binop (MULT_EXPR, sz, typesize);
-	      return sz;
 	    }
 	}
     }
-  return size_unknown (object_size_type);
+  if (sz == NULL_TREE)
+    sz = size_unknown (object_size_type);
+
+  if (wholesize)
+    *wholesize = sz;
+  return bundle_whole_size (osi, var, sz, wholesize);
 }
 
 /* Compute object sizes for VAR.
@@ -1469,23 +1607,32 @@  parm_object_size (struct object_size_info *osi, tree var)
    Otherwise, object size is the maximum of object sizes of variables
    that it might be set to.  */
 
-static void
-collect_object_sizes_for (struct object_size_info *osi, tree var)
+static tree
+collect_object_sizes_for (struct object_size_info *osi, tree var,
+			  tree *wholesize)
 {
   int object_size_type = osi->object_size_type;
   unsigned int varno = SSA_NAME_VERSION (var);
+  tree res, wholeres = NULL_TREE;
   gimple *stmt;
-  tree res;
+
+  if ((object_size_type & OST_DYNAMIC) && !wholesize)
+    wholesize = &wholeres;
 
   if (bitmap_bit_p (computed[object_size_type], varno))
-    return;
+    {
+      if (wholesize)
+	*wholesize = object_sizes_get (osi, varno, true);
+      return object_sizes_get (osi, varno);
+    }
 
   /* We want to evaluate the self-referencing object only once.  */
   if (bitmap_set_bit (osi->visited, varno))
     {
       /* Initialize to 0 for maximum size and M1U for minimum size so that
 	 it gets immediately overridden.  */
-      object_sizes_initialize (osi, varno);
+      object_sizes_initialize (osi, varno, size_initval (object_size_type),
+			       object_size_type & OST_DYNAMIC);
     }
   else
     {
@@ -1497,6 +1644,19 @@  collect_object_sizes_for (struct object_size_info *osi, tree var)
 	  fprintf (dump_file, "\n");
 	}
       res = make_or_get_tempsize (osi, varno);
+      if (wholesize)
+	{
+	  tree tmp = object_sizes_get (osi, varno, true);
+	  if (TREE_CODE (tmp) == INTEGER_CST)
+	    *wholesize = make_tempsize (osi, varno);
+	  else
+	    {
+	      gcc_assert (TREE_CODE (res) == SSA_NAME
+			  && bitmap_bit_p (osi->reexamine,
+					   SSA_NAME_VERSION (tmp)));
+	      *wholesize = tmp;
+	    }
+	}
       goto out;
     }
 
@@ -1517,58 +1677,57 @@  collect_object_sizes_for (struct object_size_info *osi, tree var)
 	if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR
 	    || (gimple_assign_rhs_code (stmt) == ADDR_EXPR
 		&& TREE_CODE (TREE_OPERAND (rhs, 0)) == MEM_REF))
-	  res = plus_stmt_object_size (osi, stmt);
+	  res = plus_stmt_object_size (osi, var, wholesize);
 	else if (gimple_assign_rhs_code (stmt) == COND_EXPR)
-	  res = cond_expr_object_size (osi, stmt);
+	  res = cond_expr_object_size (osi, var, wholesize);
 	else if (gimple_assign_single_p (stmt)
 		 || gimple_assign_unary_nop_p (stmt))
 	  {
 	    if (TREE_CODE (rhs) == SSA_NAME
 		&& POINTER_TYPE_P (TREE_TYPE (rhs)))
-	      res = ssa_object_size (osi, rhs, size_int (0));
+	      res = collect_object_sizes_for (osi, rhs, wholesize);
 	    else
-	       res = expr_object_size (osi, rhs);
+	       res = expr_object_size (osi, rhs, wholesize);
 	  }
 	else
-	  res = unknown_object_size (osi);
+	  res = unknown_object_size (osi, wholesize);
 	break;
       }
 
     case GIMPLE_CALL:
       {
-	gcall *call_stmt = as_a <gcall *> (stmt);
-	tree arg = pass_through_call (call_stmt);
+	tree arg = pass_through_call (as_a <gcall *> (stmt));
 	if (arg)
 	  {
 	    if (TREE_CODE (arg) == SSA_NAME
 		&& POINTER_TYPE_P (TREE_TYPE (arg)))
-	      res = ssa_object_size (osi, arg, size_int (0));
+	      res = collect_object_sizes_for (osi, arg, wholesize);
 	    else
-	      res = expr_object_size (osi, arg);
+	      res = expr_object_size (osi, arg, wholesize);
 	  }
 	else
-	  res = call_object_size (osi, call_stmt);
+	  res = call_object_size (osi, var, wholesize);
 	break;
       }
 
     case GIMPLE_ASM:
       /* Pointers defined by __asm__ statements can point anywhere.  */
-      res = size_unknown (object_size_type);
+      res = unknown_object_size (osi, wholesize);
       break;
 
     case GIMPLE_NOP:
       if (SSA_NAME_VAR (var)
 	  && TREE_CODE (SSA_NAME_VAR (var)) == PARM_DECL)
-	res = parm_object_size (osi, var);
+	res = parm_object_size (osi, var, wholesize);
       else
 	/* Uninitialized SSA names point nowhere.  */
-	res = size_unknown (object_size_type);
+	res = unknown_object_size (osi, wholesize);
       break;
 
     case GIMPLE_PHI:
       {
 	if (object_size_type & OST_DYNAMIC)
-	  res = phi_dynamic_object_size (osi, var);
+	  res = phi_dynamic_object_size (osi, var, wholesize);
 	else
 	  res = phi_object_size (osi, stmt);
 	break;
@@ -1578,16 +1737,26 @@  collect_object_sizes_for (struct object_size_info *osi, tree var)
       gcc_unreachable ();
     }
 
-  if ((object_size_type & OST_DYNAMIC)
-      && TREE_CODE (res) != INTEGER_CST && !is_gimple_variable (res))
+  res = bundle_whole_size (osi, var, res, wholesize);
+
+  if (wholesize)
+    gcc_checking_assert (TREE_CODE (*wholesize) == INTEGER_CST
+			 || is_gimple_variable (*wholesize));
+
+  if ((object_size_type & OST_DYNAMIC) && TREE_CODE (res) != INTEGER_CST
+      && !is_gimple_variable (res))
     {
       tree ssa = make_or_get_tempsize (osi, varno);
       object_sizes_initialize (osi, SSA_NAME_VERSION (ssa), res);
       res = ssa;
     }
+
   bitmap_set_bit (computed[object_size_type], varno);
 out:
   object_sizes_set (osi, varno, res);
+  if (object_size_type & OST_DYNAMIC)
+    object_sizes_set (osi, varno, *wholesize, true);
+  return res;
 }
 
 
@@ -1603,7 +1772,7 @@  init_object_sizes (void)
 
   for (object_size_type = 0; object_size_type < OST_END; object_size_type++)
     {
-      object_sizes_grow (object_size_type);
+      object_sizes_grow (object_size_type, object_size_type >= OST_DYNAMIC);
       computed[object_size_type] = BITMAP_ALLOC (NULL);
     }
 
@@ -1620,7 +1789,7 @@  fini_object_sizes (void)
 
   for (object_size_type = 0; object_size_type < OST_END; object_size_type++)
     {
-      object_sizes_release (object_size_type);
+      object_sizes_release (object_size_type, object_size_type >= OST_DYNAMIC);
       BITMAP_FREE (computed[object_size_type]);
     }
 }