[llvm-dev] Dereferenceable load semantics & LICM

Piotr Padlewski via llvm-dev
Thu, 06 Apr 2017 09:36:31 -0000

2017-04-05 18:30 GMT+02:00 Sanjoy Das <sanjoy at playingwithpointers.com>:

> Hi Piotr,
>
> On Mon, Apr 3, 2017 at 2:01 PM, Piotr Padlewski
> <piotr.padlewski at gmail.com> wrote:
> >> I don't see any way to model it right now in LLVM, but I see a
one
> simple
> >> solution:
> >>  add extra information to the metadata nodes indicating that this
> property
> >> is non-local:
> >>
> >> %0 = load... %p, !invariant.group !1, !dereferenceable !2
> >> %1 = load ... %0, !invariant.load !0, !dereferenceable !2
> >>
> >> !0 = !{!"GlobalProperty"}
> >> !1 = !{!"MyType", !"GlobalProperty"}
> >> !2 = !{i64 8, !"GlobalProperty}
> >>
> >> With that we would strip only the metadata not containing this
> >> information.
> >>
> >> For devirtualization it would make sense with invariant.load,
> >> invariant.group and dereferenceable.
> >>
> >> What do you think about this idea?
>
> I'm sorry for being *this* annoying, but I'm not on board with
this.  :)
>
> I think this will run into the same dead-code-can-affect-behavior
> issue discussed in https://reviews.llvm.org/D20116.
>
> Specifically, if your program is:
>
> if (false) {
>   ptr = load i8*, i8** %ptrptr, !dereferenceable !{i64 8,
!"GlobalProperty}
>   // ptr is not actually dereferenceable, even the load above has UB
>   // (since the metadata is "wrong"), but it is never executed so
all is
> well.
>   int val = *ptr;
> }
>
> then you could transform it to
>
> ptr = load i8*, i8** %ptrptr, !dereferenceable !{i64 8,
!"GlobalProperty}
> // ptr is not actually dereferenceable
> int val = *ptr;
> if (false) {
> }
>
> and you'd have gone from a program with no UB to one with UB.
>
> -- Sanjoy
>
I disagree, I find it different than the patch you mentioned. We don't have
any problems with code like this:

ptr = load i8*, i8** %ptrptr, !dereferenceable !{i64 8}
if (false) {

  // ptr is not actually dereferenceable, even the load above has UB
  // (since the metadata is "wrong"), but it is never executed so all
is
well.
  int val = *ptr;
}

because we rely on the information provided by someone else that ptr is
dereferenceable, so we can transform it to

ptr = load i8*, i8** %ptrptr, !dereferenceable !{i64 8}
// ptr is not actually dereferenceable
int val = *ptr;
if (false) {
}

And this is exactly how we treat any other UB.
It is also important to mention that in order to perform optimization you
have mentioned we need to know that %ptrptr is also dereferenceable.
The vptr and vtable properties are non local, so I don't see how
hoisting vptr load could be dangerous:

void foo(A& a) {
  if (false)
    a.foo();
}

will be:

void foo(A* dereferenceable(8) %a) {
  if (false)
    %vptr = load %a, !dereferenceable !{VtableSize, "GlobalProperty"},
!invariant.group !0
    %vfunction = load %vptr, !invariant.load !0
    call %vfunction();
}

and after hoisting:

void foo(A* dereferenceable(8) %a) {
  %vptr = load %a, !dereferenceable !{VtableSize, "GlobalProperty"},
!invariant.group !{"GlobalProperty"}
  %vfunction = load %vptr, !invariant.load !{"GlobalProperty"}
  if (false)
    call %vfunction();
}
-------------- next part --------------
An HTML attachment was scrubbed...
URL:
<http://lists.llvm.org/pipermail/llvm-dev/attachments/20170406/ed368791/attachment.html>