@@ -594,6 +594,62 @@ SILInstruction *SILCombiner::optimizeLoadFromStringLiteral(LoadInst *LI) {
594
594
return Builder.createIntegerLiteral (LI->getLoc (), LI->getType (), str[index ]);
595
595
}
596
596
597
+ // / Returns true if \p LI loads a zero integer from the empty Array, Dictionary
598
+ // / or Set singleton.
599
+ static bool isZeroLoadFromEmptyCollection (LoadInst *LI) {
600
+ auto intTy = LI->getType ().getAs <BuiltinIntegerType>();
601
+ if (!intTy)
602
+ return false ;
603
+
604
+ SILValue addr = LI->getOperand ();
605
+
606
+ // Find the root object of the load-address.
607
+ for (;;) {
608
+ switch (addr->getKind ()) {
609
+ case ValueKind::GlobalAddrInst: {
610
+ StringRef gName =
611
+ cast<GlobalAddrInst>(addr)->getReferencedGlobal ()->getName ();
612
+ return gName == " _swiftEmptyArrayStorage" ||
613
+ gName == " _swiftEmptyDictionarySingleton" ||
614
+ gName == " _swiftEmptySetSingleton" ;
615
+ }
616
+ case ValueKind::StructElementAddrInst: {
617
+ auto *SEA = cast<StructElementAddrInst>(addr);
618
+ // For Array, we only support "count". The value of "capacityAndFlags"
619
+ // is not defined in the ABI and could change in another version of the
620
+ // runtime (the capacity must be 0, but the flags may be not 0).
621
+ if (SEA->getStructDecl ()->getName ().is (" _SwiftArrayBodyStorage" ) &&
622
+ !SEA->getField ()->getName ().is (" count" )) {
623
+ return false ;
624
+ }
625
+ addr = SEA->getOperand ();
626
+ break ;
627
+ }
628
+ case ValueKind::RefElementAddrInst: {
629
+ auto *REA = cast<RefElementAddrInst>(addr);
630
+ Identifier className = REA->getClassDecl ()->getName ();
631
+ // For Dictionary and Set we support "count" and "capacity".
632
+ if (className.is (" __RawDictionaryStorage" ) ||
633
+ className.is (" __RawSetStorage" )) {
634
+ Identifier fieldName = REA->getField ()->getName ();
635
+ if (!fieldName.is (" _count" ) && !fieldName.is (" _capacity" ))
636
+ return false ;
637
+ }
638
+ addr = REA->getOperand ();
639
+ break ;
640
+ }
641
+ case ValueKind::UncheckedRefCastInst:
642
+ case ValueKind::UpcastInst:
643
+ case ValueKind::RawPointerToRefInst:
644
+ case ValueKind::AddressToPointerInst:
645
+ addr = cast<SingleValueInstruction>(addr)->getOperand (0 );
646
+ break ;
647
+ default :
648
+ return false ;
649
+ }
650
+ }
651
+ }
652
+
597
653
SILInstruction *SILCombiner::visitLoadInst (LoadInst *LI) {
598
654
// (load (upcast-ptr %x)) -> (upcast-ref (load %x))
599
655
Builder.setCurrentDebugScope (LI->getDebugScope ());
@@ -606,6 +662,17 @@ SILInstruction *SILCombiner::visitLoadInst(LoadInst *LI) {
606
662
if (SILInstruction *I = optimizeLoadFromStringLiteral (LI))
607
663
return I;
608
664
665
+ // Constant-propagate the 0 value when loading "count" or "capacity" from the
666
+ // empty Array, Set or Dictionary storage.
667
+ // On high-level SIL this optimization is also done by the
668
+ // ArrayCountPropagation pass, but only for Array. And even for Array it's
669
+ // sometimes needed to propagate the empty-array count when high-level
670
+ // semantics function are already inlined.
671
+ // Note that for non-empty arrays/sets/dictionaries, the count can be
672
+ // propagated by redundant load elimination.
673
+ if (isZeroLoadFromEmptyCollection (LI))
674
+ return Builder.createIntegerLiteral (LI->getLoc (), LI->getType (), 0 );
675
+
609
676
return nullptr ;
610
677
}
611
678
0 commit comments