authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-05-01 16:34:05+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-05-09 18:51:46+02:00
logf76027220042d965278184088f1f04b6c8430bcb
tree93e00b10ed446fafd785df7f111eb9431de8e6d3
parentd46cdb539620a2f3647507ffed519908a1c31647

wasm: Debug info for lines + pro/epilogue

Maps lines and columns between wasm bytecode and Zig source code. While this supports prologue and epilogue information, we need to add support for performing relocations as the offsets are relative to the code section, which means we must relocate it according to the atom offset's offset while keeping function count in mind as well (due to leb128 encoding).

3 files changed, 82 insertions(+), 0 deletions(-)

src/arch/wasm/CodeGen.zig+8
...@@ -882,6 +882,8 @@ fn genFunc(self: *Self) InnerError!void {...@@ -882,6 +882,8 @@ fn genFunc(self: *Self) InnerError!void {
882 self.args = cc_result.args;882 self.args = cc_result.args;
883 self.return_value = cc_result.return_value;883 self.return_value = cc_result.return_value;
884884
885 try self.addTag(.dbg_prologue_end);
886
885 // Generate MIR for function body887 // Generate MIR for function body
886 try self.genBody(self.air.getMainBody());888 try self.genBody(self.air.getMainBody());
887 // In case we have a return value, but the last instruction is a noreturn (such as a while loop)889 // In case we have a return value, but the last instruction is a noreturn (such as a while loop)
...@@ -896,6 +898,8 @@ fn genFunc(self: *Self) InnerError!void {...@@ -896,6 +898,8 @@ fn genFunc(self: *Self) InnerError!void {
896 // End of function body898 // End of function body
897 try self.addTag(.end);899 try self.addTag(.end);
898900
901 try self.addTag(.dbg_epilogue_begin);
902
899 // check if we have to initialize and allocate anything into the stack frame.903 // check if we have to initialize and allocate anything into the stack frame.
900 // If so, create enough stack space and insert the instructions at the front of the list.904 // If so, create enough stack space and insert the instructions at the front of the list.
901 if (self.stack_size > 0) {905 if (self.stack_size > 0) {
...@@ -942,6 +946,10 @@ fn genFunc(self: *Self) InnerError!void {...@@ -942,6 +946,10 @@ fn genFunc(self: *Self) InnerError!void {
942 .code = self.code,946 .code = self.code,
943 .locals = self.locals.items,947 .locals = self.locals.items,
944 .decl = self.decl,948 .decl = self.decl,
949 .dbg_output = self.debug_output,
950 .prev_di_line = 0,
951 .prev_di_column = 0,
952 .prev_di_offset = 0,
945 };953 };
946954
947 emit.emitMir() catch |err| switch (err) {955 emit.emitMir() catch |err| switch (err) {
src/arch/wasm/Emit.zig+55
...@@ -6,6 +6,7 @@ const std = @import("std");...@@ -6,6 +6,7 @@ const std = @import("std");
6const Mir = @import("Mir.zig");6const Mir = @import("Mir.zig");
7const link = @import("../../link.zig");7const link = @import("../../link.zig");
8const Module = @import("../../Module.zig");8const Module = @import("../../Module.zig");
9const codegen = @import("../../codegen.zig");
9const leb128 = std.leb;10const leb128 = std.leb;
1011
11/// Contains our list of instructions12/// Contains our list of instructions
...@@ -22,6 +23,16 @@ locals: []const u8,...@@ -22,6 +23,16 @@ locals: []const u8,
22/// The declaration that code is being generated for.23/// The declaration that code is being generated for.
23decl: *Module.Decl,24decl: *Module.Decl,
2425
26// Debug information
27/// Holds the debug information for this emission
28dbg_output: codegen.DebugInfoOutput,
29/// Previous debug info line
30prev_di_line: u32,
31/// Previous debug info column
32prev_di_column: u32,
33/// Previous offset relative to code section
34prev_di_offset: u32,
35
25const InnerError = error{36const InnerError = error{
26 OutOfMemory,37 OutOfMemory,
27 EmitFail,38 EmitFail,
...@@ -40,6 +51,10 @@ pub fn emitMir(emit: *Emit) InnerError!void {...@@ -40,6 +51,10 @@ pub fn emitMir(emit: *Emit) InnerError!void {
40 .block => try emit.emitBlock(tag, inst),51 .block => try emit.emitBlock(tag, inst),
41 .loop => try emit.emitBlock(tag, inst),52 .loop => try emit.emitBlock(tag, inst),
4253
54 .dbg_line => try emit.emitDbgLine(inst),
55 .dbg_epilogue_begin => try emit.emitDbgEpilogueBegin(),
56 .dbg_prologue_end => try emit.emitDbgPrologueEnd(),
57
43 // branch instructions58 // branch instructions
44 .br_if => try emit.emitLabel(tag, inst),59 .br_if => try emit.emitLabel(tag, inst),
45 .br_table => try emit.emitBrTable(inst),60 .br_table => try emit.emitBrTable(inst),
...@@ -419,3 +434,43 @@ fn emitMemFill(emit: *Emit) !void {...@@ -419,3 +434,43 @@ fn emitMemFill(emit: *Emit) !void {
419 // For now we will always emit index 0.434 // For now we will always emit index 0.
420 try leb128.writeULEB128(emit.code.writer(), @as(u32, 0));435 try leb128.writeULEB128(emit.code.writer(), @as(u32, 0));
421}436}
437
438fn emitDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void {
439 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
440 const dbg_line = emit.mir.extraData(Mir.DbgLineColumn, extra_index).data;
441 try emit.dbgAdvancePCAndLine(dbg_line.line, dbg_line.column);
442}
443
444fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void {
445 if (emit.dbg_output != .dwarf) return;
446
447 const dbg_line = &emit.dbg_output.dwarf.dbg_line;
448 try dbg_line.ensureUnusedCapacity(11);
449 dbg_line.appendAssumeCapacity(std.dwarf.LNS.advance_pc);
450 // TODO: This must emit a relocation to calculate the offset relative
451 // to the code section start.
452 leb128.writeULEB128(dbg_line.writer(), emit.offset() - emit.prev_di_offset) catch unreachable;
453 const delta_line = @intCast(i32, line) - @intCast(i32, emit.prev_di_line);
454 if (delta_line != 0) {
455 dbg_line.appendAssumeCapacity(std.dwarf.LNS.advance_line);
456 leb128.writeILEB128(dbg_line.writer(), delta_line) catch unreachable;
457 }
458 dbg_line.appendAssumeCapacity(std.dwarf.LNS.copy);
459 emit.prev_di_line = line;
460 emit.prev_di_column = column;
461 emit.prev_di_offset = emit.offset();
462}
463
464fn emitDbgPrologueEnd(emit: *Emit) !void {
465 if (emit.dbg_output != .dwarf) return;
466
467 try emit.dbg_output.dwarf.dbg_line.append(std.dwarf.LNS.set_prologue_end);
468 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
469}
470
471fn emitDbgEpilogueBegin(emit: *Emit) !void {
472 if (emit.dbg_output != .dwarf) return;
473
474 try emit.dbg_output.dwarf.dbg_line.append(std.dwarf.LNS.set_epilogue_begin);
475 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
476}
src/arch/wasm/Mir.zig+19
...@@ -47,6 +47,19 @@ pub const Inst = struct {...@@ -47,6 +47,19 @@ pub const Inst = struct {
47 ///47 ///
48 /// Type of the loop is given in data `block_type`48 /// Type of the loop is given in data `block_type`
49 loop = 0x03,49 loop = 0x03,
50 /// Inserts debug information about the current line and column
51 /// of the source code
52 ///
53 /// Uses `payload` of which the payload type is `DbgLineColumn`
54 dbg_line = 0x06,
55 /// Emits epilogue begin debug information
56 ///
57 /// Uses `nop`
58 dbg_epilogue_begin = 0x07,
59 /// Emits prologue end debug information
60 ///
61 /// Uses `nop`
62 dbg_prologue_end = 0x08,
50 /// Represents the end of a function body or an initialization expression63 /// Represents the end of a function body or an initialization expression
51 ///64 ///
52 /// Payload is `nop`65 /// Payload is `nop`
...@@ -645,3 +658,9 @@ pub const Memory = struct {...@@ -645,3 +658,9 @@ pub const Memory = struct {
645 pointer: u32,658 pointer: u32,
646 offset: u32,659 offset: u32,
647};660};
661
662/// Maps a source line with wasm bytecode
663pub const DbgLineColumn = struct {
664 line: u32,
665 column: u32,
666};