Daniel Berlin via llvm-dev
2017-Mar-31 17:33 UTC
[llvm-dev] Dereferenceable load semantics & LICM
On Fri, Mar 31, 2017 at 10:23 AM, Sanjoy Das <sanjoy at playingwithpointers.com> wrote:> Hi Piotr, > > On March 31, 2017 at 9:07:42 AM, Piotr Padlewski > (piotr.padlewski at gmail.com) wrote: > > Hi all, > > I have a question about dereferenceable metadata on load instruction. I > > have a patch (https://reviews.llvm.org/D31539) for LICM that hoists > loads > > with !invariant.group. > > The motivation example is devirtualization: > > ... > > [snip] > > > > On the other hand, after performing my LICM of !invariant.group load, GVN > > hoists the second load and I am not sure why it is legal then. > > I suspect what's going on is that we first canonicalize the loop to: > > if (precondition) { > do { > vptr = load vtable; > fptr = *vptr; > ... > } while (backedge_condition); > } > > after which it is safe to transform the program to (modulo aliasing): > > if (precondition) { > vptr = load vtable; > fptr = *vptr; > do { > ... > } while (backedge_condition); > } > > since the we moved a load from a ("strongly") postdominating location. > We know that once we were in the preheader we know we're definitely > going to execute the vptr and fptr loads, so they better be > dereferenceable. In other words, we're "exploiting undefined > behavior" here. >Yes, this appears to be exactly the case - dominance tells us they must execute at least once in both situations.> > -- Sanjoy >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20170331/3d38117d/attachment.html>
Piotr Padlewski via llvm-dev
2017-Mar-31 20:06 UTC
[llvm-dev] Dereferenceable load semantics & LICM
Thanks guys! I have another small example: struct A { virtual void foo(); }; void indirect(A *a, int n) { for (int i = 0 ; i < n; i++) a->foo(); } generates: define void @_Z8indirectP1Ai(%struct.A* %a, i32 %n) local_unnamed_addr #0 { entry: %cmp3 = icmp sgt i32 %n, 0 br i1 %cmp3, label %for.body.lr.ph, label %for.cond.cleanup for.body.lr.ph: ; preds = %entry %0 = bitcast %struct.A* %a to void (%struct.A*)*** br label %for.body for.cond.cleanup.loopexit: ; preds = %for.body br label %for.cond.cleanup for.cond.cleanup: ; preds %for.cond.cleanup.loopexit, %entry ret void for.body: ; preds = %for.body, % for.body.lr.ph %i.04 = phi i32 [ 0, %for.body.lr.ph ], [ %inc, %for.body ] %vtable = load void (%struct.A*)**, void (%struct.A*)*** %0, align 8, !tbaa !4, !invariant.group !7 %1 = load void (%struct.A*)*, void (%struct.A*)** %vtable, align 8, !invariant.load !8 tail call void %1(%struct.A* %a) %inc = add nuw nsw i32 %i.04, 1 %exitcond = icmp eq i32 %inc, %n br i1 %exitcond, label %for.cond.cleanup.loopexit, label %for.body } So now `a` is not dereferenceable and I was counting that because we have loop preheader that only executes if the loop executes, it will be hoisted. for invariant.group load the isGuaranteedToExecute returns with (!SafetyInfo->HeaderMayThrow). The isGuaranteedToTransferExecutionToSuccessor called on the %vtable instruction returns true, but few instructions later, the call of %1 may throw. Do I understand it correctly, that it is legal to do the hoist because all of the instructions above %vtable does not throw? Are there any plans to fix it in the future? The fix doesn't seem hard to write and I can do it, but I am not sure if it won't be too expensive. Piotr PS: please review the https://reviews.llvm.org/D31539 2017-03-31 19:33 GMT+02:00 Daniel Berlin <dberlin at dberlin.org>:> > > On Fri, Mar 31, 2017 at 10:23 AM, Sanjoy Das <sanjoy at playingwithpointers. > com> wrote: > >> Hi Piotr, >> >> On March 31, 2017 at 9:07:42 AM, Piotr Padlewski >> (piotr.padlewski at gmail.com) wrote: >> > Hi all, >> > I have a question about dereferenceable metadata on load instruction. I >> > have a patch (https://reviews.llvm.org/D31539) for LICM that hoists >> loads >> > with !invariant.group. >> > The motivation example is devirtualization: >> > ... >> > [snip] >> > >> > On the other hand, after performing my LICM of !invariant.group load, >> GVN >> > hoists the second load and I am not sure why it is legal then. >> >> I suspect what's going on is that we first canonicalize the loop to: >> >> if (precondition) { >> do { >> vptr = load vtable; >> fptr = *vptr; >> ... >> } while (backedge_condition); >> } >> >> after which it is safe to transform the program to (modulo aliasing): >> >> if (precondition) { >> vptr = load vtable; >> fptr = *vptr; >> do { >> ... >> } while (backedge_condition); >> } >> >> since the we moved a load from a ("strongly") postdominating location. >> We know that once we were in the preheader we know we're definitely >> going to execute the vptr and fptr loads, so they better be >> dereferenceable. In other words, we're "exploiting undefined >> behavior" here. >> > > Yes, this appears to be exactly the case - dominance tells us they must > execute at least once in both situations. > > >> >> -- Sanjoy >> > >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20170331/169b5f4b/attachment.html>
Sanjoy Das via llvm-dev
2017-Mar-31 21:20 UTC
[llvm-dev] Dereferenceable load semantics & LICM
Hi Piotr, On March 31, 2017 at 1:07:12 PM, Piotr Padlewski (piotr.padlewski at gmail.com) wrote:> [snip] > Do I understand it correctly, that it is legal to do the hoist because all > of the instructions above %vtable does not throw?Yes, I think you're right. HeaderMayThrow is a conservative approximation, and the conservativeness is biting us here.> Are there any plans to fix it in the future? The fix doesn't seem hard toNot to my knowledge.> write and I can do it, but I am not sure if it won't be too expensive.Maybe we can do it (relatively) cheaply: - Remember the first throwing instruction in the header, instead of a boolean, in LoopSafetyInfo - In hoistRegion, remember if you've seen the first throwing instruction yet - Pass the above as a boolean parameter to isGuaranteedToExecute, and instead of if (Inst.getParent() == CurLoop->getHeader()) return !SafetyInfo->HeaderMayThrow; do something like if (Inst.getParent() == CurLoop->getHeader()) return IsBeforeThrowingInst; -- Sanjoy