Skip to content

Commit 38e2a3b

Browse files
committed
[inst-simplify] Eliminate address_to_pointer/pointer_to_address identity transforms using new interior pointer handling utility.
Btw for those curious, the reason why this optimization is important is that the stdlib often times performs reinterpret casts using address to pointer, pointer to address to perform the case. So we need to be able to reliably eliminate these. Since the underlying base object's liveness is already what provides the liveness to the underlying address, our approach of treating this as a lifetime extension requiring pattern makes sense since we are adding information into the IR by canonicalizing these escapes to be normal address operations. NOTE: In this commit, we only handle the identity transform. In a subsequent commit, I am going to hit the non-identity transform that is handled by SILCombine. Also, I added some specific more general OSSA RAUW tests for this case to ossa_rauw_tests using this functionality in a more exhaustive way that would be in appropriate in sil_combine_ossa.sil (since I am stress testing this specific piece of code).
1 parent 73ba521 commit 38e2a3b

File tree

3 files changed

+310
-12
lines changed

3 files changed

+310
-12
lines changed

lib/SILOptimizer/Analysis/SimplifyInstruction.cpp

-8
Original file line numberDiff line numberDiff line change
@@ -300,14 +300,6 @@ SILValue InstSimplifier::visitAddressToPointerInst(AddressToPointerInst *ATPI) {
300300
}
301301

