/* String length optimization
- Copyright (C) 2011-2018 Free Software Foundation, Inc.
+ Copyright (C) 2011-2019 Free Software Foundation, Inc.
Contributed by Jakub Jelinek <jakub@redhat.com>
This file is part of GCC.
static int
get_stridx (tree exp)
{
- tree s, o;
-
if (TREE_CODE (exp) == SSA_NAME)
{
if (ssa_ver_to_stridx[SSA_NAME_VERSION (exp)])
return idx;
}
- s = string_constant (exp, &o);
- if (s != NULL_TREE
- && (o == NULL_TREE || tree_fits_shwi_p (o))
- && TREE_STRING_LENGTH (s) > 0)
- {
- HOST_WIDE_INT offset = o ? tree_to_shwi (o) : 0;
- const char *p = TREE_STRING_POINTER (s);
- int max = TREE_STRING_LENGTH (s) - 1;
+ const char *p = c_getstr (exp);
+ if (p)
+ return ~(int) strlen (p);
- if (p[max] == '\0' && offset >= 0 && offset <= max)
- return ~(int) strlen (p + offset);
- }
return 0;
}
gcc_assert (is_gimple_call (stmt));
callee = gimple_call_fndecl (stmt);
- gcc_assert (callee && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL);
+ gcc_assert (callee && fndecl_built_in_p (callee, BUILT_IN_NORMAL));
lhs = gimple_call_lhs (stmt);
/* unshare_strinfo is intentionally not called here. The (delayed)
transformation of strcpy or strcat into stpcpy is done at the place
if (!integer_zerop (gimple_assign_rhs1 (last.stmt)))
return;
- if (stmt_could_throw_p (last.stmt))
+ if (stmt_could_throw_p (cfun, last.stmt))
return;
gsi = gsi_for_stmt (last.stmt);
unlink_stmt_vdef (last.stmt);
to store the extra '\0' in that case. */
if ((tree_to_uhwi (len) & 3) == 0)
return;
+
+ /* Don't fold away an out of bounds access, as this defeats proper
+ warnings. */
+ tree dst = gimple_call_arg (last.stmt, 0);
+ tree size = compute_objsize (dst, 0);
+ if (size && tree_int_cst_lt (size, len))
+ return;
}
else if (TREE_CODE (len) == SSA_NAME)
{
update_stmt (last.stmt);
}
-/* For an LHS that is an SSA_NAME 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)
- return;
+ if (TREE_CODE (lhs) != SSA_NAME
+ || !INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
+ return NULL_TREE;
if (TREE_CODE (src) == SSA_NAME)
{
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
+ && 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);
- 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);
- }
+ /* 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
+ 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_kind 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
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;
}
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);
+
+ /* Set for strnlen() calls with a non-constant bound. */
+ bool noncst_bound = false;
+ if (bound)
+ {
+ tree new_rhs
+ = fold_build2_loc (loc, MIN_EXPR, TREE_TYPE (rhs), rhs, bound);
+
+ noncst_bound = (TREE_CODE (new_rhs) != INTEGER_CST
+ || tree_int_cst_lt (new_rhs, rhs));
+
+ rhs = new_rhs;
+ }
+
if (!update_call_from_tree (gsi, rhs))
gimplify_and_update_call_from_tree (gsi, rhs);
stmt = gsi_stmt (*gsi);
fprintf (dump_file, "into: ");
print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
}
+
+ /* Avoid storing the length for calls to strnlen() with
+ a non-constant bound. */
+ if (noncst_bound)
+ return;
+
if (si != NULL
&& TREE_CODE (si->nonzero_chars) != SSA_NAME
&& TREE_CODE (si->nonzero_chars) != INTEGER_CST
}
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;
}
}
if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs))
return;
+
if (idx == 0)
idx = new_stridx (src);
else
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);
}
if (idx)
{
- strinfo *si = new_strinfo (src, idx, lhs, true);
- set_strinfo (idx, si);
- find_equal_ptrs (src, idx);
+ if (!bound)
+ {
+ /* Only store the new length information for calls to strlen(),
+ not for those to strnlen(). */
+ strinfo *si = new_strinfo (src, idx, lhs, true);
+ set_strinfo (idx, si);
+ 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));
- }
+ if (strlen_to_stridx && !bound)
+ strlen_to_stridx->put (lhs, stridx_strlenloc (idx, loc));
}
}
tree type = TREE_TYPE (oldlen);
oldlen = fold_build2 (PLUS_EXPR, type, oldlen,
build_int_cst (type, 1));
- check_bounds_or_overlap (as_a <gcall *>(stmt), olddsi->ptr, src,
- oldlen, NULL_TREE);
+ check_bounds_or_overlap (stmt, olddsi->ptr, src, oldlen, NULL_TREE);
}
return;
if (const strinfo *chksi = olddsi ? olddsi : dsi)
if (si
- && !check_bounds_or_overlap (as_a <gcall *>(stmt), chksi->ptr, si->ptr,
- NULL_TREE, len))
+ && !check_bounds_or_overlap (stmt, chksi->ptr, si->ptr, NULL_TREE, len))
{
gimple_set_no_warning (stmt, true);
set_no_warning = true;
assumed to be the argument in some call to strlen() whose relationship
to SRC is being ascertained. */
-static bool
+bool
is_strlen_related_p (tree src, tree len)
{
if (TREE_CODE (TREE_TYPE (len)) == POINTER_TYPE
&& (code == BIT_AND_EXPR
|| code == NOP_EXPR)))
{
- /* Pointer plus (an integer) and integer cast or truncation are
- considered among the (potentially) related expressions to strlen.
- Others are not. */
+ /* Pointer plus (an integer), and truncation are considered among
+ the (potentially) related expressions to strlen. */
return is_strlen_related_p (src, rhs1);
}
+ if (tree rhs2 = gimple_assign_rhs2 (def_stmt))
+ {
+ /* Integer subtraction is considered strlen-related when both
+ arguments are integers and second one is strlen-related. */
+ rhstype = TREE_TYPE (rhs2);
+ if (INTEGRAL_TYPE_P (rhstype) && code == MINUS_EXPR)
+ return is_strlen_related_p (src, rhs2);
+ }
+
return false;
}
cntrange[0] = cntrange[1] = wi::to_wide (cnt);
else if (TREE_CODE (cnt) == SSA_NAME)
{
- enum value_range_type rng = get_range_info (cnt, cntrange, cntrange + 1);
+ enum value_range_kind rng = get_range_info (cnt, cntrange, cntrange + 1);
if (rng == VR_RANGE)
;
else if (rng == VR_ANTI_RANGE)
if (TREE_CODE (dstdecl) == ADDR_EXPR)
dstdecl = TREE_OPERAND (dstdecl, 0);
- /* If the destination refers to a an array/pointer declared nonstring
- return early. */
tree ref = NULL_TREE;
+
+ if (!sidx)
+ {
+ /* If the source is a non-string return early to avoid warning
+ for possible truncation (if the truncation is certain SIDX
+ is non-zero). */
+ tree srcdecl = gimple_call_arg (stmt, 1);
+ if (TREE_CODE (srcdecl) == ADDR_EXPR)
+ srcdecl = TREE_OPERAND (srcdecl, 0);
+ if (get_attr_nonstring_decl (srcdecl, &ref))
+ return false;
+ }
+
+ /* Likewise, if the destination refers to a an array/pointer declared
+ nonstring return early. */
if (get_attr_nonstring_decl (dstdecl, &ref))
return false;
lenrange[0] = lenrange[1] = wi::shwi (~sidx, prec);
else
{
- tree range[2];
- get_range_strlen (src, range);
- if (range[0] != NULL_TREE
- && TREE_CODE (range[0]) == INTEGER_CST
- && range[1] != NULL_TREE
- && TREE_CODE (range[1]) == INTEGER_CST)
+ c_strlen_data lendata = { };
+ get_range_strlen (src, &lendata, /* eltsize = */1);
+ if (TREE_CODE (lendata.minlen) == INTEGER_CST
+ && TREE_CODE (lendata.maxbound) == INTEGER_CST)
{
- lenrange[0] = wi::to_wide (range[0], prec);
- lenrange[1] = wi::to_wide (range[1], prec);
+ /* When LENDATA.MAXLEN is unknown, reset LENDATA.MINLEN
+ which stores the length of the shortest known string. */
+ if (integer_all_onesp (lendata.maxlen))
+ lenrange[0] = wi::shwi (0, prec);
+ else
+ lenrange[0] = wi::to_wide (lendata.minlen, prec);
+ lenrange[1] = wi::to_wide (lendata.maxbound, prec);
}
else
{
}
}
- location_t callloc = gimple_location (stmt);
+ location_t callloc = gimple_nonartificial_location (stmt);
+ callloc = expansion_point_location_if_in_system_header (callloc);
+
tree func = gimple_call_fndecl (stmt);
if (lenrange[0] != 0 || !wi::neg_p (lenrange[1]))
lenrange[0] = wi::shwi (0, prec);
}
- gcall *call = as_a <gcall *> (stmt);
+ /* Set to true for strncat whose bound is derived from the length
+ of the destination (the expected usage pattern). */
+ bool cat_dstlen_bounded = false;
+ if (DECL_FUNCTION_CODE (func) == BUILT_IN_STRNCAT)
+ cat_dstlen_bounded = is_strlen_related_p (dst, cnt);
if (lenrange[0] == cntrange[1] && cntrange[0] == cntrange[1])
return warning_n (callloc, OPT_Wstringop_truncation,
"%G%qD output truncated before terminating nul "
"copying %E bytes from a string of the same "
"length",
- call, func, cnt);
- else if (wi::geu_p (lenrange[0], cntrange[1]))
- {
- /* The shortest string is longer than the upper bound of
- the count so the truncation is certain. */
- if (cntrange[0] == cntrange[1])
- return warning_n (callloc, OPT_Wstringop_truncation,
- cntrange[0].to_uhwi (),
- "%G%qD output truncated copying %E byte "
- "from a string of length %wu",
- "%G%qD output truncated copying %E bytes "
- "from a string of length %wu",
- call, func, cnt, lenrange[0].to_uhwi ());
-
- return warning_at (callloc, OPT_Wstringop_truncation,
- "%G%qD output truncated copying between %wu "
- "and %wu bytes from a string of length %wu",
- call, func, cntrange[0].to_uhwi (),
- cntrange[1].to_uhwi (), lenrange[0].to_uhwi ());
- }
- else if (wi::geu_p (lenrange[1], cntrange[1]))
+ stmt, func, cnt);
+ else if (!cat_dstlen_bounded)
{
- /* The longest string is longer than the upper bound of
- the count so the truncation is possible. */
- if (cntrange[0] == cntrange[1])
- return warning_n (callloc, OPT_Wstringop_truncation,
- cntrange[0].to_uhwi (),
- "%G%qD output may be truncated copying %E "
- "byte from a string of length %wu",
- "%G%qD output may be truncated copying %E "
- "bytes from a string of length %wu",
- call, func, cnt, lenrange[1].to_uhwi ());
-
- return warning_at (callloc, OPT_Wstringop_truncation,
- "%G%qD output may be truncated copying between %wu "
- "and %wu bytes from a string of length %wu",
- call, func, cntrange[0].to_uhwi (),
- cntrange[1].to_uhwi (), lenrange[1].to_uhwi ());
+ if (wi::geu_p (lenrange[0], cntrange[1]))
+ {
+ /* The shortest string is longer than the upper bound of
+ the count so the truncation is certain. */
+ if (cntrange[0] == cntrange[1])
+ return warning_n (callloc, OPT_Wstringop_truncation,
+ cntrange[0].to_uhwi (),
+ "%G%qD output truncated copying %E byte "
+ "from a string of length %wu",
+ "%G%qD output truncated copying %E bytes "
+ "from a string of length %wu",
+ stmt, func, cnt, lenrange[0].to_uhwi ());
+
+ return warning_at (callloc, OPT_Wstringop_truncation,
+ "%G%qD output truncated copying between %wu "
+ "and %wu bytes from a string of length %wu",
+ stmt, func, cntrange[0].to_uhwi (),
+ cntrange[1].to_uhwi (), lenrange[0].to_uhwi ());
+ }
+ else if (wi::geu_p (lenrange[1], cntrange[1]))
+ {
+ /* The longest string is longer than the upper bound of
+ the count so the truncation is possible. */
+ if (cntrange[0] == cntrange[1])
+ return warning_n (callloc, OPT_Wstringop_truncation,
+ cntrange[0].to_uhwi (),
+ "%G%qD output may be truncated copying %E "
+ "byte from a string of length %wu",
+ "%G%qD output may be truncated copying %E "
+ "bytes from a string of length %wu",
+ stmt, func, cnt, lenrange[1].to_uhwi ());
+
+ return warning_at (callloc, OPT_Wstringop_truncation,
+ "%G%qD output may be truncated copying between "
+ "%wu and %wu bytes from a string of length %wu",
+ stmt, func, cntrange[0].to_uhwi (),
+ cntrange[1].to_uhwi (), lenrange[1].to_uhwi ());
+ }
}
- if (cntrange[0] != cntrange[1]
+ if (!cat_dstlen_bounded
+ && cntrange[0] != cntrange[1]
&& wi::leu_p (cntrange[0], lenrange[0])
&& wi::leu_p (cntrange[1], lenrange[0] + 1))
{
return warning_at (callloc, OPT_Wstringop_truncation,
"%G%qD output may be truncated copying between "
"%wu and %wu bytes from a string of length %wu",
- call, func, cntrange[0].to_uhwi (),
+ stmt, func, cntrange[0].to_uhwi (),
cntrange[1].to_uhwi (), lenrange[0].to_uhwi ());
}
}
if (wi::to_wide (dstsize) != cntrange[1])
return false;
+ /* Avoid warning for strncpy(a, b, N) calls where the following
+ equalities hold:
+ N == sizeof a && N == sizeof b */
+ if (tree srcsize = compute_objsize (src, 1))
+ if (wi::to_wide (srcsize) == cntrange[1])
+ return false;
+
if (cntrange[0] == cntrange[1])
return warning_at (callloc, OPT_Wstringop_truncation,
"%G%qD specified bound %E equals destination size",
- as_a <gcall *> (stmt), func, cnt);
+ stmt, func, cnt);
}
return false;
else
srcsize = NULL_TREE;
- if (!check_bounds_or_overlap (as_a <gcall *>(stmt), dst, src,
- dstsize, srcsize))
+ if (!check_bounds_or_overlap (stmt, dst, src, dstsize, srcsize))
{
gimple_set_no_warning (stmt, true);
return;
to strlen(S)). */
strinfo *silen = get_strinfo (pss->first);
- location_t callloc = gimple_location (stmt);
+ location_t callloc = gimple_nonartificial_location (stmt);
+ callloc = expansion_point_location_if_in_system_header (callloc);
tree func = gimple_call_fndecl (stmt);
&& warning_at (callloc, OPT_Wstringop_truncation,
"%G%qD output truncated before terminating nul "
"copying as many bytes from a string as its length",
- as_a <gcall *>(stmt), func))
+ stmt, func))
warned = true;
else if (silen && is_strlen_related_p (src, silen->ptr))
warned = warning_at (callloc, OPT_Wstringop_overflow_,
"%G%qD specified bound depends on the length "
"of the source argument",
- as_a <gcall *>(stmt), func);
+ stmt, func);
if (warned)
{
location_t strlenloc = pss->second;
full_string_p = clen > nonzero_chars;
}
+ if (!full_string_p
+ && olddsi
+ && olddsi->nonzero_chars
+ && TREE_CODE (olddsi->nonzero_chars) == INTEGER_CST
+ && tree_int_cst_le (newlen, olddsi->nonzero_chars))
+ {
+ /* The SRC substring being written strictly overlaps
+ a subsequence of the existing string OLDDSI. */
+ newlen = olddsi->nonzero_chars;
+ full_string_p = olddsi->full_string_p;
+ }
+
if (olddsi != NULL && TREE_CODE (len) == SSA_NAME)
adjust_last_stmt (olddsi, stmt, false);
tree sptr = si && si->ptr ? si->ptr : src;
- if (!check_bounds_or_overlap (as_a <gcall *>(stmt), dst, sptr,
- NULL_TREE, slen))
+ if (!check_bounds_or_overlap (stmt, dst, sptr, NULL_TREE, slen))
{
gimple_set_no_warning (stmt, true);
set_no_warning = true;
tree sptr = si && si->ptr ? si->ptr : src;
- if (!check_bounds_or_overlap (as_a <gcall *>(stmt), dst, sptr,
- dstlen, srcsize))
+ if (!check_bounds_or_overlap (stmt, dst, sptr, dstlen, srcsize))
{
gimple_set_no_warning (stmt, true);
set_no_warning = true;
if (endptr)
dst = fold_convert_loc (loc, TREE_TYPE (dst), unshare_expr (endptr));
else
- dst = fold_build2_loc (loc, POINTER_PLUS_EXPR,
- TREE_TYPE (dst), unshare_expr (dst),
+ dst = fold_build2_loc (loc, POINTER_PLUS_EXPR, TREE_TYPE (dst), dst,
fold_convert_loc (loc, sizetype,
unshare_expr (dstlen)));
dst = force_gimple_operand_gsi (gsi, dst, true, NULL_TREE, true,
GSI_SAME_STMT);
+ if (objsz)
+ {
+ objsz = fold_build2_loc (loc, MINUS_EXPR, TREE_TYPE (objsz), objsz,
+ fold_convert_loc (loc, TREE_TYPE (objsz),
+ unshare_expr (dstlen)));
+ objsz = force_gimple_operand_gsi (gsi, objsz, true, NULL_TREE, true,
+ GSI_SAME_STMT);
+ }
if (dump_file && (dump_flags & TDF_DETAILS) != 0)
{
fprintf (dump_file, "Optimizing: ");
}
/* If RHS, either directly or indirectly, refers to a string of constant
- length, return it. Otherwise return a negative value. */
+ length, return the length. Otherwise, if it refers to a character
+ constant, return 1 if the constant is non-zero and 0 if it is nul.
+ Otherwise, return a negative value. */
static HOST_WIDE_INT
-get_string_cst_length (tree rhs)
+get_min_string_length (tree rhs, bool *full_string_p)
{
+ if (INTEGRAL_TYPE_P (TREE_TYPE (rhs)))
+ {
+ if (tree_expr_nonzero_p (rhs))
+ {
+ *full_string_p = false;
+ return 1;
+ }
+
+ *full_string_p = true;
+ return 0;
+ }
+
if (TREE_CODE (rhs) == MEM_REF
&& integer_zerop (TREE_OPERAND (rhs, 1)))
{
{
strinfo *si = get_strinfo (idx);
if (si
- && si->full_string_p
&& tree_fits_shwi_p (si->nonzero_chars))
- return tree_to_shwi (si->nonzero_chars);
+ {
+ *full_string_p = si->full_string_p;
+ return tree_to_shwi (si->nonzero_chars);
+ }
}
}
}
rhs = DECL_INITIAL (rhs);
if (rhs && TREE_CODE (rhs) == STRING_CST)
- return strlen (TREE_STRING_POINTER (rhs));
+ {
+ *full_string_p = true;
+ return strlen (TREE_STRING_POINTER (rhs));
+ }
return -1;
}
-/* Handle a single character store. */
+/* Handle a single or multiple character store either by single
+ character assignment or by multi-character assignment from
+ MEM_REF. */
static bool
handle_char_store (gimple_stmt_iterator *gsi)
si = get_strinfo (idx);
}
- bool storing_zero_p = initializer_zerop (rhs);
- bool storing_nonzero_p = !storing_zero_p && tree_expr_nonzero_p (rhs);
+ /* STORING_NONZERO_P is true iff not all stored characters are zero.
+ STORING_ALL_ZEROS_P is true iff all stored characters are zero.
+ Both are false when it's impossible to determine which is true. */
+ bool storing_nonzero_p;
+ bool storing_all_zeros_p = initializer_zerop (rhs, &storing_nonzero_p);
+ if (!storing_nonzero_p)
+ storing_nonzero_p = tree_expr_nonzero_p (rhs);
+ bool full_string_p = storing_all_zeros_p;
+
/* Set to the length of the string being assigned if known. */
HOST_WIDE_INT rhslen;
{
int cmp = compare_nonzero_chars (si, offset);
gcc_assert (offset == 0 || cmp >= 0);
- if (storing_zero_p && cmp == 0 && si->full_string_p)
+ if (storing_all_zeros_p && cmp == 0 && si->full_string_p)
{
/* When overwriting a '\0' with a '\0', the store can be removed
if we know it has been stored in the current function. */
- if (!stmt_could_throw_p (stmt) && si->writable)
+ if (!stmt_could_throw_p (cfun, stmt) && si->writable)
{
unlink_stmt_vdef (stmt);
release_defs (stmt);
gsi_next (gsi);
return false;
}
- else if (storing_zero_p || storing_nonzero_p || (offset != 0 && cmp > 0))
+ else if (storing_all_zeros_p || storing_nonzero_p || (offset != 0 && cmp > 0))
{
- /* When storing_nonzero_p, we know that the string now starts
- with OFFSET + 1 nonzero characters, but don't know whether
- there's a following nul terminator.
+ /* When STORING_NONZERO_P, we know that the string will start
+ with at least OFFSET + 1 nonzero characters. If storing
+ a single character, set si->NONZERO_CHARS to the result.
+ If storing multiple characters, try to determine the number
+ of leading non-zero characters and set si->NONZERO_CHARS to
+ the result instead.
- When storing_zero_p, we know that the string is now OFFSET
- characters long.
+ When STORING_ALL_ZEROS_P, we know that the string is now
+ OFFSET characters long.
Otherwise, we're storing an unknown value at offset OFFSET,
so need to clip the nonzero_chars to OFFSET. */
+ bool full_string_p = storing_all_zeros_p;
+ HOST_WIDE_INT len = 1;
+ if (storing_nonzero_p)
+ {
+ /* Try to get the minimum length of the string (or
+ individual character) being stored. If it fails,
+ STORING_NONZERO_P guarantees it's at least 1. */
+ len = get_min_string_length (rhs, &full_string_p);
+ if (len < 0)
+ len = 1;
+ }
+
location_t loc = gimple_location (stmt);
tree oldlen = si->nonzero_chars;
if (cmp == 0 && si->full_string_p)
adjust_last_stmt (si, stmt, false);
si = unshare_strinfo (si);
if (storing_nonzero_p)
- si->nonzero_chars = build_int_cst (size_type_node, offset + 1);
+ {
+ gcc_assert (len >= 0);
+ si->nonzero_chars = build_int_cst (size_type_node, offset + len);
+ }
else
si->nonzero_chars = build_int_cst (size_type_node, offset);
- si->full_string_p = storing_zero_p;
- if (storing_zero_p
+ si->full_string_p = full_string_p;
+ if (storing_all_zeros_p
&& ssaname
&& !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ssaname))
si->endptr = ssaname;
si->prev = 0;
}
}
- else if (idx == 0 && (storing_zero_p || storing_nonzero_p))
+ else if (idx == 0 && (storing_all_zeros_p || storing_nonzero_p))
{
if (ssaname)
idx = new_stridx (ssaname);
if (idx != 0)
{
tree ptr = (ssaname ? ssaname : build_fold_addr_expr (lhs));
- tree len = storing_nonzero_p ? size_one_node : size_zero_node;
- si = new_strinfo (ptr, idx, len, storing_zero_p);
+ HOST_WIDE_INT slen = (storing_all_zeros_p
+ ? 0
+ : (storing_nonzero_p
+ ? get_min_string_length (rhs, &full_string_p)
+ : -1));
+ tree len = (slen <= 0
+ ? size_zero_node
+ : build_int_cst (size_type_node, slen));
+ si = new_strinfo (ptr, idx, len, slen >= 0 && full_string_p);
set_strinfo (idx, si);
- if (storing_zero_p
+ if (storing_all_zeros_p
&& ssaname
&& !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ssaname))
si->endptr = ssaname;
}
}
else if (idx == 0
- && (rhslen = get_string_cst_length (gimple_assign_rhs1 (stmt))) >= 0
+ && (rhslen = get_min_string_length (rhs, &full_string_p)) >= 0
&& ssaname == NULL_TREE
&& TREE_CODE (TREE_TYPE (lhs)) == ARRAY_TYPE)
{
if (idx != 0)
{
si = new_strinfo (build_fold_addr_expr (lhs), idx,
- build_int_cst (size_type_node, rhslen), true);
+ build_int_cst (size_type_node, rhslen),
+ full_string_p);
set_strinfo (idx, si);
si->dont_invalidate = true;
}
}
}
- if (si != NULL && offset == 0 && storing_zero_p)
+ if (si != NULL && offset == 0 && storing_all_zeros_p)
{
/* Allow adjust_last_stmt to remove it if the stored '\0'
is immediately overwritten. */
switch (DECL_FUNCTION_CODE (callee))
{
case BUILT_IN_STRLEN:
+ case BUILT_IN_STRNLEN:
handle_builtin_strlen (gsi);
break;
case BUILT_IN_STRCHR:
if we don't have anything better. */
wide_int min, max;
tree type = TREE_TYPE (lhs);
- enum value_range_type vr
+ enum value_range_kind vr
= get_range_info (lhs, &min, &max);
if (vr == VR_VARYING
|| (vr == VR_RANGE