blob: 970404683963bc7d7e69a61969901f21d2e1d2f3 [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 Etuaho4dd06d52017-07-05 12:41:06 +0300154 nextTemporaryId();
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; }
165 TIntermSequence tempInitSeq;
166 tempInitSeq.push_back(createTempInitDeclaration(node->getCondition()->deepCopy()));
167 insertStatementsInParentBlock(tempInitSeq);
168
169 TIntermBlock *newBody = new TIntermBlock();
170 if (node->getBody())
171 {
172 newBody->getSequence()->push_back(node->getBody());
173 }
174 newBody->getSequence()->push_back(
175 createTempAssignment(node->getCondition()->deepCopy()));
176
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);
181 node->setCondition(createTempSymbol(node->getCondition()->getType()));
182 }
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);
195 TIntermSequence tempInitSeq;
Olli Etuaho3ec75682017-07-05 17:02:55 +0300196 tempInitSeq.push_back(createTempInitDeclaration(CreateBoolNode(true)));
Corentin Wallez1212bca2016-11-23 13:44:05 -0500197 insertStatementsInParentBlock(tempInitSeq);
198
199 TIntermBlock *newBody = new TIntermBlock();
200 if (node->getBody())
201 {
202 newBody->getSequence()->push_back(node->getBody());
203 }
204 newBody->getSequence()->push_back(
205 createTempAssignment(node->getCondition()->deepCopy()));
206
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);
211 node->setCondition(createTempSymbol(node->getCondition()->getType()));
212 }
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 }
247 loopScopeSequence->push_back(createTempInitDeclaration(conditionInitializer));
248
249 // Insert "{ body; }" in the while loop
Corentin Wallez1212bca2016-11-23 13:44:05 -0500250 TIntermBlock *whileLoopBody = new TIntermBlock();
251 if (node->getBody())
252 {
253 whileLoopBody->getSequence()->push_back(node->getBody());
254 }
Corentin Wallez36fd1002016-12-08 11:30:44 -0500255 // Insert "exprB;" in the while loop
Corentin Wallez1212bca2016-11-23 13:44:05 -0500256 if (node->getExpression())
257 {
258 whileLoopBody->getSequence()->push_back(node->getExpression());
259 }
Corentin Wallez36fd1002016-12-08 11:30:44 -0500260 // Insert "s0 = expr;" in the while loop
261 if (node->getCondition())
262 {
263 whileLoopBody->getSequence()->push_back(
264 createTempAssignment(node->getCondition()->deepCopy()));
265 }
266
267 // Create "while(s0) { whileLoopBody }"
Corentin Wallez1212bca2016-11-23 13:44:05 -0500268 TIntermLoop *whileLoop = new TIntermLoop(
Corentin Wallez36fd1002016-12-08 11:30:44 -0500269 ELoopWhile, nullptr, createTempSymbol(conditionInitializer->getType()), nullptr,
Corentin Wallez1212bca2016-11-23 13:44:05 -0500270 whileLoopBody);
271 loopScope->getSequence()->push_back(whileLoop);
Olli Etuahoea39a222017-07-06 12:47:59 +0300272 queueReplacement(loopScope, OriginalNode::IS_DROPPED);
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300273
274 // After this the old body node will be traversed and loops inside it may be
275 // transformed. This is fine, since the old body node will still be in the AST after the
276 // transformation that's queued here, and transforming loops inside it doesn't need to
277 // know the exact post-transform path to it.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300278 }
279 }
280
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300281 mFoundLoopToChange = false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300282
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300283 // We traverse the body of the loop even if the loop is transformed.
284 if (node->getBody())
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300285 node->getBody()->traverse(this);
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300286}
287
288} // namespace
289
290void SimplifyLoopConditions(TIntermNode *root,
291 unsigned int conditionsToSimplifyMask,
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300292 TSymbolTable *symbolTable,
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300293 int shaderVersion)
294{
295 SimplifyLoopConditionsTraverser traverser(conditionsToSimplifyMask, symbolTable, shaderVersion);
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