bool RangeAnalysis::tryHoistBoundsCheck(MBasicBlock *header, MBoundsCheck *ins) { // The bounds check's length must be loop invariant. if (ins->length()->block()->isMarked()) return false; // The bounds check's index should not be loop invariant (else we would // already have hoisted it during LICM). SimpleLinearSum index = ExtractLinearSum(ins->index()); if (!index.term || !index.term->block()->isMarked()) return false; // Check for a symbolic lower and upper bound on the index. If either // condition depends on an iteration bound for the loop, only hoist if // the bounds check is dominated by the iteration bound's test. if (!index.term->range()) return false; const SymbolicBound *lower = index.term->range()->symbolicLower(); if (!lower || !SymbolicBoundIsValid(header, ins, lower)) return false; const SymbolicBound *upper = index.term->range()->symbolicUpper(); if (!upper || !SymbolicBoundIsValid(header, ins, upper)) return false; MBasicBlock *preLoop = header->loopPredecessor(); JS_ASSERT(!preLoop->isMarked()); MDefinition *lowerTerm = ConvertLinearSum(preLoop, lower->sum); if (!lowerTerm) return false; MDefinition *upperTerm = ConvertLinearSum(preLoop, upper->sum); if (!upperTerm) return false; // We are checking that index + indexConstant >= 0, and know that // index >= lowerTerm + lowerConstant. Thus, check that: // // lowerTerm + lowerConstant + indexConstant >= 0 // lowerTerm >= -lowerConstant - indexConstant int32_t lowerConstant = 0; if (!SafeSub(lowerConstant, index.constant, &lowerConstant)) return false; if (!SafeSub(lowerConstant, lower->sum.constant(), &lowerConstant)) return false; MBoundsCheckLower *lowerCheck = MBoundsCheckLower::New(lowerTerm); lowerCheck->setMinimum(lowerConstant); // We are checking that index < boundsLength, and know that // index <= upperTerm + upperConstant. Thus, check that: // // upperTerm + upperConstant < boundsLength int32_t upperConstant = index.constant; if (!SafeAdd(upper->sum.constant(), upperConstant, &upperConstant)) return false; MBoundsCheck *upperCheck = MBoundsCheck::New(upperTerm, ins->length()); upperCheck->setMinimum(upperConstant); upperCheck->setMaximum(upperConstant); // Hoist the loop invariant upper and lower bounds checks. preLoop->insertBefore(preLoop->lastIns(), lowerCheck); preLoop->insertBefore(preLoop->lastIns(), upperCheck); return true; }
// Try to compute hoistable checks for the upper and lower bound on ins, // according to a test in the loop which dominates ins. // // Given a bounds check within a loop which is not loop invariant, we would // like to compute loop invariant bounds checks which imply that the inner // check will succeed. These invariant checks can then be added to the // preheader, and the inner check eliminated. // // Example: // // for (i = v; i < n; i++) // x[i] = 0; // // There are two constraints captured by the bounds check here: i >= 0, and // i < length(x). 'i' is not loop invariant, but we can still hoist these // checks: // // - At the point of the check, it is known that i < n. Given this, // if n <= length(x) then i < length(x), and since n and length(x) are loop // invariant the former condition can be hoisted and the i < length(x) check // removed. // // - i is only incremented within the loop, so if its initial value is >= 0 // then all its values within the loop will also be >= 0. The lower bounds // check can be hoisted as v >= 0. // // tryHoistBoundsCheck encodes this logic. Given a bounds check B and a test T // in the loop dominating that bounds check, where B and T share a non-invariant // term lhs, a new check C is computed such that T && C imply B. void Loop::tryHoistBoundsCheck(MBoundsCheck *ins, MTest *test, BranchDirection direction, MInstruction **pupper, MInstruction **plower) { *pupper = NULL; *plower = NULL; if (!isLoopInvariant(ins->length())) return; LinearSum lhs(NULL, 0); MDefinition *rhs; bool lessEqual; if (!ExtractLinearInequality(test, direction, &lhs, &rhs, &lessEqual)) return; // Ensure the rhs is a loop invariant term. if (rhs && !isLoopInvariant(rhs)) { if (!isLoopInvariant(lhs.term)) return; MDefinition *temp = lhs.term; lhs.term = rhs; rhs = temp; if (!SafeSub(0, lhs.constant, &lhs.constant)) return; lessEqual = !lessEqual; } // Ensure the lhs is a phi node from the start of the loop body. if (!lhs.term || !lhs.term->isPhi() || lhs.term->block() != header_) return; // Check if the lhs in the conditional matches the bounds check index. LinearSum index = ExtractLinearSum(ins->index()); if (index.term != lhs.term) return; if (!lessEqual) return; // At the point of the access, it is known that lhs + lhsN <= rhs, and the // bounds check is that lhs + indexN + maximum < length. To ensure the // bounds check holds then, we need to ensure that: // // rhs - lhsN + indexN + maximum < length int32 adjustment; if (!SafeSub(index.constant, lhs.constant, &adjustment)) return; if (!SafeAdd(adjustment, ins->maximum(), &adjustment)) return; // For the lower bound, check that lhs + indexN + minimum >= 0, e.g. // // lhs >= -indexN - minimum // // lhs is not loop invariant, but if this condition holds of the backing // variable at loop entry and the variable's value never decreases in the // loop body, it will hold throughout the loop. uint32 position = preLoop_->positionInPhiSuccessor(); MDefinition *initialIndex = lhs.term->toPhi()->getOperand(position); if (!nonDecreasing(initialIndex, lhs.term)) return; int32 lowerBound; if (!SafeSub(0, index.constant, &lowerBound)) return; if (!SafeSub(lowerBound, ins->minimum(), &lowerBound)) return; // XXX limit on how much can be hoisted, to ensure ballast works? if (!rhs) { rhs = MConstant::New(Int32Value(adjustment)); adjustment = 0; preLoop_->insertBefore(preLoop_->lastIns(), rhs->toInstruction()); } MBoundsCheck *upper = MBoundsCheck::New(rhs, ins->length()); upper->setMinimum(adjustment); upper->setMaximum(adjustment); MBoundsCheckLower *lower = MBoundsCheckLower::New(initialIndex); lower->setMinimum(lowerBound); *pupper = upper; *plower = lower; }