diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index f36ffa4740b..5189dcfcc0d 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1608,6 +1608,7 @@ OBJS = \
 	tree-diagnostic.o \
 	tree-diagnostic-path.o \
 	tree-dump.o \
+	tree-dynamic-object-size.o \
 	tree-eh.o \
 	tree-emutls.o \
 	tree-if-conv.o \
@@ -3647,15 +3648,15 @@ PLUGIN_HEADERS = $(TREE_H) $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) \
   ssa-iterators.h $(RESOURCE_H) tree-cfgcleanup.h attribs.h calls.h \
   cfgexpand.h diagnostic-color.h gcc-symtab.h gimple-builder.h gimple-low.h \
   gimple-walk.h gimplify-me.h pass_manager.h print-rtl.h stmt.h \
-  tree-dfa.h tree-hasher.h tree-nested.h tree-object-size.h tree-outof-ssa.h \
-  tree-parloops.h tree-ssa-address.h tree-ssa-coalesce.h tree-ssa-dom.h \
-  tree-ssa-loop.h tree-ssa-loop-ivopts.h tree-ssa-loop-manip.h \
-  tree-ssa-loop-niter.h tree-ssa-ter.h tree-ssa-threadedge.h \
-  tree-ssa-threadupdate.h inchash.h wide-int.h signop.h hash-map.h \
-  hash-set.h dominance.h cfg.h cfgrtl.h cfganal.h cfgbuild.h cfgcleanup.h \
-  lcm.h cfgloopmanip.h file-prefix-map.h builtins.def $(INSN_ATTR_H) \
-  pass-instances.def params.list $(srcdir)/../include/gomp-constants.h \
-  $(EXPR_H)
+  tree-dfa.h tree-dynamic-object-size.h tree-hasher.h tree-nested.h \
+  tree-object-size.h tree-outof-ssa.h tree-parloops.h tree-ssa-address.h \
+  tree-ssa-coalesce.h tree-ssa-dom.h tree-ssa-loop.h tree-ssa-loop-ivopts.h \
+  tree-ssa-loop-manip.h tree-ssa-loop-niter.h tree-ssa-ter.h \
+  tree-ssa-threadedge.h tree-ssa-threadupdate.h inchash.h wide-int.h signop.h \
+  hash-map.h hash-set.h dominance.h cfg.h cfgrtl.h cfganal.h cfgbuild.h \
+  cfgcleanup.h lcm.h cfgloopmanip.h file-prefix-map.h builtins.def \
+  $(INSN_ATTR_H) pass-instances.def params.list \
+  $(srcdir)/../include/gomp-constants.h $(EXPR_H)
 
 # generate the 'build fragment' b-header-vars
 s-header-vars: Makefile
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 894d62359b4..d015029765b 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -48,6 +48,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "calls.h"
 #include "varasm.h"
 #include "tree-object-size.h"
+#include "tree-dynamic-object-size.h"
 #include "tree-ssa-strlen.h"
 #include "realmpfr.h"
 #include "cfgrtl.h"
@@ -10325,6 +10326,7 @@ static tree
 fold_builtin_dyn_object_size (tree ptr, tree ost)
 {
   int object_size_type;
+  tree bytes;
 
   if (!valid_object_size_args (ptr, ost, &object_size_type))
     return NULL_TREE;
@@ -10335,6 +10337,12 @@ fold_builtin_dyn_object_size (tree ptr, tree ost)
   if (TREE_SIDE_EFFECTS (ptr))
     return build_int_cst_type (size_type_node, object_size_type < 2 ? -1 : 0);
 
+  /* If object size expression cannot be evaluated yet, delay folding until
+     later.  Maybe subsequent passes will help determining it.  */
+  if (TREE_CODE (ptr) == SSA_NAME
+      && compute_builtin_dyn_object_size (ptr, object_size_type, &bytes))
+    return bytes;
+
   return NULL_TREE;
 }
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 133b82eef38..082d167cd65 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -12777,6 +12777,17 @@ 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}.
+
 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/passes.def b/gcc/passes.def
index c11c237f6d2..7dc55cc6ba0 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -74,6 +74,7 @@ along with GCC; see the file COPYING3.  If not see
       NEXT_PASS (pass_all_early_optimizations);
       PUSH_INSERT_PASSES_WITHIN (pass_all_early_optimizations)
 	  NEXT_PASS (pass_remove_cgraph_callee_edges);
