Commit 389b29fd8c

Andrew Kelley <andrew@ziglang.org>
2024-12-20 00:15:53
wasm linker: avoid recursion in lowerZcuData
instead of recursion, callers of the function are responsible for checking the respective tables that might have new entries in them and then calling lowerZcuData again.
1 parent 4f8a6b0
Changed files (3)
src/arch/wasm/CodeGen.zig
@@ -1042,9 +1042,9 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {
                 try cg.addInst(.{
                     .tag = .uav_ref,
                     .data = if (is_obj) .{
-                        .uav_obj = try wasm.refUavObj(cg.pt, uav.ip_index),
+                        .uav_obj = try wasm.refUavObj(uav.ip_index),
                     } else .{
-                        .uav_exe = try wasm.refUavExe(cg.pt, uav.ip_index),
+                        .uav_exe = try wasm.refUavExe(uav.ip_index),
                     },
                 });
             } else {
@@ -1052,10 +1052,10 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {
                     .tag = .uav_ref_off,
                     .data = .{
                         .payload = if (is_obj) try cg.addExtra(Mir.UavRefOffObj{
-                            .uav_obj = try wasm.refUavObj(cg.pt, uav.ip_index),
+                            .uav_obj = try wasm.refUavObj(uav.ip_index),
                             .offset = uav.offset,
                         }) else try cg.addExtra(Mir.UavRefOffExe{
-                            .uav_exe = try wasm.refUavExe(cg.pt, uav.ip_index),
+                            .uav_exe = try wasm.refUavExe(uav.ip_index),
                             .offset = uav.offset,
                         }),
                     },
src/link/Wasm.zig
@@ -2364,24 +2364,50 @@ pub fn updateNav(wasm: *Wasm, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index
         return;
     }
 
-    const zcu_data = try lowerZcuData(wasm, pt, nav_init);
-
-    try wasm.data_segments.ensureUnusedCapacity(gpa, 1);
-
     if (is_obj) {
-        const gop = try wasm.navs_obj.getOrPut(gpa, nav_index);
-        gop.value_ptr.* = zcu_data;
-        wasm.data_segments.putAssumeCapacity(.pack(wasm, .{ .nav_obj = @enumFromInt(gop.index) }), {});
+        var uavs_i = wasm.uavs_obj.entries.len;
+        var navs_i = wasm.navs_obj.entries.len;
+        _ = try refNavObj(wasm, nav_index); // Possibly creates an entry in `Wasm.navs_obj`.
+        while (true) {
+            while (navs_i < wasm.navs_obj.entries.len) : (navs_i += 1) {
+                const elem_nav = ip.getNav(wasm.navs_obj.keys()[navs_i]);
+                const elem_nav_init = switch (ip.indexToKey(elem_nav.status.resolved.val)) {
+                    .variable => |variable| variable.init,
+                    else => elem_nav.status.resolved.val,
+                };
+                // Call to `lowerZcuData` here possibly creates more entries in these tables.
+                wasm.navs_obj.values()[navs_i] = try lowerZcuData(wasm, pt, elem_nav_init);
+            }
+            while (uavs_i < wasm.uavs_obj.entries.len) : (uavs_i += 1) {
+                // Call to `lowerZcuData` here possibly creates more entries in these tables.
+                wasm.uavs_obj.values()[uavs_i] = try lowerZcuData(wasm, pt, wasm.uavs_obj.keys()[uavs_i]);
+            }
+            if (navs_i >= wasm.navs_obj.entries.len) break;
+        }
+    } else {
+        var uavs_i = wasm.uavs_exe.entries.len;
+        var navs_i = wasm.navs_exe.entries.len;
+        _ = try refNavExe(wasm, nav_index); // Possibly creates an entry in `Wasm.navs_exe`.
+        while (true) {
+            while (navs_i < wasm.navs_exe.entries.len) : (navs_i += 1) {
+                const elem_nav = ip.getNav(wasm.navs_exe.keys()[navs_i]);
+                const elem_nav_init = switch (ip.indexToKey(elem_nav.status.resolved.val)) {
+                    .variable => |variable| variable.init,
+                    else => elem_nav.status.resolved.val,
+                };
+                // Call to `lowerZcuData` here possibly creates more entries in these tables.
+                const zcu_data = try lowerZcuData(wasm, pt, elem_nav_init);
+                assert(zcu_data.relocs.len == 0);
+                wasm.navs_exe.values()[navs_i].code = zcu_data.code;
+            }
+            while (uavs_i < wasm.uavs_exe.entries.len) : (uavs_i += 1) {
+                // Call to `lowerZcuData` here possibly creates more entries in these tables.
+                const zcu_data = try lowerZcuData(wasm, pt, wasm.uavs_exe.keys()[uavs_i]);
+                wasm.uavs_exe.values()[uavs_i].code = zcu_data.code;
+            }
+            if (navs_i >= wasm.navs_exe.entries.len) break;
+        }
     }
-
-    assert(zcu_data.relocs.len == 0);
-
-    const gop = try wasm.navs_exe.getOrPut(gpa, nav_index);
-    gop.value_ptr.* = .{
-        .code = zcu_data.code,
-        .count = if (gop.found_existing) gop.value_ptr.count else 0,
-    };
-    wasm.data_segments.putAssumeCapacity(.pack(wasm, .{ .nav_exe = @enumFromInt(gop.index) }), {});
 }
 
 pub fn updateLineNumber(wasm: *Wasm, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {
@@ -3346,18 +3372,23 @@ pub fn symbolNameIndex(wasm: *Wasm, name: String) Allocator.Error!SymbolTableInd
     return @enumFromInt(gop.index);
 }
 
-pub fn refUavObj(wasm: *Wasm, pt: Zcu.PerThread, ip_index: InternPool.Index) !UavsObjIndex {
+pub fn refUavObj(wasm: *Wasm, ip_index: InternPool.Index) !UavsObjIndex {
     const comp = wasm.base.comp;
     const gpa = comp.gpa;
     assert(comp.config.output_mode == .Obj);
+    try wasm.data_segments.ensureUnusedCapacity(gpa, 1);
     const gop = try wasm.uavs_obj.getOrPut(gpa, ip_index);
-    if (!gop.found_existing) gop.value_ptr.* = try lowerZcuData(wasm, pt, ip_index);
+    if (!gop.found_existing) gop.value_ptr.* = .{
+        // Lowering the value is delayed to avoid recursion.
+        .code = undefined,
+        .relocs = undefined,
+    };
     const uav_index: UavsObjIndex = @enumFromInt(gop.index);
-    try wasm.data_segments.put(gpa, .pack(wasm, .{ .uav_obj = uav_index }), {});
+    wasm.data_segments.putAssumeCapacity(.pack(wasm, .{ .uav_obj = uav_index }), {});
     return uav_index;
 }
 
-pub fn refUavExe(wasm: *Wasm, pt: Zcu.PerThread, ip_index: InternPool.Index) !UavsExeIndex {
+pub fn refUavExe(wasm: *Wasm, ip_index: InternPool.Index) !UavsExeIndex {
     const comp = wasm.base.comp;
     const gpa = comp.gpa;
     assert(comp.config.output_mode != .Obj);
@@ -3365,9 +3396,9 @@ pub fn refUavExe(wasm: *Wasm, pt: Zcu.PerThread, ip_index: InternPool.Index) !Ua
     if (gop.found_existing) {
         gop.value_ptr.count += 1;
     } else {
-        const zcu_data = try lowerZcuData(wasm, pt, ip_index);
         gop.value_ptr.* = .{
-            .code = zcu_data.code,
+            // Lowering the value is delayed to avoid recursion.
+            .code = undefined,
             .count = 1,
         };
     }
@@ -3376,6 +3407,21 @@ pub fn refUavExe(wasm: *Wasm, pt: Zcu.PerThread, ip_index: InternPool.Index) !Ua
     return uav_index;
 }
 
+pub fn refNavObj(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsObjIndex {
+    const comp = wasm.base.comp;
+    const gpa = comp.gpa;
+    assert(comp.config.output_mode != .Obj);
+    const gop = try wasm.navs_obj.getOrPut(gpa, nav_index);
+    if (!gop.found_existing) gop.value_ptr.* = .{
+        // Lowering the value is delayed to avoid recursion.
+        .code = undefined,
+        .relocs = undefined,
+    };
+    const navs_obj_index: NavsObjIndex = @enumFromInt(gop.index);
+    try wasm.data_segments.put(gpa, .pack(wasm, .{ .nav_obj = navs_obj_index }), {});
+    return navs_obj_index;
+}
+
 pub fn refNavExe(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsExeIndex {
     const comp = wasm.base.comp;
     const gpa = comp.gpa;
@@ -3385,8 +3431,9 @@ pub fn refNavExe(wasm: *Wasm, nav_index: InternPool.Nav.Index) !NavsExeIndex {
         gop.value_ptr.count += 1;
     } else {
         gop.value_ptr.* = .{
+            // Lowering the value is delayed to avoid recursion.
             .code = undefined,
-            .count = 1,
+            .count = 0,
         };
     }
     const navs_exe_index: NavsExeIndex = @enumFromInt(gop.index);
@@ -3481,6 +3528,11 @@ pub fn isBss(wasm: *const Wasm, optional_name: OptionalString) bool {
     return mem.eql(u8, s, ".bss") or mem.startsWith(u8, s, ".bss.");
 }
 
+/// After this function is called, there may be additional entries in
+/// `Wasm.uavs_obj`, `Wasm.uavs_exe`, `Wasm.navs_obj`, and `Wasm.navs_exe`
+/// which have uninitialized code and relocations. This function is
+/// non-recursive, so callers must coordinate additional calls to populate
+/// those entries.
 fn lowerZcuData(wasm: *Wasm, pt: Zcu.PerThread, ip_index: InternPool.Index) !ZcuDataObj {
     const code_start: u32 = @intCast(wasm.string_bytes.items.len);
     const relocs_start: u32 = @intCast(wasm.out_relocs.len);
src/codegen.zig
@@ -676,7 +676,7 @@ fn lowerUavRef(
             } else {
                 try wasm.uav_fixups.ensureUnusedCapacity(gpa, 1);
                 wasm.uav_fixups.appendAssumeCapacity(.{
-                    .uavs_exe_index = try wasm.refUavExe(pt, uav.val),
+                    .uavs_exe_index = try wasm.refUavExe(uav.val),
                     .offset = @intCast(code.items.len),
                 });
             }