diff --git a/src/coreclr/inc/sigparser.h b/src/coreclr/inc/sigparser.h index 6aad0669d94ed7..c5f539599175e6 100644 --- a/src/coreclr/inc/sigparser.h +++ b/src/coreclr/inc/sigparser.h @@ -116,7 +116,7 @@ class SigParser void GetSignature( PCCOR_SIGNATURE * pSig, - uint32_t * pcbSigSize) + uint32_t * pcbSigSize) const { *pSig = m_ptr; *pcbSigSize = m_dwLen; diff --git a/src/coreclr/inc/utilcode.h b/src/coreclr/inc/utilcode.h index 9eefb47875f5be..18adfa7650e3d6 100644 --- a/src/coreclr/inc/utilcode.h +++ b/src/coreclr/inc/utilcode.h @@ -3098,9 +3098,9 @@ class RangeList return this->AddRangeWorker(start, end, id); } - void RemoveRanges(void *id, const BYTE *start = NULL, const BYTE *end = NULL) + void RemoveRanges(void *id) { - return this->RemoveRangesWorker(id, start, end); + return this->RemoveRangesWorker(id); } BOOL IsInRange(TADDR address, TADDR *pID = NULL) @@ -3114,16 +3114,14 @@ class RangeList // You can overload these two for synchronization (as LockedRangeList does) virtual BOOL AddRangeWorker(const BYTE *start, const BYTE *end, void *id); - // If both "start" and "end" are NULL, then this method deletes all ranges with - // the given id (i.e. the original behaviour). Otherwise, it ignores the given - // id and deletes all ranges falling in the region [start, end). - virtual void RemoveRangesWorker(void *id, const BYTE *start = NULL, const BYTE *end = NULL); + // Deletes all ranges with the given id + virtual void RemoveRangesWorker(void *id); #else virtual BOOL AddRangeWorker(const BYTE *start, const BYTE *end, void *id) { return TRUE; } - virtual void RemoveRangesWorker(void *id, const BYTE *start = NULL, const BYTE *end = NULL) { } + virtual void RemoveRangesWorker(void *id) { } #endif // !DACCESS_COMPILE virtual BOOL IsInRangeWorker(TADDR address, TADDR *pID = NULL); diff --git a/src/coreclr/utilcode/loaderheap.cpp b/src/coreclr/utilcode/loaderheap.cpp index 9cbda2a5244567..50f51486d1cbb6 100644 --- a/src/coreclr/utilcode/loaderheap.cpp +++ b/src/coreclr/utilcode/loaderheap.cpp @@ -150,7 +150,7 @@ BOOL RangeList::AddRangeWorker(const BYTE *start, const BYTE *end, void *id) } } -void RangeList::RemoveRangesWorker(void *id, const BYTE* start, const BYTE* end) +void RangeList::RemoveRangesWorker(void *id) { CONTRACTL { @@ -177,24 +177,9 @@ void RangeList::RemoveRangesWorker(void *id, const BYTE* start, const BYTE* end) while (r < rEnd) { - if (r->id != (TADDR)NULL) + if (r->id == (TADDR)id) { - if (start != NULL) - { - _ASSERTE(end != NULL); - - if (r->start >= (TADDR)start && r->start < (TADDR)end) - { - CONSISTENCY_CHECK_MSGF(r->end >= (TADDR)start && - r->end <= (TADDR)end, - ("r: %p start: %p end: %p", r, start, end)); - r->id = (TADDR)NULL; - } - } - else if (r->id == (TADDR)id) - { - r->id = (TADDR)NULL; - } + r->id = (TADDR)NULL; } r++; diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h index 1744870fe9f159..ed8f4ae98e1f94 100644 --- a/src/coreclr/vm/callingconvention.h +++ b/src/coreclr/vm/callingconvention.h @@ -642,7 +642,7 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE //------------------------------------------------------------ int GetNextOffset(); - CorElementType GetArgType(TypeHandle *pTypeHandle = NULL) + CorElementType GetArgType(TypeHandle *pTypeHandle = NULL) const { LIMITED_METHOD_CONTRACT; if (pTypeHandle != NULL) @@ -652,7 +652,7 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE return m_argType; } - int GetArgSize() + int GetArgSize() const { LIMITED_METHOD_CONTRACT; return m_argSize; diff --git a/src/coreclr/vm/class.cpp b/src/coreclr/vm/class.cpp index 58bd150972a759..6120ee32ea696e 100644 --- a/src/coreclr/vm/class.cpp +++ b/src/coreclr/vm/class.cpp @@ -124,25 +124,22 @@ void EEClass::Destruct(MethodTable * pOwningMT) if (IsDelegate()) { DelegateEEClass* pDelegateEEClass = (DelegateEEClass*)this; - - if (pDelegateEEClass->m_pStaticCallStub) + for (Stub* pThunk : {pDelegateEEClass->m_pStaticCallStub, pDelegateEEClass->m_pInstRetBuffCallStub}) { - // Collect data to remove stub entry from StubManager if - // stub is deleted. - BYTE* entry = (BYTE*)pDelegateEEClass->m_pStaticCallStub->GetEntryPoint(); - UINT length = pDelegateEEClass->m_pStaticCallStub->GetNumCodeBytes(); - - ExecutableWriterHolder stubWriterHolder(pDelegateEEClass->m_pStaticCallStub, sizeof(Stub)); - BOOL fStubDeleted = stubWriterHolder.GetRW()->DecRef(); - if (fStubDeleted) + if (pThunk == nullptr) + continue; + + _ASSERTE(pThunk->IsShuffleThunk()); + + if (pThunk->HasExternalEntryPoint()) // IL thunk { - StubLinkStubManager::g_pManager->RemoveStubRange(entry, length); + pThunk->DecRef(); + } + else + { + ExecutableWriterHolder stubWriterHolder(pThunk, sizeof(Stub)); + stubWriterHolder.GetRW()->DecRef(); } - } - if (pDelegateEEClass->m_pInstRetBuffCallStub) - { - ExecutableWriterHolder stubWriterHolder(pDelegateEEClass->m_pInstRetBuffCallStub, sizeof(Stub)); - stubWriterHolder.GetRW()->DecRef(); } } diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 8f20a405f7a5f8..104a82d9bf5766 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -337,6 +337,18 @@ struct ShuffleGraphNode BOOL AddNextShuffleEntryToArray(ArgLocDesc sArgSrc, ArgLocDesc sArgDst, SArray * pShuffleEntryArray, ShuffleComputationType shuffleType) { +#if defined(TARGET_RISCV64) + if (sArgSrc.m_structFields.flags != sArgDst.m_structFields.flags) + { + _ASSERT(sArgSrc.m_structFields.flags == FpStruct::UseIntCallConv + || sArgDst.m_structFields.flags == FpStruct::UseIntCallConv); + // StubLinkerCPU::EmitShuffleThunk supports shuffles only within the integer calling convention (floating-point + // arguments may be passed in registers but these are not shuffled then). Transferring arguments between + // calling conventions is handled by IL stubs. + return FALSE; + } +#endif // TARGET_RISCV64 + ShuffleEntry entry; ZeroMemory(&entry, sizeof(entry)); @@ -408,25 +420,6 @@ BOOL GenerateShuffleArrayPortable(MethodDesc* pMethodSrc, MethodDesc *pMethodDst return FALSE; } - UINT stackSizeDelta = 0; - -#if defined(TARGET_X86) && !defined(UNIX_X86_ABI) - { - UINT stackSizeSrc = sArgPlacerSrc.SizeOfArgStack(); - UINT stackSizeDst = sArgPlacerDst.SizeOfArgStack(); - - // Windows X86 calling convention requires the stack to shrink when removing - // arguments, as it is callee pop - if (stackSizeDst > stackSizeSrc) - { - // we can drop arguments but we can never make them up - this is definitely not allowed - COMPlusThrow(kVerificationException); - } - - stackSizeDelta = stackSizeSrc - stackSizeDst; - } -#endif // Callee pop architectures - defined(TARGET_X86) && !defined(UNIX_X86_ABI) - INT ofsSrc; INT ofsDst; ArgLocDesc sArgSrc; @@ -623,20 +616,21 @@ BOOL GenerateShuffleArrayPortable(MethodDesc* pMethodSrc, MethodDesc *pMethodDst } entry.srcofs = ShuffleEntry::SENTINEL; - entry.dstofs = 0; + entry.stacksizedelta = 0; pShuffleEntryArray->Append(entry); return TRUE; } #endif // FEATURE_PORTABLE_SHUFFLE_THUNKS -VOID GenerateShuffleArray(MethodDesc* pInvoke, MethodDesc *pTargetMeth, SArray * pShuffleEntryArray) +BOOL GenerateShuffleArray(MethodDesc* pInvoke, MethodDesc *pTargetMeth, SArray * pShuffleEntryArray) { STANDARD_VM_CONTRACT; #ifdef FEATURE_PORTABLE_SHUFFLE_THUNKS // Portable default implementation - GenerateShuffleArrayPortable(pInvoke, pTargetMeth, pShuffleEntryArray, ShuffleComputationType::DelegateShuffleThunk); + if (!GenerateShuffleArrayPortable(pInvoke, pTargetMeth, pShuffleEntryArray, ShuffleComputationType::DelegateShuffleThunk)) + return FALSE; #elif defined(TARGET_X86) ShuffleEntry entry; ZeroMemory(&entry, sizeof(entry)); @@ -724,6 +718,48 @@ VOID GenerateShuffleArray(MethodDesc* pInvoke, MethodDesc *pTargetMeth, SArray %s.%s:\n", + pShuffleEntryArray->GetCount(), + pInvoke->GetMethodTable()->GetDebugClassName(), pInvoke->GetName(), + pTargetMeth->GetMethodTable()->GetDebugClassName(), pTargetMeth->GetName())); + + for (COUNT_T i = 0; i < pShuffleEntryArray->GetCount(); ++i) + { + ShuffleEntry entry = (*pShuffleEntryArray)[i]; + if (entry.srcofs == ShuffleEntry::SENTINEL) + { + LOGALWAYS((" [%u] sentinel, stack size delta %u\n", i, entry.stacksizedelta)); + _ASSERTE(i == pShuffleEntryArray->GetCount() - 1); + break; + } + + struct ShuffleInfo { const char* type; int offset; }; + auto getShuffleInfo = [](UINT16 offset) -> ShuffleInfo { + if (offset == ShuffleEntry::HELPERREG) + { + return { "helper register" }; + } + else if (offset & ShuffleEntry::REGMASK) + { + const char* type = (offset & ShuffleEntry::FPREGMASK) + ? ((offset & ShuffleEntry::FPSINGLEMASK) ? "single-FP register" : "FP register") + : "integer register"; + return { type, offset & ShuffleEntry::OFSREGMASK }; + } + else + { + return {"stack slot", offset & ShuffleEntry::OFSMASK }; + } + }; + ShuffleInfo src = getShuffleInfo(entry.srcofs); + ShuffleInfo dst = getShuffleInfo(entry.dstofs); + LOGALWAYS((" [%u] %s %i -> %s %i\n", i, src.type, src.offset, dst.type, dst.offset)); + } + } + return TRUE; } static ShuffleThunkCache* s_pShuffleThunkCache = NULL; @@ -789,8 +825,69 @@ LoaderHeap *DelegateEEClass::GetStubHeap() return GetInvokeMethod()->GetLoaderAllocator()->GetStubHeap(); } +#if defined(TARGET_RISCV64) +static Stub* CreateILDelegateShuffleThunk(MethodDesc* pDelegateMD, bool callTargetWithThis) +{ + SigTypeContext typeContext(pDelegateMD); + MetaSig sig(pDelegateMD); + if (LoggingOn(LF_STUBS, LL_INFO1000000)) + { + SString delegateName; + pDelegateMD->GetFullMethodInfo(delegateName); + LOGALWAYS(("CreateILDelegateShuffleThunk %s (%i args, callTargetWithThis:%i)\n", + delegateName.GetUTF8(), sig.NumFixedArgs(), callTargetWithThis)); + } + _ASSERTE(sig.HasThis()); + + Module* pModule = sig.GetModule(); + Signature signature = pDelegateMD->GetSignature(); + + ILStubLinkerFlags flags = ILSTUB_LINKER_FLAG_STUB_HAS_THIS; + ILStubLinker stubLinker(pModule, signature, &typeContext, pDelegateMD, flags); + ILCodeStream *pCode = stubLinker.NewCodeStream(ILStubLinker::kDispatch); -static Stub* SetupShuffleThunk(MethodTable* pDelMT, MethodDesc* pTargetMeth) + for (unsigned i = 0; i < sig.NumFixedArgs(); ++i) + pCode->EmitLDARG(i); + + pCode->EmitLoadThis(); + pCode->EmitLDFLD(pCode->GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__METHOD_PTR_AUX))); + pCode->EmitCALLI(TOKEN_ILSTUB_TARGET_SIG, sig.NumFixedArgs(), sig.IsReturnTypeVoid() ? 0 : 1); + pCode->EmitRET(); + + MethodDesc* pStubMD = ILStubCache::CreateAndLinkNewILStubMethodDesc( + pDelegateMD->GetLoaderAllocator(), pDelegateMD->GetMethodTable(), ILSTUB_DELEGATE_SHUFFLE_THUNK, + pModule, signature.GetRawSig(), signature.GetRawSigLen(), &typeContext, &stubLinker); + + // Build target signature + SigBuilder sigBuilder(signature.GetRawSigLen()); + sigBuilder.AppendByte(callTargetWithThis + ? IMAGE_CEE_CS_CALLCONV_DEFAULT_HASTHIS + : IMAGE_CEE_CS_CALLCONV_DEFAULT); + + unsigned numFixedArgs = sig.NumFixedArgs() - callTargetWithThis; + sigBuilder.AppendData(numFixedArgs); + + SigPointer pReturn = sig.GetReturnProps(); + pReturn.ConvertToInternalExactlyOne(pModule, &typeContext, &sigBuilder); + + sig.SkipArg(); // skip delegate object + if (callTargetWithThis) + sig.SkipArg(); + + SigPointer pArgs = sig.GetArgProps(); + for (unsigned i = 0; i < numFixedArgs; ++i) + pArgs.ConvertToInternalExactlyOne(pModule, &typeContext, &sigBuilder); + + DWORD cbTargetSig; + PCCOR_SIGNATURE pTargetSig = (PCCOR_SIGNATURE)sigBuilder.GetSignature(&cbTargetSig); + ILStubResolver* pResolver = pStubMD->AsDynamicMethodDesc()->GetILStubResolver(); + pResolver->SetStubTargetMethodSig(pTargetSig, cbTargetSig); + + return Stub::NewStub(JitILStub(pStubMD), NEWSTUB_FL_SHUFFLE_THUNK); +} +#endif // TARGET_RISCV64 + +static PCODE SetupShuffleThunk(MethodTable * pDelMT, MethodDesc *pTargetMeth) { CONTRACTL { @@ -801,49 +898,66 @@ static Stub* SetupShuffleThunk(MethodTable* pDelMT, MethodDesc* pTargetMeth) } CONTRACTL_END; - GCX_PREEMP(); - + bool isInstRetBuff = (!pTargetMeth->IsStatic() && pTargetMeth->HasRetBuffArg() && IsRetBuffPassedAsFirstArg()); DelegateEEClass * pClass = (DelegateEEClass *)pDelMT->GetClass(); + // Look for a thunk cached on the delegate class first. Note we need a different thunk for instance methods with a + // hidden return buffer argument because the extra argument switches place with the target when coming from the caller. + Stub* pShuffleThunk = isInstRetBuff ? pClass->m_pInstRetBuffCallStub : pClass->m_pStaticCallStub; + if (pShuffleThunk) + return pShuffleThunk->GetEntryPoint(); + + GCX_PREEMP(); + MethodDesc *pMD = pClass->GetInvokeMethod(); + // We haven't already setup a shuffle thunk, go do it now (which will cache the result automatically). StackSArray rShuffleEntryArray; - GenerateShuffleArray(pMD, pTargetMeth, &rShuffleEntryArray); + if (GenerateShuffleArray(pMD, pTargetMeth, &rShuffleEntryArray)) + { + ShuffleThunkCache* pShuffleThunkCache = s_pShuffleThunkCache; - ShuffleThunkCache* pShuffleThunkCache = s_pShuffleThunkCache; + LoaderAllocator* pLoaderAllocator = pDelMT->GetLoaderAllocator(); + if (pLoaderAllocator->IsCollectible()) + { + pShuffleThunkCache = ((AssemblyLoaderAllocator*)pLoaderAllocator)->GetShuffleThunkCache(); + } - LoaderAllocator* pLoaderAllocator = pDelMT->GetLoaderAllocator(); - if (pLoaderAllocator->IsCollectible()) + pShuffleThunk = pShuffleThunkCache->Canonicalize((const BYTE *)&rShuffleEntryArray[0]); + } + else { - pShuffleThunkCache = ((AssemblyLoaderAllocator*)pLoaderAllocator)->GetShuffleThunkCache(); +#if defined(TARGET_RISCV64) + pShuffleThunk = CreateILDelegateShuffleThunk(pMD, isInstRetBuff); +#else + _ASSERTE(FALSE); + return (PCODE)NULL; +#endif // TARGET_RISCV64 } - Stub* pShuffleThunk = pShuffleThunkCache->Canonicalize((const BYTE *)&rShuffleEntryArray[0]); if (!pShuffleThunk) { COMPlusThrowOM(); } - if (!pTargetMeth->IsStatic() && pTargetMeth->HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) + // Cache the shuffle thunk + Stub** ppThunk = isInstRetBuff ? &pClass->m_pInstRetBuffCallStub : &pClass->m_pStaticCallStub; + Stub* pExistingThunk = InterlockedCompareExchangeT(ppThunk, pShuffleThunk, NULL); + if (pExistingThunk != NULL) { - if (InterlockedCompareExchangeT(&pClass->m_pInstRetBuffCallStub, pShuffleThunk, NULL ) != NULL) + if (pShuffleThunk->HasExternalEntryPoint()) // IL thunk { - ExecutableWriterHolder shuffleThunkWriterHolder(pShuffleThunk, sizeof(Stub)); - shuffleThunkWriterHolder.GetRW()->DecRef(); - pShuffleThunk = pClass->m_pInstRetBuffCallStub; + pShuffleThunk->DecRef(); } - } - else - { - if (InterlockedCompareExchangeT(&pClass->m_pStaticCallStub, pShuffleThunk, NULL ) != NULL) + else { ExecutableWriterHolder shuffleThunkWriterHolder(pShuffleThunk, sizeof(Stub)); shuffleThunkWriterHolder.GetRW()->DecRef(); - pShuffleThunk = pClass->m_pStaticCallStub; } + pShuffleThunk = pExistingThunk; } - return pShuffleThunk; + return pShuffleThunk->GetEntryPoint(); } static PCODE GetVirtualCallStub(MethodDesc *method, TypeHandle scopeType) @@ -1101,22 +1215,10 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, // We need to shuffle arguments for open delegates since the first argument on the calling side is not meaningful to the // callee. MethodTable * pDelegateMT = (*pRefThis)->GetMethodTable(); - DelegateEEClass *pDelegateClass = (DelegateEEClass*)pDelegateMT->GetClass(); - Stub *pShuffleThunk = NULL; - - // Look for a thunk cached on the delegate class first. Note we need a different thunk for instance methods with a - // hidden return buffer argument because the extra argument switches place with the target when coming from the caller. - if (!pTargetMethod->IsStatic() && pTargetMethod->HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) - pShuffleThunk = pDelegateClass->m_pInstRetBuffCallStub; - else - pShuffleThunk = pDelegateClass->m_pStaticCallStub; - - // If we haven't already setup a shuffle thunk go do it now (which will cache the result automatically). - if (!pShuffleThunk) - pShuffleThunk = SetupShuffleThunk(pDelegateMT, pTargetMethod); + PCODE pEntryPoint = SetupShuffleThunk(pDelegateMT, pTargetMethod); // Indicate that the delegate will jump to the shuffle thunk rather than directly to the target method. - refRealDelegate->SetMethodPtr(pShuffleThunk->GetEntryPoint()); + refRealDelegate->SetMethodPtr(pEntryPoint); // Use stub dispatch for all virtuals. // Investigate not using this for non-interface virtuals. @@ -1612,7 +1714,6 @@ extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, Q if (Nullable::IsNullableType(pMeth->GetMethodTable())) COMPlusThrow(kNotSupportedException); - DelegateEEClass* pDelCls = (DelegateEEClass*)pDelMT->GetClass(); MethodDesc* pDelegateInvoke = COMDelegate::FindDelegateInvokeMethod(pDelMT); UINT invokeArgCount = MethodDescToNumFixedArgs(pDelegateInvoke); @@ -1634,14 +1735,8 @@ extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, Q refThis->SetTarget(refThis); // set the shuffle thunk - Stub *pShuffleThunk = (!pMeth->IsStatic() && pMeth->HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) - ? pDelCls->m_pInstRetBuffCallStub - : pDelCls->m_pStaticCallStub; - - if (pShuffleThunk == NULL) - pShuffleThunk = SetupShuffleThunk(pDelMT, pMeth); - - refThis->SetMethodPtr(pShuffleThunk->GetEntryPoint()); + PCODE pEntryPoint = SetupShuffleThunk(pDelMT, pMeth); + refThis->SetMethodPtr(pEntryPoint); // set the ptr aux according to what is needed, if virtual need to call make virtual stub dispatch if (!pMeth->IsStatic() && pMeth->IsVirtual() && !pMeth->GetMethodTable()->IsValueType()) @@ -2705,7 +2800,6 @@ MethodDesc* COMDelegate::GetDelegateCtor(TypeHandle delegateType, MethodDesc *pT MethodDesc *pRealCtor = NULL; MethodTable *pDelMT = delegateType.AsMethodTable(); - DelegateEEClass *pDelCls = (DelegateEEClass*)(pDelMT->GetClass()); MethodDesc *pDelegateInvoke = COMDelegate::FindDelegateInvokeMethod(pDelMT); @@ -2847,15 +2941,8 @@ MethodDesc* COMDelegate::GetDelegateCtor(TypeHandle delegateType, MethodDesc *pT else pRealCtor = CoreLibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_OPENED); } - Stub *pShuffleThunk = NULL; - if (!pTargetMethod->IsStatic() && pTargetMethod->HasRetBuffArg() && IsRetBuffPassedAsFirstArg()) - pShuffleThunk = pDelCls->m_pInstRetBuffCallStub; - else - pShuffleThunk = pDelCls->m_pStaticCallStub; - if (!pShuffleThunk) - pShuffleThunk = SetupShuffleThunk(pDelMT, pTargetMethod); - pCtorData->pArg3 = (void*)pShuffleThunk->GetEntryPoint(); + pCtorData->pArg3 = (void*)SetupShuffleThunk(pDelMT, pTargetMethod); if (isCollectible) { pCtorData->pArg4 = pTargetMethodLoaderAllocator->GetLoaderAllocatorObjectHandle(); diff --git a/src/coreclr/vm/dllimport.h b/src/coreclr/vm/dllimport.h index b61f8a7f35c3ac..915c298bc5ef54 100644 --- a/src/coreclr/vm/dllimport.h +++ b/src/coreclr/vm/dllimport.h @@ -203,6 +203,7 @@ enum ILStubTypes ILSTUB_TAILCALL_CALLTARGET = 0x80000009, ILSTUB_STATIC_VIRTUAL_DISPATCH_STUB = 0x8000000A, ILSTUB_DELEGATE_INVOKE_METHOD = 0x8000000B, + ILSTUB_DELEGATE_SHUFFLE_THUNK = 0x8000000C, }; #ifdef FEATURE_COMINTEROP @@ -240,6 +241,7 @@ inline bool SF_IsInstantiatingStub (DWORD dwStubFlags) { LIMITED_METHOD_CON #endif inline bool SF_IsTailCallStoreArgsStub (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags == ILSTUB_TAILCALL_STOREARGS); } inline bool SF_IsTailCallCallTargetStub (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags == ILSTUB_TAILCALL_CALLTARGET); } +inline bool SF_IsDelegateShuffleThunk (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return (dwStubFlags == ILSTUB_DELEGATE_SHUFFLE_THUNK); } inline bool SF_IsCOMStub (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return COM_ONLY(dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_COM)); } inline bool SF_IsCOMLateBoundStub (DWORD dwStubFlags) { LIMITED_METHOD_CONTRACT; return COM_ONLY(dwStubFlags < NDIRECTSTUB_FL_INVALID && 0 != (dwStubFlags & NDIRECTSTUB_FL_COMLATEBOUND)); } diff --git a/src/coreclr/vm/ilstubcache.cpp b/src/coreclr/vm/ilstubcache.cpp index 89cac9112463a0..655a6b2d169f3f 100644 --- a/src/coreclr/vm/ilstubcache.cpp +++ b/src/coreclr/vm/ilstubcache.cpp @@ -156,7 +156,8 @@ namespace case DynamicMethodDesc::StubWrapperDelegate: return "IL_STUB_WrapperDelegate_Invoke"; case DynamicMethodDesc::StubTailCallStoreArgs: return "IL_STUB_StoreTailCallArgs"; case DynamicMethodDesc::StubTailCallCallTarget: return "IL_STUB_CallTailCallTarget"; - case DynamicMethodDesc::StubVirtualStaticMethodDispatch: return "IL_STUB_bVirtualStaticMethodDispatch"; + case DynamicMethodDesc::StubVirtualStaticMethodDispatch: return "IL_STUB_VirtualStaticMethodDispatch"; + case DynamicMethodDesc::StubDelegateShuffleThunk: return "IL_STUB_DelegateShuffleThunk"; default: UNREACHABLE_MSG("Unknown stub type"); } @@ -303,6 +304,11 @@ MethodDesc* ILStubCache::CreateNewMethodDesc(LoaderHeap* pCreationHeap, MethodTa pMD->SetILStubType(DynamicMethodDesc::StubVirtualStaticMethodDispatch); } else + if (SF_IsDelegateShuffleThunk(dwStubFlags)) + { + pMD->SetILStubType(DynamicMethodDesc::StubDelegateShuffleThunk); + } + else { // mark certain types of stub MDs with random flags so ILStubManager recognizes them if (SF_IsReverseStub(dwStubFlags)) diff --git a/src/coreclr/vm/loaderallocator.hpp b/src/coreclr/vm/loaderallocator.hpp index 2bce73a85cc241..f2a200ef761730 100644 --- a/src/coreclr/vm/loaderallocator.hpp +++ b/src/coreclr/vm/loaderallocator.hpp @@ -67,7 +67,7 @@ class CodeRangeMapRangeList : public RangeList ~CodeRangeMapRangeList() { LIMITED_METHOD_CONTRACT; - RemoveRangesWorker(_id, NULL, NULL); + RemoveRangesWorker(_id); } StubCodeBlockKind GetCodeBlockKind() @@ -132,7 +132,7 @@ class CodeRangeMapRangeList : public RangeList #endif // DACCESS_COMPILE } - virtual void RemoveRangesWorker(void *id, const BYTE *start, const BYTE *end) + virtual void RemoveRangesWorker(void *id) { CONTRACTL { @@ -142,10 +142,6 @@ class CodeRangeMapRangeList : public RangeList CONTRACTL_END; #ifndef DACCESS_COMPILE - // This implementation only works for the case where the RangeList is used in a single LoaderHeap - _ASSERTE(start == NULL); - _ASSERTE(end == NULL); - SimpleWriteLockHolder lh(&_RangeListRWLock); _ASSERTE(id == _id || (_id == NULL && _starts.IsEmpty())); diff --git a/src/coreclr/vm/lockedrangelist.h b/src/coreclr/vm/lockedrangelist.h index 8f3b7acfc30205..44e5bb63c444a3 100644 --- a/src/coreclr/vm/lockedrangelist.h +++ b/src/coreclr/vm/lockedrangelist.h @@ -40,11 +40,11 @@ class LockedRangeList : public RangeList return RangeList::AddRangeWorker(start,end,id); } - virtual void RemoveRangesWorker(void *id, const BYTE *start = NULL, const BYTE *end = NULL) + virtual void RemoveRangesWorker(void *id) { WRAPPER_NO_CONTRACT; SimpleWriteLockHolder lh(&m_RangeListRWLock); - RangeList::RemoveRangesWorker(id,start,end); + RangeList::RemoveRangesWorker(id); } virtual BOOL IsInRangeWorker(TADDR address, TADDR *pID = NULL) diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index 956765b76ff050..a3f4d4feeae788 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -2512,6 +2512,7 @@ class DynamicMethodDesc : public StoredSigMethodDesc StubTailCallCallTarget, StubVirtualStaticMethodDispatch, + StubDelegateShuffleThunk, StubDelegateInvokeMethod, @@ -2682,6 +2683,12 @@ class DynamicMethodDesc : public StoredSigMethodDesc return GetILStubType() == DynamicMethodDesc::StubUnboxingIL; } #endif + bool IsDelegateShuffleThunk() const + { + LIMITED_METHOD_DAC_CONTRACT; + _ASSERTE(IsILStub()); + return GetILStubType() == DynamicMethodDesc::StubDelegateShuffleThunk; + } // Whether the stub takes a context argument that is an interop MethodDesc. bool HasMDContextArg() const diff --git a/src/coreclr/vm/riscv64/cgencpu.h b/src/coreclr/vm/riscv64/cgencpu.h index 4a2ab29fc95b11..c08f6a144f5562 100644 --- a/src/coreclr/vm/riscv64/cgencpu.h +++ b/src/coreclr/vm/riscv64/cgencpu.h @@ -360,15 +360,6 @@ struct FloatReg WORD Mask() const { return 1 << reg; } }; -struct CondCode -{ - int cond; - CondCode(int cond):cond(cond) - { - _ASSERTE(0 <= cond && cond < 16); - } -}; - const IntReg RegSp = IntReg(2); const IntReg RegFp = IntReg(8); const IntReg RegRa = IntReg(1); @@ -400,15 +391,15 @@ class StubLinkerCPU : public StubLinker void EmitMovConstant(IntReg target, UINT64 constant); void EmitJumpRegister(IntReg regTarget); void EmitMovReg(IntReg dest, IntReg source); - void EmitMovReg(FloatReg dest, FloatReg source); - void EmitSubImm(IntReg Xd, IntReg Xn, unsigned int value); - void EmitAddImm(IntReg Xd, IntReg Xn, unsigned int value); + void EmitSubImm(IntReg Xd, IntReg Xn, int value); + void EmitAddImm(IntReg Xd, IntReg Xn, int value); void EmitSllImm(IntReg Xd, IntReg Xn, unsigned int value); void EmitLuImm(IntReg Xd, unsigned int value); void EmitLoad(IntReg dest, IntReg srcAddr, int offset = 0); void EmitLoad(FloatReg dest, IntReg srcAddr, int offset = 0); + void EmitStore(IntReg src, IntReg destAddr, int offset = 0); void EmitStore(FloatReg src, IntReg destAddr, int offset = 0); diff --git a/src/coreclr/vm/riscv64/stubs.cpp b/src/coreclr/vm/riscv64/stubs.cpp index 1273bb6e739e7f..2d947b231eb9ca 100644 --- a/src/coreclr/vm/riscv64/stubs.cpp +++ b/src/coreclr/vm/riscv64/stubs.cpp @@ -1066,19 +1066,12 @@ void StubLinkerCPU::EmitMovConstant(IntReg reg, UINT64 imm) // Since ADDIW use sign extension for immediate // we have to adjust higher 19 bit loaded by LUI - // for case when low part is bigger than 0x800. + // for case when the low 12-bit part is negative. UINT32 high19 = (high31 + 0x800) >> 12; EmitLuImm(reg, high19); - if (high31 & 0x800) - { - // EmitAddImm does not allow negative immediate values, so use EmitSubImm. - EmitSubImm(reg, reg, (~high31 + 1) & 0xFFF); - } - else - { - EmitAddImm(reg, reg, high31 & 0x7FF); - } + int low12 = int(high31) << (32-12) >> (32-12); + EmitAddImm(reg, reg, low12); // And load remaining part by batches of 11 bits size. INT32 remainingShift = msb - 30; @@ -1110,10 +1103,6 @@ void StubLinkerCPU::EmitMovConstant(IntReg reg, UINT64 imm) } } -void StubLinkerCPU::EmitJumpRegister(IntReg regTarget) -{ - Emit32(0x00000067 | (regTarget << 15)); -} void StubLinkerCPU::EmitProlog(unsigned short cIntRegArgs, unsigned short cFpRegArgs, unsigned short cbStackSpace) { @@ -1186,8 +1175,9 @@ void StubLinkerCPU::EmitProlog(unsigned short cIntRegArgs, unsigned short cFpReg EmitStore(RegFp, RegSp, cbStackSpace); EmitStore(RegRa, RegSp, cbStackSpace + sizeof(void*)); - // 3. Set the frame pointer - EmitMovReg(RegFp, RegSp); + // 3. Set the frame pointer to the Canonical Frame Address or CFA, which is the stack pointer value on entry to the + // current procedure + EmitAddImm(RegFp, RegSp, totalPaddedFrameSize); // 4. Store floating point argument registers _ASSERTE(cFpRegArgs <= 8); @@ -1282,51 +1272,78 @@ static unsigned BTypeInstr(unsigned opcode, unsigned funct3, unsigned rs1, unsig return opcode | (immLo4 << 8) | (funct3 << 12) | (rs1 << 15) | (rs2 << 20) | (immHi6 << 25) | (immLo1 << 7) | (immHi1 << 31); } +static const char* intRegAbiNames[] = { + "zero", "ra", "sp", "gp", "tp", + "t0", "t1", "t2", + "fp", "s1", + "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", + "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11", + "t3", "t4", "t5", "t6" +}; + +static const char* fpRegAbiNames[] = { + "ft0", "ft1", "ft2", "ft3", "ft4", "ft5", "ft6", "ft7", + "fs0", "fs1", + "fa0", "fa1", "fa2", "fa3", "fa4", "fa5", "fa6", "fa7", + "fs2", "fs3", "fs4", "fs5", "fs6", "fs7", "fs8", "fs9", "fs10", "fs11", + "ft8", "ft9", "ft10", "ft11" +}; + +void StubLinkerCPU::EmitJumpRegister(IntReg regTarget) +{ + Emit32(0x00000067 | (regTarget << 15)); + LOG((LF_STUBS, LL_EVERYTHING, "jalr zero, 0(%s)\n", intRegAbiNames[regTarget])); +} + void StubLinkerCPU::EmitLoad(IntReg dest, IntReg srcAddr, int offset) { Emit32(ITypeInstr(0x3, 0x3, dest, srcAddr, offset)); // ld + LOG((LF_STUBS, LL_EVERYTHING, "ld %s, %i(%s)\n", intRegAbiNames[dest], offset, intRegAbiNames[srcAddr])); } void StubLinkerCPU::EmitLoad(FloatReg dest, IntReg srcAddr, int offset) { Emit32(ITypeInstr(0x7, 0x3, dest, srcAddr, offset)); // fld + LOG((LF_STUBS, LL_EVERYTHING, "fld %s, %i(%s)\n", fpRegAbiNames[dest], offset, intRegAbiNames[srcAddr])); } void StubLinkerCPU:: EmitStore(IntReg src, IntReg destAddr, int offset) { Emit32(STypeInstr(0x23, 0x3, destAddr, src, offset)); // sd + LOG((LF_STUBS, LL_EVERYTHING, "sd %s, %i(%s)\n", intRegAbiNames[src], offset, intRegAbiNames[destAddr])); } void StubLinkerCPU::EmitStore(FloatReg src, IntReg destAddr, int offset) { Emit32(STypeInstr(0x27, 0x3, destAddr, src, offset)); // fsd + LOG((LF_STUBS, LL_EVERYTHING, "fsd %s, %i(%s)\n", fpRegAbiNames[src], offset, intRegAbiNames[destAddr])); } void StubLinkerCPU::EmitMovReg(IntReg Xd, IntReg Xm) { EmitAddImm(Xd, Xm, 0); } -void StubLinkerCPU::EmitMovReg(FloatReg dest, FloatReg source) +void StubLinkerCPU::EmitSubImm(IntReg Xd, IntReg Xn, int value) { - Emit32(RTypeInstr(0x53, 0, 0x11, dest, source, source)); // fsgnj.d + EmitAddImm(Xd, Xn, -value); } - -void StubLinkerCPU::EmitSubImm(IntReg Xd, IntReg Xn, unsigned int value) -{ - _ASSERTE(value <= 0x800); - EmitAddImm(Xd, Xn, ~value + 0x1); -} -void StubLinkerCPU::EmitAddImm(IntReg Xd, IntReg Xn, unsigned int value) +void StubLinkerCPU::EmitAddImm(IntReg Xd, IntReg Xn, int value) { Emit32(ITypeInstr(0x13, 0, Xd, Xn, value)); // addi + if (value) + LOG((LF_STUBS, LL_EVERYTHING, "addi %s, %s, %i\n", intRegAbiNames[Xd], intRegAbiNames[Xn], value)); + else + LOG((LF_STUBS, LL_EVERYTHING, "mv %s, %s\n", intRegAbiNames[Xd], intRegAbiNames[Xn])); } void StubLinkerCPU::EmitSllImm(IntReg Xd, IntReg Xn, unsigned int value) { _ASSERTE(!(value >> 6)); Emit32(ITypeInstr(0x13, 0x1, Xd, Xn, value)); // slli + LOG((LF_STUBS, LL_EVERYTHING, "slli %s, %s, %u\n", intRegAbiNames[Xd], intRegAbiNames[Xn], value)); } void StubLinkerCPU::EmitLuImm(IntReg Xd, unsigned int value) { _ASSERTE(value <= 0xFFFFF); Emit32((DWORD)(0x00000037 | (value << 12) | (Xd << 7))); // lui Xd, value + LOG((LF_STUBS, LL_EVERYTHING, "lui %s, %u\n", intRegAbiNames[Xd], value)); } void StubLinkerCPU::Init() @@ -1334,10 +1351,35 @@ void StubLinkerCPU::Init() new (gBranchIF) BranchInstructionFormat(); } +static bool InRegister(UINT16 ofs) +{ + _ASSERTE(ofs != ShuffleEntry::SENTINEL); + return (ofs & ShuffleEntry::REGMASK); +} +static bool IsRegisterFloating(UINT16 ofs) +{ + _ASSERTE(InRegister(ofs)); + return (ofs & ShuffleEntry::FPREGMASK); +} + +static const int argRegBase = 10; // first argument register: a0, fa0 +static const IntReg lastIntArgReg = argRegBase + NUM_ARGUMENT_REGISTERS - 1; // a7 +static const IntReg intTempReg = 29; // t4 + +static int GetRegister(UINT16 ofs) +{ + _ASSERTE(InRegister(ofs)); + return (ofs & ShuffleEntry::OFSREGMASK) + argRegBase; +} +static unsigned GetStackSlot(UINT16 ofs) +{ + _ASSERTE(!InRegister(ofs)); + return ofs; +} + // Emits code to adjust arguments for static delegate target. VOID StubLinkerCPU::EmitShuffleThunk(ShuffleEntry *pShuffleEntryArray) { - static const int argRegBase = 10; // first argument register: a0, fa0 static const IntReg t6 = 31, t5 = 30, a0 = argRegBase + 0; // On entry a0 holds the delegate instance. Look up the real target address stored in the MethodPtrAux // field and saved in t6. Tailcall to the target method after re-arranging the arguments @@ -1345,113 +1387,38 @@ VOID StubLinkerCPU::EmitShuffleThunk(ShuffleEntry *pShuffleEntryArray) // load the indirection cell into t5 used by ResolveWorkerAsmStub EmitAddImm(t5, a0, DelegateObject::GetOffsetOfMethodPtrAux()); - int delay_index[8] = {-1}; - bool is_store = false; - UINT16 index = 0; - int i = 0; - for (ShuffleEntry* pEntry = pShuffleEntryArray; pEntry->srcofs != ShuffleEntry::SENTINEL; pEntry++, i++) + const ShuffleEntry* entry = pShuffleEntryArray; + // Shuffle integer argument registers + for (; entry->srcofs != ShuffleEntry::SENTINEL && InRegister(entry->dstofs) && InRegister(entry->srcofs); ++entry) { - if (pEntry->srcofs & ShuffleEntry::REGMASK) - { - // Source in register, destination in register - - // Both the srcofs and dstofs must be of the same kind of registers - float or general purpose. - // If source is present in register then destination may be a stack-slot. - _ASSERTE(((pEntry->dstofs & ShuffleEntry::FPREGMASK) == (pEntry->srcofs & ShuffleEntry::FPREGMASK)) || !(pEntry->dstofs & (ShuffleEntry::FPREGMASK | ShuffleEntry::REGMASK))); - _ASSERTE((pEntry->dstofs & ShuffleEntry::OFSREGMASK) <= 8);//should amend for offset! - _ASSERTE((pEntry->srcofs & ShuffleEntry::OFSREGMASK) <= 8); - - if (pEntry->srcofs & ShuffleEntry::FPREGMASK) - { - int j = 1; - while (pEntry[j].srcofs & ShuffleEntry::FPREGMASK) - { - j++; - } - assert((pEntry->dstofs - pEntry->srcofs) == index); - assert(8 > index); - - int tmp_reg = 0; // ft0. - ShuffleEntry* tmp_entry = pShuffleEntryArray + delay_index[0]; - while (index) - { - EmitLoad(FloatReg(tmp_reg), RegSp, tmp_entry->srcofs * sizeof(void*)); - tmp_reg++; - index--; - tmp_entry++; - } - - j -= 1; - tmp_entry = pEntry + j; - i += j; - while (pEntry[j].srcofs & ShuffleEntry::FPREGMASK) - { - FloatReg src = (pEntry[j].srcofs & ShuffleEntry::OFSREGMASK) + argRegBase; - if (pEntry[j].dstofs & ShuffleEntry::FPREGMASK) { - FloatReg dst = (pEntry[j].dstofs & ShuffleEntry::OFSREGMASK) + argRegBase; - EmitMovReg(dst, src); - } - else - { - EmitStore(src, RegSp, pEntry[j].dstofs * sizeof(void*)); - } - j--; - } - - assert(tmp_reg <= 7); - while (tmp_reg > 0) - { - tmp_reg--; - EmitMovReg(FloatReg(index + argRegBase), FloatReg(tmp_reg)); - index++; - } - index = 0; - pEntry = tmp_entry; - } - else - { - assert(pEntry->dstofs & ShuffleEntry::REGMASK); - IntReg dst = (pEntry->dstofs & ShuffleEntry::OFSREGMASK) + argRegBase; - IntReg src = (pEntry->srcofs & ShuffleEntry::OFSREGMASK) + argRegBase; - assert(dst < src); - EmitMovReg(dst, src); - } - } - else if (pEntry->dstofs & ShuffleEntry::REGMASK) - { - // source must be on the stack - _ASSERTE(!(pEntry->srcofs & ShuffleEntry::REGMASK)); + _ASSERTE(!IsRegisterFloating(entry->srcofs)); + _ASSERTE(!IsRegisterFloating(entry->dstofs)); + IntReg src = GetRegister(entry->srcofs); + IntReg dst = GetRegister(entry->dstofs); + _ASSERTE((src - dst) == 1 || (src - dst) == 2); + EmitMovReg(dst, src); + } - int dstReg = (pEntry->dstofs & ShuffleEntry::OFSREGMASK) + argRegBase; - int srcOfs = (pEntry->srcofs & ShuffleEntry::OFSMASK) * sizeof(void*); - if (pEntry->dstofs & ShuffleEntry::FPREGMASK) - { - if (!is_store) - { - delay_index[index++] = i; - continue; - } - EmitLoad(FloatReg(dstReg), RegSp, srcOfs); - } - else - { - EmitLoad(IntReg(dstReg), RegSp, srcOfs); - } - } - else + if (entry->srcofs != ShuffleEntry::SENTINEL) + { + _ASSERTE(!IsRegisterFloating(entry->dstofs)); + _ASSERTE(GetStackSlot(entry->srcofs) == 0); + _ASSERTE(lastIntArgReg == GetRegister(entry->dstofs)); + EmitLoad(lastIntArgReg, RegSp, 0); + ++entry; + + // All further shuffling is (stack) <- (stack+1) + for (unsigned dst = 0; entry->srcofs != ShuffleEntry::SENTINEL; ++entry, ++dst) { - // source & dest must be on the stack - _ASSERTE(!(pEntry->srcofs & ShuffleEntry::REGMASK)); - _ASSERTE(!(pEntry->dstofs & ShuffleEntry::REGMASK)); - - IntReg t4 = 29; - EmitLoad(t4, RegSp, pEntry->srcofs * sizeof(void*)); - EmitStore(t4, RegSp, pEntry->dstofs * sizeof(void*)); + unsigned src = dst + 1; + _ASSERTE(src == GetStackSlot(entry->srcofs)); + _ASSERTE(dst == GetStackSlot(entry->dstofs)); + EmitLoad (intTempReg, RegSp, src * sizeof(void*)); + EmitStore(intTempReg, RegSp, dst * sizeof(void*)); } } - // Tailcall to target - // jalr x0, 0(t6) - EmitJumpRegister(t6); + + EmitJumpRegister(t6); // Tailcall to target } // Emits code to adjust arguments for static delegate target. @@ -1461,26 +1428,24 @@ VOID StubLinkerCPU::EmitComputedInstantiatingMethodStub(MethodDesc* pSharedMD, s for (ShuffleEntry* pEntry = pShuffleEntryArray; pEntry->srcofs != ShuffleEntry::SENTINEL; pEntry++) { - _ASSERTE(pEntry->dstofs & ShuffleEntry::REGMASK); - _ASSERTE(pEntry->srcofs & ShuffleEntry::REGMASK); - _ASSERTE(!(pEntry->dstofs & ShuffleEntry::FPREGMASK)); - _ASSERTE(!(pEntry->srcofs & ShuffleEntry::FPREGMASK)); + _ASSERTE(!IsRegisterFloating(pEntry->srcofs)); + _ASSERTE(!IsRegisterFloating(pEntry->dstofs)); _ASSERTE(pEntry->dstofs != ShuffleEntry::HELPERREG); _ASSERTE(pEntry->srcofs != ShuffleEntry::HELPERREG); - - EmitMovReg(IntReg((pEntry->dstofs & ShuffleEntry::OFSREGMASK) + 10), IntReg((pEntry->srcofs & ShuffleEntry::OFSREGMASK) + 10)); + EmitMovReg(IntReg(GetRegister(pEntry->dstofs)), IntReg(GetRegister(pEntry->srcofs))); } MetaSig msig(pSharedMD); ArgIterator argit(&msig); + static const IntReg a0 = argRegBase + 0; if (argit.HasParamType()) { ArgLocDesc sInstArgLoc; argit.GetParamTypeLoc(&sInstArgLoc); int regHidden = sInstArgLoc.m_idxGenReg; _ASSERTE(regHidden != -1); - regHidden += 10;//NOTE: RISCV64 should start at a0=10; + regHidden += argRegBase;//NOTE: RISCV64 should start at a0=10; if (extraArg == NULL) { @@ -1488,8 +1453,7 @@ VOID StubLinkerCPU::EmitComputedInstantiatingMethodStub(MethodDesc* pSharedMD, s { // Unboxing stub case // Fill param arg with methodtable of this pointer - // ld regHidden, a0, 0 - EmitLoad(IntReg(regHidden), IntReg(10)); + EmitLoad(IntReg(regHidden), a0); } } else @@ -1502,8 +1466,7 @@ VOID StubLinkerCPU::EmitComputedInstantiatingMethodStub(MethodDesc* pSharedMD, s { // Unboxing stub case // Address of the value type is address of the boxed instance plus sizeof(MethodDesc*). - // addi a0, a0, sizeof(MethodDesc*) - EmitAddImm(IntReg(10), IntReg(10), sizeof(MethodDesc*)); + EmitAddImm(a0, a0, sizeof(MethodDesc*)); } // Tail call the real target. @@ -1563,13 +1526,6 @@ void StubLinkerCPU::EmitCallManagedMethod(MethodDesc *pMD, BOOL fTailCall) while (p < pStart + cbAligned) { *(DWORD*)p = 0xffffff0f/*badcode*/; p += 4; }\ ClrFlushInstructionCache(pStart, cbAligned); \ return (PCODE)pStart -// Uses x8 as scratch register to store address of data label -// After load x8 is increment to point to next data -// only accepts positive offsets -static void LoadRegPair(BYTE* p, int reg1, int reg2, UINT32 offset) -{ - _ASSERTE(!"RISCV64: not implementation on riscv64!!!"); -} PCODE DynamicHelpers::CreateHelper(LoaderAllocator * pAllocator, TADDR arg, PCODE target) { diff --git a/src/coreclr/vm/stubmgr.cpp b/src/coreclr/vm/stubmgr.cpp index 31a01fc83986f5..225310c646cd15 100644 --- a/src/coreclr/vm/stubmgr.cpp +++ b/src/coreclr/vm/stubmgr.cpp @@ -1237,15 +1237,6 @@ BOOL StubLinkStubManager::TraceDelegateObject(BYTE* pbDel, TraceDestination *tra #endif // DACCESS_COMPILE -void StubLinkStubManager::RemoveStubRange(BYTE* start, UINT length) -{ - WRAPPER_NO_CONTRACT; - _ASSERTE(start != NULL && length > 0); - - BYTE* end = start + length; - GetRangeList()->RemoveRanges((LPVOID)start, start, end); -} - BOOL StubLinkStubManager::CheckIsStub_Internal(PCODE stubStartAddress) { WRAPPER_NO_CONTRACT; @@ -1879,6 +1870,11 @@ BOOL ILStubManager::TraceManager(Thread *thread, LOG((LF_CORDB, LL_INFO1000, "ILSM::TraceManager: Delegate Invoke Method\n")); return StubLinkStubManager::TraceDelegateObject((BYTE*)pThis, trace); } + else if (pStubMD->IsDelegateShuffleThunk()) + { + LOG((LF_CORDB, LL_INFO1000, "ILSM::TraceManager: Delegate Shuffle Thunk\n")); + return TraceShuffleThunk(trace, pContext, pRetAddr); + } else { LOG((LF_CORDB, LL_INFO1000, "ILSM::TraceManager: No known target, IL Stub is a leaf\n")); diff --git a/src/coreclr/vm/stubmgr.h b/src/coreclr/vm/stubmgr.h index 6d3174366a729b..9c8deefcc37358 100644 --- a/src/coreclr/vm/stubmgr.h +++ b/src/coreclr/vm/stubmgr.h @@ -475,8 +475,6 @@ class StubLinkStubManager : public StubManager return PTR_RangeList(addr); } - void RemoveStubRange(BYTE* start, UINT length); - virtual BOOL CheckIsStub_Internal(PCODE stubStartAddress); virtual BOOL DoTraceStub(PCODE stubStartAddress, TraceDestination *trace); diff --git a/src/tests/JIT/Directed/StructABI/EmptyStructs.cs b/src/tests/JIT/Directed/StructABI/EmptyStructs.cs index 423015b831b7a8..6be88b68dea71e 100644 --- a/src/tests/JIT/Directed/StructABI/EmptyStructs.cs +++ b/src/tests/JIT/Directed/StructABI/EmptyStructs.cs @@ -1437,7 +1437,6 @@ public static void Test_PackedEmptyFloatLong_OnStack_ByReflection_RiscV() } #endregion - #region PackedFloatEmptyByte_RiscVTests [StructLayout(LayoutKind.Sequential, Pack=1)] public struct PackedFloatEmptyByte @@ -1582,4 +1581,653 @@ public static void Test_PackedFloatEmptyByte_OnStack_ByReflection_RiscV() Assert.Equal(expected, managed); } #endregion + +#region ShufflingThunks_RiscVTests + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ShufflingThunk_EmptyFloatEmpty5Byte_RiscV( + int a1_to_a0, int a2_to_a1, int a3_to_a2, int a4_to_a3, int a5_to_a4, int a6_to_a5, int a7_to_a6, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, + EmptyFloatEmpty5Byte stack0_stack1_to_fa7_a7, + int stack2_to_stack0, float fa7_to_stack1) + { + Assert.Equal(0, a1_to_a0); + Assert.Equal(1, a2_to_a1); + Assert.Equal(2, a3_to_a2); + Assert.Equal(3, a4_to_a3); + Assert.Equal(4, a5_to_a4); + Assert.Equal(5, a6_to_a5); + Assert.Equal(6, a7_to_a6); + Assert.Equal(0f, fa0); + Assert.Equal(1f, fa1); + Assert.Equal(2f, fa2); + Assert.Equal(3f, fa3); + Assert.Equal(4f, fa4); + Assert.Equal(5f, fa5); + Assert.Equal(6f, fa6); + Assert.Equal(EmptyFloatEmpty5Byte.Get(), stack0_stack1_to_fa7_a7); + Assert.Equal(7, stack2_to_stack0); + Assert.Equal(7f, fa7_to_stack1); + } + + [Fact] + public static void Test_ShufflingThunk_EmptyFloatEmpty5Byte_RiscV() + { + var getDelegate = [MethodImpl(MethodImplOptions.NoOptimization)] () + => ShufflingThunk_EmptyFloatEmpty5Byte_RiscV; + getDelegate()(0, 1, 2, 3, 4, 5, 6, 0f, 1f, 2f, 3f, 4f, 5f, 6f, + EmptyFloatEmpty5Byte.Get(), 7, 7f); + } + + + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ShufflingThunk_EmptyFloatEmpty5Sbyte_Empty8Float_RiscV( + int a1_to_a0, int a2_to_a1, int a3_to_a2, int a4_to_a3, int a5_to_a4, int a6_to_a5, int a7_to_a6, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, + EmptyFloatEmpty5Sbyte stack0_stack1_to_fa7_a7, + int stack2_to_stack0, + Empty8Float fa7_to_stack1_stack2) + { + Assert.Equal(0, a1_to_a0); + Assert.Equal(1, a2_to_a1); + Assert.Equal(2, a3_to_a2); + Assert.Equal(3, a4_to_a3); + Assert.Equal(4, a5_to_a4); + Assert.Equal(5, a6_to_a5); + Assert.Equal(6, a7_to_a6); + Assert.Equal(0f, fa0); + Assert.Equal(1f, fa1); + Assert.Equal(2f, fa2); + Assert.Equal(3f, fa3); + Assert.Equal(4f, fa4); + Assert.Equal(5f, fa5); + Assert.Equal(6f, fa6); + Assert.Equal(EmptyFloatEmpty5Sbyte.Get(), stack0_stack1_to_fa7_a7); + Assert.Equal(7, stack2_to_stack0); + Assert.Equal(Empty8Float.Get(), fa7_to_stack1_stack2); + } + + [Fact] + public static void Test_ShufflingThunk_EmptyFloatEmpty5Sbyte_Empty8Float_RiscV() + { + var getDelegate = [MethodImpl(MethodImplOptions.NoOptimization)] () + => ShufflingThunk_EmptyFloatEmpty5Sbyte_Empty8Float_RiscV; + getDelegate()(0, 1, 2, 3, 4, 5, 6, 0f, 1f, 2f, 3f, 4f, 5f, 6f, + EmptyFloatEmpty5Sbyte.Get(), 7, Empty8Float.Get()); + } + + + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ShufflingThunk_EmptyUshortAndDouble_FloatEmpty8Float_Empty8Float_RiscV( + int a1_to_a0, int a2_to_a1, int a3_to_a2, int a4_to_a3, int a5_to_a4, int a6_to_a5, int a7_to_a6, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, + EmptyUshortAndDouble stack0_stack1_to_a7_fa6, // 1st lowering + FloatEmpty8Float fa6_fa7_to_stack0_stack1, // delowering + Empty8Float stack1_stack2_to_fa7, // 2nd lowering + int stack3_to_stack2) + { + Assert.Equal(0, a1_to_a0); + Assert.Equal(1, a2_to_a1); + Assert.Equal(2, a3_to_a2); + Assert.Equal(3, a4_to_a3); + Assert.Equal(4, a5_to_a4); + Assert.Equal(5, a6_to_a5); + Assert.Equal(6, a7_to_a6); + Assert.Equal(0f, fa0); + Assert.Equal(1f, fa1); + Assert.Equal(2f, fa2); + Assert.Equal(3f, fa3); + Assert.Equal(4f, fa4); + Assert.Equal(5f, fa5); + Assert.Equal(EmptyUshortAndDouble.Get(), stack0_stack1_to_a7_fa6); + Assert.Equal(FloatEmpty8Float.Get(), fa6_fa7_to_stack0_stack1); + Assert.Equal(Empty8Float.Get(), stack1_stack2_to_fa7); + Assert.Equal(7, stack3_to_stack2); + } + + [Fact] + public static void Test_ShufflingThunk_EmptyUshortAndDouble_FloatEmpty8Float_Empty8Float_RiscV() + { + var getDelegate = [MethodImpl(MethodImplOptions.NoOptimization)] () + => ShufflingThunk_EmptyUshortAndDouble_FloatEmpty8Float_Empty8Float_RiscV; + getDelegate()(0, 1, 2, 3, 4, 5, 6, 0f, 1f, 2f, 3f, 4f, 5f, + EmptyUshortAndDouble.Get(), FloatEmpty8Float.Get(), Empty8Float.Get(), 7); + } + + + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ShufflingThunk_FloatEmptyShort_DoubleFloatNestedEmpty_Float_RiscV( + int a1_to_a0, int a2_to_a1, int a3_to_a2, int a4_to_a3, int a5_to_a4, int a6_to_a5, int a7_to_a6, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, + FloatEmptyShort stack0_to_fa6_a7, // frees 1 stack slot + int stack1_to_stack0, + DoubleFloatNestedEmpty fa6_fa7_to_stack1_stack2, // takes 2 stack slots + int stack2_to_stack3, // shuffle stack slots to the right + int stack3_to_stack4, + Empty8Float stack4_stack5_to_fa7, // frees 2 stack slots + int stack6_to_stack5, // shuffle stack slots to the left + int stack7_to_stack6) + { + Assert.Equal(0, a1_to_a0); + Assert.Equal(1, a2_to_a1); + Assert.Equal(2, a3_to_a2); + Assert.Equal(3, a4_to_a3); + Assert.Equal(4, a5_to_a4); + Assert.Equal(5, a6_to_a5); + Assert.Equal(6, a7_to_a6); + Assert.Equal(0f, fa0); + Assert.Equal(1f, fa1); + Assert.Equal(2f, fa2); + Assert.Equal(3f, fa3); + Assert.Equal(4f, fa4); + Assert.Equal(5f, fa5); + Assert.Equal(FloatEmptyShort.Get(), stack0_to_fa6_a7); + Assert.Equal(7, stack1_to_stack0); + Assert.Equal(DoubleFloatNestedEmpty.Get(), fa6_fa7_to_stack1_stack2); + Assert.Equal(8, stack2_to_stack3); + Assert.Equal(9, stack3_to_stack4); + Assert.Equal(Empty8Float.Get(), stack4_stack5_to_fa7); + Assert.Equal(10, stack6_to_stack5); + Assert.Equal(11, stack7_to_stack6); + } + + [Fact] + public static void Test_ShufflingThunk_FloatEmptyShort_DoubleFloatNestedEmpty_Float_RiscV() + { + var getDelegate = [MethodImpl(MethodImplOptions.NoOptimization)] () + => ShufflingThunk_FloatEmptyShort_DoubleFloatNestedEmpty_Float_RiscV; + getDelegate()(0, 1, 2, 3, 4, 5, 6, 0f, 1f, 2f, 3f, 4f, 5f, + FloatEmptyShort.Get(), 7, DoubleFloatNestedEmpty.Get(), 8, 9, Empty8Float.Get(), 10, 11); + } + + + + public struct FloatFloat + { + public float Float0; + public float Float1; + + public static FloatFloat Get() + => new FloatFloat { Float0 = 2.71828f, Float1 = 1.61803f }; + + public override bool Equals(object other) + => other is FloatFloat o && Float0 == o.Float0 && Float1 == o.Float1; + + public override string ToString() + => $"{{Float0:{Float0}, Float1:{Float1}}}"; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ShufflingThunk_PackedEmptyFloatLong_FloatFloat_RiscV( + int a1_to_a0, int a2_to_a1, int a3_to_a2, int a4_to_a3, int a5_to_a4, int a6_to_a5, int a7_to_a6, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, + PackedEmptyFloatLong stack0_stack1_to_fa7_a7, + int stack2_to_stack0, + FloatFloat fa6_fa7_to_stack1) + { + Assert.Equal(0, a1_to_a0); + Assert.Equal(1, a2_to_a1); + Assert.Equal(2, a3_to_a2); + Assert.Equal(3, a4_to_a3); + Assert.Equal(4, a5_to_a4); + Assert.Equal(5, a6_to_a5); + Assert.Equal(6, a7_to_a6); + Assert.Equal(0f, fa0); + Assert.Equal(1f, fa1); + Assert.Equal(2f, fa2); + Assert.Equal(3f, fa3); + Assert.Equal(4f, fa4); + Assert.Equal(5f, fa5); + Assert.Equal(PackedEmptyFloatLong.Get(), stack0_stack1_to_fa7_a7); + Assert.Equal(7, stack2_to_stack0); + Assert.Equal(FloatFloat.Get(), fa6_fa7_to_stack1); + } + + [Fact] + public static void Test_ShufflingThunk_PackedEmptyFloatLong_FloatFloat_RiscV() + { + var getDelegate = [MethodImpl(MethodImplOptions.NoOptimization)] () + => ShufflingThunk_PackedEmptyFloatLong_FloatFloat_RiscV; + getDelegate()(0, 1, 2, 3, 4, 5, 6, 0f, 1f, 2f, 3f, 4f, 5f, + PackedEmptyFloatLong.Get(), 7, FloatFloat.Get()); + } + + + + [StructLayout(LayoutKind.Sequential, Pack=1)] + public struct PackedEmptyUintEmptyFloat + { + public Empty Empty0; + public uint Uint0; + public Empty Empty1; + public float Float0; + + public static PackedEmptyUintEmptyFloat Get() + => new PackedEmptyUintEmptyFloat { Uint0 = 0xB1ed0c1e, Float0 = 2.71828f }; + + public override bool Equals(object other) + => other is PackedEmptyUintEmptyFloat o && Uint0 == o.Uint0 && Float0 == o.Float0; + + public override string ToString() + => $"{{Uint0:{Uint0}, Float0:{Float0}}}"; + } + + [StructLayout(LayoutKind.Sequential, Pack=1)] + public struct PackedEmptyDouble + { + public Empty Empty0; + public double Double0; + + public static PackedEmptyDouble Get() + => new PackedEmptyDouble { Double0 = 1.61803 }; + + public override bool Equals(object other) + => other is PackedEmptyDouble o && Double0 == o.Double0; + + public override string ToString() + => $"{{Double0:{Double0}}}"; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ShufflingThunk_PackedEmptyUintEmptyFloat_PackedEmptyDouble( + int a1_to_a0, int a2_to_a1, int a3_to_a2, int a4_to_a3, int a5_to_a4, int a6_to_a5, int a7_to_a6, + float fa0, float fa1, + PackedEmptyUintEmptyFloat stack0_stack1_to_a7_fa2, + float fa2_to_fa3, float fa3_to_fa4, float fa4_to_fa5, + int stack2_to_stack0, + float fa5_to_fa6, float fa6_to_fa7, + PackedEmptyDouble fa7_to_stack1_stack2) + { + Assert.Equal(0, a1_to_a0); + Assert.Equal(1, a2_to_a1); + Assert.Equal(2, a3_to_a2); + Assert.Equal(3, a4_to_a3); + Assert.Equal(4, a5_to_a4); + Assert.Equal(5, a6_to_a5); + Assert.Equal(6, a7_to_a6); + Assert.Equal(0f, fa0); + Assert.Equal(1f, fa1); + Assert.Equal(PackedEmptyUintEmptyFloat.Get(), stack0_stack1_to_a7_fa2); + Assert.Equal(2f, fa2_to_fa3); + Assert.Equal(3f, fa3_to_fa4); + Assert.Equal(4f, fa4_to_fa5); + Assert.Equal(7, stack2_to_stack0); + Assert.Equal(5f, fa5_to_fa6); + Assert.Equal(6f, fa6_to_fa7); + Assert.Equal(PackedEmptyDouble.Get(), fa7_to_stack1_stack2); + } + + [Fact] + public static void Test_ShufflingThunk_PackedEmptyUintEmptyFloat_PackedEmptyDouble() + { + var getDelegate = [MethodImpl(MethodImplOptions.NoOptimization)] () + => ShufflingThunk_PackedEmptyUintEmptyFloat_PackedEmptyDouble; + getDelegate()(0, 1, 2, 3, 4, 5, 6, 0f, 1f, + PackedEmptyUintEmptyFloat.Get(), 2f, 3f, 4f, 7, 5f, 6f, PackedEmptyDouble.Get()); + } + + + + public struct FloatFloatEmpty + { + public FloatFloat FloatFloat0; + public Empty Empty0; + + public static FloatFloatEmpty Get() + => new FloatFloatEmpty { FloatFloat0 = FloatFloat.Get() }; + + public override bool Equals(object other) + => other is FloatFloatEmpty o && FloatFloat0.Equals(o.FloatFloat0); + + public override string ToString() + => $"{{FloatFloat0:{FloatFloat0}}}"; + } + + public struct FloatEmpty8 + { + public float Float0; + public Eight EightEmpty0; + + public static FloatEmpty8 Get() + => new FloatEmpty8 { Float0 = 2.71828f }; + + public override bool Equals(object other) + => other is FloatEmpty8 o && Float0 == o.Float0; + + public override string ToString() + => $"{{Float0:{Float0}}}"; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ShufflingThunk_FloatEmptyShort_FloatFloatEmpty_FloatEmpty8( + int a1_to_a0, int a2_to_a1, int a3_to_a2, int a4_to_a3, int a5_to_a4, int a6_to_a5, int a7_to_a6, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, + FloatEmptyShort stack0_to_fa6_a7, // frees 1 stack slot + int stack1_to_stack0, + FloatFloatEmpty fa6_fa7_to_stack1_stack2, // takes 2 stack slots + int stack2_to_stack3, // shuffle stack slots to the right + int stack3_to_stack4, + FloatEmpty8 stack4_stack5_to_fa7, // frees 2 stack slots + int stack6_to_stack5, // shuffle stack slots to the left + int stack7_to_stack6) + { + Assert.Equal(0, a1_to_a0); + Assert.Equal(1, a2_to_a1); + Assert.Equal(2, a3_to_a2); + Assert.Equal(3, a4_to_a3); + Assert.Equal(4, a5_to_a4); + Assert.Equal(5, a6_to_a5); + Assert.Equal(6, a7_to_a6); + Assert.Equal(0f, fa0); + Assert.Equal(1f, fa1); + Assert.Equal(2f, fa2); + Assert.Equal(3f, fa3); + Assert.Equal(4f, fa4); + Assert.Equal(5f, fa5); + Assert.Equal(FloatEmptyShort.Get(), stack0_to_fa6_a7); + Assert.Equal(7, stack1_to_stack0); + Assert.Equal(FloatFloatEmpty.Get(), fa6_fa7_to_stack1_stack2); + Assert.Equal(8, stack2_to_stack3); + Assert.Equal(9, stack3_to_stack4); + Assert.Equal(FloatEmpty8.Get(), stack4_stack5_to_fa7); + Assert.Equal(10, stack6_to_stack5); + Assert.Equal(11, stack7_to_stack6); + } + + [Fact] + public static void Test_ShufflingThunk_FloatEmptyShort_FloatFloatEmpty_FloatEmpty8() + { + var getDelegate = [MethodImpl(MethodImplOptions.NoOptimization)] () + => ShufflingThunk_FloatEmptyShort_FloatFloatEmpty_FloatEmpty8; + getDelegate()(0, 1, 2, 3, 4, 5, 6, 0f, 1f, 2f, 3f, 4f, 5f, + FloatEmptyShort.Get(), 7, FloatFloatEmpty.Get(), 8, 9, FloatEmpty8.Get(), 10, 11); + } + + + + [MethodImpl(MethodImplOptions.NoInlining)] + private static DoubleFloatNestedEmpty ShufflingThunk_FloatEmptyShort_DoubleFloatNestedEmpty_RiscV( + int a1_to_a0, int a2_to_a1, int a3_to_a2, int a4_to_a3, int a5_to_a4, int a6_to_a5, int a7_to_a6, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, + FloatEmptyShort stack0_to_fa6_a7, // frees 1 stack slot + int stack1_to_stack0, + DoubleFloatNestedEmpty fa6_fa7_to_stack1_stack2, // takes 2 stack slots + int stack2_to_stack3, // shuffle stack slots to the right + int stack3_to_stack4) // shuffling thunk must grow the stack + { + Assert.Equal(0, a1_to_a0); + Assert.Equal(1, a2_to_a1); + Assert.Equal(2, a3_to_a2); + Assert.Equal(3, a4_to_a3); + Assert.Equal(4, a5_to_a4); + Assert.Equal(5, a6_to_a5); + Assert.Equal(6, a7_to_a6); + Assert.Equal(0f, fa0); + Assert.Equal(1f, fa1); + Assert.Equal(2f, fa2); + Assert.Equal(3f, fa3); + Assert.Equal(4f, fa4); + Assert.Equal(5f, fa5); + Assert.Equal(FloatEmptyShort.Get(), stack0_to_fa6_a7); + Assert.Equal(7, stack1_to_stack0); + Assert.Equal(DoubleFloatNestedEmpty.Get(), fa6_fa7_to_stack1_stack2); + Assert.Equal(8, stack2_to_stack3); + Assert.Equal(9, stack3_to_stack4); + return fa6_fa7_to_stack1_stack2; + } + + [Fact] + public static void Test_ShufflingThunk_FloatEmptyShort_DoubleFloatNestedEmpty_RiscV() + { + var getDelegate = [MethodImpl(MethodImplOptions.NoOptimization)] () + => ShufflingThunk_FloatEmptyShort_DoubleFloatNestedEmpty_RiscV; + var delegat = getDelegate(); + Span stackBeforeCall = stackalloc[] {11, 22, 33, 44}; + DoubleFloatNestedEmpty ret = delegat(0, 1, 2, 3, 4, 5, 6, 0f, 1f, 2f, 3f, 4f, 5f, + FloatEmptyShort.Get(), 7, DoubleFloatNestedEmpty.Get(), 8, 9); + Assert.Equal([11, 22, 33, 44], stackBeforeCall); + Assert.Equal(DoubleFloatNestedEmpty.Get(), ret); + } + + + class EverythingIsFineException : Exception {} + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ShufflingThunk_FloatEmptyShort_Empty8Float_RiscV( + int a1_to_a0, int a2_to_a1, int a3_to_a2, int a4_to_a3, int a5_to_a4, int a6_to_a5, int a7_to_a6, + float fa0, + FloatEmptyShort stack0_to_fa1_a7, // frees 1 stack slot + double fa1_to_fa2, + double fa2_to_fa3, + byte stack1_to_stack0, + short stack2_to_stack1, + double fa3_to_fa4, + float fa4_to_fa5, + int stack3_to_stack2, + float fa5_to_fa6, + double fa6_to_fa7, + long stack4_to_stack3, + Empty8Float fa7_to_stack4_stack5) // takes 2 stack slots, shuffling thunk must grow the stack + { + Assert.Equal(0, a1_to_a0); + Assert.Equal(1, a2_to_a1); + Assert.Equal(2, a3_to_a2); + Assert.Equal(3, a4_to_a3); + Assert.Equal(4, a5_to_a4); + Assert.Equal(5, a6_to_a5); + Assert.Equal(6, a7_to_a6); + Assert.Equal(0f, fa0); + Assert.Equal(FloatEmptyShort.Get(), stack0_to_fa1_a7); + Assert.Equal(1d, fa1_to_fa2); + Assert.Equal(2d, fa2_to_fa3); + Assert.Equal(7, stack1_to_stack0); + Assert.Equal(8, stack2_to_stack1); + Assert.Equal(3d, fa3_to_fa4); + Assert.Equal(4f, fa4_to_fa5); + Assert.Equal(9, stack3_to_stack2); + Assert.Equal(5f, fa5_to_fa6); + Assert.Equal(6d, fa6_to_fa7); + Assert.Equal(10, stack4_to_stack3); + Assert.Equal(Empty8Float.Get(), fa7_to_stack4_stack5); + throw new EverythingIsFineException(); // see if we can walk out of the stack frame laid by the shuffle thunk + } + + [Fact] + public static void Test_ShufflingThunk_FloatEmptyShort_Empty8Float_RiscV() + { + var getDelegate = [MethodImpl(MethodImplOptions.NoOptimization)] () + => ShufflingThunk_FloatEmptyShort_Empty8Float_RiscV; + var delegat = getDelegate(); + Assert.Throws(() => + delegat(0, 1, 2, 3, 4, 5, 6, 0f, + FloatEmptyShort.Get(), 1d, 2d, 7, 8, 3d, 4f, 9, 5f, 6d, 10, Empty8Float.Get()) + ); + } + + + + public struct UintFloat + { + public uint Uint0; + public float Float0; + + public static UintFloat Get() + => new UintFloat { Uint0 = 0xB1ed0c1e, Float0 = 2.71828f }; + + public override bool Equals(object other) + => other is UintFloat o && Uint0 == o.Uint0 && Float0 == o.Float0; + + public override string ToString() + => $"{{Uint0:{Uint0}, Float0:{Float0}}}"; + } + + public struct LongDoubleInt + { + public long Long0; + public double Double0; + public int Int0; + + public static LongDoubleInt Get() + => new LongDoubleInt { Long0 = 0xDadAddedC0ffee, Double0 = 3.14159, Int0 = 0xBabc1a }; + + public override bool Equals(object other) + => other is LongDoubleInt o && Long0 == o.Long0 && Double0 == o.Double0 && Int0 == o.Int0; + + public override string ToString() + => $"{{Long:{Long0}, Double0:{Double0}, Int0:{Int0}}}"; + } + + class ShufflingThunk_MemberGrowsStack_RiscV + { + public static ShufflingThunk_MemberGrowsStack_RiscV TestInstance = + new ShufflingThunk_MemberGrowsStack_RiscV(); + + public delegate FloatEmpty8Float TestDelegate( + ShufflingThunk_MemberGrowsStack_RiscV _this, + int a2_to_a1, int a3_to_a2, int a4_to_a3, int a5_to_a4, int a6_to_a5, int a7_to_a6, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, + UintFloat stack0_to_a7_fa6, // frees 1 stack slot + DoubleFloatNestedEmpty fa6_fa7_to_stack0_stack1); // takes 2 stack slots, shuffling thunk must grow the stack + + [MethodImpl(MethodImplOptions.NoInlining)] + public FloatEmpty8Float TestMethod( + int a2_to_a1, int a3_to_a2, int a4_to_a3, int a5_to_a4, int a6_to_a5, int a7_to_a6, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, + UintFloat stack0_to_a7_fa6, // frees 1 stack slot + DoubleFloatNestedEmpty fa6_fa7_to_stack0_stack1) // takes 2 stack slots, shuffling thunk must grow the stack + { + return StaticTestMethod(this, + a2_to_a1, a3_to_a2, a4_to_a3, a5_to_a4, a6_to_a5, a7_to_a6, + fa0, fa1, fa2, fa3, fa4, fa5, + stack0_to_a7_fa6, + fa6_fa7_to_stack0_stack1); + } + + public static FloatEmpty8Float StaticTestMethod( + ShufflingThunk_MemberGrowsStack_RiscV _this, + int a2_to_a1, int a3_to_a2, int a4_to_a3, int a5_to_a4, int a6_to_a5, int a7_to_a6, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, + UintFloat stack0_to_a7_fa6, // frees 1 stack slot + DoubleFloatNestedEmpty fa6_fa7_to_stack0_stack1) // takes 2 stack slots, shuffling thunk must grow the stack + { + Assert.Equal(TestInstance, _this); + Assert.Equal(1, a2_to_a1); + Assert.Equal(2, a3_to_a2); + Assert.Equal(3, a4_to_a3); + Assert.Equal(4, a5_to_a4); + Assert.Equal(5, a6_to_a5); + Assert.Equal(6, a7_to_a6); + Assert.Equal(0f, fa0); + Assert.Equal(1f, fa1); + Assert.Equal(2f, fa2); + Assert.Equal(3f, fa3); + Assert.Equal(4f, fa4); + Assert.Equal(5f, fa5); + Assert.Equal(UintFloat.Get(), stack0_to_a7_fa6); + Assert.Equal(DoubleFloatNestedEmpty.Get(), fa6_fa7_to_stack0_stack1); + return FloatEmpty8Float.Get(); + } + } + + [Fact] + public static void Test_ShufflingThunk_MemberGrowsStack_RiscV() + { + var delegat = (ShufflingThunk_MemberGrowsStack_RiscV.TestDelegate)Delegate.CreateDelegate( + typeof(ShufflingThunk_MemberGrowsStack_RiscV.TestDelegate), null, + typeof(ShufflingThunk_MemberGrowsStack_RiscV).GetMethod( + nameof(ShufflingThunk_MemberGrowsStack_RiscV.TestMethod)) + ); + FloatEmpty8Float ret = delegat(ShufflingThunk_MemberGrowsStack_RiscV.TestInstance, + 1, 2, 3, 4, 5, 6, 0f, 1f, 2f, 3f, 4f, 5f, UintFloat.Get(), DoubleFloatNestedEmpty.Get()); + Assert.Equal(FloatEmpty8Float.Get(), ret); + + var getStaticMethod = [MethodImpl(MethodImplOptions.NoOptimization)] () + => (ShufflingThunk_MemberGrowsStack_RiscV.TestDelegate) + ShufflingThunk_MemberGrowsStack_RiscV.StaticTestMethod; + delegat = getStaticMethod(); + ret = delegat(ShufflingThunk_MemberGrowsStack_RiscV.TestInstance, + 1, 2, 3, 4, 5, 6, 0f, 1f, 2f, 3f, 4f, 5f, UintFloat.Get(), DoubleFloatNestedEmpty.Get()); + Assert.Equal(FloatEmpty8Float.Get(), ret); + } + + + + class ShufflingThunk_MemberGrowsStack_ReturnBuffer_RiscV + { + public static ShufflingThunk_MemberGrowsStack_ReturnBuffer_RiscV TestInstance = + new ShufflingThunk_MemberGrowsStack_ReturnBuffer_RiscV(); + + public delegate LongDoubleInt TestDelegate( + ShufflingThunk_MemberGrowsStack_ReturnBuffer_RiscV _this, + // ReturnBuffer* a2_to_a0 + int a3_to_a2, int a4_to_a3, int a5_to_a4, int a6_to_a5, int a7_to_a6, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, + UintFloat stack0_to_a7_fa6, // frees 1 stack slot + DoubleFloatNestedEmpty fa6_fa7_to_stack0_stack1); // takes 2 stack slots, shuffling thunk must grow the stack + + [MethodImpl(MethodImplOptions.NoInlining)] + public LongDoubleInt TestMethod( + // ReturnBuffer* a2_to_a0 + int a3_to_a2, int a4_to_a3, int a5_to_a4, int a6_to_a5, int a7_to_a6, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, + UintFloat stack0_to_a7_fa6, // frees 1 stack slot + DoubleFloatNestedEmpty fa6_fa7_to_stack0_stack1) // takes 2 stack slots, shuffling thunk must grow the stack + { + return StaticTestMethod(this, + a3_to_a2, a4_to_a3, a5_to_a4, a6_to_a5, a7_to_a6, + fa0, fa1, fa2, fa3, fa4, fa5, + stack0_to_a7_fa6, + fa6_fa7_to_stack0_stack1); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static LongDoubleInt StaticTestMethod( + ShufflingThunk_MemberGrowsStack_ReturnBuffer_RiscV _this, + // ReturnBuffer* a2_to_a0 + int a3_to_a2, int a4_to_a3, int a5_to_a4, int a6_to_a5, int a7_to_a6, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, + UintFloat stack0_to_a7_fa6, // frees 1 stack slot + DoubleFloatNestedEmpty fa6_fa7_to_stack0_stack1) // takes 2 stack slots, shuffling thunk must grow the stack + { + Assert.Equal(TestInstance, _this); + Assert.Equal(2, a3_to_a2); + Assert.Equal(3, a4_to_a3); + Assert.Equal(4, a5_to_a4); + Assert.Equal(5, a6_to_a5); + Assert.Equal(6, a7_to_a6); + Assert.Equal(0f, fa0); + Assert.Equal(1f, fa1); + Assert.Equal(2f, fa2); + Assert.Equal(3f, fa3); + Assert.Equal(4f, fa4); + Assert.Equal(5f, fa5); + Assert.Equal(UintFloat.Get(), stack0_to_a7_fa6); + Assert.Equal(DoubleFloatNestedEmpty.Get(), fa6_fa7_to_stack0_stack1); + return LongDoubleInt.Get(); // via return buffer + } + } + + [Fact] + public static void Test_ShufflingThunk_MemberGrowsStack_ReturnBuffer_RiscV() + { + var delegat = (ShufflingThunk_MemberGrowsStack_ReturnBuffer_RiscV.TestDelegate)Delegate.CreateDelegate( + typeof(ShufflingThunk_MemberGrowsStack_ReturnBuffer_RiscV.TestDelegate), null, + typeof(ShufflingThunk_MemberGrowsStack_ReturnBuffer_RiscV).GetMethod( + nameof(ShufflingThunk_MemberGrowsStack_ReturnBuffer_RiscV.TestMethod)) + ); + LongDoubleInt ret = delegat(ShufflingThunk_MemberGrowsStack_ReturnBuffer_RiscV.TestInstance, + 2, 3, 4, 5, 6, 0f, 1f, 2f, 3f, 4f, 5f, UintFloat.Get(), DoubleFloatNestedEmpty.Get()); + Assert.Equal(LongDoubleInt.Get(), ret); + + var getStaticMethod = [MethodImpl(MethodImplOptions.NoOptimization)] () + => (ShufflingThunk_MemberGrowsStack_ReturnBuffer_RiscV.TestDelegate) + ShufflingThunk_MemberGrowsStack_ReturnBuffer_RiscV.StaticTestMethod; + delegat = getStaticMethod(); + ret = delegat(ShufflingThunk_MemberGrowsStack_ReturnBuffer_RiscV.TestInstance, + 2, 3, 4, 5, 6, 0f, 1f, 2f, 3f, 4f, 5f, UintFloat.Get(), DoubleFloatNestedEmpty.Get()); + Assert.Equal(LongDoubleInt.Get(), ret); + } +#endregion } \ No newline at end of file