From: Martin Sebor Date: Wed, 29 Aug 2018 17:17:08 +0000 (+0000) Subject: re PR middle-end/86714 (tree-ssa-forwprop.c confused by too long initializer) X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=e84bf0ef66aa72228299dd92d342c4de3a37c55c;p=gcc.git re PR middle-end/86714 (tree-ssa-forwprop.c confused by too long initializer) PR tree-optimization/86714 PR tree-optimization/86711 * builtins.c (c_strlen): Add arguments to call to string_constant. * expr.c (string_constant): Add argument. Detect missing nul terminator and outermost declaration it's missing in. * expr.h (string_constant): Add argument. * fold-const.c (read_from_constant_string): Add arguments to call to string_constant. (c_getstr): Likewise. * tree-ssa-forwprop.c (simplify_builtin_call): Likewise. to string_constant. * tree-ssa-strlen.c (get_stridx): Likewise. PR tree-optimization/86714 PR tree-optimization/86711 * gcc.c-torture/execute/memchr-1.c: New test. * gcc.c-torture/execute/pr86714.c: New test. * gcc.c-torture/execute/widechar-3.c: New test. * gcc.dg/strlenopt-58.c: New test. Co-Authored-By: Bernd Edlinger Co-Authored-By: Jeff Law From-SVN: r263963 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 00d6f306744..3cf10ca500e 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,19 @@ +2018-08-29 Martin Sebor + Jeff Law + + PR tree-optimization/86714 + PR tree-optimization/86711 + * builtins.c (c_strlen): Add arguments to call to string_constant. + * expr.c (string_constant): Add argument. Detect missing nul + terminator and outermost declaration it's missing in. + * expr.h (string_constant): Add argument. + * fold-const.c (read_from_constant_string): Add arguments to call to + string_constant. + (c_getstr): Likewise. + * tree-ssa-forwprop.c (simplify_builtin_call): Likewise. + to string_constant. + * tree-ssa-strlen.c (get_stridx): Likewise. + 2018-08-29 Jan Hubicka * tree-streamer-in.c (lto_input_ts_function_decl_tree_pointers): diff --git a/gcc/builtins.c b/gcc/builtins.c index eb69a409272..c9de1e6f1f3 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -589,7 +589,7 @@ c_strlen (tree src, int only_value, unsigned eltsize) /* Offset from the beginning of the string in bytes. */ tree byteoff; tree memsize; - src = string_constant (src, &byteoff, &memsize); + src = string_constant (src, &byteoff, &memsize, NULL); if (src == 0) return NULL_TREE; diff --git a/gcc/expr.c b/gcc/expr.c index 2645c259af8..cd5cf12fca6 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -11303,12 +11303,15 @@ is_aligning_offset (const_tree offset, const_tree exp) /* Return the tree node if an ARG corresponds to a string constant or zero if it doesn't. If we return nonzero, set *PTR_OFFSET to the (possibly non-constant) offset in bytes within the string that ARG is accessing. + If NONSTR is non-null, consider valid even sequences of characters that + aren't nul-terminated strings. In that case, if ARG refers to such + a sequence set *NONSTR to its declaration and clear it otherwise. The type of the offset is sizetype. If MEM_SIZE is non-zero the storage size of the memory is returned. If MEM_SIZE is zero, the string is only returned when it is properly zero terminated. */ tree -string_constant (tree arg, tree *ptr_offset, tree *mem_size) +string_constant (tree arg, tree *ptr_offset, tree *mem_size, tree *nonstr) { tree array; STRIP_NOPS (arg); @@ -11362,7 +11365,7 @@ string_constant (tree arg, tree *ptr_offset, tree *mem_size) return NULL_TREE; tree offset; - if (tree str = string_constant (arg0, &offset, mem_size)) + if (tree str = string_constant (arg0, &offset, mem_size, nonstr)) { /* Avoid pointers to arrays (see bug 86622). */ if (POINTER_TYPE_P (TREE_TYPE (arg)) @@ -11404,6 +11407,9 @@ string_constant (tree arg, tree *ptr_offset, tree *mem_size) *ptr_offset = fold_convert (sizetype, offset); if (mem_size) *mem_size = TYPE_SIZE_UNIT (TREE_TYPE (array)); + /* This is not strictly correct. FIXME in follow-up patch. */ + if (nonstr) + *nonstr = NULL_TREE; return array; } @@ -11450,22 +11456,35 @@ string_constant (tree arg, tree *ptr_offset, tree *mem_size) if (!array_size || TREE_CODE (array_size) != INTEGER_CST) return NULL_TREE; - /* Avoid returning a string that doesn't fit in the array - it is stored in, like + /* Avoid returning an array that is unterminated because it lacks + a terminating nul, like const char a[4] = "abcde"; - but do handle those that fit even if they have excess + but do handle those that are strings even if they have excess initializers, such as in const char a[4] = "abc\000\000"; The excess elements contribute to TREE_STRING_LENGTH() but not to strlen(). */ unsigned HOST_WIDE_INT charsize = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (init)))); + /* Compute the lower bound number of elements (not bytes) in the array + that the string is used to initialize. The actual size of the array + may be greater if the string is shorter, but the the important + data point is whether the literal, inlcuding the terminating nul, + fits the array. */ + unsigned HOST_WIDE_INT array_elts + = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (init))) / charsize; + + /* Compute the string length in (wide) characters. */ unsigned HOST_WIDE_INT length = TREE_STRING_LENGTH (init); length = string_length (TREE_STRING_POINTER (init), charsize, length / charsize); if (mem_size) *mem_size = TYPE_SIZE_UNIT (TREE_TYPE (init)); - else if (compare_tree_int (array_size, length + 1) < 0) + if (nonstr) + *nonstr = array_elts > length ? NULL_TREE : array; + + if ((!mem_size && !nonstr) + && array_elts <= length) return NULL_TREE; *ptr_offset = offset; diff --git a/gcc/expr.h b/gcc/expr.h index d4d25645f61..4177de8060b 100644 --- a/gcc/expr.h +++ b/gcc/expr.h @@ -288,7 +288,7 @@ expand_normal (tree exp) /* Return the tree node and offset if a given argument corresponds to a string constant. */ -extern tree string_constant (tree, tree *, tree * = NULL); +extern tree string_constant (tree, tree *, tree *, tree *); /* Two different ways of generating switch statements. */ extern int try_casesi (tree, tree, tree, tree, rtx, rtx, rtx, profile_probability); diff --git a/gcc/fold-const.c b/gcc/fold-const.c index 68bd64fcc59..bdd24c5969b 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-const.c @@ -13779,7 +13779,7 @@ fold_read_from_constant_string (tree exp) location_t loc = EXPR_LOCATION (exp); if (TREE_CODE (exp) == INDIRECT_REF) - string = string_constant (exp1, &index); + string = string_constant (exp1, &index, NULL, NULL); else { tree low_bound = array_ref_low_bound (exp); @@ -14592,7 +14592,7 @@ c_getstr (tree src, unsigned HOST_WIDE_INT *strlen /* = NULL */, if (strlen) *strlen = 0; - src = string_constant (src, &offset_node); + src = string_constant (src, &offset_node, NULL, NULL); if (src == 0) return NULL; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index d785c903497..20119739b15 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,13 @@ +2018-08-29 Martin Sebor + Bernd Edlinger + + PR tree-optimization/86714 + PR tree-optimization/86711 + * gcc.c-torture/execute/memchr-1.c: New test. + * gcc.c-torture/execute/pr86714.c: New test. + * gcc.c-torture/execute/widechar-3.c: New test. + * gcc.dg/strlenopt-58.c: New test. + 2018-08-29 Richard Biener PR tree-optimization/87132 diff --git a/gcc/testsuite/gcc.c-torture/execute/memchr-1.c b/gcc/testsuite/gcc.c-torture/execute/memchr-1.c new file mode 100644 index 00000000000..ec376322992 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/memchr-1.c @@ -0,0 +1,153 @@ +/* PR tree-optimization/86711 - wrong folding of memchr + + Verify that memchr() of arrays initialized with string literals + where the nul doesn't fit in the array doesn't find the nul. */ +typedef __SIZE_TYPE__ size_t; +typedef __WCHAR_TYPE__ wchar_t; + +extern void* memchr (const void*, int, size_t); + +#define A(expr) \ + ((expr) \ + ? (void)0 \ + : (__builtin_printf ("assertion failed on line %i: %s\n", \ + __LINE__, #expr), \ + __builtin_abort ())) + +static const char c = '1'; +static const char s1[1] = "1"; +static const char s4[4] = "1234"; + +static const char s4_2[2][4] = { "1234", "5678" }; +static const char s5_3[3][5] = { "12345", "6789", "01234" }; + +volatile int v0 = 0; +volatile int v1 = 1; +volatile int v2 = 2; +volatile int v3 = 3; +volatile int v4 = 3; + +void test_narrow (void) +{ + int i0 = 0; + int i1 = i0 + 1; + int i2 = i1 + 1; + int i3 = i2 + 1; + int i4 = i3 + 1; + + A (memchr ("" + 1, 0, 0) == 0); + + A (memchr (&c, 0, sizeof c) == 0); + A (memchr (&c + 1, 0, sizeof c - 1) == 0); + A (memchr (&c + i1, 0, sizeof c - i1) == 0); + A (memchr (&c + v1, 0, sizeof c - v1) == 0); + + A (memchr (s1, 0, sizeof s1) == 0); + A (memchr (s1 + 1, 0, sizeof s1 - 1) == 0); + A (memchr (s1 + i1, 0, sizeof s1 - i1) == 0); + A (memchr (s1 + v1, 0, sizeof s1 - v1) == 0); + + A (memchr (&s1, 0, sizeof s1) == 0); + A (memchr (&s1 + 1, 0, sizeof s1 - 1) == 0); + A (memchr (&s1 + i1, 0, sizeof s1 - i1) == 0); + A (memchr (&s1 + v1, 0, sizeof s1 - v1) == 0); + + A (memchr (&s1[0], 0, sizeof s1) == 0); + A (memchr (&s1[0] + 1, 0, sizeof s1 - 1) == 0); + A (memchr (&s1[0] + i1, 0, sizeof s1 - i1) == 0); + A (memchr (&s1[0] + v1, 0, sizeof s1 - v1) == 0); + + A (memchr (&s1[i0], 0, sizeof s1) == 0); + A (memchr (&s1[i0] + 1, 0, sizeof s1 - 1) == 0); + A (memchr (&s1[i0] + i1, 0, sizeof s1 - i1) == 0); + A (memchr (&s1[i0] + v1, 0, sizeof s1 - v1) == 0); + + A (memchr (&s1[v0], 0, sizeof s1) == 0); + A (memchr (&s1[v0] + 1, 0, sizeof s1 - 1) == 0); + A (memchr (&s1[v0] + i1, 0, sizeof s1 - i1) == 0); + A (memchr (&s1[v0] + v1, 0, sizeof s1 - v1) == 0); + + + A (memchr (s4 + i0, 0, sizeof s4 - i0) == 0); + A (memchr (s4 + i1, 0, sizeof s4 - i1) == 0); + A (memchr (s4 + i2, 0, sizeof s4 - i2) == 0); + A (memchr (s4 + i3, 0, sizeof s4 - i3) == 0); + A (memchr (s4 + i4, 0, sizeof s4 - i4) == 0); + + A (memchr (s4 + v0, 0, sizeof s4 - v0) == 0); + A (memchr (s4 + v1, 0, sizeof s4 - v1) == 0); + A (memchr (s4 + v2, 0, sizeof s4 - v2) == 0); + A (memchr (s4 + v3, 0, sizeof s4 - v3) == 0); + A (memchr (s4 + v4, 0, sizeof s4 - v4) == 0); + + + A (memchr (s4_2, 0, sizeof s4_2) == 0); + + A (memchr (s4_2[0], 0, sizeof s4_2[0]) == 0); + A (memchr (s4_2[1], 0, sizeof s4_2[1]) == 0); + + A (memchr (s4_2[0] + 1, 0, sizeof s4_2[0] - 1) == 0); + A (memchr (s4_2[1] + 2, 0, sizeof s4_2[1] - 2) == 0); + A (memchr (s4_2[1] + 3, 0, sizeof s4_2[1] - 3) == 0); + + A (memchr (s4_2[v0], 0, sizeof s4_2[v0]) == 0); + A (memchr (s4_2[v0] + 1, 0, sizeof s4_2[v0] - 1) == 0); + + + /* The following calls must find the nul. */ + A (memchr ("", 0, 1) != 0); + A (memchr (s5_3, 0, sizeof s5_3) == &s5_3[1][4]); + + A (memchr (&s5_3[0][0] + i0, 0, sizeof s5_3 - i0) == &s5_3[1][4]); + A (memchr (&s5_3[0][0] + i1, 0, sizeof s5_3 - i1) == &s5_3[1][4]); + A (memchr (&s5_3[0][0] + i2, 0, sizeof s5_3 - i2) == &s5_3[1][4]); + A (memchr (&s5_3[0][0] + i4, 0, sizeof s5_3 - i4) == &s5_3[1][4]); + + A (memchr (&s5_3[1][i0], 0, sizeof s5_3[1] - i0) == &s5_3[1][4]); +} + +static const wchar_t wc = L'1'; +static const wchar_t ws1[] = L"1"; +static const wchar_t ws4[] = L"\x00123456\x12005678\x12340078\x12345600"; + +void test_wide (void) +{ + int i0 = 0; + int i1 = i0 + 1; + int i2 = i1 + 1; + int i3 = i2 + 1; + int i4 = i3 + 1; + + A (memchr (L"" + 1, 0, 0) == 0); + A (memchr (&wc + 1, 0, 0) == 0); + A (memchr (L"\x12345678", 0, sizeof (wchar_t)) == 0); + + const size_t nb = sizeof ws4; + const size_t nwb = sizeof (wchar_t); + + const char *pws1 = (const char*)ws1; + const char *pws4 = (const char*)ws4; + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + A (memchr (ws1, 0, sizeof ws1) == pws1 + 1); + + A (memchr (&ws4[0], 0, nb) == pws4 + 3); + A (memchr (&ws4[1], 0, nb - 1 * nwb) == pws4 + 1 * nwb + 2); + A (memchr (&ws4[2], 0, nb - 2 * nwb) == pws4 + 2 * nwb + 1); + A (memchr (&ws4[3], 0, nb - 3 * nwb) == pws4 + 3 * nwb + 0); +#else + A (memchr (ws1, 0, sizeof ws1) == pws1 + 0); + + A (memchr (&ws4[0], 0, nb) == pws4 + 0); + A (memchr (&ws4[1], 0, nb - 1 * nwb) == pws4 + 1 * nwb + 0); + A (memchr (&ws4[2], 0, nb - 2 * nwb) == pws4 + 2 * nwb + 1); + A (memchr (&ws4[3], 0, nb - 3 * nwb) == pws4 + 3 * nwb + 2); +#endif +} + + +int main () +{ + test_narrow (); + test_wide (); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/pr86714.c b/gcc/testsuite/gcc.c-torture/execute/pr86714.c new file mode 100644 index 00000000000..3ad68522e71 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/pr86714.c @@ -0,0 +1,26 @@ +/* PR tree-optimization/86714 - tree-ssa-forwprop.c confused by too + long initializer + + The excessively long initializer for a[0] is undefined but this + test verifies that the excess elements are not considered a part + of the value of the array as a matter of QoI. */ + +const char a[2][3] = { "1234", "xyz" }; +char b[6]; + +void *pb = b; + +int main () +{ + __builtin_memcpy (b, a, 4); + __builtin_memset (b + 4, 'a', 2); + + if (b[0] != '1' || b[1] != '2' || b[2] != '3' + || b[3] != 'x' || b[4] != 'a' || b[5] != 'a') + __builtin_abort (); + + if (__builtin_memcmp (pb, "123xaa", 6)) + __builtin_abort (); + + return 0; +} diff --git a/gcc/testsuite/gcc.c-torture/execute/widechar-3.c b/gcc/testsuite/gcc.c-torture/execute/widechar-3.c new file mode 100644 index 00000000000..0810c7dd5f4 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/widechar-3.c @@ -0,0 +1,26 @@ +extern void abort (void); +extern void exit (int); + +static int f(char *x) +{ + return __builtin_strlen(x); +} + +int foo () +{ + return f((char*)&L"abcdef"[0]); +} + + +int +main() +{ +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + if (foo () != 0) + abort (); +#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + if (foo () != 1) + abort (); +#endif + exit (0); +} diff --git a/gcc/testsuite/gcc.dg/strlenopt-58.c b/gcc/testsuite/gcc.dg/strlenopt-58.c new file mode 100644 index 00000000000..e0e80680936 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-58.c @@ -0,0 +1,93 @@ +/* PR tree-optimization/86711 - wrong folding of memchr + + Verify that calls to memchr() with constant arrays initialized + with wide string literals are folded. + + { dg-do compile } + { dg-options "-O1 -Wall -fdump-tree-optimized" } */ + +#include "strlenopt.h" + +typedef __WCHAR_TYPE__ wchar_t; + +extern void* memchr (const void*, int, size_t); + +#define CONCAT(x, y) x ## y +#define CAT(x, y) CONCAT (x, y) +#define FAILNAME(name) CAT (call_ ## name ##_on_line_, __LINE__) + +#define FAIL(name) do { \ + extern void FAILNAME (name) (void); \ + FAILNAME (name)(); \ + } while (0) + +/* Macro to emit a call to funcation named + call_in_true_branch_not_eliminated_on_line_NNN() + for each call that's expected to be eliminated. The dg-final + scan-tree-dump-time directive at the bottom of the test verifies + that no such call appears in output. */ +#define ELIM(expr) \ + if (!(expr)) FAIL (in_true_branch_not_eliminated); else (void)0 + +#define T(s, n) ELIM (strlen (s) == n) + + +static const wchar_t wc = L'1'; +static const wchar_t ws1[] = L"1"; +static const wchar_t wsx[] = L"\x12345678"; +static const wchar_t ws4[] = L"\x00123456\x12005678\x12340078\x12345600"; + +void test_wide (void) +{ + int i0 = 0; + int i1 = i0 + 1; + int i2 = i1 + 1; + int i3 = i2 + 1; + int i4 = i3 + 1; + + ELIM (memchr (L"" + 1, 0, 0) == 0); + ELIM (memchr (&wc + 1, 0, 0) == 0); + ELIM (memchr (L"\x12345678", 0, sizeof (wchar_t)) == 0); + + const size_t nb = sizeof ws4; + const size_t nwb = sizeof (wchar_t); + + const char *pws1 = (const char*)ws1; + const char *pws4 = (const char*)ws4; + const char *pwsx = (const char*)wsx; + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + ELIM (memchr (ws1, 0, sizeof ws1) == pws1 + 1); + ELIM (memchr (wsx, 0, sizeof wsx) == pwsx + sizeof *wsx); + + ELIM (memchr (&ws4[0], 0, nb) == pws4 + 3); + ELIM (memchr (&ws4[1], 0, nb - 1 * nwb) == pws4 + 1 * nwb + 2); + ELIM (memchr (&ws4[2], 0, nb - 2 * nwb) == pws4 + 2 * nwb + 1); + ELIM (memchr (&ws4[3], 0, nb - 3 * nwb) == pws4 + 3 * nwb + 0); + ELIM (memchr (&ws4[4], 0, nb - 4 * nwb) == pws4 + 4 * nwb + 0); + + ELIM (memchr (&ws4[i0], 0, nb) == pws4 + 3); + ELIM (memchr (&ws4[i1], 0, nb - 1 * nwb) == pws4 + 1 * nwb + 2); + ELIM (memchr (&ws4[i2], 0, nb - 2 * nwb) == pws4 + 2 * nwb + 1); + ELIM (memchr (&ws4[i3], 0, nb - 3 * nwb) == pws4 + 3 * nwb + 0); + ELIM (memchr (&ws4[i4], 0, nb - 4 * nwb) == pws4 + 4 * nwb + 0); +#else + ELIM (memchr (ws1, 0, sizeof ws1) == pws1 + 0); + ELIM (memchr (wsx, 0, sizeof wsx) == pwsx + sizeof *wsx); + + ELIM (memchr (&ws4[0], 0, nb) == pws4 + 0); + ELIM (memchr (&ws4[1], 0, nb - 1 * nwb) == pws4 + 1 * nwb + 1); + ELIM (memchr (&ws4[2], 0, nb - 2 * nwb) == pws4 + 2 * nwb + 2); + ELIM (memchr (&ws4[3], 0, nb - 3 * nwb) == pws4 + 3 * nwb + 3); + ELIM (memchr (&ws4[4], 0, nb - 4 * nwb) == pws4 + 4 * nwb + 0); + + ELIM (memchr (&ws4[i0], 0, nb) == pws4 + 0); + ELIM (memchr (&ws4[i1], 0, nb - 1 * nwb) == pws4 + 1 * nwb + 1); + ELIM (memchr (&ws4[i2], 0, nb - 2 * nwb) == pws4 + 2 * nwb + 2); + ELIM (memchr (&ws4[i3], 0, nb - 3 * nwb) == pws4 + 3 * nwb + 3); + ELIM (memchr (&ws4[i4], 0, nb - 4 * nwb) == pws4 + 4 * nwb + 0); +#endif +} + +/* { dg-final { scan-tree-dump-times "memchr" 0 "optimized" } } + { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated" 0 "optimized" } } */ diff --git a/gcc/tree-ssa-forwprop.c b/gcc/tree-ssa-forwprop.c index efdf2094c8b..67133983cc3 100644 --- a/gcc/tree-ssa-forwprop.c +++ b/gcc/tree-ssa-forwprop.c @@ -1290,7 +1290,7 @@ simplify_builtin_call (gimple_stmt_iterator *gsi_p, tree callee2) lhs1 = gimple_call_lhs (stmt1); if (!tree_fits_uhwi_p (len1)) break; - str1 = string_constant (src1, &off1); + str1 = string_constant (src1, &off1, NULL, NULL); if (str1 == NULL_TREE) break; if (!tree_fits_uhwi_p (off1) diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c index 84e61526f94..d625b940c1f 100644 --- a/gcc/tree-ssa-strlen.c +++ b/gcc/tree-ssa-strlen.c @@ -336,7 +336,7 @@ get_stridx (tree exp) return idx; } - s = string_constant (exp, &o); + s = string_constant (exp, &o, NULL, NULL); if (s != NULL_TREE && (o == NULL_TREE || tree_fits_shwi_p (o)) && TREE_STRING_LENGTH (s) > 0)