[llvm-dev] [RFC] A nofree (and nosynch) function attribute: Mixing dereferenceable and delete
Philip Reames via llvm-dev
llvm-dev at lists.llvm.org
Mon Sep 17 10:53:49 PDT 2018
Great example, thanks! That's exactly what I needed.
Philip
On 09/15/2018 04:51 PM, Sanjoy Das wrote:
> On Fri, Sep 14, 2018 at 3:45 PM Philip Reames via llvm-dev
> <llvm-dev at lists.llvm.org> wrote:
>> Sorry for the late response. Reading over the thread, the general plan
>> expressed seems non-objectionable, but I find myself questioning the
>> premise.
>>
>> Can you give an example or two of how we miscompile such functions? My
> Maybe something like:
>
> void f(int& x) {
> free(&x);
> for (;;) {
> if (false) {
> int k = x;
> }
> }
> }
>
> =>
>
> void f(int& x) {
> free(&x);
> // "safe" to hoist since the "x" arg will be lowered to i32*
> dereferenceable(4)
> int k = x;
> for (;;) {
> if (false) {
> }
> }
> }
>
>
>> mental model here is that free conceptually writes an unspecified value
>> to the memory before actually releasing it. With that, I'd expect most
>> obvious miscompiles to be inhibited by memory dependence. Can you
>> provide a concrete example to illustrate why my mental model is broken?
>>
>> Philip
>>
>>
>> On 07/10/2018 07:01 PM, Hal Finkel via llvm-dev wrote:
>>> Hi, everyone,
>>>
>>> I'd like to propose adding a nofree function attribute to indicate that
>>> a function does not, directly or indirectly, call a memory-deallocation
>>> function (e.g., free, C++'s operator delete). Clang/LLVM can currently
>>> misoptimize functions that:
>>>
>>> 1. Have a reference argument.
>>>
>>> 2. Free the memory backing the object to which the reference is bound
>>> during the function's execution.
>>>
>>> Because we tag, in Clang, all reference arguments using the
>>> dereferenceable attribute, LLVM assumes that the pointer is
>>> unconditionally dereferenceable throughout the course of the entire
>>> function. This isn't true, however, if the memory is freed during the
>>> execution of the function. For more information, please see the
>>> discussion in https://reviews.llvm.org/D48239.
>>>
>>> To solve this problem, we need to give LLVM more information in order to
>>> help it determine when a pointer, which is dereferenceable when the
>>> functions begins to execute, will still be dereferenceable later on in
>>> the function's execution. This nofree attribute can be part of that
>>> solution. If we know that free (and friends) are not called by the
>>> function (nor by any function called by the function, and so on), then
>>> we know that pointers that started out dereferenceable will stay that
>>> way (except as explained below).
>>>
>>> I'm initially proposing this to be only a function attribute, although
>>> one could easily imagine a parameter attribute as well (that indicates
>>> that a particular pointer argument is not freed by the function). This
>>> might be useful, but for the use case of helping dereferenceable, it
>>> would be subtle to use, unless the parameter was also marked as noalias,
>>> because you'd need to know that the parameter was not also aliased with
>>> another argument (or had not been captured). Another analysis would need
>>> to provide this kind of information.
>>>
>>> Also, just because a function does not, directly or indirectly, call
>>> free does not mean that it cannot cause memory to be deallocated. The
>>> function might communicate (synchronize) with another thread causing
>>> that other thread to delete the memory. For this reason, to use
>>> dereferenceable as we currently do, we also need to know that the
>>> function does not synchronize with any other threads. To solve this
>>> problem, like nofree, I propose to add a nosynch attribute (to indicate
>>> that a function does not use (non-relaxed) atomics or otherwise
>>> synchronize with any other threads (e.g., perform I/O or, as a practical
>>> matter, use volatile accesses).
>>>
>>> I've posted a patch for the nofree attribute
>>> (https://reviews.llvm.org/D49165). nosynch's implementation would be
>>> very similar (except instead of looking for calls to free, it would look
>>> for uses of non-relaxed atomics, volatile ops, and known functions that
>>> are not I/O functions).
>>>
>>> With both of these attributes (nofree and nosynch), a function argument
>>> with the dereferenceable attribute will be known to be dereferenceable
>>> throughout the execution of the attributed function. We can update
>>> isDereferenceableAndAlignedPointer to include these additional checks on
>>> the current function.
>>>
>>> One more choice we have: We can, as I proposed above, essentially weaken
>>> the current semantics of dereferenceable to not exclude
>>> mid-function-execution deallocation. We can also add a second attribute
>>> with the current, stronger, semantics. We can keep the current attribute
>>> as-is, and add a second attribute with the weaker semantics (and switch
>>> Clang to use that).
>>>
>>> Please let me know what you think.
>>>
>>> Thanks again,
>>>
>>> Hal
>>>
>> _______________________________________________
>> LLVM Developers mailing list
>> llvm-dev at lists.llvm.org
>> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
More information about the llvm-dev
mailing list