This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Functions that are CSEable but not pure


On Thu, Oct 4, 2012 at 7:15 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Thu, Oct 04, 2012 at 07:08:02PM +0200, Richard Guenther wrote:
>> 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;
>> }
>
> The above isn't a singleton function, as you are clearing initialized,
> therefore it doesn't have the properties the thread_local var get_address
> wrapper has.  The singleton function really is
> void singleton (void)
> {
>   static __thread bool initialized;
>   if (!initialized) {
>     initialized = true;
>     call_some_function_that_may_modify_memory ();
>   }
> }
> and has side effects just first time in a thread.

So I suppose the testcase that would be "valid" but break with using
pure would be instead

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 main()
{
  int x = init_count;
  int *p = get_me();
  if (init_count == x)
    __builtin_abort();
  int *q = get_me();
  if (init_count == x)
    __builtin_abort();
}

here when get_me is pure we CSE init_count over the _first_ call of get_me.
Technically we only might CSE it over the second call of get_me.  Thus there
is flow-sensitive information coming out of nowhere when we try to skip
the second get_me () call in looking up the init_count load after it (coming
out of nowhere for the alias-oracle query which only includes 'init_count'
and the stmt 'int *q = get_me ()').

Thus my answer is, for the alias-oracle the hypothetical 'singleton_init'
attribute provides no useful information.  Thus CSE and/or DCE of
'singleton_init'
calls has to use special code (which I can think of being added to DCE, harder
to existing SCCVN, maybe easier to DOM) which will usually not fit into
the algorithm (so you might be able to teach FRE elimination of how to
elminiate the 2nd call to get_me () but you will need a 2nd FRE pass to
then figure out that init_count can be CSEd as well - which makes this
all very undesirable).  Inlining will also make this harder (you lose
the attribute and fall into the lucas situation ...).

At some point this "singleton"-ness should be auto-detected (there is
inlined code in SPEC 2k lucas that would benefit from constant propagation,
and SPEC 2k6 libquantum that uses a related concept - change / restore
of a global in a function to the net effect that the function should not be
considered clobbering it).

Richard.

>         Jakub


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]