On 6/4/19 5:23 AM, Richard Biener wrote:
> On Tue, Jun 4, 2019 at 12:30 AM Jeff Law <l...@redhat.com> wrote:
>>
>> 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.
> 
> It's of course a latent issue.  One I fixed in DOMs use of the EVRP machinery.
> The following fixes it in a conservative way:
> 
> Index: gcc/gimple-ssa-evrp.c
> ===================================================================
> --- gcc/gimple-ssa-evrp.c       (revision 271904)
> +++ gcc/gimple-ssa-evrp.c       (working copy)
> @@ -175,6 +175,8 @@ evrp_dom_walker::before_dom_children (ba
> 
>        /* Try folding stmts with the VR discovered.  */
>        bool did_replace = evrp_folder.replace_uses_in (stmt);
> +      gimple_stmt_iterator prev_gsi = gsi;
> +      gsi_prev (&prev_gsi);
>        if (fold_stmt (&gsi, follow_single_use_edges)
>           || did_replace)
>         {
> @@ -191,6 +193,17 @@ evrp_dom_walker::before_dom_children (ba
> 
>        if (did_replace)
>         {
> +         /* If we wound up generating new stmts during folding
> +            drop all their defs to VARYING.
> +            ???  Properly process them.  */
> +         if (gsi_end_p (prev_gsi))
> +           prev_gsi = gsi_start_bb (bb);
> +         while (gsi_stmt (prev_gsi) != gsi_stmt (gsi))
> +           {
> +             evrp_range_analyzer.get_vr_values ()
> +               ->set_defs_to_varying (gsi_stmt (prev_gsi));
> +             gsi_next (&prev_gsi);
> +           }
>           /* If we cleaned up EH information from the statement,
>              remove EH edges.  */
>           if (maybe_clean_or_replace_eh_stmt (old_stmt, stmt))
> 
> the more "proper" fix requires keeping track of stmts already
> processed (you don't want to re-process stmts, I ran into
> "issues" when doing that for the same issue in DOM).  For
> DOM I used the visited flag (see dom_opt_dom_walker::before_dom_children).
> 
> You might want to copy that handling.
> 
> Just give me a note if you want to work on that, otherwise I'll try to
> queue it for somewhen this week.Umm, I think you're working around a problem 
> in the simplifier code
which creates SSA_NAMEs, leaving them in an VR_UNDEFINED state.

jeff

Reply via email to