aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoryifeng.dongyifeng <yifeng.dongyifeng@alibaba-inc.com>2021-04-12 10:59:22 +0800
committeryifeng.dongyifeng <yifeng.dongyifeng@alibaba-inc.com>2021-04-12 11:10:47 +0800
commit3a6a80b641bcffc570d5bc187b45f7bb9a2e7ce2 (patch)
treea24d0ced9cd4d7a729f6d6447dec31660d8cfb59
parent[PowerPC] Lower f128 SETCC/SELECT_CC as libcall if p9vector disabled (diff)
downloadllvm-project-3a6a80b641bcffc570d5bc187b45f7bb9a2e7ce2.tar.gz
llvm-project-3a6a80b641bcffc570d5bc187b45f7bb9a2e7ce2.tar.bz2
llvm-project-3a6a80b641bcffc570d5bc187b45f7bb9a2e7ce2.zip
[Clang][Coroutine][DebugInfo] In c++ coroutine, clang will emit different debug info variables for parameters and move-parameters.
The first one is the real parameters of the coroutine function, the other one just for copying parameters to the coroutine frame. Considering the following c++ code: ``` struct coro { ... }; coro foo(struct test & t) { ... co_await suspend_always(); ... co_await suspend_always(); ... co_await suspend_always(); } int main(int argc, char *argv[]) { auto c = foo(...); c.handle.resume(); ... } ``` Function foo is the standard coroutine function, and it has only one parameter named t (ignoring this at first), when we use the llvm code to compile this function, we can get the following ir: ``` !2921 = distinct !DISubprogram(name: "foo", linkageName: "_ZN6Object3fooE4test", scope: !2211, file: !45, li\ ne: 48, type: !2329, scopeLine: 48, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefi\ nition | DISPFlagOptimized, unit: !44, declaration: !2328, retainedNodes: !2922) !2924 = !DILocalVariable(name: "t", arg: 2, scope: !2921, file: !45, line: 48, type: !838) ... !2926 = !DILocalVariable(name: "t", scope: !2921, type: !838, flags: DIFlagArtificial) ``` We can find there are two `the same` DIVariable named t in the same dwarf scope for foo.resume. And when we try to use llvm-dwarfdump to dump the dwarf info of this elf, we get the following output: ``` 0x00006684: DW_TAG_subprogram DW_AT_low_pc (0x00000000004013a0) DW_AT_high_pc (0x00000000004013a8) DW_AT_frame_base (DW_OP_reg7 RSP) DW_AT_object_pointer (0x0000669c) DW_AT_GNU_all_call_sites (true) DW_AT_specification (0x00005b5c "_ZN6Object3fooE4test") 0x000066a5: DW_TAG_formal_parameter DW_AT_name ("t") DW_AT_decl_file ("/disk1/yifeng.dongyifeng/my_code/llvm/build/bin/coro-debug-1.cpp") DW_AT_decl_line (48) DW_AT_type (0x00004146 "test") 0x000066ba: DW_TAG_variable DW_AT_name ("t") DW_AT_type (0x00004146 "test") DW_AT_artificial (true) ``` The elf also has two 't' in the same scope. But unluckily, it might let the debugger confused. And failed to print parameters for O0 or above. This patch will make coroutine parameters and move parameters use the same DIVar and try to fix the problems that I mentioned before. Test Plan: check-clang Reviewed By: aprantl, jmorse Differential Revision: https://reviews.llvm.org/D97533
-rw-r--r--clang/lib/CodeGen/CGCoroutine.cpp11
-rw-r--r--clang/lib/CodeGen/CGDebugInfo.cpp60
-rw-r--r--clang/lib/CodeGen/CGDebugInfo.h20
-rw-r--r--clang/lib/CodeGen/CGDecl.cpp5
-rw-r--r--clang/lib/CodeGen/CodeGenFunction.cpp5
-rw-r--r--clang/lib/CodeGen/CodeGenFunction.h3
-rw-r--r--clang/test/CodeGenCoroutines/coro-dwarf.cpp77
7 files changed, 168 insertions, 13 deletions
diff --git a/clang/lib/CodeGen/CGCoroutine.cpp b/clang/lib/CodeGen/CGCoroutine.cpp
index 038238c84046..ca071d3d2e80 100644
--- a/clang/lib/CodeGen/CGCoroutine.cpp
+++ b/clang/lib/CodeGen/CGCoroutine.cpp
@@ -602,10 +602,21 @@ void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) {
CurCoro.Data->CleanupJD = getJumpDestInCurrentScope(RetBB);
{
+ CGDebugInfo *DI = getDebugInfo();
ParamReferenceReplacerRAII ParamReplacer(LocalDeclMap);
CodeGenFunction::RunCleanupsScope ResumeScope(*this);
EHStack.pushCleanup<CallCoroDelete>(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.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 455552a5ea6d..d20b309b476d 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -4364,13 +4364,53 @@ llvm::DILocalVariable *CGDebugInfo::EmitDeclare(const VarDecl *VD,
}
// 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<llvm::DISubprogram>(Scope) || !Scope->isDistinct())
+ return nullptr;
+
+ auto Iter = llvm::find_if(CoroutineParameterMappings, [&](auto &Pair) {
+ Stmt *StmtPtr = const_cast<Stmt *>(Pair.second);
+ if (DeclStmt *DeclStmtPtr = dyn_cast<DeclStmt>(StmtPtr)) {
+ DeclGroupRef DeclGroup = DeclStmtPtr->getDeclGroup();
+ Decl *Decl = DeclGroup.getSingleDecl();
+ if (VD == dyn_cast_or_null<VarDecl>(Decl))
+ return true;
+ }
+ return false;
+ });
+
+ if (Iter != CoroutineParameterMappings.end()) {
+ ParmVarDecl *PD = const_cast<ParmVarDecl *>(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<llvm::DILocalVariable *>(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 @@ void CGDebugInfo::EmitDeclareOfBlockDeclRefVariable(
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/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h
index afd5b50c182a..228ac691a0b7 100644
--- a/clang/lib/CodeGen/CGDebugInfo.h
+++ b/clang/lib/CodeGen/CGDebugInfo.h
@@ -161,6 +161,15 @@ class CGDebugInfo {
llvm::DenseMap<const Decl *, llvm::TypedTrackingMDRef<llvm::DIDerivedType>>
StaticDataMemberCache;
+ using ParamDecl2StmtTy = llvm::DenseMap<const ParmVarDecl *, const Stmt *>;
+ using Param2DILocTy =
+ llvm::DenseMap<const ParmVarDecl *, llvm::DILocalVariable *>;
+
+ /// 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 @@ public:
/// 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 @@ public:
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/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 243d93a8c165..d72b9c4b6e15 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -2569,7 +2569,10 @@ void CodeGenFunction::EmitParmDecl(const VarDecl &D, ParamValue Arg,
// 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<ParmVarDecl>(&D))
+ DI->getParamDbgMappings().insert({Var, DILocalVar});
}
}
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 5ff969dbf285..ca59dc4a2b61 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -1331,6 +1331,11 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn,
// Emit the standard function prologue.
StartFunction(GD, ResTy, Fn, FnInfo, Args, Loc, BodyRange.getBegin());
+ // Save parameters for coroutine function.
+ if (Body && isa_and_nonnull<CoroutineBodyStmt>(Body))
+ for (const auto *ParamDecl : FD->parameters())
+ FnArgs.push_back(ParamDecl);
+
// Generate the body of the function.
PGO.assignRegionCounters(GD, CurFn);
if (isa<CXXDestructorDecl>(FD))
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index f14a1c248396..ceb161f5a8cb 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -325,6 +325,9 @@ public:
QualType FnRetTy;
llvm::Function *CurFn = nullptr;
+ /// Save Parameter Decl for coroutine.
+ llvm::SmallVector<const ParmVarDecl *, 4> 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/test/CodeGenCoroutines/coro-dwarf.cpp b/clang/test/CodeGenCoroutines/coro-dwarf.cpp
new file mode 100644
index 000000000000..eacbd5b39d2a
--- /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 <typename... T> struct coroutine_traits;
+
+template <class Promise = void> struct coroutine_handle {
+ coroutine_handle() = default;
+ static coroutine_handle from_address(void *) noexcept;
+};
+template <> struct coroutine_handle<void> {
+ static coroutine_handle from_address(void *) noexcept;
+ coroutine_handle() = default;
+ template <class PromiseType>
+ coroutine_handle(coroutine_handle<PromiseType>) noexcept;
+};
+} // namespace std::experimental
+
+struct suspend_always {
+ bool await_ready() noexcept;
+ void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_resume() noexcept;
+};
+
+template <typename... Args> struct std::experimental::coroutine_traits<void, Args...> {
+ 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)