| ... | @@ -21,10 +21,11 @@ const assert = std.debug.assert; | ... | @@ -21,10 +21,11 @@ const assert = std.debug.assert; |
| 21 | | 21 | |
| 22 | /// Ordered list of data segments that will appear in the final binary. | 22 | /// Ordered list of data segments that will appear in the final binary. |
| 23 | /// When sorted, to-be-merged segments will be made adjacent. | 23 | /// When sorted, to-be-merged segments will be made adjacent. |
| 24 | /// Values are offset relative to segment start. | 24 | /// Values are virtual address. |
| 25 | data_segments: std.AutoArrayHashMapUnmanaged(Wasm.DataSegment.Id, u32) = .empty, | 25 | data_segments: std.AutoArrayHashMapUnmanaged(Wasm.DataSegment.Id, u32) = .empty, |
| 26 | /// Each time a `data_segment` offset equals zero it indicates a new group, and | 26 | /// Each time a `data_segment` offset equals zero it indicates a new group, and |
| 27 | /// the next element in this array will contain the total merged segment size. | 27 | /// the next element in this array will contain the total merged segment size. |
| | 28 | /// Value is the virtual memory address of the end of the segment. |
| 28 | data_segment_groups: std.ArrayListUnmanaged(u32) = .empty, | 29 | data_segment_groups: std.ArrayListUnmanaged(u32) = .empty, |
| 29 | | 30 | |
| 30 | binary_bytes: std.ArrayListUnmanaged(u8) = .empty, | 31 | binary_bytes: std.ArrayListUnmanaged(u8) = .empty, |
| ... | @@ -71,6 +72,10 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { | ... | @@ -71,6 +72,10 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 71 | }; | 72 | }; |
| 72 | const is_obj = comp.config.output_mode == .Obj; | 73 | const is_obj = comp.config.output_mode == .Obj; |
| 73 | const allow_undefined = is_obj or wasm.import_symbols; | 74 | const allow_undefined = is_obj or wasm.import_symbols; |
| | 75 | //const undef_byte: u8 = switch (comp.root_mod.optimize_mode) { |
| | 76 | // .Debug, .ReleaseSafe => 0xaa, |
| | 77 | // .ReleaseFast, .ReleaseSmall => 0x00, |
| | 78 | //}; |
| 74 | | 79 | |
| 75 | if (comp.zcu) |zcu| { | 80 | if (comp.zcu) |zcu| { |
| 76 | const ip: *const InternPool = &zcu.intern_pool; // No mutations allowed! | 81 | const ip: *const InternPool = &zcu.intern_pool; // No mutations allowed! |
| ... | @@ -304,45 +309,46 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { | ... | @@ -304,45 +309,46 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 304 | } | 309 | } |
| 305 | | 310 | |
| 306 | const segment_ids = f.data_segments.keys(); | 311 | const segment_ids = f.data_segments.keys(); |
| 307 | const segment_offsets = f.data_segments.values(); | 312 | const segment_vaddrs = f.data_segments.values(); |
| 308 | assert(f.data_segment_groups.items.len == 0); | 313 | assert(f.data_segment_groups.items.len == 0); |
| | 314 | const data_vaddr: u32 = @intCast(memory_ptr); |
| 309 | { | 315 | { |
| 310 | var seen_tls: enum { before, during, after } = .before; | 316 | var seen_tls: enum { before, during, after } = .before; |
| 311 | var offset: u32 = 0; | 317 | var category: Wasm.DataSegment.Category = undefined; |
| 312 | for (segment_ids, segment_offsets, 0..) |segment_id, *segment_offset, i| { | 318 | for (segment_ids, segment_vaddrs, 0..) |segment_id, *segment_vaddr, i| { |
| 313 | const alignment = segment_id.alignment(wasm); | 319 | const alignment = segment_id.alignment(wasm); |
| 314 | memory_ptr = alignment.forward(memory_ptr); | 320 | category = segment_id.category(wasm); |
| | 321 | const start_addr = alignment.forward(memory_ptr); |
| 315 | | 322 | |
| 316 | const want_new_segment = b: { | 323 | const want_new_segment = b: { |
| 317 | if (is_obj) break :b false; | 324 | if (is_obj) break :b false; |
| 318 | switch (seen_tls) { | 325 | switch (seen_tls) { |
| 319 | .before => if (segment_id.isTls(wasm)) { | 326 | .before => if (category == .tls) { |
| 320 | virtual_addrs.tls_base = if (shared_memory) 0 else @intCast(memory_ptr); | 327 | virtual_addrs.tls_base = if (shared_memory) 0 else @intCast(start_addr); |
| 321 | virtual_addrs.tls_align = alignment; | 328 | virtual_addrs.tls_align = alignment; |
| 322 | seen_tls = .during; | 329 | seen_tls = .during; |
| 323 | break :b true; | 330 | break :b f.data_segment_groups.items.len > 0; |
| 324 | }, | 331 | }, |
| 325 | .during => if (!segment_id.isTls(wasm)) { | 332 | .during => if (category != .tls) { |
| 326 | virtual_addrs.tls_size = @intCast(memory_ptr - virtual_addrs.tls_base.?); | 333 | virtual_addrs.tls_size = @intCast(start_addr - virtual_addrs.tls_base.?); |
| 327 | virtual_addrs.tls_align = virtual_addrs.tls_align.maxStrict(alignment); | 334 | virtual_addrs.tls_align = virtual_addrs.tls_align.maxStrict(alignment); |
| 328 | seen_tls = .after; | 335 | seen_tls = .after; |
| 329 | break :b true; | 336 | break :b true; |
| 330 | }, | 337 | }, |
| 331 | .after => {}, | 338 | .after => {}, |
| 332 | } | 339 | } |
| 333 | break :b i >= 1 and !wantSegmentMerge(wasm, segment_ids[i - 1], segment_id); | 340 | break :b i >= 1 and !wantSegmentMerge(wasm, segment_ids[i - 1], segment_id, category); |
| 334 | }; | 341 | }; |
| 335 | if (want_new_segment) { | 342 | if (want_new_segment) { |
| 336 | if (offset > 0) try f.data_segment_groups.append(gpa, offset); | 343 | log.debug("new segment at 0x{x} {} {s} {}", .{ start_addr, segment_id, segment_id.name(wasm), category }); |
| 337 | offset = 0; | 344 | try f.data_segment_groups.append(gpa, @intCast(memory_ptr)); |
| 338 | } | 345 | } |
| 339 | | 346 | |
| 340 | const size = segment_id.size(wasm); | 347 | const size = segment_id.size(wasm); |
| 341 | segment_offset.* = offset; | 348 | segment_vaddr.* = @intCast(start_addr); |
| 342 | offset += size; | 349 | memory_ptr = start_addr + size; |
| 343 | memory_ptr += size; | | |
| 344 | } | 350 | } |
| 345 | if (offset > 0) try f.data_segment_groups.append(gpa, offset); | 351 | if (category != .zero) try f.data_segment_groups.append(gpa, @intCast(memory_ptr)); |
| 346 | } | 352 | } |
| 347 | | 353 | |
| 348 | if (shared_memory and any_passive_inits) { | 354 | if (shared_memory and any_passive_inits) { |
| ... | @@ -588,7 +594,8 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { | ... | @@ -588,7 +594,8 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 588 | try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len))); | 594 | try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len))); |
| 589 | try binary_bytes.appendSlice(gpa, name); | 595 | try binary_bytes.appendSlice(gpa, name); |
| 590 | try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.function)); | 596 | try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.function)); |
| 591 | try leb.writeUleb128(binary_writer, @intFromEnum(exp.function_index)); | 597 | const func_index = Wasm.OutputFunctionIndex.fromFunctionIndex(wasm, exp.function_index); |
| | 598 | try leb.writeUleb128(binary_writer, @intFromEnum(func_index)); |
| 592 | } | 599 | } |
| 593 | exports_len += wasm.function_exports.items.len; | 600 | exports_len += wasm.function_exports.items.len; |
| 594 | | 601 | |
| ... | @@ -620,9 +627,9 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { | ... | @@ -620,9 +627,9 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 620 | } | 627 | } |
| 621 | } | 628 | } |
| 622 | | 629 | |
| 623 | if (Wasm.FunctionIndex.fromResolution(wasm, wasm.entry_resolution)) |entry_index| { | 630 | if (Wasm.OutputFunctionIndex.fromResolution(wasm, wasm.entry_resolution)) |func_index| { |
| 624 | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); | 631 | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); |
| 625 | replaceVecSectionHeader(binary_bytes, header_offset, .start, @intFromEnum(entry_index)); | 632 | replaceVecSectionHeader(binary_bytes, header_offset, .start, @intFromEnum(func_index)); |
| 626 | } | 633 | } |
| 627 | | 634 | |
| 628 | // element section (function table) | 635 | // element section (function table) |
| ... | @@ -720,28 +727,41 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { | ... | @@ -720,28 +727,41 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 720 | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); | 727 | const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); |
| 721 | | 728 | |
| 722 | var group_index: u32 = 0; | 729 | var group_index: u32 = 0; |
| 723 | var offset: u32 = undefined; | 730 | var segment_offset: u32 = 0; |
| 724 | for (segment_ids, segment_offsets) |segment_id, segment_offset| { | 731 | var group_start_addr: u32 = data_vaddr; |
| 725 | if (segment_id.isEmpty(wasm)) { | 732 | var group_end_addr = f.data_segment_groups.items[group_index]; |
| 726 | // It counted for virtual memory but it does not go into the binary. | 733 | for (segment_ids, segment_vaddrs) |segment_id, segment_vaddr| { |
| 727 | continue; | 734 | if (segment_vaddr >= group_end_addr) { |
| | 735 | try binary_bytes.appendNTimes(gpa, 0, group_end_addr - group_start_addr - segment_offset); |
| | 736 | group_index += 1; |
| | 737 | if (group_index >= f.data_segment_groups.items.len) { |
| | 738 | // All remaining segments are zero. |
| | 739 | break; |
| | 740 | } |
| | 741 | group_start_addr = group_end_addr; |
| | 742 | group_end_addr = f.data_segment_groups.items[group_index]; |
| | 743 | segment_offset = 0; |
| 728 | } | 744 | } |
| 729 | if (segment_offset == 0) { | 745 | if (segment_offset == 0) { |
| 730 | const group_size = f.data_segment_groups.items[group_index]; | 746 | const group_size = group_end_addr - group_start_addr; |
| 731 | group_index += 1; | 747 | log.debug("emit data section group, {d} bytes", .{group_size}); |
| 732 | offset = 0; | | |
| 733 | | | |
| 734 | const flags: Object.DataSegmentFlags = if (segment_id.isPassive(wasm)) .passive else .active; | 748 | const flags: Object.DataSegmentFlags = if (segment_id.isPassive(wasm)) .passive else .active; |
| 735 | try leb.writeUleb128(binary_writer, @intFromEnum(flags)); | 749 | try leb.writeUleb128(binary_writer, @intFromEnum(flags)); |
| 736 | // when a segment is passive, it's initialized during runtime. | 750 | // Passive segments are initialized at runtime. |
| 737 | if (flags != .passive) { | 751 | if (flags != .passive) { |
| 738 | try emitInit(binary_writer, .{ .i32_const = @as(i32, @bitCast(segment_offset)) }); | 752 | try emitInit(binary_writer, .{ .i32_const = @as(i32, @bitCast(segment_offset)) }); |
| 739 | } | 753 | } |
| 740 | try leb.writeUleb128(binary_writer, group_size); | 754 | try leb.writeUleb128(binary_writer, group_size); |
| 741 | } | 755 | } |
| | 756 | if (segment_id.isEmpty(wasm)) { |
| | 757 | // It counted for virtual memory but it does not go into the binary. |
| | 758 | continue; |
| | 759 | } |
| 742 | | 760 | |
| 743 | try binary_bytes.appendNTimes(gpa, 0, segment_offset - offset); | 761 | // Padding for alignment. |
| 744 | offset = segment_offset; | 762 | const needed_offset = segment_vaddr - group_start_addr; |
| | 763 | try binary_bytes.appendNTimes(gpa, 0, needed_offset - segment_offset); |
| | 764 | segment_offset = needed_offset; |
| 745 | | 765 | |
| 746 | const code_start = binary_bytes.items.len; | 766 | const code_start = binary_bytes.items.len; |
| 747 | append: { | 767 | append: { |
| ... | @@ -768,7 +788,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { | ... | @@ -768,7 +788,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { |
| 768 | }; | 788 | }; |
| 769 | try binary_bytes.appendSlice(gpa, code.slice(wasm)); | 789 | try binary_bytes.appendSlice(gpa, code.slice(wasm)); |
| 770 | } | 790 | } |
| 771 | offset += @intCast(binary_bytes.items.len - code_start); | 791 | segment_offset += @intCast(binary_bytes.items.len - code_start); |
| 772 | } | 792 | } |
| 773 | assert(group_index == f.data_segment_groups.items.len); | 793 | assert(group_index == f.data_segment_groups.items.len); |
| 774 | | 794 | |
| ... | @@ -1111,12 +1131,17 @@ fn splitSegmentName(name: []const u8) struct { []const u8, []const u8 } { | ... | @@ -1111,12 +1131,17 @@ fn splitSegmentName(name: []const u8) struct { []const u8, []const u8 } { |
| 1111 | return .{ name[0..pivot], name[pivot..] }; | 1131 | return .{ name[0..pivot], name[pivot..] }; |
| 1112 | } | 1132 | } |
| 1113 | | 1133 | |
| 1114 | fn wantSegmentMerge(wasm: *const Wasm, a_id: Wasm.DataSegment.Id, b_id: Wasm.DataSegment.Id) bool { | 1134 | fn wantSegmentMerge( |
| | 1135 | wasm: *const Wasm, |
| | 1136 | a_id: Wasm.DataSegment.Id, |
| | 1137 | b_id: Wasm.DataSegment.Id, |
| | 1138 | b_category: Wasm.DataSegment.Category, |
| | 1139 | ) bool { |
| 1115 | const a_category = a_id.category(wasm); | 1140 | const a_category = a_id.category(wasm); |
| 1116 | const b_category = b_id.category(wasm); | | |
| 1117 | if (a_category != b_category) return false; | 1141 | if (a_category != b_category) return false; |
| 1118 | if (a_category == .tls or b_category == .tls) return false; | 1142 | if (a_category == .tls or b_category == .tls) return false; |
| 1119 | if (a_id.isPassive(wasm) != b_id.isPassive(wasm)) return false; | 1143 | if (a_id.isPassive(wasm) != b_id.isPassive(wasm)) return false; |
| | 1144 | if (b_category == .zero) return true; |
| 1120 | const a_name = a_id.name(wasm); | 1145 | const a_name = a_id.name(wasm); |
| 1121 | const b_name = b_id.name(wasm); | 1146 | const b_name = b_id.name(wasm); |
| 1122 | const a_prefix, _ = splitSegmentName(a_name); | 1147 | const a_prefix, _ = splitSegmentName(a_name); |