Example #1
0
// Merge assertions on the edge flowing into the block about a variable.
void RangeCheck::MergeEdgeAssertions(GenTreePtr tree, EXPSET_TP assertions, Range* pRange)
{
    if (assertions == 0)
    {
        return;
    }

    GenTreeLclVarCommon* lcl = (GenTreeLclVarCommon*) tree;
    if (lcl->gtSsaNum == SsaConfig::RESERVED_SSA_NUM)
    {
        return;
    }
    // Walk through the "assertions" to check if the apply.
    unsigned index = 1;
    for (EXPSET_TP mask = 1; index <= m_pCompiler->GetAssertionCount(); index++, mask <<= 1)
    {
        if ((assertions & mask) == 0)
        {
            continue;
        }

        Compiler::AssertionDsc* curAssertion = m_pCompiler->optGetAssertion(index);

        // Current assertion is about array length.
        if (!curAssertion->IsArrLenArithBound() &&
            !curAssertion->IsArrLenBound())
        {
            continue;
        }

#ifdef DEBUG
        if (m_pCompiler->verbose)
        {
            m_pCompiler->optPrintAssertion(curAssertion, index);
        }
#endif

        assert(m_pCompiler->vnStore->IsVNArrLenArithBound(curAssertion->op1.vn) ||
               m_pCompiler->vnStore->IsVNArrLenBound(curAssertion->op1.vn));
        
        ValueNumStore::ArrLenArithBoundInfo info;
        Limit limit(Limit::keUndef);
        // Current assertion is of the form (i < a.len - cns) != 0
        if (curAssertion->IsArrLenArithBound())
        {
            // Get i, a.len, cns and < as "info."
            m_pCompiler->vnStore->GetArrLenArithBoundInfo(curAssertion->op1.vn, &info);
                       
            if (m_pCompiler->lvaTable[lcl->gtLclNum].GetPerSsaData(lcl->gtSsaNum)->m_vnPair.GetConservative()
                    != info.cmpOp)
            {
                continue;
            }

            switch (info.arrOper)
            {
            case GT_SUB:
            case GT_ADD:
                {
                    // If the operand that operates on the array is not constant, then done.
                    if (!m_pCompiler->vnStore->IsVNConstant(info.arrOp) || m_pCompiler->vnStore->TypeOfVN(info.arrOp) != TYP_INT)
                    {
                        break;
                    }
                    int cons = m_pCompiler->vnStore->ConstantValue<int>(info.arrOp);
                    limit = Limit(Limit::keBinOpArray, info.vnArray, info.arrOper == GT_SUB ? -cons : cons);
                }
            }
        }
        // Current assertion is of the form (i < a.len) != 0
        else if (curAssertion->IsArrLenBound())
        {
            // Get the info as "i", "<" and "a.len"
            m_pCompiler->vnStore->GetArrLenBoundInfo(curAssertion->op1.vn, &info);

            ValueNum lclVn = m_pCompiler->lvaTable[lcl->gtLclNum].GetPerSsaData(lcl->gtSsaNum)->m_vnPair.GetConservative();
            // If we don't have the same variable we are comparing against, bail.
            if (lclVn != info.cmpOp)
            {
                continue;
            }
            limit.type = Limit::keArray;
            limit.vn = info.vnArray;
        }
        else
        {
            noway_assert(false);
        }

        if (limit.IsUndef())
        {
            continue;
        }

        // Make sure the assertion is of the form != 0 or == 0.
        if (curAssertion->op2.vn != m_pCompiler->vnStore->VNZeroForType(TYP_INT))
        {
            continue;
        }
#ifdef DEBUG
        if (m_pCompiler->verbose) m_pCompiler->optPrintAssertion(curAssertion, index);
#endif

        noway_assert(limit.IsBinOpArray() || limit.IsArray());

        ValueNum arrLenVN = m_pCurBndsChk->gtArrLen->gtVNPair.GetConservative();
        ValueNum arrRefVN = m_pCompiler->vnStore->GetArrForLenVn(arrLenVN);

        // During assertion prop we add assertions of the form:
        // 
        //      (i < a.Length) == 0
        //      (i < a.Length) != 0
        //
        // At this point, we have detected that op1.vn is (i < a.Length) or (i < a.Length + cns),
        // and the op2.vn is 0.
        //
        // Now, let us check if we are == 0 (i.e., op1 assertion is false) or != 0 (op1 assertion
        // is true.),
        //
        // If we have an assertion of the form == 0 (i.e., equals false), then reverse relop.
        // The relop has to be reversed because we have: (i < a.Length) is false which is the same
        // as (i >= a.Length).
        genTreeOps cmpOper = (genTreeOps) info.cmpOper;
        if (curAssertion->assertionKind == Compiler::OAK_EQUAL)
        {
            cmpOper = GenTree::ReverseRelop(cmpOper);
        }

        // Bounds are inclusive, so add -1 for upper bound when "<". But make sure we won't overflow.
        if (cmpOper == GT_LT && !limit.AddConstant(-1))
        {
            continue;
        }
        // Bounds are inclusive, so add +1 for lower bound when ">". But make sure we won't overflow.
        if (cmpOper == GT_GT && !limit.AddConstant(1))
        {
            continue;
        }

        // Doesn't tighten the current bound. So skip.
        if (pRange->uLimit.IsConstant() && limit.vn != arrRefVN)
        {
            continue;
        }

        // Check if the incoming limit from assertions tightens the existing upper limit.
        if ((pRange->uLimit.IsArray() || pRange->uLimit.IsBinOpArray()) && pRange->uLimit.vn == arrRefVN)
        {
            // We have checked the current range's (pRange's) upper limit is either of the form:
            //      a.Length
            //      a.Length + cns
            //      and a == the bndsChkCandidate's arrRef
            //
            // We want to check if the incoming limit tightens the bound, and for that the
            // we need to make sure that incoming limit is also on a.Length or a.Length + cns
            // and not b.Length or some c.Length.

            if (limit.vn != arrRefVN)
            {
                JITDUMP("Array ref did not match cur=$%x, assert=$%x\n", arrRefVN, limit.vn);
                continue;
            }

            int curCns = (pRange->uLimit.IsBinOpArray()) ? pRange->uLimit.cns : 0;
            int limCns = (limit.IsBinOpArray()) ? limit.cns : 0;
            
            // Incoming limit doesn't tighten the existing upper limit.
            if (limCns >= curCns)
            {
                JITDUMP("Bound limit %d doesn't tighten current bound %d\n", limCns, curCns);
                continue;
            }
        }
        else
        {
            // Current range's upper bound is not "a.Length or a.Length + cns" and the
            // incoming limit is not on the same arrRef as the bounds check candidate.
            // So we could skip this assertion. But in cases, of Dependent or Unknown
            // type of upper limit, the incoming assertion still tightens the upper
            // bound to a saner value. So do not skip the assertion.
        }

        // cmpOp (loop index i) cmpOper a.len +/- cns
        switch (cmpOper)
        {
        case GT_LT:
            pRange->uLimit = limit;
            break;

        case GT_GT:
            pRange->lLimit = limit;
            break;

        case GT_GE:
            pRange->lLimit = limit;
            break;

        case GT_LE:
            pRange->uLimit = limit;
            break;
        }
        JITDUMP("The range after edge merging:");
        JITDUMP(pRange->ToString(m_pCompiler->getAllocatorDebugOnly()));
        JITDUMP("\n");
    }
}
Example #2
0
// Merge assertions on the edge flowing into the block about a variable.
void RangeCheck::MergeEdgeAssertions(GenTreeLclVarCommon* lcl, ASSERT_VALARG_TP assertions, Range* pRange)
{
    if (BitVecOps::IsEmpty(m_pCompiler->apTraits, assertions))
    {
        return;
    }

    if (lcl->gtSsaNum == SsaConfig::RESERVED_SSA_NUM)
    {
        return;
    }
    // Walk through the "assertions" to check if the apply.
    BitVecOps::Iter iter(m_pCompiler->apTraits, assertions);
    unsigned        index = 0;
    while (iter.NextElem(&index))
    {
        AssertionIndex assertionIndex = GetAssertionIndex(index);

        Compiler::AssertionDsc* curAssertion = m_pCompiler->optGetAssertion(assertionIndex);

        Limit      limit(Limit::keUndef);
        genTreeOps cmpOper = GT_NONE;

        LclSsaVarDsc* ssaData     = m_pCompiler->lvaTable[lcl->gtLclNum].GetPerSsaData(lcl->gtSsaNum);
        ValueNum      normalLclVN = m_pCompiler->vnStore->VNConservativeNormalValue(ssaData->m_vnPair);

        // Current assertion is of the form (i < len - cns) != 0
        if (curAssertion->IsCheckedBoundArithBound())
        {
            ValueNumStore::CompareCheckedBoundArithInfo info;

            // Get i, len, cns and < as "info."
            m_pCompiler->vnStore->GetCompareCheckedBoundArithInfo(curAssertion->op1.vn, &info);

            // If we don't have the same variable we are comparing against, bail.
            if (normalLclVN != info.cmpOp)
            {
                continue;
            }

            if ((info.arrOper != GT_ADD) && (info.arrOper != GT_SUB))
            {
                continue;
            }

            // If the operand that operates on the bound is not constant, then done.
            if (!m_pCompiler->vnStore->IsVNInt32Constant(info.arrOp))
            {
                continue;
            }

            int cons = m_pCompiler->vnStore->ConstantValue<int>(info.arrOp);
            limit    = Limit(Limit::keBinOpArray, info.vnBound, info.arrOper == GT_SUB ? -cons : cons);
            cmpOper  = (genTreeOps)info.cmpOper;
        }
        // Current assertion is of the form (i < len) != 0
        else if (curAssertion->IsCheckedBoundBound())
        {
            ValueNumStore::CompareCheckedBoundArithInfo info;

            // Get the info as "i", "<" and "len"
            m_pCompiler->vnStore->GetCompareCheckedBound(curAssertion->op1.vn, &info);

            // If we don't have the same variable we are comparing against, bail.
            if (normalLclVN != info.cmpOp)
            {
                continue;
            }

            limit   = Limit(Limit::keBinOpArray, info.vnBound, 0);
            cmpOper = (genTreeOps)info.cmpOper;
        }
        // Current assertion is of the form (i < 100) != 0
        else if (curAssertion->IsConstantBound())
        {
            ValueNumStore::ConstantBoundInfo info;

            // Get the info as "i", "<" and "100"
            m_pCompiler->vnStore->GetConstantBoundInfo(curAssertion->op1.vn, &info);

            // If we don't have the same variable we are comparing against, bail.
            if (normalLclVN != info.cmpOpVN)
            {
                continue;
            }

            limit   = Limit(Limit::keConstant, info.constVal);
            cmpOper = (genTreeOps)info.cmpOper;
        }
        // Current assertion is not supported, ignore it
        else
        {
            continue;
        }

        assert(limit.IsBinOpArray() || limit.IsConstant());

        // Make sure the assertion is of the form != 0 or == 0.
        if (curAssertion->op2.vn != m_pCompiler->vnStore->VNZeroForType(TYP_INT))
        {
            continue;
        }
#ifdef DEBUG
        if (m_pCompiler->verbose)
        {
            m_pCompiler->optPrintAssertion(curAssertion, assertionIndex);
        }
#endif

        ValueNum arrLenVN = m_pCompiler->vnStore->VNConservativeNormalValue(m_pCurBndsChk->gtArrLen->gtVNPair);

        if (m_pCompiler->vnStore->IsVNConstant(arrLenVN))
        {
            // Set arrLenVN to NoVN; this will make it match the "vn" recorded on
            // constant limits (where we explicitly track the constant and don't
            // redundantly store its VN in the "vn" field).
            arrLenVN = ValueNumStore::NoVN;
        }

        // During assertion prop we add assertions of the form:
        //
        //      (i < length) == 0
        //      (i < length) != 0
        //      (i < 100) == 0
        //      (i < 100) != 0
        //
        // At this point, we have detected that op1.vn is (i < length) or (i < length + cns) or
        // (i < 100) and the op2.vn is 0.
        //
        // Now, let us check if we are == 0 (i.e., op1 assertion is false) or != 0 (op1 assertion
        // is true.),
        //
        // If we have an assertion of the form == 0 (i.e., equals false), then reverse relop.
        // The relop has to be reversed because we have: (i < length) is false which is the same
        // as (i >= length).
        if (curAssertion->assertionKind == Compiler::OAK_EQUAL)
        {
            cmpOper = GenTree::ReverseRelop(cmpOper);
        }

        // Bounds are inclusive, so add -1 for upper bound when "<". But make sure we won't overflow.
        if (cmpOper == GT_LT && !limit.AddConstant(-1))
        {
            continue;
        }
        // Bounds are inclusive, so add +1 for lower bound when ">". But make sure we won't overflow.
        if (cmpOper == GT_GT && !limit.AddConstant(1))
        {
            continue;
        }

        // Doesn't tighten the current bound. So skip.
        if (pRange->uLimit.IsConstant() && limit.vn != arrLenVN)
        {
            continue;
        }

        // Check if the incoming limit from assertions tightens the existing upper limit.
        if (pRange->uLimit.IsBinOpArray() && (pRange->uLimit.vn == arrLenVN))
        {
            // We have checked the current range's (pRange's) upper limit is either of the form:
            //      length + cns
            //      and length == the bndsChkCandidate's arrLen
            //
            // We want to check if the incoming limit tightens the bound, and for that
            // we need to make sure that incoming limit is also on the same length (or
            // length + cns) and not some other length.

            if (limit.vn != arrLenVN)
            {
                JITDUMP("Array length VN did not match arrLen=" FMT_VN ", limit=" FMT_VN "\n", arrLenVN, limit.vn);
                continue;
            }

            int curCns = pRange->uLimit.cns;
            int limCns = (limit.IsBinOpArray()) ? limit.cns : 0;

            // Incoming limit doesn't tighten the existing upper limit.
            if (limCns >= curCns)
            {
                JITDUMP("Bound limit %d doesn't tighten current bound %d\n", limCns, curCns);
                continue;
            }
        }
        else
        {
            // Current range's upper bound is not "length + cns" and the
            // incoming limit is not on the same length as the bounds check candidate.
            // So we could skip this assertion. But in cases, of Dependent or Unknown
            // type of upper limit, the incoming assertion still tightens the upper
            // bound to a saner value. So do not skip the assertion.
        }

        // cmpOp (loop index i) cmpOper len +/- cns
        switch (cmpOper)
        {
            case GT_LT:
                pRange->uLimit = limit;
                break;

            case GT_GT:
                pRange->lLimit = limit;
                break;

            case GT_GE:
                pRange->lLimit = limit;
                break;

            case GT_LE:
                pRange->uLimit = limit;
                break;

            default:
                // All other 'cmpOper' kinds leave lLimit/uLimit unchanged
                break;
        }
        JITDUMP("The range after edge merging:");
        JITDUMP(pRange->ToString(m_pCompiler->getAllocatorDebugOnly()));
        JITDUMP("\n");
    }
}