tree-object-size: Support dynamic sizes in conditions
Handle GIMPLE_PHI and conditionals specially for dynamic objects, returning PHI/conditional expressions instead of just a MIN/MAX estimate. This makes the returned object size variable for loops and conditionals, so tests need to be adjusted to look for precise size in some cases. builtin-dynamic-object-size-5.c had to be modified to only look for success in maximum object size case and skip over the minimum object size tests because the result is no longer a compile time constant. I also added some simple tests to exercise conditionals with dynamic object sizes. gcc/ChangeLog: PR middle-end/70090 * builtins.c (fold_builtin_object_size): Adjust for dynamic size expressions. * tree-object-size.c: Include gimplify-me.h. (struct object_size_info): New member UNKNOWNS. (size_initval_p, size_usable_p, object_sizes_get_raw): New functions. (object_sizes_get): Return suitable gimple variable for object size. (bundle_sizes): New function. (object_sizes_set): Use it and handle dynamic object size expressions. (object_sizes_set_temp): New function. (size_for_offset): Adjust for dynamic size expressions. (emit_phi_nodes, propagate_unknowns, gimplify_size_expressions): New functions. (compute_builtin_object_size): Call gimplify_size_expressions for OST_DYNAMIC. (dynamic_object_size): New function. (cond_expr_object_size): Use it. (phi_dynamic_object_size): New function. (collect_object_sizes_for): Call it for OST_DYNAMIC. Adjust to accommodate dynamic object sizes. gcc/testsuite/ChangeLog: PR middle-end/70090 * gcc.dg/builtin-dynamic-object-size-0.c: New tests. * gcc.dg/builtin-dynamic-object-size-10.c: Add comment. * gcc.dg/builtin-dynamic-object-size-5-main.c: New file. * gcc.dg/builtin-dynamic-object-size-5.c: Use it and change test to dg-do run. * gcc.dg/builtin-object-size-5.c [!N]: Define N. (test1, test2, test3, test4) [__builtin_object_size]: Expect exact result for __builtin_dynamic_object_size. * gcc.dg/builtin-object-size-1.c [__builtin_object_size]: Expect exact size expressions for __builtin_dynamic_object_size. * gcc.dg/builtin-object-size-2.c [__builtin_object_size]: Likewise. * gcc.dg/builtin-object-size-3.c [__builtin_object_size]: Likewise. * gcc.dg/builtin-object-size-4.c [__builtin_object_size]: Likewise. Signed-off-by: Siddhesh Poyarekar <siddhesh@gotplt.org>
This commit is contained in:
parent
026d44cbbd
commit
404c787e2b
@ -10306,7 +10306,8 @@ fold_builtin_object_size (tree ptr, tree ost, enum built_in_function fcode)
|
||||
if (TREE_CODE (ptr) == ADDR_EXPR)
|
||||
{
|
||||
compute_builtin_object_size (ptr, object_size_type, &bytes);
|
||||
if (int_fits_type_p (bytes, size_type_node))
|
||||
if ((object_size_type & OST_DYNAMIC)
|
||||
|| int_fits_type_p (bytes, size_type_node))
|
||||
return fold_convert (size_type_node, bytes);
|
||||
}
|
||||
else if (TREE_CODE (ptr) == SSA_NAME)
|
||||
@ -10315,7 +10316,8 @@ fold_builtin_object_size (tree ptr, tree ost, enum built_in_function fcode)
|
||||
later. Maybe subsequent passes will help determining
|
||||
it. */
|
||||
if (compute_builtin_object_size (ptr, object_size_type, &bytes)
|
||||
&& int_fits_type_p (bytes, size_type_node))
|
||||
&& ((object_size_type & OST_DYNAMIC)
|
||||
|| int_fits_type_p (bytes, size_type_node)))
|
||||
return fold_convert (size_type_node, bytes);
|
||||
}
|
||||
|
||||
|
58
gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
Normal file
58
gcc/testsuite/gcc.dg/builtin-dynamic-object-size-0.c
Normal file
@ -0,0 +1,58 @@
|
||||
/* { dg-do run } */
|
||||
/* { dg-options "-O2" } */
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
#define abort __builtin_abort
|
||||
|
||||
size_t
|
||||
__attribute__ ((noinline))
|
||||
test_builtin_malloc_condphi (int cond)
|
||||
{
|
||||
void *ch;
|
||||
|
||||
if (cond)
|
||||
ch = __builtin_malloc (32);
|
||||
else
|
||||
ch = __builtin_malloc (64);
|
||||
|
||||
size_t ret = __builtin_dynamic_object_size (ch, 0);
|
||||
|
||||
__builtin_free (ch);
|
||||
return ret;
|
||||
}
|
||||
|
||||
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 (64);
|
||||
|
||||
return __builtin_dynamic_object_size (bin, 0);
|
||||
}
|
||||
|
||||
unsigned nfails = 0;
|
||||
|
||||
#define FAIL() ({ \
|
||||
__builtin_printf ("Failure at line: %d\n", __LINE__); \
|
||||
nfails++; \
|
||||
})
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
if (test_builtin_malloc_condphi (1) != 32)
|
||||
FAIL ();
|
||||
if (test_builtin_malloc_condphi (0) != 64)
|
||||
FAIL ();
|
||||
if (test_deploop (128, 129) != 32)
|
||||
FAIL ();
|
||||
|
||||
if (nfails > 0)
|
||||
__builtin_abort ();
|
||||
|
||||
return 0;
|
||||
}
|
@ -5,5 +5,7 @@
|
||||
#define __builtin_object_size __builtin_dynamic_object_size
|
||||
#include "builtin-object-size-10.c"
|
||||
|
||||
/* early_objsz should resolve __builtin_dynamic_object_size like
|
||||
__builtin_object_size. */
|
||||
/* { dg-final { scan-tree-dump "maximum object size 21" "early_objsz" } } */
|
||||
/* { dg-final { scan-tree-dump "maximum subobject size 16" "early_objsz" } } */
|
||||
|
32
gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5-main.c
Normal file
32
gcc/testsuite/gcc.dg/builtin-dynamic-object-size-5-main.c
Normal file
@ -0,0 +1,32 @@
|
||||
#ifdef N
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
char buf[N];
|
||||
|
||||
void test1 (size_t);
|
||||
void test2 (size_t);
|
||||
void test3 (size_t);
|
||||
void test4 (size_t);
|
||||
void test5 (size_t);
|
||||
void test6 (size_t);
|
||||
void test7 (size_t);
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
test1 (42);
|
||||
test2 (42);
|
||||
test3 (42);
|
||||
test4 (42);
|
||||
test5 (42);
|
||||
test6 (42);
|
||||
test7 (42);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
@ -1,7 +1,6 @@
|
||||
/* { dg-do compile { target i?86-*-linux* i?86-*-gnu* x86_64-*-linux* } } */
|
||||
/* { dg-options "-O2" } */
|
||||
/* { dg-do run { target i?86-*-linux* i?86-*-gnu* x86_64-*-linux* } } */
|
||||
/* { dg-options "-O2 -DN=0x4000000" } */
|
||||
/* { dg-additional-sources "builtin-dynamic-object-size-5-main.c" } */
|
||||
|
||||
#define __builtin_object_size __builtin_dynamic_object_size
|
||||
#include "builtin-object-size-5.c"
|
||||
|
||||
/* { dg-final { scan-assembler-not "abort" } } */
|
||||
|
@ -42,9 +42,17 @@ test1 (void *q, int x)
|
||||
abort ();
|
||||
if (__builtin_object_size (q, 0) != (size_t) -1)
|
||||
abort ();
|
||||
#ifdef __builtin_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 __builtin_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
|
||||
@ -165,6 +181,7 @@ test2 (void)
|
||||
struct B { char buf1[10]; char buf2[10]; } a;
|
||||
char *r, buf3[20];
|
||||
int i;
|
||||
size_t res;
|
||||
|
||||
if (sizeof (a) != 20)
|
||||
return;
|
||||
@ -181,7 +198,24 @@ test2 (void)
|
||||
else if (i == l1 + 2)
|
||||
r = &a.buf1[9];
|
||||
}
|
||||
if (__builtin_object_size (r, 0) != 20)
|
||||
#ifdef __builtin_object_size
|
||||
res = sizeof (buf3);
|
||||
|
||||
for (i = 0; i < 4; ++i)
|
||||
{
|
||||
if (i == l1 - 1)
|
||||
res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 1;
|
||||
else if (i == l1)
|
||||
res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 7;
|
||||
else if (i == l1 + 1)
|
||||
res = sizeof (buf3) - 5;
|
||||
else if (i == l1 + 2)
|
||||
res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
|
||||
}
|
||||
#else
|
||||
res = 20;
|
||||
#endif
|
||||
if (__builtin_object_size (r, 0) != res)
|
||||
abort ();
|
||||
r = &buf3[20];
|
||||
for (i = 0; i < 4; ++i)
|
||||
@ -195,13 +229,45 @@ test2 (void)
|
||||
else if (i == l1 + 2)
|
||||
r = &a.buf1[9];
|
||||
}
|
||||
if (__builtin_object_size (r, 0) != 15)
|
||||
#ifdef __builtin_object_size
|
||||
res = sizeof (buf3) - 20;
|
||||
|
||||
for (i = 0; i < 4; ++i)
|
||||
{
|
||||
if (i == l1 - 1)
|
||||
res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 7;
|
||||
else if (i == l1)
|
||||
res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 7;
|
||||
else if (i == l1 + 1)
|
||||
res = sizeof (buf3) - 5;
|
||||
else if (i == l1 + 2)
|
||||
res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
|
||||
}
|
||||
if (__builtin_object_size (r, 0) != res)
|
||||
abort ();
|
||||
#else
|
||||
res = 15;
|
||||
#endif
|
||||
if (__builtin_object_size (r, 0) != res)
|
||||
abort ();
|
||||
r += 8;
|
||||
#ifdef __builtin_object_size
|
||||
res -= 8;
|
||||
if (__builtin_object_size (r, 0) != res)
|
||||
abort ();
|
||||
if (res >= 6)
|
||||
{
|
||||
if (__builtin_object_size (r + 6, 0) != 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 +280,31 @@ test2 (void)
|
||||
else if (i == l1 + 2)
|
||||
r = &a.buf1[4];
|
||||
}
|
||||
#ifdef __builtin_object_size
|
||||
res = sizeof (buf3) - 18;
|
||||
|
||||
for (i = 0; i < 4; ++i)
|
||||
{
|
||||
if (i == l1 - 1)
|
||||
res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 9;
|
||||
else if (i == l1)
|
||||
res = sizeof (a) - __builtin_offsetof (struct B, buf2) - 9;
|
||||
else if (i == l1 + 1)
|
||||
res = sizeof (buf3) - 5;
|
||||
else if (i == l1 + 2)
|
||||
res = sizeof (a) - __builtin_offsetof (struct B, buf1) - 4;
|
||||
}
|
||||
if (res >= 12)
|
||||
{
|
||||
if (__builtin_object_size (r + 12, 0) != 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 +447,10 @@ test5 (size_t x)
|
||||
|
||||
for (i = 0; i < x; ++i)
|
||||
p = p + 4;
|
||||
#ifdef __builtin_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 +460,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 +474,13 @@ test6 (size_t x)
|
||||
|
||||
for (i = 0; i < x; ++i)
|
||||
p = p + 4;
|
||||
#ifdef __builtin_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);
|
||||
}
|
||||
|
||||
@ -436,21 +535,37 @@ test9 (unsigned cond)
|
||||
else
|
||||
p = &buf2[4];
|
||||
|
||||
#ifdef __builtin_object_size
|
||||
if (__builtin_object_size (&p[-4], 0) != (cond ? 6 : 10))
|
||||
abort ();
|
||||
#else
|
||||
if (__builtin_object_size (&p[-4], 0) != 10)
|
||||
abort ();
|
||||
#endif
|
||||
|
||||
for (unsigned i = cond; i > 0; i--)
|
||||
p--;
|
||||
|
||||
#ifdef __builtin_object_size
|
||||
if (__builtin_object_size (p, 0) != ((cond ? 2 : 6) + cond))
|
||||
abort ();
|
||||
#else
|
||||
if (__builtin_object_size (p, 0) != 10)
|
||||
abort ();
|
||||
#endif
|
||||
|
||||
p = &y.c[8];
|
||||
for (unsigned i = cond; i > 0; i--)
|
||||
p--;
|
||||
|
||||
#ifdef __builtin_object_size
|
||||
if (__builtin_object_size (p, 0)
|
||||
!= sizeof (y) - __builtin_offsetof (struct A, c) - 8 + cond)
|
||||
abort ();
|
||||
#else
|
||||
if (__builtin_object_size (p, 0) != sizeof (y))
|
||||
abort ();
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -43,8 +43,15 @@ test1 (void *q, int x)
|
||||
abort ();
|
||||
if (__builtin_object_size (q, 1) != (size_t) -1)
|
||||
abort ();
|
||||
#ifdef __builtin_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,8 +62,15 @@ test1 (void *q, int x)
|
||||
abort ();
|
||||
if (__builtin_object_size (&y.b, 1) != sizeof (a.b))
|
||||
abort ();
|
||||
#ifdef __builtin_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
|
||||
@ -185,6 +199,9 @@ test2 (void)
|
||||
struct B { char buf1[10]; char buf2[10]; } a;
|
||||
char *r, buf3[20];
|
||||
int i;
|
||||
#ifdef __builtin_object_size
|
||||
size_t dyn_res;
|
||||
#endif
|
||||
|
||||
if (sizeof (a) != 20)
|
||||
return;
|
||||
@ -201,8 +218,26 @@ test2 (void)
|
||||
else if (i == l1 + 2)
|
||||
r = &a.buf1[9];
|
||||
}
|
||||
#ifdef __builtin_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 +250,50 @@ test2 (void)
|
||||
else if (i == l1 + 2)
|
||||
r = &a.buf1[9];
|
||||
}
|
||||
#ifdef __builtin_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 __builtin_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 +411,13 @@ test5 (size_t x)
|
||||
|
||||
for (i = 0; i < x; ++i)
|
||||
p = p + 4;
|
||||
#ifdef __builtin_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);
|
||||
}
|
||||
|
||||
@ -394,21 +471,36 @@ test8 (unsigned cond)
|
||||
else
|
||||
p = &buf2[4];
|
||||
|
||||
#ifdef __builtin_object_size
|
||||
if (__builtin_object_size (&p[-4], 1) != (cond ? 6 : 10))
|
||||
abort ();
|
||||
#else
|
||||
if (__builtin_object_size (&p[-4], 1) != 10)
|
||||
abort ();
|
||||
#endif
|
||||
|
||||
for (unsigned i = cond; i > 0; i--)
|
||||
p--;
|
||||
|
||||
#ifdef __builtin_object_size
|
||||
if (__builtin_object_size (p, 1) != ((cond ? 2 : 6) + cond))
|
||||
abort ();
|
||||
#else
|
||||
if (__builtin_object_size (p, 1) != 10)
|
||||
abort ();
|
||||
#endif
|
||||
|
||||
p = &y.c[8];
|
||||
for (unsigned i = cond; i > 0; i--)
|
||||
p--;
|
||||
|
||||
#ifdef __builtin_object_size
|
||||
if (__builtin_object_size (p, 1) != sizeof (y.c) - 8 + cond)
|
||||
abort ();
|
||||
#else
|
||||
if (__builtin_object_size (p, 1) != sizeof (y.c))
|
||||
abort ();
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -71,23 +71,45 @@ test1 (void *q, int x)
|
||||
r = malloc (30);
|
||||
else
|
||||
r = calloc (2, 14);
|
||||
#ifdef __builtin_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 __builtin_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 __builtin_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 __builtin_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)
|
||||
@ -164,6 +186,9 @@ test2 (void)
|
||||
struct B { char buf1[10]; char buf2[10]; } a;
|
||||
char *r, buf3[20];
|
||||
int i;
|
||||
#ifdef __builtin_object_size
|
||||
size_t dyn_res;
|
||||
#endif
|
||||
|
||||
if (sizeof (a) != 20)
|
||||
return;
|
||||
@ -180,8 +205,26 @@ test2 (void)
|
||||
else if (i == l1 + 2)
|
||||
r = &a.buf1[9];
|
||||
}
|
||||
#ifdef __builtin_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)
|
||||
{
|
||||
@ -208,13 +251,44 @@ test2 (void)
|
||||
else if (i == l1 + 2)
|
||||
r = &a.buf1[4];
|
||||
}
|
||||
#ifdef __builtin_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 __builtin_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 +301,31 @@ test2 (void)
|
||||
else if (i == l1 + 2)
|
||||
r = &a.buf1[4];
|
||||
}
|
||||
#ifdef __builtin_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 +468,11 @@ test5 (size_t x)
|
||||
|
||||
for (i = 0; i < x; ++i)
|
||||
p = p + 4;
|
||||
#ifdef __builtin_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 +487,11 @@ test6 (size_t x)
|
||||
|
||||
for (i = 0; i < x; ++i)
|
||||
p = p + 4;
|
||||
#ifdef __builtin_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);
|
||||
}
|
||||
@ -442,22 +547,38 @@ test9 (unsigned cond)
|
||||
else
|
||||
p = &buf2[4];
|
||||
|
||||
#ifdef __builtin_object_size
|
||||
if (__builtin_object_size (&p[-4], 2) != (cond ? 6 : 10))
|
||||
abort ();
|
||||
#else
|
||||
if (__builtin_object_size (&p[-4], 2) != 6)
|
||||
abort ();
|
||||
#endif
|
||||
|
||||
for (unsigned i = cond; i > 0; i--)
|
||||
p--;
|
||||
|
||||
#ifdef __builtin_object_size
|
||||
if (__builtin_object_size (p, 2) != ((cond ? 2 : 6) + cond))
|
||||
abort ();
|
||||
#else
|
||||
if (__builtin_object_size (p, 2) != 2)
|
||||
abort ();
|
||||
#endif
|
||||
|
||||
p = &y.c[8];
|
||||
for (unsigned i = cond; i > 0; i--)
|
||||
p--;
|
||||
|
||||
#ifdef __builtin_object_size
|
||||
if (__builtin_object_size (p, 2)
|
||||
!= sizeof (y) - __builtin_offsetof (struct A, c) - 8 + cond)
|
||||
abort ();
|
||||
#else
|
||||
if (__builtin_object_size (p, 2)
|
||||
!= sizeof (y) - __builtin_offsetof (struct A, c) - 8)
|
||||
abort ();
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -43,7 +43,12 @@ test1 (void *q, int x)
|
||||
abort ();
|
||||
if (__builtin_object_size (q, 3) != 0)
|
||||
abort ();
|
||||
#ifdef __builtin_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 __builtin_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 __builtin_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 __builtin_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 __builtin_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 __builtin_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 __builtin_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);
|
||||
@ -184,6 +215,9 @@ test2 (void)
|
||||
struct B { char buf1[10]; char buf2[10]; } a;
|
||||
char *r, buf3[20];
|
||||
int i;
|
||||
#ifdef __builtin_object_size
|
||||
size_t dyn_res = 0;
|
||||
#endif
|
||||
|
||||
if (sizeof (a) != 20)
|
||||
return;
|
||||
@ -228,13 +262,38 @@ test2 (void)
|
||||
else if (i == l1 + 2)
|
||||
r = &a.buf1[2];
|
||||
}
|
||||
#ifdef __builtin_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 __builtin_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 +411,11 @@ test5 (size_t x)
|
||||
|
||||
for (i = 0; i < x; ++i)
|
||||
p = p + 4;
|
||||
#ifdef __builtin_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);
|
||||
}
|
||||
@ -407,21 +470,36 @@ test8 (unsigned cond)
|
||||
else
|
||||
p = &buf2[4];
|
||||
|
||||
#ifdef __builtin_object_size
|
||||
if (__builtin_object_size (&p[-4], 3) != (cond ? 6 : 10))
|
||||
abort ();
|
||||
#else
|
||||
if (__builtin_object_size (&p[-4], 3) != 6)
|
||||
abort ();
|
||||
#endif
|
||||
|
||||
for (unsigned i = cond; i > 0; i--)
|
||||
p--;
|
||||
|
||||
#ifdef __builtin_object_size
|
||||
if (__builtin_object_size (p, 3) != ((cond ? 2 : 6) + cond))
|
||||
abort ();
|
||||
#else
|
||||
if (__builtin_object_size (p, 3) != 2)
|
||||
abort ();
|
||||
#endif
|
||||
|
||||
p = &y.c[8];
|
||||
for (unsigned i = cond; i > 0; i--)
|
||||
p--;
|
||||
|
||||
#ifdef __builtin_object_size
|
||||
if (__builtin_object_size (p, 3) != sizeof (y.c) - 8 + cond)
|
||||
abort ();
|
||||
#else
|
||||
if (__builtin_object_size (p, 3) != sizeof (y.c) - 8)
|
||||
abort ();
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -1,9 +1,13 @@
|
||||
/* { dg-do compile { target i?86-*-linux* i?86-*-gnu* x86_64-*-linux* } } */
|
||||
/* { dg-options "-O2" } */
|
||||
|
||||
#ifndef N
|
||||
# define N 0x40000000
|
||||
#endif
|
||||
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
extern void abort (void);
|
||||
extern char buf[0x40000000];
|
||||
extern char buf[N];
|
||||
|
||||
void
|
||||
test1 (size_t x)
|
||||
@ -13,7 +17,11 @@ test1 (size_t x)
|
||||
|
||||
for (i = 0; i < x; ++i)
|
||||
p = p + 4;
|
||||
#ifdef __builtin_object_size
|
||||
if (__builtin_object_size (p, 0) != sizeof (buf) - 8 - 4 * x)
|
||||
#else
|
||||
if (__builtin_object_size (p, 0) != sizeof (buf) - 8)
|
||||
#endif
|
||||
abort ();
|
||||
}
|
||||
|
||||
@ -25,7 +33,11 @@ test2 (size_t x)
|
||||
|
||||
for (i = 0; i < x; ++i)
|
||||
p = p + 4;
|
||||
#ifdef __builtin_object_size
|
||||
if (__builtin_object_size (p, 1) != sizeof (buf) - 8 - 4 * x)
|
||||
#else
|
||||
if (__builtin_object_size (p, 1) != sizeof (buf) - 8)
|
||||
#endif
|
||||
abort ();
|
||||
}
|
||||
|
||||
@ -37,7 +49,11 @@ test3 (size_t x)
|
||||
|
||||
for (i = 0; i < x; ++i)
|
||||
p = p + 4;
|
||||
#ifdef __builtin_object_size
|
||||
if (__builtin_object_size (p, 2) != sizeof (buf) - 8 - 4 * x)
|
||||
#else
|
||||
if (__builtin_object_size (p, 2) != 0)
|
||||
#endif
|
||||
abort ();
|
||||
}
|
||||
|
||||
@ -49,7 +65,11 @@ test4 (size_t x)
|
||||
|
||||
for (i = 0; i < x; ++i)
|
||||
p = p + 4;
|
||||
#ifdef __builtin_object_size
|
||||
if (__builtin_object_size (p, 3) != sizeof (buf) - 8 - 4 * x)
|
||||
#else
|
||||
if (__builtin_object_size (p, 3) != 0)
|
||||
#endif
|
||||
abort ();
|
||||
}
|
||||
|
||||
|
@ -35,13 +35,14 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "stringpool.h"
|
||||
#include "attribs.h"
|
||||
#include "builtins.h"
|
||||
#include "gimplify-me.h"
|
||||
|
||||
struct object_size_info
|
||||
{
|
||||
int object_size_type;
|
||||
unsigned char pass;
|
||||
bool changed;
|
||||
bitmap visited, reexamine;
|
||||
bitmap visited, reexamine, unknowns;
|
||||
unsigned int *depths;
|
||||
unsigned int *stack, *tos;
|
||||
};
|
||||
@ -74,7 +75,11 @@ static void check_for_plus_in_loops_1 (struct object_size_info *, tree,
|
||||
object_sizes[1] is upper bound for the object size and number of bytes till
|
||||
the end of the subobject (innermost array or field with address taken).
|
||||
object_sizes[2] is lower bound for the object size and number of bytes till
|
||||
the end of the object and object_sizes[3] lower bound for subobject. */
|
||||
the end of the object and object_sizes[3] lower bound for subobject.
|
||||
|
||||
For static object sizes, the object size and the bytes till the end of the
|
||||
object are both INTEGER_CST. In the dynamic case, they are finally either a
|
||||
gimple variable or an INTEGER_CST. */
|
||||
static vec<object_size> object_sizes[OST_END];
|
||||
|
||||
/* Bitmaps what object sizes have been computed already. */
|
||||
@ -83,7 +88,16 @@ static bitmap computed[OST_END];
|
||||
/* Maximum value of offset we consider to be addition. */
|
||||
static unsigned HOST_WIDE_INT offset_limit;
|
||||
|
||||
/* Return true if VAL is represents an unknown size for OBJECT_SIZE_TYPE. */
|
||||
/* Return true if VAL represents an initial size for OBJECT_SIZE_TYPE. */
|
||||
|
||||
static inline bool
|
||||
size_initval_p (tree val, int object_size_type)
|
||||
{
|
||||
return ((object_size_type & OST_MINIMUM)
|
||||
? integer_all_onesp (val) : integer_zerop (val));
|
||||
}
|
||||
|
||||
/* Return true if VAL represents an unknown size for OBJECT_SIZE_TYPE. */
|
||||
|
||||
static inline bool
|
||||
size_unknown_p (tree val, int object_size_type)
|
||||
@ -92,6 +106,15 @@ size_unknown_p (tree val, int object_size_type)
|
||||
? integer_zerop (val) : integer_all_onesp (val));
|
||||
}
|
||||
|
||||
/* Return true if VAL is usable as an object size in the object_sizes
|
||||
vectors. */
|
||||
|
||||
static inline bool
|
||||
size_usable_p (tree val)
|
||||
{
|
||||
return TREE_CODE (val) == SSA_NAME || TREE_CODE (val) == INTEGER_CST;
|
||||
}
|
||||
|
||||
/* Return a tree with initial value for OBJECT_SIZE_TYPE. */
|
||||
|
||||
static inline tree
|
||||
@ -136,17 +159,43 @@ object_sizes_unknown_p (int object_size_type, unsigned varno)
|
||||
object_size_type);
|
||||
}
|
||||
|
||||
/* Return size for VARNO corresponding to OSI. If WHOLE is true, return the
|
||||
whole object size. */
|
||||
/* Return the raw size expression for VARNO corresponding to OSI. This returns
|
||||
the TREE_VEC as is and should only be used during gimplification. */
|
||||
|
||||
static inline object_size
|
||||
object_sizes_get_raw (struct object_size_info *osi, unsigned varno)
|
||||
{
|
||||
gcc_assert (osi->pass != 0);
|
||||
return object_sizes[osi->object_size_type][varno];
|
||||
}
|
||||
|
||||
/* Return a size tree for VARNO corresponding to OSI. If WHOLE is true, return
|
||||
the whole object size. Use this for building size expressions based on size
|
||||
of VARNO. */
|
||||
|
||||
static inline tree
|
||||
object_sizes_get (struct object_size_info *osi, unsigned varno,
|
||||
bool whole = false)
|
||||
{
|
||||
tree ret;
|
||||
int object_size_type = osi->object_size_type;
|
||||
|
||||
if (whole)
|
||||
return object_sizes[osi->object_size_type][varno].wholesize;
|
||||
ret = object_sizes[object_size_type][varno].wholesize;
|
||||
else
|
||||
return object_sizes[osi->object_size_type][varno].size;
|
||||
ret = object_sizes[object_size_type][varno].size;
|
||||
|
||||
if (object_size_type & OST_DYNAMIC)
|
||||
{
|
||||
if (TREE_CODE (ret) == MODIFY_EXPR)
|
||||
return TREE_OPERAND (ret, 0);
|
||||
else if (TREE_CODE (ret) == TREE_VEC)
|
||||
return TREE_VEC_ELT (ret, TREE_VEC_LENGTH (ret) - 1);
|
||||
else
|
||||
gcc_checking_assert (size_usable_p (ret));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set size for VARNO corresponding to OSI to VAL. */
|
||||
@ -161,28 +210,116 @@ object_sizes_initialize (struct object_size_info *osi, unsigned varno,
|
||||
object_sizes[object_size_type][varno].wholesize = wholeval;
|
||||
}
|
||||
|
||||
/* Set size for VARNO corresponding to OSI to VAL if it is the new minimum or
|
||||
maximum. */
|
||||
/* Return a MODIFY_EXPR for cases where SSA and EXPR have the same type. The
|
||||
TREE_VEC is returned only in case of PHI nodes. */
|
||||
|
||||
static inline bool
|
||||
static tree
|
||||
bundle_sizes (tree name, tree expr)
|
||||
{
|
||||
gcc_checking_assert (TREE_TYPE (name) == sizetype);
|
||||
|
||||
if (TREE_CODE (expr) == TREE_VEC)
|
||||
{
|
||||
TREE_VEC_ELT (expr, TREE_VEC_LENGTH (expr) - 1) = name;
|
||||
return expr;
|
||||
}
|
||||
|
||||
gcc_checking_assert (types_compatible_p (TREE_TYPE (expr), sizetype));
|
||||
return build2 (MODIFY_EXPR, sizetype, name, expr);
|
||||
}
|
||||
|
||||
/* Set size for VARNO corresponding to OSI to VAL if it is the new minimum or
|
||||
maximum. For static sizes, each element of TREE_VEC is always INTEGER_CST
|
||||
throughout the computation. For dynamic sizes, each element may either be a
|
||||
gimple variable, a MODIFY_EXPR or a TREE_VEC. The MODIFY_EXPR is for
|
||||
expressions that need to be gimplified. TREE_VECs are special, they're
|
||||
emitted only for GIMPLE_PHI and the PHI result variable is the last element
|
||||
of the vector. */
|
||||
|
||||
static bool
|
||||
object_sizes_set (struct object_size_info *osi, unsigned varno, tree val,
|
||||
tree wholeval)
|
||||
{
|
||||
int object_size_type = osi->object_size_type;
|
||||
object_size osize = object_sizes[object_size_type][varno];
|
||||
bool changed = true;
|
||||
|
||||
tree oldval = osize.size;
|
||||
tree old_wholeval = osize.wholesize;
|
||||
|
||||
enum tree_code code = object_size_type & OST_MINIMUM ? MIN_EXPR : MAX_EXPR;
|
||||
if (object_size_type & OST_DYNAMIC)
|
||||
{
|
||||
if (bitmap_bit_p (osi->reexamine, varno))
|
||||
{
|
||||
if (size_unknown_p (val, object_size_type))
|
||||
{
|
||||
oldval = object_sizes_get (osi, varno);
|
||||
old_wholeval = object_sizes_get (osi, varno, true);
|
||||
bitmap_set_bit (osi->unknowns, SSA_NAME_VERSION (oldval));
|
||||
bitmap_set_bit (osi->unknowns, SSA_NAME_VERSION (old_wholeval));
|
||||
bitmap_clear_bit (osi->reexamine, varno);
|
||||
}
|
||||
else
|
||||
{
|
||||
val = bundle_sizes (oldval, val);
|
||||
wholeval = bundle_sizes (old_wholeval, wholeval);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gcc_checking_assert (size_initval_p (oldval, object_size_type));
|
||||
gcc_checking_assert (size_initval_p (old_wholeval,
|
||||
object_size_type));
|
||||
/* For dynamic object sizes, all object sizes that are not gimple
|
||||
variables will need to be gimplified. */
|
||||
if (wholeval != val && !size_usable_p (wholeval))
|
||||
{
|
||||
bitmap_set_bit (osi->reexamine, varno);
|
||||
wholeval = bundle_sizes (make_ssa_name (sizetype), wholeval);
|
||||
}
|
||||
if (!size_usable_p (val))
|
||||
{
|
||||
bitmap_set_bit (osi->reexamine, varno);
|
||||
tree newval = bundle_sizes (make_ssa_name (sizetype), val);
|
||||
if (val == wholeval)
|
||||
wholeval = newval;
|
||||
val = newval;
|
||||
}
|
||||
/* If the new value is a temporary variable, mark it for
|
||||
reexamination. */
|
||||
else if (TREE_CODE (val) == SSA_NAME && !SSA_NAME_DEF_STMT (val))
|
||||
bitmap_set_bit (osi->reexamine, varno);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
enum tree_code code = (object_size_type & OST_MINIMUM
|
||||
? MIN_EXPR : MAX_EXPR);
|
||||
|
||||
val = size_binop (code, val, oldval);
|
||||
wholeval = size_binop (code, wholeval, old_wholeval);
|
||||
val = size_binop (code, val, oldval);
|
||||
wholeval = size_binop (code, wholeval, old_wholeval);
|
||||
changed = (tree_int_cst_compare (val, oldval) != 0
|
||||
|| tree_int_cst_compare (old_wholeval, wholeval) != 0);
|
||||
}
|
||||
|
||||
object_sizes[object_size_type][varno].size = val;
|
||||
object_sizes[object_size_type][varno].wholesize = wholeval;
|
||||
return (tree_int_cst_compare (oldval, val) != 0
|
||||
|| tree_int_cst_compare (old_wholeval, wholeval) != 0);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
/* Set temporary SSA names for object size and whole size to resolve dependency
|
||||
loops in dynamic size computation. */
|
||||
|
||||
static inline void
|
||||
object_sizes_set_temp (struct object_size_info *osi, unsigned varno)
|
||||
{
|
||||
tree val = object_sizes_get (osi, varno);
|
||||
|
||||
if (size_initval_p (val, osi->object_size_type))
|
||||
object_sizes_set (osi, varno,
|
||||
make_ssa_name (sizetype),
|
||||
make_ssa_name (sizetype));
|
||||
}
|
||||
|
||||
/* Initialize OFFSET_LIMIT variable. */
|
||||
@ -204,14 +341,15 @@ static tree
|
||||
size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE)
|
||||
{
|
||||
gcc_checking_assert (TREE_CODE (offset) == INTEGER_CST);
|
||||
gcc_checking_assert (TREE_CODE (sz) == INTEGER_CST);
|
||||
gcc_checking_assert (types_compatible_p (TREE_TYPE (sz), sizetype));
|
||||
|
||||
/* For negative offsets, if we have a distinct WHOLESIZE, use it to get a net
|
||||
offset from the whole object. */
|
||||
if (wholesize && tree_int_cst_compare (sz, wholesize))
|
||||
if (wholesize && wholesize != sz
|
||||
&& (TREE_CODE (sz) != INTEGER_CST
|
||||
|| TREE_CODE (wholesize) != INTEGER_CST
|
||||
|| tree_int_cst_compare (sz, wholesize)))
|
||||
{
|
||||
gcc_checking_assert (TREE_CODE (wholesize) == INTEGER_CST);
|
||||
gcc_checking_assert (types_compatible_p (TREE_TYPE (wholesize),
|
||||
sizetype));
|
||||
|
||||
@ -228,13 +366,16 @@ size_for_offset (tree sz, tree offset, tree wholesize = NULL_TREE)
|
||||
if (!types_compatible_p (TREE_TYPE (offset), sizetype))
|
||||
fold_convert (sizetype, offset);
|
||||
|
||||
if (integer_zerop (offset))
|
||||
return sz;
|
||||
if (TREE_CODE (offset) == INTEGER_CST)
|
||||
{
|
||||
if (integer_zerop (offset))
|
||||
return sz;
|
||||
|
||||
/* Negative or too large offset even after adjustment, cannot be within
|
||||
bounds of an object. */
|
||||
if (compare_tree_int (offset, offset_limit) > 0)
|
||||
return size_zero_node;
|
||||
/* Negative or too large offset even after adjustment, cannot be within
|
||||
bounds of an object. */
|
||||
if (compare_tree_int (offset, offset_limit) > 0)
|
||||
return size_zero_node;
|
||||
}
|
||||
|
||||
return size_binop (MINUS_EXPR, size_binop (MAX_EXPR, sz, offset), offset);
|
||||
}
|
||||
@ -671,6 +812,201 @@ pass_through_call (const gcall *call)
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Emit PHI nodes for size expressions fo. */
|
||||
|
||||
static void
|
||||
emit_phi_nodes (gimple *stmt, tree size, tree wholesize)
|
||||
{
|
||||
tree phires;
|
||||
gphi *wholephi = NULL;
|
||||
|
||||
if (wholesize != size)
|
||||
{
|
||||
phires = TREE_VEC_ELT (wholesize, TREE_VEC_LENGTH (wholesize) - 1);
|
||||
wholephi = create_phi_node (phires, gimple_bb (stmt));
|
||||
}
|
||||
|
||||
phires = TREE_VEC_ELT (size, TREE_VEC_LENGTH (size) - 1);
|
||||
gphi *phi = create_phi_node (phires, gimple_bb (stmt));
|
||||
gphi *obj_phi = as_a <gphi *> (stmt);
|
||||
|
||||
gcc_checking_assert (TREE_CODE (wholesize) == TREE_VEC);
|
||||
gcc_checking_assert (TREE_CODE (size) == TREE_VEC);
|
||||
|
||||
for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++)
|
||||
{
|
||||
gimple_seq seq = NULL;
|
||||
tree wsz = TREE_VEC_ELT (wholesize, i);
|
||||
tree sz = TREE_VEC_ELT (size, i);
|
||||
|
||||
/* If we built an expression, we will need to build statements
|
||||
and insert them on the edge right away. */
|
||||
if (TREE_CODE (wsz) != SSA_NAME)
|
||||
wsz = force_gimple_operand (wsz, &seq, true, NULL);
|
||||
if (TREE_CODE (sz) != SSA_NAME)
|
||||
{
|
||||
gimple_seq s;
|
||||
sz = force_gimple_operand (sz, &s, true, NULL);
|
||||
gimple_seq_add_seq (&seq, s);
|
||||
}
|
||||
|
||||
if (seq)
|
||||
gsi_insert_seq_on_edge (gimple_phi_arg_edge (obj_phi, i), seq);
|
||||
|
||||
if (wholephi)
|
||||
add_phi_arg (wholephi, wsz,
|
||||
gimple_phi_arg_edge (obj_phi, i),
|
||||
gimple_phi_arg_location (obj_phi, i));
|
||||
|
||||
add_phi_arg (phi, sz,
|
||||
gimple_phi_arg_edge (obj_phi, i),
|
||||
gimple_phi_arg_location (obj_phi, i));
|
||||
}
|
||||
}
|
||||
|
||||
/* Descend through EXPR and return size_unknown if it uses any SSA variable
|
||||
object_size_set or object_size_set_temp generated, which turned out to be
|
||||
size_unknown, as noted in UNKNOWNS. */
|
||||
|
||||
static tree
|
||||
propagate_unknowns (object_size_info *osi, tree expr)
|
||||
{
|
||||
int object_size_type = osi->object_size_type;
|
||||
|
||||
switch (TREE_CODE (expr))
|
||||
{
|
||||
case SSA_NAME:
|
||||
if (bitmap_bit_p (osi->unknowns, SSA_NAME_VERSION (expr)))
|
||||
return size_unknown (object_size_type);
|
||||
return expr;
|
||||
|
||||
case MIN_EXPR:
|
||||
case MAX_EXPR:
|
||||
{
|
||||
tree res = propagate_unknowns (osi, TREE_OPERAND (expr, 0));
|
||||
if (size_unknown_p (res, object_size_type))
|
||||
return res;
|
||||
|
||||
res = propagate_unknowns (osi, TREE_OPERAND (expr, 1));
|
||||
if (size_unknown_p (res, object_size_type))
|
||||
return res;
|
||||
|
||||
return expr;
|
||||
}
|
||||
case MODIFY_EXPR:
|
||||
{
|
||||
tree res = propagate_unknowns (osi, TREE_OPERAND (expr, 1));
|
||||
if (size_unknown_p (res, object_size_type))
|
||||
return res;
|
||||
return expr;
|
||||
}
|
||||
case TREE_VEC:
|
||||
for (int i = 0; i < TREE_VEC_LENGTH (expr); i++)
|
||||
{
|
||||
tree res = propagate_unknowns (osi, TREE_VEC_ELT (expr, i));
|
||||
if (size_unknown_p (res, object_size_type))
|
||||
return res;
|
||||
}
|
||||
return expr;
|
||||
case PLUS_EXPR:
|
||||
case MINUS_EXPR:
|
||||
{
|
||||
tree res = propagate_unknowns (osi, TREE_OPERAND (expr, 0));
|
||||
if (size_unknown_p (res, object_size_type))
|
||||
return res;
|
||||
|
||||
return expr;
|
||||
}
|
||||
default:
|
||||
return expr;
|
||||
}
|
||||
}
|
||||
|
||||
/* Walk through size expressions that need reexamination and generate
|
||||
statements for them. */
|
||||
|
||||
static void
|
||||
gimplify_size_expressions (object_size_info *osi)
|
||||
{
|
||||
int object_size_type = osi->object_size_type;
|
||||
bitmap_iterator bi;
|
||||
unsigned int i;
|
||||
bool changed;
|
||||
|
||||
/* Step 1: Propagate unknowns into expressions. */
|
||||
bitmap reexamine = BITMAP_ALLOC (NULL);
|
||||
bitmap_copy (reexamine, osi->reexamine);
|
||||
do
|
||||
{
|
||||
changed = false;
|
||||
EXECUTE_IF_SET_IN_BITMAP (reexamine, 0, i, bi)
|
||||
{
|
||||
object_size cur = object_sizes_get_raw (osi, i);
|
||||
|
||||
if (size_unknown_p (propagate_unknowns (osi, cur.size),
|
||||
object_size_type)
|
||||
|| size_unknown_p (propagate_unknowns (osi, cur.wholesize),
|
||||
object_size_type))
|
||||
{
|
||||
object_sizes_set (osi, i,
|
||||
size_unknown (object_size_type),
|
||||
size_unknown (object_size_type));
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
bitmap_copy (reexamine, osi->reexamine);
|
||||
}
|
||||
while (changed);
|
||||
|
||||
/* Release all unknowns. */
|
||||
EXECUTE_IF_SET_IN_BITMAP (osi->unknowns, 0, i, bi)
|
||||
release_ssa_name (ssa_name (i));
|
||||
|
||||
/* Expand all size expressions to put their definitions close to the objects
|
||||
for which size is being computed. */
|
||||
EXECUTE_IF_SET_IN_BITMAP (osi->reexamine, 0, i, bi)
|
||||
{
|
||||
gimple_seq seq = NULL;
|
||||
object_size osize = object_sizes_get_raw (osi, i);
|
||||
|
||||
gimple *stmt = SSA_NAME_DEF_STMT (ssa_name (i));
|
||||
enum gimple_code code = gimple_code (stmt);
|
||||
|
||||
/* PHI nodes need special attention. */
|
||||
if (code == GIMPLE_PHI)
|
||||
emit_phi_nodes (stmt, osize.size, osize.wholesize);
|
||||
else
|
||||
{
|
||||
tree size_expr = NULL_TREE;
|
||||
|
||||
/* Bundle wholesize in with the size to gimplify if needed. */
|
||||
if (osize.wholesize != osize.size
|
||||
&& !size_usable_p (osize.wholesize))
|
||||
size_expr = size_binop (COMPOUND_EXPR,
|
||||
osize.wholesize,
|
||||
osize.size);
|
||||
else if (!size_usable_p (osize.size))
|
||||
size_expr = osize.size;
|
||||
|
||||
if (size_expr)
|
||||
{
|
||||
gimple_stmt_iterator gsi;
|
||||
if (code == GIMPLE_NOP)
|
||||
gsi = gsi_start_bb (single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)));
|
||||
else
|
||||
gsi = gsi_for_stmt (stmt);
|
||||
|
||||
force_gimple_operand (size_expr, &seq, true, NULL);
|
||||
gsi_insert_seq_before (&gsi, seq, GSI_CONTINUE_LINKING);
|
||||
}
|
||||
}
|
||||
|
||||
/* We're done, so replace the MODIFY_EXPRs with the SSA names. */
|
||||
object_sizes_initialize (osi, i,
|
||||
object_sizes_get (osi, i),
|
||||
object_sizes_get (osi, i, true));
|
||||
}
|
||||
}
|
||||
|
||||
/* Compute __builtin_object_size value for PTR and set *PSIZE to
|
||||
the resulting value. If the declared object is known and PDECL
|
||||
@ -749,9 +1085,15 @@ compute_builtin_object_size (tree ptr, int object_size_type,
|
||||
|
||||
osi.visited = BITMAP_ALLOC (NULL);
|
||||
osi.reexamine = BITMAP_ALLOC (NULL);
|
||||
osi.depths = NULL;
|
||||
osi.stack = NULL;
|
||||
osi.tos = NULL;
|
||||
|
||||
if (object_size_type & OST_DYNAMIC)
|
||||
osi.unknowns = BITMAP_ALLOC (NULL);
|
||||
else
|
||||
{
|
||||
osi.depths = NULL;
|
||||
osi.stack = NULL;
|
||||
osi.tos = NULL;
|
||||
}
|
||||
|
||||
/* First pass: walk UD chains, compute object sizes that
|
||||
can be computed. osi.reexamine bitmap at the end will
|
||||
@ -761,6 +1103,14 @@ compute_builtin_object_size (tree ptr, int object_size_type,
|
||||
osi.changed = false;
|
||||
collect_object_sizes_for (&osi, ptr);
|
||||
|
||||
if (object_size_type & OST_DYNAMIC)
|
||||
{
|
||||
osi.pass = 1;
|
||||
gimplify_size_expressions (&osi);
|
||||
BITMAP_FREE (osi.unknowns);
|
||||
bitmap_clear (osi.reexamine);
|
||||
}
|
||||
|
||||
/* Second pass: keep recomputing object sizes of variables
|
||||
that need reexamination, until no object sizes are
|
||||
increased or all object sizes are computed. */
|
||||
@ -1004,6 +1354,28 @@ plus_stmt_object_size (struct object_size_info *osi, tree var, gimple *stmt)
|
||||
return reexamine;
|
||||
}
|
||||
|
||||
/* Compute the dynamic object size for VAR. Return the result in SIZE and
|
||||
WHOLESIZE. */
|
||||
|
||||
static void
|
||||
dynamic_object_size (struct object_size_info *osi, tree var,
|
||||
tree *size, tree *wholesize)
|
||||
{
|
||||
int object_size_type = osi->object_size_type;
|
||||
|
||||
if (TREE_CODE (var) == SSA_NAME)
|
||||
{
|
||||
unsigned varno = SSA_NAME_VERSION (var);
|
||||
|
||||
collect_object_sizes_for (osi, var);
|
||||
*size = object_sizes_get (osi, varno);
|
||||
*wholesize = object_sizes_get (osi, varno, true);
|
||||
}
|
||||
else if (TREE_CODE (var) == ADDR_EXPR)
|
||||
addr_object_size (osi, var, object_size_type, size, wholesize);
|
||||
else
|
||||
*size = *wholesize = size_unknown (object_size_type);
|
||||
}
|
||||
|
||||
/* Compute object_sizes for VAR, defined at STMT, which is
|
||||
a COND_EXPR. Return true if the object size might need reexamination
|
||||
@ -1025,6 +1397,33 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
|
||||
then_ = gimple_assign_rhs2 (stmt);
|
||||
else_ = gimple_assign_rhs3 (stmt);
|
||||
|
||||
if (object_size_type & OST_DYNAMIC)
|
||||
{
|
||||
tree then_size, then_wholesize, else_size, else_wholesize;
|
||||
|
||||
dynamic_object_size (osi, then_, &then_size, &then_wholesize);
|
||||
if (!size_unknown_p (then_size, object_size_type))
|
||||
dynamic_object_size (osi, else_, &else_size, &else_wholesize);
|
||||
|
||||
tree cond_size, cond_wholesize;
|
||||
if (size_unknown_p (then_size, object_size_type)
|
||||
|| size_unknown_p (else_size, object_size_type))
|
||||
cond_size = cond_wholesize = size_unknown (object_size_type);
|
||||
else
|
||||
{
|
||||
cond_size = fold_build3 (COND_EXPR, sizetype,
|
||||
gimple_assign_rhs1 (stmt),
|
||||
then_size, else_size);
|
||||
cond_wholesize = fold_build3 (COND_EXPR, sizetype,
|
||||
gimple_assign_rhs1 (stmt),
|
||||
then_wholesize, else_wholesize);
|
||||
}
|
||||
|
||||
object_sizes_set (osi, varno, cond_size, cond_wholesize);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TREE_CODE (then_) == SSA_NAME)
|
||||
reexamine |= merge_object_sizes (osi, var, then_);
|
||||
else
|
||||
@ -1041,6 +1440,64 @@ cond_expr_object_size (struct object_size_info *osi, tree var, gimple *stmt)
|
||||
return reexamine;
|
||||
}
|
||||
|
||||
/* Compute an object size expression for VAR, which is the result of a PHI
|
||||
node. */
|
||||
|
||||
static void
|
||||
phi_dynamic_object_size (struct object_size_info *osi, tree var)
|
||||
{
|
||||
int object_size_type = osi->object_size_type;
|
||||
unsigned int varno = SSA_NAME_VERSION (var);
|
||||
gimple *stmt = SSA_NAME_DEF_STMT (var);
|
||||
unsigned i, num_args = gimple_phi_num_args (stmt);
|
||||
bool wholesize_needed = false;
|
||||
|
||||
/* The extra space is for the PHI result at the end, which object_sizes_set
|
||||
sets for us. */
|
||||
tree sizes = make_tree_vec (num_args + 1);
|
||||
tree wholesizes = make_tree_vec (num_args + 1);
|
||||
|
||||
/* Bail out if the size of any of the PHI arguments cannot be
|
||||
determined. */
|
||||
for (i = 0; i < num_args; i++)
|
||||
{
|
||||
edge e = gimple_phi_arg_edge (as_a <gphi *> (stmt), i);
|
||||
if (e->flags & EDGE_COMPLEX)
|
||||
break;
|
||||
|
||||
tree rhs = gimple_phi_arg_def (stmt, i);
|
||||
tree size, wholesize;
|
||||
|
||||
dynamic_object_size (osi, rhs, &size, &wholesize);
|
||||
|
||||
if (size_unknown_p (size, object_size_type))
|
||||
break;
|
||||
|
||||
if (size != wholesize)
|
||||
wholesize_needed = true;
|
||||
|
||||
TREE_VEC_ELT (sizes, i) = size;
|
||||
TREE_VEC_ELT (wholesizes, i) = wholesize;
|
||||
}
|
||||
|
||||
if (i < num_args)
|
||||
{
|
||||
ggc_free (sizes);
|
||||
ggc_free (wholesizes);
|
||||
sizes = wholesizes = size_unknown (object_size_type);
|
||||
}
|
||||
|
||||
/* Point to the same TREE_VEC so that we can avoid emitting two PHI
|
||||
nodes. */
|
||||
else if (!wholesize_needed)
|
||||
{
|
||||
ggc_free (wholesizes);
|
||||
wholesizes = sizes;
|
||||
}
|
||||
|
||||
object_sizes_set (osi, varno, sizes, wholesizes);
|
||||
}
|
||||
|
||||
/* Compute object sizes for VAR.
|
||||
For ADDR_EXPR an object size is the number of remaining bytes
|
||||
to the end of the object (where what is considered an object depends on
|
||||
@ -1086,6 +1543,9 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
|
||||
{
|
||||
/* Found a dependency loop. Mark the variable for later
|
||||
re-examination. */
|
||||
if (object_size_type & OST_DYNAMIC)
|
||||
object_sizes_set_temp (osi, varno);
|
||||
|
||||
bitmap_set_bit (osi->reexamine, varno);
|
||||
if (dump_file && (dump_flags & TDF_DETAILS))
|
||||
{
|
||||
@ -1167,6 +1627,12 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
if (object_size_type & OST_DYNAMIC)
|
||||
{
|
||||
phi_dynamic_object_size (osi, var);
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < gimple_phi_num_args (stmt); i++)
|
||||
{
|
||||
tree rhs = gimple_phi_arg (stmt, i)->def;
|
||||
@ -1189,7 +1655,8 @@ collect_object_sizes_for (struct object_size_info *osi, tree var)
|
||||
if (! reexamine || object_sizes_unknown_p (object_size_type, varno))
|
||||
{
|
||||
bitmap_set_bit (computed[object_size_type], varno);
|
||||
bitmap_clear_bit (osi->reexamine, varno);
|
||||
if (!(object_size_type & OST_DYNAMIC))
|
||||
bitmap_clear_bit (osi->reexamine, varno);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user