blob: 4d47e053fb80d97053e042cace69770d8db70b21 [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 Etuahob60d30f2018-01-16 12:31:06 +020016#include "compiler/translator/StaticType.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 Etuahoa5e693a2017-07-13 16:07:26 +030028 TSymbolTable *symbolTable,
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030029 int shaderVersion);
30
31 void traverseLoop(TIntermLoop *node) override;
32
Olli Etuahobb5a7e22017-08-30 13:03:12 +030033 bool visitUnary(Visit visit, TIntermUnary *node) override;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030034 bool visitBinary(Visit visit, TIntermBinary *node) override;
35 bool visitAggregate(Visit visit, TIntermAggregate *node) override;
Olli Etuahod0bad2c2016-09-09 18:01:16 +030036 bool visitTernary(Visit visit, TIntermTernary *node) override;
Corentin Wallez1212bca2016-11-23 13:44:05 -050037 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030038
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030039 bool foundLoopToChange() const { return mFoundLoopToChange; }
40
41 protected:
Olli Etuaho9676d1a2017-05-16 11:29:24 +030042 // Marked to true once an operation that needs to be hoisted out of a loop expression has been
43 // found.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030044 bool mFoundLoopToChange;
Corentin Wallez1212bca2016-11-23 13:44:05 -050045 bool mInsideLoopInitConditionOrExpression;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030046 IntermNodePatternMatcher mConditionsToSimplify;
47};
48
49SimplifyLoopConditionsTraverser::SimplifyLoopConditionsTraverser(
50 unsigned int conditionsToSimplifyMask,
Olli Etuahoa5e693a2017-07-13 16:07:26 +030051 TSymbolTable *symbolTable,
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030052 int shaderVersion)
53 : TLValueTrackingTraverser(true, false, false, symbolTable, shaderVersion),
54 mFoundLoopToChange(false),
Corentin Wallez1212bca2016-11-23 13:44:05 -050055 mInsideLoopInitConditionOrExpression(false),
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030056 mConditionsToSimplify(conditionsToSimplifyMask)
57{
58}
59
Olli Etuaho9676d1a2017-05-16 11:29:24 +030060// If we're inside a loop initialization, condition, or expression, we check for expressions that
61// should be moved out of the loop condition or expression. If one is found, the loop is
62// transformed.
63// If we're not inside loop initialization, condition, or expression, we only need to traverse nodes
64// that may contain loops.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030065
Olli Etuahobb5a7e22017-08-30 13:03:12 +030066bool SimplifyLoopConditionsTraverser::visitUnary(Visit visit, TIntermUnary *node)
67{
68 if (!mInsideLoopInitConditionOrExpression)
69 return false;
70
71 if (mFoundLoopToChange)
72 return false; // Already decided to change this loop.
73
74 mFoundLoopToChange = mConditionsToSimplify.match(node);
75 return !mFoundLoopToChange;
76}
77
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030078bool SimplifyLoopConditionsTraverser::visitBinary(Visit visit, TIntermBinary *node)
79{
Corentin Wallez1212bca2016-11-23 13:44:05 -050080 if (!mInsideLoopInitConditionOrExpression)
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030081 return false;
82
Olli Etuaho9676d1a2017-05-16 11:29:24 +030083 if (mFoundLoopToChange)
84 return false; // Already decided to change this loop.
85
Jamie Madill666f65a2016-08-26 01:34:37 +000086 mFoundLoopToChange = mConditionsToSimplify.match(node, getParentNode(), isLValueRequiredHere());
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030087 return !mFoundLoopToChange;
88}
89
90bool SimplifyLoopConditionsTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
91{
Corentin Wallez1212bca2016-11-23 13:44:05 -050092 if (!mInsideLoopInitConditionOrExpression)
Olli Etuaho336b1472016-10-05 16:37:55 +010093 return false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030094
Olli Etuaho9676d1a2017-05-16 11:29:24 +030095 if (mFoundLoopToChange)
96 return false; // Already decided to change this loop.
97
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030098 mFoundLoopToChange = mConditionsToSimplify.match(node, getParentNode());
99 return !mFoundLoopToChange;
100}
101
Olli Etuahod0bad2c2016-09-09 18:01:16 +0300102bool SimplifyLoopConditionsTraverser::visitTernary(Visit visit, TIntermTernary *node)
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300103{
Corentin Wallez1212bca2016-11-23 13:44:05 -0500104 if (!mInsideLoopInitConditionOrExpression)
105 return false;
106
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300107 if (mFoundLoopToChange)
108 return false; // Already decided to change this loop.
109
Corentin Wallez1212bca2016-11-23 13:44:05 -0500110 mFoundLoopToChange = mConditionsToSimplify.match(node);
111 return !mFoundLoopToChange;
112}
113
114bool SimplifyLoopConditionsTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
115{
Corentin Wallez1212bca2016-11-23 13:44:05 -0500116 if (!mInsideLoopInitConditionOrExpression)
Olli Etuahod0bad2c2016-09-09 18:01:16 +0300117 return false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300118
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300119 if (mFoundLoopToChange)
120 return false; // Already decided to change this loop.
121
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300122 mFoundLoopToChange = mConditionsToSimplify.match(node);
123 return !mFoundLoopToChange;
124}
125
126void SimplifyLoopConditionsTraverser::traverseLoop(TIntermLoop *node)
127{
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300128 // Mark that we're inside a loop condition or expression, and determine if the loop needs to be
129 // transformed.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300130
Olli Etuaho1d9dcc22017-01-19 11:25:32 +0000131 ScopedNodeInTraversalPath addToPath(this, node);
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300132
Corentin Wallez1212bca2016-11-23 13:44:05 -0500133 mInsideLoopInitConditionOrExpression = true;
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300134 mFoundLoopToChange = false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300135
Corentin Wallez1212bca2016-11-23 13:44:05 -0500136 if (!mFoundLoopToChange && node->getInit())
137 {
138 node->getInit()->traverse(this);
139 }
140
141 if (!mFoundLoopToChange && node->getCondition())
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300142 {
143 node->getCondition()->traverse(this);
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300144 }
145
146 if (!mFoundLoopToChange && node->getExpression())
147 {
148 node->getExpression()->traverse(this);
Corentin Wallez1212bca2016-11-23 13:44:05 -0500149 }
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300150
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300151 mInsideLoopInitConditionOrExpression = false;
152
Corentin Wallez1212bca2016-11-23 13:44:05 -0500153 if (mFoundLoopToChange)
154 {
Olli Etuahob60d30f2018-01-16 12:31:06 +0200155 const TType *boolType = StaticType::Get<EbtBool, EbpUndefined, EvqTemporary, 1, 1>();
Olli Etuaho195be942017-12-04 23:40:14 +0200156 TVariable *conditionVariable = CreateTempVariable(mSymbolTable, boolType);
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300157
Corentin Wallez1212bca2016-11-23 13:44:05 -0500158 // Replace the loop condition with a boolean variable that's updated on each iteration.
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300159 TLoopType loopType = node->getType();
Corentin Wallez1212bca2016-11-23 13:44:05 -0500160 if (loopType == ELoopWhile)
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300161 {
Corentin Wallez1212bca2016-11-23 13:44:05 -0500162 // Transform:
163 // while (expr) { body; }
164 // into
165 // bool s0 = expr;
166 // while (s0) { { body; } s0 = expr; }
Olli Etuaho195be942017-12-04 23:40:14 +0200167 TIntermDeclaration *tempInitDeclaration =
168 CreateTempInitDeclarationNode(conditionVariable, node->getCondition()->deepCopy());
169 insertStatementInParentBlock(tempInitDeclaration);
Corentin Wallez1212bca2016-11-23 13:44:05 -0500170
171 TIntermBlock *newBody = new TIntermBlock();
172 if (node->getBody())
173 {
174 newBody->getSequence()->push_back(node->getBody());
175 }
176 newBody->getSequence()->push_back(
Olli Etuaho195be942017-12-04 23:40:14 +0200177 CreateTempAssignmentNode(conditionVariable, node->getCondition()->deepCopy()));
Corentin Wallez1212bca2016-11-23 13:44:05 -0500178
179 // Can't use queueReplacement to replace old body, since it may have been nullptr.
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300180 // It's safe to do the replacements in place here - the new body will still be
181 // traversed, but that won't create any problems.
Corentin Wallez1212bca2016-11-23 13:44:05 -0500182 node->setBody(newBody);
Olli Etuaho195be942017-12-04 23:40:14 +0200183 node->setCondition(CreateTempSymbolNode(conditionVariable));
Corentin Wallez1212bca2016-11-23 13:44:05 -0500184 }
185 else if (loopType == ELoopDoWhile)
186 {
187 // Transform:
188 // do {
189 // body;
190 // } while (expr);
191 // into
192 // bool s0 = true;
193 // do {
194 // { body; }
195 // s0 = expr;
196 // } while (s0);
Olli Etuaho195be942017-12-04 23:40:14 +0200197 TIntermDeclaration *tempInitDeclaration =
198 CreateTempInitDeclarationNode(conditionVariable, CreateBoolNode(true));
199 insertStatementInParentBlock(tempInitDeclaration);
Corentin Wallez1212bca2016-11-23 13:44:05 -0500200
201 TIntermBlock *newBody = new TIntermBlock();
202 if (node->getBody())
203 {
204 newBody->getSequence()->push_back(node->getBody());
205 }
206 newBody->getSequence()->push_back(
Olli Etuaho195be942017-12-04 23:40:14 +0200207 CreateTempAssignmentNode(conditionVariable, node->getCondition()->deepCopy()));
Corentin Wallez1212bca2016-11-23 13:44:05 -0500208
209 // Can't use queueReplacement to replace old body, since it may have been nullptr.
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300210 // It's safe to do the replacements in place here - the new body will still be
211 // traversed, but that won't create any problems.
Corentin Wallez1212bca2016-11-23 13:44:05 -0500212 node->setBody(newBody);
Olli Etuaho195be942017-12-04 23:40:14 +0200213 node->setCondition(CreateTempSymbolNode(conditionVariable));
Corentin Wallez1212bca2016-11-23 13:44:05 -0500214 }
215 else if (loopType == ELoopFor)
216 {
217 // Move the loop condition inside the loop.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300218 // Transform:
219 // for (init; expr; exprB) { body; }
220 // into
Corentin Wallez1212bca2016-11-23 13:44:05 -0500221 // {
222 // init;
223 // bool s0 = expr;
Corentin Wallez36fd1002016-12-08 11:30:44 -0500224 // while (s0) {
225 // { body; }
226 // exprB;
227 // s0 = expr;
228 // }
Corentin Wallez1212bca2016-11-23 13:44:05 -0500229 // }
Corentin Wallez36fd1002016-12-08 11:30:44 -0500230 TIntermBlock *loopScope = new TIntermBlock();
231 TIntermSequence *loopScopeSequence = loopScope->getSequence();
232
233 // Insert "init;"
Corentin Wallez1212bca2016-11-23 13:44:05 -0500234 if (node->getInit())
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300235 {
Corentin Wallez36fd1002016-12-08 11:30:44 -0500236 loopScopeSequence->push_back(node->getInit());
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300237 }
Corentin Wallez1212bca2016-11-23 13:44:05 -0500238
Corentin Wallez36fd1002016-12-08 11:30:44 -0500239 // Insert "bool s0 = expr;" if applicable, "bool s0 = true;" otherwise
240 TIntermTyped *conditionInitializer = nullptr;
241 if (node->getCondition())
242 {
243 conditionInitializer = node->getCondition()->deepCopy();
244 }
245 else
246 {
Olli Etuaho3ec75682017-07-05 17:02:55 +0300247 conditionInitializer = CreateBoolNode(true);
Corentin Wallez36fd1002016-12-08 11:30:44 -0500248 }
Olli Etuaho195be942017-12-04 23:40:14 +0200249 loopScopeSequence->push_back(
250 CreateTempInitDeclarationNode(conditionVariable, conditionInitializer));
Corentin Wallez36fd1002016-12-08 11:30:44 -0500251
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(
Olli Etuaho195be942017-12-04 23:40:14 +0200267 CreateTempAssignmentNode(conditionVariable, node->getCondition()->deepCopy()));
Corentin Wallez36fd1002016-12-08 11:30:44 -0500268 }
269
270 // Create "while(s0) { whileLoopBody }"
Olli Etuaho195be942017-12-04 23:40:14 +0200271 TIntermLoop *whileLoop =
272 new TIntermLoop(ELoopWhile, nullptr, CreateTempSymbolNode(conditionVariable),
273 nullptr, whileLoopBody);
Corentin Wallez1212bca2016-11-23 13:44:05 -0500274 loopScope->getSequence()->push_back(whileLoop);
Olli Etuahoea39a222017-07-06 12:47:59 +0300275 queueReplacement(loopScope, OriginalNode::IS_DROPPED);
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300276
277 // After this the old body node will be traversed and loops inside it may be
278 // transformed. This is fine, since the old body node will still be in the AST after the
279 // transformation that's queued here, and transforming loops inside it doesn't need to
280 // know the exact post-transform path to it.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300281 }
282 }
283
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300284 mFoundLoopToChange = false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300285
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300286 // We traverse the body of the loop even if the loop is transformed.
287 if (node->getBody())
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300288 node->getBody()->traverse(this);
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300289}
290
291} // namespace
292
293void SimplifyLoopConditions(TIntermNode *root,
294 unsigned int conditionsToSimplifyMask,
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300295 TSymbolTable *symbolTable,
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300296 int shaderVersion)
297{
298 SimplifyLoopConditionsTraverser traverser(conditionsToSimplifyMask, symbolTable, shaderVersion);
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300299 root->traverse(&traverser);
300 traverser.updateTree();
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300301}
Jamie Madill45bcc782016-11-07 13:58:48 -0500302
303} // namespace sh