From f76027220042d965278184088f1f04b6c8430bcb Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Sun, 1 May 2022 16:34:05 +0200 Subject: [PATCH] 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). --- src/arch/wasm/CodeGen.zig | 8 ++++++ src/arch/wasm/Emit.zig | 55 +++++++++++++++++++++++++++++++++++++++ src/arch/wasm/Mir.zig | 19 ++++++++++++++ 3 files changed, 82 insertions(+) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 9c90aa0afa..cb2578bee9 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/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) { diff --git a/src/arch/wasm/Emit.zig b/src/arch/wasm/Emit.zig index bcbff8d195..0081dc4d5d 100644 --- a/src/arch/wasm/Emit.zig +++ b/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); +} diff --git a/src/arch/wasm/Mir.zig b/src/arch/wasm/Mir.zig index 87e64ce9e0..6cf43c1e03 100644 --- a/src/arch/wasm/Mir.zig +++ b/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, +};