/* 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.
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);
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). */
+/* 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. */
-static tree
-maybe_set_strlen_range (tree lhs, tree src, tree bound)
+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
else if (TREE_CODE (bound) == SSA_NAME)
{
wide_int minbound, maxbound;
- value_range_type rng = get_range_info (bound, &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. */
+ in an unknown range, use the range determined by callers. */
if (wi::ltu_p (minbound, min))
min = minbound;
if (wi::ltu_p (maxbound, max))
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. */
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)
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
{
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",
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: ");
{
/* 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);
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