public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH 0/8] __builtin_dynamic_object_size and more
@ 2021-10-07 22:14 Siddhesh Poyarekar
  2021-10-07 22:14 ` [PATCH 1/8] __builtin_dynamic_object_size: Recognize builtin name Siddhesh Poyarekar
                   ` (8 more replies)
  0 siblings, 9 replies; 18+ messages in thread
From: Siddhesh Poyarekar @ 2021-10-07 22:14 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

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

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

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

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

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

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

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

- If __builtin_dynamic_object_size returns a non-constant expression,
  the value of that expression is never (size_t)-1.

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

- The __builtin_dynamic_object_size support is implemented in
  tree-dynamic-object-size.c as two passes, just like tree-object-size.
  In most cases the first pass (early_dynobjsz) is able to evaluate
  object size expressions.  The second phase mainly ends up simplifying
  the __builtin_dynamic_object_size to __builtin_object_size.  Speaking
  of which...

- Currently if __builtin_dynamic_object_size fails to evaluate a precise
  size expression, it replaces the call with __builtin_object_size and
  punts to the last tree-object-size pass.  In future, this could become
  more sophisticated.

- Size expressions returned directly by __builtin_dynamic_object_size
  are bounded in the range of [0, SIZE_MAX - 1] so that equality tests
  with (size_t)-1 fail.  This is necessary to eliminate unnecessary
  branches in _FORTIFY_SOURCE=3.  This could be tightened further in
  future to SIZE_MAX / 2 since there are already assumptions built in
  that prevent object sizes from crossing that limit.

- I have split the implementation into one feature per patchset (calls,
  function parameters, PHI, etc.) to hopefully ease review.

- Some code duplication from tree-object-size is intentional.  The
  immediate need for that is to reduce impact on existing passes.  Over
  the medium/long term though, the intention is to fully replace
  tree-object-size.  See Future work for more information.

- I have marked the new files as Copyright (C) me (since I'm
  contributing under DCO), but I've based some of the code on the
  existing tree-object-size.c, so I need advice on the correct copyright
  notice.

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

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

I have also not implemented simplification of __*_chk to their normal
variants if we can determine at compile time that it is safe.  I am
working on that right now and hope to have it ready before stage 1
closes.

Testing:
--------

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

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

I'm running more tests with _FORTIFY_SOURCE=3 and will do more analysis
to quantify the coverage improvement more clearly.

Limitations/Future work:

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

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

- AFAICT, there are no cases where __builtin_dynamic_object_size misses
  and __builtin_object_size gets a non-fail estimate.  I have still kept
  the backstop in place since this is a new pass and it's very likely
  that I've missed something.  It should be possible to use ranger to
  get an upper and lower bound on the size expression computed by
  __builtin_dynamic_object_size and use that to implement
  __builtin_object_size.  In practice though, ranger currently seems to
  have some difficulty evaluating ranges for some PHI objects.  I intend
  to look into that a bit more once this pass has settled in.

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

Siddhesh Poyarekar (8):
  __builtin_dynamic_object_size: Recognize builtin name
  tree-dynamic-object-size: New pass
  tree-dynamic-object-size: Handle GIMPLE_PHI
  tree-dynamic-object-size: Support ADDR_EXPR
  tree-dynamic-object-size: Handle GIMPLE_ASSIGN
  tree-dynamic-object-size: Handle function parameters
  tree-dynamic-object-size: Get subobject sizes
  tree-dynamic-object-size: Add test wrappers to extend testing

 gcc/Makefile.in                               |   19 +-
 gcc/builtins.c                                |   71 +-
 gcc/builtins.def                              |    1 +
 gcc/doc/extend.texi                           |   11 +
 gcc/passes.def                                |    3 +
 .../g++.dg/ext/builtin-dynamic-object-size1.C |    5 +
 .../g++.dg/ext/builtin-dynamic-object-size2.C |    5 +
 .../gcc.dg/builtin-dynamic-alloc-size.c       |    7 +
 .../gcc.dg/builtin-dynamic-object-size-0.c    |  401 +++++
 .../gcc.dg/builtin-dynamic-object-size-1.c    |    7 +
 .../gcc.dg/builtin-dynamic-object-size-10.c   |    9 +
 .../gcc.dg/builtin-dynamic-object-size-11.c   |    7 +
 .../gcc.dg/builtin-dynamic-object-size-12.c   |    5 +
 .../gcc.dg/builtin-dynamic-object-size-13.c   |    5 +
 .../gcc.dg/builtin-dynamic-object-size-14.c   |    5 +
 .../gcc.dg/builtin-dynamic-object-size-15.c   |    6 +
 .../gcc.dg/builtin-dynamic-object-size-16.c   |    7 +
 .../gcc.dg/builtin-dynamic-object-size-17.c   |    8 +
 .../gcc.dg/builtin-dynamic-object-size-18.c   |    8 +
 .../gcc.dg/builtin-dynamic-object-size-19.c   |  104 ++
 .../gcc.dg/builtin-dynamic-object-size-2.c    |    7 +
 .../gcc.dg/builtin-dynamic-object-size-3.c    |    7 +
 .../gcc.dg/builtin-dynamic-object-size-4.c    |    7 +
 .../gcc.dg/builtin-dynamic-object-size-6.c    |    5 +
 .../gcc.dg/builtin-dynamic-object-size-7.c    |    5 +
 .../gcc.dg/builtin-dynamic-object-size-8.c    |    5 +
 .../gcc.dg/builtin-dynamic-object-size-9.c    |    5 +
 gcc/testsuite/gcc.dg/builtin-object-size-1.c  |  110 ++
 gcc/testsuite/gcc.dg/builtin-object-size-16.c |   10 +
 gcc/testsuite/gcc.dg/builtin-object-size-17.c |    2 +
 gcc/testsuite/gcc.dg/builtin-object-size-2.c  |  126 ++
 gcc/testsuite/gcc.dg/builtin-object-size-3.c  |  148 ++
 gcc/testsuite/gcc.dg/builtin-object-size-4.c  |  117 ++
 gcc/tree-dynamic-object-size.c                | 1513 +++++++++++++++++
 gcc/tree-dynamic-object-size.h                |   26 +
 gcc/tree-pass.h                               |    2 +
 36 files changed, 2769 insertions(+), 20 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size1.C
 create mode 100644 gcc/testsuite/g++.dg/ext/builtin-dynamic-object-size2.C
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-alloc-size.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-10.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-11.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-12.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-13.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-14.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-15.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-16.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-17.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-18.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-19.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-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
 create mode 100644 gcc/tree-dynamic-object-size.c
 create mode 100644 gcc/tree-dynamic-object-size.h

-- 
2.31.1


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

* [PATCH 1/8] __builtin_dynamic_object_size: Recognize builtin name
  2021-10-07 22:14 [PATCH 0/8] __builtin_dynamic_object_size and more Siddhesh Poyarekar
@ 2021-10-07 22:14 ` Siddhesh Poyarekar
  2021-10-12 13:42   ` Jakub Jelinek
  2021-10-07 22:14 ` [PATCH 2/8] tree-dynamic-object-size: New pass Siddhesh Poyarekar
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 18+ messages in thread
From: Siddhesh Poyarekar @ 2021-10-07 22:14 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Recognize the __builtin_dynamic_object_size builtin, but simply
replace it with -1 or 0 for now.

gcc/ChangeLog:

	* builtins.c (expand_builtin, fold_builtin_2): Add
	BUILT_IN_DYN_OBJECT_SIZE.
	(fold_builtin_dyn_object_size): New function.
	(valid_object_size_args): New function.
	(fold_builtin_object_size): Use it.
	* builtins.def (BUILT_IN_DYN_OBJECT_SIZE): New builtin.
---
 gcc/builtins.c   | 63 +++++++++++++++++++++++++++++++++++++++---------
 gcc/builtins.def |  1 +
 2 files changed, 53 insertions(+), 11 deletions(-)

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 3e57eb03af0..894d62359b4 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -180,6 +180,7 @@ static rtx expand_builtin_memory_chk (tree, rtx, machine_mode,
 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_dyn_object_size (tree, tree);
 
 unsigned HOST_WIDE_INT target_newline;
 unsigned HOST_WIDE_INT target_percent;
@@ -7910,6 +7911,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
       return const0_rtx;
 
     case BUILT_IN_OBJECT_SIZE:
+    case BUILT_IN_DYN_OBJECT_SIZE:
       return expand_builtin_object_size (exp);
 
     case BUILT_IN_MEMCPY_CHK:
@@ -9318,6 +9320,9 @@ fold_builtin_2 (location_t loc, tree expr, tree fndecl, tree arg0, tree arg1)
     case BUILT_IN_OBJECT_SIZE:
       return fold_builtin_object_size (arg0, arg1);
 
+    case BUILT_IN_DYN_OBJECT_SIZE:
+      return fold_builtin_dyn_object_size (arg0, arg1);
+
     case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE:
       return fold_builtin_atomic_always_lock_free (arg0, arg1);
 
@@ -9964,7 +9969,10 @@ fold_builtin_next_arg (tree exp, bool va_start_p)
 }
 
 
-/* Expand a call EXP to __builtin_object_size.  */
+/* Expand a call EXP to __builtin_object_size or
+   __builtin_dynamic_object_size.  If the builtin survived up to this point
+   then it means we have failed to get an object size, so replace the call with
+   0 or -1 depending on the object size type argument.  */
 
 static rtx
 expand_builtin_object_size (tree exp)
@@ -10250,27 +10258,40 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
 		access_write_only);
 }
 
-/* Fold a call to __builtin_object_size with arguments PTR and OST,
-   if possible.  */
+/* Validate arguments to __builtin_object_size and
+   __builtin_dynamic_object_size.  If both arguments are valid, return the
+   object size type in OSTP.  */
 
-static tree
-fold_builtin_object_size (tree ptr, tree ost)
+static bool
+valid_object_size_args (const tree ptr, tree ost, int *ostp)
 {
-  unsigned HOST_WIDE_INT bytes;
-  int object_size_type;
-
   if (!validate_arg (ptr, POINTER_TYPE)
       || !validate_arg (ost, INTEGER_TYPE))
-    return NULL_TREE;
+    return false;
 
   STRIP_NOPS (ost);
 
   if (TREE_CODE (ost) != INTEGER_CST
       || tree_int_cst_sgn (ost) < 0
       || compare_tree_int (ost, 3) > 0)
-    return NULL_TREE;
+    return false;
 
-  object_size_type = tree_to_shwi (ost);
+  *ostp = tree_to_shwi (ost);
+
+  return true;
+}
+
+/* Fold a call to __builtin_object_size with arguments PTR and OST,
+   if possible.  */
+
+static tree
+fold_builtin_object_size (tree ptr, tree ost)
+{
+  unsigned HOST_WIDE_INT bytes;
+  int object_size_type;
+
+  if (!valid_object_size_args (ptr, ost, &object_size_type))
+    return NULL_TREE;
 
   /* __builtin_object_size doesn't evaluate side-effects in its arguments;
      if there are any side-effects, it returns (size_t) -1 for types 0 and 1
@@ -10297,6 +10318,26 @@ fold_builtin_object_size (tree ptr, tree ost)
   return NULL_TREE;
 }
 
+/* Fold a call to __builtin_dynamic_object_size with arguments PTR and OST,
+   if possible.  */
+
+static tree
+fold_builtin_dyn_object_size (tree ptr, tree ost)
+{
+  int object_size_type;
+
+  if (!valid_object_size_args (ptr, ost, &object_size_type))
+    return NULL_TREE;
+
+  /* __builtin_dynamic_object_size doesn't evaluate side-effects in its
+     arguments; if there are any side-effects, it returns (size_t) -1 for types
+     0 and 1 and (size_t) 0 for types 2 and 3.  */
+  if (TREE_SIDE_EFFECTS (ptr))
+    return build_int_cst_type (size_type_node, object_size_type < 2 ? -1 : 0);
+
+  return NULL_TREE;
+}
+
 /* Builtins with folding operations that operate on "..." arguments
    need special handling; we need to store the arguments in a convenient
    data structure before attempting any folding.  Fortunately there are
diff --git a/gcc/builtins.def b/gcc/builtins.def
index 45a09b4d42d..ae94caab921 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_DYN_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)
-- 
2.31.1


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

* [PATCH 2/8] tree-dynamic-object-size: New pass
  2021-10-07 22:14 [PATCH 0/8] __builtin_dynamic_object_size and more Siddhesh Poyarekar
  2021-10-07 22:14 ` [PATCH 1/8] __builtin_dynamic_object_size: Recognize builtin name Siddhesh Poyarekar
