Skip to content

Commit

Permalink
Add function pointer type support to type loader (#85287)
Browse files Browse the repository at this point in the history
So that we can create new function pointer types at runtime within the context of `MakeGenericXXX`.
  • Loading branch information
MichalStrehovsky authored Apr 25, 2023
1 parent 4e2228e commit 19d545c
Show file tree
Hide file tree
Showing 13 changed files with 352 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1568,6 +1568,7 @@ internal static uint GetSizeofEEType(
bool fRequiresOptionalFields,
bool fHasSealedVirtuals,
bool fHasGenericInfo,
int cFunctionPointerTypeParameters,
bool fHasNonGcStatics,
bool fHasGcStatics,
bool fHasThreadStatics)
Expand All @@ -1580,6 +1581,7 @@ internal static uint GetSizeofEEType(
(fHasFinalizer ? sizeof(UIntPtr) : 0) +
(fRequiresOptionalFields ? sizeof(IntPtr) : 0) +
(fHasSealedVirtuals ? sizeof(IntPtr) : 0) +
cFunctionPointerTypeParameters * sizeof(IntPtr) +
(fHasGenericInfo ? sizeof(IntPtr)*2 : 0) + // pointers to GenericDefinition and GenericComposition
(fHasNonGcStatics ? sizeof(IntPtr) : 0) + // pointer to data
(fHasGcStatics ? sizeof(IntPtr) : 0) + // pointer to data
Expand Down Expand Up @@ -1740,7 +1742,7 @@ public MethodTable* this[int index]
if (((nint)_pFirst & IsRelative) != 0)
return (((RelativePointer<MethodTable>*)((nint)_pFirst - IsRelative)) + index)->Value;

return (MethodTable*)_pFirst + index;
return *(MethodTable**)_pFirst + index;
}
#if TYPE_LOADER_IMPLEMENTATION
set
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/nativeaot/Runtime/inc/MethodTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ enum EETypeElementType : uint8_t
ElementType_SzArray = 0x18,
ElementType_ByRef = 0x19,
ElementType_Pointer = 0x1A,
ElementType_FunctionPointer = 0x1B,
};

//-------------------------------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo

flags |= (uint)EETypeFlags.IsDynamicTypeFlag;

int numFunctionPointerTypeParameters = 0;
if (state.TypeBeingBuilt.IsMdArray)
{
// If we're building an MDArray, the template is object[,] and we
Expand All @@ -197,6 +198,17 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo
2 * IntPtr.Size + // EETypePtr + Length
state.ArrayRank.Value * sizeof(int) * 2; // 2 ints per rank for bounds
}
else if (state.TypeBeingBuilt.IsFunctionPointer)
{
// Base size encodes number of parameters and calling convention
MethodSignature sig = ((FunctionPointerType)state.TypeBeingBuilt).Signature;
baseSize = (sig.Flags & MethodSignatureFlags.UnmanagedCallingConventionMask) switch
{
0 => sig.Length,
_ => sig.Length | unchecked((int)FunctionPointerFlags.IsUnmanaged),
};
numFunctionPointerTypeParameters = sig.Length;
}

