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