[llvm-dev] Ambiguity in the nofree function attribute

Philip Reames via llvm-dev llvm-dev at lists.llvm.org
Tue Apr 13 14:43:47 PDT 2021


Replying to self to summarize takeaways.

The consensus in responses seems to be strongly in favor of option #2.  
My main hesitation with option #2 has always been the lost ability for a 
frontend to provide the stronger scoped fact, but in the process of 
writing a detailed response to Johannes downthread, I realized we don't 
have a motivating example frontend which actually benefits from said 
ability.  Given that, it seems like option #2 wins over option #1.

It seems stopping to ask for a sanity check was well warranted in this 
case.  :)

I will go ahead and cleanup https://reviews.llvm.org/D100141 into a real 
patch.  I will give it a couple of days (concurrent with review) to give 
anyone who might disagree with this decision a chance to see this thread 
and chime in.

Philip

On 4/9/21 12:05 PM, Philip Reames wrote:
>
> I've stumbled across a case related to the nofree attribute where we 
> seem to have inconsistent interpretations of the attribute semantic in 
> tree.  I'd like some input from others as to what the "right" semantic 
> should be.
>
> The basic question is does the presence of nofree prevent the callee 
> from allocating and freeing memory entirely within it's dynamic 
> scope?  At first, it seems obvious that it does, but that turns out to 
> be a bit inconsistent with other attributes and leads to some 
> surprising results.
>
> For reference in the following discussion, here is the current wording 
> for the nofree function attribute in LangRef:
>
>     "This function attribute indicates that the function does not,
>     directly or indirectly, call a memory-deallocation function (free,
>     for example). As a result, uncaptured pointers that are known to
>     be dereferenceable prior to a call to a function with the |nofree|
>     attribute are still known to be dereferenceable after the call
>     (the capturing condition is necessary in environments where the
>     function might communicate the pointer to another thread which
>     then deallocates the memory)."
>
> For discussion purposes, please assume the concurrency case has been 
> separately proven.  That's not the point I'm getting at here.
>
> The two possible semantics as I see them are:
>
> *Option 1* - nofree implies no call to free, period
>
> This is the one that to me seems most consistent with the current 
> wording, but it prevents the callee from allocating storage and 
> freeing it entirely within it's scope.  This is, for instance, a 
> reasonable thing a target might want to do when lowering large 
> allocs.  This requires transforms to be careful in stripping the 
> attribute, but isn't entirely horrible.
>
> The more surprising bit is that it means we can not infer nofree from 
> readonly or readnone.  Why?  Because both are specified only in terms 
> of memory effects visible to the caller.  As a result, a readnone 
> function can allocate storage, write to it, and still be readonly.  
> Our current inference rules for readnone and readonly do exploit this 
> flexibility.
>
> The optimizer does currently assume that readonly implies nofree.  
> (See the accessor on Function)  Removing this substantially weakens 
> our ability to infer nofree when faced with a function declaration 
> which hasn't been explicitly annotated for nofree.  We can get most of 
> this back by adding appropriate annotations to intrinsics, but not all.
>
> *Option 2* - nofree applies to memory visible to the caller
>
> In this case, we'd add wording to the nofree definition analogous to 
> that in the readonly/readnone specification. (There's a subtlety about 
> the precise definition of visible here, but for the moment, let's hand 
> wave in the same way we do for the other attributes.)
>
> This allows us to infer nofree from readonly, but essentially cripples 
> our ability to drive transformations within an annotated function.  
> We'd have to restrict all transforms and inference to cases where we 
> can prove that the object being affected is visible to the caller.
>
> The benefit is that this makes it slightly easier to infer nofree in 
> some cases.  The main impact of this is improving ability to reason 
> about dereferenceability for uncaptured objects over calls to 
> functions for which we inferred nofree.
>
> The downside of this is that we essentially loose all ability to 
> reason about nofree in a context free manner.  For a specific example 
> of the impact of this, it means we can't infer dereferenceability for 
> an object allocated in F, and returned (e.g. not freed), in the scope 
> of F.
>
> This breaks hoisting and vectorization improvements (e.g. 
> unconditional loads instead of predicated ones) I've checked in over 
> the last few months, and makes the ongoing deref redefinition work 
> substantially harder. https://reviews.llvm.org/D100141 shows what this 
> looks like code wise.
>
> *My Take*
>
> At first, I was strongly convinced that option 1 was the right 
> choice.  So much so in fact that I nearly didn't bother to post this 
> question.  However, after giving it more thought, I've come to 
> distrust my own response a bit.  I definitely have a conflict of 
> interest here.  Option 2 requires me to effectively cripple several 
> recent optimizer enhancements, and maybe even revert some code which 
> becomes effectively useless.  It also makes a project I'm currently 
> working on (deref redef) substantially harder.
>
> On the other hand, the inconsistency with readonly and readnone is 
> surprising.  I can see an argument for that being the right overall 
> approach long term.
>
> So essentially, this email is me asking for a sanity check.  Do folks 
> think option 1 is the right option?  Or am I forcing it to be the 
> right option because it makes things easier for me?
>
> Philip
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20210413/7aab0d3e/attachment-0001.html>


More information about the llvm-dev mailing list