Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
1 change: 1 addition & 0 deletions src/coreclr/dlls/mscorrc/mscorrc.rc
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ BEGIN
IDS_EE_NDIRECT_DISABLEDMARSHAL_LCID "The LCIDConversionAttribute is not supported when runtime marshalling is disabled."
IDS_EE_NDIRECT_DISABLEDMARSHAL_PRESERVESIG "Setting PreserveSig to false for a P/Invoke is not supported when runtime marshalling is disabled."
IDS_EE_NDIRECT_DISABLEDMARSHAL_VARARGS "Using a variable argument list in a P/Invoke is not supported when runtime marshalling is disabled."
IDS_EE_NDIRECT_SWIFT_VALUETYPE "Passing non-primitive value types to a P/Invoke with the Swift calling convention is unsupported."
IDS_EE_CLASS_CONSTRAINTS_VIOLATION "GenericArguments[%1], '%2', on '%3' violates the constraint of type parameter '%4'."
IDS_EE_METHOD_CONSTRAINTS_VIOLATION "Method %1.%2: type argument '%3' violates the constraint of type parameter '%4'."
IDS_EE_NOSYNCHRONIZED "Synchronized attribute cannot be used with this method type."
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/dlls/mscorrc/resource.h
Original file line number Diff line number Diff line change
Expand Up @@ -567,3 +567,4 @@
#define IDS_EE_NDIRECT_DISABLEDMARSHAL_LCID 0x264E
#define IDS_EE_NDIRECT_DISABLEDMARSHAL_PRESERVESIG 0x264F
#define IDS_EE_NDIRECT_DISABLEDMARSHAL_VARARGS 0x2650
#define IDS_EE_NDIRECT_SWIFT_VALUETYPE 0x2651
1 change: 1 addition & 0 deletions src/coreclr/inc/corhdr.h
Original file line number Diff line number Diff line change
Expand Up @@ -1715,6 +1715,7 @@ typedef enum LoadHintEnum
#define CMOD_CALLCONV_NAME_STDCALL "CallConvStdcall"
#define CMOD_CALLCONV_NAME_THISCALL "CallConvThiscall"
#define CMOD_CALLCONV_NAME_FASTCALL "CallConvFastcall"
#define CMOD_CALLCONV_NAME_SWIFT "CallConvSwift"
#define CMOD_CALLCONV_NAME_SUPPRESSGCTRANSITION "CallConvSuppressGCTransition"
#define CMOD_CALLCONV_NAME_MEMBERFUNCTION "CallConvMemberFunction"

Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -772,7 +772,8 @@ enum class CorInfoCallConvExtension
// New calling conventions supported with the extensible calling convention encoding go here.
CMemberFunction,
StdcallMemberFunction,
FastcallMemberFunction
FastcallMemberFunction,
Swift
};

#ifdef TARGET_X86
Expand Down
5 changes: 4 additions & 1 deletion src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1590,6 +1590,9 @@ private static CorInfoCallConvExtension ToCorInfoCallConvExtension(UnmanagedCall
case UnmanagedCallingConventions.Fastcall:
result = CorInfoCallConvExtension.Fastcall;
break;
case UnmanagedCallingConventions.Swift:
result = CorInfoCallConvExtension.Swift;
break;
default:
ThrowHelper.ThrowInvalidProgramException();
result = CorInfoCallConvExtension.Managed; // unreachable
Expand Down Expand Up @@ -4099,7 +4102,7 @@ private uint getJitFlags(ref CORJIT_FLAGS flags, uint sizeInBytes)

#if READYTORUN
// TODO: enable this check in full AOT
if (Marshaller.IsMarshallingRequired(this.MethodBeingCompiled.Signature, Array.Empty<ParameterMetadata>(), ((MetadataType)this.MethodBeingCompiled.OwningType).Module)) // Only blittable arguments
if (Marshaller.IsMarshallingRequired(this.MethodBeingCompiled.Signature, ((MetadataType)this.MethodBeingCompiled.OwningType).Module, this.MethodBeingCompiled.GetUnmanagedCallersOnlyMethodCallingConventions())) // Only blittable arguments
{
ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramNonBlittableTypes, this.MethodBeingCompiled);
}
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,8 @@ public enum CorInfoCallConvExtension
// New calling conventions supported with the extensible calling convention encoding go here.
CMemberFunction,
StdcallMemberFunction,
FastcallMemberFunction
FastcallMemberFunction,
Swift
}

