diff options
author | Martin Sebor <msebor@redhat.com> | 2016-10-24 16:53:20 +0000 |
---|---|---|
committer | Martin Sebor <msebor@gcc.gnu.org> | 2016-10-24 10:53:20 -0600 |
commit | 1eb4547b1058d6a4e1912ac0f15cc0a69bf4fd78 (patch) | |
tree | 3fd153c90170bbed75e929c3a8588d01578162b8 /gcc/builtins.c | |
parent | 94caf86019219030154f76b786f9de08c2b81275 (diff) | |
download | gcc-1eb4547b1058d6a4e1912ac0f15cc0a69bf4fd78.zip gcc-1eb4547b1058d6a4e1912ac0f15cc0a69bf4fd78.tar.gz gcc-1eb4547b1058d6a4e1912ac0f15cc0a69bf4fd78.tar.bz2 |
PR middle-end/77735 - FAIL: gcc.dg/tree-ssa/builtin-sprintf-warn-1.c
gcc/ChangeLog:
PR middle-end/77735
* builtins.c (string_length): New function.
(c_strlen): Use string_length. Correctly handle wide strings.
* gimple-ssa-sprintf.c (target_max_value, target_size_max): New
functions.
(target_int_max): Call target_max_value.
(format_result::knownrange): New data member.
(fmtresult::fmtresult): Define default constructor.
(format_integer): Use it and set format_result::knownrange.
Handle global constants.
(format_floating_max): Add third argument.
(format_floating): Recompute maximum value for %a for each argument.
(get_string_length): Use fmtresult default ctor.
(format_string): Set format_result::knownrange.
(format_directive): Check format_result::knownrange.
(add_bytes): Same. Correct caret placement in diagnostics.
(pass_sprintf_length::compute_format_length): Set
format_result::knownrange.
(pass_sprintf_length::handle_gimple_call): Use target_size_max.
gcc/testsuite/ChangeLog:
PR middle-end/77735
* gcc.dg/tree-ssa/builtin-sprintf-2.c: Add test cases.
* gcc.dg/tree-ssa/builtin-sprintf-warn-1.c: Same.
* gcc.dg/tree-ssa/builtin-sprintf-warn-2.c: Same.
* gcc.dg/tree-ssa/builtin-sprintf-warn-3.c: Adjust/relax.
* gcc.dg/tree-ssa/builtin-sprintf-warn-4.c: Add test cases.
* gcc.dg/tree-ssa/builtin-sprintf-warn-6.c: XFAIL for LP64 only.
* gcc.dg/tree-ssa/builtin-sprintf.c: Add test cases.
From-SVN: r241489
Diffstat (limited to 'gcc/builtins.c')
-rw-r--r-- | gcc/builtins.c | 103 |
1 files changed, 75 insertions, 28 deletions
diff --git a/gcc/builtins.c b/gcc/builtins.c index ff37773..997c0e8 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -503,9 +503,44 @@ get_pointer_alignment (tree exp) return align; } -/* Compute the length of a C string. TREE_STRING_LENGTH is not the right - way, because it could contain a zero byte in the middle. - TREE_STRING_LENGTH is the size of the character array, not the string. +/* Return the number of non-zero elements in the sequence + [ PTR, PTR + MAXELTS ) where each element's size is ELTSIZE bytes. + ELTSIZE must be a power of 2 less than 8. Used by c_strlen. */ + +static unsigned +string_length (const void *ptr, unsigned eltsize, unsigned maxelts) +{ + gcc_checking_assert (eltsize == 1 || eltsize == 2 || eltsize == 4); + + unsigned n; + + if (eltsize == 1) + { + /* Optimize the common case of plain char. */ + for (n = 0; n < maxelts; n++) + { + const char *elt = (const char*) ptr + n; + if (!*elt) + break; + } + } + else + { + for (n = 0; n < maxelts; n++) + { + const char *elt = (const char*) ptr + n * eltsize; + if (!memcmp (elt, "\0\0\0\0", eltsize)) + break; + } + } + return n; +} + +/* Compute the length of a null-terminated character string or wide + character string handling character sizes of 1, 2, and 4 bytes. + TREE_STRING_LENGTH is not the right way because it evaluates to + the size of the character array in bytes (as opposed to characters) + and because it can contain a zero byte in the middle. ONLY_VALUE should be nonzero if the result is not going to be emitted into the instruction stream and zero if it is going to be expanded. @@ -526,12 +561,6 @@ get_pointer_alignment (tree exp) tree c_strlen (tree src, int only_value) { - tree offset_node; - HOST_WIDE_INT offset; - int max; - const char *ptr; - location_t loc; - STRIP_NOPS (src); if (TREE_CODE (src) == COND_EXPR && (only_value || !TREE_SIDE_EFFECTS (TREE_OPERAND (src, 0)))) @@ -548,25 +577,36 @@ c_strlen (tree src, int only_value) && (only_value || !TREE_SIDE_EFFECTS (TREE_OPERAND (src, 0)))) return c_strlen (TREE_OPERAND (src, 1), only_value); - loc = EXPR_LOC_OR_LOC (src, input_location); + location_t loc = EXPR_LOC_OR_LOC (src, input_location); - src = string_constant (src, &offset_node); + /* Offset from the beginning of the string in bytes. */ + tree byteoff; + src = string_constant (src, &byteoff); if (src == 0) return NULL_TREE; - max = TREE_STRING_LENGTH (src) - 1; - ptr = TREE_STRING_POINTER (src); + /* Determine the size of the string element. */ + unsigned eltsize + = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (src)))); + + /* Set MAXELTS to sizeof (SRC) / sizeof (*SRC) - 1, the maximum possible + length of SRC. */ + unsigned maxelts = TREE_STRING_LENGTH (src) / eltsize - 1; - if (offset_node && TREE_CODE (offset_node) != INTEGER_CST) + /* PTR can point to the byte representation of any string type, including + char* and wchar_t*. */ + const char *ptr = TREE_STRING_POINTER (src); + + if (byteoff && TREE_CODE (byteoff) != INTEGER_CST) { /* If the string has an internal zero byte (e.g., "foo\0bar"), we can't compute the offset to the following null if we don't know where to start searching for it. */ - int i; - - for (i = 0; i < max; i++) - if (ptr[i] == 0) + if (string_length (ptr, eltsize, maxelts) < maxelts) + { + /* Return when an embedded null character is found. */ return NULL_TREE; + } /* We don't know the starting offset, but we do know that the string has no internal zero bytes. We can assume that the offset falls @@ -575,27 +615,31 @@ c_strlen (tree src, int only_value) and return that. This would perhaps not be valid if we were dealing with named arrays in addition to literal string constants. */ - return size_diffop_loc (loc, size_int (max), offset_node); + return size_diffop_loc (loc, size_int (maxelts * eltsize), byteoff); } + /* Offset from the beginning of the string in elements. */ + HOST_WIDE_INT eltoff; + /* We have a known offset into the string. Start searching there for a null character if we can represent it as a single HOST_WIDE_INT. */ - if (offset_node == 0) - offset = 0; - else if (! tree_fits_shwi_p (offset_node)) - offset = -1; + if (byteoff == 0) + eltoff = 0; + else if (! tree_fits_shwi_p (byteoff)) + eltoff = -1; else - offset = tree_to_shwi (offset_node); + eltoff = tree_to_shwi (byteoff) / eltsize; /* If the offset is known to be out of bounds, warn, and call strlen at runtime. */ - if (offset < 0 || offset > max) + if (eltoff < 0 || eltoff > maxelts) { /* Suppress multiple warnings for propagated constant strings. */ if (only_value != 2 && !TREE_NO_WARNING (src)) { - warning_at (loc, 0, "offset outside bounds of constant string"); + warning_at (loc, 0, "offset %qwi outside bounds of constant string", + eltoff); TREE_NO_WARNING (src) = 1; } return NULL_TREE; @@ -605,9 +649,12 @@ c_strlen (tree src, int only_value) constructed with build_string will have nulls appended, we win even if we get handed something like (char[4])"abcd". - Since OFFSET is our starting index into the string, no further + Since ELTOFF is our starting index into the string, no further calculation is needed. */ - return ssize_int (strlen (ptr + offset)); + unsigned len = string_length (ptr + eltoff * eltsize, eltsize, + maxelts - eltoff); + + return ssize_int (len); } /* Return a constant integer corresponding to target reading |