gimple-fold.c (gimple_fold_builtin_strlen): Use set_strlen_range rather than set_range_info.

* gimple-fold.c (gimple_fold_builtin_strlen): Use set_strlen_range
	rather than set_range_info.
	* tree-ssa-strlen.c (set_strlen_range): Extracted from
	maybe_set_strlen_range.  Handle potentially boundary crossing
	cases more conservatively.
	(maybe_set_strlen_range): Parts refactored into set_strlen_range.
	Call set_strlen_range.
	* tree-ssa-strlen.h (set_strlen_range): Add prototype.

	* gcc.dg/strlenopt-36.c: Update.
	* gcc.dg/strlenopt-45.c: Update.
	* gcc.c-torture/execute/strlen-5.c: New test.
	* gcc.c-torture/execute/strlen-6.c: New test.
	* gcc.c-torture/execute/strlen-7.c: New test.

Co-Authored-By: Jeff Law <law@redhat.com>

From-SVN: r267531
This commit is contained in:
Martin Sebor 2019-01-02 21:38:56 +00:00 committed by Jeff Law
parent ec1faddf89
commit d4bf69750d
10 changed files with 925 additions and 227 deletions

View File

@ -1,6 +1,16 @@
2019-01-02 Martin Sebor <msebor@redhat.com>
Jeff Law <law@redhat.com>
* gimple-fold.c (gimple_fold_builtin_strlen): Use set_strlen_range
rather than set_range_info.
* tree-ssa-strlen.c (set_strlen_range): Extracted from
maybe_set_strlen_range. Handle potentially boundary crossing
cases more conservatively.
(maybe_set_strlen_range): Parts refactored into set_strlen_range.
Call set_strlen_range.
* tree-ssa-strlen.h (set_strlen_range): Add prototype.
PR middle-end/88663
* gimple-fold.c (get_range_strlen): Update prototype to no longer
need the flexp argument.

View File

@ -3739,10 +3739,9 @@ gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi)
return true;
}
/* Set the strlen() range to [0, MAXLEN]. */
if (tree lhs = gimple_call_lhs (stmt))
if (TREE_CODE (lhs) == SSA_NAME
&& INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
set_range_info (lhs, VR_RANGE, minlen, maxlen);
set_strlen_range (lhs, maxlen);
return false;
}

View File

@ -1,3 +1,12 @@
2019-01-02 Martin Sebor <msebor@redhat.com>
Jeff Law <law@redhat.com>
* gcc.dg/strlenopt-36.c: Update.
* gcc.dg/strlenopt-45.c: Update.
* gcc.c-torture/execute/strlen-5.c: New test.
* gcc.c-torture/execute/strlen-6.c: New test.
* gcc.c-torture/execute/strlen-7.c: New test.
2019-01-02 Jakub Jelinek <jakub@redhat.com>
PR testsuite/87304

View File

