tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

RemoveSwitchFallThrough.cpp (9331B)


      1 //
      2 // Copyright 2002 The ANGLE Project Authors. All rights reserved.
      3 // Use of this source code is governed by a BSD-style license that can be
      4 // found in the LICENSE file.
      5 //
      6 // RemoveSwitchFallThrough.cpp: Remove fall-through from switch statements.
      7 // Note that it is unsafe to do further AST transformations on the AST generated
      8 // by this function. It leaves duplicate nodes in the AST making replacements
      9 // unreliable.
     10 
     11 #include "compiler/translator/tree_ops/d3d/RemoveSwitchFallThrough.h"
     12 
     13 #include "compiler/translator/Diagnostics.h"
     14 #include "compiler/translator/tree_util/IntermTraverse.h"
     15 
     16 namespace sh
     17 {
     18 
     19 namespace
     20 {
     21 
     22 class RemoveSwitchFallThroughTraverser : public TIntermTraverser
     23 {
     24  public:
     25    static TIntermBlock *removeFallThrough(TIntermBlock *statementList,
     26                                           PerformanceDiagnostics *perfDiagnostics);
     27 
     28  private:
     29    RemoveSwitchFallThroughTraverser(TIntermBlock *statementList,
     30                                     PerformanceDiagnostics *perfDiagnostics);
     31 
     32    void visitSymbol(TIntermSymbol *node) override;
     33    void visitConstantUnion(TIntermConstantUnion *node) override;
     34    bool visitDeclaration(Visit, TIntermDeclaration *node) override;
     35    bool visitBinary(Visit, TIntermBinary *node) override;
     36    bool visitUnary(Visit, TIntermUnary *node) override;
     37    bool visitTernary(Visit visit, TIntermTernary *node) override;
     38    bool visitSwizzle(Visit, TIntermSwizzle *node) override;
     39    bool visitIfElse(Visit visit, TIntermIfElse *node) override;
     40    bool visitSwitch(Visit, TIntermSwitch *node) override;
     41    bool visitCase(Visit, TIntermCase *node) override;
     42    bool visitAggregate(Visit, TIntermAggregate *node) override;
     43    bool visitBlock(Visit, TIntermBlock *node) override;
     44    bool visitLoop(Visit, TIntermLoop *node) override;
     45    bool visitBranch(Visit, TIntermBranch *node) override;
     46 
     47    void outputSequence(TIntermSequence *sequence, size_t startIndex);
     48    void handlePreviousCase();
     49 
     50    TIntermBlock *mStatementList;
     51    TIntermBlock *mStatementListOut;
     52    bool mLastStatementWasBreak;
     53    TIntermBlock *mPreviousCase;
     54    std::vector<TIntermBlock *> mCasesSharingBreak;
     55    PerformanceDiagnostics *mPerfDiagnostics;
     56 };
     57 
     58 TIntermBlock *RemoveSwitchFallThroughTraverser::removeFallThrough(
     59    TIntermBlock *statementList,
     60    PerformanceDiagnostics *perfDiagnostics)
     61 {
     62    RemoveSwitchFallThroughTraverser rm(statementList, perfDiagnostics);
     63    ASSERT(statementList);
     64    statementList->traverse(&rm);
     65    ASSERT(rm.mPreviousCase || statementList->getSequence()->empty());
     66    if (!rm.mLastStatementWasBreak && rm.mPreviousCase)
     67    {
     68        // Make sure that there's a branch at the end of the final case inside the switch statement.
     69        // This also ensures that any cases that fall through to the final case will get the break.
     70        TIntermBranch *finalBreak = new TIntermBranch(EOpBreak, nullptr);
     71        rm.mPreviousCase->getSequence()->push_back(finalBreak);
     72        rm.mLastStatementWasBreak = true;
     73    }
     74    rm.handlePreviousCase();
     75    return rm.mStatementListOut;
     76 }
     77 
     78 RemoveSwitchFallThroughTraverser::RemoveSwitchFallThroughTraverser(
     79    TIntermBlock *statementList,
     80    PerformanceDiagnostics *perfDiagnostics)
     81    : TIntermTraverser(true, false, false),
     82      mStatementList(statementList),
     83      mLastStatementWasBreak(false),
     84      mPreviousCase(nullptr),
     85      mPerfDiagnostics(perfDiagnostics)
     86 {
     87    mStatementListOut = new TIntermBlock();
     88 }
     89 
     90 void RemoveSwitchFallThroughTraverser::visitSymbol(TIntermSymbol *node)
     91 {
     92    // Note that this assumes that switch statements which don't begin by a case statement
     93    // have already been weeded out in validation.
     94    mPreviousCase->getSequence()->push_back(node);
     95    mLastStatementWasBreak = false;
     96 }
     97 
     98 void RemoveSwitchFallThroughTraverser::visitConstantUnion(TIntermConstantUnion *node)
     99 {
    100    // Conditions of case labels are not traversed, so this is a constant statement like "0;".
    101    // These are no-ops so there's no need to add them back to the statement list. Should have
    102    // already been pruned out of the AST, in fact.
    103    UNREACHABLE();
    104 }
    105 
    106 bool RemoveSwitchFallThroughTraverser::visitDeclaration(Visit, TIntermDeclaration *node)
    107 {
    108    mPreviousCase->getSequence()->push_back(node);
    109    mLastStatementWasBreak = false;
    110    return false;
    111 }
    112 
    113 bool RemoveSwitchFallThroughTraverser::visitBinary(Visit, TIntermBinary *node)
    114 {
    115    mPreviousCase->getSequence()->push_back(node);
    116    mLastStatementWasBreak = false;
    117    return false;
    118 }
    119 
    120 bool RemoveSwitchFallThroughTraverser::visitUnary(Visit, TIntermUnary *node)
    121 {
    122    mPreviousCase->getSequence()->push_back(node);
    123    mLastStatementWasBreak = false;
    124    return false;
    125 }
    126 
    127 bool RemoveSwitchFallThroughTraverser::visitTernary(Visit, TIntermTernary *node)
    128 {
    129    mPreviousCase->getSequence()->push_back(node);
    130    mLastStatementWasBreak = false;
    131    return false;
    132 }
    133 
    134 bool RemoveSwitchFallThroughTraverser::visitSwizzle(Visit, TIntermSwizzle *node)
    135 {
    136    mPreviousCase->getSequence()->push_back(node);
    137    mLastStatementWasBreak = false;
    138    return false;
    139 }
    140 
    141 bool RemoveSwitchFallThroughTraverser::visitIfElse(Visit, TIntermIfElse *node)
    142 {
    143    mPreviousCase->getSequence()->push_back(node);
    144    mLastStatementWasBreak = false;
    145    return false;
    146 }
    147 
    148 bool RemoveSwitchFallThroughTraverser::visitSwitch(Visit, TIntermSwitch *node)
    149 {
    150    mPreviousCase->getSequence()->push_back(node);
    151    mLastStatementWasBreak = false;
    152    // Don't go into nested switch statements
    153    return false;
    154 }
    155 
    156 void RemoveSwitchFallThroughTraverser::outputSequence(TIntermSequence *sequence, size_t startIndex)
    157 {
    158    for (size_t i = startIndex; i < sequence->size(); ++i)
    159    {
    160        mStatementListOut->getSequence()->push_back(sequence->at(i));
    161    }
    162 }
    163 
    164 void RemoveSwitchFallThroughTraverser::handlePreviousCase()
    165 {
    166    if (mPreviousCase)
    167        mCasesSharingBreak.push_back(mPreviousCase);
    168    if (mLastStatementWasBreak)
    169    {
    170        for (size_t i = 0; i < mCasesSharingBreak.size(); ++i)
    171        {
    172            ASSERT(!mCasesSharingBreak.at(i)->getSequence()->empty());
    173            if (mCasesSharingBreak.at(i)->getSequence()->size() == 1)
    174            {
    175                // Fall-through is allowed in case the label has no statements.
    176                outputSequence(mCasesSharingBreak.at(i)->getSequence(), 0);
    177            }
    178            else
    179            {
    180                // Include all the statements that this case can fall through under the same label.
    181                if (mCasesSharingBreak.size() > i + 1u)
    182                {
    183                    mPerfDiagnostics->warning(mCasesSharingBreak.at(i)->getLine(),
    184                                              "Performance: non-empty fall-through cases in "
    185                                              "switch statements generate extra code.",
    186                                              "switch");
    187                }
    188                for (size_t j = i; j < mCasesSharingBreak.size(); ++j)
    189                {
    190                    size_t startIndex =
    191                        j > i ? 1 : 0;  // Add the label only from the first sequence.
    192                    outputSequence(mCasesSharingBreak.at(j)->getSequence(), startIndex);
    193                }
    194            }
    195        }
    196        mCasesSharingBreak.clear();
    197    }
    198    mLastStatementWasBreak = false;
    199    mPreviousCase          = nullptr;
    200 }
    201 
    202 bool RemoveSwitchFallThroughTraverser::visitCase(Visit, TIntermCase *node)
    203 {
    204    handlePreviousCase();
    205    mPreviousCase = new TIntermBlock();
    206    mPreviousCase->getSequence()->push_back(node);
    207    mPreviousCase->setLine(node->getLine());
    208    // Don't traverse the condition of the case statement
    209    return false;
    210 }
    211 
    212 bool RemoveSwitchFallThroughTraverser::visitAggregate(Visit, TIntermAggregate *node)
    213 {
    214    mPreviousCase->getSequence()->push_back(node);
    215    mLastStatementWasBreak = false;
    216    return false;
    217 }
    218 
    219 bool DoesBlockAlwaysBreak(TIntermBlock *node)
    220 {
    221    if (node->getSequence()->empty())
    222    {
    223        return false;
    224    }
    225 
    226    TIntermBlock *lastStatementAsBlock = node->getSequence()->back()->getAsBlock();
    227    if (lastStatementAsBlock)
    228    {
    229        return DoesBlockAlwaysBreak(lastStatementAsBlock);
    230    }
    231 
    232    TIntermBranch *lastStatementAsBranch = node->getSequence()->back()->getAsBranchNode();
    233    return lastStatementAsBranch != nullptr;
    234 }
    235 
    236 bool RemoveSwitchFallThroughTraverser::visitBlock(Visit, TIntermBlock *node)
    237 {
    238    if (node != mStatementList)
    239    {
    240        mPreviousCase->getSequence()->push_back(node);
    241        mLastStatementWasBreak = DoesBlockAlwaysBreak(node);
    242        return false;
    243    }
    244    return true;
    245 }
    246 
    247 bool RemoveSwitchFallThroughTraverser::visitLoop(Visit, TIntermLoop *node)
    248 {
    249    mPreviousCase->getSequence()->push_back(node);
    250    mLastStatementWasBreak = false;
    251    return false;
    252 }
    253 
    254 bool RemoveSwitchFallThroughTraverser::visitBranch(Visit, TIntermBranch *node)
    255 {
    256    mPreviousCase->getSequence()->push_back(node);
    257    // TODO: Verify that accepting return or continue statements here doesn't cause problems.
    258    mLastStatementWasBreak = true;
    259    return false;
    260 }
    261 
    262 }  // anonymous namespace
    263 
    264 TIntermBlock *RemoveSwitchFallThrough(TIntermBlock *statementList,
    265                                      PerformanceDiagnostics *perfDiagnostics)
    266 {
    267    return RemoveSwitchFallThroughTraverser::removeFallThrough(statementList, perfDiagnostics);
    268 }
    269 
    270 }  // namespace sh