Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 48 additions & 6 deletions docs/design/datacontracts/RuntimeTypeSystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ partial interface IRuntimeTypeSystem : IContract
// return true if the TypeHandle represents an array, and set the rank to either 0 (if the type is not an array), or the rank number if it is.
bool IsArray(TypeHandle typeHandle, out uint rank);
TypeHandle GetTypeParam(TypeHandle typeHandle);
TypeHandle GetConstructedType(TypeHandle typeHandle, CorElementType corElementType, int rank, ImmutableArray<TypeHandle> typeArguments);
TypeHandle GetConstructedType(TypeHandle typeHandle, CorElementType corElementType, int rank, ImmutableArray<TypeHandle> typeArguments, byte callConv = 0);
TypeHandle GetPrimitiveType(CorElementType typeCode);
bool IsGenericVariable(TypeHandle typeHandle, out TargetPointer module, out uint token);
bool IsFunctionPointer(TypeHandle typeHandle, out ReadOnlySpan<TypeHandle> retAndArgTypes, out byte callConv);
Expand Down Expand Up @@ -501,6 +501,8 @@ The contract additionally depends on these data descriptors
| `FnPtrTypeDesc` | `RetAndArgTypes` | Pointer to an array of TypeHandle addresses. This length of this is 1 more than `NumArgs` |
| `GenericsDictInfo` | `NumDicts` | Number of instantiation dictionaries, including inherited ones, in this `GenericsDictInfo` |
| `GenericsDictInfo` | `NumTypeArgs` | Number of type arguments in the type or method instantiation described by this `GenericsDictInfo` |
| `LoaderAllocator` | `IsCollectible` | Non-zero if the `LoaderAllocator` is collectible. |
| `LoaderAllocator` | `CreationNumber` | Monotonically-increasing creation number assigned to each collectible `LoaderAllocator`. |

