Commit e7868dc6 by Martin Sebor

PR tree-optimization/92765 - wrong code for strcmp of a union member

gcc/ChangeLog:

	PR tree-optimization/92765
	* gimple-fold.c (get_range_strlen_tree): Handle MEM_REF and PARM_DECL.
	* tree-ssa-strlen.c (compute_string_length): Remove.
	(determine_min_objsize): Remove.
	(get_len_or_size): Add an argument.  Call get_range_strlen_dynamic.
	Avoid using type size as the upper bound on string length.
	(handle_builtin_string_cmp): Add an argument.  Adjust.
	(strlen_check_and_optimize_call): Pass additional argument to
	handle_builtin_string_cmp.

gcc/testsuite/ChangeLog:

	PR tree-optimization/92765
	* g++.dg/tree-ssa/strlenopt-1.C: New test.
	* g++.dg/tree-ssa/strlenopt-2.C: New test.
	* gcc.dg/Warray-bounds-58.c: New test.
	* gcc.dg/Wrestrict-20.c: Avoid a valid -Wformat-overflow.
	* gcc.dg/Wstring-compare.c: Xfail a test.
	* gcc.dg/strcmpopt_2.c: Disable tests.
	* gcc.dg/strcmpopt_4.c: Adjust tests.
	* gcc.dg/strcmpopt_10.c: New test.
	* gcc.dg/strcmpopt_11.c: New test.
	* gcc.dg/strlenopt-69.c: Disable tests.
	* gcc.dg/strlenopt-92.c: New test.
	* gcc.dg/strlenopt-93.c: New test.
	* gcc.dg/strlenopt.h: Declare calloc.
	* gcc.dg/tree-ssa/pr92056.c: Xfail tests until pr93518 is resolved.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-23.c: Correct test (pr93517).
