Bug 863518 - Consider types added by loop body when unboxing OSR values, r=dvander.
authorBrian Hackett <bhackett1024@gmail.com>
Mon, 22 Apr 2013 20:22:30 -0600
changeset 129606 9aff2a52d88b0215e9a6045714b00ba328efb7f5
parent 129605 c946f7a3939716cdcce47255ff6ff6ae20363215
child 129607 63788e2eb007acbda2ce20cd1a16f9bdd69f46f9
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersdvander
bugs863518
milestone23.0a1
Bug 863518 - Consider types added by loop body when unboxing OSR values, r=dvander.
js/src/ion/IonBuilder.cpp
js/src/ion/IonBuilder.h
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -388,17 +388,17 @@ IonBuilder::analyzeNewLoopTypes(MBasicBl
             }
             if (type != MIRType_None)
                 phi->addBackedgeType(type, NULL);
         }
     }
 }
 
 bool
-IonBuilder::pushLoop(CFGState::State initial, jsbytecode *stopAt, MBasicBlock *entry,
+IonBuilder::pushLoop(CFGState::State initial, jsbytecode *stopAt, MBasicBlock *entry, bool osr,
                      jsbytecode *loopHead, jsbytecode *initialPc,
                      jsbytecode *bodyStart, jsbytecode *bodyEnd, jsbytecode *exitpc,
                      jsbytecode *continuepc)
 {
     if (!continuepc)
         continuepc = entry->pc();
 
     ControlFlowInfo loop(cfgStack_.length(), continuepc);
@@ -408,16 +408,17 @@ IonBuilder::pushLoop(CFGState::State ini
     CFGState state;
     state.state = initial;
     state.stopAt = stopAt;
     state.loop.bodyStart = bodyStart;
     state.loop.bodyEnd = bodyEnd;
     state.loop.exitpc = exitpc;
     state.loop.continuepc = continuepc;
     state.loop.entry = entry;
+    state.loop.osr = osr;
     state.loop.successor = NULL;
     state.loop.breaks = NULL;
     state.loop.continues = NULL;
     state.loop.initialState = initial;
     state.loop.initialPc = initialPc;
     state.loop.initialStopAt = stopAt;
     state.loop.loopHead = loopHead;
     return cfgStack_.append(state);
@@ -760,16 +761,131 @@ IonBuilder::initScopeChain()
         scope = MConstant::New(ObjectValue(script()->global()));
         current->add(scope);
     }
 
     current->setScopeChain(scope);
     return true;
 }
 
+bool
+IonBuilder::addOsrValueTypeBarrier(uint32_t slot, MInstruction **def_,
+                                   MIRType type, types::StackTypeSet *typeSet)
+{
+    MInstruction *&def = *def_;
+    MBasicBlock *osrBlock = def->block();
+
+    // Clear bogus type information added in newOsrPreheader().
+    def->setResultType(MIRType_Value);
+    def->setResultTypeSet(NULL);
+
+    if (typeSet && !typeSet->unknown()) {
+        MInstruction *barrier = MTypeBarrier::New(def, typeSet);
+        osrBlock->insertBefore(osrBlock->lastIns(), barrier);
+        osrBlock->rewriteSlot(slot, barrier);
+        def = barrier;
+    } else if (type == MIRType_Null ||
+               type == MIRType_Undefined ||
+               type == MIRType_Magic)
+    {
+        // No unbox instruction will be added below, so check the type by
+        // adding a type barrier for a singleton type set.
+        types::Type ntype = types::Type::PrimitiveType(ValueTypeFromMIRType(type));
+        typeSet = GetIonContext()->temp->lifoAlloc()->new_<types::StackTypeSet>(ntype);
+        if (!typeSet)
+            return false;
+        MInstruction *barrier = MTypeBarrier::New(def, typeSet);
+        osrBlock->insertBefore(osrBlock->lastIns(), barrier);
+        osrBlock->rewriteSlot(slot, barrier);
+        def = barrier;
+    }
+
+    switch (type) {
+      case MIRType_Boolean:
+      case MIRType_Int32:
+      case MIRType_Double:
+      case MIRType_String:
+      case MIRType_Object:
+      {
+        MUnbox *unbox = MUnbox::New(def, type, MUnbox::Fallible);
+        osrBlock->insertBefore(osrBlock->lastIns(), unbox);
+        osrBlock->rewriteSlot(slot, unbox);
+        def = unbox;
+        break;
+      }
+
+      case MIRType_Null:
+      {
+        MConstant *c = MConstant::New(NullValue());
+        osrBlock->insertBefore(osrBlock->lastIns(), c);
+        osrBlock->rewriteSlot(slot, c);
+        def = c;
+        break;
+      }
+
+      case MIRType_Undefined:
+      {
+        MConstant *c = MConstant::New(UndefinedValue());
+        osrBlock->insertBefore(osrBlock->lastIns(), c);
+        osrBlock->rewriteSlot(slot, c);
+        def = c;
+        break;
+      }
+
+      case MIRType_Magic:
+        JS_ASSERT(lazyArguments_);
+        osrBlock->rewriteSlot(slot, lazyArguments_);
+        def = lazyArguments_;
+        break;
+
+      default:
+        break;
+    }
+
+    JS_ASSERT(def == osrBlock->getSlot(slot));
+    return true;
+}
+
+bool
+IonBuilder::maybeAddOsrTypeBarriers()
+{
+    if (!info().osrPc())
+        return true;
+
+    // The loop has successfully been processed, and the loop header phis
+    // have their final type. Add unboxes and type barriers in the OSR
+    // block to check that the values have the appropriate type, and update
+    // the types in the preheader.
+
+    MBasicBlock *osrBlock = graph().osrBlock();
+    MBasicBlock *preheader = osrBlock->getSuccessor(0);
+    MBasicBlock *header = preheader->getSuccessor(0);
+    static const size_t OSR_PHI_POSITION = 1;
+    JS_ASSERT(preheader->getPredecessor(OSR_PHI_POSITION) == osrBlock);
+
+    for (uint32_t i = 1; i < osrBlock->stackDepth(); i++) {
+        MInstruction *def = osrBlock->getSlot(i)->toOsrValue();
+
+        MPhi *headerPhi = header->getSlot(i)->toPhi();
+        MPhi *preheaderPhi = preheader->getSlot(i)->toPhi();
+
+        MIRType type = headerPhi->type();
+        types::StackTypeSet *typeSet = headerPhi->resultTypeSet();
+
+        if (!addOsrValueTypeBarrier(i, &def, type, typeSet))
+            return false;
+
+        preheaderPhi->replaceOperand(OSR_PHI_POSITION, def);
+        preheaderPhi->setResultType(type);
+        preheaderPhi->setResultTypeSet(typeSet);
+    }
+
+    return true;
+}
+
 // We try to build a control-flow graph in the order that it would be built as
 // if traversing the AST. This leads to a nice ordering and lets us build SSA
 // in one pass, since the bytecode is structured.
 //
 // We traverse the bytecode iteratively, maintaining a current basic block.
 // Each basic block has a mapping of local slots to instructions, as well as a
 // stack depth. As we encounter instructions we mutate this mapping in the
 // current block.
@@ -807,17 +923,17 @@ IonBuilder::traverseBytecode()
             // thus |while| instead of |if| so we don't skip any opcodes.
             if (!cfgStack_.empty() && cfgStack_.back().stopAt == pc) {
                 ControlStatus status = processCfgStack();
                 if (status == ControlStatus_Error)
                     return false;
                 if (status == ControlStatus_Abort)
                     return abort("Aborted while processing control flow");
                 if (!current)
-                    return true;
+                    return maybeAddOsrTypeBarriers();
                 continue;
             }
 
             // Some opcodes need to be handled early because they affect control
             // flow, terminating the current basic block and/or instructing the
             // traversal algorithm to continue from a new pc.
             //
             //   (1) If the opcode does not affect control flow, then the opcode
@@ -833,31 +949,31 @@ IonBuilder::traverseBytecode()
             // control flow point, so we iterate until it's time to inspect a real
             // opcode.
             ControlStatus status;
             if ((status = snoopControlFlow(JSOp(*pc))) == ControlStatus_None)
                 break;
             if (status == ControlStatus_Error)
                 return false;
             if (!current)
-                return true;
+                return maybeAddOsrTypeBarriers();
         }
 
         // Nothing in inspectOpcode() is allowed to advance the pc.
         JSOp op = JSOp(*pc);
         if (!inspectOpcode(op))
             return false;
 
         pc += js_CodeSpec[op].length;
 #ifdef TRACK_SNAPSHOTS
         current->updateTrackedPc(pc);
 #endif
     }
 
