// 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 { 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 { var big: Big } sil @make_big : $ () -> (@owned Big) sil @use_some_class : $ (@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 () -> (@yields @owned Big) { 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) () -> (@owned Big) %value = apply %make() : $@convention(thin) () -> (@owned Big) // Suspend. // CHECK-NEXT: [[IS_UNWIND:%.*]] = call i1 (...) @llvm.coro.suspend.retcon.i1(ptr [[TEMP]]) // CHECK-NEXT: br i1 [[IS_UNWIND]] yield %value : $Big, 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 () -> (@yields @owned Big) (%value, %token) = begin_apply %0() : $@convention(thin) @yield_once () -> (@yields @owned Big) // 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 // 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 (@in_guaranteed BigWrapper) -> (@yields @guaranteed Big) { entry(%arg : $*BigWrapper): // 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 %field = struct_extract %value : $BigWrapper, #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, #BigWrapper.big %field3 = struct_extract %field2 : $Big, #Big.a %f = function_ref @use_some_class : $@convention(thin) (@guaranteed T) -> () apply %f(%field3) : $@convention(thin) (@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, resume resume, unwind unwind resume: end_borrow %value : $BigWrapper // 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 // 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)