aboutsummaryrefslogtreecommitdiff
path: root/gcc/tree-ssa-strlen.c
diff options
context:
space:
mode:
authorMartin Sebor <msebor@redhat.com>2019-01-02 21:38:56 +0000
committerJeff Law <law@gcc.gnu.org>2019-01-02 14:38:56 -0700
commitd4bf69750d31d08068f8242225b8fa06cdf11411 (patch)
treebd9ca177fac5106370caa472164a9245fffa7ab5 /gcc/tree-ssa-strlen.c
parentec1faddf89cb8219c426418d7af772655d5ad40e (diff)
downloadgcc-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.c139
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. */