1//! This implements a debug formatter for Hir
2hir: Hir,
3tty: ?std.io.tty.Config = null,
4
5const State = struct {
6 arena: std.mem.Allocator,
7 hir: *const Hir,
8 indent: usize = 0,
9 tty: std.io.tty.Config,
10 visited: std.AutoHashMapUnmanaged(Hir.Inst.Index, void) = .{},
11
12 fn newline(state: State, writer: anytype) !void {
13 try writer.writeByte('\n');
14 if (state.indent > 0)
15 try writer.writeByteNTimes(' ', state.indent * 2);
16 }
17
18 fn dumpInst(s: *State, writer: anytype, inst: Hir.Inst.Index) @TypeOf(writer).Error!void {
19 const hir = s.hir;
20 const tty = s.tty;
21
22 try writer.print("%{} = ", .{@intFromEnum(inst)});
23
24 const tag = s.hir.instTag(inst);
25
26 try tty.setColor(writer, .bright_black);
27 const node_offset = hir.instNodeOffset(inst);
28 try writer.print("{c}{d} ", .{
29 @as(u8, if (node_offset >= 0) '+' else '-'),
30 @abs(node_offset),
31 });
32 try tty.setColor(writer, .reset);
33
34 try tty.setColor(writer, .cyan);
35 try writer.print("{s}", .{@tagName(tag)});
36 try tty.setColor(writer, .reset);
37
38 if ((try s.visited.fetchPut(s.arena, inst, {})) != null) {
39 try tty.setColor(writer, .red);
40 try writer.writeAll(" [error: recursive tree]");
41 try tty.setColor(writer, .reset);
42 return;
43 }
44
45 switch (tag) {
46 .module => {
47 try s.dumpInstList(writer, hir.moduleDeclarations(inst), '{', '}');
48 },
49 .declaration, .variable_declaration, .test_declaration => {
50 const decl = hir.fullDecl(inst);
51
52 try writer.writeAll(" ");
53 const name = decl.name.slice(hir.string_bytes);
54 if (name.len == 0) {
55 try tty.setColor(writer, .bold);
56 try writer.writeAll("[no name]");
57 } else {
58 try tty.setColor(writer, .magenta);
59 try writer.writeAll(name);
60 }
61 try tty.setColor(writer, .reset);
62 try writer.writeAll(" (");
63 try writer.print("{d}", .{decl.instruction_count});
64 try writer.writeAll(")");
65 try tty.setColor(writer, .bright_black);
66 try writer.writeAll(" [");
67 try writer.writeAll(&std.fmt.bytesToHex(decl.hash, .lower));
68 try writer.writeAll("]");
69 try tty.setColor(writer, .reset);
70 try writer.writeAll(" (");
71 s.indent += 1;
72 try s.newline(writer);
73 try s.dumpRef(writer, decl.body);
74 s.indent -= 1;
75 try s.newline(writer);
76 try writer.writeAll(")");
77 },
78 .func => {
79 const func: Hir.Func = hir.instPayload(.func, inst);
80 try writer.writeAll(" {");
81 s.indent += 1;
82 try s.newline(writer);
83 try writer.writeAll("params: ");
84 try s.dumpOptionalRefList(writer, func.params.slice(hir), '[', ']');
85 try s.newline(writer);
86 try writer.writeAll("return_type: ");
87 if (func.return_type.unwrap()) |ret|
88 try s.dumpRef(writer, ret)
89 else {
90 try tty.setColor(writer, .bright_black);
91 try writer.writeAll("<inferred>");
92 try tty.setColor(writer, .reset);
93 }
94 try s.newline(writer);
95 try writer.writeAll("captures: ");
96 try s.dumpInstList2(writer, func.captures.slice(hir), '[', ']');
97 try s.newline(writer);
98 try writer.writeAll("body: ");
99 try s.dumpInst(writer, func.body);
100 s.indent -= 1;
101 try s.newline(writer);
102 try writer.writeAll("}");
103 },
104 .param => {
105 const data = hir.instData(.param, inst);
106 try writer.writeAll("(");
107 if (data.data == std.math.maxInt(u32)) {
108 try tty.setColor(writer, .bright_black);
109 try writer.writeAll("<return type>");
110 } else {
111 try tty.setColor(writer, .yellow);
112 try writer.print("{d}", .{data.data});
113 }
114 try tty.setColor(writer, .reset);
115 try writer.writeAll(")");
116 },
117 inline .block, .@"comptime" => |t| {
118 const block = hir.instData(t, inst);
119 const index_list = block.extra_index.get(hir);
120 try s.dumpInstList(writer, index_list.slice(hir), '[', ']');
121 },
122 .number => {
123 const number = hir.instData(.number, inst);
124 const float = number.extra_index.get(hir).decode();
125 try tty.setColor(writer, .yellow);
126 try writer.print(" {d}", .{float});
127 try tty.setColor(writer, .reset);
128 },
129 inline .field,
130 .index,
131 .add,
132 .sub,
133 .mul,
134 .div,
135 .rem,
136 .pow,
137 .@"and",
138 .@"or",
139 .bit_and,
140 .bit_or,
141 .bit_xor,
142 .shl,
143 .shr,
144 .eq,
145 .ne,
146 .lt,
147 .le,
148 .gt,
149 .ge,
150 => |comptime_tag| {
151 const data = hir.instData(comptime_tag, inst).extra_index.get(hir);
152 try writer.writeAll("(");
153 try s.dumpRefMarker(writer, data.lhs);
154 try writer.writeAll(", ");
155 try s.dumpRefMarker(writer, data.rhs);
156 try writer.writeAll(")");
157 },
158 .coercion => {
159 const data = hir.instData(.coercion, inst).extra_index.get(hir);
160 try writer.writeAll("(");
161 try s.dumpRefMarker(writer, data.type);
162 try writer.writeAll(", ");
163 try s.dumpRefMarker(writer, data.operand);
164 try writer.writeAll(")");
165 },
166 .@"break" => {
167 const data: Hir.Break = hir.instData(.@"break", inst).extra_index.get(hir);
168 try writer.writeAll("(");
169 try s.dumpRefMarker(writer, data.block.toRef());
170 try writer.writeAll(", ");
171 try s.dumpRefMarker(writer, data.operand);
172 try writer.writeAll(")");
173 },
174 .store => {
175 const data: Hir.Store = hir.instData(.store, inst).extra_index.get(hir);
176 try writer.writeAll("(");
177 try s.dumpRefMarker(writer, data.memory.toRef());
178 try writer.writeAll(", ");
179 try s.dumpRefMarker(writer, data.operand);
180 try writer.writeAll(")");
181 },
182 .store_field => {
183 const data: Hir.StoreField = hir.instData(.store_field, inst).extra_index.get(hir);
184 try writer.writeAll("(");
185 try s.dumpRefMarker(writer, data.base);
186 try writer.writeAll(", ");
187 try s.dumpRefMarker(writer, data.field);
188 try writer.writeAll(", ");
189 try s.dumpRefMarker(writer, data.operand);
190 try writer.writeAll(")");
191 },
192 .store_decl => {
193 const data: Hir.StoreVarDecl = hir.instData(.store_decl, inst).extra_index.get(hir);
194 try writer.writeAll("(");
195 try tty.setColor(writer, .magenta);
196 try writer.writeAll(data.var_decl.slice(hir.string_bytes));
197 try tty.setColor(writer, .reset);
198 try writer.writeAll(", ");
199 try s.dumpRefMarker(writer, data.operand);
200 try writer.writeAll(")");
201 },
202 .string => {
203 const data: Hir.SlicedString = hir.instData(.string, inst).extra_index.get(hir);
204 try tty.setColor(writer, .green);
205 try writer.writeAll(" \"");
206 try writer.writeAll(data.slice(hir.string_bytes));
207 try writer.writeAll("\"");
208 try tty.setColor(writer, .reset);
209 },
210 inline .memory,
211 .discard,
212 .ensure_void,
213 .negate,
214 .bool_not,
215 .bit_not,
216 .@"extern",
217 .import,
218 .fetch,
219 .compile_error,
220 .type_of,
221 .array_type,
222 => |comptime_tag| {
223 const data = hir.instData(comptime_tag, inst);
224 try writer.writeAll("(");
225 try s.dumpRefMarker(writer, data.operand);
226 try writer.writeAll(")");
227 },
228 .assert => {
229 const data = hir.instData(.assert, inst);
230 try writer.writeAll("(");
231 try s.dumpRef(writer, data.operand);
232 try writer.writeAll(")");
233 },
234 .call => {
235 const call: Hir.Call = hir.instData(.call, inst).extra_index.get(hir);
236 try writer.writeAll("(");
237 try s.dumpRefMarker(writer, call.base);
238 try writer.writeAll(", ");
239 try s.dumpRefMarkerList(writer, call.args.slice(hir), '[', ']');
240 try writer.writeAll(")");
241 },
242 .js_new => {
243 const call: Hir.JsNew = hir.instData(.js_new, inst).extra_index.get(hir);
244 try writer.writeAll("(");
245 try s.dumpRefMarker(writer, call.base);
246 try writer.writeAll(", ");
247 try s.dumpRefMarkerList(writer, call.args.slice(hir), '[', ']');
248 try writer.writeAll(")");
249 },
250 .load_decl => {
251 const call: Hir.StrNode = hir.instData(.load_decl, inst);
252
253 try tty.setColor(writer, .red);
254 try writer.print(" {s}", .{call.string.slice(hir.string_bytes)});
255 try tty.setColor(writer, .reset);
256 },
257 .@"if" => {
258 const data: Hir.If = hir.instPayload(.@"if", inst);
259 try writer.writeAll("(");
260 try s.dumpRefMarker(writer, data.cond);
261 try writer.writeAll(") {");
262 s.indent += 1;
263 try s.newline(writer);
264 try writer.writeAll("yes: ");
265 try s.dumpRef(writer, data.yes);
266 try s.newline(writer);
267 try writer.writeAll("no: ");
268 try s.dumpRef(writer, data.no);
269 s.indent -= 1;
270 try s.newline(writer);
271 try writer.writeAll("}");
272 },
273 .struct_decl => {
274 const data: Hir.StructDecl = hir.instPayload(.struct_decl, inst);
275 try writer.writeAll(" {");
276 s.indent += 1;
277 try s.newline(writer);
278 for (data.keys(hir), data.types(hir), 0..) |key, value, i| {
279 if (i != 0)
280 try s.newline(writer);
281 try writer.print("{s}: ", .{key.slice(hir.string_bytes)});
282 try s.dumpRefMarker(writer, value);
283 }
284 s.indent -= 1;
285 try s.newline(writer);
286 try writer.writeAll("}");
287 },
288 .struct_init => {
289 const data: Hir.StructInit = hir.instPayload(.struct_init, inst);
290 try writer.writeAll("(");
291 try s.dumpRefMarker(writer, data.base_type);
292 try writer.writeAll(") {");
293 s.indent += 1;
294 try s.newline(writer);
295 for (data.keys(hir), data.inits(hir), 0..) |key, value, i| {
296 if (i != 0)
297 try s.newline(writer);
298 try writer.print("{s}: ", .{key.slice(hir.string_bytes)});
299 try s.dumpRefMarker(writer, value);
300 }
301 s.indent -= 1;
302 try s.newline(writer);
303 try writer.writeAll("}");
304 },
305 .array_init => {
306 const data: Hir.ArrayInit = hir.instPayload(.array_init, inst);
307
308 if (data.type_hint.unwrap()) |type_hint| {
309 try writer.writeAll("(");
310 try s.dumpRefMarker(writer, type_hint);
311 try writer.writeAll(", ");
312 }
313
314 try writer.writeAll("[");
315
316 s.indent += 1;
317
318 for (data.list.slice(hir)) |item| {
319 try s.newline(writer);
320 try s.dumpRefMarker(writer, item);
321 try writer.writeAll(",");
322 }
323
324 s.indent -= 1;
325 try s.newline(writer);
326
327 try writer.writeAll("]");
328
329 if (data.type_hint != .none)
330 try writer.writeAll(")");
331 },
332 .tuple_type => {
333 const data: Hir.TupleType = hir.instPayload(.tuple_type, inst);
334
335 try writer.writeAll("(");
336 try s.dumpRefMarkerList(writer, data.items.slice(hir), '[', ']');
337 try writer.writeAll(")");
338 },
339 .tuple_init => {
340 const data: Hir.TupleInit = hir.instPayload(.tuple_init, inst);
341
342 try writer.writeAll("(");
343 if (data.type_hint.unwrap()) |type_hint| {
344 try s.dumpRef(writer, type_hint);
345 try writer.writeAll(", ");
346 }
347 try s.dumpRefMarkerList(writer, data.items.slice(hir), '[', ']');
348 try writer.writeAll(")");
349 },
350 .result_type_argument => {
351 const data: Hir.ResultTypeArgument = hir.instData(.result_type_argument, inst);
352
353 try writer.writeAll("(");
354 try s.dumpRefMarker(writer, data.call_base);
355 try writer.print(", {d}", .{data.argument});
356 try writer.writeAll(")");
357 },
358 .result_type_struct_field => {
359 const data: Hir.ResultTypeStructField = hir.instData(.result_type_struct_field, inst);
360
361 try writer.writeAll("(");
362 try s.dumpRefMarker(writer, data.type);
363 try writer.print(", {s}", .{data.field.slice(s.hir.string_bytes)});
364 try writer.writeAll(")");
365 },
366 .result_type_tuple_field => {
367 const data: Hir.ResultTypeTupleField = hir.instData(.result_type_tuple_field, inst);
368
369 try writer.writeAll("(");
370 try s.dumpRefMarker(writer, data.type);
371 try writer.print(", {d}", .{data.item});
372 try writer.writeAll(")");
373 },
374 .result_type_array_value => {
375 const data: Hir.ResultTypeArrayValue = hir.instData(.result_type_array_value, inst);
376
377 try writer.writeAll("(");
378 try s.dumpRefMarker(writer, data.type);
379 try writer.writeAll(")");
380 },
381 }
382 }
383
384 fn dumpRef(s: *State, writer: anytype, ref: Hir.Inst.Ref) !void {
385 switch (ref.decode()) {
386 .index => |index| try s.dumpInst(writer, index),
387 .value => {
388 try s.tty.setColor(writer, .blue);
389 try writer.print("%{s}", .{@tagName(ref)});
390 try s.tty.setColor(writer, .reset);
391 },
392 }
393 }
394
395 fn dumpRefMarker(s: *State, writer: anytype, ref: Hir.Inst.Ref) !void {
396 switch (ref.decode()) {
397 .index => |index| {
398 try s.tty.setColor(writer, .blue);
399 try writer.print("%{d}", .{@intFromEnum(index)});
400 try s.tty.setColor(writer, .reset);
401 },
402 .value => {
403 try s.tty.setColor(writer, .blue);
404 try writer.print("%{s}", .{@tagName(ref)});
405 try s.tty.setColor(writer, .reset);
406 },
407 }
408 }
409
410 fn dumpInstList(state: *State, writer: anytype, list: []const Hir.Inst.Index, l: u8, r: u8) !void {
411 try writer.print(" {c}", .{l});
412 if (list.len > 0) {
413 state.indent += 1;
414
415 for (list) |inst| {
416 try state.newline(writer);
417 try state.dumpInst(writer, inst);
418 }
419
420 state.indent -= 1;
421 try state.newline(writer);
422 }
423 try writer.print("{c}", .{r});
424 }
425
426 fn dumpInstList2(state: *State, writer: anytype, list: []const Hir.Inst.Index, l: u8, r: u8) !void {
427 try writer.print(" {c}", .{l});
428 if (list.len > 0) {
429 state.indent += 1;
430
431 for (list) |inst| {
432 try state.newline(writer);
433 try state.dumpRefMarker(writer, inst.toRef());
434 }
435
436 state.indent -= 1;
437 try state.newline(writer);
438 }
439 try writer.print("{c}", .{r});
440 }
441
442 fn dumpRefList(state: *State, writer: anytype, list: []const Hir.Inst.Ref, l: u8, r: u8) !void {
443 try writer.print(" {c}", .{l});
444 if (list.len > 0) {
445 state.indent += 1;
446
447 for (list) |ref| {
448 try state.newline(writer);
449 try state.dumpRef(writer, ref);
450 }
451
452 state.indent -= 1;
453
454 try state.newline(writer);
455 }
456 try writer.print("{c}", .{r});
457 }
458
459 fn dumpOptionalRefList(state: *State, writer: anytype, list: []const Hir.Inst.Ref.Optional, l: u8, r: u8) !void {
460 try writer.print(" {c}", .{l});
461 if (list.len > 0) {
462 state.indent += 1;
463
464 for (list) |optional_ref| {
465 try state.newline(writer);
466 if (optional_ref.unwrap()) |ref|
467 try state.dumpRef(writer, ref)
468 else {
469 try state.tty.setColor(writer, .bright_black);
470 try writer.writeAll("<inferred>");
471 try state.tty.setColor(writer, .reset);
472 }
473 }
474
475 state.indent -= 1;
476
477 try state.newline(writer);
478 }
479 try writer.print("{c}", .{r});
480 }
481
482 fn dumpRefMarkerList(state: *State, writer: anytype, list: []const Hir.Inst.Ref, l: u8, r: u8) !void {
483 try writer.print("{c}", .{l});
484 if (list.len > 0) {
485 state.indent += 1;
486
487 for (list) |ref| {
488 try state.newline(writer);
489 try state.dumpRefMarker(writer, ref);
490 }
491
492 state.indent -= 1;
493
494 try state.newline(writer);
495 }
496 try writer.print("{c}", .{r});
497 }
498};
499
500pub fn format(
501 dump: @This(),
502 comptime unused_fmt: []const u8,
503 _: std.fmt.FormatOptions,
504 writer: anytype,
505) !void {
506 comptime assert(unused_fmt.len == 0);
507
508 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
509 defer arena.deinit();
510
511 const hir = dump.hir;
512 var state = State{
513 .hir = &hir,
514 .tty = dump.tty orelse detectTTYFromWriter(writer),
515 .arena = arena.allocator(),
516 };
517 try state.dumpInst(writer, .main_module_inst);
518 try writer.writeAll("\n");
519 assert(state.indent == 0);
520}
521
522const std = @import("std");
523const assert = std.debug.assert;
524
525const core = @import("mod.zig");
526const Hir = core.Hir;
527const detectTTYFromWriter = core.detectTTYFromWriter;