-
Notifications
You must be signed in to change notification settings - Fork 10.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[coroutines] Use DILocation from new storage for hoisted dbg.declare #75104
Conversation
@llvm/pr-subscribers-llvm-transforms @llvm/pr-subscribers-coroutines Author: Wei Wang (apolloww) ChangesAfter hoisting, the dbg.declare is moved into the block that defines the new storage. This could create an inconsistency in the debug location scope hierarchy where the scope of hoisted dbg.declare (i.e. DILexicalBlock) is enclosed with the scope of the block (i.e. DISubprogram). This confuses LiveDebugValues pass to think that the hoisted dbg.declare is killed in that block and does not generate DBG_VALUE. Full diff: https://github.com/llvm/llvm-project/pull/75104.diff 2 Files Affected:
diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
index 1134b20880f183..2c43f49e712f20 100644
--- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
@@ -2887,9 +2887,11 @@ void coro::salvageDebugInfo(
// dbg.declare does.
if (isa<DbgDeclareInst>(DVI)) {
std::optional<BasicBlock::iterator> InsertPt;
- if (auto *I = dyn_cast<Instruction>(Storage))
+ if (auto *I = dyn_cast<Instruction>(Storage)) {
InsertPt = I->getInsertionPointAfterDef();
- else if (isa<Argument>(Storage))
+ if (I->getDebugLoc())
+ DVI->setDebugLoc(I->getDebugLoc());
+ } else if (isa<Argument>(Storage))
InsertPt = F->getEntryBlock().begin();
if (InsertPt)
DVI->moveBefore(*(*InsertPt)->getParent(), *InsertPt);
diff --git a/llvm/test/Transforms/Coroutines/coro-debug-frame-variable.ll b/llvm/test/Transforms/Coroutines/coro-debug-frame-variable.ll
index 37b4126ce37304..53995fb3498e59 100644
--- a/llvm/test/Transforms/Coroutines/coro-debug-frame-variable.ll
+++ b/llvm/test/Transforms/Coroutines/coro-debug-frame-variable.ll
@@ -31,8 +31,8 @@
; CHECK: entry:
; CHECK: %j = alloca i32, align 4
; CHECK: call void @llvm.dbg.declare(metadata ptr %j, metadata ![[JVAR:[0-9]+]], metadata !DIExpression()), !dbg ![[JDBGLOC:[0-9]+]]
-; CHECK: %[[MEMORY:.*]] = call ptr @new
-; CHECK: call void @llvm.dbg.declare(metadata ptr %[[MEMORY]], metadata ![[XVAR:[0-9]+]], metadata !DIExpression(DW_OP_plus_uconst, 32)), !dbg ![[IDBGLOC:[0-9]+]]
+; CHECK: %[[MEMORY:.*]] = call ptr @new({{.+}}), !dbg ![[IDBGLOC:[0-9]+]]
+; CHECK: call void @llvm.dbg.declare(metadata ptr %[[MEMORY]], metadata ![[XVAR:[0-9]+]], metadata !DIExpression(DW_OP_plus_uconst, 32)), !dbg ![[IDBGLOC]]
; CHECK: call void @llvm.dbg.declare(metadata ptr %[[MEMORY]], metadata ![[IVAR:[0-9]+]], metadata !DIExpression(DW_OP_plus_uconst, 20)), !dbg ![[IDBGLOC]]
; CHECK: await.ready:
;
@@ -48,18 +48,20 @@
; CHECK: await.ready:
;
; CHECK-DAG: ![[IVAR]] = !DILocalVariable(name: "i"
-; CHECK-DAG: ![[SCOPE:[0-9]+]] = distinct !DILexicalBlock(scope: !6, file: !1, line: 23, column: 12)
-; CHECK-DAG: ![[IDBGLOC]] = !DILocation(line: 24, column: 7, scope: ![[SCOPE]])
+; CHECK-DAG: ![[PROG_SCOPE:[0-9]+]] = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov"
+; CHECK-DAG: ![[BLK_SCOPE:[0-9]+]] = distinct !DILexicalBlock(scope: ![[PROG_SCOPE]], file: !1, line: 23, column: 12)
+; CHECK-DAG: ![[IDBGLOC]] = !DILocation(line: 23, column: 6, scope: ![[PROG_SCOPE]])
; CHECK-DAG: ![[XVAR]] = !DILocalVariable(name: "x"
; CHECK-DAG: ![[JVAR]] = !DILocalVariable(name: "j"
-; CHECK-DAG: ![[JDBGLOC]] = !DILocation(line: 32, column: 7, scope: ![[SCOPE]])
+; CHECK-DAG: ![[JDBGLOC]] = !DILocation(line: 32, column: 7, scope: ![[BLK_SCOPE]])
; CHECK-DAG: ![[XVAR_RESUME]] = !DILocalVariable(name: "x"
-; CHECK-DAG: ![[IDBGLOC_RESUME]] = !DILocation(line: 24, column: 7, scope: ![[RESUME_SCOPE:[0-9]+]])
-; CHECK-DAG: ![[RESUME_SCOPE]] = distinct !DILexicalBlock(scope: !22, file: !1, line: 23, column: 12)
+; CHECK-DAG: ![[RESUME_PROG_SCOPE:[0-9]+]] = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov"
+; CHECK-DAG: ![[IDBGLOC_RESUME]] = !DILocation(line: 24, column: 7, scope: ![[RESUME_BLK_SCOPE:[0-9]+]])
+; CHECK-DAG: ![[RESUME_BLK_SCOPE]] = distinct !DILexicalBlock(scope: ![[RESUME_PROG_SCOPE]], file: !1, line: 23, column: 12)
; CHECK-DAG: ![[IVAR_RESUME]] = !DILocalVariable(name: "i"
; CHECK-DAG: ![[JVAR_RESUME]] = !DILocalVariable(name: "j"
-; CHECK-DAG: ![[JDBGLOC_RESUME]] = !DILocation(line: 32, column: 7, scope: ![[RESUME_SCOPE]])
+; CHECK-DAG: ![[JDBGLOC_RESUME]] = !DILocation(line: 32, column: 7, scope: ![[RESUME_BLK_SCOPE]])
define void @f() presplitcoroutine !dbg !8 {
entry:
%__promise = alloca i8, align 8
@@ -71,13 +73,13 @@ entry:
br i1 %alloc, label %coro.alloc, label %coro.init
coro.alloc: ; preds = %entry
- %size = call i64 @llvm.coro.size.i64()
- %memory = call ptr @new(i64 %size)
- br label %coro.init
+ %size = call i64 @llvm.coro.size.i64(), !dbg !23
+ %memory = call ptr @new(i64 %size), !dbg !23
+ br label %coro.init, !dbg !23
coro.init: ; preds = %coro.alloc, %entry
- %phi.entry.alloc = phi ptr [ null, %entry ], [ %memory, %coro.alloc ]
- %begin = call ptr @llvm.coro.begin(token %id, ptr %phi.entry.alloc)
+ %phi.entry.alloc = phi ptr [ null, %entry ], [ %memory, %coro.alloc ], !dbg !23
+ %begin = call ptr @llvm.coro.begin(token %id, ptr %phi.entry.alloc), !dbg !23
%ready = call i1 @await_ready()
br i1 %ready, label %init.ready, label %init.suspend
@@ -239,3 +241,4 @@ declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg)
!20 = !DILocation(line: 43, column: 3, scope: !7)
!21 = !DILocation(line: 43, column: 8, scope: !7)
!22 = distinct !DILexicalBlock(scope: !8, file: !1, line: 23, column: 12)
+!23 = !DILocation(line: 23, column: 6, scope: !8)
|
This is to fix an issue we saw internally where lldb can't see local variable (lives on frame) in the ramp function. It is because after hoisting, we have something like define dso_local ptr @_Z3fooi(i32 noundef %arg) !dbg !1455 {
... ...
coro.init: ; preds = %coro.init.from.entry, %coro.init.from.coro.alloc
%0 = phi ptr [ %.coro.init, %coro.init.from.entry ], [ %call.coro.init, %coro.init.from.coro.alloc ], !dbg !1489
call void @llvm.dbg.declare(metadata ptr %0, metadata !1491, metadata !DIExpression(DW_OP_plus_uconst, 24)), !dbg !1492
call void @llvm.dbg.declare(metadata ptr %0, metadata !1493, metadata !DIExpression(DW_OP_plus_uconst, 16)), !dbg !1494
call void @llvm.dbg.declare(metadata ptr %0, metadata !1484, metadata !DIExpression(DW_OP_plus_uconst, 20)), !dbg !1494
call void @llvm.dbg.declare(metadata ptr %0, metadata !1459, metadata !DIExpression()), !dbg !1494
... ...
!1455 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !19, file: !19, line: 20, type: !1456, scopeLine: 20, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !18, retainedNodes: !1458)
!1489 = !DILocation(line: 20, column: 19, scope: !1455)
!1487 = distinct !DILexicalBlock(scope: !1455, file: !19, line: 20, column: 19)
!1492 = !DILocation(line: 21, column: 7, scope: !1487)
!1494 = !DILocation(line: 0, scope: !1455) Because The same issue also happens to define internal fastcc void @_Z3fooi.resume(ptr noundef nonnull align 8 dereferenceable(32) %0) !dbg !1779 {
entry.resume:
%.debug = alloca ptr, align 8
call void @llvm.dbg.declare(metadata ptr %.debug, metadata !1782, metadata !DIExpression(DW_OP_deref, DW_OP_plus_uconst, 24)), !dbg !1784
call void @llvm.dbg.declare(metadata ptr %.debug, metadata !1785, metadata !DIExpression(DW_OP_deref, DW_OP_plus_uconst, 16)), !dbg !1786
call void @llvm.dbg.declare(metadata ptr %.debug, metadata !1787, metadata !DIExpression(DW_OP_deref, DW_OP_plus_uconst, 20)), !dbg !1786
call void @llvm.dbg.declare(metadata ptr %.debug, metadata !1781, metadata !DIExpression(DW_OP_deref)), !dbg !1786
.... ....
!1779 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !19, file: !19, line: 20, type: !1456, scopeLine: 20, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !18, retainedNodes: !1780)
!1783 = distinct !DILexicalBlock(scope: !1779, file: !19, line: 20, column: 19)
!1784 = !DILocation(line: 21, column: 7, scope: !1783)
!1786 = !DILocation(line: 0, scope: !1779) but we've got lucky because |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Thanks.
This caused a regression building one of my projects, clang now generates invalid IR:
I can confirm reverting this fixes the issue. I am going to revert this on main now, and will work to provide a reduced reproducer shortly. |
Sorry for the breakage. I'll look into this once the repro is available. |
Reproducer: clang -cc1 -triple x86_64-pc-windows-msvc -emit-llvm -flto=full -gcodeview -debug-info-kind=constructor -O1 -std=c++20 -Wno-coroutine-missing-unhandled-exception -x c++ test.cpp namespace std {
template <class = void> struct coroutine_handle {
static coroutine_handle from_address(void *);
operator coroutine_handle<>();
operator bool();
void *address();
};
template <class _Ret, class...> struct coroutine_traits : _Ret {};
}
struct A {
bool await_ready() noexcept;
template <class PromiseType>
auto await_suspend(std::coroutine_handle<PromiseType>) -> std::coroutine_handle<>;
void await_resume() noexcept;
};
std::coroutine_handle a;
struct B {
struct promise_type {
B get_return_object();
void return_void();
A initial_suspend();
A final_suspend() noexcept;
};
~B() {
if (auto b = a)
;
}
};
B f() {
for (;;) {
A d;
[]() -> B { co_return; }();
co_await d;
}
} Produces:
|
…level for hoisted DbgDeclare Loc (#92978) Minor patch following up on #75402. The more generalized version of [this error](#75104 (comment)) is whenever we have a debug variable created in one subprogram scope inlined into another subprogram scope. So instead of checking optimization level, it is safer to just check whether the subprogram scope of the variable matches the subprogram scope of the hoisted position.
Make the hoisted dbg.declare inherent the DILocation scope from the new storage.
After hoisting, the dbg.declare is moved into the block that defines the new storage. This could create an inconsistency in the debug location scope hierarchy where the scope of hoisted dbg.declare (i.e. DILexicalBlock) is enclosed with the scope of the block (i.e. DISubprogram). This confuses LiveDebugValues pass to think that the hoisted dbg.declare is killed in that block and does not generate DBG_VALUE in other blocks. Debugger won't be able to track its value anymore.