blob: a2ed7f3846b1f8c96b179d50d03b7696baaf5c53 [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
Olli Etuaho1d9dcc22017-01-19 11:25:32 +0000138 ScopedNodeInTraversalPath addToPath(this, node);
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300139
Corentin Wallez1212bca2016-11-23 13:44:05 -0500140 mInsideLoopInitConditionOrExpression = true;
141 TLoopType loopType = node->getType();
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300142
Corentin Wallez1212bca2016-11-23 13:44:05 -0500143 if (!mFoundLoopToChange && node->getInit())
144 {
145 node->getInit()->traverse(this);
146 }
147
148 if (!mFoundLoopToChange && node->getCondition())
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300149 {
150 node->getCondition()->traverse(this);
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300151 }
152
153 if (!mFoundLoopToChange && node->getExpression())
154 {
155 node->getExpression()->traverse(this);
Corentin Wallez1212bca2016-11-23 13:44:05 -0500156 }
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300157
Corentin Wallez1212bca2016-11-23 13:44:05 -0500158 if (mFoundLoopToChange)
159 {
160 // Replace the loop condition with a boolean variable that's updated on each iteration.
161 if (loopType == ELoopWhile)
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300162 {
Corentin Wallez1212bca2016-11-23 13:44:05 -0500163 // Transform:
164 // while (expr) { body; }
165 // into
166 // bool s0 = expr;
167 // while (s0) { { body; } s0 = expr; }
168 TIntermSequence tempInitSeq;
169 tempInitSeq.push_back(createTempInitDeclaration(node->getCondition()->deepCopy()));
170 insertStatementsInParentBlock(tempInitSeq);
171
172 TIntermBlock *newBody = new TIntermBlock();
173 if (node->getBody())
174 {
175 newBody->getSequence()->push_back(node->getBody());
176 }
177 newBody->getSequence()->push_back(
178 createTempAssignment(node->getCondition()->deepCopy()));
179
180 // Can't use queueReplacement to replace old body, since it may have been nullptr.
181 // It's safe to do the replacements in place here - this node won't be traversed
182 // further.
183 node->setBody(newBody);
184 node->setCondition(createTempSymbol(node->getCondition()->getType()));
185 }
186 else if (loopType == ELoopDoWhile)
187 {
188 // Transform:
189 // do {
190 // body;
191 // } while (expr);
192 // into
193 // bool s0 = true;
194 // do {
195 // { body; }
196 // s0 = expr;
197 // } while (s0);
198 TIntermSequence tempInitSeq;
199 tempInitSeq.push_back(createTempInitDeclaration(CreateBoolConstantNode(true)));
200 insertStatementsInParentBlock(tempInitSeq);
201
202 TIntermBlock *newBody = new TIntermBlock();
203 if (node->getBody())
204 {
205 newBody->getSequence()->push_back(node->getBody());
206 }
207 newBody->getSequence()->push_back(
208 createTempAssignment(node->getCondition()->deepCopy()));
209
210 // Can't use queueReplacement to replace old body, since it may have been nullptr.
211 // It's safe to do the replacements in place here - this node won't be traversed
212 // further.
213 node->setBody(newBody);
214 node->setCondition(createTempSymbol(node->getCondition()->getType()));
215 }
216 else if (loopType == ELoopFor)
217 {
218 // Move the loop condition inside the loop.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300219 // Transform:
220 // for (init; expr; exprB) { body; }
221 // into
Corentin Wallez1212bca2016-11-23 13:44:05 -0500222 // {
223 // init;
224 // bool s0 = expr;
Corentin Wallez36fd1002016-12-08 11:30:44 -0500225 // while (s0) {
226 // { body; }
227 // exprB;
228 // s0 = expr;
229 // }
Corentin Wallez1212bca2016-11-23 13:44:05 -0500230 // }
Corentin Wallez36fd1002016-12-08 11:30:44 -0500231 TIntermBlock *loopScope = new TIntermBlock();
232 TIntermSequence *loopScopeSequence = loopScope->getSequence();
233
234 // Insert "init;"
Corentin Wallez1212bca2016-11-23 13:44:05 -0500235 if (node->getInit())
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300236 {
Corentin Wallez36fd1002016-12-08 11:30:44 -0500237 loopScopeSequence->push_back(node->getInit());
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300238 }
Corentin Wallez1212bca2016-11-23 13:44:05 -0500239
Corentin Wallez36fd1002016-12-08 11:30:44 -0500240 // Insert "bool s0 = expr;" if applicable, "bool s0 = true;" otherwise
241 TIntermTyped *conditionInitializer = nullptr;
242 if (node->getCondition())
243 {
244 conditionInitializer = node->getCondition()->deepCopy();
245 }
246 else
247 {
248 conditionInitializer = TIntermTyped::CreateBool(true);
249 }
250 loopScopeSequence->push_back(createTempInitDeclaration(conditionInitializer));
251
252 // Insert "{ body; }" in the while loop
Corentin Wallez1212bca2016-11-23 13:44:05 -0500253 TIntermBlock *whileLoopBody = new TIntermBlock();
254 if (node->getBody())
255 {
256 whileLoopBody->getSequence()->push_back(node->getBody());
257 }
Corentin Wallez36fd1002016-12-08 11:30:44 -0500258 // Insert "exprB;" in the while loop
Corentin Wallez1212bca2016-11-23 13:44:05 -0500259 if (node->getExpression())
260 {
261 whileLoopBody->getSequence()->push_back(node->getExpression());
262 }
Corentin Wallez36fd1002016-12-08 11:30:44 -0500263 // Insert "s0 = expr;" in the while loop
264 if (node->getCondition())
265 {
266 whileLoopBody->getSequence()->push_back(
267 createTempAssignment(node->getCondition()->deepCopy()));
268 }
269
270 // Create "while(s0) { whileLoopBody }"
Corentin Wallez1212bca2016-11-23 13:44:05 -0500271 TIntermLoop *whileLoop = new TIntermLoop(
Corentin Wallez36fd1002016-12-08 11:30:44 -0500272 ELoopWhile, nullptr, createTempSymbol(conditionInitializer->getType()), nullptr,
Corentin Wallez1212bca2016-11-23 13:44:05 -0500273 whileLoopBody);
274 loopScope->getSequence()->push_back(whileLoop);
Olli Etuaho1d9dcc22017-01-19 11:25:32 +0000275 queueReplacement(node, loopScope, OriginalNode::IS_DROPPED);
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300276 }
277 }
278
Corentin Wallez1212bca2016-11-23 13:44:05 -0500279 mInsideLoopInitConditionOrExpression = false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300280
281 if (!mFoundLoopToChange && node->getBody())
282 node->getBody()->traverse(this);
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300283}
284
285} // namespace
286
287void SimplifyLoopConditions(TIntermNode *root,
288 unsigned int conditionsToSimplifyMask,
289 unsigned int *temporaryIndex,
290 const TSymbolTable &symbolTable,
291 int shaderVersion)
292{
293 SimplifyLoopConditionsTraverser traverser(conditionsToSimplifyMask, symbolTable, shaderVersion);
294 ASSERT(temporaryIndex != nullptr);
295 traverser.useTemporaryIndex(temporaryIndex);
296 // Process one loop at a time, and reset the traverser between iterations.
297 do
298 {
299 traverser.nextIteration();
300 root->traverse(&traverser);
301 if (traverser.foundLoopToChange())
302 traverser.updateTree();
303 } while (traverser.foundLoopToChange());
304}
Jamie Madill45bcc782016-11-07 13:58:48 -0500305
306} // namespace sh