On 6/4/19 9:04 AM, Richard Biener wrote:
> On Tue, Jun 4, 2019 at 3:40 PM Jeff Law <l...@redhat.com> wrote:
>> 
>> 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.
> 
> But the simplifier is / can be just fold_stmt () - it doesn't know
> it has to populate a value-range lattice.  But yes, the VRP specific
> simplifier likely has the same issue in the EVRP context (in SSA VRP
> all "new" SSA names are beyond the static allocated array and get
> VARYING automatically).  So I think handling the "new stmts" in the 
> propagator is correct?
I didn't realize we had so many calls to make_ssa_name in gimple-fold.
I was primarily concerned with the ones in the evrp simplification
engine which are easy to fix in-place.  Looking at gimple-fold.c I agree
we do need to address the walking problem.

I'm not sure we can drop to varying though -- my first twiddle of this
did precisely that, but that'll likely regress vrp47 where we know the
resultant range after simplification is just [0,1].

Ideally we wouldn't have the simplifiers creating new names or we'd have
a more robust mechanism for identifying when we've created a new name
and doing the right thing.  I suspect whatever we do here right now is
going to be a bandaid and as long as we keep creating new names in the
simplifier we're likely going to be coming back and applying more bandaids.

jeff


Reply via email to