The value of the `NativeCodeVersionNode::OptimizationTier` field is one of:
```csharp
Expand Down Expand Up @@ -1003,6 +1005,22 @@ Contracts used:

}

private bool FnPtrMatch(TypeHandle candidate, ImmutableArray<TypeHandle> retAndArgTypes, byte callConv)
{
if (!IsFunctionPointer(candidate, out ReadOnlySpan<TypeHandle> candidateRetAndArgs, out byte candidateCallConv))
return false;
if (candidateCallConv != callConv)
return false;
if (candidateRetAndArgs.Length != retAndArgTypes.Length)
return false;
for (int i = 0; i < candidateRetAndArgs.Length; i++)
{
if (candidateRetAndArgs[i].Address != retAndArgTypes[i].Address)
return false;
}
return true;
}

private bool IsLoaded(TypeHandle typeHandle)
{
if (typeHandle.Address == TargetPointer.Null)
Expand All @@ -1018,12 +1036,19 @@ Contracts used:
return (flags & (uint)MethodTableAuxiliaryFlags.IsNotFullyLoaded) == 0;
}

TypeHandle GetConstructedType(TypeHandle typeHandle, CorElementType corElementType, int rank, ImmutableArray<TypeHandle> typeArguments)
TypeHandle GetConstructedType(TypeHandle typeHandle, CorElementType corElementType, int rank, ImmutableArray<TypeHandle> typeArguments, byte callConv)
{
if (typeHandle.Address == TargetPointer.Null)
// For function pointers the "parent" type handle is unused. `callConv` discriminates
// overloads in the cache; for all other element types it is zero.
if (corElementType != CorElementType.FnPtr && typeHandle.Address == TargetPointer.Null)
return new TypeHandle(TargetPointer.Null);
ILoader loaderContract = _target.Contracts.Loader;
TargetPointer loaderModule = GetLoaderModule(typeHandle);
// Function pointer types are not owned by the loader module of any single type argument,
// so they need their own loader-module selection. Every other constructed type is owned
// by the loader module of its "parent" type handle.
TargetPointer loaderModule = corElementType == CorElementType.FnPtr
? FindFnPtrLoaderModule(typeArguments)
: GetLoaderModule(typeHandle);
ModuleHandle moduleHandle = loaderContract.GetModuleHandleFromModulePtr(loaderModule);
TypeHandle potentialMatch = new TypeHandle(TargetPointer.Null);
foreach (TargetPointer ptr in loaderContract.GetAvailableTypeParams(moduleHandle))
Expand All @@ -1033,19 +1058,36 @@ Contracts used:
{
if (GenericInstantiationMatch(typeHandle, potentialMatch, typeArguments) && IsLoaded(potentialMatch))
{
_ = _typeHandles.TryAdd(new TypeKey(typeHandle, corElementType, rank, typeArguments), potentialMatch);
_ = _typeHandles.TryAdd(new TypeKey(typeHandle, corElementType, rank, typeArguments, callConv), potentialMatch);
return potentialMatch;
}
}
else if (corElementType == CorElementType.FnPtr)
{
if (FnPtrMatch(potentialMatch, typeArguments, callConv) && IsLoaded(potentialMatch))
{
_ = _typeHandles.TryAdd(new TypeKey(typeHandle, corElementType, rank, typeArguments, callConv), potentialMatch);
return potentialMatch;
}
}
else if (ArrayPtrMatch(typeHandle, corElementType, rank, potentialMatch) && IsLoaded(potentialMatch))
{
_ = _typeHandles.TryAdd(new TypeKey(typeHandle, corElementType, rank, typeArguments), potentialMatch);
_ = _typeHandles.TryAdd(new TypeKey(typeHandle, corElementType, rank, typeArguments, callConv), potentialMatch);
return potentialMatch;
}
}
return new TypeHandle(TargetPointer.Null);
}

// Mirrors `ClassLoader::ComputeLoaderModuleForFunctionPointer`.
private TargetPointer FindFnPtrLoaderModule(ImmutableArray<TypeHandle> retAndArgTypes)
{
// The loader module of a
// function pointer type is the loader module of the collectible ret/arg type with the
// largest `LoaderAllocator::CreationNumber`, or CoreLib's loader module if none of the
// ret/arg types are collectible.
}

public TypeHandle GetPrimitiveType(CorElementType typeCode)
{
TargetPointer coreLib = _target.ReadGlobalPointer("CoreLib");
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/vm/datadescriptor/datadescriptor.inc
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,8 @@ CDAC_TYPE_FIELD(LoaderAllocator, T_POINTER, DynamicHelpersStubHeap, cdac_data<Lo
#endif // defined(FEATURE_READYTORUN) && defined(FEATURE_STUBPRECODE_DYNAMIC_HELPERS)
CDAC_TYPE_FIELD(LoaderAllocator, T_POINTER, VirtualCallStubManager, cdac_data<LoaderAllocator>::VirtualCallStubManager)
CDAC_TYPE_FIELD(LoaderAllocator, TYPE(ObjectHandle), ObjectHandle, cdac_data<LoaderAllocator>::ObjectHandle)
CDAC_TYPE_FIELD(LoaderAllocator, T_UINT8, IsCollectible, cdac_data<LoaderAllocator>::IsCollectible)
CDAC_TYPE_FIELD(LoaderAllocator, T_UINT64, CreationNumber, cdac_data<LoaderAllocator>::CreationNumber)
CDAC_TYPE_END(LoaderAllocator)

CDAC_TYPE_BEGIN(LoaderHeap)
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/vm/loaderallocator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -963,6 +963,8 @@ struct cdac_data<LoaderAllocator>
#endif // defined(FEATURE_READYTORUN) && defined(FEATURE_STUBPRECODE_DYNAMIC_HELPERS)
static constexpr size_t VirtualCallStubManager = offsetof(LoaderAllocator, m_pVirtualCallStubManager);
static constexpr size_t ObjectHandle = offsetof(LoaderAllocator, m_hLoaderAllocatorObjectHandle);
static constexpr size_t IsCollectible = offsetof(LoaderAllocator, m_IsCollectible);
static constexpr size_t CreationNumber = offsetof(LoaderAllocator, m_nLoaderAllocator);
};

typedef VPTR(LoaderAllocator) PTR_LoaderAllocator;
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/vm/typedesc.h
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,8 @@ class FnPtrTypeDesc : public TypeDesc
LIMITED_METHOD_CONTRACT;
SUPPORTS_DAC;

return PTR_TypeHandle(m_RetAndArgTypes);
return dac_cast<PTR_TypeHandle>(
PTR_HOST_MEMBER_TADDR(FnPtrTypeDesc, this, m_RetAndArgTypes));
}

BOOL IsSharedByGenericInstantiations();
Expand Down
7 changes: 0 additions & 7 deletions src/coreclr/vm/typehash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -433,8 +433,6 @@ BOOL EETypeHashTable::CompareFnPtrType(TypeHandle t, BYTE callConv, DWORD numArg
if (!t.IsFnPtrType())
return FALSE;

#ifndef DACCESS_COMPILE

FnPtrTypeDesc* pTD = t.AsFnPtrType();

if (pTD->GetNumArgs() != numArgs || pTD->GetCallConv() != callConv)
Expand All @@ -451,11 +449,6 @@ BOOL EETypeHashTable::CompareFnPtrType(TypeHandle t, BYTE callConv, DWORD numArg
}

return TRUE;

#else
DacNotImpl();
return FALSE;
#endif // #ifndef DACCESS_COMPILE
}

TypeHandle EETypeHashTable::GetValue(const TypeKey *pKey)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ public interface IRuntimeTypeSystem : IContract
// return true if the TypeHandle represents an array, and set the rank to either 0 (if the type is not an array), or the rank number if it is.
bool IsArray(TypeHandle typeHandle, out uint rank) => throw new NotImplementedException();
TypeHandle GetTypeParam(TypeHandle typeHandle) => throw new NotImplementedException();
TypeHandle GetConstructedType(TypeHandle typeHandle, CorElementType corElementType, int rank, ImmutableArray<TypeHandle> typeArguments) => throw new NotImplementedException();
TypeHandle GetConstructedType(TypeHandle typeHandle, CorElementType corElementType, int rank, ImmutableArray<TypeHandle> typeArguments, byte callConv = 0) => throw new NotImplementedException();
TypeHandle GetPrimitiveType(CorElementType typeCode) => throw new NotImplementedException();
TypeHandle GetTypeByNameAndModule(string name, string nameSpace, ModuleHandle moduleHandle) => throw new NotImplementedException();
Comment thread
rcj1 marked this conversation as resolved.
bool IsGenericVariable(TypeHandle typeHandle, out TargetPointer module, out uint token) => throw new NotImplementedException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,21 +73,23 @@ internal MethodTable(Data.MethodTable data)

private readonly struct TypeKey : IEquatable<TypeKey>
{
public TypeKey(TypeHandle typeHandle, CorElementType elementType, int rank, ImmutableArray<TypeHandle> typeArgs)
public TypeKey(TypeHandle typeHandle, CorElementType elementType, int rank, ImmutableArray<TypeHandle> typeArgs, byte callConv = 0)
{
TypeHandle = typeHandle;
ElementType = elementType;
Rank = rank;
TypeArgs = typeArgs;
CallConv = callConv;
}
public TypeHandle TypeHandle { get; }
public CorElementType ElementType { get; }
public int Rank { get; }
public ImmutableArray<TypeHandle> TypeArgs { get; }
public byte CallConv { get; }

public bool Equals(TypeKey other)
{
if (ElementType != other.ElementType || Rank != other.Rank || TypeArgs.Length != other.TypeArgs.Length)
if (ElementType != other.ElementType || Rank != other.Rank || CallConv != other.CallConv || TypeArgs.Length != other.TypeArgs.Length)
return false;
for (int i = 0; i < TypeArgs.Length; i++)
{
Expand All @@ -101,7 +103,7 @@ public bool Equals(TypeKey other)

public override int GetHashCode()
{
int hash = HashCode.Combine(TypeHandle.GetHashCode(), (int)ElementType, Rank);
int hash = HashCode.Combine(TypeHandle.GetHashCode(), (int)ElementType, Rank, (int)CallConv);
foreach (TypeHandle th in TypeArgs)
Comment on lines 90 to 107
{
hash = HashCode.Combine(hash, th.GetHashCode());
Expand Down Expand Up @@ -1038,6 +1040,22 @@ private bool ArrayPtrMatch(TypeHandle elementType, CorElementType corElementType

}

private bool FnPtrMatch(TypeHandle candidate, ImmutableArray<TypeHandle> retAndArgTypes, byte callConv)
{
if (!IsFunctionPointer(candidate, out ReadOnlySpan<TypeHandle> candidateRetAndArgs, out byte candidateCallConv))
return false;
if (candidateCallConv != callConv)
return false;
if (candidateRetAndArgs.Length != retAndArgTypes.Length)
return false;
for (int i = 0; i < candidateRetAndArgs.Length; i++)
{
if (candidateRetAndArgs[i].Address != retAndArgTypes[i].Address)
return false;
}
return true;
}

private bool IsLoaded(TypeHandle typeHandle)
{
if (typeHandle.Address == TargetPointer.Null)
Expand All @@ -1053,14 +1071,19 @@ private bool IsLoaded(TypeHandle typeHandle)
return (auxData.Flags & (uint)MethodTableAuxiliaryFlags.IsNotFullyLoaded) == 0; // IsUnloaded
}

TypeHandle IRuntimeTypeSystem.GetConstructedType(TypeHandle typeHandle, CorElementType corElementType, int rank, ImmutableArray<TypeHandle> typeArguments)
TypeHandle IRuntimeTypeSystem.GetConstructedType(TypeHandle typeHandle, CorElementType corElementType, int rank, ImmutableArray<TypeHandle> typeArguments, byte callConv)
{
if (typeHandle.Address == TargetPointer.Null)
if (typeHandle.Address == TargetPointer.Null && corElementType != CorElementType.FnPtr)
return new TypeHandle(TargetPointer.Null);
if (_typeHandles.TryGetValue(new TypeKey(typeHandle, corElementType, rank, typeArguments), out TypeHandle existing))
if (_typeHandles.TryGetValue(new TypeKey(typeHandle, corElementType, rank, typeArguments, callConv), out TypeHandle existing))
return existing;
ILoader loaderContract = _target.Contracts.Loader;
TargetPointer loaderModule = GetLoaderModule(typeHandle);
TargetPointer loaderModule;
if (corElementType == CorElementType.FnPtr)
loaderModule = FindFnPtrLoaderModule(typeArguments);
else
loaderModule = GetLoaderModule(typeHandle);

ModuleHandle moduleHandle = loaderContract.GetModuleHandleFromModulePtr(loaderModule);
TypeHandle potentialMatch;
foreach (TargetPointer ptr in loaderContract.GetAvailableTypeParams(moduleHandle))
Expand All @@ -1074,6 +1097,14 @@ TypeHandle IRuntimeTypeSystem.GetConstructedType(TypeHandle typeHandle, CorEleme
return potentialMatch;
}
}
else if (corElementType == CorElementType.FnPtr)
{
if (FnPtrMatch(potentialMatch, typeArguments, callConv) && IsLoaded(potentialMatch))
{
_ = _typeHandles.TryAdd(new TypeKey(typeHandle, corElementType, rank, typeArguments, callConv), potentialMatch);
return potentialMatch;
}
}
else if (ArrayPtrMatch(typeHandle, corElementType, rank, potentialMatch) && IsLoaded(potentialMatch))
{
_ = _typeHandles.TryAdd(new TypeKey(typeHandle, corElementType, rank, typeArguments), potentialMatch);
Expand All @@ -1083,6 +1114,54 @@ TypeHandle IRuntimeTypeSystem.GetConstructedType(TypeHandle typeHandle, CorEleme
return new TypeHandle(TargetPointer.Null);
}

// Mirrors the native algorithm
// ClassLoader::ComputeLoaderModuleForFunctionPointer: the loader module is the collectible
// ret/arg type's loader module with the largest LoaderAllocator creation number, or CoreLib's
// loader module if none of the ret/arg types are collectible.
private TargetPointer FindFnPtrLoaderModule(ImmutableArray<TypeHandle> retAndArgTypes)
{
ILoader loaderContract = _target.Contracts.Loader;

TargetPointer loaderModulePtr = TargetPointer.Null;
ulong latestFoundNumber = 0;
bool anyCollectible = false;

foreach (TypeHandle arg in retAndArgTypes)
{
if (arg.Address == TargetPointer.Null)
continue;

TargetPointer argModulePtr = GetLoaderModule(arg);
if (argModulePtr == TargetPointer.Null)
continue;

ModuleHandle argModuleHandle = loaderContract.GetModuleHandleFromModulePtr(argModulePtr);
TargetPointer argLoaderAllocator = loaderContract.GetLoaderAllocator(argModuleHandle);
if (argLoaderAllocator == TargetPointer.Null)
continue;

Data.LoaderAllocator loaderAllocator = _target.ProcessedData.GetOrAdd<Data.LoaderAllocator>(argLoaderAllocator);
if (!loaderAllocator.IsCollectible)
continue;

if (!anyCollectible || loaderAllocator.CreationNumber > latestFoundNumber)
{
anyCollectible = true;
latestFoundNumber = loaderAllocator.CreationNumber;
loaderModulePtr = argModulePtr;
}
}

if (!anyCollectible)
{
TargetPointer systemAssembly = loaderContract.GetSystemAssembly();
ModuleHandle coreLibModuleHandle = loaderContract.GetModuleHandleFromAssemblyPtr(systemAssembly);
loaderModulePtr = loaderContract.GetModule(coreLibModuleHandle);
}

return loaderModulePtr;
}
Comment on lines +1117 to +1163

TypeHandle IRuntimeTypeSystem.GetPrimitiveType(CorElementType typeCode)
{
TargetPointer coreLib = _target.ReadGlobalPointer(Constants.Globals.CoreLib);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ public LoaderAllocator(Target target, TargetPointer address)
VirtualCallStubManager = target.ReadPointerField(address, type, nameof(VirtualCallStubManager));

ObjectHandle = target.ReadDataField<ObjectHandle>(address, type, nameof(ObjectHandle));

IsCollectible = target.ReadField<byte>(address, type, nameof(IsCollectible)) != 0;
CreationNumber = target.ReadField<ulong>(address, type, nameof(CreationNumber));
}

public uint ReferenceCount { get; init; }
Expand All @@ -42,6 +45,8 @@ public LoaderAllocator(Target target, TargetPointer address)
public TargetPointer? DynamicHelpersStubHeap { get; init; }
public TargetPointer VirtualCallStubManager { get; init; }
public ObjectHandle ObjectHandle { get; init; }
public bool IsCollectible { get; init; }
public ulong CreationNumber { get; init; }

public bool IsAlive => ReferenceCount != 0;
}
Loading
Loading