// Check if the computed range is within bounds. bool RangeCheck::BetweenBounds(Range& range, int lower, GenTreePtr upper) { #ifdef DEBUG if (m_pCompiler->verbose) { printf("%s BetweenBounds <%d, ", range.ToString(m_pCompiler->getAllocatorDebugOnly()), lower); Compiler::printTreeID(upper); printf(">\n"); } #endif // DEBUG // Get the VN for the upper limit. ValueNum uLimitVN = upper->gtVNPair.GetConservative(); #ifdef DEBUG JITDUMP("VN%04X upper bound is: ", uLimitVN); if (m_pCompiler->verbose) { m_pCompiler->vnStore->vnDump(m_pCompiler, uLimitVN); } JITDUMP("\n"); #endif // If the upper limit is not length, then bail. if (!m_pCompiler->vnStore->IsVNArrLen(uLimitVN)) { return false; } // Get the array reference from the length. ValueNum arrRefVN = m_pCompiler->vnStore->GetArrForLenVn(uLimitVN); #ifdef DEBUG JITDUMP("Array ref VN"); if (m_pCompiler->verbose) { m_pCompiler->vnStore->vnDump(m_pCompiler, arrRefVN); } JITDUMP("\n"); #endif // Check if array size can be obtained. int arrSize = m_pCompiler->vnStore->GetNewArrSize(arrRefVN); JITDUMP("Array size is: %d\n", arrSize); // Upper limit: a.len + ucns (upper limit constant). if (range.UpperLimit().IsBinOpArray()) { if (range.UpperLimit().vn != arrRefVN) { return false; } int ucns = range.UpperLimit().GetConstant(); // Upper limit: a.Len + [0..n] if (ucns >= 0) { return false; } // If lower limit is a.len return false. if (range.LowerLimit().IsArray()) { return false; } // Since upper limit is bounded by the array, return true if lower bound is good. // TODO-CQ: I am retaining previous behavior to minimize ASM diffs, but we could // return "true" here if GetConstant() >= 0 instead of "== 0". if (range.LowerLimit().IsConstant() && range.LowerLimit().GetConstant() == 0) { return true; } // Check if we have the array size allocated by new. if (arrSize <= 0) { return false; } // At this point, // upper limit = a.len + ucns. ucns < 0 // lower limit = a.len + lcns. if (range.LowerLimit().IsBinOpArray()) { int lcns = range.LowerLimit().GetConstant(); if (lcns >= 0 || -lcns > arrSize) { return false; } return (range.LowerLimit().vn == arrRefVN && lcns <= ucns); } } // If upper limit is constant else if (range.UpperLimit().IsConstant()) { if (arrSize <= 0) { return false; } int ucns = range.UpperLimit().GetConstant(); if (ucns >= arrSize) { return false; } if (range.LowerLimit().IsConstant()) { int lcns = range.LowerLimit().GetConstant(); // Make sure lcns < ucns which is already less than arrSize. return (lcns >= 0 && lcns <= ucns); } if (range.LowerLimit().IsBinOpArray()) { int lcns = range.LowerLimit().GetConstant(); // a.len + lcns, make sure we don't subtract too much from a.len. if (lcns >= 0 || -lcns > arrSize) { return false; } // Make sure a.len + lcns <= ucns. return (range.LowerLimit().vn == arrRefVN && (arrSize + lcns) <= ucns); } } return false; }
// Check if the computed range is within bounds. bool RangeCheck::BetweenBounds(Range& range, int lower, GenTree* upper) { #ifdef DEBUG if (m_pCompiler->verbose) { printf("%s BetweenBounds <%d, ", range.ToString(m_pCompiler->getAllocatorDebugOnly()), lower); Compiler::printTreeID(upper); printf(">\n"); } #endif // DEBUG ValueNumStore* vnStore = m_pCompiler->vnStore; // Get the VN for the upper limit. ValueNum uLimitVN = vnStore->VNConservativeNormalValue(upper->gtVNPair); #ifdef DEBUG JITDUMP(FMT_VN " upper bound is: ", uLimitVN); if (m_pCompiler->verbose) { vnStore->vnDump(m_pCompiler, uLimitVN); } JITDUMP("\n"); #endif int arrSize = 0; if (vnStore->IsVNConstant(uLimitVN)) { ssize_t constVal = -1; unsigned iconFlags = 0; if (m_pCompiler->optIsTreeKnownIntValue(true, upper, &constVal, &iconFlags)) { arrSize = (int)constVal; } } else if (vnStore->IsVNArrLen(uLimitVN)) { // Get the array reference from the length. ValueNum arrRefVN = vnStore->GetArrForLenVn(uLimitVN); // Check if array size can be obtained. arrSize = vnStore->GetNewArrSize(arrRefVN); } else if (!vnStore->IsVNCheckedBound(uLimitVN)) { // If the upper limit is not length, then bail. return false; } JITDUMP("Array size is: %d\n", arrSize); // Upper limit: len + ucns (upper limit constant). if (range.UpperLimit().IsBinOpArray()) { if (range.UpperLimit().vn != uLimitVN) { return false; } int ucns = range.UpperLimit().GetConstant(); // Upper limit: Len + [0..n] if (ucns >= 0) { return false; } // Since upper limit is bounded by the array, return true if lower bound is good. if (range.LowerLimit().IsConstant() && range.LowerLimit().GetConstant() >= 0) { return true; } // Check if we have the array size allocated by new. if (arrSize <= 0) { return false; } // At this point, // upper limit = len + ucns. ucns < 0 // lower limit = len + lcns. if (range.LowerLimit().IsBinOpArray()) { int lcns = range.LowerLimit().GetConstant(); if (lcns >= 0 || -lcns > arrSize) { return false; } return (range.LowerLimit().vn == uLimitVN && lcns <= ucns); } } // If upper limit is constant else if (range.UpperLimit().IsConstant()) { if (arrSize <= 0) { return false; } int ucns = range.UpperLimit().GetConstant(); if (ucns >= arrSize) { return false; } if (range.LowerLimit().IsConstant()) { int lcns = range.LowerLimit().GetConstant(); // Make sure lcns < ucns which is already less than arrSize. return (lcns >= 0 && lcns <= ucns); } if (range.LowerLimit().IsBinOpArray()) { int lcns = range.LowerLimit().GetConstant(); // len + lcns, make sure we don't subtract too much from len. if (lcns >= 0 || -lcns > arrSize) { return false; } // Make sure a.len + lcns <= ucns. return (range.LowerLimit().vn == uLimitVN && (arrSize + lcns) <= ucns); } } return false; }
void RangeCheck::OptimizeRangeCheck(BasicBlock* block, GenTreePtr stmt, GenTreePtr treeParent) { // Check if we are dealing with a bounds check node. if (treeParent->OperGet() != GT_COMMA) { return; } // If we are not looking at array bounds check, bail. GenTreePtr tree = treeParent->gtOp.gtOp1; if (tree->gtOper != GT_ARR_BOUNDS_CHECK) { return; } GenTreeBoundsChk* bndsChk = tree->AsBoundsChk(); m_pCurBndsChk = bndsChk; GenTreePtr treeIndex = bndsChk->gtIndex; // Take care of constant index first, like a[2], for example. ValueNum idxVn = treeIndex->gtVNPair.GetConservative(); ValueNum arrLenVn = bndsChk->gtArrLen->gtVNPair.GetConservative(); int arrSize = GetArrLength(arrLenVn); JITDUMP("ArrSize for lengthVN:%03X = %d\n", arrLenVn, arrSize); if (m_pCompiler->vnStore->IsVNConstant(idxVn) && arrSize > 0) { ssize_t idxVal = -1; unsigned iconFlags = 0; if (!m_pCompiler->optIsTreeKnownIntValue(true, treeIndex, &idxVal, &iconFlags)) { return; } JITDUMP("[RangeCheck::OptimizeRangeCheck] Is index %d in <0, arrLenVn VN%X sz:%d>.\n", idxVal, arrLenVn, arrSize); if (arrSize > 0 && idxVal < arrSize && idxVal >= 0) { JITDUMP("Removing range check\n"); m_pCompiler->optRemoveRangeCheck(treeParent, stmt, true, GTF_ASG, true /* force remove */); return; } } GetRangeMap()->RemoveAll(); GetOverflowMap()->RemoveAll(); // Get the range for this index. SearchPath* path = new (m_pCompiler->getAllocator()) SearchPath(m_pCompiler->getAllocator()); Range range = GetRange(block, stmt, treeIndex, path, false DEBUGARG(0)); // If upper or lower limit is found to be unknown (top), or it was found to // be unknown because of over budget or a deep search, then return early. if (range.UpperLimit().IsUnknown() || range.LowerLimit().IsUnknown()) { // Note: If we had stack depth too deep in the GetRange call, we'd be // too deep even in the DoesOverflow call. So return early. return; } if (DoesOverflow(block, stmt, treeIndex, path)) { JITDUMP("Method determined to overflow.\n"); return; } JITDUMP("Range value %s\n", range.ToString(m_pCompiler->getAllocatorDebugOnly())); path->RemoveAll(); Widen(block, stmt, treeIndex, path, &range); // If upper or lower limit is unknown, then return. if (range.UpperLimit().IsUnknown() || range.LowerLimit().IsUnknown()) { return; } // Is the range between the lower and upper bound values. if (BetweenBounds(range, 0, bndsChk->gtArrLen)) { JITDUMP("[RangeCheck::OptimizeRangeCheck] Between bounds\n"); m_pCompiler->optRemoveRangeCheck(treeParent, stmt, true, GTF_ASG, true /* force remove */); } return; }
void RangeCheck::OptimizeRangeCheck(BasicBlock* block, GenTreeStmt* stmt, GenTree* treeParent) { // Check if we are dealing with a bounds check node. if (treeParent->OperGet() != GT_COMMA) { return; } // If we are not looking at array bounds check, bail. GenTree* tree = treeParent->gtOp.gtOp1; if (!tree->OperIsBoundsCheck()) { return; } GenTreeBoundsChk* bndsChk = tree->AsBoundsChk(); m_pCurBndsChk = bndsChk; GenTree* treeIndex = bndsChk->gtIndex; // Take care of constant index first, like a[2], for example. ValueNum idxVn = m_pCompiler->vnStore->VNConservativeNormalValue(treeIndex->gtVNPair); ValueNum arrLenVn = m_pCompiler->vnStore->VNConservativeNormalValue(bndsChk->gtArrLen->gtVNPair); int arrSize = 0; if (m_pCompiler->vnStore->IsVNConstant(arrLenVn)) { ssize_t constVal = -1; unsigned iconFlags = 0; if (m_pCompiler->optIsTreeKnownIntValue(true, bndsChk->gtArrLen, &constVal, &iconFlags)) { arrSize = (int)constVal; } } else #ifdef FEATURE_SIMD if (tree->gtOper != GT_SIMD_CHK #ifdef FEATURE_HW_INTRINSICS && tree->gtOper != GT_HW_INTRINSIC_CHK #endif // FEATURE_HW_INTRINSICS ) #endif // FEATURE_SIMD { arrSize = GetArrLength(arrLenVn); } JITDUMP("ArrSize for lengthVN:%03X = %d\n", arrLenVn, arrSize); if (m_pCompiler->vnStore->IsVNConstant(idxVn) && (arrSize > 0)) { ssize_t idxVal = -1; unsigned iconFlags = 0; if (!m_pCompiler->optIsTreeKnownIntValue(true, treeIndex, &idxVal, &iconFlags)) { return; } JITDUMP("[RangeCheck::OptimizeRangeCheck] Is index %d in <0, arrLenVn " FMT_VN " sz:%d>.\n", idxVal, arrLenVn, arrSize); if ((idxVal < arrSize) && (idxVal >= 0)) { JITDUMP("Removing range check\n"); m_pCompiler->optRemoveRangeCheck(treeParent, stmt); return; } } GetRangeMap()->RemoveAll(); GetOverflowMap()->RemoveAll(); m_pSearchPath = new (m_alloc) SearchPath(m_alloc); // Get the range for this index. Range range = GetRange(block, treeIndex, false DEBUGARG(0)); // If upper or lower limit is found to be unknown (top), or it was found to // be unknown because of over budget or a deep search, then return early. if (range.UpperLimit().IsUnknown() || range.LowerLimit().IsUnknown()) { // Note: If we had stack depth too deep in the GetRange call, we'd be // too deep even in the DoesOverflow call. So return early. return; } if (DoesOverflow(block, treeIndex)) { JITDUMP("Method determined to overflow.\n"); return; } JITDUMP("Range value %s\n", range.ToString(m_pCompiler->getAllocatorDebugOnly())); m_pSearchPath->RemoveAll(); Widen(block, treeIndex, &range); // If upper or lower limit is unknown, then return. if (range.UpperLimit().IsUnknown() || range.LowerLimit().IsUnknown()) { return; } // Is the range between the lower and upper bound values. if (BetweenBounds(range, 0, bndsChk->gtArrLen)) { JITDUMP("[RangeCheck::OptimizeRangeCheck] Between bounds\n"); m_pCompiler->optRemoveRangeCheck(treeParent, stmt); } return; }