bool ValueNumberer::eliminateRedundancies() { // A definition is 'redundant' iff it is dominated by another definition // with the same value number. // // So, we traverse the dominator tree in pre-order, maintaining a hashmap // from value numbers to instructions. // // For each definition d with value number v, we look up v in the hashmap. // // If there is a definition d' in the hashmap, and the current traversal // index is within that instruction's dominated range, then we eliminate d, // replacing all uses of d with uses of d'. // // If there is no valid definition in the hashtable (the current definition // is not in dominated scope), then we insert the current instruction, // since it is the most dominant instruction with the given value number. InstructionMap defs; if (!defs.init()) return false; IonSpew(IonSpew_GVN, "Eliminating redundant instructions"); // Stack for pre-order CFG traversal. Vector<MBasicBlock *, 1, IonAllocPolicy> worklist; // The index of the current block in the CFG traversal. size_t index = 0; // Add all self-dominating blocks to the worklist. // This includes all roots. Order does not matter. for (MBasicBlockIterator i(graph_.begin()); i != graph_.end(); i++) { MBasicBlock *block = *i; if (block->immediateDominator() == block) { if (!worklist.append(block)) return false; } } // Starting from each self-dominating block, traverse the CFG in pre-order. while (!worklist.empty()) { MBasicBlock *block = worklist.popCopy(); IonSpew(IonSpew_GVN, "Looking at block %d", block->id()); // Add all immediate dominators to the front of the worklist. for (size_t i = 0; i < block->numImmediatelyDominatedBlocks(); i++) { if (!worklist.append(block->getImmediatelyDominatedBlock(i))) return false; } // For each instruction, attempt to look up a dominating definition. for (MDefinitionIterator iter(block); iter; ) { MDefinition *ins = simplify(*iter, true); // Instruction was replaced, and all uses have already been fixed. if (ins != *iter) { iter = block->discardDefAt(iter); continue; } // Instruction has side-effects and cannot be folded. if (!ins->isMovable() || ins->isEffectful()) { iter++; continue; } MDefinition *dom = findDominatingDef(defs, ins, index); if (!dom) return false; // Insertion failed. if (dom == ins || !dom->updateForReplacement(ins)) { iter++; continue; } IonSpew(IonSpew_GVN, "instruction %d is dominated by instruction %d (from block %d)", ins->id(), dom->id(), dom->block()->id()); ins->replaceAllUsesWith(dom); JS_ASSERT(!ins->hasUses()); JS_ASSERT(ins->block() == block); JS_ASSERT(!ins->isEffectful()); JS_ASSERT(ins->isMovable()); iter = ins->block()->discardDefAt(iter); } index++; } JS_ASSERT(index == graph_.numBlocks()); return true; }
bool ValueNumberer::computeValueNumbers() { // At the end of this function, we will have the value numbering stored in // each instruction. // // We also need an "optimistic" value number, for temporary use, which is // stored in a hashtable. // // For the instruction x := y op z, we map (op, VN[y], VN[z]) to a value // number, say v. If it is not in the map, we use the instruction id. // // If the instruction in question's value number is not already // v, we break the congruence and set it to v. We repeat until saturation. // This will take at worst O(d) time, where d is the loop connectedness // of the SSA def/use graph. // // The algorithm is the simple RPO-based algorithm from // "SCC-Based Value Numbering" by Cooper and Simpson. // // If we are performing a pessimistic pass, then we assume that every // definition is in its own congruence class, since we know nothing about // values that enter Phi nodes through back edges. We then make one pass // through the graph, ignoring back edges. This yields less congruences on // any graph with back-edges, but is much faster to perform. IonSpew(IonSpew_GVN, "Numbering instructions"); if (!values.init()) return false; // Stick a VN object onto every mdefinition for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) { if (mir->shouldCancel("Value Numbering (preparation loop")) return false; for (MDefinitionIterator iter(*block); iter; iter++) iter->setValueNumberData(new(alloc()) ValueNumberData); MControlInstruction *jump = block->lastIns(); jump->setValueNumberData(new(alloc()) ValueNumberData); } // Assign unique value numbers if pessimistic. // It might be productive to do this in the MDefinition constructor or // possibly in a previous pass, if it seems reasonable. if (pessimisticPass_) { for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) { for (MDefinitionIterator iter(*block); iter; iter++) iter->setValueNumber(iter->id()); } } else { // For each root block, add all of its instructions to the worklist. markBlock(*(graph_.begin())); if (graph_.osrBlock()) markBlock(graph_.osrBlock()); } while (count_ > 0) { #ifdef DEBUG if (!pessimisticPass_) { size_t debugCount = 0; IonSpew(IonSpew_GVN, "The following instructions require processing:"); for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) { for (MDefinitionIterator iter(*block); iter; iter++) { if (iter->isInWorklist()) { IonSpew(IonSpew_GVN, "\t%d", iter->id()); debugCount++; } } if (block->lastIns()->isInWorklist()) { IonSpew(IonSpew_GVN, "\t%d", block->lastIns()->id()); debugCount++; } } if (!debugCount) IonSpew(IonSpew_GVN, "\tNone"); JS_ASSERT(debugCount == count_); } #endif for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) { if (mir->shouldCancel("Value Numbering (main loop)")) return false; for (MDefinitionIterator iter(*block); iter; ) { if (!isMarked(*iter)) { iter++; continue; } JS_ASSERT_IF(!pessimisticPass_, count_ > 0); unmarkDefinition(*iter); MDefinition *ins = simplify(*iter, false); if (ins != *iter) { iter = block->discardDefAt(iter); continue; } // Don't bother storing this instruction in the HashMap if // (a) eliminateRedundancies will never eliminate it (because // it's non-movable or effectful) and (b) no other instruction's // value number depends on it. if (!ins->hasDefUses() && (!ins->isMovable() || ins->isEffectful())) { iter++; continue; } uint32_t value = lookupValue(ins); if (!value) return false; // Hashtable insertion failed if (ins->valueNumber() != value) { IonSpew(IonSpew_GVN, "Broke congruence for instruction %d (%p) with VN %d (now using %d)", ins->id(), (void *) ins, ins->valueNumber(), value); ins->setValueNumber(value); markConsumers(ins); } iter++; } // Process control flow instruction: MControlInstruction *jump = block->lastIns(); jump = simplifyControlInstruction(jump); // If we are pessimistic, then this will never get set. if (!jump->isInWorklist()) continue; unmarkDefinition(jump); if (jump->valueNumber() == 0) { jump->setValueNumber(jump->id()); for (size_t i = 0; i < jump->numSuccessors(); i++) markBlock(jump->getSuccessor(i)); } } // If we are doing a pessimistic pass, we only go once through the // instruction list. if (pessimisticPass_) break; } #ifdef DEBUG for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) { for (MDefinitionIterator iter(*block); iter; iter++) { JS_ASSERT(!iter->isInWorklist()); JS_ASSERT_IF(iter->valueNumber() == 0, !iter->hasDefUses() && (!iter->isMovable() || iter->isEffectful())); } } #endif return true; }