VRP: undefined shifting calculation should not need sign bit

Aldy Hernandez aldyh@redhat.com
Wed Oct 17 14:23:00 GMT 2018



On 10/17/18 6:52 AM, Richard Sandiford wrote:
> Aldy Hernandez <aldyh@redhat.com> writes:
>> On 9/13/18 3:33 AM, Richard Sandiford wrote:
>>> Aldy Hernandez <aldyh@redhat.com> writes:
>>>> On 09/12/2018 12:57 PM, Richard Sandiford wrote:
>>>>> Aldy Hernandez <aldyh@redhat.com> writes:
>>>>>> diff --git a/gcc/wide-int-range.h b/gcc/wide-int-range.h
>>>>>> index 589fdea4df6..e9ee418e5b2 100644
>>>>>> --- a/gcc/wide-int-range.h
>>>>>> +++ b/gcc/wide-int-range.h
>>>>>> @@ -131,7 +131,7 @@ extern bool wide_int_range_div (wide_int &wmin, wide_int &wmax,
>>>>>>     /* Return TRUE if shifting by range [MIN, MAX] is undefined behavior.  */
>>>>>>     
>>>>>>     inline bool
>>>>>> -wide_int_range_shift_undefined_p (signop sign, unsigned prec,
>>>>>> +wide_int_range_shift_undefined_p (unsigned prec,
>>>>>>     				  const wide_int &min, const wide_int &max)
>>>>>>     {
>>>>>>       /* ?? Note: The original comment said this only applied to
>>>>>> @@ -142,7 +142,7 @@ wide_int_range_shift_undefined_p (signop sign, unsigned prec,
>>>>>>          behavior from the shift operation.  We cannot even trust
>>>>>>          SHIFT_COUNT_TRUNCATED at this stage, because that applies to rtl
>>>>>>          shifts, and the operation at the tree level may be widened.  */
>>>>>> -  return wi::lt_p (min, 0, sign) || wi::ge_p (max, prec, sign);
>>>>>> +  return wi::sign_mask (min) || wi::ge_p (max, prec, UNSIGNED);
>>>>>
>>>>> I don't think this is a good idea.  Logically the comparison should
>>>>> be done relative to the TYPE_SIGN of the type, so I think the original
>>>>> code was correct.
>>>>
>>>> The operation to calculate undefinedness must be done with the type of
>>>> the RHS, as opposed to the type of the entire operation.  This can be
>>>> confusing, as most operations use the same type for all operands as well
>>>> as for the type of the entire operation.  For example, AFAICT, the
>>>> following is valid gimple:
>>>>
>>>> 	UINT64 = UINT64 << INT32
>>>>
>>>> The original code was doing this (correctly), but since it was confusing
>>>> to remember which type to pass, I rewrote the above function to not need
>>>> the sign of the RHS.  This came about because in my ranger work, I
>>>> passed the wrong type which took forever to find ;-).  My patch avoids
>>>> further confusion.
>>>>
>>>> Am I missing a subtle incorrectness in my approach?
>>>
>>> The problem is with things like UINT256 << UINT8 vs. UINT256 << INT8.
>>> A range of [128, 131] on the UINT8 would be represented using the same
>>> wide_ints as a range of [-128, -125] on the INT8, but the former is
>>> well-defined while the latter isn't.  Only the TYPE_SIGN tells you
>>> which applies.
>>>
>>> The original code got this right, but the new code effectively assumes
>>> all shift amounts are signed, and so would treat UINT8 like INT8.
>>>
>>> OK, so no current target actually supports UINT256 AFAIK, so it might
>>> be academic.  But the original point of wide-int.h was to support such
>>> wide types, so they could become a thing in future.
>>>
>>> Thanks,
>>> Richard
>>>
>>
>> As promised.  Here is the reversal of the bits you suggested.
> 
> Thanks!
> 
>> I think this is an obvious patch, but would appreciate a sanity peek.
>>
>> Aldy
>>
>> commit 38a335af9aa5d72778fbacba247ab2219672da7b
>> Author: Aldy Hernandez <aldyh@redhat.com>
>> Date:   Wed Oct 17 11:25:21 2018 +0200
>>
>>              * wide-int-range.h (wide_int_range_shift_undefined_p): Adjust to
>>              use sign as argument.
>>              * tree-vrp.c (extract_range_from_binary_expr_1): Pass sign to
>>              wide_int_range_shift_undefined_p.
>>
>> diff --git a/gcc/tree-vrp.c b/gcc/tree-vrp.c
>> index cbc2ea2f26b..c519613bb28 100644
>> --- a/gcc/tree-vrp.c
>> +++ b/gcc/tree-vrp.c
>> @@ -1521,7 +1521,8 @@ extract_range_from_binary_expr_1 (value_range *vr,
>>   	   || code == LSHIFT_EXPR)
>>       {
>>         if (range_int_cst_p (&vr1)
>> -	  && !wide_int_range_shift_undefined_p (prec,
>> +	  && !wide_int_range_shift_undefined_p (TYPE_SIGN (TREE_TYPE (vr1.min)),
>> +						prec,
>>   						wi::to_wide (vr1.min),
>>   						wi::to_wide (vr1.max)))
>>   	{
>> diff --git a/gcc/wide-int-range.h b/gcc/wide-int-range.h
>> index e9ee418e5b2..589fdea4df6 100644
>> --- a/gcc/wide-int-range.h
>> +++ b/gcc/wide-int-range.h
>> @@ -131,7 +131,7 @@ extern bool wide_int_range_div (wide_int &wmin, wide_int &wmax,
>>   /* Return TRUE if shifting by range [MIN, MAX] is undefined behavior.  */
>>   
>>   inline bool
>> -wide_int_range_shift_undefined_p (unsigned prec,
>> +wide_int_range_shift_undefined_p (signop sign, unsigned prec,
>>   				  const wide_int &min, const wide_int &max)
> 
> Need to add SIGN back to the comment, maybe something like:
> 
>    /* Return TRUE if shifting by range [MIN, MAX] is undefined behavior,
>       interpreting MIN and MAX according to SIGN.  */
> 
> (or whatever you think's best).

Done.

Committed.

Thanks.
Aldy



More information about the Gcc-patches mailing list