Commit 9b6b7034c2

Luuk de Gram <luuk@degram.dev>
2022-05-02 21:41:47
wasm: Flush debug information + commit decl
This implements parts to commit a decl's debug information into a linear memory buffer. The goal is to write this buffer at once after we finished linking.
1 parent f760272
Changed files (2)
src/link/Dwarf.zig
@@ -818,6 +818,7 @@ pub fn commitDeclState(
             const src_fn = switch (self.tag) {
                 .elf => &decl.fn_link.elf,
                 .macho => &decl.fn_link.macho,
+                .wasm => &decl.fn_link.wasm.src_fn,
                 else => unreachable, // TODO
             };
             src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
@@ -958,6 +959,26 @@ pub fn commitDeclState(
                         next_padding_size,
                     );
                 },
+                .wasm => {
+                    const wasm_file = file.cast(File.Wasm).?;
+                    const segment_index = try wasm_file.getDebugLineIndex();
+                    const segment = &wasm_file.segments.items[segment_index];
+                    const debug_atom = wasm_file.atoms.get(segment_index).?;
+                    if (needed_size != segment.size) {
+                        log.debug(" needed size does not equal allocated size: {d}", .{needed_size});
+                        if (needed_size > segment.size) {
+                            log.debug("  allocating {d} bytes for debug line information", .{needed_size - segment.size});
+                            try debug_atom.code.resize(self.allocator, needed_size);
+                            std.mem.set(u8, debug_atom.code.items[segment.size..], 0);
+                        }
+                        debug_atom.size = needed_size;
+                        segment.size = needed_size;
+                    }
+                    // since we can tighly pack the debug lines, wasm does not require
+                    // us to pad with Nops.
+                    const offset = segment.offset + src_fn.off;
+                    std.mem.copy(u8, debug_atom.code.items[offset..], dbg_line_buffer.items);
+                },
                 else => unreachable,
             }
 
@@ -973,6 +994,7 @@ pub fn commitDeclState(
     const atom = switch (self.tag) {
         .elf => &decl.link.elf.dbg_info_atom,
         .macho => &decl.link.macho.dbg_info_atom,
+        .wasm => &decl.link.wasm.dbg_info_atom,
         else => unreachable,
     };
 
@@ -1094,6 +1116,7 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, file: *File, atom: *Atom, len: u3
                         const file_pos = debug_info_sect.offset + atom.off;
                         try pwriteDbgInfoNops(d_sym.file, file_pos, 0, &[0]u8{}, atom.len, false);
                     },
+                    .wasm => {},
                     else => unreachable,
                 }
                 // TODO Look at the free list before appending at the end.
@@ -1253,7 +1276,11 @@ pub fn updateDeclLineNumber(self: *Dwarf, file: *File, decl: *const Module.Decl)
         },
         .wasm => {
             const wasm_file = file.cast(File.Wasm).?;
-            _ = wasm_file; // TODO, update .debug_line
+            const segment_index = wasm_file.getDebugLineIndex() catch unreachable;
+            const segment = wasm_file.segments.items[segment_index];
+            const offset = segment.offset + decl.fn_link.wasm.src_fn.off + self.getRelocDbgLineOff();
+            const debug_atom = wasm_file.atoms.get(segment_index).?;
+            std.mem.copy(u8, debug_atom.code.items[offset..], &data);
         },
         else => unreachable,
     }
src/link/Wasm.zig
@@ -62,6 +62,10 @@ managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
 /// Represents the index into `segments` where the 'code' section
 /// lives.
 code_section_index: ?u32 = null,
+/// The index of the segment representing the custom '.debug_info' section.
+debug_info_index: ?u32 = null,
+/// The index of the segment representing the custom '.debug_line' section.
+debug_line_index: ?u32 = null,
 /// The count of imported functions. This number will be appended
 /// to the function indexes as their index starts at the lowest non-extern function.
 imported_functions_count: u32 = 0,
@@ -311,6 +315,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
             .init = .{ .i32_const = 0 },
         };
     }
+
     return wasm_bin;
 }
 
@@ -566,6 +571,17 @@ pub fn updateFunc(self: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes
         },
     };
 
+    if (self.dwarf) |*dwarf| {
+        try dwarf.commitDeclState(
+            &self.base,
+            mod,
+            decl,
+            // Actual value will be written after relocation
+            0,
+            code.len,
+            &decl_state.?,
+        );
+    }
     return self.finishUpdateDecl(decl, code);
 }
 
@@ -1517,6 +1533,35 @@ fn populateErrorNameTable(self: *Wasm) !void {
     try self.parseAtom(names_atom, .data);
 }
 
+pub fn getDebugInfoIndex(self: *Wasm) !u32 {
+    assert(self.dwarf != null);
+    return self.debug_info_index orelse {
+        self.debug_info_index = @intCast(u32, self.segments.items.len);
+        const segment = try self.segments.addOne(self.base.allocator);
+        segment.* = .{
+            .size = 0,
+            .offset = 0,
+            // debug sections always have alignment '1'
+            .alignment = 1,
+        };
+        return self.debug_info_index.?;
+    };
+}
+
+pub fn getDebugLineIndex(self: *Wasm) !u32 {
+    assert(self.dwarf != null);
+    return self.debug_line_index orelse {
+        self.debug_line_index = @intCast(u32, self.segments.items.len);
+        const segment = try self.segments.addOne(self.base.allocator);
+        segment.* = .{
+            .size = 0,
+            .offset = 0,
+            .alignment = 1,
+        };
+        return self.debug_line_index.?;
+    };
+}
+
 fn resetState(self: *Wasm) void {
     for (self.segment_info.items) |*segment_info| {
         self.base.allocator.free(segment_info.name);
@@ -1542,6 +1587,7 @@ fn resetState(self: *Wasm) void {
     self.atoms.clearRetainingCapacity();
     self.symbol_atom.clearRetainingCapacity();
     self.code_section_index = null;
+    self.debug_info_index = null;
 }
 
 pub fn flush(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void {
@@ -1636,6 +1682,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
         try self.objects.items[object_index].parseIntoAtoms(self.base.allocator, object_index, self);
     }
 
+    if (self.dwarf) |*dwarf| {
+        try dwarf.flushModule(&self.base, self.base.options.module.?);
+    }
     try self.allocateAtoms();
     try self.setupMemory();
     self.mapFunctionTable();