mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
This occured if a stack-promoted object with a devirtualized final release is not actually allocated on the stack. Now the ReleaseDevirtualizer models the procedure of a final release more accurately. It inserts a set_deallocating instruction and calles the deallocator (instead of just the deinit). This changes also includes two peephole optimizations in IRGen and LLVMStackPromotion which get rid of unused runtime calls in case the stack promoted object is really allocated on the stack. This fixes rdar://problem/25068118
316 lines
13 KiB
Plaintext
316 lines
13 KiB
Plaintext
// RUN: %target-sil-opt -enable-sil-verify-all %s -release-devirtualizer -module-name=test | FileCheck %s
|
|
|
|
sil_stage canonical
|
|
|
|
import Builtin
|
|
import Swift
|
|
|
|
class B {
|
|
}
|
|
|
|
|
|
// CHECK-LABEL: sil @devirtualize_object
|
|
// CHECK: [[A:%[0-9]+]] = alloc_ref
|
|
// CHECK-NEXT: set_deallocating [[A]]
|
|
// CHECK-NOT: strong_release
|
|
// CHECK: [[D:%[0-9]+]] = function_ref @_TFC4test1BD
|
|
// CHECK-NEXT: apply [[D]]([[A]])
|
|
// CHECK-NEXT: dealloc_ref [stack] [[A]]
|
|
// CHECK: return
|
|
sil @devirtualize_object : $@convention(thin) () -> () {
|
|
bb0:
|
|
%1 = alloc_ref [stack] $B
|
|
strong_release %1 : $B
|
|
dealloc_ref [stack] %1 : $B
|
|
%r = tuple ()
|
|
return %r : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @dont_devirtualize_heap_allocated
|
|
// CHECK: alloc_ref
|
|
// CHECK-NEXT: strong_release
|
|
// CHECK-NEXT: dealloc_ref
|
|
// CHECK: return
|
|
sil @dont_devirtualize_heap_allocated : $@convention(thin) () -> () {
|
|
bb0:
|
|
%1 = alloc_ref $B
|
|
strong_release %1 : $B
|
|
dealloc_ref %1 : $B
|
|
%r = tuple ()
|
|
return %r : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @dont_devirtualize_wrong_release
|
|
// CHECK: alloc_ref
|
|
// CHECK-NEXT: strong_release
|
|
// CHECK-NEXT: strong_release
|
|
// CHECK-NEXT: dealloc_ref
|
|
// CHECK: return
|
|
sil @dont_devirtualize_wrong_release : $@convention(thin) (@owned B) -> () {
|
|
bb0(%0 : $B):
|
|
%1 = alloc_ref $B
|
|
strong_release %1 : $B
|
|
strong_release %0 : $B
|
|
dealloc_ref %1 : $B
|
|
%r = tuple ()
|
|
return %r : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @dont_devirtualize_unknown_release
|
|
// CHECK: alloc_ref
|
|
// CHECK-NEXT: strong_release
|
|
// CHECK: [[F:%[0-9]+]] = function_ref @unknown_func
|
|
// CHECK-NEXT: apply [[F]]()
|
|
// CHECK-NEXT: dealloc_ref
|
|
// CHECK: return
|
|
sil @dont_devirtualize_unknown_release : $@convention(thin) (@owned B) -> () {
|
|
bb0(%0 : $B):
|
|
%1 = alloc_ref $B
|
|
strong_release %1 : $B
|
|
%f = function_ref @unknown_func : $@convention(thin) () -> ()
|
|
%a = apply %f() : $@convention(thin) () -> ()
|
|
dealloc_ref %1 : $B
|
|
%r = tuple ()
|
|
return %r : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @dont_crash_with_missing_release
|
|
// CHECK: [[A:%[0-9]+]] = alloc_ref
|
|
// CHECK-NEXT: set_deallocating [[A]]
|
|
// CHECK-NOT: strong_release
|
|
// CHECK: [[D:%[0-9]+]] = function_ref @_TFC4test1BD
|
|
// CHECK-NEXT: apply [[D]]([[A]])
|
|
// CHECK-NEXT: dealloc_ref [stack] [[A]]
|
|
// CHECK: return
|
|
sil @dont_crash_with_missing_release : $@convention(thin) () -> () {
|
|
bb0:
|
|
%1 = alloc_ref [stack] $B
|
|
strong_release %1 : $B
|
|
dealloc_ref [stack] %1 : $B
|
|
%2 = alloc_ref [stack] $B
|
|
dealloc_ref [stack] %2 : $B
|
|
%r = tuple ()
|
|
return %r : $()
|
|
}
|
|
|
|
|
|
class MyArrayStorage<Element> {
|
|
}
|
|
|
|
struct WrongStorage {
|
|
}
|
|
|
|
// CHECK-LABEL: sil @devirtualize_buffer
|
|
// CHECK: [[A:%[0-9]+]] = function_ref @swift_bufferAllocateOnStack
|
|
// CHECK: [[B:%[0-9]+]] = apply [[A]]
|
|
// CHECK-NOT: strong_release
|
|
// CHECK: unchecked_ref_cast
|
|
// CHECK: [[C:%[0-9]+]] = unchecked_ref_cast [[B]]
|
|
// CHECK: set_deallocating [[C]]
|
|
// CHECK: [[D:%[0-9]+]] = function_ref @_TFC4test14MyArrayStorageD
|
|
// CHECK: apply [[D]]<Int64>([[C]])
|
|
// CHECK: [[X:%[0-9]+]] = function_ref @swift_bufferDeallocateFromStack
|
|
// CHECK: apply [[X]]([[B]])
|
|
// CHECK: return
|
|
sil @devirtualize_buffer : $@convention(thin) () -> () {
|
|
bb0:
|
|
%3 = integer_literal $Builtin.Int64, 7
|
|
%4 = struct $Int64 (%3 : $Builtin.Int64)
|
|
%mt = metatype $@thick MyArrayStorage<Int64>.Type // user: %5
|
|
%5 = init_existential_metatype %mt : $@thick MyArrayStorage<Int64>.Type, $@thick AnyObject.Type
|
|
%66 = function_ref @swift_bufferAllocateOnStack : $@convention(thin) (@thick AnyObject.Type, Int64, Int64) -> @owned AnyObject
|
|
%67 = apply %66(%5, %4, %4) : $@convention(thin) (@thick AnyObject.Type, Int64, Int64) -> @owned AnyObject
|
|
%95 = unchecked_ref_cast %67 : $AnyObject to $Builtin.BridgeObject
|
|
strong_release %95 : $Builtin.BridgeObject
|
|
%119 = function_ref @swift_bufferDeallocateFromStack : $@convention(thin) (@guaranteed AnyObject) -> @owned ()
|
|
%120 = apply %119(%67) : $@convention(thin) (@guaranteed AnyObject) -> @owned ()
|
|
|
|
%r = tuple ()
|
|
return %r : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @dont_devirtualize_buffer_with_unknown_release
|
|
// CHECK: function_ref @swift_bufferAllocateOnStack
|
|
// CHECK-NEXT: apply
|
|
// CHECK-NOT: apply
|
|
// CHECK: strong_release
|
|
// CHECK-NEXT: strong_release
|
|
// CHECK-NOT: apply
|
|
// CHECK: function_ref @swift_bufferDeallocateFromStack
|
|
// CHECK-NEXT: apply
|
|
// CHECK: return
|
|
sil @dont_devirtualize_buffer_with_unknown_release : $@convention(thin) (@owned Builtin.BridgeObject) -> () {
|
|
bb0(%0 : $Builtin.BridgeObject):
|
|
%3 = integer_literal $Builtin.Int64, 7
|
|
%4 = struct $Int64 (%3 : $Builtin.Int64)
|
|
%mt = metatype $@thick MyArrayStorage<Int64>.Type // user: %5
|
|
%5 = init_existential_metatype %mt : $@thick MyArrayStorage<Int64>.Type, $@thick AnyObject.Type
|
|
%66 = function_ref @swift_bufferAllocateOnStack : $@convention(thin) (@thick AnyObject.Type, Int64, Int64) -> @owned AnyObject
|
|
%67 = apply %66(%5, %4, %4) : $@convention(thin) (@thick AnyObject.Type, Int64, Int64) -> @owned AnyObject
|
|
%95 = unchecked_ref_cast %67 : $AnyObject to $Builtin.BridgeObject
|
|
strong_release %95 : $Builtin.BridgeObject
|
|
strong_release %0 : $Builtin.BridgeObject
|
|
%119 = function_ref @swift_bufferDeallocateFromStack : $@convention(thin) (@guaranteed AnyObject) -> @owned ()
|
|
%120 = apply %119(%67) : $@convention(thin) (@guaranteed AnyObject) -> @owned ()
|
|
|
|
%r = tuple ()
|
|
return %r : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @dont_devirtualize_buffer_with_unknown_allocation
|
|
// CHECK: function_ref @swift_bufferAllocate
|
|
// CHECK-NEXT: apply
|
|
// CHECK-NOT: apply
|
|
// CHECK: strong_release
|
|
// CHECK-NOT: apply
|
|
// CHECK: function_ref @swift_bufferDeallocateFromStack
|
|
// CHECK-NEXT: apply
|
|
// CHECK: return
|
|
sil @dont_devirtualize_buffer_with_unknown_allocation : $@convention(thin) () -> () {
|
|
bb0:
|
|
%3 = integer_literal $Builtin.Int64, 7
|
|
%4 = struct $Int64 (%3 : $Builtin.Int64)
|
|
%mt = metatype $@thick MyArrayStorage<Int64>.Type // user: %5
|
|
%5 = init_existential_metatype %mt : $@thick MyArrayStorage<Int64>.Type, $@thick AnyObject.Type
|
|
%66 = function_ref @swift_bufferAllocate: $@convention(thin) (@thick AnyObject.Type, Int64, Int64) -> @owned AnyObject
|
|
%67 = apply %66(%5, %4, %4) : $@convention(thin) (@thick AnyObject.Type, Int64, Int64) -> @owned AnyObject
|
|
%95 = unchecked_ref_cast %67 : $AnyObject to $Builtin.BridgeObject
|
|
strong_release %95 : $Builtin.BridgeObject
|
|
%119 = function_ref @swift_bufferDeallocateFromStack : $@convention(thin) (@guaranteed AnyObject) -> @owned ()
|
|
%120 = apply %119(%67) : $@convention(thin) (@guaranteed AnyObject) -> @owned ()
|
|
|
|
%r = tuple ()
|
|
return %r : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @dont_devirtualize_buffer_with_unknown_metatype
|
|
// CHECK: function_ref @swift_bufferAllocateOnStack
|
|
// CHECK-NEXT: apply
|
|
// CHECK-NOT: apply
|
|
// CHECK: strong_release
|
|
// CHECK-NOT: apply
|
|
// CHECK: function_ref @swift_bufferDeallocateFromStack
|
|
// CHECK-NEXT: apply
|
|
// CHECK: return
|
|
sil @dont_devirtualize_buffer_with_unknown_metatype : $@convention(thin) (@thick AnyObject.Type) -> () {
|
|
bb0(%0 : $@thick AnyObject.Type):
|
|
%3 = integer_literal $Builtin.Int64, 7
|
|
%4 = struct $Int64 (%3 : $Builtin.Int64)
|
|
%66 = function_ref @swift_bufferAllocateOnStack: $@convention(thin) (@thick AnyObject.Type, Int64, Int64) -> @owned AnyObject
|
|
%67 = apply %66(%0, %4, %4) : $@convention(thin) (@thick AnyObject.Type, Int64, Int64) -> @owned AnyObject
|
|
%95 = unchecked_ref_cast %67 : $AnyObject to $Builtin.BridgeObject
|
|
strong_release %95 : $Builtin.BridgeObject
|
|
%119 = function_ref @swift_bufferDeallocateFromStack : $@convention(thin) (@guaranteed AnyObject) -> @owned ()
|
|
%120 = apply %119(%67) : $@convention(thin) (@guaranteed AnyObject) -> @owned ()
|
|
|
|
%r = tuple ()
|
|
return %r : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil @dont_devirtualize_buffer_with_wrong_metatype
|
|
// CHECK: function_ref @swift_bufferAllocateOnStack
|
|
// CHECK-NEXT: apply
|
|
// CHECK-NOT: apply
|
|
// CHECK: strong_release
|
|
// CHECK-NOT: apply
|
|
// CHECK: function_ref @swift_bufferDeallocateFromStack
|
|
// CHECK-NEXT: apply
|
|
// CHECK: return
|
|
sil @dont_devirtualize_buffer_with_wrong_metatype : $@convention(thin) () -> () {
|
|
bb0:
|
|
%3 = integer_literal $Builtin.Int64, 7
|
|
%4 = struct $Int64 (%3 : $Builtin.Int64)
|
|
%mt = metatype $@thick WrongStorage.Type // user: %5
|
|
%5 = init_existential_metatype %mt : $@thick WrongStorage.Type, $@thick AnyObject.Type
|
|
%66 = function_ref @swift_bufferAllocateOnStack : $@convention(thin) (@thick AnyObject.Type, Int64, Int64) -> @owned AnyObject
|
|
%67 = apply %66(%5, %4, %4) : $@convention(thin) (@thick AnyObject.Type, Int64, Int64) -> @owned AnyObject
|
|
%95 = unchecked_ref_cast %67 : $AnyObject to $Builtin.BridgeObject
|
|
strong_release %95 : $Builtin.BridgeObject
|
|
%119 = function_ref @swift_bufferDeallocateFromStack : $@convention(thin) (@guaranteed AnyObject) -> @owned ()
|
|
%120 = apply %119(%67) : $@convention(thin) (@guaranteed AnyObject) -> @owned ()
|
|
|
|
%r = tuple ()
|
|
return %r : $()
|
|
}
|
|
|
|
sil hidden_external @swift_bufferAllocate : $@convention(thin) (@thick AnyObject.Type, Int64, Int64) -> @owned AnyObject
|
|
|
|
sil hidden_external @swift_bufferAllocateOnStack : $@convention(thin) (@thick AnyObject.Type, Int64, Int64) -> @owned AnyObject
|
|
|
|
sil hidden_external @swift_bufferDeallocateFromStack : $@convention(thin) (@guaranteed AnyObject) -> @owned ()
|
|
|
|
sil @unknown_func : $@convention(thin) () -> ()
|
|
|
|
// test.B.__deallocating_deinit
|
|
sil hidden @_TFC4test1BD : $@convention(method) (@owned B) -> () {
|
|
// %0 // users: %1, %3
|
|
bb0(%0 : $B):
|
|
debug_value %0 : $B, let, name "self" // id: %1
|
|
// function_ref test.B.deinit
|
|
%2 = function_ref @_TFC4test1Bd : $@convention(method) (@guaranteed B) -> @owned Builtin.NativeObject // user: %3
|
|
%3 = apply %2(%0) : $@convention(method) (@guaranteed B) -> @owned Builtin.NativeObject // user: %4
|
|
%4 = unchecked_ref_cast %3 : $Builtin.NativeObject to $B // user: %5
|
|
dealloc_ref %4 : $B // id: %5
|
|
%6 = tuple () // user: %7
|
|
return %6 : $() // id: %7
|
|
}
|
|
|
|
// test.B.deinit
|
|
sil hidden @_TFC4test1Bd : $@convention(method) (@guaranteed B) -> @owned Builtin.NativeObject {
|
|
// %0 // users: %1, %2
|
|
bb0(%0 : $B):
|
|
debug_value %0 : $B, let, name "self" // id: %1
|
|
%2 = unchecked_ref_cast %0 : $B to $Builtin.NativeObject // user: %3
|
|
return %2 : $Builtin.NativeObject // id: %3
|
|
}
|
|
|
|
// test.B.init () -> test.B
|
|
sil hidden @_TFC4test1BcfT_S0_ : $@convention(method) (@owned B) -> @owned B {
|
|
// %0 // users: %1, %2
|
|
bb0(%0 : $B):
|
|
debug_value %0 : $B, let, name "self" // id: %1
|
|
return %0 : $B // id: %2
|
|
}
|
|
|
|
sil_vtable B {
|
|
#B.deinit!deallocator: _TFC4test1BD // test.B.__deallocating_deinit
|
|
#B.init!initializer.1: _TFC4test1BcfT_S0_ // test.B.init () -> test.B
|
|
}
|
|
|
|
// test.MyArrayStorage.__deallocating_deinit
|
|
sil hidden @_TFC4test14MyArrayStorageD : $@convention(method) <Element> (@owned MyArrayStorage<Element>) -> () {
|
|
// %0 // users: %1, %3
|
|
bb0(%0 : $MyArrayStorage<Element>):
|
|
debug_value %0 : $MyArrayStorage<Element>, let, name "self" // id: %1
|
|
// function_ref test.MyArrayStorage.deinit
|
|
%2 = function_ref @_TFC4test14MyArrayStoraged : $@convention(method) <τ_0_0> (@guaranteed MyArrayStorage<τ_0_0>) -> @owned Builtin.NativeObject // user: %3
|
|
%3 = apply %2<Element>(%0) : $@convention(method) <τ_0_0> (@guaranteed MyArrayStorage<τ_0_0>) -> @owned Builtin.NativeObject // user: %4
|
|
%4 = unchecked_ref_cast %3 : $Builtin.NativeObject to $MyArrayStorage<Element> // user: %5
|
|
dealloc_ref %4 : $MyArrayStorage<Element> // id: %5
|
|
%6 = tuple () // user: %7
|
|
return %6 : $() // id: %7
|
|
}
|
|
|
|
// test.MyArrayStorage.deinit
|
|
sil hidden @_TFC4test14MyArrayStoraged : $@convention(method) <Element> (@guaranteed MyArrayStorage<Element>) -> @owned Builtin.NativeObject {
|
|
// %0 // users: %1, %2
|
|
bb0(%0 : $MyArrayStorage<Element>):
|
|
debug_value %0 : $MyArrayStorage<Element>, let, name "self" // id: %1
|
|
%2 = unchecked_ref_cast %0 : $MyArrayStorage<Element> to $Builtin.NativeObject // user: %3
|
|
return %2 : $Builtin.NativeObject // id: %3
|
|
}
|
|
|
|
// test.MyArrayStorage.init () -> test.MyArrayStorage<A>
|
|
sil hidden @_TFC4test14MyArrayStoragecfT_GS0_x_ : $@convention(method) <Element> (@owned MyArrayStorage<Element>) -> @owned MyArrayStorage<Element> {
|
|
// %0 // users: %1, %2
|
|
bb0(%0 : $MyArrayStorage<Element>):
|
|
debug_value %0 : $MyArrayStorage<Element>, let, name "self" // id: %1
|
|
return %0 : $MyArrayStorage<Element> // id: %2
|
|
}
|
|
|
|
sil_vtable MyArrayStorage {
|
|
#MyArrayStorage.deinit!deallocator: _TFC4test14MyArrayStorageD // test.MyArrayStorage.__deallocating_deinit
|
|
#MyArrayStorage.init!initializer.1: _TFC4test14MyArrayStoragecfT_GS0_x_ // test.MyArrayStorage.init () -> test.MyArrayStorage<A>
|
|
}
|
|
|