Skip to content

Preliminary support for _specialize(exported: true, ...) #32657

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

aschwaighofer
Copy link
Contributor

@aschwaighofer aschwaighofer commented Jul 1, 2020

Adds support for _specialize(exported: true, ...)

This attribute allows to define a pre-specialized entry point of a
generic function in a library.

The following definition provides a pre-specialized entry point for
genericFunc(_:) for the parameter type Int that clients of the
library can call.

@_specialize(exported: true, where T == Int)
public func genericFunc<T>(_ t: T) { ... }

Pre-specializations of internal @inlinable functions are allowed.

@usableFromInline
internal struct GenericThing<T> {
  @_specialize(exported: true, where T == Int)
  @inlinable
  internal func genericMethod(_ t: T) {
  }
}

There is syntax to pre-specialize a method from a different module.

import ModuleDefiningGenericFunc

@_specialize(exported: true, target: genericFunc(_:), where T == Double)
func prespecialize_genericFunc(_ t: T) { fatalError("dont call") }

Specially marked extensions allow for pre-specialization of internal
methods accross module boundries (respecting @inlinable and
@usableFromInline).

import ModuleDefiningGenericThing
public struct Something {}

@_specializeExtension
extension GenericThing {
  @_specialize(exported: true, target: genericMethod(_:), where T == Something)
  func prespecialize_genericMethod(_ t: T) { fatalError("dont call") }
}

Adds support for marking a _specialize attribute as SPI

  @_specialize(exported: true, spi: SPIGroupName, where T == Int)
  public func myFunc() { } 

The specialized entry point is only visible for modules that import
using _spi(SPIGroupName) import ModuleDefiningMyFunc .

rdar://64993425

Copy link
Contributor

@slavapestov slavapestov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool! Can this eventually be used to generate SwiftOnoneSupport too?

@@ -221,6 +228,12 @@ bool SILDeclRef::isImplicit() const {
}

SILLinkage SILDeclRef::getLinkage(ForDefinition_t forDefinition) const {

// Prespecializations are public.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only specializations of public symbols though, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pre-specialization are intentionally always public.

For example, there is going to be a public entry point for the specialized version of:

@_alwaysEmitIntoClient
@_specialize(exported: true, where T == Int)
internal func testGeneric<T>(t: T) {
  print(t)
}

@@ -151,6 +153,8 @@ struct SILDeclRef {
/// The derivative function identifier.
AutoDiffDerivativeFunctionIdentifier *derivativeFunctionIdentifier = nullptr;

GenericSignature specializedSignature;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we stick this in a union with derivativeFunctionIdentifier to save space?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point.

@@ -631,9 +631,6 @@ bool Parser::parseSpecializeAttributeArguments(
if (ParamLabel == "exported") {
auto trueLoc = Tok.getLoc();
bool isTrue = consumeIf(tok::kw_true);
if (isTrue) {
diagnose(trueLoc, diag::attr_specialize_export_true_no_op);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can remove this diagnostic now

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did.

// use the same set of generic parameters, i.e. each generic
// parameter should be mapped to itself.
for (auto GP : CalleeGenericSig->getGenericParams()) {
CalleeInterfaceToSpecializedInterfaceMapping[GP] = Type(GP);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you really need CalleeInterfaceToSpecializedInterfaceMapping at all then? The lookup() below can be replaced with just 'type' itself, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Likely. I have copied this code from the FunctionSignaturePartialSpecializer and have not gone back to simplify it.

: TargetModule(targetModule), isWholeModule(isWholeModule),
isPrespecialization(isPrespecialization) {
Serialized =
this->isPrespecialization ? IsNotSerialized : Callee->isSerialized();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't we serialize the pre-specialization if the original function is serialized?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intentional. Pre-specialization as implemented in this patch get their own symbol/mangling to make them easily identifiable for tooling. The intent of exported pre-specializations is to reduce code size. When using the pre-specializated symbol I don't want the code to be available in the client module for further optimization.

@aschwaighofer
Copy link
Contributor Author

Can this eventually be used to generate SwiftOnoneSupport too?

I think so.

@shahmishal
Copy link
Member

Please update the base branch to main by Oct 5th otherwise the pull request will be closed automatically.

  • How to change the base branch: (Link)
  • More detail about the branch update: (Link)

@aschwaighofer aschwaighofer force-pushed the wip_prespecialize_exported branch from efb616e to 76cd65a Compare October 1, 2020 14:09
@aschwaighofer aschwaighofer changed the base branch from master to main October 1, 2020 14:10
@aschwaighofer aschwaighofer force-pushed the wip_prespecialize_exported branch 4 times, most recently from ecc4510 to 5a39f76 Compare October 6, 2020 21:42
@aschwaighofer aschwaighofer force-pushed the wip_prespecialize_exported branch 2 times, most recently from e0a4d8f to f7c31a1 Compare October 9, 2020 17:07
@aschwaighofer aschwaighofer changed the title WIP: Preliminary support for _specialize(exported: true, ...) Preliminary support for _specialize(exported: true, ...) Oct 9, 2020
@aschwaighofer
Copy link
Contributor Author

@swift-ci Please test

@swift-ci
Copy link
Contributor

swift-ci commented Oct 9, 2020

Build failed
Swift Test OS X Platform
Git Sha - 4ec70aa7568dea343dbccea00b872c3073c71e5b

This attribute allows to define a pre-specialized entry point of a
generic function in a library.

The following definition provides a pre-specialized entry point for
`genericFunc(_:)` for the parameter type `Int` that clients of the
library can call.

```
@_specialize(exported: true, where T == Int)
public func genericFunc<T>(_ t: T) { ... }
```

Pre-specializations of internal `@inlinable` functions are allowed.

```
@usableFromInline
internal struct GenericThing<T> {
  @_specialize(exported: true, where T == Int)
  @inlinable
  internal func genericMethod(_ t: T) {
  }
}
```

There is syntax to pre-specialize a method from a different module.

```
import ModuleDefiningGenericFunc

@_specialize(exported: true, target: genericFunc(_:), where T == Double)
func prespecialize_genericFunc(_ t: T) { fatalError("dont call") }

```

Specially marked extensions allow for pre-specialization of internal
methods accross module boundries (respecting `@inlinable` and
`@usableFromInline`).

```
import ModuleDefiningGenericThing
public struct Something {}

@_specializeExtension
extension GenericThing {
  @_specialize(exported: true, target: genericMethod(_:), where T == Something)
  func prespecialize_genericMethod(_ t: T) { fatalError("dont call") }
}
```

rdar://64993425
```
  @_specialize(exported: true, spi: SPIGroupName, where T == Int)
  public func myFunc() { }
```

The specialized entry point is only visible for modules that import
using `_spi(SPIGroupName) import ModuleDefiningMyFunc `.

rdar://64993425
@aschwaighofer
Copy link
Contributor Author

Please test with following PR:
swiftlang/swift-syntax#241

@swift-ci Please test

@aschwaighofer aschwaighofer marked this pull request as ready for review October 12, 2020 20:32
@aschwaighofer
Copy link
Contributor Author

@swift-ci Please test source compatibility

@aschwaighofer
Copy link
Contributor Author

The compiler failure in the source compatibility run also occurs without this change in: https://ci.swift.org/job/swift-main-source-compat-suite/5513/artifact/swift-source-compat-suite/FAIL_swift-nio-ssl_5.0_BuildSwiftPackage.log

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants