From e94dc2f387117073074f295c573c4d40344749fb Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Tue, 25 Aug 2020 07:17:41 -0700 Subject: [PATCH] Fix tailcall regression with compiled F# (#41206) * Fix tailcall regression with compiled F# This change skips instantiating stubs for direct tailcalls and instead passes the inst argument directly to the target method. Fixes #40864 * Add regression test Co-authored-by: Jakob Botsch Nielsen --- .../RuntimeHelpers.CoreCLR.cs | 12 +- src/coreclr/src/jit/compiler.h | 1 - src/coreclr/src/jit/morph.cpp | 89 ++---- src/coreclr/src/vm/corelib.h | 8 - src/coreclr/src/vm/ecalllist.h | 1 - src/coreclr/src/vm/gcenv.ee.cpp | 20 +- src/coreclr/src/vm/jitinterface.cpp | 2 +- src/coreclr/src/vm/tailcallhelp.cpp | 145 +++++----- src/coreclr/src/vm/tailcallhelp.h | 5 +- src/coreclr/src/vm/threads.cpp | 44 ++- src/coreclr/src/vm/threads.h | 25 +- .../JIT/Directed/tailcall/more_tailcalls.cs | 56 +++- .../JIT/Directed/tailcall/more_tailcalls.il | 253 +++++++++++++----- 13 files changed, 397 insertions(+), 264 deletions(-) diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs index 5c9a38e2a6f70..6b6e4639bfec2 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Runtime/CompilerServices/RuntimeHelpers.CoreCLR.cs @@ -286,9 +286,6 @@ public static IntPtr AllocateTypeAssociatedMemory(Type type, int size) [MethodImpl(MethodImplOptions.InternalCall)] private static extern IntPtr AllocTailCallArgBuffer(int size, IntPtr gcDesc); - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern void FreeTailCallArgBuffer(); - [MethodImpl(MethodImplOptions.InternalCall)] private static unsafe extern TailCallTls* GetTailCallInfo(IntPtr retAddrSlot, IntPtr* retAddr); @@ -323,6 +320,12 @@ private static unsafe void DispatchTailCalls( finally { tls->Frame = prevFrame; + + // If the arg buffer is reporting inst argument, it is safe to abandon it now + if (tls->ArgBuffer != IntPtr.Zero && *(int*)tls->ArgBuffer == 1 /* TAILCALLARGBUFFER_INSTARG_ONLY */) + { + *(int*)tls->ArgBuffer = 2 /* TAILCALLARGBUFFER_ABANDONED */; + } } } @@ -481,9 +484,6 @@ internal unsafe struct TailCallTls { public PortableTailCallFrame* Frame; public IntPtr ArgBuffer; - private IntPtr _argBufferSize; - private IntPtr _argBufferGCDesc; - private fixed byte _argBufferInline[64]; } } diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index e6788ddfe5679..e94dc8ad4c9a2 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -5579,7 +5579,6 @@ class Compiler GenTree* fgCreateCallDispatcherAndGetResult(GenTreeCall* origCall, CORINFO_METHOD_HANDLE callTargetStubHnd, CORINFO_METHOD_HANDLE dispatcherHnd); - GenTree* getMethodPointerTree(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_CALL_INFO* pCallInfo); GenTree* getLookupTree(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_LOOKUP* pLookup, unsigned handleFlags, diff --git a/src/coreclr/src/jit/morph.cpp b/src/coreclr/src/jit/morph.cpp index 844692cc41543..5430a2b02a96f 100644 --- a/src/coreclr/src/jit/morph.cpp +++ b/src/coreclr/src/jit/morph.cpp @@ -7700,6 +7700,8 @@ GenTree* Compiler::fgMorphTailCallViaHelpers(GenTreeCall* call, CORINFO_TAILCALL assert(!call->IsImplicitTailCall()); assert(!fgCanFastTailCall(call, nullptr)); + bool virtualCall = call->IsVirtual(); + // If VSD then get rid of arg to VSD since we turn this into a direct call. // The extra arg will be the first arg so this needs to be done before we // handle the retbuf below. @@ -7734,41 +7736,39 @@ GenTree* Compiler::fgMorphTailCallViaHelpers(GenTreeCall* call, CORINFO_TAILCALL // where we pass instantiating stub. if ((help.flags & CORINFO_TAILCALL_STORE_TARGET) != 0) { - // If asked to store target and we have a type arg we will store - // instantiating stub, so in that case we should not pass the type arg. - if (call->tailCallInfo->GetSig()->hasTypeArg()) + JITDUMP("Adding target since VM requested it\n"); + GenTree* target; + if (!virtualCall) { - JITDUMP("Removing type arg"); - - assert(call->gtCallArgs != nullptr); - if (Target::g_tgtArgOrder == Target::ARG_ORDER_R2L) + if (call->gtCallType == CT_INDIRECT) { - // Generic context is first arg - call->gtCallArgs = call->gtCallArgs->GetNext(); + noway_assert(call->gtCallAddr != nullptr); + target = call->gtCallAddr; } else { - // Generic context is last arg - GenTreeCall::Use** lastArgSlot = &call->gtCallArgs; - while ((*lastArgSlot)->GetNext() != nullptr) + CORINFO_CONST_LOOKUP addrInfo; + info.compCompHnd->getFunctionEntryPoint(call->gtCallMethHnd, &addrInfo); + + CORINFO_GENERIC_HANDLE handle = nullptr; + void* pIndirection = nullptr; + assert(addrInfo.accessType != IAT_PPVALUE && addrInfo.accessType != IAT_RELPVALUE); + + if (addrInfo.accessType == IAT_VALUE) { - lastArgSlot = &(*lastArgSlot)->NextRef(); + handle = addrInfo.handle; } - - *lastArgSlot = nullptr; + else if (addrInfo.accessType == IAT_PVALUE) + { + pIndirection = addrInfo.addr; + } + target = gtNewIconEmbHndNode(handle, pIndirection, GTF_ICON_FTN_ADDR, call->gtCallMethHnd); } - call->fgArgInfo = nullptr; - } - - JITDUMP("Adding target since VM requested it\n"); - GenTree* target; - if (call->tailCallInfo->IsCalli()) - { - noway_assert(call->gtCallType == CT_INDIRECT && call->gtCallAddr != nullptr); - target = call->gtCallAddr; } else { + assert(!call->tailCallInfo->GetSig()->hasTypeArg()); + CORINFO_CALL_INFO callInfo; unsigned flags = CORINFO_CALLINFO_LDFTN; if (call->tailCallInfo->IsCallvirt()) @@ -7778,19 +7778,10 @@ GenTree* Compiler::fgMorphTailCallViaHelpers(GenTreeCall* call, CORINFO_TAILCALL eeGetCallInfo(call->tailCallInfo->GetToken(), nullptr, (CORINFO_CALLINFO_FLAGS)flags, &callInfo); - if (!call->tailCallInfo->IsCallvirt() || - ((callInfo.methodFlags & (CORINFO_FLG_FINAL | CORINFO_FLG_STATIC)) != 0) || - ((callInfo.methodFlags & CORINFO_FLG_VIRTUAL) == 0)) - { - target = getMethodPointerTree(call->tailCallInfo->GetToken(), &callInfo); - } - else - { - assert(call->gtCallThisArg != nullptr); - // TODO: Proper cloning of the this pointer. - target = getVirtMethodPointerTree(gtCloneExpr(call->gtCallThisArg->GetNode()), - call->tailCallInfo->GetToken(), &callInfo); - } + assert(call->gtCallThisArg != nullptr); + // TODO: Proper cloning of the this pointer. + target = getVirtMethodPointerTree(gtCloneExpr(call->gtCallThisArg->GetNode()), + call->tailCallInfo->GetToken(), &callInfo); } // Insert target as last arg @@ -8044,30 +8035,6 @@ GenTree* Compiler::fgCreateCallDispatcherAndGetResult(GenTreeCall* orig return finalTree; } -//------------------------------------------------------------------------ -// getMethodPointerTree: get a method pointer tree -// -// Arguments: -// pResolvedToken - resolved token of the call -// pCallInfo - the call info of the call -// -// Return Value: -// A node representing the method pointer -// -GenTree* Compiler::getMethodPointerTree(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_CALL_INFO* pCallInfo) -{ - switch (pCallInfo->kind) - { - case CORINFO_CALL: - return new (this, GT_FTN_ADDR) GenTreeFptrVal(TYP_I_IMPL, pCallInfo->hMethod); - case CORINFO_CALL_CODE_POINTER: - return getLookupTree(pResolvedToken, &pCallInfo->codePointerLookup, GTF_ICON_FTN_ADDR, pCallInfo->hMethod); - default: - noway_assert(!"unknown call kind"); - return nullptr; - } -} - //------------------------------------------------------------------------ // getLookupTree: get a lookup tree // diff --git a/src/coreclr/src/vm/corelib.h b/src/coreclr/src/vm/corelib.h index 5b08cc4695c6e..044147c8eb2a6 100644 --- a/src/coreclr/src/vm/corelib.h +++ b/src/coreclr/src/vm/corelib.h @@ -708,7 +708,6 @@ DEFINE_METHOD(RUNTIME_HELPERS, ENUM_EQUALS, EnumEquals, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, ENUM_COMPARE_TO, EnumCompareTo, NoSig) DEFINE_METHOD(RUNTIME_HELPERS, ALLOC_TAILCALL_ARG_BUFFER, AllocTailCallArgBuffer, SM_Int_IntPtr_RetIntPtr) DEFINE_METHOD(RUNTIME_HELPERS, GET_TAILCALL_INFO, GetTailCallInfo, NoSig) -DEFINE_METHOD(RUNTIME_HELPERS, FREE_TAILCALL_ARG_BUFFER, FreeTailCallArgBuffer, SM_RetVoid) DEFINE_METHOD(RUNTIME_HELPERS, DISPATCH_TAILCALLS, DispatchTailCalls, NoSig) DEFINE_CLASS(UNSAFE, InternalCompilerServices, Unsafe) @@ -760,9 +759,6 @@ DEFINE_FIELD(PORTABLE_TAIL_CALL_FRAME, NEXT_CALL, NextCall) DEFINE_CLASS(TAIL_CALL_TLS, CompilerServices, TailCallTls) DEFINE_FIELD(TAIL_CALL_TLS, FRAME, Frame) DEFINE_FIELD(TAIL_CALL_TLS, ARG_BUFFER, ArgBuffer) -DEFINE_FIELD(TAIL_CALL_TLS, ARG_BUFFER_SIZE, _argBufferSize) -DEFINE_FIELD(TAIL_CALL_TLS, ARG_BUFFER_GC_DESC, _argBufferGCDesc) -DEFINE_FIELD(TAIL_CALL_TLS, ARG_BUFFER_INLINE, _argBufferInline) DEFINE_CLASS_U(CompilerServices, PortableTailCallFrame, PortableTailCallFrame) DEFINE_FIELD_U(Prev, PortableTailCallFrame, Prev) @@ -772,10 +768,6 @@ DEFINE_FIELD_U(NextCall, PortableTailCallFrame, NextCall) DEFINE_CLASS_U(CompilerServices, TailCallTls, TailCallTls) DEFINE_FIELD_U(Frame, TailCallTls, m_frame) DEFINE_FIELD_U(ArgBuffer, TailCallTls, m_argBuffer) -DEFINE_FIELD_U(_argBufferSize, TailCallTls, m_argBufferSize) -DEFINE_FIELD_U(_argBufferGCDesc, TailCallTls, m_argBufferGCDesc) -DEFINE_FIELD_U(_argBufferInline, TailCallTls, m_argBufferInline) - DEFINE_CLASS(RUNTIME_WRAPPED_EXCEPTION, CompilerServices, RuntimeWrappedException) DEFINE_METHOD(RUNTIME_WRAPPED_EXCEPTION, OBJ_CTOR, .ctor, IM_Obj_RetVoid) diff --git a/src/coreclr/src/vm/ecalllist.h b/src/coreclr/src/vm/ecalllist.h index f1827fa3bd97a..2c2dd518c09bf 100644 --- a/src/coreclr/src/vm/ecalllist.h +++ b/src/coreclr/src/vm/ecalllist.h @@ -881,7 +881,6 @@ FCFuncStart(gRuntimeHelpers) FCFuncElement("GetUninitializedObjectInternal", ReflectionSerialization::GetUninitializedObject) QCFuncElement("AllocateTypeAssociatedMemoryInternal", RuntimeTypeHandle::AllocateTypeAssociatedMemory) FCFuncElement("AllocTailCallArgBuffer", TailCallHelp::AllocTailCallArgBuffer) - FCFuncElement("FreeTailCallArgBuffer", TailCallHelp::FreeTailCallArgBuffer) FCFuncElement("GetTailCallInfo", TailCallHelp::GetTailCallInfo) FCFuncElement("GetILBytesJitted", GetJittedBytes) FCFuncElement("GetMethodsJittedCount", GetJittedMethodsCount) diff --git a/src/coreclr/src/vm/gcenv.ee.cpp b/src/coreclr/src/vm/gcenv.ee.cpp index 54bde52c750b4..6eaf17683cc28 100644 --- a/src/coreclr/src/vm/gcenv.ee.cpp +++ b/src/coreclr/src/vm/gcenv.ee.cpp @@ -157,27 +157,33 @@ static void ScanStackRoots(Thread * pThread, promote_func* fn, ScanContext* sc) static void ScanTailCallArgBufferRoots(Thread* pThread, promote_func* fn, ScanContext* sc) { - void* gcDesc; - char* argBuffer = pThread->GetTailCallTls()->GetArgBuffer(&gcDesc); - if (gcDesc == NULL) + TailCallArgBuffer* argBuffer = pThread->GetTailCallTls()->GetArgBuffer(); + if (argBuffer == NULL || argBuffer->GCDesc == NULL) return; - GCRefMapDecoder decoder(static_cast(gcDesc)); + if (argBuffer->State == TAILCALLARGBUFFER_ABANDONED) + return; + + bool instArgOnly = argBuffer->State == TAILCALLARGBUFFER_INSTARG_ONLY; + + GCRefMapDecoder decoder(static_cast(argBuffer->GCDesc)); while (!decoder.AtEnd()) { int pos = decoder.CurrentPos(); int token = decoder.ReadToken(); - PTR_TADDR ppObj = dac_cast(argBuffer + pos * sizeof(TADDR)); + PTR_TADDR ppObj = dac_cast(((BYTE*)argBuffer->Args) + pos * sizeof(TADDR)); switch (token) { case GCREFMAP_SKIP: break; case GCREFMAP_REF: - fn(dac_cast(ppObj), sc, CHECK_APP_DOMAIN); + if (!instArgOnly) + fn(dac_cast(ppObj), sc, CHECK_APP_DOMAIN); break; case GCREFMAP_INTERIOR: - PromoteCarefully(fn, dac_cast(ppObj), sc, GC_CALL_INTERIOR); + if (!instArgOnly) + PromoteCarefully(fn, dac_cast(ppObj), sc, GC_CALL_INTERIOR); break; case GCREFMAP_METHOD_PARAM: if (sc->promotion) diff --git a/src/coreclr/src/vm/jitinterface.cpp b/src/coreclr/src/vm/jitinterface.cpp index dbf4ec793bf0b..37bede3e2dc0d 100644 --- a/src/coreclr/src/vm/jitinterface.cpp +++ b/src/coreclr/src/vm/jitinterface.cpp @@ -13952,7 +13952,7 @@ bool CEEInfo::getTailCallHelpersInternal(CORINFO_RESOLVED_TOKEN* callToken, TailCallHelp::CreateTailCallHelperStubs( m_pMethodBeingCompiled, pTargetMD, - msig, isCallvirt, isThisArgByRef, + msig, isCallvirt, isThisArgByRef, sig->hasTypeArg(), &pStoreArgsMD, &needsTarget, &pCallTargetMD); diff --git a/src/coreclr/src/vm/tailcallhelp.cpp b/src/coreclr/src/vm/tailcallhelp.cpp index 0db1330b7b3c0..10a9c501cf2f0 100644 --- a/src/coreclr/src/vm/tailcallhelp.cpp +++ b/src/coreclr/src/vm/tailcallhelp.cpp @@ -23,7 +23,7 @@ FCIMPL2(void*, TailCallHelp::AllocTailCallArgBuffer, INT32 size, void* gcDesc) _ASSERTE(size >= 0); - void* result = GetThread()->GetTailCallTls()->AllocArgBuffer(static_cast(size), gcDesc); + void* result = GetThread()->GetTailCallTls()->AllocArgBuffer(size, gcDesc); if (result == NULL) FCThrow(kOutOfMemoryException); @@ -32,14 +32,6 @@ FCIMPL2(void*, TailCallHelp::AllocTailCallArgBuffer, INT32 size, void* gcDesc) } FCIMPLEND -FCIMPL0(void, TailCallHelp::FreeTailCallArgBuffer) -{ - FCALL_CONTRACT; - - GetThread()->GetTailCallTls()->FreeArgBuffer(); -} -FCIMPLEND - FCIMPL2(void*, TailCallHelp::GetTailCallInfo, void** retAddrSlot, void** retAddr) { FCALL_CONTRACT; @@ -67,12 +59,14 @@ struct ArgBufferValue struct ArgBufferLayout { bool HasTargetAddress; + bool HasInstArg; unsigned int TargetAddressOffset; InlineSArray Values; unsigned int Size; ArgBufferLayout() : HasTargetAddress(false) + , HasInstArg(false) , TargetAddressOffset(0) , Size(0) { @@ -133,7 +127,7 @@ MethodDesc* TailCallHelp::GetTailCallDispatcherMD() void TailCallHelp::CreateTailCallHelperStubs( MethodDesc* pCallerMD, MethodDesc* pCalleeMD, - MetaSig& callSiteSig, bool virt, bool thisArgByRef, + MetaSig& callSiteSig, bool virt, bool thisArgByRef, bool hasInstArg, MethodDesc** storeArgsStub, bool* storeArgsNeedsTarget, MethodDesc** callTargetStub) { @@ -154,7 +148,7 @@ void TailCallHelp::CreateTailCallHelperStubs( TypeHandle retTyHnd = NormalizeSigType(callSiteSig.GetRetTypeHandleThrowing()); TailCallInfo info(pCallerMD, pCalleeMD, pLoaderAllocator, &callSiteSig, virt, retTyHnd); - LayOutArgBuffer(callSiteSig, pCalleeMD, *storeArgsNeedsTarget, thisArgByRef, &info.ArgBufLayout); + LayOutArgBuffer(callSiteSig, pCalleeMD, *storeArgsNeedsTarget, thisArgByRef, hasInstArg, &info.ArgBufLayout); info.HasGCDescriptor = GenerateGCDescriptor(pCalleeMD, info.ArgBufLayout, &info.GCRefMapBuilder); *storeArgsStub = CreateStoreArgsStub(info); @@ -163,9 +157,9 @@ void TailCallHelp::CreateTailCallHelperStubs( void TailCallHelp::LayOutArgBuffer( MetaSig& callSiteSig, MethodDesc* calleeMD, - bool storeTarget, bool thisArgByRef, ArgBufferLayout* layout) + bool storeTarget, bool thisArgByRef, bool hasInstArg, ArgBufferLayout* layout) { - unsigned int offs = 0; + unsigned int offs = offsetof(TailCallArgBuffer, Args); auto addValue = [&](TypeHandle th) { @@ -194,6 +188,15 @@ void TailCallHelp::LayOutArgBuffer( addValue(thisHnd); } + layout->HasInstArg = hasInstArg; + +#ifndef TARGET_X86 + if (hasInstArg) + { + addValue(TypeHandle(CoreLibBinder::GetElementType(ELEMENT_TYPE_I))); + } +#endif + callSiteSig.Reset(); CorElementType ty; while ((ty = callSiteSig.NextArg()) != ELEMENT_TYPE_END) @@ -203,6 +206,13 @@ void TailCallHelp::LayOutArgBuffer( addValue(tyHnd); } +#ifdef TARGET_X86 + if (hasInstArg) + { + addValue(TypeHandle(CoreLibBinder::GetElementType(ELEMENT_TYPE_I))); + } +#endif + if (storeTarget) { offs = AlignUp(offs, TARGET_POINTER_SIZE); @@ -247,35 +257,49 @@ TypeHandle TailCallHelp::NormalizeSigType(TypeHandle tyHnd) bool TailCallHelp::GenerateGCDescriptor( MethodDesc* pTargetMD, const ArgBufferLayout& layout, GCRefMapBuilder* builder) { - auto writeGCType = [&](unsigned int offset, CorInfoGCType type) + auto writeGCType = [&](unsigned int argPos, CorInfoGCType type) { - _ASSERTE(offset % TARGET_POINTER_SIZE == 0); switch (type) { - case TYPE_GC_REF: builder->WriteToken(offset / TARGET_POINTER_SIZE, GCREFMAP_REF); break; - case TYPE_GC_BYREF: builder->WriteToken(offset / TARGET_POINTER_SIZE, GCREFMAP_INTERIOR); break; + case TYPE_GC_REF: builder->WriteToken(argPos, GCREFMAP_REF); break; + case TYPE_GC_BYREF: builder->WriteToken(argPos, GCREFMAP_INTERIOR); break; case TYPE_GC_NONE: break; default: UNREACHABLE_MSG("Invalid type"); break; } }; + bool reportInstArg = layout.HasInstArg; + CQuickBytes gcPtrs; for (COUNT_T i = 0; i < layout.Values.GetCount(); i++) { const ArgBufferValue& val = layout.Values[i]; + unsigned int argPos = (val.Offset - offsetof(TailCallArgBuffer, Args)) / TARGET_POINTER_SIZE; + TypeHandle tyHnd = val.TyHnd; if (tyHnd.IsValueType()) { if (!tyHnd.GetMethodTable()->ContainsPointers()) + { +#ifndef TARGET_X86 + // The generic instantiation arg is right after this pointer + if (reportInstArg) + { + _ASSERTE(i == 0 || i == 1); + builder->WriteToken(argPos, pTargetMD->RequiresInstMethodDescArg() ? GCREFMAP_METHOD_PARAM : GCREFMAP_TYPE_PARAM); + reportInstArg = false; + } +#endif continue; + } unsigned int numSlots = (tyHnd.GetSize() + TARGET_POINTER_SIZE - 1) / TARGET_POINTER_SIZE; BYTE* ptr = static_cast(gcPtrs.AllocThrows(numSlots)); CEEInfo::getClassGClayoutStatic(tyHnd, ptr); for (unsigned int i = 0; i < numSlots; i++) { - writeGCType(val.Offset + i * TARGET_POINTER_SIZE, (CorInfoGCType)ptr[i]); + writeGCType(argPos + i , (CorInfoGCType)ptr[i]); } continue; @@ -284,9 +308,19 @@ bool TailCallHelp::GenerateGCDescriptor( CorElementType ety = tyHnd.GetSignatureCorElementType(); CorInfoGCType gc = CorTypeInfo::GetGCType(ety); - writeGCType(val.Offset, gc); + writeGCType(argPos, gc); } +#ifdef TARGET_X86 + // The generic instantiation arg is last + if (reportInstArg) + { + const ArgBufferValue& val = layout.Values[layout.Values.GetCount() - 1]; + unsigned int argPos = (val.Offset - offsetof(TailCallArgBuffer, Args)) / TARGET_POINTER_SIZE; + builder->WriteToken(argPos, pTargetMD->RequiresInstMethodDescArg() ? GCREFMAP_METHOD_PARAM : GCREFMAP_TYPE_PARAM); + } +#endif + builder->Flush(); return builder->GetBlobLength() > 0; @@ -331,29 +365,24 @@ MethodDesc* TailCallHelp::CreateStoreArgsStub(TailCallInfo& info) auto emitOffs = [&](UINT offs) { pCode->EmitLDLOC(bufferLcl); - if (offs != 0) - { - pCode->EmitLDC(offs); - pCode->EmitADD(); - } + pCode->EmitLDC(offs); + pCode->EmitADD(); }; - unsigned int argIndex = 0; - for (COUNT_T i = 0; i < info.ArgBufLayout.Values.GetCount(); i++) { const ArgBufferValue& arg = info.ArgBufLayout.Values[i]; CorElementType ty = arg.TyHnd.GetSignatureCorElementType(); emitOffs(arg.Offset); - pCode->EmitLDARG(argIndex++); + pCode->EmitLDARG(i); EmitStoreTyHnd(pCode, arg.TyHnd); } if (info.ArgBufLayout.HasTargetAddress) { emitOffs(info.ArgBufLayout.TargetAddressOffset); - pCode->EmitLDARG(argIndex++); + pCode->EmitLDARG(info.ArgBufLayout.Values.GetCount()); pCode->EmitSTIND_I(); } @@ -447,42 +476,31 @@ MethodDesc* TailCallHelp::CreateCallTargetStub(const TailCallInfo& info) auto emitOffs = [&](UINT offs) { pCode->EmitLDARG(ARG_ARG_BUFFER); - if (offs != 0) - { - pCode->EmitLDC(offs); - pCode->EmitADD(); - } + pCode->EmitLDC(offs); + pCode->EmitADD(); }; - StackSArray argLocals; + // *pTailCallAwareRetAddr = NextCallReturnAddress(); + pCode->EmitLDARG(ARG_PTR_TAILCALL_AWARE_RET_ADDR); + pCode->EmitCALL(METHOD__STUBHELPERS__NEXT_CALL_RETURN_ADDRESS, 0, 1); + pCode->EmitSTIND_I(); + for (COUNT_T i = 0; i < info.ArgBufLayout.Values.GetCount(); i++) { const ArgBufferValue& arg = info.ArgBufLayout.Values[i]; - DWORD argLcl = pCode->NewLocal(LocalDesc(arg.TyHnd)); - argLocals.Append(argLcl); // arg = args->Arg_i emitOffs(arg.Offset); EmitLoadTyHnd(pCode, arg.TyHnd); - pCode->EmitSTLOC(argLcl); - } - - DWORD targetAddrLcl; - if (info.ArgBufLayout.HasTargetAddress) - { - targetAddrLcl = pCode->NewLocal(ELEMENT_TYPE_I); - - emitOffs(info.ArgBufLayout.TargetAddressOffset); - pCode->EmitLDIND_I(); - pCode->EmitSTLOC(targetAddrLcl); } - // RuntimeHelpers.FreeTailCallArgBuffer(); - pCode->EmitCALL(METHOD__RUNTIME_HELPERS__FREE_TAILCALL_ARG_BUFFER, 0, 0); - - // *pTailCallAwareRetAddr = NextCallReturnAddress(); - pCode->EmitLDARG(ARG_PTR_TAILCALL_AWARE_RET_ADDR); - pCode->EmitCALL(METHOD__STUBHELPERS__NEXT_CALL_RETURN_ADDRESS, 0, 1); + // All arguments are loaded on the stack, it is safe to disable the GC reporting of ArgBuffer now. + // This is optimization to avoid extending argument lifetime unnecessarily. + // We still need to report the inst argument of shared generic code to prevent it from being unloaded. The inst + // argument is just a regular IntPtr on the stack. It is safe to stop reporting it only after the target method + // takes over. + pCode->EmitLDARG(ARG_ARG_BUFFER); + pCode->EmitLDC(info.ArgBufLayout.HasInstArg ? TAILCALLARGBUFFER_INSTARG_ONLY : TAILCALLARGBUFFER_ABANDONED); pCode->EmitSTIND_I(); int numRetVals = info.CallSiteSig->IsReturnTypeVoid() ? 0 : 1; @@ -495,23 +513,18 @@ MethodDesc* TailCallHelp::CreateCallTargetStub(const TailCallInfo& info) // the proper MethodRef. _ASSERTE(!info.CallSiteSig->IsVarArg()); - for (COUNT_T i = 0; i < argLocals.GetCount(); i++) - { - pCode->EmitLDLOC(argLocals[i]); - } - if (info.CallSiteIsVirtual) { pCode->EmitCALLVIRT( pCode->GetToken(info.Callee), - static_cast(argLocals.GetCount()), + static_cast(info.ArgBufLayout.Values.GetCount()), numRetVals); } else { pCode->EmitCALL( pCode->GetToken(info.Callee), - static_cast(argLocals.GetCount()), + static_cast(info.ArgBufLayout.Values.GetCount()), numRetVals); } } @@ -538,7 +551,7 @@ MethodDesc* TailCallHelp::CreateCallTargetStub(const TailCallInfo& info) COUNT_T firstSigArg = info.CallSiteSig->HasThis() ? 1 : 0; - for (COUNT_T i = firstSigArg; i < argLocals.GetCount(); i++) + for (COUNT_T i = firstSigArg; i < info.ArgBufLayout.Values.GetCount(); i++) { const ArgBufferValue& val = info.ArgBufLayout.Values[i]; AppendTypeHandle(calliSig, val.TyHnd); @@ -547,16 +560,12 @@ MethodDesc* TailCallHelp::CreateCallTargetStub(const TailCallInfo& info) DWORD cbCalliSig; PCCOR_SIGNATURE pCalliSig = (PCCOR_SIGNATURE)calliSig.GetSignature(&cbCalliSig); - for (COUNT_T i = 0; i < argLocals.GetCount(); i++) - { - pCode->EmitLDLOC(argLocals[i]); - } - - pCode->EmitLDLOC(targetAddrLcl); + emitOffs(info.ArgBufLayout.TargetAddressOffset); + pCode->EmitLDIND_I(); pCode->EmitCALLI( pCode->GetSigToken(pCalliSig, cbCalliSig), - static_cast(argLocals.GetCount()), + static_cast(info.ArgBufLayout.Values.GetCount()), numRetVals); } diff --git a/src/coreclr/src/vm/tailcallhelp.h b/src/coreclr/src/vm/tailcallhelp.h index 327dc3d2e1dc0..32883076a6920 100644 --- a/src/coreclr/src/vm/tailcallhelp.h +++ b/src/coreclr/src/vm/tailcallhelp.h @@ -14,12 +14,11 @@ class TailCallHelp { public: static FCDECL2(void*, AllocTailCallArgBuffer, INT32, void*); - static FCDECL0(void, FreeTailCallArgBuffer); static FCDECL2(void*, GetTailCallInfo, void**, void**); static void CreateTailCallHelperStubs( MethodDesc* pCallerMD, MethodDesc* pCalleeMD, - MetaSig& callSiteSig, bool virt, bool thisArgByRef, + MetaSig& callSiteSig, bool virt, bool thisArgByRef, bool hasInstArg, MethodDesc** storeArgsStub, bool* storeArgsNeedsTarget, MethodDesc** callTargetStub); @@ -29,7 +28,7 @@ class TailCallHelp static void LayOutArgBuffer( MetaSig& callSiteSig, MethodDesc* calleeMD, - bool storeTarget, bool thisArgByRef, ArgBufferLayout* layout); + bool storeTarget, bool thisArgByRef, bool hasInstArg, ArgBufferLayout* layout); static TypeHandle NormalizeSigType(TypeHandle tyHnd); static bool GenerateGCDescriptor(MethodDesc* pTargetMD, const ArgBufferLayout& values, GCRefMapBuilder* builder); diff --git a/src/coreclr/src/vm/threads.cpp b/src/coreclr/src/vm/threads.cpp index 2a1be66eb3802..588e230c12980 100644 --- a/src/coreclr/src/vm/threads.cpp +++ b/src/coreclr/src/vm/threads.cpp @@ -61,12 +61,10 @@ TailCallTls::TailCallTls() // so casting away const is ok here. : m_frame(const_cast(&g_sentinelTailCallFrame)) , m_argBuffer(NULL) - , m_argBufferSize(0) - , m_argBufferGCDesc(NULL) { } -void* TailCallTls::AllocArgBuffer(size_t size, void* gcDesc) +TailCallArgBuffer* TailCallTls::AllocArgBuffer(int size, void* gcDesc) { CONTRACTL { @@ -75,42 +73,30 @@ void* TailCallTls::AllocArgBuffer(size_t size, void* gcDesc) } CONTRACTL_END - _ASSERTE(m_argBuffer == NULL); + _ASSERTE(size >= (int)offsetof(TailCallArgBuffer, Args)); - if (size > sizeof(m_argBufferInline)) + if (m_argBuffer != NULL && m_argBuffer->Size < size) { - m_argBuffer = new (nothrow) char[size]; - if (m_argBuffer == NULL) - return NULL; + FreeArgBuffer(); } - else - m_argBuffer = m_argBufferInline; - if (gcDesc != NULL) + if (m_argBuffer == NULL) { - memset(m_argBuffer, 0, size); - m_argBufferGCDesc = gcDesc; + m_argBuffer = (TailCallArgBuffer*)new (nothrow) BYTE[size]; + if (m_argBuffer == NULL) + return NULL; + m_argBuffer->Size = size; } - m_argBufferSize = size; - - return m_argBuffer; -} + m_argBuffer->State = TAILCALLARGBUFFER_ACTIVE; -void TailCallTls::FreeArgBuffer() -{ - CONTRACTL + m_argBuffer->GCDesc = gcDesc; + if (gcDesc != NULL) { - NOTHROW; - GC_NOTRIGGER; + memset(m_argBuffer->Args, 0, size - offsetof(TailCallArgBuffer, Args)); } - CONTRACTL_END - - if (m_argBufferSize > sizeof(m_argBufferInline)) - delete[] m_argBuffer; - m_argBufferGCDesc = NULL; - m_argBuffer = NULL; + return m_argBuffer; } #if defined (_DEBUG_IMPL) || defined(_PREFAST_) @@ -2646,6 +2632,8 @@ Thread::~Thread() delete m_pIBCInfo; } + m_tailCallTls.FreeArgBuffer(); + #ifdef FEATURE_EVENT_TRACE // Destruct the thread local type cache for allocation sampling if(m_pAllLoggedTypes) { diff --git a/src/coreclr/src/vm/threads.h b/src/coreclr/src/vm/threads.h index c8acec7af83ec..64192c20dd959 100644 --- a/src/coreclr/src/vm/threads.h +++ b/src/coreclr/src/vm/threads.h @@ -239,6 +239,19 @@ struct ThreadLocalBlock #endif }; +// TailCallArgBuffer states +#define TAILCALLARGBUFFER_ACTIVE 0 +#define TAILCALLARGBUFFER_INSTARG_ONLY 1 +#define TAILCALLARGBUFFER_ABANDONED 2 + +struct TailCallArgBuffer +{ + int State; + int Size; + void* GCDesc; + BYTE Args[1]; +}; + #ifdef CROSSGEN_COMPILE #include "asmconstants.h" @@ -968,18 +981,14 @@ class TailCallTls friend class CoreLibBinder; PortableTailCallFrame* m_frame; - char* m_argBuffer; - size_t m_argBufferSize; - void* m_argBufferGCDesc; - char m_argBufferInline[64]; + TailCallArgBuffer* m_argBuffer; public: TailCallTls(); - void* AllocArgBuffer(size_t size, void* gcDesc); - void FreeArgBuffer(); - char* GetArgBuffer(void** gcDesc) + TailCallArgBuffer* AllocArgBuffer(int size, void* gcDesc); + void FreeArgBuffer() { delete[] (BYTE*)m_argBuffer; m_argBuffer = NULL; } + TailCallArgBuffer* GetArgBuffer() { - *gcDesc = m_argBufferGCDesc; return m_argBuffer; } const PortableTailCallFrame* GetFrame() { return m_frame; } diff --git a/src/tests/JIT/Directed/tailcall/more_tailcalls.cs b/src/tests/JIT/Directed/tailcall/more_tailcalls.cs index 2a778241bffeb..141933daca50d 100644 --- a/src/tests/JIT/Directed/tailcall/more_tailcalls.cs +++ b/src/tests/JIT/Directed/tailcall/more_tailcalls.cs @@ -61,22 +61,26 @@ static Program() { IL.Emit.Ldftn(new MethodRef(typeof(Program), nameof(CalcStaticCalli))); IL.Pop(out IntPtr calcStaticCalli); + s_calcStaticCalli = calcStaticCalli; + IL.Emit.Ldftn(new MethodRef(typeof(Program), nameof(CalcStaticCalliOther))); IL.Pop(out IntPtr calcStaticCalliOther); + s_calcStaticCalliOther = calcStaticCalliOther; + IL.Emit.Ldftn(new MethodRef(typeof(Program), nameof(CalcStaticCalliRetbuf))); IL.Pop(out IntPtr calcStaticCalliRetbuf); + s_calcStaticCalliRetbuf = calcStaticCalliRetbuf; + IL.Emit.Ldftn(new MethodRef(typeof(Program), nameof(CalcStaticCalliRetbufOther))); IL.Pop(out IntPtr calcStaticCalliRetbufOther); + s_calcStaticCalliRetbufOther = calcStaticCalliRetbufOther; + IL.Emit.Ldftn(new MethodRef(typeof(Program), nameof(EmptyCalliOther))); IL.Pop(out IntPtr emptyCalliOther); + s_emptyCalliOther = emptyCalliOther; + IL.Emit.Ldftn(new MethodRef(typeof(S16), nameof(S16.InstanceMethod))); IL.Pop(out IntPtr instanceMethodOnValueType); - - s_calcStaticCalli = calcStaticCalli; - s_calcStaticCalliOther = calcStaticCalliOther; - s_calcStaticCalliRetbuf = calcStaticCalliRetbuf; - s_calcStaticCalliRetbufOther = calcStaticCalliRetbufOther; - s_emptyCalliOther = emptyCalliOther; s_instanceMethodOnValueType = instanceMethodOnValueType; } @@ -183,6 +187,10 @@ void TestCalc(Func f, T expected, string name) Test(() => GenAbstractGString(ga1), "System.String", "Abstract generic without generic on method 1"); Test(() => GenAbstractGInt(ga2), "System.Int32", "Abstract generic without generic on method 2"); + int[] a = new int[1_000_000]; + a[99] = 1; + Test(() => InstantiatingStub1(0, 0, "string", a), a.Length + 1, "Instantiating stub direct"); + if (result) Console.WriteLine("All tailcall-via-help succeeded"); else @@ -686,6 +694,42 @@ private static string GenAbstractGInt(GenAbstract ga) IL.Emit.Callvirt(new MethodRef(typeof(GenAbstract), nameof(GenAbstract.G))); return IL.Return(); } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static int InstantiatingStub1(int a, int r, T c, Span d) + { + IL.Push(c); + IL.Push(c); + IL.Push(c); + IL.Push(c); + IL.Push(c); + IL.Push(c); + IL.Push(c); + IL.Push(c); + IL.Push(a); + IL.Push(r); + IL.Emit.Ldarg(nameof(d)); + IL.Push(r + d[99]); + IL.Emit.Tail(); + IL.Emit.Call(new MethodRef(typeof(Program), nameof(InstantiatingStub1Other)).MakeGenericMethod(typeof(T))); + return IL.Return(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static int InstantiatingStub1Other(T c0, T c1, T c2, T c3, T c4, T c5, T c6, T c7, int a, int r, Span d, int result) + { + if (a == d.Length) return result; + else + { + IL.Push(a + 1); + IL.Push(result); + IL.Push(c0); + IL.Emit.Ldarg(nameof(d)); + IL.Emit.Tail(); + IL.Emit.Call(new MethodRef(typeof(Program), nameof(InstantiatingStub1)).MakeGenericMethod(typeof(T))); + return IL.Return(); + } + } } class Instance diff --git a/src/tests/JIT/Directed/tailcall/more_tailcalls.il b/src/tests/JIT/Directed/tailcall/more_tailcalls.il index 6b88e673dffb9..f46fb3d748f0f 100644 --- a/src/tests/JIT/Directed/tailcall/more_tailcalls.il +++ b/src/tests/JIT/Directed/tailcall/more_tailcalls.il @@ -8,19 +8,14 @@ .assembly extern System.Runtime { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:2:2:0 -} -.assembly extern System.Runtime.Extensions -{ - .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:2:2:0 + .ver 5:0:0:0 } .assembly extern System.Console { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....: - .ver 4:1:2:0 + .ver 5:0:0:0 } -.assembly example1 +.assembly more_tailcalls { .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [System.Runtime]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx @@ -30,29 +25,32 @@ // .custom instance void [System.Runtime]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [System.Runtime]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 ) .custom instance void [System.Runtime]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = ( 01 00 18 2E 4E 45 54 43 6F 72 65 41 70 70 2C 56 // ....NETCoreApp,V - 65 72 73 69 6F 6E 3D 76 33 2E 31 01 00 54 0E 14 // ersion=v3.1..T.. + 65 72 73 69 6F 6E 3D 76 35 2E 30 01 00 54 0E 14 // ersion=v5.0..T.. 46 72 61 6D 65 77 6F 72 6B 44 69 73 70 6C 61 79 // FrameworkDisplay 4E 61 6D 65 00 ) // Name. - .custom instance void [System.Runtime]System.Reflection.AssemblyCompanyAttribute::.ctor(string) = ( 01 00 08 65 78 61 6D 70 6C 65 31 00 00 ) // ...example1.. + .custom instance void [System.Runtime]System.Reflection.AssemblyCompanyAttribute::.ctor(string) = ( 01 00 0E 6D 6F 72 65 5F 74 61 69 6C 63 61 6C 6C // ...more_tailcall + 73 00 00 ) // s.. .custom instance void [System.Runtime]System.Reflection.AssemblyConfigurationAttribute::.ctor(string) = ( 01 00 07 52 65 6C 65 61 73 65 00 00 ) // ...Release.. .custom instance void [System.Runtime]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 31 2E 30 2E 30 2E 30 00 00 ) // ...1.0.0.0.. .custom instance void [System.Runtime]System.Reflection.AssemblyInformationalVersionAttribute::.ctor(string) = ( 01 00 05 31 2E 30 2E 30 00 00 ) // ...1.0.0.. - .custom instance void [System.Runtime]System.Reflection.AssemblyProductAttribute::.ctor(string) = ( 01 00 08 65 78 61 6D 70 6C 65 31 00 00 ) // ...example1.. - .custom instance void [System.Runtime]System.Reflection.AssemblyTitleAttribute::.ctor(string) = ( 01 00 08 65 78 61 6D 70 6C 65 31 00 00 ) // ...example1.. + .custom instance void [System.Runtime]System.Reflection.AssemblyProductAttribute::.ctor(string) = ( 01 00 0E 6D 6F 72 65 5F 74 61 69 6C 63 61 6C 6C // ...more_tailcall + 73 00 00 ) // s.. + .custom instance void [System.Runtime]System.Reflection.AssemblyTitleAttribute::.ctor(string) = ( 01 00 0E 6D 6F 72 65 5F 74 61 69 6C 63 61 6C 6C // ...more_tailcall + 73 00 00 ) // s.. .permissionset reqmin - = {class 'System.Security.Permissions.SecurityPermissionAttribute, System.Runtime.Extensions, Version=4.2.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' = {property bool 'SkipVerification' = bool(true)}} + = {class 'System.Security.Permissions.SecurityPermissionAttribute, System.Runtime, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' = {property bool 'SkipVerification' = bool(true)}} .hash algorithm 0x00008004 .ver 1:0:0:0 } -.module example1.dll -// MVID: {E5213016-898B-454A-941D-D8C885B9972D} +.module more_tailcalls.dll +// MVID: {2945C9C4-EED0-49EA-8A87-27475136401B} .custom instance void [System.Runtime]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 ) .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY -// Image base: 0x052D0000 +// Image base: 0x00000273609B0000 // =============== CLASS MEMBERS DECLARATION =================== @@ -271,6 +269,7 @@ .field public class IGenInterface`2 ig2 .field public class GenAbstractImpl`1 ga1 .field public class GenAbstractImpl`1 ga2 + .field public int32[] a .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { @@ -288,19 +287,19 @@ { // Code size 105 (0x69) .maxstack 4 - .locals init (class [System.Runtime.Extensions]System.Diagnostics.Stopwatch V_0, + .locals init (class [System.Runtime]System.Diagnostics.Stopwatch V_0, !!T V_1) IL_0000: ldstr "{0}: " IL_0005: ldarg.3 IL_0006: call void [System.Console]System.Console::Write(string, object) - IL_000b: call class [System.Runtime.Extensions]System.Diagnostics.Stopwatch [System.Runtime.Extensions]System.Diagnostics.Stopwatch::StartNew() + IL_000b: call class [System.Runtime]System.Diagnostics.Stopwatch [System.Runtime]System.Diagnostics.Stopwatch::StartNew() IL_0010: stloc.0 IL_0011: ldarg.1 IL_0012: callvirt instance !0 class [System.Runtime]System.Func`1::Invoke() IL_0017: stloc.1 IL_0018: ldloc.0 - IL_0019: callvirt instance void [System.Runtime.Extensions]System.Diagnostics.Stopwatch::Stop() + IL_0019: callvirt instance void [System.Runtime]System.Diagnostics.Stopwatch::Stop() IL_001e: ldloca.s V_1 IL_0020: ldarg.2 IL_0021: box !!T @@ -311,7 +310,7 @@ IL_0033: ldstr "OK in {1} ms" IL_0038: ldarg.3 IL_0039: ldloc.0 - IL_003a: callvirt instance int64 [System.Runtime.Extensions]System.Diagnostics.Stopwatch::get_ElapsedMilliseconds() + IL_003a: callvirt instance int64 [System.Runtime]System.Diagnostics.Stopwatch::get_ElapsedMilliseconds() IL_003f: box [System.Runtime]System.Int64 IL_0044: call void [System.Console]System.Console::WriteLine(string, object, @@ -349,7 +348,7 @@ IL_0008: stfld class [System.Runtime]System.Func`3 class Program/'<>c__DisplayClass7_1`1'::f IL_000d: ldarg.0 IL_000e: ldloc.0 - IL_000f: ldftn instance !0 class Program/'<>c__DisplayClass7_1`1'::'
b__29'() + IL_000f: ldftn instance !0 class Program/'<>c__DisplayClass7_1`1'::'
b__30'() IL_0015: newobj instance void class [System.Runtime]System.Func`1::.ctor(object, native int) IL_001a: ldarg.2 @@ -537,6 +536,24 @@ IL_000b: ret } // end of method '<>c__DisplayClass7_0'::'
b__28' + .method assembly hidebysig instance int32 + '
b__29'() cil managed + { + // Code size 24 (0x18) + .maxstack 8 + IL_0000: ldc.i4.0 + IL_0001: ldc.i4.0 + IL_0002: ldstr "string" + IL_0007: ldarg.0 + IL_0008: ldfld int32[] Program/'<>c__DisplayClass7_0'::a + IL_000d: call valuetype [System.Runtime]System.Span`1 valuetype [System.Runtime]System.Span`1::op_Implicit(!0[]) + IL_0012: call int32 Program::InstantiatingStub1(int32, + int32, + !!0, + valuetype [System.Runtime]System.Span`1) + IL_0017: ret + } // end of method '<>c__DisplayClass7_0'::'
b__29' + } // end of class '<>c__DisplayClass7_0' .class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass7_1`1' @@ -555,7 +572,7 @@ } // end of method '<>c__DisplayClass7_1`1'::.ctor .method assembly hidebysig instance !T - '
b__29'() cil managed + '
b__30'() cil managed { // Code size 18 (0x12) .maxstack 8 @@ -566,7 +583,7 @@ IL_000c: callvirt instance !2 class [System.Runtime]System.Func`3::Invoke(!0, !1) IL_0011: ret - } // end of method '<>c__DisplayClass7_1`1'::'
b__29' + } // end of method '<>c__DisplayClass7_1`1'::'
b__30' } // end of class '<>c__DisplayClass7_1`1' @@ -827,31 +844,31 @@ IL_0000: ldftn int32 Program::CalcStaticCalli(int32, int32) IL_0006: stloc.0 - IL_0007: ldftn int32 Program::CalcStaticCalliOther(int32, + IL_0007: ldloc.0 + IL_0008: stsfld native int Program::s_calcStaticCalli + IL_000d: ldftn int32 Program::CalcStaticCalliOther(int32, valuetype S32, int32) - IL_000d: stloc.1 - IL_000e: ldftn valuetype S32 Program::CalcStaticCalliRetbuf(int32, + IL_0013: stloc.1 + IL_0014: ldloc.1 + IL_0015: stsfld native int Program::s_calcStaticCalliOther + IL_001a: ldftn valuetype S32 Program::CalcStaticCalliRetbuf(int32, int32) - IL_0014: stloc.2 - IL_0015: ldftn valuetype S32 Program::CalcStaticCalliRetbufOther(int32, + IL_0020: stloc.2 + IL_0021: ldloc.2 + IL_0022: stsfld native int Program::s_calcStaticCalliRetbuf + IL_0027: ldftn valuetype S32 Program::CalcStaticCalliRetbufOther(int32, valuetype S32, int32) - IL_001b: stloc.3 - IL_001c: ldftn string Program::EmptyCalliOther() - IL_0022: stloc.s V_4 - IL_0024: ldftn instance string S16::InstanceMethod() - IL_002a: stloc.s V_5 - IL_002c: ldloc.0 - IL_002d: stsfld native int Program::s_calcStaticCalli - IL_0032: ldloc.1 - IL_0033: stsfld native int Program::s_calcStaticCalliOther - IL_0038: ldloc.2 - IL_0039: stsfld native int Program::s_calcStaticCalliRetbuf - IL_003e: ldloc.3 - IL_003f: stsfld native int Program::s_calcStaticCalliRetbufOther - IL_0044: ldloc.s V_4 - IL_0046: stsfld native int Program::s_emptyCalliOther + IL_002d: stloc.3 + IL_002e: ldloc.3 + IL_002f: stsfld native int Program::s_calcStaticCalliRetbufOther + IL_0034: ldftn string Program::EmptyCalliOther() + IL_003a: stloc.s V_4 + IL_003c: ldloc.s V_4 + IL_003e: stsfld native int Program::s_emptyCalliOther + IL_0043: ldftn instance string S16::InstanceMethod() + IL_0049: stloc.s V_5 IL_004b: ldloc.s V_5 IL_004d: stsfld native int Program::s_instanceMethodOnValueType IL_0052: ret @@ -861,7 +878,7 @@ Main() cil managed { .entrypoint - // Code size 1733 (0x6c5) + // Code size 1792 (0x700) .maxstack 4 .locals init (class Program/'<>c__DisplayClass7_0' V_0, int32 V_1, @@ -1530,24 +1547,48 @@ !!0, string) IL_069a: ldloc.0 - IL_069b: ldfld bool Program/'<>c__DisplayClass7_0'::result - IL_06a0: brfalse.s IL_06ae + IL_069b: ldc.i4 0xf4240 + IL_06a0: newarr [System.Runtime]System.Int32 + IL_06a5: stfld int32[] Program/'<>c__DisplayClass7_0'::a + IL_06aa: ldloc.0 + IL_06ab: ldfld int32[] Program/'<>c__DisplayClass7_0'::a + IL_06b0: ldc.i4.s 99 + IL_06b2: ldc.i4.1 + IL_06b3: stelem.i4 + IL_06b4: ldloc.0 + IL_06b5: ldloc.0 + IL_06b6: ldftn instance int32 Program/'<>c__DisplayClass7_0'::'
b__29'() + IL_06bc: newobj instance void class [System.Runtime]System.Func`1::.ctor(object, + native int) + IL_06c1: ldloc.0 + IL_06c2: ldfld int32[] Program/'<>c__DisplayClass7_0'::a + IL_06c7: ldlen + IL_06c8: conv.i4 + IL_06c9: ldc.i4.1 + IL_06ca: add + IL_06cb: ldstr "Instantiating stub direct" + IL_06d0: callvirt instance void Program/'<>c__DisplayClass7_0'::'
g__Test|0'(class [System.Runtime]System.Func`1, + !!0, + string) + IL_06d5: ldloc.0 + IL_06d6: ldfld bool Program/'<>c__DisplayClass7_0'::result + IL_06db: brfalse.s IL_06e9 - IL_06a2: ldstr "All tailcall-via-help succeeded" - IL_06a7: call void [System.Console]System.Console::WriteLine(string) - IL_06ac: br.s IL_06b8 + IL_06dd: ldstr "All tailcall-via-help succeeded" + IL_06e2: call void [System.Console]System.Console::WriteLine(string) + IL_06e7: br.s IL_06f3 - IL_06ae: ldstr "One or more failures in tailcall-via-help test" - IL_06b3: call void [System.Console]System.Console::WriteLine(string) - IL_06b8: ldloc.0 - IL_06b9: ldfld bool Program/'<>c__DisplayClass7_0'::result - IL_06be: brtrue.s IL_06c2 + IL_06e9: ldstr "One or more failures in tailcall-via-help test" + IL_06ee: call void [System.Console]System.Console::WriteLine(string) + IL_06f3: ldloc.0 + IL_06f4: ldfld bool Program/'<>c__DisplayClass7_0'::result + IL_06f9: brtrue.s IL_06fd - IL_06c0: ldc.i4.1 - IL_06c1: ret + IL_06fb: ldc.i4.1 + IL_06fc: ret - IL_06c2: ldc.i4.s 100 - IL_06c4: ret + IL_06fd: ldc.i4.s 100 + IL_06ff: ret } // end of method Program::Main .method public hidebysig static void Calc(int32& x, @@ -2023,7 +2064,7 @@ // Code size 41 (0x29) .maxstack 2 .locals init (int32 V_0) - IL_0000: call int32 [System.Runtime.Extensions]System.Environment::get_TickCount() + IL_0000: call int32 [System.Runtime]System.Environment::get_TickCount() IL_0005: ldc.i4.0 IL_0006: blt.s IL_000c @@ -2047,14 +2088,14 @@ IL_0028: ret } // end of method Program::EmptyCalli - .method private hidebysig static string + .method private hidebysig static string ValueTypeInstanceMethodCalli() cil managed noinlining { // Code size 51 (0x33) .maxstack 2 .locals init (valuetype S16 V_0, int32 V_1) - IL_0000: call int32 [System.Runtime.Extensions]System.Environment::get_TickCount() + IL_0000: call int32 [System.Runtime]System.Environment::get_TickCount() IL_0005: ldc.i4.0 IL_0006: blt.s IL_000c @@ -2088,7 +2129,7 @@ .maxstack 2 .locals init (valuetype S16 V_0, int32 V_1) - IL_0000: call int32 [System.Runtime.Extensions]System.Environment::get_TickCount() + IL_0000: call int32 [System.Runtime]System.Environment::get_TickCount() IL_0005: ldc.i4.0 IL_0006: blt.s IL_000c @@ -2631,6 +2672,85 @@ IL_0008: ret } // end of method Program::GenAbstractGInt + .method private hidebysig static int32 + InstantiatingStub1(int32 a, + int32 r, + !!T c, + valuetype [System.Runtime]System.Span`1 d) cil managed noinlining + { + // Code size 31 (0x1f) + .maxstack 14 + IL_0000: ldarg.2 + IL_0001: ldarg.2 + IL_0002: ldarg.2 + IL_0003: ldarg.2 + IL_0004: ldarg.2 + IL_0005: ldarg.2 + IL_0006: ldarg.2 + IL_0007: ldarg.2 + IL_0008: ldarg.0 + IL_0009: ldarg.1 + IL_000a: ldarg.3 + IL_000b: ldarg.1 + IL_000c: ldarga.s d + IL_000e: ldc.i4.s 99 + IL_0010: call instance !0& valuetype [System.Runtime]System.Span`1::get_Item(int32) + IL_0015: ldind.i4 + IL_0016: add + IL_0017: tail. + IL_0019: call int32 Program::InstantiatingStub1Other(!!0, + !!0, + !!0, + !!0, + !!0, + !!0, + !!0, + !!0, + int32, + int32, + valuetype [System.Runtime]System.Span`1, + int32) + IL_001e: ret + } // end of method Program::InstantiatingStub1 + + .method private hidebysig static int32 + InstantiatingStub1Other(!!T c0, + !!T c1, + !!T c2, + !!T c3, + !!T c4, + !!T c5, + !!T c6, + !!T c7, + int32 a, + int32 r, + valuetype [System.Runtime]System.Span`1 d, + int32 result) cil managed noinlining + { + // Code size 31 (0x1f) + .maxstack 4 + IL_0000: ldarg.s a + IL_0002: ldarga.s d + IL_0004: call instance int32 valuetype [System.Runtime]System.Span`1::get_Length() + IL_0009: bne.un.s IL_000e + + IL_000b: ldarg.s result + IL_000d: ret + + IL_000e: ldarg.s a + IL_0010: ldc.i4.1 + IL_0011: add + IL_0012: ldarg.s result + IL_0014: ldarg.0 + IL_0015: ldarg.s d + IL_0017: tail. + IL_0019: call int32 Program::InstantiatingStub1(int32, + int32, + !!0, + valuetype [System.Runtime]System.Span`1) + IL_001e: ret + } // end of method Program::InstantiatingStub1Other + .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { @@ -4038,14 +4158,15 @@ } // end of class GenAbstractImpl`1 -.class private auto ansi example1_Fody.ProcessedByFody +.class private auto ansi more_tailcalls_ProcessedByFody extends [System.Runtime]System.Object { - .field static assembly literal string FodyVersion = "6.1.1.0" - .field static assembly literal string InlineIL = "1.4.0.0" -} // end of class example1_Fody.ProcessedByFody + .field static assembly literal string FodyVersion = "6.2.4.0" + .field static assembly literal string InlineIL = "1.5.0.0" +} // end of class more_tailcalls_ProcessedByFody // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** +// WARNING: Created Win32 resource file more_tailcalls.res