// 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"); } }
// Merge assertions on the edge flowing into the block about a variable. void RangeCheck::MergeEdgeAssertions(GenTreePtr tree, const ASSERT_VALARG_TP assertions, Range* pRange) { if (BitVecOps::IsEmpty(m_pCompiler->apTraits, assertions)) { return; } GenTreeLclVarCommon* lcl = (GenTreeLclVarCommon*)tree; 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(m_pCompiler->apTraits, &index)) { index++; Compiler::AssertionDsc* curAssertion = m_pCompiler->optGetAssertion((Compiler::AssertionIndex)index); // Current assertion is about array length. if (!curAssertion->IsArrLenArithBound() && !curAssertion->IsArrLenBound() && !curAssertion->IsConstantBound()) { continue; } #ifdef DEBUG if (m_pCompiler->verbose) { m_pCompiler->optPrintAssertion(curAssertion, (Compiler::AssertionIndex)index); } #endif assert(m_pCompiler->vnStore->IsVNArrLenArithBound(curAssertion->op1.vn) || m_pCompiler->vnStore->IsVNArrLenBound(curAssertion->op1.vn) || m_pCompiler->vnStore->IsVNConstantBound(curAssertion->op1.vn)); Limit limit(Limit::keUndef); genTreeOps cmpOper = GT_NONE; // Current assertion is of the form (i < a.len - cns) != 0 if (curAssertion->IsArrLenArithBound()) { ValueNumStore::ArrLenArithBoundInfo info; // 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); } } cmpOper = (genTreeOps)info.cmpOper; } // Current assertion is of the form (i < a.len) != 0 else if (curAssertion->IsArrLenBound()) { ValueNumStore::ArrLenArithBoundInfo info; // 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; 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); 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.cmpOpVN) { continue; } limit = Limit(Limit::keConstant, ValueNumStore::NoVN, info.constVal); cmpOper = (genTreeOps)info.cmpOper; } 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, (Compiler::AssertionIndex)index); } #endif noway_assert(limit.IsBinOpArray() || limit.IsArray() || limit.IsConstant()); ValueNum arrLenVN = m_pCurBndsChk->gtArrLen->gtVNPair.GetConservative(); ValueNum arrRefVN = ValueNumStore::NoVN; if (m_pCompiler->vnStore->IsVNArrLen(arrLenVN)) { // Get the array reference from the length. arrRefVN = m_pCompiler->vnStore->GetArrForLenVn(arrLenVN); } // During assertion prop we add assertions of the form: // // (i < a.Length) == 0 // (i < a.Length) != 0 // (i < 100) == 0 // (i < 100) != 0 // // At this point, we have detected that op1.vn is (i < a.Length) or (i < a.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 < a.Length) is false which is the same // as (i >= a.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 != 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; default: // All other 'cmpOper' kinds leave lLimit/uLimit unchanged break; } JITDUMP("The range after edge merging:"); JITDUMP(pRange->ToString(m_pCompiler->getAllocatorDebugOnly())); JITDUMP("\n"); } }