diff --git a/clang/lib/CodeGen/CGCoroutine.cpp b/clang/lib/CodeGen/CGCoroutine.cpp --- a/clang/lib/CodeGen/CGCoroutine.cpp +++ b/clang/lib/CodeGen/CGCoroutine.cpp @@ -602,10 +602,21 @@ CurCoro.Data->CleanupJD = getJumpDestInCurrentScope(RetBB); { + CGDebugInfo *DI = getDebugInfo(); ParamReferenceReplacerRAII ParamReplacer(LocalDeclMap); CodeGenFunction::RunCleanupsScope ResumeScope(*this); EHStack.pushCleanup(NormalAndEHCleanup, S.getDeallocate()); + // Create mapping between parameters and copy-params for coroutine function. + auto ParamMoves = S.getParamMoves(); + assert( + (ParamMoves.size() == 0 || (ParamMoves.size() == FnArgs.size())) && + "ParamMoves and FnArgs should be the same size for coroutine function"); + if (ParamMoves.size() == FnArgs.size() && DI) + for (const auto Pair : llvm::zip(FnArgs, ParamMoves)) + DI->getCoroutineParameterMappings().insert( + {std::get<0>(Pair), std::get<1>(Pair)}); + // Create parameter copies. We do it before creating a promise, since an // evolution of coroutine TS may allow promise constructor to observe // parameter copies. diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h --- a/clang/lib/CodeGen/CGDebugInfo.h +++ b/clang/lib/CodeGen/CGDebugInfo.h @@ -161,6 +161,15 @@ llvm::DenseMap> StaticDataMemberCache; + using ParamDecl2StmtTy = llvm::DenseMap; + using Param2DILocTy = + llvm::DenseMap; + + /// The key is coroutine real parameters, value is coroutine move parameters. + ParamDecl2StmtTy CoroutineParameterMappings; + /// The key is coroutine real parameters, value is DIVariable in LLVM IR. + Param2DILocTy ParamDbgMappings; + /// Helper functions for getOrCreateType. /// @{ /// Currently the checksum of an interface includes the number of @@ -463,8 +472,10 @@ /// Emit call to \c llvm.dbg.declare for an argument variable /// declaration. - void EmitDeclareOfArgVariable(const VarDecl *Decl, llvm::Value *AI, - unsigned ArgNo, CGBuilderTy &Builder); + llvm::DILocalVariable *EmitDeclareOfArgVariable(const VarDecl *Decl, + llvm::Value *AI, + unsigned ArgNo, + CGBuilderTy &Builder); /// Emit call to \c llvm.dbg.declare for the block-literal argument /// to a block invocation function. @@ -533,6 +544,11 @@ SourceLocation LineLoc, SourceLocation FileLoc); + Param2DILocTy &getParamDbgMappings() { return ParamDbgMappings; } + ParamDecl2StmtTy &getCoroutineParameterMappings() { + return CoroutineParameterMappings; + } + private: /// Emit call to llvm.dbg.declare for a variable declaration. /// Returns a pointer to the DILocalVariable associated with the diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -4364,13 +4364,53 @@ } // Create the descriptor for the variable. - auto *D = ArgNo ? DBuilder.createParameterVariable( - Scope, Name, *ArgNo, Unit, Line, Ty, - CGM.getLangOpts().Optimize, Flags) - : DBuilder.createAutoVariable(Scope, Name, Unit, Line, Ty, - CGM.getLangOpts().Optimize, - Flags, Align); + llvm::DILocalVariable *D = nullptr; + if (ArgNo) { + D = DBuilder.createParameterVariable(Scope, Name, *ArgNo, Unit, Line, Ty, + CGM.getLangOpts().Optimize, Flags); + } else { + // For normal local variable, we will try to find out whether 'VD' is the + // copy parameter of coroutine. + // If yes, we are going to use DIVariable of the origin parameter instead + // of creating the new one. + // If no, it might be a normal alloc, we just create a new one for it. + + // Check whether the VD is move parameters. + auto RemapCoroArgToLocalVar = [&]() -> llvm::DILocalVariable * { + // The scope of parameter and move-parameter should be distinct + // DISubprogram. + if (!isa(Scope) || !Scope->isDistinct()) + return nullptr; + + auto Iter = llvm::find_if(CoroutineParameterMappings, [&](auto &Pair) { + Stmt *StmtPtr = const_cast(Pair.second); + if (DeclStmt *DeclStmtPtr = dyn_cast(StmtPtr)) { + DeclGroupRef DeclGroup = DeclStmtPtr->getDeclGroup(); + Decl *Decl = DeclGroup.getSingleDecl(); + if (VD == dyn_cast_or_null(Decl)) + return true; + } + return false; + }); + + if (Iter != CoroutineParameterMappings.end()) { + ParmVarDecl *PD = const_cast(Iter->first); + auto Iter2 = llvm::find_if(ParamDbgMappings, [&](auto &DbgPair) { + return DbgPair.first == PD && DbgPair.second->getScope() == Scope; + }); + if (Iter2 != ParamDbgMappings.end()) + return const_cast(Iter2->second); + } + return nullptr; + }; + // If we couldn't find a move param DIVariable, create a new one. + D = RemapCoroArgToLocalVar(); + // Or we will create a new DIVariable for this Decl if D dose not exists. + if (!D) + D = DBuilder.createAutoVariable(Scope, Name, Unit, Line, Ty, + CGM.getLangOpts().Optimize, Flags, Align); + } // Insert an llvm.dbg.declare into the current block. DBuilder.insertDeclare(Storage, D, DBuilder.createExpression(Expr), llvm::DILocation::get(CGM.getLLVMContext(), Line, @@ -4495,11 +4535,11 @@ DBuilder.insertDeclare(Storage, D, Expr, DL, Builder.GetInsertBlock()); } -void CGDebugInfo::EmitDeclareOfArgVariable(const VarDecl *VD, llvm::Value *AI, - unsigned ArgNo, - CGBuilderTy &Builder) { +llvm::DILocalVariable * +CGDebugInfo::EmitDeclareOfArgVariable(const VarDecl *VD, llvm::Value *AI, + unsigned ArgNo, CGBuilderTy &Builder) { assert(CGM.getCodeGenOpts().hasReducedDebugInfo()); - EmitDeclare(VD, AI, ArgNo, Builder); + return EmitDeclare(VD, AI, ArgNo, Builder); } namespace { diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -2569,7 +2569,10 @@ // Emit debug info for param declarations in non-thunk functions. if (CGDebugInfo *DI = getDebugInfo()) { if (CGM.getCodeGenOpts().hasReducedDebugInfo() && !CurFuncIsThunk) { - DI->EmitDeclareOfArgVariable(&D, DeclPtr.getPointer(), ArgNo, Builder); + llvm::DILocalVariable *DILocalVar = DI->EmitDeclareOfArgVariable( + &D, DeclPtr.getPointer(), ArgNo, Builder); + if (const auto *Var = dyn_cast_or_null(&D)) + DI->getParamDbgMappings().insert({Var, DILocalVar}); } } diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -325,6 +325,9 @@ QualType FnRetTy; llvm::Function *CurFn = nullptr; + /// Save Parameter Decl for coroutine. + llvm::SmallVector FnArgs; + // Holds coroutine data if the current function is a coroutine. We use a // wrapper to manage its lifetime, so that we don't have to define CGCoroData // in this header. diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -1331,6 +1331,11 @@ // Emit the standard function prologue. StartFunction(GD, ResTy, Fn, FnInfo, Args, Loc, BodyRange.getBegin()); + // Save parameters for coroutine function. + if (Body && isa_and_nonnull(Body)) + for (const auto *ParamDecl : FD->parameters()) + FnArgs.push_back(ParamDecl); + // Generate the body of the function. PGO.assignRegionCounters(GD, CurFn); if (isa(FD)) diff --git a/clang/test/CodeGenCoroutines/coro-dwarf.cpp b/clang/test/CodeGenCoroutines/coro-dwarf.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCoroutines/coro-dwarf.cpp @@ -0,0 +1,77 @@ +// RUN: %clang_cc1 -std=c++2a -fcoroutines-ts -triple=x86_64 -dwarf-version=4 -debug-info-kind=limited -emit-llvm -o - %s | FileCheck %s + +namespace std::experimental { +template struct coroutine_traits; + +template struct coroutine_handle { + coroutine_handle() = default; + static coroutine_handle from_address(void *) noexcept; +}; +template <> struct coroutine_handle { + static coroutine_handle from_address(void *) noexcept; + coroutine_handle() = default; + template + coroutine_handle(coroutine_handle) noexcept; +}; +} // namespace std::experimental + +struct suspend_always { + bool await_ready() noexcept; + void await_suspend(std::experimental::coroutine_handle<>) noexcept; + void await_resume() noexcept; +}; + +template struct std::experimental::coroutine_traits { + struct promise_type { + void get_return_object() noexcept; + suspend_always initial_suspend() noexcept; + suspend_always final_suspend() noexcept; + void return_void() noexcept; + promise_type(); + ~promise_type() noexcept; + void unhandled_exception() noexcept; + }; +}; + +// TODO: Not supported yet +struct CopyOnly { + int val; + CopyOnly(const CopyOnly &) noexcept; + CopyOnly(CopyOnly &&) = delete; + ~CopyOnly(); +}; + +struct MoveOnly { + int val; + MoveOnly(const MoveOnly &) = delete; + MoveOnly(MoveOnly &&) noexcept; + ~MoveOnly(); +}; + +struct MoveAndCopy { + int val; + MoveAndCopy(const MoveAndCopy &) noexcept; + MoveAndCopy(MoveAndCopy &&) noexcept; + ~MoveAndCopy(); +}; + +void consume(int, int, int) noexcept; + +void f_coro(int val, MoveOnly moParam, MoveAndCopy mcParam) { + consume(val, moParam.val, mcParam.val); + co_return; +} + +// CHECK: ![[SP:[0-9]+]] = distinct !DISubprogram(name: "f_coro", linkageName: "_Z6f_coroi8MoveOnly11MoveAndCopy" +// CHECK: !{{[0-9]+}} = !DILocalVariable(name: "val", arg: 1, scope: ![[SP]], file: !8, line: 60, type: !{{[0-9]+}}) +// CHECK: !{{[0-9]+}} = !DILocation(line: 60, column: 17, scope: ![[SP]]) +// CHECK: !{{[0-9]+}} = !DILocalVariable(name: "moParam", arg: 2, scope: ![[SP]], file: !8, line: 60, type: !{{[0-9]+}}) +// CHECK: !{{[0-9]+}} = !DILocation(line: 60, column: 31, scope: ![[SP]]) +// CHECK: !{{[0-9]+}} = !DILocalVariable(name: "mcParam", arg: 3, scope: ![[SP]], file: !8, line: 60, type: !{{[0-9]+}}) +// CHECK: !{{[0-9]+}} = !DILocation(line: 60, column: 52, scope: ![[SP]]) +// CHECK: !{{[0-9]+}} = !DILocation(line: 60, column: 61, scope: ![[SP]]) +// CHECK: !{{[0-9]+}} = !DILocation(line: 60, column: 6, scope: ![[SP]]) +// CHECK: !{{[0-9]+}} = !DILocation(line: 0, scope: ![[SP]]) +// CHECK-NOT: !{{[0-9]+}} = !DILocalVariable(name: "val", scope: ![[SP]], type: !{{[0-9]+}}, flags: DIFlagArtificial) +// CHECK-NOT: !{{[0-9]+}} = !DILocalVariable(name: "moParam", scope: ![[SP]], type: !{{[0-9]+}}, flags: DIFlagArtificial) +// CHECK-NOT:: !{{[0-9]+}} = !DILocalVariable(name: "mcParam", scope: ![[SP]], type: !{{[0-9]+}}, flags: DIFlagArtificial)