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

Chandler Carruth via llvm-dev llvm-dev at lists.llvm.org
Tue Feb 23 10:55:06 PST 2016


On Tue, Feb 23, 2016 at 9:33 AM Sanjoy Das <sanjoy at playingwithpointers.com>
wrote:

> On Mon, Feb 22, 2016 at 11:18 PM, Chandler Carruth <chandlerc at gmail.com>
> wrote:
> >> # step A: Introduce an `interposable` function attribute
> >>
> >> We can bike shed on the name and the exact specification, but the
> >> general idea is that you cannot do IPA / IPO over callsites calling
> >> `interposable` functions without inlining them.  This attribute will
> >> (usually) have to be used on function bodies that can deoptimize (e.g.
> has
> >> a
> >> side exit / guard it in); but also has more general use cases.
> >
> >
> > Note that we already have this *exact* concept in the IR via linkage for
> > better or worse. I think it is really confusing as you are currently
>
> I was going to have a more detailed discussion on this in the (yet to
> be started) review thread for `interposable`: we'd like to be able to
> inline `interposable` functions.  The "interposition" can only happen
> in physical function boundaries, so opt is allowed to do as much
> IPA/IPO it wants once it makes the physical function boundary go away
> via inlining.  None of linkage types seem to have this property.
>
> Part of the challenge here is to specify the attribute in a way that
> allows inlining, but not IPA without inlining.  In fact, maybe it is
> best to not call it "interposable" at all?
>

Yea, this is something *very* different from interposable. GCC and other
compilers that work to support symbol interposition make specific efforts
to not inline them in specific ways (that frankly I don't fully understand,
as it doesn't seem to be always which is what the definition of
interposable indicates to me...).


>
> Actually, I think one of the problems we're trying to solve with
> `interposable` is applicable to the available_externally linkage as
> well.  Say we have
>
> ```
> void foo() available_externally {
>   %t0 = load atomic %ptr
>   %t1 = load atomic %ptr
>   if (%t0 != %t1) print("X");
> }
> void main() {
>   foo();
>   print("Y");
> }
> ```
>
> Now the possible behaviors of the above program are {print("X"),
> print("Y")} or {print("Y")}.  But if we run opt then we have
>
> ```
> void foo() available_externally readnone nounwind {
>   ;; After CSE'ing the two loads and folding the condition
> }
> void main() {
>   foo();
>   print("Y");
> }
> ```
>
> and some generic reordering
>
> ```
> void foo() available_externally readnone nounwind {
>   ;; After CSE'ing the two loads and folding the condition
> }
> void main() {
>   print("Y");
>   foo();  // legal since we're moving a readnone nounwind function that
>           // was guaranteed to execute (hence can't have UB)
> }
> ```
>
> Now if we do not inline @foo(), and instead re-link the call site in
> @main to some non-optimized copy (or differently optimized copy) of
> foo, then it is possible for the program to have the behavior
> {print("Y"); print ("X")}, which was disallowed in the earlier
> program.
>
> In other words, opt refined the semantics of @foo() (i.e. reduced the
> set of behaviors it may have) in ways that would make later
> optimizations invalid if we de-refine the implementation of @foo().
>
> Given this, I'd say we don't need a new attribute / linkage type, and
> can add our restriction to the available_externally linkage.
>

Interesting example, I agree it seems quite broken. Even more interesting,
I can't see anything we do in LLVM that prevents this from breaking
essentially everywhere. =[[[[[[

link_once and link_once_odr at least seem equally broken because we don't
put the caller and callee into a single comdat or anything to ensure that
the optimized one is selected at link time.

But there are also multiple different kinds of overriding we should think
about:

1) Can the definition get replaced at link time (or at runtime via an
interpreter) with a differently *optimized* variant stemming from the same
definition (thus it has the same behavior but not the same refinement).
This is the "ODR" guarantee in some linkages (and vaguely implied for
available_externally)

2) Can the definition get replaced at link time (or at runtime via an
interpreter) with a function that has fundamentally different behavior

3) To support replacing the definition, the call edge must be preserved.

To support interposition you need #3, the most restrictive model. LLVM (i
think) actually does a decent job of modeling this as we say that the
function is totally opaque. We don't do IPA or inlining. But I don't think
that's what you're looking for.

I'm curious whether your use case is actually in the #1 bucket or #2
bucket. That is, I'm wondering if there is any way in which the "different
implementation" would actually break in the face of optimizations on things
like *non-deduced* function attributes, etc.

If your use case looks more like #1, then I actually think this is what we
want for link_once_odr and available_externally. You probably want the
former rather than the latter as you don't want it to be discardable.

If your use case looks more like #2, then I think its essentially
"link_once" or "link_any", and it isn't clear that LLVM does a great job of
modeling this today.

I'd be mildly interested in factoring the discarding semantics from the
"what do other definitions look like" semantics. The former are what I
think fit cleanly into linkages, and the latter I think we wedged into them
because they seemed to correspond in some cases and because attributes used
to be very limited in number.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20160223/ecb0ddf1/attachment.html>


More information about the llvm-dev mailing list