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