Andrew Kelley via llvm-dev
2018-Jun-27 17:41 UTC
[llvm-dev] can debug info for coroutines be improved?
I'm going to show the same function, first normally, and then as a coroutine, and show how gdb can see the variable when it's a normal function, but not when it's a coroutine. I'd like to understand if this can be improved. I'm trying to debug a real world problem, but the lack of debug info on variables in coroutines is making it difficult. Should I file a bug? Is this a fundamental limitation? Is anyone else interested in this? Here is a normal function source (in zig): const Point = struct { x: i32, y: i32, }; pub fn main() void { testAsyncSeq(); } fn testAsyncSeq() void { var blah = Point{ .x = 12, .y = 34 }; var bar = blah.x + 1; var baz = bar + 1; } when we step through `testAsyncSeq` in gdb, the `blah` variable is visible: Breakpoint 1, testAsyncSeq () at /home/andy/downloads/zig/build/test2.zig:13 13 var blah = Point{ .x = 12, .y = 34 }; (gdb) info locals blah = { x = 0, y = 0 } (gdb) next 14 var bar = blah.x + 1; (gdb) info locals bar = 0 blah = { x = 12, y = 34 } (gdb) quit However if I take advantage of LLVM's coroutines, the debug info seems to not be correct: const std = @import("std"); const Point = struct { x: i32, y: i32, }; pub fn main() !void { const p = try async<std.debug.global_allocator> testAsyncSeq(); resume p; cancel p; } async fn testAsyncSeq() void { var blah = Point{ .x = 12, .y = 34 }; suspend; var bar = blah.x + 1; var baz = bar + 1; } Now in gdb the `blah` variable is not visible: Breakpoint 1, testAsyncSeq () at /home/andy/downloads/zig/build/test.zig:14 14 async fn testAsyncSeq() void { (gdb) next 15 var blah = Point{ .x = 12, .y = 34 }; (gdb) 16 suspend; (gdb) info locals bar = <optimized out> blah = <optimized out> _anon = 0x0 _anon = 368 _anon = 0x23a028 <global_fixed_allocator> _anon = <optimized out> (gdb) quit Here is the LLVM IR generated for the function in the coroutines case. You can see that it has full debug info and @llvm.dbg.declare just like in the "normal function" case. ; Function Attrs: nobuiltin noinline nounwind optnone define internal fastcc i8* @testAsyncSeq(%StackTrace* nonnull, %Allocator*, i16*) unnamed_addr #5 !dbg !1163 { Entry: %3 = alloca %"[]usize", align 8 %4 = alloca i8*, align 8 %5 = alloca %"[]u8", align 8 %_anon = alloca %"AsyncFramePromise(void)", align 8 %_anon1 = alloca i8*, align 8 %_anon2 = alloca i64, align 8 %_anon3 = alloca %Allocator*, align 8 %blah = alloca %Point, align 4 %bar = alloca i32, align 4 %baz = alloca i32, align 4 %6 = bitcast %"AsyncFramePromise(void)"* %_anon to i8*, !dbg !1191 call void @llvm.memcpy.p0i8.p0i8.i64(i8* %6, i8* bitcast (%"AsyncFramePromise(void)"* @57 to i8*), i64 280, i32 8, i1 false), !dbg !1191 call void @llvm.dbg.declare(metadata %"AsyncFramePromise(void)"* %_anon, metadata !1168, metadata !DIExpression()), !dbg !1191 store i8* null, i8** %_anon1, align 8, !dbg !1191 call void @llvm.dbg.declare(metadata i8** %_anon1, metadata !1178, metadata !DIExpression()), !dbg !1191 %7 = bitcast %"AsyncFramePromise(void)"* %_anon to i8*, !dbg !1191 %8 = call token @llvm.coro.id(i32 16, i8* %7, i8* null, i8* null), !dbg !1191 %9 = call i64 @llvm.coro.size.i64(), !dbg !1191 store i64 %9, i64* %_anon2, align 8, !dbg !1191 call void @llvm.dbg.declare(metadata i64* %_anon2, metadata !1179, metadata !DIExpression()), !dbg !1191 store %Allocator* %1, %Allocator** %_anon3, align 8, !dbg !1191 call void @llvm.dbg.declare(metadata %Allocator** %_anon3, metadata !1180, metadata !DIExpression()), !dbg !1191 %10 = getelementptr inbounds %Allocator, %Allocator* %1, i32 0, i32 0, !dbg !1191 %11 = load void ({ i16, %"[]u8" }*, %StackTrace*, %Allocator*, i64, i29)*, void ({ i16, %"[]u8" }*, %StackTrace*, %Allocator*, i64, i29)** %10, align 8, !dbg !1191 %12 = call fastcc i8* @__zig_coro_alloc_helper(void ({ i16, %"[]u8" }*, %StackTrace*, %Allocator*, i64, i29)* %11, %StackTrace* %0, %Allocator* %1, i16* %2, i64 %9), !dbg !1191 %13 = icmp ne i8* %12, null, !dbg !1191 br i1 %13, label %AllocOk, label %AllocError, !dbg !1191 AllocError: ; preds = %Entry ret i8* undef, !dbg !1191 AllocOk: ; preds = %Entry %14 = call i8* @llvm.coro.begin(token %8, i8* %12), !dbg !1191 %15 = getelementptr inbounds %"AsyncFramePromise(void)", %"AsyncFramePromise(void)"* %_anon, i32 0, i32 0, !dbg !1191 store i8* null, i8** %15, align 8, !dbg !1191 %16 = getelementptr inbounds %"AsyncFramePromise(void)", %"AsyncFramePromise(void)"* %_anon, i32 0, i32 3, !dbg !1191 %17 = getelementptr inbounds %"AsyncFramePromise(void)", %"AsyncFramePromise(void)"* %_anon, i32 0, i32 2, !dbg !1191 %18 = getelementptr inbounds %StackTrace, %StackTrace* %17, i32 0, i32 0, !dbg !1191 store i64 0, i64* %18, align 8, !dbg !1191 %19 = getelementptr inbounds %StackTrace, %StackTrace* %17, i32 0, i32 1, !dbg !1191 %20 = getelementptr inbounds %"[]usize", %"[]usize"* %3, i32 0, i32 0, !dbg !1191 %21 = getelementptr inbounds [30 x i64], [30 x i64]* %16, i64 0, i64 0, !dbg !1191 store i64* %21, i64** %20, align 8, !dbg !1191 %22 = getelementptr inbounds %"[]usize", %"[]usize"* %3, i32 0, i32 1, !dbg !1191 store i64 30, i64* %22, align 8, !dbg !1191 %23 = bitcast %"[]usize"* %3 to i8*, !dbg !1191 %24 = bitcast %"[]usize"* %19 to i8*, !dbg !1191 call void @llvm.memcpy.p0i8.p0i8.i64(i8* %24, i8* %23, i64 16, i32 8, i1 false), !dbg !1191 %25 = bitcast %Point* %blah to i8*, !dbg !1192 call void @llvm.memcpy.p0i8.p0i8.i64(i8* %25, i8* bitcast (%Point* @60 to i8*), i64 8, i32 4, i1 false), !dbg !1192 call void @llvm.dbg.declare(metadata %Point* %blah, metadata !1181, metadata !DIExpression()), !dbg !1192 %26 = call i8 @llvm.coro.suspend(token none, i1 false), !dbg !1193 switch i8 %26, label %Suspend [ i8 0, label %SuspendResume i8 1, label %SuspendCleanup ], !dbg !1193 SuspendCleanup: ; preds = %AllocOk br label %FinalCleanup, !dbg !1193 SuspendResume: ; preds = %AllocOk %27 = getelementptr inbounds %Point, %Point* %blah, i32 0, i32 0, !dbg !1194 %28 = load i32, i32* %27, align 4, !dbg !1194 %29 = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %28, i32 1), !dbg !1195 %30 = extractvalue { i32, i1 } %29, 0, !dbg !1195 %31 = extractvalue { i32, i1 } %29, 1, !dbg !1195 br i1 %31, label %OverflowFail, label %OverflowOk, !dbg !1195 CoroEarlyFinal: ; preds = %OverflowOk5 %32 = call i8 @llvm.coro.suspend(token none, i1 true), !dbg !1191 switch i8 %32, label %Suspend [ i8 0, label %InvalidResume i8 1, label %FinalCleanup ], !dbg !1191 Suspend: ; preds = %Resume, %CheckFree, %CoroEarlyFinal, %AllocOk %33 = call i1 @llvm.coro.end(i8* null, i1 false), !dbg !1191 ret i8* %14, !dbg !1191 InvalidResume: ; preds = %CoroEarlyFinal tail call fastcc void @panic(%"[]u8"* @49, %StackTrace* null), !dbg !1191 unreachable, !dbg !1191 CoroNormalFinal: ; preds = %OverflowOk5 %34 = getelementptr inbounds %"AsyncFramePromise(void)", %"AsyncFramePromise(void)"* %_anon, i32 0, i32 1, !dbg !1191 br label %CheckFree, !dbg !1191 FinalCleanup: ; preds %CoroEarlyFinal, %SuspendCleanup br label %CheckFree, !dbg !1191 CheckFree: ; preds = %FinalCleanup, %CoroNormalFinal %35 = phi i1 [ false, %FinalCleanup ], [ true, %CoroNormalFinal ], !dbg !1191 %36 = load %Allocator*, %Allocator** %_anon3, align 8, !dbg !1191 %37 = getelementptr inbounds %Allocator, %Allocator* %36, i32 0, i32 2, !dbg !1191 %38 = load void (%Allocator*, %"[]u8"*)*, void (%Allocator*, %"[]u8"*)** %37, align 8, !dbg !1191 %39 = call i8* @llvm.coro.free(token %8, i8* %14), !dbg !1191 store i8* %39, i8** %4, align 8, !dbg !1191 %40 = load i64, i64* %_anon2, align 8, !dbg !1191 %41 = load i8*, i8** %4, align 8, !dbg !1191 %42 = getelementptr inbounds %"[]u8", %"[]u8"* %5, i32 0, i32 0, !dbg !1191 %43 = getelementptr inbounds i8, i8* %41, i64 0, !dbg !1191 store i8* %43, i8** %42, align 8, !dbg !1191 %44 = getelementptr inbounds %"[]u8", %"[]u8"* %5, i32 0, i32 1, !dbg !1191 %45 = sub nsw i64 %40, 0, !dbg !1191 store i64 %45, i64* %44, align 8, !dbg !1191 call fastcc void %38(%Allocator* %36, %"[]u8"* %5), !dbg !1191 br i1 %35, label %Resume, label %Suspend, !dbg !1191 Resume: ; preds = %CheckFree %46 = load i8*, i8** %_anon1, align 8, !dbg !1191 %47 = load i8*, i8** %_anon1, align 8, !dbg !1191 call void @llvm.coro.resume(i8* %47), !dbg !1191 br label %Suspend, !dbg !1191 OverflowFail: ; preds = %SuspendResume tail call fastcc void @panic(%"[]u8"* @41, %StackTrace* null), !dbg !1195 unreachable, !dbg !1195 OverflowOk: ; preds = %SuspendResume store i32 %30, i32* %bar, align 4, !dbg !1196 call void @llvm.dbg.declare(metadata i32* %bar, metadata !1187, metadata !DIExpression()), !dbg !1196 %48 = load i32, i32* %bar, align 4, !dbg !1197 %49 = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %48, i32 1), !dbg !1198 %50 = extractvalue { i32, i1 } %49, 0, !dbg !1198 %51 = extractvalue { i32, i1 } %49, 1, !dbg !1198 br i1 %51, label %OverflowFail4, label %OverflowOk5, !dbg !1198 OverflowFail4: ; preds = %OverflowOk tail call fastcc void @panic(%"[]u8"* @41, %StackTrace* null), !dbg !1198 unreachable, !dbg !1198 OverflowOk5: ; preds = %OverflowOk store i32 %50, i32* %baz, align 4, !dbg !1199 call void @llvm.dbg.declare(metadata i32* %baz, metadata !1189, metadata !DIExpression()), !dbg !1199 %52 = bitcast i8** %15 to i64*, !dbg !1191 %53 = ptrtoint i8* %14 to i64, !dbg !1191 %54 = atomicrmw xchg i64* %52, i64 %53 seq_cst, !dbg !1191 %55 = inttoptr i64 %54 to i8*, !dbg !1191 store i8* %55, i8** %_anon1, align 8, !dbg !1191 %56 = icmp ne i8* %55, null, !dbg !1191 br i1 %56, label %CoroNormalFinal, label %CoroEarlyFinal, !dbg !1191 } -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20180627/195f9422/attachment.html>
David Blaikie via llvm-dev
2018-Jun-28 22:47 UTC
[llvm-dev] can debug info for coroutines be improved?
[+debug info cabal & Gor for coroutines] Might be a bit easier with a reproduction using C++ coroutines (easier for the rest of us to reproduce/etc). At first glance (not knowing a great deal about coroutines, not having looked at the IR, etc) - it doesn't look like it's /wrong/ per se, the two variables are still there, but without locations (so it's not like LLVM lost the notion that there were those two variables in that scope). I doubt there's any fundamental limitation & likely this can be fixed/improved. Happy to dive in/point you in the right direction if you'd like to work on fixing that. Not sure about the anon variables - where they come from/why they're there. On Wed, Jun 27, 2018 at 10:42 AM Andrew Kelley via llvm-dev < llvm-dev at lists.llvm.org> wrote:> I'm going to show the same function, first normally, and then as a > coroutine, and show how gdb can see the variable when it's a normal > function, but not when it's a coroutine. I'd like to understand if this can > be improved. > > I'm trying to debug a real world problem, but the lack of debug info on > variables in coroutines is making it difficult. Should I file a bug? Is > this a fundamental limitation? Is anyone else interested in this? > > Here is a normal function source (in zig): > > const Point = struct { > x: i32, > y: i32, > }; > > pub fn main() void { > testAsyncSeq(); > } > > fn testAsyncSeq() void { > var blah = Point{ .x = 12, .y = 34 }; > var bar = blah.x + 1; > var baz = bar + 1; > } > > when we step through `testAsyncSeq` in gdb, the `blah` variable is visible: > > Breakpoint 1, testAsyncSeq () at > /home/andy/downloads/zig/build/test2.zig:13 > 13 var blah = Point{ .x = 12, .y = 34 }; > (gdb) info locals > blah = { > x = 0, > y = 0 > } > (gdb) next > 14 var bar = blah.x + 1; > (gdb) info locals > bar = 0 > blah = { > x = 12, > y = 34 > } > (gdb) quit > > > However if I take advantage of LLVM's coroutines, the debug info seems to > not be correct: > > const std = @import("std"); > > const Point = struct { > x: i32, > y: i32, > }; > > pub fn main() !void { > const p = try async<std.debug.global_allocator> testAsyncSeq(); > resume p; > cancel p; > } > > async fn testAsyncSeq() void { > var blah = Point{ .x = 12, .y = 34 }; > suspend; > var bar = blah.x + 1; > var baz = bar + 1; > } > > Now in gdb the `blah` variable is not visible: > > Breakpoint 1, testAsyncSeq () at /home/andy/downloads/zig/build/test.zig:14 > 14 async fn testAsyncSeq() void { > (gdb) next > 15 var blah = Point{ .x = 12, .y = 34 }; > (gdb) > 16 suspend; > (gdb) info locals > bar = <optimized out> > blah = <optimized out> > _anon = 0x0 > _anon = 368 > _anon = 0x23a028 <global_fixed_allocator> > _anon = <optimized out> > (gdb) quit > > Here is the LLVM IR generated for the function in the coroutines case. You > can see that it has full debug info and @llvm.dbg.declare just like in the > "normal function" case. > > ; Function Attrs: nobuiltin noinline nounwind optnone > define internal fastcc i8* @testAsyncSeq(%StackTrace* nonnull, > %Allocator*, i16*) unnamed_addr #5 !dbg !1163 { > Entry: > %3 = alloca %"[]usize", align 8 > %4 = alloca i8*, align 8 > %5 = alloca %"[]u8", align 8 > %_anon = alloca %"AsyncFramePromise(void)", align 8 > %_anon1 = alloca i8*, align 8 > %_anon2 = alloca i64, align 8 > %_anon3 = alloca %Allocator*, align 8 > %blah = alloca %Point, align 4 > %bar = alloca i32, align 4 > %baz = alloca i32, align 4 > %6 = bitcast %"AsyncFramePromise(void)"* %_anon to i8*, !dbg !1191 > call void @llvm.memcpy.p0i8.p0i8.i64(i8* %6, i8* bitcast > (%"AsyncFramePromise(void)"* @57 to i8*), i64 280, i32 8, i1 false), !dbg > !1191 > call void @llvm.dbg.declare(metadata %"AsyncFramePromise(void)"* %_anon, > metadata !1168, metadata !DIExpression()), !dbg !1191 > store i8* null, i8** %_anon1, align 8, !dbg !1191 > call void @llvm.dbg.declare(metadata i8** %_anon1, metadata !1178, > metadata !DIExpression()), !dbg !1191 > %7 = bitcast %"AsyncFramePromise(void)"* %_anon to i8*, !dbg !1191 > %8 = call token @llvm.coro.id(i32 16, i8* %7, i8* null, i8* null), !dbg > !1191 > %9 = call i64 @llvm.coro.size.i64(), !dbg !1191 > store i64 %9, i64* %_anon2, align 8, !dbg !1191 > call void @llvm.dbg.declare(metadata i64* %_anon2, metadata !1179, > metadata !DIExpression()), !dbg !1191 > store %Allocator* %1, %Allocator** %_anon3, align 8, !dbg !1191 > call void @llvm.dbg.declare(metadata %Allocator** %_anon3, metadata > !1180, metadata !DIExpression()), !dbg !1191 > %10 = getelementptr inbounds %Allocator, %Allocator* %1, i32 0, i32 0, > !dbg !1191 > %11 = load void ({ i16, %"[]u8" }*, %StackTrace*, %Allocator*, i64, > i29)*, void ({ i16, %"[]u8" }*, %StackTrace*, %Allocator*, i64, i29)** %10, > align 8, !dbg !1191 > %12 = call fastcc i8* @__zig_coro_alloc_helper(void ({ i16, %"[]u8" }*, > %StackTrace*, %Allocator*, i64, i29)* %11, %StackTrace* %0, %Allocator* %1, > i16* %2, i64 %9), !dbg !1191 > %13 = icmp ne i8* %12, null, !dbg !1191 > br i1 %13, label %AllocOk, label %AllocError, !dbg !1191 > > AllocError: ; preds = %Entry > ret i8* undef, !dbg !1191 > > AllocOk: ; preds = %Entry > %14 = call i8* @llvm.coro.begin(token %8, i8* %12), !dbg !1191 > %15 = getelementptr inbounds %"AsyncFramePromise(void)", > %"AsyncFramePromise(void)"* %_anon, i32 0, i32 0, !dbg !1191 > store i8* null, i8** %15, align 8, !dbg !1191 > %16 = getelementptr inbounds %"AsyncFramePromise(void)", > %"AsyncFramePromise(void)"* %_anon, i32 0, i32 3, !dbg !1191 > %17 = getelementptr inbounds %"AsyncFramePromise(void)", > %"AsyncFramePromise(void)"* %_anon, i32 0, i32 2, !dbg !1191 > %18 = getelementptr inbounds %StackTrace, %StackTrace* %17, i32 0, i32 > 0, !dbg !1191 > store i64 0, i64* %18, align 8, !dbg !1191 > %19 = getelementptr inbounds %StackTrace, %StackTrace* %17, i32 0, i32 > 1, !dbg !1191 > %20 = getelementptr inbounds %"[]usize", %"[]usize"* %3, i32 0, i32 0, > !dbg !1191 > %21 = getelementptr inbounds [30 x i64], [30 x i64]* %16, i64 0, i64 0, > !dbg !1191 > store i64* %21, i64** %20, align 8, !dbg !1191 > %22 = getelementptr inbounds %"[]usize", %"[]usize"* %3, i32 0, i32 1, > !dbg !1191 > store i64 30, i64* %22, align 8, !dbg !1191 > %23 = bitcast %"[]usize"* %3 to i8*, !dbg !1191 > %24 = bitcast %"[]usize"* %19 to i8*, !dbg !1191 > call void @llvm.memcpy.p0i8.p0i8.i64(i8* %24, i8* %23, i64 16, i32 8, i1 > false), !dbg !1191 > %25 = bitcast %Point* %blah to i8*, !dbg !1192 > call void @llvm.memcpy.p0i8.p0i8.i64(i8* %25, i8* bitcast (%Point* @60 > to i8*), i64 8, i32 4, i1 false), !dbg !1192 > call void @llvm.dbg.declare(metadata %Point* %blah, metadata !1181, > metadata !DIExpression()), !dbg !1192 > %26 = call i8 @llvm.coro.suspend(token none, i1 false), !dbg !1193 > switch i8 %26, label %Suspend [ > i8 0, label %SuspendResume > i8 1, label %SuspendCleanup > ], !dbg !1193 > > SuspendCleanup: ; preds = %AllocOk > br label %FinalCleanup, !dbg !1193 > > SuspendResume: ; preds = %AllocOk > %27 = getelementptr inbounds %Point, %Point* %blah, i32 0, i32 0, !dbg > !1194 > %28 = load i32, i32* %27, align 4, !dbg !1194 > %29 = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %28, i32 1), > !dbg !1195 > %30 = extractvalue { i32, i1 } %29, 0, !dbg !1195 > %31 = extractvalue { i32, i1 } %29, 1, !dbg !1195 > br i1 %31, label %OverflowFail, label %OverflowOk, !dbg !1195 > > CoroEarlyFinal: ; preds = %OverflowOk5 > %32 = call i8 @llvm.coro.suspend(token none, i1 true), !dbg !1191 > switch i8 %32, label %Suspend [ > i8 0, label %InvalidResume > i8 1, label %FinalCleanup > ], !dbg !1191 > > Suspend: ; preds = %Resume, > %CheckFree, %CoroEarlyFinal, %AllocOk > %33 = call i1 @llvm.coro.end(i8* null, i1 false), !dbg !1191 > ret i8* %14, !dbg !1191 > > InvalidResume: ; preds = %CoroEarlyFinal > tail call fastcc void @panic(%"[]u8"* @49, %StackTrace* null), !dbg !1191 > unreachable, !dbg !1191 > > CoroNormalFinal: ; preds = %OverflowOk5 > %34 = getelementptr inbounds %"AsyncFramePromise(void)", > %"AsyncFramePromise(void)"* %_anon, i32 0, i32 1, !dbg !1191 > br label %CheckFree, !dbg !1191 > > FinalCleanup: ; preds > %CoroEarlyFinal, %SuspendCleanup > br label %CheckFree, !dbg !1191 > > CheckFree: ; preds = %FinalCleanup, > %CoroNormalFinal > %35 = phi i1 [ false, %FinalCleanup ], [ true, %CoroNormalFinal ], !dbg > !1191 > %36 = load %Allocator*, %Allocator** %_anon3, align 8, !dbg !1191 > %37 = getelementptr inbounds %Allocator, %Allocator* %36, i32 0, i32 2, > !dbg !1191 > %38 = load void (%Allocator*, %"[]u8"*)*, void (%Allocator*, %"[]u8"*)** > %37, align 8, !dbg !1191 > %39 = call i8* @llvm.coro.free(token %8, i8* %14), !dbg !1191 > store i8* %39, i8** %4, align 8, !dbg !1191 > %40 = load i64, i64* %_anon2, align 8, !dbg !1191 > %41 = load i8*, i8** %4, align 8, !dbg !1191 > %42 = getelementptr inbounds %"[]u8", %"[]u8"* %5, i32 0, i32 0, !dbg > !1191 > %43 = getelementptr inbounds i8, i8* %41, i64 0, !dbg !1191 > store i8* %43, i8** %42, align 8, !dbg !1191 > %44 = getelementptr inbounds %"[]u8", %"[]u8"* %5, i32 0, i32 1, !dbg > !1191 > %45 = sub nsw i64 %40, 0, !dbg !1191 > store i64 %45, i64* %44, align 8, !dbg !1191 > call fastcc void %38(%Allocator* %36, %"[]u8"* %5), !dbg !1191 > br i1 %35, label %Resume, label %Suspend, !dbg !1191 > > Resume: ; preds = %CheckFree > %46 = load i8*, i8** %_anon1, align 8, !dbg !1191 > %47 = load i8*, i8** %_anon1, align 8, !dbg !1191 > call void @llvm.coro.resume(i8* %47), !dbg !1191 > br label %Suspend, !dbg !1191 > > OverflowFail: ; preds = %SuspendResume > tail call fastcc void @panic(%"[]u8"* @41, %StackTrace* null), !dbg !1195 > unreachable, !dbg !1195 > > OverflowOk: ; preds = %SuspendResume > store i32 %30, i32* %bar, align 4, !dbg !1196 > call void @llvm.dbg.declare(metadata i32* %bar, metadata !1187, metadata > !DIExpression()), !dbg !1196 > %48 = load i32, i32* %bar, align 4, !dbg !1197 > %49 = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %48, i32 1), > !dbg !1198 > %50 = extractvalue { i32, i1 } %49, 0, !dbg !1198 > %51 = extractvalue { i32, i1 } %49, 1, !dbg !1198 > br i1 %51, label %OverflowFail4, label %OverflowOk5, !dbg !1198 > > OverflowFail4: ; preds = %OverflowOk > tail call fastcc void @panic(%"[]u8"* @41, %StackTrace* null), !dbg !1198 > unreachable, !dbg !1198 > > OverflowOk5: ; preds = %OverflowOk > store i32 %50, i32* %baz, align 4, !dbg !1199 > call void @llvm.dbg.declare(metadata i32* %baz, metadata !1189, metadata > !DIExpression()), !dbg !1199 > %52 = bitcast i8** %15 to i64*, !dbg !1191 > %53 = ptrtoint i8* %14 to i64, !dbg !1191 > %54 = atomicrmw xchg i64* %52, i64 %53 seq_cst, !dbg !1191 > %55 = inttoptr i64 %54 to i8*, !dbg !1191 > store i8* %55, i8** %_anon1, align 8, !dbg !1191 > %56 = icmp ne i8* %55, null, !dbg !1191 > br i1 %56, label %CoroNormalFinal, label %CoroEarlyFinal, !dbg !1191 > } > _______________________________________________ > LLVM Developers mailing list > llvm-dev at lists.llvm.org > http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20180628/0a110eaf/attachment.html>
Jonas Devlieghere via llvm-dev
2018-Jun-29 14:53 UTC
[llvm-dev] can debug info for coroutines be improved?
> On Jun 28, 2018, at 11:47 PM, David Blaikie <dblaikie at gmail.com> wrote: > > [+debug info cabal & Gor for coroutines] > > Might be a bit easier with a reproduction using C++ coroutines (easier for the rest of us to reproduce/etc).+1 I wanted to look at the DWARF and tried to compile your samples with zig 0.2.0 (from Homebrew) but I got this error: /private/tmp/b.zig:9:24: error: invalid token: '<' const p = try async<std.debug.global_allocator> testAsyncSeq();> At first glance (not knowing a great deal about coroutines, not having looked at the IR, etc) - it doesn't look like it's /wrong/ per se, the two variables are still there, but without locations (so it's not like LLVM lost the notion that there were those two variables in that scope). I doubt there's any fundamental limitation & likely this can be fixed/improved. Happy to dive in/point you in the right direction if you'd like to work on fixing that. > > Not sure about the anon variables - where they come from/why they're there. > > On Wed, Jun 27, 2018 at 10:42 AM Andrew Kelley via llvm-dev <llvm-dev at lists.llvm.org> wrote: > I'm going to show the same function, first normally, and then as a coroutine, and show how gdb can see the variable when it's a normal function, but not when it's a coroutine. I'd like to understand if this can be improved. > > I'm trying to debug a real world problem, but the lack of debug info on variables in coroutines is making it difficult. Should I file a bug? Is this a fundamental limitation? Is anyone else interested in this? > > Here is a normal function source (in zig): > > const Point = struct { > x: i32, > y: i32, > }; > > pub fn main() void { > testAsyncSeq(); > } > > fn testAsyncSeq() void { > var blah = Point{ .x = 12, .y = 34 }; > var bar = blah.x + 1; > var baz = bar + 1; > } > > when we step through `testAsyncSeq` in gdb, the `blah` variable is visible: > > Breakpoint 1, testAsyncSeq () at /home/andy/downloads/zig/build/test2.zig:13 > 13 var blah = Point{ .x = 12, .y = 34 }; > (gdb) info locals > blah = { > x = 0, > y = 0 > } > (gdb) next > 14 var bar = blah.x + 1; > (gdb) info locals > bar = 0 > blah = { > x = 12, > y = 34 > } > (gdb) quit > > > However if I take advantage of LLVM's coroutines, the debug info seems to not be correct: > > const std = @import("std"); > > const Point = struct { > x: i32, > y: i32, > }; > > pub fn main() !void { > const p = try async<std.debug.global_allocator> testAsyncSeq(); > resume p; > cancel p; > } > > async fn testAsyncSeq() void { > var blah = Point{ .x = 12, .y = 34 }; > suspend; > var bar = blah.x + 1; > var baz = bar + 1; > } > > Now in gdb the `blah` variable is not visible: > > Breakpoint 1, testAsyncSeq () at /home/andy/downloads/zig/build/test.zig:14 > 14 async fn testAsyncSeq() void { > (gdb) next > 15 var blah = Point{ .x = 12, .y = 34 }; > (gdb) > 16 suspend; > (gdb) info locals > bar = <optimized out> > blah = <optimized out> > _anon = 0x0 > _anon = 368 > _anon = 0x23a028 <global_fixed_allocator> > _anon = <optimized out> > (gdb) quit > > Here is the LLVM IR generated for the function in the coroutines case. You can see that it has full debug info and @llvm.dbg.declare just like in the "normal function" case. > > ; Function Attrs: nobuiltin noinline nounwind optnone > define internal fastcc i8* @testAsyncSeq(%StackTrace* nonnull, %Allocator*, i16*) unnamed_addr #5 !dbg !1163 { > Entry: > %3 = alloca %"[]usize", align 8 > %4 = alloca i8*, align 8 > %5 = alloca %"[]u8", align 8 > %_anon = alloca %"AsyncFramePromise(void)", align 8 > %_anon1 = alloca i8*, align 8 > %_anon2 = alloca i64, align 8 > %_anon3 = alloca %Allocator*, align 8 > %blah = alloca %Point, align 4 > %bar = alloca i32, align 4 > %baz = alloca i32, align 4 > %6 = bitcast %"AsyncFramePromise(void)"* %_anon to i8*, !dbg !1191 > call void @llvm.memcpy.p0i8.p0i8.i64(i8* %6, i8* bitcast (%"AsyncFramePromise(void)"* @57 to i8*), i64 280, i32 8, i1 false), !dbg !1191 > call void @llvm.dbg.declare(metadata %"AsyncFramePromise(void)"* %_anon, metadata !1168, metadata !DIExpression()), !dbg !1191 > store i8* null, i8** %_anon1, align 8, !dbg !1191 > call void @llvm.dbg.declare(metadata i8** %_anon1, metadata !1178, metadata !DIExpression()), !dbg !1191 > %7 = bitcast %"AsyncFramePromise(void)"* %_anon to i8*, !dbg !1191 > %8 = call token @llvm.coro.id(i32 16, i8* %7, i8* null, i8* null), !dbg !1191 > %9 = call i64 @llvm.coro.size.i64(), !dbg !1191 > store i64 %9, i64* %_anon2, align 8, !dbg !1191 > call void @llvm.dbg.declare(metadata i64* %_anon2, metadata !1179, metadata !DIExpression()), !dbg !1191 > store %Allocator* %1, %Allocator** %_anon3, align 8, !dbg !1191 > call void @llvm.dbg.declare(metadata %Allocator** %_anon3, metadata !1180, metadata !DIExpression()), !dbg !1191 > %10 = getelementptr inbounds %Allocator, %Allocator* %1, i32 0, i32 0, !dbg !1191 > %11 = load void ({ i16, %"[]u8" }*, %StackTrace*, %Allocator*, i64, i29)*, void ({ i16, %"[]u8" }*, %StackTrace*, %Allocator*, i64, i29)** %10, align 8, !dbg !1191 > %12 = call fastcc i8* @__zig_coro_alloc_helper(void ({ i16, %"[]u8" }*, %StackTrace*, %Allocator*, i64, i29)* %11, %StackTrace* %0, %Allocator* %1, i16* %2, i64 %9), !dbg !1191 > %13 = icmp ne i8* %12, null, !dbg !1191 > br i1 %13, label %AllocOk, label %AllocError, !dbg !1191 > > AllocError: ; preds = %Entry > ret i8* undef, !dbg !1191 > > AllocOk: ; preds = %Entry > %14 = call i8* @llvm.coro.begin(token %8, i8* %12), !dbg !1191 > %15 = getelementptr inbounds %"AsyncFramePromise(void)", %"AsyncFramePromise(void)"* %_anon, i32 0, i32 0, !dbg !1191 > store i8* null, i8** %15, align 8, !dbg !1191 > %16 = getelementptr inbounds %"AsyncFramePromise(void)", %"AsyncFramePromise(void)"* %_anon, i32 0, i32 3, !dbg !1191 > %17 = getelementptr inbounds %"AsyncFramePromise(void)", %"AsyncFramePromise(void)"* %_anon, i32 0, i32 2, !dbg !1191 > %18 = getelementptr inbounds %StackTrace, %StackTrace* %17, i32 0, i32 0, !dbg !1191 > store i64 0, i64* %18, align 8, !dbg !1191 > %19 = getelementptr inbounds %StackTrace, %StackTrace* %17, i32 0, i32 1, !dbg !1191 > %20 = getelementptr inbounds %"[]usize", %"[]usize"* %3, i32 0, i32 0, !dbg !1191 > %21 = getelementptr inbounds [30 x i64], [30 x i64]* %16, i64 0, i64 0, !dbg !1191 > store i64* %21, i64** %20, align 8, !dbg !1191 > %22 = getelementptr inbounds %"[]usize", %"[]usize"* %3, i32 0, i32 1, !dbg !1191 > store i64 30, i64* %22, align 8, !dbg !1191 > %23 = bitcast %"[]usize"* %3 to i8*, !dbg !1191 > %24 = bitcast %"[]usize"* %19 to i8*, !dbg !1191 > call void @llvm.memcpy.p0i8.p0i8.i64(i8* %24, i8* %23, i64 16, i32 8, i1 false), !dbg !1191 > %25 = bitcast %Point* %blah to i8*, !dbg !1192 > call void @llvm.memcpy.p0i8.p0i8.i64(i8* %25, i8* bitcast (%Point* @60 to i8*), i64 8, i32 4, i1 false), !dbg !1192 > call void @llvm.dbg.declare(metadata %Point* %blah, metadata !1181, metadata !DIExpression()), !dbg !1192 > %26 = call i8 @llvm.coro.suspend(token none, i1 false), !dbg !1193 > switch i8 %26, label %Suspend [ > i8 0, label %SuspendResume > i8 1, label %SuspendCleanup > ], !dbg !1193 > > SuspendCleanup: ; preds = %AllocOk > br label %FinalCleanup, !dbg !1193 > > SuspendResume: ; preds = %AllocOk > %27 = getelementptr inbounds %Point, %Point* %blah, i32 0, i32 0, !dbg !1194 > %28 = load i32, i32* %27, align 4, !dbg !1194 > %29 = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %28, i32 1), !dbg !1195 > %30 = extractvalue { i32, i1 } %29, 0, !dbg !1195 > %31 = extractvalue { i32, i1 } %29, 1, !dbg !1195 > br i1 %31, label %OverflowFail, label %OverflowOk, !dbg !1195 > > CoroEarlyFinal: ; preds = %OverflowOk5 > %32 = call i8 @llvm.coro.suspend(token none, i1 true), !dbg !1191 > switch i8 %32, label %Suspend [ > i8 0, label %InvalidResume > i8 1, label %FinalCleanup > ], !dbg !1191 > > Suspend: ; preds = %Resume, %CheckFree, %CoroEarlyFinal, %AllocOk > %33 = call i1 @llvm.coro.end(i8* null, i1 false), !dbg !1191 > ret i8* %14, !dbg !1191 > > InvalidResume: ; preds = %CoroEarlyFinal > tail call fastcc void @panic(%"[]u8"* @49, %StackTrace* null), !dbg !1191 > unreachable, !dbg !1191 > > CoroNormalFinal: ; preds = %OverflowOk5 > %34 = getelementptr inbounds %"AsyncFramePromise(void)", %"AsyncFramePromise(void)"* %_anon, i32 0, i32 1, !dbg !1191 > br label %CheckFree, !dbg !1191 > > FinalCleanup: ; preds = %CoroEarlyFinal, %SuspendCleanup > br label %CheckFree, !dbg !1191 > > CheckFree: ; preds = %FinalCleanup, %CoroNormalFinal > %35 = phi i1 [ false, %FinalCleanup ], [ true, %CoroNormalFinal ], !dbg !1191 > %36 = load %Allocator*, %Allocator** %_anon3, align 8, !dbg !1191 > %37 = getelementptr inbounds %Allocator, %Allocator* %36, i32 0, i32 2, !dbg !1191 > %38 = load void (%Allocator*, %"[]u8"*)*, void (%Allocator*, %"[]u8"*)** %37, align 8, !dbg !1191 > %39 = call i8* @llvm.coro.free(token %8, i8* %14), !dbg !1191 > store i8* %39, i8** %4, align 8, !dbg !1191 > %40 = load i64, i64* %_anon2, align 8, !dbg !1191 > %41 = load i8*, i8** %4, align 8, !dbg !1191 > %42 = getelementptr inbounds %"[]u8", %"[]u8"* %5, i32 0, i32 0, !dbg !1191 > %43 = getelementptr inbounds i8, i8* %41, i64 0, !dbg !1191 > store i8* %43, i8** %42, align 8, !dbg !1191 > %44 = getelementptr inbounds %"[]u8", %"[]u8"* %5, i32 0, i32 1, !dbg !1191 > %45 = sub nsw i64 %40, 0, !dbg !1191 > store i64 %45, i64* %44, align 8, !dbg !1191 > call fastcc void %38(%Allocator* %36, %"[]u8"* %5), !dbg !1191 > br i1 %35, label %Resume, label %Suspend, !dbg !1191 > > Resume: ; preds = %CheckFree > %46 = load i8*, i8** %_anon1, align 8, !dbg !1191 > %47 = load i8*, i8** %_anon1, align 8, !dbg !1191 > call void @llvm.coro.resume(i8* %47), !dbg !1191 > br label %Suspend, !dbg !1191 > > OverflowFail: ; preds = %SuspendResume > tail call fastcc void @panic(%"[]u8"* @41, %StackTrace* null), !dbg !1195 > unreachable, !dbg !1195 > > OverflowOk: ; preds = %SuspendResume > store i32 %30, i32* %bar, align 4, !dbg !1196 > call void @llvm.dbg.declare(metadata i32* %bar, metadata !1187, metadata !DIExpression()), !dbg !1196 > %48 = load i32, i32* %bar, align 4, !dbg !1197 > %49 = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %48, i32 1), !dbg !1198 > %50 = extractvalue { i32, i1 } %49, 0, !dbg !1198 > %51 = extractvalue { i32, i1 } %49, 1, !dbg !1198 > br i1 %51, label %OverflowFail4, label %OverflowOk5, !dbg !1198 > > OverflowFail4: ; preds = %OverflowOk > tail call fastcc void @panic(%"[]u8"* @41, %StackTrace* null), !dbg !1198 > unreachable, !dbg !1198 > > OverflowOk5: ; preds = %OverflowOk > store i32 %50, i32* %baz, align 4, !dbg !1199 > call void @llvm.dbg.declare(metadata i32* %baz, metadata !1189, metadata !DIExpression()), !dbg !1199 > %52 = bitcast i8** %15 to i64*, !dbg !1191 > %53 = ptrtoint i8* %14 to i64, !dbg !1191 > %54 = atomicrmw xchg i64* %52, i64 %53 seq_cst, !dbg !1191 > %55 = inttoptr i64 %54 to i8*, !dbg !1191 > store i8* %55, i8** %_anon1, align 8, !dbg !1191 > %56 = icmp ne i8* %55, null, !dbg !1191 > br i1 %56, label %CoroNormalFinal, label %CoroEarlyFinal, !dbg !1191 > } > _______________________________________________ > LLVM Developers mailing list > llvm-dev at lists.llvm.org > http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev