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
6XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
7XX XX
8XX LoopCloning XX
9XX XX
10XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
11XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
12*/
13
14#include "jitpch.h"
15
16//--------------------------------------------------------------------------------------------------
17// ToGenTree - Convert an arrLen operation into a gentree node.
18//
19// Arguments:
20// comp Compiler instance to allocate trees
21//
22// Return Values:
23// Returns the gen tree representation for arrLen or MD Array node as defined by
24// the "type" member
25//
26// Notes:
27// This tree produces GT_INDEX node, the caller is supposed to morph it appropriately
28// so it can be codegen'ed.
29//
30GenTree* LC_Array::ToGenTree(Compiler* comp)
31{
32 // If jagged array
33 if (type == Jagged)
34 {
35 // Create a a[i][j][k].length type node.
36 GenTree* arr = comp->gtNewLclvNode(arrIndex->arrLcl, comp->lvaTable[arrIndex->arrLcl].lvType);
37 int rank = GetDimRank();
38 for (int i = 0; i < rank; ++i)
39 {
40 arr = comp->gtNewIndexRef(TYP_REF, arr, comp->gtNewLclvNode(arrIndex->indLcls[i],
41 comp->lvaTable[arrIndex->indLcls[i]].lvType));
42 }
43 // If asked for arrlen invoke arr length operator.
44 if (oper == ArrLen)
45 {
46 GenTree* arrLen = comp->gtNewArrLen(TYP_INT, arr, OFFSETOF__CORINFO_Array__length);
47 return arrLen;
48 }
49 else
50 {
51 assert(oper == None);
52 return arr;
53 }
54 }
55 else
56 {
57 // TODO-CQ: Optimize for MD Array.
58 assert(!"Optimize for MD Array");
59 }
60 return nullptr;
61}
62
63//--------------------------------------------------------------------------------------------------
64// ToGenTree - Convert an "identifier" into a gentree node.
65//
66// Arguments:
67// comp Compiler instance to allocate trees
68//
69// Return Values:
70// Returns the gen tree representation for either a constant or a variable or an arrLen operation
71// defined by the "type" member
72//
73GenTree* LC_Ident::ToGenTree(Compiler* comp)
74{
75 // Convert to GenTree nodes.
76 switch (type)
77 {
78 case Const:
79 assert(constant <= INT32_MAX);
80 return comp->gtNewIconNode(constant);
81 case Var:
82 return comp->gtNewLclvNode(constant, comp->lvaTable[constant].lvType);
83 case ArrLen:
84 return arrLen.ToGenTree(comp);
85 case Null:
86 return comp->gtNewIconNode(0, TYP_REF);
87 default:
88 assert(!"Could not convert LC_Ident to GenTree");
89 unreached();
90 break;
91 }
92}
93
94//--------------------------------------------------------------------------------------------------
95// ToGenTree - Convert an "expression" into a gentree node.
96//
97// Arguments:
98// comp Compiler instance to allocate trees
99//
100// Return Values:
101// Returns the gen tree representation for either a constant or a variable or an arrLen operation
102// defined by the "type" member
103//
104GenTree* LC_Expr::ToGenTree(Compiler* comp)
105{
106 // Convert to GenTree nodes.
107 switch (type)
108 {
109 case Ident:
110 return ident.ToGenTree(comp);
111 default:
112 assert(!"Could not convert LC_Expr to GenTree");
113 unreached();
114 break;
115 }
116}
117
118//--------------------------------------------------------------------------------------------------
119// ToGenTree - Convert a "condition" into a gentree node.
120//
121// Arguments:
122// comp Compiler instance to allocate trees
123//
124// Return Values:
125// Returns the gen tree representation for the conditional operator on lhs and rhs trees
126//
127GenTree* LC_Condition::ToGenTree(Compiler* comp)
128{
129 GenTree* op1Tree = op1.ToGenTree(comp);
130 GenTree* op2Tree = op2.ToGenTree(comp);
131 assert(genTypeSize(genActualType(op1Tree->TypeGet())) == genTypeSize(genActualType(op2Tree->TypeGet())));
132 return comp->gtNewOperNode(oper, TYP_INT, op1Tree, op2Tree);
133}
134
135//--------------------------------------------------------------------------------------------------
136// Evaluates - Evaluate a given loop cloning condition if it can be statically evaluated.
137//
138// Arguments:
139// pResult The evaluation result
140//
141// Return Values:
142// Returns true if the condition can be statically evaluated. If the condition's result
143// is statically unknown then return false. In other words, true if "pResult" is valid.
144//
145bool LC_Condition::Evaluates(bool* pResult)
146{
147 switch (oper)
148 {
149 case GT_EQ:
150 case GT_GE:
151 case GT_LE:
152 // If op1 == op2 then equality should result in true.
153 if (op1 == op2)
154 {
155 *pResult = true;
156 return true;
157 }
158 break;
159
160 case GT_GT:
161 case GT_LT:
162 case GT_NE:
163 // If op1 == op2 then inequality should result in false.
164 if (op1 == op2)
165 {
166 *pResult = false;
167 return true;
168 }
169 break;
170
171 default:
172 // for all other 'oper' kinds, we will return false
173 break;
174 }
175 return false;
176}
177
178//--------------------------------------------------------------------------------------------------
179// Combines - Check whether two conditions would combine to yield a single new condition.
180//
181// Arguments:
182// cond The condition that is checked if it would combine with "*this" condition.
183// newCond The resulting combined condition.
184//
185// Return Values:
186// Returns true if "cond" combines with the "this" condition.
187// "newCond" contains the combines condition.
188//
189// Operation:
190// Check if both conditions are equal. If so, return just 1 of them.
191// Reverse their operators and check if their reversed operands match. If so, return either of them.
192//
193// Notes:
194// This is not a full-fledged expression optimizer, it is supposed
195// to remove redundant conditions that are generated for optimization
196// opportunities. Anything further should be implemented as needed.
197// For example, for (i = beg; i < end; i += inc) a[i]. Then, the conditions
198// would be: "beg >= 0, end <= a.len, inc > 0"
199bool LC_Condition::Combines(const LC_Condition& cond, LC_Condition* newCond)
200{
201 if (oper == cond.oper && op1 == cond.op1 && op2 == cond.op2)
202 {
203 *newCond = *this;
204 return true;
205 }
206 else if ((oper == GT_LT || oper == GT_LE || oper == GT_GT || oper == GT_GE) &&
207 GenTree::ReverseRelop(oper) == cond.oper && op1 == cond.op2 && op2 == cond.op1)
208 {
209 *newCond = *this;
210 return true;
211 }
212 return false;
213}
214
215//--------------------------------------------------------------------------------------------------
216// GetLoopOptInfo - Retrieve the loop opt info candidate array.
217//
218// Arguments:
219// loopNum the loop index.
220//
221// Return Values:
222// Return the optInfo array member. The method doesn't allocate memory.
223//
224JitExpandArrayStack<LcOptInfo*>* LoopCloneContext::GetLoopOptInfo(unsigned loopNum)
225{
226 return optInfo[loopNum];
227}
228
229//--------------------------------------------------------------------------------------------------
230// CancelLoopOptInfo - Cancel loop cloning optimization for this loop.
231//
232// Arguments:
233// loopNum the loop index.
234//
235// Return Values:
236// None.
237//
238void LoopCloneContext::CancelLoopOptInfo(unsigned loopNum)
239{
240 JITDUMP("Cancelling loop cloning for loop L_%02u\n", loopNum);
241 optInfo[loopNum] = nullptr;
242 if (conditions[loopNum] != nullptr)
243 {
244 conditions[loopNum]->Reset();
245 conditions[loopNum] = nullptr;
246 }
247}
248
249//--------------------------------------------------------------------------------------------------
250// EnsureLoopOptInfo - Retrieve the loop opt info candidate array, if it is not present, allocate
251// memory.
252//
253// Arguments:
254// loopNum the loop index.
255//
256// Return Values:
257// The array of optimization candidates for the loop.
258//
259JitExpandArrayStack<LcOptInfo*>* LoopCloneContext::EnsureLoopOptInfo(unsigned loopNum)
260{
261 if (optInfo[loopNum] == nullptr)
262 {
263 optInfo[loopNum] = new (alloc) JitExpandArrayStack<LcOptInfo*>(alloc, 4);
264 }
265 return optInfo[loopNum];
266}
267
268//--------------------------------------------------------------------------------------------------
269// EnsureLoopOptInfo - Retrieve the loop cloning conditions candidate array,
270// if it is not present, allocate memory.
271//
272// Arguments:
273// loopNum the loop index.
274//
275// Return Values:
276// The array of cloning conditions for the loop.
277//
278JitExpandArrayStack<LC_Condition>* LoopCloneContext::EnsureConditions(unsigned loopNum)
279{
280 if (conditions[loopNum] == nullptr)
281 {
282 conditions[loopNum] = new (alloc) JitExpandArrayStack<LC_Condition>(alloc, 4);
283 }
284 return conditions[loopNum];
285}
286
287//--------------------------------------------------------------------------------------------------
288// GetConditions - Get the cloning conditions array for the loop, no allocation.
289//
290// Arguments:
291// loopNum the loop index.
292//
293// Return Values:
294// The array of cloning conditions for the loop.
295//
296JitExpandArrayStack<LC_Condition>* LoopCloneContext::GetConditions(unsigned loopNum)
297{
298 return conditions[loopNum];
299}
300
301//--------------------------------------------------------------------------------------------------
302// EnsureDerefs - Ensure an array of dereferences is created if it doesn't exist.
303//
304// Arguments:
305// loopNum the loop index.
306//
307// Return Values:
308// The array of dereferences for the loop.
309//
310JitExpandArrayStack<LC_Array>* LoopCloneContext::EnsureDerefs(unsigned loopNum)
311{
312 if (derefs[loopNum] == nullptr)
313 {
314 derefs[loopNum] = new (alloc) JitExpandArrayStack<LC_Array>(alloc, 4);
315 }
316 return derefs[loopNum];
317}
318
319//--------------------------------------------------------------------------------------------------
320// HasBlockConditions - Check if there are block level conditions for the loop.
321//
322// Arguments:
323// loopNum the loop index.
324//
325// Return Values:
326// Return true if there are any block level conditions.
327//
328bool LoopCloneContext::HasBlockConditions(unsigned loopNum)
329{
330 JitExpandArrayStack<JitExpandArrayStack<LC_Condition>*>* levelCond = blockConditions[loopNum];
331 if (levelCond == nullptr)
332 {
333 return false;
334 }
335
336 // Walk through each block to check if any of them has conditions.
337 for (unsigned i = 0; i < levelCond->Size(); ++i)
338 {
339 if ((*levelCond)[i]->Size() > 0)
340 {
341 return true;
342 }
343 }
344 return false;
345}
346
347//--------------------------------------------------------------------------------------------------
348// GetBlockConditions - Return block level conditions for the loop.
349//
350// Arguments:
351// loopNum the loop index.
352//
353// Return Values:
354// Return block conditions.
355//
356JitExpandArrayStack<JitExpandArrayStack<LC_Condition>*>* LoopCloneContext::GetBlockConditions(unsigned loopNum)
357{
358 assert(HasBlockConditions(loopNum));
359 return blockConditions[loopNum];
360}
361
362//--------------------------------------------------------------------------------------------------
363// EnsureBlockConditions - Allocate block level conditions for the loop if not exists.
364//
365// Arguments:
366// loopNum the loop index.
367// condBlocks the number of block-level conditions for each loop, corresponding to the blocks
368// created.
369//
370// Return Values:
371// Return block conditions.
372//
373JitExpandArrayStack<JitExpandArrayStack<LC_Condition>*>* LoopCloneContext::EnsureBlockConditions(unsigned loopNum,
374 unsigned condBlocks)
375{
376 if (blockConditions[loopNum] == nullptr)
377 {
378 blockConditions[loopNum] =
379 new (alloc) JitExpandArrayStack<JitExpandArrayStack<LC_Condition>*>(alloc, condBlocks);
380 }
381 JitExpandArrayStack<JitExpandArrayStack<LC_Condition>*>* levelCond = blockConditions[loopNum];
382 for (unsigned i = 0; i < condBlocks; ++i)
383 {
384 levelCond->Set(i, new (alloc) JitExpandArrayStack<LC_Condition>(alloc));
385 }
386 return levelCond;
387}
388
389#ifdef DEBUG
390void LoopCloneContext::PrintBlockConditions(unsigned loopNum)
391{
392 JitExpandArrayStack<JitExpandArrayStack<LC_Condition>*>* levelCond = blockConditions[loopNum];
393 if (levelCond == nullptr || levelCond->Size() == 0)
394 {
395 JITDUMP("No block conditions\n");
396 return;
397 }
398
399 for (unsigned i = 0; i < levelCond->Size(); ++i)
400 {
401 JITDUMP("%d = {", i);
402 for (unsigned j = 0; j < ((*levelCond)[i])->Size(); ++j)
403 {
404 if (j != 0)
405 {
406 JITDUMP(" & ");
407 }
408 (*((*levelCond)[i]))[j].Print();
409 }
410 JITDUMP("}\n");
411 }
412}
413#endif
414
415//--------------------------------------------------------------------------------------------------
416// EvaluateConditions - Evaluate the loop cloning conditions statically, if it can be evaluated.
417//
418// Arguments:
419// loopNum the loop index.
420// pAllTrue all the cloning conditions evaluated to "true" statically.
421// pAnyFalse some cloning condition evaluated to "false" statically.
422// verbose verbose logging required.
423//
424// Return Values:
425// None.
426//
427// Operation:
428// For example, a condition like "V02 >= V02" statically evaluates to true. Caller should detect such
429// conditions and remove them from the "conditions" array.
430//
431// Similarly, conditions like "V02 > V02" will evaluate to "false". In this case caller has to abort
432// loop cloning optimization for the loop. Note that the assumption for conditions is that they will
433// all be "AND"ed, so statically we know we will never take the fast path.
434//
435// Sometimes we simply can't say statically whether "V02 > V01.length" is true or false.
436// In that case, the "pAllTrue" will be false because this condition doesn't evaluate to "true" and
437// "pAnyFalse" could be false if no other condition statically evaluates to "false".
438void LoopCloneContext::EvaluateConditions(unsigned loopNum, bool* pAllTrue, bool* pAnyFalse DEBUGARG(bool verbose))
439{
440 bool allTrue = true;
441 bool anyFalse = false;
442
443 JitExpandArrayStack<LC_Condition>& conds = *conditions[loopNum];
444
445 JITDUMP("Evaluating %d loop cloning conditions for loop %d\n", conds.Size(), loopNum);
446
447 assert(conds.Size() > 0);
448 for (unsigned i = 0; i < conds.Size(); ++i)
449 {
450#ifdef DEBUG
451 if (verbose)
452 {
453 printf("Considering condition %d: (", i);
454 conds[i].Print();
455 }
456#endif
457
458 bool res = false;
459 // Check if this condition evaluates to true or false.
460 if (conds[i].Evaluates(&res))
461 {
462 JITDUMP(") evaluates to %d\n", res);
463 if (!res)
464 {
465 anyFalse = true;
466 return;
467 }
468 }
469 else
470 {
471 JITDUMP("), could not be evaluated\n");
472 allTrue = false;
473 }
474 }
475
476 JITDUMP("Evaluation result allTrue = %d, anyFalse = %d\n", allTrue, anyFalse);
477 *pAllTrue = allTrue;
478 *pAnyFalse = anyFalse;
479}
480
481//--------------------------------------------------------------------------------------------------
482// OptimizeConditions - Evaluate the loop cloning conditions statically, if they can be evaluated
483// then optimize the "conditions" array accordingly.
484//
485// Arguments:
486// conds The conditions array to optimize.
487//
488// Return Values:
489// None.
490//
491// Operation:
492// For example, a condition like "V02 >= V02" statically evaluates to true. Remove such conditions
493// from the "conditions" array.
494//
495// Similarly, conditions like "V02 > V02" will evaluate to "false". In this case abort loop cloning
496// optimization for the loop.
497//
498// Sometimes, two conditions will combine together to yield a single condition, then remove a
499// duplicate condition.
500void LoopCloneContext::OptimizeConditions(JitExpandArrayStack<LC_Condition>& conds)
501{
502 for (unsigned i = 0; i < conds.Size(); ++i)
503 {
504 // Check if the conditions evaluate.
505 bool result = false;
506 if (conds[i].Evaluates(&result))
507 {
508 // If statically known to be true, then remove this condition.
509 if (result)
510 {
511 conds.Remove(i);
512 --i;
513 continue;
514 }
515 else
516 {
517 // Some condition is statically false, then simply indicate
518 // not to clone this loop.
519 CancelLoopOptInfo(i);
520 break;
521 }
522 }
523
524 // Check for all other conditions[j], if it would combine with
525 // conditions[i].
526 for (unsigned j = i + 1; j < conds.Size(); ++j)
527 {
528 LC_Condition newCond;
529 if (conds[i].Combines(conds[j], &newCond))
530 {
531 conds.Remove(j);
532 conds[i] = newCond;
533 i = -1;
534 break;
535 }
536 }
537 }
538#ifdef DEBUG
539 // Make sure we didn't miss some combining.
540 for (unsigned i = 0; i < conds.Size(); ++i)
541 {
542 for (unsigned j = 0; j < conds.Size(); ++j)
543 {
544 LC_Condition newCond;
545 if ((i != j) && conds[i].Combines(conds[j], &newCond))
546 {
547 assert(!"Loop cloning conditions can still be optimized further.");
548 }
549 }
550 }
551#endif
552}
553
554//--------------------------------------------------------------------------------------------------
555// OptimizeBlockConditions - Optimize block level conditions.
556//
557// Arguments:
558// loopNum the loop index.
559//
560// Operation:
561// Calls OptimizeConditions helper on block level conditions.
562//
563// Return Values:
564// None.
565//
566void LoopCloneContext::OptimizeBlockConditions(unsigned loopNum DEBUGARG(bool verbose))
567{
568 if (!HasBlockConditions(loopNum))
569 {
570 return;
571 }
572 JitExpandArrayStack<JitExpandArrayStack<LC_Condition>*>* levelCond = blockConditions[loopNum];
573 for (unsigned i = 0; i < levelCond->Size(); ++i)
574 {
575 OptimizeConditions(*((*levelCond)[i]));
576 }
577#ifdef DEBUG
578 if (verbose)
579 {
580 printf("After optimizing block-level cloning conditions\n\t");
581 PrintConditions(loopNum);
582 printf("\n");
583 }
584#endif
585}
586
587//--------------------------------------------------------------------------------------------------
588// OptimizeConditions - Optimize cloning conditions.
589//
590// Arguments:
591// loopNum the loop index.
592// verbose verbose logging required.
593//
594// Operation:
595// Calls OptimizeConditions helper on cloning conditions.
596//
597// Return Values:
598// None.
599//
600void LoopCloneContext::OptimizeConditions(unsigned loopNum DEBUGARG(bool verbose))
601{
602#ifdef DEBUG
603 if (verbose)
604 {
605 printf("Before optimizing cloning conditions\n\t");
606 PrintConditions(loopNum);
607 printf("\n");
608 }
609#endif
610 JitExpandArrayStack<LC_Condition>& conds = *conditions[loopNum];
611 OptimizeConditions(conds);
612
613#ifdef DEBUG
614 if (verbose)
615 {
616 printf("After optimizing cloning conditions\n\t");
617 PrintConditions(loopNum);
618 printf("\n");
619 }
620#endif
621}
622
623#ifdef DEBUG
624//--------------------------------------------------------------------------------------------------
625// PrintConditions - Print loop cloning conditions necessary to clone the loop.
626//
627// Arguments:
628// loopNum the loop index.
629//
630// Return Values:
631// None.
632//
633void LoopCloneContext::PrintConditions(unsigned loopNum)
634{
635 if (conditions[loopNum] == nullptr)
636 {
637 JITDUMP("NO conditions");
638 return;
639 }
640 if (conditions[loopNum]->Size() == 0)
641 {
642 JITDUMP("Conditions were optimized away! Will always take cloned path.");
643 }
644 for (unsigned i = 0; i < conditions[loopNum]->Size(); ++i)
645 {
646 if (i != 0)
647 {
648 JITDUMP(" & ");
649 }
650 (*conditions[loopNum])[i].Print();
651 }
652}
653#endif
654
655//--------------------------------------------------------------------------------------------------
656// CondToStmtInBlock - Convert an array of conditions. Evaluate them into a JTRUE stmt and add it to
657// the block
658//
659// Arguments:
660// comp Compiler instance
661// conds Array of conditions to evaluate into a JTRUE stmt
662// block Block to insert the stmt into
663// reverse Reverse conditions if true.
664//
665// Note:
666// The condition that will be generated: jmpTrue(cond1 & cond2 ... == 0)
667//
668// Return Values:
669// None.
670//
671void LoopCloneContext::CondToStmtInBlock(Compiler* comp,
672 JitExpandArrayStack<LC_Condition>& conds,
673 BasicBlock* block,
674 bool reverse)
675{
676 noway_assert(conds.Size() > 0);
677
678 // Get the first condition.
679 GenTree* cond = conds[0].ToGenTree(comp);
680 for (unsigned i = 1; i < conds.Size(); ++i)
681 {
682 // Append all conditions using AND operator.
683 cond = comp->gtNewOperNode(GT_AND, TYP_INT, cond, conds[i].ToGenTree(comp));
684 }
685
686 // Add "cond == 0" node
687 cond = comp->gtNewOperNode(reverse ? GT_NE : GT_EQ, TYP_INT, cond, comp->gtNewIconNode(0));
688
689 // Add jmpTrue "cond == 0" to slow path.
690 GenTree* stmt = comp->fgNewStmtFromTree(comp->gtNewOperNode(GT_JTRUE, TYP_VOID, cond));
691
692 // Add stmt to the block.
693 comp->fgInsertStmtAtEnd(block, stmt);
694
695 // Remorph.
696 comp->fgMorphBlockStmt(block, stmt->AsStmt() DEBUGARG("Loop cloning condition"));
697}
698
699//--------------------------------------------------------------------------------------------------
700// Lcl - the current node's local variable.
701//
702// Arguments:
703// None.
704//
705// Operation:
706// If level is 0, then just return the array base. Else return the index variable on dim 'level'
707//
708// Return Values:
709// The local variable in the node's level.
710//
711unsigned LC_Deref::Lcl()
712{
713 unsigned lvl = level;
714 if (lvl == 0)
715 {
716 return array.arrIndex->arrLcl;
717 }
718 lvl--;
719 return array.arrIndex->indLcls[lvl];
720}
721
722//--------------------------------------------------------------------------------------------------
723// HasChildren - Check if there are children to 'this' node.
724//
725// Arguments:
726// None.
727//
728// Return Values:
729// Return true if children are present.
730//
731bool LC_Deref::HasChildren()
732{
733 return children != nullptr && children->Size() > 0;
734}
735
736//--------------------------------------------------------------------------------------------------
737// DeriveLevelConditions - Generate conditions for each level of the tree.
738//
739// Arguments:
740// conds An array of conditions for each level i.e., (level x conditions). This array will
741// contain the conditions for the tree at the end of the method.
742//
743// Operation:
744// level0 yields only (a != null) condition. All other levels yield two conditions:
745// (level < a[...].length && a[...][level] != null)
746//
747// Return Values:
748// None
749//
750void LC_Deref::DeriveLevelConditions(JitExpandArrayStack<JitExpandArrayStack<LC_Condition>*>* conds)
751{
752 if (level == 0)
753 {
754 // For level 0, just push (a != null).
755 (*conds)[level]->Push(
756 LC_Condition(GT_NE, LC_Expr(LC_Ident(Lcl(), LC_Ident::Var)), LC_Expr(LC_Ident(LC_Ident::Null))));
757 }
758 else
759 {
760 // Adjust for level0 having just 1 condition and push condition (i < a.len).
761 LC_Array arrLen = array;
762 arrLen.oper = LC_Array::ArrLen;
763 arrLen.dim = level - 1;
764 (*conds)[level * 2 - 1]->Push(
765 LC_Condition(GT_LT, LC_Expr(LC_Ident(Lcl(), LC_Ident::Var)), LC_Expr(LC_Ident(arrLen))));
766
767 // Push condition (a[i] != null)
768 LC_Array arrTmp = array;
769 arrTmp.dim = level;
770 (*conds)[level * 2]->Push(LC_Condition(GT_NE, LC_Expr(LC_Ident(arrTmp)), LC_Expr(LC_Ident(LC_Ident::Null))));
771 }
772
773 // Invoke on the children recursively.
774 if (HasChildren())
775 {
776 for (unsigned i = 0; i < children->Size(); ++i)
777 {
778 (*children)[i]->DeriveLevelConditions(conds);
779 }
780 }
781}
782
783//--------------------------------------------------------------------------------------------------
784// EnsureChildren - Create an array of child nodes if nullptr.
785//
786// Arguments:
787// alloc CompAllocator instance
788//
789// Return Values:
790// None
791//
792void LC_Deref::EnsureChildren(CompAllocator alloc)
793{
794 if (children == nullptr)
795 {
796 children = new (alloc) JitExpandArrayStack<LC_Deref*>(alloc);
797 }
798}
799
800//--------------------------------------------------------------------------------------------------
801// Find - Find the node representing the local variable in child nodes of the 'this' node.
802//
803// Arguments:
804// lcl the local to find in the children array
805//
806// Return Values:
807// The child node if found or nullptr.
808//
809LC_Deref* LC_Deref::Find(unsigned lcl)
810{
811 return Find(children, lcl);
812}
813
814//--------------------------------------------------------------------------------------------------
815// Find - Find the node representing the local variable in a list of nodes.
816//
817// Arguments:
818// lcl the local to find.
819// children the list of nodes to find the node representing the lcl.
820//
821// Return Values:
822// The node if found or nullptr.
823//
824
825// static
826LC_Deref* LC_Deref::Find(JitExpandArrayStack<LC_Deref*>* children, unsigned lcl)
827{
828 if (children == nullptr)
829 {
830 return nullptr;
831 }
832 for (unsigned i = 0; i < children->Size(); ++i)
833 {
834 if ((*children)[i]->Lcl() == lcl)
835 {
836 return (*children)[i];
837 }
838 }
839 return nullptr;
840}
841