396 lines · cpp
1// RUN: %clang_cc1 -no-enable-noundef-analysis -triple x86_64-unknown-linux-gnu -std=c++20 \2// RUN: -emit-llvm %s -o - -disable-llvm-passes -Wno-coroutine -Wno-unused | FileCheck %s3 4namespace std {5template <typename... T>6struct coroutine_traits;7 8template <typename Promise = void> struct coroutine_handle;9 10template <>11struct coroutine_handle<void> {12 void *ptr;13 static coroutine_handle from_address(void *);14 void *address();15};16 17template <typename Promise>18struct coroutine_handle : coroutine_handle<> {19 static coroutine_handle from_address(void *) noexcept;20};21 22} // namespace std23 24struct init_susp {25 bool await_ready();26 void await_suspend(std::coroutine_handle<>);27 void await_resume();28};29struct final_susp {30 bool await_ready() noexcept;31 void await_suspend(std::coroutine_handle<>) noexcept;32 void await_resume() noexcept;33};34 35struct suspend_always {36 int stuff;37 bool await_ready();38 void await_suspend(std::coroutine_handle<>);39 void await_resume();40};41 42template <>43struct std::coroutine_traits<void> {44 struct promise_type {45 void get_return_object();46 init_susp initial_suspend();47 final_susp final_suspend() noexcept;48 void return_void();49 };50};51 52// CHECK-LABEL: f0(53extern "C" void f0() {54 // CHECK: %[[FRAME:.+]] = call ptr @llvm.coro.begin(55 56 // See if initial_suspend was issued:57 // ----------------------------------58 // CHECK: call void @_ZNSt16coroutine_traitsIJvEE12promise_type15initial_suspendEv(59 // CHECK-NEXT: call zeroext i1 @_ZN9init_susp11await_readyEv(ptr60 // CHECK: %[[INITSP_ID:.+]] = call token @llvm.coro.save(61 // CHECK: call i8 @llvm.coro.suspend(token %[[INITSP_ID]], i1 false)62 63 co_await suspend_always{};64 // See if we need to suspend:65 // --------------------------66 // CHECK: %[[READY:.+]] = call zeroext i1 @_ZN14suspend_always11await_readyEv(ptr {{[^,]*}} %[[AWAITABLE:.+]])67 // CHECK: br i1 %[[READY]], label %[[READY_BB:.+]], label %[[SUSPEND_BB:.+]]68 69 // If we are suspending:70 // ---------------------71 // CHECK: [[SUSPEND_BB]]:72 // CHECK: %[[SUSPEND_ID:.+]] = call token @llvm.coro.save(73 // ---------------------------74 // Call coro.await.suspend75 // ---------------------------76 // CHECK-NEXT: call void @llvm.coro.await.suspend.void(ptr %[[AWAITABLE]], ptr %[[FRAME]], ptr @f0.__await_suspend_wrapper__await)77 // -------------------------78 // Generate a suspend point:79 // -------------------------80 // CHECK-NEXT: %[[OUTCOME:.+]] = call i8 @llvm.coro.suspend(token %[[SUSPEND_ID]], i1 false)81 // CHECK: switch i8 %[[OUTCOME]], label %[[RET_BB:.+]] [82 // CHECK: i8 0, label %[[READY_BB]]83 // CHECK: i8 1, label %[[CLEANUP_BB:.+]]84 // CHECK: ]85 86 // Cleanup code goes here:87 // -----------------------88 // CHECK: [[CLEANUP_BB]]:89 90 // When coroutine is resumed, call await_resume91 // --------------------------92 // CHECK: [[READY_BB]]:93 // CHECK: call void @_ZN14suspend_always12await_resumeEv(ptr {{[^,]*}} %[[AWAITABLE]])94 95 // See if final_suspend was issued:96 // ----------------------------------97 // CHECK: call void @_ZNSt16coroutine_traitsIJvEE12promise_type13final_suspendEv(98 // CHECK-NEXT: call zeroext i1 @_ZN10final_susp11await_readyEv(ptr99 // CHECK: %[[FINALSP_ID:.+]] = call token @llvm.coro.save(100 // CHECK: call i8 @llvm.coro.suspend(token %[[FINALSP_ID]], i1 true)101 102 // Await suspend wrapper103 // CHECK: define internal {{.*}} @f0.__await_suspend_wrapper__await(ptr {{[^,]*}} %[[AWAITABLE_ARG:.+]], ptr {{[^,]*}} %[[FRAME_ARG:.+]])104 // CHECK: store ptr %[[AWAITABLE_ARG]], ptr %[[AWAITABLE_TMP:.+]],105 // CHECK: store ptr %[[FRAME_ARG]], ptr %[[FRAME_TMP:.+]],106 // CHECK: %[[AWAITABLE:.+]] = load ptr, ptr %[[AWAITABLE_TMP]]107 // CHECK: %[[FRAME:.+]] = load ptr, ptr %[[FRAME_TMP]]108 // CHECK: call ptr @_ZNSt16coroutine_handleINSt16coroutine_traitsIJvEE12promise_typeEE12from_addressEPv(ptr %[[FRAME]])109 // ... many lines of code to coerce coroutine_handle into an ptr scalar110 // CHECK: %[[CH:.+]] = load ptr, ptr %{{.+}}111 // CHECK: call void @_ZN14suspend_always13await_suspendESt16coroutine_handleIvE(ptr {{[^,]*}} %[[AWAITABLE]], ptr %[[CH]])112}113 114struct suspend_maybe {115 float stuff;116 ~suspend_maybe();117 bool await_ready();118 bool await_suspend(std::coroutine_handle<>);119 void await_resume();120};121 122template <>123struct std::coroutine_traits<void, int> {124 struct promise_type {125 void get_return_object();126 init_susp initial_suspend();127 final_susp final_suspend() noexcept;128 void return_void();129 suspend_maybe yield_value(int);130 };131};132 133// CHECK-LABEL: f1(134extern "C" void f1(int) {135 // CHECK: %[[PROMISE:.+]] = alloca %"struct.std::coroutine_traits<void, int>::promise_type"136 // CHECK: %[[FRAME:.+]] = call ptr @llvm.coro.begin(137 co_yield 42;138 // CHECK: call void @_ZNSt16coroutine_traitsIJviEE12promise_type11yield_valueEi(ptr dead_on_unwind writable sret(%struct.suspend_maybe) align 4 %[[AWAITER:.+]], ptr {{[^,]*}} %[[PROMISE]], i32 42)139 140 // See if we need to suspend:141 // --------------------------142 // CHECK: %[[READY:.+]] = call zeroext i1 @_ZN13suspend_maybe11await_readyEv(ptr {{[^,]*}} %[[AWAITABLE:.+]])143 // CHECK: br i1 %[[READY]], label %[[READY_BB:.+]], label %[[SUSPEND_BB:.+]]144 145 // If we are suspending:146 // ---------------------147 // CHECK: [[SUSPEND_BB]]:148 // CHECK: %[[SUSPEND_ID:.+]] = call token @llvm.coro.save(149 // ---------------------------150 // Call coro.await.suspend151 // ---------------------------152 // CHECK-NEXT: %[[YES:.+]] = call i1 @llvm.coro.await.suspend.bool(ptr %[[AWAITABLE]], ptr %[[FRAME]], ptr @f1.__await_suspend_wrapper__yield)153 // -------------------------------------------154 // See if await_suspend decided not to suspend155 // -------------------------------------------156 // CHECK: br i1 %[[YES]], label %[[SUSPEND_PLEASE:.+]], label %[[READY_BB]]157 158 // CHECK: [[SUSPEND_PLEASE]]:159 // CHECK: call i8 @llvm.coro.suspend(token %[[SUSPEND_ID]], i1 false)160 161 // CHECK: [[READY_BB]]:162 // CHECK: call void @_ZN13suspend_maybe12await_resumeEv(ptr {{[^,]*}} %[[AWAITABLE]])163 164 // Await suspend wrapper165 // CHECK: define internal {{.*}} i1 @f1.__await_suspend_wrapper__yield(ptr {{[^,]*}} %[[AWAITABLE_ARG:.+]], ptr {{[^,]*}} %[[FRAME_ARG:.+]])166 // CHECK: store ptr %[[AWAITABLE_ARG]], ptr %[[AWAITABLE_TMP:.+]],167 // CHECK: store ptr %[[FRAME_ARG]], ptr %[[FRAME_TMP:.+]],168 // CHECK: %[[AWAITABLE:.+]] = load ptr, ptr %[[AWAITABLE_TMP]]169 // CHECK: %[[FRAME:.+]] = load ptr, ptr %[[FRAME_TMP]]170 // CHECK: call ptr @_ZNSt16coroutine_handleINSt16coroutine_traitsIJviEE12promise_typeEE12from_addressEPv(ptr %[[FRAME]])171 // ... many lines of code to coerce coroutine_handle into an ptr scalar172 // CHECK: %[[CH:.+]] = load ptr, ptr %{{.+}}173 // CHECK: %[[YES:.+]] = call zeroext i1 @_ZN13suspend_maybe13await_suspendESt16coroutine_handleIvE(ptr {{[^,]*}} %[[AWAITABLE]], ptr %[[CH]]) 174 // CHECK-NEXT: ret i1 %[[YES]]175}176 177struct ComplexAwaiter {178 template <typename F> void await_suspend(F);179 bool await_ready();180 _Complex float await_resume();181};182extern "C" void UseComplex(_Complex float);183 184// CHECK-LABEL: @TestComplex(185extern "C" void TestComplex() {186 UseComplex(co_await ComplexAwaiter{});187 // CHECK: call <2 x float> @_ZN14ComplexAwaiter12await_resumeEv(ptr188 // CHECK: call void @UseComplex(<2 x float> %{{.+}})189 190 co_await ComplexAwaiter{};191 // CHECK: call <2 x float> @_ZN14ComplexAwaiter12await_resumeEv(ptr192 193 _Complex float Val = co_await ComplexAwaiter{};194 // CHECK: call <2 x float> @_ZN14ComplexAwaiter12await_resumeEv(ptr195}196 197struct Aggr { int X, Y, Z; ~Aggr(); };198struct AggrAwaiter {199 template <typename F> void await_suspend(F);200 bool await_ready();201 Aggr await_resume();202};203 204extern "C" void Whatever();205extern "C" void UseAggr(Aggr&&);206 207// FIXME: Once the cleanup code is in, add testing that destructors for Aggr208// are invoked properly on the cleanup branches.209 210// CHECK-LABEL: @TestAggr(211extern "C" void TestAggr() {212 UseAggr(co_await AggrAwaiter{});213 Whatever();214 // CHECK: call void @_ZN11AggrAwaiter12await_resumeEv(ptr dead_on_unwind writable sret(%struct.Aggr) align 4 %[[AwaitResume:.+]],215 // CHECK: call void @UseAggr(ptr nonnull align 4 dereferenceable(12) %[[AwaitResume]])216 // CHECK: call void @_ZN4AggrD1Ev(ptr {{[^,]*}} %[[AwaitResume]])217 // CHECK: call void @Whatever()218 219 co_await AggrAwaiter{};220 Whatever();221 // CHECK: call void @_ZN11AggrAwaiter12await_resumeEv(ptr dead_on_unwind writable sret(%struct.Aggr) align 4 %[[AwaitResume2:.+]],222 // CHECK: call void @_ZN4AggrD1Ev(ptr {{[^,]*}} %[[AwaitResume2]])223 // CHECK: call void @Whatever()224 225 Aggr Val = co_await AggrAwaiter{};226 Whatever();227 // CHECK: call void @_ZN11AggrAwaiter12await_resumeEv(ptr dead_on_unwind writable sret(%struct.Aggr) align 4 %[[AwaitResume3:.+]],228 // CHECK: call void @Whatever()229 // CHECK: call void @_ZN4AggrD1Ev(ptr {{[^,]*}} %[[AwaitResume3]])230}231 232struct ScalarAwaiter {233 template <typename F> void await_suspend(F);234 bool await_ready();235 int await_resume();236};237 238extern "C" void UseScalar(int);239 240// CHECK-LABEL: @TestScalar(241extern "C" void TestScalar() {242 UseScalar(co_await ScalarAwaiter{});243 // CHECK: %[[Result:.+]] = call i32 @_ZN13ScalarAwaiter12await_resumeEv(ptr244 // CHECK: call void @UseScalar(i32 %[[Result]])245 246 int Val = co_await ScalarAwaiter{};247 // CHECK: %[[Result2:.+]] = call i32 @_ZN13ScalarAwaiter12await_resumeEv(ptr248 // CHECK: store i32 %[[Result2]], ptr %[[TMP_EXPRCLEANUP:.+]],249 // CHECK: %[[TMP:.+]] = load i32, ptr %[[TMP_EXPRCLEANUP]],250 // CHECK: store i32 %[[TMP]], ptr %Val,251 252 co_await ScalarAwaiter{};253 // CHECK: call i32 @_ZN13ScalarAwaiter12await_resumeEv(ptr254}255 256// Test operator co_await codegen.257enum class MyInt: int {};258ScalarAwaiter operator co_await(MyInt);259 260struct MyAgg {261 AggrAwaiter operator co_await();262};263 264// CHECK-LABEL: @TestOpAwait(265extern "C" void TestOpAwait() {266 co_await MyInt(42);267 // CHECK: call void @_Zaw5MyInt(i32 42)268 // CHECK: call i32 @_ZN13ScalarAwaiter12await_resumeEv(ptr {{[^,]*}} %269 270 co_await MyAgg{};271 // CHECK: call void @_ZN5MyAggawEv(ptr {{[^,]*}} %272 // CHECK: call void @_ZN11AggrAwaiter12await_resumeEv(ptr dead_on_unwind writable sret(%struct.Aggr) align 4 %273}274 275// CHECK-LABEL: EndlessLoop(276extern "C" void EndlessLoop() {277 // CHECK: %[[FRAME:.+]] = call ptr @llvm.coro.begin(278 279 // See if initial_suspend was issued:280 // ----------------------------------281 // CHECK: call void @_ZNSt16coroutine_traitsIJvEE12promise_type15initial_suspendEv(282 // CHECK-NEXT: call zeroext i1 @_ZN9init_susp11await_readyEv(ptr283 284 for (;;)285 co_await suspend_always{};286 287 // Verify that final_suspend was NOT issued:288 // ----------------------------------289 // CHECK-NOT: call void @_ZNSt16coroutine_traitsIJvEE12promise_type13final_suspendEv(290 // CHECK-NOT: call zeroext i1 @_ZN10final_susp11await_readyEv(ptr291}292 293// Verifies that we don't crash when awaiting on an lvalue.294// CHECK-LABEL: @_Z11AwaitLValuev(295void AwaitLValue() {296 suspend_always lval;297 co_await lval;298}299 300struct RefTag { };301 302struct AwaitResumeReturnsLValue {303 bool await_ready();304 void await_suspend(std::coroutine_handle<>);305 RefTag& await_resume();306};307 308template <>309struct std::coroutine_traits<void, double> {310 struct promise_type {311 void get_return_object();312 init_susp initial_suspend();313 final_susp final_suspend() noexcept;314 void return_void();315 AwaitResumeReturnsLValue yield_value(int);316 };317};318 319// Verifies that we don't crash when returning an lvalue from an await_resume()320// expression.321// CHECK-LABEL: define{{.*}} void @_Z18AwaitReturnsLValued(double %0)322void AwaitReturnsLValue(double) {323 AwaitResumeReturnsLValue a;324 // CHECK: %[[AVAR:.+]] = alloca %struct.AwaitResumeReturnsLValue,325 // CHECK: %[[XVAR:.+]] = alloca ptr,326 327 // CHECK: %[[YVAR:.+]] = alloca ptr,328 // CHECK-NEXT: %[[TMP1:.+]] = alloca %struct.AwaitResumeReturnsLValue,329 330 // CHECK: %[[TMP_EXPRCLEANUP1:.+]] = alloca ptr,331 // CHECK: %[[ZVAR:.+]] = alloca ptr,332 // CHECK-NEXT: %[[TMP2:.+]] = alloca %struct.AwaitResumeReturnsLValue,333 // CHECK: %[[TMP_EXPRCLEANUP2:.+]] = alloca ptr,334 335 // CHECK: %[[RES1:.+]] = call nonnull align 1 dereferenceable({{.*}}) ptr @_ZN24AwaitResumeReturnsLValue12await_resumeEv(ptr {{[^,]*}} %[[AVAR]])336 // CHECK-NEXT: store ptr %[[RES1]], ptr %[[XVAR]],337 RefTag& x = co_await a;338 339 // CHECK: %[[RES2:.+]] = call nonnull align 1 dereferenceable({{.*}}) ptr @_ZN24AwaitResumeReturnsLValue12await_resumeEv(ptr {{[^,]*}} %[[TMP1]])340 // CHECK-NEXT: store ptr %[[RES2]], ptr %[[TMP_EXPRCLEANUP1]],341 // CHECK: %[[LOAD_TMP1:.+]] = load ptr, ptr %[[TMP_EXPRCLEANUP1]],342 // CHECK: store ptr %[[LOAD_TMP1]], ptr %[[YVAR]],343 344 RefTag& y = co_await AwaitResumeReturnsLValue{};345 // CHECK: %[[RES3:.+]] = call nonnull align 1 dereferenceable({{.*}}) ptr @_ZN24AwaitResumeReturnsLValue12await_resumeEv(ptr {{[^,]*}} %[[TMP2]])346 // CHECK-NEXT: store ptr %[[RES3]], ptr %[[TMP_EXPRCLEANUP2]],347 // CHECK: %[[LOAD_TMP2:.+]] = load ptr, ptr %[[TMP_EXPRCLEANUP2]],348 // CHECK: store ptr %[[LOAD_TMP2]], ptr %[[ZVAR]],349 RefTag& z = co_yield 42;350}351 352struct TailCallAwait {353 bool await_ready();354 std::coroutine_handle<> await_suspend(std::coroutine_handle<>);355 void await_resume();356};357 358// CHECK-LABEL: @TestTailcall(359extern "C" void TestTailcall() {360 // CHECK: %[[PROMISE:.+]] = alloca %"struct.std::coroutine_traits<void>::promise_type"361 // CHECK: %[[FRAME:.+]] = call ptr @llvm.coro.begin(362 co_await TailCallAwait{};363 // CHECK: %[[READY:.+]] = call zeroext i1 @_ZN13TailCallAwait11await_readyEv(ptr {{[^,]*}} %[[AWAITABLE:.+]])364 // CHECK: br i1 %[[READY]], label %[[READY_BB:.+]], label %[[SUSPEND_BB:.+]]365 366 // If we are suspending:367 // ---------------------368 // CHECK: [[SUSPEND_BB]]:369 // CHECK: %[[SUSPEND_ID:.+]] = call token @llvm.coro.save(370 // ---------------------------371 // Call coro.await.suspend372 // ---------------------------373 // Note: The call must not be nounwind since the resumed function could throw.374 // CHECK-NEXT: call void @llvm.coro.await.suspend.handle(ptr %[[AWAITABLE]], ptr %[[FRAME]], ptr @TestTailcall.__await_suspend_wrapper__await){{$}}375 // CHECK-NEXT: %[[OUTCOME:.+]] = call i8 @llvm.coro.suspend(token %[[SUSPEND_ID]], i1 false)376 // CHECK-NEXT: switch i8 %[[OUTCOME]], label %[[RET_BB:.+]] [377 // CHECK-NEXT: i8 0, label %[[READY_BB]]378 // CHECK-NEXT: i8 1, label %{{.+}}379 // CHECK-NEXT: ]380 381 // Await suspend wrapper382 // CHECK: define internal {{.*}} ptr @TestTailcall.__await_suspend_wrapper__await(ptr {{[^,]*}} %[[AWAITABLE_ARG:.+]], ptr {{[^,]*}} %[[FRAME_ARG:.+]])383 // CHECK: store ptr %[[AWAITABLE_ARG]], ptr %[[AWAITABLE_TMP:.+]],384 // CHECK: store ptr %[[FRAME_ARG]], ptr %[[FRAME_TMP:.+]],385 // CHECK: %[[AWAITABLE:.+]] = load ptr, ptr %[[AWAITABLE_TMP]]386 // CHECK: %[[FRAME:.+]] = load ptr, ptr %[[FRAME_TMP]]387 // CHECK: call ptr @_ZNSt16coroutine_handleINSt16coroutine_traitsIJvEE12promise_typeEE12from_addressEPv(ptr %[[FRAME]])388 // ... many lines of code to coerce coroutine_handle into an ptr scalar389 // CHECK: %[[CH:.+]] = load ptr, ptr %{{.+}}390 // CHECK-NEXT: %[[RESULT:.+]] = call ptr @_ZN13TailCallAwait13await_suspendESt16coroutine_handleIvE(ptr {{[^,]*}} %[[AWAITABLE]], ptr %[[CH]]) 391 // CHECK-NEXT: %[[COERCE:.+]] = getelementptr inbounds nuw %"struct.std::coroutine_handle", ptr %[[TMP:.+]], i32 0, i32 0392 // CHECK-NEXT: store ptr %[[RESULT]], ptr %[[COERCE]]393 // CHECK-NEXT: %[[ADDR:.+]] = call ptr @_ZNSt16coroutine_handleIvE7addressEv(ptr {{[^,]*}} %[[TMP]])394 // CHECK-NEXT: ret ptr %[[ADDR]]395}396