@@ -693,6 +693,12 @@ private extension MovableInstructions {
693
693
accessPath: AccessPath ,
694
694
context: FunctionPassContext
695
695
) -> Bool {
696
+
697
+ // If the memory is not initialized at all exits, it would be wrong to insert stores at exit blocks.
698
+ guard memoryIsInitializedAtAllExits ( of: loop, accessPath: accessPath, context) else {
699
+ return false
700
+ }
701
+
696
702
// Initially load the value in the loop pre header.
697
703
let builder = Builder ( before: loop. preheader!. terminator, context)
698
704
var firstStore : StoreInst ?
@@ -721,7 +727,18 @@ private extension MovableInstructions {
721
727
guard let firstStore else {
722
728
return false
723
729
}
724
-
730
+
731
+ // We currently don't support split `load [take]`, i.e. `load [take]` which does _not_ load all
732
+ // non-trivial fields of the initial value.
733
+ for case let load as LoadInst in loadsAndStores {
734
+ if load. loadOwnership == . take,
735
+ let path = accessPath. getProjection ( to: load. address. accessPath) ,
736
+ !firstStore. destination. type. isProjectingEntireNonTrivialMembers ( path: path, in: load. parentFunction)
737
+ {
738
+ return false
739
+ }
740
+ }
741
+
725
742
var ssaUpdater = SSAUpdater (
726
743
function: firstStore. parentFunction,
727
744
type: firstStore. destination. type. objectType,
@@ -792,7 +809,13 @@ private extension MovableInstructions {
792
809
let rootVal = currentVal ?? ssaUpdater. getValue ( inMiddleOf: block)
793
810
794
811
if loadInst. operand. value. accessPath == accessPath {
795
- loadInst. replace ( with: rootVal, context)
812
+ if loadInst. loadOwnership == . copy {
813
+ let builder = Builder ( before: loadInst, context)
814
+ let copy = builder. createCopyValue ( operand: rootVal)
815
+ loadInst. replace ( with: copy, context)
816
+ } else {
817
+ loadInst. replace ( with: rootVal, context)
818
+ }
796
819
changed = true
797
820
continue
798
821
}
@@ -802,7 +825,11 @@ private extension MovableInstructions {
802
825
}
803
826
804
827
let builder = Builder ( before: loadInst, context)
805
- let projection = rootVal. createProjection ( path: projectionPath, builder: builder)
828
+ let projection = if loadInst. loadOwnership == . copy {
829
+ rootVal. createProjectionAndCopy ( path: projectionPath, builder: builder)
830
+ } else {
831
+ rootVal. createProjection ( path: projectionPath, builder: builder)
832
+ }
806
833
loadInst. replace ( with: projection, context)
807
834
808
835
changed = true
@@ -831,6 +858,69 @@ private extension MovableInstructions {
831
858
832
859
return changed
833
860
}
861
+
862
+ func memoryIsInitializedAtAllExits( of loop: Loop , accessPath: AccessPath , _ context: FunctionPassContext ) -> Bool {
863
+
864
+ // Perform a simple dataflow analysis which checks if there is a path from a `load [take]`
865
+ // (= the only kind of instruction which can de-initialize the memory) to a loop exit without
866
+ // a `store` in between.
867
+
868
+ var stores = InstructionSet ( context)
869
+ defer { stores. deinitialize ( ) }
870
+ for case let store as StoreInst in loadsAndStores where store. storesTo ( accessPath) {
871
+ stores. insert ( store)
872
+ }
873
+
874
+ var exitInsts = InstructionSet ( context)
875
+ defer { exitInsts. deinitialize ( ) }
876
+ exitInsts. insert ( contentsOf: loop. exitBlocks. lazy. map { $0. instructions. first! } )
877
+
878
+ var worklist = InstructionWorklist ( context)
879
+ defer { worklist. deinitialize ( ) }
880
+ for case let load as LoadInst in loadsAndStores where load. loadOwnership == . take && load. loadsFrom ( accessPath) {
881
+ worklist. pushIfNotVisited ( load)
882
+ }
883
+
884
+ while let inst = worklist. pop ( ) {
885
+ if stores. contains ( inst) {
886
+ continue
887
+ }
888
+ if exitInsts. contains ( inst) {
889
+ return false
890
+ }
891
+ worklist. pushSuccessors ( of: inst)
892
+ }
893
+ return true
894
+ }
895
+ }
896
+
897
+ private extension Type {
898
+ func isProjectingEntireNonTrivialMembers( path: SmallProjectionPath , in function: Function ) -> Bool {
899
+ let ( kind, index, subPath) = path. pop ( )
900
+ switch kind {
901
+ case . root:
902
+ return true
903
+ case . structField:
904
+ guard let fields = getNominalFields ( in: function) else {
905
+ return false
906
+ }
907
+ for (fieldIdx, fieldType) in fields. enumerated ( ) {
908
+ if fieldIdx != index && !fieldType. isTrivial ( in: function) {
909
+ return false
910
+ }
911
+ }
912
+ return fields [ index] . isProjectingEntireNonTrivialMembers ( path: subPath, in: function)
913
+ case . tupleField:
914
+ for (elementIdx, elementType) in tupleElements. enumerated ( ) {
915
+ if elementIdx != index && !elementType. isTrivial ( in: function) {
916
+ return false
917
+ }
918
+ }
919
+ return tupleElements [ index] . isProjectingEntireNonTrivialMembers ( path: subPath, in: function)
920
+ default :
921
+ fatalError ( " path is not materializable " )
922
+ }
923
+ }
834
924
}
835
925
836
926
private extension Instruction {
0 commit comments