-    return true;
+    return maybeAddOsrTypeBarriers();
 }
 
 IonBuilder::ControlStatus
 IonBuilder::snoopControlFlow(JSOp op)
 {
     switch (op) {
       case JSOP_NOP:
         return maybeLoop(op, info().getNote(cx, pc));
@@ -1509,16 +1625,17 @@ IonBuilder::finishLoop(CFGState &state, 
     if (r == AbortReason_Disable) {
         // If there are types for variables on the backedge that were not
         // present at the original loop header, then uses of the variables'
         // phis may have generated incorrect nodes. The new types have been
         // incorporated into the header phis, so remove all blocks for the
         // loop body and restart with the new types.
         return restartLoop(state);
     }
+
     if (successor) {
         graph().moveBlockToEnd(successor);
         successor->inheritPhis(state.loop.entry);
     }
 
     if (state.loop.breaks) {
         // Propagate phis placed in the header to individual break exit points.
         DeferredEdge *edge = state.loop.breaks;
@@ -1569,17 +1686,17 @@ IonBuilder::restartLoop(CFGState state)
     // Remove all instructions from the header itself.
     header->discardAllInstructions();
     header->setStackDepth(header->getPredecessor(0)->stackDepth());
 
     popCfgStack();
 
     loopDepth_++;
 
-    if (!pushLoop(state.loop.initialState, state.loop.initialStopAt, header,
+    if (!pushLoop(state.loop.initialState, state.loop.initialStopAt, header, state.loop.osr,
                   state.loop.loopHead, state.loop.initialPc,
                   state.loop.bodyStart, state.loop.bodyEnd,
                   state.loop.exitpc, state.loop.continuepc))
     {
         return ControlStatus_Error;
     }
 
     CFGState &nstate = cfgStack_.back();
@@ -2153,35 +2270,37 @@ IonBuilder::doWhileLoop(JSOp op, jssrcno
     JS_ASSERT(ifne > pc);
 
     // Verify that the IFNE goes back to a loophead op.
     jsbytecode *loopHead = GetNextPc(pc);
     JS_ASSERT(JSOp(*loopHead) == JSOP_LOOPHEAD);
     JS_ASSERT(loopHead == ifne + GetJumpOffset(ifne));
 
     jsbytecode *loopEntry = GetNextPc(loopHead);
-    if (info().hasOsrAt(loopEntry)) {
+    bool osr = info().hasOsrAt(loopEntry);
+
+    if (osr) {
         MBasicBlock *preheader = newOsrPreheader(current, loopEntry);
         if (!preheader)
             return ControlStatus_Error;
         current->end(MGoto::New(preheader));
         setCurrentAndSpecializePhis(preheader);
     }
 
-    MBasicBlock *header = newPendingLoopHeader(current, pc);
+    MBasicBlock *header = newPendingLoopHeader(current, pc, osr);
     if (!header)
         return ControlStatus_Error;
     current->end(MGoto::New(header));
 
     jsbytecode *loophead = GetNextPc(pc);
     jsbytecode *bodyStart = GetNextPc(loophead);
     jsbytecode *bodyEnd = conditionpc;
     jsbytecode *exitpc = GetNextPc(ifne);
     analyzeNewLoopTypes(header, bodyStart, exitpc);
-    if (!pushLoop(CFGState::DO_WHILE_LOOP_BODY, conditionpc, header,
+    if (!pushLoop(CFGState::DO_WHILE_LOOP_BODY, conditionpc, header, osr,
                   loopHead, bodyStart, bodyStart, bodyEnd, exitpc, conditionpc))
     {
         return ControlStatus_Error;
     }
 
     CFGState &state = cfgStack_.back();
     state.loop.updatepc = conditionpc;
     state.loop.updateEnd = ifne;
@@ -2211,36 +2330,38 @@ IonBuilder::whileOrForInLoop(jssrcnote *
     jsbytecode *ifne = pc + ifneOffset;
     JS_ASSERT(ifne > pc);
 
     // Verify that the IFNE goes back to a loophead op.
     JS_ASSERT(JSOp(*GetNextPc(pc)) == JSOP_LOOPHEAD);
     JS_ASSERT(GetNextPc(pc) == ifne + GetJumpOffset(ifne));
 
     jsbytecode *loopEntry = pc + GetJumpOffset(pc);
-    if (info().hasOsrAt(loopEntry)) {
+    bool osr = info().hasOsrAt(loopEntry);
+
+    if (osr) {
         MBasicBlock *preheader = newOsrPreheader(current, loopEntry);
         if (!preheader)
             return ControlStatus_Error;
         current->end(MGoto::New(preheader));
         setCurrentAndSpecializePhis(preheader);
     }
 
-    MBasicBlock *header = newPendingLoopHeader(current, pc);
+    MBasicBlock *header = newPendingLoopHeader(current, pc, osr);
     if (!header)
         return ControlStatus_Error;
     current->end(MGoto::New(header));
 
     // Skip past the JSOP_LOOPHEAD for the body start.
     jsbytecode *loopHead = GetNextPc(pc);
     jsbytecode *bodyStart = GetNextPc(loopHead);
     jsbytecode *bodyEnd = pc + GetJumpOffset(pc);
     jsbytecode *exitpc = GetNextPc(ifne);
     analyzeNewLoopTypes(header, bodyStart, exitpc);
-    if (!pushLoop(CFGState::WHILE_LOOP_COND, ifne, header,
+    if (!pushLoop(CFGState::WHILE_LOOP_COND, ifne, header, osr,
                   loopHead, bodyEnd, bodyStart, bodyEnd, exitpc))
     {
         return ControlStatus_Error;
     }
 
     // Parse the condition first.
     setCurrentAndSpecializePhis(header);
     if (!jsop_loophead(loopHead))
@@ -2293,25 +2414,27 @@ IonBuilder::forLoop(JSOp op, jssrcnote *
         }
         loopEntry = GetNextPc(bodyStart);
     }
     jsbytecode *loopHead = bodyStart;
     JS_ASSERT(JSOp(*bodyStart) == JSOP_LOOPHEAD);
     JS_ASSERT(ifne + GetJumpOffset(ifne) == bodyStart);
     bodyStart = GetNextPc(bodyStart);
 
-    if (info().hasOsrAt(loopEntry)) {
+    bool osr = info().hasOsrAt(loopEntry);
+
+    if (osr) {
         MBasicBlock *preheader = newOsrPreheader(current, loopEntry);
         if (!preheader)
             return ControlStatus_Error;
         current->end(MGoto::New(preheader));
         setCurrentAndSpecializePhis(preheader);
     }
 
-    MBasicBlock *header = newPendingLoopHeader(current, pc);
+    MBasicBlock *header = newPendingLoopHeader(current, pc, osr);
     if (!header)
         return ControlStatus_Error;
     current->end(MGoto::New(header));
 
     // If there is no condition, we immediately parse the body. Otherwise, we
     // parse the condition.
     jsbytecode *stopAt;
     CFGState::State initial;
@@ -2321,18 +2444,21 @@ IonBuilder::forLoop(JSOp op, jssrcnote *
         initial = CFGState::FOR_LOOP_COND;
     } else {
         pc = bodyStart;
         stopAt = bodyEnd;
         initial = CFGState::FOR_LOOP_BODY;
     }
 
     analyzeNewLoopTypes(header, bodyStart, exitpc);
-    if (!pushLoop(initial, stopAt, header, loopHead, pc, bodyStart, bodyEnd, exitpc, updatepc))
+    if (!pushLoop(initial, stopAt, header, osr,
+                  loopHead, pc, bodyStart, bodyEnd, exitpc, updatepc))
+    {
         return ControlStatus_Error;
+    }
 
     CFGState &state = cfgStack_.back();
     state.loop.condpc = (condpc != ifne) ? condpc : NULL;
     state.loop.updatepc = (updatepc != condpc) ? updatepc : NULL;
     if (state.loop.updatepc)
         state.loop.updateEnd = condpc;
 
     setCurrentAndSpecializePhis(header);
@@ -5159,141 +5285,91 @@ IonBuilder::newOsrPreheader(MBasicBlock 
     osrBlock->linkOsrValues(start);
 
     // Clone types of the other predecessor of the pre-header to the osr block,
     // such as pre-header phi's won't discard specialized type of the
     // predecessor.
     JS_ASSERT(predecessor->stackDepth() == osrBlock->stackDepth());
     JS_ASSERT(info().scopeChainSlot() == 0);
 
-    // Unbox the MOsrValue if it is known to be unboxable.
+    // Treat the OSR values as having the same type as the existing values
+    // coming in to the loop. These will be fixed up with appropriate
+    // unboxing and type barriers in finishLoop, once the possible types
+    // at the loop header are known.
     for (uint32_t i = 1; i < osrBlock->stackDepth(); i++) {
         MDefinition *existing = current->getSlot(i);
         MDefinition *def = osrBlock->getSlot(i);
         JS_ASSERT(def->type() == MIRType_Value);
 
-        MIRType existingType = existing->type();
-        types::StackTypeSet *existingTypeSet = existing->resultTypeSet();
-
-        // The existing slot only reflects types for variables on entry to the
-        // loop, not those resulting from writes within the loop. Moreover,
-        // there may be types for variables not known at all to the compiler,
-        // either from values assigned to variables before the baseline
-        // compiler originally ran or from other value transformations such as
-        // using undefined values in bailouts for dead variables.
-        //
-        // Address all of these issues by incorporating the variable's type
-        // from the OSR frame into the types used for unboxing the OSR value.
-
-        bool haveValue = false;
-        Value existingValue;
-        {
-            uint32_t arg = i - info().firstArgSlot();
-            uint32_t var = i - info().firstLocalSlot();
-            if (arg < info().nargs()) {
-                if (!script()->formalIsAliased(arg)) {
-                    haveValue = true;
-                    existingValue = fp.unaliasedFormal(arg);
-                }
-            } else if (var < info().nlocals()) {
-                if (!script()->varIsAliased(var)) {
-                    haveValue = true;
-                    existingValue = fp.unaliasedVar(var);
-                }
-            }
-        }
-        if (haveValue) {
-            MIRType type = existingValue.isDouble()
-                           ? MIRType_Double
-                           : MIRTypeFromValueType(existingValue.extractNonDoubleType());
-            types::Type ntype = types::GetValueType(cx, existingValue);
-            types::StackTypeSet *typeSet =
-                GetIonContext()->temp->lifoAlloc()->new_<types::StackTypeSet>(ntype);
-            MergeTypes(&existingType, &existingTypeSet, type, typeSet);
-        }
-
-        if (existingTypeSet && !existingTypeSet->unknown()) {
-            MInstruction *barrier = MTypeBarrier::New(def, existingTypeSet);
-            osrBlock->add(barrier);
-            osrBlock->rewriteSlot(i, barrier);
-            def = barrier;
-        } else if (existingType == MIRType_Null ||
-                   existingType == MIRType_Undefined ||
-                   existingType == MIRType_Magic)
-        {
-            // No unbox instruction will be added below, so check the type by
-            // adding a type barrier for a singleton type set.
-            types::Type ntype = types::Type::PrimitiveType(ValueTypeFromMIRType(existingType));
-            existingTypeSet = GetIonContext()->temp->lifoAlloc()->new_<types::StackTypeSet>(ntype);
-            if (!existingTypeSet)
-                return NULL;
-            MInstruction *barrier = MTypeBarrier::New(def, existingTypeSet);
-            osrBlock->add(barrier);
-            osrBlock->rewriteSlot(i, barrier);
-            def = barrier;
-        }
-
-        switch (existingType) {
-          case MIRType_Boolean:
-          case MIRType_Int32:
-          case MIRType_Double:
-          case MIRType_String:
-          case MIRType_Object:
-          {
-            MInstruction *actual = MUnbox::New(def, existingType, MUnbox::Fallible);
-            osrBlock->add(actual);
-            osrBlock->rewriteSlot(i, actual);
-            break;
-          }
-
-          case MIRType_Null:
-          {
-            MConstant *c = MConstant::New(NullValue());
-            osrBlock->add(c);
-            osrBlock->rewriteSlot(i, c);
-            break;
-          }
-
-          case MIRType_Undefined:
-          {
-            MConstant *c = MConstant::New(UndefinedValue());
-            osrBlock->add(c);
-            osrBlock->rewriteSlot(i, c);
-            break;
-          }
-
-          case MIRType_Magic:
-            JS_ASSERT(lazyArguments_);
-            osrBlock->rewriteSlot(i, lazyArguments_);
-            break;
-
-          default:
-            break;
-        }
+        def->setResultType(existing->type());
+        def->setResultTypeSet(existing->resultTypeSet());
     }
 
     // Finish the osrBlock.
     osrBlock->end(MGoto::New(preheader));
     preheader->addPredecessor(osrBlock);
     graph().setOsrBlock(osrBlock);
 
     // Wrap |this| with a guaranteed use, to prevent instruction elimination.
     // Prevent |this| from being DCE'd: necessary for constructors.
     if (info().fun())
         preheader->getSlot(info().thisSlot())->setGuard();
 
     return preheader;
 }
 
 MBasicBlock *
-IonBuilder::newPendingLoopHeader(MBasicBlock *predecessor, jsbytecode *pc)
+IonBuilder::newPendingLoopHeader(MBasicBlock *predecessor, jsbytecode *pc, bool osr)
 {
     loopDepth_++;
     MBasicBlock *block = MBasicBlock::NewPendingLoopHeader(graph(), info(), predecessor, pc);
-    return addBlock(block, loopDepth_);
+    if (!addBlock(block, loopDepth_))
+        return NULL;
+
+    if (osr) {
+        // Incorporate type information from the OSR frame into the loop
+        // header. The OSR frame may have unexpected types due to type changes
+        // within the loop body or due to incomplete profiling information,
+        // in which case this may avoid restarts of loop analysis or bailouts
+        // during the OSR itself.
+
+        // Unbox the MOsrValue if it is known to be unboxable.
+        for (uint32_t i = 1; i < block->stackDepth(); i++) {
+            MPhi *phi = block->getSlot(i)->toPhi();
+
+            bool haveValue = false;
+            Value existingValue;
+            {
+                uint32_t arg = i - info().firstArgSlot();
+                uint32_t var = i - info().firstLocalSlot();
+                if (arg < info().nargs()) {
+                    if (!script()->formalIsAliased(arg)) {
+                        haveValue = true;
+                        existingValue = fp.unaliasedFormal(arg);
+                    }
+                } else if (var < info().nlocals()) {
+                    if (!script()->varIsAliased(var)) {
+                        haveValue = true;
+                        existingValue = fp.unaliasedVar(var);
+                    }
+                }
+            }
+            if (haveValue) {
+                MIRType type = existingValue.isDouble()
+                             ? MIRType_Double
+                             : MIRTypeFromValueType(existingValue.extractNonDoubleType());
+                types::Type ntype = types::GetValueType(cx, existingValue);
+                types::StackTypeSet *typeSet =
+                    GetIonContext()->temp->lifoAlloc()->new_<types::StackTypeSet>(ntype);
+                phi->addBackedgeType(type, typeSet);
+            }
+        }
+    }
+
+    return block;
 }
 
 // A resume point is a mapping of stack slots to MDefinitions. It is used to
 // capture the environment such that if a guard fails, and IonMonkey needs
 // to exit back to the interpreter, the interpreter state can be
 // reconstructed.
 //
 // We capture stack state at critical points:
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -87,16 +87,19 @@ class IonBuilder : public MIRGenerator
                 MBasicBlock *ifFalse;
                 jsbytecode *falseEnd;
                 MBasicBlock *ifTrue;    // Set when the end of the true path is reached.
             } branch;
             struct {
                 // Common entry point.
                 MBasicBlock *entry;
 
+                // Whether OSR is being performed for this loop.
+                bool osr;
+
                 // Position of where the loop body starts and ends.
                 jsbytecode *bodyStart;
                 jsbytecode *bodyEnd;
 
                 // pc immediately after the loop exits.
                 jsbytecode *exitpc;
 
                 // pc for 'continue' jumps.
@@ -232,30 +235,30 @@ class IonBuilder : public MIRGenerator
     ControlStatus processSwitchEnd(DeferredEdge *breaks, jsbytecode *exitpc);
     ControlStatus processAndOrEnd(CFGState &state);
     ControlStatus processLabelEnd(CFGState &state);
     ControlStatus processReturn(JSOp op);
     ControlStatus processThrow();
     ControlStatus processContinue(JSOp op);
     ControlStatus processBreak(JSOp op, jssrcnote *sn);
     ControlStatus maybeLoop(JSOp op, jssrcnote *sn);
-    bool pushLoop(CFGState::State state, jsbytecode *stopAt, MBasicBlock *entry,
+    bool pushLoop(CFGState::State state, jsbytecode *stopAt, MBasicBlock *entry, bool osr,
                   jsbytecode *loopHead, jsbytecode *initialPc,
                   jsbytecode *bodyStart, jsbytecode *bodyEnd, jsbytecode *exitpc,
                   jsbytecode *continuepc = NULL);
     void analyzeNewLoopTypes(MBasicBlock *entry, jsbytecode *start, jsbytecode *end);
 
     MBasicBlock *addBlock(MBasicBlock *block, uint32_t loopDepth);
     MBasicBlock *newBlock(MBasicBlock *predecessor, jsbytecode *pc);
     MBasicBlock *newBlock(MBasicBlock *predecessor, jsbytecode *pc, uint32_t loopDepth);
     MBasicBlock *newBlock(MBasicBlock *predecessor, jsbytecode *pc, MResumePoint *priorResumePoint);
     MBasicBlock *newBlockPopN(MBasicBlock *predecessor, jsbytecode *pc, uint32_t popped);
     MBasicBlock *newBlockAfter(MBasicBlock *at, MBasicBlock *predecessor, jsbytecode *pc);
     MBasicBlock *newOsrPreheader(MBasicBlock *header, jsbytecode *loopEntry);
-    MBasicBlock *newPendingLoopHeader(MBasicBlock *predecessor, jsbytecode *pc);
+    MBasicBlock *newPendingLoopHeader(MBasicBlock *predecessor, jsbytecode *pc, bool osr);
     MBasicBlock *newBlock(jsbytecode *pc) {
         return newBlock(NULL, pc);
     }
     MBasicBlock *newBlockAfter(MBasicBlock *at, jsbytecode *pc) {
         return newBlockAfter(at, NULL, pc);
     }
 
     // Given a list of pending breaks, creates a new block and inserts a Goto
@@ -265,16 +268,22 @@ class IonBuilder : public MIRGenerator
     // Finishes loops that do not actually loop, containing only breaks or
     // returns.
     ControlStatus processBrokenLoop(CFGState &state);
 
     // Computes loop phis, places them in all successors of a loop, then
     // handles any pending breaks.
     ControlStatus finishLoop(CFGState &state, MBasicBlock *successor);
 
+    // Incorporates a type/typeSet into an OSR value for a loop, after the loop
+    // body has been processed.
+    bool addOsrValueTypeBarrier(uint32_t slot, MInstruction **def,
+                                MIRType type, types::StackTypeSet *typeSet);
+    bool maybeAddOsrTypeBarriers();
+
     // Restarts processing of a loop if the type information at its header was
     // incomplete.
     ControlStatus restartLoop(CFGState state);
 
     void assertValidLoopHeadOp(jsbytecode *pc);
 
     ControlStatus forLoop(JSOp op, jssrcnote *sn);
     ControlStatus whileOrForInLoop(jssrcnote *sn);