| // |
| // Copyright (c) 2017 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| // RemoveNoOpCasesFromEndOfSwitchStatements.cpp: Clean up cases from the end of a switch statement |
| // that only contain no-ops. |
| |
| #include "compiler/translator/RemoveNoOpCasesFromEndOfSwitchStatements.h" |
| |
| #include "compiler/translator/IntermNode.h" |
| #include "compiler/translator/IntermNode_util.h" |
| #include "compiler/translator/IntermTraverse.h" |
| #include "compiler/translator/SymbolTable.h" |
| |
| namespace sh |
| { |
| |
| namespace |
| { |
| |
| bool AreEmptyBlocks(TIntermSequence *statements, size_t i); |
| |
| bool IsEmptyBlock(TIntermNode *node) |
| { |
| TIntermBlock *asBlock = node->getAsBlock(); |
| if (asBlock) |
| { |
| if (asBlock->getSequence()->empty()) |
| { |
| return true; |
| } |
| return AreEmptyBlocks(asBlock->getSequence(), 0u); |
| } |
| // Empty declarations should have already been pruned, otherwise they would need to be handled |
| // here. Note that declarations for struct types do contain a nameless child node. |
| ASSERT(node->getAsDeclarationNode() == nullptr || |
| !node->getAsDeclarationNode()->getSequence()->empty()); |
| // Pure literal statements should also already be pruned. |
| ASSERT(node->getAsConstantUnion() == nullptr); |
| return false; |
| } |
| |
| // Return true if all statements in "statements" starting from index i consist only of empty blocks |
| // and no-op statements. Returns true also if there are no statements. |
| bool AreEmptyBlocks(TIntermSequence *statements, size_t i) |
| { |
| for (; i < statements->size(); ++i) |
| { |
| if (!IsEmptyBlock(statements->at(i))) |
| { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| void RemoveNoOpCasesFromEndOfStatementList(TIntermBlock *statementList, TSymbolTable *symbolTable) |
| { |
| TIntermSequence *statements = statementList->getSequence(); |
| |
| bool foundDeadCase = false; |
| do |
| { |
| if (statements->empty()) |
| { |
| return; |
| } |
| |
| // Find the last case label. |
| size_t i = statements->size(); |
| while (i > 0u && !(*statements)[i - 1]->getAsCaseNode()) |
| { |
| --i; |
| } |
| // Now i is the index of the first statement following the last label inside the switch |
| // statement. |
| ASSERT(i > 0u); |
| |
| foundDeadCase = AreEmptyBlocks(statements, i); |
| if (foundDeadCase) |
| { |
| statements->erase(statements->begin() + (i - 1u), statements->end()); |
| } |
| } while (foundDeadCase); |
| } |
| |
| class RemoveNoOpCasesFromEndOfSwitchTraverser : public TIntermTraverser |
| { |
| public: |
| RemoveNoOpCasesFromEndOfSwitchTraverser(TSymbolTable *symbolTable) |
| : TIntermTraverser(true, false, false, symbolTable) |
| { |
| } |
| |
| bool visitSwitch(Visit visit, TIntermSwitch *node) override; |
| }; |
| |
| bool RemoveNoOpCasesFromEndOfSwitchTraverser::visitSwitch(Visit visit, TIntermSwitch *node) |
| { |
| // Here we may mutate the statement list, but it's safe since traversal has not yet reached |
| // there. |
| RemoveNoOpCasesFromEndOfStatementList(node->getStatementList(), mSymbolTable); |
| // Handle also nested switch statements. |
| return true; |
| } |
| |
| } // anonymous namespace |
| |
| void RemoveNoOpCasesFromEndOfSwitchStatements(TIntermBlock *root, TSymbolTable *symbolTable) |
| { |
| RemoveNoOpCasesFromEndOfSwitchTraverser traverser(symbolTable); |
| root->traverse(&traverser); |
| } |
| |
| } // namespace sh |