| 1 | const std = @import("std"); |
| 2 | const Allocator = std.mem.Allocator; |
| 3 | const assert = std.debug.assert; |
| 4 | const Interner = @import("Interner.zig"); |
| 5 | const Object = @import("Object.zig"); |
| 6 | |
| 7 | const Ir = @This(); |
| 8 | |
| 9 | interner: *Interner, |
| 10 | decls: std.array_hash_map.String(Decl), |
| 11 | |
| 12 | pub const Decl = struct { |
| 13 | instructions: std.MultiArrayList(Inst), |
| 14 | body: std.ArrayList(Ref), |
| 15 | arena: std.heap.ArenaAllocator.State, |
| 16 | |
| 17 | pub fn deinit(decl: *Decl, gpa: Allocator) void { |
| 18 | decl.instructions.deinit(gpa); |
| 19 | decl.body.deinit(gpa); |
| 20 | decl.arena.promote(gpa).deinit(); |
| 21 | } |
| 22 | }; |
| 23 | |
| 24 | pub const Builder = struct { |
| 25 | gpa: Allocator, |
| 26 | arena: std.heap.ArenaAllocator, |
| 27 | interner: *Interner, |
| 28 | |
| 29 | decls: std.array_hash_map.String(Decl) = .empty, |
| 30 | instructions: std.MultiArrayList(Ir.Inst) = .empty, |
| 31 | body: std.ArrayList(Ref) = .empty, |
| 32 | alloc_count: u32 = 0, |
| 33 | arg_count: u32 = 0, |
| 34 | current_label: Ref = undefined, |
| 35 | |
| 36 | pub fn deinit(b: *Builder) void { |
| 37 | for (b.decls.values()) |*decl| { |
| 38 | decl.deinit(b.gpa); |
| 39 | } |
| 40 | b.decls.deinit(b.gpa); |
| 41 | b.arena.deinit(); |
| 42 | b.instructions.deinit(b.gpa); |
| 43 | b.body.deinit(b.gpa); |
| 44 | b.* = undefined; |
| 45 | } |
| 46 | |
| 47 | pub fn finish(b: *Builder) Ir { |
| 48 | return .{ |
| 49 | .interner = b.interner, |
| 50 | .decls = b.decls.move(), |
| 51 | }; |
| 52 | } |
| 53 | |
| 54 | pub fn startFn(b: *Builder) Allocator.Error!void { |
| 55 | const entry = try b.makeLabel("entry"); |
| 56 | try b.body.append(b.gpa, entry); |
| 57 | b.current_label = entry; |
| 58 | } |
| 59 | |
| 60 | pub fn finishFn(b: *Builder, name: []const u8) !void { |
| 61 | var duped_instructions = try b.instructions.clone(b.gpa); |
| 62 | errdefer duped_instructions.deinit(b.gpa); |
| 63 | var duped_body = try b.body.clone(b.gpa); |
| 64 | errdefer duped_body.deinit(b.gpa); |
| 65 | |
| 66 | try b.decls.put(b.gpa, name, .{ |
| 67 | .instructions = duped_instructions, |
| 68 | .body = duped_body, |
| 69 | .arena = b.arena.state, |
| 70 | }); |
| 71 | b.instructions.shrinkRetainingCapacity(0); |
| 72 | b.body.shrinkRetainingCapacity(0); |
| 73 | b.arena = std.heap.ArenaAllocator.init(b.gpa); |
| 74 | b.alloc_count = 0; |
| 75 | b.arg_count = 0; |
| 76 | } |
| 77 | |
| 78 | pub fn startBlock(b: *Builder, label: Ref) !void { |
| 79 | try b.body.append(b.gpa, label); |
| 80 | b.current_label = label; |
| 81 | } |
| 82 | |
| 83 | pub fn addArg(b: *Builder, ty: Interner.Ref) Allocator.Error!Ref { |
| 84 | const ref: Ref = @fromBackingInt(@intCast(b.instructions.len)); |
| 85 | try b.instructions.append(b.gpa, .{ .tag = .arg, .data = .{ .none = {} }, .ty = ty }); |
| 86 | try b.body.insert(b.gpa, b.arg_count, ref); |
| 87 | b.arg_count += 1; |
| 88 | return ref; |
| 89 | } |
| 90 | |
| 91 | pub fn addAlloc(b: *Builder, size: u32, @"align": u32) Allocator.Error!Ref { |
| 92 | const ref: Ref = @fromBackingInt(@intCast(b.instructions.len)); |
| 93 | try b.instructions.append(b.gpa, .{ |
| 94 | .tag = .alloc, |
| 95 | .data = .{ .alloc = .{ .size = size, .@"align" = @"align" } }, |
| 96 | .ty = .ptr, |
| 97 | }); |
| 98 | try b.body.insert(b.gpa, b.alloc_count + b.arg_count + 1, ref); |
| 99 | b.alloc_count += 1; |
| 100 | return ref; |
| 101 | } |
| 102 | |
| 103 | pub fn addInst(b: *Builder, tag: Ir.Inst.Tag, data: Ir.Inst.Data, ty: Interner.Ref) Allocator.Error!Ref { |
| 104 | const ref: Ref = @fromBackingInt(@intCast(b.instructions.len)); |
| 105 | try b.instructions.append(b.gpa, .{ .tag = tag, .data = data, .ty = ty }); |
| 106 | try b.body.append(b.gpa, ref); |
| 107 | return ref; |
| 108 | } |
| 109 | |
| 110 | pub fn makeLabel(b: *Builder, name: [*:0]const u8) Allocator.Error!Ref { |
| 111 | const ref: Ref = @fromBackingInt(@intCast(b.instructions.len)); |
| 112 | try b.instructions.append(b.gpa, .{ .tag = .label, .data = .{ .label = name }, .ty = .void }); |
| 113 | return ref; |
| 114 | } |
| 115 | |
| 116 | pub fn addJump(b: *Builder, label: Ref) Allocator.Error!void { |
| 117 | _ = try b.addInst(.jmp, .{ .un = label }, .noreturn); |
| 118 | } |
| 119 | |
| 120 | pub fn addBranch(b: *Builder, cond: Ref, true_label: Ref, false_label: Ref) Allocator.Error!void { |
| 121 | const branch = try b.arena.allocator().create(Ir.Inst.Branch); |
| 122 | branch.* = .{ |
| 123 | .cond = cond, |
| 124 | .then = true_label, |
| 125 | .@"else" = false_label, |
| 126 | }; |
| 127 | _ = try b.addInst(.branch, .{ .branch = branch }, .noreturn); |
| 128 | } |
| 129 | |
| 130 | pub fn addSwitch(b: *Builder, target: Ref, values: []Interner.Ref, labels: []Ref, default: Ref) Allocator.Error!void { |
| 131 | assert(values.len == labels.len); |
| 132 | const a = b.arena.allocator(); |
| 133 | const @"switch" = try a.create(Ir.Inst.Switch); |
| 134 | @"switch".* = .{ |
| 135 | .target = target, |
| 136 | .cases_len = @intCast(values.len), |
| 137 | .case_vals = (try a.dupe(Interner.Ref, values)).ptr, |
| 138 | .case_labels = (try a.dupe(Ref, labels)).ptr, |
| 139 | .default = default, |
| 140 | }; |
| 141 | _ = try b.addInst(.@"switch", .{ .@"switch" = @"switch" }, .noreturn); |
| 142 | } |
| 143 | |
| 144 | pub fn addStore(b: *Builder, ptr: Ref, val: Ref) Allocator.Error!void { |
| 145 | _ = try b.addInst(.store, .{ .bin = .{ .lhs = ptr, .rhs = val } }, .void); |
| 146 | } |
| 147 | |
| 148 | pub fn addConstant(b: *Builder, val: Interner.Ref, ty: Interner.Ref) Allocator.Error!Ref { |
| 149 | const ref: Ref = @fromBackingInt(@intCast(b.instructions.len)); |
| 150 | try b.instructions.append(b.gpa, .{ |
| 151 | .tag = .constant, |
| 152 | .data = .{ .constant = val }, |
| 153 | .ty = ty, |
| 154 | }); |
| 155 | return ref; |
| 156 | } |
| 157 | |
| 158 | pub fn addPhi(b: *Builder, inputs: []const Inst.Phi.Input, ty: Interner.Ref) Allocator.Error!Ref { |
| 159 | const a = b.arena.allocator(); |
| 160 | const input_refs = try a.alloc(Ref, inputs.len * 2 + 1); |
| 161 | input_refs[0] = @fromBackingInt(@intCast(inputs.len)); |
| 162 | @memcpy(input_refs[1..], std.mem.bytesAsSlice(Ref, std.mem.sliceAsBytes(inputs))); |
| 163 | |
| 164 | return b.addInst(.phi, .{ .phi = .{ .ptr = input_refs.ptr } }, ty); |
| 165 | } |
| 166 | |
| 167 | pub fn addSelect(b: *Builder, cond: Ref, then: Ref, @"else": Ref, ty: Interner.Ref) Allocator.Error!Ref { |
| 168 | const branch = try b.arena.allocator().create(Ir.Inst.Branch); |
| 169 | branch.* = .{ |
| 170 | .cond = cond, |
| 171 | .then = then, |
| 172 | .@"else" = @"else", |
| 173 | }; |
| 174 | return b.addInst(.select, .{ .branch = branch }, ty); |
| 175 | } |
| 176 | }; |
| 177 | |
| 178 | pub const Renderer = struct { |
| 179 | gpa: Allocator, |
| 180 | obj: *Object, |
| 181 | ir: *const Ir, |
| 182 | errors: ErrorList = .{}, |
| 183 | |
| 184 | pub const ErrorList = std.array_hash_map.String([]const u8); |
| 185 | |
| 186 | pub const Error = Allocator.Error || error{LowerFail}; |
| 187 | |
| 188 | pub fn deinit(r: *Renderer) void { |
| 189 | for (r.errors.values()) |msg| r.gpa.free(msg); |
| 190 | r.errors.deinit(r.gpa); |
| 191 | } |
| 192 | |
| 193 | pub fn render(r: *Renderer) !void { |
| 194 | switch (r.obj.target.cpu.arch) { |
| 195 | .x86, .x86_64 => return @import("Ir/x86/Renderer.zig").render(r), |
| 196 | else => unreachable, |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | pub fn fail( |
| 201 | r: *Renderer, |
| 202 | name: []const u8, |
| 203 | comptime format: []const u8, |
| 204 | args: anytype, |
| 205 | ) Error { |
| 206 | try r.errors.ensureUnusedCapacity(r.gpa, 1); |
| 207 | r.errors.putAssumeCapacity(name, try std.fmt.allocPrint(r.gpa, format, args)); |
| 208 | return error.LowerFail; |
| 209 | } |
| 210 | }; |
| 211 | |
| 212 | pub fn render( |
| 213 | ir: *const Ir, |
| 214 | gpa: Allocator, |
| 215 | target: std.Target, |
| 216 | errors: ?*Renderer.ErrorList, |
| 217 | ) !*Object { |
| 218 | const obj = try Object.create(gpa, target); |
| 219 | errdefer obj.deinit(); |
| 220 | |
| 221 | var renderer: Renderer = .{ |
| 222 | .gpa = gpa, |
| 223 | .obj = obj, |
| 224 | .ir = ir, |
| 225 | }; |
| 226 | defer { |
| 227 | if (errors) |some| { |
| 228 | some.* = renderer.errors.move(); |
| 229 | } |
| 230 | renderer.deinit(); |
| 231 | } |
| 232 | |
| 233 | try renderer.render(); |
| 234 | return obj; |
| 235 | } |
| 236 | |
| 237 | pub const Ref = enum(u32) { none = std.math.maxInt(u32), _ }; |
| 238 | |
| 239 | pub const Inst = struct { |
| 240 | tag: Tag, |
| 241 | data: Data, |
| 242 | ty: Interner.Ref, |
| 243 | |
| 244 | pub const Tag = enum { |
| 245 | // data.constant |
| 246 | // not included in blocks |
| 247 | constant, |
| 248 | |
| 249 | // data.arg |
| 250 | // not included in blocks |
| 251 | arg, |
| 252 | symbol, |
| 253 | |
| 254 | // data.label |
| 255 | label, |
| 256 | |
| 257 | // data.block |
| 258 | label_addr, |
| 259 | jmp, |
| 260 | |
| 261 | // data.switch |
| 262 | @"switch", |
| 263 | |
| 264 | // data.branch |
| 265 | branch, |
| 266 | select, |
| 267 | |
| 268 | // data.un |
| 269 | jmp_val, |
| 270 | |
| 271 | // data.call |
| 272 | call, |
| 273 | |
| 274 | // data.alloc |
| 275 | alloc, |
| 276 | |
| 277 | // data.phi |
| 278 | phi, |
| 279 | |
| 280 | // data.bin |
| 281 | store, |
| 282 | bit_or, |
| 283 | bit_xor, |
| 284 | bit_and, |
| 285 | bit_shl, |
| 286 | bit_shr, |
| 287 | cmp_eq, |
| 288 | cmp_ne, |
| 289 | cmp_lt, |
| 290 | cmp_lte, |
| 291 | cmp_gt, |
| 292 | cmp_gte, |
| 293 | add, |
| 294 | sub, |
| 295 | mul, |
| 296 | div, |
| 297 | mod, |
| 298 | |
| 299 | // data.un |
| 300 | ret, |
| 301 | load, |
| 302 | bit_not, |
| 303 | negate, |
| 304 | trunc, |
| 305 | zext, |
| 306 | sext, |
| 307 | }; |
| 308 | |
| 309 | pub const Data = union { |
| 310 | constant: Interner.Ref, |
| 311 | none: void, |
| 312 | bin: struct { |
| 313 | lhs: Ref, |
| 314 | rhs: Ref, |
| 315 | }, |
| 316 | un: Ref, |
| 317 | arg: u32, |
| 318 | alloc: struct { |
| 319 | size: u32, |
| 320 | @"align": u32, |
| 321 | }, |
| 322 | @"switch": *Switch, |
| 323 | call: *Call, |
| 324 | label: [*:0]const u8, |
| 325 | branch: *Branch, |
| 326 | phi: Phi, |
| 327 | }; |
| 328 | |
| 329 | pub const Branch = struct { |
| 330 | cond: Ref, |
| 331 | then: Ref, |
| 332 | @"else": Ref, |
| 333 | }; |
| 334 | |
| 335 | pub const Switch = struct { |
| 336 | target: Ref, |
| 337 | cases_len: u32, |
| 338 | default: Ref, |
| 339 | case_vals: [*]Interner.Ref, |
| 340 | case_labels: [*]Ref, |
| 341 | }; |
| 342 | |
| 343 | pub const Call = struct { |
| 344 | func: Ref, |
| 345 | args_len: u32, |
| 346 | args_ptr: [*]Ref, |
| 347 | |
| 348 | pub fn args(c: Call) []Ref { |
| 349 | return c.args_ptr[0..c.args_len]; |
| 350 | } |
| 351 | }; |
| 352 | |
| 353 | pub const Phi = struct { |
| 354 | ptr: [*]Ir.Ref, |
| 355 | |
| 356 | pub const Input = struct { |
| 357 | label: Ir.Ref, |
| 358 | value: Ir.Ref, |
| 359 | }; |
| 360 | |
| 361 | pub fn inputs(p: Phi) []Input { |
| 362 | const len = @backingInt(p.ptr[0]) * 2; |
| 363 | const slice = (p.ptr + 1)[0..len]; |
| 364 | return std.mem.bytesAsSlice(Input, std.mem.sliceAsBytes(slice)); |
| 365 | } |
| 366 | }; |
| 367 | }; |
| 368 | |
| 369 | pub fn deinit(ir: *Ir, gpa: std.mem.Allocator) void { |
| 370 | for (ir.decls.values()) |*decl| { |
| 371 | decl.deinit(gpa); |
| 372 | } |
| 373 | ir.decls.deinit(gpa); |
| 374 | ir.* = undefined; |
| 375 | } |
| 376 | |
| 377 | const TYPE = std.Io.Terminal.Color.bright_magenta; |
| 378 | const INST = std.Io.Terminal.Color.bright_cyan; |
| 379 | const REF = std.Io.Terminal.Color.bright_blue; |
| 380 | const LITERAL = std.Io.Terminal.Color.bright_green; |
| 381 | const ATTRIBUTE = std.Io.Terminal.Color.bright_yellow; |
| 382 | |
| 383 | const RefMap = std.array_hash_map.Auto(Ref, void); |
| 384 | |
| 385 | pub const DumpError = std.Io.Terminal.SetColorError || std.mem.Allocator.Error; |
| 386 | |
| 387 | pub fn dump(ir: *const Ir, gpa: Allocator, term: std.Io.Terminal) DumpError!void { |
| 388 | for (ir.decls.keys(), ir.decls.values()) |name, *decl| { |
| 389 | try ir.dumpDecl(decl, gpa, name, term); |
| 390 | } |
| 391 | try term.writer.flush(); |
| 392 | } |
| 393 | |
| 394 | fn dumpDecl(ir: *const Ir, decl: *const Decl, gpa: Allocator, name: []const u8, term: std.Io.Terminal) !void { |
| 395 | const tags = decl.instructions.items(.tag); |
| 396 | const data = decl.instructions.items(.data); |
| 397 | const w = term.writer; |
| 398 | |
| 399 | var ref_map: RefMap = .empty; |
| 400 | defer ref_map.deinit(gpa); |
| 401 | |
| 402 | var label_map: RefMap = .empty; |
| 403 | defer label_map.deinit(gpa); |
| 404 | |
| 405 | const ret_inst = decl.body.items[decl.body.items.len - 1]; |
| 406 | const ret_operand = data[@backingInt(ret_inst)].un; |
| 407 | const ret_ty = decl.instructions.items(.ty)[@backingInt(ret_operand)]; |
| 408 | try ir.writeType(ret_ty, term); |
| 409 | try term.setColor(REF); |
| 410 | try w.print(" @{s}", .{name}); |
| 411 | try term.setColor(.reset); |
| 412 | try w.writeAll("("); |
| 413 | |
| 414 | var arg_count: u32 = 0; |
| 415 | while (true) : (arg_count += 1) { |
| 416 | const ref = decl.body.items[arg_count]; |
| 417 | if (tags[@backingInt(ref)] != .arg) break; |
| 418 | if (arg_count != 0) try w.writeAll(", "); |
| 419 | try ref_map.put(gpa, ref, {}); |
| 420 | try ir.writeRef(decl, &ref_map, ref, term); |
| 421 | try term.setColor(.reset); |
| 422 | } |
| 423 | try w.writeAll(") {\n"); |
| 424 | for (decl.body.items[arg_count..]) |ref| { |
| 425 | switch (tags[@backingInt(ref)]) { |
| 426 | .label => try label_map.put(gpa, ref, {}), |
| 427 | else => {}, |
| 428 | } |
| 429 | } |
| 430 | |
| 431 | for (decl.body.items[arg_count..]) |ref| { |
| 432 | const i = @backingInt(ref); |
| 433 | const tag = tags[i]; |
| 434 | switch (tag) { |
| 435 | .arg, .constant, .symbol => unreachable, |
| 436 | .label => { |
| 437 | const label_index = label_map.getIndex(ref).?; |
| 438 | try term.setColor(REF); |
| 439 | try w.print("{s}.{d}:\n", .{ data[i].label, label_index }); |
| 440 | }, |
| 441 | // .label_val => { |
| 442 | // const un = data[i].un; |
| 443 | // try w.print(" %{d} = label.{d}\n", .{ i, @intFromEnum(un) }); |
| 444 | // }, |
| 445 | .jmp => { |
| 446 | const un = data[i].un; |
| 447 | try term.setColor(INST); |
| 448 | try w.writeAll(" jmp "); |
| 449 | try writeLabel(decl, &label_map, un, term); |
| 450 | try w.writeByte('\n'); |
| 451 | }, |
| 452 | .branch => { |
| 453 | const br = data[i].branch; |
| 454 | try term.setColor(INST); |
| 455 | try w.writeAll(" branch "); |
| 456 | try ir.writeRef(decl, &ref_map, br.cond, term); |
| 457 | try term.setColor(.reset); |
| 458 | try w.writeAll(", "); |
| 459 | try writeLabel(decl, &label_map, br.then, term); |
| 460 | try term.setColor(.reset); |
| 461 | try w.writeAll(", "); |
| 462 | try writeLabel(decl, &label_map, br.@"else", term); |
| 463 | try w.writeByte('\n'); |
| 464 | }, |
| 465 | .select => { |
| 466 | const br = data[i].branch; |
| 467 | try ir.writeNewRef(gpa, decl, &ref_map, ref, term); |
| 468 | try w.writeAll("select "); |
| 469 | try ir.writeRef(decl, &ref_map, br.cond, term); |
| 470 | try term.setColor(.reset); |
| 471 | try w.writeAll(", "); |
| 472 | try ir.writeRef(decl, &ref_map, br.then, term); |
| 473 | try term.setColor(.reset); |
| 474 | try w.writeAll(", "); |
| 475 | try ir.writeRef(decl, &ref_map, br.@"else", term); |
| 476 | try w.writeByte('\n'); |
| 477 | }, |
| 478 | // .jmp_val => { |
| 479 | // const bin = data[i].bin; |
| 480 | // try w.print(" %{s} %{d} label.{d}\n", .{ @tagName(tag), @intFromEnum(bin.lhs), @intFromEnum(bin.rhs) }); |
| 481 | // }, |
| 482 | .@"switch" => { |
| 483 | const @"switch" = data[i].@"switch"; |
| 484 | try term.setColor(INST); |
| 485 | try w.writeAll(" switch "); |
| 486 | try ir.writeRef(decl, &ref_map, @"switch".target, term); |
| 487 | try term.setColor(.reset); |
| 488 | try w.writeAll(" {"); |
| 489 | for (@"switch".case_vals[0..@"switch".cases_len], @"switch".case_labels) |val_ref, label_ref| { |
| 490 | try w.writeAll("\n "); |
| 491 | try ir.writeValue(val_ref, term); |
| 492 | try term.setColor(.reset); |
| 493 | try w.writeAll(" => "); |
| 494 | try writeLabel(decl, &label_map, label_ref, term); |
| 495 | try term.setColor(.reset); |
| 496 | } |
| 497 | try term.setColor(LITERAL); |
| 498 | try w.writeAll("\n default "); |
| 499 | try term.setColor(.reset); |
| 500 | try w.writeAll("=> "); |
| 501 | try writeLabel(decl, &label_map, @"switch".default, term); |
| 502 | try term.setColor(.reset); |
| 503 | try w.writeAll("\n }\n"); |
| 504 | }, |
| 505 | .call => { |
| 506 | const call = data[i].call; |
| 507 | try ir.writeNewRef(gpa, decl, &ref_map, ref, term); |
| 508 | try w.writeAll("call "); |
| 509 | try ir.writeRef(decl, &ref_map, call.func, term); |
| 510 | try term.setColor(.reset); |
| 511 | try w.writeAll("("); |
| 512 | for (call.args(), 0..) |arg, arg_i| { |
| 513 | if (arg_i != 0) try w.writeAll(", "); |
| 514 | try ir.writeRef(decl, &ref_map, arg, term); |
| 515 | try term.setColor(.reset); |
| 516 | } |
| 517 | try w.writeAll(")\n"); |
| 518 | }, |
| 519 | .alloc => { |
| 520 | const alloc = data[i].alloc; |
| 521 | try ir.writeNewRef(gpa, decl, &ref_map, ref, term); |
| 522 | try w.writeAll("alloc "); |
| 523 | try term.setColor(ATTRIBUTE); |
| 524 | try w.writeAll("size "); |
| 525 | try term.setColor(LITERAL); |
| 526 | try w.print("{d}", .{alloc.size}); |
| 527 | try term.setColor(ATTRIBUTE); |
| 528 | try w.writeAll(" align "); |
| 529 | try term.setColor(LITERAL); |
| 530 | try w.print("{d}", .{alloc.@"align"}); |
| 531 | try w.writeByte('\n'); |
| 532 | }, |
| 533 | .phi => { |
| 534 | try ir.writeNewRef(gpa, decl, &ref_map, ref, term); |
| 535 | try w.writeAll("phi"); |
| 536 | try term.setColor(.reset); |
| 537 | try w.writeAll(" {"); |
| 538 | for (data[i].phi.inputs()) |input| { |
| 539 | try w.writeAll("\n "); |
| 540 | try writeLabel(decl, &label_map, input.label, term); |
| 541 | try term.setColor(.reset); |
| 542 | try w.writeAll(" => "); |
| 543 | try ir.writeRef(decl, &ref_map, input.value, term); |
| 544 | try term.setColor(.reset); |
| 545 | } |
| 546 | try term.setColor(.reset); |
| 547 | try w.writeAll("\n }\n"); |
| 548 | }, |
| 549 | .store => { |
| 550 | const bin = data[i].bin; |
| 551 | try term.setColor(INST); |
| 552 | try w.writeAll(" store "); |
| 553 | try ir.writeRef(decl, &ref_map, bin.lhs, term); |
| 554 | try term.setColor(.reset); |
| 555 | try w.writeAll(", "); |
| 556 | try ir.writeRef(decl, &ref_map, bin.rhs, term); |
| 557 | try w.writeByte('\n'); |
| 558 | }, |
| 559 | .ret => { |
| 560 | try term.setColor(INST); |
| 561 | try w.writeAll(" ret "); |
| 562 | if (data[i].un != .none) try ir.writeRef(decl, &ref_map, data[i].un, term); |
| 563 | try w.writeByte('\n'); |
| 564 | }, |
| 565 | .load => { |
| 566 | try ir.writeNewRef(gpa, decl, &ref_map, ref, term); |
| 567 | try w.writeAll("load "); |
| 568 | try ir.writeRef(decl, &ref_map, data[i].un, term); |
| 569 | try w.writeByte('\n'); |
| 570 | }, |
| 571 | .bit_or, |
| 572 | .bit_xor, |
| 573 | .bit_and, |
| 574 | .bit_shl, |
| 575 | .bit_shr, |
| 576 | .cmp_eq, |
| 577 | .cmp_ne, |
| 578 | .cmp_lt, |
| 579 | .cmp_lte, |
| 580 | .cmp_gt, |
| 581 | .cmp_gte, |
| 582 | .add, |
| 583 | .sub, |
| 584 | .mul, |
| 585 | .div, |
| 586 | .mod, |
| 587 | => { |
| 588 | const bin = data[i].bin; |
| 589 | try ir.writeNewRef(gpa, decl, &ref_map, ref, term); |
| 590 | try w.print("{s} ", .{@tagName(tag)}); |
| 591 | try ir.writeRef(decl, &ref_map, bin.lhs, term); |
| 592 | try term.setColor(.reset); |
| 593 | try w.writeAll(", "); |
| 594 | try ir.writeRef(decl, &ref_map, bin.rhs, term); |
| 595 | try w.writeByte('\n'); |
| 596 | }, |
| 597 | .bit_not, |
| 598 | .negate, |
| 599 | .trunc, |
| 600 | .zext, |
| 601 | .sext, |
| 602 | => { |
| 603 | const un = data[i].un; |
| 604 | try ir.writeNewRef(gpa, decl, &ref_map, ref, term); |
| 605 | try w.print("{s} ", .{@tagName(tag)}); |
| 606 | try ir.writeRef(decl, &ref_map, un, term); |
| 607 | try w.writeByte('\n'); |
| 608 | }, |
| 609 | .label_addr, .jmp_val => {}, |
| 610 | } |
| 611 | } |
| 612 | try term.setColor(.reset); |
| 613 | try w.writeAll("}\n\n"); |
| 614 | } |
| 615 | |
| 616 | fn writeType(ir: Ir, ty_ref: Interner.Ref, term: std.Io.Terminal) !void { |
| 617 | const w = term.writer; |
| 618 | const ty = ir.interner.get(ty_ref); |
| 619 | try term.setColor(TYPE); |
| 620 | switch (ty) { |
| 621 | .ptr_ty, .noreturn_ty, .void_ty, .func_ty => try w.writeAll(@tagName(ty)), |
| 622 | .int_ty => |bits| try w.print("i{d}", .{bits}), |
| 623 | .float_ty => |bits| try w.print("f{d}", .{bits}), |
| 624 | .array_ty => |info| { |
| 625 | try w.print("[{d} * ", .{info.len}); |
| 626 | try ir.writeType(info.child, .{ .mode = .no_color, .writer = w }); |
| 627 | try w.writeByte(']'); |
| 628 | }, |
| 629 | .vector_ty => |info| { |
| 630 | try w.print("<{d} * ", .{info.len}); |
| 631 | try ir.writeType(info.child, .{ .mode = .no_color, .writer = w }); |
| 632 | try w.writeByte('>'); |
| 633 | }, |
| 634 | .record_ty => |elems| { |
| 635 | // TODO collect into buffer and only print once |
| 636 | try w.writeAll("{ "); |
| 637 | for (elems, 0..) |elem, i| { |
| 638 | if (i != 0) try w.writeAll(", "); |
| 639 | try ir.writeType(elem, .{ .mode = .no_color, .writer = w }); |
| 640 | } |
| 641 | try w.writeAll(" }"); |
| 642 | }, |
| 643 | else => unreachable, // not a type |
| 644 | } |
| 645 | } |
| 646 | |
| 647 | fn writeValue(ir: Ir, val: Interner.Ref, term: std.Io.Terminal) !void { |
| 648 | const w = term.writer; |
| 649 | try term.setColor(LITERAL); |
| 650 | const key = ir.interner.get(val); |
| 651 | switch (key) { |
| 652 | .null => return w.writeAll("nullptr_t"), |
| 653 | .int => |repr| switch (repr) { |
| 654 | inline else => |x| return w.print("{d}", .{x}), |
| 655 | }, |
| 656 | .float => |repr| switch (repr) { |
| 657 | inline else => |x| return w.print("{d}", .{@as(f64, @floatCast(x))}), |
| 658 | }, |
| 659 | .bytes => |b| return std.zig.stringEscape(b, w), |
| 660 | else => unreachable, // not a value |
| 661 | } |
| 662 | } |
| 663 | |
| 664 | fn writeRef(ir: Ir, decl: *const Decl, ref_map: *RefMap, ref: Ref, term: std.Io.Terminal) !void { |
| 665 | const w = term.writer; |
| 666 | assert(ref != .none); |
| 667 | const index = @backingInt(ref); |
| 668 | const ty_ref = decl.instructions.items(.ty)[index]; |
| 669 | if (decl.instructions.items(.tag)[index] == .constant) { |
| 670 | try ir.writeType(ty_ref, term); |
| 671 | const v_ref = decl.instructions.items(.data)[index].constant; |
| 672 | try w.writeByte(' '); |
| 673 | try ir.writeValue(v_ref, term); |
| 674 | return; |
| 675 | } else if (decl.instructions.items(.tag)[index] == .symbol) { |
| 676 | const name = decl.instructions.items(.data)[index].label; |
| 677 | try ir.writeType(ty_ref, term); |
| 678 | try term.setColor(REF); |
| 679 | try w.print(" @{s}", .{name}); |
| 680 | return; |
| 681 | } |
| 682 | try ir.writeType(ty_ref, term); |
| 683 | try term.setColor(REF); |
| 684 | const ref_index = ref_map.getIndex(ref).?; |
| 685 | try w.print(" %{d}", .{ref_index}); |
| 686 | } |
| 687 | |
| 688 | fn writeNewRef(ir: Ir, gpa: Allocator, decl: *const Decl, ref_map: *RefMap, ref: Ref, term: std.Io.Terminal) !void { |
| 689 | const w = term.writer; |
| 690 | try ref_map.put(gpa, ref, {}); |
| 691 | try w.writeAll(" "); |
| 692 | try ir.writeRef(decl, ref_map, ref, term); |
| 693 | try term.setColor(.reset); |
| 694 | try w.writeAll(" = "); |
| 695 | try term.setColor(INST); |
| 696 | } |
| 697 | |
| 698 | fn writeLabel(decl: *const Decl, label_map: *RefMap, ref: Ref, term: std.Io.Terminal) !void { |
| 699 | const w = term.writer; |
| 700 | assert(ref != .none); |
| 701 | const index = @backingInt(ref); |
| 702 | const label = decl.instructions.items(.data)[index].label; |
| 703 | try term.setColor(REF); |
| 704 | const label_index = label_map.getIndex(ref).?; |
| 705 | try w.print("{s}.{d}", .{ label, label_index }); |
| 706 | } |