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