From 9bf6abf869ee8a5ac0bbf89210c8b94399576523 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Mon, 17 Jun 2024 14:14:02 +0200 Subject: [PATCH] JIT: Add support for SwiftSelf in Swift calling convention This adds support to allow SwiftSelf with a frozen struct as T. Swift allows enregistration of 'self' in these cases, but the 'self' must still be passed in the dedicated context register when the frozen struct is not enregistered, which makes this support necessary to handle as part of the calling convention. A few notes: - If `T` is not a value class we `BADCODE` - If the signature includes `SwiftSelf`, then it must be the first argument of the signature, which matches how 'self' gets enregistered on the Swift side, otherwise we `BADCODE` - There is not support for reverse pinvokes for `SwiftSelf`. That's because the context passed to function pointers is always a pointer, so I do not see any use case for this in reverse pinvokes. - Some care must be taken since `SwiftSelf` is a generic struct whose layout generally is not going to match `T` without tail padding (until we get something like #100896). --- src/coreclr/jit/importercalls.cpp | 42 ++++++++++++++++-- .../Swift/SwiftSelfContext/CMakeLists.txt | 2 +- .../SwiftSelfContext/SwiftSelfContext.cs | 43 ++++++++++++++++++- .../SwiftSelfContext/SwiftSelfContext.swift | 24 +++++++++++ 4 files changed, 105 insertions(+), 6 deletions(-) diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index f52fe739f11c00..9fd93d861405fa 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -2027,6 +2027,7 @@ void Compiler::impPopArgsForSwiftCall(GenTreeCall* call, CORINFO_SIG_INFO* sig, unsigned short swiftErrorIndex = sig->numArgs; unsigned short swiftSelfIndex = sig->numArgs; + CORINFO_CLASS_HANDLE selfType = NO_CLASS_HANDLE; // We are importing an unmanaged Swift call, which might require special parameter handling bool checkEntireStack = false; @@ -2090,6 +2091,34 @@ void Compiler::impPopArgsForSwiftCall(GenTreeCall* call, CORINFO_SIG_INFO* sig, swiftSelfIndex = argIndex; // Fall through to make sure the struct value becomes a local. } + else if ((strcmp(className, "SwiftSelf`1") == 0) && + (strcmp(namespaceName, "System.Runtime.InteropServices.Swift") == 0)) + { + // We expect a SwiftSelf struct to be passed, not a pointer/reference + if (argIsByrefOrPtr) + { + BADCODE("Expected SwiftSelf struct, got pointer/reference"); + } + + if (swiftSelfIndex != sig->numArgs) + { + BADCODE("Duplicate SwiftSelf parameter"); + } + + if (argIndex != 0) + { + BADCODE("SwiftSelf must be the first argument in the signature"); + } + + selfType = info.compCompHnd->getTypeInstantiationArgument(argClass, 0); + CorInfoType selfCorType = info.compCompHnd->asCorInfoType(selfType); + if (selfCorType != CORINFO_TYPE_VALUECLASS) + { + BADCODE("SwiftSelf expects T to be a value class"); + } + + swiftSelfIndex = argIndex; + } // TODO: Handle SwiftAsync } @@ -2157,7 +2186,7 @@ void Compiler::impPopArgsForSwiftCall(GenTreeCall* call, CORINFO_SIG_INFO* sig, // For the self arg, change it from the SwiftSelf struct to a // TYP_I_IMPL primitive directly. It must also be marked as a well // known arg because it has a non-standard calling convention. - if (argIndex == swiftSelfIndex) + if ((argIndex == swiftSelfIndex) && (selfType == NO_CLASS_HANDLE)) { assert(arg->GetNode()->OperIsLocalRead()); GenTree* primitiveSelf = gtNewLclFldNode(structVal->GetLclNum(), TYP_I_IMPL, structVal->GetLclOffs()); @@ -2166,7 +2195,8 @@ void Compiler::impPopArgsForSwiftCall(GenTreeCall* call, CORINFO_SIG_INFO* sig, } else { - const CORINFO_SWIFT_LOWERING* lowering = GetSwiftLowering(arg->GetSignatureClassHandle()); + CORINFO_CLASS_HANDLE argClass = argIndex == swiftSelfIndex ? selfType : arg->GetSignatureClassHandle(); + const CORINFO_SWIFT_LOWERING* lowering = GetSwiftLowering(argClass); if (lowering->byReference) { JITDUMP(" Argument %d of type %s must be passed by reference\n", argIndex, @@ -2188,7 +2218,13 @@ void Compiler::impPopArgsForSwiftCall(GenTreeCall* call, CORINFO_SIG_INFO* sig, GenTree* addrNode = gtNewLclAddrNode(structVal->GetLclNum(), structVal->GetLclOffs()); JITDUMP(" Passing by reference\n"); - insertAfter = call->gtArgs.InsertAfter(this, insertAfter, NewCallArg::Primitive(addrNode, TYP_I_IMPL)); + NewCallArg newArg = NewCallArg::Primitive(addrNode, TYP_I_IMPL); + if (argIndex == swiftSelfIndex) + { + newArg = newArg.WellKnown(WellKnownArg::SwiftSelf); + } + + insertAfter = call->gtArgs.InsertAfter(this, insertAfter, newArg); } else { diff --git a/src/tests/Interop/Swift/SwiftSelfContext/CMakeLists.txt b/src/tests/Interop/Swift/SwiftSelfContext/CMakeLists.txt index 8d7efa1eea6757..294fcd0e0ba28f 100644 --- a/src/tests/Interop/Swift/SwiftSelfContext/CMakeLists.txt +++ b/src/tests/Interop/Swift/SwiftSelfContext/CMakeLists.txt @@ -11,7 +11,7 @@ if (NOT SWIFT_COMPILER_TARGET AND CLR_CMAKE_TARGET_OSX) endif() add_custom_target(${SOURCE} ALL - COMMAND xcrun swiftc -target ${SWIFT_COMPILER_TARGET} -emit-library ${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE}.swift -o ${CMAKE_CURRENT_BINARY_DIR}/lib${SOURCE}.dylib + COMMAND xcrun swiftc -target ${SWIFT_COMPILER_TARGET} -enable-library-evolution -emit-library ${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE}.swift -o ${CMAKE_CURRENT_BINARY_DIR}/lib${SOURCE}.dylib DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE}.swift COMMENT "Generating ${SOURCE} library" ) diff --git a/src/tests/Interop/Swift/SwiftSelfContext/SwiftSelfContext.cs b/src/tests/Interop/Swift/SwiftSelfContext/SwiftSelfContext.cs index bbd234639cc9e9..77c046cf824805 100644 --- a/src/tests/Interop/Swift/SwiftSelfContext/SwiftSelfContext.cs +++ b/src/tests/Interop/Swift/SwiftSelfContext/SwiftSelfContext.cs @@ -15,11 +15,11 @@ public class SelfContextTests public unsafe static extern void* getInstance(); [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })] - [DllImport(SwiftLib, EntryPoint = "$s16SwiftSelfContext0B7LibraryC14getMagicNumberSiyF")] + [DllImport(SwiftLib, EntryPoint = "$s16SwiftSelfContext0B7LibraryC14getMagicNumberSiyFTj")] public static extern nint getMagicNumber(SwiftSelf self); [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })] - [DllImport(SwiftLib, EntryPoint = "$s16SwiftSelfContext0B7LibraryC14getMagicNumberSiyF")] + [DllImport(SwiftLib, EntryPoint = "$s16SwiftSelfContext0B7LibraryC14getMagicNumberSiyFTj")] public static extern nint getMagicNumberOnStack(int dummy0, int dummy1, int dummy2, int dummy3, int dummy4, int dummy5, int dummy6, int dummy7, int dummy8, int dummy9, SwiftSelf self); [Fact] @@ -44,4 +44,43 @@ public unsafe static void TestSwiftSelfContextOnStack() int result = (int)getMagicNumberOnStack(i, i + 1, i + 2, i + 3, i + 4, i + 5, i + 6, i + 7, i + 8, i + 9, self); Assert.True(result == 42, "The result from Swift does not match the expected value."); } + + public struct FrozenEnregisteredStruct + { + public long A; + public long B; + } + + public struct FrozenNonEnregisteredStruct + { + public long A; + public long B; + public long C; + public long D; + public long E; + } + + [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })] + [DllImport(SwiftLib, EntryPoint = "$s16SwiftSelfContext24FrozenEnregisteredStructV3Sums5Int64VyF")] + public static extern long SumFrozenEnregisteredStruct(SwiftSelf self); + + [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })] + [DllImport(SwiftLib, EntryPoint = "$s16SwiftSelfContext27FrozenNonEnregisteredStructV3Sums5Int64VyF")] + public static extern long SumFrozenNonEnregisteredStruct(SwiftSelf self); + + [Fact] + [SkipOnMono("SwiftSelf is not supported on Mono")] + public unsafe static void TestSelfIsFrozenEnregisteredStruct() + { + long sum = SumFrozenEnregisteredStruct(new SwiftSelf(new FrozenEnregisteredStruct { A = 10, B = 20 })); + Assert.Equal(30, sum); + } + + [Fact] + [SkipOnMono("SwiftSelf is not supported on Mono")] + public unsafe static void TestSelfIsFrozenNonEnregisteredStruct() + { + long sum = SumFrozenNonEnregisteredStruct(new SwiftSelf(new FrozenNonEnregisteredStruct { A = 10, B = 20, C = 30, D = 40, E = 50 })); + Assert.Equal(150, sum); + } } diff --git a/src/tests/Interop/Swift/SwiftSelfContext/SwiftSelfContext.swift b/src/tests/Interop/Swift/SwiftSelfContext/SwiftSelfContext.swift index 047f3bd942801b..92d26668367cac 100644 --- a/src/tests/Interop/Swift/SwiftSelfContext/SwiftSelfContext.swift +++ b/src/tests/Interop/Swift/SwiftSelfContext/SwiftSelfContext.swift @@ -19,3 +19,27 @@ public class SelfLibrary { return pointer } } + +@frozen +public struct FrozenEnregisteredStruct +{ + let a : Int64; + let b : Int64; + + public func Sum() -> Int64 { + return a + b + } +} + +@frozen +public struct FrozenNonEnregisteredStruct { + let a : Int64; + let b : Int64; + let c : Int64; + let d : Int64; + let e : Int64; + + public func Sum() -> Int64 { + return a + b + c + d + e + } +}