Pitch: Cross-module inlining and specialization

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.

Private @inlinable functions are useful. For example, you might want a helper function to be inlined into a public @inlinable function for performance, but you don't want that helper function to be called by outside clients directly.

The restriction would then be "inlinable declarations can only reference other public declarations and other @inlinable non-public declarations". This relaxation would have no affect on ABI: only @inlinable things could be inlined, and only public things could be ultimately referenced by inlined things.

Having said that, it should be fine to use "public @inlinable only" for now and consider relaxing the restriction later.

···

On Oct 2, 2017, at 1:31 PM, Slava Pestov via swift-evolution <swift-evolution@swift.org> wrote:
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.

--
Greg Parker gparker@apple.com <mailto:gparker@apple.com> Runtime Wrangler

Very much looking forward to this. Any possibility of rolling in some
version (ha) of @_versioned so that @inlinable functions can reference
internal declarations?

···

On Mon, Oct 2, 2017 at 3:31 PM, Slava Pestov via swift-evolution < swift-evolution@swift.org> 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
   - 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 false
otherwise:

@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

I am hugely in favor of an @inlinable attribute, and I look forward to its
arrival with relish!

Some feedback:

1. I think “inlinable” is the right spelling: it indicates that something
is *able* to be inlined.

2. If I want to pass an @inlinable function as an argument (say, to map or
filter) can I do so directly or must I use a closure which calls the
inlinable function?

3. Even though @inlinable will have no effect on declarations which are not
public, we should still allow it to be placed there. That way when the
access level is later changed to be public, the attribute is already where
it should be. This is similar to why we permit, eg., members of an internal
type to be declared public, which was discussed and decided previously on
Swift Evolution.

Other than that the proposal looks great, thanks for writing it up.

Nevin

This is something I’ve been waiting for for a long time and I’m glad this
is finally becoming a reality. This is a big step forward for anyone
interested in seeing Swift get core “almost-stdlib” libraries.

···

On Mon, Oct 2, 2017 at 3:31 PM, Slava Pestov via swift-evolution < swift-evolution@swift.org> 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
   - 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 false
otherwise:

@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

This is a great proposal, I’m a strong supporter, but have one question and one strong push back:

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.

The major question I have is “why yet another attribute”. The thread about exhaustive/extensible enums is similarly proposing introducing another one-off way to be enums fragile, and this is directly related just for function-like things.

I’d love to see rationale in the proposal for why you’re not taking this in one of these directions:

1) Why not another level of access control? There is a reasonable argument that what you’re doing is making something “more public than public” or that you’re making the “body also public”. I’m not strongly in favor of this design approach, but if you agree, the doc should explain why you’re not in favor of it.

2) Why can’t we have a single Swift-wide concept that unifies all of the resilience ideas under a single umbrella like “fragile” - which indicates that the body of a declaration is knowable to clients? There is a very reasonable holistic design where “fragile public func” makes its body inlinable, and “fragile enum” similarly makes the cases knowable to the client (thus making exhaustive switching a possibility). I am strongly in favor of this approach.

In any case, even if you’re opposed to these approaches, I’d love for the “alternatives considered” section to indicate what the objection is. I am really very concerned that you’re causing a keyword/attribute explosion and conceptual complexity by adding too many small things to individual parts of the language. We would ideally have a simple and holistic solution to resilience.

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.

This semantic doesn’t make sense to me, and I think we need to change it. I think we are better served with the semantics of “the body may be inlined, but doesn’t have to.” This corresponds to available_externally linkage in LLVM, <http://llvm.org/docs/LangRef.html#linkage-types> and is provided by GNU89 "extern inline".

Here are 5 reasons:

1) the current spelling: "@inlinable” implies that the body “may” be inlined, not that it “must” be inlined.

2) there are lots of reasons why the compiler may not *want* to inline the body of a declaration, including wanting fast debug builds, “optimizing for size” builds, or cost heuristics that lead the compiler to believe that there is no gain for inlining the body of a function in some context.

