summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/glslang/glslang/MachineIndependent/propagateNoContraction.cpp
blob: ae95688ae8bbaf13975a07c888f417c4a85af58a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
//
// Copyright (C) 2015-2016 Google, Inc.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//    Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//
//    Redistributions in binary form must reproduce the above
//    copyright notice, this list of conditions and the following
//    disclaimer in the documentation and/or other materials provided
//    with the distribution.
//
//    Neither the name of Google Inc. nor the names of its
//    contributors may be used to endorse or promote products derived
//    from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

//
// Visit the nodes in the glslang intermediate tree representation to
// propagate the 'noContraction' qualifier.
//

#include "propagateNoContraction.h"

#include <cstdlib>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>

#include "localintermediate.h"
namespace {

// Use a string to hold the access chain information, as in most cases the
// access chain is short and may contain only one element, which is the symbol
// ID.
// Example: struct {float a; float b;} s;
//  Object s.a will be represented with: <symbol ID of s>/0
//  Object s.b will be represented with: <symbol ID of s>/1
//  Object s will be represented with: <symbol ID of s>
// For members of vector, matrix and arrays, they will be represented with the
// same symbol ID of their container symbol objects. This is because their
// preciseness is always the same as their container symbol objects.
typedef std::string ObjectAccessChain;

// The delimiter used in the ObjectAccessChain string to separate symbol ID and
// different level of struct indices.
const char ObjectAccesschainDelimiter = '/';

// Mapping from Symbol IDs of symbol nodes, to their defining operation
// nodes.
typedef std::unordered_multimap<ObjectAccessChain, glslang::TIntermOperator*> NodeMapping;
// Mapping from object nodes to their access chain info string.
typedef std::unordered_map<glslang::TIntermTyped*, ObjectAccessChain> AccessChainMapping;

// Set of object IDs.
typedef std::unordered_set<ObjectAccessChain> ObjectAccesschainSet;
// Set of return branch nodes.
typedef std::unordered_set<glslang::TIntermBranch*> ReturnBranchNodeSet;

// A helper function to tell whether a node is 'noContraction'. Returns true if
// the node has 'noContraction' qualifier, otherwise false.
bool isPreciseObjectNode(glslang::TIntermTyped* node)
{
    return node->getType().getQualifier().noContraction;
}

// Returns true if the opcode is a dereferencing one.
bool isDereferenceOperation(glslang::TOperator op)
{
    switch (op) {
    case glslang::EOpIndexDirect:
    case glslang::EOpIndexDirectStruct:
    case glslang::EOpIndexIndirect:
    case glslang::EOpVectorSwizzle:
    case glslang::EOpMatrixSwizzle:
        return true;
    default:
        return false;
    }
}

// Returns true if the opcode leads to an assignment operation.
bool isAssignOperation(glslang::TOperator op)
{
    switch (op) {
    case glslang::EOpAssign:
    case glslang::EOpAddAssign:
    case glslang::EOpSubAssign:
    case glslang::EOpMulAssign:
    case glslang::EOpVectorTimesMatrixAssign:
    case glslang::EOpVectorTimesScalarAssign:
    case glslang::EOpMatrixTimesScalarAssign:
    case glslang::EOpMatrixTimesMatrixAssign:
    case glslang::EOpDivAssign:
    case glslang::EOpModAssign:
    case glslang::EOpAndAssign:
    case glslang::EOpLeftShiftAssign:
    case glslang::EOpRightShiftAssign:
    case glslang::EOpInclusiveOrAssign:
    case glslang::EOpExclusiveOrAssign:

    case glslang::EOpPostIncrement:
    case glslang::EOpPostDecrement:
    case glslang::EOpPreIncrement:
    case glslang::EOpPreDecrement:
        return true;
    default:
        return false;
    }
}

// A helper function to get the unsigned int from a given constant union node.
// Note the node should only hold a uint scalar.
unsigned getStructIndexFromConstantUnion(glslang::TIntermTyped* node)
{
    assert(node->getAsConstantUnion() && node->getAsConstantUnion()->isScalar());
    unsigned struct_dereference_index = node->getAsConstantUnion()->getConstArray()[0].getUConst();
    return struct_dereference_index;
}

// A helper function to generate symbol_label.
ObjectAccessChain generateSymbolLabel(glslang::TIntermSymbol* node)
{
    ObjectAccessChain symbol_id =
        std::to_string(node->getId()) + "(" + node->getName().c_str() + ")";
    return symbol_id;
}

// Returns true if the operation is an arithmetic operation and valid for
// the 'NoContraction' decoration.
bool isArithmeticOperation(glslang::TOperator op)
{
    switch (op) {
    case glslang::EOpAddAssign:
    case glslang::EOpSubAssign:
    case glslang::EOpMulAssign:
    case glslang::EOpVectorTimesMatrixAssign:
    case glslang::EOpVectorTimesScalarAssign:
    case glslang::EOpMatrixTimesScalarAssign:
    case glslang::EOpMatrixTimesMatrixAssign:
    case glslang::EOpDivAssign:
    case glslang::EOpModAssign:

    case glslang::EOpNegative:

    case glslang::EOpAdd:
    case glslang::EOpSub:
    case glslang::EOpMul:
    case glslang::EOpDiv:
    case glslang::EOpMod:

    case glslang::EOpVectorTimesScalar:
    case glslang::EOpVectorTimesMatrix:
    case glslang::EOpMatrixTimesVector:
    case glslang::EOpMatrixTimesScalar:
    case glslang::EOpMatrixTimesMatrix:

    case glslang::EOpDot:

    case glslang::EOpPostIncrement:
    case glslang::EOpPostDecrement:
    case glslang::EOpPreIncrement:
    case glslang::EOpPreDecrement:
        return true;
    default:
        return false;
    }
}

// A helper class to help manage the populating_initial_no_contraction_ flag.
template <typename T> class StateSettingGuard {
public:
    StateSettingGuard(T* state_ptr, T new_state_value)
        : state_ptr_(state_ptr), previous_state_(*state_ptr)
    {
        *state_ptr = new_state_value;
    }
    StateSettingGuard(T* state_ptr) : state_ptr_(state_ptr), previous_state_(*state_ptr) {}
    void setState(T new_state_value) { *state_ptr_ = new_state_value; }
    ~StateSettingGuard() { *state_ptr_ = previous_state_; }

private:
    T* state_ptr_;
    T previous_state_;
};

// A helper function to get the front element from a given ObjectAccessChain
ObjectAccessChain getFrontElement(const ObjectAccessChain& chain)
{
    size_t pos_delimiter = chain.find(ObjectAccesschainDelimiter);
    return pos_delimiter == std::string::npos ? chain : chain.substr(0, pos_delimiter);
}

// A helper function to get the access chain starting from the second element.
ObjectAccessChain subAccessChainFromSecondElement(const ObjectAccessChain& chain)
{
    size_t pos_delimiter = chain.find(ObjectAccesschainDelimiter);
    return pos_delimiter == std::string::npos ? "" : chain.substr(pos_delimiter + 1);
}

// A helper function to get the access chain after removing a given prefix.
ObjectAccessChain getSubAccessChainAfterPrefix(const ObjectAccessChain& chain,
                                               const ObjectAccessChain& prefix)
{
    size_t pos = chain.find(prefix);
    if (pos != 0)
        return chain;
    return chain.substr(prefix.length() + sizeof(ObjectAccesschainDelimiter));
}

//
// A traverser which traverses the whole AST and populates:
//  1) A mapping from symbol nodes' IDs to their defining operation nodes.
//  2) A set of access chains of the initial precise object nodes.
//
class TSymbolDefinitionCollectingTraverser : public glslang::TIntermTraverser {
public:
    TSymbolDefinitionCollectingTraverser(NodeMapping* symbol_definition_mapping,
                                         AccessChainMapping* accesschain_mapping,
                                         ObjectAccesschainSet* precise_objects,
                                         ReturnBranchNodeSet* precise_return_nodes);