public enum CORINFO_CALLINFO_FLAGS
Expand Down
22 changes: 22 additions & 0 deletions src/coreclr/tools/Common/TypeSystem/IL/Stubs/PInvokeILEmitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public struct PInvokeILEmitter
private readonly PInvokeILEmitterConfiguration _pInvokeILEmitterConfiguration;
private readonly PInvokeMetadata _pInvokeMetadata;
private readonly PInvokeFlags _flags;
private readonly UnmanagedCallingConventions _callingConvention;
private readonly InteropStateManager _interopStateManager;

private PInvokeILEmitter(MethodDesc targetMethod, PInvokeILEmitterConfiguration pinvokeILEmitterConfiguration, InteropStateManager interopStateManager)
Expand All @@ -38,10 +39,17 @@ private PInvokeILEmitter(MethodDesc targetMethod, PInvokeILEmitterConfiguration
if (_targetMethod is DelegateMarshallingMethodThunk delegateMethod)
{
_flags = ((EcmaType)delegateMethod.DelegateType.GetTypeDefinition()).GetDelegatePInvokeFlags();
_callingConvention = delegateMethod.DelegateType.GetDelegateCallingConventions();
}
else if (targetMethod is CalliMarshallingMethodThunk calliMethod)
{
_flags = _pInvokeMetadata.Flags;
_callingConvention = calliMethod.TargetSignature.GetStandaloneMethodSignatureCallingConventions();
}
else
{
_flags = _pInvokeMetadata.Flags;
_callingConvention = _targetMethod.GetPInvokeMethodCallingConventions();
}
_marshallers = InitializeMarshallers(targetMethod, interopStateManager, _flags);
}
Expand Down Expand Up @@ -380,6 +388,20 @@ private PInvokeILStubMethodIL EmitIL()
if (_targetMethod.HasCustomAttribute("System.Runtime.InteropServices", "LCIDConversionAttribute"))
throw new NotSupportedException();

if (_callingConvention == UnmanagedCallingConventions.Swift)
Comment thread
kotlarmilos marked this conversation as resolved.
Outdated
{
// Swift calling convention has strict rules about value types that the JIT does not implement.
// Throw an exception if we encounter a value type that is not a primitive (and as such needs this lowering logic applied).
foreach (var marshaller in _marshallers)
{
if (marshaller.ManagedType is { IsValueType: true, IsEnum: false, IsPrimitive: false } param
&& !MarshalHelpers.IsSwiftIntrinsicValueType(param))
{
throw new NotSupportedException();
}
}
}

PInvokeILCodeStreams pInvokeILCodeStreams = new PInvokeILCodeStreams();
ILEmitter emitter = pInvokeILCodeStreams.Emitter;
ILCodeStream marshallingCodestream = pInvokeILCodeStreams.MarshallingCodeStream;
Expand Down
19 changes: 19 additions & 0 deletions src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using Debug = System.Diagnostics.Debug;
using System.Runtime.InteropServices.ObjectiveC;
using ILCompiler;
Comment thread
jkoritzinsky marked this conversation as resolved.
Outdated
using Internal.TypeSystem.Ecma;

namespace Internal.TypeSystem.Interop
Expand Down Expand Up @@ -971,5 +972,23 @@ public static bool IsRuntimeMarshallingEnabled(ModuleDesc module)
{
return module.Assembly is not EcmaAssembly assembly || !assembly.HasAssemblyCustomAttribute("System.Runtime.CompilerServices", "DisableRuntimeMarshallingAttribute");
}

