This is an archive of the discontinued LLVM Phabricator instance.

Insert invariant.group.barrier for pointers comparisons
AbandonedPublic

Authored by Prazek on Apr 21 2017, 2:52 PM.

Details

Reviewers
rjmccall
rsmith
Summary

This code was wrongly devirtualized before:

A* a = new A;
a->foo();
A* b = new(a) B;

if (a == b)
  b->foo();

Now we insert barrier before comparing dynamic pointers.

Event Timeline

Prazek created this revision.Apr 21 2017, 2:52 PM
Prazek retitled this revision from Insert invariant.group.barrier for pointers comparsons to Insert invariant.group.barrier for pointers comparisons.Apr 21 2017, 3:06 PM
rsmith added inline comments.Apr 21 2017, 3:41 PM
lib/CodeGen/CGExprScalar.cpp
3066–3067

I think we need to do this regardless of optimization level -- if we LTO together a -O0 translation unit with a -O2 translation unit, we still need this protection for the comparisons in the -O0 TU.

(IIRC we chose to make -fstrict-vtable-pointers an IR-level ABI break, but we shouldn't do the same thing for optimization level.)

Prazek marked an inline comment as done.Apr 23 2017, 9:40 AM
Prazek added inline comments.
lib/CodeGen/CGExprScalar.cpp
3066–3067

sounds good

Prazek updated this revision to Diff 96311.Apr 23 2017, 9:40 AM
Prazek marked an inline comment as done.

Addressing Richard's comment

Prazek marked an inline comment as done.Apr 23 2017, 9:40 AM

This is actually good catch, we also need to do it when inserting barrier in placement new.
I think that for the ctors and dtors we can do it only with optimizations enabled, because if optimizations are not enabled then ctors and dtors won't have the invariant.group in stores.

Prazek updated this revision to Diff 96371.Apr 24 2017, 3:28 AM

Don't add barrier if compared with nullptr. With this it reduces added
barriers to compares by half. Note that we will transform barrier(null) -> barrier
in llvm

Has it been discussed whether this is something to be addressed in the optimizer as opposed to the front-end?

lib/CodeGen/CGExprScalar.cpp
3069

Consider:

extern "C" int printf(const char *, ...);
void *operator new(decltype(sizeof 0), void *);

struct A {
  virtual void foo() { printf("%s\n", __PRETTY_FUNCTION__); }
  int m;
  int *zip() { return &m; }
};

struct B : A {
  virtual void foo() { printf("%s\n", __PRETTY_FUNCTION__); }
};

int main(void) {
  A *ap = new A;
  ap->foo();
  int *const apz = ap->zip();
  B *bp = new (ap) B;
  if (apz == bp->zip()) {
    bp->foo();
  }
}
Prazek added a comment.May 3 2017, 8:41 AM

Has it been discussed whether this is something to be addressed in the optimizer as opposed to the front-end?

The example that you showed is excellent. I didn't know that LLVM does the transformation with pointers and it clearly shows that we need the different approach.
I got following idea how to solve it in the middle end - drop all invariant.group metadata when we replace dominated uses based on the comparison. This works for simple cases, but it doesn't when the changed pointer
is passed to a not inlined function, or returned. The solution I see that does not satisfy me, is to put barriers between passed/returned pointer. I will bring it to mailing list

Prazek planned changes to this revision.May 3 2017, 9:24 AM
Prazek abandoned this revision.Feb 20 2018, 5:33 AM