[PR tree-optimization/67955] Exploit PTA in DSE

Jeff Law law@redhat.com
Mon Jan 9 17:02:00 GMT 2017


On 01/09/2017 02:36 AM, Richard Biener wrote:
>>
>>
>> a = 1;
>> <trigger non-call exception>
>> a = 2;
>>
>>
>> If "a" escapes such that its value can be queried in the exception handler,
>> then the exception handler would be able to observe the first store and thus
>> it should not be removed.
>
> Yes, and it won't as long as the EH is thrown internally (and thus we have
> a CFG reflecting it).  When it's only externally catched we lose of course...
>
> We'd need an Ada testcase to actually show behavior that is not conforming
> to an existing language specification though.
I'm not versed enough in Ada to even attempt to pull together a testcase 
for this.

>
> I suspect we have a similar issue in C++ for sth like
>
> void __attribute__((const)) foo () { throw; }
>
> int x;
> void bar ()
> {
>   x = 1;
>   foo ();
>   x = 2;
> }
>
> where foo is const but not nothrow.
I wouldn't be surprised if there's other problems with const functions 
that can throw.

>
>> We also have to be cognizant of systems where there is memory mapped at
>> location 0.  When that is true, we must check pt.null and honor it, even if
>> it pessimizes code.
>
> With -fno-delete-null-pointer-checks (that's what such systems set) PTA computes
> 0 as "nonlocal" and thus it won't be a singleton points-to solution.
Ah, good.

>
>>
>>
>>> For
>>>
>>> int foo (int *p, int b)
>>> {
>>>   int *q;
>>>   int i = 1;
>>>   if (b)
>>>     q = &i;
>>>   else
>>>     q = (void *)0;
>>>   *q = 2;
>>>   i = 3;
>>>   return *q;
>>> }
>>
>> So on a system where *0 is a valid memory address, *q = 2 does not make
>> anything dead, nor does i = 3 unless we were to isolate the THEN/ELSE
>> blocks.
>>
>> On a system where *0 traps, there is no way to observe the value of "i" in
>> the handler.  Thus i = 1 is a dead store.  I believe we must keep the *q = 2
>> store because it can trigger a signal/exception which is itself an
>> observable side effect?  Right?
>
> But writing to 0 invokes undefined behavior which we have no obligation to
> preserve (unless we make it well-defined with -fnon-call-exceptions -fexceptions
> as a GCC extension).
It may invoke undefined behavior, but to date we have explicitly chosen 
to preserve the *0 = <something> to trigger a fault via the virtual 
memory system.  We kicked this around extensively in Nov 2013 with the 
introduction of isolation of erroneous paths.

I think part of what pushed us that direction was that a program could 
catch the signal related to the NULL dereference, then do something 
sensible in the handler.  That also happens to match what the Go 
language requires.



>
>>>
>>> we remove all stores but the last store to i and the load from q (but we
>>> don't
>>> replace q with &i here, a missed optimization if removing the other stores
>>> is
>>> valid).
>>
>> But if we remove the *q = 2 store, we remove an observable side effect, the
>> trap/exception itself if we reach that statement via the ELSE path.
>
> As said above - I don't think we have to care for C/C++ w/o
> -fnon-call-exceptions.
So in the immediate term I propose we conditionalize the pt.null check 
on non-call exceptions.  THen I'll look more closely at the example 
above and see what we can reasonably do there.

jeff



More information about the Gcc-patches mailing list