1const std = @import("std");
2const Allocator = std.mem.Allocator;
3const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
4
5const Module = @import("Module.zig");
6const Value = @import("value.zig").Value;
7const Zir = @import("Zir.zig");
8const Air = @import("Air.zig");
9const Liveness = @import("Liveness.zig");
10
11pub fn dump(gpa: Allocator, air: Air, zir: Zir, liveness: Liveness) void {
12    const instruction_bytes = air.instructions.len *
13        // Here we don't use @sizeOf(Air.Inst.Data) because it would include
14        // the debug safety tag but we want to measure release size.
15        (@sizeOf(Air.Inst.Tag) + 8);
16    const extra_bytes = air.extra.len * @sizeOf(u32);
17    const values_bytes = air.values.len * @sizeOf(Value);
18    const tomb_bytes = liveness.tomb_bits.len * @sizeOf(usize);
19    const liveness_extra_bytes = liveness.extra.len * @sizeOf(u32);
20    const liveness_special_bytes = liveness.special.count() * 8;
21    const total_bytes = @sizeOf(Air) + instruction_bytes + extra_bytes +
22        values_bytes + @sizeOf(Liveness) + liveness_extra_bytes +
23        liveness_special_bytes + tomb_bytes;
24
25    // zig fmt: off
26    std.debug.print(
27        \\# Total AIR+Liveness bytes: {}
28        \\# AIR Instructions:         {d} ({})
29        \\# AIR Extra Data:           {d} ({})
30        \\# AIR Values Bytes:         {d} ({})
31        \\# Liveness tomb_bits:       {}
32        \\# Liveness Extra Data:      {d} ({})
33        \\# Liveness special table:   {d} ({})
34        \\
35    , .{
36        fmtIntSizeBin(total_bytes),
37        air.instructions.len, fmtIntSizeBin(instruction_bytes),
38        air.extra.len, fmtIntSizeBin(extra_bytes),
39        air.values.len, fmtIntSizeBin(values_bytes),
40        fmtIntSizeBin(tomb_bytes),
41        liveness.extra.len, fmtIntSizeBin(liveness_extra_bytes),
42        liveness.special.count(), fmtIntSizeBin(liveness_special_bytes),
43    });
44    // zig fmt: on
45    var arena = std.heap.ArenaAllocator.init(gpa);
46    defer arena.deinit();
47
48    var writer: Writer = .{
49        .gpa = gpa,
50        .arena = arena.allocator(),
51        .air = air,
52        .zir = zir,
53        .liveness = liveness,
54        .indent = 2,
55    };
56    const stream = std.io.getStdErr().writer();
57    writer.writeAllConstants(stream) catch return;
58    stream.writeByte('\n') catch return;
59    writer.writeBody(stream, air.getMainBody()) catch return;
60}
61
62const Writer = struct {
63    gpa: Allocator,
64    arena: Allocator,
65    air: Air,
66    zir: Zir,
67    liveness: Liveness,
68    indent: usize,
69
70    fn writeAllConstants(w: *Writer, s: anytype) @TypeOf(s).Error!void {
71        for (w.air.instructions.items(.tag)) |tag, i| {
72            const inst = @intCast(u32, i);
73            switch (tag) {
74                .constant, .const_ty => {
75                    try s.writeByteNTimes(' ', w.indent);
76                    try s.print("%{d} ", .{inst});
77                    try w.writeInst(s, inst);
78                    try s.writeAll(")\n");
79                },
80                else => continue,
81            }
82        }
83    }
84
85    fn writeBody(w: *Writer, s: anytype, body: []const Air.Inst.Index) @TypeOf(s).Error!void {
86        for (body) |inst| {
87            try s.writeByteNTimes(' ', w.indent);
88            if (w.liveness.isUnused(inst)) {
89                try s.print("%{d}!", .{inst});
90            } else {
91                try s.print("%{d} ", .{inst});
92            }
93            try w.writeInst(s, inst);
94            try s.writeAll(")\n");
95        }
96    }
97
98    fn writeInst(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
99        const tags = w.air.instructions.items(.tag);
100        const tag = tags[inst];
101        try s.print("= {s}(", .{@tagName(tags[inst])});
102        switch (tag) {
103            .arg => try w.writeTyStr(s, inst),
104
105            .add,
106            .addwrap,
107            .add_sat,
108            .sub,
109            .subwrap,
110            .sub_sat,
111            .mul,
112            .mulwrap,
113            .mul_sat,
114            .div_float,
115            .div_trunc,
116            .div_floor,
117            .div_exact,
118            .rem,
119            .mod,
120            .ptr_add,
121            .ptr_sub,
122            .bit_and,
123            .bit_or,
124            .xor,
125            .cmp_lt,
126            .cmp_lte,
127            .cmp_eq,
128            .cmp_gte,
129            .cmp_gt,
130            .cmp_neq,
131            .bool_and,
132            .bool_or,
133            .store,
134            .array_elem_val,
135            .slice_elem_val,
136            .ptr_elem_val,
137            .shl,
138            .shl_exact,
139            .shl_sat,
140            .shr,
141            .set_union_tag,
142            .min,
143            .max,
144            => try w.writeBinOp(s, inst),
145
146            .is_null,
147            .is_non_null,
148            .is_null_ptr,
149            .is_non_null_ptr,
150            .is_err,
151            .is_non_err,
152            .is_err_ptr,
153            .is_non_err_ptr,
154            .ptrtoint,
155            .bool_to_int,
156            .ret,
157            .ret_load,
158            => try w.writeUnOp(s, inst),
159
160            .breakpoint,
161            .unreach,
162            => try w.writeNoOp(s, inst),
163
164            .const_ty,
165            .alloc,
166            .ret_ptr,
167            => try w.writeTy(s, inst),
168
169            .not,
170            .bitcast,
171            .load,
172            .fptrunc,
173            .fpext,
174            .intcast,
175            .trunc,
176            .optional_payload,
177            .optional_payload_ptr,
178            .optional_payload_ptr_set,
179            .wrap_optional,
180            .unwrap_errunion_payload,
181            .unwrap_errunion_err,
182            .unwrap_errunion_payload_ptr,
183            .unwrap_errunion_err_ptr,
184            .wrap_errunion_payload,
185            .wrap_errunion_err,
186            .slice_ptr,
187            .slice_len,
188            .ptr_slice_len_ptr,
189            .ptr_slice_ptr_ptr,
190            .struct_field_ptr_index_0,
191            .struct_field_ptr_index_1,
192            .struct_field_ptr_index_2,
193            .struct_field_ptr_index_3,
194            .array_to_slice,
195            .int_to_float,
196            .float_to_int,
197            .get_union_tag,
198            .clz,
199            .ctz,
200            .popcount,
201            => try w.writeTyOp(s, inst),
202
203            .block,
204            .loop,
205            => try w.writeBlock(s, inst),
206
207            .slice,
208            .slice_elem_ptr,
209            .ptr_elem_ptr,
210            => try w.writeTyPlBin(s, inst),
211
212            .struct_field_ptr => try w.writeStructField(s, inst),
213            .struct_field_val => try w.writeStructField(s, inst),
214            .constant => try w.writeConstant(s, inst),
215            .assembly => try w.writeAssembly(s, inst),
216            .dbg_stmt => try w.writeDbgStmt(s, inst),
217            .call => try w.writeCall(s, inst),
218            .br => try w.writeBr(s, inst),
219            .cond_br => try w.writeCondBr(s, inst),
220            .switch_br => try w.writeSwitchBr(s, inst),
221            .cmpxchg_weak, .cmpxchg_strong => try w.writeCmpxchg(s, inst),
222            .fence => try w.writeFence(s, inst),
223            .atomic_load => try w.writeAtomicLoad(s, inst),
224            .atomic_store_unordered => try w.writeAtomicStore(s, inst, .Unordered),
225            .atomic_store_monotonic => try w.writeAtomicStore(s, inst, .Monotonic),
226            .atomic_store_release => try w.writeAtomicStore(s, inst, .Release),
227            .atomic_store_seq_cst => try w.writeAtomicStore(s, inst, .SeqCst),
228            .atomic_rmw => try w.writeAtomicRmw(s, inst),
229            .memcpy => try w.writeMemcpy(s, inst),
230            .memset => try w.writeMemset(s, inst),
231        }
232    }
233
234    fn writeTyStr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
235        const ty_str = w.air.instructions.items(.data)[inst].ty_str;
236        const name = w.zir.nullTerminatedString(ty_str.str);
237        try s.print("\"{}\", {}", .{ std.zig.fmtEscapes(name), w.air.getRefType(ty_str.ty) });
238    }
239
240    fn writeBinOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
241        const bin_op = w.air.instructions.items(.data)[inst].bin_op;
242        try w.writeOperand(s, inst, 0, bin_op.lhs);
243        try s.writeAll(", ");
244        try w.writeOperand(s, inst, 1, bin_op.rhs);
245    }
246
247    fn writeUnOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
248        const un_op = w.air.instructions.items(.data)[inst].un_op;
249        try w.writeOperand(s, inst, 0, un_op);
250    }
251
252    fn writeNoOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
253        _ = w;
254        _ = inst;
255        _ = s;
256        // no-op, no argument to write
257    }
258
259    fn writeTy(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
260        const ty = w.air.instructions.items(.data)[inst].ty;
261        try s.print("{}", .{ty});
262    }
263
264    fn writeTyOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
265        const ty_op = w.air.instructions.items(.data)[inst].ty_op;
266        try s.print("{}, ", .{w.air.getRefType(ty_op.ty)});
267        try w.writeOperand(s, inst, 0, ty_op.operand);
268    }
269
270    fn writeBlock(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
271        const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
272        const extra = w.air.extraData(Air.Block, ty_pl.payload);
273        const body = w.air.extra[extra.end..][0..extra.data.body_len];
274
275        try s.print("{}, {{\n", .{w.air.getRefType(ty_pl.ty)});
276        const old_indent = w.indent;
277        w.indent += 2;
278        try w.writeBody(s, body);
279        w.indent = old_indent;
280        try s.writeByteNTimes(' ', w.indent);
281        try s.writeAll("}");
282    }
283
284    fn writeStructField(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
285        const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
286        const extra = w.air.extraData(Air.StructField, ty_pl.payload).data;
287
288        try w.writeOperand(s, inst, 0, extra.struct_operand);
289        try s.print(", {d}", .{extra.field_index});
290    }
291
292    fn writeTyPlBin(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
293        const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
294        const extra = w.air.extraData(Air.Bin, ty_pl.payload).data;
295
296        try w.writeOperand(s, inst, 0, extra.lhs);
297        try s.writeAll(", ");
298        try w.writeOperand(s, inst, 1, extra.rhs);
299    }
300
301    fn writeCmpxchg(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
302        const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
303        const extra = w.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
304
305        try w.writeOperand(s, inst, 0, extra.ptr);
306        try s.writeAll(", ");
307        try w.writeOperand(s, inst, 1, extra.expected_value);
308        try s.writeAll(", ");
309        try w.writeOperand(s, inst, 2, extra.new_value);
310        try s.print(", {s}, {s}", .{
311            @tagName(extra.successOrder()), @tagName(extra.failureOrder()),
312        });
313    }
314
315    fn writeFence(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
316        const atomic_order = w.air.instructions.items(.data)[inst].fence;
317
318        try s.print("{s}", .{@tagName(atomic_order)});
319    }
320
321    fn writeAtomicLoad(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
322        const atomic_load = w.air.instructions.items(.data)[inst].atomic_load;
323
324        try w.writeOperand(s, inst, 0, atomic_load.ptr);
325        try s.print(", {s}", .{@tagName(atomic_load.order)});
326    }
327
328    fn writeAtomicStore(
329        w: *Writer,
330        s: anytype,
331        inst: Air.Inst.Index,
332        order: std.builtin.AtomicOrder,
333    ) @TypeOf(s).Error!void {
334        const bin_op = w.air.instructions.items(.data)[inst].bin_op;
335        try w.writeOperand(s, inst, 0, bin_op.lhs);
336        try s.writeAll(", ");
337        try w.writeOperand(s, inst, 1, bin_op.rhs);
338        try s.print(", {s}", .{@tagName(order)});
339    }
340
341    fn writeAtomicRmw(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
342        const pl_op = w.air.instructions.items(.data)[inst].pl_op;
343        const extra = w.air.extraData(Air.AtomicRmw, pl_op.payload).data;
344
345        try w.writeOperand(s, inst, 0, pl_op.operand);
346        try s.writeAll(", ");
347        try w.writeOperand(s, inst, 1, extra.operand);
348        try s.print(", {s}, {s}", .{ @tagName(extra.op()), @tagName(extra.ordering()) });
349    }
350
351    fn writeMemset(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
352        const pl_op = w.air.instructions.items(.data)[inst].pl_op;
353        const extra = w.air.extraData(Air.Bin, pl_op.payload).data;
354
355        try w.writeOperand(s, inst, 0, pl_op.operand);
356        try s.writeAll(", ");
357        try w.writeOperand(s, inst, 1, extra.lhs);
358        try s.writeAll(", ");
359        try w.writeOperand(s, inst, 2, extra.rhs);
360    }
361
362    fn writeMemcpy(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
363        const pl_op = w.air.instructions.items(.data)[inst].pl_op;
364        const extra = w.air.extraData(Air.Bin, pl_op.payload).data;
365
366        try w.writeOperand(s, inst, 0, pl_op.operand);
367        try s.writeAll(", ");
368        try w.writeOperand(s, inst, 1, extra.lhs);
369        try s.writeAll(", ");
370        try w.writeOperand(s, inst, 2, extra.rhs);
371    }
372
373    fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
374        const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
375        const val = w.air.values[ty_pl.payload];
376        try s.print("{}, {}", .{ w.air.getRefType(ty_pl.ty), val });
377    }
378
379    fn writeAssembly(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
380        const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
381        const air_asm = w.air.extraData(Air.Asm, ty_pl.payload);
382        const zir = w.zir;
383        const extended = zir.instructions.items(.data)[air_asm.data.zir_index].extended;
384        const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
385        const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
386        const outputs_len = @truncate(u5, extended.small);
387        const args_len = @truncate(u5, extended.small >> 5);
388        const clobbers_len = @truncate(u5, extended.small >> 10);
389        const args = @bitCast([]const Air.Inst.Ref, w.air.extra[air_asm.end..][0..args_len]);
390
391        var extra_i: usize = zir_extra.end;
392        const output_constraint: ?[]const u8 = out: {
393            var i: usize = 0;
394            while (i < outputs_len) : (i += 1) {
395                const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
396                extra_i = output.end;
397                break :out zir.nullTerminatedString(output.data.constraint);
398            }
399            break :out null;
400        };
401
402        try s.print("\"{s}\"", .{asm_source});
403
404        if (output_constraint) |constraint| {
405            const ret_ty = w.air.typeOfIndex(inst);
406            try s.print(", {s} -> {}", .{ constraint, ret_ty });
407        }
408
409        for (args) |arg| {
410            const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
411            extra_i = input.end;
412            const constraint = zir.nullTerminatedString(input.data.constraint);
413
414            try s.print(", {s} = (", .{constraint});
415            try w.writeOperand(s, inst, 0, arg);
416            try s.writeByte(')');
417        }
418
419        const clobbers = zir.extra[extra_i..][0..clobbers_len];
420        for (clobbers) |clobber_index| {
421            const clobber = zir.nullTerminatedString(clobber_index);
422            try s.writeAll(", ~{");
423            try s.writeAll(clobber);
424            try s.writeAll("}");
425        }
426    }
427
428    fn writeDbgStmt(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
429        const dbg_stmt = w.air.instructions.items(.data)[inst].dbg_stmt;
430        try s.print("{d}:{d}", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 });
431    }
432
433    fn writeCall(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
434        const pl_op = w.air.instructions.items(.data)[inst].pl_op;
435        const extra = w.air.extraData(Air.Call, pl_op.payload);
436        const args = @bitCast([]const Air.Inst.Ref, w.air.extra[extra.end..][0..extra.data.args_len]);
437        try w.writeOperand(s, inst, 0, pl_op.operand);
438        try s.writeAll(", [");
439        for (args) |arg, i| {
440            if (i != 0) try s.writeAll(", ");
441            try w.writeOperand(s, inst, 1 + i, arg);
442        }
443        try s.writeAll("]");
444    }
445
446    fn writeBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
447        const br = w.air.instructions.items(.data)[inst].br;
448        try w.writeInstIndex(s, br.block_inst, false);
449        try s.writeAll(", ");
450        try w.writeOperand(s, inst, 0, br.operand);
451    }
452
453    fn writeCondBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
454        const pl_op = w.air.instructions.items(.data)[inst].pl_op;
455        const extra = w.air.extraData(Air.CondBr, pl_op.payload);
456        const then_body = w.air.extra[extra.end..][0..extra.data.then_body_len];
457        const else_body = w.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
458        const liveness_condbr = w.liveness.getCondBr(inst);
459
460        try w.writeOperand(s, inst, 0, pl_op.operand);
461        try s.writeAll(", {\n");
462        const old_indent = w.indent;
463        w.indent += 2;
464
465        if (liveness_condbr.then_deaths.len != 0) {
466            try s.writeByteNTimes(' ', w.indent);
467            for (liveness_condbr.then_deaths) |operand, i| {
468                if (i != 0) try s.writeAll(" ");
469                try s.print("%{d}!", .{operand});
470            }
471            try s.writeAll("\n");
472        }
473
474        try w.writeBody(s, then_body);
475        try s.writeByteNTimes(' ', old_indent);
476        try s.writeAll("}, {\n");
477
478        if (liveness_condbr.else_deaths.len != 0) {
479            try s.writeByteNTimes(' ', w.indent);
480            for (liveness_condbr.else_deaths) |operand, i| {
481                if (i != 0) try s.writeAll(" ");
482                try s.print("%{d}!", .{operand});
483            }
484            try s.writeAll("\n");
485        }
486
487        try w.writeBody(s, else_body);
488        w.indent = old_indent;
489
490        try s.writeByteNTimes(' ', old_indent);
491        try s.writeAll("}");
492    }
493
494    fn writeSwitchBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
495        const pl_op = w.air.instructions.items(.data)[inst].pl_op;
496        const switch_br = w.air.extraData(Air.SwitchBr, pl_op.payload);
497        var extra_index: usize = switch_br.end;
498        var case_i: u32 = 0;
499
500        try w.writeOperand(s, inst, 0, pl_op.operand);
501        const old_indent = w.indent;
502        w.indent += 2;
503
504        while (case_i < switch_br.data.cases_len) : (case_i += 1) {
505            const case = w.air.extraData(Air.SwitchBr.Case, extra_index);
506            const items = @bitCast([]const Air.Inst.Ref, w.air.extra[case.end..][0..case.data.items_len]);
507            const case_body = w.air.extra[case.end + items.len ..][0..case.data.body_len];
508            extra_index = case.end + case.data.items_len + case_body.len;
509
510            try s.writeAll(", [");
511            for (items) |item, item_i| {
512                if (item_i != 0) try s.writeAll(", ");
513                try w.writeInstRef(s, item, false);
514            }
515            try s.writeAll("] => {\n");
516            w.indent += 2;
517            try w.writeBody(s, case_body);
518            w.indent -= 2;
519            try s.writeByteNTimes(' ', w.indent);
520            try s.writeAll("}");
521        }
522
523        const else_body = w.air.extra[extra_index..][0..switch_br.data.else_body_len];
524        if (else_body.len != 0) {
525            try s.writeAll(", else => {\n");
526            w.indent += 2;
527            try w.writeBody(s, else_body);
528            w.indent -= 2;
529            try s.writeByteNTimes(' ', w.indent);
530            try s.writeAll("}");
531        }
532
533        try s.writeAll("\n");
534        try s.writeByteNTimes(' ', old_indent);
535        try s.writeAll("}");
536    }
537
538    fn writeOperand(
539        w: *Writer,
540        s: anytype,
541        inst: Air.Inst.Index,
542        op_index: usize,
543        operand: Air.Inst.Ref,
544    ) @TypeOf(s).Error!void {
545        const dies = if (op_index < Liveness.bpi - 1)
546            w.liveness.operandDies(inst, @intCast(Liveness.OperandInt, op_index))
547        else blk: {
548            // TODO
549            break :blk false;
550        };
551        return w.writeInstRef(s, operand, dies);
552    }
553
554    fn writeInstRef(
555        w: *Writer,
556        s: anytype,
557        operand: Air.Inst.Ref,
558        dies: bool,
559    ) @TypeOf(s).Error!void {
560        var i: usize = @enumToInt(operand);
561
562        if (i < Air.Inst.Ref.typed_value_map.len) {
563            return s.print("@{}", .{operand});
564        }
565        i -= Air.Inst.Ref.typed_value_map.len;
566
567        return w.writeInstIndex(s, @intCast(Air.Inst.Index, i), dies);
568    }
569
570    fn writeInstIndex(
571        w: *Writer,
572        s: anytype,
573        inst: Air.Inst.Index,
574        dies: bool,
575    ) @TypeOf(s).Error!void {
576        _ = w;
577        if (dies) {
578            try s.print("%{d}!", .{inst});
579        } else {
580            try s.print("%{d}", .{inst});
581        }
582    }
583};
584