diff --git a/src/coreclr/jit/abi.cpp b/src/coreclr/jit/abi.cpp index 524eedb48ee4d7..4ef6f034dd0f87 100644 --- a/src/coreclr/jit/abi.cpp +++ b/src/coreclr/jit/abi.cpp @@ -379,13 +379,11 @@ ABIPassingInformation SwiftABIClassifier::Classify(Compiler* comp, ClassLayout* structLayout, WellKnownArg wellKnownParam) { -#ifdef TARGET_AMD64 if (wellKnownParam == WellKnownArg::RetBuffer) { - return ABIPassingInformation::FromSegment(comp, ABIPassingSegment::InRegister(REG_SWIFT_ARG_RET_BUFF, 0, - TARGET_POINTER_SIZE)); + regNumber reg = theFixedRetBuffReg(CorInfoCallConvExtension::Swift); + return ABIPassingInformation::FromSegment(comp, ABIPassingSegment::InRegister(reg, 0, TARGET_POINTER_SIZE)); } -#endif if (wellKnownParam == WellKnownArg::SwiftSelf) { diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 5a886b3929a55a..2a1d7652ac5419 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -4356,7 +4356,7 @@ void CodeGen::genHomeSwiftStructParameters(bool handleStack) { for (unsigned lclNum = 0; lclNum < compiler->info.compArgsCount; lclNum++) { - if (lclNum == compiler->lvaSwiftSelfArg) + if ((lclNum == compiler->lvaSwiftSelfArg) || (lclNum == compiler->lvaSwiftIndirectResultArg)) { continue; } @@ -5696,6 +5696,15 @@ void CodeGen::genFnProlog() intRegState.rsCalleeRegArgMaskLiveIn &= ~RBM_SWIFT_SELF; } + if ((compiler->lvaSwiftIndirectResultArg != BAD_VAR_NUM) && + ((intRegState.rsCalleeRegArgMaskLiveIn & theFixedRetBuffMask(CorInfoCallConvExtension::Swift)) != 0)) + { + GetEmitter()->emitIns_S_R(ins_Store(TYP_I_IMPL), EA_PTRSIZE, + theFixedRetBuffReg(CorInfoCallConvExtension::Swift), + compiler->lvaSwiftIndirectResultArg, 0); + intRegState.rsCalleeRegArgMaskLiveIn &= ~theFixedRetBuffMask(CorInfoCallConvExtension::Swift); + } + if (compiler->lvaSwiftErrorArg != BAD_VAR_NUM) { intRegState.rsCalleeRegArgMaskLiveIn &= ~RBM_SWIFT_ERROR; diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index f0f97ef291bf05..e6b6a48a53205d 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3928,6 +3928,7 @@ class Compiler #ifdef SWIFT_SUPPORT unsigned lvaSwiftSelfArg; + unsigned lvaSwiftIndirectResultArg; unsigned lvaSwiftErrorArg; unsigned lvaSwiftErrorLocal; #endif diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index 7f9eb53fee914e..1b79624976a08f 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -2024,12 +2024,13 @@ void Compiler::impPopArgsForSwiftCall(GenTreeCall* call, CORINFO_SIG_INFO* sig, { JITDUMP("Creating args for Swift call [%06u]\n", dspTreeID(call)); - unsigned short swiftErrorIndex = sig->numArgs; - unsigned short swiftSelfIndex = sig->numArgs; + unsigned swiftErrorIndex = UINT_MAX; + unsigned swiftSelfIndex = UINT_MAX; + unsigned swiftIndirectResultIndex = UINT_MAX; CORINFO_CLASS_HANDLE selfType = NO_CLASS_HANDLE; // We are importing an unmanaged Swift call, which might require special parameter handling - bool checkEntireStack = false; + bool spillStack = false; // Check the signature of the Swift call for the special types CORINFO_ARG_LIST_HANDLE sigArg = sig->args; @@ -2066,13 +2067,15 @@ void Compiler::impPopArgsForSwiftCall(GenTreeCall* call, CORINFO_SIG_INFO* sig, BADCODE("Expected SwiftError pointer/reference, got struct"); } - if (swiftErrorIndex != sig->numArgs) + if (swiftErrorIndex != UINT_MAX) { BADCODE("Duplicate SwiftError* parameter"); } - swiftErrorIndex = argIndex; - checkEntireStack = true; + swiftErrorIndex = argIndex; + // Spill entire stack as we will need to reuse the error + // argument after the call. + spillStack = true; } else if ((strcmp(className, "SwiftSelf") == 0) && (strcmp(namespaceName, "System.Runtime.InteropServices.Swift") == 0)) @@ -2083,7 +2086,7 @@ void Compiler::impPopArgsForSwiftCall(GenTreeCall* call, CORINFO_SIG_INFO* sig, BADCODE("Expected SwiftSelf struct, got pointer/reference"); } - if (swiftSelfIndex != sig->numArgs) + if (swiftSelfIndex != UINT_MAX) { BADCODE("Duplicate SwiftSelf parameter"); } @@ -2100,7 +2103,7 @@ void Compiler::impPopArgsForSwiftCall(GenTreeCall* call, CORINFO_SIG_INFO* sig, BADCODE("Expected SwiftSelf struct, got pointer/reference"); } - if (swiftSelfIndex != sig->numArgs) + if (swiftSelfIndex != UINT_MAX) { BADCODE("Duplicate SwiftSelf parameter"); } @@ -2119,6 +2122,30 @@ void Compiler::impPopArgsForSwiftCall(GenTreeCall* call, CORINFO_SIG_INFO* sig, swiftSelfIndex = argIndex; } + else if ((strcmp(className, "SwiftIndirectResult") == 0) && + (strcmp(namespaceName, "System.Runtime.InteropServices.Swift") == 0)) + { + if (argIsByrefOrPtr) + { + BADCODE("Expected SwiftIndirectResult struct, got pointer/reference"); + } + + if (sig->retType != CORINFO_TYPE_VOID) + { + BADCODE("Functions with SwiftIndirectResult arguments must return void"); + } + + if (swiftIndirectResultIndex != UINT_MAX) + { + BADCODE("Duplicate SwiftIndirectResult argument"); + } + + swiftIndirectResultIndex = argIndex; + + // We will move this arg to the beginning of the arg list, so + // we must spill due to this potential reordering of arguments. + spillStack = true; + } // TODO: Handle SwiftAsync } @@ -2127,12 +2154,6 @@ void Compiler::impPopArgsForSwiftCall(GenTreeCall* call, CORINFO_SIG_INFO* sig, continue; } - if (argIndex != swiftSelfIndex) - { - // This is a struct type. Check if it needs to be lowered. - // TODO-Bug: SIMD types are not handled correctly by this. - } - // We must spill this struct to a local to be able to expand it into primitives. GenTree* node = impStackTop(sig->numArgs - 1 - argIndex).val; if (!node->OperIsLocalRead()) @@ -2145,9 +2166,7 @@ void Compiler::impPopArgsForSwiftCall(GenTreeCall* call, CORINFO_SIG_INFO* sig, } } - // If using SwiftError*, spill entire stack as we will need to reuse the - // error argument after the call. - if (checkEntireStack) + if (spillStack) { impSpillSideEffects(true, CHECK_SPILL_ALL DEBUGARG("Spill for swift call")); } @@ -2160,7 +2179,7 @@ void Compiler::impPopArgsForSwiftCall(GenTreeCall* call, CORINFO_SIG_INFO* sig, // Get SwiftError* arg (if it exists) before modifying the arg list CallArg* const swiftErrorArg = - (swiftErrorIndex != sig->numArgs) ? call->gtArgs.GetArgByIndex(swiftErrorIndex) : nullptr; + (swiftErrorIndex != UINT_MAX) ? call->gtArgs.GetArgByIndex(swiftErrorIndex) : nullptr; // Now expand struct args that must be lowered into primitives unsigned argIndex = 0; @@ -2183,15 +2202,28 @@ void Compiler::impPopArgsForSwiftCall(GenTreeCall* call, CORINFO_SIG_INFO* sig, GenTreeLclVarCommon* structVal = arg->GetNode()->AsLclVarCommon(); CallArg* insertAfter = arg; - // 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) && (selfType == NO_CLASS_HANDLE)) + // For the self/indirect result args, change them to a TYP_I_IMPL + // primitive directly. They must also be marked as a well known arg + // because they have a non-standard calling convention. + if (((argIndex == swiftSelfIndex) && (selfType == NO_CLASS_HANDLE)) || (argIndex == swiftIndirectResultIndex)) { assert(arg->GetNode()->OperIsLocalRead()); GenTree* primitiveSelf = gtNewLclFldNode(structVal->GetLclNum(), TYP_I_IMPL, structVal->GetLclOffs()); - NewCallArg newArg = NewCallArg::Primitive(primitiveSelf, TYP_I_IMPL).WellKnown(WellKnownArg::SwiftSelf); - insertAfter = call->gtArgs.InsertAfter(this, insertAfter, newArg); + NewCallArg newArg = NewCallArg::Primitive(primitiveSelf, TYP_I_IMPL); + if (argIndex == swiftSelfIndex) + { + newArg = newArg.WellKnown(WellKnownArg::SwiftSelf); + insertAfter = call->gtArgs.InsertAfter(this, insertAfter, newArg); + } + else + { + // We move the retbuf to the beginning of the arg list to make + // the call follow the same invariant as other calls with + // retbufs. + newArg = newArg.WellKnown(WellKnownArg::RetBuffer); + call->gtArgs.PushFront(this, newArg); + call->gtCallMoreFlags |= GTF_CALL_M_RETBUFFARG; + } } else { diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 889eacefd29f13..bf7dc8fb14d7a8 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -73,8 +73,9 @@ void Compiler::lvaInit() lvaRetAddrVar = BAD_VAR_NUM; #ifdef SWIFT_SUPPORT - lvaSwiftSelfArg = BAD_VAR_NUM; - lvaSwiftErrorArg = BAD_VAR_NUM; + lvaSwiftSelfArg = BAD_VAR_NUM; + lvaSwiftIndirectResultArg = BAD_VAR_NUM; + lvaSwiftErrorArg = BAD_VAR_NUM; #endif lvaInlineeReturnSpillTemp = BAD_VAR_NUM; @@ -1443,6 +1444,34 @@ bool Compiler::lvaInitSpecialSwiftParam(CORINFO_ARG_LIST_HANDLE argHnd, return true; } + if ((strcmp(className, "SwiftIndirectResult") == 0) && + (strcmp(namespaceName, "System.Runtime.InteropServices.Swift") == 0)) + { + if (argIsByrefOrPtr) + { + BADCODE("Expected SwiftIndirectResult struct, got pointer/reference"); + } + + if (info.compRetType != TYP_VOID) + { + BADCODE("Functions with SwiftIndirectResult parameters must return void"); + } + + if (lvaSwiftIndirectResultArg != BAD_VAR_NUM) + { + BADCODE("Duplicate SwiftIndirectResult parameter"); + } + + LclVarDsc* const varDsc = varDscInfo->varDsc; + varDsc->SetArgReg(theFixedRetBuffReg(CorInfoCallConvExtension::Swift)); + varDsc->lvIsRegArg = true; + + compArgSize += TARGET_POINTER_SIZE; + + lvaSwiftIndirectResultArg = varDscInfo->varNum; + return true; + } + if ((strcmp(className, "SwiftError") == 0) && (strcmp(namespaceName, "System.Runtime.InteropServices.Swift") == 0)) { if (!argIsByrefOrPtr) @@ -1709,6 +1738,10 @@ void Compiler::lvaClassifyParameterABI(Classifier& classifier) { wellKnownArg = WellKnownArg::SwiftSelf; } + else if (i == lvaSwiftIndirectResultArg) + { + wellKnownArg = WellKnownArg::RetBuffer; + } else if (i == lvaSwiftErrorArg) { wellKnownArg = WellKnownArg::SwiftError; diff --git a/src/tests/Interop/CMakeLists.txt b/src/tests/Interop/CMakeLists.txt index fa3217993a8a70..4a6bca7b8fad87 100644 --- a/src/tests/Interop/CMakeLists.txt +++ b/src/tests/Interop/CMakeLists.txt @@ -103,10 +103,11 @@ endif(CLR_CMAKE_TARGET_UNIX) if(CLR_CMAKE_TARGET_APPLE) add_subdirectory(ObjectiveC/AutoReleaseTest) add_subdirectory(ObjectiveC/ObjectiveCMarshalAPI) + add_subdirectory(Swift/SwiftAbiStress) + add_subdirectory(Swift/SwiftCallbackAbiStress) add_subdirectory(Swift/SwiftErrorHandling) - add_subdirectory(Swift/SwiftSelfContext) + add_subdirectory(Swift/SwiftIndirectResult) add_subdirectory(Swift/SwiftInvalidCallConv) - add_subdirectory(Swift/SwiftAbiStress) add_subdirectory(Swift/SwiftRetAbiStress) - add_subdirectory(Swift/SwiftCallbackAbiStress) + add_subdirectory(Swift/SwiftSelfContext) endif() diff --git a/src/tests/Interop/Swift/SwiftIndirectResult/CMakeLists.txt b/src/tests/Interop/Swift/SwiftIndirectResult/CMakeLists.txt new file mode 100644 index 00000000000000..7490bbf39ce62f --- /dev/null +++ b/src/tests/Interop/Swift/SwiftIndirectResult/CMakeLists.txt @@ -0,0 +1,21 @@ +project(SwiftIndirectResult) +include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake") + +set(SOURCE SwiftIndirectResult) + +if (NOT SWIFT_COMPILER_TARGET AND CLR_CMAKE_TARGET_OSX) + set(SWIFT_PLATFORM "macosx") + set(SWIFT_PLATFORM_SUFFIX "") + set(SWIFT_DEPLOYMENT_TARGET ${CMAKE_OSX_DEPLOYMENT_TARGET}) + set(SWIFT_COMPILER_TARGET "${CMAKE_OSX_ARCHITECTURES}-apple-${SWIFT_PLATFORM}${SWIFT_DEPLOYMENT_TARGET}${SWIFT_PLATFORM_SUFFIX}") +endif() + +add_custom_target(${SOURCE} ALL + 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" +) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/lib${SOURCE}.dylib + DESTINATION bin +) diff --git a/src/tests/Interop/Swift/SwiftIndirectResult/SwiftIndirectResult.cs b/src/tests/Interop/Swift/SwiftIndirectResult/SwiftIndirectResult.cs new file mode 100644 index 00000000000000..8a4bdce8dcc8ac --- /dev/null +++ b/src/tests/Interop/Swift/SwiftIndirectResult/SwiftIndirectResult.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Swift; +using Xunit; + +public unsafe class SwiftIndirectResultTests +{ + private struct NonFrozenStruct + { + public int A; + public int B; + public int C; + } + + private const string SwiftLib = "libSwiftIndirectResult.dylib"; + + [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })] + [DllImport(SwiftLib, EntryPoint = "$s19SwiftIndirectResult21ReturnNonFrozenStruct1a1b1cAA0efG0Vs5Int32V_A2ItF")] + public static extern void ReturnNonFrozenStruct(SwiftIndirectResult result, int a, int b, int c); + + [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })] + [DllImport(SwiftLib, EntryPoint = "$s19SwiftIndirectResult26SumReturnedNonFrozenStruct1fs5Int32VAA0fgH0VyXE_tF")] + public static extern int SumReturnedNonFrozenStruct(delegate* unmanaged[Swift] func, void* funcContext); + + [Fact] + public static void TestReturnNonFrozenStruct() + { + // In normal circumstances this instance would have unknown/dynamically determined size. + NonFrozenStruct instance; + ReturnNonFrozenStruct(new SwiftIndirectResult(&instance), 10, 20, 30); + Assert.Equal(10, instance.A); + Assert.Equal(20, instance.B); + Assert.Equal(30, instance.C); + } + + [UnmanagedCallersOnly(CallConvs = new Type[] { typeof(CallConvSwift) })] + private static void ReversePInvokeReturnNonFrozenStruct(SwiftIndirectResult result, SwiftSelf self) + { + // In normal circumstances this would require using dynamically sized memcpy and members to create the struct. + *(NonFrozenStruct*)result.Value = new NonFrozenStruct { A = 10, B = 20, C = 30 }; + } + + [Fact] + public static void TestSumReturnedNonFrozenStruct() + { + int result = SumReturnedNonFrozenStruct(&ReversePInvokeReturnNonFrozenStruct, null); + Assert.Equal(60, result); + } +} diff --git a/src/tests/Interop/Swift/SwiftIndirectResult/SwiftIndirectResult.csproj b/src/tests/Interop/Swift/SwiftIndirectResult/SwiftIndirectResult.csproj new file mode 100644 index 00000000000000..89eda99352fd20 --- /dev/null +++ b/src/tests/Interop/Swift/SwiftIndirectResult/SwiftIndirectResult.csproj @@ -0,0 +1,16 @@ + + + + true + true + + true + + + + + + + + + diff --git a/src/tests/Interop/Swift/SwiftIndirectResult/SwiftIndirectResult.swift b/src/tests/Interop/Swift/SwiftIndirectResult/SwiftIndirectResult.swift new file mode 100644 index 00000000000000..8ccb0bbc0d3b32 --- /dev/null +++ b/src/tests/Interop/Swift/SwiftIndirectResult/SwiftIndirectResult.swift @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +public struct NonFrozenStruct +{ + let a : Int32; + let b : Int32; + let c : Int32; +} + +public func ReturnNonFrozenStruct(a: Int32, b: Int32, c: Int32) -> NonFrozenStruct { + return NonFrozenStruct(a: a, b: b, c: c) +} + +public func SumReturnedNonFrozenStruct(f: () -> NonFrozenStruct) -> Int32 { + let s = f() + return s.a + s.b + s.c +} \ No newline at end of file diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 5da277d04e87b4..7d362bff260f0d 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1917,6 +1917,9 @@ https://github.com/dotnet/runtime/issues/93631: Swift reverse pinvokes are not implemented on Mono yet + + https://github.com/dotnet/runtime/issues/93631: SwiftIndirectResult is not implemented on Mono yet +