+	  NEXT_PASS (pass_early_dynamic_object_sizes);
 	  NEXT_PASS (pass_early_object_sizes);
 	  /* Don't record nonzero bits before IPA to avoid
 	     using too much memory.  */
@@ -198,6 +199,7 @@ along with GCC; see the file COPYING3.  If not see
       NEXT_PASS (pass_ccp, true /* nonzero_p */);
       /* After CCP we rewrite no longer addressed locals into SSA
 	 form if possible.  */
+      NEXT_PASS (pass_dynamic_object_sizes);
       NEXT_PASS (pass_object_sizes);
       NEXT_PASS (pass_post_ipa_warn);
       NEXT_PASS (pass_complete_unrolli);
@@ -379,6 +381,7 @@ along with GCC; see the file COPYING3.  If not see
       /* Perform simple scalar cleanup which is constant/copy propagation.  */
       NEXT_PASS (pass_ccp, true /* nonzero_p */);
       NEXT_PASS (pass_post_ipa_warn);
+      NEXT_PASS (pass_dynamic_object_sizes);
       NEXT_PASS (pass_object_sizes);
       /* Fold remaining builtins.  */
       NEXT_PASS (pass_fold_builtins);
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..620e8cbc611
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -0,0 +1,166 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+typedef __SIZE_TYPE__ size_t;
+#define abort __builtin_abort
+
+void *
+__attribute__ ((alloc_size (1)))
+__attribute__ ((__nothrow__ , __leaf__))
+__attribute__ ((noinline))
+alloc_func (size_t sz)
+{
+  return __builtin_malloc (sz);
+}
+
+void *
+__attribute__ ((alloc_size (1, 2)))
+__attribute__ ((__nothrow__ , __leaf__))
+__attribute__ ((noinline))
+calloc_func (size_t cnt, size_t sz)
+{
+  return __builtin_calloc (cnt, sz);
+}
+
+void *
+__attribute__ ((noinline))
+unknown_allocator (size_t cnt, size_t sz)
+{
+  return __builtin_calloc (cnt, sz);
+}
+
+size_t
+__attribute__ ((noinline))
+test_unknown (size_t cnt, size_t sz)
+{
+  void *ret = unknown_allocator (cnt, sz);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+/* Malloc-like allocator.  */
+
+size_t
+__attribute__ ((noinline))
+test_malloc (size_t sz)
+{
+  void *ret = alloc_func (sz);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc (size_t sz)
+{
+  void *ret = __builtin_malloc (sz);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_cond (int cond)
+{
+  void *ret = __builtin_malloc (cond ? 32 : 64);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+/* Calloc-like allocator.  */
+
+size_t
+__attribute__ ((noinline))
+test_calloc (size_t cnt, size_t sz)
+{
+  void *ret = calloc_func (cnt, sz);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_calloc (size_t cnt, size_t sz)
+{
+  void *ret = __builtin_calloc (cnt, sz);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_calloc_cond (int cond1, int cond2)
+{
+  void *ret = __builtin_calloc (cond1 ? 32 : 64, cond2 ? 1024 : 16);
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+/* Passthrough functions.  */
+
+size_t
+__attribute__ ((noinline))
+test_passthrough (size_t sz, char *in)
+{
+  char *bin = __builtin_malloc (sz);
+  char *dest = __builtin_memcpy (bin, in, sz);
+
+  return __builtin_dynamic_object_size (dest, 0);
+}
+
+/* Variable length arrays.  */
+size_t
+__attribute__ ((noinline))
+test_dynarray (size_t sz)
+{
+  char bin[sz];
+
+  return __builtin_dynamic_object_size (bin, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_dynarray_cond (int cond)
+{
+  char bin[cond ? 8 : 16];
+
+  return __builtin_dynamic_object_size (bin, 0);
+}
+
+int
+main (int argc, char **argv)
+{
+  unsigned nfails = 0;
+
+#define FAIL() ({ \
+  __builtin_printf ("Failure at line: %d\n", __LINE__);			      \
+  nfails++;								      \
+})
+
+  size_t outsz = test_unknown (32, 42);
+  if (outsz != -1 && outsz != 32)
+    FAIL ();
+  if (test_malloc (2048) != 2048)
+    FAIL ();
+  if (test_builtin_malloc (2048) != 2048)
+    FAIL ();
+  if (test_builtin_malloc_cond (1) != 32)
+    FAIL ();
+  if (test_builtin_malloc_cond (0) != 64)
+    FAIL ();
+  if (test_calloc (2048, 4) != 2048 * 4)
+    FAIL ();
+  if (test_builtin_calloc (2048, 8) != 2048 * 8)
+    FAIL ();
+  if (test_builtin_calloc_cond (0, 0) != 64 * 16)
+    FAIL ();
+  if (test_builtin_calloc_cond (1, 1) != 32 * 1024)
+    FAIL ();
+  if (test_passthrough (__builtin_strlen (argv[0]) + 1, argv[0])
+      != __builtin_strlen (argv[0]) + 1)
+    FAIL ();
+  if (test_dynarray (__builtin_strlen (argv[0])) != __builtin_strlen (argv[0]))
+    FAIL ();
+  if (test_dynarray_cond (0) != 16)
+    FAIL ();
+  if (test_dynarray_cond (1) != 8)
+    FAIL ();
+
+  if (nfails > 0)
+    __builtin_abort ();
+
+  return 0;
+}
diff --git a/gcc/tree-dynamic-object-size.c b/gcc/tree-dynamic-object-size.c
new file mode 100644
index 00000000000..8e52dd46c03
--- /dev/null
+++ b/gcc/tree-dynamic-object-size.c
@@ -0,0 +1,517 @@
+/* __builtin_dynamic_object_size (ptr, object_size_type) computation
+   Copyright (C) 2021 Siddhesh Poyarekar <siddhesh@gotplt.org>
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* __builtin_dynamic_object_size returns richer object size information with
+   the intent of improving precision of checks that depend on object sizes.  It
+   is a drop-in replacement for __builtin_object_size due to having the
+   following common semantics:
+
+   * Both take the same arguments.
+   * Like __builtin_object_size, __builtin_dynamic_object_size also provides an
+     estimate (either lower or higher, based on the second argument) of the
+     object size and not the precise object size.
+   * On failure, both return either (size_t)-1 or (size_t)0 depending on the
+     second byte of the TYPE argument.
+
+   There are minor semantic differences in both builtins:
+
+   * On success, __builtin_dynamic_object_size is more likely to return the
+     closest object size, since it may return one of the following:
+     - An expression that evaluates to the exact object size
+     - When the exact size is not available, an expression that evaluates to
+       the maximum or minimum estimate of the size of the object.  Currently
+       this is a constant since the pass simplifies
+       __builtin_dynamic_object_size to __builtin_object_size if it cannot
+       determine a size expression.  However in future it could be a
+       non-constant expression.
+
+   See definition of collect_object_sizes_for to know what patterns are
+   currently recognized.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+#include "tree-pass.h"
+#include "ssa.h"
+#include "gimple-pretty-print.h"
+#include "fold-const.h"
+#include "tree-object-size.h"
+#include "gimple-fold.h"
+#include "gimple-iterator.h"
+#include "tree-cfg.h"
+#include "stringpool.h"
+#include "attribs.h"
+#include "builtins.h"
+#include "print-tree.h"
+
+struct object_size_info
+{
+  int object_size_type;
+  bitmap visited;
+};
+
+static tree alloc_object_size (const gcall *);
+static tree pass_through_call (const gcall *);
+static void collect_object_sizes_for (struct object_size_info *, tree);
+
+/* object_sizes[0] is upper bound for number of bytes till the end of
+   the object.
+   object_sizes[1] is upper bound for number of bytes till the end of
+   the subobject (innermost array or field with address taken).
+   object_sizes[2] is lower bound for number of bytes till the end of
+   the object and object_sizes[3] lower bound for subobject.  */
+static vec<tree> object_sizes[4];
+
+/* Bitmaps what object sizes have been computed already.  */
+static bitmap computed[4];
+
+/* Compute __builtin_dynamic_object_size for CALL, which is a GIMPLE_CALL.
+   Handles calls to functions declared with attribute alloc_size.
+   OBJECT_SIZE_TYPE is the second argument from __builtin_dynamic_object_size.
+   If unknown, return NULL_TREE.  */
+
+static tree
+alloc_object_size (const gcall *call)
+{
+  gcc_assert (is_gimple_call (call));
+
+  tree calltype;
+  tree callfn = gimple_call_fndecl (call);
+
+  if (callfn)
+    calltype = TREE_TYPE (callfn);
+  else
+    calltype = gimple_call_fntype (call);
+
+  if (!calltype)
+    return NULL_TREE;
+
+  /* Set to positions of alloc_size arguments.  */
+  int arg1 = -1, arg2 = -1;
+  tree alloc_size = lookup_attribute ("alloc_size",
+				      TYPE_ATTRIBUTES (calltype));
+  if (alloc_size && TREE_VALUE (alloc_size))
+    {
+      tree p = TREE_VALUE (alloc_size);
+
+      arg1 = TREE_INT_CST_LOW (TREE_VALUE (p))-1;
+      if (TREE_CHAIN (p))
+	arg2 = TREE_INT_CST_LOW (TREE_VALUE (TREE_CHAIN (p)))-1;
+    }
+  else if (gimple_call_builtin_p (call, BUILT_IN_NORMAL)
+	   && callfn && ALLOCA_FUNCTION_CODE_P (DECL_FUNCTION_CODE (callfn)))
+    arg1 = 0;
+
+  if (arg1 < 0 || arg1 >= (int)gimple_call_num_args (call)
+      || arg2 >= (int)gimple_call_num_args (call))
+    return NULL_TREE;
+
+  if (arg2 >= 0)
+    {
+      tree ret = fold_build2 (MULT_EXPR, sizetype,
+			      fold_convert (sizetype, gimple_call_arg (call,
+								       arg1)),
+			      fold_convert (sizetype, gimple_call_arg (call,
+								       arg2)));
+      return STRIP_NOPS (ret);
+    }
+  else if (arg1 >= 0)
+    {
+      tree ret = fold_convert (sizetype, gimple_call_arg (call, arg1));
+      return STRIP_NOPS (ret);
+    }
+
+  return NULL_TREE;
+}
+
+
+/* If object size is propagated from one of function's arguments directly
+   to its return value, return that argument for GIMPLE_CALL statement CALL.
+   Otherwise return NULL.  */
+
+static tree
+pass_through_call (const gcall *call)
+{
+  unsigned rf = gimple_call_return_flags (call);
+  if (rf & ERF_RETURNS_ARG)
+    {
+      unsigned argnum = rf & ERF_RETURN_ARG_MASK;
+      if (argnum < gimple_call_num_args (call))
+	return gimple_call_arg (call, argnum);
+    }
+
+  /* __builtin_assume_aligned is intentionally not marked RET1.  */
+  if (gimple_call_builtin_p (call, BUILT_IN_ASSUME_ALIGNED))
+    return gimple_call_arg (call, 0);
+
+  return NULL_TREE;
+}
+
+/* Compute object size estimate for PTR and set *PSIZE to the resulting value.
+   OBJECT_SIZE_TYPE is the second argument to __builtin_dynamic_object_size.
+   Returns true on success and false when the object size could not be
+   determined.  */
+
+bool
+compute_builtin_dyn_object_size (tree ptr, int object_size_type, tree *psize)
+{
+  gcc_assert (object_size_type >= 0 && object_size_type <= 3);
+
+  /* Set to unknown and overwrite just before returning if the size
+     could be determined.  */
+  *psize = NULL_TREE;
+
+  if (TREE_CODE (ptr) != SSA_NAME
+      || !POINTER_TYPE_P (TREE_TYPE (ptr)))
+      return false;
+
+  if (computed[object_size_type] == NULL)
+    return false;
+
+  if (bitmap_bit_p (computed[object_size_type], SSA_NAME_VERSION (ptr)))
+    goto out;
+
+  struct object_size_info osi;
+  bitmap_iterator bi;
+  unsigned int i;
+
+  if (num_ssa_names > object_sizes[object_size_type].length ())
+    object_sizes[object_size_type].safe_grow (num_ssa_names, true);
+  if (dump_file)
+    {
+      fprintf (dump_file, "Computing %s dynamic %sobject size for ",
+	       (object_size_type & 2) ? "minimum" : "maximum",
+	       (object_size_type & 1) ? "sub" : "");
+      print_generic_expr (dump_file, ptr, dump_flags);
+      fprintf (dump_file, ":\n");
+    }
+
+  osi.visited = BITMAP_ALLOC (NULL);
+  osi.object_size_type = object_size_type;
+
+  collect_object_sizes_for (&osi, ptr);
+
+  /* Debugging dumps.  */
+  if (dump_file)
+    {
+      EXECUTE_IF_SET_IN_BITMAP (osi.visited, 0, i, bi)
+	if (object_sizes[object_size_type][i] != NULL_TREE)
+	  {
+	    print_generic_expr (dump_file, ssa_name (i),
+				dump_flags);
+	    fprintf (dump_file, ": %s dynamic %sobject size ",
+		     (object_size_type & 2) ? "minimum" : "maximum",
+		     (object_size_type & 1) ? "sub" : "");
+	    print_generic_expr (dump_file, object_sizes[object_size_type][i],
+				dump_flags);
+	    fprintf (dump_file, ":\n");
+	  }
+    }
+
+  BITMAP_FREE (osi.visited);
+
+out:
+  *psize = object_sizes[object_size_type][SSA_NAME_VERSION (ptr)];
+  return *psize != NULL_TREE;
+}
+
+
+/* Use size of ORIG for DEST and return it.  */
+
+static void
+set_object_size_ssa (struct object_size_info *osi, tree dest, tree orig)
+{
+  collect_object_sizes_for (osi, orig);
+  object_sizes[osi->object_size_type][SSA_NAME_VERSION (dest)] =
+    object_sizes[osi->object_size_type][SSA_NAME_VERSION (orig)];
+}
+
+
+/* Compute object_sizes for PTR, defined to the result of a call.  */
+
+static void
+call_object_size (struct object_size_info *osi, tree ptr, gcall *call)
+{
+  unsigned int varno = SSA_NAME_VERSION (ptr);
+
+  gcc_assert (is_gimple_call (call));
+
+  object_sizes[osi->object_size_type][varno] = alloc_object_size (call);
+}
+
+
+/* Compute object sizes for VAR.
+
+   - For allocation GIMPLE_CALL like malloc or calloc object size is the size
+     of the allocation.
+   - For a memcpy like GIMPLE_CALL that always returns one of its arguments,
+     the object size is object size of that argument.  */
+
+static void
+collect_object_sizes_for (struct object_size_info *osi, tree var)
+{
+  int object_size_type = osi->object_size_type;
+  unsigned int varno = SSA_NAME_VERSION (var);
+  gimple *stmt;
+
+  if (bitmap_bit_p (computed[object_size_type], varno))
+    return;
+
+  if (bitmap_set_bit (osi->visited, varno))
+    object_sizes[object_size_type][varno] = NULL_TREE;
+  else
+    {
+      /* No dependency loop handling at the moment.  */
+      if (dump_file && (dump_flags & TDF_DETAILS))
+	{
+	  fprintf (dump_file, "Found a dependency loop at ");
+	  print_generic_expr (dump_file, var, dump_flags);
+	  fprintf (dump_file, "\n");
+	}
+      return;
+    }
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "Visiting use-def links for ");
+      print_generic_expr (dump_file, var, dump_flags);
+      fprintf (dump_file, "\n");
+    }
+
+  stmt = SSA_NAME_DEF_STMT (var);
+
+  switch (gimple_code (stmt))
+    {
+    case GIMPLE_CALL:
+      {
+	gcall *call_stmt = as_a <gcall *> (stmt);
+	tree arg = pass_through_call (call_stmt);
+	if (arg)
+	  {
+	    if (TREE_CODE (arg) == SSA_NAME)
+	      set_object_size_ssa (osi, var, arg);
+	  }
+	else
+	  call_object_size (osi, var, call_stmt);
+	break;
+      }
+
+    /* Bail out for all other cases.  */
+    case GIMPLE_NOP:
+    case GIMPLE_PHI:
+    case GIMPLE_ASSIGN:
+    case GIMPLE_ASM:
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  bitmap_set_bit (computed[object_size_type], varno);
+}
+
+
+/* Initialize data structures for the object size computation.  */
+
+void
+init_dynamic_object_sizes (void)
+{
+  int object_size_type;
+
+  if (computed[0])
+    return;
+
+  for (object_size_type = 0; object_size_type <= 3; object_size_type++)
+    {
+      object_sizes[object_size_type].safe_grow (num_ssa_names, true);
+      computed[object_size_type] = BITMAP_ALLOC (NULL);
+    }
+}
+
+
+/* Destroy data structures after the object size computation.  */
+
+void
+fini_dynamic_object_sizes (void)
+{
+  int object_size_type;
+
+  for (object_size_type = 0; object_size_type <= 3; object_size_type++)
+    {
+      object_sizes[object_size_type].release ();
+      BITMAP_FREE (computed[object_size_type]);
+    }
+}
+
+unsigned int
+dynamic_object_sizes_execute (function *fun, bool lower_to_bos)
+{
+  basic_block bb;
+
+  init_dynamic_object_sizes ();
+
+  FOR_EACH_BB_FN (bb, fun)
+    {
+      gimple_stmt_iterator i;
+      for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i))
+	{
+	  gimple *call = gsi_stmt (i);
+
+	  if (!gimple_call_builtin_p (call, BUILT_IN_DYN_OBJECT_SIZE))
+	    continue;
+
+	  tree lhs = gimple_call_lhs (call);
+	  if (!lhs)
+	    continue;
+
+	  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 builtin in\n  ");
+		  print_gimple_stmt (dump_file, call, 0, dump_flags);
+		  fprintf (dump_file, " to ");
+		  print_generic_expr (dump_file, result);
+		  fprintf (dump_file, "\n");
+		}
+	    }
+	  else if (lower_to_bos)
+	    {
+	      /* 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);
+	      gimple_call_set_arg (call, 0, args[0]);
+	      gimple_call_set_arg (call, 1, args[1]);
+	      update_stmt (call);
+
+	      if (dump_file && (dump_flags & TDF_DETAILS))
+		{
+		  print_generic_expr (dump_file, args[0], dump_flags);
+		  fprintf (dump_file,
+			   ": Simplified to __builtin_object_size\n");
+		}
+	    }
+	}
+    }
+
+  fini_dynamic_object_sizes ();
+  return 0;
+}
+
+/* Evaluate __builtin_dynamic_object_size () builtins early.  */
+
+namespace {
+
+const pass_data pass_data_early_dynamic_object_sizes =
+{
+  GIMPLE_PASS, /* type */
+  "early_dynobjsz", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  ( PROP_cfg | PROP_ssa ), /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_early_dynamic_object_sizes : public gimple_opt_pass
+{
+public:
+  pass_early_dynamic_object_sizes (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_early_dynamic_object_sizes, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  opt_pass * clone () { return new pass_early_dynamic_object_sizes (m_ctxt); }
+  virtual unsigned int execute (function *fun)
+  {
+    return dynamic_object_sizes_execute (fun, false);
+  }
+}; // class pass_early_dynamic_object_sizes
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_early_dynamic_object_sizes (gcc::context *ctxt)
+{
+  return new pass_early_dynamic_object_sizes (ctxt);
+}
+
+/* Evaluate __builtin_dynamic_object_size () builtins, simplifying to
+   __builtin_object_size () if a size expression cannot be produced.  */
+
+namespace {
+
+const pass_data pass_data_dynamic_object_sizes =
+{
+  GIMPLE_PASS, /* type */
+  "dynobjsz", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  ( PROP_cfg | PROP_ssa ), /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_dynamic_object_sizes : public gimple_opt_pass
+{
+public:
+  pass_dynamic_object_sizes (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_dynamic_object_sizes, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  opt_pass * clone () { return new pass_dynamic_object_sizes (m_ctxt); }
+  virtual unsigned int execute (function *fun)
+  {
+    return dynamic_object_sizes_execute (fun, true);
+  }
+}; // class pass_dynamic_object_sizes
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_dynamic_object_sizes (gcc::context *ctxt)
+{
+  return new pass_dynamic_object_sizes (ctxt);
+}
diff --git a/gcc/tree-dynamic-object-size.h b/gcc/tree-dynamic-object-size.h
new file mode 100644
index 00000000000..145b4b88bca
--- /dev/null
+++ b/gcc/tree-dynamic-object-size.h
@@ -0,0 +1,25 @@
+/* Declarations for tree-dynamic-object-size.c.
+   Copyright (C) 2021 Siddhesh Poyarekar <siddhesh@gotplt.org>
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_TREE_DYNAMIC_OBJECT_SIZE_H
+#define GCC_TREE_DYNAMIC_OBJECT_SIZE_H
+
+extern bool compute_builtin_dyn_object_size (tree, int, tree *);
+
+#endif  // GCC_TREE_DYNAMIC_OBJECT_SIZE_H
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index 84477a47b88..d81460d0703 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -430,7 +430,9 @@ extern gimple_opt_pass *make_pass_oacc_loop_designation (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_omp_oacc_neuter_broadcast (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_oacc_device_lower (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_omp_device_lower (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_dynamic_object_sizes (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_object_sizes (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_early_dynamic_object_sizes (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_early_object_sizes (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_warn_access (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_warn_printf (gcc::context *ctxt);
