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