Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove Helper Method Frames (HMF) from Reflection #110211

Merged
merged 9 commits into from
Dec 3, 2024
17 changes: 2 additions & 15 deletions src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -434,23 +434,10 @@ private bool BindToMethodInfo(object? target, IRuntimeMethodInfo method, Runtime

private static MulticastDelegate InternalAlloc(RuntimeType type)
{
MulticastDelegate? d = null;
InternalAlloc(new QCallTypeHandle(ref type), ObjectHandleOnStack.Create(ref d));
return d!;
Debug.Assert(type.IsAssignableTo(typeof(MulticastDelegate)));
return Unsafe.As<MulticastDelegate>(RuntimeTypeHandle.InternalAlloc(type));
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_InternalAlloc")]
private static partial void InternalAlloc(QCallTypeHandle type, ObjectHandleOnStack d);

internal static MulticastDelegate InternalAllocLike(MulticastDelegate d)
{
InternalAllocLike(ObjectHandleOnStack.Create(ref d));
return d;
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_InternalAllocLike")]
private static partial void InternalAllocLike(ObjectHandleOnStack d);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe bool InternalEqualTypes(object a, object b)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ private static bool TrySetSlot(object?[] a, int index, object o)
private MulticastDelegate NewMulticastDelegate(object[] invocationList, int invocationCount, bool thisIsMultiCastAlready)
{
// First, allocate a new multicast delegate just like this one, i.e. same type as the this object
MulticastDelegate result = InternalAllocLike(this);
MulticastDelegate result = Unsafe.As<MulticastDelegate>(RuntimeTypeHandle.InternalAllocNoChecks((RuntimeType)GetType()));

// Performance optimization - if this already points to a true multicast delegate,
// copy _methodPtr and _methodPtrAux fields rather than calling into the EE to get them
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,32 @@ internal static void Unbox_Nullable(ref byte destPtr, MethodTable* typeMT, objec
}
}

[DebuggerHidden]
internal static object? ReboxFromNullable(MethodTable* srcMT, object src)
{
Debug.Assert(srcMT->IsNullable);

ref byte nullableData = ref src.GetRawData();

// If 'hasValue' is false, return null.
if (!Unsafe.As<byte, bool>(ref nullableData))
return null;

// Allocate a new instance of the T in Nullable<T>.
MethodTable* dstMT = srcMT->InstantiationArg0();
object dst = RuntimeTypeHandle.InternalAlloc(dstMT);

// Copy data from the Nullable<T>.
ref byte srcData = ref Unsafe.Add(ref nullableData, srcMT->NullableValueAddrOffset);
ref byte dstData = ref RuntimeHelpers.GetRawData(dst);
if (dstMT->ContainsGCPointers)
Buffer.BulkMoveWithWriteBarrier(ref dstData, ref srcData, dstMT->GetNumInstanceFieldBytesIfContainsGCPointers());
else
SpanHelpers.Memmove(ref dstData, ref srcData, dstMT->GetNumInstanceFieldBytes());

return dst;
}

[DebuggerHidden]
[MethodImpl(MethodImplOptions.NoInlining)]
private static ref byte Unbox_Helper(MethodTable* pMT1, object obj)
Expand Down
87 changes: 81 additions & 6 deletions src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,36 @@ private static partial void CreateInstanceForAnotherGenericParameter(
int cTypeHandles,
ObjectHandleOnStack instantiatedObject);

internal static unsafe object InternalAlloc(MethodTable* pMT)
{
object? result = null;
InternalAlloc(pMT, ObjectHandleOnStack.Create(ref result));
return result!;
}

internal static object InternalAlloc(RuntimeType type)
{
Debug.Assert(!type.GetNativeTypeHandle().IsTypeDesc);
object result = InternalAlloc(type.GetNativeTypeHandle().AsMethodTable());
GC.KeepAlive(type);
return result;
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeTypeHandle_InternalAlloc")]
private static unsafe partial void InternalAlloc(MethodTable* pMT, ObjectHandleOnStack result);

internal static object InternalAllocNoChecks(RuntimeType type)
{
Debug.Assert(!type.GetNativeTypeHandle().IsTypeDesc);
object? result = null;
InternalAllocNoChecks(type.GetNativeTypeHandle().AsMethodTable(), ObjectHandleOnStack.Create(ref result));
GC.KeepAlive(type);
return result!;
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeTypeHandle_InternalAllocNoChecks")]
private static unsafe partial void InternalAllocNoChecks(MethodTable* pMT, ObjectHandleOnStack result);

/// <summary>
/// Given a RuntimeType, returns information about how to activate it via calli
/// semantics. This method will ensure the type object is fully initialized within
Expand Down Expand Up @@ -1026,14 +1056,59 @@ internal static MdUtf8String GetUtf8Name(RuntimeMethodHandleInternal method)

[DebuggerStepThrough]
[DebuggerHidden]
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern object? InvokeMethod(object? target, void** arguments, Signature sig, bool isConstructor);
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeMethodHandle_InvokeMethod")]
private static partial void InvokeMethod(ObjectHandleOnStack target, void** arguments, ObjectHandleOnStack sig, Interop.BOOL isConstructor, ObjectHandleOnStack result);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern object? ReboxFromNullable(object? src);
[DebuggerStepThrough]
[DebuggerHidden]
internal static object? InvokeMethod(object? target, void** arguments, Signature sig, bool isConstructor)
{
object? result = null;
InvokeMethod(
ObjectHandleOnStack.Create(ref target),
arguments,
ObjectHandleOnStack.Create(ref sig),
isConstructor ? Interop.BOOL.TRUE : Interop.BOOL.FALSE,
ObjectHandleOnStack.Create(ref result));
return result;
}

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern object ReboxToNullable(object? src, RuntimeType destNullableType);
/// <summary>
/// For a true boxed Nullable{T}, re-box to a boxed {T} or null, otherwise just return the input.
/// </summary>
internal static object? ReboxFromNullable(object? src)
{
// If src is null or not NullableOfT, just return that state.
if (src is null)
{
return null;
}

MethodTable* pMT = RuntimeHelpers.GetMethodTable(src);
if (!pMT->IsNullable)
{
return src;
}

return CastHelpers.ReboxFromNullable(pMT, src);
}

/// <summary>
/// Convert a boxed value of {T} (which is either {T} or null) to a true boxed Nullable{T}.
/// </summary>
internal static object ReboxToNullable(object? src, RuntimeType destNullableType)
{
Debug.Assert(destNullableType.IsNullableOfT);
MethodTable* pMT = destNullableType.GetNativeTypeHandle().AsMethodTable();
object obj = RuntimeTypeHandle.InternalAlloc(pMT);
GC.KeepAlive(destNullableType); // The obj instance will keep the type alive.

CastHelpers.Unbox_Nullable(
ref obj.GetRawData(),
pMT,
src);
return obj;
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "RuntimeMethodHandle_GetMethodInstantiation")]
private static partial void GetMethodInstantiation(RuntimeMethodHandleInternal method, ObjectHandleOnStack types, Interop.BOOL fAsRuntimeTypeArray);
Expand Down
30 changes: 0 additions & 30 deletions src/coreclr/vm/comdelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2024,36 +2024,6 @@ Stub* COMDelegate::GetInvokeMethodStub(EEImplMethodDesc* pMD)
}
}

extern "C" void QCALLTYPE Delegate_InternalAlloc(QCall::TypeHandle pType, QCall::ObjectHandleOnStack d)
{
QCALL_CONTRACT;

BEGIN_QCALL;

GCX_COOP();

_ASSERTE(pType.AsTypeHandle().AsMethodTable()->IsDelegate());

d.Set(pType.AsTypeHandle().AsMethodTable()->Allocate());

END_QCALL;
}

extern "C" void QCALLTYPE Delegate_InternalAllocLike(QCall::ObjectHandleOnStack d)
{
QCALL_CONTRACT;

BEGIN_QCALL;

GCX_COOP();

_ASSERTE(d.Get()->GetMethodTable()->IsDelegate());

d.Set(d.Get()->GetMethodTable()->AllocateNoChecks());

END_QCALL;
}

void COMDelegate::ThrowIfInvalidUnmanagedCallersOnlyUsage(MethodDesc* pMD)
{
CONTRACTL
Expand Down
4 changes: 0 additions & 4 deletions src/coreclr/vm/comdelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,6 @@ extern "C" BOOL QCALLTYPE Delegate_BindToMethodName(QCall::ObjectHandleOnStack d
extern "C" BOOL QCALLTYPE Delegate_BindToMethodInfo(QCall::ObjectHandleOnStack d, QCall::ObjectHandleOnStack target,
MethodDesc * method, QCall::TypeHandle pMethodType, DelegateBindingFlags flags);

extern "C" void QCALLTYPE Delegate_InternalAlloc(QCall::TypeHandle pType, QCall::ObjectHandleOnStack d);

extern "C" void QCALLTYPE Delegate_InternalAllocLike(QCall::ObjectHandleOnStack d);

extern "C" void QCALLTYPE Delegate_FindMethodHandle(QCall::ObjectHandleOnStack d, QCall::ObjectHandleOnStack retMethodInfo);

extern "C" BOOL QCALLTYPE Delegate_InternalEqualMethodHandles(QCall::ObjectHandleOnStack left, QCall::ObjectHandleOnStack right);
Expand Down
3 changes: 0 additions & 3 deletions src/coreclr/vm/ecalllist.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,6 @@ FCFuncStart(gSignatureNative)
FCFuncEnd()

FCFuncStart(gRuntimeMethodHandle)
FCFuncElement("InvokeMethod", RuntimeMethodHandle::InvokeMethod)
FCFuncElement("ReboxFromNullable", RuntimeMethodHandle::ReboxFromNullable)
FCFuncElement("ReboxToNullable", RuntimeMethodHandle::ReboxToNullable)
FCFuncElement("GetImplAttributes", RuntimeMethodHandle::GetImplAttributes)
FCFuncElement("GetAttributes", RuntimeMethodHandle::GetAttributes)
FCFuncElement("GetMethodTable", RuntimeMethodHandle::GetMethodTable)
Expand Down
15 changes: 7 additions & 8 deletions src/coreclr/vm/methodtable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3940,18 +3940,17 @@ void MethodTable::CheckRunClassInitAsIfConstructingThrowing()
THROWS;
GC_TRIGGERS;
MODE_ANY;
PRECONDITION(HasPreciseInitCctors());
}
CONTRACTL_END;
if (HasPreciseInitCctors())

MethodTable *pMTCur = this;
while (pMTCur != NULL)
{
MethodTable *pMTCur = this;
while (pMTCur != NULL)
{
if (!pMTCur->GetClass()->IsBeforeFieldInit())
pMTCur->CheckRunClassInitThrowing();
if (!pMTCur->GetClass()->IsBeforeFieldInit())
pMTCur->CheckRunClassInitThrowing();

pMTCur = pMTCur->GetParentMethodTable();
}
pMTCur = pMTCur->GetParentMethodTable();
}
}

Expand Down
7 changes: 3 additions & 4 deletions src/coreclr/vm/methodtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -2676,8 +2676,8 @@ class MethodTable
// This flavor of Allocate is more efficient, but can only be used
// if CheckInstanceActivated(), IsClassInited() are known to be true.
// A sufficient condition is that another instance of the exact same type already
// exists in the same appdomain. It's currently called only from Delegate.Combine
// via COMDelegate::InternalAllocLike.
// exists in the same ALC. It's currently called only from Delegate.Combine
// via RuntimeTypeHandle_InternalAllocNoChecks.
OBJECTREF AllocateNoChecks();

OBJECTREF Box(void* data);
Expand Down Expand Up @@ -3921,12 +3921,11 @@ public :
TADDR m_ElementTypeHnd;
};
public:
union
union
{
PTR_InterfaceInfo m_pInterfaceMap;
TADDR m_encodedNullableUnboxData; // Used for Nullable<T> to represent the offset to the value field, and the size of the value field
};


// VTable slots go here

Expand Down
3 changes: 1 addition & 2 deletions src/coreclr/vm/methodtable.inl
Original file line number Diff line number Diff line change
Expand Up @@ -1230,7 +1230,7 @@ inline OBJECTREF MethodTable::AllocateNoChecks()
}
CONTRACTL_END;

