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

Andrew Trick via llvm-dev llvm-dev at lists.llvm.org
Mon Feb 22 10:08:56 PST 2016

> On Feb 21, 2016, at 12:24 PM, Sanjoy Das via llvm-dev <llvm-dev at lists.llvm.org> wrote:
> Hi Andy,
> Thanks for replying, responses inline below:
> On Fri, Feb 19, 2016 at 11:12 AM, Andrew Trick <atrick at apple.com> wrote:
>> This clearly doesn't need operand bundles, but using an intrinsic
>> would permit special code motion semantics. It could be hoisted and
>> merged with other traps, but the condition could never be widened
>> beyond the union of the original conditions. Unlike deoptimizing
>> guards, it would need to be sequenced with memory barriers, but could
> By memory barrier, do you mean things like fences?

Yes, the ‘fence’ instruction (not to be confused with GC barriers)

>> otherwise be hoisted as readnone.
> Can you clarify this a little bit?  Are you talking about things like:
>  *ptr = 42
>  @trap_if(%foo)
> =>
>  @trap_if(%foo)
>  *ptr = 42
> ?  I.e. if a failing guard postdominates a point "P" in the program,
> then the guard can be failed early at P.

That’s right. Conservatively, I would not hoist at the LLVM level past 
opaque non-readonly calls or fences.

> (We'd want a stronger condition than postdomination, since we won't
> want to hoist
>    while (!*cond) { }
>    @trap_if(*cond)
>  =>
>    @trap_if(*cond)
>    while (!*cond) { }
> )

Sorry, if you need an infinite loop you need to hide it in an opaque call or perform a 
volatile read.

>> Of course it would be nice to consider these intrinsics
>> readonly. (One thing we never fixed with patchpoints is the memory
>> effects and that was probably hurting performance.) But has this bug
>> been fixed yet: http://llvm.org/pr18912 Optimizer should consider
>> "maythrow" calls (those without "nounwind) as having side effects? I
>> vaguely remember some work being done to improve the situation, but
>> last I checked LICM was still violating it, and who knows what else?
> The specific case in the bug was fixed by David in
> http://reviews.llvm.org/rL256728.  But I agree with your concern, that
> this notion of "readnone calls are always okay to remove" may have
> leaked into other parts of LLVM.

I've begun to think that may-unwind (or may fail guard) and readonly
should be mutually exclusive. readonly refers to all system memory,
not just LLVM-visible memory. To achieve the effect of
"aliases-with-nothing-in-llvm" it's cleaner to use alias analysis.

>> BTW, if you do want readonly semantics, why would you want readonly
>> to be implicit instead of explicit?
> I don't understand the question. :) What's explicit vs. implicit here?

I meant readonly can always be a property of the call site as opposed an intrinsic 
property for more frontend control

>> I think you would need to mark @guard_on as may-unwind AND fix any
>> lingering assumptions in LLVM that readonly calls are nounwind. (Loads
>> can be CSE's across unwind calls but can't be hoisted across them).
> Yes, but a failed guard doesn't exactly "unwind".  I.e. if A invokes B
> and B fails a guard, then the guard may not always unwind to the A->B
> callsite's landingpad, but can also return to its normal no-exception
> successor; unless you inline B into A, in which case "B"s guard (now
> physically present in the A+B function) will return from A if it
> fails.  In other words, we'll have to re-purpose the term "unwind" to
> mean either "throwing an exception" or "failed a guard".  I'm fine
> with this, but it is a choice we need to make explicitly.

I was thinking that that "unwind" would be repurposed as you say. But
after responding to your points below, I think that could be
misleading. It's probably cleaner to adhere to the rule that unwinding
can only resume in a landing pad.

>> Another good point mentioned later in the thread is that a readonly
>> callee should *not* imply a readonly @guard_on or other "deopt" call
>> site. The simple solution for this is to disallow "deopt" of readonly
>> calls.
> Assuming we're talking about the same thing, the concern was more
> along the lines of: you cannot do IPA on a callsite that calls
> something that contains a potential deoptimization point, be it either
> for a @guard_on call, or a "normal" deoptimization safepoint.  This is
> because if you have:
> ```
> void @something()
> void callee() {
>  call @something() [ "deopt"(state0) ]
>  *ptr = 100;
> }
> void caller() {
>  call @callee()  // whether this is a "deopt" call site or a
>                  // a normal call site doesn't matter
>  int k = *ptr;
> }
> ```
> you cannot, say, fold `k` to `100` in @caller() because the deopt
> state, state0, if resumed to may actually write something else to
> *ptr.
> Making the call to @something() read/write/may unwind does not solve
> the problem -- even if the call to @something() wrote to *ptr (or
> threw an exception), we could still fold k to 100.  The novel control
> flow here is that `@caller`, if deoptimized with `state0`, will
> execute some arbitrary code, and **return** to @caller at the callsite
> to @callee.
> (Btw: things are a little different inside our JVM because of the way
> we register dependencies, but I'm trying to make a LLVM-centric
> argument here.)
> At this point, I think it is most straightforward to do a slight
> variant of what Philip suggested to me offline: if a function can
> deoptimize, it needs to be marked as "mayBeOverridden", preferably by
> introducing a new function attribute or by setting the linkage type to
> one of those checked by GlobalValue::mayBeOverridden.  This is the
> responsibility of the frontend, and resuming to the "deopt"
> continuation in a physical frame where the enclosing function was not
> marked mayBeOverridden is UB.

I understand your problem. I was ignoring an aspect of analyzing the
unwind path and the fact the LLVM could make assumptions about where
control will resume.

mayBeOverriden makes sense. I would think that the symbol at the deopt
call site needs to be marked mayBeOverriden.


More information about the llvm-dev mailing list