/src/shaderc/third_party/glslang/glslang/MachineIndependent/propagateNoContraction.cpp
Line | Count | Source |
1 | | // |
2 | | // Copyright (C) 2015-2016 Google, Inc. |
3 | | // |
4 | | // All rights reserved. |
5 | | // |
6 | | // Redistribution and use in source and binary forms, with or without |
7 | | // modification, are permitted provided that the following conditions |
8 | | // are met: |
9 | | // |
10 | | // Redistributions of source code must retain the above copyright |
11 | | // notice, this list of conditions and the following disclaimer. |
12 | | // |
13 | | // Redistributions in binary form must reproduce the above |
14 | | // copyright notice, this list of conditions and the following |
15 | | // disclaimer in the documentation and/or other materials provided |
16 | | // with the distribution. |
17 | | // |
18 | | // Neither the name of Google Inc. nor the names of its |
19 | | // contributors may be used to endorse or promote products derived |
20 | | // from this software without specific prior written permission. |
21 | | // |
22 | | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
23 | | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
24 | | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
25 | | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
26 | | // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
27 | | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
28 | | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
29 | | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
30 | | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
31 | | // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
32 | | // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
33 | | // POSSIBILITY OF SUCH DAMAGE. |
34 | | |
35 | | // |
36 | | // Visit the nodes in the glslang intermediate tree representation to |
37 | | // propagate the 'noContraction' qualifier. |
38 | | // |
39 | | |
40 | | #include "propagateNoContraction.h" |
41 | | |
42 | | #include <cstdlib> |
43 | | #include <string> |
44 | | #include <tuple> |
45 | | #include <unordered_map> |
46 | | #include <unordered_set> |
47 | | |
48 | | #include "localintermediate.h" |
49 | | namespace { |
50 | | |
51 | | // Use a string to hold the access chain information, as in most cases the |
52 | | // access chain is short and may contain only one element, which is the symbol |
53 | | // ID. |
54 | | // Example: struct {float a; float b;} s; |
55 | | // Object s.a will be represented with: <symbol ID of s>/0 |
56 | | // Object s.b will be represented with: <symbol ID of s>/1 |
57 | | // Object s will be represented with: <symbol ID of s> |
58 | | // For members of vector, matrix and arrays, they will be represented with the |
59 | | // same symbol ID of their container symbol objects. This is because their |
60 | | // preciseness is always the same as their container symbol objects. |
61 | | typedef std::string ObjectAccessChain; |
62 | | |
63 | | // The delimiter used in the ObjectAccessChain string to separate symbol ID and |
64 | | // different level of struct indices. |
65 | | const char ObjectAccesschainDelimiter = '/'; |
66 | | |
67 | | // Mapping from Symbol IDs of symbol nodes, to their defining operation |
68 | | // nodes. |
69 | | typedef std::unordered_multimap<ObjectAccessChain, glslang::TIntermOperator*> NodeMapping; |
70 | | // Mapping from object nodes to their access chain info string. |
71 | | typedef std::unordered_map<glslang::TIntermTyped*, ObjectAccessChain> AccessChainMapping; |
72 | | |
73 | | // Set of object IDs. |
74 | | typedef std::unordered_set<ObjectAccessChain> ObjectAccesschainSet; |
75 | | // Set of return branch nodes. |
76 | | typedef std::unordered_set<glslang::TIntermBranch*> ReturnBranchNodeSet; |
77 | | |
78 | | // A helper function to tell whether a node is 'noContraction'. Returns true if |
79 | | // the node has 'noContraction' qualifier, otherwise false. |
80 | | bool isPreciseObjectNode(glslang::TIntermTyped* node) |
81 | 42.2k | { |
82 | 42.2k | return node->getType().getQualifier().isNoContraction(); |
83 | 42.2k | } |
84 | | |
85 | | // Returns true if the opcode is a dereferencing one. |
86 | | bool isDereferenceOperation(glslang::TOperator op) |
87 | 45.2k | { |
88 | 45.2k | switch (op) { |
89 | 7.18k | case glslang::EOpIndexDirect: |
90 | 18.9k | case glslang::EOpIndexDirectStruct: |
91 | 20.9k | case glslang::EOpIndexIndirect: |
92 | 27.7k | case glslang::EOpVectorSwizzle: |
93 | 27.7k | case glslang::EOpMatrixSwizzle: |
94 | 27.7k | return true; |
95 | 17.5k | default: |
96 | 17.5k | return false; |
97 | 45.2k | } |
98 | 45.2k | } |
99 | | |
100 | | // Returns true if the opcode leads to an assignment operation. |
101 | | bool isAssignOperation(glslang::TOperator op) |
102 | 104k | { |
103 | 104k | switch (op) { |
104 | 33.2k | case glslang::EOpAssign: |
105 | 36.9k | case glslang::EOpAddAssign: |
106 | 37.2k | case glslang::EOpSubAssign: |
107 | 37.7k | case glslang::EOpMulAssign: |
108 | 37.7k | case glslang::EOpVectorTimesMatrixAssign: |
109 | 38.0k | case glslang::EOpVectorTimesScalarAssign: |
110 | 38.0k | case glslang::EOpMatrixTimesScalarAssign: |
111 | 38.0k | case glslang::EOpMatrixTimesMatrixAssign: |
112 | 38.1k | case glslang::EOpDivAssign: |
113 | 38.4k | case glslang::EOpModAssign: |
114 | 38.7k | case glslang::EOpAndAssign: |
115 | 39.0k | case glslang::EOpLeftShiftAssign: |
116 | 39.1k | case glslang::EOpRightShiftAssign: |
117 | 39.2k | case glslang::EOpInclusiveOrAssign: |
118 | 39.3k | case glslang::EOpExclusiveOrAssign: |
119 | | |
120 | 40.4k | case glslang::EOpPostIncrement: |
121 | 40.6k | case glslang::EOpPostDecrement: |
122 | 41.9k | case glslang::EOpPreIncrement: |
123 | 42.2k | case glslang::EOpPreDecrement: |
124 | 42.2k | return true; |
125 | 62.6k | default: |
126 | 62.6k | return false; |
127 | 104k | } |
128 | 104k | } |
129 | | |
130 | | // A helper function to get the unsigned int from a given constant union node. |
131 | | // Note the node should only hold a uint scalar. |
132 | | unsigned getStructIndexFromConstantUnion(glslang::TIntermTyped* node) |
133 | 11.7k | { |
134 | 11.7k | assert(node->getAsConstantUnion() && node->getAsConstantUnion()->isScalar()); |
135 | 11.7k | unsigned struct_dereference_index = node->getAsConstantUnion()->getConstArray()[0].getUConst(); |
136 | 11.7k | return struct_dereference_index; |
137 | 11.7k | } |
138 | | |
139 | | // A helper function to generate symbol_label. |
140 | | ObjectAccessChain generateSymbolLabel(glslang::TIntermSymbol* node) |
141 | 129k | { |
142 | 129k | ObjectAccessChain symbol_id = |
143 | 129k | std::to_string(node->getId()) + "(" + node->getName().c_str() + ")"; |
144 | 129k | return symbol_id; |
145 | 129k | } |
146 | | |
147 | | // Returns true if the operation is an arithmetic operation and valid for |
148 | | // the 'NoContraction' decoration. |
149 | | bool isArithmeticOperation(glslang::TOperator op) |
150 | 0 | { |
151 | 0 | switch (op) { |
152 | 0 | case glslang::EOpAddAssign: |
153 | 0 | case glslang::EOpSubAssign: |
154 | 0 | case glslang::EOpMulAssign: |
155 | 0 | case glslang::EOpVectorTimesMatrixAssign: |
156 | 0 | case glslang::EOpVectorTimesScalarAssign: |
157 | 0 | case glslang::EOpMatrixTimesScalarAssign: |
158 | 0 | case glslang::EOpMatrixTimesMatrixAssign: |
159 | 0 | case glslang::EOpDivAssign: |
160 | 0 | case glslang::EOpModAssign: |
161 | |
|
162 | 0 | case glslang::EOpNegative: |
163 | |
|
164 | 0 | case glslang::EOpAdd: |
165 | 0 | case glslang::EOpSub: |
166 | 0 | case glslang::EOpMul: |
167 | 0 | case glslang::EOpDiv: |
168 | 0 | case glslang::EOpMod: |
169 | |
|
170 | 0 | case glslang::EOpVectorTimesScalar: |
171 | 0 | case glslang::EOpVectorTimesMatrix: |
172 | 0 | case glslang::EOpMatrixTimesVector: |
173 | 0 | case glslang::EOpMatrixTimesScalar: |
174 | 0 | case glslang::EOpMatrixTimesMatrix: |
175 | |
|
176 | 0 | case glslang::EOpDot: |
177 | 0 | case glslang::EOpDotPackedEXT: |
178 | 0 | case glslang::EOpDotAccSatEXT: |
179 | 0 | case glslang::EOpDotPackedAccSatEXT: |
180 | |
|
181 | 0 | case glslang::EOpPostIncrement: |
182 | 0 | case glslang::EOpPostDecrement: |
183 | 0 | case glslang::EOpPreIncrement: |
184 | 0 | case glslang::EOpPreDecrement: |
185 | 0 | return true; |
186 | 0 | default: |
187 | 0 | return false; |
188 | 0 | } |
189 | 0 | } |
190 | | |
191 | | // A helper class to help manage the populating_initial_no_contraction_ flag. |
192 | | template <typename T> class StateSettingGuard { |
193 | | public: |
194 | | StateSettingGuard(T* state_ptr, T new_state_value) |
195 | 0 | : state_ptr_(state_ptr), previous_state_(*state_ptr) |
196 | 0 | { |
197 | 0 | *state_ptr = new_state_value; |
198 | 0 | } |
199 | 65.3k | StateSettingGuard(T* state_ptr) : state_ptr_(state_ptr), previous_state_(*state_ptr) {} |
200 | 5.14k | void setState(T new_state_value) { *state_ptr_ = new_state_value; } |
201 | 65.3k | ~StateSettingGuard() { *state_ptr_ = previous_state_; }propagateNoContraction.cpp:(anonymous namespace)::StateSettingGuard<glslang::TIntermAggregate*>::~StateSettingGuard() Line | Count | Source | 201 | 65.3k | ~StateSettingGuard() { *state_ptr_ = previous_state_; } |
Unexecuted instantiation: propagateNoContraction.cpp:(anonymous namespace)::StateSettingGuard<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >::~StateSettingGuard() |
202 | | |
203 | | private: |
204 | | T* state_ptr_; |
205 | | T previous_state_; |
206 | | }; |
207 | | |
208 | | // A helper function to get the front element from a given ObjectAccessChain |
209 | | ObjectAccessChain getFrontElement(const ObjectAccessChain& chain) |
210 | 42.2k | { |
211 | 42.2k | size_t pos_delimiter = chain.find(ObjectAccesschainDelimiter); |
212 | 42.2k | return pos_delimiter == std::string::npos ? chain : chain.substr(0, pos_delimiter); |
213 | 42.2k | } |
214 | | |
215 | | // A helper function to get the access chain starting from the second element. |
216 | | ObjectAccessChain subAccessChainFromSecondElement(const ObjectAccessChain& chain) |
217 | 0 | { |
218 | 0 | size_t pos_delimiter = chain.find(ObjectAccesschainDelimiter); |
219 | 0 | return pos_delimiter == std::string::npos ? "" : chain.substr(pos_delimiter + 1); |
220 | 0 | } |
221 | | |
222 | | // A helper function to get the access chain after removing a given prefix. |
223 | | ObjectAccessChain getSubAccessChainAfterPrefix(const ObjectAccessChain& chain, |
224 | | const ObjectAccessChain& prefix) |
225 | 0 | { |
226 | 0 | size_t pos = chain.find(prefix); |
227 | 0 | if (pos != 0) |
228 | 0 | return chain; |
229 | 0 | return chain.substr(prefix.length() + sizeof(ObjectAccesschainDelimiter)); |
230 | 0 | } |
231 | | |
232 | | // |
233 | | // A traverser which traverses the whole AST and populates: |
234 | | // 1) A mapping from symbol nodes' IDs to their defining operation nodes. |
235 | | // 2) A set of access chains of the initial precise object nodes. |
236 | | // |
237 | | class TSymbolDefinitionCollectingTraverser : public glslang::TIntermTraverser { |
238 | | public: |
239 | | TSymbolDefinitionCollectingTraverser(NodeMapping* symbol_definition_mapping, |
240 | | AccessChainMapping* accesschain_mapping, |
241 | | ObjectAccesschainSet* precise_objects, |
242 | | ReturnBranchNodeSet* precise_return_nodes); |
243 | | |
244 | | bool visitUnary(glslang::TVisit, glslang::TIntermUnary*) override; |
245 | | bool visitBinary(glslang::TVisit, glslang::TIntermBinary*) override; |
246 | | void visitSymbol(glslang::TIntermSymbol*) override; |
247 | | bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate*) override; |
248 | | bool visitBranch(glslang::TVisit, glslang::TIntermBranch*) override; |
249 | | |
250 | | protected: |
251 | | TSymbolDefinitionCollectingTraverser& operator=(const TSymbolDefinitionCollectingTraverser&); |
252 | | |
253 | | // The mapping from symbol node IDs to their defining nodes. This should be |
254 | | // populated along traversing the AST. |
255 | | NodeMapping& symbol_definition_mapping_; |
256 | | // The set of symbol node IDs for precise symbol nodes, the ones marked as |
257 | | // 'noContraction'. |
258 | | ObjectAccesschainSet& precise_objects_; |
259 | | // The set of precise return nodes. |
260 | | ReturnBranchNodeSet& precise_return_nodes_; |
261 | | // A temporary cache of the symbol node whose defining node is to be found |
262 | | // currently along traversing the AST. |
263 | | ObjectAccessChain current_object_; |
264 | | // A map from object node to its access chain. This traverser stores |
265 | | // the built access chains into this map for each object node it has |
266 | | // visited. |
267 | | AccessChainMapping& accesschain_mapping_; |
268 | | // The pointer to the Function Definition node, so we can get the |
269 | | // preciseness of the return expression from it when we traverse the |
270 | | // return branch node. |
271 | | glslang::TIntermAggregate* current_function_definition_node_; |
272 | | }; |
273 | | |
274 | | TSymbolDefinitionCollectingTraverser::TSymbolDefinitionCollectingTraverser( |
275 | | NodeMapping* symbol_definition_mapping, AccessChainMapping* accesschain_mapping, |
276 | | ObjectAccesschainSet* precise_objects, |
277 | | std::unordered_set<glslang::TIntermBranch*>* precise_return_nodes) |
278 | 3.13k | : TIntermTraverser(true, false, false), symbol_definition_mapping_(*symbol_definition_mapping), |
279 | 3.13k | precise_objects_(*precise_objects), precise_return_nodes_(*precise_return_nodes), |
280 | 3.13k | current_object_(), accesschain_mapping_(*accesschain_mapping), |
281 | 3.13k | current_function_definition_node_(nullptr) {} |
282 | | |
283 | | // Visits a symbol node, set the current_object_ to the |
284 | | // current node symbol ID, and record a mapping from this node to the current |
285 | | // current_object_, which is the just obtained symbol |
286 | | // ID. |
287 | | void TSymbolDefinitionCollectingTraverser::visitSymbol(glslang::TIntermSymbol* node) |
288 | 129k | { |
289 | 129k | current_object_ = generateSymbolLabel(node); |
290 | 129k | accesschain_mapping_[node] = current_object_; |
291 | 129k | } |
292 | | |
293 | | // Visits an aggregate node, traverses all of its children. |
294 | | bool TSymbolDefinitionCollectingTraverser::visitAggregate(glslang::TVisit, |
295 | | glslang::TIntermAggregate* node) |
296 | 65.3k | { |
297 | | // This aggregate node might be a function definition node, in which case we need to |
298 | | // cache this node, so we can get the preciseness information of the return value |
299 | | // of this function later. |
300 | 65.3k | StateSettingGuard<glslang::TIntermAggregate*> current_function_definition_node_setting_guard( |
301 | 65.3k | ¤t_function_definition_node_); |
302 | 65.3k | if (node->getOp() == glslang::EOpFunction) { |
303 | | // This is function definition node, we need to cache this node so that we can |
304 | | // get the preciseness of the return value later. |
305 | 5.14k | current_function_definition_node_setting_guard.setState(node); |
306 | 5.14k | } |
307 | | // Traverse the items in the sequence. |
308 | 65.3k | glslang::TIntermSequence& seq = node->getSequence(); |
309 | 226k | for (int i = 0; i < (int)seq.size(); ++i) { |
310 | 160k | current_object_.clear(); |
311 | 160k | seq[i]->traverse(this); |
312 | 160k | } |
313 | 65.3k | return false; |
314 | 65.3k | } |
315 | | |
316 | | bool TSymbolDefinitionCollectingTraverser::visitBranch(glslang::TVisit, |
317 | | glslang::TIntermBranch* node) |
318 | 5.28k | { |
319 | 5.28k | if (node->getFlowOp() == glslang::EOpReturn && node->getExpression() && |
320 | 1.43k | current_function_definition_node_ && |
321 | 1.43k | current_function_definition_node_->getType().getQualifier().noContraction) { |
322 | | // This node is a return node with an expression, and its function has a |
323 | | // precise return value. We need to find the involved objects in its |
324 | | // expression and add them to the set of initial precise objects. |
325 | 0 | precise_return_nodes_.insert(node); |
326 | 0 | node->getExpression()->traverse(this); |
327 | 0 | } |
328 | 5.28k | return false; |
329 | 5.28k | } |
330 | | |
331 | | // Visits a unary node. This might be an implicit assignment like i++, i--. etc. |
332 | | bool TSymbolDefinitionCollectingTraverser::visitUnary(glslang::TVisit /* visit */, |
333 | | glslang::TIntermUnary* node) |
334 | 20.3k | { |
335 | 20.3k | current_object_.clear(); |
336 | 20.3k | node->getOperand()->traverse(this); |
337 | 20.3k | if (isAssignOperation(node->getOp())) { |
338 | | // We should always be able to get an access chain of the operand node. |
339 | 2.90k | assert(!current_object_.empty()); |
340 | | |
341 | | // If the operand node object is 'precise', we collect its access chain |
342 | | // for the initial set of 'precise' objects. |
343 | 2.90k | if (isPreciseObjectNode(node->getOperand())) { |
344 | | // The operand node is an 'precise' object node, add its |
345 | | // access chain to the set of 'precise' objects. This is to collect |
346 | | // the initial set of 'precise' objects. |
347 | 0 | precise_objects_.insert(current_object_); |
348 | 0 | } |
349 | | // Gets the symbol ID from the object's access chain. |
350 | 2.90k | ObjectAccessChain id_symbol = getFrontElement(current_object_); |
351 | | // Add a mapping from the symbol ID to this assignment operation node. |
352 | 2.90k | symbol_definition_mapping_.insert(std::make_pair(id_symbol, node)); |
353 | 2.90k | } |
354 | | // A unary node is not a dereference node, so we clear the access chain which |
355 | | // is under construction. |
356 | 20.3k | current_object_.clear(); |
357 | 20.3k | return false; |
358 | 20.3k | } |
359 | | |
360 | | // Visits a binary node and updates the mapping from symbol IDs to the definition |
361 | | // nodes. Also collects the access chains for the initial precise objects. |
362 | | bool TSymbolDefinitionCollectingTraverser::visitBinary(glslang::TVisit /* visit */, |
363 | | glslang::TIntermBinary* node) |
364 | 84.6k | { |
365 | | // Traverses the left node to build the access chain info for the object. |
366 | 84.6k | current_object_.clear(); |
367 | 84.6k | node->getLeft()->traverse(this); |
368 | | |
369 | 84.6k | if (isAssignOperation(node->getOp())) { |
370 | | // We should always be able to get an access chain for the left node. |
371 | 39.3k | assert(!current_object_.empty()); |
372 | | |
373 | | // If the left node object is 'precise', it is an initial precise object |
374 | | // specified in the shader source. Adds it to the initial work list to |
375 | | // process later. |
376 | 39.3k | if (isPreciseObjectNode(node->getLeft())) { |
377 | | // The left node is an 'precise' object node, add its access chain to |
378 | | // the set of 'precise' objects. This is to collect the initial set |
379 | | // of 'precise' objects. |
380 | 0 | precise_objects_.insert(current_object_); |
381 | 0 | } |
382 | | // Gets the symbol ID from the object access chain, which should be the |
383 | | // first element recorded in the access chain. |
384 | 39.3k | ObjectAccessChain id_symbol = getFrontElement(current_object_); |
385 | | // Adds a mapping from the symbol ID to this assignment operation node. |
386 | 39.3k | symbol_definition_mapping_.insert(std::make_pair(id_symbol, node)); |
387 | | |
388 | | // Traverses the right node, there may be other 'assignment' |
389 | | // operations in the right. |
390 | 39.3k | current_object_.clear(); |
391 | 39.3k | node->getRight()->traverse(this); |
392 | | |
393 | 45.2k | } else if (isDereferenceOperation(node->getOp())) { |
394 | | // The left node (parent node) is a struct type object. We need to |
395 | | // record the access chain information of the current node into its |
396 | | // object id. |
397 | 27.7k | if (node->getOp() == glslang::EOpIndexDirectStruct) { |
398 | 11.7k | unsigned struct_dereference_index = getStructIndexFromConstantUnion(node->getRight()); |
399 | 11.7k | current_object_.push_back(ObjectAccesschainDelimiter); |
400 | 11.7k | current_object_.append(std::to_string(struct_dereference_index)); |
401 | 11.7k | } |
402 | 27.7k | accesschain_mapping_[node] = current_object_; |
403 | | |
404 | | // For a dereference node, there is no need to traverse the right child |
405 | | // node as the right node should always be an integer type object. |
406 | | |
407 | 27.7k | } else { |
408 | | // For other binary nodes, still traverse the right node. |
409 | 17.5k | current_object_.clear(); |
410 | 17.5k | node->getRight()->traverse(this); |
411 | 17.5k | } |
412 | 84.6k | return false; |
413 | 84.6k | } |
414 | | |
415 | | // Traverses the AST and returns a tuple of four members: |
416 | | // 1) a mapping from symbol IDs to the definition nodes (aka. assignment nodes) of these symbols. |
417 | | // 2) a mapping from object nodes in the AST to the access chains of these objects. |
418 | | // 3) a set of access chains of precise objects. |
419 | | // 4) a set of return nodes with precise expressions. |
420 | | std::tuple<NodeMapping, AccessChainMapping, ObjectAccesschainSet, ReturnBranchNodeSet> |
421 | | getSymbolToDefinitionMappingAndPreciseSymbolIDs(const glslang::TIntermediate& intermediate) |
422 | 3.13k | { |
423 | 3.13k | auto result_tuple = std::make_tuple(NodeMapping(), AccessChainMapping(), ObjectAccesschainSet(), |
424 | 3.13k | ReturnBranchNodeSet()); |
425 | | |
426 | 3.13k | TIntermNode* root = intermediate.getTreeRoot(); |
427 | 3.13k | if (root == nullptr) |
428 | 0 | return result_tuple; |
429 | | |
430 | 3.13k | NodeMapping& symbol_definition_mapping = std::get<0>(result_tuple); |
431 | 3.13k | AccessChainMapping& accesschain_mapping = std::get<1>(result_tuple); |
432 | 3.13k | ObjectAccesschainSet& precise_objects = std::get<2>(result_tuple); |
433 | 3.13k | ReturnBranchNodeSet& precise_return_nodes = std::get<3>(result_tuple); |
434 | | |
435 | | // Traverses the AST and populate the results. |
436 | 3.13k | TSymbolDefinitionCollectingTraverser collector(&symbol_definition_mapping, &accesschain_mapping, |
437 | 3.13k | &precise_objects, &precise_return_nodes); |
438 | 3.13k | root->traverse(&collector); |
439 | | |
440 | 3.13k | return result_tuple; |
441 | 3.13k | } |
442 | | |
443 | | // |
444 | | // A traverser that determine whether the left node (or operand node for unary |
445 | | // node) of an assignment node is 'precise', containing 'precise' or not, |
446 | | // according to the access chain a given precise object which share the same |
447 | | // symbol as the left node. |
448 | | // |
449 | | // Post-orderly traverses the left node subtree of an binary assignment node and: |
450 | | // |
451 | | // 1) Propagates the 'precise' from the left object nodes to this object node. |
452 | | // |
453 | | // 2) Builds object access chain along the traversal, and also compares with |
454 | | // the access chain of the given 'precise' object along with the traversal to |
455 | | // tell if the node to be defined is 'precise' or not. |
456 | | // |
457 | | class TNoContractionAssigneeCheckingTraverser : public glslang::TIntermTraverser { |
458 | | |
459 | | enum DecisionStatus { |
460 | | // The object node to be assigned to may contain 'precise' objects and also not 'precise' objects. |
461 | | Mixed = 0, |
462 | | // The object node to be assigned to is either a 'precise' object or a struct objects whose members are all 'precise'. |
463 | | Precise = 1, |
464 | | // The object node to be assigned to is not a 'precise' object. |
465 | | NotPreicse = 2, |
466 | | }; |
467 | | |
468 | | public: |
469 | | TNoContractionAssigneeCheckingTraverser(const AccessChainMapping& accesschain_mapping) |
470 | 3.13k | : TIntermTraverser(true, false, false), accesschain_mapping_(accesschain_mapping), |
471 | 3.13k | precise_object_(nullptr) {} |
472 | | |
473 | | // Checks the preciseness of a given assignment node with a precise object |
474 | | // represented as access chain. The precise object shares the same symbol |
475 | | // with the assignee of the given assignment node. Return a tuple of two: |
476 | | // |
477 | | // 1) The preciseness of the assignee node of this assignment node. True |
478 | | // if the assignee contains 'precise' objects or is 'precise', false if |
479 | | // the assignee is not 'precise' according to the access chain of the given |
480 | | // precise object. |
481 | | // |
482 | | // 2) The incremental access chain from the assignee node to its nested |
483 | | // 'precise' object, according to the access chain of the given precise |
484 | | // object. This incremental access chain can be empty, which means the |
485 | | // assignee is 'precise'. Otherwise it shows the path to the nested |
486 | | // precise object. |
487 | | std::tuple<bool, ObjectAccessChain> |
488 | | getPrecisenessAndRemainedAccessChain(glslang::TIntermOperator* node, |
489 | | const ObjectAccessChain& precise_object) |
490 | 0 | { |
491 | 0 | assert(isAssignOperation(node->getOp())); |
492 | 0 | precise_object_ = &precise_object; |
493 | 0 | ObjectAccessChain assignee_object; |
494 | 0 | if (glslang::TIntermBinary* BN = node->getAsBinaryNode()) { |
495 | | // This is a binary assignment node, we need to check the |
496 | | // preciseness of the left node. |
497 | 0 | assert(accesschain_mapping_.count(BN->getLeft())); |
498 | | // The left node (assignee node) is an object node, traverse the |
499 | | // node to let the 'precise' of nesting objects being transfered to |
500 | | // nested objects. |
501 | 0 | BN->getLeft()->traverse(this); |
502 | | // After traversing the left node, if the left node is 'precise', |
503 | | // we can conclude this assignment should propagate 'precise'. |
504 | 0 | if (isPreciseObjectNode(BN->getLeft())) { |
505 | 0 | return make_tuple(true, ObjectAccessChain()); |
506 | 0 | } |
507 | | // If the preciseness of the left node (assignee node) can not |
508 | | // be determined by now, we need to compare the access chain string |
509 | | // of the assignee object with the given precise object. |
510 | 0 | assignee_object = accesschain_mapping_.at(BN->getLeft()); |
511 | |
|
512 | 0 | } else if (glslang::TIntermUnary* UN = node->getAsUnaryNode()) { |
513 | | // This is a unary assignment node, we need to check the |
514 | | // preciseness of the operand node. For unary assignment node, the |
515 | | // operand node should always be an object node. |
516 | 0 | assert(accesschain_mapping_.count(UN->getOperand())); |
517 | | // Traverse the operand node to let the 'precise' being propagated |
518 | | // from lower nodes to upper nodes. |
519 | 0 | UN->getOperand()->traverse(this); |
520 | | // After traversing the operand node, if the operand node is |
521 | | // 'precise', this assignment should propagate 'precise'. |
522 | 0 | if (isPreciseObjectNode(UN->getOperand())) { |
523 | 0 | return make_tuple(true, ObjectAccessChain()); |
524 | 0 | } |
525 | | // If the preciseness of the operand node (assignee node) can not |
526 | | // be determined by now, we need to compare the access chain string |
527 | | // of the assignee object with the given precise object. |
528 | 0 | assignee_object = accesschain_mapping_.at(UN->getOperand()); |
529 | 0 | } else { |
530 | | // Not a binary or unary node, should not happen. |
531 | 0 | assert(false); |
532 | 0 | } |
533 | | |
534 | | // Compare the access chain string of the assignee node with the given |
535 | | // precise object to determine if this assignment should propagate |
536 | | // 'precise'. |
537 | 0 | if (assignee_object.find(precise_object) == 0) { |
538 | | // The access chain string of the given precise object is a prefix |
539 | | // of assignee's access chain string. The assignee should be |
540 | | // 'precise'. |
541 | 0 | return make_tuple(true, ObjectAccessChain()); |
542 | 0 | } else if (precise_object.find(assignee_object) == 0) { |
543 | | // The assignee's access chain string is a prefix of the given |
544 | | // precise object, the assignee object contains 'precise' object, |
545 | | // and we need to pass the remained access chain to the object nodes |
546 | | // in the right. |
547 | 0 | return make_tuple(true, getSubAccessChainAfterPrefix(precise_object, assignee_object)); |
548 | 0 | } else { |
549 | | // The access chain strings do not match, the assignee object can |
550 | | // not be labeled as 'precise' according to the given precise |
551 | | // object. |
552 | 0 | return make_tuple(false, ObjectAccessChain()); |
553 | 0 | } |
554 | 0 | } |
555 | | |
556 | | protected: |
557 | | TNoContractionAssigneeCheckingTraverser& operator=(const TNoContractionAssigneeCheckingTraverser&); |
558 | | |
559 | | bool visitBinary(glslang::TVisit, glslang::TIntermBinary* node) override; |
560 | | void visitSymbol(glslang::TIntermSymbol* node) override; |
561 | | |
562 | | // A map from object nodes to their access chain string (used as object ID). |
563 | | const AccessChainMapping& accesschain_mapping_; |
564 | | // A given precise object, represented in it access chain string. This |
565 | | // precise object is used to be compared with the assignee node to tell if |
566 | | // the assignee node is 'precise', contains 'precise' object or not |
567 | | // 'precise'. |
568 | | const ObjectAccessChain* precise_object_; |
569 | | }; |
570 | | |
571 | | // Visits a binary node. If the node is an object node, it must be a dereference |
572 | | // node. In such cases, if the left node is 'precise', this node should also be |
573 | | // 'precise'. |
574 | | bool TNoContractionAssigneeCheckingTraverser::visitBinary(glslang::TVisit, |
575 | | glslang::TIntermBinary* node) |
576 | 0 | { |
577 | | // Traverses the left so that we transfer the 'precise' from nesting object |
578 | | // to its nested object. |
579 | 0 | node->getLeft()->traverse(this); |
580 | | // If this binary node is an object node, we should have it in the |
581 | | // accesschain_mapping_. |
582 | 0 | if (accesschain_mapping_.count(node)) { |
583 | | // A binary object node must be a dereference node. |
584 | 0 | assert(isDereferenceOperation(node->getOp())); |
585 | | // If the left node is 'precise', this node should also be precise, |
586 | | // otherwise, compare with the given precise_object_. If the |
587 | | // access chain of this node matches with the given precise_object_, |
588 | | // this node should be marked as 'precise'. |
589 | 0 | if (isPreciseObjectNode(node->getLeft())) { |
590 | 0 | node->getWritableType().getQualifier().noContraction = true; |
591 | 0 | } else if (accesschain_mapping_.at(node) == *precise_object_) { |
592 | 0 | node->getWritableType().getQualifier().noContraction = true; |
593 | 0 | } |
594 | 0 | } |
595 | 0 | return false; |
596 | 0 | } |
597 | | |
598 | | // Visits a symbol node, if the symbol node ID (its access chain string) matches |
599 | | // with the given precise object, this node should be 'precise'. |
600 | | void TNoContractionAssigneeCheckingTraverser::visitSymbol(glslang::TIntermSymbol* node) |
601 | 0 | { |
602 | | // A symbol node should always be an object node, and should have been added |
603 | | // to the map from object nodes to their access chain strings. |
604 | 0 | assert(accesschain_mapping_.count(node)); |
605 | 0 | if (accesschain_mapping_.at(node) == *precise_object_) { |
606 | 0 | node->getWritableType().getQualifier().noContraction = true; |
607 | 0 | } |
608 | 0 | } |
609 | | |
610 | | // |
611 | | // A traverser that only traverses the right side of binary assignment nodes |
612 | | // and the operand node of unary assignment nodes. |
613 | | // |
614 | | // 1) Marks arithmetic operations as 'NoContraction'. |
615 | | // |
616 | | // 2) Find the object which should be marked as 'precise' in the right and |
617 | | // update the 'precise' object work list. |
618 | | // |
619 | | class TNoContractionPropagator : public glslang::TIntermTraverser { |
620 | | public: |
621 | | TNoContractionPropagator(ObjectAccesschainSet* precise_objects, |
622 | | const AccessChainMapping& accesschain_mapping) |
623 | 3.13k | : TIntermTraverser(true, false, false), |
624 | 3.13k | precise_objects_(*precise_objects), added_precise_object_ids_(), |
625 | 3.13k | remained_accesschain_(), accesschain_mapping_(accesschain_mapping) {} |
626 | | |
627 | | // Propagates 'precise' in the right nodes of a given assignment node with |
628 | | // access chain record from the assignee node to a 'precise' object it |
629 | | // contains. |
630 | | void |
631 | | propagateNoContractionInOneExpression(glslang::TIntermTyped* defining_node, |
632 | | const ObjectAccessChain& assignee_remained_accesschain) |
633 | 0 | { |
634 | 0 | remained_accesschain_ = assignee_remained_accesschain; |
635 | 0 | if (glslang::TIntermBinary* BN = defining_node->getAsBinaryNode()) { |
636 | 0 | assert(isAssignOperation(BN->getOp())); |
637 | 0 | BN->getRight()->traverse(this); |
638 | 0 | if (isArithmeticOperation(BN->getOp())) { |
639 | 0 | BN->getWritableType().getQualifier().noContraction = true; |
640 | 0 | } |
641 | 0 | } else if (glslang::TIntermUnary* UN = defining_node->getAsUnaryNode()) { |
642 | 0 | assert(isAssignOperation(UN->getOp())); |
643 | 0 | UN->getOperand()->traverse(this); |
644 | 0 | if (isArithmeticOperation(UN->getOp())) { |
645 | 0 | UN->getWritableType().getQualifier().noContraction = true; |
646 | 0 | } |
647 | 0 | } |
648 | 0 | } |
649 | | |
650 | | // Propagates 'precise' in a given precise return node. |
651 | | void propagateNoContractionInReturnNode(glslang::TIntermBranch* return_node) |
652 | 0 | { |
653 | 0 | remained_accesschain_ = ""; |
654 | 0 | assert(return_node->getFlowOp() == glslang::EOpReturn && return_node->getExpression()); |
655 | 0 | return_node->getExpression()->traverse(this); |
656 | 0 | } |
657 | | |
658 | | protected: |
659 | | TNoContractionPropagator& operator=(const TNoContractionPropagator&); |
660 | | |
661 | | // Visits an aggregate node. The node can be a initializer list, in which |
662 | | // case we need to find the 'precise' or 'precise' containing object node |
663 | | // with the access chain record. In other cases, just need to traverse all |
664 | | // the children nodes. |
665 | | bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate* node) override |
666 | 0 | { |
667 | 0 | if (!remained_accesschain_.empty() && node->getOp() == glslang::EOpConstructStruct) { |
668 | | // This is a struct initializer node, and the remained |
669 | | // access chain is not empty, we need to refer to the |
670 | | // assignee_remained_access_chain_ to find the nested |
671 | | // 'precise' object. And we don't need to visit other nodes in this |
672 | | // aggregate node. |
673 | | |
674 | | // Gets the struct dereference index that leads to 'precise' object. |
675 | 0 | ObjectAccessChain precise_accesschain_index_str = |
676 | 0 | getFrontElement(remained_accesschain_); |
677 | 0 | unsigned precise_accesschain_index = (unsigned)strtoul(precise_accesschain_index_str.c_str(), nullptr, 10); |
678 | | // Gets the node pointed by the access chain index extracted before. |
679 | 0 | glslang::TIntermTyped* potential_precise_node = |
680 | 0 | node->getSequence()[precise_accesschain_index]->getAsTyped(); |
681 | 0 | assert(potential_precise_node); |
682 | | // Pop the front access chain index from the path, and visit the nested node. |
683 | 0 | { |
684 | 0 | ObjectAccessChain next_level_accesschain = |
685 | 0 | subAccessChainFromSecondElement(remained_accesschain_); |
686 | 0 | StateSettingGuard<ObjectAccessChain> setup_remained_accesschain_for_next_level( |
687 | 0 | &remained_accesschain_, next_level_accesschain); |
688 | 0 | potential_precise_node->traverse(this); |
689 | 0 | } |
690 | 0 | return false; |
691 | 0 | } |
692 | 0 | return true; |
693 | 0 | } |
694 | | |
695 | | // Visits a binary node. A binary node can be an object node, e.g. a dereference node. |
696 | | // As only the top object nodes in the right side of an assignment needs to be visited |
697 | | // and added to 'precise' work list, this traverser won't visit the children nodes of |
698 | | // an object node. If the binary node does not represent an object node, it should |
699 | | // go on to traverse its children nodes and if it is an arithmetic operation node, this |
700 | | // operation should be marked as 'noContraction'. |
701 | | bool visitBinary(glslang::TVisit, glslang::TIntermBinary* node) override |
702 | 0 | { |
703 | 0 | if (isDereferenceOperation(node->getOp())) { |
704 | | // This binary node is an object node. Need to update the precise |
705 | | // object set with the access chain of this node + remained |
706 | | // access chain . |
707 | 0 | ObjectAccessChain new_precise_accesschain = accesschain_mapping_.at(node); |
708 | 0 | if (remained_accesschain_.empty()) { |
709 | 0 | node->getWritableType().getQualifier().noContraction = true; |
710 | 0 | } else { |
711 | 0 | new_precise_accesschain += ObjectAccesschainDelimiter + remained_accesschain_; |
712 | 0 | } |
713 | | // Cache the access chain as added precise object, so we won't add the |
714 | | // same object to the work list again. |
715 | 0 | if (!added_precise_object_ids_.count(new_precise_accesschain)) { |
716 | 0 | precise_objects_.insert(new_precise_accesschain); |
717 | 0 | added_precise_object_ids_.insert(new_precise_accesschain); |
718 | 0 | } |
719 | | // Only the upper-most object nodes should be visited, so do not |
720 | | // visit children of this object node. |
721 | 0 | return false; |
722 | 0 | } |
723 | | // If this is an arithmetic operation, marks this node as 'noContraction'. |
724 | 0 | if (isArithmeticOperation(node->getOp()) && node->getBasicType() != glslang::EbtInt) { |
725 | 0 | node->getWritableType().getQualifier().noContraction = true; |
726 | 0 | } |
727 | | // As this node is not an object node, need to traverse the children nodes. |
728 | 0 | return true; |
729 | 0 | } |
730 | | |
731 | | // Visits a unary node. A unary node can not be an object node. If the operation |
732 | | // is an arithmetic operation, need to mark this node as 'noContraction'. |
733 | | bool visitUnary(glslang::TVisit /* visit */, glslang::TIntermUnary* node) override |
734 | 0 | { |
735 | | // If this is an arithmetic operation, marks this with 'noContraction' |
736 | 0 | if (isArithmeticOperation(node->getOp())) { |
737 | 0 | node->getWritableType().getQualifier().noContraction = true; |
738 | 0 | } |
739 | 0 | return true; |
740 | 0 | } |
741 | | |
742 | | // Visits a symbol node. A symbol node is always an object node. So we |
743 | | // should always be able to find its in our collected mapping from object |
744 | | // nodes to access chains. As an object node, a symbol node can be either |
745 | | // 'precise' or containing 'precise' objects according to unused |
746 | | // access chain information we have when we visit this node. |
747 | | void visitSymbol(glslang::TIntermSymbol* node) override |
748 | 0 | { |
749 | | // Symbol nodes are object nodes and should always have an |
750 | | // access chain collected before matches with it. |
751 | 0 | assert(accesschain_mapping_.count(node)); |
752 | 0 | ObjectAccessChain new_precise_accesschain = accesschain_mapping_.at(node); |
753 | | // If the unused access chain is empty, this symbol node should be |
754 | | // marked as 'precise'. Otherwise, the unused access chain should be |
755 | | // appended to the symbol ID to build a new access chain which points to |
756 | | // the nested 'precise' object in this symbol object. |
757 | 0 | if (remained_accesschain_.empty()) { |
758 | 0 | node->getWritableType().getQualifier().noContraction = true; |
759 | 0 | } else { |
760 | 0 | new_precise_accesschain += ObjectAccesschainDelimiter + remained_accesschain_; |
761 | 0 | } |
762 | | // Add the new 'precise' access chain to the work list and make sure we |
763 | | // don't visit it again. |
764 | 0 | if (!added_precise_object_ids_.count(new_precise_accesschain)) { |
765 | 0 | precise_objects_.insert(new_precise_accesschain); |
766 | 0 | added_precise_object_ids_.insert(new_precise_accesschain); |
767 | 0 | } |
768 | 0 | } |
769 | | |
770 | | // A set of precise objects, represented as access chains. |
771 | | ObjectAccesschainSet& precise_objects_; |
772 | | // Visited symbol nodes, should not revisit these nodes. |
773 | | ObjectAccesschainSet added_precise_object_ids_; |
774 | | // The left node of an assignment operation might be an parent of 'precise' objects. |
775 | | // This means the left node might not be an 'precise' object node, but it may contains |
776 | | // 'precise' qualifier which should be propagated to the corresponding child node in |
777 | | // the right. So we need the path from the left node to its nested 'precise' node to |
778 | | // tell us how to find the corresponding 'precise' node in the right. |
779 | | ObjectAccessChain remained_accesschain_; |
780 | | // A map from node pointers to their access chains. |
781 | | const AccessChainMapping& accesschain_mapping_; |
782 | | }; |
783 | | } |
784 | | |
785 | | namespace glslang { |
786 | | |
787 | | void PropagateNoContraction(const glslang::TIntermediate& intermediate) |
788 | 3.13k | { |
789 | | // First, traverses the AST, records symbols with their defining operations |
790 | | // and collects the initial set of precise symbols (symbol nodes that marked |
791 | | // as 'noContraction') and precise return nodes. |
792 | 3.13k | auto mappings_and_precise_objects = |
793 | 3.13k | getSymbolToDefinitionMappingAndPreciseSymbolIDs(intermediate); |
794 | | |
795 | | // The mapping of symbol node IDs to their defining nodes. This enables us |
796 | | // to get the defining node directly from a given symbol ID without |
797 | | // traversing the tree again. |
798 | 3.13k | NodeMapping& symbol_definition_mapping = std::get<0>(mappings_and_precise_objects); |
799 | | |
800 | | // The mapping of object nodes to their access chains recorded. |
801 | 3.13k | AccessChainMapping& accesschain_mapping = std::get<1>(mappings_and_precise_objects); |
802 | | |
803 | | // The initial set of 'precise' objects which are represented as the |
804 | | // access chain toward them. |
805 | 3.13k | ObjectAccesschainSet& precise_object_accesschains = std::get<2>(mappings_and_precise_objects); |
806 | | |
807 | | // The set of 'precise' return nodes. |
808 | 3.13k | ReturnBranchNodeSet& precise_return_nodes = std::get<3>(mappings_and_precise_objects); |
809 | | |
810 | | // Second, uses the initial set of precise objects as a work list, pops an |
811 | | // access chain, extract the symbol ID from it. Then: |
812 | | // 1) Check the assignee object, see if it is 'precise' object node or |
813 | | // contains 'precise' object. Obtain the incremental access chain from the |
814 | | // assignee node to its nested 'precise' node (if any). |
815 | | // 2) If the assignee object node is 'precise' or it contains 'precise' |
816 | | // objects, traverses the right side of the assignment operation |
817 | | // expression to mark arithmetic operations as 'noContration' and update |
818 | | // 'precise' access chain work list with new found object nodes. |
819 | | // Repeat above steps until the work list is empty. |
820 | 3.13k | TNoContractionAssigneeCheckingTraverser checker(accesschain_mapping); |
821 | 3.13k | TNoContractionPropagator propagator(&precise_object_accesschains, accesschain_mapping); |
822 | | |
823 | | // We have two initial precise work lists to handle: |
824 | | // 1) precise return nodes |
825 | | // 2) precise object access chains |
826 | | // We should process the precise return nodes first and the involved |
827 | | // objects in the return expression should be added to the precise object |
828 | | // access chain set. |
829 | 3.13k | while (!precise_return_nodes.empty()) { |
830 | 0 | glslang::TIntermBranch* precise_return_node = *precise_return_nodes.begin(); |
831 | 0 | propagator.propagateNoContractionInReturnNode(precise_return_node); |
832 | 0 | precise_return_nodes.erase(precise_return_node); |
833 | 0 | } |
834 | | |
835 | 3.13k | while (!precise_object_accesschains.empty()) { |
836 | | // Get the access chain of a precise object from the work list. |
837 | 0 | ObjectAccessChain precise_object_accesschain = *precise_object_accesschains.begin(); |
838 | | // Get the symbol id from the access chain. |
839 | 0 | ObjectAccessChain symbol_id = getFrontElement(precise_object_accesschain); |
840 | | // Get all the defining nodes of that symbol ID. |
841 | 0 | std::pair<NodeMapping::iterator, NodeMapping::iterator> range = |
842 | 0 | symbol_definition_mapping.equal_range(symbol_id); |
843 | | // Visits all the assignment nodes of that symbol ID and |
844 | | // 1) Check if the assignee node is 'precise' or contains 'precise' |
845 | | // objects. |
846 | | // 2) Propagate the 'precise' to the top layer object nodes |
847 | | // in the right side of the assignment operation, update the 'precise' |
848 | | // work list with new access chains representing the new 'precise' |
849 | | // objects, and mark arithmetic operations as 'noContraction'. |
850 | 0 | for (NodeMapping::iterator defining_node_iter = range.first; |
851 | 0 | defining_node_iter != range.second; defining_node_iter++) { |
852 | 0 | TIntermOperator* defining_node = defining_node_iter->second; |
853 | | // Check the assignee node. |
854 | 0 | auto checker_result = checker.getPrecisenessAndRemainedAccessChain( |
855 | 0 | defining_node, precise_object_accesschain); |
856 | 0 | bool& contain_precise = std::get<0>(checker_result); |
857 | 0 | ObjectAccessChain& remained_accesschain = std::get<1>(checker_result); |
858 | | // If the assignee node is 'precise' or contains 'precise', propagate the |
859 | | // 'precise' to the right. Otherwise just skip this assignment node. |
860 | 0 | if (contain_precise) { |
861 | 0 | propagator.propagateNoContractionInOneExpression(defining_node, |
862 | 0 | remained_accesschain); |
863 | 0 | } |
864 | 0 | } |
865 | | // Remove the last processed 'precise' object from the work list. |
866 | 0 | precise_object_accesschains.erase(precise_object_accesschain); |
867 | 0 | } |
868 | 3.13k | } |
869 | | } |