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