<html><head><meta http-equiv="Content-Type" content="text/html charset=us-ascii"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">Last week, I put some effort into trying to reduce the power of SIL's infamous partial_apply instruction. The instruction was intended to make representation and optimization of closures easy by avoiding concretization of the closure type and layout, but in practice, the complexity of dealing with ARC and the differing ownership needs for closure objects vs normal function arguments makes the abstraction unwieldy to understand and work with. As we look into adding new features that rely on static analysis and optimization of SIL, such as the borrow model, partial_apply is going to be a continuing drag on development. I have a work-in-progress branch to make SILGen generate at least some closures by explicit box allocation and initialization:<div class=""><br class=""></div><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;" class=""><div class=""><a href="https://github.com/apple/swift/pull/4565" class="">https://github.com/apple/swift/pull/4565</a></div><div class=""><br class=""></div></blockquote>My idea is to get to a point where all partial_apply does is bind the thin invocation function to its context object to build the "thick" function value. I'm using the existing infrastructure for @box types in SIL to represent a closure allocation, using tuples to aggregate multiple captures. This is enough to get a start on the project, but there a few limitations and inefficiencies to this approach, and I wanted some feedback on how to tackle these problems:<div class=""><br class=""></div><div class="">(A) Aggregating contexts as tuples imposes some unnecessary abstraction limitations on capture layout, and would potentially force otherwise unnecessary metadata instantiation for tuple types. Tuples need to tolerate potentially being accessed as fully generic (T, U, V) types (and possibly variadic (T...) types, should we add variadics in the future), which limits the layout optimizations we can potentially do with them. Closure contexts don't need to support generic abstraction, since the details of layout only need to be known by the closure's invocation function and its enclosing scope, so we should have full freedom for layout optimization like we have with structs and classes.</div><div class="">(B) Boxed tuples can't represent captured type information for forming closures in generic contexts. Closure contexts need to be able to provide type metadata to the invocation function for execution of generic code and layout of the context object itself.</div><div class=""><br class=""></div><div class="">One idea I'm entertaining is, instead of relying on structural box types, to introduce "nominal" box layouts, which can give an identity to a particular closure layout, thereby addressing problem (A), and can potentially carry a generic signature to represent captured type metadata, addressing (B). For example, in code like this:</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>var escaper: () -> ()</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>func foo(x: Bool, y: Bool) {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span> escaper = { _ = x; _ = y }</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><div class=""><br class=""></div><div class="">We'd emit something like this SIL:</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>// Closure layout for closure inside foo</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>sil_box_layout foo.1 {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span> let x: Bool</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span> let y: Bool</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>sil @foo : $(Bool, Bool) -> () {</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>entry(%x : $Bool, %y : $Bool):</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span> // Allocate a box with the layout for this closure</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span> %context = alloc_box foo.1</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span> // Initialize the members</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span> %context.x = project_box %context, #x</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span> store %x to %context.x</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span> %context.y = project_box %context, #y</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span> store %y to %closure.y</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span> // Bind to the invocation function</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span> %closure = partial_apply @foo.1(%closure)</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span> assign %closure to @escaper</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span> return</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>// Invocation function for the closure</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>sil @foo.1 : $(@box foo.1) -> () {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>entry(%context : $@box foo.1):</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span> // Load captured locals out of context</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span> %context.x = project_box %context, #x</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span> %x = load %context.x</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span> %context.y = project_box %context, #y</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span> %y = load %context.y</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span> // do nothing</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span> release %context</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span> return</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><div class=""><br class=""></div><div class="">The unique box layout gives us license to pack those bools into a single word, or a tagged pointer, or do other fancy layout optimizations. For generic closures, a box layout could be generic, so that something like this:</div><div class=""><br class=""></div><div class=""><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>func bar<T>(x: T, y: T) {</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span> escaper = { _ = x; _ = y }</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>}</div><div class=""><br class=""></div></div><div class="">lowers to:</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>// Closure layout for closure inside bar, which captures type info</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>sil_closure_layout bar.1<A> {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span> let x: T</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span> let y: T</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><div class=""><br class=""></div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>sil @bar : $<B> (B, B) -> () {</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>entry(%x : $*B, %y : $*B):</div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span> // Allocate a box with the layout for this closure, bound to this generic context</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span> %context = alloc_box bar.1<B></div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span> // Initialize the members</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span> %context.x = project_box %context, #x</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span> copy_addr %x to [init] %context.x</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span> %context.y = project_box %context, #y</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span> copy_addr %y to [init] %closure.y</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span> // Bind to the invocation function</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span> %closure = partial_apply @bar.1<B>(%closure)</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span> assign %closure to @escaper</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span> return</div><div class=""><span class="Apple-tab-span" style="white-space: pre;">        </span>}</div><div class=""> </div><div class=""><span class="Apple-tab-span" style="white-space:pre">        </span>sil @bar.1 : $<C> (@box bar.1<C>) { ... }</div><div class=""><br class=""></div><div class="">As a hack, we can guarantee in the polymorphic convention that a generic box context parameter acts as a type metadata source for its generic parameters, similar to what we do with 'self' in class and protocol method signatures. Having an opening instruction might be a better long-term solution, though, and could make generic boxes also useful for representing copy-on-write existentials and GADT enum payloads. This is just a rough sketch of an idea; I'm open to other approaches. We've talked about tracking the SIL-level layout of structs, enums, and classes in SIL separate from the Swift level, so representing box layouts concretely feels consistent with that potential direction. OTOH, at this point, we'd be constructing a lot of SIL data structures for every closure.</div><div class=""><br class=""></div><div class="">-Joe</div></body></html>