On 6/3/19 7:13 AM, Aldy Hernandez wrote:
> On 5/31/19 5:00 AM, Richard Biener wrote:
>> On Fri, May 31, 2019 at 2:27 AM Jeff Law <l...@redhat.com> wrote:
>>>
>>> On 5/29/19 10:20 AM, Aldy Hernandez wrote:
>>>> On 5/29/19 12:12 PM, Jeff Law wrote:
>>>>> On 5/29/19 9:58 AM, Aldy Hernandez wrote:
>>>>>> On 5/29/19 9:24 AM, Richard Biener wrote:
>>>>>>> On Wed, May 29, 2019 at 2:18 PM Aldy Hernandez <al...@redhat.com>
>>>>>>> wrote:
>>>>>>>>
>>>>>>>> As per the API, and the original documentation to value_range,
>>>>>>>> VR_UNDEFINED and VR_VARYING should never have equivalences. 
>>>>>>>> However,
>>>>>>>> equiv_add is tacking on equivalences blindly, and there are various
>>>>>>>> regressions that happen if I fix this oversight.
>>>>>>>>
>>>>>>>> void
>>>>>>>> value_range::equiv_add (const_tree var,
>>>>>>>>                            const value_range *var_vr,
>>>>>>>>                            bitmap_obstack *obstack)
>>>>>>>> {
>>>>>>>>       if (!m_equiv)
>>>>>>>>         m_equiv = BITMAP_ALLOC (obstack);
>>>>>>>>       unsigned ver = SSA_NAME_VERSION (var);
>>>>>>>>       bitmap_set_bit (m_equiv, ver);
>>>>>>>>       if (var_vr && var_vr->m_equiv)
>>>>>>>>         bitmap_ior_into (m_equiv, var_vr->m_equiv);
>>>>>>>> }
>>>>>>>>
>>>>>>>> Is this a bug in the documentation / API, or is equiv_add incorrect
>>>>>>>> and
>>>>>>>> we should fix the fall-out elsewhere?
>>>>>>>
>>>>>>> I think this must have been crept in during the classification. 
>>>>>>> If you
>>>>>>> go back to say GCC 7 you shouldn't see value-ranges with
>>>>>>> UNDEFINED/VARYING state in the lattice that have equivalences.
>>>>>>>
>>>>>>> It may not be easy to avoid with the new classy interface but we're
>>>>>>> certainly not tacking on them "blindly".  At least we're not
>>>>>>> supposed
>>>>>>> to.  As usual the intermediate state might be "broken" but
>>>>>>> intermediateness is not sth the new class "likes".
>>>>>>
>>>>>> It looks like extract_range_from_stmt (by virtue of
>>>>>> vrp_visit_assignment_or_call and then extract_range_from_ssa_name)
>>>>>> returns one of these intermediate ranges.  It would seem to me
>>>>>> that an
>>>>>> outward looking API method like vr_values::extract_range_from_stmt
>>>>>> shouldn't be returning inconsistent ranges.  Or are there no
>>>>>> guarantees
>>>>>> for value_ranges from within all of vr_values?
>>>>> ISTM that if we have an implementation constraint that says a
>>>>> VR_VARYING
>>>>> or VR_UNDEFINED range can't have equivalences, then we need to honor
>>>>> that at the minimum for anything returned by an external API. 
>>>>> Returning
>>>>> an inconsistent state is bad.  I'd even state that we should try damn
>>>>> hard to avoid it in internal APIs as well.
>>>>
>>>> Agreed * 2.
>>>>
>>>>>
>>>>>>
>>>>>> Perhaps I should give a little background.  As part of your
>>>>>> value_range_base re-factoring last year, you mentioned that you
>>>>>> didn't
>>>>>> split out intersect like you did union because of time or
>>>>>> oversight.  I
>>>>>> have code to implement intersect (attached), for which I've
>>>>>> noticed that
>>>>>> I must leave equivalences intact, even when transitioning to
>>>>>> VR_UNDEFINED:
>>>>>>
>>>>>> [from the attached patch]
>>>>>> +  /* If THIS is varying we want to pick up equivalences from OTHER.
>>>>>> +     Just special-case this here rather than trying to fixup
>>>>>> after the
>>>>>> +     fact.  */
>>>>>> +  if (this->varying_p ())
>>>>>> +    this->deep_copy (other);
>>>>>> +  else if (this->undefined_p ())
>>>>>> +    /* ?? Leave any equivalences already present in an undefined.
>>>>>> +       This is technically not allowed, but we may get an in-flight
>>>>>> +       value_range in an intermediate state.  */
>>>>> Where/when does this happen?
>>>>
>>>> The above snippet is not currently in mainline.  It's in the patch I'm
>>>> proposing to clean up intersect.  It's just that while cleaning up
>>>> intersect I noticed that if we keep to the value_range API, we end up
>>>> clobbering an equivalence to a VR_UNDEFINED that we depend up
>>>> further up
>>>> the call chain.
>>>>
>>>> The reason it doesn't happen in mainline is because intersect_helper
>>>> bails early on an undefined, thus leaving the problematic equivalence
>>>> intact.
>>>>
>>>> You can see it in mainline though, with the following testcase:
>>>>
>>>> int f(int x)
>>>> {
>>>>    if (x != 0 && x != 1)
>>>>      return -2;
>>>>
>>>>    return !x;
>>>> }
>>>>
>>>> Break in evrp_range_analyzer::record_ranges_from_stmt() and see that
>>>> the
>>>> call to extract_range_from_stmt() returns a VR of undefined *WITH*
>>>> equivalences:
>>>>
>>>>        vr_values->extract_range_from_stmt (stmt, &taken_edge,
>>>> &output, &vr);
>>>>
>>>> This VR is later fed to update_value_range() and ultimately
>>>> intersect(),
>>>> which in mainline, leaves the equivalences alone, but IMO should
>>>> respect
>>>> that API and nuke them.
>>> So I think this is coming from extract_range_from_ssa_name:
>>>
>>>> void
>>>> vr_values::extract_range_from_ssa_name (value_range *vr, tree var)
>>>> {
>>>>    value_range *var_vr = get_value_range (var);
>>>>
>>>>    if (!var_vr->varying_p ())
>>>>      vr->deep_copy (var_vr);
>>>>    else
>>>>      vr->set (var);
>>>>
>>>>    vr->equiv_add (var, get_value_range (var), &vrp_equiv_obstack);
>>>> }
>>>
>>> Where we blindly add VAR to the equiv bitmap in the range.
>>>
>>> AFAICT gcc-7 has equivalent code, it's just not inside the class.
>>>
>>> I don't know what the fallout might be, but we could conditionalize the
>>> call to add_equivalence to avoid the inconsistent state.
>>
>> Well, the issue is that the equivalence _is_ there.  So above we
>> know that we never end up with VARYING but the deep_copy
>> can bring over UNDEFINED to us.  I guess we _could_ elide
>> the equiv adding then.  OTOH the equivalence does look
>> useful for predicate simplification which IIRC doesn't treat
>> UNDEFINED != x in a useful way.  Which would mean allowing
>> equivalences on UNDEFINED.  That somewhat makes sense
>> since UNDEFINED is bottom-most in the lattice and one up
>> (CONSTANT) we have equivalences while just on topmost
>> (VARYING) we do not.
>>
>> That said, I never liked equivalences - they are an odd way
>> to represent ranges intersected from multiple ranges thus
>> a way out of our too simplistic range representation.
>>
>> So lets fix this in a way avoiding testsuite fallout.  But please
>> not with special hacks in consumers.  Does guariding the
>> above equiv_add with !vr->undefined_p () fix things?
> 
> Agreed.  I never suggested we add special hacks to intersect.  The
> two-liner was only to explain why equivalences in varying/undefined
> currently matter in intersect.
> 
> Guarding the equiv_add causes regressions.  For example, for this simple
> testcase we incorrectly get rid of the final PHI:
> 
> int f(int x)
> {
>   if (x != 0 && x != 1)
>     return -2;
> 
>   return !x;
> }
> 
> In the evrp dump we now see:
> 
> Visiting PHI node: _3 = PHI <-2(3), _5(4)>
>     Argument #0 (3 -> 5 executable)
>         -2: int [-2, -2]
>     Argument #1 (4 -> 5 executable)
>         _5: UNDEFINED
> Meeting
>   int [-2, -2]
> and
>   UNDEFINED
> to
>   int [-2, -2]
> 
> whereas before the change, argument #1 was:
> 
>     Argument #1 (4 -> 5 executable)
>         _5: int [_5, _5]
> 
> and the meeting result was VARYING.
> 
> I *think* this starts somewhere up the call chain in update_value_range,
> which as I've described earlier, will no longer make a difference
> between UNDEFINED and UNDEFINED + equivalence.  This causes that when
> transitioning from UNDEFINED to UNDEFINED + equivalence, we actually
> transition to VARYING:
> 
>  if (is_new)
>     {
>       if (old_vr->varying_p ())
>     {
>       new_vr->set_varying ();
>       is_new = false;
>     }
>       else if (new_vr->undefined_p ())
>     {
>       old_vr->set_varying ();
>       new_vr->set_varying ();
>       return true;
>     }
> 
> Could someone better versed in this take a peek, please?  It's just a
> matter of applying the attached one-liner, and looking at "Visiting PHI"
> in the evrp dump.
As I mentioned in IRC.  I know what's going on here and see a path to
fix it.  Hoping to get a patch into my tester overnight, but life seems
to be getting in the way.

jeff

Reply via email to