[v6,1/4] tree-object-size: Support dynamic sizes in conditions

Message ID 20220111085750.1900047-2-siddhesh@gotplt.org
State Committed
Headers
Series __builtin_dynamic_object_size |

Commit Message

Siddhesh Poyarekar Jan. 11, 2022, 8:57 a.m. UTC
  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
  

Comments

Jakub Jelinek Jan. 11, 2022, 9:43 a.m. UTC | #1
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
  
Siddhesh Poyarekar Jan. 11, 2022, 9:44 a.m. UTC | #2
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
  

Patch

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
     {