blob: 68fdb0334ffad2e9fd9018a3e911b9c1b6e510bd [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{
Jamie Madilld7b1ab52016-12-12 14:42:19 -050069 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;
Corentin Wallez36fd1002016-12-08 11:30:44 -0500227 // while (s0) {
228 // { body; }
229 // exprB;
230 // s0 = expr;
231 // }
Corentin Wallez1212bca2016-11-23 13:44:05 -0500232 // }
Corentin Wallez36fd1002016-12-08 11:30:44 -0500233 TIntermBlock *loopScope = new TIntermBlock();
234 TIntermSequence *loopScopeSequence = loopScope->getSequence();
235
236 // Insert "init;"
Corentin Wallez1212bca2016-11-23 13:44:05 -0500237 if (node->getInit())
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300238 {
Corentin Wallez36fd1002016-12-08 11:30:44 -0500239 loopScopeSequence->push_back(node->getInit());
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300240 }
Corentin Wallez1212bca2016-11-23 13:44:05 -0500241
Corentin Wallez36fd1002016-12-08 11:30:44 -0500242 // Insert "bool s0 = expr;" if applicable, "bool s0 = true;" otherwise
243 TIntermTyped *conditionInitializer = nullptr;
244 if (node->getCondition())
245 {
246 conditionInitializer = node->getCondition()->deepCopy();
247 }
248 else
249 {
250 conditionInitializer = TIntermTyped::CreateBool(true);
251 }
252 loopScopeSequence->push_back(createTempInitDeclaration(conditionInitializer));
253
254 // Insert "{ body; }" in the while loop
Corentin Wallez1212bca2016-11-23 13:44:05 -0500255 TIntermBlock *whileLoopBody = new TIntermBlock();
256 if (node->getBody())
257 {
258 whileLoopBody->getSequence()->push_back(node->getBody());
259 }
Corentin Wallez36fd1002016-12-08 11:30:44 -0500260 // Insert "exprB;" in the while loop
Corentin Wallez1212bca2016-11-23 13:44:05 -0500261 if (node->getExpression())
262 {
263 whileLoopBody->getSequence()->push_back(node->getExpression());
264 }
Corentin Wallez36fd1002016-12-08 11:30:44 -0500265 // Insert "s0 = expr;" in the while loop
266 if (node->getCondition())
267 {
268 whileLoopBody->getSequence()->push_back(
269 createTempAssignment(node->getCondition()->deepCopy()));
270 }
271
272 // Create "while(s0) { whileLoopBody }"
Corentin Wallez1212bca2016-11-23 13:44:05 -0500273 TIntermLoop *whileLoop = new TIntermLoop(
Corentin Wallez36fd1002016-12-08 11:30:44 -0500274 ELoopWhile, nullptr, createTempSymbol(conditionInitializer->getType()), nullptr,
Corentin Wallez1212bca2016-11-23 13:44:05 -0500275 whileLoopBody);
276 loopScope->getSequence()->push_back(whileLoop);
277 queueReplacementWithParent(getAncestorNode(1), node, loopScope,
278 OriginalNode::IS_DROPPED);
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300279 }
280 }
281
Corentin Wallez1212bca2016-11-23 13:44:05 -0500282 mInsideLoopInitConditionOrExpression = false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300283
284 if (!mFoundLoopToChange && node->getBody())
285 node->getBody()->traverse(this);
286
287 decrementDepth();
288}
289
290} // namespace
291
292void SimplifyLoopConditions(TIntermNode *root,
293 unsigned int conditionsToSimplifyMask,
294 unsigned int *temporaryIndex,
295 const TSymbolTable &symbolTable,
296 int shaderVersion)
297{
298 SimplifyLoopConditionsTraverser traverser(conditionsToSimplifyMask, symbolTable, shaderVersion);
299 ASSERT(temporaryIndex != nullptr);
300 traverser.useTemporaryIndex(temporaryIndex);
301 // Process one loop at a time, and reset the traverser between iterations.
302 do
303 {
304 traverser.nextIteration();
305 root->traverse(&traverser);
306 if (traverser.foundLoopToChange())
307 traverser.updateTree();
308 } while (traverser.foundLoopToChange());
309}
Jamie Madill45bcc782016-11-07 13:58:48 -0500310
311} // namespace sh