Repository: dotnet/runtime
File: src/coreclr/vm/jitinterface.cpp (C++)

Code before:
        case CORINFO_TOKENKIND_Await:
        case CORINFO_TOKENKIND_AwaitVirtual:
            // in rare cases a method that returns Task is not actually TaskReturning (i.e. returns T).
            // we cannot resolve to an Async variant in such case.
            // return NULL, so that caller would re-resolve as a regular method call
            if (pMD->ReturnsTaskOrValueTask())
            {
                bool isDirect = tokenType == CORINFO_TOKENKIND_Await || pMD->IsStatic();
                if (!isDirect)
                {
                    DWORD attrs = pMD->GetAttrs();
                    if (pMD->GetMethodTable()->IsInterface())
                    {
                        isDirect = !IsMdVirtual(attrs);
                    }
                    else
                    {
                        isDirect = !IsMdVirtual(attrs) || IsMdFinal(attrs) || pMD->GetMethodTable()->IsSealed();
                    }
                }

                if (isDirect && !pMD->IsAsyncThunkMethod())
                {
                    // Async variant would be a thunk. Do not resolve direct calls
                    // to async thunks. That just creates and JITs unnecessary
                    // thunks, and the thunks are harder for the JIT to optimize.
                    pMD = NULL;
                }
                else
                {
                    pMD = pMD->GetAsyncVariant(/*allowInstParam*/FALSE);
                }
            }

            break;

        default:
            // Disallow ELEMENT_TYPE_BYREF and ELEMENT_TYPE_VOID
            if (et == ELEMENT_TYPE_BYREF || et == ELEMENT_TYPE_VOID)
                COMPlusThrow(kInvalidProgramException);
            break;
    }

    // The JIT interface should always return fully loaded types
    _ASSERTE(th.IsFullyLoaded());

    pResolvedToken->hClass = CORINFO_CLASS_HANDLE(th.AsPtr());
    pResolvedToken->hMethod = CORINFO_METHOD_HANDLE(pMD);
    pResolvedToken->hField = CORINFO_FIELD_HANDLE(pFD);

    EE_TO_JIT_TRANSITION();

Review comment (performance):
I think those thunks contain almost identical code to the `Await` helpers, so maybe not _harder_ to optimize, but it is certainly redundant to produce and JIT the thunks, where the `Await` helper does roughly the same.

Code after:
        case CORINFO_TOKENKIND_Await:
        case CORINFO_TOKENKIND_AwaitVirtual:
            {
                // in rare cases a method that returns Task is not actually TaskReturning (i.e. returns T).
                // we cannot resolve to an Async variant in such case.
                // return NULL, so that caller would re-resolve as a regular method call
                bool allowAsyncVariant = pMD->ReturnsTaskOrValueTask();
                if (allowAsyncVariant)
                {
                    bool isDirect = tokenType == CORINFO_TOKENKIND_Await || pMD->IsStatic();
                    if (!isDirect)
                    {
                        DWORD attrs = pMD->GetAttrs();
                        if (pMD->GetMethodTable()->IsInterface())
                        {
                            isDirect = !IsMdVirtual(attrs);
                        }
                        else
                        {
                            isDirect = !IsMdVirtual(attrs) || IsMdFinal(attrs) || pMD->GetMethodTable()->IsSealed();
                        }
                    }

                    if (isDirect && !pMD->IsAsyncThunkMethod())
                    {
                        // Async variant would be a thunk. Do not resolve direct calls
                        // to async thunks. That just creates and JITs unnecessary
                        // thunks, and the thunks are harder for the JIT to optimize.
                        allowAsyncVariant = false;
                    }
                }

                pMD = allowAsyncVariant ? pMD->GetAsyncVariant(/*allowInstParam*/FALSE) : NULL;
            }
            break;

        default:
            // Disallow ELEMENT_TYPE_BYREF and ELEMENT_TYPE_VOID
            if (et == ELEMENT_TYPE_BYREF || et == ELEMENT_TYPE_VOID)
                COMPlusThrow(kInvalidProgramException);
            break;
    }

    // The JIT interface should always return fully loaded types
    _ASSERTE(th.IsFullyLoaded());

    pResolvedToken->hClass = CORINFO_CLASS_HANDLE(th.AsPtr());
    pResolvedToken->hMethod = CORINFO_METHOD_HANDLE(pMD);
    pResolvedToken->hField = CORINFO_FIELD_HANDLE(pFD);

    EE_TO_JIT_TRANSITION();