1 //
2 // Copyright 2002 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 // PruneNoOps.cpp: The PruneNoOps function prunes:
7 // 1. Empty declarations "int;". Empty declarators will be pruned as well, so for example:
8 // int , a;
9 // is turned into
10 // int a;
11 // 2. Literal statements: "1.0;". The ESSL output doesn't define a default precision for float,
12 // so float literal statements would end up with no precision which is invalid ESSL.
13 // 3. Statements after discard, return, break and continue.
14
15 #include "compiler/translator/tree_ops/PruneNoOps.h"
16
17 #include "compiler/translator/Symbol.h"
18 #include "compiler/translator/tree_util/IntermTraverse.h"
19
20 namespace sh
21 {
22
23 namespace
24 {
25
IsNoOp(TIntermNode * node)26 bool IsNoOp(TIntermNode *node)
27 {
28 if (node->getAsConstantUnion() != nullptr)
29 {
30 return true;
31 }
32 bool isEmptyDeclaration = node->getAsDeclarationNode() != nullptr &&
33 node->getAsDeclarationNode()->getSequence()->empty();
34 if (isEmptyDeclaration)
35 {
36 return true;
37 }
38 return false;
39 }
40
41 class PruneNoOpsTraverser : private TIntermTraverser
42 {
43 public:
44 ANGLE_NO_DISCARD static bool apply(TCompiler *compiler,
45 TIntermBlock *root,
46 TSymbolTable *symbolTable);
47
48 private:
49 PruneNoOpsTraverser(TSymbolTable *symbolTable);
50 bool visitDeclaration(Visit, TIntermDeclaration *node) override;
51 bool visitBlock(Visit visit, TIntermBlock *node) override;
52 bool visitLoop(Visit visit, TIntermLoop *loop) override;
53 bool visitBranch(Visit visit, TIntermBranch *node) override;
54
55 bool mIsBranchVisited = false;
56 };
57
apply(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable)58 bool PruneNoOpsTraverser::apply(TCompiler *compiler, TIntermBlock *root, TSymbolTable *symbolTable)
59 {
60 PruneNoOpsTraverser prune(symbolTable);
61 root->traverse(&prune);
62 return prune.updateTree(compiler, root);
63 }
64
PruneNoOpsTraverser(TSymbolTable * symbolTable)65 PruneNoOpsTraverser::PruneNoOpsTraverser(TSymbolTable *symbolTable)
66 : TIntermTraverser(true, true, true, symbolTable)
67 {}
68
visitDeclaration(Visit visit,TIntermDeclaration * node)69 bool PruneNoOpsTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
70 {
71 if (visit != PreVisit)
72 {
73 return true;
74 }
75
76 TIntermSequence *sequence = node->getSequence();
77 if (sequence->size() >= 1)
78 {
79 TIntermSymbol *declaratorSymbol = sequence->front()->getAsSymbolNode();
80 // Prune declarations without a variable name, unless it's an interface block declaration.
81 if (declaratorSymbol != nullptr &&
82 declaratorSymbol->variable().symbolType() == SymbolType::Empty &&
83 !declaratorSymbol->isInterfaceBlock())
84 {
85 if (sequence->size() > 1)
86 {
87 // Generate a replacement that will remove the empty declarator in the beginning of
88 // a declarator list. Example of a declaration that will be changed:
89 // float, a;
90 // will be changed to
91 // float a;
92 // This applies also to struct declarations.
93 TIntermSequence emptyReplacement;
94 mMultiReplacements.emplace_back(node, declaratorSymbol,
95 std::move(emptyReplacement));
96 }
97 else if (declaratorSymbol->getBasicType() != EbtStruct)
98 {
99 // If there are entirely empty non-struct declarations, they result in
100 // TIntermDeclaration nodes without any children in the parsing stage. These are
101 // handled in visitBlock and visitLoop.
102 UNREACHABLE();
103 }
104 else if (declaratorSymbol->getQualifier() != EvqGlobal &&
105 declaratorSymbol->getQualifier() != EvqTemporary)
106 {
107 // Single struct declarations may just declare the struct type and no variables, so
108 // they should not be pruned. Here we handle an empty struct declaration with a
109 // qualifier, for example like this:
110 // const struct a { int i; };
111 // NVIDIA GL driver version 367.27 doesn't accept this kind of declarations, so we
112 // convert the declaration to a regular struct declaration. This is okay, since ESSL
113 // 1.00 spec section 4.1.8 says about structs that "The optional qualifiers only
114 // apply to any declarators, and are not part of the type being defined for name."
115
116 // Create a new variable to use in the declarator so that the variable and node
117 // types are kept consistent.
118 TType *type = new TType(declaratorSymbol->getType());
119 if (mInGlobalScope)
120 {
121 type->setQualifier(EvqGlobal);
122 }
123 else
124 {
125 type->setQualifier(EvqTemporary);
126 }
127 TVariable *variable =
128 new TVariable(mSymbolTable, kEmptyImmutableString, type, SymbolType::Empty);
129 queueReplacementWithParent(node, declaratorSymbol, new TIntermSymbol(variable),
130 OriginalNode::IS_DROPPED);
131 }
132 }
133 }
134 return false;
135 }
136
visitBlock(Visit visit,TIntermBlock * node)137 bool PruneNoOpsTraverser::visitBlock(Visit visit, TIntermBlock *node)
138 {
139 if (visit == PreVisit)
140 {
141 return true;
142 }
143
144 TIntermSequence *statements = node->getSequence();
145 const size_t lastChildIndex = getLastTraversedChildIndex(visit);
146 TIntermSequence emptyReplacement;
147
148 // If a branch is visited, prune the rest of the statements.
149 if (mIsBranchVisited)
150 {
151 for (size_t removeIndex = lastChildIndex + 1; removeIndex < statements->size();
152 ++removeIndex)
153 {
154 TIntermNode *statement = (*statements)[removeIndex];
155
156 // If the statement is a switch case label, stop pruning and continue visiting the
157 // children.
158 if (statement->getAsCaseNode() != nullptr)
159 {
160 mIsBranchVisited = false;
161 return true;
162 }
163
164 mMultiReplacements.emplace_back(node, statement, std::move(emptyReplacement));
165 }
166
167 // If the parent is a block, this is a nested block without any condition (like if, loop or
168 // switch), so the rest of the parent block should also be pruned. Otherwise the parent
169 // block should be unaffected.
170 if (getParentNode()->getAsBlock() == nullptr)
171 {
172 mIsBranchVisited = false;
173 }
174
175 // Don't visit the pruned children.
176 return false;
177 }
178
179 // If the statement is a noop, prune it.
180 if (!statements->empty())
181 {
182 TIntermNode *statement = (*statements)[lastChildIndex];
183 if (IsNoOp(statement))
184 {
185 mMultiReplacements.emplace_back(node, statement, std::move(emptyReplacement));
186 }
187 }
188
189 return true;
190 }
191
visitLoop(Visit visit,TIntermLoop * loop)192 bool PruneNoOpsTraverser::visitLoop(Visit visit, TIntermLoop *loop)
193 {
194 if (visit != PreVisit)
195 {
196 return true;
197 }
198
199 TIntermTyped *expr = loop->getExpression();
200 if (expr != nullptr && IsNoOp(expr))
201 {
202 loop->setExpression(nullptr);
203 }
204 TIntermNode *init = loop->getInit();
205 if (init != nullptr && IsNoOp(init))
206 {
207 loop->setInit(nullptr);
208 }
209
210 return true;
211 }
212
visitBranch(Visit visit,TIntermBranch * node)213 bool PruneNoOpsTraverser::visitBranch(Visit visit, TIntermBranch *node)
214 {
215 ASSERT(visit == PreVisit);
216
217 mIsBranchVisited = true;
218
219 // Only possible child is the value of a return statement, which has nothing to prune.
220 return false;
221 }
222 } // namespace
223
PruneNoOps(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable)224 bool PruneNoOps(TCompiler *compiler, TIntermBlock *root, TSymbolTable *symbolTable)
225 {
226 return PruneNoOpsTraverser::apply(compiler, root, symbolTable);
227 }
228
229 } // namespace sh
230