diff options
author | Martin Sebor <msebor@redhat.com> | 2019-01-02 21:38:56 +0000 |
---|---|---|
committer | Jeff Law <law@gcc.gnu.org> | 2019-01-02 14:38:56 -0700 |
commit | d4bf69750d31d08068f8242225b8fa06cdf11411 (patch) | |
tree | bd9ca177fac5106370caa472164a9245fffa7ab5 /gcc/tree-ssa-strlen.c | |
parent | ec1faddf89cb8219c426418d7af772655d5ad40e (diff) | |
download | gcc-d4bf69750d31d08068f8242225b8fa06cdf11411.zip gcc-d4bf69750d31d08068f8242225b8fa06cdf11411.tar.gz gcc-d4bf69750d31d08068f8242225b8fa06cdf11411.tar.bz2 |
gimple-fold.c (gimple_fold_builtin_strlen): Use set_strlen_range rather than set_range_info.
* gimple-fold.c (gimple_fold_builtin_strlen): Use set_strlen_range
rather than set_range_info.
* tree-ssa-strlen.c (set_strlen_range): Extracted from
maybe_set_strlen_range. Handle potentially boundary crossing
cases more conservatively.
(maybe_set_strlen_range): Parts refactored into set_strlen_range.
Call set_strlen_range.
* tree-ssa-strlen.h (set_strlen_range): Add prototype.
* gcc.dg/strlenopt-36.c: Update.
* gcc.dg/strlenopt-45.c: Update.
* gcc.c-torture/execute/strlen-5.c: New test.
* gcc.c-torture/execute/strlen-6.c: New test.
* gcc.c-torture/execute/strlen-7.c: New test.
Co-Authored-By: Jeff Law <law@redhat.com>
From-SVN: r267531
Diffstat (limited to 'gcc/tree-ssa-strlen.c')
-rw-r--r-- | gcc/tree-ssa-strlen.c | 139 |
1 files changed, 84 insertions, 55 deletions
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c index f64bc9b..55fba88 100644 --- a/gcc/tree-ssa-strlen.c +++ b/gcc/tree-ssa-strlen.c @@ -1121,67 +1121,23 @@ adjust_last_stmt (strinfo *si, gimple *stmt, bool is_strcat) update_stmt (last.stmt); } -/* For an LHS that is an SSA_NAME and for strlen() or strnlen() argument - SRC, set LHS range info to [0, min (N, BOUND)] if SRC refers to - a character array A[N] with unknown length bounded by N, and for - strnlen(), by min (N, BOUND). */ - -static tree -maybe_set_strlen_range (tree lhs, tree src, tree bound) +/* For an LHS that is an SSA_NAME that is the result of a strlen() + call, or when BOUND is non-null, of a strnlen() call, set LHS + range info to [0, min (MAX, BOUND)] when the range includes more + than one value and return LHS. Otherwise, when the range + [MIN, MAX] is such that MIN == MAX, return the tree representation + of (MIN). The latter allows callers to fold suitable strnlen() calls + to constants. */ + +tree +set_strlen_range (tree lhs, wide_int max, tree bound /* = NULL_TREE */) { if (TREE_CODE (lhs) != SSA_NAME || !INTEGRAL_TYPE_P (TREE_TYPE (lhs))) return NULL_TREE; - if (TREE_CODE (src) == SSA_NAME) - { - gimple *def = SSA_NAME_DEF_STMT (src); - if (is_gimple_assign (def) - && gimple_assign_rhs_code (def) == ADDR_EXPR) - src = gimple_assign_rhs1 (def); - } - - wide_int max = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node)); wide_int min = wi::zero (max.get_precision ()); - if (TREE_CODE (src) == ADDR_EXPR) - { - /* The last array member of a struct can be bigger than its size - suggests if it's treated as a poor-man's flexible array member. */ - src = TREE_OPERAND (src, 0); - bool src_is_array = TREE_CODE (TREE_TYPE (src)) == ARRAY_TYPE; - if (src_is_array - && TREE_CODE (src) != MEM_REF - && !array_at_struct_end_p (src)) - { - tree type = TREE_TYPE (src); - if (tree size = TYPE_SIZE_UNIT (type)) - if (size && TREE_CODE (size) == INTEGER_CST) - max = wi::to_wide (size); - - /* For strlen() the upper bound above is equal to - the longest string that can be stored in the array - (i.e., it accounts for the terminating nul. For - strnlen() bump up the maximum by one since the array - need not be nul-terminated. */ - if (!bound && max != 0) - --max; - } - else - { - if (TREE_CODE (src) == COMPONENT_REF && !src_is_array) - src = TREE_OPERAND (src, 1); - if (DECL_P (src)) - { - /* Handle the unlikely case of strlen (&c) where c is some - variable. */ - if (tree size = DECL_SIZE_UNIT (src)) - if (TREE_CODE (size) == INTEGER_CST) - max = wi::to_wide (size); - } - } - } - if (bound) { /* For strnlen, adjust MIN and MAX as necessary. If the bound @@ -1205,7 +1161,7 @@ maybe_set_strlen_range (tree lhs, tree src, tree bound) { /* For a bound in a known range, adjust the range determined above as necessary. For a bound in some anti-range or - in an unknown range, use the range determined above. */ + in an unknown range, use the range determined by callers. */ if (wi::ltu_p (minbound, min)) min = minbound; if (wi::ltu_p (maxbound, max)) @@ -1221,6 +1177,79 @@ maybe_set_strlen_range (tree lhs, tree src, tree bound) return lhs; } +/* For an LHS that is an SSA_NAME and for strlen() or strnlen() argument + SRC, set LHS range info to [0, min (N, BOUND)] if SRC refers to + a character array A[N] with unknown length bounded by N, and for + strnlen(), by min (N, BOUND). */ + +static tree +maybe_set_strlen_range (tree lhs, tree src, tree bound) +{ + if (TREE_CODE (lhs) != SSA_NAME + || !INTEGRAL_TYPE_P (TREE_TYPE (lhs))) + return NULL_TREE; + + if (TREE_CODE (src) == SSA_NAME) + { + gimple *def = SSA_NAME_DEF_STMT (src); + if (is_gimple_assign (def) + && gimple_assign_rhs_code (def) == ADDR_EXPR) + src = gimple_assign_rhs1 (def); + } + + /* The longest string is PTRDIFF_MAX - 1 bytes including the final + NUL so that the difference between a pointer to just past it and + one to its beginning is positive. */ + wide_int max = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node)) - 2; + + if (TREE_CODE (src) == ADDR_EXPR) + { + /* The last array member of a struct can be bigger than its size + suggests if it's treated as a poor-man's flexible array member. */ + src = TREE_OPERAND (src, 0); + if (TREE_CODE (src) != MEM_REF + && !array_at_struct_end_p (src)) + { + tree type = TREE_TYPE (src); + tree size = TYPE_SIZE_UNIT (type); + if (size + && TREE_CODE (size) == INTEGER_CST + && !integer_zerop (size)) + { + /* Even though such uses of strlen would be undefined, + avoid relying on arrays of arrays in case some genius + decides to call strlen on an unterminated array element + that's followed by a terminated one. Likewise, avoid + assuming that a struct array member is necessarily + nul-terminated (the nul may be in the member that + follows). In those cases, assume that the length + of the string stored in such an array is bounded + by the size of the enclosing object if one can be + determined. */ + tree base = get_base_address (src); + if (VAR_P (base)) + { + if (tree size = DECL_SIZE_UNIT (base)) + if (size + && TREE_CODE (size) == INTEGER_CST + && TREE_CODE (TREE_TYPE (base)) != POINTER_TYPE) + max = wi::to_wide (size); + } + } + + /* For strlen() the upper bound above is equal to + the longest string that can be stored in the array + (i.e., it accounts for the terminating nul. For + strnlen() bump up the maximum by one since the array + need not be nul-terminated. */ + if (!bound && max != 0) + --max; + } + } + + return set_strlen_range (lhs, max, bound); +} + /* Handle a strlen call. If strlen of the argument is known, replace the strlen call with the known value, otherwise remember that strlen of the argument is stored in the lhs SSA_NAME. */ |