Martin Sebor <mse...@gmail.com> writes:
> On 08/24/2017 08:03 AM, Richard Biener wrote:
>> On Wed, Aug 23, 2017 at 9:42 PM, Martin Sebor <mse...@gmail.com> wrote:
>>> Bug 81908 is about a -Wstringop-overflow warning for a Fortran
>>> test triggered by a recent VRP improvement.  A simple test case
>>> that approximates the warning is:
>>>
>>>   void f (char *d, const char *s, size_t n)
>>>   {
>>>     if (n > 0 && n <= SIZE_MAX / 2)
>>>       n = 0;
>>>
>>>     memcpy (d, s, n);   // n in ~[1, SIZE_MAX / 2]
>>>   }
>>>
>>> Since the only valid value of n is zero the call to memcpy can
>>> be considered a no-op (a value of n > SIZE_MAX is in excess of
>>> the maximum size of the largest object and would surely make
>>> the call crash).
>>>
>>> The important difference between the test case and Bug 81908
>>> is that in the latter, the code is emitted by GCC itself from
>>> what appears to be correct source (looks like it's the result
>>> of the loop distribution pass).  I believe the warning for
>>> the test case above and for other human-written code like it
>>> is helpful, but warning for code emitted by GCC, even if it's
>>> dead or unreachable, is obviously not (at least not to users).
>>>
>>> The attached patch enhances the gimple_fold_builtin_memory_op
>>> function to eliminate this patholohgical case by making use
>>> of range information to fold into no-ops calls to memcpy whose
>>> size argument is in a range where the only valid value is zero.
>>> This gets rid of the warning and improves the emitted code.
>>>
>>> Tested on x86_64-linux.
>>
>> @@ -646,6 +677,12 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator 
>> *gsi,
>>    tree destvar, srcvar;
>>    location_t loc = gimple_location (stmt);
>>
>> +  if (tree valid_len = only_valid_value (len))
>> +    {
>> +      /* LEN is in range whose only valid value is zero.  */
>> +      len = valid_len;
>> +    }
>> +
>>    /* If the LEN parameter is zero, return DEST.  */
>>    if (integer_zerop (len))
>>
>> just enhance this check to
>>
>>   if (integer_zerop (len)
>>       || size_must_be_zero_p (len))
>>
>> ?  'only_valid_value' looks too generic for this.
>
> Sure.
>
> FWIW, the reason I did it this was because my original patch
> returned error_mark_node for entirely invalid ranges and had
> the caller replace the call with a trap.  I decided not to
> include that part in this fix to keep it contained.
>
>>
>> +  if (!wi::fits_uhwi_p (min) || !wi::fits_uhwi_p (max))
>> +    return NULL_TREE;
>> +
>>
>> why?
>
> Only because I never remember what APIs are safe to use with
> what input.
>
>> +  if (wi::eq_p (min, wone)
>> +      && wi::geu_p (max + 1, ssize_max))
>>
>>    if (wi::eq_p (min, 1)
>>       && wi::gtu_p (max, wi::max_value (prec, SIGNED)))
>>
>> your ssize_max isn't signed size max, and max + 1 might overflow to zero.
>
> You're right that the addition to max would be better done
> as subtraction from the result of (1 << N).  Thank you.
>
> If (max + 1) overflowed then (max == TYPE_MAX) would have
> to hold which I thought could never be true for an anti
> range. (The patch includes tests for this case.)  Was I
> wrong?
>
> Attached is an updated version with the suggested changes
> plus an additional test to verify the absence of warnings.
>
> Martin
>
> PR middle-end/81908 - FAIL: gfortran.dg/alloc_comp_auto_array_2.f90 -O3 -g 
> -m32
>
> gcc/ChangeLog:
>
>       PR middle-end/81908
>       * gimple-fold.c (size_must_be_zero_p): New function.
>       (gimple_fold_builtin_memory_op): Call it.
>
> gcc/testsuite/ChangeLog:
>
>       PR middle-end/81908
>       * gcc.dg/tree-ssa/builtins-folding-gimple-2.c: New test.
>       * gcc.dg/tree-ssa/builtins-folding-gimple-3.c: New test.
>       * gcc.dg/tree-ssa/pr81908.c: New test.
>
> diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
> index 251446c..c4184fa 100644
> --- a/gcc/gimple-fold.c
> +++ b/gcc/gimple-fold.c
> @@ -628,6 +628,36 @@ var_decl_component_p (tree var)
>    return SSA_VAR_P (inner);
>  }
>  
> +/* If the SIZE argument representing the size of an object is in a range
> +   of values of which exactly one is valid (and that is zero), return
> +   true, otherwise false.  */
> +
> +static bool
> +size_must_be_zero_p (tree size)
> +{
> +  if (integer_zerop (size))
> +    return true;
> +
> +  if (TREE_CODE (size) != SSA_NAME)
> +    return false;
> +
> +  wide_int min, max;
> +  enum value_range_type rtype = get_range_info (size, &min, &max);
> +  if (rtype != VR_ANTI_RANGE)
> +    return false;
> +
> +  tree type = TREE_TYPE (size);
> +  int prec = TYPE_PRECISION (type);
> +
> +  wide_int wone = wi::one (prec);
> +
> +  /* Compute the value of SSIZE_MAX, the largest positive value that
> +     can be stored in ssize_t, the signed counterpart of size_t .  */
> +  wide_int ssize_max = wi::lshift (wi::one (prec), prec - 1) - 1;
> +
> +  return wi::eq_p (min, wone) && wi::geu_p (max, ssize_max);

Just a stylistic thing, but since the only use of "wone" is in
the eq_p, it'd be simpler just to use "1".  Also, the maximum
value is better calculated as "wi::max_value (prec, SIGNED)".  So:

  /* Compute the value of SSIZE_MAX, the largest positive value that
     can be stored in ssize_t, the signed counterpart of size_t .  */
  wide_int ssize_max = wi::max_value (prec, SIGNED);

  return wi::eq_p (min, 1) && wi::geu_p (max, ssize_max);

Thanks,
Richard

Reply via email to