• 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 
14 #include "compiler/translator/tree_ops/PruneNoOps.h"
15 
16 #include "compiler/translator/Symbol.h"
17 #include "compiler/translator/tree_util/IntermTraverse.h"
18 
19 namespace sh
20 {
21 
22 namespace
23 {
24 
IsNoOp(TIntermNode * node)25 bool IsNoOp(TIntermNode *node)
26 {
27     if (node->getAsConstantUnion() != nullptr)
28     {
29         return true;
30     }
31     bool isEmptyDeclaration = node->getAsDeclarationNode() != nullptr &&
32                               node->getAsDeclarationNode()->getSequence()->empty();
33     if (isEmptyDeclaration)
34     {
35         return true;
36     }
37     return false;
38 }
39 
40 class PruneNoOpsTraverser : private TIntermTraverser
41 {
42   public:
43     ANGLE_NO_DISCARD static bool apply(TCompiler *compiler,
44                                        TIntermBlock *root,
45                                        TSymbolTable *symbolTable);
46 
47   private:
48     PruneNoOpsTraverser(TSymbolTable *symbolTable);
49     bool visitDeclaration(Visit, TIntermDeclaration *node) override;
50     bool visitBlock(Visit visit, TIntermBlock *node) override;
51     bool visitLoop(Visit visit, TIntermLoop *loop) override;
52 };
53 
apply(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable)54 bool PruneNoOpsTraverser::apply(TCompiler *compiler, TIntermBlock *root, TSymbolTable *symbolTable)
55 {
56     PruneNoOpsTraverser prune(symbolTable);
57     root->traverse(&prune);
58     return prune.updateTree(compiler, root);
59 }
60 
PruneNoOpsTraverser(TSymbolTable * symbolTable)61 PruneNoOpsTraverser::PruneNoOpsTraverser(TSymbolTable *symbolTable)
62     : TIntermTraverser(true, false, false, symbolTable)
63 {}
64 
visitDeclaration(Visit,TIntermDeclaration * node)65 bool PruneNoOpsTraverser::visitDeclaration(Visit, TIntermDeclaration *node)
66 {
67     TIntermSequence *sequence = node->getSequence();
68     if (sequence->size() >= 1)
69     {
70         TIntermSymbol *declaratorSymbol = sequence->front()->getAsSymbolNode();
71         // Prune declarations without a variable name, unless it's an interface block declaration.
72         if (declaratorSymbol != nullptr &&
73             declaratorSymbol->variable().symbolType() == SymbolType::Empty &&
74             !declaratorSymbol->isInterfaceBlock())
75         {
76             if (sequence->size() > 1)
77             {
78                 // Generate a replacement that will remove the empty declarator in the beginning of
79                 // a declarator list. Example of a declaration that will be changed:
80                 // float, a;
81                 // will be changed to
82                 // float a;
83                 // This applies also to struct declarations.
84                 TIntermSequence emptyReplacement;
85                 mMultiReplacements.push_back(
86                     NodeReplaceWithMultipleEntry(node, declaratorSymbol, emptyReplacement));
87             }
88             else if (declaratorSymbol->getBasicType() != EbtStruct)
89             {
90                 // If there are entirely empty non-struct declarations, they result in
91                 // TIntermDeclaration nodes without any children in the parsing stage. These are
92                 // handled in visitBlock and visitLoop.
93                 UNREACHABLE();
94             }
95             else if (declaratorSymbol->getQualifier() != EvqGlobal &&
96                      declaratorSymbol->getQualifier() != EvqTemporary)
97             {
98                 // Single struct declarations may just declare the struct type and no variables, so
99                 // they should not be pruned. Here we handle an empty struct declaration with a
100                 // qualifier, for example like this:
101                 //   const struct a { int i; };
102                 // NVIDIA GL driver version 367.27 doesn't accept this kind of declarations, so we
103                 // convert the declaration to a regular struct declaration. This is okay, since ESSL
104                 // 1.00 spec section 4.1.8 says about structs that "The optional qualifiers only
105                 // apply to any declarators, and are not part of the type being defined for name."
106 
107                 // Create a new variable to use in the declarator so that the variable and node
108                 // types are kept consistent.
109                 TType *type = new TType(declaratorSymbol->getType());
110                 if (mInGlobalScope)
111                 {
112                     type->setQualifier(EvqGlobal);
113                 }
114                 else
115                 {
116                     type->setQualifier(EvqTemporary);
117                 }
118                 TVariable *variable =
119                     new TVariable(mSymbolTable, kEmptyImmutableString, type, SymbolType::Empty);
120                 queueReplacementWithParent(node, declaratorSymbol, new TIntermSymbol(variable),
121                                            OriginalNode::IS_DROPPED);
122             }
123         }
124     }
125     return false;
126 }
127 
visitBlock(Visit visit,TIntermBlock * node)128 bool PruneNoOpsTraverser::visitBlock(Visit visit, TIntermBlock *node)
129 {
130     TIntermSequence *statements = node->getSequence();
131 
132     for (TIntermNode *statement : *statements)
133     {
134         if (IsNoOp(statement))
135         {
136             TIntermSequence emptyReplacement;
137             mMultiReplacements.push_back(
138                 NodeReplaceWithMultipleEntry(node, statement, emptyReplacement));
139         }
140     }
141 
142     return true;
143 }
144 
visitLoop(Visit visit,TIntermLoop * loop)145 bool PruneNoOpsTraverser::visitLoop(Visit visit, TIntermLoop *loop)
146 {
147     TIntermTyped *expr = loop->getExpression();
148     if (expr != nullptr && IsNoOp(expr))
149     {
150         loop->setExpression(nullptr);
151     }
152     TIntermNode *init = loop->getInit();
153     if (init != nullptr && IsNoOp(init))
154     {
155         loop->setInit(nullptr);
156     }
157 
158     return true;
159 }
160 
161 }  // namespace
162 
PruneNoOps(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable)163 bool PruneNoOps(TCompiler *compiler, TIntermBlock *root, TSymbolTable *symbolTable)
164 {
165     return PruneNoOpsTraverser::apply(compiler, root, symbolTable);
166 }
167 
168 }  // namespace sh
169