LCOV - code coverage report
Current view: top level - js/src/jit - IonControlFlow.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 612 1052 58.2 %
Date: 2017-07-14 16:53:18 Functions: 46 60 76.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "jit/IonControlFlow.h"
       8             : 
       9             : #include "mozilla/DebugOnly.h"
      10             : #include "mozilla/SizePrintfMacros.h"
      11             : 
      12             : using namespace js;
      13             : using namespace js::jit;
      14             : using mozilla::DebugOnly;
      15             : 
      16         150 : ControlFlowGenerator::ControlFlowGenerator(TempAllocator& temp, JSScript* script)
      17             :   : script(script),
      18             :     current(nullptr),
      19             :     alloc_(temp),
      20             :     blocks_(temp),
      21             :     cfgStack_(temp),
      22             :     loops_(temp),
      23             :     switches_(temp),
      24             :     labels_(temp),
      25             :     analysis_(temp, script),
      26         150 :     aborted_(false)
      27         150 : { }
      28             : 
      29             : bool
      30         150 : ControlFlowGenerator::init()
      31             : {
      32         150 :     return analysis_.init(alloc(), gsn);
      33             : }
      34             : 
      35             : static inline int32_t
      36        1098 : GetJumpOffset(jsbytecode* pc)
      37             : {
      38        1098 :     MOZ_ASSERT(CodeSpec[JSOp(*pc)].type() == JOF_JUMP);
      39        1098 :     return GET_JUMP_OFFSET(pc);
      40             : }
      41             : 
      42             : void
      43           0 : ControlFlowGraph::dump(GenericPrinter& print, JSScript* script)
      44             : {
      45           0 :     if (blocks_.length() == 0) {
      46           0 :         print.printf("Didn't run yet.\n");
      47           0 :         return;
      48             :     }
      49             : 
      50           0 :     fprintf(stderr, "Dumping cfg:\n\n");
      51           0 :     for (size_t i = 0; i < blocks_.length(); i++) {
      52           0 :         print.printf(" Block %" PRIuSIZE ", %" PRIuSIZE ":%" PRIuSIZE "\n",
      53           0 :                      blocks_[i].id(),
      54           0 :                      script->pcToOffset(blocks_[i].startPc()),
      55           0 :                      script->pcToOffset(blocks_[i].stopPc()));
      56             : 
      57           0 :         jsbytecode* pc = blocks_[i].startPc();
      58           0 :         for ( ; pc < blocks_[i].stopPc(); pc += CodeSpec[JSOp(*pc)].length) {
      59           0 :             MOZ_ASSERT(pc < script->codeEnd());
      60           0 :             print.printf("  %" PRIuSIZE ": %s\n", script->pcToOffset(pc),
      61           0 :                                                   CodeName[JSOp(*pc)]);
      62             :         }
      63             : 
      64           0 :         if (blocks_[i].stopIns()->isGoto()) {
      65           0 :             print.printf("  %s (popping:%" PRIuSIZE ") [",
      66           0 :                          blocks_[i].stopIns()->Name(),
      67           0 :                          blocks_[i].stopIns()->toGoto()->popAmount());
      68             :         } else {
      69           0 :             print.printf("  %s [", blocks_[i].stopIns()->Name());
      70             :         }
      71           0 :         for (size_t j=0; j<blocks_[i].stopIns()->numSuccessors(); j++) {
      72           0 :             if (j!=0)
      73           0 :                 print.printf(", ");
      74           0 :             print.printf("%" PRIuSIZE, blocks_[i].stopIns()->getSuccessor(j)->id());
      75             :         }
      76           0 :         print.printf("]\n\n");
      77             :     }
      78             : }
      79             : 
      80             : bool
      81         150 : ControlFlowGraph::init(TempAllocator& alloc, const CFGBlockVector& blocks)
      82             : {
      83         150 :     if (!blocks_.reserve(blocks.length()))
      84           0 :         return false;
      85             : 
      86        2478 :     for (size_t i = 0; i < blocks.length(); i++) {
      87        2328 :         MOZ_ASSERT(blocks[i]->id() == i);
      88        2328 :         CFGBlock block(blocks[i]->startPc());
      89             : 
      90        2328 :         block.setStopPc(blocks[i]->stopPc());
      91        2328 :         block.setId(i);
      92        2328 :         blocks_.infallibleAppend(mozilla::Move(block));
      93             :     }
      94             : 
      95        2478 :     for (size_t i = 0; i < blocks.length(); i++) {
      96        2328 :         if (!alloc.ensureBallast())
      97           0 :             return false;
      98             : 
      99        2328 :         CFGControlInstruction* copy = nullptr;
     100        2328 :         CFGControlInstruction* ins = blocks[i]->stopIns();
     101        2328 :         switch (ins->type()) {
     102             :           case CFGControlInstruction::Type_Goto: {
     103         784 :             CFGBlock* successor = &blocks_[ins->getSuccessor(0)->id()];
     104         784 :             copy = CFGGoto::CopyWithNewTargets(alloc, ins->toGoto(), successor);
     105         784 :             break;
     106             :           }
     107             :           case CFGControlInstruction::Type_BackEdge: {
     108         117 :             CFGBlock* successor = &blocks_[ins->getSuccessor(0)->id()];
     109         117 :             copy = CFGBackEdge::CopyWithNewTargets(alloc, ins->toBackEdge(), successor);
     110         117 :             break;
     111             :           }
     112             :           case CFGControlInstruction::Type_LoopEntry: {
     113         117 :             CFGLoopEntry* old = ins->toLoopEntry();
     114         117 :             CFGBlock* successor = &blocks_[ins->getSuccessor(0)->id()];
     115         117 :             copy = CFGLoopEntry::CopyWithNewTargets(alloc, old, successor);
     116         117 :             break;
     117             :           }
     118             :           case CFGControlInstruction::Type_Throw: {
     119           1 :             copy = CFGThrow::New(alloc);
     120           1 :             break;
     121             :           }
     122             :           case CFGControlInstruction::Type_Test: {
     123         760 :             CFGTest* old = ins->toTest();
     124         760 :             CFGBlock* trueBranch = &blocks_[old->trueBranch()->id()];
     125         760 :             CFGBlock* falseBranch = &blocks_[old->falseBranch()->id()];
     126         760 :             copy = CFGTest::CopyWithNewTargets(alloc, old, trueBranch, falseBranch);
     127         760 :             break;
     128             :           }
     129             :           case CFGControlInstruction::Type_Compare: {
     130           0 :             CFGCompare* old = ins->toCompare();
     131           0 :             CFGBlock* trueBranch = &blocks_[old->trueBranch()->id()];
     132           0 :             CFGBlock* falseBranch = &blocks_[old->falseBranch()->id()];
     133           0 :             copy = CFGCompare::CopyWithNewTargets(alloc, old, trueBranch, falseBranch);
     134           0 :             break;
     135             :           }
     136             :           case CFGControlInstruction::Type_Return: {
     137         478 :             copy = CFGReturn::New(alloc);
     138         478 :             break;
     139             :           }
     140             :           case CFGControlInstruction::Type_RetRVal: {
     141          22 :             copy = CFGRetRVal::New(alloc);
     142          22 :             break;
     143             :           }
     144             :           case CFGControlInstruction::Type_Try: {
     145           5 :             CFGTry* old = ins->toTry();
     146           5 :             CFGBlock* tryBlock = &blocks_[old->tryBlock()->id()];
     147           5 :             CFGBlock* merge = nullptr;
     148           5 :             if (old->numSuccessors() == 2)
     149           5 :                 merge = &blocks_[old->afterTryCatchBlock()->id()];
     150           5 :             copy = CFGTry::CopyWithNewTargets(alloc, old, tryBlock, merge);
     151           5 :             break;
     152             :           }
     153             :           case CFGControlInstruction::Type_TableSwitch: {
     154          44 :             CFGTableSwitch* old = ins->toTableSwitch();
     155             :             CFGTableSwitch* tableSwitch =
     156          44 :                 CFGTableSwitch::New(alloc, old->low(), old->high());
     157          44 :             if (!tableSwitch->addDefault(&blocks_[old->defaultCase()->id()]))
     158           0 :                 return false;
     159         308 :             for (size_t i = 0; i < ins->numSuccessors() - 1; i++) {
     160         264 :                 if (!tableSwitch->addCase(&blocks_[old->getCase(i)->id()]))
     161           0 :                     return false;
     162             :             }
     163          44 :             copy = tableSwitch;
     164          44 :             break;
     165             :           }
     166             :         }
     167        2328 :         MOZ_ASSERT(copy);
     168        2328 :         blocks_[i].setStopIns(copy);
     169             :     }
     170         150 :     return true;
     171             : }
     172             : 
     173             : bool
     174        2328 : ControlFlowGenerator::addBlock(CFGBlock* block)
     175             : {
     176        2328 :     block->setId(blocks_.length());
     177        2328 :     return blocks_.append(block);
     178             : }
     179             : 
     180             : // We try to build a control-flow graph in the order that it would be built as
     181             : // if traversing the AST. This leads to a nice ordering and lets us build SSA
     182             : // in one pass, since the bytecode is structured.
     183             : //
     184             : // Things get interesting when we encounter a control structure. This can be
     185             : // either an IFEQ, downward GOTO, or a decompiler hint stashed away in source
     186             : // notes. Once we encounter such an opcode, we recover the structure of the
     187             : // control flow (its branches and bounds), and push it on a stack.
     188             : //
     189             : // As we continue traversing the bytecode, we look for points that would
     190             : // terminate the topmost control flow path pushed on the stack. These are:
     191             : //  (1) The bounds of the current structure (end of a loop or join/edge of a
     192             : //      branch).
     193             : //  (2) A "return", "break", or "continue" statement.
     194             : //
     195             : // For (1), we expect that there is a current block in the progress of being
     196             : // built, and we complete the necessary edges in the CFG. For (2), we expect
     197             : // that there is no active block.
     198             : bool
     199         150 : ControlFlowGenerator::traverseBytecode()
     200             : {
     201         150 :     blocks_.clear();
     202             : 
     203         150 :     current = CFGBlock::New(alloc(), script->code());
     204         150 :     pc = current->startPc();
     205             : 
     206         150 :     if (!addBlock(current))
     207           0 :         return false;
     208             : 
     209             :     for (;;) {
     210       17167 :         MOZ_ASSERT(pc < script->codeEnd());
     211             : 
     212             :         for (;;) {
     213       19387 :             if (!alloc().ensureBallast())
     214           0 :                 return false;
     215             : 
     216             :             // Check if we've hit an expected join point or edge in the bytecode.
     217             :             // Leaving one control structure could place us at the edge of another,
     218             :             // thus |while| instead of |if| so we don't skip any opcodes.
     219       19387 :             MOZ_ASSERT_IF(!cfgStack_.empty(), cfgStack_.back().stopAt >= pc);
     220       19387 :             if (!cfgStack_.empty() && cfgStack_.back().stopAt == pc) {
     221        1031 :                 ControlStatus status = processCfgStack();
     222        1031 :                 if (status == ControlStatus::Error)
     223           0 :                     return false;
     224        1031 :                 if (status == ControlStatus::Abort) {
     225           0 :                     aborted_ = true;
     226           0 :                     return false;
     227             :                 }
     228        1031 :                 if (!current)
     229           0 :                     return true;
     230        1031 :                 continue;
     231             :             }
     232             : 
     233             :             // Some opcodes need to be handled early because they affect control
     234             :             // flow, terminating the current basic block and/or instructing the
     235             :             // traversal algorithm to continue from a new pc.
     236             :             //
     237             :             //   (1) If the opcode does not affect control flow, then the opcode
     238             :             //       is inspected and transformed to IR. This is the process_opcode
     239             :             //       label.
     240             :             //   (2) A loop could be detected via a forward GOTO. In this case,
     241             :             //       we don't want to process the GOTO, but the following
     242             :             //       instruction.
     243             :             //   (3) A RETURN, STOP, BREAK, or CONTINUE may require processing the
     244             :             //       CFG stack to terminate open branches.
     245             :             //
     246             :             // Similar to above, snooping control flow could land us at another
     247             :             // control flow point, so we iterate until it's time to inspect a real
     248             :             // opcode.
     249             :             ControlStatus status;
     250       18356 :             if ((status = snoopControlFlow(JSOp(*pc))) == ControlStatus::None)
     251       17017 :                 break;
     252        1339 :             if (status == ControlStatus::Error)
     253           0 :                 return false;
     254        1339 :             if (status == ControlStatus::Abort) {
     255           0 :                 aborted_ = true;
     256           0 :                 return false;
     257             :             }
     258        1339 :             if (!current)
     259         150 :                 return true;
     260        2220 :         }
     261             : 
     262       17017 :         JSOp op = JSOp(*pc);
     263       17017 :         pc += CodeSpec[op].length;
     264       17017 :     }
     265             : 
     266             :     return true;
     267             : }
     268             : 
     269             : ControlFlowGenerator::ControlStatus
     270       18356 : ControlFlowGenerator::snoopControlFlow(JSOp op)
     271             : {
     272       18356 :     switch (op) {
     273             :       case JSOP_POP:
     274             :       case JSOP_NOP: {
     275        1825 :         jssrcnote* sn = GetSrcNote(gsn, script, pc);
     276        1825 :         return maybeLoop(op, sn);
     277             :       }
     278             : 
     279             :       case JSOP_RETURN:
     280             :       case JSOP_RETRVAL:
     281         500 :         return processReturn(op);
     282             : 
     283             :       case JSOP_THROW:
     284           1 :         return processThrow();
     285             : 
     286             :       case JSOP_GOTO:
     287             :       {
     288          41 :         jssrcnote* sn = GetSrcNote(gsn, script, pc);
     289          41 :         switch (sn ? SN_TYPE(sn) : SRC_NULL) {
     290             :           case SRC_BREAK:
     291             :           case SRC_BREAK2LABEL:
     292          18 :             return processBreak(op, sn);
     293             : 
     294             :           case SRC_CONTINUE:
     295          11 :             return processContinue(op);
     296             : 
     297             :           case SRC_SWITCHBREAK:
     298           0 :             return processSwitchBreak(op);
     299             : 
     300             :           case SRC_WHILE:
     301             :           case SRC_FOR_IN:
     302             :           case SRC_FOR_OF:
     303             :             // while (cond) { }
     304          12 :             return processWhileOrForInLoop(sn);
     305             : 
     306             :           default:
     307             :             // Hard assert for now - make an error later.
     308           0 :             MOZ_CRASH("unknown goto case");
     309             :         }
     310             :         break;
     311             :       }
     312             : 
     313             :       case JSOP_TABLESWITCH: {
     314          44 :         jssrcnote* sn = GetSrcNote(gsn, script, pc);
     315          44 :         return processTableSwitch(op, sn);
     316             :       }
     317             : 
     318             :       case JSOP_CONDSWITCH:
     319           0 :         return processCondSwitch();
     320             : 
     321             :       case JSOP_IFNE:
     322             :         // We should never reach an IFNE, it's a stopAt point, which will
     323             :         // trigger closing the loop.
     324           0 :         MOZ_CRASH("we should never reach an ifne!");
     325             : 
     326             :       case JSOP_IFEQ:
     327         597 :         return processIfStart(JSOP_IFEQ);
     328             : 
     329             :       case JSOP_AND:
     330             :       case JSOP_OR:
     331          46 :         return processAndOr(op);
     332             : 
     333             :       case JSOP_LABEL:
     334           0 :         return processLabel();
     335             : 
     336             :       case JSOP_TRY:
     337           5 :         return processTry();
     338             : 
     339             :       case JSOP_THROWMSG:
     340             :         // Not implemented yet.
     341           0 :         return ControlStatus::Abort;
     342             : 
     343             :       default:
     344       15297 :         break;
     345             :     }
     346       15297 :     return ControlStatus::None;
     347             : }
     348             : 
     349             : ControlFlowGenerator::ControlStatus
     350         500 : ControlFlowGenerator::processReturn(JSOp op)
     351             : {
     352         500 :     MOZ_ASSERT(op == JSOP_RETURN || op == JSOP_RETRVAL);
     353             : 
     354             :     CFGControlInstruction* ins;
     355         500 :     if (op == JSOP_RETURN)
     356         478 :         ins = CFGReturn::New(alloc());
     357             :     else
     358          22 :         ins = CFGRetRVal::New(alloc());
     359         500 :     endCurrentBlock(ins);
     360             : 
     361         500 :     return processControlEnd();
     362             : }
     363             : 
     364             : ControlFlowGenerator::ControlStatus
     365           1 : ControlFlowGenerator::processThrow()
     366             : {
     367           1 :     CFGThrow* ins = CFGThrow::New(alloc());
     368           1 :     endCurrentBlock(ins);
     369             : 
     370           1 :     return processControlEnd();
     371             : }
     372             : 
     373             : void
     374         501 : ControlFlowGenerator::endCurrentBlock(CFGControlInstruction* ins)
     375             : {
     376         501 :     current->setStopPc(pc);
     377         501 :     current->setStopIns(ins);
     378             : 
     379             :     // Make sure no one tries to use this block now.
     380         501 :     current = nullptr;
     381         501 : }
     382             : 
     383             : // Processes the top of the CFG stack. This is used from two places:
     384             : // (1) processControlEnd(), whereby a break, continue, or return may interrupt
     385             : //     an in-progress CFG structure before reaching its actual termination
     386             : //     point in the bytecode.
     387             : // (2) traverseBytecode(), whereby we reach the last instruction in a CFG
     388             : //     structure.
     389             : ControlFlowGenerator::ControlStatus
     390        1429 : ControlFlowGenerator::processCfgStack()
     391             : {
     392        1429 :     ControlStatus status = processCfgEntry(cfgStack_.back());
     393             : 
     394             :     // If this terminated a CFG structure, act like processControlEnd() and
     395             :     // keep propagating upward.
     396        1501 :     while (status == ControlStatus::Ended) {
     397          54 :         popCfgStack();
     398          54 :         if (cfgStack_.empty())
     399          18 :             return status;
     400          36 :         status = processCfgEntry(cfgStack_.back());
     401             :     }
     402             : 
     403             :     // If some join took place, the current structure is finished.
     404        1411 :     if (status == ControlStatus::Joined)
     405         755 :         popCfgStack();
     406             : 
     407        1411 :     return status;
     408             : }
     409             : 
     410             : // Given that the current control flow structure has ended forcefully,
     411             : // via a return, break, or continue (rather than joining), propagate the
     412             : // termination up. For example, a return nested 5 loops deep may terminate
     413             : // every outer loop at once, if there are no intervening conditionals:
     414             : //
     415             : // for (...) {
     416             : //   for (...) {
     417             : //     return x;
     418             : //   }
     419             : // }
     420             : //
     421             : // If |current| is nullptr when this function returns, then there is no more
     422             : // control flow to be processed.
     423             : ControlFlowGenerator::ControlStatus
     424         530 : ControlFlowGenerator::processControlEnd()
     425             : {
     426         530 :     MOZ_ASSERT(!current);
     427             : 
     428         530 :     if (cfgStack_.empty()) {
     429             :         // If there is no more control flow to process, then this is the
     430             :         // last return in the function.
     431         132 :         return ControlStatus::Ended;
     432             :     }
     433             : 
     434         398 :     return processCfgStack();
     435             : }
     436             : 
     437             : ControlFlowGenerator::ControlStatus
     438        1465 : ControlFlowGenerator::processCfgEntry(CFGState& state)
     439             : {
     440        1465 :     switch (state.state) {
     441             :       case CFGState::IF_TRUE:
     442             :       case CFGState::IF_TRUE_EMPTY_ELSE:
     443         427 :         return processIfEnd(state);
     444             : 
     445             :       case CFGState::IF_ELSE_TRUE:
     446         170 :         return processIfElseTrueEnd(state);
     447             : 
     448             :       case CFGState::IF_ELSE_FALSE:
     449         170 :         return processIfElseFalseEnd(state);
     450             : 
     451             :       case CFGState::DO_WHILE_LOOP_BODY:
     452           0 :         return processDoWhileBodyEnd(state);
     453             : 
     454             :       case CFGState::DO_WHILE_LOOP_COND:
     455           0 :         return processDoWhileCondEnd(state);
     456             : 
     457             :       case CFGState::WHILE_LOOP_COND:
     458          12 :         return processWhileCondEnd(state);
     459             : 
     460             :       case CFGState::WHILE_LOOP_BODY:
     461          12 :         return processWhileBodyEnd(state);
     462             : 
     463             :       case CFGState::FOR_LOOP_COND:
     464         105 :         return processForCondEnd(state);
     465             : 
     466             :       case CFGState::FOR_LOOP_BODY:
     467         105 :         return processForBodyEnd(state);
     468             : 
     469             :       case CFGState::FOR_LOOP_UPDATE:
     470         105 :         return processForUpdateEnd(state);
     471             : 
     472             :       case CFGState::TABLE_SWITCH:
     473         308 :         return processNextTableSwitchCase(state);
     474             : 
     475             :       case CFGState::COND_SWITCH_CASE:
     476           0 :         return processCondSwitchCase(state);
     477             : 
     478             :       case CFGState::COND_SWITCH_BODY:
     479           0 :         return processCondSwitchBody(state);
     480             : 
     481             :       case CFGState::AND_OR:
     482          46 :         return processAndOrEnd(state);
     483             : 
     484             :       case CFGState::LABEL:
     485           0 :         return processLabelEnd(state);
     486             : 
     487             :       case CFGState::TRY:
     488           5 :         return processTryEnd(state);
     489             : 
     490             :       default:
     491           0 :         MOZ_CRASH("unknown cfgstate");
     492             :     }
     493             : }
     494             : 
     495             : void
     496         809 : ControlFlowGenerator::popCfgStack()
     497             : {
     498         809 :     if (cfgStack_.back().isLoop())
     499         117 :         loops_.popBack();
     500         809 :     if (cfgStack_.back().state == CFGState::LABEL)
     501           0 :         labels_.popBack();
     502         809 :     cfgStack_.popBack();
     503         809 : }
     504             : 
     505             : ControlFlowGenerator::ControlStatus
     506           0 : ControlFlowGenerator::processLabelEnd(CFGState& state)
     507             : {
     508           0 :     MOZ_ASSERT(state.state == CFGState::LABEL);
     509             : 
     510             :     // If there are no breaks and no current, controlflow is terminated.
     511           0 :     if (!state.label.breaks && !current)
     512           0 :         return ControlStatus::Ended;
     513             : 
     514             :     // If there are no breaks to this label, there's nothing to do.
     515           0 :     if (!state.label.breaks)
     516           0 :         return ControlStatus::Joined;
     517             : 
     518           0 :     CFGBlock* successor = createBreakCatchBlock(state.label.breaks, state.stopAt);
     519           0 :     if (!successor)
     520           0 :         return ControlStatus::Error;
     521             : 
     522           0 :     if (current) {
     523           0 :         current->setStopIns(CFGGoto::New(alloc(), successor));
     524           0 :         current->setStopPc(pc);
     525             :     }
     526             : 
     527           0 :     current = successor;
     528           0 :     pc = successor->startPc();
     529             : 
     530           0 :     if (!addBlock(successor))
     531           0 :         return ControlStatus::Error;
     532             : 
     533           0 :     return ControlStatus::Joined;
     534             : }
     535             : 
     536             : ControlFlowGenerator::ControlStatus
     537           5 : ControlFlowGenerator::processTry()
     538             : {
     539           5 :     MOZ_ASSERT(JSOp(*pc) == JSOP_TRY);
     540             : 
     541             :     // Try-finally is not yet supported.
     542           5 :     if (analysis_.hasTryFinally())
     543           0 :         return ControlStatus::Abort;
     544             : 
     545           5 :     jssrcnote* sn = GetSrcNote(gsn, script, pc);
     546           5 :     MOZ_ASSERT(SN_TYPE(sn) == SRC_TRY);
     547             : 
     548             :     // Get the pc of the last instruction in the try block. It's a JSOP_GOTO to
     549             :     // jump over the catch block.
     550           5 :     jsbytecode* endpc = pc + GetSrcNoteOffset(sn, 0);
     551           5 :     MOZ_ASSERT(JSOp(*endpc) == JSOP_GOTO);
     552           5 :     MOZ_ASSERT(GetJumpOffset(endpc) > 0);
     553             : 
     554           5 :     jsbytecode* afterTry = endpc + GetJumpOffset(endpc);
     555             : 
     556             :     // If controlflow in the try body is terminated (by a return or throw
     557             :     // statement), the code after the try-statement may still be reachable
     558             :     // via the catch block (which we don't compile) and OSR can enter it.
     559             :     // For example:
     560             :     //
     561             :     //     try {
     562             :     //         throw 3;
     563             :     //     } catch(e) { }
     564             :     //
     565             :     //     for (var i=0; i<1000; i++) {}
     566             :     //
     567             :     // To handle this, we create two blocks: one for the try block and one
     568             :     // for the code following the try-catch statement.
     569             :     //
     570             :     // If the code after the try block is unreachable (control flow in both the
     571             :     // try and catch blocks is terminated), only create the try block, to avoid
     572             :     // parsing unreachable code.
     573             : 
     574           5 :     CFGBlock* tryBlock = CFGBlock::New(alloc(), GetNextPc(pc));
     575             : 
     576             :     CFGBlock* successor;
     577           5 :     if (analysis_.maybeInfo(afterTry)) {
     578           5 :         successor = CFGBlock::New(alloc(), afterTry);
     579           5 :         current->setStopIns(CFGTry::New(alloc(), tryBlock, endpc, successor));
     580             :     } else {
     581           0 :         successor = nullptr;
     582           0 :         current->setStopIns(CFGTry::New(alloc(), tryBlock, endpc));
     583             :     }
     584           5 :     current->setStopPc(pc);
     585             : 
     586           5 :     if (!cfgStack_.append(CFGState::Try(endpc, successor)))
     587           0 :         return ControlStatus::Error;
     588             : 
     589           5 :     current = tryBlock;
     590           5 :     pc = current->startPc();
     591             : 
     592           5 :     if (!addBlock(current))
     593           0 :         return ControlStatus::Error;
     594             : 
     595           5 :     return ControlStatus::Jumped;
     596             : }
     597             : 
     598             : ControlFlowGenerator::ControlStatus
     599           5 : ControlFlowGenerator::processTryEnd(CFGState& state)
     600             : {
     601           5 :     MOZ_ASSERT(state.state == CFGState::TRY);
     602             : 
     603           5 :     if (!state.try_.successor) {
     604           0 :         MOZ_ASSERT(!current);
     605           0 :         return ControlStatus::Ended;
     606             :     }
     607             : 
     608           5 :     if (current) {
     609           5 :         current->setStopIns(CFGGoto::New(alloc(), state.try_.successor));
     610           5 :         current->setStopPc(pc);
     611             :     }
     612             : 
     613             :     // Start parsing the code after this try-catch statement.
     614           5 :     current = state.try_.successor;
     615           5 :     pc = current->startPc();
     616             : 
     617           5 :     if (!addBlock(current))
     618           0 :         return ControlStatus::Error;
     619             : 
     620           5 :     return ControlStatus::Joined;
     621             : }
     622             : 
     623             : ControlFlowGenerator::ControlStatus
     624         427 : ControlFlowGenerator::processIfEnd(CFGState& state)
     625             : {
     626         427 :     if (current) {
     627             :         // Here, the false block is the join point. Create an edge from the
     628             :         // current block to the false block. Note that a RETURN opcode
     629             :         // could have already ended the block.
     630         329 :         current->setStopIns(CFGGoto::New(alloc(), state.branch.ifFalse));
     631         329 :         current->setStopPc(pc);
     632             :     }
     633             : 
     634         427 :     current = state.branch.ifFalse;
     635         427 :     pc = current->startPc();
     636             : 
     637         427 :     if (!addBlock(current))
     638           0 :         return ControlStatus::Error;
     639             : 
     640         427 :     return ControlStatus::Joined;
     641             : }
     642             : 
     643             : ControlFlowGenerator::ControlStatus
     644         170 : ControlFlowGenerator::processIfElseTrueEnd(CFGState& state)
     645             : {
     646             :     // We've reached the end of the true branch of an if-else. Don't
     647             :     // create an edge yet, just transition to parsing the false branch.
     648         170 :     state.state = CFGState::IF_ELSE_FALSE;
     649         170 :     state.branch.ifTrue = current;
     650         170 :     state.stopAt = state.branch.falseEnd;
     651             : 
     652         170 :     if (current)
     653         152 :         current->setStopPc(pc);
     654             : 
     655         170 :     current = state.branch.ifFalse;
     656         170 :     pc = current->startPc();
     657             : 
     658         170 :     if (!addBlock(current))
     659           0 :         return ControlStatus::Error;
     660             : 
     661         170 :     return ControlStatus::Jumped;
     662             : }
     663             : 
     664             : ControlFlowGenerator::ControlStatus
     665         170 : ControlFlowGenerator::processIfElseFalseEnd(CFGState& state)
     666             : {
     667             :     // Update the state to have the latest block from the false path.
     668         170 :     state.branch.ifFalse = current;
     669         170 :     if (current)
     670         152 :         current->setStopPc(pc);
     671             : 
     672             :     // To create the join node, we need an incoming edge that has not been
     673             :     // terminated yet.
     674         170 :     CFGBlock* pred = state.branch.ifTrue
     675         170 :                         ? state.branch.ifTrue
     676         170 :                         : state.branch.ifFalse;
     677         170 :     CFGBlock* other = (pred == state.branch.ifTrue)
     678         170 :                         ? state.branch.ifFalse
     679         170 :                         : state.branch.ifTrue;
     680             : 
     681         170 :     if (!pred)
     682          18 :         return ControlStatus::Ended;
     683             : 
     684             :     // Create a new block to represent the join.
     685         152 :     CFGBlock* join = CFGBlock::New(alloc(), state.branch.falseEnd);
     686             : 
     687             :     // Create edges from the true and false blocks as needed.
     688         152 :     pred->setStopIns(CFGGoto::New(alloc(), join));
     689             : 
     690         152 :     if (other)
     691         152 :         other->setStopIns(CFGGoto::New(alloc(), join));
     692             : 
     693             :     // Ignore unreachable remainder of false block if existent.
     694         152 :     current = join;
     695         152 :     pc = current->startPc();
     696             : 
     697         152 :     if (!addBlock(current))
     698           0 :         return ControlStatus::Error;
     699             : 
     700         152 :     return ControlStatus::Joined;
     701             : }
     702             : 
     703             : CFGBlock*
     704          16 : ControlFlowGenerator::createBreakCatchBlock(DeferredEdge* edge, jsbytecode* pc)
     705             : {
     706             :     // Create block, using the first break statement as predecessor
     707          16 :     CFGBlock* successor = CFGBlock::New(alloc(), pc);
     708             : 
     709             :     // Finish up remaining breaks.
     710          52 :     while (edge) {
     711          18 :         if (!alloc().ensureBallast())
     712           0 :             return nullptr;
     713             : 
     714          18 :         CFGGoto* brk = CFGGoto::New(alloc(), successor);
     715          18 :         edge->block->setStopIns(brk);
     716          18 :         edge = edge->next;
     717             :     }
     718             : 
     719          16 :     return successor;
     720             : }
     721             : 
     722             : ControlFlowGenerator::ControlStatus
     723           0 : ControlFlowGenerator::processDoWhileBodyEnd(CFGState& state)
     724             : {
     725           0 :     if (!processDeferredContinues(state))
     726           0 :         return ControlStatus::Error;
     727             : 
     728             :     // No current means control flow cannot reach the condition, so this will
     729             :     // never loop.
     730           0 :     if (!current)
     731           0 :         return processBrokenLoop(state);
     732             : 
     733           0 :     CFGBlock* header = CFGBlock::New(alloc(), state.loop.updatepc);
     734           0 :     current->setStopIns(CFGGoto::New(alloc(), header));
     735           0 :     current->setStopPc(pc);
     736             : 
     737           0 :     state.state = CFGState::DO_WHILE_LOOP_COND;
     738           0 :     state.stopAt = state.loop.updateEnd;
     739             : 
     740           0 :     current = header;
     741           0 :     pc = header->startPc();
     742             : 
     743           0 :     if (!addBlock(current))
     744           0 :         return ControlStatus::Error;
     745             : 
     746           0 :     return ControlStatus::Jumped;
     747             : }
     748             : 
     749             : ControlFlowGenerator::ControlStatus
     750           0 : ControlFlowGenerator::processDoWhileCondEnd(CFGState& state)
     751             : {
     752           0 :     MOZ_ASSERT(JSOp(*pc) == JSOP_IFNE);
     753             : 
     754             :     // We're guaranteed a |current|, it's impossible to break or return from
     755             :     // inside the conditional expression.
     756           0 :     MOZ_ASSERT(current);
     757             : 
     758             :     // Create the successor block.
     759           0 :     CFGBlock* successor = CFGBlock::New(alloc(), GetNextPc(pc));
     760             : 
     761           0 :     CFGLoopEntry* entry = state.loop.entry->stopIns()->toLoopEntry();
     762           0 :     entry->setLoopStopPc(pc);
     763             : 
     764             :     // Create backedge with pc at start of loop to make sure we capture the
     765             :     // right stack.
     766           0 :     CFGBlock* backEdge = CFGBlock::New(alloc(), entry->successor()->startPc());
     767           0 :     backEdge->setStopIns(CFGBackEdge::New(alloc(), entry->successor()));
     768           0 :     backEdge->setStopPc(entry->successor()->startPc());
     769             : 
     770           0 :     if (!addBlock(backEdge))
     771           0 :         return ControlStatus::Error;
     772             : 
     773             :     // Create the test instruction and end the current block.
     774           0 :     CFGTest* test = CFGTest::New(alloc(), backEdge, successor);
     775           0 :     current->setStopIns(test);
     776           0 :     current->setStopPc(pc);
     777           0 :     return finishLoop(state, successor);
     778             : }
     779             : 
     780             : ControlFlowGenerator::ControlStatus
     781          12 : ControlFlowGenerator::processWhileCondEnd(CFGState& state)
     782             : {
     783          12 :     MOZ_ASSERT(JSOp(*pc) == JSOP_IFNE || JSOp(*pc) == JSOP_IFEQ);
     784             : 
     785             :     // Create the body and successor blocks.
     786          12 :     CFGBlock* body = CFGBlock::New(alloc(), state.loop.bodyStart);
     787          12 :     state.loop.successor = CFGBlock::New(alloc(), state.loop.exitpc);
     788          12 :     if (!body || !state.loop.successor)
     789           0 :         return ControlStatus::Error;
     790             : 
     791             :     CFGTest* test;
     792          12 :     if (JSOp(*pc) == JSOP_IFNE)
     793           9 :         test = CFGTest::New(alloc(), body, state.loop.successor);
     794             :     else
     795           3 :         test = CFGTest::New(alloc(), state.loop.successor, body);
     796          12 :     current->setStopIns(test);
     797          12 :     current->setStopPc(pc);
     798             : 
     799          12 :     state.state = CFGState::WHILE_LOOP_BODY;
     800          12 :     state.stopAt = state.loop.bodyEnd;
     801             : 
     802          12 :     current = body;
     803          12 :     pc = body->startPc();
     804             : 
     805          12 :     if (!addBlock(current))
     806           0 :         return ControlStatus::Error;
     807             : 
     808          12 :     return ControlStatus::Jumped;
     809             : }
     810             : 
     811             : ControlFlowGenerator::ControlStatus
     812          12 : ControlFlowGenerator::processWhileBodyEnd(CFGState& state)
     813             : {
     814          12 :     if (!processDeferredContinues(state))
     815           0 :         return ControlStatus::Error;
     816             : 
     817          12 :     if (!current)
     818           0 :         return processBrokenLoop(state);
     819             : 
     820          12 :     CFGLoopEntry* entry = state.loop.entry->stopIns()->toLoopEntry();
     821          12 :     entry->setLoopStopPc(pc);
     822             : 
     823          12 :     current->setStopIns(CFGBackEdge::New(alloc(), entry->successor()));
     824          12 :     if (pc != current->startPc()) {
     825          12 :         current->setStopPc(pc);
     826             :     } else {
     827             :         // If the block is empty update the pc to the start of loop to make
     828             :         // sure we capture the right stack.
     829           0 :         current->setStartPc(entry->successor()->startPc());
     830           0 :         current->setStopPc(entry->successor()->startPc());
     831             :     }
     832          12 :     return finishLoop(state, state.loop.successor);
     833             : }
     834             : 
     835             : ControlFlowGenerator::ControlStatus
     836         105 : ControlFlowGenerator::processForCondEnd(CFGState& state)
     837             : {
     838         105 :     MOZ_ASSERT(JSOp(*pc) == JSOP_IFNE);
     839             : 
     840             :     // Create the body and successor blocks.
     841         105 :     CFGBlock* body = CFGBlock::New(alloc(), state.loop.bodyStart);
     842         105 :     state.loop.successor = CFGBlock::New(alloc(), state.loop.exitpc);
     843             : 
     844         105 :     CFGTest* test = CFGTest::New(alloc(), body, state.loop.successor);
     845         105 :     current->setStopIns(test);
     846         105 :     current->setStopPc(pc);
     847             : 
     848         105 :     state.state = CFGState::FOR_LOOP_BODY;
     849         105 :     state.stopAt = state.loop.bodyEnd;
     850             : 
     851         105 :     current = body;
     852         105 :     pc = body->startPc();
     853             : 
     854         105 :     if (!addBlock(current))
     855           0 :         return ControlStatus::Error;
     856             : 
     857         105 :     return ControlStatus::Jumped;
     858             : }
     859             : 
     860             : ControlFlowGenerator::ControlStatus
     861         105 : ControlFlowGenerator::processForBodyEnd(CFGState& state)
     862             : {
     863         105 :     if (!processDeferredContinues(state))
     864           0 :         return ControlStatus::Error;
     865             : 
     866             :     // If there is no updatepc, just go right to processing what would be the
     867             :     // end of the update clause. Otherwise, |current| might be nullptr; if this is
     868             :     // the case, the update is unreachable anyway.
     869         105 :     if (!state.loop.updatepc || !current)
     870           0 :         return processForUpdateEnd(state);
     871             : 
     872             :     //MOZ_ASSERT(pc == state.loop.updatepc);
     873             : 
     874         105 :     if (state.loop.updatepc != pc) {
     875           0 :         CFGBlock* next = CFGBlock::New(alloc(), state.loop.updatepc);
     876           0 :         current->setStopIns(CFGGoto::New(alloc(), next));
     877           0 :         current->setStopPc(pc);
     878           0 :         current = next;
     879             : 
     880           0 :         if (!addBlock(current))
     881           0 :             return ControlStatus::Error;
     882             :     }
     883             : 
     884         105 :     pc = state.loop.updatepc;
     885             : 
     886         105 :     state.state = CFGState::FOR_LOOP_UPDATE;
     887         105 :     state.stopAt = state.loop.updateEnd;
     888         105 :     return ControlStatus::Jumped;
     889             : }
     890             : 
     891             : ControlFlowGenerator::ControlStatus
     892         105 : ControlFlowGenerator::processForUpdateEnd(CFGState& state)
     893             : {
     894             :     // If there is no current, we couldn't reach the loop edge and there was no
     895             :     // update clause.
     896         105 :     if (!current)
     897           0 :         return processBrokenLoop(state);
     898             : 
     899         105 :     CFGLoopEntry* entry = state.loop.entry->stopIns()->toLoopEntry();
     900         105 :     entry->setLoopStopPc(pc);
     901             : 
     902         105 :     current->setStopIns(CFGBackEdge::New(alloc(), entry->successor()));
     903         105 :     if (pc != current->startPc()) {
     904         105 :         current->setStopPc(pc);
     905             :     } else {
     906             :         // If the block is empty update the pc to the start of loop to make
     907             :         // sure we capture the right stack.
     908           0 :         current->setStartPc(entry->successor()->startPc());
     909           0 :         current->setStopPc(entry->successor()->startPc());
     910             :     }
     911         105 :     return finishLoop(state, state.loop.successor);
     912             : }
     913             : 
     914             : ControlFlowGenerator::ControlStatus
     915          12 : ControlFlowGenerator::processWhileOrForInLoop(jssrcnote* sn)
     916             : {
     917             :     // while (cond) { } loops have the following structure:
     918             :     //    GOTO cond   ; SRC_WHILE (offset to IFNE)
     919             :     //    LOOPHEAD
     920             :     //    ...
     921             :     //  cond:
     922             :     //    LOOPENTRY
     923             :     //    ...
     924             :     //    IFNE        ; goes to LOOPHEAD
     925             :     // for (x in y) { } loops are similar; the cond will be a MOREITER.
     926          12 :     MOZ_ASSERT(SN_TYPE(sn) == SRC_FOR_OF || SN_TYPE(sn) == SRC_FOR_IN || SN_TYPE(sn) == SRC_WHILE);
     927          12 :     int ifneOffset = GetSrcNoteOffset(sn, 0);
     928          12 :     jsbytecode* ifne = pc + ifneOffset;
     929          12 :     MOZ_ASSERT(ifne > pc);
     930             : 
     931             :     // Verify that the IFNE goes back to a loophead op.
     932          12 :     MOZ_ASSERT(JSOp(*GetNextPc(pc)) == JSOP_LOOPHEAD);
     933          12 :     MOZ_ASSERT(GetNextPc(pc) == ifne + GetJumpOffset(ifne));
     934             : 
     935          12 :     jsbytecode* loopEntry = pc + GetJumpOffset(pc);
     936             : 
     937             :     size_t stackPhiCount;
     938          12 :     if (SN_TYPE(sn) == SRC_FOR_OF)
     939           3 :         stackPhiCount = 2;
     940           9 :     else if (SN_TYPE(sn) == SRC_FOR_IN)
     941           0 :         stackPhiCount = 1;
     942             :     else
     943           9 :         stackPhiCount = 0;
     944             : 
     945             :     // Skip past the JSOP_LOOPHEAD for the body start.
     946          12 :     jsbytecode* loopHead = GetNextPc(pc);
     947          12 :     jsbytecode* bodyStart = GetNextPc(loopHead);
     948          12 :     jsbytecode* bodyEnd = pc + GetJumpOffset(pc);
     949          12 :     jsbytecode* exitpc = GetNextPc(ifne);
     950          12 :     jsbytecode* continuepc = pc;
     951             : 
     952          12 :     CFGBlock* header = CFGBlock::New(alloc(), GetNextPc(loopEntry));
     953             : 
     954          12 :     CFGLoopEntry* ins = CFGLoopEntry::New(alloc(), header, stackPhiCount);
     955          12 :     if (LoopEntryCanIonOsr(loopEntry))
     956          12 :         ins->setCanOsr();
     957             : 
     958          12 :     current->setStopIns(ins);
     959          12 :     current->setStopPc(pc);
     960             : 
     961          12 :     if (!pushLoop(CFGState::WHILE_LOOP_COND, ifne, current,
     962             :                   loopHead, bodyEnd, bodyStart, bodyEnd, exitpc, continuepc))
     963             :     {
     964           0 :         return ControlStatus::Error;
     965             :     }
     966             : 
     967             :     // Parse the condition first.
     968          12 :     current = header;
     969          12 :     pc = header->startPc();
     970             : 
     971          12 :     if (!addBlock(current))
     972           0 :         return ControlStatus::Error;
     973             : 
     974          12 :     return ControlStatus::Jumped;
     975             : }
     976             : 
     977             : ControlFlowGenerator::ControlStatus
     978           0 : ControlFlowGenerator::processBrokenLoop(CFGState& state)
     979             : {
     980           0 :     MOZ_ASSERT(!current);
     981             : 
     982             :     {
     983           0 :         state.loop.entry->setStopIns(
     984           0 :             CFGGoto::New(alloc(), state.loop.entry->stopIns()->toLoopEntry()->successor()));
     985             :     }
     986             : 
     987             :     // If the loop started with a condition (while/for) then even if the
     988             :     // structure never actually loops, the condition itself can still fail and
     989             :     // thus we must resume at the successor, if one exists.
     990           0 :     current = state.loop.successor;
     991           0 :     if (current) {
     992           0 :         if (!addBlock(current))
     993           0 :             return ControlStatus::Error;
     994             :     }
     995             : 
     996             :     // Join the breaks together and continue parsing.
     997           0 :     if (state.loop.breaks) {
     998           0 :         CFGBlock* block = createBreakCatchBlock(state.loop.breaks, state.loop.exitpc);
     999           0 :         if (!block)
    1000           0 :             return ControlStatus::Error;
    1001             : 
    1002           0 :         if (current) {
    1003           0 :             current->setStopIns(CFGGoto::New(alloc(), block));
    1004           0 :             current->setStopPc(current->startPc());
    1005             :         }
    1006             : 
    1007           0 :         current = block;
    1008             : 
    1009           0 :         if (!addBlock(current))
    1010           0 :             return ControlStatus::Error;
    1011             :     }
    1012             : 
    1013             :     // If the loop is not gated on a condition, and has only returns, we'll
    1014             :     // reach this case. For example:
    1015             :     // do { ... return; } while ();
    1016           0 :     if (!current)
    1017           0 :         return ControlStatus::Ended;
    1018             : 
    1019             :     // Otherwise, the loop is gated on a condition and/or has breaks so keep
    1020             :     // parsing at the successor.
    1021           0 :     pc = current->startPc();
    1022           0 :     return ControlStatus::Joined;
    1023             : }
    1024             : 
    1025             : ControlFlowGenerator::ControlStatus
    1026         117 : ControlFlowGenerator::finishLoop(CFGState& state, CFGBlock* successor)
    1027             : {
    1028         117 :     MOZ_ASSERT(current);
    1029             : 
    1030         117 :     if (state.loop.breaks) {
    1031          16 :         if (successor) {
    1032          16 :             if (!addBlock(successor))
    1033           0 :                 return ControlStatus::Error;
    1034             :         }
    1035             : 
    1036             :         // Create a catch block to join all break exits.
    1037          16 :         CFGBlock* block = createBreakCatchBlock(state.loop.breaks, state.loop.exitpc);
    1038          16 :         if (!block)
    1039           0 :             return ControlStatus::Error;
    1040             : 
    1041          16 :         if (successor) {
    1042             :             // Finally, create an unconditional edge from the successor to the
    1043             :             // catch block.
    1044          16 :             successor->setStopIns(CFGGoto::New(alloc(), block));
    1045          16 :             successor->setStopPc(successor->startPc());
    1046             :         }
    1047          16 :         successor = block;
    1048             :     }
    1049             : 
    1050             :     // An infinite loop (for (;;) { }) will not have a successor.
    1051         117 :     if (!successor) {
    1052           0 :         current = nullptr;
    1053           0 :         return ControlStatus::Ended;
    1054             :     }
    1055             : 
    1056         117 :     current = successor;
    1057         117 :     pc = current->startPc();
    1058             : 
    1059         117 :     if (!addBlock(current))
    1060           0 :         return ControlStatus::Error;
    1061             : 
    1062         117 :     return ControlStatus::Joined;
    1063             : }
    1064             : 
    1065             : bool
    1066         117 : ControlFlowGenerator::processDeferredContinues(CFGState& state)
    1067             : {
    1068             :     // If there are any continues for this loop, and there is an update block,
    1069             :     // then we need to create a new basic block to house the update.
    1070         117 :     if (state.loop.continues) {
    1071           1 :         DeferredEdge* edge = state.loop.continues;
    1072             : 
    1073           1 :         CFGBlock* update = CFGBlock::New(alloc(), pc);
    1074             : 
    1075           1 :         if (current) {
    1076           1 :             current->setStopIns(CFGGoto::New(alloc(), update));
    1077           1 :             current->setStopPc(pc);
    1078             :         }
    1079             : 
    1080             :         // Remaining edges
    1081          23 :         while (edge) {
    1082          11 :             if (!alloc().ensureBallast())
    1083           0 :                 return false;
    1084          11 :             edge->block->setStopIns(CFGGoto::New(alloc(), update));
    1085          11 :             edge = edge->next;
    1086             :         }
    1087           1 :         state.loop.continues = nullptr;
    1088             : 
    1089           1 :         current = update;
    1090           1 :         if (!addBlock(current))
    1091           0 :             return false;
    1092             :     }
    1093             : 
    1094         117 :     return true;
    1095             : }
    1096             : 
    1097             : ControlFlowGenerator::ControlStatus
    1098           0 : ControlFlowGenerator::processCondSwitch()
    1099             : {
    1100             :     // CondSwitch op looks as follows:
    1101             :     //   condswitch [length +exit_pc; first case offset +next-case ]
    1102             :     //   {
    1103             :     //     {
    1104             :     //       ... any code ...
    1105             :     //       case (+jump) [pcdelta offset +next-case]
    1106             :     //     }+
    1107             :     //     default (+jump)
    1108             :     //     ... jump targets ...
    1109             :     //   }
    1110             :     //
    1111             :     // The default case is always emitted even if there is no default case in
    1112             :     // the source.  The last case statement pcdelta source note might have a 0
    1113             :     // offset on the last case (not all the time).
    1114             :     //
    1115             :     // A conditional evaluate the condition of each case and compare it to the
    1116             :     // switch value with a strict equality.  Cases conditions are iterated
    1117             :     // linearly until one is matching. If one case succeeds, the flow jumps into
    1118             :     // the corresponding body block.  The body block might alias others and
    1119             :     // might continue in the next body block if the body is not terminated with
    1120             :     // a break.
    1121             :     //
    1122             :     // Algorithm:
    1123             :     //  1/ Loop over the case chain to reach the default target
    1124             :     //   & Estimate the number of uniq bodies.
    1125             :     //  2/ Generate code for all cases (see processCondSwitchCase).
    1126             :     //  3/ Generate code for all bodies (see processCondSwitchBody).
    1127             : 
    1128           0 :     MOZ_ASSERT(JSOp(*pc) == JSOP_CONDSWITCH);
    1129           0 :     jssrcnote* sn = GetSrcNote(gsn, script, pc);
    1130           0 :     MOZ_ASSERT(SN_TYPE(sn) == SRC_CONDSWITCH);
    1131             : 
    1132             :     // Get the exit pc
    1133           0 :     jsbytecode* exitpc = pc + GetSrcNoteOffset(sn, 0);
    1134           0 :     jsbytecode* firstCase = pc + GetSrcNoteOffset(sn, 1);
    1135             : 
    1136             :     // Iterate all cases in the conditional switch.
    1137             :     // - Stop at the default case. (always emitted after the last case)
    1138             :     // - Estimate the number of uniq bodies. This estimation might be off by 1
    1139             :     //   if the default body alias a case body.
    1140           0 :     jsbytecode* curCase = firstCase;
    1141           0 :     jsbytecode* lastTarget = GetJumpOffset(curCase) + curCase;
    1142           0 :     size_t nbBodies = 1; // default target and the first body.
    1143             : 
    1144           0 :     MOZ_ASSERT(pc < curCase && curCase <= exitpc);
    1145           0 :     while (JSOp(*curCase) == JSOP_CASE) {
    1146             :         // Fetch the next case.
    1147           0 :         jssrcnote* caseSn = GetSrcNote(gsn, script, curCase);
    1148           0 :         MOZ_ASSERT(caseSn && SN_TYPE(caseSn) == SRC_NEXTCASE);
    1149           0 :         ptrdiff_t off = GetSrcNoteOffset(caseSn, 0);
    1150           0 :         MOZ_ASSERT_IF(off == 0, JSOp(*GetNextPc(curCase)) == JSOP_JUMPTARGET);
    1151           0 :         curCase = off ? curCase + off : GetNextPc(GetNextPc(curCase));
    1152           0 :         MOZ_ASSERT(pc < curCase && curCase <= exitpc);
    1153             : 
    1154             :         // Count non-aliased cases.
    1155           0 :         jsbytecode* curTarget = GetJumpOffset(curCase) + curCase;
    1156           0 :         if (lastTarget < curTarget)
    1157           0 :             nbBodies++;
    1158           0 :         lastTarget = curTarget;
    1159             :     }
    1160             : 
    1161             :     // The current case now be the default case which jump to the body of the
    1162             :     // default case, which might be behind the last target.
    1163           0 :     MOZ_ASSERT(JSOp(*curCase) == JSOP_DEFAULT);
    1164           0 :     jsbytecode* defaultTarget = GetJumpOffset(curCase) + curCase;
    1165           0 :     MOZ_ASSERT(curCase < defaultTarget && defaultTarget <= exitpc);
    1166             : 
    1167             :     // Iterate over all cases again to find the position of the default block.
    1168           0 :     curCase = firstCase;
    1169           0 :     lastTarget = nullptr;
    1170           0 :     size_t defaultIdx = 0;
    1171           0 :     while (JSOp(*curCase) == JSOP_CASE) {
    1172           0 :         jsbytecode* curTarget = GetJumpOffset(curCase) + curCase;
    1173           0 :         if (lastTarget < defaultTarget && defaultTarget <= curTarget) {
    1174           0 :             if (defaultTarget < curTarget)
    1175           0 :                 nbBodies++;
    1176           0 :             break;
    1177             :         }
    1178           0 :         if (lastTarget < curTarget)
    1179           0 :             defaultIdx++;
    1180             : 
    1181           0 :         jssrcnote* caseSn = GetSrcNote(gsn, script, curCase);
    1182           0 :         ptrdiff_t off = GetSrcNoteOffset(caseSn, 0);
    1183           0 :         curCase = off ? curCase + off : GetNextPc(GetNextPc(curCase));
    1184           0 :         lastTarget = curTarget;
    1185             :     }
    1186             : 
    1187             :     // Allocate the current graph state.
    1188           0 :     CFGState state = CFGState::CondSwitch(alloc(), exitpc, defaultTarget);
    1189           0 :     if (!state.switch_.bodies || !state.switch_.bodies->init(alloc(), nbBodies))
    1190           0 :         return ControlStatus::Error;
    1191           0 :     state.switch_.defaultIdx = defaultIdx;
    1192             : 
    1193             :     // Create the default case already.
    1194           0 :     FixedList<CFGBlock*>& bodies = *state.switch_.bodies;
    1195           0 :     bodies[state.switch_.defaultIdx] = CFGBlock::New(alloc(), defaultTarget);
    1196             : 
    1197             :     // Skip default case.
    1198           0 :     if (state.switch_.defaultIdx == 0)
    1199           0 :         state.switch_.currentIdx++;
    1200             : 
    1201             :     // We loop on case conditions with processCondSwitchCase.
    1202           0 :     MOZ_ASSERT(JSOp(*firstCase) == JSOP_CASE);
    1203           0 :     state.stopAt = firstCase;
    1204           0 :     state.state = CFGState::COND_SWITCH_CASE;
    1205             : 
    1206           0 :     if (!cfgStack_.append(state))
    1207           0 :         return ControlStatus::Error;
    1208             : 
    1209           0 :     jsbytecode* nextPc = GetNextPc(pc);
    1210           0 :     CFGBlock* next = CFGBlock::New(alloc(), nextPc);
    1211             : 
    1212           0 :     current->setStopIns(CFGGoto::New(alloc(), next));
    1213           0 :     current->setStopPc(pc);
    1214             : 
    1215           0 :     current = next;
    1216           0 :     pc = current->startPc();
    1217             : 
    1218           0 :     if (!addBlock(current))
    1219           0 :         return ControlStatus::Error;
    1220             : 
    1221           0 :     return ControlStatus::Jumped;
    1222             : }
    1223             : 
    1224             : ControlFlowGenerator::ControlStatus
    1225           0 : ControlFlowGenerator::processCondSwitchCase(CFGState& state)
    1226             : {
    1227           0 :     MOZ_ASSERT(state.state == CFGState::COND_SWITCH_CASE);
    1228           0 :     MOZ_ASSERT(!state.switch_.breaks);
    1229           0 :     MOZ_ASSERT(current);
    1230           0 :     MOZ_ASSERT(JSOp(*pc) == JSOP_CASE);
    1231           0 :     FixedList<CFGBlock*>& bodies = *state.switch_.bodies;
    1232           0 :     uint32_t& currentIdx = state.switch_.currentIdx;
    1233             : 
    1234           0 :     jsbytecode* lastTarget = currentIdx ? bodies[currentIdx - 1]->startPc() : nullptr;
    1235             : 
    1236             :     // Fetch the following case in which we will continue.
    1237           0 :     jssrcnote* sn = GetSrcNote(gsn, script, pc);
    1238           0 :     ptrdiff_t off = GetSrcNoteOffset(sn, 0);
    1239           0 :     MOZ_ASSERT_IF(off == 0, JSOp(*GetNextPc(pc)) == JSOP_JUMPTARGET);
    1240           0 :     jsbytecode* casePc = off ? pc + off : GetNextPc(GetNextPc(pc));
    1241           0 :     bool nextIsDefault = JSOp(*casePc) == JSOP_DEFAULT;
    1242           0 :     MOZ_ASSERT(JSOp(*casePc) == JSOP_CASE || nextIsDefault);
    1243             : 
    1244             :     // Allocate the block of the matching case.
    1245           0 :     jsbytecode* bodyTarget = pc + GetJumpOffset(pc);
    1246           0 :     CFGBlock* bodyBlock = nullptr;
    1247           0 :     if (lastTarget < bodyTarget) {
    1248             : 
    1249             :         // Skip default case.
    1250           0 :         if (currentIdx == state.switch_.defaultIdx) {
    1251           0 :             currentIdx++;
    1252           0 :             lastTarget = bodies[currentIdx - 1]->startPc();
    1253           0 :             if (lastTarget < bodyTarget) {
    1254           0 :                 bodyBlock = CFGBlock::New(alloc(), bodyTarget);
    1255           0 :                 bodies[currentIdx++] = bodyBlock;
    1256             :             } else {
    1257             :                 // This body alias the previous one.
    1258           0 :                 MOZ_ASSERT(lastTarget == bodyTarget);
    1259           0 :                 MOZ_ASSERT(currentIdx > 0);
    1260           0 :                 bodyBlock = bodies[currentIdx - 1];
    1261             :             }
    1262             :         } else {
    1263           0 :             bodyBlock = CFGBlock::New(alloc(), bodyTarget);
    1264           0 :             bodies[currentIdx++] = bodyBlock;
    1265             :         }
    1266             : 
    1267             :     } else {
    1268             :         // This body alias the previous one.
    1269           0 :         MOZ_ASSERT(lastTarget == bodyTarget);
    1270           0 :         MOZ_ASSERT(currentIdx > 0);
    1271           0 :         bodyBlock = bodies[currentIdx - 1];
    1272             :     }
    1273             : 
    1274           0 :     CFGBlock* emptyBlock = CFGBlock::New(alloc(), bodyBlock->startPc());
    1275           0 :     emptyBlock->setStopIns(CFGGoto::New(alloc(), bodyBlock));
    1276           0 :     emptyBlock->setStopPc(bodyBlock->startPc());
    1277           0 :     if (!addBlock(emptyBlock))
    1278           0 :         return ControlStatus::Error;
    1279             : 
    1280           0 :     if (nextIsDefault) {
    1281           0 :         CFGBlock* defaultBlock = bodies[state.switch_.defaultIdx];
    1282             : 
    1283           0 :         CFGBlock* emptyBlock2 = CFGBlock::New(alloc(), defaultBlock->startPc());
    1284           0 :         emptyBlock2->setStopIns(CFGGoto::New(alloc(), defaultBlock));
    1285           0 :         emptyBlock2->setStopPc(defaultBlock->startPc());
    1286           0 :         if (!addBlock(emptyBlock2))
    1287           0 :             return ControlStatus::Error;
    1288             : 
    1289           0 :         current->setStopIns(CFGCompare::NewFalseBranchIsDefault(alloc(), emptyBlock, emptyBlock2));
    1290           0 :         current->setStopPc(pc);
    1291             : 
    1292           0 :         return processCondSwitchDefault(state);
    1293             :     }
    1294             : 
    1295           0 :     CFGBlock* nextBlock = CFGBlock::New(alloc(), GetNextPc(pc));
    1296           0 :     current->setStopIns(CFGCompare::NewFalseBranchIsNextCompare(alloc(), emptyBlock, nextBlock));
    1297           0 :     current->setStopPc(pc);
    1298             : 
    1299             :     // Continue until the case condition.
    1300           0 :     current = nextBlock;
    1301           0 :     pc = current->startPc();
    1302           0 :     state.stopAt = casePc;
    1303             : 
    1304           0 :     if (!addBlock(current))
    1305           0 :         return ControlStatus::Error;
    1306             : 
    1307           0 :     return ControlStatus::Jumped;
    1308             : }
    1309             : 
    1310             : ControlFlowGenerator::ControlStatus
    1311           0 : ControlFlowGenerator::processCondSwitchDefault(CFGState& state)
    1312             : {
    1313           0 :     uint32_t& currentIdx = state.switch_.currentIdx;
    1314             : 
    1315             :     // The last case condition is finished.  Loop in processCondSwitchBody,
    1316             :     // with potential stops in processSwitchBreak.
    1317             : 
    1318             : #ifdef DEBUG
    1319             :     // Test that we calculated the number of bodies correctly.
    1320           0 :     FixedList<CFGBlock*>& bodies = *state.switch_.bodies;
    1321           0 :     MOZ_ASSERT(state.switch_.currentIdx == bodies.length() ||
    1322             :                state.switch_.defaultIdx + 1 == bodies.length());
    1323             : #endif
    1324             : 
    1325             :     // Handle break statements in processSwitchBreak while processing
    1326             :     // bodies.
    1327           0 :     ControlFlowInfo breakInfo(cfgStack_.length() - 1, state.switch_.exitpc);
    1328           0 :     if (!switches_.append(breakInfo))
    1329           0 :         return ControlStatus::Error;
    1330             : 
    1331             :     // Jump into the first body.
    1332           0 :     currentIdx = 0;
    1333           0 :     current = nullptr;
    1334           0 :     state.state = CFGState::COND_SWITCH_BODY;
    1335             : 
    1336           0 :     return processCondSwitchBody(state);
    1337             : }
    1338             : 
    1339             : ControlFlowGenerator::ControlStatus
    1340           0 : ControlFlowGenerator::processCondSwitchBody(CFGState& state)
    1341             : {
    1342           0 :     MOZ_ASSERT(state.state == CFGState::COND_SWITCH_BODY);
    1343           0 :     MOZ_ASSERT(pc <= state.switch_.exitpc);
    1344           0 :     FixedList<CFGBlock*>& bodies = *state.switch_.bodies;
    1345           0 :     uint32_t& currentIdx = state.switch_.currentIdx;
    1346             : 
    1347           0 :     MOZ_ASSERT(currentIdx <= bodies.length());
    1348           0 :     if (currentIdx == bodies.length()) {
    1349           0 :         MOZ_ASSERT_IF(current, pc == state.switch_.exitpc);
    1350           0 :         return processSwitchEnd(state.switch_.breaks, state.switch_.exitpc);
    1351             :     }
    1352             : 
    1353             :     // Get the next body
    1354           0 :     CFGBlock* nextBody = bodies[currentIdx++];
    1355           0 :     MOZ_ASSERT_IF(current, pc == nextBody->startPc());
    1356             : 
    1357             :     // The last body continue into the new one.
    1358           0 :     if (current) {
    1359           0 :         current->setStopIns(CFGGoto::New(alloc(), nextBody));
    1360           0 :         current->setStopPc(pc);
    1361             :     }
    1362             : 
    1363             :     // Continue in the next body.
    1364           0 :     current = nextBody;
    1365           0 :     pc = current->startPc();
    1366             : 
    1367           0 :     if (!addBlock(current))
    1368           0 :         return ControlStatus::Error;
    1369             : 
    1370           0 :     if (currentIdx < bodies.length())
    1371           0 :         state.stopAt = bodies[currentIdx]->startPc();
    1372             :     else
    1373           0 :         state.stopAt = state.switch_.exitpc;
    1374           0 :     return ControlStatus::Jumped;
    1375             : }
    1376             : 
    1377             : ControlFlowGenerator::ControlStatus
    1378          46 : ControlFlowGenerator::processAndOrEnd(CFGState& state)
    1379             : {
    1380          46 :     MOZ_ASSERT(current);
    1381          46 :     CFGBlock* lhs = state.branch.ifFalse;
    1382             : 
    1383             :     // Create a new block to represent the join.
    1384          46 :     CFGBlock* join = CFGBlock::New(alloc(), state.stopAt);
    1385             : 
    1386             :     // End the rhs.
    1387          46 :     current->setStopIns(CFGGoto::New(alloc(), join));
    1388          46 :     current->setStopPc(pc);
    1389             : 
    1390             :     // End the lhs.
    1391          46 :     lhs->setStopIns(CFGGoto::New(alloc(), join));
    1392          46 :     lhs->setStopPc(pc);
    1393             : 
    1394             :     // Set the join path as current path.
    1395          46 :     current = join;
    1396          46 :     pc = current->startPc();
    1397             : 
    1398          46 :     if (!addBlock(current))
    1399           0 :         return ControlStatus::Error;
    1400             : 
    1401          46 :     return ControlStatus::Joined;
    1402             : }
    1403             : 
    1404             : ControlFlowGenerator::ControlStatus
    1405        1825 : ControlFlowGenerator::maybeLoop(JSOp op, jssrcnote* sn)
    1406             : {
    1407             :     // This function looks at the opcode and source note and tries to
    1408             :     // determine the structure of the loop. For some opcodes, like
    1409             :     // POP/NOP which are not explicitly control flow, this source note is
    1410             :     // optional. For opcodes with control flow, like GOTO, an unrecognized
    1411             :     // or not-present source note is a compilation failure.
    1412        1825 :     switch (op) {
    1413             :       case JSOP_POP:
    1414             :         // for (init; ; update?) ...
    1415        1720 :         if (sn && SN_TYPE(sn) == SRC_FOR) {
    1416           0 :             MOZ_CRASH("Not supported anymore?");
    1417             :             return processForLoop(op, sn);
    1418             :         }
    1419        1720 :         break;
    1420             : 
    1421             :       case JSOP_NOP:
    1422         105 :         if (sn) {
    1423             :             // do { } while (cond)
    1424         105 :             if (SN_TYPE(sn) == SRC_WHILE)
    1425           0 :                 return processDoWhileLoop(op, sn);
    1426             :             // Build a mapping such that given a basic block, whose successor
    1427             :             // has a phi
    1428             : 
    1429             :             // for (; ; update?)
    1430         105 :             if (SN_TYPE(sn) == SRC_FOR)
    1431         105 :                 return processForLoop(op, sn);
    1432             :         }
    1433           0 :         break;
    1434             : 
    1435             :       default:
    1436           0 :         MOZ_CRASH("unexpected opcode");
    1437             :     }
    1438             : 
    1439        1720 :     return ControlStatus::None;
    1440             : }
    1441             : 
    1442             : ControlFlowGenerator::ControlStatus
    1443         105 : ControlFlowGenerator::processForLoop(JSOp op, jssrcnote* sn)
    1444             : {
    1445             :     // Skip the NOP.
    1446         105 :     MOZ_ASSERT(op == JSOP_NOP);
    1447         105 :     pc = GetNextPc(pc);
    1448             : 
    1449         105 :     jsbytecode* condpc = pc + GetSrcNoteOffset(sn, 0);
    1450         105 :     jsbytecode* updatepc = pc + GetSrcNoteOffset(sn, 1);
    1451         105 :     jsbytecode* ifne = pc + GetSrcNoteOffset(sn, 2);
    1452         105 :     jsbytecode* exitpc = GetNextPc(ifne);
    1453             : 
    1454             :     // for loops have the following structures:
    1455             :     //
    1456             :     //   NOP or POP
    1457             :     //   [GOTO cond | NOP]
    1458             :     //   LOOPHEAD
    1459             :     // body:
    1460             :     //    ; [body]
    1461             :     // [increment:]
    1462             :     //   [FRESHENBLOCKSCOPE, if needed by a cloned block]
    1463             :     //    ; [increment]
    1464             :     // [cond:]
    1465             :     //   LOOPENTRY
    1466             :     //   GOTO body
    1467             :     //
    1468             :     // If there is a condition (condpc != ifne), this acts similar to a while
    1469             :     // loop otherwise, it acts like a do-while loop.
    1470             :     //
    1471             :     // Note that currently Ion does not compile pushblockscope/popblockscope as
    1472             :     // necessary prerequisites to freshenblockscope.  So the code below doesn't
    1473             :     // and needn't consider the implications of freshenblockscope.
    1474         105 :     jsbytecode* bodyStart = pc;
    1475         105 :     jsbytecode* bodyEnd = updatepc;
    1476         105 :     jsbytecode* loopEntry = condpc;
    1477         105 :     if (condpc != ifne) {
    1478         105 :         MOZ_ASSERT(JSOp(*bodyStart) == JSOP_GOTO);
    1479         105 :         MOZ_ASSERT(bodyStart + GetJumpOffset(bodyStart) == condpc);
    1480         105 :         bodyStart = GetNextPc(bodyStart);
    1481             :     } else {
    1482             :         // No loop condition, such as for(j = 0; ; j++)
    1483           0 :         if (op != JSOP_NOP) {
    1484             :             // If the loop starts with POP, we have to skip a NOP.
    1485           0 :             MOZ_ASSERT(JSOp(*bodyStart) == JSOP_NOP);
    1486           0 :             bodyStart = GetNextPc(bodyStart);
    1487             :         }
    1488           0 :         loopEntry = GetNextPc(bodyStart);
    1489             :     }
    1490         105 :     jsbytecode* loopHead = bodyStart;
    1491         105 :     MOZ_ASSERT(JSOp(*bodyStart) == JSOP_LOOPHEAD);
    1492         105 :     MOZ_ASSERT(ifne + GetJumpOffset(ifne) == bodyStart);
    1493         105 :     bodyStart = GetNextPc(bodyStart);
    1494             : 
    1495         105 :     MOZ_ASSERT(JSOp(*loopEntry) == JSOP_LOOPENTRY);
    1496             : 
    1497         105 :     CFGBlock* header = CFGBlock::New(alloc(), GetNextPc(loopEntry));
    1498             : 
    1499         105 :     CFGLoopEntry* ins = CFGLoopEntry::New(alloc(), header, 0);
    1500         105 :     if (LoopEntryCanIonOsr(loopEntry))
    1501         105 :         ins->setCanOsr();
    1502             : 
    1503         105 :     current->setStopIns(ins);
    1504         105 :     current->setStopPc(pc);
    1505             : 
    1506             :     // If there is no condition, we immediately parse the body. Otherwise, we
    1507             :     // parse the condition.
    1508             :     jsbytecode* stopAt;
    1509             :     CFGState::State initial;
    1510         105 :     if (condpc != ifne) {
    1511         105 :         pc = condpc;
    1512         105 :         stopAt = ifne;
    1513         105 :         initial = CFGState::FOR_LOOP_COND;
    1514             :     } else {
    1515           0 :         pc = bodyStart;
    1516           0 :         stopAt = bodyEnd;
    1517           0 :         initial = CFGState::FOR_LOOP_BODY;
    1518             :     }
    1519             : 
    1520         105 :     if (!pushLoop(initial, stopAt, current,
    1521             :                   loopHead, pc, bodyStart, bodyEnd, exitpc, updatepc))
    1522             :     {
    1523           0 :         return ControlStatus::Error;
    1524             :     }
    1525             : 
    1526         105 :     CFGState& state = cfgStack_.back();
    1527         105 :     state.loop.condpc = (condpc != ifne) ? condpc : nullptr;
    1528         105 :     state.loop.updatepc = (updatepc != condpc) ? updatepc : nullptr;
    1529         105 :     if (state.loop.updatepc)
    1530         105 :         state.loop.updateEnd = condpc;
    1531             : 
    1532         105 :     current = header;
    1533         105 :     if (!addBlock(current))
    1534           0 :         return ControlStatus::Error;
    1535         105 :     return ControlStatus::Jumped;
    1536             : }
    1537             : 
    1538             : ControlFlowGenerator::ControlStatus
    1539           0 : ControlFlowGenerator::processDoWhileLoop(JSOp op, jssrcnote* sn)
    1540             : {
    1541             :     // do { } while() loops have the following structure:
    1542             :     //    NOP         ; SRC_WHILE (offset to COND)
    1543             :     //    LOOPHEAD    ; SRC_WHILE (offset to IFNE)
    1544             :     //    LOOPENTRY
    1545             :     //    ...         ; body
    1546             :     //    ...
    1547             :     //    COND        ; start of condition
    1548             :     //    ...
    1549             :     //    IFNE ->     ; goes to LOOPHEAD
    1550           0 :     int condition_offset = GetSrcNoteOffset(sn, 0);
    1551           0 :     jsbytecode* conditionpc = pc + condition_offset;
    1552             : 
    1553           0 :     jssrcnote* sn2 = GetSrcNote(gsn, script, pc + 1);
    1554           0 :     int offset = GetSrcNoteOffset(sn2, 0);
    1555           0 :     jsbytecode* ifne = pc + offset + 1;
    1556           0 :     MOZ_ASSERT(ifne > pc);
    1557             : 
    1558             :     // Verify that the IFNE goes back to a loophead op.
    1559           0 :     jsbytecode* loopHead = GetNextPc(pc);
    1560           0 :     MOZ_ASSERT(JSOp(*loopHead) == JSOP_LOOPHEAD);
    1561           0 :     MOZ_ASSERT(loopHead == ifne + GetJumpOffset(ifne));
    1562             : 
    1563           0 :     jsbytecode* loopEntry = GetNextPc(loopHead);
    1564             : 
    1565           0 :     CFGBlock* header = CFGBlock::New(alloc(), GetNextPc(loopEntry));
    1566             : 
    1567           0 :     CFGLoopEntry* ins = CFGLoopEntry::New(alloc(), header, 0);
    1568           0 :     if (LoopEntryCanIonOsr(loopEntry))
    1569           0 :         ins->setCanOsr();
    1570             : 
    1571           0 :     current->setStopIns(ins);
    1572           0 :     current->setStopPc(pc);
    1573             : 
    1574           0 :     jsbytecode* bodyEnd = conditionpc;
    1575           0 :     jsbytecode* exitpc = GetNextPc(ifne);
    1576           0 :     if (!pushLoop(CFGState::DO_WHILE_LOOP_BODY, conditionpc, current,
    1577             :                   loopHead, loopEntry, loopEntry, bodyEnd, exitpc, conditionpc))
    1578             :     {
    1579           0 :         return ControlStatus::Error;
    1580             :     }
    1581             : 
    1582           0 :     CFGState& state = cfgStack_.back();
    1583           0 :     state.loop.updatepc = conditionpc;
    1584           0 :     state.loop.updateEnd = ifne;
    1585             : 
    1586           0 :     current = header;
    1587           0 :     pc = loopEntry;
    1588             : 
    1589           0 :     if (!addBlock(current))
    1590           0 :         return ControlStatus::Error;
    1591             : 
    1592           0 :     return ControlStatus::Jumped;
    1593             : }
    1594             : 
    1595             : bool
    1596         117 : ControlFlowGenerator::pushLoop(CFGState::State initial, jsbytecode* stopAt, CFGBlock* entry,
    1597             :                                jsbytecode* loopHead, jsbytecode* initialPc,
    1598             :                                jsbytecode* bodyStart, jsbytecode* bodyEnd,
    1599             :                                jsbytecode* exitpc, jsbytecode* continuepc)
    1600             : {
    1601         117 :     ControlFlowInfo loop(cfgStack_.length(), continuepc);
    1602         117 :     if (!loops_.append(loop))
    1603           0 :         return false;
    1604             : 
    1605             :     CFGState state;
    1606         117 :     state.state = initial;
    1607         117 :     state.stopAt = stopAt;
    1608         117 :     state.loop.bodyStart = bodyStart;
    1609         117 :     state.loop.bodyEnd = bodyEnd;
    1610         117 :     state.loop.exitpc = exitpc;
    1611         117 :     state.loop.entry = entry;
    1612         117 :     state.loop.successor = nullptr;
    1613         117 :     state.loop.breaks = nullptr;
    1614         117 :     state.loop.continues = nullptr;
    1615         117 :     state.loop.initialState = initial;
    1616         117 :     state.loop.initialPc = initialPc;
    1617         117 :     state.loop.initialStopAt = stopAt;
    1618         117 :     state.loop.loopHead = loopHead;
    1619         117 :     return cfgStack_.append(state);
    1620             : }
    1621             : 
    1622             : ControlFlowGenerator::ControlStatus
    1623          18 : ControlFlowGenerator::processBreak(JSOp op, jssrcnote* sn)
    1624             : {
    1625          18 :     MOZ_ASSERT(op == JSOP_GOTO);
    1626             : 
    1627          18 :     MOZ_ASSERT(SN_TYPE(sn) == SRC_BREAK ||
    1628             :                SN_TYPE(sn) == SRC_BREAK2LABEL);
    1629             : 
    1630             :     // Find the break target.
    1631          18 :     jsbytecode* target = pc + GetJumpOffset(pc);
    1632          36 :     DebugOnly<bool> found = false;
    1633             : 
    1634          18 :     if (SN_TYPE(sn) == SRC_BREAK2LABEL) {
    1635           0 :         for (size_t i = labels_.length() - 1; i < labels_.length(); i--) {
    1636           0 :             CFGState& cfg = cfgStack_[labels_[i].cfgEntry];
    1637           0 :             MOZ_ASSERT(cfg.state == CFGState::LABEL);
    1638           0 :             if (cfg.stopAt == target) {
    1639           0 :                 cfg.label.breaks = new(alloc()) DeferredEdge(current, cfg.label.breaks);
    1640           0 :                 found = true;
    1641           0 :                 break;
    1642             :             }
    1643             :         }
    1644             :     } else {
    1645          18 :         for (size_t i = loops_.length() - 1; i < loops_.length(); i--) {
    1646          18 :             CFGState& cfg = cfgStack_[loops_[i].cfgEntry];
    1647          18 :             MOZ_ASSERT(cfg.isLoop());
    1648          18 :             if (cfg.loop.exitpc == target) {
    1649          18 :                 cfg.loop.breaks = new(alloc()) DeferredEdge(current, cfg.loop.breaks);
    1650          18 :                 found = true;
    1651          18 :                 break;
    1652             :             }
    1653             :         }
    1654             :     }
    1655             : 
    1656          18 :     current->setStopPc(pc);
    1657             : 
    1658          18 :     MOZ_ASSERT(found);
    1659             : 
    1660          18 :     current = nullptr;
    1661          18 :     pc += CodeSpec[op].length;
    1662          36 :     return processControlEnd();
    1663             : }
    1664             : 
    1665             : static inline jsbytecode*
    1666          11 : EffectiveContinue(jsbytecode* pc)
    1667             : {
    1668          11 :     if (JSOp(*pc) == JSOP_GOTO)
    1669           0 :         return pc + GetJumpOffset(pc);
    1670          11 :     return pc;
    1671             : }
    1672             : 
    1673             : ControlFlowGenerator::ControlStatus
    1674          11 : ControlFlowGenerator::processContinue(JSOp op)
    1675             : {
    1676          11 :     MOZ_ASSERT(op == JSOP_GOTO);
    1677             : 
    1678             :     // Find the target loop.
    1679          11 :     CFGState* found = nullptr;
    1680          11 :     jsbytecode* target = pc + GetJumpOffset(pc);
    1681          11 :     for (size_t i = loops_.length() - 1; i < loops_.length(); i--) {
    1682             :         // +1 to skip JSOP_JUMPTARGET.
    1683          22 :         if (loops_[i].continuepc == target + 1 ||
    1684          11 :             EffectiveContinue(loops_[i].continuepc) == target)
    1685             :         {
    1686          11 :             found = &cfgStack_[loops_[i].cfgEntry];
    1687          11 :             break;
    1688             :         }
    1689             :     }
    1690             : 
    1691             :     // There must always be a valid target loop structure. If not, there's
    1692             :     // probably an off-by-something error in which pc we track.
    1693          11 :     MOZ_ASSERT(found);
    1694          11 :     CFGState& state = *found;
    1695             : 
    1696          11 :     state.loop.continues = new(alloc()) DeferredEdge(current, state.loop.continues);
    1697          11 :     if (!state.loop.continues)
    1698           0 :         return ControlStatus::Error;
    1699          11 :     current->setStopPc(pc);
    1700             : 
    1701          11 :     current = nullptr;
    1702          11 :     pc += CodeSpec[op].length;
    1703          11 :     return processControlEnd();
    1704             : }
    1705             : 
    1706             : ControlFlowGenerator::ControlStatus
    1707           0 : ControlFlowGenerator::processSwitchBreak(JSOp op)
    1708             : {
    1709           0 :     MOZ_ASSERT(op == JSOP_GOTO);
    1710             : 
    1711             :     // Find the target switch.
    1712           0 :     CFGState* found = nullptr;
    1713           0 :     jsbytecode* target = pc + GetJumpOffset(pc);
    1714           0 :     for (size_t i = switches_.length() - 1; i < switches_.length(); i--) {
    1715           0 :         if (switches_[i].continuepc == target) {
    1716           0 :             found = &cfgStack_[switches_[i].cfgEntry];
    1717           0 :             break;
    1718             :         }
    1719             :     }
    1720             : 
    1721             :     // There must always be a valid target loop structure. If not, there's
    1722             :     // probably an off-by-something error in which pc we track.
    1723           0 :     MOZ_ASSERT(found);
    1724           0 :     CFGState& state = *found;
    1725             : 
    1726           0 :     DeferredEdge** breaks = nullptr;
    1727           0 :     switch (state.state) {
    1728             :       case CFGState::TABLE_SWITCH:
    1729           0 :         breaks = &state.switch_.breaks;
    1730           0 :         break;
    1731             :       case CFGState::COND_SWITCH_BODY:
    1732           0 :         breaks = &state.switch_.breaks;
    1733           0 :         break;
    1734             :       default:
    1735           0 :         MOZ_CRASH("Unexpected switch state.");
    1736             :     }
    1737             : 
    1738           0 :     *breaks = new(alloc()) DeferredEdge(current, *breaks);
    1739             : 
    1740           0 :     current->setStopPc(pc);
    1741             : 
    1742           0 :     current = nullptr;
    1743           0 :     pc += CodeSpec[op].length;
    1744           0 :     return processControlEnd();
    1745             : }
    1746             : 
    1747             : ControlFlowGenerator::ControlStatus
    1748         597 : ControlFlowGenerator::processIfStart(JSOp op)
    1749             : {
    1750             :     // IFEQ always has a forward offset.
    1751         597 :     jsbytecode* trueStart = pc + CodeSpec[op].length;
    1752         597 :     jsbytecode* falseStart = pc + GetJumpOffset(pc);
    1753         597 :     MOZ_ASSERT(falseStart > pc);
    1754             : 
    1755             :     // We only handle cases that emit source notes.
    1756         597 :     jssrcnote* sn = GetSrcNote(gsn, script, pc);
    1757         597 :     if (!sn)
    1758           0 :         return ControlStatus::Error;
    1759             : 
    1760             :     // Create true and false branches.
    1761         597 :     CFGBlock* ifTrue = CFGBlock::New(alloc(), trueStart);
    1762         597 :     CFGBlock* ifFalse = CFGBlock::New(alloc(), falseStart);
    1763             : 
    1764         597 :     CFGTest* test = CFGTest::New(alloc(), ifTrue, ifFalse);
    1765         597 :     current->setStopIns(test);
    1766         597 :     current->setStopPc(pc);
    1767             : 
    1768             :     // The bytecode for if/ternary gets emitted either like this:
    1769             :     //
    1770             :     //    IFEQ X  ; src note (IF_ELSE, COND) points to the GOTO
    1771             :     //    ...
    1772             :     //    GOTO Z
    1773             :     // X: ...     ; else/else if
    1774             :     //    ...
    1775             :     // Z:         ; join
    1776             :     //
    1777             :     // Or like this:
    1778             :     //
    1779             :     //    IFEQ X  ; src note (IF) has no offset
    1780             :     //    ...
    1781             :     // Z: ...     ; join
    1782             :     //
    1783             :     // We want to parse the bytecode as if we were parsing the AST, so for the
    1784             :     // IF_ELSE/COND cases, we use the source note and follow the GOTO. For the
    1785             :     // IF case, the IFEQ offset is the join point.
    1786         597 :     switch (SN_TYPE(sn)) {
    1787             :       case SRC_IF:
    1788         427 :         if (!cfgStack_.append(CFGState::If(falseStart, test)))
    1789           0 :             return ControlStatus::Error;
    1790         427 :         break;
    1791             : 
    1792             :       case SRC_IF_ELSE:
    1793             :       case SRC_COND:
    1794             :       {
    1795             :         // Infer the join point from the JSOP_GOTO[X] sitting here, then
    1796             :         // assert as we much we can that this is the right GOTO.
    1797         170 :         jsbytecode* trueEnd = pc + GetSrcNoteOffset(sn, 0);
    1798         170 :         MOZ_ASSERT(trueEnd > pc);
    1799         170 :         MOZ_ASSERT(trueEnd < falseStart);
    1800         170 :         MOZ_ASSERT(JSOp(*trueEnd) == JSOP_GOTO);
    1801         170 :         MOZ_ASSERT(!GetSrcNote(gsn, script, trueEnd));
    1802             : 
    1803         170 :         jsbytecode* falseEnd = trueEnd + GetJumpOffset(trueEnd);
    1804         170 :         MOZ_ASSERT(falseEnd > trueEnd);
    1805         170 :         MOZ_ASSERT(falseEnd >= falseStart);
    1806             : 
    1807         170 :         if (!cfgStack_.append(CFGState::IfElse(trueEnd, falseEnd, test)))
    1808           0 :             return ControlStatus::Error;
    1809         170 :         break;
    1810             :       }
    1811             : 
    1812             :       default:
    1813           0 :         MOZ_CRASH("unexpected source note type");
    1814             :     }
    1815             : 
    1816             :     // Switch to parsing the true branch. Note that no PC update is needed,
    1817             :     // it's the next instruction.
    1818         597 :     current = ifTrue;
    1819         597 :     pc = ifTrue->startPc();
    1820             : 
    1821         597 :     if (!addBlock(current))
    1822           0 :         return ControlStatus::Error;
    1823             : 
    1824         597 :     return ControlStatus::Jumped;
    1825             : }
    1826             : 
    1827             : int
    1828         572 : ControlFlowGenerator::CmpSuccessors(const void* a, const void* b)
    1829             : {
    1830         572 :     const CFGBlock* a0 = * (CFGBlock * const*)a;
    1831         572 :     const CFGBlock* b0 = * (CFGBlock * const*)b;
    1832         572 :     if (a0->startPc() == b0->startPc())
    1833           0 :         return 0;
    1834             : 
    1835         572 :     return (a0->startPc() > b0->startPc()) ? 1 : -1;
    1836             : }
    1837             : 
    1838             : ControlFlowGenerator::ControlStatus
    1839          44 : ControlFlowGenerator::processTableSwitch(JSOp op, jssrcnote* sn)
    1840             : {
    1841             :     // TableSwitch op contains the following data
    1842             :     // (length between data is JUMP_OFFSET_LEN)
    1843             :     //
    1844             :     // 0: Offset of default case
    1845             :     // 1: Lowest number in tableswitch
    1846             :     // 2: Highest number in tableswitch
    1847             :     // 3: Offset of case low
    1848             :     // 4: Offset of case low+1
    1849             :     // .: ...
    1850             :     // .: Offset of case high
    1851             : 
    1852          44 :     MOZ_ASSERT(op == JSOP_TABLESWITCH);
    1853          44 :     MOZ_ASSERT(SN_TYPE(sn) == SRC_TABLESWITCH);
    1854             : 
    1855             :     // Get the default and exit pc
    1856          44 :     jsbytecode* exitpc = pc + GetSrcNoteOffset(sn, 0);
    1857          44 :     jsbytecode* defaultpc = pc + GET_JUMP_OFFSET(pc);
    1858             : 
    1859          44 :     MOZ_ASSERT(defaultpc > pc && defaultpc <= exitpc);
    1860             : 
    1861             :     // Get the low and high from the tableswitch
    1862          44 :     jsbytecode* pc2 = pc;
    1863          44 :     pc2 += JUMP_OFFSET_LEN;
    1864          44 :     int low = GET_JUMP_OFFSET(pc2);
    1865          44 :     pc2 += JUMP_OFFSET_LEN;
    1866          44 :     int high = GET_JUMP_OFFSET(pc2);
    1867          44 :     pc2 += JUMP_OFFSET_LEN;
    1868             : 
    1869             :     // Create MIR instruction
    1870          44 :     CFGTableSwitch* tableswitch = CFGTableSwitch::New(alloc(), low, high);
    1871             : 
    1872             :     // Create default case
    1873          44 :     CFGBlock* defaultcase = CFGBlock::New(alloc(), defaultpc);
    1874             : 
    1875          44 :     if (!tableswitch->addDefault(defaultcase))
    1876           0 :         return ControlStatus::Error;
    1877             : 
    1878             :     // Create cases
    1879          44 :     jsbytecode* casepc = nullptr;
    1880         308 :     for (int i = 0; i < high-low+1; i++) {
    1881         264 :         if (!alloc().ensureBallast())
    1882           0 :             return ControlStatus::Error;
    1883         264 :         casepc = pc + GET_JUMP_OFFSET(pc2);
    1884             : 
    1885         264 :         MOZ_ASSERT(casepc >= pc && casepc <= exitpc);
    1886             :         CFGBlock* caseBlock;
    1887             : 
    1888         264 :         if (casepc == pc) {
    1889             :             // If the casepc equals the current pc, it is not a written case,
    1890             :             // but a filled gap. That way we can use a tableswitch instead of
    1891             :             // condswitch, even if not all numbers are consecutive.
    1892             :             // In that case this block goes to the default case
    1893           0 :             caseBlock = CFGBlock::New(alloc(), defaultpc);
    1894           0 :             caseBlock->setStopIns(CFGGoto::New(alloc(), defaultcase));
    1895             :         } else {
    1896             :             // If this is an actual case (not filled gap),
    1897             :             // add this block to the list that still needs to get processed.
    1898         264 :             caseBlock = CFGBlock::New(alloc(), casepc);
    1899             :         }
    1900             : 
    1901         264 :         if (!tableswitch->addCase(caseBlock))
    1902           0 :             return ControlStatus::Error;
    1903             : 
    1904         264 :         pc2 += JUMP_OFFSET_LEN;
    1905             :     }
    1906             : 
    1907             :     // Create info
    1908          44 :     ControlFlowInfo switchinfo(cfgStack_.length(), exitpc);
    1909          44 :     if (!switches_.append(switchinfo))
    1910           0 :         return ControlStatus::Error;
    1911             : 
    1912             :     // Use a state to retrieve some information
    1913          44 :     CFGState state = CFGState::TableSwitch(alloc(), exitpc);
    1914          88 :     if (!state.switch_.bodies ||
    1915          44 :         !state.switch_.bodies->init(alloc(), tableswitch->numSuccessors()))
    1916             :     {
    1917           0 :         return ControlStatus::Error;
    1918             :     }
    1919             : 
    1920          44 :     FixedList<CFGBlock*>& bodies = *state.switch_.bodies;
    1921         352 :     for (size_t i = 0; i < tableswitch->numSuccessors(); i++)
    1922         308 :         bodies[i] = tableswitch->getSuccessor(i);
    1923             : 
    1924          44 :     qsort(bodies.begin(), state.switch_.bodies->length(),
    1925          44 :           sizeof(CFGBlock*), CmpSuccessors);
    1926             : 
    1927             :     // Save the MIR instruction as last instruction of this block.
    1928          44 :     current->setStopIns(tableswitch);
    1929          44 :     current->setStopPc(pc);
    1930             : 
    1931             :     // If there is only one successor the block should stop at the end of the switch
    1932             :     // Else it should stop at the start of the next successor
    1933          44 :     if (bodies.length() > 1)
    1934          44 :         state.stopAt = bodies[1]->startPc();
    1935             :     else
    1936           0 :         state.stopAt = exitpc;
    1937             : 
    1938          44 :     if (!cfgStack_.append(state))
    1939           0 :         return ControlStatus::Error;
    1940             : 
    1941          44 :     current = bodies[0];
    1942          44 :     pc = current->startPc();
    1943             : 
    1944          44 :     if (!addBlock(current))
    1945           0 :         return ControlStatus::Error;
    1946             : 
    1947          44 :     return ControlStatus::Jumped;
    1948             : }
    1949             : 
    1950             : ControlFlowGenerator::ControlStatus
    1951         308 : ControlFlowGenerator::processNextTableSwitchCase(CFGState& state)
    1952             : {
    1953         308 :     MOZ_ASSERT(state.state == CFGState::TABLE_SWITCH);
    1954         308 :     FixedList<CFGBlock*>& bodies = *state.switch_.bodies;
    1955             : 
    1956         308 :     state.switch_.currentIdx++;
    1957             : 
    1958             :     // Test if there are still unprocessed successors (cases/default)
    1959         308 :     if (state.switch_.currentIdx >= bodies.length())
    1960          44 :         return processSwitchEnd(state.switch_.breaks, state.switch_.exitpc);
    1961             : 
    1962             :     // Get the next successor
    1963         264 :     CFGBlock* successor = bodies[state.switch_.currentIdx];
    1964             : 
    1965             :     // Add current block as predecessor if available.
    1966             :     // This means the previous case didn't have a break statement.
    1967             :     // So flow will continue in this block.
    1968         264 :     if (current) {
    1969           0 :         current->setStopIns(CFGGoto::New(alloc(), successor));
    1970           0 :         current->setStopPc(pc);
    1971             :     }
    1972             : 
    1973             :     // If this is the last successor the block should stop at the end of the tableswitch
    1974             :     // Else it should stop at the start of the next successor
    1975         264 :     if (state.switch_.currentIdx + 1 < bodies.length())
    1976         220 :         state.stopAt = bodies[state.switch_.currentIdx + 1]->startPc();
    1977             :     else
    1978          44 :         state.stopAt = state.switch_.exitpc;
    1979             : 
    1980         264 :     current = bodies[state.switch_.currentIdx];
    1981         264 :     pc = current->startPc();
    1982             : 
    1983         264 :     if (!addBlock(current))
    1984           0 :         return ControlStatus::Error;
    1985             : 
    1986         264 :     return ControlStatus::Jumped;
    1987             : }
    1988             : 
    1989             : ControlFlowGenerator::ControlStatus
    1990          44 : ControlFlowGenerator::processSwitchEnd(DeferredEdge* breaks, jsbytecode* exitpc)
    1991             : {
    1992             :     // No break statements, no current.
    1993             :     // This means that control flow is cut-off from this point
    1994             :     // (e.g. all cases have return statements).
    1995          44 :     if (!breaks && !current)
    1996          36 :         return ControlStatus::Ended;
    1997             : 
    1998             :     // Create successor block.
    1999             :     // If there are breaks, create block with breaks as predecessor
    2000             :     // Else create a block with current as predecessor
    2001           8 :     CFGBlock* successor = nullptr;
    2002           8 :     if (breaks) {
    2003           0 :         successor = createBreakCatchBlock(breaks, exitpc);
    2004           0 :         if (!successor)
    2005           0 :             return ControlStatus::Error;
    2006             :     } else {
    2007           8 :         successor = CFGBlock::New(alloc(), exitpc);
    2008             :     }
    2009             : 
    2010             :     // If there is current, the current block flows into this one.
    2011             :     // So current is also a predecessor to this block
    2012           8 :     if (current) {
    2013           8 :         current->setStopIns(CFGGoto::New(alloc(), successor));
    2014           8 :         current->setStopPc(pc);
    2015             :     }
    2016             : 
    2017           8 :     current = successor;
    2018           8 :     pc = successor->startPc();
    2019             : 
    2020           8 :     if (!addBlock(successor))
    2021           0 :         return ControlStatus::Error;
    2022             : 
    2023           8 :     return ControlStatus::Joined;
    2024             : }
    2025             : 
    2026             : ControlFlowGenerator::CFGState
    2027         427 : ControlFlowGenerator::CFGState::If(jsbytecode* join, CFGTest* test)
    2028             : {
    2029             :     CFGState state;
    2030         427 :     state.state = IF_TRUE;
    2031         427 :     state.stopAt = join;
    2032         427 :     state.branch.ifFalse = test->getSuccessor(1);
    2033         427 :     state.branch.test = test;
    2034         427 :     return state;
    2035             : }
    2036             : 
    2037             : ControlFlowGenerator::CFGState
    2038         170 : ControlFlowGenerator::CFGState::IfElse(jsbytecode* trueEnd, jsbytecode* falseEnd,
    2039             :                                        CFGTest* test)
    2040             : {
    2041         170 :     CFGBlock* ifFalse = test->getSuccessor(1);
    2042             : 
    2043             :     CFGState state;
    2044             :     // If the end of the false path is the same as the start of the
    2045             :     // false path, then the "else" block is empty and we can devolve
    2046             :     // this to the IF_TRUE case. We handle this here because there is
    2047             :     // still an extra GOTO on the true path and we want stopAt to point
    2048             :     // there, whereas the IF_TRUE case does not have the GOTO.
    2049         340 :     state.state = (falseEnd == ifFalse->startPc())
    2050         170 :                   ? IF_TRUE_EMPTY_ELSE
    2051             :                   : IF_ELSE_TRUE;
    2052         170 :     state.stopAt = trueEnd;
    2053         170 :     state.branch.falseEnd = falseEnd;
    2054         170 :     state.branch.ifFalse = ifFalse;
    2055         170 :     state.branch.test = test;
    2056         170 :     return state;
    2057             : }
    2058             : 
    2059             : ControlFlowGenerator::CFGState
    2060          46 : ControlFlowGenerator::CFGState::AndOr(jsbytecode* join, CFGBlock* lhs)
    2061             : {
    2062             :     CFGState state;
    2063          46 :     state.state = AND_OR;
    2064          46 :     state.stopAt = join;
    2065          46 :     state.branch.ifFalse = lhs;
    2066          46 :     state.branch.test = nullptr;
    2067          46 :     return state;
    2068             : }
    2069             : 
    2070             : ControlFlowGenerator::CFGState
    2071          44 : ControlFlowGenerator::CFGState::TableSwitch(TempAllocator& alloc, jsbytecode* exitpc)
    2072             : {
    2073             :     CFGState state;
    2074          44 :     state.state = TABLE_SWITCH;
    2075          44 :     state.stopAt = exitpc;
    2076          44 :     state.switch_.bodies = (FixedList<CFGBlock*>*)alloc.allocate(sizeof(FixedList<CFGBlock*>));
    2077          44 :     state.switch_.currentIdx = 0;
    2078          44 :     state.switch_.exitpc = exitpc;
    2079          44 :     state.switch_.breaks = nullptr;
    2080          44 :     return state;
    2081             : }
    2082             : 
    2083             : ControlFlowGenerator::CFGState
    2084           0 : ControlFlowGenerator::CFGState::CondSwitch(TempAllocator& alloc, jsbytecode* exitpc,
    2085             :                                            jsbytecode* defaultTarget)
    2086             : {
    2087             :     CFGState state;
    2088           0 :     state.state = COND_SWITCH_CASE;
    2089           0 :     state.stopAt = nullptr;
    2090           0 :     state.switch_.bodies = (FixedList<CFGBlock*>*)alloc.allocate(sizeof(FixedList<CFGBlock*>));
    2091           0 :     state.switch_.currentIdx = 0;
    2092           0 :     state.switch_.defaultTarget = defaultTarget;
    2093           0 :     state.switch_.defaultIdx = uint32_t(-1);
    2094           0 :     state.switch_.exitpc = exitpc;
    2095           0 :     state.switch_.breaks = nullptr;
    2096           0 :     return state;
    2097             : }
    2098             : ControlFlowGenerator::CFGState
    2099           0 : ControlFlowGenerator::CFGState::Label(jsbytecode* exitpc)
    2100             : {
    2101             :     CFGState state;
    2102           0 :     state.state = LABEL;
    2103           0 :     state.stopAt = exitpc;
    2104           0 :     state.label.breaks = nullptr;
    2105           0 :     return state;
    2106             : }
    2107             : 
    2108             : ControlFlowGenerator::CFGState
    2109           5 : ControlFlowGenerator::CFGState::Try(jsbytecode* exitpc, CFGBlock* successor)
    2110             : {
    2111             :     CFGState state;
    2112           5 :     state.state = TRY;
    2113           5 :     state.stopAt = exitpc;
    2114           5 :     state.try_.successor = successor;
    2115           5 :     return state;
    2116             : }
    2117             : 
    2118             : ControlFlowGenerator::ControlStatus
    2119          46 : ControlFlowGenerator::processAndOr(JSOp op)
    2120             : {
    2121          46 :     MOZ_ASSERT(op == JSOP_AND || op == JSOP_OR);
    2122             : 
    2123          46 :     jsbytecode* rhsStart = pc + CodeSpec[op].length;
    2124          46 :     jsbytecode* joinStart = pc + GetJumpOffset(pc);
    2125          46 :     MOZ_ASSERT(joinStart > pc);
    2126             : 
    2127          46 :     CFGBlock* evalLhs = CFGBlock::New(alloc(), joinStart);
    2128          46 :     CFGBlock* evalRhs = CFGBlock::New(alloc(), rhsStart);
    2129             : 
    2130             :     CFGTest* test = (op == JSOP_AND)
    2131          63 :                   ? CFGTest::New(alloc(), evalRhs, evalLhs)
    2132          63 :                   : CFGTest::New(alloc(), evalLhs, evalRhs);
    2133          46 :     test->keepCondition();
    2134          46 :     current->setStopIns(test);
    2135          46 :     current->setStopPc(pc);
    2136             : 
    2137             :     // Create the rhs block.
    2138          46 :     if (!cfgStack_.append(CFGState::AndOr(joinStart, evalLhs)))
    2139           0 :         return ControlStatus::Error;
    2140             : 
    2141          46 :     if (!addBlock(evalLhs))
    2142           0 :         return ControlStatus::Error;
    2143             : 
    2144          46 :     current = evalRhs;
    2145          46 :     pc = current->startPc();
    2146             : 
    2147          46 :     if (!addBlock(current))
    2148           0 :         return ControlStatus::Error;
    2149             : 
    2150          46 :     return ControlStatus::Jumped;
    2151             : }
    2152             : 
    2153             : ControlFlowGenerator::ControlStatus
    2154           0 : ControlFlowGenerator::processLabel()
    2155             : {
    2156           0 :     MOZ_ASSERT(JSOp(*pc) == JSOP_LABEL);
    2157             : 
    2158           0 :     jsbytecode* endpc = pc + GET_JUMP_OFFSET(pc);
    2159           0 :     MOZ_ASSERT(endpc > pc);
    2160             : 
    2161           0 :     ControlFlowInfo label(cfgStack_.length(), endpc);
    2162           0 :     if (!labels_.append(label))
    2163           0 :         return ControlStatus::Error;
    2164             : 
    2165           0 :     if (!cfgStack_.append(CFGState::Label(endpc)))
    2166           0 :         return ControlStatus::Error;
    2167             : 
    2168           0 :     return ControlStatus::None;
    2169             : }

Generated by: LCOV version 1.13