diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 2f98da83f5d9..e8c6bca3f776 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -1515,6 +1515,19 @@ OptimizeMIR(MIRGenerator *mir) return false; } + // Make loops contiguious. We do this after GVN/UCE and range analysis, + // which can remove CFG edges, exposing more blocks that can be moved. + { + AutoTraceLog log(logger, TraceLogger::MakeLoopsContiguous); + if (!MakeLoopsContiguous(graph)) + return false; + IonSpewPass("Make loops contiguous"); + AssertExtendedGraphCoherency(graph); + + if (mir->shouldCancel("Make loops contiguous")) + return false; + } + // Passes after this point must not move instructions; these analyses // depend on knowing the final order in which instructions will execute. diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp index adcbe452ce30..417fad7bbbcc 100644 --- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -2563,3 +2563,96 @@ jit::AnalyzeArgumentsUsage(JSContext *cx, JSScript *scriptArg) script->setNeedsArgsObj(false); return true; } + +// Reorder the blocks in the loop starting at the given header to be contiguous. +static void +MakeLoopContiguous(MIRGraph &graph, MBasicBlock *header, MBasicBlock *backedge, size_t numMarked) +{ + MOZ_ASSERT(header->isMarked(), "Loop header is not part of loop"); + MOZ_ASSERT(backedge->isMarked(), "Loop backedge is not part of loop"); + + // If there are any blocks between the loop header and the loop backedge + // that are not part of the loop, prepare to move them to the end. We keep + // them in order, which preserves RPO. + ReversePostorderIterator insertIter = graph.rpoBegin(backedge); + insertIter++; + MBasicBlock *insertPt = *insertIter; + + // Visit all the blocks from the loop header to the loop backedge. + size_t headerId = header->id(); + size_t inLoopId = headerId; + size_t afterLoopId = inLoopId + numMarked; + ReversePostorderIterator i = graph.rpoBegin(header); + for (;;) { + MBasicBlock *block = *i++; + MOZ_ASSERT(block->id() >= header->id() && block->id() <= backedge->id(), + "Loop backedge should be last block in loop"); + + if (block->isMarked()) { + // This block is in the loop. + block->unmark(); + block->setId(inLoopId++); + // If we've reached the loop backedge, we're done! + if (block == backedge) + break; + } else { + // This block is not in the loop. Move it to the end. + graph.moveBlockBefore(insertPt, block); + block->setId(afterLoopId++); + } + } + MOZ_ASSERT(header->id() == headerId, "Loop header id changed"); + MOZ_ASSERT(inLoopId == headerId + numMarked, "Wrong number of blocks kept in loop"); + MOZ_ASSERT(afterLoopId == (insertIter != graph.rpoEnd() ? insertPt->id() : graph.numBlocks()), + "Wrong number of blocks moved out of loop"); +} + +// Reorder the blocks in the graph so that loops are contiguous. +bool +jit::MakeLoopsContiguous(MIRGraph &graph) +{ + MBasicBlock *osrBlock = graph.osrBlock(); + Vector inlooplist(graph.alloc()); + + // Visit all loop headers (in any order). + for (MBasicBlockIterator i(graph.begin()); i != graph.end(); i++) { + MBasicBlock *header = *i; + if (!header->isLoopHeader()) + continue; + + // Mark all the blocks in the loop by marking all blocks in a path + // between the backedge and the loop header. + MBasicBlock *backedge = header->backedge(); + size_t numMarked = 1; + backedge->mark(); + if (!inlooplist.append(backedge)) + return false; + do { + MBasicBlock *block = inlooplist.popCopy(); + MOZ_ASSERT(block->id() >= header->id() && block->id() <= backedge->id(), + "Non-OSR predecessor of loop block not between header and backedge"); + if (block == header) + continue; + for (size_t p = 0; p < block->numPredecessors(); p++) { + MBasicBlock *pred = block->getPredecessor(p); + if (pred->isMarked()) + continue; + // Ignore paths entering the loop in the middle from an OSR + // entry. They won't pass through the loop header and they + // aren't part of the loop. + if (osrBlock && osrBlock->dominates(pred) && !osrBlock->dominates(header)) + continue; + ++numMarked; + pred->mark(); + if (!inlooplist.append(pred)) + return false; + } + } while (!inlooplist.empty()); + + // Move all blocks between header and backedge that aren't marked to + // the end of the loop, making the loop itself contiguous. + MakeLoopContiguous(graph, header, backedge, numMarked); + } + + return true; +} diff --git a/js/src/jit/IonAnalysis.h b/js/src/jit/IonAnalysis.h index 4d9dae0b31d4..a9edb1a4e526 100644 --- a/js/src/jit/IonAnalysis.h +++ b/js/src/jit/IonAnalysis.h @@ -31,6 +31,9 @@ enum Observability { bool EliminatePhis(MIRGenerator *mir, MIRGraph &graph, Observability observe); +bool +MakeLoopsContiguous(MIRGraph &graph); + bool EliminateDeadResumePointOperands(MIRGenerator *mir, MIRGraph &graph); diff --git a/js/src/jit/MIRGraph.h b/js/src/jit/MIRGraph.h index e8a52f76acc5..cfec0e739a2a 100644 --- a/js/src/jit/MIRGraph.h +++ b/js/src/jit/MIRGraph.h @@ -616,6 +616,11 @@ class MIRGraph blocks_.remove(block); blocks_.pushBack(block); } + void moveBlockBefore(MBasicBlock *at, MBasicBlock *block) { + JS_ASSERT(block->id()); + blocks_.remove(block); + blocks_.insertBefore(at, block); + } size_t numBlocks() const { return numBlocks_; } diff --git a/js/src/vm/TraceLogging.h b/js/src/vm/TraceLogging.h index 5f465d46b63d..877602848475 100644 --- a/js/src/vm/TraceLogging.h +++ b/js/src/vm/TraceLogging.h @@ -137,6 +137,7 @@ namespace jit { _(RenumberBlocks) \ _(DominatorTree) \ _(PhiAnalysis) \ + _(MakeLoopsContiguous) \ _(ApplyTypes) \ _(ParallelSafetyAnalysis) \ _(AliasAnalysis) \