3) If the symbol is always guaranteed to be present, adding @inlinable is an ABI preserving change. I think that this is also really important because it reflects a natural evolution of code: in R1 of a module’s public release, a symbol my be public, but after numerous releases, it may be decided that it is stable enough to make “inlinable”.

4) Certain declarations *have* to be emitted anyway, e.g. an @inlinable open method on a class can’t actually be inlined in most cases, because the call is dynamicly dispatched.

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.

5) It eliminates this complexity.

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.

The writing should be clarified, because there are multiple concepts going on here, including GNU89’s notion of inline, C99’s notion of inline (aka extern inline), and static inline, each with overlapping but and confusingly different semantics.

-Chris

···

On Oct 2, 2017, at 1:31 PM, Slava Pestov via swift-evolution <swift-evolution@swift.org> wrote:

How does @inlinable relate to the @_specialize attribute described in https://github.com/apple/swift/pull/6797 (original swift-evolution post about it: https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20160314/001449.html)?

Here's how I understand it:

@_specialize emits the specialized generic code (for the types specified in the @_specialize declaration) in the module binary (module A). The downside is that the author of module A has to decide for which types they want to emit specialized code. Clients of module A that need specialization for other types are out of luck.

@inlinable enables the optimizer to emit specialized code in the binaries that import module A. It shifts the decision what to specialize where it belongs (and where it can be made).

Is this understanding correct? Will @inlinable cover everything we'd need @_specialize for, or is the plan to make @_specialize public as well in the future?

Ole

···

On 02.10.17 22:31, Slava Pestov via swift-evolution 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 |map|method 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 |false|otherwise:

>@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, |didSet|and |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 inline|functions 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 inline|functions 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

Hi Slava,

What is the impact on debugging? Will we emit a non-inline version of the function into the client at Onone? Will it be impossible to call from a debugger with optimization enabled? I've been burned by this in C++.

Ben

···

On Oct 2, 2017, at 1:31 PM, Slava Pestov via swift-evolution <swift-evolution@swift.org> 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

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 <spestov@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.

Thanks for taking a look.

@_versioned is not something we want to keep in the long term. The original idea was to allow @available annotations to be put on internal declarations, which would have the effect of giving their symbols public linkage. I think a follow-up proposal could introduce this feature as well as “conditionally available inlinable”, which I describe in document below. However if people feel strongly we could roll both of them into this proposal, but it would require some more implementation work.

Slava

···

On Oct 2, 2017, at 1:44 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Very much looking forward to this. Any possibility of rolling in some version (ha) of @_versioned so that @inlinable functions can reference internal declarations?

On Mon, Oct 2, 2017 at 3:31 PM, Slava Pestov via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> 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 <>
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 <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Today's @available can't be the thing that makes symbols public, since it's also used to affect the availability context for private symbols. The one described in the library evolution document is specifically about marking something available with respect to the current module. So we would want another spelling.

