static void VisitLoop(MIRGraph &graph, MBasicBlock *header) { MInstruction *hoistPoint = header->loopPredecessor()->lastIns(); JitSpew(JitSpew_LICM, " Visiting loop with header block%u, hoisting to %s%u", header->id(), hoistPoint->opName(), hoistPoint->id()); MBasicBlock *backedge = header->backedge(); // This indicates whether the loop contains calls or other things which // clobber most or all floating-point registers. In such loops, // floating-point constants should not be hoisted unless it enables further // hoisting. bool hasCalls = LoopContainsPossibleCall(graph, header, backedge); for (auto i(graph.rpoBegin(header)); ; ++i) { MOZ_ASSERT(i != graph.rpoEnd(), "Reached end of graph searching for blocks in loop"); MBasicBlock *block = *i; if (!block->isMarked()) continue; VisitLoopBlock(block, header, hoistPoint, hasCalls); if (block == backedge) break; } }
bool jit::LICM(MIRGenerator *mir, MIRGraph &graph) { JitSpew(JitSpew_LICM, "Beginning LICM pass"); // Iterate in RPO to visit outer loops before inner loops. We'd hoist the // same things either way, but outer first means we do a little less work. for (auto i(graph.rpoBegin()), e(graph.rpoEnd()); i != e; ++i) { MBasicBlock *header = *i; if (!header->isLoopHeader()) continue; bool canOsr; MarkLoopBlocks(graph, header, &canOsr); // Hoisting out of a loop that has an entry from the OSR block in // addition to its normal entry is tricky. In theory we could clone // the instruction and insert phis. if (!canOsr) VisitLoop(graph, header); else JitSpew(JitSpew_LICM, " Skipping loop with header block%u due to OSR", header->id()); UnmarkLoopBlocks(graph, header); if (mir->shouldCancel("LICM (main loop)")) return false; } return true; }
bool ion::RenumberBlocks(MIRGraph &graph) { size_t id = 0; for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) block->setId(id++); return true; }
static void ComputeImmediateDominators(MIRGraph &graph) { // The default start block is a root and therefore only self-dominates. MBasicBlock *startBlock = *graph.begin(); startBlock->setImmediateDominator(startBlock); // Any OSR block is a root and therefore only self-dominates. MBasicBlock *osrBlock = graph.osrBlock(); if (osrBlock) osrBlock->setImmediateDominator(osrBlock); bool changed = true; while (changed) { changed = false; ReversePostorderIterator block = graph.rpoBegin(); // For each block in RPO, intersect all dominators. for (; block != graph.rpoEnd(); block++) { // If a node has once been found to have no exclusive dominator, // it will never have an exclusive dominator, so it may be skipped. if (block->immediateDominator() == *block) continue; MBasicBlock *newIdom = block->getPredecessor(0); // Find the first common dominator. for (size_t i = 1; i < block->numPredecessors(); i++) { MBasicBlock *pred = block->getPredecessor(i); if (pred->immediateDominator() != NULL) newIdom = IntersectDominators(pred, newIdom); // If there is no common dominator, the block self-dominates. if (newIdom == NULL) { block->setImmediateDominator(*block); changed = true; break; } } if (newIdom && block->immediateDominator() != newIdom) { block->setImmediateDominator(newIdom); changed = true; } } } #ifdef DEBUG // Assert that all blocks have dominator information. for (MBasicBlockIterator block(graph.begin()); block != graph.end(); block++) { JS_ASSERT(block->immediateDominator() != NULL); } #endif }
static void AssertReversePostOrder(MIRGraph &graph) { // Check that every block is visited after all its predecessors (except backedges). for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) { JS_ASSERT(!block->isMarked()); for (size_t i = 0; i < block->numPredecessors(); i++) { MBasicBlock *pred = block->getPredecessor(i); JS_ASSERT_IF(!pred->isLoopBackedge(), pred->isMarked()); } block->mark(); } graph.unmarkBlocks(); }
// Test whether any instruction in the loop possiblyCalls(). static bool LoopContainsPossibleCall(MIRGraph &graph, MBasicBlock *header, MBasicBlock *backedge) { for (auto i(graph.rpoBegin(header)); ; ++i) { MOZ_ASSERT(i != graph.rpoEnd(), "Reached end of graph searching for blocks in loop"); MBasicBlock *block = *i; if (!block->isMarked()) continue; for (auto insIter(block->begin()), insEnd(block->end()); insIter != insEnd; ++insIter) { MInstruction *ins = *insIter; if (ins->possiblyCalls()) { JitSpew(JitSpew_LICM, " Possile call found at %s%u", ins->opName(), ins->id()); return true; } } if (block == backedge) break; } return false; }
bool jit::ReorderInstructions(MIRGraph& graph) { // Renumber all instructions in the graph as we go. size_t nextId = 0; // List of the headers of any loops we are in. Vector<MBasicBlock*, 4, SystemAllocPolicy> loopHeaders; for (ReversePostorderIterator block(graph.rpoBegin()); block != graph.rpoEnd(); block++) { // Renumber all definitions inside the basic blocks. for (MPhiIterator iter(block->phisBegin()); iter != block->phisEnd(); iter++) iter->setId(nextId++); for (MInstructionIterator iter(block->begin()); iter != block->end(); iter++) iter->setId(nextId++); // Don't reorder instructions within entry blocks, which have special requirements. if (*block == graph.entryBlock() || *block == graph.osrBlock()) continue; if (block->isLoopHeader()) { if (!loopHeaders.append(*block)) return false; } MBasicBlock* innerLoop = loopHeaders.empty() ? nullptr : loopHeaders.back(); MInstruction* top = block->safeInsertTop(); MInstructionReverseIterator rtop = ++block->rbegin(top); for (MInstructionIterator iter(block->begin(top)); iter != block->end(); ) { MInstruction* ins = *iter; // Filter out some instructions which are never reordered. if (ins->isEffectful() || !ins->isMovable() || ins->resumePoint() || ins == block->lastIns()) { iter++; continue; } // Move constants with a single use in the current block to the // start of the block. Constants won't be reordered by the logic // below, as they have no inputs. Moving them up as high as // possible can allow their use to be moved up further, though, // and has no cost if the constant is emitted at its use. if (ins->isConstant() && ins->hasOneUse() && ins->usesBegin()->consumer()->block() == *block && !IsFloatingPointType(ins->type())) { iter++; MInstructionIterator targetIter = block->begin(); while (targetIter->isConstant() || targetIter->isInterruptCheck()) { if (*targetIter == ins) break; targetIter++; } MoveBefore(*block, *targetIter, ins); continue; } // Look for inputs where this instruction is the last use of that // input. If we move this instruction up, the input's lifetime will // be shortened, modulo resume point uses (which don't need to be // stored in a register, and can be handled by the register // allocator by just spilling at some point with no reload). Vector<MDefinition*, 4, SystemAllocPolicy> lastUsedInputs; for (size_t i = 0; i < ins->numOperands(); i++) { MDefinition* input = ins->getOperand(i); if (!input->isConstant() && IsLastUse(ins, input, innerLoop)) { if (!lastUsedInputs.append(input)) return false; } } // Don't try to move instructions which aren't the last use of any // of their inputs (we really ought to move these down instead). if (lastUsedInputs.length() < 2) { iter++; continue; } MInstruction* target = ins; for (MInstructionReverseIterator riter = ++block->rbegin(ins); riter != rtop; riter++) { MInstruction* prev = *riter; if (prev->isInterruptCheck()) break; // The instruction can't be moved before any of its uses. bool isUse = false; for (size_t i = 0; i < ins->numOperands(); i++) { if (ins->getOperand(i) == prev) { isUse = true; break; } } if (isUse) break; // The instruction can't be moved before an instruction that // stores to a location read by the instruction. if (prev->isEffectful() && (ins->getAliasSet().flags() & prev->getAliasSet().flags()) && ins->mightAlias(prev) != MDefinition::AliasType::NoAlias) { break; } // Make sure the instruction will still be the last use of one // of its inputs when moved up this far. for (size_t i = 0; i < lastUsedInputs.length(); ) { bool found = false; for (size_t j = 0; j < prev->numOperands(); j++) { if (prev->getOperand(j) == lastUsedInputs[i]) { found = true; break; } } if (found) { lastUsedInputs[i] = lastUsedInputs.back(); lastUsedInputs.popBack(); } else { i++; } } if (lastUsedInputs.length() < 2) break; // We can move the instruction before this one. target = prev; } iter++; MoveBefore(*block, target, ins); } if (block->isLoopBackedge()) loopHeaders.popBack(); } return true; }