Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion src/coreclr/debug/daccess/dacdbiimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1352,7 +1352,7 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetNativeCodeInfo(VMPTR_DomainAss
MethodDesc* pMethodDesc = FindLoadedMethodRefOrDef(pModule, functionToken);
if (pMethodDesc != NULL && pMethodDesc->IsAsyncThunkMethod())
{
MethodDesc* pAsyncVariant = pMethodDesc->GetAsyncOtherVariantNoCreate();
MethodDesc* pAsyncVariant = pMethodDesc->GetOrdinaryVariantNoCreate();
if (pAsyncVariant != NULL)
{
pMethodDesc = pAsyncVariant;
Expand Down
222 changes: 118 additions & 104 deletions src/coreclr/vm/asyncthunks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,27 @@ bool MethodDesc::TryGenerateAsyncThunk(DynamicResolver** resolver, COR_ILMETHOD_
return false;
}

MethodDesc *pAsyncOtherVariant = this->GetAsyncOtherVariant();
MethodDesc* pAsyncOtherVariant = nullptr;
if (!IsAsyncMethod())
{
// a non-async thunk is implemented in terms of the async variant which has user code
pAsyncOtherVariant = this->GetAsyncVariant();
}
else
{
if (!IsReturnDroppingThunk())
{
// an async thunk is implemented in terms of non-async variant
pAsyncOtherVariant = this->GetOrdinaryVariant();
}
else
{
// this is a special void-returning async variant that calls
// the normal async variant and drops the result
pAsyncOtherVariant = this->GetAsyncVariant();
}
}

_ASSERTE(!IsWrapperStub() && !pAsyncOtherVariant->IsWrapperStub());

MetaSig msig(this);
Expand All @@ -38,13 +58,20 @@ bool MethodDesc::TryGenerateAsyncThunk(DynamicResolver** resolver, COR_ILMETHOD_
pAsyncOtherVariant,
(ILStubLinkerFlags)ILSTUB_LINKER_FLAG_NONE);

if (IsAsyncMethod())
if (!IsAsyncMethod())
{
EmitAsyncMethodThunk(pAsyncOtherVariant, msig, &sl);
EmitTaskReturningThunk(pAsyncOtherVariant, msig, &sl);
}
else
{
EmitTaskReturningThunk(pAsyncOtherVariant, msig, &sl);
if (IsReturnDroppingThunk())
{
EmitReturnDroppingThunk(pAsyncOtherVariant, msig, &sl);
}
else
{
EmitAsyncMethodThunk(pAsyncOtherVariant, msig, &sl);
}
}

NewHolder<ILStubResolver> ilResolver = new ILStubResolver();
Expand Down Expand Up @@ -132,60 +159,7 @@ void MethodDesc::EmitTaskReturningThunk(MethodDesc* pAsyncCallVariant, MetaSig&
pCode->EmitLDARG(localArg++);
}

int token;
Comment thread
VSadov marked this conversation as resolved.
_ASSERTE(!pAsyncCallVariant->IsWrapperStub());
if (pAsyncCallVariant->HasClassOrMethodInstantiation())
{
// For generic code emit generic signatures.
int typeSigToken = mdTokenNil;
if (pAsyncCallVariant->HasClassInstantiation())
{
SigBuilder typeSigBuilder;
typeSigBuilder.AppendElementType(ELEMENT_TYPE_GENERICINST);
typeSigBuilder.AppendElementType(ELEMENT_TYPE_INTERNAL);
// TODO: (async) Encoding potentially shared method tables in
// signatures of tokens seems odd, but this hits assert
// with the typical method table.
typeSigBuilder.AppendPointer(pAsyncCallVariant->GetMethodTable());
DWORD numClassTypeArgs = pAsyncCallVariant->GetNumGenericClassArgs();
typeSigBuilder.AppendData(numClassTypeArgs);
for (DWORD i = 0; i < numClassTypeArgs; ++i)
{
typeSigBuilder.AppendElementType(ELEMENT_TYPE_VAR);
typeSigBuilder.AppendData(i);
}

DWORD typeSigLen;
PCCOR_SIGNATURE typeSig = (PCCOR_SIGNATURE)typeSigBuilder.GetSignature(&typeSigLen);
typeSigToken = pCode->GetSigToken(typeSig, typeSigLen);
}

if (pAsyncCallVariant->HasMethodInstantiation())
{
SigBuilder methodSigBuilder;
DWORD numMethodTypeArgs = pAsyncCallVariant->GetNumGenericMethodArgs();
methodSigBuilder.AppendByte(IMAGE_CEE_CS_CALLCONV_GENERICINST);
methodSigBuilder.AppendData(numMethodTypeArgs);
for (DWORD i = 0; i < numMethodTypeArgs; ++i)
{
methodSigBuilder.AppendElementType(ELEMENT_TYPE_MVAR);
methodSigBuilder.AppendData(i);
}

DWORD sigLen;
PCCOR_SIGNATURE sig = (PCCOR_SIGNATURE)methodSigBuilder.GetSignature(&sigLen);
int methodSigToken = pCode->GetSigToken(sig, sigLen);
token = pCode->GetToken(pAsyncCallVariant, typeSigToken, methodSigToken);
}
else
{
token = pCode->GetToken(pAsyncCallVariant, typeSigToken);
}
}
else
{
token = pCode->GetToken(pAsyncCallVariant);
}
int token = GetTokenForThunkTarget(pCode, pAsyncCallVariant);

pCode->EmitCALL(token, localArg, logicalResultLocal != UINT_MAX ? 1 : 0);

Expand Down Expand Up @@ -429,56 +403,24 @@ int MethodDesc::GetTokenForGenericTypeMethodCallWithAsyncReturnType(ILCodeStream
return pCode->GetToken(md, typeSigToken);
}

// Provided a Task-returning method, emits an async wrapper.
// The emitted code matches method EmitAsyncMethodThunk in the Managed Type System.
void MethodDesc::EmitAsyncMethodThunk(MethodDesc* pTaskReturningVariant, MetaSig& msig, ILStubLinker* pSL)
int MethodDesc::GetTokenForThunkTarget(ILCodeStream* pCode, MethodDesc* md)
{
_ASSERTE(!pTaskReturningVariant->IsAsyncThunkMethod());
_ASSERTE(!pTaskReturningVariant->IsVoid());

// Implement IL that is effectively the following:
// {
// Task task = other(arg);
// if (!task.IsCompleted)
// {
// // Magic function which will suspend the current run of async methods
// AsyncHelpers.TransparentAwait(task);
// }
// return AsyncHelpers.CompletedTaskResult(task);
// }

// For ValueTask:

// {
// ValueTask vt = other(arg);
// if (!vt.IsCompleted)
// {
// taskOrNotifier = vt.AsTaskOrNotifier()

// // Magic function which will suspend the current run of async methods
// AsyncHelpers.TransparentAwait(taskOrNotifier);
// }

// return vt.Result/vt.ThrowIfCompletedUnsuccessfully();
// }
ILCodeStream* pCode = pSL->NewCodeStream(ILStubLinker::kDispatch);

int userFuncToken;
_ASSERTE(!pTaskReturningVariant->IsWrapperStub());
if (pTaskReturningVariant->HasClassOrMethodInstantiation())
int token;
_ASSERTE(!md->IsWrapperStub());
if (md->HasClassOrMethodInstantiation())
{
// For generic code emit generic signatures.
int typeSigToken = mdTokenNil;
if (pTaskReturningVariant->HasClassInstantiation())
if (md->HasClassInstantiation())
{
SigBuilder typeSigBuilder;
typeSigBuilder.AppendElementType(ELEMENT_TYPE_GENERICINST);
typeSigBuilder.AppendElementType(ELEMENT_TYPE_INTERNAL);
// TODO: (async) Encoding potentially shared method tables in
// signatures of tokens seems odd, but this hits assert
// with the typical method table.
typeSigBuilder.AppendPointer(pTaskReturningVariant->GetMethodTable());
DWORD numClassTypeArgs = pTaskReturningVariant->GetNumGenericClassArgs();
typeSigBuilder.AppendPointer(md->GetMethodTable());
DWORD numClassTypeArgs = md->GetNumGenericClassArgs();
typeSigBuilder.AppendData(numClassTypeArgs);
for (DWORD i = 0; i < numClassTypeArgs; ++i)
{
Expand All @@ -491,10 +433,10 @@ void MethodDesc::EmitAsyncMethodThunk(MethodDesc* pTaskReturningVariant, MetaSig
typeSigToken = pCode->GetSigToken(typeSig, typeSigLen);
}

if (pTaskReturningVariant->HasMethodInstantiation())
if (md->HasMethodInstantiation())
{
SigBuilder methodSigBuilder;
DWORD numMethodTypeArgs = pTaskReturningVariant->GetNumGenericMethodArgs();
DWORD numMethodTypeArgs = md->GetNumGenericMethodArgs();
methodSigBuilder.AppendByte(IMAGE_CEE_CS_CALLCONV_GENERICINST);
methodSigBuilder.AppendData(numMethodTypeArgs);
for (DWORD i = 0; i < numMethodTypeArgs; ++i)
Expand All @@ -506,18 +448,57 @@ void MethodDesc::EmitAsyncMethodThunk(MethodDesc* pTaskReturningVariant, MetaSig
DWORD sigLen;
PCCOR_SIGNATURE sig = (PCCOR_SIGNATURE)methodSigBuilder.GetSignature(&sigLen);
int methodSigToken = pCode->GetSigToken(sig, sigLen);
userFuncToken = pCode->GetToken(pTaskReturningVariant, typeSigToken, methodSigToken);
token = pCode->GetToken(md, typeSigToken, methodSigToken);
}
else
{
userFuncToken = pCode->GetToken(pTaskReturningVariant, typeSigToken);
token = pCode->GetToken(md, typeSigToken);
}
}
else
{
userFuncToken = pCode->GetToken(pTaskReturningVariant);
token = pCode->GetToken(md);
}

return token;
}

// Provided a Task-returning method, emits an async wrapper.
// The emitted code matches method EmitAsyncMethodThunk in the Managed Type System.
void MethodDesc::EmitAsyncMethodThunk(MethodDesc* pTaskReturningVariant, MetaSig& msig, ILStubLinker* pSL)
{
_ASSERTE(!pTaskReturningVariant->IsAsyncThunkMethod());
_ASSERTE(!pTaskReturningVariant->IsVoid());

// Implement IL that is effectively the following:
// {
// Task task = other(arg);
// if (!task.IsCompleted)
// {
// // Magic function which will suspend the current run of async methods
// AsyncHelpers.TransparentAwait(task);
// }
// return AsyncHelpers.CompletedTaskResult(task);
// }

// For ValueTask:

// {
// ValueTask vt = other(arg);
// if (!vt.IsCompleted)
// {
// taskOrNotifier = vt.AsTaskOrNotifier()

// // Magic function which will suspend the current run of async methods
// AsyncHelpers.TransparentAwait(taskOrNotifier);
// }

// return vt.Result/vt.ThrowIfCompletedUnsuccessfully();
// }
ILCodeStream* pCode = pSL->NewCodeStream(ILStubLinker::kDispatch);

int token = GetTokenForThunkTarget(pCode, pTaskReturningVariant);

DWORD localArg = 0;
if (msig.HasThis())
{
Expand All @@ -532,11 +513,11 @@ void MethodDesc::EmitAsyncMethodThunk(MethodDesc* pTaskReturningVariant, MetaSig
if (pTaskReturningVariant->IsAbstract())
{
_ASSERTE(pTaskReturningVariant->IsCLRToCOMCall());
pCode->EmitCALLVIRT(userFuncToken, localArg, 1);
pCode->EmitCALLVIRT(token, localArg, 1);
}
else
{
pCode->EmitCALL(userFuncToken, localArg, 1);
pCode->EmitCALL(token, localArg, 1);
}

TypeHandle thLogicalRetType = msig.GetRetTypeHandleThrowing();
Expand Down Expand Up @@ -637,3 +618,36 @@ void MethodDesc::EmitAsyncMethodThunk(MethodDesc* pTaskReturningVariant, MetaSig
pCode->EmitRET();
}
}

// Provided an async variant, emits an async wrapper that drops the returned value.
// Used in the covariant return scenario.
void MethodDesc::EmitReturnDroppingThunk(MethodDesc* pAsyncOtherVariant, MetaSig& msig, ILStubLinker* pSL)
{
_ASSERTE(pAsyncOtherVariant->IsAsyncVariantMethod());

_ASSERTE(!pAsyncOtherVariant->IsVoid());
_ASSERTE(pAsyncOtherVariant->IsVirtual());
_ASSERTE(this->IsVoid());
_ASSERTE(this->IsVirtual());

// Implement IL that is effectively the following:
// {
// this.other(arg); // CALLVIRT
// return;
// }
ILCodeStream* pCode = pSL->NewCodeStream(ILStubLinker::kDispatch);
int token = GetTokenForThunkTarget(pCode, pAsyncOtherVariant);

DWORD localArg = 0;
pCode->EmitLDARG(localArg++);
for (UINT iArg = 0; iArg < msig.NumFixedArgs(); iArg++)
{
pCode->EmitLDARG(localArg++);
}

// other(arg)
pCode->EmitCALLVIRT(token, localArg, 1);
Comment thread
VSadov marked this conversation as resolved.
// return;
pCode->EmitPOP();
pCode->EmitRET();
}
6 changes: 0 additions & 6 deletions src/coreclr/vm/class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1366,12 +1366,6 @@ void ClassLoader::ValidateMethodsWithCovariantReturnTypes(MethodTable* pMT)
continue;
}
MethodDesc* pMD = pMT->GetMethodDescForSlot(i);

// Skip validation for async variant methods, as they have different signatures by design
Comment thread
VSadov marked this conversation as resolved.
// to support the async calling convention
if (pMD->IsAsyncVariantMethod())
continue;

MethodDesc* pParentMD = pParentMT->GetMethodDescForSlot(i);

if (pMD == pParentMD)
Expand Down
19 changes: 12 additions & 7 deletions src/coreclr/vm/genmeth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -748,9 +748,9 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
BOOL forceBoxedEntryPoint,
Instantiation methodInst,
BOOL allowInstParam,
AsyncVariantLookup asyncVariantLookup,
BOOL forceRemotableMethod,
BOOL allowCreate,
AsyncVariantLookup asyncVariantLookup,
ClassLoadLevel level)
{
CONTRACT(MethodDesc*)
Expand Down Expand Up @@ -788,7 +788,7 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
methodInst.IsEmpty() &&
!forceBoxedEntryPoint &&
!pDefMD->IsUnboxingStub() &&
asyncVariantLookup == AsyncVariantLookup::MatchingAsyncVariant)
pDefMD->MatchesAsyncVariantLookup(asyncVariantLookup))
{
// Make sure that pDefMD->GetMethodTable() and pExactMT are related types even
// if we took the fast path.
Expand Down Expand Up @@ -817,7 +817,9 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
COMPlusThrowHR(COR_E_TYPELOAD);
}

if (pDefMD->HasClassOrMethodInstantiation() || !methodInst.IsEmpty() || asyncVariantLookup == AsyncVariantLookup::AsyncOtherVariant)
if (pDefMD->HasClassOrMethodInstantiation() ||
!methodInst.IsEmpty() ||
!pDefMD->MatchesAsyncVariantLookup(asyncVariantLookup))
{
// General checks related to generics: arity (if any) must match and generic method
// instantiation (if any) must be well-formed.
Expand Down Expand Up @@ -845,8 +847,8 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
if ( methodInst.IsEmpty()
&& (allowInstParam || !pMDescInCanonMT->RequiresInstArg())
&& (forceBoxedEntryPoint == pMDescInCanonMT->IsUnboxingStub())
&& (!forceRemotableMethod || !pMDescInCanonMT->IsInterface()
|| !pMDescInCanonMT->GetMethodTable()->IsSharedByGenericInstantiations()) )
&& (!forceRemotableMethod || !pMDescInCanonMT->IsInterface() || !pMDescInCanonMT->GetMethodTable()->IsSharedByGenericInstantiations())
&& (pMDescInCanonMT->MatchesAsyncVariantLookup(asyncVariantLookup)))
{
RETURN(pMDescInCanonMT);
}
Expand Down Expand Up @@ -992,7 +994,10 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
pExactMT,
FALSE /* not Unboxing */,
methodInst,
FALSE, FALSE, TRUE, asyncVariantLookup);
FALSE,
asyncVariantLookup,
FALSE,
TRUE);

_ASSERTE(pNonUnboxingStub->GetClassification() == mcInstantiated);
_ASSERTE(!pNonUnboxingStub->RequiresInstArg());
Expand Down Expand Up @@ -1200,9 +1205,9 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
FALSE,
Instantiation(repInst, methodInst.GetNumArgs()),
/* allowInstParam */ TRUE,
asyncVariantLookup,
/* forceRemotableMethod */ FALSE,
/* allowCreate */ TRUE,
asyncVariantLookup,
/* level */ level);

_ASSERTE(pWrappedMD->IsSharedByGenericInstantiations());
Expand Down
Loading
Loading