public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH 00/10] __builtin_dynamic_object_size
@ 2021-11-09 19:01 Siddhesh Poyarekar
  2021-11-09 19:01 ` [PATCH 01/10] tree-object-size: Replace magic numbers with enums Siddhesh Poyarekar
                   ` (14 more replies)
  0 siblings, 15 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-09 19:01 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub


This patchset implements the __builtin_dynamic_object_size builtin for
gcc.  The primary motivation to have this builtin in gcc is to enable
_FORTIFY_SOURCE=3 support with gcc, thus allowing greater fortification
in use cases where the potential performance tradeoff is acceptable.

Semantics:
----------

__builtin_dynamic_object_size has the same signature as
__builtin_object_size; it accepts a pointer and type ranging from 0 to 3
and it returns an object size estimate for the pointer based on an
analysis of which objects the pointer could point to.  The actual
properties of the object size estimate are different:

- In the best case __builtin_dynamic_object_size evaluates to an
  expression that represents a precise size of the object being pointed
  to.

- In case a precise object size expression cannot be evaluated,
  __builtin_dynamic_object_size attempts to evaluate an estimate size
  expression based on the object size type.

- In what situations the builtin returns an estimate vs a precise
  expression is an implementation detail and may change in future.
  Users must always assume, as in the case of __builtin_object_size, that
  the returned value is the maximum or minimum based on the object size
  type they have provided.

- In the worst case of failure, __builtin_dynamic_object_size returns a
  constant (size_t)-1 or (size_t)0.

Implementation:
---------------

- The __builtin_dynamic_object_size support is implemented in
  tree-object-size.  In most cases the first pass (early_objsz) is able
  to evaluate object size expressions.  The second phase mainly ends up
  simplifying the __builtin_dynamic_object_size to
  __builtin_object_size.

- The patchset begins with structural modification of the
  tree-object-size pass, followed by enhancement to return size
  expressions.  I have split the implementation into one feature per
  patch (calls, function parameters, PHI, etc.) to hopefully ease
  review.

Performance:
------------

Expressions generated by this pass in theory could be arbitrarily
complex.  I have not made an attempt to limit nesting of objects since
it seemed too early to do that.  In practice based on the few
applications I built, most of the complexity of the expressions got
folded away.  Even so, the performance overhead is likely to be
non-zero.  If we find performance degradation to be significant, we
could later add nesting limits to bail out if a size expression gets too
complex.

I have also not implemented simplification of __*_chk to their normal
variants if we can determine at compile time that it is safe, which
still depends on the object size to be constant.  I hope to do this as a
minor performance improvement in stage 3.

Build time performance doesn't seem to be affected much based on an
unscientific check to time
`make check-gcc RUNTESTFLAGS="dg.exp=builtin*"`.  It only increases by
about a couple of seconds when the dynamic tests are added and remains
more or less in the same ballpark otherwise.

Testing:
--------

I have added tests for dynamic object sizes as well as wrappers for all
__builtin_object_size tests to provide wide coverage.  With that in
place I have run full bootstrap builds on Fedora rawhide by backporting the
patches to the gcc11 branch and x86_64 and i686 have no failures in any
of the builtin-*object-size* tests and no new failures.

I have also built bash, cmake, zfs-fuse and systemtap with
_FORTIFY_SOURCE=3 (with a hacked up glibc to make sure it works) and saw
no issues in any of those builds.  I did some rudimentary analysis of
the generated binaries to see if there was any difference in coverage
and found that there was.  In terms of pure numbers, there were far more
_chk variants of calls than the regular ones due to _FORTIFY_SOURCE
(from about 4% to 70% in bash), but that could well be due to the _chk
variants not being folded into regular variants when safe.  However, on
manual inspection of some of these sites, it was clear that coverage was
increasing significantly where __builtin_object_size was previously
bailing out.

Specifically for bash, the coverage went from 30.81% to 86.87% (it was
84.5% with the v1 patch).  I actually hope to reduce this a bit with
folding improvements for __builtin___memcpy_chk, etc.

A bootstrap build is in progress on x86_64.

Limitations/Future work:
------------------------

- The most immediate work is to fold _chk variants of builtins into
  regular ones when it can be proven at compile time that the object
  size will alwasy be less than the length of the write.  I am working
  on it right now.

- I need to enable _FORTIFY_SOURCE=3 for gcc in glibc; currently it is
  llvm-only.  I've started working on these patches too on the side.

- Instead of bailing out on non-constant sizes with
  __builtin_object_size, it should be possible to use ranger to
  get an upper and lower bound on the size expression and use that to
  implement __builtin_object_size.

- More work could to be done to reduce the performance impact of the
  computation.  One way could be to add a heuristic where the pass keeps
  track of nesting in the expression and either bail out or compute an
  estimate if nesting crosses a threshold.  I'll take this up once we
  have more data on the nature of the bottlenecks.


Siddhesh Poyarekar (10):
  tree-object-size: Replace magic numbers with enums
  tree-object-size: Abstract object_sizes array
  tree-object-size: Use tree instead of HOST_WIDE_INT
  tree-object-size: Single pass dependency loop resolution
  __builtin_dynamic_object_size: Recognize builtin
  tree-object-size: Support dynamic sizes in conditions
  tree-object-size: Handle function parameters
  tree-object-size: Handle GIMPLE_CALL
  tree-object-size: Dynamic sizes for ADDR_EXPR
  tree-object-size: Handle dynamic offsets

 gcc/builtins.c                                |   22 +-
 gcc/builtins.def                              |    1 +
 gcc/doc/extend.texi                           |   13 +
 gcc/gimple-fold.c                             |    9 +-
 .../g++.dg/ext/builtin-dynamic-object-size1.C |    5 +
 .../g++.dg/ext/builtin-dynamic-object-size2.C |    5 +
 .../gcc.dg/builtin-dynamic-alloc-size.c       |    7 +
 .../gcc.dg/builtin-dynamic-object-size-0.c    |  463 +++++
 .../gcc.dg/builtin-dynamic-object-size-1.c    |    7 +
 .../gcc.dg/builtin-dynamic-object-size-10.c   |    9 +
 .../gcc.dg/builtin-dynamic-object-size-11.c   |    7 +
 .../gcc.dg/builtin-dynamic-object-size-12.c   |    5 +
 .../gcc.dg/builtin-dynamic-object-size-13.c   |    5 +
 .../gcc.dg/builtin-dynamic-object-size-14.c   |    5 +
 .../gcc.dg/builtin-dynamic-object-size-15.c   |    5 +
 .../gcc.dg/builtin-dynamic-object-size-16.c   |    7 +
 .../gcc.dg/builtin-dynamic-object-size-17.c   |    8 +
 .../gcc.dg/builtin-dynamic-object-size-18.c   |    8 +
 .../gcc.dg/builtin-dynamic-object-size-19.c   |  104 +
 .../gcc.dg/builtin-dynamic-object-size-2.c    |    7 +
 .../gcc.dg/builtin-dynamic-object-size-3.c    |    7 +
 .../gcc.dg/builtin-dynamic-object-size-4.c    |    7 +
 .../gcc.dg/builtin-dynamic-object-size-5.c    |    8 +
 .../gcc.dg/builtin-dynamic-object-size-6.c    |    5 +
 .../gcc.dg/builtin-dynamic-object-size-7.c    |    5 +
 .../gcc.dg/builtin-dynamic-object-size-8.c    |    5 +
 .../gcc.dg/builtin-dynamic-object-size-9.c    |    5 +
 gcc/testsuite/gcc.dg/builtin-object-size-1.c  |  160 +-
 gcc/testsuite/gcc.dg/builtin-object-size-16.c |    2 +
 gcc/testsuite/gcc.dg/builtin-object-size-17.c |    2 +
 gcc/testsuite/gcc.dg/builtin-object-size-2.c  |  134 ++
 gcc/testsuite/gcc.dg/builtin-object-size-3.c  |  151 ++
 gcc/testsuite/gcc.dg/builtin-object-size-4.c  |   99 +
 gcc/testsuite/gcc.dg/builtin-object-size-5.c  |   12 +
 gcc/tree-object-size.c                        | 1766 +++++++++++------
 gcc/tree-object-size.h                        |    3 +-
 gcc/ubsan.c                                   |   46 +-
 37 files changed, 2499 insertions(+), 620 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size1.C
 create mode 100644 gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size2.C
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-alloc-size.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-11.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-12.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-13.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-14.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-15.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-16.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-17.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-18.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-19.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-6.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-7.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-8.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-9.c

-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH 01/10] tree-object-size: Replace magic numbers with enums
  2021-11-09 19:01 [PATCH 00/10] __builtin_dynamic_object_size Siddhesh Poyarekar
@ 2021-11-09 19:01 ` Siddhesh Poyarekar
  2021-11-19 16:00   ` Jakub Jelinek
  2021-11-09 19:01 ` [PATCH 02/10] tree-object-size: Abstract object_sizes array Siddhesh Poyarekar
                   ` (13 subsequent siblings)
  14 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-09 19:01 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

A simple cleanup to allow inserting dynamic size code more easily.

gcc/ChangeLog:

	* tree-object-size.c: New enum.
	(object_sizes, computed, addr_object_size,
	compute_builtin_object_size, init_object_sizes,
	fini_object_sizes, object_sizes_execute): Replace magic numbers
	with enums.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 gcc/tree-object-size.c | 47 +++++++++++++++++++++++++-----------------
 1 file changed, 28 insertions(+), 19 deletions(-)

diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index 4334e05ef70..f70f95a7b29 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -45,6 +45,13 @@ struct object_size_info
   unsigned int *stack, *tos;
 };
 
+enum
+{
+  OST_SUBOBJECT = 1,
+  OST_MINIMUM = 2,
+  OST_END = 4,
+};
+
 static tree compute_object_offset (const_tree, const_tree);
 static bool addr_object_size (struct object_size_info *,
 			      const_tree, int, unsigned HOST_WIDE_INT *);
@@ -67,10 +74,10 @@ static void check_for_plus_in_loops_1 (struct object_size_info *, tree,
    the subobject (innermost array or field with address taken).
    object_sizes[2] is lower bound for number of bytes till the end of
    the object and object_sizes[3] lower bound for subobject.  */
-static vec<unsigned HOST_WIDE_INT> object_sizes[4];
+static vec<unsigned HOST_WIDE_INT> object_sizes[OST_END];
 
 /* Bitmaps what object sizes have been computed already.  */
-static bitmap computed[4];
+static bitmap computed[OST_END];
 
 /* Maximum value of offset we consider to be addition.  */
 static unsigned HOST_WIDE_INT offset_limit;
@@ -227,11 +234,11 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
     {
       unsigned HOST_WIDE_INT sz;
 
-      if (!osi || (object_size_type & 1) != 0
+      if (!osi || (object_size_type & OST_SUBOBJECT) != 0
 	  || TREE_CODE (TREE_OPERAND (pt_var, 0)) != SSA_NAME)
 	{
 	  compute_builtin_object_size (TREE_OPERAND (pt_var, 0),
-				       object_size_type & ~1, &sz);
+				       object_size_type & ~OST_SUBOBJECT, &sz);
 	}
       else
 	{
@@ -266,7 +273,7 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
     }
   else if (DECL_P (pt_var))
     {
-      pt_var_size = decl_init_size (pt_var, object_size_type & 2);
+      pt_var_size = decl_init_size (pt_var, object_size_type & OST_MINIMUM);
       if (!pt_var_size)
 	return false;
     }
@@ -287,7 +294,7 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
     {
       tree var;
 
-      if (object_size_type & 1)
+      if (object_size_type & OST_SUBOBJECT)
 	{
 	  var = TREE_OPERAND (ptr, 0);
 
@@ -528,7 +535,7 @@ bool
 compute_builtin_object_size (tree ptr, int object_size_type,
 			     unsigned HOST_WIDE_INT *psize)
 {
-  gcc_assert (object_size_type >= 0 && object_size_type <= 3);
+  gcc_assert (object_size_type >= 0 && object_size_type < OST_END);
 
   /* Set to unknown and overwrite just before returning if the size
      could be determined.  */
@@ -546,7 +553,7 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 
   if (computed[object_size_type] == NULL)
     {
-      if (optimize || object_size_type & 1)
+      if (optimize || object_size_type & OST_SUBOBJECT)
 	return false;
 
       /* When not optimizing, rather than failing, make a small effort
@@ -586,8 +593,8 @@ compute_builtin_object_size (tree ptr, int object_size_type,
       if (dump_file)
 	{
 	  fprintf (dump_file, "Computing %s %sobject size for ",
-		   (object_size_type & 2) ? "minimum" : "maximum",
-		   (object_size_type & 1) ? "sub" : "");
+		   (object_size_type & OST_MINIMUM) ? "minimum" : "maximum",
+		   (object_size_type & OST_SUBOBJECT) ? "sub" : "");
 	  print_generic_expr (dump_file, ptr, dump_flags);
 	  fprintf (dump_file, ":\n");
 	}
@@ -679,8 +686,9 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 		fprintf (dump_file,
 			 ": %s %sobject size "
 			 HOST_WIDE_INT_PRINT_UNSIGNED "\n",
-			 (object_size_type & 2) ? "minimum" : "maximum",
-			 (object_size_type & 1) ? "sub" : "",
+			 ((object_size_type & OST_MINIMUM) ? "minimum"
+			  : "maximum"),
+			 (object_size_type & OST_SUBOBJECT) ? "sub" : "",
 			 object_sizes[object_size_type][i]);
 	      }
 	}
@@ -1238,7 +1246,7 @@ init_object_sizes (void)
   if (computed[0])
     return;
 
-  for (object_size_type = 0; object_size_type <= 3; object_size_type++)
+  for (object_size_type = 0; object_size_type < OST_END; object_size_type++)
     {
       object_sizes[object_size_type].safe_grow (num_ssa_names, true);
       computed[object_size_type] = BITMAP_ALLOC (NULL);
@@ -1255,7 +1263,7 @@ fini_object_sizes (void)
 {
   int object_size_type;
 
-  for (object_size_type = 0; object_size_type <= 3; object_size_type++)
+  for (object_size_type = 0; object_size_type < OST_END; object_size_type++)
     {
       object_sizes[object_size_type].release ();
       BITMAP_FREE (computed[object_size_type]);
@@ -1302,7 +1310,7 @@ object_sizes_execute (function *fun, bool insert_min_max_p)
 		{
 		  unsigned HOST_WIDE_INT object_size_type = tree_to_uhwi (ost);
 		  tree ptr = gimple_call_arg (call, 0);
-		  if ((object_size_type == 1 || object_size_type == 3)
+		  if ((object_size_type & OST_SUBOBJECT)
 		      && (TREE_CODE (ptr) == ADDR_EXPR
 			  || TREE_CODE (ptr) == SSA_NAME))
 		    {
@@ -1315,7 +1323,8 @@ object_sizes_execute (function *fun, bool insert_min_max_p)
 			  tree tem = make_ssa_name (type);
 			  gimple_call_set_lhs (call, tem);
 			  enum tree_code code
-			    = object_size_type == 1 ? MIN_EXPR : MAX_EXPR;
+			    = (object_size_type & OST_MINIMUM
+			       ? MAX_EXPR : MIN_EXPR);
 			  tree cst = build_int_cstu (type, bytes);
 			  gimple *g
 			    = gimple_build_assign (lhs, code, tem, cst);
@@ -1336,11 +1345,11 @@ object_sizes_execute (function *fun, bool insert_min_max_p)
 		{
 		  unsigned HOST_WIDE_INT object_size_type = tree_to_uhwi (ost);
 
-		  if (object_size_type < 2)
+		  if (object_size_type & OST_MINIMUM)
+		    result = build_zero_cst (size_type_node);
+		  else if (object_size_type < OST_END)
 		    result = fold_convert (size_type_node,
 					   integer_minus_one_node);
-		  else if (object_size_type < 4)
-		    result = build_zero_cst (size_type_node);
 		}
 
 	      if (!result)
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH 02/10] tree-object-size: Abstract object_sizes array
  2021-11-09 19:01 [PATCH 00/10] __builtin_dynamic_object_size Siddhesh Poyarekar
  2021-11-09 19:01 ` [PATCH 01/10] tree-object-size: Replace magic numbers with enums Siddhesh Poyarekar
@ 2021-11-09 19:01 ` Siddhesh Poyarekar
  2021-11-19 16:18   ` Jakub Jelinek
  2021-11-09 19:01 ` [PATCH 03/10] tree-object-size: Use tree instead of HOST_WIDE_INT Siddhesh Poyarekar
                   ` (12 subsequent siblings)
  14 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-09 19:01 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Put all accesses to object_sizes behind functions so that we can add
dynamic capability more easily.

gcc/ChangeLog:

	* tree-object-size.c (object_sizes_grow, object_sizes_release,
	object_sizes_unknown_p, object_sizes_get, object_size_set_force,
	object_sizes_set): New functions.
	(addr_object_size, compute_builtin_object_size,
	expr_object_size, call_object_size, unknown_object_size,
	merge_object_sizes, plus_stmt_object_size,
	cond_expr_object_size, collect_object_sizes_for,
	check_for_plus_in_loops_1, init_object_sizes,
	fini_object_sizes): Adjust.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 gcc/tree-object-size.c | 176 +++++++++++++++++++++++------------------
 1 file changed, 97 insertions(+), 79 deletions(-)

diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index f70f95a7b29..7a22539cc43 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -88,6 +88,71 @@ unknown (int object_size_type)
   return ((unsigned HOST_WIDE_INT) -((object_size_type >> 1) ^ 1));
 }
 
+/* Grow object_sizes[OBJECT_SIZE_TYPE] to num_ssa_names.  */
+
+static inline void
+object_sizes_grow (int object_size_type)
+{
+  if (num_ssa_names > object_sizes[object_size_type].length ())
+    object_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[object_size_type].release ();
+}
+
+/* Return true if object_sizes[OBJECT_SIZE_TYPE][VARNO] is unknown.  */
+
+static inline bool
+object_sizes_unknown_p (int object_size_type, unsigned varno)
+{
+  return (object_sizes[object_size_type][varno]
+	  == unknown (object_size_type));
+}
+
+/* Return size for VARNO corresponding to OSI.  */
+
+static inline unsigned HOST_WIDE_INT
+object_sizes_get (struct object_size_info *osi, unsigned varno)
+{
+  return object_sizes[osi->object_size_type][varno];
+}
+
+/* Set size for VARNO corresponding to OSI to VAL.  */
+
+static inline bool
+object_sizes_set_force (struct object_size_info *osi, unsigned varno,
+			unsigned HOST_WIDE_INT val)
+{
+  object_sizes[osi->object_size_type][varno] = val;
+  return true;
+}
+
+/* Set size for VARNO corresponding to OSI to VAL if it is the new minimum or
+   maximum.  */
+
+static inline bool
+object_sizes_set (struct object_size_info *osi, unsigned varno,
+		  unsigned HOST_WIDE_INT val)
+{
+  int object_size_type = osi->object_size_type;
+  if ((object_size_type & OST_MINIMUM) == 0)
+    {
+      if (object_sizes[object_size_type][varno] < val)
+	return object_sizes_set_force (osi, varno, val);
+    }
+  else
+    {
+      if (object_sizes[object_size_type][varno] > val)
+	return object_sizes_set_force (osi, varno, val);
+    }
+  return false;
+}
+
 /* Initialize OFFSET_LIMIT variable.  */
 static void
 init_offset_limit (void)
@@ -247,7 +312,7 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 	    collect_object_sizes_for (osi, var);
 	  if (bitmap_bit_p (computed[object_size_type],
 			    SSA_NAME_VERSION (var)))
-	    sz = object_sizes[object_size_type][SSA_NAME_VERSION (var)];
+	    sz = object_sizes_get (osi, SSA_NAME_VERSION (var));
 	  else
 	    sz = unknown (object_size_type);
 	}
@@ -582,14 +647,14 @@ compute_builtin_object_size (tree ptr, int object_size_type,
       return false;
     }
 
+  struct object_size_info osi;
+  osi.object_size_type = object_size_type;
   if (!bitmap_bit_p (computed[object_size_type], SSA_NAME_VERSION (ptr)))
     {
-      struct object_size_info osi;
       bitmap_iterator bi;
       unsigned int i;
 
-      if (num_ssa_names > object_sizes[object_size_type].length ())
-	object_sizes[object_size_type].safe_grow (num_ssa_names, true);
+      object_sizes_grow (object_size_type);
       if (dump_file)
 	{
 	  fprintf (dump_file, "Computing %s %sobject size for ",
@@ -601,7 +666,6 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 
       osi.visited = BITMAP_ALLOC (NULL);
       osi.reexamine = BITMAP_ALLOC (NULL);
-      osi.object_size_type = object_size_type;
       osi.depths = NULL;
       osi.stack = NULL;
       osi.tos = NULL;
@@ -678,8 +742,7 @@ compute_builtin_object_size (tree ptr, int object_size_type,
       if (dump_file)
 	{
 	  EXECUTE_IF_SET_IN_BITMAP (osi.visited, 0, i, bi)
-	    if (object_sizes[object_size_type][i]
-		!= unknown (object_size_type))
+	    if (!object_sizes_unknown_p (object_size_type, i))
 	      {
 		print_generic_expr (dump_file, ssa_name (i),
 				    dump_flags);
@@ -689,7 +752,7 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 			 ((object_size_type & OST_MINIMUM) ? "minimum"
 			  : "maximum"),
 			 (object_size_type & OST_SUBOBJECT) ? "sub" : "",
-			 object_sizes[object_size_type][i]);
+			 object_sizes_get (&osi, i));
 	      }
 	}
 
@@ -697,7 +760,7 @@ compute_builtin_object_size (tree ptr, int object_size_type,
       BITMAP_FREE (osi.visited);
     }
 
-  *psize = object_sizes[object_size_type][SSA_NAME_VERSION (ptr)];
+  *psize = object_sizes_get (&osi, SSA_NAME_VERSION (ptr));
   return *psize != unknown (object_size_type);
 }
 
@@ -710,8 +773,7 @@ expr_object_size (struct object_size_info *osi, tree ptr, tree value)
   unsigned int varno = SSA_NAME_VERSION (ptr);
   unsigned HOST_WIDE_INT bytes;
 
-  gcc_assert (object_sizes[object_size_type][varno]
-	      != unknown (object_size_type));
+  gcc_assert (!object_sizes_unknown_p (object_size_type, varno));
   gcc_assert (osi->pass == 0);
 
   if (TREE_CODE (value) == WITH_SIZE_EXPR)
@@ -726,16 +788,7 @@ expr_object_size (struct object_size_info *osi, tree ptr, tree value)
   else
     bytes = unknown (object_size_type);
 
-  if ((object_size_type & 2) == 0)
-    {
-      if (object_sizes[object_size_type][varno] < bytes)
-	object_sizes[object_size_type][varno] = bytes;
-    }
-  else
-    {
-      if (object_sizes[object_size_type][varno] > bytes)
-	object_sizes[object_size_type][varno] = bytes;
-    }
+  object_sizes_set (osi, varno, bytes);
 }
 
 
@@ -750,22 +803,12 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call)
 
   gcc_assert (is_gimple_call (call));
 
-  gcc_assert (object_sizes[object_size_type][varno]
-	      != unknown (object_size_type));
+  gcc_assert (!object_sizes_unknown_p (object_size_type, varno));
   gcc_assert (osi->pass == 0);
 
   bytes = alloc_object_size (call, object_size_type);
 
-  if ((object_size_type & 2) == 0)
-    {
-      if (object_sizes[object_size_type][varno] < bytes)
-	object_sizes[object_size_type][varno] = bytes;
-    }
-  else
-    {
-      if (object_sizes[object_size_type][varno] > bytes)
-	object_sizes[object_size_type][varno] = bytes;
-    }
+  object_sizes_set (osi, varno, bytes);
 }
 
 
@@ -776,12 +819,11 @@ unknown_object_size (struct object_size_info *osi, tree ptr)
 {
   int object_size_type = osi->object_size_type;
   unsigned int varno = SSA_NAME_VERSION (ptr);
-  unsigned HOST_WIDE_INT bytes = unknown (object_size_type);
 
-  gcc_checking_assert (object_sizes[object_size_type][varno] != bytes);
+  gcc_checking_assert (!object_sizes_unknown_p (object_size_type, varno));
   gcc_checking_assert (osi->pass == 0);
 
-  object_sizes[object_size_type][varno] = bytes;
+  object_sizes_set (osi, varno, unknown (object_size_type));
 }
 
 
@@ -796,38 +838,24 @@ merge_object_sizes (struct object_size_info *osi, tree dest, tree orig,
   unsigned int varno = SSA_NAME_VERSION (dest);
   unsigned HOST_WIDE_INT orig_bytes;
 
-  if (object_sizes[object_size_type][varno] == unknown (object_size_type))
+  if (object_sizes_unknown_p (object_size_type, varno))
     return false;
   if (offset >= offset_limit)
     {
-      object_sizes[object_size_type][varno] = unknown (object_size_type);
+      object_sizes_set (osi, varno, unknown (object_size_type));
       return false;
     }
 
   if (osi->pass == 0)
     collect_object_sizes_for (osi, orig);
 
-  orig_bytes = object_sizes[object_size_type][SSA_NAME_VERSION (orig)];
+  orig_bytes = object_sizes_get (osi, SSA_NAME_VERSION (orig));
   if (orig_bytes != unknown (object_size_type))
     orig_bytes = (offset > orig_bytes)
 		 ? HOST_WIDE_INT_0U : orig_bytes - offset;
 
-  if ((object_size_type & 2) == 0)
-    {
-      if (object_sizes[object_size_type][varno] < orig_bytes)
-	{
-	  object_sizes[object_size_type][varno] = orig_bytes;
-	  osi->changed = true;
-	}
-    }
-  else
-    {
-      if (object_sizes[object_size_type][varno] > orig_bytes)
-	{
-	  object_sizes[object_size_type][varno] = orig_bytes;
-	  osi->changed = true;
-	}
-    }
+  osi->changed = object_sizes_set (osi, varno, orig_bytes);
+
   return bitmap_bit_p (osi->reexamine, SSA_NAME_VERSION (orig));
 }
 
@@ -859,7 +887,7 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   else
     gcc_unreachable ();
 
-  if (object_sizes[object_size_type][varno] == unknown (object_size_type))
+  if (object_sizes_unknown_p (object_size_type, varno))
     return false;
 
   /* Handle PTR + OFFSET here.  */
@@ -890,16 +918,7 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   else
     bytes = unknown (object_size_type);
 
-  if ((object_size_type & 2) == 0)
-    {
-      if (object_sizes[object_size_type][varno] < bytes)
-	object_sizes[object_size_type][varno] = bytes;
-    }
-  else
-    {
-      if (object_sizes[object_size_type][varno] > bytes)
-	object_sizes[object_size_type][varno] = bytes;
-    }
+  object_sizes_set (osi, varno, bytes);
   return false;
 }
 
@@ -918,7 +937,7 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
 
   gcc_assert (gimple_assign_rhs_code (stmt) == COND_EXPR);
 
-  if (object_sizes[object_size_type][varno] == unknown (object_size_type))
+  if (object_sizes_unknown_p (object_size_type, varno))
     return false;
 
   then_ = gimple_assign_rhs2 (stmt);
@@ -929,7 +948,7 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   else
     expr_object_size (osi, var, then_);
 
-  if (object_sizes[object_size_type][varno] == unknown (object_size_type))
+  if (object_sizes_unknown_p (object_size_type, varno))
     return reexamine;
 
   if (TREE_CODE (else_) == SSA_NAME)
@@ -975,8 +994,9 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
     {
       if (bitmap_set_bit (osi->visited, varno))
 	{
-	  object_sizes[object_size_type][varno]
-	    = (object_size_type & 2) ? -1 : 0;
+	  /* Initialize to 0 for maximum size and M1U for minimum size so that
+	     it gets immediately overridden.  */
+	  object_sizes_set_force (osi, varno, unknown (object_size_type ^ 2));
 	}
       else
 	{
@@ -1047,7 +1067,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 
     case GIMPLE_ASM:
       /* Pointers defined by __asm__ statements can point anywhere.  */
-      object_sizes[object_size_type][varno] = unknown (object_size_type);
+      object_sizes_set (osi, varno, unknown (object_size_type));
       break;
 
     case GIMPLE_NOP:
@@ -1056,7 +1076,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 	expr_object_size (osi, var, SSA_NAME_VAR (var));
       else
 	/* Uninitialized SSA names point nowhere.  */
-	object_sizes[object_size_type][varno] = unknown (object_size_type);
+	object_sizes_set (osi, varno, unknown (object_size_type));
       break;
 
     case GIMPLE_PHI:
@@ -1067,8 +1087,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 	  {
 	    tree rhs = gimple_phi_arg (stmt, i)->def;
 
-	    if (object_sizes[object_size_type][varno]
-		== unknown (object_size_type))
+	    if (object_sizes_unknown_p (object_size_type, varno))
 	      break;
 
 	    if (TREE_CODE (rhs) == SSA_NAME)
@@ -1083,8 +1102,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
       gcc_unreachable ();
     }
 
-  if (! reexamine
-      || object_sizes[object_size_type][varno] == unknown (object_size_type))
+  if (! reexamine || object_sizes_unknown_p (object_size_type, varno))
     {
       bitmap_set_bit (computed[object_size_type], varno);
       bitmap_clear_bit (osi->reexamine, varno);
@@ -1124,7 +1142,7 @@ check_for_plus_in_loops_1 (struct object_size_info *osi, tree var,
 	      --sp;
 	      bitmap_clear_bit (osi->reexamine, *sp);
 	      bitmap_set_bit (computed[osi->object_size_type], *sp);
-	      object_sizes[osi->object_size_type][*sp] = 0;
+	      object_sizes_set_force (osi, *sp, 0);
 	      if (*sp == varno)
 		break;
 	    }
@@ -1248,7 +1266,7 @@ init_object_sizes (void)
 
   for (object_size_type = 0; object_size_type < OST_END; object_size_type++)
     {
-      object_sizes[object_size_type].safe_grow (num_ssa_names, true);
+      object_sizes_grow (object_size_type);
       computed[object_size_type] = BITMAP_ALLOC (NULL);
     }
 
@@ -1265,7 +1283,7 @@ fini_object_sizes (void)
 
   for (object_size_type = 0; object_size_type < OST_END; object_size_type++)
     {
-      object_sizes[object_size_type].release ();
+      object_sizes_release (object_size_type);
       BITMAP_FREE (computed[object_size_type]);
     }
 }
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH 03/10] tree-object-size: Use tree instead of HOST_WIDE_INT
  2021-11-09 19:01 [PATCH 00/10] __builtin_dynamic_object_size Siddhesh Poyarekar
  2021-11-09 19:01 ` [PATCH 01/10] tree-object-size: Replace magic numbers with enums Siddhesh Poyarekar
  2021-11-09 19:01 ` [PATCH 02/10] tree-object-size: Abstract object_sizes array Siddhesh Poyarekar
@ 2021-11-09 19:01 ` Siddhesh Poyarekar
  2021-11-19 17:06   ` Jakub Jelinek
  2021-11-09 19:01 ` [PATCH 04/10] tree-object-size: Single pass dependency loop resolution Siddhesh Poyarekar
                   ` (11 subsequent siblings)
  14 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-09 19:01 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Transform tree-object-size to operate on tree objects instead of host
wide integers.  This makes it easier to extend to dynamic expressions
for object sizes.

The compute_builtin_object_size interface also now returns a tree
expression instead of HOST_WIDE_INT, so callers have been adjusted to
account for that.

gcc/ChangeLog:

	* tree-object-size.h (compute_builtin_object_size): Return tree
	instead of HOST_WIDE_INT.
	* builtins.c (fold_builtin_object_size): Adjust.
	* gimple-fold.c (gimple_fold_builtin_strncat): Likewise.
	* ubsan.c (instrument_object_size): Likewise.
	* tree-object-size.c (object_sizes): Change type to vec<tree>.
	(initval): New function.
	(unknown): Use it.
	(size_unknown_p, size_initval, size_unknown): New functions.
	(object_sizes_unknown_p): Use it.
	(object_sizes_get): Return tree.
	(object_sizes_initialize): Rename from object_sizes_set_force
	and set VAL parameter type as tree.
	(object_sizes_set): Set VAL parameter type as tree and adjust
	implementation.
	(size_for_offset): New function.
	(addr_object_size): Change PSIZE parameter to tree and adjust
	implementation.
	(alloc_object_size): Return tree.
	(compute_builtin_object_size): Return tree in PSIZE.
	(expr_object_size, call_object_size, unknown_object_size):
	Adjust for object_sizes_set change.
	(merge_object_sizes): Set OFFSET type to tree and adjust
	implementation.
	(plus_stmt_object_size, cond_expr_object_size,
	collect_object_sizes_for, check_for_plus_in_loops_1): Adjust for
	change of type from HOST_WIDE_INT to tree.
	(object_sizes_execute): Adjust for type change to tree.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 gcc/builtins.c         |  10 +-
 gcc/gimple-fold.c      |   9 +-
 gcc/tree-object-size.c | 253 ++++++++++++++++++++++-------------------
 gcc/tree-object-size.h |   2 +-
 gcc/ubsan.c            |  46 ++++----
 5 files changed, 168 insertions(+), 152 deletions(-)

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 384864bfb3a..d2e6d95a175 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -10226,7 +10226,7 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
 static tree
 fold_builtin_object_size (tree ptr, tree ost)
 {
-  unsigned HOST_WIDE_INT bytes;
+  tree bytes;
   int object_size_type;
 
   if (!validate_arg (ptr, POINTER_TYPE)
@@ -10251,17 +10251,15 @@ fold_builtin_object_size (tree ptr, tree ost)
   if (TREE_CODE (ptr) == ADDR_EXPR)
     {
       compute_builtin_object_size (ptr, object_size_type, &bytes);
-      if (wi::fits_to_tree_p (bytes, size_type_node))
-	return build_int_cstu (size_type_node, bytes);
+      return fold_convert (size_type_node, bytes);
     }
   else if (TREE_CODE (ptr) == SSA_NAME)
     {
       /* If object size is not known yet, delay folding until
        later.  Maybe subsequent passes will help determining
        it.  */
-      if (compute_builtin_object_size (ptr, object_size_type, &bytes)
-	  && wi::fits_to_tree_p (bytes, size_type_node))
-	return build_int_cstu (size_type_node, bytes);
+      if (compute_builtin_object_size (ptr, object_size_type, &bytes))
+	return fold_convert (size_type_node, bytes);
     }
 
   return NULL_TREE;
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 6e25a7c05db..cbd0fb08f80 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -2486,13 +2486,14 @@ gimple_fold_builtin_strncat (gimple_stmt_iterator *gsi)
   if (cmpsrc < 0)
     return false;
 
-  unsigned HOST_WIDE_INT dstsize;
+  tree dstsize;
 
   bool nowarn = warning_suppressed_p (stmt, OPT_Wstringop_overflow_);
 
-  if (!nowarn && compute_builtin_object_size (dst, 1, &dstsize))
+  if (!nowarn && compute_builtin_object_size (dst, 1, &dstsize)
+      && tree_fits_uhwi_p (dstsize))
     {
-      int cmpdst = compare_tree_int (len, dstsize);
+      int cmpdst = compare_tree_int (len, tree_to_uhwi (dstsize));
 
       if (cmpdst >= 0)
 	{
@@ -2509,7 +2510,7 @@ gimple_fold_builtin_strncat (gimple_stmt_iterator *gsi)
 				    "destination size")
 			       : G_("%qD specified bound %E exceeds "
 				    "destination size %wu"),
-			       fndecl, len, dstsize);
+			       fndecl, len, tree_to_uhwi (dstsize));
 	  if (nowarn)
 	    suppress_warning (stmt, OPT_Wstringop_overflow_);
 	}
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index 7a22539cc43..4b9c45c6af2 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -54,13 +54,12 @@ enum
 
 static tree compute_object_offset (const_tree, const_tree);
 static bool addr_object_size (struct object_size_info *,
-			      const_tree, int, unsigned HOST_WIDE_INT *);
-static unsigned HOST_WIDE_INT alloc_object_size (const gcall *, int);
+			      const_tree, int, tree *);
+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 void expr_object_size (struct object_size_info *, tree, tree);
-static bool merge_object_sizes (struct object_size_info *, tree, tree,
-				unsigned HOST_WIDE_INT);
+static bool merge_object_sizes (struct object_size_info *, tree, tree, tree);
 static bool plus_stmt_object_size (struct object_size_info *, tree, gimple *);
 static bool cond_expr_object_size (struct object_size_info *, tree, gimple *);
 static void init_offset_limit (void);
@@ -74,7 +73,7 @@ static void check_for_plus_in_loops_1 (struct object_size_info *, tree,
    the subobject (innermost array or field with address taken).
    object_sizes[2] is lower bound for number of bytes till the end of
    the object and object_sizes[3] lower bound for subobject.  */
-static vec<unsigned HOST_WIDE_INT> object_sizes[OST_END];
+static vec<tree> object_sizes[OST_END];
 
 /* Bitmaps what object sizes have been computed already.  */
 static bitmap computed[OST_END];
@@ -82,10 +81,47 @@ static bitmap computed[OST_END];
 /* Maximum value of offset we consider to be addition.  */
 static unsigned HOST_WIDE_INT offset_limit;
 
+/* Initial value of object sizes; zero for maximum and SIZE_MAX for minimum
+   object size.  */
+
+static inline unsigned HOST_WIDE_INT
+initval (int object_size_type)
+{
+  return (unsigned HOST_WIDE_INT) -popcount_hwi (object_size_type
+						 & OST_MINIMUM);
+}
+
+/* Unknown object size value; it's the opposite of initval.  */
+
 static inline unsigned HOST_WIDE_INT
 unknown (int object_size_type)
 {
-  return ((unsigned HOST_WIDE_INT) -((object_size_type >> 1) ^ 1));
+  return ~initval (object_size_type);
+}
+
+/* Return true if VAL is represents an unknown size for OBJECT_SIZE_TYPE.  */
+
+static inline bool
+size_unknown_p (tree val, int object_size_type)
+{
+  return (tree_fits_uhwi_p (val)
+	  && tree_to_uhwi (val) == unknown (object_size_type));
+}
+
+/* Return a tree with initial value for OBJECT_SIZE_TYPE.  */
+
+static inline tree
+size_initval (int object_size_type)
+{
+  return size_int (initval (object_size_type));
+}
+
+/* Return a tree with unknown value for OBJECT_SIZE_TYPE.  */
+
+static inline tree
+size_unknown (int object_size_type)
+{
+  return size_int (unknown (object_size_type));
 }
 
 /* Grow object_sizes[OBJECT_SIZE_TYPE] to num_ssa_names.  */
@@ -110,13 +146,13 @@ object_sizes_release (int object_size_type)
 static inline bool
 object_sizes_unknown_p (int object_size_type, unsigned varno)
 {
-  return (object_sizes[object_size_type][varno]
-	  == unknown (object_size_type));
+  return size_unknown_p (object_sizes[object_size_type][varno],
+			 object_size_type);
 }
 
 /* Return size for VARNO corresponding to OSI.  */
 
-static inline unsigned HOST_WIDE_INT
+static inline tree
 object_sizes_get (struct object_size_info *osi, unsigned varno)
 {
   return object_sizes[osi->object_size_type][varno];
@@ -124,33 +160,32 @@ object_sizes_get (struct object_size_info *osi, unsigned varno)
 
 /* Set size for VARNO corresponding to OSI to VAL.  */
 
-static inline bool
-object_sizes_set_force (struct object_size_info *osi, unsigned varno,
-			unsigned HOST_WIDE_INT val)
+static inline void
+object_sizes_initialize (struct object_size_info *osi, unsigned varno,
+			 tree val = NULL_TREE)
 {
-  object_sizes[osi->object_size_type][varno] = val;
-  return true;
+  int object_size_type = osi->object_size_type;
+
+  if (!val)
+    val = size_initval (object_size_type);
+  object_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 bool
-object_sizes_set (struct object_size_info *osi, unsigned varno,
-		  unsigned HOST_WIDE_INT val)
+object_sizes_set (struct object_size_info *osi, unsigned varno, tree val)
 {
   int object_size_type = osi->object_size_type;
-  if ((object_size_type & OST_MINIMUM) == 0)
-    {
-      if (object_sizes[object_size_type][varno] < val)
-	return object_sizes_set_force (osi, varno, val);
-    }
-  else
-    {
-      if (object_sizes[object_size_type][varno] > val)
-	return object_sizes_set_force (osi, varno, val);
-    }
-  return false;
+  tree oldval = object_sizes[object_size_type][varno];
+
+  enum tree_code code = object_size_type & OST_MINIMUM ? MIN_EXPR : MAX_EXPR;
+
+  if (compare_tree_int (oldval, initval (object_size_type)) != 0)
+    val = size_binop (code, val, oldval);
+  object_sizes[object_size_type][varno] = val;
+  return tree_int_cst_compare (val, oldval) != 0;
 }
 
 /* Initialize OFFSET_LIMIT variable.  */
@@ -164,6 +199,13 @@ init_offset_limit (void)
   offset_limit /= 2;
 }
 
+/* Bytes at end of the object with SZ from offset OFFSET. */
+
+static tree
+size_for_offset (tree sz, tree offset)
+{
+  return size_binop (MINUS_EXPR, size_binop (MAX_EXPR, sz, offset), offset);
+}
 
 /* Compute offset of EXPR within VAR.  Return error_mark_node
    if unknown.  */
@@ -274,11 +316,11 @@ decl_init_size (tree decl, bool min)
 
 /* Compute __builtin_object_size for PTR, which is a ADDR_EXPR.
    OBJECT_SIZE_TYPE is the second argument from __builtin_object_size.
-   If unknown, return unknown (object_size_type).  */
+   If unknown, return size_unknown (object_size_type).  */
 
 static bool
 addr_object_size (struct object_size_info *osi, const_tree ptr,
-		  int object_size_type, unsigned HOST_WIDE_INT *psize)
+		  int object_size_type, tree *psize)
 {
   tree pt_var, pt_var_size = NULL_TREE, var_size, bytes;
 
@@ -286,7 +328,7 @@ 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 = unknown (object_size_type);
+  *psize = size_unknown (object_size_type);
 
   pt_var = TREE_OPERAND (ptr, 0);
   while (handled_component_p (pt_var))
@@ -297,7 +339,7 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 
   if (TREE_CODE (pt_var) == MEM_REF)
     {
-      unsigned HOST_WIDE_INT sz;
+      tree sz;
 
       if (!osi || (object_size_type & OST_SUBOBJECT) != 0
 	  || TREE_CODE (TREE_OPERAND (pt_var, 0)) != SSA_NAME)
@@ -314,27 +356,22 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 			    SSA_NAME_VERSION (var)))
 	    sz = object_sizes_get (osi, SSA_NAME_VERSION (var));
 	  else
-	    sz = unknown (object_size_type);
+	    sz = size_unknown (object_size_type);
 	}
-      if (sz != unknown (object_size_type))
+      if (!size_unknown_p (sz, object_size_type))
 	{
-	  offset_int mem_offset;
-	  if (mem_ref_offset (pt_var).is_constant (&mem_offset))
-	    {
-	      offset_int dsz = wi::sub (sz, mem_offset);
-	      if (wi::neg_p (dsz))
-		sz = 0;
-	      else if (wi::fits_uhwi_p (dsz))
-		sz = dsz.to_uhwi ();
-	      else
-		sz = unknown (object_size_type);
-	    }
+	  tree offset = TREE_OPERAND (pt_var, 1);
+	  if (TREE_CODE (offset) != INTEGER_CST
+	      || TREE_CODE (sz) != INTEGER_CST)
+	    sz = size_unknown (object_size_type);
 	  else
-	    sz = unknown (object_size_type);
+	    sz = size_for_offset (sz, offset);
 	}
 
-      if (sz != unknown (object_size_type) && sz < offset_limit)
-	pt_var_size = size_int (sz);
+      if (!size_unknown_p (sz, object_size_type)
+	  && TREE_CODE (sz) == INTEGER_CST
+	  && compare_tree_int (sz, offset_limit) < 0)
+	pt_var_size = sz;
     }
   else if (DECL_P (pt_var))
     {
@@ -350,8 +387,7 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
   if (pt_var_size)
     {
       /* Validate the size determined above.  */
-      if (!tree_fits_uhwi_p (pt_var_size)
-	  || tree_to_uhwi (pt_var_size) >= offset_limit)
+      if (compare_tree_int (pt_var_size, offset_limit) >= 0)
 	return false;
     }
 
@@ -502,22 +538,17 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
   else
     bytes = pt_var_size;
 
-  if (tree_fits_uhwi_p (bytes))
-    {
-      *psize = tree_to_uhwi (bytes);
-      return true;
-    }
-
-  return false;
+  *psize = bytes;
+  return true;
 }
 
 
 /* Compute __builtin_object_size for CALL, which is a GIMPLE_CALL.
    Handles calls to functions declared with attribute alloc_size.
    OBJECT_SIZE_TYPE is the second argument from __builtin_object_size.
-   If unknown, return unknown (object_size_type).  */
+   If unknown, return size_unknown (object_size_type).  */
 
-static unsigned HOST_WIDE_INT
+static tree
 alloc_object_size (const gcall *call, int object_size_type)
 {
   gcc_assert (is_gimple_call (call));
@@ -529,7 +560,7 @@ alloc_object_size (const gcall *call, int object_size_type)
     calltype = gimple_call_fntype (call);
 
   if (!calltype)
-    return unknown (object_size_type);
+    return size_unknown (object_size_type);
 
   /* Set to positions of alloc_size arguments.  */
   int arg1 = -1, arg2 = -1;
@@ -549,7 +580,7 @@ alloc_object_size (const gcall *call, int object_size_type)
       || (arg2 >= 0
 	  && (arg2 >= (int)gimple_call_num_args (call)
 	      || TREE_CODE (gimple_call_arg (call, arg2)) != INTEGER_CST)))
-    return unknown (object_size_type);
+    return size_unknown (object_size_type);
 
   tree bytes = NULL_TREE;
   if (arg2 >= 0)
@@ -559,10 +590,7 @@ alloc_object_size (const gcall *call, int object_size_type)
   else if (arg1 >= 0)
     bytes = fold_convert (sizetype, gimple_call_arg (call, arg1));
 
-  if (bytes && tree_fits_uhwi_p (bytes))
-    return tree_to_uhwi (bytes);
-
-  return unknown (object_size_type);
+  return bytes;
 }
 
 
@@ -598,13 +626,13 @@ pass_through_call (const gcall *call)
 
 bool
 compute_builtin_object_size (tree ptr, int object_size_type,
-			     unsigned HOST_WIDE_INT *psize)
+			     tree *psize)
 {
   gcc_assert (object_size_type >= 0 && object_size_type < OST_END);
 
   /* Set to unknown and overwrite just before returning if the size
      could be determined.  */
-  *psize = unknown (object_size_type);
+  *psize = size_unknown (object_size_type);
 
   if (! offset_limit)
     init_offset_limit ();
@@ -638,8 +666,7 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 						  psize))
 		{
 		  /* Return zero when the offset is out of bounds.  */
-		  unsigned HOST_WIDE_INT off = tree_to_shwi (offset);
-		  *psize = off < *psize ? *psize - off : 0;
+		  *psize = size_for_offset (*psize, offset);
 		  return true;
 		}
 	    }
@@ -747,12 +774,13 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 		print_generic_expr (dump_file, ssa_name (i),
 				    dump_flags);
 		fprintf (dump_file,
-			 ": %s %sobject size "
-			 HOST_WIDE_INT_PRINT_UNSIGNED "\n",
+			 ": %s %sobject size ",
 			 ((object_size_type & OST_MINIMUM) ? "minimum"
 			  : "maximum"),
-			 (object_size_type & OST_SUBOBJECT) ? "sub" : "",
-			 object_sizes_get (&osi, i));
+			 (object_size_type & OST_SUBOBJECT) ? "sub" : "");
+		print_generic_expr (dump_file, object_sizes_get (&osi, i),
+				    dump_flags);
+		fprintf (dump_file, "\n");
 	      }
 	}
 
@@ -761,7 +789,7 @@ compute_builtin_object_size (tree ptr, int object_size_type,
     }
 
   *psize = object_sizes_get (&osi, SSA_NAME_VERSION (ptr));
-  return *psize != unknown (object_size_type);
+  return !size_unknown_p (*psize, object_size_type);
 }
 
 /* Compute object_sizes for PTR, defined to VALUE, which is not an SSA_NAME.  */
@@ -771,7 +799,7 @@ expr_object_size (struct object_size_info *osi, tree ptr, tree value)
 {
   int object_size_type = osi->object_size_type;
   unsigned int varno = SSA_NAME_VERSION (ptr);
-  unsigned HOST_WIDE_INT bytes;
+  tree bytes;
 
   gcc_assert (!object_sizes_unknown_p (object_size_type, varno));
   gcc_assert (osi->pass == 0);
@@ -786,7 +814,7 @@ expr_object_size (struct object_size_info *osi, tree ptr, tree value)
   if (TREE_CODE (value) == ADDR_EXPR)
     addr_object_size (osi, value, object_size_type, &bytes);
   else
-    bytes = unknown (object_size_type);
+    bytes = size_unknown (object_size_type);
 
   object_sizes_set (osi, varno, bytes);
 }
@@ -799,16 +827,13 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call)
 {
   int object_size_type = osi->object_size_type;
   unsigned int varno = SSA_NAME_VERSION (ptr);
-  unsigned HOST_WIDE_INT bytes;
 
   gcc_assert (is_gimple_call (call));
 
   gcc_assert (!object_sizes_unknown_p (object_size_type, varno));
   gcc_assert (osi->pass == 0);
 
-  bytes = alloc_object_size (call, object_size_type);
-
-  object_sizes_set (osi, varno, bytes);
+  object_sizes_set (osi, varno, alloc_object_size (call, object_size_type));
 }
 
 
@@ -823,7 +848,7 @@ unknown_object_size (struct object_size_info *osi, tree ptr)
   gcc_checking_assert (!object_sizes_unknown_p (object_size_type, varno));
   gcc_checking_assert (osi->pass == 0);
 
-  object_sizes_set (osi, varno, unknown (object_size_type));
+  object_sizes_set (osi, varno, size_unknown (object_size_type));
 }
 
 
@@ -832,17 +857,17 @@ unknown_object_size (struct object_size_info *osi, tree ptr)
 
 static bool
 merge_object_sizes (struct object_size_info *osi, tree dest, tree orig,
-		    unsigned HOST_WIDE_INT offset)
+		    tree offset)
 {
   int object_size_type = osi->object_size_type;
   unsigned int varno = SSA_NAME_VERSION (dest);
-  unsigned HOST_WIDE_INT orig_bytes;
+  tree orig_bytes;
 
   if (object_sizes_unknown_p (object_size_type, varno))
     return false;
-  if (offset >= offset_limit)
+  if (compare_tree_int (offset, offset_limit) >= 0)
     {
-      object_sizes_set (osi, varno, unknown (object_size_type));
+      object_sizes_set (osi, varno, size_unknown (object_size_type));
       return false;
     }
 
@@ -850,9 +875,8 @@ merge_object_sizes (struct object_size_info *osi, tree dest, tree orig,
     collect_object_sizes_for (osi, orig);
 
   orig_bytes = object_sizes_get (osi, SSA_NAME_VERSION (orig));
-  if (orig_bytes != unknown (object_size_type))
-    orig_bytes = (offset > orig_bytes)
-		 ? HOST_WIDE_INT_0U : orig_bytes - offset;
+  if (!size_unknown_p (orig_bytes, object_size_type))
+    orig_bytes = size_for_offset (orig_bytes, offset);
 
   osi->changed = object_sizes_set (osi, varno, orig_bytes);
 
@@ -869,7 +893,7 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
 {
   int object_size_type = osi->object_size_type;
   unsigned int varno = SSA_NAME_VERSION (var);
-  unsigned HOST_WIDE_INT bytes;
+  tree bytes;
   tree op0, op1;
 
   if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
@@ -895,28 +919,22 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
       && (TREE_CODE (op0) == SSA_NAME
 	  || TREE_CODE (op0) == ADDR_EXPR))
     {
-      if (! tree_fits_uhwi_p (op1))
-	bytes = unknown (object_size_type);
-      else if (TREE_CODE (op0) == SSA_NAME)
-	return merge_object_sizes (osi, var, op0, tree_to_uhwi (op1));
+      if (TREE_CODE (op0) == SSA_NAME)
+	return merge_object_sizes (osi, var, op0, op1);
       else
 	{
-	  unsigned HOST_WIDE_INT off = tree_to_uhwi (op1);
-
           /* op0 will be ADDR_EXPR here.  */
 	  addr_object_size (osi, op0, object_size_type, &bytes);
-	  if (bytes == unknown (object_size_type))
+	  if (size_unknown_p (bytes, object_size_type))
 	    ;
-	  else if (off > offset_limit)
-	    bytes = unknown (object_size_type);
-	  else if (off > bytes)
-	    bytes = 0;
+	  else if (compare_tree_int (op1, offset_limit) > 0)
+	    bytes = size_unknown (object_size_type);
 	  else
-	    bytes -= off;
+	    bytes = size_for_offset (bytes, op1);
 	}
     }
   else
-    bytes = unknown (object_size_type);
+    bytes = size_unknown (object_size_type);
 
   object_sizes_set (osi, varno, bytes);
   return false;
@@ -944,7 +962,7 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   else_ = gimple_assign_rhs3 (stmt);
 
   if (TREE_CODE (then_) == SSA_NAME)
-    reexamine |= merge_object_sizes (osi, var, then_, 0);
+    reexamine |= merge_object_sizes (osi, var, then_, size_int (0));
   else
     expr_object_size (osi, var, then_);
 
@@ -952,7 +970,7 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
     return reexamine;
 
   if (TREE_CODE (else_) == SSA_NAME)
-    reexamine |= merge_object_sizes (osi, var, else_, 0);
+    reexamine |= merge_object_sizes (osi, var, else_, size_int (0));
   else
     expr_object_size (osi, var, else_);
 
@@ -969,11 +987,11 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
    object size is object size of the first operand minus the constant.
    If the constant is bigger than the number of remaining bytes until the
    end of the object, object size is 0, but if it is instead a pointer
-   subtraction, object size is unknown (object_size_type).
+   subtraction, object size is size_unknown (object_size_type).
    To differentiate addition from subtraction, ADDR_EXPR returns
-   unknown (object_size_type) for all objects bigger than half of the address
-   space, and constants less than half of the address space are considered
-   addition, while bigger constants subtraction.
+   size_unknown (object_size_type) for all objects bigger than half of the
+   address space, and constants less than half of the address space are
+   considered addition, while bigger constants subtraction.
    For a memcpy like GIMPLE_CALL that always returns one of its arguments, the
    object size is object size of that argument.
    Otherwise, object size is the maximum of object sizes of variables
@@ -996,7 +1014,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 	{
 	  /* Initialize to 0 for maximum size and M1U for minimum size so that
 	     it gets immediately overridden.  */
-	  object_sizes_set_force (osi, varno, unknown (object_size_type ^ 2));
+	  object_sizes_initialize (osi, varno);
 	}
       else
 	{
@@ -1039,7 +1057,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
           {
             if (TREE_CODE (rhs) == SSA_NAME
                 && POINTER_TYPE_P (TREE_TYPE (rhs)))
-              reexamine = merge_object_sizes (osi, var, rhs, 0);
+	      reexamine = merge_object_sizes (osi, var, rhs, size_int (0));
             else
               expr_object_size (osi, var, rhs);
           }
@@ -1056,7 +1074,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
           {
             if (TREE_CODE (arg) == SSA_NAME
                 && POINTER_TYPE_P (TREE_TYPE (arg)))
-              reexamine = merge_object_sizes (osi, var, arg, 0);
+	      reexamine = merge_object_sizes (osi, var, arg, size_int (0));
             else
               expr_object_size (osi, var, arg);
           }
@@ -1067,7 +1085,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 
     case GIMPLE_ASM:
       /* Pointers defined by __asm__ statements can point anywhere.  */
-      object_sizes_set (osi, varno, unknown (object_size_type));
+      object_sizes_set (osi, varno, size_unknown (object_size_type));
       break;
 
     case GIMPLE_NOP:
@@ -1076,7 +1094,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 	expr_object_size (osi, var, SSA_NAME_VAR (var));
       else
 	/* Uninitialized SSA names point nowhere.  */
-	object_sizes_set (osi, varno, unknown (object_size_type));
+	object_sizes_set (osi, varno, size_unknown (object_size_type));
       break;
 
     case GIMPLE_PHI:
@@ -1091,7 +1109,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 	      break;
 
 	    if (TREE_CODE (rhs) == SSA_NAME)
-	      reexamine |= merge_object_sizes (osi, var, rhs, 0);
+	      reexamine |= merge_object_sizes (osi, var, rhs, size_int (0));
 	    else if (osi->pass == 0)
 	      expr_object_size (osi, var, rhs);
 	  }
@@ -1142,7 +1160,7 @@ check_for_plus_in_loops_1 (struct object_size_info *osi, tree var,
 	      --sp;
 	      bitmap_clear_bit (osi->reexamine, *sp);
 	      bitmap_set_bit (computed[osi->object_size_type], *sp);
-	      object_sizes_set_force (osi, *sp, 0);
+	      object_sizes_set (osi, *sp, size_int (0));
 	      if (*sp == varno)
 		break;
 	    }
@@ -1333,17 +1351,16 @@ object_sizes_execute (function *fun, bool insert_min_max_p)
 			  || TREE_CODE (ptr) == SSA_NAME))
 		    {
 		      tree type = TREE_TYPE (lhs);
-		      unsigned HOST_WIDE_INT bytes;
+		      tree bytes;
 		      if (compute_builtin_object_size (ptr, object_size_type,
-						       &bytes)
-			  && wi::fits_to_tree_p (bytes, type))
+						       &bytes))
 			{
 			  tree tem = make_ssa_name (type);
 			  gimple_call_set_lhs (call, tem);
 			  enum tree_code code
 			    = (object_size_type & OST_MINIMUM
 			       ? MAX_EXPR : MIN_EXPR);
-			  tree cst = build_int_cstu (type, bytes);
+			  tree cst = fold_convert (type, bytes);
 			  gimple *g
 			    = gimple_build_assign (lhs, code, tem, cst);
 			  gsi_insert_after (&i, g, GSI_NEW_STMT);
diff --git a/gcc/tree-object-size.h b/gcc/tree-object-size.h
index ef18aea50db..b2d6a58324c 100644
--- a/gcc/tree-object-size.h
+++ b/gcc/tree-object-size.h
@@ -22,7 +22,7 @@ along with GCC; see the file COPYING3.  If not see
 
 extern void init_object_sizes (void);
 extern void fini_object_sizes (void);
-extern bool compute_builtin_object_size (tree, int, unsigned HOST_WIDE_INT *);
+extern bool compute_builtin_object_size (tree, int, tree *);
 extern tree decl_init_size (tree, bool);
 
 #endif  // GCC_TREE_OBJECT_SIZE_H
diff --git a/gcc/ubsan.c b/gcc/ubsan.c
index ba9adf0ad96..2b1b7571e25 100644
--- a/gcc/ubsan.c
+++ b/gcc/ubsan.c
@@ -2160,33 +2160,33 @@ instrument_object_size (gimple_stmt_iterator *gsi, tree t, bool is_lhs)
   if (decl_p)
     base_addr = build1 (ADDR_EXPR,
 			build_pointer_type (TREE_TYPE (base)), base);
-  unsigned HOST_WIDE_INT size;
-  if (compute_builtin_object_size (base_addr, 0, &size))
-    sizet = build_int_cst (sizetype, size);
-  else if (optimize)
-    {
-      if (LOCATION_LOCUS (loc) == UNKNOWN_LOCATION)
-	loc = input_location;
-      /* Generate __builtin_object_size call.  */
-      sizet = builtin_decl_explicit (BUILT_IN_OBJECT_SIZE);
-      sizet = build_call_expr_loc (loc, sizet, 2, base_addr,
-				   integer_zero_node);
-      sizet = force_gimple_operand_gsi (gsi, sizet, false, NULL_TREE, true,
-					GSI_SAME_STMT);
-      /* If the call above didn't end up being an integer constant, go one
-	 statement back and get the __builtin_object_size stmt.  Save it,
-	 we might need it later.  */
-      if (SSA_VAR_P (sizet))
+  if (!compute_builtin_object_size (base_addr, 0, &sizet))
+    {
+      if (optimize)
 	{
-	  gsi_prev (gsi);
-	  bos_stmt = gsi_stmt (*gsi);
+	  if (LOCATION_LOCUS (loc) == UNKNOWN_LOCATION)
+	    loc = input_location;
+	  /* Generate __builtin_object_size call.  */
+	  sizet = builtin_decl_explicit (BUILT_IN_OBJECT_SIZE);
+	  sizet = build_call_expr_loc (loc, sizet, 2, base_addr,
+				       integer_zero_node);
+	  sizet = force_gimple_operand_gsi (gsi, sizet, false, NULL_TREE, true,
+					    GSI_SAME_STMT);
+	  /* If the call above didn't end up being an integer constant, go one
+	     statement back and get the __builtin_object_size stmt.  Save it,
+	     we might need it later.  */
+	  if (SSA_VAR_P (sizet))
+	    {
+	      gsi_prev (gsi);
+	      bos_stmt = gsi_stmt (*gsi);
 
-	  /* Move on to where we were.  */
-	  gsi_next (gsi);
+	      /* Move on to where we were.  */
+	      gsi_next (gsi);
+	    }
 	}
+      else
+	return;
     }
-  else
-    return;
 
   /* Generate UBSAN_OBJECT_SIZE (ptr, ptr+sizeof(*ptr)-base, objsize, ckind)
      call.  */
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH 04/10] tree-object-size: Single pass dependency loop resolution
  2021-11-09 19:01 [PATCH 00/10] __builtin_dynamic_object_size Siddhesh Poyarekar
                   ` (2 preceding siblings ...)
  2021-11-09 19:01 ` [PATCH 03/10] tree-object-size: Use tree instead of HOST_WIDE_INT Siddhesh Poyarekar
@ 2021-11-09 19:01 ` Siddhesh Poyarekar
  2021-11-23 12:07   ` Jakub Jelinek
  2021-11-09 19:01 ` [PATCH 05/10] __builtin_dynamic_object_size: Recognize builtin Siddhesh Poyarekar
                   ` (10 subsequent siblings)
  14 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-09 19:01 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Use SSA names as placeholders self-referencing variables to generate
expressions for object sizes and then reduce those size expressions
to constants instead of repeatedly walking through statements.

This change also makes sure that object sizes for an SSA name are
updated at most twice, once if there is a dependency loop and then the
final time upon computation of object size.  Iteration to deduce the
final size is now done on the size expressions instead of walking
through the object references.

Added test to include a case where __builtin_object_size incorrectly
returned the minimum object size as zero.

gcc/ChangeLog:

	* tree-object-size.c (struct object_size_info): Remove pass,
	changed, depths, stack and tos.  Add tempsize_objs.
	(OST_TREE_CODE): New macro.
	(expr_object_size, merge_object_sizes, plus_stmt_object_size,
	cond_expr_object_size): Return tree and don't pass pointer tree.
	(object_sizes_set): Return void.  Adjust implementation to hold
	placeholder SSA names and their values in different slots.
	(addr_object_size): Adjust for single pass.
	(reducing_size, estimate_size, resolve_dependency_loops): New
	functions.
	(compute_builtin_object_size): Call them.
	(make_tempsize): New function.
	(collect_object_sizes_for): Use it.  Update object_sizes at most
	twice.
	(check_for_plus_in_loops, check_for_plus_in_loops_1): Remove
	functions.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-object-size-1.c (test6): New test for
	passthrough.
	* gcc.dg/builtin-object-size-2.c: Likewise.
	* gcc.dg/builtin-object-size-3.c: Likewise.
	* gcc.dg/builtin-object-size-4.c: Likewise.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 gcc/testsuite/gcc.dg/builtin-object-size-1.c |  16 +
 gcc/testsuite/gcc.dg/builtin-object-size-2.c |  16 +
 gcc/testsuite/gcc.dg/builtin-object-size-3.c |  16 +
 gcc/testsuite/gcc.dg/builtin-object-size-4.c |  16 +
 gcc/tree-object-size.c                       | 724 +++++++++----------
 5 files changed, 424 insertions(+), 364 deletions(-)

diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
index 8cdae49a6b1..b270e8d8827 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
@@ -376,6 +376,7 @@ test6 (size_t x)
 {
   struct T { char buf[64]; char buf2[64]; } t;
   char *p = &t.buf[8];
+  char *r = t.buf2;
   size_t i;
 
   for (i = 0; i < x; ++i)
@@ -383,6 +384,21 @@ test6 (size_t x)
   if (__builtin_object_size (p, 0) != sizeof (t) - 8)
     abort ();
   memset (p, ' ', sizeof (t) - 8 - 4 * 4);
+  p = &t.buf[8];
+  for (i = 0; i < x; i++)
+    {
+      r = __builtin_memcpy (r, t.buf, i);
+      p = r + 1;
+    }
+  if (__builtin_object_size (p, 0) != sizeof (t) - 8)
+    abort ();
+  for (i = 0; i < x; i++)
+    {
+      r = __builtin_mempcpy (r, t.buf, i);
+      p = r + 1;
+    }
+  if (__builtin_object_size (p, 0) != -1)
+    abort ();
 }
 
 void
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
index ad2dd296a9a..ea11a17b6d8 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-2.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
@@ -335,6 +335,7 @@ test5 (size_t x)
 {
   struct T { char buf[64]; char buf2[64]; } t;
   char *p = &t.buf[8];
+  char *r = t.buf2;
   size_t i;
 
   for (i = 0; i < x; ++i)
@@ -342,6 +343,21 @@ test5 (size_t x)
   if (__builtin_object_size (p, 1) != sizeof (t.buf) - 8)
     abort ();
   memset (p, ' ', sizeof (t.buf) - 8 - 4 * 4);
+  p = &t.buf[8];
+  for (i = 0; i < x; i++)
+    {
+      r = __builtin_memcpy (r, t.buf, i);
+      p = r + 1;
+    }
+  if (__builtin_object_size (p, 1) != sizeof (t.buf2) - 1)
+    abort ();
+  for (i = 0; i < x; i++)
+    {
+      r = __builtin_mempcpy (r, t.buf, i);
+      p = r + 1;
+    }
+  if (__builtin_object_size (p, 1) != -1)
+    abort ();
 }
 
 void
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
index d5ca5047ee9..2d68925077e 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-3.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
@@ -382,6 +382,7 @@ test6 (size_t x)
 {
   struct T { char buf[64]; char buf2[64]; } t;
   char *p = &t.buf[8];
+  char *r = t.buf2;
   size_t i;
 
   for (i = 0; i < x; ++i)
@@ -389,6 +390,21 @@ test6 (size_t x)
   if (__builtin_object_size (p, 2) != 0)
     abort ();
   memset (p, ' ', sizeof (t) - 8 - 4 * 4);
+  p = &t.buf[8];
+  for (i = 0; i < x; i++)
+    {
+      r = __builtin_memcpy (r, t.buf, i);
+      p = r + 1;
+    }
+  if (__builtin_object_size (p, 2) != sizeof (t.buf2) - 1)
+    abort ();
+  for (i = 0; i < x; i++)
+    {
+      r = __builtin_mempcpy (r, t.buf, i);
+      p = r + 1;
+    }
+  if (__builtin_object_size (p, 2) != 0)
+    abort ();
 }
 
 void
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
index 9f159e36a0f..dd7f6d7336d 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-4.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
@@ -348,6 +348,7 @@ test5 (size_t x)
 {
   struct T { char buf[64]; char buf2[64]; } t;
   char *p = &t.buf[8];
+  char *r = t.buf2;
   size_t i;
 
   for (i = 0; i < x; ++i)
@@ -355,6 +356,21 @@ test5 (size_t x)
   if (__builtin_object_size (p, 3) != 0)
     abort ();
   memset (p, ' ', sizeof (t.buf) - 8 - 4 * 4);
+  p = &t.buf[8];
+  for (i = 0; i < x; i++)
+    {
+      r = __builtin_memcpy (r, t.buf, i);
+      p = r + 1;
+    }
+  if (__builtin_object_size (p, 3) != sizeof (t.buf) - 8)
+    abort ();
+  for (i = 0; i < x; i++)
+    {
+      r = __builtin_mempcpy (r, t.buf, i);
+      p = r + 1;
+    }
+  if (__builtin_object_size (p, 3) != 0)
+    abort ();
 }
 
 void
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index 4b9c45c6af2..e48120559d3 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -38,11 +38,8 @@ along with GCC; see the file COPYING3.  If not see
 struct object_size_info
 {
   int object_size_type;
-  unsigned char pass;
-  bool changed;
   bitmap visited, reexamine;
-  unsigned int *depths;
-  unsigned int *stack, *tos;
+  vec<unsigned> tempsize_objs;
 };
 
 enum
@@ -52,27 +49,41 @@ enum
   OST_END = 4,
 };
 
+#define OST_TREE_CODE(_ost) ((_ost) & OST_MINIMUM ? MIN_EXPR : MAX_EXPR)
+
 static tree compute_object_offset (const_tree, const_tree);
 static bool addr_object_size (struct object_size_info *,
 			      const_tree, int, tree *);
 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 void expr_object_size (struct object_size_info *, tree, tree);
-static bool merge_object_sizes (struct object_size_info *, tree, tree, tree);
-static bool plus_stmt_object_size (struct object_size_info *, tree, gimple *);
-static bool cond_expr_object_size (struct object_size_info *, tree, gimple *);
+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 void init_offset_limit (void);
-static void check_for_plus_in_loops (struct object_size_info *, tree);
-static void check_for_plus_in_loops_1 (struct object_size_info *, tree,
-				       unsigned int);
 
 /* object_sizes[0] is upper bound for number of bytes till the end of
    the object.
    object_sizes[1] is upper bound for number of bytes till the end of
    the subobject (innermost array or field with address taken).
    object_sizes[2] is lower bound for number of bytes till the end of
-   the object and object_sizes[3] lower bound for subobject.  */
+   the object and object_sizes[3] lower bound for subobject.
+
+   Each array contains sizes subscripted by an SSA name version, which could be
+   of two types and correspondingly, their values have special properties:
+
+   When the SSA name is an object:
+   - The value is either a constant or a gimple variable that is the size of
+     the object.
+   - If the value is an SSA variable, it could be a placeholder SSA name, which
+   again is a subscript in object_sizes.
+
+   When the SSA name is a placeholder:
+   - The name version is also set in the osi.reexamine bitmap
+   - The value at its index in osi.tempsize_objs is the SSA name version of the
+     object whose size it is.
+   - Its value in object_sizes is an expression that evaluates to the size.  */
 static vec<tree> object_sizes[OST_END];
 
 /* Bitmaps what object sizes have been computed already.  */
@@ -174,18 +185,33 @@ object_sizes_initialize (struct object_size_info *osi, unsigned varno,
 /* Set size for VARNO corresponding to OSI to VAL if it is the new minimum or
    maximum.  */
 
-static inline bool
+static inline void
 object_sizes_set (struct object_size_info *osi, unsigned varno, tree val)
 {
   int object_size_type = osi->object_size_type;
-  tree oldval = object_sizes[object_size_type][varno];
-
-  enum tree_code code = object_size_type & OST_MINIMUM ? MIN_EXPR : MAX_EXPR;
-
-  if (compare_tree_int (oldval, initval (object_size_type)) != 0)
-    val = size_binop (code, val, oldval);
-  object_sizes[object_size_type][varno] = val;
-  return tree_int_cst_compare (val, oldval) != 0;
+  tree curval = object_sizes[object_size_type][varno];
+
+  /* Object size is set at most twice, once to put in an SSA name to resolve
+     dependency loops and the second time to set the final size.  */
+  gcc_checking_assert (TREE_CODE (curval) == SSA_NAME
+		       || (tree_fits_uhwi_p (curval)
+			   && !compare_tree_int (curval,
+						 initval (object_size_type))));
+
+  /* For self-referencing objects, update the element that the size SSA name
+     refers to, not the object SSA name, except if the size is unknown, in
+     which case we update both.  */
+  if (TREE_CODE (curval) == SSA_NAME
+      && bitmap_bit_p (osi->reexamine, SSA_NAME_VERSION (curval))
+      && (TREE_CODE (val) != SSA_NAME
+	  || SSA_NAME_VERSION (curval) != SSA_NAME_VERSION (val)))
+    {
+      object_sizes[object_size_type][SSA_NAME_VERSION (curval)] = val;
+      if (size_unknown_p (val, object_size_type))
+	object_sizes[object_size_type][varno] = val;
+    }
+  else
+    object_sizes[object_size_type][varno] = val;
 }
 
 /* Initialize OFFSET_LIMIT variable.  */
@@ -350,8 +376,7 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
       else
 	{
 	  tree var = TREE_OPERAND (pt_var, 0);
-	  if (osi->pass == 0)
-	    collect_object_sizes_for (osi, var);
+	  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));
@@ -616,6 +641,190 @@ pass_through_call (const gcall *call)
   return NULL_TREE;
 }
 
+/* Recursively look for presence of ORIG in EXPR.  Return true if it was found
+   and there was a MINUS_EXPR in the pathway.  */
+
+static bool
+reducing_size (tree orig, tree expr, bool found_minus)
+{
+  switch (TREE_CODE (expr))
+    {
+    case SSA_NAME:
+      if (SSA_NAME_VERSION (orig) == SSA_NAME_VERSION (expr))
+	return found_minus;
+      return false;
+    case MIN_EXPR:
+    case MAX_EXPR:
+      return (reducing_size (orig, TREE_OPERAND (expr, 0), found_minus)
+	      || reducing_size (orig, TREE_OPERAND (expr, 1), found_minus));
+    case PLUS_EXPR:
+      /* Negative object offsets are not supported.  */
+      gcc_checking_assert (TREE_CODE (TREE_OPERAND (expr, 1)) == INTEGER_CST);
+      gcc_checking_assert (compare_tree_int (TREE_OPERAND (expr, 1),
+					     offset_limit) > 0);
+      /* Fall through.  */
+    case MINUS_EXPR:
+      gcc_checking_assert (TREE_CODE (TREE_OPERAND (expr, 1)) == INTEGER_CST);
+      if (reducing_size (orig, TREE_OPERAND (expr, 0), true))
+	return true;
+      /* Fall through.  */
+    case INTEGER_CST:
+    default:
+      return false;
+    }
+}
+
+/* Return a constant size estimate for the input SZEXPR and update it with a
+   simplified expression.  */
+
+static tree
+estimate_size (object_size_info *osi, tree size)
+{
+  enum tree_code code = TREE_CODE (size);
+  int object_size_type = osi->object_size_type;
+
+  switch (code)
+    {
+    case SSA_NAME:
+	{
+	  unsigned num = SSA_NAME_VERSION (size);
+	  if (!bitmap_bit_p (osi->reexamine, num))
+	    return size;
+	  return object_sizes_get (osi, osi->tempsize_objs[num]);
+	}
+    case MIN_EXPR:
+    case MAX_EXPR:
+	{
+	  tree op0 = estimate_size (osi, TREE_OPERAND (size, 0));
+	  tree op1 = estimate_size (osi, TREE_OPERAND (size, 1));
+	  if (size_unknown_p (op0, object_size_type)
+	      || size_unknown_p (op1, object_size_type))
+	    return size_unknown (object_size_type);
+	  return size_binop (code, op0, op1);
+	}
+    case MINUS_EXPR:
+    case PLUS_EXPR:
+	{
+	  tree ret = estimate_size (osi, TREE_OPERAND (size, 0));
+
+	  if (size_unknown_p (ret, object_size_type))
+	    return size_unknown (object_size_type);
+
+	  tree off = TREE_OPERAND (size, 1);
+	  gcc_checking_assert (TREE_CODE (off) == INTEGER_CST);
+
+	  if (code == PLUS_EXPR)
+	    off = fold_build1 (NEGATE_EXPR, sizetype, off);
+
+	  if (tree_fits_uhwi_p (ret) && tree_int_cst_le (ret, off))
+	    return size_int (0);
+	  return size_binop (MINUS_EXPR, ret, off);
+	}
+    case INTEGER_CST:
+    default:
+      return size;
+    }
+}
+
+/* Replace dependency loop SSA names with their actual values.  */
+
+static void
+resolve_dependency_loops (struct object_size_info *osi)
+{
+  bitmap_iterator bi;
+  unsigned int i;
+  int object_size_type = osi->object_size_type;
+
+  /* Step 1: Update the self-referencing sizes until they don't
+     change anymore.  */
+  bool changed;
+  bitmap tempsize_free = BITMAP_ALLOC (NULL);
+  do
+    {
+      changed = false;
+      bitmap_and_compl_into (osi->reexamine, tempsize_free);
+      EXECUTE_IF_SET_IN_BITMAP (osi->reexamine, 0, i, bi)
+	{
+	  unsigned varno = osi->tempsize_objs[i];
+
+	  tree cur = object_sizes_get (osi, varno);
+
+	  if (size_unknown_p (cur, object_size_type))
+	    {
+	      bitmap_set_bit (tempsize_free, i);
+	      continue;
+	    }
+
+	  tree szexpr = object_sizes_get (osi, i);
+
+	  /* First run, initialize.  */
+	  if (TREE_CODE (cur) == SSA_NAME)
+	    {
+	      if (reducing_size (cur, szexpr, false))
+		{
+		  cur = size_int (0);
+		  object_sizes_initialize (osi, varno, cur);
+		  if (object_size_type & OST_MINIMUM)
+		    bitmap_set_bit (tempsize_free, i);
+		}
+	      else
+		{
+		  cur = size_initval (object_size_type);
+		  object_sizes_initialize (osi, varno, cur);
+		}
+	      changed = true;
+	    }
+
+	  tree sz = estimate_size (osi, szexpr);
+
+	  /* It depends on some self-referencing size that has not been
+	     initialized yet.  */
+	  if (TREE_CODE (sz) != INTEGER_CST)
+	    continue;
+
+	  if (size_unknown_p (sz, object_size_type))
+	    bitmap_set_bit (tempsize_free, i);
+	  /* If we have a new estimate, then update it.  */
+	  if (tree_int_cst_compare (cur, sz) != 0)
+	    {
+	      object_sizes_initialize (osi, varno, sz);
+	      changed = true;
+	    }
+	}
+    }
+  while (changed);
+
+  /* Now we only need to dump and free all SSAs.  */
+  bitmap_ior_into_and_free (osi->reexamine, &tempsize_free);
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "After dependency resolution:\n");
+      EXECUTE_IF_SET_IN_BITMAP (osi->reexamine, 0, i, bi)
+	{
+	  fprintf (dump_file, "  ");
+	  print_generic_expr (dump_file, ssa_name (i), dump_flags);
+	  fprintf (dump_file, ": ");
+	  print_generic_expr (dump_file,
+			      object_sizes_get (osi, osi->tempsize_objs[i]),
+			      dump_flags);
+	  fprintf (dump_file, "\n");
+	}
+    }
+
+  /* Step 2: Update all remaining non-constant sizes.  */
+  EXECUTE_IF_SET_IN_BITMAP (computed[object_size_type], 0, i, bi)
+    {
+      tree szexpr = object_sizes_get (osi, i);
+      if (TREE_CODE (szexpr) == INTEGER_CST)
+	continue;
+      tree sz = estimate_size (osi, szexpr);
+      object_sizes_initialize (osi, i, sz);
+    }
+
+  /* Release all the SSA names we created.  */
+  EXECUTE_IF_SET_IN_BITMAP (osi->reexamine, 0, i, bi)
+    release_ssa_name (ssa_name (i));
+}
 
 /* Compute __builtin_object_size value for PTR and set *PSIZE to
    the resulting value.  If the declared object is known and PDECL
@@ -693,77 +902,11 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 
       osi.visited = BITMAP_ALLOC (NULL);
       osi.reexamine = BITMAP_ALLOC (NULL);
-      osi.depths = NULL;
-      osi.stack = NULL;
-      osi.tos = NULL;
-
-      /* First pass: walk UD chains, compute object sizes that
-	 can be computed.  osi.reexamine bitmap at the end will
-	 contain what variables were found in dependency cycles
-	 and therefore need to be reexamined.  */
-      osi.pass = 0;
-      osi.changed = false;
+      osi.tempsize_objs.create (0);
       collect_object_sizes_for (&osi, ptr);
 
-      /* Second pass: keep recomputing object sizes of variables
-	 that need reexamination, until no object sizes are
-	 increased or all object sizes are computed.  */
-      if (! bitmap_empty_p (osi.reexamine))
-	{
-	  bitmap reexamine = BITMAP_ALLOC (NULL);
-
-	  /* If looking for minimum instead of maximum object size,
-	     detect cases where a pointer is increased in a loop.
-	     Although even without this detection pass 2 would eventually
-	     terminate, it could take a long time.  If a pointer is
-	     increasing this way, we need to assume 0 object size.
-	     E.g. p = &buf[0]; while (cond) p = p + 4;  */
-	  if (object_size_type & 2)
-	    {
-	      osi.depths = XCNEWVEC (unsigned int, num_ssa_names);
-	      osi.stack = XNEWVEC (unsigned int, num_ssa_names);
-	      osi.tos = osi.stack;
-	      osi.pass = 1;
-	      /* collect_object_sizes_for is changing
-		 osi.reexamine bitmap, so iterate over a copy.  */
-	      bitmap_copy (reexamine, osi.reexamine);
-	      EXECUTE_IF_SET_IN_BITMAP (reexamine, 0, i, bi)
-		if (bitmap_bit_p (osi.reexamine, i))
-		  check_for_plus_in_loops (&osi, ssa_name (i));
-
-	      free (osi.depths);
-	      osi.depths = NULL;
-	      free (osi.stack);
-	      osi.stack = NULL;
-	      osi.tos = NULL;
-	    }
-
-	  do
-	    {
-	      osi.pass = 2;
-	      osi.changed = false;
-	      /* collect_object_sizes_for is changing
-		 osi.reexamine bitmap, so iterate over a copy.  */
-	      bitmap_copy (reexamine, osi.reexamine);
-	      EXECUTE_IF_SET_IN_BITMAP (reexamine, 0, i, bi)
-		if (bitmap_bit_p (osi.reexamine, i))
-		  {
-		    collect_object_sizes_for (&osi, ssa_name (i));
-		    if (dump_file && (dump_flags & TDF_DETAILS))
-		      {
-			fprintf (dump_file, "Reexamining ");
-			print_generic_expr (dump_file, ssa_name (i),
-					    dump_flags);
-			fprintf (dump_file, "\n");
-		      }
-		  }
-	    }
-	  while (osi.changed);
-
-	  BITMAP_FREE (reexamine);
-	}
-      EXECUTE_IF_SET_IN_BITMAP (osi.reexamine, 0, i, bi)
-	bitmap_set_bit (computed[object_size_type], i);
+      if (!bitmap_empty_p (osi.reexamine))
+	resolve_dependency_loops (&osi);
 
       /* Debugging dumps.  */
       if (dump_file)
@@ -784,6 +927,7 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 	      }
 	}
 
+      osi.tempsize_objs.release ();
       BITMAP_FREE (osi.reexamine);
       BITMAP_FREE (osi.visited);
     }
@@ -792,107 +936,107 @@ compute_builtin_object_size (tree ptr, int object_size_type,
   return !size_unknown_p (*psize, object_size_type);
 }
 
+/* Make a temporary placeholder variable for size.  */
+
+static tree
+make_tempsize (struct object_size_info *osi, unsigned varno)
+{
+  tree ssa = make_ssa_name (sizetype);
+  unsigned ssano = SSA_NAME_VERSION (ssa);
+
+  if (dump_file)
+    {
+      print_generic_expr (dump_file, ssa_name (varno), dump_flags);
+      fprintf (dump_file, ": Making temp SSA name: ");
+      print_generic_expr (dump_file, ssa, dump_flags);
+      fprintf (dump_file, "\n");
+    }
+
+  object_sizes_grow (osi->object_size_type);
+  if (osi->tempsize_objs.length () < num_ssa_names)
+    osi->tempsize_objs.safe_grow (num_ssa_names);
+
+  bitmap_set_bit (osi->reexamine, ssano);
+  osi->tempsize_objs[ssano] = varno;
+
+  return ssa;
+}
+
 /* Compute object_sizes for PTR, defined to VALUE, which is not an SSA_NAME.  */
 
-static void
-expr_object_size (struct object_size_info *osi, tree ptr, tree value)
+static tree
+expr_object_size (struct object_size_info *osi, tree value)
 {
   int object_size_type = osi->object_size_type;
-  unsigned int varno = SSA_NAME_VERSION (ptr);
   tree bytes;
 
-  gcc_assert (!object_sizes_unknown_p (object_size_type, varno));
-  gcc_assert (osi->pass == 0);
-
   if (TREE_CODE (value) == WITH_SIZE_EXPR)
     value = TREE_OPERAND (value, 0);
 
-  /* Pointer variables should have been handled by merge_object_sizes.  */
+  /* Pointer variables should have been handled by ssa_object_size.  */
   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);
-  else
-    bytes = size_unknown (object_size_type);
+  if (TREE_CODE (value) == ADDR_EXPR
+      && addr_object_size (osi, value, object_size_type, &bytes))
+    return bytes;
 
-  object_sizes_set (osi, varno, bytes);
+  return size_unknown (object_size_type);
 }
 
 
 /* Compute object_sizes for PTR, defined to the result of a call.  */
 
-static void
-call_object_size (struct object_size_info *osi, tree ptr, gcall *call)
+static tree
+call_object_size (struct object_size_info *osi, gcall *call)
 {
   int object_size_type = osi->object_size_type;
-  unsigned int varno = SSA_NAME_VERSION (ptr);
 
   gcc_assert (is_gimple_call (call));
 
-  gcc_assert (!object_sizes_unknown_p (object_size_type, varno));
-  gcc_assert (osi->pass == 0);
-
-  object_sizes_set (osi, varno, alloc_object_size (call, object_size_type));
+  return alloc_object_size (call, object_size_type);
 }
 
 
 /* Compute object_sizes for PTR, defined to an unknown value.  */
 
-static void
-unknown_object_size (struct object_size_info *osi, tree ptr)
+static tree
+unknown_object_size (struct object_size_info *osi)
 {
   int object_size_type = osi->object_size_type;
-  unsigned int varno = SSA_NAME_VERSION (ptr);
-
-  gcc_checking_assert (!object_sizes_unknown_p (object_size_type, varno));
-  gcc_checking_assert (osi->pass == 0);
-
-  object_sizes_set (osi, varno, size_unknown (object_size_type));
+  return size_unknown (object_size_type);
 }
 
 
-/* Merge object sizes of ORIG + OFFSET into DEST.  Return true if
-   the object size might need reexamination later.  */
+/* Return object size of ORIG + OFFSET.  */
 
-static bool
-merge_object_sizes (struct object_size_info *osi, tree dest, tree orig,
-		    tree offset)
+static tree
+ssa_object_size (struct object_size_info *osi, tree orig, tree offset)
 {
   int object_size_type = osi->object_size_type;
-  unsigned int varno = SSA_NAME_VERSION (dest);
   tree orig_bytes;
 
-  if (object_sizes_unknown_p (object_size_type, varno))
-    return false;
   if (compare_tree_int (offset, offset_limit) >= 0)
-    {
-      object_sizes_set (osi, varno, size_unknown (object_size_type));
-      return false;
-    }
+    return size_unknown (object_size_type);
 
-  if (osi->pass == 0)
-    collect_object_sizes_for (osi, orig);
+  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))
+  if (!size_unknown_p (orig_bytes, object_size_type)
+      && !integer_zerop (offset))
     orig_bytes = size_for_offset (orig_bytes, offset);
 
-  osi->changed = object_sizes_set (osi, varno, orig_bytes);
-
-  return bitmap_bit_p (osi->reexamine, SSA_NAME_VERSION (orig));
+  return orig_bytes;
 }
 
 
 /* Compute object_sizes for VAR, defined to the result of an assignment
-   with operator POINTER_PLUS_EXPR.  Return true if the object size might
-   need reexamination  later.  */
+   with operator POINTER_PLUS_EXPR.  */
 
-static bool
-plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
+static tree
+plus_stmt_object_size (struct object_size_info *osi, gimple *stmt)
 {
   int object_size_type = osi->object_size_type;
-  unsigned int varno = SSA_NAME_VERSION (var);
   tree bytes;
   tree op0, op1;
 
@@ -911,16 +1055,13 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   else
     gcc_unreachable ();
 
-  if (object_sizes_unknown_p (object_size_type, varno))
-    return false;
-
   /* 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)
-	return merge_object_sizes (osi, var, op0, op1);
+	bytes = ssa_object_size (osi, op0, op1);
       else
 	{
           /* op0 will be ADDR_EXPR here.  */
@@ -929,52 +1070,43 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
 	    ;
 	  else if (compare_tree_int (op1, offset_limit) > 0)
 	    bytes = size_unknown (object_size_type);
-	  else
+	  else if (!integer_zerop (op1))
 	    bytes = size_for_offset (bytes, op1);
 	}
     }
   else
     bytes = size_unknown (object_size_type);
 
-  object_sizes_set (osi, varno, bytes);
-  return false;
+  return bytes;
 }
 
 
 /* Compute object_sizes for VAR, defined at STMT, which is
-   a COND_EXPR.  Return true if the object size might need reexamination
-   later.  */
+   a COND_EXPR.  */
 
-static bool
-cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
+static tree
+cond_expr_object_size (struct object_size_info *osi, gimple *stmt)
 {
   tree then_, else_;
   int object_size_type = osi->object_size_type;
-  unsigned int varno = SSA_NAME_VERSION (var);
-  bool reexamine = false;
+  tree thenbytes, elsebytes;
 
   gcc_assert (gimple_assign_rhs_code (stmt) == COND_EXPR);
 
-  if (object_sizes_unknown_p (object_size_type, varno))
-    return false;
-
   then_ = gimple_assign_rhs2 (stmt);
   else_ = gimple_assign_rhs3 (stmt);
 
   if (TREE_CODE (then_) == SSA_NAME)
-    reexamine |= merge_object_sizes (osi, var, then_, size_int (0));
+    thenbytes = ssa_object_size (osi, then_, size_int (0));
   else
-    expr_object_size (osi, var, then_);
-
-  if (object_sizes_unknown_p (object_size_type, varno))
-    return reexamine;
+    thenbytes = expr_object_size (osi, then_);
 
   if (TREE_CODE (else_) == SSA_NAME)
-    reexamine |= merge_object_sizes (osi, var, else_, size_int (0));
+    elsebytes = ssa_object_size (osi, else_, size_int (0));
   else
-    expr_object_size (osi, var, else_);
+    elsebytes = expr_object_size (osi, else_);
 
-  return reexamine;
+  return size_binop (OST_TREE_CODE (object_size_type), thenbytes, elsebytes);
 }
 
 /* Compute object sizes for VAR.
@@ -1003,32 +1135,36 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
   int object_size_type = osi->object_size_type;
   unsigned int varno = SSA_NAME_VERSION (var);
   gimple *stmt;
-  bool reexamine;
+  tree res;
 
   if (bitmap_bit_p (computed[object_size_type], varno))
     return;
 
-  if (osi->pass == 0)
+  /* We want to evaluate the self-referencing object only once.  */
+  if (bitmap_set_bit (osi->visited, varno))
     {
-      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);
+    }
+  else
+    {
+      /* Found a dependency loop.  Mark it for later resolution.  */
+      if (dump_file && (dump_flags & TDF_DETAILS))
 	{
-	  /* Initialize to 0 for maximum size and M1U for minimum size so that
-	     it gets immediately overridden.  */
-	  object_sizes_initialize (osi, varno);
+	  fprintf (dump_file, "Found a dependency loop at ");
+	  print_generic_expr (dump_file, var, dump_flags);
+	  fprintf (dump_file, "\n");
 	}
-      else
+      res = object_sizes_get (osi, varno);
+      if (TREE_CODE (res) != SSA_NAME)
+	res = make_tempsize (osi, varno);
+      else if (dump_file)
 	{
-	  /* Found a dependency loop.  Mark the variable for later
-	     re-examination.  */
-	  bitmap_set_bit (osi->reexamine, varno);
-	  if (dump_file && (dump_flags & TDF_DETAILS))
-	    {
-	      fprintf (dump_file, "Found a dependency loop at ");
-	      print_generic_expr (dump_file, var, dump_flags);
-	      fprintf (dump_file, "\n");
-	    }
-	  return;
+	  fprintf (dump_file, "  temp name already assigned: ");
+	  print_generic_expr (dump_file, res, dump_flags);
 	}
+      goto out;
     }
 
   if (dump_file && (dump_flags & TDF_DETAILS))
@@ -1039,192 +1175,86 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
     }
 
   stmt = SSA_NAME_DEF_STMT (var);
-  reexamine = false;
 
   switch (gimple_code (stmt))
     {
     case GIMPLE_ASSIGN:
       {
 	tree rhs = gimple_assign_rhs1 (stmt);
-        if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR
+	if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR
 	    || (gimple_assign_rhs_code (stmt) == ADDR_EXPR
 		&& TREE_CODE (TREE_OPERAND (rhs, 0)) == MEM_REF))
-          reexamine = plus_stmt_object_size (osi, var, stmt);
+	  res = plus_stmt_object_size (osi, stmt);
 	else if (gimple_assign_rhs_code (stmt) == COND_EXPR)
-	  reexamine = cond_expr_object_size (osi, var, stmt);
-        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)))
-	      reexamine = merge_object_sizes (osi, var, rhs, size_int (0));
-            else
-              expr_object_size (osi, var, rhs);
-          }
-        else
-	  unknown_object_size (osi, var);
-        break;
+	  res = cond_expr_object_size (osi, stmt);
+	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));
+	    else
+	       res = expr_object_size (osi, rhs);
+	  }
+	else
+	  res = unknown_object_size (osi);
+	break;
       }
 
     case GIMPLE_CALL:
       {
 	gcall *call_stmt = as_a <gcall *> (stmt);
-        tree arg = pass_through_call (call_stmt);
-        if (arg)
-          {
-            if (TREE_CODE (arg) == SSA_NAME
-                && POINTER_TYPE_P (TREE_TYPE (arg)))
-	      reexamine = merge_object_sizes (osi, var, arg, size_int (0));
-            else
-              expr_object_size (osi, var, arg);
-          }
-        else
-          call_object_size (osi, var, call_stmt);
+	tree arg = pass_through_call (call_stmt);
+	if (arg)
+	  {
+	    if (TREE_CODE (arg) == SSA_NAME
+		&& POINTER_TYPE_P (TREE_TYPE (arg)))
+	      res = ssa_object_size (osi, arg, size_int (0));
+	    else
+	      res = expr_object_size (osi, arg);
+	  }
+	else
+	  res = call_object_size (osi, call_stmt);
 	break;
       }
 
     case GIMPLE_ASM:
       /* Pointers defined by __asm__ statements can point anywhere.  */
-      object_sizes_set (osi, varno, size_unknown (object_size_type));
+      res = size_unknown (object_size_type);
       break;
 
     case GIMPLE_NOP:
       if (SSA_NAME_VAR (var)
 	  && TREE_CODE (SSA_NAME_VAR (var)) == PARM_DECL)
-	expr_object_size (osi, var, SSA_NAME_VAR (var));
+	res = expr_object_size (osi, SSA_NAME_VAR (var));
       else
 	/* Uninitialized SSA names point nowhere.  */
-	object_sizes_set (osi, varno, size_unknown (object_size_type));
+	res = size_unknown (object_size_type);
       break;
 
     case GIMPLE_PHI:
       {
 	unsigned i;
 
+	res = size_initval (object_size_type);
+
 	for (i = 0; i < gimple_phi_num_args (stmt); i++)
 	  {
 	    tree rhs = gimple_phi_arg (stmt, i)->def;
+	    tree phires;
 
 	    if (object_sizes_unknown_p (object_size_type, varno))
 	      break;
 
 	    if (TREE_CODE (rhs) == SSA_NAME)
-	      reexamine |= merge_object_sizes (osi, var, rhs, size_int (0));
-	    else if (osi->pass == 0)
-	      expr_object_size (osi, var, rhs);
-	  }
-	break;
-      }
-
-    default:
-      gcc_unreachable ();
-    }
-
-  if (! reexamine || object_sizes_unknown_p (object_size_type, varno))
-    {
-      bitmap_set_bit (computed[object_size_type], varno);
-      bitmap_clear_bit (osi->reexamine, varno);
-    }
-  else
-    {
-      bitmap_set_bit (osi->reexamine, varno);
-      if (dump_file && (dump_flags & TDF_DETAILS))
-	{
-	  fprintf (dump_file, "Need to reexamine ");
-	  print_generic_expr (dump_file, var, dump_flags);
-	  fprintf (dump_file, "\n");
-	}
-    }
-}
+	      phires = ssa_object_size (osi, rhs, size_int (0));
+	    else
+	      phires = expr_object_size (osi, rhs);
 
+	    res = size_binop (OST_TREE_CODE (object_size_type), res, phires);
 
-/* Helper function for check_for_plus_in_loops.  Called recursively
-   to detect loops.  */
-
-static void
-check_for_plus_in_loops_1 (struct object_size_info *osi, tree var,
-			   unsigned int depth)
-{
-  gimple *stmt = SSA_NAME_DEF_STMT (var);
-  unsigned int varno = SSA_NAME_VERSION (var);
-
-  if (osi->depths[varno])
-    {
-      if (osi->depths[varno] != depth)
-	{
-	  unsigned int *sp;
-
-	  /* Found a loop involving pointer addition.  */
-	  for (sp = osi->tos; sp > osi->stack; )
-	    {
-	      --sp;
-	      bitmap_clear_bit (osi->reexamine, *sp);
-	      bitmap_set_bit (computed[osi->object_size_type], *sp);
-	      object_sizes_set (osi, *sp, size_int (0));
-	      if (*sp == varno)
-		break;
-	    }
-	}
-      return;
-    }
-  else if (! bitmap_bit_p (osi->reexamine, varno))
-    return;
-
-  osi->depths[varno] = depth;
-  *osi->tos++ = varno;
-
-  switch (gimple_code (stmt))
-    {
-
-    case GIMPLE_ASSIGN:
-      {
-        if ((gimple_assign_single_p (stmt)
-             || gimple_assign_unary_nop_p (stmt))
-            && TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME)
-          {
-            tree rhs = gimple_assign_rhs1 (stmt);
-
-            check_for_plus_in_loops_1 (osi, rhs, depth);
-          }
-        else if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
-          {
-            tree basevar = gimple_assign_rhs1 (stmt);
-            tree cst = gimple_assign_rhs2 (stmt);
-
-            gcc_assert (TREE_CODE (cst) == INTEGER_CST);
-
-            check_for_plus_in_loops_1 (osi, basevar,
-                                       depth + !integer_zerop (cst));
-          }
-        else
-          gcc_unreachable ();
-        break;
-      }
-
-    case GIMPLE_CALL:
-      {
-	gcall *call_stmt = as_a <gcall *> (stmt);
-        tree arg = pass_through_call (call_stmt);
-        if (arg)
-          {
-            if (TREE_CODE (arg) == SSA_NAME)
-              check_for_plus_in_loops_1 (osi, arg, depth);
-            else
-              gcc_unreachable ();
-          }
-        break;
-      }
-
-    case GIMPLE_PHI:
-      {
-	unsigned i;
-
-	for (i = 0; i < gimple_phi_num_args (stmt); i++)
-	  {
-	    tree rhs = gimple_phi_arg (stmt, i)->def;
-
-	    if (TREE_CODE (rhs) == SSA_NAME)
-	      check_for_plus_in_loops_1 (osi, rhs, depth);
+	    if (size_unknown_p (phires, object_size_type))
+	      break;
 	  }
 	break;
       }
@@ -1232,43 +1262,9 @@ check_for_plus_in_loops_1 (struct object_size_info *osi, tree var,
     default:
       gcc_unreachable ();
     }
-
-  osi->depths[varno] = 0;
-  osi->tos--;
-}
-
-
-/* Check if some pointer we are computing object size of is being increased
-   within a loop.  If yes, assume all the SSA variables participating in
-   that loop have minimum object sizes 0.  */
-
-static void
-check_for_plus_in_loops (struct object_size_info *osi, tree var)
-{
-  gimple *stmt = SSA_NAME_DEF_STMT (var);
-
-  /* NOTE: In the pre-tuples code, we handled a CALL_EXPR here,
-     and looked for a POINTER_PLUS_EXPR in the pass-through
-     argument, if any.  In GIMPLE, however, such an expression
-     is not a valid call operand.  */
-
-  if (is_gimple_assign (stmt)
-      && gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
-    {
-      tree basevar = gimple_assign_rhs1 (stmt);
-      tree cst = gimple_assign_rhs2 (stmt);
-
-      gcc_assert (TREE_CODE (cst) == INTEGER_CST);
-
-      if (integer_zerop (cst))
-        return;
-
-      osi->depths[SSA_NAME_VERSION (basevar)] = 1;
-      *osi->tos++ = SSA_NAME_VERSION (basevar);
-      check_for_plus_in_loops_1 (osi, var, 2);
-      osi->depths[SSA_NAME_VERSION (basevar)] = 0;
-      osi->tos--;
-    }
+  bitmap_set_bit (computed[object_size_type], varno);
+out:
+  object_sizes_set (osi, varno, res);
 }
 
 
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH 05/10] __builtin_dynamic_object_size: Recognize builtin
  2021-11-09 19:01 [PATCH 00/10] __builtin_dynamic_object_size Siddhesh Poyarekar
                   ` (3 preceding siblings ...)
  2021-11-09 19:01 ` [PATCH 04/10] tree-object-size: Single pass dependency loop resolution Siddhesh Poyarekar
@ 2021-11-09 19:01 ` Siddhesh Poyarekar
  2021-11-23 12:41   ` Jakub Jelinek
  2021-11-09 19:01 ` [PATCH 06/10] tree-object-size: Support dynamic sizes in conditions Siddhesh Poyarekar
                   ` (9 subsequent siblings)
  14 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-09 19:01 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Recognize the __builtin_dynamic_object_size builtin and add paths in the
object size path to deal with it, but treat it like
__builtin_object_size for now.  Also add tests to provide the same
testing coverage for the new builtin name.

gcc/ChangeLog:

	* builtins.def (BUILT_IN_DYNAMIC_OBJECT_SIZE): New builtin.
	* tree-object-size.h (compute_builtin_object_size): Add new
	argument dynamic.
	* builtins.c (expand_builtin, fold_builtin_2): Handle it.
	(fold_builtin_object_size): Handle new builtin and adjust for
	change to compute_builtin_object_size.
	* tree-object-size.c: Include builtins.h.
	(OST_DYNAMIC): New enum value.
	(compute_builtin_object_size): Add new argument dynamic.
	(addr_object_size): Adjust.
	(early_object_sizes_execute_one,
	dynamic_object_sizes_execute_one): New functions.
	(object_sizes_execute): Rename insert_min_max_p argument to
	early. Handle BUILT_IN_DYNAMIC_OBJECT_SIZE and call the new
	functions.
	doc/extend.texi (__builtin_dynamic_object_size): Document new
	builtin.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/builtin-dynamic-object-size1.C: New test.
	* g++.dg/ext/builtin-dynamic-object-size2.C: Likewise.
	* gcc.dg/builtin-dynamic-alloc-size.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-1.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-10.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-11.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-12.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-13.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-14.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-15.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-16.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-17.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-18.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-19.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-2.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-3.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-4.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-5.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-6.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-7.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-8.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-9.c: Likewise.
	* gcc.dg/builtin-object-size-16.c: Adjust to allow inclusion
	from builtin-dynamic-object-size-16.c.
	* gcc.dg/builtin-object-size-17.c: Likewise.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 gcc/builtins.c                                |  14 +-
 gcc/builtins.def                              |   1 +
 gcc/doc/extend.texi                           |  13 ++
 .../g++.dg/ext/builtin-dynamic-object-size1.C |   5 +
 .../g++.dg/ext/builtin-dynamic-object-size2.C |   5 +
 .../gcc.dg/builtin-dynamic-alloc-size.c       |   7 +
 .../gcc.dg/builtin-dynamic-object-size-1.c    |   6 +
 .../gcc.dg/builtin-dynamic-object-size-10.c   |   9 ++
 .../gcc.dg/builtin-dynamic-object-size-11.c   |   7 +
 .../gcc.dg/builtin-dynamic-object-size-12.c   |   5 +
 .../gcc.dg/builtin-dynamic-object-size-13.c   |   5 +
 .../gcc.dg/builtin-dynamic-object-size-14.c   |   5 +
 .../gcc.dg/builtin-dynamic-object-size-15.c   |   5 +
 .../gcc.dg/builtin-dynamic-object-size-16.c   |   7 +
 .../gcc.dg/builtin-dynamic-object-size-17.c   |   8 +
 .../gcc.dg/builtin-dynamic-object-size-18.c   |   8 +
 .../gcc.dg/builtin-dynamic-object-size-19.c   | 104 ++++++++++++
 .../gcc.dg/builtin-dynamic-object-size-2.c    |   6 +
 .../gcc.dg/builtin-dynamic-object-size-3.c    |   6 +
 .../gcc.dg/builtin-dynamic-object-size-4.c    |   6 +
 .../gcc.dg/builtin-dynamic-object-size-5.c    |   7 +
 .../gcc.dg/builtin-dynamic-object-size-6.c    |   5 +
 .../gcc.dg/builtin-dynamic-object-size-7.c    |   5 +
 .../gcc.dg/builtin-dynamic-object-size-8.c    |   5 +
 .../gcc.dg/builtin-dynamic-object-size-9.c    |   5 +
 gcc/testsuite/gcc.dg/builtin-object-size-16.c |   2 +
 gcc/testsuite/gcc.dg/builtin-object-size-17.c |   2 +
 gcc/tree-object-size.c                        | 152 ++++++++++++++----
 gcc/tree-object-size.h                        |   3 +-
 29 files changed, 377 insertions(+), 41 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size1.C
 create mode 100644 gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size2.C
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-alloc-size.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-11.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-12.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-13.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-14.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-15.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-16.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-17.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-18.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-19.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-6.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-7.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-8.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-9.c

diff --git a/gcc/builtins.c b/gcc/builtins.c
index d2e6d95a175..9135d553c87 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -178,7 +178,7 @@ static rtx expand_builtin_memory_chk (tree, rtx, machine_mode,
 				      enum built_in_function);
 static void maybe_emit_chk_warning (tree, enum built_in_function);
 static void maybe_emit_sprintf_chk_warning (tree, enum built_in_function);
-static tree fold_builtin_object_size (tree, tree);
+static tree fold_builtin_object_size (tree, tree, enum built_in_function);
 
 unsigned HOST_WIDE_INT target_newline;
 unsigned HOST_WIDE_INT target_percent;
@@ -7880,6 +7880,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
       return const0_rtx;
 
     case BUILT_IN_OBJECT_SIZE:
+    case BUILT_IN_DYNAMIC_OBJECT_SIZE:
       return expand_builtin_object_size (exp);
 
     case BUILT_IN_MEMCPY_CHK:
@@ -9286,7 +9287,8 @@ fold_builtin_2 (location_t loc, tree expr, tree fndecl, tree arg0, tree arg1)
       break;
 
     case BUILT_IN_OBJECT_SIZE:
-      return fold_builtin_object_size (arg0, arg1);
+    case BUILT_IN_DYNAMIC_OBJECT_SIZE:
+      return fold_builtin_object_size (arg0, arg1, fcode);
 
     case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE:
       return fold_builtin_atomic_always_lock_free (arg0, arg1);
@@ -10224,7 +10226,7 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
    if possible.  */
 
 static tree
-fold_builtin_object_size (tree ptr, tree ost)
+fold_builtin_object_size (tree ptr, tree ost, enum built_in_function fcode)
 {
   tree bytes;
   int object_size_type;
@@ -10250,7 +10252,8 @@ fold_builtin_object_size (tree ptr, tree ost)
 
   if (TREE_CODE (ptr) == ADDR_EXPR)
     {
-      compute_builtin_object_size (ptr, object_size_type, &bytes);
+      compute_builtin_object_size (ptr, object_size_type, &bytes,
+				   fcode == BUILT_IN_DYNAMIC_OBJECT_SIZE);
       return fold_convert (size_type_node, bytes);
     }
   else if (TREE_CODE (ptr) == SSA_NAME)
@@ -10258,7 +10261,8 @@ fold_builtin_object_size (tree ptr, tree ost)
       /* If object size is not known yet, delay folding until
        later.  Maybe subsequent passes will help determining
        it.  */
-      if (compute_builtin_object_size (ptr, object_size_type, &bytes))
+      if (compute_builtin_object_size (ptr, object_size_type, &bytes,
+				       fcode == BUILT_IN_DYNAMIC_OBJECT_SIZE))
 	return fold_convert (size_type_node, bytes);
     }
 
diff --git a/gcc/builtins.def b/gcc/builtins.def
index 45a09b4d42d..2b2c46e9df6 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -972,6 +972,7 @@ DEF_BUILTIN_STUB (BUILT_IN_STRNCMP_EQ, "__builtin_strncmp_eq")
 
 /* Object size checking builtins.  */
 DEF_GCC_BUILTIN	       (BUILT_IN_OBJECT_SIZE, "object_size", BT_FN_SIZE_CONST_PTR_INT, ATTR_CONST_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN	       (BUILT_IN_DYNAMIC_OBJECT_SIZE, "dynamic_object_size", BT_FN_SIZE_CONST_PTR_INT, ATTR_NOTHROW_LEAF_LIST)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_MEMCPY_CHK, "__memcpy_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_NOTHROW_NONNULL_LEAF)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_MEMMOVE_CHK, "__memmove_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_NOTHROW_NONNULL_LEAF)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_MEMPCPY_CHK, "__mempcpy_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_RETNONNULL_NOTHROW_LEAF)
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index eee4c6737bb..7fe244ce3c8 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -12712,6 +12712,7 @@ __atomic_store_n(&lockvar, 0, __ATOMIC_RELEASE|__ATOMIC_HLE_RELEASE);
 @node Object Size Checking
 @section Object Size Checking Built-in Functions
 @findex __builtin_object_size
+@findex __builtin_dynamic_object_size
 @findex __builtin___memcpy_chk
 @findex __builtin___mempcpy_chk
 @findex __builtin___memmove_chk
@@ -12779,6 +12780,18 @@ assert (__builtin_object_size (q, 1) == sizeof (var.b));
 @end smallexample
 @end deftypefn
 
+@deftypefn {Built-in Function} {size_t} __builtin_dynamic_object_size (const void * @var{ptr}, int @var{type})
+is similar to @code{__builtin_object_size} in that it returns a number of bytes
+from @var{ptr} to the end of the object @var{ptr} pointer points to, except
+that the size returned may not be a constant.  This results in successful
+evaluation of object size estimates in a wider range of use cases and can be
+more precise than @code{__builtin_object_size}, but it incurs a performance
+penalty since it may add a runtime overhead on size computation.  Semantics of
+@var{type} as well as return values in case it is not possible to determine
+which objects @var{ptr} points to at compile time are the same as in the case
+of @code{__builtin_object_size}.
+@end deftypefn
+
 There are built-in functions added for many common string operation
 functions, e.g., for @code{memcpy} @code{__builtin___memcpy_chk}
 built-in is provided.  This built-in has an additional last argument,
diff --git a/gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size1.C b/gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size1.C
new file mode 100644
index 00000000000..b11ac200751
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size1.C
@@ -0,0 +1,5 @@
+// { dg-do run }
+// { dg-options "-O2" }
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size1.C"
diff --git a/gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size2.C b/gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size2.C
new file mode 100644
index 00000000000..6e52cf38533
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size2.C
@@ -0,0 +1,5 @@
+// { dg-do run }
+// { dg-options "-O2" }
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size2.C"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-alloc-size.c b/gcc/testsuite/gcc.dg/builtin-dynamic-alloc-size.c
new file mode 100644
index 00000000000..9d0eadd6be4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-alloc-size.c
@@ -0,0 +1,7 @@
+/* { dg-do compile }
+   { dg-require-effective-target alloca }
+   { dg-additional-options "-O2 -fdump-tree-optimized" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-alloc-size.c"
+/* { dg-final { scan-tree-dump-not "abort" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c
new file mode 100644
index 00000000000..7cc8b1c9488
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c
@@ -0,0 +1,6 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+/* { dg-require-effective-target alloca } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-1.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
new file mode 100644
index 00000000000..bc880a589ae
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-early_objsz-details" } */
+// { dg-skip-if "packed attribute missing for drone_source_packet" { "epiphany-*-*" } }
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-10.c"
+
+/* { dg-final { scan-tree-dump "maximum object size 21" "early_objsz" } } */
+/* { dg-final { scan-tree-dump "maximum subobject size 16" "early_objsz" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-11.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-11.c
new file mode 100644
index 00000000000..65dcec9fcae
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-11.c
@@ -0,0 +1,7 @@
+/* PR48985 */
+/* { dg-do run } */
+/* { dg-options "-std=gnu89" } */
+/* { dg-skip-if "packed attribute missing for struct s" { "epiphany-*-*" } } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-11.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-12.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-12.c
new file mode 100644
index 00000000000..f0ce050a943
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-12.c
@@ -0,0 +1,5 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-12.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-13.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-13.c
new file mode 100644
index 00000000000..555e23522dc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-13.c
@@ -0,0 +1,5 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-13.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-14.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-14.c
new file mode 100644
index 00000000000..26207200191
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-14.c
@@ -0,0 +1,5 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-14.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-15.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-15.c
new file mode 100644
index 00000000000..cd8a941438d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-15.c
@@ -0,0 +1,5 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-15.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-16.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-16.c
new file mode 100644
index 00000000000..5aa256dec1a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-16.c
@@ -0,0 +1,7 @@
+/* { dg-do run } */
+/* { dg-options "-O0" } */
+
+#define DYNAMIC_OBJECT_SIZE
+#define __builtin_object_size __builtin_dynamic_object_size
+char ax2[];               /* { dg-warning "assumed to have one element" } */
+#include "builtin-object-size-16.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-17.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-17.c
new file mode 100644
index 00000000000..f4c4b0fbc7a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-17.c
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-options "-O0 -fdump-tree-ssa" } */
+
+#define DYNAMIC_OBJECT_SIZE
+char ax2[];               /* { dg-warning "assumed to have one element" } */
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-17.c"
+/* { dg-final { scan-tree-dump-not "failure_on_line" "ssa" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-18.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-18.c
new file mode 100644
index 00000000000..70c1ebcff21
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-18.c
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* __stpncpy_chk could return buf up to buf + 64, so
+   the minimum object size might be far smaller than 64.  */
+/* { dg-final { scan-tree-dump-not "return 64;" "optimized" } } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-18.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-19.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-19.c
new file mode 100644
index 00000000000..44141a38607
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-19.c
@@ -0,0 +1,104 @@
+/* PR tree-optimization/88372 - alloc_size attribute is ignored
+   on function pointers { dg-do compile }
+   { dg-options "-O2 -fdump-tree-optimized" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-18.c"
+
+typedef __SIZE_TYPE__ size_t;
+
+#define ATTR(...) __attribute__ ((__VA_ARGS__))
+#define CONCAT(x, y) x ## y
+#define CAT(x, y) CONCAT (x, y)
+#define FAILNAME(name) CAT (call_ ## name ##_on_line_, __LINE__)
+
+#define FAIL(name) do {				\
+    extern void FAILNAME (name) (void);		\
+    FAILNAME (name)();				\
+  } while (0)
+
+/* Macro to emit a call to function named
+   call_in_true_branch_not_eliminated_on_line_NNN()
+   for each call that's expected to be eliminated.  The dg-final
+   scan-tree-dump-time directive at the bottom of the test verifies
+   that no such call appears in output.  */
+#define ELIM(expr)							\
+  if (!(expr)) FAIL (in_true_branch_not_eliminated); else (void)0
+
+void sink (void*);
+
+#define T(alloc, n) do {			\
+    void *p = alloc;				\
+    sink (p);					\
+    ELIM (n == __builtin_object_size (p, 0));	\
+    ELIM (n == __builtin_object_size (p, 1));	\
+    ELIM (n == __builtin_object_size (p, 2));	\
+    ELIM (n == __builtin_object_size (p, 3));	\
+  } while (0)
+
+
+ATTR (alloc_size (1)) void* (*alloc_1_x)(size_t, size_t);
+ATTR (alloc_size (2)) void* (*alloc_x_2)(size_t, size_t);
+
+/* Verify that things work when attribute alloc_size is applied
+   to a typedef that is then used to declared a pointer.  */
+typedef ATTR (alloc_size (1, 2)) void* (alloc_1_2_t)(size_t, size_t);
+
+void test_alloc_ptr (alloc_1_2_t *alloc_1_2)
+{
+  T (alloc_1_x (0, 0), 0);
+  T (alloc_1_x (1, 0), 1);
+  T (alloc_1_x (3, 0), 3);
+  T (alloc_1_x (9, 5), 9);
+
+  T (alloc_x_2 (0, 0), 0);
+  T (alloc_x_2 (1, 0), 0);
+  T (alloc_x_2 (0, 1), 1);
+  T (alloc_x_2 (9, 5), 5);
+
+  T (alloc_1_2 (0, 0), 0);
+  T (alloc_1_2 (1, 0), 0);
+  T (alloc_1_2 (0, 1), 0);
+  T (alloc_1_2 (9, 5), 45);
+}
+
+/* Verify that object size is detected even in indirect calls via
+   function pointers to built-in allocation functions, even without
+   explicit use of attribute alloc_size on the pointers.  */
+
+typedef void *(allocfn_1) (size_t);
+typedef void *(allocfn_1_2) (size_t, size_t);
+
+static inline void *
+call_alloc (allocfn_1 *fn1, allocfn_1_2 *fn2, size_t n1, size_t n2)
+{
+  return fn1 ? fn1 (n1) : fn2 (n1, n2);
+}
+
+static inline void *
+call_malloc (size_t n)
+{
+  return call_alloc (__builtin_malloc, 0, n, 0);
+}
+
+static inline void *
+call_calloc (size_t n1, size_t n2)
+{
+  return call_alloc (0, __builtin_calloc, n1, n2);
+}
+
+void test_builtin_ptr (void)
+{
+  T (call_malloc (0), 0);
+  T (call_malloc (1), 1);
+  T (call_malloc (9), 9);
+
+  T (call_calloc (0, 0), 0);
+  T (call_calloc (0, 1), 0);
+  T (call_calloc (1, 0), 0);
+  T (call_calloc (1, 1), 1);
+  T (call_calloc (1, 3), 3);
+  T (call_calloc (2, 3), 6);
+}
+
+/* { dg-final { scan-tree-dump-not "not_eliminated" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c
new file mode 100644
index 00000000000..267dbf48ca7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c
@@ -0,0 +1,6 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+/* { dg-require-effective-target alloca } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-2.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c
new file mode 100644
index 00000000000..fb9dc56da7e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c
@@ -0,0 +1,6 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+/* { dg-require-effective-target alloca } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-3.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c
new file mode 100644
index 00000000000..870548b4206
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c
@@ -0,0 +1,6 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+/* { dg-require-effective-target alloca } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-4.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c
new file mode 100644
index 00000000000..698b03c34be
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c
@@ -0,0 +1,7 @@
+/* { dg-do compile { target i?86-*-linux* i?86-*-gnu* x86_64-*-linux* } } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-5.c"
+
+/* { dg-final { scan-assembler-not "abort" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-6.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-6.c
new file mode 100644
index 00000000000..6a275ce5b37
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-6.c
@@ -0,0 +1,5 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-6.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-7.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-7.c
new file mode 100644
index 00000000000..e2a65994687
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-7.c
@@ -0,0 +1,5 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-7.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-8.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-8.c
new file mode 100644
index 00000000000..e7af383d9b6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-8.c
@@ -0,0 +1,5 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-8.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-9.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-9.c
new file mode 100644
index 00000000000..19021bc2ce9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-9.c
@@ -0,0 +1,5 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-9.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-16.c b/gcc/testsuite/gcc.dg/builtin-object-size-16.c
index 48229390bfd..a4557fbb1fb 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-16.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-16.c
@@ -54,7 +54,9 @@ static int nfails;
 typedef __SIZE_TYPE__ size_t;
 
 extern char ax[];
+#ifndef DYNAMIC_OBJECT_SIZE
 char ax2[];               /* { dg-warning "assumed to have one element" } */
+#endif
 
 extern char a0[0];
 static char a1[1];
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-17.c b/gcc/testsuite/gcc.dg/builtin-object-size-17.c
index 0497bbf4505..09552a8a963 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-17.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-17.c
@@ -49,7 +49,9 @@
 typedef __SIZE_TYPE__ size_t;
 
 extern char ax[];
+#ifndef DYNAMIC_OBJECT_SIZE
 char ax2[];               /* { dg-warning "assumed to have one element" } */
+#endif
 
 extern char a0[0];
 static char a1[1];
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index e48120559d3..983df24719e 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -34,6 +34,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-cfg.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "builtins.h"
 
 struct object_size_info
 {
@@ -46,7 +47,8 @@ enum
 {
   OST_SUBOBJECT = 1,
   OST_MINIMUM = 2,
-  OST_END = 4,
+  OST_DYNAMIC = 4,
+  OST_END = 8,
 };
 
 #define OST_TREE_CODE(_ost) ((_ost) & OST_MINIMUM ? MIN_EXPR : MAX_EXPR)
@@ -371,7 +373,8 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 	  || TREE_CODE (TREE_OPERAND (pt_var, 0)) != SSA_NAME)
 	{
 	  compute_builtin_object_size (TREE_OPERAND (pt_var, 0),
-				       object_size_type & ~OST_SUBOBJECT, &sz);
+				       object_size_type & OST_MINIMUM, &sz,
+				       object_size_type & OST_DYNAMIC);
 	}
       else
 	{
@@ -835,9 +838,10 @@ resolve_dependency_loops (struct object_size_info *osi)
 
 bool
 compute_builtin_object_size (tree ptr, int object_size_type,
-			     tree *psize)
+			     tree *psize, bool dynamic)
 {
   gcc_assert (object_size_type >= 0 && object_size_type < OST_END);
+  object_size_type |= dynamic ? OST_DYNAMIC : 0;
 
   /* Set to unknown and overwrite just before returning if the size
      could be determined.  */
@@ -871,8 +875,10 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 	      ptr = gimple_assign_rhs1 (def);
 
 	      if (tree_fits_shwi_p (offset)
-		  && compute_builtin_object_size (ptr, object_size_type,
-						  psize))
+		  && compute_builtin_object_size (ptr,
+						  (object_size_type
+						   & ~OST_DYNAMIC),
+						  psize, dynamic))
 		{
 		  /* Return zero when the offset is out of bounds.  */
 		  *psize = size_for_offset (*psize, offset);
@@ -893,8 +899,9 @@ compute_builtin_object_size (tree ptr, int object_size_type,
       object_sizes_grow (object_size_type);
       if (dump_file)
 	{
-	  fprintf (dump_file, "Computing %s %sobject size for ",
+	  fprintf (dump_file, "Computing %s %s%sobject size for ",
 		   (object_size_type & OST_MINIMUM) ? "minimum" : "maximum",
+		   dynamic ? "dynamic " : "",
 		   (object_size_type & OST_SUBOBJECT) ? "sub" : "");
 	  print_generic_expr (dump_file, ptr, dump_flags);
 	  fprintf (dump_file, ":\n");
@@ -917,9 +924,10 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 		print_generic_expr (dump_file, ssa_name (i),
 				    dump_flags);
 		fprintf (dump_file,
-			 ": %s %sobject size ",
+			 ": %s %s%sobject size ",
 			 ((object_size_type & OST_MINIMUM) ? "minimum"
 			  : "maximum"),
+			 dynamic ? "dynamic " : "",
 			 (object_size_type & OST_SUBOBJECT) ? "sub" : "");
 		print_generic_expr (dump_file, object_sizes_get (&osi, i),
 				    dump_flags);
@@ -1310,8 +1318,80 @@ do_valueize (tree t)
   return t;
 }
 
+/* Process a __builtin_object_size or __builtin_dynamic_object_size call in
+   CALL early before any object information is lost due to optimization.
+   Insert a MIN or MAX expression of the result and __builtin_object_size at I
+   so that it may be processed in the second pass.  */
+
+static void
+early_object_sizes_execute_one (gimple_stmt_iterator *i, gimple *call)
+{
+  tree ost = gimple_call_arg (call, 1);
+  tree lhs = gimple_call_lhs (call);
+  gcc_assert (lhs != NULL_TREE);
+
+  if (tree_fits_uhwi_p (ost))
+    {
+      unsigned HOST_WIDE_INT object_size_type = tree_to_uhwi (ost);
+      tree ptr = gimple_call_arg (call, 0);
+      if ((object_size_type == 1 || object_size_type == 3)
+	  && (TREE_CODE (ptr) == ADDR_EXPR || TREE_CODE (ptr) == SSA_NAME))
+	{
+	  tree type = TREE_TYPE (lhs);
+	  tree bytes;
+	  if (compute_builtin_object_size (ptr, object_size_type, &bytes))
+	    {
+	      tree tem = make_ssa_name (type);
+	      gimple_call_set_lhs (call, tem);
+	      enum tree_code code
+		= object_size_type & OST_MINIMUM ? MAX_EXPR : MIN_EXPR;
+	      tree cst = fold_convert (type, bytes);
+	      gimple *g = gimple_build_assign (lhs, code, tem, cst);
+	      gsi_insert_after (i, g, GSI_NEW_STMT);
+	      update_stmt (call);
+	    }
+	}
+    }
+}
+
+/* Attempt to fold one __builtin_dynamic_object_size call in CALL into an
+   expression and insert it at I.  Return true if it succeeds.  */
+
+static bool
+dynamic_object_sizes_execute_one (gimple_stmt_iterator *i, gimple *call)
+{
+  unsigned numargs = gimple_call_num_args (call);
+  tree *args = XALLOCAVEC (tree, numargs);
+  args[0] = gimple_call_arg (call, 0);
+  args[1] = gimple_call_arg (call, 1);
+
+  location_t loc = EXPR_LOC_OR_LOC (args[0], input_location);
+  tree result_type = gimple_call_return_type (as_a <gcall *> (call));
+  tree result = fold_builtin_call_array (loc, result_type,
+					 gimple_call_fn (call), numargs, args);
+
+  if (result)
+    {
+      /* fold_builtin_call_array may wrap the result inside a
+	 NOP_EXPR.  */
+      STRIP_NOPS (result);
+      gimplify_and_update_call_from_tree (i, result);
+
+      if (dump_file && (dump_flags & TDF_DETAILS))
+	{
+	  fprintf (dump_file, "Simplified (dynamic)\n  ");
+	  print_gimple_stmt (dump_file, call, 0, dump_flags);
+	  fprintf (dump_file, " to ");
+	  print_generic_expr (dump_file, result);
+	  fprintf (dump_file, "\n");
+	}
+      return true;
+    }
+  return false;
+}
+
 static unsigned int
-object_sizes_execute (function *fun, bool insert_min_max_p)
+object_sizes_execute (function *fun, bool early)
 {
   basic_block bb;
   FOR_EACH_BB_FN (bb, fun)
@@ -1320,8 +1400,12 @@ object_sizes_execute (function *fun, bool insert_min_max_p)
       for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i))
 	{
 	  tree result;
+	  bool dynamic = false;
+
 	  gimple *call = gsi_stmt (i);
-	  if (!gimple_call_builtin_p (call, BUILT_IN_OBJECT_SIZE))
+	  if (gimple_call_builtin_p (call, BUILT_IN_DYNAMIC_OBJECT_SIZE))
+	    dynamic = true;
+	  else if (!gimple_call_builtin_p (call, BUILT_IN_OBJECT_SIZE))
 	    continue;
 
 	  tree lhs = gimple_call_lhs (call);
@@ -1330,41 +1414,39 @@ object_sizes_execute (function *fun, bool insert_min_max_p)
 
 	  init_object_sizes ();
 
-	  /* If insert_min_max_p, only attempt to fold
+	  /* If early, only attempt to fold
 	     __builtin_object_size (x, 1) and __builtin_object_size (x, 3),
 	     and rather than folding the builtin to the constant if any,
 	     create a MIN_EXPR or MAX_EXPR of the __builtin_object_size
 	     call result and the computed constant.  */
-	  if (insert_min_max_p)
+	  if (early)
 	    {
-	      tree ost = gimple_call_arg (call, 1);
-	      if (tree_fits_uhwi_p (ost))
+	      early_object_sizes_execute_one (&i, call);
+	      continue;
+	    }
+
+	  if (dynamic)
+	    {
+	      bool done = dynamic_object_sizes_execute_one (&i, call);
+	      if (done || early)
+		continue;
+	      else
 		{
-		  unsigned HOST_WIDE_INT object_size_type = tree_to_uhwi (ost);
-		  tree ptr = gimple_call_arg (call, 0);
-		  if ((object_size_type & OST_SUBOBJECT)
-		      && (TREE_CODE (ptr) == ADDR_EXPR
-			  || TREE_CODE (ptr) == SSA_NAME))
+		  /* If we could not find a suitable size expression, lower to
+		     __builtin_object_size so that we may at least get a
+		     constant lower or higher estimate.  */
+		  tree bosfn = builtin_decl_implicit (BUILT_IN_OBJECT_SIZE);
+		  gimple_call_set_fndecl (call, bosfn);
+		  update_stmt (call);
+
+		  if (dump_file && (dump_flags & TDF_DETAILS))
 		    {
-		      tree type = TREE_TYPE (lhs);
-		      tree bytes;
-		      if (compute_builtin_object_size (ptr, object_size_type,
-						       &bytes))
-			{
-			  tree tem = make_ssa_name (type);
-			  gimple_call_set_lhs (call, tem);
-			  enum tree_code code
-			    = (object_size_type & OST_MINIMUM
-			       ? MAX_EXPR : MIN_EXPR);
-			  tree cst = fold_convert (type, bytes);
-			  gimple *g
-			    = gimple_build_assign (lhs, code, tem, cst);
-			  gsi_insert_after (&i, g, GSI_NEW_STMT);
-			  update_stmt (call);
-			}
+		      print_generic_expr (dump_file, gimple_call_arg (call, 0),
+					  dump_flags);
+		      fprintf (dump_file,
+			       ": Retrying as __builtin_object_size\n");
 		    }
 		}
-	      continue;
 	    }
 
 	  result = gimple_fold_stmt_to_constant (call, do_valueize);
diff --git a/gcc/tree-object-size.h b/gcc/tree-object-size.h
index b2d6a58324c..edc7b573cc5 100644
--- a/gcc/tree-object-size.h
+++ b/gcc/tree-object-size.h
@@ -22,7 +22,8 @@ along with GCC; see the file COPYING3.  If not see
 
 extern void init_object_sizes (void);
 extern void fini_object_sizes (void);
-extern bool compute_builtin_object_size (tree, int, tree *);
+extern bool compute_builtin_object_size (tree, int, tree *,
+					 bool dynamic = false);
 extern tree decl_init_size (tree, bool);
 
 #endif  // GCC_TREE_OBJECT_SIZE_H
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH 06/10] tree-object-size: Support dynamic sizes in conditions
  2021-11-09 19:01 [PATCH 00/10] __builtin_dynamic_object_size Siddhesh Poyarekar
                   ` (4 preceding siblings ...)
  2021-11-09 19:01 ` [PATCH 05/10] __builtin_dynamic_object_size: Recognize builtin Siddhesh Poyarekar
@ 2021-11-09 19:01 ` Siddhesh Poyarekar
  2021-11-23 15:12   ` Jakub Jelinek
  2021-11-09 19:01 ` [PATCH 07/10] tree-object-size: Handle function parameters Siddhesh Poyarekar
                   ` (8 subsequent siblings)
  14 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-09 19:01 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Handle GIMPLE_PHI and conditionals specially for dynamic objects,
returning PHI/conditional expressions instead of just a MIN/MAX
estimate.

This makes the returned object size variable for loops and conditionals,
so tests need to be adjusted to look for precise size in some cases.
builtin-dynamic-object-size-5.c had to be modified to only look for
success in maximum object size case and skip over the minimum object
size tests because the result is no longer a compile time constant.

I also added some simple tests to exercise conditionals with dynamic
object sizes.

gcc/ChangeLog:

	* tree-object-size.c: Include gimplify-me.h.
	(struct object_size_info): New member phiresults.
	(estimate_size): New argument visitlog.  Handle newly inserted
	PHI nodes.
	(get_insertion_point, gimplify_size_expressions): New functions.
	(compute_builtin_object_size): Call gimplify_size_expressions.
	(make_or_get_tempsize): New function.
	(cond_expr_object_size): Return COND_EXPR for dynamic sizes.
	(phi_object_size, phi_dynamic_object_size): New functions.
	(collect_object_sizes_for): Call them.
	(object_sizes_execute): Don't insert min/max for dynamic sizes.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-dynamic-object-size-0.c: New file.
	* gcc.dg/builtin-dynamic-object-size-10.c: Adjust expected
	output.
	* gcc.dg/builtin-dynamic-object-size-1.c (DYNAMIC_OBJECT_SIZE):
	New macro.
	* gcc.dg/builtin-dynamic-object-size-2.c (DYNAMIC_OBJECT_SIZE):
	Likewise.
	* gcc.dg/builtin-dynamic-object-size-3.c (DYNAMIC_OBJECT_SIZE):
	Likewise.
	* gcc.dg/builtin-dynamic-object-size-4.c (DYNAMIC_OBJECT_SIZE):
	Likewise.
	* gcc.dg/builtin-dynamic-object-size-5.c (DYNAMIC_OBJECT_SIZE):
	Likewise.
	* gcc.dg/builtin-object-size-1.c [DYNAMIC_OBJECT_SIZE]: Alter
	expected results for dynamic object size.
	* gcc.dg/builtin-object-size-2.c [DYNAMIC_OBJECT_SIZE]:
	Likewise.
	* gcc.dg/builtin-object-size-3.c [DYNAMIC_OBJECT_SIZE]:
	Likewise.
	* gcc.dg/builtin-object-size-4.c [DYNAMIC_OBJECT_SIZE]:
	Likewise.
	* gcc.dg/builtin-object-size-5.c [DYNAMIC_OBJECT_SIZE]:
	Likewise.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 .../gcc.dg/builtin-dynamic-object-size-0.c    |  72 ++++
 .../gcc.dg/builtin-dynamic-object-size-1.c    |   1 +
 .../gcc.dg/builtin-dynamic-object-size-10.c   |   4 +-
 .../gcc.dg/builtin-dynamic-object-size-2.c    |   1 +
 .../gcc.dg/builtin-dynamic-object-size-3.c    |   1 +
 .../gcc.dg/builtin-dynamic-object-size-4.c    |   1 +
 .../gcc.dg/builtin-dynamic-object-size-5.c    |   1 +
 gcc/testsuite/gcc.dg/builtin-object-size-1.c  | 109 +++++-
 gcc/testsuite/gcc.dg/builtin-object-size-2.c  |  77 +++++
 gcc/testsuite/gcc.dg/builtin-object-size-3.c  | 105 ++++++
 gcc/testsuite/gcc.dg/builtin-object-size-4.c  |  68 ++++
 gcc/testsuite/gcc.dg/builtin-object-size-5.c  |  12 +
 gcc/tree-object-size.c                        | 315 +++++++++++++++---
 13 files changed, 725 insertions(+), 42 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c

diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
new file mode 100644
index 00000000000..ddedf6a49bd
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -0,0 +1,72 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+typedef __SIZE_TYPE__ size_t;
+#define abort __builtin_abort
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi (int cond)
+{
+  void *ret;
+ 
+  if (cond)
+    ret = __builtin_malloc (32);
+  else
+    ret = __builtin_malloc (64);
+
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_calloc_condphi (size_t cnt, size_t sz, int cond)
+{
+  struct
+    {
+      int a;
+      char b;
+    } bin[cnt];
+
+  char *ch = __builtin_calloc (cnt, sz);
+
+  return __builtin_dynamic_object_size (cond ? ch : (void *) &bin, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_deploop (size_t sz, size_t cond)
+{
+  char *bin = __builtin_alloca (32);
+
+  for (size_t i = 0; i < sz; i++)
+    if (i == cond)
+      bin = __builtin_alloca (64);
+
+  return __builtin_dynamic_object_size (bin, 0);
+}
+
+unsigned nfails = 0;
+
+#define FAIL() ({ \
+  __builtin_printf ("Failure at line: %d\n", __LINE__);			      \
+  nfails++;								      \
+})
+
+int
+main (int argc, char **argv)
+{
+  if (test_builtin_malloc_condphi (1) != 32)
+    FAIL ();
+  if (test_builtin_malloc_condphi (0) != 64)
+    FAIL ();
+  if (test_builtin_calloc_condphi (128, 1, 0) == 128)
+    FAIL ();
+  if (test_deploop (128, 129) != 32)
+    FAIL ();
+
+  if (nfails > 0)
+    __builtin_abort ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c
index 7cc8b1c9488..2c7d2128913 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c
@@ -2,5 +2,6 @@
 /* { dg-options "-O2" } */
 /* { dg-require-effective-target alloca } */
 
+#define DYNAMIC_OBJECT_SIZE
 #define __builtin_object_size __builtin_dynamic_object_size
 #include "builtin-object-size-1.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
index bc880a589ae..e234467d6d0 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
@@ -5,5 +5,5 @@
 #define __builtin_object_size __builtin_dynamic_object_size
 #include "builtin-object-size-10.c"
 
-/* { dg-final { scan-tree-dump "maximum object size 21" "early_objsz" } } */
-/* { dg-final { scan-tree-dump "maximum subobject size 16" "early_objsz" } } */
+/* { dg-final { scan-tree-dump "maximum dynamic object size 21" "early_objsz" } } */
+/* { dg-final { scan-tree-dump "maximum dynamic subobject size 16" "early_objsz" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c
index 267dbf48ca7..2f07534c11b 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c
@@ -2,5 +2,6 @@
 /* { dg-options "-O2" } */
 /* { dg-require-effective-target alloca } */
 
+#define DYNAMIC_OBJECT_SIZE
 #define __builtin_object_size __builtin_dynamic_object_size
 #include "builtin-object-size-2.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c
index fb9dc56da7e..29b5b358845 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c
@@ -2,5 +2,6 @@
 /* { dg-options "-O2" } */
 /* { dg-require-effective-target alloca } */
 
+#define DYNAMIC_OBJECT_SIZE
 #define __builtin_object_size __builtin_dynamic_object_size
 #include "builtin-object-size-3.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c
index 870548b4206..5ff1f16c978 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c
@@ -2,5 +2,6 @@
 /* { dg-options "-O2" } */
 /* { dg-require-effective-target alloca } */
 
+#define DYNAMIC_OBJECT_SIZE
 #define __builtin_object_size __builtin_dynamic_object_size
 #include "builtin-object-size-4.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c
index 698b03c34be..2438a26d920 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c
@@ -1,6 +1,7 @@
 /* { dg-do compile { target i?86-*-linux* i?86-*-gnu* x86_64-*-linux* } } */
 /* { dg-options "-O2" } */
 
+#define DYNAMIC_OBJECT_SIZE
 #define __builtin_object_size __builtin_dynamic_object_size
 #include "builtin-object-size-5.c"
 
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
index b270e8d8827..76df4c96271 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
@@ -42,9 +42,17 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (q, 0) != (size_t) -1)
     abort ();
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 0)
+      != (x < 0
+	  ? sizeof (a) - __builtin_offsetof (struct A, a) - 9
+	  : sizeof (a) - __builtin_offsetof (struct A, c) - 1))
+    abort ();
+#else
   if (__builtin_object_size (r, 0)
       != sizeof (a) - __builtin_offsetof (struct A, a) - 9)
     abort ();
+#endif
   if (x < 6)
     r = &w[2].a[1];
   else
@@ -58,9 +66,17 @@ test1 (void *q, int x)
   if (__builtin_object_size (&y.b, 0)
       != sizeof (a) - __builtin_offsetof (struct A, b))
     abort ();
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 0)
+      != (x < 6
+	  ? 2 * sizeof (w[0]) - __builtin_offsetof (struct A, a) - 1
+	  : sizeof (a) - __builtin_offsetof (struct A, a) - 6))
+    abort ();
+#else
   if (__builtin_object_size (r, 0)
       != 2 * sizeof (w[0]) - __builtin_offsetof (struct A, a) - 1)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
@@ -165,6 +181,7 @@ test2 (void)
   struct B { char buf1[10]; char buf2[10]; } a;
   char *r, buf3[20];
   int i;
+  size_t res;
 
   if (sizeof (a) != 20)
     return;
@@ -181,7 +198,24 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[9];
     }
-  if (__builtin_object_size (r, 0) != 20)
+#ifdef DYNAMIC_OBJECT_SIZE
+  res = sizeof (buf3);
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 1;
+      else if (i == l1)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 7;
+      else if (i == l1 + 1)
+        res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
+    }
+#else
+  res = 20;
+#endif
+  if (__builtin_object_size (r, 0) != res)
     abort ();
   r = &buf3[20];
   for (i = 0; i < 4; ++i)
@@ -195,13 +229,45 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[9];
     }
-  if (__builtin_object_size (r, 0) != 15)
+#ifdef DYNAMIC_OBJECT_SIZE
+  res = sizeof (buf3) - 20;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 7;
+      else if (i == l1)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 7;
+      else if (i == l1 + 1)
+        res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
+    }
+  if (__builtin_object_size (r, 0) != res)
+    abort ();
+#else
+  res = 15;
+#endif
+  if (__builtin_object_size (r, 0) != res)
     abort ();
   r += 8;
+#ifdef DYNAMIC_OBJECT_SIZE
+  res -= 8;
+  if (__builtin_object_size (r, 0) != res)
+    abort ();
+  if (res >= 6)
+    {
+      if (__builtin_object_size (r + 6, 0) != res - 6)
+        abort ();
+    }
+  else if (__builtin_object_size (r + 6, 0) != 0)
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != 7)
     abort ();
   if (__builtin_object_size (r + 6, 0) != 1)
     abort ();
+#endif
   r = &buf3[18];
   for (i = 0; i < 4; ++i)
     {
@@ -214,8 +280,31 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[4];
     }
+#ifdef DYNAMIC_OBJECT_SIZE
+  res = sizeof (buf3) - 18;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+          res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
+      else if (i == l1)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 9;
+      else if (i == l1 + 1)
+        res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 4;
+    }
+  if (res >= 12)
+    {
+      if (__builtin_object_size (r + 12, 0) != res - 12)
+        abort ();
+    }
+  else if (__builtin_object_size (r + 12, 0) != 0)
+    abort ();
+#else
   if (__builtin_object_size (r + 12, 0) != 4)
     abort ();
+#endif
 }
 
 void
@@ -358,6 +447,10 @@ test5 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (p, 0) != sizeof (buf) - 8 - 4 * x)
+    abort ();
+#else
   /* My understanding of ISO C99 6.5.6 is that a conforming
      program will not end up with p equal to &buf[0]
      through &buf[7], i.e. calling this function with say
@@ -367,6 +460,7 @@ test5 (size_t x)
      it would be 64 (or conservative (size_t) -1 == unknown).  */
   if (__builtin_object_size (p, 0) != sizeof (buf) - 8)
     abort ();
+#endif
   memset (p, ' ', sizeof (buf) - 8 - 4 * 4);
 }
 
@@ -381,8 +475,13 @@ test6 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (p, 0) != sizeof (t) - 8 - 4 * x)
+    abort ();
+#else
   if (__builtin_object_size (p, 0) != sizeof (t) - 8)
     abort ();
+#endif
   memset (p, ' ', sizeof (t) - 8 - 4 * 4);
   p = &t.buf[8];
   for (i = 0; i < x; i++)
@@ -390,8 +489,14 @@ test6 (size_t x)
       r = __builtin_memcpy (r, t.buf, i);
       p = r + 1;
     }
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (p, 0)
+      != ((x > 0) ? sizeof (t.buf2) - 1 : sizeof (t) - 8))
+    abort ();
+#else
   if (__builtin_object_size (p, 0) != sizeof (t) - 8)
     abort ();
+#endif
   for (i = 0; i < x; i++)
     {
       r = __builtin_mempcpy (r, t.buf, i);
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
index ea11a17b6d8..c395d2e95b3 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-2.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
@@ -43,8 +43,15 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (q, 1) != (size_t) -1)
     abort ();
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (x < 0
+      ? __builtin_object_size (r, 1) != sizeof (a.a) - 9
+      : __builtin_object_size (r, 1) != sizeof (a.c) - 1)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a.c) - 1)
     abort ();
+#endif
   if (x < 6)
     r = &w[2].a[1];
   else
@@ -55,8 +62,15 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&y.b, 1) != sizeof (a.b))
     abort ();
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (x < 6
+      ? __builtin_object_size (r, 1) != sizeof (a.a) - 1
+      : __builtin_object_size (r, 1) != sizeof (a.a) - 6)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a.a) - 1)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
@@ -185,6 +199,9 @@ test2 (void)
   struct B { char buf1[10]; char buf2[10]; } a;
   char *r, buf3[20];
   int i;
+#ifdef DYNAMIC_OBJECT_SIZE
+  size_t dyn_res;
+#endif
 
   if (sizeof (a) != 20)
     return;
@@ -201,8 +218,26 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[9];
     }
+#ifdef DYNAMIC_OBJECT_SIZE
+  dyn_res = sizeof (buf3);
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	dyn_res = sizeof (a.buf1) - 1;
+      else if (i == l1)
+	dyn_res = sizeof (a.buf2) - 7;
+      else if (i == l1 + 1)
+	dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+	dyn_res = sizeof (a.buf1) - 9;
+    }
+  if (__builtin_object_size (r, 1) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (buf3))
     abort ();
+#endif
   r = &buf3[20];
   for (i = 0; i < 4; ++i)
     {
@@ -215,13 +250,50 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[9];
     }
+#ifdef DYNAMIC_OBJECT_SIZE
+  dyn_res = sizeof (buf3) - 20;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+        dyn_res = sizeof (a.buf1) - 7;
+      else if (i == l1)
+        dyn_res = sizeof (a.buf2) - 7;
+      else if (i == l1 + 1)
+        dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+        dyn_res = sizeof (a.buf1) - 9;
+    }
+  if (__builtin_object_size (r, 1) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (buf3) - 5)
     abort ();
+#endif
   r += 8;
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (dyn_res >= 8)
+    {
+      dyn_res -= 8;
+      if (__builtin_object_size (r, 1) != dyn_res)
+	abort ();
+
+      if (dyn_res >= 6)
+	{
+	  if (__builtin_object_size (r + 6, 1) != dyn_res - 6)
+	    abort ();
+	}
+      else if (__builtin_object_size (r + 6, 1) != 0)
+	abort ();
+    }
+  else if (__builtin_object_size (r, 1) != 0)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (buf3) - 13)
     abort ();
   if (__builtin_object_size (r + 6, 1) != sizeof (buf3) - 19)
     abort ();
+#endif
 }
 
 void
@@ -340,8 +412,13 @@ test5 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (p, 1) != sizeof (t.buf) - 8 - 4 * x)
+    abort ();
+#else
   if (__builtin_object_size (p, 1) != sizeof (t.buf) - 8)
     abort ();
+#endif
   memset (p, ' ', sizeof (t.buf) - 8 - 4 * 4);
   p = &t.buf[8];
   for (i = 0; i < x; i++)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
index 2d68925077e..ccdc6ef8cc8 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-3.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
@@ -71,23 +71,45 @@ test1 (void *q, int x)
     r = malloc (30);
   else
     r = calloc (2, 14);
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 2) != (x < 20 ? 30 : 2 * 14))
+    abort ();
+#else
   if (__builtin_object_size (r, 2) != 2 * 14)
     abort ();
+#endif
   if (x < 30)
     r = malloc (sizeof (a));
   else
     r = &a.a[3];
+#ifdef DYNAMIC_OBJECT_SIZE
+  size_t objsz = (x < 30 ? sizeof (a)
+                  : sizeof (a) - __builtin_offsetof (struct A, a) - 3);
+  if (__builtin_object_size (r, 2) != objsz)
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, a) - 3)
     abort ();
+#endif
   r = memcpy (r, "a", 2);
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 2) != objsz)
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, a) - 3)
     abort ();
+#endif
   r = memcpy (r + 2, "b", 2) + 2;
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 2) != objsz - 4)
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, a) - 3 - 4)
     abort ();
+#endif
   r = &a.a[4];
   r = memset (r, 'a', 2);
   if (__builtin_object_size (r, 2)
@@ -164,6 +186,9 @@ test2 (void)
   struct B { char buf1[10]; char buf2[10]; } a;
   char *r, buf3[20];
   int i;
+#ifdef DYNAMIC_OBJECT_SIZE
+  size_t dyn_res;
+#endif
 
   if (sizeof (a) != 20)
     return;
@@ -180,8 +205,26 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[9];
     }
+#ifdef DYNAMIC_OBJECT_SIZE
+  dyn_res = sizeof (buf3);
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 1;
+      else if (i == l1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 7;
+      else if (i == l1 + 1)
+	dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
+    }
+  if (__builtin_object_size (r, 2) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 2) != 3)
     abort ();
+#endif
   r = &buf3[20];
   for (i = 0; i < 4; ++i)
     {
@@ -208,13 +251,44 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[4];
     }
+#ifdef DYNAMIC_OBJECT_SIZE
+  dyn_res = sizeof (buf3) - 2;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 1;
+      else if (i == l1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 2;
+      else if (i == l1 + 1)
+	dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 4;
+    }
+  if (__builtin_object_size (r, 2) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 2) != 15)
     abort ();
+#endif
   r += 8;
+#ifdef DYNAMIC_OBJECT_SIZE
+  dyn_res -= 8;
+  if (__builtin_object_size (r, 2) != dyn_res)
+    abort ();
+  if (dyn_res >= 6)
+    {
+      if (__builtin_object_size (r + 6, 2) != dyn_res - 6)
+	abort ();
+    }
+  else if (__builtin_object_size (r + 6, 2) != 0)
+    abort ();
+#else
   if (__builtin_object_size (r, 2) != 7)
     abort ();
   if (__builtin_object_size (r + 6, 2) != 1)
     abort ();
+#endif
   r = &buf3[18];
   for (i = 0; i < 4; ++i)
     {
@@ -227,8 +301,31 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[4];
     }
+#ifdef DYNAMIC_OBJECT_SIZE
+  dyn_res = sizeof (buf3) - 18;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
+      else if (i == l1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 9;
+      else if (i == l1 + 1)
+	dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 4;
+    }
+  if (dyn_res >= 12)
+    {
+      if (__builtin_object_size (r + 12, 2) != dyn_res - 12)
+	abort ();
+    }
+  else if (__builtin_object_size (r + 12, 2) != 0)
+    abort ();
+#else
   if (__builtin_object_size (r + 12, 2) != 0)
     abort ();
+#endif
 }
 
 void
@@ -371,7 +468,11 @@ test5 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (p, 2) != sizeof (buf) - 8 - 4 * x)
+#else
   if (__builtin_object_size (p, 2) != 0)
+#endif
     abort ();
   memset (p, ' ', sizeof (buf) - 8 - 4 * 4);
 }
@@ -387,7 +488,11 @@ test6 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (p, 2) != sizeof (t) - 8 - 4 * x)
+#else
   if (__builtin_object_size (p, 2) != 0)
+#endif
     abort ();
   memset (p, ' ', sizeof (t) - 8 - 4 * 4);
   p = &t.buf[8];
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
index dd7f6d7336d..002512d38ab 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-4.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
@@ -43,7 +43,12 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (q, 3) != 0)
     abort ();
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 3)
+      != (x < 0 ? sizeof (a.a) - 9 : sizeof (a.c) - 1))
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.a) - 9)
+#endif
     abort ();
   if (x < 6)
     r = &w[2].a[1];
@@ -55,31 +60,57 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&y.b, 3) != sizeof (a.b))
     abort ();
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 3)
+      != (x < 6 ? sizeof (w[2].a) - 1 : sizeof (a.a) - 6))
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.a) - 6)
+#endif
     abort ();
   if (x < 20)
     r = malloc (30);
   else
     r = calloc (2, 16);
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 3) != (x < 20 ? 30 : 2 * 16))
+#else
   if (__builtin_object_size (r, 3) != 30)
+#endif
     abort ();
   if (x < 20)
     r = malloc (30);
   else
     r = calloc (2, 14);
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 3) != (x < 20 ? 30 : 2 * 14))
+#else
   if (__builtin_object_size (r, 3) != 2 * 14)
+#endif
     abort ();
   if (x < 30)
     r = malloc (sizeof (a));
   else
     r = &a.a[3];
+#ifdef DYNAMIC_OBJECT_SIZE
+  size_t objsz = x < 30 ? sizeof (a) : sizeof (a.a) - 3;
+  if (__builtin_object_size (r, 3) != objsz)
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.a) - 3)
+#endif
     abort ();
   r = memcpy (r, "a", 2);
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 3) != objsz)
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.a) - 3)
+#endif
     abort ();
   r = memcpy (r + 2, "b", 2) + 2;
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 3) != objsz - 4)
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.a) - 3 - 4)
+#endif
     abort ();
   r = &a.a[4];
   r = memset (r, 'a', 2);
@@ -184,6 +215,9 @@ test2 (void)
   struct B { char buf1[10]; char buf2[10]; } a;
   char *r, buf3[20];
   int i;
+#ifdef DYNAMIC_OBJECT_SIZE
+  size_t dyn_res = 0;
+#endif
 
   if (sizeof (a) != 20)
     return;
@@ -228,13 +262,38 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[2];
     }
+#ifdef DYNAMIC_OBJECT_SIZE
+  dyn_res = sizeof (buf3) - 1;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+        dyn_res = sizeof (a.buf1) - 6;
+      else if (i == l1)
+        dyn_res = sizeof (a.buf2) - 4;
+      else if (i == l1 + 1)
+        dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+        dyn_res = sizeof (a.buf1) - 2;
+    }
+  if (__builtin_object_size (r, 3) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.buf1) - 6)
     abort ();
+#endif
   r += 2;
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 3) != dyn_res - 2)
+    abort ();
+  if (__builtin_object_size (r + 1, 3) != dyn_res - 3)
+    abort ();
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.buf1) - 6 - 2)
     abort ();
   if (__builtin_object_size (r + 1, 3) != sizeof (a.buf1) - 6 - 3)
     abort ();
+#endif
 }
 
 void
@@ -353,7 +412,11 @@ test5 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (p, 3) != sizeof (t.buf) - 8 - 4 * x)
+#else
   if (__builtin_object_size (p, 3) != 0)
+#endif
     abort ();
   memset (p, ' ', sizeof (t.buf) - 8 - 4 * 4);
   p = &t.buf[8];
@@ -362,7 +425,12 @@ test5 (size_t x)
       r = __builtin_memcpy (r, t.buf, i);
       p = r + 1;
     }
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (p, 3)
+      != (x > 0 ? sizeof (t.buf2) - 1 : sizeof (t.buf) - 8))
+#else
   if (__builtin_object_size (p, 3) != sizeof (t.buf) - 8)
+#endif
     abort ();
   for (i = 0; i < x; i++)
     {
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-5.c b/gcc/testsuite/gcc.dg/builtin-object-size-5.c
index 7c274cdfd42..dd19747c97a 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-5.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-5.c
@@ -1,5 +1,7 @@
 /* { dg-do compile { target i?86-*-linux* i?86-*-gnu* x86_64-*-linux* } } */
 /* { dg-options "-O2" } */
+/* For dynamic object sizes we 'succeed' if the returned size is known for
+   maximum object size.  */
 
 typedef __SIZE_TYPE__ size_t;
 extern void abort (void);
@@ -13,7 +15,11 @@ test1 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (p, 0) == -1)
+#else
   if (__builtin_object_size (p, 0) != sizeof (buf) - 8)
+#endif
     abort ();
 }
 
@@ -25,10 +31,15 @@ test2 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (p, 1) == -1)
+#else
   if (__builtin_object_size (p, 1) != sizeof (buf) - 8)
+#endif
     abort ();
 }
 
+#ifndef DYNAMIC_OBJECT_SIZE
 void
 test3 (size_t x)
 {
@@ -52,5 +63,6 @@ test4 (size_t x)
   if (__builtin_object_size (p, 3) != 0)
     abort ();
 }
+#endif
 
 /* { dg-final { scan-assembler-not "abort" } } */
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index 983df24719e..33598ddc91c 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -35,11 +35,12 @@ along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "attribs.h"
 #include "builtins.h"
+#include "gimplify-me.h"
 
 struct object_size_info
 {
   int object_size_type;
-  bitmap visited, reexamine;
+  bitmap visited, reexamine, phiresults;
   vec<unsigned> tempsize_objs;
 };
 
@@ -681,7 +682,7 @@ reducing_size (tree orig, tree expr, bool found_minus)
    simplified expression.  */
 
 static tree
-estimate_size (object_size_info *osi, tree size)
+estimate_size (object_size_info *osi, tree size, bitmap *visitlog = NULL)
 {
   enum tree_code code = TREE_CODE (size);
   int object_size_type = osi->object_size_type;
@@ -691,15 +692,38 @@ estimate_size (object_size_info *osi, tree size)
     case SSA_NAME:
 	{
 	  unsigned num = SSA_NAME_VERSION (size);
-	  if (!bitmap_bit_p (osi->reexamine, num))
+	  if (!bitmap_bit_p (osi->reexamine, num)
+	      || (visitlog && !bitmap_set_bit (*visitlog, num)))
 	    return size;
+	  gimple *stmt = SSA_NAME_DEF_STMT (size);
+	  if (stmt)
+	    {
+	      /* Only the PHI results are added to gimple.  */
+	      gcc_checking_assert (gimple_code (stmt) == GIMPLE_PHI);
+	      gcc_checking_assert (osi->object_size_type & OST_DYNAMIC);
+	      unsigned i, num_args = gimple_phi_num_args (stmt);
+
+	      gcc_checking_assert (num_args > 0);
+	      for (i = 0; i < num_args; i++)
+		{
+		  tree rhs = gimple_phi_arg_def (stmt, i);
+
+		  if (TREE_CODE (rhs) == SSA_NAME
+		      && bitmap_bit_p (osi->reexamine, SSA_NAME_VERSION (rhs)))
+		    rhs = estimate_size (osi, rhs, visitlog);
+
+		  if (size_unknown_p (rhs, object_size_type))
+		    return size_unknown (object_size_type);
+		}
+	      return size;
+	    }
 	  return object_sizes_get (osi, osi->tempsize_objs[num]);
 	}
     case MIN_EXPR:
     case MAX_EXPR:
 	{
-	  tree op0 = estimate_size (osi, TREE_OPERAND (size, 0));
-	  tree op1 = estimate_size (osi, TREE_OPERAND (size, 1));
+	  tree op0 = estimate_size (osi, TREE_OPERAND (size, 0), visitlog);
+	  tree op1 = estimate_size (osi, TREE_OPERAND (size, 1), visitlog);
 	  if (size_unknown_p (op0, object_size_type)
 	      || size_unknown_p (op1, object_size_type))
 	    return size_unknown (object_size_type);
@@ -708,7 +732,7 @@ estimate_size (object_size_info *osi, tree size)
     case MINUS_EXPR:
     case PLUS_EXPR:
 	{
-	  tree ret = estimate_size (osi, TREE_OPERAND (size, 0));
+	  tree ret = estimate_size (osi, TREE_OPERAND (size, 0), visitlog);
 
 	  if (size_unknown_p (ret, object_size_type))
 	    return size_unknown (object_size_type);
@@ -821,6 +845,7 @@ resolve_dependency_loops (struct object_size_info *osi)
       if (TREE_CODE (szexpr) == INTEGER_CST)
 	continue;
       tree sz = estimate_size (osi, szexpr);
+      gcc_checking_assert (TREE_CODE (sz) == INTEGER_CST);
       object_sizes_initialize (osi, i, sz);
     }
 
@@ -829,6 +854,109 @@ resolve_dependency_loops (struct object_size_info *osi)
     release_ssa_name (ssa_name (i));
 }
 
+static void
+get_insertion_point (struct object_size_info *osi, unsigned ssano,
+		     gimple_stmt_iterator *gsi)
+{
+  unsigned varno = osi->tempsize_objs[ssano];
+  gimple *stmt = SSA_NAME_DEF_STMT (ssa_name (varno));
+
+  switch (gimple_code (stmt))
+    {
+    case GIMPLE_NOP:
+      *gsi = gsi_start_bb (single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)));
+      break;
+    case GIMPLE_PHI:
+	{
+	  gimple *size_stmt = SSA_NAME_DEF_STMT (object_sizes_get (osi,
+								   varno));
+
+	  for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++)
+	    {
+	      tree rhs = gimple_phi_arg_def (size_stmt, i);
+	      if (TREE_CODE (rhs) == SSA_NAME
+		  && SSA_NAME_VERSION (rhs) == ssano)
+		{
+		  edge e = gimple_phi_arg_edge (as_a <gphi *> (stmt), i);
+		  *gsi = gsi_last_bb (e->src);
+		  break;
+		}
+	    }
+	  break;
+	}
+    default:
+      *gsi = gsi_for_stmt (stmt);
+    }
+}
+
+static void
+gimplify_size_expressions (object_size_info *osi)
+{
+  int object_size_type = osi->object_size_type;
+  bitmap_iterator bi;
+  unsigned int i;
+  bool changed;
+
+  /* Step 1: Propagate unknowns into expressions.  */
+  bitmap tempsize_free = BITMAP_ALLOC (NULL);
+  do
+    {
+      changed = false;
+      EXECUTE_IF_SET_IN_BITMAP (osi->reexamine, 0, i, bi)
+	{
+	  unsigned varno = osi->tempsize_objs[i];
+
+	  tree cur = object_sizes_get (osi, varno);
+
+	  if (size_unknown_p (cur, object_size_type))
+	    {
+	      bitmap_set_bit (tempsize_free, i);
+	      continue;
+	    }
+
+	  tree szexpr = object_sizes_get (osi, i);
+	  bitmap visitlog = BITMAP_ALLOC (NULL);
+	  tree sz = estimate_size (osi, szexpr, &visitlog);
+
+	  if (size_unknown_p (sz, object_size_type))
+	    {
+	      gimple *stmt = SSA_NAME_DEF_STMT (cur);
+	      if (stmt)
+		{
+		  gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
+		  remove_phi_node (&gsi, true);
+		}
+	      bitmap_set_bit (tempsize_free, i);
+	      object_sizes_initialize (osi, varno, sz);
+	      changed = true;
+	    }
+	}
+      bitmap_and_compl_into (osi->reexamine, tempsize_free);
+    }
+  while (changed);
+
+  /* Expand all size expressions to put their definitions close to the objects
+     for whom size is being computed.  */
+  bitmap_and_compl_into (osi->reexamine, osi->phiresults);
+  EXECUTE_IF_SET_IN_BITMAP (osi->reexamine, 0, i, bi)
+    {
+      gimple_stmt_iterator gsi;
+      gimple_seq seq = NULL;
+      tree size_expr = object_sizes_get (osi, i);
+
+      size_expr = size_binop (MODIFY_EXPR, ssa_name (i), size_expr);
+      force_gimple_operand (size_expr, &seq, true, NULL);
+
+      get_insertion_point (osi, i, &gsi);
+      gsi_insert_seq_before (&gsi, seq, GSI_CONTINUE_LINKING);
+    }
+
+  EXECUTE_IF_SET_IN_BITMAP (tempsize_free, 0, i, bi)
+    release_ssa_name (ssa_name (i));
+
+  BITMAP_FREE (tempsize_free);
+}
+
 /* Compute __builtin_object_size value for PTR and set *PSIZE to
    the resulting value.  If the declared object is known and PDECL
    is nonnull, sets *PDECL to the object's DECL.  OBJECT_SIZE_TYPE
@@ -909,11 +1037,17 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 
       osi.visited = BITMAP_ALLOC (NULL);
       osi.reexamine = BITMAP_ALLOC (NULL);
+      osi.phiresults = BITMAP_ALLOC (NULL);
       osi.tempsize_objs.create (0);
       collect_object_sizes_for (&osi, ptr);
 
       if (!bitmap_empty_p (osi.reexamine))
-	resolve_dependency_loops (&osi);
+	{
+	  if (dynamic)
+	    gimplify_size_expressions (&osi);
+	  else
+	    resolve_dependency_loops (&osi);
+	}
 
       /* Debugging dumps.  */
       if (dump_file)
@@ -936,6 +1070,7 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 	}
 
       osi.tempsize_objs.release ();
+      BITMAP_FREE (osi.phiresults);
       BITMAP_FREE (osi.reexamine);
       BITMAP_FREE (osi.visited);
     }
@@ -970,6 +1105,24 @@ make_tempsize (struct object_size_info *osi, unsigned varno)
   return ssa;
 }
 
+/* Get the temp size variable if it exists for the object with VARNO as ssa
+   name version and if it doesn't exist, create one.  */
+
+static tree
+make_or_get_tempsize (struct object_size_info *osi, unsigned varno)
+{
+  tree ssa = object_sizes_get (osi, varno);
+  if (TREE_CODE (ssa) != SSA_NAME)
+    ssa = make_tempsize (osi, varno);
+  else if (dump_file)
+    {
+      fprintf (dump_file, "  temp name already assigned: ");
+      print_generic_expr (dump_file, ssa, dump_flags);
+      fprintf (dump_file, "\n");
+    }
+  return ssa;
+}
+
 /* Compute object_sizes for PTR, defined to VALUE, which is not an SSA_NAME.  */
 
 static tree
@@ -1114,9 +1267,112 @@ cond_expr_object_size (struct object_size_info *osi, gimple *stmt)
   else
     elsebytes = expr_object_size (osi, else_);
 
+  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);
+
   return size_binop (OST_TREE_CODE (object_size_type), thenbytes, elsebytes);
 }
 
+static tree
+phi_object_size (struct object_size_info *osi, gimple *stmt)
+{
+  int object_size_type = osi->object_size_type;
+  unsigned i;
+
+  tree res = size_initval (object_size_type);
+
+  for (i = 0; i < gimple_phi_num_args (stmt); i++)
+    {
+      tree rhs = gimple_phi_arg (stmt, i)->def;
+      tree phires;
+
+      if (TREE_CODE (rhs) == SSA_NAME)
+	phires = ssa_object_size (osi, rhs, size_int (0));
+      else
+	phires = expr_object_size (osi, rhs);
+
+      res = size_binop (OST_TREE_CODE (object_size_type), res, phires);
+
+      if (size_unknown_p (phires, object_size_type))
+	break;
+    }
+  return res;
+}
+
+static tree
+phi_dynamic_object_size (struct object_size_info *osi, tree var)
+{
+  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;
+
+  vec<tree> sizes;
+  sizes.create (0);
+  sizes.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;
+
+      if (TREE_CODE (rhs) != SSA_NAME)
+	sz = expr_object_size (osi, rhs);
+      else
+	sz = ssa_object_size (osi, rhs, size_int (0));
+
+      if (size_unknown_p (sz, object_size_type))
+	break;
+
+      sizes[i] = sz;
+    }
+
+  if (i == num_args)
+    {
+      res = make_or_get_tempsize (osi, varno);
+      bitmap_set_bit (osi->phiresults, SSA_NAME_VERSION (res));
+      object_sizes_initialize (osi, SSA_NAME_VERSION (res), res);
+
+      gphi *phi = create_phi_node (res, gimple_bb (stmt));
+      gphi *obj_phi =  as_a <gphi *> (stmt);
+
+      for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++)
+	{
+	  if (!is_gimple_variable (sizes[i]))
+	    {
+	      tree ssa = make_tempsize (osi, varno);
+	      object_sizes_initialize (osi, SSA_NAME_VERSION (ssa), sizes[i]);
+	      sizes[i] = ssa;
+	    }
+
+	  add_phi_arg (phi, sizes[i],
+		       gimple_phi_arg_edge (obj_phi, i),
+		       gimple_phi_arg_location (obj_phi, i));
+	}
+
+      if (dump_file)
+	{
+	  print_generic_expr (dump_file, var, dump_flags);
+	  fprintf (dump_file, ": PHI Node with result: ");
+	  print_gimple_stmt (dump_file, phi, dump_flags);
+	}
+    }
+  else
+    res = size_unknown (object_size_type);
+
+  sizes.release ();
+
+  return res;
+}
+
 /* Compute object sizes for VAR.
    For ADDR_EXPR an object size is the number of remaining bytes
    to the end of the object (where what is considered an object depends on
@@ -1164,14 +1420,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 	  print_generic_expr (dump_file, var, dump_flags);
 	  fprintf (dump_file, "\n");
 	}
-      res = object_sizes_get (osi, varno);
-      if (TREE_CODE (res) != SSA_NAME)
-	res = make_tempsize (osi, varno);
-      else if (dump_file)
-	{
-	  fprintf (dump_file, "  temp name already assigned: ");
-	  print_generic_expr (dump_file, res, dump_flags);
-	}
+      res = make_or_get_tempsize (osi, varno);
       goto out;
     }
 
@@ -1242,34 +1491,24 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 
     case GIMPLE_PHI:
       {
-	unsigned i;
-
-	res = size_initval (object_size_type);
-
-	for (i = 0; i < gimple_phi_num_args (stmt); i++)
-	  {
-	    tree rhs = gimple_phi_arg (stmt, i)->def;
-	    tree phires;
-
-	    if (object_sizes_unknown_p (object_size_type, varno))
-	      break;
-
-	    if (TREE_CODE (rhs) == SSA_NAME)
-	      phires = ssa_object_size (osi, rhs, size_int (0));
-	    else
-	      phires = expr_object_size (osi, rhs);
-
-	    res = size_binop (OST_TREE_CODE (object_size_type), res, phires);
-
-	    if (size_unknown_p (phires, object_size_type))
-	      break;
-	  }
+	if (object_size_type & OST_DYNAMIC)
+	  res = phi_dynamic_object_size (osi, var);
+	else
+	  res = phi_object_size (osi, stmt);
 	break;
       }
 
     default:
       gcc_unreachable ();
     }
+
+  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);
@@ -1419,7 +1658,7 @@ object_sizes_execute (function *fun, bool early)
 	     and rather than folding the builtin to the constant if any,
 	     create a MIN_EXPR or MAX_EXPR of the __builtin_object_size
 	     call result and the computed constant.  */
-	  if (early)
+	  if (early && !dynamic)
 	    {
 	      early_object_sizes_execute_one (&i, call);
 	      continue;
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH 07/10] tree-object-size: Handle function parameters
  2021-11-09 19:01 [PATCH 00/10] __builtin_dynamic_object_size Siddhesh Poyarekar
                   ` (5 preceding siblings ...)
  2021-11-09 19:01 ` [PATCH 06/10] tree-object-size: Support dynamic sizes in conditions Siddhesh Poyarekar
@ 2021-11-09 19:01 ` Siddhesh Poyarekar
  2021-11-09 19:01 ` [PATCH 08/10] tree-object-size: Handle GIMPLE_CALL Siddhesh Poyarekar
                   ` (7 subsequent siblings)
  14 siblings, 0 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-09 19:01 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Handle hints provided by __attribute__ ((access (...))) to compute
dynamic sizes for objects.

gcc/ChangeLog:

	* tree-object-size.c: Include tree-dfa.h.
	(parm_object_size): New function.
	(collect_object_sizes_for): Call it.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-dynamic-object-size-0.c (test_parmsz_simple):
	New function.
	(main): Call it.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 .../gcc.dg/builtin-dynamic-object-size-0.c    | 10 ++++
 gcc/tree-object-size.c                        | 55 ++++++++++++++++++-
 2 files changed, 64 insertions(+), 1 deletion(-)

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 ddedf6a49bd..80896d54693 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -46,6 +46,13 @@ test_deploop (size_t sz, size_t cond)
   return __builtin_dynamic_object_size (bin, 0);
 }
 
+size_t
+__attribute__ ((access (__read_write__, 1, 2)))
+test_parmsz_simple (void *obj, size_t sz)
+{
+  return __builtin_dynamic_object_size (obj, 0);
+}
+
 unsigned nfails = 0;
 
 #define FAIL() ({ \
@@ -64,6 +71,9 @@ main (int argc, char **argv)
     FAIL ();
   if (test_deploop (128, 129) != 32)
     FAIL ();
+  if (test_parmsz_simple (argv[0], __builtin_strlen (argv[0]) + 1)
+      != __builtin_strlen (argv[0]) + 1)
+    FAIL ();
 
   if (nfails > 0)
     __builtin_abort ();
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index 33598ddc91c..5a80432d40c 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-fold.h"
 #include "gimple-iterator.h"
 #include "tree-cfg.h"
+#include "tree-dfa.h"
 #include "stringpool.h"
 #include "attribs.h"
 #include "builtins.h"
@@ -1373,6 +1374,58 @@ phi_dynamic_object_size (struct object_size_info *osi, tree var)
   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)
+{
+  int object_size_type = osi->object_size_type;
+  tree parm = SSA_NAME_VAR (var);
+
+  if (dump_file)
+    {
+      fprintf (dump_file, "Object is a parameter: ");
+      print_generic_expr (dump_file, parm, dump_flags);
+      fprintf (dump_file, " which is %s a pointer type\n",
+	       POINTER_TYPE_P (TREE_TYPE (parm)) ? "" : "not");
+    }
+
+  if (!(object_size_type & OST_DYNAMIC) || !POINTER_TYPE_P (TREE_TYPE (parm)))
+    return expr_object_size (osi, parm);
+
+  /* Look for access attribute.  */
+  rdwr_map rdwr_idx;
+
+  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)));
+
+  if (access && access->sizarg != UINT_MAX)
+    {
+      tree fnargs = DECL_ARGUMENTS (fndecl);
+      tree arg = NULL_TREE;
+      unsigned argpos = 0;
+
+      /* Walk through the parameters to pick the size parameter and safely
+	 scale it by the type size.  */
+      for (arg = fnargs; argpos != access->sizarg && arg;
+	   arg = TREE_CHAIN (arg), ++argpos);
+
+      if (arg != NULL_TREE && INTEGRAL_TYPE_P (TREE_TYPE (arg)))
+	{
+	  tree 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);
+}
+
 /* Compute object sizes for VAR.
    For ADDR_EXPR an object size is the number of remaining bytes
    to the end of the object (where what is considered an object depends on
@@ -1483,7 +1536,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
     case GIMPLE_NOP:
       if (SSA_NAME_VAR (var)
 	  && TREE_CODE (SSA_NAME_VAR (var)) == PARM_DECL)
-	res = expr_object_size (osi, SSA_NAME_VAR (var));
+	res = parm_object_size (osi, var);
       else
 	/* Uninitialized SSA names point nowhere.  */
 	res = size_unknown (object_size_type);
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH 08/10] tree-object-size: Handle GIMPLE_CALL
  2021-11-09 19:01 [PATCH 00/10] __builtin_dynamic_object_size Siddhesh Poyarekar
                   ` (6 preceding siblings ...)
  2021-11-09 19:01 ` [PATCH 07/10] tree-object-size: Handle function parameters Siddhesh Poyarekar
@ 2021-11-09 19:01 ` Siddhesh Poyarekar
  2021-11-09 19:01 ` [PATCH 09/10] tree-object-size: Dynamic sizes for ADDR_EXPR Siddhesh Poyarekar
                   ` (6 subsequent siblings)
  14 siblings, 0 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-09 19:01 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Handle non-constant expressions in GIMPLE_CALL arguments.  Also handle
alloca.

gcc/ChangeLog:

	* tree-object-size.c (alloc_object_size): Make and return
	non-constant size expression.
	(call_object_size): Return expression or unknown based on
	whether dynamic object size is requested.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-dynamic-object-size-0.c: Add new tests.
	* gcc.dg/builtin-object-size-1.c (test1) [DYNAMIC_OBJECT_SIZE]:
	Alter expected result for dynamic object size.
	* gcc.dg/builtin-object-size-2.c (test1) [DYNAMIC_OBJECT_SIZE]:
	Likewise.
	* gcc.dg/builtin-object-size-3.c (test1) [DYNAMIC_OBJECT_SIZE]:
	Likewise.
	* gcc.dg/builtin-object-size-4.c (test1) [DYNAMIC_OBJECT_SIZE]:
	Likewise.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 .../gcc.dg/builtin-dynamic-object-size-0.c    | 227 +++++++++++++++++-
 gcc/testsuite/gcc.dg/builtin-object-size-1.c  |   7 +
 gcc/testsuite/gcc.dg/builtin-object-size-2.c  |  14 ++
 gcc/testsuite/gcc.dg/builtin-object-size-3.c  |   7 +
 gcc/testsuite/gcc.dg/builtin-object-size-4.c  |  14 ++
 gcc/tree-object-size.c                        |  26 +-
 6 files changed, 285 insertions(+), 10 deletions(-)

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 80896d54693..4ad49a51878 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -4,12 +4,71 @@
 typedef __SIZE_TYPE__ size_t;
 #define abort __builtin_abort
 
+void *
+__attribute__ ((alloc_size (1)))
+__attribute__ ((__nothrow__ , __leaf__))
+__attribute__ ((noinline))
+alloc_func (size_t sz)
+{
+  return __builtin_malloc (sz);
+}
+
+void *
+__attribute__ ((alloc_size (1, 2)))
+__attribute__ ((__nothrow__ , __leaf__))
+__attribute__ ((noinline))
+calloc_func (size_t cnt, size_t sz)
+{
+  return __builtin_calloc (cnt, sz);
+}
+
+void *
+__attribute__ ((noinline))
+unknown_allocator (size_t cnt, size_t sz)
+{
+  return __builtin_calloc (cnt, sz);
+}
+
+size_t
+__attribute__ ((noinline))
+test_unknown (size_t cnt, size_t sz)
+{
+  void *ret = unknown_allocator (cnt, sz);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+/* Malloc-like allocator.  */
+
+size_t
+__attribute__ ((noinline))
+test_malloc (size_t sz)
+{
+  void *ret = alloc_func (sz);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc (size_t sz)
+{
+  void *ret = __builtin_malloc (sz);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_cond (int cond)
+{
+  void *ret = __builtin_malloc (cond ? 32 : 64);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
 size_t
 __attribute__ ((noinline))
 test_builtin_malloc_condphi (int cond)
 {
   void *ret;
- 
+
   if (cond)
     ret = __builtin_malloc (32);
   else
@@ -18,6 +77,79 @@ test_builtin_malloc_condphi (int cond)
   return __builtin_dynamic_object_size (ret, 0);
 }
 
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi2 (int cond, size_t in)
+{
+  void *ret;
+
+  if (cond)
+    ret = __builtin_malloc (in);
+  else
+    ret = __builtin_malloc (64);
+
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi3 (int cond, size_t in, size_t in2)
+{
+  void *ret;
+
+  if (cond)
+    ret = __builtin_malloc (in);
+  else
+    ret = __builtin_malloc (in2);
+
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi4 (size_t sz, int cond)
+{
+  char *a = __builtin_malloc (sz);
+  char b[sz / 2];
+
+  return __builtin_dynamic_object_size (cond ? b : (void *) &a, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi5 (size_t sz, int cond, char *c)
+{
+  char *a = __builtin_malloc (sz);
+
+  return __builtin_dynamic_object_size (cond ? c : (void *) &a, 0);
+}
+
+/* Calloc-like allocator.  */
+
+size_t
+__attribute__ ((noinline))
+test_calloc (size_t cnt, size_t sz)
+{
+  void *ret = calloc_func (cnt, sz);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_calloc (size_t cnt, size_t sz)
+{
+  void *ret = __builtin_calloc (cnt, sz);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_calloc_cond (int cond1, int cond2)
+{
+  void *ret = __builtin_calloc (cond1 ? 32 : 64, cond2 ? 1024 : 16);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
 size_t
 __attribute__ ((noinline))
 test_builtin_calloc_condphi (size_t cnt, size_t sz, int cond)
@@ -33,6 +165,47 @@ test_builtin_calloc_condphi (size_t cnt, size_t sz, int cond)
   return __builtin_dynamic_object_size (cond ? ch : (void *) &bin, 0);
 }
 
+/* Passthrough functions.  */
+
+size_t
+__attribute__ ((noinline))
+test_passthrough (size_t sz, char *in)
+{
+  char *bin = __builtin_malloc (sz);
+  char *dest = __builtin_memcpy (bin, in, sz);
+
+  return __builtin_dynamic_object_size (dest, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_passthrough_nonssa (char *in)
+{
+  char bin[__builtin_strlen (in) + 1];
+  char *dest = __builtin_memcpy (bin, in, __builtin_strlen (in) + 1);
+
+  return __builtin_dynamic_object_size (dest, 0);
+}
+
+/* Variable length arrays.  */
+size_t
+__attribute__ ((noinline))
+test_dynarray (size_t sz)
+{
+  char bin[sz];
+
+  return __builtin_dynamic_object_size (bin, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_dynarray_cond (int cond)
+{
+  char bin[cond ? 8 : 16];
+
+  return __builtin_dynamic_object_size (bin, 0);
+}
+
 size_t
 __attribute__ ((noinline))
 test_deploop (size_t sz, size_t cond)
@@ -41,7 +214,7 @@ test_deploop (size_t sz, size_t cond)
 
   for (size_t i = 0; i < sz; i++)
     if (i == cond)
-      bin = __builtin_alloca (64);
+      bin = __builtin_alloca (sz);
 
   return __builtin_dynamic_object_size (bin, 0);
 }
@@ -63,12 +236,62 @@ unsigned nfails = 0;
 int
 main (int argc, char **argv)
 {
+  size_t outsz = test_unknown (32, 42);
+  if (outsz != -1 && outsz != 32)
+    FAIL ();
+  if (test_malloc (2048) != 2048)
+    FAIL ();
+  if (test_builtin_malloc (2048) != 2048)
+    FAIL ();
+  if (test_builtin_malloc_cond (1) != 32)
+    FAIL ();
+  if (test_builtin_malloc_cond (0) != 64)
+    FAIL ();
   if (test_builtin_malloc_condphi (1) != 32)
     FAIL ();
   if (test_builtin_malloc_condphi (0) != 64)
     FAIL ();
+  if (test_builtin_malloc_condphi2 (1, 128) != 128)
+    FAIL ();
+  if (test_builtin_malloc_condphi2 (0, 128) != 64)
+    FAIL ();
+  if (test_builtin_malloc_condphi3 (1, 128, 256) != 128)
+    FAIL ();
+  if (test_builtin_malloc_condphi3 (0, 128, 256) != 256)
+    FAIL ();
+  if (test_builtin_malloc_condphi4 (128, 1) != 64)
+    FAIL ();
+  if (test_builtin_malloc_condphi4 (128, 0) != sizeof (void *))
+    FAIL ();
+  if (test_builtin_malloc_condphi5 (128, 0, argv[0]) != -1)
+    FAIL ();
+  if (test_calloc (2048, 4) != 2048 * 4)
+    FAIL ();
+  if (test_builtin_calloc (2048, 8) != 2048 * 8)
+    FAIL ();
+  if (test_builtin_calloc_cond (0, 0) != 64 * 16)
+    FAIL ();
+  if (test_builtin_calloc_cond (1, 1) != 32 * 1024)
+    FAIL ();
+  if (test_builtin_calloc_condphi (128, 1, 1) != 128)
+    FAIL ();
   if (test_builtin_calloc_condphi (128, 1, 0) == 128)
     FAIL ();
+  if (test_builtin_calloc_condphi (128, 1, 0) == -1)
+    FAIL ();
+  if (test_passthrough (__builtin_strlen (argv[0]) + 1, argv[0])
+      != __builtin_strlen (argv[0]) + 1)
+    FAIL ();
+  if (test_passthrough_nonssa (argv[0]) != __builtin_strlen (argv[0]) + 1)
+    FAIL ();
+  if (test_dynarray (__builtin_strlen (argv[0])) != __builtin_strlen (argv[0]))
+    FAIL ();
+  if (test_dynarray_cond (0) != 16)
+    FAIL ();
+  if (test_dynarray_cond (1) != 8)
+    FAIL ();
+  if (test_deploop (128, 4) != 128)
+    FAIL ();
   if (test_deploop (128, 129) != 32)
     FAIL ();
   if (test_parmsz_simple (argv[0], __builtin_strlen (argv[0]) + 1)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
index 76df4c96271..606141f9405 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
@@ -135,10 +135,17 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&extb[5], 0) != sizeof (extb) - 5)
     abort ();
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (var, 0) != x + 10)
+    abort ();
+  if (__builtin_object_size (var + 10, 0) != x)
+    abort ();
+#else
   if (__builtin_object_size (var, 0) != (size_t) -1)
     abort ();
   if (__builtin_object_size (var + 10, 0) != (size_t) -1)
     abort ();
+#endif
   if (__builtin_object_size (&var[5], 0) != (size_t) -1)
     abort ();
   if (__builtin_object_size (zerol, 0) != 0)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
index c395d2e95b3..57fd17d5a45 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-2.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
@@ -137,16 +137,30 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&extc[5].c[3], 1) != (size_t) -1)
     abort ();
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (var, 1) != x + 10)
+    abort ();
+  if (__builtin_object_size (var + 10, 1) != x)
+    abort ();
+#else
   if (__builtin_object_size (var, 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (var + 10, 1) != (size_t) -1)
     abort ();
+#endif
   if (__builtin_object_size (&var[5], 1) != (size_t) -1)
     abort ();
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (vara, 1) != (x + 10) * sizeof (struct A))
+    abort ();
+  if (__builtin_object_size (vara + 10, 1) != x * sizeof (struct A))
+    abort ();    
+#else
   if (__builtin_object_size (vara, 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (vara + 10, 1) != (size_t) -1)
     abort ();    
+#endif
   if (__builtin_object_size (&vara[5], 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (&vara[0].a, 1) != sizeof (vara[0].a))
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
index ccdc6ef8cc8..5b323ca3527 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-3.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
@@ -140,10 +140,17 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&extb[5], 2) != sizeof (extb) - 5)
     abort ();
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (var, 2) != x + 10)
+    abort ();
+  if (__builtin_object_size (var + 10, 2) != x)
+    abort ();
+#else
   if (__builtin_object_size (var, 2) != 0)
     abort ();
   if (__builtin_object_size (var + 10, 2) != 0)
     abort ();
+#endif
   if (__builtin_object_size (&var[5], 2) != 0)
     abort ();
   if (__builtin_object_size (zerol, 2) != 0)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
index 002512d38ab..2d222301eff 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-4.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
@@ -150,16 +150,30 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&extc[5].c[3], 3) != 0)
     abort ();
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (var, 3) != x + 10)
+    abort ();
+  if (__builtin_object_size (var + 10, 3) != x)
+    abort ();
+#else
   if (__builtin_object_size (var, 3) != 0)
     abort ();
   if (__builtin_object_size (var + 10, 3) != 0)
     abort ();
+#endif
   if (__builtin_object_size (&var[5], 3) != 0)
     abort ();
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (vara, 3) != (x + 10) * sizeof (struct A))
+    abort ();
+  if (__builtin_object_size (vara + 10, 3) != x * sizeof (struct A))
+    abort ();    
+#else
   if (__builtin_object_size (vara, 3) != 0)
     abort ();
   if (__builtin_object_size (vara + 10, 3) != 0)
     abort ();    
+#endif
   if (__builtin_object_size (&vara[5], 3) != 0)
     abort ();
   if (__builtin_object_size (&vara[0].a, 3) != sizeof (vara[0].a))
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index 5a80432d40c..d5b0c8226f0 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -584,7 +584,8 @@ alloc_object_size (const gcall *call, int object_size_type)
   gcc_assert (is_gimple_call (call));
 
   tree calltype;
-  if (tree callfn = gimple_call_fndecl (call))
+  tree callfn = gimple_call_fndecl (call);
+  if (callfn)
     calltype = TREE_TYPE (callfn);
   else
     calltype = gimple_call_fntype (call);
@@ -604,12 +605,13 @@ alloc_object_size (const gcall *call, int object_size_type)
       if (TREE_CHAIN (p))
         arg2 = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (p)))-1;
     }
+  else if (gimple_call_builtin_p (call, BUILT_IN_NORMAL)
+	   && callfn && ALLOCA_FUNCTION_CODE_P (DECL_FUNCTION_CODE (callfn)))
+  arg1 = 0;
 
-  if (arg1 < 0 || arg1 >= (int)gimple_call_num_args (call)
-      || TREE_CODE (gimple_call_arg (call, arg1)) != INTEGER_CST
-      || (arg2 >= 0
-	  && (arg2 >= (int)gimple_call_num_args (call)
-	      || TREE_CODE (gimple_call_arg (call, arg2)) != INTEGER_CST)))
+  /* Non-const arguments are OK here, let the caller handle constness.  */
+  if (arg1 < 0 || arg1 >= (int) gimple_call_num_args (call)
+      || arg2 >= (int) gimple_call_num_args (call))
     return size_unknown (object_size_type);
 
   tree bytes = NULL_TREE;
@@ -620,7 +622,10 @@ alloc_object_size (const gcall *call, int object_size_type)
   else if (arg1 >= 0)
     bytes = fold_convert (sizetype, gimple_call_arg (call, arg1));
 
-  return bytes;
+  if (bytes)
+    return STRIP_NOPS (bytes);
+
+  return size_unknown (object_size_type);
 }
 
 
@@ -1156,7 +1161,12 @@ call_object_size (struct object_size_info *osi, gcall *call)
 
   gcc_assert (is_gimple_call (call));
 
-  return alloc_object_size (call, object_size_type);
+  tree bytes = alloc_object_size (call, object_size_type);
+
+  if ((object_size_type & OST_DYNAMIC) || TREE_CODE (bytes) == INTEGER_CST)
+    return bytes;
+
+  return  size_unknown (object_size_type);
 }
 
 
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH 09/10] tree-object-size: Dynamic sizes for ADDR_EXPR
  2021-11-09 19:01 [PATCH 00/10] __builtin_dynamic_object_size Siddhesh Poyarekar
                   ` (7 preceding siblings ...)
  2021-11-09 19:01 ` [PATCH 08/10] tree-object-size: Handle GIMPLE_CALL Siddhesh Poyarekar
@ 2021-11-09 19:01 ` Siddhesh Poyarekar
  2021-11-09 19:01 ` [PATCH 10/10] tree-object-size: Handle dynamic offsets Siddhesh Poyarekar
                   ` (5 subsequent siblings)
  14 siblings, 0 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-09 19:01 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Allow dynamic expressions from ADDR_EXPR for
__builtin_dynamic_object_size.  Offsets in objects still need to be
constant for now because offset computation need more validation to
support, e.g. negative offsets with dynamic sizes.

gcc/ChangeLog:

	* tree-object-size.c (size_known_p): New function.
	(addr_object_size): Build dynamic expressions for object
	sizes.
	(estimate_size): Limit PLUS_EXPR rewriting to static object
	sizes.
	(call_object_size): Call size_known_p.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-dynamic-object-size-0.c: Add new tests.
	* gcc.dg/builtin-object-size-1.c (test1) [DYNAMIC_OBJECT_SIZE]:
	Adjust expected output for dynamic object sizes.
	* gcc.dg/builtin-object-size-2.c (test1) [DYNAMIC_OBJECT_SIZE]:
	Likewise.
	* gcc.dg/builtin-object-size-3.c (test1) [DYNAMIC_OBJECT_SIZE]:
	Likewise.
	* gcc.dg/builtin-object-size-4.c (test1) [DYNAMIC_OBJECT_SIZE]:
	Likewise.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 .../gcc.dg/builtin-dynamic-object-size-0.c    | 71 ++++++++++++++
 gcc/testsuite/gcc.dg/builtin-object-size-1.c  | 30 +++++-
 gcc/testsuite/gcc.dg/builtin-object-size-2.c  | 43 +++++++--
 gcc/testsuite/gcc.dg/builtin-object-size-3.c  | 25 ++++-
 gcc/testsuite/gcc.dg/builtin-object-size-4.c  | 17 ++--
 gcc/tree-object-size.c                        | 93 +++++++++++--------
 6 files changed, 221 insertions(+), 58 deletions(-)

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 4ad49a51878..a1db63b2d45 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -219,6 +219,60 @@ test_deploop (size_t sz, size_t cond)
   return __builtin_dynamic_object_size (bin, 0);
 }
 
+/* Address expressions.  */
+
+struct dynarray_struct
+{
+  long a;
+  char c[16];
+  int b;
+};
+
+size_t
+__attribute__ ((noinline))
+test_dynarray_struct (size_t sz, size_t off)
+{
+  struct dynarray_struct bin[sz];
+
+  return __builtin_dynamic_object_size (&bin[off].c, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_dynarray_struct_subobj (size_t sz, size_t off)
+{
+  struct dynarray_struct bin[sz];
+
+  return __builtin_dynamic_object_size (&bin[off].c[4], 1);
+}
+
+size_t
+__attribute__ ((noinline))
+test_dynarray_struct_subobj2 (size_t sz, size_t off, size_t *objsz)
+{
+  struct dynarray_struct2
+    {
+      long a;
+      int b;
+      char c[sz];
+    };
+
+  struct dynarray_struct2 bin;
+
+  *objsz = sizeof (bin);
+
+  return __builtin_dynamic_object_size (&bin.c[off], 1);
+}
+
+size_t
+__attribute__ ((noinline))
+test_substring (size_t sz, size_t off)
+{
+  char str[sz];
+
+  return __builtin_dynamic_object_size (&str[off], 0);
+}
+
 size_t
 __attribute__ ((access (__read_write__, 1, 2)))
 test_parmsz_simple (void *obj, size_t sz)
@@ -286,6 +340,23 @@ main (int argc, char **argv)
     FAIL ();
   if (test_dynarray (__builtin_strlen (argv[0])) != __builtin_strlen (argv[0]))
     FAIL ();
+  if (test_dynarray_struct (42, 4) !=
+      ((42 - 4) * sizeof (struct dynarray_struct)
+       - __builtin_offsetof (struct dynarray_struct, c)))
+    FAIL ();
+  if (test_dynarray_struct (42, 48) != 0)
+    FAIL ();
+  if (test_substring (128, 4) != 128 - 4)
+    FAIL ();
+  if (test_substring (128, 142) != 0)
+    FAIL ();
+  if (test_dynarray_struct_subobj (42, 4) != 16 - 4)
+    FAIL ();
+  if (test_dynarray_struct_subobj (42, 48) != 0)
+    FAIL ();
+  size_t objsz = 0;
+  if (test_dynarray_struct_subobj2 (42, 4, &objsz) != objsz - 4 - 12)
+    FAIL ();
   if (test_dynarray_cond (0) != 16)
     FAIL ();
   if (test_dynarray_cond (1) != 8)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
index 606141f9405..ba5a34c1f8e 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
@@ -81,30 +81,56 @@ test1 (void *q, int x)
     r = malloc (30);
   else
     r = calloc (2, 16);
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 0) != (x < 20 ? 30 : 2 * 16))
+    abort ();
+#else
   /* We may duplicate this test onto the two exit paths.  On one path
      the size will be 32, the other it will be 30.  If we don't duplicate
      this test, then the size will be 32.  */
   if (__builtin_object_size (r, 0) != 2 * 16
       && __builtin_object_size (r, 0) != 30)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
     r = calloc (2, 14);
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 0) != (x < 20 ? 30 : 2 * 14))
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != 30)
     abort ();
+#endif
   if (x < 30)
     r = malloc (sizeof (a));
   else
     r = &a.a[3];
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 0) != (x < 30 ? sizeof (a) : sizeof (a) - 3))
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != sizeof (a))
     abort ();
+#endif
   r = memcpy (r, "a", 2);
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 0) != (x < 30 ? sizeof (a) : sizeof (a) - 3))
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != sizeof (a))
     abort ();
+#endif
   r = memcpy (r + 2, "b", 2) + 2;
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 0)
+      != (x < 30 ? sizeof (a) - 4 : sizeof (a) - 7))
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != sizeof (a) - 4)
     abort ();
+#endif
   r = &a.a[4];
   r = memset (r, 'a', 2);
   if (__builtin_object_size (r, 0)
@@ -140,14 +166,16 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (var + 10, 0) != x)
     abort ();
+  if (__builtin_object_size (&var[5], 0) != x + 5)
+    abort ();
 #else
   if (__builtin_object_size (var, 0) != (size_t) -1)
     abort ();
   if (__builtin_object_size (var + 10, 0) != (size_t) -1)
     abort ();
-#endif
   if (__builtin_object_size (&var[5], 0) != (size_t) -1)
     abort ();
+#endif
   if (__builtin_object_size (zerol, 0) != 0)
     abort ();
   if (__builtin_object_size (&zerol, 0) != 0)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
index 57fd17d5a45..4af30e8c580 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-2.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
@@ -75,30 +75,56 @@ test1 (void *q, int x)
     r = malloc (30);
   else
     r = calloc (2, 16);
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 1) != (x < 20 ? 30 : 2 * 16))
+    abort ();
+#else
   /* We may duplicate this test onto the two exit paths.  On one path
      the size will be 32, the other it will be 30.  If we don't duplicate
      this test, then the size will be 32.  */
   if (__builtin_object_size (r, 1) != 2 * 16
       && __builtin_object_size (r, 1) != 30)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
     r = calloc (2, 14);
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 1) != (x < 20 ? 30 : 2 * 14))
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != 30)
     abort ();
+#endif
   if (x < 30)
     r = malloc (sizeof (a));
   else
     r = &a.a[3];
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 1) != (x < 30 ? sizeof (a) : sizeof (a) - 3))
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a))
     abort ();
+#endif
   r = memcpy (r, "a", 2);
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 1) != (x < 30 ? sizeof (a) : sizeof (a) - 3))
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a))
     abort ();
+#endif
   r = memcpy (r + 2, "b", 2) + 2;
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 0)
+      != (x < 30 ? sizeof (a) - 4 : sizeof (a) - 7))
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a) - 4)
     abort ();
+#endif
   r = &a.a[4];
   r = memset (r, 'a', 2);
   if (__builtin_object_size (r, 1) != sizeof (a.a) - 4)
@@ -142,27 +168,28 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (var + 10, 1) != x)
     abort ();
+  if (__builtin_object_size (&var[5], 1) != x + 5)
+    abort ();
+  if (__builtin_object_size (vara, 1) != (x + 10) * sizeof (struct A))
+    abort ();
+  if (__builtin_object_size (vara + 10, 1) != x * sizeof (struct A))
+    abort ();    
+  if (__builtin_object_size (&vara[5], 1) != (x + 5) * sizeof (struct A))
+    abort ();
 #else
   if (__builtin_object_size (var, 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (var + 10, 1) != (size_t) -1)
     abort ();
-#endif
   if (__builtin_object_size (&var[5], 1) != (size_t) -1)
     abort ();
-#ifdef DYNAMIC_OBJECT_SIZE
-  if (__builtin_object_size (vara, 1) != (x + 10) * sizeof (struct A))
-    abort ();
-  if (__builtin_object_size (vara + 10, 1) != x * sizeof (struct A))
-    abort ();    
-#else
   if (__builtin_object_size (vara, 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (vara + 10, 1) != (size_t) -1)
     abort ();    
-#endif
   if (__builtin_object_size (&vara[5], 1) != (size_t) -1)
     abort ();
+#endif
   if (__builtin_object_size (&vara[0].a, 1) != sizeof (vara[0].a))
     abort ();
   if (__builtin_object_size (&vara[10].a[0], 1) != sizeof (vara[0].a))
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
index 5b323ca3527..ce4418e785b 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-3.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
@@ -42,9 +42,17 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (q, 2) != 0)
     abort ();
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 2)
+      != (x < 0
+	  ? sizeof (a) - __builtin_offsetof (struct A, a) - 9
+	  : sizeof (a) - __builtin_offsetof (struct A, c) - 1))
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, c) - 1)
     abort ();
+#endif
   if (x < 6)
     r = &w[2].a[1];
   else
@@ -58,15 +66,28 @@ test1 (void *q, int x)
   if (__builtin_object_size (&y.b, 2)
       != sizeof (a) - __builtin_offsetof (struct A, b))
     abort ();
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 2)
+      != (x < 6
+	  ? 2 * sizeof (w[0]) - __builtin_offsetof (struct A, a) - 1
+	  : sizeof (a) - __builtin_offsetof (struct A, a) - 6))
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, a) - 6)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
     r = calloc (2, 16);
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 2) != (x < 20 ? 30 : 2 * 16))
+    abort ();
+#else
   if (__builtin_object_size (r, 2) != 30)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
@@ -145,14 +166,16 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (var + 10, 2) != x)
     abort ();
+  if (__builtin_object_size (&var[5], 2) != x + 5)
+    abort ();
 #else
   if (__builtin_object_size (var, 2) != 0)
     abort ();
   if (__builtin_object_size (var + 10, 2) != 0)
     abort ();
-#endif
   if (__builtin_object_size (&var[5], 2) != 0)
     abort ();
+#endif
   if (__builtin_object_size (zerol, 2) != 0)
     abort ();
   if (__builtin_object_size (&zerol, 2) != 0)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
index 2d222301eff..81e0255dcc4 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-4.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
@@ -155,27 +155,28 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (var + 10, 3) != x)
     abort ();
+  if (__builtin_object_size (&var[5], 3) != x + 5)
+    abort ();
+  if (__builtin_object_size (vara, 3) != (x + 10) * sizeof (struct A))
+    abort ();
+  if (__builtin_object_size (vara + 10, 3) != x * sizeof (struct A))
+    abort ();    
+  if (__builtin_object_size (&vara[5], 3) != (x + 5) * sizeof (struct A))
+    abort ();
 #else
   if (__builtin_object_size (var, 3) != 0)
     abort ();
   if (__builtin_object_size (var + 10, 3) != 0)
     abort ();
-#endif
   if (__builtin_object_size (&var[5], 3) != 0)
     abort ();
-#ifdef DYNAMIC_OBJECT_SIZE
-  if (__builtin_object_size (vara, 3) != (x + 10) * sizeof (struct A))
-    abort ();
-  if (__builtin_object_size (vara + 10, 3) != x * sizeof (struct A))
-    abort ();    
-#else
   if (__builtin_object_size (vara, 3) != 0)
     abort ();
   if (__builtin_object_size (vara + 10, 3) != 0)
     abort ();    
-#endif
   if (__builtin_object_size (&vara[5], 3) != 0)
     abort ();
+#endif
   if (__builtin_object_size (&vara[0].a, 3) != sizeof (vara[0].a))
     abort ();
   if (__builtin_object_size (&vara[10].a[0], 3) != sizeof (vara[0].a))
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index d5b0c8226f0..865fc3feea5 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -123,6 +123,16 @@ size_unknown_p (tree val, int object_size_type)
 	  && tree_to_uhwi (val) == unknown (object_size_type));
 }
 
+/* Return true if VAL is represents a known size for OBJECT_SIZE_TYPE.  */
+
+static inline bool
+size_known_p (tree val, int object_size_type)
+{
+  return (!size_unknown_p (val, object_size_type)
+	  && ((object_size_type & OST_DYNAMIC)
+	      || TREE_CODE (val) == INTEGER_CST));
+}
+
 /* Return a tree with initial value for OBJECT_SIZE_TYPE.  */
 
 static inline tree
@@ -391,16 +401,15 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
       if (!size_unknown_p (sz, object_size_type))
 	{
 	  tree offset = TREE_OPERAND (pt_var, 1);
-	  if (TREE_CODE (offset) != INTEGER_CST
-	      || TREE_CODE (sz) != INTEGER_CST)
+	  if (TREE_CODE (offset) != INTEGER_CST)
 	    sz = size_unknown (object_size_type);
 	  else
 	    sz = size_for_offset (sz, offset);
 	}
 
       if (!size_unknown_p (sz, object_size_type)
-	  && TREE_CODE (sz) == INTEGER_CST
-	  && compare_tree_int (sz, offset_limit) < 0)
+	  && (TREE_CODE (sz) != INTEGER_CST
+	      || compare_tree_int (sz, offset_limit) < 0))
 	pt_var_size = sz;
     }
   else if (DECL_P (pt_var))
@@ -416,8 +425,9 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 
   if (pt_var_size)
     {
-      /* Validate the size determined above.  */
-      if (compare_tree_int (pt_var_size, offset_limit) >= 0)
+      /* Validate the size determined above if it is a constant.  */
+      if (TREE_CODE (pt_var_size) == INTEGER_CST
+	  && compare_tree_int (pt_var_size, offset_limit) >= 0)
 	return false;
     }
 
@@ -441,7 +451,7 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 	    var = TREE_OPERAND (var, 0);
 	  if (! TYPE_SIZE_UNIT (TREE_TYPE (var))
 	      || ! tree_fits_uhwi_p (TYPE_SIZE_UNIT (TREE_TYPE (var)))
-	      || (pt_var_size
+	      || (pt_var_size && TREE_CODE (pt_var_size) == INTEGER_CST
 		  && tree_int_cst_lt (pt_var_size,
 				      TYPE_SIZE_UNIT (TREE_TYPE (var)))))
 	    var = pt_var;
@@ -455,17 +465,11 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 		switch (TREE_CODE (v))
 		  {
 		  case ARRAY_REF:
-		    if (TYPE_SIZE_UNIT (TREE_TYPE (TREE_OPERAND (v, 0)))
-			&& TREE_CODE (TREE_OPERAND (v, 1)) == INTEGER_CST)
+		    if (TYPE_SIZE_UNIT (TREE_TYPE (TREE_OPERAND (v, 0))))
 		      {
 			tree domain
 			  = TYPE_DOMAIN (TREE_TYPE (TREE_OPERAND (v, 0)));
-			if (domain
-			    && TYPE_MAX_VALUE (domain)
-			    && TREE_CODE (TYPE_MAX_VALUE (domain))
-			       == INTEGER_CST
-			    && tree_int_cst_lt (TREE_OPERAND (v, 1),
-						TYPE_MAX_VALUE (domain)))
+			if (domain && TYPE_MAX_VALUE (domain))
 			  {
 			    v = NULL_TREE;
 			    break;
@@ -532,20 +536,20 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 	var = pt_var;
 
       if (var != pt_var)
-	var_size = TYPE_SIZE_UNIT (TREE_TYPE (var));
+	{
+	  var_size = TYPE_SIZE_UNIT (TREE_TYPE (var));
+	  if (!TREE_CONSTANT (var_size))
+	    var_size = get_or_create_ssa_default_def (cfun, var_size);
+	  if (!var_size)
+	    return false;
+	}
       else if (!pt_var_size)
 	return false;
       else
 	var_size = pt_var_size;
       bytes = compute_object_offset (TREE_OPERAND (ptr, 0), var);
       if (bytes != error_mark_node)
-	{
-	  if (TREE_CODE (bytes) == INTEGER_CST
-	      && tree_int_cst_lt (var_size, bytes))
-	    bytes = size_zero_node;
-	  else
-	    bytes = size_binop (MINUS_EXPR, var_size, bytes);
-	}
+	bytes = size_for_offset (var_size, bytes);
       if (var != pt_var
 	  && pt_var_size
 	  && TREE_CODE (pt_var) == MEM_REF
@@ -554,11 +558,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)
 	    {
-	      if (TREE_CODE (bytes2) == INTEGER_CST
-		  && tree_int_cst_lt (pt_var_size, bytes2))
-		bytes2 = size_zero_node;
-	      else
-		bytes2 = size_binop (MINUS_EXPR, pt_var_size, bytes2);
+	      bytes2 = size_for_offset (pt_var_size, bytes2);
 	      bytes = size_binop (MIN_EXPR, bytes, bytes2);
 	    }
 	}
@@ -568,8 +568,13 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
   else
     bytes = pt_var_size;
 
-  *psize = bytes;
-  return true;
+  if (size_known_p (bytes, object_size_type))
+    {
+      *psize = bytes;
+      return true;
+    }
+
+  return false;
 }
 
 
@@ -739,19 +744,27 @@ estimate_size (object_size_info *osi, tree size, bitmap *visitlog = NULL)
     case PLUS_EXPR:
 	{
 	  tree ret = estimate_size (osi, TREE_OPERAND (size, 0), visitlog);
+	  tree off = TREE_OPERAND (size, 1);
 
 	  if (size_unknown_p (ret, object_size_type))
 	    return size_unknown (object_size_type);
 
-	  tree off = TREE_OPERAND (size, 1);
-	  gcc_checking_assert (TREE_CODE (off) == INTEGER_CST);
-
-	  if (code == PLUS_EXPR)
-	    off = fold_build1 (NEGATE_EXPR, sizetype, off);
-
-	  if (tree_fits_uhwi_p (ret) && tree_int_cst_le (ret, off))
-	    return size_int (0);
-	  return size_binop (MINUS_EXPR, ret, off);
+	  /* We don't need this for dynamic object sizes because we don't make
+	     reducing estimates like in case of static sizes.  Instead, we
+	     return an expression that gives the precise size after a specific
+	     number of iterations, reducing to zero at runtime if the pointer
+	     being evaluated overflows.  */
+	  if (!(object_size_type & OST_DYNAMIC))
+	    {
+	      gcc_checking_assert (TREE_CODE (off) == INTEGER_CST);
+	      if (code == PLUS_EXPR)
+		{
+		  off = fold_build1 (NEGATE_EXPR, sizetype, off);
+		  ret = size_binop (MAX_EXPR, ret, off);
+		  code = MINUS_EXPR;
+		}
+	    }
+	  return size_binop (code, ret, off);
 	}
     case INTEGER_CST:
     default:
@@ -1163,7 +1176,7 @@ call_object_size (struct object_size_info *osi, gcall *call)
 
   tree bytes = alloc_object_size (call, object_size_type);
 
-  if ((object_size_type & OST_DYNAMIC) || TREE_CODE (bytes) == INTEGER_CST)
+  if (size_known_p (bytes, object_size_type))
     return bytes;
 
   return  size_unknown (object_size_type);
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH 10/10] tree-object-size: Handle dynamic offsets
  2021-11-09 19:01 [PATCH 00/10] __builtin_dynamic_object_size Siddhesh Poyarekar
                   ` (8 preceding siblings ...)
  2021-11-09 19:01 ` [PATCH 09/10] tree-object-size: Dynamic sizes for ADDR_EXPR Siddhesh Poyarekar
@ 2021-11-09 19:01 ` Siddhesh Poyarekar
  2021-11-19 15:56 ` [PATCH 00/10] __builtin_dynamic_object_size Jakub Jelinek
                   ` (4 subsequent siblings)
  14 siblings, 0 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-09 19:01 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

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 --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]);
     }
 }
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 00/10] __builtin_dynamic_object_size
  2021-11-09 19:01 [PATCH 00/10] __builtin_dynamic_object_size Siddhesh Poyarekar
                   ` (9 preceding siblings ...)
  2021-11-09 19:01 ` [PATCH 10/10] tree-object-size: Handle dynamic offsets Siddhesh Poyarekar
@ 2021-11-19 15:56 ` Jakub Jelinek
  2021-11-26  5:28 ` [PATCH v3 0/8] __builtin_dynamic_object_size Siddhesh Poyarekar
                   ` (3 subsequent siblings)
  14 siblings, 0 replies; 97+ messages in thread
From: Jakub Jelinek @ 2021-11-19 15:56 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Wed, Nov 10, 2021 at 12:31:26AM +0530, Siddhesh Poyarekar wrote:
> - Instead of bailing out on non-constant sizes with
>   __builtin_object_size, it should be possible to use ranger to
>   get an upper and lower bound on the size expression and use that to
>   implement __builtin_object_size.

I'd prefer not to.  One thing is that VRP heavily relies on UB not happening
in the program, while __bos is typically used to catch UB in those programs.
And, I'm afraid fairly often VRP would result in runtime tests for limits
that aren't useful for security at all.  Say VRP figures out that some
integer isn't negative or doesn't have MSB set etc., suddenly we have range
of [0, INT_MAX] or similar and making that imply __builtin_object_size
INT_MAX rather than ~(size_t) 0 would mean we need to use __*_chk and
compare at runtime, even when it is very unlikely an object would be that
big...
The compiler computes some range, but there is not information on how much
the range actually maps to the actual range the program is using, or when it
is some much larger superset of the actual range (same problem is with
Martin's warnings BTW).  Some unrelated inlined function can perform some
comparison just in case, perhaps some jump threading is done and all of sudden
there is non-VARYING range.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 01/10] tree-object-size: Replace magic numbers with enums
  2021-11-09 19:01 ` [PATCH 01/10] tree-object-size: Replace magic numbers with enums Siddhesh Poyarekar
@ 2021-11-19 16:00   ` Jakub Jelinek
  0 siblings, 0 replies; 97+ messages in thread
From: Jakub Jelinek @ 2021-11-19 16:00 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Wed, Nov 10, 2021 at 12:31:27AM +0530, Siddhesh Poyarekar wrote:
> A simple cleanup to allow inserting dynamic size code more easily.
> 
> gcc/ChangeLog:
> 
> 	* tree-object-size.c: New enum.
> 	(object_sizes, computed, addr_object_size,
> 	compute_builtin_object_size, init_object_sizes,
> 	fini_object_sizes, object_sizes_execute): Replace magic numbers
> 	with enums.

Ok.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 02/10] tree-object-size: Abstract object_sizes array
  2021-11-09 19:01 ` [PATCH 02/10] tree-object-size: Abstract object_sizes array Siddhesh Poyarekar
@ 2021-11-19 16:18   ` Jakub Jelinek
  2021-11-19 16:53     ` Siddhesh Poyarekar
  0 siblings, 1 reply; 97+ messages in thread
From: Jakub Jelinek @ 2021-11-19 16:18 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Wed, Nov 10, 2021 at 12:31:28AM +0530, Siddhesh Poyarekar wrote:
> Put all accesses to object_sizes behind functions so that we can add
> dynamic capability more easily.
> 
> gcc/ChangeLog:
> 
> 	* tree-object-size.c (object_sizes_grow, object_sizes_release,
> 	object_sizes_unknown_p, object_sizes_get, object_size_set_force,
> 	object_sizes_set): New functions.
> 	(addr_object_size, compute_builtin_object_size,
> 	expr_object_size, call_object_size, unknown_object_size,
> 	merge_object_sizes, plus_stmt_object_size,
> 	cond_expr_object_size, collect_object_sizes_for,
> 	check_for_plus_in_loops_1, init_object_sizes,
> 	fini_object_sizes): Adjust.

> @@ -975,8 +994,9 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
>      {
>        if (bitmap_set_bit (osi->visited, varno))
>  	{
> -	  object_sizes[object_size_type][varno]
> -	    = (object_size_type & 2) ? -1 : 0;
> +	  /* Initialize to 0 for maximum size and M1U for minimum size so that
> +	     it gets immediately overridden.  */
	  object_sizes_set_force (osi, varno, unknown (object_size_type ^ 2));

Shouldn't that be			      unknown (object_size_type
						       ^ OST_MINIMUM)
?
Or, if you redo the series, update the first patch so that it doesn't leave
any & 2 or & 1 etc. around and instead uses the enumerators and redo this
patch?

Otherwise LGTM.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 02/10] tree-object-size: Abstract object_sizes array
  2021-11-19 16:18   ` Jakub Jelinek
@ 2021-11-19 16:53     ` Siddhesh Poyarekar
  0 siblings, 0 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-19 16:53 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 11/19/21 21:48, Jakub Jelinek wrote:
> On Wed, Nov 10, 2021 at 12:31:28AM +0530, Siddhesh Poyarekar wrote:
>> Put all accesses to object_sizes behind functions so that we can add
>> dynamic capability more easily.
>>
>> gcc/ChangeLog:
>>
>> 	* tree-object-size.c (object_sizes_grow, object_sizes_release,
>> 	object_sizes_unknown_p, object_sizes_get, object_size_set_force,
>> 	object_sizes_set): New functions.
>> 	(addr_object_size, compute_builtin_object_size,
>> 	expr_object_size, call_object_size, unknown_object_size,
>> 	merge_object_sizes, plus_stmt_object_size,
>> 	cond_expr_object_size, collect_object_sizes_for,
>> 	check_for_plus_in_loops_1, init_object_sizes,
>> 	fini_object_sizes): Adjust.
> 
>> @@ -975,8 +994,9 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
>>       {
>>         if (bitmap_set_bit (osi->visited, varno))
>>   	{
>> -	  object_sizes[object_size_type][varno]
>> -	    = (object_size_type & 2) ? -1 : 0;
>> +	  /* Initialize to 0 for maximum size and M1U for minimum size so that
>> +	     it gets immediately overridden.  */
> 	  object_sizes_set_force (osi, varno, unknown (object_size_type ^ 2));
> 
> Shouldn't that be			      unknown (object_size_type
> 						       ^ OST_MINIMUM)
> ?
> Or, if you redo the series, update the first patch so that it doesn't leave
> any & 2 or & 1 etc. around and instead uses the enumerators and redo this
> patch?

Thanks, I'll update 1/10 and redo this on top of it.

Siddhesh

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 03/10] tree-object-size: Use tree instead of HOST_WIDE_INT
  2021-11-09 19:01 ` [PATCH 03/10] tree-object-size: Use tree instead of HOST_WIDE_INT Siddhesh Poyarekar
@ 2021-11-19 17:06   ` Jakub Jelinek
  2021-11-19 19:01     ` Siddhesh Poyarekar
  0 siblings, 1 reply; 97+ messages in thread
From: Jakub Jelinek @ 2021-11-19 17:06 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Wed, Nov 10, 2021 at 12:31:29AM +0530, Siddhesh Poyarekar wrote:
> 	* tree-object-size.h (compute_builtin_object_size): Return tree
> 	instead of HOST_WIDE_INT.
> 	* builtins.c (fold_builtin_object_size): Adjust.
> 	* gimple-fold.c (gimple_fold_builtin_strncat): Likewise.
> 	* ubsan.c (instrument_object_size): Likewise.
> 	* tree-object-size.c (object_sizes): Change type to vec<tree>.
> 	(initval): New function.
> 	(unknown): Use it.
> 	(size_unknown_p, size_initval, size_unknown): New functions.
> 	(object_sizes_unknown_p): Use it.
> 	(object_sizes_get): Return tree.
> 	(object_sizes_initialize): Rename from object_sizes_set_force
> 	and set VAL parameter type as tree.
> 	(object_sizes_set): Set VAL parameter type as tree and adjust
> 	implementation.
> 	(size_for_offset): New function.
> 	(addr_object_size): Change PSIZE parameter to tree and adjust
> 	implementation.
> 	(alloc_object_size): Return tree.
> 	(compute_builtin_object_size): Return tree in PSIZE.
> 	(expr_object_size, call_object_size, unknown_object_size):
> 	Adjust for object_sizes_set change.
> 	(merge_object_sizes): Set OFFSET type to tree and adjust
> 	implementation.
> 	(plus_stmt_object_size, cond_expr_object_size,
> 	collect_object_sizes_for, check_for_plus_in_loops_1): Adjust for
> 	change of type from HOST_WIDE_INT to tree.
> 	(object_sizes_execute): Adjust for type change to tree.

> --- a/gcc/builtins.c
> +++ b/gcc/builtins.c
> @@ -10226,7 +10226,7 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
>  static tree
>  fold_builtin_object_size (tree ptr, tree ost)
>  {
> -  unsigned HOST_WIDE_INT bytes;
> +  tree bytes;
>    int object_size_type;
>  
>    if (!validate_arg (ptr, POINTER_TYPE)
> @@ -10251,17 +10251,15 @@ fold_builtin_object_size (tree ptr, tree ost)
>    if (TREE_CODE (ptr) == ADDR_EXPR)
>      {
>        compute_builtin_object_size (ptr, object_size_type, &bytes);
> -      if (wi::fits_to_tree_p (bytes, size_type_node))
> -	return build_int_cstu (size_type_node, bytes);
> +      return fold_convert (size_type_node, bytes);
>      }
>    else if (TREE_CODE (ptr) == SSA_NAME)
>      {
>        /* If object size is not known yet, delay folding until
>         later.  Maybe subsequent passes will help determining
>         it.  */
> -      if (compute_builtin_object_size (ptr, object_size_type, &bytes)
> -	  && wi::fits_to_tree_p (bytes, size_type_node))
> -	return build_int_cstu (size_type_node, bytes);
> +      if (compute_builtin_object_size (ptr, object_size_type, &bytes))
> +	return fold_convert (size_type_node, bytes);

Neither of these are equivalent to what it used to do before.
If some target has e.g. pointers wider than size_t, then previously we could
compute bytes that doesn't fit into size_t and would return NULL which
eventually would result in object_sizes_execute or expand_builtin_object_size
resolving it to the unknown value.  But fold_convert will perform modulo
arithmetics on it, so say if size_t is 24-bit and bytes is 0x1000001, it
will fold to (size_t) 1.  I think we should still punt if it doesn't fit.
Or do we ensure that what it returns always has sizetype type?

> @@ -2486,13 +2486,14 @@ gimple_fold_builtin_strncat (gimple_stmt_iterator *gsi)
>    if (cmpsrc < 0)
>      return false;
>  
> -  unsigned HOST_WIDE_INT dstsize;
> +  tree dstsize;
>  
>    bool nowarn = warning_suppressed_p (stmt, OPT_Wstringop_overflow_);
>  
> -  if (!nowarn && compute_builtin_object_size (dst, 1, &dstsize))
> +  if (!nowarn && compute_builtin_object_size (dst, 1, &dstsize)
> +      && tree_fits_uhwi_p (dstsize))
>      {
> -      int cmpdst = compare_tree_int (len, dstsize);
> +      int cmpdst = compare_tree_int (len, tree_to_uhwi (dstsize));

Why jump from tree to UHWI and back?
Check that TREE_CODE (dstsize) == INTEGER_CST and do
tree_int_cst_compare instead?

>  
>        if (cmpdst >= 0)
>  	{
> @@ -2509,7 +2510,7 @@ gimple_fold_builtin_strncat (gimple_stmt_iterator *gsi)
>  				    "destination size")
>  			       : G_("%qD specified bound %E exceeds "
>  				    "destination size %wu"),
> -			       fndecl, len, dstsize);
> +			       fndecl, len, tree_to_uhwi (dstsize));

Similarly, use %E and pass dstsize to it.

> +/* Initial value of object sizes; zero for maximum and SIZE_MAX for minimum
> +   object size.  */
> +
> +static inline unsigned HOST_WIDE_INT
> +initval (int object_size_type)
> +{
> +  return (unsigned HOST_WIDE_INT) -popcount_hwi (object_size_type
> +						 & OST_MINIMUM);

This makes the code quite unreadable and on some arches it will be also
much worse (popcount is a libcall on many, and even if not, it is inline
function using a builtin inside of it).  Can't you use a simple readable
  return (object_size_type & OST_MINUMUM) ? HOST_WIDE_INT_M1U : 0;
and let the compiler optimize as much as it wants?
?

> +/* Bytes at end of the object with SZ from offset OFFSET. */
> +
> +static tree
> +size_for_offset (tree sz, tree offset)
> +{
> +  return size_binop (MINUS_EXPR, size_binop (MAX_EXPR, sz, offset), offset);
> +}
>  
> @@ -314,27 +356,22 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
>  			    SSA_NAME_VERSION (var)))
>  	    sz = object_sizes_get (osi, SSA_NAME_VERSION (var));
>  	  else
> -	    sz = unknown (object_size_type);
> +	    sz = size_unknown (object_size_type);
>  	}
> -      if (sz != unknown (object_size_type))
> +      if (!size_unknown_p (sz, object_size_type))
>  	{
> -	  offset_int mem_offset;
> -	  if (mem_ref_offset (pt_var).is_constant (&mem_offset))
> -	    {
> -	      offset_int dsz = wi::sub (sz, mem_offset);
> -	      if (wi::neg_p (dsz))
> -		sz = 0;
> -	      else if (wi::fits_uhwi_p (dsz))
> -		sz = dsz.to_uhwi ();
> -	      else
> -		sz = unknown (object_size_type);
> -	    }
> +	  tree offset = TREE_OPERAND (pt_var, 1);
> +	  if (TREE_CODE (offset) != INTEGER_CST
> +	      || TREE_CODE (sz) != INTEGER_CST)
> +	    sz = size_unknown (object_size_type);
>  	  else
> -	    sz = unknown (object_size_type);
> +	    sz = size_for_offset (sz, offset);

This doesn't match what the code did and I'm surprised if it works at all.
TREE_OPERAND (pt_var, 1), while it is an INTEGER_CST or POLY_INT_CST,
has in its type encoded the type for aliasing, so the type is some pointer
type.  Performing size_binop etc. on such values can misbehave, the code
assumes that it is sizetype or at least some integral type compatible with
it.Also, mem_ref_offset is signed and offset_int has bigger precision
than pointers on the target such that it can be always signed.  So
e.g. if MEM_REF's second operand is bigger or equal than half of the
address space, in the old code it would appear to be negative, wi::sub would
result in a value bigger than sz.  Not really sure right now if that is
exactly how we want to treat it, would be nice to try some testcase.
In any case, size_for_offset should be called with the offset converted
to sizetype if it does size_binop on it.
> +    reexamine |= merge_object_sizes (osi, var, then_, size_int (0));

size_zero_node ?
>    else
>      expr_object_size (osi, var, then_);
>  
> @@ -952,7 +970,7 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
>      return reexamine;
>  
>    if (TREE_CODE (else_) == SSA_NAME)
> -    reexamine |= merge_object_sizes (osi, var, else_, 0);
> +    reexamine |= merge_object_sizes (osi, var, else_, size_int (0));

Likewise.  Many times.

> -  unsigned HOST_WIDE_INT size;
> -  if (compute_builtin_object_size (base_addr, 0, &size))
> -    sizet = build_int_cst (sizetype, size);
> -  else if (optimize)

I'd prefer not to reindent it to avoid useless churn, just do
  if (compute_builtin_object_size (base_addr, 0, &sizet))
    ;
  else if (optimize)
...

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 03/10] tree-object-size: Use tree instead of HOST_WIDE_INT
  2021-11-19 17:06   ` Jakub Jelinek
@ 2021-11-19 19:01     ` Siddhesh Poyarekar
  2021-11-19 19:16       ` Jakub Jelinek
  2021-11-22 10:11       ` Siddhesh Poyarekar
  0 siblings, 2 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-19 19:01 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 11/19/21 22:36, Jakub Jelinek wrote:
> On Wed, Nov 10, 2021 at 12:31:29AM +0530, Siddhesh Poyarekar wrote:
>> 	* tree-object-size.h (compute_builtin_object_size): Return tree
>> 	instead of HOST_WIDE_INT.
>> 	* builtins.c (fold_builtin_object_size): Adjust.
>> 	* gimple-fold.c (gimple_fold_builtin_strncat): Likewise.
>> 	* ubsan.c (instrument_object_size): Likewise.
>> 	* tree-object-size.c (object_sizes): Change type to vec<tree>.
>> 	(initval): New function.
>> 	(unknown): Use it.
>> 	(size_unknown_p, size_initval, size_unknown): New functions.
>> 	(object_sizes_unknown_p): Use it.
>> 	(object_sizes_get): Return tree.
>> 	(object_sizes_initialize): Rename from object_sizes_set_force
>> 	and set VAL parameter type as tree.
>> 	(object_sizes_set): Set VAL parameter type as tree and adjust
>> 	implementation.
>> 	(size_for_offset): New function.
>> 	(addr_object_size): Change PSIZE parameter to tree and adjust
>> 	implementation.
>> 	(alloc_object_size): Return tree.
>> 	(compute_builtin_object_size): Return tree in PSIZE.
>> 	(expr_object_size, call_object_size, unknown_object_size):
>> 	Adjust for object_sizes_set change.
>> 	(merge_object_sizes): Set OFFSET type to tree and adjust
>> 	implementation.
>> 	(plus_stmt_object_size, cond_expr_object_size,
>> 	collect_object_sizes_for, check_for_plus_in_loops_1): Adjust for
>> 	change of type from HOST_WIDE_INT to tree.
>> 	(object_sizes_execute): Adjust for type change to tree.
> 
>> --- a/gcc/builtins.c
>> +++ b/gcc/builtins.c
>> @@ -10226,7 +10226,7 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
>>   static tree
>>   fold_builtin_object_size (tree ptr, tree ost)
>>   {
>> -  unsigned HOST_WIDE_INT bytes;
>> +  tree bytes;
>>     int object_size_type;
>>   
>>     if (!validate_arg (ptr, POINTER_TYPE)
>> @@ -10251,17 +10251,15 @@ fold_builtin_object_size (tree ptr, tree ost)
>>     if (TREE_CODE (ptr) == ADDR_EXPR)
>>       {
>>         compute_builtin_object_size (ptr, object_size_type, &bytes);
>> -      if (wi::fits_to_tree_p (bytes, size_type_node))
>> -	return build_int_cstu (size_type_node, bytes);
>> +      return fold_convert (size_type_node, bytes);
>>       }
>>     else if (TREE_CODE (ptr) == SSA_NAME)
>>       {
>>         /* If object size is not known yet, delay folding until
>>          later.  Maybe subsequent passes will help determining
>>          it.  */
>> -      if (compute_builtin_object_size (ptr, object_size_type, &bytes)
>> -	  && wi::fits_to_tree_p (bytes, size_type_node))
>> -	return build_int_cstu (size_type_node, bytes);
>> +      if (compute_builtin_object_size (ptr, object_size_type, &bytes))
>> +	return fold_convert (size_type_node, bytes);
> 
> Neither of these are equivalent to what it used to do before.
> If some target has e.g. pointers wider than size_t, then previously we could
> compute bytes that doesn't fit into size_t and would return NULL which
> eventually would result in object_sizes_execute or expand_builtin_object_size
> resolving it to the unknown value.  But fold_convert will perform modulo
> arithmetics on it, so say if size_t is 24-bit and bytes is 0x1000001, it
> will fold to (size_t) 1.  I think we should still punt if it doesn't fit.
> Or do we ensure that what it returns always has sizetype type?

compute_builtin_object_size should always return sizetype.  I probably 
haven't strictly ensured that but I could do that.  Would it be 
necessary to check for fit in sizetype -> size_type_node conversion too? 
  I've been assuming that they'd have the same precision.

> 
>> @@ -2486,13 +2486,14 @@ gimple_fold_builtin_strncat (gimple_stmt_iterator *gsi)
>>     if (cmpsrc < 0)
>>       return false;
>>   
>> -  unsigned HOST_WIDE_INT dstsize;
>> +  tree dstsize;
>>   
>>     bool nowarn = warning_suppressed_p (stmt, OPT_Wstringop_overflow_);
>>   
>> -  if (!nowarn && compute_builtin_object_size (dst, 1, &dstsize))
>> +  if (!nowarn && compute_builtin_object_size (dst, 1, &dstsize)
>> +      && tree_fits_uhwi_p (dstsize))
>>       {
>> -      int cmpdst = compare_tree_int (len, dstsize);
>> +      int cmpdst = compare_tree_int (len, tree_to_uhwi (dstsize));
> 
> Why jump from tree to UHWI and back?
> Check that TREE_CODE (dstsize) == INTEGER_CST and do
> tree_int_cst_compare instead?
> 
>>   
>>         if (cmpdst >= 0)
>>   	{
>> @@ -2509,7 +2510,7 @@ gimple_fold_builtin_strncat (gimple_stmt_iterator *gsi)
>>   				    "destination size")
>>   			       : G_("%qD specified bound %E exceeds "
>>   				    "destination size %wu"),
>> -			       fndecl, len, dstsize);
>> +			       fndecl, len, tree_to_uhwi (dstsize));
> 
> Similarly, use %E and pass dstsize to it.
> 

I noticed it while doing the gimple fold patches; I'll fix it.  I need 
to rebase this bit anyway.

>> +/* Initial value of object sizes; zero for maximum and SIZE_MAX for minimum
>> +   object size.  */
>> +
>> +static inline unsigned HOST_WIDE_INT
>> +initval (int object_size_type)
>> +{
>> +  return (unsigned HOST_WIDE_INT) -popcount_hwi (object_size_type
>> +						 & OST_MINIMUM);
> 
> This makes the code quite unreadable and on some arches it will be also
> much worse (popcount is a libcall on many, and even if not, it is inline
> function using a builtin inside of it).  Can't you use a simple readable
>    return (object_size_type & OST_MINUMUM) ? HOST_WIDE_INT_M1U : 0;
> and let the compiler optimize as much as it wants?
> ?

OK, I'll do that; I was trying too hard there I suppose :)

> 
>> +/* Bytes at end of the object with SZ from offset OFFSET. */
>> +
>> +static tree
>> +size_for_offset (tree sz, tree offset)
>> +{
>> +  return size_binop (MINUS_EXPR, size_binop (MAX_EXPR, sz, offset), offset);
>> +}
>>   
>> @@ -314,27 +356,22 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
>>   			    SSA_NAME_VERSION (var)))
>>   	    sz = object_sizes_get (osi, SSA_NAME_VERSION (var));
>>   	  else
>> -	    sz = unknown (object_size_type);
>> +	    sz = size_unknown (object_size_type);
>>   	}
>> -      if (sz != unknown (object_size_type))
>> +      if (!size_unknown_p (sz, object_size_type))
>>   	{
>> -	  offset_int mem_offset;
>> -	  if (mem_ref_offset (pt_var).is_constant (&mem_offset))
>> -	    {
>> -	      offset_int dsz = wi::sub (sz, mem_offset);
>> -	      if (wi::neg_p (dsz))
>> -		sz = 0;
>> -	      else if (wi::fits_uhwi_p (dsz))
>> -		sz = dsz.to_uhwi ();
>> -	      else
>> -		sz = unknown (object_size_type);
>> -	    }
>> +	  tree offset = TREE_OPERAND (pt_var, 1);
>> +	  if (TREE_CODE (offset) != INTEGER_CST
>> +	      || TREE_CODE (sz) != INTEGER_CST)
>> +	    sz = size_unknown (object_size_type);
>>   	  else
>> -	    sz = unknown (object_size_type);
>> +	    sz = size_for_offset (sz, offset);
> 
> This doesn't match what the code did and I'm surprised if it works at all.
> TREE_OPERAND (pt_var, 1), while it is an INTEGER_CST or POLY_INT_CST,
> has in its type encoded the type for aliasing, so the type is some pointer
> type.  Performing size_binop etc. on such values can misbehave, the code
> assumes that it is sizetype or at least some integral type compatible with
> it.Also, mem_ref_offset is signed and offset_int has bigger precision
> than pointers on the target such that it can be always signed.  So
> e.g. if MEM_REF's second operand is bigger or equal than half of the
> address space, in the old code it would appear to be negative, wi::sub would
> result in a value bigger than sz.  Not really sure right now if that is
> exactly how we want to treat it, would be nice to try some testcase.

Let me try coming up with a test case for it.

> In any case, size_for_offset should be called with the offset converted
> to sizetype if it does size_binop on it.

I'll fix this.

>> +    reexamine |= merge_object_sizes (osi, var, then_, size_int (0));
> 
> size_zero_node ?
>>     else
>>       expr_object_size (osi, var, then_);
>>   
>> @@ -952,7 +970,7 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
>>       return reexamine;
>>   
>>     if (TREE_CODE (else_) == SSA_NAME)
>> -    reexamine |= merge_object_sizes (osi, var, else_, 0);
>> +    reexamine |= merge_object_sizes (osi, var, else_, size_int (0));
> 
> Likewise.  Many times.

OK.

>> -  unsigned HOST_WIDE_INT size;
>> -  if (compute_builtin_object_size (base_addr, 0, &size))
>> -    sizet = build_int_cst (sizetype, size);
>> -  else if (optimize)
> 
> I'd prefer not to reindent it to avoid useless churn, just do
>    if (compute_builtin_object_size (base_addr, 0, &sizet))
>      ;
>    else if (optimize)

OK.

> ...
> 
> 	Jakub
> 

Thanks,
Siddhesh

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 03/10] tree-object-size: Use tree instead of HOST_WIDE_INT
  2021-11-19 19:01     ` Siddhesh Poyarekar
@ 2021-11-19 19:16       ` Jakub Jelinek
  2021-11-22  8:41         ` Richard Biener
  2021-11-22 10:11       ` Siddhesh Poyarekar
  1 sibling, 1 reply; 97+ messages in thread
From: Jakub Jelinek @ 2021-11-19 19:16 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Sat, Nov 20, 2021 at 12:31:19AM +0530, Siddhesh Poyarekar wrote:
> > Neither of these are equivalent to what it used to do before.
> > If some target has e.g. pointers wider than size_t, then previously we could
> > compute bytes that doesn't fit into size_t and would return NULL which
> > eventually would result in object_sizes_execute or expand_builtin_object_size
> > resolving it to the unknown value.  But fold_convert will perform modulo
> > arithmetics on it, so say if size_t is 24-bit and bytes is 0x1000001, it
> > will fold to (size_t) 1.  I think we should still punt if it doesn't fit.
> > Or do we ensure that what it returns always has sizetype type?
> 
> compute_builtin_object_size should always return sizetype.  I probably
> haven't strictly ensured that but I could do that.  Would it be necessary to
> check for fit in sizetype -> size_type_node conversion too?  I've been
> assuming that they'd have the same precision.

sizetype and size_type_node should be indeed always the same precision.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 03/10] tree-object-size: Use tree instead of HOST_WIDE_INT
  2021-11-19 19:16       ` Jakub Jelinek
@ 2021-11-22  8:41         ` Richard Biener
  0 siblings, 0 replies; 97+ messages in thread
From: Richard Biener @ 2021-11-22  8:41 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Siddhesh Poyarekar, GCC Patches

On Fri, Nov 19, 2021 at 8:17 PM Jakub Jelinek via Gcc-patches
<gcc-patches@gcc.gnu.org> wrote:
>
> On Sat, Nov 20, 2021 at 12:31:19AM +0530, Siddhesh Poyarekar wrote:
> > > Neither of these are equivalent to what it used to do before.
> > > If some target has e.g. pointers wider than size_t, then previously we could
> > > compute bytes that doesn't fit into size_t and would return NULL which
> > > eventually would result in object_sizes_execute or expand_builtin_object_size
> > > resolving it to the unknown value.  But fold_convert will perform modulo
> > > arithmetics on it, so say if size_t is 24-bit and bytes is 0x1000001, it
> > > will fold to (size_t) 1.  I think we should still punt if it doesn't fit.
> > > Or do we ensure that what it returns always has sizetype type?
> >
> > compute_builtin_object_size should always return sizetype.  I probably
> > haven't strictly ensured that but I could do that.  Would it be necessary to
> > check for fit in sizetype -> size_type_node conversion too?  I've been
> > assuming that they'd have the same precision.
>
> sizetype and size_type_node should be indeed always the same precision.

I don't think so.  vms.h has

/* Always a 32 bit type.  */
#undef SIZE_TYPE
#define SIZE_TYPE  "unsigned int"

...

#define SIZETYPE (flag_vms_pointer_size == VMS_POINTER_SIZE_NONE ? \
                  "unsigned int" : "long long unsigned int")

interestingly that's the _only_ SIZETYPE override we have, so it does look like
we might want to try removing the 'SIZETYPE' target macro.  I'm not sure the
above does anything good - it looks more like a ptr_mode vs Pmode thing.

Richard.

>         Jakub
>

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 03/10] tree-object-size: Use tree instead of HOST_WIDE_INT
  2021-11-19 19:01     ` Siddhesh Poyarekar
  2021-11-19 19:16       ` Jakub Jelinek
@ 2021-11-22 10:11       ` Siddhesh Poyarekar
  2021-11-22 10:31         ` Jakub Jelinek
  1 sibling, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-22 10:11 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 11/20/21 00:31, Siddhesh Poyarekar wrote:
>> This doesn't match what the code did and I'm surprised if it works at 
>> all.
>> TREE_OPERAND (pt_var, 1), while it is an INTEGER_CST or POLY_INT_CST,
>> has in its type encoded the type for aliasing, so the type is some 
>> pointer
>> type.  Performing size_binop etc. on such values can misbehave, the code
>> assumes that it is sizetype or at least some integral type compatible 
>> with
>> it.Also, mem_ref_offset is signed and offset_int has bigger precision
>> than pointers on the target such that it can be always signed.  So
>> e.g. if MEM_REF's second operand is bigger or equal than half of the
>> address space, in the old code it would appear to be negative, wi::sub 
>> would
>> result in a value bigger than sz.  Not really sure right now if that is
>> exactly how we want to treat it, would be nice to try some testcase.
> 
> Let me try coming up with a test case for it.
> 

So I played around a bit with this.  Basically:

char buf[8];

__SIZE_TYPE__ test (void)
{
   char *p = &buf[0x90000004];
   return __builtin_object_size (p + 2, 0);
}

when built with -m32 returns 0x70000002 but on 64-bit, returns 0 as 
expected.  of course, with subscript as 0x9000000000000004, 64-bit gives 
  0x7000000000000002 as the result.

With the tree conversion, this is at least partly taken care of since 
offset larger than size, to the extent that it fits into sizetype, 
returns a size of zero.  So in the above example, the size returned is 
zero in both -m32 as well as -m64.  Likewise for negative offset, i.e. 
&buf[-4]; the old code returns 10 while with trees it returns 0, which 
seems correct to me since it is an underflow.

It's only partly taken care of because, e.g.

   char *p = &buf[0x100000004];

ends up truncating the offset, returning object size of 2.  This however 
is an unrelated problem; it's the folding of offsets that is responsible 
for this since it ends up truncating the offset to shwi bounds.  Perhaps 
there's an opportunity in get_addr_base_and_unit_offset to warn of 
overflow if offset goes above pointer precision before truncating it.

So for this patch, may I simply ensure that offset is converted to 
sizetype and keep everything else the same?  it appears to demonstrate 
better behaviour than the older code.  I'll also add these tests.

Thanks,
Siddhesh

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 03/10] tree-object-size: Use tree instead of HOST_WIDE_INT
  2021-11-22 10:11       ` Siddhesh Poyarekar
@ 2021-11-22 10:31         ` Jakub Jelinek
  2021-11-22 12:00           ` Siddhesh Poyarekar
  0 siblings, 1 reply; 97+ messages in thread
From: Jakub Jelinek @ 2021-11-22 10:31 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Mon, Nov 22, 2021 at 03:41:57PM +0530, Siddhesh Poyarekar wrote:
> So I played around a bit with this.  Basically:
> 
> char buf[8];
> 
> __SIZE_TYPE__ test (void)
> {
>   char *p = &buf[0x90000004];
>   return __builtin_object_size (p + 2, 0);
> }
> 
> when built with -m32 returns 0x70000002 but on 64-bit, returns 0 as
> expected.  of course, with subscript as 0x9000000000000004, 64-bit gives
> 0x7000000000000002 as the result.

That is one case, I think when the base is a ADDR_EXPR VAR_DECL, offsets
larger than half of the address space will be UB, though of course the
question is what __bos should return for that because it wants to prevent
UB exploiting.

Another case is where the base is some pointer, something like
  &MEM_REF [ptr, -16].a
etc., where the MEM_REF offset negative makes a lot of sense,
there could be
  ptr = &var + 32;
earlier etc.
That gives us to the general question on what to do with POINTER_PLUS_EXPR
if the offset is constant but "negative" (as it is sizetype, it is never
negative, but usually we treat it or should treat it as signed in the end).
If the offset is "negative", for e.g. __bos 0/1 one option is to use a
conservative maximum, i.e. subtract the offset from the value (make it even
larger, of course make sure it doesn't wrap around).  Another one would be
to consider also __bos 2 in that case, if the negation of the offset is
bigger than __bos 2, then the pointer points to before the start of the
object and we could return 0.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 03/10] tree-object-size: Use tree instead of HOST_WIDE_INT
  2021-11-22 10:31         ` Jakub Jelinek
@ 2021-11-22 12:00           ` Siddhesh Poyarekar
  2021-11-22 12:31             ` Siddhesh Poyarekar
  0 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-22 12:00 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 11/22/21 16:01, Jakub Jelinek wrote:
> On Mon, Nov 22, 2021 at 03:41:57PM +0530, Siddhesh Poyarekar wrote:
>> So I played around a bit with this.  Basically:
>>
>> char buf[8];
>>
>> __SIZE_TYPE__ test (void)
>> {
>>    char *p = &buf[0x90000004];
>>    return __builtin_object_size (p + 2, 0);
>> }
>>
>> when built with -m32 returns 0x70000002 but on 64-bit, returns 0 as
>> expected.  of course, with subscript as 0x9000000000000004, 64-bit gives
>> 0x7000000000000002 as the result.
> 
> That is one case, I think when the base is a ADDR_EXPR VAR_DECL, offsets
> larger than half of the address space will be UB, though of course the
> question is what __bos should return for that because it wants to prevent
> UB exploiting.
> 
> Another case is where the base is some pointer, something like
>    &MEM_REF [ptr, -16].a
> etc., where the MEM_REF offset negative makes a lot of sense,
> there could be
>    ptr = &var + 32;
> earlier etc.
> That gives us to the general question on what to do with POINTER_PLUS_EXPR
> if the offset is constant but "negative" (as it is sizetype, it is never
> negative, but usually we treat it or should treat it as signed in the end).
> If the offset is "negative", for e.g. __bos 0/1 one option is to use a
> conservative maximum, i.e. subtract the offset from the value (make it even
> larger, of course make sure it doesn't wrap around).  Another one would be
> to consider also __bos 2 in that case, if the negation of the offset is
> bigger than __bos 2, then the pointer points to before the start of the
> object and we could return 0.

So I've got patch 10/10, which handles dynamic (and consequently 
negative) offsets.  It basically computes a "whole size", which then 
gives the extent to which a negative offset is valid, making the 
estimates a bit more precise.  I didn't do it for static object sizes 
because I didn't have time then, but I could add a patch 11/10 if the 
idea sounds OK to you.

Siddhesh

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 03/10] tree-object-size: Use tree instead of HOST_WIDE_INT
  2021-11-22 12:00           ` Siddhesh Poyarekar
@ 2021-11-22 12:31             ` Siddhesh Poyarekar
  2021-11-22 12:32               ` Jakub Jelinek
  0 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-22 12:31 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 11/22/21 17:30, Siddhesh Poyarekar wrote:
> So I've got patch 10/10, which handles dynamic (and consequently 
> negative) offsets.  It basically computes a "whole size", which then 
> gives the extent to which a negative offset is valid, making the 
> estimates a bit more precise.  I didn't do it for static object sizes 
> because I didn't have time then, but I could add a patch 11/10 if the 
> idea sounds OK to you.

... or alternatively, I could bring the whole size idea into this tree 
conversion patch so that it handles all kinds of offsets.  That might 
even eliminate patch 10/10.  What would you prefer?

Siddhesh

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 03/10] tree-object-size: Use tree instead of HOST_WIDE_INT
  2021-11-22 12:31             ` Siddhesh Poyarekar
@ 2021-11-22 12:32               ` Jakub Jelinek
  2021-11-23 11:58                 ` Jakub Jelinek
  0 siblings, 1 reply; 97+ messages in thread
From: Jakub Jelinek @ 2021-11-22 12:32 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Mon, Nov 22, 2021 at 06:01:08PM +0530, Siddhesh Poyarekar wrote:
> On 11/22/21 17:30, Siddhesh Poyarekar wrote:
> > So I've got patch 10/10, which handles dynamic (and consequently
> > negative) offsets.  It basically computes a "whole size", which then
> > gives the extent to which a negative offset is valid, making the
> > estimates a bit more precise.  I didn't do it for static object sizes
> > because I didn't have time then, but I could add a patch 11/10 if the
> > idea sounds OK to you.
> 
> ... or alternatively, I could bring the whole size idea into this tree
> conversion patch so that it handles all kinds of offsets.  That might even
> eliminate patch 10/10.  What would you prefer?

Into this patch.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 03/10] tree-object-size: Use tree instead of HOST_WIDE_INT
  2021-11-22 12:32               ` Jakub Jelinek
@ 2021-11-23 11:58                 ` Jakub Jelinek
  2021-11-23 13:33                   ` Siddhesh Poyarekar
  0 siblings, 1 reply; 97+ messages in thread
From: Jakub Jelinek @ 2021-11-23 11:58 UTC (permalink / raw)
  To: Siddhesh Poyarekar, gcc-patches

On Mon, Nov 22, 2021 at 01:32:22PM +0100, Jakub Jelinek via Gcc-patches wrote:
> On Mon, Nov 22, 2021 at 06:01:08PM +0530, Siddhesh Poyarekar wrote:
> > On 11/22/21 17:30, Siddhesh Poyarekar wrote:
> > > So I've got patch 10/10, which handles dynamic (and consequently
> > > negative) offsets.  It basically computes a "whole size", which then
> > > gives the extent to which a negative offset is valid, making the
> > > estimates a bit more precise.  I didn't do it for static object sizes
> > > because I didn't have time then, but I could add a patch 11/10 if the
> > > idea sounds OK to you.
> > 
> > ... or alternatively, I could bring the whole size idea into this tree
> > conversion patch so that it handles all kinds of offsets.  That might even
> > eliminate patch 10/10.  What would you prefer?
> 
> Into this patch.

BTW, seems the current behavior is to punt on those "negative" values,
we trigger
  if (offset >= offset_limit)
case for it and return unknown.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 04/10] tree-object-size: Single pass dependency loop resolution
  2021-11-09 19:01 ` [PATCH 04/10] tree-object-size: Single pass dependency loop resolution Siddhesh Poyarekar
@ 2021-11-23 12:07   ` Jakub Jelinek
  2021-11-23 13:44     ` Siddhesh Poyarekar
  0 siblings, 1 reply; 97+ messages in thread
From: Jakub Jelinek @ 2021-11-23 12:07 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Wed, Nov 10, 2021 at 12:31:30AM +0530, Siddhesh Poyarekar wrote:
> Use SSA names as placeholders self-referencing variables to generate
> expressions for object sizes and then reduce those size expressions
> to constants instead of repeatedly walking through statements.
> 
> This change also makes sure that object sizes for an SSA name are
> updated at most twice, once if there is a dependency loop and then the
> final time upon computation of object size.  Iteration to deduce the
> final size is now done on the size expressions instead of walking
> through the object references.
> 
> Added test to include a case where __builtin_object_size incorrectly
> returned the minimum object size as zero.

This feels way too risky to me.  I think making some code do something
different between (x & OST_DYNAMIC) == 0 and == 1 is just fine,
it doesn't have to share everything.  After all, for __bdos we actually
emit code to compute it at runtime, while for __bos we don't.
So I'd keep e.g. .pass = 0, .pass = 1 and .pass = 2 (in a loop) in
compute_builtin_object_size for non-OST_DYNAMIC and only use your new
stuff for __bdos.
E.g. creating new SSA_NAMEs for __bos doesn't look like a good idea to me.
The GCC __bos testsuite is not big enough that it covers everything and
even in it we sometimes reexamine 2, 3 or 4 times.

> gcc/ChangeLog:
> 
> 	* tree-object-size.c (struct object_size_info): Remove pass,
> 	changed, depths, stack and tos.  Add tempsize_objs.
> 	(OST_TREE_CODE): New macro.
> 	(expr_object_size, merge_object_sizes, plus_stmt_object_size,
> 	cond_expr_object_size): Return tree and don't pass pointer tree.
> 	(object_sizes_set): Return void.  Adjust implementation to hold
> 	placeholder SSA names and their values in different slots.
> 	(addr_object_size): Adjust for single pass.
> 	(reducing_size, estimate_size, resolve_dependency_loops): New
> 	functions.
> 	(compute_builtin_object_size): Call them.
> 	(make_tempsize): New function.
> 	(collect_object_sizes_for): Use it.  Update object_sizes at most
> 	twice.
> 	(check_for_plus_in_loops, check_for_plus_in_loops_1): Remove
> 	functions.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* gcc.dg/builtin-object-size-1.c (test6): New test for
> 	passthrough.
> 	* gcc.dg/builtin-object-size-2.c: Likewise.
> 	* gcc.dg/builtin-object-size-3.c: Likewise.
> 	* gcc.dg/builtin-object-size-4.c: Likewise.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 05/10] __builtin_dynamic_object_size: Recognize builtin
  2021-11-09 19:01 ` [PATCH 05/10] __builtin_dynamic_object_size: Recognize builtin Siddhesh Poyarekar
@ 2021-11-23 12:41   ` Jakub Jelinek
  2021-11-23 13:53     ` Siddhesh Poyarekar
  0 siblings, 1 reply; 97+ messages in thread
From: Jakub Jelinek @ 2021-11-23 12:41 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Wed, Nov 10, 2021 at 12:31:31AM +0530, Siddhesh Poyarekar wrote:
> Recognize the __builtin_dynamic_object_size builtin and add paths in the
> object size path to deal with it, but treat it like
> __builtin_object_size for now.  Also add tests to provide the same
> testing coverage for the new builtin name.
> 
> gcc/ChangeLog:
> 
> 	* builtins.def (BUILT_IN_DYNAMIC_OBJECT_SIZE): New builtin.
> 	* tree-object-size.h (compute_builtin_object_size): Add new
> 	argument dynamic.
> 	* builtins.c (expand_builtin, fold_builtin_2): Handle it.
> 	(fold_builtin_object_size): Handle new builtin and adjust for
> 	change to compute_builtin_object_size.
> 	* tree-object-size.c: Include builtins.h.
> 	(OST_DYNAMIC): New enum value.
> 	(compute_builtin_object_size): Add new argument dynamic.
> 	(addr_object_size): Adjust.
> 	(early_object_sizes_execute_one,
> 	dynamic_object_sizes_execute_one): New functions.
> 	(object_sizes_execute): Rename insert_min_max_p argument to
> 	early. Handle BUILT_IN_DYNAMIC_OBJECT_SIZE and call the new

Two spaces after . instead of just one.

> --- a/gcc/builtins.def
> +++ b/gcc/builtins.def
> @@ -972,6 +972,7 @@ DEF_BUILTIN_STUB (BUILT_IN_STRNCMP_EQ, "__builtin_strncmp_eq")
>  
>  /* Object size checking builtins.  */
>  DEF_GCC_BUILTIN	       (BUILT_IN_OBJECT_SIZE, "object_size", BT_FN_SIZE_CONST_PTR_INT, ATTR_CONST_NOTHROW_LEAF_LIST)
> +DEF_GCC_BUILTIN	       (BUILT_IN_DYNAMIC_OBJECT_SIZE, "dynamic_object_size", BT_FN_SIZE_CONST_PTR_INT, ATTR_NOTHROW_LEAF_LIST)

Are you sure about the omission of CONST_ in there?
If I do:
  size_t a = __builtin_dynamic_object_size (x, 0);
  size_t b = __builtin_dynamic_object_size (x, 0);
I'd expect the compiler to perform it just once.  While it might actually do
it eventually after objsz2 pass lowers it, with the above it won't really do
it.  Perhaps const attribute isn't really safe, the function might need to
read some memory in order to compute the return value, but certainly it will
not store to any memory, so perhaps
ATTR_PURE_NOTHROW_LEAF_LIST ?

> +#define DYNAMIC_OBJECT_SIZE

Why this extra macro?

> +#define __builtin_object_size __builtin_dynamic_object_size

>  extern char ax[];
> +#ifndef DYNAMIC_OBJECT_SIZE

You can #ifndef __builtin_object_size
instead...

> @@ -371,7 +373,8 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
>  	  || TREE_CODE (TREE_OPERAND (pt_var, 0)) != SSA_NAME)
>  	{
>  	  compute_builtin_object_size (TREE_OPERAND (pt_var, 0),
> -				       object_size_type & ~OST_SUBOBJECT, &sz);
> +				       object_size_type & OST_MINIMUM, &sz,
> +				       object_size_type & OST_DYNAMIC);
>  	}
>        else
>  	{
> @@ -835,9 +838,10 @@ resolve_dependency_loops (struct object_size_info *osi)
>  
>  bool
>  compute_builtin_object_size (tree ptr, int object_size_type,
> -			     tree *psize)
> +			     tree *psize, bool dynamic)
>  {
>    gcc_assert (object_size_type >= 0 && object_size_type < OST_END);
> +  object_size_type |= dynamic ? OST_DYNAMIC : 0;

What's the advantage of another argument and then merging it with
object_size_type over just passing object_size_type which will have
all the bits in?

> +static void
> +early_object_sizes_execute_one (gimple_stmt_iterator *i, gimple *call)
> +{
> +  tree ost = gimple_call_arg (call, 1);
> +  tree lhs = gimple_call_lhs (call);
> +  gcc_assert (lhs != NULL_TREE);
> +
> +  if (tree_fits_uhwi_p (ost))
> +    {
> +      unsigned HOST_WIDE_INT object_size_type = tree_to_uhwi (ost);
> +      tree ptr = gimple_call_arg (call, 0);
> +      if ((object_size_type == 1 || object_size_type == 3)
> +	  && (TREE_CODE (ptr) == ADDR_EXPR || TREE_CODE (ptr) == SSA_NAME))

I think it would be better to have early exits there to avoid
indenting most of the function too much, because the function doesn't
do anything otherwise.  So:
  if (!tree_fits_uhwi_p (ost))
    return;

  unsigned HOST_WIDE_INT object_size_type = tree_to_uhwi (ost);
  tree ptr = gimple_call_arg (call, 0);
  if (object_size_type != 1 && object_size_type != 3)
    return;
  if (TREE_CODE (ptr) != ADDR_EXPR && TREE_CODE (ptr) != SSA_NAME)
    return;

  tree type = ...

> +	{
> +	  tree type = TREE_TYPE (lhs);
> +	  tree bytes;
> +	  if (compute_builtin_object_size (ptr, object_size_type, &bytes))
> +	    {
> +	      tree tem = make_ssa_name (type);
> +	      gimple_call_set_lhs (call, tem);
> +	      enum tree_code code
> +		= object_size_type & OST_MINIMUM ? MAX_EXPR : MIN_EXPR;
> +	      tree cst = fold_convert (type, bytes);
> +	      gimple *g = gimple_build_assign (lhs, code, tem, cst);
> +	      gsi_insert_after (i, g, GSI_NEW_STMT);
> +	      update_stmt (call);
> +	    }
> +	}
> +    }

> +/* Attempt to fold one __builtin_dynamic_object_size call in CALL into an
> +   expression and insert it at I.  Return true if it succeeds.  */
> +
> +static bool
> +dynamic_object_sizes_execute_one (gimple_stmt_iterator *i, gimple *call)
> +{
> +  unsigned numargs = gimple_call_num_args (call);
> +  tree *args = XALLOCAVEC (tree, numargs);
> +  args[0] = gimple_call_arg (call, 0);
> +  args[1] = gimple_call_arg (call, 1);

Why it would have numargs different from 2?  It is a builtin, and we reject
((__SIZE_TYPE__ (*) (const void *, int, int)) &__builtin_object_size) (x, 0, 3)
etc. with error that the builtin must be directly called.
And after all, you rely on it having at least 2 arguments anyway.
So, just tree args[2]; instead of XALLOCAVEC and if you want,
gcc_assert (numargs == 2);
?

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 03/10] tree-object-size: Use tree instead of HOST_WIDE_INT
  2021-11-23 11:58                 ` Jakub Jelinek
@ 2021-11-23 13:33                   ` Siddhesh Poyarekar
  0 siblings, 0 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-23 13:33 UTC (permalink / raw)
  To: Jakub Jelinek, gcc-patches

On 11/23/21 17:28, Jakub Jelinek wrote:
> On Mon, Nov 22, 2021 at 01:32:22PM +0100, Jakub Jelinek via Gcc-patches wrote:
>> On Mon, Nov 22, 2021 at 06:01:08PM +0530, Siddhesh Poyarekar wrote:
>>> On 11/22/21 17:30, Siddhesh Poyarekar wrote:
>>>> So I've got patch 10/10, which handles dynamic (and consequently
>>>> negative) offsets.  It basically computes a "whole size", which then
>>>> gives the extent to which a negative offset is valid, making the
>>>> estimates a bit more precise.  I didn't do it for static object sizes
>>>> because I didn't have time then, but I could add a patch 11/10 if the
>>>> idea sounds OK to you.
>>>
>>> ... or alternatively, I could bring the whole size idea into this tree
>>> conversion patch so that it handles all kinds of offsets.  That might even
>>> eliminate patch 10/10.  What would you prefer?
>>
>> Into this patch.
> 
> BTW, seems the current behavior is to punt on those "negative" values,
> we trigger
>    if (offset >= offset_limit)
> case for it and return unknown.

The current behaviour is actually inconsistent; for SSA names it punts 
for sizes greater than offset limit and for addr_expr it ends up with 
larger sizes.

Siddhesh

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 04/10] tree-object-size: Single pass dependency loop resolution
  2021-11-23 12:07   ` Jakub Jelinek
@ 2021-11-23 13:44     ` Siddhesh Poyarekar
  2021-11-23 14:22       ` Jakub Jelinek
  0 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-23 13:44 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 11/23/21 17:37, Jakub Jelinek wrote:
> On Wed, Nov 10, 2021 at 12:31:30AM +0530, Siddhesh Poyarekar wrote:
>> Use SSA names as placeholders self-referencing variables to generate
>> expressions for object sizes and then reduce those size expressions
>> to constants instead of repeatedly walking through statements.
>>
>> This change also makes sure that object sizes for an SSA name are
>> updated at most twice, once if there is a dependency loop and then the
>> final time upon computation of object size.  Iteration to deduce the
>> final size is now done on the size expressions instead of walking
>> through the object references.
>>
>> Added test to include a case where __builtin_object_size incorrectly
>> returned the minimum object size as zero.
> 
> This feels way too risky to me.  I think making some code do something
> different between (x & OST_DYNAMIC) == 0 and == 1 is just fine,
> it doesn't have to share everything.  After all, for __bdos we actually
> emit code to compute it at runtime, while for __bos we don't.
> So I'd keep e.g. .pass = 0, .pass = 1 and .pass = 2 (in a loop) in
> compute_builtin_object_size for non-OST_DYNAMIC and only use your new
> stuff for __bdos.
> E.g. creating new SSA_NAMEs for __bos doesn't look like a good idea to me.
> The GCC __bos testsuite is not big enough that it covers everything and
> even in it we sometimes reexamine 2, 3 or 4 times.

OK, so addr_object_size does not participate in dependency loops, so I 
can keep its changes intact and simply add a INTEGER_CST check at the 
end to return unknown if the size expression is not constant; we kinda 
do that for object offsets anyway.

I could then make a new entry point collect_object_sizes_for (renaming 
the current one to collect_static_object_sizes_for and a new one 
collect_dynamic_object_sizes_for, routing based on object_size_type & 
OST_DYNAMIC) which sends the object size collection down different 
paths.  I can reuse the object size vectors and just have different data 
in them.

Would that be reasonable?

Thanks,
Siddhesh

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 05/10] __builtin_dynamic_object_size: Recognize builtin
  2021-11-23 12:41   ` Jakub Jelinek
@ 2021-11-23 13:53     ` Siddhesh Poyarekar
  2021-11-23 14:00       ` Jakub Jelinek
  0 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-23 13:53 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 11/23/21 18:11, Jakub Jelinek wrote:
> On Wed, Nov 10, 2021 at 12:31:31AM +0530, Siddhesh Poyarekar wrote:
>> Recognize the __builtin_dynamic_object_size builtin and add paths in the
>> object size path to deal with it, but treat it like
>> __builtin_object_size for now.  Also add tests to provide the same
>> testing coverage for the new builtin name.
>>
>> gcc/ChangeLog:
>>
>> 	* builtins.def (BUILT_IN_DYNAMIC_OBJECT_SIZE): New builtin.
>> 	* tree-object-size.h (compute_builtin_object_size): Add new
>> 	argument dynamic.
>> 	* builtins.c (expand_builtin, fold_builtin_2): Handle it.
>> 	(fold_builtin_object_size): Handle new builtin and adjust for
>> 	change to compute_builtin_object_size.
>> 	* tree-object-size.c: Include builtins.h.
>> 	(OST_DYNAMIC): New enum value.
>> 	(compute_builtin_object_size): Add new argument dynamic.
>> 	(addr_object_size): Adjust.
>> 	(early_object_sizes_execute_one,
>> 	dynamic_object_sizes_execute_one): New functions.
>> 	(object_sizes_execute): Rename insert_min_max_p argument to
>> 	early. Handle BUILT_IN_DYNAMIC_OBJECT_SIZE and call the new
> 
> Two spaces after . instead of just one.
> 
>> --- a/gcc/builtins.def
>> +++ b/gcc/builtins.def
>> @@ -972,6 +972,7 @@ DEF_BUILTIN_STUB (BUILT_IN_STRNCMP_EQ, "__builtin_strncmp_eq")
>>   
>>   /* Object size checking builtins.  */
>>   DEF_GCC_BUILTIN	       (BUILT_IN_OBJECT_SIZE, "object_size", BT_FN_SIZE_CONST_PTR_INT, ATTR_CONST_NOTHROW_LEAF_LIST)
>> +DEF_GCC_BUILTIN	       (BUILT_IN_DYNAMIC_OBJECT_SIZE, "dynamic_object_size", BT_FN_SIZE_CONST_PTR_INT, ATTR_NOTHROW_LEAF_LIST)
> 
> Are you sure about the omission of CONST_ in there?
> If I do:
>    size_t a = __builtin_dynamic_object_size (x, 0);
>    size_t b = __builtin_dynamic_object_size (x, 0);
> I'd expect the compiler to perform it just once.  While it might actually do
> it eventually after objsz2 pass lowers it, with the above it won't really do
> it.  Perhaps const attribute isn't really safe, the function might need to
> read some memory in order to compute the return value, but certainly it will
> not store to any memory, so perhaps
> ATTR_PURE_NOTHROW_LEAF_LIST ?

Thanks, I'll fix this.

> 
>> +#define DYNAMIC_OBJECT_SIZE
> 
> Why this extra macro?
> 
>> +#define __builtin_object_size __builtin_dynamic_object_size
> 
>>   extern char ax[];
>> +#ifndef DYNAMIC_OBJECT_SIZE
> 
> You can #ifndef __builtin_object_size
> instead...

I'll fix this too.

> 
>> @@ -371,7 +373,8 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
>>   	  || TREE_CODE (TREE_OPERAND (pt_var, 0)) != SSA_NAME)
>>   	{
>>   	  compute_builtin_object_size (TREE_OPERAND (pt_var, 0),
>> -				       object_size_type & ~OST_SUBOBJECT, &sz);
>> +				       object_size_type & OST_MINIMUM, &sz,
>> +				       object_size_type & OST_DYNAMIC);
>>   	}
>>         else
>>   	{
>> @@ -835,9 +838,10 @@ resolve_dependency_loops (struct object_size_info *osi)
>>   
>>   bool
>>   compute_builtin_object_size (tree ptr, int object_size_type,
>> -			     tree *psize)
>> +			     tree *psize, bool dynamic)
>>   {
>>     gcc_assert (object_size_type >= 0 && object_size_type < OST_END);
>> +  object_size_type |= dynamic ? OST_DYNAMIC : 0;
> 
> What's the advantage of another argument and then merging it with
> object_size_type over just passing object_size_type which will have
> all the bits in?

I kept the size bits as an internal detail, I can define them in 
tree-object-size.h and hae builtins.c (and others) use them.

>> +static void
>> +early_object_sizes_execute_one (gimple_stmt_iterator *i, gimple *call)
>> +{
>> +  tree ost = gimple_call_arg (call, 1);
>> +  tree lhs = gimple_call_lhs (call);
>> +  gcc_assert (lhs != NULL_TREE);
>> +
>> +  if (tree_fits_uhwi_p (ost))
>> +    {
>> +      unsigned HOST_WIDE_INT object_size_type = tree_to_uhwi (ost);
>> +      tree ptr = gimple_call_arg (call, 0);
>> +      if ((object_size_type == 1 || object_size_type == 3)
>> +	  && (TREE_CODE (ptr) == ADDR_EXPR || TREE_CODE (ptr) == SSA_NAME))
> 
> I think it would be better to have early exits there to avoid
> indenting most of the function too much, because the function doesn't
> do anything otherwise.  So:
>    if (!tree_fits_uhwi_p (ost))
>      return;

OK, I'll fix this.

> 
>    unsigned HOST_WIDE_INT object_size_type = tree_to_uhwi (ost);
>    tree ptr = gimple_call_arg (call, 0);
>    if (object_size_type != 1 && object_size_type != 3)
>      return;
>    if (TREE_CODE (ptr) != ADDR_EXPR && TREE_CODE (ptr) != SSA_NAME)
>      return;
> 
>    tree type = ...
> 
>> +	{
>> +	  tree type = TREE_TYPE (lhs);
>> +	  tree bytes;
>> +	  if (compute_builtin_object_size (ptr, object_size_type, &bytes))
>> +	    {
>> +	      tree tem = make_ssa_name (type);
>> +	      gimple_call_set_lhs (call, tem);
>> +	      enum tree_code code
>> +		= object_size_type & OST_MINIMUM ? MAX_EXPR : MIN_EXPR;
>> +	      tree cst = fold_convert (type, bytes);
>> +	      gimple *g = gimple_build_assign (lhs, code, tem, cst);
>> +	      gsi_insert_after (i, g, GSI_NEW_STMT);
>> +	      update_stmt (call);
>> +	    }
>> +	}
>> +    }
> 
>> +/* Attempt to fold one __builtin_dynamic_object_size call in CALL into an
>> +   expression and insert it at I.  Return true if it succeeds.  */
>> +
>> +static bool
>> +dynamic_object_sizes_execute_one (gimple_stmt_iterator *i, gimple *call)
>> +{
>> +  unsigned numargs = gimple_call_num_args (call);
>> +  tree *args = XALLOCAVEC (tree, numargs);
>> +  args[0] = gimple_call_arg (call, 0);
>> +  args[1] = gimple_call_arg (call, 1);
> 
> Why it would have numargs different from 2?  It is a builtin, and we reject
> ((__SIZE_TYPE__ (*) (const void *, int, int)) &__builtin_object_size) (x, 0, 3)
> etc. with error that the builtin must be directly called.
> And after all, you rely on it having at least 2 arguments anyway.
> So, just tree args[2]; instead of XALLOCAVEC and if you want,
> gcc_assert (numargs == 2);
> ?

Thanks, I'll fix this too.

Siddhesh

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 05/10] __builtin_dynamic_object_size: Recognize builtin
  2021-11-23 13:53     ` Siddhesh Poyarekar
@ 2021-11-23 14:00       ` Jakub Jelinek
  0 siblings, 0 replies; 97+ messages in thread
From: Jakub Jelinek @ 2021-11-23 14:00 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Tue, Nov 23, 2021 at 07:23:01PM +0530, Siddhesh Poyarekar wrote:
> > What's the advantage of another argument and then merging it with
> > object_size_type over just passing object_size_type which will have
> > all the bits in?
> 
> I kept the size bits as an internal detail, I can define them in
> tree-object-size.h and hae builtins.c (and others) use them.

Good idea.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 04/10] tree-object-size: Single pass dependency loop resolution
  2021-11-23 13:44     ` Siddhesh Poyarekar
@ 2021-11-23 14:22       ` Jakub Jelinek
  0 siblings, 0 replies; 97+ messages in thread
From: Jakub Jelinek @ 2021-11-23 14:22 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Tue, Nov 23, 2021 at 07:14:04PM +0530, Siddhesh Poyarekar wrote:
> > This feels way too risky to me.  I think making some code do something
> > different between (x & OST_DYNAMIC) == 0 and == 1 is just fine,
> > it doesn't have to share everything.  After all, for __bdos we actually
> > emit code to compute it at runtime, while for __bos we don't.
> > So I'd keep e.g. .pass = 0, .pass = 1 and .pass = 2 (in a loop) in
> > compute_builtin_object_size for non-OST_DYNAMIC and only use your new
> > stuff for __bdos.
> > E.g. creating new SSA_NAMEs for __bos doesn't look like a good idea to me.
> > The GCC __bos testsuite is not big enough that it covers everything and
> > even in it we sometimes reexamine 2, 3 or 4 times.
> 
> OK, so addr_object_size does not participate in dependency loops, so I can
> keep its changes intact and simply add a INTEGER_CST check at the end to
> return unknown if the size expression is not constant; we kinda do that for
> object offsets anyway.
> 
> I could then make a new entry point collect_object_sizes_for (renaming the
> current one to collect_static_object_sizes_for and a new one
> collect_dynamic_object_sizes_for, routing based on object_size_type &
> OST_DYNAMIC) which sends the object size collection down different paths.  I
> can reuse the object size vectors and just have different data in them.

I thought you'd in compute_builtin_object_size do something like:
      osi.depths = NULL;
      osi.stack = NULL;
      osi.tos = NULL;

      if (object_size_type & OST_DYNAMIC)
	{
	  osi.tempsize_objs.create (0);
	  // All the new stuff in there
	  goto done;
	}

      /* First pass: walk UD chains, compute object sizes that
	 can be computed.  osi.reexamine bitmap at the end will
	 contain what variables were found in dependency cycles
	 and therefore need to be reexamined.  */
      osi.pass = 0;
      osi.changed = false;
      // Current code

     done:
      BITMAP_FREE (osi.reexamine);
      BITMAP_FREE (osi.visited);
    }
or so.  The new routine you've added used just for OST_DYNAMIC, keep the
old ones like check_for_plus_in_loops{,_1}, and for the likes of
cond_expr_object_size tweak it so that it can be used in both modes.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 06/10] tree-object-size: Support dynamic sizes in conditions
  2021-11-09 19:01 ` [PATCH 06/10] tree-object-size: Support dynamic sizes in conditions Siddhesh Poyarekar
@ 2021-11-23 15:12   ` Jakub Jelinek
  2021-11-23 15:36     ` Siddhesh Poyarekar
  0 siblings, 1 reply; 97+ messages in thread
From: Jakub Jelinek @ 2021-11-23 15:12 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Wed, Nov 10, 2021 at 12:31:32AM +0530, Siddhesh Poyarekar wrote:
> 	(object_sizes_execute): Don't insert min/max for dynamic sizes.

I'm worried about this.
I'd say what we might want to do is in the early pass for __bdos
compute actually __bos (i.e. the static one) and add MIN_EXPR/MAX_EXPR
for the result of the __bdos call from the second pass with the
statically computed value.

The reason for the MIN_EXPR/MAX_EXPR stuff is that GIMPLE optimizations
can remove exact ADDR_EXPRs with detailed COMPONENT_REF etc. access paths
in it, so during the late objsz2 pass the subobject modes don't work
reliably anymore.  But the subobject knowledge should be the same between
the static and dynamic evaluation...

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 06/10] tree-object-size: Support dynamic sizes in conditions
  2021-11-23 15:12   ` Jakub Jelinek
@ 2021-11-23 15:36     ` Siddhesh Poyarekar
  2021-11-23 15:38       ` Siddhesh Poyarekar
  2021-11-23 15:52       ` Jakub Jelinek
  0 siblings, 2 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-23 15:36 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 11/23/21 20:42, Jakub Jelinek wrote:
> On Wed, Nov 10, 2021 at 12:31:32AM +0530, Siddhesh Poyarekar wrote:
>> 	(object_sizes_execute): Don't insert min/max for dynamic sizes.
> 
> I'm worried about this.
> I'd say what we might want to do is in the early pass for __bdos
> compute actually __bos (i.e. the static one) and add MIN_EXPR/MAX_EXPR
> for the result of the __bdos call from the second pass with the
> statically computed value.
> 
> The reason for the MIN_EXPR/MAX_EXPR stuff is that GIMPLE optimizations
> can remove exact ADDR_EXPRs with detailed COMPONENT_REF etc. access paths
> in it, so during the late objsz2 pass the subobject modes don't work
> reliably anymore.  But the subobject knowledge should be the same between
> the static and dynamic evaluation...

So in the dynamic case we almost always end up with the right expression 
in objsz1, except in cases where late optimizations make available 
information that wasn't available earlier.  How about putting in a 
MIN_EXPR/MAX_EXPR if we *fail* to get the subobject size instead?

Siddhesh

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 06/10] tree-object-size: Support dynamic sizes in conditions
  2021-11-23 15:36     ` Siddhesh Poyarekar
@ 2021-11-23 15:38       ` Siddhesh Poyarekar
  2021-11-23 16:17         ` Jakub Jelinek
  2021-11-23 15:52       ` Jakub Jelinek
  1 sibling, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-23 15:38 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 11/23/21 21:06, Siddhesh Poyarekar wrote:
> On 11/23/21 20:42, Jakub Jelinek wrote:
>> On Wed, Nov 10, 2021 at 12:31:32AM +0530, Siddhesh Poyarekar wrote:
>>>     (object_sizes_execute): Don't insert min/max for dynamic sizes.
>>
>> I'm worried about this.
>> I'd say what we might want to do is in the early pass for __bdos
>> compute actually __bos (i.e. the static one) and add MIN_EXPR/MAX_EXPR
>> for the result of the __bdos call from the second pass with the
>> statically computed value.
>>
>> The reason for the MIN_EXPR/MAX_EXPR stuff is that GIMPLE optimizations
>> can remove exact ADDR_EXPRs with detailed COMPONENT_REF etc. access paths
>> in it, so during the late objsz2 pass the subobject modes don't work
>> reliably anymore.  But the subobject knowledge should be the same between
>> the static and dynamic evaluation...
> 
> So in the dynamic case we almost always end up with the right expression 
> in objsz1, except in cases where late optimizations make available 
> information that wasn't available earlier.  How about putting in a 
> MIN_EXPR/MAX_EXPR if we *fail* to get the subobject size instead?

Actually if we don't get a dynamic expression it's unlikely that we'll 
get a static size either, so I'm not sure if MIN_EXPR/MAX_EXPR will 
actually do anything useful.

Siddhesh

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 06/10] tree-object-size: Support dynamic sizes in conditions
  2021-11-23 15:36     ` Siddhesh Poyarekar
  2021-11-23 15:38       ` Siddhesh Poyarekar
@ 2021-11-23 15:52       ` Jakub Jelinek
  2021-11-23 16:00         ` Siddhesh Poyarekar
  1 sibling, 1 reply; 97+ messages in thread
From: Jakub Jelinek @ 2021-11-23 15:52 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Tue, Nov 23, 2021 at 09:06:49PM +0530, Siddhesh Poyarekar wrote:
> On 11/23/21 20:42, Jakub Jelinek wrote:
> > On Wed, Nov 10, 2021 at 12:31:32AM +0530, Siddhesh Poyarekar wrote:
> > > 	(object_sizes_execute): Don't insert min/max for dynamic sizes.
> > 
> > I'm worried about this.
> > I'd say what we might want to do is in the early pass for __bdos
> > compute actually __bos (i.e. the static one) and add MIN_EXPR/MAX_EXPR
> > for the result of the __bdos call from the second pass with the
> > statically computed value.
> > 
> > The reason for the MIN_EXPR/MAX_EXPR stuff is that GIMPLE optimizations
> > can remove exact ADDR_EXPRs with detailed COMPONENT_REF etc. access paths
> > in it, so during the late objsz2 pass the subobject modes don't work
> > reliably anymore.  But the subobject knowledge should be the same between
> > the static and dynamic evaluation...
> 
> So in the dynamic case we almost always end up with the right expression in
> objsz1, except in cases where late optimizations make available information
> that wasn't available earlier.  How about putting in a MIN_EXPR/MAX_EXPR if
> we *fail* to get the subobject size instead?

I don't think that is the case, perhaps for trivial testcases yes when early
inlining inlines the fortification always_inline functions and everything
appears in a single function.
The primary reason for objsz2 being done later is that it is after inlining,
IPA optimizations and some optimization passes that clean up after those.
But at the same time it is after too many optimizations that could have
broken the exact subobject details.
But very often in objsz1 you'll just see const char *p argument and only
inlining will reveal how that was allocated etc.

Evaluating __bdos in both passes is undesirable, certainly for the same
SSA_NAME, but even for different SSA_NAMEs, if everything is done in a
single pass it can easily share temporaries (object sizes for SSA_NAMEs it
uses), while if some __bdos is evaluated early and other late, we'll need to
hope further optimizations CSE those.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 06/10] tree-object-size: Support dynamic sizes in conditions
  2021-11-23 15:52       ` Jakub Jelinek
@ 2021-11-23 16:00         ` Siddhesh Poyarekar
  2021-11-23 16:19           ` Jakub Jelinek
  0 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-23 16:00 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 11/23/21 21:22, Jakub Jelinek wrote:
> Evaluating __bdos in both passes is undesirable, certainly for the same
> SSA_NAME, but even for different SSA_NAMEs, if everything is done in a
> single pass it can easily share temporaries (object sizes for SSA_NAMEs it
> uses), while if some __bdos is evaluated early and other late, we'll need to
> hope further optimizations CSE those.

OK, then treat __bdos like __bos in objsz1, adding MIN?MAX for 
subobjects and full evaluation in objsz2?

Thanks,
Siddhesh

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 06/10] tree-object-size: Support dynamic sizes in conditions
  2021-11-23 15:38       ` Siddhesh Poyarekar
@ 2021-11-23 16:17         ` Jakub Jelinek
  0 siblings, 0 replies; 97+ messages in thread
From: Jakub Jelinek @ 2021-11-23 16:17 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Tue, Nov 23, 2021 at 09:08:35PM +0530, Siddhesh Poyarekar wrote:
> On 11/23/21 21:06, Siddhesh Poyarekar wrote:
> > On 11/23/21 20:42, Jakub Jelinek wrote:
> > > On Wed, Nov 10, 2021 at 12:31:32AM +0530, Siddhesh Poyarekar wrote:
> > > >     (object_sizes_execute): Don't insert min/max for dynamic sizes.
> > > 
> > > I'm worried about this.
> > > I'd say what we might want to do is in the early pass for __bdos
> > > compute actually __bos (i.e. the static one) and add MIN_EXPR/MAX_EXPR
> > > for the result of the __bdos call from the second pass with the
> > > statically computed value.
> > > 
> > > The reason for the MIN_EXPR/MAX_EXPR stuff is that GIMPLE optimizations
> > > can remove exact ADDR_EXPRs with detailed COMPONENT_REF etc. access paths
> > > in it, so during the late objsz2 pass the subobject modes don't work
> > > reliably anymore.  But the subobject knowledge should be the same between
> > > the static and dynamic evaluation...
> > 
> > So in the dynamic case we almost always end up with the right expression
> > in objsz1, except in cases where late optimizations make available
> > information that wasn't available earlier.  How about putting in a
> > MIN_EXPR/MAX_EXPR if we *fail* to get the subobject size instead?
> 
> Actually if we don't get a dynamic expression it's unlikely that we'll get a
> static size either, so I'm not sure if MIN_EXPR/MAX_EXPR will actually do
> anything useful.

Consider:
struct S { int a; char b[16]; int c; char d[]; };

static int
foo (struct S *s)
{
  return __builtin_object_size (&s->b[2], 1);
}

int
bar (int m)
{
  struct S *s = (struct S *) __builtin_malloc (m);
  int r = foo (s);
  __builtin_free (s);
  return r;
}

In early_objsz, foo isn't inlined, we can statically determine the maximum
bound of 14 but don't really know how large the allocation will actually be.
So, we record that foo returns MIN_EXPR <__builtin_object_size (&s->b[2], 1), 14>
and in the late objsz compute that as MIN_EXPR <-1UL, 14>.
Now, with __builtin_dynamic_object_size, I think it is pretty much the same,
you don't know how large the allocation actually is, so IMHO we want to
record MIN_EXPR <__buitin_dynamic_object_size (&s->b[2], 1), 14> and
at runtime do MIN_EXPR <m - 6, 14> or so.

It is true that it is just an upper bound, if we do:

static int
baz (struct S *s, int l)
{
  return __builtin_dynamic_object_size (l ? &s->b[3] : &s->b[2], 1);
}

int
qux (int m, int l)
{
  struct S *s = (struct S *) __builtin_malloc (m);
  int r = foo (s, l);
  __builtin_free (s);
  return r;
}

then the statically computed __bos in early_objsz would be still 14 and I
think dynamic needs to punt because it really doesn't know how large the
allocation will be.  In the late objsz it can make
m - 6 - !!l out of it for the __bdos (, 0) from it and combine that
to MIN_EXPR <m - 6 - !!l, 14> which still isn't exact, that would be
l ? MIN_EXPR <m - 7, 13> : MIN_EXPR <m - 6, 14>.
But, at late objsz time, the information that there was &s->b[3] and
&s->b[2] might be gone, consider e.g. in baz above a call
  corge ((char *) s + offsetof (struct S, b) + 2,
	 (char *) s + offsetof (struct S, b) + 3);
where SCCVN will CSE the (char *) s + offsetof (struct S, b) + 2
and &s->b[2] etc. expressions because they have the same value.
But __bos ((char *) s + offsetof (struct S, b) + 2, 1) should be
equal to __bos ((char *) s + offsetof (struct S, b) + 2, 0).

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH 06/10] tree-object-size: Support dynamic sizes in conditions
  2021-11-23 16:00         ` Siddhesh Poyarekar
@ 2021-11-23 16:19           ` Jakub Jelinek
  0 siblings, 0 replies; 97+ messages in thread
From: Jakub Jelinek @ 2021-11-23 16:19 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Tue, Nov 23, 2021 at 09:30:30PM +0530, Siddhesh Poyarekar wrote:
> On 11/23/21 21:22, Jakub Jelinek wrote:
> > Evaluating __bdos in both passes is undesirable, certainly for the same
> > SSA_NAME, but even for different SSA_NAMEs, if everything is done in a
> > single pass it can easily share temporaries (object sizes for SSA_NAMEs it
> > uses), while if some __bdos is evaluated early and other late, we'll need to
> > hope further optimizations CSE those.
> 
> OK, then treat __bdos like __bos in objsz1, adding MIN?MAX for subobjects
> and full evaluation in objsz2?

Yes.  It is not perfect, but unfortunately it is hard to get perfect
results.  The subobject stuff for __bos has been designed when GCC wasn't
doing such optimizations, later MEM_REF has been introduced and we really
can't prevent all those optimizations just because there could be a
subobject __bos somewhere that cares about that.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v3 0/8] __builtin_dynamic_object_size
  2021-11-09 19:01 [PATCH 00/10] __builtin_dynamic_object_size Siddhesh Poyarekar
                   ` (10 preceding siblings ...)
  2021-11-19 15:56 ` [PATCH 00/10] __builtin_dynamic_object_size Jakub Jelinek
@ 2021-11-26  5:28 ` Siddhesh Poyarekar
  2021-11-26  5:28   ` [PATCH v3 1/8] tree-object-size: Replace magic numbers with enums Siddhesh Poyarekar
                     ` (8 more replies)
  2021-12-01 14:27 ` [PATCH v4 0/6] __builtin_dynamic_object_size Siddhesh Poyarekar
                   ` (2 subsequent siblings)
  14 siblings, 9 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-26  5:28 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

This patchset implements the __builtin_dynamic_object_size builtin for
gcc.  The primary motivation to have this builtin in gcc is to enable
_FORTIFY_SOURCE=3 support with gcc, thus allowing greater fortification
in use cases where the potential performance tradeoff is acceptable.

Semantics:
----------

__builtin_dynamic_object_size has the same signature as
__builtin_object_size; it accepts a pointer and type ranging from 0 to 3
and it returns an object size estimate for the pointer based on an
analysis of which objects the pointer could point to.  The actual
properties of the object size estimate are different:

- In the best case __builtin_dynamic_object_size evaluates to an
  expression that represents a precise size of the object being pointed
  to.

- In case a precise object size expression cannot be evaluated,
  __builtin_dynamic_object_size attempts to evaluate an estimate size
  expression based on the object size type.

- In what situations the builtin returns an estimate vs a precise
  expression is an implementation detail and may change in future.
  Users must always assume, as in the case of __builtin_object_size, that
  the returned value is the maximum or minimum based on the object size
  type they have provided.

- In the worst case of failure, __builtin_dynamic_object_size returns a
  constant (size_t)-1 or (size_t)0.

Implementation:
---------------

- The __builtin_dynamic_object_size support is implemented in
  tree-object-size.  In most cases the first pass (early_objsz) the
  builtin is treated like __builtin_object_size to preserve subobject
  bounds.

- Each element of the object_sizes vector is now a TREE_VEC of size 2
  holding bytes to the end of the object and the full size of the
  object.  This allows proper handling of negative offsets, allowing
  them to the extent of the whole object bounds.  This improves
  __builtin_object_size usage too with negative offsets, consistently
  returning valid results for pointer decrementing loops too.

- The patchset begins with structural modification of the
  tree-object-size pass, followed by enhancement to return size
  expressions.  I have split the implementation into one feature per
  patch (calls, function parameters, PHI, etc.) to hopefully ease
  review.

Performance:
------------

Expressions generated by this pass in theory could be arbitrarily
complex.  I have not made an attempt to limit nesting of objects since
it seemed too early to do that.  In practice based on the few
applications I built, most of the complexity of the expressions got
folded away.  Even so, the performance overhead is likely to be
non-zero.  If we find performance degradation to be significant, we
could later add nesting limits to bail out if a size expression gets too
complex.

I have implemented simplification of __*_chk to their normal
variants if we can determine at compile time that it is safe.  This
should limit the performance overhead of the expressions in valid cases.

Build time performance doesn't seem to be affected much based on an
unscientific check to time
`make check-gcc RUNTESTFLAGS="dg.exp=builtin*"`.  It only increases by
about a couple of seconds when the dynamic tests are added and remains
more or less in the same ballpark otherwise.

Testing:
--------

I have added tests for dynamic object sizes as well as wrappers for all
__builtin_object_size tests to provide wide coverage.  I have also done
a full bootstrap build and test run on x86_64.

I have also built bash, cmake, wpa_supplicant and systemtap with
_FORTIFY_SOURCE=2 and _FORTIFY_SOURCE=3 (with a hacked up glibc to make
sure it works) and saw no issues in any of those builds.  I did some
rudimentary analysis of the generated binaries using fortify-metrics[1]
to confirm that there was a difference in coverage between the two
fortification levels.

Here is a summary of coverage in the above packages:

F = number of fortified calls
T = Total number of calls to fortifiable functions (fortified as well as
unfortified)
C = F * 100/ T

Package		F(2)	T(2)	F(3)	T(3)	C(2)	C(3)
bash		428	1220	1005	1196	35.08%	84.03%
wpa_supplicant	1635	3232	2350	3408	50.59%	68.96%
systemtap	324	1990	343	1994	16.28%	17.20%
cmake		830	14181	958	14196	5.85%	6.75%

The numbers are slightly lower than the previous patch series because in
the interim I pushed an improvement to folding of the _chk builtins so
that they can use ranges to simplify the calls to their regular
variants.  Also note that even _FORTIFY_SOURCE=2 coverage should be
improved due to negative offset handling.

Additional testing plans (i.e. I've already started to do some of this):

- Build packages to compare values returned by __builtin_object_size
  with the older pass and this new one.  Also compare with
  __builtin_dynamic_object_size.

- Expand the list of packages to get more coverage metrics.

- Explore performance impact on applications on building with
  _FORTIFY_SOURCE=3.

Limitations/Future work:
------------------------

- I need to enable _FORTIFY_SOURCE=3 for gcc in glibc; currently it is
  llvm-only.  I've started working on these patches too on the side.

- Explore ways to use the non-constant sizes returned for
  __builtin_object_size to arrive at a constant estimate to improve
  _FORTIFY_SOURCE=2 coverage in a way that accounts for undefined
  behaviour.

- More work could to be done to reduce the performance impact of the
  computation.  One way could be to add a heuristic where the pass keeps
  track of nesting in the expression and either bail out or compute an
  estimate if nesting crosses a threshold.  I'll take this up once we
  have more data on the nature of the bottlenecks.

Changes from v2:

Changes to individual patches have been mentioned in the patches
themselves.

- Dropped patch to remove check_for_plus_in_for_loops and osi->pass
- Merge negative offset support (10/10 in v2) into 3/8 and support
  static object sizes
- Merge dynamic offset (10/10 in v2) support into 8/8

Siddhesh Poyarekar (8):
  tree-object-size: Replace magic numbers with enums
  tree-object-size: Abstract object_sizes array
  tree-object-size: Save sizes as trees and support negative offsets
  __builtin_dynamic_object_size: Recognize builtin
  tree-object-size: Support dynamic sizes in conditions
  tree-object-size: Handle function parameters
  tree-object-size: Handle GIMPLE_CALL
  tree-object-size: Dynamic sizes for ADDR_EXPR

 gcc/builtins.c                                |   23 +-
 gcc/builtins.def                              |    1 +
 gcc/doc/extend.texi                           |   13 +
 gcc/gimple-fold.c                             |   11 +-
 .../g++.dg/ext/builtin-dynamic-object-size1.C |    5 +
 .../g++.dg/ext/builtin-dynamic-object-size2.C |    5 +
 .../gcc.dg/builtin-dynamic-alloc-size.c       |    7 +
 .../gcc.dg/builtin-dynamic-object-size-0.c    |  464 +++++++
 .../gcc.dg/builtin-dynamic-object-size-1.c    |    6 +
 .../gcc.dg/builtin-dynamic-object-size-10.c   |   11 +
 .../gcc.dg/builtin-dynamic-object-size-11.c   |    7 +
 .../gcc.dg/builtin-dynamic-object-size-12.c   |    5 +
 .../gcc.dg/builtin-dynamic-object-size-13.c   |    5 +
 .../gcc.dg/builtin-dynamic-object-size-14.c   |    5 +
 .../gcc.dg/builtin-dynamic-object-size-15.c   |    5 +
 .../gcc.dg/builtin-dynamic-object-size-16.c   |    6 +
 .../gcc.dg/builtin-dynamic-object-size-17.c   |    7 +
 .../gcc.dg/builtin-dynamic-object-size-18.c   |    8 +
 .../gcc.dg/builtin-dynamic-object-size-19.c   |  104 ++
 .../gcc.dg/builtin-dynamic-object-size-2.c    |    6 +
 .../gcc.dg/builtin-dynamic-object-size-3.c    |    6 +
 .../gcc.dg/builtin-dynamic-object-size-4.c    |    6 +
 .../gcc.dg/builtin-dynamic-object-size-5.c    |    7 +
 .../gcc.dg/builtin-dynamic-object-size-6.c    |    5 +
 .../gcc.dg/builtin-dynamic-object-size-7.c    |    5 +
 .../gcc.dg/builtin-dynamic-object-size-8.c    |    5 +
 .../gcc.dg/builtin-dynamic-object-size-9.c    |    5 +
 gcc/testsuite/gcc.dg/builtin-object-size-1.c  |  184 ++-
 gcc/testsuite/gcc.dg/builtin-object-size-16.c |    2 +
 gcc/testsuite/gcc.dg/builtin-object-size-17.c |    2 +
 gcc/testsuite/gcc.dg/builtin-object-size-2.c  |  163 +++
 gcc/testsuite/gcc.dg/builtin-object-size-3.c  |  182 +++
 gcc/testsuite/gcc.dg/builtin-object-size-4.c  |  123 ++
 gcc/testsuite/gcc.dg/builtin-object-size-5.c  |   37 +
 gcc/tree-object-size.c                        | 1219 +++++++++++++----
 gcc/tree-object-size.h                        |   12 +-
 gcc/ubsan.c                                   |    5 +-
 37 files changed, 2392 insertions(+), 280 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size1.C
 create mode 100644 gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size2.C
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-alloc-size.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-11.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-12.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-13.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-14.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-15.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-16.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-17.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-18.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-19.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-6.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-7.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-8.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-9.c

-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v3 1/8] tree-object-size: Replace magic numbers with enums
  2021-11-26  5:28 ` [PATCH v3 0/8] __builtin_dynamic_object_size Siddhesh Poyarekar
@ 2021-11-26  5:28   ` Siddhesh Poyarekar
  2021-11-26 16:46     ` Jakub Jelinek
  2021-11-26  5:28   ` [PATCH v3 2/8] tree-object-size: Abstract object_sizes array Siddhesh Poyarekar
                     ` (7 subsequent siblings)
  8 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-26  5:28 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

A simple cleanup to allow inserting dynamic size code more easily.

gcc/ChangeLog:

	* tree-object-size.c: New enum.
	(object_sizes, computed, addr_object_size,
	compute_builtin_object_size, expr_object_size, call_object_size,
	merge_object_sizes, plus_stmt_object_size,
	collect_object_sizes_for, init_object_sizes, fini_object_sizes,
	object_sizes_execute): Replace magic numbers with enums.

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

Changes from v2:

- Incorporated review suggestions.

 gcc/tree-object-size.c | 59 ++++++++++++++++++++++++------------------
 1 file changed, 34 insertions(+), 25 deletions(-)

diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index 4334e05ef70..5e93bb74f92 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -45,6 +45,13 @@ struct object_size_info
   unsigned int *stack, *tos;
 };
 
+enum
+{
+  OST_SUBOBJECT = 1,
+  OST_MINIMUM = 2,
+  OST_END = 4,
+};
+
 static tree compute_object_offset (const_tree, const_tree);
 static bool addr_object_size (struct object_size_info *,
 			      const_tree, int, unsigned HOST_WIDE_INT *);
@@ -67,10 +74,10 @@ static void check_for_plus_in_loops_1 (struct object_size_info *, tree,
    the subobject (innermost array or field with address taken).
    object_sizes[2] is lower bound for number of bytes till the end of
    the object and object_sizes[3] lower bound for subobject.  */
-static vec<unsigned HOST_WIDE_INT> object_sizes[4];
+static vec<unsigned HOST_WIDE_INT> object_sizes[OST_END];
 
 /* Bitmaps what object sizes have been computed already.  */
-static bitmap computed[4];
+static bitmap computed[OST_END];
 
 /* Maximum value of offset we consider to be addition.  */
 static unsigned HOST_WIDE_INT offset_limit;
@@ -227,11 +234,11 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
     {
       unsigned HOST_WIDE_INT sz;
 
-      if (!osi || (object_size_type & 1) != 0
+      if (!osi || (object_size_type & OST_SUBOBJECT) != 0
 	  || TREE_CODE (TREE_OPERAND (pt_var, 0)) != SSA_NAME)
 	{
 	  compute_builtin_object_size (TREE_OPERAND (pt_var, 0),
-				       object_size_type & ~1, &sz);
+				       object_size_type & ~OST_SUBOBJECT, &sz);
 	}
       else
 	{
@@ -266,7 +273,7 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
     }
   else if (DECL_P (pt_var))
     {
-      pt_var_size = decl_init_size (pt_var, object_size_type & 2);
+      pt_var_size = decl_init_size (pt_var, object_size_type & OST_MINIMUM);
       if (!pt_var_size)
 	return false;
     }
@@ -287,7 +294,7 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
     {
       tree var;
 
-      if (object_size_type & 1)
+      if (object_size_type & OST_SUBOBJECT)
 	{
 	  var = TREE_OPERAND (ptr, 0);
 
@@ -528,7 +535,7 @@ bool
 compute_builtin_object_size (tree ptr, int object_size_type,
 			     unsigned HOST_WIDE_INT *psize)
 {
-  gcc_assert (object_size_type >= 0 && object_size_type <= 3);
+  gcc_assert (object_size_type >= 0 && object_size_type < OST_END);
 
   /* Set to unknown and overwrite just before returning if the size
      could be determined.  */
@@ -546,7 +553,7 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 
   if (computed[object_size_type] == NULL)
     {
-      if (optimize || object_size_type & 1)
+      if (optimize || object_size_type & OST_SUBOBJECT)
 	return false;
 
       /* When not optimizing, rather than failing, make a small effort
@@ -586,8 +593,8 @@ compute_builtin_object_size (tree ptr, int object_size_type,
       if (dump_file)
 	{
 	  fprintf (dump_file, "Computing %s %sobject size for ",
-		   (object_size_type & 2) ? "minimum" : "maximum",
-		   (object_size_type & 1) ? "sub" : "");
+		   (object_size_type & OST_MINIMUM) ? "minimum" : "maximum",
+		   (object_size_type & OST_SUBOBJECT) ? "sub" : "");
 	  print_generic_expr (dump_file, ptr, dump_flags);
 	  fprintf (dump_file, ":\n");
 	}
@@ -620,7 +627,7 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 	     terminate, it could take a long time.  If a pointer is
 	     increasing this way, we need to assume 0 object size.
 	     E.g. p = &buf[0]; while (cond) p = p + 4;  */
-	  if (object_size_type & 2)
+	  if (object_size_type & OST_MINIMUM)
 	    {
 	      osi.depths = XCNEWVEC (unsigned int, num_ssa_names);
 	      osi.stack = XNEWVEC (unsigned int, num_ssa_names);
@@ -679,8 +686,9 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 		fprintf (dump_file,
 			 ": %s %sobject size "
 			 HOST_WIDE_INT_PRINT_UNSIGNED "\n",
-			 (object_size_type & 2) ? "minimum" : "maximum",
-			 (object_size_type & 1) ? "sub" : "",
+			 ((object_size_type & OST_MINIMUM) ? "minimum"
+			  : "maximum"),
+			 (object_size_type & OST_SUBOBJECT) ? "sub" : "",
 			 object_sizes[object_size_type][i]);
 	      }
 	}
@@ -718,7 +726,7 @@ expr_object_size (struct object_size_info *osi, tree ptr, tree value)
   else
     bytes = unknown (object_size_type);
 
-  if ((object_size_type & 2) == 0)
+  if ((object_size_type & OST_MINIMUM) == 0)
     {
       if (object_sizes[object_size_type][varno] < bytes)
 	object_sizes[object_size_type][varno] = bytes;
@@ -748,7 +756,7 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call)
 
   bytes = alloc_object_size (call, object_size_type);
 
-  if ((object_size_type & 2) == 0)
+  if ((object_size_type & OST_MINIMUM) == 0)
     {
       if (object_sizes[object_size_type][varno] < bytes)
 	object_sizes[object_size_type][varno] = bytes;
@@ -804,7 +812,7 @@ merge_object_sizes (struct object_size_info *osi, tree dest, tree orig,
     orig_bytes = (offset > orig_bytes)
 		 ? HOST_WIDE_INT_0U : orig_bytes - offset;
 
-  if ((object_size_type & 2) == 0)
+  if ((object_size_type & OST_MINIMUM) == 0)
     {
       if (object_sizes[object_size_type][varno] < orig_bytes)
 	{
@@ -882,7 +890,7 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   else
     bytes = unknown (object_size_type);
 
-  if ((object_size_type & 2) == 0)
+  if ((object_size_type & OST_MINIMUM) == 0)
     {
       if (object_sizes[object_size_type][varno] < bytes)
 	object_sizes[object_size_type][varno] = bytes;
@@ -968,7 +976,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
       if (bitmap_set_bit (osi->visited, varno))
 	{
 	  object_sizes[object_size_type][varno]
-	    = (object_size_type & 2) ? -1 : 0;
+	    = (object_size_type & OST_MINIMUM) ? -1 : 0;
 	}
       else
 	{
@@ -1238,7 +1246,7 @@ init_object_sizes (void)
   if (computed[0])
     return;
 
-  for (object_size_type = 0; object_size_type <= 3; object_size_type++)
+  for (object_size_type = 0; object_size_type < OST_END; object_size_type++)
     {
       object_sizes[object_size_type].safe_grow (num_ssa_names, true);
       computed[object_size_type] = BITMAP_ALLOC (NULL);
@@ -1255,7 +1263,7 @@ fini_object_sizes (void)
 {
   int object_size_type;
 
-  for (object_size_type = 0; object_size_type <= 3; object_size_type++)
+  for (object_size_type = 0; object_size_type < OST_END; object_size_type++)
     {
       object_sizes[object_size_type].release ();
       BITMAP_FREE (computed[object_size_type]);
@@ -1302,7 +1310,7 @@ object_sizes_execute (function *fun, bool insert_min_max_p)
 		{
 		  unsigned HOST_WIDE_INT object_size_type = tree_to_uhwi (ost);
 		  tree ptr = gimple_call_arg (call, 0);
-		  if ((object_size_type == 1 || object_size_type == 3)
+		  if ((object_size_type & OST_SUBOBJECT)
 		      && (TREE_CODE (ptr) == ADDR_EXPR
 			  || TREE_CODE (ptr) == SSA_NAME))
 		    {
@@ -1315,7 +1323,8 @@ object_sizes_execute (function *fun, bool insert_min_max_p)
 			  tree tem = make_ssa_name (type);
 			  gimple_call_set_lhs (call, tem);
 			  enum tree_code code
-			    = object_size_type == 1 ? MIN_EXPR : MAX_EXPR;
+			    = (object_size_type & OST_MINIMUM
+			       ? MAX_EXPR : MIN_EXPR);
 			  tree cst = build_int_cstu (type, bytes);
 			  gimple *g
 			    = gimple_build_assign (lhs, code, tem, cst);
@@ -1336,11 +1345,11 @@ object_sizes_execute (function *fun, bool insert_min_max_p)
 		{
 		  unsigned HOST_WIDE_INT object_size_type = tree_to_uhwi (ost);
 
-		  if (object_size_type < 2)
+		  if (object_size_type & OST_MINIMUM)
+		    result = build_zero_cst (size_type_node);
+		  else if (object_size_type < OST_END)
 		    result = fold_convert (size_type_node,
 					   integer_minus_one_node);
-		  else if (object_size_type < 4)
-		    result = build_zero_cst (size_type_node);
 		}
 
 	      if (!result)
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v3 2/8] tree-object-size: Abstract object_sizes array
  2021-11-26  5:28 ` [PATCH v3 0/8] __builtin_dynamic_object_size Siddhesh Poyarekar
  2021-11-26  5:28   ` [PATCH v3 1/8] tree-object-size: Replace magic numbers with enums Siddhesh Poyarekar
@ 2021-11-26  5:28   ` Siddhesh Poyarekar
  2021-11-26 16:47     ` Jakub Jelinek
  2021-11-26  5:28   ` [PATCH v3 3/8] tree-object-size: Save sizes as trees and support negative offsets Siddhesh Poyarekar
                     ` (6 subsequent siblings)
  8 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-26  5:28 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Put all accesses to object_sizes behind functions so that we can add
dynamic capability more easily.

gcc/ChangeLog:

	* tree-object-size.c (object_sizes_grow, object_sizes_release,
	object_sizes_unknown_p, object_sizes_get, object_size_set_force,
	object_sizes_set): New functions.
	(addr_object_size, compute_builtin_object_size,
	expr_object_size, call_object_size, unknown_object_size,
	merge_object_sizes, plus_stmt_object_size,
	cond_expr_object_size, collect_object_sizes_for,
	check_for_plus_in_loops_1, init_object_sizes,
	fini_object_sizes): Adjust.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
Changes from v2:

- Incorporated review suggestions.

 gcc/tree-object-size.c | 177 +++++++++++++++++++++++------------------
 1 file changed, 98 insertions(+), 79 deletions(-)

diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index 5e93bb74f92..3780437ff91 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -88,6 +88,71 @@ unknown (int object_size_type)
   return ((unsigned HOST_WIDE_INT) -((object_size_type >> 1) ^ 1));
 }
 
+/* Grow object_sizes[OBJECT_SIZE_TYPE] to num_ssa_names.  */
+
+static inline void
+object_sizes_grow (int object_size_type)
+{
+  if (num_ssa_names > object_sizes[object_size_type].length ())
+    object_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[object_size_type].release ();
+}
+
+/* Return true if object_sizes[OBJECT_SIZE_TYPE][VARNO] is unknown.  */
+
+static inline bool
+object_sizes_unknown_p (int object_size_type, unsigned varno)
+{
+  return (object_sizes[object_size_type][varno]
+	  == unknown (object_size_type));
+}
+
+/* Return size for VARNO corresponding to OSI.  */
+
+static inline unsigned HOST_WIDE_INT
+object_sizes_get (struct object_size_info *osi, unsigned varno)
+{
+  return object_sizes[osi->object_size_type][varno];
+}
+
+/* Set size for VARNO corresponding to OSI to VAL.  */
+
+static inline bool
+object_sizes_set_force (struct object_size_info *osi, unsigned varno,
+			unsigned HOST_WIDE_INT val)
+{
+  object_sizes[osi->object_size_type][varno] = val;
+  return true;
+}
+
+/* Set size for VARNO corresponding to OSI to VAL if it is the new minimum or
+   maximum.  */
+
+static inline bool
+object_sizes_set (struct object_size_info *osi, unsigned varno,
+		  unsigned HOST_WIDE_INT val)
+{
+  int object_size_type = osi->object_size_type;
+  if ((object_size_type & OST_MINIMUM) == 0)
+    {
+      if (object_sizes[object_size_type][varno] < val)
+	return object_sizes_set_force (osi, varno, val);
+    }
+  else
+    {
+      if (object_sizes[object_size_type][varno] > val)
+	return object_sizes_set_force (osi, varno, val);
+    }
+  return false;
+}
+
 /* Initialize OFFSET_LIMIT variable.  */
 static void
 init_offset_limit (void)
@@ -247,7 +312,7 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 	    collect_object_sizes_for (osi, var);
 	  if (bitmap_bit_p (computed[object_size_type],
 			    SSA_NAME_VERSION (var)))
-	    sz = object_sizes[object_size_type][SSA_NAME_VERSION (var)];
+	    sz = object_sizes_get (osi, SSA_NAME_VERSION (var));
 	  else
 	    sz = unknown (object_size_type);
 	}
@@ -582,14 +647,14 @@ compute_builtin_object_size (tree ptr, int object_size_type,
       return false;
     }
 
+  struct object_size_info osi;
+  osi.object_size_type = object_size_type;
   if (!bitmap_bit_p (computed[object_size_type], SSA_NAME_VERSION (ptr)))
     {
-      struct object_size_info osi;
       bitmap_iterator bi;
       unsigned int i;
 
-      if (num_ssa_names > object_sizes[object_size_type].length ())
-	object_sizes[object_size_type].safe_grow (num_ssa_names, true);
+      object_sizes_grow (object_size_type);
       if (dump_file)
 	{
 	  fprintf (dump_file, "Computing %s %sobject size for ",
@@ -601,7 +666,6 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 
       osi.visited = BITMAP_ALLOC (NULL);
       osi.reexamine = BITMAP_ALLOC (NULL);
-      osi.object_size_type = object_size_type;
       osi.depths = NULL;
       osi.stack = NULL;
       osi.tos = NULL;
@@ -678,8 +742,7 @@ compute_builtin_object_size (tree ptr, int object_size_type,
       if (dump_file)
 	{
 	  EXECUTE_IF_SET_IN_BITMAP (osi.visited, 0, i, bi)
-	    if (object_sizes[object_size_type][i]
-		!= unknown (object_size_type))
+	    if (!object_sizes_unknown_p (object_size_type, i))
 	      {
 		print_generic_expr (dump_file, ssa_name (i),
 				    dump_flags);
@@ -689,7 +752,7 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 			 ((object_size_type & OST_MINIMUM) ? "minimum"
 			  : "maximum"),
 			 (object_size_type & OST_SUBOBJECT) ? "sub" : "",
-			 object_sizes[object_size_type][i]);
+			 object_sizes_get (&osi, i));
 	      }
 	}
 
@@ -697,7 +760,7 @@ compute_builtin_object_size (tree ptr, int object_size_type,
       BITMAP_FREE (osi.visited);
     }
 
-  *psize = object_sizes[object_size_type][SSA_NAME_VERSION (ptr)];
+  *psize = object_sizes_get (&osi, SSA_NAME_VERSION (ptr));
   return *psize != unknown (object_size_type);
 }
 
@@ -710,8 +773,7 @@ expr_object_size (struct object_size_info *osi, tree ptr, tree value)
   unsigned int varno = SSA_NAME_VERSION (ptr);
   unsigned HOST_WIDE_INT bytes;
 
-  gcc_assert (object_sizes[object_size_type][varno]
-	      != unknown (object_size_type));
+  gcc_assert (!object_sizes_unknown_p (object_size_type, varno));
   gcc_assert (osi->pass == 0);
 
   if (TREE_CODE (value) == WITH_SIZE_EXPR)
@@ -726,16 +788,7 @@ expr_object_size (struct object_size_info *osi, tree ptr, tree value)
   else
     bytes = unknown (object_size_type);
 
-  if ((object_size_type & OST_MINIMUM) == 0)
-    {
-      if (object_sizes[object_size_type][varno] < bytes)
-	object_sizes[object_size_type][varno] = bytes;
-    }
-  else
-    {
-      if (object_sizes[object_size_type][varno] > bytes)
-	object_sizes[object_size_type][varno] = bytes;
-    }
+  object_sizes_set (osi, varno, bytes);
 }
 
 
@@ -750,22 +803,12 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call)
 
   gcc_assert (is_gimple_call (call));
 
-  gcc_assert (object_sizes[object_size_type][varno]
-	      != unknown (object_size_type));
+  gcc_assert (!object_sizes_unknown_p (object_size_type, varno));
   gcc_assert (osi->pass == 0);
 
   bytes = alloc_object_size (call, object_size_type);
 
-  if ((object_size_type & OST_MINIMUM) == 0)
-    {
-      if (object_sizes[object_size_type][varno] < bytes)
-	object_sizes[object_size_type][varno] = bytes;
-    }
-  else
-    {
-      if (object_sizes[object_size_type][varno] > bytes)
-	object_sizes[object_size_type][varno] = bytes;
-    }
+  object_sizes_set (osi, varno, bytes);
 }
 
 
@@ -776,12 +819,11 @@ unknown_object_size (struct object_size_info *osi, tree ptr)
 {
   int object_size_type = osi->object_size_type;
   unsigned int varno = SSA_NAME_VERSION (ptr);
-  unsigned HOST_WIDE_INT bytes = unknown (object_size_type);
 
-  gcc_checking_assert (object_sizes[object_size_type][varno] != bytes);
+  gcc_checking_assert (!object_sizes_unknown_p (object_size_type, varno));
   gcc_checking_assert (osi->pass == 0);
 
-  object_sizes[object_size_type][varno] = bytes;
+  object_sizes_set (osi, varno, unknown (object_size_type));
 }
 
 
@@ -796,38 +838,24 @@ merge_object_sizes (struct object_size_info *osi, tree dest, tree orig,
   unsigned int varno = SSA_NAME_VERSION (dest);
   unsigned HOST_WIDE_INT orig_bytes;
 
-  if (object_sizes[object_size_type][varno] == unknown (object_size_type))
+  if (object_sizes_unknown_p (object_size_type, varno))
     return false;
   if (offset >= offset_limit)
     {
-      object_sizes[object_size_type][varno] = unknown (object_size_type);
+      object_sizes_set (osi, varno, unknown (object_size_type));
       return false;
     }
 
   if (osi->pass == 0)
     collect_object_sizes_for (osi, orig);
 
-  orig_bytes = object_sizes[object_size_type][SSA_NAME_VERSION (orig)];
+  orig_bytes = object_sizes_get (osi, SSA_NAME_VERSION (orig));
   if (orig_bytes != unknown (object_size_type))
     orig_bytes = (offset > orig_bytes)
 		 ? HOST_WIDE_INT_0U : orig_bytes - offset;
 
-  if ((object_size_type & OST_MINIMUM) == 0)
-    {
-      if (object_sizes[object_size_type][varno] < orig_bytes)
-	{
-	  object_sizes[object_size_type][varno] = orig_bytes;
-	  osi->changed = true;
-	}
-    }
-  else
-    {
-      if (object_sizes[object_size_type][varno] > orig_bytes)
-	{
-	  object_sizes[object_size_type][varno] = orig_bytes;
-	  osi->changed = true;
-	}
-    }
+  osi->changed = object_sizes_set (osi, varno, orig_bytes);
+
   return bitmap_bit_p (osi->reexamine, SSA_NAME_VERSION (orig));
 }
 
@@ -859,7 +887,7 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   else
     gcc_unreachable ();
 
-  if (object_sizes[object_size_type][varno] == unknown (object_size_type))
+  if (object_sizes_unknown_p (object_size_type, varno))
     return false;
 
   /* Handle PTR + OFFSET here.  */
@@ -890,16 +918,7 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   else
     bytes = unknown (object_size_type);
 
-  if ((object_size_type & OST_MINIMUM) == 0)
-    {
-      if (object_sizes[object_size_type][varno] < bytes)
-	object_sizes[object_size_type][varno] = bytes;
-    }
-  else
-    {
-      if (object_sizes[object_size_type][varno] > bytes)
-	object_sizes[object_size_type][varno] = bytes;
-    }
+  object_sizes_set (osi, varno, bytes);
   return false;
 }
 
@@ -918,7 +937,7 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
 
   gcc_assert (gimple_assign_rhs_code (stmt) == COND_EXPR);
 
-  if (object_sizes[object_size_type][varno] == unknown (object_size_type))
+  if (object_sizes_unknown_p (object_size_type, varno))
     return false;
 
   then_ = gimple_assign_rhs2 (stmt);
@@ -929,7 +948,7 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   else
     expr_object_size (osi, var, then_);
 
-  if (object_sizes[object_size_type][varno] == unknown (object_size_type))
+  if (object_sizes_unknown_p (object_size_type, varno))
     return reexamine;
 
   if (TREE_CODE (else_) == SSA_NAME)
@@ -975,8 +994,10 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
     {
       if (bitmap_set_bit (osi->visited, varno))
 	{
-	  object_sizes[object_size_type][varno]
-	    = (object_size_type & OST_MINIMUM) ? -1 : 0;
+	  /* Initialize to 0 for maximum size and M1U for minimum size so that
+	     it gets immediately overridden.  */
+	  object_sizes_set_force (osi, varno,
+				  unknown (object_size_type ^ OST_MINIMUM));
 	}
       else
 	{
@@ -1047,7 +1068,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 
     case GIMPLE_ASM:
       /* Pointers defined by __asm__ statements can point anywhere.  */
-      object_sizes[object_size_type][varno] = unknown (object_size_type);
+      object_sizes_set (osi, varno, unknown (object_size_type));
       break;
 
     case GIMPLE_NOP:
@@ -1056,7 +1077,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 	expr_object_size (osi, var, SSA_NAME_VAR (var));
       else
 	/* Uninitialized SSA names point nowhere.  */
-	object_sizes[object_size_type][varno] = unknown (object_size_type);
+	object_sizes_set (osi, varno, unknown (object_size_type));
       break;
 
     case GIMPLE_PHI:
@@ -1067,8 +1088,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 	  {
 	    tree rhs = gimple_phi_arg (stmt, i)->def;
 
-	    if (object_sizes[object_size_type][varno]
-		== unknown (object_size_type))
+	    if (object_sizes_unknown_p (object_size_type, varno))
 	      break;
 
 	    if (TREE_CODE (rhs) == SSA_NAME)
@@ -1083,8 +1103,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
       gcc_unreachable ();
     }
 
-  if (! reexamine
-      || object_sizes[object_size_type][varno] == unknown (object_size_type))
+  if (! reexamine || object_sizes_unknown_p (object_size_type, varno))
     {
       bitmap_set_bit (computed[object_size_type], varno);
       bitmap_clear_bit (osi->reexamine, varno);
@@ -1124,7 +1143,7 @@ check_for_plus_in_loops_1 (struct object_size_info *osi, tree var,
 	      --sp;
 	      bitmap_clear_bit (osi->reexamine, *sp);
 	      bitmap_set_bit (computed[osi->object_size_type], *sp);
-	      object_sizes[osi->object_size_type][*sp] = 0;
+	      object_sizes_set_force (osi, *sp, 0);
 	      if (*sp == varno)
 		break;
 	    }
@@ -1248,7 +1267,7 @@ init_object_sizes (void)
 
   for (object_size_type = 0; object_size_type < OST_END; object_size_type++)
     {
-      object_sizes[object_size_type].safe_grow (num_ssa_names, true);
+      object_sizes_grow (object_size_type);
       computed[object_size_type] = BITMAP_ALLOC (NULL);
     }
 
@@ -1265,7 +1284,7 @@ fini_object_sizes (void)
 
   for (object_size_type = 0; object_size_type < OST_END; object_size_type++)
     {
-      object_sizes[object_size_type].release ();
+      object_sizes_release (object_size_type);
       BITMAP_FREE (computed[object_size_type]);
     }
 }
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v3 3/8] tree-object-size: Save sizes as trees and support negative offsets
  2021-11-26  5:28 ` [PATCH v3 0/8] __builtin_dynamic_object_size Siddhesh Poyarekar
  2021-11-26  5:28   ` [PATCH v3 1/8] tree-object-size: Replace magic numbers with enums Siddhesh Poyarekar
  2021-11-26  5:28   ` [PATCH v3 2/8] tree-object-size: Abstract object_sizes array Siddhesh Poyarekar
@ 2021-11-26  5:28   ` Siddhesh Poyarekar
  2021-11-26 16:56     ` Jakub Jelinek
  2021-11-26  5:28   ` [PATCH v3 4/8] __builtin_dynamic_object_size: Recognize builtin Siddhesh Poyarekar
                     ` (5 subsequent siblings)
  8 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-26  5:28 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Transform tree-object-size to operate on tree objects instead of host
wide integers.  This makes it easier to extend to dynamic expressions
for object sizes.

The compute_builtin_object_size interface also now returns a tree
expression instead of HOST_WIDE_INT, so callers have been adjusted to
account for that.

The trees in object_sizes are each a TREE_VEC with the first element
being the bytes from the pointer to the end of the object and the
second, the size of the whole object.  This allows analysis of negative
offsets, which can now be allowed to the extent of the object bounds.
Tests have been added to verify that it actually works.

gcc/ChangeLog:

	* tree-object-size.h (compute_builtin_object_size): Return tree
	instead of HOST_WIDE_INT.
	* builtins.c (fold_builtin_object_size): Adjust.
	* gimple-fold.c (gimple_fold_builtin_strncat): Likewise.
	* ubsan.c (instrument_object_size): Likewise.
	* tree-object-size.c (object_sizes): Change type to vec<tree>.
	(initval): New function.
	(unknown): Use it.
	(size_unknown_p, size_initval, size_unknown): New functions.
	(object_sizes_unknown_p): Use it.
	(object_sizes_get): Return tree.
	(object_sizes_initialize): Rename from object_sizes_set_force
	and set VAL parameter type as tree.  Add new parameter WHOLEVAL.
	(object_sizes_set): Set VAL parameter type as tree and adjust
	implementation.  Add new parameter WHOLEVAL.
	(size_for_offset): New function.
	(decl_init_size): Adjust comment.
	(addr_object_size): Change PSIZE parameter to tree and adjust
	implementation.  Add new parameter PWHOLESIZE.
	(alloc_object_size): Return tree.
	(compute_builtin_object_size): Return tree in PSIZE.
	(expr_object_size, call_object_size, unknown_object_size):
	Adjust for object_sizes_set change.
	(merge_object_sizes): Drop OFFSET parameter and adjust
	implementation for tree change.
	(plus_stmt_object_size): Call collect_object_sizes_for directly
	instead of merge_object_size and call size_for_offset to get net
	size.
	(cond_expr_object_size, collect_object_sizes_for,
	object_sizes_execute): Adjust for change of type from
	HOST_WIDE_INT to tree.
	(check_for_plus_in_loops_1): Likewise and skip non-positive
	offsets.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-object-size-1.c (test9): New test.
	(main): Call it.
	* gcc.dg/builtin-object-size-2.c (test8): New test.
	(main): Call it.
	* gcc.dg/builtin-object-size-3.c (test9): New test.
	(main): Call it.
	* gcc.dg/builtin-object-size-4.c (test8): New test.
	(main): Call it.
	* gcc.dg/builtin-object-size-5.c (test5, test6, test7): New
	tests.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
Changes from v2:

- Incorporated review suggestions.
- Added support for negative offsets.

 gcc/builtins.c                               |  10 +-
 gcc/gimple-fold.c                            |  11 +-
 gcc/testsuite/gcc.dg/builtin-object-size-1.c |  30 ++
 gcc/testsuite/gcc.dg/builtin-object-size-2.c |  30 ++
 gcc/testsuite/gcc.dg/builtin-object-size-3.c |  31 ++
 gcc/testsuite/gcc.dg/builtin-object-size-4.c |  30 ++
 gcc/testsuite/gcc.dg/builtin-object-size-5.c |  25 ++
 gcc/tree-object-size.c                       | 388 ++++++++++++-------
 gcc/tree-object-size.h                       |   2 +-
 gcc/ubsan.c                                  |   5 +-
 10 files changed, 403 insertions(+), 159 deletions(-)

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 384864bfb3a..50e66692775 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -10226,7 +10226,7 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
 static tree
 fold_builtin_object_size (tree ptr, tree ost)
 {
-  unsigned HOST_WIDE_INT bytes;
+  tree bytes;
   int object_size_type;
 
   if (!validate_arg (ptr, POINTER_TYPE)
@@ -10251,8 +10251,8 @@ fold_builtin_object_size (tree ptr, tree ost)
   if (TREE_CODE (ptr) == ADDR_EXPR)
     {
       compute_builtin_object_size (ptr, object_size_type, &bytes);
-      if (wi::fits_to_tree_p (bytes, size_type_node))
-	return build_int_cstu (size_type_node, bytes);
+      if (int_fits_type_p (bytes, size_type_node))
+	return fold_convert (size_type_node, bytes);
     }
   else if (TREE_CODE (ptr) == SSA_NAME)
     {
@@ -10260,8 +10260,8 @@ fold_builtin_object_size (tree ptr, tree ost)
        later.  Maybe subsequent passes will help determining
        it.  */
       if (compute_builtin_object_size (ptr, object_size_type, &bytes)
-	  && wi::fits_to_tree_p (bytes, size_type_node))
-	return build_int_cstu (size_type_node, bytes);
+	  && int_fits_type_p (bytes, size_type_node))
+	return fold_convert (size_type_node, bytes);
     }
 
   return NULL_TREE;
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 73f090bb14c..b5cfb502633 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -2492,17 +2492,16 @@ gimple_fold_builtin_strncat (gimple_stmt_iterator *gsi)
   if (!src_len || known_lower (stmt, len, src_len, true))
     return false;
 
-  unsigned HOST_WIDE_INT dstsize;
-  bool found_dstsize = compute_builtin_object_size (dst, 1, &dstsize);
-
   /* Warn on constant LEN.  */
   if (TREE_CODE (len) == INTEGER_CST)
     {
       bool nowarn = warning_suppressed_p (stmt, OPT_Wstringop_overflow_);
+      tree dstsize;
 
-      if (!nowarn && found_dstsize)
+      if (!nowarn && compute_builtin_object_size (dst, 1, &dstsize)
+	  && TREE_CODE (dstsize) == INTEGER_CST)
 	{
-	  int cmpdst = compare_tree_int (len, dstsize);
+	  int cmpdst = tree_int_cst_compare (len, dstsize);
 
 	  if (cmpdst >= 0)
 	    {
@@ -2518,7 +2517,7 @@ gimple_fold_builtin_strncat (gimple_stmt_iterator *gsi)
 				   ? G_("%qD specified bound %E equals "
 					"destination size")
 				   : G_("%qD specified bound %E exceeds "
-					"destination size %wu"),
+					"destination size %E"),
 				   fndecl, len, dstsize);
 	      if (nowarn)
 		suppress_warning (stmt, OPT_Wstringop_overflow_);
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
index 8cdae49a6b1..0154f4e9695 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
@@ -424,6 +424,35 @@ test8 (void)
     abort ();
 }
 
+void
+__attribute__ ((noinline))
+test9 (unsigned cond)
+{
+  char *buf2 = malloc (10);
+  char *p;
+
+  if (cond)
+    p = &buf2[8];
+  else
+    p = &buf2[4];
+
+  if (__builtin_object_size (&p[-4], 0) != 10)
+    abort ();
+
+  for (unsigned i = cond; i > 0; i--)
+    p--;
+
+  if (__builtin_object_size (p, 0) != 10)
+    abort ();
+
+  p = &y.c[8];
+  for (unsigned i = cond; i > 0; i--)
+    p--;
+
+  if (__builtin_object_size (p, 0) != sizeof (y))
+    abort ();
+}
+
 int
 main (void)
 {
@@ -437,5 +466,6 @@ main (void)
   test6 (4);
   test7 ();
   test8 ();
+  test9 (1);
   exit (0);
 }
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
index ad2dd296a9a..5cf29291aff 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-2.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
@@ -382,6 +382,35 @@ test7 (void)
     abort ();
 }
 
+void
+__attribute__ ((noinline))
+test8 (unsigned cond)
+{
+  char *buf2 = malloc (10);
+  char *p;
+
+  if (cond)
+    p = &buf2[8];
+  else
+    p = &buf2[4];
+
+  if (__builtin_object_size (&p[-4], 1) != 10)
+    abort ();
+
+  for (unsigned i = cond; i > 0; i--)
+    p--;
+
+  if (__builtin_object_size (p, 1) != 10)
+    abort ();
+
+  p = &y.c[8];
+  for (unsigned i = cond; i > 0; i--)
+    p--;
+
+  if (__builtin_object_size (p, 1) != sizeof (y.c))
+    abort ();
+}
+
 int
 main (void)
 {
@@ -394,5 +423,6 @@ main (void)
   test5 (4);
   test6 ();
   test7 ();
+  test8 (1);
   exit (0);
 }
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
index d5ca5047ee9..3a692c4e3d2 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-3.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
@@ -430,6 +430,36 @@ test8 (void)
     abort ();
 }
 
+void
+__attribute__ ((noinline))
+test9 (unsigned cond)
+{
+  char *buf2 = malloc (10);
+  char *p;
+
+  if (cond)
+    p = &buf2[8];
+  else
+    p = &buf2[4];
+
+  if (__builtin_object_size (&p[-4], 2) != 6)
+    abort ();
+
+  for (unsigned i = cond; i > 0; i--)
+    p--;
+
+  if (__builtin_object_size (p, 2) != 2)
+    abort ();
+
+  p = &y.c[8];
+  for (unsigned i = cond; i > 0; i--)
+    p--;
+
+  if (__builtin_object_size (p, 2)
+      != sizeof (y) - __builtin_offsetof (struct A, c) - 8)
+    abort ();
+}
+
 int
 main (void)
 {
@@ -443,5 +473,6 @@ main (void)
   test6 (4);
   test7 ();
   test8 ();
+  test9 (1);
   exit (0);
 }
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
index 9f159e36a0f..87381620cc9 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-4.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
@@ -395,6 +395,35 @@ test7 (void)
     abort ();
 }
 
+void
+__attribute__ ((noinline))
+test8 (unsigned cond)
+{
+  char *buf2 = malloc (10);
+  char *p;
+
+  if (cond)
+    p = &buf2[8];
+  else
+    p = &buf2[4];
+
+  if (__builtin_object_size (&p[-4], 3) != 6)
+    abort ();
+
+  for (unsigned i = cond; i > 0; i--)
+    p--;
+
+  if (__builtin_object_size (p, 3) != 2)
+    abort ();
+
+  p = &y.c[8];
+  for (unsigned i = cond; i > 0; i--)
+    p--;
+
+  if (__builtin_object_size (p, 3) != sizeof (y.c) - 8)
+    abort ();
+}
+
 int
 main (void)
 {
@@ -407,5 +436,6 @@ main (void)
   test5 (4);
   test6 ();
   test7 ();
+  test8 (1);
   exit (0);
 }
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-5.c b/gcc/testsuite/gcc.dg/builtin-object-size-5.c
index 7c274cdfd42..8e63d9c7a5e 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-5.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-5.c
@@ -53,4 +53,29 @@ test4 (size_t x)
     abort ();
 }
 
+void
+test5 (void)
+{
+  char *p = &buf[0x90000004];
+  if (__builtin_object_size (p + 2, 0) != 0)
+    abort ();
+}
+
+void
+test6 (void)
+{
+  char *p = &buf[-4];
+  if (__builtin_object_size (p + 2, 0) != 0)
+    abort ();
+}
+
+void
+test7 (void)
+{
+  char *buf2 = __builtin_malloc (8);
+  char *p = &buf2[0x90000004];
+  if (__builtin_object_size (p + 2, 0) != 0)
+    abort ();
+}
+
 /* { dg-final { scan-assembler-not "abort" } } */
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index 3780437ff91..1a1e3d56126 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -54,13 +54,12 @@ enum
 
 static tree compute_object_offset (const_tree, const_tree);
 static bool addr_object_size (struct object_size_info *,
-			      const_tree, int, unsigned HOST_WIDE_INT *);
-static unsigned HOST_WIDE_INT alloc_object_size (const gcall *, int);
+			      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 void expr_object_size (struct object_size_info *, tree, tree);
-static bool merge_object_sizes (struct object_size_info *, tree, tree,
-				unsigned HOST_WIDE_INT);
+static bool merge_object_sizes (struct object_size_info *, tree, tree);
 static bool plus_stmt_object_size (struct object_size_info *, tree, gimple *);
 static bool cond_expr_object_size (struct object_size_info *, tree, gimple *);
 static void init_offset_limit (void);
@@ -68,13 +67,17 @@ static void check_for_plus_in_loops (struct object_size_info *, tree);
 static void check_for_plus_in_loops_1 (struct object_size_info *, tree,
 				       unsigned int);
 
-/* object_sizes[0] is upper bound for number of bytes till the end of
-   the object.
-   object_sizes[1] is upper bound for number of bytes till the end of
-   the subobject (innermost array or field with address taken).
-   object_sizes[2] is lower bound for number of bytes till the end of
-   the object and object_sizes[3] lower bound for subobject.  */
-static vec<unsigned HOST_WIDE_INT> object_sizes[OST_END];
+/* Each tree in the object_sizes vector is a TREE_VEC.  The first element is an
+   estimate of the number of bytes till the end of the object and the second
+   element is an estimate of the the whole size of the object.
+
+   object_sizes[0] is upper bound for the object size and number of bytes till
+   the end of the object.
+   object_sizes[1] is upper bound for the object size and number of bytes till
+   the end of the subobject (innermost array or field with address taken).
+   object_sizes[2] is lower bound for the object size and number of bytes till
+   the end of the object and object_sizes[3] lower bound for subobject.  */
+static vec<tree> object_sizes[OST_END];
 
 /* Bitmaps what object sizes have been computed already.  */
 static bitmap computed[OST_END];
@@ -82,10 +85,46 @@ static bitmap computed[OST_END];
 /* Maximum value of offset we consider to be addition.  */
 static unsigned HOST_WIDE_INT offset_limit;
 
+/* Initial value of object sizes; zero for maximum and SIZE_MAX for minimum
+   object size.  */
+
+static inline unsigned HOST_WIDE_INT
+initval (int object_size_type)
+{
+  return (object_size_type & OST_MINIMUM) ? HOST_WIDE_INT_M1U : 0;
+}
+
+/* Unknown object size value; it's the opposite of initval.  */
+
 static inline unsigned HOST_WIDE_INT
 unknown (int object_size_type)
 {
-  return ((unsigned HOST_WIDE_INT) -((object_size_type >> 1) ^ 1));
+  return ~initval (object_size_type);
+}
+
+/* Return true if VAL is represents an unknown size for OBJECT_SIZE_TYPE.  */
+
+static inline bool
+size_unknown_p (tree val, int object_size_type)
+{
+  return (tree_fits_uhwi_p (val)
+	  && tree_to_uhwi (val) == unknown (object_size_type));
+}
+
+/* Return a tree with initial value for OBJECT_SIZE_TYPE.  */
+
+static inline tree
+size_initval (int object_size_type)
+{
+  return size_int (initval (object_size_type));
+}
+
+/* Return a tree with unknown value for OBJECT_SIZE_TYPE.  */
+
+static inline tree
+size_unknown (int object_size_type)
+{
+  return size_int (unknown (object_size_type));
 }
 
 /* Grow object_sizes[OBJECT_SIZE_TYPE] to num_ssa_names.  */
@@ -110,47 +149,56 @@ object_sizes_release (int object_size_type)
 static inline bool
 object_sizes_unknown_p (int object_size_type, unsigned varno)
 {
-  return (object_sizes[object_size_type][varno]
-	  == unknown (object_size_type));
+  return size_unknown_p (object_sizes[object_size_type][varno],
+			 object_size_type);
 }
 
-/* Return size for VARNO corresponding to OSI.  */
+/* Return size for VARNO corresponding to OSI.  If WHOLE is true, return the
+   whole object size.  */
 
-static inline unsigned HOST_WIDE_INT
-object_sizes_get (struct object_size_info *osi, unsigned varno)
+static inline tree
+object_sizes_get (struct object_size_info *osi, unsigned varno,
+		  bool whole = false)
 {
-  return object_sizes[osi->object_size_type][varno];
+  return TREE_VEC_ELT (object_sizes[osi->object_size_type][varno], whole);
 }
 
 /* Set size for VARNO corresponding to OSI to VAL.  */
 
-static inline bool
-object_sizes_set_force (struct object_size_info *osi, unsigned varno,
-			unsigned HOST_WIDE_INT val)
+static inline void
+object_sizes_initialize (struct object_size_info *osi, unsigned varno,
+			 tree val, tree wholeval)
 {
-  object_sizes[osi->object_size_type][varno] = val;
-  return true;
+  int object_size_type = osi->object_size_type;
+
+  tree res = make_tree_vec (2);
+  TREE_VEC_ELT (res, 0) = val;
+  TREE_VEC_ELT (res, 1) = wholeval;
+
+  object_sizes[object_size_type][varno] = res;
 }
 
 /* Set size for VARNO corresponding to OSI to VAL if it is the new minimum or
    maximum.  */
 
 static inline bool
-object_sizes_set (struct object_size_info *osi, unsigned varno,
-		  unsigned HOST_WIDE_INT val)
+object_sizes_set (struct object_size_info *osi, unsigned varno, tree val,
+		  tree wholeval)
 {
   int object_size_type = osi->object_size_type;
-  if ((object_size_type & OST_MINIMUM) == 0)
-    {
-      if (object_sizes[object_size_type][varno] < val)
-	return object_sizes_set_force (osi, varno, val);
-    }
-  else
-    {
-      if (object_sizes[object_size_type][varno] > val)
-	return object_sizes_set_force (osi, varno, val);
-    }
-  return false;
+  tree size = object_sizes[object_size_type][varno];
+
+  tree oldval = TREE_VEC_ELT (size, 0);
+  tree old_wholeval = TREE_VEC_ELT (size, 1);
+
+  enum tree_code code = object_size_type & OST_MINIMUM ? MIN_EXPR : MAX_EXPR;
+
+  val = size_binop (code, val, oldval);
+  wholeval = size_binop (code, wholeval, old_wholeval);
+
+  TREE_VEC_ELT (size, 0) = val;
+  TREE_VEC_ELT (size, 1) = wholeval;
+  return tree_int_cst_compare (oldval, val) != 0;
 }
 
 /* Initialize OFFSET_LIMIT variable.  */
@@ -164,6 +212,48 @@ init_offset_limit (void)
   offset_limit /= 2;
 }
 
+/* Bytes at end of the object with SZ from offset OFFSET.  If WHOLESIZE is not
+   NULL_TREE, use it to get the net offset of the pointer, which should always
+   be positive and hence, be within OFFSET_LIMIT for valid offsets.  */
+
+static tree
+size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE)
+{
+  gcc_checking_assert (TREE_CODE (offset) == INTEGER_CST);
+  gcc_checking_assert (TREE_CODE (sz) == INTEGER_CST);
+  gcc_checking_assert (types_compatible_p (TREE_TYPE (sz), sizetype));
+
+  /* For negative offsets, if we have a distinct WHOLESIZE, use it to get a net
+     offset from the whole object.  */
+  if (wholesize && tree_int_cst_compare (sz, wholesize))
+    {
+      gcc_checking_assert (TREE_CODE (wholesize) == INTEGER_CST);
+      gcc_checking_assert (types_compatible_p (TREE_TYPE (wholesize),
+					       sizetype));
+
+      /* Restructure SZ - OFFSET as
+	 WHOLESIZE - (WHOLESIZE + OFFSET - SZ) so that the offset part, i.e.
+	 WHOLESIZE + OFFSET - SZ is only allowed to be positive.  */
+      tree tmp = size_binop (MAX_EXPR, wholesize, sz);
+      offset = fold_build2 (PLUS_EXPR, sizetype, tmp, offset);
+      offset = fold_build2 (MINUS_EXPR, sizetype, offset, sz);
+      sz = tmp;
+    }
+
+  /* Safe to convert now, since a valid net offset should be non-negative.  */
+  if (!types_compatible_p (TREE_TYPE (offset), sizetype))
+    fold_convert (sizetype, offset);
+
+  if (integer_zerop (offset))
+    return sz;
+
+  /* Negative or too large offset even after adjustment, cannot be within
+     bounds of an object.  */
+  if (compare_tree_int (offset, offset_limit) > 0)
+    return size_zero_node;
+
+  return size_binop (MINUS_EXPR, size_binop (MAX_EXPR, sz, offset), offset);
+}
 
 /* Compute offset of EXPR within VAR.  Return error_mark_node
    if unknown.  */
@@ -274,19 +364,22 @@ decl_init_size (tree decl, bool min)
 
 /* Compute __builtin_object_size for PTR, which is a ADDR_EXPR.
    OBJECT_SIZE_TYPE is the second argument from __builtin_object_size.
-   If unknown, return unknown (object_size_type).  */
+   If unknown, return size_unknown (object_size_type).  */
 
 static bool
 addr_object_size (struct object_size_info *osi, const_tree ptr,
-		  int object_size_type, unsigned HOST_WIDE_INT *psize)
+		  int object_size_type, tree *psize, tree *pwholesize)
 {
-  tree pt_var, pt_var_size = NULL_TREE, var_size, bytes;
+  tree pt_var, pt_var_size = NULL_TREE, pt_var_wholesize = NULL_TREE;
+  tree var_size, bytes, wholebytes;
 
   gcc_assert (TREE_CODE (ptr) == ADDR_EXPR);
 
   /* Set to unknown and overwrite just before returning if the size
      could be determined.  */
-  *psize = unknown (object_size_type);
+  *psize = size_unknown (object_size_type);
+  if (pwholesize)
+    *pwholesize = size_unknown (object_size_type);
 
   pt_var = TREE_OPERAND (ptr, 0);
   while (handled_component_p (pt_var))
@@ -297,13 +390,14 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 
   if (TREE_CODE (pt_var) == MEM_REF)
     {
-      unsigned HOST_WIDE_INT sz;
+      tree sz, wholesize;
 
       if (!osi || (object_size_type & OST_SUBOBJECT) != 0
 	  || TREE_CODE (TREE_OPERAND (pt_var, 0)) != SSA_NAME)
 	{
 	  compute_builtin_object_size (TREE_OPERAND (pt_var, 0),
 				       object_size_type & ~OST_SUBOBJECT, &sz);
+	  wholesize = sz;
 	}
       else
 	{
@@ -312,46 +406,47 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 	    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));
+	    {
+	      sz = object_sizes_get (osi, SSA_NAME_VERSION (var));
+	      wholesize = object_sizes_get (osi, SSA_NAME_VERSION (var), true);
+	    }
 	  else
-	    sz = unknown (object_size_type);
+	    sz = wholesize = size_unknown (object_size_type);
 	}
-      if (sz != unknown (object_size_type))
+      if (!size_unknown_p (sz, object_size_type))
 	{
-	  offset_int mem_offset;
-	  if (mem_ref_offset (pt_var).is_constant (&mem_offset))
-	    {
-	      offset_int dsz = wi::sub (sz, mem_offset);
-	      if (wi::neg_p (dsz))
-		sz = 0;
-	      else if (wi::fits_uhwi_p (dsz))
-		sz = dsz.to_uhwi ();
-	      else
-		sz = unknown (object_size_type);
-	    }
+	  tree offset = TREE_OPERAND (pt_var, 1);
+	  if (TREE_CODE (offset) != INTEGER_CST
+	      || TREE_CODE (sz) != INTEGER_CST)
+	    sz = wholesize = size_unknown (object_size_type);
 	  else
-	    sz = unknown (object_size_type);
+	    sz = size_for_offset (sz, offset, wholesize);
 	}
 
-      if (sz != unknown (object_size_type) && sz < offset_limit)
-	pt_var_size = size_int (sz);
+      if (!size_unknown_p (sz, object_size_type)
+	  && TREE_CODE (sz) == INTEGER_CST
+	  && compare_tree_int (sz, offset_limit) < 0)
+	{
+	  pt_var_size = sz;
+	  pt_var_wholesize = wholesize;
+	}
     }
   else if (DECL_P (pt_var))
     {
-      pt_var_size = decl_init_size (pt_var, object_size_type & OST_MINIMUM);
+      pt_var_size = pt_var_wholesize
+	= decl_init_size (pt_var, object_size_type & OST_MINIMUM);
       if (!pt_var_size)
 	return false;
     }
   else if (TREE_CODE (pt_var) == STRING_CST)
-    pt_var_size = TYPE_SIZE_UNIT (TREE_TYPE (pt_var));
+    pt_var_size = pt_var_wholesize = TYPE_SIZE_UNIT (TREE_TYPE (pt_var));
   else
     return false;
 
   if (pt_var_size)
     {
       /* Validate the size determined above.  */
-      if (!tree_fits_uhwi_p (pt_var_size)
-	  || tree_to_uhwi (pt_var_size) >= offset_limit)
+      if (compare_tree_int (pt_var_size, offset_limit) >= 0)
 	return false;
     }
 
@@ -496,28 +591,35 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 	      bytes = size_binop (MIN_EXPR, bytes, bytes2);
 	    }
 	}
+
+      wholebytes
+	= object_size_type & OST_SUBOBJECT ? var_size : pt_var_wholesize;
     }
   else if (!pt_var_size)
     return false;
   else
-    bytes = pt_var_size;
-
-  if (tree_fits_uhwi_p (bytes))
     {
-      *psize = tree_to_uhwi (bytes);
-      return true;
+      bytes = pt_var_size;
+      wholebytes = pt_var_wholesize;
     }
 
-  return false;
+  if (TREE_CODE (bytes) != INTEGER_CST
+      || TREE_CODE (wholebytes) != INTEGER_CST)
+    return false;
+
+  *psize = bytes;
+  if (pwholesize)
+    *pwholesize = wholebytes;
+  return true;
 }
 
 
 /* Compute __builtin_object_size for CALL, which is a GIMPLE_CALL.
    Handles calls to functions declared with attribute alloc_size.
    OBJECT_SIZE_TYPE is the second argument from __builtin_object_size.
-   If unknown, return unknown (object_size_type).  */
+   If unknown, return size_unknown (object_size_type).  */
 
-static unsigned HOST_WIDE_INT
+static tree
 alloc_object_size (const gcall *call, int object_size_type)
 {
   gcc_assert (is_gimple_call (call));
@@ -529,7 +631,7 @@ alloc_object_size (const gcall *call, int object_size_type)
     calltype = gimple_call_fntype (call);
 
   if (!calltype)
-    return unknown (object_size_type);
+    return size_unknown (object_size_type);
 
   /* Set to positions of alloc_size arguments.  */
   int arg1 = -1, arg2 = -1;
@@ -549,7 +651,7 @@ alloc_object_size (const gcall *call, int object_size_type)
       || (arg2 >= 0
 	  && (arg2 >= (int)gimple_call_num_args (call)
 	      || TREE_CODE (gimple_call_arg (call, arg2)) != INTEGER_CST)))
-    return unknown (object_size_type);
+    return size_unknown (object_size_type);
 
   tree bytes = NULL_TREE;
   if (arg2 >= 0)
@@ -559,10 +661,7 @@ alloc_object_size (const gcall *call, int object_size_type)
   else if (arg1 >= 0)
     bytes = fold_convert (sizetype, gimple_call_arg (call, arg1));
 
-  if (bytes && tree_fits_uhwi_p (bytes))
-    return tree_to_uhwi (bytes);
-
-  return unknown (object_size_type);
+  return bytes;
 }
 
 
@@ -598,13 +697,13 @@ pass_through_call (const gcall *call)
 
 bool
 compute_builtin_object_size (tree ptr, int object_size_type,
-			     unsigned HOST_WIDE_INT *psize)
+			     tree *psize)
 {
   gcc_assert (object_size_type >= 0 && object_size_type < OST_END);
 
   /* Set to unknown and overwrite just before returning if the size
      could be determined.  */
-  *psize = unknown (object_size_type);
+  *psize = size_unknown (object_size_type);
 
   if (! offset_limit)
     init_offset_limit ();
@@ -638,8 +737,7 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 						  psize))
 		{
 		  /* Return zero when the offset is out of bounds.  */
-		  unsigned HOST_WIDE_INT off = tree_to_shwi (offset);
-		  *psize = off < *psize ? *psize - off : 0;
+		  *psize = size_for_offset (*psize, offset);
 		  return true;
 		}
 	    }
@@ -747,12 +845,13 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 		print_generic_expr (dump_file, ssa_name (i),
 				    dump_flags);
 		fprintf (dump_file,
-			 ": %s %sobject size "
-			 HOST_WIDE_INT_PRINT_UNSIGNED "\n",
+			 ": %s %sobject size ",
 			 ((object_size_type & OST_MINIMUM) ? "minimum"
 			  : "maximum"),
-			 (object_size_type & OST_SUBOBJECT) ? "sub" : "",
-			 object_sizes_get (&osi, i));
+			 (object_size_type & OST_SUBOBJECT) ? "sub" : "");
+		print_generic_expr (dump_file, object_sizes_get (&osi, i),
+				    dump_flags);
+		fprintf (dump_file, "\n");
 	      }
 	}
 
@@ -761,7 +860,7 @@ compute_builtin_object_size (tree ptr, int object_size_type,
     }
 
   *psize = object_sizes_get (&osi, SSA_NAME_VERSION (ptr));
-  return *psize != unknown (object_size_type);
+  return !size_unknown_p (*psize, object_size_type);
 }
 
 /* Compute object_sizes for PTR, defined to VALUE, which is not an SSA_NAME.  */
@@ -771,7 +870,7 @@ expr_object_size (struct object_size_info *osi, tree ptr, tree value)
 {
   int object_size_type = osi->object_size_type;
   unsigned int varno = SSA_NAME_VERSION (ptr);
-  unsigned HOST_WIDE_INT bytes;
+  tree bytes, wholesize;
 
   gcc_assert (!object_sizes_unknown_p (object_size_type, varno));
   gcc_assert (osi->pass == 0);
@@ -784,11 +883,11 @@ expr_object_size (struct object_size_info *osi, tree ptr, tree value)
 	      || !POINTER_TYPE_P (TREE_TYPE (value)));
 
   if (TREE_CODE (value) == ADDR_EXPR)
-    addr_object_size (osi, value, object_size_type, &bytes);
+    addr_object_size (osi, value, object_size_type, &bytes, &wholesize);
   else
-    bytes = unknown (object_size_type);
+    bytes = wholesize = size_unknown (object_size_type);
 
-  object_sizes_set (osi, varno, bytes);
+  object_sizes_set (osi, varno, bytes, wholesize);
 }
 
 
@@ -799,16 +898,14 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call)
 {
   int object_size_type = osi->object_size_type;
   unsigned int varno = SSA_NAME_VERSION (ptr);
-  unsigned HOST_WIDE_INT bytes;
 
   gcc_assert (is_gimple_call (call));
 
   gcc_assert (!object_sizes_unknown_p (object_size_type, varno));
   gcc_assert (osi->pass == 0);
+  tree bytes = alloc_object_size (call, object_size_type);
 
-  bytes = alloc_object_size (call, object_size_type);
-
-  object_sizes_set (osi, varno, bytes);
+  object_sizes_set (osi, varno, bytes, bytes);
 }
 
 
@@ -822,8 +919,9 @@ unknown_object_size (struct object_size_info *osi, tree ptr)
 
   gcc_checking_assert (!object_sizes_unknown_p (object_size_type, varno));
   gcc_checking_assert (osi->pass == 0);
+  tree bytes = size_unknown (object_size_type);
 
-  object_sizes_set (osi, varno, unknown (object_size_type));
+  object_sizes_set (osi, varno, bytes, bytes);
 }
 
 
@@ -831,30 +929,22 @@ unknown_object_size (struct object_size_info *osi, tree ptr)
    the object size might need reexamination later.  */
 
 static bool
-merge_object_sizes (struct object_size_info *osi, tree dest, tree orig,
-		    unsigned HOST_WIDE_INT offset)
+merge_object_sizes (struct object_size_info *osi, tree dest, tree orig)
 {
   int object_size_type = osi->object_size_type;
   unsigned int varno = SSA_NAME_VERSION (dest);
-  unsigned HOST_WIDE_INT orig_bytes;
+  tree orig_bytes, wholesize;
 
   if (object_sizes_unknown_p (object_size_type, varno))
     return false;
-  if (offset >= offset_limit)
-    {
-      object_sizes_set (osi, varno, unknown (object_size_type));
-      return false;
-    }
 
   if (osi->pass == 0)
     collect_object_sizes_for (osi, orig);
 
   orig_bytes = object_sizes_get (osi, SSA_NAME_VERSION (orig));
-  if (orig_bytes != unknown (object_size_type))
-    orig_bytes = (offset > orig_bytes)
-		 ? HOST_WIDE_INT_0U : orig_bytes - offset;
+  wholesize = object_sizes_get (osi, SSA_NAME_VERSION (orig), true);
 
-  osi->changed = object_sizes_set (osi, varno, orig_bytes);
+  osi->changed = object_sizes_set (osi, varno, orig_bytes, wholesize);
 
   return bitmap_bit_p (osi->reexamine, SSA_NAME_VERSION (orig));
 }
@@ -869,8 +959,9 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
 {
   int object_size_type = osi->object_size_type;
   unsigned int varno = SSA_NAME_VERSION (var);
-  unsigned HOST_WIDE_INT bytes;
+  tree bytes, wholesize;
   tree op0, op1;
+  bool reexamine = false;
 
   if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
     {
@@ -895,31 +986,37 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
       && (TREE_CODE (op0) == SSA_NAME
 	  || TREE_CODE (op0) == ADDR_EXPR))
     {
-      if (! tree_fits_uhwi_p (op1))
-	bytes = unknown (object_size_type);
-      else if (TREE_CODE (op0) == SSA_NAME)
-	return merge_object_sizes (osi, var, op0, tree_to_uhwi (op1));
+      if (TREE_CODE (op0) == SSA_NAME)
+	{
+	  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
 	{
-	  unsigned HOST_WIDE_INT off = tree_to_uhwi (op1);
-
-          /* op0 will be ADDR_EXPR here.  */
-	  addr_object_size (osi, op0, object_size_type, &bytes);
-	  if (bytes == unknown (object_size_type))
-	    ;
-	  else if (off > offset_limit)
-	    bytes = unknown (object_size_type);
-	  else if (off > bytes)
-	    bytes = 0;
-	  else
-	    bytes -= off;
+	  /* 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);
     }
   else
-    bytes = unknown (object_size_type);
+    bytes = wholesize = size_unknown (object_size_type);
 
-  object_sizes_set (osi, varno, bytes);
-  return false;
+  osi->changed = object_sizes_set (osi, varno, bytes, wholesize);
+  return reexamine;
 }
 
 
@@ -944,7 +1041,7 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   else_ = gimple_assign_rhs3 (stmt);
 
   if (TREE_CODE (then_) == SSA_NAME)
-    reexamine |= merge_object_sizes (osi, var, then_, 0);
+    reexamine |= merge_object_sizes (osi, var, then_);
   else
     expr_object_size (osi, var, then_);
 
@@ -952,7 +1049,7 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
     return reexamine;
 
   if (TREE_CODE (else_) == SSA_NAME)
-    reexamine |= merge_object_sizes (osi, var, else_, 0);
+    reexamine |= merge_object_sizes (osi, var, else_);
   else
     expr_object_size (osi, var, else_);
 
@@ -969,11 +1066,11 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
    object size is object size of the first operand minus the constant.
    If the constant is bigger than the number of remaining bytes until the
    end of the object, object size is 0, but if it is instead a pointer
-   subtraction, object size is unknown (object_size_type).
+   subtraction, object size is size_unknown (object_size_type).
    To differentiate addition from subtraction, ADDR_EXPR returns
-   unknown (object_size_type) for all objects bigger than half of the address
-   space, and constants less than half of the address space are considered
-   addition, while bigger constants subtraction.
+   size_unknown (object_size_type) for all objects bigger than half of the
+   address space, and constants less than half of the address space are
+   considered addition, while bigger constants subtraction.
    For a memcpy like GIMPLE_CALL that always returns one of its arguments, the
    object size is object size of that argument.
    Otherwise, object size is the maximum of object sizes of variables
@@ -996,8 +1093,9 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 	{
 	  /* Initialize to 0 for maximum size and M1U for minimum size so that
 	     it gets immediately overridden.  */
-	  object_sizes_set_force (osi, varno,
-				  unknown (object_size_type ^ OST_MINIMUM));
+	  object_sizes_initialize (osi, varno,
+				   size_initval (object_size_type),
+				   size_initval (object_size_type));
 	}
       else
 	{
@@ -1040,7 +1138,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
           {
             if (TREE_CODE (rhs) == SSA_NAME
                 && POINTER_TYPE_P (TREE_TYPE (rhs)))
-              reexamine = merge_object_sizes (osi, var, rhs, 0);
+	      reexamine = merge_object_sizes (osi, var, rhs);
             else
               expr_object_size (osi, var, rhs);
           }
@@ -1057,7 +1155,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
           {
             if (TREE_CODE (arg) == SSA_NAME
                 && POINTER_TYPE_P (TREE_TYPE (arg)))
-              reexamine = merge_object_sizes (osi, var, arg, 0);
+	      reexamine = merge_object_sizes (osi, var, arg);
             else
               expr_object_size (osi, var, arg);
           }
@@ -1068,7 +1166,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 
     case GIMPLE_ASM:
       /* Pointers defined by __asm__ statements can point anywhere.  */
-      object_sizes_set (osi, varno, unknown (object_size_type));
+      unknown_object_size (osi, var);
       break;
 
     case GIMPLE_NOP:
@@ -1077,7 +1175,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 	expr_object_size (osi, var, SSA_NAME_VAR (var));
       else
 	/* Uninitialized SSA names point nowhere.  */
-	object_sizes_set (osi, varno, unknown (object_size_type));
+	unknown_object_size (osi, var);
       break;
 
     case GIMPLE_PHI:
@@ -1092,7 +1190,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 	      break;
 
 	    if (TREE_CODE (rhs) == SSA_NAME)
-	      reexamine |= merge_object_sizes (osi, var, rhs, 0);
+	      reexamine |= merge_object_sizes (osi, var, rhs);
 	    else if (osi->pass == 0)
 	      expr_object_size (osi, var, rhs);
 	  }
@@ -1143,7 +1241,8 @@ check_for_plus_in_loops_1 (struct object_size_info *osi, tree var,
 	      --sp;
 	      bitmap_clear_bit (osi->reexamine, *sp);
 	      bitmap_set_bit (computed[osi->object_size_type], *sp);
-	      object_sizes_set_force (osi, *sp, 0);
+	      object_sizes_set (osi, *sp, size_zero_node,
+				object_sizes_get (osi, *sp, true));
 	      if (*sp == varno)
 		break;
 	    }
@@ -1243,7 +1342,8 @@ check_for_plus_in_loops (struct object_size_info *osi, tree var)
 
       gcc_assert (TREE_CODE (cst) == INTEGER_CST);
 
-      if (integer_zerop (cst))
+      /* Skip non-positive offsets.  */
+      if (integer_zerop (cst) || compare_tree_int (cst, offset_limit) > 0)
         return;
 
       osi->depths[SSA_NAME_VERSION (basevar)] = 1;
@@ -1334,17 +1434,17 @@ object_sizes_execute (function *fun, bool insert_min_max_p)
 			  || TREE_CODE (ptr) == SSA_NAME))
 		    {
 		      tree type = TREE_TYPE (lhs);
-		      unsigned HOST_WIDE_INT bytes;
+		      tree bytes;
 		      if (compute_builtin_object_size (ptr, object_size_type,
 						       &bytes)
-			  && wi::fits_to_tree_p (bytes, type))
+			  && int_fits_type_p (bytes, type))
 			{
 			  tree tem = make_ssa_name (type);
 			  gimple_call_set_lhs (call, tem);
 			  enum tree_code code
 			    = (object_size_type & OST_MINIMUM
 			       ? MAX_EXPR : MIN_EXPR);
-			  tree cst = build_int_cstu (type, bytes);
+			  tree cst = fold_convert (type, bytes);
 			  gimple *g
 			    = gimple_build_assign (lhs, code, tem, cst);
 			  gsi_insert_after (&i, g, GSI_NEW_STMT);
diff --git a/gcc/tree-object-size.h b/gcc/tree-object-size.h
index ef18aea50db..b2d6a58324c 100644
--- a/gcc/tree-object-size.h
+++ b/gcc/tree-object-size.h
@@ -22,7 +22,7 @@ along with GCC; see the file COPYING3.  If not see
 
 extern void init_object_sizes (void);
 extern void fini_object_sizes (void);
-extern bool compute_builtin_object_size (tree, int, unsigned HOST_WIDE_INT *);
+extern bool compute_builtin_object_size (tree, int, tree *);
 extern tree decl_init_size (tree, bool);
 
 #endif  // GCC_TREE_OBJECT_SIZE_H
diff --git a/gcc/ubsan.c b/gcc/ubsan.c
index ba9adf0ad96..364cf174bf0 100644
--- a/gcc/ubsan.c
+++ b/gcc/ubsan.c
@@ -2160,9 +2160,8 @@ instrument_object_size (gimple_stmt_iterator *gsi, tree t, bool is_lhs)
   if (decl_p)
     base_addr = build1 (ADDR_EXPR,
 			build_pointer_type (TREE_TYPE (base)), base);
-  unsigned HOST_WIDE_INT size;
-  if (compute_builtin_object_size (base_addr, 0, &size))
-    sizet = build_int_cst (sizetype, size);
+  if (compute_builtin_object_size (base_addr, 0, &sizet))
+    ;
   else if (optimize)
     {
       if (LOCATION_LOCUS (loc) == UNKNOWN_LOCATION)
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v3 4/8] __builtin_dynamic_object_size: Recognize builtin
  2021-11-26  5:28 ` [PATCH v3 0/8] __builtin_dynamic_object_size Siddhesh Poyarekar
                     ` (2 preceding siblings ...)
  2021-11-26  5:28   ` [PATCH v3 3/8] tree-object-size: Save sizes as trees and support negative offsets Siddhesh Poyarekar
@ 2021-11-26  5:28   ` Siddhesh Poyarekar
  2021-11-26  5:28   ` [PATCH v3 5/8] tree-object-size: Support dynamic sizes in conditions Siddhesh Poyarekar
                     ` (4 subsequent siblings)
  8 siblings, 0 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-26  5:28 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Recognize the __builtin_dynamic_object_size builtin and add paths in the
object size path to deal with it, but treat it like
__builtin_object_size for now.  Also add tests to provide the same
testing coverage for the new builtin name.

gcc/ChangeLog:

	* builtins.def (BUILT_IN_DYNAMIC_OBJECT_SIZE): New builtin.
	* tree-object-size.h: Move object size type bits enum from
	tree-object-size.c and add new value OST_DYNAMIC.
	* builtins.c (expand_builtin, fold_builtin_2): Handle it.
	(fold_builtin_object_size): Handle new builtin and adjust for
	change to compute_builtin_object_size.
	* tree-object-size.c: Include builtins.h.
	(compute_builtin_object_size): Adjust.
	(early_object_sizes_execute_one,
	dynamic_object_sizes_execute_one): New functions.
	(object_sizes_execute): Rename insert_min_max_p argument to
	early.  Handle BUILT_IN_DYNAMIC_OBJECT_SIZE and call the new
	functions.
	doc/extend.texi (__builtin_dynamic_object_size): Document new
	builtin.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/builtin-dynamic-object-size1.C: New test.
	* g++.dg/ext/builtin-dynamic-object-size2.C: Likewise.
	* gcc.dg/builtin-dynamic-alloc-size.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-1.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-10.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-11.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-12.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-13.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-14.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-15.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-16.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-17.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-18.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-19.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-2.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-3.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-4.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-5.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-6.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-7.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-8.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-9.c: Likewise.
	* gcc.dg/builtin-object-size-16.c: Adjust to allow inclusion
	from builtin-dynamic-object-size-16.c.
	* gcc.dg/builtin-object-size-17.c: Likewise.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
Changes from v2:

- Incorporated review suggestions.

 gcc/builtins.c                                |  11 +-
 gcc/builtins.def                              |   1 +
 gcc/doc/extend.texi                           |  13 ++
 .../g++.dg/ext/builtin-dynamic-object-size1.C |   5 +
 .../g++.dg/ext/builtin-dynamic-object-size2.C |   5 +
 .../gcc.dg/builtin-dynamic-alloc-size.c       |   7 +
 .../gcc.dg/builtin-dynamic-object-size-1.c    |   6 +
 .../gcc.dg/builtin-dynamic-object-size-10.c   |   9 ++
 .../gcc.dg/builtin-dynamic-object-size-11.c   |   7 +
 .../gcc.dg/builtin-dynamic-object-size-12.c   |   5 +
 .../gcc.dg/builtin-dynamic-object-size-13.c   |   5 +
 .../gcc.dg/builtin-dynamic-object-size-14.c   |   5 +
 .../gcc.dg/builtin-dynamic-object-size-15.c   |   5 +
 .../gcc.dg/builtin-dynamic-object-size-16.c   |   6 +
 .../gcc.dg/builtin-dynamic-object-size-17.c   |   7 +
 .../gcc.dg/builtin-dynamic-object-size-18.c   |   8 +
 .../gcc.dg/builtin-dynamic-object-size-19.c   | 104 ++++++++++++
 .../gcc.dg/builtin-dynamic-object-size-2.c    |   6 +
 .../gcc.dg/builtin-dynamic-object-size-3.c    |   6 +
 .../gcc.dg/builtin-dynamic-object-size-4.c    |   6 +
 .../gcc.dg/builtin-dynamic-object-size-5.c    |   7 +
 .../gcc.dg/builtin-dynamic-object-size-6.c    |   5 +
 .../gcc.dg/builtin-dynamic-object-size-7.c    |   5 +
 .../gcc.dg/builtin-dynamic-object-size-8.c    |   5 +
 .../gcc.dg/builtin-dynamic-object-size-9.c    |   5 +
 gcc/testsuite/gcc.dg/builtin-object-size-16.c |   2 +
 gcc/testsuite/gcc.dg/builtin-object-size-17.c |   2 +
 gcc/tree-object-size.c                        | 152 +++++++++++++-----
 gcc/tree-object-size.h                        |  10 ++
 29 files changed, 378 insertions(+), 42 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size1.C
 create mode 100644 gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size2.C
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-alloc-size.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-11.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-12.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-13.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-14.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-15.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-16.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-17.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-18.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-19.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-6.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-7.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-8.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-9.c

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 50e66692775..573f7e9b9df 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -178,7 +178,7 @@ static rtx expand_builtin_memory_chk (tree, rtx, machine_mode,
 				      enum built_in_function);
 static void maybe_emit_chk_warning (tree, enum built_in_function);
 static void maybe_emit_sprintf_chk_warning (tree, enum built_in_function);
-static tree fold_builtin_object_size (tree, tree);
+static tree fold_builtin_object_size (tree, tree, enum built_in_function);
 
 unsigned HOST_WIDE_INT target_newline;
 unsigned HOST_WIDE_INT target_percent;
@@ -7880,6 +7880,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
       return const0_rtx;
 
     case BUILT_IN_OBJECT_SIZE:
+    case BUILT_IN_DYNAMIC_OBJECT_SIZE:
       return expand_builtin_object_size (exp);
 
     case BUILT_IN_MEMCPY_CHK:
@@ -9286,7 +9287,8 @@ fold_builtin_2 (location_t loc, tree expr, tree fndecl, tree arg0, tree arg1)
       break;
 
     case BUILT_IN_OBJECT_SIZE:
-      return fold_builtin_object_size (arg0, arg1);
+    case BUILT_IN_DYNAMIC_OBJECT_SIZE:
+      return fold_builtin_object_size (arg0, arg1, fcode);
 
     case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE:
       return fold_builtin_atomic_always_lock_free (arg0, arg1);
@@ -10224,7 +10226,7 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
    if possible.  */
 
 static tree
-fold_builtin_object_size (tree ptr, tree ost)
+fold_builtin_object_size (tree ptr, tree ost, enum built_in_function fcode)
 {
   tree bytes;
   int object_size_type;
@@ -10248,6 +10250,9 @@ fold_builtin_object_size (tree ptr, tree ost)
   if (TREE_SIDE_EFFECTS (ptr))
     return build_int_cst_type (size_type_node, object_size_type < 2 ? -1 : 0);
 
+  if (fcode == BUILT_IN_DYNAMIC_OBJECT_SIZE)
+    object_size_type |= OST_DYNAMIC;
+
   if (TREE_CODE (ptr) == ADDR_EXPR)
     {
       compute_builtin_object_size (ptr, object_size_type, &bytes);
diff --git a/gcc/builtins.def b/gcc/builtins.def
index 45a09b4d42d..a92948de2b8 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -972,6 +972,7 @@ DEF_BUILTIN_STUB (BUILT_IN_STRNCMP_EQ, "__builtin_strncmp_eq")
 
 /* Object size checking builtins.  */
 DEF_GCC_BUILTIN	       (BUILT_IN_OBJECT_SIZE, "object_size", BT_FN_SIZE_CONST_PTR_INT, ATTR_CONST_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN	       (BUILT_IN_DYNAMIC_OBJECT_SIZE, "dynamic_object_size", BT_FN_SIZE_CONST_PTR_INT, ATTR_PURE_NOTHROW_LEAF_LIST)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_MEMCPY_CHK, "__memcpy_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_NOTHROW_NONNULL_LEAF)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_MEMMOVE_CHK, "__memmove_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_NOTHROW_NONNULL_LEAF)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_MEMPCPY_CHK, "__mempcpy_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_RETNONNULL_NOTHROW_LEAF)
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index ef654d7b878..7bb3c5b3410 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -12712,6 +12712,7 @@ __atomic_store_n(&lockvar, 0, __ATOMIC_RELEASE|__ATOMIC_HLE_RELEASE);
 @node Object Size Checking
 @section Object Size Checking Built-in Functions
 @findex __builtin_object_size
+@findex __builtin_dynamic_object_size
 @findex __builtin___memcpy_chk
 @findex __builtin___mempcpy_chk
 @findex __builtin___memmove_chk
@@ -12779,6 +12780,18 @@ assert (__builtin_object_size (q, 1) == sizeof (var.b));
 @end smallexample
 @end deftypefn
 
+@deftypefn {Built-in Function} {size_t} __builtin_dynamic_object_size (const void * @var{ptr}, int @var{type})
+is similar to @code{__builtin_object_size} in that it returns a number of bytes
+from @var{ptr} to the end of the object @var{ptr} pointer points to, except
+that the size returned may not be a constant.  This results in successful
+evaluation of object size estimates in a wider range of use cases and can be
+more precise than @code{__builtin_object_size}, but it incurs a performance
+penalty since it may add a runtime overhead on size computation.  Semantics of
+@var{type} as well as return values in case it is not possible to determine
+which objects @var{ptr} points to at compile time are the same as in the case
+of @code{__builtin_object_size}.
+@end deftypefn
+
 There are built-in functions added for many common string operation
 functions, e.g., for @code{memcpy} @code{__builtin___memcpy_chk}
 built-in is provided.  This built-in has an additional last argument,
diff --git a/gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size1.C b/gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size1.C
new file mode 100644
index 00000000000..b11ac200751
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size1.C
@@ -0,0 +1,5 @@
+// { dg-do run }
+// { dg-options "-O2" }
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size1.C"
diff --git a/gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size2.C b/gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size2.C
new file mode 100644
index 00000000000..6e52cf38533
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size2.C
@@ -0,0 +1,5 @@
+// { dg-do run }
+// { dg-options "-O2" }
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size2.C"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-alloc-size.c b/gcc/testsuite/gcc.dg/builtin-dynamic-alloc-size.c
new file mode 100644
index 00000000000..9d0eadd6be4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-alloc-size.c
@@ -0,0 +1,7 @@
+/* { dg-do compile }
+   { dg-require-effective-target alloca }
+   { dg-additional-options "-O2 -fdump-tree-optimized" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-alloc-size.c"
+/* { dg-final { scan-tree-dump-not "abort" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c
new file mode 100644
index 00000000000..7cc8b1c9488
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c
@@ -0,0 +1,6 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+/* { dg-require-effective-target alloca } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-1.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
new file mode 100644
index 00000000000..bc880a589ae
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-early_objsz-details" } */
+// { dg-skip-if "packed attribute missing for drone_source_packet" { "epiphany-*-*" } }
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-10.c"
+
+/* { dg-final { scan-tree-dump "maximum object size 21" "early_objsz" } } */
+/* { dg-final { scan-tree-dump "maximum subobject size 16" "early_objsz" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-11.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-11.c
new file mode 100644
index 00000000000..65dcec9fcae
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-11.c
@@ -0,0 +1,7 @@
+/* PR48985 */
+/* { dg-do run } */
+/* { dg-options "-std=gnu89" } */
+/* { dg-skip-if "packed attribute missing for struct s" { "epiphany-*-*" } } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-11.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-12.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-12.c
new file mode 100644
index 00000000000..f0ce050a943
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-12.c
@@ -0,0 +1,5 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-12.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-13.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-13.c
new file mode 100644
index 00000000000..555e23522dc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-13.c
@@ -0,0 +1,5 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-13.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-14.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-14.c
new file mode 100644
index 00000000000..26207200191
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-14.c
@@ -0,0 +1,5 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-14.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-15.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-15.c
new file mode 100644
index 00000000000..cd8a941438d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-15.c
@@ -0,0 +1,5 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-15.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-16.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-16.c
new file mode 100644
index 00000000000..cc775089410
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-16.c
@@ -0,0 +1,6 @@
+/* { dg-do run } */
+/* { dg-options "-O0" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+char ax2[];               /* { dg-warning "assumed to have one element" } */
+#include "builtin-object-size-16.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-17.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-17.c
new file mode 100644
index 00000000000..f5c57a8788d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-17.c
@@ -0,0 +1,7 @@
+/* { dg-do compile } */
+/* { dg-options "-O0 -fdump-tree-ssa" } */
+
+char ax2[];               /* { dg-warning "assumed to have one element" } */
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-17.c"
+/* { dg-final { scan-tree-dump-not "failure_on_line" "ssa" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-18.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-18.c
new file mode 100644
index 00000000000..70c1ebcff21
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-18.c
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* __stpncpy_chk could return buf up to buf + 64, so
+   the minimum object size might be far smaller than 64.  */
+/* { dg-final { scan-tree-dump-not "return 64;" "optimized" } } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-18.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-19.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-19.c
new file mode 100644
index 00000000000..44141a38607
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-19.c
@@ -0,0 +1,104 @@
+/* PR tree-optimization/88372 - alloc_size attribute is ignored
+   on function pointers { dg-do compile }
+   { dg-options "-O2 -fdump-tree-optimized" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-18.c"
+
+typedef __SIZE_TYPE__ size_t;
+
+#define ATTR(...) __attribute__ ((__VA_ARGS__))
+#define CONCAT(x, y) x ## y
+#define CAT(x, y) CONCAT (x, y)
+#define FAILNAME(name) CAT (call_ ## name ##_on_line_, __LINE__)
+
+#define FAIL(name) do {				\
+    extern void FAILNAME (name) (void);		\
+    FAILNAME (name)();				\
+  } while (0)
+
+/* Macro to emit a call to function named
+   call_in_true_branch_not_eliminated_on_line_NNN()
+   for each call that's expected to be eliminated.  The dg-final
+   scan-tree-dump-time directive at the bottom of the test verifies
+   that no such call appears in output.  */
+#define ELIM(expr)							\
+  if (!(expr)) FAIL (in_true_branch_not_eliminated); else (void)0
+
+void sink (void*);
+
+#define T(alloc, n) do {			\
+    void *p = alloc;				\
+    sink (p);					\
+    ELIM (n == __builtin_object_size (p, 0));	\
+    ELIM (n == __builtin_object_size (p, 1));	\
+    ELIM (n == __builtin_object_size (p, 2));	\
+    ELIM (n == __builtin_object_size (p, 3));	\
+  } while (0)
+
+
+ATTR (alloc_size (1)) void* (*alloc_1_x)(size_t, size_t);
+ATTR (alloc_size (2)) void* (*alloc_x_2)(size_t, size_t);
+
+/* Verify that things work when attribute alloc_size is applied
+   to a typedef that is then used to declared a pointer.  */
+typedef ATTR (alloc_size (1, 2)) void* (alloc_1_2_t)(size_t, size_t);
+
+void test_alloc_ptr (alloc_1_2_t *alloc_1_2)
+{
+  T (alloc_1_x (0, 0), 0);
+  T (alloc_1_x (1, 0), 1);
+  T (alloc_1_x (3, 0), 3);
+  T (alloc_1_x (9, 5), 9);
+
+  T (alloc_x_2 (0, 0), 0);
+  T (alloc_x_2 (1, 0), 0);
+  T (alloc_x_2 (0, 1), 1);
+  T (alloc_x_2 (9, 5), 5);
+
+  T (alloc_1_2 (0, 0), 0);
+  T (alloc_1_2 (1, 0), 0);
+  T (alloc_1_2 (0, 1), 0);
+  T (alloc_1_2 (9, 5), 45);
+}
+
+/* Verify that object size is detected even in indirect calls via
+   function pointers to built-in allocation functions, even without
+   explicit use of attribute alloc_size on the pointers.  */
+
+typedef void *(allocfn_1) (size_t);
+typedef void *(allocfn_1_2) (size_t, size_t);
+
+static inline void *
+call_alloc (allocfn_1 *fn1, allocfn_1_2 *fn2, size_t n1, size_t n2)
+{
+  return fn1 ? fn1 (n1) : fn2 (n1, n2);
+}
+
+static inline void *
+call_malloc (size_t n)
+{
+  return call_alloc (__builtin_malloc, 0, n, 0);
+}
+
+static inline void *
+call_calloc (size_t n1, size_t n2)
+{
+  return call_alloc (0, __builtin_calloc, n1, n2);
+}
+
+void test_builtin_ptr (void)
+{
+  T (call_malloc (0), 0);
+  T (call_malloc (1), 1);
+  T (call_malloc (9), 9);
+
+  T (call_calloc (0, 0), 0);
+  T (call_calloc (0, 1), 0);
+  T (call_calloc (1, 0), 0);
+  T (call_calloc (1, 1), 1);
+  T (call_calloc (1, 3), 3);
+  T (call_calloc (2, 3), 6);
+}
+
+/* { dg-final { scan-tree-dump-not "not_eliminated" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c
new file mode 100644
index 00000000000..267dbf48ca7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c
@@ -0,0 +1,6 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+/* { dg-require-effective-target alloca } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-2.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c
new file mode 100644
index 00000000000..fb9dc56da7e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c
@@ -0,0 +1,6 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+/* { dg-require-effective-target alloca } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-3.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c
new file mode 100644
index 00000000000..870548b4206
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c
@@ -0,0 +1,6 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+/* { dg-require-effective-target alloca } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-4.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c
new file mode 100644
index 00000000000..698b03c34be
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c
@@ -0,0 +1,7 @@
+/* { dg-do compile { target i?86-*-linux* i?86-*-gnu* x86_64-*-linux* } } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-5.c"
+
+/* { dg-final { scan-assembler-not "abort" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-6.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-6.c
new file mode 100644
index 00000000000..6a275ce5b37
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-6.c
@@ -0,0 +1,5 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-6.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-7.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-7.c
new file mode 100644
index 00000000000..e2a65994687
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-7.c
@@ -0,0 +1,5 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-7.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-8.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-8.c
new file mode 100644
index 00000000000..e7af383d9b6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-8.c
@@ -0,0 +1,5 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-8.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-9.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-9.c
new file mode 100644
index 00000000000..19021bc2ce9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-9.c
@@ -0,0 +1,5 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-9.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-16.c b/gcc/testsuite/gcc.dg/builtin-object-size-16.c
index 48229390bfd..f26d5d97a5e 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-16.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-16.c
@@ -54,7 +54,9 @@ static int nfails;
 typedef __SIZE_TYPE__ size_t;
 
 extern char ax[];
+#ifndef __builtin_object_size
 char ax2[];               /* { dg-warning "assumed to have one element" } */
+#endif
 
 extern char a0[0];
 static char a1[1];
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-17.c b/gcc/testsuite/gcc.dg/builtin-object-size-17.c
index 0497bbf4505..28f8414636b 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-17.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-17.c
@@ -49,7 +49,9 @@
 typedef __SIZE_TYPE__ size_t;
 
 extern char ax[];
+#ifndef __builtin_object_size
 char ax2[];               /* { dg-warning "assumed to have one element" } */
+#endif
 
 extern char a0[0];
 static char a1[1];
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index 1a1e3d56126..969e01e70fb 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -34,6 +34,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-cfg.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "builtins.h"
 
 struct object_size_info
 {
@@ -45,13 +46,6 @@ struct object_size_info
   unsigned int *stack, *tos;
 };
 
-enum
-{
-  OST_SUBOBJECT = 1,
-  OST_MINIMUM = 2,
-  OST_END = 4,
-};
-
 static tree compute_object_offset (const_tree, const_tree);
 static bool addr_object_size (struct object_size_info *,
 			      const_tree, int, tree *, tree *t = NULL);
@@ -755,8 +749,9 @@ compute_builtin_object_size (tree ptr, int object_size_type,
       object_sizes_grow (object_size_type);
       if (dump_file)
 	{
-	  fprintf (dump_file, "Computing %s %sobject size for ",
+	  fprintf (dump_file, "Computing %s %s%sobject size for ",
 		   (object_size_type & OST_MINIMUM) ? "minimum" : "maximum",
+		   (object_size_type & OST_DYNAMIC) ? "dynamic " : "",
 		   (object_size_type & OST_SUBOBJECT) ? "sub" : "");
 	  print_generic_expr (dump_file, ptr, dump_flags);
 	  fprintf (dump_file, ":\n");
@@ -845,9 +840,10 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 		print_generic_expr (dump_file, ssa_name (i),
 				    dump_flags);
 		fprintf (dump_file,
-			 ": %s %sobject size ",
+			 ": %s %s%sobject size ",
 			 ((object_size_type & OST_MINIMUM) ? "minimum"
 			  : "maximum"),
+			 (object_size_type & OST_DYNAMIC) ? "dynamic " : "",
 			 (object_size_type & OST_SUBOBJECT) ? "sub" : "");
 		print_generic_expr (dump_file, object_sizes_get (&osi, i),
 				    dump_flags);
@@ -1397,8 +1393,85 @@ do_valueize (tree t)
   return t;
 }
 
+/* Process a __builtin_object_size or __builtin_dynamic_object_size call in
+   CALL early for subobjects before any object information is lost due to
+   optimization.  Insert a MIN or MAX expression of the result and
+   __builtin_object_size at I so that it may be processed in the second pass.
+   __builtin_dynamic_object_size is treated like __builtin_object_size here
+   since we're only looking for constant bounds.  */
+
+static void
+early_object_sizes_execute_one (gimple_stmt_iterator *i, gimple *call)
+{
+  tree ost = gimple_call_arg (call, 1);
+  tree lhs = gimple_call_lhs (call);
+  gcc_assert (lhs != NULL_TREE);
+
+  if (!tree_fits_uhwi_p (ost))
+    return;
+
+  unsigned HOST_WIDE_INT object_size_type = tree_to_uhwi (ost);
+  tree ptr = gimple_call_arg (call, 0);
+
+  if (object_size_type != 1 && object_size_type != 3)
+    return;
+
+  if (TREE_CODE (ptr) != ADDR_EXPR && TREE_CODE (ptr) != SSA_NAME)
+    return;
+
+  tree type = TREE_TYPE (lhs);
+  tree bytes;
+  if (!compute_builtin_object_size (ptr, object_size_type, &bytes)
+      || !int_fits_type_p (bytes, type))
+    return;
+
+  tree tem = make_ssa_name (type);
+  gimple_call_set_lhs (call, tem);
+  enum tree_code code = object_size_type & OST_MINIMUM ? MAX_EXPR : MIN_EXPR;
+  tree cst = fold_convert (type, bytes);
+  gimple *g = gimple_build_assign (lhs, code, tem, cst);
+  gsi_insert_after (i, g, GSI_NEW_STMT);
+  update_stmt (call);
+}
+
+/* Attempt to fold one __builtin_dynamic_object_size call in CALL into an
+   expression and insert it at I.  Return true if it succeeds.  */
+
+static bool
+dynamic_object_sizes_execute_one (gimple_stmt_iterator *i, gimple *call)
+{
+  gcc_assert (gimple_call_num_args (call) == 2);
+
+  tree args[2];
+  args[0] = gimple_call_arg (call, 0);
+  args[1] = gimple_call_arg (call, 1);
+
+  location_t loc = EXPR_LOC_OR_LOC (args[0], input_location);
+  tree result_type = gimple_call_return_type (as_a <gcall *> (call));
+  tree result = fold_builtin_call_array (loc, result_type,
+					 gimple_call_fn (call), 2, args);
+
+  if (!result)
+    return false;
+
+  /* fold_builtin_call_array may wrap the result inside a
+     NOP_EXPR.  */
+  STRIP_NOPS (result);
+  gimplify_and_update_call_from_tree (i, result);
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "Simplified (dynamic)\n  ");
+      print_gimple_stmt (dump_file, call, 0, dump_flags);
+      fprintf (dump_file, " to ");
+      print_generic_expr (dump_file, result);
+      fprintf (dump_file, "\n");
+    }
+  return true;
+}
+
 static unsigned int
-object_sizes_execute (function *fun, bool insert_min_max_p)
+object_sizes_execute (function *fun, bool early)
 {
   basic_block bb;
   FOR_EACH_BB_FN (bb, fun)
@@ -1407,8 +1480,12 @@ object_sizes_execute (function *fun, bool insert_min_max_p)
       for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i))
 	{
 	  tree result;
+	  bool dynamic = false;
+
 	  gimple *call = gsi_stmt (i);
-	  if (!gimple_call_builtin_p (call, BUILT_IN_OBJECT_SIZE))
+	  if (gimple_call_builtin_p (call, BUILT_IN_DYNAMIC_OBJECT_SIZE))
+	    dynamic = true;
+	  else if (!gimple_call_builtin_p (call, BUILT_IN_OBJECT_SIZE))
 	    continue;
 
 	  tree lhs = gimple_call_lhs (call);
@@ -1417,42 +1494,39 @@ object_sizes_execute (function *fun, bool insert_min_max_p)
 
 	  init_object_sizes ();
 
-	  /* If insert_min_max_p, only attempt to fold
+	  /* If early, only attempt to fold
 	     __builtin_object_size (x, 1) and __builtin_object_size (x, 3),
 	     and rather than folding the builtin to the constant if any,
 	     create a MIN_EXPR or MAX_EXPR of the __builtin_object_size
-	     call result and the computed constant.  */
-	  if (insert_min_max_p)
+	     call result and the computed constant.  Do the same for
+	     __builtin_dynamic_object_size too.  */
+	  if (early)
 	    {
-	      tree ost = gimple_call_arg (call, 1);
-	      if (tree_fits_uhwi_p (ost))
+	      early_object_sizes_execute_one (&i, call);
+	      continue;
+	    }
+
+	  if (dynamic)
+	    {
+	      if (dynamic_object_sizes_execute_one (&i, call))
+		continue;
+	      else
 		{
-		  unsigned HOST_WIDE_INT object_size_type = tree_to_uhwi (ost);
-		  tree ptr = gimple_call_arg (call, 0);
-		  if ((object_size_type & OST_SUBOBJECT)
-		      && (TREE_CODE (ptr) == ADDR_EXPR
-			  || TREE_CODE (ptr) == SSA_NAME))
+		  /* If we could not find a suitable size expression, lower to
+		     __builtin_object_size so that we may at least get a
+		     constant lower or higher estimate.  */
+		  tree bosfn = builtin_decl_implicit (BUILT_IN_OBJECT_SIZE);
+		  gimple_call_set_fndecl (call, bosfn);
+		  update_stmt (call);
+
+		  if (dump_file && (dump_flags & TDF_DETAILS))
 		    {
-		      tree type = TREE_TYPE (lhs);
-		      tree bytes;
-		      if (compute_builtin_object_size (ptr, object_size_type,
-						       &bytes)
-			  && int_fits_type_p (bytes, type))
-			{
-			  tree tem = make_ssa_name (type);
-			  gimple_call_set_lhs (call, tem);
-			  enum tree_code code
-			    = (object_size_type & OST_MINIMUM
-			       ? MAX_EXPR : MIN_EXPR);
-			  tree cst = fold_convert (type, bytes);
-			  gimple *g
-			    = gimple_build_assign (lhs, code, tem, cst);
-			  gsi_insert_after (&i, g, GSI_NEW_STMT);
-			  update_stmt (call);
-			}
+		      print_generic_expr (dump_file, gimple_call_arg (call, 0),
+					  dump_flags);
+		      fprintf (dump_file,
+			       ": Retrying as __builtin_object_size\n");
 		    }
 		}
-	      continue;
 	    }
 
 	  result = gimple_fold_stmt_to_constant (call, do_valueize);
diff --git a/gcc/tree-object-size.h b/gcc/tree-object-size.h
index b2d6a58324c..f4f438c10a8 100644
--- a/gcc/tree-object-size.h
+++ b/gcc/tree-object-size.h
@@ -20,6 +20,16 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_TREE_OBJECT_SIZE_H
 #define GCC_TREE_OBJECT_SIZE_H
 
+/* Bits in object_size_type.  */
+
+enum
+{
+  OST_SUBOBJECT = 1,
+  OST_MINIMUM = 2,
+  OST_DYNAMIC = 4,
+  OST_END = 8,
+};
+
 extern void init_object_sizes (void);
 extern void fini_object_sizes (void);
 extern bool compute_builtin_object_size (tree, int, tree *);
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v3 5/8] tree-object-size: Support dynamic sizes in conditions
  2021-11-26  5:28 ` [PATCH v3 0/8] __builtin_dynamic_object_size Siddhesh Poyarekar
                     ` (3 preceding siblings ...)
  2021-11-26  5:28   ` [PATCH v3 4/8] __builtin_dynamic_object_size: Recognize builtin Siddhesh Poyarekar
@ 2021-11-26  5:28   ` Siddhesh Poyarekar
  2021-11-26  5:28   ` [PATCH v3 6/8] tree-object-size: Handle function parameters Siddhesh Poyarekar
                     ` (3 subsequent siblings)
  8 siblings, 0 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-26  5:28 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Handle GIMPLE_PHI and conditionals specially for dynamic objects,
returning PHI/conditional expressions instead of just a MIN/MAX
estimate.

This makes the returned object size variable for loops and conditionals,
so tests need to be adjusted to look for precise size in some cases.
builtin-dynamic-object-size-5.c had to be modified to only look for
success in maximum object size case and skip over the minimum object
size tests because the result is no longer a compile time constant.

I also added some simple tests to exercise conditionals with dynamic
object sizes.

gcc/ChangeLog:

	* builtins.c (fold_builtin_object_size): Adjust for dynamic size
	expressions.
	* tree-object-size.c: Include gimplify-me.h.
	(struct object_size_info): New member UNKNOWNS.
	(size_initval_p, object_sizes_get_raw): New functions.
	(object_sizes_get): Return suitable gimple variable for
	object size.
	(object_sizes_initialize): Reuse existing object size TREE_VEC
	during gimplification.
	(bundle_sizes): New function.
	(object_sizes_set): Use it and handle dynamic object size
	expressions.
	(object_sizes_set_temp): New function.
	(size_for_offset): Adjust for dynamic size expressions.
	(emit_phi_nodes, propagate_unknowns, gimplify_size_expressions):
	New functions.
	(compute_builtin_object_size): Call gimplify_size_expressions
	for OST_DYNAMIC.
	(dynamic_object_size): New function.
	(cond_expr_object_size): Use it.
	(phi_dynamic_object_size): New function.
	(collect_object_sizes_for): Call it for OST_DYNAMIC.  Adjust to
	accommodate dynamic object sizes.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-dynamic-object-size-0.c: New tests.
	* gcc.dg/builtin-dynamic-object-size-10.c: Add comment.
	* gcc.dg/builtin-object-size-1.c [__builtin_object_size]: Expect
	exact size expressions for __builtin_dynamic_object_size.
	* gcc.dg/builtin-object-size-2.c [__builtin_object_size]:
	Likewise.
	* gcc.dg/builtin-object-size-3.c [__builtin_object_size]:
	Likewise.
	* gcc.dg/builtin-object-size-4.c [__builtin_object_size]:
	Likewise.
	* gcc.dg/builtin-object-size-5.c [__builtin_object_size]:
	Likewise.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
Changes from v2:

- Incorporated review suggestions.
- Delay generating PHI nodes until gimplification so that it doesn't
  have to be undone if it was found to be unknown.
- Adapt to retaining the multipass approach for static object sizes.

 gcc/builtins.c                                |   6 +-
 .../gcc.dg/builtin-dynamic-object-size-0.c    |  72 +++
 .../gcc.dg/builtin-dynamic-object-size-10.c   |   2 +
 gcc/testsuite/gcc.dg/builtin-object-size-1.c  | 119 ++++-
 gcc/testsuite/gcc.dg/builtin-object-size-2.c  |  92 ++++
 gcc/testsuite/gcc.dg/builtin-object-size-3.c  | 121 +++++
 gcc/testsuite/gcc.dg/builtin-object-size-4.c  |  78 +++
 gcc/testsuite/gcc.dg/builtin-object-size-5.c  |  12 +
 gcc/tree-object-size.c                        | 494 +++++++++++++++++-
 9 files changed, 966 insertions(+), 30 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 573f7e9b9df..9770e13353d 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -10256,7 +10256,8 @@ fold_builtin_object_size (tree ptr, tree ost, enum built_in_function fcode)
   if (TREE_CODE (ptr) == ADDR_EXPR)
     {
       compute_builtin_object_size (ptr, object_size_type, &bytes);
-      if (int_fits_type_p (bytes, size_type_node))
+      if ((object_size_type & OST_DYNAMIC)
+	  || int_fits_type_p (bytes, size_type_node))
 	return fold_convert (size_type_node, bytes);
     }
   else if (TREE_CODE (ptr) == SSA_NAME)
@@ -10265,7 +10266,8 @@ fold_builtin_object_size (tree ptr, tree ost, enum built_in_function fcode)
        later.  Maybe subsequent passes will help determining
        it.  */
       if (compute_builtin_object_size (ptr, object_size_type, &bytes)
-	  && int_fits_type_p (bytes, size_type_node))
+	  && ((object_size_type & OST_DYNAMIC)
+	      || int_fits_type_p (bytes, size_type_node)))
 	return fold_convert (size_type_node, bytes);
     }
 
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
new file mode 100644
index 00000000000..ddedf6a49bd
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -0,0 +1,72 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+typedef __SIZE_TYPE__ size_t;
+#define abort __builtin_abort
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi (int cond)
+{
+  void *ret;
+ 
+  if (cond)
+    ret = __builtin_malloc (32);
+  else
+    ret = __builtin_malloc (64);
+
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_calloc_condphi (size_t cnt, size_t sz, int cond)
+{
+  struct
+    {
+      int a;
+      char b;
+    } bin[cnt];
+
+  char *ch = __builtin_calloc (cnt, sz);
+
+  return __builtin_dynamic_object_size (cond ? ch : (void *) &bin, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_deploop (size_t sz, size_t cond)
+{
+  char *bin = __builtin_alloca (32);
+
+  for (size_t i = 0; i < sz; i++)
+    if (i == cond)
+      bin = __builtin_alloca (64);
+
+  return __builtin_dynamic_object_size (bin, 0);
+}
+
+unsigned nfails = 0;
+
+#define FAIL() ({ \
+  __builtin_printf ("Failure at line: %d\n", __LINE__);			      \
+  nfails++;								      \
+})
+
+int
+main (int argc, char **argv)
+{
+  if (test_builtin_malloc_condphi (1) != 32)
+    FAIL ();
+  if (test_builtin_malloc_condphi (0) != 64)
+    FAIL ();
+  if (test_builtin_calloc_condphi (128, 1, 0) == 128)
+    FAIL ();
+  if (test_deploop (128, 129) != 32)
+    FAIL ();
+
+  if (nfails > 0)
+    __builtin_abort ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
index bc880a589ae..3a2d9821a44 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
@@ -5,5 +5,7 @@
 #define __builtin_object_size __builtin_dynamic_object_size
 #include "builtin-object-size-10.c"
 
+/* early_objsz should resolve __builtin_dynamic_object_size like
+   __builtin_object_size.  */
 /* { dg-final { scan-tree-dump "maximum object size 21" "early_objsz" } } */
 /* { dg-final { scan-tree-dump "maximum subobject size 16" "early_objsz" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
index 0154f4e9695..265c87ed6fb 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
@@ -42,9 +42,17 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (q, 0) != (size_t) -1)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0)
+      != (x < 0
+	  ? sizeof (a) - __builtin_offsetof (struct A, a) - 9
+	  : sizeof (a) - __builtin_offsetof (struct A, c) - 1))
+    abort ();
+#else
   if (__builtin_object_size (r, 0)
       != sizeof (a) - __builtin_offsetof (struct A, a) - 9)
     abort ();
+#endif
   if (x < 6)
     r = &w[2].a[1];
   else
@@ -58,9 +66,17 @@ test1 (void *q, int x)
   if (__builtin_object_size (&y.b, 0)
       != sizeof (a) - __builtin_offsetof (struct A, b))
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0)
+      != (x < 6
+	  ? 2 * sizeof (w[0]) - __builtin_offsetof (struct A, a) - 1
+	  : sizeof (a) - __builtin_offsetof (struct A, a) - 6))
+    abort ();
+#else
   if (__builtin_object_size (r, 0)
       != 2 * sizeof (w[0]) - __builtin_offsetof (struct A, a) - 1)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
@@ -165,6 +181,7 @@ test2 (void)
   struct B { char buf1[10]; char buf2[10]; } a;
   char *r, buf3[20];
   int i;
+  size_t res;
 
   if (sizeof (a) != 20)
     return;
@@ -181,7 +198,24 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[9];
     }
-  if (__builtin_object_size (r, 0) != 20)
+#ifdef __builtin_object_size
+  res = sizeof (buf3);
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 1;
+      else if (i == l1)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 7;
+      else if (i == l1 + 1)
+        res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
+    }
+#else
+  res = 20;
+#endif
+  if (__builtin_object_size (r, 0) != res)
     abort ();
   r = &buf3[20];
   for (i = 0; i < 4; ++i)
@@ -195,13 +229,45 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[9];
     }
-  if (__builtin_object_size (r, 0) != 15)
+#ifdef __builtin_object_size
+  res = sizeof (buf3) - 20;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 7;
+      else if (i == l1)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 7;
+      else if (i == l1 + 1)
+        res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
+    }
+  if (__builtin_object_size (r, 0) != res)
+    abort ();
+#else
+  res = 15;
+#endif
+  if (__builtin_object_size (r, 0) != res)
     abort ();
   r += 8;
+#ifdef __builtin_object_size
+  res -= 8;
+  if (__builtin_object_size (r, 0) != res)
+    abort ();
+  if (res >= 6)
+    {
+      if (__builtin_object_size (r + 6, 0) != res - 6)
+        abort ();
+    }
+  else if (__builtin_object_size (r + 6, 0) != 0)
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != 7)
     abort ();
   if (__builtin_object_size (r + 6, 0) != 1)
     abort ();
+#endif
   r = &buf3[18];
   for (i = 0; i < 4; ++i)
     {
@@ -214,8 +280,31 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[4];
     }
+#ifdef __builtin_object_size
+  res = sizeof (buf3) - 18;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+          res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
+      else if (i == l1)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 9;
+      else if (i == l1 + 1)
+        res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 4;
+    }
+  if (res >= 12)
+    {
+      if (__builtin_object_size (r + 12, 0) != res - 12)
+        abort ();
+    }
+  else if (__builtin_object_size (r + 12, 0) != 0)
+    abort ();
+#else
   if (__builtin_object_size (r + 12, 0) != 4)
     abort ();
+#endif
 }
 
 void
@@ -358,6 +447,10 @@ test5 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 0) != sizeof (buf) - 8 - 4 * x)
+    abort ();
+#else
   /* My understanding of ISO C99 6.5.6 is that a conforming
      program will not end up with p equal to &buf[0]
      through &buf[7], i.e. calling this function with say
@@ -367,6 +460,7 @@ test5 (size_t x)
      it would be 64 (or conservative (size_t) -1 == unknown).  */
   if (__builtin_object_size (p, 0) != sizeof (buf) - 8)
     abort ();
+#endif
   memset (p, ' ', sizeof (buf) - 8 - 4 * 4);
 }
 
@@ -380,8 +474,13 @@ test6 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 0) != sizeof (t) - 8 - 4 * x)
+    abort ();
+#else
   if (__builtin_object_size (p, 0) != sizeof (t) - 8)
     abort ();
+#endif
   memset (p, ' ', sizeof (t) - 8 - 4 * 4);
 }
 
@@ -436,21 +535,37 @@ test9 (unsigned cond)
   else
     p = &buf2[4];
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (&p[-4], 0) != (cond ? 6 : 10))
+    abort ();
+#else
   if (__builtin_object_size (&p[-4], 0) != 10)
     abort ();
+#endif
 
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 0) != ((cond ? 2 : 6) + cond))
+    abort ();
+#else
   if (__builtin_object_size (p, 0) != 10)
     abort ();
+#endif
 
   p = &y.c[8];
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 0)
+      != sizeof (y) - __builtin_offsetof (struct A, c) - 8 + cond)
+    abort ();
+#else
   if (__builtin_object_size (p, 0) != sizeof (y))
     abort ();
+#endif
 }
 
 int
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
index 5cf29291aff..5051fea47c3 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-2.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
@@ -43,8 +43,15 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (q, 1) != (size_t) -1)
     abort ();
+#ifdef __builtin_object_size
+  if (x < 0
+      ? __builtin_object_size (r, 1) != sizeof (a.a) - 9
+      : __builtin_object_size (r, 1) != sizeof (a.c) - 1)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a.c) - 1)
     abort ();
+#endif
   if (x < 6)
     r = &w[2].a[1];
   else
@@ -55,8 +62,15 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&y.b, 1) != sizeof (a.b))
     abort ();
+#ifdef __builtin_object_size
+  if (x < 6
+      ? __builtin_object_size (r, 1) != sizeof (a.a) - 1
+      : __builtin_object_size (r, 1) != sizeof (a.a) - 6)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a.a) - 1)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
@@ -185,6 +199,9 @@ test2 (void)
   struct B { char buf1[10]; char buf2[10]; } a;
   char *r, buf3[20];
   int i;
+#ifdef __builtin_object_size
+  size_t dyn_res;
+#endif
 
   if (sizeof (a) != 20)
     return;
@@ -201,8 +218,26 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[9];
     }
+#ifdef __builtin_object_size
+  dyn_res = sizeof (buf3);
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	dyn_res = sizeof (a.buf1) - 1;
+      else if (i == l1)
+	dyn_res = sizeof (a.buf2) - 7;
+      else if (i == l1 + 1)
+	dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+	dyn_res = sizeof (a.buf1) - 9;
+    }
+  if (__builtin_object_size (r, 1) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (buf3))
     abort ();
+#endif
   r = &buf3[20];
   for (i = 0; i < 4; ++i)
     {
@@ -215,13 +250,50 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[9];
     }
+#ifdef __builtin_object_size
+  dyn_res = sizeof (buf3) - 20;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+        dyn_res = sizeof (a.buf1) - 7;
+      else if (i == l1)
+        dyn_res = sizeof (a.buf2) - 7;
+      else if (i == l1 + 1)
+        dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+        dyn_res = sizeof (a.buf1) - 9;
+    }
+  if (__builtin_object_size (r, 1) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (buf3) - 5)
     abort ();
+#endif
   r += 8;
+#ifdef __builtin_object_size
+  if (dyn_res >= 8)
+    {
+      dyn_res -= 8;
+      if (__builtin_object_size (r, 1) != dyn_res)
+	abort ();
+
+      if (dyn_res >= 6)
+	{
+	  if (__builtin_object_size (r + 6, 1) != dyn_res - 6)
+	    abort ();
+	}
+      else if (__builtin_object_size (r + 6, 1) != 0)
+	abort ();
+    }
+  else if (__builtin_object_size (r, 1) != 0)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (buf3) - 13)
     abort ();
   if (__builtin_object_size (r + 6, 1) != sizeof (buf3) - 19)
     abort ();
+#endif
 }
 
 void
@@ -339,8 +411,13 @@ test5 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 1) != sizeof (t.buf) - 8 - 4 * x)
+    abort ();
+#else
   if (__builtin_object_size (p, 1) != sizeof (t.buf) - 8)
     abort ();
+#endif
   memset (p, ' ', sizeof (t.buf) - 8 - 4 * 4);
 }
 
@@ -394,21 +471,36 @@ test8 (unsigned cond)
   else
     p = &buf2[4];
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (&p[-4], 1) != (cond ? 6 : 10))
+    abort ();
+#else
   if (__builtin_object_size (&p[-4], 1) != 10)
     abort ();
+#endif
 
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 1) != ((cond ? 2 : 6) + cond))
+    abort ();
+#else
   if (__builtin_object_size (p, 1) != 10)
     abort ();
+#endif
 
   p = &y.c[8];
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 1) != sizeof (y.c) - 8 + cond)
+    abort ();
+#else
   if (__builtin_object_size (p, 1) != sizeof (y.c))
     abort ();
+#endif
 }
 
 int
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
index 3a692c4e3d2..1d92627266b 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-3.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
@@ -71,23 +71,45 @@ test1 (void *q, int x)
     r = malloc (30);
   else
     r = calloc (2, 14);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 2) != (x < 20 ? 30 : 2 * 14))
+    abort ();
+#else
   if (__builtin_object_size (r, 2) != 2 * 14)
     abort ();
+#endif
   if (x < 30)
     r = malloc (sizeof (a));
   else
     r = &a.a[3];
+#ifdef __builtin_object_size
+  size_t objsz = (x < 30 ? sizeof (a)
+                  : sizeof (a) - __builtin_offsetof (struct A, a) - 3);
+  if (__builtin_object_size (r, 2) != objsz)
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, a) - 3)
     abort ();
+#endif
   r = memcpy (r, "a", 2);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 2) != objsz)
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, a) - 3)
     abort ();
+#endif
   r = memcpy (r + 2, "b", 2) + 2;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 2) != objsz - 4)
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, a) - 3 - 4)
     abort ();
+#endif
   r = &a.a[4];
   r = memset (r, 'a', 2);
   if (__builtin_object_size (r, 2)
@@ -164,6 +186,9 @@ test2 (void)
   struct B { char buf1[10]; char buf2[10]; } a;
   char *r, buf3[20];
   int i;
+#ifdef __builtin_object_size
+  size_t dyn_res;
+#endif
 
   if (sizeof (a) != 20)
     return;
@@ -180,8 +205,26 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[9];
     }
+#ifdef __builtin_object_size
+  dyn_res = sizeof (buf3);
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 1;
+      else if (i == l1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 7;
+      else if (i == l1 + 1)
+	dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
+    }
+  if (__builtin_object_size (r, 2) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 2) != 3)
     abort ();
+#endif
   r = &buf3[20];
   for (i = 0; i < 4; ++i)
     {
@@ -208,13 +251,44 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[4];
     }
+#ifdef __builtin_object_size
+  dyn_res = sizeof (buf3) - 2;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 1;
+      else if (i == l1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 2;
+      else if (i == l1 + 1)
+	dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 4;
+    }
+  if (__builtin_object_size (r, 2) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 2) != 15)
     abort ();
+#endif
   r += 8;
+#ifdef __builtin_object_size
+  dyn_res -= 8;
+  if (__builtin_object_size (r, 2) != dyn_res)
+    abort ();
+  if (dyn_res >= 6)
+    {
+      if (__builtin_object_size (r + 6, 2) != dyn_res - 6)
+	abort ();
+    }
+  else if (__builtin_object_size (r + 6, 2) != 0)
+    abort ();
+#else
   if (__builtin_object_size (r, 2) != 7)
     abort ();
   if (__builtin_object_size (r + 6, 2) != 1)
     abort ();
+#endif
   r = &buf3[18];
   for (i = 0; i < 4; ++i)
     {
@@ -227,8 +301,31 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[4];
     }
+#ifdef __builtin_object_size
+  dyn_res = sizeof (buf3) - 18;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
+      else if (i == l1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 9;
+      else if (i == l1 + 1)
+	dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 4;
+    }
+  if (dyn_res >= 12)
+    {
+      if (__builtin_object_size (r + 12, 2) != dyn_res - 12)
+	abort ();
+    }
+  else if (__builtin_object_size (r + 12, 2) != 0)
+    abort ();
+#else
   if (__builtin_object_size (r + 12, 2) != 0)
     abort ();
+#endif
 }
 
 void
@@ -371,7 +468,11 @@ test5 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 2) != sizeof (buf) - 8 - 4 * x)
+#else
   if (__builtin_object_size (p, 2) != 0)
+#endif
     abort ();
   memset (p, ' ', sizeof (buf) - 8 - 4 * 4);
 }
@@ -386,7 +487,11 @@ test6 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 2) != sizeof (t) - 8 - 4 * x)
+#else
   if (__builtin_object_size (p, 2) != 0)
+#endif
     abort ();
   memset (p, ' ', sizeof (t) - 8 - 4 * 4);
 }
@@ -442,22 +547,38 @@ test9 (unsigned cond)
   else
     p = &buf2[4];
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (&p[-4], 2) != (cond ? 6 : 10))
+    abort ();
+#else
   if (__builtin_object_size (&p[-4], 2) != 6)
     abort ();
+#endif
 
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 2) != ((cond ? 2 : 6) + cond))
+    abort ();
+#else
   if (__builtin_object_size (p, 2) != 2)
     abort ();
+#endif
 
   p = &y.c[8];
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 2)
+      != sizeof (y) - __builtin_offsetof (struct A, c) - 8 + cond)
+    abort ();
+#else
   if (__builtin_object_size (p, 2)
       != sizeof (y) - __builtin_offsetof (struct A, c) - 8)
     abort ();
+#endif
 }
 
 int
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
index 87381620cc9..9da3537a5f7 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-4.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
@@ -43,7 +43,12 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (q, 3) != 0)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3)
+      != (x < 0 ? sizeof (a.a) - 9 : sizeof (a.c) - 1))
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.a) - 9)
+#endif
     abort ();
   if (x < 6)
     r = &w[2].a[1];
@@ -55,31 +60,57 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&y.b, 3) != sizeof (a.b))
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3)
+      != (x < 6 ? sizeof (w[2].a) - 1 : sizeof (a.a) - 6))
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.a) - 6)
+#endif
     abort ();
   if (x < 20)
     r = malloc (30);
   else
     r = calloc (2, 16);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3) != (x < 20 ? 30 : 2 * 16))
+#else
   if (__builtin_object_size (r, 3) != 30)
+#endif
     abort ();
   if (x < 20)
     r = malloc (30);
   else
     r = calloc (2, 14);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3) != (x < 20 ? 30 : 2 * 14))
+#else
   if (__builtin_object_size (r, 3) != 2 * 14)
+#endif
     abort ();
   if (x < 30)
     r = malloc (sizeof (a));
   else
     r = &a.a[3];
+#ifdef __builtin_object_size
+  size_t objsz = x < 30 ? sizeof (a) : sizeof (a.a) - 3;
+  if (__builtin_object_size (r, 3) != objsz)
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.a) - 3)
+#endif
     abort ();
   r = memcpy (r, "a", 2);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3) != objsz)
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.a) - 3)
+#endif
     abort ();
   r = memcpy (r + 2, "b", 2) + 2;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3) != objsz - 4)
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.a) - 3 - 4)
+#endif
     abort ();
   r = &a.a[4];
   r = memset (r, 'a', 2);
@@ -184,6 +215,9 @@ test2 (void)
   struct B { char buf1[10]; char buf2[10]; } a;
   char *r, buf3[20];
   int i;
+#ifdef __builtin_object_size
+  size_t dyn_res = 0;
+#endif
 
   if (sizeof (a) != 20)
     return;
@@ -228,13 +262,38 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[2];
     }
+#ifdef __builtin_object_size
+  dyn_res = sizeof (buf3) - 1;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+        dyn_res = sizeof (a.buf1) - 6;
+      else if (i == l1)
+        dyn_res = sizeof (a.buf2) - 4;
+      else if (i == l1 + 1)
+        dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+        dyn_res = sizeof (a.buf1) - 2;
+    }
+  if (__builtin_object_size (r, 3) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.buf1) - 6)
     abort ();
+#endif
   r += 2;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3) != dyn_res - 2)
+    abort ();
+  if (__builtin_object_size (r + 1, 3) != dyn_res - 3)
+    abort ();
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.buf1) - 6 - 2)
     abort ();
   if (__builtin_object_size (r + 1, 3) != sizeof (a.buf1) - 6 - 3)
     abort ();
+#endif
 }
 
 void
@@ -352,7 +411,11 @@ test5 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 3) != sizeof (t.buf) - 8 - 4 * x)
+#else
   if (__builtin_object_size (p, 3) != 0)
+#endif
     abort ();
   memset (p, ' ', sizeof (t.buf) - 8 - 4 * 4);
 }
@@ -407,21 +470,36 @@ test8 (unsigned cond)
   else
     p = &buf2[4];
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (&p[-4], 3) != (cond ? 6 : 10))
+    abort ();
+#else
   if (__builtin_object_size (&p[-4], 3) != 6)
     abort ();
+#endif
 
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 3) != ((cond ? 2 : 6) + cond))
+    abort ();
+#else
   if (__builtin_object_size (p, 3) != 2)
     abort ();
+#endif
 
   p = &y.c[8];
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 3) != sizeof (y.c) - 8 + cond)
+    abort ();
+#else
   if (__builtin_object_size (p, 3) != sizeof (y.c) - 8)
     abort ();
+#endif
 }
 
 int
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-5.c b/gcc/testsuite/gcc.dg/builtin-object-size-5.c
index 8e63d9c7a5e..904e616949d 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-5.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-5.c
@@ -1,5 +1,7 @@
 /* { dg-do compile { target i?86-*-linux* i?86-*-gnu* x86_64-*-linux* } } */
 /* { dg-options "-O2" } */
+/* For dynamic object sizes we 'succeed' if the returned size is known for
+   maximum object size.  */
 
 typedef __SIZE_TYPE__ size_t;
 extern void abort (void);
@@ -13,7 +15,11 @@ test1 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 0) == -1)
+#else
   if (__builtin_object_size (p, 0) != sizeof (buf) - 8)
+#endif
     abort ();
 }
 
@@ -25,10 +31,15 @@ test2 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 1) == -1)
+#else
   if (__builtin_object_size (p, 1) != sizeof (buf) - 8)
+#endif
     abort ();
 }
 
+#ifndef __builtin_object_size
 void
 test3 (size_t x)
 {
@@ -52,6 +63,7 @@ test4 (size_t x)
   if (__builtin_object_size (p, 3) != 0)
     abort ();
 }
+#endif
 
 void
 test5 (void)
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index 969e01e70fb..5b4dcb619cd 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -35,13 +35,14 @@ along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "attribs.h"
 #include "builtins.h"
+#include "gimplify-me.h"
 
 struct object_size_info
 {
   int object_size_type;
   unsigned char pass;
   bool changed;
-  bitmap visited, reexamine;
+  bitmap visited, reexamine, unknowns;
   unsigned int *depths;
   unsigned int *stack, *tos;
 };
@@ -70,7 +71,11 @@ static void check_for_plus_in_loops_1 (struct object_size_info *, tree,
    object_sizes[1] is upper bound for the object size and number of bytes till
    the end of the subobject (innermost array or field with address taken).
    object_sizes[2] is lower bound for the object size and number of bytes till
-   the end of the object and object_sizes[3] lower bound for subobject.  */
+   the end of the object and object_sizes[3] lower bound for subobject.
+
+   For static object sizes, both elements of the TREE_VEC are INTEGER_CST.  In
+   the dynamic case, each element is finally a gimple variable or an
+   INTEGER_CST.  */
 static vec<tree> object_sizes[OST_END];
 
 /* Bitmaps what object sizes have been computed already.  */
@@ -96,6 +101,15 @@ unknown (int object_size_type)
   return ~initval (object_size_type);
 }
 
+/* Return true if VAL is represents an initial size for OBJECT_SIZE_TYPE.  */
+
+static inline bool
+size_initval_p (tree val, int object_size_type)
+{
+  return (tree_fits_uhwi_p (val)
+	  && tree_to_uhwi (val) == initval (object_size_type));
+}
+
 /* Return true if VAL is represents an unknown size for OBJECT_SIZE_TYPE.  */
 
 static inline bool
@@ -147,14 +161,39 @@ object_sizes_unknown_p (int object_size_type, unsigned varno)
 			 object_size_type);
 }
 
-/* Return size for VARNO corresponding to OSI.  If WHOLE is true, return the
-   whole object size.  */
+/* Return the raw size expression for VARNO corresponding to OSI.  This returns
+   the TREE_VEC as is and should only be used during gimplification.  */
+
+static inline tree
+object_sizes_get_raw (struct object_size_info *osi, unsigned varno)
+{
+  gcc_assert (osi->pass != 0);
+  return object_sizes[osi->object_size_type][varno];
+}
+
+/* Return a size tree for VARNO corresponding to OSI.  If WHOLE is true, return
+   the whole object size.  Use this for building size expressions based on size
+   of VARNO.  */
 
 static inline tree
 object_sizes_get (struct object_size_info *osi, unsigned varno,
 		  bool whole = false)
 {
-  return TREE_VEC_ELT (object_sizes[osi->object_size_type][varno], whole);
+  tree ret = TREE_VEC_ELT (object_sizes[osi->object_size_type][varno], whole);
+  int object_size_type = osi->object_size_type;
+
+  if (object_size_type & OST_DYNAMIC)
+    {
+      if (TREE_CODE (ret) == MODIFY_EXPR)
+	return TREE_OPERAND (ret, 0);
+      else if (TREE_CODE (ret) == TREE_VEC)
+	return TREE_VEC_ELT (ret, TREE_VEC_LENGTH (ret) - 1);
+      else
+	gcc_checking_assert (is_gimple_variable (ret)
+			     || TREE_CODE (ret) == INTEGER_CST);
+    }
+
+  return ret;
 }
 
 /* Set size for VARNO corresponding to OSI to VAL.  */
@@ -164,35 +203,127 @@ object_sizes_initialize (struct object_size_info *osi, unsigned varno,
 			 tree val, tree wholeval)
 {
   int object_size_type = osi->object_size_type;
+  tree res;
+
+  if (osi->pass != 0)
+    res = object_sizes_get_raw (osi, varno);
+  else
+    {
+      res = make_tree_vec (2);
+      object_sizes[object_size_type][varno] = res;
+    }
 
-  tree res = make_tree_vec (2);
   TREE_VEC_ELT (res, 0) = val;
   TREE_VEC_ELT (res, 1) = wholeval;
+}
 
-  object_sizes[object_size_type][varno] = res;
+/* Return a MODIFY_EXPR for cases where SSA and EXPR have the same type.  The
+   TREE_VEC is returned only in case of PHI nodes.  */
+
+static tree
+bundle_sizes (tree ssa, tree expr)
+{
+  gcc_checking_assert (TREE_TYPE (ssa) == sizetype);
+
+  if (!TREE_TYPE (expr))
+    {
+      gcc_checking_assert (TREE_CODE (expr) == TREE_VEC);
+      TREE_VEC_ELT (expr, TREE_VEC_LENGTH (expr) - 1) = ssa;
+      return expr;
+    }
+
+  gcc_checking_assert (types_compatible_p (TREE_TYPE (expr), sizetype));
+  return size_binop (MODIFY_EXPR, ssa, expr);
 }
 
 /* Set size for VARNO corresponding to OSI to VAL if it is the new minimum or
-   maximum.  */
+   maximum.  For static sizes, each element of TREE_VEC is always INTEGER_CST
+   throughout the computation.  For dynamic sizes, each element may either be a
+   gimple variable, a MODIFY_EXPR or a TREE_VEC.  The MODIFY_EXPR is for
+   expressions that need to be gimplified.  TREE_VECs are special, they're
+   emitted only for GIMPLE_PHI and the PHI result variable is the last element
+   of the vector.  */
 
-static inline bool
+static bool
 object_sizes_set (struct object_size_info *osi, unsigned varno, tree val,
 		  tree wholeval)
 {
   int object_size_type = osi->object_size_type;
   tree size = object_sizes[object_size_type][varno];
+  bool changed = true;
 
   tree oldval = TREE_VEC_ELT (size, 0);
   tree old_wholeval = TREE_VEC_ELT (size, 1);
 
-  enum tree_code code = object_size_type & OST_MINIMUM ? MIN_EXPR : MAX_EXPR;
+  if (object_size_type & OST_DYNAMIC)
+    {
+      if (bitmap_bit_p (osi->reexamine, varno))
+	{
+	  if (size_unknown_p (val, object_size_type))
+	    {
+	      oldval = object_sizes_get (osi, varno);
+	      old_wholeval = object_sizes_get (osi, varno, true);
+	      bitmap_set_bit (osi->unknowns, SSA_NAME_VERSION (oldval));
+	      bitmap_set_bit (osi->unknowns, SSA_NAME_VERSION (old_wholeval));
+	      bitmap_clear_bit (osi->reexamine, varno);
+	    }
+	  else
+	    {
+	      val = bundle_sizes (oldval, val);
+	      wholeval = bundle_sizes (old_wholeval, wholeval);
+	    }
+	}
+      else
+	{
+	  gcc_checking_assert (size_initval_p (oldval, object_size_type));
+	  gcc_checking_assert (size_initval_p (old_wholeval,
+					       object_size_type));
+	  /* For dynamic object sizes, all object sizes that are not gimple
+	     variables will need to be gimplified.  */
+	  if (TREE_CODE (wholeval) != INTEGER_CST
+	      && !is_gimple_variable (wholeval))
+	    {
+	      bitmap_set_bit (osi->reexamine, varno);
+	      wholeval = bundle_sizes (make_ssa_name (sizetype), wholeval);
+	    }
+	  if (TREE_CODE (val) != INTEGER_CST && !is_gimple_variable (val))
+	    {
+	      bitmap_set_bit (osi->reexamine, varno);
+	      val = bundle_sizes (make_ssa_name (sizetype), val);
+	    }
+	  /* If the new value is a temporary variable, mark it for
+	     reexamination.  */
+	  else if (TREE_CODE (val) == SSA_NAME && !SSA_NAME_DEF_STMT (val))
+	    bitmap_set_bit (osi->reexamine, varno);
+	}
+    }
+  else
+    {
+      enum tree_code code = (object_size_type & OST_MINIMUM
+			     ? MIN_EXPR : MAX_EXPR);
 
-  val = size_binop (code, val, oldval);
-  wholeval = size_binop (code, wholeval, old_wholeval);
+      val = size_binop (code, val, oldval);
+      wholeval = size_binop (code, wholeval, old_wholeval);
+      changed = tree_int_cst_compare (val, oldval) != 0;
+    }
 
   TREE_VEC_ELT (size, 0) = val;
   TREE_VEC_ELT (size, 1) = wholeval;
-  return tree_int_cst_compare (oldval, val) != 0;
+  return changed;
+}
+
+/* Set temporary SSA names for object size and whole size to resolve dependency
+   loops in dynamic size computation.  */
+
+static inline void
+object_sizes_set_temp (struct object_size_info *osi, unsigned varno)
+{
+  tree val = object_sizes_get (osi, varno);
+
+  if (size_initval_p (val, osi->object_size_type))
+    object_sizes_set (osi, varno,
+		      make_ssa_name (sizetype),
+		      make_ssa_name (sizetype));
 }
 
 /* Initialize OFFSET_LIMIT variable.  */
@@ -214,14 +345,15 @@ static tree
 size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE)
 {
   gcc_checking_assert (TREE_CODE (offset) == INTEGER_CST);
-  gcc_checking_assert (TREE_CODE (sz) == INTEGER_CST);
   gcc_checking_assert (types_compatible_p (TREE_TYPE (sz), sizetype));
 
   /* For negative offsets, if we have a distinct WHOLESIZE, use it to get a net
      offset from the whole object.  */
-  if (wholesize && tree_int_cst_compare (sz, wholesize))
+  if (wholesize
+      && (TREE_CODE (sz) != INTEGER_CST
+	  || TREE_CODE (wholesize) != INTEGER_CST
+	  || tree_int_cst_compare (sz, wholesize)))
     {
-      gcc_checking_assert (TREE_CODE (wholesize) == INTEGER_CST);
       gcc_checking_assert (types_compatible_p (TREE_TYPE (wholesize),
 					       sizetype));
 
@@ -238,13 +370,16 @@ size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE)
   if (!types_compatible_p (TREE_TYPE (offset), sizetype))
     fold_convert (sizetype, offset);
 
-  if (integer_zerop (offset))
-    return sz;
+  if (TREE_CODE (offset) == INTEGER_CST)
+    {
+      if (integer_zerop (offset))
+	return sz;
 
-  /* Negative or too large offset even after adjustment, cannot be within
-     bounds of an object.  */
-  if (compare_tree_int (offset, offset_limit) > 0)
-    return size_zero_node;
+      /* Negative or too large offset even after adjustment, cannot be within
+	 bounds of an object.  */
+      if (compare_tree_int (offset, offset_limit) > 0)
+	return size_zero_node;
+    }
 
   return size_binop (MINUS_EXPR, size_binop (MAX_EXPR, sz, offset), offset);
 }
@@ -681,6 +816,205 @@ pass_through_call (const gcall *call)
   return NULL_TREE;
 }
 
+/* Emit PHI nodes for size expressions fo.  */
+
+static void
+emit_phi_nodes (gimple *stmt, tree size_expr)
+{
+  tree size = TREE_VEC_ELT (size_expr, 0);
+  tree wholesize = TREE_VEC_ELT (size_expr, 1);
+
+  tree phires = TREE_VEC_ELT (wholesize, TREE_VEC_LENGTH (wholesize) - 1);
+  gphi *wholephi = create_phi_node (phires, gimple_bb (stmt));
+  phires = TREE_VEC_ELT (size, TREE_VEC_LENGTH (size) - 1);
+  gphi *phi = create_phi_node (phires, gimple_bb (stmt));
+  gphi *obj_phi =  as_a <gphi *> (stmt);
+
+  gcc_checking_assert (TREE_CODE (wholesize) == TREE_VEC);
+  gcc_checking_assert (TREE_CODE (size) == TREE_VEC);
+
+  for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++)
+    {
+      gimple_seq seq = NULL;
+      tree wsz = TREE_VEC_ELT (wholesize, i);
+      tree sz = TREE_VEC_ELT (size, i);
+
+      /* If we built an expression, we will need to build statements
+	 and insert them on the edge right away.  */
+      if (!is_gimple_variable (wsz))
+	wsz = force_gimple_operand (wsz, &seq, true, NULL);
+      if (!is_gimple_variable (sz))
+	{
+	  gimple_seq s;
+	  sz = force_gimple_operand (sz, &s, true, NULL);
+	  gimple_seq_add_seq (&seq, s);
+	}
+
+      if (seq)
+	{
+	  edge e = gimple_phi_arg_edge (obj_phi, i);
+
+	  /* Put the size definition before the last statement of the source
+	     block of the PHI edge.  This ensures that any branches at the end
+	     of the source block remain the last statement.  We are OK even if
+	     the last statement is the definition of the object since it will
+	     succeed any definitions that contribute to its size and the size
+	     expression will succeed them too.  */
+	  gimple_stmt_iterator gsi = gsi_last_bb (e->src);
+	  gsi_insert_seq_before (&gsi, seq, GSI_CONTINUE_LINKING);
+	}
+
+      add_phi_arg (wholephi, wsz,
+		   gimple_phi_arg_edge (obj_phi, i),
+		   gimple_phi_arg_location (obj_phi, i));
+
+      add_phi_arg (phi, sz,
+		   gimple_phi_arg_edge (obj_phi, i),
+		   gimple_phi_arg_location (obj_phi, i));
+    }
+}
+
+/* Descend through EXPR and return size_unknown if it uses any SSA variable
+   object_size_set or object_size_set_temp generated, which turned out to be
+   size_unknown, as noted in UNKNOWNS.  */
+
+static tree
+propagate_unknowns (object_size_info *osi, tree expr)
+{
+  int object_size_type = osi->object_size_type;
+
+  switch (TREE_CODE (expr))
+    {
+    case SSA_NAME:
+      if (bitmap_bit_p (osi->unknowns, SSA_NAME_VERSION (expr)))
+	return size_unknown (object_size_type);
+      return expr;
+
+    case MIN_EXPR:
+    case MAX_EXPR:
+	{
+	  tree res = propagate_unknowns (osi, TREE_OPERAND (expr, 0));
+	  if (size_unknown_p (res, object_size_type))
+	    return res;
+
+	  res = propagate_unknowns (osi, TREE_OPERAND (expr, 1));
+	  if (size_unknown_p (res, object_size_type))
+	    return res;
+
+	  return expr;
+	}
+    case MODIFY_EXPR:
+	{
+	  tree res = propagate_unknowns (osi, TREE_OPERAND (expr, 1));
+	  if (size_unknown_p (res, object_size_type))
+	    return res;
+	  return expr;
+	}
+    case TREE_VEC:
+      for (int i = 0; i < TREE_VEC_LENGTH (expr); i++)
+	{
+	  tree res = propagate_unknowns (osi, TREE_VEC_ELT (expr, i));
+	  if (size_unknown_p (res, object_size_type))
+	    return res;
+	}
+      return expr;
+    case PLUS_EXPR:
+    case MINUS_EXPR:
+	{
+	  tree res = propagate_unknowns (osi, TREE_OPERAND (expr, 0));
+	  if (size_unknown_p (res, object_size_type))
+	    return res;
+
+	  return expr;
+	}
+    default:
+      return expr;
+    }
+}
+
+/* Walk through size expressions that need reexamination and generate
+   statements for them.  */
+
+static void
+gimplify_size_expressions (object_size_info *osi)
+{
+  int object_size_type = osi->object_size_type;
+  bitmap_iterator bi;
+  unsigned int i;
+  bool changed;
+
+  /* Step 1: Propagate unknowns into expressions.  */
+  bitmap reexamine = BITMAP_ALLOC (NULL);
+  bitmap_copy (reexamine, osi->reexamine);
+  do
+    {
+      changed = false;
+      EXECUTE_IF_SET_IN_BITMAP (reexamine, 0, i, bi)
+	{
+	  tree cur = object_sizes_get_raw (osi, i);
+
+	  if (size_unknown_p (propagate_unknowns (osi, cur), object_size_type))
+	    {
+	      object_sizes_set (osi, i,
+				size_unknown (object_size_type),
+				size_unknown (object_size_type));
+	      changed = true;
+	    }
+	}
+      bitmap_copy (reexamine, osi->reexamine);
+    }
+  while (changed);
+
+  /* Release all unknowns.  */
+  EXECUTE_IF_SET_IN_BITMAP (osi->unknowns, 0, i, bi)
+    release_ssa_name (ssa_name (i));
+
+  /* Expand all size expressions to put their definitions close to the objects
+     for whom size is being computed.  */
+  EXECUTE_IF_SET_IN_BITMAP (osi->reexamine, 0, i, bi)
+    {
+      gimple_seq seq = NULL;
+      tree size_expr = object_sizes_get_raw (osi, i);
+
+      gimple *stmt = SSA_NAME_DEF_STMT (ssa_name (i));
+      enum gimple_code code = gimple_code (stmt);
+
+      /* PHI nodes need special attention.  */
+      if (code == GIMPLE_PHI)
+	emit_phi_nodes (stmt, size_expr);
+      else
+	{
+	  /* Bundle wholesize in with the size to gimplify if needed.  */
+	  if (!is_gimple_variable (TREE_VEC_ELT (size_expr, 1))
+	      && TREE_CODE (TREE_VEC_ELT (size_expr, 1)) != INTEGER_CST)
+	    size_expr = size_binop (COMPOUND_EXPR,
+				    TREE_VEC_ELT (size_expr, 1),
+				    TREE_VEC_ELT (size_expr, 0));
+	  else if (!is_gimple_variable (TREE_VEC_ELT (size_expr, 0))
+		   && TREE_CODE (TREE_VEC_ELT (size_expr, 0)) != INTEGER_CST)
+	    size_expr = TREE_VEC_ELT (size_expr, 0);
+	  else
+	    size_expr = NULL_TREE;
+
+	  if (size_expr)
+	    {
+	      gimple_stmt_iterator gsi;
+	      if (code == GIMPLE_NOP)
+		gsi = gsi_start_bb (single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)));
+	      else
+		gsi = gsi_for_stmt (stmt);
+
+	      force_gimple_operand (size_expr, &seq, true, NULL);
+	      gsi_insert_seq_before (&gsi, seq, GSI_CONTINUE_LINKING);
+	    }
+	}
+
+      /* We're done, so replace the MODIFY_EXPRs with the SSA names.  */
+      object_sizes_initialize (osi, i,
+			       object_sizes_get (osi, i),
+			       object_sizes_get (osi, i, true));
+    }
+}
 
 /* Compute __builtin_object_size value for PTR and set *PSIZE to
    the resulting value.  If the declared object is known and PDECL
@@ -759,9 +1093,15 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 
       osi.visited = BITMAP_ALLOC (NULL);
       osi.reexamine = BITMAP_ALLOC (NULL);
-      osi.depths = NULL;
-      osi.stack = NULL;
-      osi.tos = NULL;
+
+      if (object_size_type & OST_DYNAMIC)
+	osi.unknowns = BITMAP_ALLOC (NULL);
+      else
+	{
+	  osi.depths = NULL;
+	  osi.stack = NULL;
+	  osi.tos = NULL;
+	}
 
       /* First pass: walk UD chains, compute object sizes that
 	 can be computed.  osi.reexamine bitmap at the end will
@@ -771,6 +1111,14 @@ compute_builtin_object_size (tree ptr, int object_size_type,
       osi.changed = false;
       collect_object_sizes_for (&osi, ptr);
 
+      if (object_size_type & OST_DYNAMIC)
+	{
+	  osi.pass = 1;
+	  gimplify_size_expressions (&osi);
+	  BITMAP_FREE (osi.unknowns);
+	  bitmap_clear (osi.reexamine);
+	}
+
       /* Second pass: keep recomputing object sizes of variables
 	 that need reexamination, until no object sizes are
 	 increased or all object sizes are computed.  */
@@ -1015,6 +1363,25 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   return reexamine;
 }
 
+static void
+dynamic_object_size (struct object_size_info *osi, tree var,
+		     tree *size, tree *wholesize)
+{
+  int object_size_type = osi->object_size_type;
+
+  if (TREE_CODE (var) == SSA_NAME)
+    {
+      unsigned varno = SSA_NAME_VERSION (var);
+
+      collect_object_sizes_for (osi, var);
+      *size = object_sizes_get (osi, varno);
+      *wholesize = object_sizes_get (osi, varno, true);
+    }
+  else if (TREE_CODE (var) == ADDR_EXPR)
+    addr_object_size (osi, var, object_size_type, size, wholesize);
+  else
+    *size = *wholesize = size_unknown (object_size_type);
+}
 
 /* Compute object_sizes for VAR, defined at STMT, which is
    a COND_EXPR.  Return true if the object size might need reexamination
@@ -1036,6 +1403,33 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   then_ = gimple_assign_rhs2 (stmt);
   else_ = gimple_assign_rhs3 (stmt);
 
+  if (object_size_type & OST_DYNAMIC)
+    {
+      tree then_size, then_wholesize, else_size, else_wholesize;
+
+      dynamic_object_size (osi, then_, &then_size, &then_wholesize);
+      if (!size_unknown_p (then_size, object_size_type))
+	dynamic_object_size (osi, else_, &else_size, &else_wholesize);
+
+      tree cond_size, cond_wholesize;
+      if (size_unknown_p (then_size, object_size_type)
+	  || size_unknown_p (else_size, object_size_type))
+	cond_size = cond_wholesize = size_unknown (object_size_type);
+      else
+	{
+	  cond_size = fold_build3 (COND_EXPR, sizetype,
+				   gimple_assign_rhs1 (stmt),
+				   then_size, else_size);
+	  cond_wholesize = fold_build3 (COND_EXPR, sizetype,
+					gimple_assign_rhs1 (stmt),
+					then_wholesize, else_wholesize);
+	}
+
+      object_sizes_set (osi, varno, cond_size, cond_wholesize);
+
+      return false;
+    }
+
   if (TREE_CODE (then_) == SSA_NAME)
     reexamine |= merge_object_sizes (osi, var, then_);
   else
@@ -1052,6 +1446,44 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   return reexamine;
 }
 
+/* Compute an object size expression for VAR, which is the result of a PHI
+   node.  */
+
+static void
+phi_dynamic_object_size (struct object_size_info *osi, tree var)
+{
+  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);
+
+  /* The extra space is for the PHI result at the end, which object_sizes_set
+     sets for us.  */
+  tree sizes = make_tree_vec (num_args + 1);
+  tree wholesizes = make_tree_vec (num_args + 1);
+
+  /* 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 size, wholesize;
+
+      dynamic_object_size (osi, rhs, &size, &wholesize);
+
+      if (size_unknown_p (size, object_size_type))
+       break;
+
+      TREE_VEC_ELT (sizes, i) = size;
+      TREE_VEC_ELT (wholesizes, i) = wholesize;
+    }
+
+  if (i < num_args)
+    sizes = wholesizes = size_unknown (object_size_type);
+
+  object_sizes_set (osi, varno, sizes, wholesizes);
+}
+
 /* Compute object sizes for VAR.
    For ADDR_EXPR an object size is the number of remaining bytes
    to the end of the object (where what is considered an object depends on
@@ -1097,6 +1529,9 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 	{
 	  /* Found a dependency loop.  Mark the variable for later
 	     re-examination.  */
+	  if (object_size_type & OST_DYNAMIC)
+	    object_sizes_set_temp (osi, varno);
+
 	  bitmap_set_bit (osi->reexamine, varno);
 	  if (dump_file && (dump_flags & TDF_DETAILS))
 	    {
@@ -1178,6 +1613,12 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
       {
 	unsigned i;
 
+	if (object_size_type & OST_DYNAMIC)
+	  {
+	    phi_dynamic_object_size (osi, var);
+	    break;
+	  }
+
 	for (i = 0; i < gimple_phi_num_args (stmt); i++)
 	  {
 	    tree rhs = gimple_phi_arg (stmt, i)->def;
@@ -1200,7 +1641,8 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
   if (! reexamine || object_sizes_unknown_p (object_size_type, varno))
     {
       bitmap_set_bit (computed[object_size_type], varno);
-      bitmap_clear_bit (osi->reexamine, varno);
+      if (!(object_size_type & OST_DYNAMIC))
+	bitmap_clear_bit (osi->reexamine, varno);
     }
   else
     {
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v3 6/8] tree-object-size: Handle function parameters
  2021-11-26  5:28 ` [PATCH v3 0/8] __builtin_dynamic_object_size Siddhesh Poyarekar
                     ` (4 preceding siblings ...)
  2021-11-26  5:28   ` [PATCH v3 5/8] tree-object-size: Support dynamic sizes in conditions Siddhesh Poyarekar
@ 2021-11-26  5:28   ` Siddhesh Poyarekar
  2021-11-26  5:28   ` [PATCH v3 7/8] tree-object-size: Handle GIMPLE_CALL Siddhesh Poyarekar
                     ` (2 subsequent siblings)
  8 siblings, 0 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-26  5:28 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Handle hints provided by __attribute__ ((access (...))) to compute
dynamic sizes for objects.

gcc/ChangeLog:

	* tree-object-size.c: Include tree-dfa.h.
	(parm_object_size): New function.
	(collect_object_sizes_for): Call it.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-dynamic-object-size-0.c (test_parmsz_simple):
	New function.
	(main): Call it.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 .../gcc.dg/builtin-dynamic-object-size-0.c    | 11 ++++
 gcc/tree-object-size.c                        | 50 ++++++++++++++++++-
 2 files changed, 60 insertions(+), 1 deletion(-)

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 ddedf6a49bd..ce0f4eb17f3 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -46,6 +46,14 @@ test_deploop (size_t sz, size_t cond)
   return __builtin_dynamic_object_size (bin, 0);
 }
 
+size_t
+__attribute__ ((access (__read_write__, 1, 2)))
+__attribute__ ((noinline))
+test_parmsz_simple (void *obj, size_t sz)
+{
+  return __builtin_dynamic_object_size (obj, 0);
+}
+
 unsigned nfails = 0;
 
 #define FAIL() ({ \
@@ -64,6 +72,9 @@ main (int argc, char **argv)
     FAIL ();
   if (test_deploop (128, 129) != 32)
     FAIL ();
+  if (test_parmsz_simple (argv[0], __builtin_strlen (argv[0]) + 1)
+      != __builtin_strlen (argv[0]) + 1)
+    FAIL ();
 
   if (nfails > 0)
     __builtin_abort ();
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index 5b4dcb619cd..48b1ec6e26a 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-fold.h"
 #include "gimple-iterator.h"
 #include "tree-cfg.h"
+#include "tree-dfa.h"
 #include "stringpool.h"
 #include "attribs.h"
 #include "builtins.h"
@@ -1446,6 +1447,53 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   return reexamine;
 }
 
+/* Find size of an object passed as a parameter to the function.  */
+
+static void
+parm_object_size (struct object_size_info *osi, tree var)
+{
+  int object_size_type = osi->object_size_type;
+  tree parm = SSA_NAME_VAR (var);
+
+  if (!(object_size_type & OST_DYNAMIC) || !POINTER_TYPE_P (TREE_TYPE (parm)))
+    expr_object_size (osi, var, parm);
+
+  /* Look for access attribute.  */
+  rdwr_map rdwr_idx;
+
+  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)
+    {
+      tree fnargs = DECL_ARGUMENTS (fndecl);
+      tree arg = NULL_TREE;
+      unsigned argpos = 0;
+
+      /* Walk through the parameters to pick the size parameter and safely
+	 scale it by the type size.  */
+      for (arg = fnargs; argpos != access->sizarg && arg;
+	   arg = TREE_CHAIN (arg), ++argpos);
+
+      if (arg != NULL_TREE && INTEGRAL_TYPE_P (TREE_TYPE (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);
+	    }
+	}
+    }
+  if (!sz)
+    sz = size_unknown (object_size_type);
+
+  object_sizes_set (osi, SSA_NAME_VERSION (var), sz, sz);
+}
+
 /* Compute an object size expression for VAR, which is the result of a PHI
    node.  */
 
@@ -1603,7 +1651,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
     case GIMPLE_NOP:
       if (SSA_NAME_VAR (var)
 	  && TREE_CODE (SSA_NAME_VAR (var)) == PARM_DECL)
-	expr_object_size (osi, var, SSA_NAME_VAR (var));
+	parm_object_size (osi, var);
       else
 	/* Uninitialized SSA names point nowhere.  */
 	unknown_object_size (osi, var);
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v3 7/8] tree-object-size: Handle GIMPLE_CALL
  2021-11-26  5:28 ` [PATCH v3 0/8] __builtin_dynamic_object_size Siddhesh Poyarekar
                     ` (5 preceding siblings ...)
  2021-11-26  5:28   ` [PATCH v3 6/8] tree-object-size: Handle function parameters Siddhesh Poyarekar
@ 2021-11-26  5:28   ` Siddhesh Poyarekar
  2021-11-26  5:28   ` [PATCH v3 8/8] tree-object-size: Dynamic sizes for ADDR_EXPR Siddhesh Poyarekar
  2021-11-26  5:38   ` [PATCH v3 0/8] __builtin_dynamic_object_size Siddhesh Poyarekar
  8 siblings, 0 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-26  5:28 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Handle non-constant expressions in GIMPLE_CALL arguments.  Also handle
alloca.

gcc/ChangeLog:

	* tree-object-size.c (alloc_object_size): Make and return
	non-constant size expression.
	(call_object_size): Return expression or unknown based on
	whether dynamic object size is requested.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-dynamic-object-size-0.c: Add new tests.
	* gcc.dg/builtin-object-size-1.c (test1)
	[__builtin_object_size]: Alter expected result for dynamic
	object size.
	* gcc.dg/builtin-object-size-2.c (test1)
	[__builtin_object_size]: Likewise.
	* gcc.dg/builtin-object-size-3.c (test1)
	[__builtin_object_size]: Likewise.
	* gcc.dg/builtin-object-size-4.c (test1)
	[__builtin_object_size]: Likewise.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 .../gcc.dg/builtin-dynamic-object-size-0.c    | 227 +++++++++++++++++-
 gcc/testsuite/gcc.dg/builtin-object-size-1.c  |   7 +
 gcc/testsuite/gcc.dg/builtin-object-size-2.c  |  14 ++
 gcc/testsuite/gcc.dg/builtin-object-size-3.c  |   7 +
 gcc/testsuite/gcc.dg/builtin-object-size-4.c  |  14 ++
 gcc/tree-object-size.c                        |  22 +-
 6 files changed, 282 insertions(+), 9 deletions(-)

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 ce0f4eb17f3..2db0e0d1aa2 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -4,12 +4,71 @@
 typedef __SIZE_TYPE__ size_t;
 #define abort __builtin_abort
 
+void *
+__attribute__ ((alloc_size (1)))
+__attribute__ ((__nothrow__ , __leaf__))
+__attribute__ ((noinline))
+alloc_func (size_t sz)
+{
+  return __builtin_malloc (sz);
+}
+
+void *
+__attribute__ ((alloc_size (1, 2)))
+__attribute__ ((__nothrow__ , __leaf__))
+__attribute__ ((noinline))
+calloc_func (size_t cnt, size_t sz)
+{
+  return __builtin_calloc (cnt, sz);
+}
+
+void *
+__attribute__ ((noinline))
+unknown_allocator (size_t cnt, size_t sz)
+{
+  return __builtin_calloc (cnt, sz);
+}
+
+size_t
+__attribute__ ((noinline))
+test_unknown (size_t cnt, size_t sz)
+{
+  void *ret = unknown_allocator (cnt, sz);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+/* Malloc-like allocator.  */
+
+size_t
+__attribute__ ((noinline))
+test_malloc (size_t sz)
+{
+  void *ret = alloc_func (sz);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc (size_t sz)
+{
+  void *ret = __builtin_malloc (sz);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_cond (int cond)
+{
+  void *ret = __builtin_malloc (cond ? 32 : 64);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
 size_t
 __attribute__ ((noinline))
 test_builtin_malloc_condphi (int cond)
 {
   void *ret;
- 
+
   if (cond)
     ret = __builtin_malloc (32);
   else
@@ -18,6 +77,79 @@ test_builtin_malloc_condphi (int cond)
   return __builtin_dynamic_object_size (ret, 0);
 }
 
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi2 (int cond, size_t in)
+{
+  void *ret;
+
+  if (cond)
+    ret = __builtin_malloc (in);
+  else
+    ret = __builtin_malloc (64);
+
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi3 (int cond, size_t in, size_t in2)
+{
+  void *ret;
+
+  if (cond)
+    ret = __builtin_malloc (in);
+  else
+    ret = __builtin_malloc (in2);
+
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi4 (size_t sz, int cond)
+{
+  char *a = __builtin_malloc (sz);
+  char b[sz / 2];
+
+  return __builtin_dynamic_object_size (cond ? b : (void *) &a, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi5 (size_t sz, int cond, char *c)
+{
+  char *a = __builtin_malloc (sz);
+
+  return __builtin_dynamic_object_size (cond ? c : (void *) &a, 0);
+}
+
+/* Calloc-like allocator.  */
+
+size_t
+__attribute__ ((noinline))
+test_calloc (size_t cnt, size_t sz)
+{
+  void *ret = calloc_func (cnt, sz);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_calloc (size_t cnt, size_t sz)
+{
+  void *ret = __builtin_calloc (cnt, sz);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_calloc_cond (int cond1, int cond2)
+{
+  void *ret = __builtin_calloc (cond1 ? 32 : 64, cond2 ? 1024 : 16);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
 size_t
 __attribute__ ((noinline))
 test_builtin_calloc_condphi (size_t cnt, size_t sz, int cond)
@@ -33,6 +165,47 @@ test_builtin_calloc_condphi (size_t cnt, size_t sz, int cond)
   return __builtin_dynamic_object_size (cond ? ch : (void *) &bin, 0);
 }
 
+/* Passthrough functions.  */
+
+size_t
+__attribute__ ((noinline))
+test_passthrough (size_t sz, char *in)
+{
+  char *bin = __builtin_malloc (sz);
+  char *dest = __builtin_memcpy (bin, in, sz);
+
+  return __builtin_dynamic_object_size (dest, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_passthrough_nonssa (char *in)
+{
+  char bin[__builtin_strlen (in) + 1];
+  char *dest = __builtin_memcpy (bin, in, __builtin_strlen (in) + 1);
+
+  return __builtin_dynamic_object_size (dest, 0);
+}
+
+/* Variable length arrays.  */
+size_t
+__attribute__ ((noinline))
+test_dynarray (size_t sz)
+{
+  char bin[sz];
+
+  return __builtin_dynamic_object_size (bin, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_dynarray_cond (int cond)
+{
+  char bin[cond ? 8 : 16];
+
+  return __builtin_dynamic_object_size (bin, 0);
+}
+
 size_t
 __attribute__ ((noinline))
 test_deploop (size_t sz, size_t cond)
@@ -41,7 +214,7 @@ test_deploop (size_t sz, size_t cond)
 
   for (size_t i = 0; i < sz; i++)
     if (i == cond)
-      bin = __builtin_alloca (64);
+      bin = __builtin_alloca (sz);
 
   return __builtin_dynamic_object_size (bin, 0);
 }
@@ -64,12 +237,62 @@ unsigned nfails = 0;
 int
 main (int argc, char **argv)
 {
+  size_t outsz = test_unknown (32, 42);
+  if (outsz != -1 && outsz != 32)
+    FAIL ();
+  if (test_malloc (2048) != 2048)
+    FAIL ();
+  if (test_builtin_malloc (2048) != 2048)
+    FAIL ();
+  if (test_builtin_malloc_cond (1) != 32)
+    FAIL ();
+  if (test_builtin_malloc_cond (0) != 64)
+    FAIL ();
   if (test_builtin_malloc_condphi (1) != 32)
     FAIL ();
   if (test_builtin_malloc_condphi (0) != 64)
     FAIL ();
+  if (test_builtin_malloc_condphi2 (1, 128) != 128)
+    FAIL ();
+  if (test_builtin_malloc_condphi2 (0, 128) != 64)
+    FAIL ();
+  if (test_builtin_malloc_condphi3 (1, 128, 256) != 128)
+    FAIL ();
+  if (test_builtin_malloc_condphi3 (0, 128, 256) != 256)
+    FAIL ();
+  if (test_builtin_malloc_condphi4 (128, 1) != 64)
+    FAIL ();
+  if (test_builtin_malloc_condphi4 (128, 0) != sizeof (void *))
+    FAIL ();
+  if (test_builtin_malloc_condphi5 (128, 0, argv[0]) != -1)
+    FAIL ();
+  if (test_calloc (2048, 4) != 2048 * 4)
+    FAIL ();
+  if (test_builtin_calloc (2048, 8) != 2048 * 8)
+    FAIL ();
+  if (test_builtin_calloc_cond (0, 0) != 64 * 16)
+    FAIL ();
+  if (test_builtin_calloc_cond (1, 1) != 32 * 1024)
+    FAIL ();
+  if (test_builtin_calloc_condphi (128, 1, 1) != 128)
+    FAIL ();
   if (test_builtin_calloc_condphi (128, 1, 0) == 128)
     FAIL ();
+  if (test_builtin_calloc_condphi (128, 1, 0) == -1)
+    FAIL ();
+  if (test_passthrough (__builtin_strlen (argv[0]) + 1, argv[0])
+      != __builtin_strlen (argv[0]) + 1)
+    FAIL ();
+  if (test_passthrough_nonssa (argv[0]) != __builtin_strlen (argv[0]) + 1)
+    FAIL ();
+  if (test_dynarray (__builtin_strlen (argv[0])) != __builtin_strlen (argv[0]))
+    FAIL ();
+  if (test_dynarray_cond (0) != 16)
+    FAIL ();
+  if (test_dynarray_cond (1) != 8)
+    FAIL ();
+  if (test_deploop (128, 4) != 128)
+    FAIL ();
   if (test_deploop (128, 129) != 32)
     FAIL ();
   if (test_parmsz_simple (argv[0], __builtin_strlen (argv[0]) + 1)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
index 265c87ed6fb..06d442796cb 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
@@ -135,10 +135,17 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&extb[5], 0) != sizeof (extb) - 5)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (var, 0) != x + 10)
+    abort ();
+  if (__builtin_object_size (var + 10, 0) != x)
+    abort ();
+#else
   if (__builtin_object_size (var, 0) != (size_t) -1)
     abort ();
   if (__builtin_object_size (var + 10, 0) != (size_t) -1)
     abort ();
+#endif
   if (__builtin_object_size (&var[5], 0) != (size_t) -1)
     abort ();
   if (__builtin_object_size (zerol, 0) != 0)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
index 5051fea47c3..2364f2d6afd 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-2.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
@@ -137,16 +137,30 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&extc[5].c[3], 1) != (size_t) -1)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (var, 1) != x + 10)
+    abort ();
+  if (__builtin_object_size (var + 10, 1) != x)
+    abort ();
+#else
   if (__builtin_object_size (var, 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (var + 10, 1) != (size_t) -1)
     abort ();
+#endif
   if (__builtin_object_size (&var[5], 1) != (size_t) -1)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (vara, 1) != (x + 10) * sizeof (struct A))
+    abort ();
+  if (__builtin_object_size (vara + 10, 1) != x * sizeof (struct A))
+    abort ();    
+#else
   if (__builtin_object_size (vara, 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (vara + 10, 1) != (size_t) -1)
     abort ();    
+#endif
   if (__builtin_object_size (&vara[5], 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (&vara[0].a, 1) != sizeof (vara[0].a))
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
index 1d92627266b..753ee4a1a4f 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-3.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
@@ -140,10 +140,17 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&extb[5], 2) != sizeof (extb) - 5)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (var, 2) != x + 10)
+    abort ();
+  if (__builtin_object_size (var + 10, 2) != x)
+    abort ();
+#else
   if (__builtin_object_size (var, 2) != 0)
     abort ();
   if (__builtin_object_size (var + 10, 2) != 0)
     abort ();
+#endif
   if (__builtin_object_size (&var[5], 2) != 0)
     abort ();
   if (__builtin_object_size (zerol, 2) != 0)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
index 9da3537a5f7..c383385e060 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-4.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
@@ -150,16 +150,30 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&extc[5].c[3], 3) != 0)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (var, 3) != x + 10)
+    abort ();
+  if (__builtin_object_size (var + 10, 3) != x)
+    abort ();
+#else
   if (__builtin_object_size (var, 3) != 0)
     abort ();
   if (__builtin_object_size (var + 10, 3) != 0)
     abort ();
+#endif
   if (__builtin_object_size (&var[5], 3) != 0)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (vara, 3) != (x + 10) * sizeof (struct A))
+    abort ();
+  if (__builtin_object_size (vara + 10, 3) != x * sizeof (struct A))
+    abort ();    
+#else
   if (__builtin_object_size (vara, 3) != 0)
     abort ();
   if (__builtin_object_size (vara + 10, 3) != 0)
     abort ();    
+#endif
   if (__builtin_object_size (&vara[5], 3) != 0)
     abort ();
   if (__builtin_object_size (&vara[0].a, 3) != sizeof (vara[0].a))
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index 48b1ec6e26a..f03714c4f18 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -755,7 +755,8 @@ alloc_object_size (const gcall *call, int object_size_type)
   gcc_assert (is_gimple_call (call));
 
   tree calltype;
-  if (tree callfn = gimple_call_fndecl (call))
+  tree callfn = gimple_call_fndecl (call);
+  if (callfn)
     calltype = TREE_TYPE (callfn);
   else
     calltype = gimple_call_fntype (call);
@@ -775,12 +776,13 @@ alloc_object_size (const gcall *call, int object_size_type)
       if (TREE_CHAIN (p))
         arg2 = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (p)))-1;
     }
+  else if (gimple_call_builtin_p (call, BUILT_IN_NORMAL)
+	   && callfn && ALLOCA_FUNCTION_CODE_P (DECL_FUNCTION_CODE (callfn)))
+  arg1 = 0;
 
-  if (arg1 < 0 || arg1 >= (int)gimple_call_num_args (call)
-      || TREE_CODE (gimple_call_arg (call, arg1)) != INTEGER_CST
-      || (arg2 >= 0
-	  && (arg2 >= (int)gimple_call_num_args (call)
-	      || TREE_CODE (gimple_call_arg (call, arg2)) != INTEGER_CST)))
+  /* Non-const arguments are OK here, let the caller handle constness.  */
+  if (arg1 < 0 || arg1 >= (int) gimple_call_num_args (call)
+      || arg2 >= (int) gimple_call_num_args (call))
     return size_unknown (object_size_type);
 
   tree bytes = NULL_TREE;
@@ -791,7 +793,10 @@ alloc_object_size (const gcall *call, int object_size_type)
   else if (arg1 >= 0)
     bytes = fold_convert (sizetype, gimple_call_arg (call, arg1));
 
-  return bytes;
+  if (bytes)
+    return STRIP_NOPS (bytes);
+
+  return size_unknown (object_size_type);
 }
 
 
@@ -1250,6 +1255,9 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call)
   gcc_assert (osi->pass == 0);
   tree bytes = alloc_object_size (call, object_size_type);
 
+  if (!(object_size_type & OST_DYNAMIC) && TREE_CODE (bytes) != INTEGER_CST)
+    bytes = size_unknown (object_size_type);
+
   object_sizes_set (osi, varno, bytes, bytes);
 }
 
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v3 8/8] tree-object-size: Dynamic sizes for ADDR_EXPR
  2021-11-26  5:28 ` [PATCH v3 0/8] __builtin_dynamic_object_size Siddhesh Poyarekar
                     ` (6 preceding siblings ...)
  2021-11-26  5:28   ` [PATCH v3 7/8] tree-object-size: Handle GIMPLE_CALL Siddhesh Poyarekar
@ 2021-11-26  5:28   ` Siddhesh Poyarekar
  2021-11-26  5:38   ` [PATCH v3 0/8] __builtin_dynamic_object_size Siddhesh Poyarekar
  8 siblings, 0 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-26  5:28 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Allow returning dynamic expressions from ADDR_EXPR for
__builtin_dynamic_object_size and also allow offsets to be dynamic.

gcc/ChangeLog:

	* tree-object-size.c (size_valid_p): New function.
	(size_for_offset): Remove OFFSET constness assertion.
	(addr_object_size): Build dynamic expressions for object
	sizes and use size_valid_p to decide if it is valid for the
	given OBJECT_SIZE_TYPE.
	(compute_builtin_object_size): Allow dynamic offsets when
	computing size at O0.
	(call_object_size): Call size_valid_p.
	(plus_stmt_object_size): Allow non-constant offset and use
	size_valid_p to decide if it is valid for the given
	OBJECT_SIZE_TYPE.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-dynamic-object-size-0.c: Add new tests.
	* gcc.dg/builtin-object-size-1.c (test1)
	[__builtin_object_size]: Adjust expected output for dynamic
	object sizes.
	* gcc.dg/builtin-object-size-2.c (test1)
	[__builtin_object_size]: Likewise.
	* gcc.dg/builtin-object-size-3.c (test1)
	[__builtin_object_size]: Likewise.
	* gcc.dg/builtin-object-size-4.c (test1)
	[__builtin_object_size]: Likewise.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 .../gcc.dg/builtin-dynamic-object-size-0.c    | 158 ++++++++++++++++++
 gcc/testsuite/gcc.dg/builtin-object-size-1.c  |  30 +++-
 gcc/testsuite/gcc.dg/builtin-object-size-2.c  |  43 ++++-
 gcc/testsuite/gcc.dg/builtin-object-size-3.c  |  25 ++-
 gcc/testsuite/gcc.dg/builtin-object-size-4.c  |  17 +-
 gcc/tree-object-size.c                        |  91 +++++-----
 6 files changed, 300 insertions(+), 64 deletions(-)

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 2db0e0d1aa2..4a1f4965ebd 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -219,6 +219,79 @@ test_deploop (size_t sz, size_t cond)
   return __builtin_dynamic_object_size (bin, 0);
 }
 
+/* Address expressions.  */
+
+struct dynarray_struct
+{
+  long a;
+  char c[16];
+  int b;
+};
+
+size_t
+__attribute__ ((noinline))
+test_dynarray_struct (size_t sz, size_t off)
+{
+  struct dynarray_struct bin[sz];
+
+  return __builtin_dynamic_object_size (&bin[off].c, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_dynarray_struct_subobj (size_t sz, size_t off)
+{
+  struct dynarray_struct bin[sz];
+
+  return __builtin_dynamic_object_size (&bin[off].c[4], 1);
+}
+
+size_t
+__attribute__ ((noinline))
+test_dynarray_struct_subobj2 (size_t sz, size_t off, size_t *objsz)
+{
+  struct dynarray_struct2
+    {
+      long a;
+      int b;
+      char c[sz];
+    };
+
+  struct dynarray_struct2 bin;
+
+  *objsz = sizeof (bin);
+
+  return __builtin_dynamic_object_size (&bin.c[off], 1);
+}
+
+size_t
+__attribute__ ((noinline))
+test_substring (size_t sz, size_t off)
+{
+  char str[sz];
+
+  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)))
 __attribute__ ((noinline))
@@ -227,6 +300,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() ({ \
@@ -287,6 +394,31 @@ main (int argc, char **argv)
     FAIL ();
   if (test_dynarray (__builtin_strlen (argv[0])) != __builtin_strlen (argv[0]))
     FAIL ();
+  if (test_dynarray_struct (42, 4) !=
+      ((42 - 4) * sizeof (struct dynarray_struct)
+       - __builtin_offsetof (struct dynarray_struct, c)))
+    FAIL ();
+  if (test_dynarray_struct (42, 48) != 0)
+    FAIL ();
+  if (test_substring (128, 4) != 128 - 4)
+    FAIL ();
+  if (test_substring (128, 142) != 0)
+    FAIL ();
+  if (test_dynarray_struct_subobj (42, 4) != 16 - 4)
+    FAIL ();
+  if (test_dynarray_struct_subobj (42, 48) != 0)
+    FAIL ();
+  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)
@@ -298,6 +430,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/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
index 06d442796cb..161f426ec0b 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
@@ -81,30 +81,56 @@ test1 (void *q, int x)
     r = malloc (30);
   else
     r = calloc (2, 16);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0) != (x < 20 ? 30 : 2 * 16))
+    abort ();
+#else
   /* We may duplicate this test onto the two exit paths.  On one path
      the size will be 32, the other it will be 30.  If we don't duplicate
      this test, then the size will be 32.  */
   if (__builtin_object_size (r, 0) != 2 * 16
       && __builtin_object_size (r, 0) != 30)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
     r = calloc (2, 14);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0) != (x < 20 ? 30 : 2 * 14))
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != 30)
     abort ();
+#endif
   if (x < 30)
     r = malloc (sizeof (a));
   else
     r = &a.a[3];
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0) != (x < 30 ? sizeof (a) : sizeof (a) - 3))
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != sizeof (a))
     abort ();
+#endif
   r = memcpy (r, "a", 2);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0) != (x < 30 ? sizeof (a) : sizeof (a) - 3))
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != sizeof (a))
     abort ();
+#endif
   r = memcpy (r + 2, "b", 2) + 2;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0)
+      != (x < 30 ? sizeof (a) - 4 : sizeof (a) - 7))
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != sizeof (a) - 4)
     abort ();
+#endif
   r = &a.a[4];
   r = memset (r, 'a', 2);
   if (__builtin_object_size (r, 0)
@@ -140,14 +166,16 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (var + 10, 0) != x)
     abort ();
+  if (__builtin_object_size (&var[5], 0) != x + 5)
+    abort ();
 #else
   if (__builtin_object_size (var, 0) != (size_t) -1)
     abort ();
   if (__builtin_object_size (var + 10, 0) != (size_t) -1)
     abort ();
-#endif
   if (__builtin_object_size (&var[5], 0) != (size_t) -1)
     abort ();
+#endif
   if (__builtin_object_size (zerol, 0) != 0)
     abort ();
   if (__builtin_object_size (&zerol, 0) != 0)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
index 2364f2d6afd..2729538da17 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-2.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
@@ -75,30 +75,56 @@ test1 (void *q, int x)
     r = malloc (30);
   else
     r = calloc (2, 16);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 1) != (x < 20 ? 30 : 2 * 16))
+    abort ();
+#else
   /* We may duplicate this test onto the two exit paths.  On one path
      the size will be 32, the other it will be 30.  If we don't duplicate
      this test, then the size will be 32.  */
   if (__builtin_object_size (r, 1) != 2 * 16
       && __builtin_object_size (r, 1) != 30)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
     r = calloc (2, 14);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 1) != (x < 20 ? 30 : 2 * 14))
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != 30)
     abort ();
+#endif
   if (x < 30)
     r = malloc (sizeof (a));
   else
     r = &a.a[3];
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 1) != (x < 30 ? sizeof (a) : sizeof (a) - 3))
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a))
     abort ();
+#endif
   r = memcpy (r, "a", 2);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 1) != (x < 30 ? sizeof (a) : sizeof (a) - 3))
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a))
     abort ();
+#endif
   r = memcpy (r + 2, "b", 2) + 2;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0)
+      != (x < 30 ? sizeof (a) - 4 : sizeof (a) - 7))
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a) - 4)
     abort ();
+#endif
   r = &a.a[4];
   r = memset (r, 'a', 2);
   if (__builtin_object_size (r, 1) != sizeof (a.a) - 4)
@@ -142,27 +168,28 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (var + 10, 1) != x)
     abort ();
+  if (__builtin_object_size (&var[5], 1) != x + 5)
+    abort ();
+  if (__builtin_object_size (vara, 1) != (x + 10) * sizeof (struct A))
+    abort ();
+  if (__builtin_object_size (vara + 10, 1) != x * sizeof (struct A))
+    abort ();    
+  if (__builtin_object_size (&vara[5], 1) != (x + 5) * sizeof (struct A))
+    abort ();
 #else
   if (__builtin_object_size (var, 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (var + 10, 1) != (size_t) -1)
     abort ();
-#endif
   if (__builtin_object_size (&var[5], 1) != (size_t) -1)
     abort ();
-#ifdef __builtin_object_size
-  if (__builtin_object_size (vara, 1) != (x + 10) * sizeof (struct A))
-    abort ();
-  if (__builtin_object_size (vara + 10, 1) != x * sizeof (struct A))
-    abort ();    
-#else
   if (__builtin_object_size (vara, 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (vara + 10, 1) != (size_t) -1)
     abort ();    
-#endif
   if (__builtin_object_size (&vara[5], 1) != (size_t) -1)
     abort ();
+#endif
   if (__builtin_object_size (&vara[0].a, 1) != sizeof (vara[0].a))
     abort ();
   if (__builtin_object_size (&vara[10].a[0], 1) != sizeof (vara[0].a))
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
index 753ee4a1a4f..db31171a8bd 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-3.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
@@ -42,9 +42,17 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (q, 2) != 0)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 2)
+      != (x < 0
+	  ? sizeof (a) - __builtin_offsetof (struct A, a) - 9
+	  : sizeof (a) - __builtin_offsetof (struct A, c) - 1))
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, c) - 1)
     abort ();
+#endif
   if (x < 6)
     r = &w[2].a[1];
   else
@@ -58,15 +66,28 @@ test1 (void *q, int x)
   if (__builtin_object_size (&y.b, 2)
       != sizeof (a) - __builtin_offsetof (struct A, b))
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 2)
+      != (x < 6
+	  ? 2 * sizeof (w[0]) - __builtin_offsetof (struct A, a) - 1
+	  : sizeof (a) - __builtin_offsetof (struct A, a) - 6))
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, a) - 6)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
     r = calloc (2, 16);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 2) != (x < 20 ? 30 : 2 * 16))
+    abort ();
+#else
   if (__builtin_object_size (r, 2) != 30)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
@@ -145,14 +166,16 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (var + 10, 2) != x)
     abort ();
+  if (__builtin_object_size (&var[5], 2) != x + 5)
+    abort ();
 #else
   if (__builtin_object_size (var, 2) != 0)
     abort ();
   if (__builtin_object_size (var + 10, 2) != 0)
     abort ();
-#endif
   if (__builtin_object_size (&var[5], 2) != 0)
     abort ();
+#endif
   if (__builtin_object_size (zerol, 2) != 0)
     abort ();
   if (__builtin_object_size (&zerol, 2) != 0)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
index c383385e060..f644890dd14 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-4.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
@@ -155,27 +155,28 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (var + 10, 3) != x)
     abort ();
+  if (__builtin_object_size (&var[5], 3) != x + 5)
+    abort ();
+  if (__builtin_object_size (vara, 3) != (x + 10) * sizeof (struct A))
+    abort ();
+  if (__builtin_object_size (vara + 10, 3) != x * sizeof (struct A))
+    abort ();    
+  if (__builtin_object_size (&vara[5], 3) != (x + 5) * sizeof (struct A))
+    abort ();
 #else
   if (__builtin_object_size (var, 3) != 0)
     abort ();
   if (__builtin_object_size (var + 10, 3) != 0)
     abort ();
-#endif
   if (__builtin_object_size (&var[5], 3) != 0)
     abort ();
-#ifdef __builtin_object_size
-  if (__builtin_object_size (vara, 3) != (x + 10) * sizeof (struct A))
-    abort ();
-  if (__builtin_object_size (vara + 10, 3) != x * sizeof (struct A))
-    abort ();    
-#else
   if (__builtin_object_size (vara, 3) != 0)
     abort ();
   if (__builtin_object_size (vara + 10, 3) != 0)
     abort ();    
-#endif
   if (__builtin_object_size (&vara[5], 3) != 0)
     abort ();
+#endif
   if (__builtin_object_size (&vara[0].a, 3) != sizeof (vara[0].a))
     abort ();
   if (__builtin_object_size (&vara[10].a[0], 3) != sizeof (vara[0].a))
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index f03714c4f18..86cfb16855e 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -120,6 +120,14 @@ size_unknown_p (tree val, int object_size_type)
 	  && tree_to_uhwi (val) == unknown (object_size_type));
 }
 
+/* Return true if VAL is represents a valid size for OBJECT_SIZE_TYPE.  */
+
+static inline bool
+size_valid_p (tree val, int object_size_type)
+{
+  return ((object_size_type & OST_DYNAMIC) || TREE_CODE (val) == INTEGER_CST);
+}
+
 /* Return a tree with initial value for OBJECT_SIZE_TYPE.  */
 
 static inline tree
@@ -345,7 +353,6 @@ init_offset_limit (void)
 static tree
 size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE)
 {
-  gcc_checking_assert (TREE_CODE (offset) == INTEGER_CST);
   gcc_checking_assert (types_compatible_p (TREE_TYPE (sz), sizetype));
 
   /* For negative offsets, if we have a distinct WHOLESIZE, use it to get a net
@@ -544,18 +551,11 @@ 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))
-	{
-	  tree offset = TREE_OPERAND (pt_var, 1);
-	  if (TREE_CODE (offset) != INTEGER_CST
-	      || TREE_CODE (sz) != INTEGER_CST)
-	    sz = wholesize = size_unknown (object_size_type);
-	  else
-	    sz = size_for_offset (sz, offset, wholesize);
-	}
+	sz = size_for_offset (sz, TREE_OPERAND (pt_var, 1), wholesize);
 
       if (!size_unknown_p (sz, object_size_type)
-	  && TREE_CODE (sz) == INTEGER_CST
-	  && compare_tree_int (sz, offset_limit) < 0)
+	  && (TREE_CODE (sz) != INTEGER_CST
+	      || compare_tree_int (sz, offset_limit) < 0))
 	{
 	  pt_var_size = sz;
 	  pt_var_wholesize = wholesize;
@@ -575,8 +575,9 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 
   if (pt_var_size)
     {
-      /* Validate the size determined above.  */
-      if (compare_tree_int (pt_var_size, offset_limit) >= 0)
+      /* Validate the size determined above if it is a constant.  */
+      if (TREE_CODE (pt_var_size) == INTEGER_CST
+	  && compare_tree_int (pt_var_size, offset_limit) >= 0)
 	return false;
     }
 
@@ -600,7 +601,7 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 	    var = TREE_OPERAND (var, 0);
 	  if (! TYPE_SIZE_UNIT (TREE_TYPE (var))
 	      || ! tree_fits_uhwi_p (TYPE_SIZE_UNIT (TREE_TYPE (var)))
-	      || (pt_var_size
+	      || (pt_var_size && TREE_CODE (pt_var_size) == INTEGER_CST
 		  && tree_int_cst_lt (pt_var_size,
 				      TYPE_SIZE_UNIT (TREE_TYPE (var)))))
 	    var = pt_var;
@@ -614,17 +615,11 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 		switch (TREE_CODE (v))
 		  {
 		  case ARRAY_REF:
-		    if (TYPE_SIZE_UNIT (TREE_TYPE (TREE_OPERAND (v, 0)))
-			&& TREE_CODE (TREE_OPERAND (v, 1)) == INTEGER_CST)
+		    if (TYPE_SIZE_UNIT (TREE_TYPE (TREE_OPERAND (v, 0))))
 		      {
 			tree domain
 			  = TYPE_DOMAIN (TREE_TYPE (TREE_OPERAND (v, 0)));
-			if (domain
-			    && TYPE_MAX_VALUE (domain)
-			    && TREE_CODE (TYPE_MAX_VALUE (domain))
-			       == INTEGER_CST
-			    && tree_int_cst_lt (TREE_OPERAND (v, 1),
-						TYPE_MAX_VALUE (domain)))
+			if (domain && TYPE_MAX_VALUE (domain))
 			  {
 			    v = NULL_TREE;
 			    break;
@@ -691,20 +686,20 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 	var = pt_var;
 
       if (var != pt_var)
-	var_size = TYPE_SIZE_UNIT (TREE_TYPE (var));
+	{
+	  var_size = TYPE_SIZE_UNIT (TREE_TYPE (var));
+	  if (!TREE_CONSTANT (var_size))
+	    var_size = get_or_create_ssa_default_def (cfun, var_size);
+	  if (!var_size)
+	    return false;
+	}
       else if (!pt_var_size)
 	return false;
       else
 	var_size = pt_var_size;
       bytes = compute_object_offset (TREE_OPERAND (ptr, 0), var);
       if (bytes != error_mark_node)
-	{
-	  if (TREE_CODE (bytes) == INTEGER_CST
-	      && tree_int_cst_lt (var_size, bytes))
-	    bytes = size_zero_node;
-	  else
-	    bytes = size_binop (MINUS_EXPR, var_size, bytes);
-	}
+	bytes = size_for_offset (var_size, bytes);
       if (var != pt_var
 	  && pt_var_size
 	  && TREE_CODE (pt_var) == MEM_REF
@@ -713,11 +708,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)
 	    {
-	      if (TREE_CODE (bytes2) == INTEGER_CST
-		  && tree_int_cst_lt (pt_var_size, bytes2))
-		bytes2 = size_zero_node;
-	      else
-		bytes2 = size_binop (MINUS_EXPR, pt_var_size, bytes2);
+	      bytes2 = size_for_offset (pt_var_size, bytes2);
 	      bytes = size_binop (MIN_EXPR, bytes, bytes2);
 	    }
 	}
@@ -733,14 +724,18 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
       wholebytes = pt_var_wholesize;
     }
 
-  if (TREE_CODE (bytes) != INTEGER_CST
-      || TREE_CODE (wholebytes) != INTEGER_CST)
-    return false;
+  if (!size_unknown_p (bytes, object_size_type)
+      && size_valid_p (bytes, object_size_type)
+      && !size_unknown_p (bytes, object_size_type)
+      && size_valid_p (wholebytes, object_size_type))
+    {
+      *psize = bytes;
+      if (pwholesize)
+	*pwholesize = wholebytes;
+      return true;
+    }
 
-  *psize = bytes;
-  if (pwholesize)
-    *pwholesize = wholebytes;
-  return true;
+  return false;
 }
 
 
@@ -1066,11 +1061,11 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 	      tree offset = gimple_assign_rhs2 (def);
 	      ptr = gimple_assign_rhs1 (def);
 
-	      if (tree_fits_shwi_p (offset)
+	      if (((object_size_type & OST_DYNAMIC)
+		   || tree_fits_shwi_p (offset))
 		  && compute_builtin_object_size (ptr, object_size_type,
 						  psize))
 		{
-		  /* Return zero when the offset is out of bounds.  */
 		  *psize = size_for_offset (*psize, offset);
 		  return true;
 		}
@@ -1255,7 +1250,7 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call)
   gcc_assert (osi->pass == 0);
   tree bytes = alloc_object_size (call, object_size_type);
 
-  if (!(object_size_type & OST_DYNAMIC) && TREE_CODE (bytes) != INTEGER_CST)
+  if (!size_valid_p (bytes, object_size_type))
     bytes = size_unknown (object_size_type);
 
   object_sizes_set (osi, varno, bytes, bytes);
@@ -1335,7 +1330,7 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
     return false;
 
   /* Handle PTR + OFFSET here.  */
-  if (TREE_CODE (op1) == INTEGER_CST
+  if (((object_size_type & OST_DYNAMIC) || TREE_CODE (op1) == INTEGER_CST)
       && (TREE_CODE (op0) == SSA_NAME
 	  || TREE_CODE (op0) == ADDR_EXPR))
     {
@@ -1368,6 +1363,10 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   else
     bytes = wholesize = size_unknown (object_size_type);
 
+  if (!size_valid_p (bytes, object_size_type)
+      || !size_valid_p (wholesize, object_size_type))
+    bytes = wholesize = size_unknown (object_size_type);
+
   osi->changed = object_sizes_set (osi, varno, bytes, wholesize);
   return reexamine;
 }
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v3 0/8] __builtin_dynamic_object_size
  2021-11-26  5:28 ` [PATCH v3 0/8] __builtin_dynamic_object_size Siddhesh Poyarekar
                     ` (7 preceding siblings ...)
  2021-11-26  5:28   ` [PATCH v3 8/8] tree-object-size: Dynamic sizes for ADDR_EXPR Siddhesh Poyarekar
@ 2021-11-26  5:38   ` Siddhesh Poyarekar
  8 siblings, 0 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-26  5:38 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

On 11/26/21 10:58, Siddhesh Poyarekar wrote:
> sure it works) and saw no issues in any of those builds.  I did some
> rudimentary analysis of the generated binaries using fortify-metrics[1]
> to confirm that there was a difference in coverage between the two
> fortification levels.
> 
> Here is a summary of coverage in the above packages:
> 
> F = number of fortified calls
> T = Total number of calls to fortifiable functions (fortified as well as
> unfortified)
> C = F * 100/ T
> 
> Package		F(2)	T(2)	F(3)	T(3)	C(2)	C(3)
> bash		428	1220	1005	1196	35.08%	84.03%
> wpa_supplicant	1635	3232	2350	3408	50.59%	68.96%
> systemtap	324	1990	343	1994	16.28%	17.20%
> cmake		830	14181	958	14196	5.85%	6.75%
> 
> The numbers are slightly lower than the previous patch series because in
> the interim I pushed an improvement to folding of the _chk builtins so
> that they can use ranges to simplify the calls to their regular
> variants.  Also note that even _FORTIFY_SOURCE=2 coverage should be
> improved due to negative offset handling.

[1] https://github.com/siddhesh/fortify-metrics

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v3 1/8] tree-object-size: Replace magic numbers with enums
  2021-11-26  5:28   ` [PATCH v3 1/8] tree-object-size: Replace magic numbers with enums Siddhesh Poyarekar
@ 2021-11-26 16:46     ` Jakub Jelinek
  2021-11-26 17:53       ` Siddhesh Poyarekar
  0 siblings, 1 reply; 97+ messages in thread
From: Jakub Jelinek @ 2021-11-26 16:46 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Fri, Nov 26, 2021 at 10:58:44AM +0530, Siddhesh Poyarekar wrote:
> A simple cleanup to allow inserting dynamic size code more easily.
> 
> gcc/ChangeLog:
> 
> 	* tree-object-size.c: New enum.
> 	(object_sizes, computed, addr_object_size,
> 	compute_builtin_object_size, expr_object_size, call_object_size,
> 	merge_object_sizes, plus_stmt_object_size,
> 	collect_object_sizes_for, init_object_sizes, fini_object_sizes,
> 	object_sizes_execute): Replace magic numbers with enums.
> 
> Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>

Ok, thanks.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v3 2/8] tree-object-size: Abstract object_sizes array
  2021-11-26  5:28   ` [PATCH v3 2/8] tree-object-size: Abstract object_sizes array Siddhesh Poyarekar
@ 2021-11-26 16:47     ` Jakub Jelinek
  0 siblings, 0 replies; 97+ messages in thread
From: Jakub Jelinek @ 2021-11-26 16:47 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Fri, Nov 26, 2021 at 10:58:45AM +0530, Siddhesh Poyarekar wrote:
> Put all accesses to object_sizes behind functions so that we can add
> dynamic capability more easily.
> 
> gcc/ChangeLog:
> 
> 	* tree-object-size.c (object_sizes_grow, object_sizes_release,
> 	object_sizes_unknown_p, object_sizes_get, object_size_set_force,
> 	object_sizes_set): New functions.
> 	(addr_object_size, compute_builtin_object_size,
> 	expr_object_size, call_object_size, unknown_object_size,
> 	merge_object_sizes, plus_stmt_object_size,
> 	cond_expr_object_size, collect_object_sizes_for,
> 	check_for_plus_in_loops_1, init_object_sizes,
> 	fini_object_sizes): Adjust.

Ok, thanks.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v3 3/8] tree-object-size: Save sizes as trees and support negative offsets
  2021-11-26  5:28   ` [PATCH v3 3/8] tree-object-size: Save sizes as trees and support negative offsets Siddhesh Poyarekar
@ 2021-11-26 16:56     ` Jakub Jelinek
  2021-11-26 17:59       ` Siddhesh Poyarekar
  0 siblings, 1 reply; 97+ messages in thread
From: Jakub Jelinek @ 2021-11-26 16:56 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Fri, Nov 26, 2021 at 10:58:46AM +0530, Siddhesh Poyarekar wrote:
> Transform tree-object-size to operate on tree objects instead of host
> wide integers.  This makes it easier to extend to dynamic expressions
> for object sizes.
> 
> The compute_builtin_object_size interface also now returns a tree
> expression instead of HOST_WIDE_INT, so callers have been adjusted to
> account for that.
> 
> The trees in object_sizes are each a TREE_VEC with the first element
> being the bytes from the pointer to the end of the object and the
> second, the size of the whole object.  This allows analysis of negative
> offsets, which can now be allowed to the extent of the object bounds.
> Tests have been added to verify that it actually works.

If you need pairs of trees, just use pairs of trees, using TREE_VEC for it
will create lots of extra garbage.
Either std::pair<tree, tree>, but most likely gengtype won't handle that
right, so just create your own
struct GTY(()) whatever {
  /* Comment explaining what it is.  */
  tree whatever1;
  /* Comment explaining what it is.  */
  tree whatever2;
};
and if that needs to go into e.g. object_sizes vectors, use vec<whatever>.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v3 1/8] tree-object-size: Replace magic numbers with enums
  2021-11-26 16:46     ` Jakub Jelinek
@ 2021-11-26 17:53       ` Siddhesh Poyarekar
  2021-11-26 18:01         ` Jakub Jelinek
  0 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-26 17:53 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 11/26/21 22:16, Jakub Jelinek wrote:
> On Fri, Nov 26, 2021 at 10:58:44AM +0530, Siddhesh Poyarekar wrote:
>> A simple cleanup to allow inserting dynamic size code more easily.
>>
>> gcc/ChangeLog:
>>
>> 	* tree-object-size.c: New enum.
>> 	(object_sizes, computed, addr_object_size,
>> 	compute_builtin_object_size, expr_object_size, call_object_size,
>> 	merge_object_sizes, plus_stmt_object_size,
>> 	collect_object_sizes_for, init_object_sizes, fini_object_sizes,
>> 	object_sizes_execute): Replace magic numbers with enums.
>>
>> Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
> 
> Ok, thanks.

Thanks, may I push these patches since they're independent cleanups?

Siddhesh

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v3 3/8] tree-object-size: Save sizes as trees and support negative offsets
  2021-11-26 16:56     ` Jakub Jelinek
@ 2021-11-26 17:59       ` Siddhesh Poyarekar
  2021-11-26 18:04         ` Jakub Jelinek
  0 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-26 17:59 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 11/26/21 22:26, Jakub Jelinek wrote:
> On Fri, Nov 26, 2021 at 10:58:46AM +0530, Siddhesh Poyarekar wrote:
>> Transform tree-object-size to operate on tree objects instead of host
>> wide integers.  This makes it easier to extend to dynamic expressions
>> for object sizes.
>>
>> The compute_builtin_object_size interface also now returns a tree
>> expression instead of HOST_WIDE_INT, so callers have been adjusted to
>> account for that.
>>
>> The trees in object_sizes are each a TREE_VEC with the first element
>> being the bytes from the pointer to the end of the object and the
>> second, the size of the whole object.  This allows analysis of negative
>> offsets, which can now be allowed to the extent of the object bounds.
>> Tests have been added to verify that it actually works.
> 
> If you need pairs of trees, just use pairs of trees, using TREE_VEC for it
> will create lots of extra garbage.
> Either std::pair<tree, tree>, but most likely gengtype won't handle that
> right, so just create your own
> struct GTY(()) whatever {
>    /* Comment explaining what it is.  */
>    tree whatever1;
>    /* Comment explaining what it is.  */
>    tree whatever2;
> };
> and if that needs to go into e.g. object_sizes vectors, use vec<whatever>.

Got it, I'll make a new struct GTY(()).  I'm also using TREE_VEC to 
store PHI nodes args and the result (in patch 5/8) until they're emitted 
in gimplify_size_expressions.  It needs to be a tree since it would be 
stored in whatever1 and whatever2, would that be acceptable use?

Thanks,
Siddhesh

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v3 1/8] tree-object-size: Replace magic numbers with enums
  2021-11-26 17:53       ` Siddhesh Poyarekar
@ 2021-11-26 18:01         ` Jakub Jelinek
  0 siblings, 0 replies; 97+ messages in thread
From: Jakub Jelinek @ 2021-11-26 18:01 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Fri, Nov 26, 2021 at 11:23:31PM +0530, Siddhesh Poyarekar wrote:
> On 11/26/21 22:16, Jakub Jelinek wrote:
> > On Fri, Nov 26, 2021 at 10:58:44AM +0530, Siddhesh Poyarekar wrote:
> > > A simple cleanup to allow inserting dynamic size code more easily.
> > > 
> > > gcc/ChangeLog:
> > > 
> > > 	* tree-object-size.c: New enum.
> > > 	(object_sizes, computed, addr_object_size,
> > > 	compute_builtin_object_size, expr_object_size, call_object_size,
> > > 	merge_object_sizes, plus_stmt_object_size,
> > > 	collect_object_sizes_for, init_object_sizes, fini_object_sizes,
> > > 	object_sizes_execute): Replace magic numbers with enums.
> > > 
> > > Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
> > 
> > Ok, thanks.
> 
> Thanks, may I push these patches since they're independent cleanups?

The first two, yes.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v3 3/8] tree-object-size: Save sizes as trees and support negative offsets
  2021-11-26 17:59       ` Siddhesh Poyarekar
@ 2021-11-26 18:04         ` Jakub Jelinek
  2021-11-26 18:07           ` Siddhesh Poyarekar
  0 siblings, 1 reply; 97+ messages in thread
From: Jakub Jelinek @ 2021-11-26 18:04 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Fri, Nov 26, 2021 at 11:29:41PM +0530, Siddhesh Poyarekar wrote:
> > > The trees in object_sizes are each a TREE_VEC with the first element
> > > being the bytes from the pointer to the end of the object and the
> > > second, the size of the whole object.  This allows analysis of negative
> > > offsets, which can now be allowed to the extent of the object bounds.
> > > Tests have been added to verify that it actually works.
> > 
> > If you need pairs of trees, just use pairs of trees, using TREE_VEC for it
> > will create lots of extra garbage.
> > Either std::pair<tree, tree>, but most likely gengtype won't handle that
> > right, so just create your own
> > struct GTY(()) whatever {
> >    /* Comment explaining what it is.  */
> >    tree whatever1;
> >    /* Comment explaining what it is.  */
> >    tree whatever2;
> > };
> > and if that needs to go into e.g. object_sizes vectors, use vec<whatever>.
> 
> Got it, I'll make a new struct GTY(()).  I'm also using TREE_VEC to store
> PHI nodes args and the result (in patch 5/8) until they're emitted in
> gimplify_size_expressions.  It needs to be a tree since it would be stored
> in whatever1 and whatever2, would that be acceptable use?

Maybe yes, I'll need to look at it.  What I didn't like is using TREE_VEC
for something that will come in pairs all the time.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v3 3/8] tree-object-size: Save sizes as trees and support negative offsets
  2021-11-26 18:04         ` Jakub Jelinek
@ 2021-11-26 18:07           ` Siddhesh Poyarekar
  0 siblings, 0 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-11-26 18:07 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 11/26/21 23:34, Jakub Jelinek wrote:
> On Fri, Nov 26, 2021 at 11:29:41PM +0530, Siddhesh Poyarekar wrote:
>>>> The trees in object_sizes are each a TREE_VEC with the first element
>>>> being the bytes from the pointer to the end of the object and the
>>>> second, the size of the whole object.  This allows analysis of negative
>>>> offsets, which can now be allowed to the extent of the object bounds.
>>>> Tests have been added to verify that it actually works.
>>>
>>> If you need pairs of trees, just use pairs of trees, using TREE_VEC for it
>>> will create lots of extra garbage.
>>> Either std::pair<tree, tree>, but most likely gengtype won't handle that
>>> right, so just create your own
>>> struct GTY(()) whatever {
>>>     /* Comment explaining what it is.  */
>>>     tree whatever1;
>>>     /* Comment explaining what it is.  */
>>>     tree whatever2;
>>> };
>>> and if that needs to go into e.g. object_sizes vectors, use vec<whatever>.
>>
>> Got it, I'll make a new struct GTY(()).  I'm also using TREE_VEC to store
>> PHI nodes args and the result (in patch 5/8) until they're emitted in
>> gimplify_size_expressions.  It needs to be a tree since it would be stored
>> in whatever1 and whatever2, would that be acceptable use?
> 
> Maybe yes, I'll need to look at it.  What I didn't like is using TREE_VEC
> for something that will come in pairs all the time.

Ah ok, I understand your concern better now, thanks.

Siddhesh

^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v4 0/6] __builtin_dynamic_object_size
  2021-11-09 19:01 [PATCH 00/10] __builtin_dynamic_object_size Siddhesh Poyarekar
                   ` (11 preceding siblings ...)
  2021-11-26  5:28 ` [PATCH v3 0/8] __builtin_dynamic_object_size Siddhesh Poyarekar
@ 2021-12-01 14:27 ` Siddhesh Poyarekar
  2021-12-01 14:27   ` [PATCH v4 1/6] tree-object-size: Use trees and support negative offsets Siddhesh Poyarekar
                     ` (5 more replies)
  2021-12-18 12:35 ` [PATCH v5 0/4] __builtin_dynamic_object_size Siddhesh Poyarekar
  2022-01-11  8:57 ` [PATCH v6 " Siddhesh Poyarekar
  14 siblings, 6 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-12-01 14:27 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

This patchset implements the __builtin_dynamic_object_size builtin for
gcc.  The primary motivation to have this builtin in gcc is to enable
_FORTIFY_SOURCE=3 support with gcc, thus allowing greater fortification
in use cases where the potential performance tradeoff is acceptable.

Semantics:
----------

__builtin_dynamic_object_size has the same signature as
__builtin_object_size; it accepts a pointer and type ranging from 0 to 3
and it returns an object size estimate for the pointer based on an
analysis of which objects the pointer could point to.  The actual
properties of the object size estimate are different:

- In the best case __builtin_dynamic_object_size evaluates to an
  expression that represents a precise size of the object being pointed
  to.

- In case a precise object size expression cannot be evaluated,
  __builtin_dynamic_object_size attempts to evaluate an estimate size
  expression based on the object size type.

- In what situations the builtin returns an estimate vs a precise
  expression is an implementation detail and may change in future.
  Users must always assume, as in the case of __builtin_object_size, that
  the returned value is the maximum or minimum based on the object size
  type they have provided.

- In the worst case of failure, __builtin_dynamic_object_size returns a
  constant (size_t)-1 or (size_t)0.

Implementation:
---------------

- The __builtin_dynamic_object_size support is implemented in
  tree-object-size.  In most cases the first pass (early_objsz) the
  builtin is treated like __builtin_object_size to preserve subobject
  bounds.

- Each element of the object_sizes vector is now an object_size struct
  holding bytes to the end of the object and the full size of the
  object.  This allows proper handling of negative offsets, allowing
  them to the extent of the whole object bounds.  This improves
  __builtin_object_size usage too with negative offsets, consistently
  returning valid results for pointer decrementing loops too.

- The patchset begins with structural modification of the
  tree-object-size pass, followed by enhancement to return size
  expressions.  I have split the implementation into one feature per
  patch (calls, function parameters, PHI, etc.) to hopefully ease
  review.

Performance:
------------

Expressions generated by this pass in theory could be arbitrarily
complex.  I have not made an attempt to limit nesting of objects since
it seemed too early to do that.  In practice based on the few
applications I built, most of the complexity of the expressions got
folded away.  Even so, the performance overhead is likely to be
non-zero.  If we find performance degradation to be significant, we
could later add nesting limits to bail out if a size expression gets too
complex.

I have implemented simplification of __*_chk to their normal
variants if we can determine at compile time that it is safe.  This
should limit the performance overhead of the expressions in valid cases.

Build time performance doesn't seem to be affected much based on an
unscientific check to time
`make check-gcc RUNTESTFLAGS="dg.exp=builtin*"`.  It only increases by
about a couple of seconds when the dynamic tests are added and remains
more or less in the same ballpark otherwise.

Testing:
--------

I have added tests for dynamic object sizes as well as wrappers for all
__builtin_object_size tests to provide wide coverage.  I have also done
a full bootstrap build and test run on x86_64.  Further I did a
bootstrap build with --with-build-config=bootstrap-ubsan for additional
coverage since it managed to find an issue with the earlier cleanup
patches.  It is however only useful to ensure there are no regressions
since ubsan does not yet use dynamic size expressions.

I have also built bash, cmake, wpa_supplicant and systemtap with
_FORTIFY_SOURCE=2 and _FORTIFY_SOURCE=3 (with a hacked up glibc to make
sure it works) and saw no issues in any of those builds.  I did some
rudimentary analysis of the generated binaries using fortify-metrics[1]
to confirm that there was a difference in coverage between the two
fortification levels.

(Unchanged since v3) Here is a summary of coverage in the above packages:

F = number of fortified calls
T = Total number of calls to fortifiable functions (fortified as well as
unfortified)
C = F * 100/ T

Package		F(2)	T(2)	F(3)	T(3)	C(2)	C(3)
bash		428	1220	1005	1196	35.08%	84.03%
wpa_supplicant	1635	3232	2350	3408	50.59%	68.96%
systemtap	324	1990	343	1994	16.28%	17.20%
cmake		830	14181	958	14196	5.85%	6.75%

The numbers are slightly lower than the previous patch series because in
the interim I pushed an improvement to folding of the _chk builtins so
that they can use ranges to simplify the calls to their regular
variants.  Also note that even _FORTIFY_SOURCE=2 coverage should be
improved due to negative offset handling.

Additional testing plans (i.e. I've already started to do some of this):

- Build packages to compare values returned by __builtin_object_size
  with the older pass and this new one.  Also compare with
  __builtin_dynamic_object_size.

- Expand the list of packages to get more coverage metrics.

- Explore performance impact on applications on building with
  _FORTIFY_SOURCE=3.

Limitations/Future work:
------------------------

- I need to enable _FORTIFY_SOURCE=3 for gcc in glibc; currently it is
  llvm-only.  It's a fairly simple fix that I'll push once this series
  is in.

- Explore ways to use the non-constant sizes returned for
  __builtin_object_size to arrive at a constant estimate to improve
  _FORTIFY_SOURCE=2 coverage in a way that accounts for undefined
  behaviour.

- More work could to be done to reduce the performance impact of the
  computation.  One way could be to add a heuristic where the pass keeps
  track of nesting in the expression and either bail out or compute an
  estimate if nesting crosses a threshold.  I'll take this up once we
  have more data on the nature of the bottlenecks.

Changes from v3:

- Made a custom struct object_size to hold size and wholesize in the
  object_sizes arrays instead of the TREE_VEC.

Changes from v2:

Changes to individual patches have been mentioned in the patches
themselves.

- Dropped patch to remove check_for_plus_in_for_loops and osi->pass
- Merge negative offset support (10/10 in v2) into 3/8 and support
  static object sizes
- Merge dynamic offset (10/10 in v2) support into 8/8


Siddhesh Poyarekar (6):
  tree-object-size: Use trees and support negative offsets
  __builtin_dynamic_object_size: Recognize builtin
  tree-object-size: Support dynamic sizes in conditions
  tree-object-size: Handle function parameters
  tree-object-size: Handle GIMPLE_CALL
  tree-object-size: Dynamic sizes for ADDR_EXPR

 gcc/builtins.c                                |   23 +-
 gcc/builtins.def                              |    1 +
 gcc/doc/extend.texi                           |   13 +
 gcc/gimple-fold.c                             |   11 +-
 .../g++.dg/ext/builtin-dynamic-object-size1.C |    5 +
 .../g++.dg/ext/builtin-dynamic-object-size2.C |    5 +
 .../gcc.dg/builtin-dynamic-alloc-size.c       |    7 +
 .../gcc.dg/builtin-dynamic-object-size-0.c    |  464 +++++++
 .../gcc.dg/builtin-dynamic-object-size-1.c    |    6 +
 .../gcc.dg/builtin-dynamic-object-size-10.c   |   11 +
 .../gcc.dg/builtin-dynamic-object-size-11.c   |    7 +
 .../gcc.dg/builtin-dynamic-object-size-12.c   |    5 +
 .../gcc.dg/builtin-dynamic-object-size-13.c   |    5 +
 .../gcc.dg/builtin-dynamic-object-size-14.c   |    5 +
 .../gcc.dg/builtin-dynamic-object-size-15.c   |    5 +
 .../gcc.dg/builtin-dynamic-object-size-16.c   |    6 +
 .../gcc.dg/builtin-dynamic-object-size-17.c   |    7 +
 .../gcc.dg/builtin-dynamic-object-size-18.c   |    8 +
 .../gcc.dg/builtin-dynamic-object-size-19.c   |  104 ++
 .../gcc.dg/builtin-dynamic-object-size-2.c    |    6 +
 .../gcc.dg/builtin-dynamic-object-size-3.c    |    6 +
 .../gcc.dg/builtin-dynamic-object-size-4.c    |    6 +
 .../gcc.dg/builtin-dynamic-object-size-5.c    |    7 +
 .../gcc.dg/builtin-dynamic-object-size-6.c    |    5 +
 .../gcc.dg/builtin-dynamic-object-size-7.c    |    5 +
 .../gcc.dg/builtin-dynamic-object-size-8.c    |    5 +
 .../gcc.dg/builtin-dynamic-object-size-9.c    |    5 +
 gcc/testsuite/gcc.dg/builtin-object-size-1.c  |  184 ++-
 gcc/testsuite/gcc.dg/builtin-object-size-16.c |    2 +
 gcc/testsuite/gcc.dg/builtin-object-size-17.c |    2 +
 gcc/testsuite/gcc.dg/builtin-object-size-2.c  |  163 +++
 gcc/testsuite/gcc.dg/builtin-object-size-3.c  |  182 +++
 gcc/testsuite/gcc.dg/builtin-object-size-4.c  |  123 ++
 gcc/testsuite/gcc.dg/builtin-object-size-5.c  |   37 +
 gcc/tree-object-size.c                        | 1085 +++++++++++++----
 gcc/tree-object-size.h                        |   12 +-
 gcc/ubsan.c                                   |    5 +-
 37 files changed, 2312 insertions(+), 226 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size1.C
 create mode 100644 gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size2.C
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-alloc-size.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-11.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-12.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-13.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-14.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-15.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-16.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-17.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-18.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-19.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-6.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-7.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-8.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-9.c

-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v4 1/6] tree-object-size: Use trees and support negative offsets
  2021-12-01 14:27 ` [PATCH v4 0/6] __builtin_dynamic_object_size Siddhesh Poyarekar
@ 2021-12-01 14:27   ` Siddhesh Poyarekar
  2021-12-15 15:21     ` Jakub Jelinek
  2021-12-01 14:27   ` [PATCH v4 2/6] __builtin_dynamic_object_size: Recognize builtin Siddhesh Poyarekar
                     ` (4 subsequent siblings)
  5 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-12-01 14:27 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Transform tree-object-size to operate on tree objects instead of host
wide integers.  This makes it easier to extend to dynamic expressions
for object sizes.

The compute_builtin_object_size interface also now returns a tree
expression instead of HOST_WIDE_INT, so callers have been adjusted to
account for that.

The trees in object_sizes are each an object_size object with members
size (the bytes from the pointer to the end of the object) and wholesize
(the size of the whole object).  This allows analysis of negative
offsets, which can now be allowed to the extent of the object bounds.
Tests have been added to verify that it actually works.

gcc/ChangeLog:

	* tree-object-size.h (compute_builtin_object_size): Return tree
	instead of HOST_WIDE_INT.
	* builtins.c (fold_builtin_object_size): Adjust.
	* gimple-fold.c (gimple_fold_builtin_strncat): Likewise.
	* ubsan.c (instrument_object_size): Likewise.
	* tree-object-size.c (object_size): New structure.
	(object_sizes): Change type to vec<object_size>.
	(initval): New function.
	(unknown): Use it.
	(size_unknown_p, size_initval, size_unknown): New functions.
	(object_sizes_unknown_p): Use it.
	(object_sizes_get): Return tree.
	(object_sizes_initialize): Rename from object_sizes_set_force
	and set VAL parameter type as tree.  Add new parameter WHOLEVAL.
	(object_sizes_set): Set VAL parameter type as tree and adjust
	implementation.  Add new parameter WHOLEVAL.
	(size_for_offset): New function.
	(decl_init_size): Adjust comment.
	(addr_object_size): Change PSIZE parameter to tree and adjust
	implementation.  Add new parameter PWHOLESIZE.
	(alloc_object_size): Return tree.
	(compute_builtin_object_size): Return tree in PSIZE.
	(expr_object_size, call_object_size, unknown_object_size):
	Adjust for object_sizes_set change.
	(merge_object_sizes): Drop OFFSET parameter and adjust
	implementation for tree change.
	(plus_stmt_object_size): Call collect_object_sizes_for directly
	instead of merge_object_size and call size_for_offset to get net
	size.
	(cond_expr_object_size, collect_object_sizes_for,
	object_sizes_execute): Adjust for change of type from
	HOST_WIDE_INT to tree.
	(check_for_plus_in_loops_1): Likewise and skip non-positive
	offsets.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-object-size-1.c (test9): New test.
	(main): Call it.
	* gcc.dg/builtin-object-size-2.c (test8): New test.
	(main): Call it.
	* gcc.dg/builtin-object-size-3.c (test9): New test.
	(main): Call it.
	* gcc.dg/builtin-object-size-4.c (test8): New test.
	(main): Call it.
	* gcc.dg/builtin-object-size-5.c (test5, test6, test7): New
	tests.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 gcc/builtins.c                               |  10 +-
 gcc/gimple-fold.c                            |  11 +-
 gcc/testsuite/gcc.dg/builtin-object-size-1.c |  30 ++
 gcc/testsuite/gcc.dg/builtin-object-size-2.c |  30 ++
 gcc/testsuite/gcc.dg/builtin-object-size-3.c |  31 ++
 gcc/testsuite/gcc.dg/builtin-object-size-4.c |  30 ++
 gcc/testsuite/gcc.dg/builtin-object-size-5.c |  25 ++
 gcc/tree-object-size.c                       | 393 ++++++++++++-------
 gcc/tree-object-size.h                       |   2 +-
 gcc/ubsan.c                                  |   5 +-
 10 files changed, 408 insertions(+), 159 deletions(-)

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 03829c03a5a..309c3010de3 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -10254,7 +10254,7 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
 static tree
 fold_builtin_object_size (tree ptr, tree ost)
 {
-  unsigned HOST_WIDE_INT bytes;
+  tree bytes;
   int object_size_type;
 
   if (!validate_arg (ptr, POINTER_TYPE)
@@ -10279,8 +10279,8 @@ fold_builtin_object_size (tree ptr, tree ost)
   if (TREE_CODE (ptr) == ADDR_EXPR)
     {
       compute_builtin_object_size (ptr, object_size_type, &bytes);
-      if (wi::fits_to_tree_p (bytes, size_type_node))
-	return build_int_cstu (size_type_node, bytes);
+      if (int_fits_type_p (bytes, size_type_node))
+	return fold_convert (size_type_node, bytes);
     }
   else if (TREE_CODE (ptr) == SSA_NAME)
     {
@@ -10288,8 +10288,8 @@ fold_builtin_object_size (tree ptr, tree ost)
        later.  Maybe subsequent passes will help determining
        it.  */
       if (compute_builtin_object_size (ptr, object_size_type, &bytes)
-	  && wi::fits_to_tree_p (bytes, size_type_node))
-	return build_int_cstu (size_type_node, bytes);
+	  && int_fits_type_p (bytes, size_type_node))
+	return fold_convert (size_type_node, bytes);
     }
 
   return NULL_TREE;
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index 1d8fd74f72c..64515aabc04 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -2493,17 +2493,16 @@ gimple_fold_builtin_strncat (gimple_stmt_iterator *gsi)
   if (!src_len || known_lower (stmt, len, src_len, true))
     return false;
 
-  unsigned HOST_WIDE_INT dstsize;
-  bool found_dstsize = compute_builtin_object_size (dst, 1, &dstsize);
-
   /* Warn on constant LEN.  */
   if (TREE_CODE (len) == INTEGER_CST)
     {
       bool nowarn = warning_suppressed_p (stmt, OPT_Wstringop_overflow_);
+      tree dstsize;
 
-      if (!nowarn && found_dstsize)
+      if (!nowarn && compute_builtin_object_size (dst, 1, &dstsize)
+	  && TREE_CODE (dstsize) == INTEGER_CST)
 	{
-	  int cmpdst = compare_tree_int (len, dstsize);
+	  int cmpdst = tree_int_cst_compare (len, dstsize);
 
 	  if (cmpdst >= 0)
 	    {
@@ -2519,7 +2518,7 @@ gimple_fold_builtin_strncat (gimple_stmt_iterator *gsi)
 				   ? G_("%qD specified bound %E equals "
 					"destination size")
 				   : G_("%qD specified bound %E exceeds "
-					"destination size %wu"),
+					"destination size %E"),
 				   fndecl, len, dstsize);
 	      if (nowarn)
 		suppress_warning (stmt, OPT_Wstringop_overflow_);
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
index 8cdae49a6b1..0154f4e9695 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
@@ -424,6 +424,35 @@ test8 (void)
     abort ();
 }
 
+void
+__attribute__ ((noinline))
+test9 (unsigned cond)
+{
+  char *buf2 = malloc (10);
+  char *p;
+
+  if (cond)
+    p = &buf2[8];
+  else
+    p = &buf2[4];
+
+  if (__builtin_object_size (&p[-4], 0) != 10)
+    abort ();
+
+  for (unsigned i = cond; i > 0; i--)
+    p--;
+
+  if (__builtin_object_size (p, 0) != 10)
+    abort ();
+
+  p = &y.c[8];
+  for (unsigned i = cond; i > 0; i--)
+    p--;
+
+  if (__builtin_object_size (p, 0) != sizeof (y))
+    abort ();
+}
+
 int
 main (void)
 {
@@ -437,5 +466,6 @@ main (void)
   test6 (4);
   test7 ();
   test8 ();
+  test9 (1);
   exit (0);
 }
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
index ad2dd296a9a..5cf29291aff 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-2.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
@@ -382,6 +382,35 @@ test7 (void)
     abort ();
 }
 
+void
+__attribute__ ((noinline))
+test8 (unsigned cond)
+{
+  char *buf2 = malloc (10);
+  char *p;
+
+  if (cond)
+    p = &buf2[8];
+  else
+    p = &buf2[4];
+
+  if (__builtin_object_size (&p[-4], 1) != 10)
+    abort ();
+
+  for (unsigned i = cond; i > 0; i--)
+    p--;
+
+  if (__builtin_object_size (p, 1) != 10)
+    abort ();
+
+  p = &y.c[8];
+  for (unsigned i = cond; i > 0; i--)
+    p--;
+
+  if (__builtin_object_size (p, 1) != sizeof (y.c))
+    abort ();
+}
+
 int
 main (void)
 {
@@ -394,5 +423,6 @@ main (void)
   test5 (4);
   test6 ();
   test7 ();
+  test8 (1);
   exit (0);
 }
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
index d5ca5047ee9..3a692c4e3d2 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-3.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
@@ -430,6 +430,36 @@ test8 (void)
     abort ();
 }
 
+void
+__attribute__ ((noinline))
+test9 (unsigned cond)
+{
+  char *buf2 = malloc (10);
+  char *p;
+
+  if (cond)
+    p = &buf2[8];
+  else
+    p = &buf2[4];
+
+  if (__builtin_object_size (&p[-4], 2) != 6)
+    abort ();
+
+  for (unsigned i = cond; i > 0; i--)
+    p--;
+
+  if (__builtin_object_size (p, 2) != 2)
+    abort ();
+
+  p = &y.c[8];
+  for (unsigned i = cond; i > 0; i--)
+    p--;
+
+  if (__builtin_object_size (p, 2)
+      != sizeof (y) - __builtin_offsetof (struct A, c) - 8)
+    abort ();
+}
+
 int
 main (void)
 {
@@ -443,5 +473,6 @@ main (void)
   test6 (4);
   test7 ();
   test8 ();
+  test9 (1);
   exit (0);
 }
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
index 9f159e36a0f..87381620cc9 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-4.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
@@ -395,6 +395,35 @@ test7 (void)
     abort ();
 }
 
+void
+__attribute__ ((noinline))
+test8 (unsigned cond)
+{
+  char *buf2 = malloc (10);
+  char *p;
+
+  if (cond)
+    p = &buf2[8];
+  else
+    p = &buf2[4];
+
+  if (__builtin_object_size (&p[-4], 3) != 6)
+    abort ();
+
+  for (unsigned i = cond; i > 0; i--)
+    p--;
+
+  if (__builtin_object_size (p, 3) != 2)
+    abort ();
+
+  p = &y.c[8];
+  for (unsigned i = cond; i > 0; i--)
+    p--;
+
+  if (__builtin_object_size (p, 3) != sizeof (y.c) - 8)
+    abort ();
+}
+
 int
 main (void)
 {
@@ -407,5 +436,6 @@ main (void)
   test5 (4);
   test6 ();
   test7 ();
+  test8 (1);
   exit (0);
 }
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-5.c b/gcc/testsuite/gcc.dg/builtin-object-size-5.c
index 7c274cdfd42..8e63d9c7a5e 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-5.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-5.c
@@ -53,4 +53,29 @@ test4 (size_t x)
     abort ();
 }
 
+void
+test5 (void)
+{
+  char *p = &buf[0x90000004];
+  if (__builtin_object_size (p + 2, 0) != 0)
+    abort ();
+}
+
+void
+test6 (void)
+{
+  char *p = &buf[-4];
+  if (__builtin_object_size (p + 2, 0) != 0)
+    abort ();
+}
+
+void
+test7 (void)
+{
+  char *buf2 = __builtin_malloc (8);
+  char *p = &buf2[0x90000004];
+  if (__builtin_object_size (p + 2, 0) != 0)
+    abort ();
+}
+
 /* { dg-final { scan-assembler-not "abort" } } */
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index b4881ef198f..ccac60001db 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -45,6 +45,14 @@ struct object_size_info
   unsigned int *stack, *tos;
 };
 
+struct GTY(()) object_size
+{
+  /* Estimate of bytes till the end of the object.  */
+  tree size;
+  /* Estimate of the size of the whole object.  */
+  tree wholesize;
+};
+
 enum
 {
   OST_SUBOBJECT = 1,
@@ -54,13 +62,12 @@ enum
 
 static tree compute_object_offset (const_tree, const_tree);
 static bool addr_object_size (struct object_size_info *,
-			      const_tree, int, unsigned HOST_WIDE_INT *);
-static unsigned HOST_WIDE_INT alloc_object_size (const gcall *, int);
+			      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 void expr_object_size (struct object_size_info *, tree, tree);
-static bool merge_object_sizes (struct object_size_info *, tree, tree,
-				unsigned HOST_WIDE_INT);
+static bool merge_object_sizes (struct object_size_info *, tree, tree);
 static bool plus_stmt_object_size (struct object_size_info *, tree, gimple *);
 static bool cond_expr_object_size (struct object_size_info *, tree, gimple *);
 static void init_offset_limit (void);
@@ -68,13 +75,13 @@ static void check_for_plus_in_loops (struct object_size_info *, tree);
 static void check_for_plus_in_loops_1 (struct object_size_info *, tree,
 				       unsigned int);
 
-/* object_sizes[0] is upper bound for number of bytes till the end of
-   the object.
-   object_sizes[1] is upper bound for number of bytes till the end of
-   the subobject (innermost array or field with address taken).
-   object_sizes[2] is lower bound for number of bytes till the end of
-   the object and object_sizes[3] lower bound for subobject.  */
-static vec<unsigned HOST_WIDE_INT> object_sizes[OST_END];
+/* object_sizes[0] is upper bound for the object size and number of bytes till
+   the end of the object.
+   object_sizes[1] is upper bound for the object size and number of bytes till
+   the end of the subobject (innermost array or field with address taken).
+   object_sizes[2] is lower bound for the object size and number of bytes till
+   the end of the object and object_sizes[3] lower bound for subobject.  */
+static vec<object_size> object_sizes[OST_END];
 
 /* Bitmaps what object sizes have been computed already.  */
 static bitmap computed[OST_END];
@@ -82,10 +89,46 @@ static bitmap computed[OST_END];
 /* Maximum value of offset we consider to be addition.  */
 static unsigned HOST_WIDE_INT offset_limit;
 
+/* Initial value of object sizes; zero for maximum and SIZE_MAX for minimum
+   object size.  */
+
+static inline unsigned HOST_WIDE_INT
+initval (int object_size_type)
+{
+  return (object_size_type & OST_MINIMUM) ? HOST_WIDE_INT_M1U : 0;
+}
+
+/* Unknown object size value; it's the opposite of initval.  */
+
 static inline unsigned HOST_WIDE_INT
 unknown (int object_size_type)
 {
-  return ((unsigned HOST_WIDE_INT) -((object_size_type >> 1) ^ 1));
+  return ~initval (object_size_type);
+}
+
+/* Return true if VAL is represents an unknown size for OBJECT_SIZE_TYPE.  */
+
+static inline bool
+size_unknown_p (tree val, int object_size_type)
+{
+  return (tree_fits_uhwi_p (val)
+	  && tree_to_uhwi (val) == unknown (object_size_type));
+}
+
+/* Return a tree with initial value for OBJECT_SIZE_TYPE.  */
+
+static inline tree
+size_initval (int object_size_type)
+{
+  return size_int (initval (object_size_type));
+}
+
+/* Return a tree with unknown value for OBJECT_SIZE_TYPE.  */
+
+static inline tree
+size_unknown (int object_size_type)
+{
+  return size_int (unknown (object_size_type));
 }
 
 /* Grow object_sizes[OBJECT_SIZE_TYPE] to num_ssa_names.  */
@@ -110,47 +153,56 @@ object_sizes_release (int object_size_type)
 static inline bool
 object_sizes_unknown_p (int object_size_type, unsigned varno)
 {
-  return (object_sizes[object_size_type][varno]
-	  == unknown (object_size_type));
+  return size_unknown_p (object_sizes[object_size_type][varno].size,
+			 object_size_type);
 }
 
-/* Return size for VARNO corresponding to OSI.  */
+/* Return size for VARNO corresponding to OSI.  If WHOLE is true, return the
+   whole object size.  */
 
-static inline unsigned HOST_WIDE_INT
-object_sizes_get (struct object_size_info *osi, unsigned varno)
+static inline tree
+object_sizes_get (struct object_size_info *osi, unsigned varno,
+		  bool whole = false)
 {
-  return object_sizes[osi->object_size_type][varno];
+  if (whole)
+    return object_sizes[osi->object_size_type][varno].wholesize;
+  else
+    return object_sizes[osi->object_size_type][varno].size;
 }
 
 /* Set size for VARNO corresponding to OSI to VAL.  */
 
-static inline bool
-object_sizes_set_force (struct object_size_info *osi, unsigned varno,
-			unsigned HOST_WIDE_INT val)
+static inline void
+object_sizes_initialize (struct object_size_info *osi, unsigned varno,
+			 tree val, tree wholeval)
 {
-  object_sizes[osi->object_size_type][varno] = val;
-  return true;
+  int object_size_type = osi->object_size_type;
+
+  object_sizes[object_size_type][varno].size = val;
+  object_sizes[object_size_type][varno].wholesize = wholeval;
 }
 
 /* Set size for VARNO corresponding to OSI to VAL if it is the new minimum or
    maximum.  */
 
 static inline bool
-object_sizes_set (struct object_size_info *osi, unsigned varno,
-		  unsigned HOST_WIDE_INT val)
+object_sizes_set (struct object_size_info *osi, unsigned varno, tree val,
+		  tree wholeval)
 {
   int object_size_type = osi->object_size_type;
-  if ((object_size_type & OST_MINIMUM) == 0)
-    {
-      if (object_sizes[object_size_type][varno] < val)
-	return object_sizes_set_force (osi, varno, val);
-    }
-  else
-    {
-      if (object_sizes[object_size_type][varno] > val)
-	return object_sizes_set_force (osi, varno, val);
-    }
-  return false;
+  object_size osize = object_sizes[object_size_type][varno];
+
+  tree oldval = osize.size;
+  tree old_wholeval = osize.wholesize;
+
+  enum tree_code code = object_size_type & OST_MINIMUM ? MIN_EXPR : MAX_EXPR;
+
+  val = size_binop (code, val, oldval);
+  wholeval = size_binop (code, wholeval, old_wholeval);
+
+  object_sizes[object_size_type][varno].size = val;
+  object_sizes[object_size_type][varno].wholesize = wholeval;
+  return tree_int_cst_compare (oldval, val) != 0;
 }
 
 /* Initialize OFFSET_LIMIT variable.  */
@@ -164,6 +216,48 @@ init_offset_limit (void)
   offset_limit /= 2;
 }
 
+/* Bytes at end of the object with SZ from offset OFFSET.  If WHOLESIZE is not
+   NULL_TREE, use it to get the net offset of the pointer, which should always
+   be positive and hence, be within OFFSET_LIMIT for valid offsets.  */
+
+static tree
+size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE)
+{
+  gcc_checking_assert (TREE_CODE (offset) == INTEGER_CST);
+  gcc_checking_assert (TREE_CODE (sz) == INTEGER_CST);
+  gcc_checking_assert (types_compatible_p (TREE_TYPE (sz), sizetype));
+
+  /* For negative offsets, if we have a distinct WHOLESIZE, use it to get a net
+     offset from the whole object.  */
+  if (wholesize && tree_int_cst_compare (sz, wholesize))
+    {
+      gcc_checking_assert (TREE_CODE (wholesize) == INTEGER_CST);
+      gcc_checking_assert (types_compatible_p (TREE_TYPE (wholesize),
+					       sizetype));
+
+      /* Restructure SZ - OFFSET as
+	 WHOLESIZE - (WHOLESIZE + OFFSET - SZ) so that the offset part, i.e.
+	 WHOLESIZE + OFFSET - SZ is only allowed to be positive.  */
+      tree tmp = size_binop (MAX_EXPR, wholesize, sz);
+      offset = fold_build2 (PLUS_EXPR, sizetype, tmp, offset);
+      offset = fold_build2 (MINUS_EXPR, sizetype, offset, sz);
+      sz = tmp;
+    }
+
+  /* Safe to convert now, since a valid net offset should be non-negative.  */
+  if (!types_compatible_p (TREE_TYPE (offset), sizetype))
+    fold_convert (sizetype, offset);
+
+  if (integer_zerop (offset))
+    return sz;
+
+  /* Negative or too large offset even after adjustment, cannot be within
+     bounds of an object.  */
+  if (compare_tree_int (offset, offset_limit) > 0)
+    return size_zero_node;
+
+  return size_binop (MINUS_EXPR, size_binop (MAX_EXPR, sz, offset), offset);
+}
 
 /* Compute offset of EXPR within VAR.  Return error_mark_node
    if unknown.  */
@@ -274,19 +368,22 @@ decl_init_size (tree decl, bool min)
 
 /* Compute __builtin_object_size for PTR, which is a ADDR_EXPR.
    OBJECT_SIZE_TYPE is the second argument from __builtin_object_size.
-   If unknown, return unknown (object_size_type).  */
+   If unknown, return size_unknown (object_size_type).  */
 
 static bool
 addr_object_size (struct object_size_info *osi, const_tree ptr,
-		  int object_size_type, unsigned HOST_WIDE_INT *psize)
+		  int object_size_type, tree *psize, tree *pwholesize)
 {
-  tree pt_var, pt_var_size = NULL_TREE, var_size, bytes;
+  tree pt_var, pt_var_size = NULL_TREE, pt_var_wholesize = NULL_TREE;
+  tree var_size, bytes, wholebytes;
 
   gcc_assert (TREE_CODE (ptr) == ADDR_EXPR);
 
   /* Set to unknown and overwrite just before returning if the size
      could be determined.  */
-  *psize = unknown (object_size_type);
+  *psize = size_unknown (object_size_type);
+  if (pwholesize)
+    *pwholesize = size_unknown (object_size_type);
 
   pt_var = TREE_OPERAND (ptr, 0);
   while (handled_component_p (pt_var))
@@ -297,13 +394,14 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 
   if (TREE_CODE (pt_var) == MEM_REF)
     {
-      unsigned HOST_WIDE_INT sz;
+      tree sz, wholesize;
 
       if (!osi || (object_size_type & OST_SUBOBJECT) != 0
 	  || TREE_CODE (TREE_OPERAND (pt_var, 0)) != SSA_NAME)
 	{
 	  compute_builtin_object_size (TREE_OPERAND (pt_var, 0),
 				       object_size_type & ~OST_SUBOBJECT, &sz);
+	  wholesize = sz;
 	}
       else
 	{
@@ -312,46 +410,47 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 	    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));
+	    {
+	      sz = object_sizes_get (osi, SSA_NAME_VERSION (var));
+	      wholesize = object_sizes_get (osi, SSA_NAME_VERSION (var), true);
+	    }
 	  else
-	    sz = unknown (object_size_type);
+	    sz = wholesize = size_unknown (object_size_type);
 	}
-      if (sz != unknown (object_size_type))
+      if (!size_unknown_p (sz, object_size_type))
 	{
-	  offset_int mem_offset;
-	  if (mem_ref_offset (pt_var).is_constant (&mem_offset))
-	    {
-	      offset_int dsz = wi::sub (sz, mem_offset);
-	      if (wi::neg_p (dsz))
-		sz = 0;
-	      else if (wi::fits_uhwi_p (dsz))
-		sz = dsz.to_uhwi ();
-	      else
-		sz = unknown (object_size_type);
-	    }
+	  tree offset = TREE_OPERAND (pt_var, 1);
+	  if (TREE_CODE (offset) != INTEGER_CST
+	      || TREE_CODE (sz) != INTEGER_CST)
+	    sz = wholesize = size_unknown (object_size_type);
 	  else
-	    sz = unknown (object_size_type);
+	    sz = size_for_offset (sz, offset, wholesize);
 	}
 
-      if (sz != unknown (object_size_type) && sz < offset_limit)
-	pt_var_size = size_int (sz);
+      if (!size_unknown_p (sz, object_size_type)
+	  && TREE_CODE (sz) == INTEGER_CST
+	  && compare_tree_int (sz, offset_limit) < 0)
+	{
+	  pt_var_size = sz;
+	  pt_var_wholesize = wholesize;
+	}
     }
   else if (DECL_P (pt_var))
     {
-      pt_var_size = decl_init_size (pt_var, object_size_type & OST_MINIMUM);
+      pt_var_size = pt_var_wholesize
+	= decl_init_size (pt_var, object_size_type & OST_MINIMUM);
       if (!pt_var_size)
 	return false;
     }
   else if (TREE_CODE (pt_var) == STRING_CST)
-    pt_var_size = TYPE_SIZE_UNIT (TREE_TYPE (pt_var));
+    pt_var_size = pt_var_wholesize = TYPE_SIZE_UNIT (TREE_TYPE (pt_var));
   else
     return false;
 
   if (pt_var_size)
     {
       /* Validate the size determined above.  */
-      if (!tree_fits_uhwi_p (pt_var_size)
-	  || tree_to_uhwi (pt_var_size) >= offset_limit)
+      if (compare_tree_int (pt_var_size, offset_limit) >= 0)
 	return false;
     }
 
@@ -496,28 +595,35 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 	      bytes = size_binop (MIN_EXPR, bytes, bytes2);
 	    }
 	}
+
+      wholebytes
+	= object_size_type & OST_SUBOBJECT ? var_size : pt_var_wholesize;
     }
   else if (!pt_var_size)
     return false;
   else
-    bytes = pt_var_size;
-
-  if (tree_fits_uhwi_p (bytes))
     {
-      *psize = tree_to_uhwi (bytes);
-      return true;
+      bytes = pt_var_size;
+      wholebytes = pt_var_wholesize;
     }
 
-  return false;
+  if (TREE_CODE (bytes) != INTEGER_CST
+      || TREE_CODE (wholebytes) != INTEGER_CST)
+    return false;
+
+  *psize = bytes;
+  if (pwholesize)
+    *pwholesize = wholebytes;
+  return true;
 }
 
 
 /* Compute __builtin_object_size for CALL, which is a GIMPLE_CALL.
    Handles calls to functions declared with attribute alloc_size.
    OBJECT_SIZE_TYPE is the second argument from __builtin_object_size.
-   If unknown, return unknown (object_size_type).  */
+   If unknown, return size_unknown (object_size_type).  */
 
-static unsigned HOST_WIDE_INT
+static tree
 alloc_object_size (const gcall *call, int object_size_type)
 {
   gcc_assert (is_gimple_call (call));
@@ -529,7 +635,7 @@ alloc_object_size (const gcall *call, int object_size_type)
     calltype = gimple_call_fntype (call);
 
   if (!calltype)
-    return unknown (object_size_type);
+    return size_unknown (object_size_type);
 
   /* Set to positions of alloc_size arguments.  */
   int arg1 = -1, arg2 = -1;
@@ -549,7 +655,7 @@ alloc_object_size (const gcall *call, int object_size_type)
       || (arg2 >= 0
 	  && (arg2 >= (int)gimple_call_num_args (call)
 	      || TREE_CODE (gimple_call_arg (call, arg2)) != INTEGER_CST)))
-    return unknown (object_size_type);
+    return size_unknown (object_size_type);
 
   tree bytes = NULL_TREE;
   if (arg2 >= 0)
@@ -559,10 +665,7 @@ alloc_object_size (const gcall *call, int object_size_type)
   else if (arg1 >= 0)
     bytes = fold_convert (sizetype, gimple_call_arg (call, arg1));
 
-  if (bytes && tree_fits_uhwi_p (bytes))
-    return tree_to_uhwi (bytes);
-
-  return unknown (object_size_type);
+  return bytes;
 }
 
 
@@ -598,13 +701,13 @@ pass_through_call (const gcall *call)
 
 bool
 compute_builtin_object_size (tree ptr, int object_size_type,
-			     unsigned HOST_WIDE_INT *psize)
+			     tree *psize)
 {
   gcc_assert (object_size_type >= 0 && object_size_type < OST_END);
 
   /* Set to unknown and overwrite just before returning if the size
      could be determined.  */
-  *psize = unknown (object_size_type);
+  *psize = size_unknown (object_size_type);
 
   if (! offset_limit)
     init_offset_limit ();
@@ -638,8 +741,7 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 						  psize))
 		{
 		  /* Return zero when the offset is out of bounds.  */
-		  unsigned HOST_WIDE_INT off = tree_to_shwi (offset);
-		  *psize = off < *psize ? *psize - off : 0;
+		  *psize = size_for_offset (*psize, offset);
 		  return true;
 		}
 	    }
@@ -747,12 +849,13 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 		print_generic_expr (dump_file, ssa_name (i),
 				    dump_flags);
 		fprintf (dump_file,
-			 ": %s %sobject size "
-			 HOST_WIDE_INT_PRINT_UNSIGNED "\n",
+			 ": %s %sobject size ",
 			 ((object_size_type & OST_MINIMUM) ? "minimum"
 			  : "maximum"),
-			 (object_size_type & OST_SUBOBJECT) ? "sub" : "",
-			 object_sizes_get (&osi, i));
+			 (object_size_type & OST_SUBOBJECT) ? "sub" : "");
+		print_generic_expr (dump_file, object_sizes_get (&osi, i),
+				    dump_flags);
+		fprintf (dump_file, "\n");
 	      }
 	}
 
@@ -761,7 +864,7 @@ compute_builtin_object_size (tree ptr, int object_size_type,
     }
 
   *psize = object_sizes_get (&osi, SSA_NAME_VERSION (ptr));
-  return *psize != unknown (object_size_type);
+  return !size_unknown_p (*psize, object_size_type);
 }
 
 /* Compute object_sizes for PTR, defined to VALUE, which is not an SSA_NAME.  */
@@ -771,7 +874,7 @@ expr_object_size (struct object_size_info *osi, tree ptr, tree value)
 {
   int object_size_type = osi->object_size_type;
   unsigned int varno = SSA_NAME_VERSION (ptr);
-  unsigned HOST_WIDE_INT bytes;
+  tree bytes, wholesize;
 
   gcc_assert (!object_sizes_unknown_p (object_size_type, varno));
   gcc_assert (osi->pass == 0);
@@ -784,11 +887,11 @@ expr_object_size (struct object_size_info *osi, tree ptr, tree value)
 	      || !POINTER_TYPE_P (TREE_TYPE (value)));
 
   if (TREE_CODE (value) == ADDR_EXPR)
-    addr_object_size (osi, value, object_size_type, &bytes);
+    addr_object_size (osi, value, object_size_type, &bytes, &wholesize);
   else
-    bytes = unknown (object_size_type);
+    bytes = wholesize = size_unknown (object_size_type);
 
-  object_sizes_set (osi, varno, bytes);
+  object_sizes_set (osi, varno, bytes, wholesize);
 }
 
 
@@ -799,16 +902,14 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call)
 {
   int object_size_type = osi->object_size_type;
   unsigned int varno = SSA_NAME_VERSION (ptr);
-  unsigned HOST_WIDE_INT bytes;
 
   gcc_assert (is_gimple_call (call));
 
   gcc_assert (!object_sizes_unknown_p (object_size_type, varno));
   gcc_assert (osi->pass == 0);
+  tree bytes = alloc_object_size (call, object_size_type);
 
-  bytes = alloc_object_size (call, object_size_type);
-
-  object_sizes_set (osi, varno, bytes);
+  object_sizes_set (osi, varno, bytes, bytes);
 }
 
 
@@ -822,8 +923,9 @@ unknown_object_size (struct object_size_info *osi, tree ptr)
 
   gcc_checking_assert (!object_sizes_unknown_p (object_size_type, varno));
   gcc_checking_assert (osi->pass == 0);
+  tree bytes = size_unknown (object_size_type);
 
-  object_sizes_set (osi, varno, unknown (object_size_type));
+  object_sizes_set (osi, varno, bytes, bytes);
 }
 
 
@@ -831,30 +933,22 @@ unknown_object_size (struct object_size_info *osi, tree ptr)
    the object size might need reexamination later.  */
 
 static bool
-merge_object_sizes (struct object_size_info *osi, tree dest, tree orig,
-		    unsigned HOST_WIDE_INT offset)
+merge_object_sizes (struct object_size_info *osi, tree dest, tree orig)
 {
   int object_size_type = osi->object_size_type;
   unsigned int varno = SSA_NAME_VERSION (dest);
-  unsigned HOST_WIDE_INT orig_bytes;
+  tree orig_bytes, wholesize;
 
   if (object_sizes_unknown_p (object_size_type, varno))
     return false;
-  if (offset >= offset_limit)
-    {
-      object_sizes_set (osi, varno, unknown (object_size_type));
-      return false;
-    }
 
   if (osi->pass == 0)
     collect_object_sizes_for (osi, orig);
 
   orig_bytes = object_sizes_get (osi, SSA_NAME_VERSION (orig));
-  if (orig_bytes != unknown (object_size_type))
-    orig_bytes = (offset > orig_bytes)
-		 ? HOST_WIDE_INT_0U : orig_bytes - offset;
+  wholesize = object_sizes_get (osi, SSA_NAME_VERSION (orig), true);
 
-  if (object_sizes_set (osi, varno, orig_bytes))
+  if (object_sizes_set (osi, varno, orig_bytes, wholesize))
     osi->changed = true;
 
   return bitmap_bit_p (osi->reexamine, SSA_NAME_VERSION (orig));
@@ -870,8 +964,9 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
 {
   int object_size_type = osi->object_size_type;
   unsigned int varno = SSA_NAME_VERSION (var);
-  unsigned HOST_WIDE_INT bytes;
+  tree bytes, wholesize;
   tree op0, op1;
+  bool reexamine = false;
 
   if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
     {
@@ -896,31 +991,38 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
       && (TREE_CODE (op0) == SSA_NAME
 	  || TREE_CODE (op0) == ADDR_EXPR))
     {
-      if (! tree_fits_uhwi_p (op1))
-	bytes = unknown (object_size_type);
-      else if (TREE_CODE (op0) == SSA_NAME)
-	return merge_object_sizes (osi, var, op0, tree_to_uhwi (op1));
+      if (TREE_CODE (op0) == SSA_NAME)
+	{
+	  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
 	{
-	  unsigned HOST_WIDE_INT off = tree_to_uhwi (op1);
-
-          /* op0 will be ADDR_EXPR here.  */
-	  addr_object_size (osi, op0, object_size_type, &bytes);
-	  if (bytes == unknown (object_size_type))
-	    ;
-	  else if (off > offset_limit)
-	    bytes = unknown (object_size_type);
-	  else if (off > bytes)
-	    bytes = 0;
-	  else
-	    bytes -= off;
+	  /* 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);
     }
   else
-    bytes = unknown (object_size_type);
+    bytes = wholesize = size_unknown (object_size_type);
 
-  object_sizes_set (osi, varno, bytes);
-  return false;
+  if (object_sizes_set (osi, varno, bytes, wholesize))
+    osi->changed = true;
+  return reexamine;
 }
 
 
@@ -945,7 +1047,7 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   else_ = gimple_assign_rhs3 (stmt);
 
   if (TREE_CODE (then_) == SSA_NAME)
-    reexamine |= merge_object_sizes (osi, var, then_, 0);
+    reexamine |= merge_object_sizes (osi, var, then_);
   else
     expr_object_size (osi, var, then_);
 
@@ -953,7 +1055,7 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
     return reexamine;
 
   if (TREE_CODE (else_) == SSA_NAME)
-    reexamine |= merge_object_sizes (osi, var, else_, 0);
+    reexamine |= merge_object_sizes (osi, var, else_);
   else
     expr_object_size (osi, var, else_);
 
@@ -970,11 +1072,11 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
    object size is object size of the first operand minus the constant.
    If the constant is bigger than the number of remaining bytes until the
    end of the object, object size is 0, but if it is instead a pointer
-   subtraction, object size is unknown (object_size_type).
+   subtraction, object size is size_unknown (object_size_type).
    To differentiate addition from subtraction, ADDR_EXPR returns
-   unknown (object_size_type) for all objects bigger than half of the address
-   space, and constants less than half of the address space are considered
-   addition, while bigger constants subtraction.
+   size_unknown (object_size_type) for all objects bigger than half of the
+   address space, and constants less than half of the address space are
+   considered addition, while bigger constants subtraction.
    For a memcpy like GIMPLE_CALL that always returns one of its arguments, the
    object size is object size of that argument.
    Otherwise, object size is the maximum of object sizes of variables
@@ -997,8 +1099,9 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 	{
 	  /* Initialize to 0 for maximum size and M1U for minimum size so that
 	     it gets immediately overridden.  */
-	  object_sizes_set_force (osi, varno,
-				  unknown (object_size_type ^ OST_MINIMUM));
+	  object_sizes_initialize (osi, varno,
+				   size_initval (object_size_type),
+				   size_initval (object_size_type));
 	}
       else
 	{
@@ -1041,7 +1144,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
           {
             if (TREE_CODE (rhs) == SSA_NAME
                 && POINTER_TYPE_P (TREE_TYPE (rhs)))
-              reexamine = merge_object_sizes (osi, var, rhs, 0);
+	      reexamine = merge_object_sizes (osi, var, rhs);
             else
               expr_object_size (osi, var, rhs);
           }
@@ -1058,7 +1161,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
           {
             if (TREE_CODE (arg) == SSA_NAME
                 && POINTER_TYPE_P (TREE_TYPE (arg)))
-              reexamine = merge_object_sizes (osi, var, arg, 0);
+	      reexamine = merge_object_sizes (osi, var, arg);
             else
               expr_object_size (osi, var, arg);
           }
@@ -1069,7 +1172,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 
     case GIMPLE_ASM:
       /* Pointers defined by __asm__ statements can point anywhere.  */
-      object_sizes_set (osi, varno, unknown (object_size_type));
+      unknown_object_size (osi, var);
       break;
 
     case GIMPLE_NOP:
@@ -1078,7 +1181,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 	expr_object_size (osi, var, SSA_NAME_VAR (var));
       else
 	/* Uninitialized SSA names point nowhere.  */
-	object_sizes_set (osi, varno, unknown (object_size_type));
+	unknown_object_size (osi, var);
       break;
 
     case GIMPLE_PHI:
@@ -1093,7 +1196,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 	      break;
 
 	    if (TREE_CODE (rhs) == SSA_NAME)
-	      reexamine |= merge_object_sizes (osi, var, rhs, 0);
+	      reexamine |= merge_object_sizes (osi, var, rhs);
 	    else if (osi->pass == 0)
 	      expr_object_size (osi, var, rhs);
 	  }
@@ -1144,7 +1247,8 @@ check_for_plus_in_loops_1 (struct object_size_info *osi, tree var,
 	      --sp;
 	      bitmap_clear_bit (osi->reexamine, *sp);
 	      bitmap_set_bit (computed[osi->object_size_type], *sp);
-	      object_sizes_set_force (osi, *sp, 0);
+	      object_sizes_set (osi, *sp, size_zero_node,
+				object_sizes_get (osi, *sp, true));
 	      if (*sp == varno)
 		break;
 	    }
@@ -1244,7 +1348,8 @@ check_for_plus_in_loops (struct object_size_info *osi, tree var)
 
       gcc_assert (TREE_CODE (cst) == INTEGER_CST);
 
-      if (integer_zerop (cst))
+      /* Skip non-positive offsets.  */
+      if (integer_zerop (cst) || compare_tree_int (cst, offset_limit) > 0)
         return;
 
       osi->depths[SSA_NAME_VERSION (basevar)] = 1;
@@ -1335,17 +1440,17 @@ object_sizes_execute (function *fun, bool insert_min_max_p)
 			  || TREE_CODE (ptr) == SSA_NAME))
 		    {
 		      tree type = TREE_TYPE (lhs);
-		      unsigned HOST_WIDE_INT bytes;
+		      tree bytes;
 		      if (compute_builtin_object_size (ptr, object_size_type,
 						       &bytes)
-			  && wi::fits_to_tree_p (bytes, type))
+			  && int_fits_type_p (bytes, type))
 			{
 			  tree tem = make_ssa_name (type);
 			  gimple_call_set_lhs (call, tem);
 			  enum tree_code code
 			    = (object_size_type & OST_MINIMUM
 			       ? MAX_EXPR : MIN_EXPR);
-			  tree cst = build_int_cstu (type, bytes);
+			  tree cst = fold_convert (type, bytes);
 			  gimple *g
 			    = gimple_build_assign (lhs, code, tem, cst);
 			  gsi_insert_after (&i, g, GSI_NEW_STMT);
diff --git a/gcc/tree-object-size.h b/gcc/tree-object-size.h
index ef18aea50db..b2d6a58324c 100644
--- a/gcc/tree-object-size.h
+++ b/gcc/tree-object-size.h
@@ -22,7 +22,7 @@ along with GCC; see the file COPYING3.  If not see
 
 extern void init_object_sizes (void);
 extern void fini_object_sizes (void);
-extern bool compute_builtin_object_size (tree, int, unsigned HOST_WIDE_INT *);
+extern bool compute_builtin_object_size (tree, int, tree *);
 extern tree decl_init_size (tree, bool);
 
 #endif  // GCC_TREE_OBJECT_SIZE_H
diff --git a/gcc/ubsan.c b/gcc/ubsan.c
index ba9adf0ad96..364cf174bf0 100644
--- a/gcc/ubsan.c
+++ b/gcc/ubsan.c
@@ -2160,9 +2160,8 @@ instrument_object_size (gimple_stmt_iterator *gsi, tree t, bool is_lhs)
   if (decl_p)
     base_addr = build1 (ADDR_EXPR,
 			build_pointer_type (TREE_TYPE (base)), base);
-  unsigned HOST_WIDE_INT size;
-  if (compute_builtin_object_size (base_addr, 0, &size))
-    sizet = build_int_cst (sizetype, size);
+  if (compute_builtin_object_size (base_addr, 0, &sizet))
+    ;
   else if (optimize)
     {
       if (LOCATION_LOCUS (loc) == UNKNOWN_LOCATION)
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v4 2/6] __builtin_dynamic_object_size: Recognize builtin
  2021-12-01 14:27 ` [PATCH v4 0/6] __builtin_dynamic_object_size Siddhesh Poyarekar
  2021-12-01 14:27   ` [PATCH v4 1/6] tree-object-size: Use trees and support negative offsets Siddhesh Poyarekar
@ 2021-12-01 14:27   ` Siddhesh Poyarekar
  2021-12-15 15:24     ` Jakub Jelinek
  2021-12-01 14:27   ` [PATCH v4 3/6] tree-object-size: Support dynamic sizes in conditions Siddhesh Poyarekar
                     ` (3 subsequent siblings)
  5 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-12-01 14:27 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Recognize the __builtin_dynamic_object_size builtin and add paths in the
object size path to deal with it, but treat it like
__builtin_object_size for now.  Also add tests to provide the same
testing coverage for the new builtin name.

gcc/ChangeLog:

	* builtins.def (BUILT_IN_DYNAMIC_OBJECT_SIZE): New builtin.
	* tree-object-size.h: Move object size type bits enum from
	tree-object-size.c and add new value OST_DYNAMIC.
	* builtins.c (expand_builtin, fold_builtin_2): Handle it.
	(fold_builtin_object_size): Handle new builtin and adjust for
	change to compute_builtin_object_size.
	* tree-object-size.c: Include builtins.h.
	(compute_builtin_object_size): Adjust.
	(early_object_sizes_execute_one,
	dynamic_object_sizes_execute_one): New functions.
	(object_sizes_execute): Rename insert_min_max_p argument to
	early.  Handle BUILT_IN_DYNAMIC_OBJECT_SIZE and call the new
	functions.
	doc/extend.texi (__builtin_dynamic_object_size): Document new
	builtin.

gcc/testsuite/ChangeLog:

	* g++.dg/ext/builtin-dynamic-object-size1.C: New test.
	* g++.dg/ext/builtin-dynamic-object-size2.C: Likewise.
	* gcc.dg/builtin-dynamic-alloc-size.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-1.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-10.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-11.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-12.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-13.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-14.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-15.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-16.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-17.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-18.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-19.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-2.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-3.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-4.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-5.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-6.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-7.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-8.c: Likewise.
	* gcc.dg/builtin-dynamic-object-size-9.c: Likewise.
	* gcc.dg/builtin-object-size-16.c: Adjust to allow inclusion
	from builtin-dynamic-object-size-16.c.
	* gcc.dg/builtin-object-size-17.c: Likewise.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 gcc/builtins.c                                |  11 +-
 gcc/builtins.def                              |   1 +
 gcc/doc/extend.texi                           |  13 ++
 .../g++.dg/ext/builtin-dynamic-object-size1.C |   5 +
 .../g++.dg/ext/builtin-dynamic-object-size2.C |   5 +
 .../gcc.dg/builtin-dynamic-alloc-size.c       |   7 +
 .../gcc.dg/builtin-dynamic-object-size-1.c    |   6 +
 .../gcc.dg/builtin-dynamic-object-size-10.c   |   9 ++
 .../gcc.dg/builtin-dynamic-object-size-11.c   |   7 +
 .../gcc.dg/builtin-dynamic-object-size-12.c   |   5 +
 .../gcc.dg/builtin-dynamic-object-size-13.c   |   5 +
 .../gcc.dg/builtin-dynamic-object-size-14.c   |   5 +
 .../gcc.dg/builtin-dynamic-object-size-15.c   |   5 +
 .../gcc.dg/builtin-dynamic-object-size-16.c   |   6 +
 .../gcc.dg/builtin-dynamic-object-size-17.c   |   7 +
 .../gcc.dg/builtin-dynamic-object-size-18.c   |   8 +
 .../gcc.dg/builtin-dynamic-object-size-19.c   | 104 ++++++++++++
 .../gcc.dg/builtin-dynamic-object-size-2.c    |   6 +
 .../gcc.dg/builtin-dynamic-object-size-3.c    |   6 +
 .../gcc.dg/builtin-dynamic-object-size-4.c    |   6 +
 .../gcc.dg/builtin-dynamic-object-size-5.c    |   7 +
 .../gcc.dg/builtin-dynamic-object-size-6.c    |   5 +
 .../gcc.dg/builtin-dynamic-object-size-7.c    |   5 +
 .../gcc.dg/builtin-dynamic-object-size-8.c    |   5 +
 .../gcc.dg/builtin-dynamic-object-size-9.c    |   5 +
 gcc/testsuite/gcc.dg/builtin-object-size-16.c |   2 +
 gcc/testsuite/gcc.dg/builtin-object-size-17.c |   2 +
 gcc/tree-object-size.c                        | 152 +++++++++++++-----
 gcc/tree-object-size.h                        |  10 ++
 29 files changed, 378 insertions(+), 42 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size1.C
 create mode 100644 gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size2.C
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-alloc-size.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-11.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-12.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-13.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-14.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-15.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-16.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-17.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-18.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-19.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-6.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-7.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-8.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-9.c

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 309c3010de3..3b815a6e42a 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -178,7 +178,7 @@ static rtx expand_builtin_memory_chk (tree, rtx, machine_mode,
 				      enum built_in_function);
 static void maybe_emit_chk_warning (tree, enum built_in_function);
 static void maybe_emit_sprintf_chk_warning (tree, enum built_in_function);
-static tree fold_builtin_object_size (tree, tree);
+static tree fold_builtin_object_size (tree, tree, enum built_in_function);
 
 unsigned HOST_WIDE_INT target_newline;
 unsigned HOST_WIDE_INT target_percent;
@@ -7908,6 +7908,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
       return const0_rtx;
 
     case BUILT_IN_OBJECT_SIZE:
+    case BUILT_IN_DYNAMIC_OBJECT_SIZE:
       return expand_builtin_object_size (exp);
 
     case BUILT_IN_MEMCPY_CHK:
@@ -9314,7 +9315,8 @@ fold_builtin_2 (location_t loc, tree expr, tree fndecl, tree arg0, tree arg1)
       break;
 
     case BUILT_IN_OBJECT_SIZE:
-      return fold_builtin_object_size (arg0, arg1);
+    case BUILT_IN_DYNAMIC_OBJECT_SIZE:
+      return fold_builtin_object_size (arg0, arg1, fcode);
 
     case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE:
       return fold_builtin_atomic_always_lock_free (arg0, arg1);
@@ -10252,7 +10254,7 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
    if possible.  */
 
 static tree
-fold_builtin_object_size (tree ptr, tree ost)
+fold_builtin_object_size (tree ptr, tree ost, enum built_in_function fcode)
 {
   tree bytes;
   int object_size_type;
@@ -10276,6 +10278,9 @@ fold_builtin_object_size (tree ptr, tree ost)
   if (TREE_SIDE_EFFECTS (ptr))
     return build_int_cst_type (size_type_node, object_size_type < 2 ? -1 : 0);
 
+  if (fcode == BUILT_IN_DYNAMIC_OBJECT_SIZE)
+    object_size_type |= OST_DYNAMIC;
+
   if (TREE_CODE (ptr) == ADDR_EXPR)
     {
       compute_builtin_object_size (ptr, object_size_type, &bytes);
diff --git a/gcc/builtins.def b/gcc/builtins.def
index 45a09b4d42d..a92948de2b8 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -972,6 +972,7 @@ DEF_BUILTIN_STUB (BUILT_IN_STRNCMP_EQ, "__builtin_strncmp_eq")
 
 /* Object size checking builtins.  */
 DEF_GCC_BUILTIN	       (BUILT_IN_OBJECT_SIZE, "object_size", BT_FN_SIZE_CONST_PTR_INT, ATTR_CONST_NOTHROW_LEAF_LIST)
+DEF_GCC_BUILTIN	       (BUILT_IN_DYNAMIC_OBJECT_SIZE, "dynamic_object_size", BT_FN_SIZE_CONST_PTR_INT, ATTR_PURE_NOTHROW_LEAF_LIST)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_MEMCPY_CHK, "__memcpy_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_NOTHROW_NONNULL_LEAF)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_MEMMOVE_CHK, "__memmove_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_NOTHROW_NONNULL_LEAF)
 DEF_EXT_LIB_BUILTIN    (BUILT_IN_MEMPCPY_CHK, "__mempcpy_chk", BT_FN_PTR_PTR_CONST_PTR_SIZE_SIZE, ATTR_RETNONNULL_NOTHROW_LEAF)
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index ef654d7b878..7bb3c5b3410 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -12712,6 +12712,7 @@ __atomic_store_n(&lockvar, 0, __ATOMIC_RELEASE|__ATOMIC_HLE_RELEASE);
 @node Object Size Checking
 @section Object Size Checking Built-in Functions
 @findex __builtin_object_size
+@findex __builtin_dynamic_object_size
 @findex __builtin___memcpy_chk
 @findex __builtin___mempcpy_chk
 @findex __builtin___memmove_chk
@@ -12779,6 +12780,18 @@ assert (__builtin_object_size (q, 1) == sizeof (var.b));
 @end smallexample
 @end deftypefn
 
+@deftypefn {Built-in Function} {size_t} __builtin_dynamic_object_size (const void * @var{ptr}, int @var{type})
+is similar to @code{__builtin_object_size} in that it returns a number of bytes
+from @var{ptr} to the end of the object @var{ptr} pointer points to, except
+that the size returned may not be a constant.  This results in successful
+evaluation of object size estimates in a wider range of use cases and can be
+more precise than @code{__builtin_object_size}, but it incurs a performance
+penalty since it may add a runtime overhead on size computation.  Semantics of
+@var{type} as well as return values in case it is not possible to determine
+which objects @var{ptr} points to at compile time are the same as in the case
+of @code{__builtin_object_size}.
+@end deftypefn
+
 There are built-in functions added for many common string operation
 functions, e.g., for @code{memcpy} @code{__builtin___memcpy_chk}
 built-in is provided.  This built-in has an additional last argument,
diff --git a/gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size1.C b/gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size1.C
new file mode 100644
index 00000000000..b11ac200751
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size1.C
@@ -0,0 +1,5 @@
+// { dg-do run }
+// { dg-options "-O2" }
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size1.C"
diff --git a/gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size2.C b/gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size2.C
new file mode 100644
index 00000000000..6e52cf38533
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size2.C
@@ -0,0 +1,5 @@
+// { dg-do run }
+// { dg-options "-O2" }
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size2.C"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-alloc-size.c b/gcc/testsuite/gcc.dg/builtin-dynamic-alloc-size.c
new file mode 100644
index 00000000000..9d0eadd6be4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-alloc-size.c
@@ -0,0 +1,7 @@
+/* { dg-do compile }
+   { dg-require-effective-target alloca }
+   { dg-additional-options "-O2 -fdump-tree-optimized" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-alloc-size.c"
+/* { dg-final { scan-tree-dump-not "abort" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c
new file mode 100644
index 00000000000..7cc8b1c9488
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c
@@ -0,0 +1,6 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+/* { dg-require-effective-target alloca } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-1.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
new file mode 100644
index 00000000000..bc880a589ae
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-early_objsz-details" } */
+// { dg-skip-if "packed attribute missing for drone_source_packet" { "epiphany-*-*" } }
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-10.c"
+
+/* { dg-final { scan-tree-dump "maximum object size 21" "early_objsz" } } */
+/* { dg-final { scan-tree-dump "maximum subobject size 16" "early_objsz" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-11.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-11.c
new file mode 100644
index 00000000000..65dcec9fcae
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-11.c
@@ -0,0 +1,7 @@
+/* PR48985 */
+/* { dg-do run } */
+/* { dg-options "-std=gnu89" } */
+/* { dg-skip-if "packed attribute missing for struct s" { "epiphany-*-*" } } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-11.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-12.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-12.c
new file mode 100644
index 00000000000..f0ce050a943
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-12.c
@@ -0,0 +1,5 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-12.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-13.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-13.c
new file mode 100644
index 00000000000..555e23522dc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-13.c
@@ -0,0 +1,5 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-13.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-14.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-14.c
new file mode 100644
index 00000000000..26207200191
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-14.c
@@ -0,0 +1,5 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-14.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-15.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-15.c
new file mode 100644
index 00000000000..cd8a941438d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-15.c
@@ -0,0 +1,5 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-15.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-16.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-16.c
new file mode 100644
index 00000000000..cc775089410
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-16.c
@@ -0,0 +1,6 @@
+/* { dg-do run } */
+/* { dg-options "-O0" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+char ax2[];               /* { dg-warning "assumed to have one element" } */
+#include "builtin-object-size-16.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-17.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-17.c
new file mode 100644
index 00000000000..f5c57a8788d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-17.c
@@ -0,0 +1,7 @@
+/* { dg-do compile } */
+/* { dg-options "-O0 -fdump-tree-ssa" } */
+
+char ax2[];               /* { dg-warning "assumed to have one element" } */
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-17.c"
+/* { dg-final { scan-tree-dump-not "failure_on_line" "ssa" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-18.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-18.c
new file mode 100644
index 00000000000..70c1ebcff21
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-18.c
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* __stpncpy_chk could return buf up to buf + 64, so
+   the minimum object size might be far smaller than 64.  */
+/* { dg-final { scan-tree-dump-not "return 64;" "optimized" } } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-18.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-19.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-19.c
new file mode 100644
index 00000000000..44141a38607
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-19.c
@@ -0,0 +1,104 @@
+/* PR tree-optimization/88372 - alloc_size attribute is ignored
+   on function pointers { dg-do compile }
+   { dg-options "-O2 -fdump-tree-optimized" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-18.c"
+
+typedef __SIZE_TYPE__ size_t;
+
+#define ATTR(...) __attribute__ ((__VA_ARGS__))
+#define CONCAT(x, y) x ## y
+#define CAT(x, y) CONCAT (x, y)
+#define FAILNAME(name) CAT (call_ ## name ##_on_line_, __LINE__)
+
+#define FAIL(name) do {				\
+    extern void FAILNAME (name) (void);		\
+    FAILNAME (name)();				\
+  } while (0)
+
+/* Macro to emit a call to function named
+   call_in_true_branch_not_eliminated_on_line_NNN()
+   for each call that's expected to be eliminated.  The dg-final
+   scan-tree-dump-time directive at the bottom of the test verifies
+   that no such call appears in output.  */
+#define ELIM(expr)							\
+  if (!(expr)) FAIL (in_true_branch_not_eliminated); else (void)0
+
+void sink (void*);
+
+#define T(alloc, n) do {			\
+    void *p = alloc;				\
+    sink (p);					\
+    ELIM (n == __builtin_object_size (p, 0));	\
+    ELIM (n == __builtin_object_size (p, 1));	\
+    ELIM (n == __builtin_object_size (p, 2));	\
+    ELIM (n == __builtin_object_size (p, 3));	\
+  } while (0)
+
+
+ATTR (alloc_size (1)) void* (*alloc_1_x)(size_t, size_t);
+ATTR (alloc_size (2)) void* (*alloc_x_2)(size_t, size_t);
+
+/* Verify that things work when attribute alloc_size is applied
+   to a typedef that is then used to declared a pointer.  */
+typedef ATTR (alloc_size (1, 2)) void* (alloc_1_2_t)(size_t, size_t);
+
+void test_alloc_ptr (alloc_1_2_t *alloc_1_2)
+{
+  T (alloc_1_x (0, 0), 0);
+  T (alloc_1_x (1, 0), 1);
+  T (alloc_1_x (3, 0), 3);
+  T (alloc_1_x (9, 5), 9);
+
+  T (alloc_x_2 (0, 0), 0);
+  T (alloc_x_2 (1, 0), 0);
+  T (alloc_x_2 (0, 1), 1);
+  T (alloc_x_2 (9, 5), 5);
+
+  T (alloc_1_2 (0, 0), 0);
+  T (alloc_1_2 (1, 0), 0);
+  T (alloc_1_2 (0, 1), 0);
+  T (alloc_1_2 (9, 5), 45);
+}
+
+/* Verify that object size is detected even in indirect calls via
+   function pointers to built-in allocation functions, even without
+   explicit use of attribute alloc_size on the pointers.  */
+
+typedef void *(allocfn_1) (size_t);
+typedef void *(allocfn_1_2) (size_t, size_t);
+
+static inline void *
+call_alloc (allocfn_1 *fn1, allocfn_1_2 *fn2, size_t n1, size_t n2)
+{
+  return fn1 ? fn1 (n1) : fn2 (n1, n2);
+}
+
+static inline void *
+call_malloc (size_t n)
+{
+  return call_alloc (__builtin_malloc, 0, n, 0);
+}
+
+static inline void *
+call_calloc (size_t n1, size_t n2)
+{
+  return call_alloc (0, __builtin_calloc, n1, n2);
+}
+
+void test_builtin_ptr (void)
+{
+  T (call_malloc (0), 0);
+  T (call_malloc (1), 1);
+  T (call_malloc (9), 9);
+
+  T (call_calloc (0, 0), 0);
+  T (call_calloc (0, 1), 0);
+  T (call_calloc (1, 0), 0);
+  T (call_calloc (1, 1), 1);
+  T (call_calloc (1, 3), 3);
+  T (call_calloc (2, 3), 6);
+}
+
+/* { dg-final { scan-tree-dump-not "not_eliminated" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c
new file mode 100644
index 00000000000..267dbf48ca7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c
@@ -0,0 +1,6 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+/* { dg-require-effective-target alloca } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-2.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c
new file mode 100644
index 00000000000..fb9dc56da7e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c
@@ -0,0 +1,6 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+/* { dg-require-effective-target alloca } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-3.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c
new file mode 100644
index 00000000000..870548b4206
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c
@@ -0,0 +1,6 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+/* { dg-require-effective-target alloca } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-4.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c
new file mode 100644
index 00000000000..698b03c34be
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c
@@ -0,0 +1,7 @@
+/* { dg-do compile { target i?86-*-linux* i?86-*-gnu* x86_64-*-linux* } } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-5.c"
+
+/* { dg-final { scan-assembler-not "abort" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-6.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-6.c
new file mode 100644
index 00000000000..6a275ce5b37
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-6.c
@@ -0,0 +1,5 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-6.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-7.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-7.c
new file mode 100644
index 00000000000..e2a65994687
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-7.c
@@ -0,0 +1,5 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-7.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-8.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-8.c
new file mode 100644
index 00000000000..e7af383d9b6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-8.c
@@ -0,0 +1,5 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-8.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-9.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-9.c
new file mode 100644
index 00000000000..19021bc2ce9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-9.c
@@ -0,0 +1,5 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define __builtin_object_size __builtin_dynamic_object_size
+#include "builtin-object-size-9.c"
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-16.c b/gcc/testsuite/gcc.dg/builtin-object-size-16.c
index 48229390bfd..f26d5d97a5e 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-16.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-16.c
@@ -54,7 +54,9 @@ static int nfails;
 typedef __SIZE_TYPE__ size_t;
 
 extern char ax[];
+#ifndef __builtin_object_size
 char ax2[];               /* { dg-warning "assumed to have one element" } */
+#endif
 
 extern char a0[0];
 static char a1[1];
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-17.c b/gcc/testsuite/gcc.dg/builtin-object-size-17.c
index 0497bbf4505..28f8414636b 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-17.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-17.c
@@ -49,7 +49,9 @@
 typedef __SIZE_TYPE__ size_t;
 
 extern char ax[];
+#ifndef __builtin_object_size
 char ax2[];               /* { dg-warning "assumed to have one element" } */
+#endif
 
 extern char a0[0];
 static char a1[1];
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index ccac60001db..c4f3fbeb8f5 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -34,6 +34,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-cfg.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "builtins.h"
 
 struct object_size_info
 {
@@ -53,13 +54,6 @@ struct GTY(()) object_size
   tree wholesize;
 };
 
-enum
-{
-  OST_SUBOBJECT = 1,
-  OST_MINIMUM = 2,
-  OST_END = 4,
-};
-
 static tree compute_object_offset (const_tree, const_tree);
 static bool addr_object_size (struct object_size_info *,
 			      const_tree, int, tree *, tree *t = NULL);
@@ -759,8 +753,9 @@ compute_builtin_object_size (tree ptr, int object_size_type,
       object_sizes_grow (object_size_type);
       if (dump_file)
 	{
-	  fprintf (dump_file, "Computing %s %sobject size for ",
+	  fprintf (dump_file, "Computing %s %s%sobject size for ",
 		   (object_size_type & OST_MINIMUM) ? "minimum" : "maximum",
+		   (object_size_type & OST_DYNAMIC) ? "dynamic " : "",
 		   (object_size_type & OST_SUBOBJECT) ? "sub" : "");
 	  print_generic_expr (dump_file, ptr, dump_flags);
 	  fprintf (dump_file, ":\n");
@@ -849,9 +844,10 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 		print_generic_expr (dump_file, ssa_name (i),
 				    dump_flags);
 		fprintf (dump_file,
-			 ": %s %sobject size ",
+			 ": %s %s%sobject size ",
 			 ((object_size_type & OST_MINIMUM) ? "minimum"
 			  : "maximum"),
+			 (object_size_type & OST_DYNAMIC) ? "dynamic " : "",
 			 (object_size_type & OST_SUBOBJECT) ? "sub" : "");
 		print_generic_expr (dump_file, object_sizes_get (&osi, i),
 				    dump_flags);
@@ -1403,8 +1399,85 @@ do_valueize (tree t)
   return t;
 }
 
+/* Process a __builtin_object_size or __builtin_dynamic_object_size call in
+   CALL early for subobjects before any object information is lost due to
+   optimization.  Insert a MIN or MAX expression of the result and
+   __builtin_object_size at I so that it may be processed in the second pass.
+   __builtin_dynamic_object_size is treated like __builtin_object_size here
+   since we're only looking for constant bounds.  */
+
+static void
+early_object_sizes_execute_one (gimple_stmt_iterator *i, gimple *call)
+{
+  tree ost = gimple_call_arg (call, 1);
+  tree lhs = gimple_call_lhs (call);
+  gcc_assert (lhs != NULL_TREE);
+
+  if (!tree_fits_uhwi_p (ost))
+    return;
+
+  unsigned HOST_WIDE_INT object_size_type = tree_to_uhwi (ost);
+  tree ptr = gimple_call_arg (call, 0);
+
+  if (object_size_type != 1 && object_size_type != 3)
+    return;
+
+  if (TREE_CODE (ptr) != ADDR_EXPR && TREE_CODE (ptr) != SSA_NAME)
+    return;
+
+  tree type = TREE_TYPE (lhs);
+  tree bytes;
+  if (!compute_builtin_object_size (ptr, object_size_type, &bytes)
+      || !int_fits_type_p (bytes, type))
+    return;
+
+  tree tem = make_ssa_name (type);
+  gimple_call_set_lhs (call, tem);
+  enum tree_code code = object_size_type & OST_MINIMUM ? MAX_EXPR : MIN_EXPR;
+  tree cst = fold_convert (type, bytes);
+  gimple *g = gimple_build_assign (lhs, code, tem, cst);
+  gsi_insert_after (i, g, GSI_NEW_STMT);
+  update_stmt (call);
+}
+
+/* Attempt to fold one __builtin_dynamic_object_size call in CALL into an
+   expression and insert it at I.  Return true if it succeeds.  */
+
+static bool
+dynamic_object_sizes_execute_one (gimple_stmt_iterator *i, gimple *call)
+{
+  gcc_assert (gimple_call_num_args (call) == 2);
+
+  tree args[2];
+  args[0] = gimple_call_arg (call, 0);
+  args[1] = gimple_call_arg (call, 1);
+
+  location_t loc = EXPR_LOC_OR_LOC (args[0], input_location);
+  tree result_type = gimple_call_return_type (as_a <gcall *> (call));
+  tree result = fold_builtin_call_array (loc, result_type,
+					 gimple_call_fn (call), 2, args);
+
+  if (!result)
+    return false;
+
+  /* fold_builtin_call_array may wrap the result inside a
+     NOP_EXPR.  */
+  STRIP_NOPS (result);
+  gimplify_and_update_call_from_tree (i, result);
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "Simplified (dynamic)\n  ");
+      print_gimple_stmt (dump_file, call, 0, dump_flags);
+      fprintf (dump_file, " to ");
+      print_generic_expr (dump_file, result);
+      fprintf (dump_file, "\n");
+    }
+  return true;
+}
+
 static unsigned int
-object_sizes_execute (function *fun, bool insert_min_max_p)
+object_sizes_execute (function *fun, bool early)
 {
   basic_block bb;
   FOR_EACH_BB_FN (bb, fun)
@@ -1413,8 +1486,12 @@ object_sizes_execute (function *fun, bool insert_min_max_p)
       for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i))
 	{
 	  tree result;
+	  bool dynamic = false;
+
 	  gimple *call = gsi_stmt (i);
-	  if (!gimple_call_builtin_p (call, BUILT_IN_OBJECT_SIZE))
+	  if (gimple_call_builtin_p (call, BUILT_IN_DYNAMIC_OBJECT_SIZE))
+	    dynamic = true;
+	  else if (!gimple_call_builtin_p (call, BUILT_IN_OBJECT_SIZE))
 	    continue;
 
 	  tree lhs = gimple_call_lhs (call);
@@ -1423,42 +1500,39 @@ object_sizes_execute (function *fun, bool insert_min_max_p)
 
 	  init_object_sizes ();
 
-	  /* If insert_min_max_p, only attempt to fold
+	  /* If early, only attempt to fold
 	     __builtin_object_size (x, 1) and __builtin_object_size (x, 3),
 	     and rather than folding the builtin to the constant if any,
 	     create a MIN_EXPR or MAX_EXPR of the __builtin_object_size
-	     call result and the computed constant.  */
-	  if (insert_min_max_p)
+	     call result and the computed constant.  Do the same for
+	     __builtin_dynamic_object_size too.  */
+	  if (early)
 	    {
-	      tree ost = gimple_call_arg (call, 1);
-	      if (tree_fits_uhwi_p (ost))
+	      early_object_sizes_execute_one (&i, call);
+	      continue;
+	    }
+
+	  if (dynamic)
+	    {
+	      if (dynamic_object_sizes_execute_one (&i, call))
+		continue;
+	      else
 		{
-		  unsigned HOST_WIDE_INT object_size_type = tree_to_uhwi (ost);
-		  tree ptr = gimple_call_arg (call, 0);
-		  if ((object_size_type & OST_SUBOBJECT)
-		      && (TREE_CODE (ptr) == ADDR_EXPR
-			  || TREE_CODE (ptr) == SSA_NAME))
+		  /* If we could not find a suitable size expression, lower to
+		     __builtin_object_size so that we may at least get a
+		     constant lower or higher estimate.  */
+		  tree bosfn = builtin_decl_implicit (BUILT_IN_OBJECT_SIZE);
+		  gimple_call_set_fndecl (call, bosfn);
+		  update_stmt (call);
+
+		  if (dump_file && (dump_flags & TDF_DETAILS))
 		    {
-		      tree type = TREE_TYPE (lhs);
-		      tree bytes;
-		      if (compute_builtin_object_size (ptr, object_size_type,
-						       &bytes)
-			  && int_fits_type_p (bytes, type))
-			{
-			  tree tem = make_ssa_name (type);
-			  gimple_call_set_lhs (call, tem);
-			  enum tree_code code
-			    = (object_size_type & OST_MINIMUM
-			       ? MAX_EXPR : MIN_EXPR);
-			  tree cst = fold_convert (type, bytes);
-			  gimple *g
-			    = gimple_build_assign (lhs, code, tem, cst);
-			  gsi_insert_after (&i, g, GSI_NEW_STMT);
-			  update_stmt (call);
-			}
+		      print_generic_expr (dump_file, gimple_call_arg (call, 0),
+					  dump_flags);
+		      fprintf (dump_file,
+			       ": Retrying as __builtin_object_size\n");
 		    }
 		}
-	      continue;
 	    }
 
 	  result = gimple_fold_stmt_to_constant (call, do_valueize);
diff --git a/gcc/tree-object-size.h b/gcc/tree-object-size.h
index b2d6a58324c..f4f438c10a8 100644
--- a/gcc/tree-object-size.h
+++ b/gcc/tree-object-size.h
@@ -20,6 +20,16 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_TREE_OBJECT_SIZE_H
 #define GCC_TREE_OBJECT_SIZE_H
 
+/* Bits in object_size_type.  */
+
+enum
+{
+  OST_SUBOBJECT = 1,
+  OST_MINIMUM = 2,
+  OST_DYNAMIC = 4,
+  OST_END = 8,
+};
+
 extern void init_object_sizes (void);
 extern void fini_object_sizes (void);
 extern bool compute_builtin_object_size (tree, int, tree *);
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v4 3/6] tree-object-size: Support dynamic sizes in conditions
  2021-12-01 14:27 ` [PATCH v4 0/6] __builtin_dynamic_object_size Siddhesh Poyarekar
  2021-12-01 14:27   ` [PATCH v4 1/6] tree-object-size: Use trees and support negative offsets Siddhesh Poyarekar
  2021-12-01 14:27   ` [PATCH v4 2/6] __builtin_dynamic_object_size: Recognize builtin Siddhesh Poyarekar
@ 2021-12-01 14:27   ` Siddhesh Poyarekar
  2021-12-15 16:24     ` Jakub Jelinek
  2021-12-01 14:27   ` [PATCH v4 4/6] tree-object-size: Handle function parameters Siddhesh Poyarekar
                     ` (2 subsequent siblings)
  5 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-12-01 14:27 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Handle GIMPLE_PHI and conditionals specially for dynamic objects,
returning PHI/conditional expressions instead of just a MIN/MAX
estimate.

This makes the returned object size variable for loops and conditionals,
so tests need to be adjusted to look for precise size in some cases.
builtin-dynamic-object-size-5.c had to be modified to only look for
success in maximum object size case and skip over the minimum object
size tests because the result is no longer a compile time constant.

I also added some simple tests to exercise conditionals with dynamic
object sizes.

gcc/ChangeLog:

	* builtins.c (fold_builtin_object_size): Adjust for dynamic size
	expressions.
	* tree-object-size.c: Include gimplify-me.h.
	(struct object_size_info): New member UNKNOWNS.
	(size_initval_p, object_sizes_get_raw): New functions.
	(object_sizes_get): Return suitable gimple variable for
	object size.
	(bundle_sizes): New function.
	(object_sizes_set): Use it and handle dynamic object size
	expressions.
	(object_sizes_set_temp): New function.
	(size_for_offset): Adjust for dynamic size expressions.
	(emit_phi_nodes, propagate_unknowns, gimplify_size_expressions):
	New functions.
	(compute_builtin_object_size): Call gimplify_size_expressions
	for OST_DYNAMIC.
	(dynamic_object_size): New function.
	(cond_expr_object_size): Use it.
	(phi_dynamic_object_size): New function.
	(collect_object_sizes_for): Call it for OST_DYNAMIC.  Adjust to
	accommodate dynamic object sizes.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-dynamic-object-size-0.c: New tests.
	* gcc.dg/builtin-dynamic-object-size-10.c: Add comment.
	* gcc.dg/builtin-object-size-1.c [__builtin_object_size]: Expect
	exact size expressions for __builtin_dynamic_object_size.
	* gcc.dg/builtin-object-size-2.c [__builtin_object_size]:
	Likewise.
	* gcc.dg/builtin-object-size-3.c [__builtin_object_size]:
	Likewise.
	* gcc.dg/builtin-object-size-4.c [__builtin_object_size]:
	Likewise.
	* gcc.dg/builtin-object-size-5.c [__builtin_object_size]:
	Likewise.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 gcc/builtins.c                                |   6 +-
 .../gcc.dg/builtin-dynamic-object-size-0.c    |  72 +++
 .../gcc.dg/builtin-dynamic-object-size-10.c   |   2 +
 gcc/testsuite/gcc.dg/builtin-object-size-1.c  | 119 ++++-
 gcc/testsuite/gcc.dg/builtin-object-size-2.c  |  92 ++++
 gcc/testsuite/gcc.dg/builtin-object-size-3.c  | 121 +++++
 gcc/testsuite/gcc.dg/builtin-object-size-4.c  |  78 +++
 gcc/testsuite/gcc.dg/builtin-object-size-5.c  |  12 +
 gcc/tree-object-size.c                        | 489 +++++++++++++++++-
 9 files changed, 962 insertions(+), 29 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 3b815a6e42a..52b62f32b44 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -10284,7 +10284,8 @@ fold_builtin_object_size (tree ptr, tree ost, enum built_in_function fcode)
   if (TREE_CODE (ptr) == ADDR_EXPR)
     {
       compute_builtin_object_size (ptr, object_size_type, &bytes);
-      if (int_fits_type_p (bytes, size_type_node))
+      if ((object_size_type & OST_DYNAMIC)
+	  || int_fits_type_p (bytes, size_type_node))
 	return fold_convert (size_type_node, bytes);
     }
   else if (TREE_CODE (ptr) == SSA_NAME)
@@ -10293,7 +10294,8 @@ fold_builtin_object_size (tree ptr, tree ost, enum built_in_function fcode)
        later.  Maybe subsequent passes will help determining
        it.  */
       if (compute_builtin_object_size (ptr, object_size_type, &bytes)
-	  && int_fits_type_p (bytes, size_type_node))
+	  && ((object_size_type & OST_DYNAMIC)
+	      || int_fits_type_p (bytes, size_type_node)))
 	return fold_convert (size_type_node, bytes);
     }
 
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
new file mode 100644
index 00000000000..ddedf6a49bd
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -0,0 +1,72 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+typedef __SIZE_TYPE__ size_t;
+#define abort __builtin_abort
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi (int cond)
+{
+  void *ret;
+ 
+  if (cond)
+    ret = __builtin_malloc (32);
+  else
+    ret = __builtin_malloc (64);
+
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_calloc_condphi (size_t cnt, size_t sz, int cond)
+{
+  struct
+    {
+      int a;
+      char b;
+    } bin[cnt];
+
+  char *ch = __builtin_calloc (cnt, sz);
+
+  return __builtin_dynamic_object_size (cond ? ch : (void *) &bin, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_deploop (size_t sz, size_t cond)
+{
+  char *bin = __builtin_alloca (32);
+
+  for (size_t i = 0; i < sz; i++)
+    if (i == cond)
+      bin = __builtin_alloca (64);
+
+  return __builtin_dynamic_object_size (bin, 0);
+}
+
+unsigned nfails = 0;
+
+#define FAIL() ({ \
+  __builtin_printf ("Failure at line: %d\n", __LINE__);			      \
+  nfails++;								      \
+})
+
+int
+main (int argc, char **argv)
+{
+  if (test_builtin_malloc_condphi (1) != 32)
+    FAIL ();
+  if (test_builtin_malloc_condphi (0) != 64)
+    FAIL ();
+  if (test_builtin_calloc_condphi (128, 1, 0) == 128)
+    FAIL ();
+  if (test_deploop (128, 129) != 32)
+    FAIL ();
+
+  if (nfails > 0)
+    __builtin_abort ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
index bc880a589ae..3a2d9821a44 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
@@ -5,5 +5,7 @@
 #define __builtin_object_size __builtin_dynamic_object_size
 #include "builtin-object-size-10.c"
 
+/* early_objsz should resolve __builtin_dynamic_object_size like
+   __builtin_object_size.  */
 /* { dg-final { scan-tree-dump "maximum object size 21" "early_objsz" } } */
 /* { dg-final { scan-tree-dump "maximum subobject size 16" "early_objsz" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
index 0154f4e9695..265c87ed6fb 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
@@ -42,9 +42,17 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (q, 0) != (size_t) -1)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0)
+      != (x < 0
+	  ? sizeof (a) - __builtin_offsetof (struct A, a) - 9
+	  : sizeof (a) - __builtin_offsetof (struct A, c) - 1))
+    abort ();
+#else
   if (__builtin_object_size (r, 0)
       != sizeof (a) - __builtin_offsetof (struct A, a) - 9)
     abort ();
+#endif
   if (x < 6)
     r = &w[2].a[1];
   else
@@ -58,9 +66,17 @@ test1 (void *q, int x)
   if (__builtin_object_size (&y.b, 0)
       != sizeof (a) - __builtin_offsetof (struct A, b))
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0)
+      != (x < 6
+	  ? 2 * sizeof (w[0]) - __builtin_offsetof (struct A, a) - 1
+	  : sizeof (a) - __builtin_offsetof (struct A, a) - 6))
+    abort ();
+#else
   if (__builtin_object_size (r, 0)
       != 2 * sizeof (w[0]) - __builtin_offsetof (struct A, a) - 1)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
@@ -165,6 +181,7 @@ test2 (void)
   struct B { char buf1[10]; char buf2[10]; } a;
   char *r, buf3[20];
   int i;
+  size_t res;
 
   if (sizeof (a) != 20)
     return;
@@ -181,7 +198,24 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[9];
     }
-  if (__builtin_object_size (r, 0) != 20)
+#ifdef __builtin_object_size
+  res = sizeof (buf3);
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 1;
+      else if (i == l1)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 7;
+      else if (i == l1 + 1)
+        res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
+    }
+#else
+  res = 20;
+#endif
+  if (__builtin_object_size (r, 0) != res)
     abort ();
   r = &buf3[20];
   for (i = 0; i < 4; ++i)
@@ -195,13 +229,45 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[9];
     }
-  if (__builtin_object_size (r, 0) != 15)
+#ifdef __builtin_object_size
+  res = sizeof (buf3) - 20;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 7;
+      else if (i == l1)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 7;
+      else if (i == l1 + 1)
+        res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
+    }
+  if (__builtin_object_size (r, 0) != res)
+    abort ();
+#else
+  res = 15;
+#endif
+  if (__builtin_object_size (r, 0) != res)
     abort ();
   r += 8;
+#ifdef __builtin_object_size
+  res -= 8;
+  if (__builtin_object_size (r, 0) != res)
+    abort ();
+  if (res >= 6)
+    {
+      if (__builtin_object_size (r + 6, 0) != res - 6)
+        abort ();
+    }
+  else if (__builtin_object_size (r + 6, 0) != 0)
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != 7)
     abort ();
   if (__builtin_object_size (r + 6, 0) != 1)
     abort ();
+#endif
   r = &buf3[18];
   for (i = 0; i < 4; ++i)
     {
@@ -214,8 +280,31 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[4];
     }
+#ifdef __builtin_object_size
+  res = sizeof (buf3) - 18;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+          res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
+      else if (i == l1)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 9;
+      else if (i == l1 + 1)
+        res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 4;
+    }
+  if (res >= 12)
+    {
+      if (__builtin_object_size (r + 12, 0) != res - 12)
+        abort ();
+    }
+  else if (__builtin_object_size (r + 12, 0) != 0)
+    abort ();
+#else
   if (__builtin_object_size (r + 12, 0) != 4)
     abort ();
+#endif
 }
 
 void
@@ -358,6 +447,10 @@ test5 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 0) != sizeof (buf) - 8 - 4 * x)
+    abort ();
+#else
   /* My understanding of ISO C99 6.5.6 is that a conforming
      program will not end up with p equal to &buf[0]
      through &buf[7], i.e. calling this function with say
@@ -367,6 +460,7 @@ test5 (size_t x)
      it would be 64 (or conservative (size_t) -1 == unknown).  */
   if (__builtin_object_size (p, 0) != sizeof (buf) - 8)
     abort ();
+#endif
   memset (p, ' ', sizeof (buf) - 8 - 4 * 4);
 }
 
@@ -380,8 +474,13 @@ test6 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 0) != sizeof (t) - 8 - 4 * x)
+    abort ();
+#else
   if (__builtin_object_size (p, 0) != sizeof (t) - 8)
     abort ();
+#endif
   memset (p, ' ', sizeof (t) - 8 - 4 * 4);
 }
 
@@ -436,21 +535,37 @@ test9 (unsigned cond)
   else
     p = &buf2[4];
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (&p[-4], 0) != (cond ? 6 : 10))
+    abort ();
+#else
   if (__builtin_object_size (&p[-4], 0) != 10)
     abort ();
+#endif
 
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 0) != ((cond ? 2 : 6) + cond))
+    abort ();
+#else
   if (__builtin_object_size (p, 0) != 10)
     abort ();
+#endif
 
   p = &y.c[8];
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 0)
+      != sizeof (y) - __builtin_offsetof (struct A, c) - 8 + cond)
+    abort ();
+#else
   if (__builtin_object_size (p, 0) != sizeof (y))
     abort ();
+#endif
 }
 
 int
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
index 5cf29291aff..5051fea47c3 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-2.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
@@ -43,8 +43,15 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (q, 1) != (size_t) -1)
     abort ();
+#ifdef __builtin_object_size
+  if (x < 0
+      ? __builtin_object_size (r, 1) != sizeof (a.a) - 9
+      : __builtin_object_size (r, 1) != sizeof (a.c) - 1)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a.c) - 1)
     abort ();
+#endif
   if (x < 6)
     r = &w[2].a[1];
   else
@@ -55,8 +62,15 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&y.b, 1) != sizeof (a.b))
     abort ();
+#ifdef __builtin_object_size
+  if (x < 6
+      ? __builtin_object_size (r, 1) != sizeof (a.a) - 1
+      : __builtin_object_size (r, 1) != sizeof (a.a) - 6)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a.a) - 1)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
@@ -185,6 +199,9 @@ test2 (void)
   struct B { char buf1[10]; char buf2[10]; } a;
   char *r, buf3[20];
   int i;
+#ifdef __builtin_object_size
+  size_t dyn_res;
+#endif
 
   if (sizeof (a) != 20)
     return;
@@ -201,8 +218,26 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[9];
     }
+#ifdef __builtin_object_size
+  dyn_res = sizeof (buf3);
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	dyn_res = sizeof (a.buf1) - 1;
+      else if (i == l1)
+	dyn_res = sizeof (a.buf2) - 7;
+      else if (i == l1 + 1)
+	dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+	dyn_res = sizeof (a.buf1) - 9;
+    }
+  if (__builtin_object_size (r, 1) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (buf3))
     abort ();
+#endif
   r = &buf3[20];
   for (i = 0; i < 4; ++i)
     {
@@ -215,13 +250,50 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[9];
     }
+#ifdef __builtin_object_size
+  dyn_res = sizeof (buf3) - 20;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+        dyn_res = sizeof (a.buf1) - 7;
+      else if (i == l1)
+        dyn_res = sizeof (a.buf2) - 7;
+      else if (i == l1 + 1)
+        dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+        dyn_res = sizeof (a.buf1) - 9;
+    }
+  if (__builtin_object_size (r, 1) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (buf3) - 5)
     abort ();
+#endif
   r += 8;
+#ifdef __builtin_object_size
+  if (dyn_res >= 8)
+    {
+      dyn_res -= 8;
+      if (__builtin_object_size (r, 1) != dyn_res)
+	abort ();
+
+      if (dyn_res >= 6)
+	{
+	  if (__builtin_object_size (r + 6, 1) != dyn_res - 6)
+	    abort ();
+	}
+      else if (__builtin_object_size (r + 6, 1) != 0)
+	abort ();
+    }
+  else if (__builtin_object_size (r, 1) != 0)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (buf3) - 13)
     abort ();
   if (__builtin_object_size (r + 6, 1) != sizeof (buf3) - 19)
     abort ();
+#endif
 }
 
 void
@@ -339,8 +411,13 @@ test5 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 1) != sizeof (t.buf) - 8 - 4 * x)
+    abort ();
+#else
   if (__builtin_object_size (p, 1) != sizeof (t.buf) - 8)
     abort ();
+#endif
   memset (p, ' ', sizeof (t.buf) - 8 - 4 * 4);
 }
 
@@ -394,21 +471,36 @@ test8 (unsigned cond)
   else
     p = &buf2[4];
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (&p[-4], 1) != (cond ? 6 : 10))
+    abort ();
+#else
   if (__builtin_object_size (&p[-4], 1) != 10)
     abort ();
+#endif
 
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 1) != ((cond ? 2 : 6) + cond))
+    abort ();
+#else
   if (__builtin_object_size (p, 1) != 10)
     abort ();
+#endif
 
   p = &y.c[8];
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 1) != sizeof (y.c) - 8 + cond)
+    abort ();
+#else
   if (__builtin_object_size (p, 1) != sizeof (y.c))
     abort ();
+#endif
 }
 
 int
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
index 3a692c4e3d2..1d92627266b 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-3.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
@@ -71,23 +71,45 @@ test1 (void *q, int x)
     r = malloc (30);
   else
     r = calloc (2, 14);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 2) != (x < 20 ? 30 : 2 * 14))
+    abort ();
+#else
   if (__builtin_object_size (r, 2) != 2 * 14)
     abort ();
+#endif
   if (x < 30)
     r = malloc (sizeof (a));
   else
     r = &a.a[3];
+#ifdef __builtin_object_size
+  size_t objsz = (x < 30 ? sizeof (a)
+                  : sizeof (a) - __builtin_offsetof (struct A, a) - 3);
+  if (__builtin_object_size (r, 2) != objsz)
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, a) - 3)
     abort ();
+#endif
   r = memcpy (r, "a", 2);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 2) != objsz)
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, a) - 3)
     abort ();
+#endif
   r = memcpy (r + 2, "b", 2) + 2;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 2) != objsz - 4)
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, a) - 3 - 4)
     abort ();
+#endif
   r = &a.a[4];
   r = memset (r, 'a', 2);
   if (__builtin_object_size (r, 2)
@@ -164,6 +186,9 @@ test2 (void)
   struct B { char buf1[10]; char buf2[10]; } a;
   char *r, buf3[20];
   int i;
+#ifdef __builtin_object_size
+  size_t dyn_res;
+#endif
 
   if (sizeof (a) != 20)
     return;
@@ -180,8 +205,26 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[9];
     }
+#ifdef __builtin_object_size
+  dyn_res = sizeof (buf3);
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 1;
+      else if (i == l1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 7;
+      else if (i == l1 + 1)
+	dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
+    }
+  if (__builtin_object_size (r, 2) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 2) != 3)
     abort ();
+#endif
   r = &buf3[20];
   for (i = 0; i < 4; ++i)
     {
@@ -208,13 +251,44 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[4];
     }
+#ifdef __builtin_object_size
+  dyn_res = sizeof (buf3) - 2;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 1;
+      else if (i == l1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 2;
+      else if (i == l1 + 1)
+	dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 4;
+    }
+  if (__builtin_object_size (r, 2) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 2) != 15)
     abort ();
+#endif
   r += 8;
+#ifdef __builtin_object_size
+  dyn_res -= 8;
+  if (__builtin_object_size (r, 2) != dyn_res)
+    abort ();
+  if (dyn_res >= 6)
+    {
+      if (__builtin_object_size (r + 6, 2) != dyn_res - 6)
+	abort ();
+    }
+  else if (__builtin_object_size (r + 6, 2) != 0)
+    abort ();
+#else
   if (__builtin_object_size (r, 2) != 7)
     abort ();
   if (__builtin_object_size (r + 6, 2) != 1)
     abort ();
+#endif
   r = &buf3[18];
   for (i = 0; i < 4; ++i)
     {
@@ -227,8 +301,31 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[4];
     }
+#ifdef __builtin_object_size
+  dyn_res = sizeof (buf3) - 18;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
+      else if (i == l1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 9;
+      else if (i == l1 + 1)
+	dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 4;
+    }
+  if (dyn_res >= 12)
+    {
+      if (__builtin_object_size (r + 12, 2) != dyn_res - 12)
+	abort ();
+    }
+  else if (__builtin_object_size (r + 12, 2) != 0)
+    abort ();
+#else
   if (__builtin_object_size (r + 12, 2) != 0)
     abort ();
+#endif
 }
 
 void
@@ -371,7 +468,11 @@ test5 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 2) != sizeof (buf) - 8 - 4 * x)
+#else
   if (__builtin_object_size (p, 2) != 0)
+#endif
     abort ();
   memset (p, ' ', sizeof (buf) - 8 - 4 * 4);
 }
@@ -386,7 +487,11 @@ test6 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 2) != sizeof (t) - 8 - 4 * x)
+#else
   if (__builtin_object_size (p, 2) != 0)
+#endif
     abort ();
   memset (p, ' ', sizeof (t) - 8 - 4 * 4);
 }
@@ -442,22 +547,38 @@ test9 (unsigned cond)
   else
     p = &buf2[4];
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (&p[-4], 2) != (cond ? 6 : 10))
+    abort ();
+#else
   if (__builtin_object_size (&p[-4], 2) != 6)
     abort ();
+#endif
 
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 2) != ((cond ? 2 : 6) + cond))
+    abort ();
+#else
   if (__builtin_object_size (p, 2) != 2)
     abort ();
+#endif
 
   p = &y.c[8];
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 2)
+      != sizeof (y) - __builtin_offsetof (struct A, c) - 8 + cond)
+    abort ();
+#else
   if (__builtin_object_size (p, 2)
       != sizeof (y) - __builtin_offsetof (struct A, c) - 8)
     abort ();
+#endif
 }
 
 int
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
index 87381620cc9..9da3537a5f7 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-4.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
@@ -43,7 +43,12 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (q, 3) != 0)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3)
+      != (x < 0 ? sizeof (a.a) - 9 : sizeof (a.c) - 1))
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.a) - 9)
+#endif
     abort ();
   if (x < 6)
     r = &w[2].a[1];
@@ -55,31 +60,57 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&y.b, 3) != sizeof (a.b))
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3)
+      != (x < 6 ? sizeof (w[2].a) - 1 : sizeof (a.a) - 6))
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.a) - 6)
+#endif
     abort ();
   if (x < 20)
     r = malloc (30);
   else
     r = calloc (2, 16);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3) != (x < 20 ? 30 : 2 * 16))
+#else
   if (__builtin_object_size (r, 3) != 30)
+#endif
     abort ();
   if (x < 20)
     r = malloc (30);
   else
     r = calloc (2, 14);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3) != (x < 20 ? 30 : 2 * 14))
+#else
   if (__builtin_object_size (r, 3) != 2 * 14)
+#endif
     abort ();
   if (x < 30)
     r = malloc (sizeof (a));
   else
     r = &a.a[3];
+#ifdef __builtin_object_size
+  size_t objsz = x < 30 ? sizeof (a) : sizeof (a.a) - 3;
+  if (__builtin_object_size (r, 3) != objsz)
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.a) - 3)
+#endif
     abort ();
   r = memcpy (r, "a", 2);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3) != objsz)
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.a) - 3)
+#endif
     abort ();
   r = memcpy (r + 2, "b", 2) + 2;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3) != objsz - 4)
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.a) - 3 - 4)
+#endif
     abort ();
   r = &a.a[4];
   r = memset (r, 'a', 2);
@@ -184,6 +215,9 @@ test2 (void)
   struct B { char buf1[10]; char buf2[10]; } a;
   char *r, buf3[20];
   int i;
+#ifdef __builtin_object_size
+  size_t dyn_res = 0;
+#endif
 
   if (sizeof (a) != 20)
     return;
@@ -228,13 +262,38 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[2];
     }
+#ifdef __builtin_object_size
+  dyn_res = sizeof (buf3) - 1;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+        dyn_res = sizeof (a.buf1) - 6;
+      else if (i == l1)
+        dyn_res = sizeof (a.buf2) - 4;
+      else if (i == l1 + 1)
+        dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+        dyn_res = sizeof (a.buf1) - 2;
+    }
+  if (__builtin_object_size (r, 3) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.buf1) - 6)
     abort ();
+#endif
   r += 2;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3) != dyn_res - 2)
+    abort ();
+  if (__builtin_object_size (r + 1, 3) != dyn_res - 3)
+    abort ();
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.buf1) - 6 - 2)
     abort ();
   if (__builtin_object_size (r + 1, 3) != sizeof (a.buf1) - 6 - 3)
     abort ();
+#endif
 }
 
 void
@@ -352,7 +411,11 @@ test5 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 3) != sizeof (t.buf) - 8 - 4 * x)
+#else
   if (__builtin_object_size (p, 3) != 0)
+#endif
     abort ();
   memset (p, ' ', sizeof (t.buf) - 8 - 4 * 4);
 }
@@ -407,21 +470,36 @@ test8 (unsigned cond)
   else
     p = &buf2[4];
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (&p[-4], 3) != (cond ? 6 : 10))
+    abort ();
+#else
   if (__builtin_object_size (&p[-4], 3) != 6)
     abort ();
+#endif
 
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 3) != ((cond ? 2 : 6) + cond))
+    abort ();
+#else
   if (__builtin_object_size (p, 3) != 2)
     abort ();
+#endif
 
   p = &y.c[8];
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 3) != sizeof (y.c) - 8 + cond)
+    abort ();
+#else
   if (__builtin_object_size (p, 3) != sizeof (y.c) - 8)
     abort ();
+#endif
 }
 
 int
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-5.c b/gcc/testsuite/gcc.dg/builtin-object-size-5.c
index 8e63d9c7a5e..904e616949d 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-5.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-5.c
@@ -1,5 +1,7 @@
 /* { dg-do compile { target i?86-*-linux* i?86-*-gnu* x86_64-*-linux* } } */
 /* { dg-options "-O2" } */
+/* For dynamic object sizes we 'succeed' if the returned size is known for
+   maximum object size.  */
 
 typedef __SIZE_TYPE__ size_t;
 extern void abort (void);
@@ -13,7 +15,11 @@ test1 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 0) == -1)
+#else
   if (__builtin_object_size (p, 0) != sizeof (buf) - 8)
+#endif
     abort ();
 }
 
@@ -25,10 +31,15 @@ test2 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 1) == -1)
+#else
   if (__builtin_object_size (p, 1) != sizeof (buf) - 8)
+#endif
     abort ();
 }
 
+#ifndef __builtin_object_size
 void
 test3 (size_t x)
 {
@@ -52,6 +63,7 @@ test4 (size_t x)
   if (__builtin_object_size (p, 3) != 0)
     abort ();
 }
+#endif
 
 void
 test5 (void)
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index c4f3fbeb8f5..dd6c90efebf 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -35,13 +35,14 @@ along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "attribs.h"
 #include "builtins.h"
+#include "gimplify-me.h"
 
 struct object_size_info
 {
   int object_size_type;
   unsigned char pass;
   bool changed;
-  bitmap visited, reexamine;
+  bitmap visited, reexamine, unknowns;
   unsigned int *depths;
   unsigned int *stack, *tos;
 };
@@ -74,7 +75,11 @@ static void check_for_plus_in_loops_1 (struct object_size_info *, tree,
    object_sizes[1] is upper bound for the object size and number of bytes till
    the end of the subobject (innermost array or field with address taken).
    object_sizes[2] is lower bound for the object size and number of bytes till
-   the end of the object and object_sizes[3] lower bound for subobject.  */
+   the end of the object and object_sizes[3] lower bound for subobject.
+
+   For static object sizes, the object size and the bytes till the end of the
+   object are both INTEGER_CST.  In the dynamic case, they are finally either a
+   gimple variable or an INTEGER_CST.  */
 static vec<object_size> object_sizes[OST_END];
 
 /* Bitmaps what object sizes have been computed already.  */
@@ -100,6 +105,15 @@ unknown (int object_size_type)
   return ~initval (object_size_type);
 }
 
+/* Return true if VAL is represents an initial size for OBJECT_SIZE_TYPE.  */
+
+static inline bool
+size_initval_p (tree val, int object_size_type)
+{
+  return (tree_fits_uhwi_p (val)
+	  && tree_to_uhwi (val) == initval (object_size_type));
+}
+
 /* Return true if VAL is represents an unknown size for OBJECT_SIZE_TYPE.  */
 
 static inline bool
@@ -151,17 +165,45 @@ object_sizes_unknown_p (int object_size_type, unsigned varno)
 			 object_size_type);
 }
 
-/* Return size for VARNO corresponding to OSI.  If WHOLE is true, return the
-   whole object size.  */
+/* Return the raw size expression for VARNO corresponding to OSI.  This returns
+   the TREE_VEC as is and should only be used during gimplification.  */
+
+static inline object_size
+object_sizes_get_raw (struct object_size_info *osi, unsigned varno)
+{
+  gcc_assert (osi->pass != 0);
+  return object_sizes[osi->object_size_type][varno];
+}
+
+/* Return a size tree for VARNO corresponding to OSI.  If WHOLE is true, return
+   the whole object size.  Use this for building size expressions based on size
+   of VARNO.  */
 
 static inline tree
 object_sizes_get (struct object_size_info *osi, unsigned varno,
 		  bool whole = false)
 {
+  tree ret;
+
   if (whole)
-    return object_sizes[osi->object_size_type][varno].wholesize;
+    ret = object_sizes[osi->object_size_type][varno].wholesize;
   else
-    return object_sizes[osi->object_size_type][varno].size;
+    ret = object_sizes[osi->object_size_type][varno].size;
+
+  int object_size_type = osi->object_size_type;
+
+  if (object_size_type & OST_DYNAMIC)
+    {
+      if (TREE_CODE (ret) == MODIFY_EXPR)
+	return TREE_OPERAND (ret, 0);
+      else if (TREE_CODE (ret) == TREE_VEC)
+	return TREE_VEC_ELT (ret, TREE_VEC_LENGTH (ret) - 1);
+      else
+	gcc_checking_assert (is_gimple_variable (ret)
+			     || TREE_CODE (ret) == INTEGER_CST);
+    }
+
+  return ret;
 }
 
 /* Set size for VARNO corresponding to OSI to VAL.  */
@@ -176,27 +218,113 @@ object_sizes_initialize (struct object_size_info *osi, unsigned varno,
   object_sizes[object_size_type][varno].wholesize = wholeval;
 }
 
+/* Return a MODIFY_EXPR for cases where SSA and EXPR have the same type.  The
+   TREE_VEC is returned only in case of PHI nodes.  */
+
+static tree
+bundle_sizes (tree ssa, tree expr)
+{
+  gcc_checking_assert (TREE_TYPE (ssa) == sizetype);
+
+  if (!TREE_TYPE (expr))
+    {
+      gcc_checking_assert (TREE_CODE (expr) == TREE_VEC);
+      TREE_VEC_ELT (expr, TREE_VEC_LENGTH (expr) - 1) = ssa;
+      return expr;
+    }
+
+  gcc_checking_assert (types_compatible_p (TREE_TYPE (expr), sizetype));
+  return size_binop (MODIFY_EXPR, ssa, expr);
+}
+
 /* Set size for VARNO corresponding to OSI to VAL if it is the new minimum or
-   maximum.  */
+   maximum.  For static sizes, each element of TREE_VEC is always INTEGER_CST
+   throughout the computation.  For dynamic sizes, each element may either be a
+   gimple variable, a MODIFY_EXPR or a TREE_VEC.  The MODIFY_EXPR is for
+   expressions that need to be gimplified.  TREE_VECs are special, they're
+   emitted only for GIMPLE_PHI and the PHI result variable is the last element
+   of the vector.  */
 
-static inline bool
+static bool
 object_sizes_set (struct object_size_info *osi, unsigned varno, tree val,
 		  tree wholeval)
 {
   int object_size_type = osi->object_size_type;
   object_size osize = object_sizes[object_size_type][varno];
+  bool changed = true;
 
   tree oldval = osize.size;
   tree old_wholeval = osize.wholesize;
 
-  enum tree_code code = object_size_type & OST_MINIMUM ? MIN_EXPR : MAX_EXPR;
+  if (object_size_type & OST_DYNAMIC)
+    {
+      if (bitmap_bit_p (osi->reexamine, varno))
+	{
+	  if (size_unknown_p (val, object_size_type))
+	    {
+	      oldval = object_sizes_get (osi, varno);
+	      old_wholeval = object_sizes_get (osi, varno, true);
+	      bitmap_set_bit (osi->unknowns, SSA_NAME_VERSION (oldval));
+	      bitmap_set_bit (osi->unknowns, SSA_NAME_VERSION (old_wholeval));
+	      bitmap_clear_bit (osi->reexamine, varno);
+	    }
+	  else
+	    {
+	      val = bundle_sizes (oldval, val);
+	      wholeval = bundle_sizes (old_wholeval, wholeval);
+	    }
+	}
+      else
+	{
+	  gcc_checking_assert (size_initval_p (oldval, object_size_type));
+	  gcc_checking_assert (size_initval_p (old_wholeval,
+					       object_size_type));
+	  /* For dynamic object sizes, all object sizes that are not gimple
+	     variables will need to be gimplified.  */
+	  if (TREE_CODE (wholeval) != INTEGER_CST
+	      && !is_gimple_variable (wholeval))
+	    {
+	      bitmap_set_bit (osi->reexamine, varno);
+	      wholeval = bundle_sizes (make_ssa_name (sizetype), wholeval);
+	    }
+	  if (TREE_CODE (val) != INTEGER_CST && !is_gimple_variable (val))
+	    {
+	      bitmap_set_bit (osi->reexamine, varno);
+	      val = bundle_sizes (make_ssa_name (sizetype), val);
+	    }
+	  /* If the new value is a temporary variable, mark it for
+	     reexamination.  */
+	  else if (TREE_CODE (val) == SSA_NAME && !SSA_NAME_DEF_STMT (val))
+	    bitmap_set_bit (osi->reexamine, varno);
+	}
+    }
+  else
+    {
+      enum tree_code code = (object_size_type & OST_MINIMUM
+			     ? MIN_EXPR : MAX_EXPR);
 
-  val = size_binop (code, val, oldval);
-  wholeval = size_binop (code, wholeval, old_wholeval);
+      val = size_binop (code, val, oldval);
+      wholeval = size_binop (code, wholeval, old_wholeval);
+      changed = tree_int_cst_compare (val, oldval) != 0;
+    }
 
   object_sizes[object_size_type][varno].size = val;
   object_sizes[object_size_type][varno].wholesize = wholeval;
-  return tree_int_cst_compare (oldval, val) != 0;
+  return changed;
+}
+
+/* Set temporary SSA names for object size and whole size to resolve dependency
+   loops in dynamic size computation.  */
+
+static inline void
+object_sizes_set_temp (struct object_size_info *osi, unsigned varno)
+{
+  tree val = object_sizes_get (osi, varno);
+
+  if (size_initval_p (val, osi->object_size_type))
+    object_sizes_set (osi, varno,
+		      make_ssa_name (sizetype),
+		      make_ssa_name (sizetype));
 }
 
 /* Initialize OFFSET_LIMIT variable.  */
@@ -218,14 +346,15 @@ static tree
 size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE)
 {
   gcc_checking_assert (TREE_CODE (offset) == INTEGER_CST);
-  gcc_checking_assert (TREE_CODE (sz) == INTEGER_CST);
   gcc_checking_assert (types_compatible_p (TREE_TYPE (sz), sizetype));
 
   /* For negative offsets, if we have a distinct WHOLESIZE, use it to get a net
      offset from the whole object.  */
-  if (wholesize && tree_int_cst_compare (sz, wholesize))
+  if (wholesize
+      && (TREE_CODE (sz) != INTEGER_CST
+	  || TREE_CODE (wholesize) != INTEGER_CST
+	  || tree_int_cst_compare (sz, wholesize)))
     {
-      gcc_checking_assert (TREE_CODE (wholesize) == INTEGER_CST);
       gcc_checking_assert (types_compatible_p (TREE_TYPE (wholesize),
 					       sizetype));
 
@@ -242,13 +371,16 @@ size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE)
   if (!types_compatible_p (TREE_TYPE (offset), sizetype))
     fold_convert (sizetype, offset);
 
-  if (integer_zerop (offset))
-    return sz;
+  if (TREE_CODE (offset) == INTEGER_CST)
+    {
+      if (integer_zerop (offset))
+	return sz;
 
-  /* Negative or too large offset even after adjustment, cannot be within
-     bounds of an object.  */
-  if (compare_tree_int (offset, offset_limit) > 0)
-    return size_zero_node;
+      /* Negative or too large offset even after adjustment, cannot be within
+	 bounds of an object.  */
+      if (compare_tree_int (offset, offset_limit) > 0)
+	return size_zero_node;
+    }
 
   return size_binop (MINUS_EXPR, size_binop (MAX_EXPR, sz, offset), offset);
 }
@@ -685,6 +817,205 @@ pass_through_call (const gcall *call)
   return NULL_TREE;
 }
 
+/* Emit PHI nodes for size expressions fo.  */
+
+static void
+emit_phi_nodes (gimple *stmt, tree size, tree wholesize)
+{
+  tree phires = TREE_VEC_ELT (wholesize, TREE_VEC_LENGTH (wholesize) - 1);
+  gphi *wholephi = create_phi_node (phires, gimple_bb (stmt));
+  phires = TREE_VEC_ELT (size, TREE_VEC_LENGTH (size) - 1);
+  gphi *phi = create_phi_node (phires, gimple_bb (stmt));
+  gphi *obj_phi =  as_a <gphi *> (stmt);
+
+  gcc_checking_assert (TREE_CODE (wholesize) == TREE_VEC);
+  gcc_checking_assert (TREE_CODE (size) == TREE_VEC);
+
+  for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++)
+    {
+      gimple_seq seq = NULL;
+      tree wsz = TREE_VEC_ELT (wholesize, i);
+      tree sz = TREE_VEC_ELT (size, i);
+
+      /* If we built an expression, we will need to build statements
+	 and insert them on the edge right away.  */
+      if (!is_gimple_variable (wsz))
+	wsz = force_gimple_operand (wsz, &seq, true, NULL);
+      if (!is_gimple_variable (sz))
+	{
+	  gimple_seq s;
+	  sz = force_gimple_operand (sz, &s, true, NULL);
+	  gimple_seq_add_seq (&seq, s);
+	}
+
+      if (seq)
+	{
+	  edge e = gimple_phi_arg_edge (obj_phi, i);
+
+	  /* Put the size definition before the last statement of the source
+	     block of the PHI edge.  This ensures that any branches at the end
+	     of the source block remain the last statement.  We are OK even if
+	     the last statement is the definition of the object since it will
+	     succeed any definitions that contribute to its size and the size
+	     expression will succeed them too.  */
+	  gimple_stmt_iterator gsi = gsi_last_bb (e->src);
+	  gsi_insert_seq_before (&gsi, seq, GSI_CONTINUE_LINKING);
+	}
+
+      add_phi_arg (wholephi, wsz,
+		   gimple_phi_arg_edge (obj_phi, i),
+		   gimple_phi_arg_location (obj_phi, i));
+
+      add_phi_arg (phi, sz,
+		   gimple_phi_arg_edge (obj_phi, i),
+		   gimple_phi_arg_location (obj_phi, i));
+    }
+}
+
+/* Descend through EXPR and return size_unknown if it uses any SSA variable
+   object_size_set or object_size_set_temp generated, which turned out to be
+   size_unknown, as noted in UNKNOWNS.  */
+
+static tree
+propagate_unknowns (object_size_info *osi, tree expr)
+{
+  int object_size_type = osi->object_size_type;
+
+  switch (TREE_CODE (expr))
+    {
+    case SSA_NAME:
+      if (bitmap_bit_p (osi->unknowns, SSA_NAME_VERSION (expr)))
+	return size_unknown (object_size_type);
+      return expr;
+
+    case MIN_EXPR:
+    case MAX_EXPR:
+	{
+	  tree res = propagate_unknowns (osi, TREE_OPERAND (expr, 0));
+	  if (size_unknown_p (res, object_size_type))
+	    return res;
+
+	  res = propagate_unknowns (osi, TREE_OPERAND (expr, 1));
+	  if (size_unknown_p (res, object_size_type))
+	    return res;
+
+	  return expr;
+	}
+    case MODIFY_EXPR:
+	{
+	  tree res = propagate_unknowns (osi, TREE_OPERAND (expr, 1));
+	  if (size_unknown_p (res, object_size_type))
+	    return res;
+	  return expr;
+	}
+    case TREE_VEC:
+      for (int i = 0; i < TREE_VEC_LENGTH (expr); i++)
+	{
+	  tree res = propagate_unknowns (osi, TREE_VEC_ELT (expr, i));
+	  if (size_unknown_p (res, object_size_type))
+	    return res;
+	}
+      return expr;
+    case PLUS_EXPR:
+    case MINUS_EXPR:
+	{
+	  tree res = propagate_unknowns (osi, TREE_OPERAND (expr, 0));
+	  if (size_unknown_p (res, object_size_type))
+	    return res;
+
+	  return expr;
+	}
+    default:
+      return expr;
+    }
+}
+
+/* Walk through size expressions that need reexamination and generate
+   statements for them.  */
+
+static void
+gimplify_size_expressions (object_size_info *osi)
+{
+  int object_size_type = osi->object_size_type;
+  bitmap_iterator bi;
+  unsigned int i;
+  bool changed;
+
+  /* Step 1: Propagate unknowns into expressions.  */
+  bitmap reexamine = BITMAP_ALLOC (NULL);
+  bitmap_copy (reexamine, osi->reexamine);
+  do
+    {
+      changed = false;
+      EXECUTE_IF_SET_IN_BITMAP (reexamine, 0, i, bi)
+	{
+	  object_size cur = object_sizes_get_raw (osi, i);
+
+	  if (size_unknown_p (propagate_unknowns (osi, cur.size),
+			      object_size_type)
+	      || size_unknown_p (propagate_unknowns (osi, cur.wholesize),
+				 object_size_type))
+	    {
+	      object_sizes_set (osi, i,
+				size_unknown (object_size_type),
+				size_unknown (object_size_type));
+	      changed = true;
+	    }
+	}
+      bitmap_copy (reexamine, osi->reexamine);
+    }
+  while (changed);
+
+  /* Release all unknowns.  */
+  EXECUTE_IF_SET_IN_BITMAP (osi->unknowns, 0, i, bi)
+    release_ssa_name (ssa_name (i));
+
+  /* Expand all size expressions to put their definitions close to the objects
+     for whom size is being computed.  */
+  EXECUTE_IF_SET_IN_BITMAP (osi->reexamine, 0, i, bi)
+    {
+      gimple_seq seq = NULL;
+      object_size osize = object_sizes_get_raw (osi, i);
+
+      gimple *stmt = SSA_NAME_DEF_STMT (ssa_name (i));
+      enum gimple_code code = gimple_code (stmt);
+
+      /* PHI nodes need special attention.  */
+      if (code == GIMPLE_PHI)
+	emit_phi_nodes (stmt, osize.size, osize.wholesize);
+      else
+	{
+	  tree size_expr = NULL_TREE;
+
+	  /* Bundle wholesize in with the size to gimplify if needed.  */
+	  if (!is_gimple_variable (osize.wholesize)
+	      && TREE_CODE (osize.wholesize) != INTEGER_CST)
+	    size_expr = size_binop (COMPOUND_EXPR,
+				    osize.wholesize,
+				    osize.size);
+	  else if (!is_gimple_variable (osize.size)
+		   && TREE_CODE (osize.size) != INTEGER_CST)
+	    size_expr = osize.size;
+
+	  if (size_expr)
+	    {
+	      gimple_stmt_iterator gsi;
+	      if (code == GIMPLE_NOP)
+		gsi = gsi_start_bb (single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)));
+	      else
+		gsi = gsi_for_stmt (stmt);
+
+	      force_gimple_operand (size_expr, &seq, true, NULL);
+	      gsi_insert_seq_before (&gsi, seq, GSI_CONTINUE_LINKING);
+	    }
+	}
+
+      /* We're done, so replace the MODIFY_EXPRs with the SSA names.  */
+      object_sizes_initialize (osi, i,
+			       object_sizes_get (osi, i),
+			       object_sizes_get (osi, i, true));
+    }
+}
 
 /* Compute __builtin_object_size value for PTR and set *PSIZE to
    the resulting value.  If the declared object is known and PDECL
@@ -763,9 +1094,15 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 
       osi.visited = BITMAP_ALLOC (NULL);
       osi.reexamine = BITMAP_ALLOC (NULL);
-      osi.depths = NULL;
-      osi.stack = NULL;
-      osi.tos = NULL;
+
+      if (object_size_type & OST_DYNAMIC)
+	osi.unknowns = BITMAP_ALLOC (NULL);
+      else
+	{
+	  osi.depths = NULL;
+	  osi.stack = NULL;
+	  osi.tos = NULL;
+	}
 
       /* First pass: walk UD chains, compute object sizes that
 	 can be computed.  osi.reexamine bitmap at the end will
@@ -775,6 +1112,14 @@ compute_builtin_object_size (tree ptr, int object_size_type,
       osi.changed = false;
       collect_object_sizes_for (&osi, ptr);
 
+      if (object_size_type & OST_DYNAMIC)
+	{
+	  osi.pass = 1;
+	  gimplify_size_expressions (&osi);
+	  BITMAP_FREE (osi.unknowns);
+	  bitmap_clear (osi.reexamine);
+	}
+
       /* Second pass: keep recomputing object sizes of variables
 	 that need reexamination, until no object sizes are
 	 increased or all object sizes are computed.  */
@@ -1021,6 +1366,25 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   return reexamine;
 }
 
+static void
+dynamic_object_size (struct object_size_info *osi, tree var,
+		     tree *size, tree *wholesize)
+{
+  int object_size_type = osi->object_size_type;
+
+  if (TREE_CODE (var) == SSA_NAME)
+    {
+      unsigned varno = SSA_NAME_VERSION (var);
+
+      collect_object_sizes_for (osi, var);
+      *size = object_sizes_get (osi, varno);
+      *wholesize = object_sizes_get (osi, varno, true);
+    }
+  else if (TREE_CODE (var) == ADDR_EXPR)
+    addr_object_size (osi, var, object_size_type, size, wholesize);
+  else
+    *size = *wholesize = size_unknown (object_size_type);
+}
 
 /* Compute object_sizes for VAR, defined at STMT, which is
    a COND_EXPR.  Return true if the object size might need reexamination
@@ -1042,6 +1406,33 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   then_ = gimple_assign_rhs2 (stmt);
   else_ = gimple_assign_rhs3 (stmt);
 
+  if (object_size_type & OST_DYNAMIC)
+    {
+      tree then_size, then_wholesize, else_size, else_wholesize;
+
+      dynamic_object_size (osi, then_, &then_size, &then_wholesize);
+      if (!size_unknown_p (then_size, object_size_type))
+	dynamic_object_size (osi, else_, &else_size, &else_wholesize);
+
+      tree cond_size, cond_wholesize;
+      if (size_unknown_p (then_size, object_size_type)
+	  || size_unknown_p (else_size, object_size_type))
+	cond_size = cond_wholesize = size_unknown (object_size_type);
+      else
+	{
+	  cond_size = fold_build3 (COND_EXPR, sizetype,
+				   gimple_assign_rhs1 (stmt),
+				   then_size, else_size);
+	  cond_wholesize = fold_build3 (COND_EXPR, sizetype,
+					gimple_assign_rhs1 (stmt),
+					then_wholesize, else_wholesize);
+	}
+
+      object_sizes_set (osi, varno, cond_size, cond_wholesize);
+
+      return false;
+    }
+
   if (TREE_CODE (then_) == SSA_NAME)
     reexamine |= merge_object_sizes (osi, var, then_);
   else
@@ -1058,6 +1449,44 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   return reexamine;
 }
 
+/* Compute an object size expression for VAR, which is the result of a PHI
+   node.  */
+
+static void
+phi_dynamic_object_size (struct object_size_info *osi, tree var)
+{
+  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);
+
+  /* The extra space is for the PHI result at the end, which object_sizes_set
+     sets for us.  */
+  tree sizes = make_tree_vec (num_args + 1);
+  tree wholesizes = make_tree_vec (num_args + 1);
+
+  /* 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 size, wholesize;
+
+      dynamic_object_size (osi, rhs, &size, &wholesize);
+
+      if (size_unknown_p (size, object_size_type))
+       break;
+
+      TREE_VEC_ELT (sizes, i) = size;
+      TREE_VEC_ELT (wholesizes, i) = wholesize;
+    }
+
+  if (i < num_args)
+    sizes = wholesizes = size_unknown (object_size_type);
+
+  object_sizes_set (osi, varno, sizes, wholesizes);
+}
+
 /* Compute object sizes for VAR.
    For ADDR_EXPR an object size is the number of remaining bytes
    to the end of the object (where what is considered an object depends on
@@ -1103,6 +1532,9 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 	{
 	  /* Found a dependency loop.  Mark the variable for later
 	     re-examination.  */
+	  if (object_size_type & OST_DYNAMIC)
+	    object_sizes_set_temp (osi, varno);
+
 	  bitmap_set_bit (osi->reexamine, varno);
 	  if (dump_file && (dump_flags & TDF_DETAILS))
 	    {
@@ -1184,6 +1616,12 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
       {
 	unsigned i;
 
+	if (object_size_type & OST_DYNAMIC)
+	  {
+	    phi_dynamic_object_size (osi, var);
+	    break;
+	  }
+
 	for (i = 0; i < gimple_phi_num_args (stmt); i++)
 	  {
 	    tree rhs = gimple_phi_arg (stmt, i)->def;
@@ -1206,7 +1644,8 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
   if (! reexamine || object_sizes_unknown_p (object_size_type, varno))
     {
       bitmap_set_bit (computed[object_size_type], varno);
-      bitmap_clear_bit (osi->reexamine, varno);
+      if (!(object_size_type & OST_DYNAMIC))
+	bitmap_clear_bit (osi->reexamine, varno);
     }
   else
     {
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v4 4/6] tree-object-size: Handle function parameters
  2021-12-01 14:27 ` [PATCH v4 0/6] __builtin_dynamic_object_size Siddhesh Poyarekar
                     ` (2 preceding siblings ...)
  2021-12-01 14:27   ` [PATCH v4 3/6] tree-object-size: Support dynamic sizes in conditions Siddhesh Poyarekar
@ 2021-12-01 14:27   ` Siddhesh Poyarekar
  2021-12-01 14:27   ` [PATCH v4 5/6] tree-object-size: Handle GIMPLE_CALL Siddhesh Poyarekar
  2021-12-01 14:27   ` [PATCH v4 6/6] tree-object-size: Dynamic sizes for ADDR_EXPR Siddhesh Poyarekar
  5 siblings, 0 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-12-01 14:27 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Handle hints provided by __attribute__ ((access (...))) to compute
dynamic sizes for objects.

gcc/ChangeLog:

	* tree-object-size.c: Include tree-dfa.h.
	(parm_object_size): New function.
	(collect_object_sizes_for): Call it.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-dynamic-object-size-0.c (test_parmsz_simple):
	New function.
	(main): Call it.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 .../gcc.dg/builtin-dynamic-object-size-0.c    | 11 ++++
 gcc/tree-object-size.c                        | 50 ++++++++++++++++++-
 2 files changed, 60 insertions(+), 1 deletion(-)

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 ddedf6a49bd..ce0f4eb17f3 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -46,6 +46,14 @@ test_deploop (size_t sz, size_t cond)
   return __builtin_dynamic_object_size (bin, 0);
 }
 
+size_t
+__attribute__ ((access (__read_write__, 1, 2)))
+__attribute__ ((noinline))
+test_parmsz_simple (void *obj, size_t sz)
+{
+  return __builtin_dynamic_object_size (obj, 0);
+}
+
 unsigned nfails = 0;
 
 #define FAIL() ({ \
@@ -64,6 +72,9 @@ main (int argc, char **argv)
     FAIL ();
   if (test_deploop (128, 129) != 32)
     FAIL ();
+  if (test_parmsz_simple (argv[0], __builtin_strlen (argv[0]) + 1)
+      != __builtin_strlen (argv[0]) + 1)
+    FAIL ();
 
   if (nfails > 0)
     __builtin_abort ();
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index dd6c90efebf..bbc708f8ade 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-fold.h"
 #include "gimple-iterator.h"
 #include "tree-cfg.h"
+#include "tree-dfa.h"
 #include "stringpool.h"
 #include "attribs.h"
 #include "builtins.h"
@@ -1449,6 +1450,53 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   return reexamine;
 }
 
+/* Find size of an object passed as a parameter to the function.  */
+
+static void
+parm_object_size (struct object_size_info *osi, tree var)
+{
+  int object_size_type = osi->object_size_type;
+  tree parm = SSA_NAME_VAR (var);
+
+  if (!(object_size_type & OST_DYNAMIC) || !POINTER_TYPE_P (TREE_TYPE (parm)))
+    expr_object_size (osi, var, parm);
+
+  /* Look for access attribute.  */
+  rdwr_map rdwr_idx;
+
+  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)
+    {
+      tree fnargs = DECL_ARGUMENTS (fndecl);
+      tree arg = NULL_TREE;
+      unsigned argpos = 0;
+
+      /* Walk through the parameters to pick the size parameter and safely
+	 scale it by the type size.  */
+      for (arg = fnargs; argpos != access->sizarg && arg;
+	   arg = TREE_CHAIN (arg), ++argpos);
+
+      if (arg != NULL_TREE && INTEGRAL_TYPE_P (TREE_TYPE (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);
+	    }
+	}
+    }
+  if (!sz)
+    sz = size_unknown (object_size_type);
+
+  object_sizes_set (osi, SSA_NAME_VERSION (var), sz, sz);
+}
+
 /* Compute an object size expression for VAR, which is the result of a PHI
    node.  */
 
@@ -1606,7 +1654,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
     case GIMPLE_NOP:
       if (SSA_NAME_VAR (var)
 	  && TREE_CODE (SSA_NAME_VAR (var)) == PARM_DECL)
-	expr_object_size (osi, var, SSA_NAME_VAR (var));
+	parm_object_size (osi, var);
       else
 	/* Uninitialized SSA names point nowhere.  */
 	unknown_object_size (osi, var);
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v4 5/6] tree-object-size: Handle GIMPLE_CALL
  2021-12-01 14:27 ` [PATCH v4 0/6] __builtin_dynamic_object_size Siddhesh Poyarekar
                     ` (3 preceding siblings ...)
  2021-12-01 14:27   ` [PATCH v4 4/6] tree-object-size: Handle function parameters Siddhesh Poyarekar
@ 2021-12-01 14:27   ` Siddhesh Poyarekar
  2021-12-01 14:27   ` [PATCH v4 6/6] tree-object-size: Dynamic sizes for ADDR_EXPR Siddhesh Poyarekar
  5 siblings, 0 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-12-01 14:27 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Handle non-constant expressions in GIMPLE_CALL arguments.  Also handle
alloca.

gcc/ChangeLog:

	* tree-object-size.c (alloc_object_size): Make and return
	non-constant size expression.
	(call_object_size): Return expression or unknown based on
	whether dynamic object size is requested.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-dynamic-object-size-0.c: Add new tests.
	* gcc.dg/builtin-object-size-1.c (test1)
	[__builtin_object_size]: Alter expected result for dynamic
	object size.
	* gcc.dg/builtin-object-size-2.c (test1)
	[__builtin_object_size]: Likewise.
	* gcc.dg/builtin-object-size-3.c (test1)
	[__builtin_object_size]: Likewise.
	* gcc.dg/builtin-object-size-4.c (test1)
	[__builtin_object_size]: Likewise.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 .../gcc.dg/builtin-dynamic-object-size-0.c    | 227 +++++++++++++++++-
 gcc/testsuite/gcc.dg/builtin-object-size-1.c  |   7 +
 gcc/testsuite/gcc.dg/builtin-object-size-2.c  |  14 ++
 gcc/testsuite/gcc.dg/builtin-object-size-3.c  |   7 +
 gcc/testsuite/gcc.dg/builtin-object-size-4.c  |  14 ++
 gcc/tree-object-size.c                        |  22 +-
 6 files changed, 282 insertions(+), 9 deletions(-)

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 ce0f4eb17f3..2db0e0d1aa2 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -4,12 +4,71 @@
 typedef __SIZE_TYPE__ size_t;
 #define abort __builtin_abort
 
+void *
+__attribute__ ((alloc_size (1)))
+__attribute__ ((__nothrow__ , __leaf__))
+__attribute__ ((noinline))
+alloc_func (size_t sz)
+{
+  return __builtin_malloc (sz);
+}
+
+void *
+__attribute__ ((alloc_size (1, 2)))
+__attribute__ ((__nothrow__ , __leaf__))
+__attribute__ ((noinline))
+calloc_func (size_t cnt, size_t sz)
+{
+  return __builtin_calloc (cnt, sz);
+}
+
+void *
+__attribute__ ((noinline))
+unknown_allocator (size_t cnt, size_t sz)
+{
+  return __builtin_calloc (cnt, sz);
+}
+
+size_t
+__attribute__ ((noinline))
+test_unknown (size_t cnt, size_t sz)
+{
+  void *ret = unknown_allocator (cnt, sz);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+/* Malloc-like allocator.  */
+
+size_t
+__attribute__ ((noinline))
+test_malloc (size_t sz)
+{
+  void *ret = alloc_func (sz);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc (size_t sz)
+{
+  void *ret = __builtin_malloc (sz);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_cond (int cond)
+{
+  void *ret = __builtin_malloc (cond ? 32 : 64);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
 size_t
 __attribute__ ((noinline))
 test_builtin_malloc_condphi (int cond)
 {
   void *ret;
- 
+
   if (cond)
     ret = __builtin_malloc (32);
   else
@@ -18,6 +77,79 @@ test_builtin_malloc_condphi (int cond)
   return __builtin_dynamic_object_size (ret, 0);
 }
 
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi2 (int cond, size_t in)
+{
+  void *ret;
+
+  if (cond)
+    ret = __builtin_malloc (in);
+  else
+    ret = __builtin_malloc (64);
+
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi3 (int cond, size_t in, size_t in2)
+{
+  void *ret;
+
+  if (cond)
+    ret = __builtin_malloc (in);
+  else
+    ret = __builtin_malloc (in2);
+
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi4 (size_t sz, int cond)
+{
+  char *a = __builtin_malloc (sz);
+  char b[sz / 2];
+
+  return __builtin_dynamic_object_size (cond ? b : (void *) &a, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi5 (size_t sz, int cond, char *c)
+{
+  char *a = __builtin_malloc (sz);
+
+  return __builtin_dynamic_object_size (cond ? c : (void *) &a, 0);
+}
+
+/* Calloc-like allocator.  */
+
+size_t
+__attribute__ ((noinline))
+test_calloc (size_t cnt, size_t sz)
+{
+  void *ret = calloc_func (cnt, sz);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_calloc (size_t cnt, size_t sz)
+{
+  void *ret = __builtin_calloc (cnt, sz);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_calloc_cond (int cond1, int cond2)
+{
+  void *ret = __builtin_calloc (cond1 ? 32 : 64, cond2 ? 1024 : 16);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
 size_t
 __attribute__ ((noinline))
 test_builtin_calloc_condphi (size_t cnt, size_t sz, int cond)
@@ -33,6 +165,47 @@ test_builtin_calloc_condphi (size_t cnt, size_t sz, int cond)
   return __builtin_dynamic_object_size (cond ? ch : (void *) &bin, 0);
 }
 
+/* Passthrough functions.  */
+
+size_t
+__attribute__ ((noinline))
+test_passthrough (size_t sz, char *in)
+{
+  char *bin = __builtin_malloc (sz);
+  char *dest = __builtin_memcpy (bin, in, sz);
+
+  return __builtin_dynamic_object_size (dest, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_passthrough_nonssa (char *in)
+{
+  char bin[__builtin_strlen (in) + 1];
+  char *dest = __builtin_memcpy (bin, in, __builtin_strlen (in) + 1);
+
+  return __builtin_dynamic_object_size (dest, 0);
+}
+
+/* Variable length arrays.  */
+size_t
+__attribute__ ((noinline))
+test_dynarray (size_t sz)
+{
+  char bin[sz];
+
+  return __builtin_dynamic_object_size (bin, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_dynarray_cond (int cond)
+{
+  char bin[cond ? 8 : 16];
+
+  return __builtin_dynamic_object_size (bin, 0);
+}
+
 size_t
 __attribute__ ((noinline))
 test_deploop (size_t sz, size_t cond)
@@ -41,7 +214,7 @@ test_deploop (size_t sz, size_t cond)
 
   for (size_t i = 0; i < sz; i++)
     if (i == cond)
-      bin = __builtin_alloca (64);
+      bin = __builtin_alloca (sz);
 
   return __builtin_dynamic_object_size (bin, 0);
 }
@@ -64,12 +237,62 @@ unsigned nfails = 0;
 int
 main (int argc, char **argv)
 {
+  size_t outsz = test_unknown (32, 42);
+  if (outsz != -1 && outsz != 32)
+    FAIL ();
+  if (test_malloc (2048) != 2048)
+    FAIL ();
+  if (test_builtin_malloc (2048) != 2048)
+    FAIL ();
+  if (test_builtin_malloc_cond (1) != 32)
+    FAIL ();
+  if (test_builtin_malloc_cond (0) != 64)
+    FAIL ();
   if (test_builtin_malloc_condphi (1) != 32)
     FAIL ();
   if (test_builtin_malloc_condphi (0) != 64)
     FAIL ();
+  if (test_builtin_malloc_condphi2 (1, 128) != 128)
+    FAIL ();
+  if (test_builtin_malloc_condphi2 (0, 128) != 64)
+    FAIL ();
+  if (test_builtin_malloc_condphi3 (1, 128, 256) != 128)
+    FAIL ();
+  if (test_builtin_malloc_condphi3 (0, 128, 256) != 256)
+    FAIL ();
+  if (test_builtin_malloc_condphi4 (128, 1) != 64)
+    FAIL ();
+  if (test_builtin_malloc_condphi4 (128, 0) != sizeof (void *))
+    FAIL ();
+  if (test_builtin_malloc_condphi5 (128, 0, argv[0]) != -1)
+    FAIL ();
+  if (test_calloc (2048, 4) != 2048 * 4)
+    FAIL ();
+  if (test_builtin_calloc (2048, 8) != 2048 * 8)
+    FAIL ();
+  if (test_builtin_calloc_cond (0, 0) != 64 * 16)
+    FAIL ();
+  if (test_builtin_calloc_cond (1, 1) != 32 * 1024)
+    FAIL ();
+  if (test_builtin_calloc_condphi (128, 1, 1) != 128)
+    FAIL ();
   if (test_builtin_calloc_condphi (128, 1, 0) == 128)
     FAIL ();
+  if (test_builtin_calloc_condphi (128, 1, 0) == -1)
+    FAIL ();
+  if (test_passthrough (__builtin_strlen (argv[0]) + 1, argv[0])
+      != __builtin_strlen (argv[0]) + 1)
+    FAIL ();
+  if (test_passthrough_nonssa (argv[0]) != __builtin_strlen (argv[0]) + 1)
+    FAIL ();
+  if (test_dynarray (__builtin_strlen (argv[0])) != __builtin_strlen (argv[0]))
+    FAIL ();
+  if (test_dynarray_cond (0) != 16)
+    FAIL ();
+  if (test_dynarray_cond (1) != 8)
+    FAIL ();
+  if (test_deploop (128, 4) != 128)
+    FAIL ();
   if (test_deploop (128, 129) != 32)
     FAIL ();
   if (test_parmsz_simple (argv[0], __builtin_strlen (argv[0]) + 1)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
index 265c87ed6fb..06d442796cb 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
@@ -135,10 +135,17 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&extb[5], 0) != sizeof (extb) - 5)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (var, 0) != x + 10)
+    abort ();
+  if (__builtin_object_size (var + 10, 0) != x)
+    abort ();
+#else
   if (__builtin_object_size (var, 0) != (size_t) -1)
     abort ();
   if (__builtin_object_size (var + 10, 0) != (size_t) -1)
     abort ();
+#endif
   if (__builtin_object_size (&var[5], 0) != (size_t) -1)
     abort ();
   if (__builtin_object_size (zerol, 0) != 0)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
index 5051fea47c3..2364f2d6afd 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-2.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
@@ -137,16 +137,30 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&extc[5].c[3], 1) != (size_t) -1)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (var, 1) != x + 10)
+    abort ();
+  if (__builtin_object_size (var + 10, 1) != x)
+    abort ();
+#else
   if (__builtin_object_size (var, 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (var + 10, 1) != (size_t) -1)
     abort ();
+#endif
   if (__builtin_object_size (&var[5], 1) != (size_t) -1)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (vara, 1) != (x + 10) * sizeof (struct A))
+    abort ();
+  if (__builtin_object_size (vara + 10, 1) != x * sizeof (struct A))
+    abort ();    
+#else
   if (__builtin_object_size (vara, 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (vara + 10, 1) != (size_t) -1)
     abort ();    
+#endif
   if (__builtin_object_size (&vara[5], 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (&vara[0].a, 1) != sizeof (vara[0].a))
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
index 1d92627266b..753ee4a1a4f 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-3.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
@@ -140,10 +140,17 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&extb[5], 2) != sizeof (extb) - 5)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (var, 2) != x + 10)
+    abort ();
+  if (__builtin_object_size (var + 10, 2) != x)
+    abort ();
+#else
   if (__builtin_object_size (var, 2) != 0)
     abort ();
   if (__builtin_object_size (var + 10, 2) != 0)
     abort ();
+#endif
   if (__builtin_object_size (&var[5], 2) != 0)
     abort ();
   if (__builtin_object_size (zerol, 2) != 0)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
index 9da3537a5f7..c383385e060 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-4.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
@@ -150,16 +150,30 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&extc[5].c[3], 3) != 0)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (var, 3) != x + 10)
+    abort ();
+  if (__builtin_object_size (var + 10, 3) != x)
+    abort ();
+#else
   if (__builtin_object_size (var, 3) != 0)
     abort ();
   if (__builtin_object_size (var + 10, 3) != 0)
     abort ();
+#endif
   if (__builtin_object_size (&var[5], 3) != 0)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (vara, 3) != (x + 10) * sizeof (struct A))
+    abort ();
+  if (__builtin_object_size (vara + 10, 3) != x * sizeof (struct A))
+    abort ();    
+#else
   if (__builtin_object_size (vara, 3) != 0)
     abort ();
   if (__builtin_object_size (vara + 10, 3) != 0)
     abort ();    
+#endif
   if (__builtin_object_size (&vara[5], 3) != 0)
     abort ();
   if (__builtin_object_size (&vara[0].a, 3) != sizeof (vara[0].a))
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index bbc708f8ade..8d440b1d0b7 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -756,7 +756,8 @@ alloc_object_size (const gcall *call, int object_size_type)
   gcc_assert (is_gimple_call (call));
 
   tree calltype;
-  if (tree callfn = gimple_call_fndecl (call))
+  tree callfn = gimple_call_fndecl (call);
+  if (callfn)
     calltype = TREE_TYPE (callfn);
   else
     calltype = gimple_call_fntype (call);
@@ -776,12 +777,13 @@ alloc_object_size (const gcall *call, int object_size_type)
       if (TREE_CHAIN (p))
         arg2 = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (p)))-1;
     }
+  else if (gimple_call_builtin_p (call, BUILT_IN_NORMAL)
+	   && callfn && ALLOCA_FUNCTION_CODE_P (DECL_FUNCTION_CODE (callfn)))
+  arg1 = 0;
 
-  if (arg1 < 0 || arg1 >= (int)gimple_call_num_args (call)
-      || TREE_CODE (gimple_call_arg (call, arg1)) != INTEGER_CST
-      || (arg2 >= 0
-	  && (arg2 >= (int)gimple_call_num_args (call)
-	      || TREE_CODE (gimple_call_arg (call, arg2)) != INTEGER_CST)))
+  /* Non-const arguments are OK here, let the caller handle constness.  */
+  if (arg1 < 0 || arg1 >= (int) gimple_call_num_args (call)
+      || arg2 >= (int) gimple_call_num_args (call))
     return size_unknown (object_size_type);
 
   tree bytes = NULL_TREE;
@@ -792,7 +794,10 @@ alloc_object_size (const gcall *call, int object_size_type)
   else if (arg1 >= 0)
     bytes = fold_convert (sizetype, gimple_call_arg (call, arg1));
 
-  return bytes;
+  if (bytes)
+    return STRIP_NOPS (bytes);
+
+  return size_unknown (object_size_type);
 }
 
 
@@ -1251,6 +1256,9 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call)
   gcc_assert (osi->pass == 0);
   tree bytes = alloc_object_size (call, object_size_type);
 
+  if (!(object_size_type & OST_DYNAMIC) && TREE_CODE (bytes) != INTEGER_CST)
+    bytes = size_unknown (object_size_type);
+
   object_sizes_set (osi, varno, bytes, bytes);
 }
 
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v4 6/6] tree-object-size: Dynamic sizes for ADDR_EXPR
  2021-12-01 14:27 ` [PATCH v4 0/6] __builtin_dynamic_object_size Siddhesh Poyarekar
                     ` (4 preceding siblings ...)
  2021-12-01 14:27   ` [PATCH v4 5/6] tree-object-size: Handle GIMPLE_CALL Siddhesh Poyarekar
@ 2021-12-01 14:27   ` Siddhesh Poyarekar
  5 siblings, 0 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-12-01 14:27 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Allow returning dynamic expressions from ADDR_EXPR for
__builtin_dynamic_object_size and also allow offsets to be dynamic.

gcc/ChangeLog:

	* tree-object-size.c (size_valid_p): New function.
	(size_for_offset): Remove OFFSET constness assertion.
	(addr_object_size): Build dynamic expressions for object
	sizes and use size_valid_p to decide if it is valid for the
	given OBJECT_SIZE_TYPE.
	(compute_builtin_object_size): Allow dynamic offsets when
	computing size at O0.
	(call_object_size): Call size_valid_p.
	(plus_stmt_object_size): Allow non-constant offset and use
	size_valid_p to decide if it is valid for the given
	OBJECT_SIZE_TYPE.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-dynamic-object-size-0.c: Add new tests.
	* gcc.dg/builtin-object-size-1.c (test1)
	[__builtin_object_size]: Adjust expected output for dynamic
	object sizes.
	* gcc.dg/builtin-object-size-2.c (test1)
	[__builtin_object_size]: Likewise.
	* gcc.dg/builtin-object-size-3.c (test1)
	[__builtin_object_size]: Likewise.
	* gcc.dg/builtin-object-size-4.c (test1)
	[__builtin_object_size]: Likewise.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 .../gcc.dg/builtin-dynamic-object-size-0.c    | 158 ++++++++++++++++++
 gcc/testsuite/gcc.dg/builtin-object-size-1.c  |  30 +++-
 gcc/testsuite/gcc.dg/builtin-object-size-2.c  |  43 ++++-
 gcc/testsuite/gcc.dg/builtin-object-size-3.c  |  25 ++-
 gcc/testsuite/gcc.dg/builtin-object-size-4.c  |  17 +-
 gcc/tree-object-size.c                        |  91 +++++-----
 6 files changed, 300 insertions(+), 64 deletions(-)

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 2db0e0d1aa2..4a1f4965ebd 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -219,6 +219,79 @@ test_deploop (size_t sz, size_t cond)
   return __builtin_dynamic_object_size (bin, 0);
 }
 
+/* Address expressions.  */
+
+struct dynarray_struct
+{
+  long a;
+  char c[16];
+  int b;
+};
+
+size_t
+__attribute__ ((noinline))
+test_dynarray_struct (size_t sz, size_t off)
+{
+  struct dynarray_struct bin[sz];
+
+  return __builtin_dynamic_object_size (&bin[off].c, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_dynarray_struct_subobj (size_t sz, size_t off)
+{
+  struct dynarray_struct bin[sz];
+
+  return __builtin_dynamic_object_size (&bin[off].c[4], 1);
+}
+
+size_t
+__attribute__ ((noinline))
+test_dynarray_struct_subobj2 (size_t sz, size_t off, size_t *objsz)
+{
+  struct dynarray_struct2
+    {
+      long a;
+      int b;
+      char c[sz];
+    };
+
+  struct dynarray_struct2 bin;
+
+  *objsz = sizeof (bin);
+
+  return __builtin_dynamic_object_size (&bin.c[off], 1);
+}
+
+size_t
+__attribute__ ((noinline))
+test_substring (size_t sz, size_t off)
+{
+  char str[sz];
+
+  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)))
 __attribute__ ((noinline))
@@ -227,6 +300,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() ({ \
@@ -287,6 +394,31 @@ main (int argc, char **argv)
     FAIL ();
   if (test_dynarray (__builtin_strlen (argv[0])) != __builtin_strlen (argv[0]))
     FAIL ();
+  if (test_dynarray_struct (42, 4) !=
+      ((42 - 4) * sizeof (struct dynarray_struct)
+       - __builtin_offsetof (struct dynarray_struct, c)))
+    FAIL ();
+  if (test_dynarray_struct (42, 48) != 0)
+    FAIL ();
+  if (test_substring (128, 4) != 128 - 4)
+    FAIL ();
+  if (test_substring (128, 142) != 0)
+    FAIL ();
+  if (test_dynarray_struct_subobj (42, 4) != 16 - 4)
+    FAIL ();
+  if (test_dynarray_struct_subobj (42, 48) != 0)
+    FAIL ();
+  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)
@@ -298,6 +430,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/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
index 06d442796cb..161f426ec0b 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
@@ -81,30 +81,56 @@ test1 (void *q, int x)
     r = malloc (30);
   else
     r = calloc (2, 16);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0) != (x < 20 ? 30 : 2 * 16))
+    abort ();
+#else
   /* We may duplicate this test onto the two exit paths.  On one path
      the size will be 32, the other it will be 30.  If we don't duplicate
      this test, then the size will be 32.  */
   if (__builtin_object_size (r, 0) != 2 * 16
       && __builtin_object_size (r, 0) != 30)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
     r = calloc (2, 14);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0) != (x < 20 ? 30 : 2 * 14))
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != 30)
     abort ();
+#endif
   if (x < 30)
     r = malloc (sizeof (a));
   else
     r = &a.a[3];
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0) != (x < 30 ? sizeof (a) : sizeof (a) - 3))
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != sizeof (a))
     abort ();
+#endif
   r = memcpy (r, "a", 2);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0) != (x < 30 ? sizeof (a) : sizeof (a) - 3))
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != sizeof (a))
     abort ();
+#endif
   r = memcpy (r + 2, "b", 2) + 2;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0)
+      != (x < 30 ? sizeof (a) - 4 : sizeof (a) - 7))
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != sizeof (a) - 4)
     abort ();
+#endif
   r = &a.a[4];
   r = memset (r, 'a', 2);
   if (__builtin_object_size (r, 0)
@@ -140,14 +166,16 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (var + 10, 0) != x)
     abort ();
+  if (__builtin_object_size (&var[5], 0) != x + 5)
+    abort ();
 #else
   if (__builtin_object_size (var, 0) != (size_t) -1)
     abort ();
   if (__builtin_object_size (var + 10, 0) != (size_t) -1)
     abort ();
-#endif
   if (__builtin_object_size (&var[5], 0) != (size_t) -1)
     abort ();
+#endif
   if (__builtin_object_size (zerol, 0) != 0)
     abort ();
   if (__builtin_object_size (&zerol, 0) != 0)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
index 2364f2d6afd..2729538da17 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-2.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
@@ -75,30 +75,56 @@ test1 (void *q, int x)
     r = malloc (30);
   else
     r = calloc (2, 16);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 1) != (x < 20 ? 30 : 2 * 16))
+    abort ();
+#else
   /* We may duplicate this test onto the two exit paths.  On one path
      the size will be 32, the other it will be 30.  If we don't duplicate
      this test, then the size will be 32.  */
   if (__builtin_object_size (r, 1) != 2 * 16
       && __builtin_object_size (r, 1) != 30)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
     r = calloc (2, 14);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 1) != (x < 20 ? 30 : 2 * 14))
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != 30)
     abort ();
+#endif
   if (x < 30)
     r = malloc (sizeof (a));
   else
     r = &a.a[3];
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 1) != (x < 30 ? sizeof (a) : sizeof (a) - 3))
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a))
     abort ();
+#endif
   r = memcpy (r, "a", 2);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 1) != (x < 30 ? sizeof (a) : sizeof (a) - 3))
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a))
     abort ();
+#endif
   r = memcpy (r + 2, "b", 2) + 2;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0)
+      != (x < 30 ? sizeof (a) - 4 : sizeof (a) - 7))
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a) - 4)
     abort ();
+#endif
   r = &a.a[4];
   r = memset (r, 'a', 2);
   if (__builtin_object_size (r, 1) != sizeof (a.a) - 4)
@@ -142,27 +168,28 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (var + 10, 1) != x)
     abort ();
+  if (__builtin_object_size (&var[5], 1) != x + 5)
+    abort ();
+  if (__builtin_object_size (vara, 1) != (x + 10) * sizeof (struct A))
+    abort ();
+  if (__builtin_object_size (vara + 10, 1) != x * sizeof (struct A))
+    abort ();    
+  if (__builtin_object_size (&vara[5], 1) != (x + 5) * sizeof (struct A))
+    abort ();
 #else
   if (__builtin_object_size (var, 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (var + 10, 1) != (size_t) -1)
     abort ();
-#endif
   if (__builtin_object_size (&var[5], 1) != (size_t) -1)
     abort ();
-#ifdef __builtin_object_size
-  if (__builtin_object_size (vara, 1) != (x + 10) * sizeof (struct A))
-    abort ();
-  if (__builtin_object_size (vara + 10, 1) != x * sizeof (struct A))
-    abort ();    
-#else
   if (__builtin_object_size (vara, 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (vara + 10, 1) != (size_t) -1)
     abort ();    
-#endif
   if (__builtin_object_size (&vara[5], 1) != (size_t) -1)
     abort ();
+#endif
   if (__builtin_object_size (&vara[0].a, 1) != sizeof (vara[0].a))
     abort ();
   if (__builtin_object_size (&vara[10].a[0], 1) != sizeof (vara[0].a))
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
index 753ee4a1a4f..db31171a8bd 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-3.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
@@ -42,9 +42,17 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (q, 2) != 0)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 2)
+      != (x < 0
+	  ? sizeof (a) - __builtin_offsetof (struct A, a) - 9
+	  : sizeof (a) - __builtin_offsetof (struct A, c) - 1))
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, c) - 1)
     abort ();
+#endif
   if (x < 6)
     r = &w[2].a[1];
   else
@@ -58,15 +66,28 @@ test1 (void *q, int x)
   if (__builtin_object_size (&y.b, 2)
       != sizeof (a) - __builtin_offsetof (struct A, b))
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 2)
+      != (x < 6
+	  ? 2 * sizeof (w[0]) - __builtin_offsetof (struct A, a) - 1
+	  : sizeof (a) - __builtin_offsetof (struct A, a) - 6))
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, a) - 6)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
     r = calloc (2, 16);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 2) != (x < 20 ? 30 : 2 * 16))
+    abort ();
+#else
   if (__builtin_object_size (r, 2) != 30)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
@@ -145,14 +166,16 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (var + 10, 2) != x)
     abort ();
+  if (__builtin_object_size (&var[5], 2) != x + 5)
+    abort ();
 #else
   if (__builtin_object_size (var, 2) != 0)
     abort ();
   if (__builtin_object_size (var + 10, 2) != 0)
     abort ();
-#endif
   if (__builtin_object_size (&var[5], 2) != 0)
     abort ();
+#endif
   if (__builtin_object_size (zerol, 2) != 0)
     abort ();
   if (__builtin_object_size (&zerol, 2) != 0)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
index c383385e060..f644890dd14 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-4.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
@@ -155,27 +155,28 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (var + 10, 3) != x)
     abort ();
+  if (__builtin_object_size (&var[5], 3) != x + 5)
+    abort ();
+  if (__builtin_object_size (vara, 3) != (x + 10) * sizeof (struct A))
+    abort ();
+  if (__builtin_object_size (vara + 10, 3) != x * sizeof (struct A))
+    abort ();    
+  if (__builtin_object_size (&vara[5], 3) != (x + 5) * sizeof (struct A))
+    abort ();
 #else
   if (__builtin_object_size (var, 3) != 0)
     abort ();
   if (__builtin_object_size (var + 10, 3) != 0)
     abort ();
-#endif
   if (__builtin_object_size (&var[5], 3) != 0)
     abort ();
-#ifdef __builtin_object_size
-  if (__builtin_object_size (vara, 3) != (x + 10) * sizeof (struct A))
-    abort ();
-  if (__builtin_object_size (vara + 10, 3) != x * sizeof (struct A))
-    abort ();    
-#else
   if (__builtin_object_size (vara, 3) != 0)
     abort ();
   if (__builtin_object_size (vara + 10, 3) != 0)
     abort ();    
-#endif
   if (__builtin_object_size (&vara[5], 3) != 0)
     abort ();
+#endif
   if (__builtin_object_size (&vara[0].a, 3) != sizeof (vara[0].a))
     abort ();
   if (__builtin_object_size (&vara[10].a[0], 3) != sizeof (vara[0].a))
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index 8d440b1d0b7..c22bfc7bbee 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -124,6 +124,14 @@ size_unknown_p (tree val, int object_size_type)
 	  && tree_to_uhwi (val) == unknown (object_size_type));
 }
 
+/* Return true if VAL is represents a valid size for OBJECT_SIZE_TYPE.  */
+
+static inline bool
+size_valid_p (tree val, int object_size_type)
+{
+  return ((object_size_type & OST_DYNAMIC) || TREE_CODE (val) == INTEGER_CST);
+}
+
 /* Return a tree with initial value for OBJECT_SIZE_TYPE.  */
 
 static inline tree
@@ -346,7 +354,6 @@ init_offset_limit (void)
 static tree
 size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE)
 {
-  gcc_checking_assert (TREE_CODE (offset) == INTEGER_CST);
   gcc_checking_assert (types_compatible_p (TREE_TYPE (sz), sizetype));
 
   /* For negative offsets, if we have a distinct WHOLESIZE, use it to get a net
@@ -545,18 +552,11 @@ 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))
-	{
-	  tree offset = TREE_OPERAND (pt_var, 1);
-	  if (TREE_CODE (offset) != INTEGER_CST
-	      || TREE_CODE (sz) != INTEGER_CST)
-	    sz = wholesize = size_unknown (object_size_type);
-	  else
-	    sz = size_for_offset (sz, offset, wholesize);
-	}
+	sz = size_for_offset (sz, TREE_OPERAND (pt_var, 1), wholesize);
 
       if (!size_unknown_p (sz, object_size_type)
-	  && TREE_CODE (sz) == INTEGER_CST
-	  && compare_tree_int (sz, offset_limit) < 0)
+	  && (TREE_CODE (sz) != INTEGER_CST
+	      || compare_tree_int (sz, offset_limit) < 0))
 	{
 	  pt_var_size = sz;
 	  pt_var_wholesize = wholesize;
@@ -576,8 +576,9 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 
   if (pt_var_size)
     {
-      /* Validate the size determined above.  */
-      if (compare_tree_int (pt_var_size, offset_limit) >= 0)
+      /* Validate the size determined above if it is a constant.  */
+      if (TREE_CODE (pt_var_size) == INTEGER_CST
+	  && compare_tree_int (pt_var_size, offset_limit) >= 0)
 	return false;
     }
 
@@ -601,7 +602,7 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 	    var = TREE_OPERAND (var, 0);
 	  if (! TYPE_SIZE_UNIT (TREE_TYPE (var))
 	      || ! tree_fits_uhwi_p (TYPE_SIZE_UNIT (TREE_TYPE (var)))
-	      || (pt_var_size
+	      || (pt_var_size && TREE_CODE (pt_var_size) == INTEGER_CST
 		  && tree_int_cst_lt (pt_var_size,
 				      TYPE_SIZE_UNIT (TREE_TYPE (var)))))
 	    var = pt_var;
@@ -615,17 +616,11 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 		switch (TREE_CODE (v))
 		  {
 		  case ARRAY_REF:
-		    if (TYPE_SIZE_UNIT (TREE_TYPE (TREE_OPERAND (v, 0)))
-			&& TREE_CODE (TREE_OPERAND (v, 1)) == INTEGER_CST)
+		    if (TYPE_SIZE_UNIT (TREE_TYPE (TREE_OPERAND (v, 0))))
 		      {
 			tree domain
 			  = TYPE_DOMAIN (TREE_TYPE (TREE_OPERAND (v, 0)));
-			if (domain
-			    && TYPE_MAX_VALUE (domain)
-			    && TREE_CODE (TYPE_MAX_VALUE (domain))
-			       == INTEGER_CST
-			    && tree_int_cst_lt (TREE_OPERAND (v, 1),
-						TYPE_MAX_VALUE (domain)))
+			if (domain && TYPE_MAX_VALUE (domain))
 			  {
 			    v = NULL_TREE;
 			    break;
@@ -692,20 +687,20 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 	var = pt_var;
 
       if (var != pt_var)
-	var_size = TYPE_SIZE_UNIT (TREE_TYPE (var));
+	{
+	  var_size = TYPE_SIZE_UNIT (TREE_TYPE (var));
+	  if (!TREE_CONSTANT (var_size))
+	    var_size = get_or_create_ssa_default_def (cfun, var_size);
+	  if (!var_size)
+	    return false;
+	}
       else if (!pt_var_size)
 	return false;
       else
 	var_size = pt_var_size;
       bytes = compute_object_offset (TREE_OPERAND (ptr, 0), var);
       if (bytes != error_mark_node)
-	{
-	  if (TREE_CODE (bytes) == INTEGER_CST
-	      && tree_int_cst_lt (var_size, bytes))
-	    bytes = size_zero_node;
-	  else
-	    bytes = size_binop (MINUS_EXPR, var_size, bytes);
-	}
+	bytes = size_for_offset (var_size, bytes);
       if (var != pt_var
 	  && pt_var_size
 	  && TREE_CODE (pt_var) == MEM_REF
@@ -714,11 +709,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)
 	    {
-	      if (TREE_CODE (bytes2) == INTEGER_CST
-		  && tree_int_cst_lt (pt_var_size, bytes2))
-		bytes2 = size_zero_node;
-	      else
-		bytes2 = size_binop (MINUS_EXPR, pt_var_size, bytes2);
+	      bytes2 = size_for_offset (pt_var_size, bytes2);
 	      bytes = size_binop (MIN_EXPR, bytes, bytes2);
 	    }
 	}
@@ -734,14 +725,18 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
       wholebytes = pt_var_wholesize;
     }
 
-  if (TREE_CODE (bytes) != INTEGER_CST
-      || TREE_CODE (wholebytes) != INTEGER_CST)
-    return false;
+  if (!size_unknown_p (bytes, object_size_type)
+      && size_valid_p (bytes, object_size_type)
+      && !size_unknown_p (bytes, object_size_type)
+      && size_valid_p (wholebytes, object_size_type))
+    {
+      *psize = bytes;
+      if (pwholesize)
+	*pwholesize = wholebytes;
+      return true;
+    }
 
-  *psize = bytes;
-  if (pwholesize)
-    *pwholesize = wholebytes;
-  return true;
+  return false;
 }
 
 
@@ -1067,11 +1062,11 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 	      tree offset = gimple_assign_rhs2 (def);
 	      ptr = gimple_assign_rhs1 (def);
 
-	      if (tree_fits_shwi_p (offset)
+	      if (((object_size_type & OST_DYNAMIC)
+		   || tree_fits_shwi_p (offset))
 		  && compute_builtin_object_size (ptr, object_size_type,
 						  psize))
 		{
-		  /* Return zero when the offset is out of bounds.  */
 		  *psize = size_for_offset (*psize, offset);
 		  return true;
 		}
@@ -1256,7 +1251,7 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call)
   gcc_assert (osi->pass == 0);
   tree bytes = alloc_object_size (call, object_size_type);
 
-  if (!(object_size_type & OST_DYNAMIC) && TREE_CODE (bytes) != INTEGER_CST)
+  if (!size_valid_p (bytes, object_size_type))
     bytes = size_unknown (object_size_type);
 
   object_sizes_set (osi, varno, bytes, bytes);
@@ -1337,7 +1332,7 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
     return false;
 
   /* Handle PTR + OFFSET here.  */
-  if (TREE_CODE (op1) == INTEGER_CST
+  if (((object_size_type & OST_DYNAMIC) || TREE_CODE (op1) == INTEGER_CST)
       && (TREE_CODE (op0) == SSA_NAME
 	  || TREE_CODE (op0) == ADDR_EXPR))
     {
@@ -1370,6 +1365,10 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   else
     bytes = wholesize = size_unknown (object_size_type);
 
+  if (!size_valid_p (bytes, object_size_type)
+      || !size_valid_p (wholesize, object_size_type))
+    bytes = wholesize = size_unknown (object_size_type);
+
   if (object_sizes_set (osi, varno, bytes, wholesize))
     osi->changed = true;
   return reexamine;
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v4 1/6] tree-object-size: Use trees and support negative offsets
  2021-12-01 14:27   ` [PATCH v4 1/6] tree-object-size: Use trees and support negative offsets Siddhesh Poyarekar
@ 2021-12-15 15:21     ` Jakub Jelinek
  2021-12-15 17:12       ` Siddhesh Poyarekar
  0 siblings, 1 reply; 97+ messages in thread
From: Jakub Jelinek @ 2021-12-15 15:21 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Wed, Dec 01, 2021 at 07:57:52PM +0530, Siddhesh Poyarekar wrote:

>  static inline bool
> -object_sizes_set (struct object_size_info *osi, unsigned varno,
> -		  unsigned HOST_WIDE_INT val)
> +object_sizes_set (struct object_size_info *osi, unsigned varno, tree val,
> +		  tree wholeval)
>  {
>    int object_size_type = osi->object_size_type;
> -  if ((object_size_type & OST_MINIMUM) == 0)
> -    {
> -      if (object_sizes[object_size_type][varno] < val)
> -	return object_sizes_set_force (osi, varno, val);
> -    }
> -  else
> -    {
> -      if (object_sizes[object_size_type][varno] > val)
> -	return object_sizes_set_force (osi, varno, val);
> -    }
> -  return false;
> +  object_size osize = object_sizes[object_size_type][varno];
> +
> +  tree oldval = osize.size;
> +  tree old_wholeval = osize.wholesize;
> +
> +  enum tree_code code = object_size_type & OST_MINIMUM ? MIN_EXPR : MAX_EXPR;
> +
> +  val = size_binop (code, val, oldval);
> +  wholeval = size_binop (code, wholeval, old_wholeval);
> +
> +  object_sizes[object_size_type][varno].size = val;
> +  object_sizes[object_size_type][varno].wholesize = wholeval;
> +  return tree_int_cst_compare (oldval, val) != 0;

Shouldn't this also tree_int_cst_compare (old_wholeval, wholeval) ?

Otherwise LGTM.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v4 2/6] __builtin_dynamic_object_size: Recognize builtin
  2021-12-01 14:27   ` [PATCH v4 2/6] __builtin_dynamic_object_size: Recognize builtin Siddhesh Poyarekar
@ 2021-12-15 15:24     ` Jakub Jelinek
  2021-12-16  2:16       ` Siddhesh Poyarekar
  0 siblings, 1 reply; 97+ messages in thread
From: Jakub Jelinek @ 2021-12-15 15:24 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Wed, Dec 01, 2021 at 07:57:53PM +0530, Siddhesh Poyarekar wrote:
> Recognize the __builtin_dynamic_object_size builtin and add paths in the
> object size path to deal with it, but treat it like
> __builtin_object_size for now.  Also add tests to provide the same
> testing coverage for the new builtin name.
> 
> gcc/ChangeLog:
> 
> 	* builtins.def (BUILT_IN_DYNAMIC_OBJECT_SIZE): New builtin.
> 	* tree-object-size.h: Move object size type bits enum from
> 	tree-object-size.c and add new value OST_DYNAMIC.
> 	* builtins.c (expand_builtin, fold_builtin_2): Handle it.
> 	(fold_builtin_object_size): Handle new builtin and adjust for
> 	change to compute_builtin_object_size.
> 	* tree-object-size.c: Include builtins.h.
> 	(compute_builtin_object_size): Adjust.
> 	(early_object_sizes_execute_one,
> 	dynamic_object_sizes_execute_one): New functions.
> 	(object_sizes_execute): Rename insert_min_max_p argument to
> 	early.  Handle BUILT_IN_DYNAMIC_OBJECT_SIZE and call the new
> 	functions.
> 	doc/extend.texi (__builtin_dynamic_object_size): Document new
> 	builtin.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/ext/builtin-dynamic-object-size1.C: New test.
> 	* g++.dg/ext/builtin-dynamic-object-size2.C: Likewise.
> 	* gcc.dg/builtin-dynamic-alloc-size.c: Likewise.
> 	* gcc.dg/builtin-dynamic-object-size-1.c: Likewise.
> 	* gcc.dg/builtin-dynamic-object-size-10.c: Likewise.
> 	* gcc.dg/builtin-dynamic-object-size-11.c: Likewise.
> 	* gcc.dg/builtin-dynamic-object-size-12.c: Likewise.
> 	* gcc.dg/builtin-dynamic-object-size-13.c: Likewise.
> 	* gcc.dg/builtin-dynamic-object-size-14.c: Likewise.
> 	* gcc.dg/builtin-dynamic-object-size-15.c: Likewise.
> 	* gcc.dg/builtin-dynamic-object-size-16.c: Likewise.
> 	* gcc.dg/builtin-dynamic-object-size-17.c: Likewise.
> 	* gcc.dg/builtin-dynamic-object-size-18.c: Likewise.
> 	* gcc.dg/builtin-dynamic-object-size-19.c: Likewise.
> 	* gcc.dg/builtin-dynamic-object-size-2.c: Likewise.
> 	* gcc.dg/builtin-dynamic-object-size-3.c: Likewise.
> 	* gcc.dg/builtin-dynamic-object-size-4.c: Likewise.
> 	* gcc.dg/builtin-dynamic-object-size-5.c: Likewise.
> 	* gcc.dg/builtin-dynamic-object-size-6.c: Likewise.
> 	* gcc.dg/builtin-dynamic-object-size-7.c: Likewise.
> 	* gcc.dg/builtin-dynamic-object-size-8.c: Likewise.
> 	* gcc.dg/builtin-dynamic-object-size-9.c: Likewise.
> 	* gcc.dg/builtin-object-size-16.c: Adjust to allow inclusion
> 	from builtin-dynamic-object-size-16.c.
> 	* gcc.dg/builtin-object-size-17.c: Likewise.

Ok, thanks.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v4 3/6] tree-object-size: Support dynamic sizes in conditions
  2021-12-01 14:27   ` [PATCH v4 3/6] tree-object-size: Support dynamic sizes in conditions Siddhesh Poyarekar
@ 2021-12-15 16:24     ` Jakub Jelinek
  2021-12-15 17:56       ` Siddhesh Poyarekar
  0 siblings, 1 reply; 97+ messages in thread
From: Jakub Jelinek @ 2021-12-15 16:24 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Wed, Dec 01, 2021 at 07:57:54PM +0530, Siddhesh Poyarekar wrote:
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
> @@ -0,0 +1,72 @@
> +/* { dg-do run } */
> +/* { dg-options "-O2" } */
> +
> +typedef __SIZE_TYPE__ size_t;
> +#define abort __builtin_abort
> +
> +size_t
> +__attribute__ ((noinline))
> +test_builtin_malloc_condphi (int cond)
> +{
> +  void *ret;
> + 
> +  if (cond)
> +    ret = __builtin_malloc (32);
> +  else
> +    ret = __builtin_malloc (64);
> +
> +  return __builtin_dynamic_object_size (ret, 0);
> +}
> +
> +size_t
> +__attribute__ ((noinline))
> +test_builtin_calloc_condphi (size_t cnt, size_t sz, int cond)
> +{
> +  struct
> +    {
> +      int a;
> +      char b;
> +    } bin[cnt];
> +
> +  char *ch = __builtin_calloc (cnt, sz);
> +
> +  return __builtin_dynamic_object_size (cond ? ch : (void *) &bin, 0);

I think it would be nice if the testcases didn't leak memory, can
you replace return ... with size_t ret = 
and add
  __builtin_free (ch);
  return ret;
in both cases (in the first perhaps rename ret to ch first.

> --- a/gcc/testsuite/gcc.dg/builtin-object-size-5.c
> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-5.c
> @@ -1,5 +1,7 @@
>  /* { dg-do compile { target i?86-*-linux* i?86-*-gnu* x86_64-*-linux* } } */
>  /* { dg-options "-O2" } */
> +/* For dynamic object sizes we 'succeed' if the returned size is known for
> +   maximum object size.  */
>  
>  typedef __SIZE_TYPE__ size_t;
>  extern void abort (void);
> @@ -13,7 +15,11 @@ test1 (size_t x)
>  
>    for (i = 0; i < x; ++i)
>      p = p + 4;
> +#ifdef __builtin_object_size
> +  if (__builtin_object_size (p, 0) == -1)
> +#else
>    if (__builtin_object_size (p, 0) != sizeof (buf) - 8)
> +#endif
>      abort ();
>  }
>  
> @@ -25,10 +31,15 @@ test2 (size_t x)
>  
>    for (i = 0; i < x; ++i)
>      p = p + 4;
> +#ifdef __builtin_object_size
> +  if (__builtin_object_size (p, 1) == -1)
> +#else
>    if (__builtin_object_size (p, 1) != sizeof (buf) - 8)
> +#endif
>      abort ();
>  }

I'd say for __bdos it would be better to rewrite the testcase
as dg-do run, perhaps use somewhat smaller buffer (say 16 times smaller;
and dg-additional-sources for a file that actually defines that buffer
and main.  Perhaps you can have those
#ifdef __builtin_object_size
  if (__builtin_object_size (p, 0) != sizeof (buf) - 8 - 4 * x)
#else
in there, just in the wrapper that #define __builtin_object_size
make it dg-do run and have dg-additional-sources (and
#ifndef N
#define N 0x40000000
#endif
and use that as size of buf.

> +	gcc_checking_assert (is_gimple_variable (ret)

This should be TREE_CODE (ret) == SSA_NAME
The reason why is_gimple_variable accepts VAR_DECLs/PARM_DECLs/RESULT_DECLs
is high vs. low gimple, but size_type_node sizes are gimple types and
both objsz passes are run when in ssa form, so it should always be either
a SSA_NAME or INTEGER_CST.

> +			     || TREE_CODE (ret) == INTEGER_CST);
> +    }
> +
> +  return ret;
>  }
>  
>  /* Set size for VARNO corresponding to OSI to VAL.  */
> @@ -176,27 +218,113 @@ object_sizes_initialize (struct object_size_info *osi, unsigned varno,
>    object_sizes[object_size_type][varno].wholesize = wholeval;
>  }
>  
> +/* Return a MODIFY_EXPR for cases where SSA and EXPR have the same type.  The
> +   TREE_VEC is returned only in case of PHI nodes.  */
> +
> +static tree
> +bundle_sizes (tree ssa, tree expr)
> +{
> +  gcc_checking_assert (TREE_TYPE (ssa) == sizetype);
> +
> +  if (!TREE_TYPE (expr))
> +    {
> +      gcc_checking_assert (TREE_CODE (expr) == TREE_VEC);

I think I'd prefer to do it the other way, condition on TREE_CODE (expr) == TREE_VEC
and if needed assert it has NULL TREE_TYPE.

> +      TREE_VEC_ELT (expr, TREE_VEC_LENGTH (expr) - 1) = ssa;
> +      return expr;
> +    }
> +
> +  gcc_checking_assert (types_compatible_p (TREE_TYPE (expr), sizetype));
> +  return size_binop (MODIFY_EXPR, ssa, expr);

This looks wrong.  MODIFY_EXPR isn't a binary expression
(tcc_binary/tcc_comparison), size_binop shouldn't be called on it.
I think you even don't want to fold it, so
  return build2 (MODIFY_EXPR, sizetype, ssa, expr);
?
Also, calling a parameter or var ssa is quite unusual, normally
one calls a SSA_NAME either name, or ssa_name etc.

> +	  gcc_checking_assert (size_initval_p (oldval, object_size_type));
> +	  gcc_checking_assert (size_initval_p (old_wholeval,
> +					       object_size_type));
> +	  /* For dynamic object sizes, all object sizes that are not gimple
> +	     variables will need to be gimplified.  */
> +	  if (TREE_CODE (wholeval) != INTEGER_CST
> +	      && !is_gimple_variable (wholeval))
> +	    {
> +	      bitmap_set_bit (osi->reexamine, varno);
> +	      wholeval = bundle_sizes (make_ssa_name (sizetype), wholeval);
> +	    }
> +	  if (TREE_CODE (val) != INTEGER_CST && !is_gimple_variable (val))

Again twice above.

> +/* Set temporary SSA names for object size and whole size to resolve dependency
> +   loops in dynamic size computation.  */
> +
> +static inline void
> +object_sizes_set_temp (struct object_size_info *osi, unsigned varno)
> +{
> +  tree val = object_sizes_get (osi, varno);
> +
> +  if (size_initval_p (val, osi->object_size_type))
> +    object_sizes_set (osi, varno,
> +		      make_ssa_name (sizetype),
> +		      make_ssa_name (sizetype));

This makes me a little bit worried.  Do you compute the wholesize SSA_NAME
at runtime always, or only when it is really needed and known not to always
be equal to the size?
I mean, e.g. for the cases where there is just const char *p = malloc (size);
and the pointer is never increased size == wholesize.  For __bos it will
just be 2 different INTEGER_CSTs, but if it would at runtime mean we compute
something twice and hope we eventually find out during later passes
it is the same, it would be bad.

> +  tree phires = TREE_VEC_ELT (wholesize, TREE_VEC_LENGTH (wholesize) - 1);
> +  gphi *wholephi = create_phi_node (phires, gimple_bb (stmt));
> +  phires = TREE_VEC_ELT (size, TREE_VEC_LENGTH (size) - 1);
> +  gphi *phi = create_phi_node (phires, gimple_bb (stmt));
> +  gphi *obj_phi =  as_a <gphi *> (stmt);

Just one space instead of 2 above.
And the above shows that you actually create 2 PHIs unconditionally,
rather than trying to do that only if 1) wholesize is ever actually
different from size 2) something needs wholesize.

> +      /* If we built an expression, we will need to build statements
> +	 and insert them on the edge right away.  */
> +      if (!is_gimple_variable (wsz))

Again, above comments about is_gimple_variable.

> +	wsz = force_gimple_operand (wsz, &seq, true, NULL);
> +      if (!is_gimple_variable (sz))
> +	{
> +	  gimple_seq s;
> +	  sz = force_gimple_operand (sz, &s, true, NULL);
> +	  gimple_seq_add_seq (&seq, s);
> +	}
> +
> +      if (seq)
> +	{
> +	  edge e = gimple_phi_arg_edge (obj_phi, i);
> +
> +	  /* Put the size definition before the last statement of the source
> +	     block of the PHI edge.  This ensures that any branches at the end
> +	     of the source block remain the last statement.  We are OK even if
> +	     the last statement is the definition of the object since it will
> +	     succeed any definitions that contribute to its size and the size
> +	     expression will succeed them too.  */
> +	  gimple_stmt_iterator gsi = gsi_last_bb (e->src);
> +	  gsi_insert_seq_before (&gsi, seq, GSI_CONTINUE_LINKING);

This looks problematic.  The last stmt in the bb might not exist at all,
or can be one that needs to terminate the bb (stmt_ends_bb_p), or can be
some other normal stmt that is just last in the bb, or it can be a debug
stmt.  E.g. for -fcompare-debug sanity, inserting before the last stmt
in the block is wrong, because without -g it could be some random stmt and
with -g it can be a debug stmt, so the resulting stmts will then differ
between -g and -g0.  Or the last stmt could actually be computing something
you use in the expressions?
I think generally you want gsi_insert_seq_on_edge, just be prepared that it
doesn't always work - one can't insert on EH or ABNORMAL edges.
For EH/ABNORMAL edges not really sure what can be done, punt, force just
__bos behavior for it, or perhaps insert before the last stmt in that case,
but beware that it would need to be SSA_NAME_OCCURS_IN_ABNORMAL_PHI
SSA_NAME which I think needs to have underlying SSA_NAME_VAR and needs to
follow rules such that out of SSA can handle it.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v4 1/6] tree-object-size: Use trees and support negative offsets
  2021-12-15 15:21     ` Jakub Jelinek
@ 2021-12-15 17:12       ` Siddhesh Poyarekar
  2021-12-15 18:43         ` Jakub Jelinek
  0 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-12-15 17:12 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 12/15/21 20:51, Jakub Jelinek wrote:
> Shouldn't this also tree_int_cst_compare (old_wholeval, wholeval) ?
> 

AFAICT, there is no situation where wholeval changes but val doesn't, so 
I believe the val check should be sufficient.  Do you think otherwise?

Siddhesh

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v4 3/6] tree-object-size: Support dynamic sizes in conditions
  2021-12-15 16:24     ` Jakub Jelinek
@ 2021-12-15 17:56       ` Siddhesh Poyarekar
  2021-12-15 18:52         ` Jakub Jelinek
  0 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-12-15 17:56 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 12/15/21 21:54, Jakub Jelinek wrote:
> On Wed, Dec 01, 2021 at 07:57:54PM +0530, Siddhesh Poyarekar wrote:
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
>> @@ -0,0 +1,72 @@
>> +/* { dg-do run } */
>> +/* { dg-options "-O2" } */
>> +
>> +typedef __SIZE_TYPE__ size_t;
>> +#define abort __builtin_abort
>> +
>> +size_t
>> +__attribute__ ((noinline))
>> +test_builtin_malloc_condphi (int cond)
>> +{
>> +  void *ret;
>> +
>> +  if (cond)
>> +    ret = __builtin_malloc (32);
>> +  else
>> +    ret = __builtin_malloc (64);
>> +
>> +  return __builtin_dynamic_object_size (ret, 0);
>> +}
>> +
>> +size_t
>> +__attribute__ ((noinline))
>> +test_builtin_calloc_condphi (size_t cnt, size_t sz, int cond)
>> +{
>> +  struct
>> +    {
>> +      int a;
>> +      char b;
>> +    } bin[cnt];
>> +
>> +  char *ch = __builtin_calloc (cnt, sz);
>> +
>> +  return __builtin_dynamic_object_size (cond ? ch : (void *) &bin, 0);
> 
> I think it would be nice if the testcases didn't leak memory, can
> you replace return ... with size_t ret =
> and add
>    __builtin_free (ch);
>    return ret;
> in both cases (in the first perhaps rename ret to ch first.
> 

OK, I'll fix up all patches for this.

>> --- a/gcc/testsuite/gcc.dg/builtin-object-size-5.c
>> +++ b/gcc/testsuite/gcc.dg/builtin-object-size-5.c
>> @@ -1,5 +1,7 @@
>>   /* { dg-do compile { target i?86-*-linux* i?86-*-gnu* x86_64-*-linux* } } */
>>   /* { dg-options "-O2" } */
>> +/* For dynamic object sizes we 'succeed' if the returned size is known for
>> +   maximum object size.  */
>>   
>>   typedef __SIZE_TYPE__ size_t;
>>   extern void abort (void);
>> @@ -13,7 +15,11 @@ test1 (size_t x)
>>   
>>     for (i = 0; i < x; ++i)
>>       p = p + 4;
>> +#ifdef __builtin_object_size
>> +  if (__builtin_object_size (p, 0) == -1)
>> +#else
>>     if (__builtin_object_size (p, 0) != sizeof (buf) - 8)
>> +#endif
>>       abort ();
>>   }
>>   
>> @@ -25,10 +31,15 @@ test2 (size_t x)
>>   
>>     for (i = 0; i < x; ++i)
>>       p = p + 4;
>> +#ifdef __builtin_object_size
>> +  if (__builtin_object_size (p, 1) == -1)
>> +#else
>>     if (__builtin_object_size (p, 1) != sizeof (buf) - 8)
>> +#endif
>>       abort ();
>>   }
> 
> I'd say for __bdos it would be better to rewrite the testcase
> as dg-do run, perhaps use somewhat smaller buffer (say 16 times smaller;
> and dg-additional-sources for a file that actually defines that buffer
> and main.  Perhaps you can have those
> #ifdef __builtin_object_size
>    if (__builtin_object_size (p, 0) != sizeof (buf) - 8 - 4 * x)
> #else
> in there, just in the wrapper that #define __builtin_object_size
> make it dg-do run and have dg-additional-sources (and
> #ifndef N
> #define N 0x40000000
> #endif
> and use that as size of buf.

Got it, I'll do that.

>> +	gcc_checking_assert (is_gimple_variable (ret)
> 
> This should be TREE_CODE (ret) == SSA_NAME
> The reason why is_gimple_variable accepts VAR_DECLs/PARM_DECLs/RESULT_DECLs
> is high vs. low gimple, but size_type_node sizes are gimple types and
> both objsz passes are run when in ssa form, so it should always be either
> a SSA_NAME or INTEGER_CST.

OK.

> 
>> +			     || TREE_CODE (ret) == INTEGER_CST);
>> +    }
>> +
>> +  return ret;
>>   }
>>   
>>   /* Set size for VARNO corresponding to OSI to VAL.  */
>> @@ -176,27 +218,113 @@ object_sizes_initialize (struct object_size_info *osi, unsigned varno,
>>     object_sizes[object_size_type][varno].wholesize = wholeval;
>>   }
>>   
>> +/* Return a MODIFY_EXPR for cases where SSA and EXPR have the same type.  The
>> +   TREE_VEC is returned only in case of PHI nodes.  */
>> +
>> +static tree
>> +bundle_sizes (tree ssa, tree expr)
>> +{
>> +  gcc_checking_assert (TREE_TYPE (ssa) == sizetype);
>> +
>> +  if (!TREE_TYPE (expr))
>> +    {
>> +      gcc_checking_assert (TREE_CODE (expr) == TREE_VEC);
> 
> I think I'd prefer to do it the other way, condition on TREE_CODE (expr) == TREE_VEC
> and if needed assert it has NULL TREE_TYPE.

OK.

> 
>> +      TREE_VEC_ELT (expr, TREE_VEC_LENGTH (expr) - 1) = ssa;
>> +      return expr;
>> +    }
>> +
>> +  gcc_checking_assert (types_compatible_p (TREE_TYPE (expr), sizetype));
>> +  return size_binop (MODIFY_EXPR, ssa, expr);
> 
> This looks wrong.  MODIFY_EXPR isn't a binary expression
> (tcc_binary/tcc_comparison), size_binop shouldn't be called on it.
> I think you even don't want to fold it, so
>    return build2 (MODIFY_EXPR, sizetype, ssa, expr);
> ?

Got it, I'll fix that.

> Also, calling a parameter or var ssa is quite unusual, normally
> one calls a SSA_NAME either name, or ssa_name etc.

OK.

>> +	  gcc_checking_assert (size_initval_p (oldval, object_size_type));
>> +	  gcc_checking_assert (size_initval_p (old_wholeval,
>> +					       object_size_type));
>> +	  /* For dynamic object sizes, all object sizes that are not gimple
>> +	     variables will need to be gimplified.  */
>> +	  if (TREE_CODE (wholeval) != INTEGER_CST
>> +	      && !is_gimple_variable (wholeval))
>> +	    {
>> +	      bitmap_set_bit (osi->reexamine, varno);
>> +	      wholeval = bundle_sizes (make_ssa_name (sizetype), wholeval);
>> +	    }
>> +	  if (TREE_CODE (val) != INTEGER_CST && !is_gimple_variable (val))
> 
> Again twice above.

OK.

>> +/* Set temporary SSA names for object size and whole size to resolve dependency
>> +   loops in dynamic size computation.  */
>> +
>> +static inline void
>> +object_sizes_set_temp (struct object_size_info *osi, unsigned varno)
>> +{
>> +  tree val = object_sizes_get (osi, varno);
>> +
>> +  if (size_initval_p (val, osi->object_size_type))
>> +    object_sizes_set (osi, varno,
>> +		      make_ssa_name (sizetype),
>> +		      make_ssa_name (sizetype));
> 
> This makes me a little bit worried.  Do you compute the wholesize SSA_NAME
> at runtime always, or only when it is really needed and known not to always
> be equal to the size?
> I mean, e.g. for the cases where there is just const char *p = malloc (size);
> and the pointer is never increased size == wholesize.  For __bos it will
> just be 2 different INTEGER_CSTs, but if it would at runtime mean we compute
> something twice and hope we eventually find out during later passes
> it is the same, it would be bad.

I'm emitting both size and wholesize all the time; wholesize only really 
gets used in size_for_offset and otherwise should get DCE'd.  Right now 
for __bos (and constant sizes) wholesize is unused if it is the same as 
size.

FOR GIMPLE_CALL, GIMPLE_NOP, etc. I return the same tree for size and 
wholesize; maybe a trivial pointer comparison (sz != wholesize) ought to 
get rid of most of the uses in size_for_offset.

>> +  tree phires = TREE_VEC_ELT (wholesize, TREE_VEC_LENGTH (wholesize) - 1);
>> +  gphi *wholephi = create_phi_node (phires, gimple_bb (stmt));
>> +  phires = TREE_VEC_ELT (size, TREE_VEC_LENGTH (size) - 1);
>> +  gphi *phi = create_phi_node (phires, gimple_bb (stmt));
>> +  gphi *obj_phi =  as_a <gphi *> (stmt);
> 
> Just one space instead of 2 above.
> And the above shows that you actually create 2 PHIs unconditionally,
> rather than trying to do that only if 1) wholesize is ever actually
> different from size 2) something needs wholesize.

Hmm, so I only really need wholesize in ADDR_EXPR and POINTER_PLUS 
expressions.  I suppose I could improve this bit too and reduce 
wholesize computations.

>> +      /* If we built an expression, we will need to build statements
>> +	 and insert them on the edge right away.  */
>> +      if (!is_gimple_variable (wsz))
> 
> Again, above comments about is_gimple_variable.
> 
>> +	wsz = force_gimple_operand (wsz, &seq, true, NULL);
>> +      if (!is_gimple_variable (sz))
>> +	{
>> +	  gimple_seq s;
>> +	  sz = force_gimple_operand (sz, &s, true, NULL);
>> +	  gimple_seq_add_seq (&seq, s);
>> +	}
>> +
>> +      if (seq)
>> +	{
>> +	  edge e = gimple_phi_arg_edge (obj_phi, i);
>> +
>> +	  /* Put the size definition before the last statement of the source
>> +	     block of the PHI edge.  This ensures that any branches at the end
>> +	     of the source block remain the last statement.  We are OK even if
>> +	     the last statement is the definition of the object since it will
>> +	     succeed any definitions that contribute to its size and the size
>> +	     expression will succeed them too.  */
>> +	  gimple_stmt_iterator gsi = gsi_last_bb (e->src);
>> +	  gsi_insert_seq_before (&gsi, seq, GSI_CONTINUE_LINKING);
> 
> This looks problematic.  The last stmt in the bb might not exist at all,

Wouldn't the bb minimally have to contain the definition of the object 
whose size we computed?  e.g. for PHI [a(2), b(3)], wouldn't bb 2 at 
least have a statement with the definition of a?

Or wait, there could be situations where the definition is in a 
different block, e.g. bb 1, which has a single edge going on to bb 2?

> or can be one that needs to terminate the bb (stmt_ends_bb_p), or can be
> some other normal stmt that is just last in the bb, or it can be a debug
> stmt.  E.g. for -fcompare-debug sanity, inserting before the last stmt
> in the block is wrong, because without -g it could be some random stmt and
> with -g it can be a debug stmt, so the resulting stmts will then differ
> between -g and -g0.  Or the last stmt could actually be computing something
> you use in the expressions?
> I think generally you want gsi_insert_seq_on_edge, just be prepared that it
> doesn't always work - one can't insert on EH or ABNORMAL edges.
> For EH/ABNORMAL edges not really sure what can be done, punt, force just
> __bos behavior for it, or perhaps insert before the last stmt in that case,
> but beware that it would need to be SSA_NAME_OCCURS_IN_ABNORMAL_PHI
> SSA_NAME which I think needs to have underlying SSA_NAME_VAR and needs to
> follow rules such that out of SSA can handle it.

I suppose __bos-like behaviour could be a good compromise, i.e. insert a 
MAX_EXPR (or MIN_EXPR) if we can't find a suitable location to insert on 
edge.

Siddhesh

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v4 1/6] tree-object-size: Use trees and support negative offsets
  2021-12-15 17:12       ` Siddhesh Poyarekar
@ 2021-12-15 18:43         ` Jakub Jelinek
  2021-12-16  0:41           ` Siddhesh Poyarekar
  0 siblings, 1 reply; 97+ messages in thread
From: Jakub Jelinek @ 2021-12-15 18:43 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Wed, Dec 15, 2021 at 10:42:29PM +0530, Siddhesh Poyarekar wrote:
> On 12/15/21 20:51, Jakub Jelinek wrote:
> > Shouldn't this also tree_int_cst_compare (old_wholeval, wholeval) ?
> > 
> 
> AFAICT, there is no situation where wholeval changes but val doesn't, so I
> believe the val check should be sufficient.  Do you think otherwise?

Dunno, just something that caught my eye.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v4 3/6] tree-object-size: Support dynamic sizes in conditions
  2021-12-15 17:56       ` Siddhesh Poyarekar
@ 2021-12-15 18:52         ` Jakub Jelinek
  0 siblings, 0 replies; 97+ messages in thread
From: Jakub Jelinek @ 2021-12-15 18:52 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Wed, Dec 15, 2021 at 11:26:48PM +0530, Siddhesh Poyarekar wrote:
> > This makes me a little bit worried.  Do you compute the wholesize SSA_NAME
> > at runtime always, or only when it is really needed and known not to always
> > be equal to the size?
> > I mean, e.g. for the cases where there is just const char *p = malloc (size);
> > and the pointer is never increased size == wholesize.  For __bos it will
> > just be 2 different INTEGER_CSTs, but if it would at runtime mean we compute
> > something twice and hope we eventually find out during later passes
> > it is the same, it would be bad.
> 
> I'm emitting both size and wholesize all the time; wholesize only really
> gets used in size_for_offset and otherwise should get DCE'd.  Right now for
> __bos (and constant sizes) wholesize is unused if it is the same as size.
> 
> FOR GIMPLE_CALL, GIMPLE_NOP, etc. I return the same tree for size and
> wholesize; maybe a trivial pointer comparison (sz != wholesize) ought to get
> rid of most of the uses in size_for_offset.

Perhaps DCE can handle well when you compute something (wholesize) that isn't really
needed and VN/CSE the case where size and wholesize is equal.  I think it
would be worth looking at a few testcases.

> > > +	{
> > > +	  edge e = gimple_phi_arg_edge (obj_phi, i);
> > > +
> > > +	  /* Put the size definition before the last statement of the source
> > > +	     block of the PHI edge.  This ensures that any branches at the end
> > > +	     of the source block remain the last statement.  We are OK even if
> > > +	     the last statement is the definition of the object since it will
> > > +	     succeed any definitions that contribute to its size and the size
> > > +	     expression will succeed them too.  */
> > > +	  gimple_stmt_iterator gsi = gsi_last_bb (e->src);
> > > +	  gsi_insert_seq_before (&gsi, seq, GSI_CONTINUE_LINKING);
> > 
> > This looks problematic.  The last stmt in the bb might not exist at all,
> 
> Wouldn't the bb minimally have to contain the definition of the object whose
> size we computed?  e.g. for PHI [a(2), b(3)], wouldn't bb 2 at least have a
> statement with the definition of a?

It can e.g. contain just a PHI.

> Or wait, there could be situations where the definition is in a different
> block, e.g. bb 1, which has a single edge going on to bb 2?

> I suppose __bos-like behaviour could be a good compromise, i.e. insert a
> MAX_EXPR (or MIN_EXPR) if we can't find a suitable location to insert on
> edge.

MAX_EXPR or MIN_EXPR?  I'd have expect the __bos constant in there.
But I must say I'm right now unsure what kind of PHIs one can have on bbs
reachable from both ab/eh edges and normal edges if we can have such bbs at
all.  I guess looking at some sigjmp/longjmp or non-local or computed goto
testcases might show something, perhaps I'll have a look tomorrow.
I'm sure we can have vop PHI.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v4 1/6] tree-object-size: Use trees and support negative offsets
  2021-12-15 18:43         ` Jakub Jelinek
@ 2021-12-16  0:41           ` Siddhesh Poyarekar
  2021-12-16 15:49             ` Jakub Jelinek
  0 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-12-16  0:41 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 12/16/21 00:13, Jakub Jelinek wrote:
> On Wed, Dec 15, 2021 at 10:42:29PM +0530, Siddhesh Poyarekar wrote:
>> On 12/15/21 20:51, Jakub Jelinek wrote:
>>> Shouldn't this also tree_int_cst_compare (old_wholeval, wholeval) ?
>>>
>>
>> AFAICT, there is no situation where wholeval changes but val doesn't, so I
>> believe the val check should be sufficient.  Do you think otherwise?
> 
> Dunno, just something that caught my eye.

How about if I add an assert like so:

   if (tree_int_cst_compare (oldval, val))
     return true;
   else
     {
       gcc_checking_assert (tree_int_cst_compare (old_wholeval,
			   wholeval) == 0);
       return false;
     }

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v4 2/6] __builtin_dynamic_object_size: Recognize builtin
  2021-12-15 15:24     ` Jakub Jelinek
@ 2021-12-16  2:16       ` Siddhesh Poyarekar
  0 siblings, 0 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-12-16  2:16 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 12/15/21 20:54, Jakub Jelinek wrote:
> On Wed, Dec 01, 2021 at 07:57:53PM +0530, Siddhesh Poyarekar wrote:
>> Recognize the __builtin_dynamic_object_size builtin and add paths in the
>> object size path to deal with it, but treat it like
>> __builtin_object_size for now.  Also add tests to provide the same
>> testing coverage for the new builtin name.
>>
>> gcc/ChangeLog:
>>
>> 	* builtins.def (BUILT_IN_DYNAMIC_OBJECT_SIZE): New builtin.
>> 	* tree-object-size.h: Move object size type bits enum from
>> 	tree-object-size.c and add new value OST_DYNAMIC.
>> 	* builtins.c (expand_builtin, fold_builtin_2): Handle it.
>> 	(fold_builtin_object_size): Handle new builtin and adjust for
>> 	change to compute_builtin_object_size.
>> 	* tree-object-size.c: Include builtins.h.
>> 	(compute_builtin_object_size): Adjust.
>> 	(early_object_sizes_execute_one,
>> 	dynamic_object_sizes_execute_one): New functions.
>> 	(object_sizes_execute): Rename insert_min_max_p argument to
>> 	early.  Handle BUILT_IN_DYNAMIC_OBJECT_SIZE and call the new
>> 	functions.
>> 	doc/extend.texi (__builtin_dynamic_object_size): Document new
>> 	builtin.
>>
>> gcc/testsuite/ChangeLog:
>>
>> 	* g++.dg/ext/builtin-dynamic-object-size1.C: New test.
>> 	* g++.dg/ext/builtin-dynamic-object-size2.C: Likewise.
>> 	* gcc.dg/builtin-dynamic-alloc-size.c: Likewise.
>> 	* gcc.dg/builtin-dynamic-object-size-1.c: Likewise.
>> 	* gcc.dg/builtin-dynamic-object-size-10.c: Likewise.
>> 	* gcc.dg/builtin-dynamic-object-size-11.c: Likewise.
>> 	* gcc.dg/builtin-dynamic-object-size-12.c: Likewise.
>> 	* gcc.dg/builtin-dynamic-object-size-13.c: Likewise.
>> 	* gcc.dg/builtin-dynamic-object-size-14.c: Likewise.
>> 	* gcc.dg/builtin-dynamic-object-size-15.c: Likewise.
>> 	* gcc.dg/builtin-dynamic-object-size-16.c: Likewise.
>> 	* gcc.dg/builtin-dynamic-object-size-17.c: Likewise.
>> 	* gcc.dg/builtin-dynamic-object-size-18.c: Likewise.
>> 	* gcc.dg/builtin-dynamic-object-size-19.c: Likewise.
>> 	* gcc.dg/builtin-dynamic-object-size-2.c: Likewise.
>> 	* gcc.dg/builtin-dynamic-object-size-3.c: Likewise.
>> 	* gcc.dg/builtin-dynamic-object-size-4.c: Likewise.
>> 	* gcc.dg/builtin-dynamic-object-size-5.c: Likewise.
>> 	* gcc.dg/builtin-dynamic-object-size-6.c: Likewise.
>> 	* gcc.dg/builtin-dynamic-object-size-7.c: Likewise.
>> 	* gcc.dg/builtin-dynamic-object-size-8.c: Likewise.
>> 	* gcc.dg/builtin-dynamic-object-size-9.c: Likewise.
>> 	* gcc.dg/builtin-object-size-16.c: Adjust to allow inclusion
>> 	from builtin-dynamic-object-size-16.c.
>> 	* gcc.dg/builtin-object-size-17.c: Likewise.
> 
> Ok, thanks.
> 
> 	Jakub
> 

May I commit this and the first patch (with the added assert)?  That 
will allow me to add _FORTIFY_SOURCE=3 support for gcc 12.1 into glibc 
right away.

Thanks,
Siddhesh

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v4 1/6] tree-object-size: Use trees and support negative offsets
  2021-12-16  0:41           ` Siddhesh Poyarekar
@ 2021-12-16 15:49             ` Jakub Jelinek
  2021-12-16 18:56               ` Siddhesh Poyarekar
  0 siblings, 1 reply; 97+ messages in thread
From: Jakub Jelinek @ 2021-12-16 15:49 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Thu, Dec 16, 2021 at 06:11:27AM +0530, Siddhesh Poyarekar wrote:
> On 12/16/21 00:13, Jakub Jelinek wrote:
> > On Wed, Dec 15, 2021 at 10:42:29PM +0530, Siddhesh Poyarekar wrote:
> > > On 12/15/21 20:51, Jakub Jelinek wrote:
> > > > Shouldn't this also tree_int_cst_compare (old_wholeval, wholeval) ?
> > > > 
> > > 
> > > AFAICT, there is no situation where wholeval changes but val doesn't, so I
> > > believe the val check should be sufficient.  Do you think otherwise?
> > 
> > Dunno, just something that caught my eye.
> 
> How about if I add an assert like so:
> 
>   if (tree_int_cst_compare (oldval, val))
>     return true;
>   else
>     {
>       gcc_checking_assert (tree_int_cst_compare (old_wholeval,
> 			   wholeval) == 0);
>       return false;
>     }

Yes, but please fix up formatting, wholeval should go below old_wholeval.
Though, perhaps it would be better to:

  if (tree_int_cst_compare (oldval, val))
    return true;

  gcc_checking_assert (tree_int_cst_compare (old_wholeval, wholeval) == 0);
  return false;

Ok with that change.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v4 1/6] tree-object-size: Use trees and support negative offsets
  2021-12-16 15:49             ` Jakub Jelinek
@ 2021-12-16 18:56               ` Siddhesh Poyarekar
  2021-12-16 21:16                 ` Jakub Jelinek
  0 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-12-16 18:56 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 12/16/21 21:19, Jakub Jelinek wrote:
> Yes, but please fix up formatting, wholeval should go below old_wholeval.
> Though, perhaps it would be better to:
> 
>    if (tree_int_cst_compare (oldval, val))
>      return true;
> 
>    gcc_checking_assert (tree_int_cst_compare (old_wholeval, wholeval) == 0);
>    return false;
> 
> Ok with that change.
> 
> 	Jakub
> 

I thought about this some more and I think I'm wrong in assuming that 
wholeval won't change in subsequent passes; my tests show I'm wrong too. 
  I'm now testing with the condition fixed up to:

   return (tree_int_cst_compare (oldval, val) != 0
	  || tree_int_cst_compare (old_wholeval, wholeval) != 0);

as you had suggested earlier and will push it if it passes bootstrap and 
a clean testsuite run.  I'm doing a ubsan bootstrap too because it's 
been quite useful in catching corner cases in __bos before.

Thanks,
Siddhesh

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v4 1/6] tree-object-size: Use trees and support negative offsets
  2021-12-16 18:56               ` Siddhesh Poyarekar
@ 2021-12-16 21:16                 ` Jakub Jelinek
  0 siblings, 0 replies; 97+ messages in thread
From: Jakub Jelinek @ 2021-12-16 21:16 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Fri, Dec 17, 2021 at 12:26:56AM +0530, Siddhesh Poyarekar wrote:
> On 12/16/21 21:19, Jakub Jelinek wrote:
> > Yes, but please fix up formatting, wholeval should go below old_wholeval.
> > Though, perhaps it would be better to:
> > 
> >    if (tree_int_cst_compare (oldval, val))
> >      return true;
> > 
> >    gcc_checking_assert (tree_int_cst_compare (old_wholeval, wholeval) == 0);
> >    return false;
> > 
> > Ok with that change.
> > 
> > 	Jakub
> > 
> 
> I thought about this some more and I think I'm wrong in assuming that
> wholeval won't change in subsequent passes; my tests show I'm wrong too.
> I'm now testing with the condition fixed up to:
> 
>   return (tree_int_cst_compare (oldval, val) != 0
> 	  || tree_int_cst_compare (old_wholeval, wholeval) != 0);
> 
> as you had suggested earlier and will push it if it passes bootstrap and a
> clean testsuite run.  I'm doing a ubsan bootstrap too because it's been
> quite useful in catching corner cases in __bos before.

Ok.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v5 0/4] __builtin_dynamic_object_size
  2021-11-09 19:01 [PATCH 00/10] __builtin_dynamic_object_size Siddhesh Poyarekar
                   ` (12 preceding siblings ...)
  2021-12-01 14:27 ` [PATCH v4 0/6] __builtin_dynamic_object_size Siddhesh Poyarekar
@ 2021-12-18 12:35 ` Siddhesh Poyarekar
  2021-12-18 12:35   ` [PATCH v5 1/4] tree-object-size: Support dynamic sizes in conditions Siddhesh Poyarekar
                     ` (4 more replies)
  2022-01-11  8:57 ` [PATCH v6 " Siddhesh Poyarekar
  14 siblings, 5 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-12-18 12:35 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

This patchset enhances the __builtin_dynamic_object_size builtin to
produce dynamic expressions for object sizes to improve coverage of
_FORTIFY_SOURCE.

Testing:
--------

This series has been tested with build and test for i686, bootstrap with
ubsan and full bootstrap and test with x86_64.  I also tested the
toolchain with a glibc build and testsuite run for x86_64 and i686 with
_FORTIFY_SOURCE=3 enabled for gcc12.

Additional testing plans (i.e. I've already started to do some of this):

- Build packages to compare values returned by __builtin_object_size
  with the older pass and this new one.  Also compare with
  __builtin_dynamic_object_size.

- Expand the list of packages to get more coverage metrics.

- Explore performance impact on applications on building with
  _FORTIFY_SOURCE=3.

Siddhesh Poyarekar (4):
  tree-object-size: Support dynamic sizes in conditions
  tree-object-size: Handle function parameters
  tree-object-size: Handle GIMPLE_CALL
  tree-object-size: Dynamic sizes for ADDR_EXPR

 gcc/builtins.c                                |   6 +-
 .../gcc.dg/builtin-dynamic-object-size-0.c    | 495 +++++++++++++
 .../gcc.dg/builtin-dynamic-object-size-10.c   |   2 +
 .../builtin-dynamic-object-size-5-main.c      |  32 +
 .../gcc.dg/builtin-dynamic-object-size-5.c    |   7 +-
 gcc/testsuite/gcc.dg/builtin-object-size-1.c  | 154 +++-
 gcc/testsuite/gcc.dg/builtin-object-size-2.c  | 133 ++++
 gcc/testsuite/gcc.dg/builtin-object-size-3.c  | 151 ++++
 gcc/testsuite/gcc.dg/builtin-object-size-4.c  |  93 +++
 gcc/testsuite/gcc.dg/builtin-object-size-5.c  |  22 +-
 gcc/tree-object-size.c                        | 670 +++++++++++++++---
 11 files changed, 1677 insertions(+), 88 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5-main.c

-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v5 1/4] tree-object-size: Support dynamic sizes in conditions
  2021-12-18 12:35 ` [PATCH v5 0/4] __builtin_dynamic_object_size Siddhesh Poyarekar
@ 2021-12-18 12:35   ` Siddhesh Poyarekar
  2022-01-10 10:37     ` Jakub Jelinek
  2021-12-18 12:35   ` [PATCH v5 2/4] tree-object-size: Handle function parameters Siddhesh Poyarekar
                     ` (3 subsequent siblings)
  4 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-12-18 12:35 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Handle GIMPLE_PHI and conditionals specially for dynamic objects,
returning PHI/conditional expressions instead of just a MIN/MAX
estimate.

This makes the returned object size variable for loops and conditionals,
so tests need to be adjusted to look for precise size in some cases.
builtin-dynamic-object-size-5.c had to be modified to only look for
success in maximum object size case and skip over the minimum object
size tests because the result is no longer a compile time constant.

I also added some simple tests to exercise conditionals with dynamic
object sizes.

gcc/ChangeLog:

	* builtins.c (fold_builtin_object_size): Adjust for dynamic size
	expressions.
	* tree-object-size.c: Include gimplify-me.h.
	(struct object_size_info): New member UNKNOWNS.
	(size_initval_p, size_usable_p, object_sizes_get_raw): New
	functions.
	(object_sizes_get): Return suitable gimple variable for
	object size.
	(bundle_sizes): New function.
	(object_sizes_set): Use it and handle dynamic object size
	expressions.
	(object_sizes_set_temp): New function.
	(size_for_offset): Adjust for dynamic size expressions.
	(emit_phi_nodes, propagate_unknowns, gimplify_size_expressions):
	New functions.
	(compute_builtin_object_size): Call gimplify_size_expressions
	for OST_DYNAMIC.
	(dynamic_object_size): New function.
	(cond_expr_object_size): Use it.
	(phi_dynamic_object_size): New function.
	(collect_object_sizes_for): Call it for OST_DYNAMIC.  Adjust to
	accommodate dynamic object sizes.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-dynamic-object-size-0.c: New tests.
	* gcc.dg/builtin-dynamic-object-size-10.c: Add comment.
	* gcc.dg/builtin-dynamic-object-size-5-main.c: New file.
	* gcc.dg/builtin-dynamic-object-size-5.c: Use it and change test
	to dg-do run.
	* gcc.dg/builtin-object-size-5.c [!N]: Define N.
	(test1, test2, test3, test4) [__builtin_object_size]: Expect
	exact result for __builtin_dynamic_object_size.
	* gcc.dg/builtin-object-size-1.c [__builtin_object_size]: Expect
	exact size expressions for __builtin_dynamic_object_size.
	* gcc.dg/builtin-object-size-2.c [__builtin_object_size]:
	Likewise.
	* gcc.dg/builtin-object-size-3.c [__builtin_object_size]:
	Likewise.
	* gcc.dg/builtin-object-size-4.c [__builtin_object_size]:
	Likewise.
	* gcc.dg/builtin-object-size-5.c [__builtin_object_size]:
	Likewise.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
Changes since v4:

- Propagate sameness of size and wholesize through PHI nodes whenever
  possible.  Check and avoid emitting wholesize if it is the same as
  size.
- Bail out and punt to __builtin_object_size if PHI node has any complex
  edges.  Use insert_seq_on_edge to emit size PHI node edge values.
- Free allocations in tests.

 gcc/builtins.c                                |   6 +-
 .../gcc.dg/builtin-dynamic-object-size-0.c    |  77 +++
 .../gcc.dg/builtin-dynamic-object-size-10.c   |   2 +
 .../builtin-dynamic-object-size-5-main.c      |  32 ++
 .../gcc.dg/builtin-dynamic-object-size-5.c    |   7 +-
 gcc/testsuite/gcc.dg/builtin-object-size-1.c  | 119 +++-
 gcc/testsuite/gcc.dg/builtin-object-size-2.c  |  92 ++++
 gcc/testsuite/gcc.dg/builtin-object-size-3.c  | 121 +++++
 gcc/testsuite/gcc.dg/builtin-object-size-4.c  |  78 +++
 gcc/testsuite/gcc.dg/builtin-object-size-5.c  |  22 +-
 gcc/tree-object-size.c                        | 509 +++++++++++++++++-
 11 files changed, 1030 insertions(+), 35 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5-main.c

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 00f6c5552bf..01c24f42540 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -10285,7 +10285,8 @@ fold_builtin_object_size (tree ptr, tree ost, enum built_in_function fcode)
   if (TREE_CODE (ptr) == ADDR_EXPR)
     {
       compute_builtin_object_size (ptr, object_size_type, &bytes);
-      if (int_fits_type_p (bytes, size_type_node))
+      if ((object_size_type & OST_DYNAMIC)
+	  || int_fits_type_p (bytes, size_type_node))
 	return fold_convert (size_type_node, bytes);
     }
   else if (TREE_CODE (ptr) == SSA_NAME)
@@ -10294,7 +10295,8 @@ fold_builtin_object_size (tree ptr, tree ost, enum built_in_function fcode)
        later.  Maybe subsequent passes will help determining
        it.  */
       if (compute_builtin_object_size (ptr, object_size_type, &bytes)
-	  && int_fits_type_p (bytes, size_type_node))
+	  && ((object_size_type & OST_DYNAMIC)
+	      || int_fits_type_p (bytes, size_type_node)))
 	return fold_convert (size_type_node, bytes);
     }
 
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
new file mode 100644
index 00000000000..81588cb28a6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -0,0 +1,77 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+typedef __SIZE_TYPE__ size_t;
+#define abort __builtin_abort
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi (int cond)
+{
+  void *ch;
+ 
+  if (cond)
+    ch = __builtin_malloc (32);
+  else
+    ch = __builtin_malloc (64);
+
+  size_t ret = __builtin_dynamic_object_size (ch, 0);
+
+  __builtin_free (ch);
+  return ret;
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_calloc_condphi (size_t cnt, size_t sz, int cond)
+{
+  struct
+    {
+      int a;
+      char b;
+    } bin[cnt];
+
+  char *ch = __builtin_calloc (cnt, sz);
+  size_t ret = __builtin_dynamic_object_size (cond ? ch : (void *) &bin, 0);
+
+  __builtin_free (ch);
+  return ret;
+}
+
+size_t
+__attribute__ ((noinline))
+test_deploop (size_t sz, size_t cond)
+{
+  char *bin = __builtin_alloca (32);
+
+  for (size_t i = 0; i < sz; i++)
+    if (i == cond)
+      bin = __builtin_alloca (64);
+
+  return __builtin_dynamic_object_size (bin, 0);
+}
+
+unsigned nfails = 0;
+
+#define FAIL() ({ \
+  __builtin_printf ("Failure at line: %d\n", __LINE__);			      \
+  nfails++;								      \
+})
+
+int
+main (int argc, char **argv)
+{
+  if (test_builtin_malloc_condphi (1) != 32)
+    FAIL ();
+  if (test_builtin_malloc_condphi (0) != 64)
+    FAIL ();
+  if (test_builtin_calloc_condphi (128, 1, 0) == 128)
+    FAIL ();
+  if (test_deploop (128, 129) != 32)
+    FAIL ();
+
+  if (nfails > 0)
+    __builtin_abort ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
index bc880a589ae..3a2d9821a44 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
@@ -5,5 +5,7 @@
 #define __builtin_object_size __builtin_dynamic_object_size
 #include "builtin-object-size-10.c"
 
+/* early_objsz should resolve __builtin_dynamic_object_size like
+   __builtin_object_size.  */
 /* { dg-final { scan-tree-dump "maximum object size 21" "early_objsz" } } */
 /* { dg-final { scan-tree-dump "maximum subobject size 16" "early_objsz" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5-main.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5-main.c
new file mode 100644
index 00000000000..49f459b8cf2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5-main.c
@@ -0,0 +1,32 @@
+#ifdef N
+typedef __SIZE_TYPE__ size_t;
+
+char buf[N];
+
+void test1 (size_t);
+void test2 (size_t);
+void test3 (size_t);
+void test4 (size_t);
+void test5 (size_t);
+void test6 (size_t);
+void test7 (size_t);
+
+int
+main (void)
+{
+  test1 (42);
+  test2 (42);
+  test3 (42);
+  test4 (42);
+  test5 (42);
+  test6 (42);
+  test7 (42);
+  return 0;
+}
+#else
+int
+main (void)
+{
+  return 0;
+}
+#endif
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c
index 698b03c34be..80b77dd5249 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c
@@ -1,7 +1,6 @@
-/* { dg-do compile { target i?86-*-linux* i?86-*-gnu* x86_64-*-linux* } } */
-/* { dg-options "-O2" } */
+/* { dg-do run { target i?86-*-linux* i?86-*-gnu* x86_64-*-linux* } } */
+/* { dg-options "-O2 -DN=0x4000000" } */
+/* { dg-additional-sources "builtin-dynamic-object-size-5-main.c" } */
 
 #define __builtin_object_size __builtin_dynamic_object_size
 #include "builtin-object-size-5.c"
-
-/* { dg-final { scan-assembler-not "abort" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
index 0154f4e9695..265c87ed6fb 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
@@ -42,9 +42,17 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (q, 0) != (size_t) -1)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0)
+      != (x < 0
+	  ? sizeof (a) - __builtin_offsetof (struct A, a) - 9
+	  : sizeof (a) - __builtin_offsetof (struct A, c) - 1))
+    abort ();
+#else
   if (__builtin_object_size (r, 0)
       != sizeof (a) - __builtin_offsetof (struct A, a) - 9)
     abort ();
+#endif
   if (x < 6)
     r = &w[2].a[1];
   else
@@ -58,9 +66,17 @@ test1 (void *q, int x)
   if (__builtin_object_size (&y.b, 0)
       != sizeof (a) - __builtin_offsetof (struct A, b))
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0)
+      != (x < 6
+	  ? 2 * sizeof (w[0]) - __builtin_offsetof (struct A, a) - 1
+	  : sizeof (a) - __builtin_offsetof (struct A, a) - 6))
+    abort ();
+#else
   if (__builtin_object_size (r, 0)
       != 2 * sizeof (w[0]) - __builtin_offsetof (struct A, a) - 1)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
@@ -165,6 +181,7 @@ test2 (void)
   struct B { char buf1[10]; char buf2[10]; } a;
   char *r, buf3[20];
   int i;
+  size_t res;
 
   if (sizeof (a) != 20)
     return;
@@ -181,7 +198,24 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[9];
     }
-  if (__builtin_object_size (r, 0) != 20)
+#ifdef __builtin_object_size
+  res = sizeof (buf3);
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 1;
+      else if (i == l1)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 7;
+      else if (i == l1 + 1)
+        res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
+    }
+#else
+  res = 20;
+#endif
+  if (__builtin_object_size (r, 0) != res)
     abort ();
   r = &buf3[20];
   for (i = 0; i < 4; ++i)
@@ -195,13 +229,45 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[9];
     }
-  if (__builtin_object_size (r, 0) != 15)
+#ifdef __builtin_object_size
+  res = sizeof (buf3) - 20;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 7;
+      else if (i == l1)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 7;
+      else if (i == l1 + 1)
+        res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
+    }
+  if (__builtin_object_size (r, 0) != res)
+    abort ();
+#else
+  res = 15;
+#endif
+  if (__builtin_object_size (r, 0) != res)
     abort ();
   r += 8;
+#ifdef __builtin_object_size
+  res -= 8;
+  if (__builtin_object_size (r, 0) != res)
+    abort ();
+  if (res >= 6)
+    {
+      if (__builtin_object_size (r + 6, 0) != res - 6)
+        abort ();
+    }
+  else if (__builtin_object_size (r + 6, 0) != 0)
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != 7)
     abort ();
   if (__builtin_object_size (r + 6, 0) != 1)
     abort ();
+#endif
   r = &buf3[18];
   for (i = 0; i < 4; ++i)
     {
@@ -214,8 +280,31 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[4];
     }
+#ifdef __builtin_object_size
+  res = sizeof (buf3) - 18;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+          res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
+      else if (i == l1)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 9;
+      else if (i == l1 + 1)
+        res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 4;
+    }
+  if (res >= 12)
+    {
+      if (__builtin_object_size (r + 12, 0) != res - 12)
+        abort ();
+    }
+  else if (__builtin_object_size (r + 12, 0) != 0)
+    abort ();
+#else
   if (__builtin_object_size (r + 12, 0) != 4)
     abort ();
+#endif
 }
 
 void
@@ -358,6 +447,10 @@ test5 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 0) != sizeof (buf) - 8 - 4 * x)
+    abort ();
+#else
   /* My understanding of ISO C99 6.5.6 is that a conforming
      program will not end up with p equal to &buf[0]
      through &buf[7], i.e. calling this function with say
@@ -367,6 +460,7 @@ test5 (size_t x)
      it would be 64 (or conservative (size_t) -1 == unknown).  */
   if (__builtin_object_size (p, 0) != sizeof (buf) - 8)
     abort ();
+#endif
   memset (p, ' ', sizeof (buf) - 8 - 4 * 4);
 }
 
@@ -380,8 +474,13 @@ test6 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 0) != sizeof (t) - 8 - 4 * x)
+    abort ();
+#else
   if (__builtin_object_size (p, 0) != sizeof (t) - 8)
     abort ();
+#endif
   memset (p, ' ', sizeof (t) - 8 - 4 * 4);
 }
 
@@ -436,21 +535,37 @@ test9 (unsigned cond)
   else
     p = &buf2[4];
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (&p[-4], 0) != (cond ? 6 : 10))
+    abort ();
+#else
   if (__builtin_object_size (&p[-4], 0) != 10)
     abort ();
+#endif
 
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 0) != ((cond ? 2 : 6) + cond))
+    abort ();
+#else
   if (__builtin_object_size (p, 0) != 10)
     abort ();
+#endif
 
   p = &y.c[8];
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 0)
+      != sizeof (y) - __builtin_offsetof (struct A, c) - 8 + cond)
+    abort ();
+#else
   if (__builtin_object_size (p, 0) != sizeof (y))
     abort ();
+#endif
 }
 
 int
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
index 5cf29291aff..5051fea47c3 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-2.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
@@ -43,8 +43,15 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (q, 1) != (size_t) -1)
     abort ();
+#ifdef __builtin_object_size
+  if (x < 0
+      ? __builtin_object_size (r, 1) != sizeof (a.a) - 9
+      : __builtin_object_size (r, 1) != sizeof (a.c) - 1)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a.c) - 1)
     abort ();
+#endif
   if (x < 6)
     r = &w[2].a[1];
   else
@@ -55,8 +62,15 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&y.b, 1) != sizeof (a.b))
     abort ();
+#ifdef __builtin_object_size
+  if (x < 6
+      ? __builtin_object_size (r, 1) != sizeof (a.a) - 1
+      : __builtin_object_size (r, 1) != sizeof (a.a) - 6)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a.a) - 1)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
@@ -185,6 +199,9 @@ test2 (void)
   struct B { char buf1[10]; char buf2[10]; } a;
   char *r, buf3[20];
   int i;
+#ifdef __builtin_object_size
+  size_t dyn_res;
+#endif
 
   if (sizeof (a) != 20)
     return;
@@ -201,8 +218,26 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[9];
     }
+#ifdef __builtin_object_size
+  dyn_res = sizeof (buf3);
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	dyn_res = sizeof (a.buf1) - 1;
+      else if (i == l1)
+	dyn_res = sizeof (a.buf2) - 7;
+      else if (i == l1 + 1)
+	dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+	dyn_res = sizeof (a.buf1) - 9;
+    }
+  if (__builtin_object_size (r, 1) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (buf3))
     abort ();
+#endif
   r = &buf3[20];
   for (i = 0; i < 4; ++i)
     {
@@ -215,13 +250,50 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[9];
     }
+#ifdef __builtin_object_size
+  dyn_res = sizeof (buf3) - 20;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+        dyn_res = sizeof (a.buf1) - 7;
+      else if (i == l1)
+        dyn_res = sizeof (a.buf2) - 7;
+      else if (i == l1 + 1)
+        dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+        dyn_res = sizeof (a.buf1) - 9;
+    }
+  if (__builtin_object_size (r, 1) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (buf3) - 5)
     abort ();
+#endif
   r += 8;
+#ifdef __builtin_object_size
+  if (dyn_res >= 8)
+    {
+      dyn_res -= 8;
+      if (__builtin_object_size (r, 1) != dyn_res)
+	abort ();
+
+      if (dyn_res >= 6)
+	{
+	  if (__builtin_object_size (r + 6, 1) != dyn_res - 6)
+	    abort ();
+	}
+      else if (__builtin_object_size (r + 6, 1) != 0)
+	abort ();
+    }
+  else if (__builtin_object_size (r, 1) != 0)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (buf3) - 13)
     abort ();
   if (__builtin_object_size (r + 6, 1) != sizeof (buf3) - 19)
     abort ();
+#endif
 }
 
 void
@@ -339,8 +411,13 @@ test5 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 1) != sizeof (t.buf) - 8 - 4 * x)
+    abort ();
+#else
   if (__builtin_object_size (p, 1) != sizeof (t.buf) - 8)
     abort ();
+#endif
   memset (p, ' ', sizeof (t.buf) - 8 - 4 * 4);
 }
 
@@ -394,21 +471,36 @@ test8 (unsigned cond)
   else
     p = &buf2[4];
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (&p[-4], 1) != (cond ? 6 : 10))
+    abort ();
+#else
   if (__builtin_object_size (&p[-4], 1) != 10)
     abort ();
+#endif
 
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 1) != ((cond ? 2 : 6) + cond))
+    abort ();
+#else
   if (__builtin_object_size (p, 1) != 10)
     abort ();
+#endif
 
   p = &y.c[8];
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 1) != sizeof (y.c) - 8 + cond)
+    abort ();
+#else
   if (__builtin_object_size (p, 1) != sizeof (y.c))
     abort ();
+#endif
 }
 
 int
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
index 3a692c4e3d2..1d92627266b 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-3.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
@@ -71,23 +71,45 @@ test1 (void *q, int x)
     r = malloc (30);
   else
     r = calloc (2, 14);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 2) != (x < 20 ? 30 : 2 * 14))
+    abort ();
+#else
   if (__builtin_object_size (r, 2) != 2 * 14)
     abort ();
+#endif
   if (x < 30)
     r = malloc (sizeof (a));
   else
     r = &a.a[3];
+#ifdef __builtin_object_size
+  size_t objsz = (x < 30 ? sizeof (a)
+                  : sizeof (a) - __builtin_offsetof (struct A, a) - 3);
+  if (__builtin_object_size (r, 2) != objsz)
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, a) - 3)
     abort ();
+#endif
   r = memcpy (r, "a", 2);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 2) != objsz)
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, a) - 3)
     abort ();
+#endif
   r = memcpy (r + 2, "b", 2) + 2;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 2) != objsz - 4)
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, a) - 3 - 4)
     abort ();
+#endif
   r = &a.a[4];
   r = memset (r, 'a', 2);
   if (__builtin_object_size (r, 2)
@@ -164,6 +186,9 @@ test2 (void)
   struct B { char buf1[10]; char buf2[10]; } a;
   char *r, buf3[20];
   int i;
+#ifdef __builtin_object_size
+  size_t dyn_res;
+#endif
 
   if (sizeof (a) != 20)
     return;
@@ -180,8 +205,26 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[9];
     }
+#ifdef __builtin_object_size
+  dyn_res = sizeof (buf3);
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 1;
+      else if (i == l1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 7;
+      else if (i == l1 + 1)
+	dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
+    }
+  if (__builtin_object_size (r, 2) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 2) != 3)
     abort ();
+#endif
   r = &buf3[20];
   for (i = 0; i < 4; ++i)
     {
@@ -208,13 +251,44 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[4];
     }
+#ifdef __builtin_object_size
+  dyn_res = sizeof (buf3) - 2;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 1;
+      else if (i == l1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 2;
+      else if (i == l1 + 1)
+	dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 4;
+    }
+  if (__builtin_object_size (r, 2) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 2) != 15)
     abort ();
+#endif
   r += 8;
+#ifdef __builtin_object_size
+  dyn_res -= 8;
+  if (__builtin_object_size (r, 2) != dyn_res)
+    abort ();
+  if (dyn_res >= 6)
+    {
+      if (__builtin_object_size (r + 6, 2) != dyn_res - 6)
+	abort ();
+    }
+  else if (__builtin_object_size (r + 6, 2) != 0)
+    abort ();
+#else
   if (__builtin_object_size (r, 2) != 7)
     abort ();
   if (__builtin_object_size (r + 6, 2) != 1)
     abort ();
+#endif
   r = &buf3[18];
   for (i = 0; i < 4; ++i)
     {
@@ -227,8 +301,31 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[4];
     }
+#ifdef __builtin_object_size
+  dyn_res = sizeof (buf3) - 18;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
+      else if (i == l1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 9;
+      else if (i == l1 + 1)
+	dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 4;
+    }
+  if (dyn_res >= 12)
+    {
+      if (__builtin_object_size (r + 12, 2) != dyn_res - 12)
+	abort ();
+    }
+  else if (__builtin_object_size (r + 12, 2) != 0)
+    abort ();
+#else
   if (__builtin_object_size (r + 12, 2) != 0)
     abort ();
+#endif
 }
 
 void
@@ -371,7 +468,11 @@ test5 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 2) != sizeof (buf) - 8 - 4 * x)
+#else
   if (__builtin_object_size (p, 2) != 0)
+#endif
     abort ();
   memset (p, ' ', sizeof (buf) - 8 - 4 * 4);
 }
@@ -386,7 +487,11 @@ test6 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 2) != sizeof (t) - 8 - 4 * x)
+#else
   if (__builtin_object_size (p, 2) != 0)
+#endif
     abort ();
   memset (p, ' ', sizeof (t) - 8 - 4 * 4);
 }
@@ -442,22 +547,38 @@ test9 (unsigned cond)
   else
     p = &buf2[4];
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (&p[-4], 2) != (cond ? 6 : 10))
+    abort ();
+#else
   if (__builtin_object_size (&p[-4], 2) != 6)
     abort ();
+#endif
 
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 2) != ((cond ? 2 : 6) + cond))
+    abort ();
+#else
   if (__builtin_object_size (p, 2) != 2)
     abort ();
+#endif
 
   p = &y.c[8];
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 2)
+      != sizeof (y) - __builtin_offsetof (struct A, c) - 8 + cond)
+    abort ();
+#else
   if (__builtin_object_size (p, 2)
       != sizeof (y) - __builtin_offsetof (struct A, c) - 8)
     abort ();
+#endif
 }
 
 int
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
index 87381620cc9..9da3537a5f7 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-4.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
@@ -43,7 +43,12 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (q, 3) != 0)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3)
+      != (x < 0 ? sizeof (a.a) - 9 : sizeof (a.c) - 1))
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.a) - 9)
+#endif
     abort ();
   if (x < 6)
     r = &w[2].a[1];
@@ -55,31 +60,57 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&y.b, 3) != sizeof (a.b))
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3)
+      != (x < 6 ? sizeof (w[2].a) - 1 : sizeof (a.a) - 6))
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.a) - 6)
+#endif
     abort ();
   if (x < 20)
     r = malloc (30);
   else
     r = calloc (2, 16);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3) != (x < 20 ? 30 : 2 * 16))
+#else
   if (__builtin_object_size (r, 3) != 30)
+#endif
     abort ();
   if (x < 20)
     r = malloc (30);
   else
     r = calloc (2, 14);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3) != (x < 20 ? 30 : 2 * 14))
+#else
   if (__builtin_object_size (r, 3) != 2 * 14)
+#endif
     abort ();
   if (x < 30)
     r = malloc (sizeof (a));
   else
     r = &a.a[3];
+#ifdef __builtin_object_size
+  size_t objsz = x < 30 ? sizeof (a) : sizeof (a.a) - 3;
+  if (__builtin_object_size (r, 3) != objsz)
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.a) - 3)
+#endif
     abort ();
   r = memcpy (r, "a", 2);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3) != objsz)
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.a) - 3)
+#endif
     abort ();
   r = memcpy (r + 2, "b", 2) + 2;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3) != objsz - 4)
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.a) - 3 - 4)
+#endif
     abort ();
   r = &a.a[4];
   r = memset (r, 'a', 2);
@@ -184,6 +215,9 @@ test2 (void)
   struct B { char buf1[10]; char buf2[10]; } a;
   char *r, buf3[20];
   int i;
+#ifdef __builtin_object_size
+  size_t dyn_res = 0;
+#endif
 
   if (sizeof (a) != 20)
     return;
@@ -228,13 +262,38 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[2];
     }
+#ifdef __builtin_object_size
+  dyn_res = sizeof (buf3) - 1;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+        dyn_res = sizeof (a.buf1) - 6;
+      else if (i == l1)
+        dyn_res = sizeof (a.buf2) - 4;
+      else if (i == l1 + 1)
+        dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+        dyn_res = sizeof (a.buf1) - 2;
+    }
+  if (__builtin_object_size (r, 3) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.buf1) - 6)
     abort ();
+#endif
   r += 2;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3) != dyn_res - 2)
+    abort ();
+  if (__builtin_object_size (r + 1, 3) != dyn_res - 3)
+    abort ();
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.buf1) - 6 - 2)
     abort ();
   if (__builtin_object_size (r + 1, 3) != sizeof (a.buf1) - 6 - 3)
     abort ();
+#endif
 }
 
 void
@@ -352,7 +411,11 @@ test5 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 3) != sizeof (t.buf) - 8 - 4 * x)
+#else
   if (__builtin_object_size (p, 3) != 0)
+#endif
     abort ();
   memset (p, ' ', sizeof (t.buf) - 8 - 4 * 4);
 }
@@ -407,21 +470,36 @@ test8 (unsigned cond)
   else
     p = &buf2[4];
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (&p[-4], 3) != (cond ? 6 : 10))
+    abort ();
+#else
   if (__builtin_object_size (&p[-4], 3) != 6)
     abort ();
+#endif
 
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 3) != ((cond ? 2 : 6) + cond))
+    abort ();
+#else
   if (__builtin_object_size (p, 3) != 2)
     abort ();
+#endif
 
   p = &y.c[8];
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 3) != sizeof (y.c) - 8 + cond)
+    abort ();
+#else
   if (__builtin_object_size (p, 3) != sizeof (y.c) - 8)
     abort ();
+#endif
 }
 
 int
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-5.c b/gcc/testsuite/gcc.dg/builtin-object-size-5.c
index 8e63d9c7a5e..cb8f753b039 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-5.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-5.c
@@ -1,9 +1,13 @@
 /* { dg-do compile { target i?86-*-linux* i?86-*-gnu* x86_64-*-linux* } } */
 /* { dg-options "-O2" } */
 
+#ifndef N
+# define N 0x40000000
+#endif
+
 typedef __SIZE_TYPE__ size_t;
 extern void abort (void);
-extern char buf[0x40000000];
+extern char buf[N];
 
 void
 test1 (size_t x)
@@ -13,7 +17,11 @@ test1 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 0) != sizeof (buf) - 8 - 4 * x)
+#else
   if (__builtin_object_size (p, 0) != sizeof (buf) - 8)
+#endif
     abort ();
 }
 
@@ -25,7 +33,11 @@ test2 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 1) != sizeof (buf) - 8 - 4 * x)
+#else
   if (__builtin_object_size (p, 1) != sizeof (buf) - 8)
+#endif
     abort ();
 }
 
@@ -37,7 +49,11 @@ test3 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 2) != sizeof (buf) - 8 - 4 * x)
+#else
   if (__builtin_object_size (p, 2) != 0)
+#endif
     abort ();
 }
 
@@ -49,7 +65,11 @@ test4 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 3) != sizeof (buf) - 8 - 4 * x)
+#else
   if (__builtin_object_size (p, 3) != 0)
+#endif
     abort ();
 }
 
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index ee9ea1bfbfd..95cb44d9c7e 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -35,13 +35,14 @@ along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "attribs.h"
 #include "builtins.h"
+#include "gimplify-me.h"
 
 struct object_size_info
 {
   int object_size_type;
   unsigned char pass;
   bool changed;
-  bitmap visited, reexamine;
+  bitmap visited, reexamine, unknowns;
   unsigned int *depths;
   unsigned int *stack, *tos;
 };
@@ -74,7 +75,11 @@ static void check_for_plus_in_loops_1 (struct object_size_info *, tree,
    object_sizes[1] is upper bound for the object size and number of bytes till
    the end of the subobject (innermost array or field with address taken).
    object_sizes[2] is lower bound for the object size and number of bytes till
-   the end of the object and object_sizes[3] lower bound for subobject.  */
+   the end of the object and object_sizes[3] lower bound for subobject.
+
+   For static object sizes, the object size and the bytes till the end of the
+   object are both INTEGER_CST.  In the dynamic case, they are finally either a
+   gimple variable or an INTEGER_CST.  */
 static vec<object_size> object_sizes[OST_END];
 
 /* Bitmaps what object sizes have been computed already.  */
@@ -83,6 +88,15 @@ static bitmap computed[OST_END];
 /* Maximum value of offset we consider to be addition.  */
 static unsigned HOST_WIDE_INT offset_limit;
 
+/* Return true if VAL is represents an initial size for OBJECT_SIZE_TYPE.  */
+
+static inline bool
+size_initval_p (tree val, int object_size_type)
+{
+  return ((object_size_type & OST_MINIMUM)
+	  ? integer_all_onesp (val) : integer_zerop (val));
+}
+
 /* Return true if VAL is represents an unknown size for OBJECT_SIZE_TYPE.  */
 
 static inline bool
@@ -92,6 +106,15 @@ size_unknown_p (tree val, int object_size_type)
 	  ? integer_zerop (val) : integer_all_onesp (val));
 }
 
+/* Return true if VAL is usable as an object size in the object_sizes
+   vectors.  */
+
+static inline bool
+size_usable_p (tree val)
+{
+  return TREE_CODE (val) == SSA_NAME || TREE_CODE (val) == INTEGER_CST;
+}
+
 /* Return a tree with initial value for OBJECT_SIZE_TYPE.  */
 
 static inline tree
@@ -136,17 +159,43 @@ object_sizes_unknown_p (int object_size_type, unsigned varno)
 			 object_size_type);
 }
 
-/* Return size for VARNO corresponding to OSI.  If WHOLE is true, return the
-   whole object size.  */
+/* Return the raw size expression for VARNO corresponding to OSI.  This returns
+   the TREE_VEC as is and should only be used during gimplification.  */
+
+static inline object_size
+object_sizes_get_raw (struct object_size_info *osi, unsigned varno)
+{
+  gcc_assert (osi->pass != 0);
+  return object_sizes[osi->object_size_type][varno];
+}
+
+/* Return a size tree for VARNO corresponding to OSI.  If WHOLE is true, return
+   the whole object size.  Use this for building size expressions based on size
+   of VARNO.  */
 
 static inline tree
 object_sizes_get (struct object_size_info *osi, unsigned varno,
 		  bool whole = false)
 {
+  tree ret;
+  int object_size_type = osi->object_size_type;
+
   if (whole)
-    return object_sizes[osi->object_size_type][varno].wholesize;
+    ret = object_sizes[object_size_type][varno].wholesize;
   else
-    return object_sizes[osi->object_size_type][varno].size;
+    ret = object_sizes[object_size_type][varno].size;
+
+  if (object_size_type & OST_DYNAMIC)
+    {
+      if (TREE_CODE (ret) == MODIFY_EXPR)
+	return TREE_OPERAND (ret, 0);
+      else if (TREE_CODE (ret) == TREE_VEC)
+	return TREE_VEC_ELT (ret, TREE_VEC_LENGTH (ret) - 1);
+      else
+	gcc_checking_assert (size_usable_p (ret));
+    }
+
+  return ret;
 }
 
 /* Set size for VARNO corresponding to OSI to VAL.  */
@@ -161,28 +210,116 @@ object_sizes_initialize (struct object_size_info *osi, unsigned varno,
   object_sizes[object_size_type][varno].wholesize = wholeval;
 }
 
+/* Return a MODIFY_EXPR for cases where SSA and EXPR have the same type.  The
+   TREE_VEC is returned only in case of PHI nodes.  */
+
+static tree
+bundle_sizes (tree name, tree expr)
+{
+  gcc_checking_assert (TREE_TYPE (name) == sizetype);
+
+  if (TREE_CODE (expr) == TREE_VEC)
+    {
+      TREE_VEC_ELT (expr, TREE_VEC_LENGTH (expr) - 1) = name;
+      return expr;
+    }
+
+  gcc_checking_assert (types_compatible_p (TREE_TYPE (expr), sizetype));
+  return build2 (MODIFY_EXPR, sizetype, name, expr);
+}
+
 /* Set size for VARNO corresponding to OSI to VAL if it is the new minimum or
-   maximum.  */
+   maximum.  For static sizes, each element of TREE_VEC is always INTEGER_CST
+   throughout the computation.  For dynamic sizes, each element may either be a
+   gimple variable, a MODIFY_EXPR or a TREE_VEC.  The MODIFY_EXPR is for
+   expressions that need to be gimplified.  TREE_VECs are special, they're
+   emitted only for GIMPLE_PHI and the PHI result variable is the last element
+   of the vector.  */
 
-static inline bool
+static bool
 object_sizes_set (struct object_size_info *osi, unsigned varno, tree val,
 		  tree wholeval)
 {
   int object_size_type = osi->object_size_type;
   object_size osize = object_sizes[object_size_type][varno];
+  bool changed = true;
 
   tree oldval = osize.size;
   tree old_wholeval = osize.wholesize;
 
-  enum tree_code code = object_size_type & OST_MINIMUM ? MIN_EXPR : MAX_EXPR;
+  if (object_size_type & OST_DYNAMIC)
+    {
+      if (bitmap_bit_p (osi->reexamine, varno))
+	{
+	  if (size_unknown_p (val, object_size_type))
+	    {
+	      oldval = object_sizes_get (osi, varno);
+	      old_wholeval = object_sizes_get (osi, varno, true);
+	      bitmap_set_bit (osi->unknowns, SSA_NAME_VERSION (oldval));
+	      bitmap_set_bit (osi->unknowns, SSA_NAME_VERSION (old_wholeval));
+	      bitmap_clear_bit (osi->reexamine, varno);
+	    }
+	  else
+	    {
+	      val = bundle_sizes (oldval, val);
+	      wholeval = bundle_sizes (old_wholeval, wholeval);
+	    }
+	}
+      else
+	{
+	  gcc_checking_assert (size_initval_p (oldval, object_size_type));
+	  gcc_checking_assert (size_initval_p (old_wholeval,
+					       object_size_type));
+	  /* For dynamic object sizes, all object sizes that are not gimple
+	     variables will need to be gimplified.  */
+	  if (wholeval != val && !size_usable_p (wholeval))
+	    {
+	      bitmap_set_bit (osi->reexamine, varno);
+	      wholeval = bundle_sizes (make_ssa_name (sizetype), wholeval);
+	    }
+	  if (!size_usable_p (val))
+	    {
+	      bitmap_set_bit (osi->reexamine, varno);
+	      tree newval = bundle_sizes (make_ssa_name (sizetype), val);
+	      if (val == wholeval)
+		wholeval = newval;
+	      val = newval;
+	    }
+	  /* If the new value is a temporary variable, mark it for
+	     reexamination.  */
+	  else if (TREE_CODE (val) == SSA_NAME && !SSA_NAME_DEF_STMT (val))
+	    bitmap_set_bit (osi->reexamine, varno);
+	}
+    }
+  else
+    {
+      enum tree_code code = (object_size_type & OST_MINIMUM
+			     ? MIN_EXPR : MAX_EXPR);
 
-  val = size_binop (code, val, oldval);
-  wholeval = size_binop (code, wholeval, old_wholeval);
+      val = size_binop (code, val, oldval);
+      wholeval = size_binop (code, wholeval, old_wholeval);
+      changed = (tree_int_cst_compare (val, oldval) != 0
+		 || tree_int_cst_compare (old_wholeval, wholeval) != 0);
+    }
 
   object_sizes[object_size_type][varno].size = val;
   object_sizes[object_size_type][varno].wholesize = wholeval;
-  return (tree_int_cst_compare (oldval, val) != 0
-	  || tree_int_cst_compare (old_wholeval, wholeval) != 0);
+
+  return changed;
+}
+
+/* Set temporary SSA names for object size and whole size to resolve dependency
+   loops in dynamic size computation.  */
+
+static inline void
+object_sizes_set_temp (struct object_size_info *osi, unsigned varno)
+{
+  tree val = object_sizes_get (osi, varno);
+
+  if (size_initval_p (val, osi->object_size_type))
+    object_sizes_set (osi, varno,
+		      make_ssa_name (sizetype),
+		      make_ssa_name (sizetype));
 }
 
 /* Initialize OFFSET_LIMIT variable.  */
@@ -204,14 +341,15 @@ static tree
 size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE)
 {
   gcc_checking_assert (TREE_CODE (offset) == INTEGER_CST);
-  gcc_checking_assert (TREE_CODE (sz) == INTEGER_CST);
   gcc_checking_assert (types_compatible_p (TREE_TYPE (sz), sizetype));
 
   /* For negative offsets, if we have a distinct WHOLESIZE, use it to get a net
      offset from the whole object.  */
-  if (wholesize && tree_int_cst_compare (sz, wholesize))
+  if (wholesize && wholesize != sz
+      && (TREE_CODE (sz) != INTEGER_CST
+	  || TREE_CODE (wholesize) != INTEGER_CST
+	  || tree_int_cst_compare (sz, wholesize)))
     {
-      gcc_checking_assert (TREE_CODE (wholesize) == INTEGER_CST);
       gcc_checking_assert (types_compatible_p (TREE_TYPE (wholesize),
 					       sizetype));
 
@@ -228,13 +366,16 @@ size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE)
   if (!types_compatible_p (TREE_TYPE (offset), sizetype))
     fold_convert (sizetype, offset);
 
-  if (integer_zerop (offset))
-    return sz;
+  if (TREE_CODE (offset) == INTEGER_CST)
+    {
+      if (integer_zerop (offset))
+	return sz;
 
-  /* Negative or too large offset even after adjustment, cannot be within
-     bounds of an object.  */
-  if (compare_tree_int (offset, offset_limit) > 0)
-    return size_zero_node;
+      /* Negative or too large offset even after adjustment, cannot be within
+	 bounds of an object.  */
+      if (compare_tree_int (offset, offset_limit) > 0)
+	return size_zero_node;
+    }
 
   return size_binop (MINUS_EXPR, size_binop (MAX_EXPR, sz, offset), offset);
 }
@@ -671,6 +812,201 @@ pass_through_call (const gcall *call)
   return NULL_TREE;
 }
 
+/* Emit PHI nodes for size expressions fo.  */
+
+static void
+emit_phi_nodes (gimple *stmt, tree size, tree wholesize)
+{
+  tree phires;
+  gphi *wholephi = NULL;
+
+  if (wholesize != size)
+    {
+      phires = TREE_VEC_ELT (wholesize, TREE_VEC_LENGTH (wholesize) - 1);
+      wholephi = create_phi_node (phires, gimple_bb (stmt));
+    }
+
+  phires = TREE_VEC_ELT (size, TREE_VEC_LENGTH (size) - 1);
+  gphi *phi = create_phi_node (phires, gimple_bb (stmt));
+  gphi *obj_phi =  as_a <gphi *> (stmt);
+
+  gcc_checking_assert (TREE_CODE (wholesize) == TREE_VEC);
+  gcc_checking_assert (TREE_CODE (size) == TREE_VEC);
+
+  for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++)
+    {
+      gimple_seq seq = NULL;
+      tree wsz = TREE_VEC_ELT (wholesize, i);
+      tree sz = TREE_VEC_ELT (size, i);
+
+      /* If we built an expression, we will need to build statements
+	 and insert them on the edge right away.  */
+      if (TREE_CODE (wsz) != SSA_NAME)
+	wsz = force_gimple_operand (wsz, &seq, true, NULL);
+      if (TREE_CODE (sz) != SSA_NAME)
+	{
+	  gimple_seq s;
+	  sz = force_gimple_operand (sz, &s, true, NULL);
+	  gimple_seq_add_seq (&seq, s);
+	}
+
+      if (seq)
+	gsi_insert_seq_on_edge (gimple_phi_arg_edge (obj_phi, i), seq);
+
+      if (wholephi)
+	add_phi_arg (wholephi, wsz,
+		     gimple_phi_arg_edge (obj_phi, i),
+		     gimple_phi_arg_location (obj_phi, i));
+
+      add_phi_arg (phi, sz,
+		   gimple_phi_arg_edge (obj_phi, i),
+		   gimple_phi_arg_location (obj_phi, i));
+    }
+}
+
+/* Descend through EXPR and return size_unknown if it uses any SSA variable
+   object_size_set or object_size_set_temp generated, which turned out to be
+   size_unknown, as noted in UNKNOWNS.  */
+
+static tree
+propagate_unknowns (object_size_info *osi, tree expr)
+{
+  int object_size_type = osi->object_size_type;
+
+  switch (TREE_CODE (expr))
+    {
+    case SSA_NAME:
+      if (bitmap_bit_p (osi->unknowns, SSA_NAME_VERSION (expr)))
+	return size_unknown (object_size_type);
+      return expr;
+
+    case MIN_EXPR:
+    case MAX_EXPR:
+	{
+	  tree res = propagate_unknowns (osi, TREE_OPERAND (expr, 0));
+	  if (size_unknown_p (res, object_size_type))
+	    return res;
+
+	  res = propagate_unknowns (osi, TREE_OPERAND (expr, 1));
+	  if (size_unknown_p (res, object_size_type))
+	    return res;
+
+	  return expr;
+	}
+    case MODIFY_EXPR:
+	{
+	  tree res = propagate_unknowns (osi, TREE_OPERAND (expr, 1));
+	  if (size_unknown_p (res, object_size_type))
+	    return res;
+	  return expr;
+	}
+    case TREE_VEC:
+      for (int i = 0; i < TREE_VEC_LENGTH (expr); i++)
+	{
+	  tree res = propagate_unknowns (osi, TREE_VEC_ELT (expr, i));
+	  if (size_unknown_p (res, object_size_type))
+	    return res;
+	}
+      return expr;
+    case PLUS_EXPR:
+    case MINUS_EXPR:
+	{
+	  tree res = propagate_unknowns (osi, TREE_OPERAND (expr, 0));
+	  if (size_unknown_p (res, object_size_type))
+	    return res;
+
+	  return expr;
+	}
+    default:
+      return expr;
+    }
+}
+
+/* Walk through size expressions that need reexamination and generate
+   statements for them.  */
+
+static void
+gimplify_size_expressions (object_size_info *osi)
+{
+  int object_size_type = osi->object_size_type;
+  bitmap_iterator bi;
+  unsigned int i;
+  bool changed;
+
+  /* Step 1: Propagate unknowns into expressions.  */
+  bitmap reexamine = BITMAP_ALLOC (NULL);
+  bitmap_copy (reexamine, osi->reexamine);
+  do
+    {
+      changed = false;
+      EXECUTE_IF_SET_IN_BITMAP (reexamine, 0, i, bi)
+	{
+	  object_size cur = object_sizes_get_raw (osi, i);
+
+	  if (size_unknown_p (propagate_unknowns (osi, cur.size),
+			      object_size_type)
+	      || size_unknown_p (propagate_unknowns (osi, cur.wholesize),
+				 object_size_type))
+	    {
+	      object_sizes_set (osi, i,
+				size_unknown (object_size_type),
+				size_unknown (object_size_type));
+	      changed = true;
+	    }
+	}
+      bitmap_copy (reexamine, osi->reexamine);
+    }
+  while (changed);
+
+  /* Release all unknowns.  */
+  EXECUTE_IF_SET_IN_BITMAP (osi->unknowns, 0, i, bi)
+    release_ssa_name (ssa_name (i));
+
+  /* Expand all size expressions to put their definitions close to the objects
+     for whom size is being computed.  */
+  EXECUTE_IF_SET_IN_BITMAP (osi->reexamine, 0, i, bi)
+    {
+      gimple_seq seq = NULL;
+      object_size osize = object_sizes_get_raw (osi, i);
+
+      gimple *stmt = SSA_NAME_DEF_STMT (ssa_name (i));
+      enum gimple_code code = gimple_code (stmt);
+
+      /* PHI nodes need special attention.  */
+      if (code == GIMPLE_PHI)
+	emit_phi_nodes (stmt, osize.size, osize.wholesize);
+      else
+	{
+	  tree size_expr = NULL_TREE;
+
+	  /* Bundle wholesize in with the size to gimplify if needed.  */
+	  if (osize.wholesize != osize.size
+	      && !size_usable_p (osize.wholesize))
+	    size_expr = size_binop (COMPOUND_EXPR,
+				    osize.wholesize,
+				    osize.size);
+	  else if (!size_usable_p (osize.size))
+	    size_expr = osize.size;
+
+	  if (size_expr)
+	    {
+	      gimple_stmt_iterator gsi;
+	      if (code == GIMPLE_NOP)
+		gsi = gsi_start_bb (single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)));
+	      else
+		gsi = gsi_for_stmt (stmt);
+
+	      force_gimple_operand (size_expr, &seq, true, NULL);
+	      gsi_insert_seq_before (&gsi, seq, GSI_CONTINUE_LINKING);
+	    }
+	}
+
+      /* We're done, so replace the MODIFY_EXPRs with the SSA names.  */
+      object_sizes_initialize (osi, i,
+			       object_sizes_get (osi, i),
+			       object_sizes_get (osi, i, true));
+    }
+}
 
 /* Compute __builtin_object_size value for PTR and set *PSIZE to
    the resulting value.  If the declared object is known and PDECL
@@ -749,9 +1085,15 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 
       osi.visited = BITMAP_ALLOC (NULL);
       osi.reexamine = BITMAP_ALLOC (NULL);
-      osi.depths = NULL;
-      osi.stack = NULL;
-      osi.tos = NULL;
+
+      if (object_size_type & OST_DYNAMIC)
+	osi.unknowns = BITMAP_ALLOC (NULL);
+      else
+	{
+	  osi.depths = NULL;
+	  osi.stack = NULL;
+	  osi.tos = NULL;
+	}
 
       /* First pass: walk UD chains, compute object sizes that
 	 can be computed.  osi.reexamine bitmap at the end will
@@ -761,6 +1103,14 @@ compute_builtin_object_size (tree ptr, int object_size_type,
       osi.changed = false;
       collect_object_sizes_for (&osi, ptr);
 
+      if (object_size_type & OST_DYNAMIC)
+	{
+	  osi.pass = 1;
+	  gimplify_size_expressions (&osi);
+	  BITMAP_FREE (osi.unknowns);
+	  bitmap_clear (osi.reexamine);
+	}
+
       /* Second pass: keep recomputing object sizes of variables
 	 that need reexamination, until no object sizes are
 	 increased or all object sizes are computed.  */
@@ -1007,6 +1357,25 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   return reexamine;
 }
 
+static void
+dynamic_object_size (struct object_size_info *osi, tree var,
+		     tree *size, tree *wholesize)
+{
+  int object_size_type = osi->object_size_type;
+
+  if (TREE_CODE (var) == SSA_NAME)
+    {
+      unsigned varno = SSA_NAME_VERSION (var);
+
+      collect_object_sizes_for (osi, var);
+      *size = object_sizes_get (osi, varno);
+      *wholesize = object_sizes_get (osi, varno, true);
+    }
+  else if (TREE_CODE (var) == ADDR_EXPR)
+    addr_object_size (osi, var, object_size_type, size, wholesize);
+  else
+    *size = *wholesize = size_unknown (object_size_type);
+}
 
 /* Compute object_sizes for VAR, defined at STMT, which is
    a COND_EXPR.  Return true if the object size might need reexamination
@@ -1028,6 +1397,33 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   then_ = gimple_assign_rhs2 (stmt);
   else_ = gimple_assign_rhs3 (stmt);
 
+  if (object_size_type & OST_DYNAMIC)
+    {
+      tree then_size, then_wholesize, else_size, else_wholesize;
+
+      dynamic_object_size (osi, then_, &then_size, &then_wholesize);
+      if (!size_unknown_p (then_size, object_size_type))
+	dynamic_object_size (osi, else_, &else_size, &else_wholesize);
+
+      tree cond_size, cond_wholesize;
+      if (size_unknown_p (then_size, object_size_type)
+	  || size_unknown_p (else_size, object_size_type))
+	cond_size = cond_wholesize = size_unknown (object_size_type);
+      else
+	{
+	  cond_size = fold_build3 (COND_EXPR, sizetype,
+				   gimple_assign_rhs1 (stmt),
+				   then_size, else_size);
+	  cond_wholesize = fold_build3 (COND_EXPR, sizetype,
+					gimple_assign_rhs1 (stmt),
+					then_wholesize, else_wholesize);
+	}
+
+      object_sizes_set (osi, varno, cond_size, cond_wholesize);
+
+      return false;
+    }
+
   if (TREE_CODE (then_) == SSA_NAME)
     reexamine |= merge_object_sizes (osi, var, then_);
   else
@@ -1044,6 +1440,57 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   return reexamine;
 }
 
+/* Compute an object size expression for VAR, which is the result of a PHI
+   node.  */
+
+static void
+phi_dynamic_object_size (struct object_size_info *osi, tree var)
+{
+  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);
+  bool wholesize_needed = false;
+
+  /* The extra space is for the PHI result at the end, which object_sizes_set
+     sets for us.  */
+  tree sizes = make_tree_vec (num_args + 1);
+  tree wholesizes = make_tree_vec (num_args + 1);
+
+  /* Bail out if the size of any of the PHI arguments cannot be
+     determined.  */
+  for (i = 0; i < num_args; i++)
+    {
+      edge e = gimple_phi_arg_edge (as_a <gphi *> (stmt), i);
+      if (e->flags & EDGE_COMPLEX)
+	break;
+
+      tree rhs = gimple_phi_arg_def (stmt, i);
+      tree size, wholesize;
+
+      dynamic_object_size (osi, rhs, &size, &wholesize);
+
+      if (size_unknown_p (size, object_size_type))
+       break;
+
+      if (size != wholesize)
+	wholesize_needed = true;
+
+      TREE_VEC_ELT (sizes, i) = size;
+      TREE_VEC_ELT (wholesizes, i) = wholesize;
+    }
+
+  if (i < num_args)
+    sizes = wholesizes = size_unknown (object_size_type);
+
+  /* Point to the same TREE_VEC so that we can avoid emitting two PHI
+     nodes.  */
+  if (!wholesize_needed)
+    wholesizes = sizes;
+
+  object_sizes_set (osi, varno, sizes, wholesizes);
+}
+
 /* Compute object sizes for VAR.
    For ADDR_EXPR an object size is the number of remaining bytes
    to the end of the object (where what is considered an object depends on
@@ -1089,6 +1536,9 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 	{
 	  /* Found a dependency loop.  Mark the variable for later
 	     re-examination.  */
+	  if (object_size_type & OST_DYNAMIC)
+	    object_sizes_set_temp (osi, varno);
+
 	  bitmap_set_bit (osi->reexamine, varno);
 	  if (dump_file && (dump_flags & TDF_DETAILS))
 	    {
@@ -1170,6 +1620,12 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
       {
 	unsigned i;
 
+	if (object_size_type & OST_DYNAMIC)
+	  {
+	    phi_dynamic_object_size (osi, var);
+	    break;
+	  }
+
 	for (i = 0; i < gimple_phi_num_args (stmt); i++)
 	  {
 	    tree rhs = gimple_phi_arg (stmt, i)->def;
@@ -1192,7 +1648,8 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
   if (! reexamine || object_sizes_unknown_p (object_size_type, varno))
     {
       bitmap_set_bit (computed[object_size_type], varno);
-      bitmap_clear_bit (osi->reexamine, varno);
+      if (!(object_size_type & OST_DYNAMIC))
+	bitmap_clear_bit (osi->reexamine, varno);
     }
   else
     {
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v5 2/4] tree-object-size: Handle function parameters
  2021-12-18 12:35 ` [PATCH v5 0/4] __builtin_dynamic_object_size Siddhesh Poyarekar
  2021-12-18 12:35   ` [PATCH v5 1/4] tree-object-size: Support dynamic sizes in conditions Siddhesh Poyarekar
@ 2021-12-18 12:35   ` Siddhesh Poyarekar
  2022-01-10 10:50     ` Jakub Jelinek
  2021-12-18 12:35   ` [PATCH v5 3/4] tree-object-size: Handle GIMPLE_CALL Siddhesh Poyarekar
                     ` (2 subsequent siblings)
  4 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-12-18 12:35 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Handle hints provided by __attribute__ ((access (...))) to compute
dynamic sizes for objects.

gcc/ChangeLog:

	* tree-object-size.c: Include tree-dfa.h.
	(parm_object_size): New function.
	(collect_object_sizes_for): Call it.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-dynamic-object-size-0.c (test_parmsz_simple):
	New function.
	(main): Call it.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 .../gcc.dg/builtin-dynamic-object-size-0.c    | 11 ++++
 gcc/tree-object-size.c                        | 50 ++++++++++++++++++-
 2 files changed, 60 insertions(+), 1 deletion(-)

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 81588cb28a6..7616ffa4cf0 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -51,6 +51,14 @@ test_deploop (size_t sz, size_t cond)
   return __builtin_dynamic_object_size (bin, 0);
 }
 
+size_t
+__attribute__ ((access (__read_write__, 1, 2)))
+__attribute__ ((noinline))
+test_parmsz_simple (void *obj, size_t sz)
+{
+  return __builtin_dynamic_object_size (obj, 0);
+}
+
 unsigned nfails = 0;
 
 #define FAIL() ({ \
@@ -69,6 +77,9 @@ main (int argc, char **argv)
     FAIL ();
   if (test_deploop (128, 129) != 32)
     FAIL ();
+  if (test_parmsz_simple (argv[0], __builtin_strlen (argv[0]) + 1)
+      != __builtin_strlen (argv[0]) + 1)
+    FAIL ();
 
   if (nfails > 0)
     __builtin_abort ();
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index 95cb44d9c7e..bf33ac93b93 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-fold.h"
 #include "gimple-iterator.h"
 #include "tree-cfg.h"
+#include "tree-dfa.h"
 #include "stringpool.h"
 #include "attribs.h"
 #include "builtins.h"
@@ -1440,6 +1441,53 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   return reexamine;
 }
 
+/* Find size of an object passed as a parameter to the function.  */
+
+static void
+parm_object_size (struct object_size_info *osi, tree var)
+{
+  int object_size_type = osi->object_size_type;
+  tree parm = SSA_NAME_VAR (var);
+
+  if (!(object_size_type & OST_DYNAMIC) || !POINTER_TYPE_P (TREE_TYPE (parm)))
+    expr_object_size (osi, var, parm);
+
+  /* Look for access attribute.  */
+  rdwr_map rdwr_idx;
+
+  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)
+    {
+      tree fnargs = DECL_ARGUMENTS (fndecl);
+      tree arg = NULL_TREE;
+      unsigned argpos = 0;
+
+      /* Walk through the parameters to pick the size parameter and safely
+	 scale it by the type size.  */
+      for (arg = fnargs; argpos != access->sizarg && arg;
+	   arg = TREE_CHAIN (arg), ++argpos);
+
+      if (arg != NULL_TREE && INTEGRAL_TYPE_P (TREE_TYPE (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);
+	    }
+	}
+    }
+  if (!sz)
+    sz = size_unknown (object_size_type);
+
+  object_sizes_set (osi, SSA_NAME_VERSION (var), sz, sz);
+}
+
 /* Compute an object size expression for VAR, which is the result of a PHI
    node.  */
 
@@ -1610,7 +1658,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
     case GIMPLE_NOP:
       if (SSA_NAME_VAR (var)
 	  && TREE_CODE (SSA_NAME_VAR (var)) == PARM_DECL)
-	expr_object_size (osi, var, SSA_NAME_VAR (var));
+	parm_object_size (osi, var);
       else
 	/* Uninitialized SSA names point nowhere.  */
 	unknown_object_size (osi, var);
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v5 3/4] tree-object-size: Handle GIMPLE_CALL
  2021-12-18 12:35 ` [PATCH v5 0/4] __builtin_dynamic_object_size Siddhesh Poyarekar
  2021-12-18 12:35   ` [PATCH v5 1/4] tree-object-size: Support dynamic sizes in conditions Siddhesh Poyarekar
  2021-12-18 12:35   ` [PATCH v5 2/4] tree-object-size: Handle function parameters Siddhesh Poyarekar
@ 2021-12-18 12:35   ` Siddhesh Poyarekar
  2022-01-10 11:03     ` Jakub Jelinek
  2021-12-18 12:35   ` [PATCH v5 4/4] tree-object-size: Dynamic sizes for ADDR_EXPR Siddhesh Poyarekar
  2022-01-04  3:24   ` [PING][PATCH v5 0/4] __builtin_dynamic_object_size Siddhesh Poyarekar
  4 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-12-18 12:35 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Handle non-constant expressions in GIMPLE_CALL arguments.  Also handle
alloca.

gcc/ChangeLog:

	* tree-object-size.c (alloc_object_size): Make and return
	non-constant size expression.
	(call_object_size): Return expression or unknown based on
	whether dynamic object size is requested.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-dynamic-object-size-0.c: Add new tests.
	* gcc.dg/builtin-object-size-1.c (test1)
	[__builtin_object_size]: Alter expected result for dynamic
	object size.
	* gcc.dg/builtin-object-size-2.c (test1)
	[__builtin_object_size]: Likewise.
	* gcc.dg/builtin-object-size-3.c (test1)
	[__builtin_object_size]: Likewise.
	* gcc.dg/builtin-object-size-4.c (test1)
	[__builtin_object_size]: Likewise.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
Changes since v4:
- Free allocations in tests.

 .../gcc.dg/builtin-dynamic-object-size-0.c    | 251 +++++++++++++++++-
 gcc/testsuite/gcc.dg/builtin-object-size-1.c  |   7 +
 gcc/testsuite/gcc.dg/builtin-object-size-2.c  |  14 +
 gcc/testsuite/gcc.dg/builtin-object-size-3.c  |   7 +
 gcc/testsuite/gcc.dg/builtin-object-size-4.c  |  14 +
 gcc/tree-object-size.c                        |  22 +-
 6 files changed, 307 insertions(+), 8 deletions(-)

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 7616ffa4cf0..d67cf4a3c07 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -4,6 +4,73 @@
 typedef __SIZE_TYPE__ size_t;
 #define abort __builtin_abort
 
+void *
+__attribute__ ((alloc_size (1)))
+__attribute__ ((__nothrow__ , __leaf__))
+__attribute__ ((noinline))
+alloc_func (size_t sz)
+{
+  return __builtin_malloc (sz);
+}
+
+void *
+__attribute__ ((alloc_size (1, 2)))
+__attribute__ ((__nothrow__ , __leaf__))
+__attribute__ ((noinline))
+calloc_func (size_t cnt, size_t sz)
+{
+  return __builtin_calloc (cnt, sz);
+}
+
+void *
+__attribute__ ((noinline))
+unknown_allocator (size_t cnt, size_t sz)
+{
+  return __builtin_calloc (cnt, sz);
+}
+
+size_t
+__attribute__ ((noinline))
+test_unknown (size_t cnt, size_t sz)
+{
+  void *ch = unknown_allocator (cnt, sz);
+  size_t ret = __builtin_dynamic_object_size (ch, 0);
+  __builtin_free (ch);
+  return ret;
+}
+
+/* Malloc-like allocator.  */
+
+size_t
+__attribute__ ((noinline))
+test_malloc (size_t sz)
+{
+  void *ch = alloc_func (sz);
+  size_t ret = __builtin_dynamic_object_size (ch, 0);
+  __builtin_free (ch);
+  return ret;
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc (size_t sz)
+{
+  void *ch = __builtin_malloc (sz);
+  size_t ret = __builtin_dynamic_object_size (ch, 0);
+  __builtin_free (ch);
+  return ret;
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_cond (int cond)
+{
+  void *ch = __builtin_malloc (cond ? 32 : 64);
+  size_t ret = __builtin_dynamic_object_size (ch, 0);
+  __builtin_free (ch);
+  return ret;
+}
+
 size_t
 __attribute__ ((noinline))
 test_builtin_malloc_condphi (int cond)
@@ -21,6 +88,95 @@ test_builtin_malloc_condphi (int cond)
   return ret;
 }
 
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi2 (int cond, size_t in)
+{
+  void *ch;
+
+  if (cond)
+    ch = __builtin_malloc (in);
+  else
+    ch = __builtin_malloc (64);
+
+  size_t ret = __builtin_dynamic_object_size (ch, 0);
+
+  __builtin_free (ch);
+  return ret;
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi3 (int cond, size_t in, size_t in2)
+{
+  void *ch;
+
+  if (cond)
+    ch = __builtin_malloc (in);
+  else
+    ch = __builtin_malloc (in2);
+
+  size_t ret = __builtin_dynamic_object_size (ch, 0);
+
+  __builtin_free (ch);
+  return ret;
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi4 (size_t sz, int cond)
+{
+  char *a = __builtin_malloc (sz);
+  char b[sz / 2];
+
+  size_t ret = __builtin_dynamic_object_size (cond ? b : (void *) &a, 0);
+  __builtin_free (a);
+  return ret;
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi5 (size_t sz, int cond, char *c)
+{
+  char *a = __builtin_malloc (sz);
+
+  size_t ret = __builtin_dynamic_object_size (cond ? c : (void *) &a, 0);
+  __builtin_free (a);
+  return ret;
+}
+
+/* Calloc-like allocator.  */
+
+size_t
+__attribute__ ((noinline))
+test_calloc (size_t cnt, size_t sz)
+{
+  void *ch = calloc_func (cnt, sz);
+  size_t ret = __builtin_dynamic_object_size (ch, 0);
+  __builtin_free (ch);
+  return ret;
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_calloc (size_t cnt, size_t sz)
+{
+  void *ch = __builtin_calloc (cnt, sz);
+  size_t ret = __builtin_dynamic_object_size (ch, 0);
+  __builtin_free (ch);
+  return ret;
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_calloc_cond (int cond1, int cond2)
+{
+  void *ch = __builtin_calloc (cond1 ? 32 : 64, cond2 ? 1024 : 16);
+  size_t ret = __builtin_dynamic_object_size (ch, 0);
+  __builtin_free (ch);
+  return ret;
+}
+
 size_t
 __attribute__ ((noinline))
 test_builtin_calloc_condphi (size_t cnt, size_t sz, int cond)
@@ -38,6 +194,49 @@ test_builtin_calloc_condphi (size_t cnt, size_t sz, int cond)
   return ret;
 }
 
+/* Passthrough functions.  */
+
+size_t
+__attribute__ ((noinline))
+test_passthrough (size_t sz, char *in)
+{
+  char *bin = __builtin_malloc (sz);
+  char *dest = __builtin_memcpy (bin, in, sz);
+
+  size_t ret = __builtin_dynamic_object_size (dest, 0);
+  __builtin_free (bin);
+  return ret;
+}
+
+size_t
+__attribute__ ((noinline))
+test_passthrough_nonssa (char *in)
+{
+  char bin[__builtin_strlen (in) + 1];
+  char *dest = __builtin_memcpy (bin, in, __builtin_strlen (in) + 1);
+
+  return __builtin_dynamic_object_size (dest, 0);
+}
+
+/* Variable length arrays.  */
+size_t
+__attribute__ ((noinline))
+test_dynarray (size_t sz)
+{
+  char bin[sz];
+
+  return __builtin_dynamic_object_size (bin, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_dynarray_cond (int cond)
+{
+  char bin[cond ? 8 : 16];
+
+  return __builtin_dynamic_object_size (bin, 0);
+}
+
 size_t
 __attribute__ ((noinline))
 test_deploop (size_t sz, size_t cond)
@@ -46,7 +245,7 @@ test_deploop (size_t sz, size_t cond)
 
   for (size_t i = 0; i < sz; i++)
     if (i == cond)
-      bin = __builtin_alloca (64);
+      bin = __builtin_alloca (sz);
 
   return __builtin_dynamic_object_size (bin, 0);
 }
@@ -69,12 +268,62 @@ unsigned nfails = 0;
 int
 main (int argc, char **argv)
 {
+  size_t outsz = test_unknown (32, 42);
+  if (outsz != -1 && outsz != 32)
+    FAIL ();
+  if (test_malloc (2048) != 2048)
+    FAIL ();
+  if (test_builtin_malloc (2048) != 2048)
+    FAIL ();
+  if (test_builtin_malloc_cond (1) != 32)
+    FAIL ();
+  if (test_builtin_malloc_cond (0) != 64)
+    FAIL ();
   if (test_builtin_malloc_condphi (1) != 32)
     FAIL ();
   if (test_builtin_malloc_condphi (0) != 64)
     FAIL ();
+  if (test_builtin_malloc_condphi2 (1, 128) != 128)
+    FAIL ();
+  if (test_builtin_malloc_condphi2 (0, 128) != 64)
+    FAIL ();
+  if (test_builtin_malloc_condphi3 (1, 128, 256) != 128)
+    FAIL ();
+  if (test_builtin_malloc_condphi3 (0, 128, 256) != 256)
+    FAIL ();
+  if (test_builtin_malloc_condphi4 (128, 1) != 64)
+    FAIL ();
+  if (test_builtin_malloc_condphi4 (128, 0) != sizeof (void *))
+    FAIL ();
+  if (test_builtin_malloc_condphi5 (128, 0, argv[0]) != -1)
+    FAIL ();
+  if (test_calloc (2048, 4) != 2048 * 4)
+    FAIL ();
+  if (test_builtin_calloc (2048, 8) != 2048 * 8)
+    FAIL ();
+  if (test_builtin_calloc_cond (0, 0) != 64 * 16)
+    FAIL ();
+  if (test_builtin_calloc_cond (1, 1) != 32 * 1024)
+    FAIL ();
+  if (test_builtin_calloc_condphi (128, 1, 1) != 128)
+    FAIL ();
   if (test_builtin_calloc_condphi (128, 1, 0) == 128)
     FAIL ();
+  if (test_builtin_calloc_condphi (128, 1, 0) == -1)
+    FAIL ();
+  if (test_passthrough (__builtin_strlen (argv[0]) + 1, argv[0])
+      != __builtin_strlen (argv[0]) + 1)
+    FAIL ();
+  if (test_passthrough_nonssa (argv[0]) != __builtin_strlen (argv[0]) + 1)
+    FAIL ();
+  if (test_dynarray (__builtin_strlen (argv[0])) != __builtin_strlen (argv[0]))
+    FAIL ();
+  if (test_dynarray_cond (0) != 16)
+    FAIL ();
+  if (test_dynarray_cond (1) != 8)
+    FAIL ();
+  if (test_deploop (128, 4) != 128)
+    FAIL ();
   if (test_deploop (128, 129) != 32)
     FAIL ();
   if (test_parmsz_simple (argv[0], __builtin_strlen (argv[0]) + 1)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
index 265c87ed6fb..06d442796cb 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
@@ -135,10 +135,17 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&extb[5], 0) != sizeof (extb) - 5)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (var, 0) != x + 10)
+    abort ();
+  if (__builtin_object_size (var + 10, 0) != x)
+    abort ();
+#else
   if (__builtin_object_size (var, 0) != (size_t) -1)
     abort ();
   if (__builtin_object_size (var + 10, 0) != (size_t) -1)
     abort ();
+#endif
   if (__builtin_object_size (&var[5], 0) != (size_t) -1)
     abort ();
   if (__builtin_object_size (zerol, 0) != 0)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
index 5051fea47c3..2364f2d6afd 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-2.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
@@ -137,16 +137,30 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&extc[5].c[3], 1) != (size_t) -1)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (var, 1) != x + 10)
+    abort ();
+  if (__builtin_object_size (var + 10, 1) != x)
+    abort ();
+#else
   if (__builtin_object_size (var, 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (var + 10, 1) != (size_t) -1)
     abort ();
+#endif
   if (__builtin_object_size (&var[5], 1) != (size_t) -1)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (vara, 1) != (x + 10) * sizeof (struct A))
+    abort ();
+  if (__builtin_object_size (vara + 10, 1) != x * sizeof (struct A))
+    abort ();    
+#else
   if (__builtin_object_size (vara, 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (vara + 10, 1) != (size_t) -1)
     abort ();    
+#endif
   if (__builtin_object_size (&vara[5], 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (&vara[0].a, 1) != sizeof (vara[0].a))
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
index 1d92627266b..753ee4a1a4f 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-3.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
@@ -140,10 +140,17 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&extb[5], 2) != sizeof (extb) - 5)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (var, 2) != x + 10)
+    abort ();
+  if (__builtin_object_size (var + 10, 2) != x)
+    abort ();
+#else
   if (__builtin_object_size (var, 2) != 0)
     abort ();
   if (__builtin_object_size (var + 10, 2) != 0)
     abort ();
+#endif
   if (__builtin_object_size (&var[5], 2) != 0)
     abort ();
   if (__builtin_object_size (zerol, 2) != 0)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
index 9da3537a5f7..c383385e060 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-4.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
@@ -150,16 +150,30 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&extc[5].c[3], 3) != 0)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (var, 3) != x + 10)
+    abort ();
+  if (__builtin_object_size (var + 10, 3) != x)
+    abort ();
+#else
   if (__builtin_object_size (var, 3) != 0)
     abort ();
   if (__builtin_object_size (var + 10, 3) != 0)
     abort ();
+#endif
   if (__builtin_object_size (&var[5], 3) != 0)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (vara, 3) != (x + 10) * sizeof (struct A))
+    abort ();
+  if (__builtin_object_size (vara + 10, 3) != x * sizeof (struct A))
+    abort ();    
+#else
   if (__builtin_object_size (vara, 3) != 0)
     abort ();
   if (__builtin_object_size (vara + 10, 3) != 0)
     abort ();    
+#endif
   if (__builtin_object_size (&vara[5], 3) != 0)
     abort ();
   if (__builtin_object_size (&vara[0].a, 3) != sizeof (vara[0].a))
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index bf33ac93b93..e17ef294d0c 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -751,7 +751,8 @@ alloc_object_size (const gcall *call, int object_size_type)
   gcc_assert (is_gimple_call (call));
 
   tree calltype;
-  if (tree callfn = gimple_call_fndecl (call))
+  tree callfn = gimple_call_fndecl (call);
+  if (callfn)
     calltype = TREE_TYPE (callfn);
   else
     calltype = gimple_call_fntype (call);
@@ -771,12 +772,13 @@ alloc_object_size (const gcall *call, int object_size_type)
       if (TREE_CHAIN (p))
         arg2 = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (p)))-1;
     }
+  else if (gimple_call_builtin_p (call, BUILT_IN_NORMAL)
+	   && callfn && ALLOCA_FUNCTION_CODE_P (DECL_FUNCTION_CODE (callfn)))
+  arg1 = 0;
 
-  if (arg1 < 0 || arg1 >= (int)gimple_call_num_args (call)
-      || TREE_CODE (gimple_call_arg (call, arg1)) != INTEGER_CST
-      || (arg2 >= 0
-	  && (arg2 >= (int)gimple_call_num_args (call)
-	      || TREE_CODE (gimple_call_arg (call, arg2)) != INTEGER_CST)))
+  /* Non-const arguments are OK here, let the caller handle constness.  */
+  if (arg1 < 0 || arg1 >= (int) gimple_call_num_args (call)
+      || arg2 >= (int) gimple_call_num_args (call))
     return size_unknown (object_size_type);
 
   tree bytes = NULL_TREE;
@@ -787,7 +789,10 @@ alloc_object_size (const gcall *call, int object_size_type)
   else if (arg1 >= 0)
     bytes = fold_convert (sizetype, gimple_call_arg (call, arg1));
 
-  return bytes;
+  if (bytes)
+    return STRIP_NOPS (bytes);
+
+  return size_unknown (object_size_type);
 }
 
 
@@ -1242,6 +1247,9 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call)
   gcc_assert (osi->pass == 0);
   tree bytes = alloc_object_size (call, object_size_type);
 
+  if (!(object_size_type & OST_DYNAMIC) && TREE_CODE (bytes) != INTEGER_CST)
+    bytes = size_unknown (object_size_type);
+
   object_sizes_set (osi, varno, bytes, bytes);
 }
 
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v5 4/4] tree-object-size: Dynamic sizes for ADDR_EXPR
  2021-12-18 12:35 ` [PATCH v5 0/4] __builtin_dynamic_object_size Siddhesh Poyarekar
                     ` (2 preceding siblings ...)
  2021-12-18 12:35   ` [PATCH v5 3/4] tree-object-size: Handle GIMPLE_CALL Siddhesh Poyarekar
@ 2021-12-18 12:35   ` Siddhesh Poyarekar
  2022-01-10 11:09     ` Jakub Jelinek
  2022-01-04  3:24   ` [PING][PATCH v5 0/4] __builtin_dynamic_object_size Siddhesh Poyarekar
  4 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2021-12-18 12:35 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Allow returning dynamic expressions from ADDR_EXPR for
__builtin_dynamic_object_size and also allow offsets to be dynamic.

gcc/ChangeLog:

	* tree-object-size.c (size_valid_p): New function.
	(size_for_offset): Remove OFFSET constness assertion.
	(addr_object_size): Build dynamic expressions for object
	sizes and use size_valid_p to decide if it is valid for the
	given OBJECT_SIZE_TYPE.
	(compute_builtin_object_size): Allow dynamic offsets when
	computing size at O0.
	(call_object_size): Call size_valid_p.
	(plus_stmt_object_size): Allow non-constant offset and use
	size_valid_p to decide if it is valid for the given
	OBJECT_SIZE_TYPE.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-dynamic-object-size-0.c: Add new tests.
	* gcc.dg/builtin-object-size-1.c (test1)
	[__builtin_object_size]: Adjust expected output for dynamic
	object sizes.
	* gcc.dg/builtin-object-size-2.c (test1)
	[__builtin_object_size]: Likewise.
	* gcc.dg/builtin-object-size-3.c (test1)
	[__builtin_object_size]: Likewise.
	* gcc.dg/builtin-object-size-4.c (test1)
	[__builtin_object_size]: Likewise.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 .../gcc.dg/builtin-dynamic-object-size-0.c    | 158 ++++++++++++++++++
 gcc/testsuite/gcc.dg/builtin-object-size-1.c  |  30 +++-
 gcc/testsuite/gcc.dg/builtin-object-size-2.c  |  43 ++++-
 gcc/testsuite/gcc.dg/builtin-object-size-3.c  |  25 ++-
 gcc/testsuite/gcc.dg/builtin-object-size-4.c  |  17 +-
 gcc/tree-object-size.c                        |  91 +++++-----
 6 files changed, 300 insertions(+), 64 deletions(-)

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 d67cf4a3c07..ba710f47f1c 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -250,6 +250,79 @@ test_deploop (size_t sz, size_t cond)
   return __builtin_dynamic_object_size (bin, 0);
 }
 
+/* Address expressions.  */
+
+struct dynarray_struct
+{
+  long a;
+  char c[16];
+  int b;
+};
+
+size_t
+__attribute__ ((noinline))
+test_dynarray_struct (size_t sz, size_t off)
+{
+  struct dynarray_struct bin[sz];
+
+  return __builtin_dynamic_object_size (&bin[off].c, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_dynarray_struct_subobj (size_t sz, size_t off)
+{
+  struct dynarray_struct bin[sz];
+
+  return __builtin_dynamic_object_size (&bin[off].c[4], 1);
+}
+
+size_t
+__attribute__ ((noinline))
+test_dynarray_struct_subobj2 (size_t sz, size_t off, size_t *objsz)
+{
+  struct dynarray_struct2
+    {
+      long a;
+      int b;
+      char c[sz];
+    };
+
+  struct dynarray_struct2 bin;
+
+  *objsz = sizeof (bin);
+
+  return __builtin_dynamic_object_size (&bin.c[off], 1);
+}
+
+size_t
+__attribute__ ((noinline))
+test_substring (size_t sz, size_t off)
+{
+  char str[sz];
+
+  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)))
 __attribute__ ((noinline))
@@ -258,6 +331,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() ({ \
@@ -318,6 +425,31 @@ main (int argc, char **argv)
     FAIL ();
   if (test_dynarray (__builtin_strlen (argv[0])) != __builtin_strlen (argv[0]))
     FAIL ();
+  if (test_dynarray_struct (42, 4) !=
+      ((42 - 4) * sizeof (struct dynarray_struct)
+       - __builtin_offsetof (struct dynarray_struct, c)))
+    FAIL ();
+  if (test_dynarray_struct (42, 48) != 0)
+    FAIL ();
+  if (test_substring (128, 4) != 128 - 4)
+    FAIL ();
+  if (test_substring (128, 142) != 0)
+    FAIL ();
+  if (test_dynarray_struct_subobj (42, 4) != 16 - 4)
+    FAIL ();
+  if (test_dynarray_struct_subobj (42, 48) != 0)
+    FAIL ();
+  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)
@@ -329,6 +461,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/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
index 06d442796cb..161f426ec0b 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
@@ -81,30 +81,56 @@ test1 (void *q, int x)
     r = malloc (30);
   else
     r = calloc (2, 16);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0) != (x < 20 ? 30 : 2 * 16))
+    abort ();
+#else
   /* We may duplicate this test onto the two exit paths.  On one path
      the size will be 32, the other it will be 30.  If we don't duplicate
      this test, then the size will be 32.  */
   if (__builtin_object_size (r, 0) != 2 * 16
       && __builtin_object_size (r, 0) != 30)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
     r = calloc (2, 14);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0) != (x < 20 ? 30 : 2 * 14))
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != 30)
     abort ();
+#endif
   if (x < 30)
     r = malloc (sizeof (a));
   else
     r = &a.a[3];
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0) != (x < 30 ? sizeof (a) : sizeof (a) - 3))
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != sizeof (a))
     abort ();
+#endif
   r = memcpy (r, "a", 2);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0) != (x < 30 ? sizeof (a) : sizeof (a) - 3))
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != sizeof (a))
     abort ();
+#endif
   r = memcpy (r + 2, "b", 2) + 2;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0)
+      != (x < 30 ? sizeof (a) - 4 : sizeof (a) - 7))
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != sizeof (a) - 4)
     abort ();
+#endif
   r = &a.a[4];
   r = memset (r, 'a', 2);
   if (__builtin_object_size (r, 0)
@@ -140,14 +166,16 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (var + 10, 0) != x)
     abort ();
+  if (__builtin_object_size (&var[5], 0) != x + 5)
+    abort ();
 #else
   if (__builtin_object_size (var, 0) != (size_t) -1)
     abort ();
   if (__builtin_object_size (var + 10, 0) != (size_t) -1)
     abort ();
-#endif
   if (__builtin_object_size (&var[5], 0) != (size_t) -1)
     abort ();
+#endif
   if (__builtin_object_size (zerol, 0) != 0)
     abort ();
   if (__builtin_object_size (&zerol, 0) != 0)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
index 2364f2d6afd..2729538da17 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-2.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
@@ -75,30 +75,56 @@ test1 (void *q, int x)
     r = malloc (30);
   else
     r = calloc (2, 16);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 1) != (x < 20 ? 30 : 2 * 16))
+    abort ();
+#else
   /* We may duplicate this test onto the two exit paths.  On one path
      the size will be 32, the other it will be 30.  If we don't duplicate
      this test, then the size will be 32.  */
   if (__builtin_object_size (r, 1) != 2 * 16
       && __builtin_object_size (r, 1) != 30)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
     r = calloc (2, 14);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 1) != (x < 20 ? 30 : 2 * 14))
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != 30)
     abort ();
+#endif
   if (x < 30)
     r = malloc (sizeof (a));
   else
     r = &a.a[3];
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 1) != (x < 30 ? sizeof (a) : sizeof (a) - 3))
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a))
     abort ();
+#endif
   r = memcpy (r, "a", 2);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 1) != (x < 30 ? sizeof (a) : sizeof (a) - 3))
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a))
     abort ();
+#endif
   r = memcpy (r + 2, "b", 2) + 2;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0)
+      != (x < 30 ? sizeof (a) - 4 : sizeof (a) - 7))
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a) - 4)
     abort ();
+#endif
   r = &a.a[4];
   r = memset (r, 'a', 2);
   if (__builtin_object_size (r, 1) != sizeof (a.a) - 4)
@@ -142,27 +168,28 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (var + 10, 1) != x)
     abort ();
+  if (__builtin_object_size (&var[5], 1) != x + 5)
+    abort ();
+  if (__builtin_object_size (vara, 1) != (x + 10) * sizeof (struct A))
+    abort ();
+  if (__builtin_object_size (vara + 10, 1) != x * sizeof (struct A))
+    abort ();    
+  if (__builtin_object_size (&vara[5], 1) != (x + 5) * sizeof (struct A))
+    abort ();
 #else
   if (__builtin_object_size (var, 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (var + 10, 1) != (size_t) -1)
     abort ();
-#endif
   if (__builtin_object_size (&var[5], 1) != (size_t) -1)
     abort ();
-#ifdef __builtin_object_size
-  if (__builtin_object_size (vara, 1) != (x + 10) * sizeof (struct A))
-    abort ();
-  if (__builtin_object_size (vara + 10, 1) != x * sizeof (struct A))
-    abort ();    
-#else
   if (__builtin_object_size (vara, 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (vara + 10, 1) != (size_t) -1)
     abort ();    
-#endif
   if (__builtin_object_size (&vara[5], 1) != (size_t) -1)
     abort ();
+#endif
   if (__builtin_object_size (&vara[0].a, 1) != sizeof (vara[0].a))
     abort ();
   if (__builtin_object_size (&vara[10].a[0], 1) != sizeof (vara[0].a))
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
index 753ee4a1a4f..db31171a8bd 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-3.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
@@ -42,9 +42,17 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (q, 2) != 0)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 2)
+      != (x < 0
+	  ? sizeof (a) - __builtin_offsetof (struct A, a) - 9
+	  : sizeof (a) - __builtin_offsetof (struct A, c) - 1))
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, c) - 1)
     abort ();
+#endif
   if (x < 6)
     r = &w[2].a[1];
   else
@@ -58,15 +66,28 @@ test1 (void *q, int x)
   if (__builtin_object_size (&y.b, 2)
       != sizeof (a) - __builtin_offsetof (struct A, b))
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 2)
+      != (x < 6
+	  ? 2 * sizeof (w[0]) - __builtin_offsetof (struct A, a) - 1
+	  : sizeof (a) - __builtin_offsetof (struct A, a) - 6))
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, a) - 6)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
     r = calloc (2, 16);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 2) != (x < 20 ? 30 : 2 * 16))
+    abort ();
+#else
   if (__builtin_object_size (r, 2) != 30)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
@@ -145,14 +166,16 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (var + 10, 2) != x)
     abort ();
+  if (__builtin_object_size (&var[5], 2) != x + 5)
+    abort ();
 #else
   if (__builtin_object_size (var, 2) != 0)
     abort ();
   if (__builtin_object_size (var + 10, 2) != 0)
     abort ();
-#endif
   if (__builtin_object_size (&var[5], 2) != 0)
     abort ();
+#endif
   if (__builtin_object_size (zerol, 2) != 0)
     abort ();
   if (__builtin_object_size (&zerol, 2) != 0)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
index c383385e060..f644890dd14 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-4.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
@@ -155,27 +155,28 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (var + 10, 3) != x)
     abort ();
+  if (__builtin_object_size (&var[5], 3) != x + 5)
+    abort ();
+  if (__builtin_object_size (vara, 3) != (x + 10) * sizeof (struct A))
+    abort ();
+  if (__builtin_object_size (vara + 10, 3) != x * sizeof (struct A))
+    abort ();    
+  if (__builtin_object_size (&vara[5], 3) != (x + 5) * sizeof (struct A))
+    abort ();
 #else
   if (__builtin_object_size (var, 3) != 0)
     abort ();
   if (__builtin_object_size (var + 10, 3) != 0)
     abort ();
-#endif
   if (__builtin_object_size (&var[5], 3) != 0)
     abort ();
-#ifdef __builtin_object_size
-  if (__builtin_object_size (vara, 3) != (x + 10) * sizeof (struct A))
-    abort ();
-  if (__builtin_object_size (vara + 10, 3) != x * sizeof (struct A))
-    abort ();    
-#else
   if (__builtin_object_size (vara, 3) != 0)
     abort ();
   if (__builtin_object_size (vara + 10, 3) != 0)
     abort ();    
-#endif
   if (__builtin_object_size (&vara[5], 3) != 0)
     abort ();
+#endif
   if (__builtin_object_size (&vara[0].a, 3) != sizeof (vara[0].a))
     abort ();
   if (__builtin_object_size (&vara[10].a[0], 3) != sizeof (vara[0].a))
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index e17ef294d0c..bdc5c0a001b 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -107,6 +107,14 @@ size_unknown_p (tree val, int object_size_type)
 	  ? integer_zerop (val) : integer_all_onesp (val));
 }
 
+/* Return true if VAL is represents a valid size for OBJECT_SIZE_TYPE.  */
+
+static inline bool
+size_valid_p (tree val, int object_size_type)
+{
+  return ((object_size_type & OST_DYNAMIC) || TREE_CODE (val) == INTEGER_CST);
+}
+
 /* Return true if VAL is usable as an object size in the object_sizes
    vectors.  */
 
@@ -341,7 +349,6 @@ init_offset_limit (void)
 static tree
 size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE)
 {
-  gcc_checking_assert (TREE_CODE (offset) == INTEGER_CST);
   gcc_checking_assert (types_compatible_p (TREE_TYPE (sz), sizetype));
 
   /* For negative offsets, if we have a distinct WHOLESIZE, use it to get a net
@@ -540,18 +547,11 @@ 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))
-	{
-	  tree offset = TREE_OPERAND (pt_var, 1);
-	  if (TREE_CODE (offset) != INTEGER_CST
-	      || TREE_CODE (sz) != INTEGER_CST)
-	    sz = wholesize = size_unknown (object_size_type);
-	  else
-	    sz = size_for_offset (sz, offset, wholesize);
-	}
+	sz = size_for_offset (sz, TREE_OPERAND (pt_var, 1), wholesize);
 
       if (!size_unknown_p (sz, object_size_type)
-	  && TREE_CODE (sz) == INTEGER_CST
-	  && compare_tree_int (sz, offset_limit) < 0)
+	  && (TREE_CODE (sz) != INTEGER_CST
+	      || compare_tree_int (sz, offset_limit) < 0))
 	{
 	  pt_var_size = sz;
 	  pt_var_wholesize = wholesize;
@@ -571,8 +571,9 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 
   if (pt_var_size)
     {
-      /* Validate the size determined above.  */
-      if (compare_tree_int (pt_var_size, offset_limit) >= 0)
+      /* Validate the size determined above if it is a constant.  */
+      if (TREE_CODE (pt_var_size) == INTEGER_CST
+	  && compare_tree_int (pt_var_size, offset_limit) >= 0)
 	return false;
     }
 
@@ -596,7 +597,7 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 	    var = TREE_OPERAND (var, 0);
 	  if (! TYPE_SIZE_UNIT (TREE_TYPE (var))
 	      || ! tree_fits_uhwi_p (TYPE_SIZE_UNIT (TREE_TYPE (var)))
-	      || (pt_var_size
+	      || (pt_var_size && TREE_CODE (pt_var_size) == INTEGER_CST
 		  && tree_int_cst_lt (pt_var_size,
 				      TYPE_SIZE_UNIT (TREE_TYPE (var)))))
 	    var = pt_var;
@@ -610,17 +611,11 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 		switch (TREE_CODE (v))
 		  {
 		  case ARRAY_REF:
-		    if (TYPE_SIZE_UNIT (TREE_TYPE (TREE_OPERAND (v, 0)))
-			&& TREE_CODE (TREE_OPERAND (v, 1)) == INTEGER_CST)
+		    if (TYPE_SIZE_UNIT (TREE_TYPE (TREE_OPERAND (v, 0))))
 		      {
 			tree domain
 			  = TYPE_DOMAIN (TREE_TYPE (TREE_OPERAND (v, 0)));
-			if (domain
-			    && TYPE_MAX_VALUE (domain)
-			    && TREE_CODE (TYPE_MAX_VALUE (domain))
-			       == INTEGER_CST
-			    && tree_int_cst_lt (TREE_OPERAND (v, 1),
-						TYPE_MAX_VALUE (domain)))
+			if (domain && TYPE_MAX_VALUE (domain))
 			  {
 			    v = NULL_TREE;
 			    break;
@@ -687,20 +682,20 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 	var = pt_var;
 
       if (var != pt_var)
-	var_size = TYPE_SIZE_UNIT (TREE_TYPE (var));
+	{
+	  var_size = TYPE_SIZE_UNIT (TREE_TYPE (var));
+	  if (!TREE_CONSTANT (var_size))
+	    var_size = get_or_create_ssa_default_def (cfun, var_size);
+	  if (!var_size)
+	    return false;
+	}
       else if (!pt_var_size)
 	return false;
       else
 	var_size = pt_var_size;
       bytes = compute_object_offset (TREE_OPERAND (ptr, 0), var);
       if (bytes != error_mark_node)
-	{
-	  if (TREE_CODE (bytes) == INTEGER_CST
-	      && tree_int_cst_lt (var_size, bytes))
-	    bytes = size_zero_node;
-	  else
-	    bytes = size_binop (MINUS_EXPR, var_size, bytes);
-	}
+	bytes = size_for_offset (var_size, bytes);
       if (var != pt_var
 	  && pt_var_size
 	  && TREE_CODE (pt_var) == MEM_REF
@@ -709,11 +704,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)
 	    {
-	      if (TREE_CODE (bytes2) == INTEGER_CST
-		  && tree_int_cst_lt (pt_var_size, bytes2))
-		bytes2 = size_zero_node;
-	      else
-		bytes2 = size_binop (MINUS_EXPR, pt_var_size, bytes2);
+	      bytes2 = size_for_offset (pt_var_size, bytes2);
 	      bytes = size_binop (MIN_EXPR, bytes, bytes2);
 	    }
 	}
@@ -729,14 +720,18 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
       wholebytes = pt_var_wholesize;
     }
 
-  if (TREE_CODE (bytes) != INTEGER_CST
-      || TREE_CODE (wholebytes) != INTEGER_CST)
-    return false;
+  if (!size_unknown_p (bytes, object_size_type)
+      && size_valid_p (bytes, object_size_type)
+      && !size_unknown_p (bytes, object_size_type)
+      && size_valid_p (wholebytes, object_size_type))
+    {
+      *psize = bytes;
+      if (pwholesize)
+	*pwholesize = wholebytes;
+      return true;
+    }
 
-  *psize = bytes;
-  if (pwholesize)
-    *pwholesize = wholebytes;
-  return true;
+  return false;
 }
 
 
@@ -1058,11 +1053,11 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 	      tree offset = gimple_assign_rhs2 (def);
 	      ptr = gimple_assign_rhs1 (def);
 
-	      if (tree_fits_shwi_p (offset)
+	      if (((object_size_type & OST_DYNAMIC)
+		   || tree_fits_shwi_p (offset))
 		  && compute_builtin_object_size (ptr, object_size_type,
 						  psize))
 		{
-		  /* Return zero when the offset is out of bounds.  */
 		  *psize = size_for_offset (*psize, offset);
 		  return true;
 		}
@@ -1247,7 +1242,7 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call)
   gcc_assert (osi->pass == 0);
   tree bytes = alloc_object_size (call, object_size_type);
 
-  if (!(object_size_type & OST_DYNAMIC) && TREE_CODE (bytes) != INTEGER_CST)
+  if (!size_valid_p (bytes, object_size_type))
     bytes = size_unknown (object_size_type);
 
   object_sizes_set (osi, varno, bytes, bytes);
@@ -1328,7 +1323,7 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
     return false;
 
   /* Handle PTR + OFFSET here.  */
-  if (TREE_CODE (op1) == INTEGER_CST
+  if (((object_size_type & OST_DYNAMIC) || TREE_CODE (op1) == INTEGER_CST)
       && (TREE_CODE (op0) == SSA_NAME
 	  || TREE_CODE (op0) == ADDR_EXPR))
     {
@@ -1361,6 +1356,10 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   else
     bytes = wholesize = size_unknown (object_size_type);
 
+  if (!size_valid_p (bytes, object_size_type)
+      || !size_valid_p (wholesize, object_size_type))
+    bytes = wholesize = size_unknown (object_size_type);
+
   if (object_sizes_set (osi, varno, bytes, wholesize))
     osi->changed = true;
   return reexamine;
-- 
2.31.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PING][PATCH v5 0/4] __builtin_dynamic_object_size
  2021-12-18 12:35 ` [PATCH v5 0/4] __builtin_dynamic_object_size Siddhesh Poyarekar
                     ` (3 preceding siblings ...)
  2021-12-18 12:35   ` [PATCH v5 4/4] tree-object-size: Dynamic sizes for ADDR_EXPR Siddhesh Poyarekar
@ 2022-01-04  3:24   ` Siddhesh Poyarekar
  4 siblings, 0 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2022-01-04  3:24 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Happy new year, and ping!

On 12/18/21 18:05, Siddhesh Poyarekar wrote:
> This patchset enhances the __builtin_dynamic_object_size builtin to
> produce dynamic expressions for object sizes to improve coverage of
> _FORTIFY_SOURCE.
> 
> Testing:
> --------
> 
> This series has been tested with build and test for i686, bootstrap with
> ubsan and full bootstrap and test with x86_64.  I also tested the
> toolchain with a glibc build and testsuite run for x86_64 and i686 with
> _FORTIFY_SOURCE=3 enabled for gcc12.
> 
> Additional testing plans (i.e. I've already started to do some of this):
> 
> - Build packages to compare values returned by __builtin_object_size
>    with the older pass and this new one.  Also compare with
>    __builtin_dynamic_object_size.
> 
> - Expand the list of packages to get more coverage metrics.
> 
> - Explore performance impact on applications on building with
>    _FORTIFY_SOURCE=3.
> 
> Siddhesh Poyarekar (4):
>    tree-object-size: Support dynamic sizes in conditions
>    tree-object-size: Handle function parameters
>    tree-object-size: Handle GIMPLE_CALL
>    tree-object-size: Dynamic sizes for ADDR_EXPR
> 
>   gcc/builtins.c                                |   6 +-
>   .../gcc.dg/builtin-dynamic-object-size-0.c    | 495 +++++++++++++
>   .../gcc.dg/builtin-dynamic-object-size-10.c   |   2 +
>   .../builtin-dynamic-object-size-5-main.c      |  32 +
>   .../gcc.dg/builtin-dynamic-object-size-5.c    |   7 +-
>   gcc/testsuite/gcc.dg/builtin-object-size-1.c  | 154 +++-
>   gcc/testsuite/gcc.dg/builtin-object-size-2.c  | 133 ++++
>   gcc/testsuite/gcc.dg/builtin-object-size-3.c  | 151 ++++
>   gcc/testsuite/gcc.dg/builtin-object-size-4.c  |  93 +++
>   gcc/testsuite/gcc.dg/builtin-object-size-5.c  |  22 +-
>   gcc/tree-object-size.c                        | 670 +++++++++++++++---
>   11 files changed, 1677 insertions(+), 88 deletions(-)
>   create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
>   create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5-main.c
> 


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v5 1/4] tree-object-size: Support dynamic sizes in conditions
  2021-12-18 12:35   ` [PATCH v5 1/4] tree-object-size: Support dynamic sizes in conditions Siddhesh Poyarekar
@ 2022-01-10 10:37     ` Jakub Jelinek
  2022-01-10 23:55       ` Siddhesh Poyarekar
  0 siblings, 1 reply; 97+ messages in thread
From: Jakub Jelinek @ 2022-01-10 10:37 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Sat, Dec 18, 2021 at 06:05:08PM +0530, Siddhesh Poyarekar wrote:
Sorry for the delay.

> +size_t
> +__attribute__ ((noinline))
> +test_builtin_calloc_condphi (size_t cnt, size_t sz, int cond)
> +{
> +  struct
> +    {
> +      int a;
> +      char b;
> +    } bin[cnt];
> +
> +  char *ch = __builtin_calloc (cnt, sz);
> +  size_t ret = __builtin_dynamic_object_size (cond ? ch : (void *) &bin, 0);
> +
> +  __builtin_free (ch);
> +  return ret;
> +}

> +int
> +main (int argc, char **argv)

You don't use argc nor argv, just leave those out IMO.

> +{
> +  if (test_builtin_malloc_condphi (1) != 32)
> +    FAIL ();
> +  if (test_builtin_malloc_condphi (0) != 64)
> +    FAIL ();

You test the above with both possibilities.

> +  if (test_builtin_calloc_condphi (128, 1, 0) == 128)
> +    FAIL ();

But not this one, why?  Also, it would be better to have
a != ... test rather than ==, if it is the VLA, then 128 * sizeof (struct { int a; char b; })
?

> +/* Return true if VAL is represents an initial size for OBJECT_SIZE_TYPE.  */

s/is //

> +
> +static inline bool
> +size_initval_p (tree val, int object_size_type)

> +  phires = TREE_VEC_ELT (size, TREE_VEC_LENGTH (size) - 1);
> +  gphi *phi = create_phi_node (phires, gimple_bb (stmt));
> +  gphi *obj_phi =  as_a <gphi *> (stmt);

Formatting, just one space before as_a.

> +  /* Expand all size expressions to put their definitions close to the objects
> +     for whom size is being computed.  */

English is not my primary language, but shouldn't whom be used just
when talking about persons?  So for which size instead?

>  
> +static void
> +dynamic_object_size (struct object_size_info *osi, tree var,
> +		     tree *size, tree *wholesize)

Missing function comment.

> +  if (i < num_args)
> +    sizes = wholesizes = size_unknown (object_size_type);

Perhaps
      ggc_free (sizes);
      gcc_free (wholesizes);
here before the assignment?

> +
> +  /* Point to the same TREE_VEC so that we can avoid emitting two PHI
> +     nodes.  */
> +  if (!wholesize_needed)

and make this else if

> +    wholesizes = sizes;

and ggc_free (wholesizes); before the assignment?
When it is very easy and provably correct that it will just be memory
to be GCed later...

Otherwise LGTM.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v5 2/4] tree-object-size: Handle function parameters
  2021-12-18 12:35   ` [PATCH v5 2/4] tree-object-size: Handle function parameters Siddhesh Poyarekar
@ 2022-01-10 10:50     ` Jakub Jelinek
  2022-01-11  0:32       ` Siddhesh Poyarekar
  0 siblings, 1 reply; 97+ messages in thread
From: Jakub Jelinek @ 2022-01-10 10:50 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Sat, Dec 18, 2021 at 06:05:09PM +0530, Siddhesh Poyarekar wrote:
> @@ -1440,6 +1441,53 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
>    return reexamine;
>  }
>  
> +/* Find size of an object passed as a parameter to the function.  */
> +
> +static void
> +parm_object_size (struct object_size_info *osi, tree var)
> +{
> +  int object_size_type = osi->object_size_type;
> +  tree parm = SSA_NAME_VAR (var);
> +
> +  if (!(object_size_type & OST_DYNAMIC) || !POINTER_TYPE_P (TREE_TYPE (parm)))
> +    expr_object_size (osi, var, parm);

This looks very suspicious.  Didn't you mean { expr_object_size (...); return; } here?
Because the code below e.g. certainly assumes OST_DYNAMIC and that TREE_TYPE (parm)
is a pointer type (otherwise TREE_TYPE (TREE_TYPE (...) wouldn't work.

> +
> +  /* Look for access attribute.  */
> +  rdwr_map rdwr_idx;
> +
> +  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)

Perhaps && typesize here?  It makes no sense to e.g. create ssa default def
when you aren't going to use it in any way.

> +    {
> +      tree fnargs = DECL_ARGUMENTS (fndecl);
> +      tree arg = NULL_TREE;
> +      unsigned argpos = 0;
> +
> +      /* Walk through the parameters to pick the size parameter and safely
> +	 scale it by the type size.  */
> +      for (arg = fnargs; argpos != access->sizarg && arg;
> +	   arg = TREE_CHAIN (arg), ++argpos);

Instead of a loop with empty body wouldn't it be better to
do the work in that for loop?
I.e. take argpos != access->sizarg && from the condition,
replace arg != NULL_TREE with that argpos == access->sizarg
and add a break;?

> +
> +      if (arg != NULL_TREE && INTEGRAL_TYPE_P (TREE_TYPE (arg)))
> +	{
> +	  sz = get_or_create_ssa_default_def (cfun, arg);

Also, I must say I'm little bit worried about this
get_or_create_ssa_default_def call.  If the SSA_NAME doesn't exist,
so you create it and then attempt to use it but in the end don't
because e.g. some PHI's another argument was unknown etc., will
that SSA_NAME be released through release_ssa_name?
I think GIMPLE is fairly unhappy if there are SSA_NAMEs created and not
released that don't appear in the IL anywhere.

> +	  if (sz != NULL_TREE)
> +	    {
> +	      sz = fold_convert (sizetype, sz);
> +	      if (typesize)
> +		sz = size_binop (MULT_EXPR, sz, typesize);
> +	    }
> +	}
> +    }

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v5 3/4] tree-object-size: Handle GIMPLE_CALL
  2021-12-18 12:35   ` [PATCH v5 3/4] tree-object-size: Handle GIMPLE_CALL Siddhesh Poyarekar
@ 2022-01-10 11:03     ` Jakub Jelinek
  0 siblings, 0 replies; 97+ messages in thread
From: Jakub Jelinek @ 2022-01-10 11:03 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Sat, Dec 18, 2021 at 06:05:10PM +0530, Siddhesh Poyarekar wrote:
> Handle non-constant expressions in GIMPLE_CALL arguments.  Also handle
> alloca.
> 
> gcc/ChangeLog:
> 
> 	* tree-object-size.c (alloc_object_size): Make and return
> 	non-constant size expression.
> 	(call_object_size): Return expression or unknown based on
> 	whether dynamic object size is requested.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* gcc.dg/builtin-dynamic-object-size-0.c: Add new tests.
> 	* gcc.dg/builtin-object-size-1.c (test1)
> 	[__builtin_object_size]: Alter expected result for dynamic
> 	object size.
> 	* gcc.dg/builtin-object-size-2.c (test1)
> 	[__builtin_object_size]: Likewise.
> 	* gcc.dg/builtin-object-size-3.c (test1)
> 	[__builtin_object_size]: Likewise.
> 	* gcc.dg/builtin-object-size-4.c (test1)
> 	[__builtin_object_size]: Likewise.

Ok.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v5 4/4] tree-object-size: Dynamic sizes for ADDR_EXPR
  2021-12-18 12:35   ` [PATCH v5 4/4] tree-object-size: Dynamic sizes for ADDR_EXPR Siddhesh Poyarekar
@ 2022-01-10 11:09     ` Jakub Jelinek
  0 siblings, 0 replies; 97+ messages in thread
From: Jakub Jelinek @ 2022-01-10 11:09 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Sat, Dec 18, 2021 at 06:05:11PM +0530, Siddhesh Poyarekar wrote:
> --- a/gcc/tree-object-size.c
> +++ b/gcc/tree-object-size.c
> @@ -107,6 +107,14 @@ size_unknown_p (tree val, int object_size_type)
>  	  ? integer_zerop (val) : integer_all_onesp (val));
>  }
>  
> +/* Return true if VAL is represents a valid size for OBJECT_SIZE_TYPE.  */
> +
> +static inline bool
> +size_valid_p (tree val, int object_size_type)
> +{
> +  return ((object_size_type & OST_DYNAMIC) || TREE_CODE (val) == INTEGER_CST);
> +}

It is fine to introduce this predicate, but then it should be consistenly
used wherever you check the above.

> @@ -1328,7 +1323,7 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
>      return false;
>  
>    /* Handle PTR + OFFSET here.  */
> -  if (TREE_CODE (op1) == INTEGER_CST
> +  if (((object_size_type & OST_DYNAMIC) || TREE_CODE (op1) == INTEGER_CST)

E.g. above.

Otherwise LGTM.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v5 1/4] tree-object-size: Support dynamic sizes in conditions
  2022-01-10 10:37     ` Jakub Jelinek
@ 2022-01-10 23:55       ` Siddhesh Poyarekar
  0 siblings, 0 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2022-01-10 23:55 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 10/01/2022 16:07, Jakub Jelinek wrote:
> You test the above with both possibilities.
> 
>> +  if (test_builtin_calloc_condphi (128, 1, 0) == 128)
>> +    FAIL ();
> 
> But not this one, why?  Also, it would be better to have
> a != ... test rather than ==, if it is the VLA, then 128 * sizeof (struct { int a; char b; })
> ?

I think I'll move the test_builtin_calloc_condphi test into the 
GIMPLE_CALL patch since that's where it becomes fully functional.

Thanks,
Siddhesh

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v5 2/4] tree-object-size: Handle function parameters
  2022-01-10 10:50     ` Jakub Jelinek
@ 2022-01-11  0:32       ` Siddhesh Poyarekar
  0 siblings, 0 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2022-01-11  0:32 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 10/01/2022 16:20, Jakub Jelinek wrote:
> On Sat, Dec 18, 2021 at 06:05:09PM +0530, Siddhesh Poyarekar wrote:
>> @@ -1440,6 +1441,53 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
>>     return reexamine;
>>   }
>>   
>> +/* Find size of an object passed as a parameter to the function.  */
>> +
>> +static void
>> +parm_object_size (struct object_size_info *osi, tree var)
>> +{
>> +  int object_size_type = osi->object_size_type;
>> +  tree parm = SSA_NAME_VAR (var);
>> +
>> +  if (!(object_size_type & OST_DYNAMIC) || !POINTER_TYPE_P (TREE_TYPE (parm)))
>> +    expr_object_size (osi, var, parm);
> 
> This looks very suspicious.  Didn't you mean { expr_object_size (...); return; } here?
> Because the code below e.g. certainly assumes OST_DYNAMIC and that TREE_TYPE (parm)
> is a pointer type (otherwise TREE_TYPE (TREE_TYPE (...) wouldn't work.

Indeed, fixed.

> 
>> +
>> +  /* Look for access attribute.  */
>> +  rdwr_map rdwr_idx;
>> +
>> +  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)
> 
> Perhaps && typesize here?  It makes no sense to e.g. create ssa default def
> when you aren't going to use it in any way.

The typesize is only for scaling; the result of 
get_or_create_ssa_default_def should get returned unscaled if it is 
non-NULL and typesize is NULL; the latter happens when the type is void *:

	  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);
	    }
	}

> 
>> +    {
>> +      tree fnargs = DECL_ARGUMENTS (fndecl);
>> +      tree arg = NULL_TREE;
>> +      unsigned argpos = 0;
>> +
>> +      /* Walk through the parameters to pick the size parameter and safely
>> +	 scale it by the type size.  */
>> +      for (arg = fnargs; argpos != access->sizarg && arg;
>> +	   arg = TREE_CHAIN (arg), ++argpos);
> 
> Instead of a loop with empty body wouldn't it be better to
> do the work in that for loop?
> I.e. take argpos != access->sizarg && from the condition,
> replace arg != NULL_TREE with that argpos == access->sizarg
> and add a break;?

Fixed.

> 
>> +
>> +      if (arg != NULL_TREE && INTEGRAL_TYPE_P (TREE_TYPE (arg)))
>> +	{
>> +	  sz = get_or_create_ssa_default_def (cfun, arg);
> 
> Also, I must say I'm little bit worried about this
> get_or_create_ssa_default_def call.  If the SSA_NAME doesn't exist,
> so you create it and then attempt to use it but in the end don't
> because e.g. some PHI's another argument was unknown etc., will
> that SSA_NAME be released through release_ssa_name?
> I think GIMPLE is fairly unhappy if there are SSA_NAMEs created and not
> released that don't appear in the IL anywhere.

AFAICT, set_ss_default_def ends up creating a definition for the new 
SSA_NAME it creates, so it does end up in the IR and in case of object 
size computation failure, it just ends up being a dead store.  I've 
added a test to verify this:

size_t
__attribute__ ((access (__read_write__, 1, 3)))
__attribute__ ((noinline))
test_parmsz_unknown (void *obj, void *unknown, size_t sz, int cond)
{
   return __builtin_dynamic_object_size (cond ? obj : unknown, 0);
}

which works as expected and returns -1.

Thanks,
Siddhesh

^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v6 0/4] __builtin_dynamic_object_size
  2021-11-09 19:01 [PATCH 00/10] __builtin_dynamic_object_size Siddhesh Poyarekar
                   ` (13 preceding siblings ...)
  2021-12-18 12:35 ` [PATCH v5 0/4] __builtin_dynamic_object_size Siddhesh Poyarekar
@ 2022-01-11  8:57 ` Siddhesh Poyarekar
  2022-01-11  8:57   ` [PATCH v6 1/4] tree-object-size: Support dynamic sizes in conditions Siddhesh Poyarekar
                     ` (3 more replies)
  14 siblings, 4 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2022-01-11  8:57 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

This patchset enhances the __builtin_dynamic_object_size builtin to
produce dynamic expressions for object sizes to improve coverage of
_FORTIFY_SOURCE.

Testing:
--------

This series has been tested with build and test for i686, bootstrap with
ubsan and bootstrap+test with x86_64.

Additional testing plans (i.e. I've already started to do some of this):

- Build packages to compare values returned by __builtin_object_size
  with the older pass and this new one.  Also compare with
  __builtin_dynamic_object_size.

- Expand the list of packages to get more coverage metrics.

- Explore performance impact on applications on building with
  _FORTIFY_SOURCE=3.

Changes from v5:
- Address review comments
- Add a couple more tests for object sizes of pointers passed as
  parameters.

Siddhesh Poyarekar (4):
  tree-object-size: Support dynamic sizes in conditions
  tree-object-size: Handle function parameters
  tree-object-size: Handle GIMPLE_CALL
  tree-object-size: Dynamic sizes for ADDR_EXPR

 gcc/builtins.c                                |   6 +-
 .../gcc.dg/builtin-dynamic-object-size-0.c    | 514 +++++++++++++
 .../gcc.dg/builtin-dynamic-object-size-10.c   |   2 +
 .../builtin-dynamic-object-size-5-main.c      |  32 +
 .../gcc.dg/builtin-dynamic-object-size-5.c    |   7 +-
 gcc/testsuite/gcc.dg/builtin-object-size-1.c  | 154 +++-
 gcc/testsuite/gcc.dg/builtin-object-size-2.c  | 133 ++++
 gcc/testsuite/gcc.dg/builtin-object-size-3.c  | 151 ++++
 gcc/testsuite/gcc.dg/builtin-object-size-4.c  |  93 +++
 gcc/testsuite/gcc.dg/builtin-object-size-5.c  |  22 +-
 gcc/tree-object-size.c                        | 689 +++++++++++++++---
 11 files changed, 1712 insertions(+), 91 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5-main.c

-- 
2.33.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v6 1/4] tree-object-size: Support dynamic sizes in conditions
  2022-01-11  8:57 ` [PATCH v6 " Siddhesh Poyarekar
@ 2022-01-11  8:57   ` Siddhesh Poyarekar
  2022-01-11  9:43     ` Jakub Jelinek
  2022-01-11  8:57   ` [PATCH v6 2/4] tree-object-size: Handle function parameters Siddhesh Poyarekar
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2022-01-11  8:57 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Handle GIMPLE_PHI and conditionals specially for dynamic objects,
returning PHI/conditional expressions instead of just a MIN/MAX
estimate.

This makes the returned object size variable for loops and conditionals,
so tests need to be adjusted to look for precise size in some cases.
builtin-dynamic-object-size-5.c had to be modified to only look for
success in maximum object size case and skip over the minimum object
size tests because the result is no longer a compile time constant.

I also added some simple tests to exercise conditionals with dynamic
object sizes.

gcc/ChangeLog:

	* builtins.c (fold_builtin_object_size): Adjust for dynamic size
	expressions.
	* tree-object-size.c: Include gimplify-me.h.
	(struct object_size_info): New member UNKNOWNS.
	(size_initval_p, size_usable_p, object_sizes_get_raw): New
	functions.
	(object_sizes_get): Return suitable gimple variable for
	object size.
	(bundle_sizes): New function.
	(object_sizes_set): Use it and handle dynamic object size
	expressions.
	(object_sizes_set_temp): New function.
	(size_for_offset): Adjust for dynamic size expressions.
	(emit_phi_nodes, propagate_unknowns, gimplify_size_expressions):
	New functions.
	(compute_builtin_object_size): Call gimplify_size_expressions
	for OST_DYNAMIC.
	(dynamic_object_size): New function.
	(cond_expr_object_size): Use it.
	(phi_dynamic_object_size): New function.
	(collect_object_sizes_for): Call it for OST_DYNAMIC.  Adjust to
	accommodate dynamic object sizes.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-dynamic-object-size-0.c: New tests.
	* gcc.dg/builtin-dynamic-object-size-10.c: Add comment.
	* gcc.dg/builtin-dynamic-object-size-5-main.c: New file.
	* gcc.dg/builtin-dynamic-object-size-5.c: Use it and change test
	to dg-do run.
	* gcc.dg/builtin-object-size-5.c [!N]: Define N.
	(test1, test2, test3, test4) [__builtin_object_size]: Expect
	exact result for __builtin_dynamic_object_size.
	* gcc.dg/builtin-object-size-1.c [__builtin_object_size]: Expect
	exact size expressions for __builtin_dynamic_object_size.
	* gcc.dg/builtin-object-size-2.c [__builtin_object_size]:
	Likewise.
	* gcc.dg/builtin-object-size-3.c [__builtin_object_size]:
	Likewise.
	* gcc.dg/builtin-object-size-4.c [__builtin_object_size]:
	Likewise.
	* gcc.dg/builtin-object-size-5.c [__builtin_object_size]:
	Likewise.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 gcc/builtins.c                                |   6 +-
 .../gcc.dg/builtin-dynamic-object-size-0.c    |  58 ++
 .../gcc.dg/builtin-dynamic-object-size-10.c   |   2 +
 .../builtin-dynamic-object-size-5-main.c      |  32 ++
 .../gcc.dg/builtin-dynamic-object-size-5.c    |   7 +-
 gcc/testsuite/gcc.dg/builtin-object-size-1.c  | 119 +++-
 gcc/testsuite/gcc.dg/builtin-object-size-2.c  |  92 ++++
 gcc/testsuite/gcc.dg/builtin-object-size-3.c  | 121 ++++
 gcc/testsuite/gcc.dg/builtin-object-size-4.c  |  78 +++
 gcc/testsuite/gcc.dg/builtin-object-size-5.c  |  22 +-
 gcc/tree-object-size.c                        | 521 +++++++++++++++++-
 11 files changed, 1022 insertions(+), 36 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5-main.c

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 51d3635bd57..c780340ed32 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -10306,7 +10306,8 @@ fold_builtin_object_size (tree ptr, tree ost, enum built_in_function fcode)
   if (TREE_CODE (ptr) == ADDR_EXPR)
     {
       compute_builtin_object_size (ptr, object_size_type, &bytes);
-      if (int_fits_type_p (bytes, size_type_node))
+      if ((object_size_type & OST_DYNAMIC)
+	  || int_fits_type_p (bytes, size_type_node))
 	return fold_convert (size_type_node, bytes);
     }
   else if (TREE_CODE (ptr) == SSA_NAME)
@@ -10315,7 +10316,8 @@ fold_builtin_object_size (tree ptr, tree ost, enum built_in_function fcode)
        later.  Maybe subsequent passes will help determining
        it.  */
       if (compute_builtin_object_size (ptr, object_size_type, &bytes)
-	  && int_fits_type_p (bytes, size_type_node))
+	  && ((object_size_type & OST_DYNAMIC)
+	      || int_fits_type_p (bytes, size_type_node)))
 	return fold_convert (size_type_node, bytes);
     }
 
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
new file mode 100644
index 00000000000..b1013e19fd0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -0,0 +1,58 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+typedef __SIZE_TYPE__ size_t;
+#define abort __builtin_abort
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi (int cond)
+{
+  void *ch;
+ 
+  if (cond)
+    ch = __builtin_malloc (32);
+  else
+    ch = __builtin_malloc (64);
+
+  size_t ret = __builtin_dynamic_object_size (ch, 0);
+
+  __builtin_free (ch);
+  return ret;
+}
+
+size_t
+__attribute__ ((noinline))
+test_deploop (size_t sz, size_t cond)
+{
+  char *bin = __builtin_alloca (32);
+
+  for (size_t i = 0; i < sz; i++)
+    if (i == cond)
+      bin = __builtin_alloca (64);
+
+  return __builtin_dynamic_object_size (bin, 0);
+}
+
+unsigned nfails = 0;
+
+#define FAIL() ({ \
+  __builtin_printf ("Failure at line: %d\n", __LINE__);			      \
+  nfails++;								      \
+})
+
+int
+main ()
+{
+  if (test_builtin_malloc_condphi (1) != 32)
+    FAIL ();
+  if (test_builtin_malloc_condphi (0) != 64)
+    FAIL ();
+  if (test_deploop (128, 129) != 32)
+    FAIL ();
+
+  if (nfails > 0)
+    __builtin_abort ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
index bc880a589ae..3a2d9821a44 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
@@ -5,5 +5,7 @@
 #define __builtin_object_size __builtin_dynamic_object_size
 #include "builtin-object-size-10.c"
 
+/* early_objsz should resolve __builtin_dynamic_object_size like
+   __builtin_object_size.  */
 /* { dg-final { scan-tree-dump "maximum object size 21" "early_objsz" } } */
 /* { dg-final { scan-tree-dump "maximum subobject size 16" "early_objsz" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5-main.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5-main.c
new file mode 100644
index 00000000000..49f459b8cf2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5-main.c
@@ -0,0 +1,32 @@
+#ifdef N
+typedef __SIZE_TYPE__ size_t;
+
+char buf[N];
+
+void test1 (size_t);
+void test2 (size_t);
+void test3 (size_t);
+void test4 (size_t);
+void test5 (size_t);
+void test6 (size_t);
+void test7 (size_t);
+
+int
+main (void)
+{
+  test1 (42);
+  test2 (42);
+  test3 (42);
+  test4 (42);
+  test5 (42);
+  test6 (42);
+  test7 (42);
+  return 0;
+}
+#else
+int
+main (void)
+{
+  return 0;
+}
+#endif
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c
index 698b03c34be..80b77dd5249 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5.c
@@ -1,7 +1,6 @@
-/* { dg-do compile { target i?86-*-linux* i?86-*-gnu* x86_64-*-linux* } } */
-/* { dg-options "-O2" } */
+/* { dg-do run { target i?86-*-linux* i?86-*-gnu* x86_64-*-linux* } } */
+/* { dg-options "-O2 -DN=0x4000000" } */
+/* { dg-additional-sources "builtin-dynamic-object-size-5-main.c" } */
 
 #define __builtin_object_size __builtin_dynamic_object_size
 #include "builtin-object-size-5.c"
-
-/* { dg-final { scan-assembler-not "abort" } } */
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
index 0154f4e9695..265c87ed6fb 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
@@ -42,9 +42,17 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (q, 0) != (size_t) -1)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0)
+      != (x < 0
+	  ? sizeof (a) - __builtin_offsetof (struct A, a) - 9
+	  : sizeof (a) - __builtin_offsetof (struct A, c) - 1))
+    abort ();
+#else
   if (__builtin_object_size (r, 0)
       != sizeof (a) - __builtin_offsetof (struct A, a) - 9)
     abort ();
+#endif
   if (x < 6)
     r = &w[2].a[1];
   else
@@ -58,9 +66,17 @@ test1 (void *q, int x)
   if (__builtin_object_size (&y.b, 0)
       != sizeof (a) - __builtin_offsetof (struct A, b))
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0)
+      != (x < 6
+	  ? 2 * sizeof (w[0]) - __builtin_offsetof (struct A, a) - 1
+	  : sizeof (a) - __builtin_offsetof (struct A, a) - 6))
+    abort ();
+#else
   if (__builtin_object_size (r, 0)
       != 2 * sizeof (w[0]) - __builtin_offsetof (struct A, a) - 1)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
@@ -165,6 +181,7 @@ test2 (void)
   struct B { char buf1[10]; char buf2[10]; } a;
   char *r, buf3[20];
   int i;
+  size_t res;
 
   if (sizeof (a) != 20)
     return;
@@ -181,7 +198,24 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[9];
     }
-  if (__builtin_object_size (r, 0) != 20)
+#ifdef __builtin_object_size
+  res = sizeof (buf3);
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 1;
+      else if (i == l1)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 7;
+      else if (i == l1 + 1)
+        res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
+    }
+#else
+  res = 20;
+#endif
+  if (__builtin_object_size (r, 0) != res)
     abort ();
   r = &buf3[20];
   for (i = 0; i < 4; ++i)
@@ -195,13 +229,45 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[9];
     }
-  if (__builtin_object_size (r, 0) != 15)
+#ifdef __builtin_object_size
+  res = sizeof (buf3) - 20;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 7;
+      else if (i == l1)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 7;
+      else if (i == l1 + 1)
+        res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
+    }
+  if (__builtin_object_size (r, 0) != res)
+    abort ();
+#else
+  res = 15;
+#endif
+  if (__builtin_object_size (r, 0) != res)
     abort ();
   r += 8;
+#ifdef __builtin_object_size
+  res -= 8;
+  if (__builtin_object_size (r, 0) != res)
+    abort ();
+  if (res >= 6)
+    {
+      if (__builtin_object_size (r + 6, 0) != res - 6)
+        abort ();
+    }
+  else if (__builtin_object_size (r + 6, 0) != 0)
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != 7)
     abort ();
   if (__builtin_object_size (r + 6, 0) != 1)
     abort ();
+#endif
   r = &buf3[18];
   for (i = 0; i < 4; ++i)
     {
@@ -214,8 +280,31 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[4];
     }
+#ifdef __builtin_object_size
+  res = sizeof (buf3) - 18;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+          res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
+      else if (i == l1)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 9;
+      else if (i == l1 + 1)
+        res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+        res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 4;
+    }
+  if (res >= 12)
+    {
+      if (__builtin_object_size (r + 12, 0) != res - 12)
+        abort ();
+    }
+  else if (__builtin_object_size (r + 12, 0) != 0)
+    abort ();
+#else
   if (__builtin_object_size (r + 12, 0) != 4)
     abort ();
+#endif
 }
 
 void
@@ -358,6 +447,10 @@ test5 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 0) != sizeof (buf) - 8 - 4 * x)
+    abort ();
+#else
   /* My understanding of ISO C99 6.5.6 is that a conforming
      program will not end up with p equal to &buf[0]
      through &buf[7], i.e. calling this function with say
@@ -367,6 +460,7 @@ test5 (size_t x)
      it would be 64 (or conservative (size_t) -1 == unknown).  */
   if (__builtin_object_size (p, 0) != sizeof (buf) - 8)
     abort ();
+#endif
   memset (p, ' ', sizeof (buf) - 8 - 4 * 4);
 }
 
@@ -380,8 +474,13 @@ test6 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 0) != sizeof (t) - 8 - 4 * x)
+    abort ();
+#else
   if (__builtin_object_size (p, 0) != sizeof (t) - 8)
     abort ();
+#endif
   memset (p, ' ', sizeof (t) - 8 - 4 * 4);
 }
 
@@ -436,21 +535,37 @@ test9 (unsigned cond)
   else
     p = &buf2[4];
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (&p[-4], 0) != (cond ? 6 : 10))
+    abort ();
+#else
   if (__builtin_object_size (&p[-4], 0) != 10)
     abort ();
+#endif
 
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 0) != ((cond ? 2 : 6) + cond))
+    abort ();
+#else
   if (__builtin_object_size (p, 0) != 10)
     abort ();
+#endif
 
   p = &y.c[8];
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 0)
+      != sizeof (y) - __builtin_offsetof (struct A, c) - 8 + cond)
+    abort ();
+#else
   if (__builtin_object_size (p, 0) != sizeof (y))
     abort ();
+#endif
 }
 
 int
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
index 5cf29291aff..5051fea47c3 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-2.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
@@ -43,8 +43,15 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (q, 1) != (size_t) -1)
     abort ();
+#ifdef __builtin_object_size
+  if (x < 0
+      ? __builtin_object_size (r, 1) != sizeof (a.a) - 9
+      : __builtin_object_size (r, 1) != sizeof (a.c) - 1)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a.c) - 1)
     abort ();
+#endif
   if (x < 6)
     r = &w[2].a[1];
   else
@@ -55,8 +62,15 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&y.b, 1) != sizeof (a.b))
     abort ();
+#ifdef __builtin_object_size
+  if (x < 6
+      ? __builtin_object_size (r, 1) != sizeof (a.a) - 1
+      : __builtin_object_size (r, 1) != sizeof (a.a) - 6)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a.a) - 1)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
@@ -185,6 +199,9 @@ test2 (void)
   struct B { char buf1[10]; char buf2[10]; } a;
   char *r, buf3[20];
   int i;
+#ifdef __builtin_object_size
+  size_t dyn_res;
+#endif
 
   if (sizeof (a) != 20)
     return;
@@ -201,8 +218,26 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[9];
     }
+#ifdef __builtin_object_size
+  dyn_res = sizeof (buf3);
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	dyn_res = sizeof (a.buf1) - 1;
+      else if (i == l1)
+	dyn_res = sizeof (a.buf2) - 7;
+      else if (i == l1 + 1)
+	dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+	dyn_res = sizeof (a.buf1) - 9;
+    }
+  if (__builtin_object_size (r, 1) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (buf3))
     abort ();
+#endif
   r = &buf3[20];
   for (i = 0; i < 4; ++i)
     {
@@ -215,13 +250,50 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[9];
     }
+#ifdef __builtin_object_size
+  dyn_res = sizeof (buf3) - 20;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+        dyn_res = sizeof (a.buf1) - 7;
+      else if (i == l1)
+        dyn_res = sizeof (a.buf2) - 7;
+      else if (i == l1 + 1)
+        dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+        dyn_res = sizeof (a.buf1) - 9;
+    }
+  if (__builtin_object_size (r, 1) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (buf3) - 5)
     abort ();
+#endif
   r += 8;
+#ifdef __builtin_object_size
+  if (dyn_res >= 8)
+    {
+      dyn_res -= 8;
+      if (__builtin_object_size (r, 1) != dyn_res)
+	abort ();
+
+      if (dyn_res >= 6)
+	{
+	  if (__builtin_object_size (r + 6, 1) != dyn_res - 6)
+	    abort ();
+	}
+      else if (__builtin_object_size (r + 6, 1) != 0)
+	abort ();
+    }
+  else if (__builtin_object_size (r, 1) != 0)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (buf3) - 13)
     abort ();
   if (__builtin_object_size (r + 6, 1) != sizeof (buf3) - 19)
     abort ();
+#endif
 }
 
 void
@@ -339,8 +411,13 @@ test5 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 1) != sizeof (t.buf) - 8 - 4 * x)
+    abort ();
+#else
   if (__builtin_object_size (p, 1) != sizeof (t.buf) - 8)
     abort ();
+#endif
   memset (p, ' ', sizeof (t.buf) - 8 - 4 * 4);
 }
 
@@ -394,21 +471,36 @@ test8 (unsigned cond)
   else
     p = &buf2[4];
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (&p[-4], 1) != (cond ? 6 : 10))
+    abort ();
+#else
   if (__builtin_object_size (&p[-4], 1) != 10)
     abort ();
+#endif
 
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 1) != ((cond ? 2 : 6) + cond))
+    abort ();
+#else
   if (__builtin_object_size (p, 1) != 10)
     abort ();
+#endif
 
   p = &y.c[8];
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 1) != sizeof (y.c) - 8 + cond)
+    abort ();
+#else
   if (__builtin_object_size (p, 1) != sizeof (y.c))
     abort ();
+#endif
 }
 
 int
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
index 3a692c4e3d2..1d92627266b 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-3.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
@@ -71,23 +71,45 @@ test1 (void *q, int x)
     r = malloc (30);
   else
     r = calloc (2, 14);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 2) != (x < 20 ? 30 : 2 * 14))
+    abort ();
+#else
   if (__builtin_object_size (r, 2) != 2 * 14)
     abort ();
+#endif
   if (x < 30)
     r = malloc (sizeof (a));
   else
     r = &a.a[3];
+#ifdef __builtin_object_size
+  size_t objsz = (x < 30 ? sizeof (a)
+                  : sizeof (a) - __builtin_offsetof (struct A, a) - 3);
+  if (__builtin_object_size (r, 2) != objsz)
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, a) - 3)
     abort ();
+#endif
   r = memcpy (r, "a", 2);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 2) != objsz)
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, a) - 3)
     abort ();
+#endif
   r = memcpy (r + 2, "b", 2) + 2;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 2) != objsz - 4)
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, a) - 3 - 4)
     abort ();
+#endif
   r = &a.a[4];
   r = memset (r, 'a', 2);
   if (__builtin_object_size (r, 2)
@@ -164,6 +186,9 @@ test2 (void)
   struct B { char buf1[10]; char buf2[10]; } a;
   char *r, buf3[20];
   int i;
+#ifdef __builtin_object_size
+  size_t dyn_res;
+#endif
 
   if (sizeof (a) != 20)
     return;
@@ -180,8 +205,26 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[9];
     }
+#ifdef __builtin_object_size
+  dyn_res = sizeof (buf3);
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 1;
+      else if (i == l1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 7;
+      else if (i == l1 + 1)
+	dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
+    }
+  if (__builtin_object_size (r, 2) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 2) != 3)
     abort ();
+#endif
   r = &buf3[20];
   for (i = 0; i < 4; ++i)
     {
@@ -208,13 +251,44 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[4];
     }
+#ifdef __builtin_object_size
+  dyn_res = sizeof (buf3) - 2;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 1;
+      else if (i == l1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 2;
+      else if (i == l1 + 1)
+	dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 4;
+    }
+  if (__builtin_object_size (r, 2) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 2) != 15)
     abort ();
+#endif
   r += 8;
+#ifdef __builtin_object_size
+  dyn_res -= 8;
+  if (__builtin_object_size (r, 2) != dyn_res)
+    abort ();
+  if (dyn_res >= 6)
+    {
+      if (__builtin_object_size (r + 6, 2) != dyn_res - 6)
+	abort ();
+    }
+  else if (__builtin_object_size (r + 6, 2) != 0)
+    abort ();
+#else
   if (__builtin_object_size (r, 2) != 7)
     abort ();
   if (__builtin_object_size (r + 6, 2) != 1)
     abort ();
+#endif
   r = &buf3[18];
   for (i = 0; i < 4; ++i)
     {
@@ -227,8 +301,31 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[4];
     }
+#ifdef __builtin_object_size
+  dyn_res = sizeof (buf3) - 18;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
+      else if (i == l1)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 9;
+      else if (i == l1 + 1)
+	dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+	dyn_res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 4;
+    }
+  if (dyn_res >= 12)
+    {
+      if (__builtin_object_size (r + 12, 2) != dyn_res - 12)
+	abort ();
+    }
+  else if (__builtin_object_size (r + 12, 2) != 0)
+    abort ();
+#else
   if (__builtin_object_size (r + 12, 2) != 0)
     abort ();
+#endif
 }
 
 void
@@ -371,7 +468,11 @@ test5 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 2) != sizeof (buf) - 8 - 4 * x)
+#else
   if (__builtin_object_size (p, 2) != 0)
+#endif
     abort ();
   memset (p, ' ', sizeof (buf) - 8 - 4 * 4);
 }
@@ -386,7 +487,11 @@ test6 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 2) != sizeof (t) - 8 - 4 * x)
+#else
   if (__builtin_object_size (p, 2) != 0)
+#endif
     abort ();
   memset (p, ' ', sizeof (t) - 8 - 4 * 4);
 }
@@ -442,22 +547,38 @@ test9 (unsigned cond)
   else
     p = &buf2[4];
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (&p[-4], 2) != (cond ? 6 : 10))
+    abort ();
+#else
   if (__builtin_object_size (&p[-4], 2) != 6)
     abort ();
+#endif
 
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 2) != ((cond ? 2 : 6) + cond))
+    abort ();
+#else
   if (__builtin_object_size (p, 2) != 2)
     abort ();
+#endif
 
   p = &y.c[8];
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 2)
+      != sizeof (y) - __builtin_offsetof (struct A, c) - 8 + cond)
+    abort ();
+#else
   if (__builtin_object_size (p, 2)
       != sizeof (y) - __builtin_offsetof (struct A, c) - 8)
     abort ();
+#endif
 }
 
 int
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
index 87381620cc9..9da3537a5f7 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-4.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
@@ -43,7 +43,12 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (q, 3) != 0)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3)
+      != (x < 0 ? sizeof (a.a) - 9 : sizeof (a.c) - 1))
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.a) - 9)
+#endif
     abort ();
   if (x < 6)
     r = &w[2].a[1];
@@ -55,31 +60,57 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&y.b, 3) != sizeof (a.b))
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3)
+      != (x < 6 ? sizeof (w[2].a) - 1 : sizeof (a.a) - 6))
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.a) - 6)
+#endif
     abort ();
   if (x < 20)
     r = malloc (30);
   else
     r = calloc (2, 16);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3) != (x < 20 ? 30 : 2 * 16))
+#else
   if (__builtin_object_size (r, 3) != 30)
+#endif
     abort ();
   if (x < 20)
     r = malloc (30);
   else
     r = calloc (2, 14);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3) != (x < 20 ? 30 : 2 * 14))
+#else
   if (__builtin_object_size (r, 3) != 2 * 14)
+#endif
     abort ();
   if (x < 30)
     r = malloc (sizeof (a));
   else
     r = &a.a[3];
+#ifdef __builtin_object_size
+  size_t objsz = x < 30 ? sizeof (a) : sizeof (a.a) - 3;
+  if (__builtin_object_size (r, 3) != objsz)
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.a) - 3)
+#endif
     abort ();
   r = memcpy (r, "a", 2);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3) != objsz)
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.a) - 3)
+#endif
     abort ();
   r = memcpy (r + 2, "b", 2) + 2;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3) != objsz - 4)
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.a) - 3 - 4)
+#endif
     abort ();
   r = &a.a[4];
   r = memset (r, 'a', 2);
@@ -184,6 +215,9 @@ test2 (void)
   struct B { char buf1[10]; char buf2[10]; } a;
   char *r, buf3[20];
   int i;
+#ifdef __builtin_object_size
+  size_t dyn_res = 0;
+#endif
 
   if (sizeof (a) != 20)
     return;
@@ -228,13 +262,38 @@ test2 (void)
       else if (i == l1 + 2)
 	r = &a.buf1[2];
     }
+#ifdef __builtin_object_size
+  dyn_res = sizeof (buf3) - 1;
+
+  for (i = 0; i < 4; ++i)
+    {
+      if (i == l1 - 1)
+        dyn_res = sizeof (a.buf1) - 6;
+      else if (i == l1)
+        dyn_res = sizeof (a.buf2) - 4;
+      else if (i == l1 + 1)
+        dyn_res = sizeof (buf3) - 5;
+      else if (i == l1 + 2)
+        dyn_res = sizeof (a.buf1) - 2;
+    }
+  if (__builtin_object_size (r, 3) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.buf1) - 6)
     abort ();
+#endif
   r += 2;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 3) != dyn_res - 2)
+    abort ();
+  if (__builtin_object_size (r + 1, 3) != dyn_res - 3)
+    abort ();
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.buf1) - 6 - 2)
     abort ();
   if (__builtin_object_size (r + 1, 3) != sizeof (a.buf1) - 6 - 3)
     abort ();
+#endif
 }
 
 void
@@ -352,7 +411,11 @@ test5 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 3) != sizeof (t.buf) - 8 - 4 * x)
+#else
   if (__builtin_object_size (p, 3) != 0)
+#endif
     abort ();
   memset (p, ' ', sizeof (t.buf) - 8 - 4 * 4);
 }
@@ -407,21 +470,36 @@ test8 (unsigned cond)
   else
     p = &buf2[4];
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (&p[-4], 3) != (cond ? 6 : 10))
+    abort ();
+#else
   if (__builtin_object_size (&p[-4], 3) != 6)
     abort ();
+#endif
 
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 3) != ((cond ? 2 : 6) + cond))
+    abort ();
+#else
   if (__builtin_object_size (p, 3) != 2)
     abort ();
+#endif
 
   p = &y.c[8];
   for (unsigned i = cond; i > 0; i--)
     p--;
 
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 3) != sizeof (y.c) - 8 + cond)
+    abort ();
+#else
   if (__builtin_object_size (p, 3) != sizeof (y.c) - 8)
     abort ();
+#endif
 }
 
 int
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-5.c b/gcc/testsuite/gcc.dg/builtin-object-size-5.c
index 8e63d9c7a5e..cb8f753b039 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-5.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-5.c
@@ -1,9 +1,13 @@
 /* { dg-do compile { target i?86-*-linux* i?86-*-gnu* x86_64-*-linux* } } */
 /* { dg-options "-O2" } */
 
+#ifndef N
+# define N 0x40000000
+#endif
+
 typedef __SIZE_TYPE__ size_t;
 extern void abort (void);
-extern char buf[0x40000000];
+extern char buf[N];
 
 void
 test1 (size_t x)
@@ -13,7 +17,11 @@ test1 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 0) != sizeof (buf) - 8 - 4 * x)
+#else
   if (__builtin_object_size (p, 0) != sizeof (buf) - 8)
+#endif
     abort ();
 }
 
@@ -25,7 +33,11 @@ test2 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 1) != sizeof (buf) - 8 - 4 * x)
+#else
   if (__builtin_object_size (p, 1) != sizeof (buf) - 8)
+#endif
     abort ();
 }
 
@@ -37,7 +49,11 @@ test3 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 2) != sizeof (buf) - 8 - 4 * x)
+#else
   if (__builtin_object_size (p, 2) != 0)
+#endif
     abort ();
 }
 
@@ -49,7 +65,11 @@ test4 (size_t x)
 
   for (i = 0; i < x; ++i)
     p = p + 4;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (p, 3) != sizeof (buf) - 8 - 4 * x)
+#else
   if (__builtin_object_size (p, 3) != 0)
+#endif
     abort ();
 }
 
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index fbaf57a20f8..775792b4419 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -35,13 +35,14 @@ along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "attribs.h"
 #include "builtins.h"
+#include "gimplify-me.h"
 
 struct object_size_info
 {
   int object_size_type;
   unsigned char pass;
   bool changed;
-  bitmap visited, reexamine;
+  bitmap visited, reexamine, unknowns;
   unsigned int *depths;
   unsigned int *stack, *tos;
 };
@@ -74,7 +75,11 @@ static void check_for_plus_in_loops_1 (struct object_size_info *, tree,
    object_sizes[1] is upper bound for the object size and number of bytes till
    the end of the subobject (innermost array or field with address taken).
    object_sizes[2] is lower bound for the object size and number of bytes till
-   the end of the object and object_sizes[3] lower bound for subobject.  */
+   the end of the object and object_sizes[3] lower bound for subobject.
+
+   For static object sizes, the object size and the bytes till the end of the
+   object are both INTEGER_CST.  In the dynamic case, they are finally either a
+   gimple variable or an INTEGER_CST.  */
 static vec<object_size> object_sizes[OST_END];
 
 /* Bitmaps what object sizes have been computed already.  */
@@ -83,7 +88,16 @@ static bitmap computed[OST_END];
 /* Maximum value of offset we consider to be addition.  */
 static unsigned HOST_WIDE_INT offset_limit;
 
-/* Return true if VAL is represents an unknown size for OBJECT_SIZE_TYPE.  */
+/* Return true if VAL represents an initial size for OBJECT_SIZE_TYPE.  */
+
+static inline bool
+size_initval_p (tree val, int object_size_type)
+{
+  return ((object_size_type & OST_MINIMUM)
+	  ? integer_all_onesp (val) : integer_zerop (val));
+}
+
+/* Return true if VAL represents an unknown size for OBJECT_SIZE_TYPE.  */
 
 static inline bool
 size_unknown_p (tree val, int object_size_type)
@@ -92,6 +106,15 @@ size_unknown_p (tree val, int object_size_type)
 	  ? integer_zerop (val) : integer_all_onesp (val));
 }
 
+/* Return true if VAL is usable as an object size in the object_sizes
+   vectors.  */
+
+static inline bool
+size_usable_p (tree val)
+{
+  return TREE_CODE (val) == SSA_NAME || TREE_CODE (val) == INTEGER_CST;
+}
+
 /* Return a tree with initial value for OBJECT_SIZE_TYPE.  */
 
 static inline tree
@@ -136,17 +159,43 @@ object_sizes_unknown_p (int object_size_type, unsigned varno)
 			 object_size_type);
 }
 
-/* Return size for VARNO corresponding to OSI.  If WHOLE is true, return the
-   whole object size.  */
+/* Return the raw size expression for VARNO corresponding to OSI.  This returns
+   the TREE_VEC as is and should only be used during gimplification.  */
+
+static inline object_size
+object_sizes_get_raw (struct object_size_info *osi, unsigned varno)
+{
+  gcc_assert (osi->pass != 0);
+  return object_sizes[osi->object_size_type][varno];
+}
+
+/* Return a size tree for VARNO corresponding to OSI.  If WHOLE is true, return
+   the whole object size.  Use this for building size expressions based on size
+   of VARNO.  */
 
 static inline tree
 object_sizes_get (struct object_size_info *osi, unsigned varno,
 		  bool whole = false)
 {
+  tree ret;
+  int object_size_type = osi->object_size_type;
+
   if (whole)
-    return object_sizes[osi->object_size_type][varno].wholesize;
+    ret = object_sizes[object_size_type][varno].wholesize;
   else
-    return object_sizes[osi->object_size_type][varno].size;
+    ret = object_sizes[object_size_type][varno].size;
+
+  if (object_size_type & OST_DYNAMIC)
+    {
+      if (TREE_CODE (ret) == MODIFY_EXPR)
+	return TREE_OPERAND (ret, 0);
+      else if (TREE_CODE (ret) == TREE_VEC)
+	return TREE_VEC_ELT (ret, TREE_VEC_LENGTH (ret) - 1);
+      else
+	gcc_checking_assert (size_usable_p (ret));
+    }
+
+  return ret;
 }
 
 /* Set size for VARNO corresponding to OSI to VAL.  */
@@ -161,28 +210,116 @@ object_sizes_initialize (struct object_size_info *osi, unsigned varno,
   object_sizes[object_size_type][varno].wholesize = wholeval;
 }
 
+/* Return a MODIFY_EXPR for cases where SSA and EXPR have the same type.  The
+   TREE_VEC is returned only in case of PHI nodes.  */
+
+static tree
+bundle_sizes (tree name, tree expr)
+{
+  gcc_checking_assert (TREE_TYPE (name) == sizetype);
+
+  if (TREE_CODE (expr) == TREE_VEC)
+    {
+      TREE_VEC_ELT (expr, TREE_VEC_LENGTH (expr) - 1) = name;
+      return expr;
+    }
+
+  gcc_checking_assert (types_compatible_p (TREE_TYPE (expr), sizetype));
+  return build2 (MODIFY_EXPR, sizetype, name, expr);
+}
+
 /* Set size for VARNO corresponding to OSI to VAL if it is the new minimum or
-   maximum.  */
+   maximum.  For static sizes, each element of TREE_VEC is always INTEGER_CST
+   throughout the computation.  For dynamic sizes, each element may either be a
+   gimple variable, a MODIFY_EXPR or a TREE_VEC.  The MODIFY_EXPR is for
+   expressions that need to be gimplified.  TREE_VECs are special, they're
+   emitted only for GIMPLE_PHI and the PHI result variable is the last element
+   of the vector.  */
 
-static inline bool
+static bool
 object_sizes_set (struct object_size_info *osi, unsigned varno, tree val,
 		  tree wholeval)
 {
   int object_size_type = osi->object_size_type;
   object_size osize = object_sizes[object_size_type][varno];
+  bool changed = true;
 
   tree oldval = osize.size;
   tree old_wholeval = osize.wholesize;
 
-  enum tree_code code = object_size_type & OST_MINIMUM ? MIN_EXPR : MAX_EXPR;
+  if (object_size_type & OST_DYNAMIC)
+    {
+      if (bitmap_bit_p (osi->reexamine, varno))
+	{
+	  if (size_unknown_p (val, object_size_type))
+	    {
+	      oldval = object_sizes_get (osi, varno);
+	      old_wholeval = object_sizes_get (osi, varno, true);
+	      bitmap_set_bit (osi->unknowns, SSA_NAME_VERSION (oldval));
+	      bitmap_set_bit (osi->unknowns, SSA_NAME_VERSION (old_wholeval));
+	      bitmap_clear_bit (osi->reexamine, varno);
+	    }
+	  else
+	    {
+	      val = bundle_sizes (oldval, val);
+	      wholeval = bundle_sizes (old_wholeval, wholeval);
+	    }
+	}
+      else
+	{
+	  gcc_checking_assert (size_initval_p (oldval, object_size_type));
+	  gcc_checking_assert (size_initval_p (old_wholeval,
+					       object_size_type));
+	  /* For dynamic object sizes, all object sizes that are not gimple
+	     variables will need to be gimplified.  */
+	  if (wholeval != val && !size_usable_p (wholeval))
+	    {
+	      bitmap_set_bit (osi->reexamine, varno);
+	      wholeval = bundle_sizes (make_ssa_name (sizetype), wholeval);
+	    }
+	  if (!size_usable_p (val))
+	    {
+	      bitmap_set_bit (osi->reexamine, varno);
+	      tree newval = bundle_sizes (make_ssa_name (sizetype), val);
+	      if (val == wholeval)
+		wholeval = newval;
+	      val = newval;
+	    }
+	  /* If the new value is a temporary variable, mark it for
+	     reexamination.  */
+	  else if (TREE_CODE (val) == SSA_NAME && !SSA_NAME_DEF_STMT (val))
+	    bitmap_set_bit (osi->reexamine, varno);
+	}
+    }
+  else
+    {
+      enum tree_code code = (object_size_type & OST_MINIMUM
+			     ? MIN_EXPR : MAX_EXPR);
 
-  val = size_binop (code, val, oldval);
-  wholeval = size_binop (code, wholeval, old_wholeval);
+      val = size_binop (code, val, oldval);
+      wholeval = size_binop (code, wholeval, old_wholeval);
+      changed = (tree_int_cst_compare (val, oldval) != 0
+		 || tree_int_cst_compare (old_wholeval, wholeval) != 0);
+    }
 
   object_sizes[object_size_type][varno].size = val;
   object_sizes[object_size_type][varno].wholesize = wholeval;
-  return (tree_int_cst_compare (oldval, val) != 0
-	  || tree_int_cst_compare (old_wholeval, wholeval) != 0);
+
+  return changed;
+}
+
+/* Set temporary SSA names for object size and whole size to resolve dependency
+   loops in dynamic size computation.  */
+
+static inline void
+object_sizes_set_temp (struct object_size_info *osi, unsigned varno)
+{
+  tree val = object_sizes_get (osi, varno);
+
+  if (size_initval_p (val, osi->object_size_type))
+    object_sizes_set (osi, varno,
+		      make_ssa_name (sizetype),
+		      make_ssa_name (sizetype));
 }
 
 /* Initialize OFFSET_LIMIT variable.  */
@@ -204,14 +341,15 @@ static tree
 size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE)
 {
   gcc_checking_assert (TREE_CODE (offset) == INTEGER_CST);
-  gcc_checking_assert (TREE_CODE (sz) == INTEGER_CST);
   gcc_checking_assert (types_compatible_p (TREE_TYPE (sz), sizetype));
 
   /* For negative offsets, if we have a distinct WHOLESIZE, use it to get a net
      offset from the whole object.  */
-  if (wholesize && tree_int_cst_compare (sz, wholesize))
+  if (wholesize && wholesize != sz
+      && (TREE_CODE (sz) != INTEGER_CST
+	  || TREE_CODE (wholesize) != INTEGER_CST
+	  || tree_int_cst_compare (sz, wholesize)))
     {
-      gcc_checking_assert (TREE_CODE (wholesize) == INTEGER_CST);
       gcc_checking_assert (types_compatible_p (TREE_TYPE (wholesize),
 					       sizetype));
 
@@ -228,13 +366,16 @@ size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE)
   if (!types_compatible_p (TREE_TYPE (offset), sizetype))
     fold_convert (sizetype, offset);
 
-  if (integer_zerop (offset))
-    return sz;
+  if (TREE_CODE (offset) == INTEGER_CST)
+    {
+      if (integer_zerop (offset))
+	return sz;
 
-  /* Negative or too large offset even after adjustment, cannot be within
-     bounds of an object.  */
-  if (compare_tree_int (offset, offset_limit) > 0)
-    return size_zero_node;
+      /* Negative or too large offset even after adjustment, cannot be within
+	 bounds of an object.  */
+      if (compare_tree_int (offset, offset_limit) > 0)
+	return size_zero_node;
+    }
 
   return size_binop (MINUS_EXPR, size_binop (MAX_EXPR, sz, offset), offset);
 }
@@ -671,6 +812,201 @@ pass_through_call (const gcall *call)
   return NULL_TREE;
 }
 
+/* Emit PHI nodes for size expressions fo.  */
+
+static void
+emit_phi_nodes (gimple *stmt, tree size, tree wholesize)
+{
+  tree phires;
+  gphi *wholephi = NULL;
+
+  if (wholesize != size)
+    {
+      phires = TREE_VEC_ELT (wholesize, TREE_VEC_LENGTH (wholesize) - 1);
+      wholephi = create_phi_node (phires, gimple_bb (stmt));
+    }
+
+  phires = TREE_VEC_ELT (size, TREE_VEC_LENGTH (size) - 1);
+  gphi *phi = create_phi_node (phires, gimple_bb (stmt));
+  gphi *obj_phi = as_a <gphi *> (stmt);
+
+  gcc_checking_assert (TREE_CODE (wholesize) == TREE_VEC);
+  gcc_checking_assert (TREE_CODE (size) == TREE_VEC);
+
+  for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++)
+    {
+      gimple_seq seq = NULL;
+      tree wsz = TREE_VEC_ELT (wholesize, i);
+      tree sz = TREE_VEC_ELT (size, i);
+
+      /* If we built an expression, we will need to build statements
+	 and insert them on the edge right away.  */
+      if (TREE_CODE (wsz) != SSA_NAME)
+	wsz = force_gimple_operand (wsz, &seq, true, NULL);
+      if (TREE_CODE (sz) != SSA_NAME)
+	{
+	  gimple_seq s;
+	  sz = force_gimple_operand (sz, &s, true, NULL);
+	  gimple_seq_add_seq (&seq, s);
+	}
+
+      if (seq)
+	gsi_insert_seq_on_edge (gimple_phi_arg_edge (obj_phi, i), seq);
+
+      if (wholephi)
+	add_phi_arg (wholephi, wsz,
+		     gimple_phi_arg_edge (obj_phi, i),
+		     gimple_phi_arg_location (obj_phi, i));
+
+      add_phi_arg (phi, sz,
+		   gimple_phi_arg_edge (obj_phi, i),
+		   gimple_phi_arg_location (obj_phi, i));
+    }
+}
+
+/* Descend through EXPR and return size_unknown if it uses any SSA variable
+   object_size_set or object_size_set_temp generated, which turned out to be
+   size_unknown, as noted in UNKNOWNS.  */
+
+static tree
+propagate_unknowns (object_size_info *osi, tree expr)
+{
+  int object_size_type = osi->object_size_type;
+
+  switch (TREE_CODE (expr))
+    {
+    case SSA_NAME:
+      if (bitmap_bit_p (osi->unknowns, SSA_NAME_VERSION (expr)))
+	return size_unknown (object_size_type);
+      return expr;
+
+    case MIN_EXPR:
+    case MAX_EXPR:
+	{
+	  tree res = propagate_unknowns (osi, TREE_OPERAND (expr, 0));
+	  if (size_unknown_p (res, object_size_type))
+	    return res;
+
+	  res = propagate_unknowns (osi, TREE_OPERAND (expr, 1));
+	  if (size_unknown_p (res, object_size_type))
+	    return res;
+
+	  return expr;
+	}
+    case MODIFY_EXPR:
+	{
+	  tree res = propagate_unknowns (osi, TREE_OPERAND (expr, 1));
+	  if (size_unknown_p (res, object_size_type))
+	    return res;
+	  return expr;
+	}
+    case TREE_VEC:
+      for (int i = 0; i < TREE_VEC_LENGTH (expr); i++)
+	{
+	  tree res = propagate_unknowns (osi, TREE_VEC_ELT (expr, i));
+	  if (size_unknown_p (res, object_size_type))
+	    return res;
+	}
+      return expr;
+    case PLUS_EXPR:
+    case MINUS_EXPR:
+	{
+	  tree res = propagate_unknowns (osi, TREE_OPERAND (expr, 0));
+	  if (size_unknown_p (res, object_size_type))
+	    return res;
+
+	  return expr;
+	}
+    default:
+      return expr;
+    }
+}
+
+/* Walk through size expressions that need reexamination and generate
+   statements for them.  */
+
+static void
+gimplify_size_expressions (object_size_info *osi)
+{
+  int object_size_type = osi->object_size_type;
+  bitmap_iterator bi;
+  unsigned int i;
+  bool changed;
+
+  /* Step 1: Propagate unknowns into expressions.  */
+  bitmap reexamine = BITMAP_ALLOC (NULL);
+  bitmap_copy (reexamine, osi->reexamine);
+  do
+    {
+      changed = false;
+      EXECUTE_IF_SET_IN_BITMAP (reexamine, 0, i, bi)
+	{
+	  object_size cur = object_sizes_get_raw (osi, i);
+
+	  if (size_unknown_p (propagate_unknowns (osi, cur.size),
+			      object_size_type)
+	      || size_unknown_p (propagate_unknowns (osi, cur.wholesize),
+				 object_size_type))
+	    {
+	      object_sizes_set (osi, i,
+				size_unknown (object_size_type),
+				size_unknown (object_size_type));
+	      changed = true;
+	    }
+	}
+      bitmap_copy (reexamine, osi->reexamine);
+    }
+  while (changed);
+
+  /* Release all unknowns.  */
+  EXECUTE_IF_SET_IN_BITMAP (osi->unknowns, 0, i, bi)
+    release_ssa_name (ssa_name (i));
+
+  /* Expand all size expressions to put their definitions close to the objects
+     for which size is being computed.  */
+  EXECUTE_IF_SET_IN_BITMAP (osi->reexamine, 0, i, bi)
+    {
+      gimple_seq seq = NULL;
+      object_size osize = object_sizes_get_raw (osi, i);
+
+      gimple *stmt = SSA_NAME_DEF_STMT (ssa_name (i));
+      enum gimple_code code = gimple_code (stmt);
+
+      /* PHI nodes need special attention.  */
+      if (code == GIMPLE_PHI)
+	emit_phi_nodes (stmt, osize.size, osize.wholesize);
+      else
+	{
+	  tree size_expr = NULL_TREE;
+
+	  /* Bundle wholesize in with the size to gimplify if needed.  */
+	  if (osize.wholesize != osize.size
+	      && !size_usable_p (osize.wholesize))
+	    size_expr = size_binop (COMPOUND_EXPR,
+				    osize.wholesize,
+				    osize.size);
+	  else if (!size_usable_p (osize.size))
+	    size_expr = osize.size;
+
+	  if (size_expr)
+	    {
+	      gimple_stmt_iterator gsi;
+	      if (code == GIMPLE_NOP)
+		gsi = gsi_start_bb (single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)));
+	      else
+		gsi = gsi_for_stmt (stmt);
+
+	      force_gimple_operand (size_expr, &seq, true, NULL);
+	      gsi_insert_seq_before (&gsi, seq, GSI_CONTINUE_LINKING);
+	    }
+	}
+
+      /* We're done, so replace the MODIFY_EXPRs with the SSA names.  */
+      object_sizes_initialize (osi, i,
+			       object_sizes_get (osi, i),
+			       object_sizes_get (osi, i, true));
+    }
+}
 
 /* Compute __builtin_object_size value for PTR and set *PSIZE to
    the resulting value.  If the declared object is known and PDECL
@@ -749,9 +1085,15 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 
       osi.visited = BITMAP_ALLOC (NULL);
       osi.reexamine = BITMAP_ALLOC (NULL);
-      osi.depths = NULL;
-      osi.stack = NULL;
-      osi.tos = NULL;
+
+      if (object_size_type & OST_DYNAMIC)
+	osi.unknowns = BITMAP_ALLOC (NULL);
+      else
+	{
+	  osi.depths = NULL;
+	  osi.stack = NULL;
+	  osi.tos = NULL;
+	}
 
       /* First pass: walk UD chains, compute object sizes that
 	 can be computed.  osi.reexamine bitmap at the end will
@@ -761,6 +1103,14 @@ compute_builtin_object_size (tree ptr, int object_size_type,
       osi.changed = false;
       collect_object_sizes_for (&osi, ptr);
 
+      if (object_size_type & OST_DYNAMIC)
+	{
+	  osi.pass = 1;
+	  gimplify_size_expressions (&osi);
+	  BITMAP_FREE (osi.unknowns);
+	  bitmap_clear (osi.reexamine);
+	}
+
       /* Second pass: keep recomputing object sizes of variables
 	 that need reexamination, until no object sizes are
 	 increased or all object sizes are computed.  */
@@ -1007,6 +1357,28 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   return reexamine;
 }
 
+/* Compute the dynamic object size for VAR.  Return the result in SIZE and
+   WHOLESIZE.  */
+
+static void
+dynamic_object_size (struct object_size_info *osi, tree var,
+		     tree *size, tree *wholesize)
+{
+  int object_size_type = osi->object_size_type;
+
+  if (TREE_CODE (var) == SSA_NAME)
+    {
+      unsigned varno = SSA_NAME_VERSION (var);
+
+      collect_object_sizes_for (osi, var);
+      *size = object_sizes_get (osi, varno);
+      *wholesize = object_sizes_get (osi, varno, true);
+    }
+  else if (TREE_CODE (var) == ADDR_EXPR)
+    addr_object_size (osi, var, object_size_type, size, wholesize);
+  else
+    *size = *wholesize = size_unknown (object_size_type);
+}
 
 /* Compute object_sizes for VAR, defined at STMT, which is
    a COND_EXPR.  Return true if the object size might need reexamination
@@ -1028,6 +1400,33 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   then_ = gimple_assign_rhs2 (stmt);
   else_ = gimple_assign_rhs3 (stmt);
 
+  if (object_size_type & OST_DYNAMIC)
+    {
+      tree then_size, then_wholesize, else_size, else_wholesize;
+
+      dynamic_object_size (osi, then_, &then_size, &then_wholesize);
+      if (!size_unknown_p (then_size, object_size_type))
+	dynamic_object_size (osi, else_, &else_size, &else_wholesize);
+
+      tree cond_size, cond_wholesize;
+      if (size_unknown_p (then_size, object_size_type)
+	  || size_unknown_p (else_size, object_size_type))
+	cond_size = cond_wholesize = size_unknown (object_size_type);
+      else
+	{
+	  cond_size = fold_build3 (COND_EXPR, sizetype,
+				   gimple_assign_rhs1 (stmt),
+				   then_size, else_size);
+	  cond_wholesize = fold_build3 (COND_EXPR, sizetype,
+					gimple_assign_rhs1 (stmt),
+					then_wholesize, else_wholesize);
+	}
+
+      object_sizes_set (osi, varno, cond_size, cond_wholesize);
+
+      return false;
+    }
+
   if (TREE_CODE (then_) == SSA_NAME)
     reexamine |= merge_object_sizes (osi, var, then_);
   else
@@ -1044,6 +1443,64 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   return reexamine;
 }
 
+/* Compute an object size expression for VAR, which is the result of a PHI
+   node.  */
+
+static void
+phi_dynamic_object_size (struct object_size_info *osi, tree var)
+{
+  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);
+  bool wholesize_needed = false;
+
+  /* The extra space is for the PHI result at the end, which object_sizes_set
+     sets for us.  */
+  tree sizes = make_tree_vec (num_args + 1);
+  tree wholesizes = make_tree_vec (num_args + 1);
+
+  /* Bail out if the size of any of the PHI arguments cannot be
+     determined.  */
+  for (i = 0; i < num_args; i++)
+    {
+      edge e = gimple_phi_arg_edge (as_a <gphi *> (stmt), i);
+      if (e->flags & EDGE_COMPLEX)
+	break;
+
+      tree rhs = gimple_phi_arg_def (stmt, i);
+      tree size, wholesize;
+
+      dynamic_object_size (osi, rhs, &size, &wholesize);
+
+      if (size_unknown_p (size, object_size_type))
+       break;
+
+      if (size != wholesize)
+	wholesize_needed = true;
+
+      TREE_VEC_ELT (sizes, i) = size;
+      TREE_VEC_ELT (wholesizes, i) = wholesize;
+    }
+
+  if (i < num_args)
+    {
+      ggc_free (sizes);
+      ggc_free (wholesizes);
+      sizes = wholesizes = size_unknown (object_size_type);
+    }
+
+  /* Point to the same TREE_VEC so that we can avoid emitting two PHI
+     nodes.  */
+  else if (!wholesize_needed)
+    {
+      ggc_free (wholesizes);
+      wholesizes = sizes;
+    }
+
+  object_sizes_set (osi, varno, sizes, wholesizes);
+}
+
 /* Compute object sizes for VAR.
    For ADDR_EXPR an object size is the number of remaining bytes
    to the end of the object (where what is considered an object depends on
@@ -1089,6 +1546,9 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 	{
 	  /* Found a dependency loop.  Mark the variable for later
 	     re-examination.  */
+	  if (object_size_type & OST_DYNAMIC)
+	    object_sizes_set_temp (osi, varno);
+
 	  bitmap_set_bit (osi->reexamine, varno);
 	  if (dump_file && (dump_flags & TDF_DETAILS))
 	    {
@@ -1170,6 +1630,12 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
       {
 	unsigned i;
 
+	if (object_size_type & OST_DYNAMIC)
+	  {
+	    phi_dynamic_object_size (osi, var);
+	    break;
+	  }
+
 	for (i = 0; i < gimple_phi_num_args (stmt); i++)
 	  {
 	    tree rhs = gimple_phi_arg (stmt, i)->def;
@@ -1192,7 +1658,8 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
   if (! reexamine || object_sizes_unknown_p (object_size_type, varno))
     {
       bitmap_set_bit (computed[object_size_type], varno);
-      bitmap_clear_bit (osi->reexamine, varno);
+      if (!(object_size_type & OST_DYNAMIC))
+	bitmap_clear_bit (osi->reexamine, varno);
     }
   else
     {
-- 
2.33.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v6 2/4] tree-object-size: Handle function parameters
  2022-01-11  8:57 ` [PATCH v6 " Siddhesh Poyarekar
  2022-01-11  8:57   ` [PATCH v6 1/4] tree-object-size: Support dynamic sizes in conditions Siddhesh Poyarekar
@ 2022-01-11  8:57   ` Siddhesh Poyarekar
  2022-01-11  9:44     ` Jakub Jelinek
  2022-01-11  8:57   ` [PATCH v6 3/4] tree-object-size: Handle GIMPLE_CALL Siddhesh Poyarekar
  2022-01-11  8:57   ` [PATCH v6 4/4] tree-object-size: Dynamic sizes for ADDR_EXPR Siddhesh Poyarekar
  3 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2022-01-11  8:57 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Handle hints provided by __attribute__ ((access (...))) to compute
dynamic sizes for objects.

gcc/ChangeLog:

	* tree-object-size.c: Include tree-dfa.h.
	(parm_object_size): New function.
	(collect_object_sizes_for): Call it.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-dynamic-object-size-0.c (test_parmsz_simple,
	test_parmsz_scaled, test_parmsz_unknown): New functions.
	(main): Call them.  Add new arguments argc and argv.

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

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 b1013e19fd0..c89e2268943 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -34,6 +34,30 @@ test_deploop (size_t sz, size_t cond)
   return __builtin_dynamic_object_size (bin, 0);
 }
 
+size_t
+__attribute__ ((access (__read_write__, 1, 2)))
+__attribute__ ((noinline))
+test_parmsz_simple (void *obj, size_t sz)
+{
+  return __builtin_dynamic_object_size (obj, 0);
+}
+
+size_t
+__attribute__ ((access (__read_write__, 1, 2)))
+__attribute__ ((noinline))
+test_parmsz_scaled (int *obj, size_t sz)
+{
+  return __builtin_dynamic_object_size (obj, 0);
+}
+
+size_t
+__attribute__ ((access (__read_write__, 1, 3)))
+__attribute__ ((noinline))
+test_parmsz_unknown (void *obj, void *unknown, size_t sz, int cond)
+{
+  return __builtin_dynamic_object_size (cond ? obj : unknown, 0);
+}
+
 unsigned nfails = 0;
 
 #define FAIL() ({ \
@@ -42,7 +66,7 @@ unsigned nfails = 0;
 })
 
 int
-main ()
+main (int argc, char **argv)
 {
   if (test_builtin_malloc_condphi (1) != 32)
     FAIL ();
@@ -50,6 +74,15 @@ main ()
     FAIL ();
   if (test_deploop (128, 129) != 32)
     FAIL ();
+  if (test_parmsz_simple (argv[0], __builtin_strlen (argv[0]) + 1)
+      != __builtin_strlen (argv[0]) + 1)
+    FAIL ();
+  int arr[42];
+  if (test_parmsz_scaled (arr, 42) != sizeof (arr))
+    FAIL ();
+  if (test_parmsz_unknown (argv[0], argv[0], __builtin_strlen (argv[0]) + 1, 0)
+      != -1)
+    FAIL ();
 
   if (nfails > 0)
     __builtin_abort ();
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index 775792b4419..47e1c27bfd3 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-fold.h"
 #include "gimple-iterator.h"
 #include "tree-cfg.h"
+#include "tree-dfa.h"
 #include "stringpool.h"
 #include "attribs.h"
 #include "builtins.h"
@@ -1443,6 +1444,57 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   return reexamine;
 }
 
+/* Find size of an object passed as a parameter to the function.  */
+
+static void
+parm_object_size (struct object_size_info *osi, tree var)
+{
+  int object_size_type = osi->object_size_type;
+  tree parm = SSA_NAME_VAR (var);
+
+  if (!(object_size_type & OST_DYNAMIC) || !POINTER_TYPE_P (TREE_TYPE (parm)))
+    {
+      expr_object_size (osi, var, parm);
+      return;
+    }
+
+  /* Look for access attribute.  */
+  rdwr_map rdwr_idx;
+
+  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)
+    {
+      tree fnargs = DECL_ARGUMENTS (fndecl);
+      tree arg = NULL_TREE;
+      unsigned argpos = 0;
+
+      /* Walk through the parameters to pick the size parameter and safely
+	 scale it by the type size.  */
+      for (arg = fnargs; arg; arg = TREE_CHAIN (arg), ++argpos)
+	{
+	  if (argpos == access->sizarg && INTEGRAL_TYPE_P (TREE_TYPE (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);
+		}
+	      break;
+	    }
+	}
+    }
+  if (!sz)
+    sz = size_unknown (object_size_type);
+
+  object_sizes_set (osi, SSA_NAME_VERSION (var), sz, sz);
+}
+
 /* Compute an object size expression for VAR, which is the result of a PHI
    node.  */
 
@@ -1620,7 +1672,7 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
     case GIMPLE_NOP:
       if (SSA_NAME_VAR (var)
 	  && TREE_CODE (SSA_NAME_VAR (var)) == PARM_DECL)
-	expr_object_size (osi, var, SSA_NAME_VAR (var));
+	parm_object_size (osi, var);
       else
 	/* Uninitialized SSA names point nowhere.  */
 	unknown_object_size (osi, var);
-- 
2.33.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v6 3/4] tree-object-size: Handle GIMPLE_CALL
  2022-01-11  8:57 ` [PATCH v6 " Siddhesh Poyarekar
  2022-01-11  8:57   ` [PATCH v6 1/4] tree-object-size: Support dynamic sizes in conditions Siddhesh Poyarekar
  2022-01-11  8:57   ` [PATCH v6 2/4] tree-object-size: Handle function parameters Siddhesh Poyarekar
@ 2022-01-11  8:57   ` Siddhesh Poyarekar
  2022-01-11  8:57   ` [PATCH v6 4/4] tree-object-size: Dynamic sizes for ADDR_EXPR Siddhesh Poyarekar
  3 siblings, 0 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2022-01-11  8:57 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Handle non-constant expressions in GIMPLE_CALL arguments.  Also handle
alloca.

gcc/ChangeLog:

	* tree-object-size.c (alloc_object_size): Make and return
	non-constant size expression.
	(call_object_size): Return expression or unknown based on
	whether dynamic object size is requested.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-dynamic-object-size-0.c: Add new tests.
	* gcc.dg/builtin-object-size-1.c (test1)
	[__builtin_object_size]: Alter expected result for dynamic
	object size.
	* gcc.dg/builtin-object-size-2.c (test1)
	[__builtin_object_size]: Likewise.
	* gcc.dg/builtin-object-size-3.c (test1)
	[__builtin_object_size]: Likewise.
	* gcc.dg/builtin-object-size-4.c (test1)
	[__builtin_object_size]: Likewise.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 .../gcc.dg/builtin-dynamic-object-size-0.c    | 269 +++++++++++++++++-
 gcc/testsuite/gcc.dg/builtin-object-size-1.c  |   7 +
 gcc/testsuite/gcc.dg/builtin-object-size-2.c  |  14 +
 gcc/testsuite/gcc.dg/builtin-object-size-3.c  |   7 +
 gcc/testsuite/gcc.dg/builtin-object-size-4.c  |  14 +
 gcc/tree-object-size.c                        |  22 +-
 6 files changed, 325 insertions(+), 8 deletions(-)

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 c89e2268943..930fd49cd16 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -4,6 +4,73 @@
 typedef __SIZE_TYPE__ size_t;
 #define abort __builtin_abort
 
+void *
+__attribute__ ((alloc_size (1)))
+__attribute__ ((__nothrow__ , __leaf__))
+__attribute__ ((noinline))
+alloc_func (size_t sz)
+{
+  return __builtin_malloc (sz);
+}
+
+void *
+__attribute__ ((alloc_size (1, 2)))
+__attribute__ ((__nothrow__ , __leaf__))
+__attribute__ ((noinline))
+calloc_func (size_t cnt, size_t sz)
+{
+  return __builtin_calloc (cnt, sz);
+}
+
+void *
+__attribute__ ((noinline))
+unknown_allocator (size_t cnt, size_t sz)
+{
+  return __builtin_calloc (cnt, sz);
+}
+
+size_t
+__attribute__ ((noinline))
+test_unknown (size_t cnt, size_t sz)
+{
+  void *ch = unknown_allocator (cnt, sz);
+  size_t ret = __builtin_dynamic_object_size (ch, 0);
+  __builtin_free (ch);
+  return ret;
+}
+
+/* Malloc-like allocator.  */
+
+size_t
+__attribute__ ((noinline))
+test_malloc (size_t sz)
+{
+  void *ch = alloc_func (sz);
+  size_t ret = __builtin_dynamic_object_size (ch, 0);
+  __builtin_free (ch);
+  return ret;
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc (size_t sz)
+{
+  void *ch = __builtin_malloc (sz);
+  size_t ret = __builtin_dynamic_object_size (ch, 0);
+  __builtin_free (ch);
+  return ret;
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_cond (int cond)
+{
+  void *ch = __builtin_malloc (cond ? 32 : 64);
+  size_t ret = __builtin_dynamic_object_size (ch, 0);
+  __builtin_free (ch);
+  return ret;
+}
+
 size_t
 __attribute__ ((noinline))
 test_builtin_malloc_condphi (int cond)
@@ -21,6 +88,155 @@ test_builtin_malloc_condphi (int cond)
   return ret;
 }
 
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi2 (int cond, size_t in)
+{
+  void *ch;
+
+  if (cond)
+    ch = __builtin_malloc (in);
+  else
+    ch = __builtin_malloc (64);
+
+  size_t ret = __builtin_dynamic_object_size (ch, 0);
+
+  __builtin_free (ch);
+  return ret;
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi3 (int cond, size_t in, size_t in2)
+{
+  void *ch;
+
+  if (cond)
+    ch = __builtin_malloc (in);
+  else
+    ch = __builtin_malloc (in2);
+
+  size_t ret = __builtin_dynamic_object_size (ch, 0);
+
+  __builtin_free (ch);
+  return ret;
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi4 (size_t sz, int cond)
+{
+  char *a = __builtin_malloc (sz);
+  char b[sz / 2];
+
+  size_t ret = __builtin_dynamic_object_size (cond ? b : (void *) &a, 0);
+  __builtin_free (a);
+  return ret;
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi5 (size_t sz, int cond, char *c)
+{
+  char *a = __builtin_malloc (sz);
+
+  size_t ret = __builtin_dynamic_object_size (cond ? c : (void *) &a, 0);
+  __builtin_free (a);
+  return ret;
+}
+
+/* Calloc-like allocator.  */
+
+size_t
+__attribute__ ((noinline))
+test_calloc (size_t cnt, size_t sz)
+{
+  void *ch = calloc_func (cnt, sz);
+  size_t ret = __builtin_dynamic_object_size (ch, 0);
+  __builtin_free (ch);
+  return ret;
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_calloc (size_t cnt, size_t sz)
+{
+  void *ch = __builtin_calloc (cnt, sz);
+  size_t ret = __builtin_dynamic_object_size (ch, 0);
+  __builtin_free (ch);
+  return ret;
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_calloc_cond (int cond1, int cond2)
+{
+  void *ch = __builtin_calloc (cond1 ? 32 : 64, cond2 ? 1024 : 16);
+  size_t ret = __builtin_dynamic_object_size (ch, 0);
+  __builtin_free (ch);
+  return ret;
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_calloc_condphi (size_t cnt, size_t sz, int cond)
+{
+  struct
+    {
+      int a;
+      char b;
+    } bin[cnt];
+
+  char *ch = __builtin_calloc (cnt, sz);
+  size_t ret = __builtin_dynamic_object_size (cond ? ch : (void *) &bin, 0);
+
+  __builtin_free (ch);
+  return ret;
+}
+
+/* Passthrough functions.  */
+
+size_t
+__attribute__ ((noinline))
+test_passthrough (size_t sz, char *in)
+{
+  char *bin = __builtin_malloc (sz);
+  char *dest = __builtin_memcpy (bin, in, sz);
+
+  size_t ret = __builtin_dynamic_object_size (dest, 0);
+  __builtin_free (bin);
+  return ret;
+}
+
+size_t
+__attribute__ ((noinline))
+test_passthrough_nonssa (char *in)
+{
+  char bin[__builtin_strlen (in) + 1];
+  char *dest = __builtin_memcpy (bin, in, __builtin_strlen (in) + 1);
+
+  return __builtin_dynamic_object_size (dest, 0);
+}
+
+/* Variable length arrays.  */
+size_t
+__attribute__ ((noinline))
+test_dynarray (size_t sz)
+{
+  char bin[sz];
+
+  return __builtin_dynamic_object_size (bin, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_dynarray_cond (int cond)
+{
+  char bin[cond ? 8 : 16];
+
+  return __builtin_dynamic_object_size (bin, 0);
+}
+
 size_t
 __attribute__ ((noinline))
 test_deploop (size_t sz, size_t cond)
@@ -29,7 +245,7 @@ test_deploop (size_t sz, size_t cond)
 
   for (size_t i = 0; i < sz; i++)
     if (i == cond)
-      bin = __builtin_alloca (64);
+      bin = __builtin_alloca (sz);
 
   return __builtin_dynamic_object_size (bin, 0);
 }
@@ -68,10 +284,61 @@ unsigned nfails = 0;
 int
 main (int argc, char **argv)
 {
+  size_t outsz = test_unknown (32, 42);
+  if (outsz != -1 && outsz != 32)
+    FAIL ();
+  if (test_malloc (2048) != 2048)
+    FAIL ();
+  if (test_builtin_malloc (2048) != 2048)
+    FAIL ();
+  if (test_builtin_malloc_cond (1) != 32)
+    FAIL ();
+  if (test_builtin_malloc_cond (0) != 64)
+    FAIL ();
   if (test_builtin_malloc_condphi (1) != 32)
     FAIL ();
   if (test_builtin_malloc_condphi (0) != 64)
     FAIL ();
+  if (test_builtin_malloc_condphi2 (1, 128) != 128)
+    FAIL ();
+  if (test_builtin_malloc_condphi2 (0, 128) != 64)
+    FAIL ();
+  if (test_builtin_malloc_condphi3 (1, 128, 256) != 128)
+    FAIL ();
+  if (test_builtin_malloc_condphi3 (0, 128, 256) != 256)
+    FAIL ();
+  if (test_builtin_malloc_condphi4 (128, 1) != 64)
+    FAIL ();
+  if (test_builtin_malloc_condphi4 (128, 0) != sizeof (void *))
+    FAIL ();
+  if (test_builtin_malloc_condphi5 (128, 0, argv[0]) != -1)
+    FAIL ();
+  if (test_calloc (2048, 4) != 2048 * 4)
+    FAIL ();
+  if (test_builtin_calloc (2048, 8) != 2048 * 8)
+    FAIL ();
+  if (test_builtin_calloc_cond (0, 0) != 64 * 16)
+    FAIL ();
+  if (test_builtin_calloc_cond (1, 1) != 32 * 1024)
+    FAIL ();
+  if (test_builtin_calloc_condphi (128, 1, 0)
+      != 128 * sizeof (struct { int a; char b; }))
+    FAIL ();
+  if (test_builtin_calloc_condphi (128, 1, 1) != 128)
+    FAIL ();
+  if (test_passthrough (__builtin_strlen (argv[0]) + 1, argv[0])
+      != __builtin_strlen (argv[0]) + 1)
+    FAIL ();
+  if (test_passthrough_nonssa (argv[0]) != __builtin_strlen (argv[0]) + 1)
+    FAIL ();
+  if (test_dynarray (__builtin_strlen (argv[0])) != __builtin_strlen (argv[0]))
+    FAIL ();
+  if (test_dynarray_cond (0) != 16)
+    FAIL ();
+  if (test_dynarray_cond (1) != 8)
+    FAIL ();
+  if (test_deploop (128, 4) != 128)
+    FAIL ();
   if (test_deploop (128, 129) != 32)
     FAIL ();
   if (test_parmsz_simple (argv[0], __builtin_strlen (argv[0]) + 1)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
index 265c87ed6fb..06d442796cb 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
@@ -135,10 +135,17 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&extb[5], 0) != sizeof (extb) - 5)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (var, 0) != x + 10)
+    abort ();
+  if (__builtin_object_size (var + 10, 0) != x)
+    abort ();
+#else
   if (__builtin_object_size (var, 0) != (size_t) -1)
     abort ();
   if (__builtin_object_size (var + 10, 0) != (size_t) -1)
     abort ();
+#endif
   if (__builtin_object_size (&var[5], 0) != (size_t) -1)
     abort ();
   if (__builtin_object_size (zerol, 0) != 0)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
index 5051fea47c3..2364f2d6afd 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-2.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
@@ -137,16 +137,30 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&extc[5].c[3], 1) != (size_t) -1)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (var, 1) != x + 10)
+    abort ();
+  if (__builtin_object_size (var + 10, 1) != x)
+    abort ();
+#else
   if (__builtin_object_size (var, 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (var + 10, 1) != (size_t) -1)
     abort ();
+#endif
   if (__builtin_object_size (&var[5], 1) != (size_t) -1)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (vara, 1) != (x + 10) * sizeof (struct A))
+    abort ();
+  if (__builtin_object_size (vara + 10, 1) != x * sizeof (struct A))
+    abort ();    
+#else
   if (__builtin_object_size (vara, 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (vara + 10, 1) != (size_t) -1)
     abort ();    
+#endif
   if (__builtin_object_size (&vara[5], 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (&vara[0].a, 1) != sizeof (vara[0].a))
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
index 1d92627266b..753ee4a1a4f 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-3.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
@@ -140,10 +140,17 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&extb[5], 2) != sizeof (extb) - 5)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (var, 2) != x + 10)
+    abort ();
+  if (__builtin_object_size (var + 10, 2) != x)
+    abort ();
+#else
   if (__builtin_object_size (var, 2) != 0)
     abort ();
   if (__builtin_object_size (var + 10, 2) != 0)
     abort ();
+#endif
   if (__builtin_object_size (&var[5], 2) != 0)
     abort ();
   if (__builtin_object_size (zerol, 2) != 0)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
index 9da3537a5f7..c383385e060 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-4.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
@@ -150,16 +150,30 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&extc[5].c[3], 3) != 0)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (var, 3) != x + 10)
+    abort ();
+  if (__builtin_object_size (var + 10, 3) != x)
+    abort ();
+#else
   if (__builtin_object_size (var, 3) != 0)
     abort ();
   if (__builtin_object_size (var + 10, 3) != 0)
     abort ();
+#endif
   if (__builtin_object_size (&var[5], 3) != 0)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (vara, 3) != (x + 10) * sizeof (struct A))
+    abort ();
+  if (__builtin_object_size (vara + 10, 3) != x * sizeof (struct A))
+    abort ();    
+#else
   if (__builtin_object_size (vara, 3) != 0)
     abort ();
   if (__builtin_object_size (vara + 10, 3) != 0)
     abort ();    
+#endif
   if (__builtin_object_size (&vara[5], 3) != 0)
     abort ();
   if (__builtin_object_size (&vara[0].a, 3) != sizeof (vara[0].a))
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index 47e1c27bfd3..6bcd1ef78b9 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -751,7 +751,8 @@ alloc_object_size (const gcall *call, int object_size_type)
   gcc_assert (is_gimple_call (call));
 
   tree calltype;
-  if (tree callfn = gimple_call_fndecl (call))
+  tree callfn = gimple_call_fndecl (call);
+  if (callfn)
     calltype = TREE_TYPE (callfn);
   else
     calltype = gimple_call_fntype (call);
@@ -771,12 +772,13 @@ alloc_object_size (const gcall *call, int object_size_type)
       if (TREE_CHAIN (p))
         arg2 = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (p)))-1;
     }
+  else if (gimple_call_builtin_p (call, BUILT_IN_NORMAL)
+	   && callfn && ALLOCA_FUNCTION_CODE_P (DECL_FUNCTION_CODE (callfn)))
+  arg1 = 0;
 
-  if (arg1 < 0 || arg1 >= (int)gimple_call_num_args (call)
-      || TREE_CODE (gimple_call_arg (call, arg1)) != INTEGER_CST
-      || (arg2 >= 0
-	  && (arg2 >= (int)gimple_call_num_args (call)
-	      || TREE_CODE (gimple_call_arg (call, arg2)) != INTEGER_CST)))
+  /* Non-const arguments are OK here, let the caller handle constness.  */
+  if (arg1 < 0 || arg1 >= (int) gimple_call_num_args (call)
+      || arg2 >= (int) gimple_call_num_args (call))
     return size_unknown (object_size_type);
 
   tree bytes = NULL_TREE;
@@ -787,7 +789,10 @@ alloc_object_size (const gcall *call, int object_size_type)
   else if (arg1 >= 0)
     bytes = fold_convert (sizetype, gimple_call_arg (call, arg1));
 
-  return bytes;
+  if (bytes)
+    return STRIP_NOPS (bytes);
+
+  return size_unknown (object_size_type);
 }
 
 
@@ -1242,6 +1247,9 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call)
   gcc_assert (osi->pass == 0);
   tree bytes = alloc_object_size (call, object_size_type);
 
+  if (!(object_size_type & OST_DYNAMIC) && TREE_CODE (bytes) != INTEGER_CST)
+    bytes = size_unknown (object_size_type);
+
   object_sizes_set (osi, varno, bytes, bytes);
 }
 
-- 
2.33.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* [PATCH v6 4/4] tree-object-size: Dynamic sizes for ADDR_EXPR
  2022-01-11  8:57 ` [PATCH v6 " Siddhesh Poyarekar
                     ` (2 preceding siblings ...)
  2022-01-11  8:57   ` [PATCH v6 3/4] tree-object-size: Handle GIMPLE_CALL Siddhesh Poyarekar
@ 2022-01-11  8:57   ` Siddhesh Poyarekar
  2022-01-11  9:47     ` Jakub Jelinek
  3 siblings, 1 reply; 97+ messages in thread
From: Siddhesh Poyarekar @ 2022-01-11  8:57 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Allow returning dynamic expressions from ADDR_EXPR for
__builtin_dynamic_object_size and also allow offsets to be dynamic.

gcc/ChangeLog:

	* tree-object-size.c (size_valid_p): New function.
	(size_for_offset): Remove OFFSET constness assertion.
	(addr_object_size): Build dynamic expressions for object
	sizes and use size_valid_p to decide if it is valid for the
	given OBJECT_SIZE_TYPE.
	(compute_builtin_object_size): Allow dynamic offsets when
	computing size at O0.
	(call_object_size): Call size_valid_p.
	(plus_stmt_object_size): Allow non-constant offset and use
	size_valid_p to decide if it is valid for the given
	OBJECT_SIZE_TYPE.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-dynamic-object-size-0.c: Add new tests.
	* gcc.dg/builtin-object-size-1.c (test1)
	[__builtin_object_size]: Adjust expected output for dynamic
	object sizes.
	* gcc.dg/builtin-object-size-2.c (test1)
	[__builtin_object_size]: Likewise.
	* gcc.dg/builtin-object-size-3.c (test1)
	[__builtin_object_size]: Likewise.
	* gcc.dg/builtin-object-size-4.c (test1)
	[__builtin_object_size]: Likewise.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 .../gcc.dg/builtin-dynamic-object-size-0.c    | 156 ++++++++++++++++++
 gcc/testsuite/gcc.dg/builtin-object-size-1.c  |  30 +++-
 gcc/testsuite/gcc.dg/builtin-object-size-2.c  |  43 ++++-
 gcc/testsuite/gcc.dg/builtin-object-size-3.c  |  25 ++-
 gcc/testsuite/gcc.dg/builtin-object-size-4.c  |  17 +-
 gcc/tree-object-size.c                        |  94 ++++++-----
 6 files changed, 299 insertions(+), 66 deletions(-)

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 930fd49cd16..dd8dc99a580 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -250,6 +250,79 @@ test_deploop (size_t sz, size_t cond)
   return __builtin_dynamic_object_size (bin, 0);
 }
 
+/* Address expressions.  */
+
+struct dynarray_struct
+{
+  long a;
+  char c[16];
+  int b;
+};
+
+size_t
+__attribute__ ((noinline))
+test_dynarray_struct (size_t sz, size_t off)
+{
+  struct dynarray_struct bin[sz];
+
+  return __builtin_dynamic_object_size (&bin[off].c, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_dynarray_struct_subobj (size_t sz, size_t off)
+{
+  struct dynarray_struct bin[sz];
+
+  return __builtin_dynamic_object_size (&bin[off].c[4], 1);
+}
+
+size_t
+__attribute__ ((noinline))
+test_dynarray_struct_subobj2 (size_t sz, size_t off, size_t *objsz)
+{
+  struct dynarray_struct2
+    {
+      long a;
+      int b;
+      char c[sz];
+    };
+
+  struct dynarray_struct2 bin;
+
+  *objsz = sizeof (bin);
+
+  return __builtin_dynamic_object_size (&bin.c[off], 1);
+}
+
+size_t
+__attribute__ ((noinline))
+test_substring (size_t sz, size_t off)
+{
+  char str[sz];
+
+  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)))
 __attribute__ ((noinline))
@@ -258,6 +331,14 @@ 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__ ((access (__read_write__, 1, 2)))
 __attribute__ ((noinline))
@@ -266,6 +347,14 @@ test_parmsz_scaled (int *obj, size_t sz)
   return __builtin_dynamic_object_size (obj, 0);
 }
 
+size_t
+__attribute__ ((noinline))
+__attribute__ ((access (__read_write__, 1, 2)))
+test_parmsz_scaled_off (int *obj, size_t sz, size_t off)
+{
+  return __builtin_dynamic_object_size (obj + off, 0);
+}
+
 size_t
 __attribute__ ((access (__read_write__, 1, 3)))
 __attribute__ ((noinline))
@@ -274,6 +363,23 @@ test_parmsz_unknown (void *obj, void *unknown, size_t sz, int cond)
   return __builtin_dynamic_object_size (cond ? obj : unknown, 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() ({ \
@@ -333,6 +439,32 @@ main (int argc, char **argv)
     FAIL ();
   if (test_dynarray (__builtin_strlen (argv[0])) != __builtin_strlen (argv[0]))
     FAIL ();
+  if (test_dynarray_struct (42, 4) !=
+      ((42 - 4) * sizeof (struct dynarray_struct)
+       - __builtin_offsetof (struct dynarray_struct, c)))
+    FAIL ();
+  if (test_dynarray_struct (42, 48) != 0)
+    FAIL ();
+  if (test_substring (128, 4) != 128 - 4)
+    FAIL ();
+  if (test_substring (128, 142) != 0)
+    FAIL ();
+  if (test_dynarray_struct_subobj (42, 4) != 16 - 4)
+    FAIL ();
+  if (test_dynarray_struct_subobj (42, 48) != 0)
+    FAIL ();
+  size_t objsz = 0;
+  if (test_dynarray_struct_subobj2 (42, 4, &objsz)
+    != objsz - 4 - sizeof (long) - sizeof (int))
+    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)
@@ -349,6 +481,30 @@ main (int argc, char **argv)
     FAIL ();
   if (test_parmsz_unknown (argv[0], argv[0], __builtin_strlen (argv[0]) + 1, 0)
       != -1)
+  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 ();
+  if (test_parmsz_scaled_off (arr, 42, 2) != 40 * sizeof (int))
+    FAIL ();
+  if (test_loop (arr, 42, 0, 32, 1) != 10 * sizeof (int))
+    FAIL ();
+  if (test_loop (arr, 42, 32, -1, -1) != 0)
+    FAIL ();
+  if (test_loop (arr, 42, 32, 10, -1) != 32 * sizeof (int))
+    FAIL ();
+  if (test_loop (arr, 42, 42, 0, -1) != 42 * sizeof (int))
+    FAIL ();
+  if (test_loop (arr, 42, 44, 0, -1) != 0)
+    FAIL ();
+  if (test_loop (arr, 42, 20, 52, 1) != 0)
     FAIL ();
 
   if (nfails > 0)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
index 06d442796cb..161f426ec0b 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
@@ -81,30 +81,56 @@ test1 (void *q, int x)
     r = malloc (30);
   else
     r = calloc (2, 16);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0) != (x < 20 ? 30 : 2 * 16))
+    abort ();
+#else
   /* We may duplicate this test onto the two exit paths.  On one path
      the size will be 32, the other it will be 30.  If we don't duplicate
      this test, then the size will be 32.  */
   if (__builtin_object_size (r, 0) != 2 * 16
       && __builtin_object_size (r, 0) != 30)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
     r = calloc (2, 14);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0) != (x < 20 ? 30 : 2 * 14))
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != 30)
     abort ();
+#endif
   if (x < 30)
     r = malloc (sizeof (a));
   else
     r = &a.a[3];
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0) != (x < 30 ? sizeof (a) : sizeof (a) - 3))
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != sizeof (a))
     abort ();
+#endif
   r = memcpy (r, "a", 2);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0) != (x < 30 ? sizeof (a) : sizeof (a) - 3))
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != sizeof (a))
     abort ();
+#endif
   r = memcpy (r + 2, "b", 2) + 2;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0)
+      != (x < 30 ? sizeof (a) - 4 : sizeof (a) - 7))
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != sizeof (a) - 4)
     abort ();
+#endif
   r = &a.a[4];
   r = memset (r, 'a', 2);
   if (__builtin_object_size (r, 0)
@@ -140,14 +166,16 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (var + 10, 0) != x)
     abort ();
+  if (__builtin_object_size (&var[5], 0) != x + 5)
+    abort ();
 #else
   if (__builtin_object_size (var, 0) != (size_t) -1)
     abort ();
   if (__builtin_object_size (var + 10, 0) != (size_t) -1)
     abort ();
-#endif
   if (__builtin_object_size (&var[5], 0) != (size_t) -1)
     abort ();
+#endif
   if (__builtin_object_size (zerol, 0) != 0)
     abort ();
   if (__builtin_object_size (&zerol, 0) != 0)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
index 2364f2d6afd..2729538da17 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-2.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
@@ -75,30 +75,56 @@ test1 (void *q, int x)
     r = malloc (30);
   else
     r = calloc (2, 16);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 1) != (x < 20 ? 30 : 2 * 16))
+    abort ();
+#else
   /* We may duplicate this test onto the two exit paths.  On one path
      the size will be 32, the other it will be 30.  If we don't duplicate
      this test, then the size will be 32.  */
   if (__builtin_object_size (r, 1) != 2 * 16
       && __builtin_object_size (r, 1) != 30)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
     r = calloc (2, 14);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 1) != (x < 20 ? 30 : 2 * 14))
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != 30)
     abort ();
+#endif
   if (x < 30)
     r = malloc (sizeof (a));
   else
     r = &a.a[3];
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 1) != (x < 30 ? sizeof (a) : sizeof (a) - 3))
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a))
     abort ();
+#endif
   r = memcpy (r, "a", 2);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 1) != (x < 30 ? sizeof (a) : sizeof (a) - 3))
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a))
     abort ();
+#endif
   r = memcpy (r + 2, "b", 2) + 2;
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 0)
+      != (x < 30 ? sizeof (a) - 4 : sizeof (a) - 7))
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a) - 4)
     abort ();
+#endif
   r = &a.a[4];
   r = memset (r, 'a', 2);
   if (__builtin_object_size (r, 1) != sizeof (a.a) - 4)
@@ -142,27 +168,28 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (var + 10, 1) != x)
     abort ();
+  if (__builtin_object_size (&var[5], 1) != x + 5)
+    abort ();
+  if (__builtin_object_size (vara, 1) != (x + 10) * sizeof (struct A))
+    abort ();
+  if (__builtin_object_size (vara + 10, 1) != x * sizeof (struct A))
+    abort ();    
+  if (__builtin_object_size (&vara[5], 1) != (x + 5) * sizeof (struct A))
+    abort ();
 #else
   if (__builtin_object_size (var, 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (var + 10, 1) != (size_t) -1)
     abort ();
-#endif
   if (__builtin_object_size (&var[5], 1) != (size_t) -1)
     abort ();
-#ifdef __builtin_object_size
-  if (__builtin_object_size (vara, 1) != (x + 10) * sizeof (struct A))
-    abort ();
-  if (__builtin_object_size (vara + 10, 1) != x * sizeof (struct A))
-    abort ();    
-#else
   if (__builtin_object_size (vara, 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (vara + 10, 1) != (size_t) -1)
     abort ();    
-#endif
   if (__builtin_object_size (&vara[5], 1) != (size_t) -1)
     abort ();
+#endif
   if (__builtin_object_size (&vara[0].a, 1) != sizeof (vara[0].a))
     abort ();
   if (__builtin_object_size (&vara[10].a[0], 1) != sizeof (vara[0].a))
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
index 753ee4a1a4f..db31171a8bd 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-3.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
@@ -42,9 +42,17 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (q, 2) != 0)
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 2)
+      != (x < 0
+	  ? sizeof (a) - __builtin_offsetof (struct A, a) - 9
+	  : sizeof (a) - __builtin_offsetof (struct A, c) - 1))
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, c) - 1)
     abort ();
+#endif
   if (x < 6)
     r = &w[2].a[1];
   else
@@ -58,15 +66,28 @@ test1 (void *q, int x)
   if (__builtin_object_size (&y.b, 2)
       != sizeof (a) - __builtin_offsetof (struct A, b))
     abort ();
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 2)
+      != (x < 6
+	  ? 2 * sizeof (w[0]) - __builtin_offsetof (struct A, a) - 1
+	  : sizeof (a) - __builtin_offsetof (struct A, a) - 6))
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, a) - 6)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
     r = calloc (2, 16);
+#ifdef __builtin_object_size
+  if (__builtin_object_size (r, 2) != (x < 20 ? 30 : 2 * 16))
+    abort ();
+#else
   if (__builtin_object_size (r, 2) != 30)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
@@ -145,14 +166,16 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (var + 10, 2) != x)
     abort ();
+  if (__builtin_object_size (&var[5], 2) != x + 5)
+    abort ();
 #else
   if (__builtin_object_size (var, 2) != 0)
     abort ();
   if (__builtin_object_size (var + 10, 2) != 0)
     abort ();
-#endif
   if (__builtin_object_size (&var[5], 2) != 0)
     abort ();
+#endif
   if (__builtin_object_size (zerol, 2) != 0)
     abort ();
   if (__builtin_object_size (&zerol, 2) != 0)
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
index c383385e060..f644890dd14 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-4.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
@@ -155,27 +155,28 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (var + 10, 3) != x)
     abort ();
+  if (__builtin_object_size (&var[5], 3) != x + 5)
+    abort ();
+  if (__builtin_object_size (vara, 3) != (x + 10) * sizeof (struct A))
+    abort ();
+  if (__builtin_object_size (vara + 10, 3) != x * sizeof (struct A))
+    abort ();    
+  if (__builtin_object_size (&vara[5], 3) != (x + 5) * sizeof (struct A))
+    abort ();
 #else
   if (__builtin_object_size (var, 3) != 0)
     abort ();
   if (__builtin_object_size (var + 10, 3) != 0)
     abort ();
-#endif
   if (__builtin_object_size (&var[5], 3) != 0)
     abort ();
-#ifdef __builtin_object_size
-  if (__builtin_object_size (vara, 3) != (x + 10) * sizeof (struct A))
-    abort ();
-  if (__builtin_object_size (vara + 10, 3) != x * sizeof (struct A))
-    abort ();    
-#else
   if (__builtin_object_size (vara, 3) != 0)
     abort ();
   if (__builtin_object_size (vara + 10, 3) != 0)
     abort ();    
-#endif
   if (__builtin_object_size (&vara[5], 3) != 0)
     abort ();
+#endif
   if (__builtin_object_size (&vara[0].a, 3) != sizeof (vara[0].a))
     abort ();
   if (__builtin_object_size (&vara[10].a[0], 3) != sizeof (vara[0].a))
diff --git a/gcc/tree-object-size.c b/gcc/tree-object-size.c
index 6bcd1ef78b9..1063a8dcfef 100644
--- a/gcc/tree-object-size.c
+++ b/gcc/tree-object-size.c
@@ -107,6 +107,14 @@ size_unknown_p (tree val, int object_size_type)
 	  ? integer_zerop (val) : integer_all_onesp (val));
 }
 
+/* Return true if VAL is represents a valid size for OBJECT_SIZE_TYPE.  */
+
+static inline bool
+size_valid_p (tree val, int object_size_type)
+{
+  return ((object_size_type & OST_DYNAMIC) || TREE_CODE (val) == INTEGER_CST);
+}
+
 /* Return true if VAL is usable as an object size in the object_sizes
    vectors.  */
 
@@ -341,7 +349,6 @@ init_offset_limit (void)
 static tree
 size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE)
 {
-  gcc_checking_assert (TREE_CODE (offset) == INTEGER_CST);
   gcc_checking_assert (types_compatible_p (TREE_TYPE (sz), sizetype));
 
   /* For negative offsets, if we have a distinct WHOLESIZE, use it to get a net
@@ -540,18 +547,11 @@ 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))
-	{
-	  tree offset = TREE_OPERAND (pt_var, 1);
-	  if (TREE_CODE (offset) != INTEGER_CST
-	      || TREE_CODE (sz) != INTEGER_CST)
-	    sz = wholesize = size_unknown (object_size_type);
-	  else
-	    sz = size_for_offset (sz, offset, wholesize);
-	}
+	sz = size_for_offset (sz, TREE_OPERAND (pt_var, 1), wholesize);
 
       if (!size_unknown_p (sz, object_size_type)
-	  && TREE_CODE (sz) == INTEGER_CST
-	  && compare_tree_int (sz, offset_limit) < 0)
+	  && (TREE_CODE (sz) != INTEGER_CST
+	      || compare_tree_int (sz, offset_limit) < 0))
 	{
 	  pt_var_size = sz;
 	  pt_var_wholesize = wholesize;
@@ -571,8 +571,9 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 
   if (pt_var_size)
     {
-      /* Validate the size determined above.  */
-      if (compare_tree_int (pt_var_size, offset_limit) >= 0)
+      /* Validate the size determined above if it is a constant.  */
+      if (TREE_CODE (pt_var_size) == INTEGER_CST
+	  && compare_tree_int (pt_var_size, offset_limit) >= 0)
 	return false;
     }
 
@@ -596,7 +597,7 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 	    var = TREE_OPERAND (var, 0);
 	  if (! TYPE_SIZE_UNIT (TREE_TYPE (var))
 	      || ! tree_fits_uhwi_p (TYPE_SIZE_UNIT (TREE_TYPE (var)))
-	      || (pt_var_size
+	      || (pt_var_size && TREE_CODE (pt_var_size) == INTEGER_CST
 		  && tree_int_cst_lt (pt_var_size,
 				      TYPE_SIZE_UNIT (TREE_TYPE (var)))))
 	    var = pt_var;
@@ -610,17 +611,11 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 		switch (TREE_CODE (v))
 		  {
 		  case ARRAY_REF:
-		    if (TYPE_SIZE_UNIT (TREE_TYPE (TREE_OPERAND (v, 0)))
-			&& TREE_CODE (TREE_OPERAND (v, 1)) == INTEGER_CST)
+		    if (TYPE_SIZE_UNIT (TREE_TYPE (TREE_OPERAND (v, 0))))
 		      {
 			tree domain
 			  = TYPE_DOMAIN (TREE_TYPE (TREE_OPERAND (v, 0)));
-			if (domain
-			    && TYPE_MAX_VALUE (domain)
-			    && TREE_CODE (TYPE_MAX_VALUE (domain))
-			       == INTEGER_CST
-			    && tree_int_cst_lt (TREE_OPERAND (v, 1),
-						TYPE_MAX_VALUE (domain)))
+			if (domain && TYPE_MAX_VALUE (domain))
 			  {
 			    v = NULL_TREE;
 			    break;
@@ -687,20 +682,20 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
 	var = pt_var;
 
       if (var != pt_var)
-	var_size = TYPE_SIZE_UNIT (TREE_TYPE (var));
+	{
+	  var_size = TYPE_SIZE_UNIT (TREE_TYPE (var));
+	  if (!TREE_CONSTANT (var_size))
+	    var_size = get_or_create_ssa_default_def (cfun, var_size);
+	  if (!var_size)
+	    return false;
+	}
       else if (!pt_var_size)
 	return false;
       else
 	var_size = pt_var_size;
       bytes = compute_object_offset (TREE_OPERAND (ptr, 0), var);
       if (bytes != error_mark_node)
-	{
-	  if (TREE_CODE (bytes) == INTEGER_CST
-	      && tree_int_cst_lt (var_size, bytes))
-	    bytes = size_zero_node;
-	  else
-	    bytes = size_binop (MINUS_EXPR, var_size, bytes);
-	}
+	bytes = size_for_offset (var_size, bytes);
       if (var != pt_var
 	  && pt_var_size
 	  && TREE_CODE (pt_var) == MEM_REF
@@ -709,11 +704,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)
 	    {
-	      if (TREE_CODE (bytes2) == INTEGER_CST
-		  && tree_int_cst_lt (pt_var_size, bytes2))
-		bytes2 = size_zero_node;
-	      else
-		bytes2 = size_binop (MINUS_EXPR, pt_var_size, bytes2);
+	      bytes2 = size_for_offset (pt_var_size, bytes2);
 	      bytes = size_binop (MIN_EXPR, bytes, bytes2);
 	    }
 	}
@@ -729,14 +720,18 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
       wholebytes = pt_var_wholesize;
     }
 
-  if (TREE_CODE (bytes) != INTEGER_CST
-      || TREE_CODE (wholebytes) != INTEGER_CST)
-    return false;
+  if (!size_unknown_p (bytes, object_size_type)
+      && size_valid_p (bytes, object_size_type)
+      && !size_unknown_p (bytes, object_size_type)
+      && size_valid_p (wholebytes, object_size_type))
+    {
+      *psize = bytes;
+      if (pwholesize)
+	*pwholesize = wholebytes;
+      return true;
+    }
 
-  *psize = bytes;
-  if (pwholesize)
-    *pwholesize = wholebytes;
-  return true;
+  return false;
 }
 
 
@@ -1058,11 +1053,11 @@ compute_builtin_object_size (tree ptr, int object_size_type,
 	      tree offset = gimple_assign_rhs2 (def);
 	      ptr = gimple_assign_rhs1 (def);
 
-	      if (tree_fits_shwi_p (offset)
+	      if (((object_size_type & OST_DYNAMIC)
+		   || tree_fits_shwi_p (offset))
 		  && compute_builtin_object_size (ptr, object_size_type,
 						  psize))
 		{
-		  /* Return zero when the offset is out of bounds.  */
 		  *psize = size_for_offset (*psize, offset);
 		  return true;
 		}
@@ -1247,7 +1242,7 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call)
   gcc_assert (osi->pass == 0);
   tree bytes = alloc_object_size (call, object_size_type);
 
-  if (!(object_size_type & OST_DYNAMIC) && TREE_CODE (bytes) != INTEGER_CST)
+  if (!size_valid_p (bytes, object_size_type))
     bytes = size_unknown (object_size_type);
 
   object_sizes_set (osi, varno, bytes, bytes);
@@ -1328,9 +1323,8 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
     return false;
 
   /* Handle PTR + OFFSET here.  */
-  if (TREE_CODE (op1) == INTEGER_CST
-      && (TREE_CODE (op0) == SSA_NAME
-	  || TREE_CODE (op0) == ADDR_EXPR))
+  if (size_valid_p (op1, object_size_type)
+      && (TREE_CODE (op0) == SSA_NAME || TREE_CODE (op0) == ADDR_EXPR))
     {
       if (TREE_CODE (op0) == SSA_NAME)
 	{
@@ -1361,6 +1355,10 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
   else
     bytes = wholesize = size_unknown (object_size_type);
 
+  if (!size_valid_p (bytes, object_size_type)
+      || !size_valid_p (wholesize, object_size_type))
+    bytes = wholesize = size_unknown (object_size_type);
+
   if (object_sizes_set (osi, varno, bytes, wholesize))
     osi->changed = true;
   return reexamine;
-- 
2.33.1


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v6 1/4] tree-object-size: Support dynamic sizes in conditions
  2022-01-11  8:57   ` [PATCH v6 1/4] tree-object-size: Support dynamic sizes in conditions Siddhesh Poyarekar
@ 2022-01-11  9:43     ` Jakub Jelinek
  2022-01-11  9:44       ` Siddhesh Poyarekar
  0 siblings, 1 reply; 97+ messages in thread
From: Jakub Jelinek @ 2022-01-11  9:43 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Tue, Jan 11, 2022 at 02:27:47PM +0530, Siddhesh Poyarekar wrote:
> Handle GIMPLE_PHI and conditionals specially for dynamic objects,
> returning PHI/conditional expressions instead of just a MIN/MAX
> estimate.
> 
> This makes the returned object size variable for loops and conditionals,
> so tests need to be adjusted to look for precise size in some cases.
> builtin-dynamic-object-size-5.c had to be modified to only look for
> success in maximum object size case and skip over the minimum object
> size tests because the result is no longer a compile time constant.
> 
> I also added some simple tests to exercise conditionals with dynamic
> object sizes.

Ok, but IMO please fix PR103961 first before committing this patchset.

> gcc/ChangeLog:
> 
> 	* builtins.c (fold_builtin_object_size): Adjust for dynamic size
> 	expressions.
> 	* tree-object-size.c: Include gimplify-me.h.
> 	(struct object_size_info): New member UNKNOWNS.
> 	(size_initval_p, size_usable_p, object_sizes_get_raw): New
> 	functions.
> 	(object_sizes_get): Return suitable gimple variable for
> 	object size.
> 	(bundle_sizes): New function.
> 	(object_sizes_set): Use it and handle dynamic object size
> 	expressions.
> 	(object_sizes_set_temp): New function.
> 	(size_for_offset): Adjust for dynamic size expressions.
> 	(emit_phi_nodes, propagate_unknowns, gimplify_size_expressions):
> 	New functions.
> 	(compute_builtin_object_size): Call gimplify_size_expressions
> 	for OST_DYNAMIC.
> 	(dynamic_object_size): New function.
> 	(cond_expr_object_size): Use it.
> 	(phi_dynamic_object_size): New function.
> 	(collect_object_sizes_for): Call it for OST_DYNAMIC.  Adjust to
> 	accommodate dynamic object sizes.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* gcc.dg/builtin-dynamic-object-size-0.c: New tests.
> 	* gcc.dg/builtin-dynamic-object-size-10.c: Add comment.
> 	* gcc.dg/builtin-dynamic-object-size-5-main.c: New file.
> 	* gcc.dg/builtin-dynamic-object-size-5.c: Use it and change test
> 	to dg-do run.
> 	* gcc.dg/builtin-object-size-5.c [!N]: Define N.
> 	(test1, test2, test3, test4) [__builtin_object_size]: Expect
> 	exact result for __builtin_dynamic_object_size.
> 	* gcc.dg/builtin-object-size-1.c [__builtin_object_size]: Expect
> 	exact size expressions for __builtin_dynamic_object_size.
> 	* gcc.dg/builtin-object-size-2.c [__builtin_object_size]:
> 	Likewise.
> 	* gcc.dg/builtin-object-size-3.c [__builtin_object_size]:
> 	Likewise.
> 	* gcc.dg/builtin-object-size-4.c [__builtin_object_size]:
> 	Likewise.
> 	* gcc.dg/builtin-object-size-5.c [__builtin_object_size]:
> 	Likewise.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v6 1/4] tree-object-size: Support dynamic sizes in conditions
  2022-01-11  9:43     ` Jakub Jelinek
@ 2022-01-11  9:44       ` Siddhesh Poyarekar
  0 siblings, 0 replies; 97+ messages in thread
From: Siddhesh Poyarekar @ 2022-01-11  9:44 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 11/01/2022 15:13, Jakub Jelinek wrote:
> On Tue, Jan 11, 2022 at 02:27:47PM +0530, Siddhesh Poyarekar wrote:
>> Handle GIMPLE_PHI and conditionals specially for dynamic objects,
>> returning PHI/conditional expressions instead of just a MIN/MAX
>> estimate.
>>
>> This makes the returned object size variable for loops and conditionals,
>> so tests need to be adjusted to look for precise size in some cases.
>> builtin-dynamic-object-size-5.c had to be modified to only look for
>> success in maximum object size case and skip over the minimum object
>> size tests because the result is no longer a compile time constant.
>>
>> I also added some simple tests to exercise conditionals with dynamic
>> object sizes.
> 
> Ok, but IMO please fix PR103961 first before committing this patchset.
> 

+1, I'm on it right now.

Thanks,
Siddhesh

^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v6 2/4] tree-object-size: Handle function parameters
  2022-01-11  8:57   ` [PATCH v6 2/4] tree-object-size: Handle function parameters Siddhesh Poyarekar
@ 2022-01-11  9:44     ` Jakub Jelinek
  0 siblings, 0 replies; 97+ messages in thread
From: Jakub Jelinek @ 2022-01-11  9:44 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Tue, Jan 11, 2022 at 02:27:48PM +0530, Siddhesh Poyarekar wrote:
> Handle hints provided by __attribute__ ((access (...))) to compute
> dynamic sizes for objects.
> 
> gcc/ChangeLog:
> 
> 	* tree-object-size.c: Include tree-dfa.h.
> 	(parm_object_size): New function.
> 	(collect_object_sizes_for): Call it.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* gcc.dg/builtin-dynamic-object-size-0.c (test_parmsz_simple,
> 	test_parmsz_scaled, test_parmsz_unknown): New functions.
> 	(main): Call them.  Add new arguments argc and argv.
> 
> Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
> +      /* Walk through the parameters to pick the size parameter and safely
> +	 scale it by the type size.  */
> +      for (arg = fnargs; arg; arg = TREE_CHAIN (arg), ++argpos)
> +	{
> +	  if (argpos == access->sizarg && INTEGRAL_TYPE_P (TREE_TYPE (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);
> +		}
> +	      break;
> +	    }
> +	}

No need for those {}s around the if as body of for, just use
      for (arg = fnargs; arg; arg = TREE_CHAIN (arg), ++argpos)
	if (argpos == access->sizarg && INTEGRAL_TYPE_P (TREE_TYPE (arg)))
	  {
...
	    break;
	  }

Ok with that change.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

* Re: [PATCH v6 4/4] tree-object-size: Dynamic sizes for ADDR_EXPR
  2022-01-11  8:57   ` [PATCH v6 4/4] tree-object-size: Dynamic sizes for ADDR_EXPR Siddhesh Poyarekar
@ 2022-01-11  9:47     ` Jakub Jelinek
  0 siblings, 0 replies; 97+ messages in thread
From: Jakub Jelinek @ 2022-01-11  9:47 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Tue, Jan 11, 2022 at 02:27:50PM +0530, Siddhesh Poyarekar wrote:
> Allow returning dynamic expressions from ADDR_EXPR for
> __builtin_dynamic_object_size and also allow offsets to be dynamic.
> 
> gcc/ChangeLog:
> 
> 	* tree-object-size.c (size_valid_p): New function.
> 	(size_for_offset): Remove OFFSET constness assertion.
> 	(addr_object_size): Build dynamic expressions for object
> 	sizes and use size_valid_p to decide if it is valid for the
> 	given OBJECT_SIZE_TYPE.
> 	(compute_builtin_object_size): Allow dynamic offsets when
> 	computing size at O0.
> 	(call_object_size): Call size_valid_p.
> 	(plus_stmt_object_size): Allow non-constant offset and use
> 	size_valid_p to decide if it is valid for the given
> 	OBJECT_SIZE_TYPE.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* gcc.dg/builtin-dynamic-object-size-0.c: Add new tests.
> 	* gcc.dg/builtin-object-size-1.c (test1)
> 	[__builtin_object_size]: Adjust expected output for dynamic
> 	object sizes.
> 	* gcc.dg/builtin-object-size-2.c (test1)
> 	[__builtin_object_size]: Likewise.
> 	* gcc.dg/builtin-object-size-3.c (test1)
> 	[__builtin_object_size]: Likewise.
> 	* gcc.dg/builtin-object-size-4.c (test1)
> 	[__builtin_object_size]: Likewise.

> +/* Return true if VAL is represents a valid size for OBJECT_SIZE_TYPE.  */

s/is / as in the other patch.

Otherwise LGTM.

	Jakub


^ permalink raw reply	[flat|nested] 97+ messages in thread

end of thread, other threads:[~2022-01-11  9:47 UTC | newest]

Thread overview: 97+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-11-09 19:01 [PATCH 00/10] __builtin_dynamic_object_size Siddhesh Poyarekar
2021-11-09 19:01 ` [PATCH 01/10] tree-object-size: Replace magic numbers with enums Siddhesh Poyarekar
2021-11-19 16:00   ` Jakub Jelinek
2021-11-09 19:01 ` [PATCH 02/10] tree-object-size: Abstract object_sizes array Siddhesh Poyarekar
2021-11-19 16:18   ` Jakub Jelinek
2021-11-19 16:53     ` Siddhesh Poyarekar
2021-11-09 19:01 ` [PATCH 03/10] tree-object-size: Use tree instead of HOST_WIDE_INT Siddhesh Poyarekar
2021-11-19 17:06   ` Jakub Jelinek
2021-11-19 19:01     ` Siddhesh Poyarekar
2021-11-19 19:16       ` Jakub Jelinek
2021-11-22  8:41         ` Richard Biener
2021-11-22 10:11       ` Siddhesh Poyarekar
2021-11-22 10:31         ` Jakub Jelinek
2021-11-22 12:00           ` Siddhesh Poyarekar
2021-11-22 12:31             ` Siddhesh Poyarekar
2021-11-22 12:32               ` Jakub Jelinek
2021-11-23 11:58                 ` Jakub Jelinek
2021-11-23 13:33                   ` Siddhesh Poyarekar
2021-11-09 19:01 ` [PATCH 04/10] tree-object-size: Single pass dependency loop resolution Siddhesh Poyarekar
2021-11-23 12:07   ` Jakub Jelinek
2021-11-23 13:44     ` Siddhesh Poyarekar
2021-11-23 14:22       ` Jakub Jelinek
2021-11-09 19:01 ` [PATCH 05/10] __builtin_dynamic_object_size: Recognize builtin Siddhesh Poyarekar
2021-11-23 12:41   ` Jakub Jelinek
2021-11-23 13:53     ` Siddhesh Poyarekar
2021-11-23 14:00       ` Jakub Jelinek
2021-11-09 19:01 ` [PATCH 06/10] tree-object-size: Support dynamic sizes in conditions Siddhesh Poyarekar
2021-11-23 15:12   ` Jakub Jelinek
2021-11-23 15:36     ` Siddhesh Poyarekar
2021-11-23 15:38       ` Siddhesh Poyarekar
2021-11-23 16:17         ` Jakub Jelinek
2021-11-23 15:52       ` Jakub Jelinek
2021-11-23 16:00         ` Siddhesh Poyarekar
2021-11-23 16:19           ` Jakub Jelinek
2021-11-09 19:01 ` [PATCH 07/10] tree-object-size: Handle function parameters Siddhesh Poyarekar
2021-11-09 19:01 ` [PATCH 08/10] tree-object-size: Handle GIMPLE_CALL Siddhesh Poyarekar
2021-11-09 19:01 ` [PATCH 09/10] tree-object-size: Dynamic sizes for ADDR_EXPR Siddhesh Poyarekar
2021-11-09 19:01 ` [PATCH 10/10] tree-object-size: Handle dynamic offsets Siddhesh Poyarekar
2021-11-19 15:56 ` [PATCH 00/10] __builtin_dynamic_object_size Jakub Jelinek
2021-11-26  5:28 ` [PATCH v3 0/8] __builtin_dynamic_object_size Siddhesh Poyarekar
2021-11-26  5:28   ` [PATCH v3 1/8] tree-object-size: Replace magic numbers with enums Siddhesh Poyarekar
2021-11-26 16:46     ` Jakub Jelinek
2021-11-26 17:53       ` Siddhesh Poyarekar
2021-11-26 18:01         ` Jakub Jelinek
2021-11-26  5:28   ` [PATCH v3 2/8] tree-object-size: Abstract object_sizes array Siddhesh Poyarekar
2021-11-26 16:47     ` Jakub Jelinek
2021-11-26  5:28   ` [PATCH v3 3/8] tree-object-size: Save sizes as trees and support negative offsets Siddhesh Poyarekar
2021-11-26 16:56     ` Jakub Jelinek
2021-11-26 17:59       ` Siddhesh Poyarekar
2021-11-26 18:04         ` Jakub Jelinek
2021-11-26 18:07           ` Siddhesh Poyarekar
2021-11-26  5:28   ` [PATCH v3 4/8] __builtin_dynamic_object_size: Recognize builtin Siddhesh Poyarekar
2021-11-26  5:28   ` [PATCH v3 5/8] tree-object-size: Support dynamic sizes in conditions Siddhesh Poyarekar
2021-11-26  5:28   ` [PATCH v3 6/8] tree-object-size: Handle function parameters Siddhesh Poyarekar
2021-11-26  5:28   ` [PATCH v3 7/8] tree-object-size: Handle GIMPLE_CALL Siddhesh Poyarekar
2021-11-26  5:28   ` [PATCH v3 8/8] tree-object-size: Dynamic sizes for ADDR_EXPR Siddhesh Poyarekar
2021-11-26  5:38   ` [PATCH v3 0/8] __builtin_dynamic_object_size Siddhesh Poyarekar
2021-12-01 14:27 ` [PATCH v4 0/6] __builtin_dynamic_object_size Siddhesh Poyarekar
2021-12-01 14:27   ` [PATCH v4 1/6] tree-object-size: Use trees and support negative offsets Siddhesh Poyarekar
2021-12-15 15:21     ` Jakub Jelinek
2021-12-15 17:12       ` Siddhesh Poyarekar
2021-12-15 18:43         ` Jakub Jelinek
2021-12-16  0:41           ` Siddhesh Poyarekar
2021-12-16 15:49             ` Jakub Jelinek
2021-12-16 18:56               ` Siddhesh Poyarekar
2021-12-16 21:16                 ` Jakub Jelinek
2021-12-01 14:27   ` [PATCH v4 2/6] __builtin_dynamic_object_size: Recognize builtin Siddhesh Poyarekar
2021-12-15 15:24     ` Jakub Jelinek
2021-12-16  2:16       ` Siddhesh Poyarekar
2021-12-01 14:27   ` [PATCH v4 3/6] tree-object-size: Support dynamic sizes in conditions Siddhesh Poyarekar
2021-12-15 16:24     ` Jakub Jelinek
2021-12-15 17:56       ` Siddhesh Poyarekar
2021-12-15 18:52         ` Jakub Jelinek
2021-12-01 14:27   ` [PATCH v4 4/6] tree-object-size: Handle function parameters Siddhesh Poyarekar
2021-12-01 14:27   ` [PATCH v4 5/6] tree-object-size: Handle GIMPLE_CALL Siddhesh Poyarekar
2021-12-01 14:27   ` [PATCH v4 6/6] tree-object-size: Dynamic sizes for ADDR_EXPR Siddhesh Poyarekar
2021-12-18 12:35 ` [PATCH v5 0/4] __builtin_dynamic_object_size Siddhesh Poyarekar
2021-12-18 12:35   ` [PATCH v5 1/4] tree-object-size: Support dynamic sizes in conditions Siddhesh Poyarekar
2022-01-10 10:37     ` Jakub Jelinek
2022-01-10 23:55       ` Siddhesh Poyarekar
2021-12-18 12:35   ` [PATCH v5 2/4] tree-object-size: Handle function parameters Siddhesh Poyarekar
2022-01-10 10:50     ` Jakub Jelinek
2022-01-11  0:32       ` Siddhesh Poyarekar
2021-12-18 12:35   ` [PATCH v5 3/4] tree-object-size: Handle GIMPLE_CALL Siddhesh Poyarekar
2022-01-10 11:03     ` Jakub Jelinek
2021-12-18 12:35   ` [PATCH v5 4/4] tree-object-size: Dynamic sizes for ADDR_EXPR Siddhesh Poyarekar
2022-01-10 11:09     ` Jakub Jelinek
2022-01-04  3:24   ` [PING][PATCH v5 0/4] __builtin_dynamic_object_size Siddhesh Poyarekar
2022-01-11  8:57 ` [PATCH v6 " Siddhesh Poyarekar
2022-01-11  8:57   ` [PATCH v6 1/4] tree-object-size: Support dynamic sizes in conditions Siddhesh Poyarekar
2022-01-11  9:43     ` Jakub Jelinek
2022-01-11  9:44       ` Siddhesh Poyarekar
2022-01-11  8:57   ` [PATCH v6 2/4] tree-object-size: Handle function parameters Siddhesh Poyarekar
2022-01-11  9:44     ` Jakub Jelinek
2022-01-11  8:57   ` [PATCH v6 3/4] tree-object-size: Handle GIMPLE_CALL Siddhesh Poyarekar
2022-01-11  8:57   ` [PATCH v6 4/4] tree-object-size: Dynamic sizes for ADDR_EXPR Siddhesh Poyarekar
2022-01-11  9:47     ` Jakub Jelinek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).