1 //
2 // Copyright 2016 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // SplitSequenceOperator is an AST traverser that detects sequence operator expressions that
7 // go through further AST transformations that generate statements, and splits them so that
8 // possible side effects of earlier parts of the sequence operator expression are guaranteed to be
9 // evaluated before the latter parts of the sequence operator expression are evaluated.
10 //
11 
12 #include "compiler/translator/tree_ops/SplitSequenceOperator.h"
13 
14 #include "compiler/translator/tree_util/IntermNodePatternMatcher.h"
15 #include "compiler/translator/tree_util/IntermTraverse.h"
16 
17 namespace sh
18 {
19 
20 namespace
21 {
22 
23 class SplitSequenceOperatorTraverser : public TLValueTrackingTraverser
24 {
25   public:
26     SplitSequenceOperatorTraverser(unsigned int patternsToSplitMask, TSymbolTable *symbolTable);
27 
28     bool visitUnary(Visit visit, TIntermUnary *node) override;
29     bool visitBinary(Visit visit, TIntermBinary *node) override;
30     bool visitAggregate(Visit visit, TIntermAggregate *node) override;
31     bool visitTernary(Visit visit, TIntermTernary *node) override;
32 
33     void nextIteration();
foundExpressionToSplit() const34     bool foundExpressionToSplit() const { return mFoundExpressionToSplit; }
35 
36   protected:
37     // Marked to true once an operation that needs to be hoisted out of the expression has been
38     // found. After that, no more AST updates are performed on that traversal.
39     bool mFoundExpressionToSplit;
40     int mInsideSequenceOperator;
41 
42     IntermNodePatternMatcher mPatternToSplitMatcher;
43 };
44 
SplitSequenceOperatorTraverser(unsigned int patternsToSplitMask,TSymbolTable * symbolTable)45 SplitSequenceOperatorTraverser::SplitSequenceOperatorTraverser(unsigned int patternsToSplitMask,
46                                                                TSymbolTable *symbolTable)
47     : TLValueTrackingTraverser(true, false, true, symbolTable),
48       mFoundExpressionToSplit(false),
49       mInsideSequenceOperator(0),
50       mPatternToSplitMatcher(patternsToSplitMask)
51 {}
52 
nextIteration()53 void SplitSequenceOperatorTraverser::nextIteration()
54 {
55     mFoundExpressionToSplit = false;
56     mInsideSequenceOperator = 0;
57 }
58 
visitAggregate(Visit visit,TIntermAggregate * node)59 bool SplitSequenceOperatorTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
60 {
61     if (mFoundExpressionToSplit)
62         return false;
63 
64     if (mInsideSequenceOperator > 0 && visit == PreVisit)
65     {
66         // Detect expressions that need to be simplified
67         mFoundExpressionToSplit = mPatternToSplitMatcher.match(node, getParentNode());
68         return !mFoundExpressionToSplit;
69     }
70 
71     return true;
72 }
73 
visitUnary(Visit visit,TIntermUnary * node)74 bool SplitSequenceOperatorTraverser::visitUnary(Visit visit, TIntermUnary *node)
75 {
76     if (mFoundExpressionToSplit)
77         return false;
78 
79     if (mInsideSequenceOperator > 0 && visit == PreVisit)
80     {
81         // Detect expressions that need to be simplified
82         mFoundExpressionToSplit = mPatternToSplitMatcher.match(node);
83         return !mFoundExpressionToSplit;
84     }
85 
86     return true;
87 }
88 
visitBinary(Visit visit,TIntermBinary * node)89 bool SplitSequenceOperatorTraverser::visitBinary(Visit visit, TIntermBinary *node)
90 {
91     if (node->getOp() == EOpComma)
92     {
93         if (visit == PreVisit)
94         {
95             if (mFoundExpressionToSplit)
96             {
97                 return false;
98             }
99             mInsideSequenceOperator++;
100         }
101         else if (visit == PostVisit)
102         {
103             // Split sequence operators starting from the outermost one to preserve correct
104             // execution order.
105             if (mFoundExpressionToSplit && mInsideSequenceOperator == 1)
106             {
107                 // Move the left side operand into a separate statement in the parent block.
108                 TIntermSequence insertions;
109                 insertions.push_back(node->getLeft());
110                 insertStatementsInParentBlock(insertions);
111                 // Replace the comma node with its right side operand.
112                 queueReplacement(node->getRight(), OriginalNode::IS_DROPPED);
113             }
114             mInsideSequenceOperator--;
115         }
116         return true;
117     }
118 
119     if (mFoundExpressionToSplit)
120         return false;
121 
122     if (mInsideSequenceOperator > 0 && visit == PreVisit)
123     {
124         // Detect expressions that need to be simplified
125         mFoundExpressionToSplit =
126             mPatternToSplitMatcher.match(node, getParentNode(), isLValueRequiredHere());
127         return !mFoundExpressionToSplit;
128     }
129 
130     return true;
131 }
132 
visitTernary(Visit visit,TIntermTernary * node)133 bool SplitSequenceOperatorTraverser::visitTernary(Visit visit, TIntermTernary *node)
134 {
135     if (mFoundExpressionToSplit)
136         return false;
137 
138     if (mInsideSequenceOperator > 0 && visit == PreVisit)
139     {
140         // Detect expressions that need to be simplified
141         mFoundExpressionToSplit = mPatternToSplitMatcher.match(node);
142         return !mFoundExpressionToSplit;
143     }
144 
145     return true;
146 }
147 
148 }  // namespace
149 
SplitSequenceOperator(TCompiler * compiler,TIntermNode * root,int patternsToSplitMask,TSymbolTable * symbolTable)150 bool SplitSequenceOperator(TCompiler *compiler,
151                            TIntermNode *root,
152                            int patternsToSplitMask,
153                            TSymbolTable *symbolTable)
154 {
155     SplitSequenceOperatorTraverser traverser(patternsToSplitMask, symbolTable);
156     // Separate one expression at a time, and reset the traverser between iterations.
157     do
158     {
159         traverser.nextIteration();
160         root->traverse(&traverser);
161         if (traverser.foundExpressionToSplit())
162         {
163             if (!traverser.updateTree(compiler, root))
164             {
165                 return false;
166             }
167         }
168     } while (traverser.foundExpressionToSplit());
169 
170     return true;
171 }
172 
173 }  // namespace sh
174