    bool visitUnary(glslang::TVisit, glslang::TIntermUnary*) override;
    bool visitBinary(glslang::TVisit, glslang::TIntermBinary*) override;
    void visitSymbol(glslang::TIntermSymbol*) override;
    bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate*) override;
    bool visitBranch(glslang::TVisit, glslang::TIntermBranch*) override;

protected:
    TSymbolDefinitionCollectingTraverser& operator=(const TSymbolDefinitionCollectingTraverser&);

    // The mapping from symbol node IDs to their defining nodes. This should be
    // populated along traversing the AST.
    NodeMapping& symbol_definition_mapping_;
    // The set of symbol node IDs for precise symbol nodes, the ones marked as
    // 'noContraction'.
    ObjectAccesschainSet& precise_objects_;
    // The set of precise return nodes.
    ReturnBranchNodeSet& precise_return_nodes_;
    // A temporary cache of the symbol node whose defining node is to be found
    // currently along traversing the AST.
    ObjectAccessChain current_object_;
    // A map from object node to its access chain. This traverser stores
    // the built access chains into this map for each object node it has
    // visited.
    AccessChainMapping& accesschain_mapping_;
    // The pointer to the Function Definition node, so we can get the
    // preciseness of the return expression from it when we traverse the
    // return branch node.
    glslang::TIntermAggregate* current_function_definition_node_;
};

