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

JIT: Add support for SwiftIndirectResult in Swift calling convention #103570

Merged
merged 18 commits into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions src/coreclr/jit/abi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
11 changes: 10 additions & 1 deletion src/coreclr/jit/codegencommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -3928,6 +3928,7 @@ class Compiler

#ifdef SWIFT_SUPPORT
unsigned lvaSwiftSelfArg;
unsigned lvaSwiftIndirectResultArg;
unsigned lvaSwiftErrorArg;
unsigned lvaSwiftErrorLocal;
#endif
Expand Down
80 changes: 56 additions & 24 deletions src/coreclr/jit/importercalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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))
Expand All @@ -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");
}
Expand All @@ -2100,7 +2103,7 @@ void Compiler::impPopArgsForSwiftCall(GenTreeCall* call, CORINFO_SIG_INFO* sig,
BADCODE("Expected SwiftSelf<T> struct, got pointer/reference");
}

if (swiftSelfIndex != sig->numArgs)
if (swiftSelfIndex != UINT_MAX)
{
BADCODE("Duplicate SwiftSelf parameter");
}
Expand All @@ -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
}

Expand All @@ -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())
Expand All @@ -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)
Copy link
Member

Choose a reason for hiding this comment

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

Could you update this comment to mention the SwiftIndirectResult case, please?

Copy link
Member Author

Choose a reason for hiding this comment

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

I moved the existing comment up to where we assign spillStack in the SwiftError* case instead (there is already a comment on the new spillStack = true I added)

{
impSpillSideEffects(true, CHECK_SPILL_ALL DEBUGARG("Spill for swift call"));
}
Expand All @@ -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;
Expand All @@ -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
{
Expand Down
37 changes: 35 additions & 2 deletions src/coreclr/jit/lclvars.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down
7 changes: 4 additions & 3 deletions src/tests/Interop/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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()
21 changes: 21 additions & 0 deletions src/tests/Interop/Swift/SwiftIndirectResult/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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
)
53 changes: 53 additions & 0 deletions src/tests/Interop/Swift/SwiftIndirectResult/SwiftIndirectResult.cs
Original file line number Diff line number Diff line change
@@ -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]<SwiftIndirectResult, SwiftSelf, void> 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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Needed for CLRTestTargetUnsupported, CMakeProjectReference -->
<RequiresProcessIsolation>true</RequiresProcessIsolation>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<!-- Swift interop is supported on Apple platforms only -->
<CLRTestTargetUnsupported Condition="'$(TargetsOSX)' != 'true' and '$(TargetsAppleMobile)' != 'true'">true</CLRTestTargetUnsupported>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildProjectName).cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(TestLibraryProjectPath)" />
<CMakeProjectReference Include="CMakeLists.txt" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -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
}
3 changes: 3 additions & 0 deletions src/tests/issues.targets
Original file line number Diff line number Diff line change
Expand Up @@ -1917,6 +1917,9 @@
<ExcludeList Include="$(XunitTestBinBase)/Interop/Swift/SwiftCallbackAbiStress/**">
<Issue>https://github.com/dotnet/runtime/issues/93631: Swift reverse pinvokes are not implemented on Mono yet</Issue>
</ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/Interop/Swift/SwiftIndirectResult/**">
<Issue>https://github.com/dotnet/runtime/issues/93631: SwiftIndirectResult is not implemented on Mono yet</Issue>
</ExcludeList>
</ItemGroup>

<!-- Known failures for mono runtime on Windows -->
Expand Down
Loading