link.Wasm: update for new writer API

This commit is contained in:
Andrew Kelley 2025-08-11 23:34:33 -07:00
parent 57dbc9e74a
commit c8b983c59a

View file

@ -19,6 +19,7 @@ const mem = std.mem;
const leb = std.leb; const leb = std.leb;
const log = std.log.scoped(.link); const log = std.log.scoped(.link);
const assert = std.debug.assert; const assert = std.debug.assert;
const ArrayList = std.ArrayList;
/// Ordered list of data segments that will appear in the final binary. /// Ordered list of data segments that will appear in the final binary.
/// When sorted, to-be-merged segments will be made adjacent. /// 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 /// 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. /// 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. /// 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, missing_exports: std.AutoArrayHashMapUnmanaged(String, void) = .empty,
function_imports: std.AutoArrayHashMapUnmanaged(String, Wasm.FunctionImportId) = .empty, function_imports: std.AutoArrayHashMapUnmanaged(String, Wasm.FunctionImportId) = .empty,
global_imports: std.AutoArrayHashMapUnmanaged(String, Wasm.GlobalImportId) = .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); try binary_bytes.appendSlice(gpa, &std.wasm.magic ++ &std.wasm.version);
assert(binary_bytes.items.len == 8); assert(binary_bytes.items.len == 8);
const binary_writer = binary_bytes.writer(gpa);
// Type section. // Type section.
for (f.function_imports.values()) |id| { for (f.function_imports.values()) |id| {
try f.func_types.put(gpa, id.functionType(wasm), {}); 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); const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
for (f.func_types.keys()) |func_type_index| { for (f.func_types.keys()) |func_type_index| {
const func_type = func_type_index.ptr(wasm); 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); 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| { 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); 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| { 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)); 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| { for (f.function_imports.values()) |id| {
const module_name = id.moduleName(wasm).slice(wasm).?; const module_name = id.moduleName(wasm).slice(wasm).?;
try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len))); try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(module_name.len)));
try binary_writer.writeAll(module_name); try binary_bytes.appendSlice(gpa, module_name);
const name = id.importName(wasm).slice(wasm); const name = id.importName(wasm).slice(wasm);
try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len))); try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len)));
try binary_writer.writeAll(name); 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); 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; total_imports += f.function_imports.entries.len;
for (wasm.table_imports.values()) |id| { for (wasm.table_imports.values()) |id| {
const table_import = id.value(wasm); const table_import = id.value(wasm);
const module_name = table_import.module_name.slice(wasm); const module_name = table_import.module_name.slice(wasm);
try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len))); try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(module_name.len)));
try binary_writer.writeAll(module_name); try binary_bytes.appendSlice(gpa, module_name);
const name = table_import.name.slice(wasm); const name = table_import.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_writer.writeAll(name); try binary_bytes.appendSlice(gpa, name);
try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.table)); try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.table));
try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.RefType, table_import.flags.ref_type.to()))); try appendLeb128(gpa, binary_bytes, @intFromEnum(@as(std.wasm.RefType, table_import.flags.ref_type.to())));
try emitLimits(gpa, binary_bytes, table_import.limits()); try emitLimits(gpa, binary_bytes, table_import.limits());
} }
total_imports += wasm.table_imports.entries.len; 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| { for (f.global_imports.values()) |id| {
const module_name = id.moduleName(wasm).slice(wasm).?; const module_name = id.moduleName(wasm).slice(wasm).?;
try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len))); try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(module_name.len)));
try binary_writer.writeAll(module_name); try binary_bytes.appendSlice(gpa, module_name);
const name = id.importName(wasm).slice(wasm); const name = id.importName(wasm).slice(wasm);
try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len))); try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len)));
try binary_writer.writeAll(name); 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); const global_type = id.globalType(wasm);
try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.Valtype, global_type.valtype))); try appendLeb128(gpa, binary_bytes, @intFromEnum(@as(std.wasm.Valtype, global_type.valtype)));
try binary_writer.writeByte(@intFromBool(global_type.mutable)); try binary_bytes.append(gpa, @intFromBool(global_type.mutable));
} }
total_imports += f.global_imports.entries.len; 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); const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
for (wasm.functions.keys()) |function| { for (wasm.functions.keys()) |function| {
const index: FuncTypeIndex = .fromTypeIndex(function.typeIndex(wasm), f); 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())); 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); const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);
for (wasm.tables.keys()) |table| { 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)); 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| { for (wasm.function_exports.keys(), wasm.function_exports.values()) |exp_name, function_index| {
const name = exp_name.slice(wasm); 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.appendSlice(gpa, name);
try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.function)); try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.function));
const func_index = Wasm.OutputFunctionIndex.fromFunctionIndex(wasm, function_index); 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; exports_len += wasm.function_exports.entries.len;
if (wasm.export_table and f.indirect_function_table.entries.len > 0) { if (wasm.export_table and f.indirect_function_table.entries.len > 0) {
const name = "__indirect_function_table"; const name = "__indirect_function_table";
const index: u32 = @intCast(wasm.tables.getIndex(.__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.appendSlice(gpa, name);
try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.table)); 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; exports_len += 1;
} }
if (export_memory) { if (export_memory) {
const name = "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.appendSlice(gpa, name);
try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.memory)); 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; exports_len += 1;
} }
for (wasm.global_exports.items) |exp| { for (wasm.global_exports.items) |exp| {
const name = exp.name.slice(wasm); 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.appendSlice(gpa, name);
try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.global)); 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; 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).?); const table_index: u32 = @intCast(wasm.tables.getIndex(.__indirect_function_table).?);
// passive with implicit 0-index table or set table index manually // passive with implicit 0-index table or set table index manually
const flags: u32 = if (table_index == 0) 0x0 else 0x02; 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) { 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 // We start at index 1, so unresolved function pointers are invalid
try emitInit(binary_writer, .{ .i32_const = 1 }); {
if (flags == 0x02) { var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, binary_bytes);
try leb.writeUleb128(binary_writer, @as(u8, 0)); // represents funcref 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| { 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); replaceVecSectionHeader(binary_bytes, header_offset, .element, 1);
@ -851,7 +854,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
.object_function => |i| { .object_function => |i| {
const ptr = i.ptr(wasm); const ptr = i.ptr(wasm);
const code = ptr.code.slice(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; const code_start = binary_bytes.items.len;
try binary_bytes.appendSlice(gpa, code); try binary_bytes.appendSlice(gpa, code);
if (!is_obj) applyRelocs(binary_bytes.items[code_start..], ptr.offset, ptr.relocations(wasm), wasm); 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; const group_size = group_end_addr - group_start_addr;
log.debug("emit data section group, {d} bytes", .{group_size}); log.debug("emit data section group, {d} bytes", .{group_size});
const flags: Object.DataSegmentFlags = if (segment_id.isPassive(wasm)) .passive else .active; 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. // Passive segments are initialized at runtime.
if (flags != .passive) { 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)) { if (segment_id.isEmpty(wasm)) {
// It counted for virtual memory but it does not go into the binary. // It counted for virtual memory but it does not go into the binary.
@ -1077,7 +1082,7 @@ const VirtualAddrs = struct {
fn emitNameSection( fn emitNameSection(
wasm: *Wasm, wasm: *Wasm,
data_segment_groups: []const DataSegmentGroup, data_segment_groups: []const DataSegmentGroup,
binary_bytes: *std.ArrayListUnmanaged(u8), binary_bytes: *ArrayList(u8),
) !void { ) !void {
const f = &wasm.flush_buffer; const f = &wasm.flush_buffer;
const comp = wasm.base.comp; const comp = wasm.base.comp;
@ -1087,7 +1092,7 @@ fn emitNameSection(
defer writeCustomSectionHeader(binary_bytes, header_offset); defer writeCustomSectionHeader(binary_bytes, header_offset);
const name_name = "name"; 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); try binary_bytes.appendSlice(gpa, name_name);
{ {
@ -1095,18 +1100,18 @@ fn emitNameSection(
defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.function)); 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); 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| { for (f.function_imports.keys(), 0..) |name_index, function_index| {
const name = name_index.slice(wasm); const name = name_index.slice(wasm);
try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(function_index))); try appendLeb128(gpa, binary_bytes, @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(name.len)));
try binary_bytes.appendSlice(gpa, name); try binary_bytes.appendSlice(gpa, name);
} }
for (wasm.functions.keys(), f.function_imports.entries.len..) |resolution, function_index| { for (wasm.functions.keys(), f.function_imports.entries.len..) |resolution, function_index| {
const name = resolution.name(wasm).?; const name = resolution.name(wasm).?;
try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(function_index))); try appendLeb128(gpa, binary_bytes, @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(name.len)));
try binary_bytes.appendSlice(gpa, name); try binary_bytes.appendSlice(gpa, name);
} }
} }
@ -1116,18 +1121,18 @@ fn emitNameSection(
defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.global)); 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); 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| { for (f.global_imports.keys(), 0..) |name_index, global_index| {
const name = name_index.slice(wasm); const name = name_index.slice(wasm);
try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(global_index))); try appendLeb128(gpa, binary_bytes, @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(name.len)));
try binary_bytes.appendSlice(gpa, name); try binary_bytes.appendSlice(gpa, name);
} }
for (wasm.globals.keys(), f.global_imports.entries.len..) |resolution, global_index| { for (wasm.globals.keys(), f.global_imports.entries.len..) |resolution, global_index| {
const name = resolution.name(wasm).?; const name = resolution.name(wasm).?;
try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(global_index))); try appendLeb128(gpa, binary_bytes, @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(name.len)));
try binary_bytes.appendSlice(gpa, name); try binary_bytes.appendSlice(gpa, name);
} }
} }
@ -1137,12 +1142,12 @@ fn emitNameSection(
defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.data_segment)); defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.data_segment));
const total_data_segments: u32 = @intCast(data_segment_groups.len); 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| { for (data_segment_groups, 0..) |group, i| {
const name, _ = splitSegmentName(group.first_segment.name(wasm)); const name, _ = splitSegmentName(group.first_segment.name(wasm));
try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(i))); try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(i)));
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.appendSlice(gpa, name);
} }
} }
@ -1150,7 +1155,7 @@ fn emitNameSection(
fn emitFeaturesSection( fn emitFeaturesSection(
gpa: Allocator, gpa: Allocator,
binary_bytes: *std.ArrayListUnmanaged(u8), binary_bytes: *ArrayList(u8),
target: *const std.Target, target: *const std.Target,
) Allocator.Error!void { ) Allocator.Error!void {
const feature_count = target.cpu.features.count(); const feature_count = target.cpu.features.count();
@ -1159,87 +1164,84 @@ fn emitFeaturesSection(
const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes); const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
defer writeCustomSectionHeader(binary_bytes, header_offset); defer writeCustomSectionHeader(binary_bytes, header_offset);
const writer = binary_bytes.writer(gpa);
const target_features = "target_features"; const target_features = "target_features";
try leb.writeUleb128(writer, @as(u32, @intCast(target_features.len))); try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(target_features.len)));
try writer.writeAll(target_features); 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; var safety_count = feature_count;
for (target.cpu.arch.allFeaturesList(), 0..) |*feature, i| { for (target.cpu.arch.allFeaturesList(), 0..) |*feature, i| {
if (!target.cpu.has(.wasm, @as(std.Target.wasm.Feature, @enumFromInt(i)))) continue; if (!target.cpu.has(.wasm, @as(std.Target.wasm.Feature, @enumFromInt(i)))) continue;
safety_count -= 1; 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. // Depends on llvm_name for the hyphenated version that matches wasm tooling conventions.
const name = feature.llvm_name.?; const name = feature.llvm_name.?;
try leb.writeUleb128(writer, @as(u32, @intCast(name.len))); try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(name.len)));
try writer.writeAll(name); try binary_bytes.appendSlice(gpa, name);
} }
assert(safety_count == 0); 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); const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
defer writeCustomSectionHeader(binary_bytes, header_offset); defer writeCustomSectionHeader(binary_bytes, header_offset);
const writer = binary_bytes.writer(gpa);
const hdr_build_id = "build_id"; const hdr_build_id = "build_id";
try leb.writeUleb128(writer, @as(u32, @intCast(hdr_build_id.len))); try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(hdr_build_id.len)));
try writer.writeAll(hdr_build_id); try binary_bytes.appendSlice(gpa, hdr_build_id);
try leb.writeUleb128(writer, @as(u32, 1)); try appendLeb128(gpa, binary_bytes, @as(u32, 1));
try leb.writeUleb128(writer, @as(u32, @intCast(build_id.len))); try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(build_id.len)));
try writer.writeAll(build_id); 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); const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);
defer writeCustomSectionHeader(binary_bytes, header_offset); defer writeCustomSectionHeader(binary_bytes, header_offset);
const writer = binary_bytes.writer(gpa);
const producers = "producers"; const producers = "producers";
try leb.writeUleb128(writer, @as(u32, @intCast(producers.len))); try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(producers.len)));
try writer.writeAll(producers); 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 // language field
{ {
const language = "language"; const language = "language";
try leb.writeUleb128(writer, @as(u32, @intCast(language.len))); try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(language.len)));
try writer.writeAll(language); try binary_bytes.appendSlice(gpa, language);
// field_value_count (TODO: Parse object files for producer sections to detect their 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 // versioned name
{ {
try leb.writeUleb128(writer, @as(u32, 3)); // len of "Zig" try appendLeb128(gpa, binary_bytes, @as(u32, 3)); // len of "Zig"
try writer.writeAll("Zig"); try binary_bytes.appendSlice(gpa, "Zig");
try leb.writeUleb128(writer, @as(u32, @intCast(build_options.version.len))); try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(build_options.version.len)));
try writer.writeAll(build_options.version); try binary_bytes.appendSlice(gpa, build_options.version);
} }
} }
// processed-by field // processed-by field
{ {
const processed_by = "processed-by"; const processed_by = "processed-by";
try leb.writeUleb128(writer, @as(u32, @intCast(processed_by.len))); try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(processed_by.len)));
try writer.writeAll(processed_by); try binary_bytes.appendSlice(gpa, processed_by);
// field_value_count (TODO: Parse object files for producer sections to detect other used tools) // 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 // versioned name
{ {
try leb.writeUleb128(writer, @as(u32, 3)); // len of "Zig" try appendLeb128(gpa, binary_bytes, @as(u32, 3)); // len of "Zig"
try writer.writeAll("Zig"); try binary_bytes.appendSlice(gpa, "Zig");
try leb.writeUleb128(writer, @as(u32, @intCast(build_options.version.len))); try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(build_options.version.len)));
try writer.writeAll(build_options.version); 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_reserve_size = 1 + 5 + 5;
const section_header_size = 5 + 1; 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); try bytes.appendNTimes(gpa, 0, section_header_reserve_size);
return @intCast(bytes.items.len - section_header_reserve_size); return @intCast(bytes.items.len - section_header_reserve_size);
} }
fn replaceVecSectionHeader( fn replaceVecSectionHeader(
bytes: *std.ArrayListUnmanaged(u8), bytes: *ArrayList(u8),
offset: u32, offset: u32,
section: std.wasm.Section, section: std.wasm.Section,
n_items: u32, n_items: u32,
) void { ) void {
const size: u32 = @intCast(bytes.items.len - offset - section_header_reserve_size + uleb128size(n_items)); const size: u32 = @intCast(bytes.items.len - offset - section_header_reserve_size + uleb128size(n_items));
var buf: [section_header_reserve_size]u8 = undefined; var buf: [section_header_reserve_size]u8 = undefined;
var fbw = std.io.fixedBufferStream(&buf); var w: std.Io.Writer = .fixed(&buf);
const w = fbw.writer();
w.writeByte(@intFromEnum(section)) catch unreachable; w.writeByte(@intFromEnum(section)) catch unreachable;
leb.writeUleb128(w, size) catch unreachable; w.writeUleb128(size) catch unreachable;
leb.writeUleb128(w, n_items) catch unreachable; w.writeUleb128(n_items) catch unreachable;
bytes.replaceRangeAssumeCapacity(offset, section_header_reserve_size, fbw.getWritten()); 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); try bytes.appendNTimes(gpa, 0, section_header_size);
return @intCast(bytes.items.len - 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 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); const size: u32 = @intCast(bytes.items.len - offset - section_header_size);
var buf: [section_header_size]u8 = undefined; var buf: [section_header_size]u8 = undefined;
var fbw = std.io.fixedBufferStream(&buf); var w: std.Io.Writer = .fixed(&buf);
const w = fbw.writer();
w.writeByte(tag) catch unreachable; w.writeByte(tag) catch unreachable;
leb.writeUleb128(w, size) catch unreachable; w.writeUleb128(size) catch unreachable;
bytes.replaceRangeAssumeCapacity(offset, section_header_size, fbw.getWritten()); bytes.replaceRangeAssumeCapacity(offset, section_header_size, w.buffered());
} }
const max_size_encoding = 5; 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); try bytes.appendNTimes(gpa, 0, max_size_encoding);
return @intCast(bytes.items.len - 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); const size: u32 = @intCast(bytes.items.len - offset - max_size_encoding);
var buf: [max_size_encoding]u8 = undefined; var buf: [max_size_encoding]u8 = undefined;
var fbw = std.io.fixedBufferStream(&buf); var w: std.Io.Writer = .fixed(&buf);
leb.writeUleb128(fbw.writer(), size) catch unreachable; w.writeUleb128(size) catch unreachable;
bytes.replaceRangeAssumeCapacity(offset, max_size_encoding, fbw.getWritten()); bytes.replaceRangeAssumeCapacity(offset, max_size_encoding, w.buffered());
} }
fn emitLimits( fn emitLimits(
gpa: Allocator, gpa: Allocator,
binary_bytes: *std.ArrayListUnmanaged(u8), binary_bytes: *ArrayList(u8),
limits: std.wasm.Limits, limits: std.wasm.Limits,
) Allocator.Error!void { ) Allocator.Error!void {
try binary_bytes.append(gpa, @bitCast(limits.flags)); try binary_bytes.append(gpa, @bitCast(limits.flags));
try leb.writeUleb128(binary_bytes.writer(gpa), limits.min); try appendLeb128(gpa, binary_bytes, limits.min);
if (limits.flags.has_max) try leb.writeUleb128(binary_bytes.writer(gpa), limits.max); if (limits.flags.has_max) try appendLeb128(gpa, binary_bytes, limits.max);
} }
fn emitMemoryImport( fn emitMemoryImport(
wasm: *Wasm, wasm: *Wasm,
binary_bytes: *std.ArrayListUnmanaged(u8), binary_bytes: *ArrayList(u8),
name_index: String, name_index: String,
memory_import: *const Wasm.MemoryImport, memory_import: *const Wasm.MemoryImport,
) Allocator.Error!void { ) Allocator.Error!void {
const gpa = wasm.base.comp.gpa; const gpa = wasm.base.comp.gpa;
const module_name = memory_import.module_name.slice(wasm); 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); try binary_bytes.appendSlice(gpa, module_name);
const name = name_index.slice(wasm); 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.appendSlice(gpa, name);
try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.memory)); try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.memory));
try emitLimits(gpa, binary_bytes, memory_import.limits()); 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) { switch (init_expr) {
.i32_const => |val| { .i32_const => |val| {
try writer.writeByte(@intFromEnum(std.wasm.Opcode.i32_const)); try writer.writeByte(@intFromEnum(std.wasm.Opcode.i32_const));
try leb.writeIleb128(writer, val); try writer.writeSleb128(val);
}, },
.i64_const => |val| { .i64_const => |val| {
try writer.writeByte(@intFromEnum(std.wasm.Opcode.i64_const)); try writer.writeByte(@intFromEnum(std.wasm.Opcode.i64_const));
try leb.writeIleb128(writer, val); try writer.writeSleb128(val);
}, },
.f32_const => |val| { .f32_const => |val| {
try writer.writeByte(@intFromEnum(std.wasm.Opcode.f32_const)); 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| { .global_get => |val| {
try writer.writeByte(@intFromEnum(std.wasm.Opcode.global_get)); 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)); 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 gpa = wasm.base.comp.gpa;
const slice = expr.slice(wasm); const slice = expr.slice(wasm);
try binary_bytes.appendSlice(gpa, slice[0 .. slice.len + 1]); // +1 to include end opcode 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 { fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.array_list.Managed(u8)) !void {
const gpa = wasm.base.comp.gpa; const gpa = wasm.base.comp.gpa;
const writer = binary_bytes.writer(gpa); try appendLeb128(gpa, binary_bytes, @intFromEnum(Wasm.SubsectionType.segment_info));
try leb.writeUleb128(writer, @intFromEnum(Wasm.SubsectionType.segment_info));
const segment_offset = binary_bytes.items.len; 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| { for (wasm.segment_info.values()) |segment_info| {
log.debug("Emit segment: {s} align({d}) flags({b})", .{ log.debug("Emit segment: {s} align({d}) flags({b})", .{
segment_info.name, segment_info.name,
segment_info.alignment, segment_info.alignment,
segment_info.flags, segment_info.flags,
}); });
try leb.writeUleb128(writer, @as(u32, @intCast(segment_info.name.len))); try appendLeb128(gpa, binary_bytes, @as(u32, @intCast(segment_info.name.len)));
try writer.writeAll(segment_info.name); try binary_bytes.appendSlice(gpa, segment_info.name);
try leb.writeUleb128(writer, segment_info.alignment.toLog2Units()); try appendLeb128(gpa, binary_bytes, segment_info.alignment.toLog2Units());
try leb.writeUleb128(writer, segment_info.flags); try appendLeb128(gpa, binary_bytes, segment_info.flags);
} }
var buf: [5]u8 = undefined; var buf: [5]u8 = undefined;
@ -1429,7 +1428,7 @@ fn uleb128size(x: u32) u32 {
fn emitTagNameTable( fn emitTagNameTable(
gpa: Allocator, gpa: Allocator,
code: *std.ArrayListUnmanaged(u8), code: *ArrayList(u8),
tag_name_offs: []const u32, tag_name_offs: []const u32,
tag_name_bytes: []const u8, tag_name_bytes: []const u8,
base: u32, base: u32,
@ -1604,7 +1603,7 @@ fn reloc_leb_type(code: []u8, index: FuncTypeIndex) void {
leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(index)); 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; const gpa = wasm.base.comp.gpa;
try binary_bytes.ensureUnusedCapacity(gpa, 5 + 1); try binary_bytes.ensureUnusedCapacity(gpa, 5 + 1);
@ -1631,7 +1630,7 @@ fn emitCallCtorsFunction(wasm: *const Wasm, binary_bytes: *std.ArrayListUnmanage
fn emitInitMemoryFunction( fn emitInitMemoryFunction(
wasm: *const Wasm, wasm: *const Wasm,
binary_bytes: *std.ArrayListUnmanaged(u8), binary_bytes: *ArrayList(u8),
virtual_addrs: *const VirtualAddrs, virtual_addrs: *const VirtualAddrs,
) Allocator.Error!void { ) Allocator.Error!void {
const comp = wasm.base.comp; const comp = wasm.base.comp;
@ -1734,7 +1733,7 @@ fn emitInitMemoryFunction(
// notify any waiters for segment initialization completion // notify any waiters for segment initialization completion
appendReservedI32Const(binary_bytes, flag_address); appendReservedI32Const(binary_bytes, flag_address);
binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const)); 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)); binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix));
appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.AtomicsOpcode.memory_atomic_notify)); appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.AtomicsOpcode.memory_atomic_notify));
appendReservedUleb32(binary_bytes, @as(u32, 2)); // alignment appendReservedUleb32(binary_bytes, @as(u32, 2)); // alignment
@ -1750,7 +1749,7 @@ fn emitInitMemoryFunction(
appendReservedI32Const(binary_bytes, flag_address); appendReservedI32Const(binary_bytes, flag_address);
appendReservedI32Const(binary_bytes, 1); // expected flag value appendReservedI32Const(binary_bytes, 1); // expected flag value
binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_const)); 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)); binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix));
appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.AtomicsOpcode.memory_atomic_wait32)); appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.AtomicsOpcode.memory_atomic_wait32));
appendReservedUleb32(binary_bytes, @as(u32, 2)); // alignment appendReservedUleb32(binary_bytes, @as(u32, 2)); // alignment
@ -1779,7 +1778,7 @@ fn emitInitMemoryFunction(
binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); 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 comp = wasm.base.comp;
const gpa = comp.gpa; 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)); 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); const header_offset = try reserveVecSectionHeader(gpa, bytes);
replaceVecSectionHeader(bytes, header_offset, .start, @intFromEnum(i)); replaceVecSectionHeader(bytes, header_offset, .start, @intFromEnum(i));
} }
fn emitTagNameFunction( fn emitTagNameFunction(
wasm: *Wasm, wasm: *Wasm,
code: *std.ArrayListUnmanaged(u8), code: *ArrayList(u8),
table_base_addr: u32, table_base_addr: u32,
table_index: u32, table_index: u32,
enum_type_ip: InternPool.Index, enum_type_ip: InternPool.Index,
@ -1959,22 +1958,34 @@ fn emitTagNameFunction(
} }
/// Writes an unsigned 32-bit integer as a LEB128-encoded 'i32.const' value. /// 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)); 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. /// 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)); 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 { fn appendReservedUleb32(bytes: *ArrayList(u8), val: u32) void {
leb.writeUleb128(bytes.fixedWriter(), val) catch unreachable; 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); try bytes.ensureUnusedCapacity(gpa, 9);
bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Valtype.i32)); bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Valtype.i32));
bytes.appendAssumeCapacity(mutable); bytes.appendAssumeCapacity(mutable);
@ -1982,3 +1993,19 @@ fn appendGlobal(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8), mutable: u8,
appendReservedUleb32(bytes, val); appendReservedUleb32(bytes, val);
bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); 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,
};
}