Commit 7802a8ec by Martin Sebor Committed by Martin Sebor

PR tree-optimization/90662 - strlen of a string in a vla plus offset not folded

gcc/ChangeLog:

	PR tree-optimization/90662
	* tree-ssa-strlen.c (get_stridx): Handle simple VLAs and pointers
	to arrays.

gcc/testsuite/ChangeLog:

	PR tree-optimization/90662
	* gcc.dg/strlenopt-62.c: New test.
	* gcc.dg/strlenopt-63.c: New test.
	* gcc.dg/strlenopt-64.c: New test.

From-SVN: r272197
parent 4b557bcc
2019-06-12 Martin Sebor <msebor@redhat.com>
PR tree-optimization/90662
* tree-ssa-strlen.c (get_stridx): Handle simple VLAs and pointers
to arrays.
2019-06-12 Tom de Vries <tdevries@suse.de>
PR tree-optimization/90009
......
2019-06-12 Martin Sebor <msebor@redhat.com>
PR tree-optimization/90662
* gcc.dg/strlenopt-62.c: New test.
* gcc.dg/strlenopt-63.c: New test.
* gcc.dg/strlenopt-64.c: New test.
2019-06-12 Przemyslaw Wirkus <przemyslaw.wirkus@arm.com>
* gcc.target/arm/ssadv16qi.c: New test.
......
/* PR tree-optimization/90662 - strlen of a string in a vla plus offset
not folded
{ dg-do compile }
{ dg-options "-O2 -Wall -fdump-tree-gimple -fdump-tree-optimized" } */
#include "strlenopt.h"
typedef __INT16_TYPE__ int16_t;
typedef __INT32_TYPE__ int32_t;
#define CONCAT(x, y) x ## y
#define CAT(x, y) CONCAT (x, y)
#define FAILNAME(name, counter) \
CAT (CAT (CAT (call_ ## name ##_on_line_, __LINE__), _), counter)
#define FAIL(name, counter) do { \
extern void FAILNAME (name, counter) (void); \
FAILNAME (name, counter)(); \
} while (0)
/* Macro to emit a call to funcation named
call_in_true_branch_not_eliminated_on_line_NNN()
for each call that's expected to be eliminated. The dg-final
scan-tree-dump-time directive at the bottom of the test verifies
that no such call appears in output. */
#define ELIM(expr) \
if (!(expr)) FAIL (in_true_branch_not_eliminated, __COUNTER__); else (void)0
#define ARGS(...) __VA_ARGS__
void sink (void*, ...);
#define T(Type, expect, init, p) \
do { \
Type vla[n]; \
char *ptr = strcpy ((char*)vla, init); \
ELIM (expect == strlen (p)); \
sink (ptr); \
} while (0)
void test_char_vla_local (int n)
{
T (char, 0, "", vla);
T (char, 0, "\0", vla);
T (char, 1, "1", vla);
T (char, 2, "12", vla);
T (char, 3, "123", vla);
T (char, 2, "123", vla + 1);
T (char, 1, "123", &vla[2]);
T (char, 0, "123", &vla[1] + 2);
T (char, 2, "123", &vla[2] - 1);
T (char, 3, "123", &vla[1] - 1);
T (char, 0, "", ptr);
T (char, 0, "\0", ptr);
T (char, 1, "1", ptr);
T (char, 2, "12", ptr);
T (char, 3, "123", ptr);
T (char, 2, "123", ptr + 1);
T (char, 1, "123", &ptr[2]);
T (char, 0, "123", &ptr[1] + 2);
}
void test_int16_vla_local (int n)
{
T (int16_t, 0, "", (char*)vla);
T (int16_t, 0, "\0", (char*)vla);
T (int16_t, 1, "1", (char*)vla);
T (int16_t, 2, "12", (char*)vla);
T (int16_t, 3, "123", (char*)vla);
T (int16_t, 2, "1234", (char*)(vla + 1));
T (int16_t, 2, "123456", (char*)&vla[2]);
T (int16_t, 1, "123456", (char*)&vla[2] + 1);
T (int16_t, 0, "123456", (char*)&vla[2] + 2);
T (int16_t, 0, "123456", (char*)(&vla[1] + 2));
T (int16_t, 3, "123456", (char*)&vla[2] - 1);
T (int16_t, 4, "123456", (char*)&vla[2] - 2);
T (int16_t, 0, "", ptr);
T (int16_t, 0, "\0", ptr);
T (int16_t, 1, "1", ptr);
T (int16_t, 2, "12", ptr);
T (int16_t, 3, "123", ptr);
T (int16_t, 2, "123", ptr + 1);
T (int16_t, 1, "123", &ptr[2]);
T (int16_t, 0, "123", &ptr[1] + 2);
}
void test_int_vla_local (int n)
{
T (int16_t, 0, "", (char*)vla);
T (int16_t, 0, "\0", (char*)vla);
T (int16_t, 1, "1", (char*)vla);
T (int16_t, 2, "12", (char*)vla);
T (int16_t, 3, "123", (char*)vla);
T (int16_t, 2, "1234", (char*)(vla + 1));
T (int16_t, 2, "123456", (char*)&vla[2]);
T (int16_t, 1, "123456", (char*)&vla[2] + 1);
T (int16_t, 0, "123456", (char*)&vla[2] + 2);
T (int16_t, 0, "123456", (char*)(&vla[1] + 2));
T (int, 0, "", ptr);
T (int, 0, "\0", ptr);
T (int, 1, "1", ptr);
T (int, 2, "12", ptr);
T (int, 3, "123", ptr);
T (int, 2, "123", ptr + 1);
T (int, 1, "123", &ptr[2]);
T (int, 0, "123", &ptr[1] + 2);
}
#undef T
#define T(Type, expect, parr, init, p) \
do { \
Type (*parray)[] = *ppa++; \
char *ptr = strcpy ((char*)parr, init); \
(void)&ptr; \
ELIM (expect == strlen (p)); \
} while (0)
/* Have the function take a pointer to pointers to arrays so that each
test case can use its own pointer to avoid interference between. */
void test_char_array_ptr (char (**ppa)[])
{
T (char, 0, *parray, "", *parray);
T (char, 0, *parray, "", &(*parray)[0]);
T (char, 1, *parray, "1", &(*parray)[0]);
T (char, 0, *parray, "1", &(*parray)[1]);
T (char, 2, *parray, "12", &(*parray)[0]);
T (char, 1, *parray, "12", &(*parray)[1]);
T (char, 0, *parray, "12", &(*parray)[2]);
T (char, 3, *parray, "123", &(*parray)[0]);
T (char, 2, *parray, "123", &(*parray)[1]);
T (char, 1, *parray, "123", &(*parray)[2]);
T (char, 0, *parray, "123", &(*parray)[3]);
T (char, 3, *parray, "123", ptr);
T (char, 2, *parray, "123", &ptr[1]);
T (char, 1, *parray, "123", &ptr[2]);
T (char, 0, *parray, "123", &ptr[3]);
}
void test_int16_array_ptr (int16_t (**ppa)[])
{
T (int16_t, 0, *parray, "", (char*)*parray);
T (int16_t, 0, *parray, "", (char*)&(*parray)[0]);
T (int16_t, 1, *parray, "1", (char*)&(*parray)[0]);
T (int16_t, 0, *parray, "12", (char*)&(*parray)[1]);
T (int16_t, 2, *parray, "12", (char*)&(*parray)[0]);
T (int16_t, 1, *parray, "12", (char*)&(*parray)[0] + 1);
T (int16_t, 2, *parray, "1234", (char*)&(*parray)[1]);
T (int16_t, 1, *parray, "1234", (char*)&(*parray)[1] + 1);
T (int16_t, 0, *parray, "1234", (char*)&(*parray)[2]);
T (int16_t, 6, *parray, "123456", (char*)&(*parray)[0]);
T (int16_t, 5, *parray, "123456", (char*)&(*parray)[0] + 1);
T (int16_t, 0, *parray, "123456", (char*)&(*parray)[0] + 6);
T (int16_t, 4, *parray, "123456", (char*)&(*parray)[1]);
T (int16_t, 3, *parray, "123456", (char*)&(*parray)[1] + 1);
T (int16_t, 0, *parray, "123456", (char*)&(*parray)[1] + 4);
T (int16_t, 2, *parray, "123456", (char*)&(*parray)[2]);
T (int16_t, 1, *parray, "123456", (char*)&(*parray)[2] + 1);
T (int16_t, 0, *parray, "123456", (char*)&(*parray)[2] + 2);
T (int16_t, 0, *parray, "123456", (char*)&(*parray)[3]);
T (int16_t, 3, *parray, "123", (char*)ptr);
T (int16_t, 2, *parray, "123", (char*)&ptr[1]);
T (int16_t, 1, *parray, "123", (char*)&ptr[2]);
T (int16_t, 0, *parray, "123", (char*)&ptr[3]);
}
/* { dg-final { scan-tree-dump-times "strlen" 0 "optimized" } }
{ dg-final { scan-tree-dump-times "not_eliminated" 0 "optimized" } } */
/* PR tree-optimization/90662 - strlen of a string in a vla plus offset
not folded
Verify that strlen of pointers to char arrays are computed correctly
(whether folded or not).
{ dg-do run }
{ dg-options "-O2 -Wall" } */
#include "strlenopt.h"
#define A(expr) \
((expr) \
? (void)0 \
: (__builtin_printf ("assertion failed on line %i: %s\n", \
__LINE__, #expr), \
__builtin_abort ()))
typedef char A5[5];
A5 a5[5];
A5* p[5] = { &a5[4], &a5[3], &a5[2], &a5[1], &a5[0] };
__attribute__ ((noclone, noinline, noipa))
void deref_deref (void)
{
strcpy (**p, "12345");
A (strlen (**p) == 5);
}
__attribute__ ((noclone, noinline, noipa))
void deref_idx_0 (void)
{
strcpy (*p[0], "");
A (strlen (*p[0]) == 0);
}
__attribute__ ((noclone, noinline, noipa))
void deref_idx_1 (void)
{
strcpy (*p[1], "1");
A (strlen (*p[1]) == 1);
A (strlen (&(*p[1])[1]) == 0);
A (strlen (*p[0]) == 0);
}
__attribute__ ((noclone, noinline, noipa))
void deref_idx_2 (void)
{
strcpy (*p[2], "12");
A (strlen (*p[2]) == 2);
A (strlen (&(*p[2])[1]) == 1);
A (strlen (&(*p[2])[2]) == 0);
A (strlen (*p[1]) == 1);
A (strlen (*p[0]) == 0);
}
__attribute__ ((noclone, noinline, noipa))
void deref_idx_3 (void)
{
strcpy (*p[3], "123");
A (strlen (*p[3]) == 3);
A (strlen (&(*p[3])[1]) == 2);
A (strlen (&(*p[3])[2]) == 1);
A (strlen (&(*p[3])[3]) == 0);
A (strlen (*p[2]) == 2);
A (strlen (*p[1]) == 1);
A (strlen (*p[0]) == 0);
}
__attribute__ ((noclone, noinline, noipa))
void deref_idx_4 (void)
{
strcpy (*p[4], "1234");
A (strlen (*p[4]) == 4);
A (strlen (&(*p[4])[1]) == 3);
A (strlen (&(*p[4])[2]) == 2);
A (strlen (&(*p[4])[3]) == 1);
A (strlen (&(*p[4])[4]) == 0);
A (strlen (*p[3]) == 3);
A (strlen (*p[2]) == 2);
A (strlen (*p[1]) == 1);
A (strlen (*p[0]) == 0);
}
__attribute__ ((noclone, noinline, noipa))
void deref_idx_4_x (void)
{
strcpy (*p[4], "");
A (strlen (*p[4]) == 0);
A (strlen (*p[3]) == 3);
A (strlen (*p[2]) == 2);
A (strlen (*p[1]) == 1);
A (strlen (*p[0]) == 0);
}
__attribute__ ((noclone, noinline, noipa))
void deref_idx_3_x (void)
{
strcpy (&(*p[3])[0], "1");
A (strlen (*p[4]) == 0);
A (strlen (*p[3]) == 1);
A (strlen (*p[2]) == 2);
A (strlen (*p[1]) == 1);
A (strlen (*p[0]) == 0);
}
__attribute__ ((noclone, noinline, noipa))
void deref_idx_2_x (void)
{
strcpy (*p[2], "12");
A (strlen (*p[4]) == 0);
A (strlen (*p[3]) == 1);
A (strlen (*p[2]) == 2);
A (strlen (*p[1]) == 1);
A (strlen (*p[0]) == 0);
}
__attribute__ ((noclone, noinline, noipa))
void deref_idx_1_x (void)
{
strcpy (*p[1], "123");
A (strlen (*p[4]) == 0);
A (strlen (*p[3]) == 1);
A (strlen (*p[2]) == 2);
A (strlen (*p[1]) == 3);
A (strlen (*p[0]) == 0);
}
__attribute__ ((noclone, noinline, noipa))
void deref_idx_0_x (void)
{
strcpy (*p[0], "1234");
A (strlen (*p[4]) == 0);
A (strlen (*p[3]) == 1);
A (strlen (*p[2]) == 2);
A (strlen (*p[1]) == 3);
A (strlen (*p[0]) == 4);
}
int main (void)
{
deref_deref ();
deref_idx_0 ();
deref_idx_1 ();
deref_idx_2 ();
deref_idx_3 ();
deref_idx_4 ();
deref_idx_4_x ();
deref_idx_3_x ();
deref_idx_2_x ();
deref_idx_1_x ();
deref_idx_0_x ();
}
/* PR tree-optimization/90662 - strlen of a string in a vla plus offset
not folded
Verify that strlen of pointers to wide character arrays (emulated
by int16_t) are computed correctly (whether folded or not).
{ dg-do run }
{ dg-options "-O2 -Wall -Wno-incompatible-pointer-types" } */
#include "strlenopt.h"
typedef __INT16_TYPE__ int16_t;
#define A(expr) \
((expr) \
? (void)0 \
: (__builtin_printf ("assertion failed on line %i: %s\n", \
__LINE__, #expr), \
__builtin_abort ()))
typedef int16_t A5[5];
A5 a5[5];
A5* p[5] = { &a5[4], &a5[3], &a5[2], &a5[1], &a5[0] };
__attribute__ ((noclone, noinline, noipa))
void deref_deref (void)
{
strcpy (**p, "12345");
A (strlen (**p) == 5);
}
__attribute__ ((noclone, noinline, noipa))
void deref_idx_0 (void)
{
strcpy (*p[0], "");
A (strlen (*p[0]) == 0);
}
__attribute__ ((noclone, noinline, noipa))
void deref_idx_1 (void)
{
strcpy (*p[1], "12");
A (strlen (*p[1]) == 2);
A (strlen (&(*p[1])[1]) == 0);
A (strlen ((char*)*p[1] + 1) == 1);
A (strlen ((char*)*p[1] + 2) == 0);
A (strlen ((char*)*p[1] + 3) == 0);
A (strlen ((char*)&(*p[1])[1] + 1) == 0);
A (strlen (*p[0]) == 0);
}
__attribute__ ((noclone, noinline, noipa))
void deref_idx_2 (void)
{
strcpy (*p[2], "1234");
A (strlen (*p[2]) == 4);
A (strlen (&(*p[2])[1]) == 2);
A (strlen (&(*p[2])[2]) == 0);
A (strlen ((char*)*p[2] + 1) == 3);
A (strlen ((char*)*p[2] + 2) == 2);
A (strlen ((char*)*p[2] + 3) == 1);
A (strlen ((char*)*p[2] + 4) == 0);
A (strlen ((char*)*p[2] + 5) == 0);
A (strlen ((char*)&(*p[2])[1] + 1) == 1);
A (strlen ((char*)&(*p[2])[1] + 2) == 0);
A (strlen (*p[1]) == 2);
A (strlen (*p[0]) == 0);
}
__attribute__ ((noclone, noinline, noipa))
void deref_idx_3 (void)
{
strcpy (*p[3], "123456");
A (strlen (*p[3]) == 6);
A (strlen (&(*p[3])[1]) == 4);
A (strlen (&(*p[3])[2]) == 2);
A (strlen (&(*p[3])[3]) == 0);
A (strlen ((char*)*p[3] + 1) == 5);
A (strlen ((char*)*p[3] + 2) == 4);
A (strlen ((char*)*p[3] + 3) == 3);
A (strlen ((char*)*p[3] + 4) == 2);
A (strlen ((char*)*p[3] + 5) == 1);
A (strlen ((char*)*p[3] + 6) == 0);
A (strlen (*p[2]) == 4);
A (strlen (*p[1]) == 2);
A (strlen (*p[0]) == 0);
}
__attribute__ ((noclone, noinline, noipa))
void deref_idx_4 (void)
{
strcpy (*p[4], "12345678");
A (strlen (*p[4]) == 8);
A (strlen (&(*p[4])[1]) == 6);
A (strlen (&(*p[4])[2]) == 4);
A (strlen (&(*p[4])[3]) == 2);
A (strlen (&(*p[4])[4]) == 0);
A (strlen (*p[3]) == 6);
A (strlen (*p[2]) == 4);
A (strlen (*p[1]) == 2);
A (strlen (*p[0]) == 0);
}
__attribute__ ((noclone, noinline, noipa))
void deref_idx_4_x (void)
{
strcpy (*p[4], "");
A (strlen (*p[4]) == 0);
A (strlen (*p[3]) == 6);
A (strlen (*p[2]) == 4);
A (strlen (*p[1]) == 2);
A (strlen (*p[0]) == 0);
}
__attribute__ ((noclone, noinline, noipa))
void deref_idx_3_x (void)
{
strcpy (&(*p[3])[0], "1");
A (strlen (*p[4]) == 0);
A (strlen (*p[3]) == 1);
A (strlen (*p[2]) == 4);
A (strlen (*p[1]) == 2);
A (strlen (*p[0]) == 0);
}
__attribute__ ((noclone, noinline, noipa))
void deref_idx_2_x (void)
{
strcpy (*p[2], "12");
A (strlen (*p[4]) == 0);
A (strlen (*p[3]) == 1);
A (strlen (*p[2]) == 2);
A (strlen (*p[1]) == 2);
A (strlen (*p[0]) == 0);
}
__attribute__ ((noclone, noinline, noipa))
void deref_idx_1_x (void)
{
strcpy (*p[1], "123");
A (strlen (*p[4]) == 0);
A (strlen (*p[3]) == 1);
A (strlen (*p[2]) == 2);
A (strlen (*p[1]) == 3);
A (strlen (*p[0]) == 0);
}
__attribute__ ((noclone, noinline, noipa))
void deref_idx_0_x (void)
{
strcpy (*p[0], "1234");
A (strlen (*p[4]) == 0);
A (strlen (*p[3]) == 1);
A (strlen (*p[2]) == 2);
A (strlen (*p[1]) == 3);
A (strlen (*p[0]) == 4);
}
int main (void)
{
deref_deref ();
deref_idx_0 ();
deref_idx_1 ();
deref_idx_2 ();
deref_idx_3 ();
deref_idx_4 ();
deref_idx_4_x ();
deref_idx_3_x ();
deref_idx_2_x ();
deref_idx_1_x ();
deref_idx_0_x ();
}
......@@ -296,34 +296,77 @@ get_stridx (tree exp)
{
if (ssa_ver_to_stridx[SSA_NAME_VERSION (exp)])
return ssa_ver_to_stridx[SSA_NAME_VERSION (exp)];
int i;
tree e = exp;
HOST_WIDE_INT off = 0;
for (i = 0; i < 5; i++)
HOST_WIDE_INT offset = 0;
/* Follow a chain of at most 5 assignments. */
for (int i = 0; i < 5; i++)
{
gimple *def_stmt = SSA_NAME_DEF_STMT (e);
if (!is_gimple_assign (def_stmt)
|| gimple_assign_rhs_code (def_stmt) != POINTER_PLUS_EXPR)
if (!is_gimple_assign (def_stmt))
return 0;
tree rhs1 = gimple_assign_rhs1 (def_stmt);
tree rhs2 = gimple_assign_rhs2 (def_stmt);
if (TREE_CODE (rhs1) != SSA_NAME
|| !tree_fits_shwi_p (rhs2))
tree_code rhs_code = gimple_assign_rhs_code (def_stmt);
tree ptr, off;
if (rhs_code == ADDR_EXPR)
{
/* Handle indices/offsets into VLAs which are implemented
as pointers to arrays. */
ptr = gimple_assign_rhs1 (def_stmt);
ptr = TREE_OPERAND (ptr, 0);
/* Handle also VLAs of types larger than char. */
if (tree eltsize = TYPE_SIZE_UNIT (TREE_TYPE (ptr)))
{
if (TREE_CODE (ptr) == ARRAY_REF)
{
off = TREE_OPERAND (ptr, 1);
/* Scale the array index by the size of the element
type (normally 1 for char). */
off = fold_build2 (MULT_EXPR, TREE_TYPE (off), off,
eltsize);
ptr = TREE_OPERAND (ptr, 0);
}
else
off = integer_zero_node;
}
else
return 0;
if (TREE_CODE (ptr) != MEM_REF)
return 0;
/* Add the MEM_REF byte offset. */
tree mem_off = TREE_OPERAND (ptr, 1);
off = fold_build2 (PLUS_EXPR, TREE_TYPE (off), off, mem_off);
ptr = TREE_OPERAND (ptr, 0);
}
else if (rhs_code == POINTER_PLUS_EXPR)
{
ptr = gimple_assign_rhs1 (def_stmt);
off = gimple_assign_rhs2 (def_stmt);
}
else
return 0;
if (TREE_CODE (ptr) != SSA_NAME
|| !tree_fits_shwi_p (off))
return 0;
HOST_WIDE_INT this_off = tree_to_shwi (rhs2);
HOST_WIDE_INT this_off = tree_to_shwi (off);
if (this_off < 0)
return 0;
off = (unsigned HOST_WIDE_INT) off + this_off;
if (off < 0)
offset = (unsigned HOST_WIDE_INT) offset + this_off;
if (offset < 0)
return 0;
if (ssa_ver_to_stridx[SSA_NAME_VERSION (rhs1)])
if (ssa_ver_to_stridx[SSA_NAME_VERSION (ptr)])
{
strinfo *si
= get_strinfo (ssa_ver_to_stridx[SSA_NAME_VERSION (rhs1)]);
if (si && compare_nonzero_chars (si, off) >= 0)
return get_stridx_plus_constant (si, off, exp);
= get_strinfo (ssa_ver_to_stridx[SSA_NAME_VERSION (ptr)]);
if (si && compare_nonzero_chars (si, offset) >= 0)
return get_stridx_plus_constant (si, offset, exp);
}
e = rhs1;
e = ptr;
}
return 0;
}
......
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