+2019-10-09 Martin Sebor <msebor@redhat.com>
+
+ PR tree-optimization/90879
+ * builtins.c (check_access): Avoid using maxbound when null.
+ * calls.c (maybe_warn_nonstring_arg): Adjust to get_range_strlen change.
+ * doc/invoke.texi (-Wstring-compare): Document new warning option.
+ * gimple-fold.c (get_range_strlen_tree): Make setting maxbound
+ conditional.
+ (get_range_strlen): Overwrite initial maxbound when non-null.
+ * gimple-ssa-sprintf.c (get_string_length): Adjust to get_range_strlen
+ changes.
+ * tree-ssa-strlen.c (maybe_diag_stxncpy_trunc): Same.
+ (used_only_for_zero_equality): New function.
+ (handle_builtin_memcmp): Call it.
+ (determine_min_objsize): Return an integer instead of tree.
+ (get_len_or_size, strxcmp_eqz_result): New functions.
+ (maybe_warn_pointless_strcmp): New function.
+ (handle_builtin_string_cmp): Call it. Fold zero-equality of strcmp
+ between a longer string and a smaller array.
+ (get_range_strlen_dynamic): Overwrite initial maxbound when non-null.
+
2019-10-09 Iain Sandoe <iain@sandoe.co.uk>
* config/darwin.c (darwin_override_options): Make the check for
c_strlen_data lendata = { };
get_range_strlen (srcstr, &lendata, /* eltsize = */ 1);
range[0] = lendata.minlen;
- range[1] = lendata.maxbound;
+ range[1] = lendata.maxbound ? lendata.maxbound : lendata.maxlen;
if (range[0] && (!maxread || TREE_CODE (maxread) == INTEGER_CST))
{
if (maxread && tree_int_cst_le (maxread, range[0]))
+2019-10-09 Martin Sebor <msebor@redhat.com>
+
+ PR tree-optimization/90879
+ * c.opt (-Wstring-compare): New option.
+
2019-10-08 Andrew Sutton <asutton@lock3software.com>
Jason Merrill <jason@redhat.com>
C ObjC C++ ObjC++ Var(warn_sizeof_array_argument) Warning Init(1)
Warn when sizeof is applied on a parameter declared as an array.
+Wstring-compare
+C ObjC C++ LTO ObjC++ Warning Var(warn_string_compare) Warning LangEnabledBy(C ObjC C++ ObjC++, Wextra)
+Warn about calls to strcmp and strncmp used in equality expressions that
+are necessarily true or false due to the length of one and size of the other
+argument.
+
Wstringop-overflow
C ObjC C++ LTO ObjC++ Warning Alias(Wstringop-overflow=, 2, 0)
Warn about buffer overflow in string manipulation functions like memcpy
if (!get_attr_nonstring_decl (arg))
{
c_strlen_data lendata = { };
+ /* Set MAXBOUND to an arbitrary non-null non-integer
+ node as a request to have it set to the length of
+ the longest string in a PHI. */
+ lendata.maxbound = arg;
get_range_strlen (arg, &lendata, /* eltsize = */ 1);
maxlen = lendata.maxbound;
}
if (!get_attr_nonstring_decl (arg))
{
c_strlen_data lendata = { };
+ /* Set MAXBOUND to an arbitrary non-null non-integer
+ node as a request to have it set to the length of
+ the longest string in a PHI. */
+ lendata.maxbound = arg;
get_range_strlen (arg, &lendata, /* eltsize = */ 1);
maxlen = lendata.maxbound;
}
-Wsizeof-pointer-memaccess -Wsizeof-array-argument @gol
-Wstack-protector -Wstack-usage=@var{byte-size} -Wstrict-aliasing @gol
-Wstrict-aliasing=n -Wstrict-overflow -Wstrict-overflow=@var{n} @gol
+-Wstring-compare @gol
-Wstringop-overflow=@var{n} -Wstringop-truncation -Wsubobject-linkage @gol
-Wsuggest-attribute=@r{[}pure@r{|}const@r{|}noreturn@r{|}format@r{|}malloc@r{]} @gol
-Wsuggest-final-types @gol -Wsuggest-final-methods -Wsuggest-override @gol
-Wold-style-declaration @r{(C only)} @gol
-Woverride-init @gol
-Wsign-compare @r{(C only)} @gol
+-Wstring-compare @gol
-Wredundant-move @r{(only for C++)} @gol
-Wtype-limits @gol
-Wuninitialized @gol
false positives.
@end table
+@item -Wstring-compare
+@opindex Wstring-compare
+@opindex Wno-string-compare
+Warn for calls to @code{strcmp} and @code{strncmp} whose result is
+determined to be either zero or non-zero in tests for such equality
+owing to the length of one argument being greater than the size of
+the array the other argument is stored in (or the bound in the case
+of @code{strncmp}). Such calls could be mistakes. For example,
+the call to @code{strcmp} below is diagnosed because its result is
+necessarily non-zero irrespective of the contents of the array @code{a}.
+
+@smallexample
+extern char a[4];
+void f (char *d)
+@{
+ strcpy (d, "string");
+ @dots{}
+ if (0 == strcmp (a, d)) // cannot be true
+ puts ("a and d are the same");
+@}
+@end smallexample
+
+@option{-Wstring-compare} is enabled by @option{-Wextra}.
+
@item -Wstringop-overflow
@itemx -Wstringop-overflow=@var{type}
@opindex Wstringop-overflow
}
}
+ /* Set if VAL represents the maximum length based on array size (set
+ when exact length cannot be determined). */
+ bool maxbound = false;
+
if (!val && rkind == SRK_LENRANGE)
{
if (TREE_CODE (arg) == ADDR_EXPR)
pdata->minlen = ssize_int (0);
}
}
+ maxbound = true;
}
if (!val)
&& tree_int_cst_lt (val, pdata->minlen)))
pdata->minlen = val;
- if (pdata->maxbound)
+ if (pdata->maxbound && TREE_CODE (pdata->maxbound) == INTEGER_CST)
{
/* Adjust the tighter (more optimistic) string length bound
if necessary and proceed to adjust the more conservative
bound. */
if (TREE_CODE (val) == INTEGER_CST)
{
- if (TREE_CODE (pdata->maxbound) == INTEGER_CST)
- {
- if (tree_int_cst_lt (pdata->maxbound, val))
- pdata->maxbound = val;
- }
- else
- pdata->maxbound = build_all_ones_cst (size_type_node);
+ if (tree_int_cst_lt (pdata->maxbound, val))
+ pdata->maxbound = val;
}
else
pdata->maxbound = val;
}
- else
+ else if (pdata->maxbound || maxbound)
+ /* Set PDATA->MAXBOUND only if it either isn't INTEGER_CST or
+ if VAL corresponds to the maximum length determined based
+ on the type of the object. */
pdata->maxbound = val;
if (tight_bound)
/* Try to obtain the range of the lengths of the string(s) referenced
by ARG, or the size of the largest array ARG refers to if the range
- of lengths cannot be determined, and store all in *PDATA. ELTSIZE
- is the expected size of the string element in bytes: 1 for char and
+ of lengths cannot be determined, and store all in *PDATA which must
+ be zero-initialized on input except PDATA->MAXBOUND may be set to
+ a non-null tree node other than INTEGER_CST to request to have it
+ set to the length of the longest string in a PHI. ELTSIZE is
+ the expected size of the string element in bytes: 1 for char and
some power of 2 for wide characters.
Return true if the range [PDATA->MINLEN, PDATA->MAXLEN] is suitable
for optimization. Returning false means that a nonzero PDATA->MINLEN
get_range_strlen (tree arg, c_strlen_data *pdata, unsigned eltsize)
{
bitmap visited = NULL;
+ tree maxbound = pdata->maxbound;
if (!get_range_strlen (arg, &visited, SRK_LENRANGE, pdata, eltsize))
{
else if (!pdata->minlen)
pdata->minlen = ssize_int (0);
- /* Unless its null, leave the more conservative MAXBOUND unchanged. */
- if (!pdata->maxbound)
- pdata->maxbound = pdata->maxlen;
+ /* If it's unchanged from it initial non-null value, set the conservative
+ MAXBOUND to SIZE_MAX. Otherwise leave it null (if it is null). */
+ if (maxbound && pdata->maxbound == maxbound)
+ pdata->maxbound = build_all_ones_cst (size_type_node);
if (visited)
BITMAP_FREE (visited);
if (!str)
return fmtresult ();
- /* Try to determine the dynamic string length first. */
+ /* Try to determine the dynamic string length first.
+ Set MAXBOUND to an arbitrary non-null non-integer node as a request
+ to have it set to the length of the longest string in a PHI. */
c_strlen_data lendata = { };
+ lendata.maxbound = str;
if (eltsize == 1)
get_range_strlen_dynamic (str, &lendata, vr);
else
get_range_strlen (str, &lendata, eltsize);
}
- /* LENDATA.MAXBOUND is null when LENDATA.MIN corresponds to the shortest
- string referenced by STR. Otherwise, if it's not equal to .MINLEN it
- corresponds to the bound of the largest array STR refers to, if known,
- or it's SIZE_MAX otherwise. */
+ /* If LENDATA.MAXBOUND is not equal to .MINLEN it corresponds to the bound
+ of the largest array STR refers to, if known, or it's set to SIZE_MAX
+ otherwise. */
/* Return the default result when nothing is known about the string. */
- if (lendata.maxbound)
+ if ((lendata.maxbound && !tree_fits_uhwi_p (lendata.maxbound))
+ || !tree_fits_uhwi_p (lendata.maxlen))
{
- if (integer_all_onesp (lendata.maxbound)
- && integer_all_onesp (lendata.maxlen))
- return fmtresult ();
-
- if (!tree_fits_uhwi_p (lendata.maxbound)
- || !tree_fits_uhwi_p (lendata.maxlen))
- return fmtresult ();
-
- unsigned HOST_WIDE_INT lenmax = tree_to_uhwi (max_object_size ()) - 2;
- if (lenmax <= tree_to_uhwi (lendata.maxbound)
- && lenmax <= tree_to_uhwi (lendata.maxlen))
- return fmtresult ();
+ fmtresult res;
+ res.nonstr = lendata.decl;
+ return res;
+ }
+
+ unsigned HOST_WIDE_INT lenmax = tree_to_uhwi (max_object_size ()) - 2;
+ if (integer_zerop (lendata.minlen)
+ && (!lendata.maxbound || lenmax <= tree_to_uhwi (lendata.maxbound))
+ && lenmax <= tree_to_uhwi (lendata.maxlen))
+ {
+ fmtresult res;
+ res.nonstr = lendata.decl;
+ return res;
}
HOST_WIDE_INT min
{
/* When the upper bound is unknown (it can be zero or excessive)
set the likely length to the greater of 1. If MAXBOUND is
- set, also reset the length of the lower bound to zero. */
+ known, also reset the length of the lower bound to zero. */
res.range.likely = res.range.min ? res.range.min : warn_level > 1;
- if (lendata.maxbound)
+ if (lendata.maxbound && !integer_all_onesp (lendata.maxbound))
res.range.min = 0;
}
+2019-10-09 Martin Sebor <msebor@redhat.com>
+
+ PR tree-optimization/90879
+ * gcc.dg/Wstring-compare-2.c: New test.
+ * gcc.dg/Wstring-compare.c: New test.
+ * gcc.dg/strcmpopt_3.c: Scan the optmized dump instead of strlen.
+ * gcc.dg/strcmpopt_6.c: New test.
+ * gcc.dg/strlenopt-65.c: Remove uinnecessary declarations, add
+ test cases.
+ * gcc.dg/strlenopt-66.c: Run it.
+ * gcc.dg/strlenopt-68.c: New test.
+
2019-10-09 Marek Polacek <polacek@redhat.com>
PR c++/91364 - P0388R4: Permit conversions to arrays of unknown bound.
--- /dev/null
+/* PR tree-optimization/90879 - fold zero-equality of strcmp between
+ a longer string and a smaller array
+ Test for a warning for strcmp of a longer string against smaller
+ array.
+ { dg-do compile }
+ { dg-options "-O2 -Wall -Wstring-compare -Wno-stringop-truncation -ftrack-macro-expansion=0" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void* memcpy (void*, const void*, size_t);
+
+extern int strcmp (const char*, const char*);
+extern size_t strlen (const char*);
+extern char* strcpy (char*, const char*);
+extern char* strncpy (char*, const char*, size_t);
+extern int strncmp (const char*, const char*, size_t);
+
+void sink (int, ...);
+#define sink(...) sink (__LINE__, __VA_ARGS__)
+
+
+extern char a1[1], a2[2], a3[3], a4[4], a5[5], a6[6], a7[7], a8[8], a9[9];
+
+#define T(a, b) sink (0 == strcmp (a, b))
+
+
+void test_string_cst (void)
+{
+ const char *s1 = "1", *s2 = "12";
+
+ T (s1, a1); // { dg-warning ".strcmp. of a string of length 1 and an array of size 1 evaluates to nonzero" }
+ T (s1, a2);
+ T (s1, a3);
+
+ T (a1, s1); // { dg-warning ".strcmp. of a string of length 1 and an array of size 1 evaluates to nonzero" }
+ T (a2, s1);
+ T (a3, s1);
+
+ T (s2, a1); // { dg-warning ".strcmp. of a string of length 2 and an array of size 1 evaluates to nonzero" }
+ T (s2, a2); // { dg-warning ".strcmp. of a string of length 2 and an array of size 2 evaluates to nonzero" }
+ T (s2, a3);
+
+ T (a1, s2); // { dg-warning ".strcmp. of a string of length 2 and an array of size 1 evaluates to nonzero" }
+ T (a2, s2); // { dg-warning ".strcmp. of a string of length 2 and an array of size 2 evaluates to nonzero" }
+ T (a3, s2);
+}
+
+
+void test_string_cst_off_cst (void)
+{
+ const char *s1 = "1", *s2 = "12", *s3 = "123", *s4 = "1234";
+
+ T (s1, a2 + 1); // { dg-warning ".strcmp. of a string of length 1 and an array of size 1 evaluates to nonzero" }
+ T (a2 + 1, s1); // { dg-warning ".strcmp. of a string of length 1 and an array of size 1 evaluates to nonzero" }
+
+
+ T (s3 + 1, a2); // { dg-warning ".strcmp. of a string of length 2 and an array of size 2 evaluates to nonzero" }
+ T (s3 + 1, a3);
+
+ T (s2, a4 + 1);
+ T (s2, a4 + 2); // { dg-warning ".strcmp. of a string of length 2 and an array of size 2 evaluates to nonzero" }
+
+ T (s4, a4 + 1); // { dg-warning ".strcmp. of a string of length 4 and an array of size 3 evaluates to nonzero" }
+ T (s3, a5 + 1);
+}
+
+
+/* Use strncpy below rather than memcpy until PR 91183 is resolved. */
+
+#undef T
+#define T(s, n, a) \
+ do { \
+ char arr[32]; \
+ sink (arr); \
+ strncpy (arr, s, n < 0 ? strlen (s) + 1: n); \
+ sink (0 == strcmp (arr, a)); \
+ } while (0)
+
+void test_string_exact_length (void)
+{
+ const char *s1 = "1", *s2 = "12";
+
+ T (s1, -1, a1); // { dg-warning ".strcmp. of a string of length 1 and an array of size 1 evaluates to nonzero" }
+ T (s1, -1, a2);
+ T (s1, -1, a3);
+
+ T (s2, -1, a1); // { dg-warning ".strcmp. of a string of length 2 and an array of size 1 evaluates to nonzero" }
+ T (s2, -1, a2); // { dg-warning ".strcmp. of a string of length 2 and an array of size 2 evaluates to nonzero" }
+ T (s2, -1, a3);
+}
+
+
+void test_string_min_length (void)
+{
+ const char *s1 = "1", *s2 = "12";
+
+ T (s1, 1, a1); // { dg-warning ".strcmp. of a string of length 1 or more and an array of size 1 evaluates to nonzero" }
+ T (s1, 1, a2);
+ T (s1, 1, a3);
+
+ T (s2, 2, a1); // { dg-warning ".strcmp. of a string of length 2 or more and an array of size 1 evaluates to nonzero" }
+ T (s2, 2, a2); // { dg-warning ".strcmp. of a string of length 2 or more and an array of size 2 evaluates to nonzero" }
+ T (s2, 2, a3);
+}
+
+
+int test_strncmp_str_lit_var (const char *s, long n)
+{
+ if (strncmp (s, "123456", n) == 0) // { dg-bogus "\\\[-Wstring-compare" }
+ return 1;
+
+ return 0;
+}
+
+int test_strlen_strncmp_str_lit_var (const char *s, long n)
+{
+ if (__builtin_strlen (s) < n)
+ return -1;
+
+ if (n == 6)
+ if (strncmp (s, "123456", n) == 0) // { dg-bogus "\\\[-Wstring-compare" }
+ return 1;
+
+ return 0;
+}
+
+
--- /dev/null
+/* PR tree-optimization/90879 - fold zero-equality of strcmp between
+ a longer string and a smaller array
+ { dg-do compile }
+ { dg-options "-O2 -Wall -Wextra -ftrack-macro-expansion=0" } */
+
+#include "strlenopt.h"
+
+#define T(a, b) sink (0 == strcmp (a, b), a, b)
+
+void sink (int, ...);
+
+struct S { char a4[4], c; };
+
+extern char a4[4];
+extern char a5[5];
+extern char b4[4];
+
+/* Verify that comparison of string literals with arrays with unknown
+ content but size that prevents them from comparing equal is diagnosed. */
+
+void strcmp_array_lit (void)
+{
+ if (strcmp (a4, "1234")) // { dg-warning "'strcmp' of a string of length 4 and an array of size 4 evaluates to nonzero" }
+ // { dg-bogus "in this expreession" "unwanted note" { target *-*-* } .-1 }
+ sink (0, a4);
+
+ int cmp;
+ cmp = strcmp (a4, "1234"); // { dg-warning "'strcmp' of a string of length 4 and an array of size 4 evaluates to nonzero" }
+ if (cmp) // { dg-message "in this expression" }
+ sink (0, a4);
+
+ T (a4, "4321"); // { dg-warning "'strcmp' of a string of length 4 and an array of size 4 evaluates to nonzero " }
+ T (a4, "12345"); // { dg-warning "length 5 and an array of size 4 " }
+ T (a4, "123456"); // { dg-warning "length 6 and an array of size 4 " }
+ T ("1234", a4); // { dg-warning "length 4 and an array of size 4 " }
+ T ("12345", a4); // { dg-warning "length 5 and an array of size 4 " }
+ T ("123456", a4); // { dg-warning "length 6 and an array of size 4 " }
+}
+
+
+void strcmp_array_pstr (void)
+{
+ const char *s4 = "1234";
+
+ {
+ if (strcmp (a4, s4)) // { dg-warning "'strcmp' of a string of length 4 and an array of size 4 evaluates to nonzero" }
+ // { dg-bogus "in this expreession" "unwanted note" { target *-*-* } .-1 }
+ sink (1, a4);
+ else
+ sink (0, a4);
+ }
+
+ {
+ int c;
+ c = strcmp (a4, s4); // { dg-warning "'strcmp' of a string of length 4 and an array of size 4 evaluates to nonzero" }
+ if (c) // { dg-message "in this expression" }
+ sink (1, a4);
+ else
+ sink (0, a4);
+ }
+
+ const char *t4 = "4321";
+ const char *s5 = "12345";
+ const char *s6 = "123456";
+
+ T (a4, t4); // { dg-warning "'strcmp' of a string of length 4 and an array of size 4 evaluates to nonzero " }
+ T (a4, s5); // { dg-warning "length 5 and an array of size 4 " }
+ T (a4, s6); // { dg-warning "length 6 and an array of size 4 " }
+ T (s4, a4); // { dg-warning "length 4 and an array of size 4 " }
+ T (s5, a4); // { dg-warning "length 5 and an array of size 4 " }
+ T (s6, a4); // { dg-warning "length 6 and an array of size 4 " }
+}
+
+
+void strcmp_array_cond_pstr (int i)
+{
+ const char *s4 = i ? "1234" : "4321";
+ T (a4, s4); // { dg-warning "'strcmp' of a string of length 4 and an array of size 4 evaluates to nonzero " }
+ T (a5, s4);
+}
+
+void strcmp_array_copy (void)
+{
+ char s[8];
+
+ {
+ strcpy (s, "1234");
+ if (strcmp (a4, s)) // { dg-warning "'strcmp' of a string of length 4 and an array of size 4 evaluates to nonzero" }
+ // { dg-bogus "in this expreession" "unwanted note" { target *-*-* } .-1 }
+ sink (1, a4);
+ else
+ sink (0, a4);
+ }
+
+ {
+ strcpy (s, "1234");
+
+ int c;
+ c = strcmp (a4, s); // { dg-warning "'strcmp' of a string of length 4 and an array of size 4 evaluates to nonzero" }
+ if (c) // { dg-message "in this expression" }
+ sink (1, a4);
+ else
+ sink (0, a4);
+ }
+
+ strcpy (s, "4321");
+ T (a4, s); // { dg-warning "'strcmp' of a string of length 4 and an array of size 4 evaluates to nonzero " }
+ strcpy (s, "12345");
+ T (a4, s); // { dg-warning "length 5 and an array of size 4 " }
+ strcpy (s, "123456");
+ T (a4, s); // { dg-warning "length 6 and an array of size 4 " }
+ strcpy (s, "4321");
+ T (s, a4); // { dg-warning "length 4 and an array of size 4 " }
+ strcpy (s, "54321");
+ T (s, a4); // { dg-warning "length 5 and an array of size 4 " }
+ strcpy (s, "654321");
+ T (s, a4); // { dg-warning "length 6 and an array of size 4 " }
+}
+
+
+void strcmp_member_array_lit (const struct S *p)
+{
+ T (p->a4, "1234"); // { dg-warning "length 4 and an array of size 4 " }
+}
+
+
+#undef T
+#define T(a, b, n) sink (0 == strncmp (a, b, n), a, b)
+
+void strncmp_array_lit (void)
+{
+ if (strncmp (a4, "12345", 5)) // { dg-warning "'strncmp' of a string of length 5, an array of size 4 and bound of 5 evaluates to nonzero" }
+ // { dg-bogus "in this expreession" "unwanted note" { target *-*-* } .-1 }
+ sink (0, a4);
+
+ int cmp;
+ cmp = strncmp (a4, "54321", 5); // { dg-warning "'strncmp' of a string of length 5, an array of size 4 and bound of 5 evaluates to nonzero" }
+ if (cmp) // { dg-message "in this expression" }
+ sink (0, a4);
+
+ // Verify no warning when the bound is the same as the array size.
+ T (a4, "4321", 4);
+ T (a4, "654321", 4);
+
+ T (a4, "12345", 5); // { dg-warning "length 5, an array of size 4 and bound of 5 " }
+ T (a4, "123456", 6); // { dg-warning "length 6, an array of size 4 and bound of 6" }
+
+ T ("1234", a4, 4);
+ T ("12345", a4, 4);
+
+ T ("12345", a4, 5); // { dg-warning "length 5, an array of size 4 and bound of 5 " }
+ T ("123456", a4, 6); // { dg-warning "length 6, an array of size 4 and bound of 6 " }
+}
+
+
+void strncmp_strarray_copy (void)
+{
+ {
+ char a[] = "1234";
+ char b[6];
+ strcpy (b, "12345");
+ if (strncmp (a, b, 5)) // { dg-warning "'strncmp' of strings of length 4 and 5 and bound of 5 evaluates to nonzero" }
+ // { dg-bogus "in this expreession" "unwanted note" { target *-*-* } .-1 }
+ sink (0, a, b);
+ }
+
+ {
+ char a[] = "4321";
+ char b[6];
+ strcpy (b, "54321");
+ int cmp;
+ cmp = strncmp (a, b, 5); // { dg-warning "'strncmp' of strings of length 4 and 5 and bound of 5 evaluates to nonzero" }
+ if (cmp) // { dg-message "in this expression" }
+ sink (0, a, b);
+ }
+
+ strcpy (a4, "abc");
+ T (a4, "54321", 5); // { dg-warning "'strncmp' of strings of length 3 and 5 and bound of 5 evaluates to nonzero " }
+}
+
+
/* { dg-do run } */
-/* { dg-options "-O2 -fdump-tree-strlen" } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
-__attribute__ ((noinline)) int
-f1 (void)
-{
+__attribute__ ((noinline)) int
+f1 (void)
+{
char *s0= "abcd";
char s[8];
__builtin_strcpy (s, s0);
- return __builtin_strcmp(s, "abc") != 0;
+ return __builtin_strcmp (s, "abc") != 0;
}
__attribute__ ((noinline)) int
-f2 (void)
-{
+f2 (void)
+{
char *s0 = "ab";
char s[8];
__builtin_strcpy (s, s0);
- return __builtin_strcmp("abc", s) != 0;
+ return __builtin_strcmp ("abc", s) != 0;
}
int main (void)
{
- if (f1 () != 1
+ if (f1 () != 1
|| f2 () != 1)
__builtin_abort ();
return 0;
}
-/* { dg-final { scan-tree-dump-times "strcmp" 0 "strlen1" } } */
+/* { dg-final { scan-tree-dump-times "strcmp" 0 "optimized" } } */
--- /dev/null
+/* Verify that strcmp and strncmp calls with mixed constant and
+ non-constant strings are evaluated correctly.
+ { dg-do run }
+ { dg-options "-O2" } */
+
+#include "strlenopt.h"
+
+#define A(expr) \
+ ((expr) \
+ ? (void)0 \
+ : (__builtin_printf ("assertion failed on line %i: %s\n", \
+ __LINE__, #expr), \
+ __builtin_abort ()))
+
+__attribute__ ((noclone, noinline)) int
+test_strlen_gt2_strcmp_abcd (const char *s)
+{
+ if (strlen (s) < 3)
+ return -1;
+
+ return strcmp (s, "abcd") == 0;
+}
+
+__attribute__ ((noclone, noinline)) int
+test_strlen_lt6_strcmp_abcd (const char *s)
+{
+ if (strlen (s) > 5)
+ return -1;
+
+ return strcmp (s, "abcd") == 0;
+}
+
+__attribute__ ((noclone, noinline)) int
+test_strcpy_strcmp_abc (const char *s)
+{
+ char a[4];
+ strcpy (a, s);
+ return strcmp (a, "abc") == 0;
+}
+
+__attribute__ ((noclone, noinline)) int
+test_strcpy_abc_strcmp (const char *s)
+{
+ char a[4], b[6];
+ strcpy (a, "abc");
+ strcpy (b, s);
+ return strcmp (a, b) == 0;
+}
+
+/* Exercise strcmp of two strings between 1 and 3 characters long
+ stored in arrays of the same known size. */
+char ga4[4], gb4[4];
+
+__attribute__ ((noclone, noinline)) int
+test_store_0_nulterm_strcmp_same_size_arrays (void)
+{
+ ga4[0] = gb4[0] = 'x';
+ ga4[3] = gb4[3] = '\0';
+ return strcmp (ga4, gb4) == 0;
+}
+
+__attribute__ ((noclone, noinline)) int
+test_store_0_nulterm_strncmp_bound_2_same_size_arrays (void)
+{
+ ga4[0] = gb4[0] = 'x';
+ ga4[3] = gb4[3] = '\0';
+ return strncmp (ga4, gb4, 2) == 0;
+}
+
+__attribute__ ((noclone, noinline)) int
+test_store_0_nulterm_strncmp_bound_equal_same_size_arrays (void)
+{
+ ga4[0] = gb4[0] = 'x';
+ ga4[3] = gb4[3] = '\0';
+ return strncmp (ga4, gb4, 4) == 0;
+}
+
+/* Exercise strcmp of two strings between 0 and 3 characters long
+ stored in arrays of the same known size. */
+
+__attribute__ ((noclone, noinline)) int
+test_nulterm_strcmp_same_size_arrays (void)
+{
+ ga4[3] = gb4[3] = '\0';
+ return strcmp (ga4, gb4) == 0;
+}
+
+/* Exercise strcmp of two strings between 1 and 3 and 1 and 4 characters
+ long, respectively, stored in arrays of known but different sizes. */
+char gc5[5];
+
+__attribute__ ((noclone, noinline)) int
+test_store_0_nulterm_strcmp_arrays (void)
+{
+ ga4[0] = gc5[0] = 'x';
+ ga4[3] = gc5[4] = '\0';
+ return strcmp (ga4, gc5) == 0;
+}
+
+/* Exercise strcmp of two strings between 0 and 3 and 1 and 4 characters
+ long, respectively, stored in arrays of known but different sizes. */
+
+__attribute__ ((noclone, noinline)) int
+test_nulterm_strcmp_arrays (void)
+{
+ ga4[3] = gc5[4] = '\0';
+ return strcmp (ga4, gc5) == 0;
+}
+
+
+__attribute__ ((noclone, noinline)) int
+test_strcpy_strncmp_abcd (const char *s)
+{
+ char a[6];
+ strcpy (a, s);
+ return strcmp (a, "abcd") == 0;
+}
+
+__attribute__ ((noclone, noinline)) int
+test_strcpy_abcd_strncmp_3 (const char *s)
+{
+ char a[6], b[8];
+ strcpy (a, "abcd");
+ strcpy (b, s);
+ return strncmp (a, b, 3) == 0;
+}
+
+__attribute__ ((noclone, noinline)) int
+test_strcpy_abcd_strncmp_4 (const char *s)
+{
+ char a[6], b[8];
+ strcpy (a, "abcd");
+ strcpy (b, s);
+ return strncmp (a, b, 4) == 0;
+}
+
+
+int main (void)
+{
+ test_strlen_gt2_strcmp_abcd ("abcd");
+ test_strlen_lt6_strcmp_abcd ("abcd");
+
+ A (0 == test_strcpy_strcmp_abc ("ab"));
+ A (0 != test_strcpy_strcmp_abc ("abc"));
+ A (0 == test_strcpy_strcmp_abc ("abcd"));
+
+ A (0 == test_strcpy_abc_strcmp ("ab"));
+ A (0 != test_strcpy_abc_strcmp ("abc"));
+ A (0 == test_strcpy_abc_strcmp ("abcd"));
+
+ strcpy (ga4, "abc"); strcpy (gb4, "abd");
+ A (0 == test_store_0_nulterm_strcmp_same_size_arrays ());
+ strcpy (ga4, "abd"); strcpy (gb4, "abc");
+ A (0 == test_store_0_nulterm_strcmp_same_size_arrays ());
+ strcpy (ga4, "abc"); strcpy (gb4, "abc");
+ A (0 != test_store_0_nulterm_strcmp_same_size_arrays ());
+
+ strcpy (ga4, "abc"); strcpy (gb4, "acd");
+ A (0 == test_store_0_nulterm_strncmp_bound_2_same_size_arrays ());
+ strcpy (ga4, "acd"); strcpy (gb4, "abc");
+ A (0 == test_store_0_nulterm_strncmp_bound_2_same_size_arrays ());
+ strcpy (ga4, "abc"); strcpy (gb4, "abc");
+ A (0 != test_store_0_nulterm_strncmp_bound_2_same_size_arrays ());
+
+ strcpy (ga4, "abc"); strcpy (gb4, "abd");
+ A (0 == test_store_0_nulterm_strncmp_bound_equal_same_size_arrays ());
+ strcpy (ga4, "abd"); strcpy (gb4, "abc");
+ A (0 == test_store_0_nulterm_strncmp_bound_equal_same_size_arrays ());
+ strcpy (ga4, "abc"); strcpy (gb4, "abc");
+ A (0 != test_store_0_nulterm_strncmp_bound_equal_same_size_arrays ());
+
+ strcpy (ga4, "abc"); strcpy (gb4, "abd");
+ A (0 == test_nulterm_strcmp_same_size_arrays ());
+ strcpy (ga4, "abd"); strcpy (gb4, "abc");
+ A (0 == test_nulterm_strcmp_same_size_arrays ());
+ strcpy (ga4, "abc"); strcpy (gb4, "abc");
+ A (0 != test_nulterm_strcmp_same_size_arrays ());
+
+ strcpy (ga4, "abc"); strcpy (gc5, "abcd");
+ A (0 == test_store_0_nulterm_strcmp_arrays ());
+ strcpy (ga4, "abd"); strcpy (gc5, "abcd");
+ A (0 == test_store_0_nulterm_strcmp_arrays ());
+ strcpy (ga4, "abc"); strcpy (gc5, "abc");
+ A (0 != test_store_0_nulterm_strcmp_arrays ());
+
+ strcpy (ga4, "abc"); strcpy (gc5, "abcd");
+ A (0 == test_nulterm_strcmp_arrays ());
+ strcpy (ga4, "abd"); strcpy (gc5, "abc");
+ A (0 == test_nulterm_strcmp_arrays ());
+ strcpy (ga4, "abc"); strcpy (gc5, "abc");
+ A (0 != test_nulterm_strcmp_arrays ());
+
+ A (0 == test_strcpy_strncmp_abcd ("ab"));
+ A (0 == test_strcpy_strncmp_abcd ("abc"));
+ A (0 != test_strcpy_strncmp_abcd ("abcd"));
+ A (0 == test_strcpy_strncmp_abcd ("abcde"));
+
+ A (0 == test_strcpy_abcd_strncmp_3 ("ab"));
+ A (0 != test_strcpy_abcd_strncmp_3 ("abc"));
+ A (0 != test_strcpy_abcd_strncmp_3 ("abcd"));
+ A (0 != test_strcpy_abcd_strncmp_3 ("abcde"));
+
+ A (0 == test_strcpy_abcd_strncmp_4 ("ab"));
+ A (0 == test_strcpy_abcd_strncmp_4 ("abc"));
+ A (0 != test_strcpy_abcd_strncmp_4 ("abcd"));
+ A (0 != test_strcpy_abcd_strncmp_4 ("abcde"));
+}
/* PRE tree-optimization/90626 - fold strcmp(a, b) == 0 to zero when
one string length is exact and the other is unequal
{ dg-do compile }
- { dg-options "-O2 -Wall -fdump-tree-optimized" } */
+ { dg-options "-O2 -Wall -Wno-string-compare -fdump-tree-optimized -ftrack-macro-expansion=0" } */
#include "strlenopt.h"
-typedef __SIZE_TYPE__ size_t;
-
-extern void abort (void);
-extern void* memcpy (void *, const void *, size_t);
-extern int strcmp (const char *, const char *);
-extern int strncmp (const char *, const char *, size_t);
-
#define CAT(x, y) x ## y
#define CONCAT(x, y) CAT (x, y)
#define FAILNAME(name) CONCAT (call_ ## name ##_on_line_, __LINE__)
#undef CMPFUNC
#define CMPFUNC(a, b, dummy) strcmp (a, b)
- KEEP ("1", "1", a, b, -1);
+ KEEP ("123", "123\0", a, b, /* bnd = */ -1);
+ KEEP ("123\0", "123", a, b, -1);
+
+ {
+ char a[8], b[8];
+ sink (a, b);
+ strcpy (a, s);
+ strcpy (b, t);
+ TEST_KEEP (0 == strcmp (a, b));
+ }
+}
+
+
+void test_strncmp_keep (const char *s, const char *t)
+{
+#undef CMPFUNC
+#define CMPFUNC(a, b, n) strncmp (a, b, n)
+
+ KEEP ("1", "1", a, b, 2);
- KEEP ("1\0", "1", a, b, -1);
- KEEP ("1", "1\0", a, b, -1);
+ KEEP ("1\0", "1", a, b, 2);
+ KEEP ("1", "1\0", a, b, 2);
- KEEP ("12\0", "12", a, b, -1);
- KEEP ("12", "12\0", a, b, -1);
+ KEEP ("12\0", "12", a, b, 2);
+ KEEP ("12", "12\0", a, b, 2);
- KEEP ("111\0", "111", a, b, -1);
- KEEP ("112", "112\0", a, b, -1);
+ KEEP ("111\0", "111", a, b, 3);
+ KEEP ("112", "112\0", a, b, 3);
- KEEP (s, t, a, b, -1);
+ {
+ char a[8], b[8];
+ sink (a, b);
+ strcpy (a, s);
+ strcpy (b, t);
+ TEST_KEEP (0 == strncmp (a, b, sizeof a));
+ }
}
/* { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated_" 0 "optimized" } }
- { dg-final { scan-tree-dump-times "call_made_in_true_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 8 "optimized" } }
- { dg-final { scan-tree-dump-times "call_made_in_false_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 8 "optimized" } } */
+ { dg-final { scan-tree-dump-times "call_made_in_true_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 11 "optimized" } }
+ { dg-final { scan-tree-dump-times "call_made_in_false_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 11 "optimized" } } */
/* PRE tree-optimization/90626 - fold strcmp(a, b) == 0 to zero when
one string length is exact and the other is unequal
- { dg-do compile }
+ { dg-do run }
{ dg-options "-O2 -Wall -fdump-tree-optimized" } */
#include "strlenopt.h"
A (0 < strncmp (b, a, 5));
}
+
+__attribute__ ((noclone, noinline, noipa)) void
+test_strncmp_a4_cond_s5_s2_2 (const char *s, int i)
+{
+ char a4[4];
+ strcpy (a4, s);
+ A (0 == strncmp (a4, i ? "12345" : "12", 2));
+}
+
+
+__attribute__ ((noclone, noinline, noipa)) void
+test_strncmp_a4_cond_a5_s2_5 (const char *s, const char *t, int i)
+{
+ char a4[4], a5[5];
+ strcpy (a4, s);
+ strcpy (a5, t);
+ A (0 == strncmp (a4, i ? a5 : "12", 5));
+}
+
+__attribute__ ((noclone, noinline, noipa)) void
+test_strncmp_a4_cond_a5_a3_n (const char *s1, const char *s2, const char *s3,
+ int i, unsigned n)
+{
+ char a3[3], a4[4], a5[5];
+ strcpy (a3, s1);
+ strcpy (a4, s2);
+ strcpy (a5, s3);
+ A (0 == strncmp (a4, i ? a5 : a3, n));
+}
+
+
int main (void)
{
test_strcmp ();
test_strncmp ();
+ test_strncmp_a4_cond_s5_s2_2 ("12", 0);
+ test_strncmp_a4_cond_a5_s2_5 ("12", "1234", 0);
+
+ test_strncmp_a4_cond_a5_a3_n ("12", "123", "1234", 0, 2);
+ test_strncmp_a4_cond_a5_a3_n ("123", "12", "12", 1, 3);
}
--- /dev/null
+/* PR tree-optimization/90879 - fold zero-equality of strcmp between
+ a longer string and a smaller array
+ { dg-do compile }
+ { dg-options "-O2 -Wall -Wno-string-compare -fdump-tree-optimized -ftrack-macro-expansion=0" } */
+
+#include "strlenopt.h"
+
+#define A(expr) \
+ ((expr) \
+ ? (void)0 \
+ : (__builtin_printf ("assertion failed on line %i: %s\n", \
+ __LINE__, #expr), \
+ __builtin_abort ()))
+
+void clobber (void*, ...);
+
+struct S { char a4[4], c; };
+
+extern char a4[4];
+extern char b4[4];
+
+/* Verify that comparison of string literals with arrays with unknown
+ content but size that prevents them from comparing equal is folded
+ to a constant. */
+
+void test_array_lit (void)
+{
+ A (strcmp (a4, "1234")); clobber (a4);
+ A (strcmp (a4, "12345")); clobber (a4);
+ A (strcmp (a4, "123456")); clobber (a4);
+ A (strcmp ("1234", a4)); clobber (a4);
+ A (strcmp ("12345", a4)); clobber (a4);
+ A (strcmp ("123456", a4)); clobber (a4);
+}
+
+void test_memarray_lit (struct S *p)
+{
+ A (strcmp (p->a4, "1234"));
+ A (strcmp (p->a4, "12345"));
+ A (strcmp (p->a4, "123456"));
+
+ A (strcmp ("1234", p->a4));
+ A (strcmp ("12345", p->a4));
+ A (strcmp ("123456", p->a4));
+}
+
+/* Verify that the equality of empty strings is folded. */
+
+void test_empty_string (void)
+{
+ A (0 == strcmp ("", ""));
+
+ *a4 = '\0';
+ A (0 == strcmp (a4, ""));
+ A (0 == strcmp ("", a4));
+ A (0 == strcmp (a4, a4));
+
+ char s[8] = "";
+ A (0 == strcmp (a4, s));
+
+ a4[1] = '\0';
+ b4[1] = '\0';
+ A (0 == strcmp (a4 + 1, b4 + 1));
+
+ a4[2] = '\0';
+ b4[2] = '\0';
+ A (0 == strcmp (&a4[2], &b4[2]));
+
+ clobber (a4, b4);
+
+ memset (a4, 0, sizeof a4);
+ memset (b4, 0, sizeof b4);
+ A (0 == strcmp (a4, b4));
+}
+
+/* Verify that comparison of dynamically created strings with unknown
+ arrays is folded. */
+
+void test_array_copy (void)
+{
+ char s[8];
+ strcpy (s, "1234");
+ A (strcmp (a4, s));
+
+ strcpy (s, "12345");
+ A (strlen (s) == 5);
+ A (strcmp (a4, s)); clobber (a4);
+
+ strcpy (s, "123456");
+ A (strcmp (a4, s)); clobber (a4);
+
+ strcpy (s, "1234");
+ A (strcmp (s, a4)); clobber (a4);
+
+ strcpy (s, "12345");
+ A (strcmp (s, a4)); clobber (a4);
+
+ strcpy (s, "123456");
+ A (strcmp (s, a4)); clobber (a4);
+}
+
+
+void test_array_bounded (void)
+{
+ A (strncmp (a4, "12345", 5)); clobber (a4);
+ A (strncmp ("54321", a4, 5)); clobber (a4);
+
+ A (strncmp (a4, "123456", 5)); clobber (a4);
+ A (strncmp ("654321", a4, 5)); clobber (a4);
+}
+
+void test_array_copy_bounded (void)
+{
+ char s[8];
+ strcpy (s, "12345");
+ A (strncmp (a4, s, 5)); clobber (a4);
+ strcpy (s, "54321");
+ A (strncmp (s, a4, 5)); clobber (a4);
+
+ strcpy (s, "123456");
+ A (strncmp (a4, s, 5)); clobber (a4);
+ strcpy (s, "654321");
+ A (strncmp (s, a4, 5)); clobber (a4);
+}
+
+/* { dg-final { scan-tree-dump-not "abort|strcmp|strncmp" "optimized" } } */
&& tree_int_cst_lt (pdata->maxlen, argdata.maxlen)))
pdata->maxlen = argdata.maxlen;
if (!pdata->maxbound
+ || TREE_CODE (pdata->maxbound) != INTEGER_CST
|| (argdata.maxbound
&& tree_int_cst_lt (pdata->maxbound,
argdata.maxbound)
const vr_values *rvals)
{
bitmap visited = NULL;
+ tree maxbound = pdata->maxbound;
unsigned limit = PARAM_VALUE (PARAM_SSA_NAME_DEF_CHAIN_LIMIT);
if (!get_range_strlen_dynamic (src, pdata, &visited, rvals, &limit))
else if (!pdata->minlen)
pdata->minlen = ssize_int (0);
+ /* If it's unchanged from it initial non-null value, set the conservative
+ MAXBOUND to SIZE_MAX. Otherwise leave it null (if it is null). */
+ if (maxbound && pdata->maxbound == maxbound)
+ pdata->maxbound = build_all_ones_cst (size_type_node);
+
if (visited)
BITMAP_FREE (visited);
}
else
{
c_strlen_data lendata = { };
+ /* Set MAXBOUND to an arbitrary non-null non-integer node as a request
+ to have it set to the length of the longest string in a PHI. */
+ lendata.maxbound = src;
get_range_strlen (src, &lendata, /* eltsize = */1);
if (TREE_CODE (lendata.minlen) == INTEGER_CST
&& TREE_CODE (lendata.maxbound) == INTEGER_CST)
return true;
}
-/* Handle a call to memcmp. We try to handle small comparisons by
- converting them to load and compare, and replacing the call to memcmp
- with a __builtin_memcmp_eq call where possible.
- return true when call is transformed, return false otherwise. */
+/* Return a pointer to the first such equality expression if RES is used
+ only in expressions testing its equality to zero, and null otherwise. */
-static bool
-handle_builtin_memcmp (gimple_stmt_iterator *gsi)
+static gimple *
+used_only_for_zero_equality (tree res)
{
- gcall *stmt2 = as_a <gcall *> (gsi_stmt (*gsi));
- tree res = gimple_call_lhs (stmt2);
- tree arg1 = gimple_call_arg (stmt2, 0);
- tree arg2 = gimple_call_arg (stmt2, 1);
- tree len = gimple_call_arg (stmt2, 2);
- unsigned HOST_WIDE_INT leni;
+ gimple *first_use = NULL;
+
use_operand_p use_p;
imm_use_iterator iter;
- if (!res)
- return false;
-
FOR_EACH_IMM_USE_FAST (use_p, iter, res)
{
- gimple *ustmt = USE_STMT (use_p);
+ gimple *use_stmt = USE_STMT (use_p);
- if (is_gimple_debug (ustmt))
- continue;
- if (gimple_code (ustmt) == GIMPLE_ASSIGN)
+ if (is_gimple_debug (use_stmt))
+ continue;
+ if (gimple_code (use_stmt) == GIMPLE_ASSIGN)
{
- gassign *asgn = as_a <gassign *> (ustmt);
- tree_code code = gimple_assign_rhs_code (asgn);
- if ((code != EQ_EXPR && code != NE_EXPR)
- || !integer_zerop (gimple_assign_rhs2 (asgn)))
- return false;
+ tree_code code = gimple_assign_rhs_code (use_stmt);
+ if (code == COND_EXPR)
+ {
+ tree cond_expr = gimple_assign_rhs1 (use_stmt);
+ if ((TREE_CODE (cond_expr) != EQ_EXPR
+ && (TREE_CODE (cond_expr) != NE_EXPR))
+ || !integer_zerop (TREE_OPERAND (cond_expr, 1)))
+ return NULL;
+ }
+ else if (code == EQ_EXPR || code == NE_EXPR)
+ {
+ if (!integer_zerop (gimple_assign_rhs2 (use_stmt)))
+ return NULL;
+ }
+ else
+ return NULL;
}
- else if (gimple_code (ustmt) == GIMPLE_COND)
+ else if (gimple_code (use_stmt) == GIMPLE_COND)
{
- tree_code code = gimple_cond_code (ustmt);
+ tree_code code = gimple_cond_code (use_stmt);
if ((code != EQ_EXPR && code != NE_EXPR)
- || !integer_zerop (gimple_cond_rhs (ustmt)))
- return false;
+ || !integer_zerop (gimple_cond_rhs (use_stmt)))
+ return NULL;
}
else
- return false;
+ return NULL;
+
+ if (!first_use)
+ first_use = use_stmt;
}
+ return first_use;
+}
+
+/* Handle a call to memcmp. We try to handle small comparisons by
+ converting them to load and compare, and replacing the call to memcmp
+ with a __builtin_memcmp_eq call where possible.
+ return true when call is transformed, return false otherwise. */
+
+static bool
+handle_builtin_memcmp (gimple_stmt_iterator *gsi)
+{
+ gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
+ tree res = gimple_call_lhs (stmt);
+
+ if (!res || !used_only_for_zero_equality (res))
+ return false;
+
+ tree arg1 = gimple_call_arg (stmt, 0);
+ tree arg2 = gimple_call_arg (stmt, 1);
+ tree len = gimple_call_arg (stmt, 2);
+ unsigned HOST_WIDE_INT leni;
+
if (tree_fits_uhwi_p (len)
&& (leni = tree_to_uhwi (len)) <= GET_MODE_SIZE (word_mode)
&& pow2p_hwi (leni))
if (int_mode_for_size (leni, 1).exists (&mode)
&& (align >= leni || !targetm.slow_unaligned_access (mode, align)))
{
- location_t loc = gimple_location (stmt2);
+ location_t loc = gimple_location (stmt);
tree type, off;
type = build_nonstandard_integer_type (leni, 1);
gcc_assert (known_eq (GET_MODE_BITSIZE (TYPE_MODE (type)), leni));
}
}
- gimple_call_set_fndecl (stmt2, builtin_decl_explicit (BUILT_IN_MEMCMP_EQ));
+ gimple_call_set_fndecl (stmt, builtin_decl_explicit (BUILT_IN_MEMCMP_EQ));
return true;
}
-/* If IDX1 and IDX2 refer to strings A and B of unequal lengths, return
- the result of 0 == strncmp (A, B, N) (which is the same as strcmp for
- sufficiently large N). Otherwise return false. */
-
-static bool
-strxcmp_unequal (int idx1, int idx2, unsigned HOST_WIDE_INT n)
-{
- unsigned HOST_WIDE_INT len1;
- unsigned HOST_WIDE_INT len2;
-
- bool nulterm1;
- bool nulterm2;
-
- if (idx1 < 0)
- {
- len1 = ~idx1;
- nulterm1 = true;
- }
- else if (strinfo *si = get_strinfo (idx1))
- {
- if (tree_fits_uhwi_p (si->nonzero_chars))
- {
- len1 = tree_to_uhwi (si->nonzero_chars);
- nulterm1 = si->full_string_p;
- }
- else
- return false;
- }
- else
- return false;
-
- if (idx2 < 0)
- {
- len2 = ~idx2;
- nulterm2 = true;
- }
- else if (strinfo *si = get_strinfo (idx2))
- {
- if (tree_fits_uhwi_p (si->nonzero_chars))
- {
- len2 = tree_to_uhwi (si->nonzero_chars);
- nulterm2 = si->full_string_p;
- }
- else
- return false;
- }
- else
- return false;
-
- /* N is set to UHWI_MAX for strcmp and less to strncmp. Adjust
- the length of each string to consider to be no more than N. */
- if (len1 > n)
- len1 = n;
- if (len2 > n)
- len2 = n;
-
- if ((len1 < len2 && nulterm1)
- || (len2 < len1 && nulterm2))
- /* The string lengths are definitely unequal and the result can
- be folded to one (since it's used for comparison with zero). */
- return true;
-
- /* The string lengths may be equal or unequal. Even when equal and
- both strings nul-terminated, without the string contents there's
- no way to determine whether they are equal. */
- return false;
-}
-
/* Given an index to the strinfo vector, compute the string length
for the corresponding string. Return -1 when unknown. */
/* Determine the minimum size of the object referenced by DEST expression
which must have a pointer type.
- Return the minimum size of the object if successful or NULL when the size
- cannot be determined. */
-static tree
+ Return the minimum size of the object if successful or HWI_M1U when
+ the size cannot be determined. */
+
+static unsigned HOST_WIDE_INT
determine_min_objsize (tree dest)
{
unsigned HOST_WIDE_INT size = 0;
if (compute_builtin_object_size (dest, 2, &size))
- return build_int_cst (sizetype, size);
+ return size;
/* Try to determine the size of the object through the RHS
of the assign statement. */
{
gimple *stmt = SSA_NAME_DEF_STMT (dest);
if (!is_gimple_assign (stmt))
- return NULL_TREE;
+ return HOST_WIDE_INT_M1U;
if (!gimple_assign_single_p (stmt)
&& !gimple_assign_unary_nop_p (stmt))
- return NULL_TREE;
+ return HOST_WIDE_INT_M1U;
dest = gimple_assign_rhs1 (stmt);
return determine_min_objsize (dest);
/* Try to determine the size of the object from its type. */
if (TREE_CODE (dest) != ADDR_EXPR)
- return NULL_TREE;
+ return HOST_WIDE_INT_M1U;
tree type = TREE_TYPE (dest);
if (TREE_CODE (type) == POINTER_TYPE)
type = TYPE_MAIN_VARIANT (type);
- /* We cannot determine the size of the array if it's a flexible array,
- which is declared at the end of a structure. */
- if (TREE_CODE (type) == ARRAY_TYPE
- && !array_at_struct_end_p (dest))
+ /* The size of a flexible array cannot be determined. Otherwise,
+ for arrays with more than one element, return the size of its
+ type. GCC itself misuses arrays of both zero and one elements
+ as flexible array members so they are excluded as well. */
+ if (TREE_CODE (type) != ARRAY_TYPE
+ || !array_at_struct_end_p (dest))
{
- tree size_t = TYPE_SIZE_UNIT (type);
- if (size_t && TREE_CODE (size_t) == INTEGER_CST
- && !integer_zerop (size_t))
- return size_t;
+ tree type_size = TYPE_SIZE_UNIT (type);
+ if (type_size && TREE_CODE (type_size) == INTEGER_CST
+ && !integer_onep (type_size)
+ && !integer_zerop (type_size))
+ return tree_to_uhwi (type_size);
}
- return NULL_TREE;
+ return HOST_WIDE_INT_M1U;
}
-/* Handle a call to strcmp or strncmp. When the result is ONLY used to do
- equality test against zero:
-
- A. When the lengths of both arguments are constant and it's a strcmp:
- * if the lengths are NOT equal, we can safely fold the call
- to a non-zero value.
- * otherwise, do nothing now.
-
- B. When the length of one argument is constant, try to replace the call
- with a __builtin_str(n)cmp_eq call where possible, i.e:
-
- strncmp (s, STR, C) (!)= 0 in which, s is a pointer to a string, STR
- is a string with constant length , C is a constant.
- if (C <= strlen(STR) && sizeof_array(s) > C)
- {
- replace this call with
- strncmp_eq (s, STR, C) (!)= 0
- }
- if (C > strlen(STR)
- {
- it can be safely treated as a call to strcmp (s, STR) (!)= 0
- can handled by the following strcmp.
- }
-
- strcmp (s, STR) (!)= 0 in which, s is a pointer to a string, STR
- is a string with constant length.
- if (sizeof_array(s) > strlen(STR))
- {
- replace this call with
- strcmp_eq (s, STR, strlen(STR)+1) (!)= 0
- }
-
- Return true when the call is transformed, return false otherwise.
- */
+/* Given strinfo IDX for ARG, set LENRNG[] to the range of lengths
+ of the string(s) referenced by ARG if it can be determined.
+ If the length cannot be determined, set *SIZE to the size of
+ the array the string is stored in, if any. If no such array is
+ known, set *SIZE to -1. When the strings are nul-terminated set
+ *NULTERM to true, otherwise to false. Return true on success. */
static bool
-handle_builtin_string_cmp (gimple_stmt_iterator *gsi)
+get_len_or_size (tree arg, int idx, unsigned HOST_WIDE_INT lenrng[2],
+ unsigned HOST_WIDE_INT *size, bool *nulterm)
{
- gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
- tree res = gimple_call_lhs (stmt);
- use_operand_p use_p;
- imm_use_iterator iter;
- tree arg1 = gimple_call_arg (stmt, 0);
- tree arg2 = gimple_call_arg (stmt, 1);
- int idx1 = get_stridx (arg1);
- int idx2 = get_stridx (arg2);
- HOST_WIDE_INT length = -1;
- bool is_ncmp = false;
+ /* Set so that both LEN and ~LEN are invalid lengths, i.e.,
+ maximum possible length + 1. */
+ lenrng[0] = lenrng[1] = HOST_WIDE_INT_MAX;
- if (!res)
- return false;
-
- /* When both arguments are unknown, do nothing. */
- if (idx1 == 0 && idx2 == 0)
- return false;
+ *size = HOST_WIDE_INT_M1U;
- /* Handle strncmp function. */
- if (gimple_call_num_args (stmt) == 3)
+ if (idx < 0)
{
- tree len = gimple_call_arg (stmt, 2);
- if (tree_fits_shwi_p (len))
- length = tree_to_shwi (len);
-
- is_ncmp = true;
+ /* IDX is the inverted constant string length. */
+ lenrng[0] = ~idx;
+ lenrng[1] = lenrng[0];
+ *nulterm = true;
}
-
- /* For strncmp, if the length argument is NOT known, do nothing. */
- if (is_ncmp && length < 0)
- return false;
-
- /* When the result is ONLY used to do equality test against zero. */
- FOR_EACH_IMM_USE_FAST (use_p, iter, res)
+ else if (idx == 0)
+ ; /* Handled below. */
+ else if (strinfo *si = get_strinfo (idx))
{
- gimple *use_stmt = USE_STMT (use_p);
+ if (!si->nonzero_chars)
+ arg = si->ptr;
+ else if (tree_fits_uhwi_p (si->nonzero_chars))
+ {
+ lenrng[0] = tree_to_uhwi (si->nonzero_chars);
+ *nulterm = si->full_string_p;
+ /* Set the upper bound only if the string is known to be
+ nul-terminated, otherwise leave it at maximum + 1. */
+ if (*nulterm)
+ lenrng[1] = lenrng[0];
+ }
+ else if (TREE_CODE (si->nonzero_chars) == SSA_NAME)
+ {
+ wide_int min, max;
+ value_range_kind rng = get_range_info (si->nonzero_chars, &min, &max);
+ if (rng == VR_RANGE)
+ {
+ lenrng[0] = min.to_uhwi ();
+ lenrng[1] = max.to_uhwi ();
+ *nulterm = si->full_string_p;
+ }
+ }
+ else if (si->ptr)
+ arg = si->ptr;
+ }
- if (is_gimple_debug (use_stmt))
- continue;
- if (gimple_code (use_stmt) == GIMPLE_ASSIGN)
+ if (lenrng[0] == HOST_WIDE_INT_MAX)
+ {
+ /* Compute the minimum and maximum real or possible lengths. */
+ c_strlen_data lendata = { };
+ if (get_range_strlen (arg, &lendata, /* eltsize = */1))
{
- tree_code code = gimple_assign_rhs_code (use_stmt);
- if (code == COND_EXPR)
+ if (tree_fits_shwi_p (lendata.maxlen) && !lendata.maxbound)
{
- tree cond_expr = gimple_assign_rhs1 (use_stmt);
- if ((TREE_CODE (cond_expr) != EQ_EXPR
- && (TREE_CODE (cond_expr) != NE_EXPR))
- || !integer_zerop (TREE_OPERAND (cond_expr, 1)))
- return false;
+ lenrng[0] = tree_to_shwi (lendata.minlen);
+ lenrng[1] = tree_to_shwi (lendata.maxlen);
+ *nulterm = true;
}
- else if (code == EQ_EXPR || code == NE_EXPR)
+ else if (lendata.maxbound && tree_fits_shwi_p (lendata.maxbound))
{
- if (!integer_zerop (gimple_assign_rhs2 (use_stmt)))
- return false;
- }
- else
- return false;
+ /* Set *SIZE to the conservative LENDATA.MAXBOUND which
+ is a conservative estimate of the longest string based
+ on the sizes of the arrays referenced by ARG. */
+ *size = tree_to_uhwi (lendata.maxbound) + 1;
+ *nulterm = false;
+ }
}
- else if (gimple_code (use_stmt) == GIMPLE_COND)
+ else
{
- tree_code code = gimple_cond_code (use_stmt);
- if ((code != EQ_EXPR && code != NE_EXPR)
- || !integer_zerop (gimple_cond_rhs (use_stmt)))
- return false;
+ /* Set *SIZE to the size of the smallest object referenced
+ by ARG if ARG denotes a single object, or to HWI_M1U
+ otherwise. */
+ *size = determine_min_objsize (arg);
+ *nulterm = false;
}
- else
- return false;
}
- /* When the lengths of the arguments are known to be unequal
- we can safely fold the call to a non-zero value for strcmp;
- otherwise, do nothing now. */
- if (idx1 != 0 && idx2 != 0)
- {
- if (strxcmp_unequal (idx1, idx2, length))
- {
- replace_call_with_value (gsi, integer_one_node);
- return true;
- }
- return false;
+ return lenrng[0] != HOST_WIDE_INT_MAX || *size != HOST_WIDE_INT_M1U;
+}
+
+/* If IDX1 and IDX2 refer to strings A and B of unequal lengths, return
+ the result of 0 == strncmp (A, B, BOUND) (which is the same as strcmp
+ for a sufficiently large BOUND). If the result is based on the length
+ of one string being greater than the longest string that would fit in
+ the array pointer to by the argument, set *PLEN and *PSIZE to
+ the corresponding length (or its complement when the string is known
+ to be at least as long and need not be nul-terminated) and size.
+ Otherwise return null. */
+
+static tree
+strxcmp_eqz_result (tree arg1, int idx1, tree arg2, int idx2,
+ unsigned HOST_WIDE_INT bound, unsigned HOST_WIDE_INT len[2],
+ unsigned HOST_WIDE_INT *psize)
+{
+ /* Determine the range the length of each string is in and whether it's
+ known to be nul-terminated, or the size of the array it's stored in. */
+ bool nul1, nul2;
+ unsigned HOST_WIDE_INT siz1, siz2;
+ unsigned HOST_WIDE_INT len1rng[2], len2rng[2];
+ if (!get_len_or_size (arg1, idx1, len1rng, &siz1, &nul1)
+ || !get_len_or_size (arg2, idx2, len2rng, &siz2, &nul2))
+ return NULL_TREE;
+
+ /* BOUND is set to HWI_M1U for strcmp and less to strncmp, and LENiRNG
+ to HWI_MAX when invalid. Adjust the length of each string to consider
+ to be no more than BOUND. */
+ if (len1rng[0] < HOST_WIDE_INT_MAX && len1rng[0] > bound)
+ len1rng[0] = bound;
+ if (len1rng[1] < HOST_WIDE_INT_MAX && len1rng[1] > bound)
+ len1rng[1] = bound;
+ if (len2rng[0] < HOST_WIDE_INT_MAX && len2rng[0] > bound)
+ len2rng[0] = bound;
+ if (len2rng[1] < HOST_WIDE_INT_MAX && len2rng[1] > bound)
+ len2rng[1] = bound;
+
+ /* Two empty strings are equal. */
+ if (len1rng[1] == 0 && len2rng[1] == 0)
+ return integer_one_node;
+
+ /* The strings are definitely unequal when the lower bound of the length
+ of one of them is greater than the length of the longest string that
+ would fit into the other array. */
+ if (len1rng[0] == HOST_WIDE_INT_MAX
+ && len2rng[0] != HOST_WIDE_INT_MAX
+ && ((len2rng[0] < bound && len2rng[0] >= siz1)
+ || len2rng[0] > siz1))
+ {
+ *psize = siz1;
+ len[0] = len1rng[0];
+ /* Set LEN[0] to the lower bound of ARG1's length when it's
+ nul-terminated or to the complement of its minimum length
+ otherwise, */
+ len[1] = nul2 ? len2rng[0] : ~len2rng[0];
+ return integer_zero_node;
+ }
+
+ if (len2rng[0] == HOST_WIDE_INT_MAX
+ && len1rng[0] != HOST_WIDE_INT_MAX
+ && ((len1rng[0] < bound && len1rng[0] >= siz2)
+ || len1rng[0] > siz2))
+ {
+ *psize = siz2;
+ len[0] = nul1 ? len1rng[0] : ~len1rng[0];
+ len[1] = len2rng[0];
+ return integer_zero_node;
+ }
+
+ /* The strings are also definitely unequal when their lengths are unequal
+ and at least one is nul-terminated. */
+ if (len1rng[0] != HOST_WIDE_INT_MAX
+ && len2rng[0] != HOST_WIDE_INT_MAX
+ && ((len1rng[1] < len2rng[0] && nul1)
+ || (len2rng[1] < len1rng[0] && nul2)))
+ {
+ if (bound <= len1rng[0] || bound <= len2rng[0])
+ *psize = bound;
+ else
+ *psize = HOST_WIDE_INT_M1U;
+
+ len[0] = len1rng[0];
+ len[1] = len2rng[0];
+ return integer_zero_node;
}
- /* When the length of one argument is constant. */
- tree var_string = NULL_TREE;
- HOST_WIDE_INT const_string_leni = -1;
+ /* The string lengths may be equal or unequal. Even when equal and
+ both strings nul-terminated, without the string contents there's
+ no way to determine whether they are equal. */
+ return NULL_TREE;
+}
+
+/* Diagnose pointless calls to strcmp or strncmp STMT with string
+ arguments of lengths LEN or size SIZ and (for strncmp) BOUND,
+ whose result is used in equality expressions that evaluate to
+ a constant due to one argument being longer than the size of
+ the other. */
- if (idx1)
+static void
+maybe_warn_pointless_strcmp (gimple *stmt, HOST_WIDE_INT bound,
+ unsigned HOST_WIDE_INT len[2],
+ unsigned HOST_WIDE_INT siz)
+{
+ gimple *use = used_only_for_zero_equality (gimple_call_lhs (stmt));
+ if (!use)
+ return;
+
+ bool at_least = false;
+
+ /* Excessive LEN[i] indicates a lower bound. */
+ if (len[0] > HOST_WIDE_INT_MAX)
{
- const_string_leni = compute_string_length (idx1);
- var_string = arg2;
+ at_least = true;
+ len[0] = ~len[0];
}
- else
+
+ if (len[1] > HOST_WIDE_INT_MAX)
{
- gcc_checking_assert (idx2);
- const_string_leni = compute_string_length (idx2);
- var_string = arg1;
+ at_least = true;
+ len[1] = ~len[1];
}
- if (const_string_leni < 0)
- return false;
+ unsigned HOST_WIDE_INT minlen = MIN (len[0], len[1]);
- unsigned HOST_WIDE_INT var_sizei = 0;
- /* try to determine the minimum size of the object pointed by var_string. */
- tree size = determine_min_objsize (var_string);
+ /* FIXME: Include a note pointing to the declaration of the smaller
+ array. */
+ location_t stmt_loc = gimple_location (stmt);
+ tree callee = gimple_call_fndecl (stmt);
+ bool warned = false;
+ if (siz <= minlen && bound == -1)
+ warned = warning_at (stmt_loc, OPT_Wstring_compare,
+ (at_least
+ ? G_("%G%qD of a string of length %wu or more and "
+ "an array of size %wu evaluates to nonzero")
+ : G_("%G%qD of a string of length %wu and an array "
+ "of size %wu evaluates to nonzero")),
+ stmt, callee, minlen, siz);
+ else if (!at_least && siz <= HOST_WIDE_INT_MAX)
+ {
+ if (len[0] != HOST_WIDE_INT_MAX && len[1] != HOST_WIDE_INT_MAX)
+ warned = warning_at (stmt_loc, OPT_Wstring_compare,
+ "%G%qD of strings of length %wu and %wu "
+ "and bound of %wu evaluates to nonzero",
+ stmt, callee, len[0], len[1], bound);
+ else
+ warned = warning_at (stmt_loc, OPT_Wstring_compare,
+ "%G%qD of a string of length %wu, an array "
+ "of size %wu and bound of %wu evaluates to "
+ "nonzero",
+ stmt, callee, minlen, siz, bound);
+ }
+
+ if (warned)
+ {
+ location_t use_loc = gimple_location (use);
+ if (LOCATION_LINE (stmt_loc) != LOCATION_LINE (use_loc))
+ inform (use_loc, "in this expression");
+ }
+}
- if (!size)
- return false;
- if (tree_fits_uhwi_p (size))
- var_sizei = tree_to_uhwi (size);
+/* Optimize a call to strcmp or strncmp either by folding it to a constant
+ when possible or by transforming the latter to the former. Warn about
+ calls where the length of one argument is greater than the size of
+ the array to which the other argument points if the latter's length
+ is not known. Return true when the call has been transformed into
+ another and false otherwise. */
+
+static bool
+handle_builtin_string_cmp (gimple_stmt_iterator *gsi)
+{
+ gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
+ tree lhs = gimple_call_lhs (stmt);
- if (var_sizei == 0)
+ if (!lhs)
return false;
- /* For strncmp, if length > const_string_leni , this call can be safely
- transformed to a strcmp. */
- if (is_ncmp && length > const_string_leni)
- is_ncmp = false;
+ tree arg1 = gimple_call_arg (stmt, 0);
+ tree arg2 = gimple_call_arg (stmt, 1);
+ int idx1 = get_stridx (arg1);
+ int idx2 = get_stridx (arg2);
- unsigned HOST_WIDE_INT final_length
- = is_ncmp ? length : const_string_leni + 1;
+ /* For strncmp set to the the value of the third argument if known. */
+ HOST_WIDE_INT bound = -1;
- /* Replace strcmp or strncmp with the corresponding str(n)cmp_eq. */
- if (var_sizei > final_length)
+ /* Extract the strncmp bound. */
+ if (gimple_call_num_args (stmt) == 3)
{
- tree fn
- = (is_ncmp
- ? builtin_decl_implicit (BUILT_IN_STRNCMP_EQ)
- : builtin_decl_implicit (BUILT_IN_STRCMP_EQ));
- if (!fn)
+ tree len = gimple_call_arg (stmt, 2);
+ if (tree_fits_shwi_p (len))
+ bound = tree_to_shwi (len);
+
+ /* If the bound argument is NOT known, do nothing. */
+ if (bound < 0)
return false;
- tree const_string_len = build_int_cst (size_type_node, final_length);
- update_gimple_call (gsi, fn, 3, arg1, arg2, const_string_len);
}
+
+ {
+ /* Set to the length of one argument (or its complement if it's
+ the lower bound of a range) and the size of the array storing
+ the other if the result is based on the former being equal to
+ or greater than the latter. */
+ unsigned HOST_WIDE_INT len[2] = { HOST_WIDE_INT_MAX, HOST_WIDE_INT_MAX };
+ unsigned HOST_WIDE_INT siz = HOST_WIDE_INT_M1U;
+
+ /* Try to determine if the two strings are either definitely equal
+ or definitely unequal and if so, either fold the result to zero
+ (when equal) or set the range of the result to ~[0, 0] otherwise. */
+ if (tree eqz = strxcmp_eqz_result (arg1, idx1, arg2, idx2, bound,
+ len, &siz))
+ {
+ if (integer_zerop (eqz))
+ {
+ maybe_warn_pointless_strcmp (stmt, bound, len, siz);
+
+ /* When the lengths of the first two string arguments are
+ known to be unequal set the range of the result to non-zero.
+ This allows the call to be eliminated if its result is only
+ used in tests for equality to zero. */
+ wide_int zero = wi::zero (TYPE_PRECISION (TREE_TYPE (lhs)));
+ set_range_info (lhs, VR_ANTI_RANGE, zero, zero);
+ return false;
+ }
+ /* When the two strings are definitely equal (such as when they
+ are both empty) fold the call to the constant result. */
+ replace_call_with_value (gsi, integer_zero_node);
+ return true;
+ }
+ }
+
+ /* Return if nothing is known about the strings pointed to by ARG1
+ and ARG2. */
+ if (idx1 == 0 && idx2 == 0)
+ return false;
+
+ /* Determine either the length or the size of each of the strings,
+ whichever is available. */
+ HOST_WIDE_INT cstlen1 = -1, cstlen2 = -1;
+ HOST_WIDE_INT arysiz1 = -1, arysiz2 = -1;
+
+ if (idx1)
+ cstlen1 = compute_string_length (idx1) + 1;
else
+ arysiz1 = determine_min_objsize (arg1);
+
+ /* Bail if neither the string length nor the size of the array
+ it is stored in can be determined. */
+ if (cstlen1 < 0 && arysiz1 < 0)
return false;
- return true;
+ /* Repeat for the second argument. */
+ if (idx2)
+ cstlen2 = compute_string_length (idx2) + 1;
+ else
+ arysiz2 = determine_min_objsize (arg2);
+
+ if (cstlen2 < 0 && arysiz2 < 0)
+ return false;
+
+ /* The exact number of characters to compare. */
+ HOST_WIDE_INT cmpsiz = bound < 0 ? cstlen1 < 0 ? cstlen2 : cstlen1 : bound;
+ /* The size of the array in which the unknown string is stored. */
+ HOST_WIDE_INT varsiz = arysiz1 < 0 ? arysiz2 : arysiz1;
+
+ if (cmpsiz < varsiz && used_only_for_zero_equality (lhs))
+ {
+ /* If the known length is less than the size of the other array
+ and the strcmp result is only used to test equality to zero,
+ transform the call to the equivalent _eq call. */
+ if (tree fn = builtin_decl_implicit (bound < 0 ? BUILT_IN_STRCMP_EQ
+ : BUILT_IN_STRNCMP_EQ))
+ {
+ tree n = build_int_cst (size_type_node, cmpsiz);
+ update_gimple_call (gsi, fn, 3, arg1, arg2, n);
+ return true;
+ }
+ }
+
+ return false;
}
/* Handle a POINTER_PLUS_EXPR statement.