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