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 140553 9aff2a52d88b0215e9a6045714b00ba328efb7f5
parent 140552 c946f7a3939716cdcce47255ff6ff6ae20363215
child 140554 63788e2eb007acbda2ce20cd1a16f9bdd69f46f9
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs863518
milestone23.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 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);