LCOV - code coverage report
Current view: top level - js/src/jit - IonControlFlow.h (source / functions) Hit Total Coverage
Test: output.info Lines: 203 246 82.5 %
Date: 2017-07-14 16:53:18 Functions: 113 149 75.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #ifndef jit_IonControlFlow_h
       8             : #define jit_IonControlFlow_h
       9             : 
      10             : #include "mozilla/Array.h"
      11             : 
      12             : #include "jsbytecode.h"
      13             : 
      14             : #include "jit/BytecodeAnalysis.h"
      15             : #include "jit/FixedList.h"
      16             : #include "jit/JitAllocPolicy.h"
      17             : #include "js/TypeDecls.h"
      18             : 
      19             : namespace js {
      20             : namespace jit {
      21             : 
      22             : class CFGControlInstruction;
      23             : 
      24             : // Adds MFoo::New functions which are mirroring the arguments of the
      25             : // constructors. Opcodes which are using this macro can be called with a
      26             : // TempAllocator, or the fallible version of the TempAllocator.
      27             : #define TRIVIAL_CFG_NEW_WRAPPERS                                              \
      28             :     template <typename... Args>                                               \
      29             :     static CFGThisOpcode* New(TempAllocator& alloc, Args&&... args) {         \
      30             :         return new(alloc) CFGThisOpcode(mozilla::Forward<Args>(args)...);     \
      31             :     }                                                                         \
      32             :     template <typename... Args>                                               \
      33             :     static CFGThisOpcode* New(TempAllocator::Fallible alloc, Args&&... args)  \
      34             :     {                                                                         \
      35             :         return new(alloc) CFGThisOpcode(mozilla::Forward<Args>(args)...);     \
      36             :     }
      37             : 
      38           0 : class CFGSpace
      39             : {
      40             :     static const size_t DEFAULT_CHUNK_SIZE = 4096;
      41             : 
      42             :   protected:
      43             :     LifoAlloc allocator_;
      44             :   public:
      45             : 
      46          13 :     explicit CFGSpace()
      47          13 :       : allocator_(DEFAULT_CHUNK_SIZE)
      48          13 :     {}
      49             : 
      50          19 :     LifoAlloc& lifoAlloc() {
      51          19 :         return allocator_;
      52             :     }
      53             : 
      54           0 :     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
      55           0 :         return allocator_.sizeOfExcludingThis(mallocSizeOf);
      56             :     }
      57             : };
      58             : 
      59             : class CFGBlock : public TempObject
      60             : {
      61             :     size_t id_;
      62             :     jsbytecode* start;
      63             :     jsbytecode* stop;
      64             :     CFGControlInstruction* end;
      65             :     bool inWorkList;
      66             : 
      67             :   public:
      68        4656 :     explicit CFGBlock(jsbytecode* start)
      69        4656 :       : id_(-1),
      70             :         start(start),
      71             :         stop(nullptr),
      72             :         end(nullptr),
      73        4656 :         inWorkList(false)
      74        4656 :     {}
      75             : 
      76        2328 :     static CFGBlock* New(TempAllocator& alloc, jsbytecode* start) {
      77        2328 :         return new(alloc) CFGBlock(start);
      78             :     }
      79             : 
      80             :     void operator=(const CFGBlock&) = delete;
      81             : 
      82       15406 :     jsbytecode* startPc() const {
      83       15406 :         return start;
      84             :     }
      85           0 :     void setStartPc(jsbytecode* startPc) {
      86           0 :         start = startPc;
      87           0 :     }
      88       24483 :     jsbytecode* stopPc() const {
      89       24483 :         MOZ_ASSERT(stop);
      90       24483 :         return stop;
      91             :     }
      92        4656 :     void setStopPc(jsbytecode* stopPc) {
      93        4656 :         stop = stopPc;
      94        4656 :     }
      95        5314 :     CFGControlInstruction* stopIns() const {
      96        5314 :         MOZ_ASSERT(end);
      97        5314 :         return end;
      98             :     }
      99        4656 :     void setStopIns(CFGControlInstruction* stopIns) {
     100        4656 :         end = stopIns;
     101        4656 :     }
     102             :     bool isInWorkList() const {
     103             :         return inWorkList;
     104             :     }
     105             :     void setInWorklist() {
     106             :         MOZ_ASSERT(!inWorkList);
     107             :         inWorkList = true;
     108             :     }
     109             :     void clearInWorkList() {
     110             :         MOZ_ASSERT(inWorkList);
     111             :         inWorkList = false;
     112             :     }
     113        8890 :     size_t id() const {
     114        8890 :         return id_;
     115             :     }
     116        4656 :     void setId(size_t id) {
     117        4656 :         id_ = id;
     118        4656 :     }
     119             : };
     120             : 
     121             : #define CFG_CONTROL_OPCODE_LIST(_)                                          \
     122             :     _(Test)                                                                 \
     123             :     _(Compare)                                                              \
     124             :     _(Goto)                                                                 \
     125             :     _(Return)                                                               \
     126             :     _(RetRVal)                                                              \
     127             :     _(LoopEntry)                                                            \
     128             :     _(BackEdge)                                                             \
     129             :     _(TableSwitch)                                                          \
     130             :     _(Try)                                                                  \
     131             :     _(Throw)
     132             : 
     133             : // Forward declarations of MIR types.
     134             : #define FORWARD_DECLARE(type) class CFG##type;
     135             :  CFG_CONTROL_OPCODE_LIST(FORWARD_DECLARE)
     136             : #undef FORWARD_DECLARE
     137             : 
     138             : #define CFG_CONTROL_HEADER(type_name)                                        \
     139             :     static const Type classOpcode = CFGControlInstruction::Type_##type_name; \
     140             :     using CFGThisOpcode = CFG##type_name;                                    \
     141             :     Type type() const override {                                             \
     142             :         return classOpcode;                                                  \
     143             :     }                                                                        \
     144             :     const char* Name() const override {                                      \
     145             :         return #type_name;                                                   \
     146             :     }                                                                        \
     147             : 
     148             : 
     149        4656 : class CFGControlInstruction : public TempObject
     150             : {
     151             :   public:
     152             :     enum Type {
     153             : #   define DEFINE_TYPES(type) Type_##type,
     154             :         CFG_CONTROL_OPCODE_LIST(DEFINE_TYPES)
     155             : #   undef DEFINE_TYPES
     156             :     };
     157             : 
     158             :     virtual size_t numSuccessors() const = 0;
     159             :     virtual CFGBlock* getSuccessor(size_t i) const = 0;
     160             :     virtual void replaceSuccessor(size_t i, CFGBlock* successor) = 0;
     161             :     virtual Type type() const = 0;
     162             :     virtual const char* Name() const = 0;
     163             : 
     164        4252 :     template<typename CFGType> bool is() const {
     165        4252 :         return type() == CFGType::classOpcode;
     166             :     }
     167        4211 :     template<typename CFGType> CFGType* to() {
     168        4211 :         MOZ_ASSERT(this->is<CFGType>());
     169        4211 :         return static_cast<CFGType*>(this);
     170             :     }
     171             :     template<typename CFGType> const CFGType* to() const {
     172             :         MOZ_ASSERT(this->is<CFGType>());
     173             :         return static_cast<const CFGType*>(this);
     174             :     }
     175             : #   define TYPE_CASTS(type)             \
     176             :     bool is##type() const {             \
     177             :         return this->is<CFG##type>();   \
     178             :     }                                   \
     179             :     CFG##type* to##type() {             \
     180             :         return this->to<CFG##type>();   \
     181             :     }                                   \
     182             :     const CFG##type* to##type() const { \
     183             :         return this->to<CFG##type>();   \
     184             :     }
     185        4252 :     CFG_CONTROL_OPCODE_LIST(TYPE_CASTS)
     186             : #   undef TYPE_CASTS
     187             : };
     188             : 
     189             : template <size_t Successors>
     190        4558 : class CFGAryControlInstruction : public CFGControlInstruction
     191             : {
     192             :     mozilla::Array<CFGBlock*, Successors> successors_;
     193             : 
     194             :   public:
     195           0 :     size_t numSuccessors() const final override {
     196           0 :         return Successors;
     197             :     }
     198        9235 :     CFGBlock* getSuccessor(size_t i) const final override {
     199        9235 :         return successors_[i];
     200             :     }
     201        5076 :     void replaceSuccessor(size_t i, CFGBlock* succ) final override {
     202        5076 :         successors_[i] = succ;
     203        5076 :     }
     204             : };
     205             : 
     206             : class CFGTry : public CFGControlInstruction
     207             : {
     208             :     CFGBlock* tryBlock_;
     209             :     jsbytecode* catchStartPc_;
     210             :     CFGBlock* mergePoint_;
     211             : 
     212          10 :     CFGTry(CFGBlock* successor, jsbytecode* catchStartPc, CFGBlock* mergePoint = nullptr)
     213          10 :       : tryBlock_(successor),
     214             :         catchStartPc_(catchStartPc),
     215          10 :         mergePoint_(mergePoint)
     216          10 :     { }
     217             : 
     218             :   public:
     219          20 :     CFG_CONTROL_HEADER(Try)
     220           5 :     TRIVIAL_CFG_NEW_WRAPPERS
     221             : 
     222           5 :     static CFGTry* CopyWithNewTargets(TempAllocator& alloc, CFGTry* old,
     223             :                                       CFGBlock* tryBlock, CFGBlock* merge)
     224             :     {
     225           5 :         return new(alloc) CFGTry(tryBlock, old->catchStartPc(), merge);
     226             :     }
     227             : 
     228          23 :     size_t numSuccessors() const final override {
     229          23 :         return mergePoint_ ? 2 : 1;
     230             :     }
     231          18 :     CFGBlock* getSuccessor(size_t i) const final override {
     232          18 :         MOZ_ASSERT(i < numSuccessors());
     233          18 :         return (i == 0) ? tryBlock_ : mergePoint_;
     234             :     }
     235           0 :     void replaceSuccessor(size_t i, CFGBlock* succ) final override {
     236           0 :         MOZ_ASSERT(i < numSuccessors());
     237           0 :         if (i == 0)
     238           0 :             tryBlock_ = succ;
     239             :         else
     240           0 :             mergePoint_ = succ;
     241           0 :     }
     242             : 
     243           9 :     CFGBlock* tryBlock() const {
     244           9 :         return getSuccessor(0);
     245             :     }
     246             : 
     247           7 :     jsbytecode* catchStartPc() const {
     248           7 :         return catchStartPc_;
     249             :     }
     250             : 
     251           7 :     CFGBlock* afterTryCatchBlock() const {
     252           7 :         return getSuccessor(1);
     253             :     }
     254             : 
     255           2 :     bool codeAfterTryCatchReachable() const {
     256           2 :         return !!mergePoint_;
     257             :     }
     258             : };
     259             : 
     260             : class CFGTableSwitch : public CFGControlInstruction
     261             : {
     262             :     Vector<CFGBlock*, 4, JitAllocPolicy> successors_;
     263             :     size_t low_;
     264             :     size_t high_;
     265             : 
     266          88 :     CFGTableSwitch(TempAllocator& alloc, size_t low, size_t high)
     267          88 :       : successors_(alloc),
     268             :         low_(low),
     269          88 :         high_(high)
     270          88 :     {}
     271             : 
     272             :   public:
     273         176 :     CFG_CONTROL_HEADER(TableSwitch);
     274             : 
     275          88 :     static CFGTableSwitch* New(TempAllocator& alloc, size_t low, size_t high) {
     276          88 :         return new(alloc) CFGTableSwitch(alloc, low, high);
     277             :     }
     278             : 
     279        2904 :     size_t numSuccessors() const final override {
     280        2904 :         return successors_.length();
     281             :     }
     282        1540 :     CFGBlock* getSuccessor(size_t i) const final override {
     283        1540 :         MOZ_ASSERT(i < numSuccessors());
     284        1540 :         return successors_[i];
     285             :     }
     286           0 :     void replaceSuccessor(size_t i, CFGBlock* succ) final override {
     287           0 :         MOZ_ASSERT(i < numSuccessors());
     288           0 :         successors_[i] = succ;
     289           0 :     }
     290             : 
     291          88 :     bool addDefault(CFGBlock* defaultCase) {
     292          88 :         MOZ_ASSERT(successors_.length() == 0);
     293          88 :         return successors_.append(defaultCase);
     294             :     }
     295             : 
     296         528 :     bool addCase(CFGBlock* caseBlock) {
     297         528 :         MOZ_ASSERT(successors_.length() > 0);
     298         528 :         return successors_.append(caseBlock);
     299             :     }
     300             : 
     301          88 :     CFGBlock* defaultCase() const {
     302          88 :         return getSuccessor(0);
     303             :     }
     304             : 
     305         528 :     CFGBlock* getCase(size_t i) const {
     306         528 :         return getSuccessor(i + 1);
     307             :     }
     308             : 
     309          88 :     size_t high() const {
     310          88 :         return high_;
     311             :     }
     312             : 
     313          88 :     size_t low() const {
     314          88 :         return low_;
     315             :     }
     316             : };
     317             : 
     318             : /**
     319             :  * CFGCompare
     320             :  *
     321             :  * PEEK
     322             :  * PEEK
     323             :  * STRICTEQ
     324             :  *    POP truePopAmount
     325             :  *    JUMP succ1
     326             :  * STRICTNEQ
     327             :  *    POP falsePopAmount
     328             :  *    JUMP succ2
     329             :  */
     330             : class CFGCompare : public CFGAryControlInstruction<2>
     331             : {
     332             :     const size_t truePopAmount_;
     333             :     const size_t falsePopAmount_;
     334             : 
     335           0 :     CFGCompare(CFGBlock* succ1, size_t truePopAmount, CFGBlock* succ2, size_t falsePopAmount)
     336           0 :       : truePopAmount_(truePopAmount),
     337           0 :         falsePopAmount_(falsePopAmount)
     338             :     {
     339           0 :         replaceSuccessor(0, succ1);
     340           0 :         replaceSuccessor(1, succ2);
     341           0 :     }
     342             : 
     343             :   public:
     344           0 :     CFG_CONTROL_HEADER(Compare);
     345             : 
     346           0 :     static CFGCompare* NewFalseBranchIsDefault(TempAllocator& alloc, CFGBlock* case_,
     347             :                                                CFGBlock* default_)
     348             :     {
     349             :         // True and false branch both go to a body and don't need the lhs and
     350             :         // rhs to the compare. Pop them.
     351           0 :         return new(alloc) CFGCompare(case_, 2, default_, 2);
     352             :     }
     353             : 
     354           0 :     static CFGCompare* NewFalseBranchIsNextCompare(TempAllocator& alloc, CFGBlock* case_,
     355             :                                                    CFGBlock* nextCompare)
     356             :     {
     357             :         // True branch goes to the body and don't need the lhs and
     358             :         // rhs to the compare anymore. Pop them. The next compare still
     359             :         // needs the lhs.
     360           0 :         return new(alloc) CFGCompare(case_, 2, nextCompare, 1);
     361             :     }
     362             : 
     363           0 :     static CFGCompare* CopyWithNewTargets(TempAllocator& alloc, CFGCompare* old,
     364             :                                           CFGBlock* succ1, CFGBlock* succ2)
     365             :     {
     366           0 :         return new(alloc) CFGCompare(succ1, old->truePopAmount(), succ2, old->falsePopAmount());
     367             :     }
     368             : 
     369           0 :     CFGBlock* trueBranch() const {
     370           0 :         return getSuccessor(0);
     371             :     }
     372           0 :     CFGBlock* falseBranch() const {
     373           0 :         return getSuccessor(1);
     374             :     }
     375           0 :     size_t truePopAmount() const {
     376           0 :         return truePopAmount_;
     377             :     }
     378           0 :     size_t falsePopAmount() const {
     379           0 :         return falsePopAmount_;
     380             :     }
     381             : };
     382             : 
     383             : /**
     384             :  * CFGTest
     385             :  *
     386             :  * POP / PEEK (depending on keepCondition_)
     387             :  * IFEQ JUMP succ1
     388             :  * IFNEQ JUMP succ2
     389             :  *
     390             :  */
     391             : class CFGTest : public CFGAryControlInstruction<2>
     392             : {
     393             :     // By default the condition gets popped. This variable
     394             :     // keeps track if we want to keep the condition.
     395             :     bool keepCondition_;
     396             : 
     397         760 :     CFGTest(CFGBlock* succ1, CFGBlock* succ2)
     398         760 :       : keepCondition_(false)
     399             :     {
     400         760 :         replaceSuccessor(0, succ1);
     401         760 :         replaceSuccessor(1, succ2);
     402         760 :     }
     403         760 :     CFGTest(CFGBlock* succ1, CFGBlock* succ2, bool keepCondition)
     404         760 :       : keepCondition_(keepCondition)
     405             :     {
     406         760 :         replaceSuccessor(0, succ1);
     407         760 :         replaceSuccessor(1, succ2);
     408         760 :     }
     409             : 
     410             :   public:
     411        3344 :     CFG_CONTROL_HEADER(Test);
     412         760 :     TRIVIAL_CFG_NEW_WRAPPERS
     413             : 
     414         760 :     static CFGTest* CopyWithNewTargets(TempAllocator& alloc, CFGTest* old,
     415             :                                        CFGBlock* succ1, CFGBlock* succ2)
     416             :     {
     417         760 :         return new(alloc) CFGTest(succ1, succ2, old->mustKeepCondition());
     418             :     }
     419             : 
     420        2584 :     CFGBlock* trueBranch() const {
     421        2584 :         return getSuccessor(0);
     422             :     }
     423        3496 :     CFGBlock* falseBranch() const {
     424        3496 :         return getSuccessor(1);
     425             :     }
     426          46 :     void keepCondition() {
     427          46 :         keepCondition_ = true;
     428          46 :     }
     429        1672 :     bool mustKeepCondition() const {
     430        1672 :         return keepCondition_;
     431             :     }
     432             : };
     433             : 
     434             : /**
     435             :  * CFGReturn
     436             :  *
     437             :  * POP
     438             :  * RETURN popped value
     439             :  *
     440             :  */
     441         956 : class CFGReturn : public CFGAryControlInstruction<0>
     442             : {
     443             :   public:
     444        1554 :     CFG_CONTROL_HEADER(Return);
     445         956 :     TRIVIAL_CFG_NEW_WRAPPERS
     446             : };
     447             : 
     448             : /**
     449             :  * CFGRetRVal
     450             :  *
     451             :  * RETURN the value in the return value slot
     452             :  *
     453             :  */
     454          44 : class CFGRetRVal : public CFGAryControlInstruction<0>
     455             : {
     456             :   public:
     457          68 :     CFG_CONTROL_HEADER(RetRVal);
     458          44 :     TRIVIAL_CFG_NEW_WRAPPERS
     459             : };
     460             : 
     461             : /**
     462             :  * CFGThrow
     463             :  *
     464             :  * POP
     465             :  * THROW popped value
     466             :  *
     467             :  */
     468           2 : class CFGThrow : public CFGAryControlInstruction<0>
     469             : {
     470             :   public:
     471           3 :     CFG_CONTROL_HEADER(Throw);
     472           2 :     TRIVIAL_CFG_NEW_WRAPPERS
     473             : };
     474             : 
     475             : class CFGUnaryControlInstruction : public CFGAryControlInstruction<1>
     476             : {
     477             :   public:
     478        2036 :     explicit CFGUnaryControlInstruction(CFGBlock* block) {
     479        2036 :         MOZ_ASSERT(block);
     480        2036 :         replaceSuccessor(0, block);
     481        2036 :     }
     482             : 
     483         235 :     CFGBlock* successor() const {
     484         235 :         return getSuccessor(0);
     485             :     }
     486             : };
     487             : 
     488             : /**
     489             :  * CFGGOTO
     490             :  *
     491             :  * POP (x popAmount)
     492             :  * JMP block
     493             :  *
     494             :  */
     495             : class CFGGoto : public CFGUnaryControlInstruction
     496             : {
     497             :     const size_t popAmount_;
     498             : 
     499         784 :     explicit CFGGoto(CFGBlock* block)
     500         784 :       : CFGUnaryControlInstruction(block),
     501         784 :         popAmount_(0)
     502         784 :     {}
     503             : 
     504         784 :     CFGGoto(CFGBlock* block, size_t popAmount_)
     505         784 :       : CFGUnaryControlInstruction(block),
     506         784 :         popAmount_(popAmount_)
     507         784 :     {}
     508             : 
     509             :   public:
     510        3512 :     CFG_CONTROL_HEADER(Goto);
     511         784 :     TRIVIAL_CFG_NEW_WRAPPERS
     512             : 
     513         784 :     static CFGGoto* CopyWithNewTargets(TempAllocator& alloc, CFGGoto* old, CFGBlock* block)
     514             :     {
     515         784 :         return new(alloc) CFGGoto(block, old->popAmount());
     516             :     }
     517             : 
     518        1736 :     size_t popAmount() const {
     519        1736 :         return popAmount_;
     520             :     }
     521             : };
     522             : 
     523             : /**
     524             :  * CFGBackEdge
     525             :  *
     526             :  * Jumps back to the start of the loop.
     527             :  *
     528             :  * JMP block
     529             :  *
     530             :  */
     531             : class CFGBackEdge : public CFGUnaryControlInstruction
     532             : {
     533         234 :     explicit CFGBackEdge(CFGBlock* block)
     534         234 :       : CFGUnaryControlInstruction(block)
     535         234 :     {}
     536             : 
     537             :   public:
     538         469 :     CFG_CONTROL_HEADER(BackEdge);
     539         117 :     TRIVIAL_CFG_NEW_WRAPPERS
     540             : 
     541         117 :     static CFGBackEdge* CopyWithNewTargets(TempAllocator& alloc, CFGBackEdge* old, CFGBlock* block)
     542             :     {
     543         117 :         return new(alloc) CFGBackEdge(block);
     544             :     }
     545             : };
     546             : 
     547             : /**
     548             :  * CFGLOOPENTRY
     549             :  *
     550             :  * Indicates the jumping block is the start of a loop.
     551             :  * That block is the only block allowed to have a backedge.
     552             :  *
     553             :  * JMP block
     554             :  *
     555             :  */
     556             : class CFGLoopEntry : public CFGUnaryControlInstruction
     557             : {
     558             :     bool canOsr_;
     559             :     size_t stackPhiCount_;
     560             :     jsbytecode* loopStopPc_;
     561             : 
     562         117 :     CFGLoopEntry(CFGBlock* block, size_t stackPhiCount)
     563         117 :       : CFGUnaryControlInstruction(block),
     564             :         canOsr_(false),
     565             :         stackPhiCount_(stackPhiCount),
     566         117 :         loopStopPc_(nullptr)
     567         117 :     {}
     568             : 
     569         117 :     CFGLoopEntry(CFGBlock* block, bool canOsr, size_t stackPhiCount, jsbytecode* loopStopPc)
     570         117 :       : CFGUnaryControlInstruction(block),
     571             :         canOsr_(canOsr),
     572             :         stackPhiCount_(stackPhiCount),
     573         117 :         loopStopPc_(loopStopPc)
     574         117 :     {}
     575             : 
     576             :   public:
     577         705 :     CFG_CONTROL_HEADER(LoopEntry);
     578         117 :     TRIVIAL_CFG_NEW_WRAPPERS
     579             : 
     580         117 :     static CFGLoopEntry* CopyWithNewTargets(TempAllocator& alloc, CFGLoopEntry* old,
     581             :                                             CFGBlock* loopEntry)
     582             :     {
     583         117 :         return new(alloc) CFGLoopEntry(loopEntry, old->canOsr(), old->stackPhiCount(),
     584         117 :                                        old->loopStopPc());
     585             :     }
     586             : 
     587         117 :     void setCanOsr() {
     588         117 :         canOsr_ = true;
     589         117 :     }
     590             : 
     591         239 :     bool canOsr() const {
     592         239 :         return canOsr_;
     593             :     }
     594             : 
     595         235 :     size_t stackPhiCount() const {
     596         235 :         return stackPhiCount_;
     597             :     }
     598             : 
     599         235 :     jsbytecode* loopStopPc() const {
     600         235 :         MOZ_ASSERT(loopStopPc_);
     601         235 :         return loopStopPc_;
     602             :     }
     603             : 
     604         117 :     void setLoopStopPc(jsbytecode* loopStopPc) {
     605         117 :         loopStopPc_ = loopStopPc;
     606         117 :     }
     607             : };
     608             : 
     609             : typedef Vector<CFGBlock*, 4, JitAllocPolicy> CFGBlockVector;
     610             : 
     611             : class ControlFlowGraph : public TempObject
     612             : {
     613             :     // A list of blocks in RPO, containing per block a pc-range and
     614             :     // a control instruction.
     615             :     Vector<CFGBlock, 4, JitAllocPolicy> blocks_;
     616             : 
     617         150 :     explicit ControlFlowGraph(TempAllocator& alloc)
     618         150 :       : blocks_(alloc)
     619         150 :     {}
     620             : 
     621             :   public:
     622         150 :     static ControlFlowGraph* New(TempAllocator& alloc) {
     623         150 :         return new(alloc) ControlFlowGraph(alloc);
     624             :     }
     625             : 
     626             :     ControlFlowGraph(const ControlFlowGraph&) = delete;
     627             :     void operator=(const ControlFlowGraph&) = delete;
     628             : 
     629             :     void dump(GenericPrinter& print, JSScript* script);
     630             :     bool init(TempAllocator& alloc, const CFGBlockVector& blocks);
     631             : 
     632        2713 :     const CFGBlock* block(size_t i) const {
     633        2713 :         return &blocks_[i];
     634             :     }
     635             : 
     636        5883 :     size_t numBlocks() const {
     637        5883 :         return blocks_.length();
     638             :     }
     639             : };
     640             : 
     641         150 : class ControlFlowGenerator
     642             : {
     643             :     static int CmpSuccessors(const void* a, const void* b);
     644             : 
     645             :     JSScript* script;
     646             :     CFGBlock* current;
     647             :     jsbytecode* pc;
     648             :     GSNCache gsn;
     649             :     TempAllocator& alloc_;
     650             :     CFGBlockVector blocks_;
     651             : 
     652             :   public:
     653             :     ControlFlowGenerator(const ControlFlowGenerator&) = delete;
     654             :     void operator=(const ControlFlowGenerator&) = delete;
     655             : 
     656       24603 :     TempAllocator& alloc() {
     657       24603 :         return alloc_;
     658             :     }
     659             : 
     660             :     enum class ControlStatus {
     661             :         Error,
     662             :         Abort,
     663             :         Ended,        // There is no continuation/join point.
     664             :         Joined,       // Created a join node.
     665             :         Jumped,       // Parsing another branch at the same level.
     666             :         None          // No control flow.
     667             :     };
     668             : 
     669             :     struct DeferredEdge : public TempObject
     670             :     {
     671             :         CFGBlock* block;
     672             :         DeferredEdge* next;
     673             : 
     674          29 :         DeferredEdge(CFGBlock* block, DeferredEdge* next)
     675          29 :           : block(block), next(next)
     676          29 :         { }
     677             :     };
     678             : 
     679             :     struct ControlFlowInfo {
     680             :         // Entry in the cfgStack.
     681             :         uint32_t cfgEntry;
     682             : 
     683             :         // Label that continues go to.
     684             :         jsbytecode* continuepc;
     685             : 
     686         161 :         ControlFlowInfo(uint32_t cfgEntry, jsbytecode* continuepc)
     687         161 :           : cfgEntry(cfgEntry),
     688         161 :             continuepc(continuepc)
     689         161 :         { }
     690             :     };
     691             : 
     692             : 
     693             :     // To avoid recursion, the bytecode analyzer uses a stack where each entry
     694             :     // is a small state machine. As we encounter branches or jumps in the
     695             :     // bytecode, we push information about the edges on the stack so that the
     696             :     // CFG can be built in a tree-like fashion.
     697             :     struct CFGState {
     698             :         enum State {
     699             :             IF_TRUE,            // if() { }, no else.
     700             :             IF_TRUE_EMPTY_ELSE, // if() { }, empty else
     701             :             IF_ELSE_TRUE,       // if() { X } else { }
     702             :             IF_ELSE_FALSE,      // if() { } else { X }
     703             :             DO_WHILE_LOOP_BODY, // do { x } while ()
     704             :             DO_WHILE_LOOP_COND, // do { } while (x)
     705             :             WHILE_LOOP_COND,    // while (x) { }
     706             :             WHILE_LOOP_BODY,    // while () { x }
     707             :             FOR_LOOP_COND,      // for (; x;) { }
     708             :             FOR_LOOP_BODY,      // for (; ;) { x }
     709             :             FOR_LOOP_UPDATE,    // for (; ; x) { }
     710             :             TABLE_SWITCH,       // switch() { x }
     711             :             COND_SWITCH_CASE,   // switch() { case X: ... }
     712             :             COND_SWITCH_BODY,   // switch() { case ...: X }
     713             :             AND_OR,             // && x, || x
     714             :             LABEL,              // label: x
     715             :             TRY                 // try { x } catch(e) { }
     716             :         };
     717             : 
     718             :         State state;            // Current state of this control structure.
     719             :         jsbytecode* stopAt;     // Bytecode at which to stop the processing loop.
     720             : 
     721             :         // For if structures, this contains branch information.
     722             :         union {
     723             :             struct {
     724             :                 CFGBlock* ifFalse;
     725             :                 jsbytecode* falseEnd;
     726             :                 CFGBlock* ifTrue;    // Set when the end of the true path is reached.
     727             :                 CFGTest* test;
     728             :             } branch;
     729             :             struct {
     730             :                 // Common entry point.
     731             :                 CFGBlock* entry;
     732             : 
     733             :                 // Position of where the loop body starts and ends.
     734             :                 jsbytecode* bodyStart;
     735             :                 jsbytecode* bodyEnd;
     736             : 
     737             :                 // pc immediately after the loop exits.
     738             :                 jsbytecode* exitpc;
     739             : 
     740             :                 // Common exit point. Created lazily, so it may be nullptr.
     741             :                 CFGBlock* successor;
     742             : 
     743             :                 // Deferred break and continue targets.
     744             :                 DeferredEdge* breaks;
     745             :                 DeferredEdge* continues;
     746             : 
     747             :                 // Initial state, in case loop processing is restarted.
     748             :                 State initialState;
     749             :                 jsbytecode* initialPc;
     750             :                 jsbytecode* initialStopAt;
     751             :                 jsbytecode* loopHead;
     752             : 
     753             :                 // For-loops only.
     754             :                 jsbytecode* condpc;
     755             :                 jsbytecode* updatepc;
     756             :                 jsbytecode* updateEnd;
     757             :             } loop;
     758             :             struct {
     759             :                 // Vector of body blocks to process after the cases.
     760             :                 FixedList<CFGBlock*>* bodies;
     761             : 
     762             :                 // When processing case statements, this counter points at the
     763             :                 // last uninitialized body.  When processing bodies, this
     764             :                 // counter targets the next body to process.
     765             :                 uint32_t currentIdx;
     766             : 
     767             :                 // Remember the block index of the default case.
     768             :                 jsbytecode* defaultTarget;
     769             :                 uint32_t defaultIdx;
     770             : 
     771             :                 // Block immediately after the switch.
     772             :                 jsbytecode* exitpc;
     773             :                 DeferredEdge* breaks;
     774             :             } switch_;
     775             :             struct {
     776             :                 DeferredEdge* breaks;
     777             :             } label;
     778             :             struct {
     779             :                 CFGBlock* successor;
     780             :             } try_;
     781             :         };
     782             : 
     783         827 :         inline bool isLoop() const {
     784         827 :             switch (state) {
     785             :               case DO_WHILE_LOOP_COND:
     786             :               case DO_WHILE_LOOP_BODY:
     787             :               case WHILE_LOOP_COND:
     788             :               case WHILE_LOOP_BODY:
     789             :               case FOR_LOOP_COND:
     790             :               case FOR_LOOP_BODY:
     791             :               case FOR_LOOP_UPDATE:
     792         135 :                 return true;
     793             :               default:
     794         692 :                 return false;
     795             :             }
     796             :         }
     797             : 
     798             :         static CFGState If(jsbytecode* join, CFGTest* test);
     799             :         static CFGState IfElse(jsbytecode* trueEnd, jsbytecode* falseEnd, CFGTest* test);
     800             :         static CFGState AndOr(jsbytecode* join, CFGBlock* lhs);
     801             :         static CFGState TableSwitch(TempAllocator& alloc, jsbytecode* exitpc);
     802             :         static CFGState CondSwitch(TempAllocator& alloc, jsbytecode* exitpc,
     803             :                                    jsbytecode* defaultTarget);
     804             :         static CFGState Label(jsbytecode* exitpc);
     805             :         static CFGState Try(jsbytecode* exitpc, CFGBlock* successor);
     806             :     };
     807             : 
     808             :     Vector<CFGState, 8, JitAllocPolicy> cfgStack_;
     809             :     Vector<ControlFlowInfo, 4, JitAllocPolicy> loops_;
     810             :     Vector<ControlFlowInfo, 0, JitAllocPolicy> switches_;
     811             :     Vector<ControlFlowInfo, 2, JitAllocPolicy> labels_;
     812             :     BytecodeAnalysis analysis_;
     813             :     bool aborted_;
     814             : 
     815             :   public:
     816             :     ControlFlowGenerator(TempAllocator& alloc, JSScript* script);
     817             : 
     818             :     MOZ_MUST_USE bool init();
     819             : 
     820             :     MOZ_MUST_USE bool traverseBytecode();
     821             :     MOZ_MUST_USE bool addBlock(CFGBlock* block);
     822         150 :     ControlFlowGraph* getGraph(TempAllocator& alloc) {
     823         150 :         ControlFlowGraph* cfg = ControlFlowGraph::New(alloc);
     824         150 :         if (!cfg)
     825           0 :             return nullptr;
     826         150 :         if (!cfg->init(alloc, blocks_))
     827           0 :             return nullptr;
     828         150 :         return cfg;
     829             :     }
     830             : 
     831           0 :     bool aborted() const {
     832           0 :         return aborted_;
     833             :     }
     834             : 
     835             :   private:
     836             :     void popCfgStack();
     837             :     MOZ_MUST_USE bool processDeferredContinues(CFGState& state);
     838             :     ControlStatus processControlEnd();
     839             :     ControlStatus processCfgStack();
     840             :     ControlStatus processCfgEntry(CFGState& state);
     841             :     ControlStatus processIfStart(JSOp op);
     842             :     ControlStatus processIfEnd(CFGState& state);
     843             :     ControlStatus processIfElseTrueEnd(CFGState& state);
     844             :     ControlStatus processIfElseFalseEnd(CFGState& state);
     845             :     ControlStatus processDoWhileLoop(JSOp op, jssrcnote* sn);
     846             :     ControlStatus processDoWhileBodyEnd(CFGState& state);
     847             :     ControlStatus processDoWhileCondEnd(CFGState& state);
     848             :     ControlStatus processWhileCondEnd(CFGState& state);
     849             :     ControlStatus processWhileBodyEnd(CFGState& state);
     850             :     ControlStatus processForLoop(JSOp op, jssrcnote* sn);
     851             :     ControlStatus processForCondEnd(CFGState& state);
     852             :     ControlStatus processForBodyEnd(CFGState& state);
     853             :     ControlStatus processForUpdateEnd(CFGState& state);
     854             :     ControlStatus processWhileOrForInLoop(jssrcnote* sn);
     855             :     ControlStatus processNextTableSwitchCase(CFGState& state);
     856             :     ControlStatus processCondSwitch();
     857             :     ControlStatus processCondSwitchCase(CFGState& state);
     858             :     ControlStatus processCondSwitchDefault(CFGState& state);
     859             :     ControlStatus processCondSwitchBody(CFGState& state);
     860             :     ControlStatus processSwitchBreak(JSOp op);
     861             :     ControlStatus processSwitchEnd(DeferredEdge* breaks, jsbytecode* exitpc);
     862             :     ControlStatus processTry();
     863             :     ControlStatus processTryEnd(CFGState& state);
     864             :     ControlStatus processThrow();
     865             :     ControlStatus processTableSwitch(JSOp op, jssrcnote* sn);
     866             :     ControlStatus processContinue(JSOp op);
     867             :     ControlStatus processBreak(JSOp op, jssrcnote* sn);
     868             :     ControlStatus processReturn(JSOp op);
     869             :     ControlStatus maybeLoop(JSOp op, jssrcnote* sn);
     870             :     ControlStatus snoopControlFlow(JSOp op);
     871             :     ControlStatus processBrokenLoop(CFGState& state);
     872             :     ControlStatus finishLoop(CFGState& state, CFGBlock* successor);
     873             :     ControlStatus processAndOr(JSOp op);
     874             :     ControlStatus processAndOrEnd(CFGState& state);
     875             :     ControlStatus processLabel();
     876             :     ControlStatus processLabelEnd(CFGState& state);
     877             : 
     878             :     MOZ_MUST_USE bool pushLoop(CFGState::State state, jsbytecode* stopAt, CFGBlock* entry,
     879             :                                jsbytecode* loopHead, jsbytecode* initialPc,
     880             :                                jsbytecode* bodyStart, jsbytecode* bodyEnd,
     881             :                                jsbytecode* exitpc, jsbytecode* continuepc);
     882             :     void endCurrentBlock(CFGControlInstruction* ins);
     883             :     CFGBlock* createBreakCatchBlock(DeferredEdge* edge, jsbytecode* pc);
     884             : };
     885             : 
     886             : } // namespace jit
     887             : } // namespace js
     888             : 
     889             : #endif /* jit_IonControlFlow_h */

Generated by: LCOV version 1.13