1//
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/tree_ops/SimplifyLoopConditions.h"
12
13#include "compiler/translator/StaticType.h"
14#include "compiler/translator/tree_util/IntermNodePatternMatcher.h"
15#include "compiler/translator/tree_util/IntermNode_util.h"
16#include "compiler/translator/tree_util/IntermTraverse.h"
17
18namespace sh
19{
20
21namespace
22{
23
24class SimplifyLoopConditionsTraverser : public TLValueTrackingTraverser
25{
26 public:
27 SimplifyLoopConditionsTraverser(unsigned int conditionsToSimplifyMask,
28 TSymbolTable *symbolTable);
29
30 void traverseLoop(TIntermLoop *node) override;
31
32 bool visitUnary(Visit visit, TIntermUnary *node) override;
33 bool visitBinary(Visit visit, TIntermBinary *node) override;
34 bool visitAggregate(Visit visit, TIntermAggregate *node) override;
35 bool visitTernary(Visit visit, TIntermTernary *node) override;
36 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
37
38 bool foundLoopToChange() const { return mFoundLoopToChange; }
39
40 protected:
41 // Marked to true once an operation that needs to be hoisted out of a loop expression has been
42 // found.
43 bool mFoundLoopToChange;
44 bool mInsideLoopInitConditionOrExpression;
45 IntermNodePatternMatcher mConditionsToSimplify;
46};
47
48SimplifyLoopConditionsTraverser::SimplifyLoopConditionsTraverser(
49 unsigned int conditionsToSimplifyMask,
50 TSymbolTable *symbolTable)
51 : TLValueTrackingTraverser(true, false, false, symbolTable),
52 mFoundLoopToChange(false),
53 mInsideLoopInitConditionOrExpression(false),
54 mConditionsToSimplify(conditionsToSimplifyMask)
55{}
56
57// If we're inside a loop initialization, condition, or expression, we check for expressions that
58// should be moved out of the loop condition or expression. If one is found, the loop is
59// transformed.
60// If we're not inside loop initialization, condition, or expression, we only need to traverse nodes
61// that may contain loops.
62
63bool SimplifyLoopConditionsTraverser::visitUnary(Visit visit, TIntermUnary *node)
64{
65 if (!mInsideLoopInitConditionOrExpression)
66 return false;
67
68 if (mFoundLoopToChange)
69 return false; // Already decided to change this loop.
70
71 mFoundLoopToChange = mConditionsToSimplify.match(node);
72 return !mFoundLoopToChange;
73}
74
75bool SimplifyLoopConditionsTraverser::visitBinary(Visit visit, TIntermBinary *node)
76{
77 if (!mInsideLoopInitConditionOrExpression)
78 return false;
79
80 if (mFoundLoopToChange)
81 return false; // Already decided to change this loop.
82
83 mFoundLoopToChange = mConditionsToSimplify.match(node, getParentNode(), isLValueRequiredHere());
84 return !mFoundLoopToChange;
85}
86
87bool SimplifyLoopConditionsTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
88{
89 if (!mInsideLoopInitConditionOrExpression)
90 return false;
91
92 if (mFoundLoopToChange)
93 return false; // Already decided to change this loop.
94
95 mFoundLoopToChange = mConditionsToSimplify.match(node, getParentNode());
96 return !mFoundLoopToChange;
97}
98
99bool SimplifyLoopConditionsTraverser::visitTernary(Visit visit, TIntermTernary *node)
100{
101 if (!mInsideLoopInitConditionOrExpression)
102 return false;
103
104 if (mFoundLoopToChange)
105 return false; // Already decided to change this loop.
106
107 mFoundLoopToChange = mConditionsToSimplify.match(node);
108 return !mFoundLoopToChange;
109}
110
111bool SimplifyLoopConditionsTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
112{
113 if (!mInsideLoopInitConditionOrExpression)
114 return false;
115
116 if (mFoundLoopToChange)
117 return false; // Already decided to change this loop.
118
119 mFoundLoopToChange = mConditionsToSimplify.match(node);
120 return !mFoundLoopToChange;
121}
122
123void SimplifyLoopConditionsTraverser::traverseLoop(TIntermLoop *node)
124{
125 // Mark that we're inside a loop condition or expression, and determine if the loop needs to be
126 // transformed.
127
128 ScopedNodeInTraversalPath addToPath(this, node);
129
130 mInsideLoopInitConditionOrExpression = true;
131 mFoundLoopToChange = false;
132
133 if (!mFoundLoopToChange && node->getInit())
134 {
135 node->getInit()->traverse(this);
136 }
137
138 if (!mFoundLoopToChange && node->getCondition())
139 {
140 node->getCondition()->traverse(this);
141 }
142
143 if (!mFoundLoopToChange && node->getExpression())
144 {
145 node->getExpression()->traverse(this);
146 }
147
148 mInsideLoopInitConditionOrExpression = false;
149
150 if (mFoundLoopToChange)
151 {
152 const TType *boolType = StaticType::Get<EbtBool, EbpUndefined, EvqTemporary, 1, 1>();
153 TVariable *conditionVariable = CreateTempVariable(mSymbolTable, boolType);
154
155 // Replace the loop condition with a boolean variable that's updated on each iteration.
156 TLoopType loopType = node->getType();
157 if (loopType == ELoopWhile)
158 {
159 // Transform:
160 // while (expr) { body; }
161 // into
162 // bool s0 = expr;
163 // while (s0) { { body; } s0 = expr; }
164 TIntermDeclaration *tempInitDeclaration =
165 CreateTempInitDeclarationNode(conditionVariable, node->getCondition()->deepCopy());
166 insertStatementInParentBlock(tempInitDeclaration);
167
168 TIntermBlock *newBody = new TIntermBlock();
169 if (node->getBody())
170 {
171 newBody->getSequence()->push_back(node->getBody());
172 }
173 newBody->getSequence()->push_back(
174 CreateTempAssignmentNode(conditionVariable, node->getCondition()->deepCopy()));
175
176 // Can't use queueReplacement to replace old body, since it may have been nullptr.
177 // It's safe to do the replacements in place here - the new body will still be
178 // traversed, but that won't create any problems.
179 node->setBody(newBody);
180 node->setCondition(CreateTempSymbolNode(conditionVariable));
181 }
182 else if (loopType == ELoopDoWhile)
183 {
184 // Transform:
185 // do {
186 // body;
187 // } while (expr);
188 // into
189 // bool s0 = true;
190 // do {
191 // { body; }
192 // s0 = expr;
193 // } while (s0);
194 TIntermDeclaration *tempInitDeclaration =
195 CreateTempInitDeclarationNode(conditionVariable, CreateBoolNode(true));
196 insertStatementInParentBlock(tempInitDeclaration);
197
198 TIntermBlock *newBody = new TIntermBlock();
199 if (node->getBody())
200 {
201 newBody->getSequence()->push_back(node->getBody());
202 }
203 newBody->getSequence()->push_back(
204 CreateTempAssignmentNode(conditionVariable, node->getCondition()->deepCopy()));
205
206 // Can't use queueReplacement to replace old body, since it may have been nullptr.
207 // It's safe to do the replacements in place here - the new body will still be
208 // traversed, but that won't create any problems.
209 node->setBody(newBody);
210 node->setCondition(CreateTempSymbolNode(conditionVariable));
211 }
212 else if (loopType == ELoopFor)
213 {
214 // Move the loop condition inside the loop.
215 // Transform:
216 // for (init; expr; exprB) { body; }
217 // into
218 // {
219 // init;
220 // bool s0 = expr;
221 // while (s0) {
222 // { body; }
223 // exprB;
224 // s0 = expr;
225 // }
226 // }
227 TIntermBlock *loopScope = new TIntermBlock();
228 TIntermSequence *loopScopeSequence = loopScope->getSequence();
229
230 // Insert "init;"
231 if (node->getInit())
232 {
233 loopScopeSequence->push_back(node->getInit());
234 }
235
236 // Insert "bool s0 = expr;" if applicable, "bool s0 = true;" otherwise
237 TIntermTyped *conditionInitializer = nullptr;
238 if (node->getCondition())
239 {
240 conditionInitializer = node->getCondition()->deepCopy();
241 }
242 else
243 {
244 conditionInitializer = CreateBoolNode(true);
245 }
246 loopScopeSequence->push_back(
247 CreateTempInitDeclarationNode(conditionVariable, conditionInitializer));
248
249 // Insert "{ body; }" in the while loop
250 TIntermBlock *whileLoopBody = new TIntermBlock();
251 if (node->getBody())
252 {
253 whileLoopBody->getSequence()->push_back(node->getBody());
254 }
255 // Insert "exprB;" in the while loop
256 if (node->getExpression())
257 {
258 whileLoopBody->getSequence()->push_back(node->getExpression());
259 }
260 // Insert "s0 = expr;" in the while loop
261 if (node->getCondition())
262 {
263 whileLoopBody->getSequence()->push_back(
264 CreateTempAssignmentNode(conditionVariable, node->getCondition()->deepCopy()));
265 }
266
267 // Create "while(s0) { whileLoopBody }"
268 TIntermLoop *whileLoop =
269 new TIntermLoop(ELoopWhile, nullptr, CreateTempSymbolNode(conditionVariable),
270 nullptr, whileLoopBody);
271 loopScope->getSequence()->push_back(whileLoop);
272 queueReplacement(loopScope, OriginalNode::IS_DROPPED);
273
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.
278 }
279 }
280
281 mFoundLoopToChange = false;
282
283 // We traverse the body of the loop even if the loop is transformed.
284 if (node->getBody())
285 node->getBody()->traverse(this);
286}
287
288} // namespace
289
290void SimplifyLoopConditions(TIntermNode *root,
291 unsigned int conditionsToSimplifyMask,
292 TSymbolTable *symbolTable)
293{
294 SimplifyLoopConditionsTraverser traverser(conditionsToSimplifyMask, symbolTable);
295 root->traverse(&traverser);
296 traverser.updateTree();
297}
298
299} // namespace sh
300