Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
99f405d
Make incorrect PerfMap usage trigger contract failures on Windows (wh…
davidwrighton Jun 3, 2026
1759f5d
Adjust preemptive/cooperative state throughout the runtime so that th…
davidwrighton Jun 4, 2026
e1f7ef1
Update genmeth.cpp
davidwrighton Jun 4, 2026
029f87d
Adjust based on test failures
davidwrighton Jun 5, 2026
d94d6a5
Fix wasm build break
davidwrighton Jun 5, 2026
e05223a
Merge branch 'CallPerfMapFromPREEMPTIVE' of github.com:davidwrighton/…
davidwrighton Jun 5, 2026
01bf23e
Code review updates
davidwrighton Jun 11, 2026
8dd3e3f
Merge branch 'main' of https://github.com/dotnet/runtime into CallPer…
davidwrighton Jun 11, 2026
be0225a
PerfMap::LogStubs is now MODE_PREEMPTIVE only
davidwrighton Jun 11, 2026
9d6d2e9
Make apply metadata logic happen on in preemptive mode, and fix a few…
davidwrighton Jun 15, 2026
1863d42
Adjust BuildMethodTable
davidwrighton Jun 15, 2026
16a5f31
Make error handling and EEClassHashTable construction GC_NOTRIGGER
davidwrighton Jun 16, 2026
6d21f26
Make new MethodDescCallSite for the custom attribute call scenario
davidwrighton Jun 18, 2026
65990ef
Merge branch 'main' of https://github.com/dotnet/runtime into CallPer…
davidwrighton Jun 18, 2026
5d786fd
Merge branch 'main' of https://github.com/dotnet/runtime into CallPer…
davidwrighton Jun 19, 2026
df2fb2a
Tweaks on delegate construction
davidwrighton Jun 19, 2026
b467065
Handle BindToMethodDetails correctly
davidwrighton Jun 19, 2026
0fe2486
Merge branch 'main' of https://github.com/dotnet/runtime into CallPer…
davidwrighton Jun 19, 2026
92f86c4
Make the standard delegate slow construction path avoid GC transitions
davidwrighton Jun 19, 2026
efa3adb
Safety comment about cast
davidwrighton Jun 19, 2026
827e26d
Fix global.json oops
davidwrighton Jun 19, 2026
5a9fe47
Optimize access to the default FuncPtrStub
davidwrighton Jun 22, 2026
2b55af6
Merge branch 'main' of https://github.com/dotnet/runtime into CallPer…
davidwrighton Jun 22, 2026
5aff2d6
Fix build break
davidwrighton Jun 23, 2026
e53ede9
Merge branch 'main' of https://github.com/dotnet/runtime into CallPer…
davidwrighton Jun 26, 2026
b75b3db
Remove default FuncPtrStub caching optimization
davidwrighton Jun 27, 2026
fee6d0e
Remove some unnecessary gc transitions
davidwrighton Jun 29, 2026
08272b4
Merge branch 'main' of https://github.com/dotnet/runtime into CallPer…
davidwrighton Jul 6, 2026
0b295d3
Fix build and tweak handling of method parameter to use a field acces…
davidwrighton Jul 6, 2026
68ae6e5
Address copilot review feedback
davidwrighton Jul 6, 2026
ff57154
Fix more issues
davidwrighton Jul 6, 2026
bb4207d
Use a helper function for accessing the Value from IRuntimeFieldInfo …
davidwrighton Jul 7, 2026
51c6416
Update wasm helper bits
davidwrighton Jul 7, 2026
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
109 changes: 95 additions & 14 deletions src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -400,27 +400,84 @@ internal static Delegate CreateDelegateForDynamicMethod(Type type, object? targe
Justification = "The parameter 'methodType' is passed by ref to QCallTypeHandle")]
private bool BindToMethodName(object? target, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.AllMethods)] RuntimeType methodType, string method, DelegateBindingFlags flags)
{
Delegate d = this;
return BindToMethodName(ObjectHandleOnStack.Create(ref d), ObjectHandleOnStack.Create(ref target),
new QCallTypeHandle(ref methodType), method, flags);
bool ret;
BindToMethodDetails bindToMethodDetails;

unsafe
{
ret = BindToMethodName(RuntimeHelpers.GetMethodTable(this), (target != null) ? RuntimeHelpers.GetMethodTable(target) : null,
new QCallTypeHandle(ref methodType), method, flags, ObjectHandleOnStack.Create(ref target), out bindToMethodDetails);
}

if (ret)
{
// Apply the results of the QCall to the delegate instance.
_methodPtr = bindToMethodDetails.methodPtr;
_methodPtrAux = bindToMethodDetails.methodPtrAux;
Unsafe.As<MulticastDelegate>(this)._invocationCount = bindToMethodDetails.invocationCount;
if (bindToMethodDetails.loaderAllocatorGCHandle.IsAllocated)
{
_helperObject = bindToMethodDetails.loaderAllocatorGCHandle.Target;
GC.KeepAlive(method);
}

if (bindToMethodDetails.selfReferentialTarget != 0)
_target = this;
else
_target = target;
}
return ret;
}