@ -0,0 +1,653 @@
/* Test to verify that even strictly undefined strlen() calls with
unterminated character arrays yield the "expected" results when
the terminating nul is present in a subsequent suobobject. */
extern __SIZE_TYPE__ strlen (const char *);
unsigned nfails;
#define A(expr, N) \
do { \
const char *s = (expr); \
unsigned n = strlen (s); \
((n == N) \
? 0 \
: (__builtin_printf ("line %i: strlen (%s = \"%s\")" \
" == %u failed\n", \
__LINE__, #expr, s, N), \
++nfails)); \
} while (0)
int idx;
const char ca[][4] = {
{ '1', '2', '3', '4' }, { '5' },
{ '1', '2', '3', '4' }, { '5', '6' },
{ '1', '2', '3', '4' }, { '5', '6', '7' },
{ '1', '2', '3', '4' }, { '5', '6', '7', '8' },
{ '9' }
};
static void test_const_global_arrays (void)
{
A (ca[0], 5);
A (&ca[0][0], 5);
A (&ca[0][1], 4);
A (&ca[0][3], 2);
int i = 0;
A (ca[i], 5);
A (&ca[i][0], 5);
A (&ca[i][1], 4);
A (&ca[i][3], 2);
int j = i;
A (&ca[i][i], 5);
A (&ca[i][j + 1], 4);
A (&ca[i][j + 2], 3);
A (&ca[idx][i], 5);
A (&ca[idx][j + 1], 4);
A (&ca[idx][j + 2], 3);
A (&ca[idx][idx], 5);
A (&ca[idx][idx + 1], 4);
A (&ca[idx][idx + 2], 3);
A (&ca[0][++j], 4);
A (&ca[0][++j], 3);
A (&ca[0][++j], 2);
if (j != 3)
++nfails;
}
static void test_const_local_arrays (void)
{
const char a[][4] = {
{ '1', '2', '3', '4' }, { '5' },
{ '1', '2', '3', '4' }, { '5', '6' },
{ '1', '2', '3', '4' }, { '5', '6', '7' },
{ '1', '2', '3', '4' }, { '5', '6', '7', '8' },
{ '9' }
};
A (a[0], 5);
A (&a[0][0], 5);
A (&a[0][1], 4);
A (&a[0][3], 2);
int i = 0;
A (a[i], 5);
A (&a[i][0], 5);
A (&a[i][1], 4);
A (&a[i][3], 2);
int j = i;
A (&a[i][i], 5);
A (&a[i][j + 1], 4);
A (&a[i][j + 2], 3);
A (&a[idx][i], 5);
A (&a[idx][j + 1], 4);
A (&a[idx][j + 2], 3);
A (&a[idx][idx], 5);
A (&a[idx][idx + 1], 4);
A (&a[idx][idx + 2], 3);
A (&a[0][++j], 4);
A (&a[0][++j], 3);
A (&a[0][++j], 2);
if (j != 3)
++nfails;
}
char va[][4] = {
{ '1', '2', '3', '4' }, { '5' },
{ '1', '2', '3', '4' }, { '5', '6' },
{ '1', '2', '3', '4' }, { '5', '6', '7' },
{ '1', '2', '3', '4' }, { '5', '6', '7', '8' },
{ '9' }
};
static void test_nonconst_global_arrays (void)
{
{
A (va[0], 5);
A (&va[0][0], 5);
A (&va[0][1], 4);
A (&va[0][3], 2);
int i = 0;
A (va[i], 5);
A (&va[i][0], 5);
A (&va[i][1], 4);
A (&va[i][3], 2);
int j = i;
A (&va[i][i], 5);
A (&va[i][j + 1], 4);
A (&va[i][j + 2], 3);
A (&va[idx][i], 5);
A (&va[idx][j + 1], 4);
A (&va[idx][j + 2], 3);
A (&va[idx][idx], 5);
A (&va[idx][idx + 1], 4);
A (&va[idx][idx + 2], 3);
}
{
A (va[2], 6);
A (&va[2][0], 6);
A (&va[2][1], 5);
A (&va[2][3], 3);
int i = 2;
A (va[i], 6);
A (&va[i][0], 6);
A (&va[i][1], 5);
A (&va[i][3], 3);
int j = i - 1;
A (&va[i][j - 1], 6);
A (&va[i][j], 5);
A (&va[i][j + 1], 4);
A (&va[idx + 2][i - 1], 5);
A (&va[idx + 2][j], 5);
A (&va[idx + 2][j + 1], 4);
}
int j = 0;
A (&va[0][++j], 4);
A (&va[0][++j], 3);
A (&va[0][++j], 2);
if (j != 3)
++nfails;
}
static void test_nonconst_local_arrays (void)
{
char a[][4] = {
{ '1', '2', '3', '4' }, { '5' },
{ '1', '2', '3', '4' }, { '5', '6' },
{ '1', '2', '3', '4' }, { '5', '6', '7' },
{ '1', '2', '3', '4' }, { '5', '6', '7', '8' },
{ '9' }
};
A (a[0], 5);
A (&a[0][0], 5);
A (&a[0][1], 4);
A (&a[0][3], 2);
int i = 0;
A (a[i], 5);
A (&a[i][0], 5);
A (&a[i][1], 4);
A (&a[i][3], 2);
int j = i;
A (&a[i][i], 5);
A (&a[i][j + 1], 4);
A (&a[i][j + 2], 3);
A (&a[idx][i], 5);
A (&a[idx][j + 1], 4);
A (&a[idx][j + 2], 3);
A (&a[idx][idx], 5);
A (&a[idx][idx + 1], 4);
A (&a[idx][idx + 2], 3);
A (&a[0][++j], 4);
A (&a[0][++j], 3);
A (&a[0][++j], 2);
if (j != 3)
++nfails;
}
struct MemArrays { char a[4], b[4]; };
const struct MemArrays cma[] = {
{ { '1', '2', '3', '4' }, { '5' } },
{ { '1', '2', '3', '4' }, { '5', '6' } },
{ { '1', '2', '3', '4' }, { '5', '6' } },
{ { '1', '2', '3', '4' }, { '5', '6', '7' } },
{ { '1', '2', '3', '4' }, { '5', '6', '7', '8' } },
{ { '9' }, { '\0' } }
};
static void test_const_global_member_arrays (void)
{
{
A (cma[0].a, 5);
A (&cma[0].a[0], 5);
A (&cma[0].a[1], 4);
A (&cma[0].a[2], 3);
int i = 0;
A (cma[i].a, 5);
A (&cma[i].a[0], 5);
A (&cma[i].a[1], 4);
A (&cma[i].a[2], 3);
int j = i;
A (&cma[i].a[j], 5);
A (&cma[i].a[j + 1], 4);
A (&cma[i].a[j + 2], 3);
A (&cma[idx].a[i], 5);
A (&cma[idx].a[j + 1], 4);
A (&cma[idx].a[j + 2], 3);
A (&cma[idx].a[idx], 5);
A (&cma[idx].a[idx + 1], 4);
A (&cma[idx].a[idx + 2], 3);
}
{
A (cma[1].a, 6);
A (&cma[1].a[0], 6);
A (&cma[1].a[1], 5);
A (&cma[1].a[2], 4);
int i = 1;
A (cma[i].a, 6);
A (&cma[i].a[0], 6);
A (&cma[i].a[1], 5);
A (&cma[i].a[2], 4);
int j = i - 1;
A (&cma[i].a[j], 6);
A (&cma[i].a[j + 1], 5);
A (&cma[i].a[j + 2], 4);
A (&cma[idx + 1].a[j], 6);
A (&cma[idx + 1].a[j + 1], 5);
A (&cma[idx + 1].a[j + 2], 4);
A (&cma[idx + 1].a[idx], 6);
A (&cma[idx + 1].a[idx + 1], 5);
A (&cma[idx + 1].a[idx + 2], 4);
}
{
A (cma[4].a, 9);
A (&cma[4].a[0], 9);
A (&cma[4].a[1], 8);
A (&cma[4].b[0], 5);
int i = 4;
A (cma[i].a, 9);
A (&cma[i].a[0], 9);
A (&cma[i].a[1], 8);
A (&cma[i].b[0], 5);
int j = i - 1;
A (&cma[i].a[j], 6);
A (&cma[i].a[j + 1], 5);
A (&cma[i].b[j - 2], 4);
A (&cma[idx + 4].a[j], 6);
A (&cma[idx + 4].a[j + 1], 5);
A (&cma[idx + 4].b[j - 2], 4);
A (&cma[idx + 4].a[idx], 9);
A (&cma[idx + 4].a[idx + 1], 8);
A (&cma[idx + 4].b[idx + 1], 4);
}
}
static void test_const_local_member_arrays (void)
{
const struct MemArrays ma[] = {
{ { '1', '2', '3', '4' }, { '5' } },
{ { '1', '2', '3', '4' }, { '5', '6' } },
{ { '1', '2', '3', '4' }, { '5', '6' } },
{ { '1', '2', '3', '4' }, { '5', '6', '7' } },
{ { '1', '2', '3', '4' }, { '5', '6', '7', '8' } },
{ { '9' }, { '\0' } }
};
{
A (ma[0].a, 5);
A (&ma[0].a[0], 5);
A (&ma[0].a[1], 4);
A (&ma[0].a[2], 3);
int i = 0;
A (ma[i].a, 5);
A (&ma[i].a[0], 5);
A (&ma[i].a[1], 4);
A (&ma[i].a[2], 3);
int j = i;
A (&ma[i].a[j], 5);
A (&ma[i].a[j + 1], 4);
A (&ma[i].a[j + 2], 3);
A (&ma[idx].a[i], 5);
A (&ma[idx].a[j + 1], 4);
A (&ma[idx].a[j + 2], 3);
A (&ma[idx].a[idx], 5);
A (&ma[idx].a[idx + 1], 4);
A (&ma[idx].a[idx + 2], 3);
}
{
A (ma[1].a, 6);
A (&ma[1].a[0], 6);
A (&ma[1].a[1], 5);
A (&ma[1].a[2], 4);
int i = 1;
A (ma[i].a, 6);
A (&ma[i].a[0], 6);
A (&ma[i].a[1], 5);
A (&ma[i].a[2], 4);
int j = i - 1;
A (&ma[i].a[j], 6);
A (&ma[i].a[j + 1], 5);
A (&ma[i].a[j + 2], 4);
A (&ma[idx + 1].a[j], 6);
A (&ma[idx + 1].a[j + 1], 5);
A (&ma[idx + 1].a[j + 2], 4);
A (&ma[idx + 1].a[idx], 6);
A (&ma[idx + 1].a[idx + 1], 5);
A (&ma[idx + 1].a[idx + 2], 4);
}
{
A (ma[4].a, 9);
A (&ma[4].a[0], 9);
A (&ma[4].a[1], 8);
A (&ma[4].b[0], 5);
int i = 4;
A (ma[i].a, 9);
A (&ma[i].a[0], 9);
A (&ma[i].a[1], 8);
A (&ma[i].b[0], 5);
int j = i - 1;
A (&ma[i].a[j], 6);
A (&ma[i].a[j + 1], 5);
A (&ma[i].b[j - 2], 4);
A (&ma[idx + 4].a[j], 6);
A (&ma[idx + 4].a[j + 1], 5);
A (&ma[idx + 4].b[j - 2], 4);
A (&ma[idx + 4].a[idx], 9);
A (&ma[idx + 4].a[idx + 1], 8);
A (&ma[idx + 4].b[idx + 1], 4);
}
}
struct MemArrays vma[] = {
{ { '1', '2', '3', '4' }, { '5' } },
{ { '1', '2', '3', '4' }, { '5', '6' } },
{ { '1', '2', '3', '4' }, { '5', '6' } },
{ { '1', '2', '3', '4' }, { '5', '6', '7' } },
{ { '1', '2', '3', '4' }, { '5', '6', '7', '8' } },
{ { '9' }, { '\0' } }
};
static void test_nonconst_global_member_arrays (void)
{
{
A (vma[0].a, 5);
A (&vma[0].a[0], 5);
A (&vma[0].a[1], 4);
A (&vma[0].a[2], 3);
int i = 0;
A (vma[i].a, 5);
A (&vma[i].a[0], 5);
A (&vma[i].a[1], 4);
A (&vma[i].a[2], 3);
int j = i;
A (&vma[i].a[j], 5);
A (&vma[i].a[j + 1], 4);
A (&vma[i].a[j + 2], 3);
A (&vma[idx].a[i], 5);
A (&vma[idx].a[j + 1], 4);
A (&vma[idx].a[j + 2], 3);
A (&vma[idx].a[idx], 5);
A (&vma[idx].a[idx + 1], 4);
A (&vma[idx].a[idx + 2], 3);
}
{
A (vma[1].a, 6);
A (&vma[1].a[0], 6);
A (&vma[1].a[1], 5);
A (&vma[1].a[2], 4);
int i = 1;
A (vma[i].a, 6);
A (&vma[i].a[0], 6);
A (&vma[i].a[1], 5);
A (&vma[i].a[2], 4);
int j = i - 1;
A (&vma[i].a[j], 6);
A (&vma[i].a[j + 1], 5);
A (&vma[i].a[j + 2], 4);
A (&vma[idx + 1].a[j], 6);
A (&vma[idx + 1].a[j + 1], 5);
A (&vma[idx + 1].a[j + 2], 4);
A (&vma[idx + 1].a[idx], 6);
A (&vma[idx + 1].a[idx + 1], 5);
A (&vma[idx + 1].a[idx + 2], 4);
}
{
A (vma[4].a, 9);
A (&vma[4].a[0], 9);
A (&vma[4].a[1], 8);
A (&vma[4].b[0], 5);
int i = 4;
A (vma[i].a, 9);
A (&vma[i].a[0], 9);
A (&vma[i].a[1], 8);
A (&vma[i].b[0], 5);
int j = i - 1;
A (&vma[i].a[j], 6);
A (&vma[i].a[j + 1], 5);
A (&vma[i].b[j - 2], 4);
A (&vma[idx + 4].a[j], 6);
A (&vma[idx + 4].a[j + 1], 5);
A (&vma[idx + 4].b[j - 2], 4);
A (&vma[idx + 4].a[idx], 9);
A (&vma[idx + 4].a[idx + 1], 8);
A (&vma[idx + 4].b[idx + 1], 4);
}
}
static void test_nonconst_local_member_arrays (void)
{
struct MemArrays ma[] = {
{ { '1', '2', '3', '4' }, { '5' } },
{ { '1', '2', '3', '4' }, { '5', '6' } },
{ { '1', '2', '3', '4' }, { '5', '6' } },
{ { '1', '2', '3', '4' }, { '5', '6', '7' } },
{ { '1', '2', '3', '4' }, { '5', '6', '7', '8' } },
{ { '9' }, { '\0' } }
};
{
A (ma[0].a, 5);
A (&ma[0].a[0], 5);
A (&ma[0].a[1], 4);
A (&ma[0].a[2], 3);
int i = 0;
A (ma[i].a, 5);
A (&ma[i].a[0], 5);
A (&ma[i].a[1], 4);
A (&ma[i].a[2], 3);
int j = i;
A (&ma[i].a[j], 5);
A (&ma[i].a[j + 1], 4);
A (&ma[i].a[j + 2], 3);
A (&ma[idx].a[i], 5);
A (&ma[idx].a[j + 1], 4);
A (&ma[idx].a[j + 2], 3);
A (&ma[idx].a[idx], 5);
A (&ma[idx].a[idx + 1], 4);
A (&ma[idx].a[idx + 2], 3);
}
{
A (ma[1].a, 6);
A (&ma[1].a[0], 6);
A (&ma[1].a[1], 5);
A (&ma[1].a[2], 4);
int i = 1;
A (ma[i].a, 6);
A (&ma[i].a[0], 6);
A (&ma[i].a[1], 5);
A (&ma[i].a[2], 4);
int j = i - 1;
A (&ma[i].a[j], 6);
A (&ma[i].a[j + 1], 5);
A (&ma[i].a[j + 2], 4);
A (&ma[idx + 1].a[j], 6);
A (&ma[idx + 1].a[j + 1], 5);
A (&ma[idx + 1].a[j + 2], 4);
A (&ma[idx + 1].a[idx], 6);
A (&ma[idx + 1].a[idx + 1], 5);
A (&ma[idx + 1].a[idx + 2], 4);
}
{
A (ma[4].a, 9);
A (&ma[4].a[0], 9);
A (&ma[4].a[1], 8);
A (&ma[4].b[0], 5);
int i = 4;
A (ma[i].a, 9);
A (&ma[i].a[0], 9);
A (&ma[i].a[1], 8);
A (&ma[i].b[0], 5);
int j = i - 1;
A (&ma[i].a[j], 6);
A (&ma[i].a[j + 1], 5);
A (&ma[i].b[j - 2], 4);
A (&ma[idx + 4].a[j], 6);
A (&ma[idx + 4].a[j + 1], 5);
A (&ma[idx + 4].b[j - 2], 4);
A (&ma[idx + 4].a[idx], 9);
A (&ma[idx + 4].a[idx + 1], 8);
A (&ma[idx + 4].b[idx + 1], 4);
}
}
union UnionMemberArrays
{
struct { char a[4], b[4]; } a;
struct { char a[8]; } c;
};
const union UnionMemberArrays cu = {
{ { '1', '2', '3', '4' }, { '5', } }
};
static void test_const_union_member_arrays (void)
{
A (cu.a.a, 5);
A (cu.a.b, 1);
A (cu.c.a, 5);
const union UnionMemberArrays clu = {
{ { '1', '2', '3', '4' }, { '5', '6' } }
};
A (clu.a.a, 6);
A (clu.a.b, 2);
A (clu.c.a, 6);
}
union UnionMemberArrays vu = {
{ { '1', '2', '3', '4' }, { '5', '6' } }
};
static void test_nonconst_union_member_arrays (void)
{
A (vu.a.a, 6);
A (vu.a.b, 2);
A (vu.c.a, 6);
union UnionMemberArrays lvu = {
{ { '1', '2', '3', '4' }, { '5', '6', '7' } }
};
A (lvu.a.a, 7);
A (lvu.a.b, 3);
A (lvu.c.a, 7);
}
int main (void)
{
test_const_global_arrays ();
test_const_local_arrays ();
test_nonconst_global_arrays ();
test_nonconst_local_arrays ();
test_const_global_member_arrays ();
test_const_local_member_arrays ();
test_nonconst_global_member_arrays ();
test_nonconst_local_member_arrays ();
test_const_union_member_arrays ();
test_nonconst_union_member_arrays ();
if (nfails)
__builtin_abort ();
}

View File

@ -0,0 +1,113 @@
/* Test to verify that strlen() calls with conditional expressions
and unterminated arrays or pointers to such things as arguments
are evaluated without making assumptions about array sizes. */
extern __SIZE_TYPE__ strlen (const char *);
unsigned nfails;
#define A(expr, N) \
do { \
const char *_s = (expr); \
unsigned _n = strlen (_s); \
((_n == N) \
? 0 \
: (__builtin_printf ("line %i: strlen ((%s) = (\"%s\"))" \
" == %u failed\n", \
__LINE__, #expr, _s, N), \
++nfails)); \
} while (0)
volatile int i0 = 0;
const char ca[2][3] = { "12" };
const char cb[2][3] = { { '1', '2', '3', }, { '4' } };
char va[2][3] = { "123" };
char vb[2][3] = { { '1', '2', '3', }, { '4', '5' } };
const char *s = "123456";
static void test_binary_cond_expr_global (void)
{
A (i0 ? "1" : ca[0], 2);
A (i0 ? ca[0] : "123", 3);
/* The call to strlen (cb[0]) is strictly undefined because the array
isn't nul-terminated. This test verifies that the strlen range
optimization doesn't assume that the argument is necessarily nul
terminated.
Ditto for strlen (vb[0]). */
A (i0 ? "1" : cb[0], 4); /* GCC 8.2 failure */
A (i0 ? cb[0] : "12", 2);
A (i0 ? "1" : va[0], 3); /* GCC 8.2 failure */
A (i0 ? va[0] : "1234", 4);
A (i0 ? "1" : vb[0], 5); /* GCC 8.2 failure */
A (i0 ? vb[0] : "12", 2);
}
static void test_binary_cond_expr_local (void)
{
const char lca[2][3] = { "12" };
const char lcb[2][3] = { { '1', '2', '3', }, { '4' } };
char lva[2][3] = { "123" };
char lvb[2][3] = { { '1', '2', '3', }, { '4', '5' } };
/* Also undefined as above. */
A (i0 ? "1" : lca[0], 2);
A (i0 ? lca[0] : "123", 3);
A (i0 ? "1" : lcb[0], 4); /* GCC 8.2 failure */
A (i0 ? lcb[0] : "12", 2);
A (i0 ? "1" : lva[0], 3); /* GCC 8.2 failure */
A (i0 ? lva[0] : "1234", 4);
A (i0 ? "1" : lvb[0], 5); /* GCC 8.2 failure */
A (i0 ? lvb[0] : "12", 2);
}
static void test_ternary_cond_expr (void)
{
/* Also undefined. */
A (i0 == 0 ? s : i0 == 1 ? vb[0] : "123", 6);
A (i0 == 0 ? vb[0] : i0 == 1 ? s : "123", 5);
A (i0 == 0 ? "123" : i0 == 1 ? s : vb[0], 3);
}
const char (*pca)[3] = &ca[0];
const char (*pcb)[3] = &cb[0];
char (*pva)[3] = &va[0];
char (*pvb)[3] = &vb[0];
static void test_binary_cond_expr_arrayptr (void)
{
/* Also undefined. */
A (i0 ? *pca : *pcb, 4); /* GCC 8.2 failure */
A (i0 ? *pcb : *pca, 2);
A (i0 ? *pva : *pvb, 5); /* GCC 8.2 failure */
A (i0 ? *pvb : *pva, 3);
}
int main (void)
{
test_binary_cond_expr_global ();
test_binary_cond_expr_local ();
test_ternary_cond_expr ();
test_binary_cond_expr_arrayptr ();
if (nfails)
__builtin_abort ();
}

View File

@ -0,0 +1,37 @@
/* Test to verify that a strlen() call with a pointer to a dynamic type
doesn't make assumptions based on the static type of the original
pointer. See g++.dg/init/strlen.C for the corresponding C++ test. */
struct A { int i; char a[1]; void (*p)(); };
struct B { char a[sizeof (struct A) - __builtin_offsetof (struct A, a)]; };
__attribute__ ((noipa)) void
init (char *d, const char *s)
{
__builtin_strcpy (d, s);
}
struct B b;
__attribute__ ((noipa)) void
test_dynamic_type (struct A *p)
{
/* The following call is undefined because it writes past the end
of the p->a subobject, but the corresponding GIMPLE considers
it valid and there's apparently no way to distinguish invalid
cases from ones like it that might be valid. If/when GIMPLE
changes to make this possible this test can be removed. */
char *q = (char*)__builtin_memcpy (p->a, &b, sizeof b);
init (q, "foobar");
if (6 != __builtin_strlen (q))
__builtin_abort();
}
int main (void)
{
struct A *p = (struct A*)__builtin_malloc (sizeof *p);
test_dynamic_type (p);
return 0;
}

View File

@ -9,23 +9,6 @@ extern char a7[7], a6[6], a5[5], a4[4], a3[3], a2[2], a1[1];
extern char a0[0]; /* Intentionally not tested here. */
extern char ax[]; /* Same. */
struct MemArrays {
char a7[7], a6[6], a5[5], a4[4], a3[3], a2[2], a1[1];
char a0[0]; /* Not tested here. */
};
struct NestedMemArrays {
struct { char a7[7]; } ma7;
struct { char a6[6]; } ma6;
struct { char a5[5]; } ma5;
struct { char a4[4]; } ma4;
struct { char a3[3]; } ma3;
struct { char a2[2]; } ma2;
struct { char a1[1]; } ma1;
struct { char a0[0]; } ma0;
char last;
};
extern void failure_on_line (int);
#define TEST_FAIL(line) \
@ -51,36 +34,4 @@ void test_array (void)
T (strlen (a1) == 0); */
}
void test_memarray (struct MemArrays *ma)
{
T (strlen (ma->a7) < sizeof ma->a7);
T (strlen (ma->a6) < sizeof ma->a6);
T (strlen (ma->a5) < sizeof ma->a5);
T (strlen (ma->a4) < sizeof ma->a4);
T (strlen (ma->a3) < sizeof ma->a3);
/* The following two calls are folded too early which defeats
the strlen() optimization.
T (strlen (ma->a2) == 1);
T (strlen (ma->a1) == 0); */
}
/* Verify that the range of strlen(A) of a last struct member is
set even when the array is the sole member of a struct as long
as the struct itself is a member of another struct. The converse
is tested in stlenopt-37.c. */
void test_nested_memarray (struct NestedMemArrays *ma)
{
T (strlen (ma->ma7.a7) < sizeof ma->ma7.a7);
T (strlen (ma->ma6.a6) < sizeof ma->ma6.a6);
T (strlen (ma->ma5.a5) < sizeof ma->ma5.a5);
T (strlen (ma->ma4.a4) < sizeof ma->ma4.a4);
T (strlen (ma->ma3.a3) < sizeof ma->ma3.a3);
/* The following two calls are folded too early which defeats
the strlen() optimization.
T (strlen (ma->ma2.a2) == 1);
T (strlen (ma->ma1.a1) == 0); */
}
/* { dg-final { scan-tree-dump-not "failure_on_line" "optimized" } } */

View File

@ -2,7 +2,7 @@
Test to verify that strnlen built-in expansion works correctly
in the absence of tree strlen optimization.
{ dg-do compile }
{ dg-options "-O2 -Wall -fdump-tree-optimized" } */
{ dg-options "-O2 -Wall -Wno-stringop-overflow -fdump-tree-optimized" } */
#include "strlenopt.h"
@ -85,19 +85,19 @@ void elim_strnlen_arr_cst (void)
ELIM (strnlen (a3_7[0], 1) < 2);
ELIM (strnlen (a3_7[0], 2) < 3);
ELIM (strnlen (a3_7[0], 3) < 4);
ELIM (strnlen (a3_7[0], 9) < 8);
ELIM (strnlen (a3_7[0], PTRDIFF_MAX) < 8);
ELIM (strnlen (a3_7[0], SIZE_MAX) < 8);
ELIM (strnlen (a3_7[0], -1) < 8);
ELIM (strnlen (a3_7[0], 9) <= 9);
ELIM (strnlen (a3_7[0], PTRDIFF_MAX) <= sizeof a3_7);
ELIM (strnlen (a3_7[0], SIZE_MAX) <= sizeof a3_7);
ELIM (strnlen (a3_7[0], -1) <= sizeof a3_7);
ELIM (strnlen (a3_7[2], 0) == 0);
ELIM (strnlen (a3_7[2], 1) < 2);
ELIM (strnlen (a3_7[2], 2) < 3);
ELIM (strnlen (a3_7[2], 3) < 4);
ELIM (strnlen (a3_7[2], 9) < 8);
ELIM (strnlen (a3_7[2], PTRDIFF_MAX) < 8);
ELIM (strnlen (a3_7[2], SIZE_MAX) < 8);
ELIM (strnlen (a3_7[2], -1) < 8);
ELIM (strnlen (a3_7[2], 9) <= 9);
ELIM (strnlen (a3_7[2], PTRDIFF_MAX) < sizeof a3_7);
ELIM (strnlen (a3_7[2], SIZE_MAX) < sizeof a3_7);
ELIM (strnlen (a3_7[2], -1) < sizeof a3_7);
ELIM (strnlen ((char*)a3_7, 0) == 0);
ELIM (strnlen ((char*)a3_7, 1) < 2);
@ -105,123 +105,19 @@ void elim_strnlen_arr_cst (void)
ELIM (strnlen ((char*)a3_7, 3) < 4);
ELIM (strnlen ((char*)a3_7, 9) < 10);
ELIM (strnlen ((char*)a3_7, 19) < 20);
ELIM (strnlen ((char*)a3_7, 21) < 22);
ELIM (strnlen ((char*)a3_7, 23) < 22);
ELIM (strnlen ((char*)a3_7, PTRDIFF_MAX) < 22);
ELIM (strnlen ((char*)a3_7, SIZE_MAX) < 22);
ELIM (strnlen ((char*)a3_7, -1) < 22);
ELIM (strnlen ((char*)a3_7, 21) <= sizeof a3_7);
ELIM (strnlen ((char*)a3_7, 23) <= sizeof a3_7);
ELIM (strnlen ((char*)a3_7, PTRDIFF_MAX) <= sizeof a3_7);
ELIM (strnlen ((char*)a3_7, SIZE_MAX) <= sizeof a3_7);
ELIM (strnlen ((char*)a3_7, -1) <= sizeof a3_7);
ELIM (strnlen (ax, 0) == 0);
ELIM (strnlen (ax, 1) < 2);
ELIM (strnlen (ax, 2) < 3);
ELIM (strnlen (ax, 9) < 10);
ELIM (strnlen (a3, PTRDIFF_MAX) <= PTRDIFF_MAX);
ELIM (strnlen (a3, SIZE_MAX) < PTRDIFF_MAX);
ELIM (strnlen (a3, -1) < PTRDIFF_MAX);
}
struct MemArrays
{
char c;
char a0[0];
char a1[1];
char a3[3];
char a5[5];
char a3_7[3][7];
char ax[1];
};
void elim_strnlen_memarr_cst (struct MemArrays *p, int i)
{
ELIM (strnlen (&p->c, 0) == 0);
ELIM (strnlen (&p->c, 1) < 2);
ELIM (strnlen (&p->c, 9) == 0);
ELIM (strnlen (&p->c, PTRDIFF_MAX) == 0);
ELIM (strnlen (&p->c, SIZE_MAX) == 0);
ELIM (strnlen (&p->c, -1) == 0);
/* Other accesses to internal zero-length arrays are undefined. */
ELIM (strnlen (p->a0, 0) == 0);
ELIM (strnlen (p->a1, 0) == 0);
ELIM (strnlen (p->a1, 1) < 2);
ELIM (strnlen (p->a1, 9) == 0);
ELIM (strnlen (p->a1, PTRDIFF_MAX) == 0);
ELIM (strnlen (p->a1, SIZE_MAX) == 0);
ELIM (strnlen (p->a1, -1) == 0);
ELIM (strnlen (p->a3, 0) == 0);
ELIM (strnlen (p->a3, 1) < 2);
ELIM (strnlen (p->a3, 2) < 3);
ELIM (strnlen (p->a3, 3) < 4);
ELIM (strnlen (p->a3, 9) < 4);
ELIM (strnlen (p->a3, PTRDIFF_MAX) < 4);
ELIM (strnlen (p->a3, SIZE_MAX) < 4);
ELIM (strnlen (p->a3, -1) < 4);
ELIM (strnlen (p[i].a3, 0) == 0);
ELIM (strnlen (p[i].a3, 1) < 2);
ELIM (strnlen (p[i].a3, 2) < 3);
ELIM (strnlen (p[i].a3, 3) < 4);
ELIM (strnlen (p[i].a3, 9) < 4);
ELIM (strnlen (p[i].a3, PTRDIFF_MAX) < 4);
ELIM (strnlen (p[i].a3, SIZE_MAX) < 4);
ELIM (strnlen (p[i].a3, -1) < 4);
ELIM (strnlen (p->a3_7[0], 0) == 0);
ELIM (strnlen (p->a3_7[0], 1) < 2);
ELIM (strnlen (p->a3_7[0], 2) < 3);
ELIM (strnlen (p->a3_7[0], 3) < 4);
ELIM (strnlen (p->a3_7[0], 9) < 8);
ELIM (strnlen (p->a3_7[0], PTRDIFF_MAX) < 8);
ELIM (strnlen (p->a3_7[0], SIZE_MAX) < 8);
ELIM (strnlen (p->a3_7[0], -1) < 8);
ELIM (strnlen (p->a3_7[2], 0) == 0);
ELIM (strnlen (p->a3_7[2], 1) < 2);
ELIM (strnlen (p->a3_7[2], 2) < 3);
ELIM (strnlen (p->a3_7[2], 3) < 4);
ELIM (strnlen (p->a3_7[2], 9) < 8);
ELIM (strnlen (p->a3_7[2], PTRDIFF_MAX) < 8);
ELIM (strnlen (p->a3_7[2], SIZE_MAX) < 8);
ELIM (strnlen (p->a3_7[2], -1) < 8);
ELIM (strnlen (p->a3_7[i], 0) == 0);
ELIM (strnlen (p->a3_7[i], 1) < 2);
ELIM (strnlen (p->a3_7[i], 2) < 3);
ELIM (strnlen (p->a3_7[i], 3) < 4);
#if 0
/* This is tranformed into strnlen ((char*)p + offsetof (a3_7[i]), N)
which makes it impssible to determine the size of the array. */
ELIM (strnlen (p->a3_7[i], 9) < 8);
ELIM (strnlen (p->a3_7[i], PTRDIFF_MAX) < 8);
ELIM (strnlen (p->a3_7[i], SIZE_MAX) < 8);
ELIM (strnlen (p->a3_7[i], -1) < 8);
#else
ELIM (strnlen (p->a3_7[i], 9) < 10);
ELIM (strnlen (p->a3_7[i], 19) < 20);
#endif
ELIM (strnlen ((char*)p->a3_7, 0) == 0);
ELIM (strnlen ((char*)p->a3_7, 1) < 2);
ELIM (strnlen ((char*)p->a3_7, 2) < 3);
ELIM (strnlen ((char*)p->a3_7, 3) < 4);
ELIM (strnlen ((char*)p->a3_7, 9) < 10);
ELIM (strnlen ((char*)p->a3_7, 19) < 20);
ELIM (strnlen ((char*)p->a3_7, 21) < 22);
ELIM (strnlen ((char*)p->a3_7, 23) < 22);
ELIM (strnlen ((char*)p->a3_7, PTRDIFF_MAX) < 22);
ELIM (strnlen ((char*)p->a3_7, SIZE_MAX) < 22);
ELIM (strnlen ((char*)p->a3_7, -1) < 22);
ELIM (strnlen (p->ax, 0) == 0);
ELIM (strnlen (p->ax, 1) < 2);
ELIM (strnlen (p->ax, 2) < 3);
ELIM (strnlen (p->ax, 9) < 10);
ELIM (strnlen (p->a3, PTRDIFF_MAX) <= PTRDIFF_MAX);
ELIM (strnlen (p->a3, SIZE_MAX) < PTRDIFF_MAX);
ELIM (strnlen (p->a3, -1) < PTRDIFF_MAX);
ELIM (strnlen (ax, PTRDIFF_MAX) < PTRDIFF_MAX);
ELIM (strnlen (ax, SIZE_MAX) < PTRDIFF_MAX);
ELIM (strnlen (ax, -1) < PTRDIFF_MAX);
}

