On Thu, Oct 4, 2012 at 5:22 PM, Jason Merrill <[email protected]> wrote:
> On 10/04/2012 09:07 AM, Richard Guenther wrote:
>>
>> Ugh. Especially with the above (you can DCE those calls) makes this
>> severly mis-specified ... and any implementation error-prone (look what
>> mess our losely defined 'malloc' attribute opened ...).
>>
>> I thought of a testcase like
>>
>> int *p = get_me ();
>> .. = *p;
>> int *q = get_me ();
>> .. = *q;
>>
>> and get_me allocating/initalizing and returning a singleton.
>
>
> Right.
>
>
>> But you tell me it's more complicated and get_me () needs to
>> be a barrier for any load/store (as it may modify arbitrary memory,
>> but only on the "first" call).
>
>
> Yes, because the initialization is user-written code.
>
>
>> I think that "may modify arbitrary memory" isn't going to fly and
>> my answer would be, better don't try to optimize anything here,
>> at least not in generic terms. How would you handle this in
>> the alias oracle? How would you then make CSE recognize
>> two functions return the same value and are CSEable?
>
>
> For aliasing purposes, the call is like a call to a normal function. For CSE
> purposes, we want to recognize identical calls and combine them. I don't
> know the GCC bits well enough to be any more specific.
>
>
>> Can you come up with a short but complete testcase illustrating the issue
>> better (preferably C, so I don't need to read-in magic points where
>> construction
>> happens)?
>
>
> int init_count;
> int data;
>
> void init()
> {
> static int initialized;
> if (!initialized)
> {
> data = ++init_count;
> initialized = 1;
> }
> }
>
> inline int *get_me() __attribute ((pure));
> inline int *get_me()
> {
> init ();
> return &data;
> }
>
> int sink;
>
> int main()
> {
> sink = init_count;
> int *p = get_me();
> if (init_count != 1)
> __builtin_abort();
> int *q = get_me();
> if (init_count != 1)
> __builtin_abort();
> return *p + *q;
> }
>
> On this testcase, gcc -O2 doesn't reload init_count after the call to get_me
> because it thinks that the call can't have modified init_count. I want the
> compiler to know that it is safe to discard the redundant assignment, but
> not make assumptions about memory.
But isn't it a fact that it _cannot_ modify init_count? If the second call
is CSEable then it cannot have side-effects that are observable at
the call site. Is the following an example you would consider to fall
under your CSEing?
int init_count;
int data;
int initialized;
void init()
{
if (!initialized)
{
data = ++init_count;
initialized = 1;
}
}
inline int *get_me() __attribute ((pure));
inline int *get_me()
{
init ();
return &data;
}
int sink;
int main()
{
sink = init_count;
int *p = get_me();
if (init_count != 1)
__builtin_abort();
initialized = 0;
int *q = get_me();
if (init_count != 2)
__builtin_abort();
return *p + *q;
}
? If so, then why can we assume get_me returns the same pointer even here?
Richard.
>
> On 10/04/2012 08:59 AM, Jakub Jelinek wrote:> On Thu, Oct 04, 2012 at
> 08:56:03AM -0400, Jason Merrill wrote:
>> Sure, but I thought you want to inline the wrapper function as soon as
>> possible. Or do you want to keep it as some kind of builtin that gets
>> expanded during expansion to the test and call?
>
> Ah, I see your point; if get_me is inlined we end up with two calls to init,
> so it would be good to mark init with the same hypothetical attribute.
>
> Jason
>