Bug 1303399 part 2 - IonMonkey: Fallback when we fail to inline an uninlinable function. r=h4writer
authorNicolas B. Pierron <nicolas.b.pierron@mozilla.com>
Fri, 28 Oct 2016 12:45:31 +0000
changeset 320001 47e4fb57325d0766abbe7dd6260d339cb1e8f20f
parent 320000 c0f71d17d65902b04f429e556bb8d709bcb32ce9
child 320002 726ec9c83018397c0617eb25f71dde6e383b5ec3
push id20749
push userryanvm@gmail.com
push dateSat, 29 Oct 2016 13:21:21 +0000
treeherderfx-team@1b170b39ed6b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersh4writer
bugs1303399
milestone52.0a1
Bug 1303399 part 2 - IonMonkey: Fallback when we fail to inline an uninlinable function. r=h4writer
js/src/jit/Ion.h
js/src/jit/IonBuilder.cpp
js/src/jit/MIRGraph.cpp
js/src/jit/MIRGraph.h
--- a/js/src/jit/Ion.h
+++ b/js/src/jit/Ion.h
@@ -25,17 +25,16 @@ enum MethodStatus
     Method_Error,
     Method_CantCompile,
     Method_Skipped,
     Method_Compiled
 };
 
 enum AbortReason {
     AbortReason_Alloc,
-    AbortReason_Inlining,
     AbortReason_PreliminaryObjects,
     AbortReason_Disable,
     AbortReason_Error,
     AbortReason_NoAbort
 };
 
 // A JIT context is needed to enter into either an JIT method or an instance
 // of a JIT compiler. It points to a temporary allocator and the active
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -5158,16 +5158,20 @@ class AutoAccumulateReturns
 };
 
 IonBuilder::InliningStatus
 IonBuilder::inlineScriptedCall(CallInfo& callInfo, JSFunction* target)
 {
     MOZ_ASSERT(target->hasScript());
     MOZ_ASSERT(IsIonInlinablePC(pc));
 
+    MBasicBlock::BackupPoint backup(current);
+    if (!backup.init(alloc()))
+        return InliningStatus_Error;
+
     callInfo.setImplicitlyUsedUnchecked();
 
     // Ensure sufficient space in the slots: needed for inlining from FUNAPPLY.
     uint32_t depth = current->stackDepth() + callInfo.numFormals();
     if (depth > current->nslots()) {
         if (!current->increaseSlots(depth - current->nslots()))
             return InliningStatus_Error;
     }
@@ -5243,19 +5247,18 @@ IonBuilder::inlineScriptedCall(CallInfo&
             abortReason_ = AbortReason_Error;
             return InliningStatus_Error;
         }
 
         // Inlining the callee failed. Mark the callee as uninlineable only if
         // the inlining was aborted for a non-exception reason.
         if (inlineBuilder.abortReason_ == AbortReason_Disable) {
             calleeScript->setUninlineable();
-            abortReason_ = AbortReason_Inlining;
-        } else if (inlineBuilder.abortReason_ == AbortReason_Inlining) {
-            abortReason_ = AbortReason_Inlining;
+            current = backup.restore();
+            return InliningStatus_NotInlined;
         } else if (inlineBuilder.abortReason_ == AbortReason_Alloc) {
             abortReason_ = AbortReason_Alloc;
         } else if (inlineBuilder.abortReason_ == AbortReason_PreliminaryObjects) {
             const ObjectGroupVector& groups = inlineBuilder.abortedPreliminaryGroups();
             MOZ_ASSERT(!groups.empty());
             for (size_t i = 0; i < groups.length(); i++)
                 addAbortedPreliminaryGroup(groups[i]);
             abortReason_ = AbortReason_PreliminaryObjects;
@@ -5274,18 +5277,18 @@ IonBuilder::inlineScriptedCall(CallInfo&
     // Inherit the slots from current and pop |fun|.
     returnBlock->inheritSlots(current);
     returnBlock->pop();
 
     // Accumulate return values.
     if (returns.empty()) {
         // Inlining of functions that have no exit is not supported.
         calleeScript->setUninlineable();
-        abortReason_ = AbortReason_Inlining;
-        return InliningStatus_Error;
+        current = backup.restore();
+        return InliningStatus_NotInlined;
     }
     MDefinition* retvalDefn = patchInlinedReturns(callInfo, returns, returnBlock);
     if (!retvalDefn)
         return InliningStatus_Error;
     returnBlock->push(retvalDefn);
 
     // Initialize entry slots now that the stack has been fixed up.
     if (!returnBlock->initEntrySlots(alloc()))
@@ -6067,17 +6070,16 @@ IonBuilder::inlineCalls(CallInfo& callIn
         if (!setCurrentAndSpecializePhis(inlineBlock))
             return false;
         InliningStatus status = inlineSingleCall(inlineInfo, target);
         if (status == InliningStatus_Error)
             return false;
 
         // Natives may veto inlining.
         if (status == InliningStatus_NotInlined) {
-            MOZ_ASSERT(target->isNative());
             MOZ_ASSERT(current == inlineBlock);
             graph().removeBlock(inlineBlock);
             choiceSet[i] = false;
             continue;
         }
 
         // inlineSingleCall() changed |current| to the inline return block.
         MBasicBlock* inlineReturnBlock = current;
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -1583,16 +1583,112 @@ MBasicBlock::immediateDominatorBranch(Br
 
         *pdirection = (test->ifTrue() == this) ? TRUE_BRANCH : FALSE_BRANCH;
         return test;
     }
 
     return nullptr;
 }
 
+MBasicBlock::BackupPoint::BackupPoint(MBasicBlock* current)
+  : current_(current),
+    lastBlock_(nullptr),
+    lastIns_(current->hasAnyIns() ? *current->rbegin() : nullptr),
+    stackPosition_(current->stackDepth()),
+    slots_()
+#ifdef DEBUG
+  , lastPhi_(!current->phisEmpty() ? *current->phis_.rbegin() : nullptr),
+    predecessorsCheckSum_(computePredecessorsCheckSum(current)),
+    instructionsCheckSum_(computeInstructionsCheckSum(current)),
+    id_(current->id()),
+    callerResumePoint_(current->callerResumePoint()),
+    entryResumePoint_(current->entryResumePoint())
+#endif
+{
+    // The block is not yet jumping into a block of an inlined function yet.
+    MOZ_ASSERT(current->outerResumePoint_ == nullptr);
+
+    // The CFG reconstruction might add blocks and move them around.
+    uint32_t lastBlockId = 0;
+    PostorderIterator e = current->graph().poEnd();
+    for (PostorderIterator b = current->graph().poBegin(); b != e; ++b) {
+        if (lastBlockId <= b->id()) {
+            lastBlock_ = *b;
+            lastBlockId = b->id();
+        }
+    }
+    MOZ_ASSERT(lastBlock_);
+}
+
+bool
+MBasicBlock::BackupPoint::init(TempAllocator& alloc)
+{
+    if (!slots_.init(alloc, stackPosition_))
+        return false;
+    for (size_t i = 0, e = stackPosition_; i < e; ++i)
+        slots_[i] = current_->slots_[i];
+    return true;
+}
+
+#ifdef DEBUG
+uintptr_t
+MBasicBlock::BackupPoint::computePredecessorsCheckSum(MBasicBlock* block)
+{
+    uintptr_t hash = 0;
+    for (size_t i = 0; i < block->numPredecessors(); i++) {
+        MBasicBlock* pred = block->getPredecessor(i);
+        uintptr_t data = reinterpret_cast<uintptr_t>(pred);
+        hash = data + (hash << 6) + (hash << 16) - hash;
+    }
+    return hash;
+}
+
+HashNumber
+MBasicBlock::BackupPoint::computeInstructionsCheckSum(MBasicBlock* block)
+{
+    HashNumber h = 0;
+    MOZ_ASSERT_IF(lastIns_, lastIns_->block() == block);
+    for (MInstructionIterator ins = block->begin(); ins != block->end(); ++ins) {
+        h += ins->valueHash();
+        h += h << 10;
+        h ^= h >> 6;
+    }
+    return h;
+}
+#endif
+
+MBasicBlock*
+MBasicBlock::BackupPoint::restore()
+{
+    // No extra Phi got added.
+    MOZ_ASSERT((!current_->phisEmpty() ? *current_->phis_.rbegin() : nullptr) == lastPhi_);
+
+    MOZ_ASSERT_IF(lastIns_, lastIns_->block() == current_);
+    MOZ_ASSERT_IF(lastIns_, !lastIns_->isDiscarded());
+    MInstructionIterator lastIns(lastIns_ ? ++(current_->begin(lastIns_)) : current_->begin());
+    current_->discardAllInstructionsStartingAt(lastIns);
+    current_->clearOuterResumePoint();
+
+    MOZ_ASSERT(current_->slots_.length() >= stackPosition_);
+    if (current_->stackPosition_ != stackPosition_)
+        current_->setStackDepth(stackPosition_);
+    for (size_t i = 0, e = stackPosition_; i < e; ++i)
+        current_->slots_[i] = slots_[i];
+
+    MOZ_ASSERT(current_->id() == id_);
+    MOZ_ASSERT(predecessorsCheckSum_ == computePredecessorsCheckSum(current_));
+    MOZ_ASSERT(instructionsCheckSum_ == computeInstructionsCheckSum(current_));
+    MOZ_ASSERT(current_->callerResumePoint() == callerResumePoint_);
+    MOZ_ASSERT(current_->entryResumePoint() == entryResumePoint_);
+
+    current_->graph().removeBlocksAfter(lastBlock_);
+
+    return current_;
+}
+
 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);
--- a/js/src/jit/MIRGraph.h
+++ b/js/src/jit/MIRGraph.h
@@ -654,16 +654,46 @@ class MBasicBlock : public TempObject, p
     }
     jsbytecode* trackedPc() const {
         return trackedSite_ ? trackedSite_->pc() : nullptr;
     }
     InlineScriptTree* trackedTree() const {
         return trackedSite_ ? trackedSite_->tree() : nullptr;
     }
 
+    // This class is used for reverting the graph within IonBuilder.
+    class BackupPoint {
+        friend MBasicBlock;
+
+        MBasicBlock* current_;
+        MBasicBlock* lastBlock_;
+        MInstruction* lastIns_;
+        uint32_t stackPosition_;
+        FixedList<MDefinition*> slots_;
+#ifdef DEBUG
+        // The following fields should remain identical during IonBuilder
+        // construction, these are used for assertions.
+        MPhi* lastPhi_;
+        uintptr_t predecessorsCheckSum_;
+        HashNumber instructionsCheckSum_;
+        uint32_t id_;
+        MResumePoint* callerResumePoint_;
+        MResumePoint* entryResumePoint_;
+
+        size_t computePredecessorsCheckSum(MBasicBlock* block);
+        HashNumber computeInstructionsCheckSum(MBasicBlock* block);
+#endif
+      public:
+        explicit BackupPoint(MBasicBlock* current);
+        MOZ_MUST_USE bool init(TempAllocator& alloc);
+        MBasicBlock* restore();
+    };
+
+    friend BackupPoint;
+
   private:
     MIRGraph& graph_;
     const CompileInfo& info_; // Each block originates from a particular script.
     InlineList<MInstruction> instructions_;
     Vector<MBasicBlock*, 1, JitAllocPolicy> predecessors_;
     InlineList<MPhi> phis_;
     FixedList<MDefinition*> slots_;
     uint32_t stackPosition_;