]> granicus.if.org Git - clang/commitdiff
[coroutines] Replace all coro.frame builtins with an SSA value of coro.begin
authorGor Nishanov <GorNishanov@gmail.com>
Tue, 23 May 2017 03:46:59 +0000 (03:46 +0000)
committerGor Nishanov <GorNishanov@gmail.com>
Tue, 23 May 2017 03:46:59 +0000 (03:46 +0000)
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
test/CodeGenCoroutines/coro-alloc.cpp
test/CodeGenCoroutines/coro-await.cpp
test/CodeGenCoroutines/coro-builtins.c

index f6d06d0bdd02b195690348ea6ce2c9fd4365fcd3..9537d924e2e6b6e5b2f491b244ae72620e92f645 100644 (file)
@@ -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);
 }
index 14e429ebc2289652468fd03e4b857c35adfc30c6..730fc37c2139be6cde776bc3e8198e0a85b89e3e 100644 (file)
@@ -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]])
index b0fd0129de34a6e5f87967d72b2746056cd65340..8a9b9919e367dce568de062fd412afa1914bb220 100644 (file)
@@ -40,6 +40,7 @@ struct std::experimental::coroutine_traits<void> {
 
 // 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<void,int> {
 
 // CHECK-LABEL: f1(
 extern "C" void f1(int) {
-  co_yield 42;
   // CHECK: %[[PROMISE:.+]] = alloca %"struct.std::experimental::coroutine_traits<void, int>::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<void, int>::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** %{{.+}}
index d18d5e730f91132a2cafc19c60b9be23a18849fe..9ec2147642588e8fa1d2f1c6321cbbc1639a0f73 100644 (file)
@@ -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)