diff options
Diffstat (limited to 'gcc/tree-ssa-strlen.c')
-rw-r--r-- | gcc/tree-ssa-strlen.c | 161 |
1 files changed, 122 insertions, 39 deletions
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c index a4064a5..693d9d3 100644 --- a/gcc/tree-ssa-strlen.c +++ b/gcc/tree-ssa-strlen.c @@ -1124,16 +1124,17 @@ adjust_last_stmt (strinfo *si, gimple *stmt, bool is_strcat) update_stmt (last.stmt); } -/* For an LHS that is an SSA_NAME with integer type and for strlen() - argument SRC, set LHS range info to [0, N] if SRC refers to - a character array A[N] with unknown length bounded by N. */ +/* 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 void -maybe_set_strlen_range (tree lhs, tree src) +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; + return NULL_TREE; if (TREE_CODE (src) == SSA_NAME) { @@ -1143,24 +1144,87 @@ maybe_set_strlen_range (tree lhs, tree src) src = gimple_assign_rhs1 (def); } - if (TREE_CODE (src) != ADDR_EXPR) - return; + wide_int max = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node)); + wide_int min = wi::zero (max.get_precision ()); - /* 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 (TREE_TYPE (src)) != ARRAY_TYPE - || array_at_struct_end_p (src)) - return; + 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 && !array_at_struct_end_p (src)) + { + tree type = TREE_TYPE (src); + if (tree dom = TYPE_DOMAIN (type)) + { + tree maxval = TYPE_MAX_VALUE (dom); + if (maxval) + max = wi::to_wide (maxval); + else + max = wi::zero (min.get_precision ()); + + /* 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; + } + } + 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); + } + } + } - tree type = TREE_TYPE (src); - if (tree dom = TYPE_DOMAIN (type)) - if (tree maxval = TYPE_MAX_VALUE (dom)) - { - wide_int max = wi::to_wide (maxval); - wide_int min = wi::zero (max.get_precision ()); - set_range_info (lhs, VR_RANGE, min, max); - } + if (bound) + { + /* For strnlen, adjust MIN and MAX as necessary. If the bound + is less than the size of the array set MAX to it. It it's + greater than MAX and MAX is non-zero bump MAX down to account + for the necessary terminating nul. Otherwise leave it alone. */ + if (TREE_CODE (bound) == INTEGER_CST) + { + wide_int wibnd = wi::to_wide (bound); + int cmp = wi::cmpu (wibnd, max); + if (cmp < 0) + max = wibnd; + else if (cmp && wi::ne_p (max, min)) + --max; + } + else if (TREE_CODE (bound) == SSA_NAME) + { + wide_int minbound, maxbound; + value_range_type rng = get_range_info (bound, &minbound, &maxbound); + if (rng == VR_RANGE) + { + /* 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. */ + if (wi::ltu_p (minbound, min)) + min = minbound; + if (wi::ltu_p (maxbound, max)) + max = maxbound; + } + } + } + + if (min == max) + return wide_int_to_tree (size_type_node, min); + + set_range_info (lhs, VR_RANGE, min, max); + return lhs; } /* Handle a strlen call. If strlen of the argument is known, replace @@ -1170,16 +1234,18 @@ maybe_set_strlen_range (tree lhs, tree src) static void handle_builtin_strlen (gimple_stmt_iterator *gsi) { - int idx; - tree src; gimple *stmt = gsi_stmt (*gsi); tree lhs = gimple_call_lhs (stmt); if (lhs == NULL_TREE) return; - src = gimple_call_arg (stmt, 0); - idx = get_stridx (src); + location_t loc = gimple_location (stmt); + tree callee = gimple_call_fndecl (stmt); + tree src = gimple_call_arg (stmt, 0); + tree bound = (DECL_FUNCTION_CODE (callee) == BUILT_IN_STRNLEN + ? gimple_call_arg (stmt, 1) : NULL_TREE); + int idx = get_stridx (src); if (idx) { strinfo *si = NULL; @@ -1203,8 +1269,9 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi) } rhs = unshare_expr (rhs); if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (rhs))) - rhs = fold_convert_loc (gimple_location (stmt), - TREE_TYPE (lhs), rhs); + rhs = fold_convert_loc (loc, TREE_TYPE (lhs), rhs); + if (bound) + rhs = fold_build2_loc (loc, MIN_EXPR, TREE_TYPE (rhs), rhs, bound); if (!update_call_from_tree (gsi, rhs)) gimplify_and_update_call_from_tree (gsi, rhs); stmt = gsi_stmt (*gsi); @@ -1225,10 +1292,8 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi) } if (strlen_to_stridx) - { - location_t loc = gimple_location (stmt); - strlen_to_stridx->put (lhs, stridx_strlenloc (idx, loc)); - } + strlen_to_stridx->put (lhs, stridx_strlenloc (idx, loc)); + return; } } @@ -1251,7 +1316,6 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi) si->full_string_p = true; if (TREE_CODE (old) == INTEGER_CST) { - location_t loc = gimple_location (stmt); old = fold_convert_loc (loc, TREE_TYPE (lhs), old); tree adj = fold_build2_loc (loc, MINUS_EXPR, TREE_TYPE (lhs), lhs, old); @@ -1274,14 +1338,32 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi) find_equal_ptrs (src, idx); /* For SRC that is an array of N elements, set LHS's range - to [0, N]. */ - maybe_set_strlen_range (lhs, src); + to [0, min (N, BOUND)]. A constant return value means + the range would have consisted of a single value. In + that case, fold the result into the returned constant. */ + if (tree ret = maybe_set_strlen_range (lhs, src, bound)) + if (TREE_CODE (ret) == INTEGER_CST) + { + if (dump_file && (dump_flags & TDF_DETAILS) != 0) + { + fprintf (dump_file, "Optimizing: "); + print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM); + } + if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (ret))) + ret = fold_convert_loc (loc, TREE_TYPE (lhs), ret); + if (!update_call_from_tree (gsi, ret)) + gimplify_and_update_call_from_tree (gsi, ret); + stmt = gsi_stmt (*gsi); + update_stmt (stmt); + if (dump_file && (dump_flags & TDF_DETAILS) != 0) + { + fprintf (dump_file, "into: "); + print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM); + } + } if (strlen_to_stridx) - { - location_t loc = gimple_location (stmt); - strlen_to_stridx->put (lhs, stridx_strlenloc (idx, loc)); - } + strlen_to_stridx->put (lhs, stridx_strlenloc (idx, loc)); } } @@ -3333,6 +3415,7 @@ strlen_check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh) switch (DECL_FUNCTION_CODE (callee)) { case BUILT_IN_STRLEN: + case BUILT_IN_STRNLEN: handle_builtin_strlen (gsi); break; case BUILT_IN_STRCHR: |