Skip to content

Commit

Permalink
Resolve generic virtual methods in managed type system (#80035)
Browse files Browse the repository at this point in the history
Generic virtual method resolution currently happens on top of `RuntimeTypeHandle`s (i.e. the runtime type system). This works as long as we only need to do generic virtual method resolution on top of types that exist in the runtime.

In order to lay foundation for fixing #77070, we need to be able to resolve not just with types that have a type handle, but also for types that we're in the process of building (e.g. due to MakeGeneric, or due to another generic virtual method dispatch).

This pull request rewrites generic virtual method dispatch to happen on top of the managed type system, instead of the runtime type system. It's a bit more than just a mechanical replacement because the existing algorithm was set up in a confusing way, with `ref` parameters that were changing what we were resolving and an odd `slotChanged` logic that I don't fully understand. I replaced the `slotChanged` with the straightforward thing (if we resolve interface method to a virtual method on a type, find the implementation of the virtual method on the current type). Tests seem to be passing. Also instead of a bunch of `ref` parameters we now just pass a `MethodDesc`.

This also includes a compiler change, because in order for the managed type system's casting logic to work, we need to be able to find out the variance of parameters on generic definitions. The compiler change is about generating this info.
  • Loading branch information
MichalStrehovsky authored Jan 2, 2023
1 parent 233a14d commit b81b822
Show file tree
Hide file tree
Showing 17 changed files with 254 additions and 370 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -634,7 +634,7 @@ internal GenericVariance* GenericVariance
{
get
{
Debug.Assert(IsGeneric);
Debug.Assert(IsGeneric || IsGenericTypeDefinition);

if (!HasGenericVariance)
return null;
Expand Down Expand Up @@ -1452,10 +1452,10 @@ public uint GetFieldOffset(EETypeField eField)

if (eField == EETypeField.ETF_GenericComposition)
{
Debug.Assert(IsGeneric);
Debug.Assert(IsGeneric || (IsGenericTypeDefinition && HasGenericVariance));
return cbOffset;
}
if (IsGeneric)
if (IsGeneric || (IsGenericTypeDefinition && HasGenericVariance))
{
cbOffset += relativeOrFullPointerOffset;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ namespace Internal.Runtime.TypeLoader
using DynamicGenericsRegistrationData = TypeLoaderEnvironment.DynamicGenericsRegistrationData;
using GenericTypeEntry = TypeLoaderEnvironment.GenericTypeEntry;
using GenericMethodEntry = TypeLoaderEnvironment.GenericMethodEntry;
using HandleBasedGenericMethodLookup = TypeLoaderEnvironment.HandleBasedGenericMethodLookup;
using MethodDescBasedGenericMethodLookup = TypeLoaderEnvironment.MethodDescBasedGenericMethodLookup;

internal static class LowLevelListExtensions
Expand Down Expand Up @@ -76,31 +75,16 @@ public MissingTemplateException()
}


private static bool CheckAllHandlesValidForMethod(MethodDesc method)
{
if (!method.OwningType.RetrieveRuntimeTypeHandleIfPossible())
return false;

for (int i = 0; i < method.Instantiation.Length; i++)
if (!method.Instantiation[i].RetrieveRuntimeTypeHandleIfPossible())
return false;

return true;
}

internal static bool RetrieveMethodDictionaryIfPossible(InstantiatedMethod method)
{
if (method.RuntimeMethodDictionary != IntPtr.Zero)
return true;

bool allHandlesValid = CheckAllHandlesValidForMethod(method);

TypeLoaderLogger.WriteLine("Looking for method dictionary for method " + method.ToString() + " ... " + (allHandlesValid ? "(All type arg handles valid)" : ""));
TypeLoaderLogger.WriteLine("Looking for method dictionary for method " + method.ToString() + " ... ");

IntPtr methodDictionary;

if ((allHandlesValid && TypeLoaderEnvironment.Instance.TryLookupGenericMethodDictionaryForComponents(new HandleBasedGenericMethodLookup(method), out methodDictionary)) ||
(!allHandlesValid && TypeLoaderEnvironment.Instance.TryLookupGenericMethodDictionaryForComponents(new MethodDescBasedGenericMethodLookup(method), out methodDictionary)))
if (TypeLoaderEnvironment.Instance.TryLookupGenericMethodDictionary(new MethodDescBasedGenericMethodLookup(method), out methodDictionary))
{
TypeLoaderLogger.WriteLine("Found DICT = " + methodDictionary.LowLevelToString() + " for method " + method.ToString());
method.AssociateWithRuntimeMethodDictionary(methodDictionary);
Expand Down Expand Up @@ -1289,22 +1273,6 @@ public static bool TryBuildByRefType(RuntimeTypeHandle pointeeTypeHandle, out Ru
return true;
}

public static bool TryBuildGenericMethod(RuntimeTypeHandle declaringTypeHandle, RuntimeTypeHandle[] genericMethodArgHandles, MethodNameAndSignature methodNameAndSignature, out IntPtr methodDictionary)
{
TypeSystemContext context = TypeSystemContextFactory.Create();

DefType declaringType = (DefType)context.ResolveRuntimeTypeHandle(declaringTypeHandle);
InstantiatedMethod methodBeingLoaded = (InstantiatedMethod)context.ResolveGenericMethodInstantiation(false, declaringType, methodNameAndSignature, context.ResolveRuntimeTypeHandles(genericMethodArgHandles), IntPtr.Zero, false);

bool success = TryBuildGenericMethod(methodBeingLoaded, out methodDictionary);

// Recycle the context only if we successfully built the method. The state may be partially initialized otherwise.
if (success)
TypeSystemContextFactory.Recycle(context);

return success;
}

internal static bool TryBuildGenericMethod(InstantiatedMethod methodBeingLoaded, out IntPtr methodDictionary)
{
try
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,118 +172,19 @@ internal override bool MatchGenericMethodEntry(GenericMethodEntry entry)
return parsedGenericMethod == _methodToLookup;
}
}
internal class HandleBasedGenericMethodLookup : MethodDescBasedGenericMethodLookup
{
private RuntimeTypeHandle _declaringType;
private MethodNameAndSignature _nameAndSignature;
private RuntimeTypeHandle[] _genericMethodArgumentHandles;

internal HandleBasedGenericMethodLookup(InstantiatedMethod methodToLookup) : base(methodToLookup)
{
Debug.Assert(methodToLookup != null);
_declaringType = _methodToLookup.OwningType.RuntimeTypeHandle;
_nameAndSignature = _methodToLookup.NameAndSignature;
// _genericMethodArgumentHandles not initialized here to avoid allocation of a new array (and it's not used if we initialize _typeToLookup).
}

internal HandleBasedGenericMethodLookup(RuntimeTypeHandle declaringType, MethodNameAndSignature nameAndSignature, RuntimeTypeHandle[] genericMethodArgumentHandles) : base(null)
{
Debug.Assert(!declaringType.IsNull() && nameAndSignature != null && genericMethodArgumentHandles != null);
_declaringType = declaringType;
_nameAndSignature = nameAndSignature;
_genericMethodArgumentHandles = genericMethodArgumentHandles;
}

internal override int LookupHashCode()
{
// Todo: Signatures in the hash code.
return _methodToLookup != null ? _methodToLookup.GetHashCode() : (_declaringType.GetHashCode() ^ TypeHashingAlgorithms.ComputeGenericInstanceHashCode(TypeHashingAlgorithms.ComputeNameHashCode(_nameAndSignature.Name), _genericMethodArgumentHandles));
}

internal override bool MatchParsedEntry(ref NativeParser entryParser, ref ExternalReferencesTable externalReferencesLookup, TypeManagerHandle moduleHandle)
{
// Compare entry with inputs as we parse it. If we get a mismatch, stop parsing and move to the next entry...
RuntimeTypeHandle parsedDeclaringTypeHandle = externalReferencesLookup.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned());
if (!parsedDeclaringTypeHandle.Equals(_declaringType))
return false;

// Hash table names / sigs are indirected through to the native layout info
MethodNameAndSignature nameAndSignature;
if (!TypeLoaderEnvironment.Instance.TryGetMethodNameAndSignatureFromNativeLayoutOffset(moduleHandle, entryParser.GetUnsigned(), out nameAndSignature))
return false;

if (!nameAndSignature.Equals(_nameAndSignature))
return false;

int parsedArity = (int)entryParser.GetSequenceCount();
int lookupArity = (_methodToLookup != null ? _methodToLookup.Instantiation.Length : _genericMethodArgumentHandles.Length);
if (parsedArity != lookupArity)
return false;

for (int i = 0; i < parsedArity; i++)
{
RuntimeTypeHandle parsedArg = externalReferencesLookup.GetRuntimeTypeHandleFromIndex(entryParser.GetUnsigned());
RuntimeTypeHandle lookupArg = (_methodToLookup != null ? _methodToLookup.Instantiation[i].RuntimeTypeHandle : _genericMethodArgumentHandles[i]);
if (!parsedArg.Equals(lookupArg))
return false;
}

return true;
}

internal override bool MatchGenericMethodEntry(GenericMethodEntry entry)
{
if (!entry._declaringTypeHandle.Equals(_declaringType))
return false;

if (!entry._methodNameAndSignature.Equals(_nameAndSignature))
return false;

if (entry._genericMethodArgumentHandles == null)
return false;

if (_methodToLookup != null)
{
int expectedArity = _methodToLookup.Instantiation.Length;

if (entry._genericMethodArgumentHandles.Length != expectedArity)
return false;

for (int i = 0; i < expectedArity; i++)
if (!entry._genericMethodArgumentHandles[i].Equals(_methodToLookup.Instantiation[i].RuntimeTypeHandle))
return false;
}
else
{
if (entry._genericMethodArgumentHandles.Length != _genericMethodArgumentHandles.Length)
return false;

for (int i = 0; i < _genericMethodArgumentHandles.Length; i++)
if (!entry._genericMethodArgumentHandles[i].Equals(_genericMethodArgumentHandles[i]))
return false;
}

return true;
}
}

private DynamicGenericMethodsHashtable _dynamicGenericMethods = new DynamicGenericMethodsHashtable();
private DynamicGenericMethodComponentsHashtable _dynamicGenericMethodComponents = new DynamicGenericMethodComponentsHashtable();

internal bool TryLookupGenericMethodDictionaryForComponents(GenericMethodLookupData lookupData, out IntPtr result)
internal bool TryLookupGenericMethodDictionary(GenericMethodLookupData lookupData, out IntPtr result)
{
if (!TryGetStaticGenericMethodDictionaryForComponents(lookupData, out result))
if (!TryGetDynamicGenericMethodDictionaryForComponents(lookupData, out result))
if (!TryGetStaticGenericMethodDictionary(lookupData, out result))
if (!TryGetDynamicGenericMethodDictionary(lookupData, out result))
return false;

return true;
}

public bool TryLookupGenericMethodDictionaryForComponents(RuntimeTypeHandle declaringType, MethodNameAndSignature nameAndSignature, RuntimeTypeHandle[] genericMethodArgumentHandles, out IntPtr result)
{
return TryLookupGenericMethodDictionaryForComponents(new HandleBasedGenericMethodLookup(declaringType, nameAndSignature, genericMethodArgumentHandles), out result);
}

public bool TryGetGenericMethodComponents(IntPtr methodDictionary, out RuntimeTypeHandle declaringType, out MethodNameAndSignature nameAndSignature, out RuntimeTypeHandle[] genericMethodArgumentHandles)
{
if (!TryGetDynamicGenericMethodComponents(methodDictionary, out declaringType, out nameAndSignature, out genericMethodArgumentHandles))
Expand All @@ -293,14 +194,14 @@ public bool TryGetGenericMethodComponents(IntPtr methodDictionary, out RuntimeTy
return true;
}

public bool TryLookupExactMethodPointerForComponents(RuntimeTypeHandle declaringType, MethodNameAndSignature nameAndSignature, RuntimeTypeHandle[] genericMethodArgumentHandles, out IntPtr result)
public bool TryLookupExactMethodPointer(InstantiatedMethod method, out IntPtr result)
{
int lookupHashcode = declaringType.GetHashCode();
int lookupHashcode = method.OwningType.GetHashCode();

NativeHashtable hashtable;
ExternalReferencesTable externalReferencesLookup;

HandleBasedGenericMethodLookup lookupData = new HandleBasedGenericMethodLookup(declaringType, nameAndSignature, genericMethodArgumentHandles);
MethodDescBasedGenericMethodLookup lookupData = new MethodDescBasedGenericMethodLookup(method);

foreach (NativeFormatModuleInfo module in ModuleList.EnumerateModules())
{
Expand All @@ -327,66 +228,59 @@ public bool TryLookupExactMethodPointerForComponents(RuntimeTypeHandle declaring

// This method computes the method pointer and dictionary pointer for a GVM.
// Inputs:
// - targetTypeHanlde: target type on which the GVM is implemented
// - nameAndSignature: name and signature of the GVM method
// - genericMethodArgumentHandles: GVM instantiation arguments
// - method: the GVM whose pointer and dictionary to retrieve
// Outputs:
// - methodPointer: pointer to the GVM's implementation
// - dictionaryPointer: (if applicable) pointer to the dictionary to be used with the GVM call
public bool TryGetGenericVirtualMethodPointer(RuntimeTypeHandle targetTypeHandle, MethodNameAndSignature nameAndSignature, RuntimeTypeHandle[] genericMethodArgumentHandles, out IntPtr methodPointer, out IntPtr dictionaryPointer)
public bool TryGetGenericVirtualMethodPointer(InstantiatedMethod method, out IntPtr methodPointer, out IntPtr dictionaryPointer)
{
methodPointer = dictionaryPointer = IntPtr.Zero;

TypeSystemContext context = TypeSystemContextFactory.Create();

DefType targetType = (DefType)context.ResolveRuntimeTypeHandle(targetTypeHandle);
Instantiation methodInstantiation = context.ResolveRuntimeTypeHandles(genericMethodArgumentHandles);
InstantiatedMethod method = (InstantiatedMethod)context.ResolveGenericMethodInstantiation(false, targetType, nameAndSignature, methodInstantiation, IntPtr.Zero, false);

if (!method.CanShareNormalGenericCode())
{
// First see if we can find an exact method implementation for the GVM (avoid using USG implementations if we can,
// because USG code is much slower).
if (TryLookupExactMethodPointerForComponents(targetTypeHandle, nameAndSignature, genericMethodArgumentHandles, out methodPointer))
if (TryLookupExactMethodPointer(method, out methodPointer))
{
Debug.Assert(methodPointer != IntPtr.Zero);
TypeSystemContextFactory.Recycle(context);
dictionaryPointer = IntPtr.Zero;
return true;
}
}

// If we cannot find an exact method entry point, look for an equivalent template and compute the generic dictinoary
NativeLayoutInfo nativeLayoutInfo = new NativeLayoutInfo();
InstantiatedMethod templateMethod = TemplateLocator.TryGetGenericMethodTemplate(method, out nativeLayoutInfo.Module, out nativeLayoutInfo.Offset);
// If we cannot find an exact method entry point, look for an equivalent template and compute the generic dictionary
InstantiatedMethod templateMethod = TemplateLocator.TryGetGenericMethodTemplate(method, out _, out _);
if (templateMethod == null)
{
methodPointer = default;
dictionaryPointer = default;
return false;
}

methodPointer = templateMethod.IsCanonicalMethod(CanonicalFormKind.Universal) ?
templateMethod.UsgFunctionPointer :
templateMethod.FunctionPointer;

if (!TryLookupGenericMethodDictionaryForComponents(targetTypeHandle, nameAndSignature, genericMethodArgumentHandles, out dictionaryPointer))
if (!TryLookupGenericMethodDictionary(new MethodDescBasedGenericMethodLookup(method), out dictionaryPointer))
{
using (LockHolder.Hold(_typeLoaderLock))
{
// Now that we hold the lock, we may find that existing types can now find
// their associated RuntimeTypeHandle. Flush the type builder states as a way
// to force the reresolution of RuntimeTypeHandles which couldn't be found before.
context.FlushTypeBuilderStates();
method.Context.FlushTypeBuilderStates();

if (!TypeBuilder.TryBuildGenericMethod(method, out dictionaryPointer))
{
return false;
}
}
}

Debug.Assert(methodPointer != IntPtr.Zero && dictionaryPointer != IntPtr.Zero);

TypeSystemContextFactory.Recycle(context);
return true;
}

#region Privates
private bool TryGetDynamicGenericMethodDictionaryForComponents(GenericMethodLookupData lookupData, out IntPtr result)
private bool TryGetDynamicGenericMethodDictionary(GenericMethodLookupData lookupData, out IntPtr result)
{
result = IntPtr.Zero;

Expand All @@ -403,7 +297,7 @@ private bool TryGetDynamicGenericMethodDictionaryForComponents(GenericMethodLook
return true;
}
}
private static bool TryGetStaticGenericMethodDictionaryForComponents(GenericMethodLookupData lookupData, out IntPtr result)
private static bool TryGetStaticGenericMethodDictionary(GenericMethodLookupData lookupData, out IntPtr result)
{
// Search the hashtable for a generic instantiation match

Expand Down
Loading

0 comments on commit b81b822

Please sign in to comment.