@@ -451,21 +451,32 @@ static BuiltinValueKind invertCmpID(BuiltinValueKind ID) {
451
451
}
452
452
}
453
453
454
- // / Checks if Start to End is the range of 0 to the count of an array.
455
- // / Returns the array if this is the case.
456
- static SILValue getZeroToCountArray (SILValue Start , SILValue End ) {
457
- auto *IL = dyn_cast<IntegerLiteralInst>(Start );
458
- if (!IL || IL ->getValue () != 0 )
454
+ // / Checks if Start to End is the range of 0 to the count of an array or a fixed
455
+ // / storage type. Returns the self value if this is the case.
456
+ static SILValue getZeroToCountOfSelf (SILValue start , SILValue end ) {
457
+ auto *intLiteral = dyn_cast<IntegerLiteralInst>(start );
458
+ if (!intLiteral || intLiteral ->getValue () != 0 ) {
459
459
return SILValue ();
460
-
461
- auto *SEI = dyn_cast<StructExtractInst>(End );
462
- if (!SEI)
460
+ }
461
+ auto *sei = dyn_cast<StructExtractInst>(end );
462
+ if (!sei) {
463
463
return SILValue ();
464
-
465
- ArraySemanticsCall SemCall (SEI ->getOperand ());
466
- if (SemCall. getKind () != ArrayCallKind:: kGetCount )
464
+ }
465
+ auto *applyInst = dyn_cast<ApplyInst>(sei ->getOperand ());
466
+ if (!applyInst) {
467
467
return SILValue ();
468
- return SemCall.getSelf ();
468
+ }
469
+ auto *callee = applyInst->getReferencedFunctionOrNull ();
470
+ if (!callee) {
471
+ return SILValue ();
472
+ }
473
+ for (auto attr : callee->getSemanticsAttrs ()) {
474
+ if (attr == " array.get_count" || attr == " fixed_storage.get_count" ) {
475
+ return applyInst->hasSelfArgument () ? applyInst->getSelfArgument ()
476
+ : SILValue ();
477
+ }
478
+ }
479
+ return SILValue ();
469
480
}
470
481
471
482
// / Checks whether the cond_br in the preheader's predecessor ensures that the
@@ -503,7 +514,7 @@ static bool isLessThanCheck(SILValue Start, SILValue End,
503
514
// Special case: if it is a 0-to-count loop, we know that the count cannot
504
515
// be negative. In this case the 'Start < End' check can also be done with
505
516
// 'count != 0'.
506
- return getZeroToCountArray (Start, End);
517
+ return getZeroToCountOfSelf (Start, End);
507
518
default :
508
519
return false ;
509
520
}
@@ -815,6 +826,24 @@ class AccessFunction {
815
826
return getZeroToCountOfSelf (Ind->Start , Ind->End ) == selfValue;
816
827
}
817
828
829
+ SILValue getFirstValue (SILInstruction *insertPt) {
830
+ SILBuilderWithScope builder (insertPt);
831
+ auto firstValue =
832
+ Ind->getFirstValue (insertPt->getLoc (), builder, preIncrement ? 1 : 0 );
833
+ auto intType = SILType::getPrimitiveObjectType (
834
+ builder.getASTContext ().getIntType ()->getCanonicalType ());
835
+ return builder.createStruct (insertPt->getLoc (), intType, {firstValue});
836
+ }
837
+
838
+ SILValue getLastValue (SILInstruction *insertPt) {
839
+ SILBuilderWithScope builder (insertPt);
840
+ auto lastValue =
841
+ Ind->getLastValue (insertPt->getLoc (), builder, preIncrement ? 0 : 1 );
842
+ auto intType = SILType::getPrimitiveObjectType (
843
+ builder.getASTContext ().getIntType ()->getCanonicalType ());
844
+ return builder.createStruct (insertPt->getLoc (), intType, {lastValue});
845
+ }
846
+
818
847
// / Hoists the necessary check for beginning and end of the induction
819
848
// / encapsulated by this access function to the header.
820
849
void hoistCheckToPreheader (ArraySemanticsCall CheckToHoist,
@@ -958,8 +987,12 @@ class BoundsCheckOpts : public SILFunctionTransform {
958
987
std::pair<bool , std::optional<InductionAnalysis>>
959
988
findAndOptimizeInductionVariables (SILLoop *loop);
960
989
961
- bool optimizeArrayBoundsCheckInLoop (SILLoop *loop,
962
- std::optional<InductionAnalysis> indVars);
990
+ bool
991
+ optimizeArrayBoundsCheckInLoop (SILLoop *loop,
992
+ std::optional<InductionAnalysis> &indVars);
993
+
994
+ bool optimizeFixedStorageBoundsCheckInLoop (
995
+ SILLoop *loop, std::optional<InductionAnalysis> &indVars);
963
996
964
997
// / Remove redundant checks in a basic block. This function will reset the
965
998
// / state after an instruction that may modify any array allowing removal of
@@ -979,6 +1012,16 @@ class BoundsCheckOpts : public SILFunctionTransform {
979
1012
InductionAnalysis &indVars,
980
1013
int recursionDepth);
981
1014
1015
+ bool hoistFixedStorageBoundsChecksInLoop (SILLoop *loop,
1016
+ DominanceInfoNode *currentNode,
1017
+ InductionAnalysis &indVars,
1018
+ int recursionDepth);
1019
+
1020
+ bool removeRedundantFixedStorageBoundsChecksInLoop (
1021
+ SILLoop *loop, DominanceInfoNode *currentNode,
1022
+ llvm::DenseSet<std::pair<SILValue, SILValue>> &dominatingSafeChecks,
1023
+ int recursionDepth);
1024
+
982
1025
public:
983
1026
void run () override {
984
1027
bool changed = false ;
@@ -1232,7 +1275,9 @@ bool BoundsCheckOpts::optimizeArrayBoundsCheckInLoop(SILLoop *loop) {
1232
1275
bool changed = false ;
1233
1276
auto result = findAndOptimizeInductionVariables (loop);
1234
1277
changed |= result.first ;
1235
- changed |= optimizeArrayBoundsCheckInLoop (loop, std::move (result.second ));
1278
+ auto indVars = std::move (result.second );
1279
+ changed |= optimizeArrayBoundsCheckInLoop (loop, indVars);
1280
+ changed |= optimizeFixedStorageBoundsCheckInLoop (loop, indVars);
1236
1281
1237
1282
if (changed) {
1238
1283
preheader->getParent ()->verify (
@@ -1341,7 +1386,7 @@ BoundsCheckOpts::findAndOptimizeInductionVariables(SILLoop *loop) {
1341
1386
}
1342
1387
1343
1388
bool BoundsCheckOpts::optimizeArrayBoundsCheckInLoop (
1344
- SILLoop *loop, std::optional<InductionAnalysis> indVars) {
1389
+ SILLoop *loop, std::optional<InductionAnalysis> & indVars) {
1345
1390
1346
1391
// Collect safe arrays. Arrays are safe if there is no function call that
1347
1392
// could mutate their size in the loop.
@@ -1377,6 +1422,31 @@ bool BoundsCheckOpts::optimizeArrayBoundsCheckInLoop(
1377
1422
return changed;
1378
1423
}
1379
1424
1425
+ bool BoundsCheckOpts::optimizeFixedStorageBoundsCheckInLoop (
1426
+ SILLoop *loop, std::optional<InductionAnalysis> &indVars) {
1427
+ // Try removing redundant bounds checks in the loop.
1428
+ LLVM_DEBUG (llvm::dbgs () << " Attempting to eliminate redundant bounds checks "
1429
+ " for Span and InlineArray in "
1430
+ << *loop);
1431
+ llvm::DenseSet<std::pair<SILValue, SILValue>>
1432
+ dominatingSafeFixedStorageChecks;
1433
+ bool changed = removeRedundantFixedStorageBoundsChecksInLoop (
1434
+ loop, DT->getNode (loop->getHeader ()), dominatingSafeFixedStorageChecks,
1435
+ /* recursionDepth*/ 0 );
1436
+
1437
+ // Try hoisting bounds checks from the loop.
1438
+ LLVM_DEBUG (llvm::dbgs ()
1439
+ << " Attempting to hoist bounds checks for Span and InlineArray in "
1440
+ << *loop);
1441
+ if (!indVars) {
1442
+ LLVM_DEBUG (llvm::dbgs () << " No induction variables found\n " );
1443
+ return changed;
1444
+ }
1445
+ changed |= hoistFixedStorageBoundsChecksInLoop (
1446
+ loop, DT->getNode (loop->getHeader ()), *indVars, /* recursionDepth*/ 0 );
1447
+ return changed;
1448
+ }
1449
+
1380
1450
bool BoundsCheckOpts::hoistArrayBoundsChecksInLoop (
1381
1451
SILLoop *loop, DominanceInfoNode *currentNode, ABCAnalysis &abcAnalysis,
1382
1452
InductionAnalysis &indVars, int recursionDepth) {
@@ -1488,6 +1558,171 @@ bool BoundsCheckOpts::hoistArrayBoundsChecksInLoop(
1488
1558
return changed;
1489
1559
}
1490
1560
1561
+ bool BoundsCheckOpts::hoistFixedStorageBoundsChecksInLoop (
1562
+ SILLoop *loop, DominanceInfoNode *currentNode, InductionAnalysis &indVars,
1563
+ int recursionDepth) {
1564
+ auto preheader = loop->getLoopPreheader ();
1565
+ auto singleExitingBlock = loop->getExitingBlock ();
1566
+ // Avoid a stack overflow for very deep dominator trees.
1567
+ if (recursionDepth >= maxRecursionDepth)
1568
+ return false ;
1569
+
1570
+ bool changed = false ;
1571
+ auto *curBlock = currentNode->getBlock ();
1572
+ bool blockAlwaysExecutes =
1573
+ isGuaranteedToBeExecuted (DT, curBlock, singleExitingBlock);
1574
+
1575
+ for (auto instIt = curBlock->begin (); instIt != curBlock->end ();) {
1576
+ auto inst = &*instIt;
1577
+ ++instIt;
1578
+
1579
+ FixedStorageSemanticsCall fixedStorageSemantics (inst);
1580
+ if (!fixedStorageSemantics ||
1581
+ fixedStorageSemantics.getKind () !=
1582
+ FixedStorageSemanticsCallKind::CheckIndex) {
1583
+ continue ;
1584
+ }
1585
+
1586
+ if (!fixedStorageSemantics->hasSelfArgument ()) {
1587
+ continue ;
1588
+ }
1589
+
1590
+ auto selfValue = fixedStorageSemantics->getSelfArgument ();
1591
+
1592
+ if (!DT->dominates (selfValue->getParentBlock (), preheader)) {
1593
+ LLVM_DEBUG (llvm::dbgs ()
1594
+ << " " << *selfValue << " does not dominate preheader\n " );
1595
+ continue ;
1596
+ }
1597
+
1598
+ auto indexValue = fixedStorageSemantics->getArgument (0 );
1599
+
1600
+ // If the bounds check is loop invariant, hoist it.
1601
+ if (blockAlwaysExecutes && dominates (DT, indexValue, preheader)) {
1602
+ LLVM_DEBUG (llvm::dbgs () << " Invariant bounds check removed\n " );
1603
+ changed = true ;
1604
+ fixedStorageSemantics->moveBefore (preheader->getTerminator ());
1605
+ continue ;
1606
+ }
1607
+
1608
+ auto accessFunction =
1609
+ AccessFunction::getLinearFunction (indexValue, indVars);
1610
+ if (!accessFunction) {
1611
+ LLVM_DEBUG (llvm::dbgs () << " not a linear function " << *inst);
1612
+ continue ;
1613
+ }
1614
+
1615
+ // If the loop iterates 0 through count, remove the bounds check.
1616
+ if (accessFunction.isZeroToCount (selfValue)) {
1617
+ LLVM_DEBUG (llvm::dbgs ()
1618
+ << " Redundant Span/InlineArray bounds check removed\n " );
1619
+ changed = true ;
1620
+ fixedStorageSemantics->eraseFromParent ();
1621
+ continue ;
1622
+ }
1623
+
1624
+ // If the bounds check does not execute always, we cannot hoist it.
1625
+ if (!blockAlwaysExecutes) {
1626
+ LLVM_DEBUG (llvm::dbgs () << " Bounds check does not execute always\n " );
1627
+ continue ;
1628
+ }
1629
+
1630
+ LLVM_DEBUG (llvm::dbgs () << " Span/InlineArray bounds check hoisted\n " );
1631
+ changed = true ;
1632
+ auto firstValue = accessFunction.getFirstValue (preheader->getTerminator ());
1633
+ auto newLowerBoundCheck =
1634
+ fixedStorageSemantics->clone (preheader->getTerminator ());
1635
+ newLowerBoundCheck->setOperand (1 , firstValue);
1636
+
1637
+ auto lastValue = accessFunction.getLastValue (preheader->getTerminator ());
1638
+ auto newUpperBoundCheck =
1639
+ fixedStorageSemantics->clone (preheader->getTerminator ());
1640
+ newUpperBoundCheck->setOperand (1 , lastValue);
1641
+ fixedStorageSemantics->eraseFromParent ();
1642
+ }
1643
+
1644
+ // Traverse the children in the dominator tree.
1645
+ for (auto child : *currentNode) {
1646
+ changed |= hoistFixedStorageBoundsChecksInLoop (loop, child, indVars,
1647
+ recursionDepth + 1 );
1648
+ }
1649
+
1650
+ return changed;
1651
+ }
1652
+
1653
+ bool BoundsCheckOpts::removeRedundantFixedStorageBoundsChecksInLoop (
1654
+ SILLoop *loop, DominanceInfoNode *currentNode,
1655
+ llvm::DenseSet<std::pair<SILValue, SILValue>> &dominatingSafeChecks,
1656
+ int recursionDepth) {
1657
+ auto *currentBlock = currentNode->getBlock ();
1658
+ if (!loop->contains (currentBlock)) {
1659
+ return false ;
1660
+ }
1661
+
1662
+ if (recursionDepth >= maxRecursionDepth) {
1663
+ return false ;
1664
+ }
1665
+
1666
+ bool changed = false ;
1667
+
1668
+ // When we come back from the dominator tree recursion we need to remove
1669
+ // checks that we have seen for the first time.
1670
+ SmallVector<std::pair<SILValue, SILValue>, 8 > safeChecksToPop;
1671
+
1672
+ for (auto iter = currentBlock->begin (); iter != currentBlock->end ();) {
1673
+ auto inst = &*iter;
1674
+ ++iter;
1675
+
1676
+ FixedStorageSemanticsCall fixedStorageSemantics (inst);
1677
+ if (!fixedStorageSemantics ||
1678
+ fixedStorageSemantics.getKind () !=
1679
+ FixedStorageSemanticsCallKind::CheckIndex) {
1680
+ continue ;
1681
+ }
1682
+
1683
+ if (!fixedStorageSemantics->hasSelfArgument ()) {
1684
+ continue ;
1685
+ }
1686
+
1687
+ auto selfValue = fixedStorageSemantics->getSelfArgument ();
1688
+
1689
+ if (!DT->dominates (selfValue->getParentBlock (), loop->getLoopPreheader ())) {
1690
+ LLVM_DEBUG (llvm::dbgs ()
1691
+ << " " << *selfValue << " does not dominate preheader\n " );
1692
+ continue ;
1693
+ }
1694
+
1695
+ auto indexValue = fixedStorageSemantics->getArgument (0 );
1696
+ auto selfAndIndex = std::make_pair (selfValue, indexValue);
1697
+ if (!dominatingSafeChecks.count (selfAndIndex)) {
1698
+ LLVM_DEBUG (llvm::dbgs ()
1699
+ << " first time: " << *inst << " with self: " << *selfValue);
1700
+ dominatingSafeChecks.insert (selfAndIndex);
1701
+ safeChecksToPop.push_back (selfAndIndex);
1702
+ continue ;
1703
+ }
1704
+
1705
+ LLVM_DEBUG (llvm::dbgs ()
1706
+ << " Eliminated redundant Span/InlineArray bounds check" );
1707
+ changed = true ;
1708
+ fixedStorageSemantics->eraseFromParent ();
1709
+ }
1710
+
1711
+ // Traverse the children in the dominator tree inside the loop.
1712
+ for (auto child : *currentNode) {
1713
+ changed |= removeRedundantFixedStorageBoundsChecksInLoop (
1714
+ loop, child, dominatingSafeChecks, recursionDepth + 1 );
1715
+ }
1716
+
1717
+ // Remove checks we have seen for the first time.
1718
+ std::for_each (safeChecksToPop.begin (), safeChecksToPop.end (),
1719
+ [&](std::pair<SILValue, SILValue> &value) {
1720
+ dominatingSafeChecks.erase (value);
1721
+ });
1722
+
1723
+ return changed;
1724
+ }
1725
+
1491
1726
} // end anonymous namespace
1492
1727
1493
1728
SILTransform *swift::createBoundsCheckOpts () { return new BoundsCheckOpts (); }
0 commit comments