author | Brian Hackett <bhackett1024@gmail.com> |
Mon, 22 Apr 2013 20:22:30 -0600 | |
changeset 129606 | 9aff2a52d88b0215e9a6045714b00ba328efb7f5 |
parent 129605 | c946f7a3939716cdcce47255ff6ff6ae20363215 |
child 129607 | 63788e2eb007acbda2ce20cd1a16f9bdd69f46f9 |
push id | 24582 |
push user | ryanvm@gmail.com |
push date | Tue, 23 Apr 2013 19:03:31 +0000 |
treeherder | mozilla-central@8b1a7228674a [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | dvander |
bugs | 863518 |
milestone | 23.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
|
js/src/ion/IonBuilder.cpp | file | annotate | diff | comparison | revisions | |
js/src/ion/IonBuilder.h | file | annotate | diff | comparison | revisions |
--- 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);