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

Sanjoy Das via llvm-dev llvm-dev at lists.llvm.org
Tue Feb 23 09:32:39 PST 2016


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?

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.

> describing it because it seems deeply overlapping with linkage, which is
> where the whole interposition thing comes from, and yet you never mention
> how it interacts with linkage at all. What does it mean to have a common
> linkage function that lacks the interposable attribute? Or a LinkOnceODR
> function that does have that attribute?

What would you say about adding this as a new kind of linkage?  I was
trying to avoid doing that since the intended semantics of,
GlobalValue::InterposableLinkage don't just describe what a linker
does, but also restricts what can be legally linked in (for the
can-inline-but-can't-IPA property to hold), but perhaps that's the
best way forward?

[Edit: I wrote this section before I wrote the available_externally
thing above.]

> If the goal is to factor replaceability out of linkage, we should actually
> factor it out rather than adding yet one more way to talk about this.
>
> And generally, we need to be *really* careful adding function attributes.
> Look at the challenges we had figuring out norecurse. Adding attributes
> needs to be viewed as nearly as high cost as adding instructions,
> substantially higher cost than intrinsics.

Only indirectly relevant to this discussion, but this is news to me --
my mental cost model was "attributes are easy to add and maintain", so
I didn't think too hard about alternatives.

> I think it would be really helpful to work to describe these things in terms
> of semantic contracts on the IR rather than in terms of implementation
> strategies. For example, not all IR interacts with an interpreter, and so I
> don't think we should use the term "interpreter" to specify the semantic
> model exposed by the IR.

That's what I was getting at by:

>> Chandler raised some points on IRC around making `guard_on` (and
>> possibly `side_exit`?) more generally applicable to unmanaged
>> languages; so we'd want to be careful to specify these in a way that
>> allows for implementations in an unmanaged environments (by function
>> cloning, for instance).

-- Sanjoy


More information about the llvm-dev mailing list