]> granicus.if.org Git - clang/commitdiff
[coroutines] Add co_return statement emission
authorGor Nishanov <GorNishanov@gmail.com>
Mon, 6 Mar 2017 21:12:54 +0000 (21:12 +0000)
committerGor Nishanov <GorNishanov@gmail.com>
Mon, 6 Mar 2017 21:12:54 +0000 (21:12 +0000)
Summary:
Added co_return statement emission.

Tweaked coro-alloc.cpp test to use co_return to trigger coroutine processing instead of co_await, since this change starts emitting the body of the coroutine and await expression handling has not been upstreamed yet.

Reviewers: rsmith, majnemer, EricWF, aaron.ballman

Reviewed By: rsmith

Subscribers: majnemer, llvm-commits, mehdi_amini

Differential Revision: https://reviews.llvm.org/D29979

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@297076 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/CGCoroutine.cpp
lib/CodeGen/CGStmt.cpp
lib/CodeGen/CodeGenFunction.h
test/CodeGenCoroutines/coro-alloc.cpp
test/CodeGenCoroutines/coro-return.cpp [new file with mode: 0644]

index 2fdb1279ece97b9348734fe12a21a2a5d3ffe983..77497f32750ebdd012f50cf4984f8e7460d29f39 100644 (file)
@@ -21,6 +21,13 @@ namespace clang {
 namespace CodeGen {
 
 struct CGCoroData {
+
+  // Stores the jump destination just before the final suspend. Coreturn
+  // statements jumps to this point after calling return_xxx promise member.
+  CodeGenFunction::JumpDest FinalJD;
+
+  unsigned CoreturnCount = 0;
+
   // Stores the llvm.coro.id emitted in the function so that we can supply it
   // as the first argument to coro.begin, coro.alloc and coro.free intrinsics.
   // Note: llvm.coro.id returns a token that cannot be directly expressed in a
@@ -59,19 +66,46 @@ static void createCoroData(CodeGenFunction &CGF,
   CurCoro.Data->CoroIdExpr = CoroIdExpr;
 }
 
+void CodeGenFunction::EmitCoreturnStmt(CoreturnStmt const &S) {
+  ++CurCoro.Data->CoreturnCount;
+  EmitStmt(S.getPromiseCall());
+  EmitBranchThroughCleanup(CurCoro.Data->FinalJD);
+}
+
 void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) {
   auto *NullPtr = llvm::ConstantPointerNull::get(Builder.getInt8PtrTy());
   auto &TI = CGM.getContext().getTargetInfo();
   unsigned NewAlign = TI.getNewAlign() / TI.getCharWidth();
 
+  auto *FinalBB = createBasicBlock("coro.final");
+
   auto *CoroId = Builder.CreateCall(
       CGM.getIntrinsic(llvm::Intrinsic::coro_id),
       {Builder.getInt32(NewAlign), NullPtr, NullPtr, NullPtr});
   createCoroData(*this, CurCoro, CoroId);
 
   EmitScalarExpr(S.getAllocate());
-  // FIXME: Emit the rest of the coroutine.
+
+  // FIXME: Setup cleanup scopes.
+
+  EmitStmt(S.getPromiseDeclStmt());
+
+  CurCoro.Data->FinalJD = getJumpDestInCurrentScope(FinalBB);
+
+  // FIXME: Emit initial suspend and more before the body.
+
+  EmitStmt(S.getBody());
+
+  // See if we need to generate final suspend.
+  const bool CanFallthrough = Builder.GetInsertBlock();
+  const bool HasCoreturns = CurCoro.Data->CoreturnCount > 0;
+  if (CanFallthrough || HasCoreturns) {
+    EmitBlock(FinalBB);
+    // FIXME: Emit final suspend.
+  }
   EmitStmt(S.getDeallocate());
+
+  // FIXME: Emit return for the coroutine return object.
 }
 
 // Emit coroutine intrinsic and patch up arguments of the token type.
index 8370607db50f97a3d97947f54cb2a0f117ee577a..9c92367410c0da13dcdc4296c3bf0e921d11790a 100644 (file)
@@ -145,7 +145,7 @@ void CodeGenFunction::EmitStmt(const Stmt *S) {
     EmitCoroutineBody(cast<CoroutineBodyStmt>(*S));
     break;
   case Stmt::CoreturnStmtClass:
-    CGM.ErrorUnsupported(S, "coroutine");
+    EmitCoreturnStmt(cast<CoreturnStmt>(*S));
     break;
   case Stmt::CapturedStmtClass: {
     const CapturedStmt *CS = cast<CapturedStmt>(S);
index 347908b9f7b9545f695bf4af6a4fd65844d6a77c..659ec0fc5c82cea579b60d3f49b36b5b45c34421 100644 (file)
@@ -2506,6 +2506,7 @@ public:
   void EmitObjCAutoreleasePoolStmt(const ObjCAutoreleasePoolStmt &S);
 
   void EmitCoroutineBody(const CoroutineBodyStmt &S);
+  void EmitCoreturnStmt(const CoreturnStmt &S);
   RValue EmitCoroutineIntrinsic(const CallExpr *E, unsigned int IID);
 
   void EnterCXXTryStmt(const CXXTryStmt &S, bool IsFnTryBlock = false);
index 7dbbdd31be0856ef00b08066d999263842335b77..87da7aed896148186ede9fb796b2668270c62f10 100644 (file)
@@ -34,7 +34,7 @@ extern "C" void f0(global_new_delete_tag) {
   // 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_await suspend_always{};
+  co_return;
 }
 
 struct promise_new_tag {};
@@ -59,7 +59,7 @@ extern "C" void f1(promise_new_tag ) {
   // 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_await suspend_always{};
+  co_return;
 }
 
 struct promise_delete_tag {};
@@ -84,7 +84,7 @@ extern "C" void f2(promise_delete_tag) {
   // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.frame()
   // CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]])
   // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJv18promise_delete_tagEE12promise_typedlEPv(i8* %[[MEM]])
-  co_await suspend_always{};
+  co_return;
 }
 
 struct promise_sized_delete_tag {};
@@ -100,7 +100,7 @@ struct std::experimental::coroutine_traits<void, promise_sized_delete_tag> {
   };
 };
 
-// CHECK-LABEL: f3( 
+// CHECK-LABEL: f3(
 extern "C" void f3(promise_sized_delete_tag) {
   // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16
   // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
@@ -110,5 +110,5 @@ extern "C" void f3(promise_sized_delete_tag) {
   // 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]])
-  co_await suspend_always{};
+  co_return;
 }
diff --git a/test/CodeGenCoroutines/coro-return.cpp b/test/CodeGenCoroutines/coro-return.cpp
new file mode 100644 (file)
index 0000000..2b94da8
--- /dev/null
@@ -0,0 +1,52 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s
+
+namespace std {
+namespace experimental {
+template <typename... T>
+struct coroutine_traits;
+}
+}
+
+struct suspend_always {
+  bool await_ready();
+  void await_suspend();
+  void await_resume();
+};
+
+template<>
+struct std::experimental::coroutine_traits<void> {
+  struct promise_type {
+    void get_return_object();
+    suspend_always initial_suspend();
+    suspend_always final_suspend();
+    void return_void();
+  };
+};
+
+// CHECK-LABEL: f0(
+extern "C" void f0() {
+  // CHECK: %__promise = alloca %"struct.std::experimental::coroutine_traits<void>::promise_type"
+  // CHECK: %call = call i8* @_Znwm(
+  // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type11return_voidEv(%"struct.std::experimental::coroutine_traits<void>::promise_type"* %__promise)
+  // CHECK: call void @_ZdlPv
+  co_return;
+}
+
+template<>
+struct std::experimental::coroutine_traits<int> {
+  struct promise_type {
+    int get_return_object();
+    suspend_always initial_suspend();
+    suspend_always final_suspend();
+    void return_value(int);
+  };
+};
+
+// CHECK-LABEL: f1(
+extern "C" int f1() {
+  // CHECK: %__promise = alloca %"struct.std::experimental::coroutine_traits<int>::promise_type"
+  // CHECK: %call = call i8* @_Znwm(
+  // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJiEE12promise_type12return_valueEi(%"struct.std::experimental::coroutine_traits<int>::promise_type"* %__promise, i32 42)
+  // CHECK: call void @_ZdlPv
+  co_return 42;
+}