TSymbolDefinitionCollectingTraverser::TSymbolDefinitionCollectingTraverser(
    NodeMapping* symbol_definition_mapping, AccessChainMapping* accesschain_mapping,
    ObjectAccesschainSet* precise_objects,
    std::unordered_set<glslang::TIntermBranch*>* precise_return_nodes)
    : TIntermTraverser(true, false, false), symbol_definition_mapping_(*symbol_definition_mapping),
      precise_objects_(*precise_objects), precise_return_nodes_(*precise_return_nodes),
      current_object_(), accesschain_mapping_(*accesschain_mapping),
      current_function_definition_node_(nullptr) {}

// Visits a symbol node, set the current_object_ to the
// current node symbol ID, and record a mapping from this node to the current
// current_object_, which is the just obtained symbol
// ID.
void TSymbolDefinitionCollectingTraverser::visitSymbol(glslang::TIntermSymbol* node)
{
    current_object_ = generateSymbolLabel(node);
    accesschain_mapping_[node] = current_object_;
}

// Visits an aggregate node, traverses all of its children.
bool TSymbolDefinitionCollectingTraverser::visitAggregate(glslang::TVisit,
                                                          glslang::TIntermAggregate* node)
{
    // This aggregate node might be a function definition node, in which case we need to
    // cache this node, so we can get the preciseness information of the return value
    // of this function later.
    StateSettingGuard<glslang::TIntermAggregate*> current_function_definition_node_setting_guard(
        &current_function_definition_node_);
    if (node->getOp() == glslang::EOpFunction) {
        // This is function definition node, we need to cache this node so that we can
        // get the preciseness of the return value later.
        current_function_definition_node_setting_guard.setState(node);
    }
    // Traverse the items in the sequence.
    glslang::TIntermSequence& seq = node->getSequence();
    for (int i = 0; i < (int)seq.size(); ++i) {
        current_object_.clear();
        seq[i]->traverse(this);
    }
    return false;
}

bool TSymbolDefinitionCollectingTraverser::visitBranch(glslang::TVisit,
                                                       glslang::TIntermBranch* node)
{
    if (node->getFlowOp() == glslang::EOpReturn && node->getExpression() &&
        current_function_definition_node_ &&
        current_function_definition_node_->getType().getQualifier().noContraction) {
        // This node is a return node with an expression, and its function has a
        // precise return value. We need to find the involved objects in its
        // expression and add them to the set of initial precise objects.
        precise_return_nodes_.insert(node);
        node->getExpression()->traverse(this);
    }
    return false;
}

// Visits a unary node. This might be an implicit assignment like i++, i--. etc.
bool TSymbolDefinitionCollectingTraverser::visitUnary(glslang::TVisit /* visit */,
                                                      glslang::TIntermUnary* node)
{
    current_object_.clear();
    node->getOperand()->traverse(this);
    if (isAssignOperation(node->getOp())) {
        // We should always be able to get an access chain of the operand node.
        assert(!current_object_.empty());

        // If the operand node object is 'precise', we collect its access chain
        // for the initial set of 'precise' objects.
        if (isPreciseObjectNode(node->getOperand())) {
            // The operand node is an 'precise' object node, add its
            // access chain to the set of 'precise' objects. This is to collect
            // the initial set of 'precise' objects.
            precise_objects_.insert(current_object_);
        }
        // Gets the symbol ID from the object's access chain.
        ObjectAccessChain id_symbol = getFrontElement(current_object_);
        // Add a mapping from the symbol ID to this assignment operation node.
        symbol_definition_mapping_.insert(std::make_pair(id_symbol, node));
    }
    // A unary node is not a dereference node, so we clear the access chain which
    // is under construction.
    current_object_.clear();
    return false;
}

// Visits a binary node and updates the mapping from symbol IDs to the definition
// nodes. Also collects the access chains for the initial precise objects.
bool TSymbolDefinitionCollectingTraverser::visitBinary(glslang::TVisit /* visit */,
                                                       glslang::TIntermBinary* node)
{
    // Traverses the left node to build the access chain info for the object.
    current_object_.clear();
    node->getLeft()->traverse(this);

    if (isAssignOperation(node->getOp())) {
        // We should always be able to get an access chain for the left node.
        assert(!current_object_.empty());

        // If the left node object is 'precise', it is an initial precise object
        // specified in the shader source. Adds it to the initial work list to
        // process later.
        if (isPreciseObjectNode(node->getLeft())) {
            // The left node is an 'precise' object node, add its access chain to
            // the set of 'precise' objects. This is to collect the initial set
            // of 'precise' objects.
            precise_objects_.insert(current_object_);
        }
        // Gets the symbol ID from the object access chain, which should be the
        // first element recorded in the access chain.
        ObjectAccessChain id_symbol = getFrontElement(current_object_);
        // Adds a mapping from the symbol ID to this assignment operation node.
        symbol_definition_mapping_.insert(std::make_pair(id_symbol, node));

        // Traverses the right node, there may be other 'assignment'
        // operations in the right.
        current_object_.clear();
        node->getRight()->traverse(this);

    } else if (isDereferenceOperation(node->getOp())) {
        // The left node (parent node) is a struct type object. We need to
        // record the access chain information of the current node into its
        // object id.
        if (node->getOp() == glslang::EOpIndexDirectStruct) {
            unsigned struct_dereference_index = getStructIndexFromConstantUnion(node->getRight());
            current_object_.push_back(ObjectAccesschainDelimiter);
            current_object_.append(std::to_string(struct_dereference_index));
        }
        accesschain_mapping_[node] = current_object_;

        // For a dereference node, there is no need to traverse the right child
        // node as the right node should always be an integer type object.

    } else {
        // For other binary nodes, still traverse the right node.
        current_object_.clear();
        node->getRight()->traverse(this);
    }
    return false;
}

// Traverses the AST and returns a tuple of four members:
// 1) a mapping from symbol IDs to the definition nodes (aka. assignment nodes) of these symbols.
// 2) a mapping from object nodes in the AST to the access chains of these objects.
// 3) a set of access chains of precise objects.
// 4) a set of return nodes with precise expressions.
std::tuple<NodeMapping, AccessChainMapping, ObjectAccesschainSet, ReturnBranchNodeSet>
getSymbolToDefinitionMappingAndPreciseSymbolIDs(const glslang::TIntermediate& intermediate)
{
    auto result_tuple = std::make_tuple(NodeMapping(), AccessChainMapping(), ObjectAccesschainSet(),
                                        ReturnBranchNodeSet());

    TIntermNode* root = intermediate.getTreeRoot();
    if (root == 0)
        return result_tuple;

    NodeMapping& symbol_definition_mapping = std::get<0>(result_tuple);
    AccessChainMapping& accesschain_mapping = std::get<1>(result_tuple);
    ObjectAccesschainSet& precise_objects = std::get<2>(result_tuple);
    ReturnBranchNodeSet& precise_return_nodes = std::get<3>(result_tuple);

    // Traverses the AST and populate the results.
    TSymbolDefinitionCollectingTraverser collector(&symbol_definition_mapping, &accesschain_mapping,
                                                   &precise_objects, &precise_return_nodes);
    root->traverse(&collector);

    return result_tuple;
}

//
// A traverser that determine whether the left node (or operand node for unary
// node) of an assignment node is 'precise', containing 'precise' or not,
// according to the access chain a given precise object which share the same
// symbol as the left node.
//
// Post-orderly traverses the left node subtree of an binary assignment node and:
//
//  1) Propagates the 'precise' from the left object nodes to this object node.
//
//  2) Builds object access chain along the traversal, and also compares with
//  the access chain of the given 'precise' object along with the traversal to
//  tell if the node to be defined is 'precise' or not.
//
class TNoContractionAssigneeCheckingTraverser : public glslang::TIntermTraverser {

    enum DecisionStatus {
        // The object node to be assigned to may contain 'precise' objects and also not 'precise' objects.
        Mixed = 0,
        // The object node to be assigned to is either a 'precise' object or a struct objects whose members are all 'precise'.
        Precise = 1,
        // The object node to be assigned to is not a 'precise' object.
        NotPreicse = 2,
    };

public:
    TNoContractionAssigneeCheckingTraverser(const AccessChainMapping& accesschain_mapping)
        : TIntermTraverser(true, false, false), accesschain_mapping_(accesschain_mapping),
          precise_object_(nullptr) {}

