text: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
   * vim: set ts=8 sts=4 et sw=4 tw=99:
   * 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/MIRGraph.h"
  
  #include "asmjs/AsmJSValidate.h"
  #include "jit/BytecodeAnalysis.h"
  #include "jit/Ion.h"
  #include "jit/JitSpewer.h"
  #include "jit/MIR.h"
  #include "jit/MIRGenerator.h"
  
  using namespace js;
  using namespace js::jit;
  using mozilla::Swap;
  
  MIRGenerator::MIRGenerator(CompileCompartment* compartment, const JitCompileOptions& options,
                             TempAllocator* alloc, MIRGraph* graph, const CompileInfo* info,
                             const OptimizationInfo* optimizationInfo,
                             bool usesSignalHandlersForAsmJSOOB)
    : compartment(compartment),
      info_(info),
      optimizationInfo_(optimizationInfo),
      alloc_(alloc),
      graph_(graph),
      abortReason_(AbortReason_NoAbort),
      shouldForceAbort_(false),
      abortedPreliminaryGroups_(*alloc_),
      error_(false),
      pauseBuild_(nullptr),
      cancelBuild_(false),
      maxAsmJSStackArgBytes_(0),
      performsCall_(false),
      usesSimd_(false),
      usesSimdCached_(false),
      modifiesFrameArguments_(false),
      instrumentedProfiling_(false),
      instrumentedProfilingIsCached_(false),
      safeForMinorGC_(true),
  #if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
      usesSignalHandlersForAsmJSOOB_(usesSignalHandlersForAsmJSOOB),
  #endif
      options(options),
      gs_(alloc)
  { }
  
  bool
  MIRGenerator::usesSimd()
  {
      if (usesSimdCached_)
          return usesSimd_;
  
      usesSimdCached_ = true;
      for (ReversePostorderIterator block = graph_->rpoBegin(),
                                    end   = graph_->rpoEnd();
           block != end;
           block++)
      {
          // It's fine to use MInstructionIterator here because we don't have to
          // worry about Phis, since any reachable phi (or phi cycle) will have at
          // least one instruction as an input.
          for (MInstructionIterator inst = block->begin(); inst != block->end(); inst++) {
              // Instructions that have SIMD inputs but not a SIMD type are fine
              // to ignore, as their inputs are also reached at some point. By
              // induction, at least one instruction with a SIMD type is reached
              // at some point.
              if (IsSimdType(inst->type())) {
                  MOZ_ASSERT(SupportsSimd);
                  usesSimd_ = true;
                  return true;
              }
          }
      }
      usesSimd_ = false;
      return false;
  }
  
  bool
  MIRGenerator::abortFmt(const char* message, va_list ap)
  {
      JitSpewVA(JitSpew_IonAbort, message, ap);
      error_ = true;
      return false;
  }
  
  bool
  MIRGenerator::abort(const char* message, ...)
  {
      va_list ap;
      va_start(ap, message);
      abortFmt(message, ap);
      va_end(ap);
      return false;
  }
  
  void
  MIRGenerator::addAbortedPreliminaryGroup(ObjectGroup* group)
  {
      for (size_t i = 0; i < abortedPreliminaryGroups_.length(); i++) {
          if (group == abortedPreliminaryGroups_[i])
              return;
      }
      AutoEnterOOMUnsafeRegion oomUnsafe;
      if (!abortedPreliminaryGroups_.append(group))
          oomUnsafe.crash("addAbortedPreliminaryGroup");
  }
  
  bool
  MIRGenerator::needsAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* access) const
  {
      // A heap access needs a bounds-check branch if we're not relying on signal
      // handlers to catch errors, and if it's not proven to be within bounds.
      // We use signal-handlers on x64, but on x86 there isn't enough address
      // space for a guard region.  Also, on x64 the atomic loads and stores
      // can't (yet) use the signal handlers.
  #if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
      if (usesSignalHandlersForAsmJSOOB_ && !access->isAtomicAccess())
          return false;
  #endif
      return access->needsBoundsCheck();
  }
  
  size_t
  MIRGenerator::foldableOffsetRange(const MAsmJSHeapAccess* access) const
  {
      // This determines whether it's ok to fold up to AsmJSImmediateSize
      // offsets, instead of just AsmJSCheckedImmediateSize.
  
  #if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
      // With signal-handler OOB handling, we reserve guard space for the full
      // immediate size.
      if (usesSignalHandlersForAsmJSOOB_)
          return AsmJSImmediateRange;
  #endif
  
      // On 32-bit platforms, if we've proven the access is in bounds after
      // 32-bit wrapping, we can fold full offsets because they're added with
      // 32-bit arithmetic.
      if (sizeof(intptr_t) == sizeof(int32_t) && !access->needsBoundsCheck())
          return AsmJSImmediateRange;
  
      // Otherwise, only allow the checked size. This is always less than the
      // minimum heap length, and allows explicit bounds checks to fold in the
      // offset without overflow.
      return AsmJSCheckedImmediateRange;
  }
  
  void
  MIRGraph::addBlock(MBasicBlock* block)
  {
      MOZ_ASSERT(block);
      block->setId(blockIdGen_++);
      blocks_.pushBack(block);
      numBlocks_++;
  }
  
  void
  MIRGraph::insertBlockAfter(MBasicBlock* at, MBasicBlock* block)
  {
      block->setId(blockIdGen_++);
      blocks_.insertAfter(at, block);
      numBlocks_++;
  }
  
  void
  MIRGraph::insertBlockBefore(MBasicBlock* at, MBasicBlock* block)
  {
      block->setId(blockIdGen_++);
      blocks_.insertBefore(at, block);
      numBlocks_++;
  }
  
  void
  MIRGraph::renumberBlocksAfter(MBasicBlock* at)
  {
      MBasicBlockIterator iter = begin(at);
      iter++;
  
      uint32_t id = at->id();
      for (; iter != end(); iter++)
          iter->setId(++id);
  }
  
  void
  MIRGraph::removeBlocksAfter(MBasicBlock* start)
  {
      MBasicBlockIterator iter(begin());
      iter++;
      while (iter != end()) {
          MBasicBlock* block = *iter;
          iter++;
  
          if (block->id() <= start->id())
              continue;
  
          removeBlock(block);
      }
  }
  
  void
  MIRGraph::removeBlock(MBasicBlock* block)
  {
      // Remove a block from the graph. It will also cleanup the block.
  
      if (block == osrBlock_)
          osrBlock_ = nullptr;
  
      if (returnAccumulator_) {
          size_t i = 0;
          while (i < returnAccumulator_->length()) {
              if ((*returnAccumulator_)[i] == block)
                  returnAccumulator_->erase(returnAccumulator_->begin() + i);
              else
                  i++;
          }
      }
  
      block->discardAllInstructions();
      block->discardAllResumePoints();
  
      // Note: phis are disconnected from the rest of the graph, but are not
      // removed entirely. If the block being removed is a loop header then
      // IonBuilder may need to access these phis to more quickly converge on the
      // possible types in the graph. See IonBuilder::analyzeNewLoopTypes.
      block->discardAllPhiOperands();
  
      block->markAsDead();
      blocks_.remove(block);
      numBlocks_--;
  }
  
  void
  MIRGraph::removeBlockIncludingPhis(MBasicBlock* block)
  {
      // removeBlock doesn't clear phis because of IonBuilder constraints. Here,
      // we want to totally clear everything.
      removeBlock(block);
      block->discardAllPhis();
  }
  
  void
  MIRGraph::unmarkBlocks()
  {
      for (MBasicBlockIterator i(blocks_.begin()); i != blocks_.end(); i++)
          i->unmark();
  }
  
  MBasicBlock*
  MBasicBlock::New(MIRGraph& graph, BytecodeAnalysis* analysis, const CompileInfo& info,
                   MBasicBlock* pred, BytecodeSite* site, Kind kind)
  {
      MOZ_ASSERT(site->pc() != nullptr);
  
      MBasicBlock* block = new(graph.alloc()) MBasicBlock(graph, info, site, kind);
      if (!block->init())
          return nullptr;
  
      if (!block->inherit(graph.alloc(), analysis, pred, 0))
          return nullptr;
  
      return block;
  }
  
  MBasicBlock*
  MBasicBlock::NewPopN(MIRGraph& graph, const CompileInfo& info,
                       MBasicBlock* pred, BytecodeSite* site, Kind kind, uint32_t popped)
  {
      MBasicBlock* block = new(graph.alloc()) MBasicBlock(graph, info, site, kind);
      if (!block->init())
          return nullptr;
  
      if (!block->inherit(graph.alloc(), nullptr, pred, popped))
          return nullptr;
  
      return block;
  }
  
  MBasicBlock*
  MBasicBlock::NewWithResumePoint(MIRGraph& graph, const CompileInfo& info,
                                  MBasicBlock* pred, BytecodeSite* site,
                                  MResumePoint* resumePoint)
  {
      MBasicBlock* block = new(graph.alloc()) MBasicBlock(graph, info, site, NORMAL);
  
      MOZ_ASSERT(!resumePoint->instruction());
      resumePoint->block()->discardResumePoint(resumePoint, RefType_None);
      resumePoint->block_ = block;
      block->addResumePoint(resumePoint);
      block->entryResumePoint_ = resumePoint;
  
      if (!block->init())
          return nullptr;
  
      if (!block->inheritResumePoint(pred))
          return nullptr;
  
      return block;
  }
  
  MBasicBlock*
  MBasicBlock::NewPendingLoopHeader(MIRGraph& graph, const CompileInfo& info,
                                    MBasicBlock* pred, BytecodeSite* site,
                                    unsigned stackPhiCount)
  {
      MOZ_ASSERT(site->pc() != nullptr);
  
      MBasicBlock* block = new(graph.alloc()) MBasicBlock(graph, info, site, PENDING_LOOP_HEADER);
      if (!block->init())
          return nullptr;
  
      if (!block->inherit(graph.alloc(), nullptr, pred, 0, stackPhiCount))
          return nullptr;
  
      return block;
  }
  
  MBasicBlock*
  MBasicBlock::NewSplitEdge(MIRGraph& graph, const CompileInfo& info, MBasicBlock* pred)
  {
      return pred->pc()
             ? MBasicBlock::New(graph, nullptr, info, pred,
                                new(graph.alloc()) BytecodeSite(pred->trackedTree(), pred->pc()),
                                SPLIT_EDGE)
             : MBasicBlock::NewAsmJS(graph, info, pred, SPLIT_EDGE);
  }
  
  MBasicBlock*
  MBasicBlock::NewAsmJS(MIRGraph& graph, const CompileInfo& info, MBasicBlock* pred, Kind kind)
  {
      BytecodeSite* site = new(graph.alloc()) BytecodeSite();
      MBasicBlock* block = new(graph.alloc()) MBasicBlock(graph, info, site, kind);
      if (!block->init())
          return nullptr;
  
      if (pred) {
          block->stackPosition_ = pred->stackPosition_;
  
          if (block->kind_ == PENDING_LOOP_HEADER) {
              size_t nphis = block->stackPosition_;
  
              size_t nfree = graph.phiFreeListLength();
  
              TempAllocator& alloc = graph.alloc();
              MPhi* phis = nullptr;
              if (nphis > nfree) {
                  phis = alloc.allocateArray<MPhi>(nphis - nfree);
                  if (!phis)
                      return nullptr;
              }
  
              // Note: Phis are inserted in the same order as the slots.
              for (size_t i = 0; i < nphis; i++) {
                  MDefinition* predSlot = pred->getSlot(i);
  
                  MOZ_ASSERT(predSlot->type() != MIRType_Value);
  
                  MPhi* phi;
                  if (i < nfree)
                      phi = graph.takePhiFromFreeList();
                  else
                      phi = phis + (i - nfree);
                  new(phi) MPhi(alloc, predSlot->type());
  
                  phi->addInput(predSlot);
  
                  // Add append Phis in the block.
                  block->addPhi(phi);
                  block->setSlot(i, phi);
              }
          } else {
              block->copySlots(pred);
          }
  
          if (!block->predecessors_.append(pred))
              return nullptr;
      }
  
      return block;
  }
  
  MBasicBlock::MBasicBlock(MIRGraph& graph, const CompileInfo& info, BytecodeSite* site, Kind kind)
    : unreachable_(false),
      graph_(graph),
      info_(info),
      predecessors_(graph.alloc()),
      stackPosition_(info_.firstStackSlot()),
      numDominated_(0),
      pc_(site->pc()),
      lir_(nullptr),
      callerResumePoint_(nullptr),
      entryResumePoint_(nullptr),
      outerResumePoint_(nullptr),
      successorWithPhis_(nullptr),
      positionInPhiSuccessor_(0),
      loopDepth_(0),
      kind_(kind),
      mark_(false),
      immediatelyDominated_(graph.alloc()),
      immediateDominator_(nullptr),
      trackedSite_(site),
      hitCount_(0),
      hitState_(HitState::NotDefined)
  #if defined (JS_ION_PERF)
      , lineno_(0u),
      columnIndex_(0u)
  #endif
  {
  }
  
  bool
  MBasicBlock::init()
  {
      return slots_.init(graph_.alloc(), info_.nslots());
  }
  
  bool
  MBasicBlock::increaseSlots(size_t num)
  {
      return slots_.growBy(graph_.alloc(), num);
  }
  
  bool
  MBasicBlock::ensureHasSlots(size_t num)
  {
      size_t depth = stackDepth() + num;
      if (depth > nslots()) {
          if (!increaseSlots(depth - nslots()))
              return false;
      }
      return true;
  }
  
  void
  MBasicBlock::copySlots(MBasicBlock* from)
  {
      MOZ_ASSERT(stackPosition_ <= from->stackPosition_);
  
      MDefinition** thisSlots = slots_.begin();
      MDefinition** fromSlots = from->slots_.begin();
      for (size_t i = 0, e = stackPosition_; i < e; ++i)
          thisSlots[i] = fromSlots[i];
  }
  
  bool
  MBasicBlock::inherit(TempAllocator& alloc, BytecodeAnalysis* analysis, MBasicBlock* pred,
                       uint32_t popped, unsigned stackPhiCount)
  {
      if (pred) {
          stackPosition_ = pred->stackPosition_;
          MOZ_ASSERT(stackPosition_ >= popped);
          stackPosition_ -= popped;
          if (kind_ != PENDING_LOOP_HEADER)
              copySlots(pred);
      } else {
          uint32_t stackDepth = analysis->info(pc()).stackDepth;
          stackPosition_ = info().firstStackSlot() + stackDepth;
          MOZ_ASSERT(stackPosition_ >= popped);
          stackPosition_ -= popped;
      }
  
      MOZ_ASSERT(info_.nslots() >= stackPosition_);
      MOZ_ASSERT(!entryResumePoint_);
  
      // Propagate the caller resume point from the inherited block.
      callerResumePoint_ = pred ? pred->callerResumePoint() : nullptr;
  
      // Create a resume point using our initial stack state.
      entryResumePoint_ = new(alloc) MResumePoint(this, pc(), MResumePoint::ResumeAt);
      if (!entryResumePoint_->init(alloc))
          return false;
  
      if (pred) {
          if (!predecessors_.append(pred))
              return false;
  
          if (kind_ == PENDING_LOOP_HEADER) {
              size_t i = 0;
              for (i = 0; i < info().firstStackSlot(); i++) {
                  MPhi* phi = MPhi::New(alloc);
                  phi->addInput(pred->getSlot(i));
                  addPhi(phi);
                  setSlot(i, phi);
                  entryResumePoint()->initOperand(i, phi);
              }
  
              MOZ_ASSERT(stackPhiCount <= stackDepth());
              MOZ_ASSERT(info().firstStackSlot() <= stackDepth() - stackPhiCount);
  
              // Avoid creating new phis for stack values that aren't part of the
              // loop.  Note that for loop headers that can OSR, all values on the
              // stack are part of the loop.
              for (; i < stackDepth() - stackPhiCount; i++) {
                  MDefinition* val = pred->getSlot(i);
                  setSlot(i, val);
                  entryResumePoint()->initOperand(i, val);
              }
  
              for (; i < stackDepth(); i++) {
                  MPhi* phi = MPhi::New(alloc);
                  phi->addInput(pred->getSlot(i));
                  addPhi(phi);
                  setSlot(i, phi);
                  entryResumePoint()->initOperand(i, phi);
              }
          } else {
              for (size_t i = 0; i < stackDepth(); i++)
                  entryResumePoint()->initOperand(i, getSlot(i));
          }
      } else {
          /*
           * Don't leave the operands uninitialized for the caller, as it may not
           * initialize them later on.
           */
          for (size_t i = 0; i < stackDepth(); i++)
              entryResumePoint()->clearOperand(i);
      }
  
      return true;
  }
  
  bool
  MBasicBlock::inheritResumePoint(MBasicBlock* pred)
  {
      // Copy slots from the resume point.
      stackPosition_ = entryResumePoint_->stackDepth();
      for (uint32_t i = 0; i < stackPosition_; i++)
          slots_[i] = entryResumePoint_->getOperand(i);
  
      MOZ_ASSERT(info_.nslots() >= stackPosition_);
      MOZ_ASSERT(kind_ != PENDING_LOOP_HEADER);
      MOZ_ASSERT(pred != nullptr);
  
      callerResumePoint_ = pred->callerResumePoint();
  
      if (!predecessors_.append(pred))
          return false;
  
      return true;
  }
  
  void
  MBasicBlock::inheritSlots(MBasicBlock* parent)
  {
      stackPosition_ = parent->stackPosition_;
      copySlots(parent);
  }
  
  bool
  MBasicBlock::initEntrySlots(TempAllocator& alloc)
  {
      // Remove the previous resume point.
      discardResumePoint(entryResumePoint_);
  
      // Create a resume point using our initial stack state.
      entryResumePoint_ = MResumePoint::New(alloc, this, pc(), MResumePoint::ResumeAt);
      if (!entryResumePoint_)
          return false;
      return true;
  }
  
  MDefinition*
  MBasicBlock::getSlot(uint32_t index)
  {
      MOZ_ASSERT(index < stackPosition_);
      return slots_[index];
  }
  
  void
  MBasicBlock::initSlot(uint32_t slot, MDefinition* ins)
  {
      slots_[slot] = ins;
      if (entryResumePoint())
          entryResumePoint()->initOperand(slot, ins);
  }
  
  void
  MBasicBlock::shimmySlots(int discardDepth)
  {
      // Move all slots above the given depth down by one,
      // overwriting the MDefinition at discardDepth.
  
      MOZ_ASSERT(discardDepth < 0);
      MOZ_ASSERT(stackPosition_ + discardDepth >= info_.firstStackSlot());
  
      for (int i = discardDepth; i < -1; i++)
          slots_[stackPosition_ + i] = slots_[stackPosition_ + i + 1];
  
      --stackPosition_;
  }
  
  bool
  MBasicBlock::linkOsrValues(MStart* start)
  {
      MOZ_ASSERT(start->startType() == MStart::StartType_Osr);
  
      MResumePoint* res = start->resumePoint();
  
      for (uint32_t i = 0; i < stackDepth(); i++) {
          MDefinition* def = slots_[i];
          MInstruction* cloneRp = nullptr;
          if (i == info().scopeChainSlot()) {
              if (def->isOsrScopeChain())
                  cloneRp = def->toOsrScopeChain();
          } else if (i == info().returnValueSlot()) {
              if (def->isOsrReturnValue())
                  cloneRp = def->toOsrReturnValue();
          } else if (info().hasArguments() && i == info().argsObjSlot()) {
              MOZ_ASSERT(def->isConstant() || def->isOsrArgumentsObject());
              MOZ_ASSERT_IF(def->isConstant(), def->toConstant()->value() == UndefinedValue());
              if (def->isOsrArgumentsObject())
                  cloneRp = def->toOsrArgumentsObject();
          } else {
              MOZ_ASSERT(def->isOsrValue() || def->isGetArgumentsObjectArg() || def->isConstant() ||
                         def->isParameter());
  
              // A constant Undefined can show up here for an argument slot when
              // the function has an arguments object, but the argument in
              // question is stored on the scope chain.
              MOZ_ASSERT_IF(def->isConstant(), def->toConstant()->value() == UndefinedValue());
  
              if (def->isOsrValue())
                  cloneRp = def->toOsrValue();
              else if (def->isGetArgumentsObjectArg())
                  cloneRp = def->toGetArgumentsObjectArg();
              else if (def->isParameter())
                  cloneRp = def->toParameter();
          }
  
          if (cloneRp) {
              MResumePoint* clone = MResumePoint::Copy(graph().alloc(), res);
              if (!clone)
                  return false;
              cloneRp->setResumePoint(clone);
          }
      }
  
      return true;
  }
  
  void
  MBasicBlock::setSlot(uint32_t slot, MDefinition* ins)
  {
      slots_[slot] = ins;
  }
  
  void
  MBasicBlock::setVariable(uint32_t index)
  {
      MOZ_ASSERT(stackPosition_ > info_.firstStackSlot());
      setSlot(index, slots_[stackPosition_ - 1]);
  }
  
  void
  MBasicBlock::setArg(uint32_t arg)
  {
      setVariable(info_.argSlot(arg));
  }
  
  void
  MBasicBlock::setLocal(uint32_t local)
  {
      setVariable(info_.localSlot(local));
  }
  
  void
  MBasicBlock::setSlot(uint32_t slot)
  {
      setVariable(slot);
  }
  
  void
  MBasicBlock::rewriteSlot(uint32_t slot, MDefinition* ins)
  {
      setSlot(slot, ins);
  }
  
  void
  MBasicBlock::rewriteAtDepth(int32_t depth, MDefinition* ins)
  {
      MOZ_ASSERT(depth < 0);
      MOZ_ASSERT(stackPosition_ + depth >= info_.firstStackSlot());
      rewriteSlot(stackPosition_ + depth, ins);
  }
  
  void
  MBasicBlock::push(MDefinition* ins)
  {
      MOZ_ASSERT(stackPosition_ < nslots());
      slots_[stackPosition_++] = ins;
  }
  
  void
  MBasicBlock::pushVariable(uint32_t slot)
  {
      push(slots_[slot]);
  }
  
  void
  MBasicBlock::pushArg(uint32_t arg)
  {
      pushVariable(info_.argSlot(arg));
  }
  
  void
  MBasicBlock::pushLocal(uint32_t local)
  {
      pushVariable(info_.localSlot(local));
  }
  
  void
  MBasicBlock::pushSlot(uint32_t slot)
  {
      pushVariable(slot);
  }
  
  MDefinition*
  MBasicBlock::pop()
  {
      MOZ_ASSERT(stackPosition_ > info_.firstStackSlot());
      return slots_[--stackPosition_];
  }
  
  void
  MBasicBlock::popn(uint32_t n)
  {
      MOZ_ASSERT(stackPosition_ - n >= info_.firstStackSlot());
      MOZ_ASSERT(stackPosition_ >= stackPosition_ - n);
      stackPosition_ -= n;
  }
  
  MDefinition*
  MBasicBlock::scopeChain()
  {
      return getSlot(info().scopeChainSlot());
  }
  
  MDefinition*
  MBasicBlock::argumentsObject()
  {
      return getSlot(info().argsObjSlot());
  }
  
  void
  MBasicBlock::setScopeChain(MDefinition* scopeObj)
  {
      setSlot(info().scopeChainSlot(), scopeObj);
  }
  
  void
  MBasicBlock::setArgumentsObject(MDefinition* argsObj)
  {
      setSlot(info().argsObjSlot(), argsObj);
  }
  
  void
  MBasicBlock::pick(int32_t depth)
  {
      // pick take an element and move it to the top.
      // pick(-2):
      //   A B C D E
      //   A B D C E [ swapAt(-2) ]
      //   A B D E C [ swapAt(-1) ]
      for (; depth < 0; depth++)
          swapAt(depth);
  }
  
  void
  MBasicBlock::swapAt(int32_t depth)
  {
      uint32_t lhsDepth = stackPosition_ + depth - 1;
      uint32_t rhsDepth = stackPosition_ + depth;
  
      MDefinition* temp = slots_[lhsDepth];
      slots_[lhsDepth] = slots_[rhsDepth];
      slots_[rhsDepth] = temp;
  }
  
  MDefinition*
  MBasicBlock::peek(int32_t depth)
  {
      MOZ_ASSERT(depth < 0);
      MOZ_ASSERT(stackPosition_ + depth >= info_.firstStackSlot());
      return getSlot(stackPosition_ + depth);
  }
  
  void
  MBasicBlock::discardLastIns()
  {
      discard(lastIns());
  }
  
  MConstant*
  MBasicBlock::optimizedOutConstant(TempAllocator& alloc)
  {
      // If the first instruction is a MConstant(MagicValue(JS_OPTIMIZED_OUT))
      // then reuse it.
      MInstruction* ins = *begin();
      if (ins->type() == MIRType_MagicOptimizedOut)
          return ins->toConstant();
  
      MConstant* constant = MConstant::New(alloc, MagicValue(JS_OPTIMIZED_OUT));
      insertBefore(ins, constant);
      return constant;
  }
  
  void
  MBasicBlock::addFromElsewhere(MInstruction* ins)
  {
      MOZ_ASSERT(ins->block() != this);
  
      // Remove |ins| from its containing block.
      ins->block()->instructions_.remove(ins);
  
      // Add it to this block.
      add(ins);
  }
  
  void
  MBasicBlock::moveBefore(MInstruction* at, MInstruction* ins)
  {
      // Remove |ins| from the current block.
      MOZ_ASSERT(ins->block() == this);
      instructions_.remove(ins);
  
      // Insert into new block, which may be distinct.
      // Uses and operands are untouched.
      ins->setBlock(at->block());
      at->block()->instructions_.insertBefore(at, ins);
      ins->setTrackedSite(at->trackedSite());
  }
  
  MInstruction*
  MBasicBlock::safeInsertTop(MDefinition* ins, IgnoreTop ignore)
  {
      // Beta nodes and interrupt checks are required to be located at the
      // beginnings of basic blocks, so we must insert new instructions after any
      // such instructions.
      MInstructionIterator insertIter = !ins || ins->isPhi()
                                      ? begin()
                                      : begin(ins->toInstruction());
      while (insertIter->isBeta() ||
             insertIter->isInterruptCheck() ||
             insertIter->isConstant() ||
             (!(ignore & IgnoreRecover) && insertIter->isRecoveredOnBailout()))
      {
          insertIter++;
      }
  
      return *insertIter;
  }
  
  void
  MBasicBlock::discardResumePoint(MResumePoint* rp, ReferencesType refType /* = RefType_Default */)
  {
      if (refType & RefType_DiscardOperands)
          rp->releaseUses();
  #ifdef DEBUG
      MResumePointIterator iter = resumePointsBegin();
      while (*iter != rp) {
          // We should reach it before reaching the end.
          MOZ_ASSERT(iter != resumePointsEnd());
          iter++;
      }
      resumePoints_.removeAt(iter);
  #endif
  }
  
  void
  MBasicBlock::prepareForDiscard(MInstruction* ins, ReferencesType refType /* = RefType_Default */)
  {
      // Only remove instructions from the same basic block.  This is needed for
      // correctly removing the resume point if any.
      MOZ_ASSERT(ins->block() == this);
  
      MResumePoint* rp = ins->resumePoint();
      if ((refType & RefType_DiscardResumePoint) && rp)
          discardResumePoint(rp, refType);
  
      // We need to assert that instructions have no uses after removing the their
      // resume points operands as they could be captured by their own resume
      // point.
      MOZ_ASSERT_IF(refType & RefType_AssertNoUses, !ins->hasUses());
  
      const uint32_t InstructionOperands = RefType_DiscardOperands | RefType_DiscardInstruction;
      if ((refType & InstructionOperands) == InstructionOperands) {
          for (size_t i = 0, e = ins->numOperands(); i < e; i++)
              ins->releaseOperand(i);
      }
  
      ins->setDiscarded();
  }
  
  void
  MBasicBlock::discard(MInstruction* ins)
  {
      prepareForDiscard(ins);
      instructions_.remove(ins);
  }
  
  void
  MBasicBlock::discardIgnoreOperands(MInstruction* ins)
  {
  #ifdef DEBUG
      for (size_t i = 0, e = ins->numOperands(); i < e; i++)
          MOZ_ASSERT(!ins->hasOperand(i));
  #endif
  
      prepareForDiscard(ins, RefType_IgnoreOperands);
      instructions_.remove(ins);
  }
  
  void
  MBasicBlock::discardDef(MDefinition* at)
  {
      if (at->isPhi())
          at->block_->discardPhi(at->toPhi());
      else
          at->block_->discard(at->toInstruction());
  }
  
  void
  MBasicBlock::discardAllInstructions()
  {
      MInstructionIterator iter = begin();
      discardAllInstructionsStartingAt(iter);
  }
  
  void
  MBasicBlock::discardAllInstructionsStartingAt(MInstructionIterator iter)
  {
      while (iter != end()) {
          // Discard operands and resume point operands and flag the instruction
          // as discarded.  Also we do not assert that we have no uses as blocks
          // might be removed in reverse post order.
          MInstruction* ins = *iter++;
          prepareForDiscard(ins, RefType_DefaultNoAssert);
          instructions_.remove(ins);
      }
  }
  
  void
  MBasicBlock::discardAllPhiOperands()
  {
      for (MPhiIterator iter = phisBegin(); iter != phisEnd(); iter++)
          iter->removeAllOperands();
  
      for (MBasicBlock** pred = predecessors_.begin(); pred != predecessors_.end(); pred++)
          (*pred)->clearSuccessorWithPhis();
  }
  
  void
  MBasicBlock::discardAllPhis()
  {
      discardAllPhiOperands();
      phis_.clear();
  }
  
  void
  MBasicBlock::discardAllResumePoints(bool discardEntry)
  {
      if (outerResumePoint_)
          clearOuterResumePoint();
  
      if (discardEntry && entryResumePoint_)
          clearEntryResumePoint();
  
  #ifdef DEBUG
      if (!entryResumePoint()) {
          MOZ_ASSERT(resumePointsEmpty());
      } else {
          MResumePointIterator iter(resumePointsBegin());
          MOZ_ASSERT(iter != resumePointsEnd());
          iter++;
          MOZ_ASSERT(iter == resumePointsEnd());
      }
  #endif
  }
  
  void
  MBasicBlock::insertBefore(MInstruction* at, MInstruction* ins)
  {
      MOZ_ASSERT(at->block() == this);
      ins->setBlock(this);
      graph().allocDefinitionId(ins);
      instructions_.insertBefore(at, ins);
      ins->setTrackedSite(at->trackedSite());
  }
  
  void
  MBasicBlock::insertAfter(MInstruction* at, MInstruction* ins)
  {
      MOZ_ASSERT(at->block() == this);
      ins->setBlock(this);
      graph().allocDefinitionId(ins);
      instructions_.insertAfter(at, ins);
      ins->setTrackedSite(at->trackedSite());
  }
  
  void
  MBasicBlock::insertAtEnd(MInstruction* ins)
  {
      if (hasLastIns())
          insertBefore(lastIns(), ins);
      else
          add(ins);
  }
  
  void
  MBasicBlock::add(MInstruction* ins)
  {
      MOZ_ASSERT(!hasLastIns());
      ins->setBlock(this);
      graph().allocDefinitionId(ins);
      instructions_.pushBack(ins);
      ins->setTrackedSite(trackedSite_);
  }
  
  void
  MBasicBlock::end(MControlInstruction* ins)
  {
      MOZ_ASSERT(!hasLastIns()); // Existing control instructions should be removed first.
      MOZ_ASSERT(ins);
      add(ins);
  }
  
  void
  MBasicBlock::addPhi(MPhi* phi)
  {
      phis_.pushBack(phi);
      phi->setBlock(this);
      graph().allocDefinitionId(phi);
  }
  
  void
  MBasicBlock::discardPhi(MPhi* phi)
  {
      MOZ_ASSERT(!phis_.empty());
  
      phi->removeAllOperands();
      phi->setDiscarded();
  
      phis_.remove(phi);
  
      if (phis_.empty()) {
          for (MBasicBlock* pred : predecessors_)
              pred->clearSuccessorWithPhis();
      }
  }
  
  void
  MBasicBlock::flagOperandsOfPrunedBranches(MInstruction* ins)
  {
      // Find the previous resume point which would be used for bailing out.
      MResumePoint* rp = nullptr;
      for (MInstructionReverseIterator iter = rbegin(ins); iter != rend(); iter++) {
          rp = iter->resumePoint();
          if (rp)
              break;
      }
  
      // If none, take the entry resume point.
      if (!rp)
          rp = entryResumePoint();
  
      // The only blocks which do not have any entryResumePoint in Ion, are the
      // SplitEdge blocks.  SplitEdge blocks only have a Goto instruction before
      // Range Analysis phase.  In adjustInputs, we are manipulating instructions
      // which have a TypePolicy.  So, as a Goto has no operand and no type
      // policy, the entry resume point should exists.
      MOZ_ASSERT(rp);
  
      // Flag all operand as being potentially used.
      while (rp) {
          for (size_t i = 0, end = rp->numOperands(); i < end; i++)
              rp->getOperand(i)->setUseRemovedUnchecked();
          rp = rp->caller();
      }
  }
  
  bool
  MBasicBlock::addPredecessor(TempAllocator& alloc, MBasicBlock* pred)
  {
      return addPredecessorPopN(alloc, pred, 0);
  }
  
  bool
  MBasicBlock::addPredecessorPopN(TempAllocator& alloc, MBasicBlock* pred, uint32_t popped)
  {
      MOZ_ASSERT(pred);
      MOZ_ASSERT(predecessors_.length() > 0);
  
      // Predecessors must be finished, and at the correct stack depth.
      MOZ_ASSERT(pred->hasLastIns());
      MOZ_ASSERT(pred->stackPosition_ == stackPosition_ + popped);
  
      for (uint32_t i = 0, e = stackPosition_; i < e; ++i) {
          MDefinition* mine = getSlot(i);
          MDefinition* other = pred->getSlot(i);
  
          if (mine != other) {
              // If the current instruction is a phi, and it was created in this
              // basic block, then we have already placed this phi and should
              // instead append to its operands.
              if (mine->isPhi() && mine->block() == this) {
                  MOZ_ASSERT(predecessors_.length());
                  if (!mine->toPhi()->addInputSlow(other))
                      return false;
              } else {
                  // Otherwise, create a new phi node.
                  MPhi* phi;
                  if (mine->type() == other->type())
                      phi = MPhi::New(alloc, mine->type());
                  else
                      phi = MPhi::New(alloc);
                  addPhi(phi);
  
                  // Prime the phi for each predecessor, so input(x) comes from
                  // predecessor(x).
                  if (!phi->reserveLength(predecessors_.length() + 1))
                      return false;
  
                  for (size_t j = 0, numPreds = predecessors_.length(); j < numPreds; ++j) {
                      MOZ_ASSERT(predecessors_[j]->getSlot(i) == mine);
                      phi->addInput(mine);
                  }
                  phi->addInput(other);
  
                  setSlot(i, phi);
                  if (entryResumePoint())
                      entryResumePoint()->replaceOperand(i, phi);
              }
          }
      }
  
      return predecessors_.append(pred);
  }
  
  void
  MBasicBlock::addPredecessorSameInputsAs(MBasicBlock* pred, MBasicBlock* existingPred)
  {
      MOZ_ASSERT(pred);
      MOZ_ASSERT(predecessors_.length() > 0);
  
      // Predecessors must be finished, and at the correct stack depth.
      MOZ_ASSERT(pred->hasLastIns());
      MOZ_ASSERT(!pred->successorWithPhis());
  
      AutoEnterOOMUnsafeRegion oomUnsafe;
  
      if (!phisEmpty()) {
          size_t existingPosition = indexForPredecessor(existingPred);
          for (MPhiIterator iter = phisBegin(); iter != phisEnd(); iter++) {
              if (!iter->addInputSlow(iter->getOperand(existingPosition)))
                  oomUnsafe.crash("MBasicBlock::addPredecessorAdjustPhis");
          }
      }
  
      if (!predecessors_.append(pred))
          oomUnsafe.crash("MBasicBlock::addPredecessorAdjustPhis");
  }
  
  bool
  MBasicBlock::addPredecessorWithoutPhis(MBasicBlock* pred)
  {
      // Predecessors must be finished.
      MOZ_ASSERT(pred && pred->hasLastIns());
      return predecessors_.append(pred);
  }
  
  bool
  MBasicBlock::addImmediatelyDominatedBlock(MBasicBlock* child)
  {
      return immediatelyDominated_.append(child);
  }
  
  void
  MBasicBlock::removeImmediatelyDominatedBlock(MBasicBlock* child)
  {
      for (size_t i = 0; ; ++i) {
          MOZ_ASSERT(i < immediatelyDominated_.length(),
                     "Dominated block to remove not present");
          if (immediatelyDominated_[i] == child) {
              immediatelyDominated_[i] = immediatelyDominated_.back();
              immediatelyDominated_.popBack();
              return;
          }
      }
  }
  
  void
  MBasicBlock::assertUsesAreNotWithin(MUseIterator use, MUseIterator end)
  {
  #ifdef DEBUG
      for (; use != end; use++) {
          MOZ_ASSERT_IF(use->consumer()->isDefinition(),
                        use->consumer()->toDefinition()->block()->id() < id());
      }
  #endif
  }
  
  AbortReason
  MBasicBlock::setBackedge(MBasicBlock* pred)
  {
      // Predecessors must be finished, and at the correct stack depth.
      MOZ_ASSERT(hasLastIns());
      MOZ_ASSERT(pred->hasLastIns());
      MOZ_ASSERT(pred->stackDepth() == entryResumePoint()->stackDepth());
  
      // We must be a pending loop header
      MOZ_ASSERT(kind_ == PENDING_LOOP_HEADER);
  
      bool hadTypeChange = false;
  
      // Add exit definitions to each corresponding phi at the entry.
      if (!inheritPhisFromBackedge(pred, &hadTypeChange))
          return AbortReason_Alloc;
  
      if (hadTypeChange) {
          for (MPhiIterator phi = phisBegin(); phi != phisEnd(); phi++)
              phi->removeOperand(phi->numOperands() - 1);
          return AbortReason_Disable;
      }
  
      // We are now a loop header proper
      kind_ = LOOP_HEADER;
  
      if (!predecessors_.append(pred))
          return AbortReason_Alloc;
  
      return AbortReason_NoAbort;
  }
  
  bool
  MBasicBlock::setBackedgeAsmJS(MBasicBlock* pred)
  {
      // Predecessors must be finished, and at the correct stack depth.
      MOZ_ASSERT(hasLastIns());
      MOZ_ASSERT(pred->hasLastIns());
      MOZ_ASSERT(stackDepth() == pred->stackDepth());
  
      // We must be a pending loop header
      MOZ_ASSERT(kind_ == PENDING_LOOP_HEADER);
  
      // Add exit definitions to each corresponding phi at the entry.
      // Note: Phis are inserted in the same order as the slots. (see
      // MBasicBlock::NewAsmJS)
      size_t slot = 0;
      for (MPhiIterator phi = phisBegin(); phi != phisEnd(); phi++, slot++) {
          MPhi* entryDef = *phi;
          MDefinition* exitDef = pred->getSlot(slot);
  
          // Assert that we already placed phis for each slot.
          MOZ_ASSERT(entryDef->block() == this);
  
          // Assert that the phi already has the correct type.
          MOZ_ASSERT(entryDef->type() == exitDef->type());
          MOZ_ASSERT(entryDef->type() != MIRType_Value);
  
          if (entryDef == exitDef) {
              // If the exit def is the same as the entry def, make a redundant
              // phi. Since loop headers have exactly two incoming edges, we
              // know that that's just the first input.
              //
              // Note that we eliminate later rather than now, to avoid any
              // weirdness around pending continue edges which might still hold
              // onto phis.
              exitDef = entryDef->getOperand(0);
          }
  
          // Phis always have room for 2 operands, so we can use addInput.
          entryDef->addInput(exitDef);
  
          MOZ_ASSERT(slot < pred->stackDepth());
          setSlot(slot, entryDef);
      }
  
      // We are now a loop header proper
      kind_ = LOOP_HEADER;
  
      return predecessors_.append(pred);
  }
  
  void
  MBasicBlock::clearLoopHeader()
  {
      MOZ_ASSERT(isLoopHeader());
      kind_ = NORMAL;
  }
  
  void
  MBasicBlock::setLoopHeader(MBasicBlock* newBackedge)
  {
      MOZ_ASSERT(!isLoopHeader());
      kind_ = LOOP_HEADER;
  
      size_t numPreds = numPredecessors();
      MOZ_ASSERT(numPreds != 0);
  
      size_t lastIndex = numPreds - 1;
      size_t oldIndex = 0;
      for (; ; ++oldIndex) {
          MOZ_ASSERT(oldIndex < numPreds);
          MBasicBlock* pred = getPredecessor(oldIndex);
          if (pred == newBackedge)
              break;
      }
  
      // Set the loop backedge to be the last element in predecessors_.
      Swap(predecessors_[oldIndex], predecessors_[lastIndex]);
  
      // If we have phis, reorder their operands accordingly.
      if (!phisEmpty()) {
          getPredecessor(oldIndex)->setSuccessorWithPhis(this, oldIndex);
          getPredecessor(lastIndex)->setSuccessorWithPhis(this, lastIndex);
          for (MPhiIterator iter(phisBegin()), end(phisEnd()); iter != end; ++iter) {
              MPhi* phi = *iter;
              MDefinition* last = phi->getOperand(oldIndex);
              MDefinition* old = phi->getOperand(lastIndex);
              phi->replaceOperand(oldIndex, old);
              phi->replaceOperand(lastIndex, last);
          }
      }
  
      MOZ_ASSERT(newBackedge->loopHeaderOfBackedge() == this);
      MOZ_ASSERT(backedge() == newBackedge);
  }
  
  size_t
  MBasicBlock::numSuccessors() const
  {
      MOZ_ASSERT(lastIns());
      return lastIns()->numSuccessors();
  }
  
  MBasicBlock*
  MBasicBlock::getSuccessor(size_t index) const
  {
      MOZ_ASSERT(lastIns());
      return lastIns()->getSuccessor(index);
  }
  
  size_t
  MBasicBlock::getSuccessorIndex(MBasicBlock* block) const
  {
      MOZ_ASSERT(lastIns());
      for (size_t i = 0; i < numSuccessors(); i++) {
          if (getSuccessor(i) == block)
              return i;
      }
      MOZ_CRASH("Invalid successor");
  }
  
  size_t
  MBasicBlock::getPredecessorIndex(MBasicBlock* block) const
  {
      for (size_t i = 0, e = numPredecessors(); i < e; ++i) {
          if (getPredecessor(i) == block)
              return i;
      }
      MOZ_CRASH("Invalid predecessor");
  }
  
  void
  MBasicBlock::replaceSuccessor(size_t pos, MBasicBlock* split)
  {
      MOZ_ASSERT(lastIns());
  
      // Note, during split-critical-edges, successors-with-phis is not yet set.
      // During PAA, this case is handled before we enter.
      MOZ_ASSERT_IF(successorWithPhis_, successorWithPhis_ != getSuccessor(pos));
  
      lastIns()->replaceSuccessor(pos, split);
  }
  
  void
  MBasicBlock::replacePredecessor(MBasicBlock* old, MBasicBlock* split)
  {
      for (size_t i = 0; i < numPredecessors(); i++) {
          if (getPredecessor(i) == old) {
              predecessors_[i] = split;
  
  #ifdef DEBUG
              // The same block should not appear twice in the predecessor list.
              for (size_t j = i; j < numPredecessors(); j++)
                  MOZ_ASSERT(predecessors_[j] != old);
  #endif
  
              return;
          }
      }
  
      MOZ_CRASH("predecessor was not found");
  }
  
  void
  MBasicBlock::clearDominatorInfo()
  {
      setImmediateDominator(nullptr);
      immediatelyDominated_.clear();
      numDominated_ = 0;
  }
  
  void
  MBasicBlock::removePredecessorWithoutPhiOperands(MBasicBlock* pred, size_t predIndex)
  {
      // If we're removing the last backedge, this is no longer a loop.
      if (isLoopHeader() && hasUniqueBackedge() && backedge() == pred)
          clearLoopHeader();
  
      // Adjust phis.  Note that this can leave redundant phis behind.
      // Don't adjust successorWithPhis() if we haven't constructed this
      // information yet.
      if (pred->successorWithPhis()) {
          MOZ_ASSERT(pred->positionInPhiSuccessor() == predIndex);
          pred->clearSuccessorWithPhis();
          for (size_t j = predIndex+1; j < numPredecessors(); j++)
              getPredecessor(j)->setSuccessorWithPhis(this, j - 1);
      }
  
      // Remove from pred list.
      predecessors_.erase(predecessors_.begin() + predIndex);
  }
  
  void
  MBasicBlock::removePredecessor(MBasicBlock* pred)
  {
      size_t predIndex = getPredecessorIndex(pred);
  
      // Remove the phi operands.
      for (MPhiIterator iter(phisBegin()), end(phisEnd()); iter != end; ++iter)
          iter->removeOperand(predIndex);
  
      // Now we can call the underlying function, which expects that phi
      // operands have been removed.
      removePredecessorWithoutPhiOperands(pred, predIndex);
  }
  
  void
  MBasicBlock::inheritPhis(MBasicBlock* header)
  {
      MResumePoint* headerRp = header->entryResumePoint();
      size_t stackDepth = headerRp->stackDepth();
      for (size_t slot = 0; slot < stackDepth; slot++) {
          MDefinition* exitDef = getSlot(slot);
          MDefinition* loopDef = headerRp->getOperand(slot);
          if (loopDef->block() != header) {
              MOZ_ASSERT(loopDef->block()->id() < header->id());
              MOZ_ASSERT(loopDef == exitDef);
              continue;
          }
  
          // Phis are allocated by NewPendingLoopHeader.
          MPhi* phi = loopDef->toPhi();
          MOZ_ASSERT(phi->numOperands() == 2);
  
          // The entry definition is always the leftmost input to the phi.
          MDefinition* entryDef = phi->getOperand(0);
  
          if (entryDef != exitDef)
              continue;
  
          // If the entryDef is the same as exitDef, then we must propagate the
          // phi down to this successor. This chance was missed as part of
          // setBackedge() because exits are not captured in resume points.
          setSlot(slot, phi);
      }
  }
  
  bool
  MBasicBlock::inheritPhisFromBackedge(MBasicBlock* backedge, bool* hadTypeChange)
  {
      // We must be a pending loop header
      MOZ_ASSERT(kind_ == PENDING_LOOP_HEADER);
  
      size_t stackDepth = entryResumePoint()->stackDepth();
      for (size_t slot = 0; slot < stackDepth; slot++) {
          // Get the value stack-slot of the back edge.
          MDefinition* exitDef = backedge->getSlot(slot);
  
          // Get the value of the loop header.
          MDefinition* loopDef = entryResumePoint()->getOperand(slot);
          if (loopDef->block() != this) {
              // If we are finishing a pending loop header, then we need to ensure
              // that all operands are phis. This is usualy the case, except for
              // object/arrays build with generators, in which case we share the
              // same allocations across all blocks.
              MOZ_ASSERT(loopDef->block()->id() < id());
              MOZ_ASSERT(loopDef == exitDef);
              continue;
          }
  
          // Phis are allocated by NewPendingLoopHeader.
          MPhi* entryDef = loopDef->toPhi();
          MOZ_ASSERT(entryDef->block() == this);
  
          if (entryDef == exitDef) {
              // If the exit def is the same as the entry def, make a redundant
              // phi. Since loop headers have exactly two incoming edges, we
              // know that that's just the first input.
              //
              // Note that we eliminate later rather than now, to avoid any
              // weirdness around pending continue edges which might still hold
              // onto phis.
              exitDef = entryDef->getOperand(0);
          }
  
          bool typeChange = false;
  
          if (!entryDef->addInputSlow(exitDef))
              return false;
          if (!entryDef->checkForTypeChange(exitDef, &typeChange))
              return false;
          *hadTypeChange |= typeChange;
          setSlot(slot, entryDef);
      }
  
      return true;
  }
  
  bool
  MBasicBlock::specializePhis()
  {
      for (MPhiIterator iter = phisBegin(); iter != phisEnd(); iter++) {
          MPhi* phi = *iter;
          if (!phi->specializeType())
              return false;
      }
      return true;
  }
  
  MTest*
  MBasicBlock::immediateDominatorBranch(BranchDirection* pdirection)
  {
      *pdirection = FALSE_BRANCH;
  
      if (numPredecessors() != 1)
          return nullptr;
  
      MBasicBlock* dom = immediateDominator();
      if (dom != getPredecessor(0))
          return nullptr;
  
      // Look for a trailing MTest branching to this block.
      MInstruction* ins = dom->lastIns();
      if (ins->isTest()) {
          MTest* test = ins->toTest();
  
          MOZ_ASSERT(test->ifTrue() == this || test->ifFalse() == this);
          if (test->ifTrue() == this && test->ifFalse() == this)
              return nullptr;
  
          *pdirection = (test->ifTrue() == this) ? TRUE_BRANCH : FALSE_BRANCH;
          return test;
      }
  
      return nullptr;
  }
  
  void
  MBasicBlock::dumpStack(GenericPrinter& out)
  {
  #ifdef DEBUG
      out.printf(" %-3s %-16s %-6s %-10s\n", "#", "name", "copyOf", "first/next");
      out.printf("-------------------------------------------\n");
      for (uint32_t i = 0; i < stackPosition_; i++) {
          out.printf(" %-3d", i);
          out.printf(" %-16p\n", (void*)slots_[i]);
      }
  #endif
  }
  
  void
  MBasicBlock::dumpStack()
  {
      Fprinter out(stderr);
      dumpStack(out);
      out.finish();
  }
  
  void
  MIRGraph::dump(GenericPrinter& out)
  {
  #ifdef DEBUG
      for (MBasicBlockIterator iter(begin()); iter != end(); iter++) {
          iter->dump(out);
          out.printf("\n");
      }
  #endif
  }
  
  void
  MIRGraph::dump()
  {
      Fprinter out(stderr);
      dump(out);
      out.finish();
  }
  
  void
  MBasicBlock::dump(GenericPrinter& out)
  {
  #ifdef DEBUG
      out.printf("block%u:%s%s%s\n", id(),
                 isLoopHeader() ? " (loop header)" : "",
                 unreachable() ? " (unreachable)" : "",
                 isMarked() ? " (marked)" : "");
      if (MResumePoint* resume = entryResumePoint())
          resume->dump(out);
      for (MPhiIterator iter(phisBegin()); iter != phisEnd(); iter++)
          iter->dump(out);
      for (MInstructionIterator iter(begin()); iter != end(); iter++)
          iter->dump(out);
  #endif
  }
  
  void
  MBasicBlock::dump()
  {
      Fprinter out(stderr);
      dump(out);
      out.finish();
  }
  