blob: 3ba66c5ba40a9af25995bcb3f79f49f2ddce81c2 [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
Olli Etuaho39f74df2017-11-20 16:09:57 +02008// initialization code for them. Also removes unreferenced struct types.
Olli Etuaho3d70ca92017-11-10 16:53:26 +02009//
10
11#include "compiler/translator/RemoveUnreferencedVariables.h"
12
Olli Etuaho3d70ca92017-11-10 16:53:26 +020013#include "compiler/translator/SymbolTable.h"
Olli Etuahoc26214d2018-03-16 10:43:11 +020014#include "compiler/translator/tree_util/IntermTraverse.h"
Olli Etuaho3d70ca92017-11-10 16:53:26 +020015
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; }
Olli Etuaho39f74df2017-11-20 16:09:57 +020029 RefCountMap &getStructIdRefCounts() { return mStructIdRefCounts; }
Olli Etuaho3d70ca92017-11-10 16:53:26 +020030
31 void visitSymbol(TIntermSymbol *node) override;
Olli Etuaho39f74df2017-11-20 16:09:57 +020032 bool visitAggregate(Visit visit, TIntermAggregate *node) override;
Olli Etuahod4bd9632018-03-08 16:32:44 +020033 void visitFunctionPrototype(TIntermFunctionPrototype *node) override;
Olli Etuaho3d70ca92017-11-10 16:53:26 +020034
35 private:
Olli Etuaho39f74df2017-11-20 16:09:57 +020036 void incrementStructTypeRefCount(const TType &type);
37
Olli Etuaho3d70ca92017-11-10 16:53:26 +020038 RefCountMap mSymbolIdRefCounts;
Olli Etuaho39f74df2017-11-20 16:09:57 +020039
40 // Structure reference counts are counted from symbols, constructors, function calls, function
41 // return values and from interface block and structure fields. We need to track both function
42 // calls and function return values since there's a compiler option not to prune unused
43 // functions. The type of a constant union may also be a struct, but statements that are just a
44 // constant union are always pruned, and if the constant union is used somehow it will get
45 // counted by something else.
46 RefCountMap mStructIdRefCounts;
Olli Etuaho3d70ca92017-11-10 16:53:26 +020047};
48
49CollectVariableRefCountsTraverser::CollectVariableRefCountsTraverser()
50 : TIntermTraverser(true, false, false)
51{
52}
53
Olli Etuaho39f74df2017-11-20 16:09:57 +020054void CollectVariableRefCountsTraverser::incrementStructTypeRefCount(const TType &type)
55{
56 if (type.isInterfaceBlock())
57 {
58 const auto *block = type.getInterfaceBlock();
59 ASSERT(block);
60
61 // We can end up incrementing ref counts of struct types referenced from an interface block
62 // multiple times for the same block. This doesn't matter, because interface blocks can't be
63 // pruned so we'll never do the reverse operation.
64 for (const auto &field : block->fields())
65 {
66 ASSERT(!field->type()->isInterfaceBlock());
67 incrementStructTypeRefCount(*field->type());
68 }
69 return;
70 }
71
72 const auto *structure = type.getStruct();
73 if (structure != nullptr)
74 {
Olli Etuaho97fa8552017-11-28 16:28:42 +020075 auto structIter = mStructIdRefCounts.find(structure->uniqueId().get());
Olli Etuaho39f74df2017-11-20 16:09:57 +020076 if (structIter == mStructIdRefCounts.end())
77 {
Olli Etuaho97fa8552017-11-28 16:28:42 +020078 mStructIdRefCounts[structure->uniqueId().get()] = 1u;
Olli Etuaho39f74df2017-11-20 16:09:57 +020079
80 for (const auto &field : structure->fields())
81 {
82 incrementStructTypeRefCount(*field->type());
83 }
84
85 return;
86 }
87 ++(structIter->second);
88 }
89}
90
Olli Etuaho3d70ca92017-11-10 16:53:26 +020091void CollectVariableRefCountsTraverser::visitSymbol(TIntermSymbol *node)
92{
Olli Etuaho39f74df2017-11-20 16:09:57 +020093 incrementStructTypeRefCount(node->getType());
94
Olli Etuahob6af22b2017-12-15 14:05:44 +020095 auto iter = mSymbolIdRefCounts.find(node->uniqueId().get());
Olli Etuaho3d70ca92017-11-10 16:53:26 +020096 if (iter == mSymbolIdRefCounts.end())
97 {
Olli Etuahob6af22b2017-12-15 14:05:44 +020098 mSymbolIdRefCounts[node->uniqueId().get()] = 1u;
Olli Etuaho3d70ca92017-11-10 16:53:26 +020099 return;
100 }
101 ++(iter->second);
102}
103
Olli Etuaho39f74df2017-11-20 16:09:57 +0200104bool CollectVariableRefCountsTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
105{
106 // This tracks struct references in both function calls and constructors.
107 incrementStructTypeRefCount(node->getType());
108 return true;
109}
110
Olli Etuahod4bd9632018-03-08 16:32:44 +0200111void CollectVariableRefCountsTraverser::visitFunctionPrototype(TIntermFunctionPrototype *node)
Olli Etuaho39f74df2017-11-20 16:09:57 +0200112{
113 incrementStructTypeRefCount(node->getType());
Olli Etuahod4bd9632018-03-08 16:32:44 +0200114 size_t paramCount = node->getFunction()->getParamCount();
115 for (size_t i = 0; i < paramCount; ++i)
116 {
117 incrementStructTypeRefCount(node->getFunction()->getParam(i)->getType());
118 }
Olli Etuaho39f74df2017-11-20 16:09:57 +0200119}
120
Olli Etuaho3d70ca92017-11-10 16:53:26 +0200121// Traverser that removes all unreferenced variables on one traversal.
122class RemoveUnreferencedVariablesTraverser : public TIntermTraverser
123{
124 public:
125 RemoveUnreferencedVariablesTraverser(
126 CollectVariableRefCountsTraverser::RefCountMap *symbolIdRefCounts,
Olli Etuaho39f74df2017-11-20 16:09:57 +0200127 CollectVariableRefCountsTraverser::RefCountMap *structIdRefCounts,
Olli Etuaho3d70ca92017-11-10 16:53:26 +0200128 TSymbolTable *symbolTable);
129
130 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
131 void visitSymbol(TIntermSymbol *node) override;
Olli Etuaho39f74df2017-11-20 16:09:57 +0200132 bool visitAggregate(Visit visit, TIntermAggregate *node) override;
Olli Etuaho3d70ca92017-11-10 16:53:26 +0200133
134 // Traverse loop and block nodes in reverse order. Note that this traverser does not track
135 // parent block positions, so insertStatementInParentBlock is unusable!
136 void traverseBlock(TIntermBlock *block) override;
137 void traverseLoop(TIntermLoop *loop) override;
138
139 private:
Olli Etuaho39f74df2017-11-20 16:09:57 +0200140 void removeVariableDeclaration(TIntermDeclaration *node, TIntermTyped *declarator);
141 void decrementStructTypeRefCount(const TType &type);
Olli Etuaho3d70ca92017-11-10 16:53:26 +0200142
143 CollectVariableRefCountsTraverser::RefCountMap *mSymbolIdRefCounts;
Olli Etuaho39f74df2017-11-20 16:09:57 +0200144 CollectVariableRefCountsTraverser::RefCountMap *mStructIdRefCounts;
Olli Etuaho3d70ca92017-11-10 16:53:26 +0200145 bool mRemoveReferences;
146};
147
148RemoveUnreferencedVariablesTraverser::RemoveUnreferencedVariablesTraverser(
149 CollectVariableRefCountsTraverser::RefCountMap *symbolIdRefCounts,
Olli Etuaho39f74df2017-11-20 16:09:57 +0200150 CollectVariableRefCountsTraverser::RefCountMap *structIdRefCounts,
Olli Etuaho3d70ca92017-11-10 16:53:26 +0200151 TSymbolTable *symbolTable)
152 : TIntermTraverser(true, false, true, symbolTable),
153 mSymbolIdRefCounts(symbolIdRefCounts),
Olli Etuaho39f74df2017-11-20 16:09:57 +0200154 mStructIdRefCounts(structIdRefCounts),
Olli Etuaho3d70ca92017-11-10 16:53:26 +0200155 mRemoveReferences(false)
156{
157}
158
Olli Etuaho39f74df2017-11-20 16:09:57 +0200159void RemoveUnreferencedVariablesTraverser::decrementStructTypeRefCount(const TType &type)
160{
161 auto *structure = type.getStruct();
162 if (structure != nullptr)
163 {
Olli Etuaho97fa8552017-11-28 16:28:42 +0200164 ASSERT(mStructIdRefCounts->find(structure->uniqueId().get()) != mStructIdRefCounts->end());
165 unsigned int structRefCount = --(*mStructIdRefCounts)[structure->uniqueId().get()];
Olli Etuaho39f74df2017-11-20 16:09:57 +0200166
167 if (structRefCount == 0)
168 {
169 for (const auto &field : structure->fields())
170 {
171 decrementStructTypeRefCount(*field->type());
172 }
173 }
174 }
175}
176
177void RemoveUnreferencedVariablesTraverser::removeVariableDeclaration(TIntermDeclaration *node,
178 TIntermTyped *declarator)
Olli Etuaho3d70ca92017-11-10 16:53:26 +0200179{
180 if (declarator->getType().isStructSpecifier() && !declarator->getType().isNamelessStruct())
181 {
Olli Etuaho97fa8552017-11-28 16:28:42 +0200182 unsigned int structId = declarator->getType().getStruct()->uniqueId().get();
Olli Etuahoea78d2b2018-01-09 12:55:27 +0200183 unsigned int structRefCountInThisDeclarator = 1u;
184 if (declarator->getAsBinaryNode() &&
185 declarator->getAsBinaryNode()->getRight()->getAsAggregate())
186 {
187 ASSERT(declarator->getAsBinaryNode()->getLeft()->getType().getStruct() ==
188 declarator->getType().getStruct());
189 ASSERT(declarator->getAsBinaryNode()->getRight()->getType().getStruct() ==
190 declarator->getType().getStruct());
191 structRefCountInThisDeclarator = 2u;
192 }
193 if ((*mStructIdRefCounts)[structId] > structRefCountInThisDeclarator)
Olli Etuaho39f74df2017-11-20 16:09:57 +0200194 {
195 // If this declaration declares a named struct type that is used elsewhere, we need to
196 // keep it. We can still change the declarator though so that it doesn't declare an
197 // unreferenced variable.
198
199 // Note that since we're not removing the entire declaration, the struct's reference
200 // count will end up being one less than the correct refcount. But since the struct
201 // declaration is kept, the incorrect refcount can't cause any other problems.
202
Olli Etuaho8b5e8fd2017-12-15 14:59:15 +0200203 if (declarator->getAsSymbolNode() &&
204 declarator->getAsSymbolNode()->variable().symbolType() == SymbolType::Empty)
Olli Etuaho39f74df2017-11-20 16:09:57 +0200205 {
206 // Already an empty declaration - nothing to do.
207 return;
208 }
Olli Etuahofbb1c792018-01-19 16:26:59 +0200209 TVariable *emptyVariable =
210 new TVariable(mSymbolTable, ImmutableString(""), new TType(declarator->getType()),
211 SymbolType::Empty);
Olli Etuaho195be942017-12-04 23:40:14 +0200212 queueReplacementWithParent(node, declarator, new TIntermSymbol(emptyVariable),
Olli Etuaho39f74df2017-11-20 16:09:57 +0200213 OriginalNode::IS_DROPPED);
214 return;
215 }
Olli Etuaho3d70ca92017-11-10 16:53:26 +0200216 }
217
218 if (getParentNode()->getAsBlock())
219 {
220 TIntermSequence emptyReplacement;
221 mMultiReplacements.push_back(
222 NodeReplaceWithMultipleEntry(getParentNode()->getAsBlock(), node, emptyReplacement));
223 }
224 else
225 {
226 ASSERT(getParentNode()->getAsLoopNode());
227 queueReplacement(nullptr, OriginalNode::IS_DROPPED);
228 }
229}
230
231bool RemoveUnreferencedVariablesTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
232{
233 if (visit == PreVisit)
234 {
235 // SeparateDeclarations should have already been run.
236 ASSERT(node->getSequence()->size() == 1u);
237
238 TIntermTyped *declarator = node->getSequence()->back()->getAsTyped();
239 ASSERT(declarator);
240
241 // We can only remove variables that are not a part of the shader interface.
242 TQualifier qualifier = declarator->getQualifier();
Olli Etuahoea22b7a2018-01-04 17:09:11 +0200243 if (qualifier != EvqTemporary && qualifier != EvqGlobal && qualifier != EvqConst)
Olli Etuaho3d70ca92017-11-10 16:53:26 +0200244 {
245 return true;
246 }
247
Olli Etuaho39f74df2017-11-20 16:09:57 +0200248 bool canRemoveVariable = false;
Olli Etuaho3d70ca92017-11-10 16:53:26 +0200249 TIntermSymbol *symbolNode = declarator->getAsSymbolNode();
250 if (symbolNode != nullptr)
251 {
Olli Etuahob6af22b2017-12-15 14:05:44 +0200252 canRemoveVariable = (*mSymbolIdRefCounts)[symbolNode->uniqueId().get()] == 1u ||
Olli Etuaho8b5e8fd2017-12-15 14:59:15 +0200253 symbolNode->variable().symbolType() == SymbolType::Empty;
Olli Etuaho3d70ca92017-11-10 16:53:26 +0200254 }
255 TIntermBinary *initNode = declarator->getAsBinaryNode();
256 if (initNode != nullptr)
257 {
258 ASSERT(initNode->getLeft()->getAsSymbolNode());
Olli Etuahob6af22b2017-12-15 14:05:44 +0200259 int symbolId = initNode->getLeft()->getAsSymbolNode()->uniqueId().get();
Olli Etuaho39f74df2017-11-20 16:09:57 +0200260 canRemoveVariable =
Olli Etuaho3d70ca92017-11-10 16:53:26 +0200261 (*mSymbolIdRefCounts)[symbolId] == 1u && !initNode->getRight()->hasSideEffects();
262 }
263
Olli Etuaho39f74df2017-11-20 16:09:57 +0200264 if (canRemoveVariable)
Olli Etuaho3d70ca92017-11-10 16:53:26 +0200265 {
Olli Etuaho39f74df2017-11-20 16:09:57 +0200266 removeVariableDeclaration(node, declarator);
Olli Etuaho3d70ca92017-11-10 16:53:26 +0200267 mRemoveReferences = true;
268 }
269 return true;
270 }
271 ASSERT(visit == PostVisit);
272 mRemoveReferences = false;
273 return true;
274}
275
276void RemoveUnreferencedVariablesTraverser::visitSymbol(TIntermSymbol *node)
277{
278 if (mRemoveReferences)
279 {
Olli Etuahob6af22b2017-12-15 14:05:44 +0200280 ASSERT(mSymbolIdRefCounts->find(node->uniqueId().get()) != mSymbolIdRefCounts->end());
281 --(*mSymbolIdRefCounts)[node->uniqueId().get()];
Olli Etuaho39f74df2017-11-20 16:09:57 +0200282
283 decrementStructTypeRefCount(node->getType());
Olli Etuaho3d70ca92017-11-10 16:53:26 +0200284 }
285}
286
Olli Etuaho39f74df2017-11-20 16:09:57 +0200287bool RemoveUnreferencedVariablesTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
288{
Olli Etuahoea78d2b2018-01-09 12:55:27 +0200289 if (visit == PreVisit && mRemoveReferences)
Olli Etuaho39f74df2017-11-20 16:09:57 +0200290 {
291 decrementStructTypeRefCount(node->getType());
292 }
293 return true;
294}
295
Olli Etuaho3d70ca92017-11-10 16:53:26 +0200296void RemoveUnreferencedVariablesTraverser::traverseBlock(TIntermBlock *node)
297{
298 // We traverse blocks in reverse order. This way reference counts can be decremented when
299 // removing initializers, and variables that become unused when initializers are removed can be
300 // removed on the same traversal.
301
302 ScopedNodeInTraversalPath addToPath(this, node);
303
304 bool visit = true;
305
306 TIntermSequence *sequence = node->getSequence();
307
308 if (preVisit)
309 visit = visitBlock(PreVisit, node);
310
311 if (visit)
312 {
313 for (auto iter = sequence->rbegin(); iter != sequence->rend(); ++iter)
314 {
315 (*iter)->traverse(this);
316 if (visit && inVisit)
317 {
318 if ((iter + 1) != sequence->rend())
319 visit = visitBlock(InVisit, node);
320 }
321 }
322 }
323
324 if (visit && postVisit)
325 visitBlock(PostVisit, node);
326}
327
328void RemoveUnreferencedVariablesTraverser::traverseLoop(TIntermLoop *node)
329{
330 // We traverse loops in reverse order as well. The loop body gets traversed before the init
331 // node.
332
333 ScopedNodeInTraversalPath addToPath(this, node);
334
335 bool visit = true;
336
337 if (preVisit)
338 visit = visitLoop(PreVisit, node);
339
340 if (visit)
341 {
342 // We don't need to traverse loop expressions or conditions since they can't be declarations
343 // in the AST (loops which have a declaration in their condition get transformed in the
344 // parsing stage).
345 ASSERT(node->getExpression() == nullptr ||
346 node->getExpression()->getAsDeclarationNode() == nullptr);
347 ASSERT(node->getCondition() == nullptr ||
348 node->getCondition()->getAsDeclarationNode() == nullptr);
349
350 if (node->getBody())
351 node->getBody()->traverse(this);
352
353 if (node->getInit())
354 node->getInit()->traverse(this);
355 }
356
357 if (visit && postVisit)
358 visitLoop(PostVisit, node);
359}
360
361} // namespace
362
363void RemoveUnreferencedVariables(TIntermBlock *root, TSymbolTable *symbolTable)
364{
365 CollectVariableRefCountsTraverser collector;
366 root->traverse(&collector);
Olli Etuaho39f74df2017-11-20 16:09:57 +0200367 RemoveUnreferencedVariablesTraverser traverser(&collector.getSymbolIdRefCounts(),
368 &collector.getStructIdRefCounts(), symbolTable);
Olli Etuaho3d70ca92017-11-10 16:53:26 +0200369 root->traverse(&traverser);
370 traverser.updateTree();
371}
372
373} // namespace sh