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 <siddh...@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

Reply via email to