blob: c8f504a9fd0408126265966f9d6996c84027a2e7 [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 Etuaho3cbb27a2016-07-14 11:55:48 +030013#include "compiler/translator/IntermNodePatternMatcher.h"
Olli Etuaho3ec75682017-07-05 17:02:55 +030014#include "compiler/translator/IntermNode_util.h"
Olli Etuahocccf2b02017-07-05 14:50:54 +030015#include "compiler/translator/IntermTraverse.h"
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030016
Jamie Madill45bcc782016-11-07 13:58:48 -050017namespace sh
18{
19
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030020namespace
21{
22
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030023class SimplifyLoopConditionsTraverser : public TLValueTrackingTraverser
24{
25 public:
26 SimplifyLoopConditionsTraverser(unsigned int conditionsToSimplifyMask,
Olli Etuahoa5e693a2017-07-13 16:07:26 +030027 TSymbolTable *symbolTable,
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030028 int shaderVersion);
29
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 Etuahoa5e693a2017-07-13 16:07:26 +030050 TSymbolTable *symbolTable,
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030051 int shaderVersion)
52 : TLValueTrackingTraverser(true, false, false, symbolTable, shaderVersion),
53 mFoundLoopToChange(false),
Corentin Wallez1212bca2016-11-23 13:44:05 -050054 mInsideLoopInitConditionOrExpression(false),
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030055 mConditionsToSimplify(conditionsToSimplifyMask)
56{
57}
58
Olli Etuaho9676d1a2017-05-16 11:29:24 +030059// If we're inside a loop initialization, condition, or expression, we check for expressions that
60// should be moved out of the loop condition or expression. If one is found, the loop is
61// transformed.
62// If we're not inside loop initialization, condition, or expression, we only need to traverse nodes
63// that may contain loops.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030064
Olli Etuahobb5a7e22017-08-30 13:03:12 +030065bool SimplifyLoopConditionsTraverser::visitUnary(Visit visit, TIntermUnary *node)
66{
67 if (!mInsideLoopInitConditionOrExpression)
68 return false;
69
70 if (mFoundLoopToChange)
71 return false; // Already decided to change this loop.
72
73 mFoundLoopToChange = mConditionsToSimplify.match(node);
74 return !mFoundLoopToChange;
75}
76
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030077bool SimplifyLoopConditionsTraverser::visitBinary(Visit visit, TIntermBinary *node)
78{
Corentin Wallez1212bca2016-11-23 13:44:05 -050079 if (!mInsideLoopInitConditionOrExpression)
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030080 return false;
81
Olli Etuaho9676d1a2017-05-16 11:29:24 +030082 if (mFoundLoopToChange)
83 return false; // Already decided to change this loop.
84
Jamie Madill666f65a2016-08-26 01:34:37 +000085 mFoundLoopToChange = mConditionsToSimplify.match(node, getParentNode(), isLValueRequiredHere());
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030086 return !mFoundLoopToChange;
87}
88
89bool SimplifyLoopConditionsTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
90{
Corentin Wallez1212bca2016-11-23 13:44:05 -050091 if (!mInsideLoopInitConditionOrExpression)
Olli Etuaho336b1472016-10-05 16:37:55 +010092 return false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030093
Olli Etuaho9676d1a2017-05-16 11:29:24 +030094 if (mFoundLoopToChange)
95 return false; // Already decided to change this loop.
96
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030097 mFoundLoopToChange = mConditionsToSimplify.match(node, getParentNode());
98 return !mFoundLoopToChange;
99}
100
Olli Etuahod0bad2c2016-09-09 18:01:16 +0300101bool SimplifyLoopConditionsTraverser::visitTernary(Visit visit, TIntermTernary *node)
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300102{
Corentin Wallez1212bca2016-11-23 13:44:05 -0500103 if (!mInsideLoopInitConditionOrExpression)
104 return false;
105
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300106 if (mFoundLoopToChange)
107 return false; // Already decided to change this loop.
108
Corentin Wallez1212bca2016-11-23 13:44:05 -0500109 mFoundLoopToChange = mConditionsToSimplify.match(node);
110 return !mFoundLoopToChange;
111}
112
113bool SimplifyLoopConditionsTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
114{
Corentin Wallez1212bca2016-11-23 13:44:05 -0500115 if (!mInsideLoopInitConditionOrExpression)
Olli Etuahod0bad2c2016-09-09 18:01:16 +0300116 return false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300117
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300118 if (mFoundLoopToChange)
119 return false; // Already decided to change this loop.
120
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300121 mFoundLoopToChange = mConditionsToSimplify.match(node);
122 return !mFoundLoopToChange;
123}
124
125void SimplifyLoopConditionsTraverser::traverseLoop(TIntermLoop *node)
126{
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300127 // Mark that we're inside a loop condition or expression, and determine if the loop needs to be
128 // transformed.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300129
Olli Etuaho1d9dcc22017-01-19 11:25:32 +0000130 ScopedNodeInTraversalPath addToPath(this, node);
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300131
Corentin Wallez1212bca2016-11-23 13:44:05 -0500132 mInsideLoopInitConditionOrExpression = true;
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300133 mFoundLoopToChange = false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300134
Corentin Wallez1212bca2016-11-23 13:44:05 -0500135 if (!mFoundLoopToChange && node->getInit())
136 {
137 node->getInit()->traverse(this);
138 }
139
140 if (!mFoundLoopToChange && node->getCondition())
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300141 {
142 node->getCondition()->traverse(this);
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300143 }
144
145 if (!mFoundLoopToChange && node->getExpression())
146 {
147 node->getExpression()->traverse(this);
Corentin Wallez1212bca2016-11-23 13:44:05 -0500148 }
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300149
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300150 mInsideLoopInitConditionOrExpression = false;
151
Corentin Wallez1212bca2016-11-23 13:44:05 -0500152 if (mFoundLoopToChange)
153 {
Olli Etuaho195be942017-12-04 23:40:14 +0200154 TType boolType(EbtBool, EbpUndefined, EvqTemporary);
155 TVariable *conditionVariable = CreateTempVariable(mSymbolTable, boolType);
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300156
Corentin Wallez1212bca2016-11-23 13:44:05 -0500157 // Replace the loop condition with a boolean variable that's updated on each iteration.
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300158 TLoopType loopType = node->getType();
Corentin Wallez1212bca2016-11-23 13:44:05 -0500159 if (loopType == ELoopWhile)
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300160 {
Corentin Wallez1212bca2016-11-23 13:44:05 -0500161 // Transform:
162 // while (expr) { body; }
163 // into
164 // bool s0 = expr;
165 // while (s0) { { body; } s0 = expr; }
Olli Etuaho195be942017-12-04 23:40:14 +0200166 TIntermDeclaration *tempInitDeclaration =
167 CreateTempInitDeclarationNode(conditionVariable, node->getCondition()->deepCopy());
168 insertStatementInParentBlock(tempInitDeclaration);
Corentin Wallez1212bca2016-11-23 13:44:05 -0500169
170 TIntermBlock *newBody = new TIntermBlock();
171 if (node->getBody())
172 {
173 newBody->getSequence()->push_back(node->getBody());
174 }
175 newBody->getSequence()->push_back(
Olli Etuaho195be942017-12-04 23:40:14 +0200176 CreateTempAssignmentNode(conditionVariable, node->getCondition()->deepCopy()));
Corentin Wallez1212bca2016-11-23 13:44:05 -0500177
178 // Can't use queueReplacement to replace old body, since it may have been nullptr.
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300179 // It's safe to do the replacements in place here - the new body will still be
180 // traversed, but that won't create any problems.
Corentin Wallez1212bca2016-11-23 13:44:05 -0500181 node->setBody(newBody);
Olli Etuaho195be942017-12-04 23:40:14 +0200182 node->setCondition(CreateTempSymbolNode(conditionVariable));
Corentin Wallez1212bca2016-11-23 13:44:05 -0500183 }
184 else if (loopType == ELoopDoWhile)
185 {
186 // Transform:
187 // do {
188 // body;
189 // } while (expr);
190 // into
191 // bool s0 = true;
192 // do {
193 // { body; }
194 // s0 = expr;
195 // } while (s0);
Olli Etuaho195be942017-12-04 23:40:14 +0200196 TIntermDeclaration *tempInitDeclaration =
197 CreateTempInitDeclarationNode(conditionVariable, CreateBoolNode(true));
198 insertStatementInParentBlock(tempInitDeclaration);
Corentin Wallez1212bca2016-11-23 13:44:05 -0500199
200 TIntermBlock *newBody = new TIntermBlock();
201 if (node->getBody())
202 {
203 newBody->getSequence()->push_back(node->getBody());
204 }
205 newBody->getSequence()->push_back(
Olli Etuaho195be942017-12-04 23:40:14 +0200206 CreateTempAssignmentNode(conditionVariable, node->getCondition()->deepCopy()));
Corentin Wallez1212bca2016-11-23 13:44:05 -0500207
208 // Can't use queueReplacement to replace old body, since it may have been nullptr.
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300209 // It's safe to do the replacements in place here - the new body will still be
210 // traversed, but that won't create any problems.
Corentin Wallez1212bca2016-11-23 13:44:05 -0500211 node->setBody(newBody);
Olli Etuaho195be942017-12-04 23:40:14 +0200212 node->setCondition(CreateTempSymbolNode(conditionVariable));
Corentin Wallez1212bca2016-11-23 13:44:05 -0500213 }
214 else if (loopType == ELoopFor)
215 {
216 // Move the loop condition inside the loop.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300217 // Transform:
218 // for (init; expr; exprB) { body; }
219 // into
Corentin Wallez1212bca2016-11-23 13:44:05 -0500220 // {
221 // init;
222 // bool s0 = expr;
Corentin Wallez36fd1002016-12-08 11:30:44 -0500223 // while (s0) {
224 // { body; }
225 // exprB;
226 // s0 = expr;
227 // }
Corentin Wallez1212bca2016-11-23 13:44:05 -0500228 // }
Corentin Wallez36fd1002016-12-08 11:30:44 -0500229 TIntermBlock *loopScope = new TIntermBlock();
230 TIntermSequence *loopScopeSequence = loopScope->getSequence();
231
232 // Insert "init;"
Corentin Wallez1212bca2016-11-23 13:44:05 -0500233 if (node->getInit())
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300234 {
Corentin Wallez36fd1002016-12-08 11:30:44 -0500235 loopScopeSequence->push_back(node->getInit());
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300236 }
Corentin Wallez1212bca2016-11-23 13:44:05 -0500237
Corentin Wallez36fd1002016-12-08 11:30:44 -0500238 // Insert "bool s0 = expr;" if applicable, "bool s0 = true;" otherwise
239 TIntermTyped *conditionInitializer = nullptr;
240 if (node->getCondition())
241 {
242 conditionInitializer = node->getCondition()->deepCopy();
243 }
244 else
245 {
Olli Etuaho3ec75682017-07-05 17:02:55 +0300246 conditionInitializer = CreateBoolNode(true);
Corentin Wallez36fd1002016-12-08 11:30:44 -0500247 }
Olli Etuaho195be942017-12-04 23:40:14 +0200248 loopScopeSequence->push_back(
249 CreateTempInitDeclarationNode(conditionVariable, conditionInitializer));
Corentin Wallez36fd1002016-12-08 11:30:44 -0500250
251 // Insert "{ body; }" in the while loop
Corentin Wallez1212bca2016-11-23 13:44:05 -0500252 TIntermBlock *whileLoopBody = new TIntermBlock();
253 if (node->getBody())
254 {
255 whileLoopBody->getSequence()->push_back(node->getBody());
256 }
Corentin Wallez36fd1002016-12-08 11:30:44 -0500257 // Insert "exprB;" in the while loop
Corentin Wallez1212bca2016-11-23 13:44:05 -0500258 if (node->getExpression())
259 {
260 whileLoopBody->getSequence()->push_back(node->getExpression());
261 }
Corentin Wallez36fd1002016-12-08 11:30:44 -0500262 // Insert "s0 = expr;" in the while loop
263 if (node->getCondition())
264 {
265 whileLoopBody->getSequence()->push_back(
Olli Etuaho195be942017-12-04 23:40:14 +0200266 CreateTempAssignmentNode(conditionVariable, node->getCondition()->deepCopy()));
Corentin Wallez36fd1002016-12-08 11:30:44 -0500267 }
268
269 // Create "while(s0) { whileLoopBody }"
Olli Etuaho195be942017-12-04 23:40:14 +0200270 TIntermLoop *whileLoop =
271 new TIntermLoop(ELoopWhile, nullptr, CreateTempSymbolNode(conditionVariable),
272 nullptr, whileLoopBody);
Corentin Wallez1212bca2016-11-23 13:44:05 -0500273 loopScope->getSequence()->push_back(whileLoop);
Olli Etuahoea39a222017-07-06 12:47:59 +0300274 queueReplacement(loopScope, OriginalNode::IS_DROPPED);
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300275
276 // After this the old body node will be traversed and loops inside it may be
277 // transformed. This is fine, since the old body node will still be in the AST after the
278 // transformation that's queued here, and transforming loops inside it doesn't need to
279 // know the exact post-transform path to it.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300280 }
281 }
282
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300283 mFoundLoopToChange = false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300284
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300285 // We traverse the body of the loop even if the loop is transformed.
286 if (node->getBody())
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300287 node->getBody()->traverse(this);
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300288}
289
290} // namespace
291
292void SimplifyLoopConditions(TIntermNode *root,
293 unsigned int conditionsToSimplifyMask,
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300294 TSymbolTable *symbolTable,
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300295 int shaderVersion)
296{
297 SimplifyLoopConditionsTraverser traverser(conditionsToSimplifyMask, symbolTable, shaderVersion);
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300298 root->traverse(&traverser);
299 traverser.updateTree();
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300300}
Jamie Madill45bcc782016-11-07 13:58:48 -0500301
302} // namespace sh