// 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 // i386 uses a scalar result count of 3 instead of 4, so this test would need // to be substantially different to test the functionality there. It's easier // to just rule out i386 for now. If we end up with many similar exceptions, // we should turn this into a REQUIRES instead. // UNSUPPORTED: CPU=i386 // 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 small enough that we'll want to pass it directly, // but large enough that yielding it will exceed what we can return directly. struct Biggish { var a: T var b: T var c: T var d: T } sil @make_biggish : $ () -> (@owned Biggish) // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc { ptr, ptr, 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 Biggish) { entry: // Allocate space for the indirect spillover. // CHECK: [[SPILLS:%.*]] = alloca [[SPILLS_T:{ ptr, ptr }]] // 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 @"$s18yield_once_biggish7BiggishVyxGAA9SomeClassCRbzlIetAYx_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 @"$s18yield_once_biggish7BiggishVyxGAA9SomeClassCRbzlIetAYx_TC", ptr @malloc, ptr @free) // CHECK-NEXT: [[BEGIN:%.*]] = call ptr @llvm.coro.begin(token [[ID]], ptr null) // CHECK-NEXT: store ptr // 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: [[T0:%.*]] = call swiftcc [[BIGGISH:{ ptr, ptr, ptr, ptr }]] @make_biggish(ptr %C) // CHECK-NEXT: [[R0:%.*]] = extractvalue [[BIGGISH]] [[T0]], 0 // CHECK-NEXT: [[R1:%.*]] = extractvalue [[BIGGISH]] [[T0]], 1 // CHECK-NEXT: [[R2:%.*]] = extractvalue [[BIGGISH]] [[T0]], 2 // CHECK-NEXT: [[R3:%.*]] = extractvalue [[BIGGISH]] [[T0]], 3 %make = function_ref @make_biggish : $@convention(thin) () -> (@owned Biggish) %value = apply %make() : $@convention(thin) () -> (@owned Biggish) // Write the spilled objects to the buffer. // CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 {{.*}}, ptr [[SPILLS]]) // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds{{.*}} [[SPILLS_T]], ptr [[SPILLS]], i32 0, i32 0 // CHECK-NEXT: store ptr [[R2]], ptr [[T0]] // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds{{.*}} [[SPILLS_T]], ptr [[SPILLS]], i32 0, i32 1 // CHECK-NEXT: store ptr [[R3]], ptr [[T0]] // Suspend. // CHECK-NEXT: [[IS_UNWIND:%.*]] = call i1 (...) @llvm.coro.suspend.retcon.i1(ptr [[R0]], ptr [[R1]], ptr [[SPILLS]]) // Clean up the spills buffer. // CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 {{.*}}, ptr [[SPILLS]]) // CHECK-NEXT: br i1 [[IS_UNWIND]] yield %value : $Biggish, 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: 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: 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 @"$s18yield_once_biggish7BiggishVyxGAA9SomeClassCRbzlIetAYx_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 @"$s18yield_once_biggish12SomeSubclassCMa"([[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, ptr, ptr }]] [[T0]](ptr noalias dereferenceable([[BUFFER_SIZE]]) [[BUFFER]], ptr [[SUBCLASS]]) // CHECK-NEXT: [[CONTINUATION:%.*]] = extractvalue [[RAMP_RESULT_T]] [[RAMP_RESULT]], 0 // CHECK-NEXT: [[R0_ORIG:%.*]] = extractvalue [[RAMP_RESULT_T]] [[RAMP_RESULT]], 1 // CHECK-NEXT: [[R1_ORIG:%.*]] = extractvalue [[RAMP_RESULT_T]] [[RAMP_RESULT]], 2 // CHECK-NEXT: [[SPILLS:%.*]] = extractvalue [[RAMP_RESULT_T]] [[RAMP_RESULT]], 3 // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds{{.*}} [[SPILLS_T]], ptr [[SPILLS]], i32 0, i32 0 // CHECK-NEXT: [[R2_ORIG:%.*]] = load ptr, ptr [[T0]], align // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds{{.*}} [[SPILLS_T]], ptr [[SPILLS]], i32 0, i32 1 // CHECK-NEXT: [[R3_ORIG:%.*]] = load ptr, ptr [[T0]], align %0 = function_ref @test_simple : $@convention(thin) @yield_once () -> (@yields @owned Biggish) (%value, %token) = begin_apply %0() : $@convention(thin) @yield_once () -> (@yields @owned Biggish) // CHECK: call void @swift_release(ptr [[R0_ORIG]]) // CHECK-NEXT: call void @swift_release(ptr [[R1_ORIG]]) // CHECK-NEXT: call void @swift_release(ptr [[R2_ORIG]]) // CHECK-NEXT: call void @swift_release(ptr [[R3_ORIG]]) // 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 : $Biggish // CHECK: ret void %ret = tuple () return %ret : $() }