    // Checks the preciseness of a given assignment node with a precise object
    // represented as access chain. The precise object shares the same symbol
    // with the assignee of the given assignment node. Return a tuple of two:
    //
    //  1) The preciseness of the assignee node of this assignment node. True
    //  if the assignee contains 'precise' objects or is 'precise', false if
    //  the assignee is not 'precise' according to the access chain of the given
    //  precise object.
    //
    //  2) The incremental access chain from the assignee node to its nested
    //  'precise' object, according to the access chain of the given precise
    //  object. This incremental access chain can be empty, which means the
    //  assignee is 'precise'. Otherwise it shows the path to the nested
    //  precise object.
    std::tuple<bool, ObjectAccessChain>
    getPrecisenessAndRemainedAccessChain(glslang::TIntermOperator* node,
                                         const ObjectAccessChain& precise_object)
    {
        assert(isAssignOperation(node->getOp()));
        precise_object_ = &precise_object;
        ObjectAccessChain assignee_object;
        if (glslang::TIntermBinary* BN = node->getAsBinaryNode()) {
            // This is a binary assignment node, we need to check the
            // preciseness of the left node.
            assert(accesschain_mapping_.count(BN->getLeft()));
            // The left node (assignee node) is an object node, traverse the
            // node to let the 'precise' of nesting objects being transfered to
            // nested objects.
            BN->getLeft()->traverse(this);
            // After traversing the left node, if the left node is 'precise',
            // we can conclude this assignment should propagate 'precise'.
            if (isPreciseObjectNode(BN->getLeft())) {
                return make_tuple(true, ObjectAccessChain());
            }
            // If the preciseness of the left node (assignee node) can not
            // be determined by now, we need to compare the access chain string
            // of the assignee object with the given precise object.
            assignee_object = accesschain_mapping_.at(BN->getLeft());

        } else if (glslang::TIntermUnary* UN = node->getAsUnaryNode()) {
            // This is a unary assignment node, we need to check the
            // preciseness of the operand node. For unary assignment node, the
            // operand node should always be an object node.
            assert(accesschain_mapping_.count(UN->getOperand()));
            // Traverse the operand node to let the 'precise' being propagated
            // from lower nodes to upper nodes.
            UN->getOperand()->traverse(this);
            // After traversing the operand node, if the operand node is
            // 'precise', this assignment should propagate 'precise'.
            if (isPreciseObjectNode(UN->getOperand())) {
                return make_tuple(true, ObjectAccessChain());
            }
            // If the preciseness of the operand node (assignee node) can not
            // be determined by now, we need to compare the access chain string
            // of the assignee object with the given precise object.
            assignee_object = accesschain_mapping_.at(UN->getOperand());
        } else {
            // Not a binary or unary node, should not happen.
            assert(false);
        }

        // Compare the access chain string of the assignee node with the given
        // precise object to determine if this assignment should propagate
        // 'precise'.
        if (assignee_object.find(precise_object) == 0) {
            // The access chain string of the given precise object is a prefix
            // of assignee's access chain string. The assignee should be
            // 'precise'.
            return make_tuple(true, ObjectAccessChain());
        } else if (precise_object.find(assignee_object) == 0) {
            // The assignee's access chain string is a prefix of the given
            // precise object, the assignee object contains 'precise' object,
            // and we need to pass the remained access chain to the object nodes
            // in the right.
            return make_tuple(true, getSubAccessChainAfterPrefix(precise_object, assignee_object));
        } else {
            // The access chain strings do not match, the assignee object can
            // not be labeled as 'precise' according to the given precise
            // object.
            return make_tuple(false, ObjectAccessChain());
        }
    }

protected:
    TNoContractionAssigneeCheckingTraverser& operator=(const TNoContractionAssigneeCheckingTraverser&);

    bool visitBinary(glslang::TVisit, glslang::TIntermBinary* node) override;
    void visitSymbol(glslang::TIntermSymbol* node) override;

    // A map from object nodes to their access chain string (used as object ID).
    const AccessChainMapping& accesschain_mapping_;
    // A given precise object, represented in it access chain string. This
    // precise object is used to be compared with the assignee node to tell if
    // the assignee node is 'precise', contains 'precise' object or not
    // 'precise'.
    const ObjectAccessChain* precise_object_;
};

