Skip to content

Commit 19d545c

Browse files
Add function pointer type support to type loader (#85287)
So that we can create new function pointer types at runtime within the context of `MakeGenericXXX`.
1 parent 4e2228e commit 19d545c

File tree

13 files changed

+352
-34
lines changed

13 files changed

+352
-34
lines changed

src/coreclr/nativeaot/Common/src/Internal/Runtime/MethodTable.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1568,6 +1568,7 @@ internal static uint GetSizeofEEType(
15681568
bool fRequiresOptionalFields,
15691569
bool fHasSealedVirtuals,
15701570
bool fHasGenericInfo,
1571+
int cFunctionPointerTypeParameters,
15711572
bool fHasNonGcStatics,
15721573
bool fHasGcStatics,
15731574
bool fHasThreadStatics)
@@ -1580,6 +1581,7 @@ internal static uint GetSizeofEEType(
15801581
(fHasFinalizer ? sizeof(UIntPtr) : 0) +
15811582
(fRequiresOptionalFields ? sizeof(IntPtr) : 0) +
15821583
(fHasSealedVirtuals ? sizeof(IntPtr) : 0) +
1584+
cFunctionPointerTypeParameters * sizeof(IntPtr) +
15831585
(fHasGenericInfo ? sizeof(IntPtr)*2 : 0) + // pointers to GenericDefinition and GenericComposition
15841586
(fHasNonGcStatics ? sizeof(IntPtr) : 0) + // pointer to data
15851587
(fHasGcStatics ? sizeof(IntPtr) : 0) + // pointer to data
@@ -1740,7 +1742,7 @@ public MethodTable* this[int index]
17401742
if (((nint)_pFirst & IsRelative) != 0)
17411743
return (((RelativePointer<MethodTable>*)((nint)_pFirst - IsRelative)) + index)->Value;
17421744

1743-
return (MethodTable*)_pFirst + index;
1745+
return *(MethodTable**)_pFirst + index;
17441746
}
17451747
#if TYPE_LOADER_IMPLEMENTATION
17461748
set

src/coreclr/nativeaot/Runtime/inc/MethodTable.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ enum EETypeElementType : uint8_t
7575
ElementType_SzArray = 0x18,
7676
ElementType_ByRef = 0x19,
7777
ElementType_Pointer = 0x1A,
78+
ElementType_FunctionPointer = 0x1B,
7879
};
7980

8081
//-------------------------------------------------------------------------------------------------

src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/EETypeCreator.cs

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo
189189

190190
flags |= (uint)EETypeFlags.IsDynamicTypeFlag;
191191

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

