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