@@ -170,6 +170,7 @@ ArrayCallKind swift::ArraySemanticsCall::getKind() const {
170
170
ArrayCallKind::kArrayPropsIsNativeTypeChecked )
171
171
.StartsWith (" array.init" , ArrayCallKind::kArrayInit )
172
172
.Case (" array.uninitialized" , ArrayCallKind::kArrayUninitialized )
173
+ .Case (" array.uninitialized_intrinsic" , ArrayCallKind::kArrayUninitializedIntrinsic )
173
174
.Case (" array.check_subscript" , ArrayCallKind::kCheckSubscript )
174
175
.Case (" array.check_index" , ArrayCallKind::kCheckIndex )
175
176
.Case (" array.get_count" , ArrayCallKind::kGetCount )
@@ -591,11 +592,15 @@ bool swift::ArraySemanticsCall::canInlineEarly() const {
591
592
case ArrayCallKind::kAppendContentsOf :
592
593
case ArrayCallKind::kReserveCapacityForAppend :
593
594
case ArrayCallKind::kAppendElement :
595
+ case ArrayCallKind::kArrayUninitializedIntrinsic :
594
596
// append(Element) calls other semantics functions. Therefore it's
595
597
// important that it's inlined by the early inliner (which is before all
596
598
// the array optimizations). Also, this semantics is only used to lookup
597
599
// Array.append(Element), so inlining it does not prevent any other
598
600
// optimization.
601
+ //
602
+ // Early inlining array.uninitialized_intrinsic semantic call helps in
603
+ // stack promotion.
599
604
return true ;
600
605
}
601
606
}
@@ -622,61 +627,57 @@ SILValue swift::ArraySemanticsCall::getInitializationCount() const {
622
627
return SILValue ();
623
628
}
624
629
625
- SILValue swift::ArraySemanticsCall::getArrayValue () const {
626
- if (getKind () == ArrayCallKind::kArrayUninitialized ) {
627
- TupleExtractInst *ArrayDef = nullptr ;
628
- for (auto *Op : SemanticsCall->getUses ()) {
629
- auto *TupleElt = dyn_cast<TupleExtractInst>(Op->getUser ());
630
- if (!TupleElt)
631
- return SILValue ();
632
- switch (TupleElt->getFieldNo ()) {
633
- default :
634
- return SILValue ();
635
- case 0 : {
636
- // Should only have one tuple extract after CSE.
637
- if (ArrayDef)
638
- return SILValue ();
639
- ArrayDef = TupleElt;
640
- break ;
641
- }
642
- case 1 : /* Ignore the storage address */ break ;
643
- }
630
+ // / Given an array semantic call \c arrayCall, if it is an "array.uninitialized"
631
+ // / initializer, which returns a two-element tuple, return the element of the
632
+ // / tuple at \c tupleElementIndex. Return a null SILValue if the
633
+ // / array call is not an "array.uninitialized" initializer or if the extraction
634
+ // / of the result tuple fails.
635
+ static SILValue getArrayUninitializedInitResult (ArraySemanticsCall arrayCall,
636
+ unsigned tupleElementIndex) {
637
+ assert (tupleElementIndex <= 1 && " tupleElementIndex must be 0 or 1" );
638
+ ArrayCallKind arrayCallKind = arrayCall.getKind ();
639
+ if (arrayCallKind != ArrayCallKind::kArrayUninitialized &&
640
+ arrayCallKind != ArrayCallKind::kArrayUninitializedIntrinsic )
641
+ return SILValue ();
642
+
643
+ // In OSSA, the call result will be extracted through a destructure_tuple
644
+ // instruction.
645
+ ApplyInst *callInst = arrayCall;
646
+ if (callInst->getFunction ()->hasOwnership ()) {
647
+ Operand *singleUse = callInst->getSingleUse ();
648
+ if (!singleUse)
649
+ return SILValue ();
650
+ if (DestructureTupleInst *destructTuple =
651
+ dyn_cast<DestructureTupleInst>(singleUse->getUser ())) {
652
+ return destructTuple->getResult (tupleElementIndex);
644
653
}
645
- return SILValue (ArrayDef);
654
+ return SILValue ();
655
+ }
656
+
657
+ // In non-OSSA, look for a tuple_extract instruction of the call result with
658
+ // the requested tupleElementIndex.
659
+ TupleExtractInst *tupleExtractInst = nullptr ;
660
+ for (auto *op : callInst->getUses ()) {
661
+ auto *tupleElt = dyn_cast<TupleExtractInst>(op->getUser ());
662
+ if (!tupleElt)
663
+ return SILValue ();
664
+ if (tupleElt->getFieldNo () != tupleElementIndex)
665
+ continue ;
666
+ tupleExtractInst = tupleElt;
667
+ break ;
646
668
}
669
+ return SILValue (tupleExtractInst);
670
+ }
647
671
648
- if (getKind () == ArrayCallKind::kArrayInit )
672
+ SILValue swift::ArraySemanticsCall::getArrayValue () const {
673
+ ArrayCallKind arrayCallKind = getKind ();
674
+ if (arrayCallKind == ArrayCallKind::kArrayInit )
649
675
return SILValue (SemanticsCall);
650
-
651
- return SILValue ();
676
+ return getArrayUninitializedInitResult (*this , 0 );
652
677
}
653
678
654
679
SILValue swift::ArraySemanticsCall::getArrayElementStoragePointer () const {
655
- if (getKind () == ArrayCallKind::kArrayUninitialized ) {
656
- TupleExtractInst *ArrayElementStorage = nullptr ;
657
- for (auto *Op : SemanticsCall->getUses ()) {
658
- auto *TupleElt = dyn_cast<TupleExtractInst>(Op->getUser ());
659
- if (!TupleElt)
660
- return SILValue ();
661
- switch (TupleElt->getFieldNo ()) {
662
- default :
663
- return SILValue ();
664
- case 0 : {
665
- // Ignore the array value.
666
- break ;
667
- }
668
- case 1 :
669
- // Should only have one tuple extract after CSE.
670
- if (ArrayElementStorage)
671
- return SILValue ();
672
- ArrayElementStorage = TupleElt;
673
- break ;
674
- }
675
- }
676
- return SILValue (ArrayElementStorage);
677
- }
678
-
679
- return SILValue ();
680
+ return getArrayUninitializedInitResult (*this , 1 );
680
681
}
681
682
682
683
bool swift::ArraySemanticsCall::replaceByValue (SILValue V) {
@@ -786,3 +787,75 @@ bool swift::ArraySemanticsCall::replaceByAppendingValues(
786
787
787
788
return true ;
788
789
}
790
+
791
+ bool swift::ArraySemanticsCall::mapInitializationStores (
792
+ llvm::DenseMap<uint64_t , StoreInst *> &ElementValueMap) {
793
+ if (getKind () != ArrayCallKind::kArrayUninitialized &&
794
+ getKind () != ArrayCallKind::kArrayUninitializedIntrinsic )
795
+ return false ;
796
+ SILValue ElementBuffer = getArrayElementStoragePointer ();
797
+ if (!ElementBuffer)
798
+ return false ;
799
+
800
+ // Match initialization stores into ElementBuffer. E.g.
801
+ // %83 = struct_extract %element_buffer : $UnsafeMutablePointer<Int>
802
+ // %84 = pointer_to_address %83 : $Builtin.RawPointer to strict $*Int
803
+ // store %85 to %84 : $*Int
804
+ // %87 = integer_literal $Builtin.Word, 1
805
+ // %88 = index_addr %84 : $*Int, %87 : $Builtin.Word
806
+ // store %some_value to %88 : $*Int
807
+
808
+ // If this an ArrayUinitializedIntrinsic then the ElementBuffer is a
809
+ // builtin.RawPointer. Otherwise, it is an UnsafeMutablePointer, which would
810
+ // be struct-extracted to obtain a builtin.RawPointer.
811
+ SILValue UnsafeMutablePointerExtract =
812
+ (getKind () == ArrayCallKind::kArrayUninitialized )
813
+ ? dyn_cast_or_null<StructExtractInst>(
814
+ getSingleNonDebugUser (ElementBuffer))
815
+ : ElementBuffer;
816
+ if (!UnsafeMutablePointerExtract)
817
+ return false ;
818
+
819
+ auto *PointerToAddress = dyn_cast_or_null<PointerToAddressInst>(
820
+ getSingleNonDebugUser (UnsafeMutablePointerExtract));
821
+ if (!PointerToAddress)
822
+ return false ;
823
+
824
+ // Match the stores. We can have either a store directly to the address or
825
+ // to an index_addr projection.
826
+ for (auto *Op : PointerToAddress->getUses ()) {
827
+ auto *Inst = Op->getUser ();
828
+
829
+ // Store to the base.
830
+ auto *SI = dyn_cast<StoreInst>(Inst);
831
+ if (SI && SI->getDest () == PointerToAddress) {
832
+ // We have already seen an entry for this index bail.
833
+ if (ElementValueMap.count (0 ))
834
+ return false ;
835
+ ElementValueMap[0 ] = SI;
836
+ continue ;
837
+ } else if (SI)
838
+ return false ;
839
+
840
+ // Store to an index_addr projection.
841
+ auto *IndexAddr = dyn_cast<IndexAddrInst>(Inst);
842
+ if (!IndexAddr)
843
+ return false ;
844
+ SI = dyn_cast_or_null<StoreInst>(getSingleNonDebugUser (IndexAddr));
845
+ if (!SI || SI->getDest () != IndexAddr)
846
+ return false ;
847
+ auto *Index = dyn_cast<IntegerLiteralInst>(IndexAddr->getIndex ());
848
+ if (!Index)
849
+ return false ;
850
+ auto IndexVal = Index->getValue ();
851
+ // Let's not blow up our map.
852
+ if (IndexVal.getActiveBits () > 16 )
853
+ return false ;
854
+ // Already saw an entry.
855
+ if (ElementValueMap.count (IndexVal.getZExtValue ()))
856
+ return false ;
857
+
858
+ ElementValueMap[IndexVal.getZExtValue ()] = SI;
859
+ }
860
+ return !ElementValueMap.empty ();
861
+ }
0 commit comments