[llvm-dev] Placement new and TBAA

Daniel Berlin via llvm-dev llvm-dev at lists.llvm.org
Fri Nov 25 22:03:01 PST 2016


The position most compilers take is the the access must be visibly through
a union if you want it to work.  Otherwise, it's not possible to use tbaa
really at all.  Move your function f to another file, for example, and you
simply could never tell.  Some will give you a little more, but you are
pushing your luck

This was discussed in depth in the thread I quoted, and has GCC mailing
list threads going back to before llvm existed :).
Note that there are rules about touching members in a union other than the
first one you set, but pretty much everyone guarantees type punning through
unions to work.

On Fri, Nov 25, 2016, 9:07 PM Sanjoy Das <sanjoy at playingwithpointers.com>
wrote:

> Hi,
>
> On Sat, Nov 26, 2016 at 9:53 AM, Mehdi Amini <mehdi.amini at apple.com>
> wrote:
> > The PR says "passes with -fno-strict-aliasing”, my understanding is that
> it
> > is failing only with the TBAA indeed.
> >
> > You don’t need the main and the union to reproduce, extracting foo()
> alone
> > in its single own file is enough:
> >
> > void *operator new(decltype(sizeof 0), void *) noexcept;
> > float *qq;
> > void foo(int *p, int *q, long unk) {
> >    for (long i = 0; i < unk; ++i) {
> >       ++*p;
> >       qq = new (static_cast<void *>(&q[i])) float(42);
> >    }
> > }
> >
> > LICM will get the store to p out of the loop, conceptually turning it
> into:
> >
> > void foo(int *p, int *q, long unk) {
> >    for (long i = 0; i < unk; ++i) {
> >       qq = new (static_cast<void *>(&q[i])) float(42);
> >    }
> >    ++*p;
> > }
> >
> >
> > Now I don’t know if the use of placement new in this example is legal in
> the
> > first place. I thought calling delete before using placement new was
> > mandatory.
>
> So if you:
>
>  1. override operator delete to do nothing for that type (so that the
>     placement new actually has unfreed backing storage to re-use).
>  2. have an empty destructor.
>
> and have the source program be
>
>   void *operator new(decltype(sizeof 0), void *) noexcept;
>   float *qq;
>   void foo(int *p, int *q, long unk) {
>      // unk = 1
>      for (long i = 0; i < unk; ++i) {
>         ++*p;
>         delete &q[i];  // since i is only ever 0, this does not
>               // delete a derived pointer
>         qq = new (static_cast<void *>(&q[i])) float(42);
>      }
>   }
>
> then I suspect we'll have the same problem after the destructor and
> the operator delete for that type has been inlined away.
>
> > CC Sanjoy, since he looked into TBAA recently and it reminds me a similar
> > test case he mentioned, not with placement new but with a call to a
> function
> > taking int * and float *, and passing the same address (call site was
> > union).
>
> The case I was talking about was something like:
>
> // C11 source file.
>
> union S {
>   int i;
>   float f;
> };
>
> void f(int* i, float *f) {
>   *i = 20;
>   *f = 40.0;
> }
>
> void g() {
>   union S s;
>   f(&s.i, &s.f);
> }
>
>
> At least cursorily this looked well defined in C++ to me -- f should
> first write int(20) and then float(40.0) to the same location legally.
> However clang emits tbaa such that LLVM concludes that in f, the store
> to i and the store to f don't alias.
>
> However, I'm not very confident in the "this looked well defined"
> reading.  I did not consult the standard, but instead relied on second
> hand stackoverflow answers, so I won't be surprised to hear that I was
> wrong about this.  Actually I'm never surprised to hear that I'm
> wrong, but I'll be especially less surprised in this case. :)
>
> Thanks!
> -- Sanjoy
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20161126/0d9ae5ee/attachment-0001.html>


More information about the llvm-dev mailing list