diff --git a/docs/design/datacontracts/CodeVersions.md b/docs/design/datacontracts/CodeVersions.md index 2a3e186739df22..4968a750113605 100644 --- a/docs/design/datacontracts/CodeVersions.md +++ b/docs/design/datacontracts/CodeVersions.md @@ -193,9 +193,9 @@ IEnumerable ICodeVersions.GetILCodeVersions(TargetPointer m // CodeVersionManager::GetILCodeVersions GetModuleAndMethodDesc(methodDesc, out TargetPointer module, out uint methodDefToken); - ModuleHandle moduleHandle = _target.Contracts.Loader.GetModuleHandleFromModulePtr(module); - TargetPointer ilCodeVersionTable = _target.Contracts.Loader.GetLookupTables(moduleHandle).MethodDefToILCodeVersioningState; - TargetPointer ilVersionStateAddress = _target.Contracts.Loader.GetModuleLookupMapElement(ilCodeVersionTable, methodDefToken, out var _); + ModuleHandle moduleHandle = target.Contracts.Loader.GetModuleHandleFromModulePtr(module); + TargetPointer ilCodeVersionTable = target.Contracts.Loader.GetLookupTables(moduleHandle).MethodDefToILCodeVersioningState; + TargetPointer ilVersionStateAddress = target.Contracts.Loader.GetModuleLookupMapElement(ilCodeVersionTable, methodDefToken, out var _); // always add the synthetic version yield return new ILCodeVersionHandle(module, methodDefToken, TargetPointer.Null); @@ -203,11 +203,11 @@ IEnumerable ICodeVersions.GetILCodeVersions(TargetPointer m // if explicit versions exist, iterate linked list and return them if (ilVersionStateAddress != TargetPointer.Null) { - Data.ILCodeVersioningState ilState = _target.ProcessedData.GetOrAdd(ilVersionStateAddress); + Data.ILCodeVersioningState ilState = target.ProcessedData.GetOrAdd(ilVersionStateAddress); TargetPointer nodePointer = ilState.FirstVersionNode; while (nodePointer != TargetPointer.Null) { - Data.ILCodeVersionNode current = _target.ProcessedData.GetOrAdd(nodePointer); + Data.ILCodeVersionNode current = target.ProcessedData.GetOrAdd(nodePointer); yield return new ILCodeVersionHandle(TargetPointer.Null, 0, nodePointer); nodePointer = current.Next; } @@ -220,7 +220,7 @@ IEnumerable ICodeVersions.GetILCodeVersions(TargetPointer m ```csharp NativeCodeVersionHandle ICodeVersions.GetNativeCodeVersionForIP(TargetCodePointer ip) { - Contracts.IExecutionManager executionManager = _target.Contracts.ExecutionManager; + Contracts.IExecutionManager executionManager = target.Contracts.ExecutionManager; EECodeInfoHandle? info = executionManager.GetEECodeInfoHandle(ip); if (!info.HasValue) { @@ -231,7 +231,7 @@ NativeCodeVersionHandle ICodeVersions.GetNativeCodeVersionForIP(TargetCodePointe { return NativeCodeVersionHandle.Invalid; } - IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + IRuntimeTypeSystem rts = target.Contracts.RuntimeTypeSystem; MethodDescHandle md = rts.GetMethodDescHandle(methodDescAddress); if (!rts.IsVersionable(md)) { @@ -267,13 +267,13 @@ IEnumerable FindNativeCodeVersionNodes(IRuntimeTypeSyst if (versioningStateAddr == TargetPointer.Null) yield break; - Data.MethodDescVersioningState versioningState = _target.ProcessedData.GetOrAdd(versioningStateAddr); + Data.MethodDescVersioningState versioningState = target.ProcessedData.GetOrAdd(versioningStateAddr); // LinkedList stage of NativeCodeVersion::Next, heavily inlined TargetPointer currentAddress = versioningState.NativeCodeVersionNode; while (currentAddress != TargetPointer.Null) { - Data.NativeCodeVersionNode current = _target.ProcessedData.GetOrAdd(currentAddress); + Data.NativeCodeVersionNode current = target.ProcessedData.GetOrAdd(currentAddress); if (predicate(current)) { yield return NativeCodeVersionHandle.OfExplicit(currentAddress); @@ -300,7 +300,7 @@ IEnumerable ICodeVersions.GetNativeCodeVersions(TargetP } // Iterate through versioning state nodes and return the active one, matching any IL code version - Contracts.IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + Contracts.IRuntimeTypeSystem rts = target.Contracts.RuntimeTypeSystem; MethodDescHandle md = rts.GetMethodDescHandle(methodDesc); TargetNUInt ilVersionId = GetId(ilCodeVersionHandle); IEnumerable nativeCodeVersions = FindNativeCodeVersionNodes( @@ -329,7 +329,7 @@ public virtual NativeCodeVersionHandle GetActiveNativeCodeVersionForILCodeVersio ```csharp bool ICodeVersions.CodeVersionManagerSupportsMethod(TargetPointer methodDescAddress) { - IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + IRuntimeTypeSystem rts = target.Contracts.RuntimeTypeSystem; MethodDescHandle md = rts.GetMethodDescHandle(methodDescAddress); if (rts.IsDynamicMethod(md)) return false; @@ -338,7 +338,7 @@ bool ICodeVersions.CodeVersionManagerSupportsMethod(TargetPointer methodDescAddr TargetPointer mtAddr = rts.GetMethodTable(md); TypeHandle mt = rts.GetTypeHandle(mtAddr); TargetPointer modAddr = rts.GetModule(mt); - ILoader loader = _target.Contracts.Loader; + ILoader loader = target.Contracts.Loader; ModuleHandle mod = loader.GetModuleHandleFromModulePtr(modAddr); ModuleFlags modFlags = loader.GetFlags(mod); if (modFlags.HasFlag(ModuleFlags.EditAndContinue)) @@ -372,7 +372,7 @@ TargetPointer ICodeVersions.GetIL(ILCodeVersionHandle ilCodeVersionHandle, Targe { // Synthetic ILCodeVersion, get the IL from the module and method def - ILoader loader = _target.Contracts.Loader; + ILoader loader = target.Contracts.Loader; ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(ilCodeVersionHandle.Module); ilAddress = loader.GetILHeader(moduleHandle, ilCodeVersionHandle.MethodDefinition); } diff --git a/docs/design/datacontracts/Loader.md b/docs/design/datacontracts/Loader.md index 77ef18e9e8cff6..d11518f72951e7 100644 --- a/docs/design/datacontracts/Loader.md +++ b/docs/design/datacontracts/Loader.md @@ -15,9 +15,15 @@ readonly struct ModuleHandle [Flags] enum ModuleFlags { - Tenured = 0x00000001, // Set once we know for sure the Module will not be freed until the appdomain itself exits - EditAndContinue = 0x00000008, // Edit and Continue is enabled for this module - ReflectionEmit = 0x00000040, // Reflection.Emit was used to create this module + Tenured = 0x1, // Set once we know for sure the Module will not be freed until the appdomain itself exits + EditAndContinue = 0x8, // Edit and Continue is enabled for this module + + ReflectionEmit = 0x40, // Reflection.Emit was used to create this module + ProfilerDisableOptimizations = 0x80, + + DebuggerUserOverridePriv = 0x400, + DebuggerAllowJitOptsPriv = 0x800, + DebuggerTrackJitInfoPriv = 0x1000 } [Flags] @@ -60,6 +66,7 @@ string GetAppDomainFriendlyName(); TargetPointer GetModule(ModuleHandle handle); TargetPointer GetAssembly(ModuleHandle handle); TargetPointer GetPEAssembly(ModuleHandle handle); +bool GetReadyToRunImageInfo(ModuleHandle handle, out TargetPointer r2rImageBase, out TargetPointer r2rImageEnd); bool TryGetLoadedImageContents(ModuleHandle handle, out TargetPointer baseAddress, out uint size, out uint imageFlags); TargetPointer ILoader.GetILAddr(TargetPointer peAssemblyPtr, int rva); bool TryGetSymbolStream(ModuleHandle handle, out TargetPointer buffer, out uint size); @@ -95,6 +102,8 @@ TargetPointer GetObjectHandle(TargetPointer loaderAllocatorPointer); | `Module` | `Base` | Pointer to start of PE file in memory | | `Module` | `Flags` | Assembly of the Module | | `Module` | `LoaderAllocator` | LoaderAllocator of the Module | +| `Module` | `ReadyToRunInfo` | Pointer to ready to run info | +| `Module` | `ReadyToRunImage` | Pointer to PE image layout structure | | `Module` | `Path` | Path of the Module (UTF-16, null-terminated) | | `Module` | `FileName` | File name of the Module (UTF-16, null-terminated) | | `Module` | `GrowableSymbolStream` | Pointer to the in memory symbol stream | @@ -335,6 +344,20 @@ TargetPointer GetPEAssembly(ModuleHandle handle) return target.ReadPointer(handle.Address + /* Module::PEAssembly offset */); } +bool GetReadyToRunImageInfo(ModuleHandle handle, out TargetPointer r2rImageBase, out TargetPointer r2rImageEnd) +{ + r2rImageBase = TargetPointer.Null; + r2rImageEnd = TargetPointer.Null; + TargetPointer readyToRunInfo = target.ReadPointer(handle.Address + /* Module::ReadyToRunInfo offset */); + if (readyToRunInfo == TargetPointer.Null) + return false; + + TargetPointer pEImageLayout = target.ReadPointer(handle.Address + /*Module::ReadyToRunImage offset */); + uint r2rImageBase = target.Read(pEImageLayout + /* pEImageLayout::Base offset */); + uint r2rImageEnd = r2rImageBase + target.Read(pEImageLayout + /*PEImageLayout::Size offset */); + return true; +} + bool TryGetLoadedImageContents(ModuleHandle handle, out TargetPointer baseAddress, out uint size, out uint imageFlags) { baseAddress = TargetPointer.Null; diff --git a/docs/design/datacontracts/ReJIT.md b/docs/design/datacontracts/ReJIT.md index 106360c6d97685..c32a10d5d11147 100644 --- a/docs/design/datacontracts/ReJIT.md +++ b/docs/design/datacontracts/ReJIT.md @@ -12,6 +12,21 @@ public enum RejitState } ``` +```csharp +public enum OptimizationTierEnum +{ + Unknown = 0, + MinOptJitted = 1, + Optimized = 2, + QuickJitted = 3, + OptimizedTier1 = 4, + ReadyToRun = 5, + OptimizedTier1OSR = 6, + QuickJittedInstrumented = 7, + OptimizedTier1Instrumented = 8, +} +``` + ```csharp bool IsEnabled(); @@ -19,7 +34,8 @@ RejitState GetRejitState(ILCodeVersionHandle codeVersionHandle); TargetNUInt GetRejitId(ILCodeVersionHandle codeVersionHandle); -IEnumerable GetRejitIds(TargetPointer methodDesc) +IEnumerable GetRejitIds(TargetPointer methodDesc); +IEnumerable<(TargetPointer, TargetPointer, OptimizationTierEnum)> GetTieredVersions(TargetPointer methodDesc, int rejitId, int cNativeCodeAddrs); ``` ## Version 1 @@ -30,16 +46,21 @@ Data descriptors used: | ProfControlBlock | GlobalEventMask | an `ICorProfiler` `COR_PRF_MONITOR` value | | ILCodeVersionNode | VersionId | `ILCodeVersion` ReJIT ID | ILCodeVersionNode | RejitState | a `RejitFlags` value | +| MethodDesc | CodeData | Pointer to CodeData (additional info about native code) | +| MethodDescCodeData | OptimizationTier | Optimization tier of default native code version | Global variables used: | Global Name | Type | Purpose | | --- | --- | --- | |ProfilerControlBlock | TargetPointer | pointer to the `ProfControlBlock` | +| `EEConfig` | TargetPointer | Pointer to the global EEConfig | Contracts used: | Contract Name | | --- | | CodeVersions | +| RuntimeTypeSystem | +| Loader | ```csharp // see src/coreclr/inc/corprof.idl @@ -64,6 +85,19 @@ public enum RejitFlags : uint kSuppressParams = 0x80000000 } +private enum NativeOptimizationTier : uint +{ + OptimizationTier0 = 0, + OptimizationTier1 = 1, + OptimizationTier1OSR = 2, + OptimizationTierOptimized = 3, + OptimizationTier0Instrumented = 4, + OptimizationTier1Instrumented = 5, + OptimizationTierUnknown = 0xFFFFFFFF +}; +``` +```csharp + bool IsEnabled() { TargetPointer address = target.ReadGlobalPointer("ProfilerControlBlock"); @@ -124,4 +158,81 @@ IEnumerable GetRejitIds(TargetPointer methodDesc) } } } + +IEnumerable<(TargetPointer, TargetPointer, OptimizationTierEnum)> GetTieredVersions(TargetPointer methodDesc, int rejitId, int cNativeCodeAddrs) +{ + ICodeVersions codeVersionsContract = target.Contracts.CodeVersions; + IReJIT rejitContract = this; + + ILCodeVersionHandle ilCodeVersion = codeVersionsContract.GetILCodeVersions(methodDesc) + MethodDescHandle mdh = rts.GetMethodDescHandle(methodDesc); + .FirstOrDefault(ilcode => rejitContract.GetRejitId(ilcode).Value == (ulong)rejitId, + ILCodeVersionHandle.Invalid); + + if (!ilCodeVersion.IsValid) + throw new ArgumentException(); + // Iterate through versioning state nodes and return the active one, matching any IL code version + IRuntimeTypeSystem rts = target.Contracts.RuntimeTypeSystem; + ILoader loader = target.Contracts.Loader; + ModuleHandle moduleHandle = // get the module handle from the method desc using rts + + bool isReadyToRun = loader.GetReadyToRunImageInfo(moduleHandle, out TargetPointer r2rImageBase, out TargetPointer r2rImageEnd); + bool isEligibleForTieredCompilation = rts.IsEligibleForTieredCompilation(mdh); + int count = 0; + foreach (NativeCodeVersionHandle nativeCodeVersionHandle in codeVersionsContract.GetNativeCodeVersions(methodDesc, ilCodeVersion)) + { + TargetPointer nativeCode = codeVersionsContract.GetNativeCode(nativeCodeVersionHandle).AsTargetPointer; + TargetPointer nativeCodeAddr = nativeCode; + TargetPointer nativeCodeVersionNodePtr = nativeCodeVersionHandle.IsExplicit ? AsNode(nativeCodeVersionHandle).Address : TargetPointer.Null; + OptimizationTierEnum optimizationTier; + if (r2rImageBase <= nativeCode && nativeCode < r2rImageEnd) + { + optimizationTier = OptimizationTierEnum.ReadyToRun; + } + + else if (isEligibleForTieredCompilation) + { + NativeOptimizationTier optTier; + if (!nativeCodeVersionHandle.IsExplicit) + optTier = GetInitialOptimizationTier(mdh); + else + { + optTier = (NativeOptimizationTier)target.ReadPointer(/* native code version address + NativeCodeVersionNode::OptimizationTier offset */); + } + optimizationTier = GetOptimizationTier(optTier); + } + else if (rts.IsJitOptimizationDisabled(mdh)) + { + optimizationTier = OptimizationTierEnum.MinOptJitted; + } + else + { + optimizationTier = OptimizationTierEnum.Optimized; + } + count++; + yield return (nativeCodeAddr, nativeCodeVersionNodePtr, optimizationTier); + } +} + +private NativeOptimizationTier GetInitialOptimizationTier(TargetPointer mdPointer) +{ + // validation of the method desc + MethodDescHandle _ = target.Contracts.RuntimeTypeSystem.GetMethodDescHandle(mdPointer); + TargetPointer codeData = target.ReadPointer(mdPointer + /* MethodDesc::CodeData offset */); + return (NativeOptimizationTier)target.Read(codeData + /* MethodDescCodeData::OptimizationTier offset */); +} + +private static OptimizationTierEnum GetOptimizationTier(NativeOptimizationTier nativeOptimizationTier) +{ + return nativeOptimizationTier switch + { + NativeOptimizationTier.OptimizationTier0 => OptimizationTierEnum.QuickJitted, + NativeOptimizationTier.OptimizationTier1 => OptimizationTierEnum.OptimizedTier1, + NativeOptimizationTier.OptimizationTier1OSR => OptimizationTierEnum.OptimizedTier1OSR, + NativeOptimizationTier.OptimizationTierOptimized => OptimizationTierEnum.Optimized, + NativeOptimizationTier.OptimizationTier0Instrumented => OptimizationTierEnum.QuickJittedInstrumented, + NativeOptimizationTier.OptimizationTier1Instrumented => OptimizationTierEnum.OptimizedTier1Instrumented, + _ => OptimizationTierEnum.Unknown, + }; +} ``` diff --git a/docs/design/datacontracts/RuntimeTypeSystem.md b/docs/design/datacontracts/RuntimeTypeSystem.md index f0b5a49382b5f5..4913be2e861d14 100644 --- a/docs/design/datacontracts/RuntimeTypeSystem.md +++ b/docs/design/datacontracts/RuntimeTypeSystem.md @@ -164,6 +164,12 @@ partial interface IRuntimeTypeSystem : IContract // Return true if a MethodDesc supports mulitiple code versions public virtual bool IsVersionable(MethodDescHandle methodDesc); + // Returns true if the MethodDesc is eligible for tiered compilation + public virtual bool IsEligibleForTieredCompilation(MethodDescHandle methodDesc); + + // Returns true if JIT optimization has been disabled for this MethodDesc + bool IsJitOptimizationDisabled(MethodDescHandle methodDesc); + // Return a pointer to the IL versioning state of the MethodDesc public virtual TargetPointer GetMethodDescVersioningState(MethodDescHandle methodDesc); @@ -797,6 +803,8 @@ The version 1 `MethodDesc` APIs depend on the following globals: | `MethodDescAlignment` | `MethodDescChunk` trailing data is allocated in multiples of this constant. The size (in bytes) of each `MethodDesc` (or subclass) instance is a multiple of this constant. | | `MethodDescTokenRemainderBitCount` | Number of bits in the token remainder in `MethodDesc` | | `MethodDescSizeTable` | A pointer to the MethodDesc size table. The MethodDesc flags are used as an offset into this table to lookup the MethodDesc size. | +| `CORDebuggerControlFlags` | A pointer to debugger control flags, that the debugger can set to control optimizations, etc. | +| `EEConfig` | A pointer to the global EEConfig that contains global settings, etc. | In the runtime a `MethodDesc` implicitly belongs to a single `MethodDescChunk` and some common data is shared between method descriptors that belong to the same chunk. A single method table @@ -805,6 +813,9 @@ will typically have multiple chunks. There are subkinds of MethodDescs at runti We depend on the following data descriptors: | Data Descriptor Name | Field | Meaning | | --- | --- | --- | +jitMinOpts +| `EEConfig` | `JitMinOpts` | Flag for minimal JIT optimization | +| `EEConfig` | `Debuggable` | Flag for debuggable code generation | | `MethodDesc` | `ChunkIndex` | Offset of this `MethodDesc` relative to the end of its containing `MethodDescChunk` - in multiples of `MethodDescAlignment` | | `MethodDesc` | `Slot` | The method's slot | | `MethodDesc` | `Flags` | The method's flags | @@ -839,6 +850,7 @@ The contract depends on the following other contracts | ReJIT | | ExecutionManager | | PrecodeStubs | +| EcmaMetadata | And the following enumeration definitions @@ -965,7 +977,7 @@ internal struct MethodDesc } } - public bool IsEligibleForTieredCompilation => HasFlags(MethodDescFlags3.IsEligibleForTieredCompilation); + public bool IsEligibleForTieredCompilation => HasFlags(MethodDescFlags3.IsEligibleForTieredCompilation) && && _target.ReadGlobal("FeatureTieredCompilation") != 0; // non-vtable slot, native code slot and MethodImpl slots are stored after the MethodDesc itself, packed tightly // in the order: [non-vtable; methhod impl; native code]. @@ -1266,6 +1278,42 @@ Determining if a method supports multiple code versions: } ``` +Determining if a method has JIT optimizations disabled: +```csharp +private bool ModuleJitOptsDisabled(MethodDesc md) +{ + TypeHandle mt = GetTypeHandle(md.MethodTable); + TargetPointer modulePtr = GetModule(mt); + ModuleHandle moduleHandle = _target.Contracts.Loader.GetModuleHandleFromModulePtr(modulePtr); + ModuleFlags flags = _target.Contracts.Loader.GetFlags(moduleHandle); + + TargetPointer corDebuggerControlFlagsPtr = _target.ReadGlobalPointer("CORDebuggerControlFlags"); + uint corDebuggerControlFlags = _target.Read(corDebuggerControlFlagsPtr); + + bool allowJitOpts = // complex condition checking on the flags to see if JIT opts have been disabled module-wide by the debugger + if (!allowJitOpts) + return true; + // have the jit opts been disabled by the profiler? + return (flags & ModuleFlags.ProfilerDisableOptimizations) != 0; +} +private bool IsJitOptimizationDisabledForAllMethodsInChunk(MethodDesc md) +{ + TargetPointer eeConfigPtr = _target.ReadGlobalPointer("EEConfig"); + bool jitMinOpts = // read from EEConfig + bool debuggable = // read from EEConfig + return jitMinOpts || debuggable || ModuleJitOptsDisabled(md); +} + +bool IRuntimeTypeSystem.IsJitOptimizationDisabled(MethodDescHandle methodDesc) +{ + MethodDesc md = _methodDescs[methodDesc.Address]; + // we have cached the boolean isJitOptimizationDisabledForSpecificMethod on each method desc + // if there is no metadata then optimization cannot have been disabled + // we then read the metadata, if the NoOptimization attribute is set then JIT optimization has been disabled + return isJitOptimizationDisabledForSpecificMethod || IsJitOptimizationDisabledForAllMethodsInChunk(md); +} +``` + Extracting a pointer to the `MethodDescVersioningState` data for a given method ```csharp diff --git a/src/coreclr/vm/ceeload.cpp b/src/coreclr/vm/ceeload.cpp index bb0f2037816852..227f1d8b9b08e6 100644 --- a/src/coreclr/vm/ceeload.cpp +++ b/src/coreclr/vm/ceeload.cpp @@ -436,9 +436,11 @@ void Module::Initialize(AllocMemTracker *pamTracker, LPCWSTR szName) #ifdef FEATURE_READYTORUN m_pNativeImage = NULL; + m_pReadyToRunImage = NULL; if ((m_pReadyToRunInfo = ReadyToRunInfo::Initialize(this, pamTracker)) != NULL) { m_pNativeImage = m_pReadyToRunInfo->GetNativeImage(); + m_pReadyToRunImage = m_pReadyToRunInfo->GetImage(); if (m_pNativeImage != NULL) { m_NativeMetadataAssemblyRefMap = m_pNativeImage->GetManifestMetadataAssemblyRefMap(); @@ -2052,7 +2054,7 @@ PEImageLayout * Module::GetReadyToRunImage() #ifdef FEATURE_READYTORUN if (IsReadyToRun()) - return GetReadyToRunInfo()->GetImage(); + return m_pReadyToRunImage; #endif return NULL; diff --git a/src/coreclr/vm/ceeload.h b/src/coreclr/vm/ceeload.h index c4d02b6e02dbb6..67ea617af398c6 100644 --- a/src/coreclr/vm/ceeload.h +++ b/src/coreclr/vm/ceeload.h @@ -831,6 +831,7 @@ class Module : public ModuleBase #ifdef FEATURE_READYTORUN private: PTR_ReadyToRunInfo m_pReadyToRunInfo; + PTR_PEImageLayout m_pReadyToRunImage; // cached on the module for easy access PTR_NativeImage m_pNativeImage; #endif @@ -1705,6 +1706,7 @@ struct cdac_data static constexpr size_t Path = offsetof(Module, m_path); static constexpr size_t FileName = offsetof(Module, m_fileName); static constexpr size_t ReadyToRunInfo = offsetof(Module, m_pReadyToRunInfo); + static constexpr size_t ReadyToRunImage = offsetof(Module, m_pReadyToRunImage); static constexpr size_t GrowableSymbolStream = offsetof(Module, m_pIStreamSym); // Lookup map pointers diff --git a/src/coreclr/vm/codeversion.h b/src/coreclr/vm/codeversion.h index 33700718aad612..437cd14d996338 100644 --- a/src/coreclr/vm/codeversion.h +++ b/src/coreclr/vm/codeversion.h @@ -82,6 +82,7 @@ class NativeCodeVersion OptimizationTierOptimized, // may do less optimizations than tier 1 OptimizationTier0Instrumented, OptimizationTier1Instrumented, + OptimizationTierUnknown = 0xFFFFFFFF }; #ifdef FEATURE_TIERED_COMPILATION OptimizationTier GetOptimizationTier() const; @@ -140,13 +141,8 @@ class NativeCodeVersion PTR_MethodDesc m_pMethodDesc; } m_synthetic; }; -#endif // FEATURE_CODE_VERSIONING }; - - -#ifdef FEATURE_CODE_VERSIONING - enum class RejitFlags : uint32_t { // The profiler has requested a ReJit, so we've allocated stuff, but we haven't diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index 3844f70514e612..39f4dd5cd44470 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -173,6 +173,7 @@ CDAC_TYPE_FIELD(Module, /*pointer*/, DynamicMetadata, cdac_data::Dynamic CDAC_TYPE_FIELD(Module, /*pointer*/, Path, cdac_data::Path) CDAC_TYPE_FIELD(Module, /*pointer*/, FileName, cdac_data::FileName) CDAC_TYPE_FIELD(Module, /*pointer*/, ReadyToRunInfo, cdac_data::ReadyToRunInfo) +CDAC_TYPE_FIELD(Module, /*pointer*/, ReadyToRunImage, cdac_data::ReadyToRunImage) CDAC_TYPE_FIELD(Module, /*pointer*/, GrowableSymbolStream, cdac_data::GrowableSymbolStream) CDAC_TYPE_FIELD(Module, /*pointer*/, AvailableTypeParams, offsetof(Module, m_pAvailableParamTypes)) CDAC_TYPE_FIELD(Module, /*pointer*/, InstMethodHashTable, offsetof(Module, m_pInstMethodHashTable)) @@ -317,6 +318,12 @@ CDAC_TYPE_FIELD(EEClass, /*uint16*/, NumThreadStaticFields, cdac_data:: CDAC_TYPE_FIELD(EEClass, /*uint16*/, NumNonVirtualSlots, cdac_data::NumNonVirtualSlots) CDAC_TYPE_END(EEClass) +CDAC_TYPE_BEGIN(EEConfig) +CDAC_TYPE_INDETERMINATE(EEConfig) +CDAC_TYPE_FIELD(EEConfig, /*bool*/, JitMinOpts, cdac_data::JitMinOpts) +CDAC_TYPE_FIELD(EEConfig, /*bool*/, Debuggable, cdac_data::Debuggable) +CDAC_TYPE_END(EEConfig) + CDAC_TYPE_BEGIN(ArrayClass) CDAC_TYPE_INDETERMINATE(ArrayClass) CDAC_TYPE_FIELD(ArrayClass, /*uint8*/, Rank, cdac_data::Rank) @@ -493,6 +500,7 @@ CDAC_TYPE_BEGIN(MethodDescCodeData) CDAC_TYPE_INDETERMINATE(MethodDescCodeData) CDAC_TYPE_FIELD(MethodDescCodeData, /*CodePointer*/, TemporaryEntryPoint, offsetof(MethodDescCodeData,TemporaryEntryPoint)) CDAC_TYPE_FIELD(MethodDescCodeData, /*pointer*/, VersioningState, offsetof(MethodDescCodeData,VersioningState)) +CDAC_TYPE_FIELD(MethodDescCodeData, /*OptimizationTier*/, OptimizationTier, offsetof(MethodDescCodeData,OptimizationTier)) CDAC_TYPE_END(MethodDescCodeData) CDAC_TYPE_BEGIN(MethodDescVersioningState) @@ -983,6 +991,11 @@ CDAC_GLOBAL(ObjectToMethodTableUnmask, uint8, 1 | 1 << 1 | 1 << 2) #else CDAC_GLOBAL(ObjectToMethodTableUnmask, uint8, 1 | 1 << 1) #endif //TARGET_64BIT +#ifdef FEATURE_TIERED_COMPILATION +CDAC_GLOBAL(FeatureTieredCompilation, uint8, 1) +#else +CDAC_GLOBAL(FeatureTieredCompilation, uint8, 0) +#endif // FEATURE_TIERED_COMPILATION CDAC_GLOBAL(SOSBreakingChangeVersion, uint8, SOS_BREAKING_CHANGE_VERSION) CDAC_GLOBAL(DirectorySeparator, uint8, (uint8_t)DIRECTORY_SEPARATOR_CHAR_A) CDAC_GLOBAL(HashMapSlotsPerBucket, uint32, SLOTS_PER_BUCKET) @@ -999,6 +1012,8 @@ CDAC_GLOBAL(StaticsPointerMask, uintptr_t, DynamicStaticsInfo::STATICSPOINTERMAS CDAC_GLOBAL(PtrArrayOffsetToDataArray, uintptr_t, offsetof(PtrArray, m_Array)) CDAC_GLOBAL(NumberOfTlsOffsetsNotUsedInNoncollectibleArray, uint8, NUMBER_OF_TLSOFFSETS_NOT_USED_IN_NONCOLLECTIBLE_ARRAY) CDAC_GLOBAL(MaxClrNotificationArgs, uint32, MAX_CLR_NOTIFICATION_ARGS) +CDAC_GLOBAL_POINTER(EEConfig, &::g_pConfig) +CDAC_GLOBAL_POINTER(CORDebuggerControlFlags, &::g_CORDebuggerControlFlags) CDAC_GLOBAL_POINTER(ClrNotificationArguments, &::g_clrNotificationArguments) CDAC_GLOBAL_POINTER(ArrayBoundsZero, cdac_data::ArrayBoundsZero) CDAC_GLOBAL_POINTER(ExceptionMethodTable, &::g_pExceptionClass) @@ -1049,7 +1064,9 @@ CDAC_GLOBAL_CONTRACT(Loader, 1) CDAC_GLOBAL_CONTRACT(Object, 1) CDAC_GLOBAL_CONTRACT(PlatformMetadata, 1) CDAC_GLOBAL_CONTRACT(PrecodeStubs, 3) +#ifdef FEATURE_REJIT CDAC_GLOBAL_CONTRACT(ReJIT, 1) +#endif // FEATURE_REJIT CDAC_GLOBAL_CONTRACT(RuntimeInfo, 1) CDAC_GLOBAL_CONTRACT(RuntimeTypeSystem, 1) CDAC_GLOBAL_CONTRACT(SHash, 1) diff --git a/src/coreclr/vm/eeconfig.h b/src/coreclr/vm/eeconfig.h index 9c008ae7647cc2..8d4cf680e79fa1 100644 --- a/src/coreclr/vm/eeconfig.h +++ b/src/coreclr/vm/eeconfig.h @@ -695,8 +695,14 @@ class EEConfig public: DWORD GetSleepOnExit() { return dwSleepOnExit; } -}; + friend struct ::cdac_data; +}; +template<> struct cdac_data +{ + static constexpr size_t JitMinOpts = offsetof(EEConfig, fJitMinOpts); + static constexpr size_t Debuggable = offsetof(EEConfig, fDebuggable); +}; #ifdef _DEBUG_IMPL diff --git a/src/coreclr/vm/method.cpp b/src/coreclr/vm/method.cpp index 93920ad4975114..5eee80e5504876 100644 --- a/src/coreclr/vm/method.cpp +++ b/src/coreclr/vm/method.cpp @@ -253,6 +253,20 @@ HRESULT MethodDesc::SetMethodDescVersionState(PTR_MethodDescVersioningState stat return S_OK; } +HRESULT MethodDesc::SetMethodDescOptimizationTier(NativeCodeVersion::OptimizationTier tier) +{ + WRAPPER_NO_CONTRACT; + + HRESULT hr; + IfFailRet(EnsureCodeDataExists(NULL)); + + _ASSERTE(m_codeData != NULL); + if (InterlockedExchangeT(&m_codeData->OptimizationTier, tier) != NativeCodeVersion::OptimizationTierUnknown) + return S_FALSE; + + return S_OK; +} + #ifdef FEATURE_INTERPRETER // Set the call stub for the interpreter to JIT/AOT calls // Returns true if the current call set the stub, false if it was already set @@ -288,6 +302,15 @@ PTR_MethodDescVersioningState MethodDesc::GetMethodDescVersionState() return VolatileLoadWithoutBarrier(&codeData->VersioningState); } +NativeCodeVersion::OptimizationTier MethodDesc::GetMethodDescOptimizationTier() +{ + WRAPPER_NO_CONTRACT; + PTR_MethodDescCodeData codeData = VolatileLoadWithoutBarrier(&m_codeData); + if (codeData == NULL) + return NativeCodeVersion::OptimizationTierUnknown; + return VolatileLoadWithoutBarrier(&codeData->OptimizationTier); +} + //******************************************************************************* LPCUTF8 MethodDesc::GetNameThrowing() { @@ -2766,6 +2789,7 @@ void MethodDesc::EnsureTemporaryEntryPointCore(AllocMemTracker *pamTracker) #endif // FEATURE_PORTABLE_ENTRYPOINTS IfFailThrow(EnsureCodeDataExists(pamTracker)); + InterlockedExchangeT(&m_codeData->OptimizationTier, NativeCodeVersion::OptimizationTierUnknown); if (InterlockedCompareExchangeT(&m_codeData->TemporaryEntryPoint, entryPoint, (PCODE)NULL) == (PCODE)NULL) amt.SuppressRelease(); // We only need to suppress the release if we are working with a MethodDesc which is not newly allocated diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index 07683f725f4a8d..0e55272432b1d0 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -239,6 +239,7 @@ struct MethodDescCodeData final #ifdef FEATURE_INTERPRETER CallStubHeader *CallStub; #endif // FEATURE_INTERPRETER + NativeCodeVersion::OptimizationTier OptimizationTier; }; using PTR_MethodDescCodeData = DPTR(MethodDescCodeData); @@ -1844,6 +1845,7 @@ class MethodDesc HRESULT EnsureCodeDataExists(AllocMemTracker *pamTracker); HRESULT SetMethodDescVersionState(PTR_MethodDescVersioningState state); + HRESULT SetMethodDescOptimizationTier(NativeCodeVersion::OptimizationTier tier); #ifdef FEATURE_INTERPRETER bool SetCallStub(CallStubHeader *pHeader); CallStubHeader *GetCallStub(); @@ -1852,6 +1854,7 @@ class MethodDesc #endif //!DACCESS_COMPILE PTR_MethodDescVersioningState GetMethodDescVersionState(); + NativeCodeVersion::OptimizationTier GetMethodDescOptimizationTier(); public: inline DWORD GetClassification() const diff --git a/src/coreclr/vm/tieredcompilation.cpp b/src/coreclr/vm/tieredcompilation.cpp index 5a66f26ef328b3..fc20c56f6a26f1 100644 --- a/src/coreclr/vm/tieredcompilation.cpp +++ b/src/coreclr/vm/tieredcompilation.cpp @@ -91,25 +91,27 @@ NativeCodeVersion::OptimizationTier TieredCompilationManager::GetInitialOptimiza { WRAPPER_NO_CONTRACT; _ASSERTE(pMethodDesc != NULL); - + NativeCodeVersion::OptimizationTier initialTier = pMethodDesc->GetMethodDescOptimizationTier(); + if (initialTier != NativeCodeVersion::OptimizationTierUnknown) + { + return initialTier; + } #ifdef FEATURE_TIERED_COMPILATION if (!pMethodDesc->IsEligibleForTieredCompilation()) { // The optimization tier is not used - return NativeCodeVersion::OptimizationTierOptimized; + initialTier = NativeCodeVersion::OptimizationTierOptimized; } - _ASSERT(!pMethodDesc->RequestedAggressiveOptimization()); - - if (!pMethodDesc->GetLoaderAllocator()->GetCallCountingManager()->IsCallCountingEnabled(NativeCodeVersion(pMethodDesc))) + else if (!pMethodDesc->GetLoaderAllocator()->GetCallCountingManager()->IsCallCountingEnabled(NativeCodeVersion(pMethodDesc))) { // Tier 0 call counting may have been disabled for several reasons, the intention is to start with and stay at an // optimized tier - return NativeCodeVersion::OptimizationTierOptimized; + initialTier = NativeCodeVersion::OptimizationTierOptimized; } #ifdef FEATURE_PGO - if (g_pConfig->TieredPGO()) + else if (g_pConfig->TieredPGO()) { // Initial tier for R2R is always just OptimizationTier0 // For ILOnly it depends on TieredPGO_InstrumentOnlyHotCode: @@ -118,16 +120,23 @@ NativeCodeVersion::OptimizationTier TieredCompilationManager::GetInitialOptimiza if (g_pConfig->TieredPGO_InstrumentOnlyHotCode() || ExecutionManager::IsReadyToRunCode(pMethodDesc->GetNativeCode())) { - return NativeCodeVersion::OptimizationTier0; + initialTier = NativeCodeVersion::OptimizationTier0; + } + else + { + initialTier = NativeCodeVersion::OptimizationTier0Instrumented; } - return NativeCodeVersion::OptimizationTier0Instrumented; } -#endif - - return NativeCodeVersion::OptimizationTier0; +#endif // FEATURE_PGO + else + initialTier = NativeCodeVersion::OptimizationTier0; #else - return NativeCodeVersion::OptimizationTierOptimized; -#endif + initialTier = NativeCodeVersion::OptimizationTierOptimized; +#endif // FEATURE_TIERED_COMPILATION +#ifndef DACCESS_COMPILE + pMethodDesc->SetMethodDescOptimizationTier(initialTier); +#endif // DACCESS_COMPILE + return initialTier; } bool TieredCompilationManager::IsTieringDelayActive() diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ILoader.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ILoader.cs index 4e688f799c6358..c4f01f130518eb 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ILoader.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/ILoader.cs @@ -41,6 +41,13 @@ public enum ModuleFlags BeingUnloaded = 0x100000, } +[Flags] + +public enum DebuggerControlFlags +{ + AllowJitOpt = 0x0008, +}; + [Flags] public enum AssemblyIterationFlags { @@ -83,6 +90,7 @@ public interface ILoader : IContract TargetPointer GetModule(ModuleHandle handle) => throw new NotImplementedException(); TargetPointer GetAssembly(ModuleHandle handle) => throw new NotImplementedException(); TargetPointer GetPEAssembly(ModuleHandle handle) => throw new NotImplementedException(); + bool GetReadyToRunImageInfo(ModuleHandle handle, out TargetPointer r2rImageBase, out TargetPointer r2rImageEnd) => throw new NotImplementedException(); bool TryGetLoadedImageContents(ModuleHandle handle, out TargetPointer baseAddress, out uint size, out uint imageFlags) => throw new NotImplementedException(); TargetPointer GetILAddr(TargetPointer peAssemblyPtr, int rva) => throw new NotImplementedException(); bool TryGetSymbolStream(ModuleHandle handle, out TargetPointer buffer, out uint size) => throw new NotImplementedException(); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IReJIT.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IReJIT.cs index e01fca40002b73..1d45a6e1fa732c 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IReJIT.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IReJIT.cs @@ -12,6 +12,19 @@ public enum RejitState Active } +public enum OptimizationTierEnum +{ + Unknown = 0, + MinOptJitted = 1, + Optimized = 2, + QuickJitted = 3, + OptimizedTier1 = 4, + ReadyToRun = 5, + OptimizedTier1OSR = 6, + QuickJittedInstrumented = 7, + OptimizedTier1Instrumented = 8, +} + public interface IReJIT : IContract { static string IContract.Name { get; } = nameof(ReJIT); @@ -21,6 +34,7 @@ public interface IReJIT : IContract RejitState GetRejitState(ILCodeVersionHandle codeVersionHandle) => throw new NotImplementedException(); TargetNUInt GetRejitId(ILCodeVersionHandle codeVersionHandle) => throw new NotImplementedException(); + IEnumerable<(TargetPointer, TargetPointer, OptimizationTierEnum)> GetTieredVersions(TargetPointer methodDesc, int rejitId) => throw new NotImplementedException(); } public readonly struct ReJIT : IReJIT diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs index dacbde91f3dd82..251e26aedc1e06 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/Contracts/IRuntimeTypeSystem.cs @@ -181,6 +181,8 @@ public interface IRuntimeTypeSystem : IContract bool IsCollectibleMethod(MethodDescHandle methodDesc) => throw new NotImplementedException(); bool IsVersionable(MethodDescHandle methodDesc) => throw new NotImplementedException(); + bool IsEligibleForTieredCompilation(MethodDescHandle methodDesc) => throw new NotImplementedException(); + bool IsJitOptimizationDisabled(MethodDescHandle methodDesc) => throw new NotImplementedException(); TargetPointer GetMethodDescVersioningState(MethodDescHandle methodDesc) => throw new NotImplementedException(); TargetCodePointer GetNativeCode(MethodDescHandle methodDesc) => throw new NotImplementedException(); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs index d2cf878cb8a6c7..357d39f2db166a 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions/DataType.cs @@ -49,6 +49,7 @@ public enum DataType ProbeExtensionResult, MethodTable, DynamicStaticsInfo, + EEConfig, EEClass, ArrayClass, MethodTableAuxiliaryData, diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs index 084afe879f99c1..79c32addc876d1 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Constants.cs @@ -19,6 +19,7 @@ public static class Globals public const string ObjectToMethodTableUnmask = nameof(ObjectToMethodTableUnmask); public const string SOSBreakingChangeVersion = nameof(SOSBreakingChangeVersion); + public const string FeatureTieredCompilation = nameof(FeatureTieredCompilation); public const string ExceptionMethodTable = nameof(ExceptionMethodTable); public const string FreeObjectMethodTable = nameof(FreeObjectMethodTable); @@ -62,6 +63,8 @@ public static class Globals public const string PtrArrayOffsetToDataArray = nameof(PtrArrayOffsetToDataArray); public const string NumberOfTlsOffsetsNotUsedInNoncollectibleArray = nameof(NumberOfTlsOffsetsNotUsedInNoncollectibleArray); public const string MaxClrNotificationArgs = nameof(MaxClrNotificationArgs); + public const string EEConfig = nameof(EEConfig); + public const string CORDebuggerControlFlags = nameof(CORDebuggerControlFlags); public const string ClrNotificationArguments = nameof(ClrNotificationArguments); public const string PlatformMetadata = nameof(PlatformMetadata); public const string ProfilerControlBlock = nameof(ProfilerControlBlock); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Loader_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Loader_1.cs index 592ee98f8b79a6..1ebe4cb3668aa5 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Loader_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Loader_1.cs @@ -173,6 +173,20 @@ TargetPointer ILoader.GetPEAssembly(ModuleHandle handle) return module.PEAssembly; } + bool ILoader.GetReadyToRunImageInfo(ModuleHandle handle, out TargetPointer r2rImageBase, out TargetPointer r2rImageEnd) + { + r2rImageBase = TargetPointer.Null; + r2rImageEnd = TargetPointer.Null; + Data.Module module = _target.ProcessedData.GetOrAdd(handle.Address); + if (module.ReadyToRunInfo == TargetPointer.Null) + return false; + + Data.PEImageLayout pEImageLayout = _target.ProcessedData.GetOrAdd(module.ReadyToRunImage); + r2rImageBase = pEImageLayout.Base; + r2rImageEnd = pEImageLayout.Base + pEImageLayout.Size; + return true; + } + bool ILoader.TryGetLoadedImageContents(ModuleHandle handle, out TargetPointer baseAddress, out uint size, out uint imageFlags) { baseAddress = TargetPointer.Null; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ReJIT_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ReJIT_1.cs index 6e9e80022a5738..8d69952db08be1 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ReJIT_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/ReJIT_1.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.Diagnostics.DataContractReader.Data; namespace Microsoft.Diagnostics.DataContractReader.Contracts; @@ -34,6 +35,18 @@ public enum RejitFlags : uint kSuppressParams = 0x80000000 } + [Flags] + private enum NativeOptimizationTier : uint + { + OptimizationTier0 = 0, + OptimizationTier1 = 1, + OptimizationTier1OSR = 2, + OptimizationTierOptimized = 3, + OptimizationTier0Instrumented = 4, + OptimizationTier1Instrumented = 5, + OptimizationTierUnknown = 0xffffffff + }; + public ReJIT_1(Target target, Data.ProfControlBlock profControlBlock) { _target = target; @@ -77,6 +90,88 @@ TargetNUInt IReJIT.GetRejitId(ILCodeVersionHandle ilCodeVersionHandle) return ilCodeVersionNode.VersionId; } + private NativeOptimizationTier GetInitialOptimizationTier(TargetPointer mdPointer) + { + // validation of the method desc + MethodDescHandle _ = _target.Contracts.RuntimeTypeSystem.GetMethodDescHandle(mdPointer); + Data.MethodDesc md = _target.ProcessedData.GetOrAdd(mdPointer); + Data.MethodDescCodeData codeData = _target.ProcessedData.GetOrAdd(md.CodeData); + return (NativeOptimizationTier)codeData.OptimizationTier; + } + + private static OptimizationTierEnum GetOptimizationTier(NativeOptimizationTier nativeOptimizationTier) + { + return nativeOptimizationTier switch + { + NativeOptimizationTier.OptimizationTier0 => OptimizationTierEnum.QuickJitted, + NativeOptimizationTier.OptimizationTier1 => OptimizationTierEnum.OptimizedTier1, + NativeOptimizationTier.OptimizationTier1OSR => OptimizationTierEnum.OptimizedTier1OSR, + NativeOptimizationTier.OptimizationTierOptimized => OptimizationTierEnum.Optimized, + NativeOptimizationTier.OptimizationTier0Instrumented => OptimizationTierEnum.QuickJittedInstrumented, + NativeOptimizationTier.OptimizationTier1Instrumented => OptimizationTierEnum.OptimizedTier1Instrumented, + _ => OptimizationTierEnum.Unknown, + }; + } + + IEnumerable<(TargetPointer, TargetPointer, OptimizationTierEnum)> IReJIT.GetTieredVersions(TargetPointer methodDesc, int rejitId) + { + Contracts.ICodeVersions codeVersionsContract = _target.Contracts.CodeVersions; + Contracts.IReJIT rejitContract = this; + + ILCodeVersionHandle ilCodeVersion = codeVersionsContract.GetILCodeVersions(methodDesc) + .FirstOrDefault(ilcode => rejitContract.GetRejitId(ilcode).Value == (ulong)rejitId, + ILCodeVersionHandle.Invalid); + + if (!ilCodeVersion.IsValid) + throw new ArgumentException(); + // Iterate through versioning state nodes and return the active one, matching any IL code version + Contracts.IRuntimeTypeSystem rts = _target.Contracts.RuntimeTypeSystem; + Contracts.ILoader loader = _target.Contracts.Loader; + MethodDescHandle mdh = rts.GetMethodDescHandle(methodDesc); + TargetPointer methodTable = rts.GetMethodTable(mdh); + TypeHandle mtTypeHandle = rts.GetTypeHandle(methodTable); + TargetPointer modulePtr = rts.GetModule(mtTypeHandle); + ModuleHandle moduleHandle = loader.GetModuleHandleFromModulePtr(modulePtr); + + bool isReadyToRun = loader.GetReadyToRunImageInfo(moduleHandle, out TargetPointer r2rImageBase, out TargetPointer r2rImageEnd); + bool isEligibleForTieredCompilation = rts.IsEligibleForTieredCompilation(mdh); + int count = 0; + foreach (NativeCodeVersionHandle nativeCodeVersionHandle in codeVersionsContract.GetNativeCodeVersions(methodDesc, ilCodeVersion)) + { + TargetPointer nativeCode = codeVersionsContract.GetNativeCode(nativeCodeVersionHandle).AsTargetPointer; + TargetPointer nativeCodeAddr = nativeCode; + TargetPointer nativeCodeVersionNodePtr = nativeCodeVersionHandle.IsExplicit ? AsNode(nativeCodeVersionHandle).Address : TargetPointer.Null; + OptimizationTierEnum optimizationTier; + if (r2rImageBase <= nativeCode && nativeCode < r2rImageEnd) + { + optimizationTier = OptimizationTierEnum.ReadyToRun; + } + + else if (isEligibleForTieredCompilation) + { + NativeOptimizationTier optTier; + if (!nativeCodeVersionHandle.IsExplicit) + optTier = GetInitialOptimizationTier(nativeCodeVersionHandle.MethodDescAddress); + else + { + NativeCodeVersionNode nativeCodeVersionNode = AsNode(nativeCodeVersionHandle); + optTier = (NativeOptimizationTier)nativeCodeVersionNode.OptimizationTier!.Value; + } + optimizationTier = GetOptimizationTier(optTier); + } + else if (rts.IsJitOptimizationDisabled(mdh)) + { + optimizationTier = OptimizationTierEnum.MinOptJitted; + } + else + { + optimizationTier = OptimizationTierEnum.Optimized; + } + count++; + yield return (nativeCodeAddr, nativeCodeVersionNodePtr, optimizationTier); + } + } + private ILCodeVersionNode AsNode(ILCodeVersionHandle ilCodeVersionHandle) { if (ilCodeVersionHandle.ILCodeVersionNode == TargetPointer.Null) @@ -86,4 +181,14 @@ private ILCodeVersionNode AsNode(ILCodeVersionHandle ilCodeVersionHandle) return _target.ProcessedData.GetOrAdd(ilCodeVersionHandle.ILCodeVersionNode); } + + private NativeCodeVersionNode AsNode(NativeCodeVersionHandle nativeCodeVersionHandle) + { + if (!nativeCodeVersionHandle.IsExplicit) + { + throw new InvalidOperationException("Synthetic NativeCodeVersion does not have a backing node."); + } + + return _target.ProcessedData.GetOrAdd(nativeCodeVersionHandle.CodeVersionNodeAddress); + } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs index 995d2efdddec50..ae2e88e5e51c9e 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/RuntimeTypeSystem_1.cs @@ -7,6 +7,7 @@ using System.Reflection.Metadata.Ecma335; using Microsoft.Diagnostics.DataContractReader.RuntimeTypeSystemHelpers; using Microsoft.Diagnostics.DataContractReader.Data; +using System.Reflection.Metadata; namespace Microsoft.Diagnostics.DataContractReader.Contracts; @@ -124,12 +125,14 @@ internal MethodDesc(Target target, TargetPointer methodDescPointer, Data.MethodD Token = ComputeToken(target, desc, chunk); Size = ComputeSize(target, desc); + IsJitOptimizationDisabledForSpecificMethod = ComputeIsJitOptimizationDisabledForSpecificMethod(target, Token, chunk, desc); } public TargetPointer MethodTable => _chunk.MethodTable; public ushort Slot => _desc.Slot; public uint Token { get; } public uint Size { get; } + internal bool IsJitOptimizationDisabledForSpecificMethod { get; } private static uint ComputeToken(Target target, Data.MethodDesc desc, Data.MethodDescChunk chunk) { @@ -159,13 +162,26 @@ private static uint ComputeSize(Target target, Data.MethodDesc desc) return target.Read(methodDescSizeTable + arrayOffset); } + private static bool ComputeIsJitOptimizationDisabledForSpecificMethod(Target target, uint token, Data.MethodDescChunk chunk, Data.MethodDesc desc) + { + if ((MethodClassification)(desc.Flags & (int)MethodDescFlags_1.MethodDescFlags.ClassificationMask) != MethodClassification.Dynamic) + return false; + EntityHandle entityHandle = MetadataTokens.EntityHandle((int)token); + TypeHandle mt = target.Contracts.RuntimeTypeSystem.GetTypeHandle(chunk.MethodTable); + TargetPointer modulePtr = target.Contracts.RuntimeTypeSystem.GetModule(mt); + ModuleHandle moduleHandle = target.Contracts.Loader.GetModuleHandleFromModulePtr(modulePtr); + MetadataReader reader = target.Contracts.EcmaMetadata.GetMetadata(moduleHandle)!; + MethodDefinition md = reader.GetMethodDefinition((MethodDefinitionHandle)entityHandle); + return (md.ImplAttributes & System.Reflection.MethodImplAttributes.NoOptimization) != 0; + } + public MethodClassification Classification => (MethodClassification)((int)_desc.Flags & (int)MethodDescFlags_1.MethodDescFlags.ClassificationMask); private bool HasFlags(MethodDescFlags_1.MethodDescFlags flags) => (_desc.Flags & (ushort)flags) != 0; private bool HasFlags(MethodDescFlags_1.MethodDescFlags3 flags) => (_desc.Flags3AndTokenRemainder & (ushort)flags) != 0; internal bool HasFlags(MethodDescChunkFlags flags) => (_chunk.FlagsAndTokenRange & (ushort)flags) != 0; - public bool IsEligibleForTieredCompilation => HasFlags(MethodDescFlags_1.MethodDescFlags3.IsEligibleForTieredCompilation); + public bool IsEligibleForTieredCompilation => HasFlags(MethodDescFlags_1.MethodDescFlags3.IsEligibleForTieredCompilation) && _target.ReadGlobal(Constants.Globals.FeatureTieredCompilation) != 0; public bool IsUnboxingStub => HasFlags(MethodDescFlags_1.MethodDescFlags3.IsUnboxingStub); @@ -1054,6 +1070,41 @@ bool IRuntimeTypeSystem.IsVersionable(MethodDescHandle methodDesc) return false; } + bool IRuntimeTypeSystem.IsEligibleForTieredCompilation(MethodDescHandle methodDesc) + { + MethodDesc md = _methodDescs[methodDesc.Address]; + return md.IsEligibleForTieredCompilation; + } + + private bool ModuleJitOptsDisabled(MethodDesc md) + { + TypeHandle mt = GetTypeHandle(md.MethodTable); + TargetPointer modulePtr = GetModule(mt); + ModuleHandle moduleHandle = _target.Contracts.Loader.GetModuleHandleFromModulePtr(modulePtr); + ModuleFlags flags = _target.Contracts.Loader.GetFlags(moduleHandle); + + TargetPointer corDebuggerControlFlagsPtr = _target.ReadGlobalPointer(Constants.Globals.CORDebuggerControlFlags); + uint corDebuggerControlFlags = _target.Read(corDebuggerControlFlagsPtr); + // debugger + bool allowJitOpts = (flags & ModuleFlags.DebuggerAllowJitOptsPriv) != 0 || + (((corDebuggerControlFlags & (uint)DebuggerControlFlags.AllowJitOpt) != 0) && (flags & ModuleFlags.DebuggerUserOverridePriv) == 0); + if (!allowJitOpts) + return true; + // profiler + return (flags & ModuleFlags.ProfilerDisableOptimizations) != 0; + } + private bool IsJitOptimizationDisabledForAllMethodsInChunk(MethodDesc md) + { + TargetPointer eeConfigPtr = _target.ReadGlobalPointer(Constants.Globals.EEConfig); + Data.EEConfig eeConfig = _target.ProcessedData.GetOrAdd(_target.ReadPointer(eeConfigPtr)); + return eeConfig.JitMinOpts || eeConfig.Debuggable || ModuleJitOptsDisabled(md); + } + + bool IRuntimeTypeSystem.IsJitOptimizationDisabled(MethodDescHandle methodDesc) + { + MethodDesc md = _methodDescs[methodDesc.Address]; + return md.IsJitOptimizationDisabledForSpecificMethod || IsJitOptimizationDisabledForAllMethodsInChunk(md); + } TargetPointer IRuntimeTypeSystem.GetMethodDescVersioningState(MethodDescHandle methodDesc) { MethodDesc md = _methodDescs[methodDesc.Address]; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EEConfig.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EEConfig.cs new file mode 100644 index 00000000000000..9a64d483dc8d09 --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/EEConfig.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Diagnostics.DataContractReader.Data; + +internal sealed class EEConfig : IData +{ + static EEConfig IData.Create(Target target, TargetPointer address) => new EEConfig(target, address); + public EEConfig(Target target, TargetPointer address) + { + Target.TypeInfo type = target.GetTypeInfo(DataType.EEConfig); + + JitMinOpts = target.Read(address + (ulong)type.Fields[nameof(JitMinOpts)].Offset) != 0; + Debuggable = target.Read(address + (ulong)type.Fields[nameof(Debuggable)].Offset) != 0; + } + + public bool JitMinOpts { get; init; } + public bool Debuggable { get; init; } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MethodDescCodeData.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MethodDescCodeData.cs index dc76b8981b6101..fdce2c5c9c7591 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MethodDescCodeData.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/MethodDescCodeData.cs @@ -13,8 +13,10 @@ public MethodDescCodeData(Target target, TargetPointer address) TemporaryEntryPoint = target.ReadCodePointer(address + (ulong)type.Fields[nameof(TemporaryEntryPoint)].Offset); VersioningState = target.ReadPointer(address + (ulong)type.Fields[nameof(VersioningState)].Offset); + OptimizationTier = target.Read(address + (ulong)type.Fields[nameof(OptimizationTier)].Offset); } public TargetCodePointer TemporaryEntryPoint { get; set; } public TargetPointer VersioningState { get; set; } + public uint OptimizationTier { get; set; } } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Module.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Module.cs index 7fc833df437e84..c187e2aaac57ba 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Module.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Module.cs @@ -23,6 +23,7 @@ public Module(Target target, TargetPointer address) Path = target.ReadPointer(address + (ulong)type.Fields[nameof(Path)].Offset); FileName = target.ReadPointer(address + (ulong)type.Fields[nameof(FileName)].Offset); ReadyToRunInfo = target.ReadPointer(address + (ulong)type.Fields[nameof(ReadyToRunInfo)].Offset); + ReadyToRunImage = target.ReadPointer(address + (ulong)type.Fields[nameof(ReadyToRunImage)].Offset); GrowableSymbolStream = target.ReadPointer(address + (ulong)type.Fields[nameof(GrowableSymbolStream)].Offset); AvailableTypeParams = target.ReadPointer(address + (ulong)type.Fields[nameof(AvailableTypeParams)].Offset); InstMethodHashTable = target.ReadPointer(address + (ulong)type.Fields[nameof(InstMethodHashTable)].Offset); @@ -46,6 +47,7 @@ public Module(Target target, TargetPointer address) public TargetPointer Path { get; init; } public TargetPointer FileName { get; init; } public TargetPointer ReadyToRunInfo { get; init; } + public TargetPointer ReadyToRunImage { get; init; } public TargetPointer GrowableSymbolStream { get; init; } public TargetPointer AvailableTypeParams { get; init; } public TargetPointer InstMethodHashTable { get; init; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/NativeCodeVersionNode.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/NativeCodeVersionNode.cs index e68345e4499d23..0c936b499612d3 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/NativeCodeVersionNode.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/NativeCodeVersionNode.cs @@ -17,10 +17,15 @@ public NativeCodeVersionNode(Target target, TargetPointer address) NativeCode = target.ReadCodePointer(address + (ulong)type.Fields[nameof(NativeCode)].Offset); Flags = target.Read(address + (ulong)type.Fields[nameof(Flags)].Offset); ILVersionId = target.ReadNUInt(address + (ulong)type.Fields[nameof(ILVersionId)].Offset); + if (type.Fields.ContainsKey(nameof(OptimizationTier))) + { + OptimizationTier = target.Read(address + (ulong)type.Fields[nameof(OptimizationTier)].Offset); + } if (type.Fields.ContainsKey(nameof(GCCoverageInfo))) { GCCoverageInfo = target.ReadPointer(address + (ulong)type.Fields[nameof(GCCoverageInfo)].Offset); } + Address = address; } public TargetPointer Next { get; init; } @@ -29,6 +34,8 @@ public NativeCodeVersionNode(Target target, TargetPointer address) public TargetCodePointer NativeCode { get; init; } public uint Flags { get; init; } public TargetNUInt ILVersionId { get; init; } + public uint? OptimizationTier { get; init; } public TargetPointer? GCCoverageInfo { get; init; } + public TargetPointer Address { get; init; } } diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs index 067c494973b8ba..5eaeda6371ee83 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/ISOSDacInterface.cs @@ -230,6 +230,25 @@ public enum Flags : uint public ClrDataAddress NativeCodeAddr; }; +internal struct DacpTieredVersionData +{ + public enum OptimizationTierEnum + { + Unknown = 0, + MinOptJitted = 1, + Optimized = 2, + QuickJitted = 3, + OptimizedTier1 = 4, + ReadyToRun = 5, + OptimizedTier1OSR = 6, + QuickJittedInstrumented = 7, + OptimizedTier1Instrumented = 8, + } + public ClrDataAddress NativeCodeAddr; + public OptimizationTierEnum OptimizationTier; + public ClrDataAddress NativeCodeVersionNodePtr; +}; + internal struct DacpMethodDescData { public int bHasNativeCode; @@ -638,7 +657,7 @@ internal unsafe partial interface ISOSDacInterface4 internal unsafe partial interface ISOSDacInterface5 { [PreserveSig] - int GetTieredVersions(ClrDataAddress methodDesc, int rejitId, /*struct DacpTieredVersionData*/void* nativeCodeAddrs, int cNativeCodeAddrs, int* pcNativeCodeAddrs); + int GetTieredVersions(ClrDataAddress methodDesc, int rejitId, DacpTieredVersionData* nativeCodeAddrs, int cNativeCodeAddrs, int* pcNativeCodeAddrs); }; internal struct DacpMethodTableCollectibleData diff --git a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs index 157406337bbd22..01e34b2be22059 100644 --- a/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs +++ b/src/native/managed/cdac/mscordaccore_universal/Legacy/SOSDacImpl.cs @@ -3097,8 +3097,58 @@ int ISOSDacInterface4.GetClrNotification(ClrDataAddress[] arguments, int count, #endregion ISOSDacInterface4 #region ISOSDacInterface5 - int ISOSDacInterface5.GetTieredVersions(ClrDataAddress methodDesc, int rejitId, /*struct DacpTieredVersionData*/ void* nativeCodeAddrs, int cNativeCodeAddrs, int* pcNativeCodeAddrs) - => _legacyImpl5 is not null ? _legacyImpl5.GetTieredVersions(methodDesc, rejitId, nativeCodeAddrs, cNativeCodeAddrs, pcNativeCodeAddrs) : HResults.E_NOTIMPL; + int ISOSDacInterface5.GetTieredVersions(ClrDataAddress methodDesc, int rejitId, DacpTieredVersionData* nativeCodeAddrs, int cNativeCodeAddrs, int* pcNativeCodeAddrs) + { + int hr = HResults.S_OK; + try + { + if (methodDesc == 0 || cNativeCodeAddrs == 0 || pcNativeCodeAddrs == null) + throw new ArgumentException(); + + TargetPointer methodDescPtr = methodDesc.ToTargetPointer(_target); + int count = 0; + foreach ((TargetPointer nativeCode, TargetPointer nativeCodeVersionNodePtr, Contracts.OptimizationTierEnum optTier) in _target.Contracts.ReJIT.GetTieredVersions(methodDescPtr, rejitId)) + { + nativeCodeAddrs[count] = default; + nativeCodeAddrs[count].NativeCodeAddr = nativeCode.ToClrDataAddress(_target); + nativeCodeAddrs[count].NativeCodeVersionNodePtr = nativeCodeVersionNodePtr.ToClrDataAddress(_target); + nativeCodeAddrs[count++].OptimizationTier = (DacpTieredVersionData.OptimizationTierEnum)optTier; + if (count >= cNativeCodeAddrs) + { + hr = HResults.S_FALSE; + break; + } + } + *pcNativeCodeAddrs = count; + } + catch (System.Exception ex) + { + hr = ex.HResult; + } +#if DEBUG + if (_legacyImpl5 is not null) + { + DacpTieredVersionData[] nativeCodeAddrsLocal = new DacpTieredVersionData[cNativeCodeAddrs]; + int neededLocal; + fixed (DacpTieredVersionData* ptr = nativeCodeAddrsLocal) + { + int hrLocal = _legacyImpl5.GetTieredVersions(methodDesc, rejitId, ptr, cNativeCodeAddrs, &neededLocal); + Debug.Assert(hrLocal == hr, $"cDAC: {hr:x}, DAC: {hrLocal:x}"); + if (hr == HResults.S_OK || hr == HResults.S_FALSE) + { + Debug.Assert(*pcNativeCodeAddrs == neededLocal, $"cDAC: {*pcNativeCodeAddrs}, DAC: {neededLocal}"); + for (int i = 0; i < *pcNativeCodeAddrs; i++) + { + Debug.Assert(nativeCodeAddrs[i].NativeCodeAddr == nativeCodeAddrsLocal[i].NativeCodeAddr, $"cDAC: {nativeCodeAddrs[i].NativeCodeAddr:x}, DAC: {nativeCodeAddrsLocal[i].NativeCodeAddr:x}"); + Debug.Assert(nativeCodeAddrs[i].NativeCodeVersionNodePtr == nativeCodeAddrsLocal[i].NativeCodeVersionNodePtr, $"cDAC: {nativeCodeAddrs[i].NativeCodeVersionNodePtr:x}, DAC: {nativeCodeAddrsLocal[i].NativeCodeVersionNodePtr:x}"); + Debug.Assert(nativeCodeAddrs[i].OptimizationTier == nativeCodeAddrsLocal[i].OptimizationTier, $"cDAC: {nativeCodeAddrs[i].OptimizationTier}, DAC: {nativeCodeAddrsLocal[i].OptimizationTier}"); + } + } + } + } +#endif + return hr; + } #endregion ISOSDacInterface5 #region ISOSDacInterface6 diff --git a/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.CodeVersions.cs b/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.CodeVersions.cs index feee26fc9c24f8..5430ac4492addb 100644 --- a/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.CodeVersions.cs +++ b/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.CodeVersions.cs @@ -24,6 +24,7 @@ public class CodeVersions ] }; + // note: we aren't testing this on wasm so we can go ahead and include OptimizationTier private static readonly TypeFields NativeCodeVersionNodeFields = new TypeFields() { DataType = DataType.NativeCodeVersionNode, @@ -34,6 +35,7 @@ public class CodeVersions new(nameof(Data.NativeCodeVersionNode.NativeCode), DataType.pointer), new(nameof(Data.NativeCodeVersionNode.Flags), DataType.uint32), new(nameof(Data.NativeCodeVersionNode.ILVersionId), DataType.nuint), + new(nameof(Data.NativeCodeVersionNode.OptimizationTier), DataType.uint32), new(nameof(Data.NativeCodeVersionNode.GCCoverageInfo), DataType.pointer), ] }; diff --git a/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.cs b/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.cs index aab00bacc85251..ca825ac583b0ef 100644 --- a/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.cs +++ b/src/native/managed/cdac/tests/MockDescriptors/MockDescriptors.cs @@ -143,6 +143,7 @@ internal record TypeFields new(nameof(Data.Module.Path), DataType.pointer), new(nameof(Data.Module.FileName), DataType.pointer), new(nameof(Data.Module.ReadyToRunInfo), DataType.pointer), + new(nameof(Data.Module.ReadyToRunImage), DataType.pointer), new(nameof(Data.Module.GrowableSymbolStream), DataType.pointer), new(nameof(Data.Module.AvailableTypeParams), DataType.pointer), new(nameof(Data.Module.InstMethodHashTable), DataType.pointer),