diff options
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 |