Hi,
this is an update (v5) of my patch:
As discussed earlier, this version does no longer enable
-fassume-zero-terminated-char-arrays with -Ofast.
I am ready to remove the -fassume-zero-terminated-char-arrays altogether if we
decide what
to do with the code-gen test cases that still use it (xfail or remove).
Bootstrapped and reg-tested on x86_64-pc-linux-gnu.
Is it OK for trunk?
Thanks
Bernd.
gcc:
2018-08-21 Bernd Edlinger <[email protected]>
* common.opt: Add new optimization option
-fassume-zero-terminated-char-arrays.
* gimple-fold.h (looks_like_a_char_array_without_typecast_p): Declare.
* gimple-fold.c (looks_like_a_char_array_without_typecast_p): Helper
function for strlen range estimations.
(get_range_strlen): Use looks_like_a_char_array_without_typecast_p.
* tree-ssa-strlen.c (maybe_set_strlen_range): Likewise.
(get_min_string_length): Avoid not NUL terminated string literals.
* doc/invoke.texi: Document -fassume-zero-terminated-char-arrays.
* tree-ssa-dse.c (compute_trims): Avoid folding away undefined
behaviour.
testsuite:
2018-08-32 Bernd Edlinger <[email protected]>
* gcc.dg/pr83373.c: Add xfail.
* gcc.dg/strlenopt-36.c: Adjust test expectations.
* gcc.dg/strlenopt-40.c: Likewise.
* gcc.dg/strlenopt-45.c: Likewise.
* gcc.dg/strlenopt-48.c: Likewise.
* gcc.dg/strlenopt-51.c: Likewise.
* gcc.dg/strlenopt-57.c: New test.
* gcc.dg/strlenopt-58.c: New test.
* gcc.dg/strlenopt-59.c: New test.
diff -Npur gcc/common.opt gcc/common.opt
--- gcc/common.opt 2018-08-19 17:11:34.000000000 +0200
+++ gcc/common.opt 2018-08-22 09:04:53.520305828 +0200
@@ -1025,6 +1025,10 @@ fsanitize-undefined-trap-on-error
Common Driver Report Var(flag_sanitize_undefined_trap_on_error) Init(0)
Use trap instead of a library function for undefined behavior sanitization.
+fassume-zero-terminated-char-arrays
+Common Var(flag_assume_zero_terminated_char_arrays) Optimization Init(0)
+Optimize under the assumption that char arrays must always be zero terminated.
+
fasynchronous-unwind-tables
Common Report Var(flag_asynchronous_unwind_tables) Optimization
Generate unwind tables that are exact at each instruction boundary.
diff -Npur gcc/doc/invoke.texi gcc/doc/invoke.texi
--- gcc/doc/invoke.texi 2018-08-21 10:13:34.000000000 +0200
+++ gcc/doc/invoke.texi 2018-08-22 09:06:18.645102845 +0200
@@ -388,7 +388,8 @@ Objective-C and Objective-C++ Dialects}.
-falign-jumps[=@var{n}[:@var{m}:[@var{n2}[:@var{m2}]]]] @gol
-falign-labels[=@var{n}[:@var{m}:[@var{n2}[:@var{m2}]]]] @gol
-falign-loops[=@var{n}[:@var{m}:[@var{n2}[:@var{m2}]]]] @gol
--fassociative-math -fauto-profile -fauto-profile[=@var{path}] @gol
+-fassociative-math -fassume-zero-terminated-char-arrays @gol
+-fauto-profile -fauto-profile[=@var{path}] @gol
-fauto-inc-dec -fbranch-probabilities @gol
-fbranch-target-load-optimize -fbranch-target-load-optimize2 @gol
-fbtr-bb-exclusive -fcaller-saves @gol
@@ -9978,6 +9979,16 @@ is automatically enabled when both @opti
The default is @option{-fno-associative-math}.
+@item -fassume-zero-terminated-char-arrays
+@opindex fassume-zero-terminated-char-arrays
+
+Optimize under the assumption that char arrays must always be zero
+terminated. This may have an effect on code that uses strlen to
+check the string length, for instance in assertions. Under certain
+conditions such checks can be optimized away.
+
+The default is @option{-fno-assume-zero-terminated-char-arrays}.
+
@item -freciprocal-math
@opindex freciprocal-math
diff -Npur gcc/gimple-fold.c gcc/gimple-fold.c
--- gcc/gimple-fold.c 2018-08-19 17:11:34.000000000 +0200
+++ gcc/gimple-fold.c 2018-08-22 09:04:53.741302702 +0200
@@ -1257,6 +1257,45 @@ gimple_fold_builtin_memset (gimple_stmt_
return true;
}
+/* Determine if a char array is suitable for strlen range estimations.
+ Return false if ARG is not a char array, or if the inner reference
+ chain appears to go through a type cast, or if !optimistic,
+ or if !flag_assume_zero_terminated_char_arrays.
+ Otherwise return true.
+ Note that type gimple type informations are not 100% guaranteed
+ to be accurate.
+ OPTIMISTIC is true when the result is used for warnings only. */
+
+bool
+looks_like_a_char_array_without_typecast_p (tree arg, bool optimistic)
+{
+ if (!flag_assume_zero_terminated_char_arrays && !optimistic)
+ return false;
+
+ /* We handle arrays of integer types. */
+ if (TREE_CODE (TREE_TYPE (arg)) != ARRAY_TYPE
+ || TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) != INTEGER_TYPE
+ || TYPE_MODE (TREE_TYPE (TREE_TYPE (arg))) != TYPE_MODE (char_type_node)
+ || TYPE_PRECISION (TREE_TYPE (TREE_TYPE (arg)))
+ != TYPE_PRECISION (char_type_node))
+ return false;
+
+ tree base = arg;
+ while (TREE_CODE (base) == ARRAY_REF
+ || TREE_CODE (base) == ARRAY_RANGE_REF
+ || TREE_CODE (base) == COMPONENT_REF)
+ base = TREE_OPERAND (base, 0);
+
+ /* If this looks like a type cast don't assume anything. */
+ if ((TREE_CODE (base) == MEM_REF
+ && (! integer_zerop (TREE_OPERAND (base, 1))
+ || TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (TREE_OPERAND (base, 0))))
+ != TYPE_MAIN_VARIANT (TREE_TYPE (base))))
+ || handled_component_p (base))
+ return false;
+
+ return true;
+}
/* Obtain the minimum and maximum string length or minimum and maximum
value of ARG in LENGTH[0] and LENGTH[1], respectively.
@@ -1272,6 +1311,7 @@ gimple_fold_builtin_memset (gimple_stmt_
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.
+ TYPE == 2 and FUZZY != 0 cannot be used together.
Set *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
@@ -1312,8 +1352,8 @@ get_range_strlen (tree arg, tree length[
member. */
tree idx = TREE_OPERAND (op, 1);
- arg = TREE_OPERAND (op, 0);
- tree optype = TREE_TYPE (arg);
+ op = TREE_OPERAND (op, 0);
+ tree optype = TREE_TYPE (op);
if (tree dom = TYPE_DOMAIN (optype))
if (tree bound = TYPE_MAX_VALUE (dom))
if (TREE_CODE (bound) == INTEGER_CST
@@ -1339,23 +1379,21 @@ get_range_strlen (tree arg, tree length[
return get_range_strlen (TREE_OPERAND (arg, 0), length,
visited, type, fuzzy, flexp, eltsize);
+ if (eltsize != 1)
+ return false;
+
if (TREE_CODE (arg) == ARRAY_REF)
{
- tree type = TREE_TYPE (TREE_OPERAND (arg, 0));
-
- /* 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 (type);
- if (TREE_CODE (type) != ARRAY_TYPE
- || !INTEGRAL_TYPE_P (eltype))
+ if (!looks_like_a_char_array_without_typecast_p (arg, fuzzy == 2))
return false;
+ tree type = TREE_TYPE (arg);
+
+ /* Fail when the array bound is unknown or zero. */
val = TYPE_SIZE_UNIT (type);
- if (!val || integer_zerop (val))
+ if (!val
+ || TREE_CODE (val) != INTEGER_CST
+ || integer_zerop (val))
return false;
val = fold_build2 (MINUS_EXPR, TREE_TYPE (val), val,
@@ -1364,15 +1402,16 @@ get_range_strlen (tree arg, tree length[
the array could have zero length. */
*minlen = ssize_int (0);
- 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)))
+ if (TREE_CODE (arg) == COMPONENT_REF
+ && type == TREE_TYPE (arg)
+ && array_at_struct_end_p (arg))
*flexp = true;
}
- else if (TREE_CODE (arg) == COMPONENT_REF
- && (TREE_CODE (TREE_TYPE (TREE_OPERAND (arg, 1)))
- == ARRAY_TYPE))
+ else if (TREE_CODE (arg) == COMPONENT_REF)
{
+ if (!looks_like_a_char_array_without_typecast_p (arg, fuzzy == 2))
+ return false;
+
/* 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
@@ -1388,22 +1427,21 @@ get_range_strlen (tree arg, tree length[
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))
+ if (!val
+ || TREE_CODE (val) != INTEGER_CST
+ || 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. */
*minlen = ssize_int (0);
}
-
- if (VAR_P (arg))
+ else if (VAR_P (arg)
+ && (flag_assume_zero_terminated_char_arrays || fuzzy == 2))
{
tree type = TREE_TYPE (arg);
if (POINTER_TYPE_P (type))
@@ -1411,13 +1449,23 @@ get_range_strlen (tree arg, tree length[
if (TREE_CODE (type) == ARRAY_TYPE)
{
+ /* We handle arrays of integer types. */
+ if (TREE_CODE (TREE_TYPE (type)) != INTEGER_TYPE
+ || TYPE_MODE (TREE_TYPE (type))
+ != TYPE_MODE (char_type_node)
+ || TYPE_PRECISION (TREE_TYPE (type))
+ != TYPE_PRECISION (char_type_node))
+ return false;
+
+ /* Fail when the array bound is unknown or zero. */
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));
+
+ 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. */
*minlen = ssize_int (0);
@@ -1550,6 +1598,7 @@ get_range_strlen (tree arg, tree length[
if we can determine string length minimum and maximum; it will use
the minimum from the ones where it can be determined.
STRICT false should be only used for warning code.
+ STRICT is by default false.
ELTSIZE is 1 for normal single byte character strings, and 2 or
4 for wide characer strings. ELTSIZE is by default 1. */
diff -Npur gcc/gimple-fold.h gcc/gimple-fold.h
--- gcc/gimple-fold.h 2018-08-19 17:11:34.000000000 +0200
+++ gcc/gimple-fold.h 2018-08-22 09:04:53.741302702 +0200
@@ -61,6 +61,7 @@ extern bool gimple_fold_builtin_snprintf
extern bool arith_code_with_undefined_signed_overflow (tree_code);
extern gimple_seq rewrite_to_defined_overflow (gimple *);
extern void replace_call_with_value (gimple_stmt_iterator *, tree);
+extern bool looks_like_a_char_array_without_typecast_p (tree, bool);
/* gimple_build, functionally matching fold_buildN, outputs stmts
int the provided sequence, matching and simplifying them on-the-fly.
diff -Npur gcc/testsuite/gcc.dg/pr83373.c gcc/testsuite/gcc.dg/pr83373.c
--- gcc/testsuite/gcc.dg/pr83373.c 2018-08-19 17:11:34.000000000 +0200
+++ gcc/testsuite/gcc.dg/pr83373.c 2018-08-22 11:48:17.312080785 +0200
@@ -16,7 +16,7 @@ inline char* my_strcpy (char* dst, const
__builtin_memcpy (dst, src, len + 1);
else
{
- __builtin_memcpy (dst, src, size - 1); /* { dg-bogus "\\\[-Wstringop-oveflow]" } */
+ __builtin_memcpy (dst, src, size - 1); /* { dg-bogus "\\\[-Wstringop-overflow=]" "" { xfail *-*-* } } */
dst[size - 1] = '\0';
}
diff -Npur gcc/testsuite/gcc.dg/strlenopt-36.c gcc/testsuite/gcc.dg/strlenopt-36.c
--- gcc/testsuite/gcc.dg/strlenopt-36.c 2018-08-19 17:11:34.000000000 +0200
+++ gcc/testsuite/gcc.dg/strlenopt-36.c 2018-08-22 09:04:53.742302688 +0200
@@ -1,7 +1,7 @@
/* PR tree-optimization/78450 - strlen(s) return value can be assumed
to be less than the size of s
{ dg-do compile }
- { dg-options "-O2 -fdump-tree-optimized" } */
+ { dg-options "-O2 -fassume-zero-terminated-char-arrays -fdump-tree-optimized" } */
#include "strlenopt.h"
diff -Npur gcc/testsuite/gcc.dg/strlenopt-40.c gcc/testsuite/gcc.dg/strlenopt-40.c
--- gcc/testsuite/gcc.dg/strlenopt-40.c 2018-08-19 17:11:34.000000000 +0200
+++ gcc/testsuite/gcc.dg/strlenopt-40.c 2018-08-22 09:04:53.742302688 +0200
@@ -1,7 +1,7 @@
/* PR tree-optimization/83671 - fix for false positive reported by
-Wstringop-overflow does not work with inlining
{ dg-do compile }
- { dg-options "-O1 -fdump-tree-optimized" } */
+ { dg-options "-O1 -fassume-zero-terminated-char-arrays -fdump-tree-optimized" } */
#include "strlenopt.h"
@@ -219,10 +219,15 @@ void elim_member_arrays_ptr (struct MemA
ELIM_TRUE (strlen (ma0->a5_7[0]) < 7);
ELIM_TRUE (strlen (ma0[0].a5_7[0]) < 7);
+#if 0
+ /* This is transformed into strlen ((const char *) &(ma0 + 64)->a5_7[0])
+ which looks like a type cast and fails the check in
+ looks_like_a_char_array_without_typecast_p. */
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);
+#endif
ELIM_TRUE (strlen (ma0->a3) < sizeof ma0->a3);
ELIM_TRUE (strlen (ma0->a5) < sizeof ma0->a5);
diff -Npur gcc/testsuite/gcc.dg/strlenopt-45.c gcc/testsuite/gcc.dg/strlenopt-45.c
--- gcc/testsuite/gcc.dg/strlenopt-45.c 2018-08-19 17:11:34.000000000 +0200
+++ gcc/testsuite/gcc.dg/strlenopt-45.c 2018-08-22 09:04:53.767302335 +0200
@@ -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 -fassume-zero-terminated-char-arrays -fdump-tree-optimized" } */
#include "strlenopt.h"
@@ -43,7 +43,6 @@ extern size_t strnlen (const char *, siz
else \
FAIL (made_in_false_branch)
-extern char c;
extern char a1[1];
extern char a3[3];
extern char a5[5];
@@ -52,18 +51,6 @@ extern char ax[];
void elim_strnlen_arr_cst (void)
{
- /* The length of a string stored in a one-element array must be zero.
- The result reported by strnlen() for such an array can be non-zero
- only when the bound is equal to 1 (in which case the result must
- be one). */
- ELIM (strnlen (&c, 0) == 0);
- ELIM (strnlen (&c, 1) < 2);
- ELIM (strnlen (&c, 2) == 0);
- ELIM (strnlen (&c, 9) == 0);
- ELIM (strnlen (&c, PTRDIFF_MAX) == 0);
- ELIM (strnlen (&c, SIZE_MAX) == 0);
- ELIM (strnlen (&c, -1) == 0);
-
ELIM (strnlen (a1, 0) == 0);
ELIM (strnlen (a1, 1) < 2);
ELIM (strnlen (a1, 2) == 0);
@@ -99,17 +86,18 @@ void elim_strnlen_arr_cst (void)
ELIM (strnlen (a3_7[2], SIZE_MAX) < 8);
ELIM (strnlen (a3_7[2], -1) < 8);
- ELIM (strnlen ((char*)a3_7, 0) == 0);
- ELIM (strnlen ((char*)a3_7, 1) < 2);
- ELIM (strnlen ((char*)a3_7, 2) < 3);
- 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[0], 0) == 0);
+ ELIM (strnlen ((char*)a3_7[0], 1) < 2);
+ ELIM (strnlen ((char*)a3_7[0], 2) < 3);
+ ELIM (strnlen ((char*)a3_7[0], 3) < 4);
+ ELIM (strnlen ((char*)a3_7[0], 7) < 8);
+ ELIM (strnlen ((char*)a3_7[0], 9) < 7);
+ ELIM (strnlen ((char*)a3_7[0], 19) < 7);
+ ELIM (strnlen ((char*)a3_7[0], 21) < 7);
+ ELIM (strnlen ((char*)a3_7[0], 23) < 7);
+ ELIM (strnlen ((char*)a3_7[0], PTRDIFF_MAX) < 7);
+ ELIM (strnlen ((char*)a3_7[0], SIZE_MAX) < 7);
+ ELIM (strnlen ((char*)a3_7[0], -1) < 7);
ELIM (strnlen (ax, 0) == 0);
ELIM (strnlen (ax, 1) < 2);
@@ -122,7 +110,6 @@ void elim_strnlen_arr_cst (void)
struct MemArrays
{
- char c;
char a0[0];
char a1[1];
char a3[3];
@@ -133,13 +120,6 @@ struct MemArrays
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);
@@ -154,19 +134,19 @@ void elim_strnlen_memarr_cst (struct Mem
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->a3, 9) < 3);
+ ELIM (strnlen (p->a3, PTRDIFF_MAX) < 3);
+ ELIM (strnlen (p->a3, SIZE_MAX) < 3);
+ ELIM (strnlen (p->a3, -1) < 3);
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[i].a3, 9) < 3);
+ ELIM (strnlen (p[i].a3, PTRDIFF_MAX) < 3);
+ ELIM (strnlen (p[i].a3, SIZE_MAX) < 3);
+ ELIM (strnlen (p[i].a3, -1) < 3);
ELIM (strnlen (p->a3_7[0], 0) == 0);
ELIM (strnlen (p->a3_7[0], 1) < 2);
@@ -203,17 +183,18 @@ void elim_strnlen_memarr_cst (struct Mem
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 ((char*)p->a3_7[0], 0) == 0);
+ ELIM (strnlen ((char*)p->a3_7[0], 1) < 2);
+ ELIM (strnlen ((char*)p->a3_7[0], 2) < 3);
+ ELIM (strnlen ((char*)p->a3_7[0], 3) < 4);
+ ELIM (strnlen ((char*)p->a3_7[0], 7) < 8);
+ ELIM (strnlen ((char*)p->a3_7[0], 9) < 7);
+ ELIM (strnlen ((char*)p->a3_7[0], 19) < 7);
+ ELIM (strnlen ((char*)p->a3_7[0], 21) < 7);
+ ELIM (strnlen ((char*)p->a3_7[0], 23) < 7);
+ ELIM (strnlen ((char*)p->a3_7[0], PTRDIFF_MAX) < 7);
+ ELIM (strnlen ((char*)p->a3_7[0], SIZE_MAX) < 7);
+ ELIM (strnlen ((char*)p->a3_7[0], -1) < 7);
ELIM (strnlen (p->ax, 0) == 0);
ELIM (strnlen (p->ax, 1) < 2);
@@ -290,9 +271,6 @@ void elim_strnlen_range (char *s)
void keep_strnlen_arr_cst (void)
{
- KEEP (strnlen (&c, 1) == 0);
- KEEP (strnlen (&c, 1) == 1);
-
KEEP (strnlen (a1, 1) == 0);
KEEP (strnlen (a1, 1) == 1);
@@ -301,16 +279,12 @@ void keep_strnlen_arr_cst (void)
struct FlexArrays
{
- char c;
char a0[0]; /* Access to internal zero-length arrays are undefined. */
char a1[1];
};
void keep_strnlen_memarr_cst (struct FlexArrays *p)
{
- KEEP (strnlen (&p->c, 1) == 0);
- KEEP (strnlen (&p->c, 1) == 1);
-
#if 0
/* Accesses to internal zero-length arrays are undefined so avoid
exercising them. */
@@ -331,5 +305,5 @@ void keep_strnlen_memarr_cst (struct Fle
/* { dg-final { scan-tree-dump-times "call_in_true_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\]" 13 "optimized" } }
- { dg-final { scan-tree-dump-times "call_made_in_false_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 13 "optimized" } } */
+ { dg-final { scan-tree-dump-times "call_made_in_true_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 9 "optimized" } }
+ { dg-final { scan-tree-dump-times "call_made_in_false_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 9 "optimized" } } */
diff -Npur gcc/testsuite/gcc.dg/strlenopt-48.c gcc/testsuite/gcc.dg/strlenopt-48.c
--- gcc/testsuite/gcc.dg/strlenopt-48.c 2018-08-19 17:11:34.000000000 +0200
+++ gcc/testsuite/gcc.dg/strlenopt-48.c 2018-08-22 09:04:53.767302335 +0200
@@ -3,7 +3,7 @@
Verify that strlen() calls with one-character array elements of
multidimensional arrays are still folded.
{ dg-do compile }
- { dg-options "-O2 -Wall -fdump-tree-optimized" } */
+ { dg-options "-O2 -Wall -fassume-zero-terminated-char-arrays -fdump-tree-optimized" } */
#include "strlenopt.h"
diff -Npur gcc/testsuite/gcc.dg/strlenopt-51.c gcc/testsuite/gcc.dg/strlenopt-51.c
--- gcc/testsuite/gcc.dg/strlenopt-51.c 2018-08-19 17:11:34.000000000 +0200
+++ gcc/testsuite/gcc.dg/strlenopt-51.c 2018-08-22 09:04:53.768302320 +0200
@@ -101,7 +101,7 @@ void test_keep_a9_9 (int i)
{
#undef T
#define T(I) \
- KEEP (strlen (&a9_9[i][I][0]) > (1 + I) % 9); \
+ 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); \
@@ -115,7 +115,7 @@ void test_keep_a9_9 (int i)
}
/* { dg-final { scan-tree-dump-times "strlen" 72 "gimple" } }
- { dg-final { scan-tree-dump-times "strlen" 63 "optimized" } }
+ { dg-final { scan-tree-dump-times "strlen" 72 "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_true_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 81 "optimized" } }
{ dg-final { scan-tree-dump-times "call_made_in_false_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 81 "optimized" } } */
diff -Npur gcc/testsuite/gcc.dg/strlenopt-57.c gcc/testsuite/gcc.dg/strlenopt-57.c
--- gcc/testsuite/gcc.dg/strlenopt-57.c 1970-01-01 01:00:00.000000000 +0100
+++ gcc/testsuite/gcc.dg/strlenopt-57.c 2018-08-22 09:04:53.768302320 +0200
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-tree-optimized" } */
+
+#define assert(x) do { if (!(x)) __builtin_abort (); } while (0)
+extern int system (const char *);
+static int fun (char *p)
+{
+ char buf[16];
+
+ assert (__builtin_strlen (p) < 4);
+
+ __builtin_sprintf (buf, "echo %s - %s", p, p);
+ return system (buf);
+}
+
+void test (void)
+{
+ char b[2] = "ab";
+ fun (b);
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin_strlen" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_abort" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_sprintf" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "system" 1 "optimized" } } */
diff -Npur gcc/testsuite/gcc.dg/strlenopt-58.c gcc/testsuite/gcc.dg/strlenopt-58.c
--- gcc/testsuite/gcc.dg/strlenopt-58.c 1970-01-01 01:00:00.000000000 +0100
+++ gcc/testsuite/gcc.dg/strlenopt-58.c 2018-08-22 09:10:24.485637281 +0200
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fassume-zero-terminated-char-arrays -fdump-tree-optimized" } */
+
+typedef char A[6];
+typedef char B[2][3];
+
+A a;
+
+void test (void)
+{
+ B* b = (B*) a;
+ if (__builtin_strlen ((*b)[0]) > 2)
+ __builtin_abort ();
+}
+
+/* { dg-final { scan-tree-dump-times "__builtin_strlen" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "__builtin_abort" 1 "optimized" } } */
diff -Npur gcc/testsuite/gcc.dg/strlenopt-59.c gcc/testsuite/gcc.dg/strlenopt-59.c
--- gcc/testsuite/gcc.dg/strlenopt-59.c 1970-01-01 01:00:00.000000000 +0100
+++ gcc/testsuite/gcc.dg/strlenopt-59.c 2018-08-22 09:11:03.197092493 +0200
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fassume-zero-terminated-char-arrays -fdump-tree-optimized" } */
+
+typedef char B[2][3];
+
+B b;
+
+void test (void)
+{
+ if (__builtin_strlen (b[0]) > 2)
+ __builtin_abort ();
+}
+
+/* { dg-final { scan-tree-dump-not "__builtin_strlen" "optimized" } } */
+/* { dg-final { scan-tree-dump-not "__builtin_abort" "optimized" } } */
diff -Npur gcc/tree-ssa-dse.c gcc/tree-ssa-dse.c
--- gcc/tree-ssa-dse.c 2018-08-19 17:11:34.000000000 +0200
+++ gcc/tree-ssa-dse.c 2018-08-22 09:04:53.768302320 +0200
@@ -248,6 +248,12 @@ compute_trims (ao_ref *ref, sbitmap live
residual handling in mem* and str* functions is usually
reasonably efficient. */
*trim_tail = last_orig - last_live;
+ /* Don't fold away an out of bounds access, as this defeats proper
+ warnings. */
+ if (*trim_tail
+ && compare_tree_int (TYPE_SIZE_UNIT (TREE_TYPE (ref->base)),
+ last_orig) <= 0)
+ *trim_tail = 0;
}
else
*trim_tail = 0;
diff -Npur gcc/tree-ssa-strlen.c gcc/tree-ssa-strlen.c
--- gcc/tree-ssa-strlen.c 2018-08-21 10:51:08.000000000 +0200
+++ gcc/tree-ssa-strlen.c 2018-08-22 09:04:53.786302066 +0200
@@ -1156,11 +1156,13 @@ maybe_set_strlen_range (tree lhs, tree s
if (TREE_CODE (src) == ADDR_EXPR)
{
+ src = TREE_OPERAND (src, 0);
+
+ if (!looks_like_a_char_array_without_typecast_p (src, false))
+ ;
/* 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 && !array_at_struct_end_p (src))
+ else if (!array_at_struct_end_p (src))
{
tree type = TREE_TYPE (src);
if (tree size = TYPE_SIZE_UNIT (type))
@@ -1177,8 +1179,6 @@ maybe_set_strlen_range (tree lhs, tree s
}
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
@@ -3192,7 +3192,9 @@ get_min_string_length (tree rhs, bool *f
&& TREE_READONLY (rhs))
rhs = DECL_INITIAL (rhs);
- if (rhs && TREE_CODE (rhs) == STRING_CST)
+ if (rhs && TREE_CODE (rhs) == STRING_CST
+ && compare_tree_int (TYPE_SIZE_UNIT (TREE_TYPE (rhs)),
+ TREE_STRING_LENGTH (rhs)) >= 0)
{
*full_string_p = true;
return strlen (TREE_STRING_POINTER (rhs));