mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
Part of #19063. Primarily, this moves Aro from deps/ to lib/compiler/ so that it can be lazily compiled from source. src/aro_translate_c.zig is moved to lib/compiler/aro_translate_c.zig and some of Zig CLI logic moved to a main() function there. aro_translate_c.zig becomes the "common" import for clang-based translate-c. Not all of the compiler was able to be detangled from Aro, however, so it still, for now, remains being compiled with the main compiler sources due to the clang-based translate-c depending on it. Once aro-based translate-c achieves feature parity with the clang-based translate-c implementation, the clang-based one can be removed from Zig. Aro made it unnecessarily difficult to depend on with these .def files and all these Zig module requirements. I looked at the .def files and made these observations: - The canonical source is llvm .def files. - Therefore there is an update process to sync with llvm that involves regenerating the .def files in Aro. - Therefore you might as well just regenerate the .zig files directly and check those into Aro. - Also with a small amount of tinkering, the file size on disk of these generated .zig files can be made many times smaller, without compromising type safety in the usage of the data. This would make things much easier on Zig as downstream project, particularly we could remove those pesky stubs when bootstrapping. I have gone ahead with these changes since they unblock me and I will have a chat with Vexu to see what he thinks.
697 lines
22 KiB
Zig
697 lines
22 KiB
Zig
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.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: anytype) !void {
|
|
for (ir.decls.keys(), ir.decls.values()) |name, *decl| {
|
|
try ir.dumpDecl(decl, gpa, name, config, w);
|
|
}
|
|
}
|
|
|
|
fn dumpDecl(ir: *const Ir, decl: *const Decl, gpa: Allocator, name: []const u8, config: std.io.tty.Config, w: anytype) !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: anytype) !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: anytype) !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.fmt.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: anytype) !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: anytype) !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: anytype) !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 });
|
|
}
|