@@ -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)
@@ -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;
}