blob: acbb50c6328f9c17afbc91bb615523b58c1d9212 [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
Olli Etuahob60d30f2018-01-16 12:31:06 +020013#include "compiler/translator/StaticType.h"
Olli Etuahoc26214d2018-03-16 10:43:11 +020014#include "compiler/translator/tree_util/IntermNodePatternMatcher.h"
15#include "compiler/translator/tree_util/IntermNode_util.h"
16#include "compiler/translator/tree_util/IntermTraverse.h"
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030017
Jamie Madill45bcc782016-11-07 13:58:48 -050018namespace sh
19{
20
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030021namespace
22{
23
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030024class SimplifyLoopConditionsTraverser : public TLValueTrackingTraverser
25{
26 public:
27 SimplifyLoopConditionsTraverser(unsigned int conditionsToSimplifyMask,
Olli Etuaho68981eb2018-01-23 17:46:12 +020028 TSymbolTable *symbolTable);
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030029
30 void traverseLoop(TIntermLoop *node) override;
31
Olli Etuahobb5a7e22017-08-30 13:03:12 +030032 bool visitUnary(Visit visit, TIntermUnary *node) override;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030033 bool visitBinary(Visit visit, TIntermBinary *node) override;
34 bool visitAggregate(Visit visit, TIntermAggregate *node) override;
Olli Etuahod0bad2c2016-09-09 18:01:16 +030035 bool visitTernary(Visit visit, TIntermTernary *node) override;
Corentin Wallez1212bca2016-11-23 13:44:05 -050036 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030037
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030038 bool foundLoopToChange() const { return mFoundLoopToChange; }
39
40 protected:
Olli Etuaho9676d1a2017-05-16 11:29:24 +030041 // Marked to true once an operation that needs to be hoisted out of a loop expression has been
42 // found.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030043 bool mFoundLoopToChange;
Corentin Wallez1212bca2016-11-23 13:44:05 -050044 bool mInsideLoopInitConditionOrExpression;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030045 IntermNodePatternMatcher mConditionsToSimplify;
46};
47
48SimplifyLoopConditionsTraverser::SimplifyLoopConditionsTraverser(
49 unsigned int conditionsToSimplifyMask,
Olli Etuaho68981eb2018-01-23 17:46:12 +020050 TSymbolTable *symbolTable)
51 : TLValueTrackingTraverser(true, false, false, symbolTable),
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030052 mFoundLoopToChange(false),
Corentin Wallez1212bca2016-11-23 13:44:05 -050053 mInsideLoopInitConditionOrExpression(false),
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030054 mConditionsToSimplify(conditionsToSimplifyMask)
55{
56}
57
Olli Etuaho9676d1a2017-05-16 11:29:24 +030058// If we're inside a loop initialization, condition, or expression, we check for expressions that
59// should be moved out of the loop condition or expression. If one is found, the loop is
60// transformed.
61// If we're not inside loop initialization, condition, or expression, we only need to traverse nodes
62// that may contain loops.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030063
Olli Etuahobb5a7e22017-08-30 13:03:12 +030064bool SimplifyLoopConditionsTraverser::visitUnary(Visit visit, TIntermUnary *node)
65{
66 if (!mInsideLoopInitConditionOrExpression)
67 return false;
68
69 if (mFoundLoopToChange)
70 return false; // Already decided to change this loop.
71
72 mFoundLoopToChange = mConditionsToSimplify.match(node);
73 return !mFoundLoopToChange;
74}
75
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030076bool SimplifyLoopConditionsTraverser::visitBinary(Visit visit, TIntermBinary *node)
77{
Corentin Wallez1212bca2016-11-23 13:44:05 -050078 if (!mInsideLoopInitConditionOrExpression)
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030079 return false;
80
Olli Etuaho9676d1a2017-05-16 11:29:24 +030081 if (mFoundLoopToChange)
82 return false; // Already decided to change this loop.
83
Jamie Madill666f65a2016-08-26 01:34:37 +000084 mFoundLoopToChange = mConditionsToSimplify.match(node, getParentNode(), isLValueRequiredHere());
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030085 return !mFoundLoopToChange;
86}
87
88bool SimplifyLoopConditionsTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
89{
Corentin Wallez1212bca2016-11-23 13:44:05 -050090 if (!mInsideLoopInitConditionOrExpression)
Olli Etuaho336b1472016-10-05 16:37:55 +010091 return false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030092
Olli Etuaho9676d1a2017-05-16 11:29:24 +030093 if (mFoundLoopToChange)
94 return false; // Already decided to change this loop.
95
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030096 mFoundLoopToChange = mConditionsToSimplify.match(node, getParentNode());
97 return !mFoundLoopToChange;
98}
99
Olli Etuahod0bad2c2016-09-09 18:01:16 +0300100bool SimplifyLoopConditionsTraverser::visitTernary(Visit visit, TIntermTernary *node)
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300101{
Corentin Wallez1212bca2016-11-23 13:44:05 -0500102 if (!mInsideLoopInitConditionOrExpression)
103 return false;
104
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300105 if (mFoundLoopToChange)
106 return false; // Already decided to change this loop.
107
Corentin Wallez1212bca2016-11-23 13:44:05 -0500108 mFoundLoopToChange = mConditionsToSimplify.match(node);
109 return !mFoundLoopToChange;
110}
111
112bool SimplifyLoopConditionsTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
113{
Corentin Wallez1212bca2016-11-23 13:44:05 -0500114 if (!mInsideLoopInitConditionOrExpression)
Olli Etuahod0bad2c2016-09-09 18:01:16 +0300115 return false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300116
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300117 if (mFoundLoopToChange)
118 return false; // Already decided to change this loop.
119
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300120 mFoundLoopToChange = mConditionsToSimplify.match(node);
121 return !mFoundLoopToChange;
122}
123
124void SimplifyLoopConditionsTraverser::traverseLoop(TIntermLoop *node)
125{
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300126 // Mark that we're inside a loop condition or expression, and determine if the loop needs to be
127 // transformed.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300128
Olli Etuaho1d9dcc22017-01-19 11:25:32 +0000129 ScopedNodeInTraversalPath addToPath(this, node);
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300130
Corentin Wallez1212bca2016-11-23 13:44:05 -0500131 mInsideLoopInitConditionOrExpression = true;
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300132 mFoundLoopToChange = false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300133
Corentin Wallez1212bca2016-11-23 13:44:05 -0500134 if (!mFoundLoopToChange && node->getInit())
135 {
136 node->getInit()->traverse(this);
137 }
138
139 if (!mFoundLoopToChange && node->getCondition())
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300140 {
141 node->getCondition()->traverse(this);
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300142 }
143
144 if (!mFoundLoopToChange && node->getExpression())
145 {
146 node->getExpression()->traverse(this);
Corentin Wallez1212bca2016-11-23 13:44:05 -0500147 }
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300148
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300149 mInsideLoopInitConditionOrExpression = false;
150
Corentin Wallez1212bca2016-11-23 13:44:05 -0500151 if (mFoundLoopToChange)
152 {
Olli Etuahob60d30f2018-01-16 12:31:06 +0200153 const TType *boolType = StaticType::Get<EbtBool, EbpUndefined, EvqTemporary, 1, 1>();
Olli Etuaho195be942017-12-04 23:40:14 +0200154 TVariable *conditionVariable = CreateTempVariable(mSymbolTable, boolType);
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300155
Corentin Wallez1212bca2016-11-23 13:44:05 -0500156 // Replace the loop condition with a boolean variable that's updated on each iteration.
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300157 TLoopType loopType = node->getType();
Corentin Wallez1212bca2016-11-23 13:44:05 -0500158 if (loopType == ELoopWhile)
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300159 {
Corentin Wallez1212bca2016-11-23 13:44:05 -0500160 // Transform:
161 // while (expr) { body; }
162 // into
163 // bool s0 = expr;
164 // while (s0) { { body; } s0 = expr; }
Olli Etuaho195be942017-12-04 23:40:14 +0200165 TIntermDeclaration *tempInitDeclaration =
166 CreateTempInitDeclarationNode(conditionVariable, node->getCondition()->deepCopy());
167 insertStatementInParentBlock(tempInitDeclaration);
Corentin Wallez1212bca2016-11-23 13:44:05 -0500168
169 TIntermBlock *newBody = new TIntermBlock();
170 if (node->getBody())
171 {
172 newBody->getSequence()->push_back(node->getBody());
173 }
174 newBody->getSequence()->push_back(
Olli Etuaho195be942017-12-04 23:40:14 +0200175 CreateTempAssignmentNode(conditionVariable, node->getCondition()->deepCopy()));
Corentin Wallez1212bca2016-11-23 13:44:05 -0500176
177 // Can't use queueReplacement to replace old body, since it may have been nullptr.
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300178 // It's safe to do the replacements in place here - the new body will still be
179 // traversed, but that won't create any problems.
Corentin Wallez1212bca2016-11-23 13:44:05 -0500180 node->setBody(newBody);
Olli Etuaho195be942017-12-04 23:40:14 +0200181 node->setCondition(CreateTempSymbolNode(conditionVariable));
Corentin Wallez1212bca2016-11-23 13:44:05 -0500182 }
183 else if (loopType == ELoopDoWhile)
184 {
185 // Transform:
186 // do {
187 // body;
188 // } while (expr);
189 // into
190 // bool s0 = true;
191 // do {
192 // { body; }
193 // s0 = expr;
194 // } while (s0);
Olli Etuaho195be942017-12-04 23:40:14 +0200195 TIntermDeclaration *tempInitDeclaration =
196 CreateTempInitDeclarationNode(conditionVariable, CreateBoolNode(true));
197 insertStatementInParentBlock(tempInitDeclaration);
Corentin Wallez1212bca2016-11-23 13:44:05 -0500198
199 TIntermBlock *newBody = new TIntermBlock();
200 if (node->getBody())
201 {
202 newBody->getSequence()->push_back(node->getBody());
203 }
204 newBody->getSequence()->push_back(
Olli Etuaho195be942017-12-04 23:40:14 +0200205 CreateTempAssignmentNode(conditionVariable, node->getCondition()->deepCopy()));
Corentin Wallez1212bca2016-11-23 13:44:05 -0500206
207 // Can't use queueReplacement to replace old body, since it may have been nullptr.
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300208 // It's safe to do the replacements in place here - the new body will still be
209 // traversed, but that won't create any problems.
Corentin Wallez1212bca2016-11-23 13:44:05 -0500210 node->setBody(newBody);
Olli Etuaho195be942017-12-04 23:40:14 +0200211 node->setCondition(CreateTempSymbolNode(conditionVariable));
Corentin Wallez1212bca2016-11-23 13:44:05 -0500212 }
213 else if (loopType == ELoopFor)
214 {
215 // Move the loop condition inside the loop.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300216 // Transform:
217 // for (init; expr; exprB) { body; }
218 // into
Corentin Wallez1212bca2016-11-23 13:44:05 -0500219 // {
220 // init;
221 // bool s0 = expr;
Corentin Wallez36fd1002016-12-08 11:30:44 -0500222 // while (s0) {
223 // { body; }
224 // exprB;
225 // s0 = expr;
226 // }
Corentin Wallez1212bca2016-11-23 13:44:05 -0500227 // }
Corentin Wallez36fd1002016-12-08 11:30:44 -0500228 TIntermBlock *loopScope = new TIntermBlock();
229 TIntermSequence *loopScopeSequence = loopScope->getSequence();
230
231 // Insert "init;"
Corentin Wallez1212bca2016-11-23 13:44:05 -0500232 if (node->getInit())
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300233 {
Corentin Wallez36fd1002016-12-08 11:30:44 -0500234 loopScopeSequence->push_back(node->getInit());
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300235 }
Corentin Wallez1212bca2016-11-23 13:44:05 -0500236
Corentin Wallez36fd1002016-12-08 11:30:44 -0500237 // Insert "bool s0 = expr;" if applicable, "bool s0 = true;" otherwise
238 TIntermTyped *conditionInitializer = nullptr;
239 if (node->getCondition())
240 {
241 conditionInitializer = node->getCondition()->deepCopy();
242 }
243 else
244 {
Olli Etuaho3ec75682017-07-05 17:02:55 +0300245 conditionInitializer = CreateBoolNode(true);
Corentin Wallez36fd1002016-12-08 11:30:44 -0500246 }
Olli Etuaho195be942017-12-04 23:40:14 +0200247 loopScopeSequence->push_back(
248 CreateTempInitDeclarationNode(conditionVariable, conditionInitializer));
Corentin Wallez36fd1002016-12-08 11:30:44 -0500249
250 // Insert "{ body; }" in the while loop
Corentin Wallez1212bca2016-11-23 13:44:05 -0500251 TIntermBlock *whileLoopBody = new TIntermBlock();
252 if (node->getBody())
253 {
254 whileLoopBody->getSequence()->push_back(node->getBody());
255 }
Corentin Wallez36fd1002016-12-08 11:30:44 -0500256 // Insert "exprB;" in the while loop
Corentin Wallez1212bca2016-11-23 13:44:05 -0500257 if (node->getExpression())
258 {
259 whileLoopBody->getSequence()->push_back(node->getExpression());
260 }
Corentin Wallez36fd1002016-12-08 11:30:44 -0500261 // Insert "s0 = expr;" in the while loop
262 if (node->getCondition())
263 {
264 whileLoopBody->getSequence()->push_back(
Olli Etuaho195be942017-12-04 23:40:14 +0200265 CreateTempAssignmentNode(conditionVariable, node->getCondition()->deepCopy()));
Corentin Wallez36fd1002016-12-08 11:30:44 -0500266 }
267
268 // Create "while(s0) { whileLoopBody }"
Olli Etuaho195be942017-12-04 23:40:14 +0200269 TIntermLoop *whileLoop =
270 new TIntermLoop(ELoopWhile, nullptr, CreateTempSymbolNode(conditionVariable),
271 nullptr, whileLoopBody);
Corentin Wallez1212bca2016-11-23 13:44:05 -0500272 loopScope->getSequence()->push_back(whileLoop);
Olli Etuahoea39a222017-07-06 12:47:59 +0300273 queueReplacement(loopScope, OriginalNode::IS_DROPPED);
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300274
275 // After this the old body node will be traversed and loops inside it may be
276 // transformed. This is fine, since the old body node will still be in the AST after the
277 // transformation that's queued here, and transforming loops inside it doesn't need to
278 // know the exact post-transform path to it.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300279 }
280 }
281
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300282 mFoundLoopToChange = false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300283
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300284 // We traverse the body of the loop even if the loop is transformed.
285 if (node->getBody())
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300286 node->getBody()->traverse(this);
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300287}
288
289} // namespace
290
291void SimplifyLoopConditions(TIntermNode *root,
292 unsigned int conditionsToSimplifyMask,
Olli Etuaho68981eb2018-01-23 17:46:12 +0200293 TSymbolTable *symbolTable)
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300294{
Olli Etuaho68981eb2018-01-23 17:46:12 +0200295 SimplifyLoopConditionsTraverser traverser(conditionsToSimplifyMask, symbolTable);
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300296 root->traverse(&traverser);
297 traverser.updateTree();
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300298}
Jamie Madill45bcc782016-11-07 13:58:48 -0500299
300} // namespace sh