[llvm-dev] RFC: Add guard intrinsics to LLVM

Andrew Trick via llvm-dev llvm-dev at lists.llvm.org
Mon Feb 22 21:43:37 PST 2016

> On Feb 22, 2016, at 11:03 AM, Philip Reames <listmail at philipreames.com> wrote:
>> ```
>> global *ptr
>> declare @foo() readwrite
>> def @bar() { call @foo() [ "deopt"(XXX) ]; *ptr = 42 }
>> def @baz() { call @bar() [ "deopt"(YYY) ]; int v0 = *ptr }
>> ```
>> Naively, it looks like an inter-proc CSE can forward 42 to v0, but
>> that's unsound, since @bar could get deoptimized at the call to
>> @foo(), and then who knows what'll get written to *ptr.
> Ok, I think this example does a good job of getting at the root issue.  You claim this is not legal, I claim it is.  :) Specifically, because the use of the inferred information will never be executed in baz.  (see below)
> Specifically, I think the problem here is that we're mixing a couple of notions.  First, we've got the state required for the deoptimization to occur (i.e. deopt information).  Second, we've got the actual deoptimization mechanism.  Third, we've got the *policy* under which deoptimization occurs.
> The distinction between the later two is subtle and important.  The *mechanism* of exiting the callee and replacing it with an arbitrary alternate implementation could absolutely break the deopt semantics as you've pointed out.  The policy we actually use does not. Specifically, we've got the following restrictions:
> 1) We only replace callees with more general versions of themselves.  Given we might be invalidating a speculative assumption, this could be a *much* more general version which includes actions and control flow invalidate any attribute inference done over the callee.
> 2) We invalidate all callers of @foo which could have observed the incorrect inference.  (This is required to preserve correctness.)
> I think we probably need to separate out something to represent the interposition/replacement semantics implied by invalidation deoptimization.  In it's most generic form, this would model the full generality of the mechanism and thus prevent nearly all inference.  We could then clearly express our *policy* as a restriction over that full generality.
> Another interesting case to consider:
> global *ptr
> declare @foo() readwrite
> def @bar() { call @foo() [ "deopt"(XXX) ]; *ptr = 42 }
> def @baz() {
>  v0 = 42;
>  while (C) {
>    call @bar() [ "deopt"(v0) ];
>    int v0 = *ptr
>  }
> }
> Could we end up deoptimization with an incorrect deopt value for v0 based on circular logic?  We can infer that v0 is always 42 in this example.  I claim that's legal precisely up to the point at which we deoptimize @bar and @baz together.  If we deoptimized @bar, let @baz run another loop iteration, then invalidated @baz, that would be incorrect.

Wait a sec… it’s legal for the frontend to do the interprocedural CSE, but not LLVM. The frontend can guarantee that multiple functions can be deoptimized as a unit, but LLVM can’t make that assumption. As far as it knows, @guard_on will resume in the immediate caller.

- Andy

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20160222/1e555173/attachment.html>

More information about the llvm-dev mailing list