View File

@ -1121,67 +1121,23 @@ adjust_last_stmt (strinfo *si, gimple *stmt, bool is_strcat)
update_stmt (last.stmt);
}
/* For an LHS that is an SSA_NAME and for strlen() or strnlen() argument
SRC, set LHS range info to [0, min (N, BOUND)] if SRC refers to
a character array A[N] with unknown length bounded by N, and for
strnlen(), by min (N, BOUND). */
/* For an LHS that is an SSA_NAME that is the result of a strlen()
call, or when BOUND is non-null, of a strnlen() call, set LHS
range info to [0, min (MAX, BOUND)] when the range includes more
than one value and return LHS. Otherwise, when the range
[MIN, MAX] is such that MIN == MAX, return the tree representation
of (MIN). The latter allows callers to fold suitable strnlen() calls
to constants. */
static tree
maybe_set_strlen_range (tree lhs, tree src, tree bound)
tree
set_strlen_range (tree lhs, wide_int max, tree bound /* = NULL_TREE */)
{
if (TREE_CODE (lhs) != SSA_NAME
|| !INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
return NULL_TREE;
if (TREE_CODE (src) == SSA_NAME)
{
gimple *def = SSA_NAME_DEF_STMT (src);
if (is_gimple_assign (def)
&& gimple_assign_rhs_code (def) == ADDR_EXPR)
src = gimple_assign_rhs1 (def);
}
wide_int max = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node));
wide_int min = wi::zero (max.get_precision ());
if (TREE_CODE (src) == ADDR_EXPR)
{
/* The last array member of a struct can be bigger than its size
suggests if it's treated as a poor-man's flexible array member. */
src = TREE_OPERAND (src, 0);
bool src_is_array = TREE_CODE (TREE_TYPE (src)) == ARRAY_TYPE;
if (src_is_array
&& TREE_CODE (src) != MEM_REF
&& !array_at_struct_end_p (src))
{
tree type = TREE_TYPE (src);
if (tree size = TYPE_SIZE_UNIT (type))
if (size && TREE_CODE (size) == INTEGER_CST)
max = wi::to_wide (size);
/* For strlen() the upper bound above is equal to
the longest string that can be stored in the array
(i.e., it accounts for the terminating nul. For
strnlen() bump up the maximum by one since the array
need not be nul-terminated. */
if (!bound && max != 0)
--max;
}
else
{
if (TREE_CODE (src) == COMPONENT_REF && !src_is_array)
src = TREE_OPERAND (src, 1);
if (DECL_P (src))
{
/* Handle the unlikely case of strlen (&c) where c is some
variable. */
if (tree size = DECL_SIZE_UNIT (src))
if (TREE_CODE (size) == INTEGER_CST)
max = wi::to_wide (size);
}
}
}
if (bound)
{
/* For strnlen, adjust MIN and MAX as necessary. If the bound
@ -1205,7 +1161,7 @@ maybe_set_strlen_range (tree lhs, tree src, tree bound)
{
/* For a bound in a known range, adjust the range determined
above as necessary. For a bound in some anti-range or
in an unknown range, use the range determined above. */
in an unknown range, use the range determined by callers. */
if (wi::ltu_p (minbound, min))
min = minbound;
if (wi::ltu_p (maxbound, max))
@ -1221,6 +1177,79 @@ maybe_set_strlen_range (tree lhs, tree src, tree bound)
return lhs;
}
/* For an LHS that is an SSA_NAME and for strlen() or strnlen() argument
SRC, set LHS range info to [0, min (N, BOUND)] if SRC refers to
a character array A[N] with unknown length bounded by N, and for
strnlen(), by min (N, BOUND). */
static tree
maybe_set_strlen_range (tree lhs, tree src, tree bound)
{
if (TREE_CODE (lhs) != SSA_NAME
|| !INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
return NULL_TREE;
if (TREE_CODE (src) == SSA_NAME)
{
gimple *def = SSA_NAME_DEF_STMT (src);
if (is_gimple_assign (def)
&& gimple_assign_rhs_code (def) == ADDR_EXPR)
src = gimple_assign_rhs1 (def);
}
/* The longest string is PTRDIFF_MAX - 1 bytes including the final
NUL so that the difference between a pointer to just past it and
one to its beginning is positive. */
wide_int max = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node)) - 2;
if (TREE_CODE (src) == ADDR_EXPR)
{
/* The last array member of a struct can be bigger than its size
suggests if it's treated as a poor-man's flexible array member. */
src = TREE_OPERAND (src, 0);
if (TREE_CODE (src) != MEM_REF
&& !array_at_struct_end_p (src))
{
tree type = TREE_TYPE (src);
tree size = TYPE_SIZE_UNIT (type);
if (size
&& TREE_CODE (size) == INTEGER_CST
&& !integer_zerop (size))
{
/* Even though such uses of strlen would be undefined,
avoid relying on arrays of arrays in case some genius
decides to call strlen on an unterminated array element
that's followed by a terminated one. Likewise, avoid
assuming that a struct array member is necessarily
nul-terminated (the nul may be in the member that
follows). In those cases, assume that the length
of the string stored in such an array is bounded
by the size of the enclosing object if one can be
determined. */
tree base = get_base_address (src);
if (VAR_P (base))
{
if (tree size = DECL_SIZE_UNIT (base))
if (size
&& TREE_CODE (size) == INTEGER_CST
&& TREE_CODE (TREE_TYPE (base)) != POINTER_TYPE)
max = wi::to_wide (size);
}
}
/* For strlen() the upper bound above is equal to
the longest string that can be stored in the array
(i.e., it accounts for the terminating nul. For
strnlen() bump up the maximum by one since the array
need not be nul-terminated. */
if (!bound && max != 0)
--max;
}
}
return set_strlen_range (lhs, max, bound);
}
/* Handle a strlen call. If strlen of the argument is known, replace
the strlen call with the known value, otherwise remember that strlen
of the argument is stored in the lhs SSA_NAME. */

View File

@ -23,5 +23,6 @@
extern bool is_strlen_related_p (tree, tree);
extern bool maybe_diag_stxncpy_trunc (gimple_stmt_iterator, tree, tree);
extern tree set_strlen_range (tree, wide_int, tree = NULL_TREE);
#endif // GCC_TREE_SSA_STRLEN_H