public static bool IsSwiftIntrinsicValueType(TypeDesc type)
{
Debug.Assert(type.IsValueType && !type.IsPrimitive && !type.IsEnum);
if (!type.IsIntrinsic)
return false;

if (type is not DefType defType)
return false;

if (VectorFieldLayoutAlgorithm.IsVectorType(defType))
return true;

if (defType is { Namespace: "System.Runtime.InteropServices.Swift", Name: "SwiftSelf" })
Comment thread
jkoritzinsky marked this conversation as resolved.
Outdated
return true;

return false;
}
}
}
Comment thread
jkoritzinsky marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public enum UnmanagedCallingConventions
// Unmanaged = 0x00000009, - this one is always translated to cdecl/stdcall

// The ones higher than 0xF are defined by the type system
// There are no such calling conventions yet.
Swift = 0x00000010
}

public static class CallingConventionExtensions
Expand Down Expand Up @@ -135,6 +135,27 @@ public static UnmanagedCallingConventions GetPInvokeMethodCallingConventions(thi
return result;
}

public static UnmanagedCallingConventions GetDelegateCallingConventions(this TypeDesc delegateType)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This method doesn't appear used. Is that intentional?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I removed the usage of it during a refactor. I think it's still useful to have, but we can remove it and re-add later when we have a use case.

{
Debug.Assert(delegateType.IsDelegate);

if (delegateType is not EcmaType ecmaDelegate)
Comment thread
AaronRobinsonMSFT marked this conversation as resolved.
Outdated
{
return GetPlatformDefaultUnmanagedCallingConvention(delegateType.Context);
}

MethodSignatureFlags unmanagedCallConv = ecmaDelegate.GetDelegatePInvokeFlags().UnmanagedCallingConvention;
if (unmanagedCallConv != MethodSignatureFlags.None)
{
Debug.Assert((int)MethodSignatureFlags.UnmanagedCallingConventionCdecl == (int)UnmanagedCallingConventions.Cdecl
&& (int)MethodSignatureFlags.UnmanagedCallingConventionStdCall == (int)UnmanagedCallingConventions.Stdcall
&& (int)MethodSignatureFlags.UnmanagedCallingConventionThisCall == (int)UnmanagedCallingConventions.Thiscall);
return (UnmanagedCallingConventions)unmanagedCallConv;
}

return GetPlatformDefaultUnmanagedCallingConvention(delegateType.Context);
}