(But it definitely shouldn't be "_versioned", which doesn't mean anything if we don't have "versions".)

Jordan

···

On Oct 2, 2017, at 13:47, Slava Pestov via swift-evolution <swift-evolution@swift.org> wrote:

Thanks for taking a look.

@_versioned is not something we want to keep in the long term. The original idea was to allow @available annotations to be put on internal declarations, which would have the effect of giving their symbols public linkage. I think a follow-up proposal could introduce this feature as well as “conditionally available inlinable”, which I describe in document below. However if people feel strongly we could roll both of them into this proposal, but it would require some more implementation work.

Slava

On Oct 2, 2017, at 1:44 PM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> wrote:

Very much looking forward to this. Any possibility of rolling in some version (ha) of @_versioned so that @inlinable functions can reference internal declarations?

On Mon, Oct 2, 2017 at 3:31 PM, Slava Pestov via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> 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 <>
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 <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Thanks for taking a look!

I am hugely in favor of an @inlinable attribute, and I look forward to its arrival with relish!

Some feedback:

1. I think “inlinable” is the right spelling: it indicates that something is *able* to be inlined.

Yeah, it seems this is the spelling we’re going to go with.

2. If I want to pass an @inlinable function as an argument (say, to map or filter) can I do so directly or must I use a closure which calls the inlinable function?

You can pass it directly. From the viewpoint of code that uses an inlinable function, it behaves exactly the same as one that does not have the attribute.

3. Even though @inlinable will have no effect on declarations which are not public, we should still allow it to be placed there. That way when the access level is later changed to be public, the attribute is already where it should be. This is similar to why we permit, eg., members of an internal type to be declared public, which was discussed and decided previously on Swift Evolution.

This is an interesting point. Do you think the attribute should be completely ignored, or should the restrictions on references to non-public things, etc still be enforced?

Slava

···

On Oct 2, 2017, at 2:19 PM, Nevin Brackett-Rozinsky <nevin.brackettrozinsky@gmail.com> wrote:

Other than that the proposal looks great, thanks for writing it up.

Nevin

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.

Private @inlinable functions are useful. For example, you might want a helper function to be inlined into a public @inlinable function for performance, but you don't want that helper function to be called by outside clients directly.

The restriction would then be "inlinable declarations can only reference other public declarations and other @inlinable non-public declarations". This relaxation would have no affect on ABI: only @inlinable things could be inlined, and only public things could be ultimately referenced by inlined things.

Having said that, it should be fine to use "public @inlinable only" for now and consider relaxing the restriction later.

That’s what @_versioned is currently for. You can define a function as @_versioned @_inlineable and reference it from other public inlinable functions, without making it visible to AST-level lookup.

While I would prefer to punt formalizing this to a future proposal, we can still design the correct spelling and behavior of @_versioned here (I’m pretty sure the current attribute isn’t it).

Slava

···

On Oct 2, 2017, at 2:20 PM, Greg Parker <gparker@apple.com> wrote:

On Oct 2, 2017, at 1:31 PM, Slava Pestov via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

--
Greg Parker gparker@apple.com <mailto:gparker@apple.com> Runtime Wrangler

Hmm, good question!

I rather like the idea Greg Parker put forth, where non-public @inlinable
items can be used by public @inlinable ones, which implies that the
restrictions should indeed still apply—something @inlinable can only
reference public or @inlinable things.

Nevin

···

On Mon, Oct 2, 2017 at 5:21 PM, Slava Pestov <spestov@apple.com> wrote:

Thanks for taking a look!

> On Oct 2, 2017, at 2:19 PM, Nevin Brackett-Rozinsky < > nevin.brackettrozinsky@gmail.com> wrote:
> 3. Even though @inlinable will have no effect on declarations which are
not public, we should still allow it to be placed there. That way when the
access level is later changed to be public, the attribute is already where
it should be. This is similar to why we permit, eg., members of an internal
type to be declared public, which was discussed and decided previously on
Swift Evolution.

This is an interesting point. Do you think the attribute should be
completely ignored, or should the restrictions on references to non-public
things, etc still be enforced?

Thanks for the review!

This is a great proposal, I’m a strong supporter, but have one question and one strong push back:

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.

The major question I have is “why yet another attribute”. The thread about exhaustive/extensible enums is similarly proposing introducing another one-off way to be enums fragile, and this is directly related just for function-like things.

I’d love to see rationale in the proposal for why you’re not taking this in one of these directions:

1) Why not another level of access control? There is a reasonable argument that what you’re doing is making something “more public than public” or that you’re making the “body also public”. I’m not strongly in favor of this design approach, but if you agree, the doc should explain why you’re not in favor of it.

2) Why can’t we have a single Swift-wide concept that unifies all of the resilience ideas under a single umbrella like “fragile” - which indicates that the body of a declaration is knowable to clients? There is a very reasonable holistic design where “fragile public func” makes its body inlinable, and “fragile enum” similarly makes the cases knowable to the client (thus making exhaustive switching a possibility). I am strongly in favor of this approach.

In any case, even if you’re opposed to these approaches, I’d love for the “alternatives considered” section to indicate what the objection is. I am really very concerned that you’re causing a keyword/attribute explosion and conceptual complexity by adding too many small things to individual parts of the language. We would ideally have a simple and holistic solution to resilience.

