[llvm-dev] can debug info for coroutines be improved?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

[llvm-dev] can debug info for coroutines be improved?

Muhui Jiang via llvm-dev
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
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
Reply | Threaded
Open this post in threaded view
|

Re: [llvm-dev] can debug info for coroutines be improved?

Muhui Jiang via llvm-dev
[+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 <[hidden email]> 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
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev

_______________________________________________
LLVM Developers mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
Reply | Threaded
Open this post in threaded view
|

Re: [llvm-dev] can debug info for coroutines be improved?

Muhui Jiang via llvm-dev

> On Jun 28, 2018, at 11:47 PM, David Blaikie <[hidden email]> 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 <[hidden email]> 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
> [hidden email]
> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev

_______________________________________________
LLVM Developers mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev