blob: c6d721e58e1e7952c0106e642cd4d24105b5c80e [file] [log] [blame]
Olli Etuaho3d70ca92017-11-10 16:53:26 +02001//
2// Copyright (c) 2017 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// RemoveUnreferencedVariables.cpp:
7// Drop variables that are declared but never referenced in the AST. This avoids adding unnecessary
8// initialization code for them.
9//
10
11#include "compiler/translator/RemoveUnreferencedVariables.h"
12
13#include "compiler/translator/IntermTraverse.h"
14#include "compiler/translator/SymbolTable.h"
15
16namespace sh
17{
18
19namespace
20{
21
22class CollectVariableRefCountsTraverser : public TIntermTraverser
23{
24 public:
25 CollectVariableRefCountsTraverser();
26
27 using RefCountMap = std::unordered_map<int, unsigned int>;
28 RefCountMap &getSymbolIdRefCounts() { return mSymbolIdRefCounts; }
29
30 void visitSymbol(TIntermSymbol *node) override;
31
32 private:
33 RefCountMap mSymbolIdRefCounts;
34};
35
36CollectVariableRefCountsTraverser::CollectVariableRefCountsTraverser()
37 : TIntermTraverser(true, false, false)
38{
39}
40
41void CollectVariableRefCountsTraverser::visitSymbol(TIntermSymbol *node)
42{
43 auto iter = mSymbolIdRefCounts.find(node->getId());
44 if (iter == mSymbolIdRefCounts.end())
45 {
46 mSymbolIdRefCounts[node->getId()] = 1u;
47 return;
48 }
49 ++(iter->second);
50}
51
52// Traverser that removes all unreferenced variables on one traversal.
53class RemoveUnreferencedVariablesTraverser : public TIntermTraverser
54{
55 public:
56 RemoveUnreferencedVariablesTraverser(
57 CollectVariableRefCountsTraverser::RefCountMap *symbolIdRefCounts,
58 TSymbolTable *symbolTable);
59
60 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
61 void visitSymbol(TIntermSymbol *node) override;
62
63 // Traverse loop and block nodes in reverse order. Note that this traverser does not track
64 // parent block positions, so insertStatementInParentBlock is unusable!
65 void traverseBlock(TIntermBlock *block) override;
66 void traverseLoop(TIntermLoop *loop) override;
67
68 private:
69 void removeDeclaration(TIntermDeclaration *node, TIntermTyped *declarator);
70
71 CollectVariableRefCountsTraverser::RefCountMap *mSymbolIdRefCounts;
72 bool mRemoveReferences;
73};
74
75RemoveUnreferencedVariablesTraverser::RemoveUnreferencedVariablesTraverser(
76 CollectVariableRefCountsTraverser::RefCountMap *symbolIdRefCounts,
77 TSymbolTable *symbolTable)
78 : TIntermTraverser(true, false, true, symbolTable),
79 mSymbolIdRefCounts(symbolIdRefCounts),
80 mRemoveReferences(false)
81{
82}
83
84void RemoveUnreferencedVariablesTraverser::removeDeclaration(TIntermDeclaration *node,
85 TIntermTyped *declarator)
86{
87 if (declarator->getType().isStructSpecifier() && !declarator->getType().isNamelessStruct())
88 {
89 // We don't count references to struct types, so if this declaration declares a named struct
90 // type, we'll keep it. We can still change the declarator though so that it doesn't declare
91 // a variable.
92 queueReplacementWithParent(
93 node, declarator,
94 new TIntermSymbol(mSymbolTable->getEmptySymbolId(), TString(""), declarator->getType()),
95 OriginalNode::IS_DROPPED);
96 return;
97 }
98
99 if (getParentNode()->getAsBlock())
100 {
101 TIntermSequence emptyReplacement;
102 mMultiReplacements.push_back(
103 NodeReplaceWithMultipleEntry(getParentNode()->getAsBlock(), node, emptyReplacement));
104 }
105 else
106 {
107 ASSERT(getParentNode()->getAsLoopNode());
108 queueReplacement(nullptr, OriginalNode::IS_DROPPED);
109 }
110}
111
112bool RemoveUnreferencedVariablesTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
113{
114 if (visit == PreVisit)
115 {
116 // SeparateDeclarations should have already been run.
117 ASSERT(node->getSequence()->size() == 1u);
118
119 TIntermTyped *declarator = node->getSequence()->back()->getAsTyped();
120 ASSERT(declarator);
121
122 // We can only remove variables that are not a part of the shader interface.
123 TQualifier qualifier = declarator->getQualifier();
124 if (qualifier != EvqTemporary && qualifier != EvqGlobal)
125 {
126 return true;
127 }
128
129 bool canRemove = false;
130 TIntermSymbol *symbolNode = declarator->getAsSymbolNode();
131 if (symbolNode != nullptr)
132 {
133 canRemove = (*mSymbolIdRefCounts)[symbolNode->getId()] == 1u;
134 }
135 TIntermBinary *initNode = declarator->getAsBinaryNode();
136 if (initNode != nullptr)
137 {
138 ASSERT(initNode->getLeft()->getAsSymbolNode());
139 int symbolId = initNode->getLeft()->getAsSymbolNode()->getId();
140 canRemove =
141 (*mSymbolIdRefCounts)[symbolId] == 1u && !initNode->getRight()->hasSideEffects();
142 }
143
144 if (canRemove)
145 {
146 removeDeclaration(node, declarator);
147 mRemoveReferences = true;
148 }
149 return true;
150 }
151 ASSERT(visit == PostVisit);
152 mRemoveReferences = false;
153 return true;
154}
155
156void RemoveUnreferencedVariablesTraverser::visitSymbol(TIntermSymbol *node)
157{
158 if (mRemoveReferences)
159 {
160 ASSERT(mSymbolIdRefCounts->find(node->getId()) != mSymbolIdRefCounts->end());
161 --(*mSymbolIdRefCounts)[node->getId()];
162 }
163}
164
165void RemoveUnreferencedVariablesTraverser::traverseBlock(TIntermBlock *node)
166{
167 // We traverse blocks in reverse order. This way reference counts can be decremented when
168 // removing initializers, and variables that become unused when initializers are removed can be
169 // removed on the same traversal.
170
171 ScopedNodeInTraversalPath addToPath(this, node);
172
173 bool visit = true;
174
175 TIntermSequence *sequence = node->getSequence();
176
177 if (preVisit)
178 visit = visitBlock(PreVisit, node);
179
180 if (visit)
181 {
182 for (auto iter = sequence->rbegin(); iter != sequence->rend(); ++iter)
183 {
184 (*iter)->traverse(this);
185 if (visit && inVisit)
186 {
187 if ((iter + 1) != sequence->rend())
188 visit = visitBlock(InVisit, node);
189 }
190 }
191 }
192
193 if (visit && postVisit)
194 visitBlock(PostVisit, node);
195}
196
197void RemoveUnreferencedVariablesTraverser::traverseLoop(TIntermLoop *node)
198{
199 // We traverse loops in reverse order as well. The loop body gets traversed before the init
200 // node.
201
202 ScopedNodeInTraversalPath addToPath(this, node);
203
204 bool visit = true;
205
206 if (preVisit)
207 visit = visitLoop(PreVisit, node);
208
209 if (visit)
210 {
211 // We don't need to traverse loop expressions or conditions since they can't be declarations
212 // in the AST (loops which have a declaration in their condition get transformed in the
213 // parsing stage).
214 ASSERT(node->getExpression() == nullptr ||
215 node->getExpression()->getAsDeclarationNode() == nullptr);
216 ASSERT(node->getCondition() == nullptr ||
217 node->getCondition()->getAsDeclarationNode() == nullptr);
218
219 if (node->getBody())
220 node->getBody()->traverse(this);
221
222 if (node->getInit())
223 node->getInit()->traverse(this);
224 }
225
226 if (visit && postVisit)
227 visitLoop(PostVisit, node);
228}
229
230} // namespace
231
232void RemoveUnreferencedVariables(TIntermBlock *root, TSymbolTable *symbolTable)
233{
234 CollectVariableRefCountsTraverser collector;
235 root->traverse(&collector);
236 RemoveUnreferencedVariablesTraverser traverser(&collector.getSymbolIdRefCounts(), symbolTable);
237 root->traverse(&traverser);
238 traverser.updateTree();
239}
240
241} // namespace sh