On 5/31/19 9:40 AM, Andrew MacLeod wrote: > On 5/29/19 7:15 AM, Richard Biener wrote: >> On Tue, May 28, 2019 at 4:17 PM Andrew MacLeod <amacl...@redhat.com> >> wrote: >>> On 5/27/19 9:02 AM, Richard Biener wrote: >>>> On Fri, May 24, 2019 at 5:50 PM Andrew MacLeod <amacl...@redhat.com> >>>> wrote: >>>>>> The above suggests that iff this is done at all it is not in GORI >>>>>> because >>>>>> those are not conditional stmts or ranges from feeding those. The >>>>>> machinery doing the use-def walking from stmt context also cannot >>>>>> come along these so I have the suspicion that Ranger cannot handle >>>>>> telling us that for the stmt following above, for example >>>>>> >>>>>> if (_5 != 0) >>>>>> >>>>>> that _5 is not zero? >>>>>> >>>>>> Can you clarify? >>>>> So there are 2 aspects to this. the range-ops code for DIV_EXPR, if >>>>> asked for the range of op2 () would return ~[0,0] for _5. >>>>> But you are also correct in that the walk backwards would not find >>>>> this. >>>>> >>>>> This is similar functionality to how null_derefs are currently >>>>> handled, >>>>> and in fact could probably be done simultaneously using the same code >>>>> base. I didn't bring null derefs up, but this is a good time :-) >>>>> >>>>> There is a separate class used by the gori-cache which tracks the >>>>> non-nullness property at the block level. It has a single API: >>>>> non_null_deref_p (name, bb) which determines whether the is a >>>>> dereference in any BB for NAME, which indicates whether the range >>>>> has an >>>>> implicit ~[0,0] range in that basic block or not. >>>> So when we then have >>>> >>>> _1 = *_2; // after this _2 is non-NULL >>>> _3 = _1 + 1; // _3 is non-NULL >>>> _4 = *_3; >>>> ... >>>> >>>> when a on-demand user asks whether _3 is non-NULL at the >>>> point of _4 = *_3 we don't have this information? Since the >>>> per-BB caching will only say _1 is non-NULL after the BB. >>>> I'm also not sure whether _3 ever gets non-NULL during >>>> non-NULL processing of the block since walking immediate uses >>>> doesn't really help here? >>> presumably _3 is globally non-null due to the definition being (pointer >>> + x) ... ie, _3 has a global range o f ~[0,0] ? >> No, _3 is ~[0, 0] because it is derived from _1 which is ~[0, 0] and >> you cannot arrive at NULL by pointer arithmetic from a non-NULL pointer. > > I'm confused. > > _1 was loaded from _2 (thus asserting _2 is non-NULL). but we have no > idea what the range of _1 is, so how do you assert _1 is [~0,0] ? > The only way I see to determine _3 is non-NULL is through the _4 = *_3 > statement. Likewise. I don't see how we get ~[0,0] for _1, except at the point after the dereference of _3.
>>>> So this seems to be a fundamental limitation [to the caching scheme], >>>> not sure if it is bad in practice. >>>> >>>> Or am I missing something? >>>> >>> Not missing anything The non-nullness property is maintains globally at >>> the basic block level. both _1 and _3 are flagged as being non-null in >>> the block. Upon exit, its a bit check. If the global information does >>> not have the non-nullness property, then when a request is made for >>> non-nullness and the def and the use are both within the same block, >>> and its flagged as being non-null in that block, then the request is >>> forced back to a quick walk between the def and the use to see if there >>> is any non-nulless introduced in between. Yes, that makes it a linear >>> walk, but its infrequent, and often short. to the best of our knowledge >>> at this point anyway :-) >> So with the clarification above do we ever see that _3 is non-NULL? >> I suppose the worker processing _3 = _1 + 1 would ask for >> _1 non-nullness but we do not record any non-NULL-ness of _1 in >> this basic-block (but only at its end). Consider stmts >> >> _4 = (uintptr_t) _2; >> _5 = _6 / _4; >> _1 = *_2; >> ... >> >> here at _1 we know _2 is not NULL. But if we ask for non-NULLness >> of _2 at the definition of _4 we may not compute ~[0, 0] and thus >> conclude that _6 / _4 does not trap. > EVRP must look backwards to figure this out since the forward walk will > process _5 = _6 / _4 before it sees the dereference to _2... so how does > it know that _4 is non-zero without looking backwards at things after it > sees the dereference?? Does it actually do this? During the forward walk we process the assignment to _5, which is _6 / _4. We can infer that _4 is nonzero because division by zero is undefined behavior. But I'm not sure how EVRP would go back and then make a determination about _4 unless it's doing so via an equivalence. > >> >> stmt-level tracking of ranges are sometimes important. This is >> something the machinery cannot provide - correct? At least not >> optimistically enough with ranges derived about uses. > > Maybe I'm the one missing something, but in the absence of statement > level exception throwing via 'can_throw_non_call_exceptions' being true, > any assertion made anywhere in the block to an ssa_name applies to the > entire block does it not? ie it doesn't matter if the deference > happens first thing in the block or last thing, its not going to change > its value within the block.. its going to be non-null throughout the > entire block. No, I don't think it can hold for the entire block. Consider x = p ? 10 : 20; foo (x) *p = whatever We don't know p is non-null because foo might not return. If by way of the dereference we were to assert p is non-null for the entire block, then we'd pass the wrong value to foo(). Jeff