private struct BindToMethodDetails
{
public int selfReferentialTarget; // Whether the delegate's target object is the same as the first argument of the method to bind to. Only meaningful for open instance delegates.
public IntPtr methodPtr;
public IntPtr methodPtrAux;
public IntPtr invocationCount;
public GCHandle loaderAllocatorGCHandle; // The loader allocator needed if the delegate needs to keep it alive
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_BindToMethodName", StringMarshalling = StringMarshalling.Utf8)]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool BindToMethodName(ObjectHandleOnStack d, ObjectHandleOnStack target, QCallTypeHandle methodType, string method, DelegateBindingFlags flags);
private static partial bool BindToMethodName(MethodTable* pDelegateMT, MethodTable *pTargetMT, QCallTypeHandle methodType, string method, DelegateBindingFlags flags, ObjectHandleOnStack targetParameter, out BindToMethodDetails bindToMethodDetails);

private bool BindToMethodInfo(object? target, IRuntimeMethodInfo method, RuntimeType methodType, DelegateBindingFlags flags)
{
Delegate d = this;
bool ret = BindToMethodInfo(ObjectHandleOnStack.Create(ref d), ObjectHandleOnStack.Create(ref target),
method.Value, new QCallTypeHandle(ref methodType), flags);
GC.KeepAlive(method);
bool ret;
BindToMethodDetails bindToMethodDetails;

unsafe
{
// Note the use of Unsafe.As on method. This is not a generally safe operation, but we require in CoreCLR that
// all implementors of IRuntimeMethodInfo have the same offset for the m_value field, so this is safe in this context.
ret = BindToMethodInfo(RuntimeHelpers.GetMethodTable(this), (target != null) ? RuntimeHelpers.GetMethodTable(target) : null,
IRuntimeMethodInfo.GetValue(method), new QCallTypeHandle(ref methodType), flags, ObjectHandleOnStack.Create(ref target), out bindToMethodDetails);
}

Comment thread
davidwrighton marked this conversation as resolved.
if (ret)
{
// Apply the results of the QCall to the delegate instance.
_methodPtr = bindToMethodDetails.methodPtr;
_methodPtrAux = bindToMethodDetails.methodPtrAux;
Unsafe.As<MulticastDelegate>(this)._invocationCount = bindToMethodDetails.invocationCount;
if (bindToMethodDetails.loaderAllocatorGCHandle.IsAllocated)
{
_helperObject = bindToMethodDetails.loaderAllocatorGCHandle.Target;
GC.KeepAlive(method);
}

if (bindToMethodDetails.selfReferentialTarget != 0)
_target = this;
else
_target = target;
}
return ret;
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_BindToMethodInfo")]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool BindToMethodInfo(ObjectHandleOnStack d, ObjectHandleOnStack target, RuntimeMethodHandleInternal method, QCallTypeHandle methodType, DelegateBindingFlags flags);
private static partial bool BindToMethodInfo(MethodTable* pDelegateMT, MethodTable *pTargetMT, RuntimeMethodHandleInternal method, QCallTypeHandle methodType, DelegateBindingFlags flags, ObjectHandleOnStack targetParameter, out BindToMethodDetails bindToMethodDetails);

private static MulticastDelegate InternalAlloc(RuntimeType type)
{
Expand Down Expand Up @@ -465,12 +522,31 @@ private void DelegateConstruct(object target, IntPtr method)
throw new ArgumentNullException(nameof(method));
}

Delegate _this = this;
Construct(ObjectHandleOnStack.Create(ref _this), ObjectHandleOnStack.Create(ref target), method);
BindToMethodDetails bindToMethodDetails;

unsafe
{
Construct(RuntimeHelpers.GetMethodTable(this), (target != null) ? RuntimeHelpers.GetMethodTable(target) : null,
method, out bindToMethodDetails);
}

// Apply the results of the QCall to the delegate instance.
_methodPtr = bindToMethodDetails.methodPtr;
_methodPtrAux = bindToMethodDetails.methodPtrAux;
Unsafe.As<MulticastDelegate>(this)._invocationCount = bindToMethodDetails.invocationCount;
if (bindToMethodDetails.loaderAllocatorGCHandle.IsAllocated)
{
_helperObject = bindToMethodDetails.loaderAllocatorGCHandle.Target;
}

if (bindToMethodDetails.selfReferentialTarget != 0)
_target = this;
else
_target = target;
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_Construct")]
private static partial void Construct(ObjectHandleOnStack _this, ObjectHandleOnStack target, IntPtr method);
private static partial void Construct(MethodTable* pDelegateMT, MethodTable* pTargetMT, IntPtr method, out BindToMethodDetails bindToMethodDetails);

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern unsafe void* GetMulticastInvoke(MethodTable* pMT);
Expand Down Expand Up @@ -525,11 +601,16 @@ private static bool InternalEqualMethodHandles(Delegate left, Delegate right)

internal static IntPtr AdjustTarget(object target, IntPtr methodPtr)
{
return AdjustTarget(ObjectHandleOnStack.Create(ref target), methodPtr);
unsafe
{
IntPtr result = AdjustTarget(RuntimeHelpers.GetMethodTable(target), methodPtr);
GC.KeepAlive(target);
return result;
}
}

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_AdjustTarget")]
private static partial IntPtr AdjustTarget(ObjectHandleOnStack target, IntPtr methodPtr);
private static partial IntPtr AdjustTarget(MethodTable* targetMT, IntPtr methodPtr);

Comment thread
davidwrighton marked this conversation as resolved.
internal void InitializeVirtualCallStub(IntPtr methodPtr)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public abstract partial class MulticastDelegate : Delegate
// 1. Multicast delegate
// 2. Unmanaged function pointer
// 3. Open virtual delegate
private nint _invocationCount;
internal nint _invocationCount;

private bool IsUnmanagedFunctionPtr()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,7 @@ internal DynamicResolver(DynamicILInfo dynamicILInfo)

// We can never ever have two active destroy scouts for the same method. We need to initialize the scout
// outside the try/reregister block to avoid possibility of reregistration for finalization with active scout.
scout.m_methodHandle = method._methodHandle.Value;
scout.m_methodHandle = IRuntimeMethodInfo.GetValue(method._methodHandle);
}

private sealed class DestroyScout
Expand Down Expand Up @@ -1028,7 +1028,7 @@ public int GetTokenFor(RuntimeMethodHandle method)
IRuntimeMethodInfo methodReal = method.GetMethodInfo();
if (methodReal != null)
{
RuntimeMethodHandleInternal rmhi = methodReal.Value;
RuntimeMethodHandleInternal rmhi = IRuntimeMethodInfo.GetValue(methodReal);
if (!RuntimeMethodHandle.IsDynamicMethod(rmhi))
{
RuntimeType type = RuntimeMethodHandle.GetDeclaringType(rmhi);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public sealed override Delegate CreateDelegate(Type delegateType, object? target
// Compile the method since accessibility checks are done as part of compilation
GetMethodDescriptor();
IRuntimeMethodInfo? methodHandle = _methodHandle;
CompileMethod(methodHandle != null ? methodHandle.Value : RuntimeMethodHandleInternal.EmptyHandle);
CompileMethod(methodHandle != null ? IRuntimeMethodInfo.GetValue(methodHandle) : RuntimeMethodHandleInternal.EmptyHandle);
GC.KeepAlive(methodHandle);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ private int GetMemberRefOfMethodInfo(int tr, RuntimeMethodInfo method)
Debug.Assert(method != null);

RuntimeModuleBuilder thisModule = this;
int result = GetMemberRefOfMethodInfo(new QCallModule(ref thisModule), tr, ((IRuntimeMethodInfo)method).Value);
int result = GetMemberRefOfMethodInfo(new QCallModule(ref thisModule), tr, IRuntimeMethodInfo.GetValue(method));
GC.KeepAlive(method);
return result;
}
Expand All @@ -139,7 +139,7 @@ private int GetMemberRefOfMethodInfo(int tr, RuntimeConstructorInfo method)
Debug.Assert(method != null);

RuntimeModuleBuilder thisModule = this;
int result = GetMemberRefOfMethodInfo(new QCallModule(ref thisModule), tr, ((IRuntimeMethodInfo)method).Value);
int result = GetMemberRefOfMethodInfo(new QCallModule(ref thisModule), tr, IRuntimeMethodInfo.GetValue(method));
GC.KeepAlive(method);
return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,6 @@ internal RuntimeConstructorInfo(
#endregion

#region NonPublic Methods
RuntimeMethodHandleInternal IRuntimeMethodInfo.Value => new RuntimeMethodHandleInternal(m_handle);

internal override bool CacheEquals(object? o) =>
o is RuntimeConstructorInfo m && m.m_handle == m_handle &&
ReferenceEquals(m_declaringType, m.m_declaringType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1701,7 +1701,7 @@ private static bool FilterCustomAttributeRecord(
RuntimeTypeHandle attributeTypeHandle = attributeType.TypeHandle;

bool result = RuntimeMethodHandle.IsCAVisibleFromDecoratedType(new QCallTypeHandle(ref attributeTypeHandle),
ctorWithParameters is not null ? ctorWithParameters.Value : RuntimeMethodHandleInternal.EmptyHandle,
ctorWithParameters is not null ? IRuntimeMethodInfo.GetValue(ctorWithParameters) : RuntimeMethodHandleInternal.EmptyHandle,
new QCallTypeHandle(ref parentTypeHandle),
new QCallModule(ref decoratedModule)) != Interop.BOOL.FALSE;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ internal RuntimeMethodInfo(
#endregion

#region Private Methods
RuntimeMethodHandleInternal IRuntimeMethodInfo.Value => new RuntimeMethodHandleInternal(m_handle);

private RuntimeType ReflectedTypeInternal => m_reflectedTypeCache.GetRuntimeType();

private ParameterInfo[] FetchNonReturnParameters() =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ public static unsafe void PrepareMethod(RuntimeMethodHandle method, RuntimeTypeH
ReadOnlySpan<IntPtr> instantiationHandles = RuntimeTypeHandle.CopyRuntimeTypeHandles(instantiation, stackScratch: stackalloc IntPtr[8]);
fixed (IntPtr* pInstantiation = instantiationHandles)
{
PrepareMethod(methodInfo.Value, pInstantiation, instantiationHandles.Length);
PrepareMethod(IRuntimeMethodInfo.GetValue(methodInfo), pInstantiation, instantiationHandles.Length);
GC.KeepAlive(instantiation);
GC.KeepAlive(methodInfo);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ private static void PrelinkCore(MethodInfo m)
throw new ArgumentException(SR.Argument_MustBeRuntimeMethodInfo, nameof(m));
}

InternalPrelink(((IRuntimeMethodInfo)rmi).Value);
InternalPrelink(IRuntimeMethodInfo.GetValue(rmi));
GC.KeepAlive(rmi);
}

Expand Down
Loading