302302
SILValue InstSimplifier::visitPointerToAddressInst(PointerToAddressInst *PTAI) {
303-
// (pointer_to_address strict (address_to_pointer x)) -> x
304-
//
305-
// NOTE: We can not perform this optimization in OSSA without dealing with
306-
// interior pointers since we may be escaping an interior pointer address from
307-
// a borrow scope.
308-
if (PTAI->getFunction()->hasOwnership())
309-
return SILValue();
310-
311303
// If this address is not strict, then it cannot be replaced by an address
312304
// that may be strict.
313305
if (auto *ATPI = dyn_cast<AddressToPointerInst>(PTAI->getOperand()))

test/SILOptimizer/ossa_rauw_tests.sil

+306
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,18 @@ protocol Addable {
2121

2222
// Class declarations
2323

24+
struct NativeObjectWrapper {
25+
var obj: Builtin.NativeObject
26+
}
27+
28+
sil @inguaranteed_nativeobject_user : $@convention(thin) (@in_guaranteed Builtin.NativeObject) -> ()
29+
2430
class Klass {
2531
init()
2632
deinit
33+
34+
var field: Klass
35+
var structField: NativeObjectWrapper
2736
}
2837

2938
class SubKlass : Klass {}
@@ -667,3 +676,300 @@ bb3(%4 : @owned $FakeOptional<Klass>):
667676
%9999 = tuple()
668677
return %9999 : $()
669678
}
679+
680+
// CHECK-LABEL: sil [ossa] @interior_pointer_lifetime_extension_arg_not_int_ptr : $@convention(thin) (@in_guaranteed Klass) -> @owned Klass {
681+
// CHECK-NOT: address_to_pointer
682+
// CHECK-NOT: pointer_to_address
683+
// CHECK: } // end sil function 'interior_pointer_lifetime_extension_arg_not_int_ptr'
684+
sil [ossa] @interior_pointer_lifetime_extension_arg_not_int_ptr : $@convention(thin) (@in_guaranteed Klass) -> @owned Klass {
685+
bb0(%0 : $*Klass):
686+
%1 = address_to_pointer %0 : $*Klass to $Builtin.RawPointer
687+
%2 = pointer_to_address %1 : $Builtin.RawPointer to [strict] $*Klass
688+
%3 = load [copy] %2 : $*Klass
689+
return %3 : $Klass
690+
}
691+
692+
// CHECK-LABEL: sil [ossa] @interior_pointer_lifetime_extension_int_ptr_no_lifetime_ext_needed : $@convention(thin) (@owned Klass) -> @owned Klass {
693+
// CHECK-NOT: address_to_pointer
694+
// CHECK-NOT: pointer_to_address
695+
// CHECK: } // end sil function 'interior_pointer_lifetime_extension_int_ptr_no_lifetime_ext_needed'
696+
sil [ossa] @interior_pointer_lifetime_extension_int_ptr_no_lifetime_ext_needed : $@convention(thin) (@owned Klass) -> @owned Klass {
697+
bb0(%0 : @owned $Klass):
698+
%0a = begin_borrow %0 : $Klass
699+
%0b = ref_element_addr %0a : $Klass, #Klass.field
700+
%1 = address_to_pointer %0b : $*Klass to $Builtin.RawPointer
701+
%2 = pointer_to_address %1 : $Builtin.RawPointer to [strict] $*Klass
702+
%3 = load [copy] %2 : $*Klass
703+
end_borrow %0a : $Klass
704+
destroy_value %0 : $Klass
705+
return %3 : $Klass
706+
}
707+
708+
// In this case, we need to perform the interior pointer lifetime extension.
709+
//
710+
// CHECK-LABEL: sil [ossa] @interior_pointer_lifetime_extension_int_ptr_need_lifetime_ext : $@convention(thin) (@owned Klass) -> @owned Klass {
711+
// CHECK-NOT: address_to_pointer
712+
// CHECK-NOT: pointer_to_address
713+
// CHECK: } // end sil function 'interior_pointer_lifetime_extension_int_ptr_need_lifetime_ext'
714+
sil [ossa] @interior_pointer_lifetime_extension_int_ptr_need_lifetime_ext : $@convention(thin) (@owned Klass) -> @owned Klass {
715+
bb0(%0 : @owned $Klass):
716+
%0a = begin_borrow %0 : $Klass
717+
%0b = ref_element_addr %0a : $Klass, #Klass.field
718+
%1 = address_to_pointer %0b : $*Klass to $Builtin.RawPointer
719+
%2 = pointer_to_address %1 : $Builtin.RawPointer to [strict] $*Klass
720+
end_borrow %0a : $Klass
721+
%3 = load [copy] %2 : $*Klass
722+
destroy_value %0 : $Klass
723+
return %3 : $Klass
724+
}
725+
726+
// CHECK-LABEL: sil [ossa] @interior_pointer_lifetime_extension_int_ptr_need_lifetime_ext_with_proj : $@convention(thin) (@owned Klass) -> @owned Builtin.NativeObject {
727+
// CHECK-NOT: address_to_pointer
728+
// CHECK-NOT: pointer_to_address
729+
// CHECK: } // end sil function 'interior_pointer_lifetime_extension_int_ptr_need_lifetime_ext_with_proj'
730+
sil [ossa] @interior_pointer_lifetime_extension_int_ptr_need_lifetime_ext_with_proj : $@convention(thin) (@owned Klass) -> @owned Builtin.NativeObject {
731+
bb0(%0 : @owned $Klass):
732+
%0a = begin_borrow %0 : $Klass
733+
%0b = ref_element_addr %0a : $Klass, #Klass.structField
734+
%0c = struct_element_addr %0b : $*NativeObjectWrapper, #NativeObjectWrapper.obj
735+
%1 = address_to_pointer %0c : $*Builtin.NativeObject to $Builtin.RawPointer
736+
%2 = pointer_to_address %1 : $Builtin.RawPointer to [strict] $*Builtin.NativeObject
737+
end_borrow %0a : $Klass
738+
%3 = load [copy] %2 : $*Builtin.NativeObject
739+
destroy_value %0 : $Klass
740+
return %3 : $Builtin.NativeObject
741+
}
742+
743+
// Make sure we inserted everything in the right places rather than using the
744+
// ownership verifier.
745+
//
746+
// CHECK-LABEL: sil [ossa] @interior_pointer_lifetime_extension_int_ptr_need_lifetime_ext_with_proj_2 : $@convention(thin) (@owned Klass) -> @owned Builtin.NativeObject {
747+
// CHECK: bb0([[ARG:%.*]] : @owned
748+
// CHECK-NEXT: [[BORROWED_ARG:%.*]] = begin_borrow [[ARG]]
749+
// CHECK-NEXT: [[NEW_INT_PTR:%.*]] = ref_element_addr [[BORROWED_ARG]]
750+
// CHECK-NEXT: br bb1
751+
//
752+
// CHECK: bb1:
753+
// CHECK-NEXT: br bb2
754+
//
755+
// CHECK: bb2:
756+
// CHECK-NEXT: [[NEW_GEP:%.*]] = struct_element_addr [[NEW_INT_PTR]]
757+
// CHECK-NEXT: br bb3
758+
//
759+
// CHECK: bb3:
760+
// CHECK-NEXT: br bb4
761+
//
762+
// CHECK: bb4:
763+
// CHECK-NEXT: [[RESULT:%.*]] = load [copy] [[NEW_GEP]] :
764+
// CHECK-NEXT: end_borrow [[BORROWED_ARG]]
765+
// CHECK-NEXT: destroy_value [[ARG]]
766+
// CHECK-NEXT: return [[RESULT]]
767+
// CHECK-NEXT: } // end sil function 'interior_pointer_lifetime_extension_int_ptr_need_lifetime_ext_with_proj_2'
768+
sil [ossa] @interior_pointer_lifetime_extension_int_ptr_need_lifetime_ext_with_proj_2 : $@convention(thin) (@owned Klass) -> @owned Builtin.NativeObject {
769+
bb0(%0 : @owned $Klass):
770+
%0a = begin_borrow %0 : $Klass
771+
br bb1
772+
773+
bb1:
774+
%0b = ref_element_addr %0a : $Klass, #Klass.structField
775+
%0c = struct_element_addr %0b : $*NativeObjectWrapper, #NativeObjectWrapper.obj
776+
br bb2
777+
778+
bb2:
779+
%1 = address_to_pointer %0c : $*Builtin.NativeObject to $Builtin.RawPointer
780+
%2 = pointer_to_address %1 : $Builtin.RawPointer to [strict] $*Builtin.NativeObject
781+
br bb3
782+
783+
bb3:
784+
end_borrow %0a : $Klass
785+
br bb4
786+
787+
bb4:
788+
%3 = load [copy] %2 : $*Builtin.NativeObject
789+
destroy_value %0 : $Klass
790+
return %3 : $Builtin.NativeObject
791+
}
792+
793+
// Make sure we inserted everything in the right places rather than using the
794+
// ownership verifier given we can't eliminate the underlying RAUW.
795+
//
796+
// CHECK-LABEL: sil [ossa] @interior_pointer_lifetime_extension_int_ptr_need_lifetime_ext_with_proj_3 : $@convention(thin) (@owned Klass) -> @owned Builtin.NativeObject {
797+
// CHECK: bb0([[ARG:%.*]] : @owned
798+
// CHECK-NEXT: [[ORIGINAL_BORROW:%.*]] = begin_borrow [[ARG]]
799+
// CHECK-NEXT: [[COPIED_ARG:%.*]] = copy_value [[ORIGINAL_BORROW]]
800+
// CHECK-NEXT: [[BORROWED_COPIED_ARG:%.*]] = begin_borrow [[COPIED_ARG]]
801+
// CHECK-NEXT: [[NEW_INT_PTR:%.*]] = ref_element_addr [[BORROWED_COPIED_ARG]]
802+
// CHECK-NEXT: br bb1
803+
//
804+
// CHECK: bb1:
805+
// CHECK-NEXT: [[OLD_INT_PTR:%.*]] = ref_element_addr
806+
// CHECK-NEXT: [[OLD_GEP:%.*]] = struct_element_addr [[OLD_INT_PTR]]
807+
// CHECK-NEXT: br bb2
808+
//
809+
// CHECK: bb2:
810+
// CHECK-NEXT: // function_ref
811+
// CHECK-NEXT: [[USER:%.*]] = function_ref @
812+
// CHECK-NEXT: apply [[USER]]([[OLD_GEP]])
813+
// CHECK-NEXT: [[NEW_GEP:%.*]] = struct_element_addr [[NEW_INT_PTR]]
814+
// CHECK-NEXT: br bb3
815+
//
816+
// CHECK: bb3:
817+
// CHECK-NEXT: end_borrow [[ORIGINAL_BORROW]]
818+
// CHECK-NEXT: br bb4
819+
//
820+
// CHECK: bb4:
821+
// CHECK-NEXT: [[RESULT:%.*]] = load [copy] [[NEW_GEP]]
822+
// CHECK-NEXT: end_borrow [[BORROWED_COPIED_ARG]]
823+
// CHECK-NEXT: destroy_value [[COPIED_ARG]]
824+
// CHECK-NEXT: destroy_value [[ARG]]
825+
// CHECK-NEXT: return [[RESULT]]
826+
// CHECK-NEXT: } // end sil function 'interior_pointer_lifetime_extension_int_ptr_need_lifetime_ext_with_proj_3'
827+
sil [ossa] @interior_pointer_lifetime_extension_int_ptr_need_lifetime_ext_with_proj_3 : $@convention(thin) (@owned Klass) -> @owned Builtin.NativeObject {
828+
bb0(%0 : @owned $Klass):
829+
%0a = begin_borrow %0 : $Klass
830+
br bb1
831+
832+
bb1:
833+
%0b = ref_element_addr %0a : $Klass, #Klass.structField
834+
%0c = struct_element_addr %0b : $*NativeObjectWrapper, #NativeObjectWrapper.obj
835+
br bb2
836+
837+
bb2:
838+
%f = function_ref @inguaranteed_nativeobject_user : $@convention(thin) (@in_guaranteed Builtin.NativeObject) -> ()
839+
apply %f(%0c) : $@convention(thin) (@in_guaranteed Builtin.NativeObject) -> ()
840+
%1 = address_to_pointer %0c : $*Builtin.NativeObject to $Builtin.RawPointer
841+
%2 = pointer_to_address %1 : $Builtin.RawPointer to [strict] $*Builtin.NativeObject
842+
br bb3
843+
844+
bb3:
845+
end_borrow %0a : $Klass
846+
br bb4
847+
848+
bb4:
849+
%3 = load [copy] %2 : $*Builtin.NativeObject
850+
destroy_value %0 : $Klass
851+
return %3 : $Builtin.NativeObject
852+
}
853+
854+
// CHECK-LABEL: sil [ossa] @interior_pointer_lifetime_extension_int_ptr_need_lifetime_ext_loop_1 : $@convention(thin) (@owned Klass) -> @owned Builtin.NativeObject {
855+
// CHECK-NOT: address_to_pointer
856+
// CHECK-NOT: pointer_to_address
857+
// CHECK: } // end sil function 'interior_pointer_lifetime_extension_int_ptr_need_lifetime_ext_loop_1'
858+
sil [ossa] @interior_pointer_lifetime_extension_int_ptr_need_lifetime_ext_loop_1 : $@convention(thin) (@owned Klass) -> @owned Builtin.NativeObject {
859+
bb0(%0 : @owned $Klass):
860+
%0a = begin_borrow %0 : $Klass
861+
br bb1
862+
863+
bb1:
864+
%0b = ref_element_addr %0a : $Klass, #Klass.structField
865+
%0c = struct_element_addr %0b : $*NativeObjectWrapper, #NativeObjectWrapper.obj
866+
br bb2
867+
868+
bb2:
869+
br bbLoop
870+
871+
bbLoop:
872+
%f = function_ref @inguaranteed_nativeobject_user : $@convention(thin) (@in_guaranteed Builtin.NativeObject) -> ()
873+
apply %f(%0c) : $@convention(thin) (@in_guaranteed Builtin.NativeObject) -> ()
874+
%1 = address_to_pointer %0c : $*Builtin.NativeObject to $Builtin.RawPointer
875+
%2 = pointer_to_address %1 : $Builtin.RawPointer to [strict] $*Builtin.NativeObject
876+
cond_br undef, bbBackEdge, bb3
877+
878+
bbBackEdge:
879+
br bbLoop
880+
881+
bb3:
882+
end_borrow %0a : $Klass
883+
br bb4
884+
885+
bb4:
886+
%3 = load [copy] %2 : $*Builtin.NativeObject
887+
destroy_value %0 : $Klass
888+
return %3 : $Builtin.NativeObject
889+
}
890+
891+
// CHECK-LABEL: sil [ossa] @interior_pointer_lifetime_extension_int_ptr_need_lifetime_ext_loop_2 : $@convention(thin) (@owned Klass) -> @owned Builtin.NativeObject {
892+
// CHECK-NOT: address_to_pointer
893+
// CHECK-NOT: pointer_to_address
894+
// CHECK: } // end sil function 'interior_pointer_lifetime_extension_int_ptr_need_lifetime_ext_loop_2'
895+
sil [ossa] @interior_pointer_lifetime_extension_int_ptr_need_lifetime_ext_loop_2 : $@convention(thin) (@owned Klass) -> @owned Builtin.NativeObject {
896+
bb0(%0 : @owned $Klass):
897+
%0a = begin_borrow %0 : $Klass
898+
br bb1
899+
900+
bb1:
901+
%0b = ref_element_addr %0a : $Klass, #Klass.structField
902+
%0c = struct_element_addr %0b : $*NativeObjectWrapper, #NativeObjectWrapper.obj
903+
br bb2
904+
905+
bb2:
906+
br bbLoop
907+
908+
bbLoop:
909+
%f = function_ref @inguaranteed_nativeobject_user : $@convention(thin) (@in_guaranteed Builtin.NativeObject) -> ()
910+
apply %f(%0c) : $@convention(thin) (@in_guaranteed Builtin.NativeObject) -> ()
911+
%1 = address_to_pointer %0c : $*Builtin.NativeObject to $Builtin.RawPointer
912+
%2 = pointer_to_address %1 : $Builtin.RawPointer to [strict] $*Builtin.NativeObject
913+
br bbLoop2
914+
915+
bbLoop2:
916+
%3 = load [copy] %2 : $*Builtin.NativeObject
917+
cond_br undef, bbBackEdge, bb3
918+
919+
bbBackEdge:
920+
destroy_value %3 : $Builtin.NativeObject
921+
br bbLoop
922+
923+
bb3:
924+
end_borrow %0a : $Klass
925+
br bb4
926+
927+
bb4:
928+
destroy_value %0 : $Klass
929+
return %3 : $Builtin.NativeObject
930+
}
931+
932+
// CHECK-LABEL: sil [ossa] @interior_pointer_lifetime_extension_int_ptr_need_lifetime_ext_loop_3 : $@convention(thin) (@owned Klass) -> @owned Builtin.NativeObject {
933+
// CHECK-NOT: address_to_pointer
934+
// CHECK-NOT: pointer_to_address
935+
// CHECK: } // end sil function 'interior_pointer_lifetime_extension_int_ptr_need_lifetime_ext_loop_3'
936+
sil [ossa] @interior_pointer_lifetime_extension_int_ptr_need_lifetime_ext_loop_3 : $@convention(thin) (@owned Klass) -> @owned Builtin.NativeObject {
937+
bb0(%0 : @owned $Klass):
938+
%0a = begin_borrow %0 : $Klass
939+
br bb1
940+
941+
bb1:
942+
br bb2
943+
944+
bb2:
945+
br bbLoop(%0a : $Klass, undef : $Klass)
946+
947+
bbLoop(%arg : @guaranteed $Klass, %base : @owned $Klass):
948+
%0b = ref_element_addr %arg : $Klass, #Klass.structField
949+
%0c = struct_element_addr %0b : $*NativeObjectWrapper, #NativeObjectWrapper.obj
950+
br bbLoop2
951+
952+
bbLoop2:
953+
%f = function_ref @inguaranteed_nativeobject_user : $@convention(thin) (@in_guaranteed Builtin.NativeObject) -> ()
954+
apply %f(%0c) : $@convention(thin) (@in_guaranteed Builtin.NativeObject) -> ()
955+
%1 = address_to_pointer %0c : $*Builtin.NativeObject to $Builtin.RawPointer
956+
%2 = pointer_to_address %1 : $Builtin.RawPointer to [strict] $*Builtin.NativeObject
957+
br bbLoop3
958+
959+
bbLoop3:
960+
end_borrow %arg : $Klass
961+
destroy_value %base : $Klass
962+
%3 = load [copy] %2 : $*Builtin.NativeObject
963+
cond_br undef, bbBackEdge, bb3
964+
965+
bbBackEdge:
966+
destroy_value %3 : $Builtin.NativeObject
967+
br bbLoop(undef : $Klass, undef : $Klass)
968+
969+
bb3:
970+
br bb4
971+
972+
bb4:
973+
destroy_value %0 : $Klass
974+
return %3 : $Builtin.NativeObject
975+
}

test/SILOptimizer/sil_combine_ossa.sil

+4-4
Original file line numberDiff line numberDiff line change
@@ -453,10 +453,10 @@ bb0(%0 : $Builtin.Int8, %1 : @guaranteed $Builtin.NativeObject):
453453
}
454454

455455
// CHECK-LABEL: sil [ossa] @a2p_p2a_test
456-
// XHECK: bb0([[ADR:%[0-9]+]] : $*Builtin.Word, [[RAWPTR:%[0-9]+]] : $Builtin.RawPointer):
457-
// XHECK-NEXT: load
458-
// XHECK-NEXT: tuple
459-
// XHECK-NEXT: return
456+
// CHECK: bb0([[ADR:%[0-9]+]] : $*Builtin.Word, [[RAWPTR:%[0-9]+]] : $Builtin.RawPointer):
457+
// CHECK-NEXT: load
458+
// CHECK-NEXT: tuple
459+
// CHECK-NEXT: return
460460
sil [ossa] @a2p_p2a_test : $@convention(thin) (@inout Builtin.Word, Builtin.RawPointer) -> (Builtin.Word, Builtin.RawPointer) {
461461
bb0(%0 : $*Builtin.Word, %1 : $Builtin.RawPointer):
462462
%2 = address_to_pointer %0 : $*Builtin.Word to $Builtin.RawPointer

0 commit comments

Comments
 (0)