Martin Sebor <[email protected]> writes:
> On 08/24/2017 08:03 AM, Richard Biener wrote:
>> On Wed, Aug 23, 2017 at 9:42 PM, Martin Sebor <[email protected]> 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