+2019-08-14 Martin Sebor <msebor@redhat.com>
+
+ PR tree-optimization/91294
+ * tree-ssa-strlen.c (handle_store): Avoid treating lower bound of
+ source length as exact.
+
2019-08-14 Christophe Lyon <christophe.lyon@linaro.org>
* doc/extend.texi: Add "noinit" attribute documentation.
/* Set the strlen() range to [0, MAXLEN]. */
if (tree lhs = gimple_call_lhs (stmt))
- set_strlen_range (lhs, maxlen);
+ set_strlen_range (lhs, minlen, maxlen);
return false;
}
+2019-08-14 Martin Sebor <msebor@redhat.com>
+
+ PR tree-optimization/91294
+ * gcc.dg/strlenopt-44.c: Adjust tested result.
+ * gcc.dg/strlenopt-70.c: Avoid exercising unimplemnted optimization.
+ * gcc.dg/strlenopt-73.c: New test.
+ * gcc.dg/strlenopt-74.c: New test.
+ * gcc.dg/strlenopt-75.c: New test.
+ * gcc.dg/strlenopt-76.c: New test.
+ * gcc.dg/strlenopt-77.c: New test.
+
2019-08-14 Jakub Jelinek <jakub@redhat.com>
Marek Polacek <polacek@redhat.com>
size_t uchar_max = (unsigned char)-1;
KEEP ("1", 0, UR (1, uchar_max + 1), 1);
- KEEP ("1\0\3", 1, UR (1, 2), 1);
+ KEEP ("1\0\3", 1, UR (1, 2), 2);
}
/* { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated_" 0 "optimized" } }
T ("xxx", uint32_t, 0, I32 ("\1\2\3\0"), == 3);
T ("xxx", uint32_t, 0, I32 ("\0\1\2\3"), == 0);
- uint32_t x00332211 = I32 ("123\0");
- uint32_t x00002211 = I32 ("12\0\0");
- uint32_t x00000011 = I32 ("1\0\0\0");
-
- T ("xxxx", uint32_t, 0, i ? x00332211 : x00002211, <= 3);
- T ("xxxx", uint32_t, 0, i ? x00332211 : x00002211, >= 2);
- T ("xxxx", uint32_t, 0, i ? x00332211 : x00000011, <= 3);
- T ("xxxx", uint32_t, 0, i ? x00332211 : x00000011, >= 1);
+ uint32_t x123_ = I32 ("123\0");
+ uint32_t x12__ = I32 ("12\0\0");
+ uint32_t x1___ = I32 ("1\0\0\0");
+
+ // FIXME: Upper bound not implemented yet.
+ /* T ("xxxx", uint32_t, 0, i ? x123_ : x12__, <= 3); */
+ T ("xxxx", uint32_t, 0, i ? x123_ : x12__, >= 2);
+ T ("xxxx", uint32_t, 0, i ? x12__ : x123_, >= 2);
+ /* T ("xxxx", uint32_t, 0, i ? x123_ : x1___, <= 3); */
+ T ("xxxx", uint32_t, 0, i ? x123_ : x1___, >= 1);
+ T ("xxxx", uint32_t, 0, i ? x1___ : x123_, >= 1);
TX ("abcde", uint32_t, 0, i ? I32 ("1234") : I32 ("1235"), == 5);
TX ("abcde", uint32_t, 1, i ? I32 ("1234") : I32 ("1235"), == 5);
TX ("abcdef", uint32_t, 3, i ? I32 ("12\0\0") : I32 ("13\0\0"), == 5);
TX ("abcdef", uint32_t, 3, i ? I32 ("12\0\0") : I32 ("123\0"), >= 5);
- TX ("abcdef", uint32_t, 3, i ? I32 ("12\0\0") : I32 ("123\0"), < 7);
+ /* FIXME: Upper bound not implemented yet. */
+ /* TX ("abcdef", uint32_t, 3, i ? I32 ("12\0\0") : I32 ("123\0"), < 7); */
}
void store_64bit (int i)
T ("xxxxxxx", uint64_t, 0, I64 ("\1\2\3\4\5\6\0\0\0"), == 6);
T ("xxxxxxx", uint64_t, 0, I64 ("\1\2\3\4\5\6\7\0\0"), == 7);
- uint64_t x7777777 = I64 ("\7\7\7\7\7\7\7");
- uint64_t x666666 = I64 ("\6\6\6\6\6\6\0");
- uint64_t x4444 = I64 ("\4\4\4\4\0\0\0");
- uint64_t x3333 = I64 ("\3\3\3\3\0\0\0");
- uint64_t x1 = I64 ("\1\0\0\0\0\0\0");
-
- T ("x\0xxxxxx", uint64_t, 0, i ? x7777777 : x666666, <= 7);
- T ("xx\0xxxxx", uint64_t, 0, i ? x7777777 : x666666, >= 6);
- T ("xxx\0xxxx", uint64_t, 0, i ? x666666 : x1, <= 6);
- T ("xxxx\0xxx", uint64_t, 0, i ? x666666 : x1, >= 1);
- T ("xxxxxx\0x", uint64_t, 0, i ? x4444 : x3333, == 4);
+ uint64_t x7777777_ = I64 ("\7\7\7\7\7\7\7");
+ uint64_t x666666__ = I64 ("\6\6\6\6\6\6\0");
+ uint64_t x4444____ = I64 ("\4\4\4\4\0\0\0");
+ uint64_t x4343____ = I64 ("\4\3\4\3\0\0\0");
+ uint64_t x1_______ = I64 ("\1\0\0\0\0\0\0");
+
+ /* FIXME: Upper bound not implemented yet. */
+ /* T ("x\0xxxxxx", uint64_t, 0, i ? x7777777_ : x666666__, <= 7); */
+ T ("xx\0xxxxx", uint64_t, 0, i ? x7777777_ : x666666__, >= 6);
+ T ("xxx\0xxxx", uint64_t, 1, i ? x7777777_ : x666666__, >= 7);
+ /* T ("xxx\0xxxx", uint64_t, 0, i ? x666666__ : x1, <= 6); */
+ T ("xxxx\0xxx", uint64_t, 0, i ? x666666__ : x1_______, >= 1);
+ T ("xxxxxx\0x", uint64_t, 0, i ? x4444____ : x4343____, == 4);
}
#if __SIZEOF_INT128__
--- /dev/null
+/* PR tree-optimization/91183 - strlen of a strcpy result with a conditional
+ source not folded
+ Test to verify that strlen can determine string lengths from stores
+ involving PHI nodes with distinct strings of the same length of at
+ least 16 bytes.
+
+ { dg-do compile }
+ { dg-options "-O2 -fdump-tree-optimized" }
+ On strictly aligned targets the consecutive char assignments used
+ by the test aren't merged. When they involve multiple trailing nuls
+ these assignments then defeat the strlen optimization as a result of
+ pr83821. When the bug is resolved the directive below can be removed.
+ { dg-require-effective-target non_strict_align } */
+
+#include "strlenopt.h"
+
+#define CAT(x, y) x ## y
+#define CONCAT(x, y) CAT (x, y)
+#define FAILNAME(name) CONCAT (call_ ## name ##_on_line_, __LINE__)
+
+#define FAIL(name) do { \
+ extern void FAILNAME (name) (void); \
+ FAILNAME (name)(); \
+ } while (0)
+
+/* Macros to emit a call to function named
+ call_failed_to_be_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 (not_eliminated); else (void)0
+
+#define T(expect, N, ncpy, cond) do { \
+ char CONCAT (arr_, __LINE__)[N]; \
+ char *pa = CONCAT (arr_, __LINE__); \
+ memcpy (pa, cond, ncpy); \
+ ELIM (!(expect strlen (pa))); \
+ sink (pa); \
+ } while (0)
+
+void sink (void*);
+
+const char a32[33] = "0123456789abcdef0123456789abcdef";
+const char b32[33] = "fedcba9876543210fedcba9876543210";
+
+const char a16[33] = "0123456789abcdef";
+const char b16[33] = "fedcba9876543210";
+
+int i0, i1, i2;
+
+void test_copy_cond_equal_length (void)
+{
+ // The test below is represented as this:
+ // # iftmp.0_3 = PHI <&b16(2), &a16(3)>
+ // MEM <unsigned char[17]> [(char * {ref-all})&a]
+ // = MEM <unsigned char[17]> [(char * {ref-all})iftmp.0_3];
+ // _2 = strlen (&a);
+ T (16 ==, 17, 17, i0 ? a16 : b16);
+ T (16 ==, 17, 17, i0 ? a16 : b16);
+ T (15 ==, 17, 16, (i0 ? a16 : b16) + 1);
+ T (14 ==, 17, 15, (i0 ? a16 : b16) + 2);
+ T ( 0 ==, 17, 1, (i0 ? a16 : b16) + 16);
+
+ T (31 ==, 33, 32, (i0 ? a32 : b32) + 1);
+ T (30 ==, 33, 31, (i0 ? a32 : b32) + 2);
+ T (29 ==, 33, 30, (i0 ? a32 : b32) + 3);
+ T ( 1 ==, 33, 2, (i0 ? a32 : b32) + 31);
+ T ( 0 ==, 33, 1, (i0 ? a32 : b32) + 32);
+}
+
+
+const char a4[16] = "0123";
+const char b4[16] = "3210";
+
+void test_copy_cond_unequal_length_i64 (void)
+{
+ T (2 <, 16, 8, i0 ? a4 + 1 : b4 + 0);
+ T (1 <, 16, 8, i0 ? a4 + 1 : b4 + 2);
+ T (0 <, 16, 8, i0 ? a4 + 1 : b4 + 3);
+
+ T (1 <, 16, 8, i0 ? a4 + 2 : b4 + 0);
+ T (1 <, 16, 8, i0 ? a4 + 2 : b4 + 1);
+ T (0 <, 16, 8, i0 ? a4 + 2 : b4 + 3);
+}
+
+
+#if __SIZEOF_INT128__ == 16
+
+/* The following tests assume GCC transforms the memcpy calls into
+ int128_t assignments which it does only when int128_t is supported. */
+
+const char a8[32] = "01234567";
+const char b8[32] = "76543210";
+
+void test_copy_cond_unequal_length_i128 (void)
+{
+ T (6 <, 32, 16, i0 ? a8 + 1 : b8 + 0);
+ T (5 <, 32, 16, i0 ? a8 + 1 : b8 + 2);
+ T (4 <, 32, 16, i0 ? a8 + 1 : b8 + 3);
+ T (3 <, 32, 16, i0 ? a8 + 1 : b8 + 4);
+ T (2 <, 32, 16, i0 ? a8 + 1 : b8 + 5);
+ T (1 <, 32, 16, i0 ? a8 + 1 : b8 + 6);
+ T (0 <, 32, 16, i0 ? a8 + 1 : b8 + 7);
+
+ T (5 <, 32, 16, i0 ? a8 + 2 : b8 + 0);
+ T (5 <, 32, 16, i0 ? a8 + 2 : b8 + 1);
+ T (3 <, 32, 16, i0 ? a8 + 2 : b8 + 3);
+ T (2 <, 32, 16, i0 ? a8 + 2 : b8 + 4);
+ T (1 <, 32, 16, i0 ? a8 + 2 : b8 + 5);
+ T (0 <, 32, 16, i0 ? a8 + 2 : b8 + 6);
+
+ T (4 <, 32, 16, i0 ? a8 + 3 : b8 + 0);
+ T (4 <, 32, 16, i0 ? a8 + 3 : b8 + 1);
+ T (4 <, 32, 16, i0 ? a8 + 3 : b8 + 2);
+ T (3 <, 32, 16, i0 ? a8 + 3 : b8 + 4);
+ T (2 <, 32, 16, i0 ? a8 + 3 : b8 + 5);
+ T (1 <, 32, 16, i0 ? a8 + 3 : b8 + 6);
+ T (0 <, 32, 16, i0 ? a8 + 3 : b8 + 7);
+
+ T (3 <, 32, 16, i0 ? a8 + 4 : b8 + 0);
+ T (3 <, 32, 16, i0 ? a8 + 4 : b8 + 1);
+ T (3 <, 32, 16, i0 ? a8 + 4 : b8 + 2);
+ T (3 <, 32, 16, i0 ? a8 + 4 : b8 + 3);
+ T (2 <, 32, 16, i0 ? a8 + 4 : b8 + 5);
+ T (1 <, 32, 16, i0 ? a8 + 4 : b8 + 6);
+ T (0 <, 32, 16, i0 ? a8 + 4 : b8 + 7);
+}
+
+#endif /* int128_t exists */
+
+/* { dg-final { scan-tree-dump-times "strlen" 0 "optimized" } }
+ { dg-final { scan-tree-dump-times "_not_eliminated_" 0 "optimized" } } */
--- /dev/null
+/* PR tree-optimization/91294 - wrong strlen result of a conditional with
+ an offset
+ { dg-do run }
+ { dg-options "-O2 -Wall" } */
+
+#include "strlenopt.h"
+
+#define NOIPA __attribute__ ((noclone, noinline, noipa))
+
+#define CAT(a, b) a ## b
+#define CONCAT(a, b) CAT (a, b)
+#define UNIQ_NAME(name) CONCAT (name, __LINE__)
+
+extern int last_line;
+int nfails;
+
+char buf[32];
+
+#define VERIFY(expr, nbytes, expect) \
+ NOIPA void UNIQ_NAME (test_)(void) \
+ { \
+ memcpy (buf, (expr), (nbytes)); \
+ const size_t len = strlen (buf); \
+ if (len != expect) \
+ { \
+ ++nfails; \
+ __builtin_printf ("line %i: strlen(%s) == %zu failed: " \
+ "got %zu\n", \
+ __LINE__ - 1000 + last_line + 2, \
+ #expr, (size_t)expect, \
+ len); \
+ } \
+ } typedef void DummyType
+
+const char a8[12] = "01234567";
+const char b8[12] = "76543210";
+const char c4[12] = "0123";
+
+int i0, i1 = 1, i2 = 2;
+
+int last_line = __LINE__;
+#line 1000
+VERIFY (i0 ? (a8 + 0) : (b8 + 0), 9, 8);
+VERIFY (i0 ? (a8 + 0) : (b8 + 1), 8, 7);
+VERIFY (i0 ? (a8 + 0) : (b8 + 2), 8, 6);
+VERIFY (i0 ? (a8 + 0) : (b8 + 2), 7, 6);
+VERIFY (i0 ? (a8 + 0) : (b8 + 3), 8, 5);
+VERIFY (i0 ? (a8 + 0) : (b8 + 3), 7, 5);
+VERIFY (i0 ? (a8 + 0) : (b8 + 3), 6, 5);
+VERIFY (i0 ? (a8 + 0) : (b8 + 4), 8, 4);
+VERIFY (i0 ? (a8 + 0) : (b8 + 4), 7, 4);
+VERIFY (i0 ? (a8 + 0) : (b8 + 4), 6, 4);
+VERIFY (i0 ? (a8 + 0) : (b8 + 4), 5, 4);
+VERIFY (i0 ? (a8 + 0) : (b8 + 5), 7, 3);
+VERIFY (i0 ? (a8 + 0) : (b8 + 5), 6, 3);
+VERIFY (i0 ? (a8 + 0) : (b8 + 5), 5, 3);
+VERIFY (i0 ? (a8 + 0) : (b8 + 5), 4, 3);
+VERIFY (i0 ? (a8 + 0) : (b8 + 6), 3, 2);
+VERIFY (i0 ? (a8 + 0) : (b8 + 7), 2, 1);
+VERIFY (i0 ? (a8 + 1) : (b8 + 0), 8, 8);
+VERIFY (i0 ? (a8 + 2) : (b8 + 0), 7, 7);
+VERIFY (i0 ? (a8 + 1) : (b8 + 1), 8, 7);
+VERIFY (i0 ? (a8 + 1) : (b8 + 2), 7, 6);
+VERIFY (i0 ? (a8 + 2) : (b8 + 1), 8, 7); // FAIL
+VERIFY (i0 ? (a8 + 2) : (b8 + 2), 7, 6);
+VERIFY (i0 ? (a8 + 0) : (b8 + 0), 9, 8);
+VERIFY (i0 ? (a8 + 0) : (b8 + 1), 8, 7);
+VERIFY (i0 ? (a8 + 0) : (b8 + 2), 7, 6);
+VERIFY (i0 ? (a8 + 1) : (b8 + 0), 9, 8);
+VERIFY (i0 ? (a8 + 2) : (b8 + 0), 9, 8);
+VERIFY (i0 ? (a8 + 1) : (b8 + 1), 8, 7);
+VERIFY (i0 ? (a8 + 1) : (b8 + 2), 7, 6);
+VERIFY (i0 ? (a8 + 2) : (b8 + 1), 8, 7); // FAIL
+VERIFY (i0 ? (a8 + 2) : (b8 + 2), 7, 6);
+VERIFY (i0 ? (a8 + 0) : (c4 + 0), 9, 4);
+VERIFY (i0 ? (a8 + 0) : (c4 + 1), 9, 3);
+VERIFY (i0 ? (a8 + 0) : (c4 + 3), 9, 1);
+VERIFY (i0 ? (a8 + 0) : (c4 + 4), 9, 0);
+VERIFY (i0 ? (a8 + 1) : (c4 + 0), 8, 4);
+VERIFY (i0 ? (a8 + 1) : (c4 + 1), 8, 3);
+VERIFY (i0 ? (a8 + 1) : (c4 + 2), 8, 2);
+VERIFY (i0 ? (a8 + 1) : (c4 + 3), 8, 1);
+VERIFY (i0 ? (a8 + 1) : (c4 + 4), 8, 0);
+VERIFY (i0 ? (a8 + 2) : (c4 + 0), 8, 4);
+VERIFY (i0 ? (a8 + 2) : (c4 + 1), 8, 3);
+VERIFY (i0 ? (a8 + 2) : (c4 + 2), 8, 2);
+VERIFY (i0 ? (a8 + 2) : (c4 + 3), 8, 1);
+VERIFY (i0 ? (a8 + 2) : (c4 + 4), 8, 0);
+VERIFY ((i0 ? a8 : b8) + 1, 8, 7);
+VERIFY ((i0 ? a8 : b8) + 2, 8, 6);
+VERIFY ((i0 ? a8 : b8) + 2, 7, 6);
+VERIFY ((i0 ? a8 : b8) + 3, 3, 3);
+VERIFY ((i0 ? a8 : b8) + 3, 1, 1);
+VERIFY ((i0 ? a8 : c4) + 1, 8, 3);
+VERIFY ((i0 ? a8 : c4) + 3, 8, 1);
+VERIFY ((i0 ? a8 : c4) + 4, 8, 0);
+VERIFY ((i0 ? a8 + 1: b8 + 2) + 1, 9, 5);
+VERIFY ((i0 ? a8 + i1: b8 + i2) + 1, 8, 5);
+VERIFY ((i0 ? a8 + i1: b8 + 2) + 1, 8, 5);
+VERIFY ((i0 ? a8 + i2: b8 + i1) + 1, 8, 6);
+VERIFY ((i0 ? a8 + 2: b8 + i1) + 1, 8, 6);
+
+#define T(N) test_ ## N (); memset (buf, 0, sizeof buf)
+
+int main (void)
+{
+ T (1000);
+ T (1001);
+ T (1002);
+ T (1003);
+ T (1004);
+ T (1005);
+ T (1006);
+ T (1007);
+ T (1008);
+ T (1009);
+
+ T (1010);
+ T (1011);
+ T (1012);
+ T (1013);
+ T (1014);
+ T (1015);
+ T (1016);
+ T (1017);
+ T (1018);
+ T (1019);
+
+ T (1020);
+ T (1021);
+ T (1022);
+ T (1023);
+ T (1024);
+ T (1025);
+ T (1026);
+ T (1027);
+ T (1028);
+ T (1029);
+
+ T (1030);
+ T (1031);
+ T (1032);
+ T (1033);
+ T (1034);
+ T (1035);
+ T (1036);
+ T (1037);
+ T (1038);
+ T (1039);
+
+ T (1040);
+ T (1041);
+ T (1042);
+ T (1043);
+ T (1044);
+ T (1045);
+ T (1046);
+ T (1047);
+ T (1048);
+ T (1049);
+
+ T (1050);
+ T (1051);
+ T (1052);
+ T (1053);
+ T (1054);
+ T (1055);
+ T (1056);
+ T (1057);
+ T (1058);
+
+ if (nfails)
+ abort ();
+}
+
--- /dev/null
+/* PR tree-optimization/91294 - strlen result of a conditional with
+ an offset
+ { dg-do run }
+ { dg-options "-O2 -Wall" } */
+
+#include "strlenopt.h"
+
+#define NOIPA __attribute__ ((noclone, noinline, noipa))
+
+int i = 0;
+
+const char s[] = "1234567";
+
+char a[32];
+
+/* Exercise a memcpy overwriting a destination string of known length
+ with a source argument involving a conditional expression with strings
+ of unqual lengths, with the selected one being the longer of the two
+ and resulting in no change to the length of the overwritten destination
+ string. */
+NOIPA void test_memcpy_same_length ()
+{
+ memcpy (a, "123456789a", 11);
+ memcpy (a + 6, i ? "78\0" : "789\0", 4);
+ if (strlen (a) != 9)
+ abort ();
+}
+
+/* Same as above but with strcpy/strcat. */
+
+NOIPA void test_strcpy_strcat_same_length ()
+{
+ strcpy (a, "12345678");
+ strcat (a, "9a");
+ memcpy (a + 6, i ? "78\0" : "789\0", 4);
+ if (strlen (a) != 9)
+ abort ();
+}
+
+/* Same as above but using a memcpy of a power-of-two size that gets
+ (on some targets) transformed into a single MEM_REF assignment. */
+
+NOIPA void test_assign_same_length ()
+{
+ memcpy (a, s, 8);
+ memcpy (a + 5, i ? "67\0" : "678\0", 4);
+ if (strlen (a) != 8)
+ abort ();
+}
+
+/* Same as above but resulting in increasing the length of the destination
+ string. */
+
+NOIPA void test_memcpy_lengthen ()
+{
+ memcpy (a, "123456789a", 11);
+ memcpy (a + 8, i ? "9a\0" : "9ab\0", 4);
+ if (strlen (a) != 11)
+ abort ();
+}
+
+NOIPA void test_strcpy_strcat_lengthen ()
+{
+ strcpy (a, "12345678");
+ strcat (a, "9a");
+ memcpy (a + 8, i ? "9a\0" : "9ab\0", 4);
+ if (strlen (a) != 11)
+ abort ();
+}
+
+NOIPA void test_assign_lengthen ()
+{
+ memcpy (a, s, 8);
+ memcpy (a + 6, i ? "78\0" : "789\0", 4);
+ if (strlen (a) != 9)
+ abort ();
+}
+
+NOIPA void test_memcpy_shorten ()
+{
+ memcpy (a, "123456789a", 11);
+ memcpy (a + 6, i ? "789\0" : "78\0", 4);
+ if (strlen (a) != 8)
+ abort ();
+}
+
+NOIPA void test_strcpy_strcat_shorten ()
+{
+ strcpy (a, "12345678");
+ strcat (a, "9a");
+ memcpy (a + 6, i ? "789\0" : "78\0", 4);
+ if (strlen (a) != 8)
+ abort ();
+}
+
+NOIPA void test_assign_shorten ()
+{
+ memcpy (a, s, 8);
+ memcpy (a + 6, i ? "789\0" : "78\0", 4);
+ if (strlen (a) != 8)
+ abort ();
+}
+
+
+int main (void)
+{
+ test_memcpy_same_length ();
+ test_strcpy_strcat_same_length ();
+ test_assign_same_length ();
+
+ test_memcpy_lengthen ();
+ test_strcpy_strcat_lengthen ();
+ test_assign_lengthen ();
+
+ test_memcpy_shorten ();
+ test_strcpy_strcat_shorten ();
+ test_assign_shorten ();
+}
--- /dev/null
+/* PR tree-optimization/91294 - strlen result of a conditional with
+ an offset
+ { dg-do run }
+ { dg-options "-O2 -Wall" } */
+
+#include "strlenopt.h"
+
+#define NOIPA __attribute__ ((noclone, noinline, noipa))
+
+#define assert(expr) \
+ ((expr) \
+ ? (void)0 \
+ : (__builtin_printf ("line %i %s: assertion failed: %s\n", \
+ __LINE__, __func__, #expr), \
+ __builtin_abort ()))
+
+int i = 0;
+
+const char s[] = "1234567";
+
+char a[32];
+
+NOIPA void lower_bound_assign_into_empty (void)
+{
+ a[0] = '1';
+ a[1] = '2';
+ a[2] = '3';
+ assert (strlen (a) == 3);
+}
+
+NOIPA void lower_bound_assign_into_longest (void)
+{
+ a[0] = '1';
+ a[1] = '2';
+ a[2] = '3';
+ assert (strlen (a) == 31);
+}
+
+
+NOIPA void lower_bound_assign_into_empty_idx_3 (int idx)
+{
+ a[0] = '1';
+ a[1] = '2';
+ a[2] = '3';
+ a[idx] = 'x';
+ assert (strlen (a) == 4);
+}
+
+NOIPA void lower_bound_assign_into_longest_idx_2 (int idx)
+{
+ a[0] = '1';
+ a[1] = '2';
+ a[2] = '3';
+ a[idx] = '\0';
+ assert (strlen (a) == 2);
+}
+
+
+NOIPA void lower_bound_memcpy_into_empty (void)
+{
+ memcpy (a, "123", 3);
+ assert (strlen (a) == 3);
+}
+
+NOIPA void lower_bound_memcpy_into_longest (void)
+{
+ memcpy (a, "123", 3);
+ assert (strlen (a) == 31);
+}
+
+
+NOIPA void lower_bound_memcpy_memcpy_into_empty (void)
+{
+ memcpy (a, "123", 3);
+ memcpy (a + 2, "345", 3);
+ assert (strlen (a) == 5);
+}
+
+NOIPA void lower_bound_memcpy_memcpy_into_longest (void)
+{
+ memcpy (a, "123", 3);
+ memcpy (a + 2, "345", 3);
+ assert (strlen (a) == 31);
+}
+
+
+NOIPA void memove_forward_strlen (void)
+{
+ char a[] = "123456";
+
+ memmove (a, a + 1, sizeof a - 1);
+
+ assert (strlen (a) == 5);
+}
+
+NOIPA void memove_backward_into_empty_strlen (void)
+{
+ strcpy (a, "123456");
+
+ memmove (a + 1, a, 6);
+
+ assert (strlen (a) == 7);
+}
+
+NOIPA void memove_backward_into_longest_strlen (void)
+{
+ memcpy (a, "123456", 6);
+
+ memmove (a + 1, a, 6);
+
+ assert (strlen (a) == 31);
+}
+
+NOIPA void memove_strcmp (void)
+{
+ /* Test derived from libstdc++-v3's
+ 20_util/specialized_algorithms/memory_management_tools/1.cc */
+
+ char a[] = "123456";
+ char b[] = "000000";
+
+ memmove (b, a, sizeof a);
+
+ assert (strlen (a) == 6);
+ assert (strlen (b) == 6);
+ assert (strcmp (a, b) == 0);
+}
+
+
+int main (void)
+{
+ memset (a, '\0', sizeof a);
+ lower_bound_assign_into_empty ();
+
+ memset (a, 'x', sizeof a - 1);
+ a[sizeof a - 1] = '\0';
+ lower_bound_assign_into_longest ();
+
+ memset (a, '\0', sizeof a);
+ lower_bound_assign_into_empty_idx_3 (3);
+
+ memset (a, 'x', sizeof a - 1);
+ a[sizeof a - 1] = '\0';
+ lower_bound_assign_into_longest_idx_2 (2);
+
+ memset (a, '\0', sizeof a);
+ lower_bound_memcpy_into_empty ();
+
+ memset (a, 'x', sizeof a - 1);
+ a[sizeof a - 1] = '\0';
+ lower_bound_memcpy_into_longest ();
+
+ memset (a, 'x', sizeof a - 1);
+ a[sizeof a - 1] = '\0';
+ lower_bound_memcpy_into_longest ();
+
+ memset (a, '\0', sizeof a);
+ lower_bound_memcpy_memcpy_into_empty ();
+
+ memset (a, 'x', sizeof a - 1);
+ a[sizeof a - 1] = '\0';
+ lower_bound_memcpy_memcpy_into_longest ();
+
+ memove_forward_strlen ();
+
+ memset (a, '\0', sizeof a);
+ memove_backward_into_empty_strlen ();
+
+ memset (a, 'x', sizeof a - 1);
+ a[sizeof a - 1] = '\0';
+ memove_backward_into_longest_strlen ();
+
+ memove_strcmp ();
+}
--- /dev/null
+/* PR tree-optimization/91315 - missing strlen lower bound of a string
+ known to be at least N characters
+ { dg-do compile }
+ { dg-options "-O2 -Wall -fdump-tree-optimized" } */
+
+#include "strlenopt.h"
+
+#define CAT(x, y) x ## y
+#define CONCAT(x, y) CAT (x, y)
+#define FAILNAME(name) CONCAT (call_ ## name ##_on_line_, __LINE__)
+
+#define FAIL(name) do { \
+ extern void FAILNAME (name) (void); \
+ FAILNAME (name)(); \
+ } while (0)
+
+/* Macro to emit a call to function 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 ASSERT_ELIM(expr) \
+ if (!!(expr)) FAIL (in_true_branch_not_eliminated); else (void)0
+
+char a[32];
+
+void lower_bound_assign_1 (void)
+{
+ a[0] = '1';
+ ASSERT_ELIM (strlen (a) < 1);
+}
+
+void lower_bound_assign_2 (void)
+{
+ a[0] = '1';
+ a[1] = '2';
+ ASSERT_ELIM (strlen (a) < 2);
+}
+
+void lower_bound_assign_3 (void)
+{
+ a[0] = '1';
+ a[1] = '2';
+ a[2] = '3';
+ ASSERT_ELIM (strlen (a) < 3);
+}
+
+void lower_bound_memcpy (void)
+{
+ memcpy (a, "123", 3);
+ ASSERT_ELIM (strlen (a) < 3);
+}
+
+void lower_bound_memcpy_memcpy_2 (void)
+{
+ memcpy (a, "123", 3);
+ memcpy (a + 2, "345", 3);
+ ASSERT_ELIM (strlen (a) < 5);
+}
+
+void lower_bound_memcpy_memcpy_3 (void)
+{
+ memcpy (a, "123", 3);
+ memcpy (a + 3, "456", 3);
+ ASSERT_ELIM (strlen (a) < 6);
+}
+
+/* FIXME: Not optimized yet.
+void lower_bound_stpcpy_stpcpy_assign (void)
+{
+ *stpcpy (strcpy (a, "123"), "4567") = '8';
+ ASSERT_ELIM (strlen (a) < 8);
+}
+*/
+
+void lower_bound_strcpy_strcat_assign (void)
+{
+ strcpy (a, "123");
+ strcat (a, "45");
+ a[5] = '6';
+ ASSERT_ELIM (strlen (a) < 6);
+}
+
+/* { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated_" 0 "optimized" } } */
to constants. */
tree
-set_strlen_range (tree lhs, wide_int max, tree bound /* = NULL_TREE */)
+set_strlen_range (tree lhs, wide_int min, wide_int max,
+ tree bound /* = NULL_TREE */)
{
if (TREE_CODE (lhs) != SSA_NAME
|| !INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
return NULL_TREE;
- wide_int min = wi::zero (max.get_precision ());
-
if (bound)
{
/* For strnlen, adjust MIN and MAX as necessary. If the bound
}
}
- return set_strlen_range (lhs, max, bound);
+ wide_int min = wi::zero (max.get_precision ());
+ return set_strlen_range (lhs, min, max, bound);
}
/* Handle a strlen call. If strlen of the argument is known, replace
tree adj = fold_build2_loc (loc, MINUS_EXPR,
TREE_TYPE (lhs), lhs, old);
adjust_related_strinfos (loc, si, adj);
+ /* Use the constant minimim length as the lower bound
+ of the non-constant length. */
+ wide_int min = wi::to_wide (old);
+ wide_int max
+ = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node)) - 2;
+ set_strlen_range (lhs, min, max);
}
else
{
on success and false otherwise. */
static bool
-count_nonzero_bytes (tree exp, unsigned lenrange[3], bool *nulterm,
+count_nonzero_bytes (tree exp, unsigned HOST_WIDE_INT offset,
+ unsigned HOST_WIDE_INT nbytes,
+ unsigned lenrange[3], bool *nulterm,
bool *allnul, bool *allnonnul, ssa_name_limit_t &snlim)
{
+ int idx = get_stridx (exp);
+ if (idx > 0)
+ {
+ strinfo *si = get_strinfo (idx);
+ /* FIXME: Handle non-constant lengths in some range. */
+ if (!si || !tree_fits_shwi_p (si->nonzero_chars))
+ return false;
+
+ unsigned len = tree_to_shwi (si->nonzero_chars);
+ unsigned size = len + si->full_string_p;
+ if (size <= offset)
+ return false;
+
+ len -= offset;
+ size -= offset;
+
+ if (size < nbytes)
+ return false;
+
+ if (len < lenrange[0])
+ lenrange[0] = len;
+ if (lenrange[1] < len)
+ lenrange[1] = len;
+
+ if (!si->full_string_p)
+ *nulterm = false;
+
+ /* Since only the length of the string are known and
+ its contents, clear ALLNUL and ALLNONNUL purely on
+ the basis of the length. */
+ if (len)
+ *allnul = false;
+ else
+ *allnonnul = false;
+ return true;
+ }
+
+ if (TREE_CODE (exp) == ADDR_EXPR)
+ exp = TREE_OPERAND (exp, 0);
+
if (TREE_CODE (exp) == SSA_NAME)
{
/* Handle a single-character specially. */
(even if its exact value is not known) and if so, recurse
once to set the range, etc. */
if (tree_expr_nonzero_p (exp))
- return count_nonzero_bytes (build_int_cst (type, 1), lenrange,
+ return count_nonzero_bytes (build_int_cst (type, 1),
+ offset, nbytes, lenrange,
nulterm, allnul, allnonnul, snlim);
/* Don't know whether EXP is or isn't nonzero. */
return false;
for (unsigned i = 0; i != n; i++)
{
tree def = gimple_phi_arg_def (stmt, i);
- if (!count_nonzero_bytes (def, lenrange, nulterm, allnul, allnonnul,
- snlim))
+ if (!count_nonzero_bytes (def, offset, nbytes, lenrange, nulterm,
+ allnul, allnonnul, snlim))
return false;
}
return true;
}
- /* Offset from the beginning of the representation bytes, a pointer
- to the representation, and the number of bytes of the representation
- to consider (may be less than the object size for MEM_REF). */
- unsigned HOST_WIDE_INT offset = 0;
- const char *prep = NULL;
- unsigned nbytes = 0;
-
if (TREE_CODE (exp) == MEM_REF)
{
- /* If the MEM_REF operand is the address of an object such as
- a string or integer, extract it and the offset into it. */
tree arg = TREE_OPERAND (exp, 0);
- if (TREE_CODE (arg) != ADDR_EXPR)
- return false;
-
tree off = TREE_OPERAND (exp, 1);
+
if (TREE_CODE (off) != INTEGER_CST
|| !tree_fits_uhwi_p (off))
return false;
- offset = tree_to_uhwi (off);
+ unsigned HOST_WIDE_INT wioff = tree_to_uhwi (off);
+ if (INT_MAX < wioff)
+ return false;
+
+ offset += wioff;
if (INT_MAX < offset)
return false;
tree type = TREE_TYPE (exp);
if (tree typesize = TYPE_SIZE_UNIT (type))
nbytes = tree_to_uhwi (typesize);
+ else
+ return false;
- if (offset == 0 && TREE_CODE (exp) != STRING_CST)
- {
- int idx = get_stridx (arg);
- if (idx > 0)
- {
- strinfo *si = get_strinfo (idx);
- if (si && tree_fits_shwi_p (si->nonzero_chars))
- {
- unsigned len = tree_to_shwi (si->nonzero_chars);
- if (len < lenrange[0])
- lenrange[0] = len;
- if (lenrange[1] < len)
- lenrange[1] = len;
-
- if (!si->full_string_p)
- *nulterm = false;
-
- /* Since only the length of the string are known and
- its contents, clear ALLNUL and ALLNONNUL purely on
- the basis of the length. */
- if (len)
- *allnul = false;
- else
- *allnonnul = false;
- return true;
- }
- }
- }
-
- /* Proceed to extract the object representation below. */
- exp = TREE_OPERAND (arg, 0);
+ /* Handle MEM_REF = SSA_NAME types of assignments. */
+ return count_nonzero_bytes (arg, offset, nbytes, lenrange, nulterm,
+ allnul, allnonnul, snlim);
}
if (TREE_CODE (exp) == VAR_DECL && TREE_READONLY (exp))
return false;
}
+ const char *prep = NULL;
if (TREE_CODE (exp) == STRING_CST)
{
- /* Set PREP and NBYTES to the string representation. */
- gcc_assert (offset <= INT_MAX);
+ unsigned nchars = TREE_STRING_LENGTH (exp);
+ if (nchars < offset)
+ return false;
if (!nbytes)
- {
- /* Unless NBYTES has already been determined above from
- MEM_REF, set it here. It includes all internal nuls,
- including the terminating one if the string has one. */
- nbytes = TREE_STRING_LENGTH (exp);
- if (nbytes <= offset)
- return false;
- }
+ /* If NBYTES hasn't been determined earlier from MEM_REF,
+ set it here. It includes all internal nuls, including
+ the terminating one if the string has one. */
+ nbytes = nchars - offset;
prep = TREE_STRING_POINTER (exp) + offset;
}
unsigned char buf[256];
if (!prep)
{
- /* Try to extract the representation of the constant object. */
- nbytes = native_encode_expr (exp, buf, sizeof buf, -1);
+ /* If the pointer to representation hasn't been set above
+ for STRING_CST point it at the buffer. */
+ prep = reinterpret_cast <char *>(buf);
+ /* Try to extract the representation of the constant object
+ or expression starting from the offset. */
+ nbytes = native_encode_expr (exp, buf, sizeof buf, offset);
if (!nbytes)
return false;
-
- prep = reinterpret_cast <char *>(buf);
}
/* Compute the number of leading nonzero bytes in the representation
*allnonnul = true;
ssa_name_limit_t snlim;
- return count_nonzero_bytes (exp, lenrange, nulterm, allnul, allnonnul, snlim);
+ return count_nonzero_bytes (exp, 0, 0, lenrange, nulterm, allnul, allnonnul,
+ snlim);
}
/* Handle a single or multibyte store other than by a built-in function,
if (tree dstsize = compute_objsize (lhs, 1))
if (compare_tree_int (dstsize, lenrange[2]) < 0)
- warning_n (gimple_location (stmt), OPT_Wstringop_overflow_,
- lenrange[2],
- "%Gwriting %u byte into a region of size %E",
- "%Gwriting %u bytes into a region of size %E",
- stmt, lenrange[2], dstsize);
+ {
+ location_t loc = gimple_nonartificial_location (stmt);
+ warning_n (loc, OPT_Wstringop_overflow_,
+ lenrange[2],
+ "%Gwriting %u byte into a region of size %E",
+ "%Gwriting %u bytes into a region of size %E",
+ stmt, lenrange[2], dstsize);
+ }
}
else
{
}
else
si->nonzero_chars = build_int_cst (size_type_node, offset);
- si->full_string_p = full_string_p;
+
+ /* Set FULL_STRING_P only if the length of the strings being
+ written is the same, and clear it if the strings have
+ different lengths. In the latter case the length stored
+ in si->NONZERO_CHARS becomes the lower bound.
+ FIXME: Handle the upper bound of the length if possible. */
+ si->full_string_p = full_string_p && lenrange[0] == lenrange[1];
+
if (storing_all_zeros_p
&& ssaname
&& !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ssaname))
if (idx != 0)
{
tree ptr = (ssaname ? ssaname : build_fold_addr_expr (lhs));
- HOST_WIDE_INT slen = (storing_all_zeros_p
- ? 0
- : (storing_nonzero_p
- && ranges_valid ? lenrange[0] : -1));
+
+ HOST_WIDE_INT slen;
+ if (storing_all_zeros_p)
+ slen = 0;
+ else if (storing_nonzero_p && ranges_valid)
+ {
+ /* FIXME: Handle the upper bound of the length when
+ LENRANGE[0] != LENRANGE[1]. */
+ slen = lenrange[0];
+ if (lenrange[0] != lenrange[1])
+ /* Set the minimum length but ignore the maximum
+ for now. */
+ full_string_p = false;
+ }
+ else
+ slen = -1;
+
tree len = (slen <= 0
? size_zero_node
: build_int_cst (size_type_node, slen));
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);
+extern tree set_strlen_range (tree, wide_int, wide_int, tree = NULL_TREE);
#endif // GCC_TREE_SSA_STRLEN_H