blob: 169ad9b2b84e488e9a54a55b35fb82d34393572f [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
32 bool visitBinary(Visit visit, TIntermBinary *node) override;
33 bool visitAggregate(Visit visit, TIntermAggregate *node) override;
Olli Etuahod0bad2c2016-09-09 18:01:16 +030034 bool visitTernary(Visit visit, TIntermTernary *node) override;
Corentin Wallez1212bca2016-11-23 13:44:05 -050035 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030036
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030037 bool foundLoopToChange() const { return mFoundLoopToChange; }
38
39 protected:
Olli Etuaho9676d1a2017-05-16 11:29:24 +030040 // Marked to true once an operation that needs to be hoisted out of a loop expression has been
41 // found.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030042 bool mFoundLoopToChange;
Corentin Wallez1212bca2016-11-23 13:44:05 -050043 bool mInsideLoopInitConditionOrExpression;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030044 IntermNodePatternMatcher mConditionsToSimplify;
45};
46
47SimplifyLoopConditionsTraverser::SimplifyLoopConditionsTraverser(
48 unsigned int conditionsToSimplifyMask,
Olli Etuahoa5e693a2017-07-13 16:07:26 +030049 TSymbolTable *symbolTable,
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030050 int shaderVersion)
51 : TLValueTrackingTraverser(true, false, false, symbolTable, shaderVersion),
52 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
64bool SimplifyLoopConditionsTraverser::visitBinary(Visit visit, TIntermBinary *node)
65{
Corentin Wallez1212bca2016-11-23 13:44:05 -050066 if (!mInsideLoopInitConditionOrExpression)
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030067 return false;
68
Olli Etuaho9676d1a2017-05-16 11:29:24 +030069 if (mFoundLoopToChange)
70 return false; // Already decided to change this loop.
71
Jamie Madill666f65a2016-08-26 01:34:37 +000072 mFoundLoopToChange = mConditionsToSimplify.match(node, getParentNode(), isLValueRequiredHere());
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030073 return !mFoundLoopToChange;
74}
75
76bool SimplifyLoopConditionsTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
77{
Corentin Wallez1212bca2016-11-23 13:44:05 -050078 if (!mInsideLoopInitConditionOrExpression)
Olli Etuaho336b1472016-10-05 16:37:55 +010079 return false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030080
Olli Etuaho9676d1a2017-05-16 11:29:24 +030081 if (mFoundLoopToChange)
82 return false; // Already decided to change this loop.
83
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030084 mFoundLoopToChange = mConditionsToSimplify.match(node, getParentNode());
85 return !mFoundLoopToChange;
86}
87
Olli Etuahod0bad2c2016-09-09 18:01:16 +030088bool SimplifyLoopConditionsTraverser::visitTernary(Visit visit, TIntermTernary *node)
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030089{
Corentin Wallez1212bca2016-11-23 13:44:05 -050090 if (!mInsideLoopInitConditionOrExpression)
91 return false;
92
Olli Etuaho9676d1a2017-05-16 11:29:24 +030093 if (mFoundLoopToChange)
94 return false; // Already decided to change this loop.
95
Corentin Wallez1212bca2016-11-23 13:44:05 -050096 mFoundLoopToChange = mConditionsToSimplify.match(node);
97 return !mFoundLoopToChange;
98}
99
100bool SimplifyLoopConditionsTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
101{
Corentin Wallez1212bca2016-11-23 13:44:05 -0500102 if (!mInsideLoopInitConditionOrExpression)
Olli Etuahod0bad2c2016-09-09 18:01:16 +0300103 return false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300104
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300105 if (mFoundLoopToChange)
106 return false; // Already decided to change this loop.
107
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300108 mFoundLoopToChange = mConditionsToSimplify.match(node);
109 return !mFoundLoopToChange;
110}
111
112void SimplifyLoopConditionsTraverser::traverseLoop(TIntermLoop *node)
113{
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300114 // Mark that we're inside a loop condition or expression, and determine if the loop needs to be
115 // transformed.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300116
Olli Etuaho1d9dcc22017-01-19 11:25:32 +0000117 ScopedNodeInTraversalPath addToPath(this, node);
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300118
Corentin Wallez1212bca2016-11-23 13:44:05 -0500119 mInsideLoopInitConditionOrExpression = true;
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300120 mFoundLoopToChange = false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300121
Corentin Wallez1212bca2016-11-23 13:44:05 -0500122 if (!mFoundLoopToChange && node->getInit())
123 {
124 node->getInit()->traverse(this);
125 }
126
127 if (!mFoundLoopToChange && node->getCondition())
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300128 {
129 node->getCondition()->traverse(this);
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300130 }
131
132 if (!mFoundLoopToChange && node->getExpression())
133 {
134 node->getExpression()->traverse(this);
Corentin Wallez1212bca2016-11-23 13:44:05 -0500135 }
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300136
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300137 mInsideLoopInitConditionOrExpression = false;
138
Corentin Wallez1212bca2016-11-23 13:44:05 -0500139 if (mFoundLoopToChange)
140 {
Olli Etuaho4dd06d52017-07-05 12:41:06 +0300141 nextTemporaryId();
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300142
Corentin Wallez1212bca2016-11-23 13:44:05 -0500143 // Replace the loop condition with a boolean variable that's updated on each iteration.
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300144 TLoopType loopType = node->getType();
Corentin Wallez1212bca2016-11-23 13:44:05 -0500145 if (loopType == ELoopWhile)
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300146 {
Corentin Wallez1212bca2016-11-23 13:44:05 -0500147 // Transform:
148 // while (expr) { body; }
149 // into
150 // bool s0 = expr;
151 // while (s0) { { body; } s0 = expr; }
152 TIntermSequence tempInitSeq;
153 tempInitSeq.push_back(createTempInitDeclaration(node->getCondition()->deepCopy()));
154 insertStatementsInParentBlock(tempInitSeq);
155
156 TIntermBlock *newBody = new TIntermBlock();
157 if (node->getBody())
158 {
159 newBody->getSequence()->push_back(node->getBody());
160 }
161 newBody->getSequence()->push_back(
162 createTempAssignment(node->getCondition()->deepCopy()));
163
164 // Can't use queueReplacement to replace old body, since it may have been nullptr.
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300165 // It's safe to do the replacements in place here - the new body will still be
166 // traversed, but that won't create any problems.
Corentin Wallez1212bca2016-11-23 13:44:05 -0500167 node->setBody(newBody);
168 node->setCondition(createTempSymbol(node->getCondition()->getType()));
169 }
170 else if (loopType == ELoopDoWhile)
171 {
172 // Transform:
173 // do {
174 // body;
175 // } while (expr);
176 // into
177 // bool s0 = true;
178 // do {
179 // { body; }
180 // s0 = expr;
181 // } while (s0);
182 TIntermSequence tempInitSeq;
Olli Etuaho3ec75682017-07-05 17:02:55 +0300183 tempInitSeq.push_back(createTempInitDeclaration(CreateBoolNode(true)));
Corentin Wallez1212bca2016-11-23 13:44:05 -0500184 insertStatementsInParentBlock(tempInitSeq);
185
186 TIntermBlock *newBody = new TIntermBlock();
187 if (node->getBody())
188 {
189 newBody->getSequence()->push_back(node->getBody());
190 }
191 newBody->getSequence()->push_back(
192 createTempAssignment(node->getCondition()->deepCopy()));
193
194 // Can't use queueReplacement to replace old body, since it may have been nullptr.
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300195 // It's safe to do the replacements in place here - the new body will still be
196 // traversed, but that won't create any problems.
Corentin Wallez1212bca2016-11-23 13:44:05 -0500197 node->setBody(newBody);
198 node->setCondition(createTempSymbol(node->getCondition()->getType()));
199 }
200 else if (loopType == ELoopFor)
201 {
202 // Move the loop condition inside the loop.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300203 // Transform:
204 // for (init; expr; exprB) { body; }
205 // into
Corentin Wallez1212bca2016-11-23 13:44:05 -0500206 // {
207 // init;
208 // bool s0 = expr;
Corentin Wallez36fd1002016-12-08 11:30:44 -0500209 // while (s0) {
210 // { body; }
211 // exprB;
212 // s0 = expr;
213 // }
Corentin Wallez1212bca2016-11-23 13:44:05 -0500214 // }
Corentin Wallez36fd1002016-12-08 11:30:44 -0500215 TIntermBlock *loopScope = new TIntermBlock();
216 TIntermSequence *loopScopeSequence = loopScope->getSequence();
217
218 // Insert "init;"
Corentin Wallez1212bca2016-11-23 13:44:05 -0500219 if (node->getInit())
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300220 {
Corentin Wallez36fd1002016-12-08 11:30:44 -0500221 loopScopeSequence->push_back(node->getInit());
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300222 }
Corentin Wallez1212bca2016-11-23 13:44:05 -0500223
Corentin Wallez36fd1002016-12-08 11:30:44 -0500224 // Insert "bool s0 = expr;" if applicable, "bool s0 = true;" otherwise
225 TIntermTyped *conditionInitializer = nullptr;
226 if (node->getCondition())
227 {
228 conditionInitializer = node->getCondition()->deepCopy();
229 }
230 else
231 {
Olli Etuaho3ec75682017-07-05 17:02:55 +0300232 conditionInitializer = CreateBoolNode(true);
Corentin Wallez36fd1002016-12-08 11:30:44 -0500233 }
234 loopScopeSequence->push_back(createTempInitDeclaration(conditionInitializer));
235
236 // Insert "{ body; }" in the while loop
Corentin Wallez1212bca2016-11-23 13:44:05 -0500237 TIntermBlock *whileLoopBody = new TIntermBlock();
238 if (node->getBody())
239 {
240 whileLoopBody->getSequence()->push_back(node->getBody());
241 }
Corentin Wallez36fd1002016-12-08 11:30:44 -0500242 // Insert "exprB;" in the while loop
Corentin Wallez1212bca2016-11-23 13:44:05 -0500243 if (node->getExpression())
244 {
245 whileLoopBody->getSequence()->push_back(node->getExpression());
246 }
Corentin Wallez36fd1002016-12-08 11:30:44 -0500247 // Insert "s0 = expr;" in the while loop
248 if (node->getCondition())
249 {
250 whileLoopBody->getSequence()->push_back(
251 createTempAssignment(node->getCondition()->deepCopy()));
252 }
253
254 // Create "while(s0) { whileLoopBody }"
Corentin Wallez1212bca2016-11-23 13:44:05 -0500255 TIntermLoop *whileLoop = new TIntermLoop(
Corentin Wallez36fd1002016-12-08 11:30:44 -0500256 ELoopWhile, nullptr, createTempSymbol(conditionInitializer->getType()), nullptr,
Corentin Wallez1212bca2016-11-23 13:44:05 -0500257 whileLoopBody);
258 loopScope->getSequence()->push_back(whileLoop);
Olli Etuahoea39a222017-07-06 12:47:59 +0300259 queueReplacement(loopScope, OriginalNode::IS_DROPPED);
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300260
261 // After this the old body node will be traversed and loops inside it may be
262 // transformed. This is fine, since the old body node will still be in the AST after the
263 // transformation that's queued here, and transforming loops inside it doesn't need to
264 // know the exact post-transform path to it.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300265 }
266 }
267
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300268 mFoundLoopToChange = false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300269
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300270 // We traverse the body of the loop even if the loop is transformed.
271 if (node->getBody())
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300272 node->getBody()->traverse(this);
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300273}
274
275} // namespace
276
277void SimplifyLoopConditions(TIntermNode *root,
278 unsigned int conditionsToSimplifyMask,
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300279 TSymbolTable *symbolTable,
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300280 int shaderVersion)
281{
282 SimplifyLoopConditionsTraverser traverser(conditionsToSimplifyMask, symbolTable, shaderVersion);
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300283 root->traverse(&traverser);
284 traverser.updateTree();
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300285}
Jamie Madill45bcc782016-11-07 13:58:48 -0500286
287} // namespace sh