stage2: first pass at printing AIR/Liveness to text

* some instructions are not implemented yet
 * fix off-by-1 in Air.getMainBody
 * Compilation: use `@import("builtin")` rather than `std.builtin`
   for the values that are different for different build configurations.
 * Sema: avoid calling `addType` in between
   air_instructions.ensureUnusedCapacity and corresponding
   appendAssumeCapacity because it can possibly add an instruction.
 * Value: functions print their names
This commit is contained in:
Andrew Kelley 2021-07-15 15:52:06 -07:00
parent 12c10139e3
commit eadbee2041
7 changed files with 307 additions and 574 deletions

View File

@ -1,566 +0,0 @@
* be sure to test debug info of parameters
pub fn specialOperandDeaths(self: Inst) bool {
return (self.deaths & (1 << deaths_bits)) != 0;
}
/// Returns `null` if runtime-known.
/// Should be called by codegen, not by Sema. Sema functions should call
/// `resolvePossiblyUndefinedValue` or `resolveDefinedValue` instead.
/// TODO audit Sema code for violations to the above guidance.
pub fn value(base: *Inst) ?Value {
if (base.ty.onePossibleValue()) |opv| return opv;
const inst = base.castTag(.constant) orelse return null;
return inst.val;
}
/// For debugging purposes, prints a function representation to stderr.
pub fn dumpFn(old_module: Module, module_fn: *Module.Fn) void {
const allocator = old_module.gpa;
var ctx: DumpAir = .{
.allocator = allocator,
.arena = std.heap.ArenaAllocator.init(allocator),
.old_module = &old_module,
.module_fn = module_fn,
.indent = 2,
.inst_table = DumpAir.InstTable.init(allocator),
.partial_inst_table = DumpAir.InstTable.init(allocator),
.const_table = DumpAir.InstTable.init(allocator),
};
defer ctx.inst_table.deinit();
defer ctx.partial_inst_table.deinit();
defer ctx.const_table.deinit();
defer ctx.arena.deinit();
switch (module_fn.state) {
.queued => std.debug.print("(queued)", .{}),
.inline_only => std.debug.print("(inline_only)", .{}),
.in_progress => std.debug.print("(in_progress)", .{}),
.sema_failure => std.debug.print("(sema_failure)", .{}),
.dependency_failure => std.debug.print("(dependency_failure)", .{}),
.success => {
const writer = std.io.getStdErr().writer();
ctx.dump(module_fn.body, writer) catch @panic("failed to dump AIR");
},
}
}
const DumpAir = struct {
allocator: *std.mem.Allocator,
arena: std.heap.ArenaAllocator,
old_module: *const Module,
module_fn: *Module.Fn,
indent: usize,
inst_table: InstTable,
partial_inst_table: InstTable,
const_table: InstTable,
next_index: usize = 0,
next_partial_index: usize = 0,
next_const_index: usize = 0,
const InstTable = std.AutoArrayHashMap(*Inst, usize);
/// TODO: Improve this code to include a stack of Body and store the instructions
/// in there. Now we are putting all the instructions in a function local table,
/// however instructions that are in a Body can be thown away when the Body ends.
fn dump(dtz: *DumpAir, body: Body, writer: std.fs.File.Writer) !void {
// First pass to pre-populate the table so that we can show even invalid references.
// Must iterate the same order we iterate the second time.
// We also look for constants and put them in the const_table.
try dtz.fetchInstsAndResolveConsts(body);
std.debug.print("Module.Function(name={s}):\n", .{dtz.module_fn.owner_decl.name});
var it = dtz.const_table.iterator();
while (it.next()) |entry| {
const constant = entry.key_ptr.*.castTag(.constant).?;
try writer.print(" @{d}: {} = {};\n", .{
entry.value_ptr.*, constant.base.ty, constant.val,
});
}
return dtz.dumpBody(body, writer);
}
fn fetchInstsAndResolveConsts(dtz: *DumpAir, body: Body) error{OutOfMemory}!void {
for (body.instructions) |inst| {
try dtz.inst_table.put(inst, dtz.next_index);
dtz.next_index += 1;
switch (inst.tag) {
.alloc,
.retvoid,
.unreach,
.breakpoint,
.dbg_stmt,
.arg,
=> {},
.ref,
.ret,
.bitcast,
.not,
.is_non_null,
.is_non_null_ptr,
.is_null,
.is_null_ptr,
.is_err,
.is_non_err,
.is_err_ptr,
.is_non_err_ptr,
.ptrtoint,
.floatcast,
.intcast,
.load,
.optional_payload,
.optional_payload_ptr,
.wrap_optional,
.wrap_errunion_payload,
.wrap_errunion_err,
.unwrap_errunion_payload,
.unwrap_errunion_err,
.unwrap_errunion_payload_ptr,
.unwrap_errunion_err_ptr,
=> {
const un_op = inst.cast(Inst.UnOp).?;
try dtz.findConst(un_op.operand);
},
.add,
.addwrap,
.sub,
.subwrap,
.mul,
.mulwrap,
.div,
.cmp_lt,
.cmp_lte,
.cmp_eq,
.cmp_gte,
.cmp_gt,
.cmp_neq,
.store,
.bool_and,
.bool_or,
.bit_and,
.bit_or,
.xor,
=> {
const bin_op = inst.cast(Inst.BinOp).?;
try dtz.findConst(bin_op.lhs);
try dtz.findConst(bin_op.rhs);
},
.br => {
const br = inst.castTag(.br).?;
try dtz.findConst(&br.block.base);
try dtz.findConst(br.operand);
},
.br_block_flat => {
const br_block_flat = inst.castTag(.br_block_flat).?;
try dtz.findConst(&br_block_flat.block.base);
try dtz.fetchInstsAndResolveConsts(br_block_flat.body);
},
.br_void => {
const br_void = inst.castTag(.br_void).?;
try dtz.findConst(&br_void.block.base);
},
.block => {
const block = inst.castTag(.block).?;
try dtz.fetchInstsAndResolveConsts(block.body);
},
.condbr => {
const condbr = inst.castTag(.condbr).?;
try dtz.findConst(condbr.condition);
try dtz.fetchInstsAndResolveConsts(condbr.then_body);
try dtz.fetchInstsAndResolveConsts(condbr.else_body);
},
.switchbr => {
const switchbr = inst.castTag(.switchbr).?;
try dtz.findConst(switchbr.target);
try dtz.fetchInstsAndResolveConsts(switchbr.else_body);
for (switchbr.cases) |case| {
try dtz.fetchInstsAndResolveConsts(case.body);
}
},
.loop => {
const loop = inst.castTag(.loop).?;
try dtz.fetchInstsAndResolveConsts(loop.body);
},
.call => {
const call = inst.castTag(.call).?;
try dtz.findConst(call.func);
for (call.args) |arg| {
try dtz.findConst(arg);
}
},
.struct_field_ptr => {
const struct_field_ptr = inst.castTag(.struct_field_ptr).?;
try dtz.findConst(struct_field_ptr.struct_ptr);
},
// TODO fill out this debug printing
.assembly,
.constant,
.varptr,
=> {},
}
}
}
fn dumpBody(dtz: *DumpAir, body: Body, writer: std.fs.File.Writer) (std.fs.File.WriteError || error{OutOfMemory})!void {
for (body.instructions) |inst| {
const my_index = dtz.next_partial_index;
try dtz.partial_inst_table.put(inst, my_index);
dtz.next_partial_index += 1;
try writer.writeByteNTimes(' ', dtz.indent);
try writer.print("%{d}: {} = {s}(", .{
my_index, inst.ty, @tagName(inst.tag),
});
switch (inst.tag) {
.alloc,
.retvoid,
.unreach,
.breakpoint,
.dbg_stmt,
=> try writer.writeAll(")\n"),
.ref,
.ret,
.bitcast,
.not,
.is_non_null,
.is_non_null_ptr,
.is_null,
.is_null_ptr,
.is_err,
.is_err_ptr,
.is_non_err,
.is_non_err_ptr,
.ptrtoint,
.floatcast,
.intcast,
.load,
.optional_payload,
.optional_payload_ptr,
.wrap_optional,
.wrap_errunion_err,
.wrap_errunion_payload,
.unwrap_errunion_err,
.unwrap_errunion_payload,
.unwrap_errunion_payload_ptr,
.unwrap_errunion_err_ptr,
=> {
const un_op = inst.cast(Inst.UnOp).?;
const kinky = try dtz.writeInst(writer, un_op.operand);
if (kinky != null) {
try writer.writeAll(") // Instruction does not dominate all uses!\n");
} else {
try writer.writeAll(")\n");
}
},
.add,
.addwrap,
.sub,
.subwrap,
.mul,
.mulwrap,
.div,
.cmp_lt,
.cmp_lte,
.cmp_eq,
.cmp_gte,
.cmp_gt,
.cmp_neq,
.store,
.bool_and,
.bool_or,
.bit_and,
.bit_or,
.xor,
=> {
const bin_op = inst.cast(Inst.BinOp).?;
const lhs_kinky = try dtz.writeInst(writer, bin_op.lhs);
try writer.writeAll(", ");
const rhs_kinky = try dtz.writeInst(writer, bin_op.rhs);
if (lhs_kinky != null or rhs_kinky != null) {
try writer.writeAll(") // Instruction does not dominate all uses!");
if (lhs_kinky) |lhs| {
try writer.print(" %{d}", .{lhs});
}
if (rhs_kinky) |rhs| {
try writer.print(" %{d}", .{rhs});
}
try writer.writeAll("\n");
} else {
try writer.writeAll(")\n");
}
},
.arg => {
const arg = inst.castTag(.arg).?;
try writer.print("{s})\n", .{arg.name});
},
.br => {
const br = inst.castTag(.br).?;
const lhs_kinky = try dtz.writeInst(writer, &br.block.base);
try writer.writeAll(", ");
const rhs_kinky = try dtz.writeInst(writer, br.operand);
if (lhs_kinky != null or rhs_kinky != null) {
try writer.writeAll(") // Instruction does not dominate all uses!");
if (lhs_kinky) |lhs| {
try writer.print(" %{d}", .{lhs});
}
if (rhs_kinky) |rhs| {
try writer.print(" %{d}", .{rhs});
}
try writer.writeAll("\n");
} else {
try writer.writeAll(")\n");
}
},
.br_block_flat => {
const br_block_flat = inst.castTag(.br_block_flat).?;
const block_kinky = try dtz.writeInst(writer, &br_block_flat.block.base);
if (block_kinky != null) {
try writer.writeAll(", { // Instruction does not dominate all uses!\n");
} else {
try writer.writeAll(", {\n");
}
const old_indent = dtz.indent;
dtz.indent += 2;
try dtz.dumpBody(br_block_flat.body, writer);
dtz.indent = old_indent;
try writer.writeByteNTimes(' ', dtz.indent);
try writer.writeAll("})\n");
},
.br_void => {
const br_void = inst.castTag(.br_void).?;
const kinky = try dtz.writeInst(writer, &br_void.block.base);
if (kinky) |_| {
try writer.writeAll(") // Instruction does not dominate all uses!\n");
} else {
try writer.writeAll(")\n");
}
},
.block => {
const block = inst.castTag(.block).?;
try writer.writeAll("{\n");
const old_indent = dtz.indent;
dtz.indent += 2;
try dtz.dumpBody(block.body, writer);
dtz.indent = old_indent;
try writer.writeByteNTimes(' ', dtz.indent);
try writer.writeAll("})\n");
},
.condbr => {
const condbr = inst.castTag(.condbr).?;
const condition_kinky = try dtz.writeInst(writer, condbr.condition);
if (condition_kinky != null) {
try writer.writeAll(", { // Instruction does not dominate all uses!\n");
} else {
try writer.writeAll(", {\n");
}
const old_indent = dtz.indent;
dtz.indent += 2;
try dtz.dumpBody(condbr.then_body, writer);
try writer.writeByteNTimes(' ', old_indent);
try writer.writeAll("}, {\n");
try dtz.dumpBody(condbr.else_body, writer);
dtz.indent = old_indent;
try writer.writeByteNTimes(' ', old_indent);
try writer.writeAll("})\n");
},
.switchbr => {
const switchbr = inst.castTag(.switchbr).?;
const condition_kinky = try dtz.writeInst(writer, switchbr.target);
if (condition_kinky != null) {
try writer.writeAll(", { // Instruction does not dominate all uses!\n");
} else {
try writer.writeAll(", {\n");
}
const old_indent = dtz.indent;
if (switchbr.else_body.instructions.len != 0) {
dtz.indent += 2;
try dtz.dumpBody(switchbr.else_body, writer);
try writer.writeByteNTimes(' ', old_indent);
try writer.writeAll("}, {\n");
dtz.indent = old_indent;
}
for (switchbr.cases) |case| {
dtz.indent += 2;
try dtz.dumpBody(case.body, writer);
try writer.writeByteNTimes(' ', old_indent);
try writer.writeAll("}, {\n");
dtz.indent = old_indent;
}
try writer.writeByteNTimes(' ', old_indent);
try writer.writeAll("})\n");
},
.loop => {
const loop = inst.castTag(.loop).?;
try writer.writeAll("{\n");
const old_indent = dtz.indent;
dtz.indent += 2;
try dtz.dumpBody(loop.body, writer);
dtz.indent = old_indent;
try writer.writeByteNTimes(' ', dtz.indent);
try writer.writeAll("})\n");
},
.call => {
const call = inst.castTag(.call).?;
const args_kinky = try dtz.allocator.alloc(?usize, call.args.len);
defer dtz.allocator.free(args_kinky);
std.mem.set(?usize, args_kinky, null);
var any_kinky_args = false;
const func_kinky = try dtz.writeInst(writer, call.func);
for (call.args) |arg, i| {
try writer.writeAll(", ");
args_kinky[i] = try dtz.writeInst(writer, arg);
any_kinky_args = any_kinky_args or args_kinky[i] != null;
}
if (func_kinky != null or any_kinky_args) {
try writer.writeAll(") // Instruction does not dominate all uses!");
if (func_kinky) |func_index| {
try writer.print(" %{d}", .{func_index});
}
for (args_kinky) |arg_kinky| {
if (arg_kinky) |arg_index| {
try writer.print(" %{d}", .{arg_index});
}
}
try writer.writeAll("\n");
} else {
try writer.writeAll(")\n");
}
},
.struct_field_ptr => {
const struct_field_ptr = inst.castTag(.struct_field_ptr).?;
const kinky = try dtz.writeInst(writer, struct_field_ptr.struct_ptr);
if (kinky != null) {
try writer.print("{d}) // Instruction does not dominate all uses!\n", .{
struct_field_ptr.field_index,
});
} else {
try writer.print("{d})\n", .{struct_field_ptr.field_index});
}
},
// TODO fill out this debug printing
.assembly,
.constant,
.varptr,
=> {
try writer.writeAll("!TODO!)\n");
},
}
}
}
fn writeInst(dtz: *DumpAir, writer: std.fs.File.Writer, inst: *Inst) !?usize {
if (dtz.partial_inst_table.get(inst)) |operand_index| {
try writer.print("%{d}", .{operand_index});
return null;
} else if (dtz.const_table.get(inst)) |operand_index| {
try writer.print("@{d}", .{operand_index});
return null;
} else if (dtz.inst_table.get(inst)) |operand_index| {
try writer.print("%{d}", .{operand_index});
return operand_index;
} else {
try writer.writeAll("!BADREF!");
return null;
}
}
fn findConst(dtz: *DumpAir, operand: *Inst) !void {
if (operand.tag == .constant) {
try dtz.const_table.put(operand, dtz.next_const_index);
dtz.next_const_index += 1;
}
}
};
pub fn dumpInst(mod: *Module, scope: *Scope, inst: *ir.Inst) void {
const zir_module = scope.namespace();
const source = zir_module.getSource(mod) catch @panic("dumpInst failed to get source");
const loc = std.zig.findLineColumn(source, inst.src);
if (inst.tag == .constant) {
std.debug.print("constant ty={} val={} src={s}:{d}:{d}\n", .{
inst.ty,
inst.castTag(.constant).?.val,
zir_module.subFilePath(),
loc.line + 1,
loc.column + 1,
});
} else if (inst.deaths == 0) {
std.debug.print("{s} ty={} src={s}:{d}:{d}\n", .{
@tagName(inst.tag),
inst.ty,
zir_module.subFilePath(),
loc.line + 1,
loc.column + 1,
});
} else {
std.debug.print("{s} ty={} deaths={b} src={s}:{d}:{d}\n", .{
@tagName(inst.tag),
inst.ty,
inst.deaths,
zir_module.subFilePath(),
loc.line + 1,
loc.column + 1,
});
}
}
/// For debugging purposes.
pub fn dump(func: *Fn, mod: Module) void {
ir.dumpFn(mod, func);
}