201213
// Optional fields encoding
202214
int cbOptionalFieldsSize;
@@ -250,6 +262,7 @@ private static void CreateEETypeWorker(MethodTable* pTemplateEEType, uint hashCo
250262
cbOptionalFieldsSize > 0,
251263
(rareFlags & (int)EETypeRareFlags.HasSealedVTableEntriesFlag) != 0,
252264
isGeneric,
265+
numFunctionPointerTypeParameters,
253266
allocatedNonGCDataSize != 0,
254267
state.GcDataSize != 0,
255268
state.ThreadDataSize != 0);
@@ -666,7 +679,7 @@ public static RuntimeTypeHandle CreateEEType(TypeDesc type, TypeBuilderState sta
666679

667680
MethodTable* pTemplateEEType;
668681

669-
if (type is PointerType || type is ByRefType)
682+
if (type is PointerType || type is ByRefType || type is FunctionPointerType)
670683
{
671684
Debug.Assert(0 == state.NonGcDataSize);
672685
Debug.Assert(false == state.HasStaticConstructor);
@@ -675,8 +688,17 @@ public static RuntimeTypeHandle CreateEEType(TypeDesc type, TypeBuilderState sta
675688
Debug.Assert(IntPtr.Zero == state.GcStaticDesc);
676689
Debug.Assert(IntPtr.Zero == state.ThreadStaticDesc);
677690

678-
// Pointers and ByRefs only differ by the ParameterizedTypeShape and ElementType value.
679-
RuntimeTypeHandle templateTypeHandle = typeof(void*).TypeHandle;
691+
RuntimeTypeHandle templateTypeHandle;
692+
if (type is FunctionPointerType)
693+
{
694+
// There's still differences to paper over, but `delegate*<void>` is close enough.
695+
templateTypeHandle = typeof(delegate*<void>).TypeHandle;
696+
}
697+
else
698+
{
699+
// Pointers and ByRefs only differ by the ParameterizedTypeShape and ElementType value.
700+
templateTypeHandle = typeof(void*).TypeHandle;
701+
}
680702

681703
pTemplateEEType = templateTypeHandle.ToEETypePtr();
682704
}

src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/NativeLayoutInfoLoadContext.cs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ internal TypeDesc GetType(ref NativeParser parser)
125125

126126
case TypeSignatureKind.MultiDimArray:
127127
{
128-
DefType elementType = (DefType)GetType(ref parser);
128+
TypeDesc elementType = GetType(ref parser);
129129
int rank = (int)data;
130130

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

152152
case TypeSignatureKind.FunctionPointer:
153-
Debug.Fail("NYI!");
154-
NativeParser.ThrowBadImageFormatException();
155-
return null;
153+
{
154+
var callConv = (MethodCallingConvention)parser.GetUnsigned();
155+
Debug.Assert((callConv & MethodCallingConvention.Generic) == 0);
156+
157+
uint numParams = parser.GetUnsigned();
158+
159+
TypeDesc returnType = GetType(ref parser);
160+
TypeDesc[] parameters = new TypeDesc[numParams];
161+
for (uint i = 0; i < parameters.Length; i++)
162+
parameters[i] = GetType(ref parser);
163+
164+
return _typeSystemContext.GetFunctionPointerType(
165+
new MethodSignature(
166+
(callConv & MethodCallingConvention.Unmanaged) != 0 ? MethodSignatureFlags.UnmanagedCallingConvention : 0,
167+
0,
168+
returnType,
169+
parameters
170+
));
171+
}
156172

157173
default:
158174
NativeParser.ThrowBadImageFormatException();

src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilder.cs

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ public void PrepareMethod(MethodDesc method)
159159

160160
private void InsertIntoNeedsTypeHandleList(TypeDesc type)
161161
{
162-
if ((type is DefType) || (type is ArrayType) || (type is PointerType) || (type is ByRefType))
162+
if ((type is DefType) || (type is ArrayType) || (type is PointerType) || (type is ByRefType) || (type is FunctionPointerType))
163163
{
164164
_typesThatNeedTypeHandles.Add(type);
165165
}
@@ -233,7 +233,7 @@ internal void PrepareType(TypeDesc type)
233233

234234
if (type is ArrayType typeAsArrayType)
235235
{
236-
if (typeAsArrayType.IsSzArray && !typeAsArrayType.ElementType.IsPointer)
236+
if (typeAsArrayType.IsSzArray && !typeAsArrayType.ElementType.IsPointer && !typeAsArrayType.ElementType.IsFunctionPointer)
237237
{
238238
TypeDesc.ComputeTemplate(state);
239239
Debug.Assert(state.TemplateType != null && state.TemplateType is ArrayType && !state.TemplateType.RuntimeTypeHandle.IsNull());
@@ -242,10 +242,16 @@ internal void PrepareType(TypeDesc type)
242242
}
243243
else
244244
{
245-
Debug.Assert(typeAsArrayType.IsMdArray || typeAsArrayType.ElementType.IsPointer);
245+
Debug.Assert(typeAsArrayType.IsMdArray || typeAsArrayType.ElementType.IsPointer || typeAsArrayType.ElementType.IsFunctionPointer);
246246
}
247247
}
248248
}
249+
else if (type is FunctionPointerType functionPointerType)
250+
{
251+
RegisterForPreparation(functionPointerType.Signature.ReturnType);
252+
foreach (TypeDesc paramType in functionPointerType.Signature)
253+
RegisterForPreparation(paramType);
254+
}
249255
else
250256
{
251257
Debug.Assert(false);
@@ -590,7 +596,7 @@ private unsafe void AllocateRuntimeType(TypeDesc type)
590596
{
591597
TypeBuilderState state = type.GetTypeBuilderState();
592598

593-
Debug.Assert(type is DefType || type is ArrayType || type is PointerType || type is ByRefType);
599+
Debug.Assert(type is DefType || type is ArrayType || type is PointerType || type is ByRefType || type is FunctionPointerType);
594600

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

@@ -843,6 +849,19 @@ private void FinishRuntimeType(TypeDesc type)
843849
}
844850
}
845851
}
852+
else if (type is FunctionPointerType)
853+
{
854+
MethodSignature sig = ((FunctionPointerType)type).Signature;
855+
unsafe
856+
{
857+
MethodTable* halfBakedMethodTable = state.HalfBakedRuntimeTypeHandle.ToEETypePtr();
858+
halfBakedMethodTable->FunctionPointerReturnType = GetRuntimeTypeHandle(sig.ReturnType).ToEETypePtr();
859+
Debug.Assert(halfBakedMethodTable->NumFunctionPointerParameters == sig.Length);
860+
MethodTableList paramList = halfBakedMethodTable->FunctionPointerParameters;
861+
for (int i = 0; i < sig.Length; i++)
862+
paramList[i] = GetRuntimeTypeHandle(sig[i]).ToEETypePtr();
863+
}
864+
}
846865
else
847866
{
848867
Debug.Assert(false);
@@ -942,24 +961,25 @@ private void FinishTypeAndMethodBuilding()
942961
int newArrayTypesCount = 0;
943962
int newPointerTypesCount = 0;
944963
int newByRefTypesCount = 0;
964+
int newFunctionPointerTypesCount = 0;
945965
int[] mdArrayNewTypesCount = null;
946966

947967
for (int i = 0; i < _typesThatNeedTypeHandles.Count; i++)
948968
{
949-
ParameterizedType typeAsParameterizedType = _typesThatNeedTypeHandles[i] as ParameterizedType;
950-
if (typeAsParameterizedType == null)
951-
continue;
969+
TypeDesc type = _typesThatNeedTypeHandles[i];
952970

953-
if (typeAsParameterizedType.IsSzArray)
971+
if (type.IsSzArray)
954972
newArrayTypesCount++;
955-
else if (typeAsParameterizedType.IsPointer)
973+
else if (type.IsPointer)
956974
newPointerTypesCount++;
957-
else if (typeAsParameterizedType.IsByRef)
975+
else if (type.IsFunctionPointer)
976+
newFunctionPointerTypesCount++;
977+
else if (type.IsByRef)
958978
newByRefTypesCount++;
959-
else if (typeAsParameterizedType.IsMdArray)
979+
else if (type.IsMdArray)
960980
{
961981
mdArrayNewTypesCount ??= new int[MDArray.MaxRank + 1];
962-
mdArrayNewTypesCount[((ArrayType)typeAsParameterizedType).Rank]++;
982+
mdArrayNewTypesCount[((ArrayType)type).Rank]++;
963983
}
964984
}
965985
// Reserve space in array/pointer cache's so that the actual adding can be fault-free.
@@ -981,6 +1001,7 @@ private void FinishTypeAndMethodBuilding()
9811001

9821002
TypeSystemContext.PointerTypesCache.Reserve(TypeSystemContext.PointerTypesCache.Count + newPointerTypesCount);
9831003
TypeSystemContext.ByRefTypesCache.Reserve(TypeSystemContext.ByRefTypesCache.Count + newByRefTypesCount);
1004+
TypeSystemContext.FunctionPointerTypesCache.Reserve(TypeSystemContext.FunctionPointerTypesCache.Count + newFunctionPointerTypesCount);
9841005

9851006
// Finally, register all generic types and methods atomically with the runtime
9861007
RegisterGenericTypesAndMethods();
@@ -1001,7 +1022,8 @@ private void FinishTypeAndMethodBuilding()
10011022
{
10021023
if (_typesThatNeedTypeHandles[i] is FunctionPointerType typeAsFunctionPointerType)
10031024
{
1004-
throw new NotImplementedException();
1025+
Debug.Assert(!typeAsFunctionPointerType.RuntimeTypeHandle.IsNull());
1026+
TypeSystemContext.FunctionPointerTypesCache.AddOrGetExisting(typeAsFunctionPointerType.RuntimeTypeHandle);
10051027
}
10061028
continue;
10071029
}

src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/Runtime/TypeLoader/TypeBuilderState.cs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ public TypeDesc TemplateType
6969

7070
// Arrays of pointers don't implement generic interfaces and are special cases. They use
7171
// typeof(char*[]) as their template.
72-
if (TypeBeingBuilt.IsSzArray && ((ArrayType)TypeBeingBuilt).ElementType.IsPointer)
72+
if (TypeBeingBuilt.IsSzArray && ((ArrayType)TypeBeingBuilt).ElementType is TypeDesc elementType &&
73+
(elementType.IsPointer || elementType.IsFunctionPointer))
7374
{
7475
_templateType = TypeBeingBuilt.Context.ResolveRuntimeTypeHandle(typeof(char*[]).TypeHandle);
7576
_templateTypeLoaderNativeLayout = false;
@@ -252,12 +253,13 @@ private ushort ComputeNumVTableSlots()
252253
// Template type loader case
253254
unsafe
254255
{
255-
if (TypeBeingBuilt.IsPointer || TypeBeingBuilt.IsByRef)
256+
if (TypeBeingBuilt.IsPointer || TypeBeingBuilt.IsByRef || TypeBeingBuilt.IsFunctionPointer)
256257
{
257258
// Pointers and byrefs don't have vtable slots
258259
return 0;
259260
}
260-
if (TypeBeingBuilt.IsMdArray || (TypeBeingBuilt.IsSzArray && ((ArrayType)TypeBeingBuilt).ElementType.IsPointer))
261+
if (TypeBeingBuilt.IsMdArray || (TypeBeingBuilt.IsSzArray && ((ArrayType)TypeBeingBuilt).ElementType is TypeDesc elementType
262+
&& (elementType.IsPointer || elementType.IsFunctionPointer)))
261263
{
262264
// MDArray types and pointer arrays have the same vtable as the System.Array type they "derive" from.
263265
// They do not implement the generic interfaces that make this interesting for normal arrays.
@@ -355,7 +357,8 @@ public LowLevelList<bool> InstanceGCLayout
355357
Debug.Assert(TypeBeingBuilt.RetrieveRuntimeTypeHandleIfPossible() ||
356358
TypeBeingBuilt.IsTemplateCanonical() ||
357359
(TypeBeingBuilt is PointerType) ||
358-
(TypeBeingBuilt is ByRefType));
360+
(TypeBeingBuilt is ByRefType) ||
361+
(TypeBeingBuilt is FunctionPointerType));
359362
_instanceGCLayout = s_emptyLayout;
360363
}
361364
}
@@ -458,7 +461,7 @@ public bool IsArrayOfReferenceTypes
458461
{
459462
ArrayType typeAsArrayType = TypeBeingBuilt as ArrayType;
460463
if (typeAsArrayType != null)
461-
return !typeAsArrayType.ParameterType.IsValueType && !typeAsArrayType.ParameterType.IsPointer;
464+
return !typeAsArrayType.ParameterType.IsValueType && !typeAsArrayType.ParameterType.IsPointer && !typeAsArrayType.ParameterType.IsFunctionPointer;
462465
else
463466
return false;
464467
}

src/coreclr/nativeaot/System.Private.TypeLoader/src/Internal/TypeSystem/TypeDesc.Runtime.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,37 @@ internal bool RetrieveRuntimeTypeHandleIfPossible()
128128
{
129129
// SignatureVariables do not have RuntimeTypeHandles
130130
}
131+
else if (type is FunctionPointerType functionPointerType)
132+
{
133+
MethodSignature sig = functionPointerType.Signature;
134+
if (sig.ReturnType.RetrieveRuntimeTypeHandleIfPossible())
135+
{
136+
RuntimeTypeHandle[] parameterHandles = new RuntimeTypeHandle[sig.Length];
137+
bool handlesAvailable = true;
138+
for (int i = 0; i < parameterHandles.Length; i++)
139+
{
140+
if (sig[i].RetrieveRuntimeTypeHandleIfPossible())
141+
{
142+
parameterHandles[i] = sig[i].RuntimeTypeHandle;
143+
}
144+
else
145+
{
146+
handlesAvailable = false;
147+
break;
148+
}
149+
}
150+
151+
if (handlesAvailable
152+
&& TypeLoaderEnvironment.Instance.TryLookupFunctionPointerTypeForComponents(
153+
sig.ReturnType.RuntimeTypeHandle, parameterHandles,
154+
isUnmanaged: (sig.Flags & MethodSignatureFlags.UnmanagedCallingConventionMask) != 0,
155+
out RuntimeTypeHandle rtth))
156+
{
157+
functionPointerType.SetRuntimeTypeHandleUnsafe(rtth);
158+
return true;
159+
}
160+
}
161+
}
131162
else
132163
{
133164
Debug.Assert(false);

0 commit comments

Comments
 (0)