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