blob: 34d5f4486f2532b9ffa095aadd0677f3e831a6ce [file] [log] [blame]
// 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 : $()
}