blob: 1f8a384f8eaa69fcb1d237a2999b46491a6dbf7b [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;
Corentin Wallez1212bca2016-11-23 13:44:05 -050043 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030044
45 void nextIteration();
46 bool foundLoopToChange() const { return mFoundLoopToChange; }
47
48 protected:
49 // Marked to true once an operation that needs to be hoisted out of the expression has been
50 // found. After that, no more AST updates are performed on that traversal.
51 bool mFoundLoopToChange;
Corentin Wallez1212bca2016-11-23 13:44:05 -050052 bool mInsideLoopInitConditionOrExpression;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030053 IntermNodePatternMatcher mConditionsToSimplify;
54};
55
56SimplifyLoopConditionsTraverser::SimplifyLoopConditionsTraverser(
57 unsigned int conditionsToSimplifyMask,
58 const TSymbolTable &symbolTable,
59 int shaderVersion)
60 : TLValueTrackingTraverser(true, false, false, symbolTable, shaderVersion),
61 mFoundLoopToChange(false),
Corentin Wallez1212bca2016-11-23 13:44:05 -050062 mInsideLoopInitConditionOrExpression(false),
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030063 mConditionsToSimplify(conditionsToSimplifyMask)
64{
65}
66
67void SimplifyLoopConditionsTraverser::nextIteration()
68{
69 mFoundLoopToChange = false;
Corentin Wallez1212bca2016-11-23 13:44:05 -050070 mInsideLoopInitConditionOrExpression = false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030071 nextTemporaryIndex();
72}
73
Corentin Wallez1212bca2016-11-23 13:44:05 -050074// The visit functions operate in three modes:
75// 1. If a matching expression has already been found, we return early since only one loop can
76// be transformed on one traversal.
77// 2. We try to find loops. In case a node is not inside a loop and can not contain loops, we
78// stop traversing the subtree.
79// 3. If we're inside a loop initialization, condition or expression, we check for expressions
80// that should be moved out of the loop condition or expression. If one is found, the loop
81// is processed.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030082bool SimplifyLoopConditionsTraverser::visitBinary(Visit visit, TIntermBinary *node)
83{
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030084
85 if (mFoundLoopToChange)
86 return false;
87
Corentin Wallez1212bca2016-11-23 13:44:05 -050088 if (!mInsideLoopInitConditionOrExpression)
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030089 return false;
90
Jamie Madill666f65a2016-08-26 01:34:37 +000091 mFoundLoopToChange = mConditionsToSimplify.match(node, getParentNode(), isLValueRequiredHere());
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030092 return !mFoundLoopToChange;
93}
94
95bool SimplifyLoopConditionsTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
96{
97 if (mFoundLoopToChange)
98 return false;
99
Corentin Wallez1212bca2016-11-23 13:44:05 -0500100 if (!mInsideLoopInitConditionOrExpression)
Olli Etuaho336b1472016-10-05 16:37:55 +0100101 return false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300102
103 mFoundLoopToChange = mConditionsToSimplify.match(node, getParentNode());
104 return !mFoundLoopToChange;
105}
106
Olli Etuahod0bad2c2016-09-09 18:01:16 +0300107bool SimplifyLoopConditionsTraverser::visitTernary(Visit visit, TIntermTernary *node)
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300108{
109 if (mFoundLoopToChange)
110 return false;
111
Corentin Wallez1212bca2016-11-23 13:44:05 -0500112 if (!mInsideLoopInitConditionOrExpression)
113 return false;
114
115 mFoundLoopToChange = mConditionsToSimplify.match(node);
116 return !mFoundLoopToChange;
117}
118
119bool SimplifyLoopConditionsTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
120{
121 if (mFoundLoopToChange)
122 return false;
123
124 if (!mInsideLoopInitConditionOrExpression)
Olli Etuahod0bad2c2016-09-09 18:01:16 +0300125 return false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300126
127 mFoundLoopToChange = mConditionsToSimplify.match(node);
128 return !mFoundLoopToChange;
129}
130
131void SimplifyLoopConditionsTraverser::traverseLoop(TIntermLoop *node)
132{
133 if (mFoundLoopToChange)
134 return;
135
136 // Mark that we're inside a loop condition or expression, and transform the loop if needed.
137
138 incrementDepth(node);
139
140 // Note: No need to traverse the loop init node.
141
Corentin Wallez1212bca2016-11-23 13:44:05 -0500142 mInsideLoopInitConditionOrExpression = true;
143 TLoopType loopType = node->getType();
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300144
Corentin Wallez1212bca2016-11-23 13:44:05 -0500145 if (!mFoundLoopToChange && node->getInit())
146 {
147 node->getInit()->traverse(this);
148 }
149
150 if (!mFoundLoopToChange && node->getCondition())
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300151 {
152 node->getCondition()->traverse(this);
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300153 }
154
155 if (!mFoundLoopToChange && node->getExpression())
156 {
157 node->getExpression()->traverse(this);
Corentin Wallez1212bca2016-11-23 13:44:05 -0500158 }
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300159
Corentin Wallez1212bca2016-11-23 13:44:05 -0500160 if (mFoundLoopToChange)
161 {
162 // Replace the loop condition with a boolean variable that's updated on each iteration.
163 if (loopType == ELoopWhile)
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300164 {
Corentin Wallez1212bca2016-11-23 13:44:05 -0500165 // Transform:
166 // while (expr) { body; }
167 // into
168 // bool s0 = expr;
169 // while (s0) { { body; } s0 = expr; }
170 TIntermSequence tempInitSeq;
171 tempInitSeq.push_back(createTempInitDeclaration(node->getCondition()->deepCopy()));
172 insertStatementsInParentBlock(tempInitSeq);
173
174 TIntermBlock *newBody = new TIntermBlock();
175 if (node->getBody())
176 {
177 newBody->getSequence()->push_back(node->getBody());
178 }
179 newBody->getSequence()->push_back(
180 createTempAssignment(node->getCondition()->deepCopy()));
181
182 // Can't use queueReplacement to replace old body, since it may have been nullptr.
183 // It's safe to do the replacements in place here - this node won't be traversed
184 // further.
185 node->setBody(newBody);
186 node->setCondition(createTempSymbol(node->getCondition()->getType()));
187 }
188 else if (loopType == ELoopDoWhile)
189 {
190 // Transform:
191 // do {
192 // body;
193 // } while (expr);
194 // into
195 // bool s0 = true;
196 // do {
197 // { body; }
198 // s0 = expr;
199 // } while (s0);
200 TIntermSequence tempInitSeq;
201 tempInitSeq.push_back(createTempInitDeclaration(CreateBoolConstantNode(true)));
202 insertStatementsInParentBlock(tempInitSeq);
203
204 TIntermBlock *newBody = new TIntermBlock();
205 if (node->getBody())
206 {
207 newBody->getSequence()->push_back(node->getBody());
208 }
209 newBody->getSequence()->push_back(
210 createTempAssignment(node->getCondition()->deepCopy()));
211
212 // Can't use queueReplacement to replace old body, since it may have been nullptr.
213 // It's safe to do the replacements in place here - this node won't be traversed
214 // further.
215 node->setBody(newBody);
216 node->setCondition(createTempSymbol(node->getCondition()->getType()));
217 }
218 else if (loopType == ELoopFor)
219 {
220 // Move the loop condition inside the loop.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300221 // Transform:
222 // for (init; expr; exprB) { body; }
223 // into
Corentin Wallez1212bca2016-11-23 13:44:05 -0500224 // {
225 // init;
226 // bool s0 = expr;
227 // while (s0) { { body; } exprB; s0 = expr; }
228 // }
229 TIntermBlock *loopScope = new TIntermBlock();
230 if (node->getInit())
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300231 {
Corentin Wallez1212bca2016-11-23 13:44:05 -0500232 loopScope->getSequence()->push_back(node->getInit());
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300233 }
Corentin Wallez1212bca2016-11-23 13:44:05 -0500234 loopScope->getSequence()->push_back(
235 createTempInitDeclaration(node->getCondition()->deepCopy()));
236
237 TIntermBlock *whileLoopBody = new TIntermBlock();
238 if (node->getBody())
239 {
240 whileLoopBody->getSequence()->push_back(node->getBody());
241 }
242 if (node->getExpression())
243 {
244 whileLoopBody->getSequence()->push_back(node->getExpression());
245 }
246 whileLoopBody->getSequence()->push_back(
247 createTempAssignment(node->getCondition()->deepCopy()));
248 TIntermLoop *whileLoop = new TIntermLoop(
249 ELoopWhile, nullptr, createTempSymbol(node->getCondition()->getType()), nullptr,
250 whileLoopBody);
251 loopScope->getSequence()->push_back(whileLoop);
252 queueReplacementWithParent(getAncestorNode(1), node, loopScope,
253 OriginalNode::IS_DROPPED);
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300254 }
255 }
256
Corentin Wallez1212bca2016-11-23 13:44:05 -0500257 mInsideLoopInitConditionOrExpression = false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300258
259 if (!mFoundLoopToChange && node->getBody())
260 node->getBody()->traverse(this);
261
262 decrementDepth();
263}
264
265} // namespace
266
267void SimplifyLoopConditions(TIntermNode *root,
268 unsigned int conditionsToSimplifyMask,
269 unsigned int *temporaryIndex,
270 const TSymbolTable &symbolTable,
271 int shaderVersion)
272{
273 SimplifyLoopConditionsTraverser traverser(conditionsToSimplifyMask, symbolTable, shaderVersion);
274 ASSERT(temporaryIndex != nullptr);
275 traverser.useTemporaryIndex(temporaryIndex);
276 // Process one loop at a time, and reset the traverser between iterations.
277 do
278 {
279 traverser.nextIteration();
280 root->traverse(&traverser);
281 if (traverser.foundLoopToChange())
282 traverser.updateTree();
283 } while (traverser.foundLoopToChange());
284}
Jamie Madill45bcc782016-11-07 13:58:48 -0500285
286} // namespace sh