blob: 071b8b8655c933325188a8da548d5a71b2c9de85 [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)
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010097 return (node->getOp() == EOpFunction);
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030098
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
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100148 TIntermBlock *newBody = new TIntermBlock();
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300149 if (node->getBody())
150 {
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300151 newBody->getSequence()->push_back(node->getBody());
152 }
153 newBody->getSequence()->push_back(
154 createTempAssignment(node->getCondition()->deepCopy()));
155
156 // Can't use queueReplacement to replace old body, since it may have been nullptr.
157 // It's safe to do the replacements in place here - this node won't be traversed
158 // further.
159 node->setBody(newBody);
160 node->setCondition(createTempSymbol(node->getCondition()->getType()));
161 }
162 else if (loopType == ELoopDoWhile)
163 {
164 // Transform:
165 // do {
166 // body;
167 // } while (expr);
168 // into
169 // bool s0 = true;
170 // do {
171 // { body; }
172 // s0 = expr;
173 // while (s0);
174 TIntermSequence tempInitSeq;
175 tempInitSeq.push_back(createTempInitDeclaration(CreateBoolConstantNode(true)));
176 insertStatementsInParentBlock(tempInitSeq);
177
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100178 TIntermBlock *newBody = new TIntermBlock();
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300179 if (node->getBody())
180 {
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300181 newBody->getSequence()->push_back(node->getBody());
182 }
183 newBody->getSequence()->push_back(
184 createTempAssignment(node->getCondition()->deepCopy()));
185
186 // Can't use queueReplacement to replace old body, since it may have been nullptr.
187 // It's safe to do the replacements in place here - this node won't be traversed
188 // further.
189 node->setBody(newBody);
190 node->setCondition(createTempSymbol(node->getCondition()->getType()));
191 }
192 else if (loopType == ELoopFor)
193 {
194 // Move the loop condition inside the loop.
195 // Transform:
196 // for (init; expr; exprB) { body; }
197 // into
198 // {
199 // init;
200 // bool s0 = expr;
201 // while (s0) { { body; } exprB; s0 = expr; }
202 // }
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100203 TIntermBlock *loopScope = new TIntermBlock();
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300204 if (node->getInit())
205 {
206 loopScope->getSequence()->push_back(node->getInit());
207 }
208 loopScope->getSequence()->push_back(
209 createTempInitDeclaration(node->getCondition()->deepCopy()));
210
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100211 TIntermBlock *whileLoopBody = new TIntermBlock();
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300212 if (node->getBody())
213 {
214 whileLoopBody->getSequence()->push_back(node->getBody());
215 }
216 whileLoopBody->getSequence()->push_back(node->getExpression());
217 whileLoopBody->getSequence()->push_back(
218 createTempAssignment(node->getCondition()->deepCopy()));
219 TIntermLoop *whileLoop = new TIntermLoop(
220 ELoopWhile, nullptr, createTempSymbol(node->getCondition()->getType()), nullptr,
221 whileLoopBody);
222 loopScope->getSequence()->push_back(whileLoop);
223 queueReplacementWithParent(getAncestorNode(1), node, loopScope,
224 OriginalNode::IS_DROPPED);
225 }
226 }
227 }
228
229 if (!mFoundLoopToChange && node->getExpression())
230 {
231 node->getExpression()->traverse(this);
232
233 if (mFoundLoopToChange)
234 {
235 ASSERT(loopType == ELoopFor);
236 // Move the loop expression to inside the loop.
237 // Transform:
238 // for (init; expr; exprB) { body; }
239 // into
240 // for (init; expr; ) { { body; } exprB; }
241 TIntermTyped *loopExpression = node->getExpression();
242 node->setExpression(nullptr);
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100243 TIntermBlock *oldBody = node->getBody();
244 node->setBody(new TIntermBlock());
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300245 if (oldBody != nullptr)
246 {
247 node->getBody()->getSequence()->push_back(oldBody);
248 }
249 node->getBody()->getSequence()->push_back(loopExpression);
250 }
251 }
252
253 mInsideLoopConditionOrExpression = false;
254
255 if (!mFoundLoopToChange && node->getBody())
256 node->getBody()->traverse(this);
257
258 decrementDepth();
259}
260
261} // namespace
262
263void SimplifyLoopConditions(TIntermNode *root,
264 unsigned int conditionsToSimplifyMask,
265 unsigned int *temporaryIndex,
266 const TSymbolTable &symbolTable,
267 int shaderVersion)
268{
269 SimplifyLoopConditionsTraverser traverser(conditionsToSimplifyMask, symbolTable, shaderVersion);
270 ASSERT(temporaryIndex != nullptr);
271 traverser.useTemporaryIndex(temporaryIndex);
272 // Process one loop at a time, and reset the traverser between iterations.
273 do
274 {
275 traverser.nextIteration();
276 root->traverse(&traverser);
277 if (traverser.foundLoopToChange())
278 traverser.updateTree();
279 } while (traverser.foundLoopToChange());
280}