blob: da8da481f248ad5bb08d73438d59617e8ecc2f53 [file] [log] [blame]
Olli Etuaho3cbb27a2016-07-14 11:55:48 +03001//
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// SimplifyLoopConditions is an AST traverser that converts loop conditions and loop expressions
7// to regular statements inside the loop. This way further transformations that generate statements
8// from loop conditions and loop expressions work correctly.
9//
10
11#include "compiler/translator/SimplifyLoopConditions.h"
12
13#include "compiler/translator/IntermNode.h"
14#include "compiler/translator/IntermNodePatternMatcher.h"
15
16namespace
17{
18
19TIntermConstantUnion *CreateBoolConstantNode(bool value)
20{
21 TConstantUnion *u = new TConstantUnion;
22 u->setBConst(value);
23 TIntermConstantUnion *node =
24 new TIntermConstantUnion(u, TType(EbtBool, EbpUndefined, EvqConst, 1));
25 return node;
26}
27
28class SimplifyLoopConditionsTraverser : public TLValueTrackingTraverser
29{
30 public:
31 SimplifyLoopConditionsTraverser(unsigned int conditionsToSimplifyMask,
32 const TSymbolTable &symbolTable,
33 int shaderVersion);
34
35 void traverseLoop(TIntermLoop *node) override;
36
37 bool visitBinary(Visit visit, TIntermBinary *node) override;
38 bool visitAggregate(Visit visit, TIntermAggregate *node) override;
Olli Etuahod0bad2c2016-09-09 18:01:16 +030039 bool visitTernary(Visit visit, TIntermTernary *node) override;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030040
41 void nextIteration();
42 bool foundLoopToChange() const { return mFoundLoopToChange; }
43
44 protected:
45 // Marked to true once an operation that needs to be hoisted out of the expression has been
46 // found. After that, no more AST updates are performed on that traversal.
47 bool mFoundLoopToChange;
48 bool mInsideLoopConditionOrExpression;
49 IntermNodePatternMatcher mConditionsToSimplify;
50};
51
52SimplifyLoopConditionsTraverser::SimplifyLoopConditionsTraverser(
53 unsigned int conditionsToSimplifyMask,
54 const TSymbolTable &symbolTable,
55 int shaderVersion)
56 : TLValueTrackingTraverser(true, false, false, symbolTable, shaderVersion),
57 mFoundLoopToChange(false),
58 mInsideLoopConditionOrExpression(false),
59 mConditionsToSimplify(conditionsToSimplifyMask)
60{
61}
62
63void SimplifyLoopConditionsTraverser::nextIteration()
64{
65 mFoundLoopToChange = false;
66 mInsideLoopConditionOrExpression = false;
67 nextTemporaryIndex();
68}
69
70bool SimplifyLoopConditionsTraverser::visitBinary(Visit visit, TIntermBinary *node)
71{
72 // The visit functions operate in three modes:
73 // 1. If a matching expression has already been found, we return early since only one loop can
74 // be transformed on one traversal.
75 // 2. We try to find loops. In case a node is not inside a loop and can not contain loops, we
76 // stop traversing the subtree.
77 // 3. If we're inside a loop condition or expression, we check for expressions that should be
78 // moved out of the loop condition or expression. If one is found, the loop is processed.
79
80 if (mFoundLoopToChange)
81 return false;
82
83 if (!mInsideLoopConditionOrExpression)
84 return false;
85
Jamie Madill666f65a2016-08-26 01:34:37 +000086 mFoundLoopToChange = mConditionsToSimplify.match(node, getParentNode(), isLValueRequiredHere());
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030087 return !mFoundLoopToChange;
88}
89
90bool SimplifyLoopConditionsTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
91{
92 if (mFoundLoopToChange)
93 return false;
94
95 // If we're outside a loop condition, we only need to traverse nodes that may contain loops.
96 if (!mInsideLoopConditionOrExpression)
97 return (node->getOp() == EOpSequence || node->getOp() == EOpFunction);
98
99 mFoundLoopToChange = mConditionsToSimplify.match(node, getParentNode());
100 return !mFoundLoopToChange;
101}
102
Olli Etuahod0bad2c2016-09-09 18:01:16 +0300103bool SimplifyLoopConditionsTraverser::visitTernary(Visit visit, TIntermTernary *node)
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300104{
105 if (mFoundLoopToChange)
106 return false;
107
108 // Don't traverse ternary operators outside loop conditions.
109 if (!mInsideLoopConditionOrExpression)
Olli Etuahod0bad2c2016-09-09 18:01:16 +0300110 return false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300111
112 mFoundLoopToChange = mConditionsToSimplify.match(node);
113 return !mFoundLoopToChange;
114}
115
116void SimplifyLoopConditionsTraverser::traverseLoop(TIntermLoop *node)
117{
118 if (mFoundLoopToChange)
119 return;
120
121 // Mark that we're inside a loop condition or expression, and transform the loop if needed.
122
123 incrementDepth(node);
124
125 // Note: No need to traverse the loop init node.
126
127 mInsideLoopConditionOrExpression = true;
128 TLoopType loopType = node->getType();
129
130 if (node->getCondition())
131 {
132 node->getCondition()->traverse(this);
133
134 if (mFoundLoopToChange)
135 {
136 // Replace the loop condition with a boolean variable that's updated on each iteration.
137 if (loopType == ELoopWhile)
138 {
139 // Transform:
140 // while (expr) { body; }
141 // into
142 // bool s0 = expr;
143 // while (s0) { { body; } s0 = expr; }
144 TIntermSequence tempInitSeq;
145 tempInitSeq.push_back(createTempInitDeclaration(node->getCondition()->deepCopy()));
146 insertStatementsInParentBlock(tempInitSeq);
147
148 TIntermAggregate *newBody = new TIntermAggregate(EOpSequence);
149 if (node->getBody())
150 {
151 ASSERT(node->getBody()->getOp() == EOpSequence);
152 newBody->getSequence()->push_back(node->getBody());
153 }
154 newBody->getSequence()->push_back(
155 createTempAssignment(node->getCondition()->deepCopy()));
156
157 // Can't use queueReplacement to replace old body, since it may have been nullptr.
158 // It's safe to do the replacements in place here - this node won't be traversed
159 // further.
160 node->setBody(newBody);
161 node->setCondition(createTempSymbol(node->getCondition()->getType()));
162 }
163 else if (loopType == ELoopDoWhile)
164 {
165 // Transform:
166 // do {
167 // body;
168 // } while (expr);
169 // into
170 // bool s0 = true;
171 // do {
172 // { body; }
173 // s0 = expr;
174 // while (s0);
175 TIntermSequence tempInitSeq;
176 tempInitSeq.push_back(createTempInitDeclaration(CreateBoolConstantNode(true)));
177 insertStatementsInParentBlock(tempInitSeq);
178
179 TIntermAggregate *newBody = new TIntermAggregate(EOpSequence);
180 if (node->getBody())
181 {
182 ASSERT(node->getBody()->getOp() == EOpSequence);
183 newBody->getSequence()->push_back(node->getBody());
184 }
185 newBody->getSequence()->push_back(
186 createTempAssignment(node->getCondition()->deepCopy()));
187
188 // Can't use queueReplacement to replace old body, since it may have been nullptr.
189 // It's safe to do the replacements in place here - this node won't be traversed
190 // further.
191 node->setBody(newBody);
192 node->setCondition(createTempSymbol(node->getCondition()->getType()));
193 }
194 else if (loopType == ELoopFor)
195 {
196 // Move the loop condition inside the loop.
197 // Transform:
198 // for (init; expr; exprB) { body; }
199 // into
200 // {
201 // init;
202 // bool s0 = expr;
203 // while (s0) { { body; } exprB; s0 = expr; }
204 // }
205 TIntermAggregate *loopScope = new TIntermAggregate(EOpSequence);
206 if (node->getInit())
207 {
208 loopScope->getSequence()->push_back(node->getInit());
209 }
210 loopScope->getSequence()->push_back(
211 createTempInitDeclaration(node->getCondition()->deepCopy()));
212
213 TIntermAggregate *whileLoopBody = new TIntermAggregate(EOpSequence);
214 if (node->getBody())
215 {
216 whileLoopBody->getSequence()->push_back(node->getBody());
217 }
218 whileLoopBody->getSequence()->push_back(node->getExpression());
219 whileLoopBody->getSequence()->push_back(
220 createTempAssignment(node->getCondition()->deepCopy()));
221 TIntermLoop *whileLoop = new TIntermLoop(
222 ELoopWhile, nullptr, createTempSymbol(node->getCondition()->getType()), nullptr,
223 whileLoopBody);
224 loopScope->getSequence()->push_back(whileLoop);
225 queueReplacementWithParent(getAncestorNode(1), node, loopScope,
226 OriginalNode::IS_DROPPED);
227 }
228 }
229 }
230
231 if (!mFoundLoopToChange && node->getExpression())
232 {
233 node->getExpression()->traverse(this);
234
235 if (mFoundLoopToChange)
236 {
237 ASSERT(loopType == ELoopFor);
238 // Move the loop expression to inside the loop.
239 // Transform:
240 // for (init; expr; exprB) { body; }
241 // into
242 // for (init; expr; ) { { body; } exprB; }
243 TIntermTyped *loopExpression = node->getExpression();
244 node->setExpression(nullptr);
245 TIntermAggregate *oldBody = node->getBody();
246 node->setBody(new TIntermAggregate(EOpSequence));
247 if (oldBody != nullptr)
248 {
249 node->getBody()->getSequence()->push_back(oldBody);
250 }
251 node->getBody()->getSequence()->push_back(loopExpression);
252 }
253 }
254
255 mInsideLoopConditionOrExpression = false;
256
257 if (!mFoundLoopToChange && node->getBody())
258 node->getBody()->traverse(this);
259
260 decrementDepth();
261}
262
263} // namespace
264
265void SimplifyLoopConditions(TIntermNode *root,
266 unsigned int conditionsToSimplifyMask,
267 unsigned int *temporaryIndex,
268 const TSymbolTable &symbolTable,
269 int shaderVersion)
270{
271 SimplifyLoopConditionsTraverser traverser(conditionsToSimplifyMask, symbolTable, shaderVersion);
272 ASSERT(temporaryIndex != nullptr);
273 traverser.useTemporaryIndex(temporaryIndex);
274 // Process one loop at a time, and reset the traverser between iterations.
275 do
276 {
277 traverser.nextIteration();
278 root->traverse(&traverser);
279 if (traverser.foundLoopToChange())
280 traverser.updateTree();
281 } while (traverser.foundLoopToChange());
282}