1//
2// Copyright (c) 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
14namespace sh
15{
16
17namespace
18{
19
20bool AreEmptyBlocks(TIntermSequence *statements);
21
22bool 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.
40bool 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
52class PruneEmptyCasesTraverser : private TIntermTraverser
53{
54 public:
55 static void apply(TIntermBlock *root);
56
57 private:
58 PruneEmptyCasesTraverser();
59 bool visitSwitch(Visit visit, TIntermSwitch *node) override;
60};
61
62void PruneEmptyCasesTraverser::apply(TIntermBlock *root)
63{
64 PruneEmptyCasesTraverser prune;
65 root->traverse(&prune);
66 prune.updateTree();
67}
68
69PruneEmptyCasesTraverser::PruneEmptyCasesTraverser() : TIntermTraverser(true, false, false) {}
70
71bool 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.push_back(NodeReplaceWithMultipleEntry(getParentNode()->getAsBlock(),
108 node, 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
122void PruneEmptyCases(TIntermBlock *root)
123{
124 PruneEmptyCasesTraverser::apply(root);
125}
126
127} // namespace sh
128