mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 04:48:20 +00:00
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:
parent
12c10139e3
commit
eadbee2041
566
BRANCH_TODO
566
BRANCH_TODO
@ -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);
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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),
|
||||
});
|
||||
|
||||
@ -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
294
src/print_air.zig
Normal 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});
|
||||
}
|
||||
};
|
||||
@ -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 => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user