[llvm-dev] can debug info for coroutines be improved?
Andrew Kelley via llvm-dev
llvm-dev at lists.llvm.org
Wed Jun 27 10:41:17 PDT 2018
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>
More information about the llvm-dev
mailing list