Files
swift-mirror/test/SILOptimizer/class_destroy_devirtualizer.sil
Erik Eckstein 799bbe1d44 Optimizer: add OSSA support in the ReleaseDevirtualizer
For now, create a new ClassDestroyDevirtualizer pass, which is basically a copy of the old ReleaseDevirtualizer pass, just with support for OSSA.
We still don't have OSSA in the pipeline where the pass runs. So we need to keep the old pass
around. The new pass is a no-op until we extend OSSA to this part of the pipeline.
Then we can delete the old pass.

The new pass is named ClassDestroyDevirtualizer to distinguish the optimization from de-virtualizing value-type deinits.
2026-09-11 10:49:04 +02:00

283 lines
8.1 KiB
Plaintext

// RUN: %target-sil-opt -enable-sil-verify-all %s -class-destroy-devirtualizer -module-name=test | %FileCheck %s
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 [ossa] @$s4test1BCfD : $@convention(method) (@owned B) -> () {
// Note that the destructor is not destroying, but it must block the optimization.
[global: ]
bb0(%0 : @owned $B):
dealloc_ref %0
%r = tuple ()
return %r
}
// CHECK-LABEL: sil [ossa] @devirtualize_object :
// CHECK: [[A:%[0-9]+]] = alloc_ref
// CHECK-NEXT: [[BD:%.*]] = begin_dealloc_ref [[A]] of [[A]]
// CHECK-NOT: destroy_value
// 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 [ossa] @devirtualize_object : $@convention(thin) () -> () {
bb0:
%1 = alloc_ref [stack] $B
destroy_value %1
dealloc_stack_ref %1
%r = tuple ()
return %r
}
// CHECK-LABEL: sil [ossa] @dont_devirtualize_heap_allocated :
// CHECK: alloc_ref
// CHECK-NEXT: destroy_value
// CHECK: } // end sil function 'dont_devirtualize_heap_allocated'
sil [ossa] @dont_devirtualize_heap_allocated : $@convention(thin) () -> () {
bb0:
%1 = alloc_ref $B
destroy_value %1
%r = tuple ()
return %r
}
// CHECK-LABEL: sil [ossa] @dont_devirtualize_devirtualized
// CHECK: alloc_ref
// CHECK-NEXT: copy_value
// CHECK-NEXT: destroy_value
// 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 [ossa] @dont_devirtualize_devirtualized : $@convention(thin) () -> () {
bb0:
%1 = alloc_ref [stack] $B
%2 = copy_value %1
destroy_value %2
%4 = begin_dealloc_ref %1 of %1
dealloc_ref %4
dealloc_stack_ref %1
%r = tuple ()
return %r
}
// CHECK-LABEL: sil [ossa] @dont_devirtualize_devirtualized_not_inlined :
// CHECK: alloc_ref
// CHECK-NEXT: copy_value
// CHECK-NEXT: destroy_value
// CHECK-NEXT: begin_dealloc_ref
// CHECK: } // end sil function 'dont_devirtualize_devirtualized_not_inlined'
sil [ossa] @dont_devirtualize_devirtualized_not_inlined : $@convention(thin) () -> () {
bb0:
%1 = alloc_ref [stack] $B
%2 = copy_value %1
destroy_value %2
%4 = begin_dealloc_ref %1 of %1
%5 = function_ref @$s4test1BCfD : $@convention(method) (@owned B) -> ()
%6 = apply %5(%4) : $@convention(method) (@owned B) -> ()
dealloc_stack_ref %1
%r = tuple ()
return %r
}
// CHECK-LABEL: sil [ossa] @devirtualize_with_other_destroys_in_between :
// CHECK: %1 = alloc_ref [stack] $B
// CHECK-NOT: destroy_value
// CHECK: apply
// CHECK-NEXT: destroy_value %0
// CHECK-NEXT: dealloc_stack_ref %1
// CHECK: } // end sil function 'devirtualize_with_other_destroys_in_between'
sil [ossa] @devirtualize_with_other_destroys_in_between : $@convention(thin) (@owned B) -> () {
bb0(%0 : @owned $B):
%1 = alloc_ref [stack] $B
destroy_value %1
destroy_value %0
dealloc_stack_ref %1
%r = tuple ()
return %r
}
// CHECK-LABEL: sil [ossa] @devirtualize_with_apply_in_between :
// CHECK: alloc_ref
// CHECK-NEXT: begin_dealloc_ref
// CHECK-NOT: destroy_value
// CHECK: } // end sil function 'devirtualize_with_apply_in_between'
sil [ossa] @devirtualize_with_apply_in_between : $@convention(thin) () -> () {
bb0:
%1 = alloc_ref [stack] $B
destroy_value %1 : $B
%f = function_ref @unknown_func : $@convention(thin) () -> ()
%a = apply %f() : $@convention(thin) () -> ()
dealloc_stack_ref %1
%r = tuple ()
return %r
}
// CHECK-LABEL: sil [ossa] @dont_devirtualize_unknown_destroy :
// CHECK: alloc_ref
// CHECK-NEXT: copy_value
// CHECK-NEXT: destroy_value
// CHECK: } // end sil function 'dont_devirtualize_unknown_destroy'
sil [ossa] @dont_devirtualize_unknown_destroy : $@convention(thin) () -> () {
bb0:
%1 = alloc_ref [stack] $B
%2 = copy_value %1
destroy_value %2
%f = function_ref @unknown_releaseing_func : $@convention(thin) (@owned B) -> ()
%a = apply %f(%1) : $@convention(thin) (@owned B) -> ()
dealloc_stack_ref %1
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @dont_crash_with_missing_destroy
// CHECK: [[A:%[0-9]+]] = alloc_ref
// CHECK-NEXT: [[BD:%.*]] = begin_dealloc_ref [[A]] of [[A]]
// CHECK-NOT: destroy_value
// 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_destroy'
sil [ossa] @dont_crash_with_missing_destroy : $@convention(thin) () -> () {
bb0:
%1 = alloc_ref [stack] $B
destroy_value %1 : $B
dealloc_stack_ref %1
%2 = alloc_ref [stack] $B
end_lifetime %2
dealloc_stack_ref %2
%r = tuple ()
return %r : $()
}
// CHECK-LABEL: sil [ossa] @long_chain_from_alloc_to_destroy
// CHECK: bb3([[P:%.*]] : @owned $B):
// CHECK: [[T:%.*]] = tuple ({{.*}})
// CHECK: ([[S:%.*]], {{.*}}) = destructure_tuple [[T]]
// CHECK: [[A:%.*]] = destructure_struct [[S]]
// CHECK: begin_dealloc_ref [[A]]
// CHECK-NOT: destroy_value
// CHECK: } // end sil function 'long_chain_from_alloc_to_destroy'
sil [ossa] @long_chain_from_alloc_to_destroy : $@convention(thin) () -> () {
bb0:
%1 = alloc_ref [stack] $B
%2 = struct $Str(%1)
%3 = destructure_struct %2
cond_br undef, bb1, bb2
bb1:
br bb3(%3)
bb2:
br bb3(%3)
bb3(%a : @owned $B):
%s = struct $Str (%a)
%i = integer_literal $Builtin.Int64, 0
%t = tuple (%s, %i)
destroy_value %t
dealloc_stack_ref %1
%r = tuple ()
return %r
}
// CHECK-LABEL: sil [ossa] @dont_devirtualize_double_destroy
// CHECK-NOT: begin_dealloc_ref
// CHECK: destroy_value
// CHECK: } // end sil function 'dont_devirtualize_double_destroy'
sil [ossa] @dont_devirtualize_double_destroy : $@convention(thin) () -> () {
bb0:
%1 = alloc_ref [stack] $B
%2 = copy_value %1
%t = tuple (%1, %2)
destroy_value %t : $(B, B)
dealloc_stack_ref %1
%r = tuple ()
return %r
}
// CHECK-LABEL: sil [ossa] @dont_devirtualize_destroy_of_load
// CHECK: [[L:%.*]] = load
// CHECK: [[D:%[0-9]+]] = function_ref @$s4test1BCfD
// CHECK-NEXT: apply [[D]]
// CHECK-NEXT: destroy_value [[L]]
// CHECK: } // end sil function 'dont_devirtualize_destroy_of_load'
sil [ossa] @dont_devirtualize_destroy_of_load : $@convention(thin) () -> () {
bb0:
%1 = alloc_ref [stack] $B
%2 = begin_borrow %1
%3 = ref_element_addr %2, #B.next
%4 = load [copy] %3
end_borrow %2
destroy_value %1
destroy_value %4
dealloc_stack_ref %1
%r = tuple ()
return %r
}
// CHECK-LABEL: sil [ossa] @dont_devirtualize_destroy_of_different_allocations
// CHECK-NOT: begin_dealloc_ref
// CHECK: bb3([[P:%.*]] : @owned $B):
// CHECK: destroy_value [[P]]
// CHECK: } // end sil function 'dont_devirtualize_destroy_of_different_allocations'
sil [ossa] @dont_devirtualize_destroy_of_different_allocations : $@convention(thin) () -> () {
bb0:
%1 = alloc_ref [stack] $B
%2 = alloc_ref [stack] $B
cond_br undef, bb1, bb2
bb1:
destroy_value %2
br bb3(%1)
bb2:
destroy_value %1
br bb3(%2)
bb3(%a : @owned $B):
destroy_value %a
dealloc_stack_ref %2
dealloc_stack_ref %1
%r = tuple ()
return %r
}
// CHECK-LABEL: sil [ossa] @devirtualize_double_destroy :
// CHECK-NOT: destroy_value
// CHECK: } // end sil function 'devirtualize_double_destroy'
sil [ossa] @devirtualize_double_destroy : $@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
destroy_value %1
destroy_value %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
}