authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-09 18:40:01-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-01-15 15:11:36-08:00
log1c4b4fb51604f388bb4355e566aac4ea9fda8960
tree65ec97db826dce98ce9bfa63287691ff913c6310
parentd9d49ce995c555286ba4f6041bcca12477f37296

implement indirect function table for object functions


4 files changed, 91 insertions(+), 27 deletions(-)

src/arch/wasm/CodeGen.zig+2-2
...@@ -1027,7 +1027,7 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {...@@ -1027,7 +1027,7 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {
1027 const ip = &zcu.intern_pool;1027 const ip = &zcu.intern_pool;
1028 if (ip.getNav(nav_ref.nav_index).isExternOrFn(ip)) {1028 if (ip.getNav(nav_ref.nav_index).isExternOrFn(ip)) {
1029 assert(nav_ref.offset == 0);1029 assert(nav_ref.offset == 0);
1030 const gop = try wasm.indirect_function_table.getOrPut(comp.gpa, nav_ref.nav_index);1030 const gop = try wasm.zcu_indirect_function_set.getOrPut(comp.gpa, nav_ref.nav_index);
1031 if (!gop.found_existing) gop.value_ptr.* = {};1031 if (!gop.found_existing) gop.value_ptr.* = {};
1032 try cg.addInst(.{1032 try cg.addInst(.{
1033 .tag = .func_ref,1033 .tag = .func_ref,
...@@ -1056,7 +1056,7 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {...@@ -1056,7 +1056,7 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {
1056 if (ip.isFunctionType(ip.typeOf(uav.ip_index))) {1056 if (ip.isFunctionType(ip.typeOf(uav.ip_index))) {
1057 assert(uav.offset == 0);1057 assert(uav.offset == 0);
1058 const owner_nav = ip.toFunc(uav.ip_index).owner_nav;1058 const owner_nav = ip.toFunc(uav.ip_index).owner_nav;
1059 const gop = try wasm.indirect_function_table.getOrPut(comp.gpa, owner_nav);1059 const gop = try wasm.zcu_indirect_function_set.getOrPut(comp.gpa, owner_nav);
1060 if (!gop.found_existing) gop.value_ptr.* = {};1060 if (!gop.found_existing) gop.value_ptr.* = {};
1061 try cg.addInst(.{1061 try cg.addInst(.{
1062 .tag = .func_ref,1062 .tag = .func_ref,
src/arch/wasm/Mir.zig+1-1
...@@ -615,7 +615,7 @@ pub const Inst = struct {...@@ -615,7 +615,7 @@ pub const Inst = struct {
615 intrinsic: Intrinsic,615 intrinsic: Intrinsic,
616 uav_obj: Wasm.UavsObjIndex,616 uav_obj: Wasm.UavsObjIndex,
617 uav_exe: Wasm.UavsExeIndex,617 uav_exe: Wasm.UavsExeIndex,
618 indirect_function_table_index: Wasm.IndirectFunctionTableIndex,618 indirect_function_table_index: Wasm.ZcuIndirectFunctionSetIndex,
619619
620 comptime {620 comptime {
621 switch (builtin.mode) {621 switch (builtin.mode) {
src/link/Wasm.zig+24-7
...@@ -260,7 +260,9 @@ table_imports: std.AutoArrayHashMapUnmanaged(String, TableImport.Index) = .empty...@@ -260,7 +260,9 @@ table_imports: std.AutoArrayHashMapUnmanaged(String, TableImport.Index) = .empty
260260
261/// All functions that have had their address taken and therefore might be261/// All functions that have had their address taken and therefore might be
262/// called via a `call_indirect` function.262/// called via a `call_indirect` function.
263indirect_function_table: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty,263zcu_indirect_function_set: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty,
264object_indirect_function_import_set: std.AutoArrayHashMapUnmanaged(String, void) = .empty,
265object_indirect_function_set: std.AutoArrayHashMapUnmanaged(ObjectFunctionIndex, void) = .empty,
264266
265error_name_table_ref_count: u32 = 0,267error_name_table_ref_count: u32 = 0,
266268
...@@ -288,8 +290,8 @@ error_name_bytes: std.ArrayListUnmanaged(u8) = .empty,...@@ -288,8 +290,8 @@ error_name_bytes: std.ArrayListUnmanaged(u8) = .empty,
288/// is stored. No need to serialize; trivially reconstructed.290/// is stored. No need to serialize; trivially reconstructed.
289error_name_offs: std.ArrayListUnmanaged(u32) = .empty,291error_name_offs: std.ArrayListUnmanaged(u32) = .empty,
290292
291/// Index into `Wasm.indirect_function_table`.293/// Index into `Wasm.zcu_indirect_function_set`.
292pub const IndirectFunctionTableIndex = enum(u32) {294pub const ZcuIndirectFunctionSetIndex = enum(u32) {
293 _,295 _,
294};296};
295297
...@@ -1312,8 +1314,8 @@ pub const TableImport = extern struct {...@@ -1312,8 +1314,8 @@ pub const TableImport = extern struct {
1312 .unresolved => unreachable,1314 .unresolved => unreachable,
1313 .__indirect_function_table => .{1315 .__indirect_function_table => .{
1314 .flags = .{ .has_max = true, .is_shared = false },1316 .flags = .{ .has_max = true, .is_shared = false },
1315 .min = @intCast(wasm.indirect_function_table.entries.len + 1),1317 .min = @intCast(wasm.flush_buffer.indirect_function_table.entries.len + 1),
1316 .max = @intCast(wasm.indirect_function_table.entries.len + 1),1318 .max = @intCast(wasm.flush_buffer.indirect_function_table.entries.len + 1),
1317 },1319 },
1318 .object_table => |i| i.ptr(wasm).limits(),1320 .object_table => |i| i.ptr(wasm).limits(),
1319 };1321 };
...@@ -3025,7 +3027,10 @@ pub fn deinit(wasm: *Wasm) void {...@@ -3025,7 +3027,10 @@ pub fn deinit(wasm: *Wasm) void {
3025 wasm.out_relocs.deinit(gpa);3027 wasm.out_relocs.deinit(gpa);
3026 wasm.uav_fixups.deinit(gpa);3028 wasm.uav_fixups.deinit(gpa);
3027 wasm.nav_fixups.deinit(gpa);3029 wasm.nav_fixups.deinit(gpa);
3028 wasm.indirect_function_table.deinit(gpa);3030
3031 wasm.zcu_indirect_function_set.deinit(gpa);
3032 wasm.object_indirect_function_import_set.deinit(gpa);
3033 wasm.object_indirect_function_set.deinit(gpa);
30293034
3030 wasm.string_bytes.deinit(gpa);3035 wasm.string_bytes.deinit(gpa);
3031 wasm.string_table.deinit(gpa);3036 wasm.string_table.deinit(gpa);
...@@ -3515,6 +3520,7 @@ fn markDataImport(...@@ -3515,6 +3520,7 @@ fn markDataImport(
3515}3520}
35163521
3517fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.File.FlushError!void {3522fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.File.FlushError!void {
3523 const gpa = wasm.base.comp.gpa;
3518 for (relocs.slice.tags(wasm), relocs.slice.pointees(wasm), relocs.slice.offsets(wasm)) |tag, pointee, offset| {3524 for (relocs.slice.tags(wasm), relocs.slice.pointees(wasm), relocs.slice.offsets(wasm)) |tag, pointee, offset| {
3519 if (offset >= relocs.end) break;3525 if (offset >= relocs.end) break;
3520 switch (tag) {3526 switch (tag) {
...@@ -3522,6 +3528,11 @@ fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.Fil...@@ -3522,6 +3528,11 @@ fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.Fil
3522 .function_import_index_i32,3528 .function_import_index_i32,
3523 .function_import_offset_i32,3529 .function_import_offset_i32,
3524 .function_import_offset_i64,3530 .function_import_offset_i64,
3531 => {
3532 const name = pointee.symbol_name;
3533 const i: FunctionImport.Index = @enumFromInt(wasm.object_function_imports.getIndex(name).?);
3534 try markFunctionImport(wasm, name, i.value(wasm), i);
3535 },
3525 .table_import_index_sleb,3536 .table_import_index_sleb,
3526 .table_import_index_i32,3537 .table_import_index_i32,
3527 .table_import_index_sleb64,3538 .table_import_index_sleb64,
...@@ -3530,6 +3541,7 @@ fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.Fil...@@ -3530,6 +3541,7 @@ fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.Fil
3530 .table_import_index_rel_sleb64,3541 .table_import_index_rel_sleb64,
3531 => {3542 => {
3532 const name = pointee.symbol_name;3543 const name = pointee.symbol_name;
3544 try wasm.object_indirect_function_import_set.put(gpa, name, {});
3533 const i: FunctionImport.Index = @enumFromInt(wasm.object_function_imports.getIndex(name).?);3545 const i: FunctionImport.Index = @enumFromInt(wasm.object_function_imports.getIndex(name).?);
3534 try markFunctionImport(wasm, name, i.value(wasm), i);3546 try markFunctionImport(wasm, name, i.value(wasm), i);
3535 },3547 },
...@@ -3564,13 +3576,18 @@ fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.Fil...@@ -3564,13 +3576,18 @@ fn markRelocations(wasm: *Wasm, relocs: ObjectRelocation.IterableSlice) link.Fil
3564 .function_index_i32,3576 .function_index_i32,
3565 .function_offset_i32,3577 .function_offset_i32,
3566 .function_offset_i64,3578 .function_offset_i64,
3579 => try markFunction(wasm, pointee.function.chaseWeak(wasm)),
3567 .table_index_sleb,3580 .table_index_sleb,
3568 .table_index_i32,3581 .table_index_i32,
3569 .table_index_sleb64,3582 .table_index_sleb64,
3570 .table_index_i64,3583 .table_index_i64,
3571 .table_index_rel_sleb,3584 .table_index_rel_sleb,
3572 .table_index_rel_sleb64,3585 .table_index_rel_sleb64,
3573 => try markFunction(wasm, pointee.function.chaseWeak(wasm)),3586 => {
3587 const function = pointee.function;
3588 try wasm.object_indirect_function_set.put(gpa, function, {});
3589 try markFunction(wasm, function.chaseWeak(wasm));
3590 },
3574 .global_index_leb,3591 .global_index_leb,
3575 .global_index_i32,3592 .global_index_i32,
3576 => try markGlobal(wasm, pointee.global.chaseWeak(wasm)),3593 => try markGlobal(wasm, pointee.global.chaseWeak(wasm)),
src/link/Wasm/Flush.zig+64-17
...@@ -34,9 +34,28 @@ function_imports: std.AutoArrayHashMapUnmanaged(String, Wasm.FunctionImportId) =...@@ -34,9 +34,28 @@ function_imports: std.AutoArrayHashMapUnmanaged(String, Wasm.FunctionImportId) =
34global_imports: std.AutoArrayHashMapUnmanaged(String, Wasm.GlobalImportId) = .empty,34global_imports: std.AutoArrayHashMapUnmanaged(String, Wasm.GlobalImportId) = .empty,
35data_imports: std.AutoArrayHashMapUnmanaged(String, Wasm.DataImportId) = .empty,35data_imports: std.AutoArrayHashMapUnmanaged(String, Wasm.DataImportId) = .empty,
3636
37indirect_function_table: std.AutoArrayHashMapUnmanaged(Wasm.OutputFunctionIndex, void) = .empty,
38
37/// For debug purposes only.39/// For debug purposes only.
38memory_layout_finished: bool = false,40memory_layout_finished: bool = false,
3941
42/// Index into `indirect_function_table`.
43const IndirectFunctionTableIndex = enum(u32) {
44 _,
45
46 fn fromObjectFunctionHandlingWeak(wasm: *const Wasm, index: Wasm.ObjectFunctionIndex) IndirectFunctionTableIndex {
47 return fromOutputFunctionIndex(&wasm.flush_buffer, .fromObjectFunctionHandlingWeak(wasm, index));
48 }
49
50 fn fromSymbolName(wasm: *const Wasm, name: String) IndirectFunctionTableIndex {
51 return fromOutputFunctionIndex(&wasm.flush_buffer, .fromSymbolName(wasm, name));
52 }
53
54 fn fromOutputFunctionIndex(f: *const Flush, i: Wasm.OutputFunctionIndex) IndirectFunctionTableIndex {
55 return @enumFromInt(f.indirect_function_table.getIndex(i).?);
56 }
57};
58
40const DataSegmentGroup = struct {59const DataSegmentGroup = struct {
41 first_segment: Wasm.DataSegmentId,60 first_segment: Wasm.DataSegmentId,
42 end_addr: u32,61 end_addr: u32,
...@@ -46,6 +65,7 @@ pub fn clear(f: *Flush) void {...@@ -46,6 +65,7 @@ pub fn clear(f: *Flush) void {
46 f.data_segments.clearRetainingCapacity();65 f.data_segments.clearRetainingCapacity();
47 f.data_segment_groups.clearRetainingCapacity();66 f.data_segment_groups.clearRetainingCapacity();
48 f.binary_bytes.clearRetainingCapacity();67 f.binary_bytes.clearRetainingCapacity();
68 f.indirect_function_table.clearRetainingCapacity();
49 f.memory_layout_finished = false;69 f.memory_layout_finished = false;
50}70}
5171
...@@ -57,6 +77,7 @@ pub fn deinit(f: *Flush, gpa: Allocator) void {...@@ -57,6 +77,7 @@ pub fn deinit(f: *Flush, gpa: Allocator) void {
57 f.function_imports.deinit(gpa);77 f.function_imports.deinit(gpa);
58 f.global_imports.deinit(gpa);78 f.global_imports.deinit(gpa);
59 f.data_imports.deinit(gpa);79 f.data_imports.deinit(gpa);
80 f.indirect_function_table.deinit(gpa);
60 f.* = undefined;81 f.* = undefined;
61}82}
6283
...@@ -156,6 +177,17 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -156,6 +177,17 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
156177
157 if (diags.hasErrors()) return error.LinkFailure;178 if (diags.hasErrors()) return error.LinkFailure;
158179
180 // Merge indirect function tables.
181 try f.indirect_function_table.ensureUnusedCapacity(gpa, wasm.zcu_indirect_function_set.entries.len +
182 wasm.object_indirect_function_import_set.entries.len + wasm.object_indirect_function_set.entries.len);
183 // This one goes first so the indexes can be stable for MIR lowering.
184 for (wasm.zcu_indirect_function_set.keys()) |nav_index|
185 f.indirect_function_table.putAssumeCapacity(.fromIpNav(wasm, nav_index), {});
186 for (wasm.object_indirect_function_import_set.keys()) |symbol_name|
187 f.indirect_function_table.putAssumeCapacity(.fromSymbolName(wasm, symbol_name), {});
188 for (wasm.object_indirect_function_set.keys()) |object_function_index|
189 f.indirect_function_table.putAssumeCapacity(.fromObjectFunction(wasm, object_function_index), {});
190
159 // TODO only include init functions for objects with must_link=true or191 // TODO only include init functions for objects with must_link=true or
160 // which have any alive functions inside them.192 // which have any alive functions inside them.
161 if (wasm.object_init_funcs.items.len > 0) {193 if (wasm.object_init_funcs.items.len > 0) {
...@@ -213,7 +245,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -213,7 +245,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
213245
214 try wasm.tables.ensureUnusedCapacity(gpa, 1);246 try wasm.tables.ensureUnusedCapacity(gpa, 1);
215247
216 if (wasm.indirect_function_table.entries.len > 0) {248 if (f.indirect_function_table.entries.len > 0) {
217 wasm.tables.putAssumeCapacity(.__indirect_function_table, {});249 wasm.tables.putAssumeCapacity(.__indirect_function_table, {});
218 }250 }
219251
...@@ -634,7 +666,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -634,7 +666,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
634 }666 }
635667
636 // element section668 // element section
637 if (wasm.indirect_function_table.entries.len > 0) {669 if (f.indirect_function_table.entries.len > 0) {
638 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);670 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
639671
640 // indirect function table elements672 // indirect function table elements
...@@ -650,9 +682,8 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -650,9 +682,8 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
650 if (flags == 0x02) {682 if (flags == 0x02) {
651 try leb.writeUleb128(binary_writer, @as(u8, 0)); // represents funcref683 try leb.writeUleb128(binary_writer, @as(u8, 0)); // represents funcref
652 }684 }
653 try leb.writeUleb128(binary_writer, @as(u32, @intCast(wasm.indirect_function_table.entries.len)));685 try leb.writeUleb128(binary_writer, @as(u32, @intCast(f.indirect_function_table.entries.len)));
654 for (wasm.indirect_function_table.keys()) |nav_index| {686 for (f.indirect_function_table.keys()) |func_index| {
655 const func_index: Wasm.OutputFunctionIndex = .fromIpNav(wasm, nav_index);
656 try leb.writeUleb128(binary_writer, @intFromEnum(func_index));687 try leb.writeUleb128(binary_writer, @intFromEnum(func_index));
657 }688 }
658689
...@@ -1459,23 +1490,23 @@ fn applyRelocs(code: []u8, code_offset: u32, relocs: Wasm.ObjectRelocation.Itera...@@ -1459,23 +1490,23 @@ fn applyRelocs(code: []u8, code_offset: u32, relocs: Wasm.ObjectRelocation.Itera
1459 .function_index_leb => reloc_leb_function(sliced_code, .fromObjectFunctionHandlingWeak(wasm, pointee.function)),1490 .function_index_leb => reloc_leb_function(sliced_code, .fromObjectFunctionHandlingWeak(wasm, pointee.function)),
1460 .function_offset_i32 => @panic("TODO this value is not known yet"),1491 .function_offset_i32 => @panic("TODO this value is not known yet"),
1461 .function_offset_i64 => @panic("TODO this value is not known yet"),1492 .function_offset_i64 => @panic("TODO this value is not known yet"),
1462 .table_index_i32 => @panic("TODO indirect function table needs to support object functions too"),1493 .table_index_i32 => reloc_u32_table_index(sliced_code, .fromObjectFunctionHandlingWeak(wasm, pointee.function)),
1463 .table_index_i64 => @panic("TODO indirect function table needs to support object functions too"),1494 .table_index_i64 => reloc_u64_table_index(sliced_code, .fromObjectFunctionHandlingWeak(wasm, pointee.function)),
1464 .table_index_rel_sleb => @panic("TODO indirect function table needs to support object functions too"),1495 .table_index_rel_sleb => @panic("TODO what does this reloc tag mean?"),
1465 .table_index_rel_sleb64 => @panic("TODO indirect function table needs to support object functions too"),1496 .table_index_rel_sleb64 => @panic("TODO what does this reloc tag mean?"),
1466 .table_index_sleb => @panic("TODO indirect function table needs to support object functions too"),1497 .table_index_sleb => reloc_sleb_table_index(sliced_code, .fromObjectFunctionHandlingWeak(wasm, pointee.function)),
1467 .table_index_sleb64 => @panic("TODO indirect function table needs to support object functions too"),1498 .table_index_sleb64 => reloc_sleb64_table_index(sliced_code, .fromObjectFunctionHandlingWeak(wasm, pointee.function)),
14681499
1469 .function_import_index_i32 => reloc_u32_function(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)),1500 .function_import_index_i32 => reloc_u32_function(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)),
1470 .function_import_index_leb => reloc_leb_function(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)),1501 .function_import_index_leb => reloc_leb_function(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)),
1471 .function_import_offset_i32 => @panic("TODO this value is not known yet"),1502 .function_import_offset_i32 => @panic("TODO this value is not known yet"),
1472 .function_import_offset_i64 => @panic("TODO this value is not known yet"),1503 .function_import_offset_i64 => @panic("TODO this value is not known yet"),
1473 .table_import_index_i32 => @panic("TODO indirect function table needs to support object functions too"),1504 .table_import_index_i32 => reloc_u32_table_index(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)),
1474 .table_import_index_i64 => @panic("TODO indirect function table needs to support object functions too"),1505 .table_import_index_i64 => reloc_u64_table_index(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)),
1475 .table_import_index_rel_sleb => @panic("TODO indirect function table needs to support object functions too"),1506 .table_import_index_rel_sleb => @panic("TODO what does this reloc tag mean?"),
1476 .table_import_index_rel_sleb64 => @panic("TODO indirect function table needs to support object functions too"),1507 .table_import_index_rel_sleb64 => @panic("TODO what does this reloc tag mean?"),
1477 .table_import_index_sleb => @panic("TODO indirect function table needs to support object functions too"),1508 .table_import_index_sleb => reloc_sleb_table_index(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)),
1478 .table_import_index_sleb64 => @panic("TODO indirect function table needs to support object functions too"),1509 .table_import_index_sleb64 => reloc_sleb64_table_index(sliced_code, .fromSymbolName(wasm, pointee.symbol_name)),
14791510
1480 .global_index_i32 => reloc_u32_global(sliced_code, .fromObjectGlobalHandlingWeak(wasm, pointee.global)),1511 .global_index_i32 => reloc_u32_global(sliced_code, .fromObjectGlobalHandlingWeak(wasm, pointee.global)),
1481 .global_index_leb => reloc_leb_global(sliced_code, .fromObjectGlobalHandlingWeak(wasm, pointee.global)),1512 .global_index_leb => reloc_leb_global(sliced_code, .fromObjectGlobalHandlingWeak(wasm, pointee.global)),
...@@ -1517,6 +1548,22 @@ fn applyRelocs(code: []u8, code_offset: u32, relocs: Wasm.ObjectRelocation.Itera...@@ -1517,6 +1548,22 @@ fn applyRelocs(code: []u8, code_offset: u32, relocs: Wasm.ObjectRelocation.Itera
1517 }1548 }
1518}1549}
15191550
1551fn reloc_u32_table_index(code: []u8, i: IndirectFunctionTableIndex) void {
1552 mem.writeInt(u32, code[0..4], @intFromEnum(i), .little);
1553}
1554
1555fn reloc_u64_table_index(code: []u8, i: IndirectFunctionTableIndex) void {
1556 mem.writeInt(u64, code[0..8], @intFromEnum(i), .little);
1557}
1558
1559fn reloc_sleb_table_index(code: []u8, i: IndirectFunctionTableIndex) void {
1560 leb.writeSignedFixed(5, code[0..5], @intFromEnum(i));
1561}
1562
1563fn reloc_sleb64_table_index(code: []u8, i: IndirectFunctionTableIndex) void {
1564 leb.writeSignedFixed(11, code[0..11], @intFromEnum(i));
1565}
1566
1520fn reloc_u32_function(code: []u8, function: Wasm.OutputFunctionIndex) void {1567fn reloc_u32_function(code: []u8, function: Wasm.OutputFunctionIndex) void {
1521 mem.writeInt(u32, code[0..4], @intFromEnum(function), .little);1568 mem.writeInt(u32, code[0..4], @intFromEnum(function), .little);
1522}1569}