PruneEmptyCases.cpp (3854B)
1 // 2 // Copyright 2018 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 // PruneEmptyCases.cpp: The PruneEmptyCases function prunes cases that are followed by nothing from 7 // the AST. 8 9 #include "compiler/translator/tree_ops/PruneEmptyCases.h" 10 11 #include "compiler/translator/Symbol.h" 12 #include "compiler/translator/tree_util/IntermTraverse.h" 13 14 namespace sh 15 { 16 17 namespace 18 { 19 20 bool AreEmptyBlocks(const TIntermSequence *statements); 21 22 bool IsEmptyBlock(TIntermNode *node) 23 { 24 TIntermBlock *asBlock = node->getAsBlock(); 25 if (asBlock) 26 { 27 return AreEmptyBlocks(asBlock->getSequence()); 28 } 29 // Empty declarations should have already been pruned, otherwise they would need to be handled 30 // here. Note that declarations for struct types do contain a nameless child node. 31 ASSERT(node->getAsDeclarationNode() == nullptr || 32 !node->getAsDeclarationNode()->getSequence()->empty()); 33 // Pure literal statements should also already be pruned. 34 ASSERT(node->getAsConstantUnion() == nullptr); 35 return false; 36 } 37 38 // Return true if all statements in "statements" consist only of empty blocks and no-op statements. 39 // Returns true also if there are no statements. 40 bool AreEmptyBlocks(const TIntermSequence *statements) 41 { 42 for (size_t i = 0u; i < statements->size(); ++i) 43 { 44 if (!IsEmptyBlock(statements->at(i))) 45 { 46 return false; 47 } 48 } 49 return true; 50 } 51 52 class PruneEmptyCasesTraverser : private TIntermTraverser 53 { 54 public: 55 [[nodiscard]] static bool apply(TCompiler *compiler, TIntermBlock *root); 56 57 private: 58 PruneEmptyCasesTraverser(); 59 bool visitSwitch(Visit visit, TIntermSwitch *node) override; 60 }; 61 62 bool PruneEmptyCasesTraverser::apply(TCompiler *compiler, TIntermBlock *root) 63 { 64 PruneEmptyCasesTraverser prune; 65 root->traverse(&prune); 66 return prune.updateTree(compiler, root); 67 } 68 69 PruneEmptyCasesTraverser::PruneEmptyCasesTraverser() : TIntermTraverser(true, false, false) {} 70 71 bool PruneEmptyCasesTraverser::visitSwitch(Visit visit, TIntermSwitch *node) 72 { 73 // This may mutate the statementList, but that's okay, since traversal has not yet reached 74 // there. 75 TIntermBlock *statementList = node->getStatementList(); 76 TIntermSequence *statements = statementList->getSequence(); 77 78 // Iterate block children in reverse order. Cases that are only followed by other cases or empty 79 // blocks are marked for pruning. 80 size_t i = statements->size(); 81 size_t lastNoOpInStatementList = i; 82 while (i > 0) 83 { 84 --i; 85 TIntermNode *statement = statements->at(i); 86 if (statement->getAsCaseNode() || IsEmptyBlock(statement)) 87 { 88 lastNoOpInStatementList = i; 89 } 90 else 91 { 92 break; 93 } 94 } 95 if (lastNoOpInStatementList == 0) 96 { 97 // Remove the entire switch statement, extracting the init expression if needed. 98 TIntermTyped *init = node->getInit(); 99 if (init->hasSideEffects()) 100 { 101 queueReplacement(init, OriginalNode::IS_DROPPED); 102 } 103 else 104 { 105 TIntermSequence emptyReplacement; 106 ASSERT(getParentNode()->getAsBlock()); 107 mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), node, 108 std::move(emptyReplacement)); 109 } 110 return false; 111 } 112 if (lastNoOpInStatementList < statements->size()) 113 { 114 statements->erase(statements->begin() + lastNoOpInStatementList, statements->end()); 115 } 116 117 return true; 118 } 119 120 } // namespace 121 122 bool PruneEmptyCases(TCompiler *compiler, TIntermBlock *root) 123 { 124 return PruneEmptyCasesTraverser::apply(compiler, root); 125 } 126 127 } // namespace sh