authorgravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-04-15 03:30:55+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-04-15 22:57:41+02:00
logc32f7a451356817091f874a97c78dd9abb9468fe
tree9ca3c864b6f5ea6f39117531091fc6943f5b1218
parent5984d5dbde62ed1fa26ee88f8e6720b78b812edf

stage2-wasm: support assembly


4 files changed, 959 insertions(+), 18 deletions(-)

src/codegen/wasm/CodeGen.zig+84-15
...@@ -17,6 +17,7 @@ const Compilation = @import("../../Compilation.zig");...@@ -17,6 +17,7 @@ const Compilation = @import("../../Compilation.zig");
17const link = @import("../../link.zig");17const link = @import("../../link.zig");
18const Air = @import("../../Air.zig");18const Air = @import("../../Air.zig");
19const Mir = @import("Mir.zig");19const Mir = @import("Mir.zig");
20const assembly = @import("assembly.zig");
20const abi = @import("../../codegen/wasm/abi.zig");21const abi = @import("../../codegen/wasm/abi.zig");
21const Alignment = InternPool.Alignment;22const Alignment = InternPool.Alignment;
22const errUnionPayloadOffset = codegen.errUnionPayloadOffset;23const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
...@@ -277,7 +278,7 @@ pub fn deinit(cg: *CodeGen) void {...@@ -277,7 +278,7 @@ pub fn deinit(cg: *CodeGen) void {
277 cg.* = undefined;278 cg.* = undefined;
278}279}
279280
280fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {281pub fn fail(cg: *CodeGen, comptime fmt: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } {
281 const zcu = cg.pt.zcu;282 const zcu = cg.pt.zcu;
282 const func = zcu.funcInfo(cg.func_index);283 const func = zcu.funcInfo(cg.func_index);
283 return zcu.codegenFail(func.owner_nav, fmt, args);284 return zcu.codegenFail(func.owner_nav, fmt, args);
...@@ -401,45 +402,45 @@ fn processDeath(cg: *CodeGen, ref: Air.Inst.Ref) void {...@@ -401,45 +402,45 @@ fn processDeath(cg: *CodeGen, ref: Air.Inst.Ref) void {
401 }402 }
402}403}
403404
404fn addInst(cg: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!void {405pub fn addInst(cg: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!void {
405 try cg.mir_instructions.append(cg.gpa, inst);406 try cg.mir_instructions.append(cg.gpa, inst);
406}407}
407408
408fn addTag(cg: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void {409pub fn addTag(cg: *CodeGen, tag: Mir.Inst.Tag) error{OutOfMemory}!void {
409 try cg.addInst(.{ .tag = tag, .data = .{ .tag = {} } });410 try cg.addInst(.{ .tag = tag, .data = .{ .tag = {} } });
410}411}
411412
412fn addExtended(cg: *CodeGen, opcode: std.wasm.MiscOpcode) error{OutOfMemory}!void {413pub fn addExtended(cg: *CodeGen, opcode: std.wasm.MiscOpcode) error{OutOfMemory}!void {
413 const extra_index: u32 = @intCast(cg.mir_extra.items.len);414 const extra_index: u32 = @intCast(cg.mir_extra.items.len);
414 try cg.mir_extra.append(cg.gpa, @intFromEnum(opcode));415 try cg.mir_extra.append(cg.gpa, @intFromEnum(opcode));
415 try cg.addInst(.{ .tag = .misc_prefix, .data = .{ .payload = extra_index } });416 try cg.addInst(.{ .tag = .misc_prefix, .data = .{ .payload = extra_index } });
416}417}
417418
418fn addLabel(cg: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!void {419pub fn addLabel(cg: *CodeGen, tag: Mir.Inst.Tag, label: u32) error{OutOfMemory}!void {
419 try cg.addInst(.{ .tag = tag, .data = .{ .label = label } });420 try cg.addInst(.{ .tag = tag, .data = .{ .label = label } });
420}421}
421422
422fn addLocal(cg: *CodeGen, tag: Mir.Inst.Tag, local: u32) error{OutOfMemory}!void {423pub fn addLocal(cg: *CodeGen, tag: Mir.Inst.Tag, local: u32) error{OutOfMemory}!void {
423 try cg.addInst(.{ .tag = tag, .data = .{ .local = local } });424 try cg.addInst(.{ .tag = tag, .data = .{ .local = local } });
424}425}
425426
426/// Accepts an unsigned 32bit integer rather than a signed integer to427/// Accepts an unsigned 32bit integer rather than a signed integer to
427/// prevent us from having to bitcast multiple times as most values428/// prevent us from having to bitcast multiple times as most values
428/// within codegen are represented as unsigned rather than signed.429/// within codegen are represented as unsigned rather than signed.
429fn addImm32(cg: *CodeGen, imm: u32) error{OutOfMemory}!void {430pub fn addImm32(cg: *CodeGen, imm: u32) error{OutOfMemory}!void {
430 try cg.addInst(.{ .tag = .i32_const, .data = .{ .imm32 = @bitCast(imm) } });431 try cg.addInst(.{ .tag = .i32_const, .data = .{ .imm32 = @bitCast(imm) } });
431}432}
432433
433/// Accepts an unsigned 64bit integer rather than a signed integer to434/// Accepts an unsigned 64bit integer rather than a signed integer to
434/// prevent us from having to bitcast multiple times as most values435/// prevent us from having to bitcast multiple times as most values
435/// within codegen are represented as unsigned rather than signed.436/// within codegen are represented as unsigned rather than signed.
436fn addImm64(cg: *CodeGen, imm: u64) error{OutOfMemory}!void {437pub fn addImm64(cg: *CodeGen, imm: u64) error{OutOfMemory}!void {
437 const extra_index = try cg.addExtra(Mir.Imm64.init(imm));438 const extra_index = try cg.addExtra(Mir.Imm64.init(imm));
438 try cg.addInst(.{ .tag = .i64_const, .data = .{ .payload = extra_index } });439 try cg.addInst(.{ .tag = .i64_const, .data = .{ .payload = extra_index } });
439}440}
440441
441/// Accepts the index into the list of 128bit-immediates442/// Accepts the index into the list of 128bit-immediates
442fn addImm128(cg: *CodeGen, index: u32) error{OutOfMemory}!void {443pub fn addImm128(cg: *CodeGen, index: u32) error{OutOfMemory}!void {
443 const simd_values = cg.simd_immediates.items[index];444 const simd_values = cg.simd_immediates.items[index];
444 const extra_index: u32 = @intCast(cg.mir_extra.items.len);445 const extra_index: u32 = @intCast(cg.mir_extra.items.len);
445 // tag + 128bit value446 // tag + 128bit value
...@@ -449,27 +450,31 @@ fn addImm128(cg: *CodeGen, index: u32) error{OutOfMemory}!void {...@@ -449,27 +450,31 @@ fn addImm128(cg: *CodeGen, index: u32) error{OutOfMemory}!void {
449 try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });450 try cg.addInst(.{ .tag = .simd_prefix, .data = .{ .payload = extra_index } });
450}451}
451452
452fn addFloat64(cg: *CodeGen, float: f64) error{OutOfMemory}!void {453pub fn addFloat32(cg: *CodeGen, float: f32) error{OutOfMemory}!void {
454 try cg.addInst(.{ .tag = .f32_const, .data = .{ .float32 = float } });
455}
456
457pub fn addFloat64(cg: *CodeGen, float: f64) error{OutOfMemory}!void {
453 const extra_index = try cg.addExtra(Mir.Float64.init(float));458 const extra_index = try cg.addExtra(Mir.Float64.init(float));
454 try cg.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } });459 try cg.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } });
455}460}
456461
457/// Inserts an instruction to load/store from/to wasm's linear memory dependent on the given `tag`.462/// Inserts an instruction to load/store from/to wasm's linear memory dependent on the given `tag`.
458fn addMemArg(cg: *CodeGen, tag: Mir.Inst.Tag, mem_arg: Mir.MemArg) error{OutOfMemory}!void {463pub fn addMemArg(cg: *CodeGen, tag: Mir.Inst.Tag, mem_arg: Mir.MemArg) error{OutOfMemory}!void {
459 const extra_index = try cg.addExtra(mem_arg);464 const extra_index = try cg.addExtra(mem_arg);
460 try cg.addInst(.{ .tag = tag, .data = .{ .payload = extra_index } });465 try cg.addInst(.{ .tag = tag, .data = .{ .payload = extra_index } });
461}466}
462467
463/// Inserts an instruction from the 'atomics' feature which accesses wasm's linear memory dependent on the468/// Inserts an instruction from the 'atomics' feature which accesses wasm's linear memory dependent on the
464/// given `tag`.469/// given `tag`.
465fn addAtomicMemArg(cg: *CodeGen, tag: std.wasm.AtomicsOpcode, mem_arg: Mir.MemArg) error{OutOfMemory}!void {470pub fn addAtomicMemArg(cg: *CodeGen, tag: std.wasm.AtomicsOpcode, mem_arg: Mir.MemArg) error{OutOfMemory}!void {
466 const extra_index = try cg.addExtra(@as(struct { val: u32 }, .{ .val = @intFromEnum(tag) }));471 const extra_index = try cg.addExtra(@as(struct { val: u32 }, .{ .val = @intFromEnum(tag) }));
467 _ = try cg.addExtra(mem_arg);472 _ = try cg.addExtra(mem_arg);
468 try cg.addInst(.{ .tag = .atomics_prefix, .data = .{ .payload = extra_index } });473 try cg.addInst(.{ .tag = .atomics_prefix, .data = .{ .payload = extra_index } });
469}474}
470475
471/// Helper function to emit atomic mir opcodes.476/// Helper function to emit atomic mir opcodes.
472fn addAtomicTag(cg: *CodeGen, tag: std.wasm.AtomicsOpcode) error{OutOfMemory}!void {477pub fn addAtomicTag(cg: *CodeGen, tag: std.wasm.AtomicsOpcode) error{OutOfMemory}!void {
473 const extra_index = try cg.addExtra(@as(struct { val: u32 }, .{ .val = @intFromEnum(tag) }));478 const extra_index = try cg.addExtra(@as(struct { val: u32 }, .{ .val = @intFromEnum(tag) }));
474 try cg.addInst(.{ .tag = .atomics_prefix, .data = .{ .payload = extra_index } });479 try cg.addInst(.{ .tag = .atomics_prefix, .data = .{ .payload = extra_index } });
475}480}
...@@ -550,7 +555,7 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {...@@ -550,7 +555,7 @@ fn emitWValue(cg: *CodeGen, value: WValue) InnerError!void {
550 .imm32 => |val| try cg.addImm32(val),555 .imm32 => |val| try cg.addImm32(val),
551 .imm64 => |val| try cg.addImm64(val),556 .imm64 => |val| try cg.addImm64(val),
552 .imm128 => |val| try cg.addImm128(val),557 .imm128 => |val| try cg.addImm128(val),
553 .float32 => |val| try cg.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }),558 .float32 => |val| try cg.addFloat32(val),
554 .float64 => |val| try cg.addFloat64(val),559 .float64 => |val| try cg.addFloat64(val),
555 .nav_ref => |nav_ref| {560 .nav_ref => |nav_ref| {
556 const zcu = cg.pt.zcu;561 const zcu = cg.pt.zcu;
...@@ -1832,7 +1837,8 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -1832,7 +1837,8 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
18321837
1833 .runtime_nav_ptr => cg.airRuntimeNavPtr(inst),1838 .runtime_nav_ptr => cg.airRuntimeNavPtr(inst),
18341839
1835 .assembly,1840 .assembly => cg.airAsm(inst),
1841
1836 .err_return_trace,1842 .err_return_trace,
1837 .set_err_return_trace,1843 .set_err_return_trace,
1838 .save_err_return_trace_index,1844 .save_err_return_trace_index,
...@@ -7153,6 +7159,69 @@ fn airRuntimeNavPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -7153,6 +7159,69 @@ fn airRuntimeNavPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7153 return cg.fail("TODO: thread-local variables", .{});7159 return cg.fail("TODO: thread-local variables", .{});
7154}7160}
71557161
7162fn airAsm(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
7163 const unwrapped_asm = cg.air.unwrapAsm(inst);
7164 const outputs = unwrapped_asm.outputs;
7165 const inputs = unwrapped_asm.inputs;
7166
7167 const zcu = cg.pt.zcu;
7168 const output_ty = cg.typeOfIndex(inst);
7169
7170 const result: WValue = if (output_ty.hasRuntimeBits(zcu))
7171 try cg.allocLocal(output_ty)
7172 else
7173 .none;
7174
7175 var local_map: assembly.LocalMap = .empty;
7176 defer local_map.deinit(cg.gpa);
7177
7178 {
7179 var it = unwrapped_asm.iterateOutputs();
7180 if (it.next()) |output| {
7181 const constraint = output.constraint;
7182 assert(output.operand == .none);
7183 const name = output.name;
7184
7185 if (!mem.eql(u8, constraint, "=r")) {
7186 return cg.fail("Self-hosted wasm backend requires output constraint to be equal \"=r\"", .{});
7187 }
7188
7189 const gop = try local_map.getOrPutValue(cg.gpa, name, result.local.value);
7190 assert(!gop.found_existing); // first value
7191
7192 assert(it.next() == null);
7193 }
7194 }
7195
7196 {
7197 var it = unwrapped_asm.iterateInputs();
7198 while (it.next()) |input| {
7199 const constraint = input.constraint;
7200 const operand = try cg.resolveInst(input.operand);
7201 const name = input.name;
7202
7203 if (!mem.eql(u8, constraint, "r")) {
7204 return cg.fail("Self-hosted wasm backend requires input constraint to be equal \"r\"", .{});
7205 }
7206
7207 try cg.lowerToStack(operand);
7208 const op_local = try WValue.toLocal(.stack, cg, cg.typeOf(input.operand));
7209
7210 const gop = try local_map.getOrPutValue(cg.gpa, name, op_local.local.value);
7211 if (gop.found_existing) {
7212 return cg.fail("Duplicate asm variable name \"{s}\"", .{name});
7213 }
7214 }
7215 }
7216
7217 try assembly.assemble(cg, unwrapped_asm.source, &local_map);
7218
7219 var bt = cg.liveness.iterateBigTomb(inst);
7220 for (outputs) |output| if (output != .none) cg.feed(&bt, output);
7221 for (inputs) |input| cg.feed(&bt, input);
7222 return cg.finishAirResult(inst, result);
7223}
7224
7156fn typeOf(cg: *CodeGen, inst: Air.Inst.Ref) Type {7225fn typeOf(cg: *CodeGen, inst: Air.Inst.Ref) Type {
7157 const zcu = cg.pt.zcu;7226 const zcu = cg.pt.zcu;
7158 return cg.air.typeOf(inst, &zcu.intern_pool);7227 return cg.air.typeOf(inst, &zcu.intern_pool);
src/codegen/wasm/assembly.zig created+717
...@@ -0,0 +1,717 @@
1const std = @import("std");
2const mem = std.mem;
3const assert = std.debug.assert;
4const CodeGen = @import("CodeGen.zig");
5const Mir = @import("Mir.zig");
6
7pub const LocalMap = std.array_hash_map.String(u32);
8
9pub fn assemble(
10 cg: *CodeGen,
11 source: [:0]const u8,
12 local_map: *const LocalMap,
13) !void {
14 var line_it = mem.tokenizeAny(u8, source, "\n\r;");
15 next_line: while (line_it.next()) |line| {
16 var word_it = mem.tokenizeAny(u8, line, " \t");
17 const inst = word_it.next() orelse continue :next_line;
18
19 if (mem.eql(u8, inst, "unreachable")) {
20 try cg.addTag(.@"unreachable");
21 } else if (mem.eql(u8, inst, "block")) {
22 try cg.addTag(.block);
23 } else if (mem.eql(u8, inst, "loop")) {
24 try cg.addTag(.loop);
25 } else if (mem.eql(u8, inst, "end")) {
26 try cg.addTag(.end);
27 } else if (mem.eql(u8, inst, "br")) {
28 const label = try parseLabel(cg, inst, &word_it);
29 try cg.addLabel(.br, label);
30 } else if (mem.eql(u8, inst, "br_if")) {
31 const label = try parseLabel(cg, inst, &word_it);
32 try cg.addLabel(.br_if, label);
33 } else if (mem.eql(u8, inst, "return")) {
34 try cg.addTag(.@"return");
35 } else if (mem.eql(u8, inst, "drop")) {
36 try cg.addTag(.drop);
37 } else if (mem.eql(u8, inst, "select")) {
38 try cg.addTag(.select);
39 } else if (mem.eql(u8, inst, "local.get")) {
40 const local = try parseLocalArg(cg, inst, &word_it, local_map);
41 try cg.addLocal(.local_get, local);
42 } else if (mem.eql(u8, inst, "local.set")) {
43 const local = try parseLocalArg(cg, inst, &word_it, local_map);
44 try cg.addLocal(.local_set, local);
45 } else if (mem.eql(u8, inst, "local.tee")) {
46 const local = try parseLocalArg(cg, inst, &word_it, local_map);
47 try cg.addLocal(.local_tee, local);
48 } else if (mem.eql(u8, inst, "i32.load")) {
49 const mem_arg = try parseMemArg(cg, inst, &word_it);
50 try cg.addMemArg(.i32_load, mem_arg);
51 } else if (mem.eql(u8, inst, "i64.load")) {
52 const mem_arg = try parseMemArg(cg, inst, &word_it);
53 try cg.addMemArg(.i64_load, mem_arg);
54 } else if (mem.eql(u8, inst, "f32.load")) {
55 const mem_arg = try parseMemArg(cg, inst, &word_it);
56 try cg.addMemArg(.f32_load, mem_arg);
57 } else if (mem.eql(u8, inst, "f64.load")) {
58 const mem_arg = try parseMemArg(cg, inst, &word_it);
59 try cg.addMemArg(.f64_load, mem_arg);
60 } else if (mem.eql(u8, inst, "i32.load8_s")) {
61 const mem_arg = try parseMemArg(cg, inst, &word_it);
62 try cg.addMemArg(.i32_load8_s, mem_arg);
63 } else if (mem.eql(u8, inst, "i32.load8_u")) {
64 const mem_arg = try parseMemArg(cg, inst, &word_it);
65 try cg.addMemArg(.i32_load8_u, mem_arg);
66 } else if (mem.eql(u8, inst, "i32.load16_s")) {
67 const mem_arg = try parseMemArg(cg, inst, &word_it);
68 try cg.addMemArg(.i32_load16_s, mem_arg);
69 } else if (mem.eql(u8, inst, "i32.load16_u")) {
70 const mem_arg = try parseMemArg(cg, inst, &word_it);
71 try cg.addMemArg(.i32_load16_u, mem_arg);
72 } else if (mem.eql(u8, inst, "i64.load8_s")) {
73 const mem_arg = try parseMemArg(cg, inst, &word_it);
74 try cg.addMemArg(.i64_load8_s, mem_arg);
75 } else if (mem.eql(u8, inst, "i64.load8_u")) {
76 const mem_arg = try parseMemArg(cg, inst, &word_it);
77 try cg.addMemArg(.i64_load8_u, mem_arg);
78 } else if (mem.eql(u8, inst, "i64.load16_s")) {
79 const mem_arg = try parseMemArg(cg, inst, &word_it);
80 try cg.addMemArg(.i64_load16_s, mem_arg);
81 } else if (mem.eql(u8, inst, "i64.load16_u")) {
82 const mem_arg = try parseMemArg(cg, inst, &word_it);
83 try cg.addMemArg(.i64_load16_u, mem_arg);
84 } else if (mem.eql(u8, inst, "i64.load32_s")) {
85 const mem_arg = try parseMemArg(cg, inst, &word_it);
86 try cg.addMemArg(.i64_load32_s, mem_arg);
87 } else if (mem.eql(u8, inst, "i64.load32_u")) {
88 const mem_arg = try parseMemArg(cg, inst, &word_it);
89 try cg.addMemArg(.i64_load32_u, mem_arg);
90 } else if (mem.eql(u8, inst, "i32.store")) {
91 const mem_arg = try parseMemArg(cg, inst, &word_it);
92 try cg.addMemArg(.i32_store, mem_arg);
93 } else if (mem.eql(u8, inst, "i64.store")) {
94 const mem_arg = try parseMemArg(cg, inst, &word_it);
95 try cg.addMemArg(.i64_store, mem_arg);
96 } else if (mem.eql(u8, inst, "f32.store")) {
97 const mem_arg = try parseMemArg(cg, inst, &word_it);
98 try cg.addMemArg(.f32_store, mem_arg);
99 } else if (mem.eql(u8, inst, "f64.store")) {
100 const mem_arg = try parseMemArg(cg, inst, &word_it);
101 try cg.addMemArg(.f64_store, mem_arg);
102 } else if (mem.eql(u8, inst, "i32.store8")) {
103 const mem_arg = try parseMemArg(cg, inst, &word_it);
104 try cg.addMemArg(.i32_store8, mem_arg);
105 } else if (mem.eql(u8, inst, "i32.store16")) {
106 const mem_arg = try parseMemArg(cg, inst, &word_it);
107 try cg.addMemArg(.i32_store16, mem_arg);
108 } else if (mem.eql(u8, inst, "i64.store8")) {
109 const mem_arg = try parseMemArg(cg, inst, &word_it);
110 try cg.addMemArg(.i64_store8, mem_arg);
111 } else if (mem.eql(u8, inst, "i64.store16")) {
112 const mem_arg = try parseMemArg(cg, inst, &word_it);
113 try cg.addMemArg(.i64_store16, mem_arg);
114 } else if (mem.eql(u8, inst, "i64.store32")) {
115 const mem_arg = try parseMemArg(cg, inst, &word_it);
116 try cg.addMemArg(.i64_store32, mem_arg);
117 } else if (mem.eql(u8, inst, "memory.size")) {
118 try cg.addLabel(.memory_size, 0);
119 } else if (mem.eql(u8, inst, "memory.grow")) {
120 try cg.addLabel(.memory_grow, 0);
121 } else if (mem.eql(u8, inst, "i32.const")) {
122 const value = try parseInt(i32, cg, inst, &word_it);
123 try cg.addImm32(@bitCast(value));
124 } else if (mem.eql(u8, inst, "i64.const")) {
125 const value = try parseInt(i64, cg, inst, &word_it);
126 try cg.addImm64(@bitCast(value));
127 } else if (mem.eql(u8, inst, "f32.const")) {
128 const value = try parseFloat(f32, cg, inst, &word_it);
129 try cg.addFloat32(value);
130 } else if (mem.eql(u8, inst, "f64.const")) {
131 const value = try parseFloat(f64, cg, inst, &word_it);
132 try cg.addFloat64(value);
133 } else if (mem.eql(u8, inst, "i32.eqz")) {
134 try cg.addTag(.i32_eqz);
135 } else if (mem.eql(u8, inst, "i32.eq")) {
136 try cg.addTag(.i32_eq);
137 } else if (mem.eql(u8, inst, "i32.ne")) {
138 try cg.addTag(.i32_ne);
139 } else if (mem.eql(u8, inst, "i32.lt_s")) {
140 try cg.addTag(.i32_lt_s);
141 } else if (mem.eql(u8, inst, "i32.lt_u")) {
142 try cg.addTag(.i32_lt_u);
143 } else if (mem.eql(u8, inst, "i32.gt_s")) {
144 try cg.addTag(.i32_gt_s);
145 } else if (mem.eql(u8, inst, "i32.gt_u")) {
146 try cg.addTag(.i32_gt_u);
147 } else if (mem.eql(u8, inst, "i32.le_s")) {
148 try cg.addTag(.i32_le_s);
149 } else if (mem.eql(u8, inst, "i32.le_u")) {
150 try cg.addTag(.i32_le_u);
151 } else if (mem.eql(u8, inst, "i32.ge_s")) {
152 try cg.addTag(.i32_ge_s);
153 } else if (mem.eql(u8, inst, "i32.ge_u")) {
154 try cg.addTag(.i32_ge_u);
155 } else if (mem.eql(u8, inst, "i64.eqz")) {
156 try cg.addTag(.i64_eqz);
157 } else if (mem.eql(u8, inst, "i64.eq")) {
158 try cg.addTag(.i64_eq);
159 } else if (mem.eql(u8, inst, "i64.ne")) {
160 try cg.addTag(.i64_ne);
161 } else if (mem.eql(u8, inst, "i64.lt_s")) {
162 try cg.addTag(.i64_lt_s);
163 } else if (mem.eql(u8, inst, "i64.lt_u")) {
164 try cg.addTag(.i64_lt_u);
165 } else if (mem.eql(u8, inst, "i64.gt_s")) {
166 try cg.addTag(.i64_gt_s);
167 } else if (mem.eql(u8, inst, "i64.gt_u")) {
168 try cg.addTag(.i64_gt_u);
169 } else if (mem.eql(u8, inst, "i64.le_s")) {
170 try cg.addTag(.i64_le_s);
171 } else if (mem.eql(u8, inst, "i64.le_u")) {
172 try cg.addTag(.i64_le_u);
173 } else if (mem.eql(u8, inst, "i64.ge_s")) {
174 try cg.addTag(.i64_ge_s);
175 } else if (mem.eql(u8, inst, "i64.ge_u")) {
176 try cg.addTag(.i64_ge_u);
177 } else if (mem.eql(u8, inst, "f32.eq")) {
178 try cg.addTag(.f32_eq);
179 } else if (mem.eql(u8, inst, "f32.ne")) {
180 try cg.addTag(.f32_ne);
181 } else if (mem.eql(u8, inst, "f32.lt")) {
182 try cg.addTag(.f32_lt);
183 } else if (mem.eql(u8, inst, "f32.gt")) {
184 try cg.addTag(.f32_gt);
185 } else if (mem.eql(u8, inst, "f32.le")) {
186 try cg.addTag(.f32_le);
187 } else if (mem.eql(u8, inst, "f32.ge")) {
188 try cg.addTag(.f32_ge);
189 } else if (mem.eql(u8, inst, "f64.eq")) {
190 try cg.addTag(.f64_eq);
191 } else if (mem.eql(u8, inst, "f64.ne")) {
192 try cg.addTag(.f64_ne);
193 } else if (mem.eql(u8, inst, "f64.lt")) {
194 try cg.addTag(.f64_lt);
195 } else if (mem.eql(u8, inst, "f64.gt")) {
196 try cg.addTag(.f64_gt);
197 } else if (mem.eql(u8, inst, "f64.le")) {
198 try cg.addTag(.f64_le);
199 } else if (mem.eql(u8, inst, "f64.ge")) {
200 try cg.addTag(.f64_ge);
201 } else if (mem.eql(u8, inst, "i32.clz")) {
202 try cg.addTag(.i32_clz);
203 } else if (mem.eql(u8, inst, "i32.ctz")) {
204 try cg.addTag(.i32_ctz);
205 } else if (mem.eql(u8, inst, "i32.popcnt")) {
206 try cg.addTag(.i32_popcnt);
207 } else if (mem.eql(u8, inst, "i32.add")) {
208 try cg.addTag(.i32_add);
209 } else if (mem.eql(u8, inst, "i32.sub")) {
210 try cg.addTag(.i32_sub);
211 } else if (mem.eql(u8, inst, "i32.mul")) {
212 try cg.addTag(.i32_mul);
213 } else if (mem.eql(u8, inst, "i32.div_s")) {
214 try cg.addTag(.i32_div_s);
215 } else if (mem.eql(u8, inst, "i32.div_u")) {
216 try cg.addTag(.i32_div_u);
217 } else if (mem.eql(u8, inst, "i32.rem_s")) {
218 try cg.addTag(.i32_rem_s);
219 } else if (mem.eql(u8, inst, "i32.rem_u")) {
220 try cg.addTag(.i32_rem_u);
221 } else if (mem.eql(u8, inst, "i32.and")) {
222 try cg.addTag(.i32_and);
223 } else if (mem.eql(u8, inst, "i32.or")) {
224 try cg.addTag(.i32_or);
225 } else if (mem.eql(u8, inst, "i32.xor")) {
226 try cg.addTag(.i32_xor);
227 } else if (mem.eql(u8, inst, "i32.shl")) {
228 try cg.addTag(.i32_shl);
229 } else if (mem.eql(u8, inst, "i32.shr_s")) {
230 try cg.addTag(.i32_shr_s);
231 } else if (mem.eql(u8, inst, "i32.shr_u")) {
232 try cg.addTag(.i32_shr_u);
233 } else if (mem.eql(u8, inst, "i64.clz")) {
234 try cg.addTag(.i64_clz);
235 } else if (mem.eql(u8, inst, "i64.ctz")) {
236 try cg.addTag(.i64_ctz);
237 } else if (mem.eql(u8, inst, "i64.popcnt")) {
238 try cg.addTag(.i64_popcnt);
239 } else if (mem.eql(u8, inst, "i64.add")) {
240 try cg.addTag(.i64_add);
241 } else if (mem.eql(u8, inst, "i64.sub")) {
242 try cg.addTag(.i64_sub);
243 } else if (mem.eql(u8, inst, "i64.mul")) {
244 try cg.addTag(.i64_mul);
245 } else if (mem.eql(u8, inst, "i64.div_s")) {
246 try cg.addTag(.i64_div_s);
247 } else if (mem.eql(u8, inst, "i64.div_u")) {
248 try cg.addTag(.i64_div_u);
249 } else if (mem.eql(u8, inst, "i64.rem_s")) {
250 try cg.addTag(.i64_rem_s);
251 } else if (mem.eql(u8, inst, "i64.rem_u")) {
252 try cg.addTag(.i64_rem_u);
253 } else if (mem.eql(u8, inst, "i64.and")) {
254 try cg.addTag(.i64_and);
255 } else if (mem.eql(u8, inst, "i64.or")) {
256 try cg.addTag(.i64_or);
257 } else if (mem.eql(u8, inst, "i64.xor")) {
258 try cg.addTag(.i64_xor);
259 } else if (mem.eql(u8, inst, "i64.shl")) {
260 try cg.addTag(.i64_shl);
261 } else if (mem.eql(u8, inst, "i64.shr_s")) {
262 try cg.addTag(.i64_shr_s);
263 } else if (mem.eql(u8, inst, "i64.shr_u")) {
264 try cg.addTag(.i64_shr_u);
265 } else if (mem.eql(u8, inst, "f32.abs")) {
266 try cg.addTag(.f32_abs);
267 } else if (mem.eql(u8, inst, "f32.neg")) {
268 try cg.addTag(.f32_neg);
269 } else if (mem.eql(u8, inst, "f32.ceil")) {
270 try cg.addTag(.f32_ceil);
271 } else if (mem.eql(u8, inst, "f32.floor")) {
272 try cg.addTag(.f32_floor);
273 } else if (mem.eql(u8, inst, "f32.trunc")) {
274 try cg.addTag(.f32_trunc);
275 } else if (mem.eql(u8, inst, "f32.nearest")) {
276 try cg.addTag(.f32_nearest);
277 } else if (mem.eql(u8, inst, "f32.sqrt")) {
278 try cg.addTag(.f32_sqrt);
279 } else if (mem.eql(u8, inst, "f32.add")) {
280 try cg.addTag(.f32_add);
281 } else if (mem.eql(u8, inst, "f32.sub")) {
282 try cg.addTag(.f32_sub);
283 } else if (mem.eql(u8, inst, "f32.mul")) {
284 try cg.addTag(.f32_mul);
285 } else if (mem.eql(u8, inst, "f32.div")) {
286 try cg.addTag(.f32_div);
287 } else if (mem.eql(u8, inst, "f32.min")) {
288 try cg.addTag(.f32_min);
289 } else if (mem.eql(u8, inst, "f32.max")) {
290 try cg.addTag(.f32_max);
291 } else if (mem.eql(u8, inst, "f32.copysign")) {
292 try cg.addTag(.f32_copysign);
293 } else if (mem.eql(u8, inst, "f64.abs")) {
294 try cg.addTag(.f64_abs);
295 } else if (mem.eql(u8, inst, "f64.neg")) {
296 try cg.addTag(.f64_neg);
297 } else if (mem.eql(u8, inst, "f64.ceil")) {
298 try cg.addTag(.f64_ceil);
299 } else if (mem.eql(u8, inst, "f64.floor")) {
300 try cg.addTag(.f64_floor);
301 } else if (mem.eql(u8, inst, "f64.trunc")) {
302 try cg.addTag(.f64_trunc);
303 } else if (mem.eql(u8, inst, "f64.nearest")) {
304 try cg.addTag(.f64_nearest);
305 } else if (mem.eql(u8, inst, "f64.sqrt")) {
306 try cg.addTag(.f64_sqrt);
307 } else if (mem.eql(u8, inst, "f64.add")) {
308 try cg.addTag(.f64_add);
309 } else if (mem.eql(u8, inst, "f64.sub")) {
310 try cg.addTag(.f64_sub);
311 } else if (mem.eql(u8, inst, "f64.mul")) {
312 try cg.addTag(.f64_mul);
313 } else if (mem.eql(u8, inst, "f64.div")) {
314 try cg.addTag(.f64_div);
315 } else if (mem.eql(u8, inst, "f64.min")) {
316 try cg.addTag(.f64_min);
317 } else if (mem.eql(u8, inst, "f64.max")) {
318 try cg.addTag(.f64_max);
319 } else if (mem.eql(u8, inst, "f64.copysign")) {
320 try cg.addTag(.f64_copysign);
321 } else if (mem.eql(u8, inst, "i32.wrap_i64")) {
322 try cg.addTag(.i32_wrap_i64);
323 } else if (mem.eql(u8, inst, "i32.trunc_f32_s")) {
324 try cg.addTag(.i32_trunc_f32_s);
325 } else if (mem.eql(u8, inst, "i32.trunc_f32_u")) {
326 try cg.addTag(.i32_trunc_f32_u);
327 } else if (mem.eql(u8, inst, "i32.trunc_f64_s")) {
328 try cg.addTag(.i32_trunc_f64_s);
329 } else if (mem.eql(u8, inst, "i32.trunc_f64_u")) {
330 try cg.addTag(.i32_trunc_f64_u);
331 } else if (mem.eql(u8, inst, "i64.extend_i32_s")) {
332 try cg.addTag(.i64_extend_i32_s);
333 } else if (mem.eql(u8, inst, "i64.extend_i32_u")) {
334 try cg.addTag(.i64_extend_i32_u);
335 } else if (mem.eql(u8, inst, "i64.trunc_f32_s")) {
336 try cg.addTag(.i64_trunc_f32_s);
337 } else if (mem.eql(u8, inst, "i64.trunc_f32_u")) {
338 try cg.addTag(.i64_trunc_f32_u);
339 } else if (mem.eql(u8, inst, "i64.trunc_f64_s")) {
340 try cg.addTag(.i64_trunc_f64_s);
341 } else if (mem.eql(u8, inst, "i64.trunc_f64_u")) {
342 try cg.addTag(.i64_trunc_f64_u);
343 } else if (mem.eql(u8, inst, "f32.convert_i32_s")) {
344 try cg.addTag(.f32_convert_i32_s);
345 } else if (mem.eql(u8, inst, "f32.convert_i32_u")) {
346 try cg.addTag(.f32_convert_i32_u);
347 } else if (mem.eql(u8, inst, "f32.convert_i64_s")) {
348 try cg.addTag(.f32_convert_i64_s);
349 } else if (mem.eql(u8, inst, "f32.convert_i64_u")) {
350 try cg.addTag(.f32_convert_i64_u);
351 } else if (mem.eql(u8, inst, "f32.demote_f64")) {
352 try cg.addTag(.f32_demote_f64);
353 } else if (mem.eql(u8, inst, "f64.convert_i32_s")) {
354 try cg.addTag(.f64_convert_i32_s);
355 } else if (mem.eql(u8, inst, "f64.convert_i32_u")) {
356 try cg.addTag(.f64_convert_i32_u);
357 } else if (mem.eql(u8, inst, "f64.convert_i64_s")) {
358 try cg.addTag(.f64_convert_i64_s);
359 } else if (mem.eql(u8, inst, "f64.convert_i64_u")) {
360 try cg.addTag(.f64_convert_i64_u);
361 } else if (mem.eql(u8, inst, "f64.promote_f32")) {
362 try cg.addTag(.f64_promote_f32);
363 } else if (mem.eql(u8, inst, "i32.reinterpret_f32")) {
364 try cg.addTag(.i32_reinterpret_f32);
365 } else if (mem.eql(u8, inst, "i64.reinterpret_f64")) {
366 try cg.addTag(.i64_reinterpret_f64);
367 } else if (mem.eql(u8, inst, "f32.reinterpret_i32")) {
368 try cg.addTag(.f32_reinterpret_i32);
369 } else if (mem.eql(u8, inst, "f64.reinterpret_i64")) {
370 try cg.addTag(.f64_reinterpret_i64);
371 } else if (mem.eql(u8, inst, "i32.extend8_s")) {
372 try cg.addTag(.i32_extend8_s);
373 } else if (mem.eql(u8, inst, "i32.extend16_s")) {
374 try cg.addTag(.i32_extend16_s);
375 } else if (mem.eql(u8, inst, "i64.extend8_s")) {
376 try cg.addTag(.i64_extend8_s);
377 } else if (mem.eql(u8, inst, "i64.extend16_s")) {
378 try cg.addTag(.i64_extend16_s);
379 } else if (mem.eql(u8, inst, "i64.extend32_s")) {
380 try cg.addTag(.i64_extend32_s);
381 } else if (mem.eql(u8, inst, "i32.trunc_sat_f32_s")) {
382 try cg.addExtended(.i32_trunc_sat_f32_s);
383 } else if (mem.eql(u8, inst, "i32.trunc_sat_f32_u")) {
384 try cg.addExtended(.i32_trunc_sat_f32_u);
385 } else if (mem.eql(u8, inst, "i32.trunc_sat_f64_s")) {
386 try cg.addExtended(.i32_trunc_sat_f64_s);
387 } else if (mem.eql(u8, inst, "i32.trunc_sat_f64_u")) {
388 try cg.addExtended(.i32_trunc_sat_f64_u);
389 } else if (mem.eql(u8, inst, "i64.trunc_sat_f32_s")) {
390 try cg.addExtended(.i64_trunc_sat_f32_s);
391 } else if (mem.eql(u8, inst, "i64.trunc_sat_f32_u")) {
392 try cg.addExtended(.i64_trunc_sat_f32_u);
393 } else if (mem.eql(u8, inst, "i64.trunc_sat_f64_s")) {
394 try cg.addExtended(.i64_trunc_sat_f64_s);
395 } else if (mem.eql(u8, inst, "i64.trunc_sat_f64_u")) {
396 try cg.addExtended(.i64_trunc_sat_f64_u);
397 } else if (mem.eql(u8, inst, "memory.init")) {
398 try cg.addExtended(.memory_init);
399 } else if (mem.eql(u8, inst, "data.drop")) {
400 try cg.addExtended(.data_drop);
401 } else if (mem.eql(u8, inst, "memory.copy")) {
402 const str1 = word_it.next();
403 if (str1 == null or !mem.eql(u8, str1.?, "0,")) {
404 return cg.fail("Self-hosted backend requires memory.copy be in form of \"memory.copy 0, 0\"", .{});
405 }
406 const str2 = word_it.next();
407 if (str2 == null or !mem.eql(u8, str2.?, "0")) {
408 return cg.fail("Self-hosted backend requires memory.copy be in form of \"memory.copy 0, 0\"", .{});
409 }
410 try cg.addExtended(.memory_copy);
411 } else if (mem.eql(u8, inst, "memory.fill")) {
412 const str = word_it.next();
413 if (str == null or !mem.eql(u8, str.?, "0")) {
414 return cg.fail("Self-hosted backend requires memory.fill be in form of \"memory.fill 0\"", .{});
415 }
416 try cg.addExtended(.memory_fill);
417 } else if (mem.eql(u8, inst, "table.init")) {
418 try cg.addExtended(.table_init);
419 } else if (mem.eql(u8, inst, "elem.drop")) {
420 try cg.addExtended(.elem_drop);
421 } else if (mem.eql(u8, inst, "table.copy")) {
422 try cg.addExtended(.table_copy);
423 } else if (mem.eql(u8, inst, "table.grow")) {
424 try cg.addExtended(.table_grow);
425 } else if (mem.eql(u8, inst, "table.size")) {
426 try cg.addExtended(.table_size);
427 } else if (mem.eql(u8, inst, "table.fill")) {
428 try cg.addExtended(.table_fill);
429 } else if (mem.eql(u8, inst, "memory.atomic.notify")) {
430 const mem_arg = try parseMemArg(cg, inst, &word_it);
431 try cg.addAtomicMemArg(.memory_atomic_notify, mem_arg);
432 } else if (mem.eql(u8, inst, "memory.atomic.wait32")) {
433 const mem_arg = try parseMemArg(cg, inst, &word_it);
434 try cg.addAtomicMemArg(.memory_atomic_wait32, mem_arg);
435 } else if (mem.eql(u8, inst, "memory.atomic.wait64")) {
436 const mem_arg = try parseMemArg(cg, inst, &word_it);
437 try cg.addAtomicMemArg(.memory_atomic_wait64, mem_arg);
438 } else if (mem.eql(u8, inst, "atomic.fence")) {
439 try cg.addAtomicTag(.atomic_fence);
440 } else if (mem.eql(u8, inst, "i32.atomic.load")) {
441 const mem_arg = try parseMemArg(cg, inst, &word_it);
442 try cg.addAtomicMemArg(.i32_atomic_load, mem_arg);
443 } else if (mem.eql(u8, inst, "i64.atomic.load")) {
444 const mem_arg = try parseMemArg(cg, inst, &word_it);
445 try cg.addAtomicMemArg(.i64_atomic_load, mem_arg);
446 } else if (mem.eql(u8, inst, "i32.atomic.load8_u")) {
447 const mem_arg = try parseMemArg(cg, inst, &word_it);
448 try cg.addAtomicMemArg(.i32_atomic_load8_u, mem_arg);
449 } else if (mem.eql(u8, inst, "i32.atomic.load16_u")) {
450 const mem_arg = try parseMemArg(cg, inst, &word_it);
451 try cg.addAtomicMemArg(.i32_atomic_load16_u, mem_arg);
452 } else if (mem.eql(u8, inst, "i64.atomic.load8_u")) {
453 const mem_arg = try parseMemArg(cg, inst, &word_it);
454 try cg.addAtomicMemArg(.i64_atomic_load8_u, mem_arg);
455 } else if (mem.eql(u8, inst, "i64.atomic.load16_u")) {
456 const mem_arg = try parseMemArg(cg, inst, &word_it);
457 try cg.addAtomicMemArg(.i64_atomic_load16_u, mem_arg);
458 } else if (mem.eql(u8, inst, "i64.atomic.load32_u")) {
459 const mem_arg = try parseMemArg(cg, inst, &word_it);
460 try cg.addAtomicMemArg(.i64_atomic_load32_u, mem_arg);
461 } else if (mem.eql(u8, inst, "i32.atomic.store")) {
462 const mem_arg = try parseMemArg(cg, inst, &word_it);
463 try cg.addAtomicMemArg(.i32_atomic_store, mem_arg);
464 } else if (mem.eql(u8, inst, "i64.atomic.store")) {
465 const mem_arg = try parseMemArg(cg, inst, &word_it);
466 try cg.addAtomicMemArg(.i64_atomic_store, mem_arg);
467 } else if (mem.eql(u8, inst, "i32.atomic.store8")) {
468 const mem_arg = try parseMemArg(cg, inst, &word_it);
469 try cg.addAtomicMemArg(.i32_atomic_store8, mem_arg);
470 } else if (mem.eql(u8, inst, "i32.atomic.store16")) {
471 const mem_arg = try parseMemArg(cg, inst, &word_it);
472 try cg.addAtomicMemArg(.i32_atomic_store16, mem_arg);
473 } else if (mem.eql(u8, inst, "i64.atomic.store8")) {
474 const mem_arg = try parseMemArg(cg, inst, &word_it);
475 try cg.addAtomicMemArg(.i64_atomic_store8, mem_arg);
476 } else if (mem.eql(u8, inst, "i64.atomic.store16")) {
477 const mem_arg = try parseMemArg(cg, inst, &word_it);
478 try cg.addAtomicMemArg(.i64_atomic_store16, mem_arg);
479 } else if (mem.eql(u8, inst, "i64.atomic.store32")) {
480 const mem_arg = try parseMemArg(cg, inst, &word_it);
481 try cg.addAtomicMemArg(.i64_atomic_store32, mem_arg);
482 } else if (mem.eql(u8, inst, "i32.atomic.rmw.add")) {
483 const mem_arg = try parseMemArg(cg, inst, &word_it);
484 try cg.addAtomicMemArg(.i32_atomic_rmw_add, mem_arg);
485 } else if (mem.eql(u8, inst, "i64.atomic.rmw.add")) {
486 const mem_arg = try parseMemArg(cg, inst, &word_it);
487 try cg.addAtomicMemArg(.i64_atomic_rmw_add, mem_arg);
488 } else if (mem.eql(u8, inst, "i32.atomic.rmw8.add_u")) {
489 const mem_arg = try parseMemArg(cg, inst, &word_it);
490 try cg.addAtomicMemArg(.i32_atomic_rmw8_add_u, mem_arg);
491 } else if (mem.eql(u8, inst, "i32.atomic.rmw16.add_u")) {
492 const mem_arg = try parseMemArg(cg, inst, &word_it);
493 try cg.addAtomicMemArg(.i32_atomic_rmw16_add_u, mem_arg);
494 } else if (mem.eql(u8, inst, "i64.atomic.rmw8.add_u")) {
495 const mem_arg = try parseMemArg(cg, inst, &word_it);
496 try cg.addAtomicMemArg(.i64_atomic_rmw8_add_u, mem_arg);
497 } else if (mem.eql(u8, inst, "i64.atomic.rmw16.add_u")) {
498 const mem_arg = try parseMemArg(cg, inst, &word_it);
499 try cg.addAtomicMemArg(.i64_atomic_rmw16_add_u, mem_arg);
500 } else if (mem.eql(u8, inst, "i64.atomic.rmw32.add_u")) {
501 const mem_arg = try parseMemArg(cg, inst, &word_it);
502 try cg.addAtomicMemArg(.i64_atomic_rmw32_add_u, mem_arg);
503 } else if (mem.eql(u8, inst, "i32.atomic.rmw.sub")) {
504 const mem_arg = try parseMemArg(cg, inst, &word_it);
505 try cg.addAtomicMemArg(.i32_atomic_rmw_sub, mem_arg);
506 } else if (mem.eql(u8, inst, "i64.atomic.rmw.sub")) {
507 const mem_arg = try parseMemArg(cg, inst, &word_it);
508 try cg.addAtomicMemArg(.i64_atomic_rmw_sub, mem_arg);
509 } else if (mem.eql(u8, inst, "i32.atomic.rmw8.sub_u")) {
510 const mem_arg = try parseMemArg(cg, inst, &word_it);
511 try cg.addAtomicMemArg(.i32_atomic_rmw8_sub_u, mem_arg);
512 } else if (mem.eql(u8, inst, "i32.atomic.rmw16.sub_u")) {
513 const mem_arg = try parseMemArg(cg, inst, &word_it);
514 try cg.addAtomicMemArg(.i32_atomic_rmw16_sub_u, mem_arg);
515 } else if (mem.eql(u8, inst, "i64.atomic.rmw8.sub_u")) {
516 const mem_arg = try parseMemArg(cg, inst, &word_it);
517 try cg.addAtomicMemArg(.i64_atomic_rmw8_sub_u, mem_arg);
518 } else if (mem.eql(u8, inst, "i64.atomic.rmw16.sub_u")) {
519 const mem_arg = try parseMemArg(cg, inst, &word_it);
520 try cg.addAtomicMemArg(.i64_atomic_rmw16_sub_u, mem_arg);
521 } else if (mem.eql(u8, inst, "i64.atomic.rmw32.sub_u")) {
522 const mem_arg = try parseMemArg(cg, inst, &word_it);
523 try cg.addAtomicMemArg(.i64_atomic_rmw32_sub_u, mem_arg);
524 } else if (mem.eql(u8, inst, "i32.atomic.rmw.and")) {
525 const mem_arg = try parseMemArg(cg, inst, &word_it);
526 try cg.addAtomicMemArg(.i32_atomic_rmw_and, mem_arg);
527 } else if (mem.eql(u8, inst, "i64.atomic.rmw.and")) {
528 const mem_arg = try parseMemArg(cg, inst, &word_it);
529 try cg.addAtomicMemArg(.i64_atomic_rmw_and, mem_arg);
530 } else if (mem.eql(u8, inst, "i32.atomic.rmw8.and_u")) {
531 const mem_arg = try parseMemArg(cg, inst, &word_it);
532 try cg.addAtomicMemArg(.i32_atomic_rmw8_and_u, mem_arg);
533 } else if (mem.eql(u8, inst, "i32.atomic.rmw16.and_u")) {
534 const mem_arg = try parseMemArg(cg, inst, &word_it);
535 try cg.addAtomicMemArg(.i32_atomic_rmw16_and_u, mem_arg);
536 } else if (mem.eql(u8, inst, "i64.atomic.rmw8.and_u")) {
537 const mem_arg = try parseMemArg(cg, inst, &word_it);
538 try cg.addAtomicMemArg(.i64_atomic_rmw8_and_u, mem_arg);
539 } else if (mem.eql(u8, inst, "i64.atomic.rmw16.and_u")) {
540 const mem_arg = try parseMemArg(cg, inst, &word_it);
541 try cg.addAtomicMemArg(.i64_atomic_rmw16_and_u, mem_arg);
542 } else if (mem.eql(u8, inst, "i64.atomic.rmw32.and_u")) {
543 const mem_arg = try parseMemArg(cg, inst, &word_it);
544 try cg.addAtomicMemArg(.i64_atomic_rmw32_and_u, mem_arg);
545 } else if (mem.eql(u8, inst, "i32.atomic.rmw.or")) {
546 const mem_arg = try parseMemArg(cg, inst, &word_it);
547 try cg.addAtomicMemArg(.i32_atomic_rmw_or, mem_arg);
548 } else if (mem.eql(u8, inst, "i64.atomic.rmw.or")) {
549 const mem_arg = try parseMemArg(cg, inst, &word_it);
550 try cg.addAtomicMemArg(.i64_atomic_rmw_or, mem_arg);
551 } else if (mem.eql(u8, inst, "i32.atomic.rmw8.or_u")) {
552 const mem_arg = try parseMemArg(cg, inst, &word_it);
553 try cg.addAtomicMemArg(.i32_atomic_rmw8_or_u, mem_arg);
554 } else if (mem.eql(u8, inst, "i32.atomic.rmw16.or_u")) {
555 const mem_arg = try parseMemArg(cg, inst, &word_it);
556 try cg.addAtomicMemArg(.i32_atomic_rmw16_or_u, mem_arg);
557 } else if (mem.eql(u8, inst, "i64.atomic.rmw8.or_u")) {
558 const mem_arg = try parseMemArg(cg, inst, &word_it);
559 try cg.addAtomicMemArg(.i64_atomic_rmw8_or_u, mem_arg);
560 } else if (mem.eql(u8, inst, "i64.atomic.rmw16.or_u")) {
561 const mem_arg = try parseMemArg(cg, inst, &word_it);
562 try cg.addAtomicMemArg(.i64_atomic_rmw16_or_u, mem_arg);
563 } else if (mem.eql(u8, inst, "i64.atomic.rmw32.or_u")) {
564 const mem_arg = try parseMemArg(cg, inst, &word_it);
565 try cg.addAtomicMemArg(.i64_atomic_rmw32_or_u, mem_arg);
566 } else if (mem.eql(u8, inst, "i32.atomic.rmw.xor")) {
567 const mem_arg = try parseMemArg(cg, inst, &word_it);
568 try cg.addAtomicMemArg(.i32_atomic_rmw_xor, mem_arg);
569 } else if (mem.eql(u8, inst, "i64.atomic.rmw.xor")) {
570 const mem_arg = try parseMemArg(cg, inst, &word_it);
571 try cg.addAtomicMemArg(.i64_atomic_rmw_xor, mem_arg);
572 } else if (mem.eql(u8, inst, "i32.atomic.rmw8.xor_u")) {
573 const mem_arg = try parseMemArg(cg, inst, &word_it);
574 try cg.addAtomicMemArg(.i32_atomic_rmw8_xor_u, mem_arg);
575 } else if (mem.eql(u8, inst, "i32.atomic.rmw16.xor_u")) {
576 const mem_arg = try parseMemArg(cg, inst, &word_it);
577 try cg.addAtomicMemArg(.i32_atomic_rmw16_xor_u, mem_arg);
578 } else if (mem.eql(u8, inst, "i64.atomic.rmw8.xor_u")) {
579 const mem_arg = try parseMemArg(cg, inst, &word_it);
580 try cg.addAtomicMemArg(.i64_atomic_rmw8_xor_u, mem_arg);
581 } else if (mem.eql(u8, inst, "i64.atomic.rmw16.xor_u")) {
582 const mem_arg = try parseMemArg(cg, inst, &word_it);
583 try cg.addAtomicMemArg(.i64_atomic_rmw16_xor_u, mem_arg);
584 } else if (mem.eql(u8, inst, "i64.atomic.rmw32.xor_u")) {
585 const mem_arg = try parseMemArg(cg, inst, &word_it);
586 try cg.addAtomicMemArg(.i64_atomic_rmw32_xor_u, mem_arg);
587 } else if (mem.eql(u8, inst, "i32.atomic.rmw.xchg")) {
588 const mem_arg = try parseMemArg(cg, inst, &word_it);
589 try cg.addAtomicMemArg(.i32_atomic_rmw_xchg, mem_arg);
590 } else if (mem.eql(u8, inst, "i64.atomic.rmw.xchg")) {
591 const mem_arg = try parseMemArg(cg, inst, &word_it);
592 try cg.addAtomicMemArg(.i64_atomic_rmw_xchg, mem_arg);
593 } else if (mem.eql(u8, inst, "i32.atomic.rmw8.xchg_u")) {
594 const mem_arg = try parseMemArg(cg, inst, &word_it);
595 try cg.addAtomicMemArg(.i32_atomic_rmw8_xchg_u, mem_arg);
596 } else if (mem.eql(u8, inst, "i32.atomic.rmw16.xchg_u")) {
597 const mem_arg = try parseMemArg(cg, inst, &word_it);
598 try cg.addAtomicMemArg(.i32_atomic_rmw16_xchg_u, mem_arg);
599 } else if (mem.eql(u8, inst, "i64.atomic.rmw8.xchg_u")) {
600 const mem_arg = try parseMemArg(cg, inst, &word_it);
601 try cg.addAtomicMemArg(.i64_atomic_rmw8_xchg_u, mem_arg);
602 } else if (mem.eql(u8, inst, "i64.atomic.rmw16.xchg_u")) {
603 const mem_arg = try parseMemArg(cg, inst, &word_it);
604 try cg.addAtomicMemArg(.i64_atomic_rmw16_xchg_u, mem_arg);
605 } else if (mem.eql(u8, inst, "i64.atomic.rmw32.xchg_u")) {
606 const mem_arg = try parseMemArg(cg, inst, &word_it);
607 try cg.addAtomicMemArg(.i64_atomic_rmw32_xchg_u, mem_arg);
608 } else if (mem.eql(u8, inst, "i32.atomic.rmw.cmpxchg")) {
609 const mem_arg = try parseMemArg(cg, inst, &word_it);
610 try cg.addAtomicMemArg(.i32_atomic_rmw_cmpxchg, mem_arg);
611 } else if (mem.eql(u8, inst, "i64.atomic.rmw.cmpxchg")) {
612 const mem_arg = try parseMemArg(cg, inst, &word_it);
613 try cg.addAtomicMemArg(.i64_atomic_rmw_cmpxchg, mem_arg);
614 } else if (mem.eql(u8, inst, "i32.atomic.rmw8.cmpxchg_u")) {
615 const mem_arg = try parseMemArg(cg, inst, &word_it);
616 try cg.addAtomicMemArg(.i32_atomic_rmw8_cmpxchg_u, mem_arg);
617 } else if (mem.eql(u8, inst, "i32.atomic.rmw16.cmpxchg_u")) {
618 const mem_arg = try parseMemArg(cg, inst, &word_it);
619 try cg.addAtomicMemArg(.i32_atomic_rmw16_cmpxchg_u, mem_arg);
620 } else if (mem.eql(u8, inst, "i64.atomic.rmw8.cmpxchg_u")) {
621 const mem_arg = try parseMemArg(cg, inst, &word_it);
622 try cg.addAtomicMemArg(.i64_atomic_rmw8_cmpxchg_u, mem_arg);
623 } else if (mem.eql(u8, inst, "i64.atomic.rmw16.cmpxchg_u")) {
624 const mem_arg = try parseMemArg(cg, inst, &word_it);
625 try cg.addAtomicMemArg(.i64_atomic_rmw16_cmpxchg_u, mem_arg);
626 } else if (mem.eql(u8, inst, "i64.atomic.rmw32.cmpxchg_u")) {
627 const mem_arg = try parseMemArg(cg, inst, &word_it);
628 try cg.addAtomicMemArg(.i64_atomic_rmw32_cmpxchg_u, mem_arg);
629 } else {
630 if (mem.startsWith(u8, inst, "#")) continue :next_line;
631 if (mem.startsWith(u8, inst, "//")) continue :next_line;
632
633 return cg.fail("Malformed assembly, unknown instruction \"{s}\"", .{inst});
634 }
635
636 const remaining = word_it.next() orelse continue :next_line;
637 if (mem.startsWith(u8, inst, "#")) continue :next_line;
638 if (mem.startsWith(u8, inst, "//")) continue :next_line;
639 return cg.fail("Malformed assembly, unknown string after instruction \"{s}\"", .{remaining});
640 }
641}
642
643fn parseLocalArg(
644 cg: *CodeGen,
645 inst: []const u8,
646 word_it: *mem.TokenIterator(u8, .any),
647 local_map: *const LocalMap,
648) !u32 {
649 const arg = word_it.next() orelse {
650 return cg.fail("Malformed assembly, argument not found for \"{s}\"", .{inst});
651 };
652 if (arg.len < 3 or arg[0] != '%' or arg[1] != '[' or arg[arg.len - 1] != ']') {
653 const local = std.fmt.parseInt(u32, arg, 0) catch {
654 return cg.fail("Malformed assembly, malformed argument \"{s}\"", .{arg});
655 };
656 return local;
657 }
658 const name = arg[2 .. arg.len - 1];
659 return local_map.get(name) orelse {
660 return cg.fail("Malformed assembly, unknown name \"{s}\"", .{name});
661 };
662}
663
664fn parseLabel(cg: *CodeGen, inst: []const u8, word_it: *mem.TokenIterator(u8, .any)) !u32 {
665 const label_str = word_it.next() orelse {
666 return cg.fail("Malformed assembly, label not found for \"{s}\"", .{inst});
667 };
668 const label = std.fmt.parseInt(u32, label_str, 0) catch {
669 return cg.fail("Malformed assembly, malformed label \"{s}\"", .{label_str});
670 };
671 return label;
672}
673
674fn parseMemArg(cg: *CodeGen, inst: []const u8, word_it: *mem.TokenIterator(u8, .any)) !Mir.MemArg {
675 const mem_arg_str = word_it.next() orelse {
676 return cg.fail("Malformed assembly, memory argument not found for \"{s}\"", .{inst});
677 };
678 if (mem.cut(u8, mem_arg_str, ":p2align=")) |oa| {
679 const offset_str = oa[0];
680 const alignment_str = oa[1];
681
682 const offset = std.fmt.parseInt(i32, offset_str, 0) catch {
683 return cg.fail("Malformed assembly, malformed offset \"{s}\"", .{offset_str});
684 };
685 const alignment_p2 = std.fmt.parseInt(u5, alignment_str, 0) catch {
686 return cg.fail("Malformed assembly, malformed offset \"{s}\"", .{alignment_str});
687 };
688
689 return .{ .offset = @bitCast(offset), .alignment = @as(u32, 1) << alignment_p2 };
690 } else {
691 const offset_str = mem_arg_str;
692 const offset = std.fmt.parseInt(i32, offset_str, 0) catch {
693 return cg.fail("Malformed assembly, malformed offset \"{s}\"", .{offset_str});
694 };
695 return .{ .offset = @bitCast(offset), .alignment = 1 };
696 }
697}
698
699fn parseInt(comptime T: type, cg: *CodeGen, inst: []const u8, word_it: *mem.TokenIterator(u8, .any)) !T {
700 const value_str = word_it.next() orelse {
701 return cg.fail("Malformed assembly, value not found for \"{s}\"", .{inst});
702 };
703 const value = std.fmt.parseInt(T, value_str, 0) catch {
704 return cg.fail("Malformed assembly, malformed integer value \"{s}\"", .{value_str});
705 };
706 return value;
707}
708
709fn parseFloat(comptime T: type, cg: *CodeGen, inst: []const u8, word_it: *mem.TokenIterator(u8, .any)) !T {
710 const value_str = word_it.next() orelse {
711 return cg.fail("Malformed assembly, value not found for \"{s}\"", .{inst});
712 };
713 const value = std.fmt.parseFloat(T, value_str) catch {
714 return cg.fail("Malformed assembly, malformed float value \"{s}\"", .{value_str});
715 };
716 return value;
717}
src/dev.zig+2-1
...@@ -197,8 +197,9 @@ pub const Env = enum {...@@ -197,8 +197,9 @@ pub const Env = enum {
197 else => Env.sema.supports(feature),197 else => Env.sema.supports(feature),
198 },198 },
199 .wasm => switch (feature) {199 .wasm => switch (feature) {
200 .stdio_listen,
201 .incremental,200 .incremental,
201 .legalize,
202 .stdio_listen,
202 .wasm_backend,203 .wasm_backend,
203 .wasm_linker,204 .wasm_linker,
204 => true,205 => true,
test/behavior/wasm.zig+156-2
...@@ -3,10 +3,164 @@ const expect = std.testing.expect;...@@ -3,10 +3,164 @@ const expect = std.testing.expect;
3const builtin = @import("builtin");3const builtin = @import("builtin");
44
5test "memory size and grow" {5test "memory size and grow" {
6 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
7
8 var prev = @wasmMemorySize(0);6 var prev = @wasmMemorySize(0);
9 _ = &prev;7 _ = &prev;
10 try expect(prev == @wasmMemoryGrow(0, 1));8 try expect(prev == @wasmMemoryGrow(0, 1));
11 try expect(prev + 1 == @wasmMemorySize(0));9 try expect(prev + 1 == @wasmMemorySize(0));
12}10}
11
12test "asm .i32.add" {
13 const a: u32 = std.math.maxInt(u32);
14 const b: u32 = 3;
15 const result = asm (
16 \\ local.get %[a]
17 \\ local.get %[b]
18 \\ i32.add
19 \\ local.set %[ret]
20 : [ret] "=r" (-> u32),
21 : [a] "r" (a),
22 [b] "r" (b),
23 );
24
25 try expect(result == 2);
26}
27
28test "asm .i64.clz" {
29 const a: u64 = 1;
30 const result = asm (
31 \\ local.get %[a]
32 \\ i64.clz
33 \\ local.set %[ret]
34 : [ret] "=r" (-> u64),
35 : [a] "r" (a),
36 );
37
38 try expect(result == 63);
39}
40
41test "asm .i32.const" {
42 const result = asm (
43 \\ i32.const 12
44 \\ local.set %[ret]
45 : [ret] "=r" (-> u32),
46 );
47
48 try expect(result == 12);
49}
50
51test "asm .i64.const" {
52 const result = asm (
53 \\ i64.const 42
54 \\ local.set %[ret]
55 : [ret] "=r" (-> u64),
56 );
57
58 try expect(result == 42);
59}
60
61test "asm .f32.const" {
62 const result = asm (
63 \\ f32.const 1.5
64 \\ local.set %[ret]
65 : [ret] "=r" (-> f32),
66 );
67
68 try expect(result == 1.5);
69}
70
71test "asm .f64.const" {
72 const result = asm (
73 \\ f64.const 2.25
74 \\ local.set %[ret]
75 : [ret] "=r" (-> f64),
76 );
77
78 try expect(result == 2.25);
79}
80
81test "asm .local.get" {
82 const a: u32 = 77;
83 const result = asm (
84 \\ local.get %[a]
85 \\ local.set %[ret]
86 : [ret] "=r" (-> u32),
87 : [a] "r" (a),
88 );
89
90 try expect(result == 77);
91}
92
93test "asm .local.set" {
94 const result = asm (
95 \\ i32.const 55
96 \\ local.set %[ret]
97 : [ret] "=r" (-> u32),
98 );
99
100 try expect(result == 55);
101}
102
103test "asm .local.tee" {
104 const a: u32 = 3;
105 const result = asm (
106 \\ local.get %[a]
107 \\ local.tee %[ret]
108 \\ drop
109 : [ret] "=r" (-> u32),
110 : [a] "r" (a),
111 );
112
113 try expect(result == 3);
114}
115
116test "asm .memory.copy" {
117 var src: [8]u8 = .{ 1, 2, 3, 4, 5, 6, 7, 8 };
118 var dst: [8]u8 = .{ 0, 0, 0, 0, 0, 0, 0, 0 };
119
120 asm volatile (
121 \\ local.get %[dst]
122 \\ local.get %[src]
123 \\ i32.const 8
124 \\ memory.copy 0, 0
125 :
126 : [dst] "r" (@intFromPtr(&dst)),
127 [src] "r" (@intFromPtr(&src)),
128 );
129
130 try std.testing.expectEqualSlices(u8, &src, &dst);
131}
132
133test "asm .memory.fill" {
134 var buf: [8]u8 = .{ 0, 0, 0, 0, 0, 0, 0, 0 };
135
136 asm volatile (
137 \\ local.get %[dst]
138 \\ i32.const 2
139 \\ i32.const 8
140 \\ memory.fill 0
141 :
142 : [dst] "r" (@intFromPtr(&buf)),
143 );
144
145 try std.testing.expectEqualSlices(u8, &.{ 2, 2, 2, 2, 2, 2, 2, 2 }, &buf);
146}
147
148test "asm .i64.load and .i64.store" {
149 var slot: u64 = 0;
150 const value: u64 = 0x4444;
151
152 const result = asm (
153 \\ local.get %[ptr]
154 \\ local.get %[value]
155 \\ i64.store 0:p2align=0
156 \\ local.get %[ptr]
157 \\ i64.load 0
158 \\ local.set %[ret]
159 : [ret] "=r" (-> u64),
160 : [ptr] "r" (@intFromPtr(&slot)),
161 [value] "r" (value),
162 );
163
164 try expect(result == value);
165 try expect(slot == value);
166}