Repository: dotnet/runtime
File: src/coreclr/jit/lower.cpp (C++)

Code before:
#else
    GenTree* thisExpr = thisArgNode;
#endif

    // We're going to use the 'this' expression multiple times, so make a local to copy it.

    GenTree* base;
    if (thisExpr->OperIs(GT_LCL_VAR))
    {
        base = m_compiler->gtNewLclvNode(thisExpr->AsLclVar()->GetLclNum(), thisExpr->TypeGet());
    }
    else if (thisExpr->OperIs(GT_LCL_FLD))
    {
        base = m_compiler->gtNewLclFldNode(thisExpr->AsLclFld()->GetLclNum(), thisExpr->TypeGet(),
                                           thisExpr->AsLclFld()->GetLclOffs());
    }
    else
    {
        unsigned delegateInvokeTmp = m_compiler->lvaGrabTemp(true DEBUGARG("delegate invoke call"));
        base                       = m_compiler->gtNewLclvNode(delegateInvokeTmp, thisExpr->TypeGet());

#if HAS_FIXED_REGISTER_SET
        LIR::Use thisExprUse(BlockRange(), &thisArgNode->AsOp()->gtOp1, thisArgNode);
        ReplaceWithLclVar(thisExprUse, delegateInvokeTmp);
#else
        LIR::Use thisExprUse(BlockRange(), &thisArgNode, call);
        ReplaceWithLclVar(thisExprUse, delegateInvokeTmp);
#endif
        thisExpr = thisExprUse.Def(); // it's changed; reload it.
    }

    // replace original expression feeding into thisPtr with
    // [originalThis + offsetOfDelegateInstance]

    GenTree* newThisAddr = new (m_compiler, GT_LEA)
        GenTreeAddrMode(TYP_BYREF, thisExpr, nullptr, 0, m_compiler->eeGetEEInfo()->offsetOfDelegateInstance);

    GenTree* newThis = m_compiler->gtNewIndir(TYP_REF, newThisAddr);

    // Insert the new 'this' arg right before the call to get the correct null
    // behavior (the NRE that would logically happen inside Delegate.Invoke
    // should happen after all args are evaluated). We must also move the
    // PUTARG_REG node ahead.
#if HAS_FIXED_REGISTER_SET
    thisArgNode->AsOp()->gtOp1 = newThis;
#endif

    BlockRange().Remove(thisArgNode);
    BlockRange().InsertBefore(call, newThisAddr, newThis, thisArgNode);

    ContainCheckIndir(newThis->AsIndir());

Review comment (suggestion):
I don't think this reconnects the new local to the call. The place below where we skip `thisArgNode->AsOp()->gtOp1 = newThis` seems to have same problem. I think both can be solved if alongside `thisArgNode` we also initialize `thisArgNodeUse`, where it'd be along the lines of `GetThisArg().NodeRef()` for `!HAS_FIXED_REGISTER_SET`.

Code after:
    else
    {
        thisArg = call->gtArgs.GetThisArg();
    }

    assert(thisArg != nullptr);
    GenTree* thisArgNode = thisArg->GetNode();
    assert(thisArgNode != nullptr);

#if HAS_FIXED_REGISTER_SET
    assert(thisArgNode->OperIs(GT_PUTARG_REG));
    GenTree* thisExpr = thisArgNode->AsOp()->gtOp1;
    LIR::Use thisExprUse(BlockRange(), &thisArgNode->AsOp()->gtOp1, thisArgNode);
#else
    GenTree* thisExpr = thisArgNode;
    LIR::Use thisExprUse(BlockRange(), &thisArg->NodeRef(), call);
#endif

    // We're going to use the 'this' expression multiple times, so make a local to copy it.

    GenTree* base;
    if (thisExpr->OperIs(GT_LCL_VAR))
    {
        base = m_compiler->gtNewLclvNode(thisExpr->AsLclVar()->GetLclNum(), thisExpr->TypeGet());
    }
    else if (thisExpr->OperIs(GT_LCL_FLD))
    {
        base = m_compiler->gtNewLclFldNode(thisExpr->AsLclFld()->GetLclNum(), thisExpr->TypeGet(),
                                           thisExpr->AsLclFld()->GetLclOffs());
    }
    else
    {
        unsigned delegateInvokeTmp = m_compiler->lvaGrabTemp(true DEBUGARG("delegate invoke call"));
        base                       = m_compiler->gtNewLclvNode(delegateInvokeTmp, thisExpr->TypeGet());

        ReplaceWithLclVar(thisExprUse, delegateInvokeTmp);
        thisExpr = thisExprUse.Def(); // it's changed; reload it.
    }

    // replace original expression feeding into thisPtr with
    // [originalThis + offsetOfDelegateInstance]

    GenTree* newThisAddr = new (m_compiler, GT_LEA)
        GenTreeAddrMode(TYP_BYREF, thisExpr, nullptr, 0, m_compiler->eeGetEEInfo()->offsetOfDelegateInstance);

    GenTree* newThis = m_compiler->gtNewIndir(TYP_REF, newThisAddr);

    // Insert the new 'this' arg right before the call to get the correct null
    // behavior (the NRE that would logically happen inside Delegate.Invoke
    // should happen after all args are evaluated). We must also move the
    // PUTARG_REG node ahead.