From ea779cc39670cf5f3e1cee63a7909eebdb0083cb Mon Sep 17 00:00:00 2001 From: Gor Nishanov Date: Tue, 23 May 2017 03:46:59 +0000 Subject: [PATCH] [coroutines] Replace all coro.frame builtins with an SSA value of coro.begin SemaCoroutine forms expressions referring to the coroutine frame of the enclosing coroutine using coro.frame builtin. During codegen, we emit llvm.coro.begin intrinsic that returns the address of the coroutine frame. When coro.frame is emitted, we replace it with SSA value of coro.begin. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@303598 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGCoroutine.cpp | 25 ++++++++++++++++++++++++- test/CodeGenCoroutines/coro-alloc.cpp | 9 ++++----- test/CodeGenCoroutines/coro-await.cpp | 6 +++--- test/CodeGenCoroutines/coro-builtins.c | 22 ++++++++-------------- 4 files changed, 39 insertions(+), 23 deletions(-) diff --git a/lib/CodeGen/CGCoroutine.cpp b/lib/CodeGen/CGCoroutine.cpp index f6d06d0bdd..9537d924e2 100644 --- a/lib/CodeGen/CGCoroutine.cpp +++ b/lib/CodeGen/CGCoroutine.cpp @@ -57,6 +57,11 @@ struct clang::CodeGen::CGCoroData { // builtin. llvm::CallInst *CoroId = nullptr; + // Stores the llvm.coro.begin emitted in the function so that we can replace + // all coro.frame intrinsics with direct SSA value of coro.begin that returns + // the address of the coroutine frame of the current coroutine. + llvm::CallInst *CoroBegin = nullptr; + // If coro.id came from the builtin, remember the expression to give better // diagnostic. If CoroIdExpr is nullptr, the coro.id was created by // EmitCoroutineBody. @@ -330,8 +335,9 @@ void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) { auto *Phi = Builder.CreatePHI(VoidPtrTy, 2); Phi->addIncoming(NullPtr, EntryBB); Phi->addIncoming(AllocateCall, AllocOrInvokeContBB); - Builder.CreateCall( + auto *CoroBegin = Builder.CreateCall( CGM.getIntrinsic(llvm::Intrinsic::coro_begin), {CoroId, Phi}); + CurCoro.Data->CoroBegin = CoroBegin; CurCoro.Data->CleanupJD = getJumpDestInCurrentScope(RetBB); { @@ -388,6 +394,17 @@ RValue CodeGenFunction::EmitCoroutineIntrinsic(const CallExpr *E, switch (IID) { default: break; + // The coro.frame builtin is replaced with an SSA value of the coro.begin + // intrinsic. + case llvm::Intrinsic::coro_frame: { + if (CurCoro.Data && CurCoro.Data->CoroBegin) { + return RValue::get(CurCoro.Data->CoroBegin); + } + CGM.Error(E->getLocStart(), "this builtin expect that __builtin_coro_begin " + "has been used earlier in this function"); + auto NullPtr = llvm::ConstantPointerNull::get(Builder.getInt8PtrTy()); + return RValue::get(NullPtr); + } // The following three intrinsics take a token parameter referring to a token // returned by earlier call to @llvm.coro.id. Since we cannot represent it in // builtins, we patch it up here. @@ -414,10 +431,16 @@ RValue CodeGenFunction::EmitCoroutineIntrinsic(const CallExpr *E, llvm::Value *F = CGM.getIntrinsic(IID); llvm::CallInst *Call = Builder.CreateCall(F, Args); + // Note: The following code is to enable to emit coroutine intrinsics by + // hand to experiment with coroutines in C. // If we see @llvm.coro.id remember it in the CoroData. We will update // coro.alloc, coro.begin and coro.free intrinsics to refer to it. if (IID == llvm::Intrinsic::coro_id) { createCoroData(*this, CurCoro, Call, E); } + else if (IID == llvm::Intrinsic::coro_begin) { + if (CurCoro.Data) + CurCoro.Data->CoroBegin = Call; + } return RValue::get(Call); } diff --git a/test/CodeGenCoroutines/coro-alloc.cpp b/test/CodeGenCoroutines/coro-alloc.cpp index 14e429ebc2..730fc37c21 100644 --- a/test/CodeGenCoroutines/coro-alloc.cpp +++ b/test/CodeGenCoroutines/coro-alloc.cpp @@ -66,9 +66,8 @@ extern "C" void f0(global_new_delete_tag) { // CHECK: [[InitBB]]: // CHECK: %[[PHI:.+]] = phi i8* [ null, %{{.+}} ], [ %call, %[[AllocBB]] ] - // CHECK: call i8* @llvm.coro.begin(token %[[ID]], i8* %[[PHI]]) + // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin(token %[[ID]], i8* %[[PHI]]) - // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.frame() // CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]]) // CHECK: call void @_ZdlPv(i8* %[[MEM]]) co_return; @@ -93,7 +92,7 @@ extern "C" void f1(promise_new_tag ) { // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64() // CHECK: call i8* @_ZNSt12experimental16coroutine_traitsIJv15promise_new_tagEE12promise_typenwEm(i64 %[[SIZE]]) - // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.frame() + // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin( // CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]]) // CHECK: call void @_ZdlPv(i8* %[[MEM]]) co_return; @@ -118,7 +117,7 @@ extern "C" void f2(promise_delete_tag) { // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64() // CHECK: call i8* @_Znwm(i64 %[[SIZE]]) - // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.frame() + // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin( // CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]]) // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJv18promise_delete_tagEE12promise_typedlEPv(i8* %[[MEM]]) co_return; @@ -143,7 +142,7 @@ extern "C" void f3(promise_sized_delete_tag) { // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64() // CHECK: call i8* @_Znwm(i64 %[[SIZE]]) - // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.frame() + // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin( // CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]]) // CHECK: %[[SIZE2:.+]] = call i64 @llvm.coro.size.i64() // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJv24promise_sized_delete_tagEE12promise_typedlEPvm(i8* %[[MEM]], i64 %[[SIZE2]]) diff --git a/test/CodeGenCoroutines/coro-await.cpp b/test/CodeGenCoroutines/coro-await.cpp index b0fd0129de..8a9b9919e3 100644 --- a/test/CodeGenCoroutines/coro-await.cpp +++ b/test/CodeGenCoroutines/coro-await.cpp @@ -40,6 +40,7 @@ struct std::experimental::coroutine_traits { // CHECK-LABEL: f0( extern "C" void f0() { + // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin( co_await suspend_always{}; // See if we need to suspend: @@ -54,7 +55,6 @@ extern "C" void f0() { // --------------------------- // Build the coroutine handle and pass it to await_suspend // --------------------------- - // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.frame() // CHECK: call i8* @_ZNSt12experimental16coroutine_handleINS_16coroutine_traitsIJvEE12promise_typeEE12from_addressEPv(i8* %[[FRAME]]) // ... many lines of code to coerce coroutine_handle into an i8* scalar // CHECK: %[[CH:.+]] = load i8*, i8** %{{.+}} @@ -100,8 +100,9 @@ struct std::experimental::coroutine_traits { // CHECK-LABEL: f1( extern "C" void f1(int) { - co_yield 42; // CHECK: %[[PROMISE:.+]] = alloca %"struct.std::experimental::coroutine_traits::promise_type" + // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin( + co_yield 42; // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJviEE12promise_type11yield_valueEi(%struct.suspend_maybe* sret %[[AWAITER:.+]], %"struct.std::experimental::coroutine_traits::promise_type"* %[[PROMISE]], i32 42) // See if we need to suspend: @@ -116,7 +117,6 @@ extern "C" void f1(int) { // --------------------------- // Build the coroutine handle and pass it to await_suspend // --------------------------- - // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.frame() // CHECK: call i8* @_ZNSt12experimental16coroutine_handleINS_16coroutine_traitsIJviEE12promise_typeEE12from_addressEPv(i8* %[[FRAME]]) // ... many lines of code to coerce coroutine_handle into an i8* scalar // CHECK: %[[CH:.+]] = load i8*, i8** %{{.+}} diff --git a/test/CodeGenCoroutines/coro-builtins.c b/test/CodeGenCoroutines/coro-builtins.c index d18d5e730f..9ec2147642 100644 --- a/test/CodeGenCoroutines/coro-builtins.c +++ b/test/CodeGenCoroutines/coro-builtins.c @@ -2,7 +2,7 @@ void *myAlloc(long long); -// CHECK-LABEL: f( +// CHECK-LABEL: f( void f(int n) { // CHECK: %n.addr = alloca i32 // CHECK: %n_copy = alloca i32 @@ -19,31 +19,25 @@ void f(int n) { // CHECK-NEXT: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64() // CHECK-NEXT: %[[MEM:.+]] = call i8* @myAlloc(i64 %[[SIZE]]) - // CHECK-NEXT: %[[BEG:.+]] = call i8* @llvm.coro.begin(token %[[COROID]], i8* %[[MEM]]) + // CHECK-NEXT: %[[FRAME:.+]] = call i8* @llvm.coro.begin(token %[[COROID]], i8* %[[MEM]]) __builtin_coro_begin(myAlloc(__builtin_coro_size())); - // CHECK-NEXT: %[[FRAME1:.+]] = call i8* @llvm.coro.frame() - // CHECK-NEXT: call void @llvm.coro.resume(i8* %[[FRAME1]]) + // CHECK-NEXT: call void @llvm.coro.resume(i8* %[[FRAME]]) __builtin_coro_resume(__builtin_coro_frame()); - // CHECK-NEXT: %[[FRAME2:.+]] = call i8* @llvm.coro.frame() - // CHECK-NEXT: call void @llvm.coro.destroy(i8* %[[FRAME2]]) + // CHECK-NEXT: call void @llvm.coro.destroy(i8* %[[FRAME]]) __builtin_coro_destroy(__builtin_coro_frame()); - // CHECK-NEXT: %[[FRAME3:.+]] = call i8* @llvm.coro.frame() - // CHECK-NEXT: call i1 @llvm.coro.done(i8* %[[FRAME3]]) + // CHECK-NEXT: call i1 @llvm.coro.done(i8* %[[FRAME]]) __builtin_coro_done(__builtin_coro_frame()); - // CHECK-NEXT: %[[FRAME4:.+]] = call i8* @llvm.coro.frame() - // CHECK-NEXT: call i8* @llvm.coro.promise(i8* %[[FRAME4]], i32 48, i1 false) + // CHECK-NEXT: call i8* @llvm.coro.promise(i8* %[[FRAME]], i32 48, i1 false) __builtin_coro_promise(__builtin_coro_frame(), 48, 0); - // CHECK-NEXT: %[[FRAME5:.+]] = call i8* @llvm.coro.frame() - // CHECK-NEXT: call i8* @llvm.coro.free(token %[[COROID]], i8* %[[FRAME5]]) + // CHECK-NEXT: call i8* @llvm.coro.free(token %[[COROID]], i8* %[[FRAME]]) __builtin_coro_free(__builtin_coro_frame()); - // CHECK-NEXT: %[[FRAME6:.+]] = call i8* @llvm.coro.frame() - // CHECK-NEXT: call i1 @llvm.coro.end(i8* %[[FRAME6]], i1 false) + // CHECK-NEXT: call i1 @llvm.coro.end(i8* %[[FRAME]], i1 false) __builtin_coro_end(__builtin_coro_frame(), 0); // CHECK-NEXT: call i8 @llvm.coro.suspend(token none, i1 true) -- 2.40.0