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