Index: cfe/trunk/lib/CodeGen/CGCoroutine.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGCoroutine.cpp +++ cfe/trunk/lib/CodeGen/CGCoroutine.cpp @@ -21,6 +21,13 @@ 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 @@ 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: cfe/trunk/lib/CodeGen/CGStmt.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGStmt.cpp +++ cfe/trunk/lib/CodeGen/CGStmt.cpp @@ -145,7 +145,7 @@ EmitCoroutineBody(cast(*S)); break; case Stmt::CoreturnStmtClass: - CGM.ErrorUnsupported(S, "coroutine"); + EmitCoreturnStmt(cast(*S)); break; case Stmt::CapturedStmtClass: { const CapturedStmt *CS = cast(S); Index: cfe/trunk/lib/CodeGen/CodeGenFunction.h =================================================================== --- cfe/trunk/lib/CodeGen/CodeGenFunction.h +++ cfe/trunk/lib/CodeGen/CodeGenFunction.h @@ -2506,6 +2506,7 @@ 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: cfe/trunk/test/CodeGenCoroutines/coro-alloc.cpp =================================================================== --- cfe/trunk/test/CodeGenCoroutines/coro-alloc.cpp +++ cfe/trunk/test/CodeGenCoroutines/coro-alloc.cpp @@ -34,7 +34,7 @@ // 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 @@ // 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 @@ // 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 @@ }; }; -// 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 @@ // 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; } Index: cfe/trunk/test/CodeGenCoroutines/coro-return.cpp =================================================================== --- cfe/trunk/test/CodeGenCoroutines/coro-return.cpp +++ cfe/trunk/test/CodeGenCoroutines/coro-return.cpp @@ -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 +struct coroutine_traits; +} +} + +struct suspend_always { + bool await_ready(); + void await_suspend(); + void await_resume(); +}; + +template<> +struct std::experimental::coroutine_traits { + 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::promise_type" + // CHECK: %call = call i8* @_Znwm( + // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type11return_voidEv(%"struct.std::experimental::coroutine_traits::promise_type"* %__promise) + // CHECK: call void @_ZdlPv + co_return; +} + +template<> +struct std::experimental::coroutine_traits { + 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::promise_type" + // CHECK: %call = call i8* @_Znwm( + // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJiEE12promise_type12return_valueEi(%"struct.std::experimental::coroutine_traits::promise_type"* %__promise, i32 42) + // CHECK: call void @_ZdlPv + co_return 42; +}