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

Code before:
// Returns:
//   True if so.
//
bool Lowering::IsBlockReachableWithoutGCSafePoint(BasicBlock* block)
{
    ArrayStack<BasicBlock*> stack(comp->getAllocator(CMK_Lower));
    BitVecTraits            traits(comp->fgBBNumMax + 1, comp);
    BitVec                  visited(BitVecOps::MakeEmpty(&traits));

    BitVecOps::AddElemD(&traits, visited, comp->fgFirstBB->bbNum);
    stack.Push(comp->fgFirstBB);

    while (!stack.Empty())
    {
        BasicBlock* visitBlock = stack.Pop();
        if (visitBlock == block)
        {
            return true;
        }

        if (visitBlock->HasFlag(BBF_GC_SAFE_POINT))
        {
            continue;
        }

        block->VisitAllSuccs(comp, [&](BasicBlock* succ) {
            if (BitVecOps::TryAddElemD(&traits, visited, succ->bbNum))
            {
                stack.Push(succ);
            }
            return BasicBlockVisit::Continue;
        });
    }

    return false;
}
#endif

//------------------------------------------------------------------------
// LowerCFGCall: Potentially lower a call to use control-flow guard. This
// expands indirect calls into either a validate+call sequence or to a dispatch
// helper taking the original target in a special register.
//
// Arguments:
//    call         -  The call node
//
void Lowering::LowerCFGCall(GenTreeCall* call)
{
    assert(!call->IsHelperCall(comp, CORINFO_HELP_DISPATCH_INDIRECT_CALL));
    if (call->IsHelperCall(comp, CORINFO_HELP_VALIDATE_INDIRECT_CALL))
    {

Review comment (suggestion):
```suggestion
        visitBlock->VisitAllSuccs(comp, [&](BasicBlock* succ) {
```

Code after:
        if (canClone)
        {
            return comp->gtCloneExpr(use.Def());
        }
        else
        {
            unsigned newLcl = use.ReplaceWithLclVar(comp);
            return comp->gtNewLclvNode(newLcl, TYP_I_IMPL);
        }
    };

    GenTree* callTarget = call->gtCallType == CT_INDIRECT ? call->gtCallAddr : call->gtControlExpr;

    if (call->IsVirtualStub())
    {
        // VSDs go through a resolver instead which skips double validation and
        // indirection.
        CallArg* vsdCellArg = call->gtArgs.FindWellKnownArg(WellKnownArg::VirtualStubCell);
        CallArg* thisArg    = call->gtArgs.GetThisArg();

        assert((vsdCellArg != nullptr) && (thisArg != nullptr));
        assert(thisArg->GetNode()->OperIs(GT_PUTARG_REG));
        LIR::Use thisArgUse(BlockRange(), &thisArg->GetNode()->AsOp()->gtOp1, thisArg->GetNode());
        GenTree* thisArgClone = cloneUse(thisArgUse, true);

        // The VSD cell is not needed for the original call when going through the resolver.
        // It can be removed without further fixups because it has fixed ABI assignment.
        call->gtArgs.RemoveUnsafe(vsdCellArg);
        assert(vsdCellArg->GetNode()->OperIs(GT_PUTARG_REG));
        // Also PUTARG_REG can be removed.
        BlockRange().Remove(vsdCellArg->GetNode());
        // The actual cell we need for the resolver.
        GenTree* vsdCellArgNode = vsdCellArg->GetNode()->gtGetOp1();

        GenTreeCall* resolve = comp->gtNewHelperCallNode(CORINFO_HELP_INTERFACELOOKUP_FOR_SLOT, TYP_I_IMPL);

        // Use a placeholder for the cell since the cell is already inserted in
        // LIR.
        GenTree* vsdCellPlaceholder = comp->gtNewZeroConNode(TYP_I_IMPL);
        resolve->gtArgs.PushFront(comp,
                                  NewCallArg::Primitive(vsdCellPlaceholder).WellKnown(WellKnownArg::VirtualStubCell));

        // 'this' arg clone is not inserted, so no need to use a placeholder for that.
        resolve->gtArgs.PushFront(comp, NewCallArg::Primitive(thisArgClone));

        comp->fgMorphTree(resolve);

        LIR::Range resolveRange = LIR::SeqTree(comp, resolve);
        GenTree*   resolveFirst = resolveRange.FirstNode();
        GenTree*   resolveLast  = resolveRange.LastNode();
        // Resolution comes with a null check, so it must happen after all