// Visits a binary node. If the node is an object node, it must be a dereference
// node. In such cases, if the left node is 'precise', this node should also be
// 'precise'.
bool TNoContractionAssigneeCheckingTraverser::visitBinary(glslang::TVisit,
                                                          glslang::TIntermBinary* node)
{
    // Traverses the left so that we transfer the 'precise' from nesting object
    // to its nested object.
    node->getLeft()->traverse(this);
    // If this binary node is an object node, we should have it in the
    // accesschain_mapping_.
    if (accesschain_mapping_.count(node)) {
        // A binary object node must be a dereference node.
        assert(isDereferenceOperation(node->getOp()));
        // If the left node is 'precise', this node should also be precise,
        // otherwise, compare with the given precise_object_. If the
        // access chain of this node matches with the given precise_object_,
        // this node should be marked as 'precise'.
        if (isPreciseObjectNode(node->getLeft())) {
            node->getWritableType().getQualifier().noContraction = true;
        } else if (accesschain_mapping_.at(node) == *precise_object_) {
            node->getWritableType().getQualifier().noContraction = true;
        }
    }
    return false;
}

// Visits a symbol node, if the symbol node ID (its access chain string) matches
// with the given precise object, this node should be 'precise'.
void TNoContractionAssigneeCheckingTraverser::visitSymbol(glslang::TIntermSymbol* node)
{
    // A symbol node should always be an object node, and should have been added
    // to the map from object nodes to their access chain strings.
    assert(accesschain_mapping_.count(node));
    if (accesschain_mapping_.at(node) == *precise_object_) {
        node->getWritableType().getQualifier().noContraction = true;
    }
}

//
// A traverser that only traverses the right side of binary assignment nodes
// and the operand node of unary assignment nodes.
//
// 1) Marks arithmetic operations as 'NoContraction'.
//
// 2) Find the object which should be marked as 'precise' in the right and
//    update the 'precise' object work list.
//
class TNoContractionPropagator : public glslang::TIntermTraverser {
public:
    TNoContractionPropagator(ObjectAccesschainSet* precise_objects,
                             const AccessChainMapping& accesschain_mapping)
        : TIntermTraverser(true, false, false),
          precise_objects_(*precise_objects), added_precise_object_ids_(),
          remained_accesschain_(), accesschain_mapping_(accesschain_mapping) {}

    // Propagates 'precise' in the right nodes of a given assignment node with
    // access chain record from the assignee node to a 'precise' object it
    // contains.
    void
    propagateNoContractionInOneExpression(glslang::TIntermTyped* defining_node,
                                          const ObjectAccessChain& assignee_remained_accesschain)
    {
        remained_accesschain_ = assignee_remained_accesschain;
        if (glslang::TIntermBinary* BN = defining_node->getAsBinaryNode()) {
            assert(isAssignOperation(BN->getOp()));
            BN->getRight()->traverse(this);
            if (isArithmeticOperation(BN->getOp())) {
                BN->getWritableType().getQualifier().noContraction = true;
            }
        } else if (glslang::TIntermUnary* UN = defining_node->getAsUnaryNode()) {
            assert(isAssignOperation(UN->getOp()));
            UN->getOperand()->traverse(this);
            if (isArithmeticOperation(UN->getOp())) {
                UN->getWritableType().getQualifier().noContraction = true;
            }
        }
    }

    // Propagates 'precise' in a given precise return node.
    void propagateNoContractionInReturnNode(glslang::TIntermBranch* return_node)
    {
        remained_accesschain_ = "";
        assert(return_node->getFlowOp() == glslang::EOpReturn && return_node->getExpression());
        return_node->getExpression()->traverse(this);
    }

protected:
    TNoContractionPropagator& operator=(const TNoContractionPropagator&);

