diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index f9e22594e1a3ad..59819890b8ce5e 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -17,7 +17,7 @@ namespace System [ComVisible(true)] public abstract partial class MulticastDelegate : Delegate { - private object? _invocationList; + internal object? _invocationList; // This is set under 3 circumstances // 1. Multicast delegate diff --git a/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs b/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs index 3d5f7da15005ef..ac7b1065d6579e 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs @@ -2532,9 +2532,10 @@ internal static void ValidateObject(object obj, IntPtr pMD) internal static IntPtr GetStubContext() => throw new UnreachableException(); // Unconditionally expanded intrinsic [MethodImpl(MethodImplOptions.NoInlining)] - internal static void MulticastDebuggerTraceHelper(object o, int count) + internal static unsafe void MulticastDebuggerTraceHelper(MulticastDelegate d, ref object position) { - MulticastDebuggerTraceHelperQCall(ObjectHandleOnStack.Create(ref o), count); + nuint count = (nuint)Unsafe.ByteOffset(ref position, ref ((object[])d._invocationList!)[0]) / (uint)sizeof(object); + MulticastDebuggerTraceHelperQCall(ObjectHandleOnStack.Create(ref d), (int)count); } [LibraryImport(RuntimeHelpers.QCall, EntryPoint="StubHelpers_MulticastDebuggerTraceHelper")] diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateThunks.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateThunks.cs index 40b267d9d2c19c..dc774e59248432 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateThunks.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateThunks.cs @@ -310,12 +310,8 @@ public override MethodIL EmitIL() ILEmitter emitter = new ILEmitter(); ILCodeStream codeStream = emitter.NewCodeStream(); - TypeDesc delegateWrapperType = ((MetadataType)SystemDelegateType).GetKnownNestedType("Wrapper"u8); - ArrayType invocationListArrayType = delegateWrapperType.MakeArrayType(); - - ILLocalVariable delegateArrayLocal = emitter.NewLocal(invocationListArrayType); - ILLocalVariable invocationCountLocal = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32)); - ILLocalVariable iteratorLocal = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32)); + ILLocalVariable startLocal = emitter.NewLocal(_delegateInfo.Type.MakeByRefType()); + ILLocalVariable endLocal = emitter.NewLocal(_delegateInfo.Type.MakeByRefType()); ILLocalVariable returnValueLocal = 0; if (!Signature.ReturnType.IsVoid) @@ -323,96 +319,64 @@ public override MethodIL EmitIL() returnValueLocal = emitter.NewLocal(Signature.ReturnType); } - // Fill in delegateArrayLocal - // Delegate.Wrapper[] delegateArrayLocal = (Delegate.Wrapper[])this._helperObject - - // ldarg.0 (this pointer) - // ldfld Delegate._helperObject - // castclass Delegate.Wrapper[] (omitted - generate unsafe cast assuming the delegate is well-formed) - // stloc delegateArrayLocal + // Load start and end refs codeStream.EmitLdArg(0); codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(HelperObjectField)); - // codeStream.Emit(ILOpcode.castclass, emitter.NewToken(invocationListArrayType)); - codeStream.EmitStLoc(delegateArrayLocal); - // Fill in invocationCountLocal - // int invocationCountLocal = this._extraFunctionPointerOrData + // Get start ref + TypeDesc signatureVariable = Context.GetSignatureVariable(0, method: true); + MethodSignature signature = new(MethodSignatureFlags.Static, 1, signatureVariable.MakeByRefType(), [signatureVariable.MakeArrayType()]); + TypeDesc wrapper = ((MetadataType)SystemDelegateType).GetKnownNestedType("Wrapper"u8); + MethodDesc gadr = Context.GetCoreLibEntryPoint("System.Runtime.InteropServices"u8, "MemoryMarshal"u8, "GetArrayDataReference"u8, signature).MakeInstantiatedMethod(wrapper); + codeStream.Emit(ILOpcode.call, emitter.NewToken(gadr)); + codeStream.EmitStLoc(startLocal); - // ldarg.0 (this pointer) - // ldfld Delegate._extraFunctionPointerOrData - // stloc invocationCountLocal + // Get end ref from adding count + codeStream.EmitLdLoc(startLocal); codeStream.EmitLdArg(0); codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(ExtraFunctionPointerOrDataField)); - codeStream.EmitStLoc(invocationCountLocal); - - // Fill in iteratorLocal - // int iteratorLocal = 0; - - // ldc.0 - // stloc iteratorLocal - codeStream.EmitLdc(0); - codeStream.EmitStLoc(iteratorLocal); + codeStream.EmitLdc(wrapper.GetElementSize().AsInt); + codeStream.Emit(ILOpcode.mul); + codeStream.Emit(ILOpcode.add); + codeStream.EmitStLoc(endLocal); - // Loop across every element of the array. ILCodeLabel startOfLoopLabel = emitter.NewCodeLabel(); + // Label_nextDelegate: codeStream.EmitLabel(startOfLoopLabel); - // Implement as do/while loop. We only have this stub in play if we're in the multicast situation - // Find the delegate to call - // delegateArrayLocal[iteratorLocal].Value - - // ldloc delegateArrayLocal - // ldloc iteratorLocal - // ldelema Delegate.Wrapper - // ldfld Delegate.Wrapper.Value - codeStream.EmitLdLoc(delegateArrayLocal); - codeStream.EmitLdLoc(iteratorLocal); - codeStream.Emit(ILOpcode.ldelema, emitter.NewToken(delegateWrapperType)); - codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(delegateWrapperType.GetKnownField("Value"u8))); - - // Call the delegate - // delegateArrayLocal[iteratorLocal].Value(...) - - // ldarg 1, n - // callvirt DelegateType.Invoke(...) - // IF there is a return value - // stloc returnValueLocal + // Load the delegate + codeStream.EmitLdLoc(startLocal); + codeStream.Emit(ILOpcode.ldind_ref); + // Load the arguments for (int i = 0; i < Signature.Length; i++) { codeStream.EmitLdArg(i + 1); } - codeStream.Emit(ILOpcode.callvirt, emitter.NewToken(_delegateInfo.InvokeMethod.InstantiateAsOpen())); + // Call the delegate + codeStream.Emit(ILOpcode.call, emitter.NewToken(_delegateInfo.InvokeMethod.InstantiateAsOpen())); + // Save return value. if (returnValueLocal != 0) codeStream.EmitStLoc(returnValueLocal); - // Increment iteratorLocal - // ++iteratorLocal; - - // ldloc iteratorLocal - // ldc.i4.1 - // add - // stloc iteratorLocal - codeStream.EmitLdLoc(iteratorLocal); - codeStream.EmitLdc(1); + // Increment the ref + codeStream.EmitLdLoc(startLocal); + codeStream.EmitLdc(wrapper.GetElementSize().AsInt); codeStream.Emit(ILOpcode.add); - codeStream.EmitStLoc(iteratorLocal); - - // Check to see if the loop is done - codeStream.EmitLdLoc(invocationCountLocal); - codeStream.EmitLdLoc(iteratorLocal); - codeStream.Emit(ILOpcode.bne_un, startOfLoopLabel); + codeStream.EmitStLoc(startLocal); - // Return to caller. If the delegate has a return value, be certain to return that. - // return returnValueLocal; + // Compare start ref with end. If less than branch to nextDelegate + codeStream.EmitLdLoc(startLocal); + codeStream.EmitLdLoc(endLocal); + codeStream.Emit(ILOpcode.blt_un, startOfLoopLabel); - // ldloc returnValueLocal - // ret + // Load the return value, return value from the last delegate call is returned if (returnValueLocal != 0) codeStream.EmitLdLoc(returnValueLocal); + // Return codeStream.Emit(ILOpcode.ret); return emitter.Link(this); diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index ec6d9f4277c91e..6bfe025f1851a5 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -2038,84 +2038,100 @@ extern "C" PCODE QCALLTYPE Delegate_GetMulticastInvokeSlow(MethodTable* pDelegat ILCodeStream *pCode = sl.NewCodeStream(ILStubLinker::kDispatch); - DWORD dwLoopCounterNum = pCode->NewLocal(ELEMENT_TYPE_I4); + LocalDesc refLocalDesc(TypeHandle(pDelegateMT).MakeByRef()); + DWORD dwCurrentRef = pCode->NewLocal(refLocalDesc); + DWORD dwEndRef = pCode->NewLocal(refLocalDesc); - DWORD dwReturnValNum = -1; - if (fReturnVal) - dwReturnValNum = pCode->NewLocal(sig.GetRetTypeHandleNT()); + DWORD dwReturnValNum = fReturnVal ? pCode->NewLocal(sig.GetRetTypeHandleNT()) : -1; - ILCodeLabel *nextDelegate = pCode->NewCodeLabel(); + // Load start and end refs + pCode->EmitLoadThis(); + pCode->EmitLDFLD(pCode->GetToken(CoreLibBinder::GetField(FIELD__MULTICAST_DELEGATE__INVOCATION_LIST))); - // initialize counter - pCode->EmitLDC(0); - pCode->EmitSTLOC(dwLoopCounterNum); + // Get start ref + MethodDesc* gadr = CoreLibBinder::GetMethod(METHOD__MEMORY_MARSHAL__GET_ARRAY_DATA_REFERENCE); + TypeHandle th(CoreLibBinder::GetClass(CLASS__OBJECT)); + gadr = MethodDesc::FindOrCreateAssociatedMethodDesc(gadr, gadr->GetMethodTable(), FALSE, Instantiation(&th, 1), TRUE); + + // Create signature for the MethodSpec. See ECMA-335 - II.23.2.15 + SigBuilder sigBuilder; + sigBuilder.AppendByte(IMAGE_CEE_CS_CALLCONV_GENERICINST); + sigBuilder.AppendData(1); + sigBuilder.AppendElementType(ELEMENT_TYPE_MVAR); + sigBuilder.AppendData(0); + uint32_t sigLen; + PCCOR_SIGNATURE gadrSig = (PCCOR_SIGNATURE)sigBuilder.GetSignature((DWORD*)&sigLen); + mdToken methodSpecSigToken = pCode->GetSigToken(gadrSig, sigLen); + + pCode->EmitCALL(pCode->GetToken(gadr, mdTokenNil, methodSpecSigToken), 1, 1); + pCode->EmitSTLOC(dwCurrentRef); + + // Get end ref from adding count + pCode->EmitLDLOC(dwCurrentRef); + pCode->EmitLoadThis(); + pCode->EmitLDFLD(pCode->GetToken(CoreLibBinder::GetField(FIELD__MULTICAST_DELEGATE__INVOCATION_COUNT))); + pCode->EmitLDC(th.GetSize()); + pCode->EmitMUL(); + pCode->EmitADD(); + pCode->EmitSTLOC(dwEndRef); - //Label_nextDelegate: + ILCodeLabel *nextDelegate = pCode->NewCodeLabel(); + // Label_nextDelegate: pCode->EmitLabel(nextDelegate); #ifdef DEBUGGING_SUPPORTED - ILCodeLabel *invokeTraceHelper = pCode->NewCodeLabel(); - ILCodeLabel *debuggerCheckEnd = pCode->NewCodeLabel(); - // Call MulticastDebuggerTraceHelper only if we have a controller subscribing to the event pCode->EmitLDC((DWORD_PTR)&g_multicastDelegateTraceActiveCount); pCode->EmitCONV_I(); pCode->EmitLDIND_I4(); - // g_multicastDelegateTraceActiveCount != 0 + // g_multicastDelegateTraceActiveCount == 0 pCode->EmitLDC(0); - pCode->EmitCEQ(); - pCode->EmitBRFALSE(invokeTraceHelper); - pCode->EmitLabel(debuggerCheckEnd); -#endif // DEBUGGING_SUPPORTED + ILCodeLabel *realLoopStart = pCode->NewCodeLabel(); + pCode->EmitBEQ(realLoopStart); - // Load next delegate from array using LoopCounter as index + // Call debugger tracing pCode->EmitLoadThis(); - pCode->EmitLDFLD(pCode->GetToken(CoreLibBinder::GetField(FIELD__MULTICAST_DELEGATE__INVOCATION_LIST))); - pCode->EmitLDLOC(dwLoopCounterNum); - pCode->EmitLDELEM_REF(); + pCode->EmitLDLOC(dwCurrentRef); + pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0); + + // Label_realLoopStart: + pCode->EmitLabel(realLoopStart); +#endif // DEBUGGING_SUPPORTED + + // Load the delegate + pCode->EmitLDLOC(dwCurrentRef); + pCode->EmitLDIND_REF(); // Load the arguments for (UINT paramCount = 0; paramCount < sig.NumFixedArgs(); paramCount++) pCode->EmitLDARG(paramCount); - // call the delegate + // Call the delegate pCode->EmitCALL(pCode->GetToken(pMD), sig.NumFixedArgs(), fReturnVal); // Save return value. if (fReturnVal) pCode->EmitSTLOC(dwReturnValNum); - // increment counter - pCode->EmitLDLOC(dwLoopCounterNum); - pCode->EmitLDC(1); + // Increment the ref + pCode->EmitLDLOC(dwCurrentRef); + pCode->EmitLDC(th.GetSize()); pCode->EmitADD(); - pCode->EmitSTLOC(dwLoopCounterNum); + pCode->EmitSTLOC(dwCurrentRef); - // compare LoopCounter with InvocationCount. If less then branch to nextDelegate - pCode->EmitLDLOC(dwLoopCounterNum); - pCode->EmitLoadThis(); - pCode->EmitLDFLD(pCode->GetToken(CoreLibBinder::GetField(FIELD__MULTICAST_DELEGATE__INVOCATION_COUNT))); + // Compare start ref with end. If less than branch to nextDelegate + pCode->EmitLDLOC(dwCurrentRef); + pCode->EmitLDLOC(dwEndRef); pCode->EmitBLT(nextDelegate); - // load the return value. return value from the last delegate call is returned + // Load the return value, return value from the last delegate call is returned if (fReturnVal) pCode->EmitLDLOC(dwReturnValNum); - // return + // Return pCode->EmitRET(); -#ifdef DEBUGGING_SUPPORTED - // Emit debugging support at the end of the method for better perf - pCode->EmitLabel(invokeTraceHelper); - - pCode->EmitLoadThis(); - pCode->EmitLDLOC(dwLoopCounterNum); - pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0); - - pCode->EmitBR(debuggerCheckEnd); -#endif // DEBUGGING_SUPPORTED - PCCOR_SIGNATURE pSig; DWORD cbSig; pMD->GetSig(&pSig, &cbSig); diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 7020945877cc67..17d6affcbbaeef 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -815,6 +815,7 @@ DEFINE_METHOD(UNSAFE, UNBOX, Unbox, NoSig) DEFINE_METHOD(UNSAFE, WRITE, Write, NoSig) DEFINE_CLASS(MEMORY_MARSHAL, Interop, MemoryMarshal) +DEFINE_METHOD(MEMORY_MARSHAL, GET_ARRAY_DATA_REFERENCE, GetArrayDataReference, GM_ArrT_RetRefT) DEFINE_METHOD(MEMORY_MARSHAL, GET_ARRAY_DATA_REFERENCE_MDARRAY, GetArrayDataReference, SM_Array_RetRefByte) DEFINE_CLASS(INTERLOCKED, Threading, Interlocked) @@ -1126,7 +1127,7 @@ DEFINE_METHOD(STUBHELPERS, PROFILER_BEGIN_TRANSITION_CALLBACK, Profiler DEFINE_METHOD(STUBHELPERS, PROFILER_END_TRANSITION_CALLBACK, ProfilerEndTransitionCallback, SM_PtrVoid_RetVoid) #endif -DEFINE_METHOD(STUBHELPERS, MULTICAST_DEBUGGER_TRACE_HELPER, MulticastDebuggerTraceHelper, SM_Obj_Int_RetVoid) +DEFINE_METHOD(STUBHELPERS, MULTICAST_DEBUGGER_TRACE_HELPER, MulticastDebuggerTraceHelper, NoSig) DEFINE_CLASS(CLEANUP_WORK_LIST_ELEMENT, StubHelpers, CleanupWorkListElement) diff --git a/src/coreclr/vm/metasig.h b/src/coreclr/vm/metasig.h index fb3801964ee0cf..f6c1f136e3e8e8 100644 --- a/src/coreclr/vm/metasig.h +++ b/src/coreclr/vm/metasig.h @@ -246,6 +246,7 @@ DEFINE_METASIG(GM(RefByte_T_RetVoid, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, r(b) M(0) DEFINE_METASIG(GM(PtrVoid_RetT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, P(v), M(0))) DEFINE_METASIG(GM(PtrVoid_T_RetVoid, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, P(v) M(0), v)) +DEFINE_METASIG(GM(ArrT_RetRefT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, a(M(0)), r(M(0)))) DEFINE_METASIG(GM(RefT_RetRefT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, r(M(0)), r(M(0)))) DEFINE_METASIG(GM(RefTFrom_RetRefTTo, IMAGE_CEE_CS_CALLCONV_DEFAULT, 2, r(M(0)), r(M(1)))) DEFINE_METASIG(GM(Obj_RetT, IMAGE_CEE_CS_CALLCONV_DEFAULT, 1, j, M(0)))