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(TIntermSequence *statements);
21 
IsEmptyBlock(TIntermNode * node)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.
AreEmptyBlocks(TIntermSequence * statements)40 bool AreEmptyBlocks(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     ANGLE_NO_DISCARD static bool apply(TCompiler *compiler, TIntermBlock *root);
56 
57   private:
58     PruneEmptyCasesTraverser();
59     bool visitSwitch(Visit visit, TIntermSwitch *node) override;
60 };
61 
apply(TCompiler * compiler,TIntermBlock * root)62 bool PruneEmptyCasesTraverser::apply(TCompiler *compiler, TIntermBlock *root)
63 {
64     PruneEmptyCasesTraverser prune;
65     root->traverse(&prune);
66     return prune.updateTree(compiler, root);
67 }
68 
PruneEmptyCasesTraverser()69 PruneEmptyCasesTraverser::PruneEmptyCasesTraverser() : TIntermTraverser(true, false, false) {}
70 
visitSwitch(Visit visit,TIntermSwitch * node)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 
PruneEmptyCases(TCompiler * compiler,TIntermBlock * root)122 bool PruneEmptyCases(TCompiler *compiler, TIntermBlock *root)
123 {
124     return PruneEmptyCasesTraverser::apply(compiler, root);
125 }
126 
127 }  // namespace sh
128