[LLVMdev] Request for comments: TBAA on call

Filip Pizlo fpizlo at apple.com
Mon Oct 7 16:22:22 PDT 2013


On Oct 7, 2013, at 3:49 PM, Daniel Berlin <dberlin at dberlin.org> wrote:

> 
> 
> 
> On Wed, Sep 25, 2013 at 5:50 PM, Filip Pizlo <fpizlo at apple.com> wrote:
> Hi all,
> 
> TBAA on loads and stores is super useful for conveying high-level type knowledge to LLVM transformations.  But often translating a high-level language into LLVM IR will result in runtime calls that are known at the high level to only affect (either by reading or by writing) some restricted subset of the heap.  These aren't read-only calls; that is often too strong of a guarantee.  A great example would be a GC barrier or GC allocation that may arise if LLVM is used as a backend for a compiler for a higher-level language.  There are probably other examples that arise in high-level languages, but let's use allocation as an example for now.  I can envision two ways of representing allocation in LLVM IR:
> 
> Method #1:
> 
>             %object = call @runtime_allocate(... /* pass size and/or other data */)
> 
> Method #2:
> 
>         SomeBlock:
>             ... ; code preceding the allocation
>             %objectFast = ... ; IR for the inlined allocation fast path, which produces %objectFast but may yield null if the fast path fails and we need to take slow path
>             %didFail = icmp eq %objectFast, null
>             br %didFail, label %AllocationSlowPath, label &Continue
>         AllocationSlowPath:
>             %objectSlow = call @runtime_allocate_slow_path(... /* pass size and/or other data */)
>             br label %Continue
>         Continue:
>             %object = phi [%objectFast, SomeBlock], [%objectSlow, AllocationSlowPath]
> 
> In both of these cases, the call instruction will appear to clobber the world leading to more pessimistic optimization.  Moreover, it's not always correct to mark the allocation as being readonly; it may read or write observable state.  But if you don't like the allocation example then you can imagine that a GC store barrier would have an almost identical pattern to #2 and it would definitely not be readonly.  Anyway, such a pattern would lead to fewer loads being eliminated or hoisted, fewer stores being eliminated or sunk, etc - all due to those pesky calls.  But the high-level code generator that produces this IR will know for sure that @runtime_allocate or @runtime_allocate_slow_path can only affect a very narrow subset of the heap: namely, it will only read and write the GC's data structures.  So most of the loads and stores surrounding the allocation, that arose from source-level loads and stores, would definitely not have any interference with the call and this would !
>  be easy to convey using TBAA.
> 
> Hence if we could decorate the calls to these runtime functions as only reading or writing certain TBAA types,
> 
> Out of curiosity, why is this tied to TBAA types, rather than variables or something else?
> IE what analysis are you performing that gives you type answers instead of answers in terms of local/global variables?

TBAA’s utility for non-C languages (like JavaScript, in my case) isn’t for reasoning about user-facing types, but for creating what you could alternatively think of as an “abstract heaps”.  TBAA is perfect for this.  For example I might know that a certain function clobbers a field called ‘foo’.  In my frontend I create an "abstract heap" for each field name, and this gets lowered to LLVM TBAA.  I already use this for telling LLVM about dependencies between loads and stores.  If I emit a store as a result of lowering a JS “o.foo = …” operation then I will ascribe a TBAA node for the “foo” abstract heap.  This is just one example of the many different abstract heaps (i.e. “TBAA nodes”) that my compiler uses.

Note that most of the loads and stores that I’m concerned with do not involve alloca’s (i.e. locals) or “global variables” in any kind of way that LLVM could reason about.  A mechanism that only works for locals and globals would be of zero use to me since all of my alloca’s are trivially killed by mem2reg and I don’t have any globals.

> The information you are talking about here is traditional mod/ref info that is completely independent of TBAA.

You lost me.  I’m writing a compiler that generates LLVM IR.  I have high-level knowledge that allows me to prove the set of abstract heap locations that may be read or written by any call, load, or store.

What do you suggest as a mechanism by which I can communicate that information to LLVM?  Are you saying that such a mechanism already exists and that I should use that instead?

As far as I can tell, TBAA is the cleanest such mechanism that I have found thus far and its only shortcoming is that I cannot use it to annotate calls.

> 
> The more specific the info you can get from the metadata, the better off you are eliminating loads/stores
> 
> Otherwise, yes, it's certainly incredibly helpful to the other optimizations.
> 
> However, unless there is a good reason, i'd suggest you just make it generic call read/write metadata that talks about values (be they incoming arguments, alloca'd memory, or whatever else).

You lost me.  How does this help a high-level compiler convey to LLVM that a particular call and a particular load or store don’t have any dependency?  My proposal achieves this, albeit using a coarse-grained at a coarse-grained level: you have to express your knowledge of dependencies using a hierarchy of abstract sets of heap locations (i.e. exactly what TBAA gives you).

> 
> It's actually a lot easier to reason about individual heap values than entire types, unless, again, you only have an analysis that is providing answers about types (or your plan was simply to use the existing TBAA info and do a bottom-up closure over the entire callgraph, which would provide some value, but lose a lot of info due to external function calls)

The goal is to allow a front-end that already has knowledge about some high-level types to convey that knowledge to LLVM when generating LLVM IR.  In my front-end, I actually don’t really do much analysis to produce the TBAA information - it mostly just falls out naturally from my own IR.

I’m not sure what you mean about “individual heap values”.  If you mean global variables, then that’s of no use to me.  I don’t have any global variables.

It’s interesting that using TBAA for this completely subsumes any approach based on globals: you could ascribe a distinct TBAA node to each global and get the same effect.  But TBAA is more powerful because you can annotate something that you’ve already proven about a memory location, and this information can be orthogonal to the IR used to compute that memory location.  This makes TBAA ideal for conveying dependency information from a high-level compiler to LLVM at the time of LLVM IR generation.

-Filip

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20131007/93e7a50b/attachment.html>


More information about the llvm-dev mailing list