+2018-08-29 Martin Sebor <msebor@redhat.com>
+ Jeff Law <law@redhat.com>
+
+ 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 <jh@suse.cz>
* tree-streamer-in.c (lto_input_ts_function_decl_tree_pointers):
/* 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;
/* 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);
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))
*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;
}
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;
/* 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);
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);
if (strlen)
*strlen = 0;
- src = string_constant (src, &offset_node);
+ src = string_constant (src, &offset_node, NULL, NULL);
if (src == 0)
return NULL;
+2018-08-29 Martin Sebor <msebor@redhat.com>
+ Bernd Edlinger <bernd.edlinger@hotmail.de>
+
+ 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 <rguenther@suse.de>
PR tree-optimization/87132
--- /dev/null
+/* 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 ();
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+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);
+}
--- /dev/null
+/* 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" } } */
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)
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)