aboutsummaryrefslogtreecommitdiff
path: root/gcc/tree-ssa-strlen.c
diff options
context:
space:
mode:
authorMartin Sebor <msebor@redhat.com>2019-10-16 17:18:57 +0000
committerMartin Sebor <msebor@gcc.gnu.org>2019-10-16 11:18:57 -0600
commit27c14dbc6b01d5b7238d9d6893bcddce19b7056c (patch)
tree91e41b4bdffd010279c5b4036678d1c8f93a21da /gcc/tree-ssa-strlen.c
parentb7bfd3c503b063ad9bbcb975816423e82cf48636 (diff)
downloadgcc-27c14dbc6b01d5b7238d9d6893bcddce19b7056c.zip
gcc-27c14dbc6b01d5b7238d9d6893bcddce19b7056c.tar.gz
gcc-27c14dbc6b01d5b7238d9d6893bcddce19b7056c.tar.bz2
PR tree-optimization/91996 - fold non-constant strlen relational expressions
gcc/testsuite/ChangeLog: PR tree-optimization/91996 * gcc.dg/strlenopt-80.c: New test. * gcc.dg/strlenopt-81.c: New test. gcc/ChangeLog: PR tree-optimization/91996 * tree-ssa-strlen.c (maybe_warn_pointless_strcmp): Improve location information. (compare_nonzero_chars): Add an overload. (count_nonzero_bytes): Add an argument. Call overload above. Handle non-constant lengths in some range. (handle_store): Add an argument. (check_and_optimize_stmt): Pass an argument to handle_store. From-SVN: r277076
Diffstat (limited to 'gcc/tree-ssa-strlen.c')
-rw-r--r--gcc/tree-ssa-strlen.c148
1 files changed, 107 insertions, 41 deletions
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 5b90ad3..bd1eb0d 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -191,12 +191,12 @@ static void handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *);
/* Return:
- - 1 if SI is known to start with more than OFF nonzero characters.
+ * +1 if SI is known to start with more than OFF nonzero characters.
- - 0 if SI is known to start with OFF nonzero characters,
- but is not known to start with more.
+ * 0 if SI is known to start with OFF nonzero characters,
+ but is not known to start with more.
- - -1 if SI might not start with OFF nonzero characters. */
+ * -1 if SI might not start with OFF nonzero characters. */
static inline int
compare_nonzero_chars (strinfo *si, unsigned HOST_WIDE_INT off)
@@ -208,6 +208,33 @@ compare_nonzero_chars (strinfo *si, unsigned HOST_WIDE_INT off)
return -1;
}
+/* Same as above but suitable also for strings with non-constant lengths.
+ Uses RVALS to determine length range. */
+
+static int
+compare_nonzero_chars (strinfo *si, unsigned HOST_WIDE_INT off,
+ const vr_values *rvals)
+{
+ if (!si->nonzero_chars)
+ return -1;
+
+ if (TREE_CODE (si->nonzero_chars) == INTEGER_CST)
+ return compare_tree_int (si->nonzero_chars, off);
+
+ if (TREE_CODE (si->nonzero_chars) != SSA_NAME)
+ return -1;
+
+ const value_range *vr
+ = (CONST_CAST (class vr_values *, rvals)
+ ->get_value_range (si->nonzero_chars));
+
+ value_range_kind rng = vr->kind ();
+ if (rng != VR_RANGE || !range_int_cst_p (vr))
+ return -1;
+
+ return compare_tree_int (vr->min (), off);
+}
+
/* Return true if SI is known to be a zero-length string. */
static inline bool
@@ -3619,7 +3646,8 @@ 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));
+ tree lhs = gimple_call_lhs (stmt);
+ gimple *use = used_only_for_zero_equality (lhs);
if (!use)
return;
@@ -3642,7 +3670,11 @@ maybe_warn_pointless_strcmp (gimple *stmt, HOST_WIDE_INT bound,
/* FIXME: Include a note pointing to the declaration of the smaller
array. */
- location_t stmt_loc = gimple_location (stmt);
+ location_t stmt_loc = gimple_nonartificial_location (stmt);
+ if (stmt_loc == UNKNOWN_LOCATION && EXPR_HAS_LOCATION (lhs))
+ stmt_loc = tree_nonartificial_location (lhs);
+ stmt_loc = expansion_point_location_if_in_system_header (stmt_loc);
+
tree callee = gimple_call_fndecl (stmt);
bool warned = false;
if (siz <= minlen && bound == -1)
@@ -3918,40 +3950,70 @@ static bool
count_nonzero_bytes (tree exp, unsigned HOST_WIDE_INT offset,
unsigned HOST_WIDE_INT nbytes,
unsigned lenrange[3], bool *nulterm,
- bool *allnul, bool *allnonnul, ssa_name_limit_t &snlim)
+ bool *allnul, bool *allnonnul, const vr_values *rvals,
+ ssa_name_limit_t &snlim)
{
int idx = get_stridx (exp);
if (idx > 0)
{
strinfo *si = get_strinfo (idx);
- /* FIXME: Handle non-constant lengths in some range. */
- if (!si || !tree_fits_shwi_p (si->nonzero_chars))
+ if (!si)
return false;
- unsigned len = tree_to_shwi (si->nonzero_chars);
- unsigned size = len + si->full_string_p;
- if (size <= offset)
+ /* Handle both constant lengths as well non-constant lengths
+ in some range. */
+ unsigned HOST_WIDE_INT minlen, maxlen;
+ if (tree_fits_shwi_p (si->nonzero_chars))
+ minlen = maxlen = tree_to_shwi (si->nonzero_chars);
+ else if (nbytes
+ && si->nonzero_chars
+ && TREE_CODE (si->nonzero_chars) == SSA_NAME)
+ {
+ const value_range *vr
+ = CONST_CAST (class vr_values *, rvals)
+ ->get_value_range (si->nonzero_chars);
+ if (vr->kind () != VR_RANGE
+ || !range_int_cst_p (vr))
+ return false;
+
+ minlen = tree_to_uhwi (vr->min ());
+ maxlen = tree_to_uhwi (vr->max ());
+ }
+ else
return false;
- len -= offset;
+ if (maxlen < offset)
+ return false;
- if (len < lenrange[0])
- lenrange[0] = len;
- if (lenrange[1] < len)
- lenrange[1] = len;
- if (lenrange[2] < nbytes)
- lenrange[2] = nbytes;
+ minlen = minlen < offset ? 0 : minlen - offset;
+ maxlen -= offset;
+ if (maxlen + 1 < nbytes)
+ return false;
- if (!si->full_string_p)
+ if (nbytes <= minlen)
*nulterm = false;
- /* Since only the length of the string are known and
- its contents, clear ALLNUL and ALLNONNUL purely on
- the basis of the length. */
- if (len)
- *allnul = false;
- else
+ if (nbytes < minlen)
+ {
+ minlen = nbytes;
+ if (nbytes < maxlen)
+ maxlen = nbytes;
+ }
+
+ if (minlen < lenrange[0])
+ lenrange[0] = minlen;
+ if (lenrange[1] < maxlen)
+ lenrange[1] = maxlen;
+
+ if (lenrange[2] < nbytes)
+ (lenrange[2] = nbytes);
+
+ /* Since only the length of the string are known and not its contents,
+ clear ALLNUL and ALLNONNUL purely on the basis of the length. */
+ *allnul = false;
+ if (minlen < nbytes)
*allnonnul = false;
+
return true;
}
@@ -3960,7 +4022,7 @@ count_nonzero_bytes (tree exp, unsigned HOST_WIDE_INT offset,
if (TREE_CODE (exp) == SSA_NAME)
{
- /* Handle a single-character specially. */
+ /* Handle non-zero single-character stores specially. */
tree type = TREE_TYPE (exp);
if (TREE_CODE (type) == INTEGER_TYPE
&& TYPE_MODE (type) == TYPE_MODE (char_type_node)
@@ -3972,7 +4034,7 @@ count_nonzero_bytes (tree exp, unsigned HOST_WIDE_INT offset,
for an arbitrary constant. */
exp = build_int_cst (type, 1);
return count_nonzero_bytes (exp, offset, 1, lenrange,
- nulterm, allnul, allnonnul, snlim);
+ nulterm, allnul, allnonnul, rvals, snlim);
}
gimple *stmt = SSA_NAME_DEF_STMT (exp);
@@ -3981,6 +4043,7 @@ count_nonzero_bytes (tree exp, unsigned HOST_WIDE_INT offset,
exp = gimple_assign_rhs1 (stmt);
if (TREE_CODE (exp) != MEM_REF)
return false;
+ /* Handle MEM_REF below. */
}
else if (gimple_code (stmt) == GIMPLE_PHI)
{
@@ -3996,7 +4059,7 @@ count_nonzero_bytes (tree exp, unsigned HOST_WIDE_INT offset,
{
tree def = gimple_phi_arg_def (stmt, i);
if (!count_nonzero_bytes (def, offset, nbytes, lenrange, nulterm,
- allnul, allnonnul, snlim))
+ allnul, allnonnul, rvals, snlim))
return false;
}
@@ -4033,7 +4096,7 @@ count_nonzero_bytes (tree exp, unsigned HOST_WIDE_INT offset,
/* Handle MEM_REF = SSA_NAME types of assignments. */
return count_nonzero_bytes (arg, offset, nbytes, lenrange, nulterm,
- allnul, allnonnul, snlim);
+ allnul, allnonnul, rvals, snlim);
}
if (TREE_CODE (exp) == VAR_DECL && TREE_READONLY (exp))
@@ -4132,11 +4195,13 @@ count_nonzero_bytes (tree exp, unsigned HOST_WIDE_INT offset,
return true;
}
-/* Same as above except with an implicit SSA_NAME limit. */
+/* Same as above except with an implicit SSA_NAME limit. RVALS is used
+ to determine ranges of dynamically computed string lengths (the results
+ of strlen). */
static bool
count_nonzero_bytes (tree exp, unsigned lenrange[3], bool *nulterm,
- bool *allnul, bool *allnonnul)
+ bool *allnul, bool *allnonnul, const vr_values *rvals)
{
/* Set to optimistic values so the caller doesn't have to worry about
initializing these and to what. On success, the function will clear
@@ -4149,7 +4214,7 @@ count_nonzero_bytes (tree exp, unsigned lenrange[3], bool *nulterm,
ssa_name_limit_t snlim;
return count_nonzero_bytes (exp, 0, 0, lenrange, nulterm, allnul, allnonnul,
- snlim);
+ rvals, snlim);
}
/* Handle a single or multibyte store other than by a built-in function,
@@ -4158,7 +4223,7 @@ count_nonzero_bytes (tree exp, unsigned lenrange[3], bool *nulterm,
'*(int*)a = 12345'). Return true when handled. */
static bool
-handle_store (gimple_stmt_iterator *gsi)
+handle_store (gimple_stmt_iterator *gsi, const vr_values *rvals)
{
int idx = -1;
strinfo *si = NULL;
@@ -4184,7 +4249,7 @@ handle_store (gimple_stmt_iterator *gsi)
si = get_strinfo (idx);
if (offset == 0)
ssaname = TREE_OPERAND (lhs, 0);
- else if (si == NULL || compare_nonzero_chars (si, offset) < 0)
+ else if (si == NULL || compare_nonzero_chars (si, offset, rvals) < 0)
return true;
}
}
@@ -4214,7 +4279,8 @@ handle_store (gimple_stmt_iterator *gsi)
const bool ranges_valid
= count_nonzero_bytes (rhs, lenrange, &full_string_p,
- &storing_all_zeros_p, &storing_all_nonzero_p);
+ &storing_all_zeros_p, &storing_all_nonzero_p,
+ rvals);
if (ranges_valid)
{
rhs_minlen = lenrange[0];
@@ -4233,7 +4299,7 @@ handle_store (gimple_stmt_iterator *gsi)
/* Fall back on the LHS location if the statement
doesn't have one. */
location_t loc = gimple_nonartificial_location (stmt);
- if (loc == UNKNOWN_LOCATION)
+ if (loc == UNKNOWN_LOCATION && EXPR_HAS_LOCATION (lhs))
loc = tree_nonartificial_location (lhs);
loc = expansion_point_location_if_in_system_header (loc);
if (warning_n (loc, OPT_Wstringop_overflow_,
@@ -4271,15 +4337,15 @@ handle_store (gimple_stmt_iterator *gsi)
{
/* The offset of the last stored byte. */
unsigned HOST_WIDE_INT endoff = offset + lenrange[2] - 1;
- store_before_nul[0] = compare_nonzero_chars (si, offset);
+ store_before_nul[0] = compare_nonzero_chars (si, offset, rvals);
if (endoff == offset)
store_before_nul[1] = store_before_nul[0];
else
- store_before_nul[1] = compare_nonzero_chars (si, endoff);
+ store_before_nul[1] = compare_nonzero_chars (si, endoff, rvals);
}
else
{
- store_before_nul[0] = compare_nonzero_chars (si, offset);
+ store_before_nul[0] = compare_nonzero_chars (si, offset, rvals);
store_before_nul[1] = store_before_nul[0];
gcc_assert (offset == 0 || store_before_nul[0] >= 0);
}
@@ -4841,7 +4907,7 @@ check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh,
}
/* Handle a single or multibyte assignment. */
- if (is_char_store && !handle_store (gsi))
+ if (is_char_store && !handle_store (gsi, rvals))
return false;
}
}