Hi!
This patch makes strlen range computations more conservative.
Firstly if there is a visible type cast from type A to B before passing
then value to strlen, don't expect the type layout of B to restrict the
possible return value range of strlen.
Furthermore use the outermost enclosing array instead of the
innermost one, because too aggressive optimization will likely
convert harmless errors into security-relevant errors, because
as the existing test cases demonstrate, this optimization is actively
attacking string length checks in user code, while and not giving
any warnings.
Bootstrapped and reg-tested on x86_64-pc-linux-gnu.
Is it OK for trunk?
Thanks
Bernd.
gcc:
2018-07-24 Bernd Edlinger <bernd.edlin...@hotmail.de>
* gimple-fold.c (get_range_strlen): Add a check for type casts.
Use outermost enclosing array size instead of innermost one.
* tree-ssa-strlen.c (maybe_set_strlen_range): Likewise.
testsuite:
2018-07-24 Bernd Edlinger <bernd.edlin...@hotmail.de>
* gcc.dg/strlenopt-40.c: Adjust test expectations.
* gcc.dg/strlenopt-45.c: Likewise.
* gcc.dg/strlenopt-48.c: Likewise.
* gcc.dg/strlenopt-51.c: Likewise.
* gcc.dg/strlenopt-54.c: New test.
Index: gcc/gimple-fold.c
===================================================================
--- gcc/gimple-fold.c (revision 262904)
+++ gcc/gimple-fold.c (working copy)
@@ -1339,19 +1339,33 @@ get_range_strlen (tree arg, tree length[2], bitmap
if (TREE_CODE (arg) == ARRAY_REF)
{
- tree type = TREE_TYPE (TREE_OPERAND (arg, 0));
+ /* Avoid arrays of pointers. */
+ if (TREE_CODE (TREE_TYPE (arg)) == POINTER_TYPE)
+ return false;
- /* Determine the "innermost" array type. */
- while (TREE_CODE (type) == ARRAY_TYPE
- && TREE_CODE (TREE_TYPE (type)) == ARRAY_TYPE)
- type = TREE_TYPE (type);
+ /* Look for the outermost enclosing array. */
+ while (TREE_CODE (arg) == ARRAY_REF
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (arg, 0)))
+ == ARRAY_TYPE)
+ arg = TREE_OPERAND (arg, 0);
- /* Avoid arrays of pointers. */
- tree eltype = TREE_TYPE (type);
- if (TREE_CODE (type) != ARRAY_TYPE
- || !INTEGRAL_TYPE_P (eltype))
+ 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))
+ || TREE_TYPE (TREE_TYPE (TREE_OPERAND (base, 0)))
+ != TREE_TYPE (base)))
+ || TREE_CODE (base) == VIEW_CONVERT_EXPR)
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))
return false;
@@ -1362,9 +1376,9 @@ get_range_strlen (tree arg, tree length[2], bitmap
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
@@ -1371,6 +1385,20 @@ get_range_strlen (tree arg, tree length[2], bitmap
&& (TREE_CODE (TREE_TYPE (TREE_OPERAND (arg, 1)))
== ARRAY_TYPE))
{
+ tree base = TREE_OPERAND (arg, 0);
+ 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))
+ || TREE_TYPE (TREE_TYPE (TREE_OPERAND (base, 0)))
+ != TREE_TYPE (base)))
+ || TREE_CODE (base) == VIEW_CONVERT_EXPR)
+ 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
@@ -1386,10 +1414,6 @@ get_range_strlen (tree arg, tree length[2], bitmap
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))
Index: gcc/tree-ssa-strlen.c
===================================================================
--- gcc/tree-ssa-strlen.c (revision 262904)
+++ gcc/tree-ssa-strlen.c (working copy)
@@ -1149,9 +1149,33 @@ maybe_set_strlen_range (tree lhs, tree src, tree b
if (TREE_CODE (src) == ADDR_EXPR)
{
+ src = TREE_OPERAND (src, 0);
+
+ /* Avoid address of pointers. */
+ if (TREE_CODE (TREE_TYPE (src)) == POINTER_TYPE)
+ goto done;
+
+ /* Look for the outermost enclosing array. */
+ while (TREE_CODE (src) == ARRAY_REF
+ && TREE_CODE (TREE_TYPE (TREE_OPERAND (src, 0))) == ARRAY_TYPE)
+ src = TREE_OPERAND (src, 0);
+
+ tree base = src;
+ 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))
+ || TREE_TYPE (TREE_TYPE (TREE_OPERAND (base, 0)))
+ != TREE_TYPE (base)))
+ || TREE_CODE (base) == VIEW_CONVERT_EXPR)
+ goto done;
+
/* 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))
{
@@ -1183,6 +1207,7 @@ maybe_set_strlen_range (tree lhs, tree src, tree b
}
}
+done:
if (bound)
{
/* For strnlen, adjust MIN and MAX as necessary. If the bound
Index: gcc/testsuite/gcc.dg/strlenopt-40.c
===================================================================
--- gcc/testsuite/gcc.dg/strlenopt-40.c (revision 262904)
+++ gcc/testsuite/gcc.dg/strlenopt-40.c (working copy)
@@ -105,20 +105,20 @@ void elim_global_arrays (int i)
/* Verify that the expression involving the strlen call as well
as whatever depends on it is eliminated from the test output.
All these expressions must be trivially true. */
- ELIM_TRUE (strlen (a7_3[0]) < sizeof a7_3[0]);
- ELIM_TRUE (strlen (a7_3[1]) < sizeof a7_3[1]);
- ELIM_TRUE (strlen (a7_3[6]) < sizeof a7_3[6]);
- ELIM_TRUE (strlen (a7_3[i]) < sizeof a7_3[i]);
+ ELIM_TRUE (strlen (a7_3[0]) < sizeof a7_3);
+ ELIM_TRUE (strlen (a7_3[1]) < sizeof a7_3);
+ ELIM_TRUE (strlen (a7_3[6]) < sizeof a7_3);
+ ELIM_TRUE (strlen (a7_3[i]) < sizeof a7_3);
- ELIM_TRUE (strlen (a5_7[0]) < sizeof a5_7[0]);
- ELIM_TRUE (strlen (a5_7[1]) < sizeof a5_7[1]);
- ELIM_TRUE (strlen (a5_7[4]) < sizeof a5_7[4]);
- ELIM_TRUE (strlen (a5_7[i]) < sizeof a5_7[0]);
+ ELIM_TRUE (strlen (a5_7[0]) < sizeof a5_7);
+ ELIM_TRUE (strlen (a5_7[1]) < sizeof a5_7);
+ ELIM_TRUE (strlen (a5_7[4]) < sizeof a5_7);
+ ELIM_TRUE (strlen (a5_7[i]) < sizeof a5_7);
- ELIM_TRUE (strlen (ax_3[0]) < sizeof ax_3[0]);
- ELIM_TRUE (strlen (ax_3[1]) < sizeof ax_3[1]);
- ELIM_TRUE (strlen (ax_3[9]) < sizeof ax_3[9]);
- ELIM_TRUE (strlen (ax_3[i]) < sizeof ax_3[i]);
+ ELIM_TRUE (strlen (ax_3[0]) < DIFF_MAX - 1);
+ ELIM_TRUE (strlen (ax_3[1]) < DIFF_MAX - 1);
+ ELIM_TRUE (strlen (ax_3[9]) < DIFF_MAX - 1);
+ ELIM_TRUE (strlen (ax_3[i]) < DIFF_MAX - 1);
ELIM_TRUE (strlen (a3) < sizeof a3);
ELIM_TRUE (strlen (a7) < sizeof a7);
@@ -134,17 +134,17 @@ void elim_pointer_to_arrays (void)
ELIM_TRUE (strlen (*pa5) < 5);
ELIM_TRUE (strlen (*pa3) < 3);
- ELIM_TRUE (strlen ((*pa7_3)[0]) < 3);
- ELIM_TRUE (strlen ((*pa7_3)[1]) < 3);
- ELIM_TRUE (strlen ((*pa7_3)[6]) < 3);
+ ELIM_TRUE (strlen ((*pa7_3)[0]) < 21);
+ ELIM_TRUE (strlen ((*pa7_3)[1]) < 21);
+ ELIM_TRUE (strlen ((*pa7_3)[6]) < 21);
- ELIM_TRUE (strlen ((*pax_3)[0]) < 3);
- ELIM_TRUE (strlen ((*pax_3)[1]) < 3);
- ELIM_TRUE (strlen ((*pax_3)[9]) < 3);
+ ELIM_TRUE (strlen ((*pax_3)[0]) < DIFF_MAX - 1);
+ ELIM_TRUE (strlen ((*pax_3)[1]) < DIFF_MAX - 1);
+ ELIM_TRUE (strlen ((*pax_3)[9]) < DIFF_MAX - 1);
- ELIM_TRUE (strlen ((*pa5_7)[0]) < 7);
- ELIM_TRUE (strlen ((*pa5_7)[1]) < 7);
- ELIM_TRUE (strlen ((*pa5_7)[4]) < 7);
+ ELIM_TRUE (strlen ((*pa5_7)[0]) < 35);
+ ELIM_TRUE (strlen ((*pa5_7)[1]) < 35);
+ ELIM_TRUE (strlen ((*pa5_7)[4]) < 35);
}
void elim_global_arrays_and_strings (int i)
@@ -198,11 +198,11 @@ void elim_member_arrays_obj (int i)
ELIM_TRUE (strlen (ma0_3_5_7[1][1][0].a5) < 5);
ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a5) < 5);
- ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a7_3[0]) < 3);
- ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a7_3[2]) < 3);
+ ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a7_3[0]) < 21);
+ ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a7_3[2]) < 21);
- ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a5_7[0]) < 7);
- ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a5_7[4]) < 7);
+ ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a5_7[0]) < 35);
+ ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a5_7[4]) < 35);
}
void elim_member_arrays_ptr (struct MemArrays0 *ma0,
@@ -210,19 +210,23 @@ void elim_member_arrays_ptr (struct MemArrays0 *ma
struct MemArrays7 *ma7,
int i)
{
- ELIM_TRUE (strlen (ma0->a7_3[0]) < 3);
- ELIM_TRUE (strlen (ma0->a7_3[1]) < 3);
- ELIM_TRUE (strlen (ma0->a7_3[6]) < 3);
- ELIM_TRUE (strlen (ma0->a7_3[6]) < 3);
- ELIM_TRUE (strlen (ma0->a7_3[i]) < 3);
- ELIM_TRUE (strlen (ma0->a7_3[i]) < 3);
+ ELIM_TRUE (strlen (ma0->a7_3[0]) < 21);
+ ELIM_TRUE (strlen (ma0->a7_3[1]) < 21);
+ ELIM_TRUE (strlen (ma0->a7_3[6]) < 21);
+ ELIM_TRUE (strlen (ma0->a7_3[6]) < 21);
+ ELIM_TRUE (strlen (ma0->a7_3[i]) < 21);
+ ELIM_TRUE (strlen (ma0->a7_3[i]) < 21);
- ELIM_TRUE (strlen (ma0->a5_7[0]) < 7);
- ELIM_TRUE (strlen (ma0[0].a5_7[0]) < 7);
- 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);
+ ELIM_TRUE (strlen (ma0->a5_7[0]) < 35);
+ ELIM_TRUE (strlen (ma0[0].a5_7[0]) < 35);
+#if 0
+ /* This is tranformed into strlen ((const char *) &(ma0 + 64)->a5_7[0])
+ which looks like a type cast and fails the check in get_range_strlen. */
+ ELIM_TRUE (strlen (ma0[1].a5_7[0]) < 35);
+ ELIM_TRUE (strlen (ma0[1].a5_7[4]) < 35);
+ ELIM_TRUE (strlen (ma0[9].a5_7[0]) < 35);
+ ELIM_TRUE (strlen (ma0[9].a5_7[4]) < 35);
+#endif
ELIM_TRUE (strlen (ma0->a3) < sizeof ma0->a3);
ELIM_TRUE (strlen (ma0->a5) < sizeof ma0->a5);
Index: gcc/testsuite/gcc.dg/strlenopt-45.c
===================================================================
--- gcc/testsuite/gcc.dg/strlenopt-45.c (revision 262904)
+++ gcc/testsuite/gcc.dg/strlenopt-45.c (working copy)
@@ -85,19 +85,19 @@ void elim_strnlen_arr_cst (void)
ELIM (strnlen (a3_7[0], 1) < 2);
ELIM (strnlen (a3_7[0], 2) < 3);
ELIM (strnlen (a3_7[0], 3) < 4);
- ELIM (strnlen (a3_7[0], 9) < 8);
- ELIM (strnlen (a3_7[0], PTRDIFF_MAX) < 8);
- ELIM (strnlen (a3_7[0], SIZE_MAX) < 8);
- ELIM (strnlen (a3_7[0], -1) < 8);
+ ELIM (strnlen (a3_7[0], 9) < 10);
+ ELIM (strnlen (a3_7[0], PTRDIFF_MAX) < 21);
+ ELIM (strnlen (a3_7[0], SIZE_MAX) < 21);
+ ELIM (strnlen (a3_7[0], -1) < 21);
ELIM (strnlen (a3_7[2], 0) == 0);
ELIM (strnlen (a3_7[2], 1) < 2);
ELIM (strnlen (a3_7[2], 2) < 3);
ELIM (strnlen (a3_7[2], 3) < 4);
- ELIM (strnlen (a3_7[2], 9) < 8);
- ELIM (strnlen (a3_7[2], PTRDIFF_MAX) < 8);
- ELIM (strnlen (a3_7[2], SIZE_MAX) < 8);
- ELIM (strnlen (a3_7[2], -1) < 8);
+ ELIM (strnlen (a3_7[2], 9) < 10);
+ ELIM (strnlen (a3_7[2], PTRDIFF_MAX) < 21);
+ ELIM (strnlen (a3_7[2], SIZE_MAX) < 21);
+ ELIM (strnlen (a3_7[2], -1) < 21);
ELIM (strnlen ((char*)a3_7, 0) == 0);
ELIM (strnlen ((char*)a3_7, 1) < 2);
@@ -106,10 +106,10 @@ void elim_strnlen_arr_cst (void)
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, 23) < 21);
+ ELIM (strnlen ((char*)a3_7, PTRDIFF_MAX) < 21);
+ ELIM (strnlen ((char*)a3_7, SIZE_MAX) < 21);
+ ELIM (strnlen ((char*)a3_7, -1) < 21);
ELIM (strnlen (ax, 0) == 0);
ELIM (strnlen (ax, 1) < 2);
@@ -154,37 +154,39 @@ void elim_strnlen_memarr_cst (struct MemArrays *p,
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);
ELIM (strnlen (p->a3_7[0], 2) < 3);
ELIM (strnlen (p->a3_7[0], 3) < 4);
- ELIM (strnlen (p->a3_7[0], 9) < 8);
- ELIM (strnlen (p->a3_7[0], PTRDIFF_MAX) < 8);
- ELIM (strnlen (p->a3_7[0], SIZE_MAX) < 8);
- ELIM (strnlen (p->a3_7[0], -1) < 8);
+ ELIM (strnlen (p->a3_7[0], 9) < 10);
+ ELIM (strnlen (p->a3_7[0], 21) < 22);
+ ELIM (strnlen (p->a3_7[0], PTRDIFF_MAX) < 21);
+ ELIM (strnlen (p->a3_7[0], SIZE_MAX) < 21);
+ ELIM (strnlen (p->a3_7[0], -1) < 21);
ELIM (strnlen (p->a3_7[2], 0) == 0);
ELIM (strnlen (p->a3_7[2], 1) < 2);
ELIM (strnlen (p->a3_7[2], 2) < 3);
ELIM (strnlen (p->a3_7[2], 3) < 4);
- ELIM (strnlen (p->a3_7[2], 9) < 8);
- ELIM (strnlen (p->a3_7[2], PTRDIFF_MAX) < 8);
- ELIM (strnlen (p->a3_7[2], SIZE_MAX) < 8);
- ELIM (strnlen (p->a3_7[2], -1) < 8);
+ ELIM (strnlen (p->a3_7[2], 9) < 10);
+ ELIM (strnlen (p->a3_7[2], 21) < 22);
+ ELIM (strnlen (p->a3_7[2], PTRDIFF_MAX) < 21);
+ ELIM (strnlen (p->a3_7[2], SIZE_MAX) < 21);
+ ELIM (strnlen (p->a3_7[2], -1) < 21);
ELIM (strnlen (p->a3_7[i], 0) == 0);
ELIM (strnlen (p->a3_7[i], 1) < 2);
Index: gcc/testsuite/gcc.dg/strlenopt-48.c
===================================================================
--- gcc/testsuite/gcc.dg/strlenopt-48.c (revision 262904)
+++ gcc/testsuite/gcc.dg/strlenopt-48.c (working copy)
@@ -9,8 +9,8 @@
void f (void)
{
- extern char a[2][1];
- int n = strlen (a[1]);
+ extern char a[1][1];
+ int n = strlen (a[0]);
if (n)
abort();
}
@@ -17,8 +17,8 @@ void f (void)
void g (void)
{
- extern char b[3][2][1];
- int n = strlen (b[2][1]);
+ extern char b[1][1][1];
+ int n = strlen (b[0][0]);
if (n)
abort();
}
@@ -25,8 +25,8 @@ void g (void)
void h (void)
{
- extern char c[4][3][2][1];
- int n = strlen (c[3][2][1]);
+ extern char c[1][1][1][1];
+ int n = strlen (c[0][0][0]);
if (n)
abort();
}
Index: gcc/testsuite/gcc.dg/strlenopt-51.c
===================================================================
--- gcc/testsuite/gcc.dg/strlenopt-51.c (revision 262904)
+++ gcc/testsuite/gcc.dg/strlenopt-51.c (working copy)
@@ -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" } } */
Index: gcc/testsuite/gcc.dg/strlenopt-54.c
===================================================================
--- gcc/testsuite/gcc.dg/strlenopt-54.c (revision 0)
+++ gcc/testsuite/gcc.dg/strlenopt-54.c (working copy)
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -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" } } */