Coverage Report

Created: 2025-12-03 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
        &current_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
}