private static UnmanagedCallingConventions GetUnmanagedCallingConventionFromAttribute(CustomAttributeValue<TypeDesc> attributeWithCallConvsArray, TypeSystemContext context)
{
ImmutableArray<CustomAttributeTypedArgument<TypeDesc>> callConvArray = default;
Expand Down Expand Up @@ -181,6 +202,7 @@ private static UnmanagedCallingConventions AccumulateCallingConventions(Unmanage
"CallConvThiscall" => UnmanagedCallingConventions.Thiscall,
"CallConvSuppressGCTransition" => UnmanagedCallingConventions.IsSuppressGcTransition,
"CallConvMemberFunction" => UnmanagedCallingConventions.IsMemberFunction,
"CallConvSwift" => UnmanagedCallingConventions.Swift,
_ => null
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,20 @@ public static bool IsMarshallingRequired(MethodDesc targetMethod)
if (MarshalHelpers.ShouldCheckForPendingException(targetMethod.Context.Target, metadata))
return true;

if (targetMethod.GetPInvokeMethodCallingConventions() == UnmanagedCallingConventions.Swift)
{
// Swift calling convention has strict rules about value types that the JIT does not implement.
// Skip stub generation of Swift methods with value types to allow an exception at runtime.
foreach (var param in targetMethod.Signature)
{
if (param is { IsValueType: true, IsEnum: false, IsPrimitive: false }
&& !MarshalHelpers.IsSwiftIntrinsicValueType(param))
{
return true;
}
}
}

var marshallers = GetMarshallersForMethod(targetMethod);
for (int i = 0; i < marshallers.Length; i++)
{
Expand All @@ -130,8 +144,48 @@ public static bool IsMarshallingRequired(MethodDesc targetMethod)
return false;
}

public static bool IsMarshallingRequired(MethodSignature methodSig, ModuleDesc moduleContext, UnmanagedCallingConventions callingConvention)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The callingConvention parameter is not used, is it intentional?

Why do we need a new overload though? I would expect the existing IsMarshallingRequired to get an extra callingConvention argument if answer to this now depends on the calling convention as well (or why can we still decide it without the calling convention in the other overload?).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

In the other overload, we can extract the calling convention from the MethodSignature. This overload is for UnmanagedCallersOnly methods where the callconv is in the attribute, not the managed signature.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The parameter is still unused, is that intentional?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, it's intentional that it's unused.

{
if (callingConvention == UnmanagedCallingConventions.Swift)
{
// Swift calling convention has strict rules about value types that the JIT does not implement.
// Skip stub generation of Swift methods with value types to allow an exception at runtime.
foreach (var param in methodSig)
{
if (param is { IsValueType: true, IsEnum: false, IsPrimitive: false }
&& !MarshalHelpers.IsSwiftIntrinsicValueType(param))
{
return true;
}
}
}

Marshaller[] marshallers = GetMarshallersForSignature(methodSig, System.Array.Empty<ParameterMetadata>(), moduleContext);
for (int i = 0; i < marshallers.Length; i++)
{
if (marshallers[i].IsMarshallingRequired())
return true;
}

return false;
}

public static bool IsMarshallingRequired(MethodSignature methodSig, ParameterMetadata[] paramMetadata, ModuleDesc moduleContext)
{
if (methodSig.GetStandaloneMethodSignatureCallingConventions() == UnmanagedCallingConventions.Swift)
{
// Swift calling convention has strict rules about value types that the JIT does not implement.
// Skip stub generation of Swift methods with value types to allow an exception at runtime.
foreach (var param in methodSig)
{
if (param is { IsValueType: true, IsEnum: false, IsPrimitive: false }
&& !MarshalHelpers.IsSwiftIntrinsicValueType(param))
{
return true;
}
}
}

Marshaller[] marshallers = GetMarshallersForSignature(methodSig, paramMetadata, moduleContext);
for (int i = 0; i < marshallers.Length; i++)
{
Expand Down
5 changes: 4 additions & 1 deletion src/coreclr/vm/callconvbuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ namespace
BASE_CALL_CONV(CMOD_CALLCONV_NAME_CDECL, C) \
BASE_CALL_CONV(CMOD_CALLCONV_NAME_STDCALL, Stdcall) \
BASE_CALL_CONV(CMOD_CALLCONV_NAME_THISCALL, Thiscall) \
BASE_CALL_CONV(CMOD_CALLCONV_NAME_FASTCALL, Fastcall)
BASE_CALL_CONV(CMOD_CALLCONV_NAME_FASTCALL, Fastcall) \
BASE_CALL_CONV(CMOD_CALLCONV_NAME_SWIFT, Swift)

#define DECLARE_MOD_CALL_CONVS \
CALL_CONV_MODIFIER(CMOD_CALLCONV_NAME_SUPPRESSGCTRANSITION, CALL_CONV_MOD_SUPPRESSGCTRANSITION) \
Expand Down Expand Up @@ -176,6 +177,8 @@ namespace
return CorInfoCallConvExtension::FastcallMemberFunction;
case CorInfoCallConvExtension::Thiscall:
return CorInfoCallConvExtension::Thiscall;
case CorInfoCallConvExtension::Swift:
return CorInfoCallConvExtension::Swift;
Comment thread
jkoritzinsky marked this conversation as resolved.
default:
_ASSERTE("Calling convention is not an unmanaged base calling convention.");
return baseCallConv;
Expand Down
59 changes: 59 additions & 0 deletions src/coreclr/vm/dllimport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3167,6 +3167,40 @@ HRESULT NDirect::HasNAT_LAttribute(IMDInternalImport *pInternalImport, mdToken t
return S_FALSE;
}

namespace
{
bool IsSwiftIntrinsicValueType(TypeHandle hnd)
{
STANDARD_VM_CONTRACT;

_ASSERTE(hnd.IsValueType());
MethodTable* pMT = hnd.GetMethodTable();
if (!pMT->IsIntrinsicType())
return false;

LPCUTF8 namespaceName;
LPCUTF8 className = pMT->GetFullyQualifiedNameInfo(&namespaceName);

// We treat the .NET vector types as projections of the Swift SIMD intrinsic types.
if ((strcmp(className, "Vector512`1") == 0) || (strcmp(className, "Vector256`1") == 0) ||
(strcmp(className, "Vector128`1") == 0) || (strcmp(className, "Vector64`1") == 0))
{
_ASSERTE(strcmp(namespaceName, "System.Runtime.Intrinsics") == 0);
return true;
}

// The SwiftSelf type is an intrinsic type that represents the Swift self register.
if (strcmp(className, "SwiftSelf") == 0)
{
_ASSERTE(strcmp(namespaceName, "System.Runtime.InteropServices.Swift") == 0);
return true;
}

// We don't need to treat the SwiftError type as an intrinsic here as it should always be passed
// as a SwiftError* parameter.
Comment thread
jkoritzinsky marked this conversation as resolved.
Outdated
return false;
}
}

// Either MD or signature & module must be given.
/*static*/
Expand Down Expand Up @@ -3343,6 +3377,15 @@ BOOL NDirect::MarshalingRequired(
return TRUE;
}

if (callConv == CorInfoCallConvExtension::Swift && !hndArgType.IsEnum())
{
// Swift has a very complicated struct lowering algorithm that would be prohibitively expensive to implement in RyuJIT.
// Instead, we implement it in the projection layer. The runtime and JIT should only see pre-lowered, LLVM-IR-equivalent
// signatures.
// Require marshalling here so we can easily throw an exception during IL Stub generation.
return !IsSwiftIntrinsicValueType(hndArgType);
}

if (i > 0)
{
const bool isValueType = true;
Expand Down Expand Up @@ -4336,6 +4379,22 @@ static void CreateNDirectStubAccessMetadata(
COMPlusThrow(kInvalidProgramException, VLDTR_E_FMD_PINVOKENOTSTATIC);
}
}

if (unmgdCallConv == CorInfoCallConvExtension::Swift)
{
// Swift has a very complicated lowering algorithm for structs. The .NET JIT does not support it,
// so throw an exception here if we encounter a signature that uses any value types.

for (int i = 0; i < (*pNumArgs); i++)
{
TypeHandle hnd;
if (msig.NextArgNormalized(&hnd) == ELEMENT_TYPE_VALUETYPE
&& !IsSwiftIntrinsicValueType(hnd))
{
COMPlusThrow(kNotSupportedException, IDS_EE_NDIRECT_SWIFT_VALUETYPE);
}
}
}
}

namespace
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Runtime.CompilerServices;

namespace System.Runtime.InteropServices.Swift
{
Expand All @@ -19,7 +20,8 @@ namespace System.Runtime.InteropServices.Swift
/// </code>
/// </para>
/// </remarks>
[CLSCompliantAttribute(false)]
[CLSCompliant(false)]
[Intrinsic]
Comment thread
jkoritzinsky marked this conversation as resolved.
public readonly unsafe struct SwiftSelf
{
/// <summary>
Expand Down Expand Up @@ -52,7 +54,8 @@ public SwiftSelf(void* value)
/// </code>
/// </para>
/// </remarks>
[CLSCompliantAttribute(false)]
[CLSCompliant(false)]
[Intrinsic]
public readonly unsafe struct SwiftError
{
/// <summary>
Expand Down