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