Hi all, Summarizing the thread so far, I see there were three main points of discussion:
- The name itself, @inlinable, is causing some confusion. Chris suggested a more general name, like @fragile. A few other possible names were discussed. I don’t think we’ve decided on the best name yet so I’m open to suggestions here. I personally like the name @fragile if nobody has objections to it, at the very least because it does not mislead the reader into thinking inlining is going to take place. - Several people would like to see @_versioned be formalized (with a better name) and added to the proposal. My preference would be to do this in a separate proposal soon after, but I’d be willing to entertain the possibility of rolling it into this one. - The core point is whether inlinable functions should retain a public entry point in the original module, or if the compiler should always be required to emit a copy (whether inlined or not) into the client module. Joe Groff made a number of good arguments in favor of the latter. Jordan Rose also pointed out offline that we should not allow @inlinable to be applied to dynamic methods, since there’s no way for a client to devirtualize across an objc_msgSend boundary, so the attribute would have no effect in this case. I don’t expect this change to be controversial. I’d like to get the proposal into a form suitable for publishing as a draft pull request against the evolution repository. Do we have some general agreement on resolving the above issues, or does anyone still have concerns or further suggestions? Slava > On Oct 2, 2017, at 1:31 PM, Slava Pestov <spes...@apple.com> wrote: > > Hi all, > > Here is a draft proposal that makes public a feature we’ve had for a while. > Let me know what you think! > > Cross-module inlining and specialization ("@inlinable") > Proposal: SE-NNNN <file:///Users/slava/NNNN-filename.md> > Authors: Slava Pestov <https://github.com/slavapestov>, Jordan Rose > <https://github.com/jrose-apple> > Review Manager: TBD > Status: Initial pitch > Implementation: Already implemented as an underscored attribute @_inlineable > Introduction > We propose introducing an @inlinable attribute which exports the body of a > function as part of a module's interface, making it available to the > optimizer when referenced from other modules. > > Motivation > One of the top priorities of the Swift 5 release is a design and > implementation of the Swift ABI. This effort consists of three major tasks: > > Finalizing the low-level function calling convention, layout of data types, > and various runtime data structures. The goal here is to maintain > compatibility across compiler versions, ensuring that we can continue to make > improvements to the Swift compiler without breaking binaries built with an > older version of the compiler. > > Implementing support for library evolution, or the ability to make certain > source-compatible changes, without breaking binary compatibility. Examples of > source-compatible changes we are considering include adding new stored > properties to structs and classes, removing private stored properties from > structs and classes, adding new public methods to a class, or adding new > protocol requirements that have a default implementation. The goal here is to > maintain compatibility across framework versions, ensuring that framework > authors can evolve their API without breaking binaries built against an older > version of the framework. For more information about the resilience model, > see the library evolution document > <https://github.com/apple/swift/blob/master/docs/LibraryEvolution.rst> in the > Swift repository. > > Stabilizing the API of the standard library. The goal here is to ensure that > the standard library can be deployed separately from client binaries and > frameworks, without forcing recompilation of existing code. > > All existing language features of Swift were designed with these goals in > mind. In particular, the implementation of generic types and functions relies > on runtime reified types to allow separate compilation and type checking of > generic code. > > Within the scope of a single module, the Swift compiler performs very > aggressive optimization, including full and partial specialization of generic > functions, inlining, and various forms of interprocedural analysis. > > On the other hand, across module boundaries, runtime generics introduce > unavoidable overhead, as reified type metadata must be passed between > functions, and various indirect access patterns must be used to manipulate > values of generic type. We believe that for most applications, this overhead > is negligible compared to the actual work performed by the code itself. > > However, for some advanced use cases, and in particular for the standard > library, the overhead of runtime generics can dominate any useful work > performed by the library. Examples include the various algorithms defined in > protocol extensions of Sequence and Collection, for instance the mapmethod of > the Sequence protocol. Here the algorithm is very simple and spends most of > its time manipulating generic values and calling to a user-supplied closure; > specialization and inlining can completely eliminate the algorithm of the > higher-order function call and generate equivalent code to a hand-written > loop manipulating concrete types. > > We would like to annotate such functions with the @inlinable attribute. This > will make their bodies available to the optimizer when building client code; > on the other hand, calling such a function will cause it to be emitted into > the client binary, meaning that if a library were to change the definition of > such a function, only binaries built against the newer version of library > will use the new definition. > > Proposed solution > The @inlinable attribute causes the body of a function to be emitted as part > of the module interface. For example, a framework can define a rather > impractical implementation of an algorithm which returns true if all elements > of a sequence are equal or if the sequence is empty, and falseotherwise: > > @inlinable public func allEqual<T>(_ seq: T) -> Bool > where T : Sequence, T.Element : Equatable { > var iter = seq.makeIterator() > guard let first = iter.next() else { return true } > > func rec(_ iter: inout T.Iterator) -> Bool { > guard let next = iter.next() else { return true } > return next == first && rec(&iter) > } > > return rec(&iter) > } > A client binary built against this framework can call allEqual() and enjoy a > possible performance improvement when built with optimizations enabled, due > to the elimination of abstraction overhead. > > On the other hand, once the framework author comes to their senses and > implements an iterative solution to replace the recursive algorithm defined > above, the client binary cannot make use of the more efficient implementation > until recompiled. > > Detailed design > The new @inlinable attribute can only be applied to the following kinds of > declarations: > > Functions and methods > Subscripts > Computed properties > Initializers > Deinitializers > The attribute can only be applied to public declarations. This is because the > attribute only has an effect when the declaration is used from outside of the > module. Within a module, the optimizer can always rely on the function body > being available. > > For similar reasons, the attribute cannot be applied to local declarations, > that is, declarations nested inside functions or statements. However, local > functions and closure expressions defined inside public @inlinable functions > are always implicitly @inlinable. > > When applied to subscripts or computed properties, the attribute applies to > the getter, setter, didSetand willSet, if present. > > The compiler will enforce certain restrictions on bodies of inlinable > declarations: > > inlinable declarations cannot define local types. This is because all types > have a unique identity in the Swift runtime, visible to the language in the > form of the == operator on metatype values. It is not clear what it would > mean if two different libraries inline the same local type from a third > library, with all three libraries linked together into the same binary. This > becomes even worse if two different versions of the same inlinable function > appear inside the same binary. > > inlinable declarations can only reference other public declarations. This is > because they can be emitted into the client binary, and are therefore limited > to referencing symbols that the client binary can reference. > > Note: The restrictions enforced on the bodies of @inlinable declarations are > exactly those that we have in place on default argument expressions of public > functions in Swift 4. > > Source compatibility > The introduction of the @inlinable attribute is an additive change to the > language and has no impact on source compatibility. > > Effect on ABI stability > The introduction of the @inlinable attribute does not change the ABI of > existing declarations. However, adding @inlinable to an existing declaration > changes ABI, because the declaration will no longer have a public entry point > in the generated library. Removing @inlinable from an existing declaration > does not change ABI, because it merely introduces a new public symbol in the > generated library. > > We have discussed adding a "versioned @inlinable" variant that preserves the > public entry point for older clients, while making the declaration inlinable > for newer clients. This will likely be a separate proposal and discussion. > > Effect on API resilience > Because a declaration marked @inlinable is not part of the library ABI, > removing such a declaration is a binary-compatible, but source-incompatible > change. > > Any changes to the body of a declaration marked @inlinable should be > considered very carefully. As a general guideline, we feel that @inlinable > makes the most sense with "obviously correct" algorithms which manipulate > other data types abstractly through protocols, so that any future changes to > an @inlinable declaration are optimizations that do not change observed > behavior. > > Comparison with other languages > The closest language feature to the @inlinable attribute is found in C and > C++. In C and C++, the concept of a header file is similar to Swift's binary > swiftmodule files, except they are written by hand and not generated by the > compiler. Swift's public declarations are roughly analogous to declarations > whose prototypes appear in a header file. > > Header files mostly contain declarations without bodies, but can also declare > static inlinefunctions with bodies. Such functions are not part of the binary > interface of the library, and are instead emitted into client code when > referenced. As with @inlinable declarations, static inlinefunctions can only > reference other "public" declarations, that is, those that are defined in > other header files. > > Alternatives considered > One possible alterative would be to add a new compiler mode where all > declarations become implicitly @inlinable. > > However, such a compilation mode would not solve the problem of delivering a > stable ABI and standard library which can be deployed separately from user > code. We don't want all declaration bodies in the standard library to be > available to the optimizer when building user code. > > While such a feature might be useful for users who build private frameworks > that are always shipped together their application without resilience > concerns, we do not feel it aligns with our goals for ABI stability, and at > best it should be a separate discussion. > > For similar reasons, we do not feel that an "opt-out" attribute that can be > applied to declarations to mark them non-inlinable makes sense. > > We have also considered generalizing @inlinable to allow it to be applied to > entire blocks of declarations, for example at the level of an extension. As > we gain more experience with using this attribute in the standard library we > might decide this would be a useful addition, but we feel that for now, it > makes sense to focus on the case of a single inlinable declaration instead. > Any future generalizations can be introduced as additive language features. > > We originally used the spelling @inlineable for the attribute. However, we > settled on @inlinable for consistency with the Decodable and Encodable > protocols, which are named as they are and not Decodeable and Encodeable. > > Finally, we have considered some alternate spellings for this attribute. The > name @inlinable is somewhat of a misnomer, because nothing about it actually > forces the compiler to inline the declaration; it might simply generate a > concrete specialization of it, or look at the body as part of an > interprocedural analysis, or completely ignore the body. We have considered > @alwaysEmitIntoClient as a more accurate, but awkward, spelling of the > attribute's behavior. >
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution