1//! This implements a debug formatter for Air
2air: Air,
3ip: *const InternPool,
4tty: std.io.tty.Config,
5
6pub const State = struct {
7 arena: std.mem.Allocator,
8 air: *const Air,
9 ip: *const InternPool,
10 indent: usize = 0,
11 tty: std.io.tty.Config,
12 visited: std.AutoHashMapUnmanaged(Inst.Index, void) = .{},
13
14 fn newline(state: State, writer: anytype) !void {
15 try writer.writeByte('\n');
16 if (state.indent > 0)
17 try writer.writeByteNTimes(' ', state.indent * 2);
18 }
19
20 fn dumpInst(s: *State, w: anytype, inst: Inst.Index) @TypeOf(w).Error!void {
21 const air = s.air;
22 const tty = s.tty;
23 const tag = s.air.instTag(inst);
24
25 try tty.setColor(w, switch (s.air.instFlags(inst).uses) {
26 .none => .bright_black,
27 .multi => .red,
28 .single => .magenta,
29 });
30 try w.writeByte(switch (s.air.instFlags(inst).uses) {
31 .none => 'X',
32 .multi => 'M',
33 .single => 'S',
34 });
35 try tty.setColor(w, .cyan);
36
37 try w.print(" #{} = ", .{
38 @intFromEnum(inst),
39 });
40
41 try tty.setColor(w, .cyan);
42 try w.print("{s}", .{@tagName(tag)});
43 try tty.setColor(w, .reset);
44
45 if ((try s.visited.fetchPut(s.arena, inst, {})) != null) {
46 try tty.setColor(w, .red);
47 try w.writeAll(" [error: recursive tree]");
48 try tty.setColor(w, .reset);
49 return;
50 }
51
52 switch (tag) {
53 .block => {
54 const index_list: Air.IndexList = air.instData(.block, inst);
55 const insts = index_list.get(air);
56 try s.dumpInstList(w, insts, '{', '}');
57 },
58 .arg => {
59 const arg: Air.Arg = air.instData(.arg, inst);
60 try w.writeAll(" ");
61 try tty.setColor(w, .yellow);
62 try w.print("{d}", .{arg.arg_number});
63 try tty.setColor(w, .reset);
64 try w.writeAll(": ");
65 try s.dumpValue(w, arg.type.value());
66 },
67 .@"break" => {
68 const brk: Air.Break = air.instData(.@"break", inst);
69 try w.writeAll("(");
70 try s.dumpInstRef(w, brk.block);
71 try w.writeAll(", ");
72 try s.dumpRef(w, brk.value);
73 try w.writeAll(")");
74 },
75 .store => {
76 const store: Air.Store = air.instData(.store, inst);
77 try w.writeAll("(");
78 try s.dumpInstRef(w, store.memory);
79 try w.writeAll(", ");
80 try s.dumpRef(w, store.operand);
81 try w.writeAll(")");
82 },
83 inline .add,
84 .mul,
85 .sub,
86 .div,
87 .lt,
88 .gt,
89 .eq,
90 .le,
91 .ge,
92 .field_array,
93 .field_opaque,
94 => |comptime_tag| {
95 const bin: Air.Bin = air.instData(comptime_tag, inst);
96 try w.writeAll("(");
97 try s.dumpRef(w, bin.lhs);
98 try w.writeAll(", ");
99 try s.dumpRef(w, bin.rhs);
100 try w.writeAll(")");
101 },
102 inline .negate,
103 .bool_not,
104 .array_len,
105 .panic,
106 => |comptime_tag| {
107 const bin: Air.Un = air.instData(comptime_tag, inst);
108 try w.writeAll("(");
109 try s.dumpRef(w, bin.operand);
110 try w.writeAll(")");
111 },
112 .memory => {
113 const memory: Air.Memory = air.instData(.memory, inst);
114 try w.writeAll("(");
115 try s.dumpRef(w, memory.init);
116 try w.writeAll(", type: ");
117 try s.dumpValue(w, memory.ty.value());
118 try w.writeAll(")");
119 },
120 .field_struct => {
121 const field: Air.FieldStruct = air.instData(.field_struct, inst);
122 try w.writeAll("(");
123 try s.dumpInstRef(w, field.base);
124 try w.writeAll(", ");
125 try tty.setColor(w, .magenta);
126 try w.print("{s}", .{
127 air
128 .typeOfIndex(field.base, s.ip)
129 .toStructType(s.ip).?
130 .field_keys.at(s.ip, field.field_index)
131 .toSlice(s.ip),
132 });
133 try tty.setColor(w, .reset);
134 try w.writeAll(")");
135 },
136 .field_tuple => {
137 const field: Air.FieldStruct = air.instData(.field_tuple, inst);
138 try w.writeAll("(");
139 try s.dumpInstRef(w, field.base);
140 try w.writeAll(", ");
141 try tty.setColor(w, .magenta);
142 try w.print("{d}", .{field.field_index});
143 try tty.setColor(w, .reset);
144 try w.writeAll(")");
145 },
146 inline .call, .js_new => |t| {
147 const call: Air.Call = air.instData(t, inst);
148 const call_data = call.call_data.get(air);
149 try w.writeAll("(");
150 try s.dumpRef(w, call.base);
151 try w.writeAll(", ");
152 try s.dumpRefList(w, call_data.args.get(air), '[', ']');
153 try w.writeAll(")");
154 },
155 .@"if" => {
156 const if_inst: Air.If = air.instData(.@"if", inst);
157 const if_data: Air.If.Data = if_inst.data.get(air);
158 try w.writeAll("(");
159 try s.dumpRef(w, if_inst.cond);
160 try w.writeAll(", {");
161 s.indent += 1;
162 try s.newline(w);
163 try w.writeAll("yes: ");
164 try s.dumpRefRecurse(w, if_data.yes);
165 try s.newline(w);
166 try w.writeAll("no: ");
167 try s.dumpRefRecurse(w, if_data.no);
168 s.indent -= 1;
169 try s.newline(w);
170 try w.writeAll("}");
171 },
172 .struct_init => {
173 const struct_init: Air.StructInit = air.instData(.struct_init, inst);
174 const struct_type = struct_init.ty.toStructType(s.ip).?;
175
176 try w.writeAll("(");
177 try s.dumpValue(w, struct_init.ty.value());
178 try w.writeAll(", {");
179 s.indent += 1;
180 for (struct_type.field_keys.get(s.ip), struct_init.valuesPtr(air)) |key, ref| {
181 try s.newline(w);
182 try w.print("{}: ", .{key.fmt(s.ip)});
183 try s.dumpRef(w, ref);
184 }
185 s.indent -= 1;
186 try s.newline(w);
187 try w.writeAll("})");
188 },
189 .array_init => {
190 const array_init: Air.ArrayInit = air.instData(.array_init, inst);
191 const array_type = array_init.ty.toArrayType(s.ip).?;
192
193 try w.writeAll("(");
194 try s.dumpValue(w, array_type.item_type.value());
195 try s.dumpRefList(w, array_init.list.get(air).get(air), '[', ']');
196 try w.writeAll(")");
197 },
198 .tuple_init => {
199 // const array_init: Air.ArrayInit = air.instData(.array_init, inst);
200 // const array_type = array_init.ty.toArrayType(s.ip).?;
201
202 // try w.writeAll("(");
203 // try s.dumpValue(w, array_type.item_type.value());
204 // try s.dumpRefList(w, array_init.list.get(air).slice(air), '[', ']');
205 // try w.writeAll(")");
206 @panic("TODO");
207 },
208 }
209 }
210
211 fn dumpRef(s: *State, w: anytype, ref: Inst.Ref) !void {
212 switch (ref.decode()) {
213 .index => |i| try s.dumpInstRef(w, i),
214 .value => |v| try s.dumpValue(w, v),
215 }
216 }
217
218 fn dumpRefRecurse(s: *State, w: anytype, ref: Inst.Ref) !void {
219 switch (ref.decode()) {
220 .index => |i| try s.dumpInst(w, i),
221 .value => |v| try s.dumpValue(w, v),
222 }
223 }
224
225 fn dumpInstRef(s: *State, w: anytype, inst: Inst.Index) @TypeOf(w).Error!void {
226 try s.tty.setColor(w, .red);
227 try w.print("#{d}", .{@intFromEnum(inst)});
228 try s.tty.setColor(w, .reset);
229 }
230
231 pub fn dumpValue(s: *State, w: anytype, val: Value) !void {
232 const tty = s.tty;
233 switch (val.toKey(s.ip)) {
234 .simple_type => |t| {
235 try tty.setColor(w, .blue);
236 try w.print("{s}", .{@tagName(t)});
237 try tty.setColor(w, .reset);
238 },
239 .simple_value => |t| {
240 try tty.setColor(w, .yellow);
241 try w.print("{s}", .{@tagName(t)});
242 try tty.setColor(w, .reset);
243 },
244 .function_type => |ty| {
245 try tty.setColor(w, .blue);
246 try w.writeAll("fn(");
247 try tty.setColor(w, .reset);
248 for (ty.param_types.get(s.ip), 0..) |param, i| {
249 if (i != 0) {
250 try tty.setColor(w, .blue);
251 try w.writeAll(", ");
252 try tty.setColor(w, .reset);
253 }
254 try s.dumpValue(w, param.value());
255 }
256 try tty.setColor(w, .blue);
257 try w.writeAll(") -> ");
258 try tty.setColor(w, .reset);
259 if (ty.return_type) |return_type|
260 try s.dumpValue(w, return_type.value())
261 else
262 try w.writeAll("[inferred]");
263 },
264 inline .float, .int32, .comptime_float => |data, tag| {
265 try tty.setColor(w, .bright_black);
266 try w.writeAll("<");
267 try tty.setColor(w, .blue);
268 try w.writeAll(@tagName(tag));
269 try tty.setColor(w, .reset);
270 try tty.setColor(w, .bright_black);
271 try w.writeAll(", ");
272 try tty.setColor(w, .yellow);
273 try w.print("{d}", .{data});
274 try tty.setColor(w, .bright_black);
275 try w.writeAll(">");
276 try tty.setColor(w, .reset);
277 },
278 .opaque_value => |inner| {
279 try tty.setColor(w, .bright_black);
280 try w.writeAll("<");
281 try tty.setColor(w, .blue);
282 try w.writeAll("opaque");
283 try tty.setColor(w, .reset);
284 try tty.setColor(w, .bright_black);
285 try w.writeAll(", ");
286 try s.dumpValue(w, inner);
287 try w.writeAll(">");
288 try tty.setColor(w, .reset);
289 },
290 .string => |data| {
291 try w.writeAll("<");
292 try tty.setColor(w, .blue);
293 try w.writeAll("string");
294 try tty.setColor(w, .reset);
295 try w.writeAll(", ");
296 try tty.setColor(w, .green);
297 try w.writeAll("\"");
298 try w.writeAll(data);
299 try w.writeAll("\"");
300 try tty.setColor(w, .reset);
301 try w.writeAll(">");
302 },
303 .func => |func| {
304 try tty.setColor(w, .bright_black);
305 try w.writeAll("<");
306 try s.dumpValue(w, func.ty.value());
307 try tty.setColor(w, .bright_black);
308 try w.writeAll(", ");
309 const decl_name = s.ip.declPtrConst(func.owner_decl).name.toSlice(s.ip);
310 try tty.setColor(w, .bright_green);
311 try w.print("decl '{s}'", .{decl_name});
312 try tty.setColor(w, .bright_black);
313 try w.writeAll(">");
314 try tty.setColor(w, .reset);
315 },
316 .struct_type => |struct_type| {
317 try tty.setColor(w, .bright_magenta);
318 try w.writeAll("struct"); // TODO: struct name
319 try tty.setColor(w, .reset);
320 try w.writeAll(" {");
321
322 if (struct_type.field_keys.len > 0)
323 try w.writeAll(" ");
324
325 for (
326 struct_type.field_keys.get(s.ip),
327 struct_type.field_types.get(s.ip),
328 0..,
329 ) |key, field_type, i| {
330 if (i != 0)
331 try w.writeAll(", ");
332 try w.writeAll(key.toSlice(s.ip));
333 try w.writeAll(": ");
334 try s.dumpValue(w, field_type.value());
335 }
336
337 if (struct_type.field_keys.len > 0)
338 try w.writeAll(" ");
339
340 try w.writeAll("}");
341 },
342 .struct_value => |struct_value| {
343 try tty.setColor(w, .bright_black);
344 try w.writeAll("<");
345 try tty.setColor(w, .bright_magenta);
346 try w.writeAll("struct"); // TODO: struct name
347 try tty.setColor(w, .bright_black);
348 try w.writeAll(", ");
349 try tty.setColor(w, .reset);
350 try w.writeAll("{");
351
352 if (struct_value.field_values.len > 0)
353 try w.writeAll(" ");
354
355 for (
356 struct_value.ty.toStructType(s.ip).?.field_keys.get(s.ip),
357 struct_value.field_values.get(s.ip),
358 0..,
359 ) |key, value, i| {
360 if (i != 0)
361 try w.writeAll(", ");
362 try w.writeAll(key.toSlice(s.ip));
363 try w.writeAll(": ");
364 try s.dumpValue(w, value);
365 }
366
367 if (struct_value.field_values.len > 0)
368 try w.writeAll(" ");
369
370 try w.writeAll("}");
371 try tty.setColor(w, .bright_black);
372 try w.writeAll(">");
373 try tty.setColor(w, .reset);
374 },
375 .array_type => |array_type| {
376 try w.writeAll("[]");
377 try s.dumpValue(w, array_type.item_type.value());
378 },
379 .array_value => |array_value| {
380 try tty.setColor(w, .bright_black);
381 try w.writeAll("<");
382 try tty.setColor(w, .bright_magenta);
383 try s.dumpValue(w, array_value.type.value());
384 try tty.setColor(w, .bright_black);
385 try w.writeAll(", ");
386 try tty.setColor(w, .reset);
387 try w.writeAll("[");
388
389 for (array_value.items.get(s.ip), 0..) |value, i| {
390 if (i != 0)
391 try w.writeAll(", ");
392 try s.dumpValue(w, value);
393 }
394
395 try w.writeAll("]");
396 try tty.setColor(w, .bright_black);
397 try w.writeAll(">");
398 try tty.setColor(w, .reset);
399 },
400 .external_value => |external_value| {
401 try w.writeAll("external '");
402 for (external_value.key.get(s.ip), 0..) |part, i| {
403 if (i != 0) try w.writeAll(".");
404 try w.writeAll(part.toSlice(s.ip));
405 }
406 try w.writeAll("'");
407 },
408 .tuple_type => |tuple_type| {
409 try w.writeAll("(");
410 for (tuple_type.items.get(s.ip), 0..) |item, i| {
411 if (i != 0) try w.writeAll(", ");
412 try s.dumpValue(w, item.value());
413 }
414 try w.writeAll(")");
415 },
416 .tuple_value => |tuple_value| {
417 try w.writeAll("(");
418 for (tuple_value.items.get(s.ip), 0..) |item, i| {
419 if (i != 0) try w.writeAll(", ");
420 try s.dumpValue(w, item);
421 }
422 try w.writeAll(")");
423 },
424 }
425 }
426
427 fn dumpInstList(state: *State, writer: anytype, list: []const Inst.Index, l: u8, r: u8) !void {
428 try writer.print(" {c}", .{l});
429 if (list.len > 0) {
430 state.indent += 1;
431
432 for (list) |inst| {
433 try state.newline(writer);
434 try state.dumpInst(writer, inst);
435 }
436
437 state.indent -= 1;
438 try state.newline(writer);
439 }
440 try writer.print("{c}", .{r});
441 }
442
443 fn dumpRefList(state: *State, writer: anytype, list: []const Inst.Ref, l: u8, r: u8) !void {
444 try writer.print(" {c}", .{l});
445 if (list.len > 0) {
446 state.indent += 1;
447
448 for (list) |inst| {
449 try state.newline(writer);
450 try state.dumpRef(writer, inst);
451 }
452
453 state.indent -= 1;
454 try state.newline(writer);
455 }
456 try writer.print("{c}", .{r});
457 }
458};
459
460pub fn format(
461 dump: @This(),
462 comptime unused_fmt: []const u8,
463 _: std.fmt.FormatOptions,
464 writer: anytype,
465) !void {
466 comptime assert(unused_fmt.len == 0);
467
468 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
469 defer arena.deinit();
470
471 const air = dump.air;
472 var state = State{
473 .air = &air,
474 .ip = dump.ip,
475 .tty = dump.tty,
476 .arena = arena.allocator(),
477 };
478 try state.dumpInst(writer, .main_block);
479 try writer.writeAll("\n");
480 assert(state.indent == 0);
481}
482
483const std = @import("std");
484const assert = std.debug.assert;
485
486const core = @import("mod.zig");
487const Air = core.Air;
488const Inst = Air.Inst;
489const InternPool = core.InternPool;
490const Value = InternPool.Value;