Avoid using IAllocator
[platform/upstream/coreclr.git] / src / jit / loopcloning.h
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     ExpandArrayStack<unsigned> indLcls;  // The indices local nums
104     ExpandArrayStack<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     GenTreePtr stmt;     // "stmt" where the optimization opportunity occurs.
216
217     LcJaggedArrayOptInfo(ArrIndex& arrIndex, unsigned dim, GenTreePtr 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     GenTreePtr 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     INT64     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("%I64d", 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(INT64 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     GenTreePtr ToGenTree(Compiler* comp);
391 };
392
393 /**
394  *
395  *  Symbolic representation of an expr that involves an "LC_Ident" or an "LC_Ident - constant"
396  */
397 struct LC_Expr
398 {
399     enum ExprType
400     {
401         Invalid,
402         Ident,
403         IdentPlusConst
404     };
405
406     LC_Ident ident;
407     INT64    constant;
408     ExprType type;
409
410     // Equality operator
411     bool operator==(const LC_Expr& that) const
412     {
413         assert(type != Invalid && that.type != Invalid);
414
415         // If the types don't match quit.
416         if (type != that.type)
417         {
418             return false;
419         }
420
421         // If the type involves arithmetic, the constant should match.
422         if (type == IdentPlusConst && constant != that.constant)
423         {
424             return false;
425         }
426
427         // Check if the ident match.
428         return (ident == that.ident);
429     }
430
431 #ifdef DEBUG
432     void Print()
433     {
434         if (type == IdentPlusConst)
435         {
436             printf("(%I64d - ", constant);
437             ident.Print();
438             printf(")");
439         }
440         else
441         {
442             ident.Print();
443         }
444     }
445 #endif
446
447     LC_Expr() : type(Invalid)
448     {
449     }
450     explicit LC_Expr(const LC_Ident& ident) : ident(ident), type(Ident)
451     {
452     }
453     LC_Expr(const LC_Ident& ident, INT64 constant) : ident(ident), constant(constant), type(IdentPlusConst)
454     {
455     }
456
457     // Convert LC_Expr into a tree node.
458     GenTreePtr ToGenTree(Compiler* comp);
459 };
460
461 /**
462  *
463  *  Symbolic representation of a conditional operation involving two "LC_Expr":
464  *  LC_Expr < LC_Expr, for example: i > 0, i < a.length
465  */
466 struct LC_Condition
467 {
468     LC_Expr    op1;
469     LC_Expr    op2;
470     genTreeOps oper;
471
472 #ifdef DEBUG
473     void Print()
474     {
475         op1.Print();
476         printf(" %s ", GenTree::OpName(oper));
477         op2.Print();
478     }
479 #endif
480
481     // Check if the condition evaluates statically to true or false, i < i => false, a.length > 0 => true
482     // The result is put in "pResult" parameter and is valid if the method returns "true". Otherwise, the
483     // condition could not be evaluated.
484     bool Evaluates(bool* pResult);
485
486     // Check if two conditions can be combined to yield one condition.
487     bool Combines(const LC_Condition& cond, LC_Condition* newCond);
488
489     LC_Condition()
490     {
491     }
492     LC_Condition(genTreeOps oper, const LC_Expr& op1, const LC_Expr& op2) : op1(op1), op2(op2), oper(oper)
493     {
494     }
495
496     // Convert this conditional operation into a GenTree.
497     GenTreePtr ToGenTree(Compiler* comp);
498 };
499
500 /**
501  *  A deref tree of an array expression.
502  *  a[i][j][k], b[i] and a[i][y][k] are the occurrences in the loop, then, the tree would be:
503  *      a => {
504  *          i => {
505  *              j => {
506  *                  k => {}
507  *              },
508  *              y => {
509  *                  k => {}
510  *              },
511  *          }
512  *      },
513  *      b => {
514  *          i => {}
515  *      }
516  */
517 struct LC_Deref
518 {
519     const LC_Array               array;
520     ExpandArrayStack<LC_Deref*>* children;
521
522     unsigned level;
523
524     LC_Deref(const LC_Array& array, unsigned level) : array(array), children(nullptr), level(level)
525     {
526     }
527
528     LC_Deref* Find(unsigned lcl);
529
530     unsigned Lcl();
531
532     bool HasChildren();
533     void EnsureChildren(CompAllocator* alloc);
534     static LC_Deref* Find(ExpandArrayStack<LC_Deref*>* children, unsigned lcl);
535
536     void DeriveLevelConditions(ExpandArrayStack<ExpandArrayStack<LC_Condition>*>* len);
537 #ifdef DEBUG
538     void Print(unsigned indent = 0)
539     {
540         unsigned tab = 4 * indent;
541         printf("%*s%d,%d => {", tab, "", Lcl(), level);
542         if (children != nullptr)
543         {
544             for (unsigned i = 0; i < children->Size(); ++i)
545             {
546                 if (i > 0)
547                 {
548                     printf(",");
549                 }
550                 printf("\n");
551 #ifdef _MSC_VER
552                 (*children)[i]->Print(indent + 1);
553 #else  // _MSC_VER
554                 (*((ExpandArray<LC_Deref*>*)children))[i]->Print(indent + 1);
555 #endif // _MSC_VER
556             }
557         }
558         printf("\n%*s}", tab, "");
559     }
560 #endif
561 };
562
563 /**
564  *
565  *  The "context" represents data that is used for making loop-cloning decisions.
566  *   - The data is the collection of optimization opportunities
567  *   - and the conditions (LC_Condition) that decide between the fast
568  *     path or the slow path.
569  *
570  *   BNF for LC_Condition:
571  *       LC_Condition :  LC_Expr genTreeOps LC_Expr
572  *       LC_Expr      :  LC_Ident | LC_Ident + Constant
573  *       LC_Ident     :  Constant | Var | LC_Array
574  *       LC_Array    :  .
575  *       genTreeOps   :  GT_GE | GT_LE | GT_GT | GT_LT
576  *
577  */
578 struct LoopCloneContext
579 {
580     CompAllocator*                 alloc;        // The allocator
581     ExpandArrayStack<LcOptInfo*>** optInfo;      // The array of optimization opportunities found in each loop. (loop x
582                                                  // optimization-opportunities)
583     ExpandArrayStack<LC_Condition>** conditions; // The array of conditions that influence which path to take for each
584                                                  // loop. (loop x cloning-conditions)
585     ExpandArrayStack<LC_Array>** derefs;         // The array of dereference conditions found in each loop. (loop x
586                                                  // deref-conditions)
587     ExpandArrayStack<ExpandArrayStack<LC_Condition>*>** blockConditions; // The array of block levels of conditions for
588                                                                          // each loop. (loop x level x conditions)
589
590     LoopCloneContext(unsigned loopCount, CompAllocator* alloc) : alloc(alloc)
591     {
592         optInfo         = new (alloc) ExpandArrayStack<LcOptInfo*>*[loopCount];
593         conditions      = new (alloc) ExpandArrayStack<LC_Condition>*[loopCount];
594         derefs          = new (alloc) ExpandArrayStack<LC_Array>*[loopCount];
595         blockConditions = new (alloc) ExpandArrayStack<ExpandArrayStack<LC_Condition>*>*[loopCount];
596         for (unsigned i = 0; i < loopCount; ++i)
597         {
598             optInfo[i]         = nullptr;
599             conditions[i]      = nullptr;
600             derefs[i]          = nullptr;
601             blockConditions[i] = nullptr;
602         }
603     }
604
605     // Evaluate conditions into a JTRUE stmt and put it in the block. Reverse condition if 'reverse' is true.
606     void CondToStmtInBlock(Compiler* comp, ExpandArrayStack<LC_Condition>& conds, BasicBlock* block, bool reverse);
607
608     // Get all the optimization information for loop "loopNum"; This information is held in "optInfo" array.
609     // If NULL this allocates the optInfo[loopNum] array for "loopNum"
610     ExpandArrayStack<LcOptInfo*>* EnsureLoopOptInfo(unsigned loopNum);
611
612     // Get all the optimization information for loop "loopNum"; This information is held in "optInfo" array.
613     // If NULL this does not allocate the optInfo[loopNum] array for "loopNum"
614     ExpandArrayStack<LcOptInfo*>* GetLoopOptInfo(unsigned loopNum);
615
616     // Cancel all optimizations for loop "loopNum" by clearing out the "conditions" member if non-null
617     // and setting the optInfo to "null.", If "null", then the user of this class is not supposed to
618     // clone this loop.
619     void CancelLoopOptInfo(unsigned loopNum);
620
621     // Get the conditions that decide which loop to take for "loopNum." If NULL allocate an empty array.
622     ExpandArrayStack<LC_Condition>* EnsureConditions(unsigned loopNum);
623
624     // Get the conditions for loop. No allocation is performed.
625     ExpandArrayStack<LC_Condition>* GetConditions(unsigned loopNum);
626
627     // Ensure that the "deref" conditions array is allocated.
628     ExpandArrayStack<LC_Array>* EnsureDerefs(unsigned loopNum);
629
630     // Get block conditions for each loop, no allocation is performed.
631     ExpandArrayStack<ExpandArrayStack<LC_Condition>*>* GetBlockConditions(unsigned loopNum);
632
633     // Ensure that the block condition is present, if not allocate space.
634     ExpandArrayStack<ExpandArrayStack<LC_Condition>*>* EnsureBlockConditions(unsigned loopNum, unsigned totalBlocks);
635
636     // Print the block conditions for the loop.
637     void PrintBlockConditions(unsigned loopNum);
638
639     // Does the loop have block conditions?
640     bool HasBlockConditions(unsigned loopNum);
641
642     // Evaluate the conditions for "loopNum" and indicate if they are either all true or any of them are false.
643     // "pAllTrue" implies all the conditions are statically known to be true.
644     // "pAnyFalse" implies at least one condition is statically known to be false.
645     // If neither of them are true, then some conditions' evaluations are statically unknown.
646     //
647     // If all conditions yield true, then the caller doesn't need to clone the loop, but it can perform
648     // fast path optimizations.
649     // If any condition yields false, then the caller needs to abort cloning the loop (neither clone nor
650     // fast path optimizations.)
651     //
652     // Assumes the conditions involve an AND join operator.
653     void EvaluateConditions(unsigned loopNum, bool* pAllTrue, bool* pAnyFalse DEBUGARG(bool verbose));
654
655 private:
656     void OptimizeConditions(ExpandArrayStack<LC_Condition>& conds);
657
658 public:
659     // Optimize conditions to remove redundant conditions.
660     void OptimizeConditions(unsigned loopNum DEBUGARG(bool verbose));
661
662     void OptimizeBlockConditions(unsigned loopNum DEBUGARG(bool verbose));
663
664 #ifdef DEBUG
665     void PrintConditions(unsigned loopNum);
666 #endif
667 };