| 1 | //! This implements a debug formatter for Air |
| 2 | air: Air, |
| 3 | ip: *const InternPool, |
| 4 | tty: std.io.tty.Config, |
| 5 | |
| 6 | pub 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 | |
| 460 | pub 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 | |
| 483 | const std = @import("std"); |
| 484 | const assert = std.debug.assert; |
| 485 | |
| 486 | const core = @import("mod.zig"); |
| 487 | const Air = core.Air; |
| 488 | const Inst = Air.Inst; |
| 489 | const InternPool = core.InternPool; |
| 490 | const Value = InternPool.Value; |