Bug 1520998 - Remove Ion LoopUnroller code. r=nbp
authorJan de Mooij <jdemooij@mozilla.com>
Fri, 18 Jan 2019 14:06:46 +0000
changeset 511708 0cbcad902d49e0be967b41b72252e18737bbed04
parent 511707 50cc5c5bd5efdb15adda6b1490ecf907f5fe100b
child 511709 1aee4f577b05824f71ca36db2ab640e76d90cc60
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnbp
bugs1520998
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1520998 - Remove Ion LoopUnroller code. r=nbp It was added more than 4 years ago but never got enabled due to benchmark regressions. Websites can now use WebAssembly for hot code (and the plan is to use Cranelift instead of Ion there) so it's unlikely we will need this soon. Differential Revision: https://phabricator.services.mozilla.com/D16950
js/src/jit/Ion.cpp
js/src/jit/IonOptimizationLevels.cpp
js/src/jit/IonOptimizationLevels.h
js/src/jit/JitOptions.cpp
js/src/jit/JitOptions.h
js/src/jit/JitSpewer.cpp
js/src/jit/JitSpewer.h
js/src/jit/LoopUnroller.cpp
js/src/jit/LoopUnroller.h
js/src/jit/moz.build
js/src/shell/fuzz-flags.txt
js/src/shell/js.cpp
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -31,17 +31,16 @@
 #include "jit/IonOptimizationLevels.h"
 #include "jit/JitcodeMap.h"
 #include "jit/JitCommon.h"
 #include "jit/JitRealm.h"
 #include "jit/JitSpewer.h"
 #include "jit/LICM.h"
 #include "jit/Linker.h"
 #include "jit/LIR.h"
-#include "jit/LoopUnroller.h"
 #include "jit/Lowering.h"
 #include "jit/PerfSpewer.h"
 #include "jit/RangeAnalysis.h"
 #include "jit/ScalarReplacement.h"
 #include "jit/Sink.h"
 #include "jit/StupidAllocator.h"
 #include "jit/ValueNumbering.h"
 #include "jit/WasmBCE.h"
@@ -1466,27 +1465,16 @@ bool OptimizeMIR(MIRGenerator* mir) {
       }
       gs.spewPass("Truncate Doubles");
       AssertExtendedGraphCoherency(graph);
 
       if (mir->shouldCancel("Truncate Doubles")) {
         return false;
       }
     }
