1const Emit = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const Allocator = std.mem.Allocator;
6const ArrayList = std.ArrayList;
7
8const Wasm = link.File.Wasm;
9const Mir = @import("Mir.zig");
10const link = @import("../../link.zig");
11const Zcu = @import("../../Zcu.zig");
12const InternPool = @import("../../InternPool.zig");
13const codegen = @import("../../codegen.zig");
14
15mir: Mir,
16wasm: *Wasm,
17/// The binary representation that will be emitted by this module.
18code: *ArrayList(u8),
19
20pub const Error = error{
21 OutOfMemory,
22};
23
24pub fn lower(emit: *Emit) Error!void {
25 const mir = &emit.mir;
26 const code = emit.code;
27 const wasm = emit.wasm;
28 const comp = wasm.base.comp;
29 const gpa = comp.gpa;
30 const is_obj = comp.config.output_mode == .Obj;
31 const target = &comp.root_mod.resolved_target.result;
32 const is_wasm32 = target.cpu.arch == .wasm32;
33
34 // Write the locals in the prologue of the function body.
35 try code.ensureUnusedCapacity(gpa, 5 + mir.locals.len * 6 + 38);
36
37 writeUleb128(code, @as(u32, @intCast(mir.locals.len)));
38
39 for (mir.locals) |local| {
40 writeUleb128(code, @as(u32, 1));
41 code.appendAssumeCapacity(@backingInt(local));
42 }
43
44 // Stack management section of function prologue.
45 const stack_alignment = mir.prologue.flags.stack_alignment;
46 if (stack_alignment.toByteUnits()) |align_bytes| {
47 // load stack pointer
48 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.global_get));
49 try appendStackPointerGlobalIndex(wasm, code, is_obj);
50 // store stack pointer so we can restore it when we return from the function
51 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.local_tee));
52 writeUleb128(code, mir.prologue.sp_local);
53 // get the total stack size
54 const aligned_stack: i32 = @intCast(stack_alignment.forward(mir.prologue.stack_size));
55 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.i32_const));
56 writeSleb128(code, aligned_stack);
57 // subtract it from the current stack pointer
58 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.i32_sub));
59 // Get negative stack alignment
60 const neg_stack_align = @as(i32, @intCast(align_bytes)) * -1;
61 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.i32_const));
62 writeSleb128(code, neg_stack_align);
63 // Bitwise-and the value to get the new stack pointer to ensure the
64 // pointers are aligned with the abi alignment.
65 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.i32_and));
66 // The bottom will be used to calculate all stack pointer offsets.
67 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.local_tee));
68 writeUleb128(code, mir.prologue.bottom_stack_local);
69 // Store the current stack pointer value into the global stack pointer so other function calls will
70 // start from this value instead and not overwrite the current stack.
71 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.global_set));
72 try appendStackPointerGlobalIndex(wasm, code, is_obj);
73 }
74
75 const tags = mir.instructions.items(.tag);
76 const datas = mir.instructions.items(.data);
77 var inst: u32 = 0;
78
79 loop: switch (tags[inst]) {
80 .dbg_epilogue_begin => {
81 return;
82 },
83 .block, .loop => {
84 const block_type = datas[inst].block_type;
85 try code.ensureUnusedCapacity(gpa, 2);
86 code.appendAssumeCapacity(@backingInt(tags[inst]));
87 code.appendAssumeCapacity(@backingInt(block_type));
88
89 inst += 1;
90 continue :loop tags[inst];
91 },
92 .uav_ref => {
93 if (is_obj) {
94 try uavRefObj(wasm, code, datas[inst].ip_index, 0, is_wasm32);
95 } else {
96 try uavRefExe(wasm, code, datas[inst].ip_index, 0, is_wasm32);
97 }
98 inst += 1;
99 continue :loop tags[inst];
100 },
101 .uav_ref_off => {
102 const extra = mir.extraData(Mir.UavRefOff, datas[inst].payload).data;
103 if (is_obj) {
104 try uavRefObj(wasm, code, extra.value, extra.offset, is_wasm32);
105 } else {
106 try uavRefExe(wasm, code, extra.value, extra.offset, is_wasm32);
107 }
108 inst += 1;
109 continue :loop tags[inst];
110 },
111 .nav_ref => {
112 try navRefOff(wasm, code, .{ .nav_index = datas[inst].nav_index, .offset = 0 }, is_wasm32);
113 inst += 1;
114 continue :loop tags[inst];
115 },
116 .nav_ref_off => {
117 try navRefOff(wasm, code, mir.extraData(Mir.NavRefOff, datas[inst].payload).data, is_wasm32);
118 inst += 1;
119 continue :loop tags[inst];
120 },
121 .func_ref => {
122 try code.ensureUnusedCapacity(gpa, 11);
123 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;
124 code.appendAssumeCapacity(@backingInt(opcode));
125 if (is_obj) {
126 try wasm.zcu_relocations.append(gpa, .{
127 .offset = @intCast(code.items.len),
128 .pointee = .{ .function_nav = datas[inst].nav_index },
129 .tag = if (is_wasm32) .table_index_sleb else .table_index_sleb64,
130 .addend = 0,
131 });
132 appendSlebRelocPlaceholder(code, is_wasm32);
133 } else {
134 const function_index = Wasm.OutputFunctionIndex.fromIpNav(wasm, datas[inst].nav_index);
135 const table_index = wasm.flush_buffer.indirect_function_table.getIndex(function_index).? + 1;
136 writeSleb128(code, table_index);
137 }
138 inst += 1;
139 continue :loop tags[inst];
140 },
141 .dbg_line => {
142 inst += 1;
143 continue :loop tags[inst];
144 },
145 .errors_len => {
146 try code.ensureUnusedCapacity(gpa, 6);
147 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.i32_const));
148 // MIR is lowered during flush, so there is indeed only one thread at this time.
149 const errors_len = 1 + comp.zcu.?.intern_pool.global_error_set.getNamesFromMainThread().len;
150 writeSleb128(code, errors_len);
151
152 inst += 1;
153 continue :loop tags[inst];
154 },
155 .error_name_table_ref => {
156 try code.ensureUnusedCapacity(gpa, 11);
157 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;
158 code.appendAssumeCapacity(@backingInt(opcode));
159 if (is_obj) {
160 try wasm.zcu_relocations.append(gpa, .{
161 .offset = @intCast(code.items.len),
162 .pointee = .{ .data_resolution = .__zig_error_name_table },
163 .tag = if (is_wasm32) .memory_addr_sleb else .memory_addr_sleb64,
164 .addend = 0,
165 });
166 appendSlebRelocPlaceholder(code, is_wasm32);
167
168 inst += 1;
169 continue :loop tags[inst];
170 } else {
171 const addr: u32 = wasm.errorNameTableAddr();
172 writeSleb128(code, addr);
173
174 inst += 1;
175 continue :loop tags[inst];
176 }
177 },
178 .br_if, .br, .memory_grow, .memory_size => {
179 try code.ensureUnusedCapacity(gpa, 11);
180 code.appendAssumeCapacity(@backingInt(tags[inst]));
181 writeUleb128(code, datas[inst].label);
182
183 inst += 1;
184 continue :loop tags[inst];
185 },
186
187 .local_get, .local_set, .local_tee => {
188 try code.ensureUnusedCapacity(gpa, 11);
189 code.appendAssumeCapacity(@backingInt(tags[inst]));
190 writeUleb128(code, datas[inst].local);
191
192 inst += 1;
193 continue :loop tags[inst];
194 },
195
196 .br_table => {
197 const extra_index = datas[inst].payload;
198 const extra = mir.extraData(Mir.JumpTable, extra_index);
199 const labels = mir.extra[extra.end..][0..extra.data.length];
200 try code.ensureUnusedCapacity(gpa, 11 + 10 * labels.len);
201 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.br_table));
202 // -1 because default label is not part of length/depth.
203 writeUleb128(code, extra.data.length - 1);
204 for (labels) |label| writeUleb128(code, label);
205
206 inst += 1;
207 continue :loop tags[inst];
208 },
209
210 .call_nav => {
211 try code.ensureUnusedCapacity(gpa, 6);
212 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.call));
213 if (is_obj) {
214 try wasm.zcu_relocations.append(gpa, .{
215 .offset = @intCast(code.items.len),
216 .pointee = .{ .function_nav = datas[inst].nav_index },
217 .tag = .function_index_leb,
218 .addend = 0,
219 });
220 appendUlebRelocPlaceholder(code);
221 } else {
222 appendOutputFunctionIndex(code, .fromIpNav(wasm, datas[inst].nav_index));
223 }
224
225 inst += 1;
226 continue :loop tags[inst];
227 },
228
229 .call_indirect => {
230 try code.ensureUnusedCapacity(gpa, 11);
231 const fn_info = comp.zcu.?.typeToFunc(.fromInterned(datas[inst].ip_index)).?;
232 const func_ty_index = wasm.getExistingFunctionType(
233 fn_info.cc,
234 fn_info.param_types.get(&comp.zcu.?.intern_pool),
235 .fromInterned(fn_info.return_type),
236 fn_info.is_var_args,
237 target,
238 ).?;
239 if (is_obj) {
240 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.call_indirect));
241 try wasm.zcu_relocations.append(gpa, .{
242 .offset = @intCast(code.items.len),
243 .pointee = .{ .type_index = func_ty_index },
244 .tag = .type_index_leb,
245 .addend = 0,
246 });
247 appendUlebRelocPlaceholder(code);
248 } else {
249 const index: Wasm.Flush.FuncTypeIndex = @fromBackingInt(@intCast(wasm.flush_buffer.func_types.getIndex(func_ty_index) orelse {
250 // In this case we tried to call a function pointer for
251 // which the type signature does not match any function
252 // body or function import in the entire wasm executable.
253 //
254 // Since there is no way to create a reference to a
255 // function without it being in the function table or
256 // import table, this instruction is unreachable.
257 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.@"unreachable"));
258 inst += 1;
259 continue :loop tags[inst];
260 }));
261 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.call_indirect));
262 writeUleb128(code, @backingInt(index));
263 }
264 writeUleb128(code, @as(u32, 0)); // table index
265
266 inst += 1;
267 continue :loop tags[inst];
268 },
269
270 .call_tag_index => {
271 try code.ensureUnusedCapacity(gpa, 6);
272 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.call));
273 if (is_obj) {
274 try wasm.zcu_relocations.append(gpa, .{
275 .offset = @intCast(code.items.len),
276 .pointee = .{ .tag_function = datas[inst].ip_index },
277 .tag = .function_index_leb,
278 .addend = 0,
279 });
280 appendUlebRelocPlaceholder(code);
281 } else {
282 appendOutputFunctionIndex(code, .fromTagIndexType(wasm, datas[inst].ip_index));
283 }
284
285 inst += 1;
286 continue :loop tags[inst];
287 },
288
289 .enum_tag_name_table_ref => {
290 try code.ensureUnusedCapacity(gpa, 11);
291 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;
292 code.appendAssumeCapacity(@backingInt(opcode));
293 if (is_obj) {
294 try wasm.zcu_relocations.append(gpa, .{
295 .offset = @intCast(code.items.len),
296 .pointee = .{ .data_resolution = .__zig_tag_name_table },
297 .tag = if (is_wasm32) .memory_addr_sleb else .memory_addr_sleb64,
298 .addend = @intCast(wasm.tagIndexTableOffset(datas[inst].ip_index)),
299 });
300 appendSlebRelocPlaceholder(code, is_wasm32);
301 } else {
302 const addr: u32 = wasm.tagIndexTableAddr(datas[inst].ip_index);
303 writeSleb128(code, addr);
304 }
305
306 inst += 1;
307 continue :loop tags[inst];
308 },
309
310 .call_intrinsic => {
311 // Although this currently uses `wasm.internString`, note that it
312 // *could* be changed to directly index into a preloaded strings
313 // table initialized based on the `Mir.Intrinsic` enum.
314 const symbol_name = try wasm.internString(@tagName(datas[inst].intrinsic));
315
316 try code.ensureUnusedCapacity(gpa, 6);
317 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.call));
318 if (is_obj) {
319 try wasm.zcu_relocations.append(gpa, .{
320 .offset = @intCast(code.items.len),
321 .pointee = .{ .function_name = symbol_name },
322 .tag = .function_index_leb,
323 .addend = 0,
324 });
325 appendUlebRelocPlaceholder(code);
326 } else {
327 appendOutputFunctionIndex(code, .fromSymbolName(wasm, symbol_name));
328 }
329
330 inst += 1;
331 continue :loop tags[inst];
332 },
333
334 .global_set_sp => {
335 try code.ensureUnusedCapacity(gpa, 6);
336 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.global_set));
337 try appendStackPointerGlobalIndex(wasm, code, is_obj);
338
339 inst += 1;
340 continue :loop tags[inst];
341 },
342
343 .f32_const => {
344 try code.ensureUnusedCapacity(gpa, 5);
345 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.f32_const));
346 std.mem.writeInt(u32, code.addManyAsArrayAssumeCapacity(4), @bitCast(datas[inst].float32), .little);
347
348 inst += 1;
349 continue :loop tags[inst];
350 },
351
352 .f64_const => {
353 try code.ensureUnusedCapacity(gpa, 9);
354 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.f64_const));
355 const float64 = mir.extraData(Mir.Float64, datas[inst].payload).data;
356 std.mem.writeInt(u64, code.addManyAsArrayAssumeCapacity(8), float64.toInt(), .little);
357
358 inst += 1;
359 continue :loop tags[inst];
360 },
361 .i32_const => {
362 try code.ensureUnusedCapacity(gpa, 6);
363 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.i32_const));
364 writeSleb128(code, datas[inst].imm32);
365
366 inst += 1;
367 continue :loop tags[inst];
368 },
369 .i64_const => {
370 try code.ensureUnusedCapacity(gpa, 11);
371 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.i64_const));
372 const int64: i64 = @bitCast(mir.extraData(Mir.Imm64, datas[inst].payload).data.toInt());
373 writeSleb128(code, int64);
374
375 inst += 1;
376 continue :loop tags[inst];
377 },
378
379 .i32_load,
380 .i64_load,
381 .f32_load,
382 .f64_load,
383 .i32_load8_s,
384 .i32_load8_u,
385 .i32_load16_s,
386 .i32_load16_u,
387 .i64_load8_s,
388 .i64_load8_u,
389 .i64_load16_s,
390 .i64_load16_u,
391 .i64_load32_s,
392 .i64_load32_u,
393 .i32_store,
394 .i64_store,
395 .f32_store,
396 .f64_store,
397 .i32_store8,
398 .i32_store16,
399 .i64_store8,
400 .i64_store16,
401 .i64_store32,
402 => {
403 try code.ensureUnusedCapacity(gpa, 1 + 20);
404 code.appendAssumeCapacity(@backingInt(tags[inst]));
405 encodeMemArg(code, mir.extraData(Mir.MemArg, datas[inst].payload).data);
406 inst += 1;
407 continue :loop tags[inst];
408 },
409
410 .end,
411 .@"return",
412 .@"unreachable",
413 .drop,
414 .select,
415 .i32_eqz,
416 .i32_eq,
417 .i32_ne,
418 .i32_lt_s,
419 .i32_lt_u,
420 .i32_gt_s,
421 .i32_gt_u,
422 .i32_le_s,
423 .i32_le_u,
424 .i32_ge_s,
425 .i32_ge_u,
426 .i64_eqz,
427 .i64_eq,
428 .i64_ne,
429 .i64_lt_s,
430 .i64_lt_u,
431 .i64_gt_s,
432 .i64_gt_u,
433 .i64_le_s,
434 .i64_le_u,
435 .i64_ge_s,
436 .i64_ge_u,
437 .f32_eq,
438 .f32_ne,
439 .f32_lt,
440 .f32_gt,
441 .f32_le,
442 .f32_ge,
443 .f64_eq,
444 .f64_ne,
445 .f64_lt,
446 .f64_gt,
447 .f64_le,
448 .f64_ge,
449 .i32_add,
450 .i32_sub,
451 .i32_mul,
452 .i32_div_s,
453 .i32_div_u,
454 .i32_and,
455 .i32_or,
456 .i32_xor,
457 .i32_shl,
458 .i32_shr_s,
459 .i32_shr_u,
460 .i64_add,
461 .i64_sub,
462 .i64_mul,
463 .i64_div_s,
464 .i64_div_u,
465 .i64_and,
466 .i64_or,
467 .i64_xor,
468 .i64_shl,
469 .i64_shr_s,
470 .i64_shr_u,
471 .f32_abs,
472 .f32_neg,
473 .f32_ceil,
474 .f32_floor,
475 .f32_trunc,
476 .f32_nearest,
477 .f32_sqrt,
478 .f32_add,
479 .f32_sub,
480 .f32_mul,
481 .f32_div,
482 .f32_min,
483 .f32_max,
484 .f32_copysign,
485 .f64_abs,
486 .f64_neg,
487 .f64_ceil,
488 .f64_floor,
489 .f64_trunc,
490 .f64_nearest,
491 .f64_sqrt,
492 .f64_add,
493 .f64_sub,
494 .f64_mul,
495 .f64_div,
496 .f64_min,
497 .f64_max,
498 .f64_copysign,
499 .i32_wrap_i64,
500 .i64_extend_i32_s,
501 .i64_extend_i32_u,
502 .i32_extend8_s,
503 .i32_extend16_s,
504 .i64_extend8_s,
505 .i64_extend16_s,
506 .i64_extend32_s,
507 .f32_demote_f64,
508 .f64_promote_f32,
509 .i32_reinterpret_f32,
510 .i64_reinterpret_f64,
511 .f32_reinterpret_i32,
512 .f64_reinterpret_i64,
513 .i32_trunc_f32_s,
514 .i32_trunc_f32_u,
515 .i32_trunc_f64_s,
516 .i32_trunc_f64_u,
517 .i64_trunc_f32_s,
518 .i64_trunc_f32_u,
519 .i64_trunc_f64_s,
520 .i64_trunc_f64_u,
521 .f32_convert_i32_s,
522 .f32_convert_i32_u,
523 .f32_convert_i64_s,
524 .f32_convert_i64_u,
525 .f64_convert_i32_s,
526 .f64_convert_i32_u,
527 .f64_convert_i64_s,
528 .f64_convert_i64_u,
529 .i32_rem_s,
530 .i32_rem_u,
531 .i64_rem_s,
532 .i64_rem_u,
533 .i32_popcnt,
534 .i64_popcnt,
535 .i32_clz,
536 .i32_ctz,
537 .i64_clz,
538 .i64_ctz,
539 => {
540 try code.append(gpa, @backingInt(tags[inst]));
541 inst += 1;
542 continue :loop tags[inst];
543 },
544
545 .misc_prefix => {
546 try code.ensureUnusedCapacity(gpa, 6 + 6);
547 const extra_index = datas[inst].payload;
548 const opcode = mir.extra[extra_index];
549 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.misc_prefix));
550 writeUleb128(code, opcode);
551 switch (@as(std.wasm.MiscOpcode, @fromBackingInt(@intCast(opcode)))) {
552 // bulk-memory opcodes
553 .data_drop => {
554 const segment = mir.extra[extra_index + 1];
555 writeUleb128(code, segment);
556
557 inst += 1;
558 continue :loop tags[inst];
559 },
560 .memory_init => {
561 const segment = mir.extra[extra_index + 1];
562 writeUleb128(code, segment);
563 writeUleb128(code, @as(u32, 0)); // memory index
564
565 inst += 1;
566 continue :loop tags[inst];
567 },
568 .memory_fill => {
569 writeUleb128(code, @as(u32, 0)); // memory index
570
571 inst += 1;
572 continue :loop tags[inst];
573 },
574 .memory_copy => {
575 writeUleb128(code, @as(u32, 0)); // dst memory index
576 writeUleb128(code, @as(u32, 0)); // src memory index
577
578 inst += 1;
579 continue :loop tags[inst];
580 },
581
582 // nontrapping-float-to-int-conversion opcodes
583 .i32_trunc_sat_f32_s,
584 .i32_trunc_sat_f32_u,
585 .i32_trunc_sat_f64_s,
586 .i32_trunc_sat_f64_u,
587 .i64_trunc_sat_f32_s,
588 .i64_trunc_sat_f32_u,
589 .i64_trunc_sat_f64_s,
590 .i64_trunc_sat_f64_u,
591 => {
592 inst += 1;
593 continue :loop tags[inst];
594 },
595
596 .table_init => @panic("TODO"),
597 .elem_drop => @panic("TODO"),
598 .table_copy => @panic("TODO"),
599 .table_grow => @panic("TODO"),
600 .table_size => @panic("TODO"),
601 .table_fill => @panic("TODO"),
602
603 _ => unreachable,
604 }
605 comptime unreachable;
606 },
607 .simd_prefix => {
608 try code.ensureUnusedCapacity(gpa, 6 + 20);
609 const extra_index = datas[inst].payload;
610 const opcode = mir.extra[extra_index];
611 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.simd_prefix));
612 writeUleb128(code, opcode);
613 switch (@as(std.wasm.SimdOpcode, @fromBackingInt(@intCast(opcode)))) {
614 .v128_store,
615 .v128_load,
616 .v128_load8_splat,
617 .v128_load16_splat,
618 .v128_load32_splat,
619 .v128_load64_splat,
620 => {
621 encodeMemArg(code, mir.extraData(Mir.MemArg, extra_index + 1).data);
622 inst += 1;
623 continue :loop tags[inst];
624 },
625 .v128_const, .i8x16_shuffle => {
626 code.appendSliceAssumeCapacity(std.mem.asBytes(mir.extra[extra_index + 1 ..][0..4]));
627 inst += 1;
628 continue :loop tags[inst];
629 },
630 .i8x16_extract_lane_s,
631 .i8x16_extract_lane_u,
632 .i8x16_replace_lane,
633 .i16x8_extract_lane_s,
634 .i16x8_extract_lane_u,
635 .i16x8_replace_lane,
636 .i32x4_extract_lane,
637 .i32x4_replace_lane,
638 .i64x2_extract_lane,
639 .i64x2_replace_lane,
640 .f32x4_extract_lane,
641 .f32x4_replace_lane,
642 .f64x2_extract_lane,
643 .f64x2_replace_lane,
644 => {
645 code.appendAssumeCapacity(@intCast(mir.extra[extra_index + 1]));
646 inst += 1;
647 continue :loop tags[inst];
648 },
649 .i8x16_splat,
650 .i16x8_splat,
651 .i32x4_splat,
652 .i64x2_splat,
653 .f32x4_splat,
654 .f64x2_splat,
655 => {
656 inst += 1;
657 continue :loop tags[inst];
658 },
659
660 .v128_load8x8_s => @panic("TODO"),
661 .v128_load8x8_u => @panic("TODO"),
662 .v128_load16x4_s => @panic("TODO"),
663 .v128_load16x4_u => @panic("TODO"),
664 .v128_load32x2_s => @panic("TODO"),
665 .v128_load32x2_u => @panic("TODO"),
666 .i8x16_swizzle => @panic("TODO"),
667 .i8x16_eq => @panic("TODO"),
668 .i16x8_eq => @panic("TODO"),
669 .i32x4_eq => @panic("TODO"),
670 .i8x16_ne => @panic("TODO"),
671 .i16x8_ne => @panic("TODO"),
672 .i32x4_ne => @panic("TODO"),
673 .i8x16_lt_s => @panic("TODO"),
674 .i16x8_lt_s => @panic("TODO"),
675 .i32x4_lt_s => @panic("TODO"),
676 .i8x16_lt_u => @panic("TODO"),
677 .i16x8_lt_u => @panic("TODO"),
678 .i32x4_lt_u => @panic("TODO"),
679 .i8x16_gt_s => @panic("TODO"),
680 .i16x8_gt_s => @panic("TODO"),
681 .i32x4_gt_s => @panic("TODO"),
682 .i8x16_gt_u => @panic("TODO"),
683 .i16x8_gt_u => @panic("TODO"),
684 .i32x4_gt_u => @panic("TODO"),
685 .i8x16_le_s => @panic("TODO"),
686 .i16x8_le_s => @panic("TODO"),
687 .i32x4_le_s => @panic("TODO"),
688 .i8x16_le_u => @panic("TODO"),
689 .i16x8_le_u => @panic("TODO"),
690 .i32x4_le_u => @panic("TODO"),
691 .i8x16_ge_s => @panic("TODO"),
692 .i16x8_ge_s => @panic("TODO"),
693 .i32x4_ge_s => @panic("TODO"),
694 .i8x16_ge_u => @panic("TODO"),
695 .i16x8_ge_u => @panic("TODO"),
696 .i32x4_ge_u => @panic("TODO"),
697 .f32x4_eq => @panic("TODO"),
698 .f64x2_eq => @panic("TODO"),
699 .f32x4_ne => @panic("TODO"),
700 .f64x2_ne => @panic("TODO"),
701 .f32x4_lt => @panic("TODO"),
702 .f64x2_lt => @panic("TODO"),
703 .f32x4_gt => @panic("TODO"),
704 .f64x2_gt => @panic("TODO"),
705 .f32x4_le => @panic("TODO"),
706 .f64x2_le => @panic("TODO"),
707 .f32x4_ge => @panic("TODO"),
708 .f64x2_ge => @panic("TODO"),
709 .v128_not => @panic("TODO"),
710 .v128_and => @panic("TODO"),
711 .v128_andnot => @panic("TODO"),
712 .v128_or => @panic("TODO"),
713 .v128_xor => @panic("TODO"),
714 .v128_bitselect => @panic("TODO"),
715 .v128_any_true => @panic("TODO"),
716 .v128_load8_lane => @panic("TODO"),
717 .v128_load16_lane => @panic("TODO"),
718 .v128_load32_lane => @panic("TODO"),
719 .v128_load64_lane => @panic("TODO"),
720 .v128_store8_lane => @panic("TODO"),
721 .v128_store16_lane => @panic("TODO"),
722 .v128_store32_lane => @panic("TODO"),
723 .v128_store64_lane => @panic("TODO"),
724 .v128_load32_zero => @panic("TODO"),
725 .v128_load64_zero => @panic("TODO"),
726 .f32x4_demote_f64x2_zero => @panic("TODO"),
727 .f64x2_promote_low_f32x4 => @panic("TODO"),
728 .i8x16_abs => @panic("TODO"),
729 .i16x8_abs => @panic("TODO"),
730 .i32x4_abs => @panic("TODO"),
731 .i64x2_abs => @panic("TODO"),
732 .i8x16_neg => @panic("TODO"),
733 .i16x8_neg => @panic("TODO"),
734 .i32x4_neg => @panic("TODO"),
735 .i64x2_neg => @panic("TODO"),
736 .i8x16_popcnt => @panic("TODO"),
737 .i16x8_q15mulr_sat_s => @panic("TODO"),
738 .i8x16_all_true => @panic("TODO"),
739 .i16x8_all_true => @panic("TODO"),
740 .i32x4_all_true => @panic("TODO"),
741 .i64x2_all_true => @panic("TODO"),
742 .i8x16_bitmask => @panic("TODO"),
743 .i16x8_bitmask => @panic("TODO"),
744 .i32x4_bitmask => @panic("TODO"),
745 .i64x2_bitmask => @panic("TODO"),
746 .i8x16_narrow_i16x8_s => @panic("TODO"),
747 .i16x8_narrow_i32x4_s => @panic("TODO"),
748 .i8x16_narrow_i16x8_u => @panic("TODO"),
749 .i16x8_narrow_i32x4_u => @panic("TODO"),
750 .f32x4_ceil => @panic("TODO"),
751 .i16x8_extend_low_i8x16_s => @panic("TODO"),
752 .i32x4_extend_low_i16x8_s => @panic("TODO"),
753 .i64x2_extend_low_i32x4_s => @panic("TODO"),
754 .f32x4_floor => @panic("TODO"),
755 .i16x8_extend_high_i8x16_s => @panic("TODO"),
756 .i32x4_extend_high_i16x8_s => @panic("TODO"),
757 .i64x2_extend_high_i32x4_s => @panic("TODO"),
758 .f32x4_trunc => @panic("TODO"),
759 .i16x8_extend_low_i8x16_u => @panic("TODO"),
760 .i32x4_extend_low_i16x8_u => @panic("TODO"),
761 .i64x2_extend_low_i32x4_u => @panic("TODO"),
762 .f32x4_nearest => @panic("TODO"),
763 .i16x8_extend_high_i8x16_u => @panic("TODO"),
764 .i32x4_extend_high_i16x8_u => @panic("TODO"),
765 .i64x2_extend_high_i32x4_u => @panic("TODO"),
766 .i8x16_shl => @panic("TODO"),
767 .i16x8_shl => @panic("TODO"),
768 .i32x4_shl => @panic("TODO"),
769 .i64x2_shl => @panic("TODO"),
770 .i8x16_shr_s => @panic("TODO"),
771 .i16x8_shr_s => @panic("TODO"),
772 .i32x4_shr_s => @panic("TODO"),
773 .i64x2_shr_s => @panic("TODO"),
774 .i8x16_shr_u => @panic("TODO"),
775 .i16x8_shr_u => @panic("TODO"),
776 .i32x4_shr_u => @panic("TODO"),
777 .i64x2_shr_u => @panic("TODO"),
778 .i8x16_add => @panic("TODO"),
779 .i16x8_add => @panic("TODO"),
780 .i32x4_add => @panic("TODO"),
781 .i64x2_add => @panic("TODO"),
782 .i8x16_add_sat_s => @panic("TODO"),
783 .i16x8_add_sat_s => @panic("TODO"),
784 .i8x16_add_sat_u => @panic("TODO"),
785 .i16x8_add_sat_u => @panic("TODO"),
786 .i8x16_sub => @panic("TODO"),
787 .i16x8_sub => @panic("TODO"),
788 .i32x4_sub => @panic("TODO"),
789 .i64x2_sub => @panic("TODO"),
790 .i8x16_sub_sat_s => @panic("TODO"),
791 .i16x8_sub_sat_s => @panic("TODO"),
792 .i8x16_sub_sat_u => @panic("TODO"),
793 .i16x8_sub_sat_u => @panic("TODO"),
794 .f64x2_ceil => @panic("TODO"),
795 .f64x2_nearest => @panic("TODO"),
796 .f64x2_floor => @panic("TODO"),
797 .i16x8_mul => @panic("TODO"),
798 .i32x4_mul => @panic("TODO"),
799 .i64x2_mul => @panic("TODO"),
800 .i8x16_min_s => @panic("TODO"),
801 .i16x8_min_s => @panic("TODO"),
802 .i32x4_min_s => @panic("TODO"),
803 .i64x2_eq => @panic("TODO"),
804 .i8x16_min_u => @panic("TODO"),
805 .i16x8_min_u => @panic("TODO"),
806 .i32x4_min_u => @panic("TODO"),
807 .i64x2_ne => @panic("TODO"),
808 .i8x16_max_s => @panic("TODO"),
809 .i16x8_max_s => @panic("TODO"),
810 .i32x4_max_s => @panic("TODO"),
811 .i64x2_lt_s => @panic("TODO"),
812 .i8x16_max_u => @panic("TODO"),
813 .i16x8_max_u => @panic("TODO"),
814 .i32x4_max_u => @panic("TODO"),
815 .i64x2_gt_s => @panic("TODO"),
816 .f64x2_trunc => @panic("TODO"),
817 .i32x4_dot_i16x8_s => @panic("TODO"),
818 .i64x2_le_s => @panic("TODO"),
819 .i8x16_avgr_u => @panic("TODO"),
820 .i16x8_avgr_u => @panic("TODO"),
821 .i64x2_ge_s => @panic("TODO"),
822 .i16x8_extadd_pairwise_i8x16_s => @panic("TODO"),
823 .i16x8_extmul_low_i8x16_s => @panic("TODO"),
824 .i32x4_extmul_low_i16x8_s => @panic("TODO"),
825 .i64x2_extmul_low_i32x4_s => @panic("TODO"),
826 .i16x8_extadd_pairwise_i8x16_u => @panic("TODO"),
827 .i16x8_extmul_high_i8x16_s => @panic("TODO"),
828 .i32x4_extmul_high_i16x8_s => @panic("TODO"),
829 .i64x2_extmul_high_i32x4_s => @panic("TODO"),
830 .i32x4_extadd_pairwise_i16x8_s => @panic("TODO"),
831 .i16x8_extmul_low_i8x16_u => @panic("TODO"),
832 .i32x4_extmul_low_i16x8_u => @panic("TODO"),
833 .i64x2_extmul_low_i32x4_u => @panic("TODO"),
834 .i32x4_extadd_pairwise_i16x8_u => @panic("TODO"),
835 .i16x8_extmul_high_i8x16_u => @panic("TODO"),
836 .i32x4_extmul_high_i16x8_u => @panic("TODO"),
837 .i64x2_extmul_high_i32x4_u => @panic("TODO"),
838 .f32x4_abs => @panic("TODO"),
839 .f64x2_abs => @panic("TODO"),
840 .f32x4_neg => @panic("TODO"),
841 .f64x2_neg => @panic("TODO"),
842 .f32x4_sqrt => @panic("TODO"),
843 .f64x2_sqrt => @panic("TODO"),
844 .f32x4_add => @panic("TODO"),
845 .f64x2_add => @panic("TODO"),
846 .f32x4_sub => @panic("TODO"),
847 .f64x2_sub => @panic("TODO"),
848 .f32x4_mul => @panic("TODO"),
849 .f64x2_mul => @panic("TODO"),
850 .f32x4_div => @panic("TODO"),
851 .f64x2_div => @panic("TODO"),
852 .f32x4_min => @panic("TODO"),
853 .f64x2_min => @panic("TODO"),
854 .f32x4_max => @panic("TODO"),
855 .f64x2_max => @panic("TODO"),
856 .f32x4_pmin => @panic("TODO"),
857 .f64x2_pmin => @panic("TODO"),
858 .f32x4_pmax => @panic("TODO"),
859 .f64x2_pmax => @panic("TODO"),
860 .i32x4_trunc_sat_f32x4_s => @panic("TODO"),
861 .i32x4_trunc_sat_f32x4_u => @panic("TODO"),
862 .f32x4_convert_i32x4_s => @panic("TODO"),
863 .f32x4_convert_i32x4_u => @panic("TODO"),
864 .i32x4_trunc_sat_f64x2_s_zero => @panic("TODO"),
865 .i32x4_trunc_sat_f64x2_u_zero => @panic("TODO"),
866 .f64x2_convert_low_i32x4_s => @panic("TODO"),
867 .f64x2_convert_low_i32x4_u => @panic("TODO"),
868 .i8x16_relaxed_swizzle => @panic("TODO"),
869 .i32x4_relaxed_trunc_f32x4_s => @panic("TODO"),
870 .i32x4_relaxed_trunc_f32x4_u => @panic("TODO"),
871 .i32x4_relaxed_trunc_f64x2_s_zero => @panic("TODO"),
872 .i32x4_relaxed_trunc_f64x2_u_zero => @panic("TODO"),
873 .f32x4_relaxed_madd => @panic("TODO"),
874 .f32x4_relaxed_nmadd => @panic("TODO"),
875 .f64x2_relaxed_madd => @panic("TODO"),
876 .f64x2_relaxed_nmadd => @panic("TODO"),
877 .i8x16_relaxed_laneselect => @panic("TODO"),
878 .i16x8_relaxed_laneselect => @panic("TODO"),
879 .i32x4_relaxed_laneselect => @panic("TODO"),
880 .i64x2_relaxed_laneselect => @panic("TODO"),
881 .f32x4_relaxed_min => @panic("TODO"),
882 .f32x4_relaxed_max => @panic("TODO"),
883 .f64x2_relaxed_min => @panic("TODO"),
884 .f64x2_relaxed_max => @panic("TODO"),
885 .i16x8_relaxed_q15mulr_s => @panic("TODO"),
886 .i16x8_relaxed_dot_i8x16_i7x16_s => @panic("TODO"),
887 .i32x4_relaxed_dot_i8x16_i7x16_add_s => @panic("TODO"),
888 .f32x4_relaxed_dot_bf16x8_add_f32x4 => @panic("TODO"),
889 }
890 comptime unreachable;
891 },
892 .atomics_prefix => {
893 try code.ensureUnusedCapacity(gpa, 6 + 20);
894
895 const extra_index = datas[inst].payload;
896 const opcode = mir.extra[extra_index];
897 code.appendAssumeCapacity(@backingInt(std.wasm.Opcode.atomics_prefix));
898 writeUleb128(code, opcode);
899 switch (@as(std.wasm.AtomicsOpcode, @fromBackingInt(@intCast(opcode)))) {
900 .i32_atomic_load,
901 .i64_atomic_load,
902 .i32_atomic_load8_u,
903 .i32_atomic_load16_u,
904 .i64_atomic_load8_u,
905 .i64_atomic_load16_u,
906 .i64_atomic_load32_u,
907 .i32_atomic_store,
908 .i64_atomic_store,
909 .i32_atomic_store8,
910 .i32_atomic_store16,
911 .i64_atomic_store8,
912 .i64_atomic_store16,
913 .i64_atomic_store32,
914 .i32_atomic_rmw_add,
915 .i64_atomic_rmw_add,
916 .i32_atomic_rmw8_add_u,
917 .i32_atomic_rmw16_add_u,
918 .i64_atomic_rmw8_add_u,
919 .i64_atomic_rmw16_add_u,
920 .i64_atomic_rmw32_add_u,
921 .i32_atomic_rmw_sub,
922 .i64_atomic_rmw_sub,
923 .i32_atomic_rmw8_sub_u,
924 .i32_atomic_rmw16_sub_u,
925 .i64_atomic_rmw8_sub_u,
926 .i64_atomic_rmw16_sub_u,
927 .i64_atomic_rmw32_sub_u,
928 .i32_atomic_rmw_and,
929 .i64_atomic_rmw_and,
930 .i32_atomic_rmw8_and_u,
931 .i32_atomic_rmw16_and_u,
932 .i64_atomic_rmw8_and_u,
933 .i64_atomic_rmw16_and_u,
934 .i64_atomic_rmw32_and_u,
935 .i32_atomic_rmw_or,
936 .i64_atomic_rmw_or,
937 .i32_atomic_rmw8_or_u,
938 .i32_atomic_rmw16_or_u,
939 .i64_atomic_rmw8_or_u,
940 .i64_atomic_rmw16_or_u,
941 .i64_atomic_rmw32_or_u,
942 .i32_atomic_rmw_xor,
943 .i64_atomic_rmw_xor,
944 .i32_atomic_rmw8_xor_u,
945 .i32_atomic_rmw16_xor_u,
946 .i64_atomic_rmw8_xor_u,
947 .i64_atomic_rmw16_xor_u,
948 .i64_atomic_rmw32_xor_u,
949 .i32_atomic_rmw_xchg,
950 .i64_atomic_rmw_xchg,
951 .i32_atomic_rmw8_xchg_u,
952 .i32_atomic_rmw16_xchg_u,
953 .i64_atomic_rmw8_xchg_u,
954 .i64_atomic_rmw16_xchg_u,
955 .i64_atomic_rmw32_xchg_u,
956
957 .i32_atomic_rmw_cmpxchg,
958 .i64_atomic_rmw_cmpxchg,
959 .i32_atomic_rmw8_cmpxchg_u,
960 .i32_atomic_rmw16_cmpxchg_u,
961 .i64_atomic_rmw8_cmpxchg_u,
962 .i64_atomic_rmw16_cmpxchg_u,
963 .i64_atomic_rmw32_cmpxchg_u,
964 => {
965 const mem_arg = mir.extraData(Mir.MemArg, extra_index + 1).data;
966 encodeMemArg(code, mem_arg);
967 inst += 1;
968 continue :loop tags[inst];
969 },
970 .atomic_fence => {
971 // Hard-codes memory index 0 since multi-memory proposal is
972 // not yet accepted nor implemented.
973 const memory_index: u32 = 0;
974 writeUleb128(code, memory_index);
975 inst += 1;
976 continue :loop tags[inst];
977 },
978 .memory_atomic_notify => @panic("TODO"),
979 .memory_atomic_wait32 => @panic("TODO"),
980 .memory_atomic_wait64 => @panic("TODO"),
981 }
982 comptime unreachable;
983 },
984 }
985 comptime unreachable;
986}
987
988/// Asserts 20 unused capacity.
989fn encodeMemArg(code: *ArrayList(u8), mem_arg: Mir.MemArg) void {
990 assert(code.unusedCapacitySlice().len >= 20);
991 // Wasm encodes alignment as power of 2, rather than natural alignment.
992 const encoded_alignment = @ctz(mem_arg.alignment);
993 writeUleb128(code, encoded_alignment);
994 writeUleb128(code, mem_arg.offset);
995}
996
997fn uavRefObj(wasm: *Wasm, code: *ArrayList(u8), value: InternPool.Index, offset: i32, is_wasm32: bool) !void {
998 const comp = wasm.base.comp;
999 const gpa = comp.gpa;
1000 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;
1001
1002 try code.ensureUnusedCapacity(gpa, 11);
1003 code.appendAssumeCapacity(@backingInt(opcode));
1004
1005 try wasm.zcu_relocations.append(gpa, .{
1006 .offset = @intCast(code.items.len),
1007 .pointee = .{ .data_uav = value },
1008 .tag = if (is_wasm32) .memory_addr_sleb else .memory_addr_sleb64,
1009 .addend = offset,
1010 });
1011 appendSlebRelocPlaceholder(code, is_wasm32);
1012}
1013
1014fn uavRefExe(wasm: *Wasm, code: *ArrayList(u8), value: InternPool.Index, offset: i32, is_wasm32: bool) !void {
1015 const comp = wasm.base.comp;
1016 const gpa = comp.gpa;
1017 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;
1018
1019 try code.ensureUnusedCapacity(gpa, 11);
1020 code.appendAssumeCapacity(@backingInt(opcode));
1021
1022 const addr = wasm.uavAddr(value);
1023 writeSleb128(code, @as(u32, @intCast(@as(i64, addr) + offset)));
1024}
1025
1026fn navRefOff(wasm: *Wasm, code: *ArrayList(u8), data: Mir.NavRefOff, is_wasm32: bool) !void {
1027 const comp = wasm.base.comp;
1028 const zcu = comp.zcu.?;
1029 const ip = &zcu.intern_pool;
1030 const gpa = comp.gpa;
1031 const is_obj = comp.config.output_mode == .Obj;
1032 const nav_ty = ip.getNav(data.nav_index).resolved.?.type;
1033 assert(!ip.isFunctionType(nav_ty));
1034
1035 try code.ensureUnusedCapacity(gpa, 11);
1036
1037 const opcode: std.wasm.Opcode = if (is_wasm32) .i32_const else .i64_const;
1038 code.appendAssumeCapacity(@backingInt(opcode));
1039 if (is_obj) {
1040 try wasm.zcu_relocations.append(gpa, .{
1041 .offset = @intCast(code.items.len),
1042 .pointee = .{ .data_nav = data.nav_index },
1043 .tag = if (is_wasm32) .memory_addr_sleb else .memory_addr_sleb64,
1044 .addend = data.offset,
1045 });
1046 appendSlebRelocPlaceholder(code, is_wasm32);
1047 } else {
1048 const addr = wasm.navAddr(data.nav_index);
1049 writeSleb128(code, @as(u32, @intCast(@as(i64, addr) + data.offset)));
1050 }
1051}
1052
1053fn appendOutputFunctionIndex(code: *ArrayList(u8), i: Wasm.OutputFunctionIndex) void {
1054 writeUleb128(code, @backingInt(i));
1055}
1056
1057fn appendStackPointerGlobalIndex(
1058 wasm: *Wasm,
1059 code: *ArrayList(u8),
1060 is_obj: bool,
1061) Error!void {
1062 if (is_obj) {
1063 try wasm.zcu_relocations.append(wasm.base.comp.gpa, .{
1064 .offset = @intCast(code.items.len),
1065 .pointee = .stack_pointer,
1066 .tag = .global_index_leb,
1067 .addend = 0,
1068 });
1069 appendUlebRelocPlaceholder(code);
1070 } else {
1071 const sp_global: Wasm.GlobalIndex = .stack_pointer;
1072 writeUleb128(code, @backingInt(sp_global));
1073 }
1074}
1075
1076fn appendUlebRelocPlaceholder(code: *ArrayList(u8)) void {
1077 code.appendSliceAssumeCapacity(&.{ 0x80, 0x80, 0x80, 0x80, 0x00 });
1078}
1079
1080fn appendSlebRelocPlaceholder(code: *ArrayList(u8), is_wasm32: bool) void {
1081 if (is_wasm32) {
1082 code.appendSliceAssumeCapacity(&.{ 0x80, 0x80, 0x80, 0x80, 0x00 });
1083 } else {
1084 code.appendSliceAssumeCapacity(&.{
1085 0x80, 0x80, 0x80, 0x80, 0x80,
1086 0x80, 0x80, 0x80, 0x80, 0x00,
1087 });
1088 }
1089}
1090
1091fn writeUleb128(code: *ArrayList(u8), arg: anytype) void {
1092 var w: std.Io.Writer = .fixed(code.unusedCapacitySlice());
1093 w.writeUleb128(arg) catch unreachable;
1094 code.items.len += w.end;
1095}
1096
1097fn writeSleb128(code: *ArrayList(u8), arg: anytype) void {
1098 var w: std.Io.Writer = .fixed(code.unusedCapacitySlice());
1099 w.writeSleb128(arg) catch unreachable;
1100 code.items.len += w.end;
1101}