|
// 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;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Text.Unicode;
#if SUPPORT_JIT
using Internal.Runtime.CompilerServices;
#endif
using Internal.IL;
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;
using Internal.TypeSystem.Interop;
using Internal.CorConstants;
using Internal.Pgo;
using ILCompiler;
using ILCompiler.DependencyAnalysis;
using ILCompiler.DependencyAnalysis.Wasm;
#if READYTORUN
using ILCompiler.ReadyToRun.TypeSystem;
using System.Reflection.Metadata.Ecma335;
using ILCompiler.DependencyAnalysis.ReadyToRun;
#endif
using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore<ILCompiler.DependencyAnalysis.NodeFactory>.DependencyList;
#pragma warning disable IDE0060
namespace Internal.JitInterface
{
internal enum CompilationResult
{
CompilationComplete,
CompilationRetryRequested
}
internal sealed unsafe partial class CorInfoImpl
{
//
// Global initialization and state
//
internal const string JitLibrary = "clrjitilc";
#if SUPPORT_JIT
private const string JitSupportLibrary = "*";
#else
internal const string JitSupportLibrary = "jitinterface";
#endif
private IntPtr _jit;
private IntPtr _unmanagedCallbacks; // array of pointers to JIT-EE interface callbacks
private ExceptionDispatchInfo _lastException;
private struct PgoInstrumentationResults
{
public PgoInstrumentationSchema* pSchema;
public uint countSchemaItems;
public byte* pInstrumentationData;
public HRESULT hr;
}
private Dictionary<MethodDesc, PgoInstrumentationResults> _pgoResults = new Dictionary<MethodDesc, PgoInstrumentationResults>();
[DllImport(JitLibrary)]
private static extern IntPtr jitStartup(IntPtr host);
private static class JitPointerAccessor
{
[DllImport(JitLibrary)]
private static extern IntPtr getJit();
[DllImport(JitSupportLibrary)]
private static extern CorJitResult JitProcessShutdownWork(IntPtr jit);
static JitPointerAccessor()
{
s_jit = getJit();
if (s_jit != IntPtr.Zero)
{
AppDomain.CurrentDomain.ProcessExit += (_, _) => JitProcessShutdownWork(s_jit);
AppDomain.CurrentDomain.UnhandledException += (_, _) => JitProcessShutdownWork(s_jit);
}
}
public static IntPtr Get()
{
return s_jit;
}
private static readonly IntPtr s_jit;
}
private struct LikelyClassMethodRecord
{
public IntPtr handle;
public uint likelihood;
public LikelyClassMethodRecord(IntPtr handle, uint likelihood)
{
this.handle = handle;
this.likelihood = likelihood;
}
}
[DllImport(JitLibrary)]
private static extern uint getLikelyClasses(LikelyClassMethodRecord* pLikelyClasses, uint maxLikelyClasses, PgoInstrumentationSchema* schema, uint countSchemaItems, byte*pInstrumentationData, int ilOffset);
[DllImport(JitLibrary)]
private static extern uint getLikelyMethods(LikelyClassMethodRecord* pLikelyMethods, uint maxLikelyMethods, PgoInstrumentationSchema* schema, uint countSchemaItems, byte* pInstrumentationData, int ilOffset);
[DllImport(JitSupportLibrary)]
private static extern IntPtr GetJitHost(IntPtr configProvider);
//
// Per-method initialization and state
//
private static CorInfoImpl GetThis(IntPtr thisHandle)
{
CorInfoImpl _this = Unsafe.Read<CorInfoImpl>((void*)thisHandle);
Debug.Assert(_this is CorInfoImpl);
return _this;
}
[DllImport(JitSupportLibrary)]
private static extern CorJitResult JitCompileMethod(out IntPtr exception,
IntPtr jit, IntPtr thisHandle, IntPtr callbacks,
ref CORINFO_METHOD_INFO info, uint flags, out IntPtr nativeEntry, out uint codeSize);
[DllImport(JitSupportLibrary)]
private static extern IntPtr AllocException([MarshalAs(UnmanagedType.LPWStr)]string message, int messageLength);
[DllImport(JitSupportLibrary)]
private static extern void JitSetOs(IntPtr jit, CORINFO_OS os);
private IntPtr AllocException(Exception ex)
{
_lastException = ExceptionDispatchInfo.Capture(ex);
string exString = ex.ToString();
IntPtr nativeException = AllocException(exString, exString.Length);
_nativeExceptions ??= new List<IntPtr>();
_nativeExceptions.Add(nativeException);
return nativeException;
}
[DllImport(JitSupportLibrary)]
private static extern void FreeException(IntPtr obj);
[DllImport(JitSupportLibrary)]
private static extern char* GetExceptionMessage(IntPtr obj);
public static void Startup(CORINFO_OS os)
{
jitStartup(GetJitHost(JitConfigProvider.UnmanagedInstance));
JitSetOs(JitPointerAccessor.Get(), os);
}
public CorInfoImpl()
{
_jit = JitPointerAccessor.Get();
if (_jit == IntPtr.Zero)
{
throw new IOException("Failed to initialize JIT");
}
_unmanagedCallbacks = GetUnmanagedCallbacks();
}
private Logger Logger
{
get
{
return _compilation.Logger;
}
}
private CORINFO_MODULE_STRUCT_* _methodScope; // Needed to resolve CORINFO_EH_CLAUSE tokens
public static IEnumerable<PgoSchemaElem> ConvertTypeHandleHistogramsToCompactTypeHistogramFormat(PgoSchemaElem[] pgoData, CompilationModuleGroup compilationModuleGroup)
{
bool hasHistogram = false;
foreach (var elem in pgoData)
{
if (elem.InstrumentationKind == PgoInstrumentationKind.HandleHistogramTypes ||
elem.InstrumentationKind == PgoInstrumentationKind.HandleHistogramMethods)
{
// found histogram
hasHistogram = true;
break;
}
}
if (!hasHistogram)
{
foreach (var elem in pgoData)
{
yield return elem;
}
}
else
{
int currentObjectIndex = 0x1000000; // This needs to be a somewhat large non-zero number, so that the jit does not confuse it with NULL, or any other special value.
Dictionary<object, IntPtr> objectToHandle = new Dictionary<object, IntPtr>();
Dictionary<IntPtr, object> handleToObject = new Dictionary<IntPtr, object>();
MemoryStream memoryStreamInstrumentationData = new MemoryStream();
ComputeJitPgoInstrumentationSchema(LocalObjectToHandle, pgoData, out var nativeSchema, memoryStreamInstrumentationData);
var instrumentationData = memoryStreamInstrumentationData.ToArray();
for (int i = 0; i < pgoData.Length; i++)
{
if ((i + 1 < pgoData.Length) &&
(pgoData[i].InstrumentationKind == PgoInstrumentationKind.HandleHistogramIntCount ||
pgoData[i].InstrumentationKind == PgoInstrumentationKind.HandleHistogramLongCount) &&
(pgoData[i + 1].InstrumentationKind == PgoInstrumentationKind.HandleHistogramTypes ||
pgoData[i + 1].InstrumentationKind == PgoInstrumentationKind.HandleHistogramMethods))
{
PgoSchemaElem? newElem = ComputeLikelyClassMethod(i, handleToObject, nativeSchema, instrumentationData, compilationModuleGroup);
if (newElem.HasValue)
{
yield return newElem.Value;
}
i++; // The histogram is two entries long, so skip an extra entry
continue;
}
yield return pgoData[i];
}
IntPtr LocalObjectToHandle(object input)
{
if (objectToHandle.TryGetValue(input, out var result))
{
return result;
}
result = new IntPtr(currentObjectIndex++);
objectToHandle.Add(input, result);
handleToObject.Add(result, input);
return result;
}
}
}
private static PgoSchemaElem? ComputeLikelyClassMethod(int index, Dictionary<IntPtr, object> handleToObject, PgoInstrumentationSchema[] nativeSchema, byte[] instrumentationData, CompilationModuleGroup compilationModuleGroup)
{
// getLikelyClasses will use two entries from the native schema table. There must be at least two present to avoid overruning the buffer
if (index > (nativeSchema.Length - 2))
return null;
bool isType = nativeSchema[index + 1].InstrumentationKind == PgoInstrumentationKind.HandleHistogramTypes;
fixed (PgoInstrumentationSchema* pSchema = &nativeSchema[index])
{
fixed (byte* pInstrumentationData = &instrumentationData[0])
{
// We're going to store only the most popular type/method to reduce size of the profile
LikelyClassMethodRecord* likelyClassMethods = stackalloc LikelyClassMethodRecord[1];
uint numberOfRecords;
if (isType)
{
numberOfRecords = getLikelyClasses(likelyClassMethods, 1, pSchema, 2, pInstrumentationData, nativeSchema[index].ILOffset);
}
else
{
numberOfRecords = getLikelyMethods(likelyClassMethods, 1, pSchema, 2, pInstrumentationData, nativeSchema[index].ILOffset);
}
if (numberOfRecords > 0)
{
TypeSystemEntityOrUnknown[] newData = null;
if (isType)
{
TypeDesc type = (TypeDesc)handleToObject[likelyClassMethods->handle];
#if READYTORUN
if (compilationModuleGroup.VersionsWithType(type))
#endif
{
newData = new[] { new TypeSystemEntityOrUnknown(type) };
}
}
else
{
MethodDesc method = (MethodDesc)handleToObject[likelyClassMethods->handle];
#if READYTORUN
if (compilationModuleGroup.VersionsWithMethodBody(method))
#endif
{
newData = new[] { new TypeSystemEntityOrUnknown(method) };
}
}
if (newData != null)
{
PgoSchemaElem likelyClassElem = default(PgoSchemaElem);
likelyClassElem.InstrumentationKind = isType ? PgoInstrumentationKind.GetLikelyClass : PgoInstrumentationKind.GetLikelyMethod;
likelyClassElem.ILOffset = nativeSchema[index].ILOffset;
likelyClassElem.Count = 1;
likelyClassElem.Other = (int)(likelyClassMethods->likelihood | (numberOfRecords << 8));
likelyClassElem.DataObject = newData;
return likelyClassElem;
}
}
}
}
return null;
}
private CompilationResult CompileMethodInternal(IMethodNode methodCodeNodeNeedingCode, MethodIL methodIL)
{
// methodIL must not be null
if (methodIL == null)
{
ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramSpecific, MethodBeingCompiled);
}
CORINFO_METHOD_INFO methodInfo;
Get_CORINFO_METHOD_INFO(MethodBeingCompiled, methodIL, &methodInfo);
_methodScope = methodInfo.scope;
#if !READYTORUN
SetDebugInformation(methodCodeNodeNeedingCode, methodIL);
#endif
CorInfoImpl _this = this;
IntPtr exception;
IntPtr nativeEntry;
uint codeSize;
var result = JitCompileMethod(out exception,
_jit, (IntPtr)(&_this), _unmanagedCallbacks,
ref methodInfo, (uint)CorJitFlag.CORJIT_FLAG_CALL_GETJITFLAGS, out nativeEntry, out codeSize);
if (exception != IntPtr.Zero)
{
if (_lastException != null)
{
// If we captured a managed exception, rethrow that.
// TODO: might not actually be the real reason. It could be e.g. a JIT failure/bad IL that followed
// an inlining attempt with a type system problem in it...
#if SUPPORT_JIT
_lastException.Throw();
#else
if (_lastException.SourceException is TypeSystemException)
{
// Type system exceptions can be turned into code that throws the exception at runtime.
_lastException.Throw();
}
#if READYTORUN
else if (_lastException.SourceException is RequiresRuntimeJitException)
{
// Runtime JIT requirement is not a cause for failure, we just mustn't JIT a particular method
_lastException.Throw();
}
#endif
else
{
// This is just a bug somewhere.
throw new CodeGenerationFailedException(_methodCodeNode.Method, _lastException.SourceException);
}
#endif
}
// This is a failure we don't know much about.
char* szMessage = GetExceptionMessage(exception);
string message = szMessage != null ? new string(szMessage) : "JIT Exception";
throw new Exception(message);
}
if (result == CorJitResult.CORJIT_BADCODE)
{
ThrowHelper.ThrowInvalidProgramException();
}
if (result == CorJitResult.CORJIT_IMPLLIMITATION || result == CorJitResult.CORJIT_R2R_UNSUPPORTED)
{
#if READYTORUN
throw new RequiresRuntimeJitException("JIT implementation limitation");
#else
ThrowHelper.ThrowInvalidProgramException();
#endif
}
if (result == CorJitResult.CORJIT_OUTOFMEM)
{
throw new OutOfMemoryException();
}
if (result != CorJitResult.CORJIT_OK)
{
#if SUPPORT_JIT
// FailFast?
throw new Exception("JIT failed");
#else
throw new CodeGenerationFailedException(_methodCodeNode.Method);
#endif
}
if (codeSize < _code.Length)
{
if (_compilation.TypeSystemContext.Target.Architecture != TargetArchitecture.ARM64
&& _compilation.TypeSystemContext.Target.Architecture != TargetArchitecture.LoongArch64
&& _compilation.TypeSystemContext.Target.Architecture != TargetArchitecture.RiscV64)
{
// For xarch/arm32/LoongArch64/RiscV64, the generated code is sometimes smaller than the memory allocated.
// In that case, trim the codeBlock to the actual value.
//
// For arm64, the allocation request of `hotCodeSize` also includes the roData size
// while the `codeSize` returned just contains the size of the native code. As such,
// there is guarantee that for armarch, (codeSize == _code.Length) is always true.
//
// Currently, hot/cold splitting is not done and hence `codeSize` just includes the size of
// hotCode. Once hot/cold splitting is done, need to trim respective `_code` or `_coldCode`
// accordingly.
Debug.Assert(codeSize != 0);
Array.Resize(ref _code, (int)codeSize);
}
}
CompilationResult compilationCompleteBehavior = CompilationResult.CompilationComplete;
DetermineIfCompilationShouldBeRetried(ref compilationCompleteBehavior);
if (compilationCompleteBehavior == CompilationResult.CompilationRetryRequested)
return compilationCompleteBehavior;
PublishCode();
PublishROData();
PublishRWData();
return CompilationResult.CompilationComplete;
}
partial void DetermineIfCompilationShouldBeRetried(ref CompilationResult result);
private void PublishCode()
{
var relocs = _codeRelocs.ToArray();
Array.Sort(relocs, (x, y) => (x.Offset - y.Offset));
int alignment = JitConfigProvider.Instance.HasFlag(CorJitFlag.CORJIT_FLAG_SIZE_OPT) && !JitConfigProvider.Instance.HasFlag(CorJitFlag.CORJIT_FLAG_ENABLE_CFG) ?
_compilation.NodeFactory.Target.MinimumFunctionAlignment :
_compilation.NodeFactory.Target.OptimumFunctionAlignment;
alignment = Math.Max(alignment, _codeAlignment);
var objectData = new ObjectNode.ObjectData(_code,
relocs,
alignment,
new ISymbolDefinitionNode[] { _methodCodeNode });
ObjectNode.ObjectData ehInfo = _ehClauses != null ? EncodeEHInfo() : null;
DebugEHClauseInfo[] debugEHClauseInfos = null;
if (_ehClauses != null)
{
debugEHClauseInfos = new DebugEHClauseInfo[_ehClauses.Length];
for (int i = 0; i < _ehClauses.Length; i++)
{
var clause = _ehClauses[i];
// clause.TryLength returned by the JIT is actually end offset...
// https://github.com/dotnet/runtime/issues/5282
// We subtract offset from "length" to get the actual length.
Debug.Assert(clause.TryLength >= clause.TryOffset);
Debug.Assert(clause.HandlerLength >= clause.HandlerOffset);
debugEHClauseInfos[i] = new DebugEHClauseInfo(clause.TryOffset, clause.TryLength - clause.TryOffset,
clause.HandlerOffset, clause.HandlerLength - clause.HandlerOffset);
}
}
_methodCodeNode.SetCode(objectData);
#if READYTORUN
if (_methodColdCodeNode != null)
{
var relocs2 = _coldCodeRelocs.ToArray();
Array.Sort(relocs2, (x, y) => (x.Offset - y.Offset));
var coldObjectData = new ObjectNode.ObjectData(_coldCode,
relocs2,
alignment,
new ISymbolDefinitionNode[] { _methodColdCodeNode });
_methodColdCodeNode.SetCode(coldObjectData);
_methodCodeNode.ColdCodeNode = _methodColdCodeNode;
}
#endif
_methodCodeNode.InitializeFrameInfos(_frameInfos);
#if READYTORUN
_methodCodeNode.InitializeColdFrameInfos(_coldFrameInfos);
#endif
_methodCodeNode.InitializeDebugEHClauseInfos(debugEHClauseInfos);
_methodCodeNode.InitializeGCInfo(_gcInfo);
_methodCodeNode.InitializeEHInfo(ehInfo);
_methodCodeNode.InitializeDebugLocInfos(_debugLocInfos);
_methodCodeNode.InitializeDebugVarInfos(_debugVarInfos);
#if READYTORUN
MethodDesc[] inlineeArray;
if (_inlinedMethods != null)
{
inlineeArray = new MethodDesc[_inlinedMethods.Count];
_inlinedMethods.CopyTo(inlineeArray);
Array.Sort(inlineeArray, TypeSystemComparer.Instance.Compare);
}
else
{
inlineeArray = Array.Empty<MethodDesc>();
}
_methodCodeNode.InitializeInliningInfo(inlineeArray, _compilation.NodeFactory);
// Detect cases where the instruction set support used is a superset of the baseline instruction set specification
var baselineSupport = _compilation.InstructionSetSupport;
bool needPerMethodInstructionSetFixup = false;
foreach (var instructionSet in _actualInstructionSetSupported)
{
if (!baselineSupport.IsInstructionSetSupported(instructionSet))
{
needPerMethodInstructionSetFixup = true;
}
}
foreach (var instructionSet in _actualInstructionSetUnsupported)
{
if (!baselineSupport.IsInstructionSetExplicitlyUnsupported(instructionSet))
{
needPerMethodInstructionSetFixup = true;
}
}
if (needPerMethodInstructionSetFixup)
{
TargetArchitecture architecture = _compilation.TypeSystemContext.Target.Architecture;
_actualInstructionSetSupported.ExpandInstructionSetByImplication(architecture);
_actualInstructionSetUnsupported.ExpandInstructionSetByReverseImplication(architecture);
_actualInstructionSetUnsupported.Set64BitInstructionSetVariants(architecture);
InstructionSetSupport actualSupport = new InstructionSetSupport(_actualInstructionSetSupported, _actualInstructionSetUnsupported, architecture);
var node = _compilation.SymbolNodeFactory.PerMethodInstructionSetSupportFixup(actualSupport);
AddPrecodeFixup(node);
}
Debug.Assert(_stashedPrecodeFixups.Count == 0);
if (_precodeFixups != null)
{
HashSet<ISymbolNode> computedNodes = new HashSet<ISymbolNode>();
foreach (var fixup in _precodeFixups)
{
if (computedNodes.Add(fixup))
{
if (fixup is IMethodNode methodNode)
{
try
{
_compilation.NodeFactory.DetectGenericCycles(_methodCodeNode.Method, methodNode.Method);
}
catch (TypeLoadException)
{
throw new RequiresRuntimeJitException("Requires runtime JIT - potential generic cycle detected");
}
}
_methodCodeNode.Fixups.Add(fixup);
}
}
}
if (_synthesizedPgoDependencies != null)
{
Debug.Assert(_compilation.NodeFactory.InstrumentationDataTable != null, "Expected InstrumentationDataTable to be non-null with synthesized PGO data to embed");
_compilation.NodeFactory.InstrumentationDataTable.EmbedSynthesizedPgoDataForMethods(ref _additionalDependencies, _synthesizedPgoDependencies);
}
#else
var methodIL = (MethodIL)HandleToObject((void*)_methodScope);
CodeBasedDependencyAlgorithm.AddDependenciesDueToMethodCodePresence(ref _additionalDependencies, _compilation.NodeFactory, MethodBeingCompiled, methodIL);
_methodCodeNode.InitializeDebugInfo(_debugInfo);
LocalVariableDefinition[] locals = methodIL.GetLocals();
TypeDesc[] localTypes = new TypeDesc[locals.Length];
for (int i = 0; i < localTypes.Length; i++)
localTypes[i] = locals[i].Type;
_methodCodeNode.InitializeLocalTypes(localTypes);
#endif
_methodCodeNode.InitializeNonRelocationDependencies(_additionalDependencies);
}
private void PublishROData()
{
if (_roDataBlob == null)
{
return;
}
var relocs = _roDataRelocs.ToArray();
Array.Sort(relocs, (x, y) => (x.Offset - y.Offset));
var objectData = new ObjectNode.ObjectData(_roData,
relocs,
_roDataAlignment,
new ISymbolDefinitionNode[] { _roDataBlob });
_roDataBlob.InitializeData(objectData);
}
private void PublishRWData()
{
if (_rwDataBlob == null)
{
return;
}
var relocs = _rwDataRelocs.ToArray();
Array.Sort(relocs, (x, y) => (x.Offset - y.Offset));
var objectData = new ObjectNode.ObjectData(_rwData,
relocs,
_rwDataAlignment,
new ISymbolDefinitionNode[] { _rwDataBlob });
_rwDataBlob.InitializeData(objectData);
}
private MethodDesc MethodBeingCompiled
{
get
{
return _methodCodeNode.Method;
}
}
private int PointerSize
{
get
{
return _compilation.TypeSystemContext.Target.PointerSize;
}
}
private Dictionary<object, GCHandle> _pins = new Dictionary<object, GCHandle>();
private IntPtr GetPin(object obj)
{
GCHandle handle;
if (!_pins.TryGetValue(obj, out handle))
{
handle = GCHandle.Alloc(obj, GCHandleType.Pinned);
_pins.Add(obj, handle);
}
return handle.AddrOfPinnedObject();
}
private List<IntPtr> _nativeExceptions;
private void CompileMethodCleanup()
{
foreach (var pin in _pins)
pin.Value.Free();
_pins.Clear();
if (_nativeExceptions != null)
{
foreach (IntPtr ex in _nativeExceptions)
FreeException(ex);
_nativeExceptions = null;
}
_methodCodeNode = null;
#if READYTORUN
_methodColdCodeNode = null;
#endif
_code = null;
_coldCode = null;
_roData = null;
_roDataBlob = null;
_rwData = null;
_rwDataBlob = null;
_codeRelocs = default(ArrayBuilder<Relocation>);
_roDataRelocs = default(ArrayBuilder<Relocation>);
_rwDataRelocs = default(ArrayBuilder<Relocation>);
#if READYTORUN
_coldCodeRelocs = default(ArrayBuilder<Relocation>);
#endif
_numFrameInfos = 0;
_usedFrameInfos = 0;
_frameInfos = null;
#if READYTORUN
_numColdFrameInfos = 0;
_usedColdFrameInfos = 0;
_coldFrameInfos = null;
#endif
_gcInfo = null;
_ehClauses = null;
_additionalDependencies = null;
#if !READYTORUN
_debugInfo = null;
#endif
_debugLocInfos = null;
_debugVarInfos = null;
_lastException = null;
#if READYTORUN
_inlinedMethods = null;
_actualInstructionSetSupported = default(InstructionSetFlags);
_actualInstructionSetUnsupported = default(InstructionSetFlags);
_precodeFixups = null;
_stashedPrecodeFixups.Clear();
_stashedInlinedMethods.Clear();
_ilBodiesNeeded = null;
_synthesizedPgoDependencies = null;
#endif
_instantiationToJitVisibleInstantiation = null;
_pgoResults.Clear();
// We need to clear out this cache because the next compilation could actually come up
// with a different MethodIL for the same MethodDesc. This happens when we need to replace
// a MethodIL with a throw helper.
_methodILScopeToHandle.Clear();
}
private Dictionary<object, IntPtr> _objectToHandle = new Dictionary<object, IntPtr>(new JitObjectComparer());
private Dictionary<MethodDesc, IntPtr> _methodILScopeToHandle = new Dictionary<MethodDesc, IntPtr>(new JitObjectComparer());
private List<object> _handleToObject = new List<object>();
private const int handleMultiplier = 8;
private const int handleBase = 0x420000;
#if DEBUG
private static readonly IntPtr s_handleHighBitSet = (sizeof(IntPtr) == 4) ? new IntPtr(0x40000000) : new IntPtr(0x4000000000000000);
#endif
private IntPtr ObjectToHandle(object obj)
{
// MethodILScopes need to go through ObjectToHandle(MethodILScope methodIL).
Debug.Assert(obj is not MethodILScope);
return ObjectToHandleUnchecked(obj);
}
private IntPtr ObjectToHandleUnchecked(object obj)
{
// SuperPMI relies on the handle returned from this function being stable for the lifetime of the crossgen2 process
// If handle deletion is implemented, please update SuperPMI
IntPtr handle;
if (!_objectToHandle.TryGetValue(obj, out handle))
{
handle = (IntPtr)(handleMultiplier * _handleToObject.Count + handleBase);
#if DEBUG
handle = new IntPtr((long)s_handleHighBitSet | (long)handle);
#endif
_handleToObject.Add(obj);
_objectToHandle.Add(obj, handle);
}
return handle;
}
private object HandleToObject(void* handle)
{
Debug.Assert(handle != null);
#if DEBUG
handle = (void*)(~s_handleHighBitSet & (nint)handle);
#endif
int index = ((int)handle - handleBase) / handleMultiplier;
return _handleToObject[index];
}
private MethodDesc HandleToObject(CORINFO_METHOD_STRUCT_* method) => (MethodDesc)HandleToObject((void*)method);
private CORINFO_METHOD_STRUCT_* ObjectToHandle(MethodDesc method) => (CORINFO_METHOD_STRUCT_*)ObjectToHandle((object)method);
private TypeDesc HandleToObject(CORINFO_CLASS_STRUCT_* type) => (TypeDesc)HandleToObject((void*)type);
private CORINFO_CLASS_STRUCT_* ObjectToHandle(TypeDesc type) => (CORINFO_CLASS_STRUCT_*)ObjectToHandle((object)type);
private FieldDesc HandleToObject(CORINFO_FIELD_STRUCT_* field) => (FieldDesc)HandleToObject((void*)field);
private CORINFO_FIELD_STRUCT_* ObjectToHandle(FieldDesc field) => (CORINFO_FIELD_STRUCT_*)ObjectToHandle((object)field);
private MethodILScope HandleToObject(CORINFO_MODULE_STRUCT_* module) => (MethodIL)HandleToObject((void*)module);
private MethodSignature HandleToObject(MethodSignatureInfo* method) => (MethodSignature)HandleToObject((void*)method);
private MethodSignatureInfo* ObjectToHandle(MethodSignature method) => (MethodSignatureInfo*)ObjectToHandle((object)method);
private CORINFO_MODULE_STRUCT_* ObjectToHandle(MethodILScope methodIL)
{
// RyuJIT requires CORINFO_MODULE_STRUCT to be unique. MethodILScope might not be unique
// due to ILProvider cache purging. See https://github.com/dotnet/runtime/issues/93843.
MethodDesc owningMethod = methodIL.OwningMethod;
if (!_methodILScopeToHandle.TryGetValue(owningMethod, out IntPtr handle))
_methodILScopeToHandle[owningMethod] = handle = ObjectToHandleUnchecked((object)methodIL);
return (CORINFO_MODULE_STRUCT_*)handle;
}
private bool Get_CORINFO_METHOD_INFO(MethodDesc method, MethodIL methodIL, CORINFO_METHOD_INFO* methodInfo)
{
if (methodIL == null)
{
*methodInfo = default(CORINFO_METHOD_INFO);
return false;
}
methodInfo->ftn = ObjectToHandle(method);
methodInfo->scope = ObjectToHandle(methodIL);
var ilCode = methodIL.GetILBytes();
methodInfo->ILCode = (byte*)GetPin(ilCode);
methodInfo->ILCodeSize = (uint)ilCode.Length;
methodInfo->maxStack = (uint)methodIL.MaxStack;
var exceptionRegions = methodIL.GetExceptionRegions();
methodInfo->EHcount = (uint)exceptionRegions.Length;
methodInfo->options = methodIL.IsInitLocals ? CorInfoOptions.CORINFO_OPT_INIT_LOCALS : (CorInfoOptions)0;
if (method.AcquiresInstMethodTableFromThis())
{
methodInfo->options |= CorInfoOptions.CORINFO_GENERICS_CTXT_FROM_THIS;
}
else if (method.RequiresInstMethodDescArg())
{
methodInfo->options |= CorInfoOptions.CORINFO_GENERICS_CTXT_FROM_METHODDESC;
}
else if (method.RequiresInstMethodTableArg())
{
methodInfo->options |= CorInfoOptions.CORINFO_GENERICS_CTXT_FROM_METHODTABLE;
}
// Indicate this is an async method that requires save and restore
// of async contexts. Regular user implemented runtime async methods
// require this behavior, but thunks should be transparent and should not
// come with this behavior.
if (method.IsAsyncVariant() && method.IsAsync)
{
methodInfo->options |= CorInfoOptions.CORINFO_ASYNC_SAVE_CONTEXTS;
}
methodInfo->regionKind = CorInfoRegionKind.CORINFO_REGION_NONE;
Get_CORINFO_SIG_INFO(method, sig: &methodInfo->args, methodIL);
Get_CORINFO_SIG_INFO(methodIL.GetLocals(), &methodInfo->locals);
return true;
}
private Dictionary<Instantiation, IntPtr[]> _instantiationToJitVisibleInstantiation;
private CORINFO_CLASS_STRUCT_** GetJitInstantiation(Instantiation inst)
{
IntPtr [] jitVisibleInstantiation;
_instantiationToJitVisibleInstantiation ??= new Dictionary<Instantiation, IntPtr[]>();
if (!_instantiationToJitVisibleInstantiation.TryGetValue(inst, out jitVisibleInstantiation))
{
jitVisibleInstantiation = new IntPtr[inst.Length];
for (int i = 0; i < inst.Length; i++)
jitVisibleInstantiation[i] = (IntPtr)ObjectToHandle(inst[i]);
_instantiationToJitVisibleInstantiation.Add(inst, jitVisibleInstantiation);
}
return (CORINFO_CLASS_STRUCT_**)GetPin(jitVisibleInstantiation);
}
private void Get_CORINFO_SIG_INFO(MethodDesc method, CORINFO_SIG_INFO* sig, MethodILScope scope, bool suppressHiddenArgument = false)
{
Get_CORINFO_SIG_INFO(method.Signature, sig, scope);
if (method.IsAsyncCall())
sig->callConv |= CorInfoCallConv.CORINFO_CALLCONV_ASYNCCALL;
// Does the method have a hidden parameter?
bool hasHiddenParameter = !suppressHiddenArgument && method.RequiresInstArg();
if (method.IsIntrinsic)
{
// Some intrinsics will beg to differ about the hasHiddenParameter decision
if (method.IsArrayAddressMethod())
hasHiddenParameter = true;
}
if (hasHiddenParameter)
{
sig->callConv |= CorInfoCallConv.CORINFO_CALLCONV_PARAMTYPE;
}
Instantiation owningTypeInst = method.OwningType.Instantiation;
sig->sigInst.classInstCount = (uint)owningTypeInst.Length;
if (owningTypeInst.Length != 0)
{
sig->sigInst.classInst = GetJitInstantiation(owningTypeInst);
}
sig->sigInst.methInstCount = (uint)method.Instantiation.Length;
if (method.Instantiation.Length != 0)
{
sig->sigInst.methInst = GetJitInstantiation(method.Instantiation);
}
}
private void Get_CORINFO_SIG_INFO(MethodSignature signature, CORINFO_SIG_INFO* sig, MethodILScope scope)
{
sig->callConv = (CorInfoCallConv)(signature.Flags & MethodSignatureFlags.UnmanagedCallingConventionMask);
// Varargs are not supported in .NET Core
if (sig->callConv == CorInfoCallConv.CORINFO_CALLCONV_VARARG)
ThrowHelper.ThrowBadImageFormatException();
if (!signature.IsStatic) sig->callConv |= CorInfoCallConv.CORINFO_CALLCONV_HASTHIS;
if (signature.IsExplicitThis) sig->callConv |= CorInfoCallConv.CORINFO_CALLCONV_EXPLICITTHIS;
if (signature.GenericParameterCount != 0) sig->callConv |= CorInfoCallConv.CORINFO_CALLCONV_GENERIC;
TypeDesc returnType = signature.ReturnType;
CorInfoType corInfoRetType = asCorInfoType(signature.ReturnType, &sig->retTypeClass);
sig->_retType = (byte)corInfoRetType;
sig->retTypeSigClass = ObjectToHandle(signature.ReturnType);
#if READYTORUN
ValidateSafetyOfUsingTypeEquivalenceOfType(signature.ReturnType);
#endif
sig->flags = 0; // used by IL stubs code
sig->numArgs = (ushort)signature.Length;
sig->args = (CORINFO_ARG_LIST_STRUCT_*)0; // CORINFO_ARG_LIST_STRUCT_ is argument index
sig->sigInst.classInst = null; // Not used by the JIT
sig->sigInst.classInstCount = 0; // Not used by the JIT
sig->sigInst.methInst = null;
sig->sigInst.methInstCount = (uint)signature.GenericParameterCount;
sig->pSig = null;
sig->cbSig = 0; // Not used by the JIT
sig->methodSignature = ObjectToHandle(signature);
sig->scope = scope is not null ? ObjectToHandle(scope) : null; // scope can be null for internal calls and COM methods.
sig->token = 0; // Not used by the JIT
}
private void Get_CORINFO_SIG_INFO(LocalVariableDefinition[] locals, CORINFO_SIG_INFO* sig)
{
sig->callConv = CorInfoCallConv.CORINFO_CALLCONV_DEFAULT;
sig->_retType = (byte)CorInfoType.CORINFO_TYPE_VOID;
sig->retTypeClass = null;
sig->retTypeSigClass = null;
sig->flags = CorInfoSigInfoFlags.CORINFO_SIGFLAG_IS_LOCAL_SIG;
sig->numArgs = (ushort)locals.Length;
sig->sigInst.classInst = null;
sig->sigInst.classInstCount = 0;
sig->sigInst.methInst = null;
sig->sigInst.methInstCount = 0;
sig->args = (CORINFO_ARG_LIST_STRUCT_*)0; // CORINFO_ARG_LIST_STRUCT_ is argument index
sig->pSig = null;
sig->cbSig = 0; // Not used by the JIT
sig->methodSignature = (MethodSignatureInfo*)ObjectToHandle(locals);
sig->scope = null; // Not used by the JIT
sig->token = 0; // Not used by the JIT
}
private CorInfoType asCorInfoType(TypeDesc type)
{
return asCorInfoType(type, out _);
}
private CorInfoType asCorInfoType(TypeDesc type, out TypeDesc typeIfNotPrimitive)
{
if (type.IsEnum)
{
type = type.UnderlyingType;
}
if (type.IsPrimitive)
{
typeIfNotPrimitive = null;
Debug.Assert((CorInfoType)TypeFlags.Void == CorInfoType.CORINFO_TYPE_VOID);
Debug.Assert((CorInfoType)TypeFlags.Double == CorInfoType.CORINFO_TYPE_DOUBLE);
return (CorInfoType)type.Category;
}
if (type.IsPointer || type.IsFunctionPointer)
{
typeIfNotPrimitive = null;
return CorInfoType.CORINFO_TYPE_PTR;
}
typeIfNotPrimitive = type;
if (type.IsByRef)
{
return CorInfoType.CORINFO_TYPE_BYREF;
}
if (type.IsValueType)
{
if (_compilation.TypeSystemContext.Target.Architecture == TargetArchitecture.X86)
{
LayoutInt elementSize = type.GetElementSize();
#if READYTORUN
if (elementSize.IsIndeterminate)
{
throw new RequiresRuntimeJitException(type);
}
#endif
}
return CorInfoType.CORINFO_TYPE_VALUECLASS;
}
return CorInfoType.CORINFO_TYPE_CLASS;
}
private CorInfoType asCorInfoType(TypeDesc type, CORINFO_CLASS_STRUCT_** structType)
{
var corInfoType = asCorInfoType(type, out TypeDesc typeIfNotPrimitive);
*structType = (typeIfNotPrimitive != null) ? ObjectToHandle(typeIfNotPrimitive) : null;
return corInfoType;
}
private CORINFO_CONTEXT_STRUCT* contextFromMethod(MethodDesc method)
{
return (CORINFO_CONTEXT_STRUCT*)(((nuint)ObjectToHandle(method)) | (nuint)CorInfoContextFlags.CORINFO_CONTEXTFLAGS_METHOD);
}
private CORINFO_CONTEXT_STRUCT* contextFromType(TypeDesc type)
{
return (CORINFO_CONTEXT_STRUCT*)(((nuint)ObjectToHandle(type)) | (nuint)CorInfoContextFlags.CORINFO_CONTEXTFLAGS_CLASS);
}
private static CORINFO_CONTEXT_STRUCT* contextFromMethodBeingCompiled()
{
return (CORINFO_CONTEXT_STRUCT*)1;
}
private MethodDesc methodFromContext(CORINFO_CONTEXT_STRUCT* contextStruct)
{
if (contextStruct == contextFromMethodBeingCompiled())
{
return MethodBeingCompiled;
}
if (((nuint)contextStruct & (nuint)CorInfoContextFlags.CORINFO_CONTEXTFLAGS_MASK) == (nuint)CorInfoContextFlags.CORINFO_CONTEXTFLAGS_CLASS)
{
return null;
}
else
{
return HandleToObject((CORINFO_METHOD_STRUCT_*)((nuint)contextStruct & ~(nuint)CorInfoContextFlags.CORINFO_CONTEXTFLAGS_MASK));
}
}
private TypeDesc typeFromContext(CORINFO_CONTEXT_STRUCT* contextStruct)
{
if (contextStruct == contextFromMethodBeingCompiled())
{
return MethodBeingCompiled.OwningType;
}
if (((nuint)contextStruct & (nuint)CorInfoContextFlags.CORINFO_CONTEXTFLAGS_MASK) == (nuint)CorInfoContextFlags.CORINFO_CONTEXTFLAGS_CLASS)
{
return HandleToObject((CORINFO_CLASS_STRUCT_*)((nuint)contextStruct & ~(nuint)CorInfoContextFlags.CORINFO_CONTEXTFLAGS_MASK));
}
else
{
return HandleToObject((CORINFO_METHOD_STRUCT_*)((nuint)contextStruct & ~(nuint)CorInfoContextFlags.CORINFO_CONTEXTFLAGS_MASK)).OwningType;
}
}
private TypeSystemEntity entityFromContext(CORINFO_CONTEXT_STRUCT* contextStruct)
{
if (contextStruct == contextFromMethodBeingCompiled())
{
return MethodBeingCompiled.HasInstantiation ? (TypeSystemEntity)MethodBeingCompiled: (TypeSystemEntity)MethodBeingCompiled.OwningType;
}
return (TypeSystemEntity)HandleToObject((void*)((nuint)contextStruct & ~(nuint)CorInfoContextFlags.CORINFO_CONTEXTFLAGS_MASK));
}
private bool isIntrinsic(CORINFO_METHOD_STRUCT_* ftn)
{
MethodDesc method = HandleToObject(ftn);
return method.IsIntrinsic || HardwareIntrinsicHelpers.IsHardwareIntrinsic(method);
}
private uint getMethodAttribsInternal(MethodDesc method)
{
CorInfoFlag result = 0;
if (method.Signature.IsStatic)
result |= CorInfoFlag.CORINFO_FLG_STATIC;
if (method.IsSynchronized)
result |= CorInfoFlag.CORINFO_FLG_SYNCH;
if (method.IsIntrinsic)
result |= CorInfoFlag.CORINFO_FLG_INTRINSIC;
if (method.IsVirtual)
{
result |= CorInfoFlag.CORINFO_FLG_VIRTUAL;
// The JIT only cares about the sealed flag if the method is virtual, or if
// it is a delegate.
// method or class might have the final bit
if (method.IsUnboxingThunk())
{
if (_compilation.IsEffectivelySealed(method.GetUnboxedMethod()))
result |= CorInfoFlag.CORINFO_FLG_FINAL;
}
else
{
if (_compilation.IsEffectivelySealed(method))
result |= CorInfoFlag.CORINFO_FLG_FINAL;
}
}
if (method.IsAbstract)
result |= CorInfoFlag.CORINFO_FLG_ABSTRACT;
if (method.IsConstructor || method.IsStaticConstructor)
result |= CorInfoFlag.CORINFO_FLG_CONSTRUCTOR;
//
// See if we need to embed a .cctor call at the head of the
// method body.
//
if (method.IsSharedByGenericInstantiations)
result |= CorInfoFlag.CORINFO_FLG_SHAREDINST;
if (method.IsPInvoke)
result |= CorInfoFlag.CORINFO_FLG_PINVOKE;
#if READYTORUN
if (method.RequireSecObject)
{
result |= CorInfoFlag.CORINFO_FLG_DONT_INLINE_CALLER;
}
#endif
if (method.IsAggressiveOptimization)
{
result |= CorInfoFlag.CORINFO_FLG_AGGRESSIVE_OPT;
}
// TODO: Cache inlining hits
// Check for an inlining directive.
if (method.IsNoInlining || method.IsNoOptimization)
{
// NoOptimization implies NoInlining.
result |= CorInfoFlag.CORINFO_FLG_DONT_INLINE;
}
else if (method.IsAggressiveInlining)
{
result |= CorInfoFlag.CORINFO_FLG_FORCEINLINE;
}
if (method.OwningType.IsDelegate && method.Name.SequenceEqual("Invoke"u8))
{
// This is now used to emit efficient invoke code for any delegate invoke,
// including multicast.
result |= CorInfoFlag.CORINFO_FLG_DELEGATE_INVOKE;
// RyuJIT special cases this method; it would assert if it's not final
// and we might not have set the bit in the code above.
result |= CorInfoFlag.CORINFO_FLG_FINAL;
}
#if READYTORUN
// Check for SIMD intrinsics
if (method.Context.Target.MaximumSimdVectorLength == SimdVectorLength.None)
{
DefType owningDefType = method.OwningType as DefType;
if (owningDefType != null && VectorOfTFieldLayoutAlgorithm.IsVectorOfTType(owningDefType))
{
throw new RequiresRuntimeJitException("This function is using SIMD intrinsics, their size is machine specific");
}
}
#endif
// Check for hardware intrinsics
if (HardwareIntrinsicHelpers.IsHardwareIntrinsic(method))
{
result |= CorInfoFlag.CORINFO_FLG_INTRINSIC;
}
// Internal calls typically turn into fcalls that do not always
// probe for GC. Be conservative here and always let JIT know that
// this method may not do GC checks so the JIT might need to make
// callers fully interruptible.
if (method.IsInternalCall)
{
result |= CorInfoFlag.CORINFO_FLG_NOGCCHECK;
}
return (uint)result;
}
#pragma warning disable CA1822 // Mark members as static
private void setMethodAttribs(CORINFO_METHOD_STRUCT_* ftn, CorInfoMethodRuntimeFlags attribs)
#pragma warning restore CA1822 // Mark members as static
{
// TODO: Inlining
}
private void getMethodSig(CORINFO_METHOD_STRUCT_* ftn, CORINFO_SIG_INFO* sig, CORINFO_CLASS_STRUCT_* memberParent)
{
MethodDesc method = HandleToObject(ftn);
// There might be a more concrete parent type specified - this can happen when inlining.
if (memberParent != null)
{
TypeDesc type = HandleToObject(memberParent);
// Typically, the owning type of the method is a canonical type and the member parent
// supplied by RyuJIT is a concrete instantiation.
if (type != method.OwningType)
{
if (type.IsArray)
{
method = ((ArrayType)type).GetArrayMethod(((ArrayMethod)method).Kind);
}
else
{
Debug.Assert(type.HasSameTypeDefinition(method.OwningType));
Instantiation methodInst = method.Instantiation;
method = _compilation.TypeSystemContext.GetMethodForInstantiatedType(method.GetTypicalMethodDefinition(), (InstantiatedType)type);
if (methodInst.Length > 0)
{
method = method.MakeInstantiatedMethod(methodInst);
}
}
}
}
Get_CORINFO_SIG_INFO(method, sig: sig, scope: null);
}
private bool getMethodInfo(CORINFO_METHOD_STRUCT_* ftn, CORINFO_METHOD_INFO* info, CORINFO_CONTEXT_STRUCT* context)
{
MethodDesc method = HandleToObject(ftn);
if (context != null && method.IsSharedByGenericInstantiations)
{
TypeSystemEntity ctx = entityFromContext(context);
if (ctx is MethodDesc methodFromCtx && context != contextFromMethodBeingCompiled())
{
Debug.Assert(method.GetTypicalMethodDefinition() == methodFromCtx.GetTypicalMethodDefinition());
method = methodFromCtx;
}
else if (ctx is InstantiatedType instantiatedCtxType)
{
MethodDesc instantiatedMethod = _compilation.TypeSystemContext.GetMethodForInstantiatedType(method.GetTypicalMethodDefinition(), instantiatedCtxType);
if (method.HasInstantiation)
{
instantiatedMethod = _compilation.TypeSystemContext.GetInstantiatedMethod(instantiatedMethod, method.Instantiation);
}
method = instantiatedMethod;
}
}
// 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 = method.IsUnboxingThunk() ? null : _compilation.GetMethodIL(method);
return Get_CORINFO_METHOD_INFO(method, methodIL, info);
}
private bool haveSameMethodDefinition(CORINFO_METHOD_STRUCT_* methHnd1, CORINFO_METHOD_STRUCT_* methHnd2)
{
MethodDesc meth1 = HandleToObject(methHnd1);
MethodDesc meth2 = HandleToObject(methHnd2);
return meth1.GetTypicalMethodDefinition() == meth2.GetTypicalMethodDefinition();
}
private CORINFO_CLASS_STRUCT_* getTypeDefinition(CORINFO_CLASS_STRUCT_* type)
{
return ObjectToHandle(HandleToObject(type).GetTypeDefinition());
}
private CorInfoInline canInline(CORINFO_METHOD_STRUCT_* callerHnd, CORINFO_METHOD_STRUCT_* calleeHnd)
{
MethodDesc callerMethod = HandleToObject(callerHnd);
MethodDesc calleeMethod = HandleToObject(calleeHnd);
EcmaModule rootModule = (MethodBeingCompiled.OwningType as MetadataType)?.Module as EcmaModule;
EcmaModule calleeModule = (calleeMethod.OwningType as MetadataType)?.Module as EcmaModule;
// If this inline crosses module boundaries, ensure the modules agree on exception wrapping behavior.
if ((rootModule != calleeModule) && (rootModule != null) && (calleeModule != null))
{
if (rootModule.IsWrapNonExceptionThrows != calleeModule.IsWrapNonExceptionThrows)
{
var calleeIL = _compilation.GetMethodIL(calleeMethod);
if (calleeIL.GetExceptionRegions().Length != 0)
{
// Fail inlining if root method and callee have different exception wrapping behavior
return CorInfoInline.INLINE_FAIL;
}
}
}
if (_compilation.CanInline(callerMethod, calleeMethod))
{
// No restrictions on inlining
return CorInfoInline.INLINE_PASS;
}
else
{
// Call may not be inlined
//
// Note despite returning INLINE_NEVER here, in compilations where jitting is possible
// the jit may still be able to inline this method. So we rely on reportInliningDecision
// to not propagate this into metadata to short-circuit future inlining attempts.
return CorInfoInline.INLINE_NEVER;
}
}
#pragma warning disable CA1822 // Mark members as static
private void reportTailCallDecision(CORINFO_METHOD_STRUCT_* callerHnd, CORINFO_METHOD_STRUCT_* calleeHnd, bool fIsTailPrefix, CorInfoTailCall tailCallResult, byte* reason)
#pragma warning restore CA1822 // Mark members as static
{
}
private void getEHinfo(CORINFO_METHOD_STRUCT_* ftn, uint EHnumber, ref CORINFO_EH_CLAUSE clause)
{
var methodIL = _compilation.GetMethodIL(HandleToObject(ftn));
var ehRegion = methodIL.GetExceptionRegions()[EHnumber];
clause.Flags = (CORINFO_EH_CLAUSE_FLAGS)ehRegion.Kind;
clause.TryOffset = (uint)ehRegion.TryOffset;
clause.TryLength = (uint)ehRegion.TryLength;
clause.HandlerOffset = (uint)ehRegion.HandlerOffset;
clause.HandlerLength = (uint)ehRegion.HandlerLength;
clause.ClassTokenOrOffset = (uint)((ehRegion.Kind == ILExceptionRegionKind.Filter) ? ehRegion.FilterOffset : ehRegion.ClassToken);
}
private CORINFO_CLASS_STRUCT_* getMethodClass(CORINFO_METHOD_STRUCT_* method)
{
var m = HandleToObject(method);
return ObjectToHandle(m.OwningType);
}
private bool resolveVirtualMethod(CORINFO_DEVIRTUALIZATION_INFO* info)
{
// Initialize OUT fields
info->devirtualizedMethod = null;
info->tokenLookupContext = null;
info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_UNKNOWN;
info->instParamLookup = default(CORINFO_LOOKUP);
TypeDesc objType = HandleToObject(info->objClass);
// __Canon cannot be devirtualized
if (objType.IsCanonicalDefinitionType(CanonicalFormKind.Any))
{
info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_CANON;
return false;
}
MethodDesc decl = HandleToObject(info->virtualMethod);
// Transform from the unboxing thunk to the normal method
decl = decl.IsUnboxingThunk() ? decl.GetUnboxedMethod() : decl;
if ((info->context != null) && decl.OwningType.IsInterface)
{
TypeDesc ownerTypeDesc = typeFromContext(info->context);
if (decl.OwningType != ownerTypeDesc)
{
Debug.Assert(ownerTypeDesc is InstantiatedType);
decl = _compilation.TypeSystemContext.GetMethodForInstantiatedType(decl.GetTypicalMethodDefinition(), (InstantiatedType)ownerTypeDesc);
}
}
MethodDesc originalImpl = _compilation.ResolveVirtualMethod(decl, objType, out info->detail);
if (originalImpl == null)
{
// If this assert fires, we failed to devirtualize, probably due to a failure to resolve the
// virtual to an exact target. This should never happen in practice if the input IL is valid,
// and the algorithm for virtual function resolution is correct; however, if it does, this is
// a safe condition, and we could delete this assert. This assert exists in order to help identify
// cases where the virtual function resolution algorithm either does not function, or is not used
// correctly.
#if DEBUG
if (info->detail == CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_UNKNOWN)
{
Console.Error.WriteLine($"Failed devirtualization with unexpected unknown failure while compiling {MethodBeingCompiled} with decl {decl} targeting type {objType}");
Debug.Assert(info->detail != CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_UNKNOWN);
}
#endif
return false;
}
TypeDesc owningType = originalImpl.OwningType;
// RyuJIT expects to get the canonical form back
MethodDesc impl = originalImpl.GetCanonMethodTarget(CanonicalFormKind.Specific);
bool unboxingStub = impl.OwningType.IsValueType;
MethodDesc nonUnboxingImpl = impl;
if (unboxingStub)
{
impl = getUnboxingThunk(impl);
}
#if READYTORUN
// As there are a variety of situations where the resolved virtual method may be different at compile and runtime (primarily due to subtle differences
// in the virtual resolution algorithm between the runtime and the compiler, although details such as whether or not type equivalence is enabled
// can also have an effect), record any decisions made, and if there are differences, simply skip use of the compiled method.
var resolver = _compilation.NodeFactory.Resolver;
MethodWithToken methodWithTokenDecl;
if (info->pResolvedTokenVirtualMethod != null)
{
methodWithTokenDecl = ComputeMethodWithToken(decl, ref *info->pResolvedTokenVirtualMethod, null, false);
}
else
{
ModuleToken declToken = resolver.GetModuleTokenForMethod(decl.GetTypicalMethodDefinition(), allowDynamicallyCreatedReference: false, throwIfNotFound: false);
if (declToken.IsNull)
{
info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_DECL_NOT_REPRESENTABLE;
return false;
}
if (!_compilation.CompilationModuleGroup.VersionsWithTypeReference(decl.OwningType))
{
info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_FAILED_DECL_NOT_REPRESENTABLE;
return false;
}
methodWithTokenDecl = new MethodWithToken(decl, declToken, null, false, null, devirtualizedMethodOwner: decl.OwningType);
}
MethodWithToken methodWithTokenImpl;
#endif
if (decl == originalImpl)
{
#if READYTORUN
methodWithTokenImpl = methodWithTokenDecl;
#endif
if (info->pResolvedTokenVirtualMethod != null)
{
info->resolvedTokenDevirtualizedMethod = *info->pResolvedTokenVirtualMethod;
}
else
{
info->resolvedTokenDevirtualizedMethod = CreateResolvedTokenFromMethod(this, decl
#if READYTORUN
, methodWithTokenDecl
#endif
);
}
}
else
{
#if READYTORUN
methodWithTokenImpl = new MethodWithToken(nonUnboxingImpl, resolver.GetModuleTokenForMethod(nonUnboxingImpl.GetTypicalMethodDefinition(), allowDynamicallyCreatedReference: false, throwIfNotFound: true), null, unboxingStub, null, devirtualizedMethodOwner: impl.OwningType);
#endif
info->resolvedTokenDevirtualizedMethod = CreateResolvedTokenFromMethod(this, impl
#if READYTORUN
, methodWithTokenImpl
#endif
);
}
if (unboxingStub)
{
info->resolvedTokenDevirtualizedUnboxedMethod = info->resolvedTokenDevirtualizedMethod;
info->resolvedTokenDevirtualizedUnboxedMethod.tokenContext = contextFromMethod(nonUnboxingImpl);
info->resolvedTokenDevirtualizedUnboxedMethod.hMethod = ObjectToHandle(nonUnboxingImpl);
}
else
{
info->resolvedTokenDevirtualizedUnboxedMethod = default(CORINFO_RESOLVED_TOKEN);
}
#if READYTORUN
// Testing has not shown that concerns about virtual matching are significant
// Only generate verification for builds with the stress mode enabled
if (_compilation.SymbolNodeFactory.VerifyTypeAndFieldLayout)
{
if (!methodWithTokenDecl.Method.OwningType.IsValueType || !methodWithTokenImpl.Method.OwningType.IsValueType)
{
ISymbolNode virtualResolutionNode = _compilation.SymbolNodeFactory.CheckVirtualFunctionOverride(methodWithTokenDecl, objType, methodWithTokenImpl);
AddPrecodeFixup(virtualResolutionNode);
}
}
#endif
info->detail = CORINFO_DEVIRTUALIZATION_DETAIL.CORINFO_DEVIRTUALIZATION_SUCCESS;
info->devirtualizedMethod = ObjectToHandle(impl);
info->tokenLookupContext = contextFromType(owningType);
return true;
static CORINFO_RESOLVED_TOKEN CreateResolvedTokenFromMethod(CorInfoImpl jitInterface, MethodDesc method
#if READYTORUN
, MethodWithToken methodWithToken
#endif
)
{
#if !READYTORUN
MethodDesc unboxedMethodDesc = method.IsUnboxingThunk() ? method.GetUnboxedMethod() : method;
var methodWithToken = new
{
Method = unboxedMethodDesc,
OwningType = unboxedMethodDesc.OwningType,
};
#endif
CORINFO_RESOLVED_TOKEN result = default(CORINFO_RESOLVED_TOKEN);
MethodILScope scope = jitInterface._compilation.GetMethodIL(methodWithToken.Method);
scope ??= EcmaMethodILScope.Create((EcmaMethod)methodWithToken.Method.GetTypicalMethodDefinition());
result.tokenScope = jitInterface.ObjectToHandle(scope);
result.tokenContext = jitInterface.contextFromMethod(method);
#if READYTORUN
result.token = methodWithToken.Token.Token;
if (methodWithToken.Token.TokenType != CorTokenType.mdtMethodDef)
{
Debug.Assert(false); // This should never happen, but we protect against total failure with the throw below.
throw new RequiresRuntimeJitException("Attempt to devirtualize and unable to create token for devirtualized method");
}
#else
result.token = (mdToken)0x06BAAAAD;
#endif
result.tokenType = CorInfoTokenKind.CORINFO_TOKENKIND_DevirtualizedMethod;
result.hClass = jitInterface.ObjectToHandle(methodWithToken.OwningType);
result.hMethod = jitInterface.ObjectToHandle(method);
return result;
}
}
private CORINFO_METHOD_STRUCT_* getUnboxedEntry(CORINFO_METHOD_STRUCT_* ftn, ref bool requiresInstMethodTableArg)
{
MethodDesc result = null;
requiresInstMethodTableArg = false;
MethodDesc method = HandleToObject(ftn);
if (method.IsUnboxingThunk())
{
result = method.GetUnboxedMethod();
requiresInstMethodTableArg = method.RequiresInstMethodTableArg();
}
return result != null ? ObjectToHandle(result) : null;
}
private CORINFO_METHOD_STRUCT_* getInstantiatedEntry(CORINFO_METHOD_STRUCT_* ftn, CORINFO_METHOD_STRUCT_** methodArg, CORINFO_CLASS_STRUCT_** classArg)
{
*methodArg = null;
*classArg = null;
return null;
}
private CORINFO_METHOD_STRUCT_* getAsyncOtherVariant(CORINFO_METHOD_STRUCT_* ftn, ref bool variantIsThunk)
{
MethodDesc method = HandleToObject(ftn);
if (method.IsAsyncVariant())
{
method = method.GetTargetOfAsyncVariant();
}
else if (method.Signature.ReturnsTaskOrValueTask())
{
method = method.GetAsyncVariant();
}
else
{
variantIsThunk = false;
return null;
}
variantIsThunk = method?.IsAsyncThunk() ?? false;
return ObjectToHandle(method);
}
private CORINFO_CLASS_STRUCT_* getDefaultComparerClass(CORINFO_CLASS_STRUCT_* elemType)
{
TypeDesc comparand = HandleToObject(elemType);
TypeDesc comparer = IL.Stubs.ComparerIntrinsics.GetComparerForType(comparand);
return comparer != null ? ObjectToHandle(comparer) : null;
}
private CORINFO_CLASS_STRUCT_* getDefaultEqualityComparerClass(CORINFO_CLASS_STRUCT_* elemType)
{
TypeDesc comparand = HandleToObject(elemType);
TypeDesc comparer = IL.Stubs.ComparerIntrinsics.GetEqualityComparerForType(comparand);
return comparer != null ? ObjectToHandle(comparer) : null;
}
private CORINFO_CLASS_STRUCT_* getSZArrayHelperEnumeratorClass(CORINFO_CLASS_STRUCT_* elemType)
{
TypeDesc elementType = HandleToObject(elemType);
MetadataType placeholderType = _compilation.TypeSystemContext.SystemModule.GetType("System"u8, "SZGenericArrayEnumerator`1"u8, throwIfNotFound: false);
if (placeholderType == null)
{
return null;
}
return ObjectToHandle(placeholderType.MakeInstantiatedType(elementType));
}
private bool isIntrinsicType(CORINFO_CLASS_STRUCT_* classHnd)
{
TypeDesc type = HandleToObject(classHnd);
return type.IsIntrinsic;
}
private CorInfoCallConvExtension getUnmanagedCallConv(CORINFO_METHOD_STRUCT_* method, CORINFO_SIG_INFO* sig, ref bool pSuppressGCTransition)
{
pSuppressGCTransition = false;
if (method != null)
{
MethodDesc methodDesc = HandleToObject(method);
CorInfoCallConvExtension callConv = GetUnmanagedCallConv(HandleToObject(method), out pSuppressGCTransition);
return callConv;
}
else
{
Debug.Assert(sig != null);
CorInfoCallConvExtension callConv = GetUnmanagedCallConv(HandleToObject(sig->methodSignature), out pSuppressGCTransition);
return callConv;
}
}
private static CorInfoCallConvExtension GetUnmanagedCallConv(MethodDesc methodDesc, out bool suppressGCTransition)
{
UnmanagedCallingConventions callingConventions;
if ((methodDesc.Signature.Flags & MethodSignatureFlags.UnmanagedCallingConventionMask) == 0)
{
if (methodDesc.IsPInvoke)
{
callingConventions = methodDesc.GetPInvokeMethodCallingConventions();
}
else
{
Debug.Assert(methodDesc.IsUnmanagedCallersOnly);
callingConventions = methodDesc.GetUnmanagedCallersOnlyMethodCallingConventions();
}
}
else
{
callingConventions = methodDesc.Signature.GetStandaloneMethodSignatureCallingConventions();
}
return ToCorInfoCallConvExtension(callingConventions, out suppressGCTransition);
}
private static CorInfoCallConvExtension GetUnmanagedCallConv(MethodSignature signature, out bool suppressGCTransition)
{
return ToCorInfoCallConvExtension(signature.GetStandaloneMethodSignatureCallingConventions(), out suppressGCTransition);
}
private static CorInfoCallConvExtension ToCorInfoCallConvExtension(UnmanagedCallingConventions callConvs, out bool suppressGCTransition)
{
CorInfoCallConvExtension result;
switch (callConvs & UnmanagedCallingConventions.CallingConventionMask)
{
case UnmanagedCallingConventions.Cdecl:
result = CorInfoCallConvExtension.C;
break;
case UnmanagedCallingConventions.Stdcall:
result = CorInfoCallConvExtension.Stdcall;
break;
case UnmanagedCallingConventions.Thiscall:
result = CorInfoCallConvExtension.Thiscall;
break;
case UnmanagedCallingConventions.Fastcall:
result = CorInfoCallConvExtension.Fastcall;
break;
case UnmanagedCallingConventions.Swift:
result = CorInfoCallConvExtension.Swift;
break;
default:
ThrowHelper.ThrowInvalidProgramException();
result = CorInfoCallConvExtension.Managed; // unreachable
break;
}
if ((callConvs & UnmanagedCallingConventions.IsMemberFunction) != 0)
{
result = result switch
{
CorInfoCallConvExtension.C => CorInfoCallConvExtension.CMemberFunction,
CorInfoCallConvExtension.Stdcall => CorInfoCallConvExtension.StdcallMemberFunction,
CorInfoCallConvExtension.Fastcall => CorInfoCallConvExtension.FastcallMemberFunction,
_ => result,
};
}
suppressGCTransition = (callConvs & UnmanagedCallingConventions.IsSuppressGcTransition) != 0;
return result;
}
private bool satisfiesMethodConstraints(CORINFO_CLASS_STRUCT_* parent, CORINFO_METHOD_STRUCT_* method)
{ throw new NotImplementedException("satisfiesMethodConstraints"); }
private void setPatchpointInfo(PatchpointInfo* patchpointInfo)
{ throw new NotImplementedException("setPatchpointInfo"); }
private PatchpointInfo* getOSRInfo(ref uint ilOffset)
{ throw new NotImplementedException("getOSRInfo"); }
#pragma warning disable CA1822 // Mark members as static
private void methodMustBeLoadedBeforeCodeIsRun(CORINFO_METHOD_STRUCT_* method)
#pragma warning restore CA1822 // Mark members as static
{
}
private static object ResolveTokenWithSubstitution(MethodILScope methodIL, mdToken token, Instantiation typeInst, Instantiation methodInst)
{
// Grab the generic definition of the method IL, resolve the token within the definition,
// and instantiate it with the given context.
object result = methodIL.GetMethodILScopeDefinition().GetObject((int)token);
if (result is MethodDesc methodResult)
{
result = methodResult.InstantiateSignature(typeInst, methodInst);
}
else if (result is FieldDesc fieldResult)
{
result = fieldResult.InstantiateSignature(typeInst, methodInst);
}
else
{
result = ((TypeDesc)result).InstantiateSignature(typeInst, methodInst);
}
return result;
}
private static object ResolveTokenInScope(MethodILScope methodIL, object typeOrMethodContext, mdToken token)
{
MethodDesc owningMethod = methodIL.OwningMethod;
// If token context differs from the scope, it means we're inlining.
// If we're inlining a shared method body, we might be able to un-share
// the referenced token and avoid runtime lookups.
// Resolve the token in the inlining context.
object result;
if (owningMethod != typeOrMethodContext &&
owningMethod.IsCanonicalMethod(CanonicalFormKind.Any))
{
Instantiation methodInst = default;
Instantiation typeInst;
if (typeOrMethodContext is TypeDesc typeContext)
{
Debug.Assert(typeContext.HasSameTypeDefinition(owningMethod.OwningType) || typeContext.IsArray);
typeInst = typeContext.Instantiation;
}
else
{
var methodContext = (MethodDesc)typeOrMethodContext;
// Allow cases where the method's do not have instantiations themselves, if
// 1. The method defining the context is generic, but the target method is not
// 2. Both methods are not generic
// 3. The methods are the same generic
// AND
// The methods are on the same type
Debug.Assert((methodContext.HasInstantiation && !owningMethod.HasInstantiation) ||
(!methodContext.HasInstantiation && !owningMethod.HasInstantiation) ||
methodContext.GetTypicalMethodDefinition() == owningMethod.GetTypicalMethodDefinition() ||
(owningMethod.Name.SequenceEqual("CreateDefaultInstance"u8) && methodContext.Name.SequenceEqual("CreateInstance"u8)));
Debug.Assert(methodContext.OwningType.HasSameTypeDefinition(owningMethod.OwningType));
typeInst = methodContext.OwningType.Instantiation;
methodInst = methodContext.Instantiation;
}
result = ResolveTokenWithSubstitution(methodIL, token, typeInst, methodInst);
}
else
{
// Not inlining - just resolve the token within the methodIL
result = methodIL.GetObject((int)token);
}
return result;
}
private object GetRuntimeDeterminedObjectForToken(ref CORINFO_RESOLVED_TOKEN pResolvedToken)
{
// Since RyuJIT operates on canonical types (as opposed to runtime determined ones), but the
// dependency analysis operates on runtime determined ones, we convert the resolved token
// to the runtime determined form (e.g. Foo<__Canon> becomes Foo<T__Canon>).
var methodIL = HandleToObject(pResolvedToken.tokenScope);
var typeOrMethodContext = (pResolvedToken.tokenContext == contextFromMethodBeingCompiled()) ?
MethodBeingCompiled : HandleToObject((void*)pResolvedToken.tokenContext);
object result = GetRuntimeDeterminedObjectForToken(methodIL, typeOrMethodContext, pResolvedToken.token);
if (pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_Newarr)
result = ((TypeDesc)result).MakeArrayType();
if (pResolvedToken.tokenType is CorInfoTokenKind.CORINFO_TOKENKIND_Await)
result = _compilation.TypeSystemContext.GetAsyncVariantMethod((MethodDesc)result);
return result;
}
private static object GetRuntimeDeterminedObjectForToken(MethodILScope methodIL, object typeOrMethodContext, mdToken token)
{
object result = ResolveTokenInScope(methodIL, typeOrMethodContext, token);
if (result is MethodDesc method)
{
if (method.IsSharedByGenericInstantiations)
{
MethodDesc sharedMethod = methodIL.OwningMethod.GetSharedRuntimeFormMethodTarget();
result = ResolveTokenWithSubstitution(methodIL, token, sharedMethod.OwningType.Instantiation, sharedMethod.Instantiation);
Debug.Assert(((MethodDesc)result).IsRuntimeDeterminedExactMethod);
}
}
else if (result is FieldDesc field)
{
if (field.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any))
{
MethodDesc sharedMethod = methodIL.OwningMethod.GetSharedRuntimeFormMethodTarget();
result = ResolveTokenWithSubstitution(methodIL, token, sharedMethod.OwningType.Instantiation, sharedMethod.Instantiation);
Debug.Assert(((FieldDesc)result).OwningType.IsRuntimeDeterminedSubtype);
}
}
else
{
TypeDesc type = (TypeDesc)result;
if (type.IsCanonicalSubtype(CanonicalFormKind.Any))
{
MethodDesc sharedMethod = methodIL.OwningMethod.GetSharedRuntimeFormMethodTarget();
result = ResolveTokenWithSubstitution(methodIL, token, sharedMethod.OwningType.Instantiation, sharedMethod.Instantiation);
Debug.Assert(((TypeDesc)result).IsRuntimeDeterminedSubtype ||
/* If the resolved type is not runtime determined there's a chance we went down this path
because there was a literal typeof(__Canon) in the compiled IL - check for that
by resolving the token in the definition. */
((TypeDesc)methodIL.GetMethodILScopeDefinition().GetObject((int)token)).IsCanonicalDefinitionType(CanonicalFormKind.Any));
}
}
return result;
}
private void resolveToken(ref CORINFO_RESOLVED_TOKEN pResolvedToken)
{
var methodIL = HandleToObject(pResolvedToken.tokenScope);
var typeOrMethodContext = (pResolvedToken.tokenContext == contextFromMethodBeingCompiled()) ?
MethodBeingCompiled : HandleToObject((void*)pResolvedToken.tokenContext);
object result = ResolveTokenInScope(methodIL, typeOrMethodContext, pResolvedToken.token);
pResolvedToken.hClass = null;
pResolvedToken.hMethod = null;
pResolvedToken.hField = null;
#if READYTORUN
TypeDesc owningType = methodIL.OwningMethod.GetTypicalMethodDefinition().OwningType;
bool recordToken;
if (!_compilation.CompilationModuleGroup.VersionsWithMethodBody(methodIL.OwningMethod.GetTypicalMethodDefinition()))
{
recordToken = (methodIL.GetMethodILScopeDefinition() is IMethodTokensAreUseableInCompilation) && owningType is EcmaType;
}
else
{
recordToken = (_compilation.CompilationModuleGroup.VersionsWithType(owningType) || _compilation.CompilationModuleGroup.CrossModuleInlineableType(owningType)) && owningType is EcmaType;
recordToken &= methodIL.GetMethodILScopeDefinition() is IMethodTokensAreUseableInCompilation || methodIL.GetMethodILScopeDefinition() is IEcmaMethodIL;
}
#endif
if (result is MethodDesc method)
{
#if !SUPPORT_JIT
_compilation.TypeSystemContext.EnsureLoadableMethod(method);
#endif
#if READYTORUN
if (recordToken)
{
ModuleToken methodModuleToken = HandleToModuleToken(ref pResolvedToken, out bool strippedInstantiation);
Debug.Assert(!strippedInstantiation);
var resolver = _compilation.NodeFactory.Resolver;
resolver.AddModuleTokenForMethod(method, methodModuleToken);
ValidateSafetyOfUsingTypeEquivalenceInSignature(method.Signature);
}
#else
_compilation.NodeFactory.MetadataManager.GetDependenciesDueToAccess(ref _additionalDependencies, _compilation.NodeFactory, (MethodIL)methodIL, method);
#endif
if (pResolvedToken.tokenType is CorInfoTokenKind.CORINFO_TOKENKIND_Await)
{
// in rare cases a method that returns Task is not actually TaskReturning (i.e. returns T).
// we cannot resolve to an Async variant in such case.
// return NULL, so that caller would re-resolve as a regular method call
bool allowAsyncVariant = method.GetTypicalMethodDefinition().Signature.ReturnsTaskOrValueTask();
// Don't get async variant of Delegate.Invoke method; the pointed to method is not an async variant either.
allowAsyncVariant = allowAsyncVariant && !method.OwningType.IsDelegate;
method = allowAsyncVariant
? _compilation.TypeSystemContext.GetAsyncVariantMethod(method)
: null;
}
if (method != null)
{
pResolvedToken.hMethod = ObjectToHandle(method);
pResolvedToken.hClass = ObjectToHandle(method.OwningType);
}
else
{
pResolvedToken.hMethod = null;
pResolvedToken.hClass = null;
}
}
else
if (result is FieldDesc)
{
FieldDesc field = result as FieldDesc;
// References to literal fields from IL body should never resolve.
// The CLR would throw a MissingFieldException while jitting and so should we.
if (field.IsLiteral)
ThrowHelper.ThrowMissingFieldException(field.OwningType, field.GetName());
pResolvedToken.hField = ObjectToHandle(field);
TypeDesc owningClass = field.OwningType;
pResolvedToken.hClass = ObjectToHandle(owningClass);
#if !SUPPORT_JIT
_compilation.TypeSystemContext.EnsureLoadableType(owningClass);
#endif
#if !READYTORUN
_compilation.NodeFactory.MetadataManager.GetDependenciesDueToAccess(ref _additionalDependencies, _compilation.NodeFactory, (MethodIL)methodIL, field);
#else
ValidateSafetyOfUsingTypeEquivalenceOfType(field.FieldType);
#endif
}
else
{
TypeDesc type = (TypeDesc)result;
#if READYTORUN
if (recordToken)
{
_compilation.NodeFactory.Resolver.AddModuleTokenForType(type, HandleToModuleToken(ref pResolvedToken, out bool strippedInstantiation));
Debug.Assert(!strippedInstantiation);
}
#endif
if (pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_Newarr)
{
if (type.IsVoid)
ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramSpecific, methodIL.OwningMethod);
type = type.MakeArrayType();
}
pResolvedToken.hClass = ObjectToHandle(type);
#if !SUPPORT_JIT
_compilation.TypeSystemContext.EnsureLoadableType(type);
#endif
}
pResolvedToken.pTypeSpec = null;
pResolvedToken.cbTypeSpec = 0;
pResolvedToken.pMethodSpec = null;
pResolvedToken.cbMethodSpec = 0;
}
private void findSig(CORINFO_MODULE_STRUCT_* module, uint sigTOK, CORINFO_CONTEXT_STRUCT* context, CORINFO_SIG_INFO* sig)
{
var methodIL = HandleToObject(module);
var methodSig = (MethodSignature)methodIL.GetObject((int)sigTOK);
Get_CORINFO_SIG_INFO(methodSig, sig, methodIL);
#if !READYTORUN
// Check whether we need to report this as a fat pointer call
if (_compilation.IsFatPointerCandidate(methodIL.OwningMethod, methodSig))
{
sig->flags |= CorInfoSigInfoFlags.CORINFO_SIGFLAG_FAT_CALL;
}
#else
VerifyMethodSignatureIsStable(methodSig);
#endif
}
private void findCallSiteSig(CORINFO_MODULE_STRUCT_* module, uint methTOK, CORINFO_CONTEXT_STRUCT* context, CORINFO_SIG_INFO* sig)
{
var methodIL = HandleToObject(module);
Get_CORINFO_SIG_INFO(((MethodDesc)methodIL.GetObject((int)methTOK)), sig: sig, methodIL);
}
private CORINFO_CLASS_STRUCT_* getTokenTypeAsHandle(ref CORINFO_RESOLVED_TOKEN pResolvedToken)
{
WellKnownType result = WellKnownType.RuntimeTypeHandle;
if (pResolvedToken.hMethod != null)
{
result = WellKnownType.RuntimeMethodHandle;
}
else
if (pResolvedToken.hField != null)
{
result = WellKnownType.RuntimeFieldHandle;
}
return ObjectToHandle(_compilation.TypeSystemContext.GetWellKnownType(result));
}
private static CorInfoCanSkipVerificationResult canSkipVerification(CORINFO_MODULE_STRUCT_* module)
{
return CorInfoCanSkipVerificationResult.CORINFO_VERIFICATION_CAN_SKIP;
}
private int getStringLiteral(CORINFO_MODULE_STRUCT_* module, uint metaTOK, char* buffer, int size, int startIndex)
{
Debug.Assert(size >= 0);
Debug.Assert(startIndex >= 0);
MethodILScope methodIL = HandleToObject(module);
string str = (string)methodIL.GetObject((int)metaTOK);
int result = (str.Length >= startIndex) ? (str.Length - startIndex) : 0;
if (buffer != null && result != 0)
{
// Copy str's content to buffer
str.AsSpan(startIndex, Math.Min(size, result)).CopyTo(new Span<char>(buffer, size));
}
return result;
}
private nuint printObjectDescription(CORINFO_OBJECT_STRUCT_* handle, byte* buffer, nuint bufferSize, nuint* pRequiredBufferSize)
{
Debug.Assert(handle != null);
return PrintFromUtf16(HandleToObject(handle).ToString(), buffer, bufferSize, pRequiredBufferSize);
}
internal static nuint PrintFromUtf16(ReadOnlySpan<char> utf16, byte* buffer, nuint bufferSize, nuint* pRequiredBufferSize)
{
int written = 0;
if (bufferSize > 0)
{
OperationStatus status = Utf8.FromUtf16(utf16, new Span<byte>(buffer, checked((int)(bufferSize - 1))), out _, out written);
// Always null-terminate
buffer[written] = 0;
if (status == OperationStatus.Done)
{
if (pRequiredBufferSize != null)
{
*pRequiredBufferSize = (nuint)written + 1;
}
return (nuint)written;
}
}
if (pRequiredBufferSize != null)
{
*pRequiredBufferSize = (nuint)Encoding.UTF8.GetByteCount(utf16) + 1;
}
return (nuint)written;
}
private CorInfoType asCorInfoType(CORINFO_CLASS_STRUCT_* cls)
{
var type = HandleToObject(cls);
return asCorInfoType(type);
}
private byte* getClassNameFromMetadata(CORINFO_CLASS_STRUCT_* cls, byte** namespaceName)
{
TypeDesc type = HandleToObject(cls);
if (type.GetTypeDefinition() is EcmaType ecmaType)
{
var reader = ecmaType.MetadataReader;
if (namespaceName != null)
*namespaceName = reader.GetTypeNamespacePointer(ecmaType.Handle);
return reader.GetTypeNamePointer(ecmaType.Handle);
}
else if (type is MetadataType mdType)
{
if (namespaceName != null)
*namespaceName = (byte*)GetPin(SpanToPinnableBytes(mdType.Namespace));
return (byte*)GetPin(SpanToPinnableBytes(mdType.Name));
}
if (namespaceName != null)
*namespaceName = null;
return null;
}
private CORINFO_CLASS_STRUCT_* getTypeInstantiationArgument(CORINFO_CLASS_STRUCT_* cls, uint index)
{
TypeDesc type = HandleToObject(cls);
Instantiation inst = type.Instantiation;
return index < (uint)inst.Length ? ObjectToHandle(inst[(int)index]) : null;
}
#pragma warning disable CA1822 // Mark members as static
private CORINFO_CLASS_STRUCT_* getMethodInstantiationArgument(CORINFO_METHOD_STRUCT_* ftn, uint index)
{
#pragma warning restore CA1822 // Mark members as static
return null;
}
private nuint printClassName(CORINFO_CLASS_STRUCT_* cls, byte* buffer, nuint bufferSize, nuint* pRequiredBufferSize)
{
TypeDesc type = HandleToObject(cls);
string name = JitTypeNameFormatter.Instance.FormatName(type);
return PrintFromUtf16(name, buffer, bufferSize, pRequiredBufferSize);
}
private bool isValueClass(CORINFO_CLASS_STRUCT_* cls)
{
return HandleToObject(cls).IsValueType;
}
private uint getClassAttribs(CORINFO_CLASS_STRUCT_* cls)
{
TypeDesc type = HandleToObject(cls);
return getClassAttribsInternal(type);
}
private uint getClassAttribsInternal(TypeDesc type)
{
// TODO: Support for verification (CORINFO_FLG_GENERIC_TYPE_VARIABLE)
CorInfoFlag result = (CorInfoFlag)0;
var metadataType = type as MetadataType;
// The array flag is used to identify the faked-up methods on
// array types, i.e. .ctor, Get, Set and Address
if (type.IsArray)
result |= CorInfoFlag.CORINFO_FLG_ARRAY;
if (type.IsInterface)
result |= CorInfoFlag.CORINFO_FLG_INTERFACE;
if (type.IsArray || type.IsString)
result |= CorInfoFlag.CORINFO_FLG_VAROBJSIZE;
if (type.IsValueType)
{
result |= CorInfoFlag.CORINFO_FLG_VALUECLASS;
if (metadataType.IsByRefLike)
result |= CorInfoFlag.CORINFO_FLG_BYREF_LIKE;
if (metadataType.IsUnsafeValueType)
result |= CorInfoFlag.CORINFO_FLG_UNSAFE_VALUECLASS;
if (metadataType.IsInlineArray)
result |= CorInfoFlag.CORINFO_FLG_INDEXABLE_FIELDS;
if (metadataType.IsExtendedLayout)
{
if (metadataType.GetClassLayout().Kind == MetadataLayoutKind.CUnion)
{
result |= CorInfoFlag.CORINFO_FLG_OVERLAPPING_FIELDS;
}
}
}
if (type.IsCanonicalSubtype(CanonicalFormKind.Any))
result |= CorInfoFlag.CORINFO_FLG_SHAREDINST;
if (type.IsDelegate)
result |= CorInfoFlag.CORINFO_FLG_DELEGATE;
if (_compilation.IsEffectivelySealed(type))
result |= CorInfoFlag.CORINFO_FLG_FINAL;
if (type.IsIntrinsic)
result |= CorInfoFlag.CORINFO_FLG_INTRINSIC_TYPE;
if (metadataType != null)
{
if (metadataType.ContainsGCPointers)
result |= CorInfoFlag.CORINFO_FLG_CONTAINS_GC_PTR;
if (metadataType.IsBeforeFieldInit)
{
bool makeBeforeFieldInit = true;
#if READYTORUN
makeBeforeFieldInit &= _compilation.CompilationModuleGroup.VersionsWithType(type);
#endif
if (makeBeforeFieldInit)
{
result |= CorInfoFlag.CORINFO_FLG_BEFOREFIELDINIT;
}
}
// Assume overlapping fields for explicit layout.
if (metadataType.IsExplicitLayout)
result |= CorInfoFlag.CORINFO_FLG_OVERLAPPING_FIELDS;
if (metadataType.IsAbstract)
result |= CorInfoFlag.CORINFO_FLG_ABSTRACT;
}
return (uint)result;
}
private byte* getClassAssemblyName(CORINFO_CLASS_STRUCT_* cls)
{
TypeDesc type = HandleToObject(cls);
if (type is MetadataType mdType)
{
return (byte*)GetPin(StringToUTF8(mdType.Module.Assembly.GetName().Name));
}
return null;
}
#pragma warning disable CA1822 // Mark members as static
private void* LongLifetimeMalloc(nuint sz)
#pragma warning restore CA1822 // Mark members as static
{
return NativeMemory.Alloc(sz);
}
#pragma warning disable CA1822 // Mark members as static
private void LongLifetimeFree(void* obj)
#pragma warning restore CA1822 // Mark members as static
{
NativeMemory.Free(obj);
}
private void* getClassStaticDynamicInfo(CORINFO_CLASS_STRUCT_* cls)
{ throw new NotImplementedException("getClassStaticDynamicInfo"); }
private void* getClassThreadStaticDynamicInfo(CORINFO_CLASS_STRUCT_* cls)
{ throw new NotImplementedException("getClassThreadStaticDynamicInfo"); }
private uint getClassSize(CORINFO_CLASS_STRUCT_* cls)
{
TypeDesc type = HandleToObject(cls);
LayoutInt classSize = type.GetElementSize();
#if READYTORUN
if (classSize.IsIndeterminate)
{
throw new RequiresRuntimeJitException(type);
}
if (NeedsTypeLayoutCheck(type))
{
ISymbolNode node = _compilation.SymbolNodeFactory.CheckTypeLayout(type);
AddPrecodeFixup(node);
}
#endif
return (uint)classSize.AsInt;
}
private uint getHeapClassSize(CORINFO_CLASS_STRUCT_* cls)
{
TypeDesc type = HandleToObject(cls);
Debug.Assert(!type.IsValueType);
Debug.Assert(type.IsDefType);
Debug.Assert(!type.IsString);
#if READYTORUN
Debug.Assert(_compilation.IsInheritanceChainLayoutFixedInCurrentVersionBubble(type));
#endif
return (uint)((DefType)type).InstanceByteCount.AsInt;
}
private bool canAllocateOnStack(CORINFO_CLASS_STRUCT_* cls)
{
TypeDesc type = HandleToObject(cls);
Debug.Assert(!type.IsValueType);
Debug.Assert(type.IsDefType);
bool result = !type.HasFinalizer;
#if READYTORUN
if (!_compilation.IsInheritanceChainLayoutFixedInCurrentVersionBubble(type))
result = false;
#endif
return result;
}
/// <summary>
/// Managed implementation of CEEInfo::getClassAlignmentRequirementStatic
/// </summary>
public static int GetClassAlignmentRequirementStatic(DefType type)
{
int alignment = type.Context.Target.PointerSize;
if (type is MetadataType metadataType && !metadataType.IsAutoLayout)
{
if (metadataType.IsSequentialLayout || MarshalUtils.IsBlittableType(metadataType))
{
alignment = metadataType.InstanceFieldAlignment.AsInt;
}
}
if (type.Context.Target.SupportsAlign8 &&
alignment < 8 && type.RequiresAlign8())
{
// If the structure contains 64-bit primitive fields and the platform requires 8-byte alignment for
// such fields then make sure we return at least 8-byte alignment. Note that it's technically possible
// to create unmanaged APIs that take unaligned structures containing such fields and this
// unconditional alignment bump would cause us to get the calling convention wrong on platforms such
// as ARM. If we see such cases in the future we'd need to add another control (such as an alignment
// property for the StructLayout attribute or a marshaling directive attribute for p/invoke arguments)
// that allows more precise control. For now we'll go with the likely scenario.
alignment = 8;
}
return alignment;
}
private Dictionary<DefType, bool> _doubleAlignHeuristicCache = new Dictionary<DefType, bool>();
//*******************************************************************************
//
// Heuristic to determine if we should have instances of this class 8 byte aligned
//
private static bool ShouldAlign8(int dwR8Fields, int dwTotalFields)
{
return dwR8Fields*2>dwTotalFields && dwR8Fields>=2;
}
private static bool ShouldAlign8(DefType type)
{
int instanceFields = 0;
int doubleFields = 0;
var doubleType = type.Context.GetWellKnownType(WellKnownType.Double);
foreach (var field in type.GetFields())
{
if (field.IsStatic)
continue;
instanceFields++;
if (field.FieldType == doubleType)
doubleFields++;
}
return ShouldAlign8(doubleFields, instanceFields);
}
private uint getClassAlignmentRequirement(CORINFO_CLASS_STRUCT_* cls, bool fDoubleAlignHint)
{
DefType type = (DefType)HandleToObject(cls);
var target = type.Context.Target;
if (fDoubleAlignHint)
{
if (target.Architecture == TargetArchitecture.X86)
{
if ((type.IsValueType) && (type.InstanceFieldAlignment.AsInt > 4))
{
// On X86, double aligning the stack is expensive. if fDoubleAlignHint is true
// only align the local variable if it has a large enough fraction of double fields
// in comparison to the total field count.
if (!_doubleAlignHeuristicCache.TryGetValue(type, out bool doDoubleAlign))
{
doDoubleAlign = ShouldAlign8(type);
_doubleAlignHeuristicCache.Add(type, doDoubleAlign);
}
// Return the size of the double align hint. Ignore the actual alignment info account
// so that structs with 64-bit integer fields do not trigger double aligned frames on x86.
if (doDoubleAlign)
return 8;
}
return (uint)target.PointerSize;
}
}
return (uint)GetClassAlignmentRequirementStatic(type);
}
private int MarkGcField(byte* gcPtrs, CorInfoGCType gcType)
{
// Ensure that if we have multiple fields with the same offset,
// that we don't double count the data in the gc layout.
if (*gcPtrs == (byte)CorInfoGCType.TYPE_GC_NONE)
{
*gcPtrs = (byte)gcType;
return 1;
}
else
{
Debug.Assert(*gcPtrs == (byte)gcType);
return 0;
}
}
private int GatherClassGCLayout(MetadataType type, byte* gcPtrs)
{
int result = 0;
if (type.BaseType is { ContainsGCPointers: true } baseType)
result += GatherClassGCLayout(baseType, gcPtrs);
bool isInlineArray = type.IsInlineArray;
foreach (var field in type.GetFields())
{
if (field.IsStatic)
continue;
CorInfoGCType gcType = CorInfoGCType.TYPE_GC_NONE;
var fieldType = field.FieldType;
if (fieldType.IsValueType)
{
var fieldDefType = (DefType)fieldType;
if (!fieldDefType.ContainsGCPointers && !fieldDefType.IsByRefLike)
continue;
gcType = CorInfoGCType.TYPE_GC_OTHER;
}
else if (fieldType.IsGCPointer)
{
gcType = CorInfoGCType.TYPE_GC_REF;
}
else if (fieldType.IsByRef)
{
gcType = CorInfoGCType.TYPE_GC_BYREF;
}
else
{
continue;
}
Debug.Assert(field.Offset.AsInt % PointerSize == 0);
byte* fieldGcPtrs = gcPtrs + field.Offset.AsInt / PointerSize;
if (gcType == CorInfoGCType.TYPE_GC_OTHER)
{
result += GatherClassGCLayout((MetadataType)fieldType, fieldGcPtrs);
}
else
{
result += MarkGcField(fieldGcPtrs, gcType);
}
if (isInlineArray)
{
if (result > 0)
{
Debug.Assert(field.Offset.AsInt == 0);
int totalLayoutSize = type.GetElementSize().AsInt / PointerSize;
int elementLayoutSize = fieldType.GetElementSize().AsInt / PointerSize;
int gcPointersInElement = result;
for (int offset = elementLayoutSize; offset < totalLayoutSize; offset += elementLayoutSize)
{
Buffer.MemoryCopy(gcPtrs, gcPtrs + offset, elementLayoutSize, elementLayoutSize);
result += gcPointersInElement;
}
}
// inline array has only one element field
break;
}
}
return result;
}
private uint getClassGClayout(CORINFO_CLASS_STRUCT_* cls, byte* gcPtrs)
{
uint result = 0;
MetadataType type = (MetadataType)HandleToObject(cls);
uint size = type.IsValueType ? getClassSize(cls) : getHeapClassSize(cls);
new Span<byte>(gcPtrs, (int)((size + PointerSize - 1) / PointerSize)).Clear();
if (type.ContainsGCPointers || type.IsByRefLike)
{
result = (uint)GatherClassGCLayout(type, gcPtrs);
}
return result;
}
private Dictionary<TypeDesc, uint> _classNumInstanceFields = new();
private uint getClassNumInstanceFields(CORINFO_CLASS_STRUCT_* cls)
{
TypeDesc type = HandleToObject(cls);
var lookupType = type.GetTypeDefinition(); // The number of fields on an instantiation is the same as on the generic definition
if (_classNumInstanceFields.TryGetValue(lookupType, out uint numInstanceFields))
return numInstanceFields;
numInstanceFields = 0;
foreach (var field in type.GetFields())
{
if (!field.IsStatic)
numInstanceFields++;
}
_classNumInstanceFields.Add(lookupType, numInstanceFields);
return numInstanceFields;
}
private CORINFO_FIELD_STRUCT_* getFieldInClass(CORINFO_CLASS_STRUCT_* clsHnd, int num)
{
TypeDesc classWithFields = HandleToObject(clsHnd);
int iCurrentFoundField = -1;
foreach (var field in classWithFields.GetFields())
{
if (field.IsStatic)
continue;
++iCurrentFoundField;
if (iCurrentFoundField == num)
{
return ObjectToHandle(field);
}
}
// We could not find the field that was searched for.
throw new InvalidOperationException();
}
private GetTypeLayoutResult GetTypeLayoutHelper(MetadataType type, uint parentIndex, uint baseOffs, FieldDesc field, CORINFO_TYPE_LAYOUT_NODE* treeNodes, nuint maxTreeNodes, nuint* numTreeNodes)
{
if (*numTreeNodes >= maxTreeNodes)
{
return GetTypeLayoutResult.Partial;
}
uint structNodeIndex = (uint)(*numTreeNodes)++;
CORINFO_TYPE_LAYOUT_NODE* parNode = &treeNodes[structNodeIndex];
parNode->simdTypeHnd = null;
parNode->diagFieldHnd = field == null ? null : ObjectToHandle(field);
parNode->parent = parentIndex;
parNode->offset = baseOffs;
parNode->size = (uint)type.GetElementSize().AsInt;
parNode->numFields = 0;
parNode->type = CorInfoType.CORINFO_TYPE_VALUECLASS;
parNode->hasSignificantPadding = type.IsExplicitLayout || (type.IsSequentialLayout && type.GetClassLayout().Size != 0);
#if READYTORUN
// The contract of getTypeLayout is carefully crafted to still
// allow us to return hints about fields even for types outside the
// version bubble. The general idea is that the JIT does not use
// the information returned by this function to create new
// optimizations out of the blue, but only as a hint to optimize
// existing field uses more thoroughly. In particular the uses of
// fields outside the version bubble are only non-opaque and
// amenable to the optimizations that this unlocks if they already
// went through EncodeFieldBaseOffset.
//
if (!parNode->hasSignificantPadding && !_compilation.IsLayoutFixedInCurrentVersionBubble(type))
{
// For types without fixed layout the JIT is not allowed to
// rely on padding bits being insignificant, since fields could
// be added later inside that padding without invalidating the
// generated code.
parNode->hasSignificantPadding = true;
}
#endif
// The intrinsic SIMD/HW SIMD types have a lot of fields that the JIT does
// not care about since they are considered primitives by the JIT.
if (type.IsIntrinsic)
{
ReadOnlySpan<byte> ns = type.Namespace;
if (ns.SequenceEqual("System.Runtime.Intrinsics"u8) || ns.SequenceEqual("System.Numerics"u8))
{
parNode->simdTypeHnd = ObjectToHandle(type);
if (parentIndex != uint.MaxValue)
{
#if READYTORUN
if (NeedsTypeLayoutCheck(type))
{
// We cannot allow the JIT to call getClassSize for
// arbitrary types of fields as it will insert a fixup
// that we may not be able to encode. We could skip the
// field, but that will make prejit promotion different
// from the runtime promotion. We could also change the
// JIT to avoid calling getClassSize and just use the
// size from the returned node, but for that we would
// need to be sure that the type layout check fixup
// added in getTypeLayout is sufficient to guarantee
// the size of all these intrinsically handled SIMD
// types.
return GetTypeLayoutResult.Failure;
}
#endif
return GetTypeLayoutResult.Success;
}
}
}
foreach (FieldDesc fd in type.GetFields())
{
if (fd.IsStatic)
continue;
parNode->numFields++;
Debug.Assert(fd.Offset != FieldAndOffset.InvalidOffset);
TypeDesc fieldType = fd.FieldType;
CorInfoType corInfoType = asCorInfoType(fieldType);
if (corInfoType == CorInfoType.CORINFO_TYPE_VALUECLASS)
{
Debug.Assert(fieldType is MetadataType);
GetTypeLayoutResult result = GetTypeLayoutHelper((MetadataType)fieldType, structNodeIndex, baseOffs + (uint)fd.Offset.AsInt, fd, treeNodes, maxTreeNodes, numTreeNodes);
if (result != GetTypeLayoutResult.Success)
return result;
}
else
{
if (*numTreeNodes >= maxTreeNodes)
return GetTypeLayoutResult.Partial;
CORINFO_TYPE_LAYOUT_NODE* treeNode = &treeNodes[(*numTreeNodes)++];
treeNode->simdTypeHnd = null;
treeNode->diagFieldHnd = ObjectToHandle(fd);
treeNode->parent = structNodeIndex;
treeNode->offset = baseOffs + (uint)fd.Offset.AsInt;
treeNode->size = (uint)fieldType.GetElementSize().AsInt;
treeNode->numFields = 0;
treeNode->type = corInfoType;
treeNode->hasSignificantPadding = false;
}
if (type.IsInlineArray)
{
nuint treeNodeEnd = *numTreeNodes;
int elemSize = fieldType.GetElementSize().AsInt;
int arrSize = type.GetElementSize().AsInt;
// Number of fields added for each element, including all
// subfields. For example, for ValueTuple<int, int>[4]:
// [ 0]: InlineArray parent = -1
// [ 1]: ValueTuple<int, int> parent = 0 -
// [ 2]: int parent = 1 |
// [ 3]: int parent = 1 |
// [ 4]: ValueTuple<int, int> parent = 0 - stride = 3
// [ 5]: int parent = 4
// [ 6]: int parent = 4
// [ 7]: ValueTuple<int, int> parent = 0
// [ 8]: int parent = 7
// [ 9]: int parent = 7
// [10]: ValueTuple<int, int> parent = 0
// [11]: int parent = 10
// [12]: int parent = 10
uint elemFieldsStride = (uint)*numTreeNodes - (structNodeIndex + 1);
// Now duplicate the fields of the previous entry for each
// additional element. For each entry we have to update the
// offset and the parent index.
for (int elemOffset = elemSize; elemOffset < arrSize; elemOffset += elemSize)
{
nuint prevElemStart = *numTreeNodes - elemFieldsStride;
for (nuint i = 0; i < elemFieldsStride; i++)
{
if (*numTreeNodes >= maxTreeNodes)
return GetTypeLayoutResult.Partial;
CORINFO_TYPE_LAYOUT_NODE* treeNode = &treeNodes[(*numTreeNodes)++];
*treeNode = treeNodes[prevElemStart + i];
treeNode->offset += (uint)elemSize;
// The first field points back to the inline array
// and has no bias; the rest of them do.
treeNode->parent += (i == 0) ? 0 : elemFieldsStride;
}
parNode->numFields++;
}
}
}
return GetTypeLayoutResult.Success;
}
private GetTypeLayoutResult getTypeLayout(CORINFO_CLASS_STRUCT_* typeHnd, CORINFO_TYPE_LAYOUT_NODE* treeNodes, nuint* numTreeNodes)
{
TypeDesc type = HandleToObject(typeHnd);
if (type is not MetadataType metadataType || !type.IsValueType)
return GetTypeLayoutResult.Failure;
nuint maxFields = *numTreeNodes;
*numTreeNodes = 0;
GetTypeLayoutResult result = GetTypeLayoutHelper(metadataType, uint.MaxValue, 0, null, treeNodes, maxFields, numTreeNodes);
#if READYTORUN
if (NeedsTypeLayoutCheck(type))
{
ISymbolNode node = _compilation.SymbolNodeFactory.CheckTypeLayout(type);
AddPrecodeFixup(node);
}
#endif
return result;
}
private bool checkMethodModifier(CORINFO_METHOD_STRUCT_* hMethod, byte* modifier, bool fOptional)
{ throw new NotImplementedException("checkMethodModifier"); }
private CorInfoHelpFunc getSharedCCtorHelper(CORINFO_CLASS_STRUCT_* clsHnd)
{ throw new NotImplementedException("getSharedCCtorHelper"); }
private CORINFO_CLASS_STRUCT_* getTypeForBox(CORINFO_CLASS_STRUCT_* cls)
{
var type = HandleToObject(cls);
var typeForBox = type.IsNullable ? type.Instantiation[0] : type;
return ObjectToHandle(typeForBox);
}
private CorInfoHelpFunc getBoxHelper(CORINFO_CLASS_STRUCT_* cls)
{
var type = HandleToObject(cls);
if (type.IsByRefLike)
ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramSpecific, MethodBeingCompiled);
return type.IsNullable ? CorInfoHelpFunc.CORINFO_HELP_BOX_NULLABLE : CorInfoHelpFunc.CORINFO_HELP_BOX;
}
private CorInfoHelpFunc getUnBoxHelper(CORINFO_CLASS_STRUCT_* cls)
{
var type = HandleToObject(cls);
return type.IsNullable ? CorInfoHelpFunc.CORINFO_HELP_UNBOX_NULLABLE : CorInfoHelpFunc.CORINFO_HELP_UNBOX;
}
private CorInfoInitClassResult initClass(CORINFO_FIELD_STRUCT_* field, CORINFO_METHOD_STRUCT_* method, CORINFO_CONTEXT_STRUCT* context)
{
FieldDesc fd = field == null ? null : HandleToObject(field);
Debug.Assert(fd == null || fd.IsStatic);
MethodDesc md = method == null ? MethodBeingCompiled : HandleToObject(method);
TypeDesc type = fd != null ? fd.OwningType : typeFromContext(context);
#if !READYTORUN
if (
_isFallbackBodyCompilation ||
!_compilation.HasLazyStaticConstructor(type)
)
{
return CorInfoInitClassResult.CORINFO_INITCLASS_NOT_REQUIRED;
}
#endif
MetadataType typeToInit = (MetadataType)type;
if (fd == null)
{
if (typeToInit.IsBeforeFieldInit)
{
// We can wait for field accesses to run .cctor
return CorInfoInitClassResult.CORINFO_INITCLASS_NOT_REQUIRED;
}
// Run .cctor on statics & constructors
if (md.Signature.IsStatic)
{
// Except don't class construct on .cctor - it would be circular
if (md.IsStaticConstructor)
{
return CorInfoInitClassResult.CORINFO_INITCLASS_NOT_REQUIRED;
}
}
else if (!md.IsConstructor && !typeToInit.IsValueType && !typeToInit.IsInterface)
{
// According to the spec, we should be able to do this optimization for both reference and valuetypes.
// To maintain backward compatibility, we are doing it for reference types only.
// We don't do this for interfaces though, as those don't have instance constructors.
// For instance methods of types with precise-initialization
// semantics, we can assume that the .ctor triggered the
// type initialization.
// This does not hold for NULL "this" object. However, the spec does
// not require that case to work.
return CorInfoInitClassResult.CORINFO_INITCLASS_NOT_REQUIRED;
}
}
if (typeToInit.IsCanonicalSubtype(CanonicalFormKind.Any))
{
if (fd == null && method != null && context == contextFromMethodBeingCompiled())
{
// If we're inling a call to a method in our own type, then we should already
// have triggered the .cctor when caller was itself called.
return CorInfoInitClassResult.CORINFO_INITCLASS_NOT_REQUIRED;
}
// Shared generic code has to use helper. Moreover, tell JIT not to inline since
// inlining of generic dictionary lookups is not supported.
return CorInfoInitClassResult.CORINFO_INITCLASS_USE_HELPER | CorInfoInitClassResult.CORINFO_INITCLASS_DONT_INLINE;
}
//
// Try to prove that the initialization is not necessary because of nesting
//
if (fd == null)
{
// Handled above
Debug.Assert(!typeToInit.IsBeforeFieldInit);
if (method != null && typeToInit == MethodBeingCompiled.OwningType)
{
// If we're inling a call to a method in our own type, then we should already
// have triggered the .cctor when caller was itself called.
return CorInfoInitClassResult.CORINFO_INITCLASS_NOT_REQUIRED;
}
}
else
{
// This optimization may cause static fields in reference types to be accessed without cctor being triggered
// for NULL "this" object. It does not conform with what the spec says. However, we have been historically
// doing it for perf reasons.
if (!typeToInit.IsValueType && !typeToInit.IsInterface && !typeToInit.IsBeforeFieldInit)
{
if (typeToInit == typeFromContext(context) || typeToInit == MethodBeingCompiled.OwningType)
{
// The class will be initialized by the time we access the field.
return CorInfoInitClassResult.CORINFO_INITCLASS_NOT_REQUIRED;
}
}
// If we are currently compiling the class constructor for this static field access then we can skip the initClass
if (MethodBeingCompiled.OwningType == typeToInit && MethodBeingCompiled.IsStaticConstructor)
{
// The class will be initialized by the time we access the field.
return CorInfoInitClassResult.CORINFO_INITCLASS_NOT_REQUIRED;
}
}
return CorInfoInitClassResult.CORINFO_INITCLASS_USE_HELPER;
}
private CORINFO_CLASS_STRUCT_* getBuiltinClass(CorInfoClassId classId)
{
switch (classId)
{
case CorInfoClassId.CLASSID_SYSTEM_OBJECT:
return ObjectToHandle(_compilation.TypeSystemContext.GetWellKnownType(WellKnownType.Object));
case CorInfoClassId.CLASSID_TYPED_BYREF:
return ObjectToHandle(_compilation.TypeSystemContext.GetWellKnownType(WellKnownType.TypedReference));
case CorInfoClassId.CLASSID_TYPE_HANDLE:
return ObjectToHandle(_compilation.TypeSystemContext.GetWellKnownType(WellKnownType.RuntimeTypeHandle));
case CorInfoClassId.CLASSID_FIELD_HANDLE:
return ObjectToHandle(_compilation.TypeSystemContext.GetWellKnownType(WellKnownType.RuntimeFieldHandle));
case CorInfoClassId.CLASSID_METHOD_HANDLE:
return ObjectToHandle(_compilation.TypeSystemContext.GetWellKnownType(WellKnownType.RuntimeMethodHandle));
case CorInfoClassId.CLASSID_ARGUMENT_HANDLE:
ThrowHelper.ThrowTypeLoadException("System", "RuntimeArgumentHandle", _compilation.TypeSystemContext.SystemModule);
return null;
case CorInfoClassId.CLASSID_STRING:
return ObjectToHandle(_compilation.TypeSystemContext.GetWellKnownType(WellKnownType.String));
case CorInfoClassId.CLASSID_RUNTIME_TYPE:
return ObjectToHandle(_compilation.TypeSystemContext.SystemModule.GetKnownType("System"u8, "RuntimeType"u8));
default:
throw new NotImplementedException();
}
}
private CorInfoType getTypeForPrimitiveValueClass(CORINFO_CLASS_STRUCT_* cls)
{
var type = HandleToObject(cls);
if (!type.IsPrimitive && !type.IsEnum)
return CorInfoType.CORINFO_TYPE_UNDEF;
return asCorInfoType(type);
}
private CorInfoType getTypeForPrimitiveNumericClass(CORINFO_CLASS_STRUCT_* cls)
{
var type = HandleToObject(cls);
if (type.IsPrimitiveNumeric)
return asCorInfoType(type);
return CorInfoType.CORINFO_TYPE_UNDEF;
}
private bool canCast(CORINFO_CLASS_STRUCT_* child, CORINFO_CLASS_STRUCT_* parent)
{ throw new NotImplementedException("canCast"); }
private TypeCompareState compareTypesForCast(CORINFO_CLASS_STRUCT_* fromClass, CORINFO_CLASS_STRUCT_* toClass)
{
TypeDesc fromType = HandleToObject(fromClass);
TypeDesc toType = HandleToObject(toClass);
TypeCompareState result = TypeCompareState.May;
if (fromType.IsIDynamicInterfaceCastable)
{
result = TypeCompareState.May;
}
else if (toType.IsNullable)
{
// If casting to Nullable<T>, don't try to optimize
result = TypeCompareState.May;
}
else if (!fromType.IsCanonicalSubtype(CanonicalFormKind.Any) && !toType.IsCanonicalSubtype(CanonicalFormKind.Any))
{
// If the types are not shared, we can check directly.
if (fromType.CanCastTo(toType))
result = TypeCompareState.Must;
else
result = TypeCompareState.MustNot;
}
else if (fromType.IsCanonicalSubtype(CanonicalFormKind.Any) && !toType.IsCanonicalSubtype(CanonicalFormKind.Any))
{
// Casting from a shared type to an unshared type.
// Only handle casts to interface types for now
if (toType.IsInterface)
{
// Do a preliminary check.
bool canCast = fromType.CanCastTo(toType);
// Pass back positive results unfiltered. The unknown type
// parameters in fromClass did not come into play.
if (canCast)
{
result = TypeCompareState.Must;
}
// We have __Canon parameter(s) in fromClass, somewhere.
//
// In CanCastTo, these __Canon(s) won't match the interface or
// instantiated types on the interface, so CanCastTo may
// return false negatives.
//
// Only report MustNot if the fromClass is not __Canon
// and the interface is not instantiated; then there is
// no way for the fromClass __Canon(s) to confuse things.
//
// __Canon -> IBar May
// IFoo<__Canon> -> IFoo<string> May
// IFoo<__Canon> -> IBar MustNot
//
else if (fromType.IsCanonicalDefinitionType(CanonicalFormKind.Any))
{
result = TypeCompareState.May;
}
else if (toType.HasInstantiation)
{
result = TypeCompareState.May;
}
else
{
result = TypeCompareState.MustNot;
}
}
}
#if READYTORUN
// In R2R it is a breaking change for a previously positive
// cast to become negative, but not for a previously negative
// cast to become positive. So in R2R a negative result is
// always reported back as May.
if (result == TypeCompareState.MustNot)
{
result = TypeCompareState.May;
}
#endif
return result;
}
private TypeCompareState compareTypesForEquality(CORINFO_CLASS_STRUCT_* cls1, CORINFO_CLASS_STRUCT_* cls2)
{
TypeDesc type1 = HandleToObject(cls1);
TypeDesc type2 = HandleToObject(cls2);
return TypeExtensions.CompareTypesForEquality(type1, type2) switch
{
true => TypeCompareState.Must,
false => TypeCompareState.MustNot,
_ => TypeCompareState.May,
};
}
private bool isMoreSpecificType(CORINFO_CLASS_STRUCT_* cls1, CORINFO_CLASS_STRUCT_* cls2)
{
TypeDesc type1 = HandleToObject(cls1);
TypeDesc type2 = HandleToObject(cls2);
// If both types have the same type definition while
// hnd1 is shared and hnd2 is not - consider hnd2 more specific.
if (type1.HasSameTypeDefinition(type2))
{
return type1.IsCanonicalSubtype(CanonicalFormKind.Any) && !type2.IsCanonicalSubtype(CanonicalFormKind.Any);
}
// Look for a common parent type.
TypeDesc merged = TypeExtensions.MergeTypesToCommonParent(type1, type2);
// If the common parent is type1, then type2 is more specific.
return merged == type1;
}
private bool isExactType(CORINFO_CLASS_STRUCT_* cls)
{
TypeDesc type = HandleToObject(cls);
while (type.IsArray)
{
ArrayType arrayType = (ArrayType)type;
// Single dimensional array with non-zero bounds may be SZ array.
if (arrayType.IsMdArray && arrayType.Rank == 1)
return false;
type = arrayType.ElementType;
// Arrays of primitives are interchangeable with arrays of enums of the same underlying type.
if (type.IsPrimitive || type.IsEnum)
return false;
}
// Use conservative answer for pointers and custom types.
if (!type.IsDefType)
return false;
// Use conservative answer for equivalent and variant types.
if (type.HasTypeEquivalence || type.HasVariance)
return false;
// SZArrayHelper (CoreCLR) and Array<T> (NativeAOT) are phantom dispatch
// targets: the runtime maps generic array interface methods
// (IList<T>.set_Item, etc.) to methods on these sealed/effectively-sealed
// types, but no runtime object ever has them as its MethodTable - `this`
// inside those methods is always a T[]. Reporting them as exact would
// allow the JIT (e.g. VN-based invariant-load folding of GetMethodTable)
// to embed their MT as a constant and mis-read fields off it. Both types
// are marked [Intrinsic] so that the cheap flag check filters out
// non-candidates before we compare names.
if (type.IsIntrinsic && type is MetadataType mdType)
{
ReadOnlySpan<byte> name = mdType.Name;
if ((name.SequenceEqual("SZArrayHelper"u8) || name.SequenceEqual("Array`1"u8)) &&
mdType.Namespace.SequenceEqual("System"u8))
{
return false;
}
}
// Valuetypes are invariant. This assumes that introducing type equivalence to an existing type
// is not compatible change.
if (type.IsValueType)
return true;
return _compilation.IsEffectivelySealed(type);
}
private TypeCompareState isGenericType(CORINFO_CLASS_STRUCT_* cls)
{
TypeDesc type = HandleToObject(cls);
if (type.IsCanonicalDefinitionType(CanonicalFormKind.Any))
{
return TypeCompareState.May;
}
return type.HasInstantiation ? TypeCompareState.Must : TypeCompareState.MustNot;
}
private TypeCompareState isNullableType(CORINFO_CLASS_STRUCT_* cls)
{
TypeDesc type = HandleToObject(cls);
Debug.Assert(!type.IsGenericParameter);
return type.IsNullable ? TypeCompareState.Must : TypeCompareState.MustNot;
}
private TypeCompareState isEnum(CORINFO_CLASS_STRUCT_* cls, CORINFO_CLASS_STRUCT_** underlyingType)
{
Debug.Assert(cls != null);
if (underlyingType != null)
{
*underlyingType = null;
}
TypeDesc type = HandleToObject(cls);
Debug.Assert(!type.IsGenericParameter);
if (type.IsEnum)
{
if (underlyingType != null)
{
*underlyingType = ObjectToHandle(type.UnderlyingType);
}
return TypeCompareState.Must;
}
else
{
return TypeCompareState.MustNot;
}
}
private CORINFO_CLASS_STRUCT_* getParentType(CORINFO_CLASS_STRUCT_* cls)
{ throw new NotImplementedException("getParentType"); }
private CorInfoType getChildType(CORINFO_CLASS_STRUCT_* clsHnd, CORINFO_CLASS_STRUCT_** clsRet)
{
CorInfoType result = CorInfoType.CORINFO_TYPE_UNDEF;
var td = HandleToObject(clsHnd);
if (td.IsArray || td.IsByRef || td.IsPointer)
{
TypeDesc returnType = ((ParameterizedType)td).ParameterType;
result = asCorInfoType(returnType, clsRet);
}
else
{
*clsRet = null;
}
return result;
}
private bool isSDArray(CORINFO_CLASS_STRUCT_* cls)
{
var td = HandleToObject(cls);
return td.IsSzArray;
}
private uint getArrayRank(CORINFO_CLASS_STRUCT_* cls)
{
uint rank = 0;
var td = HandleToObject(cls) as ArrayType;
if (td != null)
{
rank = (uint)td.Rank;
}
return rank;
}
private CorInfoArrayIntrinsic getArrayIntrinsicID(CORINFO_METHOD_STRUCT_* ftn)
{
CorInfoArrayIntrinsic kind = CorInfoArrayIntrinsic.ILLEGAL;
if (HandleToObject(ftn) is ArrayMethod am)
{
kind = am.Kind switch
{
ArrayMethodKind.Get => CorInfoArrayIntrinsic.GET,
ArrayMethodKind.Set => CorInfoArrayIntrinsic.SET,
ArrayMethodKind.Address => CorInfoArrayIntrinsic.ADDRESS,
_ => CorInfoArrayIntrinsic.ILLEGAL
};
}
return kind;
}
private void* getArrayInitializationData(CORINFO_FIELD_STRUCT_* field, uint size)
{
var fd = HandleToObject(field);
// Check for invalid arguments passed to InitializeArray intrinsic
if (!fd.HasRva ||
size > fd.FieldType.GetElementSize().AsInt)
{
return null;
}
return (void*)ObjectToHandle(_compilation.GetFieldRvaData(fd));
}
#pragma warning disable CA1822 // Mark members as static
private CorInfoIsAccessAllowedResult canAccessClass(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_METHOD_STRUCT_* callerHandle, ref CORINFO_HELPER_DESC pAccessHelper)
#pragma warning restore CA1822 // Mark members as static
{
// TODO: Access check
return CorInfoIsAccessAllowedResult.CORINFO_ACCESS_ALLOWED;
}
private nuint printFieldName(CORINFO_FIELD_STRUCT_* fld, byte* buffer, nuint bufferSize, nuint* requiredBufferSize)
{
FieldDesc field = HandleToObject(fld);
return PrintFromUtf16(field.GetName(), buffer, bufferSize, requiredBufferSize);
}
#pragma warning disable CA1822 // Mark members as static
private uint getThreadLocalFieldInfo(CORINFO_FIELD_STRUCT_* fld, bool isGCType)
#pragma warning restore CA1822 // Mark members as static
{
// Implemented for JIT only for now.
return 0;
}
#pragma warning disable CA1822 // Mark members as static
private void getThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOCKS_INFO* pInfo)
#pragma warning restore CA1822 // Mark members as static
{
// Implemented for JIT only for now.
}
private CORINFO_CLASS_STRUCT_* getFieldClass(CORINFO_FIELD_STRUCT_* field)
{
var fieldDesc = HandleToObject(field);
return ObjectToHandle(fieldDesc.OwningType);
}
private CorInfoType getFieldType(CORINFO_FIELD_STRUCT_* field, CORINFO_CLASS_STRUCT_** structType, CORINFO_CLASS_STRUCT_* fieldOwnerHint)
{
FieldDesc fieldDesc = HandleToObject(field);
TypeDesc fieldType = fieldDesc.FieldType;
CorInfoType type;
if (structType != null)
{
type = asCorInfoType(fieldType, structType);
}
else
{
type = asCorInfoType(fieldType);
}
return type;
}
private uint getFieldOffset(CORINFO_FIELD_STRUCT_* field)
{
var fieldDesc = HandleToObject(field);
Debug.Assert(fieldDesc.Offset != FieldAndOffset.InvalidOffset);
return (uint)fieldDesc.Offset.AsInt;
}
private static CORINFO_FIELD_ACCESSOR getFieldIntrinsic(FieldDesc field)
{
Debug.Assert(field.IsIntrinsic);
var owningType = field.OwningType;
if ((owningType.IsWellKnownType(WellKnownType.IntPtr) ||
owningType.IsWellKnownType(WellKnownType.UIntPtr)) &&
field.Name.SequenceEqual("Zero"u8))
{
return CORINFO_FIELD_ACCESSOR.CORINFO_FIELD_INTRINSIC_ZERO;
}
else if (owningType.IsString && field.Name.SequenceEqual("Empty"u8))
{
return CORINFO_FIELD_ACCESSOR.CORINFO_FIELD_INTRINSIC_EMPTY_STRING;
}
else if (owningType.Name.SequenceEqual("BitConverter"u8) && owningType.Namespace.SequenceEqual("System"u8) &&
field.Name.SequenceEqual("IsLittleEndian"u8))
{
return CORINFO_FIELD_ACCESSOR.CORINFO_FIELD_INTRINSIC_ISLITTLEENDIAN;
}
return (CORINFO_FIELD_ACCESSOR)(-1);
}
private bool isFieldStatic(CORINFO_FIELD_STRUCT_* fldHnd)
{
return HandleToObject(fldHnd).IsStatic;
}
private void getBoundaries(CORINFO_METHOD_STRUCT_* ftn, ref uint cILOffsets, ref uint* pILOffsets, BoundaryTypes* implicitBoundaries)
{
// TODO: Debugging
cILOffsets = 0;
pILOffsets = null;
*implicitBoundaries = BoundaryTypes.DEFAULT_BOUNDARIES;
}
private void getVars(CORINFO_METHOD_STRUCT_* ftn, ref uint cVars, ILVarInfo** vars, ref bool extendOthers)
{
// TODO: Debugging
cVars = 0;
*vars = null;
// Just tell the JIT to extend everything.
extendOthers = true;
}
#pragma warning disable CA1822 // Mark members as static
private void reportRichMappings(InlineTreeNode* inlineTree, uint numInlineTree, RichOffsetMapping* mappings, uint numMappings)
#pragma warning restore CA1822 // Mark members as static
{
NativeMemory.Free(inlineTree);
NativeMemory.Free(mappings);
}
#pragma warning disable CA1822 // Mark members as static
private void reportAsyncDebugInfo(AsyncInfo* asyncInfo, AsyncSuspensionPoint* suspensionPoints, AsyncContinuationVarInfo* vars, uint numVars)
#pragma warning restore CA1822 // Mark members as static
{
NativeMemory.Free(suspensionPoints);
NativeMemory.Free(vars);
}
#pragma warning disable CA1822 // Mark members as static
private void reportMetadata(byte* key, void* value, nuint length)
#pragma warning restore CA1822 // Mark members as static
{
}
#pragma warning disable CA1822 // Mark members as static
private void* allocateArray(nuint cBytes)
#pragma warning restore CA1822 // Mark members as static
{
return NativeMemory.Alloc(cBytes);
}
#pragma warning disable CA1822 // Mark members as static
private void freeArray(void* array)
#pragma warning restore CA1822 // Mark members as static
{
NativeMemory.Free(array);
}
#pragma warning disable CA1822 // Mark members as static
private CORINFO_ARG_LIST_STRUCT_* getArgNext(CORINFO_ARG_LIST_STRUCT_* args)
#pragma warning restore CA1822 // Mark members as static
{
return (CORINFO_ARG_LIST_STRUCT_*)((int)args + 1);
}
private CorInfoTypeWithMod getArgType(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_STRUCT_* args, CORINFO_CLASS_STRUCT_** vcTypeRet)
{
int index = (int)args;
object sigObj = HandleToObject((void*)sig->methodSignature);
MethodSignature methodSig = sigObj as MethodSignature;
if (methodSig != null)
{
TypeDesc type = methodSig[index];
CorInfoType corInfoType = asCorInfoType(type, vcTypeRet);
return (CorInfoTypeWithMod)corInfoType;
}
else
{
LocalVariableDefinition[] locals = (LocalVariableDefinition[])sigObj;
TypeDesc type = locals[index].Type;
CorInfoType corInfoType = asCorInfoType(type, vcTypeRet);
return (CorInfoTypeWithMod)corInfoType | (locals[index].IsPinned ? CorInfoTypeWithMod.CORINFO_TYPE_MOD_PINNED : 0);
}
}
private CORINFO_CLASS_STRUCT_* getArgClass(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_STRUCT_* args)
{
int index = (int)args;
object sigObj = HandleToObject((void*)sig->methodSignature);
MethodSignature methodSig = sigObj as MethodSignature;
if (methodSig != null)
{
TypeDesc type = methodSig[index];
return ObjectToHandle(type);
}
else
{
LocalVariableDefinition[] locals = (LocalVariableDefinition[])sigObj;
TypeDesc type = locals[index].Type;
return ObjectToHandle(type);
}
}
private CorInfoHFAElemType getHFAType(CORINFO_CLASS_STRUCT_* hClass)
{
var type = (DefType)HandleToObject(hClass);
// See MethodTable::GetHFAType and Compiler::GetHfaType.
return (type.ValueTypeShapeCharacteristics & ValueTypeShapeCharacteristics.AggregateMask) switch
{
ValueTypeShapeCharacteristics.Float32Aggregate => CorInfoHFAElemType.CORINFO_HFA_ELEM_FLOAT,
ValueTypeShapeCharacteristics.Float64Aggregate => CorInfoHFAElemType.CORINFO_HFA_ELEM_DOUBLE,
ValueTypeShapeCharacteristics.Vector64Aggregate => CorInfoHFAElemType.CORINFO_HFA_ELEM_VECTOR64,
ValueTypeShapeCharacteristics.Vector128Aggregate => CorInfoHFAElemType.CORINFO_HFA_ELEM_VECTOR128,
_ => CorInfoHFAElemType.CORINFO_HFA_ELEM_NONE
};
}
#pragma warning disable CA1822 // Mark members as static
private bool runWithErrorTrap(void* function, void* parameter)
#pragma warning restore CA1822 // Mark members as static
{
// This method is completely handled by the C++ wrapper to the JIT-EE interface,
// and should never reach the managed implementation.
Debug.Fail("CorInfoImpl.runWithErrorTrap should not be called");
throw new NotSupportedException("runWithErrorTrap");
}
#pragma warning disable CA1822 // Mark members as static
private bool runWithSPMIErrorTrap(void* function, void* parameter)
#pragma warning restore CA1822 // Mark members as static
{
// This method is completely handled by the C++ wrapper to the JIT-EE interface,
// and should never reach the managed implementation.
Debug.Fail("CorInfoImpl.runWithSPMIErrorTrap should not be called");
throw new NotSupportedException("runWithSPMIErrorTrap");
}
public static CORINFO_OS TargetToOs(TargetDetails target)
{
return target.IsWindows ? CORINFO_OS.CORINFO_WINNT :
target.IsApplePlatform ? CORINFO_OS.CORINFO_APPLE : CORINFO_OS.CORINFO_UNIX;
}
private void getEEInfo(ref CORINFO_EE_INFO pEEInfoOut)
{
pEEInfoOut = default(CORINFO_EE_INFO);
#if DEBUG
// In debug, write some bogus data to the struct to ensure we have filled everything
// properly.
fixed (CORINFO_EE_INFO* tmp = &pEEInfoOut)
NativeMemory.Fill(tmp, (nuint)sizeof(CORINFO_EE_INFO), 0xcc);
#endif
int pointerSize = this.PointerSize;
pEEInfoOut.inlinedCallFrameInfo.size = (uint)SizeOfPInvokeTransitionFrame;
pEEInfoOut.offsetOfDelegateInstance = (uint)pointerSize; // Delegate::_firstParameter
pEEInfoOut.offsetOfDelegateFirstTarget = OffsetOfDelegateFirstTarget;
pEEInfoOut.sizeOfReversePInvokeFrame = (uint)SizeOfReversePInvokeTransitionFrame;
pEEInfoOut.osPageSize = 0x1000;
if (_compilation.NodeFactory.Target.IsWasm)
{
// TODO: Set this value to 0 for Wasm
pEEInfoOut.maxUncheckedOffsetForNullObject = 1024 - 1;
}
else if (_compilation.NodeFactory.Target.IsWindows)
{
pEEInfoOut.maxUncheckedOffsetForNullObject = 32 * 1024 - 1;
}
else
{
pEEInfoOut.maxUncheckedOffsetForNullObject = pEEInfoOut.osPageSize / 2 - 1;
}
pEEInfoOut.targetAbi = TargetABI;
pEEInfoOut.osType = TargetToOs(_compilation.NodeFactory.Target);
}
private void getAsyncInfo(ref CORINFO_ASYNC_INFO pAsyncInfoOut)
{
DefType continuation = _compilation.TypeSystemContext.ContinuationType;
pAsyncInfoOut.continuationClsHnd = ObjectToHandle(continuation);
pAsyncInfoOut.continuationNextFldHnd = ObjectToHandle(continuation.GetKnownField("Next"u8));
pAsyncInfoOut.continuationResumeInfoFldHnd = ObjectToHandle(continuation.GetKnownField("ResumeInfo"u8));
pAsyncInfoOut.continuationStateFldHnd = ObjectToHandle(continuation.GetKnownField("State"u8));
pAsyncInfoOut.continuationFlagsFldHnd = ObjectToHandle(continuation.GetKnownField("Flags"u8));
DefType asyncHelpers = _compilation.TypeSystemContext.SystemModule.GetKnownType("System.Runtime.CompilerServices"u8, "AsyncHelpers"u8);
pAsyncInfoOut.captureExecutionContextMethHnd = ObjectToHandle(asyncHelpers.GetKnownMethod("CaptureExecutionContext"u8, null));
pAsyncInfoOut.captureContinuationContextMethHnd = ObjectToHandle(asyncHelpers.GetKnownMethod("CaptureContinuationContext"u8, null));
pAsyncInfoOut.captureContextsMethHnd = ObjectToHandle(asyncHelpers.GetKnownMethod("CaptureContexts"u8, null));
pAsyncInfoOut.restoreContextsMethHnd = ObjectToHandle(asyncHelpers.GetKnownMethod("RestoreContexts"u8, null));
pAsyncInfoOut.restoreContextsOnSuspensionMethHnd = ObjectToHandle(asyncHelpers.GetKnownMethod("RestoreContextsOnSuspension"u8, null));
pAsyncInfoOut.finishSuspensionNoContinuationContextMethHnd = ObjectToHandle(asyncHelpers.GetKnownMethod("FinishSuspensionNoContinuationContext"u8, null));
pAsyncInfoOut.finishSuspensionWithContinuationContextMethHnd = ObjectToHandle(asyncHelpers.GetKnownMethod("FinishSuspensionWithContinuationContext"u8, null));
}
private CORINFO_CLASS_STRUCT_* getContinuationType(nuint dataSize, ref bool objRefs, nuint objRefsSize)
{
Debug.Assert(objRefsSize == (dataSize + (nuint)(PointerSize - 1)) / (nuint)PointerSize);
GCPointerMapBuilder gcMapBuilder = new GCPointerMapBuilder((int)dataSize, PointerSize);
ReadOnlySpan<bool> bools = MemoryMarshal.CreateReadOnlySpan(ref objRefs, (int)objRefsSize);
for (int i = 0; i < bools.Length; i++)
{
if (bools[i])
gcMapBuilder.MarkGCPointer(i * PointerSize);
}
return ObjectToHandle(_compilation.TypeSystemContext.GetContinuationType(gcMapBuilder.ToGCMap()));
}
private mdToken getMethodDefFromMethod(CORINFO_METHOD_STRUCT_* hMethod)
{
MethodDesc method = HandleToObject(hMethod);
#if READYTORUN
if (method is UnboxingMethodDesc unboxingMethodDesc)
{
method = unboxingMethodDesc.Target;
}
#endif
MethodDesc methodDefinition = method.GetTypicalMethodDefinition();
// Need to cast down to EcmaMethod. Do not use this as a precedent that casting to Ecma*
// within the JitInterface is fine. We might want to consider moving this to Compilation.
TypeSystem.Ecma.EcmaMethod ecmaMethodDefinition = methodDefinition as TypeSystem.Ecma.EcmaMethod;
if (ecmaMethodDefinition != null)
{
return (mdToken)System.Reflection.Metadata.Ecma335.MetadataTokens.GetToken(ecmaMethodDefinition.Handle);
}
return 0;
}
private static byte[] StringToUTF8(string s)
{
int byteCount = Encoding.UTF8.GetByteCount(s);
byte[] bytes = new byte[byteCount + 1];
Encoding.UTF8.GetBytes(s, 0, s.Length, bytes, 0);
return bytes;
}
private static byte[] SpanToPinnableBytes(ReadOnlySpan<byte> s)
{
byte[] bytes = new byte[s.Length + 1];
s.CopyTo(bytes);
return bytes;
}
private nuint printMethodName(CORINFO_METHOD_STRUCT_* ftn, byte* buffer, nuint bufferSize, nuint* requiredBufferSize)
{
MethodDesc method = HandleToObject(ftn);
return PrintFromUtf16(method.GetName(), buffer, bufferSize, requiredBufferSize);
}
private static string getMethodNameFromMetadataImpl(MethodDesc method, out string className, out string namespaceName, string[] enclosingClassName)
{
className = null;
namespaceName = null;
string result = method.GetName();
MetadataType owningType = method.OwningType as MetadataType;
if (owningType != null)
{
className = owningType.GetName();
namespaceName = owningType.GetNamespace();
// Query enclosingClassName when the method is in a nested class
// and get the namespace of enclosing classes (nested class's namespace is empty)
DefType containingType = owningType;
for (int i = 0; i < enclosingClassName.Length; i++)
{
containingType = containingType.ContainingType;
if (containingType == null)
break;
enclosingClassName[i] = containingType.GetName();
namespaceName = containingType.GetNamespace();
}
}
return result;
}
private byte* getMethodNameFromMetadata(CORINFO_METHOD_STRUCT_* ftn, byte** className, byte** namespaceName, byte** enclosingClassNames, nuint maxEnclosingClassNames)
{
MethodDesc method = HandleToObject(ftn);
for (nuint i = 0; i < maxEnclosingClassNames; i++)
enclosingClassNames[i] = null;
if (method.GetTypicalMethodDefinition() is EcmaMethod ecmaMethod)
{
EcmaType owningType = ecmaMethod.OwningType;
var reader = owningType.MetadataReader;
if (className != null)
*className = reader.GetTypeNamePointer(owningType.Handle);
if (namespaceName != null)
*namespaceName = reader.GetTypeNamespacePointer(owningType.Handle);
// Query enclosingClassName when the method is in a nested class
// and get the namespace of enclosing classes (nested class's namespace is empty)
EcmaType containingType = owningType;
for (nuint i = 0; i < maxEnclosingClassNames; i++)
{
containingType = containingType.ContainingType;
if (containingType == null)
break;
enclosingClassNames[i] = reader.GetTypeNamePointer(containingType.Handle);
if (namespaceName != null)
*namespaceName = reader.GetTypeNamespacePointer(containingType.Handle);
}
return reader.GetMethodNamePointer(ecmaMethod.Handle);
}
else
{
string result;
string classResult;
string namespaceResult;
string[] enclosingResults = new string[maxEnclosingClassNames];
result = getMethodNameFromMetadataImpl(method, out classResult, out namespaceResult, enclosingResults);
if (className != null)
*className = classResult != null ? (byte*)GetPin(StringToUTF8(classResult)) : null;
if (namespaceName != null)
*namespaceName = namespaceResult != null ? (byte*)GetPin(StringToUTF8(namespaceResult)) : null;
for (nuint i = 0; i < maxEnclosingClassNames; i++)
enclosingClassNames[i] = enclosingResults[i] != null ? (byte*)GetPin(StringToUTF8(enclosingResults[i])) : null;
return result != null ? (byte*)GetPin(StringToUTF8(result)) : null;
}
}
private uint getMethodHash(CORINFO_METHOD_STRUCT_* ftn)
{
return (uint)HandleToObject(ftn).GetHashCode();
}
private bool getSystemVAmd64PassStructInRegisterDescriptor(CORINFO_CLASS_STRUCT_* structHnd, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr)
{
TypeDesc typeDesc = HandleToObject(structHnd);
SystemVStructClassificator.GetSystemVAmd64PassStructInRegisterDescriptor(typeDesc, out *structPassInRegDescPtr);
return true;
}
private void getSwiftLowering(CORINFO_CLASS_STRUCT_* structHnd, ref CORINFO_SWIFT_LOWERING lowering)
{
lowering = SwiftPhysicalLowering.LowerTypeForSwiftSignature(HandleToObject(structHnd));
}
private void getFpStructLowering(CORINFO_CLASS_STRUCT_* structHnd, ref CORINFO_FPSTRUCT_LOWERING lowering)
{
FpStructInRegistersInfo info = RiscVLoongArch64FpStruct.GetFpStructInRegistersInfo(
HandleToObject(structHnd), _compilation.TypeSystemContext.Target.Architecture);
if (info.flags != FpStruct.UseIntCallConv)
{
lowering.byIntegerCallConv = false;
lowering.Offsets[0] = info.offset1st;
lowering.Offsets[1] = info.offset2nd;
lowering.numLoweredElements = ((info.flags & FpStruct.OnlyOne) != 0) ? 1 : 2;
if ((info.flags & (FpStruct.BothFloat | FpStruct.FloatInt | FpStruct.OnlyOne)) != 0)
lowering.LoweredElements[0] = (info.SizeShift1st() == 3) ? CorInfoType.CORINFO_TYPE_DOUBLE : CorInfoType.CORINFO_TYPE_FLOAT;
if ((info.flags & (FpStruct.BothFloat | FpStruct.IntFloat)) != 0)
lowering.LoweredElements[1] = (info.SizeShift2nd() == 3) ? CorInfoType.CORINFO_TYPE_DOUBLE : CorInfoType.CORINFO_TYPE_FLOAT;
if ((info.flags & (FpStruct.FloatInt | FpStruct.IntFloat)) != 0)
{
int index = ((info.flags & FpStruct.FloatInt) != 0) ? 1 : 0;
uint sizeShift = (index == 0) ? info.SizeShift1st() : info.SizeShift2nd();
lowering.LoweredElements[index] = (CorInfoType)((int)CorInfoType.CORINFO_TYPE_BYTE + sizeShift * 2);
// unittests
Debug.Assert((int)CorInfoType.CORINFO_TYPE_BYTE + 0 * 2 == (int)CorInfoType.CORINFO_TYPE_BYTE);
Debug.Assert((int)CorInfoType.CORINFO_TYPE_BYTE + 1 * 2 == (int)CorInfoType.CORINFO_TYPE_SHORT);
Debug.Assert((int)CorInfoType.CORINFO_TYPE_BYTE + 2 * 2 == (int)CorInfoType.CORINFO_TYPE_INT);
Debug.Assert((int)CorInfoType.CORINFO_TYPE_BYTE + 3 * 2 == (int)CorInfoType.CORINFO_TYPE_LONG);
}
}
else
{
lowering.byIntegerCallConv = true;
}
}
private CorInfoWasmType getWasmLowering(CORINFO_CLASS_STRUCT_* structHnd)
{
TypeDesc type = HandleToObject(structHnd);
#if READYTORUN
LayoutInt classSize = type.GetElementSize();
if (classSize.IsIndeterminate)
{
throw new RequiresRuntimeJitException(type);
}
if (NeedsTypeLayoutCheck(type))
{
ISymbolNode node = _compilation.SymbolNodeFactory.CheckTypeLayout(type);
AddPrecodeFixup(node);
}
#endif
TypeDesc abiType = WasmLowering.LowerToAbiType(type);
if (abiType == null)
{
// Indicate type should be passed by-ref.
return CorInfoWasmType.CORINFO_WASM_TYPE_VOID;
}
WasmValueType wasmAbiType = WasmLowering.LowerType(abiType);
switch (wasmAbiType)
{
case WasmValueType.I32:
return CorInfoWasmType.CORINFO_WASM_TYPE_I32;
case WasmValueType.I64:
return CorInfoWasmType.CORINFO_WASM_TYPE_I64;
case WasmValueType.F32:
return CorInfoWasmType.CORINFO_WASM_TYPE_F32;
case WasmValueType.F64:
return CorInfoWasmType.CORINFO_WASM_TYPE_F64;
default:
ThrowHelper.ThrowInvalidProgramException();
return CorInfoWasmType.CORINFO_WASM_TYPE_I32; // unreachable
}
}
private uint getThreadTLSIndex(ref void* ppIndirection)
{ throw new NotImplementedException("getThreadTLSIndex"); }
private Dictionary<CorInfoHelpFunc, ISymbolNode> _helperCache = new Dictionary<CorInfoHelpFunc, ISymbolNode>();
private void getHelperFtn(CorInfoHelpFunc ftnNum, CORINFO_CONST_LOOKUP *pNativeEntrypoint, CORINFO_METHOD_STRUCT_** pMethod)
{
// We never return a method handle from the managed implementation of this method today
if (pMethod != null)
*pMethod = null;
if (pNativeEntrypoint != null)
{
ISymbolNode entryPoint;
if (!_helperCache.TryGetValue(ftnNum, out entryPoint))
{
entryPoint = GetHelperFtnUncached(ftnNum);
_helperCache.Add(ftnNum, entryPoint);
}
if (entryPoint.RepresentsIndirectionCell)
{
pNativeEntrypoint->addr = (void*)ObjectToHandle(entryPoint);
pNativeEntrypoint->accessType = InfoAccessType.IAT_PVALUE;
}
else
{
pNativeEntrypoint->addr = (void*)ObjectToHandle(entryPoint);
pNativeEntrypoint->accessType = InfoAccessType.IAT_VALUE;
}
}
}
public static ReadyToRunHelperId GetReadyToRunHelperFromStaticBaseHelper(CorInfoHelpFunc helper)
{
ReadyToRunHelperId res;
switch (helper)
{
case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_GCSTATIC_BASE:
res = ReadyToRunHelperId.GetGCStaticBase;
break;
case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_NONGCSTATIC_BASE:
res = ReadyToRunHelperId.GetNonGCStaticBase;
break;
case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_THREADSTATIC_BASE:
res = ReadyToRunHelperId.GetThreadStaticBase;
break;
case CorInfoHelpFunc.CORINFO_HELP_READYTORUN_NONGCTHREADSTATIC_BASE:
res = ReadyToRunHelperId.GetThreadNonGcStaticBase;
break;
default:
throw new NotImplementedException("ReadyToRun: " + helper.ToString());
}
return res;
}
private void getFunctionFixedEntryPoint(CORINFO_METHOD_STRUCT_* ftn, bool isUnsafeFunctionPointer, ref CORINFO_CONST_LOOKUP pResult)
{ throw new NotImplementedException("getFunctionFixedEntryPoint"); }
private CORINFO_MODULE_STRUCT_* embedModuleHandle(CORINFO_MODULE_STRUCT_* handle, ref void* ppIndirection)
{ throw new NotImplementedException("embedModuleHandle"); }
private CORINFO_FIELD_STRUCT_* embedFieldHandle(CORINFO_FIELD_STRUCT_* handle, ref void* ppIndirection)
{ throw new NotImplementedException("embedFieldHandle"); }
private static CORINFO_RUNTIME_LOOKUP_KIND GetGenericRuntimeLookupKind(MethodDesc method)
{
if (method.RequiresInstMethodDescArg())
return CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_METHODPARAM;
else if (method.RequiresInstMethodTableArg())
return CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_CLASSPARAM;
else
{
Debug.Assert(method.AcquiresInstMethodTableFromThis());
return CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_THISOBJ;
}
}
private void getLocationOfThisType(CORINFO_METHOD_STRUCT_* context, ref CORINFO_LOOKUP_KIND result)
{
MethodDesc method = HandleToObject(context);
if (method.IsSharedByGenericInstantiations)
{
result.needsRuntimeLookup = true;
result.runtimeLookupKind = GetGenericRuntimeLookupKind(method);
}
else
{
result.needsRuntimeLookup = false;
result.runtimeLookupKind = CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_THISOBJ;
}
}
private void* GetCookieForInterpreterCalliSig(CORINFO_SIG_INFO* szMetaSig)
{ throw new NotImplementedException("GetCookieForInterpreterCalliSig"); }
private void* GetCookieForPInvokeCalliSig(CORINFO_SIG_INFO* szMetaSig, ref void* ppIndirection)
{
#if READYTORUN
throw new RequiresRuntimeJitException($"{MethodBeingCompiled} -> {nameof(GetCookieForPInvokeCalliSig)}");
#else
throw new NotImplementedException(nameof(GetCookieForPInvokeCalliSig));
#endif
}
#pragma warning disable CA1822 // Mark members as static
private CORINFO_JUST_MY_CODE_HANDLE_* getJustMyCodeHandle(CORINFO_METHOD_STRUCT_* method, ref CORINFO_JUST_MY_CODE_HANDLE_* ppIndirection)
#pragma warning restore CA1822 // Mark members as static
{
ppIndirection = null;
return null;
}
private void GetProfilingHandle(ref bool pbHookFunction, ref void* pProfilerHandle, ref bool pbIndirectedHandles)
{ throw new NotImplementedException("GetProfilingHandle"); }
/// <summary>
/// Create a CORINFO_CONST_LOOKUP to a symbol and put the address into the addr field
/// </summary>
private CORINFO_CONST_LOOKUP CreateConstLookupToSymbol(ISymbolNode symbol)
{
CORINFO_CONST_LOOKUP constLookup = default(CORINFO_CONST_LOOKUP);
constLookup.addr = (void*)ObjectToHandle(symbol);
constLookup.accessType = symbol.RepresentsIndirectionCell ? InfoAccessType.IAT_PVALUE : InfoAccessType.IAT_VALUE;
return constLookup;
}
private CORINFO_CLASS_STRUCT_* getStaticFieldCurrentClass(CORINFO_FIELD_STRUCT_* field, byte* pIsSpeculative)
{
if (pIsSpeculative != null)
*pIsSpeculative = 1;
return null;
}
private IntPtr getVarArgsHandle(CORINFO_SIG_INFO* pSig, CORINFO_METHOD_STRUCT_* methHnd, ref void* ppIndirection)
{ throw new NotImplementedException("getVarArgsHandle"); }
private InfoAccessType emptyStringLiteral(ref void* ppValue)
{
return constructStringLiteral(_methodScope, (mdToken)CorTokenType.mdtString, ref ppValue);
}
private uint getFieldThreadLocalStoreID(CORINFO_FIELD_STRUCT_* field, ref void* ppIndirection)
{ throw new NotImplementedException("getFieldThreadLocalStoreID"); }
private CORINFO_METHOD_STRUCT_* GetDelegateCtor(CORINFO_METHOD_STRUCT_* methHnd, CORINFO_CLASS_STRUCT_* clsHnd, CORINFO_METHOD_STRUCT_* targetMethodHnd, ref DelegateCtorArgs pCtorData)
{ throw new NotImplementedException("GetDelegateCtor"); }
private void MethodCompileComplete(CORINFO_METHOD_STRUCT_* methHnd)
{ throw new NotImplementedException("MethodCompileComplete"); }
#pragma warning disable CA1822 // Mark members as static
private bool getTailCallHelpers(ref CORINFO_RESOLVED_TOKEN callToken, CORINFO_SIG_INFO* sig, CORINFO_GET_TAILCALL_HELPERS_FLAGS flags, ref CORINFO_TAILCALL_HELPERS pResult)
#pragma warning restore CA1822 // Mark members as static
{
// Slow tailcalls are not supported yet
// https://github.com/dotnet/runtime/issues/35423
#if READYTORUN
throw new RequiresRuntimeJitException(nameof(getTailCallHelpers));
#else
return false;
#endif
}
private byte[] _code;
private byte[] _coldCode;
private int _codeAlignment;
private byte[] _roData;
private MethodReadOnlyDataNode _roDataBlob;
private int _roDataAlignment;
private byte[] _rwData;
private MethodReadWriteDataNode _rwDataBlob;
private int _rwDataAlignment;
private int _numFrameInfos;
private int _usedFrameInfos;
private FrameInfo[] _frameInfos;
#if READYTORUN
private int _numColdFrameInfos;
private int _usedColdFrameInfos;
private FrameInfo[] _coldFrameInfos;
#endif
private byte[] _gcInfo;
private CORINFO_EH_CLAUSE[] _ehClauses;
private DependencyList _additionalDependencies;
private void allocMem(ref AllocMemArgs args)
{
Span<AllocMemChunk> chunks = new Span<AllocMemChunk>(args.chunks, checked((int)args.chunksCount));
uint roDataSize = 0;
uint rwDataSize = 0;
_codeAlignment = -1;
_roDataAlignment = -1;
_rwDataAlignment = -1;
// ELF/MachO do not support relocations from read-only sections to code sections, so we must put these
// into read-write sections.
bool ChunkNeedsReadWriteSection(in AllocMemChunk chunk)
=> (chunk.flags & CorJitAllocMemFlag.CORJIT_ALLOCMEM_HAS_POINTERS_TO_CODE) != 0 && !_compilation.TypeSystemContext.Target.IsWindows;
foreach (ref AllocMemChunk chunk in chunks)
{
if ((chunk.flags & CorJitAllocMemFlag.CORJIT_ALLOCMEM_HOT_CODE) != 0)
{
chunk.block = (byte*)GetPin(_code = new byte[chunk.size]);
chunk.blockRW = chunk.block;
_codeAlignment = (int)chunk.alignment;
}
else if ((chunk.flags & CorJitAllocMemFlag.CORJIT_ALLOCMEM_COLD_CODE) != 0)
{
chunk.block = (byte*)GetPin(_coldCode = new byte[chunk.size]);
chunk.blockRW = chunk.block;
Debug.Assert(chunk.alignment == 1);
#if READYTORUN
_methodColdCodeNode = new MethodColdCodeNode(MethodBeingCompiled);
#endif
}
else if (ChunkNeedsReadWriteSection(chunk))
{
// For ELF/MachO we need to put data containing relocations into .text into a RW section
rwDataSize = (uint)((int)rwDataSize).AlignUp((int)chunk.alignment);
rwDataSize += chunk.size;
_rwDataAlignment = Math.Max(_rwDataAlignment, (int)chunk.alignment);
}
else
{
roDataSize = (uint)((int)roDataSize).AlignUp((int)chunk.alignment);
roDataSize += chunk.size;
_roDataAlignment = Math.Max(_roDataAlignment, (int)chunk.alignment);
}
}
if (roDataSize != 0)
{
_roData = new byte[roDataSize];
_roDataBlob = new MethodReadOnlyDataNode(MethodBeingCompiled);
byte* roDataBlock = (byte*)GetPin(_roData);
int offset = 0;
foreach (ref AllocMemChunk chunk in chunks)
{
if ((chunk.flags & (CorJitAllocMemFlag.CORJIT_ALLOCMEM_HOT_CODE | CorJitAllocMemFlag.CORJIT_ALLOCMEM_COLD_CODE)) != 0)
{
continue;
}
if (ChunkNeedsReadWriteSection(chunk))
{
continue;
}
offset = offset.AlignUp((int)chunk.alignment);
chunk.block = roDataBlock + offset;
chunk.blockRW = chunk.block;
offset += (int)chunk.size;
}
Debug.Assert(offset <= roDataSize);
}
if (rwDataSize != 0)
{
_rwData = new byte[rwDataSize];
_rwDataBlob = new MethodReadWriteDataNode(MethodBeingCompiled);
byte* rwDataBlock = (byte*)GetPin(_rwData);
int offset = 0;
foreach (ref AllocMemChunk chunk in chunks)
{
if ((chunk.flags & (CorJitAllocMemFlag.CORJIT_ALLOCMEM_HOT_CODE | CorJitAllocMemFlag.CORJIT_ALLOCMEM_COLD_CODE)) != 0)
{
continue;
}
if (ChunkNeedsReadWriteSection(chunk))
{
offset = offset.AlignUp((int)chunk.alignment);
chunk.block = rwDataBlock + offset;
chunk.blockRW = chunk.block;
offset += (int)chunk.size;
}
}
Debug.Assert(offset <= rwDataSize);
}
if (_numFrameInfos > 0)
{
_frameInfos = new FrameInfo[_numFrameInfos];
}
#if READYTORUN
if (_numColdFrameInfos > 0)
{
_coldFrameInfos = new FrameInfo[_numColdFrameInfos];
}
#endif
}
private void reserveUnwindInfo(bool isFunclet, bool isColdCode, uint unwindSize)
{
#if READYTORUN
if (isColdCode)
{
_numColdFrameInfos++;
}
else
#endif
{
_numFrameInfos++;
}
}
private void allocUnwindInfo(byte* pHotCode, byte* pColdCode, uint startOffset, uint endOffset, uint unwindSize, byte* pUnwindBlock, CorJitFuncKind funcKind)
{
Debug.Assert(FrameInfoFlags.Filter == (FrameInfoFlags)CorJitFuncKind.CORJIT_FUNC_FILTER);
Debug.Assert(FrameInfoFlags.Handler == (FrameInfoFlags)CorJitFuncKind.CORJIT_FUNC_HANDLER);
FrameInfoFlags flags = (FrameInfoFlags)funcKind;
if (funcKind == CorJitFuncKind.CORJIT_FUNC_ROOT)
{
if (this.MethodBeingCompiled.IsUnmanagedCallersOnly)
flags |= FrameInfoFlags.ReversePInvoke;
}
byte[] blobData = null;
if (pUnwindBlock != null || pColdCode == null)
{
blobData = new byte[unwindSize];
for (uint i = 0; i < unwindSize; i++)
{
blobData[i] = pUnwindBlock[i];
}
}
#if !READYTORUN
var target = _compilation.TypeSystemContext.Target;
if (target.Architecture == TargetArchitecture.ARM64 && target.OperatingSystem == TargetOS.Linux)
{
blobData = CompressARM64CFI(blobData);
}
#endif
#if READYTORUN
if (pColdCode == null)
#endif
{
_frameInfos[_usedFrameInfos++] = new FrameInfo(flags, (int)startOffset, (int)endOffset, blobData);
}
#if READYTORUN
else
{
_coldFrameInfos[_usedColdFrameInfos++] = new FrameInfo(flags, (int)startOffset, (int)endOffset, blobData);
}
#endif
}
private void* allocGCInfo(nuint size)
{
_gcInfo = new byte[size];
return (void*)GetPin(_gcInfo);
}
#pragma warning disable CA1822 // Mark members as static
private bool logMsg(uint level, byte* fmt, IntPtr args)
#pragma warning restore CA1822 // Mark members as static
{
// Console.WriteLine(Marshal.PtrToStringUTF8((IntPtr)fmt));
return false;
}
private int doAssert(byte* szFile, int iLine, byte* szExpr)
{
Logger.LogMessage(Marshal.PtrToStringUTF8((IntPtr)szFile) + ":" + iLine);
Logger.LogMessage(Marshal.PtrToStringUTF8((IntPtr)szExpr));
return 1;
}
#pragma warning disable CA1822 // Mark members as static
private void reportFatalError(CorJitResult result)
#pragma warning restore CA1822 // Mark members as static
{
// We could add some logging here, but for now it's unnecessary.
// CompileMethod is going to fail with this CorJitResult anyway.
}
private ArrayBuilder<Relocation> _codeRelocs;
private ArrayBuilder<Relocation> _roDataRelocs;
private ArrayBuilder<Relocation> _rwDataRelocs;
#if READYTORUN
private ArrayBuilder<Relocation> _coldCodeRelocs;
#endif
/// <summary>
/// Various type of block.
/// </summary>
public enum BlockType : sbyte
{
/// <summary>Not a generated block.</summary>
Unknown = -1,
/// <summary>Represent code.</summary>
Code = 0,
/// <summary>Represent cold code (i.e. code not called frequently).</summary>
ColdCode = 1,
/// <summary>Read-only data.</summary>
ROData = 2,
/// <summary>Read-write data.</summary>
RWData = 3,
/// <summary>Instrumented Block Count Data</summary>
BBCounts = 4
}
private BlockType findKnownBlock(void* location, out int offset)
{
fixed (byte* pCode = _code)
{
if (pCode <= (byte*)location && (byte*)location < pCode + _code.Length)
{
offset = (int)((byte*)location - pCode);
return BlockType.Code;
}
}
if (_coldCode != null)
{
fixed (byte* pColdCode = _coldCode)
{
if (pColdCode <= (byte*)location && (byte*)location < pColdCode + _coldCode.Length)
{
offset = (int)((byte*)location - pColdCode);
return BlockType.ColdCode;
}
}
}
if (_roData != null)
{
fixed (byte* pROData = _roData)
{
if (pROData <= (byte*)location && (byte*)location < pROData + _roData.Length)
{
offset = (int)((byte*)location - pROData);
return BlockType.ROData;
}
}
}
if (_rwData != null)
{
fixed (byte* pRWData = _rwData)
{
if (pRWData <= (byte*)location && (byte*)location < pRWData + _rwData.Length)
{
offset = (int)((byte*)location - pRWData);
return BlockType.RWData;
}
}
}
{
BlockType retBlockType = BlockType.Unknown;
offset = 0;
findKnownBBCountBlock(ref retBlockType, location, ref offset);
if (retBlockType == BlockType.BBCounts)
return retBlockType;
}
offset = 0;
return BlockType.Unknown;
}
partial void findKnownBBCountBlock(ref BlockType blockType, void* location, ref int offset);
private ref ArrayBuilder<Relocation> findRelocBlock(BlockType blockType, out int length)
{
switch (blockType)
{
case BlockType.Code:
length = _code.Length;
return ref _codeRelocs;
case BlockType.ROData:
length = _roData.Length;
return ref _roDataRelocs;
case BlockType.RWData:
length = _rwData.Length;
return ref _rwDataRelocs;
#if READYTORUN
case BlockType.ColdCode:
length = _coldCode.Length;
return ref _coldCodeRelocs;
#endif
default:
throw new NotImplementedException("Arbitrary relocs");
}
}
// Translates relocation type constants used by JIT to RelocType enumeration
private RelocType GetRelocType(CorInfoReloc reloc)
=> reloc switch
{
CorInfoReloc.DIRECT => PointerSize == 8 ? RelocType.IMAGE_REL_BASED_DIR64 : RelocType.IMAGE_REL_BASED_HIGHLOW,
CorInfoReloc.RELATIVE32 => RelocType.IMAGE_REL_BASED_REL32,
CorInfoReloc.ARM64_BRANCH26 => RelocType.IMAGE_REL_BASED_ARM64_BRANCH26,
CorInfoReloc.ARM64_PAGEBASE_REL21 => RelocType.IMAGE_REL_BASED_ARM64_PAGEBASE_REL21,
CorInfoReloc.ARM64_PAGEOFFSET_12A => RelocType.IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A,
CorInfoReloc.ARM64_LIN_TLSDESC_ADR_PAGE21 => RelocType.IMAGE_REL_AARCH64_TLSDESC_ADR_PAGE21,
CorInfoReloc.ARM64_LIN_TLSDESC_LD64_LO12 => RelocType.IMAGE_REL_AARCH64_TLSDESC_LD64_LO12,
CorInfoReloc.ARM64_LIN_TLSDESC_ADD_LO12 => RelocType.IMAGE_REL_AARCH64_TLSDESC_ADD_LO12,
CorInfoReloc.ARM64_LIN_TLSDESC_CALL => RelocType.IMAGE_REL_AARCH64_TLSDESC_CALL,
CorInfoReloc.ARM64_WIN_TLS_SECREL_HIGH12A => RelocType.IMAGE_REL_ARM64_TLS_SECREL_HIGH12A,
CorInfoReloc.ARM64_WIN_TLS_SECREL_LOW12A => RelocType.IMAGE_REL_ARM64_TLS_SECREL_LOW12A,
CorInfoReloc.AMD64_WIN_SECREL => RelocType.IMAGE_REL_SECREL,
CorInfoReloc.AMD64_LIN_TLSGD => RelocType.IMAGE_REL_TLSGD,
CorInfoReloc.ARM32_THUMB_BRANCH24 => RelocType.IMAGE_REL_BASED_THUMB_BRANCH24,
CorInfoReloc.ARM32_THUMB_MOV32 => RelocType.IMAGE_REL_BASED_THUMB_MOV32,
CorInfoReloc.ARM32_THUMB_MOV32_PCREL => RelocType.IMAGE_REL_BASED_THUMB_MOV32_PCREL,
CorInfoReloc.LOONGARCH64_PC => RelocType.IMAGE_REL_BASED_LOONGARCH64_PC,
CorInfoReloc.LOONGARCH64_JIR => RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR,
CorInfoReloc.RISCV64_CALL_PLT => RelocType.IMAGE_REL_BASED_RISCV64_CALL_PLT,
CorInfoReloc.RISCV64_PCREL_I => RelocType.IMAGE_REL_BASED_RISCV64_PCREL_I,
CorInfoReloc.RISCV64_PCREL_S => RelocType.IMAGE_REL_BASED_RISCV64_PCREL_S,
CorInfoReloc.WASM_FUNCTION_INDEX_LEB => RelocType.WASM_FUNCTION_INDEX_LEB,
CorInfoReloc.WASM_TABLE_INDEX_SLEB => RelocType.WASM_TABLE_INDEX_SLEB,
CorInfoReloc.WASM_MEMORY_ADDR_LEB => RelocType.WASM_MEMORY_ADDR_LEB,
CorInfoReloc.WASM_MEMORY_ADDR_SLEB => RelocType.WASM_MEMORY_ADDR_SLEB,
CorInfoReloc.WASM_MEMORY_ADDR_REL_SLEB => RelocType.WASM_MEMORY_ADDR_REL_SLEB,
CorInfoReloc.WASM_MEMORY_ADDR_REL_LEB => RelocType.WASM_MEMORY_ADDR_REL_LEB,
CorInfoReloc.WASM_TYPE_INDEX_LEB => RelocType.WASM_TYPE_INDEX_LEB,
CorInfoReloc.WASM_GLOBAL_INDEX_LEB => RelocType.WASM_GLOBAL_INDEX_LEB,
_ => throw new ArgumentException("Unsupported relocation type: " + reloc),
};
private void recordRelocation(void* location, void* locationRW, void* target, CorInfoReloc fRelocType, int addlDelta)
{
int relocOffset;
BlockType locationBlock = findKnownBlock(location, out relocOffset);
Debug.Assert(locationBlock != BlockType.Unknown, "BlockType.Unknown not expected");
int length;
ref ArrayBuilder<Relocation> sourceBlock = ref findRelocBlock(locationBlock, out length);
int relocDelta;
BlockType targetBlock = findKnownBlock(target, out relocDelta);
ISymbolNode relocTarget;
switch (targetBlock)
{
case BlockType.Code:
relocTarget = _methodCodeNode;
break;
case BlockType.ColdCode:
#if READYTORUN
Debug.Assert(_methodColdCodeNode != null);
relocTarget = _methodColdCodeNode;
break;
#else
throw new NotImplementedException("ColdCode relocs");
#endif
case BlockType.ROData:
relocTarget = _roDataBlob;
break;
case BlockType.RWData:
relocTarget = _rwDataBlob;
break;
#if READYTORUN
case BlockType.BBCounts:
relocTarget = null;
break;
#endif
default:
// Reloc points to something outside of the generated blocks
var targetObject = HandleToObject(target);
#if READYTORUN
if (targetObject is RequiresRuntimeJitIfUsedSymbol requiresRuntimeSymbol)
{
throw new RequiresRuntimeJitException(requiresRuntimeSymbol.Message);
}
#endif
relocTarget = (ISymbolNode)targetObject;
break;
}
RelocType relocType = GetRelocType(fRelocType);
relocDelta += addlDelta;
// relocDelta is stored as the value
Relocation.WriteValue(relocType, location, relocDelta);
if (sourceBlock.Count == 0)
sourceBlock.EnsureCapacity(length / 32 + 1);
sourceBlock.Add(new Relocation(relocType, relocOffset, relocTarget));
}
private CorInfoReloc getRelocTypeHint(void* target)
{
switch (_compilation.TypeSystemContext.Target.Architecture)
{
case TargetArchitecture.X64:
return CorInfoReloc.RELATIVE32;
#if READYTORUN
case TargetArchitecture.ARM:
return CorInfoReloc.ARM32_THUMB_BRANCH24;
#endif
default:
return CorInfoReloc.NONE;
}
}
private uint getExpectedTargetArchitecture()
{
TargetArchitecture arch = _compilation.TypeSystemContext.Target.Architecture;
switch (arch)
{
case TargetArchitecture.X86:
return (uint)CorInfoArch.CORINFO_ARCH_X86;
case TargetArchitecture.X64:
return (uint)CorInfoArch.CORINFO_ARCH_X64;
case TargetArchitecture.ARM:
return (uint)CorInfoArch.CORINFO_ARCH_ARM;
case TargetArchitecture.ARM64:
return (uint)CorInfoArch.CORINFO_ARCH_ARM64;
case TargetArchitecture.LoongArch64:
return (uint)CorInfoArch.CORINFO_ARCH_LOONGARCH64;
case TargetArchitecture.RiscV64:
return (uint)CorInfoArch.CORINFO_ARCH_RISCV64;
case TargetArchitecture.Wasm32:
return (uint)CorInfoArch.CORINFO_ARCH_WASM32;
default:
throw new NotImplementedException("Expected target architecture is not supported");
}
}
private bool doesFieldBelongToClass(CORINFO_FIELD_STRUCT_* fld, CORINFO_CLASS_STRUCT_* cls)
{
var field = HandleToObject(fld);
var queryType = HandleToObject(cls);
Debug.Assert(!field.IsStatic);
// doesFieldBelongToClass implements the predicate of...
// if field is not associated with the class in any way, return false.
// if field is the only FieldDesc that the JIT might see for a given class handle
// and logical field pair then return true. This is needed as the field handle here
// is used as a key into a hashtable mapping writes to fields to value numbers.
//
// In this implementation this is made more complex as the JIT is exposed to CORINFO_FIELD_STRUCT
// pointers which represent exact instantions, so performing exact matching is the necessary approach
// BaseType._field, BaseType -> true
// BaseType._field, DerivedType -> true
// BaseType<__Canon>._field, BaseType<__Canon> -> true
// BaseType<__Canon>._field, BaseType<string> -> false
// BaseType<__Canon>._field, BaseType<object> -> false
// BaseType<sbyte>._field, BaseType<sbyte> -> true
// BaseType<sbyte>._field, BaseType<byte> -> false
var fieldOwnerType = field.OwningType;
while (queryType != null)
{
if (fieldOwnerType == queryType)
return true;
queryType = queryType.BaseType;
}
return false;
}
private bool isMethodDefinedInCoreLib()
{
TypeDesc owningType = MethodBeingCompiled.OwningType;
MetadataType owningMetadataType = owningType as MetadataType;
if (owningMetadataType == null)
{
return false;
}
return owningMetadataType.Module == _compilation.TypeSystemContext.SystemModule;
}
private uint getJitFlags(ref CORJIT_FLAGS flags, uint sizeInBytes)
{
// Read the user-defined configuration options.
foreach (var flag in JitConfigProvider.Instance.Flags)
flags.Set(flag);
flags.InstructionSetFlags.Add(_compilation.InstructionSetSupport.OptimisticFlags);
// Set the rest of the flags that don't make sense to expose publicly.
flags.Set(CorJitFlag.CORJIT_FLAG_AOT);
flags.Set(CorJitFlag.CORJIT_FLAG_RELOC);
flags.Set(CorJitFlag.CORJIT_FLAG_USE_PINVOKE_HELPERS);
#if !READYTORUN
flags.Set(CorJitFlag.CORJIT_FLAG_USE_DISPATCH_HELPERS);
#endif
TargetArchitecture targetArchitecture = _compilation.TypeSystemContext.Target.Architecture;
switch (targetArchitecture)
{
case TargetArchitecture.X64:
case TargetArchitecture.X86:
Debug.Assert(InstructionSet.X86_X86Base == InstructionSet.X64_X86Base);
Debug.Assert(_compilation.InstructionSetSupport.IsInstructionSetSupported(InstructionSet.X86_X86Base));
break;
case TargetArchitecture.ARM64:
Debug.Assert(_compilation.InstructionSetSupport.IsInstructionSetSupported(InstructionSet.ARM64_AdvSimd));
break;
}
if (targetArchitecture == TargetArchitecture.ARM && !_compilation.TypeSystemContext.Target.IsWindows)
flags.Set(CorJitFlag.CORJIT_FLAG_RELATIVE_CODE_RELOCS);
if (targetArchitecture == TargetArchitecture.RiscV64)
flags.Set(CorJitFlag.CORJIT_FLAG_FRAMED);
if (this.MethodBeingCompiled.IsUnmanagedCallersOnly)
{
// Validate UnmanagedCallersOnlyAttribute usage
if (!this.MethodBeingCompiled.Signature.IsStatic) // Must be a static method
{
ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramNonStaticMethod, this.MethodBeingCompiled);
}
if (this.MethodBeingCompiled.HasInstantiation || this.MethodBeingCompiled.OwningType.HasInstantiation) // No generics involved
{
ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramGenericMethod, this.MethodBeingCompiled);
}
if (this.MethodBeingCompiled.IsAsync)
{
ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramAsync, this.MethodBeingCompiled);
}
#if READYTORUN
// TODO: enable this check in full AOT
if (Marshaller.IsMarshallingRequired(this.MethodBeingCompiled.Signature, ((MetadataType)this.MethodBeingCompiled.OwningType).Module, this.MethodBeingCompiled.GetUnmanagedCallersOnlyMethodCallingConventions())) // Only blittable arguments
{
ThrowHelper.ThrowInvalidProgramException(ExceptionStringID.InvalidProgramNonBlittableTypes, this.MethodBeingCompiled);
}
#endif
flags.Set(CorJitFlag.CORJIT_FLAG_REVERSE_PINVOKE);
}
if (this.MethodBeingCompiled.IsPInvoke)
{
flags.Set(CorJitFlag.CORJIT_FLAG_IL_STUB);
}
if (this.MethodBeingCompiled.IsNoOptimization)
{
flags.Set(CorJitFlag.CORJIT_FLAG_MIN_OPT);
}
if (this.MethodBeingCompiled.Context.Target.Abi == TargetAbi.NativeAotArmel)
{
flags.Set(CorJitFlag.CORJIT_FLAG_SOFTFP_ABI);
}
if (this.MethodBeingCompiled.IsAsyncCall()
#if !READYTORUN
|| (_compilation.TypeSystemContext.IsSpecialUnboxingThunk(this.MethodBeingCompiled) && _compilation.TypeSystemContext.GetTargetOfSpecialUnboxingThunk(this.MethodBeingCompiled).IsAsyncCall())
|| (_compilation.TypeSystemContext.IsDefaultInterfaceMethodImplementationInstantiationThunk(this.MethodBeingCompiled) && _compilation.TypeSystemContext.GetTargetOfDefaultInterfaceMethodImplementationInstantiationThunk(this.MethodBeingCompiled).IsAsyncCall())
#endif
)
{
flags.Set(CorJitFlag.CORJIT_FLAG_ASYNC);
// Runtime cannot handle hot/cold splitting for async methods
flags.Clear(CorJitFlag.CORJIT_FLAG_PROCSPLIT);
}
#if READYTORUN
if (this.MethodBeingCompiled.Context.Target.OperatingSystem == TargetOS.Browser)
{
flags.Set(CorJitFlag.CORJIT_FLAG_PORTABLE_ENTRY_POINTS);
}
#endif
return (uint)sizeof(CORJIT_FLAGS);
}
private MemoryStream _cachedMemoryStream = new MemoryStream();
public static void ComputeJitPgoInstrumentationSchema(Func<object, IntPtr> objectToHandle, PgoSchemaElem[] pgoResultsSchemas, out PgoInstrumentationSchema[] nativeSchemas, MemoryStream instrumentationData, Func<TypeDesc, bool> typeFilter = null)
{
nativeSchemas = new PgoInstrumentationSchema[pgoResultsSchemas.Length];
instrumentationData.SetLength(0);
BinaryWriter bwInstrumentationData = new BinaryWriter(instrumentationData);
for (int i = 0; i < nativeSchemas.Length; i++)
{
if ((bwInstrumentationData.BaseStream.Position % 8) == 4)
{
bwInstrumentationData.Write(0);
}
Debug.Assert((bwInstrumentationData.BaseStream.Position % 8) == 0);
nativeSchemas[i].Offset = new IntPtr(checked((int)bwInstrumentationData.BaseStream.Position));
nativeSchemas[i].ILOffset = pgoResultsSchemas[i].ILOffset;
nativeSchemas[i].Count = pgoResultsSchemas[i].Count;
nativeSchemas[i].Other = pgoResultsSchemas[i].Other;
nativeSchemas[i].InstrumentationKind = (PgoInstrumentationKind)pgoResultsSchemas[i].InstrumentationKind;
if (pgoResultsSchemas[i].DataObject == null)
{
bwInstrumentationData.Write(pgoResultsSchemas[i].DataLong);
}
else
{
object dataObject = pgoResultsSchemas[i].DataObject;
if (dataObject is int[] intArray)
{
foreach (int intVal in intArray)
bwInstrumentationData.Write(intVal);
}
else if (dataObject is long[] longArray)
{
foreach (long longVal in longArray)
bwInstrumentationData.Write(longVal);
}
else if (dataObject is TypeSystemEntityOrUnknown[] typeArray)
{
foreach (TypeSystemEntityOrUnknown typeVal in typeArray)
{
nint ptrVal;
if (typeVal.AsType != null && (typeFilter == null || typeFilter(typeVal.AsType)))
{
ptrVal = (IntPtr)objectToHandle(typeVal.AsType);
}
else if (typeVal.AsMethod != null)
{
ptrVal = (IntPtr)objectToHandle(typeVal.AsMethod);
}
else
{
// The "Unknown types are the values from 1-33
ptrVal = new IntPtr((typeVal.AsUnknown % 32) + 1);
}
if (IntPtr.Size == 4)
bwInstrumentationData.Write((int)ptrVal);
else
bwInstrumentationData.Write((long)ptrVal);
}
}
}
}
bwInstrumentationData.Flush();
}
private HRESULT getPgoInstrumentationResults(CORINFO_METHOD_STRUCT_* ftnHnd, ref PgoInstrumentationSchema* pSchema, ref uint countSchemaItems, byte** pInstrumentationData,
ref PgoSource pPgoSource, ref bool pDynamicPgo)
{
MethodDesc methodDesc = HandleToObject(ftnHnd);
if (!_pgoResults.TryGetValue(methodDesc, out PgoInstrumentationResults pgoResults))
{
#if READYTORUN
PgoSchemaElem[] pgoResultsSchemas = _compilation.ProfileData.GetAllowSynthesis(_compilation, methodDesc, out bool isSynthesized)?.SchemaData;
if (pgoResultsSchemas != null && isSynthesized && _compilation.ProfileData.EmbedPgoDataInR2RImage)
{
if (_synthesizedPgoDependencies == null)
_synthesizedPgoDependencies = new HashSet<MethodDesc>();
_synthesizedPgoDependencies.Add(methodDesc);
}
#else
PgoSchemaElem[] pgoResultsSchemas = _compilation.ProfileData[methodDesc]?.SchemaData;
#endif
if (pgoResultsSchemas == null)
{
pgoResults.hr = HRESULT.E_NOTIMPL;
}
else
{
#pragma warning disable SA1001, SA1113, SA1115 // Commas should be spaced correctly
ComputeJitPgoInstrumentationSchema(ObjectToHandle, pgoResultsSchemas, out var nativeSchemas, _cachedMemoryStream
#if !READYTORUN
, _compilation.CanReferenceConstructedMethodTable
#endif
);
#pragma warning restore SA1001, SA1113, SA1115 // Commas should be spaced correctly
var instrumentationData = _cachedMemoryStream.ToArray();
pgoResults.pInstrumentationData = (byte*)GetPin(instrumentationData);
pgoResults.countSchemaItems = (uint)nativeSchemas.Length;
pgoResults.pSchema = (PgoInstrumentationSchema*)GetPin(nativeSchemas);
pgoResults.hr = HRESULT.S_OK;
}
_pgoResults.Add(methodDesc, pgoResults);
}
pSchema = pgoResults.pSchema;
countSchemaItems = pgoResults.countSchemaItems;
*pInstrumentationData = pgoResults.pInstrumentationData;
pPgoSource = PgoSource.Static;
pDynamicPgo = false;
return pgoResults.hr;
}
#if READYTORUN
InstructionSetFlags _actualInstructionSetSupported;
InstructionSetFlags _actualInstructionSetUnsupported;
private bool notifyInstructionSetUsage(InstructionSet instructionSet, bool supportEnabled)
{
instructionSet = InstructionSetFlags.ConvertToImpliedInstructionSetForVectorInstructionSets(_compilation.TypeSystemContext.Target.Architecture, instructionSet);
Debug.Assert(!_compilation.InstructionSetSupport.NonSpecifiableFlags.HasInstructionSet(instructionSet));
if (supportEnabled)
{
_actualInstructionSetSupported.AddInstructionSet(instructionSet);
}
else
{
// By policy we code review all changes into corelib, such that failing to use an instruction
// set is not a reason to not support usage of it. Except for functions which check if a given
// feature is supported or hardware accelerated.
if (!isMethodDefinedInCoreLib())
{
_actualInstructionSetUnsupported.AddInstructionSet(instructionSet);
}
else
{
// We want explicitly implemented ISimdVector<TSelf, T> APIs to still be expanded where possible
// but, they all prefix the qualified name of the interface first, so we'll check for that and
// skip the prefix before trying to resolve the method.
ReadOnlySpan<char> methodName = MethodBeingCompiled.GetName().AsSpan();
if (methodName.StartsWith("System.Runtime.Intrinsics.ISimdVector<System."))
{
ReadOnlySpan<char> partialMethodName = methodName.Slice(45);
if (partialMethodName.StartsWith("Numerics.Vector"))
{
partialMethodName = partialMethodName.Slice(15);
if (partialMethodName.StartsWith("<T>,T>."))
{
methodName = partialMethodName.Slice(7);
}
}
if (partialMethodName.StartsWith("Runtime.Intrinsics.Vector"))
{
partialMethodName = partialMethodName.Slice(25);
if (partialMethodName.StartsWith("64<T>,T>."))
{
methodName = partialMethodName.Slice(9);
}
else if (partialMethodName.StartsWith("128<T>,T>.") ||
partialMethodName.StartsWith("256<T>,T>.") ||
partialMethodName.StartsWith("512<T>,T>."))
{
methodName = partialMethodName.Slice(10);
}
}
}
if (methodName.Equals("get_IsSupported", StringComparison.Ordinal) ||
methodName.Equals("get_IsHardwareAccelerated", StringComparison.Ordinal))
{
_actualInstructionSetUnsupported.AddInstructionSet(instructionSet);
}
}
}
return supportEnabled;
}
#else
private bool notifyInstructionSetUsage(InstructionSet instructionSet, bool supportEnabled)
{
instructionSet = InstructionSetFlags.ConvertToImpliedInstructionSetForVectorInstructionSets(_compilation.TypeSystemContext.Target.Architecture, instructionSet);
Debug.Assert(!_compilation.InstructionSetSupport.NonSpecifiableFlags.HasInstructionSet(instructionSet));
return supportEnabled ? _compilation.InstructionSetSupport.IsInstructionSetSupported(instructionSet) : false;
}
#endif
private static bool TryReadRvaFieldData(FieldDesc field, byte* buffer, int bufferSize, int valueOffset)
{
Debug.Assert(buffer != null);
Debug.Assert(bufferSize > 0);
Debug.Assert(valueOffset >= 0);
Debug.Assert(field.IsStatic);
Debug.Assert(field.HasRva);
if (!field.IsThreadStatic && field.IsInitOnly && field is EcmaField ecmaField)
{
ReadOnlySpan<byte> rvaData = ecmaField.GetFieldRvaData();
if (rvaData.Length >= bufferSize && valueOffset <= rvaData.Length - bufferSize)
{
rvaData.Slice(valueOffset, bufferSize).CopyTo(new Span<byte>(buffer, bufferSize));
return true;
}
}
return false;
}
private CORINFO_METHOD_STRUCT_* getSpecialCopyHelper(CORINFO_CLASS_STRUCT_* type)
{
throw new NotImplementedException("getSpecialCopyHelper");
}
}
}
|