@ 2021-10-07 22:14 ` Siddhesh Poyarekar
  2021-10-08  4:05   ` Siddhesh Poyarekar
  2021-10-12 13:58   ` Jakub Jelinek
  2021-10-07 22:14 ` [PATCH 3/8] tree-dynamic-object-size: Handle GIMPLE_PHI Siddhesh Poyarekar
                   ` (6 subsequent siblings)
  8 siblings, 2 replies; 18+ messages in thread
From: Siddhesh Poyarekar @ 2021-10-07 22:14 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

A new pass is added to execute just before the tree-object-size pass
to recognize and simplify __builtin_dynamic_object_size.  Some key
ideas (such as multipass object size collection to detect reference
loops) have been taken from tree-object-size but is distinct from it
to ensure minimal impact on existing code.

At the moment, the pass only recognizes allocators and passthrough
functions to attempt to derive object size expressions, and replaces
the call site with those expressions.  On failure, it replaces the
__builtin_dynamic_object_size with __builtin_object_size as a
fallback.

gcc/ChangeLog:

	* Makefile.in (OBJS): Add tree-dynamic-object-size.o.
	(PLUGIN_HEADERS): Add tree-dynamic-object-size.h.
	* tree-dynamic-object-size.c: New file.
	* tree-dynamic-object-size.h: New file.
	* builtins.c: Use it.
	(fold_builtin_dyn_object_size): Call
	compute_builtin_dyn_object_size for
	__builtin_dynamic_object_size builtin.
	(passes.def): Add pass_dynamic_object_sizes.
	* tree-pass.h: Add ake_pass_dynamic_object_sizes.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-dynamic-object-size-0.c: New test.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 gcc/Makefile.in                               |  19 +-
 gcc/builtins.c                                |   8 +
 gcc/doc/extend.texi                           |  11 +
 gcc/passes.def                                |   3 +
 .../gcc.dg/builtin-dynamic-object-size-0.c    | 166 ++++++
 gcc/tree-dynamic-object-size.c                | 517 ++++++++++++++++++
 gcc/tree-dynamic-object-size.h                |  25 +
 gcc/tree-pass.h                               |   2 +
 8 files changed, 742 insertions(+), 9 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
 create mode 100644 gcc/tree-dynamic-object-size.c
 create mode 100644 gcc/tree-dynamic-object-size.h

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


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

* [PATCH 3/8] tree-dynamic-object-size: Handle GIMPLE_PHI
  2021-10-07 22:14 [PATCH 0/8] __builtin_dynamic_object_size and more Siddhesh Poyarekar
  2021-10-07 22:14 ` [PATCH 1/8] __builtin_dynamic_object_size: Recognize builtin name Siddhesh Poyarekar
  2021-10-07 22:14 ` [PATCH 2/8] tree-dynamic-object-size: New pass Siddhesh Poyarekar
@ 2021-10-07 22:14 ` Siddhesh Poyarekar
  2021-10-07 22:14 ` [PATCH 4/8] tree-dynamic-object-size: Support ADDR_EXPR Siddhesh Poyarekar
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 18+ messages in thread
From: Siddhesh Poyarekar @ 2021-10-07 22:14 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Where the target object is a PHI node, set the size to be a PHI node
with sizes of the target object.  All object size information is now
split into an SSA name and its size expression.  This allows non-SSA
size expressions to be evaluated all at once in the end and generated
statements inserted into the right places before returning the SSA
tree representing the size.

This change also adds support for dependency loops since that is a
common occurrence with PHI nodes.  Dependency loop handling is single
pass unlike tree-object-size due to the separation of size SSA names
and actual expressions, which allow for placeholders and unresolved
dependency loop counting in case of self-reference.  The downside
though is that if there's an unresolved dependency loop and the object
size computation fails, all of the computations are scrapped.  Ideally
only computations involved in the unresolved dependency loop should be
scrapped but I don't know if it's worth the extra state that would
have to be managed.  We could revisit this if it ends up being a
bottleneck.

gcc/ChangeLog:

	* tree-dynamic-object-size.c: Include gimplify-me.h.
	(object_size_info): New member deploop.
	(object_size_exprs): New vector of object size expressions.
	(emit_size_stmts, emit_size_phi_stmts, eval_size_expr,
	gimplify_size_exprs): New functions.
	(compute_builtin_dyn_object_size): New argument FUN.
	Grow object_size_exprs and call gimplify_size_exprs.
	(maybe_update_dependency_loop, cache_object_size,
	cache_object_size_copy): New functions.
	(set_object_size_ssa, call_object_size): Call
	cache_object_size_copy.
	(collect_object_sizes_for): Handle GIMPLE_PHI.
	(init_dynamic_object_sizes): Allocate object_size_exprs.
	(fini_dynamic_object_sizes): Deallocate object_size_exprs.
	* gcc/tree-dynamic-object-size.h
	(compute_builtin_dyn_object_size): New argument.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-dynamic-object-size-0.c
	(test_builtin_malloc_condphi, test_builtin_malloc_condphi2,
	test_builtin_malloc_condphi3, test_builtin_calloc_condphi,
	test_deploop): New tests.
	(main): Call them.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 .../gcc.dg/builtin-dynamic-object-size-0.c    |  92 +++++++
 gcc/tree-dynamic-object-size.c                | 258 +++++++++++++++++-
 gcc/tree-dynamic-object-size.h                |   3 +-
 3 files changed, 339 insertions(+), 14 deletions(-)

diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
index 620e8cbc611..7098ef485c6 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -63,6 +63,48 @@ test_builtin_malloc_cond (int cond)
   return __builtin_dynamic_object_size (ret, 0);
 }
 
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi (int cond)
+{
+  void *ret;
+ 
+  if (cond)
+    ret = __builtin_malloc (32);
+  else
+    ret = __builtin_malloc (64);
+
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi2 (int cond, size_t in)
+{
+  void *ret;
+ 
+  if (cond)
+    ret = __builtin_malloc (in);
+  else
+    ret = __builtin_malloc (64);
+
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi3 (int cond, size_t in, size_t in2)
+{
+  void *ret;
+ 
+  if (cond)
+    ret = __builtin_malloc (in);
+  else
+    ret = __builtin_malloc (in2);
+
+  return __builtin_dynamic_object_size (ret, 0);
+}
+
 /* Calloc-like allocator.  */
 
 size_t
@@ -89,6 +131,21 @@ test_builtin_calloc_cond (int cond1, int cond2)
   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);
+}
+
 /* Passthrough functions.  */
 
 size_t
@@ -120,6 +177,19 @@ test_dynarray_cond (int cond)
   return __builtin_dynamic_object_size (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 (sz);
+
+  return __builtin_dynamic_object_size (bin, 0);
+}
+
 int
 main (int argc, char **argv)
 {
@@ -141,6 +211,18 @@ main (int argc, char **argv)
     FAIL ();
   if (test_builtin_malloc_cond (0) != 64)
     FAIL ();
+  if (test_builtin_malloc_condphi (1) != 32)
+    FAIL ();
+  if (test_builtin_malloc_condphi (0) != 64)
+    FAIL ();
+  if (test_builtin_malloc_condphi2 (1, 128) != 128)
+    FAIL ();
+  if (test_builtin_malloc_condphi2 (0, 128) != 64)
+    FAIL ();
+  if (test_builtin_malloc_condphi3 (1, 128, 256) != 128)
+    FAIL ();
+  if (test_builtin_malloc_condphi3 (0, 128, 256) != 256)
+    FAIL ();
   if (test_calloc (2048, 4) != 2048 * 4)
     FAIL ();
   if (test_builtin_calloc (2048, 8) != 2048 * 8)
@@ -149,6 +231,12 @@ main (int argc, char **argv)
     FAIL ();
   if (test_builtin_calloc_cond (1, 1) != 32 * 1024)
     FAIL ();
+  if (test_builtin_calloc_condphi (128, 1, 1) != 128)
+    FAIL ();
+  if (test_builtin_calloc_condphi (128, 1, 0) == 128)
+    FAIL ();
+  if (test_builtin_calloc_condphi (128, 1, 0) == -1)
+    FAIL ();
   if (test_passthrough (__builtin_strlen (argv[0]) + 1, argv[0])
       != __builtin_strlen (argv[0]) + 1)
     FAIL ();
@@ -158,6 +246,10 @@ main (int argc, char **argv)
     FAIL ();
   if (test_dynarray_cond (1) != 8)
     FAIL ();
+  if (test_deploop (128, 4) != 128)
+    FAIL ();
+  if (test_deploop (128, 129) != 32)
+    FAIL ();
 
   if (nfails > 0)
     __builtin_abort ();
diff --git a/gcc/tree-dynamic-object-size.c b/gcc/tree-dynamic-object-size.c
index 8e52dd46c03..483d6782d49 100644
--- a/gcc/tree-dynamic-object-size.c
+++ b/gcc/tree-dynamic-object-size.c
@@ -62,11 +62,14 @@ along with GCC; see the file COPYING3.  If not see
 #include "attribs.h"
 #include "builtins.h"
 #include "print-tree.h"
+#include "gimplify-me.h"
 
 struct object_size_info
 {
+  struct function *fun;
   int object_size_type;
   bitmap visited;
+  unsigned deploop;
 };
 
 static tree alloc_object_size (const gcall *);
@@ -78,9 +81,15 @@ static void collect_object_sizes_for (struct object_size_info *, tree);
    object_sizes[1] is upper bound for number of bytes till the end of
    the subobject (innermost array or field with address taken).
    object_sizes[2] is lower bound for number of bytes till the end of
-   the object and object_sizes[3] lower bound for subobject.  */
+   the object and object_sizes[3] lower bound for subobject.
+
+   These are merely SSA names of the sizes.  The actual size expressions are in
+   object_size_exprs and they need not be SSA.  */
 static vec<tree> object_sizes[4];
 
+/* The actual size expressions, indexed by the object SSA names.  */
+static vec<tree *>object_size_exprs[4];
+
 /* Bitmaps what object sizes have been computed already.  */
 static bitmap computed[4];
 
@@ -166,13 +175,123 @@ pass_through_call (const gcall *call)
   return NULL_TREE;
 }
 
+static void
+emit_size_stmts (gimple *stmt, tree size_ssa, tree size_expr)
+{
+  gimple_seq seq = NULL;
+
+  if (!is_gimple_variable (size_expr))
+    size_expr = force_gimple_operand (size_expr, &seq, true, NULL);
+
+  gassign *assign = gimple_build_assign (size_ssa, size_expr);
+  gimple_seq_add_stmt (&seq, assign);
+
+  /* Define object size right after the object is defined.  */
+  gimple_stmt_iterator i = gsi_for_stmt (stmt);
+  gsi_insert_seq_after (&i, seq, GSI_CONTINUE_LINKING);
+}
+
+static void
+emit_size_phi_stmts (gimple *stmt, tree size_ssa, tree *sizes)
+{
+  gphi *newphi = create_phi_node (size_ssa, 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]))
+	{
+	  gimple_seq seq;
+	  edge e = gimple_phi_arg_edge (obj_phi, i);
+	  sizes[i] = force_gimple_operand (sizes[i], &seq, true, NULL);
+
+	  /* Put the size definition before the last statement of the source
+	     block of the PHI edge.  This ensures that any branches at the end
+	     of the source block remain the last statement.  We are OK even if
+	     the last statement is the definition of the object since it will
+	     succeed any definitions that contribute to its size and the size
+	     expression will succeed them too.  */
+	  gimple_stmt_iterator gsi = gsi_last_bb (e->src);
+	  gsi_insert_seq_before (&gsi, seq, GSI_CONTINUE_LINKING);
+	}
+
+      add_phi_arg (newphi, sizes[i],
+		   gimple_phi_arg_edge (obj_phi, i),
+		   gimple_phi_arg_location (obj_phi, i));
+    }
+}
+
+static void
+eval_size_expr (tree var, tree size, tree *size_expr)
+{
+  if (size_expr != NULL)
+    {
+      gcc_assert (*size_expr != error_mark_node);
+
+      gimple *stmt = SSA_NAME_DEF_STMT (var);
+
+      if (gimple_code (stmt) == GIMPLE_PHI)
+	{
+	  emit_size_phi_stmts (stmt, size, size_expr);
+	  delete[] size_expr;
+	}
+      else
+	{
+	  emit_size_stmts (stmt, size, *size_expr);
+	  delete size_expr;
+	}
+    }
+}
+
+static void
+gimplify_size_exprs (object_size_info *osi, tree ptr)
+{
+  bitmap_iterator bi;
+  unsigned i;
+
+  /* If the size lookup was not successful and we were left with unresolved
+     loop dependencies, then invalidate all size computations.  This is
+     suboptimal and should eventually try to remove only size expressions that
+     depend on the unresolved dependencies, but it's unclear whether
+     maintaining the extra state to manage that is worthwhile.  */
+  if (object_sizes[osi->object_size_type][SSA_NAME_VERSION (ptr)] == NULL_TREE
+      && osi->deploop)
+    {
+      for (int object_size_type = 0; object_size_type <= 3; object_size_type++)
+	{
+	  EXECUTE_IF_SET_IN_BITMAP (computed[object_size_type], 0, i, bi)
+	    {
+	      release_ssa_name (object_sizes[object_size_type][i]);
+	      if (gimple_code (SSA_NAME_DEF_STMT (ssa_name (i))) == GIMPLE_PHI
+		  && object_size_exprs[object_size_type][i])
+		delete [] object_size_exprs[object_size_type][i];
+	      else
+		delete object_size_exprs[object_size_type][i];
+	      object_size_exprs[object_size_type][i] = NULL;
+	    }
+	  bitmap_clear (computed[object_size_type]);
+	}
+      return;
+    }
+
+  /* Gimplify and emit code for all computed size expressions.  */
+  for (int object_size_type = 0; object_size_type <= 3; object_size_type++)
+    EXECUTE_IF_SET_IN_BITMAP (computed[object_size_type], 0, i, bi)
+      {
+	eval_size_expr (ssa_name (i), object_sizes[object_size_type][i],
+			object_size_exprs[object_size_type][i]);
+	object_size_exprs[object_size_type][i] = NULL;
+      }
+}
+
 /* 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)
+compute_builtin_dyn_object_size (tree ptr, int object_size_type, tree *psize,
+				 struct function *fun)
 {
   gcc_assert (object_size_type >= 0 && object_size_type <= 3);
 
@@ -195,7 +314,10 @@ compute_builtin_dyn_object_size (tree ptr, int object_size_type, tree *psize)
   unsigned int i;
 
   if (num_ssa_names > object_sizes[object_size_type].length ())
-    object_sizes[object_size_type].safe_grow (num_ssa_names, true);
+    {
+      object_sizes[object_size_type].safe_grow (num_ssa_names, true);
+      object_size_exprs[object_size_type].safe_grow (num_ssa_names, true);
+    }
   if (dump_file)
     {
       fprintf (dump_file, "Computing %s dynamic %sobject size for ",
@@ -206,9 +328,12 @@ compute_builtin_dyn_object_size (tree ptr, int object_size_type, tree *psize)
     }
 
   osi.visited = BITMAP_ALLOC (NULL);
+  osi.deploop = 0;
   osi.object_size_type = object_size_type;
+  osi.fun = fun != NULL ? fun : cfun;
 
   collect_object_sizes_for (&osi, ptr);
+  gimplify_size_exprs (&osi, ptr);
 
   /* Debugging dumps.  */
   if (dump_file)
@@ -234,6 +359,73 @@ out:
   return *psize != NULL_TREE;
 }
 
+static void
+maybe_update_dependency_loop (struct object_size_info *osi, unsigned name,
+			      tree sz)
+{
+  if (sz == error_mark_node)
+    osi->deploop++;
+  else if (object_sizes[osi->object_size_type][name]
+	   && (*object_size_exprs[osi->object_size_type][name]
+	       == error_mark_node))
+    osi->deploop--;
+}
+
+/* Add object size to the cache so that it can be reused.  */
+
+static void
+cache_object_size (struct object_size_info *osi, unsigned name, tree *sz)
+{
+  int object_size_type = osi->object_size_type;
+  struct function *fun = osi->fun;
+
+  gcc_assert (sz);
+
+  maybe_update_dependency_loop (osi, name, *sz);
+
+  /* Reuse SSA name if it was created for a dependency loop.  */
+  if (object_sizes[object_size_type][name] != NULL_TREE)
+    gcc_assert (*object_size_exprs[object_size_type][name] == error_mark_node);
+  else
+    object_sizes[object_size_type][name] = make_ssa_name_fn (fun, sizetype, 0);
+  object_size_exprs[object_size_type][name] = sz;
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "Caching size for ");
+      print_generic_expr (dump_file, ssa_name (name), dump_flags);
+      fprintf (dump_file, ":: Object size: ");
+      print_generic_expr (dump_file, object_sizes[object_size_type][name],
+			  dump_flags);
+      fprintf (dump_file, " = ");
+      print_generic_expr (dump_file,
+			  *object_size_exprs[object_size_type][name],
+			  dump_flags);
+      fprintf (dump_file, "\n");
+    }
+
+  bitmap_set_bit (computed[object_size_type], name);
+}
+
+/* Copy SZ and then call cache_object_size above.  */
+
+static void
+cache_object_size_copy (struct object_size_info *osi, unsigned name, tree sz)
+{
+  int object_size_type = osi->object_size_type;
+
+  if (sz == NULL_TREE)
+    {
+      if (object_sizes[object_size_type][name] != NULL_TREE)
+	release_ssa_name (object_sizes[object_size_type][name]);
+      object_sizes[object_size_type][name] = NULL_TREE;
+      object_size_exprs[object_size_type][name] = NULL;
+      bitmap_set_bit (computed[object_size_type], name);
+      return;
+    }
+
+  cache_object_size (osi, name, new tree (sz));
+}
 
 /* Use size of ORIG for DEST and return it.  */
 
@@ -241,8 +433,9 @@ 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)];
+
+  tree sz = object_sizes[osi->object_size_type][SSA_NAME_VERSION (orig)];
+  cache_object_size_copy (osi, SSA_NAME_VERSION (dest), sz);
 }
 
 
@@ -251,11 +444,10 @@ set_object_size_ssa (struct object_size_info *osi, tree dest, tree orig)
 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);
+  cache_object_size_copy (osi, SSA_NAME_VERSION (ptr),
+			  alloc_object_size (call));
 }
 
 
@@ -264,7 +456,9 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call)
    - 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.  */
+     the object size is object size of that argument.
+   - For GIMPLE_PHI, compute a PHI node with sizes of all branches in the PHI
+     node of the object.  */
 
 static void
 collect_object_sizes_for (struct object_size_info *osi, tree var)
@@ -280,7 +474,10 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
     object_sizes[object_size_type][varno] = NULL_TREE;
   else
     {
-      /* No dependency loop handling at the moment.  */
+      /* Add an SSA name but mark the expression as being an error_mark_node.
+	 When we go back up the stack, the error_mark_node should get
+	 overwritten by a proper expression.  */
+      cache_object_size (osi, varno, &error_mark_node);
       if (dump_file && (dump_flags & TDF_DETAILS))
 	{
 	  fprintf (dump_file, "Found a dependency loop at ");
@@ -315,18 +512,51 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 	break;
       }
 
+    case GIMPLE_PHI:
+      {
+	unsigned i, num_args = gimple_phi_num_args (stmt);
+	tree *sizes = new tree[num_args] ();
+
+	/* Bail out if any of the PHI arguments are non-SSA expressions or
+	   if size of an argument cannot be determined.  */
+	for (i = 0; i < gimple_phi_num_args (stmt); i++)
+	  {
+	    tree rhs = gimple_phi_arg_def (stmt, i);
+
+	    if (TREE_CODE (rhs) != SSA_NAME)
+	      break;
+
+	    collect_object_sizes_for (osi, rhs);
+	    tree sz = object_sizes[object_size_type][SSA_NAME_VERSION (rhs)];
+
+	    if (sz == NULL_TREE)
+	      break;
+
+	    sizes[i] = sz;
+	  }
+
+	/* Record all possible sizes to build our PHI node later.  */
+	if (i == gimple_phi_num_args (stmt))
+	  {
+	    cache_object_size (osi, varno, sizes);
+	    break;
+	  }
+	else
+	  delete[] sizes;
+      }
+    /* FALLTHROUGH */
+
     /* Bail out for all other cases.  */
     case GIMPLE_NOP:
-    case GIMPLE_PHI:
     case GIMPLE_ASSIGN:
     case GIMPLE_ASM:
+      cache_object_size_copy (osi, varno, NULL_TREE);
       break;
 
     default:
       gcc_unreachable ();
     }
-
-  bitmap_set_bit (computed[object_size_type], varno);
+  gcc_assert (bitmap_bit_p (computed[object_size_type], varno));
 }
 
 
@@ -343,6 +573,7 @@ init_dynamic_object_sizes (void)
   for (object_size_type = 0; object_size_type <= 3; object_size_type++)
     {
       object_sizes[object_size_type].safe_grow (num_ssa_names, true);
+      object_size_exprs[object_size_type].safe_grow (num_ssa_names, true);
       computed[object_size_type] = BITMAP_ALLOC (NULL);
     }
 }
@@ -358,6 +589,7 @@ fini_dynamic_object_sizes (void)
   for (object_size_type = 0; object_size_type <= 3; object_size_type++)
     {
       object_sizes[object_size_type].release ();
+      object_size_exprs[object_size_type].release ();
       BITMAP_FREE (computed[object_size_type]);
     }
 }
diff --git a/gcc/tree-dynamic-object-size.h b/gcc/tree-dynamic-object-size.h
index 145b4b88bca..a4e0236e43e 100644
--- a/gcc/tree-dynamic-object-size.h
+++ b/gcc/tree-dynamic-object-size.h
@@ -20,6 +20,7 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_TREE_DYNAMIC_OBJECT_SIZE_H
 #define GCC_TREE_DYNAMIC_OBJECT_SIZE_H
 
-extern bool compute_builtin_dyn_object_size (tree, int, tree *);
+extern bool compute_builtin_dyn_object_size (tree, int, tree *,
+					     struct function * = NULL);
 
 #endif  // GCC_TREE_DYNAMIC_OBJECT_SIZE_H
-- 
2.31.1


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

* [PATCH 4/8] tree-dynamic-object-size: Support ADDR_EXPR
  2021-10-07 22:14 [PATCH 0/8] __builtin_dynamic_object_size and more Siddhesh Poyarekar
                   ` (2 preceding siblings ...)
  2021-10-07 22:14 ` [PATCH 3/8] tree-dynamic-object-size: Handle GIMPLE_PHI Siddhesh Poyarekar
@ 2021-10-07 22:14 ` Siddhesh Poyarekar
  2021-10-07 22:14 ` [PATCH 5/8] tree-dynamic-object-size: Handle GIMPLE_ASSIGN Siddhesh Poyarekar
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 18+ messages in thread
From: Siddhesh Poyarekar @ 2021-10-07 22:14 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

This is the beginnings of support for ADDR_EXPR objects and is partly
based on the implementation in tree-object-size, splitting out
functions for readability.

One key difference from constant-size ADDR_EXPR computation is the
computation of residual sizes based on offset.  This pass attempts to
compute an expression with guards to try and not overflow.  This is
however still rudimentary and a subsequent patch for subobject support
makes it more comprehensive by handling negative offsets as well.

The size expressions based on offsets may look arbitrarily complex but
in practice most parts of the expression tend to fold away due to
being constants.  Despite that it is still a potential bottleneck and
may need some artifical backstop (such as bailing out on computation
if the size expression nests more than an arbitrarily chosen N levels)
to reduce compile time as well as avoid adding too much of a
performance overhead to generated code.

gcc/ChangeLog:

	* builtins.c (fold_builtin_dyn_object_size): Handle ADDR_EXPR.
	* tree-dynamic-object-size.c (compute_object_offset,
	size_for_offset, get_whole_var, whole_var_size,
	addr_dyn_object_size): New functions.
	(compute_builtin_dyn_object_size): Handle ADDR_EXPR.
	(expr_object_size): New function.
	(collect_object_sizes_for): Use it.

gcc/testsuite/ChangeLog:

	* gcc.dg/builtin-dynamic-object-size-0.c
	(test_builtin_malloc_condphi4, test_builtin_malloc_condphi5,
	test_passthrough_nonssa): New tests.
	(main): Call them.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 gcc/builtins.c                                |   2 +-
 .../gcc.dg/builtin-dynamic-object-size-0.c    |  37 +++
 gcc/tree-dynamic-object-size.c                | 255 +++++++++++++++++-
 3 files changed, 287 insertions(+), 7 deletions(-)

diff --git a/gcc/builtins.c b/gcc/builtins.c
index d015029765b..c1e23324552 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -10339,7 +10339,7 @@ fold_builtin_dyn_object_size (tree ptr, tree ost)
 
   /* 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
+  if ((TREE_CODE (ptr) == SSA_NAME || TREE_CODE (ptr) == ADDR_EXPR)
       && compute_builtin_dyn_object_size (ptr, object_size_type, &bytes))
     return bytes;
 
diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
index 7098ef485c6..22c86190341 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -105,6 +105,25 @@ test_builtin_malloc_condphi3 (int cond, size_t in, size_t in2)
   return __builtin_dynamic_object_size (ret, 0);
 }
 
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi4 (size_t sz, int cond)
+{
+  char *a = __builtin_malloc (sz);
+  char b[sz / 2];
+
+  return __builtin_dynamic_object_size (cond ? b : (void *) &a, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_builtin_malloc_condphi5 (size_t sz, int cond, char *c)
+{
+  char *a = __builtin_malloc (sz);
+
+  return __builtin_dynamic_object_size (cond ? c : (void *) &a, 0);
+}
+
 /* Calloc-like allocator.  */
 
 size_t
@@ -158,6 +177,16 @@ test_passthrough (size_t sz, char *in)
   return __builtin_dynamic_object_size (dest, 0);
 }
 
+size_t
+__attribute__ ((noinline))
+test_passthrough_nonssa (char *in)
+{
+  char bin[__builtin_strlen (in) + 1];
+  char *dest = __builtin_memcpy (bin, in, __builtin_strlen (in) + 1);
+
+  return __builtin_dynamic_object_size (dest, 0);
+}
+
 /* Variable length arrays.  */
 size_t
 __attribute__ ((noinline))
@@ -223,6 +252,12 @@ main (int argc, char **argv)
     FAIL ();
   if (test_builtin_malloc_condphi3 (0, 128, 256) != 256)
     FAIL ();
+  if (test_builtin_malloc_condphi4 (128, 1) != 64)
+    FAIL ();
+  if (test_builtin_malloc_condphi4 (128, 0) != sizeof (void *))
+    FAIL ();
+  if (test_builtin_malloc_condphi5 (128, 0, argv[0]) != -1)
+    FAIL ();
   if (test_calloc (2048, 4) != 2048 * 4)
     FAIL ();
   if (test_builtin_calloc (2048, 8) != 2048 * 8)
@@ -240,6 +275,8 @@ main (int argc, char **argv)
   if (test_passthrough (__builtin_strlen (argv[0]) + 1, argv[0])
       != __builtin_strlen (argv[0]) + 1)
     FAIL ();
+  if (test_passthrough_nonssa (argv[0]) != __builtin_strlen (argv[0]) + 1)
+     FAIL ();
   if (test_dynarray (__builtin_strlen (argv[0])) != __builtin_strlen (argv[0]))
     FAIL ();
   if (test_dynarray_cond (0) != 16)
diff --git a/gcc/tree-dynamic-object-size.c b/gcc/tree-dynamic-object-size.c
index 483d6782d49..6cc63abd010 100644
--- a/gcc/tree-dynamic-object-size.c
+++ b/gcc/tree-dynamic-object-size.c
@@ -93,6 +93,212 @@ static vec<tree *>object_size_exprs[4];
 /* Bitmaps what object sizes have been computed already.  */
 static bitmap computed[4];
 
+bool compute_builtin_dyn_object_size (tree, int, tree *,
+				      struct function *fun = NULL);
+
+/* Compute offset of EXPR within VAR.  Return error_mark_node if unknown.  */
+
+static tree
+compute_object_offset (const_tree expr, const_tree var)
+{
+  enum tree_code code = PLUS_EXPR;
+  tree base, off, t;
+
+  if (expr == var)
+    return size_zero_node;
+
+  switch (TREE_CODE (expr))
+    {
+    case COMPONENT_REF:
+      base = compute_object_offset (TREE_OPERAND (expr, 0), var);
+      if (base == error_mark_node)
+	return base;
+
+      t = TREE_OPERAND (expr, 1);
+      off = fold_build2 (PLUS_EXPR, sizetype, DECL_FIELD_OFFSET (t),
+			 size_int (tree_to_uhwi (DECL_FIELD_BIT_OFFSET (t))
+				   / BITS_PER_UNIT));
+      break;
+
+    case REALPART_EXPR:
+    CASE_CONVERT:
+    case VIEW_CONVERT_EXPR:
+    case NON_LVALUE_EXPR:
+      return compute_object_offset (TREE_OPERAND (expr, 0), var);
+
+    case IMAGPART_EXPR:
+      base = compute_object_offset (TREE_OPERAND (expr, 0), var);
+      if (base == error_mark_node)
+	return base;
+
+      off = TYPE_SIZE_UNIT (TREE_TYPE (expr));
+      break;
+
+    case ARRAY_REF:
+      base = compute_object_offset (TREE_OPERAND (expr, 0), var);
+      if (base == error_mark_node)
+	return base;
+
+      t = TREE_OPERAND (expr, 1);
+      tree low_bound, unit_size;
+      low_bound = array_ref_low_bound (CONST_CAST_TREE (expr));
+      unit_size = array_ref_element_size (CONST_CAST_TREE (expr));
+      if (! integer_zerop (low_bound))
+	t = fold_build2 (MINUS_EXPR, TREE_TYPE (t), t, low_bound);
+      if (TREE_CODE (t) == INTEGER_CST && tree_int_cst_sgn (t) < 0)
+	{
+	  code = MINUS_EXPR;
+	  t = fold_build1 (NEGATE_EXPR, TREE_TYPE (t), t);
+	}
+      t = fold_convert (sizetype, t);
+      off = fold_build2 (MULT_EXPR, sizetype, unit_size, t);
+      break;
+
+    case MEM_REF:
+      gcc_assert (TREE_CODE (TREE_OPERAND (expr, 0)) == ADDR_EXPR);
+      return wide_int_to_tree (sizetype, mem_ref_offset (expr));
+
+    default:
+      return error_mark_node;
+    }
+
+  return fold_build2 (code, sizetype, base, off);
+}
+
+/* Given an object size SZ and offset OFF into it, compute the usable object
+   size.  The expression returns 0 for all offsets that invoke undefined
+   behaviour.  */
+
+static tree
+size_for_offset (tree sz, tree off)
+{
+  /* A MEM_REF offset may be a pointer, where we need to figure out the
+     multiplier based on the base type.  */
+  if (POINTER_TYPE_P (TREE_TYPE (off)))
+    {
+      tree typesize = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (off)));
+
+      if (typesize)
+	{
+	  /*  SZ < TYPESIZE ? SZ : TYPESIZE * MIN (SZ / TYPESIZE, OFF)  */
+	  tree cond = fold_build2 (LT_EXPR, sizetype, sz, typesize);
+
+	  tree tmp = fold_build2 (EXACT_DIV_EXPR, sizetype, sz, typesize);
+	  tmp = fold_build2 (MIN_EXPR, sizetype, tmp, off);
+	  tmp = fold_build2 (MULT_EXPR, sizetype, tmp, typesize);
+
+	  off = fold_build3 (COND_EXPR, sizetype, cond, sz, tmp);
+
+	  return fold_build2 (MINUS_EXPR, sizetype, sz, off);
+	}
+      else
+	off = fold_convert (sizetype, off);
+    }
+
+  off = fold_build2 (MIN_EXPR, sizetype, sz, off);
+  return fold_build2 (MINUS_EXPR, sizetype, sz, off);
+}
+
+/* Peek through ADDR_EXPR operands to get the definition of the whole variable
+   PTR points in.  Write the result expression into PT_VARP and its size into
+   PT_VAR_SIZEP.  Return true if the object is found.  */
+
+static tree
+get_whole_var (const_tree ptr)
+{
+  tree pt_var;
+
+  pt_var = TREE_OPERAND (ptr, 0);
+  while (handled_component_p (pt_var))
+    pt_var = TREE_OPERAND (pt_var, 0);
+
+  return pt_var;
+}
+
+static bool
+whole_var_size (struct object_size_info *osi, tree pt_var,
+		int object_size_type, tree *pt_var_sizep)
+{
+  tree pt_var_size = NULL_TREE;
+  int subobject = object_size_type & 1;
+  int min = object_size_type & 2;
+
+  if (TREE_CODE (pt_var) == MEM_REF)
+    {
+      tree var = TREE_OPERAND (pt_var, 0);
+      if (!osi || subobject || TREE_CODE (var) != SSA_NAME)
+	compute_builtin_dyn_object_size (var, min, &pt_var_size);
+      else
+	{
+	  collect_object_sizes_for (osi, var);
+	  pt_var_size = object_sizes[object_size_type][SSA_NAME_VERSION (var)];
+	}
+
+      if (pt_var_size != NULL_TREE)
+	{
+	  tree offset = wide_int_to_tree (size_type_node,
+					  mem_ref_offset (pt_var));
+
+	  pt_var_size = size_for_offset (pt_var_size, offset);
+	}
+    }
+  else if (DECL_P (pt_var))
+    {
+      pt_var_size = decl_init_size (pt_var, min);
+      if (!pt_var_size)
+	return false;
+    }
+  else if (TREE_CODE (pt_var) == STRING_CST)
+    pt_var_size = TYPE_SIZE_UNIT (TREE_TYPE (pt_var));
+  else
+    return false;
+
+  *pt_var_sizep = pt_var_size;
+  return true;
+}
+
+/* Compute an object size estimate for PTR, which is a ADDR_EXPR.
+   OBJECT_SIZE_TYPE is the second argument from __builtin_dynamic_object_size.
+   If unknown, return false, setting PSIZE to NULL_TREE.  */
+
+static bool
+addr_dyn_object_size (struct object_size_info *osi, const_tree ptr,
+		      int object_size_type, tree *psize)
+{
+  tree pt_var, pt_var_size = NULL_TREE, bytes;
+
+  gcc_assert (TREE_CODE (ptr) == ADDR_EXPR);
+
+  /* Set to unknown and overwrite just before returning if the size
+     could be determined.  */
+  *psize = NULL_TREE;
+
+  pt_var = get_whole_var (ptr);
+
+  if (!pt_var)
+    return false;
+
+  if (!whole_var_size (osi, pt_var, object_size_type, &pt_var_size))
+    return false;
+
+  if (!pt_var_size)
+    return false;
+
+  /* PTR points to a subobject of whole variable PT_VAR.  */
+  if (pt_var != TREE_OPERAND (ptr, 0))
+    {
+      bytes = compute_object_offset (TREE_OPERAND (ptr, 0), pt_var);
+      if (bytes != error_mark_node)
+	bytes = size_for_offset (pt_var_size, bytes);
+    }
+  else
+    bytes = pt_var_size;
+
+  *psize = bytes == error_mark_node ? NULL_TREE : bytes;
+  return true;
+}
+
+
 /* 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.
@@ -299,6 +505,13 @@ compute_builtin_dyn_object_size (tree ptr, int object_size_type, tree *psize,
      could be determined.  */
   *psize = NULL_TREE;
 
+  if (TREE_CODE (ptr) == ADDR_EXPR)
+    /* We assume that the caller will gimplify the expression.  If the
+       expression depends on any SSA objects, its size expression is gimplified
+       and returned, so the expression will definitely not depend on the cached
+       objects.  */
+    return addr_dyn_object_size (NULL, ptr, object_size_type, psize);
+
   if (TREE_CODE (ptr) != SSA_NAME
       || !POINTER_TYPE_P (TREE_TYPE (ptr)))
       return false;
@@ -359,6 +572,27 @@ out:
   return *psize != NULL_TREE;
 }
 
+/* Compute object_sizes an object defined to VALUE, which is not an
+   SSA_NAME.  */
+
+static void
+expr_object_size (struct object_size_info *osi, tree value, tree *sz)
+{
+  int object_size_type = osi->object_size_type;
+  tree bytes = NULL_TREE;
+
+  if (TREE_CODE (value) == WITH_SIZE_EXPR)
+    value = TREE_OPERAND (value, 0);
+
+  if (TREE_CODE (value) == ADDR_EXPR)
+    addr_dyn_object_size (osi, value, object_size_type, &bytes);
+
+  if (bytes)
+    STRIP_NOPS (bytes);
+
+  *sz = bytes;
+}
+
 static void
 maybe_update_dependency_loop (struct object_size_info *osi, unsigned name,
 			      tree sz)
@@ -506,6 +740,12 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 	  {
 	    if (TREE_CODE (arg) == SSA_NAME)
 	      set_object_size_ssa (osi, var, arg);
+	    else
+	      {
+		tree ret;
+		expr_object_size (osi, arg, &ret);
+		cache_object_size_copy (osi, varno, ret);
+	      }
 	  }
 	else
 	  call_object_size (osi, var, call_stmt);
@@ -517,17 +757,20 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 	unsigned i, num_args = gimple_phi_num_args (stmt);
 	tree *sizes = new tree[num_args] ();
 
-	/* Bail out if any of the PHI arguments are non-SSA expressions or
-	   if size of an argument cannot be determined.  */
+	/* Bail out if the size of any of the PHI arguments cannot be
+	   determined.  */
 	for (i = 0; i < gimple_phi_num_args (stmt); i++)
 	  {
 	    tree rhs = gimple_phi_arg_def (stmt, i);
+	    tree sz;
 
 	    if (TREE_CODE (rhs) != SSA_NAME)
-	      break;
-
-	    collect_object_sizes_for (osi, rhs);
-	    tree sz = object_sizes[object_size_type][SSA_NAME_VERSION (rhs)];
+	      expr_object_size (osi, rhs, &sz);
+	    else
+	      {
+		collect_object_sizes_for (osi, rhs);
+		sz = object_sizes[object_size_type][SSA_NAME_VERSION (rhs)];
+	      }
 
 	    if (sz == NULL_TREE)
 	      break;
-- 
2.31.1


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

* [PATCH 5/8] tree-dynamic-object-size: Handle GIMPLE_ASSIGN
  2021-10-07 22:14 [PATCH 0/8] __builtin_dynamic_object_size and more Siddhesh Poyarekar
                   ` (3 preceding siblings ...)
  2021-10-07 22:14 ` [PATCH 4/8] tree-dynamic-object-size: Support ADDR_EXPR Siddhesh Poyarekar
@ 2021-10-07 22:14 ` Siddhesh Poyarekar
  2021-10-07 22:14 ` [PATCH 6/8] tree-dynamic-object-size: Handle function parameters Siddhesh Poyarekar
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 18+ messages in thread
From: Siddhesh Poyarekar @ 2021-10-07 22:14 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Follow assignment operations and pointer-plus operations to get the
effective object size.  Unlike tree-object-size, this pass handles
negative offsets correctly and returns a size within the bounds of the
parent object; offsets > SIZE_MAX / 2 are considered negative to
emulate ssize_t.

To make negative offsets work, all object size computations store the
"whole variable" size to ensure that the negative offsets don't go
beyond bounds of the object within which PTR points.  As mentioned in
the previous patch, this makes the size_for_offset expressions quite
complex, but the aim at the moment is to be more correct than fast.
We can tweak this later if it is found to have a noticeable
performance impact.

When gcc is invoked without optimization, attempt to minimally
recognize GIMPLE_ASSIGN and ADDR_EXPR expressions so that
__builtin_dynamic_object_size is at least minimally useful without
optimization.

gcc/ChangeLog:

	* tree-dynamic-object-size.c (object_whole_sizes,
	object_whole_size_exprs): New vectors.
	(size_for_offset): Support negative offsets.
	(whole_var_size): Adjust.
	(addr_dyn_object_size): New argument wholesizep.
	(emit_size_stmts): New arguments wholesize_ssa and
	wholesize_expr.  Emit statements for whole size.
	(emit_size_phi_stmts): Likewise.
	(size_bound_expr): New function.
	(eval_size_expr): Call it.  New arguments wholesize and
	wholesize_expr.
	(gimplify_size_exprs): Adjust for whole sizes.
	(compute_builtin_dyn_object_size): Do simple object size
	computation for unoptimized case.
	(expr_object_size, cache_object_size,cache_object_size_copy,
	set_object_size_ssa, call_object_size): Adjust for wholesize.
	(make_object_size_name, plus_stmt_object_size): New functions.
	(collect_object_sizes_for): Support GIMPLE_ASSIGN.  Adjust for
	wholesize.
	(init_dynamic_object_sizes): Allocate object_wholesizes and
	object_wholesize_exprs.
	(fini_dynamic_object_sizes): Deallocate object_wholesizes and
	object_wholesize_exprs.

gcc/testsuite/ChangeLog

	* gcc.dg/builtin-dynamic-object-size-0.c (dynarray_struct):
	New struct.
	(test_dynarray_struct, test_substring,
	test_substring_ptrplus): New tests.
	(main): Call them.

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

diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
index 22c86190341..3c2c4c84264 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -219,6 +219,42 @@ test_deploop (size_t sz, size_t cond)
   return __builtin_dynamic_object_size (bin, 0);
 }
 
+/* Address expressions.  */
+
+struct dynarray_struct
+{
+  long a;
+  char c[16];
+  int b;
+};
+
+size_t
+__attribute__ ((noinline))
+test_dynarray_struct (size_t sz, size_t off)
+{
+  struct dynarray_struct bin[sz];
+
+  return __builtin_dynamic_object_size (&bin[off].c, 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_substring (size_t sz, size_t off)
+{
+  char str[sz];
+
+  return __builtin_dynamic_object_size (&str[off], 0);
+}
+
+size_t
+__attribute__ ((noinline))
+test_substring_ptrplus (size_t sz, size_t off)
+{
+  int str[sz];
+
+  return __builtin_dynamic_object_size (str + off, 0);
+}
+
 int
 main (int argc, char **argv)
 {
@@ -279,6 +315,20 @@ main (int argc, char **argv)
      FAIL ();
   if (test_dynarray (__builtin_strlen (argv[0])) != __builtin_strlen (argv[0]))
     FAIL ();
+  if (test_dynarray_struct (42, 4) !=
+      ((42 - 4) * sizeof (struct dynarray_struct)
+       - __builtin_offsetof (struct dynarray_struct, c)))
+    FAIL ();
+  if (test_dynarray_struct (42, 48) != 0)
+    FAIL ();
+  if (test_substring (128, 4) != 128 - 4)
+    FAIL ();
+  if (test_substring (128, 142) != 0)
+    FAIL ();
+  if (test_substring_ptrplus (128, 4) != (128 - 4) * sizeof (int))
+    FAIL ();
+  if (test_substring_ptrplus (128, 142) != 0)
+    FAIL ();
   if (test_dynarray_cond (0) != 16)
     FAIL ();
   if (test_dynarray_cond (1) != 8)
diff --git a/gcc/tree-dynamic-object-size.c b/gcc/tree-dynamic-object-size.c
index 6cc63abd010..f143a64777c 100644
--- a/gcc/tree-dynamic-object-size.c
+++ b/gcc/tree-dynamic-object-size.c
@@ -83,13 +83,18 @@ static void collect_object_sizes_for (struct object_size_info *, tree);
    object_sizes[2] is lower bound for number of bytes till the end of
    the object and object_sizes[3] lower bound for subobject.
 
-   These are merely SSA names of the sizes.  The actual size expressions are in
+   object_sizes are SSA names of the sizes.  The actual size expressions are in
    object_size_exprs and they need not be SSA.  */
 static vec<tree> object_sizes[4];
-
-/* The actual size expressions, indexed by the object SSA names.  */
 static vec<tree *>object_size_exprs[4];
 
+/* Indexed by object_size_type and SSA name versions of the object,
+   object_whole_sizes and object_whole_size_exprs have SSA names and
+   expressions of the whole objects in which the pointer points
+   respectively.  */
+static vec<tree> object_whole_sizes[4];
+static vec<tree *>object_whole_size_exprs[4];
+
 /* Bitmaps what object sizes have been computed already.  */
 static bitmap computed[4];
 
@@ -166,37 +171,102 @@ compute_object_offset (const_tree expr, const_tree var)
 }
 
 /* Given an object size SZ and offset OFF into it, compute the usable object
-   size.  The expression returns 0 for all offsets that invoke undefined
-   behaviour.  */
+   size.  For cases where the offset breaches object bounds, the expression
+   returns 0.
+
+   For positive offset (we asume > SIZE_MAX / 2 as negative to emulate
+   ssize_t) we assume that the offset, when scaled by typesize, is within
+   bounds of sz.  That is:
+
+   sz > typesize && off <= sz / typesize
+
+   For negative offsets, we ensure that the magnitude of the offset is within
+   bounds of the lower part of the object.  This ensures that underflows result
+   in zero size.  That is:
+
+   (off > SIZE_MAX / 2 && wholesize - min(wholesize, size) > typesize
+    && -off <= (wholesize - min(wholesize, size)) / typesize)
+
+   The whole thing is then capped to wholesize to cater for situations where
+   wholesize may have less memory allocated to it than needed, making subobject
+   sizes incorrect.  Effectively, the expression we generate is:
+
+   MIN(((sz > typesize && off <= sz / typesize)
+	|| (off > SIZE_MAX / 2 && wholesize - min(wholesize, size) > typesize
+	    && -off <= (wholesize - min(wholesize, size)) / typesize))
+       ? sz - off * typesize : 0, wholesize)
+
+   This is a fairly complex expression that may result in slow code in corner
+   cases but in most standard cases, many of those elements would be constant
+   (typesize certainly is) and is expected to be folded away.  */
 
 static tree
-size_for_offset (tree sz, tree off)
+size_for_offset (tree sz, tree off, tree wholesize, bool neg_offsets = false)
 {
+  tree typesize = NULL_TREE;
+  tree cond = NULL_TREE;
+  tree max_off = NULL_TREE;
+  tree tmp = NULL_TREE;
+
   /* A MEM_REF offset may be a pointer, where we need to figure out the
      multiplier based on the base type.  */
   if (POINTER_TYPE_P (TREE_TYPE (off)))
+    typesize = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (off)));
+
+  /* Expression for positive offset.  */
+  if (typesize)
     {
-      tree typesize = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (off)));
+      cond = fold_build2 (GT_EXPR, sizetype, sz, typesize);
+      max_off = fold_build2 (EXACT_DIV_EXPR, sizetype, sz, typesize);
+    }
+  else
+    max_off = sz;
 
-      if (typesize)
-	{
-	  /*  SZ < TYPESIZE ? SZ : TYPESIZE * MIN (SZ / TYPESIZE, OFF)  */
-	  tree cond = fold_build2 (LT_EXPR, sizetype, sz, typesize);
+  tmp = fold_build2 (LE_EXPR, sizetype, off, max_off);
+  cond = cond ? fold_build2 (TRUTH_ANDIF_EXPR, sizetype, cond, tmp) : tmp;
 
-	  tree tmp = fold_build2 (EXACT_DIV_EXPR, sizetype, sz, typesize);
-	  tmp = fold_build2 (MIN_EXPR, sizetype, tmp, off);
-	  tmp = fold_build2 (MULT_EXPR, sizetype, tmp, typesize);
+  /* Negative offsets.  */
+  if (wholesize && wholesize != sz && neg_offsets)
+    {
+      /* off > SIZE_MAX / 2 */
+      max_off = fold_build2 (RSHIFT_EXPR, sizetype, TYPE_MAX_VALUE (sizetype),
+			     size_one_node);
+      tree cond_neg = fold_build2 (GT_EXPR, sizetype, off, max_off);
 
-	  off = fold_build3 (COND_EXPR, sizetype, cond, sz, tmp);
+      tree neg_part = fold_build2 (MIN_EXPR, sizetype, wholesize, sz);
+      neg_part = fold_build2 (MINUS_EXPR, sizetype, wholesize, neg_part);
 
-	  return fold_build2 (MINUS_EXPR, sizetype, sz, off);
+      if (typesize)
+	{
+	  /* && wholesize - min(wholesize, size) > typesize */
+	  tmp = fold_build2 (GT_EXPR, sizetype, neg_part, typesize);
+	  cond_neg = fold_build2 (TRUTH_ANDIF_EXPR, sizetype, cond_neg, tmp);
+	  max_off = fold_build2 (EXACT_DIV_EXPR, sizetype, neg_part, typesize);
 	}
       else
-	off = fold_convert (sizetype, off);
+	max_off = neg_part;
+
+      /* && -off <= (wholesize - min(wholesize, size)) / typesize */
+      tmp = fold_build2 (MINUS_EXPR, sizetype, size_zero_node, off);
+      tmp = fold_build2 (LE_EXPR, sizetype, tmp, max_off);
+      cond_neg = fold_build2 (TRUTH_ANDIF_EXPR, sizetype, cond_neg, tmp);
+
+      cond = fold_build2 (TRUTH_ORIF_EXPR, sizetype, cond, cond_neg);
     }
 
-  off = fold_build2 (MIN_EXPR, sizetype, sz, off);
-  return fold_build2 (MINUS_EXPR, sizetype, sz, off);
+  off = typesize ? fold_build2 (MULT_EXPR, sizetype, sz, off) : off;
+
+  tree res = fold_build2 (MINUS_EXPR, sizetype, sz, off);
+  res = fold_build3 (COND_EXPR, sizetype, cond, res, size_zero_node);
+
+  /* Finally, cap the return value to wholesize.  This could typically happen
+     if the whole object did not get any actual storage, e.g. char var[0] or
+     less storage, e.g.
+     struct some_large_struct = __builtin_malloc (smolval).  */
+  if (wholesize)
+    res = fold_build2 (MIN_EXPR, sizetype, wholesize, res);
+
+  return res;
 }
 
 /* Peek through ADDR_EXPR operands to get the definition of the whole variable
@@ -239,7 +309,7 @@ whole_var_size (struct object_size_info *osi, tree pt_var,
 	  tree offset = wide_int_to_tree (size_type_node,
 					  mem_ref_offset (pt_var));
 
-	  pt_var_size = size_for_offset (pt_var_size, offset);
+	  pt_var_size = size_for_offset (pt_var_size, offset, pt_var_size);
 	}
     }
   else if (DECL_P (pt_var))
@@ -263,7 +333,8 @@ whole_var_size (struct object_size_info *osi, tree pt_var,
 
 static bool
 addr_dyn_object_size (struct object_size_info *osi, const_tree ptr,
-		      int object_size_type, tree *psize)
+		      int object_size_type, tree *psize,
+		      tree *wholesizep = NULL)
 {
   tree pt_var, pt_var_size = NULL_TREE, bytes;
 
@@ -289,12 +360,14 @@ addr_dyn_object_size (struct object_size_info *osi, const_tree ptr,
     {
       bytes = compute_object_offset (TREE_OPERAND (ptr, 0), pt_var);
       if (bytes != error_mark_node)
-	bytes = size_for_offset (pt_var_size, bytes);
+	bytes = size_for_offset (pt_var_size, bytes, pt_var_size);
     }
   else
     bytes = pt_var_size;
 
   *psize = bytes == error_mark_node ? NULL_TREE : bytes;
+  if (wholesizep)
+    *wholesizep = pt_var_size;
   return true;
 }
 
@@ -381,35 +454,63 @@ pass_through_call (const gcall *call)
   return NULL_TREE;
 }
 
+
 static void
-emit_size_stmts (gimple *stmt, tree size_ssa, tree size_expr)
+emit_size_stmts (gimple *stmt, tree wholesize_ssa,
+		 tree wholesize_expr, tree size_ssa, tree size_expr)
 {
   gimple_seq seq = NULL;
 
+  /* Gimplify the wholesize and size expressions.  */
+  if (!is_gimple_variable (wholesize_expr))
+    wholesize_expr = force_gimple_operand (wholesize_expr, &seq, true, NULL);
   if (!is_gimple_variable (size_expr))
-    size_expr = force_gimple_operand (size_expr, &seq, true, NULL);
+    {
+      gimple_seq s;
+      size_expr = force_gimple_operand (size_expr, &s, true, NULL);
+      gimple_seq_add_seq (&seq, s);
+    }
 
-  gassign *assign = gimple_build_assign (size_ssa, size_expr);
+  /* Assign them to their SSAs.  */
+  gassign *assign = gimple_build_assign (wholesize_ssa, wholesize_expr);
+  gimple_seq_add_stmt (&seq, assign);
+  assign = gimple_build_assign (size_ssa, size_expr);
   gimple_seq_add_stmt (&seq, assign);
 
-  /* Define object size right after the object is defined.  */
+  /* Define object size right before the object is defined, assuming that any
+     statements involved in evaluation of the object size expression precede
+     the definition statement.  For parameters, we don't have a definition
+     statement, so insert into the first code basic block.  */
   gimple_stmt_iterator i = gsi_for_stmt (stmt);
-  gsi_insert_seq_after (&i, seq, GSI_CONTINUE_LINKING);
+  gsi_insert_seq_before (&i, seq, GSI_CONTINUE_LINKING);
 }
 
 static void
-emit_size_phi_stmts (gimple *stmt, tree size_ssa, tree *sizes)
+emit_size_phi_stmts (gimple *stmt, tree wholesize_ssa, tree *wholesizes,
+		     tree size_ssa, tree *sizes)
 {
-  gphi *newphi = create_phi_node (size_ssa, gimple_bb (stmt));
+  gphi *wholephi = create_phi_node (wholesize_ssa, gimple_bb (stmt));
+  gphi *phi = create_phi_node (size_ssa, gimple_bb (stmt));
   gphi *obj_phi =  as_a <gphi *> (stmt);
 
   for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++)
     {
+      gimple_seq seq = NULL;
+
+      /* If we built an expression, we will need to build statements
+	 and insert them on the edge right away.  */
+      if (!is_gimple_variable (wholesizes[i]))
+	wholesizes[i] = force_gimple_operand (wholesizes[i], &seq, true, NULL);
       if (!is_gimple_variable (sizes[i]))
 	{
-	  gimple_seq seq;
+	  gimple_seq s;
+	  sizes[i] = force_gimple_operand (sizes[i], &s, true, NULL);
+	  gimple_seq_add_seq (&seq, s);
+	}
+
+      if (seq)
+	{
 	  edge e = gimple_phi_arg_edge (obj_phi, i);
-	  sizes[i] = force_gimple_operand (sizes[i], &seq, true, NULL);
 
 	  /* Put the size definition before the last statement of the source
 	     block of the PHI edge.  This ensures that any branches at the end
@@ -421,14 +522,28 @@ emit_size_phi_stmts (gimple *stmt, tree size_ssa, tree *sizes)
 	  gsi_insert_seq_before (&gsi, seq, GSI_CONTINUE_LINKING);
 	}
 
-      add_phi_arg (newphi, sizes[i],
+      add_phi_arg (wholephi, wholesizes[i],
+		   gimple_phi_arg_edge (obj_phi, i),
+		   gimple_phi_arg_location (obj_phi, i));
+
+      add_phi_arg (phi, sizes[i],
 		   gimple_phi_arg_edge (obj_phi, i),
 		   gimple_phi_arg_location (obj_phi, i));
     }
 }
 
+/* Set upper bound to SIZE_MAX - 1 so that equality with (size_t) -1 fails.  */
+
+static tree
+size_bound_expr (tree sz)
+{
+  tree max = size_binop (MINUS_EXPR, TYPE_MAX_VALUE (sizetype), size_one_node);
+  return fold_build2 (MIN_EXPR, sizetype, max, sz);
+}
+
 static void
-eval_size_expr (tree var, tree size, tree *size_expr)
+eval_size_expr (tree var, tree wholesize, tree *wholesize_expr,
+		tree size, tree *size_expr)
 {
   if (size_expr != NULL)
     {
@@ -438,12 +553,16 @@ eval_size_expr (tree var, tree size, tree *size_expr)
 
       if (gimple_code (stmt) == GIMPLE_PHI)
 	{
-	  emit_size_phi_stmts (stmt, size, size_expr);
+	  emit_size_phi_stmts (stmt, wholesize, wholesize_expr,
+			       size, size_expr);
+	  delete[] wholesize_expr;
 	  delete[] size_expr;
 	}
       else
 	{
-	  emit_size_stmts (stmt, size, *size_expr);
+	  emit_size_stmts (stmt, wholesize, *wholesize_expr, size,
+			   size_bound_expr (*size_expr));
+	  delete wholesize_expr;
 	  delete size_expr;
 	}
     }
@@ -467,12 +586,20 @@ gimplify_size_exprs (object_size_info *osi, tree ptr)
 	{
 	  EXECUTE_IF_SET_IN_BITMAP (computed[object_size_type], 0, i, bi)
 	    {
+	      release_ssa_name (object_whole_sizes[object_size_type][i]);
 	      release_ssa_name (object_sizes[object_size_type][i]);
 	      if (gimple_code (SSA_NAME_DEF_STMT (ssa_name (i))) == GIMPLE_PHI
 		  && object_size_exprs[object_size_type][i])
-		delete [] object_size_exprs[object_size_type][i];
+		{
+		  delete [] object_whole_size_exprs[object_size_type][i];
+		  delete [] object_size_exprs[object_size_type][i];
+		}
 	      else
-		delete object_size_exprs[object_size_type][i];
+		{
+		  delete object_whole_size_exprs[object_size_type][i];
+		  delete object_size_exprs[object_size_type][i];
+		}
+	      object_whole_size_exprs[object_size_type][i] = NULL;
 	      object_size_exprs[object_size_type][i] = NULL;
 	    }
 	  bitmap_clear (computed[object_size_type]);
@@ -484,8 +611,12 @@ gimplify_size_exprs (object_size_info *osi, tree ptr)
   for (int object_size_type = 0; object_size_type <= 3; object_size_type++)
     EXECUTE_IF_SET_IN_BITMAP (computed[object_size_type], 0, i, bi)
       {
-	eval_size_expr (ssa_name (i), object_sizes[object_size_type][i],
+	eval_size_expr (ssa_name (i),
+			object_whole_sizes[object_size_type][i],
+			object_whole_size_exprs[object_size_type][i],
+			object_sizes[object_size_type][i],
 			object_size_exprs[object_size_type][i]);
+	object_whole_size_exprs[object_size_type][i] = NULL;
 	object_size_exprs[object_size_type][i] = NULL;
       }
 }
@@ -517,7 +648,32 @@ compute_builtin_dyn_object_size (tree ptr, int object_size_type, tree *psize,
       return false;
 
   if (computed[object_size_type] == NULL)
-    return false;
+    {
+      if (optimize || object_size_type & 1)
+	return false;
+
+      /* Like with __builtin_object_size, when not optimizing, rather than
+	 failing, make a small effort to determine the object size without the
+	 full benefit of the (costly) computation below.  */
+      gimple *def = SSA_NAME_DEF_STMT (ptr);
+      if (gimple_code (def) == GIMPLE_ASSIGN)
+	{
+	  tree_code code = gimple_assign_rhs_code (def);
+	  if (code == POINTER_PLUS_EXPR)
+	    {
+	      tree offset = gimple_assign_rhs2 (def);
+	      ptr = gimple_assign_rhs1 (def);
+
+	      if (compute_builtin_dyn_object_size (ptr, object_size_type,
+						   psize))
+		{
+		  *psize = size_for_offset (*psize, offset, *psize, true);
+		  return true;
+		}
+	    }
+	}
+      return false;
+    }
 
   if (bitmap_bit_p (computed[object_size_type], SSA_NAME_VERSION (ptr)))
     goto out;
@@ -530,6 +686,9 @@ compute_builtin_dyn_object_size (tree ptr, int object_size_type, tree *psize,
     {
       object_sizes[object_size_type].safe_grow (num_ssa_names, true);
       object_size_exprs[object_size_type].safe_grow (num_ssa_names, true);
+      object_whole_sizes[object_size_type].safe_grow (num_ssa_names, true);
+      object_whole_size_exprs[object_size_type].safe_grow (num_ssa_names,
+							   true);
     }
   if (dump_file)
     {
@@ -576,21 +735,37 @@ out:
    SSA_NAME.  */
 
 static void
-expr_object_size (struct object_size_info *osi, tree value, tree *sz)
+expr_object_size (struct object_size_info *osi, tree value, tree *sz,
+		  tree *wholeszp)
 {
   int object_size_type = osi->object_size_type;
-  tree bytes = NULL_TREE;
+  tree bytes = NULL_TREE, wholesz = NULL_TREE;
 
   if (TREE_CODE (value) == WITH_SIZE_EXPR)
     value = TREE_OPERAND (value, 0);
 
   if (TREE_CODE (value) == ADDR_EXPR)
-    addr_dyn_object_size (osi, value, object_size_type, &bytes);
+    addr_dyn_object_size (osi, value, object_size_type, &bytes, &wholesz);
 
   if (bytes)
     STRIP_NOPS (bytes);
 
+  if (wholesz)
+    STRIP_NOPS (wholesz);
+
   *sz = bytes;
+  *wholeszp = wholesz;
+}
+
+static tree
+make_object_size_name (tree ssa_name, tree *val, struct function *fun)
+{
+  if (ssa_name != NULL_TREE)
+    gcc_assert (*val == error_mark_node);
+  else
+    ssa_name = make_ssa_name_fn (fun, sizetype, 0);
+
+  return ssa_name;
 }
 
 static void
@@ -608,22 +783,29 @@ maybe_update_dependency_loop (struct object_size_info *osi, unsigned name,
 /* Add object size to the cache so that it can be reused.  */
 
 static void
-cache_object_size (struct object_size_info *osi, unsigned name, tree *sz)
+cache_object_size (struct object_size_info *osi, unsigned name, tree *sz,
+		   tree *wholesz)
 {
   int object_size_type = osi->object_size_type;
   struct function *fun = osi->fun;
 
-  gcc_assert (sz);
+  gcc_assert (sz && wholesz);
 
   maybe_update_dependency_loop (osi, name, *sz);
 
   /* Reuse SSA name if it was created for a dependency loop.  */
-  if (object_sizes[object_size_type][name] != NULL_TREE)
-    gcc_assert (*object_size_exprs[object_size_type][name] == error_mark_node);
-  else
-    object_sizes[object_size_type][name] = make_ssa_name_fn (fun, sizetype, 0);
+  object_sizes[object_size_type][name] =
+    make_object_size_name (object_sizes[object_size_type][name],
+			   object_size_exprs[object_size_type][name], fun);
   object_size_exprs[object_size_type][name] = sz;
 
+  /* Likewise for whole object size.  */
+  object_whole_sizes[object_size_type][name] =
+    make_object_size_name (object_whole_sizes[object_size_type][name],
+			   object_whole_size_exprs[object_size_type][name],
+			   fun);
+  object_whole_size_exprs[object_size_type][name] = wholesz;
+
   if (dump_file && (dump_flags & TDF_DETAILS))
     {
       fprintf (dump_file, "Caching size for ");
@@ -635,6 +817,14 @@ cache_object_size (struct object_size_info *osi, unsigned name, tree *sz)
       print_generic_expr (dump_file,
 			  *object_size_exprs[object_size_type][name],
 			  dump_flags);
+      fprintf (dump_file, ", Object whole size: ");
+      print_generic_expr (dump_file,
+			  object_whole_sizes[object_size_type][name],
+			  dump_flags);
+      fprintf (dump_file, " = ");
+      print_generic_expr (dump_file,
+			  *object_whole_size_exprs[object_size_type][name],
+			  dump_flags);
       fprintf (dump_file, "\n");
     }
 
@@ -644,21 +834,36 @@ cache_object_size (struct object_size_info *osi, unsigned name, tree *sz)
 /* Copy SZ and then call cache_object_size above.  */
 
 static void
-cache_object_size_copy (struct object_size_info *osi, unsigned name, tree sz)
+cache_object_size_copy (struct object_size_info *osi, unsigned name, tree sz,
+			tree wholesz)
 {
   int object_size_type = osi->object_size_type;
 
-  if (sz == NULL_TREE)
+  if (sz == NULL_TREE || wholesz == NULL_TREE)
     {
+      /* If we are unable to determine the size of a circular dependency,
+	 release its SSA.  */
       if (object_sizes[object_size_type][name] != NULL_TREE)
-	release_ssa_name (object_sizes[object_size_type][name]);
+	{
+	  release_ssa_name (object_sizes[object_size_type][name]);
+	  release_ssa_name (object_whole_sizes[object_size_type][name]);
+	}
       object_sizes[object_size_type][name] = NULL_TREE;
       object_size_exprs[object_size_type][name] = NULL;
+      object_whole_sizes[object_size_type][name] = NULL_TREE;
+      object_whole_size_exprs[object_size_type][name] = NULL;
       bitmap_set_bit (computed[object_size_type], name);
+      if (dump_file)
+	{
+	  fprintf (dump_file, "caching UNKNOWN size for ");
+	  print_generic_expr (dump_file, ssa_name (name), dump_flags);
+	  fprintf (dump_file, "\n");
+	}
+
       return;
     }
 
-  cache_object_size (osi, name, new tree (sz));
+  cache_object_size (osi, name, new tree (sz), new tree (wholesz));
 }
 
 /* Use size of ORIG for DEST and return it.  */
@@ -669,9 +874,59 @@ set_object_size_ssa (struct object_size_info *osi, tree dest, tree orig)
   collect_object_sizes_for (osi, orig);
 
   tree sz = object_sizes[osi->object_size_type][SSA_NAME_VERSION (orig)];
-  cache_object_size_copy (osi, SSA_NAME_VERSION (dest), sz);
+  tree wholesz =
+    object_whole_sizes[osi->object_size_type][SSA_NAME_VERSION (orig)];
+
+  cache_object_size_copy (osi, SSA_NAME_VERSION (dest), sz, wholesz);
 }
 
+/* Compute object_sizes for VAR, defined to the result of an assignment
+   with operator POINTER_PLUS_EXPR.  Return true if the object size might
+   need reexamination  later.  */
+
+static void
+plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
+{
+  int object_size_type = osi->object_size_type;
+  unsigned int varno = SSA_NAME_VERSION (var);
+  tree op0, op1, bytes = NULL_TREE, wholevar = NULL_TREE;
+
+  if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
+    {
+      op0 = gimple_assign_rhs1 (stmt);
+      op1 = gimple_assign_rhs2 (stmt);
+    }
+  else if (gimple_assign_rhs_code (stmt) == ADDR_EXPR)
+    {
+      tree rhs = TREE_OPERAND (gimple_assign_rhs1 (stmt), 0);
+      gcc_assert (TREE_CODE (rhs) == MEM_REF);
+      op0 = TREE_OPERAND (rhs, 0);
+      op1 = TREE_OPERAND (rhs, 1);
+    }
+  else
+    gcc_unreachable ();
+
+  /* Handle PTR + OFFSET here.  */
+  if (TREE_CODE (op0) == SSA_NAME)
+    {
+      unsigned op0no = SSA_NAME_VERSION (op0);
+
+      collect_object_sizes_for (osi, op0);
+      if (object_sizes[object_size_type][op0no])
+	{
+	  bytes = object_sizes[object_size_type][op0no];
+	  wholevar = object_whole_sizes[object_size_type][op0no];
+	  bytes = size_for_offset (bytes, op1, wholevar, true);
+	}
+    }
+  else if (TREE_CODE (op0) == ADDR_EXPR)
+    {
+      /* op0 will be ADDR_EXPR here.  */
+      if (addr_dyn_object_size (osi, op0, object_size_type, &bytes, &wholevar))
+	bytes = size_for_offset (bytes, op1, wholevar);
+    }
+  cache_object_size_copy (osi, varno, bytes, wholevar);
+}
 
 /* Compute object_sizes for PTR, defined to the result of a call.  */
 
@@ -680,11 +935,10 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call)
 {
   gcc_assert (is_gimple_call (call));
 
-  cache_object_size_copy (osi, SSA_NAME_VERSION (ptr),
-			  alloc_object_size (call));
+  tree sz = alloc_object_size (call);
+  cache_object_size_copy (osi, SSA_NAME_VERSION (ptr), sz, sz);
 }
 
-
 /* Compute object sizes for VAR.
 
    - For allocation GIMPLE_CALL like malloc or calloc object size is the size
@@ -692,7 +946,9 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call)
    - For a memcpy like GIMPLE_CALL that always returns one of its arguments,
      the object size is object size of that argument.
    - For GIMPLE_PHI, compute a PHI node with sizes of all branches in the PHI
-     node of the object.  */
+     node of the object.
+   - For GIMPLE_ASSIGN, return the size of the object the SSA name points
+     to.  */
 
 static void
 collect_object_sizes_for (struct object_size_info *osi, tree var)
@@ -705,13 +961,16 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
     return;
 
   if (bitmap_set_bit (osi->visited, varno))
-    object_sizes[object_size_type][varno] = NULL_TREE;
+    {
+      object_sizes[object_size_type][varno] = NULL_TREE;
+      object_whole_sizes[object_size_type][varno] = NULL_TREE;
+    }
   else
     {
       /* Add an SSA name but mark the expression as being an error_mark_node.
 	 When we go back up the stack, the error_mark_node should get
 	 overwritten by a proper expression.  */
-      cache_object_size (osi, varno, &error_mark_node);
+      cache_object_size (osi, varno, &error_mark_node, &error_mark_node);
       if (dump_file && (dump_flags & TDF_DETAILS))
 	{
 	  fprintf (dump_file, "Found a dependency loop at ");
@@ -732,6 +991,29 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 
   switch (gimple_code (stmt))
     {
+    case GIMPLE_ASSIGN:
+      {
+	tree rhs = gimple_assign_rhs1 (stmt);
+	if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR
+	    || (gimple_assign_rhs_code (stmt) == ADDR_EXPR
+		&& TREE_CODE (TREE_OPERAND (rhs, 0)) == MEM_REF))
+	  plus_stmt_object_size (osi, var, stmt);
+	else if (gimple_assign_single_p (stmt)
+		 || gimple_assign_unary_nop_p (stmt))
+	  {
+	    if (TREE_CODE (rhs) == SSA_NAME
+		&& POINTER_TYPE_P (TREE_TYPE (rhs)))
+	      set_object_size_ssa (osi, var, rhs);
+	    else
+	      {
+		tree sz, wholesz;
+		expr_object_size (osi, rhs, &sz, &wholesz);
+		cache_object_size_copy (osi, varno, sz, wholesz);
+	      }
+	  }
+	break;
+      }
+
     case GIMPLE_CALL:
       {
 	gcall *call_stmt = as_a <gcall *> (stmt);
@@ -742,9 +1024,9 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 	      set_object_size_ssa (osi, var, arg);
 	    else
 	      {
-		tree ret;
-		expr_object_size (osi, arg, &ret);
-		cache_object_size_copy (osi, varno, ret);
+		tree sz, wholesz;
+		expr_object_size (osi, arg, &sz, &wholesz);
+		cache_object_size_copy (osi, varno, sz, wholesz);
 	      }
 	  }
 	else
@@ -756,44 +1038,48 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
       {
 	unsigned i, num_args = gimple_phi_num_args (stmt);
 	tree *sizes = new tree[num_args] ();
+	tree *wholesizes = new tree[num_args] ();
 
 	/* Bail out if the size of any of the PHI arguments cannot be
 	   determined.  */
 	for (i = 0; i < gimple_phi_num_args (stmt); i++)
 	  {
 	    tree rhs = gimple_phi_arg_def (stmt, i);
-	    tree sz;
+	    tree sz, wholesz;
 
 	    if (TREE_CODE (rhs) != SSA_NAME)
-	      expr_object_size (osi, rhs, &sz);
+	      expr_object_size (osi, rhs, &sz, &wholesz);
 	    else
 	      {
 		collect_object_sizes_for (osi, rhs);
 		sz = object_sizes[object_size_type][SSA_NAME_VERSION (rhs)];
+		wholesz =
+		  object_whole_sizes[object_size_type][SSA_NAME_VERSION (rhs)];
 	      }
 
-	    if (sz == NULL_TREE)
+	    if (sz == NULL_TREE || wholesz == NULL_TREE)
 	      break;
 
 	    sizes[i] = sz;
+	    wholesizes[i] = wholesz;
 	  }
 
 	/* Record all possible sizes to build our PHI node later.  */
 	if (i == gimple_phi_num_args (stmt))
+	  cache_object_size (osi, varno, sizes, wholesizes);
+	else
 	  {
-	    cache_object_size (osi, varno, sizes);
-	    break;
+	    delete[] sizes;
+	    delete[] wholesizes;
+	    cache_object_size_copy (osi, varno, NULL_TREE, NULL_TREE);
 	  }
-	else
-	  delete[] sizes;
+	break;
       }
-    /* FALLTHROUGH */
 
     /* Bail out for all other cases.  */
     case GIMPLE_NOP:
-    case GIMPLE_ASSIGN:
     case GIMPLE_ASM:
-      cache_object_size_copy (osi, varno, NULL_TREE);
+      cache_object_size_copy (osi, varno, NULL_TREE, NULL_TREE);
       break;
 
     default:
@@ -817,6 +1103,9 @@ init_dynamic_object_sizes (void)
     {
       object_sizes[object_size_type].safe_grow (num_ssa_names, true);
       object_size_exprs[object_size_type].safe_grow (num_ssa_names, true);
+      object_whole_sizes[object_size_type].safe_grow (num_ssa_names, true);
+      object_whole_size_exprs[object_size_type].safe_grow (num_ssa_names,
+							   true);
       computed[object_size_type] = BITMAP_ALLOC (NULL);
     }
 }
@@ -833,6 +1122,8 @@ fini_dynamic_object_sizes (void)
     {
       object_sizes[object_size_type].release ();
       object_size_exprs[object_size_type].release ();
+      object_whole_sizes[object_size_type].release ();
+      object_whole_size_exprs[object_size_type].release ();
       BITMAP_FREE (computed[object_size_type]);
     }
 }
-- 
2.31.1


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

* [PATCH 6/8] tree-dynamic-object-size: Handle function parameters
  2021-10-07 22:14 [PATCH 0/8] __builtin_dynamic_object_size and more Siddhesh Poyarekar
                   ` (4 preceding siblings ...)
  2021-10-07 22:14 ` [PATCH 5/8] tree-dynamic-object-size: Handle GIMPLE_ASSIGN Siddhesh Poyarekar
@ 2021-10-07 22:14 ` Siddhesh Poyarekar
  2021-10-20 16:56   ` Martin Sebor
  2021-10-07 22:14 ` [PATCH 7/8] tree-dynamic-object-size: Get subobject sizes Siddhesh Poyarekar
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 18+ messages in thread
From: Siddhesh Poyarekar @ 2021-10-07 22:14 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Handle either static sizes in function parameters or hints provided by
__attribute__ ((access (...))) to compute sizes for objects.

