SE-0496: `@inline(always)` Attribute

I see the SE doc mentioned "Interaction with @inlinable".

Can we add more information or compare the old @inline(__always)/ @_alwaysEmitIntoClient and the current proposed @inline(always)?

I have some vague contrasts. It would be great if we could clearly reflect them in the SE document.

I think this is an interesting question.

The consequence of requiring acknowledgement by addition of an attribute is that adding an @inline(always) attribute becomes a source breaking change.
For public declaration the author has very limited control over all users of the declaration (out of module uses).

I think if we wanted to require acknowledging potentially non-inlinable uses this should be under an opt-in "performance mode" (project global) or a "(scoped) performance annotation" and should be done as a separate proposal.
See the @_manualOwnership attribute being explored here: introduce @_manualOwnership performance attribute by kavon · Pull Request #81858 · swiftlang/swift · GitHub.

The language steering group asked for any mention of @_alwaysEmitIntoClient to be removed.

There are two concepts that often get intermingled when talking about attributes related to forms of "inlining" in swift.

  • There is the ability to "see" the implementation of a function (controlled by the attribute @inlinable and also by@_alwaysEmitIntoClient) SE-0193. Access to the function body allows for emitting a copy of the function into another module.
  • The optimization of expanding the function body into a call site (@inline(__always)).

The optimization of expanding the function body ("inlining") can only proceed if the compiler has access to the function body.
"Inlining" is not the only optimization enabled by access to the function body. Specialization of generic functions is another important that is enabled by having access to the body.

// Module A
@inlinable
public func a<T>(_ t: T) {...}

// Module B
import A
public func b() {
   a(1)
   // can be replaced by a call to
   a_specialized_Int(1)
}
// compiler generates specialziation in module B:
func a_specialized_Int(_ i: Int) { ... }

The existing @inline(__always) attribute will cause inlining when the body is available but silently ignored if that is not the case.

// Module A
@inline(__always)
public func a() {...}

// Module B
import A
public func b() {
   a() // not always inlined if `a()` is not marked `@inlinable`
}

In that sense, it is an optimization hint that the compiler can take into account or ignore it.

3 Likes