    // Visits an aggregate node. The node can be a initializer list, in which
    // case we need to find the 'precise' or 'precise' containing object node
    // with the access chain record. In other cases, just need to traverse all
    // the children nodes.
    bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate* node) override
    {
        if (!remained_accesschain_.empty() && node->getOp() == glslang::EOpConstructStruct) {
            // This is a struct initializer node, and the remained
            // access chain is not empty, we need to refer to the
            // assignee_remained_access_chain_ to find the nested
            // 'precise' object. And we don't need to visit other nodes in this
            // aggregate node.

            // Gets the struct dereference index that leads to 'precise' object.
            ObjectAccessChain precise_accesschain_index_str =
                getFrontElement(remained_accesschain_);
            unsigned precise_accesschain_index = (unsigned)strtoul(precise_accesschain_index_str.c_str(), nullptr, 10);
            // Gets the node pointed by the access chain index extracted before.
            glslang::TIntermTyped* potential_precise_node =
                node->getSequence()[precise_accesschain_index]->getAsTyped();
            assert(potential_precise_node);
            // Pop the front access chain index from the path, and visit the nested node.
            {
                ObjectAccessChain next_level_accesschain =
                    subAccessChainFromSecondElement(remained_accesschain_);
                StateSettingGuard<ObjectAccessChain> setup_remained_accesschain_for_next_level(
                    &remained_accesschain_, next_level_accesschain);
                potential_precise_node->traverse(this);
            }
            return false;
        }
        return true;
    }

    // Visits a binary node. A binary node can be an object node, e.g. a dereference node.
    // As only the top object nodes in the right side of an assignment needs to be visited
    // and added to 'precise' work list, this traverser won't visit the children nodes of
    // an object node. If the binary node does not represent an object node, it should
    // go on to traverse its children nodes and if it is an arithmetic operation node, this
    // operation should be marked as 'noContraction'.
    bool visitBinary(glslang::TVisit, glslang::TIntermBinary* node) override
    {
        if (isDereferenceOperation(node->getOp())) {
            // This binary node is an object node. Need to update the precise
            // object set with the access chain of this node + remained
            // access chain .
            ObjectAccessChain new_precise_accesschain = accesschain_mapping_.at(node);
            if (remained_accesschain_.empty()) {
                node->getWritableType().getQualifier().noContraction = true;
            } else {
                new_precise_accesschain += ObjectAccesschainDelimiter + remained_accesschain_;
            }
            // Cache the access chain as added precise object, so we won't add the
            // same object to the work list again.
            if (!added_precise_object_ids_.count(new_precise_accesschain)) {
                precise_objects_.insert(new_precise_accesschain);
                added_precise_object_ids_.insert(new_precise_accesschain);
            }
            // Only the upper-most object nodes should be visited, so do not
            // visit children of this object node.
            return false;
        }
        // If this is an arithmetic operation, marks this node as 'noContraction'.
        if (isArithmeticOperation(node->getOp()) && node->getBasicType() != glslang::EbtInt) {
            node->getWritableType().getQualifier().noContraction = true;
        }
        // As this node is not an object node, need to traverse the children nodes.
        return true;
    }

    // Visits a unary node. A unary node can not be an object node. If the operation
    // is an arithmetic operation, need to mark this node as 'noContraction'.
    bool visitUnary(glslang::TVisit /* visit */, glslang::TIntermUnary* node) override
    {
        // If this is an arithmetic operation, marks this with 'noContraction'
        if (isArithmeticOperation(node->getOp())) {
            node->getWritableType().getQualifier().noContraction = true;
        }
        return true;
    }

    // Visits a symbol node. A symbol node is always an object node. So we
    // should always be able to find its in our collected mapping from object
    // nodes to access chains.  As an object node, a symbol node can be either
    // 'precise' or containing 'precise' objects according to unused
    // access chain information we have when we visit this node.
    void visitSymbol(glslang::TIntermSymbol* node) override
    {
        // Symbol nodes are object nodes and should always have an
        // access chain collected before matches with it.
        assert(accesschain_mapping_.count(node));
        ObjectAccessChain new_precise_accesschain = accesschain_mapping_.at(node);
        // If the unused access chain is empty, this symbol node should be
        // marked as 'precise'.  Otherwise, the unused access chain should be
        // appended to the symbol ID to build a new access chain which points to
        // the nested 'precise' object in this symbol object.
        if (remained_accesschain_.empty()) {
            node->getWritableType().getQualifier().noContraction = true;
        } else {
            new_precise_accesschain += ObjectAccesschainDelimiter + remained_accesschain_;
        }
        // Add the new 'precise' access chain to the work list and make sure we
        // don't visit it again.
        if (!added_precise_object_ids_.count(new_precise_accesschain)) {
            precise_objects_.insert(new_precise_accesschain);
            added_precise_object_ids_.insert(new_precise_accesschain);
        }
    }

    // A set of precise objects, represented as access chains.
    ObjectAccesschainSet& precise_objects_;
    // Visited symbol nodes, should not revisit these nodes.
    ObjectAccesschainSet added_precise_object_ids_;
    // The left node of an assignment operation might be an parent of 'precise' objects.
    // This means the left node might not be an 'precise' object node, but it may contains
    // 'precise' qualifier which should be propagated to the corresponding child node in
    // the right. So we need the path from the left node to its nested 'precise' node to
    // tell us how to find the corresponding 'precise' node in the right.
    ObjectAccessChain remained_accesschain_;
    // A map from node pointers to their access chains.
    const AccessChainMapping& accesschain_mapping_;
};
}