gcc/ChangeLog:

	* tree-dynamic-object-size.c: Include tree-dfa.h.
	(emit_size_stmts): New argument osi.  Handle GIMPLE_NOP.
	(eval_size_expr, gimplify_size_exprs): Adjust.
	(parm_object_size): New function.
	(collect_object_sizes_for): Handle GIMPLE_NOP.

gcc/testsuite/ChangeLog:

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

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

diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
index 3c2c4c84264..c72fa0508db 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -255,6 +255,15 @@ test_substring_ptrplus (size_t sz, size_t off)
   return __builtin_dynamic_object_size (str + off, 0);
 }
 
+size_t
+__attribute__ ((noinline))
+__attribute__ ((access (__read_write__, 1, 2)))
+test_parmsz (void *obj, size_t sz, size_t off)
+{
+  return __builtin_dynamic_object_size (obj + off, 0);
+}
+
+
 int
 main (int argc, char **argv)
 {
@@ -338,6 +347,19 @@ main (int argc, char **argv)
   if (test_deploop (128, 129) != 32)
     FAIL ();
 
+  if (test_parmsz (argv[0], __builtin_strlen (argv[0]) + 1, -1)!= 0)
+    FAIL ();
+
+  if (test_parmsz (argv[0], __builtin_strlen (argv[0]) + 1, 0)
+      != __builtin_strlen (argv[0]) + 1)
+    FAIL ();
+  if (test_parmsz (argv[0], __builtin_strlen (argv[0]) + 1,
+		   __builtin_strlen (argv[0]))!= 1)
+    FAIL ();
+  if (test_parmsz (argv[0], __builtin_strlen (argv[0]) + 1,
+		   __builtin_strlen (argv[0]) + 2)!= 0)
+    FAIL ();
+
   if (nfails > 0)
     __builtin_abort ();
 
diff --git a/gcc/tree-dynamic-object-size.c b/gcc/tree-dynamic-object-size.c
index f143a64777c..8d7283623dc 100644
--- a/gcc/tree-dynamic-object-size.c
+++ b/gcc/tree-dynamic-object-size.c
@@ -58,6 +58,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-fold.h"
 #include "gimple-iterator.h"
 #include "tree-cfg.h"
+#include "tree-dfa.h"
 #include "stringpool.h"
 #include "attribs.h"
 #include "builtins.h"
@@ -456,7 +457,7 @@ pass_through_call (const gcall *call)
 
 
 static void
-emit_size_stmts (gimple *stmt, tree wholesize_ssa,
+emit_size_stmts (object_size_info *osi, gimple *stmt, tree wholesize_ssa,
 		 tree wholesize_expr, tree size_ssa, tree size_expr)
 {
   gimple_seq seq = NULL;
@@ -481,7 +482,14 @@ emit_size_stmts (gimple *stmt, tree wholesize_ssa,
      statements involved in evaluation of the object size expression precede
      the definition statement.  For parameters, we don't have a definition
      statement, so insert into the first code basic block.  */
-  gimple_stmt_iterator i = gsi_for_stmt (stmt);
+  gimple_stmt_iterator i;
+  if (gimple_code (stmt) == GIMPLE_NOP)
+    {
+      basic_block first_bb = single_succ (ENTRY_BLOCK_PTR_FOR_FN (osi->fun));
+      i = gsi_start_bb (first_bb);
+    }
+  else
+    i = gsi_for_stmt (stmt);
   gsi_insert_seq_before (&i, seq, GSI_CONTINUE_LINKING);
 }
 
@@ -542,8 +550,8 @@ size_bound_expr (tree sz)
 }
 
 static void
-eval_size_expr (tree var, tree wholesize, tree *wholesize_expr,
-		tree size, tree *size_expr)
+eval_size_expr (struct object_size_info *osi, tree var, tree wholesize,
+		tree *wholesize_expr, tree size, tree *size_expr)
 {
   if (size_expr != NULL)
     {
@@ -560,7 +568,7 @@ eval_size_expr (tree var, tree wholesize, tree *wholesize_expr,
 	}
       else
 	{
-	  emit_size_stmts (stmt, wholesize, *wholesize_expr, size,
+	  emit_size_stmts (osi, stmt, wholesize, *wholesize_expr, size,
 			   size_bound_expr (*size_expr));
 	  delete wholesize_expr;
 	  delete size_expr;
@@ -611,7 +619,7 @@ gimplify_size_exprs (object_size_info *osi, tree ptr)
   for (int object_size_type = 0; object_size_type <= 3; object_size_type++)
     EXECUTE_IF_SET_IN_BITMAP (computed[object_size_type], 0, i, bi)
       {
-	eval_size_expr (ssa_name (i),
+	eval_size_expr (osi, ssa_name (i),
 			object_whole_sizes[object_size_type][i],
 			object_whole_size_exprs[object_size_type][i],
 			object_sizes[object_size_type][i],
@@ -939,6 +947,71 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call)
   cache_object_size_copy (osi, SSA_NAME_VERSION (ptr), sz, sz);
 }
 
+/* Find size of an object passed as a parameter to the function.  */
+
+static void
+parm_object_size (struct object_size_info *osi, tree var)
+{
+  tree parm = SSA_NAME_VAR (var);
+  unsigned varno = SSA_NAME_VERSION (var);
+  tree sz = NULL_TREE, wholesz = NULL_TREE;
+
+  if (dump_file)
+  {
+	  fprintf (dump_file, "Object is a parameter: ");
+	  print_generic_expr (dump_file, parm, dump_flags);
+	  fprintf (dump_file, " which is %s a pointer type\n",
+			  POINTER_TYPE_P (TREE_TYPE (parm)) ? "" : "not");
+  }
+
+  if (POINTER_TYPE_P (TREE_TYPE (parm)))
+    {
+      /* Look for access attribute.  */
+      rdwr_map rdwr_idx;
+
+      tree fndecl = osi->fun->decl;
+      const attr_access *access = get_parm_access (rdwr_idx, parm, fndecl);
+      tree typesize = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (parm)));
+
+      if (access && access->sizarg != UINT_MAX)
+	{
+	  tree fnargs = DECL_ARGUMENTS (fndecl);
+	  tree arg = NULL_TREE;
+	  unsigned argpos = 0;
+
+	  /* Walk through the parameters to pick the size parameter and safely
+	     scale it by the type size.  */
+	  for (arg = fnargs; argpos != access->sizarg && arg;
+	       arg = TREE_CHAIN (arg), ++argpos);
+
+	  if (arg != NULL_TREE && INTEGRAL_TYPE_P (TREE_TYPE (arg)))
+	    {
+	      tree arrsz = get_or_create_ssa_default_def (osi->fun, arg);
+	      if (arrsz != NULL_TREE)
+		{
+		  arrsz = fold_convert (sizetype, arrsz);
+		  if (typesize)
+		    {
+		      tree res = fold_build2 (MULT_EXPR, sizetype, arrsz,
+					      typesize);
+		      tree check = fold_build2 (EXACT_DIV_EXPR, sizetype,
+						TYPE_MAX_VALUE (sizetype),
+						typesize);
+		      check = fold_build2 (LT_EXPR, sizetype, arrsz, check);
+		      arrsz = fold_build3 (COND_EXPR, sizetype, check, res,
+					   size_zero_node);
+		    }
+		}
+	      sz = wholesz = arrsz;
+	    }
+	}
+    }
+  else
+      expr_object_size (osi, parm, &sz, &wholesz);
+
+  cache_object_size_copy (osi, varno, sz, wholesz);
+}
+
 /* Compute object sizes for VAR.
 
    - For allocation GIMPLE_CALL like malloc or calloc object size is the size
@@ -948,7 +1021,9 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call)
    - For GIMPLE_PHI, compute a PHI node with sizes of all branches in the PHI
      node of the object.
    - For GIMPLE_ASSIGN, return the size of the object the SSA name points
-     to.  */
+     to.
+   - For GIMPLE_NOP, if the variable is a function parameter, compute the size
+     of the parameter.  */
 
 static void
 collect_object_sizes_for (struct object_size_info *osi, tree var)
@@ -1076,8 +1151,15 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
 	break;
       }
 
-    /* Bail out for all other cases.  */
     case GIMPLE_NOP:
+      if (SSA_NAME_VAR (var) && TREE_CODE (SSA_NAME_VAR (var)) == PARM_DECL)
+	parm_object_size (osi, var);
+      else
+	/* Uninitialized SSA names point nowhere.  */
+	cache_object_size_copy (osi, varno, NULL_TREE, NULL_TREE);
+      break;
+
+    /* Bail out for all other cases.  */
     case GIMPLE_ASM:
       cache_object_size_copy (osi, varno, NULL_TREE, NULL_TREE);
       break;
-- 
2.31.1


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

* [PATCH 7/8] tree-dynamic-object-size: Get subobject sizes
  2021-10-07 22:14 [PATCH 0/8] __builtin_dynamic_object_size and more Siddhesh Poyarekar
                   ` (5 preceding siblings ...)
  2021-10-07 22:14 ` [PATCH 6/8] tree-dynamic-object-size: Handle function parameters Siddhesh Poyarekar
@ 2021-10-07 22:14 ` Siddhesh Poyarekar
  2021-10-07 22:14 ` [PATCH 8/8] tree-dynamic-object-size: Add test wrappers to extend testing Siddhesh Poyarekar
  2021-10-08  4:50 ` [PATCH 0/8] __builtin_dynamic_object_size and more Siddhesh Poyarekar
  8 siblings, 0 replies; 18+ messages in thread
From: Siddhesh Poyarekar @ 2021-10-07 22:14 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Adapt subobject computation logic from tree-object-size to make it
work with variable sizes.

gcc/ChangeLog:

	* tree-dynamic-object-size.c (build_cond_branch): New function.
	(compute_object_offset): Use it.
	(get_closest_subobject): New function.
	(addr_dyn_object_size): Call it.  Support subobject size
	computation.

gcc/testsuite/ChangeLog:

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

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

diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
index c72fa0508db..94f8e071e2c 100644
--- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
@@ -237,6 +237,33 @@ test_dynarray_struct (size_t sz, size_t off)
   return __builtin_dynamic_object_size (&bin[off].c, 0);
 }
 
+size_t
+__attribute__ ((noinline))
+test_dynarray_struct_subobj (size_t sz, size_t off)
+{
+  struct dynarray_struct bin[sz];
+
+  return __builtin_dynamic_object_size (&bin[off].c[4], 1);
+}
+
+size_t
+__attribute__ ((noinline))
+test_dynarray_struct_subobj2 (size_t sz, size_t off, size_t *objsz)
+{
+  struct dynarray_struct2
+    {
+      long a;
+      int b;
+      char c[sz];
+    };
+
+  struct dynarray_struct2 bin;
+
+  *objsz = sizeof (bin);
+
+  return __builtin_dynamic_object_size (&bin.c[off], 1);
+}
+
 size_t
 __attribute__ ((noinline))
 test_substring (size_t sz, size_t off)
@@ -334,6 +361,13 @@ main (int argc, char **argv)
     FAIL ();
   if (test_substring (128, 142) != 0)
     FAIL ();
+  if (test_dynarray_struct_subobj (42, 4) != 16 - 4)
+    FAIL ();
+  if (test_dynarray_struct_subobj (42, 48) != 0)
+    FAIL ();
+  size_t objsz = 0;
+  if (test_dynarray_struct_subobj2 (42, 4, &objsz) != objsz - 4 - 12)
+    FAIL ();
   if (test_substring_ptrplus (128, 4) != (128 - 4) * sizeof (int))
     FAIL ();
   if (test_substring_ptrplus (128, 142) != 0)
diff --git a/gcc/tree-dynamic-object-size.c b/gcc/tree-dynamic-object-size.c
index 8d7283623dc..ebc2fad7a87 100644
--- a/gcc/tree-dynamic-object-size.c
+++ b/gcc/tree-dynamic-object-size.c
@@ -102,12 +102,21 @@ static bitmap computed[4];
 bool compute_builtin_dyn_object_size (tree, int, tree *,
 				      struct function *fun = NULL);
 
+static tree
+build_cond_branch (tree t, tree low_bound, tree unit_size)
+{
+  tree br = fold_build2 (MINUS_EXPR, TREE_TYPE (t), t, low_bound);
+  br = fold_convert (sizetype, br);
+  br = fold_build2 (MULT_EXPR, sizetype, unit_size, br);
+
+  return br;
+}
+
 /* Compute offset of EXPR within VAR.  Return error_mark_node if unknown.  */
 
 static tree
 compute_object_offset (const_tree expr, const_tree var)
 {
-  enum tree_code code = PLUS_EXPR;
   tree base, off, t;
 
   if (expr == var)
@@ -150,12 +159,16 @@ compute_object_offset (const_tree expr, const_tree var)
       low_bound = array_ref_low_bound (CONST_CAST_TREE (expr));
       unit_size = array_ref_element_size (CONST_CAST_TREE (expr));
       if (! integer_zerop (low_bound))
-	t = fold_build2 (MINUS_EXPR, TREE_TYPE (t), t, low_bound);
-      if (TREE_CODE (t) == INTEGER_CST && tree_int_cst_sgn (t) < 0)
 	{
-	  code = MINUS_EXPR;
-	  t = fold_build1 (NEGATE_EXPR, TREE_TYPE (t), t);
+	  tree cond = fold_build2 (GE_EXPR, TREE_TYPE (t), t, low_bound);
+	  tree then_br = build_cond_branch (t, low_bound, unit_size);
+	  tree else_br = build_cond_branch (low_bound, t, unit_size);
+
+	  then_br = fold_build2 (PLUS_EXPR, sizetype, base, then_br);
+	  else_br = fold_build2 (MINUS_EXPR, sizetype, base, else_br);
+	  return fold_build3 (COND_EXPR, sizetype, cond, then_br, else_br);
 	}
+
       t = fold_convert (sizetype, t);
       off = fold_build2 (MULT_EXPR, sizetype, unit_size, t);
       break;
@@ -168,7 +181,7 @@ compute_object_offset (const_tree expr, const_tree var)
       return error_mark_node;
     }
 
-  return fold_build2 (code, sizetype, base, off);
+  return fold_build2 (PLUS_EXPR, sizetype, base, off);
 }
 
 /* Given an object size SZ and offset OFF into it, compute the usable object
@@ -328,6 +341,105 @@ whole_var_size (struct object_size_info *osi, tree pt_var,
   return true;
 }
 
+/* Get the most immediate subobject encapsulating the pointer PTR, given the
+   whole_var object WHOLE_VAR.  */
+
+static tree
+get_closest_subobject (const_tree ptr, tree whole_var)
+{
+  tree var = TREE_OPERAND (ptr, 0);
+
+  while (var != whole_var
+	 && TREE_CODE (var) != BIT_FIELD_REF
+	 && TREE_CODE (var) != COMPONENT_REF
+	 && TREE_CODE (var) != ARRAY_REF
+	 && TREE_CODE (var) != ARRAY_RANGE_REF
+	 && TREE_CODE (var) != REALPART_EXPR
+	 && TREE_CODE (var) != IMAGPART_EXPR)
+    var = TREE_OPERAND (var, 0);
+
+  if (var != whole_var && TREE_CODE (var) == ARRAY_REF)
+    var = TREE_OPERAND (var, 0);
+
+  if (! TYPE_SIZE_UNIT (TREE_TYPE (var)))
+    var = whole_var;
+  else if (var != whole_var && TREE_CODE (whole_var) == MEM_REF)
+    {
+      tree v = var;
+      while (v && v != whole_var)
+	switch (TREE_CODE (v))
+	  {
+	  case ARRAY_REF:
+	    if (TYPE_SIZE_UNIT (TREE_TYPE (TREE_OPERAND (v, 0))))
+	      {
+		tree domain
+		  = TYPE_DOMAIN (TREE_TYPE (TREE_OPERAND (v, 0)));
+		if (domain && TYPE_MAX_VALUE (domain))
+		  {
+		    v = NULL_TREE;
+		    break;
+		  }
+	      }
+	    v = TREE_OPERAND (v, 0);
+	    break;
+	  case REALPART_EXPR:
+	  case IMAGPART_EXPR:
+	    v = NULL_TREE;
+	    break;
+	  case COMPONENT_REF:
+	    if (TREE_CODE (TREE_TYPE (v)) != ARRAY_TYPE)
+	      {
+		v = NULL_TREE;
+		break;
+	      }
+	    while (v != whole_var && TREE_CODE (v) == COMPONENT_REF)
+	      if (TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0)))
+		  != UNION_TYPE
+		  && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0)))
+		  != QUAL_UNION_TYPE)
+		break;
+	      else
+		v = TREE_OPERAND (v, 0);
+	    if (TREE_CODE (v) == COMPONENT_REF
+		&& TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0)))
+		== RECORD_TYPE)
+	      {
+		tree fld_chain = DECL_CHAIN (TREE_OPERAND (v, 1));
+		for (; fld_chain; fld_chain = DECL_CHAIN (fld_chain))
+		  if (TREE_CODE (fld_chain) == FIELD_DECL)
+		    break;
+
+		if (fld_chain)
+		  {
+		    v = NULL_TREE;
+		    break;
+		  }
+		v = TREE_OPERAND (v, 0);
+	      }
+	    while (v != whole_var && TREE_CODE (v) == COMPONENT_REF)
+	      if (TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0)))
+		  != UNION_TYPE
+		  && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0)))
+		  != QUAL_UNION_TYPE)
+		break;
+	      else
+		v = TREE_OPERAND (v, 0);
+	    if (v != whole_var)
+	      v = NULL_TREE;
+	    else
+	      v = whole_var;
+	    break;
+	  default:
+	    v = whole_var;
+	    break;
+	  }
+      if (v == whole_var)
+	var = whole_var;
+    }
+
+  return var;
+}
+
 /* Compute an object size estimate for PTR, which is a ADDR_EXPR.
    OBJECT_SIZE_TYPE is the second argument from __builtin_dynamic_object_size.
    If unknown, return false, setting PSIZE to NULL_TREE.  */
@@ -337,7 +449,8 @@ addr_dyn_object_size (struct object_size_info *osi, const_tree ptr,
 		      int object_size_type, tree *psize,
 		      tree *wholesizep = NULL)
 {
-  tree pt_var, pt_var_size = NULL_TREE, bytes;
+  struct function *fun = osi ? osi->fun : cfun;
+  tree pt_var, pt_var_size = NULL_TREE, var_size, bytes;
 
   gcc_assert (TREE_CODE (ptr) == ADDR_EXPR);
 
@@ -353,22 +466,57 @@ addr_dyn_object_size (struct object_size_info *osi, const_tree ptr,
   if (!whole_var_size (osi, pt_var, object_size_type, &pt_var_size))
     return false;
 
-  if (!pt_var_size)
-    return false;
+  var_size = pt_var_size;
 
   /* PTR points to a subobject of whole variable PT_VAR.  */
   if (pt_var != TREE_OPERAND (ptr, 0))
     {
-      bytes = compute_object_offset (TREE_OPERAND (ptr, 0), pt_var);
+      tree var;
+
+      if (object_size_type & 1)
+	var = get_closest_subobject (ptr, pt_var);
+      else
+	var = pt_var;
+
+      if (var != pt_var)
+	{
+	  var_size = TYPE_SIZE_UNIT (TREE_TYPE (var));
+	  if (!TREE_CONSTANT (var_size))
+	    var_size = get_or_create_ssa_default_def (fun, var_size);
+	  if (!var_size)
+	    return false;
+	}
+      else if (!pt_var_size)
+	return false;
+
+      bytes = compute_object_offset (TREE_OPERAND (ptr, 0), var);
       if (bytes != error_mark_node)
-	bytes = size_for_offset (pt_var_size, bytes, pt_var_size);
+	{
+	  tree cap_size = pt_var_size;
+
+	  /* For subobject sizes where the whole object is a structure,
+	     restrict size to the size of the struct less the offset of the
+	     subobject in the struct.  */
+	  if (var != pt_var && pt_var_size && TREE_CODE (pt_var) == MEM_REF)
+	    {
+	      tree off = compute_object_offset (TREE_OPERAND (ptr, 0), pt_var);
+	      if (off == error_mark_node)
+		off = size_zero_node;
+	      else
+		off = fold_build2 (MIN_EXPR, sizetype, pt_var_size, off);
+	      cap_size = fold_build2 (MINUS_EXPR, sizetype, pt_var_size, off);
+	    }
+	  bytes = size_for_offset (var_size, bytes, cap_size);
+	}
     }
+  else if (!pt_var_size)
+    return false;
   else
     bytes = pt_var_size;
 
   *psize = bytes == error_mark_node ? NULL_TREE : bytes;
   if (wholesizep)
-    *wholesizep = pt_var_size;
+    *wholesizep = var_size;
   return true;
 }
 
-- 
2.31.1


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

* [PATCH 8/8] tree-dynamic-object-size: Add test wrappers to extend testing
  2021-10-07 22:14 [PATCH 0/8] __builtin_dynamic_object_size and more Siddhesh Poyarekar
                   ` (6 preceding siblings ...)
  2021-10-07 22:14 ` [PATCH 7/8] tree-dynamic-object-size: Get subobject sizes Siddhesh Poyarekar
@ 2021-10-07 22:14 ` Siddhesh Poyarekar
  2021-10-08  4:50 ` [PATCH 0/8] __builtin_dynamic_object_size and more Siddhesh Poyarekar
  8 siblings, 0 replies; 18+ messages in thread
From: Siddhesh Poyarekar @ 2021-10-07 22:14 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

Wrap most tests for __builtin_object_size with
__builtin_dynamic_object_size to get better coverage.  At the moment
only one test does not work correctly with
__builtin_dynamic_object_size, viz. builtin-object-size-5.c.  The test
expects abort() calls to be optimized away, which does not happen with
__builtin_dynamic_object_size since it cannot collapse loop iteration
to a constant.

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-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-1.c: Adjust to rebuild with
	__builtin_dynamic_object_size.
	* gcc.dg/builtin-object-size-16.c: Likewise.
	* gcc.dg/builtin-object-size-17.c: Likewise.
	* gcc.dg/builtin-object-size-2.c: Likewise.
	* gcc.dg/builtin-object-size-3.c: Likewise.
	* gcc.dg/builtin-object-size-4.c: Likewise.

Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
---
 .../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    |   7 +
 .../gcc.dg/builtin-dynamic-object-size-10.c   |   9 ++
 .../gcc.dg/builtin-dynamic-object-size-11.c   |   7 +
 .../gcc.dg/builtin-dynamic-object-size-12.c   |   5 +
 .../gcc.dg/builtin-dynamic-object-size-13.c   |   5 +
 .../gcc.dg/builtin-dynamic-object-size-14.c   |   5 +
 .../gcc.dg/builtin-dynamic-object-size-15.c   |   6 +
 .../gcc.dg/builtin-dynamic-object-size-16.c   |   7 +
 .../gcc.dg/builtin-dynamic-object-size-17.c   |   8 +
 .../gcc.dg/builtin-dynamic-object-size-18.c   |   8 +
 .../gcc.dg/builtin-dynamic-object-size-19.c   | 104 ++++++++++++
 .../gcc.dg/builtin-dynamic-object-size-2.c    |   7 +
 .../gcc.dg/builtin-dynamic-object-size-3.c    |   7 +
 .../gcc.dg/builtin-dynamic-object-size-4.c    |   7 +
 .../gcc.dg/builtin-dynamic-object-size-6.c    |   5 +
 .../gcc.dg/builtin-dynamic-object-size-7.c    |   5 +
 .../gcc.dg/builtin-dynamic-object-size-8.c    |   5 +
 .../gcc.dg/builtin-dynamic-object-size-9.c    |   5 +
 gcc/testsuite/gcc.dg/builtin-object-size-1.c  | 110 +++++++++++++
 gcc/testsuite/gcc.dg/builtin-object-size-16.c |  10 ++
 gcc/testsuite/gcc.dg/builtin-object-size-17.c |   2 +
 gcc/testsuite/gcc.dg/builtin-object-size-2.c  | 126 +++++++++++++++
 gcc/testsuite/gcc.dg/builtin-object-size-3.c  | 148 ++++++++++++++++++
 gcc/testsuite/gcc.dg/builtin-object-size-4.c  | 117 ++++++++++++++
 27 files changed, 742 insertions(+)
 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-6.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-7.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-8.c
 create mode 100644 gcc/testsuite/gcc.dg/builtin-dynamic-object-size-9.c

diff --git a/gcc/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..2c7d2128913
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-1.c
@@ -0,0 +1,7 @@
+/* { dg-do run } */
+/* { 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
new file mode 100644
index 00000000000..6db9ea8c667
--- /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_dynobjsz-details -fdump-tree-dynobjsz1-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 "spkt.*: maximum dynamic object size _" "early_dynobjsz" } } */
+/* { dg-final { scan-tree-dump ": maximum dynamic subobject size _" "early_dynobjsz" } } */
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..ad4d562f777
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-15.c
@@ -0,0 +1,6 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+#define DYNAMIC_OBJECT_SIZE
+#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..d88317d111d
--- /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" } */
+
+char ax2[];               /* { dg-warning "assumed to have one element" } */
+#define __builtin_object_size __builtin_dynamic_object_size
+#define 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..2f07534c11b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-2.c
@@ -0,0 +1,7 @@
+/* { dg-do run } */
+/* { 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
new file mode 100644
index 00000000000..29b5b358845
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-3.c
@@ -0,0 +1,7 @@
+/* { dg-do run } */
+/* { 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
new file mode 100644
index 00000000000..5ff1f16c978
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-4.c
@@ -0,0 +1,7 @@
+/* { dg-do run } */
+/* { 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-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-1.c b/gcc/testsuite/gcc.dg/builtin-object-size-1.c
index 8cdae49a6b1..33987dd279f 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
@@ -119,12 +135,21 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&extb[5], 0) != sizeof (extb) - 5)
     abort ();
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (var, 0) != x + 10)
+    abort ();
+  if (__builtin_object_size (var + 10, 0) != x)
+    abort ();
+  if (__builtin_object_size (&var[5], 0) != x + 5)
+    abort ();
+#else
   if (__builtin_object_size (var, 0) != (size_t) -1)
     abort ();
   if (__builtin_object_size (var + 10, 0) != (size_t) -1)
     abort ();
   if (__builtin_object_size (&var[5], 0) != (size_t) -1)
     abort ();
+#endif
   if (__builtin_object_size (zerol, 0) != 0)
     abort ();
   if (__builtin_object_size (&zerol, 0) != 0)
@@ -165,6 +190,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;
@@ -181,8 +209,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, 0) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != 20)
     abort ();
+#endif
   r = &buf3[20];
   for (i = 0; i < 4; ++i)
     {
@@ -195,13 +241,44 @@ 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) - __builtin_offsetof (struct B, buf1) - 7;
+      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, 0) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 0) != 15)
     abort ();
+#endif
   r += 8;
+#ifdef DYNAMIC_OBJECT_SIZE
+  dyn_res -= 8;
+  if (__builtin_object_size (r, 0) != dyn_res)
+    abort ();
+  if (dyn_res >= 6)
+    {
+      if (__builtin_object_size (r + 6, 0) != dyn_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 +291,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, 0) != dyn_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 +458,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 +471,7 @@ test5 (size_t x)
      it would be 64 (or conservative (size_t) -1 == unknown).  */
   if (__builtin_object_size (p, 0) != sizeof (buf) - 8)
     abort ();
+#endif
   memset (p, ' ', sizeof (buf) - 8 - 4 * 4);
 }
 
@@ -380,8 +485,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);
 }
 
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-16.c b/gcc/testsuite/gcc.dg/builtin-object-size-16.c
index 48229390bfd..67680020337 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];
@@ -114,11 +116,19 @@ test_arrays ()
   T (     4,       2,       4,       2,   &a2x2[0][0]);
   T (     0,  F1  (0),      0,       0,   &a2x2 + 1);
   T (     2,  F1 ( 2),      2,  F3 ( 2),  &a2x2[0] + 1);
+#ifdef DYNAMIC_OBJECT_SIZE
+  T (     3,  F1 ( 1),      3,  F3 ( 1),  &a2x2[0][0] + 1);
+#else
   T (     3,  F1 ( 1),      3,  F3 ( 3),  &a2x2[0][0] + 1);
+#endif
 
   T (    15,      15,      15,      15,   a3x5);
   T (    15,       5,      15,       5,   &a3x5[0][0] + 0);
+#ifdef DYNAMIC_OBJECT_SIZE
+  T (    14,  F1 ( 4),     14,  F3 ( 4),  &a3x5[0][0] + 1);
+#else
   T (    14,  F1 ( 4),     14,  F3 (14),  &a3x5[0][0] + 1);
+#endif
 
   T (     1,       1,       1,       1,   a1 + 0);
   T (     0,  F1  (0),      0,       0,   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/testsuite/gcc.dg/builtin-object-size-2.c b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
index ad2dd296a9a..c7ce5e988ca 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-2.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-2.c
@@ -32,6 +32,9 @@ test1 (void *q, int x)
   void *p = &a.a[3], *r;
   char var[x + 10];
   struct A vara[x + 10];
+#ifdef DYNAMIC_OBJECT_SIZE
+  size_t dyn_res;
+#endif
   if (x < 0)
     r = &a.a[9];
   else
@@ -43,8 +46,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,36 +65,74 @@ 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
     r = calloc (2, 16);
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (x < 20
+      ? __builtin_object_size (r, 1) != 30
+      : __builtin_object_size (r, 1) != 2 * 16)
+    abort ();
+#else
   /* We may duplicate this test onto the two exit paths.  On one path
      the size will be 32, the other it will be 30.  If we don't duplicate
      this test, then the size will be 32.  */
   if (__builtin_object_size (r, 1) != 2 * 16
       && __builtin_object_size (r, 1) != 30)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
     r = calloc (2, 14);
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (x < 20
+      ? __builtin_object_size (r, 1) != 30
+      : __builtin_object_size (r, 1) != 2 * 14)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != 30)
     abort ();
+#endif
   if (x < 30)
     r = malloc (sizeof (a));
   else
     r = &a.a[3];
+#ifdef DYNAMIC_OBJECT_SIZE
+  dyn_res = x < 30 ? sizeof (a) : sizeof (a.a) - 3;
+  if (__builtin_object_size (r, 1) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a))
     abort ();
+#endif
   r = memcpy (r, "a", 2);
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 1) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a))
     abort ();
+#endif
   r = memcpy (r + 2, "b", 2) + 2;
+#ifdef DYNAMIC_OBJECT_SIZE
+  dyn_res -= 4;
+  if (__builtin_object_size (r, 1) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 1) != sizeof (a) - 4)
     abort ();
+#endif
   r = &a.a[4];
   r = memset (r, 'a', 2);
   if (__builtin_object_size (r, 1) != sizeof (a.a) - 4)
@@ -123,6 +171,20 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&extc[5].c[3], 1) != (size_t) -1)
     abort ();
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (var, 1) != x + 10)
+    abort ();
+  if (__builtin_object_size (var + 10, 1) != x)
+    abort ();
+  if (__builtin_object_size (&var[5], 1) != x + 5)
+    abort ();
+  if (__builtin_object_size (vara, 1) != (x + 10) * sizeof (struct A))
+    abort ();
+  if (__builtin_object_size (vara + 10, 1) != x * sizeof (struct A))
+    abort ();    
+  if (__builtin_object_size (&vara[5], 1) != (x + 5) * sizeof (struct A))
+    abort ();
+#else
   if (__builtin_object_size (var, 1) != (size_t) -1)
     abort ();
   if (__builtin_object_size (var + 10, 1) != (size_t) -1)
@@ -135,6 +197,7 @@ test1 (void *q, int x)
     abort ();    
   if (__builtin_object_size (&vara[5], 1) != (size_t) -1)
     abort ();
+#endif
   if (__builtin_object_size (&vara[0].a, 1) != sizeof (vara[0].a))
     abort ();
   if (__builtin_object_size (&vara[10].a[0], 1) != sizeof (vara[0].a))
@@ -185,6 +248,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 +267,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 +299,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
@@ -339,8 +460,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);
 }
 
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-3.c b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
index d5ca5047ee9..9533af9f058 100644
--- a/gcc/testsuite/gcc.dg/builtin-object-size-3.c
+++ b/gcc/testsuite/gcc.dg/builtin-object-size-3.c
@@ -42,9 +42,17 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (q, 2) != 0)
     abort ();
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 2)
+      != (x < 0
+	  ? sizeof (a) - __builtin_offsetof (struct A, a) - 9
+	  : sizeof (a) - __builtin_offsetof (struct A, c) - 1))
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, c) - 1)
     abort ();
+#endif
   if (x < 6)
     r = &w[2].a[1];
   else
@@ -58,9 +66,17 @@ test1 (void *q, int x)
   if (__builtin_object_size (&y.b, 2)
       != sizeof (a) - __builtin_offsetof (struct A, b))
     abort ();
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (r, 2)
+      != (x < 6
+	  ? 2 * sizeof (w[0]) - __builtin_offsetof (struct A, a) - 1
+	  : sizeof (a) - __builtin_offsetof (struct A, a) - 6))
+    abort ();
+#else
   if (__builtin_object_size (r, 2)
       != sizeof (a) - __builtin_offsetof (struct A, a) - 6)
     abort ();
+#endif
   if (x < 20)
     r = malloc (30);
   else
@@ -71,23 +87,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)
@@ -118,12 +156,21 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&extb[5], 2) != sizeof (extb) - 5)
     abort ();
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (var, 2) != x + 10)
+    abort ();
+  if (__builtin_object_size (var + 10, 2) != x)
+    abort ();
+  if (__builtin_object_size (&var[5], 2) != x + 5)
+    abort ();
+#else
   if (__builtin_object_size (var, 2) != 0)
     abort ();
   if (__builtin_object_size (var + 10, 2) != 0)
     abort ();
   if (__builtin_object_size (&var[5], 2) != 0)
     abort ();
+#endif
   if (__builtin_object_size (zerol, 2) != 0)
     abort ();
   if (__builtin_object_size (&zerol, 2) != 0)
@@ -164,6 +211,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 +230,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)
     {
@@ -194,8 +262,26 @@ 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) - __builtin_offsetof (struct B, buf1) - 7;
+      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) != 0)
     abort ();
+#endif
   r = &buf3[2];
   for (i = 0; i < 4; ++i)
     {
@@ -208,13 +294,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 +344,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 +511,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);
 }
@@ -386,7 +530,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);
 }
diff --git a/gcc/testsuite/gcc.dg/builtin-object-size-4.c b/gcc/testsuite/gcc.dg/builtin-object-size-4.c
index 9f159e36a0f..256cea0b3fb 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);
@@ -119,6 +150,23 @@ test1 (void *q, int x)
     abort ();
   if (__builtin_object_size (&extc[5].c[3], 3) != 0)
     abort ();
+#ifdef DYNAMIC_OBJECT_SIZE
+  if (__builtin_object_size (var, 3) != x + 10)
+    abort ();
+  if (__builtin_object_size (var + 10, 3) != x)
+    abort ();
+  if (__builtin_object_size (&var[5], 3) != x + 5)
+    abort ();
+  size_t vara_sz = (x + 10) * sizeof (struct A);
+  if (__builtin_object_size (vara, 3) != vara_sz)
+    abort ();
+  vara_sz = x * sizeof (struct A);
+  if (__builtin_object_size (vara + 10, 3) != vara_sz)
+    abort ();    
+  vara_sz = (x + 5) * sizeof (struct A);
+  if (__builtin_object_size (&vara[5], 3) != vara_sz)
+    abort ();
+#else
   if (__builtin_object_size (var, 3) != 0)
     abort ();
   if (__builtin_object_size (var + 10, 3) != 0)
@@ -131,6 +179,7 @@ test1 (void *q, int x)
     abort ();    
   if (__builtin_object_size (&vara[5], 3) != 0)
     abort ();
+#endif
   if (__builtin_object_size (&vara[0].a, 3) != sizeof (vara[0].a))
     abort ();
   if (__builtin_object_size (&vara[10].a[0], 3) != sizeof (vara[0].a))
@@ -184,6 +233,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;
@@ -200,8 +252,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, 3) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 3) != sizeof (a.buf1) - 9)
     abort ();
+#endif
   r = &buf3[20];
   for (i = 0; i < 4; ++i)
     {
@@ -214,8 +284,26 @@ 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, 3) != dyn_res)
+    abort ();
+#else
   if (__builtin_object_size (r, 3) != 0)
     abort ();
+#endif
   r = &buf3[1];
   for (i = 0; i < 4; ++i)
     {
@@ -228,13 +316,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
@@ -352,7 +465,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);
 }
-- 
2.31.1


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

* Re: [PATCH 2/8] tree-dynamic-object-size: New pass
  2021-10-07 22:14 ` [PATCH 2/8] tree-dynamic-object-size: New pass Siddhesh Poyarekar
@ 2021-10-08  4:05   ` Siddhesh Poyarekar
  2021-10-12 13:58   ` Jakub Jelinek
  1 sibling, 0 replies; 18+ messages in thread
From: Siddhesh Poyarekar @ 2021-10-08  4:05 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

On 10/8/21 03:44, Siddhesh Poyarekar wrote:
> 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}.
> +

Looks like I had pushed my scratch build without this doc update; this 
fails because I didn't terminate the @deftypefn.  I've fixed it locally 
and will post an updated series along with resolution of feedback I 
receive on the series.

Thanks,
Siddhesh

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

* Re: [PATCH 0/8] __builtin_dynamic_object_size and more
  2021-10-07 22:14 [PATCH 0/8] __builtin_dynamic_object_size and more Siddhesh Poyarekar
                   ` (7 preceding siblings ...)
  2021-10-07 22:14 ` [PATCH 8/8] tree-dynamic-object-size: Add test wrappers to extend testing Siddhesh Poyarekar
@ 2021-10-08  4:50 ` Siddhesh Poyarekar
  2021-10-17 19:57   ` Jeff Law
  8 siblings, 1 reply; 18+ messages in thread
From: Siddhesh Poyarekar @ 2021-10-08  4:50 UTC (permalink / raw)
  To: gcc-patches; +Cc: jakub

On 10/8/21 03:44, Siddhesh Poyarekar wrote:
> (from about 4% to 70% in bash), but that could well be due to the _chk

I should also clarify that this is for memcpy.  For all fortifiable 
functions, the coverage percentage went from 30.81% to 84.5% for bash. 
Below is the full table.  Please note that this is only based on symbols 
emitted in the end as I didn't want to rebuild the _FORTIFIED_SOURCE=2 
binaries, so it does not take into account the fact that _chk could get 
folded to regular calls if we know at compile time that it's safe to do so.

No more posting patches at 4am; it only leads to more clarification 
follow-ups :/

   Func  	F(2)	U(2)	Cov(2)	F(3)	U(3)	Cov(3)
----------------------------------------------------------------
  asprintf	   1	   0	100.00%	   1	   0	100.00%
*confstr	   0	   2	  0.00%	   1	   1	 50.00%
  fdelt	          10	   0	100.00%	  10	   0	100.00%
*fgets	           0	   1	  0.00%	   1	   0	100.00%
  fprintf	  82	   0	100.00%	  82	   0	100.00%
  getcwd	           0	   3	  0.00%	   0	   3	  0.00%
*getgroups	   0	   1	  0.00%	   1	   0	100.00%
  gethostname	   0	   1	  0.00%	   0	   1	  0.00%
  longjmp	  23	   0	100.00%	  23	   0	100.00%
  mbsnrtowcs	   0	   2	  0.00%	   0	   2	  0.00%
*mbsrtowcs	   0	   1	  0.00%	   1	   0	100.00%
*mbstowcs	   0	  10	  0.00%	   5	   5	 50.00%
*memcpy	           7	 170	  3.95%	 116	  40	 74.36%
*memmove	   4	  21	 16.00%	  12	  17	 41.38%
*memset	           0	  24	  0.00%	   3	  21	 12.50%
  printf	         150	   0	100.00%	 150	   0	100.00%
*read	           0	  19	  0.00%	   8	  11	 42.11%
*readlink	   0	   3	  0.00%	   0	   3	  0.00%
  snprintf	  11	   0	100.00%	  11	   0	100.00%
  sprintf	  28	   0	100.00%	  28	   0	100.00%
*strcat 	   0	   5	  0.00%	   8	   0	100.00%
*strcpy 	   6	 440	  1.35%	 413	  36	 91.98%
*strncpy	   2	  52	  3.70%	  33	  19	 63.46%
  vfprintf	  14	   0	100.00%	  14	   0	100.00%
  vsnprintf	   4	   0	100.00%	   4	   0	100.00%
  wcrtomb	   0	   7	  0.00%	   0	   7	  0.00%
*wcsrtombs	   0	   3	  0.00%	   2	   1	 66.67%
  wctomb 	   0	   3	  0.00%	   0	   3	  0.00%
-----------------------------------------------------------------
Total   	 342	 768	 30.81%	927	 170	 84.50%

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

* Re: [PATCH 1/8] __builtin_dynamic_object_size: Recognize builtin name
  2021-10-07 22:14 ` [PATCH 1/8] __builtin_dynamic_object_size: Recognize builtin name Siddhesh Poyarekar
@ 2021-10-12 13:42   ` Jakub Jelinek
  2021-10-12 14:22     ` Siddhesh Poyarekar
  0 siblings, 1 reply; 18+ messages in thread
From: Jakub Jelinek @ 2021-10-12 13:42 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Fri, Oct 08, 2021 at 03:44:25AM +0530, Siddhesh Poyarekar wrote:
> --- a/gcc/builtins.c
> +++ b/gcc/builtins.c
> @@ -180,6 +180,7 @@ static rtx expand_builtin_memory_chk (tree, rtx, machine_mode,
>  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_dyn_object_size (tree, tree);
>  
>  unsigned HOST_WIDE_INT target_newline;
>  unsigned HOST_WIDE_INT target_percent;
> @@ -7910,6 +7911,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
>        return const0_rtx;
>  
>      case BUILT_IN_OBJECT_SIZE:
> +    case BUILT_IN_DYN_OBJECT_SIZE:
>        return expand_builtin_object_size (exp);

I'd strongly prefer BUILT_IN_DYNAMIC_OBJECT_SIZE, we have even longer
builtin enums and the abbreviation will only lead to confusion.

> +/* Fold a call to __builtin_dynamic_object_size with arguments PTR and OST,
> +   if possible.  */
> +
> +static tree
> +fold_builtin_dyn_object_size (tree ptr, tree ost)

Also please don't abbreviate.

> +{
> +  int object_size_type;
> +
> +  if (!valid_object_size_args (ptr, ost, &object_size_type))
> +    return NULL_TREE;
> +
> +  /* __builtin_dynamic_object_size doesn't evaluate side-effects in its
> +     arguments; if there are any side-effects, it returns (size_t) -1 for types
> +     0 and 1 and (size_t) 0 for types 2 and 3.  */
> +  if (TREE_SIDE_EFFECTS (ptr))
> +    return build_int_cst_type (size_type_node, object_size_type < 2 ? -1 : 0);

If we want to commit this patch separately, then the more natural stub
implementation would be fold it into a __builtin_object_size call
(or call fold_builtin_object_size and only if it returns NULL_TREE fold
it into the builtin call).  But I assume we do not want to do that and
want to commit the whole series at once, therefore even this is good enough.

	Jakub


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

* Re: [PATCH 2/8] tree-dynamic-object-size: New pass
  2021-10-07 22:14 ` [PATCH 2/8] tree-dynamic-object-size: New pass Siddhesh Poyarekar
  2021-10-08  4:05   ` Siddhesh Poyarekar
@ 2021-10-12 13:58   ` Jakub Jelinek
  2021-10-12 14:28     ` Siddhesh Poyarekar
  1 sibling, 1 reply; 18+ messages in thread
From: Jakub Jelinek @ 2021-10-12 13:58 UTC (permalink / raw)
  To: Siddhesh Poyarekar; +Cc: gcc-patches

On Fri, Oct 08, 2021 at 03:44:26AM +0530, Siddhesh Poyarekar wrote:
> A new pass is added to execute just before the tree-object-size pass
> to recognize and simplify __builtin_dynamic_object_size.  Some key
> ideas (such as multipass object size collection to detect reference
> loops) have been taken from tree-object-size but is distinct from it
> to ensure minimal impact on existing code.
> 
> At the moment, the pass only recognizes allocators and passthrough
> functions to attempt to derive object size expressions, and replaces
> the call site with those expressions.  On failure, it replaces the
> __builtin_dynamic_object_size with __builtin_object_size as a
> fallback.

Not full review, just nits for now:
I don't really like using separate passes for this, whether
it should be done in the same source or different source file
is something less clear, I guess it depends on how much from
tree-object-size.[ch] can be reused, how much could be e.g. done
by pretending the 0-3 operands of __builtin_dynamic_object_size
are actually 4-7 and have [8] arrays in tree-object-size.[ch]
to track that and how much is separate.
I think the common case is either that a function won't
contain any of the builtin calls (most likely on non-glibc targets,
but even on glibc targets for most of the code that doesn't use any
of the fortified APIs), or it uses just one of them and not both
(e.g. glibc -D_FORTIFY_SOURCE={1,2} vs. -D_FORTIFY_SOURCE=3),
so either one or the other pass would just uselessly walk the whole
IL.
The objsz passes are walk over the whole IL, if they see
__bos calls, do something and call something in the end
and your pass is similar, so hooking it into the current pass
is trivial, just if
if (!gimple_call_builtin_p (call, BUILT_IN_OBJECT_SIZE))
before doing continue; check for the other builtin and call
a function to handle it, either from the same or different file,
and then at the end destruct what is needed too.
Especially on huge functions, each IL traversal may need to page into
caches (and out of them) lots of statements...

> 	* Makefile.in (OBJS): Add tree-dynamic-object-size.o.
> 	(PLUGIN_HEADERS): Add tree-dynamic-object-size.h.
> 	* tree-dynamic-object-size.c: New file.
> 	* tree-dynamic-object-size.h: New file.
> 	* builtins.c: Use it.
> 	(fold_builtin_dyn_object_size): Call
> 	compute_builtin_dyn_object_size for
> 	__builtin_dynamic_object_size builtin.
> 	(passes.def): Add pass_dynamic_object_sizes.
> 	* tree-pass.h: Add ake_pass_dynamic_object_sizes.

Missing m in ake

> +  if (TREE_CODE (ptr) == SSA_NAME
> +      && compute_builtin_dyn_object_size (ptr, object_size_type, &bytes))

Please don't abbreviate.

> +  object_sizes[osi->object_size_type][SSA_NAME_VERSION (dest)] =
> +    object_sizes[osi->object_size_type][SSA_NAME_VERSION (orig)];

GCC coding convention don't want to see the = at the end of line, it should
be at the start of the next line after indentation.

> +/* 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);
> +    }
> +}
> +
> +
> +unsigned int
> +dynamic_object_sizes_execute (function *fun, bool lower_to_bos)
> +{
> +  basic_block bb;
> +
> +  init_dynamic_object_sizes ();
> +

I'd prefer if the initialization could be done only lazily if
it sees at least one such call like the objsz pass does.  That is why
there is the if (computed[0]) return; at the start...

	Jakub


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

* Re: [PATCH 1/8] __builtin_dynamic_object_size: Recognize builtin name
  2021-10-12 13:42   ` Jakub Jelinek
@ 2021-10-12 14:22     ` Siddhesh Poyarekar
  0 siblings, 0 replies; 18+ messages in thread
From: Siddhesh Poyarekar @ 2021-10-12 14:22 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 10/12/21 19:12, Jakub Jelinek wrote:
> On Fri, Oct 08, 2021 at 03:44:25AM +0530, Siddhesh Poyarekar wrote:
>> --- a/gcc/builtins.c
>> +++ b/gcc/builtins.c
>> @@ -180,6 +180,7 @@ static rtx expand_builtin_memory_chk (tree, rtx, machine_mode,
>>   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_dyn_object_size (tree, tree);
>>   
>>   unsigned HOST_WIDE_INT target_newline;
>>   unsigned HOST_WIDE_INT target_percent;
>> @@ -7910,6 +7911,7 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
>>         return const0_rtx;
>>   
>>       case BUILT_IN_OBJECT_SIZE:
>> +    case BUILT_IN_DYN_OBJECT_SIZE:
>>         return expand_builtin_object_size (exp);
> 
> I'd strongly prefer BUILT_IN_DYNAMIC_OBJECT_SIZE, we have even longer
> builtin enums and the abbreviation will only lead to confusion.
> 
>> +/* Fold a call to __builtin_dynamic_object_size with arguments PTR and OST,
>> +   if possible.  */
>> +
>> +static tree
>> +fold_builtin_dyn_object_size (tree ptr, tree ost)
> 
> Also please don't abbreviate.

Got it, will fix.

>> +{
>> +  int object_size_type;
>> +
>> +  if (!valid_object_size_args (ptr, ost, &object_size_type))
>> +    return NULL_TREE;
>> +
>> +  /* __builtin_dynamic_object_size doesn't evaluate side-effects in its
>> +     arguments; if there are any side-effects, it returns (size_t) -1 for types
>> +     0 and 1 and (size_t) 0 for types 2 and 3.  */
>> +  if (TREE_SIDE_EFFECTS (ptr))
>> +    return build_int_cst_type (size_type_node, object_size_type < 2 ? -1 : 0);
> 
> If we want to commit this patch separately, then the more natural stub
> implementation would be fold it into a __builtin_object_size call
> (or call fold_builtin_object_size and only if it returns NULL_TREE fold
> it into the builtin call).  But I assume we do not want to do that and
> want to commit the whole series at once, therefore even this is good enough.

Ideally, it would be great to have the whole series go in at once but if 
we're not able to build consensus soon enough, I'll post this one patch 
for inclusion.

Thanks,
Siddhesh

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

* Re: [PATCH 2/8] tree-dynamic-object-size: New pass
  2021-10-12 13:58   ` Jakub Jelinek
@ 2021-10-12 14:28     ` Siddhesh Poyarekar
  0 siblings, 0 replies; 18+ messages in thread
From: Siddhesh Poyarekar @ 2021-10-12 14:28 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 10/12/21 19:28, Jakub Jelinek wrote:
> On Fri, Oct 08, 2021 at 03:44:26AM +0530, Siddhesh Poyarekar wrote:
>> A new pass is added to execute just before the tree-object-size pass
>> to recognize and simplify __builtin_dynamic_object_size.  Some key
>> ideas (such as multipass object size collection to detect reference
>> loops) have been taken from tree-object-size but is distinct from it
>> to ensure minimal impact on existing code.
>>
>> At the moment, the pass only recognizes allocators and passthrough
>> functions to attempt to derive object size expressions, and replaces
>> the call site with those expressions.  On failure, it replaces the
>> __builtin_dynamic_object_size with __builtin_object_size as a
>> fallback.
> 
> Not full review, just nits for now:
> I don't really like using separate passes for this, whether
> it should be done in the same source or different source file
> is something less clear, I guess it depends on how much from
> tree-object-size.[ch] can be reused, how much could be e.g. done
> by pretending the 0-3 operands of __builtin_dynamic_object_size
> are actually 4-7 and have [8] arrays in tree-object-size.[ch]
> to track that and how much is separate.
> I think the common case is either that a function won't
> contain any of the builtin calls (most likely on non-glibc targets,
> but even on glibc targets for most of the code that doesn't use any
> of the fortified APIs), or it uses just one of them and not both
> (e.g. glibc -D_FORTIFY_SOURCE={1,2} vs. -D_FORTIFY_SOURCE=3),
> so either one or the other pass would just uselessly walk the whole
> IL.

Thanks, that makes sense, I'll make it into a single pass.  My main 
motivation was to keep the code separate to have minimal impact but I 
can do that in the same pass too.  The secondary motivation (or more 
like ambition), i.e. deprecating the __bos pass can be done even if it 
were all one pass, maybe even simpler.

> The objsz passes are walk over the whole IL, if they see
> __bos calls, do something and call something in the end
> and your pass is similar, so hooking it into the current pass
> is trivial, just if
> if (!gimple_call_builtin_p (call, BUILT_IN_OBJECT_SIZE))
> before doing continue; check for the other builtin and call
> a function to handle it, either from the same or different file,
> and then at the end destruct what is needed too.
> Especially on huge functions, each IL traversal may need to page into
> caches (and out of them) lots of statements...

Agreed, I'll work the entry points into tree-object-size.

> 
>> 	* Makefile.in (OBJS): Add tree-dynamic-object-size.o.
>> 	(PLUGIN_HEADERS): Add tree-dynamic-object-size.h.
>> 	* tree-dynamic-object-size.c: New file.
>> 	* tree-dynamic-object-size.h: New file.
>> 	* builtins.c: Use it.
>> 	(fold_builtin_dyn_object_size): Call
>> 	compute_builtin_dyn_object_size for
>> 	__builtin_dynamic_object_size builtin.
>> 	(passes.def): Add pass_dynamic_object_sizes.
>> 	* tree-pass.h: Add ake_pass_dynamic_object_sizes.
> 
> Missing m in ake
> 
>> +  if (TREE_CODE (ptr) == SSA_NAME
>> +      && compute_builtin_dyn_object_size (ptr, object_size_type, &bytes))
> 
> Please don't abbreviate.
> 
>> +  object_sizes[osi->object_size_type][SSA_NAME_VERSION (dest)] =
>> +    object_sizes[osi->object_size_type][SSA_NAME_VERSION (orig)];
> 
> GCC coding convention don't want to see the = at the end of line, it should
> be at the start of the next line after indentation.
> 
>> +/* 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);
>> +    }
>> +}
>> +
>> +
>> +unsigned int
>> +dynamic_object_sizes_execute (function *fun, bool lower_to_bos)
>> +{
>> +  basic_block bb;
>> +
>> +  init_dynamic_object_sizes ();
>> +
> 
> I'd prefer if the initialization could be done only lazily if
> it sees at least one such call like the objsz pass does.  That is why
> there is the if (computed[0]) return; at the start...

OK, will fix all these nits too.

Thanks,
Siddhesh

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

* Re: [PATCH 0/8] __builtin_dynamic_object_size and more
  2021-10-08  4:50 ` [PATCH 0/8] __builtin_dynamic_object_size and more Siddhesh Poyarekar
@ 2021-10-17 19:57   ` Jeff Law
  0 siblings, 0 replies; 18+ messages in thread
From: Jeff Law @ 2021-10-17 19:57 UTC (permalink / raw)
  To: Siddhesh Poyarekar, gcc-patches; +Cc: jakub



On 10/7/2021 10:50 PM, Siddhesh Poyarekar wrote:
> On 10/8/21 03:44, Siddhesh Poyarekar wrote:
>> (from about 4% to 70% in bash), but that could well be due to the _chk
>
> I should also clarify that this is for memcpy.  For all fortifiable 
> functions, the coverage percentage went from 30.81% to 84.5% for bash. 
> Below is the full table.  Please note that this is only based on 
> symbols emitted in the end as I didn't want to rebuild the 
> _FORTIFIED_SOURCE=2 binaries, so it does not take into account the 
> fact that _chk could get folded to regular calls if we know at compile 
> time that it's safe to do so.
>
> No more posting patches at 4am; it only leads to more clarification 
> follow-ups :/
FWIW, that 30% number is roughly in-line with the data we saw from a Red 
Hat partner a year or so ago.  Bringing that up to 80%+ would be a 
notable win, even if folks have to explicitly opt-in, as I expect some 
projects would without hesitation.

I'd really like it if Jakub could take the lead on this.  While I'm a 
big proponent of the workn Jakub knows the relevant code far better than 
I and it'll affect the Red Hat team far more than I'll affect me these 
days :-)


Jeff


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

* Re: [PATCH 6/8] tree-dynamic-object-size: Handle function parameters
  2021-10-07 22:14 ` [PATCH 6/8] tree-dynamic-object-size: Handle function parameters Siddhesh Poyarekar
@ 2021-10-20 16:56   ` Martin Sebor
  2021-10-20 16:59     ` Siddhesh Poyarekar
  0 siblings, 1 reply; 18+ messages in thread
From: Martin Sebor @ 2021-10-20 16:56 UTC (permalink / raw)
  To: Siddhesh Poyarekar, gcc-patches; +Cc: jakub

On 10/7/21 4:14 PM, Siddhesh Poyarekar wrote:
> Handle either static sizes in function parameters or hints provided by
> __attribute__ ((access (...))) to compute sizes for objects.

It's been my hope to eventually teach __builtin_object_size about
attribute access but implementing it in the new built-in might be
preferable.  Glad to see you noticed it and took advantage of it!

Does this include handling "VLA function parameters" as in

   void f (int n, char d[n]);   // (or char d[static n])

I don't see tests for it in this patch but since internally, GCC
describes VLA (and array) function arguments using attribute access
hanndling it should automatically give us VLA (and array) support
as well unless we disable it, either intentionally or by accident.
Either way, I would recommend adding tests for VLA parameters.

Martin

> 
> gcc/ChangeLog:
> 
> 	* tree-dynamic-object-size.c: Include tree-dfa.h.
> 	(emit_size_stmts): New argument osi.  Handle GIMPLE_NOP.
> 	(eval_size_expr, gimplify_size_exprs): Adjust.
> 	(parm_object_size): New function.
> 	(collect_object_sizes_for): Handle GIMPLE_NOP.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* gcc.dg/builtin-dynamic-object-size-0.c (test_parmsz): New
> 	test.
> 	(main): Call it.
> 
> Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
> ---
>   .../gcc.dg/builtin-dynamic-object-size-0.c    | 22 +++++
>   gcc/tree-dynamic-object-size.c                | 98 +++++++++++++++++--
>   2 files changed, 112 insertions(+), 8 deletions(-)
> 
> diff --git a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
> index 3c2c4c84264..c72fa0508db 100644
> --- a/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
> +++ b/gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
> @@ -255,6 +255,15 @@ test_substring_ptrplus (size_t sz, size_t off)
>     return __builtin_dynamic_object_size (str + off, 0);
>   }
>   
> +size_t
> +__attribute__ ((noinline))

I think attribute noipa might be a better choice if the goal
is to keep GCC from sneaking in data from the caller and so
defeating the purpose of the test.

Martin

> +__attribute__ ((access (__read_write__, 1, 2)))
> +test_parmsz (void *obj, size_t sz, size_t off)
> +{
> +  return __builtin_dynamic_object_size (obj + off, 0);
> +}
> +
> +
>   int
>   main (int argc, char **argv)
>   {
> @@ -338,6 +347,19 @@ main (int argc, char **argv)
>     if (test_deploop (128, 129) != 32)
>       FAIL ();
>   
> +  if (test_parmsz (argv[0], __builtin_strlen (argv[0]) + 1, -1)!= 0)
> +    FAIL ();
> +
> +  if (test_parmsz (argv[0], __builtin_strlen (argv[0]) + 1, 0)
> +      != __builtin_strlen (argv[0]) + 1)
> +    FAIL ();
> +  if (test_parmsz (argv[0], __builtin_strlen (argv[0]) + 1,
> +		   __builtin_strlen (argv[0]))!= 1)
> +    FAIL ();
> +  if (test_parmsz (argv[0], __builtin_strlen (argv[0]) + 1,
> +		   __builtin_strlen (argv[0]) + 2)!= 0)
> +    FAIL ();
> +
>     if (nfails > 0)
>       __builtin_abort ();
>   
> diff --git a/gcc/tree-dynamic-object-size.c b/gcc/tree-dynamic-object-size.c
> index f143a64777c..8d7283623dc 100644
> --- a/gcc/tree-dynamic-object-size.c
> +++ b/gcc/tree-dynamic-object-size.c
> @@ -58,6 +58,7 @@ along with GCC; see the file COPYING3.  If not see
>   #include "gimple-fold.h"
>   #include "gimple-iterator.h"
>   #include "tree-cfg.h"
> +#include "tree-dfa.h"
>   #include "stringpool.h"
>   #include "attribs.h"
>   #include "builtins.h"
> @@ -456,7 +457,7 @@ pass_through_call (const gcall *call)
>   
>   
>   static void
> -emit_size_stmts (gimple *stmt, tree wholesize_ssa,
> +emit_size_stmts (object_size_info *osi, gimple *stmt, tree wholesize_ssa,
>   		 tree wholesize_expr, tree size_ssa, tree size_expr)
>   {
>     gimple_seq seq = NULL;
> @@ -481,7 +482,14 @@ emit_size_stmts (gimple *stmt, tree wholesize_ssa,
>        statements involved in evaluation of the object size expression precede
>        the definition statement.  For parameters, we don't have a definition
>        statement, so insert into the first code basic block.  */
> -  gimple_stmt_iterator i = gsi_for_stmt (stmt);
> +  gimple_stmt_iterator i;
> +  if (gimple_code (stmt) == GIMPLE_NOP)
> +    {
> +      basic_block first_bb = single_succ (ENTRY_BLOCK_PTR_FOR_FN (osi->fun));
> +      i = gsi_start_bb (first_bb);
> +    }
> +  else
> +    i = gsi_for_stmt (stmt);
>     gsi_insert_seq_before (&i, seq, GSI_CONTINUE_LINKING);
>   }
>   
> @@ -542,8 +550,8 @@ size_bound_expr (tree sz)
>   }
>   
>   static void
> -eval_size_expr (tree var, tree wholesize, tree *wholesize_expr,
> -		tree size, tree *size_expr)
> +eval_size_expr (struct object_size_info *osi, tree var, tree wholesize,
> +		tree *wholesize_expr, tree size, tree *size_expr)
>   {
>     if (size_expr != NULL)
>       {
> @@ -560,7 +568,7 @@ eval_size_expr (tree var, tree wholesize, tree *wholesize_expr,
>   	}
>         else
>   	{
> -	  emit_size_stmts (stmt, wholesize, *wholesize_expr, size,
> +	  emit_size_stmts (osi, stmt, wholesize, *wholesize_expr, size,
>   			   size_bound_expr (*size_expr));
>   	  delete wholesize_expr;
>   	  delete size_expr;
> @@ -611,7 +619,7 @@ gimplify_size_exprs (object_size_info *osi, tree ptr)
>     for (int object_size_type = 0; object_size_type <= 3; object_size_type++)
>       EXECUTE_IF_SET_IN_BITMAP (computed[object_size_type], 0, i, bi)
>         {
> -	eval_size_expr (ssa_name (i),
> +	eval_size_expr (osi, ssa_name (i),
>   			object_whole_sizes[object_size_type][i],
>   			object_whole_size_exprs[object_size_type][i],
>   			object_sizes[object_size_type][i],
> @@ -939,6 +947,71 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call)
>     cache_object_size_copy (osi, SSA_NAME_VERSION (ptr), sz, sz);
>   }
>   
> +/* Find size of an object passed as a parameter to the function.  */
> +
> +static void
> +parm_object_size (struct object_size_info *osi, tree var)
> +{
> +  tree parm = SSA_NAME_VAR (var);
> +  unsigned varno = SSA_NAME_VERSION (var);
> +  tree sz = NULL_TREE, wholesz = NULL_TREE;
> +
> +  if (dump_file)
> +  {
> +	  fprintf (dump_file, "Object is a parameter: ");
> +	  print_generic_expr (dump_file, parm, dump_flags);
> +	  fprintf (dump_file, " which is %s a pointer type\n",
> +			  POINTER_TYPE_P (TREE_TYPE (parm)) ? "" : "not");
> +  }
> +
> +  if (POINTER_TYPE_P (TREE_TYPE (parm)))
> +    {
> +      /* Look for access attribute.  */
> +      rdwr_map rdwr_idx;
> +
> +      tree fndecl = osi->fun->decl;
> +      const attr_access *access = get_parm_access (rdwr_idx, parm, fndecl);
> +      tree typesize = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (parm)));
> +
> +      if (access && access->sizarg != UINT_MAX)
> +	{
> +	  tree fnargs = DECL_ARGUMENTS (fndecl);
> +	  tree arg = NULL_TREE;
> +	  unsigned argpos = 0;
> +
> +	  /* Walk through the parameters to pick the size parameter and safely
> +	     scale it by the type size.  */
> +	  for (arg = fnargs; argpos != access->sizarg && arg;
> +	       arg = TREE_CHAIN (arg), ++argpos);
> +
> +	  if (arg != NULL_TREE && INTEGRAL_TYPE_P (TREE_TYPE (arg)))
> +	    {
> +	      tree arrsz = get_or_create_ssa_default_def (osi->fun, arg);
> +	      if (arrsz != NULL_TREE)
> +		{
> +		  arrsz = fold_convert (sizetype, arrsz);
> +		  if (typesize)
> +		    {
> +		      tree res = fold_build2 (MULT_EXPR, sizetype, arrsz,
> +					      typesize);
> +		      tree check = fold_build2 (EXACT_DIV_EXPR, sizetype,
> +						TYPE_MAX_VALUE (sizetype),
> +						typesize);
> +		      check = fold_build2 (LT_EXPR, sizetype, arrsz, check);
> +		      arrsz = fold_build3 (COND_EXPR, sizetype, check, res,
> +					   size_zero_node);
> +		    }
> +		}
> +	      sz = wholesz = arrsz;
> +	    }
> +	}
> +    }
> +  else
> +      expr_object_size (osi, parm, &sz, &wholesz);
> +
> +  cache_object_size_copy (osi, varno, sz, wholesz);
> +}
> +
>   /* Compute object sizes for VAR.
>   
>      - For allocation GIMPLE_CALL like malloc or calloc object size is the size
> @@ -948,7 +1021,9 @@ call_object_size (struct object_size_info *osi, tree ptr, gcall *call)
>      - For GIMPLE_PHI, compute a PHI node with sizes of all branches in the PHI
>        node of the object.
>      - For GIMPLE_ASSIGN, return the size of the object the SSA name points
> -     to.  */
> +     to.
> +   - For GIMPLE_NOP, if the variable is a function parameter, compute the size
> +     of the parameter.  */
>   
>   static void
>   collect_object_sizes_for (struct object_size_info *osi, tree var)
> @@ -1076,8 +1151,15 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
>   	break;
>         }
>   
> -    /* Bail out for all other cases.  */
>       case GIMPLE_NOP:
> +      if (SSA_NAME_VAR (var) && TREE_CODE (SSA_NAME_VAR (var)) == PARM_DECL)
> +	parm_object_size (osi, var);
> +      else
> +	/* Uninitialized SSA names point nowhere.  */
> +	cache_object_size_copy (osi, varno, NULL_TREE, NULL_TREE);
> +      break;
> +
> +    /* Bail out for all other cases.  */
>       case GIMPLE_ASM:
>         cache_object_size_copy (osi, varno, NULL_TREE, NULL_TREE);
>         break;
> 


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

* Re: [PATCH 6/8] tree-dynamic-object-size: Handle function parameters
  2021-10-20 16:56   ` Martin Sebor
@ 2021-10-20 16:59     ` Siddhesh Poyarekar
  0 siblings, 0 replies; 18+ messages in thread
From: Siddhesh Poyarekar @ 2021-10-20 16:59 UTC (permalink / raw)
  To: Martin Sebor, gcc-patches; +Cc: jakub

On 10/20/21 22:26, Martin Sebor wrote:
> On 10/7/21 4:14 PM, Siddhesh Poyarekar wrote:
>> Handle either static sizes in function parameters or hints provided by
>> __attribute__ ((access (...))) to compute sizes for objects.
> 
> It's been my hope to eventually teach __builtin_object_size about
> attribute access but implementing it in the new built-in might be
> preferable.  Glad to see you noticed it and took advantage of it!
> 
> Does this include handling "VLA function parameters" as in
> 
>    void f (int n, char d[n]);   // (or char d[static n])
> 
> I don't see tests for it in this patch but since internally, GCC
> describes VLA (and array) function arguments using attribute access
> hanndling it should automatically give us VLA (and array) support
> as well unless we disable it, either intentionally or by accident.
> Either way, I would recommend adding tests for VLA parameters.

Not in this patchset, but I did notice it and put it in my list of 
things to add on once this makes it in.

Thanks,
Siddhesh

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

end of thread, other threads:[~2021-10-20 17:00 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-10-07 22:14 [PATCH 0/8] __builtin_dynamic_object_size and more Siddhesh Poyarekar
2021-10-07 22:14 ` [PATCH 1/8] __builtin_dynamic_object_size: Recognize builtin name Siddhesh Poyarekar
2021-10-12 13:42   ` Jakub Jelinek
2021-10-12 14:22     ` Siddhesh Poyarekar
2021-10-07 22:14 ` [PATCH 2/8] tree-dynamic-object-size: New pass Siddhesh Poyarekar
2021-10-08  4:05   ` Siddhesh Poyarekar
2021-10-12 13:58   ` Jakub Jelinek
2021-10-12 14:28     ` Siddhesh Poyarekar
2021-10-07 22:14 ` [PATCH 3/8] tree-dynamic-object-size: Handle GIMPLE_PHI Siddhesh Poyarekar
2021-10-07 22:14 ` [PATCH 4/8] tree-dynamic-object-size: Support ADDR_EXPR Siddhesh Poyarekar
2021-10-07 22:14 ` [PATCH 5/8] tree-dynamic-object-size: Handle GIMPLE_ASSIGN Siddhesh Poyarekar
2021-10-07 22:14 ` [PATCH 6/8] tree-dynamic-object-size: Handle function parameters Siddhesh Poyarekar
2021-10-20 16:56   ` Martin Sebor
2021-10-20 16:59     ` Siddhesh Poyarekar
2021-10-07 22:14 ` [PATCH 7/8] tree-dynamic-object-size: Get subobject sizes Siddhesh Poyarekar
2021-10-07 22:14 ` [PATCH 8/8] tree-dynamic-object-size: Add test wrappers to extend testing Siddhesh Poyarekar
2021-10-08  4:50 ` [PATCH 0/8] __builtin_dynamic_object_size and more Siddhesh Poyarekar
2021-10-17 19:57   ` Jeff Law

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