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 {
882882 self.args = cc_result.args;
883883 self.return_value = cc_result.return_value;
884884
885 try self.addTag(.dbg_prologue_end);
886
885887 // Generate MIR for function body
886888 try self.genBody(self.air.getMainBody());
887889 // 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 {
896898 // End of function body
897899 try self.addTag(.end);
898900
901 try self.addTag(.dbg_epilogue_begin);
902
899903 // check if we have to initialize and allocate anything into the stack frame.
900904 // If so, create enough stack space and insert the instructions at the front of the list.
901905 if (self.stack_size > 0) {
......@@ -942,6 +946,10 @@ fn genFunc(self: *Self) InnerError!void {
942946 .code = self.code,
943947 .locals = self.locals.items,
944948 .decl = self.decl,
949 .dbg_output = self.debug_output,
950 .prev_di_line = 0,
951 .prev_di_column = 0,
952 .prev_di_offset = 0,
945953 };
946954
947955 emit.emitMir() catch |err| switch (err) {
src/arch/wasm/Emit.zig+55
......@@ -6,6 +6,7 @@ const std = @import("std");
66const Mir = @import("Mir.zig");
77const link = @import("../../link.zig");
88const Module = @import("../../Module.zig");
9const codegen = @import("../../codegen.zig");
910const leb128 = std.leb;
1011
1112/// Contains our list of instructions
......@@ -22,6 +23,16 @@ locals: []const u8,
2223/// The declaration that code is being generated for.
2324decl: *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
2536const InnerError = error{
2637 OutOfMemory,
2738 EmitFail,
......@@ -40,6 +51,10 @@ pub fn emitMir(emit: *Emit) InnerError!void {
4051 .block => try emit.emitBlock(tag, inst),
4152 .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
4358 // branch instructions
4459 .br_if => try emit.emitLabel(tag, inst),
4560 .br_table => try emit.emitBrTable(inst),
......@@ -419,3 +434,43 @@ fn emitMemFill(emit: *Emit) !void {
419434 // For now we will always emit index 0.
420435 try leb128.writeULEB128(emit.code.writer(), @as(u32, 0));
421436}
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 {
4747 ///
4848 /// Type of the loop is given in data `block_type`
4949 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,
5063 /// Represents the end of a function body or an initialization expression
5164 ///
5265 /// Payload is `nop`
......@@ -645,3 +658,9 @@ pub const Memory = struct {
645658 pointer: u32,
646659 offset: u32,
647660};
661
662/// Maps a source line with wasm bytecode
663pub const DbgLineColumn = struct {
664 line: u32,
665 column: u32,
666};