[05/10] __builtin_dynamic_object_size: Recognize builtin

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

Commit Message

Siddhesh Poyarekar Nov. 9, 2021, 7:01 p.m. UTC
  Recognize the __builtin_dynamic_object_size builtin and add paths in the
object size path to deal with it, but treat it like
__builtin_object_size for now.  Also add tests to provide the same
testing coverage for the new builtin name.

gcc/ChangeLog:

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

gcc/testsuite/ChangeLog:

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

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

Comments

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

Two spaces after . instead of just one.

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

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

> +#define DYNAMIC_OBJECT_SIZE

Why this extra macro?

> +#define __builtin_object_size __builtin_dynamic_object_size

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

You can #ifndef __builtin_object_size
instead...

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

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

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

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

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

  tree type = ...

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

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

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

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

Thanks, I'll fix this.

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

I'll fix this too.

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

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

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

OK, I'll fix this.

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

Thanks, I'll fix this too.

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

Good idea.

	Jakub
  

Patch

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