Files
swift-mirror/test/SILOptimizer/devirt_release.sil
Erik Eckstein 9cdd9fabae ReleaseDevirtualizer: improve finding the last release of an allocation
With OSSA it can happen more easily that the final release is not immediately located before the related dealloc_stack_ref.
Therefore do a more precise check (using escape analysis) if any instruction between a release and a dealloc_stack_ref might (implicitly) release the allocated object.

This is part of fixing regressions when enabling OSSA modules:
rdar://140229560
2024-12-13 10:06:35 +01:00

275 lines
7.9 KiB
Plaintext

// Legacy passes are tested with the Windows PR tests. So no need to test them on macOS/Linux
// (they don't run on these platforms anyway). Thus we don't test both new Swift and legacy passes
// on the same platform at the same time.
// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all %s -release-devirtualizer -module-name=test | %FileCheck %s
// REQUIRES: swift_in_compiler
sil_stage canonical
import Builtin
import Swift
class B {
@_hasStorage var next: B
}
struct Str {
@_hasStorage var b: B
}
sil @unknown_func : $@convention(thin) () -> ()
sil @unknown_releaseing_func : $@convention(thin) (@owned B) -> ()
sil @$s4test1BCfD : $@convention(method) (@owned B) -> () {
// Note that the destructor is not destroying, but it must block the optimization.
[global: ]
bb0(%0 : $B):
dealloc_ref %0
%r = tuple ()
return %r
}
// CHECK-LABEL: sil @devirtualize_object
// CHECK: [[A:%[0-9]+]] = alloc_ref
// CHECK-NEXT: [[BD:%.*]] = begin_dealloc_ref [[A]] : $B of [[A]]
// CHECK-NOT: strong_release
// CHECK: [[D:%[0-9]+]] = function_ref @$s4test1BCfD
// CHECK-NEXT: apply [[D]]([[BD]])
// CHECK-NEXT: dealloc_stack_ref [[A]]
// CHECK: } // end sil function 'devirtualize_object'
sil @devirtualize_object : $@convention(thin) () -> () {
bb0:
%1 = alloc_ref [stack] $B
strong_release %1 : $B
dealloc_stack_ref %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
// CHECK: } // end sil function 'dont_devirtualize_heap_allocated'
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_devirtualized
// CHECK: alloc_ref
// CHECK-NEXT: strong_retain
// CHECK-NEXT: strong_release
// CHECK-NEXT: begin_dealloc_ref
// CHECK-NEXT: dealloc_ref
// CHECK-NEXT: dealloc_stack_ref
// CHECK-NEXT: tuple
// CHECK: } // end sil function 'dont_devirtualize_devirtualized'
sil @dont_devirtualize_devirtualized : $@convention(thin) () -> () {
bb0:
%1 = alloc_ref [stack] $B
strong_retain %1 : $B
strong_release %1 : $B
%6 = begin_dealloc_ref %1 : $B of %1 : $B
dealloc_ref %6 : $B
dealloc_stack_ref %1 : $B
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil @dont_devirtualize_devirtualized_not_inlined :
// CHECK: alloc_ref
// CHECK-NEXT: strong_retain
// CHECK-NEXT: strong_release
// CHECK-NEXT: begin_dealloc_ref
// CHECK: } // end sil function 'dont_devirtualize_devirtualized_not_inlined'
sil @dont_devirtualize_devirtualized_not_inlined : $@convention(thin) () -> () {
bb0:
%1 = alloc_ref [stack] $B
strong_retain %1 : $B
strong_release %1 : $B
%6 = begin_dealloc_ref %1 : $B of %1 : $B
%7 = function_ref @$s4test1BCfD : $@convention(method) (@owned B) -> ()
%8 = apply %7(%6) : $@convention(method) (@owned B) -> ()
dealloc_stack_ref %1 : $B
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil @devirtualize_with_other_release_in_between :
// CHECK: %1 = alloc_ref [stack] $B
// CHECK-NOT: release
// CHECK: apply
// CHECK-NEXT: strong_release %0
// CHECK-NEXT: dealloc_stack_ref %1
// CHECK: } // end sil function 'devirtualize_with_other_release_in_between'
sil @devirtualize_with_other_release_in_between : $@convention(thin) (@owned B) -> () {
bb0(%0 : $B):
%1 = alloc_ref [stack] $B
strong_release %1 : $B
strong_release %0 : $B
dealloc_stack_ref %1 : $B
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil @devirtualize_with_apply_in_between :
// CHECK: alloc_ref
// CHECK-NEXT: begin_dealloc_ref
// CHECK-NOT: release
// CHECK: } // end sil function 'devirtualize_with_apply_in_between'
sil @devirtualize_with_apply_in_between : $@convention(thin) (@owned B) -> () {
bb0(%0 : $B):
%1 = alloc_ref [stack] $B
strong_release %1 : $B
%f = function_ref @unknown_func : $@convention(thin) () -> ()
%a = apply %f() : $@convention(thin) () -> ()
dealloc_stack_ref %1 : $B
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil @dont_devirtualize_unknown_release :
// CHECK: alloc_ref
// CHECK-NEXT: strong_release
// CHECK: } // end sil function 'dont_devirtualize_unknown_release'
sil @dont_devirtualize_unknown_release : $@convention(thin) (@owned B) -> () {
bb0(%0 : $B):
%1 = alloc_ref [stack] $B
strong_release %1 : $B
%f = function_ref @unknown_releaseing_func : $@convention(thin) (@owned B) -> ()
%a = apply %f(%1) : $@convention(thin) (@owned B) -> ()
dealloc_stack_ref %1 : $B
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil @dont_crash_with_missing_release
// CHECK: [[A:%[0-9]+]] = alloc_ref
// CHECK-NEXT: [[BD:%.*]] = begin_dealloc_ref [[A]] : $B of [[A]]
// CHECK-NOT: strong_release
// CHECK: [[D:%[0-9]+]] = function_ref @$s4test1BCfD
// CHECK-NEXT: apply [[D]]([[BD]])
// CHECK-NEXT: dealloc_stack_ref [[A]]
// CHECK: } // end sil function 'dont_crash_with_missing_release'
sil @dont_crash_with_missing_release : $@convention(thin) () -> () {
bb0:
%1 = alloc_ref [stack] $B
strong_release %1 : $B
dealloc_stack_ref %1 : $B
%2 = alloc_ref [stack] $B
dealloc_stack_ref %2 : $B
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil @long_chain_from_alloc_to_release
// CHECK: begin_dealloc_ref
// CHECK-NOT: release_value
// CHECK: } // end sil function 'long_chain_from_alloc_to_release'
sil @long_chain_from_alloc_to_release : $@convention(thin) () -> () {
bb0:
%1 = alloc_ref [stack] $B
%2 = struct $Str(%1 : $B)
%3 = struct_extract %2 : $Str, #Str.b
cond_br undef, bb1, bb2
bb1:
br bb3(%3 : $B)
bb2:
br bb3(%3 : $B)
bb3(%a : $B):
%s = struct $Str (%a : $B)
%i = integer_literal $Builtin.Int64, 0
%t = tuple (%s : $Str, %i : $Builtin.Int64)
release_value %t : $(Str, Builtin.Int64)
dealloc_stack_ref %1 : $B
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil @dont_devirtualize_double_release
// CHECK-NOT: set_deallocating
// CHECK: release_value
// CHECK: } // end sil function 'dont_devirtualize_double_release'
sil @dont_devirtualize_double_release : $@convention(thin) () -> () {
bb0:
%1 = alloc_ref [stack] $B
%t = tuple (%1 : $B, %1 : $B)
release_value %t : $(B, B)
dealloc_stack_ref %1 : $B
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil @dont_devirtualize_release_of_load
// CHECK-NOT: set_deallocating
// CHECK: strong_release
// CHECK: } // end sil function 'dont_devirtualize_release_of_load'
sil @dont_devirtualize_release_of_load : $@convention(thin) () -> () {
bb0:
%1 = alloc_ref [stack] $B
%2 = ref_element_addr %1 : $B, #B.next
%3 = load %2 : $*B
strong_release %3 : $B
dealloc_stack_ref %1 : $B
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil @dont_devirtualize_release_of_different_allocations
// CHECK-NOT: set_deallocating
// CHECK: strong_release
// CHECK: } // end sil function 'dont_devirtualize_release_of_different_allocations'
sil @dont_devirtualize_release_of_different_allocations : $@convention(thin) () -> () {
bb0:
%1 = alloc_ref [stack] $B
%2 = alloc_ref [stack] $B
cond_br undef, bb1, bb2
bb1:
br bb3(%1 : $B)
bb2:
br bb3(%2 : $B)
bb3(%a : $B):
strong_release %a : $B
dealloc_stack_ref %2 : $B
dealloc_stack_ref %1 : $B
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil @devirtualize_double_release :
// CHECK-NOT: release
// CHECK: } // end sil function 'devirtualize_double_release'
sil @devirtualize_double_release : $@convention(thin) () -> () {
bb0:
%0 = alloc_ref [stack] $B
%1 = end_init_let_ref %0
%2 = alloc_ref [stack] $B
%3 = end_init_let_ref %2
strong_release %1
strong_release %3
%4 = function_ref @unknown_func : $@convention(thin) () -> ()
%5 = apply %4() : $@convention(thin) () -> ()
dealloc_stack_ref %2
dealloc_stack_ref %0
%r = tuple ()
return %r
}
sil_vtable B {
#B.deinit!deallocator: @$s4test1BCfD
}