Skip to content

Commit

Permalink
Avoid IntPtr[] allocation in Associates.AssignAssociates (#66825)
Browse files Browse the repository at this point in the history
* Avoid IntPtr[] allocation in Associates.AssignAssociates

* Address PR feedback (and as a result use stack space in a few more related places)
  • Loading branch information
stephentoub authored Mar 19, 2022
1 parent 09e4390 commit 637ede5
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,20 @@ internal static bool IncludeAccessor(MethodInfo? associate, bool nonPublic)

bool isInherited = declaredType != reflectedType;

IntPtr[]? genericArgumentHandles = null;
int genericArgumentCount = 0;
Span<IntPtr> genericArgumentHandles = stackalloc IntPtr[0];
RuntimeType[] genericArguments = declaredType.TypeHandle.GetInstantiationInternal();
if (genericArguments != null)
{
genericArgumentCount = genericArguments.Length;
genericArgumentHandles = new IntPtr[genericArguments.Length];
genericArgumentHandles = genericArguments.Length <= 16 ? // arbitrary stackalloc limit
stackalloc IntPtr[genericArguments.Length] :
new IntPtr[genericArguments.Length];
for (int i = 0; i < genericArguments.Length; i++)
{
genericArgumentHandles[i] = genericArguments[i].TypeHandle.Value;
}
}

RuntimeMethodHandleInternal associateMethodHandle = ModuleHandle.ResolveMethodHandleInternal(RuntimeTypeHandle.GetModule(declaredType), tkMethod, genericArgumentHandles, genericArgumentCount, null, 0);
RuntimeMethodHandleInternal associateMethodHandle = ModuleHandle.ResolveMethodHandleInternal(RuntimeTypeHandle.GetModule(declaredType), tkMethod, genericArgumentHandles, default);
Debug.Assert(!associateMethodHandle.IsNullHandle(), "Failed to resolve associateRecord methodDef token");

if (isInherited)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,10 @@ public static unsafe void PrepareMethod(RuntimeMethodHandle method, RuntimeTypeH
// defensive copy of user-provided array, per CopyRuntimeTypeHandles contract
instantiation = (RuntimeTypeHandle[]?)instantiation?.Clone();

IntPtr[]? instantiationHandles = RuntimeTypeHandle.CopyRuntimeTypeHandles(instantiation, out int length);
ReadOnlySpan<IntPtr> instantiationHandles = RuntimeTypeHandle.CopyRuntimeTypeHandles(instantiation, stackScratch: stackalloc IntPtr[8]);
fixed (IntPtr* pInstantiation = instantiationHandles)
{
PrepareMethod(methodInfo.Value, pInstantiation, length);
PrepareMethod(methodInfo.Value, pInstantiation, instantiationHandles.Length);
GC.KeepAlive(instantiation);
GC.KeepAlive(methodInfo);
}
Expand Down
46 changes: 21 additions & 25 deletions src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,20 +171,20 @@ internal static bool HasElementType(RuntimeType type)
// may need to make a defensive copy of the input array to ensure it's not
// mutated by another thread, and this defensive copy should be passed to
// a KeepAlive routine.
internal static IntPtr[]? CopyRuntimeTypeHandles(RuntimeTypeHandle[]? inHandles, out int length)
internal static ReadOnlySpan<IntPtr> CopyRuntimeTypeHandles(RuntimeTypeHandle[]? inHandles, Span<IntPtr> stackScratch)
{
if (inHandles == null || inHandles.Length == 0)
{
length = 0;
return null;
return default;
}

IntPtr[] outHandles = new IntPtr[inHandles.Length];
Span<IntPtr> outHandles = inHandles.Length <= stackScratch.Length ?
stackScratch.Slice(0, inHandles.Length) :
new IntPtr[inHandles.Length];
for (int i = 0; i < inHandles.Length; i++)
{
outHandles[i] = inHandles[i].Value;
}
length = outHandles.Length;
return outHandles;
}

Expand Down Expand Up @@ -1323,29 +1323,27 @@ public RuntimeTypeHandle ResolveTypeHandle(int typeToken, RuntimeTypeHandle[]? t
RuntimeModule module = GetRuntimeModule();
ValidateModulePointer(module);

IntPtr[]? typeInstantiationContextHandles = null;
int typeInstCount = 0;
IntPtr[]? methodInstantiationContextHandles = null;
int methodInstCount = 0;
ReadOnlySpan<IntPtr> typeInstantiationContextHandles = stackalloc IntPtr[0];
ReadOnlySpan<IntPtr> methodInstantiationContextHandles = stackalloc IntPtr[0];

// defensive copy of user-provided array, per CopyRuntimeTypeHandles contract
if (typeInstantiationContext?.Length > 0)
{
typeInstantiationContext = (RuntimeTypeHandle[]?)typeInstantiationContext.Clone();
typeInstantiationContextHandles = RuntimeTypeHandle.CopyRuntimeTypeHandles(typeInstantiationContext, out typeInstCount);
typeInstantiationContextHandles = RuntimeTypeHandle.CopyRuntimeTypeHandles(typeInstantiationContext, stackScratch: stackalloc IntPtr[8]);
}
if (methodInstantiationContext?.Length > 0)
{
methodInstantiationContext = (RuntimeTypeHandle[]?)methodInstantiationContext.Clone();
methodInstantiationContextHandles = RuntimeTypeHandle.CopyRuntimeTypeHandles(methodInstantiationContext, out methodInstCount);
methodInstantiationContextHandles = RuntimeTypeHandle.CopyRuntimeTypeHandles(methodInstantiationContext, stackScratch: stackalloc IntPtr[8]);
}

fixed (IntPtr* typeInstArgs = typeInstantiationContextHandles, methodInstArgs = methodInstantiationContextHandles)
{
try
{
RuntimeType? type = null;
ResolveType(new QCallModule(ref module), typeToken, typeInstArgs, typeInstCount, methodInstArgs, methodInstCount, ObjectHandleOnStack.Create(ref type));
ResolveType(new QCallModule(ref module), typeToken, typeInstArgs, typeInstantiationContextHandles.Length, methodInstArgs, methodInstantiationContextHandles.Length, ObjectHandleOnStack.Create(ref type));
GC.KeepAlive(typeInstantiationContext);
GC.KeepAlive(methodInstantiationContext);
return new RuntimeTypeHandle(type!);
Expand Down Expand Up @@ -1382,30 +1380,30 @@ public RuntimeMethodHandle ResolveMethodHandle(int methodToken, RuntimeTypeHandl
typeInstantiationContext = (RuntimeTypeHandle[]?)typeInstantiationContext?.Clone();
methodInstantiationContext = (RuntimeTypeHandle[]?)methodInstantiationContext?.Clone();

IntPtr[]? typeInstantiationContextHandles = RuntimeTypeHandle.CopyRuntimeTypeHandles(typeInstantiationContext, out int typeInstCount);
IntPtr[]? methodInstantiationContextHandles = RuntimeTypeHandle.CopyRuntimeTypeHandles(methodInstantiationContext, out int methodInstCount);
ReadOnlySpan<IntPtr> typeInstantiationContextHandles = RuntimeTypeHandle.CopyRuntimeTypeHandles(typeInstantiationContext, stackScratch: stackalloc IntPtr[8]);
ReadOnlySpan<IntPtr> methodInstantiationContextHandles = RuntimeTypeHandle.CopyRuntimeTypeHandles(methodInstantiationContext, stackScratch: stackalloc IntPtr[8]);

RuntimeMethodHandleInternal handle = ResolveMethodHandleInternal(module, methodToken, typeInstantiationContextHandles, typeInstCount, methodInstantiationContextHandles, methodInstCount);
RuntimeMethodHandleInternal handle = ResolveMethodHandleInternal(module, methodToken, typeInstantiationContextHandles, methodInstantiationContextHandles);
IRuntimeMethodInfo retVal = new RuntimeMethodInfoStub(handle, RuntimeMethodHandle.GetLoaderAllocator(handle));
GC.KeepAlive(typeInstantiationContext);
GC.KeepAlive(methodInstantiationContext);
return new RuntimeMethodHandle(retVal);
}

internal static RuntimeMethodHandleInternal ResolveMethodHandleInternal(RuntimeModule module, int methodToken, IntPtr[]? typeInstantiationContext, int typeInstCount, IntPtr[]? methodInstantiationContext, int methodInstCount)
internal static RuntimeMethodHandleInternal ResolveMethodHandleInternal(RuntimeModule module, int methodToken, ReadOnlySpan<IntPtr> typeInstantiationContext, ReadOnlySpan<IntPtr> methodInstantiationContext)
{
ValidateModulePointer(module);

try
{
fixed (IntPtr* typeInstArgs = typeInstantiationContext, methodInstArgs = methodInstantiationContext)
{
return ResolveMethod(new QCallModule(ref module), methodToken, typeInstArgs, typeInstCount, methodInstArgs, methodInstCount);
return ResolveMethod(new QCallModule(ref module), methodToken, typeInstArgs, typeInstantiationContext.Length, methodInstArgs, methodInstantiationContext.Length);
}
}
catch (Exception)
{
if (!ModuleHandle.GetMetadataImport(module).IsValidToken(methodToken))
if (!GetMetadataImport(module).IsValidToken(methodToken))
throw new ArgumentOutOfRangeException(nameof(methodToken),
SR.Format(SR.Argument_InvalidToken, methodToken, new ModuleHandle(module)));
throw;
Expand All @@ -1431,29 +1429,27 @@ public RuntimeFieldHandle ResolveFieldHandle(int fieldToken, RuntimeTypeHandle[]
RuntimeModule module = GetRuntimeModule();
ValidateModulePointer(module);

IntPtr[]? typeInstantiationContextHandles = null;
int typeInstCount = 0;
IntPtr[]? methodInstantiationContextHandles = null;
int methodInstCount = 0;
ReadOnlySpan<IntPtr> typeInstantiationContextHandles = stackalloc IntPtr[0];
ReadOnlySpan<IntPtr> methodInstantiationContextHandles = stackalloc IntPtr[0];

// defensive copy of user-provided array, per CopyRuntimeTypeHandles contract
if (typeInstantiationContext?.Length > 0)
{
typeInstantiationContext = (RuntimeTypeHandle[]?)typeInstantiationContext.Clone();
typeInstantiationContextHandles = RuntimeTypeHandle.CopyRuntimeTypeHandles(typeInstantiationContext, out typeInstCount);
typeInstantiationContextHandles = RuntimeTypeHandle.CopyRuntimeTypeHandles(typeInstantiationContext, stackScratch: stackalloc IntPtr[8]);
}
if (methodInstantiationContext?.Length > 0)
{
methodInstantiationContext = (RuntimeTypeHandle[]?)methodInstantiationContext.Clone();
methodInstantiationContextHandles = RuntimeTypeHandle.CopyRuntimeTypeHandles(methodInstantiationContext, out methodInstCount);
methodInstantiationContextHandles = RuntimeTypeHandle.CopyRuntimeTypeHandles(methodInstantiationContext, stackScratch: stackalloc IntPtr[8]);
}

fixed (IntPtr* typeInstArgs = typeInstantiationContextHandles, methodInstArgs = methodInstantiationContextHandles)
{
try
{
IRuntimeFieldInfo? field = null;
ResolveField(new QCallModule(ref module), fieldToken, typeInstArgs, typeInstCount, methodInstArgs, methodInstCount, ObjectHandleOnStack.Create(ref field));
ResolveField(new QCallModule(ref module), fieldToken, typeInstArgs, typeInstantiationContextHandles.Length, methodInstArgs, methodInstantiationContextHandles.Length, ObjectHandleOnStack.Create(ref field));
GC.KeepAlive(typeInstantiationContext);
GC.KeepAlive(methodInstantiationContext);
return new RuntimeFieldHandle(field!);
Expand Down

0 comments on commit 637ede5

Please sign in to comment.