namespace glslang {

void PropagateNoContraction(const glslang::TIntermediate& intermediate)
{
    // First, traverses the AST, records symbols with their defining operations
    // and collects the initial set of precise symbols (symbol nodes that marked
    // as 'noContraction') and precise return nodes.
    auto mappings_and_precise_objects =
        getSymbolToDefinitionMappingAndPreciseSymbolIDs(intermediate);

    // The mapping of symbol node IDs to their defining nodes. This enables us
    // to get the defining node directly from a given symbol ID without
    // traversing the tree again.
    NodeMapping& symbol_definition_mapping = std::get<0>(mappings_and_precise_objects);

    // The mapping of object nodes to their access chains recorded.
    AccessChainMapping& accesschain_mapping = std::get<1>(mappings_and_precise_objects);

    // The initial set of 'precise' objects which are represented as the
    // access chain toward them.
    ObjectAccesschainSet& precise_object_accesschains = std::get<2>(mappings_and_precise_objects);

    // The set of 'precise' return nodes.
    ReturnBranchNodeSet& precise_return_nodes = std::get<3>(mappings_and_precise_objects);

    // Second, uses the initial set of precise objects as a work list, pops an
    // access chain, extract the symbol ID from it. Then:
    //  1) Check the assignee object, see if it is 'precise' object node or
    //  contains 'precise' object. Obtain the incremental access chain from the
    //  assignee node to its nested 'precise' node (if any).
    //  2) If the assignee object node is 'precise' or it contains 'precise'
    //  objects, traverses the right side of the assignment operation
    //  expression to mark arithmetic operations as 'noContration' and update
    //  'precise' access chain work list with new found object nodes.
    // Repeat above steps until the work list is empty.
    TNoContractionAssigneeCheckingTraverser checker(accesschain_mapping);
    TNoContractionPropagator propagator(&precise_object_accesschains, accesschain_mapping);

    // We have two initial precise work lists to handle:
    //  1) precise return nodes
    //  2) precise object access chains
    // We should process the precise return nodes first and the involved
    // objects in the return expression should be added to the precise object
    // access chain set.
    while (!precise_return_nodes.empty()) {
        glslang::TIntermBranch* precise_return_node = *precise_return_nodes.begin();
        propagator.propagateNoContractionInReturnNode(precise_return_node);
        precise_return_nodes.erase(precise_return_node);
    }

    while (!precise_object_accesschains.empty()) {
        // Get the access chain of a precise object from the work list.
        ObjectAccessChain precise_object_accesschain = *precise_object_accesschains.begin();
        // Get the symbol id from the access chain.
        ObjectAccessChain symbol_id = getFrontElement(precise_object_accesschain);
        // Get all the defining nodes of that symbol ID.
        std::pair<NodeMapping::iterator, NodeMapping::iterator> range =
            symbol_definition_mapping.equal_range(symbol_id);
        // Visits all the assignment nodes of that symbol ID and
        //  1) Check if the assignee node is 'precise' or contains 'precise'
        //  objects.
        //  2) Propagate the 'precise' to the top layer object nodes
        //  in the right side of the assignment operation, update the 'precise'
        //  work list with new access chains representing the new 'precise'
        //  objects, and mark arithmetic operations as 'noContraction'.
        for (NodeMapping::iterator defining_node_iter = range.first;
             defining_node_iter != range.second; defining_node_iter++) {
            TIntermOperator* defining_node = defining_node_iter->second;
            // Check the assignee node.
            auto checker_result = checker.getPrecisenessAndRemainedAccessChain(
                defining_node, precise_object_accesschain);
            bool& contain_precise = std::get<0>(checker_result);
            ObjectAccessChain& remained_accesschain = std::get<1>(checker_result);
            // If the assignee node is 'precise' or contains 'precise', propagate the
            // 'precise' to the right. Otherwise just skip this assignment node.
            if (contain_precise) {
                propagator.propagateNoContractionInOneExpression(defining_node,
                                                                 remained_accesschain);
            }
        }
        // Remove the last processed 'precise' object from the work list.
        precise_object_accesschains.erase(precise_object_accesschain);
    }
}
};