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.
This commit is contained in:
Andrew Kelley 2024-12-19 15:15:53 -08:00
parent 4f8a6b0888
commit 389b29fd8c
3 changed files with 80 additions and 28 deletions

View File

@ -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,
}),
},

View File

@ -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),
});
}

View File

@ -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);