[06/10] tree-object-size: Support dynamic sizes in conditions

Message ID 20211109190137.1107736-7-siddhesh@gotplt.org
State Superseded
Headers
Series __builtin_dynamic_object_size |

Commit Message

Siddhesh Poyarekar Nov. 9, 2021, 7:01 p.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:

	* 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
  

Comments

Jakub Jelinek Nov. 23, 2021, 3:12 p.m. UTC | #1
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
  
Siddhesh Poyarekar Nov. 23, 2021, 3:36 p.m. UTC | #2
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
  
Siddhesh Poyarekar Nov. 23, 2021, 3:38 p.m. UTC | #3
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
  
Jakub Jelinek Nov. 23, 2021, 3:52 p.m. UTC | #4
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
  
Siddhesh Poyarekar Nov. 23, 2021, 4 p.m. UTC | #5
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
  
Jakub Jelinek Nov. 23, 2021, 4:17 p.m. UTC | #6
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
  
Jakub Jelinek Nov. 23, 2021, 4:19 p.m. UTC | #7
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
  

Patch

diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
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;