aboutsummaryrefslogtreecommitdiff
path: root/gcc/builtins.c
diff options
context:
space:
mode:
authorMartin Sebor <msebor@redhat.com>2016-10-24 16:53:20 +0000
committerMartin Sebor <msebor@gcc.gnu.org>2016-10-24 10:53:20 -0600
commit1eb4547b1058d6a4e1912ac0f15cc0a69bf4fd78 (patch)
tree3fd153c90170bbed75e929c3a8588d01578162b8 /gcc/builtins.c
parent94caf86019219030154f76b786f9de08c2b81275 (diff)
downloadgcc-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.c103
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