| // RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all -remove-pins %s | %FileCheck %s |
| |
| sil_stage canonical |
| |
| import Swift |
| import SwiftShims |
| import Builtin |
| |
| ///////////// |
| // Utility // |
| ///////////// |
| |
| struct MyArrayBuffer { |
| var storage : Builtin.NativeObject |
| } |
| |
| struct MyInt {} |
| class MyClass {} |
| |
| struct MyArray<T>{ |
| var buffer : MyArrayBuffer |
| } |
| |
| sil [_semantics "array.make_mutable"] @make_mutable : $@convention(method) (@inout MyArray<MyInt>) -> Bool |
| |
| sil [_semantics "array.get_count"] @get_count : $@convention(method) (@guaranteed MyArray<MyInt>) -> () |
| |
| sil [_semantics "array.make_mutable"] @make_mutable_class : $@convention(method) (@inout MyArray<MyClass>) -> Bool |
| |
| sil [_semantics "array.get_count"] @get_count_class : $@convention(method) (@guaranteed MyArray<MyClass>) -> () |
| |
| sil @user : $@convention(thin) (Builtin.NativeObject) -> Bool |
| |
| /////////// |
| // Tests // |
| /////////// |
| |
| // CHECK-LABEL: sil @remove_pins |
| // CHECK-NOT: strong_pin |
| // CHECK-NOT: strong_unpin |
| // CHECK: return |
| |
| sil @remove_pins : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () { |
| bb0(%0 : $Builtin.NativeObject): |
| %1 = strong_pin %0 : $Builtin.NativeObject |
| strong_unpin %1 : $Optional<Builtin.NativeObject> |
| %2 = tuple() |
| return %2 : $() |
| } |
| |
| // CHECK-LABEL: sil @dont_remove_pins |
| // CHECK: strong_pin |
| // CHECK: strong_release |
| // CHECK: strong_unpin |
| // CHECK: return |
| |
| sil @dont_remove_pins : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () { |
| bb0(%0 : $Builtin.NativeObject): |
| %1 = strong_pin %0 : $Builtin.NativeObject |
| strong_release %0 : $Builtin.NativeObject |
| strong_unpin %1 : $Optional<Builtin.NativeObject> |
| %2 = tuple() |
| return %2 : $() |
| } |
| |
| /// Due to the usage pattern of the array's uniquing there will always be a |
| /// guarding make_mutable across the second access of a non-structural |
| /// modification sequence. |
| |
| // CHECK-LABEL: sil @remove_pins_across_make_mutable |
| // CHECK: [[MAKE_MUTABLE:%.*]] = function_ref @make_mutable |
| // CHECK: apply [[MAKE_MUTABLE]] |
| // CHECK-NOT: strong_pin |
| // CHECK: apply [[MAKE_MUTABLE]] |
| // CHECK-NOT: strong_unpin |
| // CHECK: return |
| |
| sil @remove_pins_across_make_mutable : $@convention(thin) (@inout MyArray<MyInt>) -> () { |
| bb0(%0 : $*MyArray<MyInt>): |
| %1 = load %0: $*MyArray<MyInt> |
| %2 = struct_extract %1 : $MyArray<MyInt>, #MyArray.buffer |
| %3 = struct_extract %2 : $MyArrayBuffer, #MyArrayBuffer.storage |
| %4 = function_ref @make_mutable : $@convention(method) (@inout MyArray<MyInt>) -> Bool |
| %5 = apply %4(%0) : $@convention(method) (@inout MyArray<MyInt>) -> Bool |
| %6 = strong_pin %3 : $Builtin.NativeObject |
| %7 = apply %4(%0) : $@convention(method) (@inout MyArray<MyInt>) -> Bool |
| strong_unpin %6 : $Optional<Builtin.NativeObject> |
| %8 = tuple() |
| return %8 : $() |
| } |
| |
| // CHECK-LABEL: sil @remove_pins_across_make_mutable_class |
| // CHECK: [[MAKE_MUTABLE:%.*]] = function_ref @make_mutable_class |
| // CHECK: apply [[MAKE_MUTABLE]] |
| // CHECK-NOT: strong_pin |
| // CHECK: apply [[MAKE_MUTABLE]] |
| // CHECK-NOT: strong_unpin |
| // CHECK: return |
| |
| sil @remove_pins_across_make_mutable_class : $@convention(thin) (@inout MyArray<MyClass>) -> () { |
| bb0(%0 : $*MyArray<MyClass>): |
| %1 = load %0: $*MyArray<MyClass> |
| %2 = struct_extract %1 : $MyArray<MyClass>, #MyArray.buffer |
| %3 = struct_extract %2 : $MyArrayBuffer, #MyArrayBuffer.storage |
| %4 = function_ref @make_mutable_class : $@convention(method) (@inout MyArray<MyClass>) -> Bool |
| %5 = apply %4(%0) : $@convention(method) (@inout MyArray<MyClass>) -> Bool |
| %6 = strong_pin %3 : $Builtin.NativeObject |
| %7 = apply %4(%0) : $@convention(method) (@inout MyArray<MyClass>) -> Bool |
| strong_unpin %6 : $Optional<Builtin.NativeObject> |
| %8 = tuple() |
| return %8 : $() |
| } |
| |
| // CHECK-LABEL: sil @dont_remove_pins_across_pin_read |
| // CHECK: strong_pin |
| // CHECK: is_unique_or_pinned |
| // CHECK: strong_unpin |
| |
| sil @dont_remove_pins_across_pin_read : $@convention(thin) (@inout MyArray<MyInt>) -> Builtin.Int1 { |
| bb0(%0 : $*MyArray<MyInt>): |
| %1 = load %0: $*MyArray<MyInt> |
| %2 = struct_element_addr %0 : $*MyArray<MyInt>, #MyArray.buffer |
| %3 = struct_element_addr %2 : $*MyArrayBuffer, #MyArrayBuffer.storage |
| %4 = struct_extract %1 : $MyArray<MyInt>, #MyArray.buffer |
| %5 = struct_extract %4 : $MyArrayBuffer, #MyArrayBuffer.storage |
| %6 = strong_pin %5 : $Builtin.NativeObject |
| %7 = is_unique_or_pinned %3 : $*Builtin.NativeObject |
| strong_unpin %6 : $Optional<Builtin.NativeObject> |
| return %7 : $Builtin.Int1 |
| } |
| |
| // CHECK-LABEL: sil @remove_pins_with_inert_rcidentity_uses : $@convention(thin) (@inout MyArray<MyInt>) -> () { |
| // CHECK-NOT: strong_pin |
| // CHECK-NOT: strong_unpin |
| sil @remove_pins_with_inert_rcidentity_uses : $@convention(thin) (@inout MyArray<MyInt>) -> () { |
| bb0(%0 : $*MyArray<MyInt>): |
| %1 = load %0: $*MyArray<MyInt> |
| %2 = struct_extract %1 : $MyArray<MyInt>, #MyArray.buffer |
| %3 = struct_extract %2 : $MyArrayBuffer, #MyArrayBuffer.storage |
| %4 = unchecked_trivial_bit_cast %3 : $Builtin.NativeObject to $UnsafePointer<HeapObject> |
| %6 = strong_pin %3 : $Builtin.NativeObject |
| %7 = integer_literal $Builtin.Int1, 0 |
| %8 = tuple(%6 : $Optional<Builtin.NativeObject>, %7 : $Builtin.Int1) |
| %9 = tuple_extract %8 : $(Optional<Builtin.NativeObject>, Builtin.Int1), 0 |
| strong_unpin %9 : $Optional<Builtin.NativeObject> |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // CHECK-LABEL: sil @cant_remove_pins_with_escaping_rcidentity_use : $@convention(thin) (@inout MyArray<MyInt>) -> Bool { |
| // CHECK: strong_pin |
| // CHECK: strong_unpin |
| sil @cant_remove_pins_with_escaping_rcidentity_use : $@convention(thin) (@inout MyArray<MyInt>) -> Bool { |
| bb0(%0 : $*MyArray<MyInt>): |
| %1 = load %0: $*MyArray<MyInt> |
| %4 = struct_extract %1 : $MyArray<MyInt>, #MyArray.buffer |
| %5 = struct_extract %4 : $MyArrayBuffer, #MyArrayBuffer.storage |
| %6 = strong_pin %5 : $Builtin.NativeObject |
| %7 = integer_literal $Builtin.Int1, 0 |
| %8 = tuple(%6 : $Optional<Builtin.NativeObject>, %7 : $Builtin.Int1) |
| %9 = tuple_extract %8 : $(Optional<Builtin.NativeObject>, Builtin.Int1), 0 |
| strong_unpin %9 : $Optional<Builtin.NativeObject> |
| %11 = unchecked_enum_data %6 : $Optional<Builtin.NativeObject>, #Optional.some!enumelt.1 |
| %12 = function_ref @user : $@convention(thin) (Builtin.NativeObject) -> Bool |
| %13 = apply %12(%11) : $@convention(thin) (Builtin.NativeObject) -> Bool |
| return %13 : $Bool |
| } |
| |
| // Make sure we ignore the uses of %10 and %12. |
| // |
| // This is safe to do since the value we are extracting has nothing to do really |
| // with the RCIdentity of the tuple we are extracting from. |
| |
| // CHECK-LABEL: sil @remove_pins_with_inert_rcidentity_uses3 : $@convention(thin) (@inout MyArray<MyInt>) -> (Builtin.Int1, Builtin.Int1) { |
| // CHECK-NOT: strong_pin |
| // CHECK-NOT: strong_unpin |
| sil @remove_pins_with_inert_rcidentity_uses3 : $@convention(thin) (@inout MyArray<MyInt>) -> (Builtin.Int1, Builtin.Int1) { |
| bb0(%0 : $*MyArray<MyInt>): |
| %1 = load %0: $*MyArray<MyInt> |
| %2 = struct_extract %1 : $MyArray<MyInt>, #MyArray.buffer |
| %3 = struct_extract %2 : $MyArrayBuffer, #MyArrayBuffer.storage |
| %4 = unchecked_trivial_bit_cast %3 : $Builtin.NativeObject to $UnsafePointer<HeapObject> |
| %6 = strong_pin %3 : $Builtin.NativeObject |
| %7 = integer_literal $Builtin.Int1, 0 |
| %8 = tuple(%6 : $Optional<Builtin.NativeObject>, %7 : $Builtin.Int1) |
| %9 = tuple_extract %8 : $(Optional<Builtin.NativeObject>, Builtin.Int1), 0 |
| %10 = tuple_extract %8 : $(Optional<Builtin.NativeObject>, Builtin.Int1), 1 |
| strong_unpin %9 : $Optional<Builtin.NativeObject> |
| %11 = tuple_extract %8 : $(Optional<Builtin.NativeObject>, Builtin.Int1), 1 |
| %12 = tuple(%10 : $Builtin.Int1, %11 : $Builtin.Int1) |
| return %12 : $(Builtin.Int1, Builtin.Int1) |
| } |
| |
| // CHECK-LABEL: sil @do_not_remove_pins_non_inert_rcidentity_uses : $@convention(thin) (@inout MyArray<MyInt>) -> Bool { |
| // CHECK: strong_pin |
| // CHECK: strong_unpin |
| sil @do_not_remove_pins_non_inert_rcidentity_uses : $@convention(thin) (@inout MyArray<MyInt>) -> Bool { |
| bb0(%0 : $*MyArray<MyInt>): |
| %1 = load %0: $*MyArray<MyInt> |
| %2 = struct_extract %1 : $MyArray<MyInt>, #MyArray.buffer |
| %3 = struct_extract %2 : $MyArrayBuffer, #MyArrayBuffer.storage |
| %4 = unchecked_trivial_bit_cast %3 : $Builtin.NativeObject to $UnsafePointer<HeapObject> |
| %6 = strong_pin %3 : $Builtin.NativeObject |
| %7 = integer_literal $Builtin.Int1, 0 |
| %8 = tuple(%6 : $Optional<Builtin.NativeObject>, %7 : $Builtin.Int1) |
| %9 = tuple_extract %8 : $(Optional<Builtin.NativeObject>, Builtin.Int1), 0 |
| strong_unpin %9 : $Optional<Builtin.NativeObject> |
| %10 = unchecked_ref_cast %9 : $Optional<Builtin.NativeObject> to $Builtin.NativeObject |
| %11 = function_ref @user : $@convention(thin) (Builtin.NativeObject) -> Bool |
| %12 = apply %11(%10) : $ @convention(thin) (Builtin.NativeObject) -> Bool |
| return %12 : $Bool |
| } |
| |
| // Make sure that we properly ignore guaranteed retain, releases. |
| // |
| // Discussion: We pattern match very closely for now by whenever we see a |
| // release, checking if the previous instruction was a guaranteed array semantic |
| // call with guaranteed self and the instruction before that a retain on self |
| // again. In such a case, we ignore the release. |
| // |
| // CHECK-LABEL: sil @pins_and_guaranteed_self : $@convention(thin) (@inout MyArray<MyInt>) -> () { |
| // CHECK: bb0 |
| // CHECK-NOT: strong_pin |
| // CHECK-NOT: strong_unpin |
| // CHECK: bb1 |
| // CHECK: strong_pin |
| // CHECK: strong_unpin |
| sil @pins_and_guaranteed_self : $@convention(thin) (@inout MyArray<MyInt>) -> () { |
| bb0(%0 : $*MyArray<MyInt>): |
| %1 = load %0: $*MyArray<MyInt> |
| %2 = struct_extract %1 : $MyArray<MyInt>, #MyArray.buffer |
| %3 = struct_extract %2 : $MyArrayBuffer, #MyArrayBuffer.storage |
| %4 = unchecked_trivial_bit_cast %3 : $Builtin.NativeObject to $UnsafePointer<HeapObject> |
| %6 = function_ref @get_count : $@convention(method) (@guaranteed MyArray<MyInt>) -> () |
| %7 = strong_pin %3 : $Builtin.NativeObject |
| retain_value %1 : $MyArray<MyInt> |
| apply %6(%1) : $@convention(method) (@guaranteed MyArray<MyInt>) -> () |
| release_value %1 : $MyArray<MyInt> |
| strong_unpin %7 : $Optional<Builtin.NativeObject> |
| |
| // We take advantage of pins being single BB here. |
| br bb1 |
| |
| bb1: |
| %8 = strong_pin %3 : $Builtin.NativeObject |
| retain_value %1 : $MyArray<MyInt> |
| apply %6(%1) : $@convention(method) (@guaranteed MyArray<MyInt>) -> () |
| apply %6(%1) : $@convention(method) (@guaranteed MyArray<MyInt>) -> () |
| release_value %1 : $MyArray<MyInt> |
| strong_unpin %8 : $Optional<Builtin.NativeObject> |
| |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // Arrays that could be backed by an NSArray may call NSArray methods which |
| // might have arbitrary side effects including a release on the array. |
| |
| // CHECK-LABEL: sil @pins_and_guaranteed_self_class |
| // CHECK: bb0({{.*}}): |
| // CHECK: strong_pin |
| // CHECK: strong_unpin |
| // CHECK: br bb1 |
| // CHECK: bb1: |
| // CHECK: strong_pin |
| // CHECK: strong_unpin |
| // CHECK: return |
| |
| sil @pins_and_guaranteed_self_class : $@convention(thin) (@inout MyArray<MyClass>) -> () { |
| bb0(%0 : $*MyArray<MyClass>): |
| %1 = load %0: $*MyArray<MyClass> |
| %2 = struct_extract %1 : $MyArray<MyClass>, #MyArray.buffer |
| %3 = struct_extract %2 : $MyArrayBuffer, #MyArrayBuffer.storage |
| %4 = unchecked_trivial_bit_cast %3 : $Builtin.NativeObject to $UnsafePointer<HeapObject> |
| %6 = function_ref @get_count_class : $@convention(method) (@guaranteed MyArray<MyClass>) -> () |
| %7 = strong_pin %3 : $Builtin.NativeObject |
| retain_value %1 : $MyArray<MyClass> |
| apply %6(%1) : $@convention(method) (@guaranteed MyArray<MyClass>) -> () |
| release_value %1 : $MyArray<MyClass> |
| strong_unpin %7 : $Optional<Builtin.NativeObject> |
| |
| // We take advantage of pins being single BB here. |
| br bb1 |
| |
| bb1: |
| %8 = strong_pin %3 : $Builtin.NativeObject |
| retain_value %1 : $MyArray<MyClass> |
| apply %6(%1) : $@convention(method) (@guaranteed MyArray<MyClass>) -> () |
| apply %6(%1) : $@convention(method) (@guaranteed MyArray<MyClass>) -> () |
| release_value %1 : $MyArray<MyClass> |
| strong_unpin %8 : $Optional<Builtin.NativeObject> |
| |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // CHECK: sil @remove_pins_mark_dependence |
| // CHECK-NOT: strong_pin |
| // CHECK-NOT: strong_unpin |
| // CHECK: return |
| |
| sil @remove_pins_mark_dependence : $@convention(thin) (@inout MyArray<MyInt>, MyInt) -> () { |
| bb0(%0 : $*MyArray<MyInt>, %1 : $MyInt): |
| %2 = integer_literal $Builtin.Word, 16 |
| %3 = load %0 : $*MyArray<MyInt> |
| %4 = struct_extract %3 : $MyArray<MyInt>, #MyArray.buffer |
| %5 = struct_extract %4 : $MyArrayBuffer, #MyArrayBuffer.storage |
| %6 = unchecked_trivial_bit_cast %5 : $Builtin.NativeObject to $Builtin.RawPointer |
| %7 = index_raw_pointer %6 : $Builtin.RawPointer, %2 : $Builtin.Word |
| %8 = strong_pin %5 : $Builtin.NativeObject |
| %9 = pointer_to_address %7 : $Builtin.RawPointer to [strict] $*MyInt |
| %10 = integer_literal $Builtin.Word, 2 |
| %11 = index_addr %9 : $*MyInt, %10 : $Builtin.Word |
| %12 = mark_dependence %11 : $*MyInt on %8 : $Optional<Builtin.NativeObject> |
| store %1 to %12 : $*MyInt |
| strong_unpin %8 : $Optional<Builtin.NativeObject> |
| %15 = tuple () |
| return %15 : $() |
| } |
| |
| // CHECK: sil @dont_remove_pins_mark_dependence |
| // CHECK: strong_pin |
| // CHECK: strong_unpin |
| // CHECK: return |
| |
| sil @dont_remove_pins_mark_dependence : $@convention(thin) (@inout MyArray<MyInt>, MyInt, Optional<Builtin.NativeObject>) -> () { |
| bb0(%0 : $*MyArray<MyInt>, %1 : $MyInt, %16 : $Optional<Builtin.NativeObject>): |
| %2 = integer_literal $Builtin.Word, 16 |
| %3 = load %0 : $*MyArray<MyInt> |
| %4 = struct_extract %3 : $MyArray<MyInt>, #MyArray.buffer |
| %5 = struct_extract %4 : $MyArrayBuffer, #MyArrayBuffer.storage |
| %6 = unchecked_trivial_bit_cast %5 : $Builtin.NativeObject to $Builtin.RawPointer |
| %7 = index_raw_pointer %6 : $Builtin.RawPointer, %2 : $Builtin.Word |
| %8 = strong_pin %5 : $Builtin.NativeObject |
| %9 = pointer_to_address %7 : $Builtin.RawPointer to [strict] $*MyInt |
| %10 = integer_literal $Builtin.Word, 2 |
| %11 = index_addr %9 : $*MyInt, %10 : $Builtin.Word |
| %12 = mark_dependence %8 : $Optional<Builtin.NativeObject> on %16 : $Optional<Builtin.NativeObject> |
| strong_unpin %8 : $Optional<Builtin.NativeObject> |
| %15 = tuple () |
| return %15 : $() |
| } |