static bool TryEliminateTypeBarrier(MTypeBarrier *barrier, bool *eliminated) { JS_ASSERT(!*eliminated); const types::StackTypeSet *barrierTypes = barrier->typeSet(); const types::StackTypeSet *inputTypes = barrier->input()->typeSet(); if (!barrierTypes || !inputTypes) return true; bool filtersNull = barrierTypes->filtersType(inputTypes, types::Type::NullType()); bool filtersUndefined = barrierTypes->filtersType(inputTypes, types::Type::UndefinedType()); if (!filtersNull && !filtersUndefined) return true; MBasicBlock *block = barrier->block(); while (true) { BranchDirection direction; MTest *test = block->immediateDominatorBranch(&direction); if (test) { TryEliminateTypeBarrierFromTest(barrier, filtersNull, filtersUndefined, test, direction, eliminated); } MBasicBlock *previous = block->immediateDominator(); if (previous == block) break; block = previous; } return true; }
bool RangeAnalysis::addBetaNobes() { IonSpew(IonSpew_Range, "Adding beta nobes"); for (PostorderIterator i(graph_.poBegin()); i != graph_.poEnd(); i++) { MBasicBlock *block = *i; IonSpew(IonSpew_Range, "Looking at block %d", block->id()); BranchDirection branch_dir; MTest *test = block->immediateDominatorBranch(&branch_dir); if (!test || !test->getOperand(0)->isCompare()) continue; MCompare *compare = test->getOperand(0)->toCompare(); MDefinition *left = compare->getOperand(0); MDefinition *right = compare->getOperand(1); int32 bound; MDefinition *val = NULL; JSOp jsop = compare->jsop(); if (branch_dir == FALSE_BRANCH) jsop = analyze::NegateCompareOp(jsop); if (left->isConstant() && left->toConstant()->value().isInt32()) { bound = left->toConstant()->value().toInt32(); val = right; jsop = analyze::ReverseCompareOp(jsop); } else if (right->isConstant() && right->toConstant()->value().isInt32()) { bound = right->toConstant()->value().toInt32(); val = left; } else { MDefinition *smaller = NULL; MDefinition *greater = NULL; if (jsop == JSOP_LT) { smaller = left; greater = right; } else if (jsop == JSOP_GT) { smaller = right; greater = left; } if (smaller && greater) { MBeta *beta; beta = MBeta::New(smaller, Range(JSVAL_INT_MIN, JSVAL_INT_MAX-1)); block->insertBefore(*block->begin(), beta); replaceDominatedUsesWith(smaller, beta, block); beta = MBeta::New(greater, Range(JSVAL_INT_MIN+1, JSVAL_INT_MAX)); block->insertBefore(*block->begin(), beta); replaceDominatedUsesWith(greater, beta, block); } continue; } JS_ASSERT(val); Range comp; switch (jsop) { case JSOP_LE: comp.setUpper(bound); break; case JSOP_LT: if (!SafeSub(bound, 1, &bound)) break; comp.setUpper(bound); break; case JSOP_GE: comp.setLower(bound); break; case JSOP_GT: if (!SafeAdd(bound, 1, &bound)) break; comp.setLower(bound); break; case JSOP_EQ: comp.setLower(bound); comp.setUpper(bound); default: break; // well, for neq we could have // [-\inf, bound-1] U [bound+1, \inf] but we only use contiguous ranges. } IonSpew(IonSpew_Range, "Adding beta node for %d", val->id()); MBeta *beta = MBeta::New(val, comp); block->insertBefore(*block->begin(), beta); replaceDominatedUsesWith(val, beta, block); } return true; }
void RangeAnalysis::analyzeLoop(MBasicBlock *header) { // Try to compute an upper bound on the number of times the loop backedge // will be taken. Look for tests that dominate the backedge and which have // an edge leaving the loop body. MBasicBlock *backedge = header->backedge(); // Ignore trivial infinite loops. if (backedge == header) return; markBlocksInLoopBody(header, backedge); LoopIterationBound *iterationBound = NULL; MBasicBlock *block = backedge; do { BranchDirection direction; MTest *branch = block->immediateDominatorBranch(&direction); if (block == block->immediateDominator()) break; block = block->immediateDominator(); if (branch) { direction = NegateBranchDirection(direction); MBasicBlock *otherBlock = branch->branchSuccessor(direction); if (!otherBlock->isMarked()) { iterationBound = analyzeLoopIterationCount(header, branch, direction); if (iterationBound) break; } } } while (block != header); if (!iterationBound) { graph_.unmarkBlocks(); return; } #ifdef DEBUG if (IonSpewEnabled(IonSpew_Range)) { Sprinter sp(GetIonContext()->cx); sp.init(); iterationBound->sum.print(sp); IonSpew(IonSpew_Range, "computed symbolic bound on backedges: %s", sp.string()); } #endif // Try to compute symbolic bounds for the phi nodes at the head of this // loop, expressed in terms of the iteration bound just computed. for (MDefinitionIterator iter(header); iter; iter++) { MDefinition *def = *iter; if (def->isPhi()) analyzeLoopPhi(header, iterationBound, def->toPhi()); } // Try to hoist any bounds checks from the loop using symbolic bounds. Vector<MBoundsCheck *, 0, IonAllocPolicy> hoistedChecks; for (ReversePostorderIterator iter(graph_.rpoBegin()); iter != graph_.rpoEnd(); iter++) { MBasicBlock *block = *iter; if (!block->isMarked()) continue; for (MDefinitionIterator iter(block); iter; iter++) { MDefinition *def = *iter; if (def->isBoundsCheck() && def->isMovable()) { if (tryHoistBoundsCheck(header, def->toBoundsCheck())) hoistedChecks.append(def->toBoundsCheck()); } } } // Note: replace all uses of the original bounds check with the // actual index. This is usually done during bounds check elimination, // but in this case it's safe to do it here since the load/store is // definitely not loop-invariant, so we will never move it before // one of the bounds checks we just added. for (size_t i = 0; i < hoistedChecks.length(); i++) { MBoundsCheck *ins = hoistedChecks[i]; ins->replaceAllUsesWith(ins->index()); ins->block()->discard(ins); } graph_.unmarkBlocks(); }
bool Loop::hoistInstructions(InstructionQueue &toHoist, InstructionQueue &boundsChecks) { // Hoist bounds checks first, so that hoistBoundsCheck can test for // invariant instructions, but delay actual insertion until the end to // handle dependencies on loop invariant instructions. InstructionQueue hoistedChecks; for (size_t i = 0; i < boundsChecks.length(); i++) { MBoundsCheck *ins = boundsChecks[i]->toBoundsCheck(); if (isLoopInvariant(ins) || !isInLoop(ins)) continue; // Try to find a test dominating the bounds check which can be // transformed into a hoistable check. Stop after the first such check // which could be transformed (the one which will be the closest to the // access in the source). MBasicBlock *block = ins->block(); while (true) { BranchDirection direction; MTest *branch = block->immediateDominatorBranch(&direction); if (branch) { MInstruction *upper, *lower; tryHoistBoundsCheck(ins, branch, direction, &upper, &lower); if (upper && !hoistedChecks.append(upper)) return false; if (lower && !hoistedChecks.append(lower)) return false; if (upper || lower) { ins->block()->discard(ins); break; } } MBasicBlock *dom = block->immediateDominator(); if (dom == block) break; block = dom; } } // Move all instructions to the preLoop_ block just before the control instruction. for (size_t i = 0; i < toHoist.length(); i++) { MInstruction *ins = toHoist[i]; // Loads may have an implicit dependency on either stores (effectful instructions) or // control instructions so we should never move these. JS_ASSERT(!ins->isControlInstruction()); JS_ASSERT(!ins->isEffectful()); JS_ASSERT(ins->isMovable()); if (checkHotness(ins->block())) { ins->block()->moveBefore(preLoop_->lastIns(), ins); ins->setNotLoopInvariant(); } } for (size_t i = 0; i < hoistedChecks.length(); i++) { MInstruction *ins = hoistedChecks[i]; preLoop_->insertBefore(preLoop_->lastIns(), ins); } return true; }