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