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

Code before:
                    if ((*ppTree == data->op) || (--(*(data->budget)) <= 0))
                    {
                        // We've found the op or run out of budget - abort the walk.
                        return Compiler::fgWalkResult::WALK_ABORT;
                    }
                    if ((*ppTree)->GeneratesAssertion())
                    {
                        AssertionInfo info = (*ppTree)->GetAssertionInfo();
                        // Normally, we extend the assertions by calling optImpliedAssertions, but that
                        // doesn't seem to improve anything here, so we just add the assertion directly.
                        BitVecOps::AddElemD(walkData->compiler->apTraits, *data->assertions,
                                            info.GetAssertionIndex() - 1);
                    }
                    return Compiler::fgWalkResult::WALK_CONTINUE;
                };

                auto data = WalkData{&budget, op, &assertions};
                if (m_pCompiler->fgWalkTreePost(stmt->GetRootNodePointer(), visitor, &data) !=
                    Compiler::fgWalkResult::WALK_CONTINUE)
                {
                    opFound = true;
                    break;
                }
            }
            assert(opFound && "Failed to find op in block statements");
        }
    }

    if (!BitVecOps::MayBeUninit(assertions))
    {
        // Perform the merge step to fine tune the range value.
        MergeEdgeAssertions(op->AsLclVarCommon(), assertions, pRange);
    }
}

// Compute the range for a binary operation.
Range RangeCheck::ComputeRangeForBinOp(BasicBlock* block, GenTreeOp* binop, bool monIncreasing DEBUGARG(int indent))
{
    assert(binop->OperIs(GT_ADD, GT_XOR, GT_AND, GT_RSH, GT_RSZ, GT_LSH, GT_UMOD, GT_MUL));

    // For XOR we only care about Log2 pattern for now
    if (binop->OperIs(GT_XOR))
    {
        int upperBound;
        if (m_pCompiler->vnStore->IsVNLog2(m_pCompiler->vnStore->VNConservativeNormalValue(binop->gtVNPair),
                                           &upperBound))
        {
            assert(upperBound > 0);
            return Range(Limit(Limit::keConstant, 0), Limit(Limit::keConstant, upperBound));
        }
        return Range(Limit(Limit::keUnknown));

Review comment (suggestion):
You could also check here if the count of assertions in `assertions` matches `GetAssertionCount()`, if so, no new assertions will be found walking the block's IR.

It probably doesn't happen all that often but is also pretty cheap to check.

Code after:
{
    JITDUMP("Merging assertions from pred edges of " FMT_BB " for op [%06d] " FMT_VN "\n", block->bbNum,
            Compiler::dspTreeID(op), m_pCompiler->vnStore->VNConservativeNormalValue(op->gtVNPair));
    ASSERT_TP assertions = BitVecOps::UninitVal();

    if (m_pCompiler->GetAssertionCount() < 1)
    {
        return;
    }

    // If we have a phi arg, we can get to the block from it and use its assertion out.
    if (op->OperIs(GT_PHI_ARG))
    {
        const BasicBlock* pred = op->AsPhiArg()->gtPredBB;
        assertions             = m_pCompiler->optGetEdgeAssertions(block, pred);
        if (!BitVecOps::MayBeUninit(assertions))
        {
            JITDUMP("Merge assertions created by " FMT_BB " for " FMT_BB "\n", pred->bbNum, block->bbNum);
            Compiler::optDumpAssertionIndices(assertions, "\n");
        }
    }
    // Get assertions from bbAssertionIn.
    else if (op->IsLocal())
    {
        assertions = block->bbAssertionIn;

        // bbAssertionIn is a bit conservative and will not include intra-block assertions.
        // e.g. created by GT_BOUNDS_CHECK nodes prior to the 'op' in the current block.
        // For that, we walk the trees in the block until we find "op" and collect assertions
        // along the way. It doesn't seem to be profitable for anything other than bounds checks
        // (ArrBnds assertions), so we limit the search to only those blocks that may have bounds
        // checks. It also helps reduce the cost of this search as it's not cheap.
        //
        if (block->HasFlag(BBF_MAY_HAVE_BOUNDS_CHECKS))
        {
            // We're going to be adding to 'assertions', so make a copy first.
            assertions = BitVecOps::MakeCopy(m_pCompiler->apTraits, assertions);

            // JIT-TP: Limit the search budget to avoid spending too much time here.
            int budget = 50;
            for (Statement* stmt : block->Statements())
            {
                AssertionsAccumulator visitor(m_pCompiler, budget, &assertions, op);
                if (visitor.WalkTree(stmt->GetRootNodePointer(), nullptr) == Compiler::fgWalkResult::WALK_ABORT)
                {
                    break;
                }
                budget = visitor.GetBudgetLeft();
            }
        }
    }