diff options
author | Martin Sebor <msebor@redhat.com> | 2018-07-09 20:33:48 +0000 |
---|---|---|
committer | Martin Sebor <msebor@gcc.gnu.org> | 2018-07-09 14:33:48 -0600 |
commit | 35b4d3a644222b7bd69b3a1e9c00e78f3dbf3eba (patch) | |
tree | a540d226c0eeee38bd5ea06483ae688c1e0c070e /gcc/expr.c | |
parent | aad2444d346d4ae504a938de8708341dd1889aed (diff) | |
download | gcc-35b4d3a644222b7bd69b3a1e9c00e78f3dbf3eba.zip gcc-35b4d3a644222b7bd69b3a1e9c00e78f3dbf3eba.tar.gz gcc-35b4d3a644222b7bd69b3a1e9c00e78f3dbf3eba.tar.bz2 |
PR middle-end/77357 - strlen of constant strings not folded
gcc/ChangeLog:
PR middle-end/77357
PR middle-end/86428
* builtins.c (c_strlen): Avoid out-of-bounds warnings when
accessing implicitly initialized array elements.
* expr.c (string_constant): Handle string initializers of
character arrays within aggregates.
* gimple-fold.c (fold_array_ctor_reference): Add argument.
Store element offset. As a special case, handle zero size.
(fold_nonarray_ctor_reference): Same.
(fold_ctor_reference): Add argument. Store subobject offset.
* gimple-fold.h (fold_ctor_reference): Add argument.
gcc/testsuite/ChangeLog:
PR middle-end/77357
* gcc.dg/strlenopt-49.c: New test.
* gcc.dg/strlenopt-50.c: New test.
* gcc.dg/strlenopt-51.c: New test.
* gcc.dg/strlenopt-52.c: New test.
From-SVN: r262522
Diffstat (limited to 'gcc/expr.c')
-rw-r--r-- | gcc/expr.c | 193 |
1 files changed, 112 insertions, 81 deletions
@@ -54,11 +54,13 @@ along with GCC; see the file COPYING3. If not see #include "reload.h" #include "langhooks.h" #include "common/common-target.h" +#include "tree-dfa.h" #include "tree-ssa-live.h" #include "tree-outof-ssa.h" #include "tree-ssa-address.h" #include "builtins.h" #include "ccmp.h" +#include "gimple-fold.h" #include "rtx-vector-builder.h" @@ -11267,61 +11269,45 @@ is_aligning_offset (const_tree offset, const_tree exp) } /* Return the tree node if an ARG corresponds to a string constant or zero - if it doesn't. If we return nonzero, set *PTR_OFFSET to the offset - in bytes within the string that ARG is accessing. The type of the - offset will be `sizetype'. */ + if it doesn't. If we return nonzero, set *PTR_OFFSET to the (possibly + non-constant) offset in bytes within the string that ARG is accessing. + The type of the offset is sizetype. */ tree string_constant (tree arg, tree *ptr_offset) { - tree array, offset, lower_bound; + tree array; STRIP_NOPS (arg); + /* Non-constant index into the character array in an ARRAY_REF + expression or null. */ + tree varidx = NULL_TREE; + + poly_int64 base_off = 0; + if (TREE_CODE (arg) == ADDR_EXPR) { - if (TREE_CODE (TREE_OPERAND (arg, 0)) == STRING_CST) - { - *ptr_offset = size_zero_node; - return TREE_OPERAND (arg, 0); - } - else if (TREE_CODE (TREE_OPERAND (arg, 0)) == VAR_DECL) - { - array = TREE_OPERAND (arg, 0); - offset = size_zero_node; - } - else if (TREE_CODE (TREE_OPERAND (arg, 0)) == ARRAY_REF) + arg = TREE_OPERAND (arg, 0); + tree ref = arg; + if (TREE_CODE (arg) == ARRAY_REF) { - array = TREE_OPERAND (TREE_OPERAND (arg, 0), 0); - offset = TREE_OPERAND (TREE_OPERAND (arg, 0), 1); - if (TREE_CODE (array) != STRING_CST && !VAR_P (array)) - return 0; - - /* Check if the array has a nonzero lower bound. */ - lower_bound = array_ref_low_bound (TREE_OPERAND (arg, 0)); - if (!integer_zerop (lower_bound)) + tree idx = TREE_OPERAND (arg, 1); + if (TREE_CODE (idx) != INTEGER_CST) { - /* If the offset and base aren't both constants, return 0. */ - if (TREE_CODE (lower_bound) != INTEGER_CST) - return 0; - if (TREE_CODE (offset) != INTEGER_CST) - return 0; - /* Adjust offset by the lower bound. */ - offset = size_diffop (fold_convert (sizetype, offset), - fold_convert (sizetype, lower_bound)); + /* Extract the variable index to prevent + get_addr_base_and_unit_offset() from failing due to + it. Use it later to compute the non-constant offset + into the string and return it to the caller. */ + varidx = idx; + ref = TREE_OPERAND (arg, 0); } } - else if (TREE_CODE (TREE_OPERAND (arg, 0)) == MEM_REF) - { - array = TREE_OPERAND (TREE_OPERAND (arg, 0), 0); - offset = TREE_OPERAND (TREE_OPERAND (arg, 0), 1); - if (TREE_CODE (array) != ADDR_EXPR) - return 0; - array = TREE_OPERAND (array, 0); - if (TREE_CODE (array) != STRING_CST && !VAR_P (array)) - return 0; - } - else - return 0; + array = get_addr_base_and_unit_offset (ref, &base_off); + if (!array + || (TREE_CODE (array) != VAR_DECL + && TREE_CODE (array) != CONST_DECL + && TREE_CODE (array) != STRING_CST)) + return NULL_TREE; } else if (TREE_CODE (arg) == PLUS_EXPR || TREE_CODE (arg) == POINTER_PLUS_EXPR) { @@ -11331,62 +11317,107 @@ string_constant (tree arg, tree *ptr_offset) STRIP_NOPS (arg0); STRIP_NOPS (arg1); - if (TREE_CODE (arg0) == ADDR_EXPR - && (TREE_CODE (TREE_OPERAND (arg0, 0)) == STRING_CST - || TREE_CODE (TREE_OPERAND (arg0, 0)) == VAR_DECL)) + if (TREE_CODE (arg0) == ADDR_EXPR) + ; /* Do nothing. */ + else if (TREE_CODE (arg1) == ADDR_EXPR) + std::swap (arg0, arg1); + else + return NULL_TREE; + + tree offset; + if (tree str = string_constant (arg0, &offset)) { - array = TREE_OPERAND (arg0, 0); - offset = arg1; + tree type = TREE_TYPE (arg1); + *ptr_offset = fold_build2 (PLUS_EXPR, type, offset, arg1); + return str; } - else if (TREE_CODE (arg1) == ADDR_EXPR - && (TREE_CODE (TREE_OPERAND (arg1, 0)) == STRING_CST - || TREE_CODE (TREE_OPERAND (arg1, 0)) == VAR_DECL)) + return NULL_TREE; + } + else if (DECL_P (arg)) + array = arg; + else + return NULL_TREE; + + tree offset = wide_int_to_tree (sizetype, base_off); + if (varidx) + { + if (tree eltsize = TYPE_SIZE_UNIT (TREE_TYPE (array))) { - array = TREE_OPERAND (arg1, 0); - offset = arg0; + /* Add the scaled variable index to the constant offset. */ + tree eltoff = fold_build2 (MULT_EXPR, TREE_TYPE (offset), + fold_convert (sizetype, varidx), + eltsize); + offset = fold_build2 (PLUS_EXPR, TREE_TYPE (offset), offset, eltoff); } else - return 0; + return NULL_TREE; } - else - return 0; if (TREE_CODE (array) == STRING_CST) { *ptr_offset = fold_convert (sizetype, offset); return array; } - else if (VAR_P (array) || TREE_CODE (array) == CONST_DECL) - { - int length; - tree init = ctor_for_folding (array); - /* Variables initialized to string literals can be handled too. */ - if (init == error_mark_node - || !init - || TREE_CODE (init) != STRING_CST) - return 0; + if (!VAR_P (array) && TREE_CODE (array) != CONST_DECL) + return NULL_TREE; - /* Avoid const char foo[4] = "abcde"; */ - if (DECL_SIZE_UNIT (array) == NULL_TREE - || TREE_CODE (DECL_SIZE_UNIT (array)) != INTEGER_CST - || (length = TREE_STRING_LENGTH (init)) <= 0 - || compare_tree_int (DECL_SIZE_UNIT (array), length) < 0) - return 0; + tree init = ctor_for_folding (array); - /* If variable is bigger than the string literal, OFFSET must be constant - and inside of the bounds of the string literal. */ - offset = fold_convert (sizetype, offset); - if (compare_tree_int (DECL_SIZE_UNIT (array), length) > 0 - && (! tree_fits_uhwi_p (offset) - || compare_tree_int (offset, length) >= 0)) - return 0; + /* Handle variables initialized with string literals. */ + if (!init || init == error_mark_node) + return NULL_TREE; + if (TREE_CODE (init) == CONSTRUCTOR) + { + if (TREE_CODE (arg) != ARRAY_REF + && TREE_CODE (arg) == COMPONENT_REF + && TREE_CODE (arg) == MEM_REF) + return NULL_TREE; + + /* Convert the 64-bit constant offset to a wider type to avoid + overflow. */ + offset_int wioff; + if (!base_off.is_constant (&wioff)) + return NULL_TREE; - *ptr_offset = offset; - return init; + wioff *= BITS_PER_UNIT; + if (!wi::fits_uhwi_p (wioff)) + return NULL_TREE; + + base_off = wioff.to_uhwi (); + unsigned HOST_WIDE_INT fieldoff = 0; + init = fold_ctor_reference (NULL_TREE, init, base_off, 0, array, + &fieldoff); + HOST_WIDE_INT cstoff; + if (init && base_off.is_constant (&cstoff)) + { + cstoff = (cstoff - fieldoff) / BITS_PER_UNIT; + offset = build_int_cst (sizetype, cstoff); + } } - return 0; + if (!init || TREE_CODE (init) != STRING_CST) + return NULL_TREE; + + tree array_size = DECL_SIZE_UNIT (array); + if (!array_size || TREE_CODE (array_size) != INTEGER_CST) + return NULL_TREE; + + /* Avoid returning a string that doesn't fit in the array + it is stored in, like + const char a[4] = "abcde"; + but do handle those that fit even if they have excess + initializers, such as in + const char a[4] = "abc\000\000"; + The excess elements contribute to TREE_STRING_LENGTH() + but not to strlen(). */ + unsigned HOST_WIDE_INT length + = strnlen (TREE_STRING_POINTER (init), TREE_STRING_LENGTH (init)); + if (compare_tree_int (array_size, length + 1) < 0) + return NULL_TREE; + + *ptr_offset = offset; + return init; } /* Generate code to calculate OPS, and exploded expression |