Files
swift-mirror/test/SILOptimizer/devirt_release.sil
Erik Eckstein bf87de3bc3 Fix a memory leak caused by the ReleaseDevirtualizer.
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
2016-03-15 12:56:54 -07:00

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>
}