//===----- CGCoroutine.cpp - Emit LLVM Code for C++ coroutines ------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This contains code dealing with C++ code generation of coroutines. // //===----------------------------------------------------------------------===// #include "CGCleanup.h" #include "CodeGenFunction.h" #include "llvm/ADT/ScopeExit.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtVisitor.h" usingnamespaceclang; usingnamespaceCodeGen; Value; BasicBlock; namespace { enum class AwaitKind { … }; static constexpr llvm::StringLiteral AwaitKindStr[] = …; } struct clang::CodeGen::CGCoroData { … }; // Defining these here allows to keep CGCoroData private to this file. clang::CodeGen::CodeGenFunction::CGCoroInfo::CGCoroInfo() { … } CodeGenFunction::CGCoroInfo::~CGCoroInfo() { … } static void createCoroData(CodeGenFunction &CGF, CodeGenFunction::CGCoroInfo &CurCoro, llvm::CallInst *CoroId, CallExpr const *CoroIdExpr = nullptr) { … } // Synthesize a pretty name for a suspend point. static SmallString<32> buildSuspendPrefixStr(CGCoroData &Coro, AwaitKind Kind) { … } // Check if function can throw based on prototype noexcept, also works for // destructors which are implicitly noexcept but can be marked noexcept(false). static bool FunctionCanThrow(const FunctionDecl *D) { … } static bool StmtCanThrow(const Stmt *S) { … } // Emit suspend expression which roughly looks like: // // auto && x = CommonExpr(); // if (!x.await_ready()) { // llvm_coro_save(); // llvm_coro_await_suspend(&x, frame, wrapper) (*) (**) // llvm_coro_suspend(); (***) // } // x.await_resume(); // // where the result of the entire expression is the result of x.await_resume() // // (*) llvm_coro_await_suspend_{void, bool, handle} is lowered to // wrapper(&x, frame) when it's certain not to interfere with // coroutine transform. await_suspend expression is // asynchronous to the coroutine body and not all analyses // and transformations can handle it correctly at the moment. // // Wrapper function encapsulates x.await_suspend(...) call and looks like: // // auto __await_suspend_wrapper(auto& awaiter, void* frame) { // std::coroutine_handle<> handle(frame); // return awaiter.await_suspend(handle); // } // // (**) If x.await_suspend return type is bool, it allows to veto a suspend: // if (x.await_suspend(...)) // llvm_coro_suspend(); // // (***) llvm_coro_suspend() encodes three possible continuations as // a switch instruction: // // %where-to = call i8 @llvm.coro.suspend(...) // switch i8 %where-to, label %coro.ret [ ; jump to epilogue to suspend // i8 0, label %yield.ready ; go here when resumed // i8 1, label %yield.cleanup ; go here when destroyed // ] // // See llvm's docs/Coroutines.rst for more details. // namespace { struct LValueOrRValue { … }; } static LValueOrRValue emitSuspendExpression(CodeGenFunction &CGF, CGCoroData &Coro, CoroutineSuspendExpr const &S, AwaitKind Kind, AggValueSlot aggSlot, bool ignoreResult, bool forLValue) { … } RValue CodeGenFunction::EmitCoawaitExpr(const CoawaitExpr &E, AggValueSlot aggSlot, bool ignoreResult) { … } RValue CodeGenFunction::EmitCoyieldExpr(const CoyieldExpr &E, AggValueSlot aggSlot, bool ignoreResult) { … } void CodeGenFunction::EmitCoreturnStmt(CoreturnStmt const &S) { … } #ifndef NDEBUG static QualType getCoroutineSuspendExprReturnType(const ASTContext &Ctx, const CoroutineSuspendExpr *E) { const auto *RE = E->getResumeExpr(); // Is it possible for RE to be a CXXBindTemporaryExpr wrapping // a MemberCallExpr? assert(isa<CallExpr>(RE) && "unexpected suspend expression type"); return cast<CallExpr>(RE)->getCallReturnType(Ctx); } #endif llvm::Function * CodeGenFunction::generateAwaitSuspendWrapper(Twine const &CoroName, Twine const &SuspendPointName, CoroutineSuspendExpr const &S) { … } LValue CodeGenFunction::EmitCoawaitLValue(const CoawaitExpr *E) { … } LValue CodeGenFunction::EmitCoyieldLValue(const CoyieldExpr *E) { … } // Hunts for the parameter reference in the parameter copy/move declaration. namespace { struct GetParamRef : public StmtVisitor<GetParamRef> { … }; } // This class replaces references to parameters to their copies by changing // the addresses in CGF.LocalDeclMap and restoring back the original values in // its destructor. namespace { struct ParamReferenceReplacerRAII { … }; } // For WinEH exception representation backend needs to know what funclet coro.end // belongs to. That information is passed in a funclet bundle. static SmallVector<llvm::OperandBundleDef, 1> getBundlesForCoroEnd(CodeGenFunction &CGF) { … } namespace { // We will insert coro.end to cut any of the destructors for objects that // do not need to be destroyed once the coroutine is resumed. // See llvm/docs/Coroutines.rst for more details about coro.end. struct CallCoroEnd final : public EHScopeStack::Cleanup { … }; } namespace { // Make sure to call coro.delete on scope exit. struct CallCoroDelete final : public EHScopeStack::Cleanup { … }; } namespace { struct GetReturnObjectManager { … }; } // namespace static void emitBodyAndFallthrough(CodeGenFunction &CGF, const CoroutineBodyStmt &S, Stmt *Body) { … } void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) { … } // Emit coroutine intrinsic and patch up arguments of the token type. RValue CodeGenFunction::EmitCoroutineIntrinsic(const CallExpr *E, unsigned int IID) { … }