Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move tailcall dispatcher into corelib #38938

Merged
merged 2 commits into from
Jul 10, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,40 @@ public static IntPtr AllocateTypeAssociatedMemory(Type type, int size)
[MethodImpl(MethodImplOptions.InternalCall)]
private static unsafe extern TailCallTls* GetTailCallInfo(IntPtr retAddrSlot, IntPtr* retAddr);

internal static unsafe void DispatchTailCalls(
jakobbotsch marked this conversation as resolved.
Show resolved Hide resolved
IntPtr callersRetAddrSlot,
delegate*<IntPtr, IntPtr, IntPtr*, void> callTarget,
IntPtr retVal)
{
IntPtr callersRetAddr;
TailCallTls* tls = GetTailCallInfo(callersRetAddrSlot, &callersRetAddr);
PortableTailCallFrame* prevFrame = tls->Frame;
if (callersRetAddr == prevFrame->TailCallAwareReturnAddress)
{
prevFrame->NextCall = callTarget;
return;
}

PortableTailCallFrame newFrame;
newFrame.Prev = prevFrame;

try
{
tls->Frame = &newFrame;

do
{
newFrame.NextCall = null;
callTarget(tls->ArgBuffer, retVal, &newFrame.TailCallAwareReturnAddress);
callTarget = newFrame.NextCall;
} while (callTarget != null);
}
finally
{
tls->Frame = prevFrame;
}
}

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern long GetILBytesJitted();

Expand Down Expand Up @@ -439,7 +473,7 @@ internal unsafe struct PortableTailCallFrame
{
public PortableTailCallFrame* Prev;
public IntPtr TailCallAwareReturnAddress;
public IntPtr NextCall;
public delegate*<IntPtr, IntPtr, IntPtr*, void> NextCall;
}

[StructLayout(LayoutKind.Sequential)]
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/src/vm/jitinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13939,7 +13939,7 @@ bool CEEInfo::getTailCallHelpersInternal(CORINFO_RESOLVED_TOKEN* callToken,
pResult->flags = (CORINFO_TAILCALL_HELPERS_FLAGS)outFlags;
pResult->hStoreArgs = (CORINFO_METHOD_HANDLE)pStoreArgsMD;
pResult->hCallTarget = (CORINFO_METHOD_HANDLE)pCallTargetMD;
pResult->hDispatcher = (CORINFO_METHOD_HANDLE)TailCallHelp::GetOrCreateTailCallDispatcherMD();
pResult->hDispatcher = (CORINFO_METHOD_HANDLE)TailCallHelp::GetTailCallDispatcherMD();
return true;
}

Expand Down
5 changes: 3 additions & 2 deletions src/coreclr/src/vm/mscorlib.h
Original file line number Diff line number Diff line change
Expand Up @@ -706,9 +706,10 @@ DEFINE_METHOD(RUNTIME_HELPERS, GET_RAW_ARRAY_DATA, GetRawArrayData, No
DEFINE_METHOD(RUNTIME_HELPERS, GET_UNINITIALIZED_OBJECT, GetUninitializedObject, NoSig)
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, 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, FREE_TAILCALL_ARG_BUFFER, FreeTailCallArgBuffer, SM_RetVoid)
DEFINE_METHOD(RUNTIME_HELPERS, DISPATCH_TAILCALLS, DispatchTailCalls, NoSig)

DEFINE_CLASS(UNSAFE, InternalCompilerServices, Unsafe)
DEFINE_METHOD(UNSAFE, AS_POINTER, AsPointer, NoSig)
Expand Down
223 changes: 8 additions & 215 deletions src/coreclr/src/vm/tailcallhelp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,227 +107,20 @@ struct TailCallInfo
}
};

static MethodDesc* s_tailCallDispatcherMD;
MethodDesc* TailCallHelp::GetTailCallDispatcherMD()
{
LIMITED_METHOD_CONTRACT;
CONTRACTL
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(ThrowOutOfMemory());
}
CONTRACTL_END;
jakobbotsch marked this conversation as resolved.
Show resolved Hide resolved

return s_tailCallDispatcherMD;
return MscorlibBinder::GetMethod(METHOD__RUNTIME_HELPERS__DISPATCH_TAILCALLS);
}


