1const std = @import("std");
2const mem = std.mem;
3const Allocator = std.mem.Allocator;
4const assert = std.debug.assert;
5const Ast = std.zig.Ast;
6
7const Zir = @import("Zir.zig");
8const Module = @import("Module.zig");
9const LazySrcLoc = Module.LazySrcLoc;
10
11/// Write human-readable, debug formatted ZIR code to a file.
12pub fn renderAsTextToFile(
13    gpa: Allocator,
14    scope_file: *Module.File,
15    fs_file: std.fs.File,
16) !void {
17    var arena = std.heap.ArenaAllocator.init(gpa);
18    defer arena.deinit();
19
20    var writer: Writer = .{
21        .gpa = gpa,
22        .arena = arena.allocator(),
23        .file = scope_file,
24        .code = scope_file.zir,
25        .indent = 0,
26        .parent_decl_node = 0,
27        .recurse_decls = true,
28        .recurse_blocks = true,
29    };
30
31    var raw_stream = std.io.bufferedWriter(fs_file.writer());
32    const stream = raw_stream.writer();
33
34    const main_struct_inst = Zir.main_struct_inst;
35    try stream.print("%{d} ", .{main_struct_inst});
36    try writer.writeInstToStream(stream, main_struct_inst);
37    try stream.writeAll("\n");
38    const imports_index = scope_file.zir.extra[@enumToInt(Zir.ExtraIndex.imports)];
39    if (imports_index != 0) {
40        try stream.writeAll("Imports:\n");
41
42        const extra = scope_file.zir.extraData(Zir.Inst.Imports, imports_index);
43        var import_i: u32 = 0;
44        var extra_index = extra.end;
45
46        while (import_i < extra.data.imports_len) : (import_i += 1) {
47            const item = scope_file.zir.extraData(Zir.Inst.Imports.Item, extra_index);
48            extra_index = item.end;
49
50            const src: LazySrcLoc = .{ .token_abs = item.data.token };
51            const import_path = scope_file.zir.nullTerminatedString(item.data.name);
52            try stream.print("  @import(\"{}\") ", .{
53                std.zig.fmtEscapes(import_path),
54            });
55            try writer.writeSrc(stream, src);
56            try stream.writeAll("\n");
57        }
58    }
59
60    try raw_stream.flush();
61}
62
63pub fn renderInstructionContext(
64    gpa: Allocator,
65    block: []const Zir.Inst.Index,
66    block_index: usize,
67    scope_file: *Module.File,
68    parent_decl_node: Ast.Node.Index,
69    indent: u32,
70    stream: anytype,
71) !void {
72    var arena = std.heap.ArenaAllocator.init(gpa);
73    defer arena.deinit();
74
75    var writer: Writer = .{
76        .gpa = gpa,
77        .arena = arena.allocator(),
78        .file = scope_file,
79        .code = scope_file.zir,
80        .indent = if (indent < 2) 2 else indent,
81        .parent_decl_node = parent_decl_node,
82        .recurse_decls = false,
83        .recurse_blocks = true,
84    };
85
86    try writer.writeBody(stream, block[0..block_index]);
87    try stream.writeByteNTimes(' ', writer.indent - 2);
88    try stream.print("> %{d} ", .{block[block_index]});
89    try writer.writeInstToStream(stream, block[block_index]);
90    try stream.writeByte('\n');
91    if (block_index + 1 < block.len) {
92        try writer.writeBody(stream, block[block_index + 1 ..]);
93    }
94}
95
96pub fn renderSingleInstruction(
97    gpa: Allocator,
98    inst: Zir.Inst.Index,
99    scope_file: *Module.File,
100    parent_decl_node: Ast.Node.Index,
101    indent: u32,
102    stream: anytype,
103) !void {
104    var arena = std.heap.ArenaAllocator.init(gpa);
105    defer arena.deinit();
106
107    var writer: Writer = .{
108        .gpa = gpa,
109        .arena = arena.allocator(),
110        .file = scope_file,
111        .code = scope_file.zir,
112        .indent = indent,
113        .parent_decl_node = parent_decl_node,
114        .recurse_decls = false,
115        .recurse_blocks = false,
116    };
117
118    try stream.print("%{d} ", .{inst});
119    try writer.writeInstToStream(stream, inst);
120}
121
122const Writer = struct {
123    gpa: Allocator,
124    arena: Allocator,
125    file: *Module.File,
126    code: Zir,
127    indent: u32,
128    parent_decl_node: Ast.Node.Index,
129    recurse_decls: bool,
130    recurse_blocks: bool,
131
132    fn relativeToNodeIndex(self: *Writer, offset: i32) Ast.Node.Index {
133        return @bitCast(Ast.Node.Index, offset + @bitCast(i32, self.parent_decl_node));
134    }
135
136    fn writeInstToStream(
137        self: *Writer,
138        stream: anytype,
139        inst: Zir.Inst.Index,
140    ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
141        const tags = self.code.instructions.items(.tag);
142        const tag = tags[inst];
143        try stream.print("= {s}(", .{@tagName(tags[inst])});
144        switch (tag) {
145            .array_type,
146            .as,
147            .coerce_result_ptr,
148            .elem_ptr,
149            .elem_val,
150            .store,
151            .store_to_block_ptr,
152            .store_to_inferred_ptr,
153            .field_ptr_type,
154            => try self.writeBin(stream, inst),
155
156            .alloc,
157            .alloc_mut,
158            .alloc_comptime,
159            .indexable_ptr_len,
160            .anyframe_type,
161            .bit_not,
162            .bool_not,
163            .negate,
164            .negate_wrap,
165            .load,
166            .ensure_result_used,
167            .ensure_result_non_error,
168            .ret_node,
169            .ret_load,
170            .resolve_inferred_alloc,
171            .optional_type,
172            .optional_payload_safe,
173            .optional_payload_unsafe,
174            .optional_payload_safe_ptr,
175            .optional_payload_unsafe_ptr,
176            .err_union_payload_safe,
177            .err_union_payload_unsafe,
178            .err_union_payload_safe_ptr,
179            .err_union_payload_unsafe_ptr,
180            .err_union_code,
181            .err_union_code_ptr,
182            .is_non_null,
183            .is_non_null_ptr,
184            .is_non_err,
185            .is_non_err_ptr,
186            .typeof,
187            .struct_init_empty,
188            .type_info,
189            .size_of,
190            .bit_size_of,
191            .typeof_log2_int_type,
192            .log2_int_type,
193            .ptr_to_int,
194            .error_to_int,
195            .int_to_error,
196            .compile_error,
197            .set_eval_branch_quota,
198            .enum_to_int,
199            .align_of,
200            .bool_to_int,
201            .embed_file,
202            .error_name,
203            .panic,
204            .set_align_stack,
205            .set_cold,
206            .set_float_mode,
207            .set_runtime_safety,
208            .sqrt,
209            .sin,
210            .cos,
211            .exp,
212            .exp2,
213            .log,
214            .log2,
215            .log10,
216            .fabs,
217            .floor,
218            .ceil,
219            .trunc,
220            .round,
221            .tag_name,
222            .reify,
223            .type_name,
224            .frame_type,
225            .frame_size,
226            .clz,
227            .ctz,
228            .pop_count,
229            .byte_swap,
230            .bit_reverse,
231            .elem_type,
232            .@"resume",
233            .@"await",
234            .await_nosuspend,
235            .fence,
236            .switch_cond,
237            .switch_cond_ref,
238            => try self.writeUnNode(stream, inst),
239
240            .ref,
241            .ret_coerce,
242            .ensure_err_payload_void,
243            .closure_capture,
244            => try self.writeUnTok(stream, inst),
245
246            .bool_br_and,
247            .bool_br_or,
248            => try self.writeBoolBr(stream, inst),
249
250            .array_type_sentinel => try self.writeArrayTypeSentinel(stream, inst),
251            .ptr_type_simple => try self.writePtrTypeSimple(stream, inst),
252            .ptr_type => try self.writePtrType(stream, inst),
253            .int => try self.writeInt(stream, inst),
254            .int_big => try self.writeIntBig(stream, inst),
255            .float => try self.writeFloat(stream, inst),
256            .float128 => try self.writeFloat128(stream, inst),
257            .str => try self.writeStr(stream, inst),
258            .int_type => try self.writeIntType(stream, inst),
259
260            .@"break",
261            .break_inline,
262            => try self.writeBreak(stream, inst),
263            .array_init,
264            .array_init_ref,
265            .array_init_anon,
266            .array_init_anon_ref,
267            => try self.writeArrayInit(stream, inst),
268
269            .slice_start => try self.writeSliceStart(stream, inst),
270            .slice_end => try self.writeSliceEnd(stream, inst),
271            .slice_sentinel => try self.writeSliceSentinel(stream, inst),
272
273            .union_init_ptr => try self.writeUnionInitPtr(stream, inst),
274
275            .struct_init,
276            .struct_init_ref,
277            => try self.writeStructInit(stream, inst),
278
279            .cmpxchg_strong, .cmpxchg_weak => try self.writeCmpxchg(stream, inst),
280            .atomic_store => try self.writeAtomicStore(stream, inst),
281            .atomic_rmw => try self.writeAtomicRmw(stream, inst),
282            .memcpy => try self.writeMemcpy(stream, inst),
283            .memset => try self.writeMemset(stream, inst),
284            .shuffle => try self.writeShuffle(stream, inst),
285            .select => try self.writeSelect(stream, inst),
286            .mul_add => try self.writeMulAdd(stream, inst),
287            .field_parent_ptr => try self.writeFieldParentPtr(stream, inst),
288            .builtin_call => try self.writeBuiltinCall(stream, inst),
289            .builtin_async_call => try self.writeBuiltinAsyncCall(stream, inst),
290
291            .struct_init_anon,
292            .struct_init_anon_ref,
293            => try self.writeStructInitAnon(stream, inst),
294
295            .field_type => try self.writeFieldType(stream, inst),
296            .field_type_ref => try self.writeFieldTypeRef(stream, inst),
297
298            .add,
299            .addwrap,
300            .add_sat,
301            .array_cat,
302            .array_mul,
303            .mul,
304            .mulwrap,
305            .mul_sat,
306            .sub,
307            .subwrap,
308            .sub_sat,
309            .cmp_lt,
310            .cmp_lte,
311            .cmp_eq,
312            .cmp_gte,
313            .cmp_gt,
314            .cmp_neq,
315            .div,
316            .has_decl,
317            .has_field,
318            .mod_rem,
319            .shl,
320            .shl_exact,
321            .shl_sat,
322            .shr,
323            .shr_exact,
324            .xor,
325            .store_node,
326            .error_union_type,
327            .merge_error_sets,
328            .bit_and,
329            .bit_or,
330            .float_to_int,
331            .int_to_float,
332            .int_to_ptr,
333            .int_to_enum,
334            .float_cast,
335            .int_cast,
336            .err_set_cast,
337            .ptr_cast,
338            .truncate,
339            .align_cast,
340            .div_exact,
341            .div_floor,
342            .div_trunc,
343            .mod,
344            .rem,
345            .bit_offset_of,
346            .offset_of,
347            .splat,
348            .reduce,
349            .atomic_load,
350            .bitcast,
351            .vector_type,
352            .maximum,
353            .minimum,
354            .elem_ptr_node,
355            .elem_val_node,
356            => try self.writePlNodeBin(stream, inst),
357
358            .elem_ptr_imm => try self.writeElemPtrImm(stream, inst),
359
360            .@"export" => try self.writePlNodeExport(stream, inst),
361            .export_value => try self.writePlNodeExportValue(stream, inst),
362
363            .call => try self.writePlNodeCall(stream, inst),
364
365            .block,
366            .block_inline,
367            .suspend_block,
368            .loop,
369            .validate_struct_init,
370            .validate_array_init,
371            .c_import,
372            => try self.writePlNodeBlock(stream, inst),
373
374            .condbr,
375            .condbr_inline,
376            => try self.writePlNodeCondBr(stream, inst),
377
378            .error_set_decl => try self.writeErrorSetDecl(stream, inst, .parent),
379            .error_set_decl_anon => try self.writeErrorSetDecl(stream, inst, .anon),
380            .error_set_decl_func => try self.writeErrorSetDecl(stream, inst, .func),
381
382            .switch_block => try self.writePlNodeSwitchBlock(stream, inst),
383
384            .field_ptr,
385            .field_val,
386            .field_call_bind,
387            => try self.writePlNodeField(stream, inst),
388
389            .field_ptr_named,
390            .field_val_named,
391            .field_call_bind_named,
392            => try self.writePlNodeFieldNamed(stream, inst),
393
394            .as_node => try self.writeAs(stream, inst),
395
396            .breakpoint,
397            .repeat,
398            .repeat_inline,
399            .alloc_inferred,
400            .alloc_inferred_mut,
401            .alloc_inferred_comptime,
402            => try self.writeNode(stream, inst),
403
404            .error_value,
405            .enum_literal,
406            .decl_ref,
407            .decl_val,
408            .import,
409            .ret_err_value,
410            .ret_err_value_code,
411            .param_anytype,
412            .param_anytype_comptime,
413            => try self.writeStrTok(stream, inst),
414
415            .param, .param_comptime => try self.writeParam(stream, inst),
416
417            .func => try self.writeFunc(stream, inst, false),
418            .func_inferred => try self.writeFunc(stream, inst, true),
419
420            .@"unreachable" => try self.writeUnreachable(stream, inst),
421
422            .switch_capture,
423            .switch_capture_ref,
424            .switch_capture_multi,
425            .switch_capture_multi_ref,
426            .switch_capture_else,
427            .switch_capture_else_ref,
428            => try self.writeSwitchCapture(stream, inst),
429
430            .dbg_stmt => try self.writeDbgStmt(stream, inst),
431
432            .closure_get => try self.writeInstNode(stream, inst),
433
434            .extended => try self.writeExtended(stream, inst),
435        }
436    }
437
438    fn writeExtended(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
439        const extended = self.code.instructions.items(.data)[inst].extended;
440        try stream.print("{s}(", .{@tagName(extended.opcode)});
441        switch (extended.opcode) {
442            .ret_ptr,
443            .ret_type,
444            .this,
445            .ret_addr,
446            .error_return_trace,
447            .frame,
448            .frame_address,
449            .builtin_src,
450            => try self.writeExtNode(stream, extended),
451
452            .@"asm" => try self.writeAsm(stream, extended),
453            .func => try self.writeFuncExtended(stream, extended),
454            .variable => try self.writeVarExtended(stream, extended),
455            .alloc => try self.writeAllocExtended(stream, extended),
456
457            .compile_log,
458            .typeof_peer,
459            => try self.writeNodeMultiOp(stream, extended),
460
461            .add_with_overflow,
462            .sub_with_overflow,
463            .mul_with_overflow,
464            .shl_with_overflow,
465            => try self.writeOverflowArithmetic(stream, extended),
466
467            .struct_decl => try self.writeStructDecl(stream, extended),
468            .union_decl => try self.writeUnionDecl(stream, extended),
469            .enum_decl => try self.writeEnumDecl(stream, extended),
470            .opaque_decl => try self.writeOpaqueDecl(stream, extended),
471
472            .c_undef, .c_include, .wasm_memory_size => {
473                const inst_data = self.code.extraData(Zir.Inst.UnNode, extended.operand).data;
474                const src: LazySrcLoc = .{ .node_offset = inst_data.node };
475                try self.writeInstRef(stream, inst_data.operand);
476                try stream.writeAll(")) ");
477                try self.writeSrc(stream, src);
478            },
479
480            .builtin_extern, .c_define, .wasm_memory_grow, .prefetch => {
481                const inst_data = self.code.extraData(Zir.Inst.BinNode, extended.operand).data;
482                const src: LazySrcLoc = .{ .node_offset = inst_data.node };
483                try self.writeInstRef(stream, inst_data.lhs);
484                try stream.writeAll(", ");
485                try self.writeInstRef(stream, inst_data.rhs);
486                try stream.writeAll(")) ");
487                try self.writeSrc(stream, src);
488            },
489        }
490    }
491
492    fn writeExtNode(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {
493        const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
494        try stream.writeAll(")) ");
495        try self.writeSrc(stream, src);
496    }
497
498    fn writeBin(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
499        const inst_data = self.code.instructions.items(.data)[inst].bin;
500        try self.writeInstRef(stream, inst_data.lhs);
501        try stream.writeAll(", ");
502        try self.writeInstRef(stream, inst_data.rhs);
503        try stream.writeByte(')');
504    }
505
506    fn writeUnNode(
507        self: *Writer,
508        stream: anytype,
509        inst: Zir.Inst.Index,
510    ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
511        const inst_data = self.code.instructions.items(.data)[inst].un_node;
512        try self.writeInstRef(stream, inst_data.operand);
513        try stream.writeAll(") ");
514        try self.writeSrc(stream, inst_data.src());
515    }
516
517    fn writeUnTok(
518        self: *Writer,
519        stream: anytype,
520        inst: Zir.Inst.Index,
521    ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
522        const inst_data = self.code.instructions.items(.data)[inst].un_tok;
523        try self.writeInstRef(stream, inst_data.operand);
524        try stream.writeAll(") ");
525        try self.writeSrc(stream, inst_data.src());
526    }
527
528    fn writeArrayTypeSentinel(
529        self: *Writer,
530        stream: anytype,
531        inst: Zir.Inst.Index,
532    ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
533        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
534        const extra = self.code.extraData(Zir.Inst.ArrayTypeSentinel, inst_data.payload_index).data;
535        try self.writeInstRef(stream, extra.len);
536        try stream.writeAll(", ");
537        try self.writeInstRef(stream, extra.sentinel);
538        try stream.writeAll(", ");
539        try self.writeInstRef(stream, extra.elem_type);
540        try stream.writeAll(") ");
541        try self.writeSrc(stream, inst_data.src());
542    }
543
544    fn writePtrTypeSimple(
545        self: *Writer,
546        stream: anytype,
547        inst: Zir.Inst.Index,
548    ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
549        const inst_data = self.code.instructions.items(.data)[inst].ptr_type_simple;
550        const str_allowzero = if (inst_data.is_allowzero) "allowzero, " else "";
551        const str_const = if (!inst_data.is_mutable) "const, " else "";
552        const str_volatile = if (inst_data.is_volatile) "volatile, " else "";
553        try self.writeInstRef(stream, inst_data.elem_type);
554        try stream.print(", {s}{s}{s}{s})", .{
555            str_allowzero,
556            str_const,
557            str_volatile,
558            @tagName(inst_data.size),
559        });
560    }
561
562    fn writePtrType(
563        self: *Writer,
564        stream: anytype,
565        inst: Zir.Inst.Index,
566    ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
567        const inst_data = self.code.instructions.items(.data)[inst].ptr_type;
568        const str_allowzero = if (inst_data.flags.is_allowzero) "allowzero, " else "";
569        const str_const = if (!inst_data.flags.is_mutable) "const, " else "";
570        const str_volatile = if (inst_data.flags.is_volatile) "volatile, " else "";
571        const extra = self.code.extraData(Zir.Inst.PtrType, inst_data.payload_index);
572        try self.writeInstRef(stream, extra.data.elem_type);
573        try stream.print(", {s}{s}{s}{s}", .{
574            str_allowzero,
575            str_const,
576            str_volatile,
577            @tagName(inst_data.size),
578        });
579        var extra_index = extra.end;
580        if (inst_data.flags.has_sentinel) {
581            try stream.writeAll(", ");
582            try self.writeInstRef(stream, @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]));
583            extra_index += 1;
584        }
585        if (inst_data.flags.has_align) {
586            try stream.writeAll(", align(");
587            try self.writeInstRef(stream, @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]));
588            extra_index += 1;
589            if (inst_data.flags.has_bit_range) {
590                const bit_start = extra_index + @boolToInt(inst_data.flags.has_addrspace);
591                try stream.writeAll(":");
592                try self.writeInstRef(stream, @intToEnum(Zir.Inst.Ref, self.code.extra[bit_start]));
593                try stream.writeAll(":");
594                try self.writeInstRef(stream, @intToEnum(Zir.Inst.Ref, self.code.extra[bit_start + 1]));
595            }
596            try stream.writeAll(")");
597        }
598        if (inst_data.flags.has_addrspace) {
599            try stream.writeAll(", addrspace(");
600            try self.writeInstRef(stream, @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]));
601            try stream.writeAll(")");
602        }
603        try stream.writeAll(")");
604    }
605
606    fn writeInt(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
607        const inst_data = self.code.instructions.items(.data)[inst].int;
608        try stream.print("{d})", .{inst_data});
609    }
610
611    fn writeIntBig(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
612        const inst_data = self.code.instructions.items(.data)[inst].str;
613        const byte_count = inst_data.len * @sizeOf(std.math.big.Limb);
614        const limb_bytes = self.code.string_bytes[inst_data.start..][0..byte_count];
615        // limb_bytes is not aligned properly; we must allocate and copy the bytes
616        // in order to accomplish this.
617        const limbs = try self.gpa.alloc(std.math.big.Limb, inst_data.len);
618        defer self.gpa.free(limbs);
619
620        mem.copy(u8, mem.sliceAsBytes(limbs), limb_bytes);
621        const big_int: std.math.big.int.Const = .{
622            .limbs = limbs,
623            .positive = true,
624        };
625        const as_string = try big_int.toStringAlloc(self.gpa, 10, .lower);
626        defer self.gpa.free(as_string);
627        try stream.print("{s})", .{as_string});
628    }
629
630    fn writeFloat(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
631        const number = self.code.instructions.items(.data)[inst].float;
632        try stream.print("{d})", .{number});
633    }
634
635    fn writeFloat128(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
636        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
637        const extra = self.code.extraData(Zir.Inst.Float128, inst_data.payload_index).data;
638        const src = inst_data.src();
639        const number = extra.get();
640        // TODO improve std.format to be able to print f128 values
641        try stream.print("{d}) ", .{@floatCast(f64, number)});
642        try self.writeSrc(stream, src);
643    }
644
645    fn writeStr(
646        self: *Writer,
647        stream: anytype,
648        inst: Zir.Inst.Index,
649    ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
650        const inst_data = self.code.instructions.items(.data)[inst].str;
651        const str = inst_data.get(self.code);
652        try stream.print("\"{}\")", .{std.zig.fmtEscapes(str)});
653    }
654
655    fn writeSliceStart(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
656        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
657        const extra = self.code.extraData(Zir.Inst.SliceStart, inst_data.payload_index).data;
658        try self.writeInstRef(stream, extra.lhs);
659        try stream.writeAll(", ");
660        try self.writeInstRef(stream, extra.start);
661        try stream.writeAll(") ");
662        try self.writeSrc(stream, inst_data.src());
663    }
664
665    fn writeSliceEnd(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
666        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
667        const extra = self.code.extraData(Zir.Inst.SliceEnd, inst_data.payload_index).data;
668        try self.writeInstRef(stream, extra.lhs);
669        try stream.writeAll(", ");
670        try self.writeInstRef(stream, extra.start);
671        try stream.writeAll(", ");
672        try self.writeInstRef(stream, extra.end);
673        try stream.writeAll(") ");
674        try self.writeSrc(stream, inst_data.src());
675    }
676
677    fn writeSliceSentinel(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
678        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
679        const extra = self.code.extraData(Zir.Inst.SliceSentinel, inst_data.payload_index).data;
680        try self.writeInstRef(stream, extra.lhs);
681        try stream.writeAll(", ");
682        try self.writeInstRef(stream, extra.start);
683        try stream.writeAll(", ");
684        try self.writeInstRef(stream, extra.end);
685        try stream.writeAll(", ");
686        try self.writeInstRef(stream, extra.sentinel);
687        try stream.writeAll(") ");
688        try self.writeSrc(stream, inst_data.src());
689    }
690
691    fn writeUnionInitPtr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
692        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
693        const extra = self.code.extraData(Zir.Inst.UnionInitPtr, inst_data.payload_index).data;
694        try self.writeInstRef(stream, extra.result_ptr);
695        try stream.writeAll(", ");
696        try self.writeInstRef(stream, extra.union_type);
697        try stream.writeAll(", ");
698        try self.writeInstRef(stream, extra.field_name);
699        try stream.writeAll(") ");
700        try self.writeSrc(stream, inst_data.src());
701    }
702
703    fn writeShuffle(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
704        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
705        const extra = self.code.extraData(Zir.Inst.Shuffle, inst_data.payload_index).data;
706        try self.writeInstRef(stream, extra.elem_type);
707        try stream.writeAll(", ");
708        try self.writeInstRef(stream, extra.a);
709        try stream.writeAll(", ");
710        try self.writeInstRef(stream, extra.b);
711        try stream.writeAll(", ");
712        try self.writeInstRef(stream, extra.mask);
713        try stream.writeAll(") ");
714        try self.writeSrc(stream, inst_data.src());
715    }
716
717    fn writeSelect(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
718        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
719        const extra = self.code.extraData(Zir.Inst.Select, inst_data.payload_index).data;
720        try self.writeInstRef(stream, extra.elem_type);
721        try stream.writeAll(", ");
722        try self.writeInstRef(stream, extra.pred);
723        try stream.writeAll(", ");
724        try self.writeInstRef(stream, extra.a);
725        try stream.writeAll(", ");
726        try self.writeInstRef(stream, extra.b);
727        try stream.writeAll(") ");
728        try self.writeSrc(stream, inst_data.src());
729    }
730
731    fn writeMulAdd(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
732        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
733        const extra = self.code.extraData(Zir.Inst.MulAdd, inst_data.payload_index).data;
734        try self.writeInstRef(stream, extra.mulend1);
735        try stream.writeAll(", ");
736        try self.writeInstRef(stream, extra.mulend2);
737        try stream.writeAll(", ");
738        try self.writeInstRef(stream, extra.addend);
739        try stream.writeAll(") ");
740        try self.writeSrc(stream, inst_data.src());
741    }
742
743    fn writeBuiltinCall(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
744        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
745        const extra = self.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index).data;
746        try self.writeInstRef(stream, extra.options);
747        try stream.writeAll(", ");
748        try self.writeInstRef(stream, extra.callee);
749        try stream.writeAll(", ");
750        try self.writeInstRef(stream, extra.args);
751        try stream.writeAll(") ");
752        try self.writeSrc(stream, inst_data.src());
753    }
754
755    fn writeFieldParentPtr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
756        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
757        const extra = self.code.extraData(Zir.Inst.FieldParentPtr, inst_data.payload_index).data;
758        try self.writeInstRef(stream, extra.parent_type);
759        try stream.writeAll(", ");
760        try self.writeInstRef(stream, extra.field_name);
761        try stream.writeAll(", ");
762        try self.writeInstRef(stream, extra.field_ptr);
763        try stream.writeAll(") ");
764        try self.writeSrc(stream, inst_data.src());
765    }
766
767    fn writeBuiltinAsyncCall(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
768        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
769        const extra = self.code.extraData(Zir.Inst.AsyncCall, inst_data.payload_index).data;
770        try self.writeInstRef(stream, extra.frame_buffer);
771        try stream.writeAll(", ");
772        try self.writeInstRef(stream, extra.result_ptr);
773        try stream.writeAll(", ");
774        try self.writeInstRef(stream, extra.fn_ptr);
775        try stream.writeAll(", ");
776        try self.writeInstRef(stream, extra.args);
777        try stream.writeAll(") ");
778        try self.writeSrc(stream, inst_data.src());
779    }
780
781    fn writeParam(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
782        const inst_data = self.code.instructions.items(.data)[inst].pl_tok;
783        const extra = self.code.extraData(Zir.Inst.Param, inst_data.payload_index);
784        const body = self.code.extra[extra.end..][0..extra.data.body_len];
785        try stream.print("\"{}\", ", .{
786            std.zig.fmtEscapes(self.code.nullTerminatedString(extra.data.name)),
787        });
788        try self.writeBracedBody(stream, body);
789        try stream.writeAll(") ");
790        try self.writeSrc(stream, inst_data.src());
791    }
792
793    fn writePlNodeBin(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
794        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
795        const extra = self.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
796        try self.writeInstRef(stream, extra.lhs);
797        try stream.writeAll(", ");
798        try self.writeInstRef(stream, extra.rhs);
799        try stream.writeAll(") ");
800        try self.writeSrc(stream, inst_data.src());
801    }
802
803    fn writeElemPtrImm(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
804        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
805        const extra = self.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data;
806
807        try self.writeInstRef(stream, extra.ptr);
808        try stream.print(", {d}) ", .{extra.index});
809        try self.writeSrc(stream, inst_data.src());
810    }
811
812    fn writePlNodeExport(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
813        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
814        const extra = self.code.extraData(Zir.Inst.Export, inst_data.payload_index).data;
815        const decl_name = self.code.nullTerminatedString(extra.decl_name);
816
817        try self.writeInstRef(stream, extra.namespace);
818        try stream.print(", {}, ", .{std.zig.fmtId(decl_name)});
819        try self.writeInstRef(stream, extra.options);
820        try stream.writeAll(") ");
821        try self.writeSrc(stream, inst_data.src());
822    }
823
824    fn writePlNodeExportValue(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
825        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
826        const extra = self.code.extraData(Zir.Inst.ExportValue, inst_data.payload_index).data;
827
828        try self.writeInstRef(stream, extra.operand);
829        try stream.writeAll(", ");
830        try self.writeInstRef(stream, extra.options);
831        try stream.writeAll(") ");
832        try self.writeSrc(stream, inst_data.src());
833    }
834
835    fn writeStructInit(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
836        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
837        const extra = self.code.extraData(Zir.Inst.StructInit, inst_data.payload_index);
838        var field_i: u32 = 0;
839        var extra_index = extra.end;
840
841        while (field_i < extra.data.fields_len) : (field_i += 1) {
842            const item = self.code.extraData(Zir.Inst.StructInit.Item, extra_index);
843            extra_index = item.end;
844
845            if (field_i != 0) {
846                try stream.writeAll(", [");
847            } else {
848                try stream.writeAll("[");
849            }
850            try self.writeInstIndex(stream, item.data.field_type);
851            try stream.writeAll(", ");
852            try self.writeInstRef(stream, item.data.init);
853            try stream.writeAll("]");
854        }
855        try stream.writeAll(") ");
856        try self.writeSrc(stream, inst_data.src());
857    }
858
859    fn writeCmpxchg(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
860        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
861        const extra = self.code.extraData(Zir.Inst.Cmpxchg, inst_data.payload_index).data;
862
863        try self.writeInstRef(stream, extra.ptr);
864        try stream.writeAll(", ");
865        try self.writeInstRef(stream, extra.expected_value);
866        try stream.writeAll(", ");
867        try self.writeInstRef(stream, extra.new_value);
868        try stream.writeAll(", ");
869        try self.writeInstRef(stream, extra.success_order);
870        try stream.writeAll(", ");
871        try self.writeInstRef(stream, extra.failure_order);
872        try stream.writeAll(") ");
873        try self.writeSrc(stream, inst_data.src());
874    }
875
876    fn writeAtomicStore(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
877        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
878        const extra = self.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data;
879
880        try self.writeInstRef(stream, extra.ptr);
881        try stream.writeAll(", ");
882        try self.writeInstRef(stream, extra.operand);
883        try stream.writeAll(", ");
884        try self.writeInstRef(stream, extra.ordering);
885        try stream.writeAll(") ");
886        try self.writeSrc(stream, inst_data.src());
887    }
888
889    fn writeAtomicRmw(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
890        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
891        const extra = self.code.extraData(Zir.Inst.AtomicRmw, inst_data.payload_index).data;
892
893        try self.writeInstRef(stream, extra.ptr);
894        try stream.writeAll(", ");
895        try self.writeInstRef(stream, extra.operation);
896        try stream.writeAll(", ");
897        try self.writeInstRef(stream, extra.operand);
898        try stream.writeAll(", ");
899        try self.writeInstRef(stream, extra.ordering);
900        try stream.writeAll(") ");
901        try self.writeSrc(stream, inst_data.src());
902    }
903
904    fn writeMemcpy(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
905        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
906        const extra = self.code.extraData(Zir.Inst.Memcpy, inst_data.payload_index).data;
907
908        try self.writeInstRef(stream, extra.dest);
909        try stream.writeAll(", ");
910        try self.writeInstRef(stream, extra.source);
911        try stream.writeAll(", ");
912        try self.writeInstRef(stream, extra.byte_count);
913        try stream.writeAll(") ");
914        try self.writeSrc(stream, inst_data.src());
915    }
916
917    fn writeMemset(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
918        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
919        const extra = self.code.extraData(Zir.Inst.Memset, inst_data.payload_index).data;
920
921        try self.writeInstRef(stream, extra.dest);
922        try stream.writeAll(", ");
923        try self.writeInstRef(stream, extra.byte);
924        try stream.writeAll(", ");
925        try self.writeInstRef(stream, extra.byte_count);
926        try stream.writeAll(") ");
927        try self.writeSrc(stream, inst_data.src());
928    }
929
930    fn writeStructInitAnon(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
931        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
932        const extra = self.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index);
933        var field_i: u32 = 0;
934        var extra_index = extra.end;
935
936        while (field_i < extra.data.fields_len) : (field_i += 1) {
937            const item = self.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index);
938            extra_index = item.end;
939
940            const field_name = self.code.nullTerminatedString(item.data.field_name);
941
942            const prefix = if (field_i != 0) ", [" else "[";
943            try stream.print("{s}{s}=", .{ prefix, field_name });
944            try self.writeInstRef(stream, item.data.init);
945            try stream.writeAll("]");
946        }
947        try stream.writeAll(") ");
948        try self.writeSrc(stream, inst_data.src());
949    }
950
951    fn writeFieldType(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
952        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
953        const extra = self.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data;
954        try self.writeInstRef(stream, extra.container_type);
955        const field_name = self.code.nullTerminatedString(extra.name_start);
956        try stream.print(", {s}) ", .{field_name});
957        try self.writeSrc(stream, inst_data.src());
958    }
959
960    fn writeFieldTypeRef(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
961        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
962        const extra = self.code.extraData(Zir.Inst.FieldTypeRef, inst_data.payload_index).data;
963        try self.writeInstRef(stream, extra.container_type);
964        try stream.writeAll(", ");
965        try self.writeInstRef(stream, extra.field_name);
966        try stream.writeAll(") ");
967        try self.writeSrc(stream, inst_data.src());
968    }
969
970    fn writeNodeMultiOp(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {
971        const extra = self.code.extraData(Zir.Inst.NodeMultiOp, extended.operand);
972        const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };
973        const operands = self.code.refSlice(extra.end, extended.small);
974
975        for (operands) |operand, i| {
976            if (i != 0) try stream.writeAll(", ");
977            try self.writeInstRef(stream, operand);
978        }
979        try stream.writeAll(")) ");
980        try self.writeSrc(stream, src);
981    }
982
983    fn writeInstNode(
984        self: *Writer,
985        stream: anytype,
986        inst: Zir.Inst.Index,
987    ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
988        const inst_data = self.code.instructions.items(.data)[inst].inst_node;
989        try self.writeInstIndex(stream, inst_data.inst);
990        try stream.writeAll(") ");
991        try self.writeSrc(stream, inst_data.src());
992    }
993
994    fn writeAsm(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {
995        const extra = self.code.extraData(Zir.Inst.Asm, extended.operand);
996        const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };
997        const outputs_len = @truncate(u5, extended.small);
998        const inputs_len = @truncate(u5, extended.small >> 5);
999        const clobbers_len = @truncate(u5, extended.small >> 10);
1000        const is_volatile = @truncate(u1, extended.small >> 15) != 0;
1001        const asm_source = self.code.nullTerminatedString(extra.data.asm_source);
1002
1003        try self.writeFlag(stream, "volatile, ", is_volatile);
1004        try stream.print("\"{}\", ", .{std.zig.fmtEscapes(asm_source)});
1005        try stream.writeAll(", ");
1006
1007        var extra_i: usize = extra.end;
1008        var output_type_bits = extra.data.output_type_bits;
1009        {
1010            var i: usize = 0;
1011            while (i < outputs_len) : (i += 1) {
1012                const output = self.code.extraData(Zir.Inst.Asm.Output, extra_i);
1013                extra_i = output.end;
1014
1015                const is_type = @truncate(u1, output_type_bits) != 0;
1016                output_type_bits >>= 1;
1017
1018                const name = self.code.nullTerminatedString(output.data.name);
1019                const constraint = self.code.nullTerminatedString(output.data.constraint);
1020                try stream.print("output({}, \"{}\", ", .{
1021                    std.zig.fmtId(name), std.zig.fmtEscapes(constraint),
1022                });
1023                try self.writeFlag(stream, "->", is_type);
1024                try self.writeInstRef(stream, output.data.operand);
1025                try stream.writeAll(")");
1026                if (i + 1 < outputs_len) {
1027                    try stream.writeAll("), ");
1028                }
1029            }
1030        }
1031        {
1032            var i: usize = 0;
1033            while (i < inputs_len) : (i += 1) {
1034                const input = self.code.extraData(Zir.Inst.Asm.Input, extra_i);
1035                extra_i = input.end;
1036
1037                const name = self.code.nullTerminatedString(input.data.name);
1038                const constraint = self.code.nullTerminatedString(input.data.constraint);
1039                try stream.print("input({}, \"{}\", ", .{
1040                    std.zig.fmtId(name), std.zig.fmtEscapes(constraint),
1041                });
1042                try self.writeInstRef(stream, input.data.operand);
1043                try stream.writeAll(")");
1044                if (i + 1 < inputs_len) {
1045                    try stream.writeAll(", ");
1046                }
1047            }
1048        }
1049        {
1050            var i: usize = 0;
1051            while (i < clobbers_len) : (i += 1) {
1052                const str_index = self.code.extra[extra_i];
1053                extra_i += 1;
1054                const clobber = self.code.nullTerminatedString(str_index);
1055                try stream.print("{}", .{std.zig.fmtId(clobber)});
1056                if (i + 1 < clobbers_len) {
1057                    try stream.writeAll(", ");
1058                }
1059            }
1060        }
1061        try stream.writeAll(")) ");
1062        try self.writeSrc(stream, src);
1063    }
1064
1065    fn writeOverflowArithmetic(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {
1066        const extra = self.code.extraData(Zir.Inst.OverflowArithmetic, extended.operand).data;
1067        const src: LazySrcLoc = .{ .node_offset = extra.node };
1068
1069        try self.writeInstRef(stream, extra.lhs);
1070        try stream.writeAll(", ");
1071        try self.writeInstRef(stream, extra.rhs);
1072        try stream.writeAll(", ");
1073        try self.writeInstRef(stream, extra.ptr);
1074        try stream.writeAll(")) ");
1075        try self.writeSrc(stream, src);
1076    }
1077
1078    fn writePlNodeCall(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1079        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1080        const extra = self.code.extraData(Zir.Inst.Call, inst_data.payload_index);
1081        const args = self.code.refSlice(extra.end, extra.data.flags.args_len);
1082
1083        if (extra.data.flags.ensure_result_used) {
1084            try stream.writeAll("nodiscard ");
1085        }
1086        try stream.print(".{s}, ", .{@tagName(@intToEnum(std.builtin.CallOptions.Modifier, extra.data.flags.packed_modifier))});
1087        try self.writeInstRef(stream, extra.data.callee);
1088        try stream.writeAll(", [");
1089        for (args) |arg, i| {
1090            if (i != 0) try stream.writeAll(", ");
1091            try self.writeInstRef(stream, arg);
1092        }
1093        try stream.writeAll("]) ");
1094        try self.writeSrc(stream, inst_data.src());
1095    }
1096
1097    fn writePlNodeBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1098        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1099        try self.writePlNodeBlockWithoutSrc(stream, inst);
1100        try self.writeSrc(stream, inst_data.src());
1101    }
1102
1103    fn writePlNodeBlockWithoutSrc(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1104        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1105        const extra = self.code.extraData(Zir.Inst.Block, inst_data.payload_index);
1106        const body = self.code.extra[extra.end..][0..extra.data.body_len];
1107        try self.writeBracedBody(stream, body);
1108        try stream.writeAll(") ");
1109    }
1110
1111    fn writePlNodeCondBr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1112        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1113        const extra = self.code.extraData(Zir.Inst.CondBr, inst_data.payload_index);
1114        const then_body = self.code.extra[extra.end..][0..extra.data.then_body_len];
1115        const else_body = self.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
1116        try self.writeInstRef(stream, extra.data.condition);
1117        try stream.writeAll(", ");
1118        try self.writeBracedBody(stream, then_body);
1119        try stream.writeAll(", ");
1120        try self.writeBracedBody(stream, else_body);
1121        try stream.writeAll(") ");
1122        try self.writeSrc(stream, inst_data.src());
1123    }
1124
1125    fn writeStructDecl(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {
1126        const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small);
1127
1128        var extra_index: usize = extended.operand;
1129
1130        const src_node: ?i32 = if (small.has_src_node) blk: {
1131            const src_node = @bitCast(i32, self.code.extra[extra_index]);
1132            extra_index += 1;
1133            break :blk src_node;
1134        } else null;
1135
1136        const body_len = if (small.has_body_len) blk: {
1137            const body_len = self.code.extra[extra_index];
1138            extra_index += 1;
1139            break :blk body_len;
1140        } else 0;
1141
1142        const fields_len = if (small.has_fields_len) blk: {
1143            const fields_len = self.code.extra[extra_index];
1144            extra_index += 1;
1145            break :blk fields_len;
1146        } else 0;
1147
1148        const decls_len = if (small.has_decls_len) blk: {
1149            const decls_len = self.code.extra[extra_index];
1150            extra_index += 1;
1151            break :blk decls_len;
1152        } else 0;
1153
1154        try self.writeFlag(stream, "known_has_bits, ", small.known_has_bits);
1155        try stream.print("{s}, {s}, ", .{
1156            @tagName(small.name_strategy), @tagName(small.layout),
1157        });
1158
1159        if (decls_len == 0) {
1160            try stream.writeAll("{}, ");
1161        } else {
1162            try stream.writeAll("{\n");
1163            self.indent += 2;
1164            extra_index = try self.writeDecls(stream, decls_len, extra_index);
1165            self.indent -= 2;
1166            try stream.writeByteNTimes(' ', self.indent);
1167            try stream.writeAll("}, ");
1168        }
1169
1170        const body = self.code.extra[extra_index..][0..body_len];
1171        extra_index += body.len;
1172
1173        if (fields_len == 0) {
1174            assert(body.len == 0);
1175            try stream.writeAll("{}, {})");
1176        } else {
1177            const prev_parent_decl_node = self.parent_decl_node;
1178            if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off);
1179            try self.writeBracedDecl(stream, body);
1180            try stream.writeAll(", {\n");
1181
1182            self.indent += 2;
1183            const bits_per_field = 4;
1184            const fields_per_u32 = 32 / bits_per_field;
1185            const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
1186            var bit_bag_index: usize = extra_index;
1187            extra_index += bit_bags_count;
1188            var cur_bit_bag: u32 = undefined;
1189            var field_i: u32 = 0;
1190            while (field_i < fields_len) : (field_i += 1) {
1191                if (field_i % fields_per_u32 == 0) {
1192                    cur_bit_bag = self.code.extra[bit_bag_index];
1193                    bit_bag_index += 1;
1194                }
1195                const has_align = @truncate(u1, cur_bit_bag) != 0;
1196                cur_bit_bag >>= 1;
1197                const has_default = @truncate(u1, cur_bit_bag) != 0;
1198                cur_bit_bag >>= 1;
1199                const is_comptime = @truncate(u1, cur_bit_bag) != 0;
1200                cur_bit_bag >>= 1;
1201                const unused = @truncate(u1, cur_bit_bag) != 0;
1202                cur_bit_bag >>= 1;
1203
1204                _ = unused;
1205
1206                const field_name = self.code.nullTerminatedString(self.code.extra[extra_index]);
1207                extra_index += 1;
1208                const field_type = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
1209                extra_index += 1;
1210
1211                try stream.writeByteNTimes(' ', self.indent);
1212                try self.writeFlag(stream, "comptime ", is_comptime);
1213                try stream.print("{}: ", .{std.zig.fmtId(field_name)});
1214                try self.writeInstRef(stream, field_type);
1215
1216                if (has_align) {
1217                    const align_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
1218                    extra_index += 1;
1219
1220                    try stream.writeAll(" align(");
1221                    try self.writeInstRef(stream, align_ref);
1222                    try stream.writeAll(")");
1223                }
1224                if (has_default) {
1225                    const default_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
1226                    extra_index += 1;
1227
1228                    try stream.writeAll(" = ");
1229                    try self.writeInstRef(stream, default_ref);
1230                }
1231                try stream.writeAll(",\n");
1232            }
1233
1234            self.parent_decl_node = prev_parent_decl_node;
1235            self.indent -= 2;
1236            try stream.writeByteNTimes(' ', self.indent);
1237            try stream.writeAll("})");
1238        }
1239        try self.writeSrcNode(stream, src_node);
1240    }
1241
1242    fn writeUnionDecl(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {
1243        const small = @bitCast(Zir.Inst.UnionDecl.Small, extended.small);
1244
1245        var extra_index: usize = extended.operand;
1246
1247        const src_node: ?i32 = if (small.has_src_node) blk: {
1248            const src_node = @bitCast(i32, self.code.extra[extra_index]);
1249            extra_index += 1;
1250            break :blk src_node;
1251        } else null;
1252
1253        const tag_type_ref = if (small.has_tag_type) blk: {
1254            const tag_type_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
1255            extra_index += 1;
1256            break :blk tag_type_ref;
1257        } else .none;
1258
1259        const body_len = if (small.has_body_len) blk: {
1260            const body_len = self.code.extra[extra_index];
1261            extra_index += 1;
1262            break :blk body_len;
1263        } else 0;
1264
1265        const fields_len = if (small.has_fields_len) blk: {
1266            const fields_len = self.code.extra[extra_index];
1267            extra_index += 1;
1268            break :blk fields_len;
1269        } else 0;
1270
1271        const decls_len = if (small.has_decls_len) blk: {
1272            const decls_len = self.code.extra[extra_index];
1273            extra_index += 1;
1274            break :blk decls_len;
1275        } else 0;
1276
1277        try stream.print("{s}, {s}, ", .{
1278            @tagName(small.name_strategy), @tagName(small.layout),
1279        });
1280        try self.writeFlag(stream, "autoenum, ", small.auto_enum_tag);
1281
1282        if (decls_len == 0) {
1283            try stream.writeAll("{}, ");
1284        } else {
1285            try stream.writeAll("{\n");
1286            self.indent += 2;
1287            extra_index = try self.writeDecls(stream, decls_len, extra_index);
1288            self.indent -= 2;
1289            try stream.writeByteNTimes(' ', self.indent);
1290            try stream.writeAll("}, ");
1291        }
1292
1293        assert(fields_len != 0);
1294
1295        if (tag_type_ref != .none) {
1296            try self.writeInstRef(stream, tag_type_ref);
1297            try stream.writeAll(", ");
1298        }
1299
1300        const body = self.code.extra[extra_index..][0..body_len];
1301        extra_index += body.len;
1302
1303        const prev_parent_decl_node = self.parent_decl_node;
1304        if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off);
1305        try self.writeBracedDecl(stream, body);
1306        try stream.writeAll(", {\n");
1307
1308        self.indent += 2;
1309        const bits_per_field = 4;
1310        const fields_per_u32 = 32 / bits_per_field;
1311        const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
1312        const body_end = extra_index;
1313        extra_index += bit_bags_count;
1314        var bit_bag_index: usize = body_end;
1315        var cur_bit_bag: u32 = undefined;
1316        var field_i: u32 = 0;
1317        while (field_i < fields_len) : (field_i += 1) {
1318            if (field_i % fields_per_u32 == 0) {
1319                cur_bit_bag = self.code.extra[bit_bag_index];
1320                bit_bag_index += 1;
1321            }
1322            const has_type = @truncate(u1, cur_bit_bag) != 0;
1323            cur_bit_bag >>= 1;
1324            const has_align = @truncate(u1, cur_bit_bag) != 0;
1325            cur_bit_bag >>= 1;
1326            const has_value = @truncate(u1, cur_bit_bag) != 0;
1327            cur_bit_bag >>= 1;
1328            const unused = @truncate(u1, cur_bit_bag) != 0;
1329            cur_bit_bag >>= 1;
1330
1331            _ = unused;
1332
1333            const field_name = self.code.nullTerminatedString(self.code.extra[extra_index]);
1334            extra_index += 1;
1335            try stream.writeByteNTimes(' ', self.indent);
1336            try stream.print("{}", .{std.zig.fmtId(field_name)});
1337
1338            if (has_type) {
1339                const field_type = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
1340                extra_index += 1;
1341
1342                try stream.writeAll(": ");
1343                try self.writeInstRef(stream, field_type);
1344            }
1345            if (has_align) {
1346                const align_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
1347                extra_index += 1;
1348
1349                try stream.writeAll(" align(");
1350                try self.writeInstRef(stream, align_ref);
1351                try stream.writeAll(")");
1352            }
1353            if (has_value) {
1354                const default_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
1355                extra_index += 1;
1356
1357                try stream.writeAll(" = ");
1358                try self.writeInstRef(stream, default_ref);
1359            }
1360            try stream.writeAll(",\n");
1361        }
1362
1363        self.parent_decl_node = prev_parent_decl_node;
1364        self.indent -= 2;
1365        try stream.writeByteNTimes(' ', self.indent);
1366        try stream.writeAll("})");
1367        try self.writeSrcNode(stream, src_node);
1368    }
1369
1370    fn writeDecls(self: *Writer, stream: anytype, decls_len: u32, extra_start: usize) !usize {
1371        const parent_decl_node = self.parent_decl_node;
1372        const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable;
1373        var extra_index = extra_start + bit_bags_count;
1374        var bit_bag_index: usize = extra_start;
1375        var cur_bit_bag: u32 = undefined;
1376        var decl_i: u32 = 0;
1377        while (decl_i < decls_len) : (decl_i += 1) {
1378            if (decl_i % 8 == 0) {
1379                cur_bit_bag = self.code.extra[bit_bag_index];
1380                bit_bag_index += 1;
1381            }
1382            const is_pub = @truncate(u1, cur_bit_bag) != 0;
1383            cur_bit_bag >>= 1;
1384            const is_exported = @truncate(u1, cur_bit_bag) != 0;
1385            cur_bit_bag >>= 1;
1386            const has_align = @truncate(u1, cur_bit_bag) != 0;
1387            cur_bit_bag >>= 1;
1388            const has_section_or_addrspace = @truncate(u1, cur_bit_bag) != 0;
1389            cur_bit_bag >>= 1;
1390
1391            const sub_index = extra_index;
1392
1393            const hash_u32s = self.code.extra[extra_index..][0..4];
1394            extra_index += 4;
1395            const line = self.code.extra[extra_index];
1396            extra_index += 1;
1397            const decl_name_index = self.code.extra[extra_index];
1398            extra_index += 1;
1399            const decl_index = self.code.extra[extra_index];
1400            extra_index += 1;
1401            const align_inst: Zir.Inst.Ref = if (!has_align) .none else inst: {
1402                const inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
1403                extra_index += 1;
1404                break :inst inst;
1405            };
1406            const section_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: {
1407                const inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
1408                extra_index += 1;
1409                break :inst inst;
1410            };
1411            const addrspace_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: {
1412                const inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
1413                extra_index += 1;
1414                break :inst inst;
1415            };
1416
1417            const pub_str = if (is_pub) "pub " else "";
1418            const hash_bytes = @bitCast([16]u8, hash_u32s.*);
1419            try stream.writeByteNTimes(' ', self.indent);
1420            if (decl_name_index == 0) {
1421                const name = if (is_exported) "usingnamespace" else "comptime";
1422                try stream.writeAll(pub_str);
1423                try stream.writeAll(name);
1424            } else if (decl_name_index == 1) {
1425                try stream.writeAll("test");
1426            } else {
1427                const raw_decl_name = self.code.nullTerminatedString(decl_name_index);
1428                const decl_name = if (raw_decl_name.len == 0)
1429                    self.code.nullTerminatedString(decl_name_index + 1)
1430                else
1431                    raw_decl_name;
1432                const test_str = if (raw_decl_name.len == 0) "test " else "";
1433                const export_str = if (is_exported) "export " else "";
1434                try stream.print("[{d}] {s}{s}{s}{}", .{
1435                    sub_index, pub_str, test_str, export_str, std.zig.fmtId(decl_name),
1436                });
1437                if (align_inst != .none) {
1438                    try stream.writeAll(" align(");
1439                    try self.writeInstRef(stream, align_inst);
1440                    try stream.writeAll(")");
1441                }
1442                if (addrspace_inst != .none) {
1443                    try stream.writeAll(" addrspace(");
1444                    try self.writeInstRef(stream, addrspace_inst);
1445                    try stream.writeAll(")");
1446                }
1447                if (section_inst != .none) {
1448                    try stream.writeAll(" linksection(");
1449                    try self.writeInstRef(stream, section_inst);
1450                    try stream.writeAll(")");
1451                }
1452            }
1453
1454            if (self.recurse_decls) {
1455                const tag = self.code.instructions.items(.tag)[decl_index];
1456                try stream.print(" line({d}) hash({}): %{d} = {s}(", .{
1457                    line, std.fmt.fmtSliceHexLower(&hash_bytes), decl_index, @tagName(tag),
1458                });
1459
1460                const decl_block_inst_data = self.code.instructions.items(.data)[decl_index].pl_node;
1461                const sub_decl_node_off = decl_block_inst_data.src_node;
1462                self.parent_decl_node = self.relativeToNodeIndex(sub_decl_node_off);
1463                try self.writePlNodeBlockWithoutSrc(stream, decl_index);
1464                self.parent_decl_node = parent_decl_node;
1465                try self.writeSrc(stream, decl_block_inst_data.src());
1466                try stream.writeAll("\n");
1467            } else {
1468                try stream.print(" line({d}) hash({}): %{d} = ...\n", .{
1469                    line, std.fmt.fmtSliceHexLower(&hash_bytes), decl_index,
1470                });
1471            }
1472        }
1473        return extra_index;
1474    }
1475
1476    fn writeEnumDecl(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {
1477        const small = @bitCast(Zir.Inst.EnumDecl.Small, extended.small);
1478        var extra_index: usize = extended.operand;
1479
1480        const src_node: ?i32 = if (small.has_src_node) blk: {
1481            const src_node = @bitCast(i32, self.code.extra[extra_index]);
1482            extra_index += 1;
1483            break :blk src_node;
1484        } else null;
1485
1486        const tag_type_ref = if (small.has_tag_type) blk: {
1487            const tag_type_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
1488            extra_index += 1;
1489            break :blk tag_type_ref;
1490        } else .none;
1491
1492        const body_len = if (small.has_body_len) blk: {
1493            const body_len = self.code.extra[extra_index];
1494            extra_index += 1;
1495            break :blk body_len;
1496        } else 0;
1497
1498        const fields_len = if (small.has_fields_len) blk: {
1499            const fields_len = self.code.extra[extra_index];
1500            extra_index += 1;
1501            break :blk fields_len;
1502        } else 0;
1503
1504        const decls_len = if (small.has_decls_len) blk: {
1505            const decls_len = self.code.extra[extra_index];
1506            extra_index += 1;
1507            break :blk decls_len;
1508        } else 0;
1509
1510        try stream.print("{s}, ", .{@tagName(small.name_strategy)});
1511        try self.writeFlag(stream, "nonexhaustive, ", small.nonexhaustive);
1512
1513        if (decls_len == 0) {
1514            try stream.writeAll("{}, ");
1515        } else {
1516            try stream.writeAll("{\n");
1517            self.indent += 2;
1518            extra_index = try self.writeDecls(stream, decls_len, extra_index);
1519            self.indent -= 2;
1520            try stream.writeByteNTimes(' ', self.indent);
1521            try stream.writeAll("}, ");
1522        }
1523
1524        if (tag_type_ref != .none) {
1525            try self.writeInstRef(stream, tag_type_ref);
1526            try stream.writeAll(", ");
1527        }
1528
1529        const body = self.code.extra[extra_index..][0..body_len];
1530        extra_index += body.len;
1531
1532        if (fields_len == 0) {
1533            assert(body.len == 0);
1534            try stream.writeAll("{}, {})");
1535        } else {
1536            const prev_parent_decl_node = self.parent_decl_node;
1537            if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off);
1538            try self.writeBracedDecl(stream, body);
1539            try stream.writeAll(", {\n");
1540
1541            self.indent += 2;
1542            const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable;
1543            const body_end = extra_index;
1544            extra_index += bit_bags_count;
1545            var bit_bag_index: usize = body_end;
1546            var cur_bit_bag: u32 = undefined;
1547            var field_i: u32 = 0;
1548            while (field_i < fields_len) : (field_i += 1) {
1549                if (field_i % 32 == 0) {
1550                    cur_bit_bag = self.code.extra[bit_bag_index];
1551                    bit_bag_index += 1;
1552                }
1553                const has_tag_value = @truncate(u1, cur_bit_bag) != 0;
1554                cur_bit_bag >>= 1;
1555
1556                const field_name = self.code.nullTerminatedString(self.code.extra[extra_index]);
1557                extra_index += 1;
1558
1559                try stream.writeByteNTimes(' ', self.indent);
1560                try stream.print("{}", .{std.zig.fmtId(field_name)});
1561
1562                if (has_tag_value) {
1563                    const tag_value_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
1564                    extra_index += 1;
1565
1566                    try stream.writeAll(" = ");
1567                    try self.writeInstRef(stream, tag_value_ref);
1568                }
1569                try stream.writeAll(",\n");
1570            }
1571            self.parent_decl_node = prev_parent_decl_node;
1572            self.indent -= 2;
1573            try stream.writeByteNTimes(' ', self.indent);
1574            try stream.writeAll("})");
1575        }
1576        try self.writeSrcNode(stream, src_node);
1577    }
1578
1579    fn writeOpaqueDecl(
1580        self: *Writer,
1581        stream: anytype,
1582        extended: Zir.Inst.Extended.InstData,
1583    ) !void {
1584        const small = @bitCast(Zir.Inst.OpaqueDecl.Small, extended.small);
1585        var extra_index: usize = extended.operand;
1586
1587        const src_node: ?i32 = if (small.has_src_node) blk: {
1588            const src_node = @bitCast(i32, self.code.extra[extra_index]);
1589            extra_index += 1;
1590            break :blk src_node;
1591        } else null;
1592
1593        const decls_len = if (small.has_decls_len) blk: {
1594            const decls_len = self.code.extra[extra_index];
1595            extra_index += 1;
1596            break :blk decls_len;
1597        } else 0;
1598
1599        try stream.print("{s}, ", .{@tagName(small.name_strategy)});
1600
1601        if (decls_len == 0) {
1602            try stream.writeAll("{})");
1603        } else {
1604            try stream.writeAll("{\n");
1605            self.indent += 2;
1606            _ = try self.writeDecls(stream, decls_len, extra_index);
1607            self.indent -= 2;
1608            try stream.writeByteNTimes(' ', self.indent);
1609            try stream.writeAll("})");
1610        }
1611        try self.writeSrcNode(stream, src_node);
1612    }
1613
1614    fn writeErrorSetDecl(
1615        self: *Writer,
1616        stream: anytype,
1617        inst: Zir.Inst.Index,
1618        name_strategy: Zir.Inst.NameStrategy,
1619    ) !void {
1620        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1621        const extra = self.code.extraData(Zir.Inst.ErrorSetDecl, inst_data.payload_index);
1622        const fields = self.code.extra[extra.end..][0..extra.data.fields_len];
1623
1624        try stream.print("{s}, ", .{@tagName(name_strategy)});
1625
1626        try stream.writeAll("{\n");
1627        self.indent += 2;
1628        for (fields) |str_index| {
1629            const name = self.code.nullTerminatedString(str_index);
1630            try stream.writeByteNTimes(' ', self.indent);
1631            try stream.print("{},\n", .{std.zig.fmtId(name)});
1632        }
1633        self.indent -= 2;
1634        try stream.writeByteNTimes(' ', self.indent);
1635        try stream.writeAll("}) ");
1636
1637        try self.writeSrc(stream, inst_data.src());
1638    }
1639
1640    fn writePlNodeSwitchBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1641        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1642        const extra = self.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
1643
1644        var extra_index: usize = extra.end;
1645
1646        const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
1647            const multi_cases_len = self.code.extra[extra_index];
1648            extra_index += 1;
1649            break :blk multi_cases_len;
1650        } else 0;
1651
1652        try self.writeInstRef(stream, extra.data.operand);
1653        try self.writeFlag(stream, ", ref", extra.data.bits.is_ref);
1654
1655        self.indent += 2;
1656
1657        else_prong: {
1658            const special_prong = extra.data.bits.specialProng();
1659            const prong_name = switch (special_prong) {
1660                .@"else" => "else",
1661                .under => "_",
1662                else => break :else_prong,
1663            };
1664
1665            const body_len = self.code.extra[extra_index];
1666            extra_index += 1;
1667            const body = self.code.extra[extra_index..][0..body_len];
1668            extra_index += body.len;
1669
1670            try stream.writeAll(",\n");
1671            try stream.writeByteNTimes(' ', self.indent);
1672            try stream.print("{s} => ", .{prong_name});
1673            try self.writeBracedBody(stream, body);
1674        }
1675
1676        {
1677            const scalar_cases_len = extra.data.bits.scalar_cases_len;
1678            var scalar_i: usize = 0;
1679            while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
1680                const item_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
1681                extra_index += 1;
1682                const body_len = self.code.extra[extra_index];
1683                extra_index += 1;
1684                const body = self.code.extra[extra_index..][0..body_len];
1685                extra_index += body_len;
1686
1687                try stream.writeAll(",\n");
1688                try stream.writeByteNTimes(' ', self.indent);
1689                try self.writeInstRef(stream, item_ref);
1690                try stream.writeAll(" => ");
1691                try self.writeBracedBody(stream, body);
1692            }
1693        }
1694        {
1695            var multi_i: usize = 0;
1696            while (multi_i < multi_cases_len) : (multi_i += 1) {
1697                const items_len = self.code.extra[extra_index];
1698                extra_index += 1;
1699                const ranges_len = self.code.extra[extra_index];
1700                extra_index += 1;
1701                const body_len = self.code.extra[extra_index];
1702                extra_index += 1;
1703                const items = self.code.refSlice(extra_index, items_len);
1704                extra_index += items_len;
1705
1706                try stream.writeAll(",\n");
1707                try stream.writeByteNTimes(' ', self.indent);
1708
1709                for (items) |item_ref, item_i| {
1710                    if (item_i != 0) try stream.writeAll(", ");
1711                    try self.writeInstRef(stream, item_ref);
1712                }
1713
1714                var range_i: usize = 0;
1715                while (range_i < ranges_len) : (range_i += 1) {
1716                    const item_first = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
1717                    extra_index += 1;
1718                    const item_last = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
1719                    extra_index += 1;
1720
1721                    if (range_i != 0 or items.len != 0) {
1722                        try stream.writeAll(", ");
1723                    }
1724                    try self.writeInstRef(stream, item_first);
1725                    try stream.writeAll("...");
1726                    try self.writeInstRef(stream, item_last);
1727                }
1728
1729                const body = self.code.extra[extra_index..][0..body_len];
1730                extra_index += body_len;
1731                try stream.writeAll(" => ");
1732                try self.writeBracedBody(stream, body);
1733            }
1734        }
1735
1736        self.indent -= 2;
1737
1738        try stream.writeAll(") ");
1739        try self.writeSrc(stream, inst_data.src());
1740    }
1741
1742    fn writePlNodeField(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1743        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1744        const extra = self.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
1745        const name = self.code.nullTerminatedString(extra.field_name_start);
1746        try self.writeInstRef(stream, extra.lhs);
1747        try stream.print(", \"{}\") ", .{std.zig.fmtEscapes(name)});
1748        try self.writeSrc(stream, inst_data.src());
1749    }
1750
1751    fn writePlNodeFieldNamed(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1752        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1753        const extra = self.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;
1754        try self.writeInstRef(stream, extra.lhs);
1755        try stream.writeAll(", ");
1756        try self.writeInstRef(stream, extra.field_name);
1757        try stream.writeAll(") ");
1758        try self.writeSrc(stream, inst_data.src());
1759    }
1760
1761    fn writeAs(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1762        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1763        const extra = self.code.extraData(Zir.Inst.As, inst_data.payload_index).data;
1764        try self.writeInstRef(stream, extra.dest_type);
1765        try stream.writeAll(", ");
1766        try self.writeInstRef(stream, extra.operand);
1767        try stream.writeAll(") ");
1768        try self.writeSrc(stream, inst_data.src());
1769    }
1770
1771    fn writeNode(
1772        self: *Writer,
1773        stream: anytype,
1774        inst: Zir.Inst.Index,
1775    ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1776        const src_node = self.code.instructions.items(.data)[inst].node;
1777        const src: LazySrcLoc = .{ .node_offset = src_node };
1778        try stream.writeAll(") ");
1779        try self.writeSrc(stream, src);
1780    }
1781
1782    fn writeStrTok(
1783        self: *Writer,
1784        stream: anytype,
1785        inst: Zir.Inst.Index,
1786    ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
1787        const inst_data = self.code.instructions.items(.data)[inst].str_tok;
1788        const str = inst_data.get(self.code);
1789        try stream.print("\"{}\") ", .{std.zig.fmtEscapes(str)});
1790        try self.writeSrc(stream, inst_data.src());
1791    }
1792
1793    fn writeFunc(
1794        self: *Writer,
1795        stream: anytype,
1796        inst: Zir.Inst.Index,
1797        inferred_error_set: bool,
1798    ) !void {
1799        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1800        const src = inst_data.src();
1801        const extra = self.code.extraData(Zir.Inst.Func, inst_data.payload_index);
1802        var extra_index = extra.end;
1803
1804        const ret_ty_body = self.code.extra[extra_index..][0..extra.data.ret_body_len];
1805        extra_index += ret_ty_body.len;
1806
1807        const body = self.code.extra[extra_index..][0..extra.data.body_len];
1808        extra_index += body.len;
1809
1810        var src_locs: Zir.Inst.Func.SrcLocs = undefined;
1811        if (body.len != 0) {
1812            src_locs = self.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data;
1813        }
1814        return self.writeFuncCommon(
1815            stream,
1816            ret_ty_body,
1817            inferred_error_set,
1818            false,
1819            false,
1820            .none,
1821            .none,
1822            body,
1823            src,
1824            src_locs,
1825        );
1826    }
1827
1828    fn writeFuncExtended(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {
1829        const extra = self.code.extraData(Zir.Inst.ExtendedFunc, extended.operand);
1830        const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };
1831        const small = @bitCast(Zir.Inst.ExtendedFunc.Small, extended.small);
1832
1833        var extra_index: usize = extra.end;
1834        if (small.has_lib_name) {
1835            const lib_name = self.code.nullTerminatedString(self.code.extra[extra_index]);
1836            extra_index += 1;
1837            try stream.print("lib_name=\"{}\", ", .{std.zig.fmtEscapes(lib_name)});
1838        }
1839        try self.writeFlag(stream, "test, ", small.is_test);
1840        const cc: Zir.Inst.Ref = if (!small.has_cc) .none else blk: {
1841            const cc = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
1842            extra_index += 1;
1843            break :blk cc;
1844        };
1845        const align_inst: Zir.Inst.Ref = if (!small.has_align) .none else blk: {
1846            const align_inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
1847            extra_index += 1;
1848            break :blk align_inst;
1849        };
1850
1851        const ret_ty_body = self.code.extra[extra_index..][0..extra.data.ret_body_len];
1852        extra_index += ret_ty_body.len;
1853
1854        const body = self.code.extra[extra_index..][0..extra.data.body_len];
1855        extra_index += body.len;
1856
1857        var src_locs: Zir.Inst.Func.SrcLocs = undefined;
1858        if (body.len != 0) {
1859            src_locs = self.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data;
1860        }
1861        return self.writeFuncCommon(
1862            stream,
1863            ret_ty_body,
1864            small.is_inferred_error,
1865            small.is_var_args,
1866            small.is_extern,
1867            cc,
1868            align_inst,
1869            body,
1870            src,
1871            src_locs,
1872        );
1873    }
1874
1875    fn writeVarExtended(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {
1876        const extra = self.code.extraData(Zir.Inst.ExtendedVar, extended.operand);
1877        const small = @bitCast(Zir.Inst.ExtendedVar.Small, extended.small);
1878
1879        try self.writeInstRef(stream, extra.data.var_type);
1880
1881        var extra_index: usize = extra.end;
1882        if (small.has_lib_name) {
1883            const lib_name = self.code.nullTerminatedString(self.code.extra[extra_index]);
1884            extra_index += 1;
1885            try stream.print(", lib_name=\"{}\"", .{std.zig.fmtEscapes(lib_name)});
1886        }
1887        const align_inst: Zir.Inst.Ref = if (!small.has_align) .none else blk: {
1888            const align_inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
1889            extra_index += 1;
1890            break :blk align_inst;
1891        };
1892        const init_inst: Zir.Inst.Ref = if (!small.has_init) .none else blk: {
1893            const init_inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
1894            extra_index += 1;
1895            break :blk init_inst;
1896        };
1897        try self.writeFlag(stream, ", is_extern", small.is_extern);
1898        try self.writeOptionalInstRef(stream, ", align=", align_inst);
1899        try self.writeOptionalInstRef(stream, ", init=", init_inst);
1900        try stream.writeAll("))");
1901    }
1902
1903    fn writeAllocExtended(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {
1904        const extra = self.code.extraData(Zir.Inst.AllocExtended, extended.operand);
1905        const small = @bitCast(Zir.Inst.AllocExtended.Small, extended.small);
1906        const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };
1907
1908        var extra_index: usize = extra.end;
1909        const type_inst: Zir.Inst.Ref = if (!small.has_type) .none else blk: {
1910            const type_inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
1911            extra_index += 1;
1912            break :blk type_inst;
1913        };
1914        const align_inst: Zir.Inst.Ref = if (!small.has_align) .none else blk: {
1915            const align_inst = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
1916            extra_index += 1;
1917            break :blk align_inst;
1918        };
1919        try self.writeFlag(stream, ",is_const", small.is_const);
1920        try self.writeFlag(stream, ",is_comptime", small.is_comptime);
1921        try self.writeOptionalInstRef(stream, ",ty=", type_inst);
1922        try self.writeOptionalInstRef(stream, ",align=", align_inst);
1923        try stream.writeAll(")) ");
1924        try self.writeSrc(stream, src);
1925    }
1926
1927    fn writeBoolBr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1928        const inst_data = self.code.instructions.items(.data)[inst].bool_br;
1929        const extra = self.code.extraData(Zir.Inst.Block, inst_data.payload_index);
1930        const body = self.code.extra[extra.end..][0..extra.data.body_len];
1931        try self.writeInstRef(stream, inst_data.lhs);
1932        try stream.writeAll(", ");
1933        try self.writeBracedBody(stream, body);
1934    }
1935
1936    fn writeIntType(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1937        const int_type = self.code.instructions.items(.data)[inst].int_type;
1938        const prefix: u8 = switch (int_type.signedness) {
1939            .signed => 'i',
1940            .unsigned => 'u',
1941        };
1942        try stream.print("{c}{d}) ", .{ prefix, int_type.bit_count });
1943        try self.writeSrc(stream, int_type.src());
1944    }
1945
1946    fn writeBreak(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1947        const inst_data = self.code.instructions.items(.data)[inst].@"break";
1948
1949        try self.writeInstIndex(stream, inst_data.block_inst);
1950        try stream.writeAll(", ");
1951        try self.writeInstRef(stream, inst_data.operand);
1952        try stream.writeAll(")");
1953    }
1954
1955    fn writeArrayInit(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1956        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1957
1958        const extra = self.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
1959        const args = self.code.refSlice(extra.end, extra.data.operands_len);
1960
1961        try stream.writeAll(".{");
1962        for (args) |arg, i| {
1963            if (i != 0) try stream.writeAll(", ");
1964            try self.writeInstRef(stream, arg);
1965        }
1966        try stream.writeAll("})");
1967    }
1968
1969    fn writeUnreachable(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1970        const inst_data = self.code.instructions.items(.data)[inst].@"unreachable";
1971        const safety_str = if (inst_data.safety) "safe" else "unsafe";
1972        try stream.print("{s}) ", .{safety_str});
1973        try self.writeSrc(stream, inst_data.src());
1974    }
1975
1976    fn writeFuncCommon(
1977        self: *Writer,
1978        stream: anytype,
1979        ret_ty_body: []const Zir.Inst.Index,
1980        inferred_error_set: bool,
1981        var_args: bool,
1982        is_extern: bool,
1983        cc: Zir.Inst.Ref,
1984        align_inst: Zir.Inst.Ref,
1985        body: []const Zir.Inst.Index,
1986        src: LazySrcLoc,
1987        src_locs: Zir.Inst.Func.SrcLocs,
1988    ) !void {
1989        if (ret_ty_body.len == 0) {
1990            try stream.writeAll("ret_ty=void");
1991        } else {
1992            try stream.writeAll("ret_ty=");
1993            try self.writeBracedBody(stream, ret_ty_body);
1994        }
1995
1996        try self.writeOptionalInstRef(stream, ", cc=", cc);
1997        try self.writeOptionalInstRef(stream, ", align=", align_inst);
1998        try self.writeFlag(stream, ", vargs", var_args);
1999        try self.writeFlag(stream, ", extern", is_extern);
2000        try self.writeFlag(stream, ", inferror", inferred_error_set);
2001
2002        try stream.writeAll(", body=");
2003        try self.writeBracedBody(stream, body);
2004        try stream.writeAll(") ");
2005        if (body.len != 0) {
2006            try stream.print("(lbrace={d}:{d},rbrace={d}:{d}) ", .{
2007                src_locs.lbrace_line + 1, @truncate(u16, src_locs.columns) + 1,
2008                src_locs.rbrace_line + 1, @truncate(u16, src_locs.columns >> 16) + 1,
2009            });
2010        }
2011        try self.writeSrc(stream, src);
2012    }
2013
2014    fn writeSwitchCapture(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
2015        const inst_data = self.code.instructions.items(.data)[inst].switch_capture;
2016        try self.writeInstIndex(stream, inst_data.switch_inst);
2017        try stream.print(", {d})", .{inst_data.prong_index});
2018    }
2019
2020    fn writeDbgStmt(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
2021        const inst_data = self.code.instructions.items(.data)[inst].dbg_stmt;
2022        try stream.print("{d}, {d})", .{ inst_data.line + 1, inst_data.column + 1 });
2023    }
2024
2025    fn writeInstRef(self: *Writer, stream: anytype, ref: Zir.Inst.Ref) !void {
2026        var i: usize = @enumToInt(ref);
2027
2028        if (i < Zir.Inst.Ref.typed_value_map.len) {
2029            return stream.print("@{}", .{ref});
2030        }
2031        i -= Zir.Inst.Ref.typed_value_map.len;
2032
2033        return self.writeInstIndex(stream, @intCast(Zir.Inst.Index, i));
2034    }
2035
2036    fn writeInstIndex(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
2037        _ = self;
2038        return stream.print("%{d}", .{inst});
2039    }
2040
2041    fn writeOptionalInstRef(
2042        self: *Writer,
2043        stream: anytype,
2044        prefix: []const u8,
2045        inst: Zir.Inst.Ref,
2046    ) !void {
2047        if (inst == .none) return;
2048        try stream.writeAll(prefix);
2049        try self.writeInstRef(stream, inst);
2050    }
2051
2052    fn writeFlag(
2053        self: *Writer,
2054        stream: anytype,
2055        name: []const u8,
2056        flag: bool,
2057    ) !void {
2058        _ = self;
2059        if (!flag) return;
2060        try stream.writeAll(name);
2061    }
2062
2063    fn writeSrc(self: *Writer, stream: anytype, src: LazySrcLoc) !void {
2064        if (self.file.tree_loaded) {
2065            const tree = self.file.tree;
2066            const src_loc: Module.SrcLoc = .{
2067                .file_scope = self.file,
2068                .parent_decl_node = self.parent_decl_node,
2069                .lazy = src,
2070            };
2071            const abs_byte_off = src_loc.byteOffset(self.gpa) catch unreachable;
2072            const delta_line = std.zig.findLineColumn(tree.source, abs_byte_off);
2073            try stream.print("{s}:{d}:{d}", .{
2074                @tagName(src), delta_line.line + 1, delta_line.column + 1,
2075            });
2076        }
2077    }
2078
2079    fn writeSrcNode(self: *Writer, stream: anytype, src_node: ?i32) !void {
2080        const node_offset = src_node orelse return;
2081        const src: LazySrcLoc = .{ .node_offset = node_offset };
2082        try stream.writeAll(" ");
2083        return self.writeSrc(stream, src);
2084    }
2085
2086    fn writeBracedDecl(self: *Writer, stream: anytype, body: []const Zir.Inst.Index) !void {
2087        try self.writeBracedBodyConditional(stream, body, self.recurse_decls);
2088    }
2089
2090    fn writeBracedBody(self: *Writer, stream: anytype, body: []const Zir.Inst.Index) !void {
2091        try self.writeBracedBodyConditional(stream, body, self.recurse_blocks);
2092    }
2093
2094    fn writeBracedBodyConditional(self: *Writer, stream: anytype, body: []const Zir.Inst.Index, enabled: bool) !void {
2095        if (body.len == 0) {
2096            try stream.writeAll("{}");
2097        } else if (enabled) {
2098            try stream.writeAll("{\n");
2099            self.indent += 2;
2100            try self.writeBody(stream, body);
2101            self.indent -= 2;
2102            try stream.writeByteNTimes(' ', self.indent);
2103            try stream.writeAll("}");
2104        } else if (body.len == 1) {
2105            try stream.writeByte('{');
2106            try self.writeInstIndex(stream, body[0]);
2107            try stream.writeByte('}');
2108        } else if (body.len == 2) {
2109            try stream.writeByte('{');
2110            try self.writeInstIndex(stream, body[0]);
2111            try stream.writeAll(", ");
2112            try self.writeInstIndex(stream, body[1]);
2113            try stream.writeByte('}');
2114        } else {
2115            try stream.writeByte('{');
2116            try self.writeInstIndex(stream, body[0]);
2117            try stream.writeAll("..");
2118            try self.writeInstIndex(stream, body[body.len - 1]);
2119            try stream.writeByte('}');
2120        }
2121    }
2122
2123    fn writeBody(self: *Writer, stream: anytype, body: []const Zir.Inst.Index) !void {
2124        for (body) |inst| {
2125            try stream.writeByteNTimes(' ', self.indent);
2126            try stream.print("%{d} ", .{inst});
2127            try self.writeInstToStream(stream, inst);
2128            try stream.writeByte('\n');
2129        }
2130    }
2131};
2132