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