From: Martin Sebor Date: Wed, 2 Jan 2019 21:38:56 +0000 (+0000) Subject: gimple-fold.c (gimple_fold_builtin_strlen): Use set_strlen_range rather than set_rang... X-Git-Tag: upstream/12.2.0~27047 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=d4bf69750d31d08068f8242225b8fa06cdf11411;p=platform%2Fupstream%2Fgcc.git 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 From-SVN: r267531 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index b344f6b..b4e2f25 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,6 +1,16 @@ 2019-01-02 Martin Sebor Jeff Law + + * 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. diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c index 688daf9..0bb4db5 100644 --- a/gcc/gimple-fold.c +++ b/gcc/gimple-fold.c @@ -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; } diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 40703c6..1d5a876 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,12 @@ +2019-01-02 Martin Sebor + Jeff Law + + * 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 PR testsuite/87304 diff --git a/gcc/testsuite/gcc.c-torture/execute/strlen-5.c b/gcc/testsuite/gcc.c-torture/execute/strlen-5.c new file mode 100644 index 0000000..9af57d5 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/strlen-5.c @@ -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 (); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/strlen-6.c b/gcc/testsuite/gcc.c-torture/execute/strlen-6.c new file mode 100644 index 0000000..1df5b21 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/strlen-6.c @@ -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 (); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/strlen-7.c b/gcc/testsuite/gcc.c-torture/execute/strlen-7.c new file mode 100644 index 0000000..884db65 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/strlen-7.c @@ -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; +} diff --git a/gcc/testsuite/gcc.dg/strlenopt-36.c b/gcc/testsuite/gcc.dg/strlenopt-36.c index d6fcca2..56e59a4 100644 --- a/gcc/testsuite/gcc.dg/strlenopt-36.c +++ b/gcc/testsuite/gcc.dg/strlenopt-36.c @@ -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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-45.c b/gcc/testsuite/gcc.dg/strlenopt-45.c index bd9b197..31c1e53 100644 --- a/gcc/testsuite/gcc.dg/strlenopt-45.c +++ b/gcc/testsuite/gcc.dg/strlenopt-45.c @@ -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); } diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c index f64bc9b..55fba88 100644 --- a/gcc/tree-ssa-strlen.c +++ b/gcc/tree-ssa-strlen.c @@ -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). */ - -static tree -maybe_set_strlen_range (tree lhs, tree src, tree 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. */ + +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. */ diff --git a/gcc/tree-ssa-strlen.h b/gcc/tree-ssa-strlen.h index 6b1b819..0b68465 100644 --- a/gcc/tree-ssa-strlen.h +++ b/gcc/tree-ssa-strlen.h @@ -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