From 52ec9ae4689006370150fb688471aa57ec5d6c94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Tue, 25 Sep 2018 14:10:12 +0200 Subject: [PATCH] Make ILProvider configurable (#6366) Refactoring to allow us having custom IL providers. * The immediate goal is to allow specifying an IL provider for ready to run compilations that doesn't do the CoreRT-specific IL intrinsic expansion. * The medium term goal is to allow IL scanner to provide IL based on the scanning results. This allows optimization that Project N dependency reducer does, such as replacing IL bodies of instance methods on types that were never seen as allocated with `throw`. * The long term goal is to allow other IL level optimizations (based on the IL scanner or other inputs). This pretty much implements https://github.com/dotnet/corert/pull/6345#discussion_r219696075, with some additions: I also got rid of `McgInteropSupport` (this is vestige of an old plan to do P/Invokes based on the MCG tool - we abandoned that plan), and I did a simplification in PrecomputedMetadataManager (we only want to read the ECMA IL - no need to use the full blown IL provider). --- .../src/TypeSystem/IL/CoreRTILProvider.cs | 317 ++++++++++++++++ src/Common/src/TypeSystem/IL/ILProvider.cs | 358 +----------------- .../src/TypeSystem/IL/McgInteropSupport.cs | 111 ------ .../src/Compiler/Compilation.cs | 86 ++++- .../src/Compiler/CompilationBuilder.cs | 8 +- .../src/Compiler/ILScanner.cs | 3 +- .../src/Compiler/ILScannerBuilder.cs | 7 +- .../src/Compiler/MethodExtensions.cs | 2 +- .../Compiler/PrecomputedMetadataManager.cs | 17 +- .../src/Compiler/RyuJitCompilation.cs | 3 +- .../src/Compiler/RyuJitCompilationBuilder.cs | 15 +- .../src/IL/Stubs/PInvokeILProvider.cs | 4 +- .../src/ILCompiler.Compiler.csproj | 6 +- .../src/Compiler/CppCodegenCompilation.cs | 4 +- .../Compiler/CppCodegenCompilationBuilder.cs | 15 +- .../Compiler/WebAssemblyCodegenCompilation.cs | 6 +- .../WebAssemblyCodegenCompilationBuilder.cs | 15 +- .../Runtime/JitSupport/JitCompilation.cs | 10 +- .../src/System.Private.Jit.csproj | 2 +- 19 files changed, 480 insertions(+), 509 deletions(-) create mode 100644 src/Common/src/TypeSystem/IL/CoreRTILProvider.cs delete mode 100644 src/Common/src/TypeSystem/IL/McgInteropSupport.cs diff --git a/src/Common/src/TypeSystem/IL/CoreRTILProvider.cs b/src/Common/src/TypeSystem/IL/CoreRTILProvider.cs new file mode 100644 index 00000000000..eef1b526bcc --- /dev/null +++ b/src/Common/src/TypeSystem/IL/CoreRTILProvider.cs @@ -0,0 +1,317 @@ +// 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 System; + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using Internal.IL.Stubs; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.IL +{ + public sealed class CoreRTILProvider : ILProvider + { + private MethodIL TryGetRuntimeImplementedMethodIL(MethodDesc method) + { + // Provides method bodies for runtime implemented methods. It can return null for + // methods that are treated specially by the codegen. + + Debug.Assert(method.IsRuntimeImplemented); + + TypeDesc owningType = method.OwningType; + + if (owningType.IsDelegate) + { + return DelegateMethodILEmitter.EmitIL(method); + } + + return null; + } + + /// + /// Provides method bodies for intrinsics recognized by the compiler. + /// It can return null if it's not an intrinsic recognized by the compiler, + /// but an intrinsic e.g. recognized by codegen. + /// + private MethodIL TryGetIntrinsicMethodIL(MethodDesc method) + { + Debug.Assert(method.IsIntrinsic); + + MetadataType owningType = method.OwningType as MetadataType; + if (owningType == null) + return null; + + switch (owningType.Name) + { + case "Unsafe": + { + if (owningType.Namespace == "Internal.Runtime.CompilerServices") + return UnsafeIntrinsics.EmitIL(method); + } + break; + case "Debug": + { + if (owningType.Namespace == "System.Diagnostics" && method.Name == "DebugBreak") + return new ILStubMethodIL(method, new byte[] { (byte)ILOpcode.break_, (byte)ILOpcode.ret }, Array.Empty(), null); + } + break; + case "EETypePtr": + { + if (owningType.Namespace == "System" && method.Name == "EETypePtrOf") + return EETypePtrOfIntrinsic.EmitIL(method); + } + break; + case "RuntimeAugments": + { + if (owningType.Namespace == "Internal.Runtime.Augments" && method.Name == "GetCanonType") + return GetCanonTypeIntrinsic.EmitIL(method); + } + break; + case "EEType": + { + if (owningType.Namespace == "Internal.Runtime" && method.Name == "get_SupportsRelativePointers") + { + ILOpcode value = method.Context.Target.SupportsRelativePointers ? + ILOpcode.ldc_i4_1 : ILOpcode.ldc_i4_0; + return new ILStubMethodIL(method, new byte[] { (byte)value, (byte)ILOpcode.ret }, Array.Empty(), null); + } + } + break; + } + + return null; + } + + /// + /// Provides method bodies for intrinsics recognized by the compiler that + /// are specialized per instantiation. It can return null if the intrinsic + /// is not recognized. + /// + private MethodIL TryGetPerInstantiationIntrinsicMethodIL(MethodDesc method) + { + Debug.Assert(method.IsIntrinsic); + + MetadataType owningType = method.OwningType.GetTypeDefinition() as MetadataType; + if (owningType == null) + return null; + + string methodName = method.Name; + + switch (owningType.Name) + { + case "RuntimeHelpers": + { + if ((methodName == "IsReferenceOrContainsReferences" || methodName == "IsReference") + && owningType.Namespace == "System.Runtime.CompilerServices") + { + TypeDesc elementType = method.Instantiation[0]; + + // Fallback to non-intrinsic implementation for universal generics + if (elementType.IsCanonicalSubtype(CanonicalFormKind.Universal)) + return null; + + bool result = elementType.IsGCPointer; + if (methodName == "IsReferenceOrContainsReferences") + { + result |= (elementType.IsDefType ? ((DefType)elementType).ContainsGCPointers : false); + } + + return new ILStubMethodIL(method, new byte[] { + result ? (byte)ILOpcode.ldc_i4_1 : (byte)ILOpcode.ldc_i4_0, + (byte)ILOpcode.ret }, + Array.Empty(), null); + } + } + break; + case "Comparer`1": + { + if (methodName == "Create" && owningType.Namespace == "System.Collections.Generic") + return ComparerIntrinsics.EmitComparerCreate(method); + } + break; + case "EqualityComparer`1": + { + if (methodName == "Create" && owningType.Namespace == "System.Collections.Generic") + return ComparerIntrinsics.EmitEqualityComparerCreate(method); + } + break; + case "EqualityComparerHelpers": + { + if (owningType.Namespace != "Internal.IntrinsicSupport") + return null; + + if (methodName == "EnumOnlyEquals") + { + // EnumOnlyEquals would basically like to do this: + // static bool EnumOnlyEquals(T x, T y) where T: struct => x == y; + // This is not legal though. + // We don't want to do this: + // static bool EnumOnlyEquals(T x, T y) where T: struct => x.Equals(y); + // Because it would box y. + // So we resort to some per-instantiation magic. + + TypeDesc elementType = method.Instantiation[0]; + if (!elementType.IsEnum) + return null; + + ILOpcode convInstruction; + if (((DefType)elementType).InstanceFieldSize.AsInt <= 4) + { + convInstruction = ILOpcode.conv_i4; + } + else + { + Debug.Assert(((DefType)elementType).InstanceFieldSize.AsInt == 8); + convInstruction = ILOpcode.conv_i8; + } + + return new ILStubMethodIL(method, new byte[] { + (byte)ILOpcode.ldarg_0, + (byte)convInstruction, + (byte)ILOpcode.ldarg_1, + (byte)convInstruction, + (byte)ILOpcode.prefix1, unchecked((byte)ILOpcode.ceq), + (byte)ILOpcode.ret, + }, + Array.Empty(), null); + } + else if (methodName == "GetComparerForReferenceTypesOnly") + { + TypeDesc elementType = method.Instantiation[0]; + if (!elementType.IsRuntimeDeterminedSubtype + && !elementType.IsCanonicalSubtype(CanonicalFormKind.Any) + && !elementType.IsGCPointer) + { + return new ILStubMethodIL(method, new byte[] { + (byte)ILOpcode.ldnull, + (byte)ILOpcode.ret + }, + Array.Empty(), null); + } + } + else if (methodName == "StructOnlyEquals") + { + TypeDesc elementType = method.Instantiation[0]; + if (!elementType.IsRuntimeDeterminedSubtype + && !elementType.IsCanonicalSubtype(CanonicalFormKind.Any) + && !elementType.IsGCPointer) + { + Debug.Assert(elementType.IsValueType); + + TypeSystemContext context = elementType.Context; + MetadataType helperType = context.SystemModule.GetKnownType("Internal.IntrinsicSupport", "EqualityComparerHelpers"); + + MethodDesc methodToCall; + if (elementType.IsEnum) + { + methodToCall = helperType.GetKnownMethod("EnumOnlyEquals", null).MakeInstantiatedMethod(elementType); + } + else if (elementType.IsNullable && ComparerIntrinsics.ImplementsIEquatable(elementType.Instantiation[0])) + { + methodToCall = helperType.GetKnownMethod("StructOnlyEqualsNullable", null).MakeInstantiatedMethod(elementType.Instantiation[0]); + } + else if (ComparerIntrinsics.ImplementsIEquatable(elementType)) + { + methodToCall = helperType.GetKnownMethod("StructOnlyEqualsIEquatable", null).MakeInstantiatedMethod(elementType); + } + else + { + methodToCall = helperType.GetKnownMethod("StructOnlyNormalEquals", null).MakeInstantiatedMethod(elementType); + } + + return new ILStubMethodIL(method, new byte[] + { + (byte)ILOpcode.ldarg_0, + (byte)ILOpcode.ldarg_1, + (byte)ILOpcode.call, 1, 0, 0, 0, + (byte)ILOpcode.ret + }, + Array.Empty(), new object[] { methodToCall }); + } + } + } + break; + } + + return null; + } + + public override MethodIL GetMethodIL(MethodDesc method) + { + if (method is EcmaMethod) + { + // TODO: Workaround: we should special case methods with Intrinsic attribute, but since + // CoreLib source is still not in the repo, we have to work with what we have, which is + // an MCG attribute on the type itself... + if (((MetadataType)method.OwningType).HasCustomAttribute("System.Runtime.InteropServices", "McgIntrinsicsAttribute")) + { + var name = method.Name; + if (name == "Call" || name.StartsWith("StdCall")) + { + return CalliIntrinsic.EmitIL(method); + } + else + if (name == "AddrOf") + { + return AddrOfIntrinsic.EmitIL(method); + } + } + + if (method.IsIntrinsic) + { + MethodIL result = TryGetIntrinsicMethodIL(method); + if (result != null) + return result; + } + + if (method.IsRuntimeImplemented) + { + MethodIL result = TryGetRuntimeImplementedMethodIL(method); + if (result != null) + return result; + } + + MethodIL methodIL = EcmaMethodIL.Create((EcmaMethod)method); + if (methodIL != null) + return methodIL; + + return null; + } + else + if (method is MethodForInstantiatedType || method is InstantiatedMethod) + { + // Intrinsics specialized per instantiation + if (method.IsIntrinsic) + { + MethodIL methodIL = TryGetPerInstantiationIntrinsicMethodIL(method); + if (methodIL != null) + return methodIL; + } + + var methodDefinitionIL = GetMethodIL(method.GetTypicalMethodDefinition()); + if (methodDefinitionIL == null) + return null; + return new InstantiatedMethodIL(method, methodDefinitionIL); + } + else + if (method is ILStubMethod) + { + return ((ILStubMethod)method).EmitIL(); + } + else + if (method is ArrayMethod) + { + return ArrayMethodILEmitter.EmitIL((ArrayMethod)method); + } + else + { + Debug.Assert(!(method is PInvokeTargetNativeMethod), "Who is asking for IL of PInvokeTargetNativeMethod?"); + return null; + } + } + } +} diff --git a/src/Common/src/TypeSystem/IL/ILProvider.cs b/src/Common/src/TypeSystem/IL/ILProvider.cs index eb1747beb69..f17bb497982 100644 --- a/src/Common/src/TypeSystem/IL/ILProvider.cs +++ b/src/Common/src/TypeSystem/IL/ILProvider.cs @@ -2,362 +2,16 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; - using Internal.TypeSystem; -using Internal.TypeSystem.Ecma; - -using Internal.IL.Stubs; - -using Debug = System.Diagnostics.Debug; namespace Internal.IL { - internal sealed class ILProvider : LockFreeReaderHashtable + /// + /// Provides IL for method bodies either by reading + /// the IL bytes from the source ECMA-335 assemblies, or through other means. + /// + public abstract class ILProvider { - private PInvokeILProvider _pinvokeILProvider; - - public ILProvider(PInvokeILProvider pinvokeILProvider) - { - _pinvokeILProvider = pinvokeILProvider; - } - - private MethodIL TryGetRuntimeImplementedMethodIL(MethodDesc method) - { - // Provides method bodies for runtime implemented methods. It can return null for - // methods that are treated specially by the codegen. - - Debug.Assert(method.IsRuntimeImplemented); - - TypeDesc owningType = method.OwningType; - - if (owningType.IsDelegate) - { - return DelegateMethodILEmitter.EmitIL(method); - } - - return null; - } - - /// - /// Provides method bodies for intrinsics recognized by the compiler. - /// It can return null if it's not an intrinsic recognized by the compiler, - /// but an intrinsic e.g. recognized by codegen. - /// - private MethodIL TryGetIntrinsicMethodIL(MethodDesc method) - { - Debug.Assert(method.IsIntrinsic); - - MetadataType owningType = method.OwningType as MetadataType; - if (owningType == null) - return null; - - switch (owningType.Name) - { - case "Unsafe": - { - if (owningType.Namespace == "Internal.Runtime.CompilerServices") - return UnsafeIntrinsics.EmitIL(method); - } - break; - case "Debug": - { - if (owningType.Namespace == "System.Diagnostics" && method.Name == "DebugBreak") - return new ILStubMethodIL(method, new byte[] { (byte)ILOpcode.break_, (byte)ILOpcode.ret }, Array.Empty(), null); - } - break; - case "EETypePtr": - { - if (owningType.Namespace == "System" && method.Name == "EETypePtrOf") - return EETypePtrOfIntrinsic.EmitIL(method); - } - break; - case "RuntimeAugments": - { - if (owningType.Namespace == "Internal.Runtime.Augments" && method.Name == "GetCanonType") - return GetCanonTypeIntrinsic.EmitIL(method); - } - break; - case "EEType": - { - if (owningType.Namespace == "Internal.Runtime" && method.Name == "get_SupportsRelativePointers") - { - ILOpcode value = method.Context.Target.SupportsRelativePointers ? - ILOpcode.ldc_i4_1 : ILOpcode.ldc_i4_0; - return new ILStubMethodIL(method, new byte[] { (byte)value, (byte)ILOpcode.ret }, Array.Empty(), null); - } - } - break; - } - - return null; - } - - /// - /// Provides method bodies for intrinsics recognized by the compiler that - /// are specialized per instantiation. It can return null if the intrinsic - /// is not recognized. - /// - private MethodIL TryGetPerInstantiationIntrinsicMethodIL(MethodDesc method) - { - Debug.Assert(method.IsIntrinsic); - - MetadataType owningType = method.OwningType.GetTypeDefinition() as MetadataType; - if (owningType == null) - return null; - - string methodName = method.Name; - - switch (owningType.Name) - { - case "RuntimeHelpers": - { - if ((methodName == "IsReferenceOrContainsReferences" || methodName == "IsReference") - && owningType.Namespace == "System.Runtime.CompilerServices") - { - TypeDesc elementType = method.Instantiation[0]; - - // Fallback to non-intrinsic implementation for universal generics - if (elementType.IsCanonicalSubtype(CanonicalFormKind.Universal)) - return null; - - bool result = elementType.IsGCPointer; - if (methodName == "IsReferenceOrContainsReferences") - { - result |= (elementType.IsDefType ? ((DefType)elementType).ContainsGCPointers : false); - } - - return new ILStubMethodIL(method, new byte[] { - result ? (byte)ILOpcode.ldc_i4_1 : (byte)ILOpcode.ldc_i4_0, - (byte)ILOpcode.ret }, - Array.Empty(), null); - } - } - break; - case "Comparer`1": - { - if (methodName == "Create" && owningType.Namespace == "System.Collections.Generic") - return ComparerIntrinsics.EmitComparerCreate(method); - } - break; - case "EqualityComparer`1": - { - if (methodName == "Create" && owningType.Namespace == "System.Collections.Generic") - return ComparerIntrinsics.EmitEqualityComparerCreate(method); - } - break; - case "EqualityComparerHelpers": - { - if (owningType.Namespace != "Internal.IntrinsicSupport") - return null; - - if (methodName == "EnumOnlyEquals") - { - // EnumOnlyEquals would basically like to do this: - // static bool EnumOnlyEquals(T x, T y) where T: struct => x == y; - // This is not legal though. - // We don't want to do this: - // static bool EnumOnlyEquals(T x, T y) where T: struct => x.Equals(y); - // Because it would box y. - // So we resort to some per-instantiation magic. - - TypeDesc elementType = method.Instantiation[0]; - if (!elementType.IsEnum) - return null; - - ILOpcode convInstruction; - if (((DefType)elementType).InstanceFieldSize.AsInt <= 4) - { - convInstruction = ILOpcode.conv_i4; - } - else - { - Debug.Assert(((DefType)elementType).InstanceFieldSize.AsInt == 8); - convInstruction = ILOpcode.conv_i8; - } - - return new ILStubMethodIL(method, new byte[] { - (byte)ILOpcode.ldarg_0, - (byte)convInstruction, - (byte)ILOpcode.ldarg_1, - (byte)convInstruction, - (byte)ILOpcode.prefix1, unchecked((byte)ILOpcode.ceq), - (byte)ILOpcode.ret, - }, - Array.Empty(), null); - } - else if (methodName == "GetComparerForReferenceTypesOnly") - { - TypeDesc elementType = method.Instantiation[0]; - if (!elementType.IsRuntimeDeterminedSubtype - && !elementType.IsCanonicalSubtype(CanonicalFormKind.Any) - && !elementType.IsGCPointer) - { - return new ILStubMethodIL(method, new byte[] { - (byte)ILOpcode.ldnull, - (byte)ILOpcode.ret - }, - Array.Empty(), null); - } - } - else if (methodName == "StructOnlyEquals") - { - TypeDesc elementType = method.Instantiation[0]; - if (!elementType.IsRuntimeDeterminedSubtype - && !elementType.IsCanonicalSubtype(CanonicalFormKind.Any) - && !elementType.IsGCPointer) - { - Debug.Assert(elementType.IsValueType); - - TypeSystemContext context = elementType.Context; - MetadataType helperType = context.SystemModule.GetKnownType("Internal.IntrinsicSupport", "EqualityComparerHelpers"); - - MethodDesc methodToCall; - if (elementType.IsEnum) - { - methodToCall = helperType.GetKnownMethod("EnumOnlyEquals", null).MakeInstantiatedMethod(elementType); - } - else if (elementType.IsNullable && ComparerIntrinsics.ImplementsIEquatable(elementType.Instantiation[0])) - { - methodToCall = helperType.GetKnownMethod("StructOnlyEqualsNullable", null).MakeInstantiatedMethod(elementType.Instantiation[0]); - } - else if (ComparerIntrinsics.ImplementsIEquatable(elementType)) - { - methodToCall = helperType.GetKnownMethod("StructOnlyEqualsIEquatable", null).MakeInstantiatedMethod(elementType); - } - else - { - methodToCall = helperType.GetKnownMethod("StructOnlyNormalEquals", null).MakeInstantiatedMethod(elementType); - } - - return new ILStubMethodIL(method, new byte[] - { - (byte)ILOpcode.ldarg_0, - (byte)ILOpcode.ldarg_1, - (byte)ILOpcode.call, 1, 0, 0, 0, - (byte)ILOpcode.ret - }, - Array.Empty(), new object[] { methodToCall }); - } - } - } - break; - } - - return null; - } - - private MethodIL CreateMethodIL(MethodDesc method) - { - if (method is EcmaMethod) - { - // TODO: Workaround: we should special case methods with Intrinsic attribute, but since - // CoreLib source is still not in the repo, we have to work with what we have, which is - // an MCG attribute on the type itself... - if (((MetadataType)method.OwningType).HasCustomAttribute("System.Runtime.InteropServices", "McgIntrinsicsAttribute")) - { - var name = method.Name; - if (name == "Call" || name.StartsWith("StdCall")) - { - return CalliIntrinsic.EmitIL(method); - } - else - if (name == "AddrOf") - { - return AddrOfIntrinsic.EmitIL(method); - } - } - - if (method.IsIntrinsic) - { - MethodIL result = TryGetIntrinsicMethodIL(method); - if (result != null) - return result; - } - - if (method.IsPInvoke) - { - var pregenerated = McgInteropSupport.TryGetPregeneratedPInvoke(method); - if (pregenerated == null) - return _pinvokeILProvider.EmitIL(method); - method = pregenerated; - } - - if (method.IsRuntimeImplemented) - { - MethodIL result = TryGetRuntimeImplementedMethodIL(method); - if (result != null) - return result; - } - - MethodIL methodIL = EcmaMethodIL.Create((EcmaMethod)method); - if (methodIL != null) - return methodIL; - - return null; - } - else - if (method is MethodForInstantiatedType || method is InstantiatedMethod) - { - // Intrinsics specialized per instantiation - if (method.IsIntrinsic) - { - MethodIL methodIL = TryGetPerInstantiationIntrinsicMethodIL(method); - if (methodIL != null) - return methodIL; - } - - var methodDefinitionIL = GetMethodIL(method.GetTypicalMethodDefinition()); - if (methodDefinitionIL == null) - return null; - return new InstantiatedMethodIL(method, methodDefinitionIL); - } - else - if (method is ILStubMethod) - { - return ((ILStubMethod)method).EmitIL(); - } - else - if (method is ArrayMethod) - { - return ArrayMethodILEmitter.EmitIL((ArrayMethod)method); - } - else - { - Debug.Assert(!(method is PInvokeTargetNativeMethod), "Who is asking for IL of PInvokeTargetNativeMethod?"); - return null; - } - } - - internal class MethodILData - { - public MethodDesc Method; - public MethodIL MethodIL; - } - protected override int GetKeyHashCode(MethodDesc key) - { - return key.GetHashCode(); - } - protected override int GetValueHashCode(MethodILData value) - { - return value.Method.GetHashCode(); - } - protected override bool CompareKeyToValue(MethodDesc key, MethodILData value) - { - return Object.ReferenceEquals(key, value.Method); - } - protected override bool CompareValueToValue(MethodILData value1, MethodILData value2) - { - return Object.ReferenceEquals(value1.Method, value2.Method); - } - protected override MethodILData CreateValueFromKey(MethodDesc key) - { - return new MethodILData() { Method = key, MethodIL = CreateMethodIL(key) }; - } - - public MethodIL GetMethodIL(MethodDesc method) - { - return GetOrCreateValue(method).MethodIL; - } + public abstract MethodIL GetMethodIL(MethodDesc method); } } diff --git a/src/Common/src/TypeSystem/IL/McgInteropSupport.cs b/src/Common/src/TypeSystem/IL/McgInteropSupport.cs deleted file mode 100644 index c956c33e9e8..00000000000 --- a/src/Common/src/TypeSystem/IL/McgInteropSupport.cs +++ /dev/null @@ -1,111 +0,0 @@ -// 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 System; -using System.Reflection; - -using Internal.TypeSystem; -using Debug = System.Diagnostics.Debug; - -namespace Internal.IL -{ - /// - /// Provides compilation hooks for interop code generated by tool - /// - public static class McgInteropSupport - { - /// - /// Assembly name suffix for pregenerated interop code. - /// - private const string AssemblyNameSuffix = ".McgInterop"; - private const string PInvokeContainerTypeNS = "McgInterop"; - private const string PInvokeMethodContainerType = "McgPInvokeMarshaller"; - - /// - /// Returns true if is pregenerated interop code - /// - public static bool IsPregeneratedInterop(MethodDesc method) - { - var metadataType = (MetadataType)method.OwningType; - var module = metadataType.Module; - - var assemblyName = ((IAssemblyDesc)module).GetName(); - var simpleName = assemblyName.Name; - - return simpleName.EndsWith(AssemblyNameSuffix); - } - - /// - /// Returns pregenerated interop code for given PInvoke method if one exist - /// - public static MethodDesc TryGetPregeneratedPInvoke(MethodDesc method) - { - Debug.Assert(method.IsPInvoke); - - var metadataType = (MetadataType)method.OwningType; - var module = metadataType.Module; - - var assemblyName = ((IAssemblyDesc)module).GetName(); - - var interopAssemblyName = new AssemblyName(); - - interopAssemblyName.Name = assemblyName.Name + AssemblyNameSuffix; - interopAssemblyName.Version = assemblyName.Version; - interopAssemblyName.SetPublicKeyToken(interopAssemblyName.GetPublicKeyToken()); - interopAssemblyName.CultureName = assemblyName.CultureName; - interopAssemblyName.ContentType = assemblyName.ContentType; - - var interopModule = module.Context.ResolveAssembly(interopAssemblyName, false); - if (interopModule == null) - return null; - - var pregeneratedMethod = GetMatchingMethod(interopModule, method); - if (pregeneratedMethod == null) - { - // TODO: Better error message - throw new MissingMemberException("Missing method in " + interopAssemblyName.Name + ":" + method.ToString()); - } - return pregeneratedMethod; - } - - // Returns null if no matching method is found - private static MethodDesc GetMatchingMethod(ModuleDesc module, MethodDesc method) - { - // TODO:Enable this once mcg generated code match GetMatchingType - // type lookup. - // var matchingType = GetMatchingType(module, method.OwningType); - var matchingType = TryGetMcgGeneratedType(module); - if (matchingType == null) - return null; - return matchingType.GetMethod(method.Name, method.Signature); - } - - // Returns null if no matching type is found - private static TypeDesc GetMatchingType(ModuleDesc module, TypeDesc type) - { - var metadataType = (MetadataType)type; - var containingType = metadataType.ContainingType; - if (containingType != null) - { - var matchingContainingType = (MetadataType)GetMatchingType(module, containingType); - if (matchingContainingType == null) - return null; - return matchingContainingType.GetNestedType(metadataType.Name); - } - else - { - return module.GetType(metadataType.Namespace, metadataType.Name, false); - } - } - - // TODO: This's to work-around the limitation of mcg code generation.Mcg currently - // do not preserve user pinvoke defining type hierarchy,but dump all pinvoke stub methods - // to a type named McgPInvokeMarshaller - private static TypeDesc TryGetMcgGeneratedType(ModuleDesc module) - { - // this should not fail since we are looking for a well known type. - return module.GetType(PInvokeContainerTypeNS, PInvokeMethodContainerType, true); - } - } -} diff --git a/src/ILCompiler.Compiler/src/Compiler/Compilation.cs b/src/ILCompiler.Compiler/src/Compiler/Compilation.cs index b697d302084..47056267062 100644 --- a/src/ILCompiler.Compiler/src/Compiler/Compilation.cs +++ b/src/ILCompiler.Compiler/src/Compiler/Compilation.cs @@ -41,6 +41,7 @@ protected Compilation( DependencyAnalyzerBase dependencyGraph, NodeFactory nodeFactory, IEnumerable compilationRoots, + ILProvider ilProvider, DebugInformationProvider debugInformationProvider, DevirtualizationManager devirtualizationManager, Logger logger) @@ -63,25 +64,30 @@ protected Compilation( _assemblyGetExecutingAssemblyMethodThunks = new AssemblyGetExecutingAssemblyMethodThunkCache(globalModuleGeneratedType); _methodBaseGetCurrentMethodThunks = new MethodBaseGetCurrentMethodThunkCache(); - bool? forceLazyPInvokeResolution = null; - // TODO: Workaround lazy PInvoke resolution not working with CppCodeGen yet - // https://github.com/dotnet/corert/issues/2454 - // https://github.com/dotnet/corert/issues/2149 - if (nodeFactory.IsCppCodegenTemporaryWorkaround) forceLazyPInvokeResolution = false; - PInvokeILProvider = new PInvokeILProvider(new PInvokeILEmitterConfiguration(forceLazyPInvokeResolution), nodeFactory.InteropStubManager.InteropStateManager); + if (!(nodeFactory.InteropStubManager is EmptyInteropStubManager)) + { + bool? forceLazyPInvokeResolution = null; + // TODO: Workaround lazy PInvoke resolution not working with CppCodeGen yet + // https://github.com/dotnet/corert/issues/2454 + // https://github.com/dotnet/corert/issues/2149 + if (nodeFactory.IsCppCodegenTemporaryWorkaround) forceLazyPInvokeResolution = false; + PInvokeILProvider = new PInvokeILProvider(new PInvokeILEmitterConfiguration(forceLazyPInvokeResolution), nodeFactory.InteropStubManager.InteropStateManager); + + ilProvider = new CombinedILProvider(ilProvider, PInvokeILProvider); + } - _methodILCache = new ILProvider(PInvokeILProvider); + _methodILCache = new ILCache(ilProvider); } - private ILProvider _methodILCache; - + private ILCache _methodILCache; + public MethodIL GetMethodIL(MethodDesc method) { // Flush the cache when it grows too big if (_methodILCache.Count > 1000) - _methodILCache = new ILProvider(PInvokeILProvider); + _methodILCache = new ILCache(_methodILCache.ILProvider); - return _methodILCache.GetMethodIL(method); + return _methodILCache.GetOrCreateValue(method).MethodIL; } protected abstract void ComputeDependencyNodeDependencies(List> obj); @@ -431,6 +437,64 @@ public void RootReadOnlyDataBlob(byte[] data, int alignment, string reason, stri _graph.AddRoot(_factory.ReadOnlyDataBlob(exportName, data, alignment), reason); } } + + private sealed class ILCache : LockFreeReaderHashtable + { + public ILProvider ILProvider { get; } + + public ILCache(ILProvider provider) + { + ILProvider = provider; + } + + protected override int GetKeyHashCode(MethodDesc key) + { + return key.GetHashCode(); + } + protected override int GetValueHashCode(MethodILData value) + { + return value.Method.GetHashCode(); + } + protected override bool CompareKeyToValue(MethodDesc key, MethodILData value) + { + return Object.ReferenceEquals(key, value.Method); + } + protected override bool CompareValueToValue(MethodILData value1, MethodILData value2) + { + return Object.ReferenceEquals(value1.Method, value2.Method); + } + protected override MethodILData CreateValueFromKey(MethodDesc key) + { + return new MethodILData() { Method = key, MethodIL = ILProvider.GetMethodIL(key) }; + } + + internal class MethodILData + { + public MethodDesc Method; + public MethodIL MethodIL; + } + } + + private sealed class CombinedILProvider : ILProvider + { + private readonly ILProvider _primaryILProvider; + private readonly PInvokeILProvider _pinvokeProvider; + + public CombinedILProvider(ILProvider primaryILProvider, PInvokeILProvider pinvokeILProvider) + { + _primaryILProvider = primaryILProvider; + _pinvokeProvider = pinvokeILProvider; + } + + public override MethodIL GetMethodIL(MethodDesc method) + { + MethodIL result = _primaryILProvider.GetMethodIL(method); + if (result == null && method.IsPInvoke) + result = _pinvokeProvider.GetMethodIL(method); + + return result; + } + } } // Interface under which Compilation is exposed externally. diff --git a/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.cs b/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.cs index fc20b34321a..08ab4ffe264 100644 --- a/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.cs +++ b/src/ILCompiler.Compiler/src/Compiler/CompilationBuilder.cs @@ -8,6 +8,8 @@ using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysisFramework; +using Internal.IL; + namespace ILCompiler { public abstract class CompilationBuilder @@ -92,6 +94,10 @@ public CompilationBuilder UseDebugInfoProvider(DebugInformationProvider provider public abstract CompilationBuilder UseBackendOptions(IEnumerable options); + public abstract CompilationBuilder UseILProvider(ILProvider ilProvider); + + protected abstract ILProvider GetILProvider(); + protected DependencyAnalyzerBase CreateDependencyGraph(NodeFactory factory, IComparer> comparer = null) { return _dependencyTrackingLevel.CreateDependencyGraph(factory, comparer); @@ -99,7 +105,7 @@ protected DependencyAnalyzerBase CreateDependencyGraph(NodeFactory public ILScannerBuilder GetILScannerBuilder(CompilationModuleGroup compilationGroup = null) { - return new ILScannerBuilder(_context, compilationGroup ?? _compilationGroup, _nameMangler); + return new ILScannerBuilder(_context, compilationGroup ?? _compilationGroup, _nameMangler, GetILProvider()); } public abstract ICompilation ToCompilation(); diff --git a/src/ILCompiler.Compiler/src/Compiler/ILScanner.cs b/src/ILCompiler.Compiler/src/Compiler/ILScanner.cs index fd5b47c30f6..ccee9617092 100644 --- a/src/ILCompiler.Compiler/src/Compiler/ILScanner.cs +++ b/src/ILCompiler.Compiler/src/Compiler/ILScanner.cs @@ -28,9 +28,10 @@ internal ILScanner( DependencyAnalyzerBase dependencyGraph, ILScanNodeFactory nodeFactory, IEnumerable roots, + ILProvider ilProvider, DebugInformationProvider debugInformationProvider, Logger logger) - : base(dependencyGraph, nodeFactory, roots, debugInformationProvider, null, logger) + : base(dependencyGraph, nodeFactory, roots, ilProvider, debugInformationProvider, null, logger) { } diff --git a/src/ILCompiler.Compiler/src/Compiler/ILScannerBuilder.cs b/src/ILCompiler.Compiler/src/Compiler/ILScannerBuilder.cs index 2202f93d6db..9ef1abe3f0c 100644 --- a/src/ILCompiler.Compiler/src/Compiler/ILScannerBuilder.cs +++ b/src/ILCompiler.Compiler/src/Compiler/ILScannerBuilder.cs @@ -9,6 +9,7 @@ using ILCompiler.DependencyAnalysisFramework; using Internal.TypeSystem; +using Internal.IL; namespace ILCompiler { @@ -17,6 +18,7 @@ public sealed class ILScannerBuilder private readonly CompilerTypeSystemContext _context; private readonly CompilationModuleGroup _compilationGroup; private readonly NameMangler _nameMangler; + private readonly ILProvider _ilProvider; // These need to provide reasonable defaults so that the user can optionally skip // calling the Use/Configure methods and still get something reasonable back. @@ -25,12 +27,13 @@ public sealed class ILScannerBuilder private IEnumerable _compilationRoots = Array.Empty(); private MetadataManager _metadataManager; - internal ILScannerBuilder(CompilerTypeSystemContext context, CompilationModuleGroup compilationGroup, NameMangler mangler) + internal ILScannerBuilder(CompilerTypeSystemContext context, CompilationModuleGroup compilationGroup, NameMangler mangler, ILProvider ilProvider) { _context = context; _compilationGroup = compilationGroup; _nameMangler = mangler; _metadataManager = new EmptyMetadataManager(context); + _ilProvider = ilProvider; } public ILScannerBuilder UseDependencyTracking(DependencyTrackingLevel trackingLevel) @@ -57,7 +60,7 @@ public IILScanner ToILScanner() var nodeFactory = new ILScanNodeFactory(_context, _compilationGroup, _metadataManager, interopStubManager, _nameMangler); DependencyAnalyzerBase graph = _dependencyTrackingLevel.CreateDependencyGraph(nodeFactory); - return new ILScanner(graph, nodeFactory, _compilationRoots, new NullDebugInformationProvider(), _logger); + return new ILScanner(graph, nodeFactory, _compilationRoots, _ilProvider, new NullDebugInformationProvider(), _logger); } } } diff --git a/src/ILCompiler.Compiler/src/Compiler/MethodExtensions.cs b/src/ILCompiler.Compiler/src/Compiler/MethodExtensions.cs index fd274af789c..d593299a51d 100644 --- a/src/ILCompiler.Compiler/src/Compiler/MethodExtensions.cs +++ b/src/ILCompiler.Compiler/src/Compiler/MethodExtensions.cs @@ -95,7 +95,7 @@ public static string GetNativeCallableExportName(this EcmaMethod This) /// public static bool IsRawPInvoke(this MethodDesc method) { - return method.IsPInvoke && ((method is Internal.IL.Stubs.PInvokeTargetNativeMethod) || Internal.IL.McgInteropSupport.IsPregeneratedInterop(method)); + return method.IsPInvoke && (method is Internal.IL.Stubs.PInvokeTargetNativeMethod); } /// diff --git a/src/ILCompiler.Compiler/src/Compiler/PrecomputedMetadataManager.cs b/src/ILCompiler.Compiler/src/Compiler/PrecomputedMetadataManager.cs index a005fa3799c..3f24e5c7480 100644 --- a/src/ILCompiler.Compiler/src/Compiler/PrecomputedMetadataManager.cs +++ b/src/ILCompiler.Compiler/src/Compiler/PrecomputedMetadataManager.cs @@ -388,13 +388,11 @@ private MetadataLoadedInfo LoadMetadata() MethodDesc requiredGenericFieldsMethod = typeWithMetadataMappings.GetMethod("RequiredGenericFields", null); MethodDesc requiredTemplatesMethod = typeWithMetadataMappings.GetMethod("CompilerDeterminedInstantiations", null); - ILProvider ilProvider = new ILProvider(null); - MetadataLoadedInfo result = new MetadataLoadedInfo(); if (fullMetadataMethod != null) { - MethodIL fullMethodIL = ilProvider.GetMethodIL(fullMetadataMethod); + MethodIL fullMethodIL = EcmaMethodIL.Create((EcmaMethod)fullMetadataMethod); ReadMetadataMethod(fullMethodIL, result.AllTypeMappings, result.MethodMappings, result.FieldMappings, metadataModules); foreach (var mapping in result.AllTypeMappings) { @@ -404,7 +402,7 @@ private MetadataLoadedInfo LoadMetadata() if (weakMetadataMethod != null) { - MethodIL weakMethodIL = ilProvider.GetMethodIL(weakMetadataMethod); + MethodIL weakMethodIL = EcmaMethodIL.Create((EcmaMethod)weakMetadataMethod); Dictionary weakMethodMappings = new Dictionary(); Dictionary weakFieldMappings = new Dictionary(); ReadMetadataMethod(weakMethodIL, result.WeakReflectedTypeMappings, weakMethodMappings, weakFieldMappings, metadataModules); @@ -417,7 +415,7 @@ private MetadataLoadedInfo LoadMetadata() if (requiredGenericTypesMethod != null) { - foreach (var type in ReadRequiredGenericsEntities(ilProvider.GetMethodIL(requiredGenericTypesMethod))) + foreach (var type in ReadRequiredGenericsEntities(EcmaMethodIL.Create((EcmaMethod)requiredGenericTypesMethod))) { Debug.Assert(type is DefType); result.RequiredGenericTypes.Add((TypeDesc)type); @@ -426,19 +424,19 @@ private MetadataLoadedInfo LoadMetadata() if (requiredGenericMethodsMethod != null) { - foreach (var method in ReadRequiredGenericsEntities(ilProvider.GetMethodIL(requiredGenericMethodsMethod))) + foreach (var method in ReadRequiredGenericsEntities(EcmaMethodIL.Create((EcmaMethod)requiredGenericMethodsMethod))) result.RequiredGenericMethods.Add((MethodDesc)method); } if (requiredGenericFieldsMethod != null) { - foreach (var field in ReadRequiredGenericsEntities(ilProvider.GetMethodIL(requiredGenericFieldsMethod))) + foreach (var field in ReadRequiredGenericsEntities(EcmaMethodIL.Create((EcmaMethod)requiredGenericFieldsMethod))) result.RequiredGenericFields.Add((FieldDesc)field); } if (requiredTemplatesMethod != null) { - ReadRequiredTemplates(ilProvider.GetMethodIL(requiredTemplatesMethod), + ReadRequiredTemplates(EcmaMethodIL.Create((EcmaMethod)requiredTemplatesMethod), result.RequiredTemplateTypes, result.RequiredTemplateMethods, result.RequiredTemplateFields); @@ -877,8 +875,7 @@ private Dictionary LoadDynamicInvokeStubs() if (dynamicInvokeStubDescriptorMethod == null) return dynamicInvokeMapTable; - ILProvider ilProvider = new ILProvider(null); - ILStreamReader il = new ILStreamReader(ilProvider.GetMethodIL(dynamicInvokeStubDescriptorMethod)); + ILStreamReader il = new ILStreamReader(EcmaMethodIL.Create((EcmaMethod)dynamicInvokeStubDescriptorMethod)); // structure is // REPEAT N TIMES //ldtoken method diff --git a/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilation.cs b/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilation.cs index 97b6ce7232e..2c8baaf016b 100644 --- a/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilation.cs +++ b/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilation.cs @@ -23,11 +23,12 @@ internal RyuJitCompilation( DependencyAnalyzerBase dependencyGraph, NodeFactory nodeFactory, IEnumerable roots, + ILProvider ilProvider, DebugInformationProvider debugInformationProvider, Logger logger, DevirtualizationManager devirtualizationManager, JitConfigProvider configProvider) - : base(dependencyGraph, nodeFactory, roots, debugInformationProvider, devirtualizationManager, logger) + : base(dependencyGraph, nodeFactory, roots, ilProvider, debugInformationProvider, devirtualizationManager, logger) { _jitConfigProvider = configProvider; } diff --git a/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilationBuilder.cs b/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilationBuilder.cs index f02559b4ebe..51b5c703b74 100644 --- a/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilationBuilder.cs +++ b/src/ILCompiler.Compiler/src/Compiler/RyuJitCompilationBuilder.cs @@ -8,6 +8,7 @@ using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysisFramework; +using Internal.IL; using Internal.JitInterface; using Internal.TypeSystem; @@ -20,6 +21,7 @@ public sealed class RyuJitCompilationBuilder : CompilationBuilder // These need to provide reasonable defaults so that the user can optionally skip // calling the Use/Configure methods and still get something reasonable back. private KeyValuePair[] _ryujitOptions = Array.Empty>(); + private ILProvider _ilProvider = new CoreRTILProvider(); public RyuJitCompilationBuilder(CompilerTypeSystemContext context, CompilationModuleGroup group) : base(context, group, @@ -52,6 +54,17 @@ public override CompilationBuilder UseBackendOptions(IEnumerable options return this; } + public override CompilationBuilder UseILProvider(ILProvider ilProvider) + { + _ilProvider = ilProvider; + return this; + } + + protected override ILProvider GetILProvider() + { + return _ilProvider; + } + public override ICompilation ToCompilation() { ArrayBuilder jitFlagBuilder = new ArrayBuilder(); @@ -91,7 +104,7 @@ public override ICompilation ToCompilation() var jitConfig = new JitConfigProvider(jitFlagBuilder.ToArray(), _ryujitOptions); DependencyAnalyzerBase graph = CreateDependencyGraph(factory, new ObjectNode.ObjectNodeComparer(new CompilerComparer())); - return new RyuJitCompilation(graph, factory, _compilationRoots, _debugInformationProvider, _logger, _devirtualizationManager, jitConfig); + return new RyuJitCompilation(graph, factory, _compilationRoots, _ilProvider, _debugInformationProvider, _logger, _devirtualizationManager, jitConfig); } } } diff --git a/src/ILCompiler.Compiler/src/IL/Stubs/PInvokeILProvider.cs b/src/ILCompiler.Compiler/src/IL/Stubs/PInvokeILProvider.cs index 7ae483dbcc4..e7c338d82f8 100644 --- a/src/ILCompiler.Compiler/src/IL/Stubs/PInvokeILProvider.cs +++ b/src/ILCompiler.Compiler/src/IL/Stubs/PInvokeILProvider.cs @@ -14,7 +14,7 @@ namespace Internal.IL /// Wraps the API and configuration for a particular PInvoke IL emitter. Eventually this will /// allow ILProvider to switch out its PInvoke IL generator with another, such as MCG. /// - class PInvokeILProvider + class PInvokeILProvider : ILProvider { private readonly PInvokeILEmitterConfiguration _pInvokeILEmitterConfiguration; private readonly InteropStateManager _interopStateManager; @@ -25,7 +25,7 @@ public PInvokeILProvider(PInvokeILEmitterConfiguration pInvokeILEmitterConfigura _interopStateManager = interopStateManager; } - public MethodIL EmitIL(MethodDesc method) + public override MethodIL GetMethodIL(MethodDesc method) { return PInvokeILEmitter.EmitIL(method, _pInvokeILEmitterConfiguration, _interopStateManager); } diff --git a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj index be2d84a4315..f490272f221 100644 --- a/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj +++ b/src/ILCompiler.Compiler/src/ILCompiler.Compiler.csproj @@ -46,6 +46,9 @@ Ecma\EcmaSignatureEncoder.cs + + IL\CoreRTILProvider.cs + IL\ILImporter.cs @@ -405,9 +408,6 @@ IL\ILProvider.cs - - IL\McgInteropSupport.cs - IL\Stubs\AddrOfIntrinsic.cs diff --git a/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilation.cs b/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilation.cs index 92a26eb4255..61e8c8d594d 100644 --- a/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilation.cs +++ b/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilation.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; +using Internal.IL; using Internal.TypeSystem; using ILCompiler.DependencyAnalysis; @@ -22,10 +23,11 @@ internal CppCodegenCompilation( DependencyAnalyzerBase dependencyGraph, NodeFactory nodeFactory, IEnumerable roots, + ILProvider ilProvider, DebugInformationProvider debugInformationProvider, Logger logger, CppCodegenConfigProvider options) - : base(dependencyGraph, nodeFactory, GetCompilationRoots(roots, nodeFactory), debugInformationProvider, null, logger) + : base(dependencyGraph, nodeFactory, GetCompilationRoots(roots, nodeFactory), ilProvider, debugInformationProvider, null, logger) { Options = options; } diff --git a/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilationBuilder.cs b/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilationBuilder.cs index 0068f2da84a..6dee6e85d13 100644 --- a/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilationBuilder.cs +++ b/src/ILCompiler.CppCodeGen/src/Compiler/CppCodegenCompilationBuilder.cs @@ -8,6 +8,7 @@ using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysisFramework; +using Internal.IL; using Internal.TypeSystem; namespace ILCompiler @@ -17,6 +18,7 @@ public sealed class CppCodegenCompilationBuilder : CompilationBuilder // These need to provide reasonable defaults so that the user can optionally skip // calling the Use/Configure methods and still get something reasonable back. CppCodegenConfigProvider _config = new CppCodegenConfigProvider(Array.Empty()); + private ILProvider _ilProvider = new CoreRTILProvider(); public CppCodegenCompilationBuilder(CompilerTypeSystemContext context, CompilationModuleGroup group) : base(context, group, new CoreRTNameMangler(new CppNodeMangler(), true)) @@ -29,13 +31,24 @@ public override CompilationBuilder UseBackendOptions(IEnumerable options return this; } + public override CompilationBuilder UseILProvider(ILProvider ilProvider) + { + _ilProvider = ilProvider; + return this; + } + + protected override ILProvider GetILProvider() + { + return _ilProvider; + } + public override ICompilation ToCompilation() { var interopStubManager = new CompilerGeneratedInteropStubManager(_compilationGroup, _context, new InteropStateManager(_context.GeneratedAssembly)); CppCodegenNodeFactory factory = new CppCodegenNodeFactory(_context, _compilationGroup, _metadataManager, interopStubManager, _nameMangler, _vtableSliceProvider, _dictionaryLayoutProvider); DependencyAnalyzerBase graph = CreateDependencyGraph(factory); - return new CppCodegenCompilation(graph, factory, _compilationRoots, _debugInformationProvider, _logger, _config); + return new CppCodegenCompilation(graph, factory, _compilationRoots, _ilProvider, _debugInformationProvider, _logger, _config); } } diff --git a/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilation.cs b/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilation.cs index e49cbba16ca..eee951f3030 100644 --- a/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilation.cs +++ b/src/ILCompiler.WebAssembly/src/Compiler/WebAssemblyCodegenCompilation.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using Internal.TypeSystem; +using Internal.IL; using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysisFramework; @@ -24,10 +25,11 @@ internal WebAssemblyCodegenCompilation( DependencyAnalyzerBase dependencyGraph, WebAssemblyCodegenNodeFactory nodeFactory, IEnumerable roots, + ILProvider ilProvider, DebugInformationProvider debugInformationProvider, Logger logger, WebAssemblyCodegenConfigProvider options) - : base(dependencyGraph, nodeFactory, GetCompilationRoots(roots, nodeFactory), debugInformationProvider, null, logger) + : base(dependencyGraph, nodeFactory, GetCompilationRoots(roots, nodeFactory), ilProvider, debugInformationProvider, null, logger) { NodeFactory = nodeFactory; Module = LLVM.ModuleCreateWithName("netscripten"); @@ -56,7 +58,7 @@ protected override void ComputeDependencyNodeDependencies(List()); + private ILProvider _ilProvider = new CoreRTILProvider(); public WebAssemblyCodegenCompilationBuilder(CompilerTypeSystemContext context, CompilationModuleGroup group) : base(context, group, new CoreRTNameMangler(new WebAssemblyNodeMangler(), false)) @@ -29,12 +31,23 @@ public override CompilationBuilder UseBackendOptions(IEnumerable options return this; } + public override CompilationBuilder UseILProvider(ILProvider ilProvider) + { + _ilProvider = ilProvider; + return this; + } + + protected override ILProvider GetILProvider() + { + return _ilProvider; + } + public override ICompilation ToCompilation() { var interopStubManager = new CompilerGeneratedInteropStubManager(_compilationGroup, _context, new InteropStateManager(_context.GeneratedAssembly)); WebAssemblyCodegenNodeFactory factory = new WebAssemblyCodegenNodeFactory(_context, _compilationGroup, _metadataManager, interopStubManager, _nameMangler, _vtableSliceProvider, _dictionaryLayoutProvider); DependencyAnalyzerBase graph = CreateDependencyGraph(factory, new ObjectNode.ObjectNodeComparer(new CompilerComparer())); - return new WebAssemblyCodegenCompilation(graph, factory, _compilationRoots, _debugInformationProvider, _logger, _config); + return new WebAssemblyCodegenCompilation(graph, factory, _compilationRoots, _ilProvider, _debugInformationProvider, _logger, _config); } } diff --git a/src/System.Private.Jit/src/Internal/Runtime/JitSupport/JitCompilation.cs b/src/System.Private.Jit/src/Internal/Runtime/JitSupport/JitCompilation.cs index 2c5f06f72e5..1d891215594 100644 --- a/src/System.Private.Jit/src/Internal/Runtime/JitSupport/JitCompilation.cs +++ b/src/System.Private.Jit/src/Internal/Runtime/JitSupport/JitCompilation.cs @@ -22,7 +22,7 @@ public Compilation(TypeSystemContext context) _typeSystemContext = context; _typeGetTypeMethodThunks = new TypeGetTypeMethodThunkCache(context.GetWellKnownType(WellKnownType.Object)); _pInvokeILProvider = new PInvokeILProvider(new PInvokeILEmitterConfiguration(forceLazyResolution: true), null); - _methodILCache = new ILProvider(_pInvokeILProvider); + _ilProvider = new CoreRTILProvider(); _nodeFactory = new NodeFactory(context); _devirtualizationManager = new DevirtualizationManager(); } @@ -31,7 +31,7 @@ public Compilation(TypeSystemContext context) private readonly TypeSystemContext _typeSystemContext; protected readonly Logger _logger = Logger.Null; private readonly TypeGetTypeMethodThunkCache _typeGetTypeMethodThunks; - private ILProvider _methodILCache; + private ILProvider _ilProvider; private PInvokeILProvider _pInvokeILProvider; private readonly DevirtualizationManager _devirtualizationManager; @@ -52,11 +52,7 @@ public ObjectNode GetFieldRvaData(FieldDesc field) internal MethodIL GetMethodIL(MethodDesc method) { - // Flush the cache when it grows too big - if (_methodILCache.Count > 1000) - _methodILCache = new ILProvider(_pInvokeILProvider); - - return _methodILCache.GetMethodIL(method); + return _ilProvider.GetMethodIL(method); } public bool HasLazyStaticConstructor(TypeDesc type) { return type.HasStaticConstructor; } diff --git a/src/System.Private.Jit/src/System.Private.Jit.csproj b/src/System.Private.Jit/src/System.Private.Jit.csproj index 942a0b74b7a..a9ef434deda 100644 --- a/src/System.Private.Jit/src/System.Private.Jit.csproj +++ b/src/System.Private.Jit/src/System.Private.Jit.csproj @@ -62,9 +62,9 @@ + -