Commit f760272200

Luuk de Gram <luuk@degram.dev>
2022-05-01 16:34:05
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).
1 parent d46cdb5
Changed files (3)
src/arch/wasm/CodeGen.zig
@@ -882,6 +882,8 @@ fn genFunc(self: *Self) InnerError!void {
     self.args = cc_result.args;
     self.return_value = cc_result.return_value;
 
+    try self.addTag(.dbg_prologue_end);
+
     // Generate MIR for function body
     try self.genBody(self.air.getMainBody());
     // 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 {
     // End of function body
     try self.addTag(.end);
 
+    try self.addTag(.dbg_epilogue_begin);
+
     // check if we have to initialize and allocate anything into the stack frame.
     // If so, create enough stack space and insert the instructions at the front of the list.
     if (self.stack_size > 0) {
@@ -942,6 +946,10 @@ fn genFunc(self: *Self) InnerError!void {
         .code = self.code,
         .locals = self.locals.items,
         .decl = self.decl,
+        .dbg_output = self.debug_output,
+        .prev_di_line = 0,
+        .prev_di_column = 0,
+        .prev_di_offset = 0,
     };
 
     emit.emitMir() catch |err| switch (err) {
src/arch/wasm/Emit.zig
@@ -6,6 +6,7 @@ const std = @import("std");
 const Mir = @import("Mir.zig");
 const link = @import("../../link.zig");
 const Module = @import("../../Module.zig");
+const codegen = @import("../../codegen.zig");
 const leb128 = std.leb;
 
 /// Contains our list of instructions
@@ -22,6 +23,16 @@ locals: []const u8,
 /// The declaration that code is being generated for.
 decl: *Module.Decl,
 
+// Debug information
+/// Holds the debug information for this emission
+dbg_output: codegen.DebugInfoOutput,
+/// Previous debug info line
+prev_di_line: u32,
+/// Previous debug info column
+prev_di_column: u32,
+/// Previous offset relative to code section
+prev_di_offset: u32,
+
 const InnerError = error{
     OutOfMemory,
     EmitFail,
@@ -40,6 +51,10 @@ pub fn emitMir(emit: *Emit) InnerError!void {
             .block => try emit.emitBlock(tag, inst),
             .loop => try emit.emitBlock(tag, inst),
 
+            .dbg_line => try emit.emitDbgLine(inst),
+            .dbg_epilogue_begin => try emit.emitDbgEpilogueBegin(),
+            .dbg_prologue_end => try emit.emitDbgPrologueEnd(),
+
             // branch instructions
             .br_if => try emit.emitLabel(tag, inst),
             .br_table => try emit.emitBrTable(inst),
@@ -419,3 +434,43 @@ fn emitMemFill(emit: *Emit) !void {
     // For now we will always emit index 0.
     try leb128.writeULEB128(emit.code.writer(), @as(u32, 0));
 }
+
+fn emitDbgLine(emit: *Emit, inst: Mir.Inst.Index) !void {
+    const extra_index = emit.mir.instructions.items(.data)[inst].payload;
+    const dbg_line = emit.mir.extraData(Mir.DbgLineColumn, extra_index).data;
+    try emit.dbgAdvancePCAndLine(dbg_line.line, dbg_line.column);
+}
+
+fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void {
+    if (emit.dbg_output != .dwarf) return;
+
+    const dbg_line = &emit.dbg_output.dwarf.dbg_line;
+    try dbg_line.ensureUnusedCapacity(11);
+    dbg_line.appendAssumeCapacity(std.dwarf.LNS.advance_pc);
+    // TODO: This must emit a relocation to calculate the offset relative
+    // to the code section start.
+    leb128.writeULEB128(dbg_line.writer(), emit.offset() - emit.prev_di_offset) catch unreachable;
+    const delta_line = @intCast(i32, line) - @intCast(i32, emit.prev_di_line);
+    if (delta_line != 0) {
+        dbg_line.appendAssumeCapacity(std.dwarf.LNS.advance_line);
+        leb128.writeILEB128(dbg_line.writer(), delta_line) catch unreachable;
+    }
+    dbg_line.appendAssumeCapacity(std.dwarf.LNS.copy);
+    emit.prev_di_line = line;
+    emit.prev_di_column = column;
+    emit.prev_di_offset = emit.offset();
+}
+
+fn emitDbgPrologueEnd(emit: *Emit) !void {
+    if (emit.dbg_output != .dwarf) return;
+
+    try emit.dbg_output.dwarf.dbg_line.append(std.dwarf.LNS.set_prologue_end);
+    try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
+}
+
+fn emitDbgEpilogueBegin(emit: *Emit) !void {
+    if (emit.dbg_output != .dwarf) return;
+
+    try emit.dbg_output.dwarf.dbg_line.append(std.dwarf.LNS.set_epilogue_begin);
+    try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
+}
src/arch/wasm/Mir.zig
@@ -47,6 +47,19 @@ pub const Inst = struct {
         ///
         /// Type of the loop is given in data `block_type`
         loop = 0x03,
+        /// Inserts debug information about the current line and column
+        /// of the source code
+        ///
+        /// Uses `payload` of which the payload type is `DbgLineColumn`
+        dbg_line = 0x06,
+        /// Emits epilogue begin debug information
+        ///
+        /// Uses `nop`
+        dbg_epilogue_begin = 0x07,
+        /// Emits prologue end debug information
+        ///
+        /// Uses `nop`
+        dbg_prologue_end = 0x08,
         /// Represents the end of a function body or an initialization expression
         ///
         /// Payload is `nop`
@@ -645,3 +658,9 @@ pub const Memory = struct {
     pointer: u32,
     offset: u32,
 };
+
+/// Maps a source line with wasm bytecode
+pub const DbgLineColumn = struct {
+    line: u32,
+    column: u32,
+};