diff --git a/src/coreclr/src/inc/readytorun.h b/src/coreclr/src/inc/readytorun.h
index 514bb0c163bad1..c78ec74d56727b 100644
--- a/src/coreclr/src/inc/readytorun.h
+++ b/src/coreclr/src/inc/readytorun.h
@@ -45,11 +45,10 @@ struct READYTORUN_SECTION
enum ReadyToRunFlag
{
- // Set if the original IL assembly was platform-neutral
- READYTORUN_FLAG_PLATFORM_NEUTRAL_SOURCE = 0x00000001,
- READYTORUN_FLAG_SKIP_TYPE_VALIDATION = 0x00000002,
- // Set of methods with native code was determined using profile data
- READYTORUN_FLAG_PARTIAL = 0x00000004,
+ READYTORUN_FLAG_PLATFORM_NEUTRAL_SOURCE = 0x00000001, // Set if the original IL assembly was platform-neutral
+ READYTORUN_FLAG_SKIP_TYPE_VALIDATION = 0x00000002, // Set of methods with native code was determined using profile data
+ READYTORUN_FLAG_PARTIAL = 0x00000004,
+ READYTORUN_FLAG_NONSHARED_PINVOKE_STUBS = 0x00000008 // PInvoke stubs compiled into image are non-shareable (no secret parameter)
};
enum ReadyToRunSectionType
diff --git a/src/coreclr/src/tools/crossgen2/Common/Internal/Runtime/ModuleHeaders.cs b/src/coreclr/src/tools/crossgen2/Common/Internal/Runtime/ModuleHeaders.cs
index c18b0d0566da1b..5c374310df9b76 100644
--- a/src/coreclr/src/tools/crossgen2/Common/Internal/Runtime/ModuleHeaders.cs
+++ b/src/coreclr/src/tools/crossgen2/Common/Internal/Runtime/ModuleHeaders.cs
@@ -36,6 +36,14 @@ internal struct ReadyToRunHeader
};
#pragma warning restore 0169
+ enum ReadyToRunFlag
+ {
+ READYTORUN_FLAG_PLATFORM_NEUTRAL_SOURCE = 0x00000001, // Set if the original IL assembly was platform-neutral
+ READYTORUN_FLAG_SKIP_TYPE_VALIDATION = 0x00000002, // Set of methods with native code was determined using profile data
+ READYTORUN_FLAG_PARTIAL = 0x00000004,
+ READYTORUN_FLAG_NONSHARED_PINVOKE_STUBS = 0x00000008 // PInvoke stubs compiled into image are non-shareable (no secret parameter)
+ };
+
//
// ReadyToRunSectionType IDs are used by the runtime to look up specific global data sections
// from each module linked into the final binary. New sections should be added at the bottom
diff --git a/src/coreclr/src/tools/crossgen2/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/src/tools/crossgen2/Common/JitInterface/CorInfoImpl.cs
index c8aa9897f7af2b..86fdeea87439da 100644
--- a/src/coreclr/src/tools/crossgen2/Common/JitInterface/CorInfoImpl.cs
+++ b/src/coreclr/src/tools/crossgen2/Common/JitInterface/CorInfoImpl.cs
@@ -659,6 +659,11 @@ private uint getMethodAttribsInternal(MethodDesc method)
if (method.IsPInvoke)
{
result |= CorInfoFlag.CORINFO_FLG_PINVOKE;
+
+ if (method.IsRawPInvoke())
+ {
+ result |= CorInfoFlag.CORINFO_FLG_FORCEINLINE;
+ }
}
#if READYTORUN
@@ -774,14 +779,6 @@ private CorInfoInline canInline(CORINFO_METHOD_STRUCT_* callerHnd, CORINFO_METHO
MethodDesc callerMethod = HandleToObject(callerHnd);
MethodDesc calleeMethod = HandleToObject(calleeHnd);
-#if READYTORUN
- // IL stubs don't inline well
- if (calleeMethod.IsPInvoke)
- {
- return CorInfoInline.INLINE_NEVER;
- }
-#endif
-
if (_compilation.CanInline(callerMethod, calleeMethod))
{
// No restrictions on inlining
@@ -3066,10 +3063,6 @@ private uint getJitFlags(ref CORJIT_FLAGS flags, uint sizeInBytes)
if (this.MethodBeingCompiled.IsPInvoke)
{
flags.Set(CorJitFlag.CORJIT_FLAG_IL_STUB);
-
-#if READYTORUN
- flags.Set(CorJitFlag.CORJIT_FLAG_PUBLISH_SECRET_PARAM);
-#endif
}
if (this.MethodBeingCompiled.IsNoOptimization)
diff --git a/src/coreclr/src/tools/crossgen2/Common/TypeSystem/IL/Stubs/PInvokeTargetNativeMethod.Diagnostic.cs b/src/coreclr/src/tools/crossgen2/Common/TypeSystem/IL/Stubs/PInvokeTargetNativeMethod.Diagnostic.cs
new file mode 100644
index 00000000000000..2cb4d1ea33c4b1
--- /dev/null
+++ b/src/coreclr/src/tools/crossgen2/Common/TypeSystem/IL/Stubs/PInvokeTargetNativeMethod.Diagnostic.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Internal.TypeSystem;
+
+namespace Internal.IL.Stubs
+{
+ partial class PInvokeTargetNativeMethod
+ {
+ public override string DiagnosticName
+ {
+ get
+ {
+ return _declMethod.DiagnosticName;
+ }
+ }
+ }
+}
diff --git a/src/coreclr/src/tools/crossgen2/Common/TypeSystem/IL/Stubs/PInvokeTargetNativeMethod.Mangling.cs b/src/coreclr/src/tools/crossgen2/Common/TypeSystem/IL/Stubs/PInvokeTargetNativeMethod.Mangling.cs
new file mode 100644
index 00000000000000..79ea39a9001f17
--- /dev/null
+++ b/src/coreclr/src/tools/crossgen2/Common/TypeSystem/IL/Stubs/PInvokeTargetNativeMethod.Mangling.cs
@@ -0,0 +1,27 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Internal.TypeSystem;
+
+namespace Internal.IL.Stubs
+{
+ partial class PInvokeTargetNativeMethod : IPrefixMangledMethod
+ {
+ MethodDesc IPrefixMangledMethod.BaseMethod
+ {
+ get
+ {
+ return _declMethod;
+ }
+ }
+
+ string IPrefixMangledMethod.Prefix
+ {
+ get
+ {
+ return "rawpinvoke";
+ }
+ }
+ }
+}
diff --git a/src/coreclr/src/tools/crossgen2/Common/TypeSystem/IL/Stubs/PInvokeTargetNativeMethod.Sorting.cs b/src/coreclr/src/tools/crossgen2/Common/TypeSystem/IL/Stubs/PInvokeTargetNativeMethod.Sorting.cs
new file mode 100644
index 00000000000000..ea754346f7a636
--- /dev/null
+++ b/src/coreclr/src/tools/crossgen2/Common/TypeSystem/IL/Stubs/PInvokeTargetNativeMethod.Sorting.cs
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Internal.TypeSystem;
+
+namespace Internal.IL.Stubs
+{
+ // Functionality related to deterministic ordering of types
+ partial class PInvokeTargetNativeMethod
+ {
+ protected internal override int ClassCode => -1626939381;
+
+ protected internal override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer)
+ {
+ var otherMethod = (PInvokeTargetNativeMethod)other;
+ return comparer.Compare(_declMethod, otherMethod._declMethod);
+ }
+ }
+}
diff --git a/src/coreclr/src/tools/crossgen2/Common/TypeSystem/IL/Stubs/PInvokeTargetNativeMethod.cs b/src/coreclr/src/tools/crossgen2/Common/TypeSystem/IL/Stubs/PInvokeTargetNativeMethod.cs
new file mode 100644
index 00000000000000..519dee4e9abfca
--- /dev/null
+++ b/src/coreclr/src/tools/crossgen2/Common/TypeSystem/IL/Stubs/PInvokeTargetNativeMethod.cs
@@ -0,0 +1,92 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Internal.TypeSystem;
+
+namespace Internal.IL.Stubs
+{
+ ///
+ /// Synthetic method that represents the actual PInvoke target method.
+ /// All parameters are simple types. There will be no code
+ /// generated for this method. Instead, a static reference to a symbol will be emitted.
+ ///
+ public sealed partial class PInvokeTargetNativeMethod : MethodDesc
+ {
+ private readonly MethodDesc _declMethod;
+ private readonly MethodSignature _signature;
+
+ public MethodDesc Target
+ {
+ get
+ {
+ return _declMethod;
+ }
+ }
+
+ public PInvokeTargetNativeMethod(MethodDesc declMethod, MethodSignature signature)
+ {
+ _declMethod = declMethod;
+ _signature = signature;
+ }
+
+ public override TypeSystemContext Context
+ {
+ get
+ {
+ return _declMethod.Context;
+ }
+ }
+
+ public override TypeDesc OwningType
+ {
+ get
+ {
+ return _declMethod.OwningType;
+ }
+ }
+
+ public override MethodSignature Signature
+ {
+ get
+ {
+ return _signature;
+ }
+ }
+
+ public override string Name
+ {
+ get
+ {
+ return _declMethod.Name;
+ }
+ }
+
+ public override bool HasCustomAttribute(string attributeNamespace, string attributeName)
+ {
+ return false;
+ }
+
+ public override bool IsPInvoke
+ {
+ get
+ {
+ return true;
+ }
+ }
+
+ public override bool IsNoInlining
+ {
+ get
+ {
+ // This method does not have real IL body. NoInlining stops the JIT asking for it.
+ return true;
+ }
+ }
+
+ public override PInvokeMetadata GetPInvokeMethodMetadata()
+ {
+ return _declMethod.GetPInvokeMethodMetadata();
+ }
+ }
+}
diff --git a/src/coreclr/src/tools/crossgen2/Common/TypeSystem/Interop/IL/Marshaller.cs b/src/coreclr/src/tools/crossgen2/Common/TypeSystem/Interop/IL/Marshaller.cs
index 624ff457e07ed3..2c27239f17ec49 100644
--- a/src/coreclr/src/tools/crossgen2/Common/TypeSystem/Interop/IL/Marshaller.cs
+++ b/src/coreclr/src/tools/crossgen2/Common/TypeSystem/Interop/IL/Marshaller.cs
@@ -155,7 +155,7 @@ internal virtual bool CleanupRequired
protected PInvokeILCodeStreams _ilCodeStreams;
protected Home _managedHome;
protected Home _nativeHome;
-#endregion
+ #endregion
enum HomeType
{
@@ -255,7 +255,7 @@ public void StoreValue(ILCodeStream stream)
int _argIndex;
}
-#region Creation of marshallers
+ #region Creation of marshallers
///
/// Protected ctor
@@ -365,12 +365,54 @@ public static Marshaller CreateMarshaller(TypeDesc parameterType,
return marshaller;
}
- public bool IsMarshallingRequired()
+ public static Marshaller[] GetMarshallersForMethod(MethodDesc targetMethod)
{
- if (Out)
- return true;
+ Debug.Assert(targetMethod.IsPInvoke);
+
+ MarshalDirection direction = MarshalDirection.Forward;
+ MethodSignature methodSig = targetMethod.Signature;
+ PInvokeFlags flags = targetMethod.GetPInvokeMethodMetadata().Flags;
+
+ ParameterMetadata[] parameterMetadataArray = targetMethod.GetParameterMetadata();
+ Marshaller[] marshallers = new Marshaller[methodSig.Length + 1];
+ ParameterMetadata parameterMetadata;
+
+ for (int i = 0, parameterIndex = 0; i < marshallers.Length; i++)
+ {
+ Debug.Assert(parameterIndex == parameterMetadataArray.Length || i <= parameterMetadataArray[parameterIndex].Index);
+ if (parameterIndex == parameterMetadataArray.Length || i < parameterMetadataArray[parameterIndex].Index)
+ {
+ // if we don't have metadata for the parameter, create a dummy one
+ parameterMetadata = new ParameterMetadata(i, ParameterMetadataAttributes.None, null);
+ }
+ else
+ {
+ Debug.Assert(i == parameterMetadataArray[parameterIndex].Index);
+ parameterMetadata = parameterMetadataArray[parameterIndex++];
+ }
- switch (MarshallerKind)
+ TypeDesc parameterType = (i == 0) ? methodSig.ReturnType : methodSig[i - 1]; //first item is the return type
+ marshallers[i] = CreateMarshaller(parameterType,
+ MarshallerType.Argument,
+ parameterMetadata.MarshalAsDescriptor,
+ direction,
+ marshallers,
+ parameterMetadata.Index,
+ flags,
+ parameterMetadata.In,
+ parameterMetadata.Out,
+ parameterMetadata.Return);
+ }
+
+ return marshallers;
+ }
+ #endregion
+
+
+ #region Marshalling Requirement Checking
+ private static bool IsMarshallingRequired(MarshallerKind kind)
+ {
+ switch (kind)
{
case MarshallerKind.Enum:
case MarshallerKind.BlittableValue:
@@ -381,7 +423,54 @@ public bool IsMarshallingRequired()
}
return true;
}
-#endregion
+
+ private bool IsMarshallingRequired()
+ {
+ return Out || IsMarshallingRequired(MarshallerKind);
+ }
+
+ public static bool IsMarshallingRequired(MethodDesc targetMethod)
+ {
+ Debug.Assert(targetMethod.IsPInvoke);
+
+ if (targetMethod.GetPInvokeMethodMetadata().Flags.SetLastError)
+ return true;
+
+ var marshallers = GetMarshallersForMethod(targetMethod);
+
+ for (int i = 0; i < marshallers.Length; i++)
+ {
+ if (marshallers[i].IsMarshallingRequired())
+ return true;
+ }
+ return false;
+ }
+
+ public static bool IsMarshallingRequired(MethodSignature methodSig, ParameterMetadata[] paramMetadata)
+ {
+ for (int i = 0, paramIndex = 0; i < methodSig.Length + 1; i++)
+ {
+ ParameterMetadata parameterMetadata = (paramIndex == paramMetadata.Length || i < paramMetadata[paramIndex].Index) ?
+ new ParameterMetadata(i, ParameterMetadataAttributes.None, null) :
+ paramMetadata[paramIndex++];
+
+ TypeDesc parameterType = (i == 0) ? methodSig.ReturnType : methodSig[i - 1]; //first item is the return type
+
+ MarshallerKind marshallerKind = MarshalHelpers.GetMarshallerKind(
+ parameterType,
+ parameterMetadata.MarshalAsDescriptor,
+ parameterMetadata.Return,
+ isAnsi: true,
+ MarshallerType.Argument,
+ out MarshallerKind elementMarshallerKind);
+
+ if (IsMarshallingRequired(marshallerKind))
+ return true;
+ }
+
+ return false;
+ }
+ #endregion
public virtual void EmitMarshallingIL(PInvokeILCodeStreams pInvokeILCodeStreams)
{
@@ -1095,17 +1184,17 @@ protected override void TransformManagedToNative(ILCodeStream codeStream)
var lRangeCheck = emitter.NewCodeLabel();
var lLoopHeader = emitter.NewCodeLabel();
- var lNullArray = emitter.NewCodeLabel();
+ var lNullArray = emitter.NewCodeLabel();
var vNativeTemp = emitter.NewLocal(NativeType);
var vIndex = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32));
var vSizeOf = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.IntPtr));
var vLength = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32));
-
+
// Check for null array
LoadManagedValue(codeStream);
codeStream.Emit(ILOpcode.brfalse, lNullArray);
-
+
// loads the number of elements
EmitElementCount(codeStream, MarshalDirection.Forward);
codeStream.EmitStLoc(vLength);
@@ -1405,7 +1494,7 @@ private bool ShouldBePinned
{
get
{
- return MarshalDirection == MarshalDirection.Forward
+ return MarshalDirection == MarshalDirection.Forward
&& MarshallerType != MarshallerType.Field
&& !IsManagedByRef
&& In
@@ -1529,7 +1618,7 @@ protected override void AllocAndTransformManagedToNative(ILCodeStream codeStream
//
// ANSI marshalling. Allocate a byte array, copy characters
//
-
+
#if READYTORUN
var stringToAnsi =
Context.SystemModule.GetKnownType("System.StubHelpers", "AnsiBSTRMarshaler")
@@ -1671,7 +1760,7 @@ private void AllocSafeHandle(ILCodeStream codeStream)
#endif
codeStream.Emit(ILOpcode.newobj, emitter.NewToken(exceptionCtor));
codeStream.Emit(ILOpcode.throw_);
-
+
// This is unreachable, but it maintains invariants about stack height prescribed by ECMA-335
codeStream.Emit(ILOpcode.ldnull);
return;
diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/HeaderNode.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/HeaderNode.cs
index 757ef62aca7d05..b984f8fc7a683e 100644
--- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/HeaderNode.cs
+++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/HeaderNode.cs
@@ -119,7 +119,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
builder.EmitShort((short)(ReadyToRunHeaderConstants.CurrentMinorVersion));
// ReadyToRunHeader.Flags
- builder.EmitInt(0);
+ builder.EmitInt((int)ReadyToRunFlag.READYTORUN_FLAG_NONSHARED_PINVOKE_STUBS);
// ReadyToRunHeader.NumberOfSections
ObjectDataBuilder.Reservation sectionCountReservation = builder.ReserveInt();
diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/MethodExtensions.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/MethodExtensions.cs
new file mode 100644
index 00000000000000..8bbe4255a4de1f
--- /dev/null
+++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/MethodExtensions.cs
@@ -0,0 +1,22 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Internal.TypeSystem;
+
+namespace ILCompiler
+{
+ static class MethodExtensions
+ {
+ ///
+ /// Returns true if is an actual native entrypoint.
+ /// There's a distinction between when a method reports it's a PInvoke in the metadata
+ /// versus how it's treated in the compiler. For many PInvoke methods the compiler will generate
+ /// an IL body. The methods with an IL method body shouldn't be treated as PInvoke within the compiler.
+ ///
+ public static bool IsRawPInvoke(this MethodDesc method)
+ {
+ return method.IsPInvoke && (method is Internal.IL.Stubs.PInvokeTargetNativeMethod);
+ }
+ }
+}
diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs
index 059dceaba1b92a..e9cfb44703e4a4 100644
--- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs
+++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs
@@ -140,8 +140,7 @@ protected override MethodILData CreateValueFromKey(MethodDesc key)
&& key.IsPInvoke
&& _compilationModuleGroup.GeneratesPInvoke(key))
{
- // TODO: enable when IL Stubs are fixed to be non-shared
- // methodIL = PInvokeILEmitter.EmitIL(key);
+ methodIL = PInvokeILEmitter.EmitIL(key);
}
return new MethodILData() { Method = key, MethodIL = methodIL };
diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunSingleAssemblyCompilationModuleGroup.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunSingleAssemblyCompilationModuleGroup.cs
index 271dcdf8bf2ad8..a079c20134351f 100644
--- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunSingleAssemblyCompilationModuleGroup.cs
+++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/ReadyToRunSingleAssemblyCompilationModuleGroup.cs
@@ -7,7 +7,7 @@
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;
-
+using Internal.TypeSystem.Interop;
using Debug = System.Diagnostics.Debug;
namespace ILCompiler
@@ -191,13 +191,19 @@ public override bool CanInline(MethodDesc callerMethod, MethodDesc calleeMethod)
public override bool GeneratesPInvoke(MethodDesc method)
{
- // PInvokes depend on details of the core library.
+ // PInvokes depend on details of the core library, so for now only compile them if:
+ // 1) We're compiling the core library module, or
+ // 2) We're compiling any module, and no marshalling is needed
//
- // We allow compiling them if both the module of the pinvoke and the core library
- // are in the version bubble.
+ // TODO Future: consider compiling PInvokes with complex marshalling in version bubble
+ // mode when the core library is included in the bubble.
+
Debug.Assert(method is EcmaMethod);
- return _versionBubbleModuleSet.Contains(((EcmaMethod)method).Module)
- && _versionBubbleModuleSet.Contains(method.Context.SystemModule);
+
+ if (((EcmaMethod)method).Module.Equals(method.Context.SystemModule))
+ return true;
+
+ return !Marshaller.IsMarshallingRequired(method);
}
}
}
diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/IL/Stubs/PInvokeILEmitter.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/IL/Stubs/PInvokeILEmitter.cs
index 954abaf6c23e4e..bf0975621aab13 100644
--- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/IL/Stubs/PInvokeILEmitter.cs
+++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/IL/Stubs/PInvokeILEmitter.cs
@@ -3,7 +3,11 @@
// See the LICENSE file in the project root for more information.
using System;
+using System.Net;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using ILCompiler;
using Internal.TypeSystem;
using Internal.TypeSystem.Interop;
using Internal.JitInterface;
@@ -26,48 +30,7 @@ private PInvokeILEmitter(MethodDesc targetMethod)
Debug.Assert(targetMethod.IsPInvoke);
_targetMethod = targetMethod;
_importMetadata = targetMethod.GetPInvokeMethodMetadata();
-
- _marshallers = InitializeMarshallers(targetMethod, _importMetadata.Flags);
- }
-
- private static Marshaller[] InitializeMarshallers(MethodDesc targetMethod, PInvokeFlags flags)
- {
- MarshalDirection direction = MarshalDirection.Forward;
- MethodSignature methodSig = targetMethod.Signature;
-
- ParameterMetadata[] parameterMetadataArray = targetMethod.GetParameterMetadata();
- Marshaller[] marshallers = new Marshaller[methodSig.Length + 1];
- int parameterIndex = 0;
- ParameterMetadata parameterMetadata;
-
- for (int i = 0; i < marshallers.Length; i++)
- {
- Debug.Assert(parameterIndex == parameterMetadataArray.Length || i <= parameterMetadataArray[parameterIndex].Index);
- if (parameterIndex == parameterMetadataArray.Length || i < parameterMetadataArray[parameterIndex].Index)
- {
- // if we don't have metadata for the parameter, create a dummy one
- parameterMetadata = new ParameterMetadata(i, ParameterMetadataAttributes.None, null);
- }
- else
- {
- Debug.Assert(i == parameterMetadataArray[parameterIndex].Index);
- parameterMetadata = parameterMetadataArray[parameterIndex++];
- }
- TypeDesc parameterType = (i == 0) ? methodSig.ReturnType : methodSig[i - 1]; //first item is the return type
- marshallers[i] = Marshaller.CreateMarshaller(parameterType,
- MarshallerType.Argument,
- parameterMetadata.MarshalAsDescriptor,
- direction,
- marshallers,
- parameterMetadata.Index,
- flags,
- parameterMetadata.In,
- parameterMetadata.Out,
- parameterMetadata.Return
- );
- }
-
- return marshallers;
+ _marshallers = Marshaller.GetMarshallersForMethod(targetMethod);
}
private void EmitPInvokeCall(PInvokeILCodeStreams ilCodeStreams)
@@ -93,18 +56,13 @@ private void EmitPInvokeCall(PInvokeILCodeStreams ilCodeStreams)
nativeParameterTypes[i - 1] = _marshallers[i].NativeParameterType;
}
- callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken(
- stubHelpersType.GetKnownMethod("GetStubContext", null)));
- callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken(
- stubHelpersType.GetKnownMethod("GetNDirectTarget", null)));
-
- MethodSignatureFlags unmanagedCallConv = _importMetadata.Flags.UnmanagedCallingConvention;
-
MethodSignature nativeSig = new MethodSignature(
- _targetMethod.Signature.Flags | unmanagedCallConv, 0, nativeReturnType,
+ _targetMethod.Signature.Flags, 0, nativeReturnType,
nativeParameterTypes);
- callsiteSetupCodeStream.Emit(ILOpcode.calli, emitter.NewToken(nativeSig));
+ var rawTargetMethod = new PInvokeTargetNativeMethod(_targetMethod, nativeSig);
+
+ callsiteSetupCodeStream.Emit(ILOpcode.call, emitter.NewToken(rawTargetMethod));
// if the SetLastError flag is set in DllImport, call the PInvokeMarshal.
// SaveLastWin32Error so that last error can be used later by calling
@@ -133,7 +91,7 @@ private MethodIL EmitIL()
_marshallers[0].LoadReturnValue(unmarshallingCodestream);
unmarshallingCodestream.Emit(ILOpcode.ret);
- return new PInvokeILStubMethodIL((ILStubMethodIL)emitter.Link(_targetMethod), IsStubRequired());
+ return new PInvokeILStubMethodIL((ILStubMethodIL)emitter.Link(_targetMethod));
}
public static MethodIL EmitIL(MethodDesc method)
@@ -151,31 +109,15 @@ public static MethodIL EmitIL(MethodDesc method)
throw new RequiresRuntimeJitException(method);
}
}
-
- private bool IsStubRequired()
- {
- Debug.Assert(_targetMethod.IsPInvoke);
-
- if (_importMetadata.Flags.SetLastError)
- {
- return true;
- }
-
- for (int i = 0; i < _marshallers.Length; i++)
- {
- if (_marshallers[i].IsMarshallingRequired())
- return true;
- }
- return false;
- }
}
public sealed class PInvokeILStubMethodIL : ILStubMethodIL
{
- public bool IsStubRequired { get; }
- public PInvokeILStubMethodIL(ILStubMethodIL methodIL, bool isStubRequired) : base(methodIL)
+ public bool IsMarshallingRequired { get; }
+
+ public PInvokeILStubMethodIL(ILStubMethodIL methodIL) : base(methodIL)
{
- IsStubRequired = isStubRequired;
+ IsMarshallingRequired = Marshaller.IsMarshallingRequired(methodIL.OwningMethod);
}
}
}
diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj
index 49afbe7fb49341..89aa91e3f5b5e0 100644
--- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj
+++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj
@@ -169,6 +169,7 @@
+
diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
index e44aa29d650a04..4e51eabdb4f929 100644
--- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
+++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs
@@ -11,6 +11,7 @@
using System.Runtime.InteropServices;
using Internal.IL;
+using Internal.IL.Stubs;
using Internal.Text;
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;
@@ -661,6 +662,9 @@ private ModuleToken HandleToModuleToken(ref CORINFO_RESOLVED_TOKEN pResolvedToke
if (resultDef is MethodDesc)
{
+ if (resultDef is IL.Stubs.PInvokeTargetNativeMethod rawPinvoke)
+ resultDef = rawPinvoke.Target;
+
Debug.Assert(resultDef is EcmaMethod);
Debug.Assert(_compilation.NodeFactory.CompilationModuleGroup.VersionsWithType(((EcmaMethod)resultDef).OwningType));
token = (mdToken)MetadataTokens.GetToken(((EcmaMethod)resultDef).Handle);
@@ -1403,6 +1407,10 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO
{
nonUnboxingMethod = methodToCall.GetUnboxedMethod();
}
+ if (nonUnboxingMethod is IL.Stubs.PInvokeTargetNativeMethod rawPinvoke)
+ {
+ nonUnboxingMethod = rawPinvoke.Target;
+ }
// READYTORUN: FUTURE: Direct calls if possible
pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol(
@@ -1926,7 +1934,10 @@ private HRESULT getMethodBlockCounts(CORINFO_METHOD_STRUCT_* ftnHnd, ref uint pC
private void getAddressOfPInvokeTarget(CORINFO_METHOD_STRUCT_* method, ref CORINFO_CONST_LOOKUP pLookup)
{
- EcmaMethod ecmaMethod = (EcmaMethod)HandleToObject(method);
+ MethodDesc methodDesc = HandleToObject(method);
+ if (methodDesc is IL.Stubs.PInvokeTargetNativeMethod rawPInvoke)
+ methodDesc = rawPInvoke.Target;
+ EcmaMethod ecmaMethod = (EcmaMethod)methodDesc;
ModuleToken moduleToken = new ModuleToken(ecmaMethod.Module, ecmaMethod.Handle);
MethodWithToken methodWithToken = new MethodWithToken(ecmaMethod, moduleToken, constrainedType: null);
@@ -1944,13 +1955,26 @@ private bool pInvokeMarshalingRequired(CORINFO_METHOD_STRUCT_* handle, CORINFO_S
if (handle != null)
{
- EcmaMethod method = (EcmaMethod)HandleToObject(handle);
- return MethodRequiresMarshaling(method);
+ var method = HandleToObject(handle);
+ if (method.IsRawPInvoke())
+ {
+ return false;
+ }
+
+ MethodIL stubIL = _compilation.GetMethodIL(method);
+ if (stubIL == null)
+ {
+ // This is the case of a PInvoke method that requires marshallers, which we can't use in this compilation
+ Debug.Assert(!_compilation.NodeFactory.CompilationModuleGroup.GeneratesPInvoke(method));
+ return true;
+ }
+
+ return ((PInvokeILStubMethodIL)stubIL).IsMarshallingRequired;
}
else
{
var sig = (MethodSignature)HandleToObject((IntPtr)callSiteSig->pSig);
- return SignatureRequiresMarshaling(sig);
+ return Marshaller.IsMarshallingRequired(sig, Array.Empty());
}
}
@@ -1966,86 +1990,6 @@ private bool canGetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig)
throw new RequiresRuntimeJitException($"{MethodBeingCompiled} -> {nameof(canGetCookieForPInvokeCalliSig)}");
}
- private bool MethodRequiresMarshaling(EcmaMethod method)
- {
- if (method.GetPInvokeMethodMetadata().Flags.SetLastError)
- {
- // SetLastError is handled by stub
- return true;
- }
-
- if ((method.ImplAttributes & MethodImplAttributes.PreserveSig) == 0)
- {
- // HRESULT swapping is handled by stub
- return true;
- }
-
- return SignatureRequiresMarshaling(method.Signature);
- }
-
- private bool SignatureRequiresMarshaling(MethodSignature sig)
- {
- if (TypeRequiresMarshaling(sig.ReturnType, isReturnType: true))
- {
- return true;
- }
-
- foreach (TypeDesc argType in sig)
- {
- if (TypeRequiresMarshaling(argType, isReturnType: false))
- {
- return true;
- }
- }
-
- return false;
- }
-
- private bool TypeRequiresMarshaling(TypeDesc type, bool isReturnType)
- {
- switch (type.Category)
- {
- case TypeFlags.Pointer:
- // TODO: custom modifiers S(NeedsCopyConstructorModifier, IsCopyConstructed)
- break;
-
- case TypeFlags.Enum:
- if (!_compilation.NodeFactory.CompilationModuleGroup.VersionsWithType(type))
- {
- return true;
- }
- break;
-
- case TypeFlags.ValueType:
- if (!MarshalUtils.IsBlittableType(type))
- {
- return true;
- }
- if (!_compilation.NodeFactory.CompilationModuleGroup.VersionsWithType(type))
- {
- return true;
- }
- if (isReturnType && !type.IsPrimitive)
- {
- return true;
- }
- break;
-
- case TypeFlags.Boolean:
- case TypeFlags.Char:
- return true;
-
- default:
- if (!type.IsPrimitive)
- {
- return true;
- }
- break;
- }
-
- return false;
- }
-
private int SizeOfPInvokeTransitionFrame => ReadyToRunRuntimeConstants.READYTORUN_PInvokeTransitionFrameSizeInPointerUnits * _compilation.NodeFactory.Target.PointerSize;
}
}
diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.TypeSystem.ReadyToRun/ILCompiler.TypeSystem.ReadyToRun.csproj b/src/coreclr/src/tools/crossgen2/ILCompiler.TypeSystem.ReadyToRun/ILCompiler.TypeSystem.ReadyToRun.csproj
index df1d68c0de758b..83edec65cde8bc 100644
--- a/src/coreclr/src/tools/crossgen2/ILCompiler.TypeSystem.ReadyToRun/ILCompiler.TypeSystem.ReadyToRun.csproj
+++ b/src/coreclr/src/tools/crossgen2/ILCompiler.TypeSystem.ReadyToRun/ILCompiler.TypeSystem.ReadyToRun.csproj
@@ -441,6 +441,18 @@
IL\Stubs\ILEmitter.cs
+
+ IL\Stubs\PInvokeTargetNativeMethod.cs
+
+
+ IL\Stubs\PInvokeTargetNativeMethod.Diagnostic.cs
+
+
+ IL\Stubs\PInvokeTargetNativeMethod.Mangling.cs
+
+
+ IL\Stubs\PInvokeTargetNativeMethod.Sorting.cs
+
TypeSystem\CodeGen\FieldDesc.Serialization.cs
diff --git a/src/coreclr/src/tools/r2rdump/R2RHeader.cs b/src/coreclr/src/tools/r2rdump/R2RHeader.cs
index 71ee6cab24a048..f237e379889cc1 100644
--- a/src/coreclr/src/tools/r2rdump/R2RHeader.cs
+++ b/src/coreclr/src/tools/r2rdump/R2RHeader.cs
@@ -16,10 +16,10 @@ public class R2RHeader
[Flags]
public enum ReadyToRunFlag
{
- NONE = 0x00000000,
- READYTORUN_FLAG_PLATFORM_NEUTRAL_SOURCE = 0x00000001,
- READYTORUN_FLAG_SKIP_TYPE_VALIDATION = 0x00000002,
+ READYTORUN_FLAG_PLATFORM_NEUTRAL_SOURCE = 0x00000001, // Set if the original IL assembly was platform-neutral
+ READYTORUN_FLAG_SKIP_TYPE_VALIDATION = 0x00000002, // Set of methods with native code was determined using profile data
READYTORUN_FLAG_PARTIAL = 0x00000004,
+ READYTORUN_FLAG_CROSSGEN2_IMAGE = 0x00000008 // Set if image was compiled using crossgen2
}
///
diff --git a/src/coreclr/src/vm/prestub.cpp b/src/coreclr/src/vm/prestub.cpp
index 509d3d315b5822..c369ba8d46c2b4 100644
--- a/src/coreclr/src/vm/prestub.cpp
+++ b/src/coreclr/src/vm/prestub.cpp
@@ -72,6 +72,14 @@ void MethodDesc::Init()
#endif
+#define LOG_USING_R2R_CODE(method) LOG((LF_ZAP, LL_INFO10000, \
+ "ZAP: Using R2R precompiled code" FMT_ADDR " for %s.%s sig=\"%s\" (token %x).\n", \
+ DBG_ADDR(pCode), \
+ m_pszDebugClassName, \
+ m_pszDebugMethodName, \
+ m_pszDebugMethodSignature, \
+ GetMemberDef()));
+
//==========================================================================
PCODE MethodDesc::DoBackpatch(MethodTable * pMT, MethodTable *pDispatchingMT, BOOL fFullBackPatch)
@@ -352,30 +360,27 @@ PCODE MethodDesc::PrepareILBasedCode(PrepareCodeConfig* pConfig)
if (pConfig->MayUsePrecompiledCode())
{
#ifdef FEATURE_READYTORUN
- // TODO: Remove IsSystem check when IL Stubs are fixed to be non-shared
- if (this->IsDynamicMethod() && GetLoaderModule()->IsSystem() && MayUsePrecompiledILStub())
+ if (IsDynamicMethod() && GetLoaderModule()->IsSystem() && MayUsePrecompiledILStub())
{
- DynamicMethodDesc *stubMethodDesc = this->AsDynamicMethodDesc();
- if (stubMethodDesc->IsILStub() && stubMethodDesc->IsPInvokeStub())
+ // Images produced using crossgen2 have non-shareable pinvoke stubs which can't be used with the IL
+ // stubs that the runtime generates (they take no secret parameter, and each pinvoke has a separate code)
+ if (GetModule()->IsReadyToRun() && !GetModule()->GetReadyToRunInfo()->HasNonShareablePInvokeStubs())
{
- ILStubResolver *pStubResolver = stubMethodDesc->GetILStubResolver();
- if (pStubResolver->IsCLRToNativeInteropStub())
+ DynamicMethodDesc* stubMethodDesc = this->AsDynamicMethodDesc();
+ if (stubMethodDesc->IsILStub() && stubMethodDesc->IsPInvokeStub())
{
- MethodDesc *pTargetMD = stubMethodDesc->GetILStubResolver()->GetStubTargetMethodDesc();
- if (pTargetMD != NULL)
+ ILStubResolver* pStubResolver = stubMethodDesc->GetILStubResolver();
+ if (pStubResolver->IsCLRToNativeInteropStub())
{
- pCode = pTargetMD->GetPrecompiledR2RCode(pConfig);
- if (pCode != NULL)
+ MethodDesc* pTargetMD = stubMethodDesc->GetILStubResolver()->GetStubTargetMethodDesc();
+ if (pTargetMD != NULL)
{
- LOG((LF_ZAP, LL_INFO10000,
- "ZAP: Using R2R precompiled code" FMT_ADDR " for %s.%s sig=\"%s\" (token %x).\n",
- DBG_ADDR(pCode),
- m_pszDebugClassName,
- m_pszDebugMethodName,
- m_pszDebugMethodSignature,
- GetMemberDef()));
-
- pConfig->SetNativeCode(pCode, &pCode);
+ pCode = pTargetMD->GetPrecompiledR2RCode(pConfig);
+ if (pCode != NULL)
+ {
+ LOG_USING_R2R_CODE(this);
+ pConfig->SetNativeCode(pCode, &pCode);
+ }
}
}
}
@@ -430,13 +435,7 @@ PCODE MethodDesc::GetPrecompiledCode(PrepareCodeConfig* pConfig)
pCode = GetPrecompiledR2RCode(pConfig);
if (pCode != NULL)
{
- LOG((LF_ZAP, LL_INFO10000,
- "ZAP: Using R2R precompiled code" FMT_ADDR " for %s.%s sig=\"%s\" (token %x).\n",
- DBG_ADDR(pCode),
- m_pszDebugClassName,
- m_pszDebugMethodName,
- m_pszDebugMethodSignature,
- GetMemberDef()));
+ LOG_USING_R2R_CODE(this);
if (pConfig->SetNativeCode(pCode, &pCode))
{
@@ -2025,7 +2024,21 @@ PCODE MethodDesc::DoPrestub(MethodTable *pDispatchingMT)
} // end else if (IsIL() || IsNoMetadata())
else if (IsNDirect())
{
- pCode = GetStubForInteropMethod(this);
+ if (GetModule()->IsReadyToRun() && GetModule()->GetReadyToRunInfo()->HasNonShareablePInvokeStubs() && MayUsePrecompiledILStub())
+ {
+ // In crossgen2, we compile non-shareable IL stubs for pinvokes. If we can find code for such
+ // a stub, we'll use it directly instead and avoid emitting an IL stub.
+ PrepareCodeConfig config(NativeCodeVersion(this), TRUE, TRUE);
+ pCode = GetPrecompiledR2RCode(&config);
+ if (pCode != NULL)
+ {
+ LOG_USING_R2R_CODE(this);
+ }
+ }
+
+ if (pCode == NULL)
+ pCode = GetStubForInteropMethod(this);
+
GetOrCreatePrecode();
}
else if (IsFCall())
diff --git a/src/coreclr/src/vm/readytoruninfo.h b/src/coreclr/src/vm/readytoruninfo.h
index c6057c7fdeb522..370dca33ce3452 100644
--- a/src/coreclr/src/vm/readytoruninfo.h
+++ b/src/coreclr/src/vm/readytoruninfo.h
@@ -74,6 +74,12 @@ class ReadyToRunInfo
return m_pHeader->Flags & READYTORUN_FLAG_PARTIAL;
}
+ BOOL HasNonShareablePInvokeStubs()
+ {
+ LIMITED_METHOD_CONTRACT;
+ return m_pHeader->Flags & READYTORUN_FLAG_NONSHARED_PINVOKE_STUBS;
+ }
+
PTR_PEImageLayout GetImage()
{
LIMITED_METHOD_CONTRACT;