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