blob: 58112b25f9c00f0c521bdb2781c500b3fc41986a [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 Etuahocccf2b02017-07-05 14:50:54 +030014#include "compiler/translator/IntermTraverse.h"
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030015
Jamie Madill45bcc782016-11-07 13:58:48 -050016namespace sh
17{
18
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030019namespace
20{
21
22TIntermConstantUnion *CreateBoolConstantNode(bool value)
23{
24 TConstantUnion *u = new TConstantUnion;
25 u->setBConst(value);
26 TIntermConstantUnion *node =
27 new TIntermConstantUnion(u, TType(EbtBool, EbpUndefined, EvqConst, 1));
28 return node;
29}
30
31class SimplifyLoopConditionsTraverser : public TLValueTrackingTraverser
32{
33 public:
34 SimplifyLoopConditionsTraverser(unsigned int conditionsToSimplifyMask,
35 const TSymbolTable &symbolTable,
36 int shaderVersion);
37
38 void traverseLoop(TIntermLoop *node) override;
39
40 bool visitBinary(Visit visit, TIntermBinary *node) override;
41 bool visitAggregate(Visit visit, TIntermAggregate *node) override;
Olli Etuahod0bad2c2016-09-09 18:01:16 +030042 bool visitTernary(Visit visit, TIntermTernary *node) override;
Corentin Wallez1212bca2016-11-23 13:44:05 -050043 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030044
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030045 bool foundLoopToChange() const { return mFoundLoopToChange; }
46
47 protected:
Olli Etuaho9676d1a2017-05-16 11:29:24 +030048 // Marked to true once an operation that needs to be hoisted out of a loop expression has been
49 // found.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030050 bool mFoundLoopToChange;
Corentin Wallez1212bca2016-11-23 13:44:05 -050051 bool mInsideLoopInitConditionOrExpression;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030052 IntermNodePatternMatcher mConditionsToSimplify;
53};
54
55SimplifyLoopConditionsTraverser::SimplifyLoopConditionsTraverser(
56 unsigned int conditionsToSimplifyMask,
57 const TSymbolTable &symbolTable,
58 int shaderVersion)
59 : TLValueTrackingTraverser(true, false, false, symbolTable, shaderVersion),
60 mFoundLoopToChange(false),
Corentin Wallez1212bca2016-11-23 13:44:05 -050061 mInsideLoopInitConditionOrExpression(false),
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030062 mConditionsToSimplify(conditionsToSimplifyMask)
63{
64}
65
Olli Etuaho9676d1a2017-05-16 11:29:24 +030066// If we're inside a loop initialization, condition, or expression, we check for expressions that
67// should be moved out of the loop condition or expression. If one is found, the loop is
68// transformed.
69// If we're not inside loop initialization, condition, or expression, we only need to traverse nodes
70// that may contain loops.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030071
72bool SimplifyLoopConditionsTraverser::visitBinary(Visit visit, TIntermBinary *node)
73{
Corentin Wallez1212bca2016-11-23 13:44:05 -050074 if (!mInsideLoopInitConditionOrExpression)
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030075 return false;
76
Olli Etuaho9676d1a2017-05-16 11:29:24 +030077 if (mFoundLoopToChange)
78 return false; // Already decided to change this loop.
79
Jamie Madill666f65a2016-08-26 01:34:37 +000080 mFoundLoopToChange = mConditionsToSimplify.match(node, getParentNode(), isLValueRequiredHere());
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030081 return !mFoundLoopToChange;
82}
83
84bool SimplifyLoopConditionsTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
85{
Corentin Wallez1212bca2016-11-23 13:44:05 -050086 if (!mInsideLoopInitConditionOrExpression)
Olli Etuaho336b1472016-10-05 16:37:55 +010087 return false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030088
Olli Etuaho9676d1a2017-05-16 11:29:24 +030089 if (mFoundLoopToChange)
90 return false; // Already decided to change this loop.
91
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030092 mFoundLoopToChange = mConditionsToSimplify.match(node, getParentNode());
93 return !mFoundLoopToChange;
94}
95
Olli Etuahod0bad2c2016-09-09 18:01:16 +030096bool SimplifyLoopConditionsTraverser::visitTernary(Visit visit, TIntermTernary *node)
Olli Etuaho3cbb27a2016-07-14 11:55:48 +030097{
Corentin Wallez1212bca2016-11-23 13:44:05 -050098 if (!mInsideLoopInitConditionOrExpression)
99 return false;
100
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300101 if (mFoundLoopToChange)
102 return false; // Already decided to change this loop.
103
Corentin Wallez1212bca2016-11-23 13:44:05 -0500104 mFoundLoopToChange = mConditionsToSimplify.match(node);
105 return !mFoundLoopToChange;
106}
107
108bool SimplifyLoopConditionsTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
109{
Corentin Wallez1212bca2016-11-23 13:44:05 -0500110 if (!mInsideLoopInitConditionOrExpression)
Olli Etuahod0bad2c2016-09-09 18:01:16 +0300111 return false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300112
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300113 if (mFoundLoopToChange)
114 return false; // Already decided to change this loop.
115
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300116 mFoundLoopToChange = mConditionsToSimplify.match(node);
117 return !mFoundLoopToChange;
118}
119
120void SimplifyLoopConditionsTraverser::traverseLoop(TIntermLoop *node)
121{
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300122 // Mark that we're inside a loop condition or expression, and determine if the loop needs to be
123 // transformed.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300124
Olli Etuaho1d9dcc22017-01-19 11:25:32 +0000125 ScopedNodeInTraversalPath addToPath(this, node);
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300126
Corentin Wallez1212bca2016-11-23 13:44:05 -0500127 mInsideLoopInitConditionOrExpression = true;
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300128 mFoundLoopToChange = false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300129
Corentin Wallez1212bca2016-11-23 13:44:05 -0500130 if (!mFoundLoopToChange && node->getInit())
131 {
132 node->getInit()->traverse(this);
133 }
134
135 if (!mFoundLoopToChange && node->getCondition())
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300136 {
137 node->getCondition()->traverse(this);
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300138 }
139
140 if (!mFoundLoopToChange && node->getExpression())
141 {
142 node->getExpression()->traverse(this);
Corentin Wallez1212bca2016-11-23 13:44:05 -0500143 }
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300144
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300145 mInsideLoopInitConditionOrExpression = false;
146
Corentin Wallez1212bca2016-11-23 13:44:05 -0500147 if (mFoundLoopToChange)
148 {
Olli Etuaho4dd06d52017-07-05 12:41:06 +0300149 nextTemporaryId();
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300150
Corentin Wallez1212bca2016-11-23 13:44:05 -0500151 // Replace the loop condition with a boolean variable that's updated on each iteration.
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300152 TLoopType loopType = node->getType();
Corentin Wallez1212bca2016-11-23 13:44:05 -0500153 if (loopType == ELoopWhile)
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300154 {
Corentin Wallez1212bca2016-11-23 13:44:05 -0500155 // Transform:
156 // while (expr) { body; }
157 // into
158 // bool s0 = expr;
159 // while (s0) { { body; } s0 = expr; }
160 TIntermSequence tempInitSeq;
161 tempInitSeq.push_back(createTempInitDeclaration(node->getCondition()->deepCopy()));
162 insertStatementsInParentBlock(tempInitSeq);
163
164 TIntermBlock *newBody = new TIntermBlock();
165 if (node->getBody())
166 {
167 newBody->getSequence()->push_back(node->getBody());
168 }
169 newBody->getSequence()->push_back(
170 createTempAssignment(node->getCondition()->deepCopy()));
171
172 // Can't use queueReplacement to replace old body, since it may have been nullptr.
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300173 // It's safe to do the replacements in place here - the new body will still be
174 // traversed, but that won't create any problems.
Corentin Wallez1212bca2016-11-23 13:44:05 -0500175 node->setBody(newBody);
176 node->setCondition(createTempSymbol(node->getCondition()->getType()));
177 }
178 else if (loopType == ELoopDoWhile)
179 {
180 // Transform:
181 // do {
182 // body;
183 // } while (expr);
184 // into
185 // bool s0 = true;
186 // do {
187 // { body; }
188 // s0 = expr;
189 // } while (s0);
190 TIntermSequence tempInitSeq;
191 tempInitSeq.push_back(createTempInitDeclaration(CreateBoolConstantNode(true)));
192 insertStatementsInParentBlock(tempInitSeq);
193
194 TIntermBlock *newBody = new TIntermBlock();
195 if (node->getBody())
196 {
197 newBody->getSequence()->push_back(node->getBody());
198 }
199 newBody->getSequence()->push_back(
200 createTempAssignment(node->getCondition()->deepCopy()));
201
202 // Can't use queueReplacement to replace old body, since it may have been nullptr.
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300203 // It's safe to do the replacements in place here - the new body will still be
204 // traversed, but that won't create any problems.
Corentin Wallez1212bca2016-11-23 13:44:05 -0500205 node->setBody(newBody);
206 node->setCondition(createTempSymbol(node->getCondition()->getType()));
207 }
208 else if (loopType == ELoopFor)
209 {
210 // Move the loop condition inside the loop.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300211 // Transform:
212 // for (init; expr; exprB) { body; }
213 // into
Corentin Wallez1212bca2016-11-23 13:44:05 -0500214 // {
215 // init;
216 // bool s0 = expr;
Corentin Wallez36fd1002016-12-08 11:30:44 -0500217 // while (s0) {
218 // { body; }
219 // exprB;
220 // s0 = expr;
221 // }
Corentin Wallez1212bca2016-11-23 13:44:05 -0500222 // }
Corentin Wallez36fd1002016-12-08 11:30:44 -0500223 TIntermBlock *loopScope = new TIntermBlock();
224 TIntermSequence *loopScopeSequence = loopScope->getSequence();
225
226 // Insert "init;"
Corentin Wallez1212bca2016-11-23 13:44:05 -0500227 if (node->getInit())
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300228 {
Corentin Wallez36fd1002016-12-08 11:30:44 -0500229 loopScopeSequence->push_back(node->getInit());
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300230 }
Corentin Wallez1212bca2016-11-23 13:44:05 -0500231
Corentin Wallez36fd1002016-12-08 11:30:44 -0500232 // Insert "bool s0 = expr;" if applicable, "bool s0 = true;" otherwise
233 TIntermTyped *conditionInitializer = nullptr;
234 if (node->getCondition())
235 {
236 conditionInitializer = node->getCondition()->deepCopy();
237 }
238 else
239 {
240 conditionInitializer = TIntermTyped::CreateBool(true);
241 }
242 loopScopeSequence->push_back(createTempInitDeclaration(conditionInitializer));
243
244 // Insert "{ body; }" in the while loop
Corentin Wallez1212bca2016-11-23 13:44:05 -0500245 TIntermBlock *whileLoopBody = new TIntermBlock();
246 if (node->getBody())
247 {
248 whileLoopBody->getSequence()->push_back(node->getBody());
249 }
Corentin Wallez36fd1002016-12-08 11:30:44 -0500250 // Insert "exprB;" in the while loop
Corentin Wallez1212bca2016-11-23 13:44:05 -0500251 if (node->getExpression())
252 {
253 whileLoopBody->getSequence()->push_back(node->getExpression());
254 }
Corentin Wallez36fd1002016-12-08 11:30:44 -0500255 // Insert "s0 = expr;" in the while loop
256 if (node->getCondition())
257 {
258 whileLoopBody->getSequence()->push_back(
259 createTempAssignment(node->getCondition()->deepCopy()));
260 }
261
262 // Create "while(s0) { whileLoopBody }"
Corentin Wallez1212bca2016-11-23 13:44:05 -0500263 TIntermLoop *whileLoop = new TIntermLoop(
Corentin Wallez36fd1002016-12-08 11:30:44 -0500264 ELoopWhile, nullptr, createTempSymbol(conditionInitializer->getType()), nullptr,
Corentin Wallez1212bca2016-11-23 13:44:05 -0500265 whileLoopBody);
266 loopScope->getSequence()->push_back(whileLoop);
Olli Etuahoea39a222017-07-06 12:47:59 +0300267 queueReplacement(loopScope, OriginalNode::IS_DROPPED);
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300268
269 // After this the old body node will be traversed and loops inside it may be
270 // transformed. This is fine, since the old body node will still be in the AST after the
271 // transformation that's queued here, and transforming loops inside it doesn't need to
272 // know the exact post-transform path to it.
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300273 }
274 }
275
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300276 mFoundLoopToChange = false;
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300277
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300278 // We traverse the body of the loop even if the loop is transformed.
279 if (node->getBody())
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300280 node->getBody()->traverse(this);
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300281}
282
283} // namespace
284
285void SimplifyLoopConditions(TIntermNode *root,
286 unsigned int conditionsToSimplifyMask,
Olli Etuaho4dd06d52017-07-05 12:41:06 +0300287 TSymbolUniqueId *temporaryId,
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300288 const TSymbolTable &symbolTable,
289 int shaderVersion)
290{
291 SimplifyLoopConditionsTraverser traverser(conditionsToSimplifyMask, symbolTable, shaderVersion);
Olli Etuaho4dd06d52017-07-05 12:41:06 +0300292 ASSERT(temporaryId != nullptr);
293 traverser.useTemporaryId(temporaryId);
Olli Etuaho9676d1a2017-05-16 11:29:24 +0300294 root->traverse(&traverser);
295 traverser.updateTree();
Olli Etuaho3cbb27a2016-07-14 11:55:48 +0300296}
Jamie Madill45bcc782016-11-07 13:58:48 -0500297
298} // namespace sh