-
-    if (mir->optimizationInfo().loopUnrollingEnabled()) {
-      AutoTraceLog log(logger, TraceLogger_LoopUnrolling);
-
-      if (!UnrollLoops(graph, r.loopIterationBounds)) {
-        return false;
-      }
-
-      gs.spewPass("Unroll Loops");
-      AssertExtendedGraphCoherency(graph);
-    }
   }
 
   if (!JitOptions.disableRecoverIns) {
     AutoTraceLog log(logger, TraceLogger_Sink);
     if (!Sink(mir, graph)) {
       return false;
     }
     gs.spewPass("Sink");
--- a/js/src/jit/IonOptimizationLevels.cpp
+++ b/js/src/jit/IonOptimizationLevels.cpp
@@ -27,17 +27,16 @@ void OptimizationInfo::initNormalOptimiz
 
   autoTruncate_ = true;
   eaa_ = true;
   edgeCaseAnalysis_ = true;
   eliminateRedundantChecks_ = true;
   inlineInterpreted_ = true;
   inlineNative_ = true;
   licm_ = true;
-  loopUnrolling_ = true;
   gvn_ = true;
   rangeAnalysis_ = true;
   reordering_ = true;
   sincos_ = true;
   sink_ = true;
 
   registerAllocator_ = RegisterAllocator_Backtracking;
 
--- a/js/src/jit/IonOptimizationLevels.h
+++ b/js/src/jit/IonOptimizationLevels.h
@@ -60,19 +60,16 @@ class OptimizationInfo {
   bool gvn_;
 
   // Toggles whether loop invariant code motion is performed.
   bool licm_;
 
   // Toggles whether Range Analysis is used.
   bool rangeAnalysis_;
 
-  // Toggles whether loop unrolling is performed.
-  bool loopUnrolling_;
-
   // Toggles whether instruction reordering is performed.
   bool reordering_;
 
   // Toggles whether Truncation based on Range Analysis is used.
   bool autoTruncate_;
 
   // Toggles whether sincos is used.
   bool sincos_;
@@ -143,17 +140,16 @@ class OptimizationInfo {
         ama_(false),
         edgeCaseAnalysis_(false),
         eliminateRedundantChecks_(false),
         inlineInterpreted_(false),
         inlineNative_(false),
         gvn_(false),
         licm_(false),
         rangeAnalysis_(false),
-        loopUnrolling_(false),
         reordering_(false),
         autoTruncate_(false),
         sincos_(false),
         sink_(false),
         registerAllocator_(RegisterAllocator_Backtracking),
         inlineMaxBytecodePerCallSiteHelperThread_(0),
         inlineMaxBytecodePerCallSiteMainThread_(0),
         inlineMaxCalleeInlinedBytecodeLength_(0),
@@ -186,20 +182,16 @@ class OptimizationInfo {
   bool gvnEnabled() const { return gvn_ && !JitOptions.disableGvn; }
 
   bool licmEnabled() const { return licm_ && !JitOptions.disableLicm; }
 
   bool rangeAnalysisEnabled() const {
     return rangeAnalysis_ && !JitOptions.disableRangeAnalysis;
   }
 
-  bool loopUnrollingEnabled() const {
-    return loopUnrolling_ && !JitOptions.disableLoopUnrolling;
-  }
-
   bool instructionReorderingEnabled() const {
     return reordering_ && !JitOptions.disableInstructionReordering;
   }
 
   bool autoTruncateEnabled() const {
     return autoTruncate_ && rangeAnalysisEnabled();
   }
 
--- a/js/src/jit/JitOptions.cpp
+++ b/js/src/jit/JitOptions.cpp
@@ -96,19 +96,16 @@ DefaultJitOptions::DefaultJitOptions() {
   SET_DEFAULT(disableGvn, false);
 
   // Toggles whether inlining is globally disabled.
   SET_DEFAULT(disableInlining, false);
 
   // Toggles whether loop invariant code motion is globally disabled.
   SET_DEFAULT(disableLicm, false);
 
-  // Toggles whether Loop Unrolling is globally disabled.
-  SET_DEFAULT(disableLoopUnrolling, true);
-
   // Toggles wheter optimization tracking is globally disabled.
   SET_DEFAULT(disableOptimizationTracking, true);
 
   // Toggle whether Profile Guided Optimization is globally disabled.
   SET_DEFAULT(disablePgo, false);
 
   // Toggles whether instruction reordering is globally disabled.
   SET_DEFAULT(disableInstructionReordering, false);
--- a/js/src/jit/JitOptions.h
+++ b/js/src/jit/JitOptions.h
@@ -50,17 +50,16 @@ struct DefaultJitOptions {
   bool runExtraChecks;
   bool disableInlineBacktracking;
   bool disableAma;
   bool disableEaa;
   bool disableEdgeCaseAnalysis;
   bool disableGvn;
   bool disableInlining;
   bool disableLicm;
-  bool disableLoopUnrolling;
   bool disableOptimizationTracking;
   bool disablePgo;
   bool disableInstructionReordering;
   bool disableRangeAnalysis;
   bool disableRecoverIns;
   bool disableScalarReplacement;
   bool disableCacheIR;
   bool disableCacheIRBinaryArith;
--- a/js/src/jit/JitSpewer.cpp
+++ b/js/src/jit/JitSpewer.cpp
@@ -364,17 +364,16 @@ void jit::CheckLogging() {
         "  codegen       Native code generation\n"
         "  bailouts      Bailouts\n"
         "  caches        Inline caches\n"
         "  osi           Invalidation\n"
         "  safepoints    Safepoints\n"
         "  pools         Literal Pools (ARM only for now)\n"
         "  cacheflush    Instruction Cache flushes (ARM only for now)\n"
         "  range         Range Analysis\n"
-        "  unroll        Loop unrolling\n"
         "  logs          JSON visualization logging\n"
         "  logs-sync     Same as logs, but flushes between each pass (sync. "
         "compiled functions only).\n"
         "  profiling     Profiling-related information\n"
         "  trackopts     Optimization tracking information gathered by the "
         "Gecko profiler. "
         "(Note: call enableGeckoProfiling() in your script to enable it).\n"
         "  trackopts-ext Encoding information about optimization tracking\n"
@@ -419,19 +418,16 @@ void jit::CheckLogging() {
     EnableChannel(JitSpew_IonMIR);
   }
   if (ContainsFlag(env, "gvn")) {
     EnableChannel(JitSpew_GVN);
   }
   if (ContainsFlag(env, "range")) {
     EnableChannel(JitSpew_Range);
   }
-  if (ContainsFlag(env, "unroll")) {
-    EnableChannel(JitSpew_Unrolling);
-  }
   if (ContainsFlag(env, "licm")) {
     EnableChannel(JitSpew_LICM);
   }
   if (ContainsFlag(env, "flac")) {
     EnableChannel(JitSpew_FLAC);
   }
   if (ContainsFlag(env, "eaa")) {
     EnableChannel(JitSpew_EAA);
--- a/js/src/jit/JitSpewer.h
+++ b/js/src/jit/JitSpewer.h
@@ -34,18 +34,16 @@ namespace jit {
   /* Information during GVN */             \
   _(GVN)                                   \
   /* Information during sincos */          \
   _(Sincos)                                \
   /* Information during sinking */         \
   _(Sink)                                  \
   /* Information during Range analysis */  \
   _(Range)                                 \
-  /* Information during loop unrolling */  \
-  _(Unrolling)                             \
   /* Information during LICM */            \
   _(LICM)                                  \
   /* Info about fold linear constants */   \
   _(FLAC)                                  \
   /* Effective address analysis info */    \
   _(EAA)                                   \
   /* Information during regalloc */        \
   _(RegAlloc)                              \
deleted file mode 100644
--- a/js/src/jit/LoopUnroller.cpp
+++ /dev/null
@@ -1,471 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: set ts=8 sts=2 et sw=2 tw=80:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "jit/LoopUnroller.h"
-
-#include "jit/MIRGraph.h"
-
-#include "vm/JSScript-inl.h"
-
-using namespace js;
-using namespace js::jit;
-
-using mozilla::ArrayLength;
-
-namespace {
-
-struct LoopUnroller {
-  typedef HashMap<MDefinition*, MDefinition*, PointerHasher<MDefinition*>,
-                  SystemAllocPolicy>
-      DefinitionMap;
-
-  explicit LoopUnroller(MIRGraph& graph)
-      : graph(graph),
-        alloc(graph.alloc()),
-        header(nullptr),
-        backedge(nullptr),
-        unrolledHeader(nullptr),
-        unrolledBackedge(nullptr),
-        oldPreheader(nullptr),
-        newPreheader(nullptr) {}
-
-  MIRGraph& graph;
-  TempAllocator& alloc;
-
-  // Header and body of the original loop.
-  MBasicBlock* header;
-  MBasicBlock* backedge;
-
-  // Header and body of the unrolled loop.
-  MBasicBlock* unrolledHeader;
-  MBasicBlock* unrolledBackedge;
-
-  // Old and new preheaders. The old preheader starts out associated with the
-  // original loop, but becomes the preheader of the new loop. The new
-  // preheader will be given to the original loop.
-  MBasicBlock* oldPreheader;
-  MBasicBlock* newPreheader;
-
-  // Map terms in the original loop to terms in the current unrolled iteration.
-  DefinitionMap unrolledDefinitions;
-
-  MDefinition* getReplacementDefinition(MDefinition* def);
-  MResumePoint* makeReplacementResumePoint(MBasicBlock* block,
-                                           MResumePoint* rp);
-  MOZ_MUST_USE bool makeReplacementInstruction(MInstruction* ins);
-
-  MOZ_MUST_USE bool go(LoopIterationBound* bound);
-};
-
-}  // namespace
-
-MDefinition* LoopUnroller::getReplacementDefinition(MDefinition* def) {
-  if (def->block()->id() < header->id()) {
-    // The definition is loop invariant.
-    return def;
-  }
-
-  DefinitionMap::Ptr p = unrolledDefinitions.lookup(def);
-  if (!p) {
-    // After phi analysis (TypeAnalyzer::replaceRedundantPhi) the resume
-    // point at the start of a block can contain definitions from within
-    // the block itself.
-    MOZ_ASSERT(def->isConstant());
-
-    MConstant* constant = MConstant::Copy(alloc, def->toConstant());
-    oldPreheader->insertBefore(*oldPreheader->begin(), constant);
-    return constant;
-  }
-
-  return p->value();
-}
-
-bool LoopUnroller::makeReplacementInstruction(MInstruction* ins) {
-  MDefinitionVector inputs(alloc);
-  for (size_t i = 0; i < ins->numOperands(); i++) {
-    MDefinition* old = ins->getOperand(i);
-    MDefinition* replacement = getReplacementDefinition(old);
-    if (!inputs.append(replacement)) {
-      return false;
-    }
-  }
-
-  MInstruction* clone = ins->clone(alloc, inputs);
-
-  unrolledBackedge->add(clone);
-
-  if (!unrolledDefinitions.putNew(ins, clone)) {
-    return false;
-  }
-
-  if (MResumePoint* old = ins->resumePoint()) {
-    MResumePoint* rp = makeReplacementResumePoint(unrolledBackedge, old);
-    if (!rp) {
-      return false;
-    }
-    clone->setResumePoint(rp);
-  }
-
-  return true;
-}
-
-MResumePoint* LoopUnroller::makeReplacementResumePoint(MBasicBlock* block,
-                                                       MResumePoint* rp) {
-  MDefinitionVector inputs(alloc);
-  for (size_t i = 0; i < rp->numOperands(); i++) {
-    MDefinition* old = rp->getOperand(i);
-    MDefinition* replacement =
-        old->isUnused() ? old : getReplacementDefinition(old);
-    if (!inputs.append(replacement)) {
-      return nullptr;
-    }
-  }
-
-  MResumePoint* clone = MResumePoint::New(alloc, block, rp, inputs);
-  if (!clone) {
-    return nullptr;
-  }
-
-  return clone;
-}
-
-bool LoopUnroller::go(LoopIterationBound* bound) {
-  // For now we always unroll loops the same number of times.
-  static const size_t UnrollCount = 10;
-
-  JitSpew(JitSpew_Unrolling, "Attempting to unroll loop");
-
-  header = bound->header;
-
-  // UCE might have determined this isn't actually a loop.
-  if (!header->isLoopHeader()) {
-    return true;
-  }
-
-  backedge = header->backedge();
-  oldPreheader = header->loopPredecessor();
-
-  MOZ_ASSERT(oldPreheader->numSuccessors() == 1);
-
-  // Only unroll loops with two blocks: an initial one ending with the
-  // bound's test, and the body ending with the backedge.
-  MTest* test = bound->test;
-  if (header->lastIns() != test) {
-    return true;
-  }
-  if (test->ifTrue() == backedge) {
-    if (test->ifFalse()->id() <= backedge->id()) {
-      return true;
-    }
-  } else if (test->ifFalse() == backedge) {
-    if (test->ifTrue()->id() <= backedge->id()) {
-      return true;
-    }
-  } else {
-    return true;
-  }
-  if (backedge->numPredecessors() != 1 || backedge->numSuccessors() != 1) {
-    return true;
-  }
-  MOZ_ASSERT(backedge->phisEmpty());
-
-  MBasicBlock* bodyBlocks[] = {header, backedge};
-
-  // All instructions in the header and body must be clonable.
-  for (size_t i = 0; i < ArrayLength(bodyBlocks); i++) {
-    MBasicBlock* block = bodyBlocks[i];
-    for (MInstructionIterator iter(block->begin()); iter != block->end();
-         iter++) {
-      MInstruction* ins = *iter;
-      if (ins->canClone()) {
-        continue;
-      }
-      if (ins->isTest() || ins->isGoto() || ins->isInterruptCheck()) {
-        continue;
-      }
-#ifdef JS_JITSPEW
-      JitSpew(JitSpew_Unrolling, "Aborting: can't clone instruction %s",
-              ins->opName());
-#endif
-      return true;
-    }
-  }
-
-  // Compute the linear inequality we will use for exiting the unrolled loop:
-  //
-  // iterationBound - iterationCount - UnrollCount >= 0
-  //
-  LinearSum remainingIterationsInequality(bound->boundSum);
-  if (!remainingIterationsInequality.add(bound->currentSum, -1)) {
-    return true;
-  }
-  if (!remainingIterationsInequality.add(-int32_t(UnrollCount))) {
-    return true;
-  }
-
-  // Terms in the inequality need to be either loop invariant or phis from
-  // the original header.
-  for (size_t i = 0; i < remainingIterationsInequality.numTerms(); i++) {
-    MDefinition* def = remainingIterationsInequality.term(i).term;
-    if (def->isDiscarded()) {
-      return true;
-    }
-    if (def->block()->id() < header->id()) {
-      continue;
-    }
-    if (def->block() == header && def->isPhi()) {
-      continue;
-    }
-    return true;
-  }
-
-  // OK, we've checked everything, now unroll the loop.
-
-  JitSpew(JitSpew_Unrolling, "Unrolling loop");
-
-  // The old preheader will go before the unrolled loop, and the old loop
-  // will need a new empty preheader.
-  const CompileInfo& info = oldPreheader->info();
-  if (header->trackedPc()) {
-    unrolledHeader =
-        MBasicBlock::New(graph, oldPreheader->stackDepth(), info, oldPreheader,
-                         header->trackedSite(), MBasicBlock::LOOP_HEADER);
-    if (!unrolledHeader) {
-      return false;
-    }
-    unrolledBackedge = MBasicBlock::New(
-        graph, unrolledHeader->stackDepth(), info, unrolledHeader,
-        backedge->trackedSite(), MBasicBlock::NORMAL);
-    if (!unrolledBackedge) {
-      return false;
-    }
-    newPreheader = MBasicBlock::New(graph, unrolledHeader->stackDepth(), info,
-                                    unrolledHeader, oldPreheader->trackedSite(),
-                                    MBasicBlock::NORMAL);
-    if (!newPreheader) {
-      return false;
-    }
-  } else {
-    unrolledHeader =
-        MBasicBlock::New(graph, info, oldPreheader, MBasicBlock::LOOP_HEADER);
-    if (!unrolledHeader) {
-      return false;
-    }
-    unrolledBackedge =
-        MBasicBlock::New(graph, info, unrolledHeader, MBasicBlock::NORMAL);
-    if (!unrolledBackedge) {
-      return false;
-    }
-    newPreheader =
-        MBasicBlock::New(graph, info, unrolledHeader, MBasicBlock::NORMAL);
-    if (!newPreheader) {
-      return false;
-    }
-  }
-
-  unrolledHeader->discardAllResumePoints();
-  unrolledBackedge->discardAllResumePoints();
-  newPreheader->discardAllResumePoints();
-
-  // Insert new blocks at their RPO position, and update block ids.
-  graph.insertBlockAfter(oldPreheader, unrolledHeader);
-  graph.insertBlockAfter(unrolledHeader, unrolledBackedge);
-  graph.insertBlockAfter(unrolledBackedge, newPreheader);
-  graph.renumberBlocksAfter(oldPreheader);
-
-  // Add phis to the unrolled loop header which correspond to the phis in the
-  // original loop header.
-  MOZ_ASSERT(header->getPredecessor(0) == oldPreheader);
-  for (MPhiIterator iter(header->phisBegin()); iter != header->phisEnd();
-       iter++) {
-    MPhi* old = *iter;
-    MOZ_ASSERT(old->numOperands() == 2);
-    MPhi* phi = MPhi::New(alloc);
-    phi->setResultType(old->type());
-    phi->setResultTypeSet(old->resultTypeSet());
-    phi->setRange(old->range());
-
-    unrolledHeader->addPhi(phi);
-
-    if (!phi->reserveLength(2)) {
-      return false;
-    }
-
-    // Set the first input for the phi for now. We'll set the second after
-    // finishing the unroll.
-    phi->addInput(old->getOperand(0));
-
-    // The old phi will now take the value produced by the unrolled loop.
-    old->replaceOperand(0, phi);
-
-    if (!unrolledDefinitions.putNew(old, phi)) {
-      return false;
-    }
-  }
-
-  // The loop condition can bail out on e.g. integer overflow, so make a
-  // resume point based on the initial resume point of the original header.
-  MResumePoint* headerResumePoint = header->entryResumePoint();
-  if (headerResumePoint) {
-    MResumePoint* rp =
-        makeReplacementResumePoint(unrolledHeader, headerResumePoint);
-    if (!rp) {
-      return false;
-    }
-    unrolledHeader->setEntryResumePoint(rp);
-
-    // Perform an interrupt check at the start of the unrolled loop.
-    unrolledHeader->add(MInterruptCheck::New(alloc));
-  }
-
-  // Generate code for the test in the unrolled loop.
-  for (size_t i = 0; i < remainingIterationsInequality.numTerms(); i++) {
-    MDefinition* def = remainingIterationsInequality.term(i).term;
-    MDefinition* replacement = getReplacementDefinition(def);
-    remainingIterationsInequality.replaceTerm(i, replacement);
-  }
-  MCompare* compare = ConvertLinearInequality(alloc, unrolledHeader,
-                                              remainingIterationsInequality);
-  MTest* unrolledTest =
-      MTest::New(alloc, compare, unrolledBackedge, newPreheader);
-  unrolledHeader->end(unrolledTest);
-
-  // Make an entry resume point for the unrolled body. The unrolled header
-  // does not have side effects on stack values, even if the original loop
-  // header does, so use the same resume point as for the unrolled header.
-  if (headerResumePoint) {
-    MResumePoint* rp =
-        makeReplacementResumePoint(unrolledBackedge, headerResumePoint);
-    if (!rp) {
-      return false;
-    }
-    unrolledBackedge->setEntryResumePoint(rp);
-  }
-
-  // Make an entry resume point for the new preheader. There are no
-  // instructions which use this but some other stuff wants one to be here.
-  if (headerResumePoint) {
-    MResumePoint* rp =
-        makeReplacementResumePoint(newPreheader, headerResumePoint);
-    if (!rp) {
-      return false;
-    }
-    newPreheader->setEntryResumePoint(rp);
-  }
-
-  // Generate the unrolled code.
-  MOZ_ASSERT(UnrollCount > 1);
-  size_t unrollIndex = 0;
-  while (true) {
-    // Clone the contents of the original loop into the unrolled loop body.
-    for (size_t i = 0; i < ArrayLength(bodyBlocks); i++) {
-      MBasicBlock* block = bodyBlocks[i];
-      for (MInstructionIterator iter(block->begin()); iter != block->end();
-           iter++) {
-        MInstruction* ins = *iter;
-        if (ins->canClone()) {
-          if (!makeReplacementInstruction(*iter)) {
-            return false;
-          }
-        } else {
-          // Control instructions are handled separately.
-          MOZ_ASSERT(ins->isTest() || ins->isGoto() || ins->isInterruptCheck());
-        }
-      }
-    }
-
-    // Compute the value of each loop header phi after the execution of
-    // this unrolled iteration.
-    MDefinitionVector phiValues(alloc);
-    MOZ_ASSERT(header->getPredecessor(1) == backedge);
-    for (MPhiIterator iter(header->phisBegin()); iter != header->phisEnd();
-         iter++) {
-      MPhi* old = *iter;
-      MDefinition* oldInput = old->getOperand(1);
-      if (!phiValues.append(getReplacementDefinition(oldInput))) {
-        return false;
-      }
-    }
-
-    unrolledDefinitions.clear();
-
-    if (unrollIndex == UnrollCount - 1) {
-      // We're at the end of the last unrolled iteration, set the
-      // backedge input for the unrolled loop phis.
-      size_t phiIndex = 0;
-      for (MPhiIterator iter(unrolledHeader->phisBegin());
-           iter != unrolledHeader->phisEnd(); iter++) {
-        MPhi* phi = *iter;
-        phi->addInput(phiValues[phiIndex++]);
-      }
-      MOZ_ASSERT(phiIndex == phiValues.length());
-      break;
-    }
-
-    // Update the map for the phis in the next iteration.
-    size_t phiIndex = 0;
-    for (MPhiIterator iter(header->phisBegin()); iter != header->phisEnd();
-         iter++) {
-      MPhi* old = *iter;
-      if (!unrolledDefinitions.putNew(old, phiValues[phiIndex++])) {
-        return false;
-      }
-    }
-    MOZ_ASSERT(phiIndex == phiValues.length());
-
-    unrollIndex++;
-  }
-
-  MGoto* backedgeJump = MGoto::New(alloc, unrolledHeader);
-  unrolledBackedge->end(backedgeJump);
-
-  // Place the old preheader before the unrolled loop.
-  MOZ_ASSERT(oldPreheader->lastIns()->isGoto());
-  oldPreheader->discardLastIns();
-  oldPreheader->end(MGoto::New(alloc, unrolledHeader));
-
-  // Place the new preheader before the original loop.
-  newPreheader->end(MGoto::New(alloc, header));
-
-  // Cleanup the MIR graph.
-  if (!unrolledHeader->addPredecessorWithoutPhis(unrolledBackedge)) {
-    return false;
-  }
-  header->replacePredecessor(oldPreheader, newPreheader);
-  oldPreheader->setSuccessorWithPhis(unrolledHeader, 0);
-  newPreheader->setSuccessorWithPhis(header, 0);
-  unrolledBackedge->setSuccessorWithPhis(unrolledHeader, 1);
-  return true;
-}
-
-bool jit::UnrollLoops(MIRGraph& graph, const LoopIterationBoundVector& bounds) {
-  if (bounds.empty()) {
-    return true;
-  }
-
-  // Loop unrolling interferes with the progress tracking performed when
-  // recording/replaying.
-  if (graph.entryBlock()->info().script()->trackRecordReplayProgress()) {
-    return true;
-  }
-
-  for (size_t i = 0; i < bounds.length(); i++) {
-    LoopUnroller unroller(graph);
-    if (!unroller.go(bounds[i])) {
-      return false;
-    }
-  }
-
-  // The MIR graph is valid, but now has several new blocks. Update or
-  // recompute previous analysis information for the remaining optimization
-  // passes.
-  ClearDominatorTree(graph);
-  if (!BuildDominatorTree(graph)) {
-    return false;
-  }
-
-  return true;
-}
deleted file mode 100644
--- a/js/src/jit/LoopUnroller.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: set ts=8 sts=2 et sw=2 tw=80:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef jit_LoopUnroller_h
-#define jit_LoopUnroller_h
-
-#include "jit/RangeAnalysis.h"
-
-namespace js {
-namespace jit {
-
-MOZ_MUST_USE bool UnrollLoops(MIRGraph& graph,
-                              const LoopIterationBoundVector& bounds);
-
-}  // namespace jit
-}  // namespace js
-
-#endif  // jit_LoopUnroller_h
--- a/js/src/jit/moz.build
+++ b/js/src/jit/moz.build
@@ -70,17 +70,16 @@ UNIFIED_SOURCES += [
     'JitFrames.cpp',
     'JitOptions.cpp',
     'JitSpewer.cpp',
     'JSJitFrameIter.cpp',
     'JSONSpewer.cpp',
     'LICM.cpp',
     'Linker.cpp',
     'LIR.cpp',
-    'LoopUnroller.cpp',
     'Lowering.cpp',
     'MacroAssembler.cpp',
     'MCallOptimize.cpp',
     'MIR.cpp',
     'MIRGraph.cpp',
     'MoveResolver.cpp',
     'OptimizationTracking.cpp',
     'PerfSpewer.cpp',
@@ -232,9 +231,9 @@ elif CONFIG['JS_CODEGEN_MIPS32'] or CONF
             'mips64/Lowering-mips64.cpp',
             'mips64/MacroAssembler-mips64.cpp',
             'mips64/MoveEmitter-mips64.cpp',
             'mips64/Trampoline-mips64.cpp',
         ]
         if CONFIG['JS_SIMULATOR_MIPS64']:
             UNIFIED_SOURCES += [
                 'mips64/Simulator-mips64.cpp'
-            ]
\ No newline at end of file
+            ]
--- a/js/src/shell/fuzz-flags.txt
+++ b/js/src/shell/fuzz-flags.txt
@@ -16,18 +16,16 @@
 --ion-inlining=off
 --ion-inlining=on
 --ion-instruction-reordering=off
 --ion-instruction-reordering=on
 --ion-licm=off
 --ion-licm=on
 --ion-limit-script-size=off
 --ion-limit-script-size=on
---ion-loop-unrolling=off
---ion-loop-unrolling=on
 --ion-offthread-compile=off
 --ion-osr=off
 --ion-osr=on
 --ion-pgo=off
 --ion-pgo=on
 --ion-range-analysis=off
 --ion-range-analysis=on
 --ion-regalloc=testbed
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -10344,26 +10344,16 @@ static bool SetContextOptions(JSContext*
       jit::JitOptions.disableSink = false;
     } else if (strcmp(str, "off") == 0) {
       jit::JitOptions.disableSink = true;
     } else {
       return OptionFailure("ion-sink", str);
     }
   }
 
-  if (const char* str = op.getStringOption("ion-loop-unrolling")) {
-    if (strcmp(str, "on") == 0) {
-      jit::JitOptions.disableLoopUnrolling = false;
-    } else if (strcmp(str, "off") == 0) {
-      jit::JitOptions.disableLoopUnrolling = true;
-    } else {
-      return OptionFailure("ion-loop-unrolling", str);
-    }
-  }
-
   if (const char* str = op.getStringOption("ion-instruction-reordering")) {
     if (strcmp(str, "on") == 0) {
       jit::JitOptions.disableInstructionReordering = false;
     } else if (strcmp(str, "off") == 0) {
       jit::JitOptions.disableInstructionReordering = true;
     } else {
       return OptionFailure("ion-instruction-reordering", str);
     }
@@ -11012,17 +11002,17 @@ int main(int argc, char** argv, char** e
 #else
       || !op.addStringOption(
              '\0', "ion-sincos", "on/off",
              "Replace sin(x)/cos(x) to sincos(x) (default: off, on to enable)")
 #endif
       || !op.addStringOption('\0', "ion-sink", "on/off",
                              "Sink code motion (default: off, on to enable)") ||
       !op.addStringOption('\0', "ion-loop-unrolling", "on/off",
-                          "Loop unrolling (default: off, on to enable)") ||
+                          "(NOP for fuzzers)") ||
       !op.addStringOption(
           '\0', "ion-instruction-reordering", "on/off",
           "Instruction reordering (default: off, on to enable)") ||
       !op.addBoolOption('\0', "ion-check-range-analysis",
                         "Range analysis checking") ||
       !op.addBoolOption('\0', "ion-extra-checks",
                         "Perform extra dynamic validation checks") ||
       !op.addStringOption(