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 | |
18 | namespace sh |
19 | { |
20 | |
21 | namespace |
22 | { |
23 | |
24 | class 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 | |
48 | SimplifyLoopConditionsTraverser::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 | |
63 | bool 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 | |
75 | bool 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 | |
87 | bool 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 | |
99 | bool 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 | |
111 | bool 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 | |
123 | void 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 | |
290 | void 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 | |