The ICE in the test case submitted in PR tree-optimization/83640 is triggered by the tree-ssa-strlen pass transforming calls to strcat to strcpy with an offset pointing to the terminating NUL of the destination string, and allowing the upper bound of the offset's range to exceed PTRDIFF_MAX by the length of the appended constant string. Although the ICE itself is due to an unsafe assumption in the -Wrestrict checker, the excessive upper bound in the strcat case suggests an optimization opportunity that's missing from the tree-ssa-strlen pass: namely, to reduce the offset's upper bound by the length of the appended string. The attached patch adds this minor optimization.
Martin
PR tree-optimization/83642 - excessive strlen range after a strcat of non-empty string gcc/ChangeLog: PR tree-optimization/83642 * tree-ssa-strlen.c (handle_builtin_strcat): Set offset range. gcc/testsuite/ChangeLog: PR tree-optimization/83642 * gcc.dg/strlenopt-39.c: New test. diff --git a/gcc/testsuite/gcc.dg/strlenopt-39.c b/gcc/testsuite/gcc.dg/strlenopt-39.c new file mode 100644 index 0000000..b070b6b --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-39.c @@ -0,0 +1,84 @@ +/* PR tree-optimization/83642] excessive strlen range after a strcat + of non-empty string + { dg-do compile } + { dg-options "-O2 -fdump-tree-optimized" } */ + +void test_strcat_0_strlen_range (char *dst, char *src) +{ + __SIZE_TYPE__ n = __builtin_strlen (dst); + + __builtin_strcat (dst, ""); + __builtin_strcat (dst, src); + + if (n >= __PTRDIFF_MAX__) + __builtin_abort (); +} + +void test_strcat_1_strlen_range (char *dst, char *src) +{ + __SIZE_TYPE__ n = __builtin_strlen (dst); + + __builtin_strcat (dst, "1"); + __builtin_strcat (dst, src); + + if (n >= __PTRDIFF_MAX__ - 1) + __builtin_abort (); +} + +void test_strcat_2_strlen_range (char *dst, char *src) +{ + __SIZE_TYPE__ n = __builtin_strlen (dst); + + __builtin_strcat (dst, "12"); + __builtin_strcat (dst, src); + + if (n >= __PTRDIFF_MAX__ - 2) + __builtin_abort (); +} + +void test_strcat_3_strlen_range (char *dst, char *src) +{ + __SIZE_TYPE__ n = __builtin_strlen (dst); + char a[] = "123"; + + __builtin_strcat (dst, a); + __builtin_strcat (dst, src); + + if (n >= __PTRDIFF_MAX__ - 3) + __builtin_abort (); +} + +void test_stpcpy_7_strlen_range (char *dst, char *src) +{ + __SIZE_TYPE__ n = __builtin_strlen (dst); + + __builtin_stpcpy (dst + n, "1234567"); + __builtin_strcat (dst, src); + + if (n >= __PTRDIFF_MAX__ - 7) + __builtin_abort (); +} + +void test_strcpy_8_strlen_range (char *dst, char *src) +{ + __SIZE_TYPE__ n = __builtin_strlen (dst); + + __builtin_strcpy (dst + n, "12345678"); + __builtin_strcat (dst, src); + + if (n >= __PTRDIFF_MAX__ - 8) + __builtin_abort (); +} + +void test_memcpy_9_strlen_range (char *dst, char *src) +{ + __SIZE_TYPE__ n = __builtin_strlen (dst); + + __builtin_memcpy (dst + n, "123456789", 10); + __builtin_strcat (dst, src); + + if (n >= __PTRDIFF_MAX__ - 9) + __builtin_abort (); +} + +/* { dg-final { scan-tree-dump-not "abort \\(\\)" "optimized" } } */ diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c index be6ab9f..9e42232 100644 --- a/gcc/tree-ssa-strlen.c +++ b/gcc/tree-ssa-strlen.c @@ -2486,10 +2486,35 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi) if (endptr) dst = fold_convert_loc (loc, TREE_TYPE (dst), unshare_expr (endptr)); else - dst = fold_build2_loc (loc, POINTER_PLUS_EXPR, - TREE_TYPE (dst), unshare_expr (dst), - fold_convert_loc (loc, sizetype, - unshare_expr (dstlen))); + { + if (TREE_CODE (dstlen) == PLUS_EXPR + && TREE_CODE (TREE_OPERAND (dstlen, 0)) == SSA_NAME + && TREE_CODE (TREE_OPERAND (dstlen, 1)) == INTEGER_CST) + { + /* For a call to strcat (DST, SRC) where DST = D + OFFSET and + where OFFSET = VAROFF + CSTOFF, adjust the upper bound of + VAROFF's range by the constant CSTOFF so that the result + is still guaranteed to be less than PTRDIFF_MAX, the size + of the longest possible string. */ + tree varoff = TREE_OPERAND (dstlen, 0); + wide_int minoff, maxoff; + value_range_type rng = get_range_info (varoff, &minoff, &maxoff); + + if (rng == VR_RANGE) + { + tree cstoff = TREE_OPERAND (dstlen, 1); + + maxoff -= wi::to_wide (cstoff); + set_range_info (varoff, rng, minoff, maxoff); + } + } + + dst = fold_build2_loc (loc, POINTER_PLUS_EXPR, + TREE_TYPE (dst), unshare_expr (dst), + fold_convert_loc (loc, sizetype, + unshare_expr (dstlen))); + } + dst = force_gimple_operand_gsi (gsi, dst, true, NULL_TREE, true, GSI_SAME_STMT); if (dump_file && (dump_flags & TDF_DETAILS) != 0)