-
Notifications
You must be signed in to change notification settings - Fork 5.3k
[mono] Add runtime support for Swift calling convention on x64 and arm64 #94764
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
kotlarmilos
merged 156 commits into
dotnet:main
from
kotlarmilos:poc/swift-interop-direct-pinvoke
Feb 6, 2024
Merged
Changes from 1 commit
Commits
Show all changes
156 commits
Select commit
Hold shift + click to select a range
5136aaf
Add README.md
kotlarmilos 674a156
Merge branch 'dotnet:main' into poc/swift-interop-direct-pinvoke
kotlarmilos 9fc9402
Update build steps
kotlarmilos d279a03
Add experiment templates as unit tests
kotlarmilos de27030
Update README.md
kotlarmilos 476a605
Update README.md
kotlarmilos b1b69d3
Update README.md
kotlarmilos a95aca8
Update README.md
kotlarmilos 6c8e477
Update README.md
kotlarmilos b06c840
Merge branch 'dotnet:main' into poc/swift-interop-direct-pinvoke
kotlarmilos 7810e56
Fix the MathLibrary.swift
kotlarmilos 6d9d67f
Update README.md
kotlarmilos 87adeed
Update README.md
kotlarmilos 0716fda
Add Swift ABI details
kotlarmilos ab53067
Merge branch 'poc/swift-interop-direct-pinvoke' of https://github.com…
kotlarmilos 675619f
Update README.md
kotlarmilos 24a8a01
Add tasks to the README.md
kotlarmilos 11d60cc
Script that exports and demangles Swift symbols
kotlarmilos e8169f0
Update README.md
kotlarmilos 8584918
Merge branch 'dotnet:main' into poc/swift-interop-direct-pinvoke
kotlarmilos 279a594
Add custom attributes for Swift calling convention
kotlarmilos acc15a0
Add runtime support for Swift error handling
kotlarmilos 5ea8755
Add sample test
kotlarmilos 7a69077
Add Swift error handling on minijit arm64
kotlarmilos ebe4116
Update README.md
kotlarmilos 0c40694
Merge branch 'dotnet:main' into poc/swift-interop-direct-pinvoke
kotlarmilos 71d33f4
Add mini arm64 support for Swift self context
kotlarmilos 7bcf61f
Remove redundant movx instruction
kotlarmilos 7d9f73d
Merge branch 'dotnet:main' into poc/swift-interop-direct-pinvoke
kotlarmilos 5973ff5
Update Swift types according to the design document
kotlarmilos ffc173a
Add mini arm64 support for Swift self and error registers
kotlarmilos 907c69a
[interp] Add interpreter support for Swift calling convention
kotlarmilos 42680b1
[inter] Simplify Swift calling convention support
kotlarmilos 2cce70f
[mono] Add mini and interpreter support for swift calling convention …
kotlarmilos a702f0d
[mono] Update Swift interop smoke test
kotlarmilos 5bc2693
[mono] Update Swift interop smoke test
kotlarmilos 78b974a
[mono] Add ArgSwiftError in emit_move_args
kotlarmilos a4ab558
Merge branch 'dotnet:main' into poc/swift-interop-direct-pinvoke
kotlarmilos ece53c2
Force ArgSwiftError to be allocated on stack
matouskozak a862485
Revert "[mono] Add ArgSwiftError in emit_move_args"
matouskozak 6a5dda2
[mono] Add helper functions for checking for Swift context classes
kotlarmilos e2b6fe9
[mono] Fix ArgSwift registers index
kotlarmilos b3cb4cb
[mono] Avoid allocating context registers on arm64
kotlarmilos 68ad76d
[mono] Add smoke tests for Swift calling convention
kotlarmilos 1efac9d
[mono] Avoid allocating context registers on x64
kotlarmilos 74dc8c9
[mono] Simplify interp support for amd64
kotlarmilos fd1b036
Exclude coreclr runtime flavor
kotlarmilos 458c1e4
Update README.md
kotlarmilos 2939996
Remove helper script
kotlarmilos 8298058
[mono] Simplify arguments check with helper macros
kotlarmilos 5447ccd
Add build script for native libs
kotlarmilos 7e04140
Merge branch 'dotnet:main' into poc/swift-interop-direct-pinvoke
kotlarmilos 64157c5
Build native Swift libraries after test build
matouskozak 6b3a52f
Revert "Build native Swift libraries after test build"
matouskozak 6077b22
[mono] Add params check for Swift calling convention in mini runtime
kotlarmilos af416eb
[mono] Add basic runtime tests for the Swift calling convention
kotlarmilos cc9c619
Update README.md
matouskozak 89f46b8
Fix README.md formatting
kotlarmilos 20eb7a6
Add comments for the Swift types
kotlarmilos ae08445
Fix lint; remove trailing spaces
kotlarmilos 5a225b5
Resolve CI failures
kotlarmilos ac7f565
Remove static functions from mini.h file
kotlarmilos b945d4d
Fix CI failures
kotlarmilos 18fcbae
Merge branch 'dotnet:main' into poc/swift-interop-direct-pinvoke
kotlarmilos 4c6c0c1
Revert API changes
kotlarmilos 0587a55
Update README.md
kotlarmilos 56bcb1b
[interp] Simplify register allocation in CallContext
kotlarmilos a5afdbe
[mono] Remove mono_arch_get_swift_error from unsupported architectures
kotlarmilos 2faea55
Update src/mono/System.Private.CoreLib/src/System/Reflection/RuntimeM…
kotlarmilos 05a8c1c
[mono] Add ext_callconv to the MonoMethodSignature
kotlarmilos b67c3c6
[mono] Throw NotImplementedException Swift calling convention on not …
kotlarmilos 4dad320
[mono] Simplify runtime tests and add error logging
kotlarmilos c088a57
[mono] Add better error logging in test cases
kotlarmilos 91e6955
Update src/native/public/mono/metadata/details/metadata-types.h
kotlarmilos 9327d1e
Update src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.cs
kotlarmilos 5df0523
[tests] Update Swift tests to utilize Assert.True with error message
kotlarmilos fc9178e
Remove README.md
kotlarmilos 738d14d
[tests] Document Swift error offset constants in the tests
kotlarmilos dccf89e
Update MONO_ARCH_HAVE_SWIFTCALL condition
kotlarmilos 9a8cc67
Update src/tests/Interop/Swift/SwiftInvalidCallConv/SwiftInvalidCallC…
kotlarmilos 8c5cf81
disable swift interop tests on mono windows
6ee1c1e
[tests] Update conditions to match Assert.True
kotlarmilos 5c5daad
[tests] Simplify error handling tests by taking Error instead of Unsa…
kotlarmilos f412721
add process isolation to runtime tests
8c53ad6
Merge branch 'poc/swift-interop-direct-pinvoke' of github.com:kotlarm…
9482a79
enable Swift tests on Apple OS
matouskozak 883d645
Revert "enable Swift tests on Apple OS"
matouskozak 4f788fc
enable Swift tests on OSX
matouskozak 1e19be8
Merge branch 'dotnet:main' into poc/swift-interop-direct-pinvoke
kotlarmilos 7b689a2
Add pointer deallocation in SwiftError test
kotlarmilos 152fa6e
Use NativeLibrary.Free for Swift interop scenarios
kotlarmilos c732e25
Update src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.cs
kotlarmilos 40f6e35
Use ReadOnlySpan in GetErrorMessageFromSwift
kotlarmilos ab3e840
Use ReadOnlySpan in GetErrorMessageFromSwift
kotlarmilos c29a35f
Use NativeMemory.Free in GetErrorMessageFromSwift
kotlarmilos 8663c3c
Simplify GetErrorMessageFromSwift to copy zero-terminated C string
kotlarmilos 3ed6733
remove OutputType Exe from tests
matouskozak cb20690
[mono][interp] Implement context register backup and restore mechanism
kotlarmilos da5279b
remove BuildAsStandalone
matouskozak a32421a
[mono][interp] Refactor context register backup and restore mechanism
kotlarmilos 3b1b4dd
add expected error printout
matouskozak 37230be
null-terminate error message string
matouskozak c25d435
Revert "null-terminate error message string"
matouskozak a57307f
refactor SwiftErrorHandling to use NSString
matouskozak 1fe9611
Update src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.cs
matouskozak e256556
Update src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.cs
matouskozak e9ce7d9
Change PtrToStringUTF8 to PtrToStringUni
matouskozak 91a3263
Change Swift to receive Int32 instead of Int
matouskozak 5e9f6fc
Merge branch 'dotnet:main' into poc/swift-interop-direct-pinvoke
kotlarmilos 97a74cc
null-terminate error message in swift
matouskozak 5384dce
Revert "null-terminate error message in swift"
matouskozak 7103cfc
Retrieve both content and length of error message
matouskozak ed2a7dc
Don't allocate amd64_rbx for Swift calling convention
kotlarmilos 8e18dc3
Merge branch 'poc/swift-interop-direct-pinvoke' of https://github.com…
kotlarmilos 7961e29
Update src/tests/Interop/Swift/SwiftErrorHandling/SwiftErrorHandling.cs
matouskozak 5e1afb5
Add missing case for ArgSwiftSelf
kotlarmilos d9da03d
[mono] Fix invalid calling convention counter
kotlarmilos 061934d
Merge branch 'dotnet:main' into poc/swift-interop-direct-pinvoke
kotlarmilos eb3103e
Merge branch 'main' of https://github.com/kotlarmilos/runtime into po…
kotlarmilos 672ac92
[mono] Resolve conflicts in SwiftTypes.cs and CallConvSwift class
kotlarmilos dfc5de6
[mono] Copy ext_callconv property
kotlarmilos 0984ffc
Refactor mono_marshal_get_native_wrapper to add non-primitive argumen…
kotlarmilos eaf9aa2
[mono] Implement sig->ext_callconv encoding and decoding
kotlarmilos fe19a09
[mini] Use MONO_ARCH_HAVE_SWIFTCALL to condition ctx regs on supporte…
kotlarmilos f93e231
[mono] Resolve tramp warnings
kotlarmilos 2d5b9f5
[mini] Remove unused func
kotlarmilos 60749f4
[mono] Add ext_callconv flag for encoding and decoding
kotlarmilos fa085d9
move swift defines and macros to mini.h
matouskozak bc19659
Merge branch 'main' into poc/swift-interop-direct-pinvoke
matouskozak 5309a58
Revert "move swift defines and macros to mini.h"
matouskozak 735b84e
initialize ext_callconv
matouskozak f444eae
ifdef mono_arch_get_swift_error
matouskozak fe6e270
refactor swift error and self class getters
matouskozak 39b8bd0
make cmake architecture specific
matouskozak eccc6a3
[mono] Use SwiftError as a ref argument instead of raw pointer
kotlarmilos a6f3369
[mono] Refactor mini to store and load Swift context regs directly in…
kotlarmilos 0b3920e
Revert unnecessary changes
kotlarmilos 0e4a48c
[mini] Always pass SwiftSelf as ArgVtypeInIRegs via dedicated ctx reg
kotlarmilos 2b3d6ff
[mono] Update Swift context registers allocation for P/Invoke
kotlarmilos f7528f8
[mini] Fix formatting and remove unnecessary code
kotlarmilos 2a1b7c2
[mini] Resolve feedback and remove unnecessary code
kotlarmilos cdaff6c
[mini] Align arm64 and amd64 implementation to use the existing arg i…
kotlarmilos 1af3d21
[mini] Add swift_error_index to the CallInfo to mark the arg ins as v…
kotlarmilos d243462
[mini] Move selferror store ins to emit_move_return_value
kotlarmilos 6f36c21
Fix lint
kotlarmilos a60f392
[mini] Preserve Swift error register value during the P/Invoke call
kotlarmilos bd11b7b
Merge branch 'dotnet:main' into poc/swift-interop-direct-pinvoke
kotlarmilos 2b2b75e
[mono] Allow SwiftError*, ref SwiftError, and function pointers in Ca…
kotlarmilos 6e54b5b
[mono] Remove unused code and fix lint
kotlarmilos 426802e
[mono] Avoid g_assert for Swift ctx regs
kotlarmilos 4e09785
Update src/mono/mono/metadata/marshal.c
kotlarmilos 89d211c
Update src/mono/mono/metadata/marshal.h
kotlarmilos d450fb1
Update Swift runtime tests to use blittable types
kotlarmilos 1a39a88
[mono] Simplify register allocation and interp native call context
kotlarmilos c2e354f
Merge branch 'poc/swift-interop-direct-pinvoke' of github.com:kotlarm…
matouskozak 1405a98
align ArgSwiftError on x64
matouskozak File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next
Next commit
Add README.md
- Loading branch information
commit 5136aafcc2ed3d606dbec041713cc160002ec5d2
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,176 @@ | ||
| # .NET Swift Interop | ||
|
|
||
| Swift has a different ABI, runtime environment, and object model, making it non-trivial to call from the .NET runtime. Existing solutions like [Binding Tools for Swift](https://github.com/xamarin/binding-tools-for-swift) and [BeyondNet](https://github.com/royalapplications/beyondnet) rely on Swift /C# /C wrappers. | ||
|
|
||
| This project aims to explore the possibilities and limitations of direct P/Invoke interop with Swift. For a comprehensive .NET-Swift Interop, the Binding Tools for Swift contains valuable components that should either be reused or built upon. | ||
|
|
||
| ## Calling convention | ||
|
|
||
| In Swift, functions parameters can be passed differently, either by reference or value. This is called "pass-by-X" for consistency. Swift allows l-values parameters to be marked as pass-by-reference with"`in/out`. It's assumed the caller ensures validity and ownership of parameters in both cases. On a physical level, it's valuable to return common value types in registers instead of indirectly. The `self` parameter for both static and instance methods is always passed through a register since it's heavily used. Also, many methods call other methods on the same object, so it's best if the register storing `self` remains stable across different method signatures. Error handling is also handled through registers, so the caller needs to check for errors and throw if necessary. Registers allocation: https://github.com/apple/swift/blob/main/docs/ABI/CallConvSummary.rst. | ||
|
|
||
| Swift's default function types are `thick` meaning they have an optional context object implicitly passed when calling the function. It would be ideal to create a thick function from a thin one without introducing a thunk just to move parameters with the missing context parameter. | ||
|
|
||
| ## Name mangling | ||
|
|
||
| Swift uses mangling for generating unique names. This process can change in different major versions (i.e. Swift 4 vs Swift 5). The Swift compiler puts mangled names in binary images to encode type references for runtime instantiation and reflection. In a binary, these mangled names may contain pointers to runtime data structures to represent locally-defined types more efficiently. When calling in from .NET, it is necessary to mangle the name during the runtime or AOT compilation. | ||
|
|
||
| ## Memory management | ||
|
|
||
| In Swift, memory management is handled by [Automatic Reference Counting](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/automaticreferencecounting/). | ||
|
|
||
| ## Types and marshalling | ||
|
|
||
| Interop should handle various types when calling from .NET, including blittable value types, non-blittable value types, tuples, classes/actors, existential containers, generic types, protocols with associated types, and closures. Enabling library evolution simplifies marshaling rules to some extent. For each entry point, the Swift compiler generates a thunk that is forward compatible. For instance, if you have an enum parameter that would typically fit into registers, the created thunk takes the value by reference rather than by value to account for potential changes in the enum's size. | ||
|
|
||
| Reference: https://github.com/xamarin/binding-tools-for-swift/blob/main/docs/ValueTypeModeling.md | ||
|
|
||
| ## Flow for invoking a Swift function from .NET | ||
|
|
||
| Here's the planned flow for invoking a Swift function from .NET if a developer uses a direct P/Invoke and demangled name: | ||
| 1. A function name should be mangled during runtime or AOT compilation to resolve the entry point. This is necessary for Swift interop but not needed otherwise, as it may slow down the compiler. | ||
| - How to mangle name efficiently? | ||
| - How to detect the Swift Interop? | ||
| 2. Function parameters should be automatically marshalled without any wrappers. | ||
| - Which types are not supported for automatic marshalling? | ||
| 3. A thunk should be emitted to handle a different calling convention, especially for instance functions and for functions with error handling. | ||
| - Implement calling convention using thunks to handle errors, self, etc. | ||
| 4. The result should be retrieved from the register or stack or indirectly. | ||
| 5. Error registers should be checked and if set, an error should be thrown. | ||
| 6. Cleanup may be required? | ||
|
|
||
|
|
||
| ## Template | ||
|
|
||
| Templates are used to define the definition of done (DoD) and contain of a set of unit tests that must be implemented. Each unit test is designed to cover a specific invocation type using different input types. The tests should be expanded with all Swift types. | ||
|
|
||
| ### Global/Static functions | ||
|
|
||
| This is the simplest case that should be implemented. | ||
|
|
||
| ```swift | ||
| public static func add(_ a: Double, _ b: Double) -> Double { | ||
| return a + b | ||
| } | ||
|
|
||
| public static func subtract(_ a: Double, _ b: Double) -> Double { | ||
| return a - b | ||
| } | ||
| ``` | ||
|
|
||
| ### Function with default params | ||
|
|
||
| Nice to have. When Swift compiles them it leaves the initialization expression behind for the compiler to consume later. It's effectively a weak macro that gets expanded later so that the compiler can potentially optimize it. | ||
|
|
||
| ```swift | ||
| public static func multiply(_ a: Double, _ b: Double = 1.0) -> Double { | ||
| return a * b | ||
| } | ||
| ``` | ||
|
|
||
| ### Function with varargs | ||
|
|
||
| Nice to have. The semantics for parameters being `f(name0: type0, name1: type1, name2: type2)`, Swift lets you do `f(a: Int..., b: Float..., c: String...)` which gets turned into `f(a: Int[], b: Float[], c: String[])` | ||
|
|
||
| ```swift | ||
| public static func average(numbers: Double...) -> Double { | ||
| let sum = numbers.reduce(0, +) | ||
| return sum / Double(numbers.count) | ||
| } | ||
| ``` | ||
|
|
||
| ### Function with closures | ||
| ```swift | ||
| public static func applyOperation(a: Double, b: Double, operation: (Double, Double) -> Double) -> Double { | ||
| return operation(a, b) | ||
| } | ||
| ``` | ||
|
|
||
| ### Computed properties | ||
| ```swift | ||
| public static var circleArea: (Double) -> Double = { radius in | ||
| return Double.pi * radius * radius | ||
| } | ||
| ``` | ||
|
|
||
| ### Initializers/Deinitializers | ||
| ```swift | ||
| public init(_internalValue: Int) { | ||
| self._internalValue = _internalValue | ||
| print("MathLibrary initialized.") | ||
| } | ||
|
|
||
| deinit { | ||
| print("MathLibrary deinitialized.") | ||
| } | ||
| ``` | ||
|
|
||
| ### Getters/Setters | ||
| ```swift | ||
| private var _internalValue: Int = 0 | ||
|
|
||
| public var value: Int { | ||
| get { | ||
| return _internalValue | ||
| } | ||
| set { | ||
| _internalValue = newValue | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ### Instance functions | ||
|
|
||
| This case is important for handling self and errors via registers. | ||
|
|
||
| ```swift | ||
| public func factorial() -> Int { | ||
| guard _internalValue >= 0 else { | ||
| fatalError("Factorial is undefined for negative numbers") | ||
| } | ||
| return _internalValue == 0 ? 1 : _internalValue * MathLibrary(_internalValue: _internalValue - 1).factorial() | ||
| } | ||
| ``` | ||
|
|
||
| ### Delegates/Protocols | ||
| ```swift | ||
| public protocol MathLibraryDelegate: AnyObject { | ||
| func mathLibraryDidInitialize() | ||
| func mathLibraryDidDeinitialize() | ||
| } | ||
| ... | ||
| public weak var delegate: MathLibraryDelegate? | ||
|
|
||
| public init(_internalValue: Int) { | ||
| self._internalValue = _internalValue | ||
| print("MathLibrary initialized.") | ||
|
|
||
| // Notify the delegate that the library was initialized | ||
| delegate?.mathLibraryDidInitialize() | ||
| } | ||
|
|
||
| deinit { | ||
| print("MathLibrary deinitialized.") | ||
|
|
||
| // Notify the delegate that the library was deinitialized | ||
| delegate?.mathLibraryDidDeinitialize() | ||
| } | ||
| ``` | ||
|
|
||
| ## Build steps | ||
|
|
||
| Build the runtime: | ||
| ```sh | ||
| ./build.sh mono+libs | ||
| ``` | ||
| Build the native library: | ||
| ```sh | ||
| swiftc -emit-library ./src/tests/Interop/Swift/MathLibrary.swift -o ./src/tests/Interop/Swift/libMathLibrary.dylib | ||
| ``` | ||
| Build the tests: | ||
| ```sh | ||
| ./src/tests/build.sh -mono osx arm64 Debug -test:Interop/Swift/SwiftInterop.csproj /p:LibrariesConfiguration=Debug | ||
| ``` | ||
| Run the tests: | ||
| ``` | ||
| sh | ||
| ``` | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.