// Optional fields encoding
int cbOptionalFieldsSize;
Expand Down Expand Up @@ -250,6 +262,7 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo
cbOptionalFieldsSize > 0,
(rareFlags & (int)EETypeRareFlags.HasSealedVTableEntriesFlag) != 0,
isGeneric,
numFunctionPointerTypeParameters,
allocatedNonGCDataSize != 0,
state.GcDataSize != 0,
state.ThreadDataSize != 0);
Expand Down Expand Up @@ -666,7 +679,7 @@ public static RuntimeTypeHandle CreateEEType(TypeDesc type, TypeBuilderState sta

MethodTable* pTemplateEEType;

if (type is PointerType || type is ByRefType)
if (type is PointerType || type is ByRefType || type is FunctionPointerType)
{
Debug.Assert(0 == state.NonGcDataSize);
Debug.Assert(false == state.HasStaticConstructor);
Expand All @@ -675,8 +688,17 @@ public static RuntimeTypeHandle CreateEEType(TypeDesc type, TypeBuilderState sta
Debug.Assert(IntPtr.Zero == state.GcStaticDesc);
Debug.Assert(IntPtr.Zero == state.ThreadStaticDesc);

// Pointers and ByRefs only differ by the ParameterizedTypeShape and ElementType value.
RuntimeTypeHandle templateTypeHandle = typeof(void*).TypeHandle;
RuntimeTypeHandle templateTypeHandle;
if (type is FunctionPointerType)
{
// There's still differences to paper over, but `delegate*<void>` is close enough.
templateTypeHandle = typeof(delegate*<void>).TypeHandle;
}
else
{
// Pointers and ByRefs only differ by the ParameterizedTypeShape and ElementType value.
templateTypeHandle = typeof(void*).TypeHandle;
}

pTemplateEEType = templateTypeHandle.ToEETypePtr();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ internal TypeDesc GetType(ref NativeParser parser)

case TypeSignatureKind.MultiDimArray:
{
DefType elementType = (DefType)GetType(ref parser);
TypeDesc elementType = GetType(ref parser);
int rank = (int)data;

// Skip encoded bounds and lobounds
Expand All @@ -150,9 +150,25 @@ internal TypeDesc GetType(ref NativeParser parser)
return _typeSystemContext.GetWellKnownType((WellKnownType)data);

case TypeSignatureKind.FunctionPointer:
Debug.Fail("NYI!");
NativeParser.ThrowBadImageFormatException();
return null;
{
var callConv = (MethodCallingConvention)parser.GetUnsigned();
Debug.Assert((callConv & MethodCallingConvention.Generic) == 0);

uint numParams = parser.GetUnsigned();

TypeDesc returnType = GetType(ref parser);
TypeDesc[] parameters = new TypeDesc[numParams];
for (uint i = 0; i < parameters.Length; i++)
parameters[i] = GetType(ref parser);

return _typeSystemContext.GetFunctionPointerType(
new MethodSignature(
(callConv & MethodCallingConvention.Unmanaged) != 0 ? MethodSignatureFlags.UnmanagedCallingConvention : 0,
0,
returnType,
parameters
));
}

default:
NativeParser.ThrowBadImageFormatException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ public void PrepareMethod(MethodDesc method)

private void InsertIntoNeedsTypeHandleList(TypeDesc type)
{
if ((type is DefType) || (type is ArrayType) || (type is PointerType) || (type is ByRefType))
if ((type is DefType) || (type is ArrayType) || (type is PointerType) || (type is ByRefType) || (type is FunctionPointerType))
{
_typesThatNeedTypeHandles.Add(type);
}
Expand Down Expand Up @@ -233,7 +233,7 @@ internal void PrepareType(TypeDesc type)

if (type is ArrayType typeAsArrayType)
{
if (typeAsArrayType.IsSzArray && !typeAsArrayType.ElementType.IsPointer)
if (typeAsArrayType.IsSzArray && !typeAsArrayType.ElementType.IsPointer && !typeAsArrayType.ElementType.IsFunctionPointer)
{
TypeDesc.ComputeTemplate(state);
Debug.Assert(state.TemplateType != null && state.TemplateType is ArrayType && !state.TemplateType.RuntimeTypeHandle.IsNull());
Expand All @@ -242,10 +242,16 @@ internal void PrepareType(TypeDesc type)
}
else
{
Debug.Assert(typeAsArrayType.IsMdArray || typeAsArrayType.ElementType.IsPointer);
Debug.Assert(typeAsArrayType.IsMdArray || typeAsArrayType.ElementType.IsPointer || typeAsArrayType.ElementType.IsFunctionPointer);
}
}
}
else if (type is FunctionPointerType functionPointerType)
{
RegisterForPreparation(functionPointerType.Signature.ReturnType);
foreach (TypeDesc paramType in functionPointerType.Signature)
RegisterForPreparation(paramType);
}
else
{
Debug.Assert(false);
Expand Down Expand Up @@ -590,7 +596,7 @@ private unsafe void AllocateRuntimeType(TypeDesc type)
{
TypeBuilderState state = type.GetTypeBuilderState();

Debug.Assert(type is DefType || type is ArrayType || type is PointerType || type is ByRefType);
Debug.Assert(type is DefType || type is ArrayType || type is PointerType || type is ByRefType || type is FunctionPointerType);

RuntimeTypeHandle rtt = EETypeCreator.CreateEEType(type, state);

Expand Down Expand Up @@ -843,6 +849,19 @@ private void FinishRuntimeType(TypeDesc type)
}
}
}
else if (type is FunctionPointerType)
{
MethodSignature sig = ((FunctionPointerType)type).Signature;
unsafe
{
MethodTable* halfBakedMethodTable = state.HalfBakedRuntimeTypeHandle.ToEETypePtr();
halfBakedMethodTable->FunctionPointerReturnType = GetRuntimeTypeHandle(sig.ReturnType).ToEETypePtr();
Debug.Assert(halfBakedMethodTable->NumFunctionPointerParameters == sig.Length);
MethodTableList paramList = halfBakedMethodTable->FunctionPointerParameters;
for (int i = 0; i < sig.Length; i++)
paramList[i] = GetRuntimeTypeHandle(sig[i]).ToEETypePtr();
}
}
else
{
Debug.Assert(false);
Expand Down Expand Up @@ -942,24 +961,25 @@ private void FinishTypeAndMethodBuilding()
int newArrayTypesCount = 0;
int newPointerTypesCount = 0;
int newByRefTypesCount = 0;
int newFunctionPointerTypesCount = 0;
int[] mdArrayNewTypesCount = null;

for (int i = 0; i < _typesThatNeedTypeHandles.Count; i++)
{
ParameterizedType typeAsParameterizedType = _typesThatNeedTypeHandles[i] as ParameterizedType;
if (typeAsParameterizedType == null)
continue;
TypeDesc type = _typesThatNeedTypeHandles[i];

if (typeAsParameterizedType.IsSzArray)
if (type.IsSzArray)
newArrayTypesCount++;
else if (typeAsParameterizedType.IsPointer)
else if (type.IsPointer)
newPointerTypesCount++;
else if (typeAsParameterizedType.IsByRef)
else if (type.IsFunctionPointer)
newFunctionPointerTypesCount++;
else if (type.IsByRef)
newByRefTypesCount++;
else if (typeAsParameterizedType.IsMdArray)
else if (type.IsMdArray)
{
mdArrayNewTypesCount ??= new int[MDArray.MaxRank + 1];
mdArrayNewTypesCount[((ArrayType)typeAsParameterizedType).Rank]++;
mdArrayNewTypesCount[((ArrayType)type).Rank]++;
}
}
// Reserve space in array/pointer cache's so that the actual adding can be fault-free.
Expand All @@ -981,6 +1001,7 @@ private void FinishTypeAndMethodBuilding()

TypeSystemContext.PointerTypesCache.Reserve(TypeSystemContext.PointerTypesCache.Count + newPointerTypesCount);
TypeSystemContext.ByRefTypesCache.Reserve(TypeSystemContext.ByRefTypesCache.Count + newByRefTypesCount);
TypeSystemContext.FunctionPointerTypesCache.Reserve(TypeSystemContext.FunctionPointerTypesCache.Count + newFunctionPointerTypesCount);

// Finally, register all generic types and methods atomically with the runtime
RegisterGenericTypesAndMethods();
Expand All @@ -1001,7 +1022,8 @@ private void FinishTypeAndMethodBuilding()
{
if (_typesThatNeedTypeHandles[i] is FunctionPointerType typeAsFunctionPointerType)
{
throw new NotImplementedException();
Debug.Assert(!typeAsFunctionPointerType.RuntimeTypeHandle.IsNull());
TypeSystemContext.FunctionPointerTypesCache.AddOrGetExisting(typeAsFunctionPointerType.RuntimeTypeHandle);
}
continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ public TypeDesc TemplateType

// Arrays of pointers don't implement generic interfaces and are special cases. They use
// typeof(char*[]) as their template.
if (TypeBeingBuilt.IsSzArray && ((ArrayType)TypeBeingBuilt).ElementType.IsPointer)
if (TypeBeingBuilt.IsSzArray && ((ArrayType)TypeBeingBuilt).ElementType is TypeDesc elementType &&
(elementType.IsPointer || elementType.IsFunctionPointer))
{
_templateType = TypeBeingBuilt.Context.ResolveRuntimeTypeHandle(typeof(char*[]).TypeHandle);
_templateTypeLoaderNativeLayout = false;
Expand Down Expand Up @@ -252,12 +253,13 @@ private ushort ComputeNumVTableSlots()
// Template type loader case
unsafe
{
if (TypeBeingBuilt.IsPointer || TypeBeingBuilt.IsByRef)
if (TypeBeingBuilt.IsPointer || TypeBeingBuilt.IsByRef || TypeBeingBuilt.IsFunctionPointer)
{
// Pointers and byrefs don't have vtable slots
return 0;
}
if (TypeBeingBuilt.IsMdArray || (TypeBeingBuilt.IsSzArray && ((ArrayType)TypeBeingBuilt).ElementType.IsPointer))
if (TypeBeingBuilt.IsMdArray || (TypeBeingBuilt.IsSzArray && ((ArrayType)TypeBeingBuilt).ElementType is TypeDesc elementType
&& (elementType.IsPointer || elementType.IsFunctionPointer)))
{
// MDArray types and pointer arrays have the same vtable as the System.Array type they "derive" from.
// They do not implement the generic interfaces that make this interesting for normal arrays.
Expand Down Expand Up @@ -355,7 +357,8 @@ public LowLevelList<bool> InstanceGCLayout
Debug.Assert(TypeBeingBuilt.RetrieveRuntimeTypeHandleIfPossible() ||
TypeBeingBuilt.IsTemplateCanonical() ||
(TypeBeingBuilt is PointerType) ||
(TypeBeingBuilt is ByRefType));
(TypeBeingBuilt is ByRefType) ||
(TypeBeingBuilt is FunctionPointerType));
_instanceGCLayout = s_emptyLayout;
}
}
Expand Down Expand Up @@ -458,7 +461,7 @@ public bool IsArrayOfReferenceTypes
{
ArrayType typeAsArrayType = TypeBeingBuilt as ArrayType;
if (typeAsArrayType != null)
return !typeAsArrayType.ParameterType.IsValueType && !typeAsArrayType.ParameterType.IsPointer;
return !typeAsArrayType.ParameterType.IsValueType && !typeAsArrayType.ParameterType.IsPointer && !typeAsArrayType.ParameterType.IsFunctionPointer;
else
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,37 @@ internal bool RetrieveRuntimeTypeHandleIfPossible()
{
// SignatureVariables do not have RuntimeTypeHandles
}
else if (type is FunctionPointerType functionPointerType)
{
MethodSignature sig = functionPointerType.Signature;
if (sig.ReturnType.RetrieveRuntimeTypeHandleIfPossible())
{
RuntimeTypeHandle[] parameterHandles = new RuntimeTypeHandle[sig.Length];
bool handlesAvailable = true;
for (int i = 0; i < parameterHandles.Length; i++)
{
if (sig[i].RetrieveRuntimeTypeHandleIfPossible())
{
parameterHandles[i] = sig[i].RuntimeTypeHandle;
}
else
{
handlesAvailable = false;
break;
}
}

if (handlesAvailable
&& TypeLoaderEnvironment.Instance.TryLookupFunctionPointerTypeForComponents(
sig.ReturnType.RuntimeTypeHandle, parameterHandles,
isUnmanaged: (sig.Flags & MethodSignatureFlags.UnmanagedCallingConventionMask) != 0,
out RuntimeTypeHandle rtth))
{
functionPointerType.SetRuntimeTypeHandleUnsafe(rtth);
return true;
}
}
}
else
{
Debug.Assert(false);
Expand Down
Loading

0 comments on commit 19d545c

Please sign in to comment.