authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-12-19 17:21:24-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-15 15:11:36-08:00
log13eef2f8ab806b2b83226073dd19c3c949446da6
treee4651baa1eb41e9ac547e856d7f7c026f6a04dc2
parent5e0d33f00fea077b362f468d89a979aee270df0b

wasm linker: initialize the data segments table in flush

it cannot be done earlier since ids are not stable yet

2 files changed, 45 insertions(+), 37 deletions(-)

src/link/Wasm.zig+18-34
...@@ -235,11 +235,6 @@ global_imports: std.AutoArrayHashMapUnmanaged(String, GlobalImportId) = .empty,...@@ -235,11 +235,6 @@ global_imports: std.AutoArrayHashMapUnmanaged(String, GlobalImportId) = .empty,
235tables: std.AutoArrayHashMapUnmanaged(TableImport.Resolution, void) = .empty,235tables: std.AutoArrayHashMapUnmanaged(TableImport.Resolution, void) = .empty,
236table_imports: std.AutoArrayHashMapUnmanaged(String, TableImport.Index) = .empty,236table_imports: std.AutoArrayHashMapUnmanaged(String, TableImport.Index) = .empty,
237237
238/// Ordered list of data segments that will appear in the final binary.
239/// When sorted, to-be-merged segments will be made adjacent.
240/// Values are offset relative to segment start.
241data_segments: std.AutoArrayHashMapUnmanaged(Wasm.DataSegment.Id, void) = .empty,
242
243error_name_table_ref_count: u32 = 0,238error_name_table_ref_count: u32 = 0,
244239
245/// Set to true if any `GLOBAL_INDEX` relocation is encountered with240/// Set to true if any `GLOBAL_INDEX` relocation is encountered with
...@@ -2360,7 +2355,6 @@ pub fn deinit(wasm: *Wasm) void {...@@ -2360,7 +2355,6 @@ pub fn deinit(wasm: *Wasm) void {
2360 wasm.global_exports.deinit(gpa);2355 wasm.global_exports.deinit(gpa);
2361 wasm.global_imports.deinit(gpa);2356 wasm.global_imports.deinit(gpa);
2362 wasm.table_imports.deinit(gpa);2357 wasm.table_imports.deinit(gpa);
2363 wasm.data_segments.deinit(gpa);
2364 wasm.symbol_table.deinit(gpa);2358 wasm.symbol_table.deinit(gpa);
2365 wasm.out_relocs.deinit(gpa);2359 wasm.out_relocs.deinit(gpa);
2366 wasm.uav_fixups.deinit(gpa);2360 wasm.uav_fixups.deinit(gpa);
...@@ -2416,13 +2410,13 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index...@@ -2416,13 +2410,13 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index
2416 const gpa = comp.gpa;2410 const gpa = comp.gpa;
2417 const is_obj = comp.config.output_mode == .Obj;2411 const is_obj = comp.config.output_mode == .Obj;
24182412
2419 const nav_init = switch (ip.indexToKey(nav.status.resolved.val)) {2413 const nav_init, const chased_nav_index = switch (ip.indexToKey(nav.status.resolved.val)) {
2420 .func => return, // global const which is a function alias2414 .func => return, // global const which is a function alias
2421 .@"extern" => |ext| {2415 .@"extern" => |ext| {
2422 if (is_obj) {2416 if (is_obj) {
2423 assert(!wasm.navs_obj.contains(nav_index));2417 assert(!wasm.navs_obj.contains(ext.owner_nav));
2424 } else {2418 } else {
2425 assert(!wasm.navs_exe.contains(nav_index));2419 assert(!wasm.navs_exe.contains(ext.owner_nav));
2426 }2420 }
2427 const name = try wasm.internString(ext.name.toSlice(ip));2421 const name = try wasm.internString(ext.name.toSlice(ip));
2428 if (ext.lib_name.toSlice(ip)) |ext_name| _ = try wasm.internString(ext_name);2422 if (ext.lib_name.toSlice(ip)) |ext_name| _ = try wasm.internString(ext_name);
...@@ -2436,27 +2430,28 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index...@@ -2436,27 +2430,28 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index
2436 }2430 }
2437 return;2431 return;
2438 },2432 },
2439 .variable => |variable| variable.init,2433 .variable => |variable| .{ variable.init, variable.owner_nav },
2440 else => nav.status.resolved.val,2434 else => .{ nav.status.resolved.val, nav_index },
2441 };2435 };
2442 assert(!wasm.imports.contains(nav_index));2436 log.debug("updateNav {} {}", .{ nav.fqn.fmt(ip), chased_nav_index });
2437 assert(!wasm.imports.contains(chased_nav_index));
24432438
2444 if (nav_init != .none and !Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(zcu)) {2439 if (nav_init != .none and !Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(zcu)) {
2445 if (is_obj) {2440 if (is_obj) {
2446 assert(!wasm.navs_obj.contains(nav_index));2441 assert(!wasm.navs_obj.contains(chased_nav_index));
2447 } else {2442 } else {
2448 assert(!wasm.navs_exe.contains(nav_index));2443 assert(!wasm.navs_exe.contains(chased_nav_index));
2449 }2444 }
2450 return;2445 return;
2451 }2446 }
24522447
2453 if (is_obj) {2448 if (is_obj) {
2454 const zcu_data_starts: ZcuDataStarts = .initObj(wasm);2449 const zcu_data_starts: ZcuDataStarts = .initObj(wasm);
2455 _ = try refNavObj(wasm, nav_index); // Possibly creates an entry in `Wasm.navs_obj`.2450 _ = try refNavObj(wasm, chased_nav_index); // Possibly creates an entry in `Wasm.navs_obj`.
2456 try zcu_data_starts.finishObj(wasm, pt);2451 try zcu_data_starts.finishObj(wasm, pt);
2457 } else {2452 } else {
2458 const zcu_data_starts: ZcuDataStarts = .initExe(wasm);2453 const zcu_data_starts: ZcuDataStarts = .initExe(wasm);
2459 _ = try refNavExe(wasm, nav_index); // Possibly creates an entry in `Wasm.navs_exe`.2454 _ = try refNavExe(wasm, chased_nav_index); // Possibly creates an entry in `Wasm.navs_exe`.
2460 try zcu_data_starts.finishExe(wasm, pt);2455 try zcu_data_starts.finishExe(wasm, pt);
2461 }2456 }
2462}2457}
...@@ -2820,12 +2815,8 @@ pub fn flushModule(...@@ -2820,12 +2815,8 @@ pub fn flushModule(
2820 const globals_end_zcu: u32 = @intCast(wasm.globals.entries.len);2815 const globals_end_zcu: u32 = @intCast(wasm.globals.entries.len);
2821 defer wasm.globals.shrinkRetainingCapacity(globals_end_zcu);2816 defer wasm.globals.shrinkRetainingCapacity(globals_end_zcu);
28222817
2823 const data_segments_end_zcu: u32 = @intCast(wasm.data_segments.entries.len);
2824 defer wasm.data_segments.shrinkRetainingCapacity(data_segments_end_zcu);
2825
2826 wasm.flush_buffer.clear();2818 wasm.flush_buffer.clear();
2827 try wasm.flush_buffer.missing_exports.reinit(gpa, wasm.missing_exports.keys(), &.{});2819 try wasm.flush_buffer.missing_exports.reinit(gpa, wasm.missing_exports.keys(), &.{});
2828 try wasm.flush_buffer.data_segments.reinit(gpa, wasm.data_segments.keys(), &.{});
2829 try wasm.flush_buffer.function_imports.reinit(gpa, wasm.function_imports.keys(), wasm.function_imports.values());2820 try wasm.flush_buffer.function_imports.reinit(gpa, wasm.function_imports.keys(), wasm.function_imports.values());
2830 try wasm.flush_buffer.global_imports.reinit(gpa, wasm.global_imports.keys(), wasm.global_imports.values());2821 try wasm.flush_buffer.global_imports.reinit(gpa, wasm.global_imports.keys(), wasm.global_imports.values());
28312822
...@@ -3427,16 +3418,13 @@ pub fn refUavObj(wasm: *Wasm, ip_index: InternPool.Index) !UavsObjIndex {...@@ -3427,16 +3418,13 @@ pub fn refUavObj(wasm: *Wasm, ip_index: InternPool.Index) !UavsObjIndex {
3427 const comp = wasm.base.comp;3418 const comp = wasm.base.comp;
3428 const gpa = comp.gpa;3419 const gpa = comp.gpa;
3429 assert(comp.config.output_mode == .Obj);3420 assert(comp.config.output_mode == .Obj);
3430 try wasm.data_segments.ensureUnusedCapacity(gpa, 1);
3431 const gop = try wasm.uavs_obj.getOrPut(gpa, ip_index);3421 const gop = try wasm.uavs_obj.getOrPut(gpa, ip_index);
3432 if (!gop.found_existing) gop.value_ptr.* = .{3422 if (!gop.found_existing) gop.value_ptr.* = .{
3433 // Lowering the value is delayed to avoid recursion.3423 // Lowering the value is delayed to avoid recursion.
3434 .code = undefined,3424 .code = undefined,
3435 .relocs = undefined,3425 .relocs = undefined,
3436 };3426 };
3437 const uav_index: UavsObjIndex = @enumFromInt(gop.index);3427 return @enumFromInt(gop.index);
3438 wasm.data_segments.putAssumeCapacity(.pack(wasm, .{ .uav_obj = uav_index }), {});
3439 return uav_index;
3440}3428}
34413429
3442pub fn refUavExe(wasm: *Wasm, ip_index: InternPool.Index) !UavsExeIndex {3430pub fn refUavExe(wasm: *Wasm, ip_index: InternPool.Index) !UavsExeIndex {
...@@ -3453,9 +3441,7 @@ pub fn refUavExe(wasm: *Wasm, ip_index: InternPool.Index) !UavsExeIndex {...@@ -3453,9 +3441,7 @@ pub fn refUavExe(wasm: *Wasm, ip_index: InternPool.Index) !UavsExeIndex {
3453 .count = 1,3441 .count = 1,
3454 };3442 };
3455 }3443 }
3456 const uav_index: UavsExeIndex = @enumFromInt(gop.index);3444 return @enumFromInt(gop.index);
3457 try wasm.data_segments.put(gpa, .pack(wasm, .{ .uav_exe = uav_index }), {});
3458 return uav_index;
3459}3445}
34603446
3461pub fn refNavObj(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsObjIndex {3447pub fn refNavObj(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsObjIndex {
...@@ -3468,9 +3454,7 @@ pub fn refNavObj(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsObjIndex {...@@ -3468,9 +3454,7 @@ pub fn refNavObj(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsObjIndex {
3468 .code = undefined,3454 .code = undefined,
3469 .relocs = undefined,3455 .relocs = undefined,
3470 };3456 };
3471 const navs_obj_index: NavsObjIndex = @enumFromInt(gop.index);3457 return @enumFromInt(gop.index);
3472 try wasm.data_segments.put(gpa, .pack(wasm, .{ .nav_obj = navs_obj_index }), {});
3473 return navs_obj_index;
3474}3458}
34753459
3476pub fn refNavExe(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsExeIndex {3460pub fn refNavExe(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsExeIndex {
...@@ -3487,9 +3471,7 @@ pub fn refNavExe(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsExeIndex {...@@ -3487,9 +3471,7 @@ pub fn refNavExe(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsExeIndex {
3487 .count = 0,3471 .count = 0,
3488 };3472 };
3489 }3473 }
3490 const navs_exe_index: NavsExeIndex = @enumFromInt(gop.index);3474 return @enumFromInt(gop.index);
3491 try wasm.data_segments.put(gpa, .pack(wasm, .{ .nav_exe = navs_exe_index }), {});
3492 return navs_exe_index;
3493}3475}
34943476
3495/// Asserts it is called after `Flush.data_segments` is fully populated and sorted.3477/// Asserts it is called after `Flush.data_segments` is fully populated and sorted.
...@@ -3506,7 +3488,9 @@ pub fn navAddr(wasm: *Wasm, nav_index: InternPool.Nav.Index) u32 {...@@ -3506,7 +3488,9 @@ pub fn navAddr(wasm: *Wasm, nav_index: InternPool.Nav.Index) u32 {
3506 assert(wasm.flush_buffer.memory_layout_finished);3488 assert(wasm.flush_buffer.memory_layout_finished);
3507 const comp = wasm.base.comp;3489 const comp = wasm.base.comp;
3508 assert(comp.config.output_mode != .Obj);3490 assert(comp.config.output_mode != .Obj);
3509 const ds_id: DataSegment.Id = .pack(wasm, .{ .nav_exe = @enumFromInt(wasm.navs_exe.getIndex(nav_index).?) });3491 const navs_exe_index: NavsExeIndex = @enumFromInt(wasm.navs_exe.getIndex(nav_index).?);
3492 log.debug("navAddr {s} {}", .{ navs_exe_index.name(wasm), nav_index });
3493 const ds_id: DataSegment.Id = .pack(wasm, .{ .nav_exe = navs_exe_index });
3510 return wasm.flush_buffer.data_segments.get(ds_id).?;3494 return wasm.flush_buffer.data_segments.get(ds_id).?;
3511}3495}
35123496
src/link/Wasm/Flush.zig+27-3
...@@ -19,6 +19,9 @@ const leb = std.leb;...@@ -19,6 +19,9 @@ const leb = std.leb;
19const log = std.log.scoped(.link);19const log = std.log.scoped(.link);
20const assert = std.debug.assert;20const assert = std.debug.assert;
2121
22/// Ordered list of data segments that will appear in the final binary.
23/// When sorted, to-be-merged segments will be made adjacent.
24/// Values are offset relative to segment start.
22data_segments: std.AutoArrayHashMapUnmanaged(Wasm.DataSegment.Id, u32) = .empty,25data_segments: std.AutoArrayHashMapUnmanaged(Wasm.DataSegment.Id, u32) = .empty,
23/// Each time a `data_segment` offset equals zero it indicates a new group, and26/// Each time a `data_segment` offset equals zero it indicates a new group, and
24/// 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.
...@@ -35,8 +38,9 @@ indirect_function_table: std.AutoArrayHashMapUnmanaged(Wasm.OutputFunctionIndex,...@@ -35,8 +38,9 @@ indirect_function_table: std.AutoArrayHashMapUnmanaged(Wasm.OutputFunctionIndex,
35memory_layout_finished: bool = false,38memory_layout_finished: bool = false,
3639
37pub fn clear(f: *Flush) void {40pub fn clear(f: *Flush) void {
38 f.binary_bytes.clearRetainingCapacity();41 f.data_segments.clearRetainingCapacity();
39 f.data_segment_groups.clearRetainingCapacity();42 f.data_segment_groups.clearRetainingCapacity();
43 f.binary_bytes.clearRetainingCapacity();
40 f.indirect_function_table.clearRetainingCapacity();44 f.indirect_function_table.clearRetainingCapacity();
41 f.memory_layout_finished = false;45 f.memory_layout_finished = false;
42}46}
...@@ -138,12 +142,30 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -138,12 +142,30 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
138142
139 // Merge and order the data segments. Depends on garbage collection so that143 // Merge and order the data segments. Depends on garbage collection so that
140 // unused segments can be omitted.144 // unused segments can be omitted.
141 try f.data_segments.ensureUnusedCapacity(gpa, wasm.object_data_segments.items.len + 1);145 try f.data_segments.ensureUnusedCapacity(gpa, wasm.object_data_segments.items.len +
146 wasm.uavs_obj.entries.len + wasm.navs_obj.entries.len +
147 wasm.uavs_exe.entries.len + wasm.navs_exe.entries.len + 1);
148 if (is_obj) assert(wasm.uavs_exe.entries.len == 0);
149 if (is_obj) assert(wasm.navs_exe.entries.len == 0);
150 if (!is_obj) assert(wasm.uavs_obj.entries.len == 0);
151 if (!is_obj) assert(wasm.navs_obj.entries.len == 0);
152 for (0..wasm.uavs_obj.entries.len) |uavs_index| f.data_segments.putAssumeCapacityNoClobber(.pack(wasm, .{
153 .uav_obj = @enumFromInt(uavs_index),
154 }), @as(u32, undefined));
155 for (0..wasm.navs_obj.entries.len) |navs_index| f.data_segments.putAssumeCapacityNoClobber(.pack(wasm, .{
156 .nav_obj = @enumFromInt(navs_index),
157 }), @as(u32, undefined));
158 for (0..wasm.uavs_exe.entries.len) |uavs_index| f.data_segments.putAssumeCapacityNoClobber(.pack(wasm, .{
159 .uav_exe = @enumFromInt(uavs_index),
160 }), @as(u32, undefined));
161 for (0..wasm.navs_exe.entries.len) |navs_index| f.data_segments.putAssumeCapacityNoClobber(.pack(wasm, .{
162 .nav_exe = @enumFromInt(navs_index),
163 }), @as(u32, undefined));
142 for (wasm.object_data_segments.items, 0..) |*ds, i| {164 for (wasm.object_data_segments.items, 0..) |*ds, i| {
143 if (!ds.flags.alive) continue;165 if (!ds.flags.alive) continue;
144 const data_segment_index: Wasm.ObjectDataSegmentIndex = @enumFromInt(i);166 const data_segment_index: Wasm.ObjectDataSegmentIndex = @enumFromInt(i);
145 any_passive_inits = any_passive_inits or ds.flags.is_passive or (import_memory and !wasm.isBss(ds.name));167 any_passive_inits = any_passive_inits or ds.flags.is_passive or (import_memory and !wasm.isBss(ds.name));
146 f.data_segments.putAssumeCapacityNoClobber(.pack(wasm, .{168 _ = f.data_segments.putAssumeCapacityNoClobber(.pack(wasm, .{
147 .object = data_segment_index,169 .object = data_segment_index,
148 }), @as(u32, undefined));170 }), @as(u32, undefined));
149 }171 }
...@@ -644,6 +666,8 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -644,6 +666,8 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
644 const code_start = try reserveSize(gpa, binary_bytes);666 const code_start = try reserveSize(gpa, binary_bytes);
645 defer replaceSize(binary_bytes, code_start);667 defer replaceSize(binary_bytes, code_start);
646668
669 log.debug("lowering function code for '{s}'", .{resolution.name(wasm).?});
670
647 try i.value(wasm).function.lower(wasm, binary_bytes);671 try i.value(wasm).function.lower(wasm, binary_bytes);
648 },672 },
649 };673 };