master
1const std = @import("std");
2const Allocator = std.mem.Allocator;
3const assert = std.debug.assert;
4
5const build_options = @import("build_options");
6const Zcu = @import("../Zcu.zig");
7const Value = @import("../Value.zig");
8const Type = @import("../Type.zig");
9const Air = @import("../Air.zig");
10const InternPool = @import("../InternPool.zig");
11
12pub fn write(air: Air, stream: *std.Io.Writer, pt: Zcu.PerThread, liveness: ?Air.Liveness) void {
13 comptime assert(build_options.enable_debug_extensions);
14 const instruction_bytes = air.instructions.len *
15 // Here we don't use @sizeOf(Air.Inst.Data) because it would include
16 // the debug safety tag but we want to measure release size.
17 (@sizeOf(Air.Inst.Tag) + 8);
18 const extra_bytes = air.extra.items.len * @sizeOf(u32);
19 const tomb_bytes = if (liveness) |l| l.tomb_bits.len * @sizeOf(usize) else 0;
20 const liveness_extra_bytes = if (liveness) |l| l.extra.len * @sizeOf(u32) else 0;
21 const liveness_special_bytes = if (liveness) |l| l.special.count() * 8 else 0;
22 const total_bytes = @sizeOf(Air) + instruction_bytes + extra_bytes +
23 @sizeOf(Air.Liveness) + liveness_extra_bytes +
24 liveness_special_bytes + tomb_bytes;
25
26 // zig fmt: off
27 stream.print(
28 \\# Total AIR+Liveness bytes: {Bi}
29 \\# AIR Instructions: {d} ({Bi})
30 \\# AIR Extra Data: {d} ({Bi})
31 \\# Liveness tomb_bits: {Bi}
32 \\# Liveness Extra Data: {d} ({Bi})
33 \\# Liveness special table: {d} ({Bi})
34 \\
35 , .{
36 total_bytes,
37 air.instructions.len, instruction_bytes,
38 air.extra.items.len, extra_bytes,
39 tomb_bytes,
40 if (liveness) |l| l.extra.len else 0, liveness_extra_bytes,
41 if (liveness) |l| l.special.count() else 0, liveness_special_bytes,
42 }) catch return;
43 // zig fmt: on
44
45 var writer: Writer = .{
46 .pt = pt,
47 .gpa = pt.zcu.gpa,
48 .air = air,
49 .liveness = liveness,
50 .indent = 2,
51 .skip_body = false,
52 };
53 writer.writeBody(stream, air.getMainBody()) catch return;
54}
55
56pub fn writeInst(
57 air: Air,
58 stream: *std.Io.Writer,
59 inst: Air.Inst.Index,
60 pt: Zcu.PerThread,
61 liveness: ?Air.Liveness,
62) void {
63 comptime assert(build_options.enable_debug_extensions);
64 var writer: Writer = .{
65 .pt = pt,
66 .gpa = pt.zcu.gpa,
67 .air = air,
68 .liveness = liveness,
69 .indent = 2,
70 .skip_body = true,
71 };
72 writer.writeInst(stream, inst) catch return;
73}
74
75pub fn dump(air: Air, pt: Zcu.PerThread, liveness: ?Air.Liveness) void {
76 const stderr_bw, _ = std.debug.lockStderrWriter(&.{});
77 defer std.debug.unlockStderrWriter();
78 air.write(stderr_bw, pt, liveness);
79}
80
81pub fn dumpInst(air: Air, inst: Air.Inst.Index, pt: Zcu.PerThread, liveness: ?Air.Liveness) void {
82 const stderr_bw, _ = std.debug.lockStderrWriter(&.{});
83 defer std.debug.unlockStderrWriter();
84 air.writeInst(stderr_bw, inst, pt, liveness);
85}
86
87const Writer = struct {
88 pt: Zcu.PerThread,
89 gpa: Allocator,
90 air: Air,
91 liveness: ?Air.Liveness,
92 indent: usize,
93 skip_body: bool,
94
95 const Error = std.Io.Writer.Error;
96
97 fn writeBody(w: *Writer, s: *std.Io.Writer, body: []const Air.Inst.Index) Error!void {
98 for (body) |inst| {
99 try w.writeInst(s, inst);
100 try s.writeByte('\n');
101 }
102 }
103
104 fn writeInst(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
105 const tag = w.air.instructions.items(.tag)[@intFromEnum(inst)];
106 try s.splatByteAll(' ', w.indent);
107 try s.print("{f}{c}= {s}(", .{
108 inst,
109 @as(u8, if (if (w.liveness) |liveness| liveness.isUnused(inst) else false) '!' else ' '),
110 @tagName(tag),
111 });
112 switch (tag) {
113 .add,
114 .add_optimized,
115 .add_safe,
116 .add_wrap,
117 .add_sat,
118 .sub,
119 .sub_optimized,
120 .sub_safe,
121 .sub_wrap,
122 .sub_sat,
123 .mul,
124 .mul_optimized,
125 .mul_safe,
126 .mul_wrap,
127 .mul_sat,
128 .div_float,
129 .div_trunc,
130 .div_floor,
131 .div_exact,
132 .rem,
133 .mod,
134 .bit_and,
135 .bit_or,
136 .xor,
137 .cmp_lt,
138 .cmp_lte,
139 .cmp_eq,
140 .cmp_gte,
141 .cmp_gt,
142 .cmp_neq,
143 .bool_and,
144 .bool_or,
145 .store,
146 .store_safe,
147 .array_elem_val,
148 .slice_elem_val,
149 .ptr_elem_val,
150 .shl,
151 .shl_exact,
152 .shl_sat,
153 .shr,
154 .shr_exact,
155 .set_union_tag,
156 .min,
157 .max,
158 .div_float_optimized,
159 .div_trunc_optimized,
160 .div_floor_optimized,
161 .div_exact_optimized,
162 .rem_optimized,
163 .mod_optimized,
164 .cmp_lt_optimized,
165 .cmp_lte_optimized,
166 .cmp_eq_optimized,
167 .cmp_gte_optimized,
168 .cmp_gt_optimized,
169 .cmp_neq_optimized,
170 .memcpy,
171 .memmove,
172 .memset,
173 .memset_safe,
174 .legalize_vec_elem_val,
175 => try w.writeBinOp(s, inst),
176
177 .is_null,
178 .is_non_null,
179 .is_null_ptr,
180 .is_non_null_ptr,
181 .is_err,
182 .is_non_err,
183 .is_err_ptr,
184 .is_non_err_ptr,
185 .ret,
186 .ret_safe,
187 .ret_load,
188 .is_named_enum_value,
189 .tag_name,
190 .error_name,
191 .sqrt,
192 .sin,
193 .cos,
194 .tan,
195 .exp,
196 .exp2,
197 .log,
198 .log2,
199 .log10,
200 .floor,
201 .ceil,
202 .round,
203 .trunc_float,
204 .neg,
205 .neg_optimized,
206 .cmp_lt_errors_len,
207 .set_err_return_trace,
208 .c_va_end,
209 => try w.writeUnOp(s, inst),
210
211 .trap,
212 .breakpoint,
213 .dbg_empty_stmt,
214 .unreach,
215 .ret_addr,
216 .frame_addr,
217 .save_err_return_trace_index,
218 => try w.writeNoOp(s, inst),
219
220 .alloc,
221 .ret_ptr,
222 .err_return_trace,
223 .c_va_start,
224 => try w.writeTy(s, inst),
225
226 .arg => try w.writeArg(s, inst),
227
228 .not,
229 .bitcast,
230 .load,
231 .fptrunc,
232 .fpext,
233 .intcast,
234 .intcast_safe,
235 .trunc,
236 .optional_payload,
237 .optional_payload_ptr,
238 .optional_payload_ptr_set,
239 .errunion_payload_ptr_set,
240 .wrap_optional,
241 .unwrap_errunion_payload,
242 .unwrap_errunion_err,
243 .unwrap_errunion_payload_ptr,
244 .unwrap_errunion_err_ptr,
245 .wrap_errunion_payload,
246 .wrap_errunion_err,
247 .slice_ptr,
248 .slice_len,
249 .ptr_slice_len_ptr,
250 .ptr_slice_ptr_ptr,
251 .struct_field_ptr_index_0,
252 .struct_field_ptr_index_1,
253 .struct_field_ptr_index_2,
254 .struct_field_ptr_index_3,
255 .array_to_slice,
256 .float_from_int,
257 .splat,
258 .int_from_float,
259 .int_from_float_optimized,
260 .int_from_float_safe,
261 .int_from_float_optimized_safe,
262 .get_union_tag,
263 .clz,
264 .ctz,
265 .popcount,
266 .byte_swap,
267 .bit_reverse,
268 .abs,
269 .error_set_has_value,
270 .addrspace_cast,
271 .c_va_arg,
272 .c_va_copy,
273 => try w.writeTyOp(s, inst),
274
275 .block, .dbg_inline_block => try w.writeBlock(s, tag, inst),
276
277 .loop => try w.writeLoop(s, inst),
278
279 .slice,
280 .slice_elem_ptr,
281 .ptr_elem_ptr,
282 .ptr_add,
283 .ptr_sub,
284 .add_with_overflow,
285 .sub_with_overflow,
286 .mul_with_overflow,
287 .shl_with_overflow,
288 => try w.writeTyPlBin(s, inst),
289
290 .call,
291 .call_always_tail,
292 .call_never_tail,
293 .call_never_inline,
294 => try w.writeCall(s, inst),
295
296 .dbg_var_ptr,
297 .dbg_var_val,
298 .dbg_arg_inline,
299 => try w.writeDbgVar(s, inst),
300
301 .struct_field_ptr => try w.writeStructField(s, inst),
302 .struct_field_val => try w.writeStructField(s, inst),
303 .inferred_alloc => @panic("TODO"),
304 .inferred_alloc_comptime => @panic("TODO"),
305 .assembly => try w.writeAssembly(s, inst),
306 .dbg_stmt => try w.writeDbgStmt(s, inst),
307
308 .aggregate_init => try w.writeAggregateInit(s, inst),
309 .union_init => try w.writeUnionInit(s, inst),
310 .br => try w.writeBr(s, inst),
311 .switch_dispatch => try w.writeBr(s, inst),
312 .repeat => try w.writeRepeat(s, inst),
313 .cond_br => try w.writeCondBr(s, inst),
314 .@"try", .try_cold => try w.writeTry(s, inst),
315 .try_ptr, .try_ptr_cold => try w.writeTryPtr(s, inst),
316 .loop_switch_br, .switch_br => try w.writeSwitchBr(s, inst),
317 .cmpxchg_weak, .cmpxchg_strong => try w.writeCmpxchg(s, inst),
318 .atomic_load => try w.writeAtomicLoad(s, inst),
319 .prefetch => try w.writePrefetch(s, inst),
320 .atomic_store_unordered => try w.writeAtomicStore(s, inst, .unordered),
321 .atomic_store_monotonic => try w.writeAtomicStore(s, inst, .monotonic),
322 .atomic_store_release => try w.writeAtomicStore(s, inst, .release),
323 .atomic_store_seq_cst => try w.writeAtomicStore(s, inst, .seq_cst),
324 .atomic_rmw => try w.writeAtomicRmw(s, inst),
325 .field_parent_ptr => try w.writeFieldParentPtr(s, inst),
326 .wasm_memory_size => try w.writeWasmMemorySize(s, inst),
327 .wasm_memory_grow => try w.writeWasmMemoryGrow(s, inst),
328 .mul_add => try w.writeMulAdd(s, inst),
329 .select => try w.writeSelect(s, inst),
330 .shuffle_one => try w.writeShuffleOne(s, inst),
331 .shuffle_two => try w.writeShuffleTwo(s, inst),
332 .reduce, .reduce_optimized => try w.writeReduce(s, inst),
333 .cmp_vector, .cmp_vector_optimized => try w.writeCmpVector(s, inst),
334 .runtime_nav_ptr => try w.writeRuntimeNavPtr(s, inst),
335 .legalize_vec_store_elem => try w.writeLegalizeVecStoreElem(s, inst),
336 .legalize_compiler_rt_call => try w.writeLegalizeCompilerRtCall(s, inst),
337
338 .work_item_id,
339 .work_group_size,
340 .work_group_id,
341 => try w.writeWorkDimension(s, inst),
342 }
343 try s.writeByte(')');
344 }
345
346 fn writeBinOp(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
347 const bin_op = w.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
348 try w.writeOperand(s, inst, 0, bin_op.lhs);
349 try s.writeAll(", ");
350 try w.writeOperand(s, inst, 1, bin_op.rhs);
351 }
352
353 fn writeUnOp(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
354 const un_op = w.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
355 try w.writeOperand(s, inst, 0, un_op);
356 }
357
358 fn writeNoOp(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
359 _ = w;
360 _ = s;
361 _ = inst;
362 // no-op, no argument to write
363 }
364
365 fn writeType(w: *Writer, s: *std.Io.Writer, ty: Type) !void {
366 return ty.print(s, w.pt, null);
367 }
368
369 fn writeTy(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
370 const ty = w.air.instructions.items(.data)[@intFromEnum(inst)].ty;
371 try w.writeType(s, ty);
372 }
373
374 fn writeArg(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
375 const arg = w.air.instructions.items(.data)[@intFromEnum(inst)].arg;
376 try w.writeType(s, arg.ty.toType());
377 try s.print(", {d}", .{arg.zir_param_index});
378 }
379
380 fn writeTyOp(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
381 const ty_op = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
382 try w.writeType(s, ty_op.ty.toType());
383 try s.writeAll(", ");
384 try w.writeOperand(s, inst, 0, ty_op.operand);
385 }
386
387 fn writeBlock(w: *Writer, s: *std.Io.Writer, tag: Air.Inst.Tag, inst: Air.Inst.Index) Error!void {
388 const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
389 try w.writeType(s, ty_pl.ty.toType());
390 const body: []const Air.Inst.Index = @ptrCast(switch (tag) {
391 inline .block, .dbg_inline_block => |comptime_tag| body: {
392 const extra = w.air.extraData(switch (comptime_tag) {
393 .block => Air.Block,
394 .dbg_inline_block => Air.DbgInlineBlock,
395 else => unreachable,
396 }, ty_pl.payload);
397 switch (comptime_tag) {
398 .block => {},
399 .dbg_inline_block => {
400 try s.writeAll(", ");
401 try w.writeInstRef(s, Air.internedToRef(extra.data.func), false);
402 },
403 else => unreachable,
404 }
405 break :body w.air.extra.items[extra.end..][0..extra.data.body_len];
406 },
407 else => unreachable,
408 });
409 if (w.skip_body) return s.writeAll(", ...");
410 const liveness_block: Air.Liveness.BlockSlices = if (w.liveness) |liveness|
411 liveness.getBlock(inst)
412 else
413 .{ .deaths = &.{} };
414
415 try s.writeAll(", {\n");
416 const old_indent = w.indent;
417 w.indent += 2;
418 try w.writeBody(s, body);
419 w.indent = old_indent;
420 try s.splatByteAll(' ', w.indent);
421 try s.writeAll("}");
422
423 for (liveness_block.deaths) |operand| {
424 try s.print(" {f}!", .{operand});
425 }
426 }
427
428 fn writeLoop(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
429 const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
430 const extra = w.air.extraData(Air.Block, ty_pl.payload);
431 const body: []const Air.Inst.Index = @ptrCast(w.air.extra.items[extra.end..][0..extra.data.body_len]);
432
433 try w.writeType(s, ty_pl.ty.toType());
434 if (w.skip_body) return s.writeAll(", ...");
435 try s.writeAll(", {\n");
436 const old_indent = w.indent;
437 w.indent += 2;
438 try w.writeBody(s, body);
439 w.indent = old_indent;
440 try s.splatByteAll(' ', w.indent);
441 try s.writeAll("}");
442 }
443
444 fn writeAggregateInit(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
445 const zcu = w.pt.zcu;
446 const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
447 const vector_ty = ty_pl.ty.toType();
448 const len = @as(usize, @intCast(vector_ty.arrayLen(zcu)));
449 const elements = @as([]const Air.Inst.Ref, @ptrCast(w.air.extra.items[ty_pl.payload..][0..len]));
450
451 try w.writeType(s, vector_ty);
452 try s.writeAll(", [");
453 for (elements, 0..) |elem, i| {
454 if (i != 0) try s.writeAll(", ");
455 try w.writeOperand(s, inst, i, elem);
456 }
457 try s.writeAll("]");
458 }
459
460 fn writeUnionInit(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
461 const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
462 const extra = w.air.extraData(Air.UnionInit, ty_pl.payload).data;
463
464 try s.print("{d}, ", .{extra.field_index});
465 try w.writeOperand(s, inst, 0, extra.init);
466 }
467
468 fn writeStructField(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
469 const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
470 const extra = w.air.extraData(Air.StructField, ty_pl.payload).data;
471
472 try w.writeOperand(s, inst, 0, extra.struct_operand);
473 try s.print(", {d}", .{extra.field_index});
474 }
475
476 fn writeTyPlBin(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
477 const data = w.air.instructions.items(.data);
478 const ty_pl = data[@intFromEnum(inst)].ty_pl;
479 const extra = w.air.extraData(Air.Bin, ty_pl.payload).data;
480
481 const inst_ty = data[@intFromEnum(inst)].ty_pl.ty.toType();
482 try w.writeType(s, inst_ty);
483 try s.writeAll(", ");
484 try w.writeOperand(s, inst, 0, extra.lhs);
485 try s.writeAll(", ");
486 try w.writeOperand(s, inst, 1, extra.rhs);
487 }
488
489 fn writeCmpxchg(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
490 const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
491 const extra = w.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
492
493 try w.writeOperand(s, inst, 0, extra.ptr);
494 try s.writeAll(", ");
495 try w.writeOperand(s, inst, 1, extra.expected_value);
496 try s.writeAll(", ");
497 try w.writeOperand(s, inst, 2, extra.new_value);
498 try s.print(", {s}, {s}", .{
499 @tagName(extra.successOrder()), @tagName(extra.failureOrder()),
500 });
501 }
502
503 fn writeMulAdd(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
504 const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
505 const extra = w.air.extraData(Air.Bin, pl_op.payload).data;
506
507 try w.writeOperand(s, inst, 0, extra.lhs);
508 try s.writeAll(", ");
509 try w.writeOperand(s, inst, 1, extra.rhs);
510 try s.writeAll(", ");
511 try w.writeOperand(s, inst, 2, pl_op.operand);
512 }
513
514 fn writeLegalizeVecStoreElem(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
515 const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
516 const bin = w.air.extraData(Air.Bin, pl_op.payload).data;
517
518 try w.writeOperand(s, inst, 0, pl_op.operand);
519 try s.writeAll(", ");
520 try w.writeOperand(s, inst, 1, bin.lhs);
521 try s.writeAll(", ");
522 try w.writeOperand(s, inst, 2, bin.rhs);
523 try s.writeAll(", ");
524 }
525
526 fn writeLegalizeCompilerRtCall(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
527 const inst_data = w.air.instructions.items(.data)[@intFromEnum(inst)].legalize_compiler_rt_call;
528 const extra = w.air.extraData(Air.Call, inst_data.payload);
529 const args: []const Air.Inst.Ref = @ptrCast(w.air.extra.items[extra.end..][0..extra.data.args_len]);
530
531 try s.print("{t}, [", .{inst_data.func});
532 for (args, 0..) |arg, i| {
533 if (i != 0) try s.writeAll(", ");
534 try w.writeOperand(s, inst, i, arg);
535 }
536 try s.writeByte(']');
537 }
538
539 fn writeShuffleOne(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
540 const unwrapped = w.air.unwrapShuffleOne(w.pt.zcu, inst);
541 try w.writeType(s, unwrapped.result_ty);
542 try s.writeAll(", ");
543 try w.writeOperand(s, inst, 0, unwrapped.operand);
544 try s.writeAll(", [");
545 for (unwrapped.mask, 0..) |mask_elem, mask_idx| {
546 if (mask_idx > 0) try s.writeAll(", ");
547 switch (mask_elem.unwrap()) {
548 .elem => |idx| try s.print("elem {d}", .{idx}),
549 .value => |val| try s.print("val {f}", .{Value.fromInterned(val).fmtValue(w.pt)}),
550 }
551 }
552 try s.writeByte(']');
553 }
554
555 fn writeShuffleTwo(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
556 const unwrapped = w.air.unwrapShuffleTwo(w.pt.zcu, inst);
557 try w.writeType(s, unwrapped.result_ty);
558 try s.writeAll(", ");
559 try w.writeOperand(s, inst, 0, unwrapped.operand_a);
560 try s.writeAll(", ");
561 try w.writeOperand(s, inst, 1, unwrapped.operand_b);
562 try s.writeAll(", [");
563 for (unwrapped.mask, 0..) |mask_elem, mask_idx| {
564 if (mask_idx > 0) try s.writeAll(", ");
565 switch (mask_elem.unwrap()) {
566 .a_elem => |idx| try s.print("a_elem {d}", .{idx}),
567 .b_elem => |idx| try s.print("b_elem {d}", .{idx}),
568 .undef => try s.writeAll("undef"),
569 }
570 }
571 try s.writeByte(']');
572 }
573
574 fn writeSelect(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
575 const zcu = w.pt.zcu;
576 const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
577 const extra = w.air.extraData(Air.Bin, pl_op.payload).data;
578
579 const elem_ty = w.typeOfIndex(inst).childType(zcu);
580 try w.writeType(s, elem_ty);
581 try s.writeAll(", ");
582 try w.writeOperand(s, inst, 0, pl_op.operand);
583 try s.writeAll(", ");
584 try w.writeOperand(s, inst, 1, extra.lhs);
585 try s.writeAll(", ");
586 try w.writeOperand(s, inst, 2, extra.rhs);
587 }
588
589 fn writeReduce(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
590 const reduce = w.air.instructions.items(.data)[@intFromEnum(inst)].reduce;
591
592 try w.writeOperand(s, inst, 0, reduce.operand);
593 try s.print(", {s}", .{@tagName(reduce.operation)});
594 }
595
596 fn writeCmpVector(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
597 const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
598 const extra = w.air.extraData(Air.VectorCmp, ty_pl.payload).data;
599
600 try s.print("{s}, ", .{@tagName(extra.compareOperator())});
601 try w.writeOperand(s, inst, 0, extra.lhs);
602 try s.writeAll(", ");
603 try w.writeOperand(s, inst, 1, extra.rhs);
604 }
605
606 fn writeRuntimeNavPtr(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
607 const ip = &w.pt.zcu.intern_pool;
608 const ty_nav = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav;
609 try w.writeType(s, .fromInterned(ty_nav.ty));
610 try s.print(", '{f}'", .{ip.getNav(ty_nav.nav).fqn.fmt(ip)});
611 }
612
613 fn writeAtomicLoad(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
614 const atomic_load = w.air.instructions.items(.data)[@intFromEnum(inst)].atomic_load;
615
616 try w.writeOperand(s, inst, 0, atomic_load.ptr);
617 try s.print(", {s}", .{@tagName(atomic_load.order)});
618 }
619
620 fn writePrefetch(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
621 const prefetch = w.air.instructions.items(.data)[@intFromEnum(inst)].prefetch;
622
623 try w.writeOperand(s, inst, 0, prefetch.ptr);
624 try s.print(", {s}, {d}, {s}", .{
625 @tagName(prefetch.rw), prefetch.locality, @tagName(prefetch.cache),
626 });
627 }
628
629 fn writeAtomicStore(
630 w: *Writer,
631 s: *std.Io.Writer,
632 inst: Air.Inst.Index,
633 order: std.builtin.AtomicOrder,
634 ) Error!void {
635 const bin_op = w.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
636 try w.writeOperand(s, inst, 0, bin_op.lhs);
637 try s.writeAll(", ");
638 try w.writeOperand(s, inst, 1, bin_op.rhs);
639 try s.print(", {s}", .{@tagName(order)});
640 }
641
642 fn writeAtomicRmw(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
643 const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
644 const extra = w.air.extraData(Air.AtomicRmw, pl_op.payload).data;
645
646 try w.writeOperand(s, inst, 0, pl_op.operand);
647 try s.writeAll(", ");
648 try w.writeOperand(s, inst, 1, extra.operand);
649 try s.print(", {s}, {s}", .{ @tagName(extra.op()), @tagName(extra.ordering()) });
650 }
651
652 fn writeFieldParentPtr(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
653 const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
654 const extra = w.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
655
656 try w.writeOperand(s, inst, 0, extra.field_ptr);
657 try s.print(", {d}", .{extra.field_index});
658 }
659
660 fn writeAssembly(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
661 const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
662 const extra = w.air.extraData(Air.Asm, ty_pl.payload);
663 const is_volatile = extra.data.flags.is_volatile;
664 const outputs_len = extra.data.flags.outputs_len;
665 var extra_i: usize = extra.end;
666 var op_index: usize = 0;
667
668 const ret_ty = w.typeOfIndex(inst);
669 try w.writeType(s, ret_ty);
670
671 if (is_volatile) {
672 try s.writeAll(", volatile");
673 }
674
675 const outputs = @as([]const Air.Inst.Ref, @ptrCast(w.air.extra.items[extra_i..][0..outputs_len]));
676 extra_i += outputs.len;
677 const inputs = @as([]const Air.Inst.Ref, @ptrCast(w.air.extra.items[extra_i..][0..extra.data.inputs_len]));
678 extra_i += inputs.len;
679
680 for (outputs) |output| {
681 const extra_bytes = std.mem.sliceAsBytes(w.air.extra.items[extra_i..]);
682 const constraint = std.mem.sliceTo(extra_bytes, 0);
683 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
684
685 // This equation accounts for the fact that even if we have exactly 4 bytes
686 // for the strings and their null terminators, we still use the next u32
687 // for the null terminator.
688 extra_i += (constraint.len + name.len + (2 + 3)) / 4;
689
690 if (output == .none) {
691 try s.print(", [{s}] -> {s}", .{ name, constraint });
692 } else {
693 try s.print(", [{s}] out {s} = (", .{ name, constraint });
694 try w.writeOperand(s, inst, op_index, output);
695 op_index += 1;
696 try s.writeByte(')');
697 }
698 }
699
700 for (inputs) |input| {
701 const extra_bytes = std.mem.sliceAsBytes(w.air.extra.items[extra_i..]);
702 const constraint = std.mem.sliceTo(extra_bytes, 0);
703 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
704 // This equation accounts for the fact that even if we have exactly 4 bytes
705 // for the strings and their null terminators, we still use the next u32
706 // for the null terminator.
707 extra_i += (constraint.len + name.len + 1) / 4 + 1;
708
709 try s.print(", [{s}] in {s} = (", .{ name, constraint });
710 try w.writeOperand(s, inst, op_index, input);
711 op_index += 1;
712 try s.writeByte(')');
713 }
714
715 const zcu = w.pt.zcu;
716 const ip = &zcu.intern_pool;
717 const aggregate = ip.indexToKey(extra.data.clobbers).aggregate;
718 const struct_type: Type = .fromInterned(aggregate.ty);
719 switch (aggregate.storage) {
720 .elems => |elems| for (elems, 0..) |elem, i| {
721 switch (elem) {
722 .bool_true => {
723 const clobber = struct_type.structFieldName(i, zcu).toSlice(ip).?;
724 assert(clobber.len != 0);
725 try s.writeAll(", ~{");
726 try s.writeAll(clobber);
727 try s.writeAll("}");
728 },
729 .bool_false => continue,
730 else => unreachable,
731 }
732 },
733 .repeated_elem => |elem| {
734 try s.writeAll(", ");
735 try s.writeAll(switch (elem) {
736 .bool_true => "<all clobbers>",
737 .bool_false => "<no clobbers>",
738 else => unreachable,
739 });
740 },
741 .bytes => |bytes| {
742 try s.print(", {x}", .{bytes});
743 },
744 }
745 const asm_source = std.mem.sliceAsBytes(w.air.extra.items[extra_i..])[0..extra.data.source_len];
746 try s.print(", \"{f}\"", .{std.zig.fmtString(asm_source)});
747 }
748
749 fn writeDbgStmt(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
750 const dbg_stmt = w.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
751 try s.print("{d}:{d}", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 });
752 }
753
754 fn writeDbgVar(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
755 const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
756 try w.writeOperand(s, inst, 0, pl_op.operand);
757 const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload);
758 try s.print(", \"{f}\"", .{std.zig.fmtString(name.toSlice(w.air))});
759 }
760
761 fn writeCall(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
762 const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
763 const extra = w.air.extraData(Air.Call, pl_op.payload);
764 const args = @as([]const Air.Inst.Ref, @ptrCast(w.air.extra.items[extra.end..][0..extra.data.args_len]));
765 try w.writeOperand(s, inst, 0, pl_op.operand);
766 try s.writeAll(", [");
767 for (args, 0..) |arg, i| {
768 if (i != 0) try s.writeAll(", ");
769 try w.writeOperand(s, inst, 1 + i, arg);
770 }
771 try s.writeAll("]");
772 }
773
774 fn writeBr(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
775 const br = w.air.instructions.items(.data)[@intFromEnum(inst)].br;
776 try w.writeInstIndex(s, br.block_inst, false);
777 try s.writeAll(", ");
778 try w.writeOperand(s, inst, 0, br.operand);
779 }
780
781 fn writeRepeat(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
782 const repeat = w.air.instructions.items(.data)[@intFromEnum(inst)].repeat;
783 try w.writeInstIndex(s, repeat.loop_inst, false);
784 }
785
786 fn writeTry(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
787 const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
788 const extra = w.air.extraData(Air.Try, pl_op.payload);
789 const body: []const Air.Inst.Index = @ptrCast(w.air.extra.items[extra.end..][0..extra.data.body_len]);
790 const liveness_condbr: Air.Liveness.CondBrSlices = if (w.liveness) |liveness|
791 liveness.getCondBr(inst)
792 else
793 .{ .then_deaths = &.{}, .else_deaths = &.{} };
794
795 try w.writeOperand(s, inst, 0, pl_op.operand);
796 if (w.skip_body) return s.writeAll(", ...");
797 try s.writeAll(", {\n");
798 const old_indent = w.indent;
799 w.indent += 2;
800
801 if (liveness_condbr.else_deaths.len != 0) {
802 try s.splatByteAll(' ', w.indent);
803 for (liveness_condbr.else_deaths, 0..) |operand, i| {
804 if (i != 0) try s.writeAll(" ");
805 try s.print("{f}!", .{operand});
806 }
807 try s.writeAll("\n");
808 }
809 try w.writeBody(s, body);
810
811 w.indent = old_indent;
812 try s.splatByteAll(' ', w.indent);
813 try s.writeAll("}");
814
815 for (liveness_condbr.then_deaths) |operand| {
816 try s.print(" {f}!", .{operand});
817 }
818 }
819
820 fn writeTryPtr(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
821 const ty_pl = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
822 const extra = w.air.extraData(Air.TryPtr, ty_pl.payload);
823 const body: []const Air.Inst.Index = @ptrCast(w.air.extra.items[extra.end..][0..extra.data.body_len]);
824 const liveness_condbr: Air.Liveness.CondBrSlices = if (w.liveness) |liveness|
825 liveness.getCondBr(inst)
826 else
827 .{ .then_deaths = &.{}, .else_deaths = &.{} };
828
829 try w.writeOperand(s, inst, 0, extra.data.ptr);
830
831 try s.writeAll(", ");
832 try w.writeType(s, ty_pl.ty.toType());
833 if (w.skip_body) return s.writeAll(", ...");
834 try s.writeAll(", {\n");
835 const old_indent = w.indent;
836 w.indent += 2;
837
838 if (liveness_condbr.else_deaths.len != 0) {
839 try s.splatByteAll(' ', w.indent);
840 for (liveness_condbr.else_deaths, 0..) |operand, i| {
841 if (i != 0) try s.writeAll(" ");
842 try s.print("{f}!", .{operand});
843 }
844 try s.writeAll("\n");
845 }
846 try w.writeBody(s, body);
847
848 w.indent = old_indent;
849 try s.splatByteAll(' ', w.indent);
850 try s.writeAll("}");
851
852 for (liveness_condbr.then_deaths) |operand| {
853 try s.print(" {f}!", .{operand});
854 }
855 }
856
857 fn writeCondBr(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
858 const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
859 const extra = w.air.extraData(Air.CondBr, pl_op.payload);
860 const then_body: []const Air.Inst.Index = @ptrCast(w.air.extra.items[extra.end..][0..extra.data.then_body_len]);
861 const else_body: []const Air.Inst.Index = @ptrCast(w.air.extra.items[extra.end + then_body.len ..][0..extra.data.else_body_len]);
862 const liveness_condbr: Air.Liveness.CondBrSlices = if (w.liveness) |liveness|
863 liveness.getCondBr(inst)
864 else
865 .{ .then_deaths = &.{}, .else_deaths = &.{} };
866
867 try w.writeOperand(s, inst, 0, pl_op.operand);
868 if (w.skip_body) return s.writeAll(", ...");
869 try s.writeAll(",");
870 if (extra.data.branch_hints.true != .none) {
871 try s.print(" {s}", .{@tagName(extra.data.branch_hints.true)});
872 }
873 if (extra.data.branch_hints.then_cov != .none) {
874 try s.print(" {s}", .{@tagName(extra.data.branch_hints.then_cov)});
875 }
876 try s.writeAll(" {\n");
877 const old_indent = w.indent;
878 w.indent += 2;
879
880 if (liveness_condbr.then_deaths.len != 0) {
881 try s.splatByteAll(' ', w.indent);
882 for (liveness_condbr.then_deaths, 0..) |operand, i| {
883 if (i != 0) try s.writeAll(" ");
884 try s.print("{f}!", .{operand});
885 }
886 try s.writeAll("\n");
887 }
888
889 try w.writeBody(s, then_body);
890 try s.splatByteAll(' ', old_indent);
891 try s.writeAll("},");
892 if (extra.data.branch_hints.false != .none) {
893 try s.print(" {s}", .{@tagName(extra.data.branch_hints.false)});
894 }
895 if (extra.data.branch_hints.else_cov != .none) {
896 try s.print(" {s}", .{@tagName(extra.data.branch_hints.else_cov)});
897 }
898 try s.writeAll(" {\n");
899
900 if (liveness_condbr.else_deaths.len != 0) {
901 try s.splatByteAll(' ', w.indent);
902 for (liveness_condbr.else_deaths, 0..) |operand, i| {
903 if (i != 0) try s.writeAll(" ");
904 try s.print("{f}!", .{operand});
905 }
906 try s.writeAll("\n");
907 }
908
909 try w.writeBody(s, else_body);
910 w.indent = old_indent;
911
912 try s.splatByteAll(' ', old_indent);
913 try s.writeAll("}");
914 }
915
916 fn writeSwitchBr(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
917 const switch_br = w.air.unwrapSwitch(inst);
918
919 const liveness: Air.Liveness.SwitchBrTable = if (w.liveness) |liveness|
920 liveness.getSwitchBr(w.gpa, inst, switch_br.cases_len + 1) catch
921 @panic("out of memory")
922 else blk: {
923 const slice = w.gpa.alloc([]const Air.Inst.Index, switch_br.cases_len + 1) catch
924 @panic("out of memory");
925 @memset(slice, &.{});
926 break :blk .{ .deaths = slice };
927 };
928 defer w.gpa.free(liveness.deaths);
929
930 try w.writeOperand(s, inst, 0, switch_br.operand);
931 if (w.skip_body) return s.writeAll(", ...");
932 const old_indent = w.indent;
933 w.indent += 2;
934
935 var it = switch_br.iterateCases();
936 while (it.next()) |case| {
937 try s.writeAll(", [");
938 for (case.items, 0..) |item, item_i| {
939 if (item_i != 0) try s.writeAll(", ");
940 try w.writeInstRef(s, item, false);
941 }
942 for (case.ranges, 0..) |range, range_i| {
943 if (range_i != 0 or case.items.len != 0) try s.writeAll(", ");
944 try w.writeInstRef(s, range[0], false);
945 try s.writeAll("...");
946 try w.writeInstRef(s, range[1], false);
947 }
948 try s.writeAll("] ");
949 const hint = switch_br.getHint(case.idx);
950 if (hint != .none) {
951 try s.print(".{s} ", .{@tagName(hint)});
952 }
953 try s.writeAll("=> {\n");
954 w.indent += 2;
955
956 const deaths = liveness.deaths[case.idx];
957 if (deaths.len != 0) {
958 try s.splatByteAll(' ', w.indent);
959 for (deaths, 0..) |operand, i| {
960 if (i != 0) try s.writeAll(" ");
961 try s.print("{f}!", .{operand});
962 }
963 try s.writeAll("\n");
964 }
965
966 try w.writeBody(s, case.body);
967 w.indent -= 2;
968 try s.splatByteAll(' ', w.indent);
969 try s.writeAll("}");
970 }
971
972 const else_body = it.elseBody();
973 if (else_body.len != 0) {
974 try s.writeAll(", else ");
975 const hint = switch_br.getElseHint();
976 if (hint != .none) {
977 try s.print(".{s} ", .{@tagName(hint)});
978 }
979 try s.writeAll("=> {\n");
980 w.indent += 2;
981
982 const deaths = liveness.deaths[liveness.deaths.len - 1];
983 if (deaths.len != 0) {
984 try s.splatByteAll(' ', w.indent);
985 for (deaths, 0..) |operand, i| {
986 if (i != 0) try s.writeAll(" ");
987 try s.print("{f}!", .{operand});
988 }
989 try s.writeAll("\n");
990 }
991
992 try w.writeBody(s, else_body);
993 w.indent -= 2;
994 try s.splatByteAll(' ', w.indent);
995 try s.writeAll("}");
996 }
997
998 try s.writeAll("\n");
999 try s.splatByteAll(' ', old_indent);
1000 }
1001
1002 fn writeWasmMemorySize(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
1003 const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
1004 try s.print("{d}", .{pl_op.payload});
1005 }
1006
1007 fn writeWasmMemoryGrow(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
1008 const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
1009 try s.print("{d}, ", .{pl_op.payload});
1010 try w.writeOperand(s, inst, 0, pl_op.operand);
1011 }
1012
1013 fn writeWorkDimension(w: *Writer, s: *std.Io.Writer, inst: Air.Inst.Index) Error!void {
1014 const pl_op = w.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
1015 try s.print("{d}", .{pl_op.payload});
1016 }
1017
1018 fn writeOperand(
1019 w: *Writer,
1020 s: *std.Io.Writer,
1021 inst: Air.Inst.Index,
1022 op_index: usize,
1023 operand: Air.Inst.Ref,
1024 ) Error!void {
1025 const small_tomb_bits = Air.Liveness.bpi - 1;
1026 const dies = if (w.liveness) |liveness| blk: {
1027 if (op_index < small_tomb_bits)
1028 break :blk liveness.operandDies(inst, @intCast(op_index));
1029 var extra_index = liveness.special.get(inst).?;
1030 var tomb_op_index: usize = small_tomb_bits;
1031 while (true) {
1032 const bits = liveness.extra[extra_index];
1033 if (op_index < tomb_op_index + 31) {
1034 break :blk @as(u1, @truncate(bits >> @as(u5, @intCast(op_index - tomb_op_index)))) != 0;
1035 }
1036 if ((bits >> 31) != 0) break :blk false;
1037 extra_index += 1;
1038 tomb_op_index += 31;
1039 }
1040 } else false;
1041 return w.writeInstRef(s, operand, dies);
1042 }
1043
1044 fn writeInstRef(
1045 w: *Writer,
1046 s: *std.Io.Writer,
1047 operand: Air.Inst.Ref,
1048 dies: bool,
1049 ) Error!void {
1050 if (@intFromEnum(operand) < InternPool.static_len) {
1051 return s.print("@{}", .{operand});
1052 } else if (operand.toInterned()) |ip_index| {
1053 const pt = w.pt;
1054 const ty = Type.fromInterned(pt.zcu.intern_pool.indexToKey(ip_index).typeOf());
1055 try s.print("<{f}, {f}>", .{
1056 ty.fmt(pt),
1057 Value.fromInterned(ip_index).fmtValue(pt),
1058 });
1059 } else {
1060 return w.writeInstIndex(s, operand.toIndex().?, dies);
1061 }
1062 }
1063
1064 fn writeInstIndex(
1065 w: *Writer,
1066 s: *std.Io.Writer,
1067 inst: Air.Inst.Index,
1068 dies: bool,
1069 ) Error!void {
1070 _ = w;
1071 try s.print("{f}", .{inst});
1072 if (dies) try s.writeByte('!');
1073 }
1074
1075 fn typeOfIndex(w: *Writer, inst: Air.Inst.Index) Type {
1076 const zcu = w.pt.zcu;
1077 return w.air.typeOfIndex(inst, &zcu.intern_pool);
1078 }
1079};