View File

@ -374,8 +374,8 @@ pub const Asm = struct {
pub fn getMainBody(air: Air) []const Air.Inst.Index {
const body_index = air.extra[@enumToInt(ExtraIndex.main_block)];
const body_len = air.extra[body_index];
return air.extra[body_index..][0..body_len];
const extra = air.extraData(Block, body_index);
return air.extra[extra.end..][0..extra.data.body_len];
}
pub fn getType(air: Air, inst: Air.Inst.Index) Type {

View File

@ -1,6 +1,7 @@
const Compilation = @This();
const std = @import("std");
const builtin = @import("builtin");
const mem = std.mem;
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
@ -907,7 +908,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
// comptime conditions
((build_options.have_llvm and comptime std.Target.current.isDarwin()) and
// runtime conditions
(use_lld and std.builtin.os.tag == .macos and options.target.isDarwin()));
(use_lld and builtin.os.tag == .macos and options.target.isDarwin()));
const sysroot = blk: {
if (options.sysroot) |sysroot| {
@ -2026,8 +2027,10 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
var liveness = try Liveness.analyze(gpa, air, decl.namespace.file_scope.zir);
defer liveness.deinit(gpa);
if (std.builtin.mode == .Debug and self.verbose_air) {
@panic("TODO implement dumping AIR and liveness");
if (builtin.mode == .Debug and self.verbose_air) {
std.debug.print("# Begin Function AIR: {s}:\n", .{decl.name});
@import("print_air.zig").dump(gpa, air, liveness);
std.debug.print("# End Function AIR: {s}:\n", .{decl.name});
}
assert(decl.ty.hasCodeGenBits());

View File

@ -3551,7 +3551,8 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
try sema.analyzeFnBody(&inner_block, func.zir_body_inst);
// Copy the block into place and mark that as the main block.
try sema.air_extra.ensureUnusedCapacity(gpa, inner_block.instructions.items.len + 1);
try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
inner_block.instructions.items.len);
const main_block_index = sema.addExtraAssumeCapacity(Air.Block{
.body_len = @intCast(u32, inner_block.instructions.items.len),
});

View File

@ -2028,6 +2028,7 @@ fn analyzeBlockBody(
refToIndex(coerced_operand).?);
// Convert the br operand to a block.
const br_operand_ty_ref = try sema.addType(br_operand_ty);
try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
coerce_block.instructions.items.len);
try sema.air_instructions.ensureUnusedCapacity(gpa, 2);
@ -2037,7 +2038,7 @@ fn analyzeBlockBody(
sema.air_instructions.appendAssumeCapacity(.{
.tag = .block,
.data = .{ .ty_pl = .{
.ty = try sema.addType(br_operand_ty),
.ty = br_operand_ty_ref,
.payload = sema.addExtraAssumeCapacity(Air.Block{
.body_len = @intCast(u32, coerce_block.instructions.items.len),
}),

294
src/print_air.zig Normal file
View File

@ -0,0 +1,294 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
const Module = @import("Module.zig");
const Value = @import("value.zig").Value;
const Air = @import("Air.zig");
const Liveness = @import("Liveness.zig");
pub fn dump(gpa: *Allocator, air: Air, liveness: Liveness) void {
const instruction_bytes = air.instructions.len *
// Here we don't use @sizeOf(Air.Inst.Data) because it would include
// the debug safety tag but we want to measure release size.
(@sizeOf(Air.Inst.Tag) + 8);
const extra_bytes = air.extra.len * @sizeOf(u32);
const values_bytes = air.values.len * @sizeOf(Value);
const variables_bytes = air.variables.len * @sizeOf(*Module.Var);
const tomb_bytes = liveness.tomb_bits.len * @sizeOf(usize);
const liveness_extra_bytes = liveness.extra.len * @sizeOf(u32);
const liveness_special_bytes = liveness.special.count() * 8;
const total_bytes = @sizeOf(Air) + instruction_bytes + extra_bytes +
values_bytes * variables_bytes + @sizeOf(Liveness) + liveness_extra_bytes +
liveness_special_bytes + tomb_bytes;
// zig fmt: off
std.debug.print(
\\# Total AIR+Liveness bytes: {}
\\# AIR Instructions: {d} ({})
\\# AIR Extra Data: {d} ({})
\\# AIR Values Bytes: {d} ({})
\\# AIR Variables Bytes: {d} ({})
\\# Liveness tomb_bits: {}
\\# Liveness Extra Data: {d} ({})
\\# Liveness special table: {d} ({})
\\
, .{
fmtIntSizeBin(total_bytes),
air.instructions.len, fmtIntSizeBin(instruction_bytes),
air.extra.len, fmtIntSizeBin(extra_bytes),
air.values.len, fmtIntSizeBin(values_bytes),
air.variables.len, fmtIntSizeBin(variables_bytes),
fmtIntSizeBin(tomb_bytes),
liveness.extra.len, fmtIntSizeBin(liveness_extra_bytes),
liveness.special.count(), fmtIntSizeBin(liveness_special_bytes),
});
// zig fmt: on
var arena = std.heap.ArenaAllocator.init(gpa);
defer arena.deinit();
var writer: Writer = .{
.gpa = gpa,
.arena = &arena.allocator,
.air = air,
.liveness = liveness,
.indent = 0,
};
const stream = std.io.getStdErr().writer();
writer.writeAllConstants(stream) catch return;
writer.writeBody(stream, air.getMainBody()) catch return;
}
const Writer = struct {
gpa: *Allocator,
arena: *Allocator,
air: Air,
liveness: Liveness,
indent: usize,
fn writeAllConstants(w: *Writer, s: anytype) @TypeOf(s).Error!void {
for (w.air.instructions.items(.tag)) |tag, i| {
const inst = @intCast(u32, i);
switch (tag) {
.constant, .const_ty => {
try s.writeByteNTimes(' ', w.indent);
try s.print("%{d} ", .{inst});
try w.writeInst(s, inst);
try s.writeAll(")\n");
},
else => continue,
}
}
}
fn writeBody(w: *Writer, s: anytype, body: []const Air.Inst.Index) @TypeOf(s).Error!void {
for (body) |inst| {
try s.writeByteNTimes(' ', w.indent);
try s.print("%{d} ", .{inst});
try w.writeInst(s, inst);
if (w.liveness.isUnused(inst)) {
try s.writeAll(") unused\n");
} else {
try s.writeAll("\n");
}
}
}
fn writeInst(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
const tags = w.air.instructions.items(.tag);
const tag = tags[inst];
try s.print("= {s}(", .{@tagName(tags[inst])});
switch (tag) {
.arg => try w.writeTyStr(s, inst),
.add,
.addwrap,
.sub,
.subwrap,
.mul,
.mulwrap,
.div,
.bit_and,
.bit_or,
.xor,
.cmp_lt,
.cmp_lte,
.cmp_eq,
.cmp_gte,
.cmp_gt,
.cmp_neq,
.bool_and,
.bool_or,
.store,
=> try w.writeBinOp(s, inst),
.is_null,
.is_non_null,
.is_null_ptr,
.is_non_null_ptr,
.is_err,
.is_non_err,
.is_err_ptr,
.is_non_err_ptr,
.ptrtoint,
.ret,
=> try w.writeUnOp(s, inst),
.breakpoint,
.unreach,
=> try w.writeNoOp(s, inst),
.const_ty,
.alloc,
=> try w.writeTy(s, inst),
.not,
.bitcast,
.load,
.ref,
.floatcast,
.intcast,
.optional_payload,
.optional_payload_ptr,
.wrap_optional,
.unwrap_errunion_payload,
.unwrap_errunion_err,
.unwrap_errunion_payload_ptr,
.unwrap_errunion_err_ptr,
.wrap_errunion_payload,
.wrap_errunion_err,
=> try w.writeTyOp(s, inst),
.block,
.loop,
=> try w.writeBlock(s, inst),
.struct_field_ptr => try w.writeStructFieldPtr(s, inst),
.varptr => try w.writeVarPtr(s, inst),
.constant => try w.writeConstant(s, inst),
.assembly => try w.writeAssembly(s, inst),
.dbg_stmt => try w.writeDbgStmt(s, inst),
.call => try w.writeCall(s, inst),
.br => try w.writeBr(s, inst),
.cond_br => try w.writeCondBr(s, inst),
.switch_br => try w.writeSwitchBr(s, inst),
}
}
fn writeTyStr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
_ = w;
_ = inst;
try s.writeAll("TODO");
}
fn writeBinOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
_ = w;
_ = inst;
try s.writeAll("TODO");
}
fn writeUnOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
_ = w;
_ = inst;
try s.writeAll("TODO");
}
fn writeNoOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
_ = w;
_ = inst;
try s.writeAll("TODO");
}
fn writeTy(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
const ty = w.air.instructions.items(.data)[inst].ty;
try s.print("{}", .{ty});
}
fn writeTyOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
_ = w;
_ = inst;
try s.writeAll("TODO");
}
fn writeBlock(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
_ = w;
_ = inst;
try s.writeAll("TODO");
}
fn writeStructFieldPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
_ = w;
_ = inst;
try s.writeAll("TODO");
}
fn writeVarPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
_ = w;
_ = inst;
try s.writeAll("TODO");
}
fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
const val = w.air.values[ty_pl.payload];
try s.print("{}, {}", .{ ty_pl.ty, val });
}
fn writeAssembly(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
_ = w;
_ = inst;
try s.writeAll("TODO");
}
fn writeDbgStmt(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
const dbg_stmt = w.air.instructions.items(.data)[inst].dbg_stmt;
try s.print("{d}:{d}", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 });
}
fn writeCall(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
const pl_op = w.air.instructions.items(.data)[inst].pl_op;
const extra = w.air.extraData(Air.Call, pl_op.payload);
const args = w.air.extra[extra.end..][0..extra.data.args_len];
try w.writeInstRef(s, pl_op.operand);
try s.writeAll(", [");
for (args) |arg, i| {
if (i != 0) try s.writeAll(", ");
try w.writeInstRef(s, @intToEnum(Air.Inst.Ref, arg));
}
try s.writeAll("]");
}
fn writeBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
_ = w;
_ = inst;
try s.writeAll("TODO");
}
fn writeCondBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
_ = w;
_ = inst;
try s.writeAll("TODO");
}
fn writeSwitchBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
_ = w;
_ = inst;
try s.writeAll("TODO");
}
fn writeInstRef(w: *Writer, s: anytype, inst: Air.Inst.Ref) @TypeOf(s).Error!void {
var i: usize = @enumToInt(inst);
if (i < Air.Inst.Ref.typed_value_map.len) {
return s.print("@{}", .{inst});
}
i -= Air.Inst.Ref.typed_value_map.len;
return w.writeInstIndex(s, @intCast(Air.Inst.Index, i));
}
fn writeInstIndex(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
_ = w;
return s.print("%{d}", .{inst});
}
};

View File

@ -573,7 +573,7 @@ pub const Value = extern union {
.int_i64 => return std.fmt.formatIntValue(val.castTag(.int_i64).?.data, "", options, out_stream),
.int_big_positive => return out_stream.print("{}", .{val.castTag(.int_big_positive).?.asBigInt()}),
.int_big_negative => return out_stream.print("{}", .{val.castTag(.int_big_negative).?.asBigInt()}),
.function => return out_stream.writeAll("(function)"),
.function => return out_stream.print("(function '{s}')", .{val.castTag(.function).?.data.owner_decl.name}),
.extern_fn => return out_stream.writeAll("(extern function)"),
.variable => return out_stream.writeAll("(variable)"),
.ref_val => {