From c8b983c59a91441a6cfa134764c6413195e824ad Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 11 Aug 2025 23:34:33 -0700 Subject: [PATCH] link.Wasm: update for new writer API --- src/link/Wasm/Flush.zig | 327 ++++++++++++++++++++++------------------ 1 file changed, 177 insertions(+), 150 deletions(-) diff --git a/src/link/Wasm/Flush.zig b/src/link/Wasm/Flush.zig index cc8df06743..f728a85786 100644 --- a/src/link/Wasm/Flush.zig +++ b/src/link/Wasm/Flush.zig @@ -19,6 +19,7 @@ const mem = std.mem; const leb = std.leb; const log = std.log.scoped(.link); const assert = std.debug.assert; +const ArrayList = std.ArrayList; /// Ordered list of data segments that will appear in the final binary. /// When sorted, to-be-merged segments will be made adjacent. @@ -27,9 +28,9 @@ data_segments: std.AutoArrayHashMapUnmanaged(Wasm.DataSegmentId, u32) = .empty, /// Each time a `data_segment` offset equals zero it indicates a new group, and /// the next element in this array will contain the total merged segment size. /// Value is the virtual memory address of the end of the segment. -data_segment_groups: std.ArrayListUnmanaged(DataSegmentGroup) = .empty, +data_segment_groups: ArrayList(DataSegmentGroup) = .empty, -binary_bytes: std.ArrayListUnmanaged(u8) = .empty, +binary_bytes: ArrayList(u8) = .empty, missing_exports: std.AutoArrayHashMapUnmanaged(String, void) = .empty, function_imports: std.AutoArrayHashMapUnmanaged(String, Wasm.FunctionImportId) = .empty, global_imports: std.AutoArrayHashMapUnmanaged(String, Wasm.GlobalImportId) = .empty, @@ -563,8 +564,6 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { try binary_bytes.appendSlice(gpa, &std.wasm.magic ++ &std.wasm.version); assert(binary_bytes.items.len == 8); - const binary_writer = binary_bytes.writer(gpa); - // Type section. for (f.function_imports.values()) |id| { try f.func_types.put(gpa, id.functionType(wasm), {}); @@ -576,16 +575,16 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); for (f.func_types.keys()) |func_type_index| { const func_type = func_type_index.ptr(wasm); - try leb.writeUleb128(binary_writer, std.wasm.function_type); + try appendLeb128(gpa, binary_bytes, std.wasm.function_type); const params = func_type.params.slice(wasm); - try leb.writeUleb128(binary_writer, @as(u32, @intCast(params.len))); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(params.len))); for (params) |param_ty| { - try leb.writeUleb128(binary_writer, @intFromEnum(param_ty)); + try appendLeb128(gpa, binary_bytes, @intFromEnum(param_ty)); } const returns = func_type.returns.slice(wasm); - try leb.writeUleb128(binary_writer, @as(u32, @intCast(returns.len))); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(returns.len))); for (returns) |ret_ty| { - try leb.writeUleb128(binary_writer, @intFromEnum(ret_ty)); + try appendLeb128(gpa, binary_bytes, @intFromEnum(ret_ty)); } } replaceVecSectionHeader(binary_bytes, header_offset, .type, @intCast(f.func_types.entries.len)); @@ -605,31 +604,31 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { for (f.function_imports.values()) |id| { const module_name = id.moduleName(wasm).slice(wasm).?; - try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len))); - try binary_writer.writeAll(module_name); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(module_name.len))); + try binary_bytes.appendSlice(gpa, module_name); const name = id.importName(wasm).slice(wasm); - try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len))); - try binary_writer.writeAll(name); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); + try binary_bytes.appendSlice(gpa, name); - try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.function)); + try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.function)); const type_index: FuncTypeIndex = .fromTypeIndex(id.functionType(wasm), f); - try leb.writeUleb128(binary_writer, @intFromEnum(type_index)); + try appendLeb128(gpa, binary_bytes, @intFromEnum(type_index)); } total_imports += f.function_imports.entries.len; for (wasm.table_imports.values()) |id| { const table_import = id.value(wasm); const module_name = table_import.module_name.slice(wasm); - try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len))); - try binary_writer.writeAll(module_name); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(module_name.len))); + try binary_bytes.appendSlice(gpa, module_name); const name = table_import.name.slice(wasm); - try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len))); - try binary_writer.writeAll(name); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); + try binary_bytes.appendSlice(gpa, name); - try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.table)); - try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.RefType, table_import.flags.ref_type.to()))); + try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.table)); + try appendLeb128(gpa, binary_bytes, @intFromEnum(@as(std.wasm.RefType, table_import.flags.ref_type.to()))); try emitLimits(gpa, binary_bytes, table_import.limits()); } total_imports += wasm.table_imports.entries.len; @@ -650,17 +649,17 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { for (f.global_imports.values()) |id| { const module_name = id.moduleName(wasm).slice(wasm).?; - try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len))); - try binary_writer.writeAll(module_name); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(module_name.len))); + try binary_bytes.appendSlice(gpa, module_name); const name = id.importName(wasm).slice(wasm); - try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len))); - try binary_writer.writeAll(name); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); + try binary_bytes.appendSlice(gpa, name); - try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.global)); + try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.global)); const global_type = id.globalType(wasm); - try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.Valtype, global_type.valtype))); - try binary_writer.writeByte(@intFromBool(global_type.mutable)); + try appendLeb128(gpa, binary_bytes, @intFromEnum(@as(std.wasm.Valtype, global_type.valtype))); + try binary_bytes.append(gpa, @intFromBool(global_type.mutable)); } total_imports += f.global_imports.entries.len; @@ -677,7 +676,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); for (wasm.functions.keys()) |function| { const index: FuncTypeIndex = .fromTypeIndex(function.typeIndex(wasm), f); - try leb.writeUleb128(binary_writer, @intFromEnum(index)); + try appendLeb128(gpa, binary_bytes, @intFromEnum(index)); } replaceVecSectionHeader(binary_bytes, header_offset, .function, @intCast(wasm.functions.count())); @@ -689,7 +688,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { const header_offset = try reserveVecSectionHeader(gpa, binary_bytes); for (wasm.tables.keys()) |table| { - try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.RefType, table.refType(wasm)))); + try appendLeb128(gpa, binary_bytes, @intFromEnum(@as(std.wasm.RefType, table.refType(wasm)))); try emitLimits(gpa, binary_bytes, table.limits(wasm)); } @@ -743,39 +742,39 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { for (wasm.function_exports.keys(), wasm.function_exports.values()) |exp_name, function_index| { const name = exp_name.slice(wasm); - try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len))); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); try binary_bytes.appendSlice(gpa, name); try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.function)); const func_index = Wasm.OutputFunctionIndex.fromFunctionIndex(wasm, function_index); - try leb.writeUleb128(binary_writer, @intFromEnum(func_index)); + try appendLeb128(gpa, binary_bytes, @intFromEnum(func_index)); } exports_len += wasm.function_exports.entries.len; if (wasm.export_table and f.indirect_function_table.entries.len > 0) { const name = "__indirect_function_table"; const index: u32 = @intCast(wasm.tables.getIndex(.__indirect_function_table).?); - try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len))); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); try binary_bytes.appendSlice(gpa, name); try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.table)); - try leb.writeUleb128(binary_writer, index); + try appendLeb128(gpa, binary_bytes, index); exports_len += 1; } if (export_memory) { const name = "memory"; - try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len))); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); try binary_bytes.appendSlice(gpa, name); try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.memory)); - try leb.writeUleb128(binary_writer, @as(u32, 0)); + try appendLeb128(gpa, binary_bytes, @as(u32, 0)); exports_len += 1; } for (wasm.global_exports.items) |exp| { const name = exp.name.slice(wasm); - try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len))); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); try binary_bytes.appendSlice(gpa, name); try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.global)); - try leb.writeUleb128(binary_writer, @intFromEnum(exp.global_index)); + try appendLeb128(gpa, binary_bytes, @intFromEnum(exp.global_index)); } exports_len += wasm.global_exports.items.len; @@ -802,18 +801,22 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { const table_index: u32 = @intCast(wasm.tables.getIndex(.__indirect_function_table).?); // passive with implicit 0-index table or set table index manually const flags: u32 = if (table_index == 0) 0x0 else 0x02; - try leb.writeUleb128(binary_writer, flags); + try appendLeb128(gpa, binary_bytes, flags); if (flags == 0x02) { - try leb.writeUleb128(binary_writer, table_index); + try appendLeb128(gpa, binary_bytes, table_index); } // We start at index 1, so unresolved function pointers are invalid - try emitInit(binary_writer, .{ .i32_const = 1 }); - if (flags == 0x02) { - try leb.writeUleb128(binary_writer, @as(u8, 0)); // represents funcref + { + var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, binary_bytes); + defer binary_bytes.* = aw.toArrayList(); + try emitInit(&aw.writer, .{ .i32_const = 1 }); } - try leb.writeUleb128(binary_writer, @as(u32, @intCast(f.indirect_function_table.entries.len))); + if (flags == 0x02) { + try appendLeb128(gpa, binary_bytes, @as(u8, 0)); // represents funcref + } + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(f.indirect_function_table.entries.len))); for (f.indirect_function_table.keys()) |func_index| { - try leb.writeUleb128(binary_writer, @intFromEnum(func_index)); + try appendLeb128(gpa, binary_bytes, @intFromEnum(func_index)); } replaceVecSectionHeader(binary_bytes, header_offset, .element, 1); @@ -851,7 +854,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { .object_function => |i| { const ptr = i.ptr(wasm); const code = ptr.code.slice(wasm); - try leb.writeUleb128(binary_writer, code.len); + try appendLeb128(gpa, binary_bytes, code.len); const code_start = binary_bytes.items.len; try binary_bytes.appendSlice(gpa, code); if (!is_obj) applyRelocs(binary_bytes.items[code_start..], ptr.offset, ptr.relocations(wasm), wasm); @@ -946,12 +949,14 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void { const group_size = group_end_addr - group_start_addr; log.debug("emit data section group, {d} bytes", .{group_size}); const flags: Object.DataSegmentFlags = if (segment_id.isPassive(wasm)) .passive else .active; - try leb.writeUleb128(binary_writer, @intFromEnum(flags)); + try appendLeb128(gpa, binary_bytes, @intFromEnum(flags)); // Passive segments are initialized at runtime. if (flags != .passive) { - try emitInit(binary_writer, .{ .i32_const = @as(i32, @bitCast(group_start_addr)) }); + var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, binary_bytes); + defer binary_bytes.* = aw.toArrayList(); + try emitInit(&aw.writer, .{ .i32_const = @as(i32, @bitCast(group_start_addr)) }); } - try leb.writeUleb128(binary_writer, group_size); + try appendLeb128(gpa, binary_bytes, group_size); } if (segment_id.isEmpty(wasm)) { // It counted for virtual memory but it does not go into the binary. @@ -1077,7 +1082,7 @@ const VirtualAddrs = struct { fn emitNameSection( wasm: *Wasm, data_segment_groups: []const DataSegmentGroup, - binary_bytes: *std.ArrayListUnmanaged(u8), + binary_bytes: *ArrayList(u8), ) !void { const f = &wasm.flush_buffer; const comp = wasm.base.comp; @@ -1087,7 +1092,7 @@ fn emitNameSection( defer writeCustomSectionHeader(binary_bytes, header_offset); const name_name = "name"; - try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, name_name.len)); + try appendLeb128(gpa, binary_bytes, @as(u32, name_name.len)); try binary_bytes.appendSlice(gpa, name_name); { @@ -1095,18 +1100,18 @@ fn emitNameSection( defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.function)); const total_functions: u32 = @intCast(f.function_imports.entries.len + wasm.functions.entries.len); - try leb.writeUleb128(binary_bytes.writer(gpa), total_functions); + try appendLeb128(gpa, binary_bytes, total_functions); for (f.function_imports.keys(), 0..) |name_index, function_index| { const name = name_index.slice(wasm); - try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(function_index))); - try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len))); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(function_index))); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); try binary_bytes.appendSlice(gpa, name); } for (wasm.functions.keys(), f.function_imports.entries.len..) |resolution, function_index| { const name = resolution.name(wasm).?; - try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(function_index))); - try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len))); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(function_index))); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); try binary_bytes.appendSlice(gpa, name); } } @@ -1116,18 +1121,18 @@ fn emitNameSection( defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.global)); const total_globals: u32 = @intCast(f.global_imports.entries.len + wasm.globals.entries.len); - try leb.writeUleb128(binary_bytes.writer(gpa), total_globals); + try appendLeb128(gpa, binary_bytes, total_globals); for (f.global_imports.keys(), 0..) |name_index, global_index| { const name = name_index.slice(wasm); - try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(global_index))); - try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len))); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(global_index))); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); try binary_bytes.appendSlice(gpa, name); } for (wasm.globals.keys(), f.global_imports.entries.len..) |resolution, global_index| { const name = resolution.name(wasm).?; - try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(global_index))); - try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len))); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(global_index))); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); try binary_bytes.appendSlice(gpa, name); } } @@ -1137,12 +1142,12 @@ fn emitNameSection( defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.data_segment)); const total_data_segments: u32 = @intCast(data_segment_groups.len); - try leb.writeUleb128(binary_bytes.writer(gpa), total_data_segments); + try appendLeb128(gpa, binary_bytes, total_data_segments); for (data_segment_groups, 0..) |group, i| { const name, _ = splitSegmentName(group.first_segment.name(wasm)); - try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(i))); - try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len))); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(i))); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); try binary_bytes.appendSlice(gpa, name); } } @@ -1150,7 +1155,7 @@ fn emitNameSection( fn emitFeaturesSection( gpa: Allocator, - binary_bytes: *std.ArrayListUnmanaged(u8), + binary_bytes: *ArrayList(u8), target: *const std.Target, ) Allocator.Error!void { const feature_count = target.cpu.features.count(); @@ -1159,87 +1164,84 @@ fn emitFeaturesSection( const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes); defer writeCustomSectionHeader(binary_bytes, header_offset); - const writer = binary_bytes.writer(gpa); const target_features = "target_features"; - try leb.writeUleb128(writer, @as(u32, @intCast(target_features.len))); - try writer.writeAll(target_features); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(target_features.len))); + try binary_bytes.appendSlice(gpa, target_features); - try leb.writeUleb128(writer, @as(u32, @intCast(feature_count))); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(feature_count))); var safety_count = feature_count; for (target.cpu.arch.allFeaturesList(), 0..) |*feature, i| { if (!target.cpu.has(.wasm, @as(std.Target.wasm.Feature, @enumFromInt(i)))) continue; safety_count -= 1; - try leb.writeUleb128(writer, @as(u32, '+')); + try appendLeb128(gpa, binary_bytes, @as(u32, '+')); // Depends on llvm_name for the hyphenated version that matches wasm tooling conventions. const name = feature.llvm_name.?; - try leb.writeUleb128(writer, @as(u32, @intCast(name.len))); - try writer.writeAll(name); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); + try binary_bytes.appendSlice(gpa, name); } assert(safety_count == 0); } -fn emitBuildIdSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8), build_id: []const u8) !void { +fn emitBuildIdSection(gpa: Allocator, binary_bytes: *ArrayList(u8), build_id: []const u8) !void { const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes); defer writeCustomSectionHeader(binary_bytes, header_offset); - const writer = binary_bytes.writer(gpa); const hdr_build_id = "build_id"; - try leb.writeUleb128(writer, @as(u32, @intCast(hdr_build_id.len))); - try writer.writeAll(hdr_build_id); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(hdr_build_id.len))); + try binary_bytes.appendSlice(gpa, hdr_build_id); - try leb.writeUleb128(writer, @as(u32, 1)); - try leb.writeUleb128(writer, @as(u32, @intCast(build_id.len))); - try writer.writeAll(build_id); + try appendLeb128(gpa, binary_bytes, @as(u32, 1)); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(build_id.len))); + try binary_bytes.appendSlice(gpa, build_id); } -fn emitProducerSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8)) !void { +fn emitProducerSection(gpa: Allocator, binary_bytes: *ArrayList(u8)) !void { const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes); defer writeCustomSectionHeader(binary_bytes, header_offset); - const writer = binary_bytes.writer(gpa); const producers = "producers"; - try leb.writeUleb128(writer, @as(u32, @intCast(producers.len))); - try writer.writeAll(producers); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(producers.len))); + try binary_bytes.appendSlice(gpa, producers); - try leb.writeUleb128(writer, @as(u32, 2)); // 2 fields: Language + processed-by + try appendLeb128(gpa, binary_bytes, @as(u32, 2)); // 2 fields: Language + processed-by // language field { const language = "language"; - try leb.writeUleb128(writer, @as(u32, @intCast(language.len))); - try writer.writeAll(language); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(language.len))); + try binary_bytes.appendSlice(gpa, language); // field_value_count (TODO: Parse object files for producer sections to detect their language) - try leb.writeUleb128(writer, @as(u32, 1)); + try appendLeb128(gpa, binary_bytes, @as(u32, 1)); // versioned name { - try leb.writeUleb128(writer, @as(u32, 3)); // len of "Zig" - try writer.writeAll("Zig"); + try appendLeb128(gpa, binary_bytes, @as(u32, 3)); // len of "Zig" + try binary_bytes.appendSlice(gpa, "Zig"); - try leb.writeUleb128(writer, @as(u32, @intCast(build_options.version.len))); - try writer.writeAll(build_options.version); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(build_options.version.len))); + try binary_bytes.appendSlice(gpa, build_options.version); } } // processed-by field { const processed_by = "processed-by"; - try leb.writeUleb128(writer, @as(u32, @intCast(processed_by.len))); - try writer.writeAll(processed_by); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(processed_by.len))); + try binary_bytes.appendSlice(gpa, processed_by); // field_value_count (TODO: Parse object files for producer sections to detect other used tools) - try leb.writeUleb128(writer, @as(u32, 1)); + try appendLeb128(gpa, binary_bytes, @as(u32, 1)); // versioned name { - try leb.writeUleb128(writer, @as(u32, 3)); // len of "Zig" - try writer.writeAll("Zig"); + try appendLeb128(gpa, binary_bytes, @as(u32, 3)); // len of "Zig" + try binary_bytes.appendSlice(gpa, "Zig"); - try leb.writeUleb128(writer, @as(u32, @intCast(build_options.version.len))); - try writer.writeAll(build_options.version); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(build_options.version.len))); + try binary_bytes.appendSlice(gpa, build_options.version); } } } @@ -1280,99 +1282,97 @@ fn wantSegmentMerge( const section_header_reserve_size = 1 + 5 + 5; const section_header_size = 5 + 1; -fn reserveVecSectionHeader(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!u32 { +fn reserveVecSectionHeader(gpa: Allocator, bytes: *ArrayList(u8)) Allocator.Error!u32 { try bytes.appendNTimes(gpa, 0, section_header_reserve_size); return @intCast(bytes.items.len - section_header_reserve_size); } fn replaceVecSectionHeader( - bytes: *std.ArrayListUnmanaged(u8), + bytes: *ArrayList(u8), offset: u32, section: std.wasm.Section, n_items: u32, ) void { const size: u32 = @intCast(bytes.items.len - offset - section_header_reserve_size + uleb128size(n_items)); var buf: [section_header_reserve_size]u8 = undefined; - var fbw = std.io.fixedBufferStream(&buf); - const w = fbw.writer(); + var w: std.Io.Writer = .fixed(&buf); w.writeByte(@intFromEnum(section)) catch unreachable; - leb.writeUleb128(w, size) catch unreachable; - leb.writeUleb128(w, n_items) catch unreachable; - bytes.replaceRangeAssumeCapacity(offset, section_header_reserve_size, fbw.getWritten()); + w.writeUleb128(size) catch unreachable; + w.writeUleb128(n_items) catch unreachable; + bytes.replaceRangeAssumeCapacity(offset, section_header_reserve_size, w.buffered()); } -fn reserveCustomSectionHeader(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!u32 { +fn reserveCustomSectionHeader(gpa: Allocator, bytes: *ArrayList(u8)) Allocator.Error!u32 { try bytes.appendNTimes(gpa, 0, section_header_size); return @intCast(bytes.items.len - section_header_size); } -fn writeCustomSectionHeader(bytes: *std.ArrayListUnmanaged(u8), offset: u32) void { +fn writeCustomSectionHeader(bytes: *ArrayList(u8), offset: u32) void { return replaceHeader(bytes, offset, 0); // 0 = 'custom' section } -fn replaceHeader(bytes: *std.ArrayListUnmanaged(u8), offset: u32, tag: u8) void { +fn replaceHeader(bytes: *ArrayList(u8), offset: u32, tag: u8) void { const size: u32 = @intCast(bytes.items.len - offset - section_header_size); var buf: [section_header_size]u8 = undefined; - var fbw = std.io.fixedBufferStream(&buf); - const w = fbw.writer(); + var w: std.Io.Writer = .fixed(&buf); w.writeByte(tag) catch unreachable; - leb.writeUleb128(w, size) catch unreachable; - bytes.replaceRangeAssumeCapacity(offset, section_header_size, fbw.getWritten()); + w.writeUleb128(size) catch unreachable; + bytes.replaceRangeAssumeCapacity(offset, section_header_size, w.buffered()); } const max_size_encoding = 5; -fn reserveSize(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!u32 { +fn reserveSize(gpa: Allocator, bytes: *ArrayList(u8)) Allocator.Error!u32 { try bytes.appendNTimes(gpa, 0, max_size_encoding); return @intCast(bytes.items.len - max_size_encoding); } -fn replaceSize(bytes: *std.ArrayListUnmanaged(u8), offset: u32) void { +fn replaceSize(bytes: *ArrayList(u8), offset: u32) void { const size: u32 = @intCast(bytes.items.len - offset - max_size_encoding); var buf: [max_size_encoding]u8 = undefined; - var fbw = std.io.fixedBufferStream(&buf); - leb.writeUleb128(fbw.writer(), size) catch unreachable; - bytes.replaceRangeAssumeCapacity(offset, max_size_encoding, fbw.getWritten()); + var w: std.Io.Writer = .fixed(&buf); + w.writeUleb128(size) catch unreachable; + bytes.replaceRangeAssumeCapacity(offset, max_size_encoding, w.buffered()); } fn emitLimits( gpa: Allocator, - binary_bytes: *std.ArrayListUnmanaged(u8), + binary_bytes: *ArrayList(u8), limits: std.wasm.Limits, ) Allocator.Error!void { try binary_bytes.append(gpa, @bitCast(limits.flags)); - try leb.writeUleb128(binary_bytes.writer(gpa), limits.min); - if (limits.flags.has_max) try leb.writeUleb128(binary_bytes.writer(gpa), limits.max); + try appendLeb128(gpa, binary_bytes, limits.min); + if (limits.flags.has_max) try appendLeb128(gpa, binary_bytes, limits.max); } fn emitMemoryImport( wasm: *Wasm, - binary_bytes: *std.ArrayListUnmanaged(u8), + binary_bytes: *ArrayList(u8), name_index: String, memory_import: *const Wasm.MemoryImport, ) Allocator.Error!void { const gpa = wasm.base.comp.gpa; const module_name = memory_import.module_name.slice(wasm); - try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(module_name.len))); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(module_name.len))); try binary_bytes.appendSlice(gpa, module_name); const name = name_index.slice(wasm); - try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len))); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len))); try binary_bytes.appendSlice(gpa, name); try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.memory)); try emitLimits(gpa, binary_bytes, memory_import.limits()); } -pub fn emitInit(writer: anytype, init_expr: std.wasm.InitExpression) !void { +fn emitInit(writer: *std.Io.Writer, init_expr: std.wasm.InitExpression) !void { switch (init_expr) { .i32_const => |val| { try writer.writeByte(@intFromEnum(std.wasm.Opcode.i32_const)); - try leb.writeIleb128(writer, val); + try writer.writeSleb128(val); }, .i64_const => |val| { try writer.writeByte(@intFromEnum(std.wasm.Opcode.i64_const)); - try leb.writeIleb128(writer, val); + try writer.writeSleb128(val); }, .f32_const => |val| { try writer.writeByte(@intFromEnum(std.wasm.Opcode.f32_const)); @@ -1384,13 +1384,13 @@ pub fn emitInit(writer: anytype, init_expr: std.wasm.InitExpression) !void { }, .global_get => |val| { try writer.writeByte(@intFromEnum(std.wasm.Opcode.global_get)); - try leb.writeUleb128(writer, val); + try writer.writeUleb128(val); }, } try writer.writeByte(@intFromEnum(std.wasm.Opcode.end)); } -pub fn emitExpr(wasm: *const Wasm, binary_bytes: *std.ArrayListUnmanaged(u8), expr: Wasm.Expr) Allocator.Error!void { +pub fn emitExpr(wasm: *const Wasm, binary_bytes: *ArrayList(u8), expr: Wasm.Expr) Allocator.Error!void { const gpa = wasm.base.comp.gpa; const slice = expr.slice(wasm); try binary_bytes.appendSlice(gpa, slice[0 .. slice.len + 1]); // +1 to include end opcode @@ -1398,21 +1398,20 @@ pub fn emitExpr(wasm: *const Wasm, binary_bytes: *std.ArrayListUnmanaged(u8), ex fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.array_list.Managed(u8)) !void { const gpa = wasm.base.comp.gpa; - const writer = binary_bytes.writer(gpa); - try leb.writeUleb128(writer, @intFromEnum(Wasm.SubsectionType.segment_info)); + try appendLeb128(gpa, binary_bytes, @intFromEnum(Wasm.SubsectionType.segment_info)); const segment_offset = binary_bytes.items.len; - try leb.writeUleb128(writer, @as(u32, @intCast(wasm.segment_info.count()))); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(wasm.segment_info.count()))); for (wasm.segment_info.values()) |segment_info| { log.debug("Emit segment: {s} align({d}) flags({b})", .{ segment_info.name, segment_info.alignment, segment_info.flags, }); - try leb.writeUleb128(writer, @as(u32, @intCast(segment_info.name.len))); - try writer.writeAll(segment_info.name); - try leb.writeUleb128(writer, segment_info.alignment.toLog2Units()); - try leb.writeUleb128(writer, segment_info.flags); + try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(segment_info.name.len))); + try binary_bytes.appendSlice(gpa, segment_info.name); + try appendLeb128(gpa, binary_bytes, segment_info.alignment.toLog2Units()); + try appendLeb128(gpa, binary_bytes, segment_info.flags); } var buf: [5]u8 = undefined; @@ -1429,7 +1428,7 @@ fn uleb128size(x: u32) u32 { fn emitTagNameTable( gpa: Allocator, - code: *std.ArrayListUnmanaged(u8), + code: *ArrayList(u8), tag_name_offs: []const u32, tag_name_bytes: []const u8, base: u32, @@ -1604,7 +1603,7 @@ fn reloc_leb_type(code: []u8, index: FuncTypeIndex) void { leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(index)); } -fn emitCallCtorsFunction(wasm: *const Wasm, binary_bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!void { +fn emitCallCtorsFunction(wasm: *const Wasm, binary_bytes: *ArrayList(u8)) Allocator.Error!void { const gpa = wasm.base.comp.gpa; try binary_bytes.ensureUnusedCapacity(gpa, 5 + 1); @@ -1631,7 +1630,7 @@ fn emitCallCtorsFunction(wasm: *const Wasm, binary_bytes: *std.ArrayListUnmanage fn emitInitMemoryFunction( wasm: *const Wasm, - binary_bytes: *std.ArrayListUnmanaged(u8), + binary_bytes: *ArrayList(u8), virtual_addrs: *const VirtualAddrs, ) Allocator.Error!void { const comp = wasm.base.comp; @@ -1734,7 +1733,7 @@ fn emitInitMemoryFunction( // notify any waiters for segment initialization completion appendReservedI32Const(binary_bytes, flag_address); binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); - leb.writeIleb128(binary_bytes.fixedWriter(), @as(i32, -1)) catch unreachable; // number of waiters + appendReservedLeb128(binary_bytes, @as(i32, -1)); // number of waiters binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix)); appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.AtomicsOpcode.memory_atomic_notify)); appendReservedUleb32(binary_bytes, @as(u32, 2)); // alignment @@ -1750,7 +1749,7 @@ fn emitInitMemoryFunction( appendReservedI32Const(binary_bytes, flag_address); appendReservedI32Const(binary_bytes, 1); // expected flag value binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_const)); - leb.writeIleb128(binary_bytes.fixedWriter(), @as(i64, -1)) catch unreachable; // timeout + appendReservedLeb128(binary_bytes, @as(i64, -1)); // timeout binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix)); appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.AtomicsOpcode.memory_atomic_wait32)); appendReservedUleb32(binary_bytes, @as(u32, 2)); // alignment @@ -1779,7 +1778,7 @@ fn emitInitMemoryFunction( binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); } -fn emitInitTlsFunction(wasm: *const Wasm, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!void { +fn emitInitTlsFunction(wasm: *const Wasm, bytes: *ArrayList(u8)) Allocator.Error!void { const comp = wasm.base.comp; const gpa = comp.gpa; @@ -1840,14 +1839,14 @@ fn emitInitTlsFunction(wasm: *const Wasm, bytes: *std.ArrayListUnmanaged(u8)) Al bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); } -fn emitStartSection(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8), i: Wasm.OutputFunctionIndex) !void { +fn emitStartSection(gpa: Allocator, bytes: *ArrayList(u8), i: Wasm.OutputFunctionIndex) !void { const header_offset = try reserveVecSectionHeader(gpa, bytes); replaceVecSectionHeader(bytes, header_offset, .start, @intFromEnum(i)); } fn emitTagNameFunction( wasm: *Wasm, - code: *std.ArrayListUnmanaged(u8), + code: *ArrayList(u8), table_base_addr: u32, table_index: u32, enum_type_ip: InternPool.Index, @@ -1959,22 +1958,34 @@ fn emitTagNameFunction( } /// Writes an unsigned 32-bit integer as a LEB128-encoded 'i32.const' value. -fn appendReservedI32Const(bytes: *std.ArrayListUnmanaged(u8), val: u32) void { +fn appendReservedI32Const(bytes: *ArrayList(u8), val: u32) void { bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); - leb.writeIleb128(bytes.fixedWriter(), @as(i32, @bitCast(val))) catch unreachable; + var w: std.Io.Writer = .fromArrayList(bytes); + defer bytes.* = w.toArrayList(); + return w.writeSleb128(val) catch |err| switch (err) { + error.WriteFailed => unreachable, + }; } /// Writes an unsigned 64-bit integer as a LEB128-encoded 'i64.const' value. -fn appendReservedI64Const(bytes: *std.ArrayListUnmanaged(u8), val: u64) void { +fn appendReservedI64Const(bytes: *ArrayList(u8), val: u64) void { bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_const)); - leb.writeIleb128(bytes.fixedWriter(), @as(i64, @bitCast(val))) catch unreachable; + var w: std.Io.Writer = .fromArrayList(bytes); + defer bytes.* = w.toArrayList(); + return w.writeSleb128(val) catch |err| switch (err) { + error.WriteFailed => unreachable, + }; } -fn appendReservedUleb32(bytes: *std.ArrayListUnmanaged(u8), val: u32) void { - leb.writeUleb128(bytes.fixedWriter(), val) catch unreachable; +fn appendReservedUleb32(bytes: *ArrayList(u8), val: u32) void { + var w: std.Io.Writer = .fromArrayList(bytes); + defer bytes.* = w.toArrayList(); + return w.writeUleb128(val) catch |err| switch (err) { + error.WriteFailed => unreachable, + }; } -fn appendGlobal(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8), mutable: u8, val: u32) Allocator.Error!void { +fn appendGlobal(gpa: Allocator, bytes: *ArrayList(u8), mutable: u8, val: u32) Allocator.Error!void { try bytes.ensureUnusedCapacity(gpa, 9); bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Valtype.i32)); bytes.appendAssumeCapacity(mutable); @@ -1982,3 +1993,19 @@ fn appendGlobal(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8), mutable: u8, appendReservedUleb32(bytes, val); bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); } + +fn appendLeb128(gpa: Allocator, bytes: *ArrayList(u8), value: anytype) Allocator.Error!void { + var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, bytes); + defer bytes.* = aw.toArrayList(); + return aw.writer.writeLeb128(value) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + }; +} + +fn appendReservedLeb128(bytes: *ArrayList(u8), value: anytype) void { + var w: std.Io.Writer = .fromArrayList(bytes); + defer bytes.* = w.toArrayList(); + return w.writeLeb128(value) catch |err| switch (err) { + error.WriteFailed => unreachable, + }; +}