[2/4] - Relax strlen range optimization to avoid making assumptions about types
gcc/ChangeLog:
* calls.c (maybe_warn_nonstring_arg): Test for -1.
* gimple-fold.c (get_range_strlen): Forward declare static.
(get_range_strlen_tree): New helper.
(get_range_strlen): Move tree code into get_range_strlen_tree.
Replace type and fuzzy arguments with a single enum.
Avoid optimizing ranges based on type, optimize on decl size intead.
(get_range_strlen): New extern overload.
(get_range_strlen): Use new overload above.
(get_maxval_strlen): Declared static. Assert preconditions.
Use new overload above.
(gimple_fold_builtin_strcpy): Adjust to pass enum.
(gimple_fold_builtin_strncpy): Same.
(gimple_fold_builtin_strcat): Same.
(gimple_fold_builtin_fputs): Same.
(gimple_fold_builtin_memory_chk): Same.
(gimple_fold_builtin_stxcpy_chk): Same.
(gimple_fold_builtin_stxncpy_chk): Same.
(gimple_fold_builtin_snprintf_chk): Same.
(gimple_fold_builtin_sprintf): Same.
(gimple_fold_builtin_snprintf): Same.
(gimple_fold_builtin_strlen): Simplify. Call set_strlen_range.
* gimple-fold.h (StrlenType): New enum.
(get_maxval_strlen): Remove.
(get_range_strlen): Change default argument value to true (strict).
* tree-ssa-strlen.c (set_strlen_range): New function.
(maybe_set_strlen_range): Call it. Make static.
(maybe_diag_stxncpy_trunc): Use new get_range_strlen overload.
Adjust.
* tree-ssa-strlen.h (set_strlen_range): Declare.
gcc/testsuite/ChangeLog:
* g++.dg/init/strlen.C: New test.
* 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.
* gcc.dg/strlenopt-36.c: Remove tests for an overly aggressive
optimization.
* gcc.dg/strlenopt-40.c: Adjust tests to reflect a relaxed
optimization, remove others for an overly aggressive optimization.
* gcc.dg/strlenopt-45.c: Same.
* gcc.dg/strlenopt-48.c: Adjust tests to reflect a relaxed
optimization.
* gcc.dg/strlenopt-51.c: Same.
* gcc.dg/strlenopt-59.c: New test.
@@ -1650,7 +1650,7 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
}
}
- if (lenrng[1] && TREE_CODE (lenrng[1]) == INTEGER_CST)
+ if (lenrng[1] && !integer_all_onesp (lenrng[1]))
{
/* Add one for the nul. */
lenrng[1] = const_binop (PLUS_EXPR, TREE_TYPE (lenrng[1]),
@@ -66,6 +66,9 @@ along with GCC; see the file COPYING3. If not see
#include "tree-vector-builder.h"
#include "tree-ssa-strlen.h"
+static bool
+get_range_strlen (tree, bitmap *, StrlenType, strlen_data_t *);
+
/* Return true when DECL can be referenced from current unit.
FROM_DECL (if non-null) specify constructor of variable DECL was taken from.
We can get declarations that are not possible to reference for various
@@ -1261,224 +1264,290 @@ gimple_fold_builtin_memset (gimple_stmt_iterator *gsi, tree c, tree len)
return true;
}
-
-/* Try to obtain the range of the lengths of the string(s) referenced
- by ARG, or the size of the largest array ARG referes to if the range
- of lengths cannot be determined, and store all in *PDATA.
- If ARG is an SSA name variable, follow its use-def chains. When
- TYPE == 0, then if PDATA->MAXLEN is not equal to the determined
- length or if we are unable to determine the length or value, return
- false.
- VISITED is a bitmap of visited variables.
- TYPE is 0 if string length should be obtained, 1 for maximum string
- length and 2 for maximum value ARG can have.
- When FUZZY is non-zero and the length of a string cannot be determined,
- the function instead considers as the maximum possible length the
- size of a character array it may refer to. If FUZZY is 2, it will handle
- PHIs and COND_EXPRs optimistically, if we can determine string length
- minimum and maximum, it will use the minimum from the ones where it
- can be determined.
- Set PDATA->FLEXP to true if the range of the string lengths has been
- obtained from the upper bound of an array at the end of a struct.
- Such an array may hold a string that's longer than its upper bound
- due to it being used as a poor-man's flexible array member.
- Set PDATA->NONSTR if ARG refers to an unterminated constant array.
- On input, set PDATA->ELTSIZE to 1 for normal single byte character
- strings, and 2 or 4 for wide characer strings. */
+/* Helper of get_range_strlen for ARG that is not SSA_NAME. */
static bool
-get_range_strlen (tree arg, bitmap *visited, int type, int fuzzy,
- strlen_data_t *pdata)
+get_range_strlen_tree (tree arg, bitmap *visited, StrlenType type,
+ strlen_data_t *pdata)
{
- tree var, val = NULL_TREE;
- gimple *def_stmt;
+ gcc_assert (TREE_CODE (arg) != SSA_NAME);
- if (TREE_CODE (arg) != SSA_NAME)
+ /* The length computed by this invocation of the function. */
+ tree val = NULL_TREE;
+
+ /* True if VAL is an optimistic (tight) bound determined from
+ the size of the character array in which the string may be
+ stored. In that case, the computed VAL is used to set
+ PDATA->MAXSIZE. */
+ bool tight_bound = false;
+
+ /* We can end up with &(*iftmp_1)[0] here as well, so handle it. */
+ if (TREE_CODE (arg) == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (arg, 0)) == ARRAY_REF)
{
- /* We can end up with &(*iftmp_1)[0] here as well, so handle it. */
- if (TREE_CODE (arg) == ADDR_EXPR
- && TREE_CODE (TREE_OPERAND (arg, 0)) == ARRAY_REF)
+ tree op = TREE_OPERAND (arg, 0);
+ if (integer_zerop (TREE_OPERAND (op, 1)))
{
- tree op = TREE_OPERAND (arg, 0);
- if (integer_zerop (TREE_OPERAND (op, 1)))
- {
- tree aop0 = TREE_OPERAND (op, 0);
- if (TREE_CODE (aop0) == INDIRECT_REF
- && TREE_CODE (TREE_OPERAND (aop0, 0)) == SSA_NAME)
- return get_range_strlen (TREE_OPERAND (aop0, 0), visited, type,
- fuzzy, pdata);
- }
- else if (TREE_CODE (TREE_OPERAND (op, 0)) == COMPONENT_REF && fuzzy)
- {
- /* Fail if an array is the last member of a struct object
- since it could be treated as a (fake) flexible array
- member. */
- tree idx = TREE_OPERAND (op, 1);
-
- arg = TREE_OPERAND (op, 0);
- tree optype = TREE_TYPE (arg);
- if (tree dom = TYPE_DOMAIN (optype))
- if (tree bound = TYPE_MAX_VALUE (dom))
- if (TREE_CODE (bound) == INTEGER_CST
- && TREE_CODE (idx) == INTEGER_CST
- && tree_int_cst_lt (bound, idx))
- return false;
- }
+ tree aop0 = TREE_OPERAND (op, 0);
+ if (TREE_CODE (aop0) == INDIRECT_REF
+ && TREE_CODE (TREE_OPERAND (aop0, 0)) == SSA_NAME)
+ return get_range_strlen (TREE_OPERAND (aop0, 0), visited, type,
+ pdata);
}
-
- if (type == 2)
+ else if (TREE_CODE (TREE_OPERAND (op, 0)) == COMPONENT_REF
+ && type == StringSizeRange)
{
- val = arg;
- if (TREE_CODE (val) != INTEGER_CST
- || tree_int_cst_sgn (val) < 0)
- return false;
+ /* Fail if an array is the last member of a struct object
+ since it could be treated as a (fake) flexible array
+ member. */
+ tree idx = TREE_OPERAND (op, 1);
+
+ arg = TREE_OPERAND (op, 0);
+ tree optype = TREE_TYPE (arg);
+ if (tree dom = TYPE_DOMAIN (optype))
+ if (tree bound = TYPE_MAX_VALUE (dom))
+ if (TREE_CODE (bound) == INTEGER_CST
+ && TREE_CODE (idx) == INTEGER_CST
+ && tree_int_cst_lt (bound, idx))
+ return false;
}
- else
- val = c_strlen (arg, 1, &pdata->nonstr, pdata->eltsize);
+ }
+
+ if (type == IntegerValue)
+ {
+ /* We are computing the maximum value (not string length). */
+ val = arg;
+ if (TREE_CODE (val) != INTEGER_CST
+ || tree_int_cst_sgn (val) < 0)
+ return false;
+ }
+ else
+ val = c_strlen (arg, 1, &pdata->nonstr, pdata->eltsize);
- if (!val && fuzzy)
+ if (!val && type == StringSizeRange)
+ {
+ if (TREE_CODE (arg) == ADDR_EXPR)
+ return get_range_strlen (TREE_OPERAND (arg, 0), visited, type, pdata);
+
+ if (TREE_CODE (arg) == ARRAY_REF)
{
- if (TREE_CODE (arg) == ADDR_EXPR)
- return get_range_strlen (TREE_OPERAND (arg, 0), visited, type,
- fuzzy, pdata);
+ tree optype = TREE_TYPE (TREE_OPERAND (arg, 0));
- if (TREE_CODE (arg) == ARRAY_REF)
- {
- tree type = TREE_TYPE (TREE_OPERAND (arg, 0));
+ /* Determine the "innermost" array type. */
+ while (TREE_CODE (optype) == ARRAY_TYPE
+ && TREE_CODE (TREE_TYPE (optype)) == ARRAY_TYPE)
+ optype = TREE_TYPE (optype);
- /* Determine the "innermost" array type. */
- while (TREE_CODE (type) == ARRAY_TYPE
- && TREE_CODE (TREE_TYPE (type)) == ARRAY_TYPE)
- type = TREE_TYPE (type);
+ /* Avoid arrays of pointers. */
+ tree eltype = TREE_TYPE (optype);
+ if (TREE_CODE (optype) != ARRAY_TYPE
+ || !INTEGRAL_TYPE_P (eltype))
+ return false;
- /* Avoid arrays of pointers. */
- tree eltype = TREE_TYPE (type);
- if (TREE_CODE (type) != ARRAY_TYPE
- || !INTEGRAL_TYPE_P (eltype))
- return false;
+ /* Fail when the array bound is unknown or zero. */
+ val = TYPE_SIZE_UNIT (optype);
+ if (!val || integer_zerop (val))
+ return false;
- val = TYPE_SIZE_UNIT (type);
- if (!val || integer_zerop (val))
- return false;
+ val = fold_build2 (MINUS_EXPR, TREE_TYPE (val), val, integer_one_node);
+ /* Set the minimum size to zero since the string in
+ the array could have zero length. */
+ pdata->minlen = ssize_int (0);
- val = fold_build2 (MINUS_EXPR, TREE_TYPE (val), val,
- integer_one_node);
- /* Set the minimum size to zero since the string in
- the array could have zero length. */
- pdata->minlen = ssize_int (0);
+ if (TREE_CODE (TREE_OPERAND (arg, 0)) == COMPONENT_REF
+ && optype == TREE_TYPE (TREE_OPERAND (arg, 0))
+ && array_at_struct_end_p (TREE_OPERAND (arg, 0)))
+ pdata->flexarray = true;
- if (TREE_CODE (TREE_OPERAND (arg, 0)) == COMPONENT_REF
- && type == TREE_TYPE (TREE_OPERAND (arg, 0))
- && array_at_struct_end_p (TREE_OPERAND (arg, 0)))
- pdata->flexarray = true;
- }
- else if (TREE_CODE (arg) == COMPONENT_REF
- && (TREE_CODE (TREE_TYPE (TREE_OPERAND (arg, 1)))
- == ARRAY_TYPE))
+ tight_bound = true;
+ }
+ else if (TREE_CODE (arg) == COMPONENT_REF
+ && (TREE_CODE (TREE_TYPE (TREE_OPERAND (arg, 1)))
+ == ARRAY_TYPE))
+ {
+ /* Use the type of the member array to determine the upper
+ bound on the length of the array. This may be overly
+ optimistic if the array itself isn't NUL-terminated and
+ the caller relies on the subsequent member to contain
+ the NUL but that would only be considered valid if
+ the array was the last member of a struct.
+ Set *FLEXP to true if the array whose bound is being
+ used is at the end of a struct. */
+ if (array_at_struct_end_p (arg))
+ pdata->flexarray = true;
+
+ tree fld = TREE_OPERAND (arg, 1);
+
+ tree optype = TREE_TYPE (fld);
+
+ /* Determine the "innermost" array type. */
+ while (TREE_CODE (optype) == ARRAY_TYPE
+ && TREE_CODE (TREE_TYPE (optype)) == ARRAY_TYPE)
+ optype = TREE_TYPE (optype);
+
+ /* Fail when the array bound is unknown or zero. */
+ val = TYPE_SIZE_UNIT (optype);
+ if (!val || integer_zerop (val))
+ return false;
+
+ val = fold_build2 (MINUS_EXPR, TREE_TYPE (val), val, integer_one_node);
+ /* Set the minimum size to zero since the string in
+ the array could have zero length. */
+ pdata->minlen = ssize_int (0);
+
+ /* The array size determined above is an optimistic bound
+ on the length. If the array isn't nul-terminated the
+ length computed by the library function would be greater.
+ Even though using strlen to cross the subobject boundary
+ is undefined, avoid drawing conclusions from the member
+ type about the length here. */
+ tight_bound = true;
+ }
+ else if (VAR_P (arg))
+ {
+ /* Avoid handling pointers to arrays. GCC might misuse
+ a pointer to an array of one bound to point to an array
+ object of a greater bound. */
+ tree argtype = TREE_TYPE (arg);
+ if (TREE_CODE (argtype) == ARRAY_TYPE)
{
- /* Use the type of the member array to determine the upper
- bound on the length of the array. This may be overly
- optimistic if the array itself isn't NUL-terminated and
- the caller relies on the subsequent member to contain
- the NUL but that would only be considered valid if
- the array were the last member of a struct.
- Set *FLEXP to true if the array whose bound is being
- used is at the end of a struct. */
- if (array_at_struct_end_p (arg))
- pdata->flexarray = true;
-
- arg = TREE_OPERAND (arg, 1);
-
- tree type = TREE_TYPE (arg);
-
- while (TREE_CODE (type) == ARRAY_TYPE
- && TREE_CODE (TREE_TYPE (type)) == ARRAY_TYPE)
- type = TREE_TYPE (type);
-
- /* Fail when the array bound is unknown or zero. */
- val = TYPE_SIZE_UNIT (type);
- if (!val || integer_zerop (val))
+ val = TYPE_SIZE_UNIT (argtype);
+ if (!val
+ || TREE_CODE (val) != INTEGER_CST
+ || integer_zerop (val))
return false;
- val = fold_build2 (MINUS_EXPR, TREE_TYPE (val), val,
- integer_one_node);
+ val = wide_int_to_tree (TREE_TYPE (val),
+ wi::sub (wi::to_wide (val), 1));
/* Set the minimum size to zero since the string in
the array could have zero length. */
pdata->minlen = ssize_int (0);
}
-
- if (VAR_P (arg))
- {
- tree type = TREE_TYPE (arg);
- if (POINTER_TYPE_P (type))
- type = TREE_TYPE (type);
-
- if (TREE_CODE (type) == ARRAY_TYPE)
- {
- val = TYPE_SIZE_UNIT (type);
- if (!val
- || TREE_CODE (val) != INTEGER_CST
- || integer_zerop (val))
- return false;
- val = wide_int_to_tree (TREE_TYPE (val),
- wi::sub (wi::to_wide (val), 1));
- /* Set the minimum size to zero since the string in
- the array could have zero length. */
- pdata->minlen = ssize_int (0);
- }
- }
}
+ }
- if (!val)
- return false;
+ if (!val)
+ return false;
- /* Adjust the lower bound on the string length as necessary. */
- if (!pdata->minlen
- || (type > 0
- && TREE_CODE (pdata->minlen) == INTEGER_CST
- && TREE_CODE (val) == INTEGER_CST
- && tree_int_cst_lt (val, pdata->minlen)))
- pdata->minlen = val;
+ /* Adjust the lower bound on the string length as necessary. */
+ if (!pdata->minlen
+ || (type != StringLength
+ && TREE_CODE (pdata->minlen) == INTEGER_CST
+ && TREE_CODE (val) == INTEGER_CST
+ && tree_int_cst_lt (val, pdata->minlen)))
+ pdata->minlen = val;
- if (pdata->maxsize)
+ if (pdata->maxsize)
+ {
+ /* Adjust the tighter (more optimistic) string length bound
+ if necessary and proceed to adjust the more conservative
+ bound. */
+ if (TREE_CODE (val) == INTEGER_CST)
{
- /* Adjust the tighter (more optimistic) bound if necessary
- and proceed to adjust the more conservative bound. */
- if (TREE_CODE (val) == INTEGER_CST)
+ if (TREE_CODE (pdata->maxsize) == INTEGER_CST)
{
- if (TREE_CODE (pdata->maxsize) == INTEGER_CST
- && tree_int_cst_lt (pdata->maxsize, val))
+ if (tree_int_cst_lt (pdata->maxsize, val))
pdata->maxsize = val;
- else
- pdata->maxsize = build_all_ones_cst (size_type_node);
}
else
- pdata->maxsize = val;
+ pdata->maxsize = build_all_ones_cst (size_type_node);
}
else
pdata->maxsize = val;
+ }
+ else
+ pdata->maxsize = val;
- if (pdata->maxlen)
+ if (tight_bound)
+ {
+ /* VAL computed above represents an otpimistically tight bound
+ on the length of the string based on the referenced object's
+ or subobject's type. Determine the conservative upper bound
+ based on the enclosing object's size if possible. */
+ if (type == StringSizeRange)
{
- /* Adjust the more conservative bound if possible/necessary
- and fail otherwise. */
- if (type > 0)
+ poly_int64 offset;
+ tree base = get_addr_base_and_unit_offset (arg, &offset);
+ if (!base)
{
- if (TREE_CODE (pdata->maxlen) != INTEGER_CST
- || TREE_CODE (val) != INTEGER_CST)
- return false;
-
- if (tree_int_cst_lt (pdata->maxlen, val))
- pdata->maxlen = val;
- return true;
+ /* When the call above fails due to a non-constant offset
+ assume the offset is zero and use the size of the whole
+ enclosing object instead. */
+ base = get_base_address (arg);
+ offset = 0;
+ }
+ /* If the base object is a pointer no upper bound on the length
+ can be determined. Otherwise the maximum length is equal to
+ the size of the enclosing object minus the offset of
+ the referenced subobject minus 1 (for the terminating nul). */
+ tree type = TREE_TYPE (base);
+ if (TREE_CODE (type) == POINTER_TYPE
+ || !VAR_P (base) || !(val = DECL_SIZE_UNIT (base)))
+ val = build_all_ones_cst (size_type_node);
+ else
+ {
+ val = DECL_SIZE_UNIT (base);
+ val = fold_build2 (MINUS_EXPR, TREE_TYPE (val), val,
+ size_int (offset + 1));
}
- else if (simple_cst_equal (val, pdata->maxlen) != 1)
- return false;
}
+ else
+ return false;
+ }
- pdata->maxlen = val;
- return true;
+ if (pdata->maxlen)
+ {
+ /* Adjust the more conservative bound if possible/necessary
+ and fail otherwise. */
+ if (type != StringLength)
+ {
+ if (TREE_CODE (pdata->maxlen) != INTEGER_CST
+ || TREE_CODE (val) != INTEGER_CST)
+ return false;
+
+ if (tree_int_cst_lt (pdata->maxlen, val))
+ pdata->maxlen = val;
+ return true;
+ }
+ else if (simple_cst_equal (val, pdata->maxlen) != 1)
+ {
+ /* Fail if the length of this ARG is different from that
+ previosuly determined from another ARG. */
+ return false;
+ }
}
+ pdata->maxlen = val;
+
+ /* When not computing a size-based range, fail if the conservative
+ bound could not be determined. */
+ return type == StringSizeRange || !integer_all_onesp (val);
+}
+
+/* For an ARG referencing one or more strings, try to obtain the range
+ of their lengths, or the size of the largest array ARG referes to if
+ the range of lengths cannot be determined, and store all in *PDATA.
+ For an integer ARG (when TYPE == IntegerValue), try to determine
+ the maximum constant value.
+ If ARG is an SSA_NAME, follow its use-def chains. When TYPE ==
+ StringLength, then if PDATA->MAXLEN is not equal to the determined
+ length or if we are unable to determine the length, return false.
+ TYPE determines the kind of value/range to obtain (see StrlenType).
+ VISITED is a bitmap of visited variables.
+ Set PDATA->FLEXP to true if the range of the string lengths has been
+ obtained from the upper bound of an array at the end of a struct.
+ Such an array may hold a string that's longer than its upper bound
+ due to it being used as a poor-man's flexible array member.
+ Set PDATA->NONSTR if ARG refers to an unterminated constant array.
+ On input, set PDATA->ELTSIZE to 1 for normal single byte character
+ strings, and 2 or 4 for wide characer strings. */
+
+static bool
+get_range_strlen (tree arg, bitmap *visited, StrlenType type,
+ strlen_data_t *pdata)
+{
+ if (TREE_CODE (arg) != SSA_NAME)
+ return get_range_strlen_tree (arg, visited, type, pdata);
+
/* If ARG is registered for SSA update we cannot look at its defining
statement. */
if (name_registered_for_update_p (arg))
@@ -1490,20 +1559,17 @@ get_range_strlen (tree arg, bitmap *visited, int type, int fuzzy,
if (!bitmap_set_bit (*visited, SSA_NAME_VERSION (arg)))
return true;
- var = arg;
- def_stmt = SSA_NAME_DEF_STMT (var);
+ tree var = arg;
+ gimple *def_stmt = SSA_NAME_DEF_STMT (var);
switch (gimple_code (def_stmt))
{
case GIMPLE_ASSIGN:
- /* The RHS of the statement defining VAR must either have a
- constant length or come from another SSA_NAME with a constant
- length. */
if (gimple_assign_single_p (def_stmt)
|| gimple_assign_unary_nop_p (def_stmt))
{
tree rhs = gimple_assign_rhs1 (def_stmt);
- return get_range_strlen (rhs, visited, type, fuzzy, pdata);
+ return get_range_strlen (rhs, visited, type, pdata);
}
else if (gimple_assign_rhs_code (def_stmt) == COND_EXPR)
{
@@ -1511,20 +1577,25 @@ get_range_strlen (tree arg, bitmap *visited, int type, int fuzzy,
gimple_assign_rhs3 (def_stmt) };
for (unsigned int i = 0; i < 2; i++)
- if (!get_range_strlen (ops[i], visited, type, fuzzy, pdata))
+ if (!get_range_strlen (ops[i], visited, type, pdata))
{
- if (fuzzy != 2)
+ if (type != StringSizeRange)
return false;
- pdata->maxsize = build_all_ones_cst (size_type_node);
- pdata->maxlen = pdata->maxsize;
+ /* Set the length range to the maximum to prevent
+ it from being adjusted in the next iteration but
+ leave the more conservative MAXSIZE determined
+ so far alone (or leave it null if it hasn't been
+ set yet). */
+ pdata->minlen = size_zero_node;
+ pdata->maxlen = build_all_ones_cst (size_type_node);
}
return true;
}
return false;
case GIMPLE_PHI:
- /* All the arguments of the PHI node must have the same constant
- length. */
+ /* Unless TYPE == StringSizeRange, all arguments of the PHI node
+ must have a constant length. */
for (unsigned i = 0; i < gimple_phi_num_args (def_stmt); i++)
{
tree arg = gimple_phi_arg (def_stmt, i)->def;
@@ -1538,12 +1609,17 @@ get_range_strlen (tree arg, bitmap *visited, int type, int fuzzy,
if (arg == gimple_phi_result (def_stmt))
continue;
- if (!get_range_strlen (arg, visited, type, fuzzy, pdata))
+ if (!get_range_strlen (arg, visited, type, pdata))
{
- if (fuzzy != 2)
+ if (type != StringSizeRange)
return false;
- pdata->maxsize = build_all_ones_cst (size_type_node);
- pdata->maxlen = pdata->maxsize;
+ /* Set the length range to the maximum to prevent
+ it from being adjusted in subsequent iterations
+ but leave the more conservative MAXSIZE determined
+ so far alone (or leave it null if it hasn't been
+ set yet). */
+ pdata->minlen = size_zero_node;
+ pdata->maxlen = build_all_ones_cst (size_type_node);
}
}
return true;
@@ -1555,30 +1631,28 @@ get_range_strlen (tree arg, bitmap *visited, int type, int fuzzy,
/* Try to obtain the range of the lengths of the string(s) referenced
by ARG, or the size of the largest array ARG refers to if the range
- of lengths cannot be determined, and store all in *PDATA.
- If STRICT is true, handle PHIs and COND_EXPRs conservatively; when
- it's false to handle PHIs and COND_EXPRs optimistically by storing
- the minimum and maximum of only those strings whose length can be
- determined, and ignoring others.
- STRICT of false should be only used for warning code. */
+ of lengths cannot be determined, and store all in *PDATA. */
-bool
-get_range_strlen (tree arg, bool strict, strlen_data_t *pdata)
+void
+get_range_strlen (tree arg, strlen_data_t *pdata)
{
bitmap visited = NULL;
- if (!get_range_strlen (arg, &visited, /* type = */ 1,
- /* fuzzy = */ strict ? 1 : 2, pdata))
+ if (!get_range_strlen (arg, &visited, StringSizeRange, pdata))
{
- pdata->minlen = NULL_TREE;
- pdata->maxsize = NULL_TREE;
- pdata->maxlen = NULL_TREE;
+ /* On failure extend the length range to an impossible maximum
+ (a valid MAXLEN must be less than PTRDIFF_MAX - 1. Other
+ members can stay unchanged regardless. */
+ pdata->minlen = size_zero_node;
+ pdata->maxlen = build_all_ones_cst (size_type_node);
}
+ /* Unless it's null, leave the more conservative MAXSIZE unchanged. */
+ if (!pdata->maxsize)
+ pdata->maxsize = pdata->maxlen;
+
if (visited)
BITMAP_FREE (visited);
-
- return pdata->flexarray;
}
/* Determine the minimum and maximum value or string length that ARG
@@ -1608,37 +1682,56 @@ get_range_strlen (tree arg, bool strict, strlen_data_t *pdata)
bool
get_range_strlen (tree arg, tree minmaxlen[2], unsigned eltsize,
- bool strict, tree *nonstr /* = NULL */)
+ bool strict /* = true */, tree *nonstr /* = NULL */)
{
strlen_data_t data (eltsize);
- bool ret = get_range_strlen (arg, strict, &data);
- minmaxlen[0] = data.minlen;
- minmaxlen[1] = data.maxlen;
+ get_range_strlen (arg, &data);
+ if (strict
+ && integer_zerop (data.minlen)
+ && integer_all_onesp (data.maxlen))
+ minmaxlen[0] = minmaxlen[1] = NULL_TREE;
+ else
+ {
+ minmaxlen[0] = data.minlen;
+ minmaxlen[1] = data.maxlen;
+ }
if (nonstr)
*nonstr = data.nonstr;
- return ret;
+ return data.flexarray;
}
-/* Return the maximum string length for ARG, counting by TYPE
- (1, 2 or 4 for normal or wide chars). NONSTR indicates
- if the caller is prepared to handle unterminated strings.
+/* Return the maximum value for ARG given TYPE (see StrlenType).
+ For ARG of pointer types, NONSTR indicates if the caller is
+ prepared to handle unterminated strings. For integer ARG
+ and when TYPE == IntegerValue, NONSTR must be null.
If an unterminated array is discovered and our caller handles
unterminated arrays, then bubble up the offending DECL and
return the maximum size. Otherwise return NULL. */
-tree
-get_maxval_strlen (tree arg, int type, tree *nonstr /* = NULL */)
+static tree
+get_maxval_strlen (tree arg, StrlenType type, tree *nonstr = NULL)
{
+ /* A non-null NONSTR is meaningless when determining the maximum
+ value of an integer ARG. */
+ gcc_assert (type != IntegerValue || nonstr == NULL);
+ /* ARG must have an integral type when TYPE says so. */
+ gcc_assert (type != IntegerValue || INTEGRAL_TYPE_P (TREE_TYPE (arg)));
+
bitmap visited = NULL;
strlen_data_t data (1);
- if (!get_range_strlen (arg, &visited, type, /* fuzzy = */ 0, &data))
+ /* Redet DATA.MAXLEN if the call fails or when DATA.MAXLEN
+ is unbounded. */
+ if (!get_range_strlen (arg, &visited, type, &data))
+ data.maxlen = NULL_TREE;
+ else if (data.maxlen && integer_all_onesp (data.maxlen))
data.maxlen = NULL_TREE;
+
if (visited)
BITMAP_FREE (visited);
@@ -1697,7 +1790,7 @@ gimple_fold_builtin_strcpy (gimple_stmt_iterator *gsi,
/* Set to non-null if ARG refers to an unterminated array. */
tree nonstr = NULL;
- tree len = get_maxval_strlen (src, 0, &nonstr);
+ tree len = get_maxval_strlen (src, StringLength, &nonstr);
if (nonstr)
{
@@ -1743,7 +1836,7 @@ gimple_fold_builtin_strncpy (gimple_stmt_iterator *gsi,
/* Warn about the lack of nul termination: the result is not
a (nul-terminated) string. */
- tree slen = get_maxval_strlen (src, 0);
+ tree slen = get_maxval_strlen (src, StringLength);
if (slen && !integer_zerop (slen))
warning_at (loc, OPT_Wstringop_truncation,
"%G%qD destination unchanged after copying no bytes "
@@ -1765,7 +1858,7 @@ gimple_fold_builtin_strncpy (gimple_stmt_iterator *gsi,
return false;
/* Now, we must be passed a constant src ptr parameter. */
- tree slen = get_maxval_strlen (src, 0);
+ tree slen = get_maxval_strlen (src, StringLength);
if (!slen || TREE_CODE (slen) != INTEGER_CST)
return false;
@@ -1987,7 +2080,7 @@ gimple_fold_builtin_strcat (gimple_stmt_iterator *gsi, tree dst, tree src)
/* If the length of the source string isn't computable don't
split strcat into strlen and memcpy. */
- tree len = get_maxval_strlen (src, 0);
+ tree len = get_maxval_strlen (src, StringLength);
if (! len)
return false;
@@ -2503,7 +2596,7 @@ gimple_fold_builtin_fputs (gimple_stmt_iterator *gsi,
/* Get the length of the string passed to fputs. If the length
can't be determined, punt. */
- tree len = get_maxval_strlen (arg0, 0);
+ tree len = get_maxval_strlen (arg0, StringLength);
if (!len
|| TREE_CODE (len) != INTEGER_CST)
return false;
@@ -2591,7 +2684,7 @@ gimple_fold_builtin_memory_chk (gimple_stmt_iterator *gsi,
if (! tree_fits_uhwi_p (size))
return false;
- tree maxlen = get_maxval_strlen (len, 2);
+ tree maxlen = get_maxval_strlen (len, IntegerValue);
if (! integer_all_onesp (size))
{
if (! tree_fits_uhwi_p (len))
@@ -2692,7 +2785,7 @@ gimple_fold_builtin_stxcpy_chk (gimple_stmt_iterator *gsi,
if (! tree_fits_uhwi_p (size))
return false;
- tree maxlen = get_maxval_strlen (src, 1);
+ tree maxlen = get_maxval_strlen (src, StringLengthMax);
if (! integer_all_onesp (size))
{
len = c_strlen (src, 1);
@@ -2787,7 +2880,7 @@ gimple_fold_builtin_stxncpy_chk (gimple_stmt_iterator *gsi,
if (! tree_fits_uhwi_p (size))
return false;
- tree maxlen = get_maxval_strlen (len, 2);
+ tree maxlen = get_maxval_strlen (len, IntegerValue);
if (! integer_all_onesp (size))
{
if (! tree_fits_uhwi_p (len))
@@ -2924,7 +3017,7 @@ gimple_fold_builtin_snprintf_chk (gimple_stmt_iterator *gsi,
if (! integer_all_onesp (size))
{
- tree maxlen = get_maxval_strlen (len, 2);
+ tree maxlen = get_maxval_strlen (len, IntegerValue);
if (! tree_fits_uhwi_p (len))
{
/* If LEN is not constant, try MAXLEN too.
@@ -3166,7 +3259,7 @@ gimple_fold_builtin_sprintf (gimple_stmt_iterator *gsi)
tree orig_len = NULL_TREE;
if (gimple_call_lhs (stmt))
{
- orig_len = get_maxval_strlen (orig, 0);
+ orig_len = get_maxval_strlen (orig, StringLength);
if (!orig_len)
return false;
}
@@ -3299,7 +3392,7 @@ gimple_fold_builtin_snprintf (gimple_stmt_iterator *gsi)
if (!orig)
return false;
- tree orig_len = get_maxval_strlen (orig, 0);
+ tree orig_len = get_maxval_strlen (orig, StringLength);
if (!orig_len || TREE_CODE (orig_len) != INTEGER_CST)
return false;
@@ -3607,33 +3700,20 @@ gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi)
gimple *stmt = gsi_stmt (*gsi);
tree arg = gimple_call_arg (stmt, 0);
- wide_int minlen;
- wide_int maxlen;
-
- /* Set to non-null if ARG refers to an unterminated array. */
+ /* NONSTR is set to non-null if ARG refers to an unterminated array.
+ Bail if the argument is unterminated, is a flexible array member
+ or if the length or its range cannot be determined. */
tree nonstr;
tree lenrange[2];
- if (!get_range_strlen (arg, lenrange, 1, true, &nonstr)
- && lenrange[0] && TREE_CODE (lenrange[0]) == INTEGER_CST
- && lenrange[1] && TREE_CODE (lenrange[1]) == INTEGER_CST)
- {
- /* The range of lengths refers to either a single constant
- string or to the longest and shortest constant string
- referenced by the argument of the strlen() call, or to
- the strings that can possibly be stored in the arrays
- the argument refers to. */
- minlen = wi::to_wide (lenrange[0]);
- maxlen = wi::to_wide (lenrange[1]);
- }
- else
- {
- unsigned prec = TYPE_PRECISION (sizetype);
-
- minlen = wi::shwi (0, prec);
- maxlen = wi::to_wide (max_object_size (), prec) - 2;
- }
+ if (get_range_strlen (arg, lenrange, /* eltsize = */ 1,
+ /* strict = */ true, &nonstr)
+ || nonstr
+ || !lenrange[0] || TREE_CODE (lenrange[0]) != INTEGER_CST
+ || !lenrange[1] || TREE_CODE (lenrange[1]) != INTEGER_CST
+ || integer_all_onesp (lenrange[1]))
+ return false;
- if (minlen == maxlen)
+ if (tree_int_cst_equal (lenrange[0], lenrange[1]))
{
lenrange[0] = force_gimple_operand_gsi (gsi, lenrange[0], true, NULL,
true, GSI_SAME_STMT);
@@ -3641,10 +3721,9 @@ gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi)
return true;
}
+ /* Set the strlen() range to [0, LENRANGE[1]]*/
if (tree lhs = gimple_call_lhs (stmt))
- if (TREE_CODE (lhs) == SSA_NAME
- && INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
- set_range_info (lhs, VR_RANGE, minlen, maxlen);
+ set_strlen_range (lhs, wi::to_wide (lenrange[1]));
return false;
}
@@ -70,11 +70,25 @@ struct strlen_data_t
flexarray () { }
};
-extern bool get_range_strlen (tree, strlen_data_t * = NULL);
+extern void get_range_strlen (tree, strlen_data_t *);
+
+enum StrlenType {
+ /* Compute the exact constant string length. */
+ StringLength,
+ /* Compute the maximum constant string length. */
+ StringLengthMax,
+ /* Compute a range of string lengths bounded by object sizes. When
+ the length of a string cannot be determined, consider as the upper
+ bound the size of the enclosing object the string may be a member
+ or element of. Also determine the size of the largest character
+ array the string may refer to. */
+ StringSizeRange,
+ /* Determine the integer value of the argument (not string length). */
+ IntegerValue
+};
-extern bool get_range_strlen (tree, tree[2], unsigned = 1,
- bool = false, tree * = NULL);
-extern tree get_maxval_strlen (tree, int, tree * = NULL);
+extern bool get_range_strlen (tree, tree[2], unsigned = 1, bool = true,
+ tree * = NULL);
extern void gimplify_and_update_call_from_tree (gimple_stmt_iterator *, tree);
extern bool fold_stmt (gimple_stmt_iterator *);
extern bool fold_stmt (gimple_stmt_iterator *, tree (*) (tree));
new file mode 100644
@@ -0,0 +1,43 @@
+// Test to verify that the strlen() optimization doesn't make assumptions
+// about the static type of the object pointed to by its argument. See
+// the following thread for background:
+// https://gcc.gnu.org/ml/gcc-patches/2018-08/msg00260.html
+
+// { dg-do run }
+// { dg-options "-O2 -Wall -fdump-tree-optimized" }
+
+typedef __SIZE_TYPE__ size_t;
+
+void *operator new[] (size_t, void *p) { return p; }
+
+struct S { int x; char a[1]; char b[64]; };
+
+__attribute__ ((noipa)) void
+init (char *s)
+{
+ *s++ = '1';
+ *s++ = '\0';
+}
+
+__attribute__ ((noipa)) void
+test_dynamic_type (S *p)
+{
+ // The placement new call below isn't strictly valid because it
+ // creates an object that is larger than the space of the p->a
+ // subobject in which it is created. However, the corresponding
+ // GIMPLE considers it valid and there's apparently no way to
+ // distinguish invalid cases from ones like it that might be valid.
+ // If/when GIMPLE changes to make this possible this test can be
+ // removed.
+ char *q = new (p->a) char [16];
+
+ init (q);
+
+ if (0 == __builtin_strlen (q))
+ __builtin_abort();
+}
+
+int main ()
+{
+ test_dynamic_type (new S);
+}
new file mode 100644
@@ -0,0 +1,653 @@
+/* Test to verify that even strictly undefined strlen() calls with
+ unterminated character arrays yield the "expected" results when
+ the terminating nul is present in a subsequent suobobject. */
+
+extern __SIZE_TYPE__ strlen (const char *);
+
+unsigned nfails;
+
+#define A(expr, N) \
+ do { \
+ const char *s = (expr); \
+ unsigned n = strlen (s); \
+ ((n == N) \
+ ? 0 \
+ : (__builtin_printf ("line %i: strlen (%s = \"%s\")" \
+ " == %u failed\n", \
+ __LINE__, #expr, s, N), \
+ ++nfails)); \
+ } while (0)
+
+
+int idx;
+
+
+const char ca[][4] = {
+ { '1', '2', '3', '4' }, { '5' },
+ { '1', '2', '3', '4' }, { '5', '6' },
+ { '1', '2', '3', '4' }, { '5', '6', '7' },
+ { '1', '2', '3', '4' }, { '5', '6', '7', '8' },
+ { '9' }
+};
+
+static void test_const_global_arrays (void)
+{
+ A (ca[0], 5);
+ A (&ca[0][0], 5);
+ A (&ca[0][1], 4);
+ A (&ca[0][3], 2);
+
+ int i = 0;
+ A (ca[i], 5);
+ A (&ca[i][0], 5);
+ A (&ca[i][1], 4);
+ A (&ca[i][3], 2);
+
+ int j = i;
+ A (&ca[i][i], 5);
+ A (&ca[i][j + 1], 4);
+ A (&ca[i][j + 2], 3);
+
+ A (&ca[idx][i], 5);
+ A (&ca[idx][j + 1], 4);
+ A (&ca[idx][j + 2], 3);
+
+ A (&ca[idx][idx], 5);
+ A (&ca[idx][idx + 1], 4);
+ A (&ca[idx][idx + 2], 3);
+
+ A (&ca[0][++j], 4);
+ A (&ca[0][++j], 3);
+ A (&ca[0][++j], 2);
+
+ if (j != 3)
+ ++nfails;
+}
+
+
+static void test_const_local_arrays (void)
+{
+ const char a[][4] = {
+ { '1', '2', '3', '4' }, { '5' },
+ { '1', '2', '3', '4' }, { '5', '6' },
+ { '1', '2', '3', '4' }, { '5', '6', '7' },
+ { '1', '2', '3', '4' }, { '5', '6', '7', '8' },
+ { '9' }
+ };
+
+ A (a[0], 5);
+ A (&a[0][0], 5);
+ A (&a[0][1], 4);
+ A (&a[0][3], 2);
+
+ int i = 0;
+ A (a[i], 5);
+ A (&a[i][0], 5);
+ A (&a[i][1], 4);
+ A (&a[i][3], 2);
+
+ int j = i;
+ A (&a[i][i], 5);
+ A (&a[i][j + 1], 4);
+ A (&a[i][j + 2], 3);
+
+ A (&a[idx][i], 5);
+ A (&a[idx][j + 1], 4);
+ A (&a[idx][j + 2], 3);
+
+ A (&a[idx][idx], 5);
+ A (&a[idx][idx + 1], 4);
+ A (&a[idx][idx + 2], 3);
+
+ A (&a[0][++j], 4);
+ A (&a[0][++j], 3);
+ A (&a[0][++j], 2);
+
+ if (j != 3)
+ ++nfails;
+}
+
+
+char va[][4] = {
+ { '1', '2', '3', '4' }, { '5' },
+ { '1', '2', '3', '4' }, { '5', '6' },
+ { '1', '2', '3', '4' }, { '5', '6', '7' },
+ { '1', '2', '3', '4' }, { '5', '6', '7', '8' },
+ { '9' }
+};
+
+static void test_nonconst_global_arrays (void)
+{
+ {
+ A (va[0], 5);
+ A (&va[0][0], 5);
+ A (&va[0][1], 4);
+ A (&va[0][3], 2);
+
+ int i = 0;
+ A (va[i], 5);
+ A (&va[i][0], 5);
+ A (&va[i][1], 4);
+ A (&va[i][3], 2);
+
+ int j = i;
+ A (&va[i][i], 5);
+ A (&va[i][j + 1], 4);
+ A (&va[i][j + 2], 3);
+
+ A (&va[idx][i], 5);
+ A (&va[idx][j + 1], 4);
+ A (&va[idx][j + 2], 3);
+
+ A (&va[idx][idx], 5);
+ A (&va[idx][idx + 1], 4);
+ A (&va[idx][idx + 2], 3);
+ }
+
+ {
+ A (va[2], 6);
+ A (&va[2][0], 6);
+ A (&va[2][1], 5);
+ A (&va[2][3], 3);
+
+ int i = 2;
+ A (va[i], 6);
+ A (&va[i][0], 6);
+ A (&va[i][1], 5);
+ A (&va[i][3], 3);
+
+ int j = i - 1;
+ A (&va[i][j - 1], 6);
+ A (&va[i][j], 5);
+ A (&va[i][j + 1], 4);
+
+ A (&va[idx + 2][i - 1], 5);
+ A (&va[idx + 2][j], 5);
+ A (&va[idx + 2][j + 1], 4);
+ }
+
+ int j = 0;
+
+ A (&va[0][++j], 4);
+ A (&va[0][++j], 3);
+ A (&va[0][++j], 2);
+
+ if (j != 3)
+ ++nfails;
+}
+
+
+static void test_nonconst_local_arrays (void)
+{
+ char a[][4] = {
+ { '1', '2', '3', '4' }, { '5' },
+ { '1', '2', '3', '4' }, { '5', '6' },
+ { '1', '2', '3', '4' }, { '5', '6', '7' },
+ { '1', '2', '3', '4' }, { '5', '6', '7', '8' },
+ { '9' }
+ };
+
+ A (a[0], 5);
+ A (&a[0][0], 5);
+ A (&a[0][1], 4);
+ A (&a[0][3], 2);
+
+ int i = 0;
+ A (a[i], 5);
+ A (&a[i][0], 5);
+ A (&a[i][1], 4);
+ A (&a[i][3], 2);
+
+ int j = i;
+ A (&a[i][i], 5);
+ A (&a[i][j + 1], 4);
+ A (&a[i][j + 2], 3);
+
+ A (&a[idx][i], 5);
+ A (&a[idx][j + 1], 4);
+ A (&a[idx][j + 2], 3);
+
+ A (&a[idx][idx], 5);
+ A (&a[idx][idx + 1], 4);
+ A (&a[idx][idx + 2], 3);
+
+ A (&a[0][++j], 4);
+ A (&a[0][++j], 3);
+ A (&a[0][++j], 2);
+
+ if (j != 3)
+ ++nfails;
+}
+
+
+struct MemArrays { char a[4], b[4]; };
+
+const struct MemArrays cma[] = {
+ { { '1', '2', '3', '4' }, { '5' } },
+ { { '1', '2', '3', '4' }, { '5', '6' } },
+ { { '1', '2', '3', '4' }, { '5', '6' } },
+ { { '1', '2', '3', '4' }, { '5', '6', '7' } },
+ { { '1', '2', '3', '4' }, { '5', '6', '7', '8' } },
+ { { '9' }, { '\0' } }
+};
+
+static void test_const_global_member_arrays (void)
+{
+ {
+ A (cma[0].a, 5);
+ A (&cma[0].a[0], 5);
+ A (&cma[0].a[1], 4);
+ A (&cma[0].a[2], 3);
+
+ int i = 0;
+ A (cma[i].a, 5);
+ A (&cma[i].a[0], 5);
+ A (&cma[i].a[1], 4);
+ A (&cma[i].a[2], 3);
+
+ int j = i;
+ A (&cma[i].a[j], 5);
+ A (&cma[i].a[j + 1], 4);
+ A (&cma[i].a[j + 2], 3);
+
+ A (&cma[idx].a[i], 5);
+ A (&cma[idx].a[j + 1], 4);
+ A (&cma[idx].a[j + 2], 3);
+
+ A (&cma[idx].a[idx], 5);
+ A (&cma[idx].a[idx + 1], 4);
+ A (&cma[idx].a[idx + 2], 3);
+ }
+
+ {
+ A (cma[1].a, 6);
+ A (&cma[1].a[0], 6);
+ A (&cma[1].a[1], 5);
+ A (&cma[1].a[2], 4);
+
+ int i = 1;
+ A (cma[i].a, 6);
+ A (&cma[i].a[0], 6);
+ A (&cma[i].a[1], 5);
+ A (&cma[i].a[2], 4);
+
+ int j = i - 1;
+ A (&cma[i].a[j], 6);
+ A (&cma[i].a[j + 1], 5);
+ A (&cma[i].a[j + 2], 4);
+
+ A (&cma[idx + 1].a[j], 6);
+ A (&cma[idx + 1].a[j + 1], 5);
+ A (&cma[idx + 1].a[j + 2], 4);
+
+ A (&cma[idx + 1].a[idx], 6);
+ A (&cma[idx + 1].a[idx + 1], 5);
+ A (&cma[idx + 1].a[idx + 2], 4);
+ }
+
+ {
+ A (cma[4].a, 9);
+ A (&cma[4].a[0], 9);
+ A (&cma[4].a[1], 8);
+ A (&cma[4].b[0], 5);
+
+ int i = 4;
+ A (cma[i].a, 9);
+ A (&cma[i].a[0], 9);
+ A (&cma[i].a[1], 8);
+ A (&cma[i].b[0], 5);
+
+ int j = i - 1;
+ A (&cma[i].a[j], 6);
+ A (&cma[i].a[j + 1], 5);
+ A (&cma[i].b[j - 2], 4);
+
+ A (&cma[idx + 4].a[j], 6);
+ A (&cma[idx + 4].a[j + 1], 5);
+ A (&cma[idx + 4].b[j - 2], 4);
+
+ A (&cma[idx + 4].a[idx], 9);
+ A (&cma[idx + 4].a[idx + 1], 8);
+ A (&cma[idx + 4].b[idx + 1], 4);
+ }
+}
+
+
+static void test_const_local_member_arrays (void)
+{
+ const struct MemArrays ma[] = {
+ { { '1', '2', '3', '4' }, { '5' } },
+ { { '1', '2', '3', '4' }, { '5', '6' } },
+ { { '1', '2', '3', '4' }, { '5', '6' } },
+ { { '1', '2', '3', '4' }, { '5', '6', '7' } },
+ { { '1', '2', '3', '4' }, { '5', '6', '7', '8' } },
+ { { '9' }, { '\0' } }
+ };
+
+ {
+ A (ma[0].a, 5);
+ A (&ma[0].a[0], 5);
+ A (&ma[0].a[1], 4);
+ A (&ma[0].a[2], 3);
+
+ int i = 0;
+ A (ma[i].a, 5);
+ A (&ma[i].a[0], 5);
+ A (&ma[i].a[1], 4);
+ A (&ma[i].a[2], 3);
+
+ int j = i;
+ A (&ma[i].a[j], 5);
+ A (&ma[i].a[j + 1], 4);
+ A (&ma[i].a[j + 2], 3);
+
+ A (&ma[idx].a[i], 5);
+ A (&ma[idx].a[j + 1], 4);
+ A (&ma[idx].a[j + 2], 3);
+
+ A (&ma[idx].a[idx], 5);
+ A (&ma[idx].a[idx + 1], 4);
+ A (&ma[idx].a[idx + 2], 3);
+ }
+
+ {
+ A (ma[1].a, 6);
+ A (&ma[1].a[0], 6);
+ A (&ma[1].a[1], 5);
+ A (&ma[1].a[2], 4);
+
+ int i = 1;
+ A (ma[i].a, 6);
+ A (&ma[i].a[0], 6);
+ A (&ma[i].a[1], 5);
+ A (&ma[i].a[2], 4);
+
+ int j = i - 1;
+ A (&ma[i].a[j], 6);
+ A (&ma[i].a[j + 1], 5);
+ A (&ma[i].a[j + 2], 4);
+
+ A (&ma[idx + 1].a[j], 6);
+ A (&ma[idx + 1].a[j + 1], 5);
+ A (&ma[idx + 1].a[j + 2], 4);
+
+ A (&ma[idx + 1].a[idx], 6);
+ A (&ma[idx + 1].a[idx + 1], 5);
+ A (&ma[idx + 1].a[idx + 2], 4);
+ }
+
+ {
+ A (ma[4].a, 9);
+ A (&ma[4].a[0], 9);
+ A (&ma[4].a[1], 8);
+ A (&ma[4].b[0], 5);
+
+ int i = 4;
+ A (ma[i].a, 9);
+ A (&ma[i].a[0], 9);
+ A (&ma[i].a[1], 8);
+ A (&ma[i].b[0], 5);
+
+ int j = i - 1;
+ A (&ma[i].a[j], 6);
+ A (&ma[i].a[j + 1], 5);
+ A (&ma[i].b[j - 2], 4);
+
+ A (&ma[idx + 4].a[j], 6);
+ A (&ma[idx + 4].a[j + 1], 5);
+ A (&ma[idx + 4].b[j - 2], 4);
+
+ A (&ma[idx + 4].a[idx], 9);
+ A (&ma[idx + 4].a[idx + 1], 8);
+ A (&ma[idx + 4].b[idx + 1], 4);
+ }
+}
+
+struct MemArrays vma[] = {
+ { { '1', '2', '3', '4' }, { '5' } },
+ { { '1', '2', '3', '4' }, { '5', '6' } },
+ { { '1', '2', '3', '4' }, { '5', '6' } },
+ { { '1', '2', '3', '4' }, { '5', '6', '7' } },
+ { { '1', '2', '3', '4' }, { '5', '6', '7', '8' } },
+ { { '9' }, { '\0' } }
+};
+
+static void test_nonconst_global_member_arrays (void)
+{
+ {
+ A (vma[0].a, 5);
+ A (&vma[0].a[0], 5);
+ A (&vma[0].a[1], 4);
+ A (&vma[0].a[2], 3);
+
+ int i = 0;
+ A (vma[i].a, 5);
+ A (&vma[i].a[0], 5);
+ A (&vma[i].a[1], 4);
+ A (&vma[i].a[2], 3);
+
+ int j = i;
+ A (&vma[i].a[j], 5);
+ A (&vma[i].a[j + 1], 4);
+ A (&vma[i].a[j + 2], 3);
+
+ A (&vma[idx].a[i], 5);
+ A (&vma[idx].a[j + 1], 4);
+ A (&vma[idx].a[j + 2], 3);
+
+ A (&vma[idx].a[idx], 5);
+ A (&vma[idx].a[idx + 1], 4);
+ A (&vma[idx].a[idx + 2], 3);
+ }
+
+ {
+ A (vma[1].a, 6);
+ A (&vma[1].a[0], 6);
+ A (&vma[1].a[1], 5);
+ A (&vma[1].a[2], 4);
+
+ int i = 1;
+ A (vma[i].a, 6);
+ A (&vma[i].a[0], 6);
+ A (&vma[i].a[1], 5);
+ A (&vma[i].a[2], 4);
+
+ int j = i - 1;
+ A (&vma[i].a[j], 6);
+ A (&vma[i].a[j + 1], 5);
+ A (&vma[i].a[j + 2], 4);
+
+ A (&vma[idx + 1].a[j], 6);
+ A (&vma[idx + 1].a[j + 1], 5);
+ A (&vma[idx + 1].a[j + 2], 4);
+
+ A (&vma[idx + 1].a[idx], 6);
+ A (&vma[idx + 1].a[idx + 1], 5);
+ A (&vma[idx + 1].a[idx + 2], 4);
+ }
+
+ {
+ A (vma[4].a, 9);
+ A (&vma[4].a[0], 9);
+ A (&vma[4].a[1], 8);
+ A (&vma[4].b[0], 5);
+
+ int i = 4;
+ A (vma[i].a, 9);
+ A (&vma[i].a[0], 9);
+ A (&vma[i].a[1], 8);
+ A (&vma[i].b[0], 5);
+
+ int j = i - 1;
+ A (&vma[i].a[j], 6);
+ A (&vma[i].a[j + 1], 5);
+ A (&vma[i].b[j - 2], 4);
+
+ A (&vma[idx + 4].a[j], 6);
+ A (&vma[idx + 4].a[j + 1], 5);
+ A (&vma[idx + 4].b[j - 2], 4);
+
+ A (&vma[idx + 4].a[idx], 9);
+ A (&vma[idx + 4].a[idx + 1], 8);
+ A (&vma[idx + 4].b[idx + 1], 4);
+ }
+}
+
+
+static void test_nonconst_local_member_arrays (void)
+{
+ struct MemArrays ma[] = {
+ { { '1', '2', '3', '4' }, { '5' } },
+ { { '1', '2', '3', '4' }, { '5', '6' } },
+ { { '1', '2', '3', '4' }, { '5', '6' } },
+ { { '1', '2', '3', '4' }, { '5', '6', '7' } },
+ { { '1', '2', '3', '4' }, { '5', '6', '7', '8' } },
+ { { '9' }, { '\0' } }
+ };
+
+ {
+ A (ma[0].a, 5);
+ A (&ma[0].a[0], 5);
+ A (&ma[0].a[1], 4);
+ A (&ma[0].a[2], 3);
+
+ int i = 0;
+ A (ma[i].a, 5);
+ A (&ma[i].a[0], 5);
+ A (&ma[i].a[1], 4);
+ A (&ma[i].a[2], 3);
+
+ int j = i;
+ A (&ma[i].a[j], 5);
+ A (&ma[i].a[j + 1], 4);
+ A (&ma[i].a[j + 2], 3);
+
+ A (&ma[idx].a[i], 5);
+ A (&ma[idx].a[j + 1], 4);
+ A (&ma[idx].a[j + 2], 3);
+
+ A (&ma[idx].a[idx], 5);
+ A (&ma[idx].a[idx + 1], 4);
+ A (&ma[idx].a[idx + 2], 3);
+ }
+
+ {
+ A (ma[1].a, 6);
+ A (&ma[1].a[0], 6);
+ A (&ma[1].a[1], 5);
+ A (&ma[1].a[2], 4);
+
+ int i = 1;
+ A (ma[i].a, 6);
+ A (&ma[i].a[0], 6);
+ A (&ma[i].a[1], 5);
+ A (&ma[i].a[2], 4);
+
+ int j = i - 1;
+ A (&ma[i].a[j], 6);
+ A (&ma[i].a[j + 1], 5);
+ A (&ma[i].a[j + 2], 4);
+
+ A (&ma[idx + 1].a[j], 6);
+ A (&ma[idx + 1].a[j + 1], 5);
+ A (&ma[idx + 1].a[j + 2], 4);
+
+ A (&ma[idx + 1].a[idx], 6);
+ A (&ma[idx + 1].a[idx + 1], 5);
+ A (&ma[idx + 1].a[idx + 2], 4);
+ }
+
+ {
+ A (ma[4].a, 9);
+ A (&ma[4].a[0], 9);
+ A (&ma[4].a[1], 8);
+ A (&ma[4].b[0], 5);
+
+ int i = 4;
+ A (ma[i].a, 9);
+ A (&ma[i].a[0], 9);
+ A (&ma[i].a[1], 8);
+ A (&ma[i].b[0], 5);
+
+ int j = i - 1;
+ A (&ma[i].a[j], 6);
+ A (&ma[i].a[j + 1], 5);
+ A (&ma[i].b[j - 2], 4);
+
+ A (&ma[idx + 4].a[j], 6);
+ A (&ma[idx + 4].a[j + 1], 5);
+ A (&ma[idx + 4].b[j - 2], 4);
+
+ A (&ma[idx + 4].a[idx], 9);
+ A (&ma[idx + 4].a[idx + 1], 8);
+ A (&ma[idx + 4].b[idx + 1], 4);
+ }
+}
+
+
+union UnionMemberArrays
+{
+ struct { char a[4], b[4]; } a;
+ struct { char a[8]; } c;
+};
+
+const union UnionMemberArrays cu = {
+ { { '1', '2', '3', '4' }, { '5', } }
+};
+
+static void test_const_union_member_arrays (void)
+{
+ A (cu.a.a, 5);
+ A (cu.a.b, 1);
+ A (cu.c.a, 5);
+
+ const union UnionMemberArrays clu = {
+ { { '1', '2', '3', '4' }, { '5', '6' } }
+ };
+
+ A (clu.a.a, 6);
+ A (clu.a.b, 2);
+ A (clu.c.a, 6);
+}
+
+
+union UnionMemberArrays vu = {
+ { { '1', '2', '3', '4' }, { '5', '6' } }
+};
+
+static void test_nonconst_union_member_arrays (void)
+{
+ A (vu.a.a, 6);
+ A (vu.a.b, 2);
+ A (vu.c.a, 6);
+
+ union UnionMemberArrays lvu = {
+ { { '1', '2', '3', '4' }, { '5', '6', '7' } }
+ };
+
+ A (lvu.a.a, 7);
+ A (lvu.a.b, 3);
+ A (lvu.c.a, 7);
+}
+
+
+int main (void)
+{
+ test_const_global_arrays ();
+ test_const_local_arrays ();
+
+ test_nonconst_global_arrays ();
+ test_nonconst_local_arrays ();
+
+ test_const_global_member_arrays ();
+ test_const_local_member_arrays ();
+
+ test_nonconst_global_member_arrays ();
+ test_nonconst_local_member_arrays ();
+
+ test_const_union_member_arrays ();
+ test_nonconst_union_member_arrays ();
+
+ if (nfails)
+ __builtin_abort ();
+}
new file mode 100644
@@ -0,0 +1,113 @@
+/* Test to verify that strlen() calls with conditional expressions
+ and unterminated arrays or pointers to such things as arguments
+ are evaluated without making assumptions about array sizes. */
+
+extern __SIZE_TYPE__ strlen (const char *);
+
+unsigned nfails;
+
+#define A(expr, N) \
+ do { \
+ const char *_s = (expr); \
+ unsigned _n = strlen (_s); \
+ ((_n == N) \
+ ? 0 \
+ : (__builtin_printf ("line %i: strlen ((%s) = (\"%s\"))" \
+ " == %u failed\n", \
+ __LINE__, #expr, _s, N), \
+ ++nfails)); \
+ } while (0)
+
+
+volatile int i0 = 0;
+
+const char ca[2][3] = { "12" };
+const char cb[2][3] = { { '1', '2', '3', }, { '4' } };
+
+char va[2][3] = { "123" };
+char vb[2][3] = { { '1', '2', '3', }, { '4', '5' } };
+
+const char *s = "123456";
+
+
+static void test_binary_cond_expr_global (void)
+{
+ A (i0 ? "1" : ca[0], 2);
+ A (i0 ? ca[0] : "123", 3);
+
+ /* The call to strlen (cb[0]) is strictly undefined because the array
+ isn't nul-terminated. This test verifies that the strlen range
+ optimization doesn't assume that the argument is necessarily nul
+ terminated.
+ Ditto for strlen (vb[0]). */
+ A (i0 ? "1" : cb[0], 4); /* GCC 8.2 failure */
+ A (i0 ? cb[0] : "12", 2);
+
+ A (i0 ? "1" : va[0], 3); /* GCC 8.2 failure */
+ A (i0 ? va[0] : "1234", 4);
+
+ A (i0 ? "1" : vb[0], 5); /* GCC 8.2 failure */
+ A (i0 ? vb[0] : "12", 2);
+}
+
+
+static void test_binary_cond_expr_local (void)
+{
+ const char lca[2][3] = { "12" };
+ const char lcb[2][3] = { { '1', '2', '3', }, { '4' } };
+
+ char lva[2][3] = { "123" };
+ char lvb[2][3] = { { '1', '2', '3', }, { '4', '5' } };
+
+ /* Also undefined as above. */
+ A (i0 ? "1" : lca[0], 2);
+ A (i0 ? lca[0] : "123", 3);
+
+ A (i0 ? "1" : lcb[0], 4); /* GCC 8.2 failure */
+ A (i0 ? lcb[0] : "12", 2);
+
+ A (i0 ? "1" : lva[0], 3); /* GCC 8.2 failure */
+ A (i0 ? lva[0] : "1234", 4);
+
+ A (i0 ? "1" : lvb[0], 5); /* GCC 8.2 failure */
+ A (i0 ? lvb[0] : "12", 2);
+}
+
+
+static void test_ternary_cond_expr (void)
+{
+ /* Also undefined. */
+ A (i0 == 0 ? s : i0 == 1 ? vb[0] : "123", 6);
+ A (i0 == 0 ? vb[0] : i0 == 1 ? s : "123", 5);
+ A (i0 == 0 ? "123" : i0 == 1 ? s : vb[0], 3);
+}
+
+
+const char (*pca)[3] = &ca[0];
+const char (*pcb)[3] = &cb[0];
+
+char (*pva)[3] = &va[0];
+char (*pvb)[3] = &vb[0];
+
+static void test_binary_cond_expr_arrayptr (void)
+{
+ /* Also undefined. */
+ A (i0 ? *pca : *pcb, 4); /* GCC 8.2 failure */
+ A (i0 ? *pcb : *pca, 2);
+
+ A (i0 ? *pva : *pvb, 5); /* GCC 8.2 failure */
+ A (i0 ? *pvb : *pva, 3);
+}
+
+
+int main (void)
+{
+ test_binary_cond_expr_global ();
+ test_binary_cond_expr_local ();
+
+ test_ternary_cond_expr ();
+ test_binary_cond_expr_arrayptr ();
+
+ if (nfails)
+ __builtin_abort ();
+}
new file mode 100644
@@ -0,0 +1,37 @@
+/* Test to verify that a strlen() call with a pointer to a dynamic type
+ doesn't make assumptions based on the static type of the original
+ pointer. See g++.dg/init/strlen.C for the corresponding C++ test. */
+
+struct A { int i; char a[1]; void (*p)(); };
+struct B { char a[sizeof (struct A) - __builtin_offsetof (struct A, a)]; };
+
+__attribute__ ((noipa)) void
+init (char *d, const char *s)
+{
+ __builtin_strcpy (d, s);
+}
+
+struct B b;
+
+__attribute__ ((noipa)) void
+test_dynamic_type (struct A *p)
+{
+ /* The following call is undefined because it writes past the end
+ of the p->a subobject, but the corresponding GIMPLE considers
+ it valid and there's apparently no way to distinguish invalid
+ cases from ones like it that might be valid. If/when GIMPLE
+ changes to make this possible this test can be removed. */
+ char *q = (char*)__builtin_memcpy (p->a, &b, sizeof b);
+
+ init (q, "foobar");
+
+ if (6 != __builtin_strlen (q))
+ __builtin_abort();
+}
+
+int main (void)
+{
+ struct A *p = (struct A*)__builtin_malloc (sizeof *p);
+ test_dynamic_type (p);
+ return 0;
+}
@@ -9,23 +9,6 @@ extern char a7[7], a6[6], a5[5], a4[4], a3[3], a2[2], a1[1];
extern char a0[0]; /* Intentionally not tested here. */
extern char ax[]; /* Same. */
-struct MemArrays {
- char a7[7], a6[6], a5[5], a4[4], a3[3], a2[2], a1[1];
- char a0[0]; /* Not tested here. */
-};
-
-struct NestedMemArrays {
- struct { char a7[7]; } ma7;
- struct { char a6[6]; } ma6;
- struct { char a5[5]; } ma5;
- struct { char a4[4]; } ma4;
- struct { char a3[3]; } ma3;
- struct { char a2[2]; } ma2;
- struct { char a1[1]; } ma1;
- struct { char a0[0]; } ma0;
- char last;
-};
-
extern void failure_on_line (int);
#define TEST_FAIL(line) \
@@ -51,36 +34,4 @@ void test_array (void)
T (strlen (a1) == 0); */
}
-void test_memarray (struct MemArrays *ma)
-{
- T (strlen (ma->a7) < sizeof ma->a7);
- T (strlen (ma->a6) < sizeof ma->a6);
- T (strlen (ma->a5) < sizeof ma->a5);
- T (strlen (ma->a4) < sizeof ma->a4);
- T (strlen (ma->a3) < sizeof ma->a3);
-
- /* The following two calls are folded too early which defeats
- the strlen() optimization.
- T (strlen (ma->a2) == 1);
- T (strlen (ma->a1) == 0); */
-}
-
-/* Verify that the range of strlen(A) of a last struct member is
- set even when the array is the sole member of a struct as long
- as the struct itself is a member of another struct. The converse
- is tested in stlenopt-37.c. */
-void test_nested_memarray (struct NestedMemArrays *ma)
-{
- T (strlen (ma->ma7.a7) < sizeof ma->ma7.a7);
- T (strlen (ma->ma6.a6) < sizeof ma->ma6.a6);
- T (strlen (ma->ma5.a5) < sizeof ma->ma5.a5);
- T (strlen (ma->ma4.a4) < sizeof ma->ma4.a4);
- T (strlen (ma->ma3.a3) < sizeof ma->ma3.a3);
-
- /* The following two calls are folded too early which defeats
- the strlen() optimization.
- T (strlen (ma->ma2.a2) == 1);
- T (strlen (ma->ma1.a1) == 0); */
-}
-
/* { dg-final { scan-tree-dump-not "failure_on_line" "optimized" } } */
@@ -105,46 +105,53 @@ void elim_global_arrays (int i)
/* Verify that the expression involving the strlen call as well
as whatever depends on it is eliminated from the test output.
All these expressions must be trivially true. */
- ELIM_TRUE (strlen (a7_3[0]) < sizeof a7_3[0]);
- ELIM_TRUE (strlen (a7_3[1]) < sizeof a7_3[1]);
- ELIM_TRUE (strlen (a7_3[6]) < sizeof a7_3[6]);
- ELIM_TRUE (strlen (a7_3[i]) < sizeof a7_3[i]);
-
- ELIM_TRUE (strlen (a5_7[0]) < sizeof a5_7[0]);
- ELIM_TRUE (strlen (a5_7[1]) < sizeof a5_7[1]);
- ELIM_TRUE (strlen (a5_7[4]) < sizeof a5_7[4]);
- ELIM_TRUE (strlen (a5_7[i]) < sizeof a5_7[0]);
-
- ELIM_TRUE (strlen (ax_3[0]) < sizeof ax_3[0]);
- ELIM_TRUE (strlen (ax_3[1]) < sizeof ax_3[1]);
- ELIM_TRUE (strlen (ax_3[9]) < sizeof ax_3[9]);
- ELIM_TRUE (strlen (ax_3[i]) < sizeof ax_3[i]);
+ ELIM_TRUE (strlen (a7_3[0]) < sizeof a7_3);
+ ELIM_TRUE (strlen (a7_3[1]) < sizeof a7_3 - sizeof *a7_3);
+ ELIM_TRUE (strlen (a7_3[6]) < sizeof a7_3 - 5 * sizeof *a7_3);
+ ELIM_TRUE (strlen (a7_3[i]) < sizeof a7_3);
+
+ ELIM_TRUE (strlen (a5_7[0]) < sizeof a5_7);
+ ELIM_TRUE (strlen (a5_7[1]) < sizeof a5_7 - sizeof *a5_7);
+ ELIM_TRUE (strlen (a5_7[4]) < sizeof a5_7 - 3 * sizeof *a5_7);
+ ELIM_TRUE (strlen (a5_7[i]) < sizeof a5_7);
+
+ /* Even when treating a multi-dimensional array as a single string
+ the length must be less DIFF_MAX - (ax_3[i] - ax_3[0]) but GCC
+ doesn't do that computation yet so avoid testing it. */
+ ELIM_TRUE (strlen (ax_3[0]) < DIFF_MAX);
+ ELIM_TRUE (strlen (ax_3[1]) < DIFF_MAX);
+ ELIM_TRUE (strlen (ax_3[9]) < DIFF_MAX);
+ ELIM_TRUE (strlen (ax_3[i]) < DIFF_MAX);
ELIM_TRUE (strlen (a3) < sizeof a3);
ELIM_TRUE (strlen (a7) < sizeof a7);
ELIM_TRUE (strlen (ax) != DIFF_MAX);
- ELIM_TRUE (strlen (ax) != DIFF_MAX - 1);
- ELIM_TRUE (strlen (ax) < DIFF_MAX - 1);
+ /* ELIM_TRUE (strlen (ax) != DIFF_MAX - 1); */
+ /* ELIM_TRUE (strlen (ax) < DIFF_MAX - 1); */
}
void elim_pointer_to_arrays (void)
{
- ELIM_TRUE (strlen (*pa7) < 7);
- ELIM_TRUE (strlen (*pa5) < 5);
- ELIM_TRUE (strlen (*pa3) < 3);
-
- ELIM_TRUE (strlen ((*pa7_3)[0]) < 3);
- ELIM_TRUE (strlen ((*pa7_3)[1]) < 3);
- ELIM_TRUE (strlen ((*pa7_3)[6]) < 3);
-
- ELIM_TRUE (strlen ((*pax_3)[0]) < 3);
- ELIM_TRUE (strlen ((*pax_3)[1]) < 3);
- ELIM_TRUE (strlen ((*pax_3)[9]) < 3);
-
- ELIM_TRUE (strlen ((*pa5_7)[0]) < 7);
- ELIM_TRUE (strlen ((*pa5_7)[1]) < 7);
- ELIM_TRUE (strlen ((*pa5_7)[4]) < 7);
+ /* Unfortunately, GCC cannot be trusted not to misuse a pointer
+ to a smaller array to point to an object of a bigger type so
+ the strlen range optimization must assume each array pointer
+ points effectively to an array of an unknown bound. */
+ ELIM_TRUE (strlen (*pa7) < DIFF_MAX);
+ ELIM_TRUE (strlen (*pa5) < DIFF_MAX);
+ ELIM_TRUE (strlen (*pa3) < DIFF_MAX);
+
+ ELIM_TRUE (strlen ((*pa7_3)[0]) < DIFF_MAX);
+ ELIM_TRUE (strlen ((*pa7_3)[1]) < DIFF_MAX);
+ ELIM_TRUE (strlen ((*pa7_3)[6]) < DIFF_MAX);
+
+ ELIM_TRUE (strlen ((*pax_3)[0]) < DIFF_MAX);
+ ELIM_TRUE (strlen ((*pax_3)[1]) < DIFF_MAX);
+ ELIM_TRUE (strlen ((*pax_3)[9]) < DIFF_MAX);
+
+ ELIM_TRUE (strlen ((*pa5_7)[0]) < DIFF_MAX);
+ ELIM_TRUE (strlen ((*pa5_7)[1]) < DIFF_MAX);
+ ELIM_TRUE (strlen ((*pa5_7)[4]) < DIFF_MAX);
}
void elim_global_arrays_and_strings (int i)
@@ -176,65 +183,33 @@ void elim_global_arrays_and_strings (int i)
void elim_member_arrays_obj (int i)
{
- ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a3) < 3);
- ELIM_TRUE (strlen (ma0_3_5_7[0][0][1].a3) < 3);
- ELIM_TRUE (strlen (ma0_3_5_7[0][0][2].a3) < 3);
- ELIM_TRUE (strlen (ma0_3_5_7[0][0][6].a3) < 3);
+ ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a3) < sizeof ma0_3_5_7);
+ ELIM_TRUE (strlen (ma0_3_5_7[0][0][1].a3) < sizeof ma0_3_5_7);
+ ELIM_TRUE (strlen (ma0_3_5_7[0][0][2].a3) < sizeof ma0_3_5_7);
+ ELIM_TRUE (strlen (ma0_3_5_7[0][0][6].a3) < sizeof ma0_3_5_7);
- ELIM_TRUE (strlen (ma0_3_5_7[1][0][0].a3) < 3);
- ELIM_TRUE (strlen (ma0_3_5_7[2][0][1].a3) < 3);
+ ELIM_TRUE (strlen (ma0_3_5_7[1][0][0].a3) < sizeof ma0_3_5_7);
+ ELIM_TRUE (strlen (ma0_3_5_7[2][0][1].a3) < sizeof ma0_3_5_7);
- ELIM_TRUE (strlen (ma0_3_5_7[1][1][0].a3) < 3);
- ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a3) < 3);
+ ELIM_TRUE (strlen (ma0_3_5_7[1][1][0].a3) < sizeof ma0_3_5_7);
+ ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a3) < sizeof ma0_3_5_7);
- ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a5) < 5);
- ELIM_TRUE (strlen (ma0_3_5_7[0][0][1].a5) < 5);
- ELIM_TRUE (strlen (ma0_3_5_7[0][0][2].a5) < 5);
- ELIM_TRUE (strlen (ma0_3_5_7[0][0][6].a5) < 5);
+ ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a5) < sizeof ma0_3_5_7);
+ ELIM_TRUE (strlen (ma0_3_5_7[0][0][1].a5) < sizeof ma0_3_5_7);
+ ELIM_TRUE (strlen (ma0_3_5_7[0][0][2].a5) < sizeof ma0_3_5_7);
+ ELIM_TRUE (strlen (ma0_3_5_7[0][0][6].a5) < sizeof ma0_3_5_7);
- ELIM_TRUE (strlen (ma0_3_5_7[1][0][0].a5) < 5);
- ELIM_TRUE (strlen (ma0_3_5_7[2][0][1].a5) < 5);
+ ELIM_TRUE (strlen (ma0_3_5_7[1][0][0].a5) < sizeof ma0_3_5_7);
+ ELIM_TRUE (strlen (ma0_3_5_7[2][0][1].a5) < sizeof ma0_3_5_7);
- ELIM_TRUE (strlen (ma0_3_5_7[1][1][0].a5) < 5);
- ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a5) < 5);
+ ELIM_TRUE (strlen (ma0_3_5_7[1][1][0].a5) < sizeof ma0_3_5_7);
+ ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a5) < sizeof ma0_3_5_7);
- ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a7_3[0]) < 3);
- ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a7_3[2]) < 3);
+ ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a7_3[0]) < sizeof ma0_3_5_7);
+ ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a7_3[2]) < sizeof ma0_3_5_7);
- ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a5_7[0]) < 7);
- ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a5_7[4]) < 7);
-}
-
-void elim_member_arrays_ptr (struct MemArrays0 *ma0,
- struct MemArraysX *max,
- struct MemArrays7 *ma7,
- int i)
-{
- ELIM_TRUE (strlen (ma0->a7_3[0]) < 3);
- ELIM_TRUE (strlen (ma0->a7_3[1]) < 3);
- ELIM_TRUE (strlen (ma0->a7_3[6]) < 3);
- ELIM_TRUE (strlen (ma0->a7_3[6]) < 3);
- ELIM_TRUE (strlen (ma0->a7_3[i]) < 3);
- ELIM_TRUE (strlen (ma0->a7_3[i]) < 3);
-
- ELIM_TRUE (strlen (ma0->a5_7[0]) < 7);
- ELIM_TRUE (strlen (ma0[0].a5_7[0]) < 7);
- ELIM_TRUE (strlen (ma0[1].a5_7[0]) < 7);
- ELIM_TRUE (strlen (ma0[1].a5_7[4]) < 7);
- ELIM_TRUE (strlen (ma0[9].a5_7[0]) < 7);
- ELIM_TRUE (strlen (ma0[9].a5_7[4]) < 7);
-
- ELIM_TRUE (strlen (ma0->a3) < sizeof ma0->a3);
- ELIM_TRUE (strlen (ma0->a5) < sizeof ma0->a5);
- ELIM_TRUE (strlen (ma0->a0) < DIFF_MAX - 1);
-
- ELIM_TRUE (strlen (max->a3) < sizeof max->a3);
- ELIM_TRUE (strlen (max->a5) < sizeof max->a5);
- ELIM_TRUE (strlen (max->ax) < DIFF_MAX - 1);
-
- ELIM_TRUE (strlen (ma7->a3) < sizeof max->a3);
- ELIM_TRUE (strlen (ma7->a5) < sizeof max->a5);
- ELIM_TRUE (strlen (ma7->a7) < DIFF_MAX - 1);
+ ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a5_7[0]) < sizeof ma0_3_5_7);
+ ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a5_7[4]) < sizeof ma0_3_5_7);
}
@@ -255,11 +230,27 @@ void keep_global_arrays (int i)
KEEP (strlen (a5_7[4]) < 6);
KEEP (strlen (a5_7[i]) < 6);
+ /* Verify also that tests (and strlen calls) are not eliminated
+ for results greater than what would the size of the innermost
+ array suggest might be possible (in case the element array is
+ not nul-terminated), even though such calls are undefined. */
+ KEEP (strlen (a5_7[0]) > sizeof a5_7 - 2);
+ KEEP (strlen (a5_7[1]) > sizeof a5_7 - sizeof a5_7[1] - 2);
+ KEEP (strlen (a5_7[i]) > sizeof a5_7 - 2);
+
KEEP (strlen (ax_3[0]) < 2);
KEEP (strlen (ax_3[1]) < 2);
KEEP (strlen (ax_3[2]) < 2);
KEEP (strlen (ax_3[i]) < 2);
+ /* Here again, verify that the ax_3 matrix is treated essentially
+ as a flat array of unknown bound for the benefit of all the
+ undefined code out there that might rely on it. */
+ KEEP (strlen (ax_3[0]) > 3);
+ KEEP (strlen (ax_3[1]) > 9);
+ KEEP (strlen (ax_3[2]) > 99);
+ KEEP (strlen (ax_3[i]) > 999);
+
KEEP (strlen (a3) < 2);
KEEP (strlen (a7) < 6);
@@ -274,24 +265,48 @@ void keep_global_arrays (int i)
KEEP (strlen (ax) < 1);
}
-void keep_pointer_to_arrays (void)
+void keep_pointer_to_arrays (int i)
{
KEEP (strlen (*pa7) < 6);
KEEP (strlen (*pa5) < 4);
KEEP (strlen (*pa3) < 2);
+ /* Since GCC cannot be trusted not to misuse a pointer to a smaller
+ array to point to an object of a larger type verify that the bound
+ in a pointer to an array of a known bound isn't relied on for
+ the strlen range optimization. If GCC is fixed to avoid these
+ misuses these tests can be removed. */
+ KEEP (strlen (*pa7) > sizeof *pa7);
+ KEEP (strlen (*pa5) > sizeof *pa5);
+ KEEP (strlen (*pa3) > sizeof *pa3);
+
KEEP (strlen ((*pa7_3)[0]) < 2);
KEEP (strlen ((*pa7_3)[1]) < 2);
KEEP (strlen ((*pa7_3)[6]) < 2);
+ KEEP (strlen ((*pa7_3)[i]) < 2);
+
+ /* Same as above. */
+ KEEP (strlen ((*pa7_3)[0]) > sizeof *pa7_3);
+ KEEP (strlen ((*pa7_3)[i]) > sizeof *pa7_3);
KEEP (strlen ((*pax_3)[0]) < 2);
KEEP (strlen ((*pax_3)[1]) < 2);
KEEP (strlen ((*pax_3)[9]) < 2);
+ KEEP (strlen ((*pax_3)[i]) < 2);
+
+ /* Same as above. */
+ KEEP (strlen ((*pax_3)[0]) > 3);
+ KEEP (strlen ((*pax_3)[i]) > 333);
KEEP (strlen ((*pa5_7)[0]) < 6);
KEEP (strlen ((*pa5_7)[1]) < 6);
KEEP (strlen ((*pa5_7)[4]) < 6);
-}
+ KEEP (strlen ((*pa5_7)[i]) < 6);
+
+ /* Same as above. */
+ KEEP (strlen ((*pa5_7)[0]) > sizeof *pa5_7);
+ KEEP (strlen ((*pa5_7)[i]) > sizeof *pa5_7);
+ }
void keep_global_arrays_and_strings (int i)
{
@@ -306,6 +321,12 @@ void keep_global_arrays_and_strings (int i)
KEEP (strlen (i < 0 ? a7 : "123") < 5);
KEEP (strlen (i < 0 ? a7 : "123456") < 6);
KEEP (strlen (i < 0 ? a7 : "1234567") < 6);
+
+ /* Verify that a matrix is treated as a flat array even in a conditional
+ expression (i.e., don't assume that a7_3[0] is nul-terminated, even
+ though calling strlen() on such an array is undefined). */
+ KEEP (strlen (i < 0 ? a7_3[0] : "") > 7);
+ KEEP (strlen (i < 0 ? a7_3[i] : "") > 7);
}
void keep_member_arrays_obj (int i)
@@ -337,6 +358,12 @@ void keep_member_arrays_obj (int i)
KEEP (strlen (ma0_3_5_7[0][0][0].a5_7[0]) < 6);
KEEP (strlen (ma0_3_5_7[2][4][6].a5_7[4]) < 6);
+
+ /* Again, verify that the .a3 array isn't assumed to necessarily
+ be nul-terminated. */
+ KEEP (strlen (ma0_3_5_7[0][0][0].a3) > 2);
+ KEEP (strlen (ma0_3_5_7[0][0][6].a3) > 2);
+ KEEP (strlen (ma0_3_5_7[0][0][i].a3) > 2);
}
void keep_member_arrays_ptr (struct MemArrays0 *ma0,
@@ -353,6 +380,11 @@ void keep_member_arrays_ptr (struct MemArrays0 *ma0,
KEEP (strlen (ma0->a7_3[i]) < 2);
KEEP (strlen (ma0->a7_3[i]) < 2);
+ /* Again, verify that the member array isn't assumed to necessarily
+ be nul-terminated. */
+ KEEP (strlen (ma0->a7_3[0]) > sizeof ma0->a7_3);
+ KEEP (strlen (ma0->a7_3[i]) > sizeof ma0->a7_3);
+
KEEP (strlen (ma0->a5_7[0]) < 5);
KEEP (strlen (ma0[0].a5_7[0]) < 5);
KEEP (strlen (ma0[1].a5_7[0]) < 5);
@@ -361,6 +393,9 @@ void keep_member_arrays_ptr (struct MemArrays0 *ma0,
KEEP (strlen (ma0[i].a5_7[4]) < 5);
KEEP (strlen (ma0[i].a5_7[i]) < 5);
+ /* Same as above. */
+ KEEP (strlen (ma0[i].a5_7[i]) > sizeof ma0[i].a5_7);
+
KEEP (strlen (ma0->a0) < DIFF_MAX - 2);
KEEP (strlen (ma0->a0) < 999);
KEEP (strlen (ma0->a0) < 1);
@@ -389,5 +424,5 @@ void keep_pointers (const char *s)
/* { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated_" 0 "optimized" } }
{ dg-final { scan-tree-dump-times "call_in_false_branch_not_eliminated_" 0 "optimized" } }
- { dg-final { scan-tree-dump-times "call_made_in_true_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 92 "optimized" } }
- { dg-final { scan-tree-dump-times "call_made_in_false_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 92 "optimized" } } */
+ { dg-final { scan-tree-dump-times "call_made_in_true_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 119 "optimized" } }
+ { dg-final { scan-tree-dump-times "call_made_in_false_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 119 "optimized" } } */
@@ -2,7 +2,7 @@
Test to verify that strnlen built-in expansion works correctly
in the absence of tree strlen optimization.
{ dg-do compile }
- { dg-options "-O2 -Wall -fdump-tree-optimized" } */
+ { dg-options "-O2 -Wall -Wno-stringop-overflow -fdump-tree-optimized" } */
#include "strlenopt.h"
@@ -85,19 +85,19 @@ void elim_strnlen_arr_cst (void)
ELIM (strnlen (a3_7[0], 1) < 2);
ELIM (strnlen (a3_7[0], 2) < 3);
ELIM (strnlen (a3_7[0], 3) < 4);
- ELIM (strnlen (a3_7[0], 9) < 8);
- ELIM (strnlen (a3_7[0], PTRDIFF_MAX) < 8);
- ELIM (strnlen (a3_7[0], SIZE_MAX) < 8);
- ELIM (strnlen (a3_7[0], -1) < 8);
+ ELIM (strnlen (a3_7[0], 9) <= 9);
+ ELIM (strnlen (a3_7[0], PTRDIFF_MAX) <= sizeof a3_7);
+ ELIM (strnlen (a3_7[0], SIZE_MAX) <= sizeof a3_7);
+ ELIM (strnlen (a3_7[0], -1) <= sizeof a3_7);
ELIM (strnlen (a3_7[2], 0) == 0);
ELIM (strnlen (a3_7[2], 1) < 2);
ELIM (strnlen (a3_7[2], 2) < 3);
ELIM (strnlen (a3_7[2], 3) < 4);
- ELIM (strnlen (a3_7[2], 9) < 8);
- ELIM (strnlen (a3_7[2], PTRDIFF_MAX) < 8);
- ELIM (strnlen (a3_7[2], SIZE_MAX) < 8);
- ELIM (strnlen (a3_7[2], -1) < 8);
+ ELIM (strnlen (a3_7[2], 9) <= 9);
+ ELIM (strnlen (a3_7[2], PTRDIFF_MAX) < sizeof a3_7);
+ ELIM (strnlen (a3_7[2], SIZE_MAX) < sizeof a3_7);
+ ELIM (strnlen (a3_7[2], -1) < sizeof a3_7);
ELIM (strnlen ((char*)a3_7, 0) == 0);
ELIM (strnlen ((char*)a3_7, 1) < 2);
@@ -105,123 +105,19 @@ void elim_strnlen_arr_cst (void)
ELIM (strnlen ((char*)a3_7, 3) < 4);
ELIM (strnlen ((char*)a3_7, 9) < 10);
ELIM (strnlen ((char*)a3_7, 19) < 20);
- ELIM (strnlen ((char*)a3_7, 21) < 22);
- ELIM (strnlen ((char*)a3_7, 23) < 22);
- ELIM (strnlen ((char*)a3_7, PTRDIFF_MAX) < 22);
- ELIM (strnlen ((char*)a3_7, SIZE_MAX) < 22);
- ELIM (strnlen ((char*)a3_7, -1) < 22);
+ ELIM (strnlen ((char*)a3_7, 21) <= sizeof a3_7);
+ ELIM (strnlen ((char*)a3_7, 23) <= sizeof a3_7);
+ ELIM (strnlen ((char*)a3_7, PTRDIFF_MAX) <= sizeof a3_7);
+ ELIM (strnlen ((char*)a3_7, SIZE_MAX) <= sizeof a3_7);
+ ELIM (strnlen ((char*)a3_7, -1) <= sizeof a3_7);
ELIM (strnlen (ax, 0) == 0);
ELIM (strnlen (ax, 1) < 2);
ELIM (strnlen (ax, 2) < 3);
ELIM (strnlen (ax, 9) < 10);
- ELIM (strnlen (a3, PTRDIFF_MAX) <= PTRDIFF_MAX);
- ELIM (strnlen (a3, SIZE_MAX) < PTRDIFF_MAX);
- ELIM (strnlen (a3, -1) < PTRDIFF_MAX);
-}
-
-struct MemArrays
-{
- char c;
- char a0[0];
- char a1[1];
- char a3[3];
- char a5[5];
- char a3_7[3][7];
- char ax[1];
-};
-
-void elim_strnlen_memarr_cst (struct MemArrays *p, int i)
-{
- ELIM (strnlen (&p->c, 0) == 0);
- ELIM (strnlen (&p->c, 1) < 2);
- ELIM (strnlen (&p->c, 9) == 0);
- ELIM (strnlen (&p->c, PTRDIFF_MAX) == 0);
- ELIM (strnlen (&p->c, SIZE_MAX) == 0);
- ELIM (strnlen (&p->c, -1) == 0);
-
- /* Other accesses to internal zero-length arrays are undefined. */
- ELIM (strnlen (p->a0, 0) == 0);
-
- ELIM (strnlen (p->a1, 0) == 0);
- ELIM (strnlen (p->a1, 1) < 2);
- ELIM (strnlen (p->a1, 9) == 0);
- ELIM (strnlen (p->a1, PTRDIFF_MAX) == 0);
- ELIM (strnlen (p->a1, SIZE_MAX) == 0);
- ELIM (strnlen (p->a1, -1) == 0);
-
- ELIM (strnlen (p->a3, 0) == 0);
- ELIM (strnlen (p->a3, 1) < 2);
- ELIM (strnlen (p->a3, 2) < 3);
- ELIM (strnlen (p->a3, 3) < 4);
- ELIM (strnlen (p->a3, 9) < 4);
- ELIM (strnlen (p->a3, PTRDIFF_MAX) < 4);
- ELIM (strnlen (p->a3, SIZE_MAX) < 4);
- ELIM (strnlen (p->a3, -1) < 4);
-
- ELIM (strnlen (p[i].a3, 0) == 0);
- ELIM (strnlen (p[i].a3, 1) < 2);
- ELIM (strnlen (p[i].a3, 2) < 3);
- ELIM (strnlen (p[i].a3, 3) < 4);
- ELIM (strnlen (p[i].a3, 9) < 4);
- ELIM (strnlen (p[i].a3, PTRDIFF_MAX) < 4);
- ELIM (strnlen (p[i].a3, SIZE_MAX) < 4);
- ELIM (strnlen (p[i].a3, -1) < 4);
-
- ELIM (strnlen (p->a3_7[0], 0) == 0);
- ELIM (strnlen (p->a3_7[0], 1) < 2);
- ELIM (strnlen (p->a3_7[0], 2) < 3);
- ELIM (strnlen (p->a3_7[0], 3) < 4);
- ELIM (strnlen (p->a3_7[0], 9) < 8);
- ELIM (strnlen (p->a3_7[0], PTRDIFF_MAX) < 8);
- ELIM (strnlen (p->a3_7[0], SIZE_MAX) < 8);
- ELIM (strnlen (p->a3_7[0], -1) < 8);
-
- ELIM (strnlen (p->a3_7[2], 0) == 0);
- ELIM (strnlen (p->a3_7[2], 1) < 2);
- ELIM (strnlen (p->a3_7[2], 2) < 3);
- ELIM (strnlen (p->a3_7[2], 3) < 4);
- ELIM (strnlen (p->a3_7[2], 9) < 8);
- ELIM (strnlen (p->a3_7[2], PTRDIFF_MAX) < 8);
- ELIM (strnlen (p->a3_7[2], SIZE_MAX) < 8);
- ELIM (strnlen (p->a3_7[2], -1) < 8);
-
- ELIM (strnlen (p->a3_7[i], 0) == 0);
- ELIM (strnlen (p->a3_7[i], 1) < 2);
- ELIM (strnlen (p->a3_7[i], 2) < 3);
- ELIM (strnlen (p->a3_7[i], 3) < 4);
-
-#if 0
- /* This is tranformed into strnlen ((char*)p + offsetof (a3_7[i]), N)
- which makes it impssible to determine the size of the array. */
- ELIM (strnlen (p->a3_7[i], 9) < 8);
- ELIM (strnlen (p->a3_7[i], PTRDIFF_MAX) < 8);
- ELIM (strnlen (p->a3_7[i], SIZE_MAX) < 8);
- ELIM (strnlen (p->a3_7[i], -1) < 8);
-#else
- ELIM (strnlen (p->a3_7[i], 9) < 10);
- ELIM (strnlen (p->a3_7[i], 19) < 20);
-#endif
-
- ELIM (strnlen ((char*)p->a3_7, 0) == 0);
- ELIM (strnlen ((char*)p->a3_7, 1) < 2);
- ELIM (strnlen ((char*)p->a3_7, 2) < 3);
- ELIM (strnlen ((char*)p->a3_7, 3) < 4);
- ELIM (strnlen ((char*)p->a3_7, 9) < 10);
- ELIM (strnlen ((char*)p->a3_7, 19) < 20);
- ELIM (strnlen ((char*)p->a3_7, 21) < 22);
- ELIM (strnlen ((char*)p->a3_7, 23) < 22);
- ELIM (strnlen ((char*)p->a3_7, PTRDIFF_MAX) < 22);
- ELIM (strnlen ((char*)p->a3_7, SIZE_MAX) < 22);
- ELIM (strnlen ((char*)p->a3_7, -1) < 22);
-
- ELIM (strnlen (p->ax, 0) == 0);
- ELIM (strnlen (p->ax, 1) < 2);
- ELIM (strnlen (p->ax, 2) < 3);
- ELIM (strnlen (p->ax, 9) < 10);
- ELIM (strnlen (p->a3, PTRDIFF_MAX) <= PTRDIFF_MAX);
- ELIM (strnlen (p->a3, SIZE_MAX) < PTRDIFF_MAX);
- ELIM (strnlen (p->a3, -1) < PTRDIFF_MAX);
+ ELIM (strnlen (ax, PTRDIFF_MAX) < PTRDIFF_MAX);
+ ELIM (strnlen (ax, SIZE_MAX) < PTRDIFF_MAX);
+ ELIM (strnlen (ax, -1) < PTRDIFF_MAX);
}
@@ -11,7 +11,7 @@ void f (void)
{
extern char a[2][1];
int n = strlen (a[1]);
- if (n)
+ if (n >= sizeof a)
abort();
}
@@ -19,7 +19,7 @@ void g (void)
{
extern char b[3][2][1];
int n = strlen (b[2][1]);
- if (n)
+ if (n >= sizeof b)
abort();
}
@@ -27,7 +27,7 @@ void h (void)
{
extern char c[4][3][2][1];
int n = strlen (c[3][2][1]);
- if (n)
+ if (n >= sizeof c)
abort();
}
@@ -1,6 +1,6 @@
/* PR tree-optimization/77357 - strlen of constant strings not folded
{ dg-do compile }
- { dg-options "-O2 -Wall -fdump-tree-gimple -fdump-tree-optimized" } */
+ { dg-options "-O0 -Wall -fdump-tree-gimple" } */
#include "strlenopt.h"
@@ -22,20 +22,6 @@
#define ELIM(expr) \
if (!(expr)) FAIL (in_true_branch_not_eliminated, __COUNTER__); else (void)0
-/* Macro to emit a call to a function named
- call_made_in_{true,false}_branch_on_line_NNN()
- for each call that's expected to be retained. The dg-final
- scan-tree-dump-time directive at the bottom of the test verifies
- that the expected number of both kinds of calls appears in output
- (a pair for each line with the invocation of the KEEP() macro. */
-#define DO_KEEP(expr, counter) \
- if (expr) \
- FAIL (made_in_true_branch, counter); \
- else \
- FAIL (made_in_false_branch, counter)
-
-#define KEEP(expr) DO_KEEP (expr, __COUNTER__)
-
#define T(s, n) ELIM (strlen (s) == n)
@@ -56,7 +42,7 @@ struct S
const char a9[][9] = { S0, S1, S2, S3, S4, S5, S6, S7, S8 };
-void test_elim_a9 (int i)
+void test_elim_a9 (unsigned i)
{
ELIM (strlen (&a9[0][i]) > 0);
ELIM (strlen (&a9[1][i]) > 1);
@@ -81,7 +67,7 @@ const char a9_9[][9][9] = {
{ S8, S0, S1, S2, S3, S4, S5, S6, S7 }
};
-void test_elim_a9_9 (int i)
+void test_elim_a9_9 (unsigned i)
{
#undef T
#define T(I) \
@@ -98,27 +84,4 @@ void test_elim_a9_9 (int i)
T (0); T (1); T (2); T (3); T (4); T (5); T (6); T (7); T (8);
}
-#line 1000
-
-void test_keep_a9_9 (int i)
-{
-#undef T
-#define T(I) \
- KEEP (strlen (&a9_9[i][I][0]) > (0 + I) % 9); \
- KEEP (strlen (&a9_9[i][I][1]) > (1 + I) % 9); \
- KEEP (strlen (&a9_9[i][I][2]) > (2 + I) % 9); \
- KEEP (strlen (&a9_9[i][I][3]) > (3 + I) % 9); \
- KEEP (strlen (&a9_9[i][I][4]) > (4 + I) % 9); \
- KEEP (strlen (&a9_9[i][I][5]) > (5 + I) % 9); \
- KEEP (strlen (&a9_9[i][I][6]) > (6 + I) % 9); \
- KEEP (strlen (&a9_9[i][I][7]) > (7 + I) % 9); \
- KEEP (strlen (&a9_9[i][I][8]) > (8 + I) % 9)
-
- T (0); T (1); T (2); T (3); T (4); T (5); T (6); T (7); T (8);
-}
-
-/* { dg-final { scan-tree-dump-times "strlen" 72 "gimple" } }
- { dg-final { scan-tree-dump-times "strlen" 63 "optimized" } }
-
- { dg-final { scan-tree-dump-times "call_made_in_true_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 72 "optimized" } }
- { dg-final { scan-tree-dump-times "call_made_in_false_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 81 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "strlen" 0 "gimple" } } */
new file mode 100644
@@ -0,0 +1,73 @@
+/* Verify that strlen() calls with constant conditional expressions are
+ eliminated as expected.
+
+ { dg-do compile }
+ { dg-options "-O1 -fdump-tree-optimized" } */
+
+extern void abort (void);
+extern __SIZE_TYPE__ strlen (const char*);
+
+
+#define CAT(x, y) x ## y
+#define CONCAT(x, y) CAT (x, y)
+#define FAILNAME(name) CONCAT (call_ ## name ##_on_line_, __LINE__)
+
+#define FAIL(name) do { \
+ extern void FAILNAME (name) (void); \
+ FAILNAME (name)(); \
+ } while (0)
+
+/* Macros to emit a call to funcation named
+ call_failed_to_be_eliminated_on_line_NNN()
+ for each call that's expected to be eliminated. The dg-final
+ scan-tree-dump-time directive at the bottom of the test verifies
+ that no such call appears in output. */
+#define ELIM(expr) \
+ if ((expr)) FAIL (test_not_eliminated); else (void)0
+
+extern char a3[3];
+extern char a7[7];
+
+struct MemArrays { char a[7], b[9]; };
+
+struct MemArrays ma;
+
+void test_elim_condexpr (int i)
+{
+ ELIM (6 < strlen (i ? "" : "123456"));
+ ELIM (6 < strlen (i ? "123456" : ""));
+
+ ELIM (4 < strlen (i < 1 ? "a" : i == 1 ? "ab" : "abc"));
+
+ ELIM (3 < strlen (i ? "" : a3));
+ ELIM (3 < strlen (i ? a3 : "1"));
+
+ ELIM (6 < strlen (i ? "12" : a7));
+ ELIM (6 < strlen (i ? a7 : "123"));
+
+ ELIM (6 < strlen (i ? "1234" : a7));
+ ELIM (7 < strlen (i ? a7 : "1234567"));
+
+ ELIM (3 < strlen (i < 1 ? "a" : i == 1 ? "ab" : a3));
+ ELIM (3 < strlen (i < 1 ? "a" : i == 1 ? a3 : "abc"));
+ ELIM (3 < strlen (i < 1 ? a3 : i == 1 ? "a" : "abc"));
+
+ ELIM (6 < strlen (i < 1 ? "a" : i == 1 ? "ab" : a7));
+ ELIM (6 < strlen (i < 1 ? "a" : i == 1 ? a7 : "abc"));
+ ELIM (6 < strlen (i < 1 ? a7 : i == 1 ? "a" : "abc"));
+
+ ELIM (6 < strlen (i < 1 ? "a" : i == 1 ? a7 : a3));
+ ELIM (6 < strlen (i < 1 ? a7 : i == 1 ? "a" : a3));
+
+ {
+ enum { maxlen = sizeof ma - 1 };
+ ELIM (maxlen < strlen (ma.a));
+ }
+
+ {
+ enum { maxlen = sizeof ma - __builtin_offsetof (struct MemArrays, b) - 1 };
+ ELIM (maxlen < strlen (ma.b));
+ }
+}
+
+/* { dg-final { scan-tree-dump-times "test_not_eliminated_" 0 "optimized" } } */
@@ -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. */
@@ -1989,15 +2018,13 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
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)
+ strlen_data_t lendata (1);
+ get_range_strlen (src, &lendata);
+ if (TREE_CODE (lendata.minlen) == INTEGER_CST
+ && TREE_CODE (lendata.maxsize) == INTEGER_CST)
{
- lenrange[0] = wi::to_wide (range[0], prec);
- lenrange[1] = wi::to_wide (range[1], prec);
+ lenrange[0] = wi::to_wide (lendata.minlen, prec);
+ lenrange[1] = wi::to_wide (lendata.maxsize, prec);
}
else
{
@@ -2114,6 +2141,13 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
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",
@@ -23,5 +23,6 @@
extern bool is_strlen_related_p (tree, tree);
extern bool maybe_diag_stxncpy_trunc (gimple_stmt_iterator, tree, tree);
+extern tree set_strlen_range (tree, wide_int, tree = NULL_TREE);
#endif // GCC_TREE_SSA_STRLEN_H