1//
2// Copyright (c) 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
17namespace sh
18{
19
20namespace
21{
22
23class 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();
34 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
45SplitSequenceOperatorTraverser::SplitSequenceOperatorTraverser(unsigned int patternsToSplitMask,
46 TSymbolTable *symbolTable)
47 : TLValueTrackingTraverser(true, false, true, symbolTable),
48 mFoundExpressionToSplit(false),
49 mInsideSequenceOperator(0),
50 mPatternToSplitMatcher(patternsToSplitMask)
51{}
52
53void SplitSequenceOperatorTraverser::nextIteration()
54{
55 mFoundExpressionToSplit = false;
56 mInsideSequenceOperator = 0;
57}
58
59bool 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
74bool 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
89bool 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
133bool 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
150void SplitSequenceOperator(TIntermNode *root, int patternsToSplitMask, TSymbolTable *symbolTable)
151{
152 SplitSequenceOperatorTraverser traverser(patternsToSplitMask, symbolTable);
153 // Separate one expression at a time, and reset the traverser between iterations.
154 do
155 {
156 traverser.nextIteration();
157 root->traverse(&traverser);
158 if (traverser.foundExpressionToSplit())
159 traverser.updateTree();
160 } while (traverser.foundExpressionToSplit());
161}
162
163} // namespace sh
164