[llvm-dev] Placement new and TBAA

Sanjoy Das via llvm-dev llvm-dev at lists.llvm.org
Fri Nov 25 21:07:34 PST 2016


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


More information about the llvm-dev mailing list