| ... | @@ -95,9 +95,10 @@ imports: std.AutoHashMapUnmanaged(SymbolLoc, types.Import) = .{}, | ... | @@ -95,9 +95,10 @@ imports: std.AutoHashMapUnmanaged(SymbolLoc, types.Import) = .{}, |
| 95 | segments: std.ArrayListUnmanaged(Segment) = .{}, | 95 | segments: std.ArrayListUnmanaged(Segment) = .{}, |
| 96 | /// Maps a data segment key (such as .rodata) to the index into `segments`. | 96 | /// Maps a data segment key (such as .rodata) to the index into `segments`. |
| 97 | data_segments: std.StringArrayHashMapUnmanaged(u32) = .{}, | 97 | data_segments: std.StringArrayHashMapUnmanaged(u32) = .{}, |
| 98 | /// A list of `types.Segment` which provide meta data | 98 | /// A table of `types.Segment` which provide meta data |
| 99 | /// about a data symbol such as its name | 99 | /// about a data symbol such as its name where the key is |
| 100 | segment_info: std.ArrayListUnmanaged(types.Segment) = .{}, | 100 | /// the segment index, which can be found from `data_segments` |
| | 101 | segment_info: std.AutoArrayHashMapUnmanaged(u32, types.Segment) = .{}, |
| 101 | /// Deduplicated string table for strings used by symbols, imports and exports. | 102 | /// Deduplicated string table for strings used by symbols, imports and exports. |
| 102 | string_table: StringTable = .{}, | 103 | string_table: StringTable = .{}, |
| 103 | /// Debug information for wasm | 104 | /// Debug information for wasm |
| ... | @@ -158,6 +159,19 @@ export_names: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{}, | ... | @@ -158,6 +159,19 @@ export_names: std.AutoHashMapUnmanaged(SymbolLoc, u32) = .{}, |
| 158 | /// The actual table is populated during `flush`. | 159 | /// The actual table is populated during `flush`. |
| 159 | error_table_symbol: ?u32 = null, | 160 | error_table_symbol: ?u32 = null, |
| 160 | | 161 | |
| | 162 | // Debug section atoms. These are only set when the current compilation |
| | 163 | // unit contains Zig code. The lifetime of these atoms are extended |
| | 164 | // until the end of the compiler's lifetime. Meaning they're not freed |
| | 165 | // during `flush()` in incremental-mode. |
| | 166 | debug_info_atom: ?*Atom = null, |
| | 167 | debug_line_atom: ?*Atom = null, |
| | 168 | debug_loc_atom: ?*Atom = null, |
| | 169 | debug_ranges_atom: ?*Atom = null, |
| | 170 | debug_abbrev_atom: ?*Atom = null, |
| | 171 | debug_str_atom: ?*Atom = null, |
| | 172 | debug_pubnames_atom: ?*Atom = null, |
| | 173 | debug_pubtypes_atom: ?*Atom = null, |
| | 174 | |
| 161 | pub const Segment = struct { | 175 | pub const Segment = struct { |
| 162 | alignment: u32, | 176 | alignment: u32, |
| 163 | size: u32, | 177 | size: u32, |
| ... | @@ -384,15 +398,16 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm { | ... | @@ -384,15 +398,16 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm { |
| 384 | /// and symbols come from the object files instead. | 398 | /// and symbols come from the object files instead. |
| 385 | pub fn initDebugSections(self: *Wasm) !void { | 399 | pub fn initDebugSections(self: *Wasm) !void { |
| 386 | if (self.dwarf == null) return; // not compiling Zig code, so no need to pre-initialize debug sections | 400 | if (self.dwarf == null) return; // not compiling Zig code, so no need to pre-initialize debug sections |
| | 401 | assert(self.debug_info_index == null); |
| 387 | // this will create an Atom and set the index for us. | 402 | // this will create an Atom and set the index for us. |
| 388 | try self.createDebugSectionForIndex(&self.debug_info_index); | 403 | self.debug_info_atom = try self.createDebugSectionForIndex(&self.debug_info_index, ".debug_info"); |
| 389 | try self.createDebugSectionForIndex(&self.debug_line_index); | 404 | self.debug_line_atom = try self.createDebugSectionForIndex(&self.debug_line_index, ".debug_line"); |
| 390 | try self.createDebugSectionForIndex(&self.debug_loc_index); | 405 | self.debug_loc_atom = try self.createDebugSectionForIndex(&self.debug_loc_index, ".debug_loc"); |
| 391 | try self.createDebugSectionForIndex(&self.debug_abbrev_index); | 406 | self.debug_abbrev_atom = try self.createDebugSectionForIndex(&self.debug_abbrev_index, ".debug_abbrev"); |
| 392 | try self.createDebugSectionForIndex(&self.debug_ranges_index); | 407 | self.debug_ranges_atom = try self.createDebugSectionForIndex(&self.debug_ranges_index, ".debug_ranges"); |
| 393 | try self.createDebugSectionForIndex(&self.debug_str_index); | 408 | self.debug_str_atom = try self.createDebugSectionForIndex(&self.debug_str_index, ".debug_str"); |
| 394 | try self.createDebugSectionForIndex(&self.debug_pubnames_index); | 409 | self.debug_pubnames_atom = try self.createDebugSectionForIndex(&self.debug_pubnames_index, ".debug_pubnames"); |
| 395 | try self.createDebugSectionForIndex(&self.debug_pubtypes_index); | 410 | self.debug_pubtypes_atom = try self.createDebugSectionForIndex(&self.debug_pubtypes_index, ".debug_pubtypes"); |
| 396 | } | 411 | } |
| 397 | | 412 | |
| 398 | fn parseInputFiles(self: *Wasm, files: []const []const u8) !void { | 413 | fn parseInputFiles(self: *Wasm, files: []const []const u8) !void { |
| ... | @@ -676,7 +691,7 @@ pub fn deinit(self: *Wasm) void { | ... | @@ -676,7 +691,7 @@ pub fn deinit(self: *Wasm) void { |
| 676 | for (self.func_types.items) |*func_type| { | 691 | for (self.func_types.items) |*func_type| { |
| 677 | func_type.deinit(gpa); | 692 | func_type.deinit(gpa); |
| 678 | } | 693 | } |
| 679 | for (self.segment_info.items) |segment_info| { | 694 | for (self.segment_info.values()) |segment_info| { |
| 680 | gpa.free(segment_info.name); | 695 | gpa.free(segment_info.name); |
| 681 | } | 696 | } |
| 682 | for (self.objects.items) |*object| { | 697 | for (self.objects.items) |*object| { |
| ... | @@ -1364,16 +1379,7 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { | ... | @@ -1364,16 +1379,7 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { |
| 1364 | const index = gop.value_ptr.*; | 1379 | const index = gop.value_ptr.*; |
| 1365 | self.segments.items[index].size += atom.size; | 1380 | self.segments.items[index].size += atom.size; |
| 1366 | | 1381 | |
| 1367 | // segment indexes can be off by 1 due to also containing a segment | 1382 | symbol.index = @intCast(u32, self.segment_info.getIndex(index).?); |
| 1368 | // for the code section, so we must check if the existing segment | | |
| 1369 | // is larger than that of the code section, and substract the index by 1 in such case. | | |
| 1370 | var info_add = if (self.code_section_index) |idx| blk: { | | |
| 1371 | if (idx < index) break :blk @as(u32, 1); | | |
| 1372 | break :blk 0; | | |
| 1373 | } else @as(u32, 0); | | |
| 1374 | if (self.debug_info_index != null) info_add += 1; | | |
| 1375 | if (self.debug_line_index != null) info_add += 1; | | |
| 1376 | symbol.index = index - info_add; | | |
| 1377 | // segment info already exists, so free its memory | 1383 | // segment info already exists, so free its memory |
| 1378 | self.base.allocator.free(segment_name); | 1384 | self.base.allocator.free(segment_name); |
| 1379 | break :result index; | 1385 | break :result index; |
| ... | @@ -1386,8 +1392,8 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { | ... | @@ -1386,8 +1392,8 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { |
| 1386 | }); | 1392 | }); |
| 1387 | gop.value_ptr.* = index; | 1393 | gop.value_ptr.* = index; |
| 1388 | | 1394 | |
| 1389 | const info_index = @intCast(u32, self.segment_info.items.len); | 1395 | const info_index = @intCast(u32, self.segment_info.count()); |
| 1390 | try self.segment_info.append(self.base.allocator, segment_info); | 1396 | try self.segment_info.put(self.base.allocator, index, segment_info); |
| 1391 | symbol.index = info_index; | 1397 | symbol.index = info_index; |
| 1392 | break :result index; | 1398 | break :result index; |
| 1393 | } | 1399 | } |
| ... | @@ -1397,18 +1403,54 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { | ... | @@ -1397,18 +1403,54 @@ fn parseAtom(self: *Wasm, atom: *Atom, kind: Kind) !void { |
| 1397 | const segment: *Segment = &self.segments.items[final_index]; | 1403 | const segment: *Segment = &self.segments.items[final_index]; |
| 1398 | segment.alignment = std.math.max(segment.alignment, atom.alignment); | 1404 | segment.alignment = std.math.max(segment.alignment, atom.alignment); |
| 1399 | | 1405 | |
| 1400 | if (self.atoms.getPtr(final_index)) |last| { | 1406 | try self.appendAtomAtIndex(final_index, atom); |
| | 1407 | } |
| | 1408 | |
| | 1409 | /// From a given index, append the given `Atom` at the back of the linked list. |
| | 1410 | /// Simply inserts it into the map of atoms when it doesn't exist yet. |
| | 1411 | pub fn appendAtomAtIndex(self: *Wasm, index: u32, atom: *Atom) !void { |
| | 1412 | if (self.atoms.getPtr(index)) |last| { |
| 1401 | last.*.next = atom; | 1413 | last.*.next = atom; |
| 1402 | atom.prev = last.*; | 1414 | atom.prev = last.*; |
| 1403 | last.* = atom; | 1415 | last.* = atom; |
| 1404 | } else { | 1416 | } else { |
| 1405 | try self.atoms.putNoClobber(self.base.allocator, final_index, atom); | 1417 | try self.atoms.putNoClobber(self.base.allocator, index, atom); |
| 1406 | } | 1418 | } |
| 1407 | } | 1419 | } |
| 1408 | | 1420 | |
| | 1421 | /// Allocates debug atoms into their respective debug sections |
| | 1422 | /// to merge them with maybe-existing debug atoms from object files. |
| | 1423 | fn allocateDebugAtoms(self: *Wasm) !void { |
| | 1424 | if (self.dwarf == null) return; |
| | 1425 | |
| | 1426 | const allocAtom = struct { |
| | 1427 | fn f(bin: *Wasm, maybe_index: *?u32, atom: *Atom) !void { |
| | 1428 | const index = maybe_index.* orelse idx: { |
| | 1429 | const index = @intCast(u32, bin.segments.items.len); |
| | 1430 | try bin.appendDummySegment(); |
| | 1431 | maybe_index.* = index; |
| | 1432 | break :idx index; |
| | 1433 | }; |
| | 1434 | atom.size = @intCast(u32, atom.code.items.len); |
| | 1435 | bin.symbols.items[atom.sym_index].index = index; |
| | 1436 | try bin.appendAtomAtIndex(index, atom); |
| | 1437 | } |
| | 1438 | }.f; |
| | 1439 | |
| | 1440 | try allocAtom(self, &self.debug_info_index, self.debug_info_atom.?); |
| | 1441 | try allocAtom(self, &self.debug_line_index, self.debug_line_atom.?); |
| | 1442 | try allocAtom(self, &self.debug_loc_index, self.debug_loc_atom.?); |
| | 1443 | try allocAtom(self, &self.debug_str_index, self.debug_str_atom.?); |
| | 1444 | try allocAtom(self, &self.debug_ranges_index, self.debug_ranges_atom.?); |
| | 1445 | try allocAtom(self, &self.debug_abbrev_index, self.debug_abbrev_atom.?); |
| | 1446 | try allocAtom(self, &self.debug_pubnames_index, self.debug_pubnames_atom.?); |
| | 1447 | try allocAtom(self, &self.debug_pubtypes_index, self.debug_pubtypes_atom.?); |
| | 1448 | } |
| | 1449 | |
| 1409 | fn allocateAtoms(self: *Wasm) !void { | 1450 | fn allocateAtoms(self: *Wasm) !void { |
| 1410 | // first sort the data segments | 1451 | // first sort the data segments |
| 1411 | try sortDataSegments(self); | 1452 | try sortDataSegments(self); |
| | 1453 | try allocateDebugAtoms(self); |
| 1412 | | 1454 | |
| 1413 | var it = self.atoms.iterator(); | 1455 | var it = self.atoms.iterator(); |
| 1414 | while (it.next()) |entry| { | 1456 | while (it.next()) |entry| { |
| ... | @@ -1426,7 +1468,7 @@ fn allocateAtoms(self: *Wasm) !void { | ... | @@ -1426,7 +1468,7 @@ fn allocateAtoms(self: *Wasm) !void { |
| 1426 | atom.size, | 1468 | atom.size, |
| 1427 | }); | 1469 | }); |
| 1428 | offset += atom.size; | 1470 | offset += atom.size; |
| 1429 | self.symbol_atom.putAssumeCapacity(atom.symbolLoc(), atom); // Update atom pointers | 1471 | try self.symbol_atom.put(self.base.allocator, atom.symbolLoc(), atom); // Update atom pointers |
| 1430 | atom = atom.next orelse break; | 1472 | atom = atom.next orelse break; |
| 1431 | } | 1473 | } |
| 1432 | segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment); | 1474 | segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment); |
| ... | @@ -1989,20 +2031,35 @@ fn populateErrorNameTable(self: *Wasm) !void { | ... | @@ -1989,20 +2031,35 @@ fn populateErrorNameTable(self: *Wasm) !void { |
| 1989 | /// From a given index variable, creates a new debug section. | 2031 | /// From a given index variable, creates a new debug section. |
| 1990 | /// This initializes the index, appends a new segment, | 2032 | /// This initializes the index, appends a new segment, |
| 1991 | /// and finally, creates a managed `Atom`. | 2033 | /// and finally, creates a managed `Atom`. |
| 1992 | pub fn createDebugSectionForIndex(self: *Wasm, index: *?u32) !void { | 2034 | pub fn createDebugSectionForIndex(self: *Wasm, index: *?u32, name: []const u8) !*Atom { |
| 1993 | const new_index = @intCast(u32, self.segments.items.len); | 2035 | const new_index = @intCast(u32, self.segments.items.len); |
| 1994 | index.* = new_index; | 2036 | index.* = new_index; |
| 1995 | try self.appendDummySegment(); | 2037 | try self.appendDummySegment(); |
| | 2038 | // _ = index; |
| | 2039 | |
| | 2040 | const sym_index = self.symbols_free_list.popOrNull() orelse idx: { |
| | 2041 | const tmp_index = @intCast(u32, self.symbols.items.len); |
| | 2042 | _ = try self.symbols.addOne(self.base.allocator); |
| | 2043 | break :idx tmp_index; |
| | 2044 | }; |
| | 2045 | self.symbols.items[sym_index] = .{ |
| | 2046 | .tag = .section, |
| | 2047 | .name = try self.string_table.put(self.base.allocator, name), |
| | 2048 | .index = 0, |
| | 2049 | .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL), |
| | 2050 | }; |
| 1996 | | 2051 | |
| 1997 | const atom = try self.base.allocator.create(Atom); | 2052 | const atom = try self.base.allocator.create(Atom); |
| 1998 | atom.* = Atom.empty; | 2053 | atom.* = Atom.empty; |
| 1999 | atom.alignment = 1; // debug sections are always 1-byte-aligned | 2054 | atom.alignment = 1; // debug sections are always 1-byte-aligned |
| | 2055 | atom.sym_index = sym_index; |
| 2000 | try self.managed_atoms.append(self.base.allocator, atom); | 2056 | try self.managed_atoms.append(self.base.allocator, atom); |
| 2001 | try self.atoms.put(self.base.allocator, new_index, atom); | 2057 | try self.symbol_atom.put(self.base.allocator, atom.symbolLoc(), atom); |
| | 2058 | return atom; |
| 2002 | } | 2059 | } |
| 2003 | | 2060 | |
| 2004 | fn resetState(self: *Wasm) void { | 2061 | fn resetState(self: *Wasm) void { |
| 2005 | for (self.segment_info.items) |*segment_info| { | 2062 | for (self.segment_info.values()) |segment_info| { |
| 2006 | self.base.allocator.free(segment_info.name); | 2063 | self.base.allocator.free(segment_info.name); |
| 2007 | } | 2064 | } |
| 2008 | if (self.base.options.module) |mod| { | 2065 | if (self.base.options.module) |mod| { |
| ... | @@ -2029,6 +2086,12 @@ fn resetState(self: *Wasm) void { | ... | @@ -2029,6 +2086,12 @@ fn resetState(self: *Wasm) void { |
| 2029 | self.code_section_index = null; | 2086 | self.code_section_index = null; |
| 2030 | self.debug_info_index = null; | 2087 | self.debug_info_index = null; |
| 2031 | self.debug_line_index = null; | 2088 | self.debug_line_index = null; |
| | 2089 | self.debug_loc_index = null; |
| | 2090 | self.debug_str_index = null; |
| | 2091 | self.debug_ranges_index = null; |
| | 2092 | self.debug_abbrev_index = null; |
| | 2093 | self.debug_pubnames_index = null; |
| | 2094 | self.debug_pubtypes_index = null; |
| 2032 | } | 2095 | } |
| 2033 | | 2096 | |
| 2034 | pub fn flush(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void { | 2097 | pub fn flush(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void { |
| ... | @@ -2508,26 +2571,31 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -2508,26 +2571,31 @@ pub fn flushModule(self: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2508 | var debug_bytes = std.ArrayList(u8).init(self.base.allocator); | 2571 | var debug_bytes = std.ArrayList(u8).init(self.base.allocator); |
| 2509 | defer debug_bytes.deinit(); | 2572 | defer debug_bytes.deinit(); |
| 2510 | | 2573 | |
| 2511 | const debug_sections = .{ | 2574 | const DebugSection = struct { |
| 2512 | .{ ".debug_info", self.debug_info_index }, | 2575 | name: []const u8, |
| 2513 | .{ ".debug_pubtypes", self.debug_pubtypes_index }, | 2576 | index: ?u32, |
| 2514 | .{ ".debug_abbrev", self.debug_abbrev_index }, | 2577 | }; |
| 2515 | .{ ".debug_line", self.debug_line_index }, | 2578 | |
| 2516 | .{ ".debug_str", self.debug_str_index }, | 2579 | const debug_sections: []const DebugSection = &.{ |
| 2517 | .{ ".debug_pubnames", self.debug_pubnames_index }, | 2580 | .{ .name = ".debug_info", .index = self.debug_info_index }, |
| 2518 | .{ ".debug_loc", self.debug_loc_index }, | 2581 | .{ .name = ".debug_pubtypes", .index = self.debug_pubtypes_index }, |
| 2519 | .{ ".debug_ranges", self.debug_ranges_index }, | 2582 | .{ .name = ".debug_abbrev", .index = self.debug_abbrev_index }, |
| | 2583 | .{ .name = ".debug_line", .index = self.debug_line_index }, |
| | 2584 | .{ .name = ".debug_str", .index = self.debug_str_index }, |
| | 2585 | .{ .name = ".debug_pubnames", .index = self.debug_pubnames_index }, |
| | 2586 | .{ .name = ".debug_loc", .index = self.debug_loc_index }, |
| | 2587 | .{ .name = ".debug_ranges", .index = self.debug_ranges_index }, |
| 2520 | }; | 2588 | }; |
| 2521 | | 2589 | |
| 2522 | inline for (debug_sections) |item| { | 2590 | for (debug_sections) |item| { |
| 2523 | if (item[1]) |index| { | 2591 | if (item.index) |index| { |
| 2524 | var atom = self.atoms.get(index).?.getFirst(); | 2592 | var atom = self.atoms.get(index).?.getFirst(); |
| 2525 | while (true) { | 2593 | while (true) { |
| 2526 | atom.resolveRelocs(self); | 2594 | atom.resolveRelocs(self); |
| 2527 | try debug_bytes.appendSlice(atom.code.items); | 2595 | try debug_bytes.appendSlice(atom.code.items); |
| 2528 | atom = atom.next orelse break; | 2596 | atom = atom.next orelse break; |
| 2529 | } | 2597 | } |
| 2530 | try emitDebugSection(file, debug_bytes.items, item[0]); | 2598 | try emitDebugSection(file, debug_bytes.items, item.name); |
| 2531 | debug_bytes.clearRetainingCapacity(); | 2599 | debug_bytes.clearRetainingCapacity(); |
| 2532 | } | 2600 | } |
| 2533 | } | 2601 | } |
| ... | @@ -3242,8 +3310,8 @@ fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void { | ... | @@ -3242,8 +3310,8 @@ fn emitSegmentInfo(self: *Wasm, file: fs.File, arena: Allocator) !void { |
| 3242 | var payload = std.ArrayList(u8).init(arena); | 3310 | var payload = std.ArrayList(u8).init(arena); |
| 3243 | const writer = payload.writer(); | 3311 | const writer = payload.writer(); |
| 3244 | try leb.writeULEB128(file.writer(), @enumToInt(types.SubsectionType.WASM_SEGMENT_INFO)); | 3312 | try leb.writeULEB128(file.writer(), @enumToInt(types.SubsectionType.WASM_SEGMENT_INFO)); |
| 3245 | try leb.writeULEB128(writer, @intCast(u32, self.segment_info.items.len)); | 3313 | try leb.writeULEB128(writer, @intCast(u32, self.segment_info.count())); |
| 3246 | for (self.segment_info.items) |segment_info| { | 3314 | for (self.segment_info.values()) |segment_info| { |
| 3247 | log.debug("Emit segment: {s} align({d}) flags({b})", .{ | 3315 | log.debug("Emit segment: {s} align({d}) flags({b})", .{ |
| 3248 | segment_info.name, | 3316 | segment_info.name, |
| 3249 | @ctz(segment_info.alignment), | 3317 | @ctz(segment_info.alignment), |