Richard Sandiford <richard.sandif...@linaro.org> writes:
> Martin Sebor <mse...@gmail.com> writes:
>> 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);
>> +        }
>> +    }
>
> I'm probably missing the point, but it seems unlikely that we can
> unconditionally subtract CSTOFF from whatever the current recorded
> maximum of VAROFF happens to be.  Shouldn't it be a maximum of
> the current maxoff and PTRDIFF_MAX-cstoff instead?

er, of course I meant minimum :-)

Reply via email to