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;