From e388570c4b11f03c92d80c53a6b5cc5672876a43 Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Sun, 3 Aug 2025 16:28:25 -0700 Subject: [PATCH 1/9] Support continuation arg in the portable arg shuffler --- src/coreclr/vm/callingconvention.h | 1 + src/coreclr/vm/comdelegate.cpp | 12 ++++ .../inst-unbox-thunks/inst-unbox-thunks.cs | 63 +++++++++++++++++++ .../inst-unbox-thunks.csproj | 10 +++ 4 files changed, 86 insertions(+) create mode 100644 src/tests/async/inst-unbox-thunks/inst-unbox-thunks.cs create mode 100644 src/tests/async/inst-unbox-thunks/inst-unbox-thunks.csproj diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h index 9ae7d10df0c9c5..13482bc0037c87 100644 --- a/src/coreclr/vm/callingconvention.h +++ b/src/coreclr/vm/callingconvention.h @@ -676,6 +676,7 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE // explicit arguments have been scanned is platform dependent. void GetThisLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetThisOffset(), pLoc); } void GetParamTypeLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetParamTypeArgOffset(), pLoc); } + void GetAsyncContinuatioLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetAsyncContinuationArgOffset(), pLoc); } void GetVASigCookieLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetVASigCookieOffset(), pLoc); } #ifndef CALLDESCR_RETBUFFARGREG diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 28b75bdf0d6932..a5f37fad071952 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -475,6 +475,18 @@ BOOL GenerateShuffleArrayPortable(MethodDesc* pMethodSrc, MethodDesc *pMethodDst #endif // !defined(TARGET_ARM64) || !defined(CALLDESCR_RETBUFFARGREG) } + // Handle async continuation argument. + _ASSERTE(!!sArgPlacerDst.HasAsyncContinuation() == !!sArgPlacerSrc.HasAsyncContinuation()); + if (sArgPlacerDst.HasAsyncContinuation()) + { + // The async continuation is implicit in both signatures. + sArgPlacerSrc.GetAsyncContinuatioLoc(&sArgSrc); + sArgPlacerDst.GetAsyncContinuatioLoc(&sArgDst); + + if (!AddNextShuffleEntryToArray(sArgSrc, sArgDst, pShuffleEntryArray, shuffleType)) + return FALSE; + } + // Iterate all the regular arguments. mapping source registers and stack locations to the corresponding // destination locations. while ((ofsSrc = sArgPlacerSrc.GetNextOffset()) != TransitionBlock::InvalidOffset) diff --git a/src/tests/async/inst-unbox-thunks/inst-unbox-thunks.cs b/src/tests/async/inst-unbox-thunks/inst-unbox-thunks.cs new file mode 100644 index 00000000000000..64820adafd2ad0 --- /dev/null +++ b/src/tests/async/inst-unbox-thunks/inst-unbox-thunks.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Xunit; + +public class InstUnBoxThunks +{ + interface I0 + { + Task M0(); + } + + struct Struct0 : I0 + { + public Task M0() + { + return Task.FromResult("hi"); + } + } + + static I0 o0; + static async Task CallStruct0M0() + { + o0 = new Struct0(); + return await o0.M0(); + } + + [Fact] + public static void NoArgUnbox() + { + Assert.Equal("hi", CallStruct0M0().Result); + } + + + interface I1 + { + Task M0(); + } + + struct Struct1 : I1 + { + public Task M0() + { + return Task.FromResult(typeof(T).ToString()); + } + } + + static I1 o1; + static async Task CallStruct1M0() + { + o1 = new Struct1(); + return await o1.M0(); + } + + [Fact] + public static void NoArgGenericUnbox() + { + Assert.Equal("System.String", CallStruct1M0().Result); + } +} diff --git a/src/tests/async/inst-unbox-thunks/inst-unbox-thunks.csproj b/src/tests/async/inst-unbox-thunks/inst-unbox-thunks.csproj new file mode 100644 index 00000000000000..c1dc91f0818707 --- /dev/null +++ b/src/tests/async/inst-unbox-thunks/inst-unbox-thunks.csproj @@ -0,0 +1,10 @@ + + + true + 0 + True + + + + + From 831c3227335d76c2fc9aea215bd59f7c96a60399 Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Tue, 5 Aug 2025 19:08:07 -0700 Subject: [PATCH 2/9] Unboxing stubs in IL --- src/coreclr/vm/ilstubcache.cpp | 19 +++++++--- src/coreclr/vm/ilstubcache.h | 4 ++- src/coreclr/vm/prestub.cpp | 25 +++---------- .../inst-unbox-thunks/inst-unbox-thunks.cs | 35 +++++++++++++++++++ 4 files changed, 57 insertions(+), 26 deletions(-) diff --git a/src/coreclr/vm/ilstubcache.cpp b/src/coreclr/vm/ilstubcache.cpp index 5aeeb2776aed3e..d7e4c3c0e96e6a 100644 --- a/src/coreclr/vm/ilstubcache.cpp +++ b/src/coreclr/vm/ilstubcache.cpp @@ -55,7 +55,7 @@ void CreateModuleIndependentSignature(LoaderHeap* pCreationHeap, SigPointer sigPtr(pSig, cbSig); SigBuilder sigBuilder; - sigPtr.ConvertToInternalSignature(pSigModule, pTypeContext, &sigBuilder, /* bSkipCustomModifier */ FALSE); + sigPtr.ConvertToInternalSignature(pSigModule, pTypeContext, &sigBuilder, /* bSkipCustomModifier */ TRUE); DWORD cbNewSig; PVOID pConvertedSig = sigBuilder.GetSignature(&cbNewSig); @@ -70,7 +70,7 @@ void CreateModuleIndependentSignature(LoaderHeap* pCreationHeap, // static MethodDesc* ILStubCache::CreateAndLinkNewILStubMethodDesc(LoaderAllocator* pAllocator, MethodTable* pMT, DWORD dwStubFlags, Module* pSigModule, PCCOR_SIGNATURE pSig, DWORD cbSig, SigTypeContext *pTypeContext, - ILStubLinker* pStubLinker) + ILStubLinker* pStubLinker, BOOL isAsync /* = FALSE */) { CONTRACT (MethodDesc*) { @@ -86,6 +86,7 @@ MethodDesc* ILStubCache::CreateAndLinkNewILStubMethodDesc(LoaderAllocator* pAllo dwStubFlags, pSigModule, pSig, cbSig, + isAsync, pTypeContext, &amTracker); @@ -143,7 +144,7 @@ namespace // static MethodDesc* ILStubCache::CreateNewMethodDesc(LoaderHeap* pCreationHeap, MethodTable* pMT, DWORD dwStubFlags, - Module* pSigModule, PCCOR_SIGNATURE pSig, DWORD cbSig, SigTypeContext *pTypeContext, + Module* pSigModule, PCCOR_SIGNATURE pSig, DWORD cbSig, BOOL isAsync, SigTypeContext *pTypeContext, AllocMemTracker* pamTracker) { CONTRACT (MethodDesc*) @@ -160,7 +161,7 @@ MethodDesc* ILStubCache::CreateNewMethodDesc(LoaderHeap* pCreationHeap, MethodTa mcDynamic, TRUE /* fNonVtableSlot */, TRUE /* fNativeCodeSlot */, - FALSE /* HasAsyncMethodData */, + isAsync /* HasAsyncMethodData */, pMT, pamTracker); @@ -175,6 +176,13 @@ MethodDesc* ILStubCache::CreateNewMethodDesc(LoaderHeap* pCreationHeap, MethodTa pMD->InitializeFlags(DynamicMethodDesc::FlagPublic | DynamicMethodDesc::FlagIsILStub); pMD->SetTemporaryEntryPoint(pamTracker); + if (isAsync) + { + pMD->SetHasAsyncMethodData(); + // Async stubs are standalone methods that do not form async/task-returning variant pairs. + pMD->GetAddrOfAsyncMethodData()->kind = AsyncMethodKind::AsyncExplicitImpl; + } + // // convert signature to a compatible signature if needed // @@ -531,7 +539,8 @@ MethodDesc* ILStubCache::GetStubMethodDesc( } MethodTable *pStubMT = GetOrCreateStubMethodTable(pSigLoaderModule); - pMD = ILStubCache::CreateNewMethodDesc(m_pAllocator->GetHighFrequencyHeap(), pStubMT, dwStubFlags, pSigModule, pSig, cbSig, pTypeContext, pamTracker); + BOOL isAsync = pTargetMD == NULL ? FALSE: pTargetMD->IsAsyncMethod(); + pMD = ILStubCache::CreateNewMethodDesc(m_pAllocator->GetHighFrequencyHeap(), pStubMT, dwStubFlags, pSigModule, pSig, cbSig, isAsync, pTypeContext, pamTracker); if (SF_IsSharedStub(dwStubFlags)) { diff --git a/src/coreclr/vm/ilstubcache.h b/src/coreclr/vm/ilstubcache.h index eb2b22952754df..52be99d9fb8ef1 100644 --- a/src/coreclr/vm/ilstubcache.h +++ b/src/coreclr/vm/ilstubcache.h @@ -73,7 +73,8 @@ class ILStubCache final PCCOR_SIGNATURE pSig, DWORD cbSig, SigTypeContext *pTypeContext, - ILStubLinker* pStubLinker); + ILStubLinker* pStubLinker, + BOOL isAsync = FALSE); MethodTable * GetStubMethodTable() { @@ -97,6 +98,7 @@ class ILStubCache final Module* pSigModule, PCCOR_SIGNATURE pSig, DWORD cbSig, + BOOL isAsync, SigTypeContext *pTypeContext, AllocMemTracker* pamTracker); diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index f9da780c74c417..bc186a0b1be1e6 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -1394,9 +1394,9 @@ PrepareCodeConfigBuffer::PrepareCodeConfigBuffer(NativeCodeVersion codeVersion) // CreateDerivedTargetSigWithExtraParams: // This method is used to create the signature of the target of the ILStub for -// instantiating, unboxing, and async variant stubs, when/where we need to -// introduce a generic context/async continuation. -// And since the generic context/async continuations are hidden parameters, +// instantiating and unboxing stubs, when/where we need to +// introduce a generic context. +// And since the generic contexts are hidden parameters, // we're creating a signature that looks like non-generic but with additional // parameters right after the thisptr void MethodDesc::CreateDerivedTargetSigWithExtraParams(MetaSig& msig, SigBuilder *stubSigBuilder) @@ -1412,8 +1412,6 @@ void MethodDesc::CreateDerivedTargetSigWithExtraParams(MetaSig& msig, SigBuilder unsigned numArgs = msig.NumFixedArgs(); if (msig.HasGenericContextArg()) numArgs++; - if (msig.HasAsyncContinuation()) - numArgs++; // ParamCount stubSigBuilder->AppendData(numArgs); // +1 is for context param @@ -1427,11 +1425,6 @@ void MethodDesc::CreateDerivedTargetSigWithExtraParams(MetaSig& msig, SigBuilder // The hidden context parameter stubSigBuilder->AppendElementType(ELEMENT_TYPE_I); } - - if (msig.HasAsyncContinuation()) - { - stubSigBuilder->AppendElementType(ELEMENT_TYPE_OBJECT); - } #endif // !TARGET_X86 // Copy rest of the arguments @@ -1447,12 +1440,6 @@ void MethodDesc::CreateDerivedTargetSigWithExtraParams(MetaSig& msig, SigBuilder { stubSigBuilder->AppendElementType(ELEMENT_TYPE_OBJECT); } - - if (msig.HasGenericContextArg()) - { - // The hidden context parameter - stubSigBuilder->AppendElementType(ELEMENT_TYPE_I); - } #endif // TARGET_X86 } @@ -1475,9 +1462,6 @@ Stub * CreateUnboxingILStubForSharedGenericValueTypeMethods(MethodDesc* pTargetM _ASSERTE(msig.HasThis()); - // TODO: (async) instantiating/unboxing stubs https://github.com/dotnet/runtime/issues/117266 - _ASSERTE(!msig.HasAsyncContinuation()); - ILStubLinker sl(pTargetMD->GetModule(), pTargetMD->GetSignature(), &typeContext, @@ -1540,7 +1524,8 @@ Stub * CreateUnboxingILStubForSharedGenericValueTypeMethods(MethodDesc* pTargetM pTargetMD->GetModule(), pSig, cbSig, &typeContext, - &sl); + &sl, + pTargetMD->IsAsyncMethod()); ILStubResolver *pResolver = pStubMD->AsDynamicMethodDesc()->GetILStubResolver(); diff --git a/src/tests/async/inst-unbox-thunks/inst-unbox-thunks.cs b/src/tests/async/inst-unbox-thunks/inst-unbox-thunks.cs index 64820adafd2ad0..dcc06a7f700b03 100644 --- a/src/tests/async/inst-unbox-thunks/inst-unbox-thunks.cs +++ b/src/tests/async/inst-unbox-thunks/inst-unbox-thunks.cs @@ -11,6 +11,7 @@ public class InstUnBoxThunks interface I0 { Task M0(); + Task M1(object a0, object a1, object a2, object a3, object a4, object a5, object a6, object a7, object a8); } struct Struct0 : I0 @@ -19,6 +20,11 @@ public Task M0() { return Task.FromResult("hi"); } + + public Task M1(object a0, object a1, object a2, object a3, object a4, object a5, object a6, object a7, object a8) + { + return Task.FromResult("hello"); + } } static I0 o0; @@ -28,16 +34,28 @@ static async Task CallStruct0M0() return await o0.M0(); } + static async Task CallStruct0M1() + { + o0 = new Struct0(); + return await o0.M1(default, default, default, default, default, default, default, default, default); + } + [Fact] public static void NoArgUnbox() { Assert.Equal("hi", CallStruct0M0().Result); } + [Fact] + public static void ManyArgUnbox() + { + Assert.Equal("hello", CallStruct0M1().Result); + } interface I1 { Task M0(); + Task M1(object a0, object a1, object a2, object a3, object a4, object a5, object a6, object a7, object a8); } struct Struct1 : I1 @@ -46,6 +64,11 @@ public Task M0() { return Task.FromResult(typeof(T).ToString()); } + + public Task M1(object a0, object a1, object a2, object a3, object a4, object a5, object a6, object a7, object a8) + { + return Task.FromResult(typeof(T).ToString()); + } } static I1 o1; @@ -55,9 +78,21 @@ static async Task CallStruct1M0() return await o1.M0(); } + static async Task CallStruct1M1() + { + o1 = new Struct1(); + return await o1.M1(default, default, default, default, default, default, default, default, default); + } + [Fact] public static void NoArgGenericUnbox() { Assert.Equal("System.String", CallStruct1M0().Result); } + + [Fact] + public static void ManyArgGenericUnbox() + { + Assert.Equal("System.String", CallStruct1M1().Result); + } } From 659f7cafd826908035ac4c385696a85d0429eae8 Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Thu, 7 Aug 2025 15:30:42 -0700 Subject: [PATCH 3/9] Instantiating stubs in IL --- src/coreclr/vm/prestub.cpp | 3 - .../inst-unbox-thunks/inst-unbox-thunks.cs | 96 +++++++++++++------ 2 files changed, 67 insertions(+), 32 deletions(-) diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index bc186a0b1be1e6..5eaf2de07bbcb6 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -1578,9 +1578,6 @@ Stub * CreateInstantiatingILStub(MethodDesc* pTargetMD, void* pHiddenArg) ILCodeStream *pCode = sl.NewCodeStream(ILStubLinker::kDispatch); - // TODO: (async) instantiating/unboxing stubs https://github.com/dotnet/runtime/issues/117266 - _ASSERTE(!msig.HasAsyncContinuation()); - // Build the new signature SigBuilder stubSigBuilder; MethodDesc::CreateDerivedTargetSigWithExtraParams(msig, &stubSigBuilder); diff --git a/src/tests/async/inst-unbox-thunks/inst-unbox-thunks.cs b/src/tests/async/inst-unbox-thunks/inst-unbox-thunks.cs index dcc06a7f700b03..075b6ce9fe48df 100644 --- a/src/tests/async/inst-unbox-thunks/inst-unbox-thunks.cs +++ b/src/tests/async/inst-unbox-thunks/inst-unbox-thunks.cs @@ -27,38 +27,21 @@ public Task M1(object a0, object a1, object a2, object a3, object a4, ob } } - static I0 o0; + static I0 o00; static async Task CallStruct0M0() { - o0 = new Struct0(); - return await o0.M0(); + o00 = new Struct0(); + return await o00.M0(); } + static I0 o01; static async Task CallStruct0M1() { - o0 = new Struct0(); - return await o0.M1(default, default, default, default, default, default, default, default, default); + o01 = new Struct0(); + return await o01.M1(default, default, default, default, default, default, default, default, default); } - [Fact] - public static void NoArgUnbox() - { - Assert.Equal("hi", CallStruct0M0().Result); - } - - [Fact] - public static void ManyArgUnbox() - { - Assert.Equal("hello", CallStruct0M1().Result); - } - - interface I1 - { - Task M0(); - Task M1(object a0, object a1, object a2, object a3, object a4, object a5, object a6, object a7, object a8); - } - - struct Struct1 : I1 + struct Struct1 : I0 { public Task M0() { @@ -71,17 +54,60 @@ public Task M1(object a0, object a1, object a2, object a3, object a4, ob } } - static I1 o1; + static I0 o10; static async Task CallStruct1M0() { - o1 = new Struct1(); - return await o1.M0(); + o10 = new Struct1(); + return await o10.M0(); } + static I0 o11; static async Task CallStruct1M1() { - o1 = new Struct1(); - return await o1.M1(default, default, default, default, default, default, default, default, default); + o11 = new Struct1(); + return await o11.M1(default, default, default, default, default, default, default, default, default); + } + + class Box where U : I0 + { + public U f; + } + + static async Task CallStruct1M0Field(Box arg) where T : I0 + { + return await arg.f.M0(); + } + + static async Task CallStruct1M1Field(Box arg) where T : I0 + { + return await arg.f.M1(default, default, default, default, default, default, default, default, default); + } + + static Box> b1 = new(); + static async Task CallStruct1M0b() + { + b1.f = new Struct1(); + return await CallStruct1M0Field(b1); + } + + static Box> b2 = new(); + static async Task CallStruct1M1b() + { + b2.f = new Struct1(); + return await CallStruct1M1Field(b2); + } + + + [Fact] + public static void NoArgUnbox() + { + Assert.Equal("hi", CallStruct0M0().Result); + } + + [Fact] + public static void ManyArgUnbox() + { + Assert.Equal("hello", CallStruct0M1().Result); } [Fact] @@ -95,4 +121,16 @@ public static void ManyArgGenericUnbox() { Assert.Equal("System.String", CallStruct1M1().Result); } + + [Fact] + public static void NoArgGenericInstantiating() + { + Assert.Equal("System.String", CallStruct1M0b().Result); + } + + [Fact] + public static void ManyArgGenericInstantiating() + { + Assert.Equal("System.String", CallStruct1M1b().Result); + } } From e4399466269543e6f46ae3f577cb07e1e6a4e1b2 Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Thu, 7 Aug 2025 15:49:50 -0700 Subject: [PATCH 4/9] fix for x86 --- src/coreclr/vm/prestub.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 5eaf2de07bbcb6..af9ca999a6a18f 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -1436,9 +1436,10 @@ void MethodDesc::CreateDerivedTargetSigWithExtraParams(MetaSig& msig, SigBuilder } #ifdef TARGET_X86 - if (msig.HasAsyncContinuation()) + if (msig.HasGenericContextArg()) { - stubSigBuilder->AppendElementType(ELEMENT_TYPE_OBJECT); + // The hidden context parameter + stubSigBuilder->AppendElementType(ELEMENT_TYPE_I); } #endif // TARGET_X86 } From ae27a494444e150b43067f7b784c6052d2a775a0 Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Thu, 7 Aug 2025 18:15:51 -0700 Subject: [PATCH 5/9] keep the modreq --- src/coreclr/vm/ilstubcache.cpp | 2 +- src/coreclr/vm/siginfo.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/ilstubcache.cpp b/src/coreclr/vm/ilstubcache.cpp index d7e4c3c0e96e6a..a5558713e17ee9 100644 --- a/src/coreclr/vm/ilstubcache.cpp +++ b/src/coreclr/vm/ilstubcache.cpp @@ -55,7 +55,7 @@ void CreateModuleIndependentSignature(LoaderHeap* pCreationHeap, SigPointer sigPtr(pSig, cbSig); SigBuilder sigBuilder; - sigPtr.ConvertToInternalSignature(pSigModule, pTypeContext, &sigBuilder, /* bSkipCustomModifier */ TRUE); + sigPtr.ConvertToInternalSignature(pSigModule, pTypeContext, &sigBuilder, /* bSkipCustomModifier */ FALSE); DWORD cbNewSig; PVOID pConvertedSig = sigBuilder.GetSignature(&cbNewSig); diff --git a/src/coreclr/vm/siginfo.cpp b/src/coreclr/vm/siginfo.cpp index 70e0c9d4751ab7..e23a17418e89f1 100644 --- a/src/coreclr/vm/siginfo.cpp +++ b/src/coreclr/vm/siginfo.cpp @@ -185,7 +185,7 @@ void SigPointer::ConvertToInternalExactlyOne(Module* pSigModule, const SigTypeCo { mdToken tk; IfFailThrowBF(GetToken(&tk), BFA_BAD_COMPLUS_SIG, pSigModule); - TypeHandle th = ClassLoader::LoadTypeDefOrRefThrowing(pSigModule, tk); + TypeHandle th = ClassLoader::LoadTypeDefOrRefThrowing(pSigModule, tk, ClassLoader::ThrowIfNotFound, ClassLoader::PermitUninstDefOrRef); pSigBuilder->AppendElementType(ELEMENT_TYPE_CMOD_INTERNAL); pSigBuilder->AppendByte(typ == ELEMENT_TYPE_CMOD_REQD); // "is required" byte pSigBuilder->AppendPointer(th.AsPtr()); From aa9c5fb8ba0a7b01e03e9a7bb36031749e29d95d Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Fri, 8 Aug 2025 00:51:38 -0700 Subject: [PATCH 6/9] TOKEN_ILSTUB_TARGET_SIG_ASYNC --- src/coreclr/vm/ilstubresolver.cpp | 2 +- src/coreclr/vm/jitinterface.cpp | 4 +++- src/coreclr/vm/prestub.cpp | 9 ++++++--- src/coreclr/vm/stubgen.cpp | 2 +- src/coreclr/vm/stubgen.h | 1 + .../inst-unbox-thunks/inst-unbox-thunks.cs | 20 +++++++++++-------- 6 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/coreclr/vm/ilstubresolver.cpp b/src/coreclr/vm/ilstubresolver.cpp index 0ab3b1476bccbb..36d1c4f6286e0e 100644 --- a/src/coreclr/vm/ilstubresolver.cpp +++ b/src/coreclr/vm/ilstubresolver.cpp @@ -240,7 +240,7 @@ ILStubResolver::ResolveSignature( { STANDARD_VM_CONTRACT; - if (token == TOKEN_ILSTUB_TARGET_SIG) + if (token == TOKEN_ILSTUB_TARGET_SIG || token == TOKEN_ILSTUB_TARGET_SIG_ASYNC) return m_pCompileTimeState->m_StubTargetMethodSig; return m_pCompileTimeState->m_tokenLookupMap.LookupSig(token); diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 3b569d3c36310d..752a6111c56575 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -480,6 +480,9 @@ static void ConvToJitSig( IfFailThrow(sig.GetCallingConvInfo(&data)); sigRet->callConv = (CorInfoCallConv) data; + if (token == TOKEN_ILSTUB_TARGET_SIG_ASYNC) + sigRet->callConv = (CorInfoCallConv)(sigRet->callConv | CORINFO_CALLCONV_ASYNCCALL); + #if defined(TARGET_UNIX) || defined(TARGET_ARM) if ((isCallConv(sigRet->callConv, IMAGE_CEE_CS_CALLCONV_VARARG)) || (isCallConv(sigRet->callConv, IMAGE_CEE_CS_CALLCONV_NATIVEVARARG))) @@ -1796,7 +1799,6 @@ CEEInfo::findSig( if (IsDynamicScope(scopeHnd)) { sig = GetDynamicResolver(scopeHnd)->ResolveSignature(sigTok); - sigTok = mdTokenNil; } else { diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index af9ca999a6a18f..22ccc37b2c1612 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -1512,7 +1512,8 @@ Stub * CreateUnboxingILStubForSharedGenericValueTypeMethods(MethodDesc* pTargetM pCode->EmitLDC((TADDR)pTargetMD->GetMultiCallableAddrOfCode(CORINFO_ACCESS_ANY)); // Do the calli - pCode->EmitCALLI(TOKEN_ILSTUB_TARGET_SIG, msig.NumFixedArgs() + 1, msig.IsReturnTypeVoid() ? 0 : 1); + int token = pTargetMD->IsAsyncMethod() ? TOKEN_ILSTUB_TARGET_SIG_ASYNC : TOKEN_ILSTUB_TARGET_SIG; + pCode->EmitCALLI(token, msig.NumFixedArgs() + 1, msig.IsReturnTypeVoid() ? 0 : 1); pCode->EmitRET(); PCCOR_SIGNATURE pSig; @@ -1614,7 +1615,8 @@ Stub * CreateInstantiatingILStub(MethodDesc* pTargetMD, void* pHiddenArg) pCode->EmitLDC((TADDR)pTargetMD->GetMultiCallableAddrOfCode(CORINFO_ACCESS_ANY)); // Do the calli - pCode->EmitCALLI(TOKEN_ILSTUB_TARGET_SIG, msig.NumFixedArgs() + 1, msig.IsReturnTypeVoid() ? 0 : 1); + int token = pTargetMD->IsAsyncMethod() ? TOKEN_ILSTUB_TARGET_SIG_ASYNC : TOKEN_ILSTUB_TARGET_SIG; + pCode->EmitCALLI(token, msig.NumFixedArgs() + 1, msig.IsReturnTypeVoid() ? 0 : 1); pCode->EmitRET(); PCCOR_SIGNATURE pSig; @@ -1627,7 +1629,8 @@ Stub * CreateInstantiatingILStub(MethodDesc* pTargetMD, void* pHiddenArg) pTargetMD->GetModule(), pSig, cbSig, &typeContext, - &sl); + &sl, + pTargetMD->IsAsyncMethod()); ILStubResolver *pResolver = pStubMD->AsDynamicMethodDesc()->GetILStubResolver(); diff --git a/src/coreclr/vm/stubgen.cpp b/src/coreclr/vm/stubgen.cpp index 06c99d1cb0cbd6..5b91329c3e63b1 100644 --- a/src/coreclr/vm/stubgen.cpp +++ b/src/coreclr/vm/stubgen.cpp @@ -118,7 +118,7 @@ void ILStubLinker::DumpIL_FormatToken(mdToken token, SString &strTokenFormatting PCCOR_SIGNATURE pSig; uint32_t cbSig; - if (token == TOKEN_ILSTUB_TARGET_SIG) + if (token == TOKEN_ILSTUB_TARGET_SIG || token == TOKEN_ILSTUB_TARGET_SIG_ASYNC) { // Build the current target sig into the buffer. cbSig = GetStubTargetMethodSigSize(); diff --git a/src/coreclr/vm/stubgen.h b/src/coreclr/vm/stubgen.h index da1bbc0a6ee4bc..45d55bb6c5b057 100644 --- a/src/coreclr/vm/stubgen.h +++ b/src/coreclr/vm/stubgen.h @@ -1105,5 +1105,6 @@ class ILCodeStream #endif // DACCESS_COMPILE #define TOKEN_ILSTUB_TARGET_SIG (TokenFromRid(0xFFFFFF, mdtSignature)) +#define TOKEN_ILSTUB_TARGET_SIG_ASYNC (TokenFromRid(0xFFFFFE, mdtSignature)) #endif // __STUBGEN_H__ diff --git a/src/tests/async/inst-unbox-thunks/inst-unbox-thunks.cs b/src/tests/async/inst-unbox-thunks/inst-unbox-thunks.cs index 075b6ce9fe48df..a3e82e3aa89d22 100644 --- a/src/tests/async/inst-unbox-thunks/inst-unbox-thunks.cs +++ b/src/tests/async/inst-unbox-thunks/inst-unbox-thunks.cs @@ -16,14 +16,16 @@ interface I0 struct Struct0 : I0 { - public Task M0() + public async Task M0() { - return Task.FromResult("hi"); + await Task.Yield(); + return "hi"; } - public Task M1(object a0, object a1, object a2, object a3, object a4, object a5, object a6, object a7, object a8) + public async Task M1(object a0, object a1, object a2, object a3, object a4, object a5, object a6, object a7, object a8) { - return Task.FromResult("hello"); + await Task.Yield(); + return "hello"; } } @@ -43,14 +45,16 @@ static async Task CallStruct0M1() struct Struct1 : I0 { - public Task M0() + public async Task M0() { - return Task.FromResult(typeof(T).ToString()); + await Task.Yield(); + return typeof(T).ToString(); } - public Task M1(object a0, object a1, object a2, object a3, object a4, object a5, object a6, object a7, object a8) + public async Task M1(object a0, object a1, object a2, object a3, object a4, object a5, object a6, object a7, object a8) { - return Task.FromResult(typeof(T).ToString()); + await Task.Yield(); + return typeof(T).ToString(); } } From af31336aec8b826aa05d78ca5e84bfe8de9010d6 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Mon, 11 Aug 2025 12:24:49 +0200 Subject: [PATCH 7/9] Insert null continuation arg on VM side --- src/coreclr/inc/corhdr.h | 3 +- src/coreclr/jit/importercalls.cpp | 13 +++++-- src/coreclr/vm/ilstubresolver.cpp | 2 +- src/coreclr/vm/jitinterface.cpp | 18 ++++++---- src/coreclr/vm/prestub.cpp | 56 +++++++++++++++++++++++++------ src/coreclr/vm/stubgen.cpp | 2 +- src/coreclr/vm/stubgen.h | 1 - 7 files changed, 71 insertions(+), 24 deletions(-) diff --git a/src/coreclr/inc/corhdr.h b/src/coreclr/inc/corhdr.h index 0bd7755e3b0d5e..01a2d602b2befd 100644 --- a/src/coreclr/inc/corhdr.h +++ b/src/coreclr/inc/corhdr.h @@ -975,7 +975,8 @@ typedef enum CorCallingConvention IMAGE_CEE_CS_CALLCONV_UNMANAGED = 0x9, // Unmanaged calling convention encoded as modopts IMAGE_CEE_CS_CALLCONV_GENERICINST = 0xa, // generic method instantiation IMAGE_CEE_CS_CALLCONV_NATIVEVARARG = 0xb, // used ONLY for 64bit vararg PInvoke calls - IMAGE_CEE_CS_CALLCONV_MAX = 0xc, // first invalid calling convention + IMAGE_CEE_CS_CALLCONV_ASYNC = 0xc, // used for calli in IL stubs + IMAGE_CEE_CS_CALLCONV_MAX = 0xd, // first invalid calling convention // The high bits of the calling convention convey additional info diff --git a/src/coreclr/jit/importercalls.cpp b/src/coreclr/jit/importercalls.cpp index bc7f0433287a10..e2882c95ee13f5 100644 --- a/src/coreclr/jit/importercalls.cpp +++ b/src/coreclr/jit/importercalls.cpp @@ -719,6 +719,11 @@ var_types Compiler::impImportCall(OPCODE opcode, JITDUMP(" Continuation continues on thread pool\n"); } } + else if (opcode == CEE_CALLI) + { + // Used for unboxing/instantiating stubs + JITDUMP("Call is an async calli\n"); + } else { JITDUMP("Call is an async non-task await\n"); @@ -941,7 +946,9 @@ var_types Compiler::impImportCall(OPCODE opcode, .WellKnown(WellKnownArg::VarArgsCookie)); } - if (call->AsCall()->IsAsync()) + // Add async continuation arg. For calli these are used for IL + // stubs and the VM inserts the arg itself. + if (call->AsCall()->IsAsync() && (opcode != CEE_CALLI)) { call->AsCall()->gtArgs.PushFront(this, NewCallArg::Primitive(gtNewNull(), TYP_REF) .WellKnown(WellKnownArg::AsyncContinuation)); @@ -955,7 +962,9 @@ var_types Compiler::impImportCall(OPCODE opcode, } else { - if (call->AsCall()->IsAsync()) + // Add async continuation arg. For calli these are used for IL + // stubs and the VM inserts the arg itself. + if (call->AsCall()->IsAsync() && (opcode != CEE_CALLI)) { call->AsCall()->gtArgs.PushBack(this, NewCallArg::Primitive(gtNewNull(), TYP_REF) .WellKnown(WellKnownArg::AsyncContinuation)); diff --git a/src/coreclr/vm/ilstubresolver.cpp b/src/coreclr/vm/ilstubresolver.cpp index 36d1c4f6286e0e..0ab3b1476bccbb 100644 --- a/src/coreclr/vm/ilstubresolver.cpp +++ b/src/coreclr/vm/ilstubresolver.cpp @@ -240,7 +240,7 @@ ILStubResolver::ResolveSignature( { STANDARD_VM_CONTRACT; - if (token == TOKEN_ILSTUB_TARGET_SIG || token == TOKEN_ILSTUB_TARGET_SIG_ASYNC) + if (token == TOKEN_ILSTUB_TARGET_SIG) return m_pCompileTimeState->m_StubTargetMethodSig; return m_pCompileTimeState->m_tokenLookupMap.LookupSig(token); diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 752a6111c56575..e79dd8e55861ab 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -478,22 +478,26 @@ static void ConvToJitSig( uint32_t data; IfFailThrow(sig.GetCallingConvInfo(&data)); - sigRet->callConv = (CorInfoCallConv) data; - - if (token == TOKEN_ILSTUB_TARGET_SIG_ASYNC) - sigRet->callConv = (CorInfoCallConv)(sigRet->callConv | CORINFO_CALLCONV_ASYNCCALL); #if defined(TARGET_UNIX) || defined(TARGET_ARM) - if ((isCallConv(sigRet->callConv, IMAGE_CEE_CS_CALLCONV_VARARG)) || - (isCallConv(sigRet->callConv, IMAGE_CEE_CS_CALLCONV_NATIVEVARARG))) + if ((isCallConv(data, IMAGE_CEE_CS_CALLCONV_VARARG)) || + (isCallConv(data, IMAGE_CEE_CS_CALLCONV_NATIVEVARARG))) { // This signature corresponds to a method that uses varargs, which are not supported. COMPlusThrow(kInvalidProgramException, IDS_EE_VARARG_NOT_SUPPORTED); } #endif // defined(TARGET_UNIX) || defined(TARGET_ARM) + // We have an internal calling convention for async used for signatures + // in IL stubs. Translate that to the flag representation in + // CorInfoCallConv. + if (isCallConv(data, IMAGE_CEE_CS_CALLCONV_ASYNC)) + sigRet->callConv = (CorInfoCallConv)(CORINFO_CALLCONV_DEFAULT | CORINFO_CALLCONV_ASYNCCALL | (data & ~IMAGE_CEE_CS_CALLCONV_MASK)); + else + sigRet->callConv = (CorInfoCallConv) data; + // Skip number of type arguments - if (sigRet->callConv & IMAGE_CEE_CS_CALLCONV_GENERIC) + if (data & IMAGE_CEE_CS_CALLCONV_GENERIC) IfFailThrow(sig.GetData(NULL)); uint32_t numArgs; diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 22ccc37b2c1612..dccce9b985229c 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -1404,6 +1404,8 @@ void MethodDesc::CreateDerivedTargetSigWithExtraParams(MetaSig& msig, SigBuilder STANDARD_VM_CONTRACT; BYTE callingConvention = IMAGE_CEE_CS_CALLCONV_DEFAULT; + if (msig.HasAsyncContinuation()) + callingConvention = IMAGE_CEE_CS_CALLCONV_ASYNC; if (msig.HasThis()) callingConvention |= IMAGE_CEE_CS_CALLCONV_HASTHIS; // CallingConvention @@ -1412,6 +1414,8 @@ void MethodDesc::CreateDerivedTargetSigWithExtraParams(MetaSig& msig, SigBuilder unsigned numArgs = msig.NumFixedArgs(); if (msig.HasGenericContextArg()) numArgs++; + if (msig.HasAsyncContinuation()) + numArgs++; // ParamCount stubSigBuilder->AppendData(numArgs); // +1 is for context param @@ -1425,6 +1429,11 @@ void MethodDesc::CreateDerivedTargetSigWithExtraParams(MetaSig& msig, SigBuilder // The hidden context parameter stubSigBuilder->AppendElementType(ELEMENT_TYPE_I); } + + if (msig.HasAsyncContinuation()) + { + stubSigBuilder->AppendElementType(ELEMENT_TYPE_OBJECT); + } #endif // !TARGET_X86 // Copy rest of the arguments @@ -1436,6 +1445,11 @@ void MethodDesc::CreateDerivedTargetSigWithExtraParams(MetaSig& msig, SigBuilder } #ifdef TARGET_X86 + if (msig.HasAsyncContinuation()) + { + stubSigBuilder->AppendElementType(ELEMENT_TYPE_OBJECT); + } + if (msig.HasGenericContextArg()) { // The hidden context parameter @@ -1484,12 +1498,17 @@ Stub * CreateUnboxingILStubForSharedGenericValueTypeMethods(MethodDesc* pTargetM pCode->EmitLoadThis(); pCode->EmitLDFLDA(tokRawData); -#if defined(TARGET_X86) +#ifdef TARGET_X86 // Push the rest of the arguments for x86 for (unsigned i = 0; i < msig.NumFixedArgs();i++) { pCode->EmitLDARG(i); } + + if (msig.HasAsyncContinuation()) + { + pCode->EmitLDNULL(); + } #endif // Push the hidden context param @@ -1500,7 +1519,12 @@ Stub * CreateUnboxingILStubForSharedGenericValueTypeMethods(MethodDesc* pTargetM pCode->EmitSUB(); pCode->EmitLDIND_I(); -#if !defined(TARGET_X86) +#ifndef TARGET_X86 + if (msig.HasAsyncContinuation()) + { + pCode->EmitLDNULL(); + } + // Push the rest of the arguments for not x86 for (unsigned i = 0; i < msig.NumFixedArgs();i++) { @@ -1512,8 +1536,7 @@ Stub * CreateUnboxingILStubForSharedGenericValueTypeMethods(MethodDesc* pTargetM pCode->EmitLDC((TADDR)pTargetMD->GetMultiCallableAddrOfCode(CORINFO_ACCESS_ANY)); // Do the calli - int token = pTargetMD->IsAsyncMethod() ? TOKEN_ILSTUB_TARGET_SIG_ASYNC : TOKEN_ILSTUB_TARGET_SIG; - pCode->EmitCALLI(token, msig.NumFixedArgs() + 1, msig.IsReturnTypeVoid() ? 0 : 1); + pCode->EmitCALLI(TOKEN_ILSTUB_TARGET_SIG, msig.NumFixedArgs() + 1, msig.IsReturnTypeVoid() ? 0 : 1); pCode->EmitRET(); PCCOR_SIGNATURE pSig; @@ -1591,32 +1614,43 @@ Stub * CreateInstantiatingILStub(MethodDesc* pTargetMD, void* pHiddenArg) pCode->EmitLoadThis(); } -#if defined(TARGET_X86) +#ifdef TARGET_X86 // Push the rest of the arguments for x86 for (unsigned i = 0; i < msig.NumFixedArgs();i++) { pCode->EmitLDARG(i); } -#endif // TARGET_X86 + if (msig.HasAsyncContinuation()) + { + pCode->EmitLDNULL(); + } + + // Push the hidden context param + // InstantiatingStub + pCode->EmitLDC((TADDR)pHiddenArg); +#else // Push the hidden context param // InstantiatingStub pCode->EmitLDC((TADDR)pHiddenArg); -#if !defined(TARGET_X86) - // Push the rest of the arguments for not x86 + if (msig.HasAsyncContinuation()) + { + pCode->EmitLDNULL(); + } + + // Push the rest of the arguments for x86 for (unsigned i = 0; i < msig.NumFixedArgs();i++) { pCode->EmitLDARG(i); } -#endif // !TARGET_X86 +#endif // Push the target address pCode->EmitLDC((TADDR)pTargetMD->GetMultiCallableAddrOfCode(CORINFO_ACCESS_ANY)); // Do the calli - int token = pTargetMD->IsAsyncMethod() ? TOKEN_ILSTUB_TARGET_SIG_ASYNC : TOKEN_ILSTUB_TARGET_SIG; - pCode->EmitCALLI(token, msig.NumFixedArgs() + 1, msig.IsReturnTypeVoid() ? 0 : 1); + pCode->EmitCALLI(TOKEN_ILSTUB_TARGET_SIG, msig.NumFixedArgs() + 1, msig.IsReturnTypeVoid() ? 0 : 1); pCode->EmitRET(); PCCOR_SIGNATURE pSig; diff --git a/src/coreclr/vm/stubgen.cpp b/src/coreclr/vm/stubgen.cpp index 5b91329c3e63b1..06c99d1cb0cbd6 100644 --- a/src/coreclr/vm/stubgen.cpp +++ b/src/coreclr/vm/stubgen.cpp @@ -118,7 +118,7 @@ void ILStubLinker::DumpIL_FormatToken(mdToken token, SString &strTokenFormatting PCCOR_SIGNATURE pSig; uint32_t cbSig; - if (token == TOKEN_ILSTUB_TARGET_SIG || token == TOKEN_ILSTUB_TARGET_SIG_ASYNC) + if (token == TOKEN_ILSTUB_TARGET_SIG) { // Build the current target sig into the buffer. cbSig = GetStubTargetMethodSigSize(); diff --git a/src/coreclr/vm/stubgen.h b/src/coreclr/vm/stubgen.h index 45d55bb6c5b057..da1bbc0a6ee4bc 100644 --- a/src/coreclr/vm/stubgen.h +++ b/src/coreclr/vm/stubgen.h @@ -1105,6 +1105,5 @@ class ILCodeStream #endif // DACCESS_COMPILE #define TOKEN_ILSTUB_TARGET_SIG (TokenFromRid(0xFFFFFF, mdtSignature)) -#define TOKEN_ILSTUB_TARGET_SIG_ASYNC (TokenFromRid(0xFFFFFE, mdtSignature)) #endif // __STUBGEN_H__ From e1c3f0ade80b256de20c7d3180682ce967c09470 Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Tue, 12 Aug 2025 14:20:23 -0700 Subject: [PATCH 8/9] undo unnecessary test changes --- src/tests/async/inst-unbox-thunks/inst-unbox-thunks.csproj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/tests/async/inst-unbox-thunks/inst-unbox-thunks.csproj b/src/tests/async/inst-unbox-thunks/inst-unbox-thunks.csproj index c1dc91f0818707..9367a79b2edbb1 100644 --- a/src/tests/async/inst-unbox-thunks/inst-unbox-thunks.csproj +++ b/src/tests/async/inst-unbox-thunks/inst-unbox-thunks.csproj @@ -1,10 +1,8 @@ - true - 0 True - + From c3d1dddaab0b33a88fe5ce08f2caa0438094f0de Mon Sep 17 00:00:00 2001 From: vsadov <8218165+VSadov@users.noreply.github.com> Date: Tue, 12 Aug 2025 14:26:59 -0700 Subject: [PATCH 9/9] typo fix lost in rebase --- src/coreclr/vm/callingconvention.h | 2 +- src/coreclr/vm/comdelegate.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h index 13482bc0037c87..3d4abbee5358d4 100644 --- a/src/coreclr/vm/callingconvention.h +++ b/src/coreclr/vm/callingconvention.h @@ -676,7 +676,7 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE // explicit arguments have been scanned is platform dependent. void GetThisLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetThisOffset(), pLoc); } void GetParamTypeLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetParamTypeArgOffset(), pLoc); } - void GetAsyncContinuatioLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetAsyncContinuationArgOffset(), pLoc); } + void GetAsyncContinuationLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetAsyncContinuationArgOffset(), pLoc); } void GetVASigCookieLoc(ArgLocDesc * pLoc) { WRAPPER_NO_CONTRACT; GetSimpleLoc(GetVASigCookieOffset(), pLoc); } #ifndef CALLDESCR_RETBUFFARGREG diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index a5f37fad071952..cc9535ca13f6ed 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -480,8 +480,8 @@ BOOL GenerateShuffleArrayPortable(MethodDesc* pMethodSrc, MethodDesc *pMethodDst if (sArgPlacerDst.HasAsyncContinuation()) { // The async continuation is implicit in both signatures. - sArgPlacerSrc.GetAsyncContinuatioLoc(&sArgSrc); - sArgPlacerDst.GetAsyncContinuatioLoc(&sArgDst); + sArgPlacerSrc.GetAsyncContinuationLoc(&sArgSrc); + sArgPlacerDst.GetAsyncContinuationLoc(&sArgDst); if (!AddNextShuffleEntryToArray(sArgSrc, sArgDst, pShuffleEntryArray, shuffleType)) return FALSE;