bool Loop::optimize() { InstructionQueue invariantInstructions; InstructionQueue boundsChecks; IonSpew(IonSpew_LICM, "These instructions are in the loop: "); while (!worklist_.empty()) { if (mir->shouldCancel("LICM (worklist)")) return false; MInstruction *ins = popFromWorklist(); IonSpewHeader(IonSpew_LICM); if (IonSpewEnabled(IonSpew_LICM)) { ins->printName(IonSpewFile); fprintf(IonSpewFile, " <- "); ins->printOpcode(IonSpewFile); fprintf(IonSpewFile, ": "); } if (isLoopInvariant(ins)) { // Flag this instruction as loop invariant. ins->setLoopInvariant(); if (!invariantInstructions.append(ins)) return false; // Loop through uses of invariant instruction and add back to work list. for (MUseDefIterator iter(ins->toDefinition()); iter; iter++) { MDefinition *consumer = iter.def(); if (consumer->isInWorklist()) continue; // if the consumer of this invariant instruction is in the // loop, and it is also worth hoisting, then process it. if (isInLoop(consumer) && isHoistable(consumer)) { if (!insertInWorklist(consumer->toInstruction())) return false; } } if (IonSpewEnabled(IonSpew_LICM)) fprintf(IonSpewFile, " Loop Invariant!\n"); } else if (ins->isBoundsCheck()) { if (!boundsChecks.append(ins)) return false; } } if (!hoistInstructions(invariantInstructions, boundsChecks)) return false; return true; }
void ValueNumberer::breakClass(MDefinition *def) { if (def->valueNumber() == def->id()) { IonSpew(IonSpew_GVN, "Breaking congruence with itself: %d", def->id()); ValueNumberData *defdata = def->valueNumberData(); JS_ASSERT(defdata->classPrev == NULL); // If the def was the only member of the class, then there is nothing to do. if (defdata->classNext == NULL) return; // If upon closer inspection, we are still equivalent to this class // then there isn't anything for us to do. if (!needsSplit(def)) return; // Get a new representative member MDefinition *newRep = defdata->classNext; // Chop off the head of the list (the old representative) newRep->valueNumberData()->classPrev = NULL; def->valueNumberData()->classNext = NULL; IonSpew(IonSpew_GVN, "Choosing a new representative: %d", newRep->id()); // make the VN of every member in the class the VN of the new representative number. for (MDefinition *tmp = newRep; tmp != NULL; tmp = tmp->valueNumberData()->classNext) { // if this instruction is already scheduled to be processed, don't do anything. if (tmp->isInWorklist()) continue; IonSpew(IonSpew_GVN, "Moving to a new congruence class: %d", tmp->id()); tmp->setValueNumber(newRep->id()); markConsumers(tmp); markDefinition(tmp); } // Insert the new representative => number mapping into the table // Logically, there should not be anything in the table currently, but // old values are never removed, so there's a good chance something will // already be there. values.put(newRep, newRep->id()); } else { // The element that is breaking from the list isn't the representative element // just strip it from the list ValueNumberData *defdata = def->valueNumberData(); if (defdata->classPrev) defdata->classPrev->valueNumberData()->classNext = defdata->classNext; if (defdata->classNext) defdata->classNext->valueNumberData()->classPrev = defdata->classPrev; // Make sure there is no nastinees accidentally linking elements into the old list later. defdata->classPrev = NULL; defdata->classNext = NULL; } }
bool LRecoverInfo::appendOperands(MNode* ins) { for (size_t i = 0, end = ins->numOperands(); i < end; i++) { MDefinition* def = ins->getOperand(i); // As there is no cycle in the data-flow (without MPhi), checking for // isInWorkList implies that the definition is already in the // instruction vector, and not processed by a caller of the current // function. if (def->isRecoveredOnBailout() && !def->isInWorklist()) { if (!appendDefinition(def)) return false; } } return true; }
bool RangeAnalysis::analyze() { int numBlocks = 0; for (PostorderIterator i(graph_.poBegin()); i != graph_.poEnd(); i++) { numBlocks++; MBasicBlock *curBlock = *i; if (!curBlock->isLoopHeader()) continue; for (MPhiIterator pi(curBlock->phisBegin()); pi != curBlock->phisEnd(); pi++) if (!pi->initCounts()) return false; } IonSpew(IonSpew_Range, "Doing range propagation"); MDefinitionVector worklist; for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) { for (MDefinitionIterator iter(*block); iter; iter++) { MDefinition *def = *iter; AddToWorklist(worklist, def); } } size_t iters = 0; while (!worklist.empty()) { MDefinition *def = PopFromWorklist(worklist); IonSpew(IonSpew_Range, "recomputing range on %d", def->id()); SpewRange(def); if (!def->earlyAbortCheck() && def->recomputeRange()) { JS_ASSERT(def->range()->lower() <= def->range()->upper()); IonSpew(IonSpew_Range, "Range changed; adding consumers"); IonSpew(IonSpew_Range, "New range for %d is: (%d, %d)", def->id(), def->range()->lower(), def->range()->upper()); for (MUseDefIterator use(def); use; use++) { if(!AddToWorklist(worklist, use.def())) return false; } } iters++; if (iters >= numBlocks * 100) return false; } // Cleanup (in case we stopped due to MAX_ITERS) for(size_t i = 0; i < worklist.length(); i++) worklist[i]->setNotInWorklist(); #ifdef DEBUG for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) { for (MDefinitionIterator iter(*block); iter; iter++) { MDefinition *def = *iter; SpewRange(def); JS_ASSERT(def->range()->lower() <= def->range()->upper()); JS_ASSERT(!def->isInWorklist()); } } #endif return true; }
bool ion::EliminatePhis(MIRGenerator *mir, MIRGraph &graph) { Vector<MPhi *, 16, SystemAllocPolicy> worklist; // Add all observable phis to a worklist. We use the "in worklist" bit to // mean "this phi is live". for (PostorderIterator block = graph.poBegin(); block != graph.poEnd(); block++) { if (mir->shouldCancel("Eliminate Phis (populate loop)")) return false; MPhiIterator iter = block->phisBegin(); while (iter != block->phisEnd()) { // Flag all as unused, only observable phis would be marked as used // when processed by the work list. iter->setUnused(); // If the phi is redundant, remove it here. if (MDefinition *redundant = IsPhiRedundant(*iter)) { iter->replaceAllUsesWith(redundant); iter = block->discardPhiAt(iter); continue; } // Enqueue observable Phis. if (IsPhiObservable(*iter)) { iter->setInWorklist(); if (!worklist.append(*iter)) return false; } iter++; } } // Iteratively mark all phis reachable from live phis. while (!worklist.empty()) { if (mir->shouldCancel("Eliminate Phis (worklist)")) return false; MPhi *phi = worklist.popCopy(); JS_ASSERT(phi->isUnused()); phi->setNotInWorklist(); // The removal of Phis can produce newly redundant phis. if (MDefinition *redundant = IsPhiRedundant(phi)) { // Add to the worklist the used phis which are impacted. for (MUseDefIterator it(phi); it; it++) { if (it.def()->isPhi()) { MPhi *use = it.def()->toPhi(); if (!use->isUnused()) { use->setUnusedUnchecked(); use->setInWorklist(); if (!worklist.append(use)) return false; } } } phi->replaceAllUsesWith(redundant); } else { // Otherwise flag them as used. phi->setNotUnused(); } // The current phi is/was used, so all its operands are used. for (size_t i = 0; i < phi->numOperands(); i++) { MDefinition *in = phi->getOperand(i); if (!in->isPhi() || !in->isUnused() || in->isInWorklist()) continue; in->setInWorklist(); if (!worklist.append(in->toPhi())) return false; } } // Sweep dead phis. for (PostorderIterator block = graph.poBegin(); block != graph.poEnd(); block++) { MPhiIterator iter = block->phisBegin(); while (iter != block->phisEnd()) { if (iter->isUnused()) iter = block->discardPhiAt(iter); else iter++; } } return true; }
void ValueNumberer::breakClass(MDefinition *def) { if (def->valueNumber() == def->id()) { IonSpew(IonSpew_GVN, "Breaking congruence with itself: %d", def->id()); ValueNumberData *defdata = def->valueNumberData(); JS_ASSERT(defdata->classPrev == NULL); // If the def was the only member of the class, then there is nothing to do. if (defdata->classNext == NULL) return; // If upon closer inspection, we are still equivalent to this class // then there isn't anything for us to do. MDefinition *newRep = findSplit(def); if (!newRep) return; ValueNumberData *newdata = newRep->valueNumberData(); // Right now, |defdata| is at the front of the list, and |newdata| is // somewhere in the middle. // // We want to move |defdata| and everything up to but excluding // |newdata| to a new list, with |defdata| still as the canonical // element. // // We then want to take |newdata| and everything after, and // mark them for processing (since |newdata| is now a new canonical // element). // MDefinition *lastOld = newdata->classPrev; JS_ASSERT(lastOld); // newRep is NOT the first element of the list. JS_ASSERT(lastOld->valueNumberData()->classNext == newRep); //lastOld is now the last element of the old list (congruent to //|def|) lastOld->valueNumberData()->classNext = NULL; #ifdef DEBUG for (MDefinition *tmp = def; tmp != NULL; tmp = tmp->valueNumberData()->classNext) { JS_ASSERT(tmp->valueNumber() == def->valueNumber()); JS_ASSERT(tmp->congruentTo(def)); JS_ASSERT(tmp != newRep); } #endif //|newRep| is now the first element of a new list, therefore it is the //new canonical element. Mark the remaining elements in the list //(including |newRep|) newdata->classPrev = NULL; IonSpew(IonSpew_GVN, "Choosing a new representative: %d", newRep->id()); // make the VN of every member in the class the VN of the new representative number. for (MDefinition *tmp = newRep; tmp != NULL; tmp = tmp->valueNumberData()->classNext) { // if this instruction is already scheduled to be processed, don't do anything. if (tmp->isInWorklist()) continue; IonSpew(IonSpew_GVN, "Moving to a new congruence class: %d", tmp->id()); tmp->setValueNumber(newRep->id()); markConsumers(tmp); markDefinition(tmp); } // Insert the new representative => number mapping into the table // Logically, there should not be anything in the table currently, but // old values are never removed, so there's a good chance something will // already be there. values.put(newRep, newRep->id()); } else { // The element that is breaking from the list isn't the representative element // just strip it from the list ValueNumberData *defdata = def->valueNumberData(); if (defdata->classPrev) defdata->classPrev->valueNumberData()->classNext = defdata->classNext; if (defdata->classNext) defdata->classNext->valueNumberData()->classPrev = defdata->classPrev; // Make sure there is no nastinees accidentally linking elements into the old list later. defdata->classPrev = NULL; defdata->classNext = NULL; } }
bool ion::EliminatePhis(MIRGenerator *mir, MIRGraph &graph, Observability observe) { // Eliminates redundant or unobservable phis from the graph. A // redundant phi is something like b = phi(a, a) or b = phi(a, b), // both of which can be replaced with a. An unobservable phi is // one that whose value is never used in the program. // // Note that we must be careful not to eliminate phis representing // values that the interpreter will require later. When the graph // is first constructed, we can be more aggressive, because there // is a greater correspondence between the CFG and the bytecode. // After optimizations such as GVN have been performed, however, // the bytecode and CFG may not correspond as closely to one // another. In that case, we must be more conservative. The flag // |conservativeObservability| is used to indicate that eliminate // phis is being run after some optimizations have been performed, // and thus we should use more conservative rules about // observability. The particular danger is that we can optimize // away uses of a phi because we think they are not executable, // but the foundation for that assumption is false TI information // that will eventually be invalidated. Therefore, if // |conservativeObservability| is set, we will consider any use // from a resume point to be observable. Otherwise, we demand a // use from an actual instruction. Vector<MPhi *, 16, SystemAllocPolicy> worklist; // Add all observable phis to a worklist. We use the "in worklist" bit to // mean "this phi is live". for (PostorderIterator block = graph.poBegin(); block != graph.poEnd(); block++) { if (mir->shouldCancel("Eliminate Phis (populate loop)")) return false; MPhiIterator iter = block->phisBegin(); while (iter != block->phisEnd()) { // Flag all as unused, only observable phis would be marked as used // when processed by the work list. iter->setUnused(); // If the phi is redundant, remove it here. if (MDefinition *redundant = IsPhiRedundant(*iter)) { iter->replaceAllUsesWith(redundant); iter = block->discardPhiAt(iter); continue; } // Enqueue observable Phis. if (IsPhiObservable(*iter, observe)) { iter->setInWorklist(); if (!worklist.append(*iter)) return false; } iter++; } } // Iteratively mark all phis reachable from live phis. while (!worklist.empty()) { if (mir->shouldCancel("Eliminate Phis (worklist)")) return false; MPhi *phi = worklist.popCopy(); JS_ASSERT(phi->isUnused()); phi->setNotInWorklist(); // The removal of Phis can produce newly redundant phis. if (MDefinition *redundant = IsPhiRedundant(phi)) { // Add to the worklist the used phis which are impacted. for (MUseDefIterator it(phi); it; it++) { if (it.def()->isPhi()) { MPhi *use = it.def()->toPhi(); if (!use->isUnused()) { use->setUnusedUnchecked(); use->setInWorklist(); if (!worklist.append(use)) return false; } } } phi->replaceAllUsesWith(redundant); } else { // Otherwise flag them as used. phi->setNotUnused(); } // The current phi is/was used, so all its operands are used. for (size_t i = 0; i < phi->numOperands(); i++) { MDefinition *in = phi->getOperand(i); if (!in->isPhi() || !in->isUnused() || in->isInWorklist()) continue; in->setInWorklist(); if (!worklist.append(in->toPhi())) return false; } } // Sweep dead phis. for (PostorderIterator block = graph.poBegin(); block != graph.poEnd(); block++) { MPhiIterator iter = block->phisBegin(); while (iter != block->phisEnd()) { if (iter->isUnused()) iter = block->discardPhiAt(iter); else iter++; } } return true; }