authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-08-10 21:39:17-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-08-10 21:39:17-07:00
log168da23d8f556d47ae9e029f70806cbd4bfad431
tree4f6b202adf6fadcc4bbca6263184f51498746520
parent3280fc98f3b0400c4ce0b8c54a157f1858490351

(wip) update wasm linker to new Writer API


6 files changed, 868 insertions(+), 1041 deletions(-)

src/arch/wasm/Emit.zig+103-134
...@@ -4,6 +4,7 @@ const std = @import("std");...@@ -4,6 +4,7 @@ const std = @import("std");
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const Allocator = std.mem.Allocator;5const Allocator = std.mem.Allocator;
6const leb = std.leb;6const leb = std.leb;
7const Writer = std.Io.Writer;
78
8const Wasm = link.File.Wasm;9const Wasm = link.File.Wasm;
9const Mir = @import("Mir.zig");10const Mir = @import("Mir.zig");
...@@ -14,16 +15,16 @@ const codegen = @import("../../codegen.zig");...@@ -14,16 +15,16 @@ const codegen = @import("../../codegen.zig");
1415
15mir: Mir,16mir: Mir,
16wasm: *Wasm,17wasm: *Wasm,
17/// The binary representation that will be emitted by this module.18/// The binary representation of this module is written here.
18code: *std.ArrayListUnmanaged(u8),19writer: *Writer,
1920
20pub const Error = error{21pub const Error = error{
21 OutOfMemory,22 WriteFailed,
22};23};
2324
24pub fn lowerToCode(emit: *Emit) Error!void {25pub fn lowerToCode(emit: *Emit) Error!void {
25 const mir = &emit.mir;26 const mir = &emit.mir;
26 const code = emit.code;27 const writer = emit.writer;
27 const wasm = emit.wasm;28 const wasm = emit.wasm;
28 const comp = wasm.base.comp;29 const comp = wasm.base.comp;
29 const gpa = comp.gpa;30 const gpa = comp.gpa;
...@@ -41,18 +42,19 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -41,18 +42,19 @@ pub fn lowerToCode(emit: *Emit) Error!void {
41 },42 },
42 .block, .loop => {43 .block, .loop => {
43 const block_type = datas[inst].block_type;44 const block_type = datas[inst].block_type;
44 try code.ensureUnusedCapacity(gpa, 2);45 try writer.writeAll(&.{
45 code.appendAssumeCapacity(@intFromEnum(tags[inst]));46 @intFromEnum(tags[inst]),
46 code.appendAssumeCapacity(@intFromEnum(block_type));47 @intFromEnum(block_type),
48 });
4749
48 inst += 1;50 inst += 1;
49 continue :loop tags[inst];51 continue :loop tags[inst];
50 },52 },
51 .uav_ref => {53 .uav_ref => {
52 if (is_obj) {54 if (is_obj) {
53 try uavRefObj(wasm, code, datas[inst].ip_index, 0, is_wasm32);55 try uavRefObj(wasm, writer, datas[inst].ip_index, 0, is_wasm32);
54 } else {56 } else {
55 try uavRefExe(wasm, code, datas[inst].ip_index, 0, is_wasm32);57 try uavRefExe(wasm, writer, datas[inst].ip_index, 0, is_wasm32);
56 }58 }
57 inst += 1;59 inst += 1;
58 continue :loop tags[inst];60 continue :loop tags[inst];
...@@ -60,20 +62,20 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -60,20 +62,20 @@ pub fn lowerToCode(emit: *Emit) Error!void {
60 .uav_ref_off => {62 .uav_ref_off => {
61 const extra = mir.extraData(Mir.UavRefOff, datas[inst].payload).data;63 const extra = mir.extraData(Mir.UavRefOff, datas[inst].payload).data;
62 if (is_obj) {64 if (is_obj) {
63 try uavRefObj(wasm, code, extra.value, extra.offset, is_wasm32);65 try uavRefObj(wasm, writer, extra.value, extra.offset, is_wasm32);
64 } else {66 } else {
65 try uavRefExe(wasm, code, extra.value, extra.offset, is_wasm32);67 try uavRefExe(wasm, writer, extra.value, extra.offset, is_wasm32);
66 }68 }
67 inst += 1;69 inst += 1;
68 continue :loop tags[inst];70 continue :loop tags[inst];
69 },71 },
70 .nav_ref => {72 .nav_ref => {
71 try navRefOff(wasm, code, .{ .nav_index = datas[inst].nav_index, .offset = 0 }, is_wasm32);73 try navRefOff(wasm, writer, .{ .nav_index = datas[inst].nav_index, .offset = 0 }, is_wasm32);
72 inst += 1;74 inst += 1;
73 continue :loop tags[inst];75 continue :loop tags[inst];
74 },76 },
75 .nav_ref_off => {77 .nav_ref_off => {
76 try navRefOff(wasm, code, mir.extraData(Mir.NavRefOff, datas[inst].payload).data, is_wasm32);78 try navRefOff(wasm, writer, mir.extraData(Mir.NavRefOff, datas[inst].payload).data, is_wasm32);
77 inst += 1;79 inst += 1;
78 continue :loop tags[inst];80 continue :loop tags[inst];
79 },81 },
...@@ -81,11 +83,11 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -81,11 +83,11 @@ pub fn lowerToCode(emit: *Emit) Error!void {
81 const indirect_func_idx: Wasm.ZcuIndirectFunctionSetIndex = @enumFromInt(83 const indirect_func_idx: Wasm.ZcuIndirectFunctionSetIndex = @enumFromInt(
82 wasm.zcu_indirect_function_set.getIndex(datas[inst].nav_index).?,84 wasm.zcu_indirect_function_set.getIndex(datas[inst].nav_index).?,
83 );85 );
84 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));86 try writer.writeByte(@intFromEnum(std.wasm.Opcode.i32_const));
85 if (is_obj) {87 if (is_obj) {
86 @panic("TODO");88 @panic("TODO");
87 } else {89 } else {
88 leb.writeUleb128(code.fixedWriter(), 1 + @intFromEnum(indirect_func_idx)) catch unreachable;90 try writer.writeLeb128(1 + @intFromEnum(indirect_func_idx));
89 }91 }
90 inst += 1;92 inst += 1;
91 continue :loop tags[inst];93 continue :loop tags[inst];
...@@ -95,52 +97,48 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -95,52 +97,48 @@ pub fn lowerToCode(emit: *Emit) Error!void {
95 continue :loop tags[inst];97 continue :loop tags[inst];
96 },98 },
97 .errors_len => {99 .errors_len => {
98 try code.ensureUnusedCapacity(gpa, 6);100 try writer.writeByte(@intFromEnum(std.wasm.Opcode.i32_const));
99 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
100 // MIR is lowered during flush, so there is indeed only one thread at this time.101 // MIR is lowered during flush, so there is indeed only one thread at this time.
101 const errors_len = 1 + comp.zcu.?.intern_pool.global_error_set.getNamesFromMainThread().len;102 const errors_len: u32 = @intCast(1 + comp.zcu.?.intern_pool.global_error_set.getNamesFromMainThread().len);
102 leb.writeIleb128(code.fixedWriter(), errors_len) catch unreachable;103 try writer.writeLeb128(@as(i32, @bitCast(errors_len)));
103104
104 inst += 1;105 inst += 1;
105 continue :loop tags[inst];106 continue :loop tags[inst];
106 },107 },
107 .error_name_table_ref => {108 .error_name_table_ref => {
108 wasm.error_name_table_ref_count += 1;109 wasm.error_name_table_ref_count += 1;
109 try code.ensureUnusedCapacity(gpa, 11);
110 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;110 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;
111 code.appendAssumeCapacity(@intFromEnum(opcode));111 try writer.writeByte(@intFromEnum(opcode));
112 if (is_obj) {112 if (is_obj) {
113 try wasm.out_relocs.append(gpa, .{113 try wasm.out_relocs.append(gpa, .{
114 .offset = @intCast(code.items.len),114 .offset = @intCast(writer.count),
115 .pointee = .{ .symbol_index = try wasm.errorNameTableSymbolIndex() },115 .pointee = .{ .symbol_index = try wasm.errorNameTableSymbolIndex() },
116 .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64,116 .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64,
117 .addend = 0,117 .addend = 0,
118 });118 });
119 code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10);119 try writer.splatByteAll(0, if (is_wasm32) 5 else 10);
120120
121 inst += 1;121 inst += 1;
122 continue :loop tags[inst];122 continue :loop tags[inst];
123 } else {123 } else {
124 const addr: u32 = wasm.errorNameTableAddr();124 const addr: u32 = wasm.errorNameTableAddr();
125 leb.writeIleb128(code.fixedWriter(), addr) catch unreachable;125 try writer.writeLeb128(@as(i32, @bitCast(addr)));
126126
127 inst += 1;127 inst += 1;
128 continue :loop tags[inst];128 continue :loop tags[inst];
129 }129 }
130 },130 },
131 .br_if, .br, .memory_grow, .memory_size => {131 .br_if, .br, .memory_grow, .memory_size => {
132 try code.ensureUnusedCapacity(gpa, 11);132 try writer.writeByte(@intFromEnum(tags[inst]));
133 code.appendAssumeCapacity(@intFromEnum(tags[inst]));133 try writer.writeLeb128(datas[inst].label);
134 leb.writeUleb128(code.fixedWriter(), datas[inst].label) catch unreachable;
135134
136 inst += 1;135 inst += 1;
137 continue :loop tags[inst];136 continue :loop tags[inst];
138 },137 },
139138
140 .local_get, .local_set, .local_tee => {139 .local_get, .local_set, .local_tee => {
141 try code.ensureUnusedCapacity(gpa, 11);140 try writer.writeByte(@intFromEnum(tags[inst]));
142 code.appendAssumeCapacity(@intFromEnum(tags[inst]));141 try writer.writeLeb128(datas[inst].local);
143 leb.writeUleb128(code.fixedWriter(), datas[inst].local) catch unreachable;
144142
145 inst += 1;143 inst += 1;
146 continue :loop tags[inst];144 continue :loop tags[inst];
...@@ -150,29 +148,27 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -150,29 +148,27 @@ pub fn lowerToCode(emit: *Emit) Error!void {
150 const extra_index = datas[inst].payload;148 const extra_index = datas[inst].payload;
151 const extra = mir.extraData(Mir.JumpTable, extra_index);149 const extra = mir.extraData(Mir.JumpTable, extra_index);
152 const labels = mir.extra[extra.end..][0..extra.data.length];150 const labels = mir.extra[extra.end..][0..extra.data.length];
153 try code.ensureUnusedCapacity(gpa, 11 + 10 * labels.len);151 try writer.writeByte(@intFromEnum(std.wasm.Opcode.br_table));
154 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br_table));
155 // -1 because default label is not part of length/depth.152 // -1 because default label is not part of length/depth.
156 leb.writeUleb128(code.fixedWriter(), extra.data.length - 1) catch unreachable;153 try writer.writeLeb128(extra.data.length - 1);
157 for (labels) |label| leb.writeUleb128(code.fixedWriter(), label) catch unreachable;154 for (labels) |label| try writer.writeLeb128(label);
158155
159 inst += 1;156 inst += 1;
160 continue :loop tags[inst];157 continue :loop tags[inst];
161 },158 },
162159
163 .call_nav => {160 .call_nav => {
164 try code.ensureUnusedCapacity(gpa, 6);161 try writer.writeByte(@intFromEnum(std.wasm.Opcode.call));
165 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call));
166 if (is_obj) {162 if (is_obj) {
167 try wasm.out_relocs.append(gpa, .{163 try wasm.out_relocs.append(gpa, .{
168 .offset = @intCast(code.items.len),164 .offset = @intCast(writer.count),
169 .pointee = .{ .symbol_index = try wasm.navSymbolIndex(datas[inst].nav_index) },165 .pointee = .{ .symbol_index = try wasm.navSymbolIndex(datas[inst].nav_index) },
170 .tag = .function_index_leb,166 .tag = .function_index_leb,
171 .addend = 0,167 .addend = 0,
172 });168 });
173 code.appendNTimesAssumeCapacity(0, 5);169 try writer.splatByteAll(0, 5);
174 } else {170 } else {
175 appendOutputFunctionIndex(code, .fromIpNav(wasm, datas[inst].nav_index));171 try appendOutputFunctionIndex(writer, .fromIpNav(wasm, datas[inst].nav_index));
176 }172 }
177173
178 inst += 1;174 inst += 1;
...@@ -180,7 +176,6 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -180,7 +176,6 @@ pub fn lowerToCode(emit: *Emit) Error!void {
180 },176 },
181177
182 .call_indirect => {178 .call_indirect => {
183 try code.ensureUnusedCapacity(gpa, 11);
184 const fn_info = comp.zcu.?.typeToFunc(.fromInterned(datas[inst].ip_index)).?;179 const fn_info = comp.zcu.?.typeToFunc(.fromInterned(datas[inst].ip_index)).?;
185 const func_ty_index = wasm.getExistingFunctionType(180 const func_ty_index = wasm.getExistingFunctionType(
186 fn_info.cc,181 fn_info.cc,
...@@ -188,38 +183,37 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -188,38 +183,37 @@ pub fn lowerToCode(emit: *Emit) Error!void {
188 .fromInterned(fn_info.return_type),183 .fromInterned(fn_info.return_type),
189 target,184 target,
190 ).?;185 ).?;
191 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call_indirect));186 try writer.writeByte(@intFromEnum(std.wasm.Opcode.call_indirect));
192 if (is_obj) {187 if (is_obj) {
193 try wasm.out_relocs.append(gpa, .{188 try wasm.out_relocs.append(gpa, .{
194 .offset = @intCast(code.items.len),189 .offset = @intCast(writer.count),
195 .pointee = .{ .type_index = func_ty_index },190 .pointee = .{ .type_index = func_ty_index },
196 .tag = .type_index_leb,191 .tag = .type_index_leb,
197 .addend = 0,192 .addend = 0,
198 });193 });
199 code.appendNTimesAssumeCapacity(0, 5);194 try writer.splatByteAll(0, 5);
200 } else {195 } else {
201 const index: Wasm.Flush.FuncTypeIndex = .fromTypeIndex(func_ty_index, &wasm.flush_buffer);196 const index: Wasm.Flush.FuncTypeIndex = .fromTypeIndex(func_ty_index, &wasm.flush_buffer);
202 leb.writeUleb128(code.fixedWriter(), @intFromEnum(index)) catch unreachable;197 try writer.writeLeb128(@intFromEnum(index));
203 }198 }
204 leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // table index199 try writer.writeUleb128(0); // table index
205200
206 inst += 1;201 inst += 1;
207 continue :loop tags[inst];202 continue :loop tags[inst];
208 },203 },
209204
210 .call_tag_name => {205 .call_tag_name => {
211 try code.ensureUnusedCapacity(gpa, 6);206 try writer.writeByte(@intFromEnum(std.wasm.Opcode.call));
212 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call));
213 if (is_obj) {207 if (is_obj) {
214 try wasm.out_relocs.append(gpa, .{208 try wasm.out_relocs.append(gpa, .{
215 .offset = @intCast(code.items.len),209 .offset = @intCast(writer.count),
216 .pointee = .{ .symbol_index = try wasm.tagNameSymbolIndex(datas[inst].ip_index) },210 .pointee = .{ .symbol_index = try wasm.tagNameSymbolIndex(datas[inst].ip_index) },
217 .tag = .function_index_leb,211 .tag = .function_index_leb,
218 .addend = 0,212 .addend = 0,
219 });213 });
220 code.appendNTimesAssumeCapacity(0, 5);214 try writer.splatByteAll(0, 5);
221 } else {215 } else {
222 appendOutputFunctionIndex(code, .fromTagNameType(wasm, datas[inst].ip_index));216 try appendOutputFunctionIndex(writer, .fromTagNameType(wasm, datas[inst].ip_index));
223 }217 }
224218
225 inst += 1;219 inst += 1;
...@@ -232,18 +226,17 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -232,18 +226,17 @@ pub fn lowerToCode(emit: *Emit) Error!void {
232 // table initialized based on the `Mir.Intrinsic` enum.226 // table initialized based on the `Mir.Intrinsic` enum.
233 const symbol_name = try wasm.internString(@tagName(datas[inst].intrinsic));227 const symbol_name = try wasm.internString(@tagName(datas[inst].intrinsic));
234228
235 try code.ensureUnusedCapacity(gpa, 6);229 try writer.writeByte(@intFromEnum(std.wasm.Opcode.call));
236 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call));
237 if (is_obj) {230 if (is_obj) {
238 try wasm.out_relocs.append(gpa, .{231 try wasm.out_relocs.append(gpa, .{
239 .offset = @intCast(code.items.len),232 .offset = @intCast(writer.count),
240 .pointee = .{ .symbol_index = try wasm.symbolNameIndex(symbol_name) },233 .pointee = .{ .symbol_index = try wasm.symbolNameIndex(symbol_name) },
241 .tag = .function_index_leb,234 .tag = .function_index_leb,
242 .addend = 0,235 .addend = 0,
243 });236 });
244 code.appendNTimesAssumeCapacity(0, 5);237 try writer.splatByteAll(0, 5);
245 } else {238 } else {
246 appendOutputFunctionIndex(code, .fromSymbolName(wasm, symbol_name));239 try appendOutputFunctionIndex(writer, .fromSymbolName(wasm, symbol_name));
247 }240 }
248241
249 inst += 1;242 inst += 1;
...@@ -251,19 +244,17 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -251,19 +244,17 @@ pub fn lowerToCode(emit: *Emit) Error!void {
251 },244 },
252245
253 .global_set_sp => {246 .global_set_sp => {
254 try code.ensureUnusedCapacity(gpa, 6);247 try writer.writeByte(@intFromEnum(std.wasm.Opcode.global_set));
255 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set));
256 if (is_obj) {248 if (is_obj) {
257 try wasm.out_relocs.append(gpa, .{249 try wasm.out_relocs.append(gpa, .{
258 .offset = @intCast(code.items.len),250 .offset = @intCast(writer.count),
259 .pointee = .{ .symbol_index = try wasm.stackPointerSymbolIndex() },251 .pointee = .{ .symbol_index = try wasm.stackPointerSymbolIndex() },
260 .tag = .global_index_leb,252 .tag = .global_index_leb,
261 .addend = 0,253 .addend = 0,
262 });254 });
263 code.appendNTimesAssumeCapacity(0, 5);255 try writer.splatByteAll(0, 5);
264 } else {256 } else {
265 const sp_global: Wasm.GlobalIndex = .stack_pointer;257 try writer.writeLeb128(@intFromEnum(Wasm.GlobalIndex.stack_pointer));
266 std.leb.writeUleb128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable;
267 }258 }
268259
269 inst += 1;260 inst += 1;
...@@ -271,36 +262,32 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -271,36 +262,32 @@ pub fn lowerToCode(emit: *Emit) Error!void {
271 },262 },
272263
273 .f32_const => {264 .f32_const => {
274 try code.ensureUnusedCapacity(gpa, 5);265 try writer.writeByte(@intFromEnum(std.wasm.Opcode.f32_const));
275 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.f32_const));266 try writer.writeInt(u32, @bitCast(datas[inst].float32), .little);
276 std.mem.writeInt(u32, code.addManyAsArrayAssumeCapacity(4), @bitCast(datas[inst].float32), .little);
277267
278 inst += 1;268 inst += 1;
279 continue :loop tags[inst];269 continue :loop tags[inst];
280 },270 },
281271
282 .f64_const => {272 .f64_const => {
283 try code.ensureUnusedCapacity(gpa, 9);273 try writer.writeByte(@intFromEnum(std.wasm.Opcode.f64_const));
284 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.f64_const));
285 const float64 = mir.extraData(Mir.Float64, datas[inst].payload).data;274 const float64 = mir.extraData(Mir.Float64, datas[inst].payload).data;
286 std.mem.writeInt(u64, code.addManyAsArrayAssumeCapacity(8), float64.toInt(), .little);275 try writer.writeInt(u64, float64.toInt(), .little);
287276
288 inst += 1;277 inst += 1;
289 continue :loop tags[inst];278 continue :loop tags[inst];
290 },279 },
291 .i32_const => {280 .i32_const => {
292 try code.ensureUnusedCapacity(gpa, 6);281 try writer.writeByte(@intFromEnum(std.wasm.Opcode.i32_const));
293 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));282 try writer.writeLeb128(datas[inst].imm32);
294 leb.writeIleb128(code.fixedWriter(), datas[inst].imm32) catch unreachable;
295283
296 inst += 1;284 inst += 1;
297 continue :loop tags[inst];285 continue :loop tags[inst];
298 },286 },
299 .i64_const => {287 .i64_const => {
300 try code.ensureUnusedCapacity(gpa, 11);288 try writer.writeByte(@intFromEnum(std.wasm.Opcode.i64_const));
301 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_const));
302 const int64: i64 = @bitCast(mir.extraData(Mir.Imm64, datas[inst].payload).data.toInt());289 const int64: i64 = @bitCast(mir.extraData(Mir.Imm64, datas[inst].payload).data.toInt());
303 leb.writeIleb128(code.fixedWriter(), int64) catch unreachable;290 try writer.writeLeb128(int64);
304291
305 inst += 1;292 inst += 1;
306 continue :loop tags[inst];293 continue :loop tags[inst];
...@@ -330,9 +317,8 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -330,9 +317,8 @@ pub fn lowerToCode(emit: *Emit) Error!void {
330 .i64_store16,317 .i64_store16,
331 .i64_store32,318 .i64_store32,
332 => {319 => {
333 try code.ensureUnusedCapacity(gpa, 1 + 20);320 try writer.writeByte(@intFromEnum(tags[inst]));
334 code.appendAssumeCapacity(@intFromEnum(tags[inst]));321 try encodeMemArg(writer, mir.extraData(Mir.MemArg, datas[inst].payload).data);
335 encodeMemArg(code, mir.extraData(Mir.MemArg, datas[inst].payload).data);
336 inst += 1;322 inst += 1;
337 continue :loop tags[inst];323 continue :loop tags[inst];
338 },324 },
...@@ -466,43 +452,42 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -466,43 +452,42 @@ pub fn lowerToCode(emit: *Emit) Error!void {
466 .i64_clz,452 .i64_clz,
467 .i64_ctz,453 .i64_ctz,
468 => {454 => {
469 try code.append(gpa, @intFromEnum(tags[inst]));455 try writer.writeByte(@intFromEnum(tags[inst]));
470 inst += 1;456 inst += 1;
471 continue :loop tags[inst];457 continue :loop tags[inst];
472 },458 },
473459
474 .misc_prefix => {460 .misc_prefix => {
475 try code.ensureUnusedCapacity(gpa, 6 + 6);
476 const extra_index = datas[inst].payload;461 const extra_index = datas[inst].payload;
477 const opcode = mir.extra[extra_index];462 const opcode: std.wasm.MiscOpcode = @enumFromInt(mir.extra[extra_index]);
478 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.misc_prefix));463 try writer.writeByte(@intFromEnum(std.wasm.Opcode.misc_prefix));
479 leb.writeUleb128(code.fixedWriter(), opcode) catch unreachable;464 try writer.writeLeb128(@intFromEnum(opcode));
480 switch (@as(std.wasm.MiscOpcode, @enumFromInt(opcode))) {465 switch (opcode) {
481 // bulk-memory opcodes466 // bulk-memory opcodes
482 .data_drop => {467 .data_drop => {
483 const segment = mir.extra[extra_index + 1];468 const segment = mir.extra[extra_index + 1];
484 leb.writeUleb128(code.fixedWriter(), segment) catch unreachable;469 try writer.writeLeb128(segment);
485470
486 inst += 1;471 inst += 1;
487 continue :loop tags[inst];472 continue :loop tags[inst];
488 },473 },
489 .memory_init => {474 .memory_init => {
490 const segment = mir.extra[extra_index + 1];475 const segment = mir.extra[extra_index + 1];
491 leb.writeUleb128(code.fixedWriter(), segment) catch unreachable;476 try writer.writeLeb128(segment);
492 leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // memory index477 try writer.writeByte(0); // memory index
493478
494 inst += 1;479 inst += 1;
495 continue :loop tags[inst];480 continue :loop tags[inst];
496 },481 },
497 .memory_fill => {482 .memory_fill => {
498 leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // memory index483 try writer.writeByte(0); // memory index
499484
500 inst += 1;485 inst += 1;
501 continue :loop tags[inst];486 continue :loop tags[inst];
502 },487 },
503 .memory_copy => {488 .memory_copy => {
504 leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // dst memory index489 try writer.writeByte(0); // dst memory index
505 leb.writeUleb128(code.fixedWriter(), @as(u32, 0)) catch unreachable; // src memory index490 try writer.writeByte(0); // src memory index
506491
507 inst += 1;492 inst += 1;
508 continue :loop tags[inst];493 continue :loop tags[inst];
...@@ -534,12 +519,11 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -534,12 +519,11 @@ pub fn lowerToCode(emit: *Emit) Error!void {
534 comptime unreachable;519 comptime unreachable;
535 },520 },
536 .simd_prefix => {521 .simd_prefix => {
537 try code.ensureUnusedCapacity(gpa, 6 + 20);
538 const extra_index = datas[inst].payload;522 const extra_index = datas[inst].payload;
539 const opcode = mir.extra[extra_index];523 const opcode: std.wasm.SimdOpcode = @enumFromInt(mir.extra[extra_index]);
540 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.simd_prefix));524 try writer.writeByte(@intFromEnum(std.wasm.Opcode.simd_prefix));
541 leb.writeUleb128(code.fixedWriter(), opcode) catch unreachable;525 try writer.writeLeb128(@intFromEnum(opcode));
542 switch (@as(std.wasm.SimdOpcode, @enumFromInt(opcode))) {526 switch (opcode) {
543 .v128_store,527 .v128_store,
544 .v128_load,528 .v128_load,
545 .v128_load8_splat,529 .v128_load8_splat,
...@@ -547,12 +531,12 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -547,12 +531,12 @@ pub fn lowerToCode(emit: *Emit) Error!void {
547 .v128_load32_splat,531 .v128_load32_splat,
548 .v128_load64_splat,532 .v128_load64_splat,
549 => {533 => {
550 encodeMemArg(code, mir.extraData(Mir.MemArg, extra_index + 1).data);534 try encodeMemArg(writer, mir.extraData(Mir.MemArg, extra_index + 1).data);
551 inst += 1;535 inst += 1;
552 continue :loop tags[inst];536 continue :loop tags[inst];
553 },537 },
554 .v128_const, .i8x16_shuffle => {538 .v128_const, .i8x16_shuffle => {
555 code.appendSliceAssumeCapacity(std.mem.asBytes(mir.extra[extra_index + 1 ..][0..4]));539 try writer.writeAll(std.mem.asBytes(mir.extra[extra_index + 1 ..][0..4]));
556 inst += 1;540 inst += 1;
557 continue :loop tags[inst];541 continue :loop tags[inst];
558 },542 },
...@@ -571,7 +555,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -571,7 +555,7 @@ pub fn lowerToCode(emit: *Emit) Error!void {
571 .f64x2_extract_lane,555 .f64x2_extract_lane,
572 .f64x2_replace_lane,556 .f64x2_replace_lane,
573 => {557 => {
574 code.appendAssumeCapacity(@intCast(mir.extra[extra_index + 1]));558 try writer.writeByte(@intCast(mir.extra[extra_index + 1]));
575 inst += 1;559 inst += 1;
576 continue :loop tags[inst];560 continue :loop tags[inst];
577 },561 },
...@@ -819,13 +803,11 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -819,13 +803,11 @@ pub fn lowerToCode(emit: *Emit) Error!void {
819 comptime unreachable;803 comptime unreachable;
820 },804 },
821 .atomics_prefix => {805 .atomics_prefix => {
822 try code.ensureUnusedCapacity(gpa, 6 + 20);
823
824 const extra_index = datas[inst].payload;806 const extra_index = datas[inst].payload;
825 const opcode = mir.extra[extra_index];807 const opcode: std.wasm.AtomicsOpcode = @enumFromInt(mir.extra[extra_index]);
826 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix));808 try writer.writeByte(@intFromEnum(std.wasm.Opcode.atomics_prefix));
827 leb.writeUleb128(code.fixedWriter(), opcode) catch unreachable;809 try writer.writeLeb128(@intFromEnum(opcode));
828 switch (@as(std.wasm.AtomicsOpcode, @enumFromInt(opcode))) {810 switch (opcode) {
829 .i32_atomic_load,811 .i32_atomic_load,
830 .i64_atomic_load,812 .i64_atomic_load,
831 .i32_atomic_load8_u,813 .i32_atomic_load8_u,
...@@ -892,15 +874,12 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -892,15 +874,12 @@ pub fn lowerToCode(emit: *Emit) Error!void {
892 .i64_atomic_rmw32_cmpxchg_u,874 .i64_atomic_rmw32_cmpxchg_u,
893 => {875 => {
894 const mem_arg = mir.extraData(Mir.MemArg, extra_index + 1).data;876 const mem_arg = mir.extraData(Mir.MemArg, extra_index + 1).data;
895 encodeMemArg(code, mem_arg);877 try encodeMemArg(writer, mem_arg);
896 inst += 1;878 inst += 1;
897 continue :loop tags[inst];879 continue :loop tags[inst];
898 },880 },
899 .atomic_fence => {881 .atomic_fence => {
900 // Hard-codes memory index 0 since multi-memory proposal is882 try writer.writeByte(0); // memory index
901 // not yet accepted nor implemented.
902 const memory_index: u32 = 0;
903 leb.writeUleb128(code.fixedWriter(), memory_index) catch unreachable;
904 inst += 1;883 inst += 1;
905 continue :loop tags[inst];884 continue :loop tags[inst];
906 },885 },
...@@ -915,44 +894,36 @@ pub fn lowerToCode(emit: *Emit) Error!void {...@@ -915,44 +894,36 @@ pub fn lowerToCode(emit: *Emit) Error!void {
915}894}
916895
917/// Asserts 20 unused capacity.896/// Asserts 20 unused capacity.
918fn encodeMemArg(code: *std.ArrayListUnmanaged(u8), mem_arg: Mir.MemArg) void {897fn encodeMemArg(writer: *Writer, mem_arg: Mir.MemArg) Writer.Error!void {
919 assert(code.unusedCapacitySlice().len >= 20);898 try writer.writeLeb128(Wasm.Alignment.fromNonzeroByteUnits(mem_arg.alignment).toLog2Units());
920 // Wasm encodes alignment as power of 2, rather than natural alignment.899 try writer.writeLeb128(mem_arg.offset);
921 const encoded_alignment = @ctz(mem_arg.alignment);
922 leb.writeUleb128(code.fixedWriter(), encoded_alignment) catch unreachable;
923 leb.writeUleb128(code.fixedWriter(), mem_arg.offset) catch unreachable;
924}900}
925901
926fn uavRefObj(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), value: InternPool.Index, offset: i32, is_wasm32: bool) !void {902fn uavRefObj(wasm: *Wasm, writer: *Writer, value: InternPool.Index, offset: i32, is_wasm32: bool) Writer.Error!void {
927 const comp = wasm.base.comp;903 const comp = wasm.base.comp;
928 const gpa = comp.gpa;904 const gpa = comp.gpa;
929 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;905 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;
930906
931 try code.ensureUnusedCapacity(gpa, 11);907 try writer.writeByte(@intFromEnum(opcode));
932 code.appendAssumeCapacity(@intFromEnum(opcode));
933908
934 try wasm.out_relocs.append(gpa, .{909 try wasm.out_relocs.append(gpa, .{
935 .offset = @intCast(code.items.len),910 .offset = @intCast(writer.count),
936 .pointee = .{ .symbol_index = try wasm.uavSymbolIndex(value) },911 .pointee = .{ .symbol_index = try wasm.uavSymbolIndex(value) },
937 .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64,912 .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64,
938 .addend = offset,913 .addend = offset,
939 });914 });
940 code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10);915 try writer.splatByteAll(0, if (is_wasm32) 5 else 10);
941}916}
942917
943fn uavRefExe(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), value: InternPool.Index, offset: i32, is_wasm32: bool) !void {918fn uavRefExe(wasm: *Wasm, writer: *Writer, value: InternPool.Index, offset: i32, is_wasm32: bool) !void {
944 const comp = wasm.base.comp;
945 const gpa = comp.gpa;
946 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;919 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;
947920 try writer.writeByte(@intFromEnum(opcode));
948 try code.ensureUnusedCapacity(gpa, 11);
949 code.appendAssumeCapacity(@intFromEnum(opcode));
950921
951 const addr = wasm.uavAddr(value);922 const addr = wasm.uavAddr(value);
952 leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(@as(i64, addr) + offset))) catch unreachable;923 try writer.writeLeb128(@as(u32, @intCast(@as(i64, addr) + offset)));
953}924}
954925
955fn navRefOff(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.NavRefOff, is_wasm32: bool) !void {926fn navRefOff(wasm: *Wasm, writer: *Writer, data: Mir.NavRefOff, is_wasm32: bool) !void {
956 const comp = wasm.base.comp;927 const comp = wasm.base.comp;
957 const zcu = comp.zcu.?;928 const zcu = comp.zcu.?;
958 const ip = &zcu.intern_pool;929 const ip = &zcu.intern_pool;
...@@ -961,24 +932,22 @@ fn navRefOff(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.NavRefOff...@@ -961,24 +932,22 @@ fn navRefOff(wasm: *Wasm, code: *std.ArrayListUnmanaged(u8), data: Mir.NavRefOff
961 const nav_ty = ip.getNav(data.nav_index).typeOf(ip);932 const nav_ty = ip.getNav(data.nav_index).typeOf(ip);
962 assert(!ip.isFunctionType(nav_ty));933 assert(!ip.isFunctionType(nav_ty));
963934
964 try code.ensureUnusedCapacity(gpa, 11);
965
966 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;935 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;
967 code.appendAssumeCapacity(@intFromEnum(opcode));936 try writer.writeByte(@intFromEnum(opcode));
968 if (is_obj) {937 if (is_obj) {
969 try wasm.out_relocs.append(gpa, .{938 try wasm.out_relocs.append(gpa, .{
970 .offset = @intCast(code.items.len),939 .offset = @intCast(writer.count),
971 .pointee = .{ .symbol_index = try wasm.navSymbolIndex(data.nav_index) },940 .pointee = .{ .symbol_index = try wasm.navSymbolIndex(data.nav_index) },
972 .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64,941 .tag = if (is_wasm32) .memory_addr_leb else .memory_addr_leb64,
973 .addend = data.offset,942 .addend = data.offset,
974 });943 });
975 code.appendNTimesAssumeCapacity(0, if (is_wasm32) 5 else 10);944 try writer.splatByteAll(0, if (is_wasm32) 5 else 10);
976 } else {945 } else {
977 const addr = wasm.navAddr(data.nav_index);946 const addr = wasm.navAddr(data.nav_index);
978 leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(@as(i64, addr) + data.offset))) catch unreachable;947 try writer.writeLeb128(@as(i32, @bitCast(@as(u32, @intCast(@as(i64, addr) + data.offset)))));
979 }948 }
980}949}
981950
982fn appendOutputFunctionIndex(code: *std.ArrayListUnmanaged(u8), i: Wasm.OutputFunctionIndex) void {951fn appendOutputFunctionIndex(writer: *Writer, i: Wasm.OutputFunctionIndex) Writer.Error!void {
983 leb.writeUleb128(code.fixedWriter(), @intFromEnum(i)) catch unreachable;952 return writer.writeLeb128(@intFromEnum(i));
984}953}
src/arch/wasm/Mir.zig+20-22
...@@ -669,16 +669,14 @@ pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {...@@ -669,16 +669,14 @@ pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
669 mir.* = undefined;669 mir.* = undefined;
670}670}
671671
672pub fn lower(mir: *const Mir, wasm: *Wasm, code: *std.ArrayListUnmanaged(u8)) std.mem.Allocator.Error!void {672pub fn lower(mir: *const Mir, wasm: *Wasm, writer: *std.Io.Writer) std.Io.Writer.Error!void {
673 const gpa = wasm.base.comp.gpa;
674
675 // Write the locals in the prologue of the function body.673 // Write the locals in the prologue of the function body.
676 try code.ensureUnusedCapacity(gpa, 5 + mir.locals.len * 6 + 38);674 _ = try writer.writableSliceGreedy(5 + mir.locals.len * 6 + 38);
677675
678 std.leb.writeUleb128(code.fixedWriter(), @as(u32, @intCast(mir.locals.len))) catch unreachable;676 writer.writeLeb128(@as(u32, @intCast(mir.locals.len))) catch unreachable;
679 for (mir.locals) |local| {677 for (mir.locals) |local| {
680 std.leb.writeUleb128(code.fixedWriter(), @as(u32, 1)) catch unreachable;678 writer.writeLeb128(@as(u32, 1)) catch unreachable;
681 code.appendAssumeCapacity(@intFromEnum(local));679 writer.writeByte(@intFromEnum(local)) catch unreachable;
682 }680 }
683681
684 // Stack management section of function prologue.682 // Stack management section of function prologue.
...@@ -686,37 +684,37 @@ pub fn lower(mir: *const Mir, wasm: *Wasm, code: *std.ArrayListUnmanaged(u8)) st...@@ -686,37 +684,37 @@ pub fn lower(mir: *const Mir, wasm: *Wasm, code: *std.ArrayListUnmanaged(u8)) st
686 if (stack_alignment.toByteUnits()) |align_bytes| {684 if (stack_alignment.toByteUnits()) |align_bytes| {
687 const sp_global: Wasm.GlobalIndex = .stack_pointer;685 const sp_global: Wasm.GlobalIndex = .stack_pointer;
688 // load stack pointer686 // load stack pointer
689 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_get));687 writer.writeByte(@intFromEnum(std.wasm.Opcode.global_get)) catch unreachable;
690 std.leb.writeUleb128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable;688 writer.writeLeb128(@intFromEnum(sp_global)) catch unreachable;
691 // store stack pointer so we can restore it when we return from the function689 // store stack pointer so we can restore it when we return from the function
692 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_tee));690 writer.writeByte(@intFromEnum(std.wasm.Opcode.local_tee)) catch unreachable;
693 leb.writeUleb128(code.fixedWriter(), mir.prologue.sp_local) catch unreachable;691 writer.writeLeb128(mir.prologue.sp_local) catch unreachable;
694 // get the total stack size692 // get the total stack size
695 const aligned_stack: i32 = @intCast(stack_alignment.forward(mir.prologue.stack_size));693 const aligned_stack: i32 = @intCast(stack_alignment.forward(mir.prologue.stack_size));
696 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));694 writer.writeByte(@intFromEnum(std.wasm.Opcode.i32_const)) catch unreachable;
697 leb.writeIleb128(code.fixedWriter(), aligned_stack) catch unreachable;695 writer.writeLeb128(aligned_stack) catch unreachable;
698 // subtract it from the current stack pointer696 // subtract it from the current stack pointer
699 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_sub));697 writer.writeByte(@intFromEnum(std.wasm.Opcode.i32_sub)) catch unreachable;
700 // Get negative stack alignment698 // Get negative stack alignment
701 const neg_stack_align = @as(i32, @intCast(align_bytes)) * -1;699 const neg_stack_align = @as(i32, @intCast(align_bytes)) * -1;
702 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));700 writer.writeByte(@intFromEnum(std.wasm.Opcode.i32_const)) catch unreachable;
703 leb.writeIleb128(code.fixedWriter(), neg_stack_align) catch unreachable;701 writer.writeLeb128(neg_stack_align) catch unreachable;
704 // Bitwise-and the value to get the new stack pointer to ensure the702 // Bitwise-and the value to get the new stack pointer to ensure the
705 // pointers are aligned with the abi alignment.703 // pointers are aligned with the abi alignment.
706 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_and));704 writer.writeByte(@intFromEnum(std.wasm.Opcode.i32_and)) catch unreachable;
707 // The bottom will be used to calculate all stack pointer offsets.705 // The bottom will be used to calculate all stack pointer offsets.
708 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_tee));706 writer.writeByte(@intFromEnum(std.wasm.Opcode.local_tee)) catch unreachable;
709 leb.writeUleb128(code.fixedWriter(), mir.prologue.bottom_stack_local) catch unreachable;707 writer.writeLeb128(mir.prologue.bottom_stack_local) catch unreachable;
710 // Store the current stack pointer value into the global stack pointer so other function calls will708 // Store the current stack pointer value into the global stack pointer so other function calls will
711 // start from this value instead and not overwrite the current stack.709 // start from this value instead and not overwrite the current stack.
712 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set));710 writer.writeByte(@intFromEnum(std.wasm.Opcode.global_set)) catch unreachable;
713 std.leb.writeUleb128(code.fixedWriter(), @intFromEnum(sp_global)) catch unreachable;711 writer.writeLeb128(@intFromEnum(sp_global)) catch unreachable;
714 }712 }
715713
716 var emit: Emit = .{714 var emit: Emit = .{
717 .mir = mir.*,715 .mir = mir.*,
718 .wasm = wasm,716 .wasm = wasm,
719 .code = code,717 .writer = writer,
720 };718 };
721 try emit.lowerToCode();719 try emit.lowerToCode();
722}720}
src/link/Wasm.zig+11-12
...@@ -28,6 +28,7 @@ const fs = std.fs;...@@ -28,6 +28,7 @@ const fs = std.fs;
28const leb = std.leb;28const leb = std.leb;
29const log = std.log.scoped(.link);29const log = std.log.scoped(.link);
30const mem = std.mem;30const mem = std.mem;
31const Writer = std.Io.Writer;
3132
32const Mir = @import("../arch/wasm/Mir.zig");33const Mir = @import("../arch/wasm/Mir.zig");
33const CodeGen = @import("../arch/wasm/CodeGen.zig");34const CodeGen = @import("../arch/wasm/CodeGen.zig");
...@@ -2087,11 +2088,9 @@ pub const Expr = enum(u32) {...@@ -2087,11 +2088,9 @@ pub const Expr = enum(u32) {
2087 pub const end = @intFromEnum(std.wasm.Opcode.end);2088 pub const end = @intFromEnum(std.wasm.Opcode.end);
20882089
2089 pub fn slice(index: Expr, wasm: *const Wasm) [:end]const u8 {2090 pub fn slice(index: Expr, wasm: *const Wasm) [:end]const u8 {
2090 const start_slice = wasm.string_bytes.items[@intFromEnum(index)..];2091 var r: std.Io.Reader = .fixed(wasm.string_bytes.items[@intFromEnum(index)..]);
2091 const end_pos = Object.exprEndPos(start_slice, 0) catch |err| switch (err) {2092 Object.skipInit(&r) catch unreachable;
2092 error.InvalidInitOpcode => unreachable,2093 return r.buffered()[0 .. r.seek - 1 :end];
2093 };
2094 return start_slice[0..end_pos :end];
2095 }2094 }
2096};2095};
20972096
...@@ -2126,7 +2125,7 @@ pub const FunctionType = extern struct {...@@ -2126,7 +2125,7 @@ pub const FunctionType = extern struct {
2126 wasm: *const Wasm,2125 wasm: *const Wasm,
2127 ft: FunctionType,2126 ft: FunctionType,
21282127
2129 pub fn format(self: Formatter, writer: *std.io.Writer) std.io.Writer.Error!void {2128 pub fn format(self: Formatter, writer: *Writer) Writer.Error!void {
2130 const params = self.ft.params.slice(self.wasm);2129 const params = self.ft.params.slice(self.wasm);
2131 const returns = self.ft.returns.slice(self.wasm);2130 const returns = self.ft.returns.slice(self.wasm);
21322131
...@@ -2905,7 +2904,7 @@ pub const Feature = packed struct(u8) {...@@ -2905,7 +2904,7 @@ pub const Feature = packed struct(u8) {
2905 @"=",2904 @"=",
2906 };2905 };
29072906
2908 pub fn format(feature: Feature, writer: *std.io.Writer) std.io.Writer.Error!void {2907 pub fn format(feature: Feature, writer: *Writer) Writer.Error!void {
2909 try writer.print("{s} {s}", .{ @tagName(feature.prefix), @tagName(feature.tag) });2908 try writer.print("{s} {s}", .{ @tagName(feature.prefix), @tagName(feature.tag) });
2910 }2909 }
29112910
...@@ -3037,16 +3036,16 @@ fn parseObject(wasm: *Wasm, obj: link.Input.Object) !void {...@@ -3037,16 +3036,16 @@ fn parseObject(wasm: *Wasm, obj: link.Input.Object) !void {
3037 const stat = try obj.file.stat();3036 const stat = try obj.file.stat();
3038 const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig;3037 const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig;
30393038
3040 const file_contents = try gpa.alloc(u8, size);3039 var br: std.Io.Reader = .fixed(try gpa.alloc(u8, size));
3041 defer gpa.free(file_contents);3040 defer gpa.free(br.buffered());
30423041
3043 const n = try obj.file.preadAll(file_contents, 0);3042 const n = try obj.file.preadAll(br.buffered(), 0);
3044 if (n != file_contents.len) return error.UnexpectedEndOfFile;3043 if (n != br.bufferedLen()) return error.UnexpectedEndOfFile;
30453044
3046 var ss: Object.ScratchSpace = .{};3045 var ss: Object.ScratchSpace = .{};
3047 defer ss.deinit(gpa);3046 defer ss.deinit(gpa);
30483047
3049 const object = try Object.parse(wasm, file_contents, obj.path, null, wasm.object_host_name, &ss, obj.must_link, gc_sections);3048 const object = try Object.parse(wasm, &br, obj.path, null, wasm.object_host_name, &ss, obj.must_link, gc_sections);
3050 wasm.objects.appendAssumeCapacity(object);3049 wasm.objects.appendAssumeCapacity(object);
3051}3050}
30523051
src/link/Wasm/Archive.zig+2-3
...@@ -167,9 +167,8 @@ pub fn parseObject(...@@ -167,9 +167,8 @@ pub fn parseObject(
167 };167 };
168168
169 const object_file_size = try header.parsedSize();169 const object_file_size = try header.parsedSize();
170 const contents = file_contents[object_offset + @sizeOf(Header) ..][0..object_file_size];170 var r: std.io.Reader = .fixed(file_contents[object_offset + @sizeOf(Header) ..][0..object_file_size]);
171171 return Object.parse(wasm, &r, path, object_name, host_name, scratch_space, must_link, gc_sections);
172 return Object.parse(wasm, contents, path, object_name, host_name, scratch_space, must_link, gc_sections);
173}172}
174173
175const Archive = @This();174const Archive = @This();
src/link/Wasm/Flush.zig+468-553
...@@ -16,9 +16,9 @@ const build_options = @import("build_options");...@@ -16,9 +16,9 @@ const build_options = @import("build_options");
16const std = @import("std");16const std = @import("std");
17const Allocator = std.mem.Allocator;17const Allocator = std.mem.Allocator;
18const mem = std.mem;18const mem = std.mem;
19const leb = std.leb;
20const log = std.log.scoped(.link);19const log = std.log.scoped(.link);
21const assert = std.debug.assert;20const assert = std.debug.assert;
21const Writer = std.Io.Writer;
2222
23/// Ordered list of data segments that will appear in the final binary.23/// Ordered list of data segments that will appear in the final binary.
24/// When sorted, to-be-merged segments will be made adjacent.24/// When sorted, to-be-merged segments will be made adjacent.
...@@ -557,13 +557,12 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -557,13 +557,12 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
557 // Index of the data section. Used to tell relocation table where the section lives.557 // Index of the data section. Used to tell relocation table where the section lives.
558 var data_section_index: ?u32 = null;558 var data_section_index: ?u32 = null;
559559
560 const binary_bytes = &f.binary_bytes;560 assert(f.binary_bytes.items.len == 0);
561 assert(binary_bytes.items.len == 0);561 var aw: Writer.Allocating = .fromArrayList(gpa, &f.binary_bytes);
562 defer f.binary_bytes = aw.toArrayList();
563 const w = &aw.writer;
562564
563 try binary_bytes.appendSlice(gpa, &std.wasm.magic ++ &std.wasm.version);565 try w.writeAll(&std.wasm.magic ++ &std.wasm.version);
564 assert(binary_bytes.items.len == 8);
565
566 const binary_writer = binary_bytes.writer(gpa);
567566
568 // Type section.567 // Type section.
569 for (f.function_imports.values()) |id| {568 for (f.function_imports.values()) |id| {
...@@ -573,22 +572,18 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -573,22 +572,18 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
573 try f.func_types.put(gpa, function.typeIndex(wasm), {});572 try f.func_types.put(gpa, function.typeIndex(wasm), {});
574 }573 }
575 if (f.func_types.entries.len != 0) {574 if (f.func_types.entries.len != 0) {
576 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);575 const header_offset = try reserveVecSectionHeader(w);
577 for (f.func_types.keys()) |func_type_index| {576 for (f.func_types.keys()) |func_type_index| {
578 const func_type = func_type_index.ptr(wasm);577 const func_type = func_type_index.ptr(wasm);
579 try leb.writeUleb128(binary_writer, std.wasm.function_type);578 try w.writeLeb128(std.wasm.function_type);
580 const params = func_type.params.slice(wasm);579 const params = func_type.params.slice(wasm);
581 try leb.writeUleb128(binary_writer, @as(u32, @intCast(params.len)));580 try w.writeLeb128(params.len);
582 for (params) |param_ty| {581 for (params) |param_ty| try w.writeLeb128(@intFromEnum(param_ty));
583 try leb.writeUleb128(binary_writer, @intFromEnum(param_ty));
584 }
585 const returns = func_type.returns.slice(wasm);582 const returns = func_type.returns.slice(wasm);
586 try leb.writeUleb128(binary_writer, @as(u32, @intCast(returns.len)));583 try w.writeLeb128(returns.len);
587 for (returns) |ret_ty| {584 for (returns) |ret_ty| try w.writeLeb128(@intFromEnum(ret_ty));
588 try leb.writeUleb128(binary_writer, @intFromEnum(ret_ty));
589 }
590 }585 }
591 replaceVecSectionHeader(binary_bytes, header_offset, .type, @intCast(f.func_types.entries.len));586 replaceVecSectionHeader(&aw, header_offset, .type, @intCast(f.func_types.entries.len));
592 section_index += 1;587 section_index += 1;
593 }588 }
594589
...@@ -601,42 +596,42 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -601,42 +596,42 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
601 // Import section596 // Import section
602 {597 {
603 var total_imports: usize = 0;598 var total_imports: usize = 0;
604 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);599 const header_offset = try reserveVecSectionHeader(w);
605600
606 for (f.function_imports.values()) |id| {601 for (f.function_imports.values()) |id| {
607 const module_name = id.moduleName(wasm).slice(wasm).?;602 const module_name = id.moduleName(wasm).slice(wasm).?;
608 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));603 try w.writeLeb128(module_name.len);
609 try binary_writer.writeAll(module_name);604 try w.writeAll(module_name);
610605
611 const name = id.importName(wasm).slice(wasm);606 const name = id.importName(wasm).slice(wasm);
612 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));607 try w.writeLeb128(name.len);
613 try binary_writer.writeAll(name);608 try w.writeAll(name);
614609
615 try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.function));610 try w.writeByte(@intFromEnum(std.wasm.ExternalKind.function));
616 const type_index: FuncTypeIndex = .fromTypeIndex(id.functionType(wasm), f);611 const type_index: FuncTypeIndex = .fromTypeIndex(id.functionType(wasm), f);
617 try leb.writeUleb128(binary_writer, @intFromEnum(type_index));612 try w.writeLeb128(@intFromEnum(type_index));
618 }613 }
619 total_imports += f.function_imports.entries.len;614 total_imports += f.function_imports.entries.len;
620615
621 for (wasm.table_imports.values()) |id| {616 for (wasm.table_imports.values()) |id| {
622 const table_import = id.value(wasm);617 const table_import = id.value(wasm);
623 const module_name = table_import.module_name.slice(wasm);618 const module_name = table_import.module_name.slice(wasm);
624 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));619 try w.writeLeb128(module_name.len);
625 try binary_writer.writeAll(module_name);620 try w.writeAll(module_name);
626621
627 const name = table_import.name.slice(wasm);622 const name = table_import.name.slice(wasm);
628 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));623 try w.writeLeb128(name.len);
629 try binary_writer.writeAll(name);624 try w.writeAll(name);
630625
631 try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.table));626 try w.writeByte(@intFromEnum(std.wasm.ExternalKind.table));
632 try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.RefType, table_import.flags.ref_type.to())));627 try w.writeLeb128(@intFromEnum(@as(std.wasm.RefType, table_import.flags.ref_type.to())));
633 try emitLimits(gpa, binary_bytes, table_import.limits());628 try emitLimits(w, table_import.limits());
634 }629 }
635 total_imports += wasm.table_imports.entries.len;630 total_imports += wasm.table_imports.entries.len;
636631
637 if (import_memory) {632 if (import_memory) {
638 const name = if (is_obj) wasm.preloaded_strings.__linear_memory else wasm.preloaded_strings.memory;633 const name = if (is_obj) wasm.preloaded_strings.__linear_memory else wasm.preloaded_strings.memory;
639 try emitMemoryImport(wasm, binary_bytes, name, &.{634 try emitMemoryImport(wasm, w, name, &.{
640 // TODO the import_memory option needs to specify from which module635 // TODO the import_memory option needs to specify from which module
641 .module_name = wasm.object_host_name.unwrap().?,636 .module_name = wasm.object_host_name.unwrap().?,
642 .limits_min = wasm.memories.limits.min,637 .limits_min = wasm.memories.limits.min,
...@@ -650,215 +645,209 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -650,215 +645,209 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
650645
651 for (f.global_imports.values()) |id| {646 for (f.global_imports.values()) |id| {
652 const module_name = id.moduleName(wasm).slice(wasm).?;647 const module_name = id.moduleName(wasm).slice(wasm).?;
653 try leb.writeUleb128(binary_writer, @as(u32, @intCast(module_name.len)));648 try w.writeLeb128(module_name.len);
654 try binary_writer.writeAll(module_name);649 try w.writeAll(module_name);
655650
656 const name = id.importName(wasm).slice(wasm);651 const name = id.importName(wasm).slice(wasm);
657 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));652 try w.writeLeb128(name.len);
658 try binary_writer.writeAll(name);653 try w.writeAll(name);
659654
660 try binary_writer.writeByte(@intFromEnum(std.wasm.ExternalKind.global));655 try w.writeByte(@intFromEnum(std.wasm.ExternalKind.global));
661 const global_type = id.globalType(wasm);656 const global_type = id.globalType(wasm);
662 try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.Valtype, global_type.valtype)));657 try w.writeLeb128(@intFromEnum(global_type.valtype));
663 try binary_writer.writeByte(@intFromBool(global_type.mutable));658 try w.writeByte(@intFromBool(global_type.mutable));
664 }659 }
665 total_imports += f.global_imports.entries.len;660 total_imports += f.global_imports.entries.len;
666661
667 if (total_imports > 0) {662 if (total_imports > 0) {
668 replaceVecSectionHeader(binary_bytes, header_offset, .import, @intCast(total_imports));663 replaceVecSectionHeader(&aw, header_offset, .import, @intCast(total_imports));
669 section_index += 1;664 section_index += 1;
670 } else {665 } else {
671 binary_bytes.shrinkRetainingCapacity(header_offset);666 aw.shrinkRetainingCapacity(header_offset);
672 }667 }
673 }668 }
674669
675 // Function section670 // Function section
676 if (wasm.functions.count() != 0) {671 if (wasm.functions.count() != 0) {
677 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);672 const header_offset = try reserveVecSectionHeader(w);
678 for (wasm.functions.keys()) |function| {673 for (wasm.functions.keys()) |function| {
679 const index: FuncTypeIndex = .fromTypeIndex(function.typeIndex(wasm), f);674 const index: FuncTypeIndex = .fromTypeIndex(function.typeIndex(wasm), f);
680 try leb.writeUleb128(binary_writer, @intFromEnum(index));675 try w.writeLeb128(@intFromEnum(index));
681 }676 }
682677
683 replaceVecSectionHeader(binary_bytes, header_offset, .function, @intCast(wasm.functions.count()));678 replaceVecSectionHeader(&aw, header_offset, .function, @intCast(wasm.functions.count()));
684 section_index += 1;679 section_index += 1;
685 }680 }
686681
687 // Table section682 // Table section
688 if (wasm.tables.entries.len > 0) {683 if (wasm.tables.entries.len > 0) {
689 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);684 const header_offset = try reserveVecSectionHeader(w);
690685
691 for (wasm.tables.keys()) |table| {686 for (wasm.tables.keys()) |table| {
692 try leb.writeUleb128(binary_writer, @intFromEnum(@as(std.wasm.RefType, table.refType(wasm))));687 try w.writeLeb128(@intFromEnum(table.refType(wasm)));
693 try emitLimits(gpa, binary_bytes, table.limits(wasm));688 try emitLimits(w, table.limits(wasm));
694 }689 }
695690
696 replaceVecSectionHeader(binary_bytes, header_offset, .table, @intCast(wasm.tables.entries.len));691 replaceVecSectionHeader(&aw, header_offset, .table, @intCast(wasm.tables.entries.len));
697 section_index += 1;692 section_index += 1;
698 }693 }
699694
700 // Memory section. wasm currently only supports 1 linear memory segment.695 // Memory section. wasm currently only supports 1 linear memory segment.
701 if (!import_memory) {696 if (!import_memory) {
702 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);697 const header_offset = try reserveVecSectionHeader(w);
703 try emitLimits(gpa, binary_bytes, wasm.memories.limits);698 try emitLimits(w, wasm.memories.limits);
704 replaceVecSectionHeader(binary_bytes, header_offset, .memory, 1);699 replaceVecSectionHeader(&aw, header_offset, .memory, 1);
705 section_index += 1;700 section_index += 1;
706 }701 }
707702
708 // Global section.703 // Global section.
709 const globals_len: u32 = @intCast(wasm.globals.entries.len);704 const globals_len: u32 = @intCast(wasm.globals.entries.len);
710 if (globals_len > 0) {705 if (globals_len > 0) {
711 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);706 const header_offset = try reserveVecSectionHeader(w);
712707
713 for (wasm.globals.keys()) |global_resolution| {708 for (wasm.globals.keys()) |global_resolution| {
714 switch (global_resolution.unpack(wasm)) {709 switch (global_resolution.unpack(wasm)) {
715 .unresolved => unreachable,710 .unresolved => unreachable,
716 .__heap_base => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.heap_base),711 .__heap_base => try appendGlobal(w, false, virtual_addrs.heap_base),
717 .__heap_end => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.heap_end),712 .__heap_end => try appendGlobal(w, false, virtual_addrs.heap_end),
718 .__stack_pointer => try appendGlobal(gpa, binary_bytes, 1, virtual_addrs.stack_pointer),713 .__stack_pointer => try appendGlobal(w, true, virtual_addrs.stack_pointer),
719 .__tls_align => try appendGlobal(gpa, binary_bytes, 0, @intCast(virtual_addrs.tls_align.toByteUnits().?)),714 .__tls_align => try appendGlobal(w, false, @intCast(virtual_addrs.tls_align.toByteUnits().?)),
720 .__tls_base => try appendGlobal(gpa, binary_bytes, 1, virtual_addrs.tls_base.?),715 .__tls_base => try appendGlobal(w, true, virtual_addrs.tls_base.?),
721 .__tls_size => try appendGlobal(gpa, binary_bytes, 0, virtual_addrs.tls_size.?),716 .__tls_size => try appendGlobal(w, false, virtual_addrs.tls_size.?),
722 .object_global => |i| {717 .object_global => |i| {
723 const global = i.ptr(wasm);718 const global = i.ptr(wasm);
724 try binary_bytes.appendSlice(gpa, &.{719 try w.writeAll(&.{
725 @intFromEnum(@as(std.wasm.Valtype, global.flags.global_type.valtype.to())),720 @intFromEnum(@as(std.wasm.Valtype, global.flags.global_type.valtype.to())),
726 @intFromBool(global.flags.global_type.mutable),721 @intFromBool(global.flags.global_type.mutable),
727 });722 });
728 try emitExpr(wasm, binary_bytes, global.expr);723 try emitExpr(wasm, w, global.expr);
729 },724 },
730 .nav_exe => unreachable, // Zig source code currently cannot represent this.725 .nav_exe => unreachable, // Zig source code currently cannot represent this.
731 .nav_obj => unreachable, // Zig source code currently cannot represent this.726 .nav_obj => unreachable, // Zig source code currently cannot represent this.
732 }727 }
733 }728 }
734729
735 replaceVecSectionHeader(binary_bytes, header_offset, .global, globals_len);730 replaceVecSectionHeader(&aw, header_offset, .global, globals_len);
736 section_index += 1;731 section_index += 1;
737 }732 }
738733
739 // Export section734 // Export section
740 {735 {
741 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);736 const header_offset = try reserveVecSectionHeader(w);
742 var exports_len: usize = 0;737 var exports_len: usize = 0;
743738
744 for (wasm.function_exports.keys(), wasm.function_exports.values()) |exp_name, function_index| {739 for (wasm.function_exports.keys(), wasm.function_exports.values()) |exp_name, function_index| {
745 const name = exp_name.slice(wasm);740 const name = exp_name.slice(wasm);
746 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));741 try w.writeLeb128(name.len);
747 try binary_bytes.appendSlice(gpa, name);742 try w.writeAll(name);
748 try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.function));743 try w.writeByte(@intFromEnum(std.wasm.ExternalKind.function));
749 const func_index = Wasm.OutputFunctionIndex.fromFunctionIndex(wasm, function_index);744 const func_index = Wasm.OutputFunctionIndex.fromFunctionIndex(wasm, function_index);
750 try leb.writeUleb128(binary_writer, @intFromEnum(func_index));745 try w.writeLeb128(@intFromEnum(func_index));
751 }746 }
752 exports_len += wasm.function_exports.entries.len;747 exports_len += wasm.function_exports.entries.len;
753748
754 if (wasm.export_table and f.indirect_function_table.entries.len > 0) {749 if (wasm.export_table and f.indirect_function_table.entries.len > 0) {
755 const name = "__indirect_function_table";750 const name = "__indirect_function_table";
756 const index: u32 = @intCast(wasm.tables.getIndex(.__indirect_function_table).?);751 const index: u32 = @intCast(wasm.tables.getIndex(.__indirect_function_table).?);
757 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));752 try w.writeLeb128(name.len);
758 try binary_bytes.appendSlice(gpa, name);753 try w.writeAll(name);
759 try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.table));754 try w.writeByte(@intFromEnum(std.wasm.ExternalKind.table));
760 try leb.writeUleb128(binary_writer, index);755 try w.writeLeb128(index);
761 exports_len += 1;756 exports_len += 1;
762 }757 }
763758
764 if (export_memory) {759 if (export_memory) {
765 const name = "memory";760 const name = "memory";
766 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));761 try w.writeLeb128(name.len);
767 try binary_bytes.appendSlice(gpa, name);762 try w.writeAll(name);
768 try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.memory));763 try w.writeByte(@intFromEnum(std.wasm.ExternalKind.memory));
769 try leb.writeUleb128(binary_writer, @as(u32, 0));764 try w.writeUleb128(0);
770 exports_len += 1;765 exports_len += 1;
771 }766 }
772767
773 for (wasm.global_exports.items) |exp| {768 for (wasm.global_exports.items) |exp| {
774 const name = exp.name.slice(wasm);769 const name = exp.name.slice(wasm);
775 try leb.writeUleb128(binary_writer, @as(u32, @intCast(name.len)));770 try w.writeLeb128(name.len);
776 try binary_bytes.appendSlice(gpa, name);771 try w.writeAll(name);
777 try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.global));772 try w.writeByte(@intFromEnum(std.wasm.ExternalKind.global));
778 try leb.writeUleb128(binary_writer, @intFromEnum(exp.global_index));773 try w.writeLeb128(@intFromEnum(exp.global_index));
779 }774 }
780 exports_len += wasm.global_exports.items.len;775 exports_len += wasm.global_exports.items.len;
781776
782 if (exports_len > 0) {777 if (exports_len > 0) {
783 replaceVecSectionHeader(binary_bytes, header_offset, .@"export", @intCast(exports_len));778 replaceVecSectionHeader(&aw, header_offset, .@"export", @intCast(exports_len));
784 section_index += 1;779 section_index += 1;
785 } else {780 } else {
786 binary_bytes.shrinkRetainingCapacity(header_offset);781 aw.shrinkRetainingCapacity(header_offset);
787 }782 }
788 }783 }
789784
790 // start section785 // start section
791 if (wasm.functions.getIndex(.__wasm_init_memory)) |func_index| {786 if (wasm.functions.getIndex(.__wasm_init_memory)) |func_index| {
792 try emitStartSection(gpa, binary_bytes, .fromFunctionIndex(wasm, @enumFromInt(func_index)));787 try emitStartSection(&aw, .fromFunctionIndex(wasm, @enumFromInt(func_index)));
793 } else if (Wasm.OutputFunctionIndex.fromResolution(wasm, wasm.entry_resolution)) |func_index| {788 } else if (Wasm.OutputFunctionIndex.fromResolution(wasm, wasm.entry_resolution)) |func_index| {
794 try emitStartSection(gpa, binary_bytes, func_index);789 try emitStartSection(&aw, func_index);
795 }790 }
796791
797 // element section792 // element section
798 if (f.indirect_function_table.entries.len > 0) {793 if (f.indirect_function_table.entries.len > 0) {
799 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);794 const header_offset = try reserveVecSectionHeader(w);
800795
801 // indirect function table elements796 // indirect function table elements
802 const table_index: u32 = @intCast(wasm.tables.getIndex(.__indirect_function_table).?);797 const table_index: u32 = @intCast(wasm.tables.getIndex(.__indirect_function_table).?);
803 // passive with implicit 0-index table or set table index manually798 // passive with implicit 0-index table or set table index manually
804 const flags: u32 = if (table_index == 0) 0x0 else 0x02;799 const flags: u32 = if (table_index == 0) 0x0 else 0x02;
805 try leb.writeUleb128(binary_writer, flags);800 try w.writeLeb128(flags);
806 if (flags == 0x02) {801 if (flags == 0x02) try w.writeLeb128(table_index);
807 try leb.writeUleb128(binary_writer, table_index);
808 }
809 // We start at index 1, so unresolved function pointers are invalid802 // We start at index 1, so unresolved function pointers are invalid
810 try emitInit(binary_writer, .{ .i32_const = 1 });803 try emitInit(w, .{ .i32_const = 1 });
811 if (flags == 0x02) {804 if (flags == 0x02) try w.writeUleb128(0); // represents funcref
812 try leb.writeUleb128(binary_writer, @as(u8, 0)); // represents funcref805 try w.writeLeb128(f.indirect_function_table.entries.len);
813 }806 for (f.indirect_function_table.keys()) |func_index| try w.writeLeb128(@intFromEnum(func_index));
814 try leb.writeUleb128(binary_writer, @as(u32, @intCast(f.indirect_function_table.entries.len)));
815 for (f.indirect_function_table.keys()) |func_index| {
816 try leb.writeUleb128(binary_writer, @intFromEnum(func_index));
817 }
818807
819 replaceVecSectionHeader(binary_bytes, header_offset, .element, 1);808 replaceVecSectionHeader(&aw, header_offset, .element, 1);
820 section_index += 1;809 section_index += 1;
821 }810 }
822811
823 // When the shared-memory option is enabled, we *must* emit the 'data count' section.812 // When the shared-memory option is enabled, we *must* emit the 'data count' section.
824 if (f.data_segment_groups.items.len > 0) {813 if (f.data_segment_groups.items.len > 0) {
825 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);814 const header_offset = try reserveVecSectionHeader(w);
826 replaceVecSectionHeader(binary_bytes, header_offset, .data_count, @intCast(f.data_segment_groups.items.len));815 replaceVecSectionHeader(&aw, header_offset, .data_count, @intCast(f.data_segment_groups.items.len));
827 }816 }
828817
829 // Code section.818 // Code section.
830 if (wasm.functions.count() != 0) {819 if (wasm.functions.count() != 0) {
831 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);820 const header_offset = try reserveVecSectionHeader(w);
832821
833 for (wasm.functions.keys()) |resolution| switch (resolution.unpack(wasm)) {822 for (wasm.functions.keys()) |resolution| switch (resolution.unpack(wasm)) {
834 .unresolved => unreachable,823 .unresolved => unreachable,
835 .__wasm_apply_global_tls_relocs => @panic("TODO lower __wasm_apply_global_tls_relocs"),824 .__wasm_apply_global_tls_relocs => @panic("TODO lower __wasm_apply_global_tls_relocs"),
836 .__wasm_call_ctors => {825 .__wasm_call_ctors => {
837 const code_start = try reserveSize(gpa, binary_bytes);826 const code_start = try reserveSizeHeader(w);
838 defer replaceSize(binary_bytes, code_start);827 defer replaceSizeHeader(&aw, code_start);
839 try emitCallCtorsFunction(wasm, binary_bytes);828 try emitCallCtorsFunction(wasm, w);
840 },829 },
841 .__wasm_init_memory => {830 .__wasm_init_memory => {
842 const code_start = try reserveSize(gpa, binary_bytes);831 const code_start = try reserveSizeHeader(w);
843 defer replaceSize(binary_bytes, code_start);832 defer replaceSizeHeader(&aw, code_start);
844 try emitInitMemoryFunction(wasm, binary_bytes, &virtual_addrs);833 try emitInitMemoryFunction(wasm, w, &virtual_addrs);
845 },834 },
846 .__wasm_init_tls => {835 .__wasm_init_tls => {
847 const code_start = try reserveSize(gpa, binary_bytes);836 const code_start = try reserveSizeHeader(w);
848 defer replaceSize(binary_bytes, code_start);837 defer replaceSizeHeader(&aw, code_start);
849 try emitInitTlsFunction(wasm, binary_bytes);838 try emitInitTlsFunction(wasm, w);
850 },839 },
851 .object_function => |i| {840 .object_function => |i| {
852 const ptr = i.ptr(wasm);841 const ptr = i.ptr(wasm);
853 const code = ptr.code.slice(wasm);842 const code = ptr.code.slice(wasm);
854 try leb.writeUleb128(binary_writer, code.len);843 try w.writeLeb128(code.len);
855 const code_start = binary_bytes.items.len;844 const code_start = w.end;
856 try binary_bytes.appendSlice(gpa, code);845 try w.writeAll(code);
857 if (!is_obj) applyRelocs(binary_bytes.items[code_start..], ptr.offset, ptr.relocations(wasm), wasm);846 if (!is_obj) applyRelocs(aw.getWritten()[code_start..], ptr.offset, ptr.relocations(wasm), wasm);
858 },847 },
859 .zcu_func => |i| {848 .zcu_func => |i| {
860 const code_start = try reserveSize(gpa, binary_bytes);849 const code_start = try reserveSizeHeader(w);
861 defer replaceSize(binary_bytes, code_start);850 defer replaceSizeHeader(&aw, code_start);
862851
863 log.debug("lowering function code for '{s}'", .{resolution.name(wasm).?});852 log.debug("lowering function code for '{s}'", .{resolution.name(wasm).?});
864853
...@@ -867,7 +856,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -867,7 +856,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
867 const ip_index = i.key(wasm).*;856 const ip_index = i.key(wasm).*;
868 switch (ip.indexToKey(ip_index)) {857 switch (ip.indexToKey(ip_index)) {
869 .enum_type => {858 .enum_type => {
870 try emitTagNameFunction(wasm, binary_bytes, f.data_segments.get(.__zig_tag_name_table).?, i.value(wasm).tag_name.table_index, ip_index);859 try emitTagNameFunction(wasm, w, f.data_segments.get(.__zig_tag_name_table).?, i.value(wasm).tag_name.table_index, ip_index);
871 },860 },
872 else => {861 else => {
873 const func = i.value(wasm).function;862 const func = i.value(wasm).function;
...@@ -882,13 +871,13 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -882,13 +871,13 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
882 .func_tys = undefined,871 .func_tys = undefined,
883 .error_name_table_ref_count = undefined,872 .error_name_table_ref_count = undefined,
884 };873 };
885 try mir.lower(wasm, binary_bytes);874 try mir.lower(wasm, w);
886 },875 },
887 }876 }
888 },877 },
889 };878 };
890879
891 replaceVecSectionHeader(binary_bytes, header_offset, .code, @intCast(wasm.functions.entries.len));880 replaceVecSectionHeader(&aw, header_offset, .code, @intCast(wasm.functions.entries.len));
892 code_section_index = section_index;881 code_section_index = section_index;
893 section_index += 1;882 section_index += 1;
894 }883 }
...@@ -924,7 +913,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -924,7 +913,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
924913
925 // Data section.914 // Data section.
926 if (f.data_segment_groups.items.len != 0) {915 if (f.data_segment_groups.items.len != 0) {
927 const header_offset = try reserveVecSectionHeader(gpa, binary_bytes);916 const header_offset = try reserveVecSectionHeader(w);
928917
929 var group_index: u32 = 0;918 var group_index: u32 = 0;
930 var segment_offset: u32 = 0;919 var segment_offset: u32 = 0;
...@@ -932,7 +921,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -932,7 +921,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
932 var group_end_addr = f.data_segment_groups.items[group_index].end_addr;921 var group_end_addr = f.data_segment_groups.items[group_index].end_addr;
933 for (segment_ids, segment_vaddrs) |segment_id, segment_vaddr| {922 for (segment_ids, segment_vaddrs) |segment_id, segment_vaddr| {
934 if (segment_vaddr >= group_end_addr) {923 if (segment_vaddr >= group_end_addr) {
935 try binary_bytes.appendNTimes(gpa, 0, group_end_addr - group_start_addr - segment_offset);924 try w.splatByteAll(0, group_end_addr - group_start_addr - segment_offset);
936 group_index += 1;925 group_index += 1;
937 if (group_index >= f.data_segment_groups.items.len) {926 if (group_index >= f.data_segment_groups.items.len) {
938 // All remaining segments are zero.927 // All remaining segments are zero.
...@@ -946,12 +935,10 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -946,12 +935,10 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
946 const group_size = group_end_addr - group_start_addr;935 const group_size = group_end_addr - group_start_addr;
947 log.debug("emit data section group, {d} bytes", .{group_size});936 log.debug("emit data section group, {d} bytes", .{group_size});
948 const flags: Object.DataSegmentFlags = if (segment_id.isPassive(wasm)) .passive else .active;937 const flags: Object.DataSegmentFlags = if (segment_id.isPassive(wasm)) .passive else .active;
949 try leb.writeUleb128(binary_writer, @intFromEnum(flags));938 try w.writeLeb128(@intFromEnum(flags));
950 // Passive segments are initialized at runtime.939 // Passive segments are initialized at runtime.
951 if (flags != .passive) {940 if (flags != .passive) try emitInit(w, .{ .i32_const = @as(i32, @bitCast(group_start_addr)) });
952 try emitInit(binary_writer, .{ .i32_const = @as(i32, @bitCast(group_start_addr)) });941 try w.writeLeb128(group_size);
953 }
954 try leb.writeUleb128(binary_writer, group_size);
955 }942 }
956 if (segment_id.isEmpty(wasm)) {943 if (segment_id.isEmpty(wasm)) {
957 // It counted for virtual memory but it does not go into the binary.944 // It counted for virtual memory but it does not go into the binary.
...@@ -960,62 +947,62 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -960,62 +947,62 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
960947
961 // Padding for alignment.948 // Padding for alignment.
962 const needed_offset = segment_vaddr - group_start_addr;949 const needed_offset = segment_vaddr - group_start_addr;
963 try binary_bytes.appendNTimes(gpa, 0, needed_offset - segment_offset);950 try w.splatByteAll(0, needed_offset - segment_offset);
964 segment_offset = needed_offset;951 segment_offset = needed_offset;
965952
966 const code_start = binary_bytes.items.len;953 const code_start = w.end;
967 append: {954 append: {
968 const code = switch (segment_id.unpack(wasm)) {955 const code = switch (segment_id.unpack(wasm)) {
969 .__heap_base => {956 .__heap_base => {
970 mem.writeInt(u32, try binary_bytes.addManyAsArray(gpa, 4), virtual_addrs.heap_base, .little);957 try w.writeInt(u32, virtual_addrs.heap_base, .little);
971 break :append;958 break :append;
972 },959 },
973 .__heap_end => {960 .__heap_end => {
974 mem.writeInt(u32, try binary_bytes.addManyAsArray(gpa, 4), virtual_addrs.heap_end, .little);961 try w.writeInt(u32, virtual_addrs.heap_end, .little);
975 break :append;962 break :append;
976 },963 },
977 .__zig_error_names => {964 .__zig_error_names => {
978 try binary_bytes.appendSlice(gpa, wasm.error_name_bytes.items);965 try w.writeAll(wasm.error_name_bytes.items);
979 break :append;966 break :append;
980 },967 },
981 .__zig_error_name_table => {968 .__zig_error_name_table => {
982 if (is_obj) @panic("TODO error name table reloc");969 if (is_obj) @panic("TODO error name table reloc");
983 const base = f.data_segments.get(.__zig_error_names).?;970 const base = f.data_segments.get(.__zig_error_names).?;
984 if (!is64) {971 if (!is64) {
985 try emitTagNameTable(gpa, binary_bytes, wasm.error_name_offs.items, wasm.error_name_bytes.items, base, u32);972 try emitTagNameTable(w, wasm.error_name_offs.items, wasm.error_name_bytes.items, base, u32);
986 } else {973 } else {
987 try emitTagNameTable(gpa, binary_bytes, wasm.error_name_offs.items, wasm.error_name_bytes.items, base, u64);974 try emitTagNameTable(w, wasm.error_name_offs.items, wasm.error_name_bytes.items, base, u64);
988 }975 }
989 break :append;976 break :append;
990 },977 },
991 .__zig_tag_names => {978 .__zig_tag_names => {
992 try binary_bytes.appendSlice(gpa, wasm.tag_name_bytes.items);979 try w.writeAll(wasm.tag_name_bytes.items);
993 break :append;980 break :append;
994 },981 },
995 .__zig_tag_name_table => {982 .__zig_tag_name_table => {
996 if (is_obj) @panic("TODO tag name table reloc");983 if (is_obj) @panic("TODO tag name table reloc");
997 const base = f.data_segments.get(.__zig_tag_names).?;984 const base = f.data_segments.get(.__zig_tag_names).?;
998 if (!is64) {985 if (!is64) {
999 try emitTagNameTable(gpa, binary_bytes, wasm.tag_name_offs.items, wasm.tag_name_bytes.items, base, u32);986 try emitTagNameTable(w, wasm.tag_name_offs.items, wasm.tag_name_bytes.items, base, u32);
1000 } else {987 } else {
1001 try emitTagNameTable(gpa, binary_bytes, wasm.tag_name_offs.items, wasm.tag_name_bytes.items, base, u64);988 try emitTagNameTable(w, wasm.tag_name_offs.items, wasm.tag_name_bytes.items, base, u64);
1002 }989 }
1003 break :append;990 break :append;
1004 },991 },
1005 .object => |i| {992 .object => |i| {
1006 const ptr = i.ptr(wasm);993 const ptr = i.ptr(wasm);
1007 try binary_bytes.appendSlice(gpa, ptr.payload.slice(wasm));994 try w.writeAll(ptr.payload.slice(wasm));
1008 if (!is_obj) applyRelocs(binary_bytes.items[code_start..], ptr.offset, ptr.relocations(wasm), wasm);995 if (!is_obj) applyRelocs(aw.getWritten()[code_start..], ptr.offset, ptr.relocations(wasm), wasm);
1009 break :append;996 break :append;
1010 },997 },
1011 inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code,998 inline .uav_exe, .uav_obj, .nav_exe, .nav_obj => |i| i.value(wasm).code,
1012 };999 };
1013 try binary_bytes.appendSlice(gpa, code.slice(wasm));1000 try w.writeAll(code.slice(wasm));
1014 }1001 }
1015 segment_offset += @intCast(binary_bytes.items.len - code_start);1002 segment_offset += @intCast(w.end - code_start);
1016 }1003 }
10171004
1018 replaceVecSectionHeader(binary_bytes, header_offset, .data, @intCast(f.data_segment_groups.items.len));1005 replaceVecSectionHeader(&aw, header_offset, .data, @intCast(f.data_segment_groups.items.len));
1019 data_section_index = section_index;1006 data_section_index = section_index;
1020 section_index += 1;1007 section_index += 1;
1021 }1008 }
...@@ -1023,7 +1010,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -1023,7 +1010,7 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
1023 if (is_obj) {1010 if (is_obj) {
1024 @panic("TODO emit link section for object file and emit modified relocations");1011 @panic("TODO emit link section for object file and emit modified relocations");
1025 } else if (comp.config.debug_format != .strip) {1012 } else if (comp.config.debug_format != .strip) {
1026 try emitNameSection(wasm, f.data_segment_groups.items, binary_bytes);1013 try emitNameSection(wasm, &aw, f.data_segment_groups.items);
1027 }1014 }
10281015
1029 if (comp.config.debug_format != .strip) {1016 if (comp.config.debug_format != .strip) {
...@@ -1033,17 +1020,17 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -1033,17 +1020,17 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
1033 .none => {},1020 .none => {},
1034 .fast => {1021 .fast => {
1035 var id: [16]u8 = undefined;1022 var id: [16]u8 = undefined;
1036 std.crypto.hash.sha3.TurboShake128(null).hash(binary_bytes.items, &id, .{});1023 std.crypto.hash.sha3.TurboShake128(null).hash(w.buffered(), &id, .{});
1037 var uuid: [36]u8 = undefined;1024 var uuid: [36]u8 = undefined;
1038 _ = try std.fmt.bufPrint(&uuid, "{x}-{x}-{x}-{x}-{x}", .{1025 _ = try std.fmt.bufPrint(&uuid, "{x}-{x}-{x}-{x}-{x}", .{
1039 id[0..4], id[4..6], id[6..8], id[8..10], id[10..],1026 id[0..4], id[4..6], id[6..8], id[8..10], id[10..],
1040 });1027 });
1041 try emitBuildIdSection(gpa, binary_bytes, &uuid);1028 try emitBuildIdSection(&aw, &uuid);
1042 },1029 },
1043 .hexstring => |hs| {1030 .hexstring => |hs| {
1044 var buffer: [32 * 2]u8 = undefined;1031 var buffer: [32 * 2]u8 = undefined;
1045 const str = std.fmt.bufPrint(&buffer, "{x}", .{hs.toSlice()}) catch unreachable;1032 const str = std.fmt.bufPrint(&buffer, "{x}", .{hs.toSlice()}) catch unreachable;
1046 try emitBuildIdSection(gpa, binary_bytes, str);1033 try emitBuildIdSection(&aw, str);
1047 },1034 },
1048 else => |mode| {1035 else => |mode| {
1049 var err = try diags.addErrorWithNotes(0);1036 var err = try diags.addErrorWithNotes(0);
...@@ -1054,14 +1041,17 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {...@@ -1054,14 +1041,17 @@ pub fn finish(f: *Flush, wasm: *Wasm) !void {
1054 var debug_bytes = std.ArrayList(u8).init(gpa);1041 var debug_bytes = std.ArrayList(u8).init(gpa);
1055 defer debug_bytes.deinit();1042 defer debug_bytes.deinit();
10561043
1057 try emitProducerSection(gpa, binary_bytes);1044 try emitProducerSection(&aw);
1058 try emitFeaturesSection(gpa, binary_bytes, target);1045 emitFeaturesSection(&aw, target) catch |err| switch (err) {
1046 error.WriteFailed => return error.OutOfMemory,
1047 };
1059 }1048 }
10601049
1061 // Finally, write the entire binary into the file.1050 // Finally, write the entire binary into the file.
1062 const file = wasm.base.file.?;1051 const file = wasm.base.file.?;
1063 try file.pwriteAll(binary_bytes.items, 0);1052 const contents = aw.getWritten();
1064 try file.setEndPos(binary_bytes.items.len);1053 try file.setEndPos(contents.len);
1054 try file.pwriteAll(contents, 0);
1065}1055}
10661056
1067const VirtualAddrs = struct {1057const VirtualAddrs = struct {
...@@ -1076,170 +1066,155 @@ const VirtualAddrs = struct {...@@ -1076,170 +1066,155 @@ const VirtualAddrs = struct {
10761066
1077fn emitNameSection(1067fn emitNameSection(
1078 wasm: *Wasm,1068 wasm: *Wasm,
1069 aw: *Writer.Allocating,
1079 data_segment_groups: []const DataSegmentGroup,1070 data_segment_groups: []const DataSegmentGroup,
1080 binary_bytes: *std.ArrayListUnmanaged(u8),
1081) !void {1071) !void {
1082 const f = &wasm.flush_buffer;1072 const f = &wasm.flush_buffer;
1083 const comp = wasm.base.comp;1073 const w = &aw.writer;
1084 const gpa = comp.gpa;1074 const header_offset = try reserveSectionHeader(w);
1075 defer replaceSectionHeader(aw, header_offset, @intFromEnum(std.wasm.Section.custom));
10851076
1086 const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);1077 const section_name = "name";
1087 defer writeCustomSectionHeader(binary_bytes, header_offset);1078 try w.writeLeb128(section_name.len);
10881079 try w.writeAll(section_name);
1089 const name_name = "name";
1090 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, name_name.len));
1091 try binary_bytes.appendSlice(gpa, name_name);
10921080
1093 {1081 {
1094 const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes);1082 const sub_header_offset = try reserveSectionHeader(w);
1095 defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.function));1083 defer replaceSectionHeader(aw, sub_header_offset, @intFromEnum(std.wasm.NameSubsection.function));
1096
1097 const total_functions: u32 = @intCast(f.function_imports.entries.len + wasm.functions.entries.len);
1098 try leb.writeUleb128(binary_bytes.writer(gpa), total_functions);
10991084
1085 try w.writeLeb128(f.function_imports.entries.len + wasm.functions.entries.len);
1100 for (f.function_imports.keys(), 0..) |name_index, function_index| {1086 for (f.function_imports.keys(), 0..) |name_index, function_index| {
1101 const name = name_index.slice(wasm);1087 const name = name_index.slice(wasm);
1102 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(function_index)));1088 try w.writeLeb128(function_index);
1103 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));1089 try w.writeLeb128(name.len);
1104 try binary_bytes.appendSlice(gpa, name);1090 try w.writeAll(name);
1105 }1091 }
1106 for (wasm.functions.keys(), f.function_imports.entries.len..) |resolution, function_index| {1092 for (wasm.functions.keys(), f.function_imports.entries.len..) |resolution, function_index| {
1107 const name = resolution.name(wasm).?;1093 const name = resolution.name(wasm).?;
1108 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(function_index)));1094 try w.writeLeb128(function_index);
1109 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));1095 try w.writeLeb128(name.len);
1110 try binary_bytes.appendSlice(gpa, name);1096 try w.writeAll(name);
1111 }1097 }
1112 }1098 }
11131099
1114 {1100 {
1115 const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes);1101 const sub_header_offset = try reserveSectionHeader(w);
1116 defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.global));1102 defer replaceSectionHeader(aw, sub_header_offset, @intFromEnum(std.wasm.NameSubsection.global));
1117
1118 const total_globals: u32 = @intCast(f.global_imports.entries.len + wasm.globals.entries.len);
1119 try leb.writeUleb128(binary_bytes.writer(gpa), total_globals);
11201103
1104 try w.writeLeb128(f.global_imports.entries.len + wasm.globals.entries.len);
1121 for (f.global_imports.keys(), 0..) |name_index, global_index| {1105 for (f.global_imports.keys(), 0..) |name_index, global_index| {
1122 const name = name_index.slice(wasm);1106 const name = name_index.slice(wasm);
1123 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(global_index)));1107 try w.writeLeb128(global_index);
1124 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));1108 try w.writeLeb128(name.len);
1125 try binary_bytes.appendSlice(gpa, name);1109 try w.writeAll(name);
1126 }1110 }
1127 for (wasm.globals.keys(), f.global_imports.entries.len..) |resolution, global_index| {1111 for (wasm.globals.keys(), f.global_imports.entries.len..) |resolution, global_index| {
1128 const name = resolution.name(wasm).?;1112 const name = resolution.name(wasm).?;
1129 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(global_index)));1113 try w.writeLeb128(global_index);
1130 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));1114 try w.writeLeb128(name.len);
1131 try binary_bytes.appendSlice(gpa, name);1115 try w.writeAll(name);
1132 }1116 }
1133 }1117 }
11341118
1135 {1119 {
1136 const sub_offset = try reserveCustomSectionHeader(gpa, binary_bytes);1120 const sub_header_offset = try reserveSectionHeader(w);
1137 defer replaceHeader(binary_bytes, sub_offset, @intFromEnum(std.wasm.NameSubsection.data_segment));1121 defer replaceSectionHeader(aw, sub_header_offset, @intFromEnum(std.wasm.NameSubsection.data_segment));
11381122
1139 const total_data_segments: u32 = @intCast(data_segment_groups.len);1123 try w.writeLeb128(data_segment_groups.len);
1140 try leb.writeUleb128(binary_bytes.writer(gpa), total_data_segments);1124 for (data_segment_groups, 0..) |group, group_index| {
1141
1142 for (data_segment_groups, 0..) |group, i| {
1143 const name, _ = splitSegmentName(group.first_segment.name(wasm));1125 const name, _ = splitSegmentName(group.first_segment.name(wasm));
1144 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(i)));1126 try w.writeLeb128(group_index);
1145 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));1127 try w.writeLeb128(name.len);
1146 try binary_bytes.appendSlice(gpa, name);1128 try w.writeAll(name);
1147 }1129 }
1148 }1130 }
1149}1131}
11501132
1151fn emitFeaturesSection(1133fn emitFeaturesSection(aw: *Writer.Allocating, target: *const std.Target) !void {
1152 gpa: Allocator,
1153 binary_bytes: *std.ArrayListUnmanaged(u8),
1154 target: *const std.Target,
1155) Allocator.Error!void {
1156 const feature_count = target.cpu.features.count();1134 const feature_count = target.cpu.features.count();
1157 if (feature_count == 0) return;1135 if (feature_count == 0) return;
11581136
1159 const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);1137 const w = &aw.writer;
1160 defer writeCustomSectionHeader(binary_bytes, header_offset);1138 const header_offset = try reserveSectionHeader(w);
11611139 defer replaceSectionHeader(aw, header_offset, @intFromEnum(std.wasm.Section.custom));
1162 const writer = binary_bytes.writer(gpa);
1163 const target_features = "target_features";
1164 try leb.writeUleb128(writer, @as(u32, @intCast(target_features.len)));
1165 try writer.writeAll(target_features);
11661140
1167 try leb.writeUleb128(writer, @as(u32, @intCast(feature_count)));1141 const section_name = "target_features";
1142 try w.writeLeb128(section_name.len);
1143 try w.writeAll(section_name);
11681144
1145 try w.writeLeb128(feature_count);
1169 var safety_count = feature_count;1146 var safety_count = feature_count;
1170 for (target.cpu.arch.allFeaturesList(), 0..) |*feature, i| {1147 for (target.cpu.arch.allFeaturesList(), 0..) |*feature, i| {
1171 if (!target.cpu.has(.wasm, @as(std.Target.wasm.Feature, @enumFromInt(i)))) continue;1148 if (!target.cpu.has(.wasm, @as(std.Target.wasm.Feature, @enumFromInt(i)))) continue;
1172 safety_count -= 1;1149 safety_count -= 1;
11731150
1174 try leb.writeUleb128(writer, @as(u32, '+'));1151 try w.writeUleb128('+');
1175 // Depends on llvm_name for the hyphenated version that matches wasm tooling conventions.1152 // Depends on llvm_name for the hyphenated version that matches wasm tooling conventions.
1176 const name = feature.llvm_name.?;1153 const name = feature.llvm_name.?;
1177 try leb.writeUleb128(writer, @as(u32, @intCast(name.len)));1154 try w.writeLeb128(name.len);
1178 try writer.writeAll(name);1155 try w.writeAll(name);
1179 }1156 }
1180 assert(safety_count == 0);1157 assert(safety_count == 0);
1181}1158}
11821159
1183fn emitBuildIdSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8), build_id: []const u8) !void {1160fn emitBuildIdSection(aw: *Writer.Allocating, build_id: []const u8) !void {
1184 const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);1161 const w = &aw.writer;
1185 defer writeCustomSectionHeader(binary_bytes, header_offset);1162 const header_offset = try reserveSectionHeader(w);
1163 defer replaceSectionHeader(aw, header_offset, @intFromEnum(std.wasm.Section.custom));
11861164
1187 const writer = binary_bytes.writer(gpa);1165 const section_name = "build_id";
1188 const hdr_build_id = "build_id";1166 try w.writeLeb128(section_name.len);
1189 try leb.writeUleb128(writer, @as(u32, @intCast(hdr_build_id.len)));1167 try w.writeAll(section_name);
1190 try writer.writeAll(hdr_build_id);
11911168
1192 try leb.writeUleb128(writer, @as(u32, 1));1169 try w.writeUleb128(1);
1193 try leb.writeUleb128(writer, @as(u32, @intCast(build_id.len)));1170 try w.writeLeb128(build_id.len);
1194 try writer.writeAll(build_id);1171 try w.writeAll(build_id);
1195}1172}
11961173
1197fn emitProducerSection(gpa: Allocator, binary_bytes: *std.ArrayListUnmanaged(u8)) !void {1174fn emitProducerSection(aw: *Writer.Allocating) !void {
1198 const header_offset = try reserveCustomSectionHeader(gpa, binary_bytes);1175 const w = &aw.writer;
1199 defer writeCustomSectionHeader(binary_bytes, header_offset);1176 const header_offset = try reserveSectionHeader(w);
12001177 defer replaceSectionHeader(aw, header_offset, @intFromEnum(std.wasm.Section.custom));
1201 const writer = binary_bytes.writer(gpa);
1202 const producers = "producers";
1203 try leb.writeUleb128(writer, @as(u32, @intCast(producers.len)));
1204 try writer.writeAll(producers);
12051178
1206 try leb.writeUleb128(writer, @as(u32, 2)); // 2 fields: Language + processed-by1179 const section_name = "producers";
1180 try w.writeLeb128(section_name.len);
1181 try w.writeAll(section_name);
12071182
1208 // language field1183 try w.writeUleb128(2); // 2 fields: language + processed-by
1209 {1184 {
1210 const language = "language";1185 const field_name = "language";
1211 try leb.writeUleb128(writer, @as(u32, @intCast(language.len)));1186 try w.writeLeb128(field_name.len);
1212 try writer.writeAll(language);1187 try w.writeAll(field_name);
12131188
1214 // field_value_count (TODO: Parse object files for producer sections to detect their language)1189 // field_value_count (TODO: Parse object files for producer sections to detect their language)
1215 try leb.writeUleb128(writer, @as(u32, 1));1190 try w.writeUleb128(1);
12161191
1217 // versioned name1192 // versioned name
1218 {1193 {
1219 try leb.writeUleb128(writer, @as(u32, 3)); // len of "Zig"1194 const field_value = "Zig";
1220 try writer.writeAll("Zig");1195 try w.writeLeb128(field_value.len);
1196 try w.writeAll(field_value);
12211197
1222 try leb.writeUleb128(writer, @as(u32, @intCast(build_options.version.len)));1198 try w.writeLeb128(build_options.version.len);
1223 try writer.writeAll(build_options.version);1199 try w.writeAll(build_options.version);
1224 }1200 }
1225 }1201 }
1226
1227 // processed-by field
1228 {1202 {
1229 const processed_by = "processed-by";1203 const field_name = "processed-by";
1230 try leb.writeUleb128(writer, @as(u32, @intCast(processed_by.len)));1204 try w.writeLeb128(field_name.len);
1231 try writer.writeAll(processed_by);1205 try w.writeAll(field_name);
12321206
1233 // field_value_count (TODO: Parse object files for producer sections to detect other used tools)1207 // field_value_count (TODO: Parse object files for producer sections to detect other used tools)
1234 try leb.writeUleb128(writer, @as(u32, 1));1208 try w.writeUleb128(1);
12351209
1236 // versioned name1210 // versioned name
1237 {1211 {
1238 try leb.writeUleb128(writer, @as(u32, 3)); // len of "Zig"1212 const field_value = "Zig";
1239 try writer.writeAll("Zig");1213 try w.writeLeb128(field_value.len);
1214 try w.writeAll(field_value);
12401215
1241 try leb.writeUleb128(writer, @as(u32, @intCast(build_options.version.len)));1216 try w.writeLeb128(build_options.version.len);
1242 try writer.writeAll(build_options.version);1217 try w.writeAll(build_options.version);
1243 }1218 }
1244 }1219 }
1245}1220}
...@@ -1277,170 +1252,130 @@ fn wantSegmentMerge(...@@ -1277,170 +1252,130 @@ fn wantSegmentMerge(
1277}1252}
12781253
1279/// section id + fixed leb contents size + fixed leb vector length1254/// section id + fixed leb contents size + fixed leb vector length
1280const section_header_reserve_size = 1 + 5 + 5;1255const vec_section_header_size = section_header_size + size_header_size;
1281const section_header_size = 5 + 1;
12821256
1283fn reserveVecSectionHeader(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!u32 {1257fn reserveVecSectionHeader(w: *Writer) Writer.Error!u32 {
1284 try bytes.appendNTimes(gpa, 0, section_header_reserve_size);1258 const offset = w.end;
1285 return @intCast(bytes.items.len - section_header_reserve_size);1259 _ = try w.writableSlice(vec_section_header_size);
1260 return @intCast(offset);
1286}1261}
12871262
1288fn replaceVecSectionHeader(1263fn replaceVecSectionHeader(
1289 bytes: *std.ArrayListUnmanaged(u8),1264 aw: *Writer.Allocating,
1290 offset: u32,1265 offset: u32,
1291 section: std.wasm.Section,1266 section: std.wasm.Section,
1292 n_items: u32,1267 n_items: u32,
1293) void {1268) void {
1294 const size: u32 = @intCast(bytes.items.len - offset - section_header_reserve_size + uleb128size(n_items));1269 const header = aw.getWritten()[offset..][0..vec_section_header_size];
1295 var buf: [section_header_reserve_size]u8 = undefined;1270 header[0] = @intFromEnum(section);
1296 var fbw = std.io.fixedBufferStream(&buf);1271 std.leb.writeUnsignedFixed(5, header[1..6], @intCast(aw.writer.end - offset - section_header_size));
1297 const w = fbw.writer();1272 std.leb.writeUnsignedFixed(5, header[6..], n_items);
1298 w.writeByte(@intFromEnum(section)) catch unreachable;
1299 leb.writeUleb128(w, size) catch unreachable;
1300 leb.writeUleb128(w, n_items) catch unreachable;
1301 bytes.replaceRangeAssumeCapacity(offset, section_header_reserve_size, fbw.getWritten());
1302}1273}
13031274
1304fn reserveCustomSectionHeader(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!u32 {1275const section_header_size = 1 + size_header_size;
1305 try bytes.appendNTimes(gpa, 0, section_header_size);
1306 return @intCast(bytes.items.len - section_header_size);
1307}
13081276
1309fn writeCustomSectionHeader(bytes: *std.ArrayListUnmanaged(u8), offset: u32) void {1277fn reserveSectionHeader(w: *Writer) Writer.Error!u32 {
1310 return replaceHeader(bytes, offset, 0); // 0 = 'custom' section1278 const offset = w.end;
1279 _ = try w.writableSlice(section_header_size);
1280 return @intCast(offset);
1311}1281}
13121282
1313fn replaceHeader(bytes: *std.ArrayListUnmanaged(u8), offset: u32, tag: u8) void {1283fn replaceSectionHeader(aw: *Writer.Allocating, offset: u32, section: u8) void {
1314 const size: u32 = @intCast(bytes.items.len - offset - section_header_size);1284 const header = aw.getWritten()[offset..][0..section_header_size];
1315 var buf: [section_header_size]u8 = undefined;1285 header[0] = section;
1316 var fbw = std.io.fixedBufferStream(&buf);1286 std.leb.writeUnsignedFixed(5, header[1..6], @intCast(aw.writer.end - offset - section_header_size));
1317 const w = fbw.writer();
1318 w.writeByte(tag) catch unreachable;
1319 leb.writeUleb128(w, size) catch unreachable;
1320 bytes.replaceRangeAssumeCapacity(offset, section_header_size, fbw.getWritten());
1321}1287}
13221288
1323const max_size_encoding = 5;1289const size_header_size = 5;
13241290
1325fn reserveSize(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!u32 {1291fn reserveSizeHeader(w: *Writer) Writer.Error!u32 {
1326 try bytes.appendNTimes(gpa, 0, max_size_encoding);1292 const offset = w.end;
1327 return @intCast(bytes.items.len - max_size_encoding);1293 _ = try w.writableSlice(size_header_size);
1294 return @intCast(offset);
1328}1295}
13291296
1330fn replaceSize(bytes: *std.ArrayListUnmanaged(u8), offset: u32) void {1297fn replaceSizeHeader(aw: *Writer.Allocating, offset: u32) void {
1331 const size: u32 = @intCast(bytes.items.len - offset - max_size_encoding);1298 const header = aw.getWritten()[offset..][0..size_header_size];
1332 var buf: [max_size_encoding]u8 = undefined;1299 std.leb.writeUnsignedFixed(5, header[0..5], @intCast(aw.writer.end - offset - size_header_size));
1333 var fbw = std.io.fixedBufferStream(&buf);
1334 leb.writeUleb128(fbw.writer(), size) catch unreachable;
1335 bytes.replaceRangeAssumeCapacity(offset, max_size_encoding, fbw.getWritten());
1336}1300}
13371301
1338fn emitLimits(1302fn emitLimits(w: *Writer, limits: std.wasm.Limits) Writer.Error!void {
1339 gpa: Allocator,1303 try w.writeByte(@bitCast(limits.flags));
1340 binary_bytes: *std.ArrayListUnmanaged(u8),1304 try w.writeLeb128(limits.min);
1341 limits: std.wasm.Limits,1305 if (limits.flags.has_max) try w.writeLeb128(limits.max);
1342) Allocator.Error!void {
1343 try binary_bytes.append(gpa, @bitCast(limits.flags));
1344 try leb.writeUleb128(binary_bytes.writer(gpa), limits.min);
1345 if (limits.flags.has_max) try leb.writeUleb128(binary_bytes.writer(gpa), limits.max);
1346}1306}
13471307
1348fn emitMemoryImport(1308fn emitMemoryImport(
1349 wasm: *Wasm,1309 wasm: *Wasm,
1350 binary_bytes: *std.ArrayListUnmanaged(u8),1310 w: *Writer,
1351 name_index: String,1311 name_index: String,
1352 memory_import: *const Wasm.MemoryImport,1312 memory_import: *const Wasm.MemoryImport,
1353) Allocator.Error!void {1313) Writer.Error!void {
1354 const gpa = wasm.base.comp.gpa;
1355 const module_name = memory_import.module_name.slice(wasm);1314 const module_name = memory_import.module_name.slice(wasm);
1356 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(module_name.len)));1315 try w.writeLeb128(module_name.len);
1357 try binary_bytes.appendSlice(gpa, module_name);1316 try w.writeAll(module_name);
13581317
1359 const name = name_index.slice(wasm);1318 const name = name_index.slice(wasm);
1360 try leb.writeUleb128(binary_bytes.writer(gpa), @as(u32, @intCast(name.len)));1319 try w.writeLeb128(name.len);
1361 try binary_bytes.appendSlice(gpa, name);1320 try w.writeAll(name);
13621321
1363 try binary_bytes.append(gpa, @intFromEnum(std.wasm.ExternalKind.memory));1322 try w.writeByte(@intFromEnum(std.wasm.ExternalKind.memory));
1364 try emitLimits(gpa, binary_bytes, memory_import.limits());1323 try emitLimits(w, memory_import.limits());
1365}1324}
13661325
1367pub fn emitInit(writer: anytype, init_expr: std.wasm.InitExpression) !void {1326pub fn emitInit(w: *Writer, init_expr: std.wasm.InitExpression) Writer.Error!void {
1368 switch (init_expr) {1327 switch (init_expr) {
1369 .i32_const => |val| {1328 inline else => |val, tag| {
1370 try writer.writeByte(@intFromEnum(std.wasm.Opcode.i32_const));1329 try w.writeByte(@intFromEnum(@field(std.wasm.Opcode, @tagName(tag))));
1371 try leb.writeIleb128(writer, val);1330 switch (@typeInfo(@TypeOf(val))) {
1372 },1331 .int => try w.writeLeb128(val),
1373 .i64_const => |val| {1332 .float => |float| try w.writeInt(
1374 try writer.writeByte(@intFromEnum(std.wasm.Opcode.i64_const));1333 @Type(.{ .int = .{ .signedness = .unsigned, .bits = float.bits } }),
1375 try leb.writeIleb128(writer, val);1334 @bitCast(val),
1376 },1335 .little,
1377 .f32_const => |val| {1336 ),
1378 try writer.writeByte(@intFromEnum(std.wasm.Opcode.f32_const));1337 else => comptime unreachable,
1379 try writer.writeInt(u32, @bitCast(val), .little);1338 }
1380 },
1381 .f64_const => |val| {
1382 try writer.writeByte(@intFromEnum(std.wasm.Opcode.f64_const));
1383 try writer.writeInt(u64, @bitCast(val), .little);
1384 },
1385 .global_get => |val| {
1386 try writer.writeByte(@intFromEnum(std.wasm.Opcode.global_get));
1387 try leb.writeUleb128(writer, val);
1388 },1339 },
1389 }1340 }
1390 try writer.writeByte(@intFromEnum(std.wasm.Opcode.end));1341 try w.writeByte(@intFromEnum(std.wasm.Opcode.end));
1391}1342}
13921343
1393pub fn emitExpr(wasm: *const Wasm, binary_bytes: *std.ArrayListUnmanaged(u8), expr: Wasm.Expr) Allocator.Error!void {1344pub fn emitExpr(wasm: *const Wasm, w: *Writer, expr: Wasm.Expr) Writer.Error!void {
1394 const gpa = wasm.base.comp.gpa;
1395 const slice = expr.slice(wasm);1345 const slice = expr.slice(wasm);
1396 try binary_bytes.appendSlice(gpa, slice[0 .. slice.len + 1]); // +1 to include end opcode1346 try w.writeAll(slice[0 .. slice.len + 1]); // +1 to include end opcode
1397}1347}
13981348
1399fn emitSegmentInfo(wasm: *Wasm, binary_bytes: *std.ArrayList(u8)) !void {1349fn emitSegmentInfo(wasm: *Wasm, aw: *Writer) Writer.Error!void {
1400 const gpa = wasm.base.comp.gpa;1350 const w = &aw.writer;
1401 const writer = binary_bytes.writer(gpa);1351 const header_offset = try reserveSectionHeader(w);
1402 try leb.writeUleb128(writer, @intFromEnum(Wasm.SubsectionType.segment_info));1352 defer replaceSectionHeader(aw, header_offset, @intFromEnum(Wasm.SubsectionType.segment_info));
1403 const segment_offset = binary_bytes.items.len;
14041353
1405 try leb.writeUleb128(writer, @as(u32, @intCast(wasm.segment_info.count())));1354 try w.writeLeb128(wasm.segment_info.count());
1406 for (wasm.segment_info.values()) |segment_info| {1355 for (wasm.segment_info.values()) |segment_info| {
1407 log.debug("Emit segment: {s} align({d}) flags({b})", .{1356 log.debug("Emit segment: {s} align({d}) flags({b})", .{
1408 segment_info.name,1357 segment_info.name,
1409 segment_info.alignment,1358 segment_info.alignment,
1410 segment_info.flags,1359 segment_info.flags,
1411 });1360 });
1412 try leb.writeUleb128(writer, @as(u32, @intCast(segment_info.name.len)));1361 try w.writeLeb128(segment_info.name.len);
1413 try writer.writeAll(segment_info.name);1362 try w.writeAll(segment_info.name);
1414 try leb.writeUleb128(writer, segment_info.alignment.toLog2Units());1363 try w.writeLeb128(segment_info.alignment.toLog2Units());
1415 try leb.writeUleb128(writer, segment_info.flags);1364 try w.writeLeb128(segment_info.flags);
1416 }1365 }
1417
1418 var buf: [5]u8 = undefined;
1419 leb.writeUnsignedFixed(5, &buf, @as(u32, @intCast(binary_bytes.items.len - segment_offset)));
1420 try binary_bytes.insertSlice(segment_offset, &buf);
1421}
1422
1423fn uleb128size(x: u32) u32 {
1424 var value = x;
1425 var size: u32 = 0;
1426 while (value != 0) : (size += 1) value >>= 7;
1427 return size;
1428}1366}
14291367
1430fn emitTagNameTable(1368fn emitTagNameTable(
1431 gpa: Allocator,1369 w: *Writer,
1432 code: *std.ArrayListUnmanaged(u8),
1433 tag_name_offs: []const u32,1370 tag_name_offs: []const u32,
1434 tag_name_bytes: []const u8,1371 tag_name_bytes: []const u8,
1435 base: u32,1372 base: u32,
1436 comptime Int: type,1373 comptime Int: type,
1437) error{OutOfMemory}!void {1374) Writer.Error!void {
1438 const ptr_size_bytes = @divExact(@bitSizeOf(Int), 8);
1439 try code.ensureUnusedCapacity(gpa, ptr_size_bytes * 2 * tag_name_offs.len);
1440 for (tag_name_offs) |off| {1375 for (tag_name_offs) |off| {
1441 const name_len: u32 = @intCast(mem.indexOfScalar(u8, tag_name_bytes[off..], 0).?);1376 const name_len: u32 = @intCast(mem.indexOfScalar(u8, tag_name_bytes[off..], 0).?);
1442 mem.writeInt(Int, code.addManyAsArrayAssumeCapacity(ptr_size_bytes), base + off, .little);1377 try w.writeInt(Int, base + off, .little);
1443 mem.writeInt(Int, code.addManyAsArrayAssumeCapacity(ptr_size_bytes), name_len, .little);1378 try w.writeInt(Int, name_len, .little);
1444 }1379 }
1445}1380}
14461381
...@@ -1525,11 +1460,11 @@ fn reloc_u64_table_index(code: []u8, i: IndirectFunctionTableIndex) void {...@@ -1525,11 +1460,11 @@ fn reloc_u64_table_index(code: []u8, i: IndirectFunctionTableIndex) void {
1525}1460}
15261461
1527fn reloc_sleb_table_index(code: []u8, i: IndirectFunctionTableIndex) void {1462fn reloc_sleb_table_index(code: []u8, i: IndirectFunctionTableIndex) void {
1528 leb.writeSignedFixed(5, code[0..5], i.toAbi());1463 std.leb.writeSignedFixed(5, code[0..5], i.toAbi());
1529}1464}
15301465
1531fn reloc_sleb64_table_index(code: []u8, i: IndirectFunctionTableIndex) void {1466fn reloc_sleb64_table_index(code: []u8, i: IndirectFunctionTableIndex) void {
1532 leb.writeSignedFixed(11, code[0..11], i.toAbi());1467 std.leb.writeSignedFixed(11, code[0..11], i.toAbi());
1533}1468}
15341469
1535fn reloc_u32_function(code: []u8, function: Wasm.OutputFunctionIndex) void {1470fn reloc_u32_function(code: []u8, function: Wasm.OutputFunctionIndex) void {
...@@ -1537,7 +1472,7 @@ fn reloc_u32_function(code: []u8, function: Wasm.OutputFunctionIndex) void {...@@ -1537,7 +1472,7 @@ fn reloc_u32_function(code: []u8, function: Wasm.OutputFunctionIndex) void {
1537}1472}
15381473
1539fn reloc_leb_function(code: []u8, function: Wasm.OutputFunctionIndex) void {1474fn reloc_leb_function(code: []u8, function: Wasm.OutputFunctionIndex) void {
1540 leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(function));1475 std.leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(function));
1541}1476}
15421477
1543fn reloc_u32_global(code: []u8, global: Wasm.GlobalIndex) void {1478fn reloc_u32_global(code: []u8, global: Wasm.GlobalIndex) void {
...@@ -1545,7 +1480,7 @@ fn reloc_u32_global(code: []u8, global: Wasm.GlobalIndex) void {...@@ -1545,7 +1480,7 @@ fn reloc_u32_global(code: []u8, global: Wasm.GlobalIndex) void {
1545}1480}
15461481
1547fn reloc_leb_global(code: []u8, global: Wasm.GlobalIndex) void {1482fn reloc_leb_global(code: []u8, global: Wasm.GlobalIndex) void {
1548 leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(global));1483 std.leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(global));
1549}1484}
15501485
1551const RelocAddr = struct {1486const RelocAddr = struct {
...@@ -1581,35 +1516,31 @@ fn reloc_u64_addr(code: []u8, ra: RelocAddr) void {...@@ -1581,35 +1516,31 @@ fn reloc_u64_addr(code: []u8, ra: RelocAddr) void {
1581}1516}
15821517
1583fn reloc_leb_addr(code: []u8, ra: RelocAddr) void {1518fn reloc_leb_addr(code: []u8, ra: RelocAddr) void {
1584 leb.writeUnsignedFixed(5, code[0..5], ra.addr);1519 std.leb.writeUnsignedFixed(5, code[0..5], ra.addr);
1585}1520}
15861521
1587fn reloc_leb64_addr(code: []u8, ra: RelocAddr) void {1522fn reloc_leb64_addr(code: []u8, ra: RelocAddr) void {
1588 leb.writeUnsignedFixed(11, code[0..11], ra.addr);1523 std.leb.writeUnsignedFixed(11, code[0..11], ra.addr);
1589}1524}
15901525
1591fn reloc_sleb_addr(code: []u8, ra: RelocAddr) void {1526fn reloc_sleb_addr(code: []u8, ra: RelocAddr) void {
1592 leb.writeSignedFixed(5, code[0..5], ra.addr);1527 std.leb.writeSignedFixed(5, code[0..5], ra.addr);
1593}1528}
15941529
1595fn reloc_sleb64_addr(code: []u8, ra: RelocAddr) void {1530fn reloc_sleb64_addr(code: []u8, ra: RelocAddr) void {
1596 leb.writeSignedFixed(11, code[0..11], ra.addr);1531 std.leb.writeSignedFixed(11, code[0..11], ra.addr);
1597}1532}
15981533
1599fn reloc_leb_table(code: []u8, table: Wasm.TableIndex) void {1534fn reloc_leb_table(code: []u8, table: Wasm.TableIndex) void {
1600 leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(table));1535 std.leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(table));
1601}1536}
16021537
1603fn reloc_leb_type(code: []u8, index: FuncTypeIndex) void {1538fn reloc_leb_type(code: []u8, index: FuncTypeIndex) void {
1604 leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(index));1539 std.leb.writeUnsignedFixed(5, code[0..5], @intFromEnum(index));
1605}1540}
16061541
1607fn emitCallCtorsFunction(wasm: *const Wasm, binary_bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!void {1542fn emitCallCtorsFunction(wasm: *const Wasm, w: *Writer) Writer.Error!void {
1608 const gpa = wasm.base.comp.gpa;1543 try w.writeUleb128(0); // no locals
1609
1610 try binary_bytes.ensureUnusedCapacity(gpa, 5 + 1);
1611 appendReservedUleb32(binary_bytes, 0); // no locals
1612
1613 for (wasm.object_init_funcs.items) |init_func| {1544 for (wasm.object_init_funcs.items) |init_func| {
1614 const func = init_func.function_index.ptr(wasm);1545 const func = init_func.function_index.ptr(wasm);
1615 if (!func.object_index.ptr(wasm).is_included) continue;1546 if (!func.object_index.ptr(wasm).is_included) continue;
...@@ -1617,25 +1548,18 @@ fn emitCallCtorsFunction(wasm: *const Wasm, binary_bytes: *std.ArrayListUnmanage...@@ -1617,25 +1548,18 @@ fn emitCallCtorsFunction(wasm: *const Wasm, binary_bytes: *std.ArrayListUnmanage
1617 const n_returns = ty.returns.slice(wasm).len;1548 const n_returns = ty.returns.slice(wasm).len;
16181549
1619 // Call function by its function index1550 // Call function by its function index
1620 try binary_bytes.ensureUnusedCapacity(gpa, 1 + 5 + n_returns + 1);
1621 const call_index: Wasm.OutputFunctionIndex = .fromObjectFunction(wasm, init_func.function_index);1551 const call_index: Wasm.OutputFunctionIndex = .fromObjectFunction(wasm, init_func.function_index);
1622 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call));1552 try w.writeByte(@intFromEnum(std.wasm.Opcode.call));
1623 appendReservedUleb32(binary_bytes, @intFromEnum(call_index));1553 try w.writeLeb128(@intFromEnum(call_index));
16241554
1625 // drop all returned values from the stack as __wasm_call_ctors has no return value1555 // drop all returned values from the stack as __wasm_call_ctors has no return value
1626 binary_bytes.appendNTimesAssumeCapacity(@intFromEnum(std.wasm.Opcode.drop), n_returns);1556 try w.splatByteAll(@intFromEnum(std.wasm.Opcode.drop), n_returns);
1627 }1557 }
16281558 try w.writeByte(@intFromEnum(std.wasm.Opcode.end)); // end function body
1629 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); // end function body
1630}1559}
16311560
1632fn emitInitMemoryFunction(1561fn emitInitMemoryFunction(wasm: *const Wasm, w: *Writer, virtual_addrs: *const VirtualAddrs) Writer.Error!void {
1633 wasm: *const Wasm,
1634 binary_bytes: *std.ArrayListUnmanaged(u8),
1635 virtual_addrs: *const VirtualAddrs,
1636) Allocator.Error!void {
1637 const comp = wasm.base.comp;1562 const comp = wasm.base.comp;
1638 const gpa = comp.gpa;
1639 const shared_memory = comp.config.shared_memory;1563 const shared_memory = comp.config.shared_memory;
16401564
1641 // Passive segments are used to avoid memory being reinitialized on each1565 // Passive segments are used to avoid memory being reinitialized on each
...@@ -1645,39 +1569,40 @@ fn emitInitMemoryFunction(...@@ -1645,39 +1569,40 @@ fn emitInitMemoryFunction(
1645 // function.1569 // function.
1646 assert(wasm.any_passive_inits);1570 assert(wasm.any_passive_inits);
16471571
1648 try binary_bytes.ensureUnusedCapacity(gpa, 5 + 1);1572 try w.writeUleb128(0); // no locals
1649 appendReservedUleb32(binary_bytes, 0); // no locals
16501573
1651 if (virtual_addrs.init_memory_flag) |flag_address| {1574 if (virtual_addrs.init_memory_flag) |flag_address| {
1652 assert(shared_memory);1575 assert(shared_memory);
1653 try binary_bytes.ensureUnusedCapacity(gpa, 2 * 3 + 6 * 3 + 1 + 6 * 3 + 1 + 5 * 4 + 1 + 1);
1654 // destination blocks1576 // destination blocks
1655 // based on values we jump to corresponding label1577 // based on values we jump to corresponding label
1656 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block)); // $drop1578 try w.writeAll(&.{
1657 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.BlockType.empty));1579 @intFromEnum(std.wasm.Opcode.block), // $drop
16581580 @intFromEnum(std.wasm.BlockType.empty),
1659 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block)); // $wait1581 @intFromEnum(std.wasm.Opcode.block), // $wait
1660 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.BlockType.empty));1582 @intFromEnum(std.wasm.BlockType.empty),
16611583 @intFromEnum(std.wasm.Opcode.block), // $init
1662 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block)); // $init1584 @intFromEnum(std.wasm.BlockType.empty),
1663 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.BlockType.empty));1585 });
16641586
1665 // atomically check1587 // atomically check
1666 appendReservedI32Const(binary_bytes, flag_address);1588 try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const));
1667 appendReservedI32Const(binary_bytes, 0);1589 try w.writeLeb128(@as(i32, @bitCast(flag_address)));
1668 appendReservedI32Const(binary_bytes, 1);1590 try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const));
1669 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix));1591 try w.writeSleb128(0);
1670 appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.AtomicsOpcode.i32_atomic_rmw_cmpxchg));1592 try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const));
1671 appendReservedUleb32(binary_bytes, 2); // alignment1593 try w.writeSleb128(1);
1672 appendReservedUleb32(binary_bytes, 0); // offset1594 try w.writeByte(@intFromEnum(std.wasm.Opcode.atomics_prefix));
1595 try w.writeLeb128(@intFromEnum(std.wasm.AtomicsOpcode.i32_atomic_rmw_cmpxchg));
1596 try w.writeLeb128(comptime Alignment.@"4".toLog2Units());
1597 try w.writeUleb128(0); // offset
16731598
1674 // based on the value from the atomic check, jump to the label.1599 // based on the value from the atomic check, jump to the label.
1675 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br_table));1600 try w.writeByte(@intFromEnum(std.wasm.Opcode.br_table));
1676 appendReservedUleb32(binary_bytes, 2); // length of the table (we have 3 blocks but because of the mandatory default the length is 2).1601 try w.writeUleb128(3 - 1); // length of the table (we have 3 blocks but because of the mandatory default the length is 2).
1677 appendReservedUleb32(binary_bytes, 0); // $init1602 try w.writeUleb128(0); // $init
1678 appendReservedUleb32(binary_bytes, 1); // $wait1603 try w.writeUleb128(1); // $wait
1679 appendReservedUleb32(binary_bytes, 2); // $drop1604 try w.writeUleb128(2); // $drop
1680 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end));1605 try w.writeByte(@intFromEnum(std.wasm.Opcode.end));
1681 }1606 }
16821607
1683 const segment_groups = wasm.flush_buffer.data_segment_groups.items;1608 const segment_groups = wasm.flush_buffer.data_segment_groups.items;
...@@ -1690,74 +1615,82 @@ fn emitInitMemoryFunction(...@@ -1690,74 +1615,82 @@ fn emitInitMemoryFunction(
1690 const start_addr: u32 = @intCast(segment.alignment(wasm).forward(prev_end));1615 const start_addr: u32 = @intCast(segment.alignment(wasm).forward(prev_end));
1691 const segment_size: u32 = group.end_addr - start_addr;1616 const segment_size: u32 = group.end_addr - start_addr;
16921617
1693 try binary_bytes.ensureUnusedCapacity(gpa, 6 + 6 + 1 + 5 + 6 + 6 + 1 + 6 * 2 + 1 + 1);
1694
1695 // For passive BSS segments we can simply issue a memory.fill(0). For1618 // For passive BSS segments we can simply issue a memory.fill(0). For
1696 // non-BSS segments we do a memory.init. Both instructions take as1619 // non-BSS segments we do a memory.init. Both instructions take as
1697 // their first argument the destination address.1620 // their first argument the destination address.
1698 appendReservedI32Const(binary_bytes, start_addr);1621 try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const));
1622 try w.writeLeb128(@as(i32, @bitCast(start_addr)));
16991623
1700 if (shared_memory and segment.isTls(wasm)) {1624 if (shared_memory and segment.isTls(wasm)) {
1701 // When we initialize the TLS segment we also set the `__tls_base`1625 // When we initialize the TLS segment we also set the `__tls_base`
1702 // global. This allows the runtime to use this static copy of the1626 // global. This allows the runtime to use this static copy of the
1703 // TLS data for the first/main thread.1627 // TLS data for the first/main thread.
1704 appendReservedI32Const(binary_bytes, start_addr);1628 try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const));
1705 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set));1629 try w.writeLeb128(@as(i32, @bitCast(start_addr)));
1706 appendReservedUleb32(binary_bytes, virtual_addrs.tls_base.?);1630 try w.writeByte(@intFromEnum(std.wasm.Opcode.global_set));
1631 try w.writeLeb128(virtual_addrs.tls_base.?);
1707 }1632 }
17081633
1709 appendReservedI32Const(binary_bytes, 0);1634 try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const));
1710 appendReservedI32Const(binary_bytes, segment_size);1635 try w.writeSleb128(0);
1711 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.misc_prefix));1636 try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const));
1637 try w.writeLeb128(@as(i32, @bitCast(segment_size)));
1638 try w.writeByte(@intFromEnum(std.wasm.Opcode.misc_prefix));
1712 if (segment.isBss(wasm)) {1639 if (segment.isBss(wasm)) {
1713 // fill bss segment with zeroes1640 // fill bss segment with zeroes
1714 appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.MiscOpcode.memory_fill));1641 try w.writeLeb128(@intFromEnum(std.wasm.MiscOpcode.memory_fill));
1715 } else {1642 } else {
1716 // initialize the segment1643 // initialize the segment
1717 appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.MiscOpcode.memory_init));1644 try w.writeLeb128(@intFromEnum(std.wasm.MiscOpcode.memory_init));
1718 appendReservedUleb32(binary_bytes, @intCast(segment_index));1645 try w.writeLeb128(segment_index);
1719 }1646 }
1720 binary_bytes.appendAssumeCapacity(0); // memory index immediate1647 try w.writeByte(0); // memory index immediate
1721 }1648 }
17221649
1723 if (virtual_addrs.init_memory_flag) |flag_address| {1650 if (virtual_addrs.init_memory_flag) |flag_address| {
1724 assert(shared_memory);1651 assert(shared_memory);
1725 try binary_bytes.ensureUnusedCapacity(gpa, 6 + 6 + 1 + 3 * 5 + 6 + 1 + 5 + 1 + 3 * 5 + 1 + 1 + 5 + 1 + 6 * 2 + 1 + 5 + 1 + 3 * 5 + 1 + 1 + 1);1652
1726 // we set the init memory flag to value '2'1653 // we set the init memory flag to value '2'
1727 appendReservedI32Const(binary_bytes, flag_address);1654 try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const));
1728 appendReservedI32Const(binary_bytes, 2);1655 try w.writeLeb128(@as(i32, @bitCast(flag_address)));
1729 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix));1656 try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const));
1730 appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.AtomicsOpcode.i32_atomic_store));1657 try w.writeSleb128(2);
1731 appendReservedUleb32(binary_bytes, @as(u32, 2)); // alignment1658 try w.writeByte(@intFromEnum(std.wasm.Opcode.atomics_prefix));
1732 appendReservedUleb32(binary_bytes, @as(u32, 0)); // offset1659 try w.writeLeb128(@intFromEnum(std.wasm.AtomicsOpcode.i32_atomic_store));
1660 try w.writeLeb128(comptime Alignment.@"4".toLog2Units());
1661 try w.writeUleb128(0); // offset
17331662
1734 // notify any waiters for segment initialization completion1663 // notify any waiters for segment initialization completion
1735 appendReservedI32Const(binary_bytes, flag_address);1664 try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const));
1736 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));1665 try w.writeLeb128(@as(i32, @bitCast(flag_address)));
1737 leb.writeIleb128(binary_bytes.fixedWriter(), @as(i32, -1)) catch unreachable; // number of waiters1666 try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const));
1738 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix));1667 try w.writeSleb128(-1); // number of waiters
1739 appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.AtomicsOpcode.memory_atomic_notify));1668
1740 appendReservedUleb32(binary_bytes, @as(u32, 2)); // alignment1669 try w.writeByte(@intFromEnum(std.wasm.Opcode.atomics_prefix));
1741 appendReservedUleb32(binary_bytes, @as(u32, 0)); // offset1670 try w.writeLeb128(@intFromEnum(std.wasm.AtomicsOpcode.memory_atomic_notify));
1742 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.drop));1671 try w.writeLeb128(comptime Alignment.@"4".toLog2Units());
1672 try w.writeUleb128(0); // offset
1673 try w.writeByte(@intFromEnum(std.wasm.Opcode.drop));
17431674
1744 // branch and drop segments1675 // branch and drop segments
1745 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br));1676 try w.writeByte(@intFromEnum(std.wasm.Opcode.br));
1746 appendReservedUleb32(binary_bytes, @as(u32, 1));1677 try w.writeUleb128(1);
17471678
1748 // wait for thread to initialize memory segments1679 // wait for thread to initialize memory segments
1749 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); // end $wait1680 try w.writeByte(@intFromEnum(std.wasm.Opcode.end)); // end $wait
1750 appendReservedI32Const(binary_bytes, flag_address);1681 try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const));
1751 appendReservedI32Const(binary_bytes, 1); // expected flag value1682 try w.writeLeb128(@as(i32, @bitCast(flag_address)));
1752 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_const));1683 try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const));
1753 leb.writeIleb128(binary_bytes.fixedWriter(), @as(i64, -1)) catch unreachable; // timeout1684 try w.writeSleb128(1); // expected flag value
1754 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.atomics_prefix));1685 try w.writeByte(@intFromEnum(std.wasm.Opcode.i64_const));
1755 appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.AtomicsOpcode.memory_atomic_wait32));1686 try w.writeSleb128(-1); // timeout
1756 appendReservedUleb32(binary_bytes, @as(u32, 2)); // alignment1687 try w.writeByte(@intFromEnum(std.wasm.Opcode.atomics_prefix));
1757 appendReservedUleb32(binary_bytes, @as(u32, 0)); // offset1688 try w.writeByte(@intFromEnum(std.wasm.AtomicsOpcode.memory_atomic_wait32));
1758 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.drop));1689 try w.writeLeb128(comptime Alignment.@"4".toLog2Units());
17591690 try w.writeUleb128(0); // offset
1760 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end)); // end $drop1691 try w.writeByte(@intFromEnum(std.wasm.Opcode.drop));
1692
1693 try w.writeByte(@intFromEnum(std.wasm.Opcode.end)); // end $drop
1761 }1694 }
17621695
1763 for (segment_groups, 0..) |group, segment_index| {1696 for (segment_groups, 0..) |group, segment_index| {
...@@ -1768,26 +1701,20 @@ fn emitInitMemoryFunction(...@@ -1768,26 +1701,20 @@ fn emitInitMemoryFunction(
1768 // during the initialization of each thread (__wasm_init_tls).1701 // during the initialization of each thread (__wasm_init_tls).
1769 if (shared_memory and segment.isTls(wasm)) continue;1702 if (shared_memory and segment.isTls(wasm)) continue;
17701703
1771 try binary_bytes.ensureUnusedCapacity(gpa, 1 + 5 + 5 + 1);1704 try w.writeByte(@intFromEnum(std.wasm.Opcode.misc_prefix));
17721705 try w.writeLeb128(@intFromEnum(std.wasm.MiscOpcode.data_drop));
1773 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.misc_prefix));1706 try w.writeLeb128(segment_index);
1774 appendReservedUleb32(binary_bytes, @intFromEnum(std.wasm.MiscOpcode.data_drop));
1775 appendReservedUleb32(binary_bytes, @intCast(segment_index));
1776 }1707 }
17771708
1778 // End of the function body1709 // End of the function body
1779 binary_bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end));1710 try w.writeByte(@intFromEnum(std.wasm.Opcode.end));
1780}1711}
17811712
1782fn emitInitTlsFunction(wasm: *const Wasm, bytes: *std.ArrayListUnmanaged(u8)) Allocator.Error!void {1713fn emitInitTlsFunction(wasm: *const Wasm, w: *Writer) Writer.Error!void {
1783 const comp = wasm.base.comp;1714 const comp = wasm.base.comp;
1784 const gpa = comp.gpa;
1785
1786 assert(comp.config.shared_memory);1715 assert(comp.config.shared_memory);
17871716
1788 try bytes.ensureUnusedCapacity(gpa, 5 * 10 + 8);1717 try w.writeUleb128(0); // no locals
1789
1790 appendReservedUleb32(bytes, 0); // no locals
17911718
1792 // If there's a TLS segment, initialize it during runtime using the bulk-memory feature1719 // If there's a TLS segment, initialize it during runtime using the bulk-memory feature
1793 // TLS segment is always the first one due to how we sort the data segments.1720 // TLS segment is always the first one due to how we sort the data segments.
...@@ -1796,36 +1723,35 @@ fn emitInitTlsFunction(wasm: *const Wasm, bytes: *std.ArrayListUnmanaged(u8)) Al...@@ -1796,36 +1723,35 @@ fn emitInitTlsFunction(wasm: *const Wasm, bytes: *std.ArrayListUnmanaged(u8)) Al
1796 const start_addr = wasm.flush_buffer.data_segments.values()[0];1723 const start_addr = wasm.flush_buffer.data_segments.values()[0];
1797 const end_addr = wasm.flush_buffer.data_segment_groups.items[0].end_addr;1724 const end_addr = wasm.flush_buffer.data_segment_groups.items[0].end_addr;
1798 const group_size = end_addr - start_addr;1725 const group_size = end_addr - start_addr;
1799 const data_segment_index = 0;1726 const data_segment_index: u32 = 0;
18001727
1801 const param_local: u32 = 0;1728 const param_local: u32 = 0;
18021729
1803 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get));1730 try w.writeByte(@intFromEnum(std.wasm.Opcode.local_get));
1804 appendReservedUleb32(bytes, param_local);1731 try w.writeLeb128(param_local);
18051732
1806 const tls_base_global_index: Wasm.GlobalIndex = @enumFromInt(wasm.globals.getIndex(.__tls_base).?);1733 const tls_base_global_index: Wasm.GlobalIndex = @enumFromInt(wasm.globals.getIndex(.__tls_base).?);
1807 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.global_set));1734 try w.writeByte(@intFromEnum(std.wasm.Opcode.global_set));
1808 appendReservedUleb32(bytes, @intFromEnum(tls_base_global_index));1735 try w.writeLeb128(@intFromEnum(tls_base_global_index));
18091736
1810 // load stack values for the bulk-memory operation1737 // load stack values for the bulk-memory operation
1811 {1738 {
1812 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get));1739 try w.writeByte(@intFromEnum(std.wasm.Opcode.local_get));
1813 appendReservedUleb32(bytes, param_local);1740 try w.writeLeb128(param_local);
18141741
1815 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));1742 try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const));
1816 appendReservedUleb32(bytes, 0); //segment offset1743 try w.writeSleb128(0); // segment offset
18171744
1818 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));1745 try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const));
1819 appendReservedUleb32(bytes, group_size); //segment offset1746 try w.writeLeb128(@as(i32, @bitCast(group_size))); // segment offset
1820 }1747 }
18211748
1822 // perform the bulk-memory operation to initialize the data segment1749 // perform the bulk-memory operation to initialize the data segment
1823 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.misc_prefix));1750 try w.writeByte(@intFromEnum(std.wasm.Opcode.misc_prefix));
1824 appendReservedUleb32(bytes, @intFromEnum(std.wasm.MiscOpcode.memory_init));1751 try w.writeLeb128(@intFromEnum(std.wasm.MiscOpcode.memory_init));
1825 // segment immediate1752 // segment immediate
1826 appendReservedUleb32(bytes, data_segment_index);1753 try w.writeLeb128(data_segment_index);
1827 // memory index immediate (always 0)1754 try w.writeByte(0); // memory index immediate
1828 appendReservedUleb32(bytes, 0);
1829 }1755 }
18301756
1831 // If we have to perform any TLS relocations, call the corresponding function1757 // If we have to perform any TLS relocations, call the corresponding function
...@@ -1833,56 +1759,59 @@ fn emitInitTlsFunction(wasm: *const Wasm, bytes: *std.ArrayListUnmanaged(u8)) Al...@@ -1833,56 +1759,59 @@ fn emitInitTlsFunction(wasm: *const Wasm, bytes: *std.ArrayListUnmanaged(u8)) Al
1833 // generated by the linker.1759 // generated by the linker.
1834 if (wasm.functions.getIndex(.__wasm_apply_global_tls_relocs)) |function_index| {1760 if (wasm.functions.getIndex(.__wasm_apply_global_tls_relocs)) |function_index| {
1835 const output_function_index: Wasm.OutputFunctionIndex = .fromFunctionIndex(wasm, @enumFromInt(function_index));1761 const output_function_index: Wasm.OutputFunctionIndex = .fromFunctionIndex(wasm, @enumFromInt(function_index));
1836 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.call));1762 try w.writeByte(@intFromEnum(std.wasm.Opcode.call));
1837 appendReservedUleb32(bytes, @intFromEnum(output_function_index));1763 try w.writeLeb128(@intFromEnum(output_function_index));
1838 }1764 }
18391765
1840 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end));1766 try w.writeByte(@intFromEnum(std.wasm.Opcode.end));
1841}1767}
18421768
1843fn emitStartSection(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8), i: Wasm.OutputFunctionIndex) !void {1769fn emitStartSection(aw: *Writer.Allocating, i: Wasm.OutputFunctionIndex) !void {
1844 const header_offset = try reserveVecSectionHeader(gpa, bytes);1770 const header_offset = try reserveVecSectionHeader(&aw.writer);
1845 replaceVecSectionHeader(bytes, header_offset, .start, @intFromEnum(i));1771 defer replaceVecSectionHeader(aw, header_offset, .start, @intFromEnum(i));
1846}1772}
18471773
1848fn emitTagNameFunction(1774fn emitTagNameFunction(
1849 wasm: *Wasm,1775 wasm: *Wasm,
1850 code: *std.ArrayListUnmanaged(u8),1776 w: *Writer,
1851 table_base_addr: u32,1777 table_base_addr: u32,
1852 table_index: u32,1778 table_index: u32,
1853 enum_type_ip: InternPool.Index,1779 enum_type_ip: InternPool.Index,
1854) !void {1780) !void {
1855 const comp = wasm.base.comp;1781 const comp = wasm.base.comp;
1856 const gpa = comp.gpa;
1857 const diags = &comp.link_diags;1782 const diags = &comp.link_diags;
1858 const zcu = comp.zcu.?;1783 const zcu = comp.zcu.?;
1859 const ip = &zcu.intern_pool;1784 const ip = &zcu.intern_pool;
1860 const enum_type = ip.loadEnumType(enum_type_ip);1785 const enum_type = ip.loadEnumType(enum_type_ip);
1861 const tag_values = enum_type.values.get(ip);1786 const tag_values = enum_type.values.get(ip);
18621787
1863 try code.ensureUnusedCapacity(gpa, 7 * 5 + 6 + 1 * 6);1788 try w.writeUleb128(0); // no locals
1864 appendReservedUleb32(code, 0); // no locals
18651789
1866 const slice_abi_size = 8;1790 const slice_abi_size: u32 = 8;
1867 const encoded_alignment = @ctz(@as(u32, 4));
1868 if (tag_values.len == 0) {1791 if (tag_values.len == 0) {
1869 // Then it's auto-numbered and therefore a direct table lookup.1792 // Then it's auto-numbered and therefore a direct table lookup.
1870 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get));1793 try w.writeByte(@intFromEnum(std.wasm.Opcode.local_get));
1871 appendReservedUleb32(code, 0);1794 try w.writeUleb128(0);
18721795
1873 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get));1796 try w.writeByte(@intFromEnum(std.wasm.Opcode.local_get));
1874 appendReservedUleb32(code, 1);1797 try w.writeUleb128(1);
18751798
1876 appendReservedI32Const(code, slice_abi_size);1799 try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const));
1877 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_mul));1800 if (std.math.isPowerOfTwo(slice_abi_size)) {
1801 try w.writeLeb128(@as(i32, @bitCast(@as(u32, std.math.log2_int(u32, slice_abi_size)))));
1802 try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_shl));
1803 } else {
1804 try w.writeLeb128(@as(i32, @bitCast(slice_abi_size)));
1805 try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_mul));
1806 }
18781807
1879 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_load));1808 try w.writeByte(@intFromEnum(std.wasm.Opcode.i64_load));
1880 appendReservedUleb32(code, encoded_alignment);1809 try w.writeLeb128(comptime Alignment.@"4".toLog2Units());
1881 appendReservedUleb32(code, table_base_addr + table_index * 8);1810 try w.writeLeb128(table_base_addr + slice_abi_size * table_index);
18821811
1883 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_store));1812 try w.writeByte(@intFromEnum(std.wasm.Opcode.i64_store));
1884 appendReservedUleb32(code, encoded_alignment);1813 try w.writeLeb128(comptime Alignment.@"4".toLog2Units());
1885 appendReservedUleb32(code, 0);1814 try w.writeUleb128(0);
1886 } else {1815 } else {
1887 const int_info = Zcu.Type.intInfo(.fromInterned(enum_type.tag_ty), zcu);1816 const int_info = Zcu.Type.intInfo(.fromInterned(enum_type.tag_ty), zcu);
1888 const outer_block_type: std.wasm.BlockType = switch (int_info.bits) {1817 const outer_block_type: std.wasm.BlockType = switch (int_info.bits) {
...@@ -1891,94 +1820,80 @@ fn emitTagNameFunction(...@@ -1891,94 +1820,80 @@ fn emitTagNameFunction(
1891 else => return diags.fail("wasm linker does not yet implement @tagName for sparse enums with more than 64 bit integer tag types", .{}),1820 else => return diags.fail("wasm linker does not yet implement @tagName for sparse enums with more than 64 bit integer tag types", .{}),
1892 };1821 };
18931822
1894 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get));1823 try w.writeByte(@intFromEnum(std.wasm.Opcode.local_get));
1895 appendReservedUleb32(code, 0);1824 try w.writeUleb128(0);
18961825
1897 // Outer block that computes table offset.1826 // Outer block that computes table offset.
1898 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block));1827 try w.writeByte(@intFromEnum(std.wasm.Opcode.block));
1899 code.appendAssumeCapacity(@intFromEnum(outer_block_type));1828 try w.writeByte(@intFromEnum(outer_block_type));
19001829
1901 for (tag_values, 0..) |tag_value, tag_index| {1830 for (tag_values, 0..) |tag_value, tag_index| {
1902 // block for this if case1831 // block for this if case
1903 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.block));1832 try w.writeByte(@intFromEnum(std.wasm.Opcode.block));
1904 code.appendAssumeCapacity(@intFromEnum(std.wasm.BlockType.empty));1833 try w.writeByte(@intFromEnum(std.wasm.BlockType.empty));
19051834
1906 // Tag value whose name should be returned.1835 // Tag value whose name should be returned.
1907 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.local_get));1836 try w.writeByte(@intFromEnum(std.wasm.Opcode.local_get));
1908 appendReservedUleb32(code, 1);1837 try w.writeUleb128(1);
19091838
1910 const val: Zcu.Value = .fromInterned(tag_value);1839 const val: Zcu.Value = .fromInterned(tag_value);
1911 switch (outer_block_type) {1840 switch (outer_block_type) {
1912 .i32 => {1841 .i32 => {
1913 const x: u32 = switch (int_info.signedness) {1842 try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const));
1914 .signed => @bitCast(@as(i32, @intCast(val.toSignedInt(zcu)))),1843 try w.writeLeb128(@as(i32, switch (int_info.signedness) {
1915 .unsigned => @intCast(val.toUnsignedInt(zcu)),1844 .signed => @intCast(val.toSignedInt(zcu)),
1916 };1845 .unsigned => @bitCast(@as(u32, @intCast(val.toUnsignedInt(zcu)))),
1917 appendReservedI32Const(code, x);1846 }));
1918 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_ne));1847 try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_ne));
1919 },1848 },
1920 .i64 => {1849 .i64 => {
1921 const x: u64 = switch (int_info.signedness) {1850 try w.writeByte(@intFromEnum(std.wasm.Opcode.i64_const));
1922 .signed => @bitCast(val.toSignedInt(zcu)),1851 try w.writeLeb128(@as(i64, switch (int_info.signedness) {
1923 .unsigned => val.toUnsignedInt(zcu),1852 .signed => val.toSignedInt(zcu),
1924 };1853 .unsigned => @bitCast(val.toUnsignedInt(zcu)),
1925 appendReservedI64Const(code, x);1854 }));
1926 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_ne));1855 try w.writeByte(@intFromEnum(std.wasm.Opcode.i64_ne));
1927 },1856 },
1928 else => unreachable,1857 else => unreachable,
1929 }1858 }
19301859
1931 // if they're not equal, break out of current branch1860 // if they're not equal, break out of current branch
1932 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br_if));1861 try w.writeByte(@intFromEnum(std.wasm.Opcode.br_if));
1933 appendReservedUleb32(code, 0);1862 try w.writeUleb128(0);
19341863
1935 // Put the table offset of the result on the stack.1864 // Put the table offset of the result on the stack.
1936 appendReservedI32Const(code, @intCast(tag_index * slice_abi_size));1865 try w.writeByte(@intFromEnum(std.wasm.Opcode.i32_const));
1866 try w.writeLeb128(@as(i32, @bitCast(@as(u32, @intCast(slice_abi_size * tag_index)))));
19371867
1938 // break outside blocks1868 // break outside blocks
1939 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.br));1869 try w.writeByte(@intFromEnum(std.wasm.Opcode.br));
1940 appendReservedUleb32(code, 1);1870 try w.writeUleb128(1);
19411871
1942 // end the block for this case1872 // end the block for this case
1943 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end));1873 try w.writeByte(@intFromEnum(std.wasm.Opcode.end));
1944 }1874 }
1945 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.@"unreachable"));1875 try w.writeByte(@intFromEnum(std.wasm.Opcode.@"unreachable"));
1946 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end));1876 try w.writeByte(@intFromEnum(std.wasm.Opcode.end));
19471877
1948 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_load));1878 try w.writeByte(@intFromEnum(std.wasm.Opcode.i64_load));
1949 appendReservedUleb32(code, encoded_alignment);1879 try w.writeLeb128(comptime Alignment.@"4".toLog2Units());
1950 appendReservedUleb32(code, table_base_addr + table_index * 8);1880 try w.writeLeb128(table_base_addr + slice_abi_size * table_index);
19511881
1952 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_store));1882 try w.writeByte(@intFromEnum(std.wasm.Opcode.i64_store));
1953 appendReservedUleb32(code, encoded_alignment);1883 try w.writeLeb128(comptime Alignment.@"4".toLog2Units());
1954 appendReservedUleb32(code, 0);1884 try w.writeUleb128(0);
1955 }1885 }
19561886
1957 // End of the function body1887 // End of the function body
1958 code.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end));1888 try w.writeByte(@intFromEnum(std.wasm.Opcode.end));
1959}
1960
1961/// Writes an unsigned 32-bit integer as a LEB128-encoded 'i32.const' value.
1962fn appendReservedI32Const(bytes: *std.ArrayListUnmanaged(u8), val: u32) void {
1963 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));
1964 leb.writeIleb128(bytes.fixedWriter(), @as(i32, @bitCast(val))) catch unreachable;
1965}
1966
1967/// Writes an unsigned 64-bit integer as a LEB128-encoded 'i64.const' value.
1968fn appendReservedI64Const(bytes: *std.ArrayListUnmanaged(u8), val: u64) void {
1969 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i64_const));
1970 leb.writeIleb128(bytes.fixedWriter(), @as(i64, @bitCast(val))) catch unreachable;
1971}
1972
1973fn appendReservedUleb32(bytes: *std.ArrayListUnmanaged(u8), val: u32) void {
1974 leb.writeUleb128(bytes.fixedWriter(), val) catch unreachable;
1975}1889}
19761890
1977fn appendGlobal(gpa: Allocator, bytes: *std.ArrayListUnmanaged(u8), mutable: u8, val: u32) Allocator.Error!void {1891fn appendGlobal(w: *Writer, mutable: bool, val: u32) Writer.Error!void {
1978 try bytes.ensureUnusedCapacity(gpa, 9);1892 try w.writeAll(&.{
1979 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Valtype.i32));1893 @intFromEnum(std.wasm.Valtype.i32),
1980 bytes.appendAssumeCapacity(mutable);1894 @intFromBool(mutable),
1981 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.i32_const));1895 @intFromEnum(std.wasm.Opcode.i32_const),
1982 appendReservedUleb32(bytes, val);1896 });
1983 bytes.appendAssumeCapacity(@intFromEnum(std.wasm.Opcode.end));1897 try w.writeLeb128(val);
1898 try w.writeByte(@intFromEnum(std.wasm.Opcode.end));
1984}1899}
src/link/Wasm/Object.zig+264-317
...@@ -8,6 +8,7 @@ const Allocator = std.mem.Allocator;...@@ -8,6 +8,7 @@ const Allocator = std.mem.Allocator;
8const Path = std.Build.Cache.Path;8const Path = std.Build.Cache.Path;
9const log = std.log.scoped(.object);9const log = std.log.scoped(.object);
10const assert = std.debug.assert;10const assert = std.debug.assert;
11const Reader = std.Io.Reader;
1112
12/// Wasm spec version used for this `Object`13/// Wasm spec version used for this `Object`
13version: u32,14version: u32,
...@@ -252,25 +253,21 @@ pub const ScratchSpace = struct {...@@ -252,25 +253,21 @@ pub const ScratchSpace = struct {
252253
253pub fn parse(254pub fn parse(
254 wasm: *Wasm,255 wasm: *Wasm,
255 bytes: []const u8,256 br: *Reader,
256 path: Path,257 path: Path,
257 archive_member_name: ?[]const u8,258 archive_member_name: ?[]const u8,
258 host_name: Wasm.OptionalString,259 host_name: Wasm.OptionalString,
259 ss: *ScratchSpace,260 ss: *ScratchSpace,
260 must_link: bool,261 must_link: bool,
261 gc_sections: bool,262 gc_sections: bool,
262) anyerror!Object {263) !Object {
263 const comp = wasm.base.comp;264 const comp = wasm.base.comp;
264 const gpa = comp.gpa;265 const gpa = comp.gpa;
265 const diags = &comp.link_diags;266 const diags = &comp.link_diags;
266267
267 var pos: usize = 0;268 if (!std.mem.eql(u8, try br.takeArray(std.wasm.magic.len), &std.wasm.magic)) return error.BadObjectMagic;
268269
269 if (!std.mem.eql(u8, bytes[0..std.wasm.magic.len], &std.wasm.magic)) return error.BadObjectMagic;270 const version = try br.takeInt(u32, .little);
270 pos += std.wasm.magic.len;
271
272 const version = std.mem.readInt(u32, bytes[pos..][0..4], .little);
273 pos += 4;
274271
275 const data_segment_start: u32 = @intCast(wasm.object_data_segments.items.len);272 const data_segment_start: u32 = @intCast(wasm.object_data_segments.items.len);
276 const custom_segment_start: u32 = @intCast(wasm.object_custom_segments.entries.len);273 const custom_segment_start: u32 = @intCast(wasm.object_custom_segments.entries.len);
...@@ -298,200 +295,187 @@ pub fn parse(...@@ -298,200 +295,187 @@ pub fn parse(
298 var code_section_index: ?Wasm.ObjectSectionIndex = null;295 var code_section_index: ?Wasm.ObjectSectionIndex = null;
299 var global_section_index: ?Wasm.ObjectSectionIndex = null;296 var global_section_index: ?Wasm.ObjectSectionIndex = null;
300 var data_section_index: ?Wasm.ObjectSectionIndex = null;297 var data_section_index: ?Wasm.ObjectSectionIndex = null;
301 while (pos < bytes.len) : (wasm.object_total_sections += 1) {298 while (br.takeEnum(std.wasm.Section, .little)) |section_tag| : (wasm.object_total_sections += 1) {
302 const section_index: Wasm.ObjectSectionIndex = @enumFromInt(wasm.object_total_sections);299 const section_index: Wasm.ObjectSectionIndex = @enumFromInt(wasm.object_total_sections);
303300
304 const section_tag: std.wasm.Section = @enumFromInt(bytes[pos]);301 const len = try br.takeLeb128(u32);
305 pos += 1;302 const section_end = br.seek + len;
306
307 const len, pos = readLeb(u32, bytes, pos);
308 const section_end = pos + len;
309 switch (section_tag) {303 switch (section_tag) {
310 .custom => {304 .custom => {
311 const section_name, pos = readBytes(bytes, pos);305 const section_name = try br.take(try br.takeLeb128(u32));
312 if (std.mem.eql(u8, section_name, "linking")) {306 if (std.mem.eql(u8, section_name, "linking")) {
313 saw_linking_section = true;307 saw_linking_section = true;
314 const section_version, pos = readLeb(u32, bytes, pos);308 const section_version = try br.takeLeb128(u32);
315 log.debug("link meta data version: {d}", .{section_version});309 log.debug("link meta data version: {d}", .{section_version});
316 if (section_version != 2) return error.UnsupportedVersion;310 if (section_version != 2) return error.UnsupportedVersion;
317 while (pos < section_end) {311 while (br.seek < section_end) {
318 const sub_type, pos = readLeb(u8, bytes, pos);312 const sub_type = try br.takeEnum(SubsectionType, .little);
319 log.debug("found subsection: {s}", .{@tagName(@as(SubsectionType, @enumFromInt(sub_type)))});313 log.debug("found subsection: {s}", .{@tagName(sub_type)});
320 const payload_len, pos = readLeb(u32, bytes, pos);314 const payload_len = try br.takeLeb128(u32);
321 if (payload_len == 0) break;315 if (payload_len == 0) break;
322316
323 const count, pos = readLeb(u32, bytes, pos);317 const count = try br.takeLeb128(u32);
324318 switch (sub_type) {
325 switch (@as(SubsectionType, @enumFromInt(sub_type))) {319 .segment_info => for (try ss.segment_info.addManyAsSlice(gpa, count)) |*segment| {
326 .segment_info => {320 const name = try br.take(try br.takeLeb128(u32));
327 for (try ss.segment_info.addManyAsSlice(gpa, count)) |*segment| {321 const alignment: Alignment = .fromLog2Units(try br.takeLeb128(u32));
328 const name, pos = readBytes(bytes, pos);322 const flags: SegmentInfo.Flags = @bitCast(try br.takeLeb128(u32));
329 const alignment, pos = readLeb(u32, bytes, pos);323 const tls = flags.tls or
330 const flags_u32, pos = readLeb(u32, bytes, pos);324 // Supports legacy object files that specified
331 const flags: SegmentInfo.Flags = @bitCast(flags_u32);325 // being TLS by the name instead of the TLS flag.
332 const tls = flags.tls or326 std.mem.startsWith(u8, name, ".tdata") or
333 // Supports legacy object files that specified327 std.mem.startsWith(u8, name, ".tbss");
334 // being TLS by the name instead of the TLS flag.328 has_tls = has_tls or tls;
335 std.mem.startsWith(u8, name, ".tdata") or329 segment.* = .{
336 std.mem.startsWith(u8, name, ".tbss");330 .name = try wasm.internString(name),
337 has_tls = has_tls or tls;331 .flags = .{
338 segment.* = .{332 .strings = flags.strings,
339 .name = try wasm.internString(name),333 .tls = tls,
340 .flags = .{334 .alignment = alignment,
341 .strings = flags.strings,335 .retain = flags.retain,
342 .tls = tls,336 },
343 .alignment = @enumFromInt(alignment),337 };
344 .retain = flags.retain,
345 },
346 };
347 }
348 },338 },
349 .init_funcs => {339 .init_funcs => for (try wasm.object_init_funcs.addManyAsSlice(gpa, count)) |*func| {
350 for (try wasm.object_init_funcs.addManyAsSlice(gpa, count)) |*func| {340 const priority = try br.takeLeb128(u32);
351 const priority, pos = readLeb(u32, bytes, pos);341 const symbol_index = try br.takeLeb128(u32);
352 const symbol_index, pos = readLeb(u32, bytes, pos);342 if (symbol_index > ss.symbol_table.items.len)
353 if (symbol_index > ss.symbol_table.items.len)343 return diags.failParse(path, "init_funcs before symbol table", .{});
354 return diags.failParse(path, "init_funcs before symbol table", .{});344 const sym = &ss.symbol_table.items[symbol_index];
355 const sym = &ss.symbol_table.items[symbol_index];345 if (sym.pointee != .function) {
356 if (sym.pointee != .function) {346 return diags.failParse(path, "init_func symbol '{s}' not a function", .{
357 return diags.failParse(path, "init_func symbol '{s}' not a function", .{347 sym.name.slice(wasm).?,
358 sym.name.slice(wasm).?,348 });
359 });349 } else if (sym.flags.undefined) {
360 } else if (sym.flags.undefined) {350 return diags.failParse(path, "init_func symbol '{s}' is an import", .{
361 return diags.failParse(path, "init_func symbol '{s}' is an import", .{351 sym.name.slice(wasm).?,
362 sym.name.slice(wasm).?,352 });
363 });
364 }
365 func.* = .{
366 .priority = priority,
367 .function_index = sym.pointee.function,
368 };
369 }353 }
354 func.* = .{
355 .priority = priority,
356 .function_index = sym.pointee.function,
357 };
370 },358 },
371 .comdat_info => {359 .comdat_info => for (try wasm.object_comdats.addManyAsSlice(gpa, count)) |*comdat| {
372 for (try wasm.object_comdats.addManyAsSlice(gpa, count)) |*comdat| {360 const name = try br.take(try br.takeLeb128(u32));
373 const name, pos = readBytes(bytes, pos);361 const flags = try br.takeLeb128(u32);
374 const flags, pos = readLeb(u32, bytes, pos);362 if (flags != 0) return error.UnexpectedComdatFlags;
375 if (flags != 0) return error.UnexpectedComdatFlags;363 const symbol_count = try br.takeLeb128(u32);
376 const symbol_count, pos = readLeb(u32, bytes, pos);364 const start_off: u32 = @intCast(wasm.object_comdat_symbols.len);
377 const start_off: u32 = @intCast(wasm.object_comdat_symbols.len);365 try wasm.object_comdat_symbols.ensureUnusedCapacity(gpa, symbol_count);
378 try wasm.object_comdat_symbols.ensureUnusedCapacity(gpa, symbol_count);366 for (0..symbol_count) |_| {
379 for (0..symbol_count) |_| {367 const kind = try br.takeEnum(Wasm.Comdat.Symbol.Type, .little);
380 const kind, pos = readEnum(Wasm.Comdat.Symbol.Type, bytes, pos);368 const index = try br.takeLeb128(u32);
381 const index, pos = readLeb(u32, bytes, pos);369 if (true) @panic("TODO rebase index depending on kind");
382 if (true) @panic("TODO rebase index depending on kind");370 wasm.object_comdat_symbols.appendAssumeCapacity(.{
383 wasm.object_comdat_symbols.appendAssumeCapacity(.{371 .kind = kind,
384 .kind = kind,372 .index = index,
385 .index = index,373 });
386 });
387 }
388 comdat.* = .{
389 .name = try wasm.internString(name),
390 .flags = flags,
391 .symbols = .{
392 .off = start_off,
393 .len = @intCast(wasm.object_comdat_symbols.len - start_off),
394 },
395 };
396 }374 }
375 comdat.* = .{
376 .name = try wasm.internString(name),
377 .flags = flags,
378 .symbols = .{
379 .off = start_off,
380 .len = @intCast(wasm.object_comdat_symbols.len - start_off),
381 },
382 };
397 },383 },
398 .symbol_table => {384 .symbol_table => for (try ss.symbol_table.addManyAsSlice(gpa, count)) |*symbol| {
399 for (try ss.symbol_table.addManyAsSlice(gpa, count)) |*symbol| {385 const tag = try br.takeEnum(Symbol.Tag, .little);
400 const tag, pos = readEnum(Symbol.Tag, bytes, pos);386 const flags: Wasm.SymbolFlags = @bitCast(try br.takeLeb128(u32));
401 const flags, pos = readLeb(u32, bytes, pos);387 symbol.* = .{
402 symbol.* = .{388 .flags = flags,
403 .flags = @bitCast(flags),389 .name = .none,
404 .name = .none,390 .pointee = undefined,
405 .pointee = undefined,391 };
406 };392 symbol.flags.initZigSpecific(must_link, gc_sections);
407 symbol.flags.initZigSpecific(must_link, gc_sections);393
408394 switch (tag) {
409 switch (tag) {395 .data => {
410 .data => {396 const name = try br.take(try br.takeLeb128(u32));
411 const name, pos = readBytes(bytes, pos);397 const interned_name = try wasm.internString(name);
412 const interned_name = try wasm.internString(name);398 symbol.name = interned_name.toOptional();
413 symbol.name = interned_name.toOptional();399 if (symbol.flags.undefined) {
414 if (symbol.flags.undefined) {400 symbol.pointee = .data_import;
415 symbol.pointee = .data_import;401 } else {
402 const segment_index = try br.takeLeb128(u32);
403 const segment_offset = try br.takeLeb128(u32);
404 const size = try br.takeLeb128(u32);
405 try wasm.object_datas.append(gpa, .{
406 .segment = @enumFromInt(data_segment_start + segment_index),
407 .offset = segment_offset,
408 .size = size,
409 .name = interned_name,
410 .flags = symbol.flags,
411 });
412 symbol.pointee = .{
413 .data = @enumFromInt(wasm.object_datas.items.len - 1),
414 };
415 }
416 },
417 .section => {
418 const local_section = try br.takeLeb128(u32);
419 const section: Wasm.ObjectSectionIndex = @enumFromInt(local_section_index_base + local_section);
420 symbol.pointee = .{ .section = section };
421 },
422
423 .function => {
424 const local_index = try br.takeLeb128(u32);
425 if (symbol.flags.undefined) {
426 const function_import: ScratchSpace.FuncImportIndex = @enumFromInt(local_index);
427 symbol.pointee = .{ .function_import = function_import };
428 if (symbol.flags.explicit_name) {
429 const name = try br.take(try br.takeLeb128(u32));
430 symbol.name = (try wasm.internString(name)).toOptional();
416 } else {431 } else {
417 const segment_index, pos = readLeb(u32, bytes, pos);432 symbol.name = function_import.ptr(ss).name.toOptional();
418 const segment_offset, pos = readLeb(u32, bytes, pos);
419 const size, pos = readLeb(u32, bytes, pos);
420 try wasm.object_datas.append(gpa, .{
421 .segment = @enumFromInt(data_segment_start + segment_index),
422 .offset = segment_offset,
423 .size = size,
424 .name = interned_name,
425 .flags = symbol.flags,
426 });
427 symbol.pointee = .{
428 .data = @enumFromInt(wasm.object_datas.items.len - 1),
429 };
430 }433 }
431 },434 } else {
432 .section => {435 symbol.pointee = .{ .function = @enumFromInt(functions_start + (local_index - ss.func_imports.items.len)) };
433 const local_section, pos = readLeb(u32, bytes, pos);436 const name = try br.take(try br.takeLeb128(u32));
434 const section: Wasm.ObjectSectionIndex = @enumFromInt(local_section_index_base + local_section);437 symbol.name = (try wasm.internString(name)).toOptional();
435 symbol.pointee = .{ .section = section };438 }
436 },439 },
437440 .global => {
438 .function => {441 const local_index = try br.takeLeb128(u32);
439 const local_index, pos = readLeb(u32, bytes, pos);442 if (symbol.flags.undefined) {
440 if (symbol.flags.undefined) {443 const global_import: ScratchSpace.GlobalImportIndex = @enumFromInt(local_index);
441 const function_import: ScratchSpace.FuncImportIndex = @enumFromInt(local_index);444 symbol.pointee = .{ .global_import = global_import };
442 symbol.pointee = .{ .function_import = function_import };445 if (symbol.flags.explicit_name) {
443 if (symbol.flags.explicit_name) {446 const name = try br.take(try br.takeLeb128(u32));
444 const name, pos = readBytes(bytes, pos);
445 symbol.name = (try wasm.internString(name)).toOptional();
446 } else {
447 symbol.name = function_import.ptr(ss).name.toOptional();
448 }
449 } else {
450 symbol.pointee = .{ .function = @enumFromInt(functions_start + (local_index - ss.func_imports.items.len)) };
451 const name, pos = readBytes(bytes, pos);
452 symbol.name = (try wasm.internString(name)).toOptional();447 symbol.name = (try wasm.internString(name)).toOptional();
453 }
454 },
455 .global => {
456 const local_index, pos = readLeb(u32, bytes, pos);
457 if (symbol.flags.undefined) {
458 const global_import: ScratchSpace.GlobalImportIndex = @enumFromInt(local_index);
459 symbol.pointee = .{ .global_import = global_import };
460 if (symbol.flags.explicit_name) {
461 const name, pos = readBytes(bytes, pos);
462 symbol.name = (try wasm.internString(name)).toOptional();
463 } else {
464 symbol.name = global_import.ptr(ss).name.toOptional();
465 }
466 } else {448 } else {
467 symbol.pointee = .{ .global = @enumFromInt(globals_start + (local_index - ss.global_imports.items.len)) };449 symbol.name = global_import.ptr(ss).name.toOptional();
468 const name, pos = readBytes(bytes, pos);
469 symbol.name = (try wasm.internString(name)).toOptional();
470 }450 }
471 },451 } else {
472 .table => {452 symbol.pointee = .{ .global = @enumFromInt(globals_start + (local_index - ss.global_imports.items.len)) };
473 const local_index, pos = readLeb(u32, bytes, pos);453 const name = try br.take(try br.takeLeb128(u32));
474 if (symbol.flags.undefined) {454 symbol.name = (try wasm.internString(name)).toOptional();
475 table_import_symbol_count += 1;455 }
476 const table_import: ScratchSpace.TableImportIndex = @enumFromInt(local_index);456 },
477 symbol.pointee = .{ .table_import = table_import };457 .table => {
478 if (symbol.flags.explicit_name) {458 const local_index = try br.takeLeb128(u32);
479 const name, pos = readBytes(bytes, pos);459 if (symbol.flags.undefined) {
480 symbol.name = (try wasm.internString(name)).toOptional();460 table_import_symbol_count += 1;
481 } else {461 const table_import: ScratchSpace.TableImportIndex = @enumFromInt(local_index);
482 symbol.name = table_import.ptr(ss).name.toOptional();462 symbol.pointee = .{ .table_import = table_import };
483 }463 if (symbol.flags.explicit_name) {
484 } else {464 const name = try br.take(try br.takeLeb128(u32));
485 symbol.pointee = .{ .table = @enumFromInt(tables_start + (local_index - ss.table_imports.items.len)) };
486 const name, pos = readBytes(bytes, pos);
487 symbol.name = (try wasm.internString(name)).toOptional();465 symbol.name = (try wasm.internString(name)).toOptional();
466 } else {
467 symbol.name = table_import.ptr(ss).name.toOptional();
488 }468 }
489 },469 } else {
490 else => {470 symbol.pointee = .{ .table = @enumFromInt(tables_start + (local_index - ss.table_imports.items.len)) };
491 log.debug("unrecognized symbol type tag: {x}", .{@intFromEnum(tag)});471 const name = try br.take(try br.takeLeb128(u32));
492 return error.UnrecognizedSymbolType;472 symbol.name = (try wasm.internString(name)).toOptional();
493 },473 }
494 }474 },
475 else => {
476 log.debug("unrecognized symbol type tag: {x}", .{@intFromEnum(tag)});
477 return error.UnrecognizedSymbolType;
478 },
495 }479 }
496 },480 },
497 }481 }
...@@ -504,8 +488,8 @@ pub fn parse(...@@ -504,8 +488,8 @@ pub fn parse(
504 // which section they apply to, and must be sequenced in488 // which section they apply to, and must be sequenced in
505 // the module after that section."489 // the module after that section."
506 // "Relocation sections can only target code, data and custom sections."490 // "Relocation sections can only target code, data and custom sections."
507 const local_section, pos = readLeb(u32, bytes, pos);491 const local_section = try br.takeLeb128(u32);
508 const count, pos = readLeb(u32, bytes, pos);492 const count = try br.takeLeb128(u32);
509 const section: Wasm.ObjectSectionIndex = @enumFromInt(local_section_index_base + local_section);493 const section: Wasm.ObjectSectionIndex = @enumFromInt(local_section_index_base + local_section);
510494
511 log.debug("found {d} relocations for section={d}", .{ count, section });495 log.debug("found {d} relocations for section={d}", .{ count, section });
...@@ -513,10 +497,9 @@ pub fn parse(...@@ -513,10 +497,9 @@ pub fn parse(
513 var prev_offset: u32 = 0;497 var prev_offset: u32 = 0;
514 try wasm.object_relocations.ensureUnusedCapacity(gpa, count);498 try wasm.object_relocations.ensureUnusedCapacity(gpa, count);
515 for (0..count) |_| {499 for (0..count) |_| {
516 const tag: RelocationType = @enumFromInt(bytes[pos]);500 const tag = try br.takeEnum(RelocationType, .little);
517 pos += 1;501 const offset = try br.takeLeb128(u32);
518 const offset, pos = readLeb(u32, bytes, pos);502 const index = try br.takeLeb128(u32);
519 const index, pos = readLeb(u32, bytes, pos);
520503
521 if (offset < prev_offset)504 if (offset < prev_offset)
522 return diags.failParse(path, "relocation entries not sorted by offset", .{});505 return diags.failParse(path, "relocation entries not sorted by offset", .{});
...@@ -537,7 +520,7 @@ pub fn parse(...@@ -537,7 +520,7 @@ pub fn parse(
537 .memory_addr_locrel_i32,520 .memory_addr_locrel_i32,
538 .memory_addr_tls_sleb64,521 .memory_addr_tls_sleb64,
539 => {522 => {
540 const addend: i32, pos = readLeb(i32, bytes, pos);523 const addend = try br.takeLeb128(i32);
541 wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) {524 wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) {
542 .data => |data| .{525 .data => |data| .{
543 .tag = .fromType(tag),526 .tag = .fromType(tag),
...@@ -555,7 +538,7 @@ pub fn parse(...@@ -555,7 +538,7 @@ pub fn parse(
555 });538 });
556 },539 },
557 .function_offset_i32, .function_offset_i64 => {540 .function_offset_i32, .function_offset_i64 => {
558 const addend: i32, pos = readLeb(i32, bytes, pos);541 const addend = try br.takeLeb128(i32);
559 wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) {542 wasm.object_relocations.appendAssumeCapacity(switch (sym.pointee) {
560 .function => .{543 .function => .{
561 .tag = .fromType(tag),544 .tag = .fromType(tag),
...@@ -573,7 +556,7 @@ pub fn parse(...@@ -573,7 +556,7 @@ pub fn parse(
573 });556 });
574 },557 },
575 .section_offset_i32 => {558 .section_offset_i32 => {
576 const addend: i32, pos = readLeb(i32, bytes, pos);559 const addend = try br.takeLeb128(i32);
577 wasm.object_relocations.appendAssumeCapacity(.{560 wasm.object_relocations.appendAssumeCapacity(.{
578 .tag = .section_offset_i32,561 .tag = .section_offset_i32,
579 .offset = offset,562 .offset = offset,
...@@ -658,10 +641,9 @@ pub fn parse(...@@ -658,10 +641,9 @@ pub fn parse(
658 .len = count,641 .len = count,
659 });642 });
660 } else if (std.mem.eql(u8, section_name, "target_features")) {643 } else if (std.mem.eql(u8, section_name, "target_features")) {
661 opt_features, pos = try parseFeatures(wasm, bytes, pos, path);644 opt_features = try parseFeatures(wasm, br, path);
662 } else if (std.mem.startsWith(u8, section_name, ".debug")) {645 } else if (std.mem.startsWith(u8, section_name, ".debug")) {
663 const debug_content = bytes[pos..section_end];646 const debug_content = try br.take(len);
664 pos = section_end;
665647
666 const data_off: u32 = @intCast(wasm.string_bytes.items.len);648 const data_off: u32 = @intCast(wasm.string_bytes.items.len);
667 try wasm.string_bytes.appendSlice(gpa, debug_content);649 try wasm.string_bytes.appendSlice(gpa, debug_content);
...@@ -669,23 +651,20 @@ pub fn parse(...@@ -669,23 +651,20 @@ pub fn parse(
669 try wasm.object_custom_segments.put(gpa, section_index, .{651 try wasm.object_custom_segments.put(gpa, section_index, .{
670 .payload = .{652 .payload = .{
671 .off = @enumFromInt(data_off),653 .off = @enumFromInt(data_off),
672 .len = @intCast(debug_content.len),654 .len = @intCast(len),
673 },655 },
674 .flags = .{},656 .flags = .{},
675 .section_name = try wasm.internString(section_name),657 .section_name = try wasm.internString(section_name),
676 });658 });
677 } else {659 } else br.seek = section_end;
678 pos = section_end;
679 }
680 },660 },
681 .type => {661 .type => {
682 const func_types_len, pos = readLeb(u32, bytes, pos);662 const func_types_len = try br.takeLeb128(u32);
683 for (try ss.func_types.addManyAsSlice(gpa, func_types_len)) |*func_type| {663 for (try ss.func_types.addManyAsSlice(gpa, func_types_len)) |*func_type| {
684 if (bytes[pos] != std.wasm.function_type) return error.ExpectedFuncType;664 if (try br.takeByte() != std.wasm.function_type) return error.ExpectedFuncType;
685 pos += 1;
686665
687 const params, pos = readBytes(bytes, pos);666 const params = try br.take(try br.takeLeb128(u32));
688 const returns, pos = readBytes(bytes, pos);667 const returns = try br.take(try br.takeLeb128(u32));
689 func_type.* = try wasm.addFuncType(.{668 func_type.* = try wasm.addFuncType(.{
690 .params = .fromString(try wasm.internString(params)),669 .params = .fromString(try wasm.internString(params)),
691 .returns = .fromString(try wasm.internString(returns)),670 .returns = .fromString(try wasm.internString(returns)),
...@@ -693,16 +672,16 @@ pub fn parse(...@@ -693,16 +672,16 @@ pub fn parse(
693 }672 }
694 },673 },
695 .import => {674 .import => {
696 const imports_len, pos = readLeb(u32, bytes, pos);675 const imports_len = try br.takeLeb128(u32);
697 for (0..imports_len) |_| {676 for (0..imports_len) |_| {
698 const module_name, pos = readBytes(bytes, pos);677 const module_name = try br.take(try br.takeLeb128(u32));
699 const name, pos = readBytes(bytes, pos);678 const name = try br.take(try br.takeLeb128(u32));
700 const kind, pos = readEnum(std.wasm.ExternalKind, bytes, pos);679 const kind = try br.takeEnum(std.wasm.ExternalKind, .little);
701 const interned_module_name = try wasm.internString(module_name);680 const interned_module_name = try wasm.internString(module_name);
702 const interned_name = try wasm.internString(name);681 const interned_name = try wasm.internString(name);
703 switch (kind) {682 switch (kind) {
704 .function => {683 .function => {
705 const function, pos = readLeb(u32, bytes, pos);684 const function = try br.takeLeb128(u32);
706 try ss.func_imports.append(gpa, .{685 try ss.func_imports.append(gpa, .{
707 .module_name = interned_module_name,686 .module_name = interned_module_name,
708 .name = interned_name,687 .name = interned_name,
...@@ -710,7 +689,7 @@ pub fn parse(...@@ -710,7 +689,7 @@ pub fn parse(
710 });689 });
711 },690 },
712 .memory => {691 .memory => {
713 const limits, pos = readLimits(bytes, pos);692 const limits = try readLimits(br);
714 const gop = try wasm.object_memory_imports.getOrPut(gpa, interned_name);693 const gop = try wasm.object_memory_imports.getOrPut(gpa, interned_name);
715 if (gop.found_existing) {694 if (gop.found_existing) {
716 if (gop.value_ptr.module_name != interned_module_name) {695 if (gop.value_ptr.module_name != interned_module_name) {
...@@ -736,9 +715,12 @@ pub fn parse(...@@ -736,9 +715,12 @@ pub fn parse(
736 }715 }
737 },716 },
738 .global => {717 .global => {
739 const valtype, pos = readEnum(std.wasm.Valtype, bytes, pos);718 const valtype = try br.takeEnum(std.wasm.Valtype, .little);
740 const mutable = bytes[pos] == 0x01;719 const mutable = switch (try br.takeByte()) {
741 pos += 1;720 0 => false,
721 1 => true,
722 else => return error.InvalidMutability,
723 };
742 try ss.global_imports.append(gpa, .{724 try ss.global_imports.append(gpa, .{
743 .name = interned_name,725 .name = interned_name,
744 .valtype = valtype,726 .valtype = valtype,
...@@ -747,8 +729,8 @@ pub fn parse(...@@ -747,8 +729,8 @@ pub fn parse(
747 });729 });
748 },730 },
749 .table => {731 .table => {
750 const ref_type, pos = readEnum(std.wasm.RefType, bytes, pos);732 const ref_type = try br.takeEnum(std.wasm.RefType, .little);
751 const limits, pos = readLimits(bytes, pos);733 const limits = try readLimits(br);
752 try ss.table_imports.append(gpa, .{734 try ss.table_imports.append(gpa, .{
753 .name = interned_name,735 .name = interned_name,
754 .module_name = interned_module_name,736 .module_name = interned_module_name,
...@@ -763,17 +745,16 @@ pub fn parse(...@@ -763,17 +745,16 @@ pub fn parse(
763 }745 }
764 },746 },
765 .function => {747 .function => {
766 const functions_len, pos = readLeb(u32, bytes, pos);748 const functions_len = try br.takeLeb128(u32);
767 for (try ss.func_type_indexes.addManyAsSlice(gpa, functions_len)) |*func_type_index| {749 for (try ss.func_type_indexes.addManyAsSlice(gpa, functions_len)) |*func_type_index| {
768 const i, pos = readLeb(u32, bytes, pos);750 func_type_index.* = @enumFromInt(try br.takeLeb128(u32));
769 func_type_index.* = @enumFromInt(i);
770 }751 }
771 },752 },
772 .table => {753 .table => {
773 const tables_len, pos = readLeb(u32, bytes, pos);754 const tables_len = try br.takeLeb128(u32);
774 for (try wasm.object_tables.addManyAsSlice(gpa, tables_len)) |*table| {755 for (try wasm.object_tables.addManyAsSlice(gpa, tables_len)) |*table| {
775 const ref_type, pos = readEnum(std.wasm.RefType, bytes, pos);756 const ref_type = try br.takeEnum(std.wasm.RefType, .little);
776 const limits, pos = readLimits(bytes, pos);757 const limits = try readLimits(br);
777 table.* = .{758 table.* = .{
778 .name = .none,759 .name = .none,
779 .module_name = .none,760 .module_name = .none,
...@@ -788,9 +769,9 @@ pub fn parse(...@@ -788,9 +769,9 @@ pub fn parse(
788 }769 }
789 },770 },
790 .memory => {771 .memory => {
791 const memories_len, pos = readLeb(u32, bytes, pos);772 const memories_len = try br.takeLeb128(u32);
792 for (try wasm.object_memories.addManyAsSlice(gpa, memories_len)) |*memory| {773 for (try wasm.object_memories.addManyAsSlice(gpa, memories_len)) |*memory| {
793 const limits, pos = readLimits(bytes, pos);774 const limits = try readLimits(br);
794 memory.* = .{775 memory.* = .{
795 .name = .none,776 .name = .none,
796 .flags = .{777 .flags = .{
...@@ -807,14 +788,17 @@ pub fn parse(...@@ -807,14 +788,17 @@ pub fn parse(
807 return diags.failParse(path, "object has more than one global section", .{});788 return diags.failParse(path, "object has more than one global section", .{});
808 global_section_index = section_index;789 global_section_index = section_index;
809790
810 const section_start = pos;791 const section_start = br.seek;
811 const globals_len, pos = readLeb(u32, bytes, pos);792 const globals_len = try br.takeLeb128(u32);
812 for (try wasm.object_globals.addManyAsSlice(gpa, globals_len)) |*global| {793 for (try wasm.object_globals.addManyAsSlice(gpa, globals_len)) |*global| {
813 const valtype, pos = readEnum(std.wasm.Valtype, bytes, pos);794 const valtype = try br.takeEnum(std.wasm.Valtype, .little);
814 const mutable = bytes[pos] == 0x01;795 const mutable = switch (try br.takeByte()) {
815 pos += 1;796 0 => false,
816 const init_start = pos;797 1 => true,
817 const expr, pos = try readInit(wasm, bytes, pos);798 else => return error.InvalidMutability,
799 };
800 const init_start = br.seek;
801 const expr = try readInit(wasm, br);
818 global.* = .{802 global.* = .{
819 .name = .none,803 .name = .none,
820 .flags = .{804 .flags = .{
...@@ -826,20 +810,19 @@ pub fn parse(...@@ -826,20 +810,19 @@ pub fn parse(
826 .expr = expr,810 .expr = expr,
827 .object_index = object_index,811 .object_index = object_index,
828 .offset = @intCast(init_start - section_start),812 .offset = @intCast(init_start - section_start),
829 .size = @intCast(pos - init_start),813 .size = @intCast(br.seek - init_start),
830 };814 };
831 }815 }
832 },816 },
833 .@"export" => {817 .@"export" => {
834 const exports_len, pos = readLeb(u32, bytes, pos);818 const exports_len = try br.takeLeb128(u32);
835 // Read into scratch space, and then later add this data as if819 // Read into scratch space, and then later add this data as if
836 // it were extra symbol table entries, but allow merging with820 // it were extra symbol table entries, but allow merging with
837 // existing symbol table data if the name matches.821 // existing symbol table data if the name matches.
838 for (try ss.exports.addManyAsSlice(gpa, exports_len)) |*exp| {822 for (try ss.exports.addManyAsSlice(gpa, exports_len)) |*exp| {
839 const name, pos = readBytes(bytes, pos);823 const name = try br.take(try br.takeLeb128(u32));
840 const kind: std.wasm.ExternalKind = @enumFromInt(bytes[pos]);824 const kind = try br.takeEnum(std.wasm.ExternalKind, .little);
841 pos += 1;825 const index = try br.takeLeb128(u32);
842 const index, pos = readLeb(u32, bytes, pos);
843 exp.* = .{826 exp.* = .{
844 .name = try wasm.internString(name),827 .name = try wasm.internString(name),
845 .pointee = switch (kind) {828 .pointee = switch (kind) {
...@@ -852,25 +835,24 @@ pub fn parse(...@@ -852,25 +835,24 @@ pub fn parse(
852 }835 }
853 },836 },
854 .start => {837 .start => {
855 const index, pos = readLeb(u32, bytes, pos);838 const index = try br.takeLeb128(u32);
856 start_function = @enumFromInt(functions_start + index);839 start_function = @enumFromInt(functions_start + index);
857 },840 },
858 .element => {841 .element => {
859 log.warn("unimplemented: element section in {f} {?s}", .{ path, archive_member_name });842 log.warn("unimplemented: element section in {f} {?s}", .{ path, archive_member_name });
860 pos = section_end;843 br.seek = section_end;
861 },844 },
862 .code => {845 .code => {
863 if (code_section_index != null)846 if (code_section_index != null)
864 return diags.failParse(path, "object has more than one code section", .{});847 return diags.failParse(path, "object has more than one code section", .{});
865 code_section_index = section_index;848 code_section_index = section_index;
866849
867 const start = pos;850 const start = br.seek;
868 const count, pos = readLeb(u32, bytes, pos);851 const count = try br.takeLeb128(u32);
869 for (try wasm.object_functions.addManyAsSlice(gpa, count)) |*elem| {852 for (try wasm.object_functions.addManyAsSlice(gpa, count)) |*elem| {
870 const code_len, pos = readLeb(u32, bytes, pos);853 const code_len = try br.takeLeb128(u32);
871 const offset: u32 = @intCast(pos - start);854 const offset: u32 = @intCast(br.seek - start);
872 const payload = try wasm.addRelocatableDataPayload(bytes[pos..][0..code_len]);855 const payload = try wasm.addRelocatableDataPayload(try br.take(code_len));
873 pos += code_len;
874 elem.* = .{856 elem.* = .{
875 .flags = .{}, // populated from symbol table857 .flags = .{}, // populated from symbol table
876 .name = .none, // populated from symbol table858 .name = .none, // populated from symbol table
...@@ -886,20 +868,19 @@ pub fn parse(...@@ -886,20 +868,19 @@ pub fn parse(
886 return diags.failParse(path, "object has more than one data section", .{});868 return diags.failParse(path, "object has more than one data section", .{});
887 data_section_index = section_index;869 data_section_index = section_index;
888870
889 const section_start = pos;871 const section_start = br.seek;
890 const count, pos = readLeb(u32, bytes, pos);872 const count = try br.takeLeb128(u32);
891 for (try wasm.object_data_segments.addManyAsSlice(gpa, count)) |*elem| {873 for (try wasm.object_data_segments.addManyAsSlice(gpa, count)) |*elem| {
892 const flags, pos = readEnum(DataSegmentFlags, bytes, pos);874 const flags: DataSegmentFlags = @enumFromInt(try br.takeLeb128(u32));
893 if (flags == .active_memidx) {875 if (flags == .active_memidx) {
894 const memidx, pos = readLeb(u32, bytes, pos);876 const memidx = try br.takeLeb128(u32);
895 if (memidx != 0) return diags.failParse(path, "data section uses mem index {d}", .{memidx});877 if (memidx != 0) return diags.failParse(path, "data section uses mem index {d}", .{memidx});
896 }878 }
897 //const expr, pos = if (flags != .passive) try readInit(wasm, bytes, pos) else .{ .none, pos };879 //const expr = if (flags != .passive) try readInit(wasm, br) else .none;
898 if (flags != .passive) pos = try skipInit(bytes, pos);880 if (flags != .passive) try skipInit(br);
899 const data_len, pos = readLeb(u32, bytes, pos);881 const data_len = try br.takeLeb128(u32);
900 const segment_start = pos;882 const segment_start = br.seek;
901 const payload = try wasm.addRelocatableDataPayload(bytes[pos..][0..data_len]);883 const payload = try wasm.addRelocatableDataPayload(try br.take(data_len));
902 pos += data_len;
903 elem.* = .{884 elem.* = .{
904 .payload = payload,885 .payload = payload,
905 .name = .none, // Populated from segment_info886 .name = .none, // Populated from segment_info
...@@ -911,10 +892,10 @@ pub fn parse(...@@ -911,10 +892,10 @@ pub fn parse(
911 };892 };
912 }893 }
913 },894 },
914 else => pos = section_end,895 else => br.seek = section_end,
915 }896 }
916 if (pos != section_end) return error.MalformedSection;897 if (br.seek != section_end) return error.MalformedSection;
917 }898 } else |_| {}
918 if (!saw_linking_section) return error.MissingLinkingSection;899 if (!saw_linking_section) return error.MissingLinkingSection;
919900
920 const cpu = comp.root_mod.resolved_target.result.cpu;901 const cpu = comp.root_mod.resolved_target.result.cpu;
...@@ -1422,27 +1403,27 @@ pub fn parse(...@@ -1422,27 +1403,27 @@ pub fn parse(
1422/// Based on the "features" custom section, parses it into a list of1403/// Based on the "features" custom section, parses it into a list of
1423/// features that tell the linker what features were enabled and may be mandatory1404/// features that tell the linker what features were enabled and may be mandatory
1424/// to be able to link.1405/// to be able to link.
1425fn parseFeatures(1406fn parseFeatures(wasm: *Wasm, reader: *Reader, path: Path) error{ OutOfMemory, LinkFailure }!Wasm.Feature.Set {
1426 wasm: *Wasm,
1427 bytes: []const u8,
1428 start_pos: usize,
1429 path: Path,
1430) error{ OutOfMemory, LinkFailure }!struct { Wasm.Feature.Set, usize } {
1431 const gpa = wasm.base.comp.gpa;1407 const gpa = wasm.base.comp.gpa;
1432 const diags = &wasm.base.comp.link_diags;1408 const diags = &wasm.base.comp.link_diags;
1433 const features_len, var pos = readLeb(u32, bytes, start_pos);1409 const features_len = reader.takeLeb128(u32) catch |err|
1410 return diags.failParse(path, "invalid features length: {t}", .{err});
1434 // This temporary allocation could be avoided by using the string_bytes buffer as a scratch space.1411 // This temporary allocation could be avoided by using the string_bytes buffer as a scratch space.
1435 const feature_buffer = try gpa.alloc(Wasm.Feature, features_len);1412 const feature_buffer = try gpa.alloc(Wasm.Feature, features_len);
1436 defer gpa.free(feature_buffer);1413 defer gpa.free(feature_buffer);
1437 for (feature_buffer) |*feature| {1414 for (feature_buffer) |*feature| {
1438 const prefix: Wasm.Feature.Prefix = switch (bytes[pos]) {1415 const prefix: Wasm.Feature.Prefix = switch (reader.takeByte() catch |err| {
1416 return diags.failParse(path, "invalid feature prefix: {t}", .{err});
1417 }) {
1439 '-' => .@"-",1418 '-' => .@"-",
1440 '+' => .@"+",1419 '+' => .@"+",
1441 '=' => .@"=",1420 '=' => .@"=",
1442 else => |b| return diags.failParse(path, "invalid feature prefix: 0x{x}", .{b}),1421 else => |b| return diags.failParse(path, "invalid feature prefix: 0x{x}", .{b}),
1443 };1422 };
1444 pos += 1;1423 const name_len = reader.takeLeb128(u32) catch |err|
1445 const name, pos = readBytes(bytes, pos);1424 return diags.failParse(path, "bad feature name length: {t}", .{err});
1425 const name = reader.take(name_len) catch |err|
1426 return diags.failParse(path, "bad feature name: {t}", .{err});
1446 const tag = std.meta.stringToEnum(Wasm.Feature.Tag, name) orelse {1427 const tag = std.meta.stringToEnum(Wasm.Feature.Tag, name) orelse {
1447 return diags.failParse(path, "unrecognized wasm feature in object: {s}", .{name});1428 return diags.failParse(path, "unrecognized wasm feature in object: {s}", .{name});
1448 };1429 };
...@@ -1453,68 +1434,34 @@ fn parseFeatures(...@@ -1453,68 +1434,34 @@ fn parseFeatures(
1453 }1434 }
1454 std.mem.sortUnstable(Wasm.Feature, feature_buffer, {}, Wasm.Feature.lessThan);1435 std.mem.sortUnstable(Wasm.Feature, feature_buffer, {}, Wasm.Feature.lessThan);
14551436
1456 return .{1437 return .fromString(try wasm.internString(@ptrCast(feature_buffer)));
1457 .fromString(try wasm.internString(@ptrCast(feature_buffer))),
1458 pos,
1459 };
1460}1438}
14611439
1462fn readLeb(comptime T: type, bytes: []const u8, pos: usize) struct { T, usize } {1440fn readLimits(reader: *Reader) !std.wasm.Limits {
1463 var fbr = std.io.fixedBufferStream(bytes[pos..]);1441 const flags: std.wasm.Limits.Flags = @bitCast(try reader.takeByte());
1442 const min = try reader.takeLeb128(u32);
1443 const max = if (flags.has_max) try reader.takeLeb128(u32) else 0;
1464 return .{1444 return .{
1465 switch (@typeInfo(T).int.signedness) {
1466 .signed => std.leb.readIleb128(T, fbr.reader()) catch unreachable,
1467 .unsigned => std.leb.readUleb128(T, fbr.reader()) catch unreachable,
1468 },
1469 pos + fbr.pos,
1470 };
1471}
1472
1473fn readBytes(bytes: []const u8, start_pos: usize) struct { []const u8, usize } {
1474 const len, const pos = readLeb(u32, bytes, start_pos);
1475 return .{
1476 bytes[pos..][0..len],
1477 pos + len,
1478 };
1479}
1480
1481fn readEnum(comptime T: type, bytes: []const u8, pos: usize) struct { T, usize } {
1482 const Tag = @typeInfo(T).@"enum".tag_type;
1483 const int, const new_pos = readLeb(Tag, bytes, pos);
1484 return .{ @enumFromInt(int), new_pos };
1485}
1486
1487fn readLimits(bytes: []const u8, start_pos: usize) struct { std.wasm.Limits, usize } {
1488 const flags: std.wasm.Limits.Flags = @bitCast(bytes[start_pos]);
1489 const min, const max_pos = readLeb(u32, bytes, start_pos + 1);
1490 const max, const end_pos = if (flags.has_max) readLeb(u32, bytes, max_pos) else .{ 0, max_pos };
1491 return .{ .{
1492 .flags = flags,1445 .flags = flags,
1493 .min = min,1446 .min = min,
1494 .max = max,1447 .max = max,
1495 }, end_pos };1448 };
1496}1449}
14971450
1498fn readInit(wasm: *Wasm, bytes: []const u8, pos: usize) !struct { Wasm.Expr, usize } {1451fn readInit(wasm: *Wasm, reader: *Reader) !Wasm.Expr {
1499 const end_pos = try skipInit(bytes, pos); // one after the end opcode1452 const start = reader.seek;
1500 return .{ try wasm.addExpr(bytes[pos..end_pos]), end_pos };1453 try skipInit(reader); // one after the end opcode
1454 return wasm.addExpr(reader.buffered()[start..reader.seek]);
1501}1455}
15021456
1503pub fn exprEndPos(bytes: []const u8, pos: usize) error{InvalidInitOpcode}!usize {1457pub fn skipInit(reader: *Reader) !void {
1504 const opcode = bytes[pos];1458 switch (try reader.takeEnumNonexhaustive(std.wasm.Opcode, .little)) {
1505 return switch (@as(std.wasm.Opcode, @enumFromInt(opcode))) {1459 .i32_const => _ = try reader.takeLeb128(i32),
1506 .i32_const => readLeb(i32, bytes, pos + 1)[1],1460 .i64_const => _ = try reader.takeLeb128(i64),
1507 .i64_const => readLeb(i64, bytes, pos + 1)[1],1461 .f32_const => try reader.discardAll(5),
1508 .f32_const => pos + 5,1462 .f64_const => try reader.discardAll(9),
1509 .f64_const => pos + 9,1463 .global_get => _ = try reader.takeLeb128(u32),
1510 .global_get => readLeb(u32, bytes, pos + 1)[1],
1511 else => return error.InvalidInitOpcode,1464 else => return error.InvalidInitOpcode,
1512 };1465 }
1513}1466 if (try reader.takeEnum(std.wasm.Opcode, .little) != .end) return error.InitExprMissingEnd;
1514
1515fn skipInit(bytes: []const u8, pos: usize) !usize {
1516 const end_pos = try exprEndPos(bytes, pos);
1517 const op, const final_pos = readEnum(std.wasm.Opcode, bytes, end_pos);
1518 if (op != .end) return error.InitExprMissingEnd;
1519 return final_pos;
1520}1467}