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);
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 */;
+ }
}
}
{
public PortableTailCallFrame* Frame;
public IntPtr ArgBuffer;
- private IntPtr _argBufferSize;
- private IntPtr _argBufferGCDesc;
- private fixed byte _argBufferInline[64];
}
}
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,
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.
// 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())
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
}
//------------------------------------------------------------------------
-// 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
//
// Arguments:
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)
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)
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)
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)
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<PTR_BYTE>(gcDesc));
+ if (argBuffer->State == TAILCALLARGBUFFER_ABANDONED)
+ return;
+
+ bool instArgOnly = argBuffer->State == TAILCALLARGBUFFER_INSTARG_ONLY;
+
+ GCRefMapDecoder decoder(static_cast<PTR_BYTE>(argBuffer->GCDesc));
while (!decoder.AtEnd())
{
int pos = decoder.CurrentPos();
int token = decoder.ReadToken();
- PTR_TADDR ppObj = dac_cast<PTR_TADDR>(argBuffer + pos * sizeof(TADDR));
+ PTR_TADDR ppObj = dac_cast<PTR_TADDR>(((BYTE*)argBuffer->Args) + pos * sizeof(TADDR));
switch (token)
{
case GCREFMAP_SKIP:
break;
case GCREFMAP_REF:
- fn(dac_cast<PTR_PTR_Object>(ppObj), sc, CHECK_APP_DOMAIN);
+ if (!instArgOnly)
+ fn(dac_cast<PTR_PTR_Object>(ppObj), sc, CHECK_APP_DOMAIN);
break;
case GCREFMAP_INTERIOR:
- PromoteCarefully(fn, dac_cast<PTR_PTR_Object>(ppObj), sc, GC_CALL_INTERIOR);
+ if (!instArgOnly)
+ PromoteCarefully(fn, dac_cast<PTR_PTR_Object>(ppObj), sc, GC_CALL_INTERIOR);
break;
case GCREFMAP_METHOD_PARAM:
if (sc->promotion)
TailCallHelp::CreateTailCallHelperStubs(
m_pMethodBeingCompiled, pTargetMD,
- msig, isCallvirt, isThisArgByRef,
+ msig, isCallvirt, isThisArgByRef, sig->hasTypeArg(),
&pStoreArgsMD, &needsTarget,
&pCallTargetMD);
_ASSERTE(size >= 0);
- void* result = GetThread()->GetTailCallTls()->AllocArgBuffer(static_cast<size_t>(size), gcDesc);
+ void* result = GetThread()->GetTailCallTls()->AllocArgBuffer(size, gcDesc);
if (result == NULL)
FCThrow(kOutOfMemoryException);
}
FCIMPLEND
-FCIMPL0(void, TailCallHelp::FreeTailCallArgBuffer)
-{
- FCALL_CONTRACT;
-
- GetThread()->GetTailCallTls()->FreeArgBuffer();
-}
-FCIMPLEND
-
FCIMPL2(void*, TailCallHelp::GetTailCallInfo, void** retAddrSlot, void** retAddr)
{
FCALL_CONTRACT;
struct ArgBufferLayout
{
bool HasTargetAddress;
+ bool HasInstArg;
unsigned int TargetAddressOffset;
InlineSArray<ArgBufferValue, 8> Values;
unsigned int Size;
ArgBufferLayout()
: HasTargetAddress(false)
+ , HasInstArg(false)
, TargetAddressOffset(0)
, Size(0)
{
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)
{
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);
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)
{
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)
addValue(tyHnd);
}
+#ifdef TARGET_X86
+ if (hasInstArg)
+ {
+ addValue(TypeHandle(CoreLibBinder::GetElementType(ELEMENT_TYPE_I)));
+ }
+#endif
+
if (storeTarget)
{
offs = AlignUp(offs, TARGET_POINTER_SIZE);
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<BYTE*>(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;
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;
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();
}
auto emitOffs = [&](UINT offs)
{
pCode->EmitLDARG(ARG_ARG_BUFFER);
- if (offs != 0)
- {
- pCode->EmitLDC(offs);
- pCode->EmitADD();
- }
+ pCode->EmitLDC(offs);
+ pCode->EmitADD();
};
- StackSArray<DWORD> 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;
// 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<int>(argLocals.GetCount()),
+ static_cast<int>(info.ArgBufLayout.Values.GetCount()),
numRetVals);
}
else
{
pCode->EmitCALL(
pCode->GetToken(info.Callee),
- static_cast<int>(argLocals.GetCount()),
+ static_cast<int>(info.ArgBufLayout.Values.GetCount()),
numRetVals);
}
}
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);
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<int>(argLocals.GetCount()),
+ static_cast<int>(info.ArgBufLayout.Values.GetCount()),
numRetVals);
}
{
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);
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);
// so casting away const is ok here.
: m_frame(const_cast<PortableTailCallFrame*>(&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
{
}
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_)
delete m_pIBCInfo;
}
+ m_tailCallTls.FreeArgBuffer();
+
#ifdef FEATURE_EVENT_TRACE
// Destruct the thread local type cache for allocation sampling
if(m_pAllLoggedTypes) {
#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"
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; }
{
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;
}
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
IL.Emit.Callvirt(new MethodRef(typeof(GenAbstract<int>), nameof(GenAbstract<int>.G)));
return IL.Return<string>();
}
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static int InstantiatingStub1<T>(int a, int r, T c, Span<int> 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<int>();
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static int InstantiatingStub1Other<T>(T c0, T c1, T c2, T c3, T c4, T c5, T c6, T c7, int a, int r, Span<int> 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<int>();
+ }
+ }
}
class Instance
.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
// .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 ===================
.field public class IGenInterface`2<string,object> ig2
.field public class GenAbstractImpl`1<string> ga1
.field public class GenAbstractImpl`1<int32> ga2
+ .field public int32[] a
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
{
// 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<!!T>::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
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,
IL_0008: stfld class [System.Runtime]System.Func`3<int32,int32,!0> class Program/'<>c__DisplayClass7_1`1'<!!T>::f
IL_000d: ldarg.0
IL_000e: ldloc.0
- IL_000f: ldftn instance !0 class Program/'<>c__DisplayClass7_1`1'<!!T>::'<Main>b__29'()
+ IL_000f: ldftn instance !0 class Program/'<>c__DisplayClass7_1`1'<!!T>::'<Main>b__30'()
IL_0015: newobj instance void class [System.Runtime]System.Func`1<!!T>::.ctor(object,
native int)
IL_001a: ldarg.2
IL_000b: ret
} // end of method '<>c__DisplayClass7_0'::'<Main>b__28'
+ .method assembly hidebysig instance int32
+ '<Main>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<!0> valuetype [System.Runtime]System.Span`1<int32>::op_Implicit(!0[])
+ IL_0012: call int32 Program::InstantiatingStub1<string>(int32,
+ int32,
+ !!0,
+ valuetype [System.Runtime]System.Span`1<int32>)
+ IL_0017: ret
+ } // end of method '<>c__DisplayClass7_0'::'<Main>b__29'
+
} // end of class '<>c__DisplayClass7_0'
.class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass7_1`1'<T>
} // end of method '<>c__DisplayClass7_1`1'::.ctor
.method assembly hidebysig instance !T
- '<Main>b__29'() cil managed
+ '<Main>b__30'() cil managed
{
// Code size 18 (0x12)
.maxstack 8
IL_000c: callvirt instance !2 class [System.Runtime]System.Func`3<int32,int32,!T>::Invoke(!0,
!1)
IL_0011: ret
- } // end of method '<>c__DisplayClass7_1`1'::'<Main>b__29'
+ } // end of method '<>c__DisplayClass7_1`1'::'<Main>b__30'
} // end of class '<>c__DisplayClass7_1`1'
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
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,
!!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'::'<Main>b__29'()
+ IL_06bc: newobj instance void class [System.Runtime]System.Func`1<int32>::.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'::'<Main>g__Test|0'<int32>(class [System.Runtime]System.Func`1<!!0>,
+ !!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,
// 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
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
.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
IL_0008: ret
} // end of method Program::GenAbstractGInt
+ .method private hidebysig static int32
+ InstantiatingStub1<T>(int32 a,
+ int32 r,
+ !!T c,
+ valuetype [System.Runtime]System.Span`1<int32> 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<int32>::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,
+ !!0,
+ int32,
+ int32,
+ valuetype [System.Runtime]System.Span`1<int32>,
+ int32)
+ IL_001e: ret
+ } // end of method Program::InstantiatingStub1
+
+ .method private hidebysig static int32
+ InstantiatingStub1Other<T>(!!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<int32> 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<int32>::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<!!0>(int32,
+ int32,
+ !!0,
+ valuetype [System.Runtime]System.Span`1<int32>)
+ IL_001e: ret
+ } // end of method Program::InstantiatingStub1Other
+
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
} // 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