I agree with that keyword/attribute explosion is a concern. We also plan on submitting a proposal to add a @fixedContents attribute for structs (currently implemented as @_fixed_layout) which enables more efficient access patterns in resilient code, for example direct access of stored properties, at the cost of preventing new stored properties from being added in a binary-compatible manner. So we would have ‘nonexhaustive’ enums, @fixedContents structs, and @inlinable functions/properties/initializers.

Perhaps it makes sense to have a single ‘fragile’ keyword replace @fixedContents and @inlinable. For enums, ‘nonexhaustive’ does “feel” a bit different, so perhaps it makes sense for it to be its own thing. From an implementation perspective it doesn’t really matter if we have multiple attributes or one, so of course I’d prefer to go with the approach that makes the most sense to people language design-wise.

This semantic doesn’t make sense to me, and I think we need to change it. I think we are better served with the semantics of “the body may be inlined, but doesn’t have to.”

That is the effect it has today. The decision to inline or not is made by the optimizer, and @inlinable doesn’t change anything here; it makes the body available if the optimizer chooses to do so.

2) there are lots of reasons why the compiler may not *want* to inline the body of a declaration, including wanting fast debug builds, “optimizing for size” builds, or cost heuristics that lead the compiler to believe that there is no gain for inlining the body of a function in some context.

Right.

3) If the symbol is always guaranteed to be present, adding @inlinable is an ABI preserving change. I think that this is also really important because it reflects a natural evolution of code: in R1 of a module’s public release, a symbol my be public, but after numerous releases, it may be decided that it is stable enough to make “inlinable”.

4) Certain declarations *have* to be emitted anyway, e.g. an @inlinable open method on a class can’t actually be inlined in most cases, because the call is dynamicly dispatched.

Yes. The function is still emitted into the library, but the symbol does not have public linkage. If we make the change to give the attribute ‘always emit into client’ semantics, a client can still reference the function without inlining it, for whatever reason, either because it cannot be (you’re using the function as a value) or the optimizer decides that it is not profitable to inline it. However the client binary would have to emit its own copy of the function (again, with non-public linkage).

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.

5) It eliminates this complexity.

Yeah. In fact the current implementation of @inlinable does not change the function’s linkage — it remains public. It just enables SIL serialization for the body. I recall the standard library folks had good reasons to prefer the ‘always emit into client’ behavior though; maybe Jordan or Dave can explain.

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.

The writing should be clarified, because there are multiple concepts going on here, including GNU89’s notion of inline, C99’s notion of inline (aka extern inline), and static inline, each with overlapping but and confusingly different semantics.

Can you suggest some better wording for this section? I admit I don’t know C very well compared to some of you here, having never implemented my own C compiler or participated in standards committee. :slight_smile:

Slava

···

On Oct 2, 2017, at 10:57 PM, Chris Lattner <clattner@nondot.org> wrote:

On Oct 2, 2017, at 1:31 PM, Slava Pestov via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

-Chris

Oh, I forgot to address this point. I’m against this because even though this proposal says inlinable requires public, it is really orthogonal to public, because eventually we will have something like the current @_versioned attribute, where internal (or even private, as some have suggested) functions can be “public ABI”. For example,

@_versioned func myInternalDetails() {
  // …
}

@inlinable
public func myPublicFunction() {
  myInternalDetails() // I can reference this from an inlinable function, but users can’t call it directly
}

Slava

···

On Oct 2, 2017, at 10:57 PM, Chris Lattner <clattner@nondot.org> wrote:

1) Why not another level of access control? There is a reasonable argument that what you’re doing is making something “more public than public” or that you’re making the “body also public”. I’m not strongly in favor of this design approach, but if you agree, the doc should explain why you’re not in favor of it.

It wouldn't avoid the complexity, because we want the "non-ABI, always-emit-into-client" behavior for the standard library. For the soon-to-be-ABI-stable libraries where @inlinable even matters, such as the standard library and Apple SDK overlays, there's pretty much perfect overlap between things we want to inline and things we don't want to take up binary space and ABI surface in binaries, so the behavior Slava proposes seems like the right default. With these semantics, the only time an inlinable declaration needs an exported symbol is for backward deployment, and we're inevitably going to need "versioned" as a general backward deployment concept for other things.