// This creates the dispatcher used to dispatch sequences of tailcalls. In C#
// code it is the following function. Once C# gets function pointer support this
// function can be put in System.Private.CoreLib.
// private static unsafe void DispatchTailCalls(
// IntPtr callersRetAddrSlot, IntPtr callTarget, IntPtr retVal)
// {
// IntPtr callersRetAddr;
// TailCallTls* tls = GetTailCallInfo(callersRetAddrSlot, &callersRetAddr);
// PortableTailCallFrame* prevFrame = tls->Frame;
// if (callersRetAddr == prevFrame->TailCallAwareReturnAddress)
// {
// prevFrame->NextCall = callTarget;
// return;
// }
//
// PortableTailCallFrame newFrame;
// newFrame.Prev = prevFrame;
//
// try
// {
// tls->Frame = &newFrame;
//
// do
// {
// newFrame.NextCall = IntPtr.Zero;
// var fptr = (func* void(IntPtr, IntPtr, void*))callTarget;
// fptr(tls->ArgBuffer, retVal, &newFrame.TailCallAwareReturnAddress);
// callTarget = newFrame.NextCall;
// } while (callTarget != IntPtr.Zero);
// }
// finally
// {
// tls->Frame = prevFrame;
// }
// }
MethodDesc* TailCallHelp::GetOrCreateTailCallDispatcherMD()
{
STANDARD_VM_CONTRACT;

if (s_tailCallDispatcherMD != NULL)
return s_tailCallDispatcherMD;

SigBuilder sigBuilder;
sigBuilder.AppendByte(IMAGE_CEE_CS_CALLCONV_DEFAULT);

sigBuilder.AppendData(3);
sigBuilder.AppendElementType(ELEMENT_TYPE_VOID);

sigBuilder.AppendElementType(ELEMENT_TYPE_I);
sigBuilder.AppendElementType(ELEMENT_TYPE_I);
sigBuilder.AppendElementType(ELEMENT_TYPE_I);

const int ARG_CALLERS_RET_ADDR_SLOT = 0;
const int ARG_CALL_TARGET = 1;
const int ARG_RET_VAL = 2;

DWORD cbSig;
PCCOR_SIGNATURE pSig = AllocateSignature(
MscorlibBinder::GetModule()->GetLoaderAllocator(), sigBuilder, &cbSig);

SigTypeContext emptyCtx;

ILStubLinker sl(MscorlibBinder::GetModule(),
Signature(pSig, cbSig),
&emptyCtx,
NULL,
FALSE,
FALSE);

ILCodeStream* pCode = sl.NewCodeStream(ILStubLinker::kDispatch);

DWORD retAddrLcl = pCode->NewLocal(ELEMENT_TYPE_I);
DWORD tlsLcl = pCode->NewLocal(ELEMENT_TYPE_I);
DWORD prevFrameLcl = pCode->NewLocal(ELEMENT_TYPE_I);
TypeHandle frameTyHnd = MscorlibBinder::GetClass(CLASS__PORTABLE_TAIL_CALL_FRAME);
DWORD newFrameEntryLcl = pCode->NewLocal(LocalDesc(frameTyHnd));
DWORD argsLcl = pCode->NewLocal(ELEMENT_TYPE_I);
ILCodeLabel* noUnwindLbl = pCode->NewCodeLabel();
ILCodeLabel* loopStart = pCode->NewCodeLabel();
ILCodeLabel* afterTryFinally = pCode->NewCodeLabel();

// tls = RuntimeHelpers.GetTailcallInfo(callersRetAddrSlot, &retAddr);
pCode->EmitLDARG(ARG_CALLERS_RET_ADDR_SLOT);
pCode->EmitLDLOCA(retAddrLcl);
pCode->EmitCALL(METHOD__RUNTIME_HELPERS__GET_TAILCALL_INFO, 2, 1);
pCode->EmitSTLOC(tlsLcl);

// prevFrame = tls.Frame;
pCode->EmitLDLOC(tlsLcl);
pCode->EmitLDFLD(FIELD__TAIL_CALL_TLS__FRAME);
pCode->EmitSTLOC(prevFrameLcl);

// if (retAddr != prevFrame.TailCallAwareReturnAddress) goto noUnwindLbl;
pCode->EmitLDLOC(retAddrLcl);
pCode->EmitLDLOC(prevFrameLcl);
pCode->EmitLDFLD(FIELD__PORTABLE_TAIL_CALL_FRAME__TAILCALL_AWARE_RETURN_ADDRESS);
pCode->EmitBNE_UN(noUnwindLbl);

// prevFrame->NextCall = callTarget;
pCode->EmitLDLOC(prevFrameLcl);
pCode->EmitLDARG(ARG_CALL_TARGET);
pCode->EmitSTFLD(FIELD__PORTABLE_TAIL_CALL_FRAME__NEXT_CALL);

// return;
pCode->EmitRET();

// Ok, we are the "first" dispatcher.
pCode->EmitLabel(noUnwindLbl);

// newFrameEntry.Prev = prevFrame;
pCode->EmitLDLOCA(newFrameEntryLcl);
pCode->EmitLDLOC(prevFrameLcl);
pCode->EmitSTFLD(FIELD__PORTABLE_TAIL_CALL_FRAME__PREV);

// try {
pCode->BeginTryBlock();

// tls->Frame = &newFrameEntry;
pCode->EmitLDLOC(tlsLcl);
pCode->EmitLDLOCA(newFrameEntryLcl);
pCode->EmitSTFLD(FIELD__TAIL_CALL_TLS__FRAME);

// do {
pCode->EmitLabel(loopStart);

// newFrameEntry.NextCall = 0
pCode->EmitLDLOCA(newFrameEntryLcl);
pCode->EmitLDC(0);
pCode->EmitCONV_I();
pCode->EmitSTFLD(FIELD__PORTABLE_TAIL_CALL_FRAME__NEXT_CALL);

SigBuilder calliSig;
calliSig.AppendByte(IMAGE_CEE_CS_CALLCONV_DEFAULT);
calliSig.AppendData(3);
calliSig.AppendElementType(ELEMENT_TYPE_VOID);
calliSig.AppendElementType(ELEMENT_TYPE_I);
calliSig.AppendElementType(ELEMENT_TYPE_I);
calliSig.AppendElementType(ELEMENT_TYPE_I);

DWORD cbCalliSig;
PCCOR_SIGNATURE pCalliSig = (PCCOR_SIGNATURE)calliSig.GetSignature(&cbCalliSig);

// callTarget(tls->ArgBuffer, retVal, &newFrameEntry.TailCallAwareReturnAddress)
// arg buffer
pCode->EmitLDLOC(tlsLcl);
pCode->EmitLDFLD(FIELD__TAIL_CALL_TLS__ARG_BUFFER);

// ret val
pCode->EmitLDARG(ARG_RET_VAL);

// TailCallAwareReturnAddress
pCode->EmitLDLOCA(newFrameEntryLcl);
pCode->EmitLDFLDA(FIELD__PORTABLE_TAIL_CALL_FRAME__TAILCALL_AWARE_RETURN_ADDRESS);

// callTarget
pCode->EmitLDARG(ARG_CALL_TARGET);

pCode->EmitCALLI(pCode->GetSigToken(pCalliSig, cbCalliSig), 2, 0);

// callTarget = newFrameEntry.NextCall;
pCode->EmitLDLOC(newFrameEntryLcl);
pCode->EmitLDFLD(FIELD__PORTABLE_TAIL_CALL_FRAME__NEXT_CALL);
pCode->EmitSTARG(ARG_CALL_TARGET);

// } while (callTarget != IntPtr.Zero);
pCode->EmitLDARG(ARG_CALL_TARGET);
pCode->EmitBRTRUE(loopStart);

// }
pCode->EmitLEAVE(afterTryFinally);
pCode->EndTryBlock();

// finally {
pCode->BeginFinallyBlock();

// tls->Frame = prevFrame;
pCode->EmitLDLOC(tlsLcl);
pCode->EmitLDLOC(prevFrameLcl);
pCode->EmitSTFLD(FIELD__TAIL_CALL_TLS__FRAME);

// }
pCode->EmitENDFINALLY();
pCode->EndFinallyBlock();

// afterTryFinally:
pCode->EmitLabel(afterTryFinally);

// return;
pCode->EmitRET();

Module* mscorlib = MscorlibBinder::GetModule();
MethodDesc* pDispatchTailCallsMD =
ILStubCache::CreateAndLinkNewILStubMethodDesc(
MscorlibBinder::GetModule()->GetLoaderAllocator(),
mscorlib->GetILStubCache()->GetOrCreateStubMethodTable(mscorlib),
ILSTUB_TAILCALL_DISPATCH,
mscorlib,
pSig, cbSig,
&emptyCtx,
&sl);

#ifdef _DEBUG
LOG((LF_STUBS, LL_INFO1000, "TAILCALLHELP: DispatchTailCalls IL created\n"));
sl.LogILStub(CORJIT_FLAGS());
#endif

// We might waste a MethodDesc here if we lose the race, but that is very
// unlikely and since this initialization only happens once not a big deal.
InterlockedCompareExchangeT(&s_tailCallDispatcherMD, pDispatchTailCallsMD, NULL);
return s_tailCallDispatcherMD;
}

void TailCallHelp::CreateTailCallHelperStubs(
MethodDesc* pCallerMD, MethodDesc* pCalleeMD,
MetaSig& callSiteSig, bool virt, bool thisArgByRef,
Expand Down