Repository: dotnet/runtime
File: src/coreclr/interpreter/compiler.cpp (C++)

Code before:
                AddIns(INTOP_NULLCHECK);
                m_pLastNewIns->SetSVar(callArgs[0]);
            }

            EmitPushCORINFO_LOOKUP(callInfo.codePointerLookup);
            m_pStackPointer--;
            int codePointerLookupResult = m_pStackPointer[0].var;

            calliCookie = m_compHnd->GetCookieForInterpreterCalliSig(&callInfo.sig);

            EmitCalli(tailcall, calliCookie, codePointerLookupResult, &callInfo.sig);

            // These calli calls cannot be pinvoke calls. (If we want to add that, we need to set the noCallArgs flag)
            assert(!((m_pLastNewIns->data[1] & (int32_t)CalliFlags::PInvoke) != 0));
            break;
        }
        case CORINFO_VIRTUALCALL_VTABLE:
            // Traditional virtual call. In theory we could optimize this to using the vtable
            AddIns(tailcall ? INTOP_CALLVIRT_TAIL : INTOP_CALLVIRT);
            m_pLastNewIns->data[0] = GetDataItemIndex(callInfo.hMethod);
            // Reserved slot for caching DispatchToken
            m_pLastNewIns->data[1] = GetNewDataItemIndex(nullptr);
            break;

        case CORINFO_VIRTUALCALL_LDVIRTFTN:
            if ((callInfo.sig.sigInst.methInstCount != 0) || (m_compHnd->getClassAttribs(m_compHnd->getMethodClass(callInfo.hMethod)) & CORINFO_FLG_SHAREDINST))
            {
                assert(extraParamArgLocation == INT_MAX);
                // We should not have a type argument for the ldvirtftn path since we don't know
                // the exact method to call until the ldvirtftn is resolved, and that only
                // produces a function pointer.

                // We need to copy the this argument to the target function to another var,
                // since var's which are passed to call instructions are destroyed (unless
                // they are global, and there is no reason to promote the this arg to a global)
                AddIns(INTOP_MOV_P);
                m_pLastNewIns->SetSVar(callArgs[0]);
                PushInterpType(InterpTypeO, NULL);
                m_pLastNewIns->SetDVar(m_pStackPointer[-1].var);
                int thisVarForLdvirtftn = m_pStackPointer[-1].var;
                m_pStackPointer--;

                EmitPushLdvirtftn(thisVarForLdvirtftn, &resolvedCallToken, &callInfo);
                m_pStackPointer--;
                int synthesizedLdvirtftnPtrVar = m_pStackPointer[0].var;

                calliCookie = m_compHnd->GetCookieForInterpreterCalliSig(&callInfo.sig);

                EmitCalli(tailcall, calliCookie, synthesizedLdvirtftnPtrVar, &callInfo.sig);
            }
            else

Review comment (performance):
Computing the DispatchToken here didn't seem to provide much benefit and it would require adding a lot of code for the addition to the EE interface, only for it to be used in the interpreter. Unless there is some existing api that might make it easy to compute something useful to use in the dispatch cache.

Code after:
            {
                // If the call is a normal call, we need to check for null instance
                // before the call.
                AddIns(INTOP_NULLCHECK);
                m_pLastNewIns->SetSVar(callArgs[0]);
            }

            EmitPushCORINFO_LOOKUP(callInfo.codePointerLookup);
            m_pStackPointer--;
            int codePointerLookupResult = m_pStackPointer[0].var;

            calliCookie = m_compHnd->GetCookieForInterpreterCalliSig(&callInfo.sig);

            EmitCalli(tailcall, calliCookie, codePointerLookupResult, &callInfo.sig);

            // These calli calls cannot be pinvoke calls. (If we want to add that, we need to set the noCallArgs flag)
            assert(!((m_pLastNewIns->data[1] & (int32_t)CalliFlags::PInvoke) != 0));
            break;
        }
        case CORINFO_VIRTUALCALL_VTABLE:
        {
            // Traditional virtual call. In theory we could optimize this to using the vtable
            AddIns(tailcall ? INTOP_CALLVIRT_TAIL : INTOP_CALLVIRT);
            m_pLastNewIns->data[0] = GetDataItemIndex(callInfo.hMethod);
            // Reserved slots for caching DispatchToken and its hash
            m_pLastNewIns->data[1] = GetNewDataItemIndex(nullptr);
            int32_t secondCache = GetNewDataItemIndex(nullptr);
#ifdef DEBUG
            assert(secondCache == (m_pLastNewIns->data[1] + 1));
#endif
            break;
        }
        case CORINFO_VIRTUALCALL_LDVIRTFTN:
            if ((callInfo.sig.sigInst.methInstCount != 0) || (m_compHnd->getClassAttribs(m_compHnd->getMethodClass(callInfo.hMethod)) & CORINFO_FLG_SHAREDINST))
            {
                assert(extraParamArgLocation == INT_MAX);
                // We should not have a type argument for the ldvirtftn path since we don't know
                // the exact method to call until the ldvirtftn is resolved, and that only
                // produces a function pointer.

                // We need to copy the this argument to the target function to another var,
                // since var's which are passed to call instructions are destroyed (unless
                // they are global, and there is no reason to promote the this arg to a global)
                AddIns(INTOP_MOV_P);
                m_pLastNewIns->SetSVar(callArgs[0]);
                PushInterpType(InterpTypeO, NULL);
                m_pLastNewIns->SetDVar(m_pStackPointer[-1].var);
                int thisVarForLdvirtftn = m_pStackPointer[-1].var;
                m_pStackPointer--;

                EmitPushLdvirtftn(thisVarForLdvirtftn, &resolvedCallToken, &callInfo);