-Joe

···

On Oct 2, 2017, at 10:58 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

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.

5) It eliminates this complexity.

I'm of two minds on this. So far, it has seemingly been the case that, for every kind of declaration, there's been exactly one resilience-related attribute, and with blurry enough vision you could say they're all really doing the same thing, "expose this declaration body directly to clients", which only makes sense as a concept for public API. If these properties hold, then sure, it might make sense to consider these all as one "extra-public" concept. On the other hand, we don't know yet whether these properties will hold in the long term as we're actively designing the facilities we need for ABI stability and resilience. From an incremental, iterative design perspective, I think it's wiser to keep them all separate attributes, even if it's a bit messy in the short term. Once the design stabilizes, we can look at whether it makes sense to fold related concepts together. It's easier to fold different things together after the fact then to try to separate things after they've been mixed together.

-Joe

···

On Oct 2, 2017, at 10:58 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

The major question I have is “why yet another attribute”. The thread about exhaustive/extensible enums is similarly proposing introducing another one-off way to be enums fragile, and this is directly related just for function-like things.

I’d love to see rationale in the proposal for why you’re not taking this in one of these directions:

1) Why not another level of access control? There is a reasonable argument that what you’re doing is making something “more public than public” or that you’re making the “body also public”. I’m not strongly in favor of this design approach, but if you agree, the doc should explain why you’re not in favor of it.

2) Why can’t we have a single Swift-wide concept that unifies all of the resilience ideas under a single umbrella like “fragile” - which indicates that the body of a declaration is knowable to clients? There is a very reasonable holistic design where “fragile public func” makes its body inlinable, and “fragile enum” similarly makes the cases knowable to the client (thus making exhaustive switching a possibility). I am strongly in favor of this approach.

This is unduly restrictive; @_versioned (despite being the wrong spelling)
is what we want here. To be callable from an inlinable function, internal
things need only be visible in terms of public ABI, not necessarily
inlinable, just as public things need only be public and not necessarily
inlinable.

···

On Mon, Oct 2, 2017 at 16:37 Nevin Brackett-Rozinsky via swift-evolution < swift-evolution@swift.org> wrote:

On Mon, Oct 2, 2017 at 5:21 PM, Slava Pestov <spestov@apple.com> wrote:

Thanks for taking a look!

> On Oct 2, 2017, at 2:19 PM, Nevin Brackett-Rozinsky < >> nevin.brackettrozinsky@gmail.com> wrote:
> 3. Even though @inlinable will have no effect on declarations which are
not public, we should still allow it to be placed there. That way when the
access level is later changed to be public, the attribute is already where
it should be. This is similar to why we permit, eg., members of an internal
type to be declared public, which was discussed and decided previously on
Swift Evolution.

This is an interesting point. Do you think the attribute should be
completely ignored, or should the restrictions on references to non-public
things, etc still be enforced?

Hmm, good question!

I rather like the idea Greg Parker put forth, where non-public @inlinable
items can be used by public @inlinable ones, which implies that the
restrictions should indeed still apply—something @inlinable can only
reference public or @inlinable things.

Nevin
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Also remember we have the @inline(never) attribute. It’s not underscored so I’m assuming it’s an “official” part of the language. And "@inline(never) @inlinable" is a perfectly valid combination — it serializes the SIL for the function body, and while inlining it is prohibited, it is still subject to specialization, function signature optimizations, etc.

Slava

···

On Oct 2, 2017, at 11:11 PM, Slava Pestov via swift-evolution <swift-evolution@swift.org> wrote:

This semantic doesn’t make sense to me, and I think we need to change it. I think we are better served with the semantics of “the body may be inlined, but doesn’t have to.”

That is the effect it has today. The decision to inline or not is made by the optimizer, and @inlinable doesn’t change anything here; it makes the body available if the optimizer chooses to do so.