xref: /aosp_15_r20/external/angle/src/compiler/translator/tree_ops/PruneNoOps.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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 no-op statements.
7 
8 #include "compiler/translator/tree_ops/PruneNoOps.h"
9 
10 #include "compiler/translator/Symbol.h"
11 #include "compiler/translator/tree_util/IntermTraverse.h"
12 
13 namespace sh
14 {
15 
16 namespace
17 {
GetSwitchConstantAsUInt(const TConstantUnion * value)18 uint32_t GetSwitchConstantAsUInt(const TConstantUnion *value)
19 {
20     TConstantUnion asUInt;
21     if (value->getType() == EbtYuvCscStandardEXT)
22     {
23         asUInt.setUConst(value->getYuvCscStandardEXTConst());
24     }
25     else
26     {
27         bool valid = asUInt.cast(EbtUInt, *value);
28         ASSERT(valid);
29     }
30     return asUInt.getUConst();
31 }
32 
IsNoOpSwitch(TIntermSwitch * node)33 bool IsNoOpSwitch(TIntermSwitch *node)
34 {
35     if (node == nullptr)
36     {
37         return false;
38     }
39 
40     TIntermConstantUnion *expr = node->getInit()->getAsConstantUnion();
41     if (expr == nullptr)
42     {
43         return false;
44     }
45 
46     const uint32_t exprValue = GetSwitchConstantAsUInt(expr->getConstantValue());
47 
48     // See if any block matches the constant value
49     const TIntermSequence &statements = *node->getStatementList()->getSequence();
50 
51     for (TIntermNode *statement : statements)
52     {
53         TIntermCase *caseLabel = statement->getAsCaseNode();
54         if (caseLabel == nullptr)
55         {
56             continue;
57         }
58 
59         // Default matches everything, consider it not a no-op.
60         if (!caseLabel->hasCondition())
61         {
62             return false;
63         }
64 
65         TIntermConstantUnion *condition = caseLabel->getCondition()->getAsConstantUnion();
66         ASSERT(condition != nullptr);
67 
68         // If any case matches the value, it's not a no-op.
69         const uint32_t caseValue = GetSwitchConstantAsUInt(condition->getConstantValue());
70         if (caseValue == exprValue)
71         {
72             return false;
73         }
74     }
75 
76     // No case matched the constant value the switch was used on, so the entire switch is a no-op.
77     return true;
78 }
79 
IsNoOp(TIntermNode * node)80 bool IsNoOp(TIntermNode *node)
81 {
82     bool isEmptyDeclaration = node->getAsDeclarationNode() != nullptr &&
83                               node->getAsDeclarationNode()->getSequence()->empty();
84     if (isEmptyDeclaration)
85     {
86         return true;
87     }
88 
89     if (IsNoOpSwitch(node->getAsSwitchNode()))
90     {
91         return true;
92     }
93 
94     if (node->getAsTyped() == nullptr || node->getAsFunctionPrototypeNode() != nullptr)
95     {
96         return false;
97     }
98 
99     return !node->getAsTyped()->hasSideEffects();
100 }
101 
102 class PruneNoOpsTraverser : private TIntermTraverser
103 {
104   public:
105     [[nodiscard]] static bool apply(TCompiler *compiler,
106                                     TIntermBlock *root,
107                                     TSymbolTable *symbolTable);
108 
109   private:
110     PruneNoOpsTraverser(TSymbolTable *symbolTable);
111     bool visitDeclaration(Visit, TIntermDeclaration *node) override;
112     bool visitBlock(Visit visit, TIntermBlock *node) override;
113     bool visitLoop(Visit visit, TIntermLoop *loop) override;
114     bool visitBranch(Visit visit, TIntermBranch *node) override;
115 
116     bool mIsBranchVisited = false;
117 };
118 
apply(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable)119 bool PruneNoOpsTraverser::apply(TCompiler *compiler, TIntermBlock *root, TSymbolTable *symbolTable)
120 {
121     PruneNoOpsTraverser prune(symbolTable);
122     root->traverse(&prune);
123     return prune.updateTree(compiler, root);
124 }
125 
PruneNoOpsTraverser(TSymbolTable * symbolTable)126 PruneNoOpsTraverser::PruneNoOpsTraverser(TSymbolTable *symbolTable)
127     : TIntermTraverser(true, true, true, symbolTable)
128 {}
129 
visitDeclaration(Visit visit,TIntermDeclaration * node)130 bool PruneNoOpsTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
131 {
132     if (visit != PreVisit)
133     {
134         return true;
135     }
136 
137     TIntermSequence *sequence = node->getSequence();
138     if (sequence->size() >= 1)
139     {
140         TIntermSymbol *declaratorSymbol = sequence->front()->getAsSymbolNode();
141         // Prune declarations without a variable name, unless it's an interface block declaration.
142         if (declaratorSymbol != nullptr &&
143             declaratorSymbol->variable().symbolType() == SymbolType::Empty &&
144             !declaratorSymbol->isInterfaceBlock())
145         {
146             if (sequence->size() > 1)
147             {
148                 // Generate a replacement that will remove the empty declarator in the beginning of
149                 // a declarator list. Example of a declaration that will be changed:
150                 // float, a;
151                 // will be changed to
152                 // float a;
153                 // This applies also to struct declarations.
154                 TIntermSequence emptyReplacement;
155                 mMultiReplacements.emplace_back(node, declaratorSymbol,
156                                                 std::move(emptyReplacement));
157             }
158             else if (declaratorSymbol->getBasicType() != EbtStruct)
159             {
160                 // If there are entirely empty non-struct declarations, they result in
161                 // TIntermDeclaration nodes without any children in the parsing stage. These are
162                 // handled in visitBlock and visitLoop.
163                 UNREACHABLE();
164             }
165             else if (declaratorSymbol->getQualifier() != EvqGlobal &&
166                      declaratorSymbol->getQualifier() != EvqTemporary)
167             {
168                 // Single struct declarations may just declare the struct type and no variables, so
169                 // they should not be pruned. Here we handle an empty struct declaration with a
170                 // qualifier, for example like this:
171                 //   const struct a { int i; };
172                 // NVIDIA GL driver version 367.27 doesn't accept this kind of declarations, so we
173                 // convert the declaration to a regular struct declaration. This is okay, since ESSL
174                 // 1.00 spec section 4.1.8 says about structs that "The optional qualifiers only
175                 // apply to any declarators, and are not part of the type being defined for name."
176 
177                 // Create a new variable to use in the declarator so that the variable and node
178                 // types are kept consistent.
179                 TType *type = new TType(declaratorSymbol->getType());
180                 if (mInGlobalScope)
181                 {
182                     type->setQualifier(EvqGlobal);
183                 }
184                 else
185                 {
186                     type->setQualifier(EvqTemporary);
187                 }
188                 TVariable *variable =
189                     new TVariable(mSymbolTable, kEmptyImmutableString, type, SymbolType::Empty);
190                 queueReplacementWithParent(node, declaratorSymbol, new TIntermSymbol(variable),
191                                            OriginalNode::IS_DROPPED);
192             }
193         }
194     }
195     return false;
196 }
197 
visitBlock(Visit visit,TIntermBlock * node)198 bool PruneNoOpsTraverser::visitBlock(Visit visit, TIntermBlock *node)
199 {
200     ASSERT(visit == PreVisit);
201 
202     TIntermSequence &statements = *node->getSequence();
203 
204     // Visit each statement in the block one by one.  Once a branch is visited (break, continue,
205     // return or discard), drop the rest of the statements.
206     for (size_t statementIndex = 0; statementIndex < statements.size(); ++statementIndex)
207     {
208         TIntermNode *statement = statements[statementIndex];
209 
210         // If the statement is a switch case label, stop pruning and continue visiting the children.
211         if (statement->getAsCaseNode() != nullptr)
212         {
213             mIsBranchVisited = false;
214         }
215 
216         // If a branch is visited, prune the statement.  If the statement is a no-op, also prune it.
217         if (mIsBranchVisited || IsNoOp(statement))
218         {
219             TIntermSequence emptyReplacement;
220             mMultiReplacements.emplace_back(node, statement, std::move(emptyReplacement));
221             continue;
222         }
223 
224         // Visit the statement if not pruned.
225         statement->traverse(this);
226     }
227 
228     // If the parent is a block and mIsBranchVisited is set, this is a nested block without any
229     // condition (like if, loop or switch), so the rest of the parent block should also be pruned.
230     // Otherwise the parent block should be unaffected.
231     if (mIsBranchVisited && getParentNode()->getAsBlock() == nullptr)
232     {
233         mIsBranchVisited = false;
234     }
235 
236     return false;
237 }
238 
visitLoop(Visit visit,TIntermLoop * loop)239 bool PruneNoOpsTraverser::visitLoop(Visit visit, TIntermLoop *loop)
240 {
241     if (visit != PreVisit)
242     {
243         return true;
244     }
245 
246     TIntermTyped *expr = loop->getExpression();
247     if (expr != nullptr && IsNoOp(expr))
248     {
249         loop->setExpression(nullptr);
250     }
251     TIntermNode *init = loop->getInit();
252     if (init != nullptr && IsNoOp(init))
253     {
254         loop->setInit(nullptr);
255     }
256 
257     return true;
258 }
259 
visitBranch(Visit visit,TIntermBranch * node)260 bool PruneNoOpsTraverser::visitBranch(Visit visit, TIntermBranch *node)
261 {
262     ASSERT(visit == PreVisit);
263 
264     mIsBranchVisited = true;
265 
266     // Only possible child is the value of a return statement, which has nothing to prune.
267     return false;
268 }
269 }  // namespace
270 
PruneNoOps(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable)271 bool PruneNoOps(TCompiler *compiler, TIntermBlock *root, TSymbolTable *symbolTable)
272 {
273     return PruneNoOpsTraverser::apply(compiler, root, symbolTable);
274 }
275 
276 }  // namespace sh
277