[llvm-dev] RFC: New function attribute HasInaccessibleState

Vaivaswatha Nagaraj via llvm-dev llvm-dev at lists.llvm.org
Fri Dec 4 08:48:24 PST 2015


>What if you had bitcode versions of your C library
Then the definitions for these functions would be available. Would we still
set function attributes to these functions in FunctionAttrs.cpp if their
definitions were available?

>This also seems a bit tailored to malloc/free, and can't work for
user-defined allocation functions
I don't think so. For example, printf would have the flag set, preventing
two calls to printfs (without the return value being used) from being
interchanged. In the case of user-defined allocation functions, the
definitions for those functions are available, and what state they modify
is directly visible. I'm not sure I understand how malloc alone would be
more powerful. This point however reminds me to add, functions that
transitively call functions with HasInaccessibleState must also have the
flag set.

  - Vaivaswatha

On Fri, Dec 4, 2015 at 10:06 PM, James Molloy <james at jamesmolloy.co.uk>
wrote:

> Hi,
>
> I'm still a bit dubious about this, I don't think it's bombproof. How does
> this fit with LTO? What if you had bitcode versions of your C library (not
> entirely crazy - it might allow a lot of LTO) - you'd collapse those two
> domains into one in a rather messy way.
>
> This also seems a bit tailored to malloc/free, and can't work for
> user-defined allocation functions. Our current attributes mechanism has the
> ability to infer noalias on such functions, so here you'll be making malloc
> more powerful than user-defined functions.
>
> All in all it just smells a bit specialist. I'd welcome it if we could
> bombproof the semantics and extend the scope somewhat somehow.
>
> Cheers,
>
> James
>
> On Fri, 4 Dec 2015 at 16:31 Vaivaswatha Nagaraj via llvm-dev <
> llvm-dev at lists.llvm.org> wrote:
>
>> >there would be two disjoint global states
>> In some sense yes, but technically not disjoint. Functions marked with
>> this attribute should still be able to access the globals within the
>> program under compilation, if its not marked with ReadNone.
>>
>>
>> >If malloc and free can both use global variables (there is no notion of
>> library in the compiler)
>> Inaccessible state here refers to any global that is not visible to the
>> program under compilation. The key idea (behind the new attribute) is to
>> convey that these external functions do things inside that the compiler
>> cannot know about, and hence deal with them conservatively.
>>
>>   - Vaivaswatha
>>
>> On Fri, Dec 4, 2015 at 9:51 PM, Mehdi Amini <mehdi.amini at apple.com>
>> wrote:
>>
>>>
>>> On Dec 4, 2015, at 8:09 AM, Vaivaswatha Nagaraj <vn at compilertree.com>
>>> wrote:
>>>
>>> >what is a non-public state that no-one but you can access? (I’d call
>>> that private).
>>> malloc and free could both use global variables that are defined in
>>> libc, but are inaccessible to the program under compilation.
>>>
>>>
>>>
>>> If malloc and free can both use global variables (there is no notion of
>>> library in the compiler), then from what I understand you are actually
>>> creating another global state: i.e. there would be two disjoint global
>>> states: the usual default one and another one that is only accessed by
>>> function having this attribute.
>>>
>>>>>> Mehdi
>>>
>>>
>>>
>>>
>>> >if you’re attribute is saying they have some internal state, then
>>> malloc() cannot access the state of free() and vice versa.
>>> Which is why it would be preferable to call it "inaccessible" state
>>> rather than "internal".
>>>
>>> >It would prevent to swap two mallocs but not moving freely a malloc
>>> with respect to a free.
>>> No, it would also prevent interchanging the order of malloc and free,
>>> since they both maintain states (which can be shared, but not accessible to
>>> the program under compilation) and the swapping order could result in a
>>> different final state.
>>>
>>> >At the cost of some redundancy, I think a new attribute is needed.
>>> @hal. I'm not sure what this implies. Does the semantics of the
>>> attribute in the first mail sound right to you?
>>>
>>> > that'd be redefining the semantics of ReadNone. ReadNone allows
>>> elision of a call if its result is unused,
>>> @James.  That right. Optimizations should hereafter (if the proposed
>>> attribute is accepted) be more careful in interpreting ReadNone. If the
>>> call also has HasInaccessibleState, it shouldn't remove the call, even if
>>> the call takes no arguments or its return value isn't, because it could be
>>> modifying some internal state.
>>>
>>>
>>>   - Vaivaswatha
>>>
>>> On Fri, Dec 4, 2015 at 9:10 PM, Mehdi Amini <mehdi.amini at apple.com>
>>> wrote:
>>>
>>>>
>>>> On Dec 3, 2015, at 11:58 PM, Vaivaswatha Nagaraj <vn at compilertree.com>
>>>> wrote:
>>>>
>>>> >is this "internal state” supposed to be private to the function?
>>>> It could be private or not. Hence the name "inaccessible", to mean that
>>>> the program under compilation has no access to the state. So while printf
>>>> and malloc (for example) could share state in libc, the program under
>>>> compilation cannot access this state.
>>>>
>>>>
>>>> This is still not clear to me, you’re saying “it could be private or
>>>> not”: what is a non-public state that no-one but you can access? (I’d call
>>>> that private).
>>>>
>>>> Now, from the point of view of the compiler, malloc and free are two
>>>> separate functions, if you’re attribute is saying they have some internal
>>>> state, then malloc() cannot access the state of free() and vice versa.
>>>>
>>>> >how this flag would prevent the last “optimization” you’re illustrating
>>>> Assuming you are referring to the quoted examples, currently these
>>>> optimizations are not happening anyway (from what I understand). The issue
>>>> is that, after malloc/free are tagged with "ReadNone", such transforms may
>>>> happen. Hence to prevent that, the additional flag denoting that these
>>>> functions maintain an internal state.
>>>>
>>>>
>>>> I’m questioning why would this flag solve that, it does not seem to to
>>>> me. It would prevent to swap two mallocs but not moving freely a malloc
>>>> with respect to a free.
>>>>
>>>>>>>> Mehdi
>>>>
>>>>
>>>>
>>>>
>>>>   - Vaivaswatha
>>>>
>>>> On Fri, Dec 4, 2015 at 12:20 PM, Mehdi Amini <mehdi.amini at apple.com>
>>>> wrote:
>>>>
>>>>> Hi,
>>>>>
>>>>> On Dec 3, 2015, at 10:33 PM, Vaivaswatha Nagaraj via llvm-dev <
>>>>> llvm-dev at lists.llvm.org> wrote:
>>>>>
>>>>> Hi,
>>>>>
>>>>> This email is in continuation to the mail thread
>>>>> http://lists.llvm.org/pipermail/llvm-dev/2015-December/092996.html,
>>>>> to propose a new function attribute that can convey that a function
>>>>> maintains state, but this state is inaccessible to the rest of the program
>>>>> under compilation.
>>>>>
>>>>> Such a flag could be added to most libc/system calls such as
>>>>> printf/malloc/free. (libc and system calls do access/modify internal
>>>>> variables such as errno).
>>>>>
>>>>> Example attributes (in addition to what are already set):
>>>>> malloc/free: HasInaccessibleState, ReadNone
>>>>> printf: HasInaccessibleState, ArgMemOnly
>>>>> realloc: HasInaccessibleState, ReadOnly (not sure).
>>>>>
>>>>> The intention behind introducing this attribute is to relax the
>>>>> conditions in GlobalsAA as below:
>>>>> (this code is in GlobalsAAResult::AnalyzeCallGraph)
>>>>>
>>>>>        if (F->isDeclaration()) {
>>>>>          // Try to get mod/ref behaviour from function attributes.
>>>>> -        if (F->doesNotAccessMemory()) {
>>>>> +        if (F->doesNotAccessMemory() || F->onlyAccessesArgMemory()) {
>>>>>            // Can't do better than that!
>>>>>          } else if (F->onlyReadsMemory()) {
>>>>>            FunctionEffect |= Ref;
>>>>>            if (!F->isIntrinsic())
>>>>>              // This function might call back into the module and read a global -
>>>>>              // consider every global as possibly being read by this function.
>>>>>              FR.MayReadAnyGlobal = true;
>>>>>          } else {
>>>>>            FunctionEffect |= ModRef;
>>>>>            // Can't say anything useful unless it's an intrinsic - they don't
>>>>>            // read or write global variables of the kind considered here.
>>>>>            KnowNothing = !F->isIntrinsic();
>>>>>          }
>>>>>          continue;
>>>>>        }
>>>>>
>>>>> This relaxation allows functions that (transitively) call library
>>>>> functions (such as printf/malloc) to still maintain and propagate GlobalsAA
>>>>> info. In general, this adds more precision to the description of these
>>>>> functions.
>>>>>
>>>>> Concerns regarding impact on other optimizations (I'm repeating a few
>>>>> examples that Hal mentioned earlier).
>>>>>
>>>>> 1.
>>>>> >A readnone function is one whose output is a function only of its
>>>>> inputs, and if you have this:
>>>>> >
>>>>> >  int *x = malloc(4);
>>>>> >  *x = 2;
>>>>> >  int *y = malloc(4);
>>>>> >  *y = 4;
>>>>> > you certainly don't want EarlyCSE to replace the second call to
>>>>> malloc with the result of the first (which it will happily do if you mark
>>>>> malloc as readnone).
>>>>>
>>>>> For malloc, even though ReadNone is set now (as proposed above),
>>>>> EarlyCSE should be taught to respect the HasInaccessibleState and not
>>>>> combine functions that maintain internal states. Similarly other
>>>>> optimizations (such as DCE) must be taught to respect the flag.
>>>>>
>>>>> 2.
>>>>> >void foo(char * restrict s1, char * restrict s2) {
>>>>> >  printf(s1);
>>>>> >  printf(s2);
>>>>> >}
>>>>>
>>>>> >If printf is argmemonly, then we could interchange the two printf
>>>>> calls.
>>>>>
>>>>> In this example too, printf maintains an internal state, preventing
>>>>> the calls from being internchanged. Also, it is now correct to add
>>>>> ArgMemOnly to printf as it does not access any other program memory.
>>>>>
>>>>> 3.
>>>>> >For malloc this is still a problem, in the following sense, if we
>>>>> have:
>>>>> >
>>>>> >  p1 = malloc(really_big);
>>>>> >  ...
>>>>> >  free(p1);
>>>>> >
>>>>> > p2 = malloc(really_big);
>>>>> >  ...
>>>>> > free(p2);
>>>>> >allowing a transformation into:
>>>>> >   p1 = malloc(really_big);
>>>>> >   p2 = malloc(really_big);
>>>>> >    ...
>>>>> >   free(p1); free(p2);
>>>>> >could be problematic.
>>>>>
>>>>> Both free and malloc would be marked with having an internal state.
>>>>> This should prevent this kind of an optimization. Note that having
>>>>> the ReadNone attribute should not cause problems anymore.
>>>>>
>>>>>
>>>>> Something is not clear to me: is this "internal state” supposed to be
>>>>> private to the function?
>>>>> How does it deal with malloc/free which can be seen as sharing a
>>>>> state? Especially it is not clear to me how this flag would prevent the
>>>>> last “optimization” you’re illustrating.
>>>>>
>>>>>>>>>> Mehdi
>>>>>
>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>
>>>
>> _______________________________________________
>> LLVM Developers mailing list
>> llvm-dev at lists.llvm.org
>> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20151204/da5afe80/attachment.html>


More information about the llvm-dev mailing list