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

Code before:
            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 = BitVecOps::MakeCopy(m_pCompiler->apTraits, block->bbAssertionIn);

        // bbAssertionIn is a bit conservative and will not include inter-block assertions.
        // e.g. created by GT_BOUNDS_CHECK nodes prior 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.
        //
        // TODO-Review: EH successor/predecessor iteration seems broken.
        if ((block->bbCatchTyp != BBCT_FAULT) && block->HasFlag(BBF_MAY_HAVE_BOUNDS_CHECKS))
        {
            bool treeFound = false;
            for (Statement* stmt : block->Statements())
            {
                if (stmt->GetRootNode() == nullptr)
                {
                    continue;
                }

                class TreeAssertionVisitor final : public GenTreeVisitor<TreeAssertionVisitor>
                {
                    GenTree*   m_op;
                    ASSERT_TP* m_assertions;

                public:
                    enum
                    {
                        DoPostOrder       = true,

Review comment (suggestion):
The comment says "prior the 'op'" but should be "prior to the 'op'" for correct grammar.
```suggestion
        // e.g. created by GT_BOUNDS_CHECK nodes prior to the 'op' in the current block.
```

Code after:
        }
    }
}

class AssertionsAccumulator final : public GenTreeVisitor<AssertionsAccumulator>
{
    int        m_Budget;
    ASSERT_TP* m_pAssertions;
    GenTree*   m_op;

public:
    enum
    {
        DoPostOrder       = true,
        UseExecutionOrder = true
    };

    int GetBudgetLeft() const
    {
        return m_Budget;
    }

    AssertionsAccumulator(Compiler* compiler, int budget, ASSERT_TP* pAssertions, GenTree* op)
        : GenTreeVisitor(compiler)
        , m_Budget(budget)
        , m_pAssertions(pAssertions)
        , m_op(op)
    {
    }

    fgWalkResult PostOrderVisit(GenTree** use, GenTree* user)
    {
        if ((*use == m_op) || (--m_Budget <= 0))
        {
            // We've found the op or run out of budget -> abort.
            return fgWalkResult::WALK_ABORT;
        }

        if ((*use)->GeneratesAssertion())
        {
            AssertionInfo info = (*use)->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(m_compiler->apTraits, *m_pAssertions, info.GetAssertionIndex() - 1);
        }
        return fgWalkResult::WALK_CONTINUE;
    }
};

// Merge assertions from the pred edges of the block, i.e., check for any assertions about "op's" value numbers for phi
// arguments. If not a phi argument, check if we have assertions about local variables.