Generic lookup support (#6449)
* Generic lookup support This change introduces CPAOT support for all three forms of generic lookup (THISOBJ, CLASSPARAM, METHODPARAM) and the corresponding new GenericLookupSignature. I have added several unit tests exercising the new functionality. * Further fixes for generic lookup support My initial generic lookup change only dealt with type-based lookup. During experimentation with framework compilation I hit the need to also implement method-based generic lookups. This change mostly expands the initial implementation to also cater for method lookups by expanding the RuntimeDeterminedTypeHelper and bits of other code as appropriate. I have also managed to get a bit more insight into emitting signatures for instantited methods. CoreCLR runtime doesn't support direct consumption of mdtMethodSpec tokens in method signatures - instantiation type arguments for generic methods must always be explicitly specified in the signature; it however does support direct consumption of an instantiated OwningType via a mdtTypeSpec. * Always emit signatures in instance entrypoint table using Def tokens I found out that method signatures used to encode methods in the INSTANCE_METHOD_ENTRYPOINTS R2R header table cannot use MemberRef encoding for methods on instantiated types as it hits an assertion failure in the CoreCLR runtime in SigMatchesMethodDesc. This change introduces a boolean parameter the table can use to specify it forbids ref-based encoding. * Version resiliency fix - partial resurrection of tokens During our today meeting JanK, Michal and David finally made me comprehend the token problem. Propagation of tokens from JIT to the managed compiler is indeed necessary due to a corner case regarding version resiliency, however it's thankfully limited to methods outside of the version bubble. The problem is as follows: assume we have a "Program" and a "Library" outside of its version bubble. At IL compilation time of "Program" using Roslyn, the "Library" looks as follows: <code> class Base { void Method(); }; class Derived : Base { void Method(); } </code> When "Program" calls "Base.Method()." vs "Derived.Method()", the appropriate MemberRef tokens representing "[Library]Base.Method()" and "[Library]Derived.Method()" get stored into the MSIL. In the second step, prior to CPAOT compilation, we replace the "Library" with an updated version where the Method() only exists in the Base class (this is supposed to be a safe change w.r.t. version resiliency): <code> class Base { void Method(); }; class Derived : Base { } </code> When we now run the CPAOT compiler, a lossy transformation takes place - both IL tokens, "[Library]Base.Method()" and "[Library]Derived.Method()" get squashed to the same MethodDesc "[Library]Base.Method()" (because that's where the compiler sees the method at CPAOT time). Without explicit token propagation i.e. with just the reverse synthetic token lookup, we end up encoding one of the methods (an arbitrary one based on which one we saw last) into both indirection cell signatures. This breaks when later, at runtime, someone puts back the original "Library" version where the Method() sits in both classes because both R2R signatures contain the same reference so that the runtime has no way of knowing they should be in fact separate. As the problem is obviously limited to methods outside of the version bubble, we can thankfully keep most of the ModuleTokenResolver and related logic as for types and fields it's completely fine, we just need to revive a bit of logic propagating tokens from JIT for methods. Amusingly enough it turns out that overall it mostly simplifies the code rather than making it more complex. The gist of my change regards splitting "ExternalMethodImport" to two classes, "ExternalMethodImport" and "LocalMethodImport". This split has several nice properties - for LocalMethodImport, we always have a _localMethod available but we don't need to maintain constrained type information - within the version bubble we can look up all tokens using the ECMA objects so we don't really need the constrained calls. The various R2R header tables (method & instance entrypoints, exception info) also mostly need to only emit local methods. ExternalMethodImport's newly include the method token information, propagating it to the method signature encoder - this is necessary as we no longer record method token mapping in ModuleTokenResolver which can now only resolve methods within the version bubble. The change passes the basic R2R unit tests and seems to have a comparable pass rate w.r.t. CoreCLR Pri#0 tests. I have yet to verify that it indeed fixes the above described repro case but in general I believe the change is sound. The only problem I hit is in the method getFunctionEntryPoint where the token is not readily available, for now I highlighted it with a TODO comment; I'm not completely clear on when JIT uses this method; if it turns out this can be used for cross-bubble calls, we'll likely need to fix JIT to pass the appropriate token information to the managed code. Thanks Tomas
This commit is contained in:
Родитель
c547f30176
Коммит
78deb182cd
|
@ -80,26 +80,14 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
Section section = writer.NewSection();
|
||||
VertexArray vertexArray = new VertexArray(section);
|
||||
section.Place(vertexArray);
|
||||
ReadyToRunCodegenNodeFactory r2rFactory = (ReadyToRunCodegenNodeFactory)factory;
|
||||
|
||||
foreach (MethodDesc method in ((ReadyToRunTableManager)factory.MetadataManager).GetCompiledMethods())
|
||||
foreach (MethodWithGCInfo method in r2rFactory.EnumerateCompiledMethods())
|
||||
{
|
||||
MethodWithGCInfo methodCodeNode = factory.MethodEntrypoint(method) as MethodWithGCInfo;
|
||||
if (methodCodeNode == null)
|
||||
{
|
||||
methodCodeNode = ((ExternalMethodImport)factory.MethodEntrypoint(method))?.MethodCodeNode;
|
||||
if (methodCodeNode == null)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (methodCodeNode.IsEmpty)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
MemoryStream methodDebugBlob = new MemoryStream();
|
||||
|
||||
byte[] bounds = CreateBoundsBlobForMethod(methodCodeNode);
|
||||
byte[] vars = CreateVarBlobForMethod(methodCodeNode);
|
||||
byte[] bounds = CreateBoundsBlobForMethod(method);
|
||||
byte[] vars = CreateVarBlobForMethod(method);
|
||||
|
||||
NibbleWriter nibbleWriter = new NibbleWriter();
|
||||
nibbleWriter.WriteUInt((uint)(bounds?.Length ?? 0));
|
||||
|
@ -120,7 +108,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
|
||||
BlobVertex debugBlob = new BlobVertex(methodDebugBlob.ToArray());
|
||||
|
||||
vertexArray.Set(((ReadyToRunCodegenNodeFactory)factory).RuntimeFunctionsTable.GetIndex(methodCodeNode), new DebugInfoVertex(debugBlob));
|
||||
vertexArray.Set(r2rFactory.RuntimeFunctionsTable.GetIndex(method), new DebugInfoVertex(debugBlob));
|
||||
}
|
||||
|
||||
vertexArray.ExpandLayout();
|
||||
|
|
|
@ -18,15 +18,19 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
|
||||
private readonly IMethodNode _targetMethod;
|
||||
|
||||
private readonly ModuleToken _methodToken;
|
||||
|
||||
private readonly SignatureContext _signatureContext;
|
||||
|
||||
public DelegateCtorSignature(
|
||||
TypeDesc delegateType,
|
||||
IMethodNode targetMethod,
|
||||
ModuleToken methodToken,
|
||||
SignatureContext signatureContext)
|
||||
{
|
||||
_delegateType = delegateType;
|
||||
_targetMethod = targetMethod;
|
||||
_methodToken = methodToken;
|
||||
_signatureContext = signatureContext;
|
||||
}
|
||||
|
||||
|
@ -42,10 +46,12 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
builder.EmitByte((byte)ReadyToRunFixupKind.READYTORUN_FIXUP_DelegateCtor);
|
||||
builder.EmitMethodSignature(
|
||||
_targetMethod.Method,
|
||||
constrainedType: null,
|
||||
isUnboxingStub: false,
|
||||
isInstantiatingStub: false,
|
||||
_signatureContext);
|
||||
constrainedType: null,
|
||||
methodToken: _methodToken,
|
||||
enforceDefEncoding: false,
|
||||
_signatureContext,
|
||||
isUnboxingStub: false,
|
||||
isInstantiatingStub: false);
|
||||
builder.EmitTypeSignature(_delegateType, _signatureContext);
|
||||
}
|
||||
|
||||
|
|
|
@ -65,11 +65,11 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
private List<MethodWithGCInfo> _methodNodes;
|
||||
private List<int> _ehInfoOffsets;
|
||||
|
||||
private readonly NodeFactory _nodeFactory;
|
||||
private readonly ReadyToRunCodegenNodeFactory _nodeFactory;
|
||||
|
||||
private readonly EHInfoNode _ehInfoNode;
|
||||
|
||||
public ExceptionInfoLookupTableNode(NodeFactory nodeFactory)
|
||||
public ExceptionInfoLookupTableNode(ReadyToRunCodegenNodeFactory nodeFactory)
|
||||
: base(nodeFactory.Target)
|
||||
{
|
||||
_nodeFactory = nodeFactory;
|
||||
|
@ -94,20 +94,12 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
_methodNodes = new List<MethodWithGCInfo>();
|
||||
_ehInfoOffsets = new List<int>();
|
||||
|
||||
foreach (MethodDesc method in _nodeFactory.MetadataManager.GetCompiledMethods())
|
||||
foreach (MethodWithGCInfo method in _nodeFactory.EnumerateCompiledMethods())
|
||||
{
|
||||
MethodWithGCInfo methodCodeNode = _nodeFactory.MethodEntrypoint(method) as MethodWithGCInfo;
|
||||
if (methodCodeNode == null)
|
||||
{
|
||||
methodCodeNode = ((ExternalMethodImport)_nodeFactory.MethodEntrypoint(method))?.MethodCodeNode;
|
||||
if (methodCodeNode == null)
|
||||
continue;
|
||||
}
|
||||
|
||||
ObjectData ehInfo = methodCodeNode.EHInfo;
|
||||
ObjectData ehInfo = method.EHInfo;
|
||||
if (ehInfo != null && ehInfo.Data.Length != 0)
|
||||
{
|
||||
_methodNodes.Add(methodCodeNode);
|
||||
_methodNodes.Add(method);
|
||||
_ehInfoOffsets.Add(_ehInfoNode.AddEHInfo(ehInfo.Data));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,48 +15,33 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
|
||||
private readonly SignatureContext _signatureContext;
|
||||
|
||||
private readonly MethodWithGCInfo _localMethod;
|
||||
|
||||
public ExternalMethodImport(
|
||||
ReadyToRunCodegenNodeFactory factory,
|
||||
ReadyToRunFixupKind fixupKind,
|
||||
MethodDesc methodDesc,
|
||||
TypeDesc constrainedType,
|
||||
SignatureContext signatureContext,
|
||||
ModuleToken methodToken,
|
||||
bool isUnboxingStub,
|
||||
MethodWithGCInfo localMethod)
|
||||
SignatureContext signatureContext)
|
||||
: base(
|
||||
factory,
|
||||
factory.MethodImports,
|
||||
factory.MethodImports,
|
||||
ReadyToRunHelper.READYTORUN_HELPER_DelayLoad_MethodCall,
|
||||
factory.MethodSignature(
|
||||
fixupKind,
|
||||
methodDesc,
|
||||
constrainedType,
|
||||
signatureContext,
|
||||
isUnboxingStub,
|
||||
fixupKind,
|
||||
methodDesc,
|
||||
constrainedType,
|
||||
methodToken,
|
||||
signatureContext,
|
||||
isUnboxingStub,
|
||||
isInstantiatingStub: false))
|
||||
{
|
||||
_methodDesc = methodDesc;
|
||||
_signatureContext = signatureContext;
|
||||
_localMethod = localMethod;
|
||||
}
|
||||
|
||||
public MethodDesc Method => _methodDesc;
|
||||
public MethodWithGCInfo MethodCodeNode => _localMethod;
|
||||
|
||||
public override int ClassCode => 458823351;
|
||||
|
||||
public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory)
|
||||
{
|
||||
foreach (DependencyListEntry entry in base.GetStaticDependencies(factory))
|
||||
{
|
||||
yield return entry;
|
||||
}
|
||||
if (_localMethod != null)
|
||||
{
|
||||
yield return new DependencyListEntry(_localMethod, "Local method import");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
using Internal.JitInterface;
|
||||
using Internal.Text;
|
||||
using Internal.TypeSystem;
|
||||
|
||||
namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
||||
{
|
||||
public class GenericLookupSignature : Signature
|
||||
{
|
||||
private CORINFO_RUNTIME_LOOKUP_KIND _runtimeLookupKind;
|
||||
|
||||
private readonly ReadyToRunFixupKind _fixupKind;
|
||||
|
||||
private readonly TypeDesc _typeArgument;
|
||||
|
||||
private readonly MethodWithToken _methodArgument;
|
||||
|
||||
private readonly TypeDesc _contextType;
|
||||
|
||||
private readonly SignatureContext _signatureContext;
|
||||
|
||||
public GenericLookupSignature(
|
||||
CORINFO_RUNTIME_LOOKUP_KIND runtimeLookupKind,
|
||||
ReadyToRunFixupKind fixupKind,
|
||||
TypeDesc typeArgument,
|
||||
MethodWithToken methodArgument,
|
||||
TypeDesc contextType,
|
||||
SignatureContext signatureContext)
|
||||
{
|
||||
_runtimeLookupKind = runtimeLookupKind;
|
||||
_fixupKind = fixupKind;
|
||||
_typeArgument = typeArgument;
|
||||
_methodArgument = methodArgument;
|
||||
_contextType = contextType;
|
||||
_signatureContext = signatureContext;
|
||||
}
|
||||
|
||||
public override int ClassCode => 258608008;
|
||||
|
||||
public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
|
||||
{
|
||||
ReadyToRunCodegenNodeFactory r2rFactory = (ReadyToRunCodegenNodeFactory)factory;
|
||||
ObjectDataSignatureBuilder dataBuilder = new ObjectDataSignatureBuilder();
|
||||
|
||||
if (!relocsOnly)
|
||||
{
|
||||
dataBuilder.AddSymbol(this);
|
||||
|
||||
switch (_runtimeLookupKind)
|
||||
{
|
||||
case CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_CLASSPARAM:
|
||||
dataBuilder.EmitByte((byte)ReadyToRunFixupKind.READYTORUN_FIXUP_TypeDictionaryLookup);
|
||||
break;
|
||||
|
||||
case CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_METHODPARAM:
|
||||
dataBuilder.EmitByte((byte)ReadyToRunFixupKind.READYTORUN_FIXUP_MethodDictionaryLookup);
|
||||
break;
|
||||
|
||||
case CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_THISOBJ:
|
||||
dataBuilder.EmitByte((byte)ReadyToRunFixupKind.READYTORUN_FIXUP_ThisObjDictionaryLookup);
|
||||
dataBuilder.EmitTypeSignature(_contextType, _signatureContext);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
dataBuilder.EmitByte((byte)_fixupKind);
|
||||
if (_typeArgument != null)
|
||||
{
|
||||
dataBuilder.EmitTypeSignature(_typeArgument, _signatureContext);
|
||||
}
|
||||
else if (_methodArgument != null)
|
||||
{
|
||||
dataBuilder.EmitMethodSignature(
|
||||
method: _methodArgument.Method,
|
||||
constrainedType: null,
|
||||
methodToken: _methodArgument.Token,
|
||||
enforceDefEncoding: false,
|
||||
context: _signatureContext,
|
||||
isUnboxingStub: false,
|
||||
isInstantiatingStub: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
return dataBuilder.ToObjectData();
|
||||
}
|
||||
|
||||
public override void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
|
||||
{
|
||||
sb.Append(nameMangler.CompilationUnitPrefix);
|
||||
sb.Append("GenericLookupSignature(");
|
||||
sb.Append(_runtimeLookupKind.ToString());
|
||||
sb.Append(" / ");
|
||||
sb.Append(_fixupKind.ToString());
|
||||
sb.Append(": ");
|
||||
if (_typeArgument != null)
|
||||
{
|
||||
RuntimeDeterminedTypeHelper.WriteTo(_typeArgument, sb);
|
||||
}
|
||||
else if (_methodArgument != null)
|
||||
{
|
||||
RuntimeDeterminedTypeHelper.WriteTo(_methodArgument.Method, sb);
|
||||
if (!_methodArgument.Token.IsNull)
|
||||
{
|
||||
sb.Append(" [");
|
||||
sb.Append(_methodArgument.Token.MetadataReader.GetString(_methodArgument.Token.MetadataReader.GetAssemblyDefinition().Name));
|
||||
sb.Append(":");
|
||||
sb.Append(((uint)_methodArgument.Token.Token).ToString("X8"));
|
||||
sb.Append("]");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
if (_contextType != null)
|
||||
{
|
||||
sb.Append(" (");
|
||||
sb.Append(_contextType.ToString());
|
||||
sb.Append(")");
|
||||
}
|
||||
}
|
||||
|
||||
public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory)
|
||||
{
|
||||
DependencyList dependencies = new DependencyList();
|
||||
if (_typeArgument != null && !_typeArgument.IsRuntimeDeterminedSubtype)
|
||||
{
|
||||
dependencies.Add(factory.NecessaryTypeSymbol(_typeArgument), "Type referenced in a generic lookup signature");
|
||||
}
|
||||
return dependencies;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -46,27 +46,21 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
Dictionary<byte[], BlobVertex> uniqueFixups = new Dictionary<byte[], BlobVertex>(ByteArrayComparer.Instance);
|
||||
Dictionary<byte[], BlobVertex> uniqueSignatures = new Dictionary<byte[], BlobVertex>(ByteArrayComparer.Instance);
|
||||
|
||||
foreach (MethodDesc method in factory.MetadataManager.GetCompiledMethods())
|
||||
foreach (MethodWithGCInfo method in r2rFactory.EnumerateCompiledMethods())
|
||||
{
|
||||
MethodWithGCInfo methodCodeNode = factory.MethodEntrypoint(method) as MethodWithGCInfo;
|
||||
if (methodCodeNode == null)
|
||||
if (method.Method.HasInstantiation || method.Method.OwningType.HasInstantiation)
|
||||
{
|
||||
methodCodeNode = ((ExternalMethodImport)factory.MethodEntrypoint(method))?.MethodCodeNode;
|
||||
if (methodCodeNode == null)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!methodCodeNode.IsEmpty && (methodCodeNode.Method.HasInstantiation || methodCodeNode.Method.OwningType.HasInstantiation))
|
||||
{
|
||||
int methodIndex = r2rFactory.RuntimeFunctionsTable.GetIndex(methodCodeNode);
|
||||
int methodIndex = r2rFactory.RuntimeFunctionsTable.GetIndex(method);
|
||||
|
||||
ArraySignatureBuilder signatureBuilder = new ArraySignatureBuilder();
|
||||
signatureBuilder.EmitMethodSignature(
|
||||
methodCodeNode.Method,
|
||||
constrainedType: null,
|
||||
method.Method,
|
||||
constrainedType: null,
|
||||
default(ModuleToken),
|
||||
enforceDefEncoding: true,
|
||||
method.SignatureContext,
|
||||
isUnboxingStub: false,
|
||||
isInstantiatingStub: false,
|
||||
methodCodeNode.SignatureContext);
|
||||
isInstantiatingStub: false);
|
||||
byte[] signature = signatureBuilder.ToArray();
|
||||
BlobVertex signatureBlob;
|
||||
if (!uniqueSignatures.TryGetValue(signature, out signatureBlob))
|
||||
|
@ -76,7 +70,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
uniqueSignatures.Add(signature, signatureBlob);
|
||||
}
|
||||
|
||||
byte[] fixup = methodCodeNode.GetFixupBlob(factory);
|
||||
byte[] fixup = method.GetFixupBlob(factory);
|
||||
BlobVertex fixupBlob = null;
|
||||
if (fixup != null && !uniqueFixups.TryGetValue(fixup, out fixupBlob))
|
||||
{
|
||||
|
@ -87,7 +81,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
|
||||
EntryPointVertex entryPointVertex = new EntryPointWithBlobVertex((uint)methodIndex, fixupBlob, signatureBlob);
|
||||
hashtableSection.Place(entryPointVertex);
|
||||
vertexHashtable.Append(unchecked((uint)ReadyToRunHashCode.MethodHashCode(methodCodeNode.Method)), entryPointVertex);
|
||||
vertexHashtable.Append(unchecked((uint)ReadyToRunHashCode.MethodHashCode(method.Method)), entryPointVertex);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Internal.Text;
|
||||
using Internal.TypeSystem;
|
||||
|
||||
namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
||||
{
|
||||
public class LocalMethodImport : DelayLoadHelperImport, IMethodNode
|
||||
{
|
||||
private readonly SignatureContext _signatureContext;
|
||||
|
||||
private readonly MethodWithGCInfo _localMethod;
|
||||
|
||||
public LocalMethodImport(
|
||||
ReadyToRunCodegenNodeFactory factory,
|
||||
ReadyToRunFixupKind fixupKind,
|
||||
MethodWithGCInfo localMethod,
|
||||
bool isUnboxingStub,
|
||||
SignatureContext signatureContext)
|
||||
: base(
|
||||
factory,
|
||||
factory.MethodImports,
|
||||
ReadyToRunHelper.READYTORUN_HELPER_DelayLoad_MethodCall,
|
||||
factory.MethodSignature(
|
||||
fixupKind,
|
||||
localMethod.Method,
|
||||
constrainedType: null,
|
||||
methodToken: default(ModuleToken),
|
||||
signatureContext,
|
||||
isUnboxingStub,
|
||||
isInstantiatingStub: false))
|
||||
{
|
||||
_signatureContext = signatureContext;
|
||||
_localMethod = localMethod;
|
||||
}
|
||||
|
||||
public MethodDesc Method => _localMethod.Method;
|
||||
public MethodWithGCInfo MethodCodeNode => _localMethod;
|
||||
|
||||
public override int ClassCode => 459923351;
|
||||
|
||||
public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFactory factory)
|
||||
{
|
||||
foreach (DependencyListEntry entry in base.GetStaticDependencies(factory))
|
||||
{
|
||||
yield return entry;
|
||||
}
|
||||
yield return new DependencyListEntry(_localMethod, "Local method import");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -55,17 +55,9 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
ReadyToRunCodegenNodeFactory r2rFactory = (ReadyToRunCodegenNodeFactory)factory;
|
||||
List<EntryPoint> ridToEntryPoint = new List<EntryPoint>();
|
||||
|
||||
foreach (MethodDesc method in factory.MetadataManager.GetCompiledMethods())
|
||||
foreach (MethodWithGCInfo method in r2rFactory.EnumerateCompiledMethods())
|
||||
{
|
||||
MethodWithGCInfo methodCodeNode = factory.MethodEntrypoint(method) as MethodWithGCInfo;
|
||||
if (methodCodeNode == null)
|
||||
{
|
||||
methodCodeNode = ((ExternalMethodImport)factory.MethodEntrypoint(method))?.MethodCodeNode;
|
||||
if (methodCodeNode == null)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!methodCodeNode.IsEmpty && methodCodeNode.Method is EcmaMethod ecmaMethod)
|
||||
if (method.Method is EcmaMethod ecmaMethod)
|
||||
{
|
||||
// Strip away the token type bits, keep just the low 24 bits RID
|
||||
uint rid = SignatureBuilder.RidFromToken((mdToken)MetadataTokens.GetToken(ecmaMethod.Handle));
|
||||
|
@ -77,8 +69,8 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
ridToEntryPoint.Add(EntryPoint.Null);
|
||||
}
|
||||
|
||||
int methodIndex = r2rFactory.RuntimeFunctionsTable.GetIndex(methodCodeNode);
|
||||
ridToEntryPoint[(int)rid] = new EntryPoint(methodIndex, methodCodeNode);
|
||||
int methodIndex = r2rFactory.RuntimeFunctionsTable.GetIndex(method);
|
||||
ridToEntryPoint[(int)rid] = new EntryPoint(methodIndex, method);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
|
||||
private readonly TypeDesc _constrainedType;
|
||||
|
||||
private readonly ModuleToken _methodToken;
|
||||
|
||||
private readonly SignatureContext _signatureContext;
|
||||
|
||||
private readonly bool _isUnboxingStub;
|
||||
|
@ -27,12 +29,14 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
ReadyToRunFixupKind fixupKind,
|
||||
MethodDesc methodDesc,
|
||||
TypeDesc constrainedType,
|
||||
ModuleToken methodToken,
|
||||
SignatureContext signatureContext,
|
||||
bool isUnboxingStub,
|
||||
bool isInstantiatingStub)
|
||||
{
|
||||
_fixupKind = fixupKind;
|
||||
_methodDesc = methodDesc;
|
||||
_methodToken = methodToken;
|
||||
_constrainedType = constrainedType;
|
||||
_signatureContext = signatureContext;
|
||||
_isUnboxingStub = isUnboxingStub;
|
||||
|
@ -54,7 +58,8 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
dataBuilder.AddSymbol(this);
|
||||
|
||||
dataBuilder.EmitUInt((uint)_fixupKind);
|
||||
dataBuilder.EmitMethodSignature(_methodDesc, _constrainedType, _isUnboxingStub, _isInstantiatingStub, _signatureContext);
|
||||
dataBuilder.EmitMethodSignature(_methodDesc, _constrainedType, _methodToken, enforceDefEncoding: false,
|
||||
_signatureContext, _isUnboxingStub, _isInstantiatingStub);
|
||||
|
||||
return dataBuilder.ToObjectData();
|
||||
}
|
||||
|
@ -68,6 +73,14 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
sb.Append(" @ ");
|
||||
sb.Append(_constrainedType.ToString());
|
||||
}
|
||||
if (!_methodToken.IsNull)
|
||||
{
|
||||
sb.Append(" [");
|
||||
sb.Append(_methodToken.MetadataReader.GetString(_methodToken.MetadataReader.GetAssemblyDefinition().Name));
|
||||
sb.Append(":");
|
||||
sb.Append(((uint)_methodToken.Token).ToString("X8"));
|
||||
sb.Append("]");
|
||||
}
|
||||
}
|
||||
|
||||
public override int CompareToImpl(ISortableNode other, CompilerComparer comparer)
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Module.GetHashCode() ^ unchecked((int)(31 * (uint)Token));
|
||||
return IsNull ? 0 : Module.GetHashCode() ^ unchecked((int)(31 * (uint)Token));
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
|
|
@ -27,8 +27,6 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
/// </summary>
|
||||
private readonly Dictionary<EcmaType, ModuleToken> _typeToRefTokens = new Dictionary<EcmaType, ModuleToken>();
|
||||
|
||||
private readonly Dictionary<MethodDesc, ModuleToken> _methodToRefTokens = new Dictionary<MethodDesc, ModuleToken>();
|
||||
|
||||
private readonly Dictionary<FieldDesc, ModuleToken> _fieldToRefTokens = new Dictionary<FieldDesc, ModuleToken>();
|
||||
|
||||
private readonly CompilationModuleGroup _compilationModuleGroup;
|
||||
|
@ -41,7 +39,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
_typeSystemContext = typeSystemContext;
|
||||
}
|
||||
|
||||
public ModuleToken GetModuleTokenForType(EcmaType type)
|
||||
public ModuleToken GetModuleTokenForType(EcmaType type, bool throwIfNotFound = true)
|
||||
{
|
||||
if (_compilationModuleGroup.ContainsType(type))
|
||||
{
|
||||
|
@ -55,10 +53,17 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
}
|
||||
|
||||
// Reverse lookup failed
|
||||
throw new NotImplementedException(type.ToString());
|
||||
if (throwIfNotFound)
|
||||
{
|
||||
throw new NotImplementedException(type.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
return default(ModuleToken);
|
||||
}
|
||||
}
|
||||
|
||||
public ModuleToken GetModuleTokenForMethod(MethodDesc method)
|
||||
public ModuleToken GetModuleTokenForMethod(MethodDesc method, bool throwIfNotFound = true)
|
||||
{
|
||||
if (_compilationModuleGroup.ContainsMethodBody(method, unboxingStub: false) &&
|
||||
method is EcmaMethod ecmaMethod)
|
||||
|
@ -66,72 +71,48 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
return new ModuleToken(ecmaMethod.Module, ecmaMethod.Handle);
|
||||
}
|
||||
|
||||
if (_methodToRefTokens.TryGetValue(method, out ModuleToken token))
|
||||
{
|
||||
return token;
|
||||
}
|
||||
|
||||
// Reverse lookup failed
|
||||
throw new NotImplementedException(method.ToString());
|
||||
if (throwIfNotFound)
|
||||
{
|
||||
throw new NotImplementedException(method.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
return default(ModuleToken);
|
||||
}
|
||||
}
|
||||
|
||||
public ModuleToken GetModuleTokenForField(FieldDesc field)
|
||||
public ModuleToken GetModuleTokenForField(FieldDesc field, bool throwIfNotFound = true)
|
||||
{
|
||||
if (_compilationModuleGroup.ContainsType(field.OwningType) && field is EcmaField ecmaField)
|
||||
{
|
||||
return new ModuleToken(ecmaField.Module, ecmaField.Handle);
|
||||
}
|
||||
|
||||
throw new NotImplementedException();
|
||||
if (throwIfNotFound)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
else
|
||||
{
|
||||
return default(ModuleToken);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddModuleTokenForMethod(MethodDesc method, ModuleToken token)
|
||||
{
|
||||
if (_methodToRefTokens.ContainsKey(method))
|
||||
if (token.TokenType == CorTokenType.mdtMethodSpec)
|
||||
{
|
||||
// This method has already been harvested
|
||||
return;
|
||||
MethodSpecification methodSpec = token.MetadataReader.GetMethodSpecification((MethodSpecificationHandle)token.Handle);
|
||||
token = new ModuleToken(token.Module, methodSpec.Method);
|
||||
}
|
||||
|
||||
if (_compilationModuleGroup.ContainsMethodBody(method, unboxingStub: false) && method is EcmaMethod)
|
||||
if (token.TokenType == CorTokenType.mdtMemberRef)
|
||||
{
|
||||
// We don't need to store handles within the current compilation group
|
||||
// as we can read them directly from the ECMA objects.
|
||||
return;
|
||||
MemberReference memberRef = token.MetadataReader.GetMemberReference((MemberReferenceHandle)token.Handle);
|
||||
EntityHandle owningTypeHandle = memberRef.Parent;
|
||||
AddModuleTokenForType(method.OwningType, new ModuleToken(token.Module, owningTypeHandle));
|
||||
memberRef.DecodeMethodSignature<DummyTypeInfo, ModuleTokenResolver>(new TokenResolverProvider(this, token.Module), this);
|
||||
}
|
||||
|
||||
_methodToRefTokens[method] = token;
|
||||
|
||||
switch (token.TokenType)
|
||||
{
|
||||
case CorTokenType.mdtMethodSpec:
|
||||
{
|
||||
MethodSpecification methodSpec = token.MetadataReader.GetMethodSpecification((MethodSpecificationHandle)token.Handle);
|
||||
AddModuleTokenForMethod((MethodDesc)token.Module.GetObject(methodSpec.Method), new ModuleToken(token.Module, methodSpec.Method));
|
||||
}
|
||||
break;
|
||||
|
||||
case CorTokenType.mdtMemberRef:
|
||||
if ((method.HasInstantiation || method.OwningType.HasInstantiation) &&
|
||||
!method.IsGenericMethodDefinition && !method.OwningType.IsGenericDefinition)
|
||||
{
|
||||
AddModuleTokenForMethod(method.GetTypicalMethodDefinition(), token);
|
||||
}
|
||||
AddModuleTokenForMethodReference(method.OwningType, token);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException(token.TokenType.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private void AddModuleTokenForMethodReference(TypeDesc owningType, ModuleToken token)
|
||||
{
|
||||
MemberReference memberRef = token.MetadataReader.GetMemberReference((MemberReferenceHandle)token.Handle);
|
||||
EntityHandle owningTypeHandle = memberRef.Parent;
|
||||
AddModuleTokenForType(owningType, new ModuleToken(token.Module, owningTypeHandle));
|
||||
|
||||
memberRef.DecodeMethodSignature<DummyTypeInfo, ModuleTokenResolver>(new TokenResolverProvider(this, token.Module), this);
|
||||
}
|
||||
|
||||
private void AddModuleTokenForFieldReference(TypeDesc owningType, ModuleToken token)
|
||||
|
|
|
@ -16,10 +16,10 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
{
|
||||
private List<MethodWithGCInfo> _methodNodes;
|
||||
private Dictionary<MethodWithGCInfo, int> _insertedMethodNodes;
|
||||
private readonly NodeFactory _nodeFactory;
|
||||
private readonly ReadyToRunCodegenNodeFactory _nodeFactory;
|
||||
private int _tableSize = -1;
|
||||
|
||||
public RuntimeFunctionsTableNode(NodeFactory nodeFactory)
|
||||
public RuntimeFunctionsTableNode(ReadyToRunCodegenNodeFactory nodeFactory)
|
||||
: base(nodeFactory.Target)
|
||||
{
|
||||
_nodeFactory = nodeFactory;
|
||||
|
@ -50,19 +50,11 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
|
||||
int runtimeFunctionIndex = 0;
|
||||
|
||||
foreach (MethodDesc method in _nodeFactory.MetadataManager.GetCompiledMethods())
|
||||
foreach (MethodWithGCInfo method in _nodeFactory.EnumerateCompiledMethods())
|
||||
{
|
||||
MethodWithGCInfo methodCodeNode = _nodeFactory.MethodEntrypoint(method) as MethodWithGCInfo;
|
||||
if (methodCodeNode == null)
|
||||
{
|
||||
methodCodeNode = ((ExternalMethodImport)_nodeFactory.MethodEntrypoint(method))?.MethodCodeNode;
|
||||
if (methodCodeNode == null)
|
||||
continue;
|
||||
}
|
||||
|
||||
_methodNodes.Add(methodCodeNode);
|
||||
_insertedMethodNodes[methodCodeNode] = runtimeFunctionIndex;
|
||||
runtimeFunctionIndex += methodCodeNode.FrameInfos.Length;
|
||||
_methodNodes.Add(method);
|
||||
_insertedMethodNodes[method] = runtimeFunctionIndex;
|
||||
runtimeFunctionIndex += method.FrameInfos.Length;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -179,6 +179,25 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
|
||||
public void EmitTypeSignature(TypeDesc typeDesc, SignatureContext context)
|
||||
{
|
||||
if (typeDesc is RuntimeDeterminedType runtimeDeterminedType)
|
||||
{
|
||||
switch (runtimeDeterminedType.RuntimeDeterminedDetailsType.Kind)
|
||||
{
|
||||
case GenericParameterKind.Type:
|
||||
EmitElementType(CorElementType.ELEMENT_TYPE_VAR);
|
||||
break;
|
||||
|
||||
case GenericParameterKind.Method:
|
||||
EmitElementType(CorElementType.ELEMENT_TYPE_MVAR);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
EmitUInt((uint)runtimeDeterminedType.RuntimeDeterminedDetailsType.Index);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeDesc.HasInstantiation && !typeDesc.IsGenericDefinition)
|
||||
{
|
||||
EmitInstantiatedTypeSignature((InstantiatedType)typeDesc, context);
|
||||
|
@ -335,7 +354,14 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
}
|
||||
}
|
||||
|
||||
public void EmitMethodSignature(MethodDesc method, TypeDesc constrainedType, bool isUnboxingStub, bool isInstantiatingStub, SignatureContext context)
|
||||
public void EmitMethodSignature(
|
||||
MethodDesc method,
|
||||
TypeDesc constrainedType,
|
||||
ModuleToken methodToken,
|
||||
bool enforceDefEncoding,
|
||||
SignatureContext context,
|
||||
bool isUnboxingStub,
|
||||
bool isInstantiatingStub)
|
||||
{
|
||||
uint flags = 0;
|
||||
if (isUnboxingStub)
|
||||
|
@ -353,24 +379,27 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
|
||||
if (method.HasInstantiation || method.OwningType.HasInstantiation)
|
||||
{
|
||||
EmitMethodSpecificationSignature(method, flags, context);
|
||||
EmitMethodSpecificationSignature(method, methodToken, flags, enforceDefEncoding, context);
|
||||
}
|
||||
else
|
||||
{
|
||||
ModuleToken token = context.GetModuleTokenForMethod(method.GetTypicalMethodDefinition());
|
||||
switch (token.TokenType)
|
||||
if (methodToken.IsNull)
|
||||
{
|
||||
methodToken = context.GetModuleTokenForMethod(method.GetTypicalMethodDefinition());
|
||||
}
|
||||
switch (methodToken.TokenType)
|
||||
{
|
||||
case CorTokenType.mdtMethodDef:
|
||||
// TODO: module override for methoddefs with external module context
|
||||
EmitUInt(flags);
|
||||
EmitMethodDefToken(token);
|
||||
EmitMethodDefToken(methodToken);
|
||||
break;
|
||||
|
||||
case CorTokenType.mdtMemberRef:
|
||||
// TODO: module override for methodrefs with external module context
|
||||
flags |= (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_MemberRefToken;
|
||||
EmitUInt(flags);
|
||||
EmitMethodRefToken(token);
|
||||
EmitMethodRefToken(methodToken);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -396,21 +425,45 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
EmitUInt(RidFromToken(memberRefToken.Token));
|
||||
}
|
||||
|
||||
private void EmitMethodSpecificationSignature(MethodDesc method, uint flags, SignatureContext context)
|
||||
private void EmitMethodSpecificationSignature(MethodDesc method, ModuleToken methodToken,
|
||||
uint flags, bool enforceDefEncoding, SignatureContext context)
|
||||
{
|
||||
if (method.HasInstantiation)
|
||||
{
|
||||
flags |= (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_MethodInstantiation
|
||||
| (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_OwnerType;
|
||||
flags |= (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_MethodInstantiation;
|
||||
if (!methodToken.IsNull)
|
||||
{
|
||||
if (methodToken.TokenType == CorTokenType.mdtMethodSpec)
|
||||
{
|
||||
MethodSpecification methodSpecification = methodToken.MetadataReader.GetMethodSpecification((MethodSpecificationHandle)methodToken.Handle);
|
||||
methodToken = new ModuleToken(methodToken.Module, methodSpecification.Method);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (methodToken.IsNull && !enforceDefEncoding)
|
||||
{
|
||||
methodToken = context.GetModuleTokenForMethod(method.GetMethodDefinition(), throwIfNotFound: false);
|
||||
}
|
||||
if (methodToken.IsNull)
|
||||
{
|
||||
flags |= (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_OwnerType;
|
||||
methodToken = context.GetModuleTokenForMethod(method.GetTypicalMethodDefinition());
|
||||
}
|
||||
|
||||
if (method.OwningType.HasInstantiation)
|
||||
{
|
||||
// resolveToken currently resolves the token in the context of a given scope;
|
||||
// in such case, we receive a method on instantiated type along with the
|
||||
// generic definition token.
|
||||
flags |= (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_OwnerType;
|
||||
}
|
||||
|
||||
ModuleToken genericMethodToken = context.GetModuleTokenForMethod(method.GetTypicalMethodDefinition());
|
||||
switch (genericMethodToken.TokenType)
|
||||
switch (methodToken.TokenType)
|
||||
{
|
||||
case CorTokenType.mdtMethodDef:
|
||||
break;
|
||||
|
@ -428,8 +481,8 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
{
|
||||
EmitTypeSignature(method.OwningType, context);
|
||||
}
|
||||
EmitTokenRid(genericMethodToken.Token);
|
||||
if (method.HasInstantiation)
|
||||
EmitTokenRid(methodToken.Token);
|
||||
if ((flags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_MethodInstantiation) != 0)
|
||||
{
|
||||
Instantiation instantiation = method.Instantiation;
|
||||
EmitUInt((uint)instantiation.Length);
|
||||
|
|
|
@ -22,19 +22,19 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun
|
|||
_resolver = resolver;
|
||||
}
|
||||
|
||||
public ModuleToken GetModuleTokenForType(EcmaType type)
|
||||
public ModuleToken GetModuleTokenForType(EcmaType type, bool throwIfNotFound = true)
|
||||
{
|
||||
return _resolver.GetModuleTokenForType(type);
|
||||
return _resolver.GetModuleTokenForType(type, throwIfNotFound);
|
||||
}
|
||||
|
||||
public ModuleToken GetModuleTokenForMethod(MethodDesc method)
|
||||
public ModuleToken GetModuleTokenForMethod(MethodDesc method, bool throwIfNotFound = true)
|
||||
{
|
||||
return _resolver.GetModuleTokenForMethod(method);
|
||||
return _resolver.GetModuleTokenForMethod(method, throwIfNotFound);
|
||||
}
|
||||
|
||||
public ModuleToken GetModuleTokenForField(FieldDesc field)
|
||||
public ModuleToken GetModuleTokenForField(FieldDesc field, bool throwIfNotFound = true)
|
||||
{
|
||||
return _resolver.GetModuleTokenForField(field);
|
||||
return _resolver.GetModuleTokenForField(field, throwIfNotFound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,47 +102,59 @@ namespace ILCompiler.DependencyAnalysis
|
|||
return new Import(EagerImports, new ReadyToRunHelperSignature(helperId));
|
||||
}
|
||||
|
||||
public IMethodNode MethodEntrypoint(MethodDesc targetMethod, TypeDesc constrainedType, MethodDesc originalMethod, SignatureContext signatureContext, bool isUnboxingStub = false)
|
||||
public IMethodNode MethodEntrypoint(
|
||||
MethodDesc targetMethod,
|
||||
TypeDesc constrainedType,
|
||||
MethodDesc originalMethod,
|
||||
ModuleToken methodToken,
|
||||
SignatureContext signatureContext,
|
||||
bool isUnboxingStub = false)
|
||||
{
|
||||
if (!CompilationModuleGroup.ContainsMethodBody(targetMethod, false))
|
||||
{
|
||||
return ImportedMethodNode(constrainedType != null ? originalMethod : targetMethod, constrainedType, methodToken, signatureContext, isUnboxingStub);
|
||||
}
|
||||
|
||||
return _methodEntrypoints.GetOrAdd(targetMethod, (m) =>
|
||||
{
|
||||
return CreateMethodEntrypointNode(targetMethod, constrainedType, originalMethod, signatureContext, isUnboxingStub);
|
||||
return CreateMethodEntrypointNode(targetMethod, signatureContext, isUnboxingStub);
|
||||
});
|
||||
}
|
||||
|
||||
private IMethodNode CreateMethodEntrypointNode(MethodDesc targetMethod, TypeDesc constrainedType, MethodDesc originalMethod, SignatureContext signatureContext, bool isUnboxingStub)
|
||||
private IMethodNode CreateMethodEntrypointNode(MethodDesc targetMethod, SignatureContext signatureContext, bool isUnboxingStub)
|
||||
{
|
||||
if (constrainedType != null)
|
||||
{
|
||||
// We may be able to optimize away the constrained call if targetMethod is within the version bubble
|
||||
if (CompilationModuleGroup.ContainsMethodBody(targetMethod, isUnboxingStub))
|
||||
{
|
||||
constrainedType = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetMethod = originalMethod;
|
||||
}
|
||||
}
|
||||
MethodWithGCInfo localMethod = new MethodWithGCInfo(targetMethod, signatureContext);
|
||||
|
||||
if (targetMethod is InstantiatedMethod instantiatedMethod)
|
||||
{
|
||||
return InstantiatedMethodNode(instantiatedMethod, constrainedType, signatureContext, isUnboxingStub);
|
||||
}
|
||||
|
||||
MethodWithGCInfo localMethod = null;
|
||||
|
||||
if (CompilationModuleGroup.ContainsMethodBody(targetMethod, false))
|
||||
{
|
||||
localMethod = new MethodWithGCInfo(targetMethod, signatureContext);
|
||||
}
|
||||
|
||||
return ImportedMethodNode(targetMethod, unboxingStub: isUnboxingStub, constrainedType: constrainedType, signatureContext: signatureContext, localMethod: localMethod);
|
||||
return new LocalMethodImport(
|
||||
this,
|
||||
ReadyToRunFixupKind.READYTORUN_FIXUP_MethodEntry,
|
||||
localMethod,
|
||||
isUnboxingStub,
|
||||
signatureContext);
|
||||
}
|
||||
|
||||
public IMethodNode StringAllocator(MethodDesc constructor, SignatureContext signatureContext)
|
||||
public IEnumerable<MethodWithGCInfo> EnumerateCompiledMethods()
|
||||
{
|
||||
return MethodEntrypoint(constructor, constrainedType: null, originalMethod: null, signatureContext: signatureContext, isUnboxingStub: false);
|
||||
foreach (MethodDesc method in MetadataManager.GetCompiledMethods())
|
||||
{
|
||||
IMethodNode methodNode = MethodEntrypoint(method);
|
||||
MethodWithGCInfo methodCodeNode = methodNode as MethodWithGCInfo;
|
||||
if (methodCodeNode == null && methodNode is LocalMethodImport localMethodImport)
|
||||
{
|
||||
methodCodeNode = localMethodImport.MethodCodeNode;
|
||||
}
|
||||
|
||||
if (methodCodeNode != null && !methodCodeNode.IsEmpty)
|
||||
{
|
||||
yield return methodCodeNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IMethodNode StringAllocator(MethodDesc constructor, ModuleToken methodToken, SignatureContext signatureContext)
|
||||
{
|
||||
return MethodEntrypoint(constructor, constrainedType: null, originalMethod: null,
|
||||
methodToken: methodToken, signatureContext: signatureContext, isUnboxingStub: false);
|
||||
}
|
||||
|
||||
protected override ISymbolNode CreateReadyToRunHelperNode(ReadyToRunHelperKey helperCall)
|
||||
|
@ -215,6 +227,7 @@ namespace ILCompiler.DependencyAnalysis
|
|||
ReadyToRunFixupKind fixupKind,
|
||||
MethodDesc methodDesc,
|
||||
TypeDesc constrainedType,
|
||||
ModuleToken methodToken,
|
||||
SignatureContext signatureContext,
|
||||
bool isUnboxingStub,
|
||||
bool isInstantiatingStub)
|
||||
|
@ -226,11 +239,12 @@ namespace ILCompiler.DependencyAnalysis
|
|||
_methodSignatures.Add(fixupKind, perFixupKindMap);
|
||||
}
|
||||
|
||||
TypeAndMethod key = new TypeAndMethod(constrainedType, methodDesc, isUnboxingStub, isInstantiatingStub);
|
||||
TypeAndMethod key = new TypeAndMethod(constrainedType, methodDesc, methodToken, isUnboxingStub, isInstantiatingStub);
|
||||
MethodFixupSignature signature;
|
||||
if (!perFixupKindMap.TryGetValue(key, out signature))
|
||||
{
|
||||
signature = new MethodFixupSignature(fixupKind, methodDesc, constrainedType, signatureContext, isUnboxingStub, isInstantiatingStub);
|
||||
signature = new MethodFixupSignature(fixupKind, methodDesc, constrainedType,
|
||||
methodToken, signatureContext, isUnboxingStub, isInstantiatingStub);
|
||||
perFixupKindMap.Add(key, signature);
|
||||
}
|
||||
return signature;
|
||||
|
@ -349,57 +363,43 @@ namespace ILCompiler.DependencyAnalysis
|
|||
MetadataManager.AttachToDependencyGraph(graph);
|
||||
}
|
||||
|
||||
public IMethodNode ImportedMethodNode(MethodDesc method, TypeDesc constrainedType, SignatureContext signatureContext, bool unboxingStub, MethodWithGCInfo localMethod)
|
||||
public IMethodNode ImportedMethodNode(
|
||||
MethodDesc targetMethod,
|
||||
TypeDesc constrainedType,
|
||||
ModuleToken methodToken,
|
||||
SignatureContext signatureContext,
|
||||
bool unboxingStub)
|
||||
{
|
||||
IMethodNode methodImport;
|
||||
TypeAndMethod key = new TypeAndMethod(constrainedType, method, unboxingStub, isInstantiatingStub: false);
|
||||
TypeAndMethod key = new TypeAndMethod(constrainedType, targetMethod, methodToken, unboxingStub, isInstantiatingStub: false);
|
||||
if (!_importMethods.TryGetValue(key, out methodImport))
|
||||
{
|
||||
// First time we see a given external method - emit indirection cell and the import entry
|
||||
ExternalMethodImport indirectionCell = new ExternalMethodImport(
|
||||
this,
|
||||
ReadyToRunFixupKind.READYTORUN_FIXUP_MethodEntry,
|
||||
method,
|
||||
targetMethod,
|
||||
constrainedType,
|
||||
signatureContext,
|
||||
methodToken,
|
||||
unboxingStub,
|
||||
localMethod);
|
||||
signatureContext);
|
||||
_importMethods.Add(key, indirectionCell);
|
||||
methodImport = indirectionCell;
|
||||
}
|
||||
return methodImport;
|
||||
}
|
||||
|
||||
private readonly Dictionary<InstantiatedMethod, IMethodNode> _instantiatedMethodImports = new Dictionary<InstantiatedMethod, IMethodNode>();
|
||||
|
||||
private IMethodNode InstantiatedMethodNode(InstantiatedMethod method, TypeDesc constrainedType, SignatureContext signatureContext, bool isUnboxingStub)
|
||||
{
|
||||
IMethodNode methodImport;
|
||||
if (!_instantiatedMethodImports.TryGetValue(method, out methodImport))
|
||||
{
|
||||
methodImport = new ExternalMethodImport(
|
||||
this,
|
||||
ReadyToRunFixupKind.READYTORUN_FIXUP_MethodEntry,
|
||||
method,
|
||||
constrainedType,
|
||||
signatureContext,
|
||||
isUnboxingStub,
|
||||
localMethod: null);
|
||||
_instantiatedMethodImports.Add(method, methodImport);
|
||||
}
|
||||
return methodImport;
|
||||
}
|
||||
|
||||
private Dictionary<TypeAndMethod, IMethodNode> _shadowConcreteMethods = new Dictionary<TypeAndMethod, IMethodNode>();
|
||||
|
||||
public IMethodNode ShadowConcreteMethod(MethodDesc method, TypeDesc constrainedType, MethodDesc originalMethod,
|
||||
SignatureContext signatureContext, bool isUnboxingStub = false)
|
||||
public IMethodNode ShadowConcreteMethod(MethodDesc targetMethod, TypeDesc constrainedType, MethodDesc originalMethod,
|
||||
ModuleToken methodToken, SignatureContext signatureContext, bool isUnboxingStub = false)
|
||||
{
|
||||
IMethodNode result;
|
||||
TypeAndMethod key = new TypeAndMethod(constrainedType, method, isUnboxingStub, isInstantiatingStub: false);
|
||||
TypeAndMethod key = new TypeAndMethod(constrainedType, constrainedType != null ? originalMethod : targetMethod,
|
||||
methodToken, isUnboxingStub, isInstantiatingStub: false);
|
||||
if (!_shadowConcreteMethods.TryGetValue(key, out result))
|
||||
{
|
||||
result = MethodEntrypoint(method, constrainedType, originalMethod, signatureContext, isUnboxingStub);
|
||||
result = MethodEntrypoint(targetMethod, constrainedType, originalMethod, methodToken, signatureContext, isUnboxingStub);
|
||||
_shadowConcreteMethods.Add(key, result);
|
||||
}
|
||||
return result;
|
||||
|
@ -440,7 +440,8 @@ namespace ILCompiler.DependencyAnalysis
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
return MethodEntrypoint(method, constrainedType: null, originalMethod: null, signatureContext: InputModuleContext, isUnboxingStub: false);
|
||||
return MethodEntrypoint(method, constrainedType: null, originalMethod: null,
|
||||
methodToken: default(ModuleToken), signatureContext: InputModuleContext, isUnboxingStub: false);
|
||||
}
|
||||
|
||||
protected override IMethodNode CreateUnboxingStubNode(MethodDesc method)
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ILCompiler.DependencyAnalysis.ReadyToRun;
|
||||
|
||||
using Internal.JitInterface;
|
||||
using Internal.TypeSystem;
|
||||
|
||||
namespace ILCompiler.DependencyAnalysis
|
||||
|
@ -96,7 +98,7 @@ namespace ILCompiler.DependencyAnalysis
|
|||
break;
|
||||
|
||||
case ReadyToRunHelperId.VirtualCall:
|
||||
helperNode = CreateVirtualCallHelper((MethodDesc)target, signatureContext);
|
||||
helperNode = CreateVirtualCallHelper((MethodWithToken)target, signatureContext);
|
||||
break;
|
||||
|
||||
case ReadyToRunHelperId.DelegateCtor:
|
||||
|
@ -197,14 +199,19 @@ namespace ILCompiler.DependencyAnalysis
|
|||
new FieldFixupSignature(ReadyToRunFixupKind.READYTORUN_FIXUP_FieldHandle, field, signatureContext));
|
||||
}
|
||||
|
||||
private ISymbolNode CreateVirtualCallHelper(MethodDesc method, SignatureContext signatureContext)
|
||||
private ISymbolNode CreateVirtualCallHelper(MethodWithToken methodWithToken, SignatureContext signatureContext)
|
||||
{
|
||||
return new DelayLoadHelperImport(
|
||||
_codegenNodeFactory,
|
||||
_codegenNodeFactory.DispatchImports,
|
||||
ILCompiler.DependencyAnalysis.ReadyToRun.ReadyToRunHelper.READYTORUN_HELPER_DelayLoad_Helper_Obj,
|
||||
_codegenNodeFactory.MethodSignature(ReadyToRunFixupKind.READYTORUN_FIXUP_VirtualEntry, method,
|
||||
constrainedType: null, signatureContext: signatureContext, isUnboxingStub: false, isInstantiatingStub: false));
|
||||
_codegenNodeFactory.MethodSignature(
|
||||
ReadyToRunFixupKind.READYTORUN_FIXUP_VirtualEntry, methodWithToken.Method,
|
||||
constrainedType: null,
|
||||
methodWithToken.Token,
|
||||
signatureContext: signatureContext,
|
||||
isUnboxingStub: false,
|
||||
isInstantiatingStub: false));
|
||||
}
|
||||
|
||||
private ISymbolNode CreateDelegateCtorHelper(DelegateCreationInfo info, SignatureContext signatureContext)
|
||||
|
@ -541,7 +548,7 @@ namespace ILCompiler.DependencyAnalysis
|
|||
|
||||
private readonly Dictionary<MethodAndCallSite, ISymbolNode> _interfaceDispatchCells = new Dictionary<MethodAndCallSite, ISymbolNode>();
|
||||
|
||||
public ISymbolNode InterfaceDispatchCell(MethodDesc method, SignatureContext signatureContext, bool isUnboxingStub, string callSite)
|
||||
public ISymbolNode InterfaceDispatchCell(MethodDesc method, ModuleToken methodToken, SignatureContext signatureContext, bool isUnboxingStub, string callSite)
|
||||
{
|
||||
MethodAndCallSite cellKey = new MethodAndCallSite(method, callSite);
|
||||
if (!_interfaceDispatchCells.TryGetValue(cellKey, out ISymbolNode dispatchCell))
|
||||
|
@ -552,7 +559,7 @@ namespace ILCompiler.DependencyAnalysis
|
|||
ILCompiler.DependencyAnalysis.ReadyToRun.ReadyToRunHelper.READYTORUN_HELPER_DelayLoad_MethodCall |
|
||||
ILCompiler.DependencyAnalysis.ReadyToRun.ReadyToRunHelper.READYTORUN_HELPER_FLAG_VSD,
|
||||
_codegenNodeFactory.MethodSignature(ReadyToRunFixupKind.READYTORUN_FIXUP_VirtualEntry, method,
|
||||
null, signatureContext, isUnboxingStub, isInstantiatingStub: false),
|
||||
null, methodToken, signatureContext, isUnboxingStub, isInstantiatingStub: false),
|
||||
callSite);
|
||||
|
||||
_interfaceDispatchCells.Add(cellKey, dispatchCell);
|
||||
|
@ -567,7 +574,7 @@ namespace ILCompiler.DependencyAnalysis
|
|||
|
||||
private readonly Dictionary<MethodDesc, ISortableSymbolNode> _genericDictionaryCache = new Dictionary<MethodDesc, ISortableSymbolNode>();
|
||||
|
||||
public ISortableSymbolNode MethodGenericDictionary(MethodDesc method, SignatureContext signatureContext)
|
||||
public ISortableSymbolNode MethodGenericDictionary(MethodDesc method, ModuleToken methodToken, SignatureContext signatureContext)
|
||||
{
|
||||
if (!_genericDictionaryCache.TryGetValue(method, out ISortableSymbolNode genericDictionary))
|
||||
{
|
||||
|
@ -577,6 +584,7 @@ namespace ILCompiler.DependencyAnalysis
|
|||
ReadyToRunFixupKind.READYTORUN_FIXUP_MethodDictionary,
|
||||
method,
|
||||
constrainedType: null,
|
||||
methodToken: methodToken,
|
||||
signatureContext: signatureContext,
|
||||
isUnboxingStub: false,
|
||||
isInstantiatingStub: true));
|
||||
|
@ -601,18 +609,24 @@ namespace ILCompiler.DependencyAnalysis
|
|||
|
||||
private readonly Dictionary<TypeAndMethod, ISymbolNode> _delegateCtors = new Dictionary<TypeAndMethod, ISymbolNode>();
|
||||
|
||||
public ISymbolNode DelegateCtor(TypeDesc delegateType, MethodDesc targetMethod, SignatureContext signatureContext)
|
||||
public ISymbolNode DelegateCtor(TypeDesc delegateType, MethodDesc targetMethod, ModuleToken methodToken, SignatureContext signatureContext)
|
||||
{
|
||||
TypeAndMethod ctorKey = new TypeAndMethod(delegateType, targetMethod, isUnboxingStub: false, isInstantiatingStub: false);
|
||||
TypeAndMethod ctorKey = new TypeAndMethod(delegateType, targetMethod, methodToken: methodToken, isUnboxingStub: false, isInstantiatingStub: false);
|
||||
if (!_delegateCtors.TryGetValue(ctorKey, out ISymbolNode ctorNode))
|
||||
{
|
||||
IMethodNode targetMethodNode = _codegenNodeFactory.MethodEntrypoint(targetMethod, constrainedType: null, originalMethod: null, signatureContext: signatureContext, isUnboxingStub: false);
|
||||
IMethodNode targetMethodNode = _codegenNodeFactory.MethodEntrypoint(
|
||||
targetMethod,
|
||||
constrainedType: null,
|
||||
originalMethod: null,
|
||||
methodToken: methodToken,
|
||||
signatureContext: signatureContext,
|
||||
isUnboxingStub: false);
|
||||
|
||||
ctorNode = new DelayLoadHelperImport(
|
||||
_codegenNodeFactory,
|
||||
_codegenNodeFactory.HelperImports,
|
||||
ILCompiler.DependencyAnalysis.ReadyToRun.ReadyToRunHelper.READYTORUN_HELPER_DelayLoad_Helper,
|
||||
new DelegateCtorSignature(delegateType, targetMethodNode, signatureContext));
|
||||
new DelegateCtorSignature(delegateType, targetMethodNode, methodToken, signatureContext));
|
||||
_delegateCtors.Add(ctorKey, ctorNode);
|
||||
}
|
||||
return ctorNode;
|
||||
|
@ -644,5 +658,145 @@ namespace ILCompiler.DependencyAnalysis
|
|||
return (CallSite != null ? CallSite.GetHashCode() : 0) + unchecked(31 * Method.GetHashCode());
|
||||
}
|
||||
}
|
||||
|
||||
private class GenericLookupKey : IEquatable<GenericLookupKey>
|
||||
{
|
||||
public readonly CORINFO_RUNTIME_LOOKUP_KIND LookupKind;
|
||||
public readonly ReadyToRunFixupKind FixupKind;
|
||||
public readonly TypeDesc TypeArgument;
|
||||
public readonly MethodWithToken MethodArgument;
|
||||
public readonly TypeDesc ContextType;
|
||||
|
||||
public GenericLookupKey(CORINFO_RUNTIME_LOOKUP_KIND lookupKind, ReadyToRunFixupKind fixupKind, TypeDesc typeArgument, MethodWithToken methodArgument, TypeDesc contextType)
|
||||
{
|
||||
LookupKind = lookupKind;
|
||||
FixupKind = fixupKind;
|
||||
TypeArgument = typeArgument;
|
||||
MethodArgument = methodArgument;
|
||||
ContextType = contextType;
|
||||
}
|
||||
|
||||
public bool Equals(GenericLookupKey other)
|
||||
{
|
||||
return LookupKind == other.LookupKind &&
|
||||
FixupKind == other.FixupKind &&
|
||||
RuntimeDeterminedTypeHelper.Equals(TypeArgument, other.TypeArgument) &&
|
||||
MethodArgument == other.MethodArgument &&
|
||||
ContextType == other.ContextType;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is GenericLookupKey other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return unchecked(((int)LookupKind << 24) +
|
||||
(int)FixupKind +
|
||||
(TypeArgument != null ? 31 * RuntimeDeterminedTypeHelper.GetHashCode(TypeArgument) : 0) +
|
||||
(MethodArgument != null ? 31 * RuntimeDeterminedTypeHelper.GetHashCode(MethodArgument.Method) : 0) +
|
||||
97 * (ContextType?.GetHashCode() ?? 0));
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<GenericLookupKey, ISymbolNode> _genericLookupHelpers = new Dictionary<GenericLookupKey, ISymbolNode>();
|
||||
|
||||
public ISymbolNode GenericLookupHelper(
|
||||
CORINFO_RUNTIME_LOOKUP_KIND runtimeLookupKind,
|
||||
ReadyToRunHelperId helperId,
|
||||
object helperArgument,
|
||||
TypeDesc contextType,
|
||||
SignatureContext signatureContext)
|
||||
{
|
||||
switch (helperId)
|
||||
{
|
||||
case ReadyToRunHelperId.NecessaryTypeHandle:
|
||||
case ReadyToRunHelperId.TypeHandle:
|
||||
return GenericLookupTypeHelper(
|
||||
runtimeLookupKind,
|
||||
ReadyToRunFixupKind.READYTORUN_FIXUP_TypeHandle,
|
||||
(TypeDesc)helperArgument,
|
||||
contextType,
|
||||
signatureContext);
|
||||
|
||||
case ReadyToRunHelperId.MethodHandle:
|
||||
return GenericLookupMethodHelper(
|
||||
runtimeLookupKind,
|
||||
ReadyToRunFixupKind.READYTORUN_FIXUP_MethodHandle,
|
||||
(MethodWithToken)helperArgument,
|
||||
contextType,
|
||||
signatureContext);
|
||||
|
||||
case ReadyToRunHelperId.MethodEntry:
|
||||
return GenericLookupMethodHelper(
|
||||
runtimeLookupKind,
|
||||
ReadyToRunFixupKind.READYTORUN_FIXUP_MethodEntry,
|
||||
(MethodWithToken)helperArgument,
|
||||
contextType,
|
||||
signatureContext);
|
||||
|
||||
case ReadyToRunHelperId.MethodDictionary:
|
||||
return GenericLookupMethodHelper(
|
||||
runtimeLookupKind,
|
||||
ReadyToRunFixupKind.READYTORUN_FIXUP_MethodDictionary,
|
||||
(MethodWithToken)helperArgument,
|
||||
contextType,
|
||||
signatureContext);
|
||||
|
||||
case ReadyToRunHelperId.VirtualDispatchCell:
|
||||
return GenericLookupMethodHelper(
|
||||
runtimeLookupKind,
|
||||
ReadyToRunFixupKind.READYTORUN_FIXUP_VirtualEntry,
|
||||
(MethodWithToken)helperArgument,
|
||||
contextType,
|
||||
signatureContext);
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private ISymbolNode GenericLookupTypeHelper(
|
||||
CORINFO_RUNTIME_LOOKUP_KIND runtimeLookupKind,
|
||||
ReadyToRunFixupKind fixupKind,
|
||||
TypeDesc typeArgument,
|
||||
TypeDesc contextType,
|
||||
SignatureContext signatureContext)
|
||||
{
|
||||
GenericLookupKey key = new GenericLookupKey(runtimeLookupKind, fixupKind, typeArgument, methodArgument: null, contextType);
|
||||
ISymbolNode node;
|
||||
if (!_genericLookupHelpers.TryGetValue(key, out node))
|
||||
{
|
||||
node = new DelayLoadHelperImport(
|
||||
_codegenNodeFactory,
|
||||
_codegenNodeFactory.HelperImports,
|
||||
ILCompiler.DependencyAnalysis.ReadyToRun.ReadyToRunHelper.READYTORUN_HELPER_DelayLoad_Helper,
|
||||
new GenericLookupSignature(runtimeLookupKind, fixupKind, typeArgument, methodArgument: null, contextType, signatureContext));
|
||||
_genericLookupHelpers.Add(key, node);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private ISymbolNode GenericLookupMethodHelper(
|
||||
CORINFO_RUNTIME_LOOKUP_KIND runtimeLookupKind,
|
||||
ReadyToRunFixupKind fixupKind,
|
||||
MethodWithToken methodArgument,
|
||||
TypeDesc contextType,
|
||||
SignatureContext signatureContext)
|
||||
{
|
||||
GenericLookupKey key = new GenericLookupKey(runtimeLookupKind, fixupKind, typeArgument: null, methodArgument, contextType);
|
||||
ISymbolNode node;
|
||||
if (!_genericLookupHelpers.TryGetValue(key, out node))
|
||||
{
|
||||
node = new DelayLoadHelperImport(
|
||||
_codegenNodeFactory,
|
||||
_codegenNodeFactory.HelperImports,
|
||||
ILCompiler.DependencyAnalysis.ReadyToRun.ReadyToRunHelper.READYTORUN_HELPER_DelayLoad_Helper,
|
||||
new GenericLookupSignature(runtimeLookupKind, fixupKind, typeArgument: null, methodArgument, contextType, signatureContext));
|
||||
_genericLookupHelpers.Add(key, node);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,19 +5,23 @@
|
|||
using System;
|
||||
using Internal.TypeSystem;
|
||||
|
||||
using ILCompiler.DependencyAnalysis.ReadyToRun;
|
||||
|
||||
namespace ILCompiler.DependencyAnalysis
|
||||
{
|
||||
internal struct TypeAndMethod : IEquatable<TypeAndMethod>
|
||||
{
|
||||
public readonly TypeDesc Type;
|
||||
public readonly MethodDesc Method;
|
||||
public readonly ModuleToken MethodToken;
|
||||
public readonly bool IsUnboxingStub;
|
||||
public readonly bool IsInstantiatingStub;
|
||||
|
||||
public TypeAndMethod(TypeDesc type, MethodDesc method, bool isUnboxingStub, bool isInstantiatingStub)
|
||||
public TypeAndMethod(TypeDesc type, MethodDesc method, ModuleToken methodToken, bool isUnboxingStub, bool isInstantiatingStub)
|
||||
{
|
||||
Type = type;
|
||||
Method = method;
|
||||
MethodToken = methodToken;
|
||||
IsUnboxingStub = isUnboxingStub;
|
||||
IsInstantiatingStub = isInstantiatingStub;
|
||||
}
|
||||
|
@ -26,6 +30,7 @@ namespace ILCompiler.DependencyAnalysis
|
|||
{
|
||||
return Type == other.Type &&
|
||||
Method == other.Method &&
|
||||
MethodToken.Equals(other.MethodToken) &&
|
||||
IsUnboxingStub == other.IsUnboxingStub &&
|
||||
IsInstantiatingStub == other.IsInstantiatingStub;
|
||||
}
|
||||
|
@ -37,7 +42,10 @@ namespace ILCompiler.DependencyAnalysis
|
|||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (Type?.GetHashCode() ?? 0) ^ unchecked(Method.GetHashCode() * 31) ^ (IsUnboxingStub ? -0x80000000 : 0) ^ (IsInstantiatingStub ? 0x40000000 : 0);
|
||||
return (Type?.GetHashCode() ?? 0) ^
|
||||
unchecked(Method.GetHashCode() * 31 + MethodToken.GetHashCode() * 97) ^
|
||||
(IsUnboxingStub ? -0x80000000 : 0) ^
|
||||
(IsInstantiatingStub ? 0x40000000 : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,7 +95,7 @@ namespace ILCompiler
|
|||
{
|
||||
hashcode = NestedTypeHashCode(TypeHashCode(containingType), hashcode);
|
||||
}
|
||||
if (type.HasInstantiation)
|
||||
if (type.HasInstantiation && !type.IsGenericDefinition)
|
||||
{
|
||||
return GenericInstanceHashCode(hashcode, type.Instantiation);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,204 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
using Internal.TypeSystem;
|
||||
using Internal.Text;
|
||||
|
||||
namespace ILCompiler
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class used to collapse runtime determined types
|
||||
/// based on their kind and index as we otherwise don't need
|
||||
/// to distinguish among them for the purpose of emitting
|
||||
/// signatures and generic lookups.
|
||||
/// </summary>
|
||||
public static class RuntimeDeterminedTypeHelper
|
||||
{
|
||||
public static bool Equals(Instantiation instantiation1, Instantiation instantiation2)
|
||||
{
|
||||
if (instantiation1.Length != instantiation2.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (int argIndex = 0; argIndex < instantiation1.Length; argIndex++)
|
||||
{
|
||||
if (!Equals(instantiation1[argIndex], instantiation2[argIndex]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool Equals(TypeDesc type1, TypeDesc type2)
|
||||
{
|
||||
if (type1 == type2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
RuntimeDeterminedType runtimeDeterminedType1 = type1 as RuntimeDeterminedType;
|
||||
RuntimeDeterminedType runtimeDeterminedType2 = type2 as RuntimeDeterminedType;
|
||||
if (runtimeDeterminedType1 != null || runtimeDeterminedType2 != null)
|
||||
{
|
||||
if (runtimeDeterminedType1 == null || runtimeDeterminedType2 == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return runtimeDeterminedType1.RuntimeDeterminedDetailsType.Index == runtimeDeterminedType2.RuntimeDeterminedDetailsType.Index &&
|
||||
runtimeDeterminedType1.RuntimeDeterminedDetailsType.Kind == runtimeDeterminedType2.RuntimeDeterminedDetailsType.Kind;
|
||||
}
|
||||
|
||||
if (type1.GetTypeDefinition() != type2.GetTypeDefinition() ||
|
||||
!Equals(type1.Instantiation, type2.Instantiation))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool Equals(MethodDesc method1, MethodDesc method2)
|
||||
{
|
||||
if (method1 == method2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (!Equals(method1.OwningType, method2.OwningType) ||
|
||||
method1.Signature.Length != method2.Signature.Length ||
|
||||
!Equals(method1.Instantiation, method2.Instantiation) ||
|
||||
!Equals(method1.Signature.ReturnType, method2.Signature.ReturnType))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (int argIndex = 0; argIndex < method1.Signature.Length; argIndex++)
|
||||
{
|
||||
if (!Equals(method1.Signature[argIndex], method2.Signature[argIndex]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static int GetHashCode(Instantiation instantiation)
|
||||
{
|
||||
int hashcode = unchecked(instantiation.Length << 24);
|
||||
for (int typeArgIndex = 0; typeArgIndex < instantiation.Length; typeArgIndex++)
|
||||
{
|
||||
hashcode = unchecked(hashcode * 73 + GetHashCode(instantiation[typeArgIndex]));
|
||||
}
|
||||
return hashcode;
|
||||
|
||||
}
|
||||
|
||||
public static int GetHashCode(TypeDesc type)
|
||||
{
|
||||
if (type is RuntimeDeterminedType runtimeDeterminedType)
|
||||
{
|
||||
return runtimeDeterminedType.RuntimeDeterminedDetailsType.Index ^
|
||||
((int)runtimeDeterminedType.RuntimeDeterminedDetailsType.Kind << 30);
|
||||
}
|
||||
return type.GetTypeDefinition().GetHashCode() ^ GetHashCode(type.Instantiation);
|
||||
}
|
||||
|
||||
public static int GetHashCode(MethodDesc method)
|
||||
{
|
||||
return unchecked(GetHashCode(method.OwningType) + 97 * (
|
||||
method.GetTypicalMethodDefinition().GetHashCode() + 31 * GetHashCode(method.Instantiation)));
|
||||
}
|
||||
|
||||
public static void WriteTo(Instantiation instantiation, Utf8StringBuilder sb)
|
||||
{
|
||||
sb.Append("<");
|
||||
for (int typeArgIndex = 0; typeArgIndex < instantiation.Length; typeArgIndex++)
|
||||
{
|
||||
if (typeArgIndex != 0)
|
||||
{
|
||||
sb.Append(", ");
|
||||
}
|
||||
WriteTo(instantiation[typeArgIndex], sb);
|
||||
}
|
||||
sb.Append(">");
|
||||
}
|
||||
|
||||
public static void WriteTo(TypeDesc type, Utf8StringBuilder sb)
|
||||
{
|
||||
if (type is RuntimeDeterminedType runtimeDeterminedType)
|
||||
{
|
||||
switch (runtimeDeterminedType.RuntimeDeterminedDetailsType.Kind)
|
||||
{
|
||||
case GenericParameterKind.Type:
|
||||
sb.Append("T");
|
||||
break;
|
||||
|
||||
case GenericParameterKind.Method:
|
||||
sb.Append("M");
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
sb.Append(runtimeDeterminedType.RuntimeDeterminedDetailsType.Index.ToString());
|
||||
}
|
||||
else if (type is InstantiatedType instantiatedType)
|
||||
{
|
||||
sb.Append(instantiatedType.GetTypeDefinition().ToString());
|
||||
WriteTo(instantiatedType.Instantiation, sb);
|
||||
}
|
||||
else if (type is ArrayType arrayType)
|
||||
{
|
||||
WriteTo(arrayType.ElementType, sb);
|
||||
sb.Append("[");
|
||||
switch (arrayType.Rank)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
sb.Append("*");
|
||||
break;
|
||||
default:
|
||||
sb.Append(new String(',', arrayType.Rank - 1));
|
||||
break;
|
||||
}
|
||||
sb.Append("]");
|
||||
}
|
||||
else if (type is ByRefType byRefType)
|
||||
{
|
||||
WriteTo(byRefType.ParameterType, sb);
|
||||
sb.Append("&");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(type is DefType);
|
||||
sb.Append(type.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public static void WriteTo(MethodDesc method, Utf8StringBuilder sb)
|
||||
{
|
||||
WriteTo(method.Signature.ReturnType, sb);
|
||||
sb.Append(" ");
|
||||
WriteTo(method.OwningType, sb);
|
||||
sb.Append(method.Name);
|
||||
if (method.HasInstantiation)
|
||||
{
|
||||
WriteTo(method.Instantiation, sb);
|
||||
}
|
||||
sb.Append("(");
|
||||
for (int argIndex = 0; argIndex < method.Signature.Length; argIndex++)
|
||||
{
|
||||
if (argIndex != 0)
|
||||
{
|
||||
sb.Append(", ");
|
||||
}
|
||||
WriteTo(method.Signature[argIndex], sb);
|
||||
}
|
||||
sb.Append(")");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -36,6 +36,7 @@
|
|||
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\CompilerIdentifierNode.cs" />
|
||||
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\DebugInfoTableNode.cs" />
|
||||
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\DelayLoadHelperImport.cs" />
|
||||
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\GenericLookupSignature.cs" />
|
||||
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\ImportThunk.cs" />
|
||||
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\DelegateCtorSignature.cs" />
|
||||
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\DevirtualizationManager.cs" />
|
||||
|
@ -48,6 +49,7 @@
|
|||
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\ImportSectionsTableNode.cs" />
|
||||
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\InstanceEntryPointTableNode.cs" />
|
||||
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\IReadyToRunMethodCodeNode.cs" />
|
||||
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\LocalMethodImport.cs" />
|
||||
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\MethodEntryPointTableNode.cs" />
|
||||
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\MethodFixupSignature.cs" />
|
||||
<Compile Include="Compiler\DependencyAnalysis\ReadyToRun\MethodGCInfoNode.cs" />
|
||||
|
@ -84,6 +86,7 @@
|
|||
<Compile Include="Compiler\ReadyToRunNodeMangler.cs" />
|
||||
<Compile Include="Compiler\ReadyToRunSingleAssemblyCompilationModuleGroup.cs" />
|
||||
<Compile Include="Compiler\ReadyToRunTableManager.cs" />
|
||||
<Compile Include="Compiler\RuntimeDeterminedTypeHelper.cs" />
|
||||
<Compile Include="Compiler\SystemObjectFieldLayoutAlgorithm.cs" />
|
||||
<Compile Include="IL\ReadyToRunILProvider.cs" />
|
||||
<Compile Include="JitInterface\CorInfoImpl.ReadyToRun.cs" />
|
||||
|
|
|
@ -16,6 +16,18 @@ using ReadyToRunHelper = ILCompiler.ReadyToRunHelper;
|
|||
|
||||
namespace Internal.JitInterface
|
||||
{
|
||||
public class MethodWithToken
|
||||
{
|
||||
public readonly MethodDesc Method;
|
||||
public readonly ModuleToken Token;
|
||||
|
||||
public MethodWithToken(MethodDesc method, ModuleToken token)
|
||||
{
|
||||
Method = method;
|
||||
Token = token;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe partial class CorInfoImpl
|
||||
{
|
||||
private const CORINFO_RUNTIME_ABI TargetABI = CORINFO_RUNTIME_ABI.CORINFO_CORECLR_ABI;
|
||||
|
@ -180,7 +192,21 @@ namespace Internal.JitInterface
|
|||
Debug.Assert(typeToInitialize.IsCanonicalSubtype(CanonicalFormKind.Any));
|
||||
|
||||
DefType helperArg = typeToInitialize.ConvertToSharedRuntimeDeterminedForm();
|
||||
ISymbolNode helper = GetGenericLookupHelper(pGenericLookupKind.runtimeLookupKind, ReadyToRunHelperId.GetNonGCStaticBase, helperArg);
|
||||
TypeDesc contextType;
|
||||
if (pGenericLookupKind.runtimeLookupKind == CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_THISOBJ)
|
||||
{
|
||||
contextType = methodFromContext(pResolvedToken.tokenContext).OwningType;
|
||||
}
|
||||
else
|
||||
{
|
||||
contextType = null;
|
||||
}
|
||||
ISymbolNode helper = _compilation.SymbolNodeFactory.GenericLookupHelper(
|
||||
pGenericLookupKind.runtimeLookupKind,
|
||||
ReadyToRunHelperId.GetNonGCStaticBase,
|
||||
helperArg,
|
||||
contextType,
|
||||
_signatureContext);
|
||||
pLookup = CreateConstLookupToSymbol(helper);
|
||||
}
|
||||
break;
|
||||
|
@ -190,7 +216,25 @@ namespace Internal.JitInterface
|
|||
|
||||
ReadyToRunHelperId helperId = (ReadyToRunHelperId)pGenericLookupKind.runtimeLookupFlags;
|
||||
object helperArg = HandleToObject((IntPtr)pGenericLookupKind.runtimeLookupArgs);
|
||||
ISymbolNode helper = GetGenericLookupHelper(pGenericLookupKind.runtimeLookupKind, helperId, helperArg);
|
||||
if (helperArg is MethodDesc methodArg)
|
||||
{
|
||||
helperArg = new MethodWithToken(methodArg, new ModuleToken(_tokenContext, pResolvedToken.token));
|
||||
}
|
||||
TypeDesc contextType;
|
||||
if (pGenericLookupKind.runtimeLookupKind == CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_THISOBJ)
|
||||
{
|
||||
contextType = methodFromContext(pResolvedToken.tokenContext).OwningType;
|
||||
}
|
||||
else
|
||||
{
|
||||
contextType = null;
|
||||
}
|
||||
ISymbolNode helper = _compilation.SymbolNodeFactory.GenericLookupHelper(
|
||||
pGenericLookupKind.runtimeLookupKind,
|
||||
helperId,
|
||||
helperArg,
|
||||
contextType,
|
||||
_signatureContext);
|
||||
pLookup = CreateConstLookupToSymbol(helper);
|
||||
}
|
||||
break;
|
||||
|
@ -240,7 +284,7 @@ namespace Internal.JitInterface
|
|||
{
|
||||
pLookup.lookupKind.needsRuntimeLookup = false;
|
||||
pLookup.constLookup = CreateConstLookupToSymbol(_compilation.SymbolNodeFactory.DelegateCtor(
|
||||
delegateTypeDesc, targetMethod, _signatureContext));
|
||||
delegateTypeDesc, targetMethod, new ModuleToken(_tokenContext, (mdToken)pTargetMethod.token), _signatureContext));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -534,7 +578,12 @@ namespace Internal.JitInterface
|
|||
if (method.IsVirtual)
|
||||
throw new NotImplementedException("getFunctionEntryPoint");
|
||||
|
||||
pResult = CreateConstLookupToSymbol(_compilation.NodeFactory.MethodEntrypoint(method));
|
||||
pResult = CreateConstLookupToSymbol(_compilation.NodeFactory.MethodEntrypoint(
|
||||
method,
|
||||
constrainedType: null,
|
||||
originalMethod: null,
|
||||
methodToken: default(ModuleToken), // TODO!!!!
|
||||
_signatureContext));
|
||||
}
|
||||
|
||||
private InfoAccessType constructStringLiteral(CORINFO_MODULE_STRUCT_* module, mdToken metaTok, ref void* ppValue)
|
||||
|
|
|
@ -836,5 +836,23 @@ namespace Internal.JitInterface
|
|||
|
||||
_parameterIndexToNameMap = parameterIndexToNameMap;
|
||||
}
|
||||
|
||||
private ISymbolNode GetGenericLookupHelper(CORINFO_RUNTIME_LOOKUP_KIND runtimeLookupKind, ReadyToRunHelperId helperId, object helperArgument)
|
||||
{
|
||||
// Necessary type handle is not something that can be in a dictionary (only a constructed type).
|
||||
// We only use necessary type handles if we can do a constant lookup.
|
||||
if (helperId == ReadyToRunHelperId.NecessaryTypeHandle)
|
||||
helperId = ReadyToRunHelperId.TypeHandle;
|
||||
|
||||
if (runtimeLookupKind == CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_THISOBJ
|
||||
|| runtimeLookupKind == CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_CLASSPARAM)
|
||||
{
|
||||
return _compilation.NodeFactory.ReadyToRunHelperFromTypeLookup(helperId, helperArgument, MethodBeingCompiled.OwningType);
|
||||
}
|
||||
|
||||
Debug.Assert(runtimeLookupKind == CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_METHODPARAM);
|
||||
return _compilation.NodeFactory.ReadyToRunHelperFromDictionaryLookup(helperId, helperArgument, MethodBeingCompiled);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1428,23 +1428,6 @@ namespace Internal.JitInterface
|
|||
return type.IsNullable ? CorInfoHelpFunc.CORINFO_HELP_UNBOX_NULLABLE : CorInfoHelpFunc.CORINFO_HELP_UNBOX;
|
||||
}
|
||||
|
||||
private ISymbolNode GetGenericLookupHelper(CORINFO_RUNTIME_LOOKUP_KIND runtimeLookupKind, ReadyToRunHelperId helperId, object helperArgument)
|
||||
{
|
||||
// Necessary type handle is not something that can be in a dictionary (only a constructed type).
|
||||
// We only use necessary type handles if we can do a constant lookup.
|
||||
if (helperId == ReadyToRunHelperId.NecessaryTypeHandle)
|
||||
helperId = ReadyToRunHelperId.TypeHandle;
|
||||
|
||||
if (runtimeLookupKind == CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_THISOBJ
|
||||
|| runtimeLookupKind == CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_CLASSPARAM)
|
||||
{
|
||||
return _compilation.NodeFactory.ReadyToRunHelperFromTypeLookup(helperId, helperArgument, MethodBeingCompiled.OwningType);
|
||||
}
|
||||
|
||||
Debug.Assert(runtimeLookupKind == CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_METHODPARAM);
|
||||
return _compilation.NodeFactory.ReadyToRunHelperFromDictionaryLookup(helperId, helperArgument, MethodBeingCompiled);
|
||||
}
|
||||
|
||||
private byte* getHelperName(CorInfoHelpFunc helpFunc)
|
||||
{
|
||||
return (byte*)GetPin(StringToUTF8(helpFunc.ToString()));
|
||||
|
@ -2689,7 +2672,8 @@ namespace Internal.JitInterface
|
|||
// Calling a string constructor doesn't call the actual constructor.
|
||||
pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol(
|
||||
#if READYTORUN
|
||||
_compilation.NodeFactory.StringAllocator(targetMethod, _signatureContext)
|
||||
_compilation.NodeFactory.StringAllocator(targetMethod,
|
||||
new ModuleToken(_tokenContext, pResolvedToken.token), _signatureContext)
|
||||
|
||||
#else
|
||||
_compilation.NodeFactory.StringAllocator(targetMethod)
|
||||
|
@ -2706,7 +2690,8 @@ namespace Internal.JitInterface
|
|||
Debug.Assert(!forceUseRuntimeLookup);
|
||||
pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol(
|
||||
#if READYTORUN
|
||||
_compilation.NodeFactory.MethodEntrypoint(targetMethod, constrainedType, method, _signatureContext)
|
||||
_compilation.NodeFactory.MethodEntrypoint(targetMethod, constrainedType, method,
|
||||
new ModuleToken(_tokenContext, pResolvedToken.token), _signatureContext)
|
||||
#else
|
||||
_compilation.NodeFactory.MethodEntrypoint(targetMethod)
|
||||
#endif
|
||||
|
@ -2719,7 +2704,8 @@ namespace Internal.JitInterface
|
|||
if (targetMethod.RequiresInstMethodDescArg())
|
||||
{
|
||||
#if READYTORUN
|
||||
instParam = _compilation.SymbolNodeFactory.MethodGenericDictionary(concreteMethod, _signatureContext);
|
||||
instParam = _compilation.SymbolNodeFactory.MethodGenericDictionary(concreteMethod,
|
||||
new ModuleToken(_tokenContext, pResolvedToken.token), _signatureContext);
|
||||
#else
|
||||
instParam = _compilation.NodeFactory.MethodGenericDictionary(concreteMethod);
|
||||
#endif
|
||||
|
@ -2741,7 +2727,8 @@ namespace Internal.JitInterface
|
|||
|
||||
pResult->codePointerOrStubLookup.constLookup = CreateConstLookupToSymbol(
|
||||
#if READYTORUN
|
||||
_compilation.NodeFactory.MethodEntrypoint(targetMethod, constrainedType, method, _signatureContext)
|
||||
_compilation.NodeFactory.MethodEntrypoint(targetMethod, constrainedType, method,
|
||||
new ModuleToken(_tokenContext, pResolvedToken.token), _signatureContext)
|
||||
#else
|
||||
_compilation.NodeFactory.MethodEntrypoint(targetMethod)
|
||||
#endif
|
||||
|
@ -2794,7 +2781,8 @@ namespace Internal.JitInterface
|
|||
pResult->codePointerOrStubLookup.constLookup.accessType = InfoAccessType.IAT_PVALUE;
|
||||
pResult->codePointerOrStubLookup.constLookup.addr = (void*)ObjectToHandle(
|
||||
#if READYTORUN
|
||||
_compilation.SymbolNodeFactory.InterfaceDispatchCell(targetMethod, _signatureContext, isUnboxingStub: false
|
||||
_compilation.SymbolNodeFactory.InterfaceDispatchCell(targetMethod,
|
||||
new ModuleToken(_tokenContext, (mdToken)pResolvedToken.token), _signatureContext, isUnboxingStub: false
|
||||
#else
|
||||
_compilation.NodeFactory.InterfaceDispatchCell(targetMethod
|
||||
#endif // READYTORUN
|
||||
|
@ -2853,7 +2841,13 @@ namespace Internal.JitInterface
|
|||
|
||||
pResult->codePointerOrStubLookup.constLookup =
|
||||
CreateConstLookupToSymbol(
|
||||
_compilation.NodeFactory.ReadyToRunHelper(helperId, slotDefiningMethod));
|
||||
_compilation.NodeFactory.ReadyToRunHelper(helperId,
|
||||
#if READYTORUN
|
||||
new MethodWithToken(slotDefiningMethod, new ModuleToken(_tokenContext, pResolvedToken.token))
|
||||
#else
|
||||
slotDefiningMethod
|
||||
#endif
|
||||
));
|
||||
}
|
||||
|
||||
// The current CoreRT ReadyToRun helpers do not handle null thisptr - ask the JIT to emit explicit null checks
|
||||
|
|
|
@ -472,6 +472,161 @@ internal class Program
|
|||
return intResult == ExpectedIntResult && stringResult == ExpectedStringResult;
|
||||
}
|
||||
|
||||
class GenericLookup<T>
|
||||
{
|
||||
public static bool CheckStaticTypeArg(string typeArgName)
|
||||
{
|
||||
return CompareArgName(typeof(T).ToString(), typeArgName);
|
||||
}
|
||||
|
||||
public bool CheckInstanceTypeArg(string typeArgName)
|
||||
{
|
||||
return CompareArgName(typeof(T).ToString(), typeArgName);
|
||||
}
|
||||
|
||||
public static bool CheckStaticTypeArg<U>(string tName, string uName)
|
||||
{
|
||||
return CompareArgName(typeof(T).ToString(), tName) && CompareArgName(typeof(U).ToString(), uName);
|
||||
}
|
||||
|
||||
public bool CheckInstanceTypeArg<U>(string tName, string uName)
|
||||
{
|
||||
return CompareArgName(typeof(T).ToString(), tName) && CompareArgName(typeof(U).ToString(), uName);
|
||||
}
|
||||
|
||||
private static bool CompareArgName(string actual, string expected)
|
||||
{
|
||||
if (actual == expected)
|
||||
{
|
||||
Console.WriteLine("Arg type match: {0}", actual);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Arg type mismatch: actual = {0}, expected = {1}", actual, expected);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct GenericStruct<T>
|
||||
{
|
||||
public T FieldOfT;
|
||||
|
||||
public GenericStruct(T fieldOfT)
|
||||
{
|
||||
FieldOfT = fieldOfT;
|
||||
}
|
||||
}
|
||||
|
||||
class GenericClass<T>
|
||||
{
|
||||
public T FieldOfT;
|
||||
|
||||
public GenericClass(T fieldOfT)
|
||||
{
|
||||
FieldOfT = fieldOfT;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ThisObjGenericLookupTest()
|
||||
{
|
||||
Console.WriteLine("ThisObjGenericLookup:");
|
||||
bool result = true;
|
||||
result &= (new GenericLookup<object>()).CheckInstanceTypeArg("System.Object");
|
||||
result &= (new GenericLookup<string>()).CheckInstanceTypeArg("System.String");
|
||||
result &= (new GenericLookup<int>()).CheckInstanceTypeArg("System.Int32");
|
||||
result &= (new GenericLookup<GenericStruct<object>>()).CheckInstanceTypeArg("Program+GenericStruct`1[System.Object]");
|
||||
result &= (new GenericLookup<GenericStruct<string>>()).CheckInstanceTypeArg("Program+GenericStruct`1[System.String]");
|
||||
result &= (new GenericLookup<GenericStruct<int>>()).CheckInstanceTypeArg("Program+GenericStruct`1[System.Int32]");
|
||||
result &= (new GenericLookup<GenericClass<object>>()).CheckInstanceTypeArg("Program+GenericClass`1[System.Object]");
|
||||
result &= (new GenericLookup<GenericClass<string>>()).CheckInstanceTypeArg("Program+GenericClass`1[System.String]");
|
||||
result &= (new GenericLookup<GenericClass<int>>()).CheckInstanceTypeArg("Program+GenericClass`1[System.Int32]");
|
||||
return result;
|
||||
}
|
||||
|
||||
private static bool ClassParamGenericLookupTest()
|
||||
{
|
||||
Console.WriteLine("ClassParamGenericLookup:");
|
||||
bool result = true;
|
||||
result &= GenericLookup<object>.CheckStaticTypeArg("System.Object");
|
||||
result &= GenericLookup<string>.CheckStaticTypeArg("System.String");
|
||||
result &= GenericLookup<int>.CheckStaticTypeArg("System.Int32");
|
||||
result &= GenericLookup<GenericStruct<object>>.CheckStaticTypeArg("Program+GenericStruct`1[System.Object]");
|
||||
result &= GenericLookup<GenericStruct<string>>.CheckStaticTypeArg("Program+GenericStruct`1[System.String]");
|
||||
result &= GenericLookup<GenericStruct<int>>.CheckStaticTypeArg("Program+GenericStruct`1[System.Int32]");
|
||||
result &= GenericLookup<GenericClass<object>>.CheckStaticTypeArg("Program+GenericClass`1[System.Object]");
|
||||
result &= GenericLookup<GenericClass<string>>.CheckStaticTypeArg("Program+GenericClass`1[System.String]");
|
||||
result &= GenericLookup<GenericClass<int>>.CheckStaticTypeArg("Program+GenericClass`1[System.Int32]");
|
||||
return result;
|
||||
}
|
||||
|
||||
private static bool MethodParamGenericLookupTest()
|
||||
{
|
||||
Console.WriteLine("MethodParamGenericLookup:");
|
||||
bool result = true;
|
||||
|
||||
result &= GenericLookup<object>.CheckStaticTypeArg<int>("System.Object", "System.Int32");
|
||||
result &= GenericLookup<string>.CheckStaticTypeArg<object>("System.String", "System.Object");
|
||||
result &= GenericLookup<int>.CheckStaticTypeArg<string>("System.Int32", "System.String");
|
||||
|
||||
result &= GenericLookup<GenericStruct<object>>.CheckStaticTypeArg<GenericStruct<int>>(
|
||||
"Program+GenericStruct`1[System.Object]", "Program+GenericStruct`1[System.Int32]");
|
||||
result &= GenericLookup<GenericStruct<string>>.CheckStaticTypeArg<GenericStruct<object>>(
|
||||
"Program+GenericStruct`1[System.String]", "Program+GenericStruct`1[System.Object]");
|
||||
result &= GenericLookup<GenericStruct<int>>.CheckStaticTypeArg<GenericStruct<string>>(
|
||||
"Program+GenericStruct`1[System.Int32]", "Program+GenericStruct`1[System.String]");
|
||||
|
||||
result &= GenericLookup<GenericClass<object>>.CheckStaticTypeArg<GenericClass<int>>(
|
||||
"Program+GenericClass`1[System.Object]", "Program+GenericClass`1[System.Int32]");
|
||||
result &= GenericLookup<GenericClass<string>>.CheckStaticTypeArg<GenericClass<object>>(
|
||||
"Program+GenericClass`1[System.String]", "Program+GenericClass`1[System.Object]");
|
||||
result &= GenericLookup<GenericClass<int>>.CheckStaticTypeArg<GenericClass<string>>(
|
||||
"Program+GenericClass`1[System.Int32]", "Program+GenericClass`1[System.String]");
|
||||
|
||||
result &= GenericLookup<GenericClass<object>>.CheckStaticTypeArg<GenericStruct<int>>(
|
||||
"Program+GenericClass`1[System.Object]", "Program+GenericStruct`1[System.Int32]");
|
||||
result &= GenericLookup<GenericClass<string>>.CheckStaticTypeArg<GenericStruct<object>>(
|
||||
"Program+GenericClass`1[System.String]", "Program+GenericStruct`1[System.Object]");
|
||||
result &= GenericLookup<GenericClass<int>>.CheckStaticTypeArg<GenericStruct<string>>(
|
||||
"Program+GenericClass`1[System.Int32]", "Program+GenericStruct`1[System.String]");
|
||||
|
||||
result &= GenericLookup<GenericStruct<object>>.CheckStaticTypeArg<GenericClass<int>>(
|
||||
"Program+GenericStruct`1[System.Object]", "Program+GenericClass`1[System.Int32]");
|
||||
result &= GenericLookup<GenericStruct<string>>.CheckStaticTypeArg<GenericClass<object>>(
|
||||
"Program+GenericStruct`1[System.String]", "Program+GenericClass`1[System.Object]");
|
||||
result &= GenericLookup<GenericStruct<int>>.CheckStaticTypeArg<GenericClass<string>>(
|
||||
"Program+GenericStruct`1[System.Int32]", "Program+GenericClass`1[System.String]");
|
||||
|
||||
result &= (new GenericLookup<object>()).CheckInstanceTypeArg<GenericStruct<int>>(
|
||||
"System.Object", "Program+GenericStruct`1[System.Int32]");
|
||||
result &= (new GenericLookup<string>()).CheckInstanceTypeArg<GenericStruct<object>>(
|
||||
"System.String", "Program+GenericStruct`1[System.Object]");
|
||||
result &= (new GenericLookup<int>()).CheckInstanceTypeArg<GenericStruct<string>>(
|
||||
"System.Int32", "Program+GenericStruct`1[System.String]");
|
||||
result &= (new GenericLookup<GenericStruct<object>>()).CheckInstanceTypeArg<int>(
|
||||
"Program+GenericStruct`1[System.Object]", "System.Int32");
|
||||
result &= (new GenericLookup<GenericStruct<string>>()).CheckInstanceTypeArg<object>(
|
||||
"Program+GenericStruct`1[System.String]", "System.Object");
|
||||
result &= (new GenericLookup<GenericStruct<int>>()).CheckInstanceTypeArg<string>(
|
||||
"Program+GenericStruct`1[System.Int32]", "System.String");
|
||||
|
||||
result &= (new GenericLookup<object>()).CheckInstanceTypeArg<GenericClass<int>>(
|
||||
"System.Object", "Program+GenericClass`1[System.Int32]");
|
||||
result &= (new GenericLookup<string>()).CheckInstanceTypeArg<GenericClass<object>>(
|
||||
"System.String", "Program+GenericClass`1[System.Object]");
|
||||
result &= (new GenericLookup<int>()).CheckInstanceTypeArg<GenericClass<string>>(
|
||||
"System.Int32", "Program+GenericClass`1[System.String]");
|
||||
result &= (new GenericLookup<GenericClass<object>>()).CheckInstanceTypeArg<int>(
|
||||
"Program+GenericClass`1[System.Object]", "System.Int32");
|
||||
result &= (new GenericLookup<GenericClass<string>>()).CheckInstanceTypeArg<object>(
|
||||
"Program+GenericClass`1[System.String]", "System.Object");
|
||||
result &= (new GenericLookup<GenericClass<int>>()).CheckInstanceTypeArg<string>(
|
||||
"Program+GenericClass`1[System.Int32]", "System.String");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
if (args.Length > 0)
|
||||
|
@ -513,6 +668,9 @@ internal class Program
|
|||
RunTest("TryCatch", TryCatch());
|
||||
RunTest("FileStreamNullRefTryCatch", FileStreamNullRefTryCatch());
|
||||
RunTest("InstanceMethodTest", InstanceMethodTest());
|
||||
RunTest("ThisObjGenericLookupTest", ThisObjGenericLookupTest());
|
||||
RunTest("ClassParamGenericLookupTest", ClassParamGenericLookupTest());
|
||||
RunTest("MethodParamGenericLookupTest", MethodParamGenericLookupTest());
|
||||
|
||||
Console.WriteLine($@"{_passedTests.Count} tests pass:");
|
||||
foreach (string testName in _passedTests)
|
||||
|
|
Загрузка…
Ссылка в новой задаче