// we know an instance of this class already exists in the same appdomain
// We know an instance of this class already exists
// therefore, some checks become redundant.
// this currently only happens for Delegate.Combine

Expand All @@ -1239,7 +1239,6 @@ inline OBJECTREF MethodTable::AllocateNoChecks()
return AllocateObject(this);
}


#ifndef DACCESS_COMPILE
//==========================================================================================
// unbox src into dest, No checks are done
Expand Down
5 changes: 3 additions & 2 deletions src/coreclr/vm/qcallentrypoints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,6 @@ static const Entry s_QCall[] =
DllImportEntry(Delegate_GetMulticastInvokeSlow)
DllImportEntry(Delegate_AdjustTarget)
DllImportEntry(Delegate_Construct)
DllImportEntry(Delegate_InternalAlloc)
DllImportEntry(Delegate_InternalAllocLike)
DllImportEntry(Delegate_FindMethodHandle)
DllImportEntry(Delegate_InternalEqualMethodHandles)
DllImportEntry(Environment_Exit)
Expand Down Expand Up @@ -140,9 +138,12 @@ static const Entry s_QCall[] =
#endif // FEATURE_COMINTEROP
DllImportEntry(RuntimeTypeHandle_GetRuntimeTypeFromHandleSlow)
DllImportEntry(RuntimeTypeHandle_CreateInstanceForAnotherGenericParameter)
DllImportEntry(RuntimeTypeHandle_InternalAlloc)
DllImportEntry(RuntimeTypeHandle_InternalAllocNoChecks)
DllImportEntry(RuntimeTypeHandle_AllocateTypeAssociatedMemory)
DllImportEntry(RuntimeTypeHandle_RegisterCollectibleTypeDependency)
DllImportEntry(MethodBase_GetCurrentMethod)
DllImportEntry(RuntimeMethodHandle_InvokeMethod)
DllImportEntry(RuntimeMethodHandle_ConstructInstantiation)
DllImportEntry(RuntimeMethodHandle_GetFunctionPointer)
DllImportEntry(RuntimeMethodHandle_GetIsCollectible)
Expand Down
Loading
Loading