From 5aa5f79bcac7e8456eb09fb9349151fd99966b37 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 9 Jun 2022 18:24:01 -0700 Subject: [PATCH 01/19] Add support for cross module inlining and cross module generic compilation to Crossgen2 - Refactor Module into ModuleBase and Module - The goal is to have allow a subset version of Module which can only hold refs, this is to be used by the manifest module in an R2R image to allow for version resilient cross module references. - Update handling of ModuleBase so that its used everywhere that tokens are parsed from R2R - Remove ENCODE_MODULE_ID_FOR_STATICS and ENCODE_ACTIVE_DEPENDENCY - These were only used for NGEN, and conflict with easy impelmentation for the ModuleBase concept - Remove locking in ENCODE_STRING_HANDLE processing, and tweak comments. Comments applied to the removed ngen based code, and the lock was only necessary for the old ngen thing. - Adjust ComputeLoaderModuleWorker for locating loader module - Follow comment more accurately, to avoid putting every generic into its definition module. This will make R2R function lookup able to find compiled instantiations in some cases. This may be what we want long term, it may not. - Remove MemberRefToDesc map and replace with LookupMap like the other token types. We no longer make use of the hot table, so this is more efficient - Also reduces complexity of implementation of ModuleBase - Build fixup to describe a single method as a standalone blob of data - There are parallel implementations in Crossgen2 and in the runtime - They produce binary identical output - Basic R2RDump support for new fixup - Adjust module indices used within the R2R format to support a module index which refers to the R2R manifest metadata. This requires bumping the R2R version to 6.2 - Add a module index between the set of assembly refs in the index 0 module and the set of assembly refs in the R2R manifest metadata - Adjust compilation dependency rules to include a few critical AsyncStateMachineBox methods - Remove PEImage handling of native metadata which was duplicative - Do not enable any more devirtualization than was already in use, even in the cross module compilation scenario. In particular, do not enable devirtualization of methods where the decl method isn't within the version bubble, even if the decl method could be represented with a cross-module reference token. (This could be fixed, but is out of scope for this initial investigation) Make the compilation deterministic in this new model, even though we are generating new tokens on demand - Implement this by detecting when we need new tokens during a compile, and recompiling with new tokens when necessary - This may result in compiling the same code as much as twice Compile the right set of methods with cross module inlining enabled - Add support for compiling the called virtual methods on generic types - This catches the List and Dictionary scenarios - Add command line switches to enable/disable the new behavior NOTE: Module::IsInSameVersionBubble doesn't actually behave as expected. Currently we're generating a native manifest metadata in all circumstances, which will cause all modules to be treated as having InputBubble semantics. However, fortunately, we don't actually do anything like that at runtime. The only use of IsInSameVersionBubble is used to control R2R function lookup, which will not cause a problem. --- src/coreclr/debug/daccess/request.cpp | 2 +- src/coreclr/inc/corcompile.h | 15 +- src/coreclr/inc/readytorun.h | 20 +- .../Common/Internal/Runtime/ModuleHeaders.cs | 3 +- .../Internal/Runtime/ReadyToRunConstants.cs | 15 + .../tools/Common/JitInterface/CorInfoImpl.cs | 30 +- .../Common/TypeSystem/Common/MethodDesc.cs | 2 +- .../Ecma/EcmaSignatureTranslator.cs | 216 ++++++ .../Common/TypeSystem/Ecma/IEcmaModule.cs | 75 ++ .../Common/TypeSystem/IL/EcmaMethodIL.cs | 7 +- .../tools/Common/TypeSystem/IL/ILProvider.cs | 6 + .../Common/TypeSystem/IL/ILTokenReplacer.cs | 163 +++++ .../TypeSystem/IL/NativeAotILProvider.cs | 11 +- .../IL/Stubs/InterlockedIntrinsics.cs | 40 +- .../Mutable/MutableModule.Sorting.cs | 22 + .../Mutable/MutableModule.Symbols.cs | 14 + .../TypeSystem/Mutable/MutableModule.cs | 315 ++++++++ .../DependencyGraphTests.cs | 2 +- .../Compiler/ManagedBinaryEmitter.cs | 386 ---------- .../ILCompiler.Compiler.csproj | 4 - .../CompilationModuleGroup.ReadyToRun.cs | 21 +- .../ReadyToRun/FieldFixupSignature.cs | 3 +- .../ReadyToRun/GenericLookupSignature.cs | 12 +- .../ReadyToRun/ILBodyFixupSignature.cs | 108 +++ .../ReadyToRun/ImportSectionNode.cs | 2 + .../ReadyToRun/InliningInfoNode.cs | 336 +++++++-- .../ReadyToRun/InstanceEntryPointTableNode.cs | 32 +- .../InstrumentationDataTableNode.cs | 13 +- .../ReadyToRun/ManifestMetadataTableNode.cs | 136 ++-- .../ReadyToRun/MethodFixupSignature.cs | 21 +- .../ReadyToRun/MethodWithGCInfo.cs | 7 +- .../ReadyToRun/ModuleToken.cs | 13 +- .../ReadyToRun/ModuleTokenResolver.cs | 50 +- .../ReadyToRun/NewArrayFixupSignature.cs | 2 +- .../ReadyToRun/NewObjectFixupSignature.cs | 2 +- .../ReadyToRun/PrecodeHelperImport.cs | 5 + .../ReadyToRun/SignatureBuilder.cs | 17 +- .../ReadyToRun/SignatureContext.cs | 25 +- .../ReadyToRun/TypeFixupSignature.cs | 41 +- .../ReadyToRunCodegenNodeFactory.cs | 92 ++- .../ReadyToRunSymbolNodeFactory.cs | 11 + .../NoMethodsCompilationModuleGroup.cs | 2 +- .../Compiler/ReadyToRunCodegenCompilation.cs | 202 +++-- .../ReadyToRunCodegenCompilationBuilder.cs | 3 +- .../ReadyToRunCompilationModuleGroupBase.cs | 130 +++- .../Compiler/ReadyToRunCompilerContext.cs | 16 + .../Compiler/ReadyToRunHashCode.cs | 2 +- ...RunSingleAssemblyCompilationModuleGroup.cs | 4 +- .../ReadyToRunStandaloneMethodMetadata.cs | 229 ++++++ .../Compiler/ReadyToRunTableManager.cs | 5 +- .../SingleMethodCompilationModuleGroup.cs | 2 +- .../IL/ReadyToRunILProvider.cs | 147 +++- .../ILCompiler.ReadyToRun.csproj | 2 + .../JitInterface/CorInfoImpl.ReadyToRun.cs | 165 ++++- .../ManifestAssemblyMetadata.cs | 39 + .../ReadyToRunReader.cs | 100 ++- .../ReadyToRunSignature.cs | 39 +- .../Compiler/RyuJitCompilationBuilder.cs | 3 +- .../ILCompiler.TypeSystem.csproj | 24 + src/coreclr/tools/aot/ILCompiler/Program.cs | 2 +- .../tools/aot/crossgen2/CommandLineOptions.cs | 9 + src/coreclr/tools/aot/crossgen2/Program.cs | 4 +- .../aot/crossgen2/Properties/Resources.resx | 62 +- src/coreclr/vm/appdomain.cpp | 2 + src/coreclr/vm/assembly.cpp | 25 +- src/coreclr/vm/assembly.hpp | 2 +- src/coreclr/vm/assemblynative.cpp | 2 +- src/coreclr/vm/binder.cpp | 2 +- src/coreclr/vm/ceeload.cpp | 439 +++++------ src/coreclr/vm/ceeload.h | 304 ++++---- src/coreclr/vm/ceeload.inl | 16 +- src/coreclr/vm/clsload.cpp | 95 ++- src/coreclr/vm/clsload.hpp | 31 +- src/coreclr/vm/common.h | 1 + src/coreclr/vm/genericdict.cpp | 9 +- src/coreclr/vm/inlinetracking.cpp | 230 +++++- src/coreclr/vm/inlinetracking.h | 28 + src/coreclr/vm/jithelpers.cpp | 2 +- src/coreclr/vm/jitinterface.cpp | 172 +++-- src/coreclr/vm/memberload.cpp | 69 +- src/coreclr/vm/memberload.h | 12 +- src/coreclr/vm/method.cpp | 691 ++++++++++++++++++ src/coreclr/vm/method.hpp | 25 + src/coreclr/vm/nativeimage.h | 5 + src/coreclr/vm/peimage.cpp | 63 +- src/coreclr/vm/peimage.h | 3 - src/coreclr/vm/prestub.cpp | 16 +- src/coreclr/vm/readytoruninfo.cpp | 267 ++++++- src/coreclr/vm/readytoruninfo.h | 52 +- src/coreclr/vm/siginfo.cpp | 102 +-- src/coreclr/vm/siginfo.hpp | 56 +- src/coreclr/vm/typeparse.cpp | 2 +- src/coreclr/vm/util.hpp | 1 + src/coreclr/vm/versionresilienthashcode.cpp | 34 + src/coreclr/vm/versionresilienthashcode.h | 3 + src/coreclr/vm/zapsig.cpp | 77 +- src/coreclr/vm/zapsig.h | 24 +- 97 files changed, 4842 insertions(+), 1508 deletions(-) create mode 100644 src/coreclr/tools/Common/TypeSystem/Ecma/EcmaSignatureTranslator.cs create mode 100644 src/coreclr/tools/Common/TypeSystem/Ecma/IEcmaModule.cs create mode 100644 src/coreclr/tools/Common/TypeSystem/IL/ILTokenReplacer.cs create mode 100644 src/coreclr/tools/Common/TypeSystem/Mutable/MutableModule.Sorting.cs create mode 100644 src/coreclr/tools/Common/TypeSystem/Mutable/MutableModule.Symbols.cs create mode 100644 src/coreclr/tools/Common/TypeSystem/Mutable/MutableModule.cs delete mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ManagedBinaryEmitter.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ILBodyFixupSignature.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunStandaloneMethodMetadata.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ManifestAssemblyMetadata.cs diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index f73b1ce8afa2c..c354d240684ee 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -1701,7 +1701,7 @@ ClrDataAccess::GetModuleData(CLRDATA_ADDRESS addr, struct DacpModuleData *Module ModuleData->TypeRefToMethodTableMap = PTR_CDADDR(pModule->m_TypeRefToMethodTableMap.pTable); ModuleData->MethodDefToDescMap = PTR_CDADDR(pModule->m_MethodDefToDescMap.pTable); ModuleData->FieldDefToDescMap = PTR_CDADDR(pModule->m_FieldDefToDescMap.pTable); - ModuleData->MemberRefToDescMap = NULL; + ModuleData->MemberRefToDescMap = PTR_CDADDR(pModule->m_MemberRefMap.pTable); ModuleData->FileReferencesMap = PTR_CDADDR(pModule->m_FileReferencesMap.pTable); ModuleData->ManifestModuleReferencesMap = PTR_CDADDR(pModule->m_ManifestModuleReferencesMap.pTable); diff --git a/src/coreclr/inc/corcompile.h b/src/coreclr/inc/corcompile.h index 4629e931b41aa..9887133db06d8 100644 --- a/src/coreclr/inc/corcompile.h +++ b/src/coreclr/inc/corcompile.h @@ -46,6 +46,16 @@ inline ReadyToRunImportSectionFlags operator &( const ReadyToRunImportSectionFla return static_cast(static_cast(left) & static_cast(right)); } +inline ReadyToRunCrossModuleInlineFlags operator |( const ReadyToRunCrossModuleInlineFlags left, const ReadyToRunCrossModuleInlineFlags right) +{ + return static_cast(static_cast(left) | static_cast(right)); +} + +inline ReadyToRunCrossModuleInlineFlags operator &( const ReadyToRunCrossModuleInlineFlags left, const ReadyToRunCrossModuleInlineFlags right) +{ + return static_cast(static_cast(left) & static_cast(right)); +} + #ifdef TARGET_X86 typedef DPTR(RUNTIME_FUNCTION) PTR_RUNTIME_FUNCTION; @@ -149,9 +159,11 @@ enum CORCOMPILE_FIXUP_BLOB_KIND ENCODE_CHECK_VIRTUAL_FUNCTION_OVERRIDE, /* Generate a runtime check to ensure that virtual function resolution has equivalent behavior at runtime as at compile time. If not equivalent, code will not be used */ ENCODE_VERIFY_VIRTUAL_FUNCTION_OVERRIDE, /* Generate a runtime check to ensure that virtual function resolution has equivalent behavior at runtime as at compile time. If not equivalent, generate runtime failure. */ + ENCODE_CHECK_IL_BODY, /* Check to see if an IL method is defined the same at runtime as at compile time. A failed match will cause code not to be used. */ + ENCODE_VERIFY_IL_BODY, /* Verify an IL body is defined the same at compile time and runtime. A failed match will cause a hard runtime failure. */ + ENCODE_MODULE_HANDLE = 0x50, /* Module token */ ENCODE_STATIC_FIELD_ADDRESS, /* For accessing a static field */ - ENCODE_MODULE_ID_FOR_STATICS, /* For accessing static fields */ ENCODE_MODULE_ID_FOR_GENERIC_STATICS, /* For accessing static fields */ ENCODE_CLASS_ID_FOR_STATICS, /* For accessing static fields */ ENCODE_SYNC_LOCK, /* For synchronizing access to a type */ @@ -159,7 +171,6 @@ enum CORCOMPILE_FIXUP_BLOB_KIND ENCODE_VARARGS_METHODDEF, /* For calling a varargs method */ ENCODE_VARARGS_METHODREF, ENCODE_VARARGS_SIG, - ENCODE_ACTIVE_DEPENDENCY, /* Conditional active dependency */ }; enum EncodeMethodSigFlags diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h index 76354362d57d8..5f41856bd1217 100644 --- a/src/coreclr/inc/readytorun.h +++ b/src/coreclr/inc/readytorun.h @@ -16,7 +16,7 @@ // Keep these in sync with src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs #define READYTORUN_MAJOR_VERSION 0x0006 -#define READYTORUN_MINOR_VERSION 0x0001 +#define READYTORUN_MINOR_VERSION 0x0002 #define MINIMUM_READYTORUN_MAJOR_VERSION 0x006 @@ -60,6 +60,7 @@ enum ReadyToRunFlag READYTORUN_FLAG_NONSHARED_PINVOKE_STUBS = 0x00000008, // PInvoke stubs compiled into image are non-shareable (no secret parameter) READYTORUN_FLAG_EMBEDDED_MSIL = 0x00000010, // MSIL is embedded in the composite R2R executable READYTORUN_FLAG_COMPONENT = 0x00000020, // This is the header describing a component assembly of composite R2R + READYTORUN_FLAG_MULTIMODULE_VERSION_BUBBLE = 0x00000040, // This R2R module has multiple modules within its version bubble (For versions before version 6.2, all modules are assumed to possibly have this characteristic) }; enum class ReadyToRunSectionType : uint32_t @@ -83,6 +84,7 @@ enum class ReadyToRunSectionType : uint32_t OwnerCompositeExecutable = 116, // Added in V4.1 PgoInstrumentationData = 117, // Added in V5.2 ManifestAssemblyMvids = 118, // Added in V5.3 + CrossModuleInlineInfo = 119, // Added in V6.2 // If you add a new section consider whether it is a breaking or non-breaking change. // Usually it is non-breaking, but if it is preferable to have older runtimes fail @@ -99,9 +101,10 @@ struct READYTORUN_SECTION enum class ReadyToRunImportSectionType : uint8_t { - Unknown = 0, + Unknown = 0, StubDispatch = 2, StringHandle = 3, + ILBodyFixups = 7, }; enum class ReadyToRunImportSectionFlags : uint16_t @@ -165,6 +168,16 @@ enum ReadyToRunVirtualFunctionOverrideFlags READYTORUN_VIRTUAL_OVERRIDE_VirtualFunctionOverriden = 0x01, }; +enum class ReadyToRunCrossModuleInlineFlags : uint32_t +{ + CrossModuleInlinee = 0x1, + HasCrossModuleInliners = 0x2, + CrossModuleInlinerIndexShift = 2, + + InlinerRidHasModule = 0x1, + InlinerRidShift = 1, +}; + // // Constants for fixup signature encoding // @@ -227,6 +240,9 @@ enum ReadyToRunFixupKind READYTORUN_FIXUP_Check_VirtualFunctionOverride = 0x33, /* Generate a runtime check to ensure that virtual function resolution has equivalent behavior at runtime as at compile time. If not equivalent, code will not be used */ READYTORUN_FIXUP_Verify_VirtualFunctionOverride = 0x34, /* Generate a runtime check to ensure that virtual function resolution has equivalent behavior at runtime as at compile time. If not equivalent, generate runtime failure. */ + + READYTORUN_FIXUP_Check_IL_Body = 0x35, /* Check to see if an IL method is defined the same at runtime as at compile time. A failed match will cause code not to be used. */ + READYTORUN_FIXUP_Verify_IL_Body = 0x36, /* Verify an IL body is defined the same at compile time and runtime. A failed match will cause a hard runtime failure. */ }; // diff --git a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs index 30d8af4efb23c..1aa5f3f904c65 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs @@ -15,7 +15,7 @@ internal struct ReadyToRunHeaderConstants public const uint Signature = 0x00525452; // 'RTR' public const ushort CurrentMajorVersion = 6; - public const ushort CurrentMinorVersion = 1; + public const ushort CurrentMinorVersion = 2; } #pragma warning disable 0169 @@ -67,6 +67,7 @@ public enum ReadyToRunSectionType OwnerCompositeExecutable = 116, // Added in 4.1 PgoInstrumentationData = 117, // Added in 5.2 ManifestAssemblyMvids = 118, // Added in 5.3 + CrossModuleInlineInfo = 119, // Added in 6.2 // // NativeAOT ReadyToRun sections diff --git a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs index 59cdfe4b38077..134f6815fb04d 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ReadyToRunConstants.cs @@ -17,6 +17,7 @@ public enum ReadyToRunFlags READYTORUN_FLAG_NonSharedPInvokeStubs = 0x00000008, // PInvoke stubs compiled into image are non-shareable (no secret parameter) READYTORUN_FLAG_EmbeddedMSIL = 0x00000010, // MSIL is embedded in the composite R2R executable READYTORUN_FLAG_Component = 0x00000020, // This is the header describing a component assembly of composite R2R + READYTORUN_FLAG_MultiModuleVersionBubble = 0x00000040, // This R2R module has multiple modules within its version bubble } public enum ReadyToRunImportSectionType : byte @@ -24,6 +25,7 @@ public enum ReadyToRunImportSectionType : byte Unknown = 0, StubDispatch = 2, StringHandle = 3, + ILBodyFixups = 7, } [Flags] @@ -76,6 +78,16 @@ public enum ReadyToRunVirtualFunctionOverrideFlags : uint VirtualFunctionOverriden = 0x01, } + [Flags] + enum ReadyToRunCrossModuleInlineFlags : uint + { + CrossModuleInlinee = 0x1, + HasCrossModuleInliners = 0x2, + CrossModuleInlinerIndexShift = 2, + InlinerRidHasModule = 0x1, + InlinerRidShift = 1, + }; + public enum DictionaryEntryKind { EmptySlot = 0, @@ -149,6 +161,9 @@ public enum ReadyToRunFixupKind Check_VirtualFunctionOverride = 0x33, // Generate a runtime check to ensure that virtual function resolution has equivalent behavior at runtime as at compile time. If not equivalent, code will not be used Verify_VirtualFunctionOverride = 0x34, // Generate a runtime check to ensure that virtual function resolution has equivalent behavior at runtime as at compile time. If not equivalent, generate runtime failure. + Check_IL_Body = 0x35, /* Check to see if an IL method is defined the same at runtime as at compile time. A failed match will cause code not to be used. */ + Verify_IL_Body = 0x36, /* Verify an IL body is defined the same at compile time and runtime. A failed match will cause a hard runtime failure. */ + ModuleOverride = 0x80, // followed by sig-encoded UInt with assemblyref index into either the assemblyref // table of the MSIL metadata of the master context module for the signature or diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 3c1eb891ac4f8..2a0f1b2adeff1 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -34,6 +34,12 @@ namespace Internal.JitInterface { + enum CompilationResult + { + CompilationComplete, + CompilationRetryRequested + } + internal sealed unsafe partial class CorInfoImpl { // @@ -285,7 +291,7 @@ IntPtr LocalObjectToHandle(object input) return null; } - private void CompileMethodInternal(IMethodNode methodCodeNodeNeedingCode, MethodIL methodIL) + private CompilationResult CompileMethodInternal(IMethodNode methodCodeNodeNeedingCode, MethodIL methodIL) { // methodIL must not be null if (methodIL == null) @@ -385,10 +391,20 @@ private void CompileMethodInternal(IMethodNode methodCodeNodeNeedingCode, Method Array.Resize(ref _code, (int)codeSize); } } + + CompilationResult compilationCompleteBehavior = CompilationResult.CompilationComplete; + DetermineIfCompilationShouldBeRetried(ref compilationCompleteBehavior); + if (compilationCompleteBehavior == CompilationResult.CompilationRetryRequested) + return compilationCompleteBehavior; + PublishCode(); PublishROData(); + + return CompilationResult.CompilationComplete; } + partial void DetermineIfCompilationShouldBeRetried(ref CompilationResult result); + private void PublishCode() { var relocs = _codeRelocs.ToArray(); @@ -596,6 +612,7 @@ private void CompileMethodCleanup() _precodeFixups = null; _stashedPrecodeFixups.Clear(); _stashedInlinedMethods.Clear(); + _ilBodiesNeeded = null; #endif _instantiationToJitVisibleInstantiation = null; @@ -1205,6 +1222,11 @@ private void getMethodSig(CORINFO_METHOD_STRUCT_* ftn, CORINFO_SIG_INFO* sig, CO private bool getMethodInfo(CORINFO_METHOD_STRUCT_* ftn, CORINFO_METHOD_INFO* info) { MethodDesc method = HandleToObject(ftn); + // Add an early CanInline check to see if referring to the IL of the target methods is + // permitted from within this MethodBeingCompiled, the full CanInline check will be performed + // later. + if (!_compilation.CanInline(MethodBeingCompiled, method)) + return false; MethodIL methodIL = _compilation.GetMethodIL(method); return Get_CORINFO_METHOD_INFO(method, methodIL, info); } @@ -1343,7 +1365,7 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) } else { - ModuleToken declToken = resolver.GetModuleTokenForMethod(decl.GetTypicalMethodDefinition(), throwIfNotFound: false); + ModuleToken declToken = resolver.GetModuleTokenForMethod(decl.GetTypicalMethodDefinition(), allowDynamicallyCreatedReference: false, throwIfNotFound: false); if (declToken.IsNull) { info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_DECL_NOT_REPRESENTABLE; @@ -1381,7 +1403,7 @@ private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info) else { #if READYTORUN - methodWithTokenImpl = new MethodWithToken(nonUnboxingImpl, resolver.GetModuleTokenForMethod(nonUnboxingImpl.GetTypicalMethodDefinition()), null, unboxingStub, null, devirtualizedMethodOwner: impl.OwningType); + methodWithTokenImpl = new MethodWithToken(nonUnboxingImpl, resolver.GetModuleTokenForMethod(nonUnboxingImpl.GetTypicalMethodDefinition(), allowDynamicallyCreatedReference: false, throwIfNotFound: true), null, unboxingStub, null, devirtualizedMethodOwner: impl.OwningType); #endif info->resolvedTokenDevirtualizedMethod = CreateResolvedTokenFromMethod(this, impl @@ -1756,7 +1778,7 @@ private void resolveToken(ref CORINFO_RESOLVED_TOKEN pResolvedToken) #if READYTORUN TypeDesc owningType = methodIL.OwningMethod.GetTypicalMethodDefinition().OwningType; - bool recordToken = _compilation.CompilationModuleGroup.VersionsWithType(owningType) && owningType is EcmaType; + bool recordToken = (_compilation.CompilationModuleGroup.VersionsWithType(owningType) || (methodIL.GetMethodILScopeDefinition() is IMethodTokensAreUseableInCompilation)) && owningType is EcmaType; #endif if (result is MethodDesc method) diff --git a/src/coreclr/tools/Common/TypeSystem/Common/MethodDesc.cs b/src/coreclr/tools/Common/TypeSystem/Common/MethodDesc.cs index 01e6153e07e7c..1a736b7f98f96 100644 --- a/src/coreclr/tools/Common/TypeSystem/Common/MethodDesc.cs +++ b/src/coreclr/tools/Common/TypeSystem/Common/MethodDesc.cs @@ -41,7 +41,7 @@ public struct EmbeddedSignatureData /// /// Represents the parameter types, the return type, and flags of a method. /// - public sealed partial class MethodSignature : TypeSystemEntity + public sealed partial class MethodSignature : TypeSystemEntity, IEquatable { internal MethodSignatureFlags _flags; internal int _genericParameterCount; diff --git a/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaSignatureTranslator.cs b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaSignatureTranslator.cs new file mode 100644 index 0000000000000..dd7e01fec9e4a --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Ecma/EcmaSignatureTranslator.cs @@ -0,0 +1,216 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; + +namespace Internal.TypeSystem.Ecma +{ + public struct EcmaSignatureTranslator + { + BlobReader _input; + BlobBuilder _output; + + Func _getAlternateStreamToken; + + public EcmaSignatureTranslator(BlobReader input, BlobBuilder output, Func getAlternateStreamToken) + { + _input = input; + _output = output; + _getAlternateStreamToken = getAlternateStreamToken; + } + + // Various parsing functions for processing through the locals of a function and translating them to the + // alternate form with new tokens. + int ParseCompressedInt() + { + int value = _input.ReadCompressedInteger(); + _output.WriteCompressedInteger(value); + return value; + } + + int ParseCompressedSignedInt() + { + int value = _input.ReadCompressedSignedInteger(); + _output.WriteCompressedSignedInteger(value); + return value; + } + + byte ParseByte() + { + byte value = _input.ReadByte(); + _output.WriteByte(value); + return value; + } + + byte PeekByte() + { + byte value = _input.ReadByte(); + _input.Offset = _input.Offset - 1; + return value; + } + + public void ParseMemberRefSignature() + { + byte sigHeader = PeekByte(); + if (sigHeader == (byte)SignatureKind.Field) + ParseFieldSignature(); + else + ParseMethodSignature(); + } + + public void ParseFieldSignature() + { + byte sigHeader = ParseByte(); + if (sigHeader != (byte)SignatureKind.Field) + throw new BadImageFormatException(); + ParseType(); + } + + public void ParseMethodSignature() + { + byte sigHeader = ParseByte(); + if (((int)sigHeader & (int)SignatureAttributes.Generic) == (int)SignatureAttributes.Generic) + { + // Parse arity + ParseCompressedInt(); + } + int argCount = ParseCompressedInt(); + for (int i = 0; i <= argCount; i++) + ParseType(); + } + + public void ParseLocalsSignature() + { + byte sigHeader = ParseByte(); + if ((SignatureKind)sigHeader != SignatureKind.LocalVariables) + throw new BadImageFormatException(); + + int localsCount = ParseCompressedInt(); + for (int i = 0; i < localsCount; i++) + { + ParseType(); + } + } + + public void ParseMethodSpecSignature() + { + byte sigHeader = ParseByte(); + if ((SignatureKind)sigHeader != SignatureKind.MethodSpecification) + { + throw new BadImageFormatException(); + } + + int argCount = ParseCompressedInt(); + for (int i = 0; i < argCount; i++) + { + ParseType(); + } + } + + + void ParseTypeHandle() + { + int token = MetadataTokens.GetToken(_input.ReadTypeHandle()); + int newToken = _getAlternateStreamToken(token); + int newEncodedHandle = CodedIndex.TypeDefOrRefOrSpec(MetadataTokens.EntityHandle(newToken)); + _output.WriteCompressedInteger(newEncodedHandle); + } + + public void ParseType() + { + SignatureTypeCode typeCode; + for (; ; ) + { + int sigcodeRaw = ParseCompressedInt(); + const int ELEMENT_TYPE_CLASS = 0x12; + const int ELEMENT_TYPE_VALUETYPE = 0x11; + if (sigcodeRaw == ELEMENT_TYPE_CLASS || sigcodeRaw == ELEMENT_TYPE_VALUETYPE) + typeCode = SignatureTypeCode.TypeHandle; + else + typeCode = (SignatureTypeCode)sigcodeRaw; + + switch (typeCode) + { + case SignatureTypeCode.RequiredModifier: + case SignatureTypeCode.OptionalModifier: + ParseTypeHandle(); + continue; + case SignatureTypeCode.Pinned: + case SignatureTypeCode.Sentinel: + continue; + } + break; + } + + switch (typeCode) + { + case SignatureTypeCode.Void: + case SignatureTypeCode.Boolean: + case SignatureTypeCode.SByte: + case SignatureTypeCode.Byte: + case SignatureTypeCode.Int16: + case SignatureTypeCode.UInt16: + case SignatureTypeCode.Int32: + case SignatureTypeCode.UInt32: + case SignatureTypeCode.Int64: + case SignatureTypeCode.UInt64: + case SignatureTypeCode.Single: + case SignatureTypeCode.Double: + case SignatureTypeCode.Char: + case SignatureTypeCode.String: + case SignatureTypeCode.IntPtr: + case SignatureTypeCode.UIntPtr: + case SignatureTypeCode.Object: + case SignatureTypeCode.TypedReference: + break; + + case SignatureTypeCode.TypeHandle: + ParseTypeHandle(); + break; + case SignatureTypeCode.SZArray: + case SignatureTypeCode.ByReference: + case SignatureTypeCode.Pointer: + { + ParseType(); + break; + } + case SignatureTypeCode.Array: + { + ParseType(); + var rank = ParseCompressedInt(); + + var boundsCount = ParseCompressedInt(); + for (int i = 0; i < boundsCount; i++) + ParseCompressedInt(); + var lowerBoundsCount = ParseCompressedInt(); + for (int j = 0; j < lowerBoundsCount; j++) + ParseCompressedSignedInt(); + break; + } + case SignatureTypeCode.GenericTypeParameter: + case SignatureTypeCode.GenericMethodParameter: + ParseCompressedInt(); + break; + case SignatureTypeCode.GenericTypeInstance: + { + ParseType(); + int instanceLength = ParseCompressedInt(); + for (int i = 0; i < instanceLength; i++) + { + ParseType(); + } + break; + } + + case SignatureTypeCode.FunctionPointer: + ParseMethodSignature(); + break; + default: + ThrowHelper.ThrowBadImageFormatException(); + return; + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Ecma/IEcmaModule.cs b/src/coreclr/tools/Common/TypeSystem/Ecma/IEcmaModule.cs new file mode 100644 index 0000000000000..816906f7cb9ad --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Ecma/IEcmaModule.cs @@ -0,0 +1,75 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Reflection.PortableExecutable; + +using Internal.TypeSystem.Ecma; + +namespace Internal.IL +{ + // Marker interface implemented by EcmaMethodIL and EcmaMethodILScope + // and other providers of IL that comes ultimately from Ecma IL. + public interface IEcmaMethodIL + { + public IEcmaModule Module { get; } + } + + public sealed partial class EcmaMethodIL : IEcmaMethodIL + { + IEcmaModule IEcmaMethodIL.Module + { + get + { + return _module; + } + } + } + + public sealed partial class EcmaMethodILScope : IEcmaMethodIL + { + IEcmaModule IEcmaMethodIL.Module + { + get + { + return _module; + } + } + } +} + +namespace Internal.TypeSystem.Ecma +{ + public interface IEcmaModule + { + MetadataReader MetadataReader { get; } + TypeDesc GetType(EntityHandle handle); + Object GetObject(EntityHandle handle, NotFoundBehavior notFoundBehavior = NotFoundBehavior.Throw); + int CompareTo(IEcmaModule other); + int ModuleTypeSort { get; } + + IAssemblyDesc Assembly { get; } + } + + public partial class EcmaModule : IEcmaModule + { + public int CompareTo(IEcmaModule other) + { + if (this == other) + return 0; + + if (other is EcmaModule emoduleOther) + { + return CompareTo(emoduleOther); + } + + return ModuleTypeSort.CompareTo(other.ModuleTypeSort); + } + + public int ModuleTypeSort => 0; + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/EcmaMethodIL.cs b/src/coreclr/tools/Common/TypeSystem/IL/EcmaMethodIL.cs index ce2ced7e34ded..a521ba23b8f24 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/EcmaMethodIL.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/EcmaMethodIL.cs @@ -11,10 +11,7 @@ namespace Internal.IL { - // Marker interface implemented by EcmaMethodIL and EcmaMethodILScope - public interface IEcmaMethodIL { } - - public sealed partial class EcmaMethodIL : MethodIL, IEcmaMethodIL + public sealed partial class EcmaMethodIL : MethodIL { private readonly EcmaModule _module; private readonly EcmaMethod _method; @@ -144,7 +141,7 @@ public override object GetObject(int token, NotFoundBehavior notFoundBehavior = } } - public sealed partial class EcmaMethodILScope : MethodILScope, IEcmaMethodIL + public sealed partial class EcmaMethodILScope : MethodILScope { private readonly EcmaModule _module; private readonly EcmaMethod _method; diff --git a/src/coreclr/tools/Common/TypeSystem/IL/ILProvider.cs b/src/coreclr/tools/Common/TypeSystem/IL/ILProvider.cs index eef7d154b42e2..788efaf21131d 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/ILProvider.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/ILProvider.cs @@ -11,6 +11,12 @@ namespace Internal.IL /// public abstract class ILProvider { + private int _version = 0; + protected void IncrementVersion() + { + _version++; + } + public int Version => _version; public abstract MethodIL GetMethodIL(MethodDesc method); } } diff --git a/src/coreclr/tools/Common/TypeSystem/IL/ILTokenReplacer.cs b/src/coreclr/tools/Common/TypeSystem/IL/ILTokenReplacer.cs new file mode 100644 index 0000000000000..ff1265923e421 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/ILTokenReplacer.cs @@ -0,0 +1,163 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Buffers.Binary; + +namespace Internal.IL +{ + public class ILTokenReplacer + { + public static void Replace(byte[] ilStream, Func tokenReplaceFunc) + { + int currentOffset = 0; + for (; currentOffset < ilStream.Length;) + { + ILOpcode opCode = (ILOpcode)ReadILByte(); + again: + + switch (opCode) + { + case ILOpcode.ldarg_s: + case ILOpcode.ldarga_s: + case ILOpcode.starg_s: + case ILOpcode.ldloc_s: + case ILOpcode.ldloca_s: + case ILOpcode.stloc_s: + case ILOpcode.ldc_i4_s: + case ILOpcode.unaligned: + case ILOpcode.no: + SkipIL(1); + break; + case ILOpcode.ldarg: + case ILOpcode.ldarga: + case ILOpcode.starg: + case ILOpcode.ldloc: + case ILOpcode.ldloca: + case ILOpcode.stloc: + SkipIL(2); + break; + case ILOpcode.ldc_i4: + case ILOpcode.ldc_r4: + SkipIL(4); + break; + case ILOpcode.ldc_i8: + case ILOpcode.ldc_r8: + SkipIL(8); + break; + case ILOpcode.jmp: + case ILOpcode.call: + case ILOpcode.calli: + case ILOpcode.callvirt: + case ILOpcode.cpobj: + case ILOpcode.ldobj: + case ILOpcode.ldstr: + case ILOpcode.newobj: + case ILOpcode.castclass: + case ILOpcode.isinst: + case ILOpcode.unbox: + case ILOpcode.ldfld: + case ILOpcode.ldflda: + case ILOpcode.stfld: + case ILOpcode.ldsfld: + case ILOpcode.ldsflda: + case ILOpcode.stsfld: + case ILOpcode.stobj: + case ILOpcode.box: + case ILOpcode.newarr: + case ILOpcode.ldelema: + case ILOpcode.ldelem: + case ILOpcode.stelem: + case ILOpcode.unbox_any: + case ILOpcode.refanyval: + case ILOpcode.mkrefany: + case ILOpcode.ldtoken: + case ILOpcode.ldftn: + case ILOpcode.ldvirtftn: + case ILOpcode.initobj: + case ILOpcode.constrained: + case ILOpcode.sizeof_: + ReplaceToken(); + break; + case ILOpcode.prefix1: + opCode = (ILOpcode)(0x100 + ReadILByte()); + goto again; + case ILOpcode.br_s: + case ILOpcode.leave_s: + case ILOpcode.brfalse_s: + case ILOpcode.brtrue_s: + case ILOpcode.beq_s: + case ILOpcode.bge_s: + case ILOpcode.bgt_s: + case ILOpcode.ble_s: + case ILOpcode.blt_s: + case ILOpcode.bne_un_s: + case ILOpcode.bge_un_s: + case ILOpcode.bgt_un_s: + case ILOpcode.ble_un_s: + case ILOpcode.blt_un_s: + SkipIL(1); + break; + case ILOpcode.br: + case ILOpcode.leave: + case ILOpcode.brfalse: + case ILOpcode.brtrue: + case ILOpcode.beq: + case ILOpcode.bge: + case ILOpcode.bgt: + case ILOpcode.ble: + case ILOpcode.blt: + case ILOpcode.bne_un: + case ILOpcode.bge_un: + case ILOpcode.bgt_un: + case ILOpcode.ble_un: + case ILOpcode.blt_un: + SkipIL(4); + break; + case ILOpcode.switch_: + { + uint count = ReadILUInt32(); + SkipIL(checked((int)(count * 4))); + } + break; + default: + continue; + } + + } + + byte ReadILByte() + { + byte b = ilStream[currentOffset++]; + return b; + } + void SkipIL(int countToSkip) + { + currentOffset += countToSkip; + } + UInt32 ReadILUInt32() + { + var result = (UInt32)BinaryPrimitives.ReadInt32LittleEndian(ilStream.AsSpan(currentOffset, 4)); + currentOffset += 4; + return result; + } + + void ReplaceToken() + { + var tokenSpan = ilStream.AsSpan(currentOffset, 4); + + // Replace token in IL stream with a new token provided by the tokenReplaceFunc + // + // This is by the StandaloneMethodMetadata logic to create method local tokens + // and by the IL provider used for cross module inlining to create tokens which are + // stable and contained within the R2R module instead of being in a module separated + // by a version boundary. + int token = BinaryPrimitives.ReadInt32LittleEndian(tokenSpan); + var alternateToken = tokenReplaceFunc(token); + BinaryPrimitives.WriteInt32LittleEndian(tokenSpan, alternateToken); + + currentOffset += 4; + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs b/src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs index fb64e67c9dc61..6b84ac244c4ed 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs @@ -3,6 +3,8 @@ using System; +using ILCompiler; + using Internal.TypeSystem; using Internal.TypeSystem.Ecma; @@ -14,6 +16,13 @@ namespace Internal.IL { public sealed class NativeAotILProvider : ILProvider { + private readonly CompilationModuleGroup _compilationGroup; + + public NativeAotILProvider(CompilationModuleGroup group) + { + _compilationGroup = group; + } + private MethodIL TryGetRuntimeImplementedMethodIL(MethodDesc method) { // Provides method bodies for runtime implemented methods. It can return null for @@ -49,7 +58,7 @@ private MethodIL TryGetIntrinsicMethodIL(MethodDesc method) case "Interlocked": { if (owningType.Namespace == "System.Threading") - return InterlockedIntrinsics.EmitIL(method); + return InterlockedIntrinsics.EmitIL(_compilationGroup, method); } break; case "Unsafe": diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/InterlockedIntrinsics.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/InterlockedIntrinsics.cs index 7fc1dbc9ef9da..3d3eb0301e519 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/InterlockedIntrinsics.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/InterlockedIntrinsics.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; - +using ILCompiler; using Internal.TypeSystem; using Debug = System.Diagnostics.Debug; @@ -14,28 +14,34 @@ namespace Internal.IL.Stubs /// public static class InterlockedIntrinsics { - public static MethodIL EmitIL(MethodDesc method) + public static MethodIL EmitIL(CompilationModuleGroup compilationModuleGroup, MethodDesc method) { Debug.Assert(((MetadataType)method.OwningType).Name == "Interlocked"); if (method.HasInstantiation && method.Name == "CompareExchange") { - TypeDesc objectType = method.Context.GetWellKnownType(WellKnownType.Object); - MethodDesc compareExchangeObject = method.OwningType.GetKnownMethod("CompareExchange", - new MethodSignature( - MethodSignatureFlags.Static, - genericParameterCount: 0, - returnType: objectType, - parameters: new TypeDesc[] { objectType.MakeByRefType(), objectType, objectType })); + // Check to see if the tokens needed to describe the CompareExchange are naturally present within + // the compilation. The current implementation of stable tokens used by cross module inlining is not + // compatible with rewriting the IL of an compiler generated intrinsic. + if (compilationModuleGroup.ContainsType(method.OwningType)) + { + TypeDesc objectType = method.Context.GetWellKnownType(WellKnownType.Object); + MethodDesc compareExchangeObject = method.OwningType.GetKnownMethod("CompareExchange", + new MethodSignature( + MethodSignatureFlags.Static, + genericParameterCount: 0, + returnType: objectType, + parameters: new TypeDesc[] { objectType.MakeByRefType(), objectType, objectType })); - ILEmitter emit = new ILEmitter(); - ILCodeStream codeStream = emit.NewCodeStream(); - codeStream.EmitLdArg(0); - codeStream.EmitLdArg(1); - codeStream.EmitLdArg(2); - codeStream.Emit(ILOpcode.call, emit.NewToken(compareExchangeObject)); - codeStream.Emit(ILOpcode.ret); - return emit.Link(method); + ILEmitter emit = new ILEmitter(); + ILCodeStream codeStream = emit.NewCodeStream(); + codeStream.EmitLdArg(0); + codeStream.EmitLdArg(1); + codeStream.EmitLdArg(2); + codeStream.Emit(ILOpcode.call, emit.NewToken(compareExchangeObject)); + codeStream.Emit(ILOpcode.ret); + return emit.Link(method); + } } return null; diff --git a/src/coreclr/tools/Common/TypeSystem/Mutable/MutableModule.Sorting.cs b/src/coreclr/tools/Common/TypeSystem/Mutable/MutableModule.Sorting.cs new file mode 100644 index 0000000000000..30128c0942dd1 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Mutable/MutableModule.Sorting.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. + +using System; +using System.Threading; + +using Debug = System.Diagnostics.Debug; + +namespace Internal.TypeSystem.Ecma +{ + partial class MutableModule + { + // This isn't deterministic, but it is functional. + static int s_globalIndex = 0; + + int _index = Interlocked.Increment(ref s_globalIndex); + public int CompareTo(MutableModule other) + { + return _index.CompareTo(_index); + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Mutable/MutableModule.Symbols.cs b/src/coreclr/tools/Common/TypeSystem/Mutable/MutableModule.Symbols.cs new file mode 100644 index 0000000000000..6558adb1e50c7 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Mutable/MutableModule.Symbols.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; + +namespace Internal.TypeSystem.Ecma +{ + // Pluggable file that adds PDB handling functionality to EcmaModule + partial class MutableModule + { + public PdbSymbolReader PdbReader => null; + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/Mutable/MutableModule.cs b/src/coreclr/tools/Common/TypeSystem/Mutable/MutableModule.cs new file mode 100644 index 0000000000000..df0fdef819120 --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/Mutable/MutableModule.cs @@ -0,0 +1,315 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Reflection.PortableExecutable; +using System.Diagnostics; +using System.IO; + +namespace Internal.TypeSystem.Ecma +{ + public partial class MutableModule : ModuleDesc, IEcmaModule + { + private class ManagedBinaryEmitterForInternalUse : TypeSystemMetadataEmitter + { + Dictionary _moduleRefs = new Dictionary(); + List _moduleRefStrings = new List(); + + protected override EntityHandle GetNonNestedResolutionScope(MetadataType metadataType) + { + var module = metadataType.Module; + + EntityHandle result; + if (_moduleRefs.TryGetValue(module, out result)) + { + return result; + } + + if (module != _typeSystemContext.SystemModule) + { + throw new NotImplementedException(); + // No support for non-system module references yet + } + + _moduleRefStrings.Add("System.Private.CoreLib"); + result = MetadataTokens.ModuleReferenceHandle(_moduleRefStrings.Count); + result = Builder.AddModuleReference(Builder.GetOrAddString("System.Private.CoreLib")); + _moduleRefs.Add(module, result); + return result; + } + + public ManagedBinaryEmitterForInternalUse(AssemblyName assemblyName, TypeSystemContext typeSystemContext, AssemblyFlags assemblyFlags, byte[] publicKeyArray, AssemblyHashAlgorithm hashAlgorithm) + : base(assemblyName, typeSystemContext, assemblyFlags, publicKeyArray, hashAlgorithm) + { + } + } + + class Cache + { + private List> _values = new List>(); + private List _perMetadata = new List(); + TypeSystemMetadataEmitter _currentBinaryEmitter; + MetadataReader _reader; + MutableModule _module; + List _readers = new List (); // For now, as we don't maintain knowledge of how long these live, keep them around forever + public Dictionary Entities = new Dictionary(); + public Dictionary ExistingEntities = new Dictionary(); + string _assemblyName; + AssemblyFlags _assemblyFlags; + byte[] _publicKeyArray; + Version _version; + AssemblyHashAlgorithm _hashAlgorithm; + + public Cache(MutableModule module, string assemblyName, AssemblyFlags assemblyFlags, byte[] publicKeyArray, Version version, AssemblyHashAlgorithm hashAlgorithm) + { + _module = module; + _assemblyName = assemblyName; + _assemblyFlags = assemblyFlags; + _publicKeyArray = publicKeyArray; + _version = version; + _hashAlgorithm = hashAlgorithm; + ResetEmitter(); + } + + private void ResetEmitter() + { + _reader = null; + AssemblyName assemblyName = new AssemblyName(); + assemblyName.Name = _assemblyName; + assemblyName.Version = _version; + + _currentBinaryEmitter = new ManagedBinaryEmitterForInternalUse(assemblyName, _module.Context, _assemblyFlags, _publicKeyArray, _hashAlgorithm); + foreach (var entry in _values) + { + var perMetadata = _perMetadata[entry.Item1]; + var handle = perMetadata.HandleGenerationFunction(_currentBinaryEmitter, entry.Item2); + Debug.Assert(handle == ExistingEntities[entry.Item2]); + } + } + + class PerMetadataFormCache + { + public Func HandleGenerationFunction; + public MutableModule _mutableModule; + public int _cacheIndex; + } + + public Func CreateCacheFunc(Func handleFunc) + { + var perMetadataTypeCache = new PerMetadataFormCache(_module, handleFunc, _perMetadata.Count); + _perMetadata.Add(perMetadataTypeCache); + return perMetadataTypeCache.TryGet; + } + + public MetadataReader Reader + { + get + { + lock (this) + { + if (_reader != null) + return _reader; + + foreach (var item in _currentBinaryEmitter.TypeSystemEntitiesKnown) + { + if (!Entities.ContainsKey(MetadataTokens.GetToken(item.Value))) + { + Entities.Add(MetadataTokens.GetToken(item.Value), item.Key); + ExistingEntities.Add(item.Key, MetadataTokens.GetToken(item.Value)); + } + } + + byte[] metadataArrayTemp = _currentBinaryEmitter.EmitToMetadataBlob(); + byte[] metadataArray = GC.AllocateArray(metadataArrayTemp.Length, pinned: true); + System.Runtime.InteropServices.GCHandle.Alloc(metadataArray, System.Runtime.InteropServices.GCHandleType.Pinned); + Array.Copy(metadataArrayTemp, metadataArray, metadataArray.Length); + _readers.Add(metadataArray); + unsafe + { + fixed (byte* pb = metadataArray) + { + _reader = new MetadataReader(pb, metadataArray.Length); + } + } + return _reader; + } + } + } + + public byte[] MetadataBlob + { + get + { + lock(this) + { + // Ensure the latest metadata blob is up to date which will have the side-effect of ensuring that the metadata blob is accessible + var reader = Reader; + return _readers[_readers.Count - 1]; + } + } + } + + class PerMetadataFormCache : PerMetadataFormCache + { + public PerMetadataFormCache(MutableModule module, Func handleFunc, int cacheIndex) + { + _cacheIndex = cacheIndex; + _mutableModule = module; + HandleGenerationFunction = handleFunc; + } + + public int? TryGet(T value) + { + lock (_mutableModule._cache) + { + try + { + int result; + if (_mutableModule._cache.ExistingEntities.TryGetValue(value, out result)) + { + return result; + } + + if (_mutableModule._cache._reader != null) + { + _mutableModule._cache.ResetEmitter(); + } + + if (_mutableModule.DisableNewTokens) + throw new DisableNewTokensException(); + + var handle = HandleGenerationFunction(_mutableModule._cache._currentBinaryEmitter, value); + _mutableModule._cache.ExistingEntities.Add(value, handle); + _mutableModule._cache.Entities.Add(handle, value); + _mutableModule._cache._values.Add((_cacheIndex, value)); + return handle; + } + catch (NotImplementedException) + { + return null; + } + } + } + } + } + + public MutableModule(TypeSystemContext context, string assemblyName, AssemblyFlags assemblyFlags, byte[] publicKeyArray, Version version, AssemblyHashAlgorithm hashAlgorithm) : base(context, null) + { + _cache = new Cache(this, assemblyName, assemblyFlags, publicKeyArray, version, hashAlgorithm); + TryGetHandle = _cache.CreateCacheFunc(GetHandleForTypeSystemEntity); + TryGetStringHandle = _cache.CreateCacheFunc(GetUserStringHandle); + TryGetAssemblyRefHandle = _cache.CreateCacheFunc(GetAssemblyRefHandle); + } + + class DisableNewTokensException : Exception { } + + public bool DisableNewTokens; + + private int GetHandleForTypeSystemEntity(TypeSystemMetadataEmitter emitter, object type) + { + return MetadataTokens.GetToken(emitter.EmitMetadataHandleForTypeSystemEntity((TypeSystemEntity)type)); + } + + private int GetUserStringHandle(TypeSystemMetadataEmitter emitter, object str) + { + return MetadataTokens.GetToken(emitter.GetUserStringHandle((string)str)); + } + + private int GetAssemblyRefHandle(TypeSystemMetadataEmitter emitter, object name) + { + return MetadataTokens.GetToken(emitter.GetAssemblyRef((AssemblyName)name)); + } + + public Func TryGetHandle { get; } + public Func TryGetStringHandle { get; } + public Func TryGetAssemblyRefHandle { get; } + public EntityHandle? TryGetEntityHandle(TypeSystemEntity tse) + { + var handle = TryGetHandle(tse); + if (handle.HasValue) + return MetadataTokens.EntityHandle(handle.Value); + else + return null; + } + public EntityHandle? TryGetExistingEntityHandle(TypeSystemEntity tse) + { + lock (_cache) + { + if (_cache.ExistingEntities.TryGetValue(tse, out var handle)) + return MetadataTokens.EntityHandle(handle); + var blob = _cache.MetadataBlob; + if (_cache.ExistingEntities.TryGetValue(tse, out handle)) + return MetadataTokens.EntityHandle(handle); + } + + return null; + } + + Cache _cache; + + public MetadataReader MetadataReader => _cache.Reader; + public byte[] MetadataBlob => _cache.MetadataBlob; + + public int ModuleTypeSort => 1; + + public int CompareTo(IEcmaModule other) + { + if (other == this) + return 0; + + if (other is MutableModule mutableModule) + { + return CompareTo(mutableModule); + } + + return ModuleTypeSort.CompareTo(other.ModuleTypeSort); + } + + public override IEnumerable GetAllTypes() => Array.Empty(); + public override MetadataType GetGlobalModuleType() => null; + + public object GetObject(EntityHandle handle, NotFoundBehavior notFoundBehavior = NotFoundBehavior.Throw) + { + lock(_cache) + { + if (_cache.Entities.TryGetValue(MetadataTokens.GetToken(handle), out var result)) + { + return result; + } + + var reader = MetadataReader; + + if (_cache.Entities.TryGetValue(MetadataTokens.GetToken(handle), out result)) + { + return result; + } + } + + throw new ArgumentException($"Invalid EntityHandle {MetadataTokens.GetToken(handle):X} passed to MutableModule.GetObject"); + } + + public string GetUserString(UserStringHandle handle) + { + lock(_cache) + { + if (_cache.Entities.TryGetValue(MetadataTokens.GetToken(handle), out var result)) + { + return (string)result; + } + } + throw new ArgumentException("Invalid UserStringHandle passed to MutableModule.GetObject"); + } + public override object GetType(string nameSpace, string name, NotFoundBehavior notFoundBehavior) => throw new NotImplementedException(); + public TypeDesc GetType(EntityHandle handle) + { + TypeDesc type = GetObject(handle, NotFoundBehavior.Throw) as TypeDesc; + if (type == null) + ThrowHelper.ThrowBadImageFormatException($"type expected for handle {MetadataTokens.GetToken(handle):X}"); + return type; + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs index dc1bf6c9cd0c4..7b8604f30c967 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs @@ -66,7 +66,7 @@ public void TestDependencyGraphInvariants(EcmaMethod method) var context = (CompilerTypeSystemContext)method.Context; CompilationModuleGroup compilationGroup = new SingleFileCompilationModuleGroup(); - NativeAotILProvider ilProvider = new NativeAotILProvider(); + NativeAotILProvider ilProvider = new NativeAotILProvider(compilationGroup); UsageBasedMetadataManager metadataManager = new UsageBasedMetadataManager(compilationGroup, context, new FullyBlockedMetadataBlockingPolicy(), new FullyBlockedManifestResourceBlockingPolicy(), diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ManagedBinaryEmitter.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ManagedBinaryEmitter.cs deleted file mode 100644 index a705a68e903dd..0000000000000 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ManagedBinaryEmitter.cs +++ /dev/null @@ -1,386 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Linq; -using System.Reflection; -using System.Collections.Generic; -using System.Reflection.Metadata.Ecma335; -using System.Reflection.Metadata; -using System.Collections.Immutable; -using System.Reflection.PortableExecutable; -using System.IO; -using System.Diagnostics; - -using Internal.TypeSystem; -using Internal.TypeSystem.Ecma; -using ILCompiler.DependencyAnalysis; - -namespace ILCompiler -{ - public class ManagedBinaryEmitter - { - public class EmittedTypeDefinition - { - private readonly ManagedBinaryEmitter _managedEmitter; - private List _methods = new List(); - - public string Name { get; private set; } - public bool IsValueType { get; private set; } - public IReadOnlyList Methods => _methods; - - internal EmittedTypeDefinition(string name, bool isValueType, ManagedBinaryEmitter managedEmitter) - { - Name = name; - IsValueType = isValueType; - _managedEmitter = managedEmitter; - } - - public EmittedMethodDefinition EmitMethodDefinition(string name, MethodSignature signature) - { - var newMethodDef = new EmittedMethodDefinition(name, signature, _managedEmitter); - _methods.Add(newMethodDef); - return newMethodDef; - } - } - public class EmittedMethodDefinition - { - private readonly ManagedBinaryEmitter _managedEmitter; - - public string Name { get; private set; } - public MethodSignature Signature { get; private set; } - public InstructionEncoder Code { get; private set; } - - internal EmittedMethodDefinition(string name, MethodSignature signature, ManagedBinaryEmitter managedEmitter) - { - Name = name; - Signature = signature; - _managedEmitter = managedEmitter; - Code = new InstructionEncoder(new BlobBuilder()); - } - } - - private readonly TypeSystemContext _typeSystemContext; - - private MetadataBuilder _metadataBuilder; - private MethodBodyStreamEncoder _methodBodyStream; - private List _emittedTypes; - - protected MetadataBuilder Builder => _metadataBuilder; - - public ManagedBinaryEmitter(TypeSystemContext typeSystemContext, string assemblyName) - { - _typeSystemContext = typeSystemContext; - - _metadataBuilder = new MetadataBuilder(); - _methodBodyStream = new MethodBodyStreamEncoder(new BlobBuilder()); - _emittedTypes = new List(); - - _metadataBuilder.AddAssembly( - _metadataBuilder.GetOrAddString(assemblyName), - new Version(0, 0, 0, 0), - culture: default(StringHandle), - publicKey: default(BlobHandle), - flags: default(AssemblyFlags), - hashAlgorithm: AssemblyHashAlgorithm.None); - - _metadataBuilder.AddModule( - 0, - _metadataBuilder.GetOrAddString(assemblyName), - default(GuidHandle), default(GuidHandle), default(GuidHandle)); - - // Module type - _metadataBuilder.AddTypeDefinition( - default(TypeAttributes), - default(StringHandle), - _metadataBuilder.GetOrAddString(""), - baseType: default(EntityHandle), - fieldList: MetadataTokens.FieldDefinitionHandle(1), - methodList: MetadataTokens.MethodDefinitionHandle(1)); - - _signatureEmitter = new EcmaSignatureEncoder(new EntityProviderForEcmaSignature(this)); - } - - public EmittedTypeDefinition EmitTypeDefinition(string typeName, bool isValueType) - { - EmittedTypeDefinition newTypeDef = new EmittedTypeDefinition(typeName, isValueType, this); - _emittedTypes.Add(newTypeDef); - return newTypeDef; - } - - public EntityHandle EmitMetadataHandleForTypeSystemEntity(TypeSystemEntity entity) - { - switch (entity) - { - case FieldDesc field: return MakeMemberReferenceHandle(field); - case MethodDesc method: return MakeMemberReferenceHandle(method); - case TypeDesc type: return MakeTypeRefOrSpecHandle(type); - - default: - throw new NotSupportedException(); - } - } - - public BlobHandle EmitSignatureBlobForMethodSignature(MethodSignature signature) - { - return MakeSignatureHandle(signature); - } - - /// - /// Encode a type signature into a specified blob. - /// - /// Blob to encode type signature into. Must not be null - public void EncodeSignatureForType(TypeDesc type, BlobBuilder blobBuilder) - { - SignatureTypeEncoder sigEncoder = new SignatureTypeEncoder(blobBuilder); - _signatureEmitter.EncodeTypeSignature(sigEncoder, type); - } - - public void EmitOutputFile(string outputPath) - { - using (FileStream sw = new FileStream(outputPath, FileMode.Create, FileAccess.ReadWrite)) - { - EmitToStream(sw); - } - } - - public void EmitToStream(Stream stream) - { - foreach (var typeDef in _emittedTypes) - { - MethodDefinitionHandle? firstMethodHandle = null; - - foreach (var methodDef in typeDef.Methods) - { - int bodyOffset = _methodBodyStream.AddMethodBody(methodDef.Code); - - BlobHandle signature = MakeSignatureHandle(methodDef.Signature); - - MethodDefinitionHandle methodHandle = _metadataBuilder.AddMethodDefinition( - MethodAttributes.PrivateScope | MethodAttributes.Static, - MethodImplAttributes.IL | MethodImplAttributes.Managed, - _metadataBuilder.GetOrAddString(methodDef.Name), - signature, - bodyOffset, - parameterList: default(ParameterHandle)); - - if (firstMethodHandle == null) - firstMethodHandle = methodHandle; - } - - _metadataBuilder.AddTypeDefinition( - default(TypeAttributes), - default(StringHandle), - _metadataBuilder.GetOrAddString(typeDef.Name), - typeDef.IsValueType ? - MakeTypeRefHandle(_typeSystemContext.GetWellKnownType(WellKnownType.ValueType)) : - MakeTypeRefHandle(_typeSystemContext.GetWellKnownType(WellKnownType.Object)), - fieldList: MetadataTokens.FieldDefinitionHandle(1), - methodList: firstMethodHandle.Value); - } - - BlobBuilder peBlob = new BlobBuilder(); - new ManagedPEBuilder(PEHeaderBuilder.CreateLibraryHeader(), new MetadataRootBuilder(_metadataBuilder), _methodBodyStream.Builder).Serialize(peBlob); - - peBlob.WriteContentTo(stream); - - // Clear some variables to catch any caller trying to emit data after writing the output file - _emittedTypes = null; - _metadataBuilder = null; - _methodBodyStream = default(MethodBodyStreamEncoder); - } - - #region TypeSystem Entities To Handle Encoders - private Dictionary _assemblyRefHandles = new Dictionary(); - private Dictionary _typeRefOrSpecHandles = new Dictionary(); - private Dictionary _memberRefOrSpecHandles = new Dictionary(); - private Dictionary _methodSignatureHandles = new Dictionary(); - private Dictionary _fieldSignatureHandles = new Dictionary(); - - private struct EntityProviderForEcmaSignature : IEntityHandleProvider - { - private ManagedBinaryEmitter _emitter; - - public EntityProviderForEcmaSignature(ManagedBinaryEmitter emitter) - { - _emitter = emitter; - } - - public EntityHandle GetTypeDefOrRefHandleForTypeDesc(TypeDesc type) - { - return _emitter.MakeTypeRefHandle(type); - } - } - - private EcmaSignatureEncoder _signatureEmitter; - - private BlobHandle MakeSignatureHandle(MethodSignature signature) - { - BlobHandle handle; - - if (!_methodSignatureHandles.TryGetValue(signature, out handle)) - { - BlobBuilder metadataSignature = new BlobBuilder(); - - _signatureEmitter.EncodeMethodSignature(metadataSignature, signature); - - _methodSignatureHandles[signature] = handle = _metadataBuilder.GetOrAddBlob(metadataSignature); - } - - return handle; - } - - private BlobHandle MakeSignatureHandle(TypeSystemEntity methodOrField) - { - if (methodOrField is MethodDesc) - { - return MakeSignatureHandle(((MethodDesc)methodOrField).Signature); - } - else - { - BlobHandle handle; - FieldDesc field = (FieldDesc)methodOrField; - - if (!_fieldSignatureHandles.TryGetValue(field, out handle)) - { - BlobBuilder metadataSignature = new BlobBuilder(); - - SignatureTypeEncoder fieldSigEncoder = new BlobEncoder(metadataSignature).FieldSignature(); - _signatureEmitter.EncodeTypeSignature(fieldSigEncoder, field.FieldType); - - _fieldSignatureHandles[field] = handle = _metadataBuilder.GetOrAddBlob(metadataSignature); - } - - return handle; - } - } - - private AssemblyReferenceHandle MakeAssemblyReferenceHandle(IAssemblyDesc assemblyRef) - { - AssemblyReferenceHandle handle; - - if (!_assemblyRefHandles.TryGetValue(assemblyRef, out handle)) - { - AssemblyName assemblyName = assemblyRef.GetName(); - - handle = _metadataBuilder.AddAssemblyReference( - _metadataBuilder.GetOrAddString(assemblyName.Name), - assemblyName.Version, - default(StringHandle), - _metadataBuilder.GetOrAddBlob(ImmutableArray.Create(assemblyName.GetPublicKeyToken())), - default(AssemblyFlags), - default(BlobHandle)); - - _assemblyRefHandles[assemblyRef] = handle; - } - - return handle; - } - - private EntityHandle MakeTypeRefHandle(TypeDesc type) - { - Debug.Assert(type.IsTypeDefinition); - Debug.Assert(type is MetadataType); - - EntityHandle handle; - - if (!_typeRefOrSpecHandles.TryGetValue(type, out handle)) - { - EntityHandle scope; - MetadataType typeAsMetadataType = (MetadataType)type; - - if (typeAsMetadataType.ContainingType != null) - scope = MakeTypeRefHandle(typeAsMetadataType.ContainingType); - else - scope = MakeAssemblyReferenceHandle((IAssemblyDesc)typeAsMetadataType.Module); - - handle = _metadataBuilder.AddTypeReference( - scope, - _metadataBuilder.GetOrAddString(typeAsMetadataType.Namespace), - _metadataBuilder.GetOrAddString(typeAsMetadataType.Name)); - - _typeRefOrSpecHandles[type] = handle; - } - - return handle; - } - - private EntityHandle MakeTypeRefOrSpecHandle(TypeDesc type) - { - EntityHandle handle; - - if (!_typeRefOrSpecHandles.TryGetValue(type, out handle)) - { - if(!type.IsDefType || !type.IsTypeDefinition || type is RuntimeDeterminedType) - { - SignatureTypeEncoder sigEncoder = new SignatureTypeEncoder(new BlobBuilder()); - _signatureEmitter.EncodeTypeSignature(sigEncoder, type); - handle = _metadataBuilder.AddTypeSpecification(_metadataBuilder.GetOrAddBlob(sigEncoder.Builder)); - } - else - { - handle = MakeTypeRefHandle(type); - } - - _typeRefOrSpecHandles[type] = handle; - } - - return handle; - } - - private EntityHandle MakeMemberReferenceHandle(TypeSystemEntity methodOrField) - { - EntityHandle handle; - - if (!_memberRefOrSpecHandles.TryGetValue(methodOrField, out handle)) - { - MethodDesc method = methodOrField as MethodDesc; - FieldDesc field = methodOrField as FieldDesc; - TypeDesc owningType = (method != null ? method.OwningType : field.OwningType); - string name = (method != null ? method.Name : field.Name); - - BlobHandle signature = method != null ? - MakeSignatureHandle(method.GetTypicalMethodDefinition()) : - MakeSignatureHandle(field); - - handle = _metadataBuilder.AddMemberReference( - MakeTypeRefOrSpecHandle(owningType), - _metadataBuilder.GetOrAddString(name), - signature); - - if (method != null && method.HasInstantiation && !method.IsTypicalMethodDefinition) - { - BlobEncoder methodSpecEncoder = new BlobEncoder(new BlobBuilder()); - - GenericTypeArgumentsEncoder argEncoder = methodSpecEncoder.MethodSpecificationSignature(method.Instantiation.Length); - for (int i = 0; i < method.Instantiation.Length; i++) - { - SignatureTypeEncoder argTypeEncoder = argEncoder.AddArgument(); - _signatureEmitter.EncodeTypeSignature(argTypeEncoder, method.Instantiation[i]); - } - - handle = _metadataBuilder.AddMethodSpecification(handle, _metadataBuilder.GetOrAddBlob(methodSpecEncoder.Builder)); - } - - _memberRefOrSpecHandles[methodOrField] = handle; - } - - return handle; - } - #endregion - } - - public static class InstructionEncoderExtensions - { - public static void EmitLdToken(this InstructionEncoder code, TypeSystemEntity typeSystemEntity, ManagedBinaryEmitter emitter) - { - code.OpCode(ILOpCode.Ldtoken); - code.Token(emitter.EmitMetadataHandleForTypeSystemEntity(typeSystemEntity)); - } - public static void EmitI4Constant(this InstructionEncoder code, int value) - { - code.OpCode(ILOpCode.Ldc_i4); - code.CodeBuilder.WriteInt32(value); - } - } -} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 6f19f9f7acc73..9a41428a91199 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -194,9 +194,6 @@ Common\Utf8StringBuilder.cs - - Ecma\EcmaSignatureEncoder.cs - IL\NativeAotILProvider.cs @@ -518,7 +515,6 @@ - diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/CompilationModuleGroup.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/CompilationModuleGroup.ReadyToRun.cs index 6ef8c57322d6f..5c5cd55260d0b 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/CompilationModuleGroup.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/CompilationModuleGroup.ReadyToRun.cs @@ -6,6 +6,7 @@ using Internal.ReadyToRunConstants; using Internal.TypeSystem.Ecma; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; namespace ILCompiler @@ -41,6 +42,23 @@ partial class CompilationModuleGroup /// True if the given method versions with the current compilation module group public virtual bool VersionsWithMethodBody(MethodDesc methodDesc) => ContainsMethodBody(methodDesc, unboxingStub: false); + /// + /// Returns true when a given method does not belong to the same version bubble as the compilation module group, + /// but we can support inlining it into other modules. + /// + /// Method to check + /// True if the given method versions with the current compilation module group + public virtual bool CrossModuleInlineable(MethodDesc methodDesc) => false; + + + /// + /// Returns true when a given method does not belong to the same version bubble as the compilation module group, + /// but we can support compiling it into the modules being compiled + /// + /// Method to check + /// True if the given method versions with the current compilation module group + public virtual bool CrossModuleCompileable(MethodDesc methodDesc) => false; + /// /// Returns true when a given module belongs to the same version bubble as the compilation module group. /// @@ -73,8 +91,9 @@ partial class CompilationModuleGroup /// When set to true, unconditionally add module overrides to all signatures. This is needed in composite /// build mode so that import cells and instance entry point table are caller module-agnostic. /// - public bool EnforceOwningType(EcmaModule module) + public bool EnforceOwningType(IEcmaModule module) { + Debug.Assert(VersionsWithModule((ModuleDesc)module) || module is MutableModule); return IsCompositeBuildMode || module != CompilationModuleSet.Single(); } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/FieldFixupSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/FieldFixupSignature.cs index f993b381d8cea..4cf38ae5ee601 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/FieldFixupSignature.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/FieldFixupSignature.cs @@ -26,7 +26,6 @@ public FieldFixupSignature(ReadyToRunFixupKind fixupKind, FieldDesc fieldDesc, N // Ensure types in signature are loadable and resolvable, otherwise we'll fail later while emitting the signature ((CompilerTypeSystemContext)fieldDesc.Context).EnsureLoadableType(fieldDesc.OwningType); - Debug.Assert(factory.SignatureContext.GetTargetModule(_fieldDesc) != null); } public override int ClassCode => 271828182; @@ -39,7 +38,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { dataBuilder.AddSymbol(this); - EcmaModule targetModule = factory.SignatureContext.GetTargetModule(_fieldDesc); + IEcmaModule targetModule = factory.SignatureContext.GetTargetModule(_fieldDesc); SignatureContext innerContext = dataBuilder.EmitFixup(factory, _fixupKind, targetModule, factory.SignatureContext); uint baseOffset = 0; uint fieldOffset = (uint)_fieldDesc.Offset.AsInt; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/GenericLookupSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/GenericLookupSignature.cs index 740c27932c238..eae25a978cb17 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/GenericLookupSignature.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/GenericLookupSignature.cs @@ -53,7 +53,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) } // Determine the need for module override - EcmaModule targetModule; + IEcmaModule targetModule; if (_methodArgument != null) { targetModule = _methodArgument.Token.Module; @@ -130,6 +130,16 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) return dataBuilder.ToObjectData(); } + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + DependencyList dependencies = null; + if (_fixupKind == ReadyToRunFixupKind.TypeHandle) + { + TypeFixupSignature.AddDependenciesForAsyncStateMachineBox(ref dependencies, factory, _typeArgument); + } + return dependencies; + } + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { sb.Append(nameMangler.CompilationUnitPrefix); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ILBodyFixupSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ILBodyFixupSignature.cs new file mode 100644 index 0000000000000..0533892925085 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ILBodyFixupSignature.cs @@ -0,0 +1,108 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +using Internal.Text; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; +using Internal.TypeSystem.Interop; +using Internal.ReadyToRunConstants; +using Internal.CorConstants; +using Internal.JitInterface; + +namespace ILCompiler.DependencyAnalysis.ReadyToRun +{ + public class ILBodyFixupSignature : Signature, IEquatable + { + private readonly ReadyToRunFixupKind _fixupKind; + private readonly EcmaMethod _method; + + public ILBodyFixupSignature(ReadyToRunFixupKind fixupKind, EcmaMethod ecmaMethod) + { + _fixupKind = fixupKind; + _method = ecmaMethod; + } + + public override int ClassCode => 308579267; + + protected override void OnMarked(NodeFactory context) + { + context.AddMarkedILBodyFixupSignature(this); + } + + public static void NotifyComplete(NodeFactory factory, List completeListOfSigs) + { + completeListOfSigs.MergeSort(new ObjectNodeComparer(CompilerComparer.Instance)); + foreach (var ilbodyFixupSig in completeListOfSigs) + { + ilbodyFixupSig.GetModuleToken(factory); + } + } + + private ModuleToken GetModuleToken(NodeFactory factory) + { + return new ModuleToken(factory.ManifestMetadataTable._mutableModule, factory.ManifestMetadataTable._mutableModule.TryGetEntityHandle(_method.GetTypicalMethodDefinition()).Value); + } + + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) + { + ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder(); + + if (!relocsOnly) + { + dataBuilder.AddSymbol(this); + + ModuleToken moduleToken = GetModuleToken(factory); + + IEcmaModule targetModule = moduleToken.Module; + SignatureContext innerContext = dataBuilder.EmitFixup(factory, _fixupKind, targetModule, factory.SignatureContext); + + var metadata = ReadyToRunStandaloneMethodMetadata.Compute(_method); + dataBuilder.EmitUInt(checked((uint)metadata.ConstantData.Length)); + dataBuilder.EmitBytes(metadata.ConstantData); + dataBuilder.EmitUInt(checked((uint)metadata.TypeRefs.Length)); + foreach (var typeRef in metadata.TypeRefs) + { + if (factory.SignatureContext.Resolver.GetModuleTokenForType((EcmaType)typeRef, allowDynamicallyCreatedReference: true, throwIfNotFound: false).Module == null) + { + // If there isn't a module token yet for this type, force it to exist + factory.ManifestMetadataTable._mutableModule.TryGetEntityHandle(typeRef); + } + dataBuilder.EmitTypeSignature(typeRef, innerContext); + } + + MethodWithToken method = new MethodWithToken(_method, moduleToken, null, unboxing: false, context: null); + dataBuilder.EmitMethodSignature(method, enforceDefEncoding: false, enforceOwningType: false, innerContext, false); + } + + return dataBuilder.ToObjectData(); + } + + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) + { + sb.Append(nameMangler.CompilationUnitPrefix); + sb.Append($@"ILBodyFixupSignature({_fixupKind.ToString()}): "); + sb.Append(nameMangler.GetMangledMethodName(_method)); + } + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + { + ILBodyFixupSignature otherNode = (ILBodyFixupSignature)other; + int result = ((int)_fixupKind).CompareTo((int)otherNode._fixupKind); + if (result != 0) + return result; + + return comparer.Compare(_method, otherNode._method); + } + + public override string ToString() + { + return $"ILBodyFixupSignature {_fixupKind} {_method}"; + } + + public bool Equals(ILBodyFixupSignature other) => object.ReferenceEquals(other, this); + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ImportSectionNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ImportSectionNode.cs index 482ad122daf8a..b0020b0ec001c 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ImportSectionNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ImportSectionNode.cs @@ -54,6 +54,8 @@ public void MaterializeSignature(NodeFactory r2rFactory) { if (!_materializedSignature) { + _signatureList.MergeSortAllowDuplicates(new SortableDependencyNode.ObjectNodeComparer(CompilerComparer.Instance)); + foreach (Signature signature in _signatureList) { signature.GetData(r2rFactory, relocsOnly: false); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InliningInfoNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InliningInfoNode.cs index 65e1f5a389a3c..da6242013b52b 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InliningInfoNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InliningInfoNode.cs @@ -8,6 +8,7 @@ using System.Reflection.Metadata.Ecma335; using Internal.NativeFormat; +using Internal.ReadyToRunConstants; using Internal.Text; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; @@ -19,21 +20,54 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun /// public class InliningInfoNode : HeaderTableNode { + public enum InfoType + { + InliningInfo2, + CrossModuleInliningForCrossModuleDataOnly, + CrossModuleAllMethods + } + private readonly EcmaModule _module; + private readonly InfoType _inlineInfoType; + private ReadyToRunSymbolNodeFactory _symbolNodeFactory; - public InliningInfoNode(TargetDetails target, EcmaModule module) + public InliningInfoNode(TargetDetails target, EcmaModule module, InfoType inlineInfoType) : base(target) { + _inlineInfoType = inlineInfoType; + if (AllowCrossModuleInlines) + { + Debug.Assert(module == null); // Cross module inlining always covers all modules + } + else + { + Debug.Assert(module != null); // InliningInfo2 is restricted to a single module at a time + } _module = module; } + public void Initialize(ReadyToRunSymbolNodeFactory symbolNodeFactory) + { + _symbolNodeFactory = symbolNodeFactory; + } + public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) { sb.Append(nameMangler.CompilationUnitPrefix); - sb.Append("__ReadyToRunInliningInfoTable__"); - sb.Append(_module.Assembly.GetName().Name); + if (_module != null) + { + sb.Append("__ReadyToRunInliningInfoTable__"); + sb.Append(_module.Assembly.GetName().Name); + } + else + { + sb.Append("__ReadyToRunCrossModuleInliningInfoTable__"); + } } + private bool AllowCrossModuleInlines => _inlineInfoType == InfoType.CrossModuleAllMethods || _inlineInfoType == InfoType.CrossModuleInliningForCrossModuleDataOnly; + private bool ReportAllInlinesInSearch => _inlineInfoType == InfoType.CrossModuleAllMethods; + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { // This node does not trigger generation of other nodes. @@ -50,8 +84,16 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) MethodDesc inliner = methodNode.Method; EcmaMethod inlinerDefinition = (EcmaMethod)inliner.GetTypicalMethodDefinition(); - // Only encode inlining info for inliners within the active module - Debug.Assert(inlinerDefinition.Module == _module); + if (inlinerDefinition.IsNonVersionable()) + { + // Non-versionable methods don't need to be reported + continue; + } + + // Only encode inlining info for inliners within the active module, or if cross module inline format is in use + Debug.Assert(AllowCrossModuleInlines || (inlinerDefinition.Module == _module)); + + bool inlinerReportAllVersionsWithInlinee = !AllowCrossModuleInlines || factory.CompilationModuleGroup.CrossModuleCompileable(inlinerDefinition); foreach (MethodDesc inlinee in inlinees) { @@ -63,15 +105,34 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) continue; } - if (!factory.CompilationModuleGroup.VersionsWithMethodBody(inlinee)) + if (inlinee.IsNonVersionable()) { - // We cannot record inlining info across version bubble as cross-bubble assemblies - // are not guaranteed to preserve token values. Only non-versionable methods may be - // inlined across the version bubble. - Debug.Assert(inlinee.IsNonVersionable()); + // Non-versionable methods don't need to be reported continue; } + if (ReportAllInlinesInSearch) + { + // We'll definitely track this inline + } + else if (factory.CompilationModuleGroup.VersionsWithMethodBody(inlineeDefinition)) + { + if (!inlinerReportAllVersionsWithInlinee) + { + // We'll won't report this method + continue; + } + } + else + { + Debug.Assert(factory.CompilationModuleGroup.CrossModuleInlineable(inlineeDefinition)); + if (_inlineInfoType != InfoType.CrossModuleInliningForCrossModuleDataOnly) + { + // We'll won't report this method + continue; + } + } + if (!inlineeToInliners.TryGetValue(ecmaInlineeDefinition, out HashSet inliners)) { inliners = new HashSet(); @@ -92,54 +153,217 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { EcmaMethod inlinee = inlineeWithInliners.Key; int inlineeRid = MetadataTokens.GetRowNumber(inlinee.Handle); - int hashCode = ReadyToRunHashCode.ModuleNameHashCode(inlinee.Module); - hashCode ^= inlineeRid; - - // Format of the sequence: - // Inlinee RID with flag in the lowest bit - // - if flag is set, followed by module ID - // Followed by inliner RIDs deltas with flag in the lowest bit - // - if flag is set, followed by module ID + int hashCode; + + if (AllowCrossModuleInlines) + { + // CrossModuleInlineInfo format + hashCode = ReadyToRunHashCode.MethodHashCode(inlinee); + } + else + { + // InliningInfo2 format + hashCode = ReadyToRunHashCode.ModuleNameHashCode(inlinee.Module); + hashCode ^= inlineeRid; + } var sig = new VertexSequence(); - bool isForeignInlinee = inlinee.Module != _module; - sig.Append(new UnsignedConstant((uint)(inlineeRid << 1 | (isForeignInlinee ? 1 : 0)))); - if (isForeignInlinee) + if (!AllowCrossModuleInlines) { - sig.Append(new UnsignedConstant((uint)factory.ManifestMetadataTable.ModuleToIndex(inlinee.Module))); - } + // Format of the sequence: + // FOR InliningInfo2 table format + // Inlinee RID with flag in the lowest bit + // - if flag is set, followed by module ID + // Followed by inliner RIDs deltas with flag in the lowest bit + // - if flag is set, followed by module ID + Debug.Assert(_module != null); + bool isForeignInlinee = inlinee.Module != _module; + sig.Append(new UnsignedConstant((uint)(inlineeRid << 1 | (isForeignInlinee ? 1 : 0)))); + if (isForeignInlinee) + { + sig.Append(new UnsignedConstant((uint)factory.ManifestMetadataTable.ModuleToIndex(inlinee.Module))); + } - List sortedInliners = new List(inlineeWithInliners.Value); - sortedInliners.MergeSort((a, b) => - { - if (a == b) - return 0; - - int aRid = MetadataTokens.GetRowNumber(a.Handle); - int bRid = MetadataTokens.GetRowNumber(b.Handle); - if (aRid < bRid) - return -1; - else if (aRid > bRid) - return 1; - - int result = a.Module.CompareTo(b.Module); - Debug.Assert(result != 0); - return result; - }); - - int baseRid = 0; - foreach (EcmaMethod inliner in sortedInliners) + List sortedInliners = new List(inlineeWithInliners.Value); + sortedInliners.MergeSort((a, b) => + { + if (a == b) + return 0; + + int aRid = MetadataTokens.GetRowNumber(a.Handle); + int bRid = MetadataTokens.GetRowNumber(b.Handle); + if (aRid < bRid) + return -1; + else if (aRid > bRid) + return 1; + + int result = a.Module.CompareTo(b.Module); + Debug.Assert(result != 0); + return result; + }); + + int baseRid = 0; + foreach (EcmaMethod inliner in sortedInliners) + { + int inlinerRid = MetadataTokens.GetRowNumber(inliner.Handle); + int ridDelta = inlinerRid - baseRid; + baseRid = inlinerRid; + Debug.Assert(ridDelta >= 0); + bool isForeignInliner = inliner.Module != _module; + sig.Append(new UnsignedConstant((uint)(ridDelta << 1 | (isForeignInliner ? 1 : 0)))); + if (isForeignInliner) + { + sig.Append(new UnsignedConstant((uint)factory.ManifestMetadataTable.ModuleToIndex(inliner.Module))); + } + } + } + else { - int inlinerRid = MetadataTokens.GetRowNumber(inliner.Handle); - int ridDelta = inlinerRid - baseRid; - baseRid = inlinerRid; - Debug.Assert(ridDelta >= 0); - bool isForeignInliner = inliner.Module != _module; - sig.Append(new UnsignedConstant((uint)(ridDelta << 1 | (isForeignInliner ? 1 : 0)))); - if (isForeignInliner) + // Format of the sequence: + // FOR CrossModuleInlineInfo format + // Index with 2 flags field in lowest 2 bits to define the inlinee + // - If flags & 1 == 0 then index is a MethodDef RID, and if the module is a composite image, a module index of the method follows + // - If flags & 1 == 1, then index is an index into the ILBody import section + // - If flags & 2 == 0 then what follows is: + // - Inliner RID deltas - See definition below + // - if flags & 2 == 2 then what follows is: + // - count of delta encoded indices into the ILBody import section + // - the sequence of delta encoded indices into the ILBody import section + // - Inliner RID deltas - See definition below + // + // Inliner RID deltas (for multi-module version bubble images (specified by the module having the READYTORUN_FLAG_MULTIMODULE_VERSION_BUBBLE flag set) + // - a sequence of inliner RID deltas with flag in the lowest bit + // - if flag is set, the inliner RID is followed by a module ID + // - otherwise the module is the same as the module of the inlinee method + // + // Inliner RID deltas (for single module version bubble images) + // - a sequence of inliner RID deltas + + bool crossModuleMultiModuleFormat = (factory.CompilationModuleGroup.GetReadyToRunFlags() & ReadyToRunFlags.READYTORUN_FLAG_MultiModuleVersionBubble) != 0; + + Debug.Assert(_module == null); + bool isCrossModuleInlinee = !factory.CompilationModuleGroup.VersionsWithMethodBody(inlinee); + Debug.Assert(!isCrossModuleInlinee || factory.CompilationModuleGroup.CrossModuleInlineable(inlinee)); + + EcmaMethod[] sortedInliners = new EcmaMethod[inlineeWithInliners.Value.Count]; + inlineeWithInliners.Value.CopyTo(sortedInliners); + + sortedInliners.MergeSort((a, b) => + { + if (a == b) + return 0; + + bool isCrossModuleInlinerA = !factory.CompilationModuleGroup.VersionsWithMethodBody(a); + bool isCrossModuleInlinerB = !factory.CompilationModuleGroup.VersionsWithMethodBody(b); + if (isCrossModuleInlinerA != isCrossModuleInlinerB) + { + if (isCrossModuleInlinerA) + return -1; + else + return 1; + } + + int result; + if (isCrossModuleInlinerA) + { + int indexA = _symbolNodeFactory.CheckILBodyFixupSignature(a).IndexFromBeginningOfArray; + int indexB = _symbolNodeFactory.CheckILBodyFixupSignature(b).IndexFromBeginningOfArray; + Debug.Assert(indexA != indexB); + result = indexA.CompareTo(indexB); + } + else + { + int aRid = MetadataTokens.GetRowNumber(a.Handle); + int bRid = MetadataTokens.GetRowNumber(b.Handle); + if (aRid < bRid) + return -1; + else if (aRid > bRid) + return 1; + + result = a.Module.CompareTo(b.Module); + } + Debug.Assert(result != 0); + return result; + }); + + uint crossModuleInlinerCount = 0; + foreach (var method in sortedInliners) + { + if (factory.CompilationModuleGroup.VersionsWithMethodBody(method)) + break; + + Debug.Assert(factory.CompilationModuleGroup.CrossModuleInlineable(method)); + crossModuleInlinerCount++; + } + + uint encodedInlinee; + checked { - sig.Append(new UnsignedConstant((uint)factory.ManifestMetadataTable.ModuleToIndex(inliner.Module))); + uint indexOfInlinee; + if (isCrossModuleInlinee) + { + indexOfInlinee = (uint)_symbolNodeFactory.CheckILBodyFixupSignature(inlinee).IndexFromBeginningOfArray; + } + else + { + indexOfInlinee = (uint)MetadataTokens.GetRowNumber(inlinee.Handle); + } + + encodedInlinee = indexOfInlinee << (int)ReadyToRunCrossModuleInlineFlags.CrossModuleInlinerIndexShift; + + if (isCrossModuleInlinee) + encodedInlinee |= (uint)ReadyToRunCrossModuleInlineFlags.CrossModuleInlinee; + + if (crossModuleInlinerCount > 0) + encodedInlinee |= (uint)ReadyToRunCrossModuleInlineFlags.HasCrossModuleInliners; + + sig.Append(new UnsignedConstant(encodedInlinee)); + if (crossModuleMultiModuleFormat && !isCrossModuleInlinee) + sig.Append(new UnsignedConstant((uint)factory.ManifestMetadataTable.ModuleToIndex(inlinee.Module))); + + int inlinerIndex = 0; + if (crossModuleInlinerCount > 0) + { + sig.Append(new UnsignedConstant(crossModuleInlinerCount)); + uint baseIndex = 0; + for (; inlinerIndex < crossModuleInlinerCount; inlinerIndex++) + { + var inliner = sortedInliners[inlinerIndex]; + + uint ilBodyIndex = (uint)_symbolNodeFactory.CheckILBodyFixupSignature(inliner).IndexFromBeginningOfArray; + uint ridDelta = ilBodyIndex - baseIndex; + sig.Append(new UnsignedConstant(ridDelta)); + } + } + + uint baseRid = 0; + for (; inlinerIndex < sortedInliners.Length; inlinerIndex++) + { + var inliner = sortedInliners[inlinerIndex]; + uint inlinerRid = (uint)MetadataTokens.GetRowNumber(inliner.Handle); + uint ridDelta = inlinerRid - baseRid; + baseRid = inlinerRid; + bool isForeignInliner = inliner.Module != inlinee.Module; + Debug.Assert(!isForeignInliner || crossModuleMultiModuleFormat); + + if (crossModuleMultiModuleFormat) + { + uint encodedRid = ridDelta << (int)ReadyToRunCrossModuleInlineFlags.InlinerRidShift; + if (isForeignInliner) + encodedRid |= (uint)ReadyToRunCrossModuleInlineFlags.InlinerRidHasModule; + + sig.Append(new UnsignedConstant(encodedRid)); + if (isForeignInliner) + { + sig.Append(new UnsignedConstant((uint)factory.ManifestMetadataTable.ModuleToIndex(inliner.Module))); + } + } + else + { + sig.Append(new UnsignedConstant(ridDelta)); + } + } } } @@ -159,6 +383,16 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) { InliningInfoNode otherInliningInfo = (InliningInfoNode)other; + + if (_module == null) + { + Debug.Assert(otherInliningInfo._module != null); + return -1; + } + else if (otherInliningInfo._module == null) + { + return 1; + } return _module.Assembly.GetName().Name.CompareTo(otherInliningInfo._module.Assembly.GetName().Name); } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InstanceEntryPointTableNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InstanceEntryPointTableNode.cs index c63e589328b30..0ffe9f9b15a29 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InstanceEntryPointTableNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InstanceEntryPointTableNode.cs @@ -51,22 +51,37 @@ public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilde sb.Append("__ReadyToRunInstanceEntryPointTable"); } - private ArraySignatureBuilder BuildSignatureForMethod(MethodWithGCInfo method, NodeFactory factory) + public static byte[] BuildSignatureForMethodDefinedInModule(MethodDesc method, NodeFactory factory) { - // In composite R2R format, always enforce owning type to let us share generic instantiations among modules + EcmaMethod typicalMethod = (EcmaMethod)method.GetTypicalMethodDefinition(); - EcmaMethod typicalMethod = (EcmaMethod)method.Method.GetTypicalMethodDefinition(); - ModuleToken moduleToken = new ModuleToken(typicalMethod.Module, typicalMethod.Handle); + ModuleToken moduleToken; + if (factory.CompilationModuleGroup.VersionsWithMethodBody(typicalMethod)) + { + moduleToken = new ModuleToken(typicalMethod.Module, typicalMethod.Handle); + } + else + { + MutableModule manifestMetadata = factory.ManifestMetadataTable._mutableModule; + var handle = manifestMetadata.TryGetExistingEntityHandle(method.GetTypicalMethodDefinition()); + Debug.Assert(handle.HasValue); + moduleToken = new ModuleToken(factory.ManifestMetadataTable._mutableModule, handle.Value); + } ArraySignatureBuilder signatureBuilder = new ArraySignatureBuilder(); signatureBuilder.EmitMethodSignature( - new MethodWithToken(method.Method, moduleToken, constrainedType: null, unboxing: false, context: null), + new MethodWithToken(method, moduleToken, constrainedType: null, unboxing: false, context: null), enforceDefEncoding: true, - enforceOwningType: _factory.CompilationModuleGroup.EnforceOwningType(moduleToken.Module), + enforceOwningType: moduleToken.Module is EcmaModule ? factory.CompilationModuleGroup.EnforceOwningType((EcmaModule)moduleToken.Module) : true, factory.SignatureContext, isInstantiatingStub: false); - return signatureBuilder; + return signatureBuilder.ToArray(); + } + + private byte[] BuildSignatureForMethod(MethodWithGCInfo method, NodeFactory factory) + { + return BuildSignatureForMethodDefinedInModule(method.Method, factory); } public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) @@ -91,8 +106,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) int methodIndex = factory.RuntimeFunctionsTable.GetIndex(method); - ArraySignatureBuilder signatureBuilder = BuildSignatureForMethod(method, factory); - byte[] signature = signatureBuilder.ToArray(); + byte[] signature = BuildSignatureForMethod(method, factory); BlobVertex signatureBlob; if (!uniqueSignatures.TryGetValue(signature, out signatureBlob)) { diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InstrumentationDataTableNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InstrumentationDataTableNode.cs index c73888a49c357..dfa5497c6fb0d 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InstrumentationDataTableNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/InstrumentationDataTableNode.cs @@ -222,18 +222,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) pgoEmitter.Clear(); PgoProcessor.EncodePgoData(CorInfoImpl.ConvertTypeHandleHistogramsToCompactTypeHistogramFormat(_profileDataManager[method].SchemaData, factory.CompilationModuleGroup), pgoEmitter, false); - // In composite R2R format, always enforce owning type to let us share generic instantiations among modules - EcmaMethod typicalMethod = (EcmaMethod)method.GetTypicalMethodDefinition(); - ModuleToken moduleToken = new ModuleToken(typicalMethod.Module, typicalMethod.Handle); - - ArraySignatureBuilder signatureBuilder = new ArraySignatureBuilder(); - signatureBuilder.EmitMethodSignature( - new MethodWithToken(method, moduleToken, constrainedType: null, unboxing: false, context: null), - enforceDefEncoding: true, - enforceOwningType: _factory.CompilationModuleGroup.EnforceOwningType(moduleToken.Module), - factory.SignatureContext, - isInstantiatingStub: false); - byte[] signature = signatureBuilder.ToArray(); + byte[] signature = InstanceEntryPointTableNode.BuildSignatureForMethodDefinedInModule(method, factory); BlobVertex signatureBlob = new BlobVertex(signature); byte[] encodedInstrumentationData = pgoEmitter.ToByteArray(); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs index ca6b4fc84d3a7..e6a1a3e8ec806 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ManifestMetadataTableNode.cs @@ -79,6 +79,8 @@ public class ManifestMetadataTableNode : HeaderTableNode /// private readonly NodeFactory _nodeFactory; + public readonly MutableModule _mutableModule; + public ManifestMetadataTableNode(NodeFactory nodeFactory) : base(nodeFactory.Target) { @@ -87,12 +89,34 @@ public ManifestMetadataTableNode(NodeFactory nodeFactory) _manifestAssemblyMvids = new List(); _signatureEmitters = new List(); _nodeFactory = nodeFactory; - _nextModuleId = 1; + _nextModuleId = 2; + + AssemblyHashAlgorithm hashAlgorithm = AssemblyHashAlgorithm.None; + byte[] publicKeyBlob = null; + AssemblyFlags manifestAssemblyFlags = default(AssemblyFlags); + Version manifestAssemblyVersion = new Version(0, 0, 0, 0); + + if ((nodeFactory.CompositeImageSettings != null) && nodeFactory.CompilationModuleGroup.IsCompositeBuildMode) + { + if (nodeFactory.CompositeImageSettings.PublicKey != null) + { + hashAlgorithm = AssemblyHashAlgorithm.Sha1; + publicKeyBlob = nodeFactory.CompositeImageSettings.PublicKey.ToArray(); + manifestAssemblyFlags |= AssemblyFlags.PublicKey; + } + + if (nodeFactory.CompositeImageSettings.AssemblyVersion != null) + { + manifestAssemblyVersion = nodeFactory.CompositeImageSettings.AssemblyVersion; + } + } + + _mutableModule = new MutableModule(nodeFactory.TypeSystemContext, "ManifestMetadata", manifestAssemblyFlags, publicKeyBlob, manifestAssemblyVersion, hashAlgorithm); if (!_nodeFactory.CompilationModuleGroup.IsCompositeBuildMode) { MetadataReader mdReader = _nodeFactory.CompilationModuleGroup.CompilationModuleSet.Single().MetadataReader; - _assemblyRefCount = mdReader.GetTableRowCount(TableIndex.AssemblyRef) + 1; + _assemblyRefCount = mdReader.GetTableRowCount(TableIndex.AssemblyRef); if (!_nodeFactory.CompilationModuleGroup.IsInputBubble) { @@ -105,14 +129,15 @@ public ManifestMetadataTableNode(NodeFactory nodeFactory) } } - // AssemblyRefCount + 1 corresponds to ROWID 0 in the manifest metadata + // AssemblyRefCount + 1 corresponds to rid 0 in the manifest metadata which indicates to use the manifest metadata itself + // AssemblyRefCount + 2 corresponds to ROWID 1 in the manifest metadata _nextModuleId += _assemblyRefCount; } if (_nodeFactory.CompilationModuleGroup.IsCompositeBuildMode) { // Fill in entries for all input modules right away to make sure they have parallel indices - int nextExpectedId = 1; + int nextExpectedId = 2; foreach (EcmaModule inputModule in _nodeFactory.CompilationModuleGroup.CompilationModuleSet) { int acquiredId = ModuleToIndexInternal(inputModule); @@ -123,6 +148,7 @@ public ManifestMetadataTableNode(NodeFactory nodeFactory) nextExpectedId++; } } + } public void RegisterEmitter(ISignatureEmitter emitter) @@ -130,7 +156,7 @@ public void RegisterEmitter(ISignatureEmitter emitter) _signatureEmitters.Add(emitter); } - public int ModuleToIndex(EcmaModule module) + public int ModuleToIndex(IEcmaModule module) { if (!_nodeFactory.MarkingComplete) { @@ -155,9 +181,23 @@ public void EnsureModuleIndexable(ModuleDesc module) } } - private int ModuleToIndexInternal(EcmaModule module) + private int ModuleToIndexInternal(IEcmaModule module) { - AssemblyName assemblyName = module.Assembly.GetName(); + Debug.Assert(module != null); + EcmaModule emodule = module as EcmaModule; + if (emodule == null) + { + Debug.Assert(module == _mutableModule); + return _assemblyRefCount + 1; + } + + if (!_nodeFactory.CompilationModuleGroup.IsCompositeBuildMode && (_nodeFactory.CompilationModuleGroup.CompilationModuleSet.Single() == module)) + { + // Must be a reference to the only module being compiled + return 0; + } + + AssemblyName assemblyName = emodule.Assembly.GetName(); int assemblyRefIndex; if (!_assemblyRefToModuleIdMap.TryGetValue(assemblyName.Name, out assemblyRefIndex)) { @@ -165,7 +205,7 @@ private int ModuleToIndexInternal(EcmaModule module) _assemblyRefToModuleIdMap.Add(assemblyName.Name, assemblyRefIndex); } - if (assemblyRefIndex >= _assemblyRefCount && !_moduleIdToAssemblyNameMap.ContainsKey(assemblyRefIndex)) + if (assemblyRefIndex > _assemblyRefCount && !_moduleIdToAssemblyNameMap.ContainsKey(assemblyRefIndex)) { if (_emissionCompleted) { @@ -174,7 +214,7 @@ private int ModuleToIndexInternal(EcmaModule module) // If we're going to add a module to the manifest, it has to be part of the version bubble, otherwise // the verification logic would be broken at runtime. - Debug.Assert(_nodeFactory.CompilationModuleGroup.VersionsWithModule(module)); + Debug.Assert(_nodeFactory.CompilationModuleGroup.VersionsWithModule(emodule)); _moduleIdToAssemblyNameMap.Add(assemblyRefIndex, assemblyName); _manifestAssemblyMvids.Add(module.MetadataReader.GetGuid(module.MetadataReader.GetModuleDefinition().Mvid)); @@ -218,85 +258,19 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) ComputeLastSetOfModuleIndices(); - MetadataBuilder metadataBuilder = new MetadataBuilder(); - - AssemblyHashAlgorithm hashAlgorithm = AssemblyHashAlgorithm.None; - BlobHandle publicKeyBlob = default(BlobHandle); - AssemblyFlags manifestAssemblyFlags = default(AssemblyFlags); - Version manifestAssemblyVersion = new Version(0, 0, 0, 0); - - if ((factory.CompositeImageSettings != null) && factory.CompilationModuleGroup.IsCompositeBuildMode) - { - if (factory.CompositeImageSettings.PublicKey != null) - { - hashAlgorithm = AssemblyHashAlgorithm.Sha1; - publicKeyBlob = metadataBuilder.GetOrAddBlob(factory.CompositeImageSettings.PublicKey); - manifestAssemblyFlags |= AssemblyFlags.PublicKey; - } - - if (factory.CompositeImageSettings.AssemblyVersion != null) - { - manifestAssemblyVersion = factory.CompositeImageSettings.AssemblyVersion; - } - } - - string manifestMetadataAssemblyName = "ManifestMetadata"; - metadataBuilder.AddAssembly( - metadataBuilder.GetOrAddString(manifestMetadataAssemblyName), - manifestAssemblyVersion, - culture: default(StringHandle), - publicKey: publicKeyBlob, - flags: manifestAssemblyFlags, - hashAlgorithm: hashAlgorithm); - - metadataBuilder.AddModule( - 0, - metadataBuilder.GetOrAddString(manifestMetadataAssemblyName), - default(GuidHandle), default(GuidHandle), default(GuidHandle)); - - // Module type - metadataBuilder.AddTypeDefinition( - default(TypeAttributes), - default(StringHandle), - metadataBuilder.GetOrAddString(""), - baseType: default(EntityHandle), - fieldList: MetadataTokens.FieldDefinitionHandle(1), - methodList: MetadataTokens.MethodDefinitionHandle(1)); - foreach (var idAndAssemblyName in _moduleIdToAssemblyNameMap.OrderBy(x => x.Key)) { AssemblyName assemblyName = idAndAssemblyName.Value; - AssemblyFlags assemblyFlags = 0; - byte[] publicKeyOrToken; - if ((assemblyName.Flags & AssemblyNameFlags.PublicKey) != 0) - { - assemblyFlags |= AssemblyFlags.PublicKey; - publicKeyOrToken = assemblyName.GetPublicKey(); - } - else - { - publicKeyOrToken = assemblyName.GetPublicKeyToken(); - } - if ((assemblyName.Flags & AssemblyNameFlags.Retargetable) != 0) - { - assemblyFlags |= AssemblyFlags.Retargetable; - } - - AssemblyReferenceHandle newHandle = metadataBuilder.AddAssemblyReference( - name: metadataBuilder.GetOrAddString(assemblyName.Name), - version: assemblyName.Version, - culture: metadataBuilder.GetOrAddString(assemblyName.CultureName), - publicKeyOrToken: metadataBuilder.GetOrAddBlob(publicKeyOrToken), - flags: assemblyFlags, - hashValue: default(BlobHandle) /* TODO */); + var handle = _mutableModule.TryGetAssemblyRefHandle(assemblyName); + Debug.Assert(handle.HasValue); + Debug.Assert(((handle.Value & 0xFFFFFF) + (_assemblyRefCount)) == (idAndAssemblyName.Key - 1)); } - MetadataRootBuilder metadataRootBuilder = new MetadataRootBuilder(metadataBuilder); - BlobBuilder metadataBlobBuilder = new BlobBuilder(); - metadataRootBuilder.Serialize(metadataBlobBuilder, methodBodyStreamRva: 0, mappedFieldDataStreamRva: 0); + // After this point new tokens will not be embedded in the final image + _mutableModule.DisableNewTokens = true; return new ObjectData( - data: metadataBlobBuilder.ToArray(), + data: _mutableModule.MetadataBlob, relocs: Array.Empty(), alignment: 1, definedSymbols: new ISymbolDefinitionNode[] { this }); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodFixupSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodFixupSignature.cs index 1de6cfcfa9251..910168f9c99ff 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodFixupSignature.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodFixupSignature.cs @@ -46,6 +46,23 @@ public MethodFixupSignature( public bool IsUnboxingStub => _method.Unboxing; + protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory) + { + DependencyList list = base.ComputeNonRelocationBasedDependencies(factory); + if (_fixupKind == ReadyToRunFixupKind.VirtualEntry && + !Method.IsAbstract && + !Method.HasInstantiation && + Method.GetCanonMethodTarget(CanonicalFormKind.Specific) is var canonMethod && + !factory.CompilationModuleGroup.VersionsWithMethodBody(canonMethod) && + factory.CompilationModuleGroup.CrossModuleCompileable(canonMethod)) + { + list = list ?? new DependencyAnalysisFramework.DependencyNodeCore.DependencyList(); + list.Add(factory.CompiledMethodNode(canonMethod), "Virtual function dependency on cross module inlineable method"); + } + + return list; + } + public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { if (relocsOnly) @@ -87,13 +104,13 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { if (method.Token.TokenType == CorTokenType.mdtMethodSpec) { - method = new MethodWithToken(method.Method, factory.SignatureContext.GetModuleTokenForMethod(method.Method, throwIfNotFound: false), method.ConstrainedType, unboxing: _method.Unboxing, null); + method = new MethodWithToken(method.Method, factory.SignatureContext.GetModuleTokenForMethod(method.Method), method.ConstrainedType, unboxing: _method.Unboxing, null); } else if (!optimized && (method.Token.TokenType == CorTokenType.mdtMemberRef)) { if (method.Method.OwningType.GetTypeDefinition() is EcmaType) { - method = new MethodWithToken(method.Method, factory.SignatureContext.GetModuleTokenForMethod(method.Method, throwIfNotFound: false), method.ConstrainedType, unboxing: _method.Unboxing, null); + method = new MethodWithToken(method.Method, factory.SignatureContext.GetModuleTokenForMethod(method.Method), method.ConstrainedType, unboxing: _method.Unboxing, null); } } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodWithGCInfo.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodWithGCInfo.cs index cad838bfe7b1e..9a43a3e89a926 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodWithGCInfo.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/MethodWithGCInfo.cs @@ -63,11 +63,12 @@ private void RegisterInlineeModuleIndices(NodeFactory factory) continue; } - if (!factory.CompilationModuleGroup.VersionsWithMethodBody(inlinee)) + if (!factory.CompilationModuleGroup.VersionsWithMethodBody(inlinee) && !factory.CompilationModuleGroup.CrossModuleInlineable(inlinee)) { // We cannot record inlining info across version bubble as cross-bubble assemblies - // are not guaranteed to preserve token values. Only non-versionable methods may be - // inlined across the version bubble. + // are not guaranteed to preserve token values unless CrossModule inlining is in place + // Otherwise non-versionable methods may be inlined across the version bubble. + Debug.Assert(inlinee.IsNonVersionable()); continue; } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ModuleToken.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ModuleToken.cs index 484fef52a1875..557b7272543d9 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ModuleToken.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ModuleToken.cs @@ -17,21 +17,21 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun /// public struct ModuleToken : IEquatable { - public readonly EcmaModule Module; + public readonly IEcmaModule Module; public readonly mdToken Token; - public ModuleToken(EcmaModule module, mdToken token) + public ModuleToken(IEcmaModule module, mdToken token) { Module = module; Token = token; } - public ModuleToken(EcmaModule module, EntityHandle entityHandle) + public ModuleToken(IEcmaModule module, EntityHandle entityHandle) { Module = module; Token = (mdToken)MetadataTokens.GetToken(entityHandle); } - public ModuleToken(EcmaModule module, Handle handle) + public ModuleToken(IEcmaModule module, Handle handle) { Module = module; Token = (mdToken)MetadataTokens.GetToken(handle); @@ -73,11 +73,6 @@ public int CompareTo(ModuleToken other) return Module.CompareTo(other.Module); } - public SignatureContext SignatureContext(ModuleTokenResolver resolver) - { - return new SignatureContext(Module, resolver); - } - public MetadataReader MetadataReader => Module.MetadataReader; public CorTokenType TokenType => SignatureBuilder.TypeFromToken(Token); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs index d15ffc25c501c..63cc3e7b4dfd6 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ModuleTokenResolver.cs @@ -31,7 +31,9 @@ public class ModuleTokenResolver private readonly CompilationModuleGroup _compilationModuleGroup; - private Func _moduleIndexLookup; + private Func _moduleIndexLookup; + + private MutableModule _manifestMutableModule; public CompilerTypeSystemContext CompilerContext { get; } @@ -41,12 +43,17 @@ public ModuleTokenResolver(CompilationModuleGroup compilationModuleGroup, Compil CompilerContext = typeSystemContext; } - public void SetModuleIndexLookup(Func moduleIndexLookup) + public void SetModuleIndexLookup(Func moduleIndexLookup) { _moduleIndexLookup = moduleIndexLookup; } - public ModuleToken GetModuleTokenForType(EcmaType type, bool throwIfNotFound = true) + public void InitManifestMutableModule(MutableModule mutableModule) + { + _manifestMutableModule = mutableModule; + } + + public ModuleToken GetModuleTokenForType(EcmaType type, bool allowDynamicallyCreatedReference, bool throwIfNotFound = true) { if (_compilationModuleGroup.VersionsWithType(type)) { @@ -65,6 +72,17 @@ public ModuleToken GetModuleTokenForType(EcmaType type, bool throwIfNotFound = t return token; } + // If that didn't work, it may be in the manifest module used for version resilient cross module inlining + if (allowDynamicallyCreatedReference) + { + var handle = _manifestMutableModule.TryGetExistingEntityHandle(type); + + if (handle.HasValue) + { + return new ModuleToken(_manifestMutableModule, handle.Value); + } + } + // Reverse lookup failed if (throwIfNotFound) { @@ -76,14 +94,26 @@ public ModuleToken GetModuleTokenForType(EcmaType type, bool throwIfNotFound = t } } - public ModuleToken GetModuleTokenForMethod(MethodDesc method, bool throwIfNotFound = true) + public ModuleToken GetModuleTokenForMethod(MethodDesc method, bool allowDynamicallyCreatedReference, bool throwIfNotFound) { method = method.GetCanonMethodTarget(CanonicalFormKind.Specific); - if (_compilationModuleGroup.VersionsWithMethodBody(method) && - method.GetTypicalMethodDefinition() is EcmaMethod ecmaMethod) + if (method.GetTypicalMethodDefinition() is EcmaMethod ecmaMethod) { - return new ModuleToken(ecmaMethod.Module, ecmaMethod.Handle); + if (_compilationModuleGroup.VersionsWithMethodBody(method)) + { + return new ModuleToken(ecmaMethod.Module, ecmaMethod.Handle); + } + + // If that didn't work, it may be in the manifest module used for version resilient cross module inlining + if (allowDynamicallyCreatedReference) + { + var handle = _manifestMutableModule.TryGetExistingEntityHandle(ecmaMethod); + if (handle.HasValue) + { + return new ModuleToken(_manifestMutableModule, handle.Value); + } + } } // Reverse lookup failed @@ -238,7 +268,7 @@ public void AddModuleTokenForType(TypeDesc type, ModuleToken token) } } - public int GetModuleIndex(EcmaModule module) + public int GetModuleIndex(IEcmaModule module) { return _moduleIndexLookup(module); } @@ -258,9 +288,9 @@ private class TokenResolverProvider : ISignatureTypeProvider" + ImportSignature.GetMangledName(factory.NameMangler); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs index 4f0fc3ddb21c4..3f4345a31bf7f 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs @@ -330,7 +330,7 @@ public void EmitTypeSignature(TypeDesc typeDesc, SignatureContext context) } } - private void EmitModuleOverride(EcmaModule module, SignatureContext context) + private void EmitModuleOverride(IEcmaModule module, SignatureContext context) { if (module != context.LocalContext) { @@ -348,7 +348,7 @@ private void EmitTypeToken(EcmaType type, SignatureContext context) private void EmitInstantiatedTypeSignature(InstantiatedType type, SignatureContext context) { - EcmaModule targetModule = context.GetTargetModule(type); + IEcmaModule targetModule = context.GetTargetModule(type); EmitModuleOverride(targetModule, context); EmitElementType(CorElementType.ELEMENT_TYPE_GENERICINST); EmitTypeSignature(type.GetTypeDefinition(), context.InnerContext(targetModule)); @@ -470,7 +470,7 @@ private void EmitMethodSpecificationSignature(MethodWithToken method, throw new NotImplementedException(); } - if ((method.Token.Module != context.LocalContext) && !enforceOwningType) + if ((method.Token.Module != context.LocalContext) && (!enforceOwningType || (enforceDefEncoding && methodToken.TokenType == CorTokenType.mdtMemberRef))) { // If enforeOwningType is set, this is an entry for the InstanceEntryPoint or InstrumentationDataTable nodes // which are not used in quite the same way, and for which the MethodDef is always matched to the module @@ -497,10 +497,15 @@ private void EmitMethodSpecificationSignature(MethodWithToken method, { Instantiation instantiation = method.Method.Instantiation; EmitUInt((uint)instantiation.Length); - SignatureContext outerContext = context.OuterContext; + SignatureContext methodInstantiationsContext; + if ((flags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_UpdateContext) != 0) + methodInstantiationsContext = context; + else + methodInstantiationsContext = context.OuterContext; + for (int typeParamIndex = 0; typeParamIndex < instantiation.Length; typeParamIndex++) { - EmitTypeSignature(instantiation[typeParamIndex], outerContext); + EmitTypeSignature(instantiation[typeParamIndex], methodInstantiationsContext); } } } @@ -573,7 +578,7 @@ public ObjectNode.ObjectData ToObjectData() return _builder.ToObjectData(); } - public SignatureContext EmitFixup(NodeFactory factory, ReadyToRunFixupKind fixupKind, EcmaModule targetModule, SignatureContext outerContext) + public SignatureContext EmitFixup(NodeFactory factory, ReadyToRunFixupKind fixupKind, IEcmaModule targetModule, SignatureContext outerContext) { if (targetModule == outerContext.LocalContext) { diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureContext.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureContext.cs index cbaf2f0218d3d..84ac68fb24208 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureContext.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureContext.cs @@ -20,12 +20,12 @@ public class SignatureContext /// needs to encode an entity external to the context module, it muse use /// an ELEMENT_TYPE_MODULE_ZAPSIG module override. /// - public readonly EcmaModule GlobalContext; + public readonly IEcmaModule GlobalContext; /// /// Local context changes during recursive descent while encoding the signature. /// - public readonly EcmaModule LocalContext; + public readonly IEcmaModule LocalContext; /// /// Resolver used to back-translate types and fields to tokens. @@ -34,26 +34,26 @@ public class SignatureContext public SignatureContext OuterContext => new SignatureContext(GlobalContext, Resolver); - public SignatureContext(EcmaModule context, ModuleTokenResolver resolver) + public SignatureContext(IEcmaModule context, ModuleTokenResolver resolver) { GlobalContext = context; LocalContext = context; Resolver = resolver; } - private SignatureContext(EcmaModule globalContext, EcmaModule localContext, ModuleTokenResolver resolver) + private SignatureContext(IEcmaModule globalContext, IEcmaModule localContext, ModuleTokenResolver resolver) { GlobalContext = globalContext; LocalContext = localContext; Resolver = resolver; } - public SignatureContext InnerContext(EcmaModule innerContext) + public SignatureContext InnerContext(IEcmaModule innerContext) { return new SignatureContext(GlobalContext, innerContext, Resolver); } - public EcmaModule GetTargetModule(TypeDesc type) + public IEcmaModule GetTargetModule(TypeDesc type) { if (type.IsPrimitive || type.IsString || type.IsObject || type.IsWellKnownType(WellKnownType.TypedReference)) { @@ -66,19 +66,24 @@ public EcmaModule GetTargetModule(TypeDesc type) return LocalContext; } - public EcmaModule GetTargetModule(FieldDesc field) + public IEcmaModule GetTargetModule(FieldDesc field) { return GetModuleTokenForField(field).Module; } + public IEcmaModule GetTargetModule(MethodDesc method) + { + return GetModuleTokenForMethod(method).Module; + } + public ModuleToken GetModuleTokenForType(EcmaType type, bool throwIfNotFound = true) { - return Resolver.GetModuleTokenForType(type, throwIfNotFound); + return Resolver.GetModuleTokenForType(type, allowDynamicallyCreatedReference:true, throwIfNotFound: throwIfNotFound); } - public ModuleToken GetModuleTokenForMethod(MethodDesc method, bool throwIfNotFound = true) + public ModuleToken GetModuleTokenForMethod(MethodDesc method) { - return Resolver.GetModuleTokenForMethod(method, throwIfNotFound); + return Resolver.GetModuleTokenForMethod(method, throwIfNotFound: false, allowDynamicallyCreatedReference: false); } public ModuleToken GetModuleTokenForField(FieldDesc field, bool throwIfNotFound = true) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs index 48d893ad2ccfe..a579c65fdd426 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs @@ -38,7 +38,7 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) { dataBuilder.AddSymbol(this); - EcmaModule targetModule = factory.SignatureContext.GetTargetModule(_typeDesc); + IEcmaModule targetModule = factory.SignatureContext.GetTargetModule(_typeDesc); SignatureContext innerContext = dataBuilder.EmitFixup(factory, _fixupKind, targetModule, factory.SignatureContext); dataBuilder.EmitTypeSignature(_typeDesc, innerContext); @@ -140,11 +140,48 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact { DependencyList dependencies = new DependencyList(); - if (_typeDesc.HasInstantiation && !_typeDesc.IsGenericDefinition && (factory.CompilationCurrentPhase == 0)) + if (_typeDesc.HasInstantiation && + !_typeDesc.IsGenericDefinition && + (factory.CompilationCurrentPhase == 0) && + factory.CompilationModuleGroup.VersionsWithType(_typeDesc)) { dependencies.Add(factory.AllMethodsOnType(_typeDesc), "Methods on generic type instantiation"); } + + if (_fixupKind == ReadyToRunFixupKind.TypeHandle) + { + AddDependenciesForAsyncStateMachineBox(ref dependencies, factory, _typeDesc); + } return dependencies; } + + public static void AddDependenciesForAsyncStateMachineBox(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) + { + ReadyToRunCompilerContext context = (ReadyToRunCompilerContext)type.Context; + // If adding a typehandle to the AsyncStateMachineBox, pre-compile the most commonly used methods. + // As long as we haven't already reached compilation phase 7, which is an arbitrary number of phases of compilation chosen so that + // simple examples of async will get compiled + if (type.GetTypeDefinition() == context.AsyncStateMachineBoxType && !type.IsGenericDefinition && factory.CompilationCurrentPhase <= 7) + { + if (dependencies == null) + dependencies = new DependencyList(); + + // This is the async state machine box, compile the cctor, and the MoveNext method. + foreach (MethodDesc method in type.ConvertToCanonForm(CanonicalFormKind.Specific).GetAllMethods()) + { + if (!method.IsGenericMethodDefinition && + factory.CompilationModuleGroup.ContainsMethodBody(method, false)) + { + switch (method.Name) + { + case "MoveNext": + case ".cctor": + dependencies.Add(factory.CompiledMethodNode(method), $"AsyncStateMachineBox Method on type {type.ToString()}"); + break; + } + } + } + } + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs index 550b4e451adc0..c77ac2c254ec5 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs @@ -11,6 +11,7 @@ using ILCompiler.DependencyAnalysisFramework; using ILCompiler.Win32Resources; +using Internal.IL; using Internal.JitInterface; using Internal.TypeSystem; using Internal.Text; @@ -65,11 +66,20 @@ public sealed class NodeFactory public ulong ImageBase; + List _markedILBodyFixupSignatures = new List(); + public bool MarkingComplete => _markingComplete; public void SetMarkingComplete() { _markingComplete = true; + ILCompiler.DependencyAnalysis.ReadyToRun.ILBodyFixupSignature.NotifyComplete(this, _markedILBodyFixupSignatures); + _markedILBodyFixupSignatures = null; + } + + public void AddMarkedILBodyFixupSignature(ILBodyFixupSignature sig) + { + _markedILBodyFixupSignatures.Add(sig); } private NodeCache _localMethodCache; @@ -330,6 +340,7 @@ private void CreateNodeCaches() public ImportSectionsTableNode ImportSectionsTable; public InstrumentationDataTableNode InstrumentationDataTable; + public InliningInfoNode CrossModuleInlningInfo; public Import ModuleImport; @@ -351,6 +362,8 @@ private void CreateNodeCaches() public ImportSectionNode PrecodeImports; + public ImportSectionNode ILBodyPrecodeImports; + private NodeCache _constructedHelpers; public Import GetReadyToRunHelperCell(ReadyToRunHelper helperId) @@ -414,20 +427,23 @@ public IEnumerable EnumerateCompiledMethods(EcmaModule moduleT MethodDesc method = methodNode.Method; MethodWithGCInfo methodCodeNode = methodNode as MethodWithGCInfo; #if DEBUG - EcmaModule module = ((EcmaMethod)method.GetTypicalMethodDefinition()).Module; - ModuleToken moduleToken = Resolver.GetModuleTokenForMethod(method, throwIfNotFound: true); - - IMethodNode methodNodeDebug = MethodEntrypoint(new MethodWithToken(method, moduleToken, constrainedType: null, unboxing: false, context: null), false, false, false); - MethodWithGCInfo methodCodeNodeDebug = methodNodeDebug as MethodWithGCInfo; - if (methodCodeNodeDebug == null && methodNodeDebug is DelayLoadMethodImport DelayLoadMethodImport) - { - methodCodeNodeDebug = DelayLoadMethodImport.MethodCodeNode; - } - if (methodCodeNodeDebug == null && methodNodeDebug is PrecodeMethodImport precodeMethodImport) + if (!methodCodeNode.IsEmpty || CompilationModuleGroup.VersionsWithMethodBody(method)) { - methodCodeNodeDebug = precodeMethodImport.MethodCodeNode; + EcmaModule module = ((EcmaMethod)method.GetTypicalMethodDefinition()).Module; + ModuleToken moduleToken = Resolver.GetModuleTokenForMethod(method, allowDynamicallyCreatedReference: true, throwIfNotFound: true); + + IMethodNode methodNodeDebug = MethodEntrypoint(new MethodWithToken(method, moduleToken, constrainedType: null, unboxing: false, context: null), false, false, false); + MethodWithGCInfo methodCodeNodeDebug = methodNodeDebug as MethodWithGCInfo; + if (methodCodeNodeDebug == null && methodNodeDebug is DelayLoadMethodImport DelayLoadMethodImport) + { + methodCodeNodeDebug = DelayLoadMethodImport.MethodCodeNode; + } + if (methodCodeNodeDebug == null && methodNodeDebug is PrecodeMethodImport precodeMethodImport) + { + methodCodeNodeDebug = precodeMethodImport.MethodCodeNode; + } + Debug.Assert(methodCodeNodeDebug == methodCodeNode); } - Debug.Assert(methodCodeNodeDebug == methodCodeNode); #endif if (methodCodeNode != null && !methodCodeNode.IsEmpty) @@ -554,6 +570,30 @@ public VirtualResolutionFixupSignature VirtualResolutionFixupSignature(ReadyToRu return _virtualResolutionSignatures.GetOrAdd(new VirtualResolutionFixupSignatureFixupKey(fixupKind, declMethod, implType, implMethod)); } + private struct ILBodyFixupSignatureFixupKey : IEquatable + { + public readonly ReadyToRunFixupKind FixupKind; + public readonly EcmaMethod Method; + + public ILBodyFixupSignatureFixupKey(ReadyToRunFixupKind fixupKind, EcmaMethod method) + { + FixupKind = fixupKind; + Method = method; + } + public bool Equals(ILBodyFixupSignatureFixupKey other) => FixupKind == other.FixupKind && Method.Equals(other.Method); + public override bool Equals(object obj) => obj is ILBodyFixupSignatureFixupKey other && Equals(other); + public override int GetHashCode() => HashCode.Combine(FixupKind, Method); + public override string ToString() => $"'{FixupKind}' '{Method}'"; + } + + private NodeCache _ilBodySignatures = + new NodeCache((key) => new ILBodyFixupSignature(key.FixupKind, key.Method)); + + public ILBodyFixupSignature ILBodyFixupSignature(ReadyToRunFixupKind fixupKind, EcmaMethod method) + { + return _ilBodySignatures.GetOrAdd(new ILBodyFixupSignatureFixupKey(fixupKind, method)); + } + private struct ImportThunkKey : IEquatable { public readonly ReadyToRunHelper Helper; @@ -599,7 +639,7 @@ public ImportThunk ImportThunk(ReadyToRunHelper helper, ImportSectionNode contai return _importThunks.GetOrAdd(thunkKey); } - public void AttachToDependencyGraph(DependencyAnalyzerBase graph) + public void AttachToDependencyGraph(DependencyAnalyzerBase graph, ILProvider ilProvider) { graph.ComputingDependencyPhaseChange += Graph_ComputingDependencyPhaseChange; @@ -622,6 +662,8 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase graph) ManifestMetadataTable = new ManifestMetadataTableNode(this); Header.Add(Internal.Runtime.ReadyToRunSectionType.ManifestMetadata, ManifestMetadataTable, ManifestMetadataTable); Resolver.SetModuleIndexLookup(ManifestMetadataTable.ModuleToIndex); + ((ReadyToRunILProvider)ilProvider).InitManifestMutableModule(ManifestMetadataTable._mutableModule); + Resolver.InitManifestMutableModule(ManifestMetadataTable._mutableModule); ManifestAssemblyMvidHeaderNode mvidTableNode = new ManifestAssemblyMvidHeaderNode(ManifestMetadataTable); Header.Add(Internal.Runtime.ReadyToRunSectionType.ManifestAssemblyMvids, mvidTableNode, mvidTableNode); @@ -653,8 +695,11 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase graph) TypesTableNode typesTable = new TypesTableNode(Target, inputModule); tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.AvailableTypes, typesTable, typesTable); - InliningInfoNode inliningInfoTable = new InliningInfoNode(Target, inputModule); - tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.InliningInfo2, inliningInfoTable, inliningInfoTable); + if (CompilationModuleGroup.IsCompositeBuildMode) + { + InliningInfoNode inliningInfoTable = new InliningInfoNode(Target, inputModule, InliningInfoNode.InfoType.InliningInfo2); + tableHeader.Add(Internal.Runtime.ReadyToRunSectionType.InliningInfo2, inliningInfoTable, inliningInfoTable); + } // Core library attributes are checked FAR more often than other dlls // attributes, so produce a highly efficient table for determining if they are @@ -667,6 +712,11 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase graph) } } + InliningInfoNode crossModuleInliningInfoTable = new InliningInfoNode(Target, null, + CompilationModuleGroup.IsCompositeBuildMode ? InliningInfoNode.InfoType.CrossModuleInliningForCrossModuleDataOnly : InliningInfoNode.InfoType.CrossModuleAllMethods); + Header.Add(Internal.Runtime.ReadyToRunSectionType.CrossModuleInlineInfo, crossModuleInliningInfoTable, crossModuleInliningInfoTable); + this.CrossModuleInlningInfo = crossModuleInliningInfoTable; + InstanceEntryPointTable = new InstanceEntryPointTableNode(this); Header.Add(Internal.Runtime.ReadyToRunSectionType.InstanceMethodEntryPoints, InstanceEntryPointTable, InstanceEntryPointTable); @@ -728,6 +778,15 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase graph) } } + ILBodyPrecodeImports = new ImportSectionNode( + "A_ILBodyPrecodeImports", + ReadyToRunImportSectionType.Unknown, + ReadyToRunImportSectionFlags.None, + (byte)Target.PointerSize, + emitPrecode: true, + emitGCRefMap: false); + ImportSectionsTable.AddEmbeddedObject(ILBodyPrecodeImports); + MethodImports = new ImportSectionNode( "MethodImports", ReadyToRunImportSectionType.StubDispatch, @@ -780,6 +839,7 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase graph) graph.AddRoot(DispatchImports, "Dispatch imports are always generated"); graph.AddRoot(HelperImports, "Helper imports are always generated"); graph.AddRoot(PrecodeImports, "Precode helper imports are always generated"); + graph.AddRoot(ILBodyPrecodeImports, "IL body precode imports are always generated"); graph.AddRoot(StringImports, "String imports are always generated"); graph.AddRoot(Header, "ReadyToRunHeader is always generated"); graph.AddRoot(CopiedCorHeaderNode, "MSIL COR header is always generated for R2R files"); @@ -788,7 +848,7 @@ public void AttachToDependencyGraph(DependencyAnalyzerBase graph) if (Win32ResourcesNode != null) graph.AddRoot(Win32ResourcesNode, "Win32 Resources are placed if not empty"); - MetadataManager.AttachToDependencyGraph(graph); + MetadataManager.AttachToDependencyGraph(graph, this); } private void Graph_ComputingDependencyPhaseChange(int newPhase) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs index 02cc4d4edada9..940daa8294ccc 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunSymbolNodeFactory.cs @@ -6,6 +6,7 @@ using Internal.JitInterface; using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; using Internal.ReadyToRunConstants; namespace ILCompiler.DependencyAnalysis @@ -141,6 +142,8 @@ private void CreateNodeCaches() return new PrecodeHelperImport(_codegenNodeFactory, key); }); + _ilBodyFixupsCache = new NodeCache(key => new PrecodeHelperImport(_codegenNodeFactory.ILBodyPrecodeImports, key)); + _genericLookupHelpers = new NodeCache(key => { return new DelayLoadHelperImport( @@ -457,6 +460,14 @@ public ISymbolNode CheckVirtualFunctionOverride(MethodWithToken declMethod, Type declMethod, implType, implMethod)); } + private NodeCache _ilBodyFixupsCache; + public Import CheckILBodyFixupSignature(EcmaMethod method) + { + return _ilBodyFixupsCache.GetOrAdd(_codegenNodeFactory.ILBodyFixupSignature( + _verifyTypeAndFieldLayout ? ReadyToRunFixupKind.Verify_IL_Body : ReadyToRunFixupKind.Check_IL_Body, + method)); + } + struct MethodAndCallSite : IEquatable { public readonly MethodWithToken Method; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/NoMethodsCompilationModuleGroup.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/NoMethodsCompilationModuleGroup.cs index 817e136077f0b..d7d57e63f5c35 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/NoMethodsCompilationModuleGroup.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/NoMethodsCompilationModuleGroup.cs @@ -33,7 +33,7 @@ public override void ApplyProfilerGuidedCompilationRestriction(ProfileDataManage public override ReadyToRunFlags GetReadyToRunFlags() { // Partial by definition. - return ReadyToRunFlags.READYTORUN_FLAG_Partial; + return base.GetReadyToRunFlags() | ReadyToRunFlags.READYTORUN_FLAG_Partial; } } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs index 99d7c73d9973f..994492813ee80 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs @@ -58,7 +58,8 @@ protected Compilation( _modulesBeingInstrumented = new HashSet(modulesBeingInstrumented); _dependencyGraph.ComputeDependencyRoutine += ComputeDependencyNodeDependencies; - NodeFactory.AttachToDependencyGraph(_dependencyGraph); + NodeFactory.AttachToDependencyGraph(_dependencyGraph, ilProvider); + var rootingService = new RootingServiceProvider(nodeFactory, _dependencyGraph.AddRoot); foreach (var rootProvider in compilationRoots) @@ -136,11 +137,13 @@ public bool IsModuleInstrumented(ModuleDesc module) public sealed class ILCache : LockFreeReaderHashtable { public ILProvider ILProvider { get; } + public int ExpectedILProviderVersion { get; } private readonly CompilationModuleGroup _compilationModuleGroup; public ILCache(ILProvider provider, CompilationModuleGroup compilationModuleGroup) { ILProvider = provider; + ExpectedILProviderVersion = provider.Version; _compilationModuleGroup = compilationModuleGroup; } @@ -253,6 +256,8 @@ public sealed class ReadyToRunCodegenCompilation : Compilation private readonly ProfileDataManager _profileData; private readonly ReadyToRunFileLayoutOptimizer _fileLayoutOptimizer; + private readonly HashSet _methodsWhichNeedMutableILBodies = new HashSet(); + private readonly HashSet _methodsToRecompile = new HashSet(); public ProfileDataManager ProfileData => _profileData; @@ -315,6 +320,8 @@ internal ReadyToRunCodegenCompilation( SymbolNodeFactory = new ReadyToRunSymbolNodeFactory(nodeFactory, verifyTypeAndFieldLayout); if (nodeFactory.InstrumentationDataTable != null) nodeFactory.InstrumentationDataTable.Initialize(SymbolNodeFactory); + if (nodeFactory.CrossModuleInlningInfo != null) + nodeFactory.CrossModuleInlningInfo.Initialize(SymbolNodeFactory); _corInfoImpls = new ConditionalWeakTable(); _inputFiles = inputFiles; _compositeRootPath = compositeRootPath; @@ -399,6 +406,8 @@ private void RewriteComponentFile(string inputFile, string outputFile, string ow flags |= ReadyToRunFlags.READYTORUN_FLAG_PlatformNeutralSource; } + flags |= _nodeFactory.CompilationModuleGroup.GetReadyToRunFlags() & ReadyToRunFlags.READYTORUN_FLAG_MultiModuleVersionBubble; + CopiedCorHeaderNode copiedCorHeader = new CopiedCorHeaderNode(inputModule); // Re-written components shouldn't have any additional diagnostic information - only information about the forwards. // Even with all of this, we might be modifying the image in a silly manner - adding a directory when if didn't have one. @@ -541,74 +550,101 @@ public bool IsInheritanceChainLayoutFixedInCurrentVersionBubble(TypeDesc type) // The _finishedFirstCompilationRunInPhase2 variable works in concert some checking to ensure that we don't violate any of this model private bool _finishedFirstCompilationRunInPhase2 = false; + public void PrepareForCompilationRetry(MethodWithGCInfo methodToBeRecompiled, IEnumerable methodsThatNeedILBodies) + { + lock (_methodsToRecompile) + { + _methodsToRecompile.Add(methodToBeRecompiled); + foreach (var method in methodsThatNeedILBodies) + _methodsWhichNeedMutableILBodies.Add(method); + } + } + protected override void ComputeDependencyNodeDependencies(List> obj) { + using (PerfEventSource.StartStopEvents.JitEvents()) { - Action> compileOneMethod = (DependencyNodeCore dependency) => + + // Use only main thread to compile if parallelism is 1. This allows SuperPMI to rely on non-reuse of handles in ObjectToHandle + if (Logger.IsVerbose) + Logger.Writer.WriteLine($"Processing {obj.Count} dependencies"); + + // Ensure all methods being compiled have assigned tokens. This matters for modules from outside of the version bubble + // as those tokens are dynamically assigned. + var ilProvider = (ReadyToRunILProvider)_methodILCache.ILProvider; + obj.MergeSortAllowDuplicates(new SortableDependencyNode.ObjectNodeComparer(CompilerComparer.Instance)); + foreach (var dependency in obj) { - MethodWithGCInfo methodCodeNodeNeedingCode = dependency as MethodWithGCInfo; - if (methodCodeNodeNeedingCode == null) + if (dependency is MethodWithGCInfo methodCodeNodeNeedingCode) { - if (dependency is DeferredTillPhaseNode deferredPhaseNode) + var method = methodCodeNodeNeedingCode.Method; + if (method.GetTypicalMethodDefinition() is EcmaMethod ecmaMethod) { - if (Logger.IsVerbose) - _logger.Writer.WriteLine($"Moved to phase {_nodeFactory.CompilationCurrentPhase}"); - deferredPhaseNode.NotifyCurrentPhase(_nodeFactory.CompilationCurrentPhase); - return; + if (ilProvider.NeedsCrossModuleInlineableTokens(ecmaMethod) && + !_methodsWhichNeedMutableILBodies.Contains(ecmaMethod) && + CorInfoImpl.IsMethodCompilable(this, methodCodeNodeNeedingCode.Method)) + _methodsWhichNeedMutableILBodies.Add(ecmaMethod); } } + } - Debug.Assert((_nodeFactory.CompilationCurrentPhase == 0) || ((_nodeFactory.CompilationCurrentPhase == 2) && !_finishedFirstCompilationRunInPhase2)); + ProcessMutableMethodBodiesList(); + ResetILCache(); + CompileMethodList(obj); - MethodDesc method = methodCodeNodeNeedingCode.Method; + while (_methodsToRecompile.Count > 0) + { + ProcessMutableMethodBodiesList(); + ResetILCache(); + MethodWithGCInfo[] methodsToRecompile = new MethodWithGCInfo[_methodsToRecompile.Count]; + _methodsToRecompile.CopyTo(methodsToRecompile); + _methodsToRecompile.Clear(); + Array.Sort(methodsToRecompile, new SortableDependencyNode.ObjectNodeComparer(CompilerComparer.Instance)); if (Logger.IsVerbose) - { - string methodName = method.ToString(); - Logger.Writer.WriteLine("Compiling " + methodName); - } + Logger.Writer.WriteLine($"Processing {methodsToRecompile.Length} recompiles"); - if (_printReproInstructions != null) - { - Logger.Writer.WriteLine($"Single method repro args:{_printReproInstructions(method)}"); - } + CompileMethodList(methodsToRecompile); + } + } - try - { - using (PerfEventSource.StartStopEvents.JitMethodEvents()) - { - // Create only 1 CorInfoImpl per thread. - // This allows SuperPMI to rely on non-reuse of handles in ObjectToHandle - CorInfoImpl corInfoImpl = _corInfoImpls.GetValue(Thread.CurrentThread, thread => new CorInfoImpl(this)); - corInfoImpl.CompileMethod(methodCodeNodeNeedingCode, Logger); - } - } - catch (TypeSystemException ex) - { - // If compilation fails, don't emit code for this method. It will be Jitted at runtime - if (Logger.IsVerbose) - Logger.Writer.WriteLine($"Warning: Method `{method}` was not compiled because: {ex.Message}"); - } - catch (RequiresRuntimeJitException ex) - { - if (Logger.IsVerbose) - Logger.Writer.WriteLine($"Info: Method `{method}` was not compiled because `{ex.Message}` requires runtime JIT"); - } - catch (CodeGenerationFailedException ex) when (_resilient) - { - if (Logger.IsVerbose) - Logger.Writer.WriteLine($"Warning: Method `{method}` was not compiled because `{ex.Message}` requires runtime JIT"); - } - }; + ResetILCache(); + + if (_nodeFactory.CompilationCurrentPhase == 2) + { + _finishedFirstCompilationRunInPhase2 = true; + } + + void ProcessMutableMethodBodiesList() + { + EcmaMethod[] mutableMethodBodyNeedList = new EcmaMethod[_methodsWhichNeedMutableILBodies.Count]; + _methodsWhichNeedMutableILBodies.CopyTo(mutableMethodBodyNeedList); + _methodsWhichNeedMutableILBodies.Clear(); + TypeSystemComparer comparer = TypeSystemComparer.Instance; + Comparison comparison = (EcmaMethod a, EcmaMethod b) => comparer.Compare(a, b); + Array.Sort(mutableMethodBodyNeedList, comparison); + var ilProvider = (ReadyToRunILProvider)_methodILCache.ILProvider; + + foreach (var method in mutableMethodBodyNeedList) + ilProvider.CreateCrossModuleInlineableTokensForILBody(method); + } + + void ResetILCache() + { + if (_methodILCache.Count > 1000 || _methodILCache.ILProvider.Version != _methodILCache.ExpectedILProviderVersion) + _methodILCache = new ILCache(_methodILCache.ILProvider, NodeFactory.CompilationModuleGroup); + } + + void CompileMethodList(IEnumerable> methodList) + { + // Disable generation of new tokens across the multi-threaded compile + NodeFactory.ManifestMetadataTable._mutableModule.DisableNewTokens = true; - // Use only main thread to compile if parallelism is 1. This allows SuperPMI to rely on non-reuse of handles in ObjectToHandle - if (Logger.IsVerbose) - Logger.Writer.WriteLine($"Processing {obj.Count} dependencies"); if (_parallelism == 1) { - foreach (var dependency in obj) - compileOneMethod(dependency); + foreach (var dependency in methodList) + CompileOneMethod(dependency); } else { @@ -617,18 +653,68 @@ protected override void ComputeDependencyNodeDependencies(List 1000) - { - _methodILCache = new ILCache(_methodILCache.ILProvider, NodeFactory.CompilationModuleGroup); + // Re-enable generation of new tokens after the multi-threaded compile + NodeFactory.ManifestMetadataTable._mutableModule.DisableNewTokens = false; } - if (_nodeFactory.CompilationCurrentPhase == 2) + void CompileOneMethod(DependencyNodeCore dependency) { - _finishedFirstCompilationRunInPhase2 = true; + MethodWithGCInfo methodCodeNodeNeedingCode = dependency as MethodWithGCInfo; + if (methodCodeNodeNeedingCode == null) + { + if (dependency is DeferredTillPhaseNode deferredPhaseNode) + { + if (Logger.IsVerbose) + _logger.Writer.WriteLine($"Moved to phase {_nodeFactory.CompilationCurrentPhase}"); + deferredPhaseNode.NotifyCurrentPhase(_nodeFactory.CompilationCurrentPhase); + return; + } + } + + Debug.Assert((_nodeFactory.CompilationCurrentPhase == 0) || ((_nodeFactory.CompilationCurrentPhase == 2) && !_finishedFirstCompilationRunInPhase2)); + + MethodDesc method = methodCodeNodeNeedingCode.Method; + + if (Logger.IsVerbose) + { + string methodName = method.ToString(); + Logger.Writer.WriteLine("Compiling " + methodName); + } + + if (_printReproInstructions != null) + { + Logger.Writer.WriteLine($"Single method repro args:{_printReproInstructions(method)}"); + } + + try + { + using (PerfEventSource.StartStopEvents.JitMethodEvents()) + { + // Create only 1 CorInfoImpl per thread. + // This allows SuperPMI to rely on non-reuse of handles in ObjectToHandle + CorInfoImpl corInfoImpl = _corInfoImpls.GetValue(Thread.CurrentThread, thread => new CorInfoImpl(this)); + corInfoImpl.CompileMethod(methodCodeNodeNeedingCode, Logger); + } + } + catch (TypeSystemException ex) + { + // If compilation fails, don't emit code for this method. It will be Jitted at runtime + if (Logger.IsVerbose) + Logger.Writer.WriteLine($"Warning: Method `{method}` was not compiled because: {ex.Message}"); + } + catch (RequiresRuntimeJitException ex) + { + if (Logger.IsVerbose) + Logger.Writer.WriteLine($"Info: Method `{method}` was not compiled because `{ex.Message}` requires runtime JIT"); + } + catch (CodeGenerationFailedException ex) when (_resilient) + { + if (Logger.IsVerbose) + Logger.Writer.WriteLine($"Warning: Method `{method}` was not compiled because `{ex.Message}` requires runtime JIT"); + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs index 03a583173545c..ba06e6b1c877f 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs @@ -47,7 +47,7 @@ public sealed class ReadyToRunCodegenCompilationBuilder : 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 ReadyToRunILProvider(); + private ILProvider _ilProvider; public ReadyToRunCodegenCompilationBuilder( CompilerTypeSystemContext context, @@ -56,6 +56,7 @@ public ReadyToRunCodegenCompilationBuilder( string compositeRootPath) : base(context, group, new NativeAotNameMangler()) { + _ilProvider = new ReadyToRunILProvider(group); _inputFiles = inputFiles; _compositeRootPath = compositeRootPath; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs index fd84222dd35c0..b5caf195e676d 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs @@ -13,6 +13,7 @@ using Internal.TypeSystem.Ecma; using Internal.TypeSystem.Interop; using Debug = System.Diagnostics.Debug; +using Internal.ReadyToRunConstants; namespace ILCompiler { @@ -21,6 +22,8 @@ public class ReadyToRunCompilationModuleGroupConfig public CompilerTypeSystemContext Context; public bool IsCompositeBuildMode; public bool IsInputBubble; + public bool CrossModuleInlining; + public bool CrossModuleGenericCompilation; public IEnumerable CompilationModuleSet; public IEnumerable VersionBubbleModuleSet; public bool CompileGenericDependenciesFromVersionBubbleModuleSet; @@ -34,10 +37,14 @@ public abstract class ReadyToRunCompilationModuleGroupBase : CompilationModuleGr private readonly bool _compileGenericDependenciesFromVersionBubbleModuleSet; private readonly bool _isCompositeBuildMode; private readonly bool _isInputBubble; + private readonly bool _crossModuleInlining; + private readonly bool _crossModuleGenericCompilation; private readonly ConcurrentDictionary _layoutCompilationUnits = new ConcurrentDictionary(); private readonly ConcurrentDictionary _versionsWithTypeCache = new ConcurrentDictionary(); private readonly ConcurrentDictionary _versionsWithTypeReferenceCache = new ConcurrentDictionary(); private readonly ConcurrentDictionary _versionsWithMethodCache = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _crossModuleInlineableCache = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _crossModuleCompilableCache = new ConcurrentDictionary(); private readonly Dictionary _moduleCompilationUnits = new Dictionary(); private CompilationUnitIndex _nextCompilationUnit = CompilationUnitIndex.FirstDynamicallyAssigned; private ModuleTokenResolver _tokenResolver = null; @@ -48,6 +55,8 @@ public ReadyToRunCompilationModuleGroupBase(ReadyToRunCompilationModuleGroupConf _compilationModuleSet = new HashSet(config.CompilationModuleSet); _isCompositeBuildMode = config.IsCompositeBuildMode; _isInputBubble = config.IsInputBubble; + _crossModuleInlining = config.CrossModuleInlining; + _crossModuleGenericCompilation = config.CrossModuleGenericCompilation; Debug.Assert(_isCompositeBuildMode || _compilationModuleSet.Count == 1); @@ -72,7 +81,7 @@ public sealed override bool ContainsType(TypeDesc type) return type.GetTypeDefinition() is EcmaType ecmaType && IsModuleInCompilationGroup(ecmaType.EcmaModule); } - private bool IsModuleInCompilationGroup(EcmaModule module) + public bool IsModuleInCompilationGroup(EcmaModule module) { return _compilationModuleSet.Contains(module); } @@ -96,6 +105,16 @@ public CompilationUnitSet TypeLayoutCompilationUnits(TypeDesc type) return _layoutCompilationUnits.GetOrAdd(type, TypeLayoutCompilationUnitsUncached); } + public override ReadyToRunFlags GetReadyToRunFlags() + { + ReadyToRunFlags flags = default(ReadyToRunFlags); + if (_versionBubbleModuleSet.Count > 0) + { + flags |= ReadyToRunFlags.READYTORUN_FLAG_MultiModuleVersionBubble; + } + return flags; + } + private enum CompilationUnitIndex { RESERVEDForHasMultipleInexactCompilationUnits = 0, @@ -344,8 +363,8 @@ public sealed override bool CanInline(MethodDesc callerMethod, MethodDesc callee // Allow inlining if the caller is within the current version bubble // (because otherwise we may not be able to encode its tokens) // and if the callee is either in the same version bubble or is marked as non-versionable. - bool canInline = VersionsWithMethodBody(callerMethod) && - (VersionsWithMethodBody(calleeMethod) || IsNonVersionableWithILTokensThatDoNotNeedTranslation(calleeMethod)); + bool canInline = (VersionsWithMethodBody(callerMethod) || CrossModuleInlineable(callerMethod)) && + (VersionsWithMethodBody(calleeMethod) || CrossModuleInlineable(calleeMethod) || IsNonVersionableWithILTokensThatDoNotNeedTranslation(calleeMethod)); return canInline; } @@ -358,6 +377,107 @@ public bool IsNonVersionableWithILTokensThatDoNotNeedTranslation(MethodDesc meth return _tokenTranslationFreeNonVersionable.GetOrAdd((EcmaMethod)method.GetTypicalMethodDefinition(), IsNonVersionableWithILTokensThatDoNotNeedTranslationUncached); } + public override bool CrossModuleCompileable(MethodDesc method) + { + if (!_crossModuleGenericCompilation) + return false; + + return _crossModuleCompilableCache.GetOrAdd(method, CrossModuleCompileableUncached); + } + + private bool CrossModuleCompileableUncached(MethodDesc method) + { + if (!CrossModuleInlineable(method)) + return false; + + // Only allow generics, non-generics should be compiled into the defining module + if (!(method.HasInstantiation || method.OwningType.HasInstantiation)) + return false; + + ModuleDesc methodDefiningModule = ((MetadataType)method.OwningType.GetTypeDefinition()).Module; + + // Where all the generic instantiation details are either: + // Instantiated over some structure type defined within the version bubble, and all other generic arguments are either + // - Canon + // - A non-generic structure type defined in the same module as the defining module of the method (Current implementation is just for non-generics, but could easily be extended to generics) + bool somethingVersionsWithType = false; + foreach (TypeDesc t in method.Instantiation) + { + bool usesCanon = t.IsCanonicalDefinitionType(CanonicalFormKind.Any); + bool versionsWithType = VersionsWithType(t); + if (!versionsWithType && !usesCanon && (t.HasInstantiation && !(t is EcmaType etype && etype.Module != methodDefiningModule))) + return false; + if (versionsWithType) + somethingVersionsWithType = true; + } + foreach (TypeDesc t in method.OwningType.Instantiation) + { + bool usesCanon = t.IsCanonicalDefinitionType(CanonicalFormKind.Any); + bool versionsWithType = VersionsWithType(t); + if (!versionsWithType && !usesCanon && (t.HasInstantiation && !(t is EcmaType etype && etype.Module != methodDefiningModule))) + return false; + if (versionsWithType) + somethingVersionsWithType = true; + } + + // Require that something versions with the type + if (!somethingVersionsWithType) + return false; + + return true; + } + + public override bool CrossModuleInlineable(MethodDesc method) + { + if (!_crossModuleInlining) + return false; + + return _crossModuleInlineableCache.GetOrAdd(method, CrossModuleInlineableUncached); + } + + private bool CrossModuleInlineableUncached(MethodDesc method) + { + // Defined in corelib + MetadataType owningMetadataType = method.OwningType.GetTypeDefinition() as MetadataType; + if (owningMetadataType == null) + { + // If the type is an array or something + return false; + } + ModuleDesc methodDefiningModule = owningMetadataType.Module; + if (method.Context.SystemModule != methodDefiningModule) + return false; + + // Where all the generic instantiation details are either: + // 1. uninstantiated + // 2. Instantiated over some structure type defined within the version bubble, and all other generic arguments are either + // - Canon + // - A non-generic structure type defined in the same module as the defining module of the method (Current implementation is just for non-generics, but could easily be extended to generics) + if ((method.GetMethodDefinition() == method) && method.OwningType.IsTypeDefinition) + { + // This should be ok + } + else + { + foreach (TypeDesc t in method.Instantiation) + { + bool usesCanon = t.IsCanonicalDefinitionType(CanonicalFormKind.Any); + bool versionsWithType = VersionsWithType(t); + if (!versionsWithType && !usesCanon && (t.HasInstantiation && !(t is EcmaType etype && etype.Module != methodDefiningModule))) + return false; + } + foreach (TypeDesc t in method.OwningType.Instantiation) + { + bool usesCanon = t.IsCanonicalDefinitionType(CanonicalFormKind.Any); + bool versionsWithType = VersionsWithType(t); + if (!versionsWithType && !usesCanon && (t.HasInstantiation && !(t is EcmaType etype && etype.Module != methodDefiningModule))) + return false; + } + } + + return true; + } + private bool IsNonVersionableWithILTokensThatDoNotNeedTranslationUncached(EcmaMethod method) { bool result = false; @@ -372,7 +492,7 @@ private bool IsNonVersionableWithILTokensThatDoNotNeedTranslationUncached(EcmaMe // In addition, the method must not have any EH. // The method may only have locals which are NonVersionable structures, or classes - MethodIL methodIL = new ReadyToRunILProvider().GetMethodIL(method); + MethodIL methodIL = new ReadyToRunILProvider(this).GetMethodIL(method); if (methodIL.GetExceptionRegions().Length > 0) return false; @@ -607,7 +727,7 @@ private bool ComputeTypeReferenceVersionsWithCode(TypeDesc type) if (type is EcmaType ecmaType) { - return !_tokenResolver.GetModuleTokenForType(ecmaType, false).IsNull; + return !_tokenResolver.GetModuleTokenForType(ecmaType, allowDynamicallyCreatedReference: false, throwIfNotFound: false).IsNull; } if (type.GetTypeDefinition() == type) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs index bd9eb74423074..97e5bd69e395a 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs @@ -137,6 +137,22 @@ protected override RuntimeInterfacesAlgorithm GetRuntimeInterfacesAlgorithmForNo { return BaseTypeRuntimeInterfacesAlgorithm.Instance; } + + TypeDesc _asyncStateMachineBox; + public TypeDesc AsyncStateMachineBoxType + { + get + { + if (_asyncStateMachineBox == null) + { + _asyncStateMachineBox = SystemModule.GetType("System.Runtime.CompilerServices", "AsyncTaskMethodBuilder`1").GetNestedType("AsyncStateMachineBox`1"); + if (_asyncStateMachineBox == null) + throw new Exception(); + } + + return _asyncStateMachineBox; + } + } } internal class VectorOfTFieldLayoutAlgorithm : FieldLayoutAlgorithm diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunHashCode.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunHashCode.cs index ff511d98c49f7..1f93e7cddb265 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunHashCode.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunHashCode.cs @@ -194,7 +194,7 @@ public static int MethodHashCode(MethodDesc method) int methodNameHashCode = NameHashCode(method.Name); // Todo: Add signature to hash. - if (method.HasInstantiation) + if (method.HasInstantiation && !method.IsGenericMethodDefinition) { hashCode ^= GenericInstanceHashCode(methodNameHashCode, method.Instantiation); } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunSingleAssemblyCompilationModuleGroup.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunSingleAssemblyCompilationModuleGroup.cs index 837708dead3a9..bfb17287bd5ca 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunSingleAssemblyCompilationModuleGroup.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunSingleAssemblyCompilationModuleGroup.cs @@ -40,7 +40,7 @@ public sealed override bool ContainsMethodBody(MethodDesc method, bool unboxingS return false; } - return (ContainsType(method.OwningType) && VersionsWithMethodBody(method)) || CompileVersionBubbleGenericsIntoCurrentModule(method); + return (ContainsType(method.OwningType) && VersionsWithMethodBody(method)) || CompileVersionBubbleGenericsIntoCurrentModule(method) || this.CrossModuleCompileable(method); } public sealed override void ApplyProfilerGuidedCompilationRestriction(ProfileDataManager profileGuidedCompileRestriction) @@ -56,7 +56,7 @@ public override ReadyToRunFlags GetReadyToRunFlags() { Debug.Assert(_profileGuidedCompileRestrictionSet); - ReadyToRunFlags flags = 0; + ReadyToRunFlags flags = base.GetReadyToRunFlags(); if (_profileGuidedCompileRestriction != null) flags |= ReadyToRunFlags.READYTORUN_FLAG_Partial; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunStandaloneMethodMetadata.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunStandaloneMethodMetadata.cs new file mode 100644 index 0000000000000..6a08f4d3908da --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunStandaloneMethodMetadata.cs @@ -0,0 +1,229 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; + +using Internal.IL; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + + +namespace ILCompiler +{ + // Alternate form of metadata that represents a single method. Self contained except for type references + public class ReadyToRunStandaloneMethodMetadata + { + public byte[] ConstantData; + public TypeDesc[] TypeRefs; + + public static ReadyToRunStandaloneMethodMetadata Compute(EcmaMethod wrappedMethod) + { + var metadataReader = wrappedMethod.MetadataReader; + var _module = wrappedMethod.Module; + var rva = wrappedMethod.MetadataReader.GetMethodDefinition(wrappedMethod.Handle).RelativeVirtualAddress; + var _methodBody = _module.PEReader.GetMethodBody(rva); + + byte[] _alternateILStream = _methodBody.GetILBytes(); + + var exceptionRegions = _methodBody.ExceptionRegions; + ILExceptionRegion[] _exceptionRegions; + + int length = exceptionRegions.Length; + if (length == 0) + { + _exceptionRegions = Array.Empty(); + } + else + { + _exceptionRegions = new ILExceptionRegion[length]; + for (int i = 0; i < length; i++) + { + var exceptionRegion = exceptionRegions[i]; + + _exceptionRegions[i] = new ILExceptionRegion( + (ILExceptionRegionKind)exceptionRegion.Kind, // assumes that ILExceptionRegionKind and ExceptionRegionKind enums are in sync + exceptionRegion.TryOffset, + exceptionRegion.TryLength, + exceptionRegion.HandlerOffset, + exceptionRegion.HandlerLength, + MetadataTokens.GetToken(exceptionRegion.CatchType), + exceptionRegion.FilterOffset); + } + } + + BlobReader localsBlob = default(BlobReader); + if (!_methodBody.LocalSignature.IsNil) + { + localsBlob = wrappedMethod.MetadataReader.GetBlobReader(wrappedMethod.MetadataReader.GetStandaloneSignature(_methodBody.LocalSignature).Signature); + } + + AlternativeTypeRefProvider alternateTypes = new AlternativeTypeRefProvider(); + EcmaSignatureEncoder alternateEncoder = new EcmaSignatureEncoder(alternateTypes); + Dictionary _alternateNonTokenStrings = new Dictionary(); + BlobBuilder _alternateNonTypeRefStream = new BlobBuilder(); + BlobBuilder _nonCodeAlternateBlob = new BlobBuilder(); + { + // Generate alternate stream for exceptions. + _nonCodeAlternateBlob.WriteCompressedInteger(_exceptionRegions.Length); + + for (int i = 0; i < _exceptionRegions.Length; i++) + { + var region = _exceptionRegions[i]; + _nonCodeAlternateBlob.WriteCompressedInteger((int)region.Kind); + _nonCodeAlternateBlob.WriteCompressedInteger((int)region.TryOffset); + _nonCodeAlternateBlob.WriteCompressedInteger((int)region.TryLength); + _nonCodeAlternateBlob.WriteCompressedInteger((int)region.HandlerOffset); + _nonCodeAlternateBlob.WriteCompressedInteger((int)region.HandlerLength); + if (region.Kind == ILExceptionRegionKind.Catch) + { + int alternateToken = GetAlternateStreamToken(region.ClassToken); + int encodedToken = CodedIndex.TypeDefOrRefOrSpec(MetadataTokens.EntityHandle(alternateToken)); + _nonCodeAlternateBlob.WriteCompressedInteger(encodedToken); + } + else if (region.Kind == ILExceptionRegionKind.Filter) + { + _nonCodeAlternateBlob.WriteCompressedInteger(region.FilterOffset); + } + } + + if (localsBlob.Length == 0) + { + // No locals. Encode a 0 to indicate this + _nonCodeAlternateBlob.WriteByte(0); + } + else + { + _nonCodeAlternateBlob.WriteByte(_methodBody.LocalVariablesInitialized ? (byte)1 : (byte)0); + EcmaSignatureTranslator sigTranslator = new EcmaSignatureTranslator(localsBlob, _nonCodeAlternateBlob, GetAlternateStreamToken); + sigTranslator.ParseLocalsSignature(); + } + } + + ILTokenReplacer.Replace(_alternateILStream, GetAlternateStreamToken); + + // Add all the streams together into the _nonCodeAlternateBlob + int expectedFinalSize = _nonCodeAlternateBlob.Count + _alternateILStream.Length + _alternateNonTypeRefStream.Count; + _nonCodeAlternateBlob.WriteBytes(_alternateILStream); + _nonCodeAlternateBlob.LinkSuffix(_alternateNonTypeRefStream); + Debug.Assert(expectedFinalSize == _nonCodeAlternateBlob.Count); + + ReadyToRunStandaloneMethodMetadata methodBlock = new ReadyToRunStandaloneMethodMetadata(); + methodBlock.ConstantData = _nonCodeAlternateBlob.ToArray(); + methodBlock.TypeRefs = alternateTypes._alternateTypeRefStream.ToArray(); + return methodBlock; + + ///// HELPER FUNCTIONS + + unsafe int GetAlternateStreamToken(int token) + { + if (token == 0 || !_alternateNonTokenStrings.TryGetValue(token, out int alternateToken)) + { + var handle = MetadataTokens.Handle(token); + + if (handle.Kind == HandleKind.TypeDefinition || handle.Kind == HandleKind.TypeReference) + { + EcmaType ecmaType = (EcmaType)wrappedMethod.Module.GetObject(MetadataTokens.EntityHandle(token)); + alternateToken = MetadataTokens.GetToken(alternateTypes.GetTypeDefOrRefHandleForTypeDesc(ecmaType)); + } + else + { + BlobBuilder blob = new BlobBuilder(); + int flag = 0; + if (handle.Kind == HandleKind.UserString) + { + string strAlternate = metadataReader.GetUserString((UserStringHandle)handle); + flag = 0x70000000; + blob.WriteCompressedInteger(strAlternate.Length); + fixed (char* charData = strAlternate) + { + blob.WriteBytes((byte*)charData, strAlternate.Length * 2); + } + // TODO: consider encoding via wtf-8, or possibly utf-8 + } + else if (handle.Kind == HandleKind.TypeSpecification) + { + flag = 0x1B000000; + var sigBlob = metadataReader.GetBlobReader(metadataReader.GetTypeSpecification((TypeSpecificationHandle)handle).Signature); + EcmaSignatureTranslator sigTranslator = new EcmaSignatureTranslator(sigBlob, blob, GetAlternateStreamToken); + sigTranslator.ParseType(); + } + else if (handle.Kind == HandleKind.MemberReference) + { + flag = 0x0a000000; + var memberReference = metadataReader.GetMemberReference((MemberReferenceHandle)handle); + var sigBlob = metadataReader.GetBlobReader(memberReference.Signature); + EcmaSignatureTranslator sigTranslator = new EcmaSignatureTranslator(sigBlob, blob, GetAlternateStreamToken); + sigTranslator.ParseMemberRefSignature(); + blob.WriteSerializedString(metadataReader.GetString(memberReference.Name)); + blob.WriteCompressedInteger(CodedIndex.MemberRefParent(MetadataTokens.EntityHandle(GetAlternateStreamToken(MetadataTokens.GetToken(memberReference.Parent))))); + } + else if (handle.Kind == HandleKind.MethodDefinition) + { + flag = 0x0a000000; + var methodDefinition = metadataReader.GetMethodDefinition((MethodDefinitionHandle)handle); + var sigBlob = metadataReader.GetBlobReader(methodDefinition.Signature); + EcmaSignatureTranslator sigTranslator = new EcmaSignatureTranslator(sigBlob, blob, GetAlternateStreamToken); + sigTranslator.ParseMethodSignature(); + blob.WriteSerializedString(metadataReader.GetString(methodDefinition.Name)); + blob.WriteCompressedInteger(CodedIndex.MemberRefParent(MetadataTokens.EntityHandle(GetAlternateStreamToken(MetadataTokens.GetToken(methodDefinition.GetDeclaringType()))))); + } + else if (handle.Kind == HandleKind.FieldDefinition) + { + flag = 0x0a000000; + var fieldDefinition = metadataReader.GetFieldDefinition((FieldDefinitionHandle)handle); + var sigBlob = metadataReader.GetBlobReader(fieldDefinition.Signature); + EcmaSignatureTranslator sigTranslator = new EcmaSignatureTranslator(sigBlob, blob, GetAlternateStreamToken); + sigTranslator.ParseFieldSignature(); + blob.WriteSerializedString(metadataReader.GetString(fieldDefinition.Name)); + blob.WriteCompressedInteger(CodedIndex.MemberRefParent(MetadataTokens.EntityHandle(GetAlternateStreamToken(MetadataTokens.GetToken(fieldDefinition.GetDeclaringType()))))); + } + else if (handle.Kind == HandleKind.MethodSpecification) + { + flag = 0x2B000000; + var methodSpecification = metadataReader.GetMethodSpecification((MethodSpecificationHandle)handle); + var sigBlob = metadataReader.GetBlobReader(methodSpecification.Signature); + blob.WriteCompressedInteger(MetadataTokens.GetRowNumber(MetadataTokens.EntityHandle(GetAlternateStreamToken(MetadataTokens.GetToken(methodSpecification.Method))))); + EcmaSignatureTranslator sigTranslator = new EcmaSignatureTranslator(sigBlob, blob, GetAlternateStreamToken); + sigTranslator.ParseMethodSpecSignature(); + } + else if (handle.Kind == HandleKind.StandaloneSignature) + { + flag = 0x11000000; + var reader = wrappedMethod.Module.MetadataReader; + var sigBlob = reader.GetBlobReader(reader.GetStandaloneSignature((StandaloneSignatureHandle)handle).Signature); + EcmaSignatureTranslator sigTranslator = new EcmaSignatureTranslator(sigBlob, blob, GetAlternateStreamToken); + sigTranslator.ParseMethodSignature(); + } + + _alternateNonTypeRefStream.WriteBytes(blob.ToArray()); + alternateToken = (_alternateNonTokenStrings.Count + 1) | flag; + } + _alternateNonTokenStrings.Add(token, alternateToken); + } + + return alternateToken; + } + } + + class AlternativeTypeRefProvider : IEntityHandleProvider + { + public List _alternateTypeRefStream = new List(); + Dictionary _alternateTypeRefTokens = new Dictionary(); + public EntityHandle GetTypeDefOrRefHandleForTypeDesc(TypeDesc type) + { + if (_alternateTypeRefTokens.TryGetValue(type, out EntityHandle result)) + { + return result; + } + _alternateTypeRefStream.Add(type); + result = MetadataTokens.TypeReferenceHandle(_alternateTypeRefStream.Count); + _alternateTypeRefTokens.Add(type, result); + return result; + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunTableManager.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunTableManager.cs index 550de8c4a39fa..93e3f3605cb03 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunTableManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunTableManager.cs @@ -53,6 +53,7 @@ public PerModuleMethodsGenerated(EcmaModule module) private Dictionary _methodsGenerated = new Dictionary(); private List _completeSortedMethods = new List(); private List _completeSortedGenericMethods = new List(); + private NodeFactory _factory; private bool _sortedMethods = false; @@ -61,9 +62,10 @@ public MetadataManager(CompilerTypeSystemContext context) _typeSystemContext = context; } - public void AttachToDependencyGraph(DependencyAnalyzerBase graph) + public void AttachToDependencyGraph(DependencyAnalyzerBase graph, NodeFactory factory) { graph.NewMarkedNode += Graph_NewMarkedNode; + _factory = factory; } protected virtual void Graph_NewMarkedNode(DependencyNodeCore obj) @@ -131,6 +133,7 @@ public IEnumerable GetCompiledMethods(EcmaModule moduleToEnumerate, } _completeSortedMethods.MergeSort(sortHelper); _completeSortedGenericMethods.MergeSort(sortHelper); + _sortedMethods = true; } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/SingleMethodCompilationModuleGroup.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/SingleMethodCompilationModuleGroup.cs index 3827c75a55f0f..7f1e7fd9ccaca 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/SingleMethodCompilationModuleGroup.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/SingleMethodCompilationModuleGroup.cs @@ -38,7 +38,7 @@ public override void ApplyProfilerGuidedCompilationRestriction(ProfileDataManage public override ReadyToRunFlags GetReadyToRunFlags() { // Partial by definition. - return ReadyToRunFlags.READYTORUN_FLAG_Partial; + return base.GetReadyToRunFlags() | ReadyToRunFlags.READYTORUN_FLAG_Partial; } } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs index 85cdcdc220dfa..682ceda6b2d07 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs @@ -2,16 +2,42 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; +using System.Diagnostics; + +using ILCompiler; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; using Internal.IL.Stubs; +using System.Buffers.Binary; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; namespace Internal.IL { + /// + /// Marker interface that promises that all tokens from this MethodIL are useable in the current compilation + /// + public interface IMethodTokensAreUseableInCompilation { } + + public sealed class ReadyToRunILProvider : ILProvider { + private CompilationModuleGroup _compilationModuleGroup; + private MutableModule _manifestMutableModule; + + public ReadyToRunILProvider(CompilationModuleGroup compilationModuleGroup) + { + _compilationModuleGroup = compilationModuleGroup; + } + + public void InitManifestMutableModule(MutableModule module) + { + _manifestMutableModule = module; + } + private MethodIL TryGetIntrinsicMethodILForActivator(MethodDesc method) { if (method.Instantiation.Length == 1 @@ -63,7 +89,7 @@ private MethodIL TryGetIntrinsicMethodIL(MethodDesc method) if (mdType.Name == "Interlocked" && mdType.Namespace == "System.Threading") { - return InterlockedIntrinsics.EmitIL(method); + return InterlockedIntrinsics.EmitIL(_compilationModuleGroup, method); } return null; @@ -93,6 +119,31 @@ private MethodIL TryGetPerInstantiationIntrinsicMethodIL(MethodDesc method) return null; } + private Dictionary _manifestModuleWrappedMethods = new Dictionary(); + + // Create the cross module inlineable tokens for a method + // This method is order dependent, and must be called during the single threaded portion of compilation + public void CreateCrossModuleInlineableTokensForILBody(EcmaMethod method) + { + Debug.Assert(_manifestMutableModule != null); + Debug.Assert(!_compilationModuleGroup.VersionsWithMethodBody(method) && + _compilationModuleGroup.CrossModuleInlineable(method)); + var wrappedMethodIL = new ManifestModuleWrappedMethodIL(_manifestMutableModule, EcmaMethodIL.Create(method)); + _manifestModuleWrappedMethods.Add(method, wrappedMethodIL); + IncrementVersion(); + } + + public bool NeedsCrossModuleInlineableTokens(EcmaMethod method) + { + if (!_compilationModuleGroup.VersionsWithMethodBody(method) && + _compilationModuleGroup.CrossModuleInlineable(method) && + !_manifestModuleWrappedMethods.ContainsKey(method)) + { + return true; + } + return false; + } + public override MethodIL GetMethodIL(MethodDesc method) { if (method is EcmaMethod ecmaMethod) @@ -104,7 +155,11 @@ public override MethodIL GetMethodIL(MethodDesc method) return result; } - MethodIL methodIL = EcmaMethodIL.Create(ecmaMethod); + if (!_manifestModuleWrappedMethods.TryGetValue(ecmaMethod, out var methodIL)) + { + methodIL = EcmaMethodIL.Create(ecmaMethod); + } + if (methodIL != null) return methodIL; @@ -130,5 +185,93 @@ public override MethodIL GetMethodIL(MethodDesc method) return null; } } + + /// + /// A MethodIL Provider which provides tokens relative to a MutableModule. Used to implement cross + /// module inlining of code in ReadyToRun files. + /// + class ManifestModuleWrappedMethodIL : MethodIL, IEcmaMethodIL, IMethodTokensAreUseableInCompilation + { + int _maxStack; + bool _isInitLocals; + MethodDesc _owningMethod; + ILExceptionRegion[] _exceptionRegions; + byte[] _ilBytes; + LocalVariableDefinition[] _locals; + + MutableModule _mutableModule; + + public ManifestModuleWrappedMethodIL(MutableModule mutableModule, EcmaMethodIL wrappedMethod) + { + mutableModule.TryGetEntityHandle(wrappedMethod.OwningMethod); + _mutableModule = mutableModule; + _maxStack = wrappedMethod.MaxStack; + _isInitLocals = wrappedMethod.IsInitLocals; + _owningMethod = wrappedMethod.OwningMethod; + _exceptionRegions = (ILExceptionRegion[])wrappedMethod.GetExceptionRegions().Clone(); + _ilBytes = (byte[])wrappedMethod.GetILBytes().Clone(); + _locals = (LocalVariableDefinition[])wrappedMethod.GetLocals(); + + for (int i = 0; i < _exceptionRegions.Length; i++) + { + var region = _exceptionRegions[i]; + if (region.Kind == ILExceptionRegionKind.Catch) + { + var newHandle = _mutableModule.TryGetHandle((TypeSystemEntity)wrappedMethod.GetObject(region.ClassToken)); + if (!newHandle.HasValue) + { + throw new Exception("Nope"); + } + _exceptionRegions[i] = new ILExceptionRegion(region.Kind, region.TryOffset, region.TryLength, region.HandlerOffset, region.HandlerLength, newHandle.Value, newHandle.Value); + } + } + + ILTokenReplacer.Replace(_ilBytes, GetMutableModuleToken); +#if DEBUG + Debug.Assert(ReadyToRunStandaloneMethodMetadata.Compute((EcmaMethod)_owningMethod) != null); +#endif // DEBUG + + return; + + int GetMutableModuleToken(int token) + { + object result = wrappedMethod.GetObject(token); + int? newToken; + if (result is string str) + { + newToken = mutableModule.TryGetStringHandle(str); + } + else + { + newToken = mutableModule.TryGetHandle((TypeSystemEntity)result); + } + if (!newToken.HasValue) + { + throw new Exception("Nope2"); + } + return newToken.Value; + } + } + + public override int MaxStack => _maxStack; + + public override bool IsInitLocals => _isInitLocals; + + public override MethodDesc OwningMethod => _owningMethod; + + public IEcmaModule Module => _mutableModule; + + public override ILExceptionRegion[] GetExceptionRegions() => _exceptionRegions; + public override byte[] GetILBytes() => _ilBytes; + public override LocalVariableDefinition[] GetLocals() => _locals; + public override object GetObject(int token, NotFoundBehavior notFoundBehavior = NotFoundBehavior.Throw) + { + // UserStrings cannot be wrapped in EntityHandle + if ((token & 0xFF000000) == 0x70000000) + return _mutableModule.GetUserString(System.Reflection.Metadata.Ecma335.MetadataTokens.UserStringHandle(token)); + + return _mutableModule.GetObject(System.Reflection.Metadata.Ecma335.MetadataTokens.EntityHandle(token), notFoundBehavior); + } + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index 7f31574bcf6fb..19880ec91c9db 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -105,6 +105,7 @@ + @@ -207,6 +208,7 @@ + diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 6e62f81bdbf34..cf761a3e1fcf4 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -92,7 +92,7 @@ private static TypeDesc GetMethodTokenOwningType(MethodWithToken methodToken, Ty return methodToken.Method.OwningType; } - TypeDesc HandleContext(EcmaModule module, EntityHandle handle, TypeDesc methodTargetOwner, TypeDesc constrainedType, object context, TypeDesc devirtualizedMethodOwner, ref bool owningTypeNotDerivedFromToken) + TypeDesc HandleContext(IEcmaModule module, EntityHandle handle, TypeDesc methodTargetOwner, TypeDesc constrainedType, object context, TypeDesc devirtualizedMethodOwner, ref bool owningTypeNotDerivedFromToken) { var tokenOnlyOwningType = module.GetType(handle); TypeDesc actualOwningType; @@ -364,6 +364,7 @@ unsafe partial class CorInfoImpl private HashSet _inlinedMethods; private UnboxingMethodDescFactory _unboxingThunkFactory = new UnboxingMethodDescFactory(); private List _precodeFixups; + private List _ilBodiesNeeded; public CorInfoImpl(ReadyToRunCodegenCompilation compilation) : this() @@ -438,7 +439,7 @@ public static bool ShouldSkipCompilation(MethodDesc methodNeedingCode) return false; } - private bool FunctionJustThrows(MethodIL ilBody) + private static bool FunctionJustThrows(MethodIL ilBody) { try { @@ -463,6 +464,32 @@ private bool FunctionJustThrows(MethodIL ilBody) return false; } + partial void DetermineIfCompilationShouldBeRetried(ref CompilationResult result) + { + // If any il bodies need to be recomputed, force recompilation + if (_ilBodiesNeeded != null) + { + _compilation.PrepareForCompilationRetry(_methodCodeNode, _ilBodiesNeeded); + result = CompilationResult.CompilationRetryRequested; + } + } + + public static bool IsMethodCompilable(Compilation compilation, MethodDesc method) + { + // This logic must mirror the logic in CompileMethod used to get to the point of calling CompileMethodInternal + if (ShouldSkipCompilation(method) || MethodSignatureIsUnstable(method.Signature, out var _)) + return false; + + MethodIL methodIL = compilation.GetMethodIL(method); + if (methodIL == null) + return false; + + if (FunctionJustThrows(methodIL)) + return false; + + return true; + } + public void CompileMethod(MethodWithGCInfo methodCodeNodeNeedingCode, Logger logger) { bool codeGotPublished = false; @@ -470,29 +497,56 @@ public void CompileMethod(MethodWithGCInfo methodCodeNodeNeedingCode, Logger log try { - if (!ShouldSkipCompilation(MethodBeingCompiled) && !MethodSignatureIsUnstable(MethodBeingCompiled.Signature, out var _)) + if (ShouldSkipCompilation(MethodBeingCompiled)) { - MethodIL methodIL = _compilation.GetMethodIL(MethodBeingCompiled); + if (logger.IsVerbose) + logger.Writer.WriteLine($"Info: Method `{MethodBeingCompiled}` was not compiled because it is skipped."); + return; + } - if (methodIL != null) + if (MethodSignatureIsUnstable(MethodBeingCompiled.Signature, out var _)) + { + if (logger.IsVerbose) + logger.Writer.WriteLine($"Info: Method `{MethodBeingCompiled}` was not compiled because it has an non version resilient signature."); + return; + } + MethodIL methodIL = _compilation.GetMethodIL(MethodBeingCompiled); + if (methodIL == null) + { + if (logger.IsVerbose) + logger.Writer.WriteLine($"Info: Method `{MethodBeingCompiled}` was not compiled because IL code could not be found for the method."); + return; + } + + if (FunctionJustThrows(methodIL)) + { + if (logger.IsVerbose) + logger.Writer.WriteLine($"Info: Method `{MethodBeingCompiled}` was not compiled because it always throws an exception"); + return; + } + + if (MethodBeingCompiled.GetTypicalMethodDefinition() is EcmaMethod ecmaMethod) + { + // At the moment the ILBody fixup generation is only safe for the System module, as further typerefs may not be safe to encode at this time. When we expand + // cross module inlining to support other modules, this will need to be fixed. + if (methodIL.GetMethodILScopeDefinition() is IEcmaMethodIL && _compilation.SymbolNodeFactory.VerifyTypeAndFieldLayout && ecmaMethod.Module == ecmaMethod.Context.SystemModule || + (!_compilation.NodeFactory.CompilationModuleGroup.VersionsWithMethodBody(MethodBeingCompiled) && + _compilation.NodeFactory.CompilationModuleGroup.CrossModuleInlineable(MethodBeingCompiled))) { - if (!FunctionJustThrows(methodIL)) - { - if (MethodBeingCompiled.GetTypicalMethodDefinition() is EcmaMethod ecmaMethod) - { - // Harvest the method being compiled for the purpose of populating the type resolver - var resolver = _compilation.NodeFactory.Resolver; - resolver.AddModuleTokenForMethod(MethodBeingCompiled, new ModuleToken(ecmaMethod.Module, ecmaMethod.Handle)); - } - CompileMethodInternal(methodCodeNodeNeedingCode, methodIL); - codeGotPublished = true; - } - else - { - if (logger.IsVerbose) - logger.Writer.WriteLine($"Warning: Method `{MethodBeingCompiled}` was not compiled because it always throws an exception"); - } + ISymbolNode ilBodyNode = _compilation.SymbolNodeFactory.CheckILBodyFixupSignature((EcmaMethod)MethodBeingCompiled.GetTypicalMethodDefinition()); + AddPrecodeFixup(ilBodyNode); } + + // Harvest the method being compiled for the purpose of populating the type resolver + var resolver = _compilation.NodeFactory.Resolver; + resolver.AddModuleTokenForMethod(MethodBeingCompiled, new ModuleToken(ecmaMethod.Module, ecmaMethod.Handle)); + } + var compilationResult = CompileMethodInternal(methodCodeNodeNeedingCode, methodIL); + codeGotPublished = true; + + if (compilationResult == CompilationResult.CompilationRetryRequested && logger.IsVerbose) + { + logger.Writer.WriteLine($"Info: Method `{MethodBeingCompiled}` triggered recompilation to acquire stable tokens for cross module inline."); } } finally @@ -1000,7 +1054,7 @@ private ModuleToken HandleToModuleToken(ref CORINFO_RESOLVED_TOKEN pResolvedToke { mdToken token = pResolvedToken.token; var methodIL = HandleToObject(pResolvedToken.tokenScope); - EcmaModule module; + IEcmaModule module; // If the method body is synthetized by the compiler (the definition of the MethodIL is not // an EcmaMethodIL), the tokens in the MethodIL are not actual tokens: they're just @@ -1041,7 +1095,7 @@ private ModuleToken HandleToModuleToken(ref CORINFO_RESOLVED_TOKEN pResolvedToke resultField = resultField.GetTypicalFieldDefinition(); Debug.Assert(resultField is EcmaField); - Debug.Assert(_compilation.NodeFactory.CompilationModuleGroup.VersionsWithType(((EcmaField)resultField).OwningType)); + Debug.Assert(_compilation.NodeFactory.CompilationModuleGroup.VersionsWithType(((EcmaField)resultField).OwningType) || ((MetadataType)resultField.OwningType).IsNonVersionable()); token = (mdToken)MetadataTokens.GetToken(((EcmaField)resultField).Handle); module = ((EcmaField)resultField).Module; } @@ -1059,13 +1113,13 @@ private ModuleToken HandleToModuleToken(ref CORINFO_RESOLVED_TOKEN pResolvedToke Debug.Assert(resultDef is SignatureMethodVariable); Debug.Assert(((SignatureMethodVariable)resultDef).Index == 0); module = (EcmaModule)((MetadataType)methodILDef.OwningMethod.OwningType).Module; - token = FindGenericMethodArgTypeSpec(module); + token = FindGenericMethodArgTypeSpec((EcmaModule)module); } } } else { - module = ((EcmaMethodIL)methodILDef).Module; + module = ((IEcmaMethodIL)methodILDef).Module; } return new ModuleToken(module, token); @@ -1076,11 +1130,11 @@ private InfoAccessType constructStringLiteral(CORINFO_MODULE_STRUCT_* module, md MethodILScope methodIL = HandleToObject(module); // If this is not a MethodIL backed by a physical method body, we need to remap the token. - Debug.Assert(methodIL.GetMethodILScopeDefinition() is EcmaMethodIL); + Debug.Assert(methodIL.GetMethodILScopeDefinition() is IEcmaMethodIL); - EcmaMethod method = (EcmaMethod)methodIL.OwningMethod.GetTypicalMethodDefinition(); + IEcmaModule metadataModule = ((IEcmaMethodIL)methodIL.GetMethodILScopeDefinition()).Module; ISymbolNode stringObject = _compilation.SymbolNodeFactory.StringLiteral( - new ModuleToken(method.Module, metaTok)); + new ModuleToken(metadataModule, metaTok)); ppValue = (void*)ObjectToHandle(stringObject); return InfoAccessType.IAT_PPVALUE; } @@ -1479,10 +1533,13 @@ private void ceeInfoGetCallInfo( throw new RequiresRuntimeJitException(callerMethod.ToString() + " -> " + originalMethod.ToString()); } - if (!_compilation.NodeFactory.CompilationModuleGroup.VersionsWithMethodBody(callerMethod)) + if (!_compilation.NodeFactory.CompilationModuleGroup.VersionsWithMethodBody(callerMethod) && + !_compilation.NodeFactory.CompilationModuleGroup.CrossModuleInlineable(callerMethod)) { // We must abort inline attempts calling from outside of the version bubble being compiled // because we have no way to remap the token relative to the external module to the current version bubble. + // + // Unless this is a call made while compiling a CrossModuleInlineable method throw new RequiresRuntimeJitException(callerMethod.ToString() + " -> " + originalMethod.ToString()); } @@ -1932,8 +1989,22 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO // We allow this at least for primitives and enums because we control them // and we know there's no state mutation. - if (getTypeForPrimitiveValueClass(pConstrainedResolvedToken->hClass) == CorInfoType.CORINFO_TYPE_UNDEF) + // + // We allow this for constrained calls to non-virtual methods, as its extremely unlikely they will become virtual (verified by fixup) + // + // TODO in the future we should consider allowing this for regular virtuals as well with a fixup that verifies the method wasn't implemented + if (getTypeForPrimitiveValueClass(pConstrainedResolvedToken->hClass) != CorInfoType.CORINFO_TYPE_UNDEF) + { + // allowed + } + else if (!originalMethod.IsVirtual && originalMethod.Context.GetWellKnownType(WellKnownType.Object) == originalMethod.OwningType) + { + // alllowed, non-virtual method's on Object will never become virtual, and will also always trigger a BOX_THIS pattern + } + else + { throw new RequiresRuntimeJitException(pResult->thisTransform.ToString()); + } } // OK, if the EE said we're not doing a stub dispatch then just return the kind to @@ -1992,7 +2063,7 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO else { // READYTORUN: FUTURE: Direct calls if possible - pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol( + pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol( _compilation.NodeFactory.MethodEntrypoint( ComputeMethodWithToken(nonUnboxingMethod, ref pResolvedToken, constrainedType, unboxing: isUnboxingStub), isInstantiatingStub: useInstantiatingStub, @@ -2348,7 +2419,7 @@ private bool NeedsTypeLayoutCheck(TypeDesc type) /// private void PreventRecursiveFieldInlinesOutsideVersionBubble(FieldDesc field, MethodDesc callerMethod) { - if (!_compilation.NodeFactory.CompilationModuleGroup.VersionsWithMethodBody(callerMethod)) + if (!_compilation.NodeFactory.CompilationModuleGroup.VersionsWithMethodBody(callerMethod) && !_compilation.NodeFactory.CompilationModuleGroup.CrossModuleInlineable(callerMethod)) { // Prevent recursive inline attempts where an inlined method outside of the version bubble is // referencing fields outside the version bubble. @@ -2686,6 +2757,36 @@ private void reportInliningDecision(CORINFO_METHOD_STRUCT_* inlinerHnd, CORINFO_ // Then add the fact we inlined this method. _inlinedMethods = _inlinedMethods ?? new HashSet(); _inlinedMethods.Add(inlinee); + + var typicalMethod = inlinee.GetTypicalMethodDefinition(); + if (!_compilation.CompilationModuleGroup.VersionsWithMethodBody(typicalMethod) && + typicalMethod is EcmaMethod ecmaMethod && + ecmaMethod.Module == typicalMethod.Context.SystemModule) // NonVersionable code in System.Collections.Immutable is causing problems and needs to be treated specially + { + Debug.Assert(_compilation.CompilationModuleGroup.CrossModuleInlineable(typicalMethod)); + bool needsTokenTranslation = !_compilation.CompilationModuleGroup.IsNonVersionableWithILTokensThatDoNotNeedTranslation(typicalMethod); + + if (needsTokenTranslation) + { + // This can happen if the method is marked as NonVersionable if there are tokens of interest + // or if we are working with a full cross module inline + ISymbolNode ilBodyNode = _compilation.SymbolNodeFactory.CheckILBodyFixupSignature(ecmaMethod); + AddPrecodeFixup(ilBodyNode); + } + + // For cross module inlines, we will compile in a potentially 2 step process + // 1. Compile the method using the set of il bodies available at the start of a multi-threaded compilation run + // 2. If at any time, the set of methods that are inlined includes a method which has an IL body without + // tokens that are useable in compilation, record that information, and once the multi-threaded portion + // of the build finishes, it will then compute the IL bodies for those methods, then run the compilation again. + MethodIL methodIL = _compilation.GetMethodIL(typicalMethod); + if (needsTokenTranslation && !(methodIL is IMethodTokensAreUseableInCompilation) && methodIL is EcmaMethodIL) + { + // We may have already acquired the right type of MethodIL here, or be working with a method that is an IL Intrinsic + _ilBodiesNeeded = _ilBodiesNeeded ?? new List(); + _ilBodiesNeeded.Add(ecmaMethod); + } + } } else { diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ManifestAssemblyMetadata.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ManifestAssemblyMetadata.cs new file mode 100644 index 0000000000000..a85237661fc0f --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ManifestAssemblyMetadata.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; +using System.Text; + +namespace ILCompiler.Reflection.ReadyToRun +{ + /// + /// Reader for the metadata associated with the R2R manifest + /// + internal class ManifestAssemblyMetadata : IAssemblyMetadata + { + /// + /// Reader representing the MSIL assembly file. + /// + private readonly PEReader _peReader; + + /// + /// Metadata reader for the MSIL assembly. We create one upfront to avoid going + /// through the GetMetadataReader() helper and constructing a new instance every time. + /// + private readonly MetadataReader _metadataReader; + + public ManifestAssemblyMetadata(PEReader peReader, MetadataReader metadataReader) + { + _peReader = peReader; + _metadataReader = metadataReader; + } + + public PEReader ImageReader => _peReader; + + public MetadataReader MetadataReader => _metadataReader; + + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs index 4dc4f1e372af8..bcf9a4a256529 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs @@ -113,6 +113,7 @@ public sealed class ReadyToRunReader private MetadataReader _manifestReader; private List _manifestReferences; private Dictionary _manifestReferenceAssemblies; + private IAssemblyMetadata _manifestAssemblyMetadata; // ExceptionInfo private Dictionary _runtimeFunctionToEHInfo; @@ -140,6 +141,7 @@ public sealed class ReadyToRunReader /// Byte array containing the ReadyToRun image /// public byte[] Image { get; private set; } + private PinningReference ImagePin; /// /// Name of the image file @@ -353,6 +355,15 @@ internal MetadataReader ManifestReader } } + internal IAssemblyMetadata R2RManifestMetadata + { + get + { + EnsureManifestReferences(); + return _manifestAssemblyMetadata; + } + } + /// /// Initializes the fields of the R2RHeader and R2RMethods /// @@ -396,6 +407,20 @@ public static bool IsReadyToRunImage(PEReader peReader) } } + class PinningReference + { + GCHandle _pinnedObject; + public PinningReference(object o) + { + _pinnedObject = GCHandle.Alloc(o, GCHandleType.Pinned); + } + + ~PinningReference() + { + if (_pinnedObject.IsAllocated) + _pinnedObject.Free(); + } + } private unsafe void Initialize(IAssemblyMetadata metadata) { _assemblyCache = new List(); @@ -404,6 +429,7 @@ private unsafe void Initialize(IAssemblyMetadata metadata) { byte[] image = File.ReadAllBytes(Filename); Image = image; + ImagePin = new PinningReference(image); CompositeReader = new PEReader(Unsafe.As>(ref image)); } @@ -411,6 +437,7 @@ private unsafe void Initialize(IAssemblyMetadata metadata) { ImmutableArray content = CompositeReader.GetEntireImage().GetContent(); Image = Unsafe.As, byte[]>(ref content); + ImagePin = new PinningReference(Image); } if (metadata == null && CompositeReader.HasMetadata) @@ -645,6 +672,7 @@ private unsafe void EnsureManifestReferences() fixed (byte* image = Image) { _manifestReader = new MetadataReader(image + GetOffset(manifestMetadata.RelativeVirtualAddress), manifestMetadata.Size); + _manifestAssemblyMetadata = new ManifestAssemblyMetadata(CompositeReader, _manifestReader); int assemblyRefCount = _manifestReader.GetTableRowCount(TableIndex.AssemblyRef); for (int assemblyRefIndex = 1; assemblyRefIndex <= assemblyRefCount; assemblyRefIndex++) { @@ -842,19 +870,36 @@ private void ParseInstanceMethodEntrypoints(bool[] isEntryPoint) while (!curParser.IsNull()) { IAssemblyMetadata mdReader = GetGlobalMetadata(); + bool updateMDReaderFromOwnerType = true; SignatureFormattingOptions dummyOptions = new SignatureFormattingOptions(); SignatureDecoder decoder = new SignatureDecoder(_assemblyResolver, dummyOptions, mdReader?.MetadataReader, this, (int)curParser.Offset); string owningType = null; uint methodFlags = decoder.ReadUInt(); + + if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_UpdateContext) != 0) + { + int moduleIndex = (int)decoder.ReadUInt(); + mdReader = OpenReferenceAssembly(moduleIndex); + + decoder = new SignatureDecoder(_assemblyResolver, dummyOptions, mdReader.MetadataReader, this, (int)curParser.Offset); + + decoder.ReadUInt(); // Skip past methodFlags + decoder.ReadUInt(); // And moduleIndex + updateMDReaderFromOwnerType = false; + } + if ((methodFlags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_OwnerType) != 0) { - mdReader = decoder.GetMetadataReaderFromModuleOverride() ?? mdReader; - if ((_composite) && mdReader == null) + if (updateMDReaderFromOwnerType) { - // The only types that don't have module overrides on them in composite images are primitive types within the system module - mdReader = GetSystemModuleMetadataReader(); + mdReader = decoder.GetMetadataReaderFromModuleOverride() ?? mdReader; + if ((_composite) && mdReader == null) + { + // The only types that don't have module overrides on them in composite images are primitive types within the system module + mdReader = GetSystemModuleMetadataReader(); + } } owningType = decoder.ReadTypeSignatureNoEmit(); } @@ -1372,18 +1417,32 @@ private AssemblyReferenceHandle GetAssemblyAtIndex(int refAsmIndex, out Metadata { Debug.Assert(refAsmIndex != 0); - int assemblyRefCount = (_composite ? 0 : _assemblyCache[0].MetadataReader.GetTableRowCount(TableIndex.AssemblyRef) + 1); - AssemblyReferenceHandle assemblyReferenceHandle; - if (refAsmIndex < assemblyRefCount) + int assemblyRefCount = (_composite ? 0 : _assemblyCache[0].MetadataReader.GetTableRowCount(TableIndex.AssemblyRef)); + AssemblyReferenceHandle assemblyReferenceHandle = default(AssemblyReferenceHandle); + metadataReader = null; + if (refAsmIndex <= (assemblyRefCount)) { metadataReader = _assemblyCache[0].MetadataReader; assemblyReferenceHandle = MetadataTokens.AssemblyReferenceHandle(refAsmIndex); } else { - metadataReader = ManifestReader; - assemblyReferenceHandle = ManifestReferences[refAsmIndex - assemblyRefCount - 1]; - } + int index = refAsmIndex - assemblyRefCount; + if (ReadyToRunHeader.MajorVersion > 6 || (ReadyToRunHeader.MajorVersion == 6 && ReadyToRunHeader.MinorVersion >= 2)) + { + if (index == 1) + { + metadataReader = ManifestReader; + assemblyReferenceHandle = default(AssemblyReferenceHandle); + } + index--; + } + if (index > 0) + { + metadataReader = ManifestReader; + assemblyReferenceHandle = ManifestReferences[index - 1]; + } + } return assemblyReferenceHandle; } @@ -1391,7 +1450,11 @@ private AssemblyReferenceHandle GetAssemblyAtIndex(int refAsmIndex, out Metadata internal string GetReferenceAssemblyName(int refAsmIndex) { AssemblyReferenceHandle handle = GetAssemblyAtIndex(refAsmIndex, out MetadataReader reader); - return reader.GetString(reader.GetAssemblyReference(handle).Name); + + if (handle.IsNil) + return reader.GetString(reader.GetAssemblyDefinition().Name); + + return reader.GetString(reader.GetAssemblyReference((AssemblyReferenceHandle)handle).Name); } /// @@ -1406,11 +1469,18 @@ internal IAssemblyMetadata OpenReferenceAssembly(int refAsmIndex) { AssemblyReferenceHandle assemblyReferenceHandle = GetAssemblyAtIndex(refAsmIndex, out MetadataReader metadataReader); - result = _assemblyResolver.FindAssembly(metadataReader, assemblyReferenceHandle, Filename); - if (result == null) + if (assemblyReferenceHandle.IsNil) { - string name = metadataReader.GetString(metadataReader.GetAssemblyReference(assemblyReferenceHandle).Name); - throw new Exception($"Missing reference assembly: {name}"); + result = R2RManifestMetadata; + } + else + { + result = _assemblyResolver.FindAssembly(metadataReader, assemblyReferenceHandle, Filename); + if (result == null) + { + string name = metadataReader.GetString(metadataReader.GetAssemblyReference(assemblyReferenceHandle).Name); + throw new Exception($"Missing reference assembly: {name}"); + } } while (_assemblyCache.Count <= refAsmIndex) { diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs index e250d64fbf9ee..f90f68b683d12 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunSignature.cs @@ -94,7 +94,7 @@ public static string FormatHandle(MetadataReader metadataReader, Handle handle, public static ReadyToRunSignature FormatSignature(IAssemblyResolver assemblyResolver, ReadyToRunReader r2rReader, int imageOffset) { SignatureFormattingOptions dummyOptions = new SignatureFormattingOptions(); - SignatureDecoder decoder = new SignatureDecoder(assemblyResolver, dummyOptions, r2rReader.GetGlobalMetadata()?.MetadataReader, r2rReader, imageOffset); + SignatureDecoder decoder = new SignatureDecoder(assemblyResolver, dummyOptions, r2rReader.GetGlobalMetadata()?.MetadataReader, r2rReader, imageOffset, forFixup: true); StringBuilder dummyBuilder = new StringBuilder(); return decoder.ReadR2RSignature(dummyBuilder); } @@ -301,7 +301,7 @@ private string EmitTypeReferenceName(TypeReferenceHandle typeRefHandle, bool nam TypeReference typeRef = _metadataReader.GetTypeReference(typeRefHandle); string typeName = EmitString(typeRef.Name); string output = ""; - if (typeRef.ResolutionScope.Kind != HandleKind.AssemblyReference) + if ((typeRef.ResolutionScope.Kind != HandleKind.AssemblyReference) && (typeRef.ResolutionScope.Kind != HandleKind.ModuleReference)) { // Nested type - format enclosing type followed by the nested type return EmitHandleName(typeRef.ResolutionScope, namespaceQualified, owningTypeOverride: null) + "+" + typeName; @@ -526,6 +526,22 @@ public byte ReadByte() return _image[_offset++]; } + public void SkipBytes(uint bytesToSkip) + { + checked + { + _offset += (int)bytesToSkip; + } + } + + public void SkipBytes(int bytesToSkip) + { + checked + { + _offset += bytesToSkip; + } + } + /// /// Read a single unsigned 32-bit in from the signature stream. Adapted from CorSigUncompressData, /// https://github.com/dotnet/coreclr/blob/master/src/inc/cor.h. @@ -813,6 +829,7 @@ public TMethod ParseMethod() { int moduleIndex = (int)ReadUInt(); IAssemblyMetadata refAsmReader = _contextReader.OpenReferenceAssembly(moduleIndex); + var refAsmDecoder = new R2RSignatureDecoder(_provider, Context, refAsmReader.MetadataReader, _image, _offset, _outerReader, _contextReader, skipOverrideMetadataReader: true); var result = refAsmDecoder.ParseMethodWithMethodFlags(methodFlags); _offset = refAsmDecoder.Offset; @@ -1015,8 +1032,8 @@ public string GetMethodWithFlags(ReadyToRunMethodSigFlags flags, string method) /// SignatureFormattingOptions for signature formatting /// R2RReader object representing the PE file containing the ECMA metadata /// Signature offset within the PE file byte array - public SignatureDecoder(IAssemblyResolver assemblyResolver, SignatureFormattingOptions options, MetadataReader metadataReader, ReadyToRunReader r2rReader, int offset) : - base(TextTypeProvider.Singleton, new TextSignatureDecoderContext(assemblyResolver, options), metadataReader, r2rReader, offset) + public SignatureDecoder(IAssemblyResolver assemblyResolver, SignatureFormattingOptions options, MetadataReader metadataReader, ReadyToRunReader r2rReader, int offset, bool forFixup = false) : + base(TextTypeProvider.Singleton, new TextSignatureDecoderContext(assemblyResolver, options), metadataReader, r2rReader, offset, skipOverrideMetadataReader: !forFixup) { } @@ -1439,6 +1456,20 @@ private ReadyToRunSignature ParseSignature(ReadyToRunFixupKind fixupType, String builder.Append(" (PINVOKE_TARGET)"); break; + case ReadyToRunFixupKind.Check_IL_Body: + case ReadyToRunFixupKind.Verify_IL_Body: + uint ilBodyByteBlobSize = ReadUInt(); + SkipBytes(ilBodyByteBlobSize); + uint types = ReadUInt(); + for (uint i = 0; i < types; i++) + ParseType(); + ParseMethod(builder); + if (fixupType == ReadyToRunFixupKind.Check_IL_Body) + builder.Append(" (CHECK_IL_BODY)"); + else + builder.Append(" (VERIFY_IL_BODY)"); + break; + default: throw new BadImageFormatException(); } diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilationBuilder.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilationBuilder.cs index 013de73cec569..1c8bd1f9a8a5e 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilationBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilationBuilder.cs @@ -20,7 +20,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 NativeAotILProvider(); + private ILProvider _ilProvider; private ProfileDataManager _profileDataManager; private string _jitPath; @@ -28,6 +28,7 @@ public RyuJitCompilationBuilder(CompilerTypeSystemContext context, CompilationMo : base(context, group, new NativeAotNameMangler(context.Target.IsWindows ? (NodeMangler)new WindowsNodeMangler() : (NodeMangler)new UnixNodeMangler(), false)) { + _ilProvider = new NativeAotILProvider(group); } public RyuJitCompilationBuilder UseProfileData(IEnumerable mibcFiles) diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj b/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj index 6b6a8381a9094..1ce63756c98b7 100644 --- a/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj @@ -398,6 +398,9 @@ Ecma\EcmaModule.Symbols.cs + + Ecma\EcmaSignatureTranslator.cs + Ecma\SymbolReader\PdbSymbolReader.cs @@ -428,6 +431,9 @@ Ecma\EcmaSignatureParser.cs + + Ecma\EcmaSignatureEncoder.cs + Ecma\EcmaType.cs @@ -446,6 +452,9 @@ Ecma\IMetadataStringDecoderProvider.cs + + Ecma\IEcmaModule.cs + Ecma\CachingMetadataStringDecoder.cs @@ -479,6 +488,9 @@ IL\ILOpcodeHelper.cs + + IL\ILTokenReplacer.cs + IL\Stubs\ILEmitter.cs @@ -494,6 +506,18 @@ IL\Stubs\PInvokeTargetNativeMethod.Sorting.cs + + MetadataEmitter\TypeSystemMetadataEmitter.cs + + + Mutable\MutableModule.Sorting.cs + + + Mutable\MutableModule.Symbols.cs + + + Mutable\MutableModule.cs + TypeSystem\CodeGen\FieldDesc.Serialization.cs diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index 925fd5c9a90d8..3c7b3c579a84e 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -724,7 +724,7 @@ static string ILLinkify(string rootedAssembly) PInvokeILEmitterConfiguration pinvokePolicy = new ConfigurablePInvokePolicy(typeSystemContext.Target, _directPInvokes, _directPInvokeLists); - ILProvider ilProvider = new NativeAotILProvider(); + ILProvider ilProvider = new NativeAotILProvider(compilationGroup); List> featureSwitches = new List>(); foreach (var switchPair in _featureSwitches) diff --git a/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs b/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs index 9e0a4ba945916..b771997a3b8d7 100644 --- a/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs +++ b/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs @@ -30,6 +30,8 @@ internal class CommandLineOptions public bool OptimizeDisabled; public bool OptimizeSpace; public bool OptimizeTime; + public bool CrossModuleInlining; + public bool CrossModuleGenericCompilation; public bool InputBubble; public bool CompileBubbleGenerics; public bool Verbose; @@ -90,6 +92,10 @@ public CommandLineOptions(string[] args) Parallelism = Environment.ProcessorCount; SingleMethodGenericArg = null; + // These behaviors default to enabled + CrossModuleInlining = true; + CrossModuleGenericCompilation = true; + bool forceHelp = false; if (args.Length == 0) { @@ -164,6 +170,9 @@ public CommandLineOptions(string[] args) syntax.DefineOption("perfmap-path", ref PerfMapPath, SR.PerfMapFilePathOption); syntax.DefineOption("perfmap-format-version", ref PerfMapFormatVersion, SR.PerfMapFormatVersionOption); + syntax.DefineOption("opt-cross-module-inlining", ref CrossModuleInlining, SR.CrossModuleInlining); + syntax.DefineOption("opt-cross-module-generic-compilation", ref CrossModuleGenericCompilation, SR.CrossModuleGenericCompilation); + syntax.DefineOption("method-layout", ref MethodLayout, SR.MethodLayoutOption); syntax.DefineOption("file-layout", ref FileLayout, SR.FileLayoutOption); syntax.DefineOption("verify-type-and-field-layout", ref VerifyTypeAndFieldLayout, SR.VerifyTypeAndFieldLayoutOption); diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index 8667b0bb1eec4..a26368721337e 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -637,6 +637,8 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru groupConfig.CompilationModuleSet = inputModules; groupConfig.VersionBubbleModuleSet = versionBubbleModules; groupConfig.CompileGenericDependenciesFromVersionBubbleModuleSet = _commandLineOptions.CompileBubbleGenerics; + groupConfig.CrossModuleGenericCompilation = _commandLineOptions.CrossModuleGenericCompilation; + groupConfig.CrossModuleInlining = _commandLineOptions.CrossModuleInlining; if (singleMethod != null) { @@ -729,7 +731,7 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru string compilationUnitPrefix = ""; builder.UseCompilationUnitPrefix(compilationUnitPrefix); - ILProvider ilProvider = new ReadyToRunILProvider(); + ILProvider ilProvider = new ReadyToRunILProvider(compilationGroup); DependencyTrackingLevel trackingLevel = _commandLineOptions.DgmlLogFileName == null ? DependencyTrackingLevel.None : (_commandLineOptions.GenerateFullDgmlLog ? DependencyTrackingLevel.All : DependencyTrackingLevel.First); diff --git a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx index e5deb22319cdc..869b5abf3cf3c 100644 --- a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx +++ b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx @@ -1,17 +1,17 @@  - @@ -372,4 +372,10 @@ Hexademical value to set target PE-file ImageBase field - + + When compiling, compile generic code defined in other modules in a version resilient manner. Defaults to enabled. Specify --opt-cross-module-generic-compilation:false to disable. + + + When compiling, inline code from other modules in a version resilient manner. Defaults to enabled. Specify --opt-cross-module-inlining:false to disable. + + \ No newline at end of file diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp index dd6541a1f7437..2c3a4b9ea30f9 100644 --- a/src/coreclr/vm/appdomain.cpp +++ b/src/coreclr/vm/appdomain.cpp @@ -1039,6 +1039,8 @@ void SystemDomain::Attach() // Each domain gets its own ReJitManager, and ReJitManager has its own static // initialization to run ReJitManager::InitStatic(); + + InitMethodBlocks(); } diff --git a/src/coreclr/vm/assembly.cpp b/src/coreclr/vm/assembly.cpp index e72b6bfe57527..8b74be74f1265 100644 --- a/src/coreclr/vm/assembly.cpp +++ b/src/coreclr/vm/assembly.cpp @@ -774,7 +774,7 @@ Module *Assembly::FindModuleByExportedType(mdExportedType mdType, DomainAssembly* pDomainModule = NULL; if (loadFlag == Loader::Load) { - pDomainModule = GetModule()->LoadModule(::GetAppDomain(), mdLinkRef); + pDomainModule = GetModule()->LoadModule(mdLinkRef); } if (pDomainModule == NULL) @@ -808,7 +808,7 @@ Module *Assembly::FindModuleByExportedType(mdExportedType mdType, // The returned Module is non-NULL unless you prevented the load by setting loadFlag=Loader::DontLoad. /* static */ Module * Assembly::FindModuleByTypeRef( - Module * pModule, + ModuleBase * pModule, mdTypeRef tkType, Loader::LoadFlag loadFlag, BOOL * pfNoResolutionScope) @@ -865,7 +865,12 @@ Module * Assembly::FindModuleByTypeRef( if (IsNilToken(tkType)) { *pfNoResolutionScope = TRUE; - RETURN(pModule); + if (!pModule->IsFullModule()) + { + // The ModuleBase scenarios should never need this + COMPlusThrowHR(COR_E_BADIMAGEFORMAT); + } + RETURN(static_cast(pModule)); } iter++; } @@ -885,10 +890,16 @@ Module * Assembly::FindModuleByTypeRef( { case mdtModule: { + if (!pModule->IsFullModule()) + { + // The ModuleBase scenarios should never need this + COMPlusThrowHR(COR_E_BADIMAGEFORMAT); + } + // Type is in the referencing module. GCX_NOTRIGGER(); CANNOTTHROWCOMPLUSEXCEPTION(); - RETURN( pModule ); + RETURN( static_cast(pModule) ); } case mdtModuleRef: @@ -904,7 +915,7 @@ Module * Assembly::FindModuleByTypeRef( #ifndef DACCESS_COMPILE if (loadFlag == Loader::Load) { - DomainAssembly* pActualDomainAssembly = pModule->LoadModule(::GetAppDomain(), tkType); + DomainAssembly* pActualDomainAssembly = pModule->LoadModule(tkType); RETURN(pActualDomainAssembly->GetModule()); } else @@ -1003,7 +1014,7 @@ Module *Assembly::FindModuleByName(LPCSTR pszModuleName) if (this == SystemDomain::SystemAssembly()) RETURN m_pModule->GetModuleIfLoaded(kFile); else - RETURN m_pModule->LoadModule(::GetAppDomain(), kFile)->GetModule(); + RETURN m_pModule->LoadModule(kFile)->GetModule(); } void Assembly::CacheManifestExportedTypes(AllocMemTracker *pamTracker) @@ -1589,7 +1600,7 @@ MethodDesc* Assembly::GetEntryPoint() Module *pModule = NULL; switch(TypeFromToken(mdEntry)) { case mdtFile: - pModule = m_pModule->LoadModule(::GetAppDomain(), mdEntry)->GetModule(); + pModule = m_pModule->LoadModule(mdEntry)->GetModule(); mdEntry = pModule->GetEntryPointToken(); if ( (TypeFromToken(mdEntry) != mdtMethodDef) || diff --git a/src/coreclr/vm/assembly.hpp b/src/coreclr/vm/assembly.hpp index 564608fa97aa7..8d4cc62b26609 100644 --- a/src/coreclr/vm/assembly.hpp +++ b/src/coreclr/vm/assembly.hpp @@ -285,7 +285,7 @@ class Assembly mdTypeDef mdNested, mdTypeDef *pCL); - static Module * FindModuleByTypeRef(Module * pModule, + static Module * FindModuleByTypeRef(ModuleBase * pModule, mdTypeRef typeRef, Loader::LoadFlag loadFlag, BOOL * pfNoResolutionScope); diff --git a/src/coreclr/vm/assemblynative.cpp b/src/coreclr/vm/assemblynative.cpp index a0879b0290c0f..098fb18791eb1 100644 --- a/src/coreclr/vm/assemblynative.cpp +++ b/src/coreclr/vm/assemblynative.cpp @@ -620,7 +620,7 @@ extern "C" void QCALLTYPE AssemblyNative_GetModules(QCall::AssemblyHandle pAssem { if (fLoadIfNotFound) { - DomainAssembly* pModule = pAssembly->GetModule()->LoadModule(GetAppDomain(), mdFile); + DomainAssembly* pModule = pAssembly->GetModule()->LoadModule(mdFile); modules.Append(pModule); } } diff --git a/src/coreclr/vm/binder.cpp b/src/coreclr/vm/binder.cpp index d9cfb45acda1a..6ab47c8e9b70d 100644 --- a/src/coreclr/vm/binder.cpp +++ b/src/coreclr/vm/binder.cpp @@ -407,7 +407,7 @@ void CoreLibBinder::BuildConvertedSignature(const BYTE* pSig, SigBuilder * pSigB } else { if ((callConv & IMAGE_CEE_CS_CALLCONV_MASK) != IMAGE_CEE_CS_CALLCONV_FIELD) - THROW_BAD_FORMAT(BFA_BAD_SIGNATURE, (Module*)NULL); + THROW_BAD_FORMAT(BFA_BAD_SIGNATURE, (ModuleBase*)NULL); argCount = 0; } diff --git a/src/coreclr/vm/ceeload.cpp b/src/coreclr/vm/ceeload.cpp index 4ffba8ab8a3c6..4c1d93990b727 100644 --- a/src/coreclr/vm/ceeload.cpp +++ b/src/coreclr/vm/ceeload.cpp @@ -80,7 +80,7 @@ BOOL Module::HasReadyToRunInlineTrackingMap() { LIMITED_METHOD_DAC_CONTRACT; #ifdef FEATURE_READYTORUN - if (IsReadyToRun() && GetReadyToRunInfo()->GetInlineTrackingMap() != NULL) + if (IsReadyToRun() && GetReadyToRunInfo()->HasReadyToRunInlineTrackingMap()) { return TRUE; } @@ -92,9 +92,9 @@ COUNT_T Module::GetReadyToRunInliners(PTR_Module inlineeOwnerMod, mdMethodDef in { WRAPPER_NO_CONTRACT; #ifdef FEATURE_READYTORUN - if(IsReadyToRun() && GetReadyToRunInfo()->GetInlineTrackingMap() != NULL) + if(HasReadyToRunInlineTrackingMap()) { - return GetReadyToRunInfo()->GetInlineTrackingMap()->GetInliners(inlineeOwnerMod, inlineeTkn, inlinersSize, inliners, incompleteData); + return GetReadyToRunInfo()->GetInliners(inlineeOwnerMod, inlineeTkn, inlinersSize, inliners, incompleteData); } #endif return 0; @@ -341,6 +341,7 @@ Module::Module(Assembly *pAssembly, mdFile moduleRef, PEAssembly *pPEAssembly) PREFIX_ASSUME(pAssembly != NULL); + m_loaderAllocator = NULL; m_pAssembly = pAssembly; m_moduleRef = moduleRef; m_pPEAssembly = pPEAssembly; @@ -419,6 +420,7 @@ void Module::Initialize(AllocMemTracker *pamTracker, LPCWSTR szName) } CONTRACTL_END; + m_loaderAllocator = GetAssembly()->GetLoaderAllocator(); m_pSimpleName = m_pPEAssembly->GetSimpleName(); m_Crst.Init(CrstModule); @@ -478,21 +480,6 @@ void Module::Initialize(AllocMemTracker *pamTracker, LPCWSTR szName) m_pInstMethodHashTable = InstMethodHashTable::Create(GetLoaderAllocator(), this, PARAMMETHODS_HASH_BUCKETS, pamTracker); } - if (m_pMemberRefToDescHashTable == NULL) - { - if (IsReflection()) - { - m_pMemberRefToDescHashTable = MemberRefToDescHashTable::Create(this, MEMBERREF_MAP_INITIAL_SIZE, pamTracker); - } - else - { - IMDInternalImport* pImport = GetMDImport(); - - // Get #MemberRefs and create memberrefToDesc hash table - m_pMemberRefToDescHashTable = MemberRefToDescHashTable::Create(this, pImport->GetCountWithTokenKind(mdtMemberRef) + 1, pamTracker); - } - } - // this will be initialized a bit later. m_ModuleID = NULL; m_ModuleIndex.m_dwIndex = (SIZE_T)-1; @@ -524,133 +511,6 @@ void Module::Initialize(AllocMemTracker *pamTracker, LPCWSTR szName) #endif // DACCESS_COMPILE -#ifndef DACCESS_COMPILE -MemberRefToDescHashTable* MemberRefToDescHashTable::Create(Module *pModule, DWORD cInitialBuckets, AllocMemTracker *pamTracker) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - INJECT_FAULT(COMPlusThrowOM();); - PRECONDITION(!FORBIDGC_LOADER_USE_ENABLED()); - } - CONTRACTL_END; - - LoaderHeap *pHeap = pModule->GetAssembly()->GetLowFrequencyHeap(); - MemberRefToDescHashTable *pThis = (MemberRefToDescHashTable*)pamTracker->Track(pHeap->AllocMem((S_SIZE_T)sizeof(MemberRefToDescHashTable))); - - // The base class get initialized through chaining of constructors. We allocated the hash instance via the - // loader heap instead of new so use an in-place new to call the constructors now. - new (pThis) MemberRefToDescHashTable(pModule, pHeap, cInitialBuckets); - - return pThis; -} - -//Inserts FieldRef -MemberRefToDescHashEntry* MemberRefToDescHashTable::Insert(mdMemberRef token , FieldDesc *value) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - INJECT_FAULT(COMPlusThrowOM();); - PRECONDITION(!FORBIDGC_LOADER_USE_ENABLED()); - } - CONTRACTL_END; - - LookupContext sAltContext; - - _ASSERTE((dac_cast(value) & IS_FIELD_MEMBER_REF) == 0); - - MemberRefToDescHashEntry *pEntry = (PTR_MemberRefToDescHashEntry) BaseFindFirstEntryByHash(RidFromToken(token), &sAltContext); - if (pEntry != NULL) - { - // If memberRef is hot token in that case entry for memberref is already persisted in ngen image. So entry for it will already be present in hash table. - // However its value will be null. We need to set its actual value. - if(pEntry->m_value == dac_cast(NULL)) - { - pEntry->m_value = dac_cast(value)|IS_FIELD_MEMBER_REF; - } - - _ASSERTE(pEntry->m_value == (dac_cast(value)|IS_FIELD_MEMBER_REF)); - return pEntry; - } - - // For non hot tokens insert new entry in hashtable - pEntry = BaseAllocateEntry(NULL); - pEntry->m_value = dac_cast(value)|IS_FIELD_MEMBER_REF; - BaseInsertEntry(RidFromToken(token), pEntry); - - return pEntry; -} - -// Insert MethodRef -MemberRefToDescHashEntry* MemberRefToDescHashTable::Insert(mdMemberRef token , MethodDesc *value) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_ANY; - INJECT_FAULT(COMPlusThrowOM();); - PRECONDITION(!FORBIDGC_LOADER_USE_ENABLED()); - } - CONTRACTL_END; - - LookupContext sAltContext; - - MemberRefToDescHashEntry *pEntry = (PTR_MemberRefToDescHashEntry) BaseFindFirstEntryByHash(RidFromToken(token), &sAltContext); - if (pEntry != NULL) - { - // If memberRef is hot token in that case entry for memberref is already persisted in ngen image. So entry for it will already be present in hash table. - // However its value will be null. We need to set its actual value. - if(pEntry->m_value == dac_cast(NULL)) - { - pEntry->m_value = dac_cast(value); - } - - _ASSERTE(pEntry->m_value == dac_cast(value)); - return pEntry; - } - - // For non hot tokens insert new entry in hashtable - pEntry = BaseAllocateEntry(NULL); - pEntry->m_value = dac_cast(value); - BaseInsertEntry(RidFromToken(token), pEntry); - - return pEntry; -} - -#endif // !DACCESS_COMPILE - -PTR_MemberRef MemberRefToDescHashTable::GetValue(mdMemberRef token, BOOL *pfIsMethod) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - SUPPORTS_DAC; - } - CONTRACTL_END; - - LookupContext sAltContext; - - MemberRefToDescHashEntry *pEntry = (PTR_MemberRefToDescHashEntry) BaseFindFirstEntryByHash(RidFromToken(token), &sAltContext); - if (pEntry != NULL) - { - if(pEntry->m_value & IS_FIELD_MEMBER_REF) - *pfIsMethod = FALSE; - else - *pfIsMethod = TRUE; - return (PTR_MemberRef)(pEntry->m_value & (~MEMBER_REF_MAP_ALL_FLAGS)); - } - - return NULL; -} - void Module::SetDebuggerInfoBits(DebuggerAssemblyControlFlags newBits) { @@ -1993,6 +1853,7 @@ void Module::AllocateMaps() { TYPEDEF_MAP_INITIAL_SIZE = 5, TYPEREF_MAP_INITIAL_SIZE = 5, + MEMBERREF_MAP_INITIAL_SIZE = 10, MEMBERDEF_MAP_INITIAL_SIZE = 10, GENERICPARAM_MAP_INITIAL_SIZE = 5, GENERICTYPEDEF_MAP_INITIAL_SIZE = 5, @@ -2012,6 +1873,7 @@ void Module::AllocateMaps() // The above is essential. The following ones are precautionary. m_TypeRefToMethodTableMap.dwCount = TYPEREF_MAP_INITIAL_SIZE; + m_MemberRefMap.dwCount = MEMBERREF_MAP_INITIAL_SIZE; m_MethodDefToDescMap.dwCount = MEMBERDEF_MAP_INITIAL_SIZE; m_FieldDefToDescMap.dwCount = MEMBERDEF_MAP_INITIAL_SIZE; m_GenericParamToDescMap.dwCount = GENERICPARAM_MAP_INITIAL_SIZE; @@ -2030,6 +1892,9 @@ void Module::AllocateMaps() // Get # TypeRefs m_TypeRefToMethodTableMap.dwCount = pImport->GetCountWithTokenKind(mdtTypeRef)+1; + // Get # MemberRefs + m_MemberRefMap.dwCount = pImport->GetCountWithTokenKind(mdtMemberRef)+1; + // Get # MethodDefs m_MethodDefToDescMap.dwCount = pImport->GetCountWithTokenKind(mdtMethodDef)+1; @@ -2053,6 +1918,7 @@ void Module::AllocateMaps() nTotal += m_TypeDefToMethodTableMap.dwCount; nTotal += m_TypeRefToMethodTableMap.dwCount; + nTotal += m_MemberRefMap.dwCount; nTotal += m_MethodDefToDescMap.dwCount; nTotal += m_FieldDefToDescMap.dwCount; nTotal += m_GenericParamToDescMap.dwCount; @@ -2075,9 +1941,13 @@ void Module::AllocateMaps() m_TypeRefToMethodTableMap.supportedFlags = TYPE_REF_MAP_ALL_FLAGS; m_TypeRefToMethodTableMap.pTable = &pTable[m_TypeDefToMethodTableMap.dwCount]; + m_MemberRefMap.pNext = NULL; + m_MemberRefMap.supportedFlags = MEMBER_REF_MAP_ALL_FLAGS; + m_MemberRefMap.pTable = &m_TypeRefToMethodTableMap.pTable[m_TypeRefToMethodTableMap.dwCount]; + m_MethodDefToDescMap.pNext = NULL; m_MethodDefToDescMap.supportedFlags = METHOD_DEF_MAP_ALL_FLAGS; - m_MethodDefToDescMap.pTable = &m_TypeRefToMethodTableMap.pTable[m_TypeRefToMethodTableMap.dwCount]; + m_MethodDefToDescMap.pTable = &m_MemberRefMap.pTable[m_MemberRefMap.dwCount]; m_FieldDefToDescMap.pNext = NULL; m_FieldDefToDescMap.supportedFlags = FIELD_DEF_MAP_ALL_FLAGS; @@ -2220,12 +2090,6 @@ void Module::StartUnload() SetBeingUnloaded(); } -BOOL Module::IsInCurrentVersionBubble() -{ - LIMITED_METHOD_CONTRACT; - return TRUE; -} - #if defined(FEATURE_READYTORUN) //--------------------------------------------------------------------------------------- // Check if the target module is in the same version bubble as this one @@ -2253,7 +2117,6 @@ BOOL Module::IsInSameVersionBubble(Module *target) } NativeImage *nativeImage = this->GetCompositeNativeImage(); - IMDInternalImport* pMdImport = NULL; if (nativeImage != NULL) { @@ -2262,20 +2125,12 @@ BOOL Module::IsInSameVersionBubble(Module *target) // Fast path for modules contained within the same native image return TRUE; } - pMdImport = nativeImage->GetManifestMetadata(); - } - else - { - // Check if the current module's image has native manifest metadata, otherwise the current->GetNativeAssemblyImport() asserts. - COUNT_T cMeta=0; - const void* pMeta = GetPEAssembly()->GetPEImage()->GetNativeManifestMetadata(&cMeta); - if (pMeta == NULL) - { - return FALSE; - } - pMdImport = GetNativeAssemblyImport(); } + IMDInternalImport* pMdImport = GetReadyToRunInfo()->GetNativeManifestModule()->GetMDImport(); + if (pMdImport == NULL) + return FALSE; + LPCUTF8 targetName = target->GetAssembly()->GetSimpleName(); HENUMInternal assemblyEnum; @@ -2920,7 +2775,7 @@ UINT32 Module::GetTlsIndex() // getting a false sense of security (in addition to its functional shortcomings) #ifndef DACCESS_COMPILE -BOOL Module::IsSigInIL(PCCOR_SIGNATURE signature) +BOOL Module::IsSigInILImpl(PCCOR_SIGNATURE signature) { CONTRACTL { @@ -2935,7 +2790,7 @@ BOOL Module::IsSigInIL(PCCOR_SIGNATURE signature) return m_pPEAssembly->IsPtrInPEImage(signature); } -void Module::InitializeStringData(DWORD token, EEStringData *pstrData, CQuickBytes *pqb) +void ModuleBase::InitializeStringData(DWORD token, EEStringData *pstrData, CQuickBytes *pqb) { CONTRACTL { @@ -2980,7 +2835,7 @@ void Module::InitializeStringData(DWORD token, EEStringData *pstrData, CQuickByt } -OBJECTHANDLE Module::ResolveStringRef(DWORD token, BaseDomain *pDomain) +OBJECTHANDLE ModuleBase::ResolveStringRef(DWORD token) { CONTRACTL { @@ -3005,16 +2860,9 @@ OBJECTHANDLE Module::ResolveStringRef(DWORD token, BaseDomain *pDomain) GCX_COOP(); - // We can only do this for native images as they guarantee that resolvestringref will be - // called only once per string from this module. @TODO: We really dont have any way of asserting - // this, which would be nice... (and is needed to guarantee correctness) - // Retrieve the string from the either the appropriate LoaderAllocator LoaderAllocator *pLoaderAllocator; - if (this->IsCollectible()) - pLoaderAllocator = this->GetLoaderAllocator(); - else - pLoaderAllocator = pDomain->GetLoaderAllocator(); + pLoaderAllocator = this->GetLoaderAllocator(); string = (OBJECTHANDLE)pLoaderAllocator->GetStringObjRefPtrFromUnicodeString(&strData); @@ -3083,6 +2931,131 @@ void Module::AddActiveDependency(Module *pModule, BOOL unconditional) #endif //!DACCESS_COMPILE +Assembly * +ModuleBase::GetAssemblyIfLoaded( + mdAssemblyRef kAssemblyRef, + IMDInternalImport * pMDImportOverride, // = NULL + BOOL fDoNotUtilizeExtraChecks, // = FALSE + AssemblyBinder *pBinderForLoadedAssembly // = NULL +) +{ + CONTRACT(Assembly *) + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + MODE_ANY; + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); + SUPPORTS_DAC; + } + CONTRACT_END; + + Assembly * pAssembly = NULL; + BOOL fCanUseRidMap = pMDImportOverride == NULL; + +#ifdef _DEBUG + fCanUseRidMap = fCanUseRidMap && (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GetAssemblyIfLoadedIgnoreRidMap) == 0); +#endif + + + // Don't do a lookup if an override IMDInternalImport is provided, since the lookup is for the + // standard IMDInternalImport and might result in an incorrect result. + if (fCanUseRidMap) + { + pAssembly = LookupAssemblyRef(kAssemblyRef); + } + +#ifndef DACCESS_COMPILE + // Check if actually loaded, unless a GC is in progress or the current thread is + // walking the stack (either its own stack, or another thread's stack) as that works + // only with loaded assemblies + // + // NOTE: The case where the current thread is walking a stack can be problematic for + // other reasons, as the remaining code of this function uses "GetAppDomain()", when + // in fact the right AppDomain to use is the one corresponding to the frame being + // traversed on the walked thread. Dev10 TFS bug# 762348 tracks that issue. + if ((pAssembly != NULL) && !IsGCThread() && !IsStackWalkerThread()) + { + _ASSERTE(::GetAppDomain() != NULL); + DomainAssembly * pDomainAssembly = pAssembly->GetDomainAssembly(); + if ((pDomainAssembly == NULL) || !pDomainAssembly->IsLoaded()) + pAssembly = NULL; + } +#endif //!DACCESS_COMPILE + + if (pAssembly == NULL) + { + do + { + AppDomain * pAppDomainExamine = AppDomain::GetCurrentDomain(); + _ASSERTE(!"Handle remote load scenarios"); +#ifdef ENABLE_LATER + DomainAssembly * pCurAssemblyInExamineDomain = GetAssembly()->GetDomainAssembly(); + if (pCurAssemblyInExamineDomain == NULL) + { + continue; + } + +#ifndef DACCESS_COMPILE + { + IMDInternalImport * pMDImport = (pMDImportOverride == NULL) ? (GetMDImport()) : (pMDImportOverride); + + //we have to be very careful here. + //we are using InitializeSpecInternal so we need to make sure that under no condition + //the data we pass to it can outlive the assembly spec. + AssemblySpec spec; + if (FAILED(spec.InitializeSpecInternal(kAssemblyRef, + pMDImport, + pCurAssemblyInExamineDomain, + FALSE /*fAllowAllocation*/))) + { + continue; + } + + // If we have been passed the binding context for the loaded assembly that is being looked up in the + // cache, then set it up in the AssemblySpec for the cache lookup to use it below. + if (pBinderForLoadedAssembly != NULL) + { + _ASSERTE(spec.GetBinder() == NULL); + spec.SetBinder(pBinderForLoadedAssembly); + } + DomainAssembly * pDomainAssembly = nullptr; + + { + pDomainAssembly = pAppDomainExamine->FindCachedAssembly(&spec, FALSE /*fThrow*/); + } + + if (pDomainAssembly && pDomainAssembly->IsLoaded()) + pAssembly = pDomainAssembly->GetAssembly(); + + // Only store in the rid map if working with the current AppDomain. + if (fCanUseRidMap && pAssembly) + StoreAssemblyRef(kAssemblyRef, pAssembly); + + if (pAssembly != NULL) + break; + } +#endif //!DACCESS_COMPILE +#endif // ENABLE_LATER + } while (false); + } + + // When walking the stack or computing GC information this function should never fail. + _ASSERTE((pAssembly != NULL) || !(IsStackWalkerThread() || IsGCThread())); + +#ifdef DACCESS_COMPILE + + // Note: In rare cases when debugger walks the stack, we could actually have pAssembly=NULL here. + // To fix that we should DACize the AppDomain-iteration code above (especially AssemblySpec). + _ASSERTE(pAssembly != NULL); + +#endif //DACCESS_COMPILE + + RETURN pAssembly; +} // Module::GetAssemblyIfLoaded + + Assembly * Module::GetAssemblyIfLoaded( mdAssemblyRef kAssemblyRef, @@ -3206,7 +3179,7 @@ Module::GetAssemblyIfLoaded( } // Module::GetAssemblyIfLoaded DWORD -Module::GetAssemblyRefFlags( +ModuleBase::GetAssemblyRefFlags( mdAssemblyRef tkAssemblyRef) { CONTRACTL @@ -3238,7 +3211,7 @@ Module::GetAssemblyRefFlags( } // Module::GetAssemblyRefFlags #ifndef DACCESS_COMPILE -DomainAssembly * Module::LoadAssembly(mdAssemblyRef kAssemblyRef) +DomainAssembly * Module::LoadAssemblyImpl(mdAssemblyRef kAssemblyRef) { CONTRACT(DomainAssembly *) { @@ -3297,7 +3270,12 @@ DomainAssembly * Module::LoadAssembly(mdAssemblyRef kAssemblyRef) RETURN pDomainAssembly; } - +#else +DomainAssembly * Module::LoadAssemblyImpl(mdAssemblyRef kAssemblyRef) +{ + WRAPPER_NO_CONTRACT; + ThrowHR(E_FAIL); +} #endif // !DACCESS_COMPILE Module *Module::GetModuleIfLoaded(mdFile kFile) @@ -3335,51 +3313,20 @@ Module *Module::GetModuleIfLoaded(mdFile kFile) RETURN GetAssembly()->GetModule()->GetModuleIfLoaded(kFile); } - Module *pModule = LookupFile(kFile); - if (pModule == NULL) + if (kFile == mdFileNil) { - if (IsManifest()) - { - if (kFile == mdFileNil) - pModule = GetAssembly()->GetModule(); - } - else - { - // If we didn't find it there, look at the "master rid map" in the manifest file - Assembly *pAssembly = GetAssembly(); - mdFile kMatch; - - // This is required only because of some lower casing on the name - kMatch = pAssembly->GetManifestFileToken(GetMDImport(), kFile); - if (IsNilToken(kMatch)) - { - if (kMatch == mdFileNil) - { - pModule = pAssembly->GetModule(); - } - else - { - RETURN NULL; - } - } - else - pModule = pAssembly->GetModule()->LookupFile(kMatch); - } - #ifndef DACCESS_COMPILE - if (pModule != NULL) - StoreFileNoThrow(kFile, pModule); + StoreFileNoThrow(kFile, this); #endif + return this; } -#ifndef DACCESS_COMPILE -#endif // !DACCESS_COMPILE - RETURN pModule; + RETURN NULL; } #ifndef DACCESS_COMPILE -DomainAssembly *Module::LoadModule(AppDomain *pDomain, mdFile kFile) +DomainAssembly *ModuleBase::LoadModule(mdFile kFile) { CONTRACT(DomainAssembly *) { @@ -3401,7 +3348,7 @@ DomainAssembly *Module::LoadModule(AppDomain *pDomain, mdFile kFile) else { // This is mdtFile - IfFailThrow(GetAssembly()->GetMDImport()->GetFileProps(kFile, + IfFailThrow(GetMDImport()->GetFileProps(kFile, &psModuleName, NULL, NULL, @@ -3444,25 +3391,11 @@ PTR_Module Module::LookupModule(mdToken kFile) } PTR_Module pModule = LookupFile(kFile); - if (pModule == NULL && !IsManifest()) - { - // If we didn't find it there, look at the "master rid map" in the manifest file - Assembly *pAssembly = GetAssembly(); - mdFile kMatch = pAssembly->GetManifestFileToken(GetMDImport(), kFile); - if (IsNilToken(kMatch)) { - if (kMatch == mdFileNil) - pModule = pAssembly->GetModule(); - else - COMPlusThrowHR(COR_E_BADIMAGEFORMAT); - } - else - pModule = pAssembly->GetModule()->LookupFile(kMatch); - } RETURN pModule; } -TypeHandle Module::LookupTypeRef(mdTypeRef token) +TypeHandle ModuleBase::LookupTypeRef(mdTypeRef token) { STATIC_CONTRACT_NOTHROW; STATIC_CONTRACT_GC_NOTRIGGER; @@ -3487,7 +3420,7 @@ TypeHandle Module::LookupTypeRef(mdTypeRef token) // This function must also check that another thread didn't already add a LookupMap capable // of containing the same RID. // -PTR_TADDR LookupMapBase::GrowMap(Module * pModule, DWORD rid) +PTR_TADDR LookupMapBase::GrowMap(ModuleBase * pModule, DWORD rid) { CONTRACT(PTR_TADDR) { @@ -4407,9 +4340,9 @@ LoaderHeap *Module::GetThunkHeap() RETURN m_pThunkHeap; } -Module *Module::GetModuleFromIndex(DWORD ix) +ModuleBase *Module::GetModuleFromIndex(DWORD ix) { - CONTRACT(Module*) + CONTRACT(ModuleBase*) { INSTANCE_CHECK; THROWS; @@ -4441,9 +4374,9 @@ Module *Module::GetModuleFromIndex(DWORD ix) #endif // !DACCESS_COMPILE -Module *Module::GetModuleFromIndexIfLoaded(DWORD ix) +ModuleBase *Module::GetModuleFromIndexIfLoaded(DWORD ix) { - CONTRACT(Module*) + CONTRACT(ModuleBase*) { INSTANCE_CHECK; NOTHROW; @@ -4479,7 +4412,7 @@ IMDInternalImport* Module::GetNativeAssemblyImport(BOOL loadAllowed) } CONTRACT_END; - RETURN GetPEAssembly()->GetPEImage()->GetNativeMDImport(loadAllowed); + RETURN GetReadyToRunInfo()->GetNativeManifestModule()->GetMDImport(); } BYTE* Module::GetNativeFixupBlobData(RVA rva) @@ -5363,7 +5296,7 @@ void Module::EnumMemoryRegions(CLRDataEnumMemoryFlags flags, // Save the LookupMap structures. m_MethodDefToDescMap.ListEnumMemoryRegions(flags); m_FieldDefToDescMap.ListEnumMemoryRegions(flags); - m_pMemberRefToDescHashTable->EnumMemoryRegions(flags); + m_MemberRefMap.ListEnumMemoryRegions(flags); m_GenericParamToDescMap.ListEnumMemoryRegions(flags); m_GenericTypeDefToCanonMethodTableMap.ListEnumMemoryRegions(flags); m_FileReferencesMap.ListEnumMemoryRegions(flags); @@ -5491,6 +5424,18 @@ LPCWSTR Module::GetPathForErrorMessages() } } +LPCWSTR ModuleBase::GetPathForErrorMessages() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + FORBID_FAULT; + } + CONTRACTL_END; + return W(""); +} + #if defined(_DEBUG) && !defined(DACCESS_COMPILE) && !defined(CROSS_COMPILE) void Module::ExpandAll() { @@ -5761,3 +5706,21 @@ void EEConfig::DebugCheckAndForceIBCFailure(BitForMask bitForMask) } } #endif // defined(_DEBUG) && !defined(DACCESS_COMPILE) + +#ifdef DACCESS_COMPILE +void DECLSPEC_NORETURN ModuleBase::ThrowTypeLoadExceptionImpl(IMDInternalImport *pInternalImport, + mdToken token, + UINT resIDWhy) +{ + WRAPPER_NO_CONTRACT; + ThrowHR(E_FAIL); +} +#else +void DECLSPEC_NORETURN Module::ThrowTypeLoadExceptionImpl(IMDInternalImport *pInternalImport, + mdToken token, + UINT resIDWhy) +{ + WRAPPER_NO_CONTRACT; + GetAssembly()->ThrowTypeLoadException(pInternalImport, token, NULL, resIDWhy); +} +#endif \ No newline at end of file diff --git a/src/coreclr/vm/ceeload.h b/src/coreclr/vm/ceeload.h index ff980ed5cfd17..396351a594c72 100644 --- a/src/coreclr/vm/ceeload.h +++ b/src/coreclr/vm/ceeload.h @@ -128,7 +128,7 @@ struct LookupMapBase } PTR_TADDR GetElementPtr(DWORD rid); - PTR_TADDR GrowMap(Module * pModule, DWORD rid); + PTR_TADDR GrowMap(ModuleBase * pModule, DWORD rid); // Get number of RIDs that this table can store DWORD GetSize(); @@ -152,7 +152,7 @@ struct LookupMap : LookupMapBase TYPE GetElement(DWORD rid, TADDR* pFlags); void SetElement(DWORD rid, TYPE value, TADDR flags); BOOL TrySetElement(DWORD rid, TYPE value, TADDR flags); - void AddElement(Module * pModule, DWORD rid, TYPE value, TADDR flags); + void AddElement(ModuleBase * pModule, DWORD rid, TYPE value, TADDR flags); void EnsureElementCanBeStored(Module * pModule, DWORD rid); DWORD Find(TYPE value, TADDR* flags); @@ -249,14 +249,14 @@ struct LookupMap : LookupMapBase // // Stores an association in a map. Grows the map as necessary. // - void AddElement(Module * pModule, DWORD rid, TYPE value) + void AddElement(ModuleBase * pModule, DWORD rid, TYPE value) { WRAPPER_NO_CONTRACT; AddElement(pModule, rid, value, 0); } - void AddElementWithFlags(Module * pModule, DWORD rid, TYPE value, TADDR flags) + void AddElementWithFlags(ModuleBase * pModule, DWORD rid, TYPE value, TADDR flags) { WRAPPER_NO_CONTRACT; @@ -429,41 +429,6 @@ class DynamicILBlobTraits : public NoRemoveSHashTraits DynamicILBlobTable; typedef DPTR(DynamicILBlobTable) PTR_DynamicILBlobTable; -//Hash for MemberRef to Desc tables (fieldDesc or MethodDesc) -typedef DPTR(struct MemberRefToDescHashEntry) PTR_MemberRefToDescHashEntry; - -struct MemberRefToDescHashEntry -{ - TADDR m_value; -}; - -typedef DPTR(class MemberRefToDescHashTable) PTR_MemberRefToDescHashTable; - -#define MEMBERREF_MAP_INITIAL_SIZE 10 - -class MemberRefToDescHashTable: public DacEnumerableHashTable -{ -#ifndef DACCESS_COMPILE - -private: - MemberRefToDescHashTable(Module *pModule, LoaderHeap *pHeap, DWORD cInitialBuckets): - DacEnumerableHashTable(pModule, pHeap, cInitialBuckets) - { LIMITED_METHOD_CONTRACT; } - -public: - - static MemberRefToDescHashTable* Create(Module *pModule, DWORD cInitialBuckets, AllocMemTracker *pamTracker); - - MemberRefToDescHashEntry* Insert(mdMemberRef token, MethodDesc *value); - MemberRefToDescHashEntry* Insert(mdMemberRef token , FieldDesc *value); -#endif //!DACCESS_COMPILE - -public: - typedef DacEnumerableHashTable::LookupContext LookupContext; - - PTR_MemberRef GetValue(mdMemberRef token, BOOL *pfIsMethod); -}; - #ifdef FEATURE_READYTORUN typedef DPTR(class ReadyToRunInfo) PTR_ReadyToRunInfo; #endif @@ -482,7 +447,7 @@ struct ThreadLocalModule; // // See file:..\inc\corhdr.h#ManagedHeader for more on the layout of managed exectuable files. -class Module +class ModuleBase { #ifdef DACCESS_COMPILE friend class ClrDataAccess; @@ -491,7 +456,152 @@ class Module friend class DataImage; - VPTR_BASE_CONCRETE_VTABLE_CLASS(Module) + VPTR_BASE_VTABLE_CLASS(ModuleBase) + +protected: + // Linear mapping from TypeRef token to TypeHandle * + LookupMap m_TypeRefToMethodTableMap; + // Mapping of AssemblyRef token to Module * + LookupMap m_ManifestModuleReferencesMap; + + // mapping from MemberRef token to MethodDesc*, FieldDesc* + LookupMap m_MemberRefMap; + + // For protecting additions to the heap + CrstExplicitInit m_LookupTableCrst; + + PTR_LoaderAllocator m_loaderAllocator; + + // The vtable needs to match between DAC and non-DAC, but we don't want any use of IsSigInIL in the DAC + virtual BOOL IsSigInILImpl(PCCOR_SIGNATURE signature) { return FALSE; } // ModuleBase doesn't have a PE image to examine + // The vtable needs to match between DAC and non-DAC, but we don't want any use of LoadAssembly in the DAC + virtual DomainAssembly * LoadAssemblyImpl(mdAssemblyRef kAssemblyRef) = 0; + + // The vtable needs to match between DAC and non-DAC, but we don't want any use of ThrowTypeLoadException in the DAC + virtual void DECLSPEC_NORETURN ThrowTypeLoadExceptionImpl(IMDInternalImport *pInternalImport, + mdToken token, + UINT resIDWhy) +#ifndef DACCESS_COMPILE + = 0; +#else + ; +#endif + +public: + ModuleBase() = default; + + virtual LPCWSTR GetPathForErrorMessages(); + + CrstBase *GetLookupTableCrst() + { + LIMITED_METHOD_CONTRACT; + return &m_LookupTableCrst; + } + + PTR_LoaderAllocator GetLoaderAllocator() + { + LIMITED_METHOD_DAC_CONTRACT; + return m_loaderAllocator; + } + + FORCEINLINE TADDR LookupMemberRef(mdMemberRef token, BOOL *pfIsMethod) + { + WRAPPER_NO_CONTRACT; + + _ASSERTE(TypeFromToken(token) == mdtMemberRef); + + TADDR flags; + TADDR pResult = m_MemberRefMap.GetElementAndFlags(RidFromToken(token), &flags); + *pfIsMethod = !(flags & IS_FIELD_MEMBER_REF); + + return pResult; + } +#ifndef DACCESS_COMPILE + void StoreMemberRef(mdMemberRef token, FieldDesc *value) + { + WRAPPER_NO_CONTRACT; + + _ASSERTE(TypeFromToken(token) == mdtMemberRef); + m_MemberRefMap.AddElementWithFlags(this, RidFromToken(token), (TADDR)value, IS_FIELD_MEMBER_REF); + } + void StoreMemberRef(mdMemberRef token, MethodDesc *value) + { + WRAPPER_NO_CONTRACT; + + _ASSERTE(TypeFromToken(token) == mdtMemberRef); + m_MemberRefMap.AddElementWithFlags(this, RidFromToken(token), (TADDR)value, 0); + } +#endif // !DACCESS_COMPILE + + TypeHandle LookupTypeRef(mdTypeRef token); + virtual IMDInternalImport *GetMDImport() const = 0; + virtual bool IsFullModule() const { return false; } + + + void StoreTypeRef(mdTypeRef token, TypeHandle value) + { + WRAPPER_NO_CONTRACT; + + _ASSERTE(TypeFromToken(token) == mdtTypeRef); + + // The TypeRef cache is strictly a lookaside cache. If we get an OOM trying to grow the table, + // we cannot abort the load. (This will cause fatal errors during gc promotion.) + m_TypeRefToMethodTableMap.TrySetElement(RidFromToken(token), + dac_cast(value.AsTAddr())); + } + virtual PTR_Module LookupModule(mdToken kFile) { return NULL; }; //wrapper over GetModuleIfLoaded, takes modulerefs as well + virtual Module *GetModuleIfLoaded(mdFile kFile) { return NULL; }; +#ifndef DACCESS_COMPILE + virtual DomainAssembly *LoadModule(mdFile kFile); +#endif + DWORD GetAssemblyRefFlags(mdAssemblyRef tkAssemblyRef); + + Assembly *LookupAssemblyRef(mdAssemblyRef token); + // Module/Assembly traversal + virtual Assembly * GetAssemblyIfLoaded( + mdAssemblyRef kAssemblyRef, + IMDInternalImport * pMDImportOverride = NULL, + BOOL fDoNotUtilizeExtraChecks = FALSE, + AssemblyBinder *pBinderForLoadedAssembly = NULL + ); + + +#ifndef DACCESS_COMPILE + // The vtable needs to match between DAC and non-DAC, but we don't want any use of ThrowTypeLoadException in the DAC + void DECLSPEC_NORETURN ThrowTypeLoadException(IMDInternalImport *pInternalImport, + mdToken token, + UINT resIDWhy) + { + ThrowTypeLoadExceptionImpl(pInternalImport, token, resIDWhy); + } + + // The vtable needs to match between DAC and non-DAC, but we don't want any use of IsSigInIL in the DAC + BOOL IsSigInIL(PCCOR_SIGNATURE signature) { return IsSigInILImpl(signature); } + DomainAssembly * LoadAssembly(mdAssemblyRef kAssemblyRef) + { + WRAPPER_NO_CONTRACT; + return LoadAssemblyImpl(kAssemblyRef); + } + + // Resolving + OBJECTHANDLE ResolveStringRef(DWORD Token); +private: + // string helper + void InitializeStringData(DWORD token, EEStringData *pstrData, CQuickBytes *pqb); +#endif + +}; + +class Module : public ModuleBase +{ +#ifdef DACCESS_COMPILE + friend class ClrDataAccess; + friend class NativeImageDumper; +#endif + + friend class DataImage; + + VPTR_VTABLE_CLASS(Module, ModuleBase) private: PTR_CUTF8 m_pSimpleName; // Cached simple name for better performance and easier diagnostics @@ -605,9 +715,6 @@ class Module // Debugger may retrieve this from out-of-process. PTR_CGrowableStream m_pIStreamSym; - // For protecting additions to the heap - CrstExplicitInit m_LookupTableCrst; - #define TYPE_DEF_MAP_ALL_FLAGS NO_MAP_FLAGS #define TYPE_REF_MAP_ALL_FLAGS NO_MAP_FLAGS @@ -638,9 +745,6 @@ class Module // For generic types, IsGenericTypeDefinition() is true i.e. instantiation at formals LookupMap m_TypeDefToMethodTableMap; - // Linear mapping from TypeRef token to TypeHandle * - LookupMap m_TypeRefToMethodTableMap; - // Linear mapping from MethodDef token to MethodDesc * // For generic methods, IsGenericTypeDefinition() is true i.e. instantiation at formals LookupMap m_MethodDefToDescMap; @@ -648,9 +752,6 @@ class Module // Linear mapping from FieldDef token to FieldDesc* LookupMap m_FieldDefToDescMap; - // mapping from MemberRef token to MethodDesc*, FieldDesc* - PTR_MemberRefToDescHashTable m_pMemberRefToDescHashTable; - // Linear mapping from GenericParam token to TypeVarTypeDesc* LookupMap m_GenericParamToDescMap; @@ -663,9 +764,6 @@ class Module // Mapping from File token to Module * LookupMap m_FileReferencesMap; - // Mapping of AssemblyRef token to Module * - LookupMap m_ManifestModuleReferencesMap; - // Mapping from MethodDef token to pointer-sized value encoding property information LookupMap m_MethodDefToPropertyInfoMap; @@ -708,6 +806,7 @@ class Module // 2) Needs to be here for ngen DWORD m_dwDebuggerJMCProbeCount; + bool IsFullModule() const final { return true; } // We can skip the JMC probes if we know that a module has no JMC stuff // inside. So keep a strict count of all functions inside us. bool HasAnyJMCFunctions(); @@ -796,8 +895,6 @@ class Module virtual void Destruct(); #endif - PTR_LoaderAllocator GetLoaderAllocator(); - PTR_PEAssembly GetPEAssembly() const { LIMITED_METHOD_DAC_CONTRACT; return m_pPEAssembly; } BOOL IsManifest(); @@ -935,7 +1032,7 @@ class Module return GetMDImport()->GetCustomAttributeByName(parentToken, GetWellKnownAttributeName(attribute), ppData, pcbData); } - IMDInternalImport *GetMDImport() const + IMDInternalImport *GetMDImport() const final { WRAPPER_NO_CONTRACT; SUPPORTS_DAC; @@ -967,14 +1064,12 @@ class Module HRESULT GetReadablePublicMetaDataInterface(DWORD dwOpenFlags, REFIID riid, LPVOID * ppvInterface); #endif // !DACCESS_COMPILE - BOOL IsInCurrentVersionBubble(); - #if defined(FEATURE_READYTORUN) BOOL IsInSameVersionBubble(Module *target); #endif // FEATURE_READYTORUN - LPCWSTR GetPathForErrorMessages(); + LPCWSTR GetPathForErrorMessages() final; #ifdef FEATURE_ISYM_READER @@ -1099,12 +1194,6 @@ class Module // the class load, which avoids the need for a 'being loaded' list MethodTable* CreateArrayMethodTable(TypeHandle elemType, CorElementType kind, unsigned rank, class AllocMemTracker *pamTracker); - // string helper - void InitializeStringData(DWORD token, EEStringData *pstrData, CQuickBytes *pqb); - - // Resolving - OBJECTHANDLE ResolveStringRef(DWORD Token, BaseDomain *pDomain); - CHECK CheckStringRef(RVA rva); // Module/Assembly traversal @@ -1113,15 +1202,19 @@ class Module IMDInternalImport * pMDImportOverride = NULL, BOOL fDoNotUtilizeExtraChecks = FALSE, AssemblyBinder *pBinderForLoadedAssembly = NULL - ); + ) final; -public: +protected: +#ifndef DACCESS_COMPILE + void DECLSPEC_NORETURN ThrowTypeLoadExceptionImpl(IMDInternalImport *pInternalImport, + mdToken token, + UINT resIDWhy) final; +#endif - DomainAssembly * LoadAssembly(mdAssemblyRef kAssemblyRef); - Module *GetModuleIfLoaded(mdFile kFile); - DomainAssembly *LoadModule(AppDomain *pDomain, mdFile kFile); - PTR_Module LookupModule(mdToken kFile); //wrapper over GetModuleIfLoaded, takes modulerefs as well - DWORD GetAssemblyRefFlags(mdAssemblyRef tkAssemblyRef); + DomainAssembly * LoadAssemblyImpl(mdAssemblyRef kAssemblyRef) final; +public: + PTR_Module LookupModule(mdToken kFile) final; + Module *GetModuleIfLoaded(mdFile kFile) final; // RID maps TypeHandle LookupTypeDef(mdTypeDef token, ClassLoadLevel *pLoadLevel = NULL) @@ -1130,8 +1223,7 @@ class Module BAD_FORMAT_NOTHROW_ASSERT(TypeFromToken(token) == mdtTypeDef); - TADDR flags; - TypeHandle th = TypeHandle(m_TypeDefToMethodTableMap.GetElementAndFlags(RidFromToken(token), &flags)); + TypeHandle th = TypeHandle(m_TypeDefToMethodTableMap.GetElement(RidFromToken(token))); if (pLoadLevel && !th.IsNull()) { @@ -1147,8 +1239,7 @@ class Module BAD_FORMAT_NOTHROW_ASSERT(TypeFromToken(token) == mdtTypeDef); - TADDR flags; - TypeHandle th = TypeHandle(m_GenericTypeDefToCanonMethodTableMap.GetElementAndFlags(RidFromToken(token), &flags)); + TypeHandle th = TypeHandle(m_GenericTypeDefToCanonMethodTableMap.GetElement(RidFromToken(token))); if (pLoadLevel && !th.IsNull()) { @@ -1175,8 +1266,6 @@ class Module #endif // !DACCESS_COMPILE - TypeHandle LookupTypeRef(mdTypeRef token); - #ifndef DACCESS_COMPILE // // Increase the size of the TypeRef-to-MethodTable LookupMap to make sure the specified token @@ -1193,18 +1282,6 @@ class Module _ASSERTE(TypeFromToken(token) == mdtTypeRef); m_TypeRefToMethodTableMap.EnsureElementCanBeStored(this, RidFromToken(token)); } - - void StoreTypeRef(mdTypeRef token, TypeHandle value) - { - WRAPPER_NO_CONTRACT; - - _ASSERTE(TypeFromToken(token) == mdtTypeRef); - - // The TypeRef cache is strictly a lookaside cache. If we get an OOM trying to grow the table, - // we cannot abort the load. (This will cause fatal errors during gc promotion.) - m_TypeRefToMethodTableMap.TrySetElement(RidFromToken(token), - dac_cast(value.AsTAddr())); - } #endif // !DACCESS_COMPILE MethodDesc *LookupMethodDef(mdMethodDef token); @@ -1254,34 +1331,7 @@ class Module } #endif // !DACCESS_COMPILE - FORCEINLINE TADDR LookupMemberRef(mdMemberRef token, BOOL *pfIsMethod) - { - WRAPPER_NO_CONTRACT; - - _ASSERTE(TypeFromToken(token) == mdtMemberRef); - - TADDR pResult = dac_cast(m_pMemberRefToDescHashTable->GetValue(token, pfIsMethod)); - return pResult; - } MethodDesc *LookupMemberRefAsMethod(mdMemberRef token); -#ifndef DACCESS_COMPILE - void StoreMemberRef(mdMemberRef token, FieldDesc *value) - { - WRAPPER_NO_CONTRACT; - - _ASSERTE(TypeFromToken(token) == mdtMemberRef); - CrstHolder ch(this->GetLookupTableCrst()); - m_pMemberRefToDescHashTable->Insert(token, value); - } - void StoreMemberRef(mdMemberRef token, MethodDesc *value) - { - WRAPPER_NO_CONTRACT; - - _ASSERTE(TypeFromToken(token) == mdtMemberRef); - CrstHolder ch(this->GetLookupTableCrst()); - m_pMemberRefToDescHashTable->Insert(token, value); - } -#endif // !DACCESS_COMPILE PTR_TypeVarTypeDesc LookupGenericParam(mdGenericParam token) { @@ -1356,8 +1406,6 @@ class Module DWORD GetFileMax() { LIMITED_METHOD_DAC_CONTRACT; return m_FileReferencesMap.GetSize(); } - Assembly *LookupAssemblyRef(mdAssemblyRef token); - #ifndef DACCESS_COMPILE // // Increase the size of the AssemblyRef-to-Module LookupMap to make sure the specified token @@ -1385,7 +1433,7 @@ class Module #endif // !DACCESS_COMPILE - DWORD GetAssemblyRefMax() {LIMITED_METHOD_CONTRACT; return m_ManifestModuleReferencesMap.GetSize(); } + DWORD GetAssemblyRefMax() {LIMITED_METHOD_CONTRACT; return m_ManifestModuleReferencesMap.GetSize() - 1; } MethodDesc *FindMethodThrowing(mdToken pMethod); MethodDesc *FindMethod(mdToken pMethod); @@ -1471,7 +1519,11 @@ class Module UINT32 GetFieldTlsOffset(DWORD field); UINT32 GetTlsIndex(); - BOOL IsSigInIL(PCCOR_SIGNATURE signature); +protected: +#ifndef DACCESS_COMPILE + BOOL IsSigInILImpl(PCCOR_SIGNATURE signature) final; +#endif +public: mdToken GetEntryPointToken(); @@ -1523,8 +1575,8 @@ class Module void RunEagerFixups(); void RunEagerFixupsUnlocked(); - Module *GetModuleFromIndex(DWORD ix); - Module *GetModuleFromIndexIfLoaded(DWORD ix); + ModuleBase *GetModuleFromIndex(DWORD ix); + ModuleBase *GetModuleFromIndexIfLoaded(DWORD ix); BOOL IsReadyToRun() const { @@ -1754,12 +1806,6 @@ class Module public: - CrstBase *GetLookupTableCrst() - { - LIMITED_METHOD_CONTRACT; - return &m_LookupTableCrst; - } - private: // This struct stores the data used by the managed debugging infrastructure. If it turns out that diff --git a/src/coreclr/vm/ceeload.inl b/src/coreclr/vm/ceeload.inl index 43e41266728ff..c3710bda73641 100644 --- a/src/coreclr/vm/ceeload.inl +++ b/src/coreclr/vm/ceeload.inl @@ -122,7 +122,7 @@ BOOL LookupMap::TrySetElement(DWORD rid, TYPE value, TADDR flags) // Stores an association in a map. Grows the map as necessary. template -void LookupMap::AddElement(Module * pModule, DWORD rid, TYPE value, TADDR flags) +void LookupMap::AddElement(ModuleBase * pModule, DWORD rid, TYPE value, TADDR flags) { CONTRACTL { @@ -275,12 +275,12 @@ inline MethodDesc *Module::LookupMemberRefAsMethod(mdMemberRef token) LIMITED_METHOD_DAC_CONTRACT; _ASSERTE(TypeFromToken(token) == mdtMemberRef); - BOOL flags = FALSE; - PTR_MemberRef pMemberRef = m_pMemberRefToDescHashTable->GetValue(token, &flags); - return flags ? dac_cast(pMemberRef) : NULL; + TADDR flags = FALSE; + TADDR pMemberRef = m_MemberRefMap.GetElementAndFlags(RidFromToken(token), &flags); + return (flags & IS_FIELD_MEMBER_REF) ? NULL : dac_cast(pMemberRef); } -inline Assembly *Module::LookupAssemblyRef(mdAssemblyRef token) +inline Assembly *ModuleBase::LookupAssemblyRef(mdAssemblyRef token) { WRAPPER_NO_CONTRACT; SUPPORTS_DAC; @@ -455,12 +455,6 @@ BOOL Module::FixupDelayListAux(TADDR pFixupList, return TRUE; } -inline PTR_LoaderAllocator Module::GetLoaderAllocator() -{ - LIMITED_METHOD_DAC_CONTRACT; - return GetAssembly()->GetLoaderAllocator(); -} - inline MethodTable* Module::GetDynamicClassMT(DWORD dynamicClassID) { LIMITED_METHOD_CONTRACT; diff --git a/src/coreclr/vm/clsload.cpp b/src/coreclr/vm/clsload.cpp index 8667c8e905ce0..77512074226a4 100644 --- a/src/coreclr/vm/clsload.cpp +++ b/src/coreclr/vm/clsload.cpp @@ -48,6 +48,33 @@ #include "stringarraylist.h" +NameHandle::NameHandle(ModuleBase* pModule, mdToken token) : + m_nameSpace(NULL), + m_name(NULL), + m_pTypeScope(pModule), + m_mdType(token), + m_mdTokenNotToLoad(tdNoTypes), + m_WhichTable(nhCaseSensitive), + m_Bucket() +{ + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; +} + +NameHandle::NameHandle(Module* pModule, mdToken token) : + m_nameSpace(NULL), + m_name(NULL), + m_pTypeScope(pModule), + m_mdType(token), + m_mdTokenNotToLoad(tdNoTypes), + m_WhichTable(nhCaseSensitive), + m_Bucket() +{ + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; +} + + // This method determines the "loader module" for an instantiated type // or method. The rule must ensure that any types involved in the // instantiated type or method do not outlive the loader module itself @@ -91,33 +118,42 @@ PTR_Module ClassLoader::ComputeLoaderModuleWorker( Module *pLoaderModule = NULL; - if (pDefinitionModule) - { - if (pDefinitionModule->IsCollectible()) - goto ComputeCollectibleLoaderModule; - pLoaderModule = pDefinitionModule; - } + if (pDefinitionModule != NULL && pDefinitionModule->IsCollectible()) + goto ComputeCollectibleLoaderModule; for (DWORD i = 0; i < classInst.GetNumArgs(); i++) { TypeHandle classArg = classInst[i]; + + // System.__Canon does not contribute to logical loader module + if (classArg == TypeHandle(g_pCanonMethodTableClass)) + continue; Module* pModule = classArg.GetLoaderModule(); if (pModule->IsCollectible()) goto ComputeCollectibleLoaderModule; - if (pLoaderModule == NULL) + if ((pLoaderModule == NULL) && (pModule != pDefinitionModule)) pLoaderModule = pModule; } for (DWORD i = 0; i < methodInst.GetNumArgs(); i++) { TypeHandle methodArg = methodInst[i]; + + // System.__Canon does not contribute to logical loader module + if (methodArg == TypeHandle(g_pCanonMethodTableClass)) + continue; Module *pModule = methodArg.GetLoaderModule(); if (pModule->IsCollectible()) goto ComputeCollectibleLoaderModule; - if (pLoaderModule == NULL) + if ((pLoaderModule == NULL) && (pModule != pDefinitionModule)) pLoaderModule = pModule; } + if ((pLoaderModule == NULL) && pDefinitionModule) + { + pLoaderModule = pDefinitionModule; + } + if (pLoaderModule == NULL) { CONSISTENCY_CHECK(CoreLibBinder::GetModule() && CoreLibBinder::GetModule()->IsSystem()); @@ -539,7 +575,7 @@ BOOL ClassLoader::CompareNestedEntryWithTypeRef(IMDInternalImport * pImport, /*static*/ -BOOL ClassLoader::IsNested(Module *pModule, mdToken token, mdToken *mdEncloser) +BOOL ClassLoader::IsNested(ModuleBase *pModule, mdToken token, mdToken *mdEncloser) { CONTRACTL { @@ -561,7 +597,8 @@ BOOL ClassLoader::IsNested(Module *pModule, mdToken token, mdToken *mdEncloser) (*mdEncloser != mdTypeRefNil)); case mdtExportedType: - IfFailThrow(pModule->GetAssembly()->GetMDImport()->GetExportedTypeProps( + _ASSERTE(pModule->IsFullModule()); + IfFailThrow(((Module*)pModule)->GetAssembly()->GetMDImport()->GetExportedTypeProps( token, NULL, // namespace NULL, // name @@ -713,7 +750,7 @@ void ClassLoader::GetClassValue(NameHandleTable nhTable, if (isNested) { - Module *pNameModule = pName->GetTypeModule(); + ModuleBase *pNameModule = pName->GetTypeModule(); PREFIX_ASSUME(pNameModule != NULL); EEClassHashTable::LookupContext sContext; @@ -736,7 +773,8 @@ void ClassLoader::GetClassValue(NameHandleTable nhTable, (pBucket = pTable->FindNextNestedClass(pName, pData, &sContext)) != NULL); break; case mdtExportedType: - while ((!CompareNestedEntryWithExportedType(pNameModule->GetAssembly()->GetMDImport(), + _ASSERTE(pNameModule->IsFullModule()); + while ((!CompareNestedEntryWithExportedType(((Module*)pNameModule)->GetAssembly()->GetMDImport(), mdEncloser, pCurrentClsModule->GetAvailableClassHash(), pBucket->GetEncloser())) && @@ -1781,7 +1819,7 @@ VOID ClassLoader::CreateCanonicallyCasedKey(LPCUTF8 pszNameSpace, LPCUTF8 pszNam // Only for type refs and type defs (not type specs) // /*static*/ -TypeHandle ClassLoader::LookupTypeDefOrRefInModule(Module *pModule, mdToken cl, ClassLoadLevel *pLoadLevel) +TypeHandle ClassLoader::LookupTypeDefOrRefInModule(ModuleBase *pModule, mdToken cl, ClassLoadLevel *pLoadLevel) { CONTRACT(TypeHandle) { @@ -1802,7 +1840,7 @@ TypeHandle ClassLoader::LookupTypeDefOrRefInModule(Module *pModule, mdToken cl, TypeHandle typeHandle; if (TypeFromToken(cl) == mdtTypeDef) - typeHandle = pModule->LookupTypeDef(cl, pLoadLevel); + typeHandle = static_cast(pModule)->LookupTypeDef(cl, pLoadLevel); else if (TypeFromToken(cl) == mdtTypeRef) { typeHandle = pModule->LookupTypeRef(cl); @@ -2246,7 +2284,7 @@ TypeHandle ClassLoader::LoadTypeDefThrowing(Module *pModule, // handle. // /*static*/ -TypeHandle ClassLoader::LoadTypeDefOrRefThrowing(Module *pModule, +TypeHandle ClassLoader::LoadTypeDefOrRefThrowing(ModuleBase *pModule, mdToken typeDefOrRef, NotFoundAction fNotFoundAction /* = ThrowIfNotFound */ , PermitUninstantiatedFlag fUninstantiated /* = FailIfUninstDefOrRef */, @@ -2262,8 +2300,6 @@ TypeHandle ClassLoader::LoadTypeDefOrRefThrowing(Module *pModule, if (FORBIDGC_LOADER_USE_ENABLED()) FORBID_FAULT; else { INJECT_FAULT(COMPlusThrowOM()); } PRECONDITION(CheckPointer(pModule)); PRECONDITION(level > CLASS_LOAD_BEGIN && level <= CLASS_LOADED); - PRECONDITION(FORBIDGC_LOADER_USE_ENABLED() - || GetAppDomain()->CheckCanLoadTypes(pModule->GetAssembly())); POSTCONDITION(CheckPointer(RETVAL, NameHandle::OKToLoad(typeDefOrRef, tokenNotToLoad) && (fNotFoundAction == ThrowIfNotFound) ? NULL_NOT_OK : NULL_OK)); POSTCONDITION(level <= CLASS_LOAD_UNRESTORED || RETVAL.IsNull() || RETVAL.IsRestored()); @@ -2326,10 +2362,10 @@ TypeHandle ClassLoader::LoadTypeDefOrRefThrowing(Module *pModule, { BOOL fNoResolutionScope; Module *pFoundModule = Assembly::FindModuleByTypeRef(pModule, typeDefOrRef, - tokenNotToLoad==tdAllTypes ? + tokenNotToLoad==tdAllTypes ? Loader::DontLoad : Loader::Load, - &fNoResolutionScope); + &fNoResolutionScope); if (pFoundModule != NULL) { @@ -2348,13 +2384,13 @@ TypeHandle ClassLoader::LoadTypeDefOrRefThrowing(Module *pModule, } else { - if (fNoResolutionScope) + if (fNoResolutionScope && pFoundModule->IsFullModule()) { // Everett C++ compiler can generate a TypeRef with RS=0 // without respective TypeDef for unmanaged valuetypes, // referenced only by pointers to them, // so we can fail to load legally w/ no exception - typeHnd = ClassLoader::LoadTypeByNameThrowing(pFoundModule->GetAssembly(), + typeHnd = ClassLoader::LoadTypeByNameThrowing(static_cast(pFoundModule)->GetAssembly(), pszNameSpace, pszClassName, ClassLoader::ReturnNullIfNotFound, @@ -2374,7 +2410,7 @@ TypeHandle ClassLoader::LoadTypeDefOrRefThrowing(Module *pModule, nameHandle.SetTokenNotToLoad(tokenNotToLoad); typeHnd = pFoundModule->GetClassLoader()-> LoadTypeHandleThrowIfFailed(&nameHandle, level, - pFoundModule->IsReflection() ? NULL : pFoundModule); + pFoundModule->IsFullModule() ? (static_cast(pFoundModule)->IsReflection() ? NULL : static_cast(pFoundModule)) : NULL); } } @@ -2387,7 +2423,7 @@ TypeHandle ClassLoader::LoadTypeDefOrRefThrowing(Module *pModule, else { // This is the mdtTypeDef case... - typeHnd = LoadTypeDefThrowing(pModule, typeDefOrRef, + typeHnd = LoadTypeDefThrowing(static_cast(pModule), typeDefOrRef, fNotFoundAction, fUninstantiated, tokenNotToLoad, @@ -2405,9 +2441,9 @@ TypeHandle ClassLoader::LoadTypeDefOrRefThrowing(Module *pModule, if ((fNotFoundAction == ThrowIfNotFound) && thRes.IsNull() && (tokenNotToLoad != tdAllTypes)) { #ifndef DACCESS_COMPILE - pModule->GetAssembly()->ThrowTypeLoadException(pModule->GetMDImport(), - typeDefOrRef, - IDS_CLASSLOAD_GENERAL); + pModule->ThrowTypeLoadException(pModule->GetMDImport(), + typeDefOrRef, + IDS_CLASSLOAD_GENERAL); #else DacNotImpl(); #endif @@ -2419,7 +2455,7 @@ TypeHandle ClassLoader::LoadTypeDefOrRefThrowing(Module *pModule, /*static*/ BOOL ClassLoader::ResolveTokenToTypeDefThrowing( - Module * pTypeRefModule, + ModuleBase * pTypeRefModule, mdTypeRef typeRefToken, Module ** ppTypeDefModule, mdTypeDef * pTypeDefToken, @@ -2440,8 +2476,11 @@ ClassLoader::ResolveTokenToTypeDefThrowing( // It's a TypeDef already if (TypeFromToken(typeRefToken) == mdtTypeDef) { + if (!pTypeRefModule->IsFullModule()) + return FALSE; + if (ppTypeDefModule != NULL) - *ppTypeDefModule = pTypeRefModule; + *ppTypeDefModule = static_cast(pTypeRefModule); if (pTypeDefToken != NULL) *pTypeDefToken = typeRefToken; RETURN TRUE; diff --git a/src/coreclr/vm/clsload.hpp b/src/coreclr/vm/clsload.hpp index bfaaab3b9449b..e446e239350af 100644 --- a/src/coreclr/vm/clsload.hpp +++ b/src/coreclr/vm/clsload.hpp @@ -124,7 +124,7 @@ class NameHandle LPCUTF8 m_nameSpace; LPCUTF8 m_name; - PTR_Module m_pTypeScope; + PTR_ModuleBase m_pTypeScope; mdToken m_mdType; mdToken m_mdTokenNotToLoad; NameHandleTable m_WhichTable; @@ -163,18 +163,9 @@ class NameHandle SUPPORTS_DAC; } - NameHandle(Module* pModule, mdToken token) : - m_nameSpace(NULL), - m_name(NULL), - m_pTypeScope(pModule), - m_mdType(token), - m_mdTokenNotToLoad(tdNoTypes), - m_WhichTable(nhCaseSensitive), - m_Bucket() - { - LIMITED_METHOD_CONTRACT; - SUPPORTS_DAC; - } + NameHandle(ModuleBase* pModule, mdToken token); + + NameHandle(Module* pModule, mdToken token); NameHandle(const NameHandle & p) { @@ -218,14 +209,16 @@ class NameHandle return m_nameSpace; } +#ifndef DACCESS_COMPILE void SetTypeToken(Module* pModule, mdToken mdToken) { LIMITED_METHOD_CONTRACT; - m_pTypeScope = dac_cast(pModule); + m_pTypeScope = dac_cast(pModule); m_mdType = mdToken; } +#endif - PTR_Module GetTypeModule() const + PTR_ModuleBase GetTypeModule() const { LIMITED_METHOD_CONTRACT; SUPPORTS_DAC; @@ -665,7 +658,7 @@ class ClassLoader ClassLoadLevel level = CLASS_LOADED, Instantiation * pTargetInstantiation = NULL /* used to verify arity of the loaded type */); - static TypeHandle LoadTypeDefOrRefThrowing(Module *pModule, + static TypeHandle LoadTypeDefOrRefThrowing(ModuleBase *pModule, mdToken typeRefOrDef, NotFoundAction fNotFound = ThrowIfNotFound, PermitUninstantiatedFlag fUninstantiated = FailIfUninstDefOrRef, @@ -718,7 +711,7 @@ class ClassLoader // (Just a no-op on TypeDefs) // Return FALSE if operation failed (e.g. type does not exist) // *pfUsesTypeForwarder is set to TRUE if a type forwarder is found. It is never set to FALSE. - static BOOL ResolveTokenToTypeDefThrowing(Module * pTypeRefModule, + static BOOL ResolveTokenToTypeDefThrowing(ModuleBase * pTypeRefModule, mdTypeRef typeRefToken, Module ** ppTypeDefModule, mdTypeDef * pTypeDefToken, @@ -749,7 +742,7 @@ class ClassLoader public: // Looks up class in the local module table, if it is there it succeeds, // Otherwise it fails, This is meant only for optimizations etc - static TypeHandle LookupTypeDefOrRefInModule(Module *pModule, mdToken cl, ClassLoadLevel *pLoadLevel = NULL); + static TypeHandle LookupTypeDefOrRefInModule(ModuleBase *pModule, mdToken cl, ClassLoadLevel *pLoadLevel = NULL); private: @@ -911,7 +904,7 @@ class ClassLoader BOOL IsNested(const NameHandle* pName, mdToken *mdEncloser); - static BOOL IsNested(Module *pModude, mdToken typeDefOrRef, mdToken *mdEncloser); + static BOOL IsNested(ModuleBase *pModude, mdToken typeDefOrRef, mdToken *mdEncloser); public: // Helpers for FindClassModule() diff --git a/src/coreclr/vm/common.h b/src/coreclr/vm/common.h index c4bd0ad55d08c..0b32d84c3af4c 100644 --- a/src/coreclr/vm/common.h +++ b/src/coreclr/vm/common.h @@ -154,6 +154,7 @@ typedef DPTR(class TypeHandle) PTR_TypeHandle; typedef VPTR(class VirtualCallStubManager) PTR_VirtualCallStubManager; typedef VPTR(class VirtualCallStubManagerManager) PTR_VirtualCallStubManagerManager; typedef VPTR(class IGCHeap) PTR_IGCHeap; +typedef VPTR(class ModuleBase) PTR_ModuleBase; // // _UNCHECKED_OBJECTREF is for code that can't deal with DEBUG OBJECTREFs diff --git a/src/coreclr/vm/genericdict.cpp b/src/coreclr/vm/genericdict.cpp index 56a40b0495972..f2cbe74837632 100644 --- a/src/coreclr/vm/genericdict.cpp +++ b/src/coreclr/vm/genericdict.cpp @@ -634,7 +634,7 @@ Dictionary::PopulateEntry( BYTE fixupKind = *pBlob++; - Module * pInfoModule = pModule; + ModuleBase * pInfoModule = pModule; if (fixupKind & ENCODE_MODULE_OVERRIDE) { DWORD moduleIndex = CorSigUncompressData(pBlob); @@ -657,7 +657,7 @@ Dictionary::PopulateEntry( if (signatureKind & ENCODE_MODULE_OVERRIDE) { DWORD moduleIndex = CorSigUncompressData(pBlob); - Module * pSignatureModule = pModule->GetModuleFromIndex(moduleIndex); + ModuleBase * pSignatureModule = pModule->GetModuleFromIndex(moduleIndex); if (pInfoModule == pModule) { pInfoModule = pSignatureModule; @@ -696,7 +696,7 @@ Dictionary::PopulateEntry( pZapSigContext = (pContainingZapModule != NULL) ? &zapSigContext : NULL; } - Module * pLookupModule = (isReadyToRunModule) ? pZapSigContext->pInfoModule : CoreLibBinder::GetModule(); + ModuleBase * pLookupModule = (isReadyToRunModule) ? pZapSigContext->pInfoModule : CoreLibBinder::GetModule(); if (pMT != NULL) { @@ -887,7 +887,8 @@ Dictionary::PopulateEntry( } else { - pMethod = MemberLoader::GetMethodDescFromMethodDef(pZapSigContext->pInfoModule, TokenFromRid(rid, mdtMethodDef), FALSE); + _ASSERTE(pZapSigContext->pInfoModule->IsFullModule()); + pMethod = MemberLoader::GetMethodDescFromMethodDef(static_cast(pZapSigContext->pInfoModule), TokenFromRid(rid, mdtMethodDef), FALSE); } } diff --git a/src/coreclr/vm/inlinetracking.cpp b/src/coreclr/vm/inlinetracking.cpp index 4bd60ab598788..e358b93cdd5f4 100644 --- a/src/coreclr/vm/inlinetracking.cpp +++ b/src/coreclr/vm/inlinetracking.cpp @@ -424,7 +424,235 @@ Module* PersistentInlineTrackingMapR2R2::GetModuleByIndex(DWORD index) // This is useful when ngen image was compiler against a different assembly version than the one loaded now. ClrFlsThreadTypeSwitch genericInstantionCompareHolder(ThreadType_GenericInstantiationCompare); - return m_module->GetModuleFromIndexIfLoaded(index); + _ASSERTE(m_module->GetModuleFromIndexIfLoaded(index)->IsFullModule()); + return static_cast(m_module->GetModuleFromIndexIfLoaded(index)); +} + +BOOL CrossModulePersistentInlineTrackingMapR2R::TryLoad(Module* pModule, LoaderAllocator* pLoaderAllocator, const BYTE* pBuffer, DWORD cbBuffer, + AllocMemTracker* pamTracker, CrossModulePersistentInlineTrackingMapR2R** ppLoadedMap) +{ + LoaderHeap* pHeap = pLoaderAllocator->GetHighFrequencyHeap(); + void* pMemory = pamTracker->Track(pHeap->AllocMem((S_SIZE_T)sizeof(CrossModulePersistentInlineTrackingMapR2R))); + CrossModulePersistentInlineTrackingMapR2R* pMap = new (pMemory) CrossModulePersistentInlineTrackingMapR2R(); + + pMap->m_module = pModule; + + pMap->m_reader = NativeReader(pBuffer, cbBuffer); + NativeParser parser = NativeParser(&pMap->m_reader, 0); + pMap->m_hashtable = NativeHashtable(parser); + *ppLoadedMap = pMap; + return TRUE; +} + +// Read a Module/MethodDef token pair from the parser. Optionally check to see if it matches expectedToken. +// (0 for expectedToken indicates that no checking is to be performed) +void CrossModulePersistentInlineTrackingMapR2R::GetILBodySection(MethodDesc*** pppMethods, COUNT_T* pcMethods) +{ + COUNT_T countImportSections; + auto pImportSections = m_module->GetImportSections(&countImportSections); + + *pppMethods = NULL; + *pcMethods = 0; + + for (COUNT_T iSection = 0; iSection < countImportSections; iSection++) + { + if (pImportSections[iSection].Type == ReadyToRunImportSectionType::ILBodyFixups) + { + COUNT_T cbData; + *pppMethods = (MethodDesc**)(m_module->GetReadyToRunImage()->GetDirectoryData(&pImportSections[iSection].Section, &cbData)); + *pcMethods = cbData / sizeof(TADDR); + return; + } + } +} + +static void GetILBodyTokenInfo(uint32_t ilBodyIndex, MethodDesc** ppMethods, COUNT_T cMethods, Module **readModule, mdMethodDef *readToken) +{ + *readModule = NULL; + *readToken = 0; + if (ilBodyIndex < cMethods) + { + PTR_MethodDesc ilBody = VolatileLoad(ppMethods + ilBodyIndex); + if (ilBody != NULL) + { + *readModule = ilBody->GetModule(); + *readToken = ilBody->GetMemberDef(); + } + } +} + +static void AddInliner(uint32_t inlinersSize, MethodInModule inliners[], uint32_t *pInlinerCount, Module* inlinerModule, mdMethodDef inlinerToken, BOOL *incompleteData) +{ + if (inlinerModule == nullptr && incompleteData) + { + // We can't find module for this inlineeModuleZapIndex, it means it hasn't been loaded yet + // (maybe it never will be), we just report it to the profiler. + // Profiler might want to try later when more modules are loaded. + *incompleteData = TRUE; + return; + } + + if ((*pInlinerCount) < inlinersSize) + { + inliners[*pInlinerCount].m_methodDef = inlinerToken; + inliners[*pInlinerCount].m_module = inlinerModule; + } + (*pInlinerCount)++; +} + +COUNT_T CrossModulePersistentInlineTrackingMapR2R::GetInliners(PTR_Module inlineeOwnerMod, mdMethodDef inlineeTkn, COUNT_T inlinersSize, MethodInModule inliners[], BOOL* incompleteData) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + _ASSERTE(inlineeOwnerMod); + _ASSERTE(inliners != NULL || inlinersSize == 0); + + // This api while it is capable of declaring its results incomplete, does not provide a reasonable + // way for the customer to detect additional inlines made visible by the resolution of ILBody fixups. + // Thus, once we have queried the set of inliners, it must not increase by the resolution of additional ILBody fixups. + m_module->GetReadyToRunInfo()->ForbidProcessMoreILBodyFixups(); + + if (incompleteData) + { + *incompleteData = FALSE; + } + uint32_t result = 0; + + int hashCode; + if (!GetVersionResilientMethodDefHashCode(inlineeOwnerMod->GetMDImport(), inlineeTkn, &hashCode)) + { + return result; + } + + bool multiModuleFormat = m_module->GetReadyToRunInfo()->MultiModuleVersionBubble(); + + MethodDesc** ppMethods; + COUNT_T cMethods; + GetILBodySection(&ppMethods, &cMethods); + + NativeHashtable::Enumerator lookup = m_hashtable.Lookup(hashCode); + NativeParser entryParser; + while (lookup.GetNext(entryParser)) + { + uint32_t streamSize = entryParser.GetUnsigned(); + _ASSERTE(streamSize > 1); + + // First make sure this is the right inlinee and not just a hash collision + Module* inlineeModule; + + uint32_t inlineeIndexAndFlags = entryParser.GetUnsigned(); + streamSize--; + + uint32_t inlineeIndex = inlineeIndexAndFlags >> (int)ReadyToRunCrossModuleInlineFlags::CrossModuleInlinerIndexShift; + bool hasCrossModuleInliners = ((ReadyToRunCrossModuleInlineFlags)inlineeIndexAndFlags & ReadyToRunCrossModuleInlineFlags::HasCrossModuleInliners) == ReadyToRunCrossModuleInlineFlags::HasCrossModuleInliners; + bool crossModuleInlinee = ((ReadyToRunCrossModuleInlineFlags)inlineeIndexAndFlags & ReadyToRunCrossModuleInlineFlags::CrossModuleInlinee) == ReadyToRunCrossModuleInlineFlags::CrossModuleInlinee; + + if (crossModuleInlinee) + { + mdMethodDef inlineeToken; + GetILBodyTokenInfo(inlineeIndex, ppMethods, cMethods, &inlineeModule, &inlineeToken); + if (inlineeToken != inlineeTkn) + continue; + } + else + { + mdMethodDef inlineeToken = TokenFromRid(inlineeIndex, mdtMethodDef); + if (inlineeTkn != inlineeToken) + continue; + + if (multiModuleFormat) + { + _ASSERTE(streamSize > 0); + uint32_t moduleIndex = entryParser.GetUnsigned(); + streamSize--; + inlineeModule = GetModuleByIndex(moduleIndex); + } + else + { + inlineeModule = m_module; + } + } + + if (inlineeModule != inlineeOwnerMod) + { + continue; + } + + // We have the right inlinee, let's look at the inliners + + // First chack for cross module inliners + if (hasCrossModuleInliners) + { + _ASSERTE(streamSize > 0); + uint32_t crossModuleInlinerCount = entryParser.GetUnsigned(); + streamSize--; + + for (uint32_t iInliner = 0; iInliner < crossModuleInlinerCount; iInliner++) + { + _ASSERTE(streamSize > 0); + uint32_t inlinerIndex = entryParser.GetUnsigned(); + streamSize--; + + Module* inlinerModule = NULL; + mdMethodDef inlinerToken = 0; + GetILBodyTokenInfo(inlinerIndex, ppMethods, cMethods, &inlinerModule, &inlinerToken); + AddInliner(inlinersSize, inliners, &result, inlinerModule, inlinerToken, incompleteData); + } + } + + // Then handle the non-cross module ones + DWORD currentInlinerIndex = 0; + while (streamSize > 0) + { + uint32_t inlinerIndexDeltaAndFlag = entryParser.GetUnsigned(); + streamSize--; + + Module* inlinerModule = inlineeModule; + + if (multiModuleFormat) + { + currentInlinerIndex += inlinerIndexDeltaAndFlag >> (int)ReadyToRunCrossModuleInlineFlags::InlinerRidShift; + if ((((ReadyToRunCrossModuleInlineFlags)inlinerIndexDeltaAndFlag) & ReadyToRunCrossModuleInlineFlags::InlinerRidHasModule) == ReadyToRunCrossModuleInlineFlags::InlinerRidHasModule) + { + _ASSERTE(streamSize >= 0); + uint32_t moduleIndex = entryParser.GetUnsigned(); + streamSize--; + inlinerModule = GetModuleByIndex(moduleIndex); + } + } + mdMethodDef inlinerToken = TokenFromRid(currentInlinerIndex, mdtMethodDef); + + + AddInliner(inlinersSize, inliners, &result, inlinerModule, inlinerToken, incompleteData); + } + } + + return result; +} + +Module* CrossModulePersistentInlineTrackingMapR2R::GetModuleByIndex(DWORD index) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + // This "black magic spell" has in fact nothing to do with GenericInstantiationCompare per se, but just sets a thread flag + // that later activates more thorough search inside Module::GetAssemblyIfLoaded, which is indirectly called from GetModuleFromIndexIfLoaded. + // This is useful when ngen image was compiler against a different assembly version than the one loaded now. + ClrFlsThreadTypeSwitch genericInstantionCompareHolder(ThreadType_GenericInstantiationCompare); + + _ASSERTE(m_module->GetModuleFromIndexIfLoaded(index)->IsFullModule()); + return static_cast(m_module->GetModuleFromIndexIfLoaded(index)); } #endif //!DACCESS_COMPILE diff --git a/src/coreclr/vm/inlinetracking.h b/src/coreclr/vm/inlinetracking.h index d0d2621dbc767..1a26ab09bc39c 100644 --- a/src/coreclr/vm/inlinetracking.h +++ b/src/coreclr/vm/inlinetracking.h @@ -340,6 +340,34 @@ class PersistentInlineTrackingMapR2R2 : private PersistentInlineTrackingMapR2R typedef DPTR(PersistentInlineTrackingMapR2R2) PTR_PersistentInlineTrackingMapR2R2; #endif +#ifndef DACCESS_COMPILE +namespace NativeFormat +{ + class NativeParser; +} + +class CrossModulePersistentInlineTrackingMapR2R : private PersistentInlineTrackingMapR2R +{ +private: + PTR_Module m_module; + + NativeFormat::NativeReader m_reader; + NativeFormat::NativeHashtable m_hashtable; + +public: + + // runtime deserialization + static BOOL TryLoad(Module* pModule, LoaderAllocator* pLoaderAllocator, const BYTE* pBuffer, DWORD cbBuffer, AllocMemTracker* pamTracker, CrossModulePersistentInlineTrackingMapR2R** ppLoadedMap); + virtual COUNT_T GetInliners(PTR_Module inlineeOwnerMod, mdMethodDef inlineeTkn, COUNT_T inlinersSize, MethodInModule inliners[], BOOL* incompleteData) override; + +private: + Module* GetModuleByIndex(DWORD index); + void GetILBodySection(MethodDesc*** pppMethods, COUNT_T* pcMethods); +}; + +typedef DPTR(CrossModulePersistentInlineTrackingMapR2R) PTR_CrossModulePersistentInlineTrackingMapR2R; +#endif + #endif //FEATURE_READYTORUN #if !defined(DACCESS_COMPILE) diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 5dfb7de70ba29..5f9d31203e0f6 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -2431,7 +2431,7 @@ OBJECTHANDLE ConstructStringLiteral(CORINFO_MODULE_HANDLE scopeHnd, mdToken meta _ASSERTE(TypeFromToken(metaTok) == mdtString); Module* module = GetModule(scopeHnd); - return module->ResolveStringRef(metaTok, module->GetAssembly()->Parent()); + return module->ResolveStringRef(metaTok); } /*********************************************************************/ diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index bc06ec12eb4cf..4edbd346d2f35 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -965,7 +965,7 @@ void CEEInfo::resolveToken(/* IN, OUT */ CORINFO_RESOLVED_TOKEN * pResolvedToken ThrowBadTokenException(pResolvedToken); { - DomainAssembly *pTargetModule = pModule->LoadModule(GetAppDomain(), metaTOK); + DomainAssembly *pTargetModule = pModule->LoadModule(metaTOK); if (pTargetModule == NULL) COMPlusThrowHR(COR_E_BADIMAGEFORMAT); th = TypeHandle(pTargetModule->GetModule()->GetGlobalMethodTable()); @@ -3163,17 +3163,6 @@ void CEEInfo::ComputeRuntimeLookupForSharedGenericToken(DictionaryEntryKind entr methodFlags |= ENCODE_METHOD_SIG_SlotInsteadOfToken; } - else - if (!pTemplateMD->GetModule()->IsInCurrentVersionBubble()) - { - // Using a method defined in another version bubble. We can assume the slot number is stable only for real interface methods. - if (!pTemplateMD->GetMethodTable()->IsInterface() || pTemplateMD->IsStatic() || pTemplateMD->HasMethodInstantiation()) - { - _ASSERTE(!"References to non-interface methods not yet supported in version resilient images"); - IfFailThrow(E_FAIL); - } - methodFlags |= ENCODE_METHOD_SIG_SlotInsteadOfToken; - } sigBuilder.AppendData(methodFlags); @@ -13304,7 +13293,7 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, BYTE kind = *pBlob++; - Module * pInfoModule = currentModule; + ModuleBase * pInfoModule = currentModule; if (kind & ENCODE_MODULE_OVERRIDE) { @@ -13379,15 +13368,7 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, } else { - CrstHolder ch(pInfoModule->GetFixupCrst()); - - if (*entry != NULL) - { - // We lost the race, just return - return TRUE; - } - - result = (size_t) pInfoModule->ResolveStringRef(TokenFromRid(rid, mdtString), currentModule->GetDomain()); + result = (size_t) pInfoModule->ResolveStringRef(TokenFromRid(rid, mdtString)); } } break; @@ -13434,7 +13415,8 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, case ENCODE_METHOD_ENTRY_DEF_TOKEN: { mdToken MethodDef = TokenFromRid(CorSigUncompressData(pBlob), mdtMethodDef); - pMD = MemberLoader::GetMethodDescFromMethodDef(pInfoModule, MethodDef, FALSE); + _ASSERTE(pInfoModule->IsFullModule()); + pMD = MemberLoader::GetMethodDescFromMethodDef(static_cast(pInfoModule), MethodDef, FALSE); pMD->PrepareForUseAsADependencyOfANativeImage(); @@ -13606,12 +13588,6 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, } break; - case ENCODE_MODULE_ID_FOR_STATICS: - { - result = pInfoModule->GetModuleID(); - } - break; - case ENCODE_MODULE_ID_FOR_GENERIC_STATICS: { TypeHandle th = ZapSig::DecodeType(currentModule, pInfoModule, pBlob); @@ -13622,15 +13598,6 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, } break; - case ENCODE_ACTIVE_DEPENDENCY: - { - Module* pModule = currentModule->GetModuleFromIndex(CorSigUncompressData(pBlob)); - - STRESS_LOG3(LF_ZAP,LL_INFO10000,"Modules are: %08x,%08x,%08x",currentModule,pInfoModule,pModule); - pInfoModule->AddActiveDependency(pModule, FALSE); - } - break; - #ifdef FEATURE_READYTORUN case ENCODE_READYTORUN_HELPER: { @@ -13647,9 +13614,11 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, { case READYTORUN_HELPER_Module: { - Module * pPrevious = InterlockedCompareExchangeT((Module **)entry, pInfoModule, NULL); + _ASSERTE(pInfoModule->IsFullModule()); + Module *fullModule = static_cast(pInfoModule); + Module * pPrevious = InterlockedCompareExchangeT((Module **)entry, fullModule, NULL); if (pPrevious != pInfoModule && pPrevious != NULL) - COMPlusThrowHR(COR_E_FILELOAD, IDS_NATIVE_IMAGE_CANNOT_BE_LOADED_MULTIPLE_TIMES, pInfoModule->GetPath()); + COMPlusThrowHR(COR_E_FILELOAD, IDS_NATIVE_IMAGE_CANNOT_BE_LOADED_MULTIPLE_TIMES, fullModule->GetPath()); return TRUE; } break; @@ -13974,9 +13943,130 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, result = 1; } break; -#endif // FEATURE_READYTORUN + case ENCODE_CHECK_IL_BODY: + case ENCODE_VERIFY_IL_BODY: + { + DWORD dwBlobSize = CorSigUncompressData(pBlob); + const uint8_t *const pBlobStart = pBlob; + pBlob += dwBlobSize; + StackSArray types; + DWORD cTypes = CorSigUncompressData(pBlob); + bool fail = false; + + for (DWORD iType = 0; iType < cTypes && !fail; iType++) + { + if (kind == ENCODE_CHECK_IL_BODY) + { + EX_TRY + { + types.Append(ZapSig::DecodeType(currentModule, pInfoModule, pBlob, CLASS_LOAD_APPROXPARENTS, &pBlob)); + } + EX_CATCH + { + fail = true; + } + EX_END_CATCH(SwallowAllExceptions) + + } + else + { + types.Append(ZapSig::DecodeType(currentModule, pInfoModule, pBlob, CLASS_LOAD_APPROXPARENTS, &pBlob)); + } + } + + MethodDesc *pMDCompare = NULL; + + if (!fail) + { + if (kind == ENCODE_CHECK_IL_BODY) + { + EX_TRY + { + pMDCompare = ZapSig::DecodeMethod(currentModule, pInfoModule, pBlob); + } + EX_CATCH + { + fail = true; + } + EX_END_CATCH(SwallowAllExceptions) + } + else + { + pMDCompare = ZapSig::DecodeMethod(currentModule, pInfoModule, pBlob); + } + } + + MethodBlock *pMethodBlock = NULL; + if (!fail) + { + pMethodBlock = GetMethodBlock(pMDCompare); + if (pMethodBlock == NULL) + fail = true; + + fail = fail || (pMethodBlock->cByteData != dwBlobSize); + } + + if (!fail) + { + fail = 0 != memcmp(pBlobStart, pMethodBlock->pByteData, dwBlobSize); + } + + if (!fail) + { + fail = cTypes != pMethodBlock->cTypes; + } + + if (!fail) + { + for (COUNT_T i = 0; i < cTypes && !fail; i++) + { + fail = types[i] != pMethodBlock->pTypes[i]; + } + } + + if (!fail && !currentModule->GetReadyToRunInfo()->IsForbidProcessMoreILBodyFixups()) + { + result = (size_t)pMDCompare; + } + else + { + if (kind == ENCODE_CHECK_IL_BODY || (!fail && currentModule->GetReadyToRunInfo()->IsForbidProcessMoreILBodyFixups())) + { + return FALSE; + } + else + { + DefineFullyQualifiedNameForClassW(); + SString methodName; + pMDCompare->GetFullMethodInfo(methodName); + void* compileTimeTypes = types.OpenRawBuffer(); + + SString fatalErrorString; + fatalErrorString.Printf(W("VERIFY_IL_BODY Method '%s' type '%s' does not match IL body expected DEBUGINFO MethodData {%d} {%p} RuntimeMethodData {%d} {%p} Types {%d} {%p} RuntimeTypes {%d} {%p}"), + methodName.GetUnicode(), + GetFullyQualifiedNameForClassW(pMDCompare->GetMethodTable()), + (int)dwBlobSize, pBlobStart, + pMethodBlock != NULL ? (int)pMethodBlock->cByteData : 0, pMethodBlock != NULL ? (void*)pMethodBlock->pByteData : (void*)NULL, + (int)cTypes, compileTimeTypes, + pMethodBlock != NULL ? (int)pMethodBlock->cTypes : 0, pMethodBlock != NULL ? (void*)pMethodBlock->pTypes : (void*)NULL + ); + +#ifdef _DEBUG + { + StackScratchBuffer buf; + _ASSERTE_MSG(false, fatalErrorString.GetUTF8(buf)); + } +#endif + _ASSERTE(!IsDebuggerPresent() && "Stop on assert here instead of fatal error for ease of live debugging"); + EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(-1, fatalErrorString.GetUnicode()); + return FALSE; + } + } + break; + } +#endif // FEATURE_READYTORUN default: STRESS_LOG1(LF_ZAP, LL_WARNING, "Unknown FIXUP_BLOB_KIND %d\n", kind); _ASSERTE(!"Unknown FIXUP_BLOB_KIND"); diff --git a/src/coreclr/vm/memberload.cpp b/src/coreclr/vm/memberload.cpp index 57e4bd342e4e7..1bc51a075cc1f 100644 --- a/src/coreclr/vm/memberload.cpp +++ b/src/coreclr/vm/memberload.cpp @@ -80,7 +80,7 @@ void DECLSPEC_NORETURN MemberLoader::ThrowMissingFieldException(MethodTable* pMT EX_THROW(EEMessageException, (kMissingFieldException, IDS_EE_MISSING_FIELD, szwFullName)); } -void DECLSPEC_NORETURN MemberLoader::ThrowMissingMethodException(MethodTable* pMT, LPCSTR szMember, Module *pModule, PCCOR_SIGNATURE pSig,DWORD cSig,const SigTypeContext *pTypeContext) +void DECLSPEC_NORETURN MemberLoader::ThrowMissingMethodException(MethodTable* pMT, LPCSTR szMember, ModuleBase *pModule, PCCOR_SIGNATURE pSig,DWORD cSig,const SigTypeContext *pTypeContext) { CONTRACTL { @@ -106,9 +106,9 @@ void DECLSPEC_NORETURN MemberLoader::ThrowMissingMethodException(MethodTable* pM szClassName = "?"; }; - if (pSig && cSig && pModule) + if (pSig && cSig && pModule && pModule->IsFullModule()) { - MetaSig tmp(pSig, cSig, pModule, pTypeContext); + MetaSig tmp(pSig, cSig, static_cast(pModule), pTypeContext); SigFormat sf(tmp, szMember ? szMember : "?", szClassName, NULL); MAKE_WIDEPTR_FROMUTF8(szwFullName, sf.GetCString()); EX_THROW(EEMessageException, (kMissingMethodException, IDS_EE_MISSING_METHOD, szwFullName)); @@ -121,7 +121,7 @@ void DECLSPEC_NORETURN MemberLoader::ThrowMissingMethodException(MethodTable* pM //--------------------------------------------------------------------------------------- // -void MemberLoader::GetDescFromMemberRef(Module * pModule, +void MemberLoader::GetDescFromMemberRef(ModuleBase * pModule, mdToken MemberRef, MethodDesc ** ppMD, FieldDesc ** ppFD, @@ -191,30 +191,41 @@ void MemberLoader::GetDescFromMemberRef(Module * pModule, return; } - MethodDesc *pMethodDef = pModule->LookupMethodDef(parent); - if (!pMethodDef) + MethodDesc *pMethodDef = NULL; + + if (pModule->IsFullModule()) { - // There is no value for this def so we haven't yet loaded the class. - mdTypeDef typeDef; - IfFailThrow(pInternalImport->GetParentToken(parent, &typeDef)); - - // Make sure it is a typedef - if (TypeFromToken(typeDef) != mdtTypeDef) + Module* pNormalModule = static_cast(pModule); + pMethodDef = pNormalModule->LookupMethodDef(parent); + if (!pMethodDef) { - COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_METHODDEF_WO_TYPEDEF_PARENT); - } + // There is no value for this def so we haven't yet loaded the class. + mdTypeDef typeDef; + IfFailThrow(pInternalImport->GetParentToken(parent, &typeDef)); - // load the class + // Make sure it is a typedef + if (TypeFromToken(typeDef) != mdtTypeDef) + { + COMPlusThrowHR(COR_E_BADIMAGEFORMAT, BFA_METHODDEF_WO_TYPEDEF_PARENT); + } - TypeHandle th = ClassLoader::LoadTypeDefThrowing( - pModule, - typeDef, - ClassLoader::ThrowIfNotFound, - strictMetadataChecks ? - ClassLoader::FailIfUninstDefOrRef : ClassLoader::PermitUninstDefOrRef); + // load the class - // the class has been loaded and the method should be in the rid map! - pMethodDef = pModule->LookupMethodDef(parent); + TypeHandle th = ClassLoader::LoadTypeDefThrowing( + pNormalModule, + typeDef, + ClassLoader::ThrowIfNotFound, + strictMetadataChecks ? + ClassLoader::FailIfUninstDefOrRef : ClassLoader::PermitUninstDefOrRef); + + // the class has been loaded and the method should be in the rid map! + pMethodDef = pNormalModule->LookupMethodDef(parent); + } + } + else + { + // Only normal modules may have MethodDefs + COMPlusThrowHR(COR_E_BADIMAGEFORMAT); } LPCUTF8 szMember; @@ -277,7 +288,7 @@ void MemberLoader::GetDescFromMemberRef(Module * pModule, { case mdtModuleRef: { - DomainAssembly *pTargetModule = pModule->LoadModule(GetAppDomain(), parent); + DomainAssembly *pTargetModule = pModule->LoadModule(parent); if (pTargetModule == NULL) COMPlusThrowHR(COR_E_BADIMAGEFORMAT); typeHnd = TypeHandle(pTargetModule->GetModule()->GetGlobalMethodTable()); @@ -438,7 +449,7 @@ void MemberLoader::GetDescFromMemberRef(Module * pModule, //--------------------------------------------------------------------------------------- // -MethodDesc * MemberLoader::GetMethodDescFromMemberRefAndType(Module * pModule, +MethodDesc * MemberLoader::GetMethodDescFromMemberRefAndType(ModuleBase * pModule, mdToken MemberRef, MethodTable * pMT) { @@ -508,7 +519,7 @@ MethodDesc * MemberLoader::GetMethodDescFromMemberRefAndType(Module * pModule, //--------------------------------------------------------------------------------------- // -FieldDesc * MemberLoader::GetFieldDescFromMemberRefAndType(Module * pModule, +FieldDesc * MemberLoader::GetFieldDescFromMemberRefAndType(ModuleBase * pModule, mdToken MemberRef, MethodTable * pMT) { @@ -1023,7 +1034,7 @@ BOOL MemberLoader::FM_ShouldSkipMethod(DWORD dwAttrs, FM_Flags flags) BOOL CompareMethodSigWithCorrectSubstitution( PCCOR_SIGNATURE pSignature, DWORD cSignature, - Module* pModule, + ModuleBase* pModule, MethodDesc *pCurDeclMD, const Substitution *pDefSubst, MethodTable *pCurMT @@ -1070,7 +1081,7 @@ MemberLoader::FindMethod( MethodTable * pMT, LPCUTF8 pszName, PCCOR_SIGNATURE pSignature, DWORD cSignature, - Module* pModule, + ModuleBase* pModule, FM_Flags flags, // = FM_Default const Substitution *pDefSubst) // = NULL { @@ -1459,7 +1470,7 @@ MemberLoader::FindConstructor(MethodTable * pMT, PCCOR_SIGNATURE pSignature,DWOR #endif // DACCESS_COMPILE FieldDesc * -MemberLoader::FindField(MethodTable * pMT, LPCUTF8 pszName, PCCOR_SIGNATURE pSignature, DWORD cSignature, Module* pModule, BOOL bCaseSensitive) +MemberLoader::FindField(MethodTable * pMT, LPCUTF8 pszName, PCCOR_SIGNATURE pSignature, DWORD cSignature, ModuleBase* pModule, BOOL bCaseSensitive) { CONTRACTL { diff --git a/src/coreclr/vm/memberload.h b/src/coreclr/vm/memberload.h index 1b15a91421ee3..e36db3f9a2c91 100644 --- a/src/coreclr/vm/memberload.h +++ b/src/coreclr/vm/memberload.h @@ -62,7 +62,7 @@ class MemberLoader public: static void DECLSPEC_NORETURN ThrowMissingMethodException(MethodTable* pMT, LPCSTR szMember, - Module *pModule, + ModuleBase *pModule, PCCOR_SIGNATURE pSig, DWORD cSig, const SigTypeContext *pTypeContext); @@ -100,7 +100,7 @@ class MemberLoader mdToken FieldDef, BOOL strictMetadataChecks); - static void GetDescFromMemberRef(Module * pModule, + static void GetDescFromMemberRef(ModuleBase * pModule, mdToken MemberRef, MethodDesc ** ppMD, FieldDesc ** ppFD, @@ -118,11 +118,11 @@ class MemberLoader PCCOR_SIGNATURE * ppTypeSig = NULL, // Optionally, return generic signatures fetched from metadata during loading. ULONG * pcbTypeSig = NULL); - static MethodDesc * GetMethodDescFromMemberRefAndType(Module * pModule, + static MethodDesc * GetMethodDescFromMemberRefAndType(ModuleBase * pModule, mdToken MemberRef, MethodTable * pMT); - static FieldDesc * GetFieldDescFromMemberRefAndType(Module * pModule, + static FieldDesc * GetFieldDescFromMemberRefAndType(ModuleBase * pModule, mdToken MemberRef, MethodTable * pMT); @@ -221,7 +221,7 @@ class MemberLoader LPCUTF8 pszName, PCCOR_SIGNATURE pSignature, DWORD cSignature, - Module* pModule, + ModuleBase* pModule, FM_Flags flags = FM_Default, const Substitution *pDefSubst = NULL); @@ -255,7 +255,7 @@ class MemberLoader LPCUTF8 pszName, PCCOR_SIGNATURE pSignature, DWORD cSignature, - Module* pModule, + ModuleBase* pModule, BOOL bCaseSensitive = TRUE); static MethodDesc *FindConstructor(MethodTable * pMT, LPHARDCODEDMETASIG pwzSignature); diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp index 8b7f075c1b50b..b30e512e04abb 100644 --- a/src/coreclr/vm/method.cpp +++ b/src/coreclr/vm/method.cpp @@ -4099,3 +4099,694 @@ void MethodDesc::PrepareForUseAsAFunctionPointer() SetValueTypeParametersLoaded(); } #endif //!DACCESS_COMPILE + +#include "openum.h" + +class AlternateBlockHelper +{ +// COR_ILMETHOD* pHeader; + COR_ILMETHOD_DECODER header; + + SigBuilder nonCodeAlternateBlob; + SigBuilder alternateNonTypeRefStream; + SArray ilStream; + COUNT_T currentILStreamIterator; + SArray *pTypeRefTokenStream; + + MapSHash alternateTokens; + Module* pModule; + IMDInternalImport* pMDImport; + +public: + + AlternateBlockHelper(MethodDesc *pMD, SArray *pTypeRefTokenStreamInput) : +// pHeader(pMD->GetILHeader(TRUE)), + header(pMD->GetILHeader(TRUE), pMD->GetMDImport(), NULL), + currentILStreamIterator(0), + pTypeRefTokenStream(pTypeRefTokenStreamInput), + pModule(pMD->GetModule()), + pMDImport(pMD->GetMDImport()) + { + { + // Fill IL stream with initial data + byte* ilStreamData = ilStream.OpenRawBuffer(header.CodeSize); + memcpy(ilStreamData, header.Code, header.CodeSize); + ilStream.CloseRawBuffer(header.CodeSize); + } + } + + void GenerateDataStreams(SArray *pDataStream) + { + unsigned ehCount = header.EHCount(); + nonCodeAlternateBlob.AppendData(ehCount); + for (unsigned e = 0; e < ehCount; e++) + { + IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT ehBuff; + const IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT* ehInfo; + + ehInfo = header.EH->EHClause(e, &ehBuff); + + nonCodeAlternateBlob.AppendData(ehInfo->Flags); + nonCodeAlternateBlob.AppendData(ehInfo->TryOffset); + nonCodeAlternateBlob.AppendData(ehInfo->TryLength); + nonCodeAlternateBlob.AppendData(ehInfo->HandlerOffset); + nonCodeAlternateBlob.AppendData(ehInfo->HandlerLength); + if (ehInfo->Flags == CorExceptionFlag::COR_ILEXCEPTION_CLAUSE_NONE) // typed eh + { + nonCodeAlternateBlob.AppendToken(GetAlternateToken(ehInfo->ClassToken)); + } + else if (ehInfo->Flags == CorExceptionFlag::COR_ILEXCEPTION_CLAUSE_FILTER) + { + nonCodeAlternateBlob.AppendData(ehInfo->FilterOffset); + } + } + + if (header.cbLocalVarSig == 0) + { + nonCodeAlternateBlob.AppendByte(0); + } + else + { + nonCodeAlternateBlob.AppendByte((header.Flags & CorILMethod_InitLocals) ? 1 : 0); + SigParser localSigParser(header.LocalVarSig, header.cbLocalVarSig); + StandaloneSigTranslator sigTranslator(&localSigParser, &nonCodeAlternateBlob, this); + sigTranslator.ParseLocalsSignature(); + } + + while (!DoneReadingIL()) + { + uint32_t ilOpcode = ReadILByte(); + if (ilOpcode == CEE_PREFIX1) + { + ilOpcode = 0x100 + ReadILByte(); + } + + switch (ilOpcode) + { + case CEE_LDARG_S: + case CEE_LDARGA_S: + case CEE_STARG_S: + case CEE_LDLOC_S: + case CEE_LDLOCA_S: + case CEE_STLOC_S: + case CEE_LDC_I4_S: + case CEE_UNALIGNED: + case CEE_UNUSED69: // This is the no. prefix that is partially defined in Partition III. + SkipIL(1); + break; + case CEE_LDARG: + case CEE_LDARGA: + case CEE_STARG: + case CEE_LDLOC: + case CEE_LDLOCA: + case CEE_STLOC: + SkipIL(2); + break; + case CEE_LDC_I4: + case CEE_LDC_R4: + SkipIL(4); + break; + case CEE_LDC_I8: + case CEE_LDC_R8: + SkipIL(8); + break; + case CEE_JMP: + case CEE_CALL: + case CEE_CALLI: + case CEE_CALLVIRT: + case CEE_CPOBJ: + case CEE_LDOBJ: + case CEE_LDSTR: + case CEE_NEWOBJ: + case CEE_CASTCLASS: + case CEE_ISINST: + case CEE_UNBOX: + case CEE_LDFLD: + case CEE_LDFLDA: + case CEE_STFLD: + case CEE_LDSFLD: + case CEE_LDSFLDA: + case CEE_STSFLD: + case CEE_STOBJ: + case CEE_BOX: + case CEE_NEWARR: + case CEE_LDELEMA: + case CEE_LDELEM: + case CEE_STELEM: + case CEE_UNBOX_ANY: + case CEE_REFANYVAL: + case CEE_MKREFANY: + case CEE_LDTOKEN: + case CEE_LDFTN: + case CEE_LDVIRTFTN: + case CEE_INITOBJ: + case CEE_CONSTRAINED: + case CEE_SIZEOF: + ReplaceToken(); + break; + case CEE_BR_S: + case CEE_LEAVE_S: + case CEE_BRFALSE_S: + case CEE_BRTRUE_S: + case CEE_BEQ_S: + case CEE_BGE_S: + case CEE_BGT_S: + case CEE_BLE_S: + case CEE_BLT_S: + case CEE_BNE_UN_S: + case CEE_BGE_UN_S: + case CEE_BGT_UN_S: + case CEE_BLE_UN_S: + case CEE_BLT_UN_S: + SkipIL(1); + break; + case CEE_BR: + case CEE_LEAVE: + case CEE_BRFALSE: + case CEE_BRTRUE: + case CEE_BEQ: + case CEE_BGE: + case CEE_BGT: + case CEE_BLE: + case CEE_BLT: + case CEE_BNE_UN: + case CEE_BGE_UN: + case CEE_BGT_UN: + case CEE_BLE_UN: + case CEE_BLT_UN: + SkipIL(4); + break; + case CEE_SWITCH: + { + uint32_t count = ReadILUInt32(); + if (count > 0x1FFFFFFF) + ThrowHR(COR_E_BADIMAGEFORMAT); + SkipIL(count * 4); + } + break; + default: + continue; + } + } + + ClrSafeInt dataStreamSize; + DWORD nonCodeAlternateDataSize = 0; + DWORD alternateNonTypeRefStreamSize = 0; + PVOID nonCodeAlternateData = nonCodeAlternateBlob.GetSignature(&nonCodeAlternateDataSize); + PVOID alternateNonTypeRefStreamData = alternateNonTypeRefStream.GetSignature(&alternateNonTypeRefStreamSize); + dataStreamSize = ClrSafeInt(S_SIZE_T(nonCodeAlternateDataSize) + S_SIZE_T(ilStream.GetCount()) + S_SIZE_T(alternateNonTypeRefStreamSize)); + if (dataStreamSize.IsOverflow()) + ThrowHR(COR_E_BADIMAGEFORMAT); + + uint8_t *pData = pDataStream->OpenRawBuffer(dataStreamSize.Value()); + memcpy(pData, nonCodeAlternateData, nonCodeAlternateDataSize); + ilStream.Copy(pData + nonCodeAlternateDataSize, ilStream.Begin(), ilStream.GetCount()); + memcpy(pData + nonCodeAlternateDataSize + ilStream.GetCount(), alternateNonTypeRefStreamData, alternateNonTypeRefStreamSize); + pDataStream->CloseRawBuffer(); + } + + // Convert a token which may be a memberref parent into a member ref coded index + uint32_t DecodeMemberRefCodedIndexToken(uint32_t codedIndex) + { + static const mdToken s_tableTokenTypes[] = {mdtTypeDef, mdtTypeRef, mdtModuleRef, mdtMethodDef, mdtTypeSpec, 0, 0, 0}; + return TokenFromRid((codedIndex & 0xFFFFFF8) >> 3, s_tableTokenTypes[codedIndex & 0x7]); + } + + uint32_t MemberRefParentCodedIndex(mdToken tk) + { + RID rid = RidFromToken(tk); + ULONG32 ulTyp = TypeFromToken(tk); + + if ((rid > 0xFFFFFF) || rid == 0) + { + ThrowHR(COR_E_BADIMAGEFORMAT); + } + + rid = (rid << 3); + + // TypeDef is encoded with low bits 000 + // TypeRef is encoded with low bits 001 + // ModuleRef is encoded with low bits 010 + // MethodDef is encoded with low bit 011 + // TypeSpec is encoded with low bits 100 + for (uint32_t codedIndexType = 0; codedIndexType <= 7; codedIndexType++) + { + if (DecodeMemberRefCodedIndexToken(rid + codedIndexType) == tk) + { + return rid + codedIndexType; + } + } + ThrowHR(COR_E_BADIMAGEFORMAT); + } + + uint32_t GetAlternateToken(uint32_t inputToken) + { + uint32_t alternativeToken = 0; + if (!alternateTokens.Lookup(inputToken, &alternativeToken)) + { + if ((TypeFromToken(inputToken) == mdtTypeDef) || (TypeFromToken(inputToken) == mdtTypeRef)) + { + pTypeRefTokenStream->Append(inputToken); + alternativeToken = TokenFromRid((uint32_t)pTypeRefTokenStream->GetCount(), mdtTypeRef); + } + else + { + uint32_t newTokenType = 0; + SigBuilder blob; + switch (TypeFromToken(inputToken)) + { + case mdtString: + { + newTokenType = mdtString; + LPCWSTR wszUserString; + DWORD cchString; + IfFailThrow(pMDImport->GetUserString(inputToken, &cchString, NULL, &wszUserString)); + blob.AppendData(cchString); + blob.AppendBlob((void * const)wszUserString, sizeof(WCHAR) * cchString); + // TODO: consider encoding via wtf-8, or possibly utf-8 + break; + } + case mdtTypeSpec: + { + newTokenType = mdtTypeSpec; + PCCOR_SIGNATURE sigPtr; + ULONG cbSig; + IfFailThrow(pMDImport->GetTypeSpecFromToken(inputToken, &sigPtr, &cbSig)); + SigParser typeSpecSig(sigPtr, cbSig); + StandaloneSigTranslator sigTranslator(&typeSpecSig, &blob, this); + sigTranslator.ParseType(); + break; + } + case mdtMemberRef: + { + newTokenType = mdtMemberRef; + PCCOR_SIGNATURE sig; + ULONG cbSig; + LPCSTR name; + mdToken memberRefParent; + + IfFailThrow(pMDImport->GetNameAndSigOfMemberRef(inputToken, &sig, &cbSig, &name)); + IfFailThrow(pMDImport->GetParentOfMemberRef(inputToken, &memberRefParent)); + + SigParser memberRefSigParse(sig, cbSig); + + StandaloneSigTranslator sigTranslator(&memberRefSigParse, &blob, this); + sigTranslator.ParseMemberRefSignature(); + ULONG strLen = (ULONG)strlen(name); // Cast to ULONG is safe, as the data is held in a PE file + blob.AppendData((ULONG)strlen(name)); + blob.AppendBlob((const PVOID)name, strLen); + blob.AppendData(MemberRefParentCodedIndex(GetAlternateToken(memberRefParent))); + break; + } + case mdtMethodDef: + { + newTokenType = mdtMemberRef; + PCCOR_SIGNATURE sig; + ULONG cbSig; + LPCSTR name; + mdToken methodDefParent; + + IfFailThrow(pMDImport->GetNameAndSigOfMethodDef(inputToken, &sig, &cbSig, &name)); + IfFailThrow(pMDImport->GetParentToken(inputToken, &methodDefParent)); + SigParser methodDefSigParse(sig, cbSig); + StandaloneSigTranslator sigTranslator(&methodDefSigParse, &blob, this); + sigTranslator.ParseMethodSignature(); + ULONG strLen = (ULONG)strlen(name); // Cast to ULONG is safe, as the data is held in a PE file + blob.AppendData((ULONG)strlen(name)); + blob.AppendBlob((const PVOID)name, strLen); + blob.AppendData(MemberRefParentCodedIndex(GetAlternateToken(methodDefParent))); + break; + } + case mdtFieldDef: + { + newTokenType = mdtMemberRef; + PCCOR_SIGNATURE sig; + ULONG cbSig; + LPCSTR name; + mdToken fieldDefParent; + + IfFailThrow(pMDImport->GetSigOfFieldDef(inputToken, &cbSig, &sig)); + IfFailThrow(pMDImport->GetNameOfFieldDef(inputToken, &name)); + IfFailThrow(pMDImport->GetParentToken(inputToken, &fieldDefParent)); + SigParser fieldDefSigParse(sig, cbSig); + StandaloneSigTranslator sigTranslator(&fieldDefSigParse, &blob, this); + sigTranslator.ParseFieldSignature(); + ULONG strLen = (ULONG)strlen(name); // Cast to ULONG is safe, as the data is held in a PE file + blob.AppendData((ULONG)strlen(name)); + blob.AppendBlob((const PVOID)name, strLen); + blob.AppendData(MemberRefParentCodedIndex(GetAlternateToken(fieldDefParent))); + break; + } + case mdtMethodSpec: + { + newTokenType = mdtMethodSpec; + PCCOR_SIGNATURE sig; + ULONG cbSig; + mdToken methodSpecParent; + IfFailThrow(pMDImport->GetMethodSpecProps(inputToken, &methodSpecParent, &sig, &cbSig)); + mdToken tkMethodSpecParentAlternate = GetAlternateToken(methodSpecParent); + if (TypeFromToken(tkMethodSpecParentAlternate) != mdtMemberRef) + { + ThrowHR(COR_E_BADIMAGEFORMAT); + } + blob.AppendData(RidFromToken(tkMethodSpecParentAlternate)); + SigParser methodSpecSigParse(sig, cbSig); + StandaloneSigTranslator sigTranslator(&methodSpecSigParse, &blob, this); + sigTranslator.ParseMethodSpecSignature(); + break; + } + case mdtSignature: + { + newTokenType = mdtSignature; + PCCOR_SIGNATURE sig; + ULONG cbSig; + IfFailThrow(pMDImport->GetSigFromToken(inputToken, &cbSig, &sig)); + SigParser standaloneSigParse(sig, cbSig); + StandaloneSigTranslator sigTranslator(&standaloneSigParse, &blob, this); + sigTranslator.ParseMethodSignature(); + break; + } + + default: + ThrowHR(COR_E_BADIMAGEFORMAT); + } + + ULONG newStreamLen; + PVOID newSig = blob.GetSignature(&newStreamLen); + + alternateNonTypeRefStream.AppendBlob(newSig, newStreamLen); + alternativeToken = TokenFromRid((uint32_t)alternateTokens.GetCount() + 1, newTokenType); + } + alternateTokens.Add(inputToken, alternativeToken); + } + return alternativeToken; + } + + bool DoneReadingIL() + { + return currentILStreamIterator == ilStream.GetCount(); + } + uint8_t ReadILByte() + { + if (DoneReadingIL()) + { + ThrowHR(COR_E_BADIMAGEFORMAT); + } + uint8_t result = ilStream[currentILStreamIterator]; + ++currentILStreamIterator; + return result; + } + void SkipIL(size_t countToSkip) + { + currentILStreamIterator = currentILStreamIterator + (COUNT_T)countToSkip; + if (currentILStreamIterator > ilStream.GetCount()) + ThrowHR(COR_E_BADIMAGEFORMAT); + } + uint32_t ReadILUInt32() + { + uint32_t val; + if ((currentILStreamIterator + 4) > ilStream.GetCount()) + ThrowHR(COR_E_BADIMAGEFORMAT); + ilStream.Copy(&val, ilStream.Begin() + currentILStreamIterator, 4); + currentILStreamIterator = currentILStreamIterator + 4; + val = VAL32(val); + return val; + } + void ReplaceToken() + { + if ((currentILStreamIterator + 4) > ilStream.GetCount()) + ThrowHR(COR_E_BADIMAGEFORMAT); + + uint32_t token; + ilStream.Copy(&token, ilStream.Begin() + currentILStreamIterator, 4); + token = VAL32(token); + uint32_t newToken = GetAlternateToken(token); + newToken = VAL32(newToken); + ilStream.Copy(ilStream.Begin() + currentILStreamIterator, reinterpret_cast(&newToken), 4); + currentILStreamIterator = currentILStreamIterator + 4; + } + + class StandaloneSigTranslator + { + SigParser *pSigInput; + SigBuilder *pSigOutput; + AlternateBlockHelper *pHelper; + + uint8_t ParseByte() + { + uint8_t value; + IfFailThrow(pSigInput->GetByte(&value)); + pSigOutput->AppendByte(value); + return value; + } + + uint8_t PeekByte() + { + uint8_t value; + IfFailThrow(pSigInput->PeekByte(&value)); + return value; + } + + uint32_t ParseCompressedInt() + { + uint32_t value; + IfFailThrow(pSigInput->GetData(&value)); + pSigOutput->AppendData(value); + return value; + } + + void ParseTypeHandle() + { + uint32_t token; + IfFailThrow(pSigInput->GetToken(&token)); + uint32_t newToken = pHelper->GetAlternateToken(token); + pSigOutput->AppendToken(newToken); + } + + public: + StandaloneSigTranslator(SigParser *sigInput, SigBuilder* sigOutput, AlternateBlockHelper *helper) : + pSigInput(sigInput), + pSigOutput(sigOutput), + pHelper(helper) + {} + + void ParseType() + { + CorElementType elemType; + for (;;) + { + elemType = (CorElementType)ParseByte(); + switch (elemType) + { + case ELEMENT_TYPE_CMOD_REQD: + case ELEMENT_TYPE_CMOD_OPT: + ParseTypeHandle(); + continue; + case ELEMENT_TYPE_PINNED: + case ELEMENT_TYPE_SENTINEL: + continue; + + default: + break; + } + break; + } + + switch (elemType) + { + case ELEMENT_TYPE_VOID: + case ELEMENT_TYPE_BOOLEAN: + case ELEMENT_TYPE_CHAR: + case ELEMENT_TYPE_I1: + case ELEMENT_TYPE_U1: + case ELEMENT_TYPE_I2: + case ELEMENT_TYPE_U2: + case ELEMENT_TYPE_I4: + case ELEMENT_TYPE_U4: + case ELEMENT_TYPE_I8: + case ELEMENT_TYPE_U8: + case ELEMENT_TYPE_R4: + case ELEMENT_TYPE_R8: + case ELEMENT_TYPE_STRING: + case ELEMENT_TYPE_OBJECT: + case ELEMENT_TYPE_TYPEDBYREF: + case ELEMENT_TYPE_I: + case ELEMENT_TYPE_U: + break; + + case ELEMENT_TYPE_SZARRAY: + case ELEMENT_TYPE_BYREF: + case ELEMENT_TYPE_PTR: + ParseType(); + break; + case ELEMENT_TYPE_ARRAY: + { + ParseType(); + uint32_t rank = ParseCompressedInt(); + uint32_t boundsCount = ParseCompressedInt(); + for (uint32_t i = 0; i < boundsCount; i++) + { + ParseCompressedInt(); + } + uint32_t lowerBoundsCount = ParseCompressedInt(); + for (uint32_t i = 0; i < lowerBoundsCount; i++) + { + ParseCompressedInt(); // We don't need to parse as signed compressed ints as those can be round-tripped without distinguishing from a normal compressed int + } + break; + } + case ELEMENT_TYPE_VAR: + case ELEMENT_TYPE_MVAR: + ParseCompressedInt(); + break; + case ELEMENT_TYPE_GENERICINST: + { + ParseType(); + uint32_t instanceLength = ParseCompressedInt(); + for (uint32_t i = 0; i < instanceLength; i++) + { + ParseType(); + } + break; + } + case ELEMENT_TYPE_FNPTR: + { + ParseMethodSignature(); + break; + } + case ELEMENT_TYPE_CLASS: + case ELEMENT_TYPE_VALUETYPE: + { + ParseTypeHandle(); + break; + } + default: + ThrowHR(COR_E_BADIMAGEFORMAT); + } + } + + void ParseLocalsSignature() + { + uint8_t sigHeader = ParseByte(); + if (sigHeader != IMAGE_CEE_CS_CALLCONV_LOCAL_SIG) + ThrowHR(COR_E_BADIMAGEFORMAT); + + uint32_t localsCount = ParseCompressedInt(); + for (uint32_t i = 0; i < localsCount; i++) + { + ParseType(); + } + } + + void ParseMemberRefSignature() + { + uint8_t sigHeader = PeekByte(); + if (sigHeader == IMAGE_CEE_CS_CALLCONV_FIELD) + { + ParseFieldSignature(); + } + else + { + ParseMethodSignature(); + } + } + + void ParseFieldSignature() + { + uint8_t sigHeader = ParseByte(); + if (sigHeader != IMAGE_CEE_CS_CALLCONV_FIELD) + { + ThrowHR(COR_E_BADIMAGEFORMAT); + } + + ParseType(); + } + + void ParseMethodSpecSignature() + { + uint8_t sigHeader = ParseByte(); + if (sigHeader != IMAGE_CEE_CS_CALLCONV_GENERICINST) + { + ThrowHR(COR_E_BADIMAGEFORMAT); + } + + uint32_t argCount = ParseCompressedInt(); + for (uint32_t i = 0; i < argCount; i++) + { + ParseType(); + } + } + + void ParseMethodSignature() + { + uint8_t sigHeader = ParseByte(); + if (sigHeader & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + // Parse arity + ParseCompressedInt(); + } + uint32_t argCount = ParseCompressedInt(); + for (uint32_t i = 0; i <= argCount; i++) + { + ParseType(); + } + } + }; +}; + +#ifndef DACCESS_COMPILE +static CrstStatic s_csMethodBlocks; +static MapSHash s_methodBlocks; + +void GenerateMethodBlock(MethodDesc *pMD, MethodBlock *pBlock) +{ + SArray tokenStream; + SArray byteData; + AlternateBlockHelper helper(pMD, &tokenStream); + helper.GenerateDataStreams(&byteData); + + pBlock->cByteData = byteData.GetCount(); + pBlock->pByteData = new uint8_t[pBlock->cByteData]; + pBlock->cTypes = tokenStream.GetCount(); + pBlock->pTypes = new TypeHandle[pBlock->cTypes]; + + byteData.Copy((uint8_t*)pBlock->pByteData, byteData.Begin(), byteData.GetCount()); + for (COUNT_T i = 0; i < tokenStream.GetCount(); i++) + { + ((TypeHandle*)pBlock->pTypes)[i] = ClassLoader::LoadTypeDefOrRefThrowing(pMD->GetModule(), tokenStream[i], ClassLoader::ThrowIfNotFound, ClassLoader::PermitUninstDefOrRef, 0, CLASS_LOAD_APPROXPARENTS); + } +} + +void InitMethodBlocks() +{ + s_csMethodBlocks.Init(CrstLeafLock); +} + +MethodBlock* GetMethodBlock(MethodDesc *pMD) +{ + MethodBlock* retVal; + + { + CrstHolder lock(&s_csMethodBlocks); + if (s_methodBlocks.Lookup(pMD, &retVal)) + { + return retVal; + } + } + + NewHolder newMethodBlock = new MethodBlock(); + GenerateMethodBlock(pMD, newMethodBlock); + + { + CrstHolder lock(&s_csMethodBlocks); + if (s_methodBlocks.Lookup(pMD, &retVal)) + { + return retVal; + } + + s_methodBlocks.Add(pMD, newMethodBlock); + retVal = newMethodBlock.Extract(); + } + return retVal; +} +#endif // DACCESS_COMPILE \ No newline at end of file diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index bc58090dd587d..3caf524f2196b 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -3705,6 +3705,31 @@ class CalledMethod }; #endif +struct MethodBlock +{ + MethodBlock() : + pByteData(nullptr), + cByteData(0), + pTypes(nullptr), + cTypes(0) + {} + + ~MethodBlock() + { + if (pByteData != nullptr) + delete[] pByteData; + if (pTypes != nullptr) + delete[] pTypes; + } + + const uint8_t * pByteData; + size_t cByteData; + const TypeHandle * pTypes; + size_t cTypes; +}; +MethodBlock* GetMethodBlock(MethodDesc *pMD); +void InitMethodBlocks(); + #include "method.inl" #endif // !_METHOD_H diff --git a/src/coreclr/vm/nativeimage.h b/src/coreclr/vm/nativeimage.h index e4195d37be3f9..7fc392ed5472e 100644 --- a/src/coreclr/vm/nativeimage.h +++ b/src/coreclr/vm/nativeimage.h @@ -60,6 +60,10 @@ class ReadyToRunInfo; class PEAssembly; class PEImage; +#ifndef DACCESS_COMPILE +ModuleBase* CreateNativeManifestModule(LoaderAllocator* pLoaderAllocator, IMDInternalImport *m_pManifestMetadata, AllocMemTracker *pamTracker); +#endif + // This class represents a ReadyToRun image with native OS-specific envelope. As of today, // this file format is used as the compiled native code cache in composite R2R Crossgen2 // build mode. Moving forward we plan to add support for OS-specific native executables @@ -81,6 +85,7 @@ class NativeImage IMDInternalImport *m_pManifestMetadata; PEImageLayout *m_pImageLayout; PTR_Assembly *m_pNativeMetadataAssemblyRefMap; + PTR_ModuleBase m_pNativeManifestModule; IMAGE_DATA_DIRECTORY *m_pComponentAssemblies; IMAGE_DATA_DIRECTORY *m_pComponentAssemblyMvids; diff --git a/src/coreclr/vm/peimage.cpp b/src/coreclr/vm/peimage.cpp index b9bb8ff068b1f..9bd97cd913892 100644 --- a/src/coreclr/vm/peimage.cpp +++ b/src/coreclr/vm/peimage.cpp @@ -102,8 +102,6 @@ PEImage::~PEImage() if (m_pMDImport) m_pMDImport->Release(); - if(m_pNativeMDImport) - m_pNativeMDImport->Release(); #ifdef METADATATRACKER_ENABLED if (m_pMDTracker != NULL) m_pMDTracker->Deactivate(); @@ -296,64 +294,6 @@ IMDInternalImport* PEImage::GetMDImport() return m_pMDImport; } -IMDInternalImport* PEImage::GetNativeMDImport(BOOL loadAllowed) -{ - CONTRACTL - { - INSTANCE_CHECK; - PRECONDITION(HasReadyToRunHeader()); - if (loadAllowed) GC_TRIGGERS; else GC_NOTRIGGER; - if (loadAllowed) THROWS; else NOTHROW; - if (loadAllowed) INJECT_FAULT(COMPlusThrowOM()); else FORBID_FAULT; - MODE_ANY; - } - CONTRACTL_END; - - if (m_pNativeMDImport == NULL) - { - if (loadAllowed) - OpenNativeMDImport(); - else - return NULL; - } - - _ASSERTE(m_pNativeMDImport); - return m_pNativeMDImport; -} - -void PEImage::OpenNativeMDImport() -{ - CONTRACTL - { - INSTANCE_CHECK; - PRECONDITION(HasReadyToRunHeader()); - GC_TRIGGERS; - THROWS; - MODE_ANY; - INJECT_FAULT(COMPlusThrowOM();); - } - CONTRACTL_END; - if (m_pNativeMDImport==NULL) - { - IMDInternalImport* m_pNewImport; - COUNT_T cMeta=0; - const void* pMeta=GetNativeManifestMetadata(&cMeta); - - if(pMeta==NULL) - return; - - IfFailThrow(GetMetaDataInternalInterface((void *) pMeta, - cMeta, - ofRead, - IID_IMDInternalImport, - (void **) &m_pNewImport)); - - if(FastInterlockCompareExchangePointer(&m_pNativeMDImport, m_pNewImport, NULL)) - m_pNewImport->Release(); - } - _ASSERTE(m_pNativeMDImport); -} - void PEImage::OpenMDImport() { CONTRACTL @@ -715,8 +655,7 @@ PEImage::PEImage(): #ifdef METADATATRACKER_DATA m_pMDTracker(NULL), #endif // METADATATRACKER_DATA - m_pMDImport(NULL), - m_pNativeMDImport(NULL) + m_pMDImport(NULL) { CONTRACTL { diff --git a/src/coreclr/vm/peimage.h b/src/coreclr/vm/peimage.h index f353cebe978d1..be5074a0ffba2 100644 --- a/src/coreclr/vm/peimage.h +++ b/src/coreclr/vm/peimage.h @@ -151,7 +151,6 @@ class PEImage final BOOL HasV1Metadata(); IMDInternalImport* GetMDImport(); BOOL MDImportLoaded(); - IMDInternalImport* GetNativeMDImport(BOOL loadAllowed = TRUE); BOOL HasContents() ; BOOL IsPtrInImage(PTR_CVOID data); @@ -205,7 +204,6 @@ class PEImage final PTR_PEImageLayout GetExistingLayoutInternal(DWORD imageLayoutMask); void OpenMDImport(); - void OpenNativeMDImport(); // ------------------------------------------------------------ // Private routines // ------------------------------------------------------------ @@ -329,7 +327,6 @@ class PEImage final #endif // METADATATRACKER_DATA IMDInternalImport* m_pMDImport; - IMDInternalImport* m_pNativeMDImport; }; FORCEINLINE void PEImageRelease(PEImage *i) diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index e1ff1a2ac43a2..cca1d5d30ee76 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -503,7 +503,7 @@ PCODE MethodDesc::GetPrecompiledR2RCode(PrepareCodeConfig* pConfig) PCODE pCode = NULL; #ifdef FEATURE_READYTORUN - Module * pModule = GetModule(); + Module * pModule = GetLoaderModule(); if (pModule->IsReadyToRun()) { pCode = pModule->GetReadyToRunInfo()->GetEntryPoint(this, pConfig, TRUE /* fFixups */); @@ -2429,7 +2429,7 @@ EXTERN_C PCODE STDCALL ExternalMethodFixupWorker(TransitionBlock * pTransitionBl BYTE kind = *pBlob++; - Module * pInfoModule = pModule; + ModuleBase * pInfoModule = pModule; if (kind & ENCODE_MODULE_OVERRIDE) { DWORD moduleIndex = CorSigUncompressData(pBlob); @@ -2446,6 +2446,8 @@ EXTERN_C PCODE STDCALL ExternalMethodFixupWorker(TransitionBlock * pTransitionBl pInfoModule, pBlob); + _ASSERTE(!pMD->GetMethodTable()->IsGenericTypeDefinition() || pMD->GetMethodTable()->GetNumGenericArgs() == 0); + if (pModule->IsReadyToRun()) { // We do not emit activation fixups for version resilient references. Activate the target explicitly. @@ -2458,7 +2460,8 @@ EXTERN_C PCODE STDCALL ExternalMethodFixupWorker(TransitionBlock * pTransitionBl case ENCODE_METHOD_ENTRY_DEF_TOKEN: { mdToken MethodDef = TokenFromRid(CorSigUncompressData(pBlob), mdtMethodDef); - pMD = MemberLoader::GetMethodDescFromMethodDef(pInfoModule, MethodDef, FALSE); + _ASSERTE(pInfoModule->IsFullModule()); + pMD = MemberLoader::GetMethodDescFromMethodDef(static_cast(pInfoModule), MethodDef, FALSE); pMD->PrepareForUseAsADependencyOfANativeImage(); @@ -2517,7 +2520,8 @@ EXTERN_C PCODE STDCALL ExternalMethodFixupWorker(TransitionBlock * pTransitionBl case ENCODE_VIRTUAL_ENTRY_DEF_TOKEN: { mdToken MethodDef = TokenFromRid(CorSigUncompressData(pBlob), mdtMethodDef); - pMD = MemberLoader::GetMethodDescFromMethodDef(pInfoModule, MethodDef, FALSE); + _ASSERTE(pInfoModule->IsFullModule()); + pMD = MemberLoader::GetMethodDescFromMethodDef(static_cast(pInfoModule), MethodDef, FALSE); goto VirtualEntry; } @@ -2816,7 +2820,7 @@ TADDR GetFirstArgumentRegisterValuePtr(TransitionBlock * pTransitionBlock) void ProcessDynamicDictionaryLookup(TransitionBlock * pTransitionBlock, Module * pModule, - Module * pInfoModule, + ModuleBase * pInfoModule, BYTE kind, PCCOR_SIGNATURE pBlob, PCCOR_SIGNATURE pBlobStart, @@ -2979,7 +2983,7 @@ PCODE DynamicHelperFixup(TransitionBlock * pTransitionBlock, TADDR * pCell, DWOR BYTE kind = *pBlob++; - Module * pInfoModule = pModule; + ModuleBase * pInfoModule = pModule; if (kind & ENCODE_MODULE_OVERRIDE) { DWORD moduleIndex = CorSigUncompressData(pBlob); diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index d2228aeba4bbb..cbc7e7e87575a 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -20,11 +20,12 @@ using namespace NativeFormat; ReadyToRunCoreInfo::ReadyToRunCoreInfo() + : m_fForbidLoadILBodyFixups(false) { } ReadyToRunCoreInfo::ReadyToRunCoreInfo(PEImageLayout* pLayout, READYTORUN_CORE_HEADER *pCoreHeader) - : m_pLayout(pLayout), m_pCoreHeader(pCoreHeader) + : m_pLayout(pLayout), m_pCoreHeader(pCoreHeader), m_fForbidLoadILBodyFixups(false) { } @@ -626,6 +627,7 @@ ReadyToRunInfo::ReadyToRunInfo(Module * pModule, LoaderAllocator* pLoaderAllocat m_pComposite = m_pCompositeInfo->GetComponentInfo(); m_component = ReadyToRunCoreInfo(m_pComposite->GetLayout(), pNativeImage->GetComponentAssemblyHeader(pModule->GetSimpleName())); m_isComponentAssembly = true; + m_pNativeManifestModule = pNativeImage->GetReadyToRunInfo()->GetNativeManifestModule(); } else { @@ -633,6 +635,23 @@ ReadyToRunInfo::ReadyToRunInfo(Module * pModule, LoaderAllocator* pLoaderAllocat m_component = ReadyToRunCoreInfo(pLayout, &pHeader->CoreHeader); m_pComposite = &m_component; m_isComponentAssembly = false; + IMDInternalImport *pNativeMDImport; + IMAGE_DATA_DIRECTORY * pNativeMetadatSection = m_pComposite->FindSection(ReadyToRunSectionType::ManifestMetadata); + if (pNativeMetadatSection != NULL) + { + pNativeMDImport = NULL; + IfFailThrow(GetMetaDataInternalInterface((void *) m_pComposite->GetLayout()->GetDirectoryData(pNativeMetadatSection), + pNativeMetadatSection->Size, + ofRead, + IID_IMDInternalImport, + (void **) &pNativeMDImport)); + } + else + { + pNativeMDImport = NULL; + } + + m_pNativeManifestModule = CreateNativeManifestModule(pLoaderAllocator, pNativeMDImport, pamTracker); } IMAGE_DATA_DIRECTORY * pRuntimeFunctionsDir = m_pComposite->FindSection(ReadyToRunSectionType::RuntimeFunctions); @@ -706,6 +725,17 @@ ReadyToRunInfo::ReadyToRunInfo(Module * pModule, LoaderAllocator* pLoaderAllocat m_entryPointToMethodDescMap.Init(TRUE, &lock); } + if (IsImageVersionAtLeast(6, 2)) + { + IMAGE_DATA_DIRECTORY* pCrossModuleInlineTrackingInfoDir = m_pComposite->FindSection(ReadyToRunSectionType::CrossModuleInlineInfo); + if (pCrossModuleInlineTrackingInfoDir != NULL) + { + const BYTE* pCrossModuleInlineTrackingMapData = (const BYTE*)m_pComposite->GetImage()->GetDirectoryData(pCrossModuleInlineTrackingInfoDir); + CrossModulePersistentInlineTrackingMapR2R::TryLoad(pModule, pLoaderAllocator, pCrossModuleInlineTrackingMapData, pCrossModuleInlineTrackingInfoDir->Size, + pamTracker, (CrossModulePersistentInlineTrackingMapR2R**)&m_pCrossModulePersistentInlineTrackingMap); + } + } + // For format version 4.1 and later, there is an optional inlining table if (IsImageVersionAtLeast(4, 1)) { @@ -744,16 +774,28 @@ ReadyToRunInfo::ReadyToRunInfo(Module * pModule, LoaderAllocator* pLoaderAllocat } } -static bool SigMatchesMethodDesc(MethodDesc* pMD, SigPointer &sig, Module * pModule) +static bool SigMatchesMethodDesc(MethodDesc* pMD, SigPointer &sig, ModuleBase * pModule) { STANDARD_VM_CONTRACT; + ModuleBase *pOrigModule = pModule; ZapSig::Context zapSigContext(pModule, (void *)pModule, ZapSig::NormalTokens); ZapSig::Context * pZapSigContext = &zapSigContext; uint32_t methodFlags; IfFailThrow(sig.GetData(&methodFlags)); + _ASSERTE((methodFlags & ENCODE_METHOD_SIG_SlotInsteadOfToken) == 0); + _ASSERTE(((methodFlags & (ENCODE_METHOD_SIG_MemberRefToken | ENCODE_METHOD_SIG_UpdateContext)) == 0) || + ((methodFlags & (ENCODE_METHOD_SIG_MemberRefToken | ENCODE_METHOD_SIG_UpdateContext)) == (ENCODE_METHOD_SIG_MemberRefToken | ENCODE_METHOD_SIG_UpdateContext))); + + if ( methodFlags & ENCODE_METHOD_SIG_UpdateContext) + { + uint32_t updatedModuleIndex; + IfFailThrow(sig.GetData(&updatedModuleIndex)); + pModule = pZapSigContext->GetZapSigModule()->GetModuleFromIndex(updatedModuleIndex); + } + if (methodFlags & ENCODE_METHOD_SIG_OwnerType) { PCCOR_SIGNATURE pSigType; @@ -765,11 +807,42 @@ static bool SigMatchesMethodDesc(MethodDesc* pMD, SigPointer &sig, Module * pMod IfFailThrow(sig.SkipExactlyOne()); } - _ASSERTE((methodFlags & ENCODE_METHOD_SIG_SlotInsteadOfToken) == 0); - _ASSERTE((methodFlags & ENCODE_METHOD_SIG_MemberRefToken) == 0); - RID rid; IfFailThrow(sig.GetData(&rid)); + + if ((methodFlags & ENCODE_METHOD_SIG_MemberRefToken) != 0) + { + // member referenced via Manifest data + // But we've already verified that the owner type is defined, so we can assume the type from the passed in methoddesc + IMDInternalImport * pInternalImport = pModule->GetMDImport(); + + LPCUTF8 szMember; + PCCOR_SIGNATURE pSig; + DWORD cSig; + + IfFailThrow(pInternalImport->GetNameAndSigOfMemberRef(TokenFromRid(rid, mdtMemberRef), &pSig, &cSig, &szMember)); + + _ASSERTE(!isCallConv(MetaSig::GetCallingConvention(Signature(pSig, cSig)), IMAGE_CEE_CS_CALLCONV_FIELD)); + + if (strcmp(szMember, pMD->GetName()) != 0) + { + // Name doesn't match + return false; + } + + PCCOR_SIGNATURE pTargetMethodSig; + DWORD cTargetMethodSig; + + pMD->GetSig(&pTargetMethodSig, &cTargetMethodSig); + if (!MetaSig::CompareMethodSigs(pSig, cSig, pModule, NULL, pTargetMethodSig, cTargetMethodSig, pMD->GetModule(), NULL, FALSE)) + { + // Sig doesn't match + return false; + } + + rid = RidFromToken(pMD->GetMemberDef()); + } + if (RidFromToken(pMD->GetMemberDef()) != rid) return false; @@ -786,7 +859,7 @@ static bool SigMatchesMethodDesc(MethodDesc* pMD, SigPointer &sig, Module * pMod PCCOR_SIGNATURE pSigArg; uint32_t cbSigArg; sig.GetSignature(&pSigArg, &cbSigArg); - if (!ZapSig::CompareSignatureToTypeHandle(pSigArg, pModule, inst[i], pZapSigContext)) + if (!ZapSig::CompareSignatureToTypeHandle(pSigArg, pOrigModule, inst[i], pZapSigContext)) return false; IfFailThrow(sig.SkipExactlyOne()); @@ -862,10 +935,18 @@ bool ReadyToRunInfo::GetPgoInstrumentationData(MethodDesc * pMD, BYTE** pAllocat return false; } +//#define LOG_R2R_ENTRYPOINT PCODE ReadyToRunInfo::GetEntryPoint(MethodDesc * pMD, PrepareCodeConfig* pConfig, BOOL fFixups) { STANDARD_VM_CONTRACT; +#ifdef LOG_R2R_ENTRYPOINT + SString tNamespace, tMethodName, tMethodSignature; + SString tFullname; + StackScratchBuffer scratch; + const char* szFullName = ""; + bool printedStart = false; +#endif PCODE pEntryPoint = NULL; #ifdef PROFILING_SUPPORTED @@ -875,11 +956,21 @@ PCODE ReadyToRunInfo::GetEntryPoint(MethodDesc * pMD, PrepareCodeConfig* pConfig int rid = RidFromToken(token); if (rid == 0) goto done; - // If R2R code is disabled for this module, simply behave as if it is never found if (ReadyToRunCodeDisabled()) goto done; +#ifdef LOG_R2R_ENTRYPOINT + pMD->GetMethodInfo(tNamespace, tMethodName, tMethodSignature); + tFullname.Append(tNamespace); + tFullname.Append(tMethodName); + tFullname.Append(tMethodSignature); + szFullName = tFullname.GetUTF8(scratch); + + printf("ReadyToRunInfo::GetEntryPoint %s\n", szFullName); + printedStart = true; +#endif + ETW::MethodLog::GetR2RGetEntryPointStart(pMD); uint offset; @@ -982,6 +1073,11 @@ PCODE ReadyToRunInfo::GetEntryPoint(MethodDesc * pMD, PrepareCodeConfig* pConfig } done: +#ifdef LOG_R2R_ENTRYPOINT + if (printedStart) + printf("ReadyToRunInfo::GetEntryPoint Found: %p %s\n", (void*)pEntryPoint, szFullName); +#endif + if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, R2RGetEntryPoint)) { ETW::MethodLog::GetR2RGetEntryPoint(pMD, pEntryPoint); @@ -1008,6 +1104,16 @@ void ReadyToRunInfo::MethodIterator::ParseGenericMethodSignatureAndRid(uint *pOf return; } + _ASSERTE((methodFlags & ENCODE_METHOD_SIG_SlotInsteadOfToken) == 0); + _ASSERTE(((methodFlags & (ENCODE_METHOD_SIG_MemberRefToken | ENCODE_METHOD_SIG_UpdateContext)) == 0) || + ((methodFlags & (ENCODE_METHOD_SIG_MemberRefToken | ENCODE_METHOD_SIG_UpdateContext)) == (ENCODE_METHOD_SIG_MemberRefToken | ENCODE_METHOD_SIG_UpdateContext))); + + if ( methodFlags & ENCODE_METHOD_SIG_UpdateContext) + { + uint32_t updatedModuleIndex; + IfFailThrow(sig.GetData(&updatedModuleIndex)); + } + if (methodFlags & ENCODE_METHOD_SIG_OwnerType) { hr = sig.SkipExactlyOne(); @@ -1017,9 +1123,6 @@ void ReadyToRunInfo::MethodIterator::ParseGenericMethodSignatureAndRid(uint *pOf } } - _ASSERTE((methodFlags & ENCODE_METHOD_SIG_SlotInsteadOfToken) == 0); - _ASSERTE((methodFlags & ENCODE_METHOD_SIG_MemberRefToken) == 0); - hr = sig.GetData(pRid); if (FAILED(hr)) { @@ -1226,4 +1329,148 @@ void ReadyToRunInfo::DisableCustomAttributeFilter() m_attributesPresence.DisableFilter(); } +class NativeManifestModule : public ModuleBase +{ + IMDInternalImport* m_pMDImport; + + // Mapping of ModuleRef token to Module * + LookupMap m_ModuleReferencesMap; +public: + + NativeManifestModule(LoaderAllocator* pLoaderAllocator, IMDInternalImport *pManifestMetadata, AllocMemTracker *pamTracker) + { + m_loaderAllocator = pLoaderAllocator; + m_pMDImport = pManifestMetadata; + m_LookupTableCrst.Init(CrstModuleLookupTable, CrstFlags(CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD)); + { + // Get the number of AssemblyReferences in the map + m_ManifestModuleReferencesMap.dwCount = pManifestMetadata->GetCountWithTokenKind(mdtAssemblyRef)+1; + + // Get # ModuleRefs + m_ModuleReferencesMap.dwCount = pManifestMetadata->GetCountWithTokenKind(mdtModuleRef)+1; + + // Get # TypeRefs + m_TypeRefToMethodTableMap.dwCount = pManifestMetadata->GetCountWithTokenKind(mdtTypeRef)+1; + + // Get # of MemberRefs + m_MemberRefMap.dwCount = pManifestMetadata->GetCountWithTokenKind(mdtMemberRef)+1; + + S_SIZE_T nTotal; + nTotal += m_ManifestModuleReferencesMap.dwCount; + nTotal += m_ModuleReferencesMap.dwCount; + nTotal += m_TypeRefToMethodTableMap.dwCount; + nTotal += m_MemberRefMap.dwCount; + PTR_TADDR pTable = (PTR_TADDR)pamTracker->Track(pLoaderAllocator->GetLowFrequencyHeap()->AllocMem(nTotal * S_SIZE_T(sizeof(TADDR)))); + + // Note: Memory allocated on loader heap is zero filled + // memset(pTable, 0, nTotal * sizeof(void*)); + + m_ManifestModuleReferencesMap.pNext = NULL; + m_ManifestModuleReferencesMap.supportedFlags = MANIFEST_MODULE_MAP_ALL_FLAGS; + m_ManifestModuleReferencesMap.pTable = pTable; + + m_ModuleReferencesMap.pNext = NULL; + m_ModuleReferencesMap.supportedFlags = NO_MAP_FLAGS; + m_ModuleReferencesMap.pTable = &m_ManifestModuleReferencesMap.pTable[m_ManifestModuleReferencesMap.dwCount]; + + m_TypeRefToMethodTableMap.pNext = NULL; + m_TypeRefToMethodTableMap.supportedFlags = TYPE_REF_MAP_ALL_FLAGS; + m_TypeRefToMethodTableMap.pTable = &m_ModuleReferencesMap.pTable[m_ModuleReferencesMap.dwCount]; + + m_MemberRefMap.pNext = NULL; + m_MemberRefMap.supportedFlags = MEMBER_REF_MAP_ALL_FLAGS; + m_MemberRefMap.pTable = &m_TypeRefToMethodTableMap.pTable[m_TypeRefToMethodTableMap.dwCount]; + } + } + + IMDInternalImport *GetMDImport() const final + { + return m_pMDImport; + } + + PTR_Module LookupModule(mdToken kFile) final + { + return GetModuleIfLoaded(kFile); + } + + DomainAssembly * LoadAssemblyImpl(mdAssemblyRef kAssemblyRef) final + { + STANDARD_VM_CONTRACT; + // Since we can only load via ModuleRef, this should never fail unless the module is improperly formatted + COMPlusThrowHR(COR_E_BADIMAGEFORMAT); + } + + Module *GetModuleIfLoaded(mdFile kFile) final + { + CONTRACT(Module *) + { + INSTANCE_CHECK; + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + PRECONDITION(TypeFromToken(kFile) == mdtFile + || TypeFromToken(kFile) == mdtModuleRef); + POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); + FORBID_FAULT; + SUPPORTS_DAC; + } + CONTRACT_END; + + // Native manifest module functionality isn't actually multi-module assemblies, and File tokens are not useable + if (TypeFromToken(kFile) == mdtFile) + return NULL; + + _ASSERTE(TypeFromToken(kFile) == mdtModuleRef); + Module* module = m_ModuleReferencesMap.GetElement(RidFromToken(kFile)); + if (module != NULL) + RETURN module; + + LPCSTR moduleName; + if (FAILED(GetMDImport()->GetModuleRefProps(kFile, &moduleName))) + { + RETURN NULL; + } + + if (strcmp(moduleName, "System.Private.CoreLib") == 0) + { + // Special handling for CoreLib + module = SystemDomain::SystemModule(); +#ifndef DACCESS_COMPILE + m_ModuleReferencesMap.TrySetElement(RidFromToken(kFile), module); +#endif + RETURN module; + } + + RETURN NULL; + } + + DomainAssembly *LoadModule(mdFile kFile) final + { + Module* module = GetModuleIfLoaded(kFile); + if (module == NULL) + { + // Since we can only load via ModuleRef, this should never fail unless the module is improperly formatted + COMPlusThrowHR(COR_E_BADIMAGEFORMAT); + } + + return module->GetDomainAssembly(); + } + + virtual void DECLSPEC_NORETURN ThrowTypeLoadExceptionImpl(IMDInternalImport *pInternalImport, + mdToken token, + UINT resIDWhy) + { + STANDARD_VM_CONTRACT; + // This should never fail + COMPlusThrowHR(COR_E_BADIMAGEFORMAT); + } +}; + +ModuleBase* CreateNativeManifestModule(LoaderAllocator* pLoaderAllocator, IMDInternalImport *pManifestMetadata, AllocMemTracker *pamTracker) +{ + // Should be moved to ModuleBase initialization + void *mem = pamTracker->Track(pLoaderAllocator->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(NativeManifestModule)))); + return new (mem) NativeManifestModule(pLoaderAllocator, pManifestMetadata, pamTracker); +} + #endif // DACCESS_COMPILE diff --git a/src/coreclr/vm/readytoruninfo.h b/src/coreclr/vm/readytoruninfo.h index 0264629e5b82c..bd828c2e51888 100644 --- a/src/coreclr/vm/readytoruninfo.h +++ b/src/coreclr/vm/readytoruninfo.h @@ -27,6 +27,7 @@ class ReadyToRunCoreInfo private: PTR_PEImageLayout m_pLayout; PTR_READYTORUN_CORE_HEADER m_pCoreHeader; + Volatile m_fForbidLoadILBodyFixups; public: ReadyToRunCoreInfo(); @@ -34,6 +35,8 @@ class ReadyToRunCoreInfo PTR_PEImageLayout GetLayout() const { return m_pLayout; } IMAGE_DATA_DIRECTORY * FindSection(ReadyToRunSectionType type) const; + void ForbidProcessMoreILBodyFixups() { m_fForbidLoadILBodyFixups = true; } + bool IsForbidProcessMoreILBodyFixups() { return m_fForbidLoadILBodyFixups; } PTR_PEImageLayout GetImage() const { @@ -50,6 +53,7 @@ class ReadyToRunInfo friend class ReadyToRunJitManager; PTR_Module m_pModule; + PTR_ModuleBase m_pNativeManifestModule; PTR_READYTORUN_HEADER m_pHeader; bool m_isComponentAssembly; PTR_NativeImage m_pNativeImage; @@ -81,6 +85,7 @@ class ReadyToRunInfo PtrHashMap m_entryPointToMethodDescMap; PTR_PersistentInlineTrackingMapR2R m_pPersistentInlineTrackingMap; + PTR_PersistentInlineTrackingMapR2R m_pCrossModulePersistentInlineTrackingMap; public: ReadyToRunInfo(Module * pModule, LoaderAllocator* pLoaderAllocator, PEImageLayout * pLayout, READYTORUN_HEADER * pHeader, NativeImage * pNativeImage, AllocMemTracker *pamTracker); @@ -93,6 +98,8 @@ class ReadyToRunInfo static bool IsNativeImageSharedBy(PTR_Module pModule1, PTR_Module pModule2); + PTR_ModuleBase GetNativeManifestModule() const { return m_pNativeManifestModule; } + PTR_READYTORUN_HEADER GetReadyToRunHeader() const { return m_pHeader; } PTR_IMAGE_DATA_DIRECTORY GetDelayMethodCallThunksSection() const { return m_pSectionDelayLoadMethodCallThunks; } @@ -134,12 +141,21 @@ class ReadyToRunInfo return m_readyToRunCodeDisabled; } + void ForbidProcessMoreILBodyFixups() { m_pComposite->ForbidProcessMoreILBodyFixups(); } + bool IsForbidProcessMoreILBodyFixups() { return m_pComposite->IsForbidProcessMoreILBodyFixups(); } + BOOL HasNonShareablePInvokeStubs() { LIMITED_METHOD_CONTRACT; return m_pHeader->CoreHeader.Flags & READYTORUN_FLAG_NONSHARED_PINVOKE_STUBS; } + bool MultiModuleVersionBubble() + { + LIMITED_METHOD_CONTRACT; + return m_pHeader->CoreHeader.Flags & READYTORUN_FLAG_MULTIMODULE_VERSION_BUBBLE; + } + PTR_READYTORUN_IMPORT_SECTION GetImportSections(COUNT_T * pCount) { LIMITED_METHOD_CONTRACT; @@ -216,14 +232,48 @@ class ReadyToRunInfo return m_pPersistentInlineTrackingMap; } + bool HasReadyToRunInlineTrackingMap() + { + return (m_pPersistentInlineTrackingMap != NULL || m_pCrossModulePersistentInlineTrackingMap != NULL); + } + + COUNT_T GetInliners(PTR_Module inlineeOwnerMod, mdMethodDef inlineeTkn, COUNT_T inlinersSize, MethodInModule *inliners, BOOL *incompleteData) + { + COUNT_T inlinersCount = 0; + if (m_pPersistentInlineTrackingMap != NULL) + { + COUNT_T newInliners = m_pPersistentInlineTrackingMap->GetInliners(inlineeOwnerMod, inlineeTkn, inlinersSize, inliners, incompleteData); + if (newInliners < inlinersSize) + { + inlinersSize -= newInliners; + inliners += newInliners; + } + inlinersCount += newInliners; + } + + if (m_pCrossModulePersistentInlineTrackingMap != NULL) + { + COUNT_T newInliners = m_pCrossModulePersistentInlineTrackingMap->GetInliners(inlineeOwnerMod, inlineeTkn, inlinersSize, inliners, incompleteData); + if (newInliners < inlinersSize) + { + inlinersSize -= newInliners; + inliners += newInliners; + } + inlinersCount += newInliners; + } + + return inlinersCount; + } + + bool MayHaveCustomAttribute(WellKnownAttribute attribute, mdToken token); void DisableCustomAttributeFilter(); + BOOL IsImageVersionAtLeast(int majorVersion, int minorVersion); private: BOOL GetTypeNameFromToken(IMDInternalImport * pImport, mdToken mdType, LPCUTF8 * ppszName, LPCUTF8 * ppszNameSpace); BOOL GetEnclosingToken(IMDInternalImport * pImport, mdToken mdType, mdToken * pEnclosingToken); BOOL CompareTypeNameOfTokens(mdToken mdToken1, IMDInternalImport * pImport1, mdToken mdToken2, IMDInternalImport * pImport2); - BOOL IsImageVersionAtLeast(int majorVersion, int minorVersion); PTR_MethodDesc GetMethodDescForEntryPointInNativeImage(PCODE entryPoint); void SetMethodDescForEntryPointInNativeImage(PCODE entryPoint, PTR_MethodDesc methodDesc); diff --git a/src/coreclr/vm/siginfo.cpp b/src/coreclr/vm/siginfo.cpp index e5b108ac7ca47..14766c118dac8 100644 --- a/src/coreclr/vm/siginfo.cpp +++ b/src/coreclr/vm/siginfo.cpp @@ -982,7 +982,7 @@ TypeHandle SigPointer::GetTypeHandleNT(Module* pModule, // pZapSigContext is only set when decoding zapsigs // TypeHandle SigPointer::GetTypeHandleThrowing( - Module * pModule, + ModuleBase * pModule, const SigTypeContext * pTypeContext, ClassLoader::LoadTypesFlag fLoadTypes/*=LoadTypes*/, ClassLoadLevel level/*=CLASS_LOADED*/, @@ -1115,7 +1115,7 @@ TypeHandle SigPointer::GetTypeHandleThrowing( // // pOrigModule is the original module that contained this ZapSig // - Module * pOrigModule = (pZapSigContext != NULL) ? pZapSigContext->pInfoModule : pModule; + ModuleBase * pOrigModule = (pZapSigContext != NULL) ? pZapSigContext->pInfoModule : pModule; ClassLoader::NotFoundAction notFoundAction; CorInternalStates tdTypes; @@ -1180,7 +1180,7 @@ TypeHandle SigPointer::GetTypeHandleThrowing( pModule = pZapSigContext->GetZapSigModule()->GetModuleFromIndex(ix); } - if ((pModule != NULL) && pModule->IsInCurrentVersionBubble()) + if (pModule != NULL) { thRet = psig.GetTypeHandleThrowing(pModule, pTypeContext, @@ -1211,24 +1211,29 @@ TypeHandle SigPointer::GetTypeHandleThrowing( mdGenericParam tkTyPar = TokenFromRid(rid, mdtGenericParam); - TypeVarTypeDesc *pTypeVarTypeDesc = pModule->LookupGenericParam(tkTyPar); + if (!pModule->IsFullModule()) + THROW_BAD_FORMAT(BFA_BAD_COMPLUS_SIG, pOrigModule); + + Module *pNormalModule = static_cast(pModule); + + TypeVarTypeDesc *pTypeVarTypeDesc = pNormalModule->LookupGenericParam(tkTyPar); if (pTypeVarTypeDesc == NULL && (fLoadTypes == ClassLoader::LoadTypes)) { mdToken tkOwner; - IfFailThrow(pModule->GetMDImport()->GetGenericParamProps(tkTyPar, NULL, NULL, &tkOwner, NULL, NULL)); + IfFailThrow(pNormalModule->GetMDImport()->GetGenericParamProps(tkTyPar, NULL, NULL, &tkOwner, NULL, NULL)); if (TypeFromToken(tkOwner) == mdtMethodDef) { - MemberLoader::GetMethodDescFromMethodDef(pModule, tkOwner, FALSE); + MemberLoader::GetMethodDescFromMethodDef(pNormalModule, tkOwner, FALSE); } else { - ClassLoader::LoadTypeDefThrowing(pModule, tkOwner, + ClassLoader::LoadTypeDefThrowing(pNormalModule, tkOwner, ClassLoader::ThrowIfNotFound, ClassLoader::PermitUninstDefOrRef); } - pTypeVarTypeDesc = pModule->LookupGenericParam(tkTyPar); + pTypeVarTypeDesc = pNormalModule->LookupGenericParam(tkTyPar); if (pTypeVarTypeDesc == NULL) { THROW_BAD_FORMAT(BFA_BAD_COMPLUS_SIG, pOrigModule); @@ -1295,7 +1300,7 @@ TypeHandle SigPointer::GetTypeHandleThrowing( if (pZapSigContext && pZapSigContext->externalTokens == ZapSig::NormalTokens && psig.IsTypeDef(&tkGenericType)) { typeAndModuleKnown = true; - pGenericTypeModule = pModule; + pGenericTypeModule = static_cast(pModule); } TypeHandle genericType = psig.GetGenericInstType(pModule, fLoadTypes, level < CLASS_LOAD_APPROXPARENTS ? level : CLASS_LOAD_APPROXPARENTS, pZapSigContext); @@ -1507,9 +1512,9 @@ TypeHandle SigPointer::GetTypeHandleThrowing( { if (pModule->GetMDImport()->GetMetadataStreamVersion() != MD_STREAM_VER_1X) { - pOrigModule->GetAssembly()->ThrowTypeLoadException(pModule->GetMDImport(), - typeToken, - BFA_CLASSLOAD_VALUETYPEMISMATCH); + pOrigModule->ThrowTypeLoadException(pModule->GetMDImport(), + typeToken, + BFA_CLASSLOAD_VALUETYPEMISMATCH); } } } @@ -1650,7 +1655,7 @@ TypeHandle SigPointer::GetTypeHandleThrowing( // the DAC is prepared to receive an invalid type handle #ifndef DACCESS_COMPILE if (pModule->IsSigInIL(m_ptr)) - THROW_BAD_FORMAT(BFA_BAD_SIGNATURE, (Module*)pModule); + THROW_BAD_FORMAT(BFA_BAD_SIGNATURE, pModule); #endif CorSigUncompressPointer(psig.GetPtr(), (void**)&hType); thRet = hType; @@ -1665,9 +1670,9 @@ TypeHandle SigPointer::GetTypeHandleThrowing( IfFailThrowBF(psig.GetToken(&token), BFA_BAD_SIGNATURE, pOrigModule); - pOrigModule->GetAssembly()->ThrowTypeLoadException(pModule->GetMDImport(), - token, - IDS_CLASSLOAD_GENERAL); + pOrigModule->ThrowTypeLoadException(pModule->GetMDImport(), + token, + IDS_CLASSLOAD_GENERAL); #else DacNotImpl(); break; @@ -1689,7 +1694,7 @@ TypeHandle SigPointer::GetTypeHandleThrowing( #pragma warning(pop) #endif -TypeHandle SigPointer::GetGenericInstType(Module * pModule, +TypeHandle SigPointer::GetGenericInstType(ModuleBase * pModule, ClassLoader::LoadTypesFlag fLoadTypes/*=LoadTypes*/, ClassLoadLevel level/*=CLASS_LOADED*/, const ZapSig::Context * pZapSigContext) @@ -1706,7 +1711,7 @@ TypeHandle SigPointer::GetGenericInstType(Module * pModule, } CONTRACTL_END - Module * pOrigModule = (pZapSigContext != NULL) ? pZapSigContext->pInfoModule : pModule; + ModuleBase * pOrigModule = (pZapSigContext != NULL) ? pZapSigContext->pInfoModule : pModule; CorElementType typ = ELEMENT_TYPE_END; IfFailThrowBF(GetElemType(&typ), BFA_BAD_SIGNATURE, pOrigModule); @@ -1719,7 +1724,7 @@ TypeHandle SigPointer::GetGenericInstType(Module * pModule, // the DAC is prepared to receive an invalid type handle #ifndef DACCESS_COMPILE if (pModule->IsSigInIL(m_ptr)) - THROW_BAD_FORMAT(BFA_BAD_SIGNATURE, (Module*)pModule); + THROW_BAD_FORMAT(BFA_BAD_SIGNATURE, pModule); #endif IfFailThrow(GetPointer((void**)&genericType)); @@ -1773,9 +1778,9 @@ TypeHandle SigPointer::GetGenericInstType(Module * pModule, if (typFromSigIsClass != typLoadedIsClass) { - pOrigModule->GetAssembly()->ThrowTypeLoadException(pModule->GetMDImport(), - typeToken, - BFA_CLASSLOAD_VALUETYPEMISMATCH); + pOrigModule->ThrowTypeLoadException(pModule->GetMDImport(), + typeToken, + BFA_CLASSLOAD_VALUETYPEMISMATCH); } } @@ -1790,7 +1795,7 @@ TypeHandle SigPointer::GetGenericInstType(Module * pModule, } // SigPointer should be just after E_T_VAR or E_T_MVAR -TypeHandle SigPointer::GetTypeVariableThrowing(Module *pModule, // unused - may be used later for better error reporting +TypeHandle SigPointer::GetTypeVariableThrowing(ModuleBase *pModule, // unused - may be used later for better error reporting CorElementType et, ClassLoader::LoadTypesFlag fLoadTypes/*=LoadTypes*/, const SigTypeContext *pTypeContext) @@ -3366,7 +3371,7 @@ BOOL CompareTypeDefsForEquivalence(mdToken tk1, mdToken tk2, Module *pModule1, M } -BOOL CompareTypeTokens(mdToken tk1, mdToken tk2, Module *pModule1, Module *pModule2, TokenPairList *pVisited /*= NULL*/) +BOOL CompareTypeTokens(mdToken tk1, mdToken tk2, ModuleBase *pModule1, ModuleBase *pModule2, TokenPairList *pVisited /*= NULL*/) { CONTRACTL { @@ -3420,7 +3425,9 @@ BOOL CompareTypeTokens(mdToken tk1, mdToken tk2, Module *pModule1, Module *pModu #ifdef FEATURE_TYPEEQUIVALENCE // two type defs can't be the same unless they are identical or resolve to // equivalent types (equivalence based on GUID and TypeIdentifierAttribute) - return CompareTypeDefsForEquivalence(tk1, tk2, pModule1, pModule2, pVisited); + _ASSERTE(pModule1->IsFullModule()); + _ASSERTE(pModule2->IsFullModule()); + return CompareTypeDefsForEquivalence(tk1, tk2, static_cast(pModule1), static_cast(pModule2), pVisited); #else // FEATURE_TYPEEQUIVALENCE // two type defs can't be the same unless they are identical return FALSE; @@ -3547,7 +3554,14 @@ BOOL CompareTypeTokens(mdToken tk1, mdToken tk2, Module *pModule1, Module *pModu #ifdef DACCESS_COMPILE ThrowHR(hr); #else - EEFileLoadException::Throw(pModule2->GetPEAssembly(), hr); + if (pModule2->IsFullModule()) + { + EEFileLoadException::Throw(static_cast(pModule2)->GetPEAssembly(), hr); + } + else + { + ThrowHR(hr); + } #endif //!DACCESS_COMPILE } // CompareTypeTokens @@ -3567,8 +3581,8 @@ MetaSig::CompareElementType( PCCOR_SIGNATURE & pSig2, PCCOR_SIGNATURE pEndSig1, PCCOR_SIGNATURE pEndSig2, - Module * pModule1, - Module * pModule2, + ModuleBase * pModule1, + ModuleBase * pModule2, const Substitution * pSubst1, const Substitution * pSubst2, TokenPairList * pVisited) // = NULL @@ -3658,7 +3672,7 @@ MetaSig::CompareElementType( #ifndef DACCESS_COMPILE if (pModule1->IsSigInIL(pSig1)) { - THROW_BAD_FORMAT(BFA_BAD_SIGNATURE, (Module *)pModule1); + THROW_BAD_FORMAT(BFA_BAD_SIGNATURE, pModule1); } #endif @@ -3671,7 +3685,7 @@ MetaSig::CompareElementType( #ifndef DACCESS_COMPILE if (pModule2->IsSigInIL(pSig2)) { - THROW_BAD_FORMAT(BFA_BAD_SIGNATURE, (Module *)pModule2); + THROW_BAD_FORMAT(BFA_BAD_SIGNATURE, pModule2); } #endif } @@ -3682,7 +3696,7 @@ MetaSig::CompareElementType( { TypeHandle hInternal; CorElementType eOtherType; - Module * pOtherModule; + ModuleBase * pOtherModule; // One type is already loaded, collect all the necessary information to identify the other type. if (Type1 == ELEMENT_TYPE_INTERNAL) @@ -4245,11 +4259,11 @@ BOOL MetaSig::CompareMethodSigs( PCCOR_SIGNATURE pSignature1, DWORD cSig1, - Module * pModule1, + ModuleBase * pModule1, const Substitution * pSubst1, PCCOR_SIGNATURE pSignature2, DWORD cSig2, - Module * pModule2, + ModuleBase * pModule2, const Substitution * pSubst2, BOOL skipReturnTypeSig, TokenPairList * pVisited) //= NULL @@ -4422,10 +4436,10 @@ MetaSig::CompareMethodSigs( BOOL MetaSig::CompareFieldSigs( PCCOR_SIGNATURE pSignature1, DWORD cSig1, - Module * pModule1, + ModuleBase * pModule1, PCCOR_SIGNATURE pSignature2, DWORD cSig2, - Module * pModule2, + ModuleBase * pModule2, TokenPairList * pVisited) //= NULL { WRAPPER_NO_CONTRACT; @@ -4460,8 +4474,8 @@ MetaSig::CompareElementTypeToToken( PCCOR_SIGNATURE & pSig1, PCCOR_SIGNATURE pEndSig1, // end of sig1 mdToken tk2, - Module * pModule1, - Module * pModule2, + ModuleBase * pModule1, + ModuleBase * pModule2, const Substitution * pSubst1, TokenPairList * pVisited) { @@ -4519,7 +4533,7 @@ MetaSig::CompareElementTypeToToken( #ifndef DACCESS_COMPILE if (pModule1->IsSigInIL(pSig1)) { - THROW_BAD_FORMAT(BFA_BAD_SIGNATURE, (Module*)pModule1); + THROW_BAD_FORMAT(BFA_BAD_SIGNATURE, pModule1); } #endif } @@ -4613,8 +4627,8 @@ MetaSig::CompareElementTypeToToken( /* static */ BOOL MetaSig::CompareTypeSpecToToken(mdTypeSpec tk1, mdToken tk2, - Module *pModule1, - Module *pModule2, + ModuleBase *pModule1, + ModuleBase *pModule2, const Substitution *pSubst1, TokenPairList *pVisited) { @@ -4644,9 +4658,9 @@ BOOL MetaSig::CompareTypeSpecToToken(mdTypeSpec tk1, /* static */ -BOOL MetaSig::CompareTypeDefOrRefOrSpec(Module *pModule1, mdToken tok1, +BOOL MetaSig::CompareTypeDefOrRefOrSpec(ModuleBase *pModule1, mdToken tok1, const Substitution *pSubst1, - Module *pModule2, mdToken tok2, + ModuleBase *pModule2, mdToken tok2, const Substitution *pSubst2, TokenPairList *pVisited) { @@ -5275,7 +5289,7 @@ BOOL MetaSig::IsReturnTypeVoid() const // Substitution::Substitution( mdToken parentTypeDefOrRefOrSpec, - Module * pModule, + ModuleBase * pModule, const Substitution * pNext) { LIMITED_METHOD_CONTRACT; @@ -5372,7 +5386,7 @@ void Substitution::DeleteChain() //--------------------------------------------------------------------------------------- // // static -TokenPairList TokenPairList::AdjustForTypeSpec(TokenPairList *pTemplate, Module *pTypeSpecModule, PCCOR_SIGNATURE pTypeSpecSig, DWORD cbTypeSpecSig) +TokenPairList TokenPairList::AdjustForTypeSpec(TokenPairList *pTemplate, ModuleBase *pTypeSpecModule, PCCOR_SIGNATURE pTypeSpecSig, DWORD cbTypeSpecSig) { CONTRACTL { diff --git a/src/coreclr/vm/siginfo.hpp b/src/coreclr/vm/siginfo.hpp index 25ea2d4d0a1a5..4d2cf06c36280 100644 --- a/src/coreclr/vm/siginfo.hpp +++ b/src/coreclr/vm/siginfo.hpp @@ -203,13 +203,13 @@ class SigPointer : public SigParser // SigPointer should be just after E_T_VAR or E_T_MVAR TypeHandle GetTypeVariable(CorElementType et,const SigTypeContext *pTypeContext); - TypeHandle GetTypeVariableThrowing(Module *pModule, + TypeHandle GetTypeVariableThrowing(ModuleBase *pModule, CorElementType et, ClassLoader::LoadTypesFlag fLoadTypes, const SigTypeContext *pTypeContext); // Parse type following E_T_GENERICINST - TypeHandle GetGenericInstType(Module * pModule, + TypeHandle GetGenericInstType(ModuleBase * pModule, ClassLoader::LoadTypesFlag = ClassLoader::LoadTypes, ClassLoadLevel level = CLASS_LOADED, const ZapSig::Context *pZapSigContext = NULL); @@ -247,7 +247,7 @@ class SigPointer : public SigParser // occurring in the type arguments // This semantics is used by the class loader to load tricky recursive definitions in phases // (e.g. class C : D, or struct S : I) - TypeHandle GetTypeHandleThrowing(Module* pModule, + TypeHandle GetTypeHandleThrowing(ModuleBase* pModule, const SigTypeContext *pTypeContext, ClassLoader::LoadTypesFlag fLoadTypes = ClassLoader::LoadTypes, ClassLoadLevel level = CLASS_LOADED, @@ -401,7 +401,7 @@ class Signature class Substitution { private: - Module * m_pModule; // Module in which instantiation lives (needed to resolve typerefs) + ModuleBase * m_pModule; // Module in which instantiation lives (needed to resolve typerefs) SigPointer m_sigInst; const Substitution * m_pNext; @@ -414,7 +414,7 @@ class Substitution } Substitution( - Module * pModuleArg, + ModuleBase * pModuleArg, const SigPointer & sigInst, const Substitution * pNextSubstitution) { @@ -426,7 +426,7 @@ class Substitution Substitution( mdToken parentTypeDefOrRefOrSpec, - Module * pModuleArg, + ModuleBase * pModuleArg, const Substitution * nextArg); Substitution(const Substitution & subst) @@ -438,7 +438,7 @@ class Substitution } void DeleteChain(); - Module * GetModule() const { LIMITED_METHOD_DAC_CONTRACT; return m_pModule; } + ModuleBase * GetModule() const { LIMITED_METHOD_DAC_CONTRACT; return m_pModule; } const Substitution * GetNext() const { LIMITED_METHOD_DAC_CONTRACT; return m_pNext; } const SigPointer & GetInst() const { LIMITED_METHOD_DAC_CONTRACT; return m_sigInst; } DWORD GetLength() const; @@ -457,14 +457,14 @@ class TokenPairList { public: // Chain using this constructor when comparing two typedefs for equivalence. - TokenPairList(mdToken token1, Module *pModule1, mdToken token2, Module *pModule2, TokenPairList *pNext) + TokenPairList(mdToken token1, ModuleBase *pModule1, mdToken token2, ModuleBase *pModule2, TokenPairList *pNext) : m_token1(token1), m_token2(token2), m_pModule1(pModule1), m_pModule2(pModule2), m_bInTypeEquivalenceForbiddenScope(pNext == NULL ? FALSE : pNext->m_bInTypeEquivalenceForbiddenScope), m_pNext(pNext) { LIMITED_METHOD_CONTRACT; } - static BOOL Exists(TokenPairList *pList, mdToken token1, Module *pModule1, mdToken token2, Module *pModule2) + static BOOL Exists(TokenPairList *pList, mdToken token1, ModuleBase *pModule1, mdToken token2, ModuleBase *pModule2) { LIMITED_METHOD_CONTRACT; while (pList != NULL) @@ -488,7 +488,7 @@ class TokenPairList } // Chain using this method when comparing type specs. - static TokenPairList AdjustForTypeSpec(TokenPairList *pTemplate, Module *pTypeSpecModule, PCCOR_SIGNATURE pTypeSpecSig, DWORD cbTypeSpecSig); + static TokenPairList AdjustForTypeSpec(TokenPairList *pTemplate, ModuleBase *pTypeSpecModule, PCCOR_SIGNATURE pTypeSpecSig, DWORD cbTypeSpecSig); static TokenPairList AdjustForTypeEquivalenceForbiddenScope(TokenPairList *pTemplate); private: @@ -502,7 +502,7 @@ class TokenPairList { LIMITED_METHOD_CONTRACT; } mdToken m_token1, m_token2; - Module *m_pModule1, *m_pModule2; + ModuleBase *m_pModule1, *m_pModule2; BOOL m_bInTypeEquivalenceForbiddenScope; TokenPairList *m_pNext; }; // class TokenPairList @@ -808,9 +808,9 @@ class MetaSig // We should not hit ELEMENT_TYPE_END in the middle of the signature if (mt == ELEMENT_TYPE_END) { - THROW_BAD_FORMAT(BFA_BAD_SIGNATURE, (Module *)NULL); + THROW_BAD_FORMAT(BFA_BAD_SIGNATURE, (ModuleBase*)NULL); } - IfFailThrowBF(m_pWalk.SkipExactlyOne(), BFA_BAD_SIGNATURE, (Module *)NULL); + IfFailThrowBF(m_pWalk.SkipExactlyOne(), BFA_BAD_SIGNATURE, (ModuleBase*)NULL); return mt; } } // NextArgNormalized @@ -934,7 +934,7 @@ class MetaSig BOOL dropGenericArgumentLevel = FALSE) const { WRAPPER_NO_CONTRACT; - return m_pLastType.GetTypeHandleThrowing(m_pModule, &m_typeContext, fLoadTypes, + return m_pLastType.GetTypeHandleThrowing((ModuleBase*)m_pModule, &m_typeContext, fLoadTypes, level, dropGenericArgumentLevel); } @@ -951,7 +951,7 @@ class MetaSig ClassLoadLevel level = CLASS_LOADED) const { WRAPPER_NO_CONTRACT; - return m_pRetType.GetTypeHandleThrowing(m_pModule, &m_typeContext, fLoadTypes, level); + return m_pRetType.GetTypeHandleThrowing((ModuleBase*)m_pModule, &m_typeContext, fLoadTypes, level); } //------------------------------------------------------------------ @@ -975,8 +975,8 @@ class MetaSig PCCOR_SIGNATURE & pSig2, PCCOR_SIGNATURE pEndSig1, PCCOR_SIGNATURE pEndSig2, - Module * pModule1, - Module * pModule2, + ModuleBase * pModule1, + ModuleBase * pModule2, const Substitution * pSubst1, const Substitution * pSubst2, TokenPairList * pVisited = NULL); @@ -999,11 +999,11 @@ class MetaSig static BOOL CompareMethodSigs( PCCOR_SIGNATURE pSig1, DWORD cSig1, - Module* pModule1, + ModuleBase* pModule1, const Substitution* pSubst1, PCCOR_SIGNATURE pSig2, DWORD cSig2, - Module* pModule2, + ModuleBase* pModule2, const Substitution* pSubst2, BOOL skipReturnTypeSig, TokenPairList* pVisited = NULL @@ -1030,10 +1030,10 @@ class MetaSig static BOOL CompareFieldSigs( PCCOR_SIGNATURE pSig1, DWORD cSig1, - Module* pModule1, + ModuleBase* pModule1, PCCOR_SIGNATURE pSig2, DWORD cSig2, - Module* pModule2, + ModuleBase* pModule2, TokenPairList *pVisited = NULL ); @@ -1058,23 +1058,23 @@ class MetaSig const Substitution *pSubst2, Module *pModule2, mdGenericParam tok2); //overridden - static BOOL CompareTypeDefOrRefOrSpec(Module *pModule1, mdToken tok1, + static BOOL CompareTypeDefOrRefOrSpec(ModuleBase *pModule1, mdToken tok1, const Substitution *pSubst1, - Module *pModule2, mdToken tok2, + ModuleBase *pModule2, mdToken tok2, const Substitution *pSubst2, TokenPairList *pVisited); static BOOL CompareTypeSpecToToken(mdTypeSpec tk1, mdToken tk2, - Module *pModule1, - Module *pModule2, + ModuleBase *pModule1, + ModuleBase *pModule2, const Substitution *pSubst1, TokenPairList *pVisited); static BOOL CompareElementTypeToToken(PCCOR_SIGNATURE &pSig1, PCCOR_SIGNATURE pEndSig1, // end of sig1 mdToken tk2, - Module* pModule1, - Module* pModule2, + ModuleBase* pModule1, + ModuleBase* pModule2, const Substitution* pSubst1, TokenPairList *pVisited); @@ -1169,7 +1169,7 @@ struct TypeIdentifierData // fResolved is TRUE when one of the tokens is a resolved TypeRef. This is used to restrict // type equivalence checks for value types. -BOOL CompareTypeTokens(mdToken tk1, mdToken tk2, Module *pModule1, Module *pModule2, TokenPairList *pVisited = NULL); +BOOL CompareTypeTokens(mdToken tk1, mdToken tk2, ModuleBase *pModule1, ModuleBase *pModule2, TokenPairList *pVisited = NULL); // Nonthrowing version of CompareTypeTokens. // diff --git a/src/coreclr/vm/typeparse.cpp b/src/coreclr/vm/typeparse.cpp index f5e1a979b018f..b29885e74af25 100644 --- a/src/coreclr/vm/typeparse.cpp +++ b/src/coreclr/vm/typeparse.cpp @@ -1363,7 +1363,7 @@ TypeName::GetTypeHaveAssemblyHelper( if (pManifestModule->LookupFile(mdFile)) continue; - pManifestModule->LoadModule(GetAppDomain(), mdFile); + pManifestModule->LoadModule(mdFile); th = GetTypeHaveAssemblyHelper(pAssembly, bThrowIfNotFound, bIgnoreCase, NULL, FALSE); diff --git a/src/coreclr/vm/util.hpp b/src/coreclr/vm/util.hpp index 920e29bdc3dd1..daec5efd5b742 100644 --- a/src/coreclr/vm/util.hpp +++ b/src/coreclr/vm/util.hpp @@ -831,6 +831,7 @@ class GcNotifications class MethodDesc; class Module; +class ModuleBase; class DACNotify { diff --git a/src/coreclr/vm/versionresilienthashcode.cpp b/src/coreclr/vm/versionresilienthashcode.cpp index 28eecdfea05d5..b3ba764baac59 100644 --- a/src/coreclr/vm/versionresilienthashcode.cpp +++ b/src/coreclr/vm/versionresilienthashcode.cpp @@ -71,6 +71,40 @@ bool GetVersionResilientTypeHashCode(IMDInternalImport *pMDImport, mdExportedTyp return true; } +bool GetVersionResilientMethodDefHashCode(IMDInternalImport *pMDImport, mdMethodDef token, int * pdwHashCode) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + PRECONDITION(CheckPointer(pdwHashCode)); + } + CONTRACTL_END + + _ASSERTE(TypeFromToken(token) == mdtMethodDef); + _ASSERTE(!IsNilToken(token)); + + LPCUTF8 szName; + + if (FAILED(pMDImport->GetNameOfMethodDef(token, &szName))) + return false; + + mdTypeDef tkTypeDef; + + if (FAILED(pMDImport->GetParentToken(token, &tkTypeDef))) + return false; + + int hashCode; + if (!GetVersionResilientTypeHashCode(pMDImport, tkTypeDef, &hashCode)) + return false; + + hashCode ^= ComputeNameHashCode(szName); + + *pdwHashCode = hashCode; + return true; +} + #ifndef DACCESS_COMPILE int GetVersionResilientTypeHashCode(TypeHandle type) { diff --git a/src/coreclr/vm/versionresilienthashcode.h b/src/coreclr/vm/versionresilienthashcode.h index 577ec32432441..8b019644b9713 100644 --- a/src/coreclr/vm/versionresilienthashcode.h +++ b/src/coreclr/vm/versionresilienthashcode.h @@ -7,6 +7,9 @@ bool GetVersionResilientTypeHashCode(IMDInternalImport *pMDImport, mdExportedTyp int GetVersionResilientMethodHashCode(MethodDesc *pMD); +// Compute hashCode for a MethodDef (Note, this is not current capable of computing a hashcode for a MethodSpec, or MemberRef) +bool GetVersionResilientMethodDefHashCode(IMDInternalImport *pMDImport, mdExportedType token, int * pdwHashCode); + int GetVersionResilientModuleHashCode(Module* pModule); bool GetVersionResilientILCodeHashCode(MethodDesc *pMD, int* hashCode, unsigned* ilSize); diff --git a/src/coreclr/vm/zapsig.cpp b/src/coreclr/vm/zapsig.cpp index 2a235bf9673d1..4c7a0f7010d55 100644 --- a/src/coreclr/vm/zapsig.cpp +++ b/src/coreclr/vm/zapsig.cpp @@ -291,7 +291,7 @@ BOOL ZapSig::GetSignatureForTypeHandle(TypeHandle handle, // Hence we can do the signature comparison without incurring any loads or restores. // /*static*/ BOOL ZapSig::CompareSignatureToTypeHandle(PCCOR_SIGNATURE pSig, - Module* pModule, + ModuleBase* pModule, TypeHandle handle, const ZapSig::Context * pZapSigContext) { @@ -316,7 +316,7 @@ BOOL ZapSig::GetSignatureForTypeHandle(TypeHandle handle, // // pOrigModule is the original module that contained this ZapSig // - Module * pOrigModule = pZapSigContext->pInfoModule; + ModuleBase * pOrigModule = pZapSigContext->pInfoModule; CorElementType sigType = CorSigUncompressElementType(pSig); CorElementType handleType = handle.GetSignatureCorElementType(); @@ -418,7 +418,9 @@ BOOL ZapSig::GetSignatureForTypeHandle(TypeHandle handle, EX_TRY { ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE(); - resolved = ClassLoader::ResolveTokenToTypeDefThrowing(pModule, tk, &pModule, &tk, Loader::DontLoad); + Module *pTypeDefModule = nullptr; + resolved = ClassLoader::ResolveTokenToTypeDefThrowing(pModule, tk, &pTypeDefModule, &tk, Loader::DontLoad); + pModule = pTypeDefModule; } EX_CATCH { @@ -479,7 +481,9 @@ BOOL ZapSig::GetSignatureForTypeHandle(TypeHandle handle, EX_TRY { ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE(); - resolved = ClassLoader::ResolveTokenToTypeDefThrowing(pModule, tk, &pModule, &tk, Loader::DontLoad); + Module *pTypeDefModule = NULL; + resolved = ClassLoader::ResolveTokenToTypeDefThrowing(pModule, tk, &pTypeDefModule, &tk, Loader::DontLoad); + pModule = pTypeDefModule; } EX_CATCH { @@ -558,7 +562,7 @@ BOOL ZapSig::CompareTypeHandleFieldToTypeHandle(TypeHandle *pTypeHnd, TypeHandle } #ifndef DACCESS_COMPILE -Module *ZapSig::DecodeModuleFromIndex(Module *fromModule, +ModuleBase *ZapSig::DecodeModuleFromIndex(Module *fromModule, DWORD index) { CONTRACTL @@ -573,7 +577,7 @@ Module *ZapSig::DecodeModuleFromIndex(Module *fromModule, NativeImage *nativeImage = fromModule->GetCompositeNativeImage(); uint32_t assemblyRefMax = (nativeImage != NULL ? 0 : fromModule->GetAssemblyRefMax()); - if (index < assemblyRefMax) + if (index <= assemblyRefMax) { if (index == 0) { @@ -588,6 +592,15 @@ Module *ZapSig::DecodeModuleFromIndex(Module *fromModule, { index -= assemblyRefMax; + if (fromModule->GetReadyToRunInfo()->IsImageVersionAtLeast(6,2)) + { + if (index == 1) + { + return fromModule->GetReadyToRunInfo()->GetNativeManifestModule(); + } + index--; + } + pAssembly = fromModule->GetNativeMetadataAssemblyRefFromCache(index); if(pAssembly == NULL) @@ -612,7 +625,7 @@ Module *ZapSig::DecodeModuleFromIndex(Module *fromModule, return pAssembly->GetModule(); } -Module *ZapSig::DecodeModuleFromIndexIfLoaded(Module *fromModule, +ModuleBase *ZapSig::DecodeModuleFromIndexIfLoaded(Module *fromModule, DWORD index) { CONTRACTL @@ -629,7 +642,7 @@ Module *ZapSig::DecodeModuleFromIndexIfLoaded(Module *fromModule, NativeImage *nativeImage = fromModule->GetCompositeNativeImage(); uint32_t assemblyRefMax = (nativeImage != NULL ? 0 : fromModule->GetAssemblyRefMax()); - if (index < assemblyRefMax) + if (index <= assemblyRefMax) { if (index == 0) { @@ -643,6 +656,16 @@ Module *ZapSig::DecodeModuleFromIndexIfLoaded(Module *fromModule, else { index -= assemblyRefMax; + + if (fromModule->GetReadyToRunInfo()->IsImageVersionAtLeast(6,2)) + { + if (index == 1) + { + return fromModule->GetReadyToRunInfo()->GetNativeManifestModule(); + } + index--; + } + pAssembly = fromModule->GetNativeMetadataAssemblyRefFromCache(index); if (pAssembly == NULL) { @@ -684,7 +707,7 @@ Module *ZapSig::DecodeModuleFromIndexIfLoaded(Module *fromModule, TypeHandle ZapSig::DecodeType(Module *pEncodeModuleContext, - Module *pInfoModule, + ModuleBase *pInfoModule, PCCOR_SIGNATURE pBuffer, ClassLoadLevel level, PCCOR_SIGNATURE *ppAfterSig /*=NULL*/) @@ -722,7 +745,7 @@ TypeHandle ZapSig::DecodeType(Module *pEncodeModuleContext, } MethodDesc *ZapSig::DecodeMethod(Module *pReferencingModule, - Module *pInfoModule, + ModuleBase *pInfoModule, PCCOR_SIGNATURE pBuffer, TypeHandle * ppTH /*=NULL*/) { @@ -733,7 +756,7 @@ MethodDesc *ZapSig::DecodeMethod(Module *pReferencingModule, return DecodeMethod(pInfoModule, pBuffer, &typeContext, &zapSigContext, ppTH, NULL, NULL); } -MethodDesc *ZapSig::DecodeMethod(Module *pInfoModule, +MethodDesc *ZapSig::DecodeMethod(ModuleBase *pInfoModule, PCCOR_SIGNATURE pBuffer, SigTypeContext *pContext, ZapSig::Context *pZapSigContext, @@ -746,6 +769,7 @@ MethodDesc *ZapSig::DecodeMethod(Module *pInfoModule, STANDARD_VM_CONTRACT; MethodDesc *pMethod = NULL; + ModuleBase *pOrigModule = pInfoModule; SigPointer sig(pBuffer); @@ -826,7 +850,15 @@ MethodDesc *ZapSig::DecodeMethod(Module *pInfoModule, } else { - pMethod = MemberLoader::GetMethodDescFromMethodDef(pInfoModule, TokenFromRid(rid, mdtMethodDef), FALSE); + if (pInfoModule->IsFullModule()) + { + pMethod = MemberLoader::GetMethodDescFromMethodDef(static_cast(pInfoModule), TokenFromRid(rid, mdtMethodDef), FALSE); + } + else + { + // Non-full modules cannot have MethodDefs + ThrowHR(COR_E_BADIMAGEFORMAT); + } } } @@ -872,7 +904,7 @@ MethodDesc *ZapSig::DecodeMethod(Module *pInfoModule, for (uint32_t i = 0; i < nargs; i++) { - pInst[i] = sig.GetTypeHandleThrowing(pInfoModule, + pInst[i] = sig.GetTypeHandleThrowing(pOrigModule, pContext, ClassLoader::LoadTypes, CLASS_LOADED, @@ -942,7 +974,7 @@ MethodDesc *ZapSig::DecodeMethod(Module *pInfoModule, } FieldDesc * ZapSig::DecodeField(Module *pReferencingModule, - Module *pInfoModule, + ModuleBase *pInfoModule, PCCOR_SIGNATURE pBuffer, TypeHandle *ppTH /*=NULL*/) { @@ -960,7 +992,7 @@ FieldDesc * ZapSig::DecodeField(Module *pReferencingModule, } FieldDesc * ZapSig::DecodeField(Module *pReferencingModule, - Module *pInfoModule, + ModuleBase *pInfoModule, PCCOR_SIGNATURE pBuffer, SigTypeContext *pContext, TypeHandle *ppTH /*=NULL*/) @@ -1034,7 +1066,8 @@ FieldDesc * ZapSig::DecodeField(Module *pReferencingModule, } else { - pField = MemberLoader::GetFieldDescFromFieldDef(pInfoModule, TokenFromRid(rid, mdtFieldDef), FALSE); + _ASSERTE(pInfoModule->IsFullModule()); + pField = MemberLoader::GetFieldDescFromFieldDef(static_cast(pInfoModule), TokenFromRid(rid, mdtFieldDef), FALSE); } } @@ -1212,18 +1245,6 @@ BOOL ZapSig::EncodeMethod( methodFlags |= ENCODE_METHOD_SIG_SlotInsteadOfToken; } else - if (!pMethod->GetModule()->IsInCurrentVersionBubble()) - { - // Using a method defined in another version bubble. We can assume the slot number is stable only for real interface methods. - if (!ownerType.IsInterface() || pMethod->IsStatic() || pMethod->HasMethodInstantiation()) - { - // FUTURE TODO: Version resilience - _ASSERTE(!"References to non-interface methods not yet supported in version resilient images"); - IfFailThrow(E_FAIL); - } - methodFlags |= ENCODE_METHOD_SIG_SlotInsteadOfToken; - } - else { Module * pTypeHandleModule = pMethod->GetModule(); diff --git a/src/coreclr/vm/zapsig.h b/src/coreclr/vm/zapsig.h index 290ca007a186a..4f0e05e6be446 100644 --- a/src/coreclr/vm/zapsig.h +++ b/src/coreclr/vm/zapsig.h @@ -42,7 +42,7 @@ class ZapSig struct Context { - Module * pInfoModule; // The tokens in this ZapSig are expressed relative to context.pInfoModule + ModuleBase * pInfoModule; // The tokens in this ZapSig are expressed relative to context.pInfoModule // This is a code:Module* when we are resolving Ngen fixups or doing an Ibc Profiling run. // And this is a MulticoreJitProfilePlayer or a MulticoreJitRecorder when we are doing multicorejit @@ -53,7 +53,7 @@ class ZapSig Module * GetZapSigModule() const { return (Module*) pModuleContext; } Context( - Module* _pInfoModule, + ModuleBase* _pInfoModule, void* _pModuleContext, ExternalTokens _externalTokens) : pInfoModule(_pInfoModule), pModuleContext(_pModuleContext), @@ -61,7 +61,7 @@ class ZapSig { LIMITED_METHOD_CONTRACT; _ASSERTE(externalTokens != IllegalValue); } Context( - Module* _pInfoModule, + ModuleBase* _pInfoModule, Module* _pZapSigModule) : pInfoModule(_pInfoModule), pModuleContext((void*) _pZapSigModule), @@ -72,7 +72,7 @@ class ZapSig public: ZapSig( - Module * _pInfoModule, + ModuleBase * _pInfoModule, void * _pModuleContext, ExternalTokens _externalTokens, EncodeModuleCallback _pfnEncodeModule, @@ -88,7 +88,7 @@ class ZapSig // Compare a type handle with a signature whose tokens are resolved with respect to pModule // pZapSigContext is used to resolve ELEMENT_TYPE_MODULE_ZAPSIG encodings static BOOL CompareSignatureToTypeHandle(PCCOR_SIGNATURE pSig, - Module* pModule, + ModuleBase* pModule, TypeHandle handle, const ZapSig::Context * pZapSigContext); @@ -134,10 +134,10 @@ class ZapSig //-------------------------------------------------------------------- // Static helper encode/decode helper methods - static Module *DecodeModuleFromIndex(Module *fromModule, + static ModuleBase *DecodeModuleFromIndex(Module *fromModule, DWORD index); - static Module *DecodeModuleFromIndexIfLoaded(Module *fromModule, + static ModuleBase *DecodeModuleFromIndexIfLoaded(Module *fromModule, DWORD index); // referencingModule is the module that references the type. @@ -146,19 +146,19 @@ class ZapSig // level is the class load level (see classloadlevel.h) to which the type should be loaded static TypeHandle DecodeType( Module *referencingModule, - Module *fromModule, + ModuleBase *fromModule, PCCOR_SIGNATURE pBuffer, ClassLoadLevel level = CLASS_LOADED, PCCOR_SIGNATURE *ppAfterSig = NULL); static MethodDesc *DecodeMethod( Module *referencingModule, - Module *fromModule, + ModuleBase *fromModule, PCCOR_SIGNATURE pBuffer, TypeHandle *ppTH = NULL); static MethodDesc *DecodeMethod( - Module *pInfoModule, + ModuleBase *pInfoModule, PCCOR_SIGNATURE pBuffer, SigTypeContext *pContext, ZapSig::Context *pZapSigContext, @@ -170,13 +170,13 @@ class ZapSig static FieldDesc *DecodeField( Module *referencingModule, - Module *fromModule, + ModuleBase *fromModule, PCCOR_SIGNATURE pBuffer, TypeHandle *ppTH = NULL); static FieldDesc *DecodeField( Module *pReferencingModule, - Module *pInfoModule, + ModuleBase *pInfoModule, PCCOR_SIGNATURE pBuffer, SigTypeContext *pContext, TypeHandle *ppTH = NULL); From acf28b39199e599108e77b0f309a0ea122fc4012 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 9 Jun 2022 18:55:18 -0700 Subject: [PATCH 02/19] Temporary workaround --- .../ILCompiler.ReadyToRun/Compiler/R2RTypeExtensions.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/R2RTypeExtensions.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/R2RTypeExtensions.cs index e07dd42b8727b..134d973f59a2c 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/R2RTypeExtensions.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/R2RTypeExtensions.cs @@ -38,6 +38,15 @@ public static bool IsNonVersionable(this MetadataType type) result = true; } + // REMOVE BEFORE CHECKIN!!!! + // As a temporary measure to ease testing of this work with an existing SDK, fix up the IsNonVersionable flag on these types in the compiler, without relying on an update to System.Private.CoreLib + // This work also contains a modification to System.Private.CoreLib to do exactly this, so this isn't actually wrong, but its slow, and in the wrong place. + if (!result && type.Module == type.Context.SystemModule && type.Namespace == "System.Runtime.CompilerServices" && (type.Name == "RawArrayData" || type.Name == "RawData")) + { + result = true; + } + // REMOVE BEFORE CHECKIN!!!! + return result; } From 313792b80685668da4c32389b9b486aeae7e7ff6 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 10 Jun 2022 11:58:12 -0700 Subject: [PATCH 03/19] Disable cross module pinvoke inlining - We don't model the details necessary to prevent this from possibly being a problem at this time --- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index cf761a3e1fcf4..3b1d9206e64f0 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -2590,6 +2590,7 @@ private unsafe HRESULT allocPgoInstrumentationBySchema(CORINFO_METHOD_STRUCT_* f private void getAddressOfPInvokeTarget(CORINFO_METHOD_STRUCT_* method, ref CORINFO_CONST_LOOKUP pLookup) { MethodDesc methodDesc = HandleToObject(method); + Debug.Assert(_compilation.CompilationModuleGroup.VersionsWithMethodBody(methodDesc)); if (methodDesc is IL.Stubs.PInvokeTargetNativeMethod rawPInvoke) methodDesc = rawPInvoke.Target; EcmaMethod ecmaMethod = (EcmaMethod)methodDesc; @@ -2628,6 +2629,11 @@ private bool pInvokeMarshalingRequired(CORINFO_METHOD_STRUCT_* handle, CORINFO_S Debug.Assert(!_compilation.NodeFactory.CompilationModuleGroup.GeneratesPInvoke(method)); return true; } + + // Marshalling behavior isn't modeled as protected by R2R rules, so disable pinvoke inlining for code outside + // of the version bubble + if (!_compilation.CompilationModuleGroup.VersionsWithMethodBody(method)) + return true; } catch (RequiresRuntimeJitException) { From 6a5130f3613f3db8156066ee850ec49416b20d6e Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 10 Jun 2022 14:03:23 -0700 Subject: [PATCH 04/19] Fix Mono.Linker.Tests --- .../aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs index a0eff369ee98a..cbf459febb260 100644 --- a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs +++ b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs @@ -52,7 +52,7 @@ public void Trim (ILCompilerOptions options, ILogWriter logWriter) compilationRoots.Add (new MainMethodRootProvider (entrypointModule, CreateInitializerList (typeSystemContext, options))); - ILProvider ilProvider = new NativeAotILProvider (); + ILProvider ilProvider = new NativeAotILProvider (compilationGroup); ilProvider = new FeatureSwitchManager (ilProvider, options.FeatureSwitches); From e31e8bfe3990e3b2a4efb72096e5283036eeb25b Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 10 Jun 2022 13:44:21 -0700 Subject: [PATCH 05/19] Make the loader module algorithm more similar to calculations in crossgen2 --- src/coreclr/vm/clsload.cpp | 10 ++++++++++ src/coreclr/vm/prestub.cpp | 24 ++++++++++++++++++------ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/coreclr/vm/clsload.cpp b/src/coreclr/vm/clsload.cpp index 77512074226a4..1c5b743cfaa4a 100644 --- a/src/coreclr/vm/clsload.cpp +++ b/src/coreclr/vm/clsload.cpp @@ -128,6 +128,11 @@ PTR_Module ClassLoader::ComputeLoaderModuleWorker( // System.__Canon does not contribute to logical loader module if (classArg == TypeHandle(g_pCanonMethodTableClass)) continue; + + CorElementType ety = classArg.GetSignatureCorElementType(); + if (CorTypeInfo::IsPrimitiveType(ety)) + continue; + Module* pModule = classArg.GetLoaderModule(); if (pModule->IsCollectible()) goto ComputeCollectibleLoaderModule; @@ -142,6 +147,11 @@ PTR_Module ClassLoader::ComputeLoaderModuleWorker( // System.__Canon does not contribute to logical loader module if (methodArg == TypeHandle(g_pCanonMethodTableClass)) continue; + + CorElementType ety = methodArg.GetSignatureCorElementType(); + if (CorTypeInfo::IsPrimitiveType(ety)) + continue; + Module *pModule = methodArg.GetLoaderModule(); if (pModule->IsCollectible()) goto ComputeCollectibleLoaderModule; diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index cca1d5d30ee76..05cf529e10993 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -509,15 +509,27 @@ PCODE MethodDesc::GetPrecompiledR2RCode(PrepareCodeConfig* pConfig) pCode = pModule->GetReadyToRunInfo()->GetEntryPoint(this, pConfig, TRUE /* fFixups */); } - // Lookup in the entry point assembly for a R2R entrypoint (generics with large version bubble enabled) - if (pCode == NULL && HasClassOrMethodInstantiation() && SystemDomain::System()->DefaultDomain()->GetRootAssembly() != NULL) + // Generics may be located in several places + if (pCode == NULL && HasClassOrMethodInstantiation()) { - pModule = SystemDomain::System()->DefaultDomain()->GetRootAssembly()->GetModule(); - _ASSERT(pModule != NULL); + Module* pDefiningModule = GetModule(); + // Lookup in the defining module of the generic (which is where in inputbubble scenarios + // that the methods may be placed. + if (pDefiningModule != pModule && pDefiningModule->IsReadyToRun()) + { + pCode = pDefiningModule->GetReadyToRunInfo()->GetEntryPoint(this, pConfig, TRUE /* fFixups */); + } - if (pModule->IsReadyToRun() && pModule->IsInSameVersionBubble(GetModule())) + // Lookup in the entry point assembly for a R2R entrypoint (generics with large version bubble enabled) + if (pCode == NULL && (SystemDomain::System()->DefaultDomain()->GetRootAssembly() != NULL)) { - pCode = pModule->GetReadyToRunInfo()->GetEntryPoint(this, pConfig, TRUE /* fFixups */); + pModule = SystemDomain::System()->DefaultDomain()->GetRootAssembly()->GetModule(); + _ASSERT(pModule != NULL); + + if (pModule->IsReadyToRun() && pModule->IsInSameVersionBubble(GetModule())) + { + pCode = pModule->GetReadyToRunInfo()->GetEntryPoint(this, pConfig, TRUE /* fFixups */); + } } } #endif From 308c5c132f7e16100caaa3655ace86a1f69e4f3d Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 10 Jun 2022 16:19:31 -0700 Subject: [PATCH 06/19] Address code review feedback, and fix naming for native side of ReadyToRunStandaloneMethodMetadata --- .../IL/Stubs/InterlockedIntrinsics.cs | 3 +- .../ReadyToRunCompilationModuleGroupBase.cs | 8 +- .../ReadyToRunStandaloneMethodMetadata.cs | 2 + .../IL/ReadyToRunILProvider.cs | 33 +- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 2 +- src/coreclr/vm/CMakeLists.txt | 1 + src/coreclr/vm/appdomain.cpp | 4 +- src/coreclr/vm/ceeload.h | 26 +- src/coreclr/vm/clsload.cpp | 2 +- src/coreclr/vm/inlinetracking.cpp | 6 +- src/coreclr/vm/jitinterface.cpp | 18 +- src/coreclr/vm/method.cpp | 691 ----------------- src/coreclr/vm/method.hpp | 13 +- src/coreclr/vm/readytoruninfo.cpp | 27 +- .../vm/readytorunstandalonemethodmetadata.cpp | 693 ++++++++++++++++++ 15 files changed, 774 insertions(+), 755 deletions(-) create mode 100644 src/coreclr/vm/readytorunstandalonemethodmetadata.cpp diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/InterlockedIntrinsics.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/InterlockedIntrinsics.cs index 3d3eb0301e519..a4f7e3953d916 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/InterlockedIntrinsics.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/InterlockedIntrinsics.cs @@ -22,7 +22,8 @@ public static MethodIL EmitIL(CompilationModuleGroup compilationModuleGroup, Met { // Check to see if the tokens needed to describe the CompareExchange are naturally present within // the compilation. The current implementation of stable tokens used by cross module inlining is not - // compatible with rewriting the IL of an compiler generated intrinsic. + // compatible with rewriting the IL of a compiler generated intrinsic. Fortunately, it turns out + // that the managed implementation of this intrinsic is correct, just a few more IL instructions. if (compilationModuleGroup.ContainsType(method.OwningType)) { TypeDesc objectType = method.Context.GetWellKnownType(WellKnownType.Object); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs index b5caf195e676d..4b98d7e80ab09 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilationModuleGroupBase.cs @@ -387,7 +387,7 @@ public override bool CrossModuleCompileable(MethodDesc method) private bool CrossModuleCompileableUncached(MethodDesc method) { - if (!CrossModuleInlineable(method)) + if (!CrossModuleInlineableInternal(method)) return false; // Only allow generics, non-generics should be compiled into the defining module @@ -431,7 +431,13 @@ public override bool CrossModuleInlineable(MethodDesc method) { if (!_crossModuleInlining) return false; + + return CrossModuleInlineableInternal(method); + } + // Internal predicate so that the switches controlling cross module inlining and compilation are independent + private bool CrossModuleInlineableInternal(MethodDesc method) + { return _crossModuleInlineableCache.GetOrAdd(method, CrossModuleInlineableUncached); } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunStandaloneMethodMetadata.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunStandaloneMethodMetadata.cs index 6a08f4d3908da..85b5cfb3d0715 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunStandaloneMethodMetadata.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunStandaloneMethodMetadata.cs @@ -15,6 +15,8 @@ namespace ILCompiler { // Alternate form of metadata that represents a single method. Self contained except for type references + // The behavior of this code must exactly match that of the logic in the VM + // That code can be found in src\coreclr\vm\readytorunstandalonemethodmetadata.cpp public class ReadyToRunStandaloneMethodMetadata { public byte[] ConstantData; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs index 682ceda6b2d07..6e21e02233db6 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs @@ -128,7 +128,14 @@ public void CreateCrossModuleInlineableTokensForILBody(EcmaMethod method) Debug.Assert(_manifestMutableModule != null); Debug.Assert(!_compilationModuleGroup.VersionsWithMethodBody(method) && _compilationModuleGroup.CrossModuleInlineable(method)); - var wrappedMethodIL = new ManifestModuleWrappedMethodIL(_manifestMutableModule, EcmaMethodIL.Create(method)); + var wrappedMethodIL = new ManifestModuleWrappedMethodIL(); + if (!wrappedMethodIL.Initialize(_manifestMutableModule, EcmaMethodIL.Create(method))) + { + // If we could not initialize the wrapped method IL, we should store a null. + // That will result in the IL code for the method being unavailable for use in + // the compilation, which is version safe. + wrappedMethodIL = null; + } _manifestModuleWrappedMethods.Add(method, wrappedMethodIL); IncrementVersion(); } @@ -155,6 +162,13 @@ public override MethodIL GetMethodIL(MethodDesc method) return result; } + // Check to see if there is an override for the EcmaMethodIL. If there is not + // then simply return the EcmaMethodIL. In theory this could call + // CreateCrossModuleInlineableTokensForILBody, but we explicitly do not want + // to do that. The reason is that this method is called during the multithreaded + // portion of compilation, and CreateCrossModuleInlineableTokensForILBody + // will produce tokens which are order dependent thus violating the determinism + // principles of the compiler. if (!_manifestModuleWrappedMethods.TryGetValue(ecmaMethod, out var methodIL)) { methodIL = EcmaMethodIL.Create(ecmaMethod); @@ -201,9 +215,14 @@ class ManifestModuleWrappedMethodIL : MethodIL, IEcmaMethodIL, IMethodTokensAreU MutableModule _mutableModule; - public ManifestModuleWrappedMethodIL(MutableModule mutableModule, EcmaMethodIL wrappedMethod) + public ManifestModuleWrappedMethodIL() {} + + public bool Initialize(MutableModule mutableModule, EcmaMethodIL wrappedMethod) { - mutableModule.TryGetEntityHandle(wrappedMethod.OwningMethod); + bool failedToReplaceToken = false; + var owningMethodHandle = mutableModule.TryGetEntityHandle(wrappedMethod.OwningMethod); + if (!owningMethodHandle.HasValue) + return false; _mutableModule = mutableModule; _maxStack = wrappedMethod.MaxStack; _isInitLocals = wrappedMethod.IsInitLocals; @@ -220,7 +239,7 @@ public ManifestModuleWrappedMethodIL(MutableModule mutableModule, EcmaMethodIL w var newHandle = _mutableModule.TryGetHandle((TypeSystemEntity)wrappedMethod.GetObject(region.ClassToken)); if (!newHandle.HasValue) { - throw new Exception("Nope"); + return false; } _exceptionRegions[i] = new ILExceptionRegion(region.Kind, region.TryOffset, region.TryLength, region.HandlerOffset, region.HandlerLength, newHandle.Value, newHandle.Value); } @@ -231,7 +250,7 @@ public ManifestModuleWrappedMethodIL(MutableModule mutableModule, EcmaMethodIL w Debug.Assert(ReadyToRunStandaloneMethodMetadata.Compute((EcmaMethod)_owningMethod) != null); #endif // DEBUG - return; + return !failedToReplaceToken; int GetMutableModuleToken(int token) { @@ -247,7 +266,9 @@ int GetMutableModuleToken(int token) } if (!newToken.HasValue) { - throw new Exception("Nope2"); + // Toekn replacement has failed. Do not attempt to use this IL. + failedToReplaceToken = true; + return 1; } return newToken.Value; } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 3b1d9206e64f0..402f8bde4deb2 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -2063,7 +2063,7 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO else { // READYTORUN: FUTURE: Direct calls if possible - pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol( + pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol( _compilation.NodeFactory.MethodEntrypoint( ComputeMethodWithToken(nonUnboxingMethod, ref pResolvedToken, constrainedType, unboxing: isUnboxingStub), isInstantiatingStub: useInstantiatingStub, diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt index 8c0b03342d016..97f9796d4ed69 100644 --- a/src/coreclr/vm/CMakeLists.txt +++ b/src/coreclr/vm/CMakeLists.txt @@ -107,6 +107,7 @@ set(VM_SOURCES_DAC_AND_WKS_COMMON pgo.cpp precode.cpp prestub.cpp + readytorunstandalonemethodmetadata.cpp rejit.cpp sigformat.cpp siginfo.cpp diff --git a/src/coreclr/vm/appdomain.cpp b/src/coreclr/vm/appdomain.cpp index 2c3a4b9ea30f9..0fab3dfb8bffe 100644 --- a/src/coreclr/vm/appdomain.cpp +++ b/src/coreclr/vm/appdomain.cpp @@ -1040,7 +1040,9 @@ void SystemDomain::Attach() // initialization to run ReJitManager::InitStatic(); - InitMethodBlocks(); +#ifdef FEATURE_READYTORUN + InitReadyToRunStandaloneMethodMetadata(); +#endif // FEATURE_READYTORUN } diff --git a/src/coreclr/vm/ceeload.h b/src/coreclr/vm/ceeload.h index 396351a594c72..f990d63520431 100644 --- a/src/coreclr/vm/ceeload.h +++ b/src/coreclr/vm/ceeload.h @@ -435,18 +435,9 @@ typedef DPTR(class ReadyToRunInfo) PTR_ReadyToRunInfo; struct ThreadLocalModule; -// A code:Module represents a DLL or EXE file loaded from the disk. It could either be a IL module or a -// Native code (NGEN module). A module live in a code:Assembly -// -// Some important fields are -// * code:Module.m_pPEAssembly - this points at a code:PEAssembly that understands the layout of a PE assembly. The most -// important part is getting at the code:Module (see file:..\inc\corhdr.h#ManagedHeader) from there -// you can get at the Meta-data and IL) -// * code:Module.m_pAvailableClasses - this is a table that lets you look up the types (the code:EEClass) -// for all the types in the module -// -// See file:..\inc\corhdr.h#ManagedHeader for more on the layout of managed exectuable files. - +// A ModuleBase represents the ability to reference code via tokens +// This abstraction exists to allow the R2R manifest metadata to have +// tokens which can be resolved at runtime. class ModuleBase { #ifdef DACCESS_COMPILE @@ -592,6 +583,17 @@ class ModuleBase }; +// A code:Module represents a DLL or EXE file loaded from the disk. It could either be a IL module or a +// Native code (NGEN module). A module live in a code:Assembly +// +// Some important fields are +// * code:Module.m_pPEAssembly - this points at a code:PEAssembly that understands the layout of a PE assembly. The most +// important part is getting at the code:Module (see file:..\inc\corhdr.h#ManagedHeader) from there +// you can get at the Meta-data and IL) +// * code:Module.m_pAvailableClasses - this is a table that lets you look up the types (the code:EEClass) +// for all the types in the module +// +// See file:..\inc\corhdr.h#ManagedHeader for more on the layout of managed exectuable files. class Module : public ModuleBase { #ifdef DACCESS_COMPILE diff --git a/src/coreclr/vm/clsload.cpp b/src/coreclr/vm/clsload.cpp index 1c5b743cfaa4a..1d405ceeb7820 100644 --- a/src/coreclr/vm/clsload.cpp +++ b/src/coreclr/vm/clsload.cpp @@ -159,7 +159,7 @@ PTR_Module ClassLoader::ComputeLoaderModuleWorker( pLoaderModule = pModule; } - if ((pLoaderModule == NULL) && pDefinitionModule) + if (pLoaderModule == NULL) { pLoaderModule = pDefinitionModule; } diff --git a/src/coreclr/vm/inlinetracking.cpp b/src/coreclr/vm/inlinetracking.cpp index e358b93cdd5f4..c22f012bc58cb 100644 --- a/src/coreclr/vm/inlinetracking.cpp +++ b/src/coreclr/vm/inlinetracking.cpp @@ -421,7 +421,7 @@ Module* PersistentInlineTrackingMapR2R2::GetModuleByIndex(DWORD index) // This "black magic spell" has in fact nothing to do with GenericInstantiationCompare per se, but just sets a thread flag // that later activates more thorough search inside Module::GetAssemblyIfLoaded, which is indirectly called from GetModuleFromIndexIfLoaded. - // This is useful when ngen image was compiler against a different assembly version than the one loaded now. + // This is useful when a R2R image was compiled against a different assembly version than the one loaded now. ClrFlsThreadTypeSwitch genericInstantionCompareHolder(ThreadType_GenericInstantiationCompare); _ASSERTE(m_module->GetModuleFromIndexIfLoaded(index)->IsFullModule()); @@ -444,8 +444,6 @@ BOOL CrossModulePersistentInlineTrackingMapR2R::TryLoad(Module* pModule, LoaderA return TRUE; } -// Read a Module/MethodDef token pair from the parser. Optionally check to see if it matches expectedToken. -// (0 for expectedToken indicates that no checking is to be performed) void CrossModulePersistentInlineTrackingMapR2R::GetILBodySection(MethodDesc*** pppMethods, COUNT_T* pcMethods) { COUNT_T countImportSections; @@ -648,7 +646,7 @@ Module* CrossModulePersistentInlineTrackingMapR2R::GetModuleByIndex(DWORD index) // This "black magic spell" has in fact nothing to do with GenericInstantiationCompare per se, but just sets a thread flag // that later activates more thorough search inside Module::GetAssemblyIfLoaded, which is indirectly called from GetModuleFromIndexIfLoaded. - // This is useful when ngen image was compiler against a different assembly version than the one loaded now. + // This is useful when a R2R image was compiled against a different assembly version than the one loaded now. ClrFlsThreadTypeSwitch genericInstantionCompareHolder(ThreadType_GenericInstantiationCompare); _ASSERTE(m_module->GetModuleFromIndexIfLoaded(index)->IsFullModule()); diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 4edbd346d2f35..e9f8b21f05bb8 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -13997,31 +13997,31 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, } } - MethodBlock *pMethodBlock = NULL; + ReadyToRunStandaloneMethodMetadata *pMethodMetadata = NULL; if (!fail) { - pMethodBlock = GetMethodBlock(pMDCompare); - if (pMethodBlock == NULL) + pMethodMetadata = GetReadyToRunStandaloneMethodMetadata(pMDCompare); + if (pMethodMetadata == NULL) fail = true; - fail = fail || (pMethodBlock->cByteData != dwBlobSize); + fail = fail || (pMethodMetadata->cByteData != dwBlobSize); } if (!fail) { - fail = 0 != memcmp(pBlobStart, pMethodBlock->pByteData, dwBlobSize); + fail = 0 != memcmp(pBlobStart, pMethodMetadata->pByteData, dwBlobSize); } if (!fail) { - fail = cTypes != pMethodBlock->cTypes; + fail = cTypes != pMethodMetadata->cTypes; } if (!fail) { for (COUNT_T i = 0; i < cTypes && !fail; i++) { - fail = types[i] != pMethodBlock->pTypes[i]; + fail = types[i] != pMethodMetadata->pTypes[i]; } } @@ -14047,9 +14047,9 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, methodName.GetUnicode(), GetFullyQualifiedNameForClassW(pMDCompare->GetMethodTable()), (int)dwBlobSize, pBlobStart, - pMethodBlock != NULL ? (int)pMethodBlock->cByteData : 0, pMethodBlock != NULL ? (void*)pMethodBlock->pByteData : (void*)NULL, + pMethodMetadata != NULL ? (int)pMethodMetadata->cByteData : 0, pMethodMetadata != NULL ? (void*)pMethodMetadata->pByteData : (void*)NULL, (int)cTypes, compileTimeTypes, - pMethodBlock != NULL ? (int)pMethodBlock->cTypes : 0, pMethodBlock != NULL ? (void*)pMethodBlock->pTypes : (void*)NULL + pMethodMetadata != NULL ? (int)pMethodMetadata->cTypes : 0, pMethodMetadata != NULL ? (void*)pMethodMetadata->pTypes : (void*)NULL ); #ifdef _DEBUG diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp index b30e512e04abb..8b7f075c1b50b 100644 --- a/src/coreclr/vm/method.cpp +++ b/src/coreclr/vm/method.cpp @@ -4099,694 +4099,3 @@ void MethodDesc::PrepareForUseAsAFunctionPointer() SetValueTypeParametersLoaded(); } #endif //!DACCESS_COMPILE - -#include "openum.h" - -class AlternateBlockHelper -{ -// COR_ILMETHOD* pHeader; - COR_ILMETHOD_DECODER header; - - SigBuilder nonCodeAlternateBlob; - SigBuilder alternateNonTypeRefStream; - SArray ilStream; - COUNT_T currentILStreamIterator; - SArray *pTypeRefTokenStream; - - MapSHash alternateTokens; - Module* pModule; - IMDInternalImport* pMDImport; - -public: - - AlternateBlockHelper(MethodDesc *pMD, SArray *pTypeRefTokenStreamInput) : -// pHeader(pMD->GetILHeader(TRUE)), - header(pMD->GetILHeader(TRUE), pMD->GetMDImport(), NULL), - currentILStreamIterator(0), - pTypeRefTokenStream(pTypeRefTokenStreamInput), - pModule(pMD->GetModule()), - pMDImport(pMD->GetMDImport()) - { - { - // Fill IL stream with initial data - byte* ilStreamData = ilStream.OpenRawBuffer(header.CodeSize); - memcpy(ilStreamData, header.Code, header.CodeSize); - ilStream.CloseRawBuffer(header.CodeSize); - } - } - - void GenerateDataStreams(SArray *pDataStream) - { - unsigned ehCount = header.EHCount(); - nonCodeAlternateBlob.AppendData(ehCount); - for (unsigned e = 0; e < ehCount; e++) - { - IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT ehBuff; - const IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT* ehInfo; - - ehInfo = header.EH->EHClause(e, &ehBuff); - - nonCodeAlternateBlob.AppendData(ehInfo->Flags); - nonCodeAlternateBlob.AppendData(ehInfo->TryOffset); - nonCodeAlternateBlob.AppendData(ehInfo->TryLength); - nonCodeAlternateBlob.AppendData(ehInfo->HandlerOffset); - nonCodeAlternateBlob.AppendData(ehInfo->HandlerLength); - if (ehInfo->Flags == CorExceptionFlag::COR_ILEXCEPTION_CLAUSE_NONE) // typed eh - { - nonCodeAlternateBlob.AppendToken(GetAlternateToken(ehInfo->ClassToken)); - } - else if (ehInfo->Flags == CorExceptionFlag::COR_ILEXCEPTION_CLAUSE_FILTER) - { - nonCodeAlternateBlob.AppendData(ehInfo->FilterOffset); - } - } - - if (header.cbLocalVarSig == 0) - { - nonCodeAlternateBlob.AppendByte(0); - } - else - { - nonCodeAlternateBlob.AppendByte((header.Flags & CorILMethod_InitLocals) ? 1 : 0); - SigParser localSigParser(header.LocalVarSig, header.cbLocalVarSig); - StandaloneSigTranslator sigTranslator(&localSigParser, &nonCodeAlternateBlob, this); - sigTranslator.ParseLocalsSignature(); - } - - while (!DoneReadingIL()) - { - uint32_t ilOpcode = ReadILByte(); - if (ilOpcode == CEE_PREFIX1) - { - ilOpcode = 0x100 + ReadILByte(); - } - - switch (ilOpcode) - { - case CEE_LDARG_S: - case CEE_LDARGA_S: - case CEE_STARG_S: - case CEE_LDLOC_S: - case CEE_LDLOCA_S: - case CEE_STLOC_S: - case CEE_LDC_I4_S: - case CEE_UNALIGNED: - case CEE_UNUSED69: // This is the no. prefix that is partially defined in Partition III. - SkipIL(1); - break; - case CEE_LDARG: - case CEE_LDARGA: - case CEE_STARG: - case CEE_LDLOC: - case CEE_LDLOCA: - case CEE_STLOC: - SkipIL(2); - break; - case CEE_LDC_I4: - case CEE_LDC_R4: - SkipIL(4); - break; - case CEE_LDC_I8: - case CEE_LDC_R8: - SkipIL(8); - break; - case CEE_JMP: - case CEE_CALL: - case CEE_CALLI: - case CEE_CALLVIRT: - case CEE_CPOBJ: - case CEE_LDOBJ: - case CEE_LDSTR: - case CEE_NEWOBJ: - case CEE_CASTCLASS: - case CEE_ISINST: - case CEE_UNBOX: - case CEE_LDFLD: - case CEE_LDFLDA: - case CEE_STFLD: - case CEE_LDSFLD: - case CEE_LDSFLDA: - case CEE_STSFLD: - case CEE_STOBJ: - case CEE_BOX: - case CEE_NEWARR: - case CEE_LDELEMA: - case CEE_LDELEM: - case CEE_STELEM: - case CEE_UNBOX_ANY: - case CEE_REFANYVAL: - case CEE_MKREFANY: - case CEE_LDTOKEN: - case CEE_LDFTN: - case CEE_LDVIRTFTN: - case CEE_INITOBJ: - case CEE_CONSTRAINED: - case CEE_SIZEOF: - ReplaceToken(); - break; - case CEE_BR_S: - case CEE_LEAVE_S: - case CEE_BRFALSE_S: - case CEE_BRTRUE_S: - case CEE_BEQ_S: - case CEE_BGE_S: - case CEE_BGT_S: - case CEE_BLE_S: - case CEE_BLT_S: - case CEE_BNE_UN_S: - case CEE_BGE_UN_S: - case CEE_BGT_UN_S: - case CEE_BLE_UN_S: - case CEE_BLT_UN_S: - SkipIL(1); - break; - case CEE_BR: - case CEE_LEAVE: - case CEE_BRFALSE: - case CEE_BRTRUE: - case CEE_BEQ: - case CEE_BGE: - case CEE_BGT: - case CEE_BLE: - case CEE_BLT: - case CEE_BNE_UN: - case CEE_BGE_UN: - case CEE_BGT_UN: - case CEE_BLE_UN: - case CEE_BLT_UN: - SkipIL(4); - break; - case CEE_SWITCH: - { - uint32_t count = ReadILUInt32(); - if (count > 0x1FFFFFFF) - ThrowHR(COR_E_BADIMAGEFORMAT); - SkipIL(count * 4); - } - break; - default: - continue; - } - } - - ClrSafeInt dataStreamSize; - DWORD nonCodeAlternateDataSize = 0; - DWORD alternateNonTypeRefStreamSize = 0; - PVOID nonCodeAlternateData = nonCodeAlternateBlob.GetSignature(&nonCodeAlternateDataSize); - PVOID alternateNonTypeRefStreamData = alternateNonTypeRefStream.GetSignature(&alternateNonTypeRefStreamSize); - dataStreamSize = ClrSafeInt(S_SIZE_T(nonCodeAlternateDataSize) + S_SIZE_T(ilStream.GetCount()) + S_SIZE_T(alternateNonTypeRefStreamSize)); - if (dataStreamSize.IsOverflow()) - ThrowHR(COR_E_BADIMAGEFORMAT); - - uint8_t *pData = pDataStream->OpenRawBuffer(dataStreamSize.Value()); - memcpy(pData, nonCodeAlternateData, nonCodeAlternateDataSize); - ilStream.Copy(pData + nonCodeAlternateDataSize, ilStream.Begin(), ilStream.GetCount()); - memcpy(pData + nonCodeAlternateDataSize + ilStream.GetCount(), alternateNonTypeRefStreamData, alternateNonTypeRefStreamSize); - pDataStream->CloseRawBuffer(); - } - - // Convert a token which may be a memberref parent into a member ref coded index - uint32_t DecodeMemberRefCodedIndexToken(uint32_t codedIndex) - { - static const mdToken s_tableTokenTypes[] = {mdtTypeDef, mdtTypeRef, mdtModuleRef, mdtMethodDef, mdtTypeSpec, 0, 0, 0}; - return TokenFromRid((codedIndex & 0xFFFFFF8) >> 3, s_tableTokenTypes[codedIndex & 0x7]); - } - - uint32_t MemberRefParentCodedIndex(mdToken tk) - { - RID rid = RidFromToken(tk); - ULONG32 ulTyp = TypeFromToken(tk); - - if ((rid > 0xFFFFFF) || rid == 0) - { - ThrowHR(COR_E_BADIMAGEFORMAT); - } - - rid = (rid << 3); - - // TypeDef is encoded with low bits 000 - // TypeRef is encoded with low bits 001 - // ModuleRef is encoded with low bits 010 - // MethodDef is encoded with low bit 011 - // TypeSpec is encoded with low bits 100 - for (uint32_t codedIndexType = 0; codedIndexType <= 7; codedIndexType++) - { - if (DecodeMemberRefCodedIndexToken(rid + codedIndexType) == tk) - { - return rid + codedIndexType; - } - } - ThrowHR(COR_E_BADIMAGEFORMAT); - } - - uint32_t GetAlternateToken(uint32_t inputToken) - { - uint32_t alternativeToken = 0; - if (!alternateTokens.Lookup(inputToken, &alternativeToken)) - { - if ((TypeFromToken(inputToken) == mdtTypeDef) || (TypeFromToken(inputToken) == mdtTypeRef)) - { - pTypeRefTokenStream->Append(inputToken); - alternativeToken = TokenFromRid((uint32_t)pTypeRefTokenStream->GetCount(), mdtTypeRef); - } - else - { - uint32_t newTokenType = 0; - SigBuilder blob; - switch (TypeFromToken(inputToken)) - { - case mdtString: - { - newTokenType = mdtString; - LPCWSTR wszUserString; - DWORD cchString; - IfFailThrow(pMDImport->GetUserString(inputToken, &cchString, NULL, &wszUserString)); - blob.AppendData(cchString); - blob.AppendBlob((void * const)wszUserString, sizeof(WCHAR) * cchString); - // TODO: consider encoding via wtf-8, or possibly utf-8 - break; - } - case mdtTypeSpec: - { - newTokenType = mdtTypeSpec; - PCCOR_SIGNATURE sigPtr; - ULONG cbSig; - IfFailThrow(pMDImport->GetTypeSpecFromToken(inputToken, &sigPtr, &cbSig)); - SigParser typeSpecSig(sigPtr, cbSig); - StandaloneSigTranslator sigTranslator(&typeSpecSig, &blob, this); - sigTranslator.ParseType(); - break; - } - case mdtMemberRef: - { - newTokenType = mdtMemberRef; - PCCOR_SIGNATURE sig; - ULONG cbSig; - LPCSTR name; - mdToken memberRefParent; - - IfFailThrow(pMDImport->GetNameAndSigOfMemberRef(inputToken, &sig, &cbSig, &name)); - IfFailThrow(pMDImport->GetParentOfMemberRef(inputToken, &memberRefParent)); - - SigParser memberRefSigParse(sig, cbSig); - - StandaloneSigTranslator sigTranslator(&memberRefSigParse, &blob, this); - sigTranslator.ParseMemberRefSignature(); - ULONG strLen = (ULONG)strlen(name); // Cast to ULONG is safe, as the data is held in a PE file - blob.AppendData((ULONG)strlen(name)); - blob.AppendBlob((const PVOID)name, strLen); - blob.AppendData(MemberRefParentCodedIndex(GetAlternateToken(memberRefParent))); - break; - } - case mdtMethodDef: - { - newTokenType = mdtMemberRef; - PCCOR_SIGNATURE sig; - ULONG cbSig; - LPCSTR name; - mdToken methodDefParent; - - IfFailThrow(pMDImport->GetNameAndSigOfMethodDef(inputToken, &sig, &cbSig, &name)); - IfFailThrow(pMDImport->GetParentToken(inputToken, &methodDefParent)); - SigParser methodDefSigParse(sig, cbSig); - StandaloneSigTranslator sigTranslator(&methodDefSigParse, &blob, this); - sigTranslator.ParseMethodSignature(); - ULONG strLen = (ULONG)strlen(name); // Cast to ULONG is safe, as the data is held in a PE file - blob.AppendData((ULONG)strlen(name)); - blob.AppendBlob((const PVOID)name, strLen); - blob.AppendData(MemberRefParentCodedIndex(GetAlternateToken(methodDefParent))); - break; - } - case mdtFieldDef: - { - newTokenType = mdtMemberRef; - PCCOR_SIGNATURE sig; - ULONG cbSig; - LPCSTR name; - mdToken fieldDefParent; - - IfFailThrow(pMDImport->GetSigOfFieldDef(inputToken, &cbSig, &sig)); - IfFailThrow(pMDImport->GetNameOfFieldDef(inputToken, &name)); - IfFailThrow(pMDImport->GetParentToken(inputToken, &fieldDefParent)); - SigParser fieldDefSigParse(sig, cbSig); - StandaloneSigTranslator sigTranslator(&fieldDefSigParse, &blob, this); - sigTranslator.ParseFieldSignature(); - ULONG strLen = (ULONG)strlen(name); // Cast to ULONG is safe, as the data is held in a PE file - blob.AppendData((ULONG)strlen(name)); - blob.AppendBlob((const PVOID)name, strLen); - blob.AppendData(MemberRefParentCodedIndex(GetAlternateToken(fieldDefParent))); - break; - } - case mdtMethodSpec: - { - newTokenType = mdtMethodSpec; - PCCOR_SIGNATURE sig; - ULONG cbSig; - mdToken methodSpecParent; - IfFailThrow(pMDImport->GetMethodSpecProps(inputToken, &methodSpecParent, &sig, &cbSig)); - mdToken tkMethodSpecParentAlternate = GetAlternateToken(methodSpecParent); - if (TypeFromToken(tkMethodSpecParentAlternate) != mdtMemberRef) - { - ThrowHR(COR_E_BADIMAGEFORMAT); - } - blob.AppendData(RidFromToken(tkMethodSpecParentAlternate)); - SigParser methodSpecSigParse(sig, cbSig); - StandaloneSigTranslator sigTranslator(&methodSpecSigParse, &blob, this); - sigTranslator.ParseMethodSpecSignature(); - break; - } - case mdtSignature: - { - newTokenType = mdtSignature; - PCCOR_SIGNATURE sig; - ULONG cbSig; - IfFailThrow(pMDImport->GetSigFromToken(inputToken, &cbSig, &sig)); - SigParser standaloneSigParse(sig, cbSig); - StandaloneSigTranslator sigTranslator(&standaloneSigParse, &blob, this); - sigTranslator.ParseMethodSignature(); - break; - } - - default: - ThrowHR(COR_E_BADIMAGEFORMAT); - } - - ULONG newStreamLen; - PVOID newSig = blob.GetSignature(&newStreamLen); - - alternateNonTypeRefStream.AppendBlob(newSig, newStreamLen); - alternativeToken = TokenFromRid((uint32_t)alternateTokens.GetCount() + 1, newTokenType); - } - alternateTokens.Add(inputToken, alternativeToken); - } - return alternativeToken; - } - - bool DoneReadingIL() - { - return currentILStreamIterator == ilStream.GetCount(); - } - uint8_t ReadILByte() - { - if (DoneReadingIL()) - { - ThrowHR(COR_E_BADIMAGEFORMAT); - } - uint8_t result = ilStream[currentILStreamIterator]; - ++currentILStreamIterator; - return result; - } - void SkipIL(size_t countToSkip) - { - currentILStreamIterator = currentILStreamIterator + (COUNT_T)countToSkip; - if (currentILStreamIterator > ilStream.GetCount()) - ThrowHR(COR_E_BADIMAGEFORMAT); - } - uint32_t ReadILUInt32() - { - uint32_t val; - if ((currentILStreamIterator + 4) > ilStream.GetCount()) - ThrowHR(COR_E_BADIMAGEFORMAT); - ilStream.Copy(&val, ilStream.Begin() + currentILStreamIterator, 4); - currentILStreamIterator = currentILStreamIterator + 4; - val = VAL32(val); - return val; - } - void ReplaceToken() - { - if ((currentILStreamIterator + 4) > ilStream.GetCount()) - ThrowHR(COR_E_BADIMAGEFORMAT); - - uint32_t token; - ilStream.Copy(&token, ilStream.Begin() + currentILStreamIterator, 4); - token = VAL32(token); - uint32_t newToken = GetAlternateToken(token); - newToken = VAL32(newToken); - ilStream.Copy(ilStream.Begin() + currentILStreamIterator, reinterpret_cast(&newToken), 4); - currentILStreamIterator = currentILStreamIterator + 4; - } - - class StandaloneSigTranslator - { - SigParser *pSigInput; - SigBuilder *pSigOutput; - AlternateBlockHelper *pHelper; - - uint8_t ParseByte() - { - uint8_t value; - IfFailThrow(pSigInput->GetByte(&value)); - pSigOutput->AppendByte(value); - return value; - } - - uint8_t PeekByte() - { - uint8_t value; - IfFailThrow(pSigInput->PeekByte(&value)); - return value; - } - - uint32_t ParseCompressedInt() - { - uint32_t value; - IfFailThrow(pSigInput->GetData(&value)); - pSigOutput->AppendData(value); - return value; - } - - void ParseTypeHandle() - { - uint32_t token; - IfFailThrow(pSigInput->GetToken(&token)); - uint32_t newToken = pHelper->GetAlternateToken(token); - pSigOutput->AppendToken(newToken); - } - - public: - StandaloneSigTranslator(SigParser *sigInput, SigBuilder* sigOutput, AlternateBlockHelper *helper) : - pSigInput(sigInput), - pSigOutput(sigOutput), - pHelper(helper) - {} - - void ParseType() - { - CorElementType elemType; - for (;;) - { - elemType = (CorElementType)ParseByte(); - switch (elemType) - { - case ELEMENT_TYPE_CMOD_REQD: - case ELEMENT_TYPE_CMOD_OPT: - ParseTypeHandle(); - continue; - case ELEMENT_TYPE_PINNED: - case ELEMENT_TYPE_SENTINEL: - continue; - - default: - break; - } - break; - } - - switch (elemType) - { - case ELEMENT_TYPE_VOID: - case ELEMENT_TYPE_BOOLEAN: - case ELEMENT_TYPE_CHAR: - case ELEMENT_TYPE_I1: - case ELEMENT_TYPE_U1: - case ELEMENT_TYPE_I2: - case ELEMENT_TYPE_U2: - case ELEMENT_TYPE_I4: - case ELEMENT_TYPE_U4: - case ELEMENT_TYPE_I8: - case ELEMENT_TYPE_U8: - case ELEMENT_TYPE_R4: - case ELEMENT_TYPE_R8: - case ELEMENT_TYPE_STRING: - case ELEMENT_TYPE_OBJECT: - case ELEMENT_TYPE_TYPEDBYREF: - case ELEMENT_TYPE_I: - case ELEMENT_TYPE_U: - break; - - case ELEMENT_TYPE_SZARRAY: - case ELEMENT_TYPE_BYREF: - case ELEMENT_TYPE_PTR: - ParseType(); - break; - case ELEMENT_TYPE_ARRAY: - { - ParseType(); - uint32_t rank = ParseCompressedInt(); - uint32_t boundsCount = ParseCompressedInt(); - for (uint32_t i = 0; i < boundsCount; i++) - { - ParseCompressedInt(); - } - uint32_t lowerBoundsCount = ParseCompressedInt(); - for (uint32_t i = 0; i < lowerBoundsCount; i++) - { - ParseCompressedInt(); // We don't need to parse as signed compressed ints as those can be round-tripped without distinguishing from a normal compressed int - } - break; - } - case ELEMENT_TYPE_VAR: - case ELEMENT_TYPE_MVAR: - ParseCompressedInt(); - break; - case ELEMENT_TYPE_GENERICINST: - { - ParseType(); - uint32_t instanceLength = ParseCompressedInt(); - for (uint32_t i = 0; i < instanceLength; i++) - { - ParseType(); - } - break; - } - case ELEMENT_TYPE_FNPTR: - { - ParseMethodSignature(); - break; - } - case ELEMENT_TYPE_CLASS: - case ELEMENT_TYPE_VALUETYPE: - { - ParseTypeHandle(); - break; - } - default: - ThrowHR(COR_E_BADIMAGEFORMAT); - } - } - - void ParseLocalsSignature() - { - uint8_t sigHeader = ParseByte(); - if (sigHeader != IMAGE_CEE_CS_CALLCONV_LOCAL_SIG) - ThrowHR(COR_E_BADIMAGEFORMAT); - - uint32_t localsCount = ParseCompressedInt(); - for (uint32_t i = 0; i < localsCount; i++) - { - ParseType(); - } - } - - void ParseMemberRefSignature() - { - uint8_t sigHeader = PeekByte(); - if (sigHeader == IMAGE_CEE_CS_CALLCONV_FIELD) - { - ParseFieldSignature(); - } - else - { - ParseMethodSignature(); - } - } - - void ParseFieldSignature() - { - uint8_t sigHeader = ParseByte(); - if (sigHeader != IMAGE_CEE_CS_CALLCONV_FIELD) - { - ThrowHR(COR_E_BADIMAGEFORMAT); - } - - ParseType(); - } - - void ParseMethodSpecSignature() - { - uint8_t sigHeader = ParseByte(); - if (sigHeader != IMAGE_CEE_CS_CALLCONV_GENERICINST) - { - ThrowHR(COR_E_BADIMAGEFORMAT); - } - - uint32_t argCount = ParseCompressedInt(); - for (uint32_t i = 0; i < argCount; i++) - { - ParseType(); - } - } - - void ParseMethodSignature() - { - uint8_t sigHeader = ParseByte(); - if (sigHeader & IMAGE_CEE_CS_CALLCONV_GENERIC) - { - // Parse arity - ParseCompressedInt(); - } - uint32_t argCount = ParseCompressedInt(); - for (uint32_t i = 0; i <= argCount; i++) - { - ParseType(); - } - } - }; -}; - -#ifndef DACCESS_COMPILE -static CrstStatic s_csMethodBlocks; -static MapSHash s_methodBlocks; - -void GenerateMethodBlock(MethodDesc *pMD, MethodBlock *pBlock) -{ - SArray tokenStream; - SArray byteData; - AlternateBlockHelper helper(pMD, &tokenStream); - helper.GenerateDataStreams(&byteData); - - pBlock->cByteData = byteData.GetCount(); - pBlock->pByteData = new uint8_t[pBlock->cByteData]; - pBlock->cTypes = tokenStream.GetCount(); - pBlock->pTypes = new TypeHandle[pBlock->cTypes]; - - byteData.Copy((uint8_t*)pBlock->pByteData, byteData.Begin(), byteData.GetCount()); - for (COUNT_T i = 0; i < tokenStream.GetCount(); i++) - { - ((TypeHandle*)pBlock->pTypes)[i] = ClassLoader::LoadTypeDefOrRefThrowing(pMD->GetModule(), tokenStream[i], ClassLoader::ThrowIfNotFound, ClassLoader::PermitUninstDefOrRef, 0, CLASS_LOAD_APPROXPARENTS); - } -} - -void InitMethodBlocks() -{ - s_csMethodBlocks.Init(CrstLeafLock); -} - -MethodBlock* GetMethodBlock(MethodDesc *pMD) -{ - MethodBlock* retVal; - - { - CrstHolder lock(&s_csMethodBlocks); - if (s_methodBlocks.Lookup(pMD, &retVal)) - { - return retVal; - } - } - - NewHolder newMethodBlock = new MethodBlock(); - GenerateMethodBlock(pMD, newMethodBlock); - - { - CrstHolder lock(&s_csMethodBlocks); - if (s_methodBlocks.Lookup(pMD, &retVal)) - { - return retVal; - } - - s_methodBlocks.Add(pMD, newMethodBlock); - retVal = newMethodBlock.Extract(); - } - return retVal; -} -#endif // DACCESS_COMPILE \ No newline at end of file diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index 3caf524f2196b..78a0f11ffe545 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -3705,16 +3705,17 @@ class CalledMethod }; #endif -struct MethodBlock +#ifdef FEATURE_READYTORUN +struct ReadyToRunStandaloneMethodMetadata { - MethodBlock() : + ReadyToRunStandaloneMethodMetadata() : pByteData(nullptr), cByteData(0), pTypes(nullptr), cTypes(0) {} - ~MethodBlock() + ~ReadyToRunStandaloneMethodMetadata() { if (pByteData != nullptr) delete[] pByteData; @@ -3727,8 +3728,10 @@ struct MethodBlock const TypeHandle * pTypes; size_t cTypes; }; -MethodBlock* GetMethodBlock(MethodDesc *pMD); -void InitMethodBlocks(); + +ReadyToRunStandaloneMethodMetadata* GetReadyToRunStandaloneMethodMetadata(MethodDesc *pMD); +void InitReadyToRunStandaloneMethodMetadata(); +#endif // FEATURE_READYTORUN #include "method.inl" diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index cbc7e7e87575a..9212b88deb4e4 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -636,12 +636,12 @@ ReadyToRunInfo::ReadyToRunInfo(Module * pModule, LoaderAllocator* pLoaderAllocat m_pComposite = &m_component; m_isComponentAssembly = false; IMDInternalImport *pNativeMDImport; - IMAGE_DATA_DIRECTORY * pNativeMetadatSection = m_pComposite->FindSection(ReadyToRunSectionType::ManifestMetadata); - if (pNativeMetadatSection != NULL) + IMAGE_DATA_DIRECTORY * pNativeMetadataSection = m_pComposite->FindSection(ReadyToRunSectionType::ManifestMetadata); + if (pNativeMetadataSection != NULL) { pNativeMDImport = NULL; - IfFailThrow(GetMetaDataInternalInterface((void *) m_pComposite->GetLayout()->GetDirectoryData(pNativeMetadatSection), - pNativeMetadatSection->Size, + IfFailThrow(GetMetaDataInternalInterface((void *) m_pComposite->GetLayout()->GetDirectoryData(pNativeMetadataSection), + pNativeMetadataSection->Size, ofRead, IID_IMDInternalImport, (void **) &pNativeMDImport)); @@ -935,8 +935,6 @@ bool ReadyToRunInfo::GetPgoInstrumentationData(MethodDesc * pMD, BYTE** pAllocat return false; } -//#define LOG_R2R_ENTRYPOINT - PCODE ReadyToRunInfo::GetEntryPoint(MethodDesc * pMD, PrepareCodeConfig* pConfig, BOOL fFixups) { STANDARD_VM_CONTRACT; @@ -960,17 +958,6 @@ PCODE ReadyToRunInfo::GetEntryPoint(MethodDesc * pMD, PrepareCodeConfig* pConfig if (ReadyToRunCodeDisabled()) goto done; -#ifdef LOG_R2R_ENTRYPOINT - pMD->GetMethodInfo(tNamespace, tMethodName, tMethodSignature); - tFullname.Append(tNamespace); - tFullname.Append(tMethodName); - tFullname.Append(tMethodSignature); - szFullName = tFullname.GetUTF8(scratch); - - printf("ReadyToRunInfo::GetEntryPoint %s\n", szFullName); - printedStart = true; -#endif - ETW::MethodLog::GetR2RGetEntryPointStart(pMD); uint offset; @@ -1073,11 +1060,6 @@ PCODE ReadyToRunInfo::GetEntryPoint(MethodDesc * pMD, PrepareCodeConfig* pConfig } done: -#ifdef LOG_R2R_ENTRYPOINT - if (printedStart) - printf("ReadyToRunInfo::GetEntryPoint Found: %p %s\n", (void*)pEntryPoint, szFullName); -#endif - if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, R2RGetEntryPoint)) { ETW::MethodLog::GetR2RGetEntryPoint(pMD, pEntryPoint); @@ -1468,7 +1450,6 @@ class NativeManifestModule : public ModuleBase ModuleBase* CreateNativeManifestModule(LoaderAllocator* pLoaderAllocator, IMDInternalImport *pManifestMetadata, AllocMemTracker *pamTracker) { - // Should be moved to ModuleBase initialization void *mem = pamTracker->Track(pLoaderAllocator->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(NativeManifestModule)))); return new (mem) NativeManifestModule(pLoaderAllocator, pManifestMetadata, pamTracker); } diff --git a/src/coreclr/vm/readytorunstandalonemethodmetadata.cpp b/src/coreclr/vm/readytorunstandalonemethodmetadata.cpp new file mode 100644 index 0000000000000..1626504da5f56 --- /dev/null +++ b/src/coreclr/vm/readytorunstandalonemethodmetadata.cpp @@ -0,0 +1,693 @@ +#include "common.h" + +#include "openum.h" + +#ifdef FEATURE_READYTORUN +// Alternate form of metadata that represents a single method. Self contained except for type references +// The behavior of this code must exactly match that of the ReadyToRunStandaloneMethodMetadata class in CrossGen2 +// That code can be found in src\coreclr\tools\aot\ILCompiler.ReadyToRun\Compiler\ReadyToRunStandaloneMethodMetadata.cs +class ReadyToRunStandaloneMethodMetadataHelper +{ + COR_ILMETHOD_DECODER header; + + SigBuilder nonCodeAlternateBlob; + SigBuilder alternateNonTypeRefStream; + SArray ilStream; + COUNT_T currentILStreamIterator; + SArray *pTypeRefTokenStream; + + MapSHash alternateTokens; + Module* pModule; + IMDInternalImport* pMDImport; + +public: + + ReadyToRunStandaloneMethodMetadataHelper(MethodDesc *pMD, SArray *pTypeRefTokenStreamInput) : + header(pMD->GetILHeader(TRUE), pMD->GetMDImport(), NULL), + currentILStreamIterator(0), + pTypeRefTokenStream(pTypeRefTokenStreamInput), + pModule(pMD->GetModule()), + pMDImport(pMD->GetMDImport()) + { + { + // Fill IL stream with initial data + byte* ilStreamData = ilStream.OpenRawBuffer(header.CodeSize); + memcpy(ilStreamData, header.Code, header.CodeSize); + ilStream.CloseRawBuffer(header.CodeSize); + } + } + + void GenerateDataStreams(SArray *pDataStream) + { + unsigned ehCount = header.EHCount(); + nonCodeAlternateBlob.AppendData(ehCount); + for (unsigned e = 0; e < ehCount; e++) + { + IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT ehBuff; + const IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT* ehInfo; + + ehInfo = header.EH->EHClause(e, &ehBuff); + + nonCodeAlternateBlob.AppendData(ehInfo->Flags); + nonCodeAlternateBlob.AppendData(ehInfo->TryOffset); + nonCodeAlternateBlob.AppendData(ehInfo->TryLength); + nonCodeAlternateBlob.AppendData(ehInfo->HandlerOffset); + nonCodeAlternateBlob.AppendData(ehInfo->HandlerLength); + if (ehInfo->Flags == CorExceptionFlag::COR_ILEXCEPTION_CLAUSE_NONE) // typed eh + { + nonCodeAlternateBlob.AppendToken(GetAlternateToken(ehInfo->ClassToken)); + } + else if (ehInfo->Flags == CorExceptionFlag::COR_ILEXCEPTION_CLAUSE_FILTER) + { + nonCodeAlternateBlob.AppendData(ehInfo->FilterOffset); + } + } + + if (header.cbLocalVarSig == 0) + { + nonCodeAlternateBlob.AppendByte(0); + } + else + { + nonCodeAlternateBlob.AppendByte((header.Flags & CorILMethod_InitLocals) ? 1 : 0); + SigParser localSigParser(header.LocalVarSig, header.cbLocalVarSig); + StandaloneSigTranslator sigTranslator(&localSigParser, &nonCodeAlternateBlob, this); + sigTranslator.ParseLocalsSignature(); + } + + while (!DoneReadingIL()) + { + uint32_t ilOpcode = ReadILByte(); + if (ilOpcode == CEE_PREFIX1) + { + ilOpcode = 0x100 + ReadILByte(); + } + + switch (ilOpcode) + { + case CEE_LDARG_S: + case CEE_LDARGA_S: + case CEE_STARG_S: + case CEE_LDLOC_S: + case CEE_LDLOCA_S: + case CEE_STLOC_S: + case CEE_LDC_I4_S: + case CEE_UNALIGNED: + case CEE_UNUSED69: // This is the no. prefix that is partially defined in Partition III. + SkipIL(1); + break; + case CEE_LDARG: + case CEE_LDARGA: + case CEE_STARG: + case CEE_LDLOC: + case CEE_LDLOCA: + case CEE_STLOC: + SkipIL(2); + break; + case CEE_LDC_I4: + case CEE_LDC_R4: + SkipIL(4); + break; + case CEE_LDC_I8: + case CEE_LDC_R8: + SkipIL(8); + break; + case CEE_JMP: + case CEE_CALL: + case CEE_CALLI: + case CEE_CALLVIRT: + case CEE_CPOBJ: + case CEE_LDOBJ: + case CEE_LDSTR: + case CEE_NEWOBJ: + case CEE_CASTCLASS: + case CEE_ISINST: + case CEE_UNBOX: + case CEE_LDFLD: + case CEE_LDFLDA: + case CEE_STFLD: + case CEE_LDSFLD: + case CEE_LDSFLDA: + case CEE_STSFLD: + case CEE_STOBJ: + case CEE_BOX: + case CEE_NEWARR: + case CEE_LDELEMA: + case CEE_LDELEM: + case CEE_STELEM: + case CEE_UNBOX_ANY: + case CEE_REFANYVAL: + case CEE_MKREFANY: + case CEE_LDTOKEN: + case CEE_LDFTN: + case CEE_LDVIRTFTN: + case CEE_INITOBJ: + case CEE_CONSTRAINED: + case CEE_SIZEOF: + ReplaceToken(); + break; + case CEE_BR_S: + case CEE_LEAVE_S: + case CEE_BRFALSE_S: + case CEE_BRTRUE_S: + case CEE_BEQ_S: + case CEE_BGE_S: + case CEE_BGT_S: + case CEE_BLE_S: + case CEE_BLT_S: + case CEE_BNE_UN_S: + case CEE_BGE_UN_S: + case CEE_BGT_UN_S: + case CEE_BLE_UN_S: + case CEE_BLT_UN_S: + SkipIL(1); + break; + case CEE_BR: + case CEE_LEAVE: + case CEE_BRFALSE: + case CEE_BRTRUE: + case CEE_BEQ: + case CEE_BGE: + case CEE_BGT: + case CEE_BLE: + case CEE_BLT: + case CEE_BNE_UN: + case CEE_BGE_UN: + case CEE_BGT_UN: + case CEE_BLE_UN: + case CEE_BLT_UN: + SkipIL(4); + break; + case CEE_SWITCH: + { + uint32_t count = ReadILUInt32(); + if (count > 0x1FFFFFFF) + ThrowHR(COR_E_BADIMAGEFORMAT); + SkipIL(count * 4); + } + break; + default: + continue; + } + } + + ClrSafeInt dataStreamSize; + DWORD nonCodeAlternateDataSize = 0; + DWORD alternateNonTypeRefStreamSize = 0; + PVOID nonCodeAlternateData = nonCodeAlternateBlob.GetSignature(&nonCodeAlternateDataSize); + PVOID alternateNonTypeRefStreamData = alternateNonTypeRefStream.GetSignature(&alternateNonTypeRefStreamSize); + dataStreamSize = ClrSafeInt(S_SIZE_T(nonCodeAlternateDataSize) + S_SIZE_T(ilStream.GetCount()) + S_SIZE_T(alternateNonTypeRefStreamSize)); + if (dataStreamSize.IsOverflow()) + ThrowHR(COR_E_BADIMAGEFORMAT); + + uint8_t *pData = pDataStream->OpenRawBuffer(dataStreamSize.Value()); + memcpy(pData, nonCodeAlternateData, nonCodeAlternateDataSize); + ilStream.Copy(pData + nonCodeAlternateDataSize, ilStream.Begin(), ilStream.GetCount()); + memcpy(pData + nonCodeAlternateDataSize + ilStream.GetCount(), alternateNonTypeRefStreamData, alternateNonTypeRefStreamSize); + pDataStream->CloseRawBuffer(); + } + + uint32_t MemberRefParentCodedIndex(mdToken tk) + { + RID rid = RidFromToken(tk); + ULONG32 ulTyp = TypeFromToken(tk); + + if ((rid > 0xFFFFFF) || rid == 0) + { + ThrowHR(COR_E_BADIMAGEFORMAT); + } + + rid = (rid << 3); + uint32_t codedIndexType; + switch (TypeFromToken(tk)) + { + // TypeDef is encoded with low bits 000 + case mdtTypeDef: codedIndexType = 0; break; + // TypeRef is encoded with low bits 001 + case mdtTypeRef: codedIndexType = 1; break; + // ModuleRef is encoded with low bits 010 + case mdtModuleRef: codedIndexType = 2; break; + // MethodDef is encoded with low bit 011 + case mdtMethodDef: codedIndexType = 3; break; + // TypeSpec is encoded with low bits 100 + case mdtTypeSpec: codedIndexType = 4; break; + default: + ThrowHR(COR_E_BADIMAGEFORMAT); + } + + return rid + codedIndexType; + } + + uint32_t GetAlternateToken(uint32_t inputToken) + { + uint32_t alternativeToken = 0; + if (!alternateTokens.Lookup(inputToken, &alternativeToken)) + { + if ((TypeFromToken(inputToken) == mdtTypeDef) || (TypeFromToken(inputToken) == mdtTypeRef)) + { + pTypeRefTokenStream->Append(inputToken); + alternativeToken = TokenFromRid((uint32_t)pTypeRefTokenStream->GetCount(), mdtTypeRef); + } + else + { + uint32_t newTokenType = 0; + SigBuilder blob; + switch (TypeFromToken(inputToken)) + { + case mdtString: + { + newTokenType = mdtString; + LPCWSTR wszUserString; + DWORD cchString; + IfFailThrow(pMDImport->GetUserString(inputToken, &cchString, NULL, &wszUserString)); + blob.AppendData(cchString); + blob.AppendBlob((void * const)wszUserString, sizeof(WCHAR) * cchString); + // TODO: consider encoding via wtf-8, or possibly utf-8 + break; + } + case mdtTypeSpec: + { + newTokenType = mdtTypeSpec; + PCCOR_SIGNATURE sigPtr; + ULONG cbSig; + IfFailThrow(pMDImport->GetTypeSpecFromToken(inputToken, &sigPtr, &cbSig)); + SigParser typeSpecSig(sigPtr, cbSig); + StandaloneSigTranslator sigTranslator(&typeSpecSig, &blob, this); + sigTranslator.ParseType(); + break; + } + case mdtMemberRef: + { + newTokenType = mdtMemberRef; + PCCOR_SIGNATURE sig; + ULONG cbSig; + LPCSTR name; + mdToken memberRefParent; + + IfFailThrow(pMDImport->GetNameAndSigOfMemberRef(inputToken, &sig, &cbSig, &name)); + IfFailThrow(pMDImport->GetParentOfMemberRef(inputToken, &memberRefParent)); + + SigParser memberRefSigParse(sig, cbSig); + + StandaloneSigTranslator sigTranslator(&memberRefSigParse, &blob, this); + sigTranslator.ParseMemberRefSignature(); + ULONG strLen = (ULONG)strlen(name); // Cast to ULONG is safe, as the data is held in a PE file + blob.AppendData((ULONG)strlen(name)); + blob.AppendBlob((const PVOID)name, strLen); + blob.AppendData(MemberRefParentCodedIndex(GetAlternateToken(memberRefParent))); + break; + } + case mdtMethodDef: + { + newTokenType = mdtMemberRef; + PCCOR_SIGNATURE sig; + ULONG cbSig; + LPCSTR name; + mdToken methodDefParent; + + IfFailThrow(pMDImport->GetNameAndSigOfMethodDef(inputToken, &sig, &cbSig, &name)); + IfFailThrow(pMDImport->GetParentToken(inputToken, &methodDefParent)); + SigParser methodDefSigParse(sig, cbSig); + StandaloneSigTranslator sigTranslator(&methodDefSigParse, &blob, this); + sigTranslator.ParseMethodSignature(); + ULONG strLen = (ULONG)strlen(name); // Cast to ULONG is safe, as the data is held in a PE file + blob.AppendData((ULONG)strlen(name)); + blob.AppendBlob((const PVOID)name, strLen); + blob.AppendData(MemberRefParentCodedIndex(GetAlternateToken(methodDefParent))); + break; + } + case mdtFieldDef: + { + newTokenType = mdtMemberRef; + PCCOR_SIGNATURE sig; + ULONG cbSig; + LPCSTR name; + mdToken fieldDefParent; + + IfFailThrow(pMDImport->GetSigOfFieldDef(inputToken, &cbSig, &sig)); + IfFailThrow(pMDImport->GetNameOfFieldDef(inputToken, &name)); + IfFailThrow(pMDImport->GetParentToken(inputToken, &fieldDefParent)); + SigParser fieldDefSigParse(sig, cbSig); + StandaloneSigTranslator sigTranslator(&fieldDefSigParse, &blob, this); + sigTranslator.ParseFieldSignature(); + ULONG strLen = (ULONG)strlen(name); // Cast to ULONG is safe, as the data is held in a PE file + blob.AppendData((ULONG)strlen(name)); + blob.AppendBlob((const PVOID)name, strLen); + blob.AppendData(MemberRefParentCodedIndex(GetAlternateToken(fieldDefParent))); + break; + } + case mdtMethodSpec: + { + newTokenType = mdtMethodSpec; + PCCOR_SIGNATURE sig; + ULONG cbSig; + mdToken methodSpecParent; + IfFailThrow(pMDImport->GetMethodSpecProps(inputToken, &methodSpecParent, &sig, &cbSig)); + mdToken tkMethodSpecParentAlternate = GetAlternateToken(methodSpecParent); + if (TypeFromToken(tkMethodSpecParentAlternate) != mdtMemberRef) + { + ThrowHR(COR_E_BADIMAGEFORMAT); + } + blob.AppendData(RidFromToken(tkMethodSpecParentAlternate)); + SigParser methodSpecSigParse(sig, cbSig); + StandaloneSigTranslator sigTranslator(&methodSpecSigParse, &blob, this); + sigTranslator.ParseMethodSpecSignature(); + break; + } + case mdtSignature: + { + newTokenType = mdtSignature; + PCCOR_SIGNATURE sig; + ULONG cbSig; + IfFailThrow(pMDImport->GetSigFromToken(inputToken, &cbSig, &sig)); + SigParser standaloneSigParse(sig, cbSig); + StandaloneSigTranslator sigTranslator(&standaloneSigParse, &blob, this); + sigTranslator.ParseMethodSignature(); + break; + } + + default: + ThrowHR(COR_E_BADIMAGEFORMAT); + } + + ULONG newStreamLen; + PVOID newSig = blob.GetSignature(&newStreamLen); + + alternateNonTypeRefStream.AppendBlob(newSig, newStreamLen); + alternativeToken = TokenFromRid((uint32_t)alternateTokens.GetCount() + 1, newTokenType); + } + alternateTokens.Add(inputToken, alternativeToken); + } + return alternativeToken; + } + + bool DoneReadingIL() + { + return currentILStreamIterator == ilStream.GetCount(); + } + uint8_t ReadILByte() + { + if (DoneReadingIL()) + { + ThrowHR(COR_E_BADIMAGEFORMAT); + } + uint8_t result = ilStream[currentILStreamIterator]; + ++currentILStreamIterator; + return result; + } + void SkipIL(size_t countToSkip) + { + currentILStreamIterator = currentILStreamIterator + (COUNT_T)countToSkip; + if (currentILStreamIterator > ilStream.GetCount()) + ThrowHR(COR_E_BADIMAGEFORMAT); + } + uint32_t ReadILUInt32() + { + uint32_t val; + if ((currentILStreamIterator + 4) > ilStream.GetCount()) + ThrowHR(COR_E_BADIMAGEFORMAT); + ilStream.Copy(&val, ilStream.Begin() + currentILStreamIterator, 4); + currentILStreamIterator = currentILStreamIterator + 4; + val = VAL32(val); + return val; + } + void ReplaceToken() + { + if ((currentILStreamIterator + 4) > ilStream.GetCount()) + ThrowHR(COR_E_BADIMAGEFORMAT); + + uint32_t token; + ilStream.Copy(&token, ilStream.Begin() + currentILStreamIterator, 4); + token = VAL32(token); + uint32_t newToken = GetAlternateToken(token); + newToken = VAL32(newToken); + ilStream.Copy(ilStream.Begin() + currentILStreamIterator, reinterpret_cast(&newToken), 4); + currentILStreamIterator = currentILStreamIterator + 4; + } + + class StandaloneSigTranslator + { + SigParser *pSigInput; + SigBuilder *pSigOutput; + ReadyToRunStandaloneMethodMetadataHelper *pHelper; + + uint8_t ParseByte() + { + uint8_t value; + IfFailThrow(pSigInput->GetByte(&value)); + pSigOutput->AppendByte(value); + return value; + } + + uint8_t PeekByte() + { + uint8_t value; + IfFailThrow(pSigInput->PeekByte(&value)); + return value; + } + + uint32_t ParseCompressedInt() + { + uint32_t value; + IfFailThrow(pSigInput->GetData(&value)); + pSigOutput->AppendData(value); + return value; + } + + void ParseTypeHandle() + { + uint32_t token; + IfFailThrow(pSigInput->GetToken(&token)); + uint32_t newToken = pHelper->GetAlternateToken(token); + pSigOutput->AppendToken(newToken); + } + + public: + StandaloneSigTranslator(SigParser *sigInput, SigBuilder* sigOutput, ReadyToRunStandaloneMethodMetadataHelper *helper) : + pSigInput(sigInput), + pSigOutput(sigOutput), + pHelper(helper) + {} + + void ParseType() + { + CorElementType elemType; + for (;;) + { + elemType = (CorElementType)ParseByte(); + switch (elemType) + { + case ELEMENT_TYPE_CMOD_REQD: + case ELEMENT_TYPE_CMOD_OPT: + ParseTypeHandle(); + continue; + case ELEMENT_TYPE_PINNED: + case ELEMENT_TYPE_SENTINEL: + continue; + + default: + break; + } + break; + } + + switch (elemType) + { + case ELEMENT_TYPE_VOID: + case ELEMENT_TYPE_BOOLEAN: + case ELEMENT_TYPE_CHAR: + case ELEMENT_TYPE_I1: + case ELEMENT_TYPE_U1: + case ELEMENT_TYPE_I2: + case ELEMENT_TYPE_U2: + case ELEMENT_TYPE_I4: + case ELEMENT_TYPE_U4: + case ELEMENT_TYPE_I8: + case ELEMENT_TYPE_U8: + case ELEMENT_TYPE_R4: + case ELEMENT_TYPE_R8: + case ELEMENT_TYPE_STRING: + case ELEMENT_TYPE_OBJECT: + case ELEMENT_TYPE_TYPEDBYREF: + case ELEMENT_TYPE_I: + case ELEMENT_TYPE_U: + break; + + case ELEMENT_TYPE_SZARRAY: + case ELEMENT_TYPE_BYREF: + case ELEMENT_TYPE_PTR: + ParseType(); + break; + case ELEMENT_TYPE_ARRAY: + { + ParseType(); + uint32_t rank = ParseCompressedInt(); + uint32_t boundsCount = ParseCompressedInt(); + for (uint32_t i = 0; i < boundsCount; i++) + { + ParseCompressedInt(); + } + uint32_t lowerBoundsCount = ParseCompressedInt(); + for (uint32_t i = 0; i < lowerBoundsCount; i++) + { + ParseCompressedInt(); // We don't need to parse as signed compressed ints as those can be round-tripped without distinguishing from a normal compressed int + } + break; + } + case ELEMENT_TYPE_VAR: + case ELEMENT_TYPE_MVAR: + ParseCompressedInt(); + break; + case ELEMENT_TYPE_GENERICINST: + { + ParseType(); + uint32_t instanceLength = ParseCompressedInt(); + for (uint32_t i = 0; i < instanceLength; i++) + { + ParseType(); + } + break; + } + case ELEMENT_TYPE_FNPTR: + { + ParseMethodSignature(); + break; + } + case ELEMENT_TYPE_CLASS: + case ELEMENT_TYPE_VALUETYPE: + { + ParseTypeHandle(); + break; + } + default: + ThrowHR(COR_E_BADIMAGEFORMAT); + } + } + + void ParseLocalsSignature() + { + uint8_t sigHeader = ParseByte(); + if (sigHeader != IMAGE_CEE_CS_CALLCONV_LOCAL_SIG) + ThrowHR(COR_E_BADIMAGEFORMAT); + + uint32_t localsCount = ParseCompressedInt(); + for (uint32_t i = 0; i < localsCount; i++) + { + ParseType(); + } + } + + void ParseMemberRefSignature() + { + uint8_t sigHeader = PeekByte(); + if (sigHeader == IMAGE_CEE_CS_CALLCONV_FIELD) + { + ParseFieldSignature(); + } + else + { + ParseMethodSignature(); + } + } + + void ParseFieldSignature() + { + uint8_t sigHeader = ParseByte(); + if (sigHeader != IMAGE_CEE_CS_CALLCONV_FIELD) + { + ThrowHR(COR_E_BADIMAGEFORMAT); + } + + ParseType(); + } + + void ParseMethodSpecSignature() + { + uint8_t sigHeader = ParseByte(); + if (sigHeader != IMAGE_CEE_CS_CALLCONV_GENERICINST) + { + ThrowHR(COR_E_BADIMAGEFORMAT); + } + + uint32_t argCount = ParseCompressedInt(); + for (uint32_t i = 0; i < argCount; i++) + { + ParseType(); + } + } + + void ParseMethodSignature() + { + uint8_t sigHeader = ParseByte(); + if (sigHeader & IMAGE_CEE_CS_CALLCONV_GENERIC) + { + // Parse arity + ParseCompressedInt(); + } + uint32_t argCount = ParseCompressedInt(); + for (uint32_t i = 0; i <= argCount; i++) + { + ParseType(); + } + } + }; +}; + +#ifndef DACCESS_COMPILE +static CrstStatic s_csReadyToRunStandaloneMethodMetadata; +static MapSHash s_methodMetadata; + +void GenerateReadyToRunStandaloneMethodMetadata(MethodDesc *pMD, ReadyToRunStandaloneMethodMetadata *pBlock) +{ + SArray tokenStream; + SArray byteData; + ReadyToRunStandaloneMethodMetadataHelper helper(pMD, &tokenStream); + helper.GenerateDataStreams(&byteData); + + pBlock->cByteData = byteData.GetCount(); + pBlock->pByteData = new uint8_t[pBlock->cByteData]; + pBlock->cTypes = tokenStream.GetCount(); + pBlock->pTypes = new TypeHandle[pBlock->cTypes]; + + byteData.Copy((uint8_t*)pBlock->pByteData, byteData.Begin(), byteData.GetCount()); + for (COUNT_T i = 0; i < tokenStream.GetCount(); i++) + { + ((TypeHandle*)pBlock->pTypes)[i] = ClassLoader::LoadTypeDefOrRefThrowing(pMD->GetModule(), tokenStream[i], ClassLoader::ThrowIfNotFound, ClassLoader::PermitUninstDefOrRef, 0, CLASS_LOAD_APPROXPARENTS); + } +} + +void InitReadyToRunStandaloneMethodMetadata() +{ + s_csReadyToRunStandaloneMethodMetadata.Init(CrstLeafLock); +} + +ReadyToRunStandaloneMethodMetadata* GetReadyToRunStandaloneMethodMetadata(MethodDesc *pMD) +{ + ReadyToRunStandaloneMethodMetadata* retVal; + + { + CrstHolder lock(&s_csReadyToRunStandaloneMethodMetadata); + if (s_methodMetadata.Lookup(pMD, &retVal)) + { + return retVal; + } + } + + NewHolder newMethodBlock = new ReadyToRunStandaloneMethodMetadata(); + GenerateReadyToRunStandaloneMethodMetadata(pMD, newMethodBlock); + + { + CrstHolder lock(&s_csReadyToRunStandaloneMethodMetadata); + if (s_methodMetadata.Lookup(pMD, &retVal)) + { + return retVal; + } + + s_methodMetadata.Add(pMD, newMethodBlock); + retVal = newMethodBlock.Extract(); + } + return retVal; +} + +#endif // !DACCESS_COMPILE +#endif // FEATURE_READYTORUN From 7326b397569b21133cef3b22281aef70cc037a33 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 10 Jun 2022 17:25:14 -0700 Subject: [PATCH 07/19] Update the R2R docs --- docs/design/coreclr/botr/readytorun-format.md | 85 ++++++++++++++++--- 1 file changed, 71 insertions(+), 14 deletions(-) diff --git a/docs/design/coreclr/botr/readytorun-format.md b/docs/design/coreclr/botr/readytorun-format.md index 6bc9a5068d9e7..4fe159720c476 100644 --- a/docs/design/coreclr/botr/readytorun-format.md +++ b/docs/design/coreclr/botr/readytorun-format.md @@ -7,6 +7,7 @@ Revisions: * 4.1 - [Tomas Rylek](https://github.com/trylek) - 2020 * 5.3 - [Tomas Rylek](https://github.com/trylek) - 2021 * 5.4 - [David Wrighton](https://github.com/davidwrighton) - 2021 +* 6.2 - [David Wrighton](https://github.com/davidwrighton) - 2022 # Introduction @@ -124,12 +125,15 @@ struct READYTORUN_CORE_HEADER ### READYTORUN_CORE_HEADER::Flags -| Flag | Value | Description -|:----------------------------------------|-----------:|:----------- -| READYTORUN_FLAG_PLATFORM_NEUTRAL_SOURCE | 0x00000001 | Set if the original IL image was platform neutral. The platform neutrality is part of assembly name. This flag can be used to reconstruct the full original assembly name. -| READYTORUN_FLAG_COMPOSITE | 0x00000002 | The image represents a composite R2R file resulting from a combined compilation of a larger number of input MSIL assemblies. -| READYTORUN_FLAG_EMBEDDED_MSIL | 0x00000004 | Input MSIL is embedded in the R2R image. -| READYTORUN_FLAG_COMPONENT | 0x00000008 | This is a component assembly of a composite R2R image +| Flag | Value | Description +|:-------------------------------------------|-----------:|:----------- +| READYTORUN_FLAG_PLATFORM_NEUTRAL_SOURCE | 0x00000001 | Set if the original IL image was platform neutral. The platform neutrality is part of assembly name. This flag can be used to reconstruct the full original assembly name. +| READYTORUN_FLAG_COMPOSITE | 0x00000002 | The image represents a composite R2R file resulting from a combined compilation of a larger number of input MSIL assemblies. +| READYTORUN_FLAG_PARTIAL | 0x00000004 | +| READYTORUN_FLAG_NONSHARED_PINVOKE_STUBS | 0x00000008 | PInvoke stubs compiled into image are non-shareable (no secret parameter) +| READYTORUN_FLAG_EMBEDDED_MSIL | 0x00000010 | Input MSIL is embedded in the R2R image. +| READYTORUN_FLAG_COMPONENT | 0x00000020 | This is a component assembly of a composite R2R image +| READYTORUN_FLAG_MULTIMODULE_VERSION_BUBBLE | 0x00000040 | This R2R module has multiple modules within its version bubble (For versions before version 6.2, all modules are assumed to possibly have this characteristic) ## READYTORUN_SECTION @@ -173,6 +177,7 @@ The following section types are defined and described later in this document: | OwnerCompositeExecutable | 116 | Image (added in V4.1) | PgoInstrumentationData | 117 | Image (added in V5.2) | ManifestAssemblyMvids | 118 | Image (added in V5.3) +| CrossModuleInlineInfo | 119 | Image (added in V6.2) ## ReadyToRunSectionType.CompilerIdentifier @@ -203,13 +208,19 @@ struct READYTORUN_IMPORT_SECTION | ReadyToRunImportSectionFlags | Value | Description |:---------------------------------------|-------:|:----------- -| READYTORUN_IMPORT_SECTION_FLAGS_EAGER | 0x0001 | Set if the slots in the section have to be initialized at image load time. It is used to avoid lazy initialization when it cannot be done or when it would have undesirable reliability or performance effects (unexpected failure or GC trigger points, overhead of lazy initialization). +| ReadyToRunImportSectionFlags::None | 0x0000 | None +| ReadyToRunImportSectionFlags::Eager | 0x0001 | Set if the slots in the section have to be initialized at image load time. It is used to avoid lazy initialization when it cannot be done or when it would have undesirable reliability or performance effects (unexpected failure or GC trigger points, overhead of lazy initialization). +| ReadyToRunImportSectionFlags::PCode | 0x0004 | Section contains pointers to code + ### READYTORUN_IMPORT_SECTIONS::Type -| ReadyToRunImportSectionType | Value | Description -|:---------------------------------------|-------:|:----------- -| READYTORUN_IMPORT_SECTION_TYPE_UNKNOWN | 0 | The type of slots in this section is unspecified. +| ReadyToRunImportSectionType | Value | Description +|:--------------------------------------------|-------:|:----------- +| ReadyToRunImportSectionType::Unknown | 0 | The type of slots in this section is unspecified. +| ReadyToRunImportSectionType::StubDispatch | 2 | The type of slots in this section rely on stubs for dispatch. +| ReadyToRunImportSectionType::StringHandle | 3 | The type of slots in this section hold strings +| ReadyToRunImportSectionType::ILBodyFixups | 7 | The type of slots in this section represent cross module IL bodies *Future*: The section type can be used to group slots of the same type together. For example, all virtual stub dispatch slots may be grouped together to simplify resetting of virtual stub dispatch cells into their @@ -264,6 +275,8 @@ fixup kind, the rest of the signature varies based on the fixup kind. | READYTORUN_FIXUP_Verify_TypeLayout | 0x32 | Generate a runtime check to ensure that the field offset matches between compile and runtime. Unlike CheckFieldOffset, this will generate a runtime exception on failure instead of silently dropping the method | READYTORUN_FIXUP_Check_VirtualFunctionOverride | 0x33 | Generate a runtime check to ensure that virtual function resolution has equivalent behavior at runtime as at compile time. If not equivalent, code will not be used. See [Virtual override signatures](virtual-override-signatures) for details of the signature used. | READYTORUN_FIXUP_Verify_VirtualFunctionOverride | 0x33 | Generate a runtime check to ensure that virtual function resolution has equivalent behavior at runtime as at compile time. If not equivalent, generate runtime failure. See [Virtual override signatures](virtual-override-signatures) for details of the signature used. +| READYTORUN_FIXUP_Check_IL_Body | 0x35 | Check to see if an IL method is defined the same at runtime as at compile time. A failed match will cause code not to be used. See[IL Body signatures](il-body-signatures) for details. +| READYTORUN_FIXUP_Verify_IL_Body | 0x36 | Verify an IL body is defined the same at compile time and runtime. A failed match will cause a hard runtime failure. See[IL Body signatures](il-body-signatures) for details. | READYTORUN_FIXUP_ModuleOverride | 0x80 | When or-ed to the fixup ID, the fixup byte in the signature is followed by an encoded uint with assemblyref index, either within the MSIL metadata of the master context module for the signature or within the manifest metadata R2R header table (used in cases inlining brings in references to assemblies not seen in the input MSIL). #### Method Signatures @@ -304,6 +317,9 @@ ECMA 335 does not have a natural encoding for describing an overriden method. Th | READYTORUN_VIRTUAL_OVERRIDE_None | 0x00 | No flags are set | READYTORUN_VIRTUAL_OVERRIDE_VirtualFunctionOverriden | 0x01 | If set, then the virtual function has an implementation, which is encoded in the optional method implementation signature. +#### IL Body signatures + +ECMA 335 does not define a format that can represent the exact implementation of a method by itself. This signature holds all of the IL of the method, the EH table, the locals table, and each token (other than type references) in those tables is replaced with an index into a local stream of signatures. Those signatures are simply verbatim copies of the needed metadata to describe MemberRefs, TypeSpecs, MethodSpecs, StandaloneSignatures and strings. All of that is bundled into a large byte array. In addition, a series of TypeSignatures follows which allow the type references to be resolved, as well as a methodreference to the uninstantiated method. Assuming all of this matches with the data that is present at runtime, the fixup is considered to be satisfied. See ReadyToRunStandaloneMetadata.cs for the exact details of the format. ### READYTORUN_IMPORT_SECTIONS::AuxiliaryData @@ -495,7 +511,7 @@ properly look up methods stored in this section in the composite R2R case. **TODO**: document profile data encoding -## ReadyToRunSectionType.ManifestMetadata (v2.3+) +## ReadyToRunSectionType.ManifestMetadata (v2.3+ with changes for v6.2+) Manifest metadata is an [ECMA-335] metadata blob containing extra reference assemblies within the version bubble introduced by inlining on top of assembly references stored in the input MSIL. @@ -504,12 +520,16 @@ translate module override indices in signatures to the actual reference modules the `READYTORUN_FIXUP_ModuleOverride` bit flag on the signature fixup byte or the `ELEMENT_TYPE_MODULE_ZAPSIG` COR element type). -**Note:** It doesn't make sense to store references to assemblies external to the version bubble -in the manifest metadata as there's no guarantee that their metadata token values remain -constant; thus we cannot encode signatures relative to them. +**Note:** It doesn't make sense to use references to assemblies external to the version bubble +in the manifest metadata via the `READYTORUN_FIXUP_ModuleOverride` or `ELEMENT_TYPE_MODULE_ZAPSIG` concept +as there's no guarantee that their metadata token values remain constant; thus we cannot encode signatures relative to them. +However, as of R2R version 6.2, the native manifest metadata may contain tokens to be further resolved to actual +implementation assemblies. The module override index translation algorithm is as follows (**ILAR** = *the number of `AssemblyRef` rows in the input MSIL*): +For R2R version 6.1 and below + | Module override index (*i*) | Reference assembly |:----------------------------|:------------------ | *i* = 0 | Global context - assembly containing the signature @@ -518,6 +538,16 @@ The module override index translation algorithm is as follows (**ILAR** = *the n **Note:** This means that the entry corresponding to *i* = **ILAR** + 1 is actually undefined as it corresponds to the `NULL` entry (ROWID #0) in the manifest metadata AssemblyRef table. The first meaningful index into the manifest metadata, *i* = **ILAR** + 2, corresponding to ROWID #1, is historically filled in by Crossgen with the input assembly info but this shouldn't be depended upon, in fact the input assembly is useless in the manifest metadata as the module override to it can be encoded by using the special index 0. +For R2R version 6.2 and above +| Module override index (*i*) | Reference assembly +|:----------------------------|:------------------ +| *i* = 0 | Global context - assembly containing the signature +| 1 <= *i* <= **ILAR** | *i* is the index into the MSIL `AssemblyRef` table +| *i* = **ILAR** + 1 | *i* is the index which refers to the Manifest metadata itself +| *i* > **ILAR** + 1 | *i* - **ILAR** - 2 is the zero-based index into the `AssemblyRef` table in the manifest metadata + +In addition, a ModuleRef within the module which refers to `System.Private.CoreLib` may be used to serve as the *ResolutionContext* of a *TypeRef* within the manifest metadata. This will always refer to the module which contains the `System.Object` type. + ## ReadyToRunSectionType.AttributePresence (v3.1+) **TODO**: document attribute presence encoding @@ -578,6 +608,33 @@ Number of assemblies stored in the manifest metadata is equal to the number of M MVID records are used at runtime to verify that the assemblies loaded match those referenced by the manifest metadata representing the versioning bubble. +## ReadyToRunSectionType.CrossModuleInlineInfo (v6.2+) +The inlining information section captures what methods got inlined into other methods. It consists of a single _Native Format Hashtable_ (described below). + +The entries in the hashtable are lists of inliners for each inlinee. One entry in the hashtable corresponds to one inlinee. The hashtable is hashed the version resilient hashcode of the uninstantiated methoddef inlinee. + +The entry of the hashtable is a counted sequence of compressed unsigned integers which begins with an InlineeIndex which has flags which then defines how a sequence of inliners may be read from the file. + +* InlineeIndex + * Index with 2 flags field in lowest 2 bits to define the inlinee + - If flags & 1 == 0 then index is a MethodDef RID, and if the module is a composite image, a module index of the method follows + - If flags & 1 == 1, then index is an index into the ILBody import section + - If flags & 2 == 0 then inliner list is: + - Inliner RID deltas - See definition below + - if flags & 2 == 2 then what follows is: + - count of delta encoded indices into the ILBody import section + - the sequence of delta encoded indices into the first import section with a type of READYTORUN_IMPORT_SECTION_TYPE_ILBODYFIXUPS + - Inliner RID deltas - See definition below + +* Inliner RID deltas (for multi-module version bubble images (specified by the module having the READYTORUN_FLAG_MULTIMODULE_VERSION_BUBBLE flag set) + - a sequence of inliner RID deltas with flag in the lowest bit + - if flag is set, the inliner RID is followed by a module ID + - otherwise the module is the same as the module of the inlinee method +* Inliner RID deltas (for single module version bubble images) + - a sequence of inliner RID deltas + +This section may be included in addition to a InliningInfo2 section. + # Native Format Native format is set of encoding patterns that allow persisting type system data in a binary format that is From ca1d623e91dc98b45a474d8e7a2abce0cf79a96d Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 13 Jun 2022 11:31:42 -0700 Subject: [PATCH 08/19] Fix issues found in testing --- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 9 ++++++--- src/coreclr/vm/clsload.cpp | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 402f8bde4deb2..f26ca46e6ed28 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -2461,7 +2461,8 @@ private void EncodeFieldBaseOffset(FieldDesc field, CORINFO_FIELD_INFO* pResult, if (_compilation.SymbolNodeFactory.VerifyTypeAndFieldLayout && !callerMethod.IsNonVersionable() && (pResult->offset <= FieldFixupSignature.MaxCheckableOffset)) { // ENCODE_CHECK_FIELD_OFFSET - AddPrecodeFixup(_compilation.SymbolNodeFactory.CheckFieldOffset(field)); + if (_compilation.CompilationModuleGroup.VersionsWithType(field.OwningType)) // Only verify versions with types + AddPrecodeFixup(_compilation.SymbolNodeFactory.CheckFieldOffset(field)); } // ENCODE_NONE } @@ -2470,7 +2471,8 @@ private void EncodeFieldBaseOffset(FieldDesc field, CORINFO_FIELD_INFO* pResult, if (_compilation.SymbolNodeFactory.VerifyTypeAndFieldLayout && !callerMethod.IsNonVersionable() && (pResult->offset <= FieldFixupSignature.MaxCheckableOffset)) { // ENCODE_CHECK_FIELD_OFFSET - AddPrecodeFixup(_compilation.SymbolNodeFactory.CheckFieldOffset(field)); + if (_compilation.CompilationModuleGroup.VersionsWithType(field.OwningType)) // Only verify versions with types + AddPrecodeFixup(_compilation.SymbolNodeFactory.CheckFieldOffset(field)); } // ENCODE_NONE } @@ -2481,7 +2483,8 @@ private void EncodeFieldBaseOffset(FieldDesc field, CORINFO_FIELD_INFO* pResult, if (_compilation.SymbolNodeFactory.VerifyTypeAndFieldLayout && !callerMethod.IsNonVersionable() && (pResult->offset <= FieldFixupSignature.MaxCheckableOffset)) { // ENCODE_CHECK_FIELD_OFFSET - AddPrecodeFixup(_compilation.SymbolNodeFactory.CheckFieldOffset(field)); + if (_compilation.CompilationModuleGroup.VersionsWithType(field.OwningType)) // Only verify versions with types + AddPrecodeFixup(_compilation.SymbolNodeFactory.CheckFieldOffset(field)); } // ENCODE_FIELD_BASE_OFFSET diff --git a/src/coreclr/vm/clsload.cpp b/src/coreclr/vm/clsload.cpp index 1d405ceeb7820..771e8b93e29b4 100644 --- a/src/coreclr/vm/clsload.cpp +++ b/src/coreclr/vm/clsload.cpp @@ -130,7 +130,7 @@ PTR_Module ClassLoader::ComputeLoaderModuleWorker( continue; CorElementType ety = classArg.GetSignatureCorElementType(); - if (CorTypeInfo::IsPrimitiveType(ety)) + if (CorTypeInfo::IsPrimitiveType_NoThrow(ety)) continue; Module* pModule = classArg.GetLoaderModule(); @@ -149,7 +149,7 @@ PTR_Module ClassLoader::ComputeLoaderModuleWorker( continue; CorElementType ety = methodArg.GetSignatureCorElementType(); - if (CorTypeInfo::IsPrimitiveType(ety)) + if (CorTypeInfo::IsPrimitiveType_NoThrow(ety)) continue; Module *pModule = methodArg.GetLoaderModule(); From 0a4247f3f2b7e320d74699467ccf38cb8f820bd0 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 13 Jun 2022 11:50:27 -0700 Subject: [PATCH 09/19] Fix markdownlint --- docs/design/coreclr/botr/readytorun-format.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/design/coreclr/botr/readytorun-format.md b/docs/design/coreclr/botr/readytorun-format.md index 4fe159720c476..d0cfa46dd0de7 100644 --- a/docs/design/coreclr/botr/readytorun-format.md +++ b/docs/design/coreclr/botr/readytorun-format.md @@ -544,7 +544,7 @@ For R2R version 6.2 and above | *i* = 0 | Global context - assembly containing the signature | 1 <= *i* <= **ILAR** | *i* is the index into the MSIL `AssemblyRef` table | *i* = **ILAR** + 1 | *i* is the index which refers to the Manifest metadata itself -| *i* > **ILAR** + 1 | *i* - **ILAR** - 2 is the zero-based index into the `AssemblyRef` table in the manifest metadata +| *i* > **ILAR** + 1 | *i* - **ILAR** - 2 is the zero-based index into the `AssemblyRef` table in the manifest metadata In addition, a ModuleRef within the module which refers to `System.Private.CoreLib` may be used to serve as the *ResolutionContext* of a *TypeRef* within the manifest metadata. This will always refer to the module which contains the `System.Object` type. From 55a8164444ee8975b170982f21de497f52760b2f Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 13 Jun 2022 16:27:19 -0700 Subject: [PATCH 10/19] Handle RawArrayData use in intrinsic in better way --- .../TypeSystem/IL/Stubs/MemoryMarshalIntrinsics.cs | 7 ++++++- .../ILCompiler.ReadyToRun/Compiler/R2RTypeExtensions.cs | 9 --------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/MemoryMarshalIntrinsics.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/MemoryMarshalIntrinsics.cs index fee30121c44f2..eb3f84c7d2998 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/MemoryMarshalIntrinsics.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/MemoryMarshalIntrinsics.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using ILCompiler; using Internal.TypeSystem; using Debug = System.Diagnostics.Debug; @@ -24,10 +25,14 @@ public static MethodIL EmitIL(MethodDesc method) if (methodName == "GetArrayDataReference") { + var rawArrayData = method.Context.SystemModule.GetKnownType("System.Runtime.CompilerServices", "RawArrayData"); + if (!rawArrayData.IsNonVersionable()) + return null; // This is only an intrinsic if we can prove that RawArrayData is known to be of fixed offset + ILEmitter emit = new ILEmitter(); ILCodeStream codeStream = emit.NewCodeStream(); codeStream.EmitLdArg(0); - codeStream.Emit(ILOpcode.ldflda, emit.NewToken(method.Context.SystemModule.GetKnownType("System.Runtime.CompilerServices", "RawArrayData").GetField("Data"))); + codeStream.Emit(ILOpcode.ldflda, emit.NewToken(rawArrayData.GetField("Data"))); codeStream.Emit(ILOpcode.ret); return emit.Link(method); } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/R2RTypeExtensions.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/R2RTypeExtensions.cs index 134d973f59a2c..e07dd42b8727b 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/R2RTypeExtensions.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/R2RTypeExtensions.cs @@ -38,15 +38,6 @@ public static bool IsNonVersionable(this MetadataType type) result = true; } - // REMOVE BEFORE CHECKIN!!!! - // As a temporary measure to ease testing of this work with an existing SDK, fix up the IsNonVersionable flag on these types in the compiler, without relying on an update to System.Private.CoreLib - // This work also contains a modification to System.Private.CoreLib to do exactly this, so this isn't actually wrong, but its slow, and in the wrong place. - if (!result && type.Module == type.Context.SystemModule && type.Namespace == "System.Runtime.CompilerServices" && (type.Name == "RawArrayData" || type.Name == "RawData")) - { - result = true; - } - // REMOVE BEFORE CHECKIN!!!! - return result; } From 64a63b9644040188709d50b4281c0cb4357fe4ab Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 13 Jun 2022 16:28:15 -0700 Subject: [PATCH 11/19] Adjust command line switches for new behavior to be disabled, and explicitly declared to be experimental --- .../DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs | 2 +- .../DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs | 9 +++++++++ .../Compiler/ReadyToRunCodegenCompilation.cs | 1 + .../Compiler/ReadyToRunCodegenCompilationBuilder.cs | 8 ++++++++ src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs | 7 +++++-- src/coreclr/tools/aot/crossgen2/Program.cs | 5 +++++ .../tools/aot/crossgen2/Properties/Resources.resx | 7 +++++-- 7 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs index a579c65fdd426..a0b9e801f2495 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TypeFixupSignature.cs @@ -161,7 +161,7 @@ public static void AddDependenciesForAsyncStateMachineBox(ref DependencyList dep // If adding a typehandle to the AsyncStateMachineBox, pre-compile the most commonly used methods. // As long as we haven't already reached compilation phase 7, which is an arbitrary number of phases of compilation chosen so that // simple examples of async will get compiled - if (type.GetTypeDefinition() == context.AsyncStateMachineBoxType && !type.IsGenericDefinition && factory.CompilationCurrentPhase <= 7) + if (factory.OptimizationFlags.OptimizeAsyncMethods && type.GetTypeDefinition() == context.AsyncStateMachineBoxType && !type.IsGenericDefinition && factory.CompilationCurrentPhase <= 7) { if (dependencies == null) dependencies = new DependencyList(); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs index c77ac2c254ec5..6f90af1de4ca0 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs @@ -44,6 +44,11 @@ public TValue GetOrAdd(TKey key) } } + public sealed class NodeFactoryOptimizationFlags + { + public bool OptimizeAsyncMethods; + } + // To make the code future compatible to the composite R2R story // do NOT attempt to pass and store _inputModule here public sealed class NodeFactory @@ -64,6 +69,8 @@ public sealed class NodeFactory public CompositeImageSettings CompositeImageSettings { get; set; } + public readonly NodeFactoryOptimizationFlags OptimizationFlags; + public ulong ImageBase; List _markedILBodyFixupSignatures = new List(); @@ -168,8 +175,10 @@ public NodeFactory( DebugDirectoryNode debugDirectoryNode, ResourceData win32Resources, ReadyToRunFlags flags, + NodeFactoryOptimizationFlags nodeFactoryOptimizationFlags, ulong imageBase) { + OptimizationFlags = nodeFactoryOptimizationFlags; TypeSystemContext = context; CompilationModuleGroup = compilationModuleGroup; ProfileDataManager = profileDataManager; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs index 994492813ee80..b091c9bf5f8b9 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs @@ -421,6 +421,7 @@ private void RewriteComponentFile(string inputFile, string outputFile, string ow debugDirectory, win32Resources: new Win32Resources.ResourceData(inputModule), flags, + _nodeFactory.OptimizationFlags, _nodeFactory.ImageBase); IComparer> comparer = new SortableDependencyNode.ObjectNodeComparer(CompilerComparer.Instance); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs index ba06e6b1c877f..f32ba652beca2 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilationBuilder.cs @@ -40,6 +40,7 @@ public sealed class ReadyToRunCodegenCompilationBuilder : CompilationBuilder private bool _verifyTypeAndFieldLayout; private CompositeImageSettings _compositeImageSettings; private ulong _imageBase; + private NodeFactoryOptimizationFlags _nodeFactoryOptimizationFlags = new NodeFactoryOptimizationFlags(); private string _jitPath; private string _outputFile; @@ -199,6 +200,12 @@ public ReadyToRunCodegenCompilationBuilder UseImageBase(ulong imageBase) return this; } + public ReadyToRunCodegenCompilationBuilder UseNodeFactoryOptimizationFlags(NodeFactoryOptimizationFlags flags) + { + _nodeFactoryOptimizationFlags = flags; + return this; + } + public override ICompilation ToCompilation() { // TODO: only copy COR headers for single-assembly build and for composite build with embedded MSIL @@ -242,6 +249,7 @@ public override ICompilation ToCompilation() debugDirectoryNode, win32Resources, flags, + _nodeFactoryOptimizationFlags, _imageBase ); diff --git a/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs b/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs index b771997a3b8d7..0043a18f1ddb7 100644 --- a/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs +++ b/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs @@ -32,6 +32,7 @@ internal class CommandLineOptions public bool OptimizeTime; public bool CrossModuleInlining; public bool CrossModuleGenericCompilation; + public bool AsyncMethodOptimization; public bool InputBubble; public bool CompileBubbleGenerics; public bool Verbose; @@ -93,8 +94,9 @@ public CommandLineOptions(string[] args) SingleMethodGenericArg = null; // These behaviors default to enabled - CrossModuleInlining = true; - CrossModuleGenericCompilation = true; + CrossModuleInlining = false; + CrossModuleGenericCompilation = false; + AsyncMethodOptimization = false; bool forceHelp = false; if (args.Length == 0) @@ -172,6 +174,7 @@ public CommandLineOptions(string[] args) syntax.DefineOption("opt-cross-module-inlining", ref CrossModuleInlining, SR.CrossModuleInlining); syntax.DefineOption("opt-cross-module-generic-compilation", ref CrossModuleGenericCompilation, SR.CrossModuleGenericCompilation); + syntax.DefineOption("opt-async-methods", ref AsyncMethodOptimization, SR.AsyncModuleOptimization); syntax.DefineOption("method-layout", ref MethodLayout, SR.MethodLayoutOption); syntax.DefineOption("file-layout", ref FileLayout, SR.FileLayoutOption); diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index a26368721337e..c3dbea10b462c 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -17,6 +17,7 @@ using Internal.TypeSystem.Ecma; using ILCompiler.Reflection.ReadyToRun; +using ILCompiler.DependencyAnalysis; namespace ILCompiler { @@ -736,6 +737,9 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru DependencyTrackingLevel trackingLevel = _commandLineOptions.DgmlLogFileName == null ? DependencyTrackingLevel.None : (_commandLineOptions.GenerateFullDgmlLog ? DependencyTrackingLevel.All : DependencyTrackingLevel.First); + NodeFactoryOptimizationFlags nodeFactoryFlags = new NodeFactoryOptimizationFlags(); + nodeFactoryFlags.OptimizeAsyncMethods = _commandLineOptions.AsyncMethodOptimization; + builder .UseMapFile(_commandLineOptions.Map) .UseMapCsvFile(_commandLineOptions.MapCsv) @@ -743,6 +747,7 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru .UsePerfMapFile(_commandLineOptions.PerfMap, _commandLineOptions.PerfMapPath, _commandLineOptions.PerfMapFormatVersion) .UseProfileFile(jsonProfile != null) .UseProfileData(profileDataManager) + .UseNodeFactoryOptimizationFlags(nodeFactoryFlags) .FileLayoutAlgorithms(_methodLayout, _fileLayout) .UseCompositeImageSettings(compositeImageSettings) .UseJitPath(_commandLineOptions.JitPath) diff --git a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx index 869b5abf3cf3c..e012d1ddbed9e 100644 --- a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx +++ b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx @@ -373,9 +373,12 @@ Hexademical value to set target PE-file ImageBase field - When compiling, compile generic code defined in other modules in a version resilient manner. Defaults to enabled. Specify --opt-cross-module-generic-compilation:false to disable. + EXPERIMENTAL: When compiling, compile generic code defined in other modules in a version resilient manner. Defaults to disabled. - When compiling, inline code from other modules in a version resilient manner. Defaults to enabled. Specify --opt-cross-module-inlining:false to disable. + EXPERIMENTAL: When compiling, inline code from other modules in a version resilient manner. Defaults to disabled. + + + EXPERIMENTAL: Attempt to compile as much async infrastructure as possible for async methods. Defaults to disabled. \ No newline at end of file From 3cbce96da7ccf4dfa0d1488306ad4070fd896f41 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Mon, 13 Jun 2022 19:11:11 -0700 Subject: [PATCH 12/19] Fix the build --- .../Common/TypeSystem/IL/Stubs/MemoryMarshalIntrinsics.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/MemoryMarshalIntrinsics.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/MemoryMarshalIntrinsics.cs index eb3f84c7d2998..de2c2712827c5 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/MemoryMarshalIntrinsics.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/MemoryMarshalIntrinsics.cs @@ -26,9 +26,10 @@ public static MethodIL EmitIL(MethodDesc method) if (methodName == "GetArrayDataReference") { var rawArrayData = method.Context.SystemModule.GetKnownType("System.Runtime.CompilerServices", "RawArrayData"); +#if READYTORUN if (!rawArrayData.IsNonVersionable()) return null; // This is only an intrinsic if we can prove that RawArrayData is known to be of fixed offset - +#endif ILEmitter emit = new ILEmitter(); ILCodeStream codeStream = emit.NewCodeStream(); codeStream.EmitLdArg(0); From c46fe1d2d1f77591a129200ef6da3f9368246eeb Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 14 Jun 2022 12:40:55 -0700 Subject: [PATCH 13/19] Fix break for R2R binaries without cross module inlining enabled --- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index f26ca46e6ed28..d7d5281875b6d 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -2772,7 +2772,8 @@ private void reportInliningDecision(CORINFO_METHOD_STRUCT_* inlinerHnd, CORINFO_ typicalMethod is EcmaMethod ecmaMethod && ecmaMethod.Module == typicalMethod.Context.SystemModule) // NonVersionable code in System.Collections.Immutable is causing problems and needs to be treated specially { - Debug.Assert(_compilation.CompilationModuleGroup.CrossModuleInlineable(typicalMethod)); + Debug.Assert(_compilation.CompilationModuleGroup.CrossModuleInlineable(typicalMethod) || + _compilation.CompilationModuleGroup.IsNonVersionableWithILTokensThatDoNotNeedTranslation(typicalMethod)); bool needsTokenTranslation = !_compilation.CompilationModuleGroup.IsNonVersionableWithILTokensThatDoNotNeedTranslation(typicalMethod); if (needsTokenTranslation) From 45fed2177a17cebe8cf506144be76c97f588aeb2 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 15 Jun 2022 15:56:37 -0700 Subject: [PATCH 14/19] Move shash to heap from global, just to see what it does --- src/coreclr/vm/readytorunstandalonemethodmetadata.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/coreclr/vm/readytorunstandalonemethodmetadata.cpp b/src/coreclr/vm/readytorunstandalonemethodmetadata.cpp index 1626504da5f56..e8e7157fdb9b3 100644 --- a/src/coreclr/vm/readytorunstandalonemethodmetadata.cpp +++ b/src/coreclr/vm/readytorunstandalonemethodmetadata.cpp @@ -635,7 +635,7 @@ class ReadyToRunStandaloneMethodMetadataHelper #ifndef DACCESS_COMPILE static CrstStatic s_csReadyToRunStandaloneMethodMetadata; -static MapSHash s_methodMetadata; +static MapSHash *s_methodMetadata = NULL; void GenerateReadyToRunStandaloneMethodMetadata(MethodDesc *pMD, ReadyToRunStandaloneMethodMetadata *pBlock) { @@ -659,6 +659,7 @@ void GenerateReadyToRunStandaloneMethodMetadata(MethodDesc *pMD, ReadyToRunStand void InitReadyToRunStandaloneMethodMetadata() { s_csReadyToRunStandaloneMethodMetadata.Init(CrstLeafLock); + s_methodMetadata = new MapSHash; } ReadyToRunStandaloneMethodMetadata* GetReadyToRunStandaloneMethodMetadata(MethodDesc *pMD) @@ -667,7 +668,7 @@ ReadyToRunStandaloneMethodMetadata* GetReadyToRunStandaloneMethodMetadata(Method { CrstHolder lock(&s_csReadyToRunStandaloneMethodMetadata); - if (s_methodMetadata.Lookup(pMD, &retVal)) + if (s_methodMetadata->Lookup(pMD, &retVal)) { return retVal; } @@ -678,12 +679,12 @@ ReadyToRunStandaloneMethodMetadata* GetReadyToRunStandaloneMethodMetadata(Method { CrstHolder lock(&s_csReadyToRunStandaloneMethodMetadata); - if (s_methodMetadata.Lookup(pMD, &retVal)) + if (s_methodMetadata->Lookup(pMD, &retVal)) { return retVal; } - s_methodMetadata.Add(pMD, newMethodBlock); + s_methodMetadata->Add(pMD, newMethodBlock); retVal = newMethodBlock.Extract(); } return retVal; From 93a041f7800fcf64b8f128b8d94b10fe6f5269a0 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Wed, 15 Jun 2022 18:41:46 -0700 Subject: [PATCH 15/19] Address Michal's feedback --- .../tools/Common/TypeSystem/IL/ILProvider.cs | 6 - .../Common/TypeSystem/IL/ILTokenReplacer.cs | 198 ++++++------------ .../TypeSystem/IL/NativeAotILProvider.cs | 11 +- .../IL/Stubs/InterlockedIntrinsics.cs | 9 +- .../DependencyGraphTests.cs | 2 +- .../Compiler/ReadyToRunCodegenCompilation.cs | 6 +- .../IL/ReadyToRunILProvider.cs | 8 + .../Compiler/RyuJitCompilationBuilder.cs | 3 +- .../ILCompiler.TypeSystem.csproj | 3 + src/coreclr/tools/aot/ILCompiler/Program.cs | 2 +- .../TestCasesRunner/ILCompilerDriver.cs | 2 +- 11 files changed, 85 insertions(+), 165 deletions(-) diff --git a/src/coreclr/tools/Common/TypeSystem/IL/ILProvider.cs b/src/coreclr/tools/Common/TypeSystem/IL/ILProvider.cs index 788efaf21131d..eef7d154b42e2 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/ILProvider.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/ILProvider.cs @@ -11,12 +11,6 @@ namespace Internal.IL /// public abstract class ILProvider { - private int _version = 0; - protected void IncrementVersion() - { - _version++; - } - public int Version => _version; public abstract MethodIL GetMethodIL(MethodDesc method); } } diff --git a/src/coreclr/tools/Common/TypeSystem/IL/ILTokenReplacer.cs b/src/coreclr/tools/Common/TypeSystem/IL/ILTokenReplacer.cs index ff1265923e421..cc6dca8ef20d7 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/ILTokenReplacer.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/ILTokenReplacer.cs @@ -3,6 +3,7 @@ using System; using System.Buffers.Binary; +using System.Diagnostics; namespace Internal.IL { @@ -10,154 +11,73 @@ public class ILTokenReplacer { public static void Replace(byte[] ilStream, Func tokenReplaceFunc) { - int currentOffset = 0; - for (; currentOffset < ilStream.Length;) + ILReader ilReader = new ILReader(ilStream); + while (ilReader.HasNext) { - ILOpcode opCode = (ILOpcode)ReadILByte(); - again: + int offsetBefore = ilReader.Offset; - switch (opCode) + ILOpcode opcode = ilReader.ReadILOpcode(); + ilReader.Skip(opcode); + int offsetAfter = ilReader.Offset; + + if (ILOpcodeHasToken(opcode)) { - case ILOpcode.ldarg_s: - case ILOpcode.ldarga_s: - case ILOpcode.starg_s: - case ILOpcode.ldloc_s: - case ILOpcode.ldloca_s: - case ILOpcode.stloc_s: - case ILOpcode.ldc_i4_s: - case ILOpcode.unaligned: - case ILOpcode.no: - SkipIL(1); - break; - case ILOpcode.ldarg: - case ILOpcode.ldarga: - case ILOpcode.starg: - case ILOpcode.ldloc: - case ILOpcode.ldloca: - case ILOpcode.stloc: - SkipIL(2); - break; - case ILOpcode.ldc_i4: - case ILOpcode.ldc_r4: - SkipIL(4); - break; - case ILOpcode.ldc_i8: - case ILOpcode.ldc_r8: - SkipIL(8); - break; - case ILOpcode.jmp: - case ILOpcode.call: - case ILOpcode.calli: - case ILOpcode.callvirt: - case ILOpcode.cpobj: - case ILOpcode.ldobj: - case ILOpcode.ldstr: - case ILOpcode.newobj: - case ILOpcode.castclass: - case ILOpcode.isinst: - case ILOpcode.unbox: - case ILOpcode.ldfld: - case ILOpcode.ldflda: - case ILOpcode.stfld: - case ILOpcode.ldsfld: - case ILOpcode.ldsflda: - case ILOpcode.stsfld: - case ILOpcode.stobj: - case ILOpcode.box: - case ILOpcode.newarr: - case ILOpcode.ldelema: - case ILOpcode.ldelem: - case ILOpcode.stelem: - case ILOpcode.unbox_any: - case ILOpcode.refanyval: - case ILOpcode.mkrefany: - case ILOpcode.ldtoken: - case ILOpcode.ldftn: - case ILOpcode.ldvirtftn: - case ILOpcode.initobj: - case ILOpcode.constrained: - case ILOpcode.sizeof_: - ReplaceToken(); - break; - case ILOpcode.prefix1: - opCode = (ILOpcode)(0x100 + ReadILByte()); - goto again; - case ILOpcode.br_s: - case ILOpcode.leave_s: - case ILOpcode.brfalse_s: - case ILOpcode.brtrue_s: - case ILOpcode.beq_s: - case ILOpcode.bge_s: - case ILOpcode.bgt_s: - case ILOpcode.ble_s: - case ILOpcode.blt_s: - case ILOpcode.bne_un_s: - case ILOpcode.bge_un_s: - case ILOpcode.bgt_un_s: - case ILOpcode.ble_un_s: - case ILOpcode.blt_un_s: - SkipIL(1); - break; - case ILOpcode.br: - case ILOpcode.leave: - case ILOpcode.brfalse: - case ILOpcode.brtrue: - case ILOpcode.beq: - case ILOpcode.bge: - case ILOpcode.bgt: - case ILOpcode.ble: - case ILOpcode.blt: - case ILOpcode.bne_un: - case ILOpcode.bge_un: - case ILOpcode.bgt_un: - case ILOpcode.ble_un: - case ILOpcode.blt_un: - SkipIL(4); - break; - case ILOpcode.switch_: - { - uint count = ReadILUInt32(); - SkipIL(checked((int)(count * 4))); - } - break; - default: - continue; - } + Debug.Assert((offsetAfter - offsetBefore) == 5 || (offsetAfter - offsetBefore) == 6); - } + var tokenSpan = ilStream.AsSpan(checked(offsetAfter - 4), 4); - byte ReadILByte() - { - byte b = ilStream[currentOffset++]; - return b; - } - void SkipIL(int countToSkip) - { - currentOffset += countToSkip; - } - UInt32 ReadILUInt32() - { - var result = (UInt32)BinaryPrimitives.ReadInt32LittleEndian(ilStream.AsSpan(currentOffset, 4)); - currentOffset += 4; - return result; + // Replace token in IL stream with a new token provided by the tokenReplaceFunc + // + // This is used by the StandaloneMethodMetadata logic to create method local tokens + // and by the IL provider used for cross module inlining to create tokens which are + // stable and contained within the R2R module instead of being in a module separated + // by a version boundary. + int token = BinaryPrimitives.ReadInt32LittleEndian(tokenSpan); + var alternateToken = tokenReplaceFunc(token); + BinaryPrimitives.WriteInt32LittleEndian(tokenSpan, alternateToken); + } } + } - void ReplaceToken() + private static bool ILOpcodeHasToken(ILOpcode opcode) + { + switch (opcode) { - var tokenSpan = ilStream.AsSpan(currentOffset, 4); - - // Replace token in IL stream with a new token provided by the tokenReplaceFunc - // - // This is by the StandaloneMethodMetadata logic to create method local tokens - // and by the IL provider used for cross module inlining to create tokens which are - // stable and contained within the R2R module instead of being in a module separated - // by a version boundary. - int token = BinaryPrimitives.ReadInt32LittleEndian(tokenSpan); - var alternateToken = tokenReplaceFunc(token); - BinaryPrimitives.WriteInt32LittleEndian(tokenSpan, alternateToken); - - currentOffset += 4; + case ILOpcode.jmp: + case ILOpcode.call: + case ILOpcode.calli: + case ILOpcode.callvirt: + case ILOpcode.cpobj: + case ILOpcode.ldobj: + case ILOpcode.ldstr: + case ILOpcode.newobj: + case ILOpcode.castclass: + case ILOpcode.isinst: + case ILOpcode.unbox: + case ILOpcode.ldfld: + case ILOpcode.ldflda: + case ILOpcode.stfld: + case ILOpcode.ldsfld: + case ILOpcode.ldsflda: + case ILOpcode.stsfld: + case ILOpcode.stobj: + case ILOpcode.box: + case ILOpcode.newarr: + case ILOpcode.ldelema: + case ILOpcode.ldelem: + case ILOpcode.stelem: + case ILOpcode.unbox_any: + case ILOpcode.refanyval: + case ILOpcode.mkrefany: + case ILOpcode.ldtoken: + case ILOpcode.ldftn: + case ILOpcode.ldvirtftn: + case ILOpcode.initobj: + case ILOpcode.constrained: + case ILOpcode.sizeof_: + return true; } + return false; } } } diff --git a/src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs b/src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs index 6b84ac244c4ed..fb64e67c9dc61 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/NativeAotILProvider.cs @@ -3,8 +3,6 @@ using System; -using ILCompiler; - using Internal.TypeSystem; using Internal.TypeSystem.Ecma; @@ -16,13 +14,6 @@ namespace Internal.IL { public sealed class NativeAotILProvider : ILProvider { - private readonly CompilationModuleGroup _compilationGroup; - - public NativeAotILProvider(CompilationModuleGroup group) - { - _compilationGroup = group; - } - private MethodIL TryGetRuntimeImplementedMethodIL(MethodDesc method) { // Provides method bodies for runtime implemented methods. It can return null for @@ -58,7 +49,7 @@ private MethodIL TryGetIntrinsicMethodIL(MethodDesc method) case "Interlocked": { if (owningType.Namespace == "System.Threading") - return InterlockedIntrinsics.EmitIL(_compilationGroup, method); + return InterlockedIntrinsics.EmitIL(method); } break; case "Unsafe": diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/InterlockedIntrinsics.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/InterlockedIntrinsics.cs index a4f7e3953d916..25523f68b100f 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/InterlockedIntrinsics.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/InterlockedIntrinsics.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using ILCompiler; using Internal.TypeSystem; using Debug = System.Diagnostics.Debug; @@ -14,17 +13,23 @@ namespace Internal.IL.Stubs /// public static class InterlockedIntrinsics { - public static MethodIL EmitIL(CompilationModuleGroup compilationModuleGroup, MethodDesc method) + public static MethodIL EmitIL( +#if READYTORUN + ILCompiler.CompilationModuleGroup compilationModuleGroup, +#endif // READYTORUN + MethodDesc method) { Debug.Assert(((MetadataType)method.OwningType).Name == "Interlocked"); if (method.HasInstantiation && method.Name == "CompareExchange") { +#if READYTORUN // Check to see if the tokens needed to describe the CompareExchange are naturally present within // the compilation. The current implementation of stable tokens used by cross module inlining is not // compatible with rewriting the IL of a compiler generated intrinsic. Fortunately, it turns out // that the managed implementation of this intrinsic is correct, just a few more IL instructions. if (compilationModuleGroup.ContainsType(method.OwningType)) +#endif // READYTORUN { TypeDesc objectType = method.Context.GetWellKnownType(WellKnownType.Object); MethodDesc compareExchangeObject = method.OwningType.GetKnownMethod("CompareExchange", diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs index 7b8604f30c967..dc1bf6c9cd0c4 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs @@ -66,7 +66,7 @@ public void TestDependencyGraphInvariants(EcmaMethod method) var context = (CompilerTypeSystemContext)method.Context; CompilationModuleGroup compilationGroup = new SingleFileCompilationModuleGroup(); - NativeAotILProvider ilProvider = new NativeAotILProvider(compilationGroup); + NativeAotILProvider ilProvider = new NativeAotILProvider(); UsageBasedMetadataManager metadataManager = new UsageBasedMetadataManager(compilationGroup, context, new FullyBlockedMetadataBlockingPolicy(), new FullyBlockedManifestResourceBlockingPolicy(), diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs index b091c9bf5f8b9..6287d6ee42c44 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCodegenCompilation.cs @@ -65,7 +65,7 @@ protected Compilation( foreach (var rootProvider in compilationRoots) rootProvider.AddCompilationRoots(rootingService); - _methodILCache = new ILCache(ilProvider, NodeFactory.CompilationModuleGroup); + _methodILCache = new ILCache((ReadyToRunILProvider)ilProvider, NodeFactory.CompilationModuleGroup); } public abstract void Dispose(); @@ -136,11 +136,11 @@ public bool IsModuleInstrumented(ModuleDesc module) public sealed class ILCache : LockFreeReaderHashtable { - public ILProvider ILProvider { get; } + public ReadyToRunILProvider ILProvider { get; } public int ExpectedILProviderVersion { get; } private readonly CompilationModuleGroup _compilationModuleGroup; - public ILCache(ILProvider provider, CompilationModuleGroup compilationModuleGroup) + public ILCache(ReadyToRunILProvider provider, CompilationModuleGroup compilationModuleGroup) { ILProvider = provider; ExpectedILProviderVersion = provider.Version; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs index 6e21e02233db6..fbb187afc9f27 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/IL/ReadyToRunILProvider.cs @@ -27,6 +27,7 @@ public sealed class ReadyToRunILProvider : ILProvider { private CompilationModuleGroup _compilationModuleGroup; private MutableModule _manifestMutableModule; + private int _version = 0; public ReadyToRunILProvider(CompilationModuleGroup compilationModuleGroup) { @@ -38,6 +39,13 @@ public void InitManifestMutableModule(MutableModule module) _manifestMutableModule = module; } + void IncrementVersion() + { + _version++; + } + + public int Version => _version; + private MethodIL TryGetIntrinsicMethodILForActivator(MethodDesc method) { if (method.Instantiation.Length == 1 diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilationBuilder.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilationBuilder.cs index 1c8bd1f9a8a5e..013de73cec569 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilationBuilder.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilationBuilder.cs @@ -20,7 +20,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; + private ILProvider _ilProvider = new NativeAotILProvider(); private ProfileDataManager _profileDataManager; private string _jitPath; @@ -28,7 +28,6 @@ public RyuJitCompilationBuilder(CompilerTypeSystemContext context, CompilationMo : base(context, group, new NativeAotNameMangler(context.Target.IsWindows ? (NodeMangler)new WindowsNodeMangler() : (NodeMangler)new UnixNodeMangler(), false)) { - _ilProvider = new NativeAotILProvider(group); } public RyuJitCompilationBuilder UseProfileData(IEnumerable mibcFiles) diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj b/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj index 1ce63756c98b7..7298d8b5b19f1 100644 --- a/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj @@ -491,6 +491,9 @@ IL\ILTokenReplacer.cs + + IL\ILReader.cs + IL\Stubs\ILEmitter.cs diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index 3c7b3c579a84e..925fd5c9a90d8 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -724,7 +724,7 @@ static string ILLinkify(string rootedAssembly) PInvokeILEmitterConfiguration pinvokePolicy = new ConfigurablePInvokePolicy(typeSystemContext.Target, _directPInvokes, _directPInvokeLists); - ILProvider ilProvider = new NativeAotILProvider(compilationGroup); + ILProvider ilProvider = new NativeAotILProvider(); List> featureSwitches = new List>(); foreach (var switchPair in _featureSwitches) diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs index cbf459febb260..a0eff369ee98a 100644 --- a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs +++ b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs @@ -52,7 +52,7 @@ public void Trim (ILCompilerOptions options, ILogWriter logWriter) compilationRoots.Add (new MainMethodRootProvider (entrypointModule, CreateInitializerList (typeSystemContext, options))); - ILProvider ilProvider = new NativeAotILProvider (compilationGroup); + ILProvider ilProvider = new NativeAotILProvider (); ilProvider = new FeatureSwitchManager (ilProvider, options.FeatureSwitches); From 7d6e658537fb811c2f7d4dc2da2e82d6c8b5ea18 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Thu, 16 Jun 2022 18:23:18 -0700 Subject: [PATCH 16/19] Address feedback final --- docs/design/coreclr/botr/readytorun-format.md | 14 +++++++------- .../tools/Common/JitInterface/CorInfoImpl.cs | 2 ++ .../TypeSystem/Mutable/MutableModule.Sorting.cs | 2 +- src/coreclr/vm/ceeload.cpp | 2 ++ src/coreclr/vm/jitinterface.cpp | 10 ++++++++-- src/coreclr/vm/prestub.cpp | 2 +- .../vm/readytorunstandalonemethodmetadata.cpp | 6 +++--- 7 files changed, 24 insertions(+), 14 deletions(-) diff --git a/docs/design/coreclr/botr/readytorun-format.md b/docs/design/coreclr/botr/readytorun-format.md index d0cfa46dd0de7..e6b39634e2157 100644 --- a/docs/design/coreclr/botr/readytorun-format.md +++ b/docs/design/coreclr/botr/readytorun-format.md @@ -611,22 +611,22 @@ manifest metadata representing the versioning bubble. ## ReadyToRunSectionType.CrossModuleInlineInfo (v6.2+) The inlining information section captures what methods got inlined into other methods. It consists of a single _Native Format Hashtable_ (described below). -The entries in the hashtable are lists of inliners for each inlinee. One entry in the hashtable corresponds to one inlinee. The hashtable is hashed the version resilient hashcode of the uninstantiated methoddef inlinee. +The entries in the hashtable are lists of inliners for each inlinee. One entry in the hashtable corresponds to one inlinee. The hashtable is hashed with the version resilient hashcode of the uninstantiated methoddef inlinee. -The entry of the hashtable is a counted sequence of compressed unsigned integers which begins with an InlineeIndex which has flags which then defines how a sequence of inliners may be read from the file. +The entry of the hashtable is a counted sequence of compressed unsigned integers which begins with an InlineeIndex which combines a 30 bit index with 2 bits of flags which how the sequence of inliners shall be parsed and what table is to be indexed into to find the inlinee. * InlineeIndex * Index with 2 flags field in lowest 2 bits to define the inlinee - - If flags & 1 == 0 then index is a MethodDef RID, and if the module is a composite image, a module index of the method follows - - If flags & 1 == 1, then index is an index into the ILBody import section - - If flags & 2 == 0 then inliner list is: + - If (flags & 1) == 0 then index is a MethodDef RID, and if the module is a composite image, a module index of the method follows + - If (flags & 1) == 1, then index is an index into the ILBody import section + - If (flags & 2) == 0 then inliner list is: - Inliner RID deltas - See definition below - - if flags & 2 == 2 then what follows is: + - if (flags & 2) == 2 then what follows is: - count of delta encoded indices into the ILBody import section - the sequence of delta encoded indices into the first import section with a type of READYTORUN_IMPORT_SECTION_TYPE_ILBODYFIXUPS - Inliner RID deltas - See definition below -* Inliner RID deltas (for multi-module version bubble images (specified by the module having the READYTORUN_FLAG_MULTIMODULE_VERSION_BUBBLE flag set) +* Inliner RID deltas (for multi-module version bubble images specified by the module having the READYTORUN_FLAG_MULTIMODULE_VERSION_BUBBLE flag set) - a sequence of inliner RID deltas with flag in the lowest bit - if flag is set, the inliner RID is followed by a module ID - otherwise the module is the same as the module of the inlinee method diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 2a0f1b2adeff1..27da124adc586 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -1222,11 +1222,13 @@ private void getMethodSig(CORINFO_METHOD_STRUCT_* ftn, CORINFO_SIG_INFO* sig, CO private bool getMethodInfo(CORINFO_METHOD_STRUCT_* ftn, CORINFO_METHOD_INFO* info) { MethodDesc method = HandleToObject(ftn); +#if READYTORUN // Add an early CanInline check to see if referring to the IL of the target methods is // permitted from within this MethodBeingCompiled, the full CanInline check will be performed // later. if (!_compilation.CanInline(MethodBeingCompiled, method)) return false; +#endif MethodIL methodIL = _compilation.GetMethodIL(method); return Get_CORINFO_METHOD_INFO(method, methodIL, info); } diff --git a/src/coreclr/tools/Common/TypeSystem/Mutable/MutableModule.Sorting.cs b/src/coreclr/tools/Common/TypeSystem/Mutable/MutableModule.Sorting.cs index 30128c0942dd1..e6676ff7d0a2d 100644 --- a/src/coreclr/tools/Common/TypeSystem/Mutable/MutableModule.Sorting.cs +++ b/src/coreclr/tools/Common/TypeSystem/Mutable/MutableModule.Sorting.cs @@ -10,7 +10,7 @@ namespace Internal.TypeSystem.Ecma { partial class MutableModule { - // This isn't deterministic, but it is functional. + // This isn't deterministic, but it is functional. At this time, since only 1 MutableModule is contained in a build, it will be deterministic as it will always return 0. static int s_globalIndex = 0; int _index = Interlocked.Increment(ref s_globalIndex); diff --git a/src/coreclr/vm/ceeload.cpp b/src/coreclr/vm/ceeload.cpp index 6b613bac8d493..6549f1a44699e 100644 --- a/src/coreclr/vm/ceeload.cpp +++ b/src/coreclr/vm/ceeload.cpp @@ -2991,6 +2991,8 @@ ModuleBase::GetAssemblyIfLoaded( AppDomain * pAppDomainExamine = AppDomain::GetCurrentDomain(); _ASSERTE(!"Handle remote load scenarios"); #ifdef ENABLE_LATER + // This (or something like it) will need to be enabled when cross module inlining supports modules other that System.Private.CoreLib. + DomainAssembly * pCurAssemblyInExamineDomain = GetAssembly()->GetDomainAssembly(); if (pCurAssemblyInExamineDomain == NULL) { diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index e9f8b21f05bb8..cee3f0d26248a 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -14042,14 +14042,20 @@ BOOL LoadDynamicInfoEntry(Module *currentModule, pMDCompare->GetFullMethodInfo(methodName); void* compileTimeTypes = types.OpenRawBuffer(); + int runtimeMethodDataSize = pMethodMetadata != NULL ? (int)pMethodMetadata->cByteData : 0; + void* runtimeMethodData = pMethodMetadata != NULL ? (void*)pMethodMetadata->pByteData : (void*)NULL; + + int runtimeTypeCount = pMethodMetadata != NULL ? (int)pMethodMetadata->cTypes : 0; + void* runtimeTypeData = pMethodMetadata != NULL ? (void*)pMethodMetadata->pTypes : (void*)NULL; + SString fatalErrorString; fatalErrorString.Printf(W("VERIFY_IL_BODY Method '%s' type '%s' does not match IL body expected DEBUGINFO MethodData {%d} {%p} RuntimeMethodData {%d} {%p} Types {%d} {%p} RuntimeTypes {%d} {%p}"), methodName.GetUnicode(), GetFullyQualifiedNameForClassW(pMDCompare->GetMethodTable()), (int)dwBlobSize, pBlobStart, - pMethodMetadata != NULL ? (int)pMethodMetadata->cByteData : 0, pMethodMetadata != NULL ? (void*)pMethodMetadata->pByteData : (void*)NULL, + runtimeMethodDataSize, runtimeMethodData, (int)cTypes, compileTimeTypes, - pMethodMetadata != NULL ? (int)pMethodMetadata->cTypes : 0, pMethodMetadata != NULL ? (void*)pMethodMetadata->pTypes : (void*)NULL + runtimeTypeCount, runtimeTypeData ); #ifdef _DEBUG diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 05cf529e10993..05aa741f10a24 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -514,7 +514,7 @@ PCODE MethodDesc::GetPrecompiledR2RCode(PrepareCodeConfig* pConfig) { Module* pDefiningModule = GetModule(); // Lookup in the defining module of the generic (which is where in inputbubble scenarios - // that the methods may be placed. + // the methods may be placed. if (pDefiningModule != pModule && pDefiningModule->IsReadyToRun()) { pCode = pDefiningModule->GetReadyToRunInfo()->GetEntryPoint(this, pConfig, TRUE /* fFixups */); diff --git a/src/coreclr/vm/readytorunstandalonemethodmetadata.cpp b/src/coreclr/vm/readytorunstandalonemethodmetadata.cpp index e8e7157fdb9b3..2933223e62c68 100644 --- a/src/coreclr/vm/readytorunstandalonemethodmetadata.cpp +++ b/src/coreclr/vm/readytorunstandalonemethodmetadata.cpp @@ -292,7 +292,7 @@ class ReadyToRunStandaloneMethodMetadataHelper StandaloneSigTranslator sigTranslator(&memberRefSigParse, &blob, this); sigTranslator.ParseMemberRefSignature(); ULONG strLen = (ULONG)strlen(name); // Cast to ULONG is safe, as the data is held in a PE file - blob.AppendData((ULONG)strlen(name)); + blob.AppendData(strLen); blob.AppendBlob((const PVOID)name, strLen); blob.AppendData(MemberRefParentCodedIndex(GetAlternateToken(memberRefParent))); break; @@ -311,7 +311,7 @@ class ReadyToRunStandaloneMethodMetadataHelper StandaloneSigTranslator sigTranslator(&methodDefSigParse, &blob, this); sigTranslator.ParseMethodSignature(); ULONG strLen = (ULONG)strlen(name); // Cast to ULONG is safe, as the data is held in a PE file - blob.AppendData((ULONG)strlen(name)); + blob.AppendData(strLen); blob.AppendBlob((const PVOID)name, strLen); blob.AppendData(MemberRefParentCodedIndex(GetAlternateToken(methodDefParent))); break; @@ -331,7 +331,7 @@ class ReadyToRunStandaloneMethodMetadataHelper StandaloneSigTranslator sigTranslator(&fieldDefSigParse, &blob, this); sigTranslator.ParseFieldSignature(); ULONG strLen = (ULONG)strlen(name); // Cast to ULONG is safe, as the data is held in a PE file - blob.AppendData((ULONG)strlen(name)); + blob.AppendData(strLen); blob.AppendBlob((const PVOID)name, strLen); blob.AppendData(MemberRefParentCodedIndex(GetAlternateToken(fieldDefParent))); break; From 7ab730176338e9d657f555563158e2b6281fd698 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 17 Jun 2022 11:59:03 -0700 Subject: [PATCH 17/19] Move MutableModule around per Michal's request --- .../ILCompiler.ReadyToRun.csproj | 4 ++++ .../TypeSystem/Mutable/MutableModule.Sorting.cs | 0 .../TypeSystem/Mutable/MutableModule.Symbols.cs | 0 .../TypeSystem/Mutable/MutableModule.cs | 0 .../ILCompiler.TypeSystem.csproj | 12 ------------ 5 files changed, 4 insertions(+), 12 deletions(-) rename src/coreclr/tools/{Common => aot/ILCompiler.ReadyToRun}/TypeSystem/Mutable/MutableModule.Sorting.cs (100%) rename src/coreclr/tools/{Common => aot/ILCompiler.ReadyToRun}/TypeSystem/Mutable/MutableModule.Symbols.cs (100%) rename src/coreclr/tools/{Common => aot/ILCompiler.ReadyToRun}/TypeSystem/Mutable/MutableModule.cs (100%) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index 19880ec91c9db..1d2e28afc787f 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -47,6 +47,7 @@ + @@ -234,6 +235,9 @@ + + + diff --git a/src/coreclr/tools/Common/TypeSystem/Mutable/MutableModule.Sorting.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/Mutable/MutableModule.Sorting.cs similarity index 100% rename from src/coreclr/tools/Common/TypeSystem/Mutable/MutableModule.Sorting.cs rename to src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/Mutable/MutableModule.Sorting.cs diff --git a/src/coreclr/tools/Common/TypeSystem/Mutable/MutableModule.Symbols.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/Mutable/MutableModule.Symbols.cs similarity index 100% rename from src/coreclr/tools/Common/TypeSystem/Mutable/MutableModule.Symbols.cs rename to src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/Mutable/MutableModule.Symbols.cs diff --git a/src/coreclr/tools/Common/TypeSystem/Mutable/MutableModule.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/Mutable/MutableModule.cs similarity index 100% rename from src/coreclr/tools/Common/TypeSystem/Mutable/MutableModule.cs rename to src/coreclr/tools/aot/ILCompiler.ReadyToRun/TypeSystem/Mutable/MutableModule.cs diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj b/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj index 7298d8b5b19f1..14ebc69bea1b9 100644 --- a/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem/ILCompiler.TypeSystem.csproj @@ -509,18 +509,6 @@ IL\Stubs\PInvokeTargetNativeMethod.Sorting.cs - - MetadataEmitter\TypeSystemMetadataEmitter.cs - - - Mutable\MutableModule.Sorting.cs - - - Mutable\MutableModule.Symbols.cs - - - Mutable\MutableModule.cs - TypeSystem\CodeGen\FieldDesc.Serialization.cs From 2e1541bc36ad44c244c37f34b89eb4f32d39cf17 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 17 Jun 2022 15:25:07 -0700 Subject: [PATCH 18/19] Fix error where the load in LookupMap can actually be torn in the presence of multithreading. --- src/coreclr/vm/ceeload.inl | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/coreclr/vm/ceeload.inl b/src/coreclr/vm/ceeload.inl index c3710bda73641..8cd5189791b8c 100644 --- a/src/coreclr/vm/ceeload.inl +++ b/src/coreclr/vm/ceeload.inl @@ -17,7 +17,11 @@ TYPE LookupMap::GetValueAt(PTR_TADDR pValue, TADDR* pFlags, TADDR supporte { WRAPPER_NO_CONTRACT; SUPPORTS_DAC; +#ifndef DACCESS_COMPILE + TYPE value = dac_cast(VolatileLoadWithoutBarrier(pValue)); // LookupMap's hold pointers, so we can use a data dependency instead of an explicit barrier here. +#else TYPE value = dac_cast(*pValue); +#endif if (pFlags) *pFlags = dac_cast(value) & supportedFlags; @@ -35,7 +39,7 @@ void LookupMap::SetValueAt(PTR_TADDR pValue, TYPE value, TADDR flags) value = dac_cast((dac_cast(value) | flags)); - *(dac_cast(pValue)) = value; + VolatileStore(pValue, dac_cast(value)); } // @@ -47,7 +51,7 @@ SIZE_T LookupMap::GetValueAt(PTR_TADDR pValue, TADDR* pFlags, TADDR supp { WRAPPER_NO_CONTRACT; - TADDR value = *pValue; + TADDR value = VolatileLoadWithoutBarrier(pValue); // LookupMap's hold pointers, so we can use a data dependency instead of an explicit barrier here. if (pFlags) *pFlags = value & supportedFlags; @@ -60,7 +64,7 @@ inline void LookupMap::SetValueAt(PTR_TADDR pValue, SIZE_T value, TADDR flags) { WRAPPER_NO_CONTRACT; - *pValue = value | flags; + VolatileStore(pValue, value | flags); } #endif // DACCESS_COMPILE From 6af05fd0588158581928f518c3e65b88da24ae2d Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Sat, 18 Jun 2022 09:13:49 -0700 Subject: [PATCH 19/19] Update R2R version bump to 6.3 as this missed checking in ahead of some work by Jakob --- docs/design/coreclr/botr/readytorun-format.md | 12 ++++++------ src/coreclr/inc/readytorun.h | 2 +- .../tools/Common/Internal/Runtime/ModuleHeaders.cs | 2 +- .../ReadyToRunReader.cs | 2 +- src/coreclr/vm/readytoruninfo.cpp | 2 +- src/coreclr/vm/zapsig.cpp | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/design/coreclr/botr/readytorun-format.md b/docs/design/coreclr/botr/readytorun-format.md index e6b39634e2157..80c4eebe53b77 100644 --- a/docs/design/coreclr/botr/readytorun-format.md +++ b/docs/design/coreclr/botr/readytorun-format.md @@ -7,7 +7,7 @@ Revisions: * 4.1 - [Tomas Rylek](https://github.com/trylek) - 2020 * 5.3 - [Tomas Rylek](https://github.com/trylek) - 2021 * 5.4 - [David Wrighton](https://github.com/davidwrighton) - 2021 -* 6.2 - [David Wrighton](https://github.com/davidwrighton) - 2022 +* 6.3 - [David Wrighton](https://github.com/davidwrighton) - 2022 # Introduction @@ -177,7 +177,7 @@ The following section types are defined and described later in this document: | OwnerCompositeExecutable | 116 | Image (added in V4.1) | PgoInstrumentationData | 117 | Image (added in V5.2) | ManifestAssemblyMvids | 118 | Image (added in V5.3) -| CrossModuleInlineInfo | 119 | Image (added in V6.2) +| CrossModuleInlineInfo | 119 | Image (added in V6.3) ## ReadyToRunSectionType.CompilerIdentifier @@ -511,7 +511,7 @@ properly look up methods stored in this section in the composite R2R case. **TODO**: document profile data encoding -## ReadyToRunSectionType.ManifestMetadata (v2.3+ with changes for v6.2+) +## ReadyToRunSectionType.ManifestMetadata (v2.3+ with changes for v6.3+) Manifest metadata is an [ECMA-335] metadata blob containing extra reference assemblies within the version bubble introduced by inlining on top of assembly references stored in the input MSIL. @@ -528,7 +528,7 @@ implementation assemblies. The module override index translation algorithm is as follows (**ILAR** = *the number of `AssemblyRef` rows in the input MSIL*): -For R2R version 6.1 and below +For R2R version 6.2 and below | Module override index (*i*) | Reference assembly |:----------------------------|:------------------ @@ -538,7 +538,7 @@ For R2R version 6.1 and below **Note:** This means that the entry corresponding to *i* = **ILAR** + 1 is actually undefined as it corresponds to the `NULL` entry (ROWID #0) in the manifest metadata AssemblyRef table. The first meaningful index into the manifest metadata, *i* = **ILAR** + 2, corresponding to ROWID #1, is historically filled in by Crossgen with the input assembly info but this shouldn't be depended upon, in fact the input assembly is useless in the manifest metadata as the module override to it can be encoded by using the special index 0. -For R2R version 6.2 and above +For R2R version 6.3 and above | Module override index (*i*) | Reference assembly |:----------------------------|:------------------ | *i* = 0 | Global context - assembly containing the signature @@ -608,7 +608,7 @@ Number of assemblies stored in the manifest metadata is equal to the number of M MVID records are used at runtime to verify that the assemblies loaded match those referenced by the manifest metadata representing the versioning bubble. -## ReadyToRunSectionType.CrossModuleInlineInfo (v6.2+) +## ReadyToRunSectionType.CrossModuleInlineInfo (v6.3+) The inlining information section captures what methods got inlined into other methods. It consists of a single _Native Format Hashtable_ (described below). The entries in the hashtable are lists of inliners for each inlinee. One entry in the hashtable corresponds to one inlinee. The hashtable is hashed with the version resilient hashcode of the uninstantiated methoddef inlinee. diff --git a/src/coreclr/inc/readytorun.h b/src/coreclr/inc/readytorun.h index 5f41856bd1217..f67d6c7edbed5 100644 --- a/src/coreclr/inc/readytorun.h +++ b/src/coreclr/inc/readytorun.h @@ -16,7 +16,7 @@ // Keep these in sync with src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs #define READYTORUN_MAJOR_VERSION 0x0006 -#define READYTORUN_MINOR_VERSION 0x0002 +#define READYTORUN_MINOR_VERSION 0x0003 #define MINIMUM_READYTORUN_MAJOR_VERSION 0x006 diff --git a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs index 1aa5f3f904c65..e1510a85f13e3 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/ModuleHeaders.cs @@ -15,7 +15,7 @@ internal struct ReadyToRunHeaderConstants public const uint Signature = 0x00525452; // 'RTR' public const ushort CurrentMajorVersion = 6; - public const ushort CurrentMinorVersion = 2; + public const ushort CurrentMinorVersion = 3; } #pragma warning disable 0169 diff --git a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs index bcf9a4a256529..893f8e3f1f51b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs +++ b/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun/ReadyToRunReader.cs @@ -1428,7 +1428,7 @@ private AssemblyReferenceHandle GetAssemblyAtIndex(int refAsmIndex, out Metadata else { int index = refAsmIndex - assemblyRefCount; - if (ReadyToRunHeader.MajorVersion > 6 || (ReadyToRunHeader.MajorVersion == 6 && ReadyToRunHeader.MinorVersion >= 2)) + if (ReadyToRunHeader.MajorVersion > 6 || (ReadyToRunHeader.MajorVersion == 6 && ReadyToRunHeader.MinorVersion >= 3)) { if (index == 1) { diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index 9212b88deb4e4..7f39d24d3b359 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -725,7 +725,7 @@ ReadyToRunInfo::ReadyToRunInfo(Module * pModule, LoaderAllocator* pLoaderAllocat m_entryPointToMethodDescMap.Init(TRUE, &lock); } - if (IsImageVersionAtLeast(6, 2)) + if (IsImageVersionAtLeast(6, 3)) { IMAGE_DATA_DIRECTORY* pCrossModuleInlineTrackingInfoDir = m_pComposite->FindSection(ReadyToRunSectionType::CrossModuleInlineInfo); if (pCrossModuleInlineTrackingInfoDir != NULL) diff --git a/src/coreclr/vm/zapsig.cpp b/src/coreclr/vm/zapsig.cpp index 4c7a0f7010d55..139994d3acb95 100644 --- a/src/coreclr/vm/zapsig.cpp +++ b/src/coreclr/vm/zapsig.cpp @@ -592,7 +592,7 @@ ModuleBase *ZapSig::DecodeModuleFromIndex(Module *fromModule, { index -= assemblyRefMax; - if (fromModule->GetReadyToRunInfo()->IsImageVersionAtLeast(6,2)) + if (fromModule->GetReadyToRunInfo()->IsImageVersionAtLeast(6,3)) { if (index == 1) { @@ -657,7 +657,7 @@ ModuleBase *ZapSig::DecodeModuleFromIndexIfLoaded(Module *fromModule, { index -= assemblyRefMax; - if (fromModule->GetReadyToRunInfo()->IsImageVersionAtLeast(6,2)) + if (fromModule->GetReadyToRunInfo()->IsImageVersionAtLeast(6,3)) { if (index == 1) {