48
48
// Arguments:
49
49
// comp - The compiler instance
50
50
// statement - The statement containing the bounds check
51
- // bndChkNode - The bounds check node
52
- // bndChkParentNode - The parent node of the bounds check node (either null or COMMA )
51
+ // statementIdx - The index of the statement in the block
52
+ // bndChk - The bounds check node (its use edge )
53
53
//
54
54
// Return Value:
55
55
// true if the initialization was successful, false otherwise.
56
56
//
57
- bool BoundsCheckInfo::Initialize (const Compiler* comp, Statement* statement, GenTree** bndChk)
57
+ bool BoundsCheckInfo::Initialize (const Compiler* comp, Statement* statement, int statementIdx, GenTree** bndChk)
58
58
{
59
59
assert ((bndChk != nullptr ) && ((*bndChk) != nullptr ));
60
60
61
61
stmt = statement;
62
+ stmtIdx = statementIdx;
62
63
bndChkUse = bndChk;
63
64
idxVN = comp->vnStore ->VNConservativeNormalValue (BndChk ()->GetIndex ()->gtVNPair );
64
65
lenVN = comp->vnStore ->VNConservativeNormalValue (BndChk ()->GetArrayLength ()->gtVNPair );
@@ -98,10 +99,9 @@ bool BoundsCheckInfo::Initialize(const Compiler* comp, Statement* statement, Gen
98
99
// RemoveBoundsChk - Remove the given bounds check from the statement and the block.
99
100
//
100
101
// Arguments:
101
- // comp - compiler instance
102
- // check - bounds check node to remove
103
- // comma - check's parent node (either null or COMMA)
104
- // stmt - statement containing the bounds check
102
+ // comp - compiler instance
103
+ // treeUse - the bounds check node to remove (its use edge)
104
+ // stmt - the statement containing the bounds check
105
105
//
106
106
static void RemoveBoundsChk (Compiler* comp, GenTree** treeUse, Statement* stmt)
107
107
{
@@ -155,18 +155,21 @@ static void RemoveBoundsChk(Compiler* comp, GenTree** treeUse, Statement* stmt)
155
155
// comp - The compiler instance
156
156
// block - The block to clone
157
157
// bndChkStack - The stack of bounds checks to clone
158
+ // lastStmt - The last statement in the block (the block is split after this statement)
158
159
//
159
160
// Return Value:
160
- // The block containing the fast path .
161
+ // The next block to visit after the cloning .
161
162
//
162
- static BasicBlock* optRangeCheckCloning_DoClone (Compiler* comp, BasicBlock* block, BoundsCheckInfoStack* bndChkStack)
163
+ static BasicBlock* optRangeCheckCloning_DoClone (Compiler* comp,
164
+ BasicBlock* block,
165
+ BoundsCheckInfoStack* bndChkStack,
166
+ Statement* lastStmt)
163
167
{
164
168
assert (block != nullptr );
165
169
assert (bndChkStack->Height () > 0 );
166
170
167
171
// The bound checks are in the execution order (top of the stack is the last check)
168
172
BoundsCheckInfo firstCheck = bndChkStack->Bottom ();
169
- BoundsCheckInfo lastCheck = bndChkStack->Top ();
170
173
BasicBlock* prevBb = block;
171
174
172
175
// First, split the block at the first bounds check using gtSplitTree (via fgSplitBlockBeforeTree):
@@ -187,7 +190,7 @@ static BasicBlock* optRangeCheckCloning_DoClone(Compiler* comp, BasicBlock* bloc
187
190
// Now split the block at the last bounds check using fgSplitBlockAfterStatement:
188
191
// TODO-RangeCheckCloning: call gtSplitTree for lastBndChkStmt as well, to cut off
189
192
// the stuff we don't have to clone.
190
- BasicBlock* lastBb = comp->fgSplitBlockAfterStatement (fastpathBb, lastCheck. stmt );
193
+ BasicBlock* lastBb = comp->fgSplitBlockAfterStatement (fastpathBb, lastStmt );
191
194
192
195
DebugInfo debugInfo = fastpathBb->firstStmt ()->GetDebugInfo ();
193
196
@@ -359,6 +362,7 @@ class BoundsChecksVisitor final : public GenTreeVisitor<BoundsChecksVisitor>
359
362
{
360
363
Statement* m_stmt;
361
364
ArrayStack<BoundCheckLocation>* m_boundsChks;
365
+ int m_stmtIdx;
362
366
363
367
public:
364
368
enum
@@ -368,10 +372,14 @@ class BoundsChecksVisitor final : public GenTreeVisitor<BoundsChecksVisitor>
368
372
UseExecutionOrder = true
369
373
};
370
374
371
- BoundsChecksVisitor (Compiler* compiler, Statement* stmt, ArrayStack<BoundCheckLocation>* bndChkLocations)
375
+ BoundsChecksVisitor (Compiler* compiler,
376
+ Statement* stmt,
377
+ int stmtIdx,
378
+ ArrayStack<BoundCheckLocation>* bndChkLocations)
372
379
: GenTreeVisitor(compiler)
373
380
, m_stmt(stmt)
374
381
, m_boundsChks(bndChkLocations)
382
+ , m_stmtIdx(stmtIdx)
375
383
{
376
384
}
377
385
@@ -389,7 +397,7 @@ class BoundsChecksVisitor final : public GenTreeVisitor<BoundsChecksVisitor>
389
397
{
390
398
if ((*use)->OperIs (GT_BOUNDS_CHECK))
391
399
{
392
- m_boundsChks->Push (BoundCheckLocation (m_stmt, use));
400
+ m_boundsChks->Push (BoundCheckLocation (m_stmt, use, m_stmtIdx ));
393
401
}
394
402
return fgWalkResult::WALK_CONTINUE;
395
403
}
@@ -401,6 +409,7 @@ class BoundsChecksVisitor final : public GenTreeVisitor<BoundsChecksVisitor>
401
409
// the bounds checks.
402
410
//
403
411
// Arguments:
412
+ // comp - The compiler instance
404
413
// bndChks - The stack of bounds checks
405
414
//
406
415
// Return Value:
@@ -499,8 +508,10 @@ PhaseStatus Compiler::optRangeCheckCloning()
499
508
bndChkLocations.Reset ();
500
509
bndChkMap.RemoveAll ();
501
510
511
+ int stmtIdx = -1 ;
502
512
for (Statement* const stmt : block->Statements ())
503
513
{
514
+ stmtIdx++;
504
515
if (block->HasTerminator () && (stmt == block->lastStmt ()))
505
516
{
506
517
// TODO-RangeCheckCloning: Splitting these blocks at the last statements
@@ -510,7 +521,7 @@ PhaseStatus Compiler::optRangeCheckCloning()
510
521
511
522
// Now just record all the bounds checks in the block (in the execution order)
512
523
//
513
- BoundsChecksVisitor visitor (this , stmt, &bndChkLocations);
524
+ BoundsChecksVisitor visitor (this , stmt, stmtIdx, &bndChkLocations);
514
525
visitor.WalkTree (stmt->GetRootNodePointer (), nullptr );
515
526
}
516
527
@@ -528,7 +539,7 @@ PhaseStatus Compiler::optRangeCheckCloning()
528
539
{
529
540
BoundCheckLocation loc = bndChkLocations.Bottom (i);
530
541
BoundsCheckInfo bci{};
531
- if (bci.Initialize (this , loc.stmt , loc.bndChkUse ))
542
+ if (bci.Initialize (this , loc.stmt , loc.stmtIdx , loc. bndChkUse ))
532
543
{
533
544
IdxLenPair key (bci.idxVN , bci.lenVN );
534
545
BoundsCheckInfoStack** value = bndChkMap.LookupPointerOrAdd (key, nullptr );
@@ -552,34 +563,72 @@ PhaseStatus Compiler::optRangeCheckCloning()
552
563
}
553
564
554
565
// Now choose the largest group of bounds checks (the one with the most checks)
555
- BoundsCheckInfoStack* largestGroup = nullptr ;
566
+ ArrayStack<BoundsCheckInfoStack*> groups (getAllocator (CMK_RangeCheckCloning));
567
+
556
568
for (BoundsCheckInfoMap::Node* keyValuePair : BoundsCheckInfoMap::KeyValueIteration (&bndChkMap))
557
569
{
558
570
ArrayStack<BoundsCheckInfo>* value = keyValuePair->GetValue ();
559
- if ((largestGroup == nullptr ) || ( value->Height () > largestGroup-> Height () ))
571
+ if ((value->Height () >= MIN_CHECKS_PER_GROUP) && ! DoesComplexityExceed ( this , value ))
560
572
{
561
- if (DoesComplexityExceed (this , value))
562
- {
563
- continue ;
564
- }
565
- largestGroup = value;
573
+ groups.Push (value);
566
574
}
567
575
}
568
576
569
- if (largestGroup == nullptr )
577
+ if (groups. Height () == 0 )
570
578
{
571
579
JITDUMP (" No suitable group of bounds checks in the block - bail out.\n " );
572
580
continue ;
573
581
}
574
582
575
- if (largestGroup->Height () < MIN_CHECKS_PER_GROUP)
583
+ // We have multiple groups of bounds checks in the block.
584
+ // let's pick a group that appears first in the block and the one whose last bounds check
585
+ // appears last in the block.
586
+ //
587
+ BoundsCheckInfoStack* firstGroup = groups.Top ();
588
+ BoundsCheckInfoStack* lastGroup = groups.Top ();
589
+ for (int i = 0 ; i < groups.Height (); i++)
576
590
{
577
- JITDUMP (" Not enough bounds checks in the largest group - bail out.\n " );
578
- continue ;
591
+ BoundsCheckInfoStack* group = groups.Bottom (i);
592
+ int firstStmt = group->Bottom ().stmtIdx ;
593
+ int secondStmt = group->Top ().stmtIdx ;
594
+ if (firstStmt < firstGroup->Bottom ().stmtIdx )
595
+ {
596
+ firstGroup = group;
597
+ }
598
+ if (secondStmt > lastGroup->Top ().stmtIdx )
599
+ {
600
+ lastGroup = group;
601
+ }
602
+ }
603
+
604
+ // We're going to clone for the first group.
605
+ // But let's see if we can extend the end of the group so future iterations
606
+ // can fit more groups in the same block.
607
+ //
608
+ Statement* lastStmt = firstGroup->Top ().stmt ;
609
+
610
+ int firstGroupStarts = firstGroup->Bottom ().stmtIdx ;
611
+ int firstGroupEnds = firstGroup->Top ().stmtIdx ;
612
+ int lastGroupStarts = lastGroup->Bottom ().stmtIdx ;
613
+ int lastGroupEnds = lastGroup->Top ().stmtIdx ;
614
+
615
+ // The only requirement is that both groups must overlap - we don't want to
616
+ // end up cloning unrelated statements between them (not a correctness issue,
617
+ // just a heuristic to avoid cloning too much).
618
+ //
619
+ if (firstGroupEnds < lastGroupEnds && firstGroupEnds >= lastGroupStarts)
620
+ {
621
+ lastStmt = lastGroup->Top ().stmt ;
579
622
}
580
623
581
- JITDUMP (" Cloning bounds checks in " FMT_BB " \n " , block->bbNum );
582
- block = optRangeCheckCloning_DoClone (this , block, largestGroup);
624
+ JITDUMP (" Cloning bounds checks in " FMT_BB " from " FMT_STMT " to " FMT_STMT " \n " , block->bbNum ,
625
+ firstGroup->Bottom ().stmt ->GetID (), lastStmt->GetID ());
626
+
627
+ BasicBlock* nextBbToVisit = optRangeCheckCloning_DoClone (this , block, firstGroup, lastStmt);
628
+ assert (nextBbToVisit != nullptr );
629
+ // optRangeCheckCloning_DoClone wants us to visit nextBbToVisit next
630
+ block = nextBbToVisit->Prev ();
631
+ assert (block != nullptr );
583
632
modified = true ;
584
633
}
585
634
0 commit comments