@@ -219,6 +219,60 @@ 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_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)
+{
+ char str[sz];
+
+ return __builtin_dynamic_object_size (&str[off], 0);
+}
+
size_t
__attribute__ ((access (__read_write__, 1, 2)))
test_parmsz_simple (void *obj, size_t sz)
@@ -286,6 +340,23 @@ 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_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_dynarray_cond (0) != 16)
FAIL ();
if (test_dynarray_cond (1) != 8)
@@ -81,30 +81,56 @@ test1 (void *q, int x)
r = malloc (30);
else
r = calloc (2, 16);
+#ifdef DYNAMIC_OBJECT_SIZE
+ if (__builtin_object_size (r, 0) != (x < 20 ? 30 : 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, 0) != 2 * 16
&& __builtin_object_size (r, 0) != 30)
abort ();
+#endif
if (x < 20)
r = malloc (30);
else
r = calloc (2, 14);
+#ifdef DYNAMIC_OBJECT_SIZE
+ if (__builtin_object_size (r, 0) != (x < 20 ? 30 : 2 * 14))
+ abort ();
+#else
if (__builtin_object_size (r, 0) != 30)
abort ();
+#endif
if (x < 30)
r = malloc (sizeof (a));
else
r = &a.a[3];
+#ifdef DYNAMIC_OBJECT_SIZE
+ if (__builtin_object_size (r, 0) != (x < 30 ? sizeof (a) : sizeof (a) - 3))
+ abort ();
+#else
if (__builtin_object_size (r, 0) != sizeof (a))
abort ();
+#endif
r = memcpy (r, "a", 2);
+#ifdef DYNAMIC_OBJECT_SIZE
+ if (__builtin_object_size (r, 0) != (x < 30 ? sizeof (a) : sizeof (a) - 3))
+ abort ();
+#else
if (__builtin_object_size (r, 0) != sizeof (a))
abort ();
+#endif
r = memcpy (r + 2, "b", 2) + 2;
+#ifdef DYNAMIC_OBJECT_SIZE
+ if (__builtin_object_size (r, 0)
+ != (x < 30 ? sizeof (a) - 4 : sizeof (a) - 7))
+ abort ();
+#else
if (__builtin_object_size (r, 0) != sizeof (a) - 4)
abort ();
+#endif
r = &a.a[4];
r = memset (r, 'a', 2);
if (__builtin_object_size (r, 0)
@@ -140,14 +166,16 @@ test1 (void *q, int x)
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 ();
-#endif
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)
@@ -75,30 +75,56 @@ test1 (void *q, int x)
r = malloc (30);
else
r = calloc (2, 16);
+#ifdef DYNAMIC_OBJECT_SIZE
+ if (__builtin_object_size (r, 1) != (x < 20 ? 30 : 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 (__builtin_object_size (r, 1) != (x < 20 ? 30 : 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
+ if (__builtin_object_size (r, 1) != (x < 30 ? sizeof (a) : sizeof (a) - 3))
+ 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) != (x < 30 ? sizeof (a) : sizeof (a) - 3))
+ abort ();
+#else
if (__builtin_object_size (r, 1) != sizeof (a))
abort ();
+#endif
r = memcpy (r + 2, "b", 2) + 2;
+#ifdef DYNAMIC_OBJECT_SIZE
+ if (__builtin_object_size (r, 0)
+ != (x < 30 ? sizeof (a) - 4 : sizeof (a) - 7))
+ 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)
@@ -142,27 +168,28 @@ test1 (void *q, int x)
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)
abort ();
-#endif
if (__builtin_object_size (&var[5], 1) != (size_t) -1)
abort ();
-#ifdef DYNAMIC_OBJECT_SIZE
- if (__builtin_object_size (vara, 1) != (x + 10) * sizeof (struct A))
- abort ();
- if (__builtin_object_size (vara + 10, 1) != x * sizeof (struct A))
- abort ();
-#else
if (__builtin_object_size (vara, 1) != (size_t) -1)
abort ();
if (__builtin_object_size (vara + 10, 1) != (size_t) -1)
abort ();
-#endif
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))
@@ -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,15 +66,28 @@ 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
r = calloc (2, 16);
+#ifdef DYNAMIC_OBJECT_SIZE
+ if (__builtin_object_size (r, 2) != (x < 20 ? 30 : 2 * 16))
+ abort ();
+#else
if (__builtin_object_size (r, 2) != 30)
abort ();
+#endif
if (x < 20)
r = malloc (30);
else
@@ -145,14 +166,16 @@ test1 (void *q, int x)
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 ();
-#endif
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)
@@ -155,27 +155,28 @@ test1 (void *q, int x)
abort ();
if (__builtin_object_size (var + 10, 3) != x)
abort ();
+ if (__builtin_object_size (&var[5], 3) != x + 5)
+ abort ();
+ if (__builtin_object_size (vara, 3) != (x + 10) * sizeof (struct A))
+ abort ();
+ if (__builtin_object_size (vara + 10, 3) != x * sizeof (struct A))
+ abort ();
+ if (__builtin_object_size (&vara[5], 3) != (x + 5) * sizeof (struct A))
+ abort ();
#else
if (__builtin_object_size (var, 3) != 0)
abort ();
if (__builtin_object_size (var + 10, 3) != 0)
abort ();
-#endif
if (__builtin_object_size (&var[5], 3) != 0)
abort ();
-#ifdef DYNAMIC_OBJECT_SIZE
- if (__builtin_object_size (vara, 3) != (x + 10) * sizeof (struct A))
- abort ();
- if (__builtin_object_size (vara + 10, 3) != x * sizeof (struct A))
- abort ();
-#else
if (__builtin_object_size (vara, 3) != 0)
abort ();
if (__builtin_object_size (vara + 10, 3) != 0)
abort ();
-#endif
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))
@@ -123,6 +123,16 @@ size_unknown_p (tree val, int object_size_type)
&& tree_to_uhwi (val) == unknown (object_size_type));
}
+/* Return true if VAL is represents a known size for OBJECT_SIZE_TYPE. */
+
+static inline bool
+size_known_p (tree val, int object_size_type)
+{
+ return (!size_unknown_p (val, object_size_type)
+ && ((object_size_type & OST_DYNAMIC)
+ || TREE_CODE (val) == INTEGER_CST));
+}
+
/* Return a tree with initial value for OBJECT_SIZE_TYPE. */
static inline tree
@@ -391,16 +401,15 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
if (!size_unknown_p (sz, object_size_type))
{
tree offset = TREE_OPERAND (pt_var, 1);
- if (TREE_CODE (offset) != INTEGER_CST
- || TREE_CODE (sz) != INTEGER_CST)
+ if (TREE_CODE (offset) != INTEGER_CST)
sz = size_unknown (object_size_type);
else
sz = size_for_offset (sz, offset);
}
if (!size_unknown_p (sz, object_size_type)
- && TREE_CODE (sz) == INTEGER_CST
- && compare_tree_int (sz, offset_limit) < 0)
+ && (TREE_CODE (sz) != INTEGER_CST
+ || compare_tree_int (sz, offset_limit) < 0))
pt_var_size = sz;
}
else if (DECL_P (pt_var))
@@ -416,8 +425,9 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
if (pt_var_size)
{
- /* Validate the size determined above. */
- if (compare_tree_int (pt_var_size, offset_limit) >= 0)
+ /* Validate the size determined above if it is a constant. */
+ if (TREE_CODE (pt_var_size) == INTEGER_CST
+ && compare_tree_int (pt_var_size, offset_limit) >= 0)
return false;
}
@@ -441,7 +451,7 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
var = TREE_OPERAND (var, 0);
if (! TYPE_SIZE_UNIT (TREE_TYPE (var))
|| ! tree_fits_uhwi_p (TYPE_SIZE_UNIT (TREE_TYPE (var)))
- || (pt_var_size
+ || (pt_var_size && TREE_CODE (pt_var_size) == INTEGER_CST
&& tree_int_cst_lt (pt_var_size,
TYPE_SIZE_UNIT (TREE_TYPE (var)))))
var = pt_var;
@@ -455,17 +465,11 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
switch (TREE_CODE (v))
{
case ARRAY_REF:
- if (TYPE_SIZE_UNIT (TREE_TYPE (TREE_OPERAND (v, 0)))
- && TREE_CODE (TREE_OPERAND (v, 1)) == INTEGER_CST)
+ 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)
- && TREE_CODE (TYPE_MAX_VALUE (domain))
- == INTEGER_CST
- && tree_int_cst_lt (TREE_OPERAND (v, 1),
- TYPE_MAX_VALUE (domain)))
+ if (domain && TYPE_MAX_VALUE (domain))
{
v = NULL_TREE;
break;
@@ -532,20 +536,20 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
var = pt_var;
if (var != pt_var)
- var_size = TYPE_SIZE_UNIT (TREE_TYPE (var));
+ {
+ var_size = TYPE_SIZE_UNIT (TREE_TYPE (var));
+ if (!TREE_CONSTANT (var_size))
+ var_size = get_or_create_ssa_default_def (cfun, var_size);
+ if (!var_size)
+ return false;
+ }
else if (!pt_var_size)
return false;
else
var_size = pt_var_size;
bytes = compute_object_offset (TREE_OPERAND (ptr, 0), var);
if (bytes != error_mark_node)
- {
- if (TREE_CODE (bytes) == INTEGER_CST
- && tree_int_cst_lt (var_size, bytes))
- bytes = size_zero_node;
- else
- bytes = size_binop (MINUS_EXPR, var_size, bytes);
- }
+ bytes = size_for_offset (var_size, bytes);
if (var != pt_var
&& pt_var_size
&& TREE_CODE (pt_var) == MEM_REF
@@ -554,11 +558,7 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
tree bytes2 = compute_object_offset (TREE_OPERAND (ptr, 0), pt_var);
if (bytes2 != error_mark_node)
{
- if (TREE_CODE (bytes2) == INTEGER_CST
- && tree_int_cst_lt (pt_var_size, bytes2))
- bytes2 = size_zero_node;
- else
- bytes2 = size_binop (MINUS_EXPR, pt_var_size, bytes2);
+ bytes2 = size_for_offset (pt_var_size, bytes2);
bytes = size_binop (MIN_EXPR, bytes, bytes2);
}
}
@@ -568,8 +568,13 @@ addr_object_size (struct object_size_info *osi, const_tree ptr,
else
bytes = pt_var_size;
- *psize = bytes;
- return true;
+ if (size_known_p (bytes, object_size_type))
+ {
+ *psize = bytes;
+ return true;
+ }
+
+ return false;
}
@@ -739,19 +744,27 @@ estimate_size (object_size_info *osi, tree size, bitmap *visitlog = NULL)
case PLUS_EXPR:
{
tree ret = estimate_size (osi, TREE_OPERAND (size, 0), visitlog);
+ tree off = TREE_OPERAND (size, 1);
if (size_unknown_p (ret, object_size_type))
return size_unknown (object_size_type);
- tree off = TREE_OPERAND (size, 1);
- gcc_checking_assert (TREE_CODE (off) == INTEGER_CST);
-
- if (code == PLUS_EXPR)
- off = fold_build1 (NEGATE_EXPR, sizetype, off);
-
- if (tree_fits_uhwi_p (ret) && tree_int_cst_le (ret, off))
- return size_int (0);
- return size_binop (MINUS_EXPR, ret, off);
+ /* We don't need this for dynamic object sizes because we don't make
+ reducing estimates like in case of static sizes. Instead, we
+ return an expression that gives the precise size after a specific
+ number of iterations, reducing to zero at runtime if the pointer
+ being evaluated overflows. */
+ if (!(object_size_type & OST_DYNAMIC))
+ {
+ gcc_checking_assert (TREE_CODE (off) == INTEGER_CST);
+ if (code == PLUS_EXPR)
+ {
+ off = fold_build1 (NEGATE_EXPR, sizetype, off);
+ ret = size_binop (MAX_EXPR, ret, off);
+ code = MINUS_EXPR;
+ }
+ }
+ return size_binop (code, ret, off);
}
case INTEGER_CST:
default:
@@ -1163,7 +1176,7 @@ call_object_size (struct object_size_info *osi, gcall *call)
tree bytes = alloc_object_size (call, object_size_type);
- if ((object_size_type & OST_DYNAMIC) || TREE_CODE (bytes) == INTEGER_CST)
+ if (size_known_p (bytes, object_size_type))
return bytes;
return size_unknown (object_size_type);
Allow dynamic expressions from ADDR_EXPR for __builtin_dynamic_object_size. Offsets in objects still need to be constant for now because offset computation need more validation to support, e.g. negative offsets with dynamic sizes. gcc/ChangeLog: * tree-object-size.c (size_known_p): New function. (addr_object_size): Build dynamic expressions for object sizes. (estimate_size): Limit PLUS_EXPR rewriting to static object sizes. (call_object_size): Call size_known_p. gcc/testsuite/ChangeLog: * gcc.dg/builtin-dynamic-object-size-0.c: Add new tests. * gcc.dg/builtin-object-size-1.c (test1) [DYNAMIC_OBJECT_SIZE]: Adjust expected output for dynamic object sizes. * gcc.dg/builtin-object-size-2.c (test1) [DYNAMIC_OBJECT_SIZE]: Likewise. * gcc.dg/builtin-object-size-3.c (test1) [DYNAMIC_OBJECT_SIZE]: Likewise. * gcc.dg/builtin-object-size-4.c (test1) [DYNAMIC_OBJECT_SIZE]: Likewise. Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org> --- .../gcc.dg/builtin-dynamic-object-size-0.c | 71 ++++++++++++++ gcc/testsuite/gcc.dg/builtin-object-size-1.c | 30 +++++- gcc/testsuite/gcc.dg/builtin-object-size-2.c | 43 +++++++-- gcc/testsuite/gcc.dg/builtin-object-size-3.c | 25 ++++- gcc/testsuite/gcc.dg/builtin-object-size-4.c | 17 ++-- gcc/tree-object-size.c | 93 +++++++++++-------- 6 files changed, 221 insertions(+), 58 deletions(-)