parent f6bef097
2020-02-05 Martin Sebor <msebor@redhat.com>
PR tree-optimization/92765
* gimple-fold.c (get_range_strlen_tree): Handle MEM_REF and PARM_DECL.
* tree-ssa-strlen.c (compute_string_length): Remove.
(determine_min_objsize): Remove.
(get_len_or_size): Add an argument. Call get_range_strlen_dynamic.
Avoid using type size as the upper bound on string length.
(handle_builtin_string_cmp): Add an argument. Adjust.
(strlen_check_and_optimize_call): Pass additional argument to
handle_builtin_string_cmp.
2020-02-05 Uroš Bizjak <ubizjak@gmail.com> 2020-02-05 Uroš Bizjak <ubizjak@gmail.com>
* config/i386/i386.md (*pushdi2_rex64 peephole2): Remove. * config/i386/i386.md (*pushdi2_rex64 peephole2): Remove.
......
...@@ -65,6 +65,7 @@ along with GCC; see the file COPYING3. If not see ...@@ -65,6 +65,7 @@ along with GCC; see the file COPYING3. If not see
#include "calls.h" #include "calls.h"
#include "tree-vector-builder.h" #include "tree-vector-builder.h"
#include "tree-ssa-strlen.h" #include "tree-ssa-strlen.h"
#include "varasm.h"
enum strlen_range_kind { enum strlen_range_kind {
/* Compute the exact constant string length. */ /* Compute the exact constant string length. */
...@@ -1422,7 +1423,44 @@ get_range_strlen_tree (tree arg, bitmap *visited, strlen_range_kind rkind, ...@@ -1422,7 +1423,44 @@ get_range_strlen_tree (tree arg, bitmap *visited, strlen_range_kind rkind,
type about the length here. */ type about the length here. */
tight_bound = true; tight_bound = true;
} }
else if (VAR_P (arg)) else if (TREE_CODE (arg) == MEM_REF
&& TREE_CODE (TREE_TYPE (arg)) == ARRAY_TYPE
&& TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) == INTEGER_TYPE
&& TREE_CODE (TREE_OPERAND (arg, 0)) == ADDR_EXPR)
{
/* Handle a MEM_REF into a DECL accessing an array of integers,
being conservative about references to extern structures with
flexible array members that can be initialized to arbitrary
numbers of elements as an extension (static structs are okay).
FIXME: Make this less conservative -- see
component_ref_size in tree.c. */
tree ref = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
if ((TREE_CODE (ref) == PARM_DECL || VAR_P (ref))
&& (decl_binds_to_current_def_p (ref)
|| !array_at_struct_end_p (arg)))
{
/* Fail if the offset is out of bounds. Such accesses
should be diagnosed at some point. */
val = DECL_SIZE_UNIT (ref);
if (!val || integer_zerop (val))
return false;
poly_offset_int psiz = wi::to_offset (val);
poly_offset_int poff = mem_ref_offset (arg);
if (known_le (psiz, poff))
return false;
pdata->minlen = ssize_int (0);
/* Subtract the offset and one for the terminating nul. */
psiz -= poff;
psiz -= 1;
val = wide_int_to_tree (TREE_TYPE (val), psiz);
/* Since VAL reflects the size of a declared object
rather the type of the access it is not a tight bound. */
}
}
else if (TREE_CODE (arg) == PARM_DECL || VAR_P (arg))
{ {
/* Avoid handling pointers to arrays. GCC might misuse /* Avoid handling pointers to arrays. GCC might misuse
a pointer to an array of one bound to point to an array a pointer to an array of one bound to point to an array
...@@ -1500,7 +1538,8 @@ get_range_strlen_tree (tree arg, bitmap *visited, strlen_range_kind rkind, ...@@ -1500,7 +1538,8 @@ get_range_strlen_tree (tree arg, bitmap *visited, strlen_range_kind rkind,
the referenced subobject minus 1 (for the terminating nul). */ the referenced subobject minus 1 (for the terminating nul). */
tree type = TREE_TYPE (base); tree type = TREE_TYPE (base);
if (TREE_CODE (type) == POINTER_TYPE if (TREE_CODE (type) == POINTER_TYPE
|| !VAR_P (base) || !(val = DECL_SIZE_UNIT (base))) || (TREE_CODE (base) != PARM_DECL && !VAR_P (base))
|| !(val = DECL_SIZE_UNIT (base)))
val = build_all_ones_cst (size_type_node); val = build_all_ones_cst (size_type_node);
else else
{ {
......
2020-02-05 Martin Sebor <msebor@redhat.com>
PR tree-optimization/92765
* g++.dg/tree-ssa/strlenopt-1.C: New test.
* g++.dg/tree-ssa/strlenopt-2.C: New test.
* gcc.dg/Warray-bounds-58.c: New test.
* gcc.dg/Wrestrict-20.c: Avoid a valid -Wformat-overflow.
* gcc.dg/Wstring-compare.c: Xfail a test.
* gcc.dg/strcmpopt_2.c: Disable tests.
* gcc.dg/strcmpopt_4.c: Adjust tests.
* gcc.dg/strcmpopt_10.c: New test.
* gcc.dg/strcmpopt_11.c: New test.
* gcc.dg/strlenopt-69.c: Disable tests.
* gcc.dg/strlenopt-92.c: New test.
* gcc.dg/strlenopt-93.c: New test.
* gcc.dg/strlenopt.h: Declare calloc.
* gcc.dg/tree-ssa/pr92056.c: Xfail tests until pr93518 is resolved.
* gcc.dg/tree-ssa/builtin-sprintf-warn-23.c: Correct test (pr93517).
2020-02-05 Marek Polacek <polacek@redhat.com> 2020-02-05 Marek Polacek <polacek@redhat.com>
PR c++/93559 - ICE with CONSTRUCTOR flags verification. PR c++/93559 - ICE with CONSTRUCTOR flags verification.
......
/* PR tree-optimization/92765 - wrong code for strcmp of a union member
{ dg-do run }
{ dg-options "-O2 -Wall" } */
typedef __SIZE_TYPE__ size_t;
inline void* operator new (size_t, void *p)
{
return p;
}
struct A { char a2[2]; };
struct B { char a4[4]; };
__attribute__((noipa)) void
sink (void*) { }
__attribute__((noipa)) void
copy (char *d, const char *s)
{
while ((*d++ = *s++));
}
__attribute__((noipa)) void
store_and_compare (void *p)
{
A *a = new (p) A;
sink (a->a2);
B *b = new (p) B;
char *q = (char *) b->a4;
copy (q, "abc");
if (__builtin_strcmp (q, "abc"))
__builtin_abort ();
}
int main ()
{
char a [sizeof (A) > sizeof (B) ? sizeof (A) : sizeof (B)];
store_and_compare (a);
}
/* PR tree-optimization/92765 - wrong code for strcmp of a union member
{ dg-do run }
{ dg-options "-O2 -Wall" } */
typedef __SIZE_TYPE__ size_t;
inline void* operator new (size_t, void *p)
{
return p;
}
struct A
{
char a[2]; char b[2]; char c[2];
A () { a[0] = 0; b[0] = 0; c[0] = 0; };
~A () { }
};
struct B
{
char d[6];
B () { d[0] = 0; d[2] = 0; }
~B () { }
};
__attribute__((noipa)) void
sink (void *) { }
__attribute__((noipa)) void
copy (char *d, const char *s)
{
while ((*d++ = *s++));
}
__attribute__((noipa)) void
store_and_compare (void *p)
{
A *a = new (p) A ();
sink (&a->b);
a->~A ();
B *b = new (p) B ();
char *q = &b->d[2];
copy (q, "abc");
if (__builtin_strcmp (q, "abc"))
__builtin_abort ();
b->~B ();
}
int main ()
{
char a [sizeof (A) > sizeof (B) ? sizeof (A) : sizeof (B)];
store_and_compare (a);
return 0;
}
/* { dg-do compile }
{ dg-options "-O2 -Wall" } */
typedef __SIZE_TYPE__ size_t;
extern size_t strlen (const char*);
void sink (size_t);
struct A0 { char i, a[0]; };
extern struct A0 ea0;
void fa0_extern (void)
{
sink (strlen (ea0.a - 2)); // { dg-warning "\\\[-Warray-bounds" }
sink (strlen (ea0.a - 1)); // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
sink (strlen (ea0.a)); // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
sink (strlen (ea0.a + 1)); // { dg-warning "\\\[-Warray-bounds" }
}
static struct A0 sa0 = { 0 };
void fa0_static (void)
{
sink (strlen (sa0.a - 2)); // { dg-warning "\\\[-Warray-bounds" }
sink (strlen (sa0.a - 1)); // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
sink (strlen (sa0.a)); // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
sink (strlen (sa0.a + 1)); // { dg-warning "\\\[-Warray-bounds" }
}
struct Ax { char i, a[]; };
extern struct Ax ax;
void fax_extern (void)
{
sink (strlen (ax.a - 2)); // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
sink (strlen (ax.a - 1)); // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
sink (strlen (ax.a));
sink (strlen (ax.a + 123));
}
static struct Ax ax0 = { 0, { 0 } };
static struct Ax ax1 = { 1, { 1, 0 } };
static struct Ax ax2 = { 2, { 2, 1, 0 } };
static struct Ax ax3 = { 3, { 3, 2, 1, 0 } };
void fax_static (void)
{
sink (strlen (ax0.a - 2)); // { dg-warning "\\\[-Warray-bounds" }
sink (strlen (ax0.a - 1)); // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
sink (strlen (ax0.a));
sink (strlen (ax0.a + 1)); // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
sink (strlen (ax0.a + 2)); // { dg-warning "\\\[-Warray-bounds" }
sink (strlen (ax1.a - 2)); // { dg-warning "\\\[-Warray-bounds" }
sink (strlen (ax1.a - 1)); // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
sink (strlen (ax1.a));
sink (strlen (ax1.a + 1));
sink (strlen (ax1.a + 2)); // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
sink (strlen (ax1.a + 3)); // { dg-warning "\\\[-Warray-bounds" }
sink (strlen (ax2.a - 2)); // { dg-warning "\\\[-Warray-bounds" }
sink (strlen (ax2.a - 1)); // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
sink (strlen (ax2.a));
sink (strlen (ax2.a + 1));
sink (strlen (ax2.a + 2));
sink (strlen (ax2.a + 3)); // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
sink (strlen (ax2.a + 4)); // { dg-warning "\\\[-Warray-bounds" }
sink (strlen (ax3.a - 2)); // { dg-warning "\\\[-Warray-bounds" }
sink (strlen (ax3.a - 1)); // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
sink (strlen (ax3.a));
sink (strlen (ax3.a + 1));
sink (strlen (ax3.a + 2));
sink (strlen (ax3.a + 3));
sink (strlen (ax3.a + 4)); // { dg-warning "\\\[-Warray-bounds" "pr93514" { xfail *-*-* } }
sink (strlen (ax3.a + 5)); // { dg-warning "\\\[-Warray-bounds" }
}
...@@ -15,7 +15,7 @@ void test_warn (char *p) ...@@ -15,7 +15,7 @@ void test_warn (char *p)
sprintf (a, "a=%s", a); /* { dg-warning "-Wrestrict" } */ sprintf (a, "a=%s", a); /* { dg-warning "-Wrestrict" } */
p = a; p = a;
char *q = p + 1; char *q = p + 3;
sprintf (p, "a=%s", q); /* { dg-warning "-Wrestrict" } */ sprintf (p, "a=%s", q); /* { dg-warning "-Wrestrict" } */
} }
......
...@@ -120,7 +120,8 @@ void strcmp_array_copy (void) ...@@ -120,7 +120,8 @@ void strcmp_array_copy (void)
void strcmp_member_array_lit (const struct S *p) void strcmp_member_array_lit (const struct S *p)
{ {
T (p->a4, "1234"); // { dg-warning "length 4 and an array of size 4 " } // Not handled due to the fix for PR 92756.
T (p->a4, "1234"); // { dg-warning "length 4 and an array of size 4 " "pr92765" { xfail *-*-* } }
} }
......
/* Verify that strncmp equalities aren't eliminated when the trailing array
type referenced by a member pointer is smaller than the string in cases
when the pointer pointed to by the enclosing object references an object
sufficiently large to store a string of equal length.
{ dg-do compile }
{ dg-options "-O2 -Wall -Wextra -fdump-tree-optimized" } */
void init (void*);
struct A1 { char i, a[1]; };
void f1_arr (void)
{
char a[9];
init (a);
struct A1 *p = (struct A1*)a;
if (__builtin_strncmp (p->a, "01234567", 8) == 0)
{
extern void array_test (void);
array_test ();
}
}
void f1_ptr (void)
{
void *p;
init (&p);
struct A1 *q = (struct A1*)p;
if (__builtin_strncmp (q->a, "0123456789", 10) == 0)
{
extern void pointer_test (void);
pointer_test ();
}
}
void f1_struct (void)
{
struct { char a[9]; } b;
init (&b);
struct A1 *p = (struct A1*)&b;
if (__builtin_strncmp (p->a, "01234567", 8) == 0)
{
extern void struct_test (void);
struct_test ();
}
}
void f1_memptr (void)
{
struct { void *p; } b;
init (&b);
struct A1 *p = (struct A1*)b.p;
if (__builtin_strncmp (p->a, "0123456789", 10) == 0)
{
extern void memptr_test (void);
memptr_test ();
}
}
struct A2 { char i, a[2]; };
void f2_arr (void)
{
char a[8];
init (a);
struct A2 *p = (struct A2*)a;
if (__builtin_strncmp (p->a, "0123456", 7) == 0)
{
extern void array_test (void);
array_test ();
}
}
void f2_ptr (void)
{
void *p;
init (&p);
struct A2 *q = (struct A2*)p;
if (__builtin_strncmp (q->a, "0123456789", 10) == 0)
{
extern void pointer_test (void);
pointer_test ();
}
}
void f2_struct (void)
{
struct { char a[8]; } b;
init (&b);
struct A2 *p = (struct A2*)&b;
if (__builtin_strncmp (p->a, "0123456", 7) == 0)
{
extern void struct_test (void);
struct_test ();
}
}
void f2_memptr (void)
{
struct { void *p; } b;
init (&b);
struct A2 *p = (struct A2*)b.p;
if (__builtin_strncmp (p->a, "0123456789", 10) == 0)
{
extern void memptr_test (void);
memptr_test ();
}
}
/* { dg-final { scan-tree-dump-times "array_test" 2 "optimized" } }
{ dg-final { scan-tree-dump-times "pointer_test" 2 "optimized" } }
{ dg-final { scan-tree-dump-times "struct_test" 2 "optimized" } }
{ dg-final { scan-tree-dump-times "memptr_test" 2 "optimized" } } */
/* Verify that strcmp doesn't make assumptions about the size of a weak
symbol.
{ dg-do compile }
{ dg-options "-O2 -Wall" } */
/* An ordinary definition of A with more elements might be provided
in another translation unit. Even though that would be undefined
(the type of the actual definition must be the same as the type
of the weak declaration) this test verifies that GCC doesn't rely
on the size of this A for optimization (as a matter of QoI). */
__attribute__ ((weak)) char a[3];
int cmp_a3_x (void)
{
return __builtin_strcmp (a, "1234567") == 0;
}
...@@ -7,7 +7,8 @@ typedef struct { char s[8]; int x; } S; ...@@ -7,7 +7,8 @@ typedef struct { char s[8]; int x; } S;
__attribute__ ((noinline)) int __attribute__ ((noinline)) int
f1 (S *s) f1 (S *s)
{ {
return __builtin_strcmp (s->s, "abc") != 0; /* Member arrays not handled due to the fix for PR 92765. */
return 0; // __builtin_strcmp (s->s, "abc") != 0;
} }
__attribute__ ((noinline)) int __attribute__ ((noinline)) int
...@@ -19,7 +20,7 @@ f2 (void) ...@@ -19,7 +20,7 @@ f2 (void)
__attribute__ ((noinline)) int __attribute__ ((noinline)) int
f3 (S *s) f3 (S *s)
{ {
return __builtin_strcmp ("abc", s->s) != 0; return 0; // __builtin_strcmp ("abc", s->s) != 0;
} }
__attribute__ ((noinline)) int __attribute__ ((noinline)) int
...@@ -31,7 +32,7 @@ f4 (void) ...@@ -31,7 +32,7 @@ f4 (void)
__attribute__ ((noinline)) int __attribute__ ((noinline)) int
f5 (S *s) f5 (S *s)
{ {
return __builtin_strncmp (s->s, "abc", 3) != 0; return 0; // __builtin_strncmp (s->s, "abc", 3) != 0;
} }
__attribute__ ((noinline)) int __attribute__ ((noinline)) int
...@@ -43,7 +44,7 @@ f6 (void) ...@@ -43,7 +44,7 @@ f6 (void)
__attribute__ ((noinline)) int __attribute__ ((noinline)) int
f7 (S *s) f7 (S *s)
{ {
return __builtin_strncmp ("abc", s->s, 3) != 0; return 0; // __builtin_strncmp ("abc", s->s, 3) != 0;
} }
__attribute__ ((noinline)) int __attribute__ ((noinline)) int
...@@ -64,4 +65,4 @@ int main (void) ...@@ -64,4 +65,4 @@ int main (void)
return 0; return 0;
} }
/* { dg-final { scan-tree-dump-times "cmp_eq \\(" 8 "strlen1" } } */ /* { dg-final { scan-tree-dump-times "cmp_eq \\(" 4 "strlen1" } } */
...@@ -2,15 +2,26 @@ ...@@ -2,15 +2,26 @@
/* { dg-options "-O2 -fdump-tree-strlen" } */ /* { dg-options "-O2 -fdump-tree-strlen" } */
typedef struct { char s[8]; int x; } S; typedef struct { char s[8]; int x; } S;
extern int max_i; extern int max_i;
int int f_param (S s)
f1 (S * s) {
int result = 0;
for (int i = 0; i < max_i; i++)
result += __builtin_strcmp (s.s, "abc") != 0 ? 2 : 1;
return result;
}
S s;
int f_object (void)
{ {
int result, i; int result = 0;
for (i = 0; i < max_i; i++) for (int i = 0; i < max_i; i++)
result += __builtin_strcmp (s->s, "abc") != 0 ? 2 : 1; result += __builtin_strcmp (s.s, "abc") != 0 ? 2 : 1;
return result; return result;
} }
/* { dg-final { scan-tree-dump-times "cmp_eq \\(" 1 "strlen1" } } */ /* { dg-final { scan-tree-dump-times "cmp_eq \\(" 2 "strlen1" } } */
...@@ -35,6 +35,8 @@ void test_array_lit (void) ...@@ -35,6 +35,8 @@ void test_array_lit (void)
void test_memarray_lit (struct S *p) void test_memarray_lit (struct S *p)
{ {
#if 0
/* Member arrays not handled due to the fix for PR 92765. */
A (strcmp (p->a4, "1234")); A (strcmp (p->a4, "1234"));
A (strcmp (p->a4, "12345")); A (strcmp (p->a4, "12345"));
A (strcmp (p->a4, "123456")); A (strcmp (p->a4, "123456"));
...@@ -42,6 +44,7 @@ void test_memarray_lit (struct S *p) ...@@ -42,6 +44,7 @@ void test_memarray_lit (struct S *p)
A (strcmp ("1234", p->a4)); A (strcmp ("1234", p->a4));
A (strcmp ("12345", p->a4)); A (strcmp ("12345", p->a4));
A (strcmp ("123456", p->a4)); A (strcmp ("123456", p->a4));
#endif
} }
/* Verify that the equality of empty strings is folded. */ /* Verify that the equality of empty strings is folded. */
......
/* PR tree-optimization/92765 - wrong code for strcmp of a union member
{ dg-do run }
{ dg-options "-O2 -Wall" } */
#include "strlenopt.h"
__attribute__((noipa)) int
copy (char *x, int y)
{
if (y == 0)
strcpy (x, "abcd");
return y;
}
__attribute__((noipa)) char *
alloc_2_copy_compare (int x)
{
char *p;
if (x)
p = malloc (4);
else
p = calloc (16, 1);
char *q = p + 2;
if (copy (q, x))
return p;
if (strcmp (q, "abcd") != 0)
abort ();
return p;
}
char a5[5], a6[6], a7[7];
__attribute__((noipa)) char *
decl_3_copy_compare (int x)
{
char *p = x < 0 ? a5 : 0 < x ? a6 : a7;
char *q = p + 1;
if (copy (q, x))
return p;
if (strcmp (q, "abcd") != 0)
abort ();
return p;
}
int main ()
{
free (alloc_2_copy_compare (0));
free (alloc_2_copy_compare (1));
decl_3_copy_compare (-1);
decl_3_copy_compare (0);
decl_3_copy_compare (1);
}
/* Verify that strlen doesn't (inadvertently) use the size of an array
of char pointers to put an upper bound on the length of the strings
they point to.
{ dg-do compile }
{ dg-options "-O2 -Wall -fdump-tree-optimized" } */
void eaa_test (void)
{
extern char eaa[4][4];
char (*p)[4] = eaa;
if (!*p)
return;
/* The longest string stored in EAA is 15 characters. */
if (__builtin_strlen (*p) > 14)
{
extern void eaa_ok (void);
eaa_ok ();
}
if (__builtin_strlen (*p) > 15)
{
extern void eaa_fail (void);
eaa_fail ();
}
}
/* { dg-final { scan-tree-dump-times "eaa_ok" 1 "optimized" } }
{ dg-final { scan-tree-dump-not "eaa_fail" "optimized" } } */
void epa_test (void)
{
extern char* epa[4];
char **p = epa;
if (*p && __builtin_strlen (*p) > 123)
{
extern void epa_ok (void);
epa_ok ();
}
}
/* { dg-final { scan-tree-dump-times "epa_ok" 1 "optimized" } } */
static char* spa[4];
void spa_test (void)
{
char **p = spa;
if (*p && __builtin_strlen (*p) > 123)
{
extern void spa_ok ();
spa_ok ();
}
}
/* { dg-final { scan-tree-dump-times "spa_ok" 1 "optimized" } } */
void sink (void*, ...);
void init (void)
{
/* Make believe even the static array SA may be non-zero. */
sink (spa);
}
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#define NULL ((void *) 0) #define NULL ((void *) 0)
typedef __SIZE_TYPE__ size_t; typedef __SIZE_TYPE__ size_t;
extern void abort (void); extern void abort (void);
void *calloc (size_t, size_t);
void *malloc (size_t); void *malloc (size_t);
void free (void *); void free (void *);
char *strdup (const char *); char *strdup (const char *);
......
...@@ -642,10 +642,22 @@ void test_multiple_overlap (int i) ...@@ -642,10 +642,22 @@ void test_multiple_overlap (int i)
} }
{ {
char a[4]; /* { dg-message "declared here" } */ char a[4];
/* There is no overlap here because the length of a3 is at most 1
and a4 is necessarily the empty string. */
char *d = a;
char *a3 = a + 2;
char *a4 = a + 3;
T (d, "%s%s", a3, a4);
}
{
char a[5]; /* { dg-message "declared here" } */
/* a3 and a4 may overlap the output. They will only not overlap /* a3 and a4 may overlap the output. They will only not overlap
it when a3 is empty, and a4 is at most chaeracter byte long. */ it when a3 is empty, and a4 is at most 1 character long. */
char *d = a; char *d = a;
char *a3 = a + 2; char *a3 = a + 2;
char *a4 = a + 3; char *a4 = a + 3;
......
/* PR tree-optimization/92056 */ /* PR tree-optimization/92056
/* { dg-do compile } */ { dg-do compile }
/* { dg-options "-O2 -fdump-tree-optimized" } */ { dg-options "-O2 -fdump-tree-optimized" }
/* { dg-final { scan-tree-dump-times "return 1;" 2 "optimized" } } */ Xfailed until pr93518 is resolved.
/* { dg-final { scan-tree-dump-not "strcmp \\(" "optimized" } } */ { dg-final { scan-tree-dump-times "return 1;" 2 "optimized" { xfail *-*-* } } }
{ dg-final { scan-tree-dump-not "strcmp \\(" "optimized" { xfail *-*-* } } } */
void bar (int, char *); void bar (int, char *);
......
...@@ -327,7 +327,8 @@ get_next_strinfo (strinfo *si) ...@@ -327,7 +327,8 @@ get_next_strinfo (strinfo *si)
/* Helper function for get_stridx. Return the strinfo index of the address /* Helper function for get_stridx. Return the strinfo index of the address
of EXP, which is available in PTR if nonnull. If OFFSET_OUT, it is of EXP, which is available in PTR if nonnull. If OFFSET_OUT, it is
OK to return the index for some X <= &EXP and store &EXP - X in OK to return the index for some X <= &EXP and store &EXP - X in
*OFFSET_OUT. When nonnull uses RVALS to determine range information. */ *OFFSET_OUT. When RVALS is nonnull uses it to determine range
information. */
static int static int
get_addr_stridx (tree exp, tree ptr, unsigned HOST_WIDE_INT *offset_out, get_addr_stridx (tree exp, tree ptr, unsigned HOST_WIDE_INT *offset_out,
...@@ -4061,105 +4062,20 @@ handle_builtin_memcmp (gimple_stmt_iterator *gsi) ...@@ -4061,105 +4062,20 @@ handle_builtin_memcmp (gimple_stmt_iterator *gsi)
return true; return true;
} }
/* Given an index to the strinfo vector, compute the string length /* Given strinfo IDX for ARG, sets LENRNG[] to the range of lengths
for the corresponding string. Return -1 when unknown. */
static HOST_WIDE_INT
compute_string_length (int idx)
{
HOST_WIDE_INT string_leni = -1;
gcc_assert (idx != 0);
if (idx < 0)
return ~idx;
strinfo *si = get_strinfo (idx);
if (si)
{
tree const_string_len = get_string_length (si);
if (const_string_len && tree_fits_shwi_p (const_string_len))
string_leni = tree_to_shwi (const_string_len);
}
if (string_leni < 0)
return -1;
return string_leni;
}
/* 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 HWI_M1U when
the size cannot be determined. */
static unsigned HOST_WIDE_INT
determine_min_objsize (tree dest)
{
unsigned HOST_WIDE_INT size = 0;
init_object_sizes ();
if (compute_builtin_object_size (dest, 2, &size))
return size;
/* Try to determine the size of the object through the RHS
of the assign statement. */
if (TREE_CODE (dest) == SSA_NAME)
{
gimple *stmt = SSA_NAME_DEF_STMT (dest);
if (!is_gimple_assign (stmt))
return HOST_WIDE_INT_M1U;
if (!gimple_assign_single_p (stmt)
&& !gimple_assign_unary_nop_p (stmt))
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 HOST_WIDE_INT_M1U;
tree type = TREE_TYPE (dest);
if (TREE_CODE (type) == POINTER_TYPE)
type = TREE_TYPE (type);
type = TYPE_MAIN_VARIANT (type);
/* 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 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 HOST_WIDE_INT_M1U;
}
/* Given strinfo IDX for ARG, set LENRNG[] to the range of lengths
of the string(s) referenced by ARG if it can be determined. of the string(s) referenced by ARG if it can be determined.
If the length cannot be determined, set *SIZE to the size of If the length cannot be determined, sets *SIZE to the size of
the array the string is stored in, if any. If no such array is 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 known, sets *SIZE to -1. When the strings are nul-terminated sets
*NULTERM to true, otherwise to false. Return true on success. */ *NULTERM to true, otherwise to false. When nonnull uses RVALS to
determine range information. Returns true on success. */
static bool static bool
get_len_or_size (tree arg, int idx, unsigned HOST_WIDE_INT lenrng[2], get_len_or_size (tree arg, int idx, unsigned HOST_WIDE_INT lenrng[2],
unsigned HOST_WIDE_INT *size, bool *nulterm) unsigned HOST_WIDE_INT *size, bool *nulterm,
const vr_values *rvals)
{ {
/* Set so that both LEN and ~LEN are invalid lengths, i.e., /* Invalidate. */
maximum possible length + 1. */
lenrng[0] = lenrng[1] = HOST_WIDE_INT_MAX;
*size = HOST_WIDE_INT_M1U; *size = HOST_WIDE_INT_M1U;
if (idx < 0) if (idx < 0)
...@@ -4168,13 +4084,18 @@ get_len_or_size (tree arg, int idx, unsigned HOST_WIDE_INT lenrng[2], ...@@ -4168,13 +4084,18 @@ get_len_or_size (tree arg, int idx, unsigned HOST_WIDE_INT lenrng[2],
lenrng[0] = ~idx; lenrng[0] = ~idx;
lenrng[1] = lenrng[0]; lenrng[1] = lenrng[0];
*nulterm = true; *nulterm = true;
return true;
} }
else if (idx == 0)
; /* Handled below. */ /* Set so that both LEN and ~LEN are invalid lengths, i.e., maximum
else if (strinfo *si = get_strinfo (idx)) possible length + 1. */
lenrng[0] = lenrng[1] = HOST_WIDE_INT_MAX;
if (strinfo *si = idx ? get_strinfo (idx) : NULL)
{ {
/* FIXME: Handle all this in_range_strlen_dynamic. */
if (!si->nonzero_chars) if (!si->nonzero_chars)
arg = si->ptr; ;
else if (tree_fits_uhwi_p (si->nonzero_chars)) else if (tree_fits_uhwi_p (si->nonzero_chars))
{ {
lenrng[0] = tree_to_uhwi (si->nonzero_chars); lenrng[0] = tree_to_uhwi (si->nonzero_chars);
...@@ -4195,42 +4116,62 @@ get_len_or_size (tree arg, int idx, unsigned HOST_WIDE_INT lenrng[2], ...@@ -4195,42 +4116,62 @@ get_len_or_size (tree arg, int idx, unsigned HOST_WIDE_INT lenrng[2],
*nulterm = si->full_string_p; *nulterm = si->full_string_p;
} }
} }
else if (si->ptr)
arg = si->ptr;
} }
if (lenrng[0] == HOST_WIDE_INT_MAX) if (lenrng[0] != HOST_WIDE_INT_MAX)
{ return true;
/* Compute the minimum and maximum real or possible lengths. */ /* Compute the minimum and maximum real or possible lengths. */
c_strlen_data lendata = { }; c_strlen_data lendata = { };
if (get_range_strlen (arg, &lendata, /* eltsize = */1)) /* 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_dynamic (arg, &lendata, rvals);
unsigned HOST_WIDE_INT maxbound = HOST_WIDE_INT_M1U;
if (tree_fits_uhwi_p (lendata.maxbound)
&& !integer_all_onesp (lendata.maxbound))
maxbound = tree_to_uhwi (lendata.maxbound);
if (tree_fits_uhwi_p (lendata.minlen) && tree_fits_uhwi_p (lendata.maxlen))
{ {
if (tree_fits_shwi_p (lendata.maxlen) && !lendata.maxbound) unsigned HOST_WIDE_INT minlen = tree_to_uhwi (lendata.minlen);
unsigned HOST_WIDE_INT maxlen = tree_to_uhwi (lendata.maxlen);
/* The longest string in this data model. */
const unsigned HOST_WIDE_INT lenmax
= tree_to_uhwi (max_object_size ()) - 2;
if (maxbound == HOST_WIDE_INT_M1U)
{ {
lenrng[0] = tree_to_shwi (lendata.minlen); lenrng[0] = minlen;
lenrng[1] = tree_to_shwi (lendata.maxlen); lenrng[1] = maxlen;
*nulterm = true; *nulterm = minlen == maxlen;
} }
else if (lendata.maxbound && tree_fits_shwi_p (lendata.maxbound)) else if (maxlen < lenmax)
{ {
/* Set *SIZE to the conservative LENDATA.MAXBOUND which *size = maxbound + 1;
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; *nulterm = false;
} }
}
else else
return false;
return true;
}
if (maxbound != HOST_WIDE_INT_M1U
&& lendata.maxlen
&& !integer_all_onesp (lendata.maxlen))
{ {
/* Set *SIZE to the size of the smallest object referenced /* Set *SIZE to LENDATA.MAXBOUND which is a conservative estimate
by ARG if ARG denotes a single object, or to HWI_M1U of the longest string based on the sizes of the arrays referenced
otherwise. */ by ARG. */
*size = determine_min_objsize (arg); *size = maxbound + 1;
*nulterm = false; *nulterm = false;
} return true;
} }
return lenrng[0] != HOST_WIDE_INT_MAX || *size != HOST_WIDE_INT_M1U; return false;
} }
/* If IDX1 and IDX2 refer to strings A and B of unequal lengths, return /* If IDX1 and IDX2 refer to strings A and B of unequal lengths, return
...@@ -4245,15 +4186,15 @@ get_len_or_size (tree arg, int idx, unsigned HOST_WIDE_INT lenrng[2], ...@@ -4245,15 +4186,15 @@ get_len_or_size (tree arg, int idx, unsigned HOST_WIDE_INT lenrng[2],
static tree static tree
strxcmp_eqz_result (tree arg1, int idx1, tree arg2, int idx2, 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 bound, unsigned HOST_WIDE_INT len[2],
unsigned HOST_WIDE_INT *psize) unsigned HOST_WIDE_INT *psize, const vr_values *rvals)
{ {
/* Determine the range the length of each string is in and whether it's /* 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. */ known to be nul-terminated, or the size of the array it's stored in. */
bool nul1, nul2; bool nul1, nul2;
unsigned HOST_WIDE_INT siz1, siz2; unsigned HOST_WIDE_INT siz1, siz2;
unsigned HOST_WIDE_INT len1rng[2], len2rng[2]; unsigned HOST_WIDE_INT len1rng[2], len2rng[2];
if (!get_len_or_size (arg1, idx1, len1rng, &siz1, &nul1) if (!get_len_or_size (arg1, idx1, len1rng, &siz1, &nul1, rvals)
|| !get_len_or_size (arg2, idx2, len2rng, &siz2, &nul2)) || !get_len_or_size (arg2, idx2, len2rng, &siz2, &nul2, rvals))
return NULL_TREE; return NULL_TREE;
/* BOUND is set to HWI_M1U for strcmp and less to strncmp, and LENiRNG /* BOUND is set to HWI_M1U for strcmp and less to strncmp, and LENiRNG
...@@ -4405,7 +4346,7 @@ maybe_warn_pointless_strcmp (gimple *stmt, HOST_WIDE_INT bound, ...@@ -4405,7 +4346,7 @@ maybe_warn_pointless_strcmp (gimple *stmt, HOST_WIDE_INT bound,
another and false otherwise. */ another and false otherwise. */
static bool static bool
handle_builtin_string_cmp (gimple_stmt_iterator *gsi) handle_builtin_string_cmp (gimple_stmt_iterator *gsi, const vr_values *rvals)
{ {
gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi)); gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
tree lhs = gimple_call_lhs (stmt); tree lhs = gimple_call_lhs (stmt);
...@@ -4451,7 +4392,7 @@ handle_builtin_string_cmp (gimple_stmt_iterator *gsi) ...@@ -4451,7 +4392,7 @@ handle_builtin_string_cmp (gimple_stmt_iterator *gsi)
or definitely unequal and if so, either fold the result to zero 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. */ (when equal) or set the range of the result to ~[0, 0] otherwise. */
if (tree eqz = strxcmp_eqz_result (arg1, idx1, arg2, idx2, bound, if (tree eqz = strxcmp_eqz_result (arg1, idx1, arg2, idx2, bound,
len, &siz)) len, &siz, rvals))
{ {
if (integer_zerop (eqz)) if (integer_zerop (eqz))
{ {
...@@ -4482,26 +4423,31 @@ handle_builtin_string_cmp (gimple_stmt_iterator *gsi) ...@@ -4482,26 +4423,31 @@ handle_builtin_string_cmp (gimple_stmt_iterator *gsi)
HOST_WIDE_INT cstlen1 = -1, cstlen2 = -1; HOST_WIDE_INT cstlen1 = -1, cstlen2 = -1;
HOST_WIDE_INT arysiz1 = -1, arysiz2 = -1; HOST_WIDE_INT arysiz1 = -1, arysiz2 = -1;
if (idx1) {
cstlen1 = compute_string_length (idx1); unsigned HOST_WIDE_INT len1rng[2], len2rng[2];
else unsigned HOST_WIDE_INT arsz1, arsz2;
arysiz1 = determine_min_objsize (arg1); bool nulterm[2];
/* Bail if neither the string length nor the size of the array if (!get_len_or_size (arg1, idx1, len1rng, &arsz1, nulterm, rvals)
it is stored in can be determined. */ || !get_len_or_size (arg2, idx2, len2rng, &arsz2, nulterm + 1, rvals))
if (cstlen1 < 0 && arysiz1 < 0)
return false; return false;
/* Repeat for the second argument. */ if (len1rng[0] == len1rng[1] && len1rng[0] < HOST_WIDE_INT_MAX)
if (idx2) cstlen1 = len1rng[0];
cstlen2 = compute_string_length (idx2); else if (arsz1 < HOST_WIDE_INT_M1U)
else arysiz1 = arsz1;
arysiz2 = determine_min_objsize (arg2);
if (cstlen2 < 0 && arysiz2 < 0) if (len2rng[0] == len2rng[1] && len2rng[0] < HOST_WIDE_INT_MAX)
return false; cstlen2 = len2rng[0];
else if (arsz2 < HOST_WIDE_INT_M1U)
arysiz2 = arsz2;
}
if (cstlen1 < 0 && cstlen2 < 0) /* Bail if neither the string length nor the size of the array
it is stored in can be determined. */
if ((cstlen1 < 0 && arysiz1 < 0)
|| (cstlen2 < 0 && arysiz2 < 0)
|| (cstlen1 < 0 && cstlen2 < 0))
return false; return false;
if (cstlen1 >= 0) if (cstlen1 >= 0)
...@@ -5435,7 +5381,7 @@ strlen_check_and_optimize_call (gimple_stmt_iterator *gsi, bool *zero_write, ...@@ -5435,7 +5381,7 @@ strlen_check_and_optimize_call (gimple_stmt_iterator *gsi, bool *zero_write,
break; break;
case BUILT_IN_STRCMP: case BUILT_IN_STRCMP:
case BUILT_IN_STRNCMP: case BUILT_IN_STRNCMP:
if (handle_builtin_string_cmp (gsi)) if (handle_builtin_string_cmp (gsi, rvals))
return false; return false;
break; break;
default: default:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment