mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
699 lines
22 KiB
Zig
Vendored
699 lines
22 KiB
Zig
Vendored
const std = @import("std");
|
|
const Allocator = std.mem.Allocator;
|
|
const assert = std.debug.assert;
|
|
const Interner = @import("Interner.zig");
|
|
const Object = @import("Object.zig");
|
|
|
|
const Ir = @This();
|
|
|
|
interner: *Interner,
|
|
decls: std.StringArrayHashMapUnmanaged(Decl),
|
|
|
|
pub const Decl = struct {
|
|
instructions: std.MultiArrayList(Inst),
|
|
body: std.ArrayListUnmanaged(Ref),
|
|
arena: std.heap.ArenaAllocator.State,
|
|
|
|
pub fn deinit(decl: *Decl, gpa: Allocator) void {
|
|
decl.instructions.deinit(gpa);
|
|
decl.body.deinit(gpa);
|
|
decl.arena.promote(gpa).deinit();
|
|
}
|
|
};
|
|
|
|
pub const Builder = struct {
|
|
gpa: Allocator,
|
|
arena: std.heap.ArenaAllocator,
|
|
interner: *Interner,
|
|
|
|
decls: std.StringArrayHashMapUnmanaged(Decl) = .{},
|
|
instructions: std.MultiArrayList(Ir.Inst) = .{},
|
|
body: std.ArrayListUnmanaged(Ref) = .{},
|
|
alloc_count: u32 = 0,
|
|
arg_count: u32 = 0,
|
|
current_label: Ref = undefined,
|
|
|
|
pub fn deinit(b: *Builder) void {
|
|
for (b.decls.values()) |*decl| {
|
|
decl.deinit(b.gpa);
|
|
}
|
|
b.decls.deinit(b.gpa);
|
|
b.arena.deinit();
|
|
b.instructions.deinit(b.gpa);
|
|
b.body.deinit(b.gpa);
|
|
b.* = undefined;
|
|
}
|
|
|
|
pub fn finish(b: *Builder) Ir {
|
|
return .{
|
|
.interner = b.interner,
|
|
.decls = b.decls.move(),
|
|
};
|
|
}
|
|
|
|
pub fn startFn(b: *Builder) Allocator.Error!void {
|
|
const entry = try b.makeLabel("entry");
|
|
try b.body.append(b.gpa, entry);
|
|
b.current_label = entry;
|
|
}
|
|
|
|
pub fn finishFn(b: *Builder, name: []const u8) !void {
|
|
var duped_instructions = try b.instructions.clone(b.gpa);
|
|
errdefer duped_instructions.deinit(b.gpa);
|
|
var duped_body = try b.body.clone(b.gpa);
|
|
errdefer duped_body.deinit(b.gpa);
|
|
|
|
try b.decls.put(b.gpa, name, .{
|
|
.instructions = duped_instructions,
|
|
.body = duped_body,
|
|
.arena = b.arena.state,
|
|
});
|
|
b.instructions.shrinkRetainingCapacity(0);
|
|
b.body.shrinkRetainingCapacity(0);
|
|
b.arena = std.heap.ArenaAllocator.init(b.gpa);
|
|
b.alloc_count = 0;
|
|
b.arg_count = 0;
|
|
}
|
|
|
|
pub fn startBlock(b: *Builder, label: Ref) !void {
|
|
try b.body.append(b.gpa, label);
|
|
b.current_label = label;
|
|
}
|
|
|
|
pub fn addArg(b: *Builder, ty: Interner.Ref) Allocator.Error!Ref {
|
|
const ref: Ref = @enumFromInt(b.instructions.len);
|
|
try b.instructions.append(b.gpa, .{ .tag = .arg, .data = .{ .none = {} }, .ty = ty });
|
|
try b.body.insert(b.gpa, b.arg_count, ref);
|
|
b.arg_count += 1;
|
|
return ref;
|
|
}
|
|
|
|
pub fn addAlloc(b: *Builder, size: u32, @"align": u32) Allocator.Error!Ref {
|
|
const ref: Ref = @enumFromInt(b.instructions.len);
|
|
try b.instructions.append(b.gpa, .{
|
|
.tag = .alloc,
|
|
.data = .{ .alloc = .{ .size = size, .@"align" = @"align" } },
|
|
.ty = .ptr,
|
|
});
|
|
try b.body.insert(b.gpa, b.alloc_count + b.arg_count + 1, ref);
|
|
b.alloc_count += 1;
|
|
return ref;
|
|
}
|
|
|
|
pub fn addInst(b: *Builder, tag: Ir.Inst.Tag, data: Ir.Inst.Data, ty: Interner.Ref) Allocator.Error!Ref {
|
|
const ref: Ref = @enumFromInt(b.instructions.len);
|
|
try b.instructions.append(b.gpa, .{ .tag = tag, .data = data, .ty = ty });
|
|
try b.body.append(b.gpa, ref);
|
|
return ref;
|
|
}
|
|
|
|
pub fn makeLabel(b: *Builder, name: [*:0]const u8) Allocator.Error!Ref {
|
|
const ref: Ref = @enumFromInt(b.instructions.len);
|
|
try b.instructions.append(b.gpa, .{ .tag = .label, .data = .{ .label = name }, .ty = .void });
|
|
return ref;
|
|
}
|
|
|
|
pub fn addJump(b: *Builder, label: Ref) Allocator.Error!void {
|
|
_ = try b.addInst(.jmp, .{ .un = label }, .noreturn);
|
|
}
|
|
|
|
pub fn addBranch(b: *Builder, cond: Ref, true_label: Ref, false_label: Ref) Allocator.Error!void {
|
|
const branch = try b.arena.allocator().create(Ir.Inst.Branch);
|
|
branch.* = .{
|
|
.cond = cond,
|
|
.then = true_label,
|
|
.@"else" = false_label,
|
|
};
|
|
_ = try b.addInst(.branch, .{ .branch = branch }, .noreturn);
|
|
}
|
|
|
|
pub fn addSwitch(b: *Builder, target: Ref, values: []Interner.Ref, labels: []Ref, default: Ref) Allocator.Error!void {
|
|
assert(values.len == labels.len);
|
|
const a = b.arena.allocator();
|
|
const @"switch" = try a.create(Ir.Inst.Switch);
|
|
@"switch".* = .{
|
|
.target = target,
|
|
.cases_len = @intCast(values.len),
|
|
.case_vals = (try a.dupe(Interner.Ref, values)).ptr,
|
|
.case_labels = (try a.dupe(Ref, labels)).ptr,
|
|
.default = default,
|
|
};
|
|
_ = try b.addInst(.@"switch", .{ .@"switch" = @"switch" }, .noreturn);
|
|
}
|
|
|
|
pub fn addStore(b: *Builder, ptr: Ref, val: Ref) Allocator.Error!void {
|
|
_ = try b.addInst(.store, .{ .bin = .{ .lhs = ptr, .rhs = val } }, .void);
|
|
}
|
|
|
|
pub fn addConstant(b: *Builder, val: Interner.Ref, ty: Interner.Ref) Allocator.Error!Ref {
|
|
const ref: Ref = @enumFromInt(b.instructions.len);
|
|
try b.instructions.append(b.gpa, .{
|
|
.tag = .constant,
|
|
.data = .{ .constant = val },
|
|
.ty = ty,
|
|
});
|
|
return ref;
|
|
}
|
|
|
|
pub fn addPhi(b: *Builder, inputs: []const Inst.Phi.Input, ty: Interner.Ref) Allocator.Error!Ref {
|
|
const a = b.arena.allocator();
|
|
const input_refs = try a.alloc(Ref, inputs.len * 2 + 1);
|
|
input_refs[0] = @enumFromInt(inputs.len);
|
|
@memcpy(input_refs[1..], std.mem.bytesAsSlice(Ref, std.mem.sliceAsBytes(inputs)));
|
|
|
|
return b.addInst(.phi, .{ .phi = .{ .ptr = input_refs.ptr } }, ty);
|
|
}
|
|
|
|
pub fn addSelect(b: *Builder, cond: Ref, then: Ref, @"else": Ref, ty: Interner.Ref) Allocator.Error!Ref {
|
|
const branch = try b.arena.allocator().create(Ir.Inst.Branch);
|
|
branch.* = .{
|
|
.cond = cond,
|
|
.then = then,
|
|
.@"else" = @"else",
|
|
};
|
|
return b.addInst(.select, .{ .branch = branch }, ty);
|
|
}
|
|
};
|
|
|
|
pub const Renderer = struct {
|
|
gpa: Allocator,
|
|
obj: *Object,
|
|
ir: *const Ir,
|
|
errors: ErrorList = .{},
|
|
|
|
pub const ErrorList = std.StringArrayHashMapUnmanaged([]const u8);
|
|
|
|
pub const Error = Allocator.Error || error{LowerFail};
|
|
|
|
pub fn deinit(r: *Renderer) void {
|
|
for (r.errors.values()) |msg| r.gpa.free(msg);
|
|
r.errors.deinit(r.gpa);
|
|
}
|
|
|
|
pub fn render(r: *Renderer) !void {
|
|
switch (r.obj.target.cpu.arch) {
|
|
.x86, .x86_64 => return @import("Ir/x86/Renderer.zig").render(r),
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
pub fn fail(
|
|
r: *Renderer,
|
|
name: []const u8,
|
|
comptime format: []const u8,
|
|
args: anytype,
|
|
) Error {
|
|
try r.errors.ensureUnusedCapacity(r.gpa, 1);
|
|
r.errors.putAssumeCapacity(name, try std.fmt.allocPrint(r.gpa, format, args));
|
|
return error.LowerFail;
|
|
}
|
|
};
|
|
|
|
pub fn render(
|
|
ir: *const Ir,
|
|
gpa: Allocator,
|
|
target: std.Target,
|
|
errors: ?*Renderer.ErrorList,
|
|
) !*Object {
|
|
const obj = try Object.create(gpa, target);
|
|
errdefer obj.deinit();
|
|
|
|
var renderer: Renderer = .{
|
|
.gpa = gpa,
|
|
.obj = obj,
|
|
.ir = ir,
|
|
};
|
|
defer {
|
|
if (errors) |some| {
|
|
some.* = renderer.errors.move();
|
|
}
|
|
renderer.deinit();
|
|
}
|
|
|
|
try renderer.render();
|
|
return obj;
|
|
}
|
|
|
|
pub const Ref = enum(u32) { none = std.math.maxInt(u32), _ };
|
|
|
|
pub const Inst = struct {
|
|
tag: Tag,
|
|
data: Data,
|
|
ty: Interner.Ref,
|
|
|
|
pub const Tag = enum {
|
|
// data.constant
|
|
// not included in blocks
|
|
constant,
|
|
|
|
// data.arg
|
|
// not included in blocks
|
|
arg,
|
|
symbol,
|
|
|
|
// data.label
|
|
label,
|
|
|
|
// data.block
|
|
label_addr,
|
|
jmp,
|
|
|
|
// data.switch
|
|
@"switch",
|
|
|
|
// data.branch
|
|
branch,
|
|
select,
|
|
|
|
// data.un
|
|
jmp_val,
|
|
|
|
// data.call
|
|
call,
|
|
|
|
// data.alloc
|
|
alloc,
|
|
|
|
// data.phi
|
|
phi,
|
|
|
|
// data.bin
|
|
store,
|
|
bit_or,
|
|
bit_xor,
|
|
bit_and,
|
|
bit_shl,
|
|
bit_shr,
|
|
cmp_eq,
|
|
cmp_ne,
|
|
cmp_lt,
|
|
cmp_lte,
|
|
cmp_gt,
|
|
cmp_gte,
|
|
add,
|
|
sub,
|
|
mul,
|
|
div,
|
|
mod,
|
|
|
|
// data.un
|
|
ret,
|
|
load,
|
|
bit_not,
|
|
negate,
|
|
trunc,
|
|
zext,
|
|
sext,
|
|
};
|
|
|
|
pub const Data = union {
|
|
constant: Interner.Ref,
|
|
none: void,
|
|
bin: struct {
|
|
lhs: Ref,
|
|
rhs: Ref,
|
|
},
|
|
un: Ref,
|
|
arg: u32,
|
|
alloc: struct {
|
|
size: u32,
|
|
@"align": u32,
|
|
},
|
|
@"switch": *Switch,
|
|
call: *Call,
|
|
label: [*:0]const u8,
|
|
branch: *Branch,
|
|
phi: Phi,
|
|
};
|
|
|
|
pub const Branch = struct {
|
|
cond: Ref,
|
|
then: Ref,
|
|
@"else": Ref,
|
|
};
|
|
|
|
pub const Switch = struct {
|
|
target: Ref,
|
|
cases_len: u32,
|
|
default: Ref,
|
|
case_vals: [*]Interner.Ref,
|
|
case_labels: [*]Ref,
|
|
};
|
|
|
|
pub const Call = struct {
|
|
func: Ref,
|
|
args_len: u32,
|
|
args_ptr: [*]Ref,
|
|
|
|
pub fn args(c: Call) []Ref {
|
|
return c.args_ptr[0..c.args_len];
|
|
}
|
|
};
|
|
|
|
pub const Phi = struct {
|
|
ptr: [*]Ir.Ref,
|
|
|
|
pub const Input = struct {
|
|
label: Ir.Ref,
|
|
value: Ir.Ref,
|
|
};
|
|
|
|
pub fn inputs(p: Phi) []Input {
|
|
const len = @intFromEnum(p.ptr[0]) * 2;
|
|
const slice = (p.ptr + 1)[0..len];
|
|
return std.mem.bytesAsSlice(Input, std.mem.sliceAsBytes(slice));
|
|
}
|
|
};
|
|
};
|
|
|
|
pub fn deinit(ir: *Ir, gpa: std.mem.Allocator) void {
|
|
for (ir.decls.values()) |*decl| {
|
|
decl.deinit(gpa);
|
|
}
|
|
ir.decls.deinit(gpa);
|
|
ir.* = undefined;
|
|
}
|
|
|
|
const TYPE = std.Io.tty.Color.bright_magenta;
|
|
const INST = std.Io.tty.Color.bright_cyan;
|
|
const REF = std.Io.tty.Color.bright_blue;
|
|
const LITERAL = std.Io.tty.Color.bright_green;
|
|
const ATTRIBUTE = std.Io.tty.Color.bright_yellow;
|
|
|
|
const RefMap = std.AutoArrayHashMap(Ref, void);
|
|
|
|
pub fn dump(ir: *const Ir, gpa: Allocator, config: std.Io.tty.Config, w: *std.Io.Writer) !void {
|
|
for (ir.decls.keys(), ir.decls.values()) |name, *decl| {
|
|
try ir.dumpDecl(decl, gpa, name, config, w);
|
|
}
|
|
try w.flush();
|
|
}
|
|
|
|
fn dumpDecl(ir: *const Ir, decl: *const Decl, gpa: Allocator, name: []const u8, config: std.Io.tty.Config, w: *std.Io.Writer) !void {
|
|
const tags = decl.instructions.items(.tag);
|
|
const data = decl.instructions.items(.data);
|
|
|
|
var ref_map = RefMap.init(gpa);
|
|
defer ref_map.deinit();
|
|
|
|
var label_map = RefMap.init(gpa);
|
|
defer label_map.deinit();
|
|
|
|
const ret_inst = decl.body.items[decl.body.items.len - 1];
|
|
const ret_operand = data[@intFromEnum(ret_inst)].un;
|
|
const ret_ty = decl.instructions.items(.ty)[@intFromEnum(ret_operand)];
|
|
try ir.writeType(ret_ty, config, w);
|
|
try config.setColor(w, REF);
|
|
try w.print(" @{s}", .{name});
|
|
try config.setColor(w, .reset);
|
|
try w.writeAll("(");
|
|
|
|
var arg_count: u32 = 0;
|
|
while (true) : (arg_count += 1) {
|
|
const ref = decl.body.items[arg_count];
|
|
if (tags[@intFromEnum(ref)] != .arg) break;
|
|
if (arg_count != 0) try w.writeAll(", ");
|
|
try ref_map.put(ref, {});
|
|
try ir.writeRef(decl, &ref_map, ref, config, w);
|
|
try config.setColor(w, .reset);
|
|
}
|
|
try w.writeAll(") {\n");
|
|
for (decl.body.items[arg_count..]) |ref| {
|
|
switch (tags[@intFromEnum(ref)]) {
|
|
.label => try label_map.put(ref, {}),
|
|
else => {},
|
|
}
|
|
}
|
|
|
|
for (decl.body.items[arg_count..]) |ref| {
|
|
const i = @intFromEnum(ref);
|
|
const tag = tags[i];
|
|
switch (tag) {
|
|
.arg, .constant, .symbol => unreachable,
|
|
.label => {
|
|
const label_index = label_map.getIndex(ref).?;
|
|
try config.setColor(w, REF);
|
|
try w.print("{s}.{d}:\n", .{ data[i].label, label_index });
|
|
},
|
|
// .label_val => {
|
|
// const un = data[i].un;
|
|
// try w.print(" %{d} = label.{d}\n", .{ i, @intFromEnum(un) });
|
|
// },
|
|
.jmp => {
|
|
const un = data[i].un;
|
|
try config.setColor(w, INST);
|
|
try w.writeAll(" jmp ");
|
|
try writeLabel(decl, &label_map, un, config, w);
|
|
try w.writeByte('\n');
|
|
},
|
|
.branch => {
|
|
const br = data[i].branch;
|
|
try config.setColor(w, INST);
|
|
try w.writeAll(" branch ");
|
|
try ir.writeRef(decl, &ref_map, br.cond, config, w);
|
|
try config.setColor(w, .reset);
|
|
try w.writeAll(", ");
|
|
try writeLabel(decl, &label_map, br.then, config, w);
|
|
try config.setColor(w, .reset);
|
|
try w.writeAll(", ");
|
|
try writeLabel(decl, &label_map, br.@"else", config, w);
|
|
try w.writeByte('\n');
|
|
},
|
|
.select => {
|
|
const br = data[i].branch;
|
|
try ir.writeNewRef(decl, &ref_map, ref, config, w);
|
|
try w.writeAll("select ");
|
|
try ir.writeRef(decl, &ref_map, br.cond, config, w);
|
|
try config.setColor(w, .reset);
|
|
try w.writeAll(", ");
|
|
try ir.writeRef(decl, &ref_map, br.then, config, w);
|
|
try config.setColor(w, .reset);
|
|
try w.writeAll(", ");
|
|
try ir.writeRef(decl, &ref_map, br.@"else", config, w);
|
|
try w.writeByte('\n');
|
|
},
|
|
// .jmp_val => {
|
|
// const bin = data[i].bin;
|
|
// try w.print(" %{s} %{d} label.{d}\n", .{ @tagName(tag), @intFromEnum(bin.lhs), @intFromEnum(bin.rhs) });
|
|
// },
|
|
.@"switch" => {
|
|
const @"switch" = data[i].@"switch";
|
|
try config.setColor(w, INST);
|
|
try w.writeAll(" switch ");
|
|
try ir.writeRef(decl, &ref_map, @"switch".target, config, w);
|
|
try config.setColor(w, .reset);
|
|
try w.writeAll(" {");
|
|
for (@"switch".case_vals[0..@"switch".cases_len], @"switch".case_labels) |val_ref, label_ref| {
|
|
try w.writeAll("\n ");
|
|
try ir.writeValue(val_ref, config, w);
|
|
try config.setColor(w, .reset);
|
|
try w.writeAll(" => ");
|
|
try writeLabel(decl, &label_map, label_ref, config, w);
|
|
try config.setColor(w, .reset);
|
|
}
|
|
try config.setColor(w, LITERAL);
|
|
try w.writeAll("\n default ");
|
|
try config.setColor(w, .reset);
|
|
try w.writeAll("=> ");
|
|
try writeLabel(decl, &label_map, @"switch".default, config, w);
|
|
try config.setColor(w, .reset);
|
|
try w.writeAll("\n }\n");
|
|
},
|
|
.call => {
|
|
const call = data[i].call;
|
|
try ir.writeNewRef(decl, &ref_map, ref, config, w);
|
|
try w.writeAll("call ");
|
|
try ir.writeRef(decl, &ref_map, call.func, config, w);
|
|
try config.setColor(w, .reset);
|
|
try w.writeAll("(");
|
|
for (call.args(), 0..) |arg, arg_i| {
|
|
if (arg_i != 0) try w.writeAll(", ");
|
|
try ir.writeRef(decl, &ref_map, arg, config, w);
|
|
try config.setColor(w, .reset);
|
|
}
|
|
try w.writeAll(")\n");
|
|
},
|
|
.alloc => {
|
|
const alloc = data[i].alloc;
|
|
try ir.writeNewRef(decl, &ref_map, ref, config, w);
|
|
try w.writeAll("alloc ");
|
|
try config.setColor(w, ATTRIBUTE);
|
|
try w.writeAll("size ");
|
|
try config.setColor(w, LITERAL);
|
|
try w.print("{d}", .{alloc.size});
|
|
try config.setColor(w, ATTRIBUTE);
|
|
try w.writeAll(" align ");
|
|
try config.setColor(w, LITERAL);
|
|
try w.print("{d}", .{alloc.@"align"});
|
|
try w.writeByte('\n');
|
|
},
|
|
.phi => {
|
|
try ir.writeNewRef(decl, &ref_map, ref, config, w);
|
|
try w.writeAll("phi");
|
|
try config.setColor(w, .reset);
|
|
try w.writeAll(" {");
|
|
for (data[i].phi.inputs()) |input| {
|
|
try w.writeAll("\n ");
|
|
try writeLabel(decl, &label_map, input.label, config, w);
|
|
try config.setColor(w, .reset);
|
|
try w.writeAll(" => ");
|
|
try ir.writeRef(decl, &ref_map, input.value, config, w);
|
|
try config.setColor(w, .reset);
|
|
}
|
|
try config.setColor(w, .reset);
|
|
try w.writeAll("\n }\n");
|
|
},
|
|
.store => {
|
|
const bin = data[i].bin;
|
|
try config.setColor(w, INST);
|
|
try w.writeAll(" store ");
|
|
try ir.writeRef(decl, &ref_map, bin.lhs, config, w);
|
|
try config.setColor(w, .reset);
|
|
try w.writeAll(", ");
|
|
try ir.writeRef(decl, &ref_map, bin.rhs, config, w);
|
|
try w.writeByte('\n');
|
|
},
|
|
.ret => {
|
|
try config.setColor(w, INST);
|
|
try w.writeAll(" ret ");
|
|
if (data[i].un != .none) try ir.writeRef(decl, &ref_map, data[i].un, config, w);
|
|
try w.writeByte('\n');
|
|
},
|
|
.load => {
|
|
try ir.writeNewRef(decl, &ref_map, ref, config, w);
|
|
try w.writeAll("load ");
|
|
try ir.writeRef(decl, &ref_map, data[i].un, config, w);
|
|
try w.writeByte('\n');
|
|
},
|
|
.bit_or,
|
|
.bit_xor,
|
|
.bit_and,
|
|
.bit_shl,
|
|
.bit_shr,
|
|
.cmp_eq,
|
|
.cmp_ne,
|
|
.cmp_lt,
|
|
.cmp_lte,
|
|
.cmp_gt,
|
|
.cmp_gte,
|
|
.add,
|
|
.sub,
|
|
.mul,
|
|
.div,
|
|
.mod,
|
|
=> {
|
|
const bin = data[i].bin;
|
|
try ir.writeNewRef(decl, &ref_map, ref, config, w);
|
|
try w.print("{s} ", .{@tagName(tag)});
|
|
try ir.writeRef(decl, &ref_map, bin.lhs, config, w);
|
|
try config.setColor(w, .reset);
|
|
try w.writeAll(", ");
|
|
try ir.writeRef(decl, &ref_map, bin.rhs, config, w);
|
|
try w.writeByte('\n');
|
|
},
|
|
.bit_not,
|
|
.negate,
|
|
.trunc,
|
|
.zext,
|
|
.sext,
|
|
=> {
|
|
const un = data[i].un;
|
|
try ir.writeNewRef(decl, &ref_map, ref, config, w);
|
|
try w.print("{s} ", .{@tagName(tag)});
|
|
try ir.writeRef(decl, &ref_map, un, config, w);
|
|
try w.writeByte('\n');
|
|
},
|
|
.label_addr, .jmp_val => {},
|
|
}
|
|
}
|
|
try config.setColor(w, .reset);
|
|
try w.writeAll("}\n\n");
|
|
}
|
|
|
|
fn writeType(ir: Ir, ty_ref: Interner.Ref, config: std.Io.tty.Config, w: *std.Io.Writer) !void {
|
|
const ty = ir.interner.get(ty_ref);
|
|
try config.setColor(w, TYPE);
|
|
switch (ty) {
|
|
.ptr_ty, .noreturn_ty, .void_ty, .func_ty => try w.writeAll(@tagName(ty)),
|
|
.int_ty => |bits| try w.print("i{d}", .{bits}),
|
|
.float_ty => |bits| try w.print("f{d}", .{bits}),
|
|
.array_ty => |info| {
|
|
try w.print("[{d} * ", .{info.len});
|
|
try ir.writeType(info.child, .no_color, w);
|
|
try w.writeByte(']');
|
|
},
|
|
.vector_ty => |info| {
|
|
try w.print("<{d} * ", .{info.len});
|
|
try ir.writeType(info.child, .no_color, w);
|
|
try w.writeByte('>');
|
|
},
|
|
.record_ty => |elems| {
|
|
// TODO collect into buffer and only print once
|
|
try w.writeAll("{ ");
|
|
for (elems, 0..) |elem, i| {
|
|
if (i != 0) try w.writeAll(", ");
|
|
try ir.writeType(elem, config, w);
|
|
}
|
|
try w.writeAll(" }");
|
|
},
|
|
else => unreachable, // not a type
|
|
}
|
|
}
|
|
|
|
fn writeValue(ir: Ir, val: Interner.Ref, config: std.Io.tty.Config, w: *std.Io.Writer) !void {
|
|
try config.setColor(w, LITERAL);
|
|
const key = ir.interner.get(val);
|
|
switch (key) {
|
|
.null => return w.writeAll("nullptr_t"),
|
|
.int => |repr| switch (repr) {
|
|
inline else => |x| return w.print("{d}", .{x}),
|
|
},
|
|
.float => |repr| switch (repr) {
|
|
inline else => |x| return w.print("{d}", .{@as(f64, @floatCast(x))}),
|
|
},
|
|
.bytes => |b| return std.zig.stringEscape(b, w),
|
|
else => unreachable, // not a value
|
|
}
|
|
}
|
|
|
|
fn writeRef(ir: Ir, decl: *const Decl, ref_map: *RefMap, ref: Ref, config: std.Io.tty.Config, w: *std.Io.Writer) !void {
|
|
assert(ref != .none);
|
|
const index = @intFromEnum(ref);
|
|
const ty_ref = decl.instructions.items(.ty)[index];
|
|
if (decl.instructions.items(.tag)[index] == .constant) {
|
|
try ir.writeType(ty_ref, config, w);
|
|
const v_ref = decl.instructions.items(.data)[index].constant;
|
|
try w.writeByte(' ');
|
|
try ir.writeValue(v_ref, config, w);
|
|
return;
|
|
} else if (decl.instructions.items(.tag)[index] == .symbol) {
|
|
const name = decl.instructions.items(.data)[index].label;
|
|
try ir.writeType(ty_ref, config, w);
|
|
try config.setColor(w, REF);
|
|
try w.print(" @{s}", .{name});
|
|
return;
|
|
}
|
|
try ir.writeType(ty_ref, config, w);
|
|
try config.setColor(w, REF);
|
|
const ref_index = ref_map.getIndex(ref).?;
|
|
try w.print(" %{d}", .{ref_index});
|
|
}
|
|
|
|
fn writeNewRef(ir: Ir, decl: *const Decl, ref_map: *RefMap, ref: Ref, config: std.Io.tty.Config, w: *std.Io.Writer) !void {
|
|
try ref_map.put(ref, {});
|
|
try w.writeAll(" ");
|
|
try ir.writeRef(decl, ref_map, ref, config, w);
|
|
try config.setColor(w, .reset);
|
|
try w.writeAll(" = ");
|
|
try config.setColor(w, INST);
|
|
}
|
|
|
|
fn writeLabel(decl: *const Decl, label_map: *RefMap, ref: Ref, config: std.Io.tty.Config, w: *std.Io.Writer) !void {
|
|
assert(ref != .none);
|
|
const index = @intFromEnum(ref);
|
|
const label = decl.instructions.items(.data)[index].label;
|
|
try config.setColor(w, REF);
|
|
const label_index = label_map.getIndex(ref).?;
|
|
try w.print("{s}.{d}", .{ label, label_index });
|
|
}
|