Commit eef2da67 by Martin Sebor Committed by Jeff Law

gimple-fold.c (get_range_strlen_tree): Record if the computed length is optimistic.

	* gimple-fold.c (get_range_strlen_tree): Record if the computed
	length is optimistic.  If it is, then arrange to compute the
	conservative length as well.

	* gcc.dg/strlenopt-40.c: Update
	* gcc.dg/strlenopt-51.c: Likewise.
	* gcc.dg/tree-ssa/pr79376.c: Likewise.

Co-Authored-By: Jeff Law <law@redhat.com>

From-SVN: r267505
parent 2667a5d0
2019-01-01 Martin Sebor <msebor@redhat.com> 2019-01-01 Martin Sebor <msebor@redhat.com>
Jeff Law <law@redhat.com> Jeff Law <law@redhat.com>
* gimple-fold.c (get_range_strlen_tree): Record if the computed
length is optimistic. If it is, then arrange to compute the
conservative length as well.
* gimple-fold.h (get_range_strlen): Update prototype. * gimple-fold.h (get_range_strlen): Update prototype.
* builtins.c (check_access): Update call to get_range_strlen to use * builtins.c (check_access): Update call to get_range_strlen to use
c_strlen_data pointer. Change various variable accesses to instead c_strlen_data pointer. Change various variable accesses to instead
......
...@@ -1291,6 +1291,12 @@ get_range_strlen_tree (tree arg, bitmap *visited, ...@@ -1291,6 +1291,12 @@ get_range_strlen_tree (tree arg, bitmap *visited,
/* The length computed by this invocation of the function. */ /* The length computed by this invocation of the function. */
tree val = NULL_TREE; tree val = NULL_TREE;
/* True if VAL is an optimistic (tight) bound determined from
the size of the character array in which the string may be
stored. In that case, the computed VAL is used to set
PDATA->MAXBOUND. */
bool tight_bound = false;
/* We can end up with &(*iftmp_1)[0] here as well, so handle it. */ /* We can end up with &(*iftmp_1)[0] here as well, so handle it. */
if (TREE_CODE (arg) == ADDR_EXPR if (TREE_CODE (arg) == ADDR_EXPR
&& TREE_CODE (TREE_OPERAND (arg, 0)) == ARRAY_REF) && TREE_CODE (TREE_OPERAND (arg, 0)) == ARRAY_REF)
...@@ -1384,6 +1390,7 @@ get_range_strlen_tree (tree arg, bitmap *visited, ...@@ -1384,6 +1390,7 @@ get_range_strlen_tree (tree arg, bitmap *visited,
&& optype == TREE_TYPE (TREE_OPERAND (arg, 0)) && optype == TREE_TYPE (TREE_OPERAND (arg, 0))
&& array_at_struct_end_p (TREE_OPERAND (arg, 0))) && array_at_struct_end_p (TREE_OPERAND (arg, 0)))
*flexp = true; *flexp = true;
tight_bound = true;
} }
else if (TREE_CODE (arg) == COMPONENT_REF else if (TREE_CODE (arg) == COMPONENT_REF
&& (TREE_CODE (TREE_TYPE (TREE_OPERAND (arg, 1))) && (TREE_CODE (TREE_TYPE (TREE_OPERAND (arg, 1)))
...@@ -1419,17 +1426,24 @@ get_range_strlen_tree (tree arg, bitmap *visited, ...@@ -1419,17 +1426,24 @@ get_range_strlen_tree (tree arg, bitmap *visited,
/* Set the minimum size to zero since the string in /* Set the minimum size to zero since the string in
the array could have zero length. */ the array could have zero length. */
pdata->minlen = ssize_int (0); pdata->minlen = ssize_int (0);
}
if (VAR_P (arg)) /* The array size determined above is an optimistic bound
{ on the length. If the array isn't nul-terminated the
tree type = TREE_TYPE (arg); length computed by the library function would be greater.
if (POINTER_TYPE_P (type)) Even though using strlen to cross the subobject boundary
type = TREE_TYPE (type); is undefined, avoid drawing conclusions from the member
type about the length here. */
if (TREE_CODE (type) == ARRAY_TYPE) tight_bound = true;
}
else if (VAR_P (arg))
{
/* Avoid handling pointers to arrays. GCC might misuse
a pointer to an array of one bound to point to an array
object of a greater bound. */
tree argtype = TREE_TYPE (arg);
if (TREE_CODE (argtype) == ARRAY_TYPE)
{ {
val = TYPE_SIZE_UNIT (type); val = TYPE_SIZE_UNIT (argtype);
if (!val if (!val
|| TREE_CODE (val) != INTEGER_CST || TREE_CODE (val) != INTEGER_CST
|| integer_zerop (val)) || integer_zerop (val))
...@@ -1476,6 +1490,43 @@ get_range_strlen_tree (tree arg, bitmap *visited, ...@@ -1476,6 +1490,43 @@ get_range_strlen_tree (tree arg, bitmap *visited,
else else
pdata->maxbound = val; pdata->maxbound = val;
if (tight_bound)
{
/* VAL computed above represents an optimistically tight bound
on the length of the string based on the referenced object's
or subobject's type. Determine the conservative upper bound
based on the enclosing object's size if possible. */
if (rkind == SRK_LENRANGE || rkind == SRK_LENRANGE_2)
{
poly_int64 offset;
tree base = get_addr_base_and_unit_offset (arg, &offset);
if (!base)
{
/* When the call above fails due to a non-constant offset
assume the offset is zero and use the size of the whole
enclosing object instead. */
base = get_base_address (arg);
offset = 0;
}
/* If the base object is a pointer no upper bound on the length
can be determined. Otherwise the maximum length is equal to
the size of the enclosing object minus the offset of
the referenced subobject minus 1 (for the terminating nul). */
tree type = TREE_TYPE (base);
if (TREE_CODE (type) == POINTER_TYPE
|| !VAR_P (base) || !(val = DECL_SIZE_UNIT (base)))
val = build_all_ones_cst (size_type_node);
else
{
val = DECL_SIZE_UNIT (base);
val = fold_build2 (MINUS_EXPR, TREE_TYPE (val), val,
size_int (offset + 1));
}
}
else
return false;
}
if (pdata->maxlen) if (pdata->maxlen)
{ {
/* Adjust the more conservative bound if possible/necessary /* Adjust the more conservative bound if possible/necessary
......
2019-01-01 Martin Sebor <msebor@redhat.com> 2019-01-01 Martin Sebor <msebor@redhat.com>
Jeff Law <law@redhat.com> Jeff Law <law@redhat.com>
* gcc.dg/strlenopt-40.c: Update
* gcc.dg/strlenopt-51.c: Likewise.
* gcc.dg/tree-ssa/pr79376.c: Likewise.
* gcc.dg/strlenopt-40.c: Disable a couple tests. * gcc.dg/strlenopt-40.c: Disable a couple tests.
* gcc.dg/strlenopt-48.c: Twiddle test slightly. * gcc.dg/strlenopt-48.c: Twiddle test slightly.
* gcc.dg/strlenopt-59.c: New test. * gcc.dg/strlenopt-59.c: New test.
......
/* PR tree-optimization/77357 - strlen of constant strings not folded /* PR tree-optimization/77357 - strlen of constant strings not folded
{ dg-do compile } { dg-do compile }
{ dg-options "-O2 -Wall -fdump-tree-gimple -fdump-tree-optimized" } */ { dg-options "-O0 -Wall -fdump-tree-gimple" } */
#include "strlenopt.h" #include "strlenopt.h"
#define CONCAT(x, y) x ## y #define CONCAT(x, y) x ## y
#define CAT(x, y) CONCAT (x, y) #define CAT(x, y) CONCAT (x, y)
#define FAILNAME(name) CAT (call_ ## name ##_on_line_, __LINE__) #define FAILNAME(name, counter) \
CAT (CAT (CAT (call_ ## name ##_on_line_, __LINE__), _), counter)
#define FAIL(name) do { \ #define FAIL(name, counter) do { \
extern void FAILNAME (name) (void); \ extern void FAILNAME (name, counter) (void); \
FAILNAME (name)(); \ FAILNAME (name, counter)(); \
} while (0) } while (0)
/* Macro to emit a call to funcation named /* Macro to emit a call to funcation named
...@@ -19,19 +20,7 @@ ...@@ -19,19 +20,7 @@
scan-tree-dump-time directive at the bottom of the test verifies scan-tree-dump-time directive at the bottom of the test verifies
that no such call appears in output. */ that no such call appears in output. */
#define ELIM(expr) \ #define ELIM(expr) \
if (!(expr)) FAIL (in_true_branch_not_eliminated); else (void)0 if (!(expr)) FAIL (in_true_branch_not_eliminated, __COUNTER__); else (void)0
/* Macro to emit a call to a function named
call_made_in_{true,false}_branch_on_line_NNN()
for each call that's expected to be retained. The dg-final
scan-tree-dump-time directive at the bottom of the test verifies
that the expected number of both kinds of calls appears in output
(a pair for each line with the invocation of the KEEP() macro. */
#define KEEP(expr) \
if (expr) \
FAIL (made_in_true_branch); \
else \
FAIL (made_in_false_branch)
#define T(s, n) ELIM (strlen (s) == n) #define T(s, n) ELIM (strlen (s) == n)
...@@ -53,7 +42,7 @@ struct S ...@@ -53,7 +42,7 @@ struct S
const char a9[][9] = { S0, S1, S2, S3, S4, S5, S6, S7, S8 }; const char a9[][9] = { S0, S1, S2, S3, S4, S5, S6, S7, S8 };
void test_elim_a9 (int i) void test_elim_a9 (unsigned i)
{ {
ELIM (strlen (&a9[0][i]) > 0); ELIM (strlen (&a9[0][i]) > 0);
ELIM (strlen (&a9[1][i]) > 1); ELIM (strlen (&a9[1][i]) > 1);
...@@ -75,10 +64,10 @@ const char a9_9[][9][9] = { ...@@ -75,10 +64,10 @@ const char a9_9[][9][9] = {
{ S5, S6, S7, S8, S0, S1, S2, S3, S4 }, { S5, S6, S7, S8, S0, S1, S2, S3, S4 },
{ S6, S7, S8, S0, S1, S2, S3, S4, S5 }, { S6, S7, S8, S0, S1, S2, S3, S4, S5 },
{ S7, S8, S0, S1, S2, S3, S4, S5, S6 }, { S7, S8, S0, S1, S2, S3, S4, S5, S6 },
{ S8, S0, S2, S2, S3, S4, S5, S6, S7 } { S8, S0, S1, S2, S3, S4, S5, S6, S7 }
}; };
void test_elim_a9_9 (int i) void test_elim_a9_9 (unsigned i)
{ {
#undef T #undef T
#define T(I) \ #define T(I) \
...@@ -95,27 +84,4 @@ void test_elim_a9_9 (int i) ...@@ -95,27 +84,4 @@ void test_elim_a9_9 (int i)
T (0); T (1); T (2); T (3); T (4); T (5); T (6); T (7); T (8); T (0); T (1); T (2); T (3); T (4); T (5); T (6); T (7); T (8);
} }
#line 1000 /* { dg-final { scan-tree-dump-times "strlen" 0 "gimple" } } */
void test_keep_a9_9 (int i)
{
#undef T
#define T(I) \
KEEP (strlen (&a9_9[i][I][0]) > (1 + I) % 9); \
KEEP (strlen (&a9_9[i][I][1]) > (1 + I) % 9); \
KEEP (strlen (&a9_9[i][I][2]) > (2 + I) % 9); \
KEEP (strlen (&a9_9[i][I][3]) > (3 + I) % 9); \
KEEP (strlen (&a9_9[i][I][4]) > (4 + I) % 9); \
KEEP (strlen (&a9_9[i][I][5]) > (5 + I) % 9); \
KEEP (strlen (&a9_9[i][I][6]) > (6 + I) % 9); \
KEEP (strlen (&a9_9[i][I][7]) > (7 + I) % 9); \
KEEP (strlen (&a9_9[i][I][8]) > (8 + I) % 9)
T (0); T (1); T (2); T (3); T (4); T (5); T (6); T (7); T (8);
}
/* { dg-final { scan-tree-dump-times "strlen" 72 "gimple" } }
{ dg-final { scan-tree-dump-times "strlen" 63 "optimized" } }
{ dg-final { scan-tree-dump-times "call_made_in_true_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 72 "optimized" } }
{ dg-final { scan-tree-dump-times "call_made_in_false_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 81 "optimized" } } */
...@@ -40,7 +40,18 @@ void test_arrays (int i, struct Arrays *a) ...@@ -40,7 +40,18 @@ void test_arrays (int i, struct Arrays *a)
int n = __builtin_snprintf (0, 0, "%-s", s); int n = __builtin_snprintf (0, 0, "%-s", s);
ASSERT (0 <= n && n < 3); /* Since it's undefined to pass an unterminated array to a %s
directive it would be valid to assume that S above is not
longer than sizeof (A->A3) but the optimization isn't done
because the GIMPLE representation of the %s argument isn't
suffficiently reliable not to confuse it for some other
array. The argument length is therefore assumed to be in
the range [0, PTRDIFF_MAX - 2] and the sprintf result to be
as big as INT_MAX and possibly even negative if the function
were to fail due to a single directive resulting in more than
the 4,095 byte maximum required to be supported.
ASSERT (0 <= n && n < 3);
*/
ASSERT_MAYBE (0 == n); ASSERT_MAYBE (0 == n);
ASSERT_MAYBE (1 == n); ASSERT_MAYBE (1 == n);
...@@ -52,7 +63,7 @@ void test_arrays (int i, struct Arrays *a) ...@@ -52,7 +63,7 @@ void test_arrays (int i, struct Arrays *a)
int n = __builtin_snprintf (0, 0, "%-s", s); int n = __builtin_snprintf (0, 0, "%-s", s);
ASSERT (0 <= n && n < 5); /* ASSERT (0 <= n && n < 5); */
ASSERT_MAYBE (0 == n); ASSERT_MAYBE (0 == n);
ASSERT_MAYBE (1 == n); ASSERT_MAYBE (1 == n);
...@@ -69,7 +80,7 @@ void test_string_and_array (int i, struct Arrays *a) ...@@ -69,7 +80,7 @@ void test_string_and_array (int i, struct Arrays *a)
int n = __builtin_snprintf (0, 0, "%-s", s); int n = __builtin_snprintf (0, 0, "%-s", s);
ASSERT (0 <= n && n < 3); /* ASSERT (0 <= n && n < 3); */
ASSERT_MAYBE (0 == n); ASSERT_MAYBE (0 == n);
ASSERT_MAYBE (1 == n); ASSERT_MAYBE (1 == n);
...@@ -81,7 +92,7 @@ void test_string_and_array (int i, struct Arrays *a) ...@@ -81,7 +92,7 @@ void test_string_and_array (int i, struct Arrays *a)
int n = __builtin_snprintf (0, 0, "%-s", s); int n = __builtin_snprintf (0, 0, "%-s", s);
ASSERT (0 <= n && n < 5); /* ASSERT (0 <= n && n < 5); */
ASSERT_MAYBE (0 == n); ASSERT_MAYBE (0 == n);
ASSERT_MAYBE (1 == n); ASSERT_MAYBE (1 == n);
...@@ -95,7 +106,7 @@ void test_string_and_array (int i, struct Arrays *a) ...@@ -95,7 +106,7 @@ void test_string_and_array (int i, struct Arrays *a)
int n = __builtin_snprintf (0, 0, "%-s", s); int n = __builtin_snprintf (0, 0, "%-s", s);
ASSERT (0 <= n && n < 5); /* ASSERT (0 <= n && n < 5); */
ASSERT_MAYBE (0 == n); ASSERT_MAYBE (0 == n);
ASSERT_MAYBE (1 == n); ASSERT_MAYBE (1 == n);
......
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