mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
It hoists `destroy_value` instructions for non-lexical values. ``` %1 = some_ownedValue ... last_use(%1) ... // other instructions destroy_value %1 ``` -> ``` %1 = some_ownedValue ... last_use(%1) destroy_value %1 // <- moved after the last use ... // other instructions ``` In contrast to non-mandatory optimization passes, this is the only pass which hoists destroys over deinit-barriers. This ensures consistent behavior in -Onone and optimized builds.
242 lines
12 KiB
Plaintext
242 lines
12 KiB
Plaintext
// RUN: %target-swift-frontend -emit-irgen -disable-emit-type-malloc-for-coro-frame %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize --check-prefix=CHECK-%target-ptrsize-%target-ptrauth -DINT=i%target-ptrsize
|
|
// UNSUPPORTED: CPU=arm64_32
|
|
|
|
import Builtin
|
|
import Swift
|
|
|
|
sil @marker : $(Builtin.Int32) -> ()
|
|
|
|
class SomeClass {}
|
|
sil_vtable SomeClass {}
|
|
|
|
class SomeSubclass : SomeClass {}
|
|
sil_vtable SomeSubclass {}
|
|
|
|
// This is designed to be loadable, but large enough that we want to
|
|
// pass it indirectly. This triggers a bunch of special handling in
|
|
// some of the IRGen preparation passes, too.
|
|
struct Big<T: SomeClass> {
|
|
var a: T
|
|
var b: T
|
|
var c: T
|
|
var d: T
|
|
var e: T
|
|
var f: T
|
|
var g: T
|
|
var h: T
|
|
}
|
|
|
|
struct BigWrapper<T : SomeClass> {
|
|
var big: Big<T>
|
|
}
|
|
|
|
sil @make_big : $<T: SomeClass> () -> (@owned Big<T>)
|
|
sil @use_some_class : $<T : SomeClass> (@guaranteed T) -> ()
|
|
|
|
// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { ptr, ptr } @test_simple
|
|
// CHECK-32-SAME: (ptr noalias dereferenceable([[BUFFER_SIZE:16]]) %0, ptr %C)
|
|
// CHECK-64-SAME: (ptr noalias dereferenceable([[BUFFER_SIZE:32]]) %0, ptr %C)
|
|
sil [ossa] @test_simple : $@yield_once <C: SomeClass> () -> (@yields @owned Big<C>) {
|
|
entry:
|
|
// Allocate space for the return value of make_big.
|
|
// CHECK: [[TEMP:%.*]] = alloca [[BIG:%T14yield_once_big3BigV]]
|
|
// CHECK-32-SAME: , align 4
|
|
// CHECK-64-SAME: , align 8
|
|
|
|
// Coroutine setup.
|
|
// CHECK-32-NEXT: [[ID:%.*]] = call token (i32, i32, ptr, ptr, ptr, ptr, ...) @llvm.coro.id.retcon.once(i32 [[BUFFER_SIZE]], i32 [[BUFFER_ALIGN:4]], ptr %0, ptr @"$s14yield_once_big3BigVyxGAA9SomeClassCRbzlIetAYi_TC", ptr @malloc, ptr @free)
|
|
// CHECK-64-NEXT: [[ID:%.*]] = call token (i32, i32, ptr, ptr, ptr, ptr, ...) @llvm.coro.id.retcon.once(i32 [[BUFFER_SIZE]], i32 [[BUFFER_ALIGN:8]], ptr %0, ptr @"$s14yield_once_big3BigVyxGAA9SomeClassCRbzlIetAYi_TC", ptr @malloc, ptr @free)
|
|
// CHECK-NEXT: [[BEGIN:%.*]] = call ptr @llvm.coro.begin(token [[ID]], ptr null)
|
|
// CHECK-NEXT: store ptr
|
|
|
|
// Create the return temporary. We could give this a tighter bound.
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 {{.*}}, ptr [[TEMP]])
|
|
|
|
// CHECK-NEXT: call swiftcc void @marker(i32 1000)
|
|
%marker = function_ref @marker : $@convention(thin) (Builtin.Int32) -> ()
|
|
%1000 = integer_literal $Builtin.Int32, 1000
|
|
apply %marker(%1000) : $@convention(thin) (Builtin.Int32) -> ()
|
|
|
|
// CHECK-NEXT: call swiftcc void @make_big(ptr noalias sret({{.*}}) captures(none) [[TEMP]], ptr %C)
|
|
%make = function_ref @make_big : $@convention(thin) <T: SomeClass> () -> (@owned Big<T>)
|
|
%value = apply %make<C>() : $@convention(thin) <T: SomeClass> () -> (@owned Big<T>)
|
|
|
|
// Suspend.
|
|
// CHECK-NEXT: [[IS_UNWIND:%.*]] = call i1 (...) @llvm.coro.suspend.retcon.i1(ptr [[TEMP]])
|
|
|
|
// CHECK-NEXT: br i1 [[IS_UNWIND]]
|
|
yield %value : $Big<C>, resume resume, unwind unwind
|
|
|
|
resume:
|
|
// CHECK: call swiftcc void @marker(i32 2000)
|
|
%2000 = integer_literal $Builtin.Int32, 2000
|
|
apply %marker(%2000) : $@convention(thin) (Builtin.Int32) -> ()
|
|
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 {{.*}}, ptr [[TEMP]])
|
|
// CHECK-NEXT: br label %coro.end
|
|
%ret = tuple ()
|
|
return %ret : $()
|
|
|
|
unwind:
|
|
// CHECK: call swiftcc void @marker(i32 3000)
|
|
%3000 = integer_literal $Builtin.Int32, 3000
|
|
apply %marker(%3000) : $@convention(thin) (Builtin.Int32) -> ()
|
|
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 {{.*}}, ptr [[TEMP]])
|
|
// CHECK-NEXT: br label %coro.end
|
|
unwind
|
|
|
|
// CHECK: coro.end:
|
|
// CHECK: call i1 @llvm.coro.end(ptr [[BEGIN]], i1 false, token none)
|
|
// CHECK-NEXT: unreachable
|
|
}
|
|
|
|
// CHECK-LABEL: declare{{( dllimport)?}}{{( protected)?}} swiftcc void @"$s14yield_once_big3BigVyxGAA9SomeClassCRbzlIetAYi_TC"
|
|
// CHECK-SAME: (ptr noalias dereferenceable([[BUFFER_SIZE]]), i1)
|
|
|
|
// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @test_simple_call(i1 %0)
|
|
sil [ossa] @test_simple_call : $(Builtin.Int1) -> () {
|
|
entry(%flag : $Builtin.Int1):
|
|
// Allocate the buffer.
|
|
// CHECK: [[T0:%.*]] = alloca {{\[}}[[BUFFER_SIZE]] x i8], align [[BUFFER_ALIGN]]
|
|
// CHECK-NEXT: [[BUFFER:%.*]] = getelementptr inbounds{{.*}} {{\[}}[[BUFFER_SIZE]] x i8], ptr [[T0]], i32 0, i32 0
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 [[BUFFER_SIZE]], ptr [[BUFFER]])
|
|
|
|
// CHECK-NEXT: [[TMP:%.*]] = call swiftcc %swift.metadata_response @"$s14yield_once_big12SomeSubclassCMa"([[INT]] 0)
|
|
// CHECK-NEXT: [[SUBCLASS:%.*]] = extractvalue %swift.metadata_response [[TMP]], 0
|
|
|
|
// Prepare the continuation function pointer to block analysis.
|
|
// CHECK-NEXT: [[T0:%.*]] = call ptr @llvm.coro.prepare.retcon(ptr @test_simple
|
|
// Call the function pointer.
|
|
// CHECK-NEXT: [[RAMP_RESULT:%.*]] = call swiftcc [[RAMP_RESULT_T:{ ptr, ptr }]] [[T0]](ptr noalias dereferenceable([[BUFFER_SIZE]]) [[BUFFER]], ptr [[SUBCLASS]])
|
|
// CHECK-NEXT: [[CONTINUATION:%.*]] = extractvalue [[RAMP_RESULT_T]] [[RAMP_RESULT]], 0
|
|
// CHECK-NEXT: [[PTR:%.*]] = extractvalue [[RAMP_RESULT_T]] [[RAMP_RESULT]], 1
|
|
%0 = function_ref @test_simple : $@convention(thin) @yield_once <T: SomeClass> () -> (@yields @owned Big<T>)
|
|
(%value, %token) = begin_apply %0<SomeSubclass>() : $@convention(thin) @yield_once <T: SomeClass> () -> (@yields @owned Big<T>)
|
|
|
|
// Load. This is spurious.
|
|
// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds{{.*}} [[BIGSUB:%T14yield_once_big3BigVyAA12SomeSubclassCG]], ptr [[PTR]], i32 0, i32 0
|
|
// CHECK-NEXT: [[R0:%.*]] = load ptr, ptr [[T0]], align
|
|
// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds{{.*}} [[BIGSUB]], ptr [[PTR]], i32 0, i32 1
|
|
// CHECK-NEXT: [[R1:%.*]] = load ptr, ptr [[T0]], align
|
|
// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds{{.*}} [[BIGSUB]], ptr [[PTR]], i32 0, i32 2
|
|
// CHECK-NEXT: [[R2:%.*]] = load ptr, ptr [[T0]], align
|
|
// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds{{.*}} [[BIGSUB]], ptr [[PTR]], i32 0, i32 3
|
|
// CHECK-NEXT: [[R3:%.*]] = load ptr, ptr [[T0]], align
|
|
// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds{{.*}} [[BIGSUB]], ptr [[PTR]], i32 0, i32 4
|
|
// CHECK-NEXT: [[R4:%.*]] = load ptr, ptr [[T0]], align
|
|
// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds{{.*}} [[BIGSUB]], ptr [[PTR]], i32 0, i32 5
|
|
// CHECK-NEXT: [[R5:%.*]] = load ptr, ptr [[T0]], align
|
|
// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds{{.*}} [[BIGSUB]], ptr [[PTR]], i32 0, i32 6
|
|
// CHECK-NEXT: [[R6:%.*]] = load ptr, ptr [[T0]], align
|
|
// CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds{{.*}} [[BIGSUB]], ptr [[PTR]], i32 0, i32 7
|
|
// CHECK-NEXT: [[R7:%.*]] = load ptr, ptr [[T0]], align
|
|
// CHECK: call void @swift_release(ptr [[R0]])
|
|
// CHECK-NEXT: call void @swift_release(ptr [[R1]])
|
|
// CHECK-NEXT: call void @swift_release(ptr [[R2]])
|
|
// CHECK-NEXT: call void @swift_release(ptr [[R3]])
|
|
// CHECK: call void @swift_release(ptr [[R4]])
|
|
// CHECK-NEXT: call void @swift_release(ptr [[R5]])
|
|
// CHECK-NEXT: call void @swift_release(ptr [[R6]])
|
|
// CHECK-NEXT: call void @swift_release(ptr [[R7]])
|
|
|
|
// Branch.
|
|
// CHECK-NEXT: br i1 %0,
|
|
cond_br %flag, yes, no
|
|
|
|
yes:
|
|
// CHECK-64-ptrauth: ptrtoint
|
|
// CHECK-64-ptrauth-NEXT: ptrauth.blend
|
|
// CHECK: call swiftcc void [[CONTINUATION]](ptr noalias dereferenceable([[BUFFER_SIZE]]) [[BUFFER]], i1 false)
|
|
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 [[BUFFER_SIZE]], ptr [[BUFFER]])
|
|
end_apply %token as $()
|
|
|
|
// CHECK-NEXT: br label
|
|
br cont
|
|
|
|
no:
|
|
// CHECK-64-ptrauth: ptrtoint
|
|
// CHECK-64-ptrauth-NEXT: ptrauth.blend
|
|
// CHECK: call swiftcc void [[CONTINUATION]](ptr noalias dereferenceable([[BUFFER_SIZE]]) [[BUFFER]], i1 true)
|
|
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 [[BUFFER_SIZE]], ptr [[BUFFER]])
|
|
abort_apply %token
|
|
|
|
// CHECK-NEXT: br label
|
|
br cont
|
|
|
|
cont:
|
|
destroy_value %value : $Big<SomeSubclass>
|
|
|
|
// CHECK: ret void
|
|
%ret = tuple ()
|
|
return %ret : $()
|
|
}
|
|
|
|
// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { ptr, ptr } @test_simple_guaranteed
|
|
// CHECK-32-SAME: (ptr noalias dereferenceable([[BUFFER_SIZE:16]]) %0, ptr noalias captures(none) dereferenceable(32) %1, ptr %C)
|
|
// CHECK-64-SAME: (ptr noalias dereferenceable([[BUFFER_SIZE:32]]) %0, ptr noalias captures(none) dereferenceable(64) %1, ptr %C)
|
|
sil [ossa] @test_simple_guaranteed : $@yield_once <C: SomeClass> (@in_guaranteed BigWrapper<C>) -> (@yields @guaranteed Big<C>) {
|
|
entry(%arg : $*BigWrapper<C>):
|
|
// Allocate space for the return value of make_big.
|
|
// CHECK: [[TEMP:%.*]] = alloca [[BIG:%T14yield_once_big3BigV]]
|
|
// CHECK-32-SAME: , align 4
|
|
// CHECK-64-SAME: , align 8
|
|
|
|
// Coroutine setup.
|
|
// CHECK-32-NEXT: [[ID:%.*]] = call token (i32, i32, ptr, ptr, ptr, ptr, ...) @llvm.coro.id.retcon.once(i32 [[BUFFER_SIZE]], i32 [[BUFFER_ALIGN:4]], ptr %0, ptr @"$s14yield_once_big10BigWrapperVyxGAA0D0VyxGAA9SomeClassCRbzlIetAnYn_TC", ptr @malloc, ptr @free)
|
|
// CHECK-64-NEXT: [[ID:%.*]] = call token (i32, i32, ptr, ptr, ptr, ptr, ...) @llvm.coro.id.retcon.once(i32 [[BUFFER_SIZE]], i32 [[BUFFER_ALIGN:8]], ptr %0, ptr @"$s14yield_once_big10BigWrapperVyxGAA0D0VyxGAA9SomeClassCRbzlIetAnYn_TC", ptr @malloc, ptr @free)
|
|
// CHECK-NEXT: [[BEGIN:%.*]] = call ptr @llvm.coro.begin(token [[ID]], ptr null)
|
|
// CHECK-NEXT: store ptr
|
|
|
|
// Create the return temporary. We could give this a tighter bound.
|
|
// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 {{.*}}, ptr [[TEMP]])
|
|
|
|
// CHECK-NEXT: call swiftcc void @marker(i32 1000)
|
|
%marker = function_ref @marker : $@convention(thin) (Builtin.Int32) -> ()
|
|
%1000 = integer_literal $Builtin.Int32, 1000
|
|
apply %marker(%1000) : $@convention(thin) (Builtin.Int32) -> ()
|
|
|
|
%value = load_borrow %arg : $*BigWrapper<C>
|
|
%field = struct_extract %value : $BigWrapper<C>, #BigWrapper.big
|
|
|
|
// Make sure that we properly convert these struct_extract to
|
|
// struct_element_addr while rewriting.
|
|
//
|
|
// CHECK: call swiftcc void @use_some_class(
|
|
%field2 = struct_extract %value : $BigWrapper<C>, #BigWrapper.big
|
|
%field3 = struct_extract %field2 : $Big<C>, #Big.a
|
|
%f = function_ref @use_some_class : $@convention(thin) <T : SomeClass> (@guaranteed T) -> ()
|
|
apply %f<C>(%field3) : $@convention(thin) <T : SomeClass>(@guaranteed T) -> ()
|
|
|
|
// Suspend.
|
|
// CHECK-NEXT: [[IS_UNWIND:%.*]] = call i1 (...) @llvm.coro.suspend.retcon.i1(ptr [[TEMP]])
|
|
|
|
// CHECK-NEXT: br i1 [[IS_UNWIND]]
|
|
yield %field : $Big<C>, resume resume, unwind unwind
|
|
|
|
resume:
|
|
end_borrow %value : $BigWrapper<C>
|
|
|
|
// CHECK: call swiftcc void @marker(i32 2000)
|
|
%2000 = integer_literal $Builtin.Int32, 2000
|
|
apply %marker(%2000) : $@convention(thin) (Builtin.Int32) -> ()
|
|
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 {{.*}}, ptr [[TEMP]])
|
|
// CHECK-NEXT: br label %coro.end
|
|
%ret = tuple ()
|
|
return %ret : $()
|
|
|
|
unwind:
|
|
end_borrow %value : $BigWrapper<C>
|
|
// CHECK: call swiftcc void @marker(i32 3000)
|
|
%3000 = integer_literal $Builtin.Int32, 3000
|
|
apply %marker(%3000) : $@convention(thin) (Builtin.Int32) -> ()
|
|
// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 {{.*}}, ptr [[TEMP]])
|
|
// CHECK-NEXT: br label %coro.end
|
|
unwind
|
|
|
|
// CHECK: coro.end:
|
|
// CHECK: call i1 @llvm.coro.end(ptr [[BEGIN]], i1 false, token none)
|
|
// CHECK-NEXT: unreachable
|
|
}
|
|
|
|
// CHECK-LABEL: declare{{( dllimport)?}}{{( protected)?}} swiftcc void @"$s14yield_once_big10BigWrapperVyxGAA0D0VyxGAA9SomeClassCRbzlIetAnYn_TC"
|
|
// CHECK-SAME: (ptr noalias dereferenceable([[BUFFER_SIZE]]), i1)
|