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

Code before:
                    helperFtn(helperArg1, helperArg2, helperArg3);
                    ip += 5;
                    break;
                }

                case INTOP_CALLVIRT_TAIL:
                case INTOP_CALLVIRT:
                {
                    isTailcall = (*ip == INTOP_CALLVIRT_TAIL);
                    returnOffset = ip[1];
                    callArgsOffset = ip[2];
                    methodSlot = ip[3];

                    MethodDesc *pMD = (MethodDesc*)pMethod->pDataItems[methodSlot];

                    OBJECTREF *pThisArg = LOCAL_VAR_ADDR(callArgsOffset, OBJECTREF);
                    NULL_CHECK(*pThisArg);

                    MethodTable *pObjMT = (*pThisArg)->GetMethodTable();

                    // Obtain the cached dispatch token or initialize it
                    size_t dispatchToken = (size_t)pMethod->pDataItems[ip[4]];
                    if (dispatchToken == 0)
                    {
                        dispatchToken = CreateDispatchTokenForMethod(pMD);
                        pMethod->pDataItems[ip[4]] = (void*)dispatchToken;
                    }

                    // Try cache lookup first
                    targetMethod = g_InterpDispatchCache.Lookup(dispatchToken, pObjMT);

                    if (targetMethod == NULL)
                    {
                        // miss, resolve the virtual method and cache it
                        targetMethod = CallWithSEHWrapper(
                            [&pMD, &pThisArg]() {
                                return pMD->GetMethodDescOfVirtualizedCode(pThisArg, pMD->GetMethodTable());
                            });
                        g_InterpDispatchCache.Insert(dispatchToken, pObjMT, targetMethod);
                    }

                    ip += 5;
                    goto CALL_INTERP_METHOD;
                }

                case INTOP_CALLI_TAIL:
                case INTOP_CALLI:
                {
                    isTailcall = (*ip == INTOP_CALLI_TAIL);
                    returnOffset = ip[1];
                    int8_t* returnValueAddress = LOCAL_VAR_ADDR(returnOffset, int8_t);

Review comment (bug):
The dispatch token is cached by writing to pMethod->pDataItems[ip[4]] without synchronization. If multiple threads execute the same callvirt instruction simultaneously, they might race to initialize the same dispatch token slot. While writing the same value should be benign, consider using InterlockedCompareExchangeT with nullptr as the expected value to ensure only one thread performs the initialization, similar to common double-checked locking patterns. This would make the intent clearer and avoid potential issues with torn writes on some architectures.

Code after:

                        targetMethod = pILTargetMethod;
                        ip += 4;
                        goto CALL_INTERP_METHOD;
                    }

                    _ASSERTE(helperFtn != NULL);
                    helperFtn(helperArg1, helperArg2);
                    ip += 4;
                    break;
                }
                case INTOP_CALL_HELPER_V_SS:
                {
                    void* helperArg1 = LOCAL_VAR(ip[1], void*);
                    void* helperArg2 = LOCAL_VAR(ip[2], void*);
                    MethodDesc *pILTargetMethod = NULL;
                    HELPER_FTN_V_PP helperFtn = GetPossiblyIndirectHelper<HELPER_FTN_V_PP>(pMethod, ip[3], &pILTargetMethod);
                    if (pILTargetMethod != NULL)
                    {
                        returnOffset = pMethod->allocaSize;
                        callArgsOffset = pMethod->allocaSize;

                        // Pass arguments to the target method
                        LOCAL_VAR(callArgsOffset, void*) = helperArg1;
                        LOCAL_VAR(callArgsOffset + INTERP_STACK_SLOT_SIZE, void*) = helperArg2;

                        targetMethod = pILTargetMethod;
                        ip += 4;
                        goto CALL_INTERP_METHOD;
                    }

                    _ASSERTE(helperFtn != NULL);
                    helperFtn(helperArg1, helperArg2);
                    ip += 4;
                    break;
                }

                case INTOP_CALL_HELPER_V_SSS:
                {
                    void* helperArg1 = LOCAL_VAR(ip[1], void*);
                    void* helperArg2 = LOCAL_VAR(ip[2], void*);
                    void* helperArg3 = LOCAL_VAR(ip[3], void*);
                    MethodDesc *pILTargetMethod = NULL;
                    HELPER_FTN_V_PPP helperFtn = GetPossiblyIndirectHelper<HELPER_FTN_V_PPP>(pMethod, ip[4], &pILTargetMethod);
                    if (pILTargetMethod != NULL)
                    {
                        returnOffset = pMethod->allocaSize;
                        callArgsOffset = pMethod->allocaSize;

                        // Pass arguments to the target method
                        LOCAL_VAR(callArgsOffset, void*) = helperArg1;