Skip to content
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

Specify how function pointers will handle context registers #309

Merged
6 changes: 3 additions & 3 deletions proposed/swift-interop.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ We plan to split the work into at least three separate components. There will be

#### Calling Conventions

Swift has two calling conventions at its core, the "Swift" calling convention, and the "SwiftAsync" calling convention. We will begin by focusing on the "Swift" calling convention, as it is the most common. The Swift calling convention has a few elements that we need to contend with. Primarily, it allows passing arguments in more registers. Additionally, it has two dedicated registers for specific components of the calling convention, the self register and the error register. The runtime support for this calling convention must support all of these features. The additional registers per argument is relatively easy to support, as each of our compilers must already have some support for this concept to run on Linux or Apple platforms today. We have a few options for the remaining two features we need to support.
Swift has two calling conventions at its core, the "Swift" calling convention, and the "SwiftAsync" calling convention. We will begin by focusing on the "Swift" calling convention, as it is the most common. The Swift calling convention has a few elements that we need to contend with. Primarily, it allows passing arguments in more registers. Additionally, it has two dedicated registers for specific components of the calling convention, the self register and the error register. The runtime support for this calling convention must support all of these features. The additional registers per argument is relatively easy to support, as each of our compilers must already have some support for this concept to run on Linux or Apple platforms today. We have a few options for the remaining two features we need to support. The `CallConvSwift` modifier can be applied in all contexts where other existing calling convention modifiers can be used. This means it can be used with UnmanagedCallConvAttribute for P/Invoke (`[UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })]`), UnmanagedCallersOnlyAttribute.CallConv for reverse P/Invoke (`[UnmanagedCallersOnly(CallConvs = new Type[] { typeof(CallConvSwift) })]`), and with unmanaged function pointers (`delegate* unmanaged[Swift]<>`).
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved

The "SwiftAsync" calling convention has an additional "async context" register. When a "SwiftAsync" function is called by a "SwiftAsync" function, it must be tail-called. A function that uses this calling convention also pops the argument area. In LLVM-IR, this calling convention is referred to as `swifttailcc`. In Clang, this convention is specified as `swiftasynccall`. Additionally, the "SwiftAsync" calling convention does not have the error register and must not have a return value. See the [LLVM-IR language reference](https://github.com/llvm/llvm-project/blob/54fe7ef70069a48c252a7e1b0c6ed8efda0bc440/llvm/docs/LangRef.rst#L452) and the [Clang attribute reference](https://clang.llvm.org/docs/AttributeReference.html#swiftasynccall) for an explaination of the calling convention.

Expand All @@ -57,7 +57,7 @@ We have selected the following option for supporting the Self register in the ca

1. Use specially-typed argument with a type, `SwiftSelf`, to represent which parameter should go into the self register.

We will provide a `SwiftSelf` type to specify "this argument goes in the self register". Specifying the type twice in a signature would generate an `InvalidProgramException`. This would allow the `self` argument to be specified anywhere in the argument list. Alternatively, many sections of the Swift ABI documentation, as well as the Clang docs refer to this parameter as the "context" argument instead of the "self" argument, so an alternative name could be `SwiftContext`.
We will provide a `SwiftSelf` type to specify "this argument goes in the self register". Specifying the type twice in a signature would generate an `InvalidProgramException`. This would allow the `self` argument to be specified anywhere in the argument list. Alternatively, many sections of the Swift ABI documentation, as well as the Clang docs refer to this parameter as the "context" argument instead of the "self" argument, so an alternative name could be `SwiftContext`. Methods annotated with `UnmanagedCallersOnly` will be treated as Swift-like functions capable of handling context registers. The value stored in the self register will be loaded into the `SwiftSelf` argument.
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved

For reference, explicitly declaring a function with the Swift or SwiftAsync calling conventions in Clang requires the "context" argument, the value that goes in the "self" register, as the last parameter or the penultimate parameter followed by the error parameter.

Expand All @@ -80,7 +80,7 @@ We have selected an approach for handling the error register in the Swift callin

1. Use a special type named something like `SwiftError*` to indicate the error parameter

This approach expresses that the to-be-called Swift function uses the Error Register in the signature and they both require signature manipulation in the JIT/AOT compilers. Like with `SwiftSelf`, we would throw an `InvalidProgramException` for a signature with multiple `SwiftError` parameters. We use a pointer-to-`SwiftError` type to indicate that the error register is a by-ref/out parameter. We don't use managed pointers as our modern JITs can reason about unmanaged pointers well enough that we do not end up losing any performance taking this route. The `UnmanagedCallersOnly` implementation will require a decent amount of JIT work to emulate a local variable for the register value, but we have prior art in the Clang implementation of the Swift error register that we can fall back on.
This approach expresses that the to-be-called Swift function uses the Error Register in the signature and they both require signature manipulation in the JIT/AOT compilers. Like with `SwiftSelf`, we would throw an `InvalidProgramException` for a signature with multiple `SwiftError` parameters. We use a pointer-to-`SwiftError` type to indicate that the error register is a by-ref/out parameter. We don't use managed pointers as our modern JITs can reason about unmanaged pointers well enough that we do not end up losing any performance taking this route. The `UnmanagedCallersOnly` implementation will require a decent amount of JIT work to emulate a local variable for the register value, but we have prior art in the Clang implementation of the Swift error register that we can fall back on. Methods annotated with `UnmanagedCallersOnly` will pass the `SwiftError*` value in the error register.

Additionally, we have selected this design as this provides consistency with the self register and async context register handling, discussed below.

Expand Down
Loading