1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | |
5 | /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
6 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
7 | XX XX |
8 | XX LoopCloning XX |
9 | XX XX |
10 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
11 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
12 | |
13 | Loop cloning optimizations comprise of the following steps: |
14 | - Loop detection logic which is existing logic in the JIT that records |
15 | loop information with loop flags. |
16 | - The next step is to identify loop optimization candidates. This is done |
17 | by optObtainLoopCloningOpts. The loop context variable is updated with |
18 | all the necessary information (for ex: block, stmt, tree information) |
19 | to do the optimization later. |
20 | a) This involves checking if the loop is well-formed with respect to |
21 | the optimization being performed. |
22 | b) In array bounds check case, reconstructing the morphed GT_INDEX |
23 | nodes back to their array representation. |
24 | i) The array index is stored in the "context" variable with |
25 | additional block, tree, stmt info. |
26 | - Once the optimization candidates are identified, we derive cloning conditions |
27 | For ex: to clone a simple "for (i=0; i<n; ++i) { a[i] }" loop, we need the |
28 | following conditions: |
29 | (a != null) && ((n >= 0) & (n <= a.length) & (stride > 0)) |
30 | a) Note the short circuit AND for (a != null). These are called block |
31 | conditions or deref-conditions since these conditions need to be in their |
32 | own blocks to be able to short-circuit. |
33 | i) For a doubly nested loop on i, j, we would then have |
34 | conditions like |
35 | (a != null) && (i < a.len) && (a[i] != null) && (j < a[i].len) |
36 | all short-circuiting creating blocks. |
37 | |
38 | Advantage: |
39 | All conditions are checked before we enter the fast path. So fast |
40 | path gets as fast as it can be. |
41 | |
42 | Disadvantage: |
43 | Creation of blocks. |
44 | |
45 | Heuristic: |
46 | Therefore we will not clone if we exceed creating 4 blocks. |
47 | |
48 | b) The other conditions called cloning conditions are transformed into LC_Condition |
49 | structs which are then optimized. |
50 | i) Optimization of conditions involves removing redundant condition checks. |
51 | ii) If some conditions evaluate to true statically, then they are removed. |
52 | iii) If any condition evaluates to false statically, then loop cloning is |
53 | aborted for that loop. |
54 | - Then the block splitting occurs and loop cloning conditions is transformed into |
55 | GenTree and added to the loop cloning choice block. |
56 | |
57 | Preconditions |
58 | - Loop detection should have completed and the loop table should be |
59 | populated with the loop dscs. |
60 | - The loops that will be considered are the ones with the LPFLG_ITER |
61 | marked on them. |
62 | |
63 | Limitations |
64 | - For array based optimizations the loop choice condition is checked |
65 | before the loop body. This implies that the loop initializer statement |
66 | has not executed at the time of the check. So any loop cloning condition |
67 | involving the initial value of the loop counter cannot be condition checked |
68 | as it hasn't been assigned yet at the time of condition checking. Therefore |
69 | the initial value has to be statically known. This can be fixed with further |
70 | effort. |
71 | |
72 | Assumption |
73 | - The assumption is that the optimization candidates collected during the |
74 | identification phase will be the ones that will be optimized. In other words, |
75 | the loop that is present originally will be the fast path. Explicitly, the cloned |
76 | path will be the slow path and will be unoptimized. This allows us to |
77 | collect additional information at the same time of identifying the optimization |
78 | candidates. This later helps us to perform the optimizations during actual cloning. |
79 | - All loop cloning choice conditions will automatically be "AND"-ed. These are |
80 | bitwise AND operations. |
81 | - Perform short circuit AND for (array != null) side effect check |
82 | before hoisting (limit <= a.length) check. |
83 | For ex: to clone a simple "for (i=0; i<n; ++i) { a[i] }" loop, we need the |
84 | following conditions: |
85 | (a != null) && ((n >= 0) & (n <= a.length) & (stride > 0)) |
86 | |
87 | */ |
88 | #pragma once |
89 | |
90 | class Compiler; |
91 | |
92 | /** |
93 | * |
94 | * Represents an array access and associated bounds checks. |
95 | * Array access is required have the array and indices in local variables. |
96 | * This struct is constructed using a GT_INDEX node that is broken into |
97 | * its sub trees. |
98 | * |
99 | */ |
100 | struct ArrIndex |
101 | { |
102 | unsigned arrLcl; // The array base local num |
103 | JitExpandArrayStack<unsigned> indLcls; // The indices local nums |
104 | JitExpandArrayStack<GenTree*> bndsChks; // The bounds checks nodes along each dimension. |
105 | unsigned rank; // Rank of the array |
106 | BasicBlock* useBlock; // Block where the [] occurs |
107 | |
108 | ArrIndex(CompAllocator alloc) : arrLcl(BAD_VAR_NUM), indLcls(alloc), bndsChks(alloc), rank(0), useBlock(nullptr) |
109 | { |
110 | } |
111 | |
112 | #ifdef DEBUG |
113 | void Print(unsigned dim = -1) |
114 | { |
115 | printf("V%02d" , arrLcl); |
116 | for (unsigned i = 0; i < ((dim == -1) ? rank : dim); ++i) |
117 | { |
118 | printf("[V%02d]" , indLcls.GetRef(i)); |
119 | } |
120 | } |
121 | #endif |
122 | }; |
123 | |
124 | // Forward declarations |
125 | #define LC_OPT(en) struct en##OptInfo; |
126 | #include "loopcloningopts.h" |
127 | |
128 | /** |
129 | * |
130 | * LcOptInfo represents the optimization information for loop cloning, |
131 | * other classes are supposed to derive from this base class. |
132 | * |
133 | * Example usage: |
134 | * LcMdArrayOptInfo is multi-dimensional array optimization for which the |
135 | * loop can be cloned. |
136 | * LcArrIndexOptInfo is a jagged array optimization for which the loop |
137 | * can be cloned. |
138 | * |
139 | * So LcOptInfo represents any type of optimization opportunity that |
140 | * occurs in a loop and the metadata for the optimization is stored in |
141 | * this class. |
142 | */ |
143 | struct LcOptInfo |
144 | { |
145 | enum OptType |
146 | { |
147 | #undef LC_OPT |
148 | #define LC_OPT(en) en, |
149 | #include "loopcloningopts.h" |
150 | }; |
151 | |
152 | void* optInfo; |
153 | OptType optType; |
154 | LcOptInfo(void* optInfo, OptType optType) : optInfo(optInfo), optType(optType) |
155 | { |
156 | } |
157 | |
158 | OptType GetOptType() |
159 | { |
160 | return optType; |
161 | } |
162 | #undef LC_OPT |
163 | #define LC_OPT(en) \ |
164 | en##OptInfo* As##en##OptInfo() \ |
165 | { \ |
166 | assert(optType == en); \ |
167 | return reinterpret_cast<en##OptInfo*>(this); \ |
168 | } |
169 | #include "loopcloningopts.h" |
170 | }; |
171 | |
172 | /** |
173 | * |
174 | * Optimization info for a multi-dimensional array. |
175 | */ |
176 | struct LcMdArrayOptInfo : public LcOptInfo |
177 | { |
178 | GenTreeArrElem* arrElem; // "arrElem" node of an MD array. |
179 | unsigned dim; // "dim" represents upto what level of the rank this optimization applies to. |
180 | // For example, a[i,j,k] could be the MD array "arrElem" but if "dim" is 2, |
181 | // then this node is treated as though it were a[i,j] |
182 | ArrIndex* index; // "index" cached computation in the form of an ArrIndex representation. |
183 | |
184 | LcMdArrayOptInfo(GenTreeArrElem* arrElem, unsigned dim) |
185 | : LcOptInfo(this, LcMdArray), arrElem(arrElem), dim(dim), index(nullptr) |
186 | { |
187 | } |
188 | |
189 | ArrIndex* GetArrIndexForDim(CompAllocator alloc) |
190 | { |
191 | if (index == nullptr) |
192 | { |
193 | index = new (alloc) ArrIndex(alloc); |
194 | index->rank = arrElem->gtArrRank; |
195 | for (unsigned i = 0; i < dim; ++i) |
196 | { |
197 | index->indLcls.Push(arrElem->gtArrInds[i]->gtLclVarCommon.gtLclNum); |
198 | } |
199 | index->arrLcl = arrElem->gtArrObj->gtLclVarCommon.gtLclNum; |
200 | } |
201 | return index; |
202 | } |
203 | }; |
204 | |
205 | /** |
206 | * |
207 | * Optimization info for a jagged array. |
208 | */ |
209 | struct LcJaggedArrayOptInfo : public LcOptInfo |
210 | { |
211 | unsigned dim; // "dim" represents upto what level of the rank this optimization applies to. |
212 | // For example, a[i][j][k] could be the jagged array but if "dim" is 2, |
213 | // then this node is treated as though it were a[i][j] |
214 | ArrIndex arrIndex; // ArrIndex representation of the array. |
215 | GenTree* stmt; // "stmt" where the optimization opportunity occurs. |
216 | |
217 | LcJaggedArrayOptInfo(ArrIndex& arrIndex, unsigned dim, GenTree* stmt) |
218 | : LcOptInfo(this, LcJaggedArray), dim(dim), arrIndex(arrIndex), stmt(stmt) |
219 | { |
220 | } |
221 | }; |
222 | |
223 | /** |
224 | * |
225 | * Symbolic representation of a.length, or a[i][j].length or a[i,j].length and so on. |
226 | * OperType decides whether "arrLength" is invoked on the array or if it is just an array. |
227 | */ |
228 | struct LC_Array |
229 | { |
230 | enum ArrType |
231 | { |
232 | Invalid, |
233 | Jagged, |
234 | MdArray |
235 | }; |
236 | |
237 | enum OperType |
238 | { |
239 | None, |
240 | ArrLen, |
241 | }; |
242 | |
243 | ArrType type; // The type of the array on which to invoke length operator. |
244 | ArrIndex* arrIndex; // ArrIndex representation of this array. |
245 | |
246 | OperType oper; |
247 | |
248 | #ifdef DEBUG |
249 | void Print() |
250 | { |
251 | arrIndex->Print(dim); |
252 | if (oper == ArrLen) |
253 | { |
254 | printf(".Length" ); |
255 | } |
256 | } |
257 | #endif |
258 | |
259 | int dim; // "dim" = which index to invoke arrLen on, if -1 invoke on the whole array |
260 | // Example 1: a[0][1][2] and dim = 2 implies a[0][1].length |
261 | // Example 2: a[0][1][2] and dim = -1 implies a[0][1][2].length |
262 | LC_Array() : type(Invalid), dim(-1) |
263 | { |
264 | } |
265 | LC_Array(ArrType type, ArrIndex* arrIndex, int dim, OperType oper) |
266 | : type(type), arrIndex(arrIndex), oper(oper), dim(dim) |
267 | { |
268 | } |
269 | |
270 | LC_Array(ArrType type, ArrIndex* arrIndex, OperType oper) : type(type), arrIndex(arrIndex), oper(oper), dim(-1) |
271 | { |
272 | } |
273 | |
274 | // Equality operator |
275 | bool operator==(const LC_Array& that) const |
276 | { |
277 | assert(type != Invalid && that.type != Invalid); |
278 | |
279 | // Types match and the array base matches. |
280 | if (type != that.type || arrIndex->arrLcl != that.arrIndex->arrLcl || oper != that.oper) |
281 | { |
282 | return false; |
283 | } |
284 | |
285 | // If the dim ranks are not matching, quit. |
286 | int rank1 = GetDimRank(); |
287 | int rank2 = that.GetDimRank(); |
288 | if (rank1 != rank2) |
289 | { |
290 | return false; |
291 | } |
292 | |
293 | // Check for the indices. |
294 | for (int i = 0; i < rank1; ++i) |
295 | { |
296 | if (arrIndex->indLcls[i] != that.arrIndex->indLcls[i]) |
297 | { |
298 | return false; |
299 | } |
300 | } |
301 | return true; |
302 | } |
303 | |
304 | // The max dim on which length is invoked. |
305 | int GetDimRank() const |
306 | { |
307 | return (dim < 0) ? (int)arrIndex->rank : dim; |
308 | } |
309 | |
310 | // Get a tree representation for this symbolic a.length |
311 | GenTree* ToGenTree(Compiler* comp); |
312 | }; |
313 | |
314 | /** |
315 | * |
316 | * Symbolic representation of either a constant like 1, 2 or a variable V02, V03 etc. or an "LC_Array" or the null |
317 | * constant. |
318 | */ |
319 | struct LC_Ident |
320 | { |
321 | enum IdentType |
322 | { |
323 | Invalid, |
324 | Const, |
325 | Var, |
326 | ArrLen, |
327 | Null, |
328 | }; |
329 | |
330 | unsigned constant; // The constant value if this node is of type "Const", or the lcl num if "Var" |
331 | LC_Array arrLen; // The LC_Array if the type is "ArrLen" |
332 | IdentType type; // The type of this object |
333 | |
334 | // Equality operator |
335 | bool operator==(const LC_Ident& that) const |
336 | { |
337 | switch (type) |
338 | { |
339 | case Const: |
340 | case Var: |
341 | return (type == that.type) && constant == that.constant; |
342 | case ArrLen: |
343 | return (type == that.type) && (arrLen == that.arrLen); |
344 | case Null: |
345 | return (type == that.type); |
346 | default: |
347 | assert(!"Unknown LC_Ident type" ); |
348 | unreached(); |
349 | } |
350 | } |
351 | |
352 | #ifdef DEBUG |
353 | void Print() |
354 | { |
355 | switch (type) |
356 | { |
357 | case Const: |
358 | printf("%u" , constant); |
359 | break; |
360 | case Var: |
361 | printf("V%02d" , constant); |
362 | break; |
363 | case ArrLen: |
364 | arrLen.Print(); |
365 | break; |
366 | case Null: |
367 | printf("null" ); |
368 | break; |
369 | default: |
370 | assert(false); |
371 | break; |
372 | } |
373 | } |
374 | #endif |
375 | |
376 | LC_Ident() : type(Invalid) |
377 | { |
378 | } |
379 | LC_Ident(unsigned constant, IdentType type) : constant(constant), type(type) |
380 | { |
381 | } |
382 | explicit LC_Ident(IdentType type) : type(type) |
383 | { |
384 | } |
385 | explicit LC_Ident(const LC_Array& arrLen) : arrLen(arrLen), type(ArrLen) |
386 | { |
387 | } |
388 | |
389 | // Convert this symbolic representation into a tree node. |
390 | GenTree* ToGenTree(Compiler* comp); |
391 | }; |
392 | |
393 | /** |
394 | * |
395 | * Symbolic representation of an expr that involves an "LC_Ident" |
396 | */ |
397 | struct LC_Expr |
398 | { |
399 | enum ExprType |
400 | { |
401 | Invalid, |
402 | Ident, |
403 | }; |
404 | |
405 | LC_Ident ident; |
406 | ExprType type; |
407 | |
408 | // Equality operator |
409 | bool operator==(const LC_Expr& that) const |
410 | { |
411 | assert(type != Invalid && that.type != Invalid); |
412 | |
413 | // If the types don't match quit. |
414 | if (type != that.type) |
415 | { |
416 | return false; |
417 | } |
418 | |
419 | // Check if the ident match. |
420 | return (ident == that.ident); |
421 | } |
422 | |
423 | #ifdef DEBUG |
424 | void Print() |
425 | { |
426 | if (type == Ident) |
427 | { |
428 | ident.Print(); |
429 | } |
430 | } |
431 | #endif |
432 | |
433 | LC_Expr() : type(Invalid) |
434 | { |
435 | } |
436 | explicit LC_Expr(const LC_Ident& ident) : ident(ident), type(Ident) |
437 | { |
438 | } |
439 | |
440 | // Convert LC_Expr into a tree node. |
441 | GenTree* ToGenTree(Compiler* comp); |
442 | }; |
443 | |
444 | /** |
445 | * |
446 | * Symbolic representation of a conditional operation involving two "LC_Expr": |
447 | * LC_Expr < LC_Expr, for example: i > 0, i < a.length |
448 | */ |
449 | struct LC_Condition |
450 | { |
451 | LC_Expr op1; |
452 | LC_Expr op2; |
453 | genTreeOps oper; |
454 | |
455 | #ifdef DEBUG |
456 | void Print() |
457 | { |
458 | op1.Print(); |
459 | printf(" %s " , GenTree::OpName(oper)); |
460 | op2.Print(); |
461 | } |
462 | #endif |
463 | |
464 | // Check if the condition evaluates statically to true or false, i < i => false, a.length > 0 => true |
465 | // The result is put in "pResult" parameter and is valid if the method returns "true". Otherwise, the |
466 | // condition could not be evaluated. |
467 | bool Evaluates(bool* pResult); |
468 | |
469 | // Check if two conditions can be combined to yield one condition. |
470 | bool Combines(const LC_Condition& cond, LC_Condition* newCond); |
471 | |
472 | LC_Condition() |
473 | { |
474 | } |
475 | LC_Condition(genTreeOps oper, const LC_Expr& op1, const LC_Expr& op2) : op1(op1), op2(op2), oper(oper) |
476 | { |
477 | } |
478 | |
479 | // Convert this conditional operation into a GenTree. |
480 | GenTree* ToGenTree(Compiler* comp); |
481 | }; |
482 | |
483 | /** |
484 | * A deref tree of an array expression. |
485 | * a[i][j][k], b[i] and a[i][y][k] are the occurrences in the loop, then, the tree would be: |
486 | * a => { |
487 | * i => { |
488 | * j => { |
489 | * k => {} |
490 | * }, |
491 | * y => { |
492 | * k => {} |
493 | * }, |
494 | * } |
495 | * }, |
496 | * b => { |
497 | * i => {} |
498 | * } |
499 | */ |
500 | struct LC_Deref |
501 | { |
502 | const LC_Array array; |
503 | JitExpandArrayStack<LC_Deref*>* children; |
504 | |
505 | unsigned level; |
506 | |
507 | LC_Deref(const LC_Array& array, unsigned level) : array(array), children(nullptr), level(level) |
508 | { |
509 | } |
510 | |
511 | LC_Deref* Find(unsigned lcl); |
512 | |
513 | unsigned Lcl(); |
514 | |
515 | bool HasChildren(); |
516 | void EnsureChildren(CompAllocator alloc); |
517 | static LC_Deref* Find(JitExpandArrayStack<LC_Deref*>* children, unsigned lcl); |
518 | |
519 | void DeriveLevelConditions(JitExpandArrayStack<JitExpandArrayStack<LC_Condition>*>* len); |
520 | #ifdef DEBUG |
521 | void Print(unsigned indent = 0) |
522 | { |
523 | unsigned tab = 4 * indent; |
524 | printf("%*s%d,%d => {" , tab, "" , Lcl(), level); |
525 | if (children != nullptr) |
526 | { |
527 | for (unsigned i = 0; i < children->Size(); ++i) |
528 | { |
529 | if (i > 0) |
530 | { |
531 | printf("," ); |
532 | } |
533 | printf("\n" ); |
534 | #ifdef _MSC_VER |
535 | (*children)[i]->Print(indent + 1); |
536 | #else // _MSC_VER |
537 | (*((JitExpandArray<LC_Deref*>*)children))[i]->Print(indent + 1); |
538 | #endif // _MSC_VER |
539 | } |
540 | } |
541 | printf("\n%*s}" , tab, "" ); |
542 | } |
543 | #endif |
544 | }; |
545 | |
546 | /** |
547 | * |
548 | * The "context" represents data that is used for making loop-cloning decisions. |
549 | * - The data is the collection of optimization opportunities |
550 | * - and the conditions (LC_Condition) that decide between the fast |
551 | * path or the slow path. |
552 | * |
553 | * BNF for LC_Condition: |
554 | * LC_Condition : LC_Expr genTreeOps LC_Expr |
555 | * LC_Expr : LC_Ident | LC_Ident + Constant |
556 | * LC_Ident : Constant | Var | LC_Array |
557 | * LC_Array : . |
558 | * genTreeOps : GT_GE | GT_LE | GT_GT | GT_LT |
559 | * |
560 | */ |
561 | struct LoopCloneContext |
562 | { |
563 | CompAllocator alloc; // The allocator |
564 | JitExpandArrayStack<LcOptInfo*>** optInfo; // The array of optimization opportunities found in each loop. (loop x |
565 | // optimization-opportunities) |
566 | JitExpandArrayStack<LC_Condition>** conditions; // The array of conditions that influence which path to take for |
567 | // each |
568 | // loop. (loop x cloning-conditions) |
569 | JitExpandArrayStack<LC_Array>** derefs; // The array of dereference conditions found in each loop. (loop x |
570 | // deref-conditions) |
571 | JitExpandArrayStack<JitExpandArrayStack<LC_Condition>*>** blockConditions; // The array of block levels of |
572 | // conditions for |
573 | // each loop. (loop x level x conditions) |
574 | |
575 | LoopCloneContext(unsigned loopCount, CompAllocator alloc) : alloc(alloc) |
576 | { |
577 | optInfo = new (alloc) JitExpandArrayStack<LcOptInfo*>*[loopCount]; |
578 | conditions = new (alloc) JitExpandArrayStack<LC_Condition>*[loopCount]; |
579 | derefs = new (alloc) JitExpandArrayStack<LC_Array>*[loopCount]; |
580 | blockConditions = new (alloc) JitExpandArrayStack<JitExpandArrayStack<LC_Condition>*>*[loopCount]; |
581 | for (unsigned i = 0; i < loopCount; ++i) |
582 | { |
583 | optInfo[i] = nullptr; |
584 | conditions[i] = nullptr; |
585 | derefs[i] = nullptr; |
586 | blockConditions[i] = nullptr; |
587 | } |
588 | } |
589 | |
590 | // Evaluate conditions into a JTRUE stmt and put it in the block. Reverse condition if 'reverse' is true. |
591 | void CondToStmtInBlock(Compiler* comp, JitExpandArrayStack<LC_Condition>& conds, BasicBlock* block, bool reverse); |
592 | |
593 | // Get all the optimization information for loop "loopNum"; This information is held in "optInfo" array. |
594 | // If NULL this allocates the optInfo[loopNum] array for "loopNum" |
595 | JitExpandArrayStack<LcOptInfo*>* EnsureLoopOptInfo(unsigned loopNum); |
596 | |
597 | // Get all the optimization information for loop "loopNum"; This information is held in "optInfo" array. |
598 | // If NULL this does not allocate the optInfo[loopNum] array for "loopNum" |
599 | JitExpandArrayStack<LcOptInfo*>* GetLoopOptInfo(unsigned loopNum); |
600 | |
601 | // Cancel all optimizations for loop "loopNum" by clearing out the "conditions" member if non-null |
602 | // and setting the optInfo to "null.", If "null", then the user of this class is not supposed to |
603 | // clone this loop. |
604 | void CancelLoopOptInfo(unsigned loopNum); |
605 | |
606 | // Get the conditions that decide which loop to take for "loopNum." If NULL allocate an empty array. |
607 | JitExpandArrayStack<LC_Condition>* EnsureConditions(unsigned loopNum); |
608 | |
609 | // Get the conditions for loop. No allocation is performed. |
610 | JitExpandArrayStack<LC_Condition>* GetConditions(unsigned loopNum); |
611 | |
612 | // Ensure that the "deref" conditions array is allocated. |
613 | JitExpandArrayStack<LC_Array>* EnsureDerefs(unsigned loopNum); |
614 | |
615 | // Get block conditions for each loop, no allocation is performed. |
616 | JitExpandArrayStack<JitExpandArrayStack<LC_Condition>*>* GetBlockConditions(unsigned loopNum); |
617 | |
618 | // Ensure that the block condition is present, if not allocate space. |
619 | JitExpandArrayStack<JitExpandArrayStack<LC_Condition>*>* EnsureBlockConditions(unsigned loopNum, |
620 | unsigned totalBlocks); |
621 | |
622 | // Print the block conditions for the loop. |
623 | void PrintBlockConditions(unsigned loopNum); |
624 | |
625 | // Does the loop have block conditions? |
626 | bool HasBlockConditions(unsigned loopNum); |
627 | |
628 | // Evaluate the conditions for "loopNum" and indicate if they are either all true or any of them are false. |
629 | // "pAllTrue" implies all the conditions are statically known to be true. |
630 | // "pAnyFalse" implies at least one condition is statically known to be false. |
631 | // If neither of them are true, then some conditions' evaluations are statically unknown. |
632 | // |
633 | // If all conditions yield true, then the caller doesn't need to clone the loop, but it can perform |
634 | // fast path optimizations. |
635 | // If any condition yields false, then the caller needs to abort cloning the loop (neither clone nor |
636 | // fast path optimizations.) |
637 | // |
638 | // Assumes the conditions involve an AND join operator. |
639 | void EvaluateConditions(unsigned loopNum, bool* pAllTrue, bool* pAnyFalse DEBUGARG(bool verbose)); |
640 | |
641 | private: |
642 | void OptimizeConditions(JitExpandArrayStack<LC_Condition>& conds); |
643 | |
644 | public: |
645 | // Optimize conditions to remove redundant conditions. |
646 | void OptimizeConditions(unsigned loopNum DEBUGARG(bool verbose)); |
647 | |
648 | void OptimizeBlockConditions(unsigned loopNum DEBUGARG(bool verbose)); |
649 | |
650 | #ifdef DEBUG |
651 | void PrintConditions(unsigned loopNum); |
652 | #endif |
653 | }; |
654 | |