diff options
author | Martin Sebor <msebor@redhat.com> | 2019-10-16 17:18:57 +0000 |
---|---|---|
committer | Martin Sebor <msebor@gcc.gnu.org> | 2019-10-16 11:18:57 -0600 |
commit | 27c14dbc6b01d5b7238d9d6893bcddce19b7056c (patch) | |
tree | 91e41b4bdffd010279c5b4036678d1c8f93a21da /gcc/tree-ssa-strlen.c | |
parent | b7bfd3c503b063ad9bbcb975816423e82cf48636 (diff) | |
download | gcc-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.c | 148 |
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; } } |