codegen: fix lowering of AIR return instruction

It incorrectly did not process the death of its operand. Additionally:

 * delete dead code accidentally introduced in fe14e33945
 * improve AIR printing code to include liveness data for operands.
   Now an exclamation point ("!") indicates the tombstone of an AIR
   instruction.
This commit is contained in:
Andrew Kelley 2021-07-20 18:51:40 -07:00
parent 91c4e28c51
commit 1097b0ec77
3 changed files with 91 additions and 95 deletions

View File

@ -2007,59 +2007,6 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
@panic("sadly stage2 is omitted from this build to save memory on the CI server");
const module = self.bin_file.options.module.?;
assert(decl.has_tv);
if (decl.val.castTag(.function)) |payload| {
if (decl.owns_tv) {
const func = payload.data;
var air = switch (func.state) {
.sema_failure, .dependency_failure => continue,
.queued => module.analyzeFnBody(decl, func) catch |err| switch (err) {
error.AnalysisFail => {
assert(func.state != .in_progress);
continue;
},
error.OutOfMemory => return error.OutOfMemory,
},
.in_progress => unreachable,
.inline_only => unreachable, // don't queue work for this
.success => unreachable, // don't queue it twice
};
defer air.deinit(gpa);
log.debug("analyze liveness of {s}", .{decl.name});
var liveness = try Liveness.analyze(gpa, air, decl.namespace.file_scope.zir);
defer liveness.deinit(gpa);
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());
self.bin_file.updateFunc(module, func, air, liveness) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
error.AnalysisFail => {
decl.analysis = .codegen_failure;
continue;
},
else => {
try module.failed_decls.ensureUnusedCapacity(gpa, 1);
module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create(
gpa,
decl.srcLoc(),
"unable to codegen: {s}",
.{@errorName(err)},
));
decl.analysis = .codegen_failure_retryable;
continue;
},
};
continue;
}
}
assert(decl.ty.hasCodeGenBits());
self.bin_file.updateDecl(module, decl) catch |err| switch (err) {
@ -2069,7 +2016,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
continue;
},
else => {
try module.failed_decls.ensureCapacity(gpa, module.failed_decls.count() + 1);
try module.failed_decls.ensureUnusedCapacity(gpa, 1);
module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create(
gpa,
decl.srcLoc(),
@ -2123,7 +2070,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
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);
@import("print_air.zig").dump(gpa, air, decl.namespace.file_scope.zir, liveness);
std.debug.print("# End Function AIR: {s}:\n", .{decl.name});
}
@ -2207,7 +2154,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
@panic("sadly stage2 is omitted from this build to save memory on the CI server");
const module = self.bin_file.options.module.?;
self.bin_file.updateDeclLineNumber(module, decl) catch |err| {
try module.failed_decls.ensureCapacity(gpa, module.failed_decls.count() + 1);
try module.failed_decls.ensureUnusedCapacity(gpa, 1);
module.failed_decls.putAssumeCapacityNoClobber(decl, try Module.ErrorMsg.create(
gpa,
decl.srcLoc(),

View File

@ -481,7 +481,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
fn finishAir(bt: *BigTomb, result: MCValue) void {
const is_used = !bt.function.liveness.isUnused(bt.inst);
if (is_used) {
log.debug("{} => {}", .{ bt.inst, result });
log.debug("%{d} => {}", .{ bt.inst, result });
const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1];
branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result);
}
@ -871,12 +871,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
// zig fmt: on
}
if (std.debug.runtime_safety) {
if (self.air_bookkeeping != old_air_bookkeeping + 1) {
std.debug.panic(
\\in codegen.zig, handling of AIR instruction %{d} ('{}') did not do proper bookkeeping.
\\Look for a missing call to finishAir or an extra call to it.
\\
, .{ inst, air_tags[inst] });
if (self.air_bookkeeping < old_air_bookkeeping + 1) {
std.debug.panic("in codegen.zig, handling of AIR instruction %{d} ('{}') did not do proper bookkeeping. Look for a missing call to finishAir.", .{ inst, air_tags[inst] });
}
}
}
@ -963,7 +959,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
}
const is_used = @truncate(u1, tomb_bits) == 0;
if (is_used) {
log.debug("{} => {}", .{ inst, result });
log.debug("%{d} => {}", .{ inst, result });
const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
branch.inst_table.putAssumeCapacityNoClobber(inst, result);
}
@ -1350,10 +1346,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
self.register_manager.registers[index] = inst;
}
}
log.debug("reusing {} => {}", .{ reg, inst });
log.debug("%{d} => {} (reused)", .{ inst, reg });
},
.stack_offset => |off| {
log.debug("reusing stack offset {} => {}", .{ off, inst });
log.debug("%{d} => stack offset {d} (reused)", .{ inst, off });
},
else => return false,
}
@ -2852,7 +2848,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
const un_op = self.air.instructions.items(.data)[inst].un_op;
const operand = try self.resolveInst(un_op);
try self.ret(operand);
return self.finishAirBookkeeping();
return self.finishAir(inst, .dead, .{ un_op, .none, .none });
}
fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {

View File

@ -4,10 +4,11 @@ const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
const Module = @import("Module.zig");
const Value = @import("value.zig").Value;
const Zir = @import("Zir.zig");
const Air = @import("Air.zig");
const Liveness = @import("Liveness.zig");
pub fn dump(gpa: *Allocator, air: Air, liveness: Liveness) void {
pub fn dump(gpa: *Allocator, air: Air, zir: Zir, 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.
@ -51,11 +52,13 @@ pub fn dump(gpa: *Allocator, air: Air, liveness: Liveness) void {
.gpa = gpa,
.arena = &arena.allocator,
.air = air,
.zir = zir,
.liveness = liveness,
.indent = 0,
.indent = 2,
};
const stream = std.io.getStdErr().writer();
writer.writeAllConstants(stream) catch return;
stream.writeByte('\n') catch return;
writer.writeBody(stream, air.getMainBody()) catch return;
}
@ -63,6 +66,7 @@ const Writer = struct {
gpa: *Allocator,
arena: *Allocator,
air: Air,
zir: Zir,
liveness: Liveness,
indent: usize,
@ -84,13 +88,13 @@ const Writer = struct {
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");
try s.print("%{d}!", .{inst});
} else {
try s.writeAll(")\n");
try s.print("%{d} ", .{inst});
}
try w.writeInst(s, inst);
try s.writeAll(")\n");
}
}
@ -176,21 +180,21 @@ const Writer = struct {
}
fn writeTyStr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
_ = w;
_ = inst;
try s.writeAll("TODO");
const ty_str = w.air.instructions.items(.data)[inst].ty_str;
const name = w.zir.nullTerminatedString(ty_str.str);
try s.print("\"{}\", {}", .{ std.zig.fmtEscapes(name), ty_str.ty });
}
fn writeBinOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
const bin_op = w.air.instructions.items(.data)[inst].bin_op;
try w.writeInstRef(s, bin_op.lhs);
try w.writeOperand(s, inst, 0, bin_op.lhs);
try s.writeAll(", ");
try w.writeInstRef(s, bin_op.rhs);
try w.writeOperand(s, inst, 1, bin_op.rhs);
}
fn writeUnOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
const un_op = w.air.instructions.items(.data)[inst].un_op;
try w.writeInstRef(s, un_op);
try w.writeOperand(s, inst, 0, un_op);
}
fn writeNoOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
@ -208,7 +212,7 @@ const Writer = struct {
fn writeTyOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
const ty_op = w.air.instructions.items(.data)[inst].ty_op;
try s.print("{}, ", .{w.air.getRefType(ty_op.ty)});
try w.writeInstRef(s, ty_op.operand);
try w.writeOperand(s, inst, 0, ty_op.operand);
}
fn writeBlock(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
@ -229,7 +233,7 @@ const Writer = struct {
const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
const extra = w.air.extraData(Air.StructField, ty_pl.payload);
try w.writeInstRef(s, extra.data.struct_ptr);
try w.writeOperand(s, inst, 0, extra.data.struct_ptr);
try s.print(", {d}", .{extra.data.field_index});
}
@ -259,21 +263,21 @@ const Writer = struct {
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);
const args = @bitCast([]const Air.Inst.Ref, w.air.extra[extra.end..][0..extra.data.args_len]);
try w.writeOperand(s, inst, 0, 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 w.writeOperand(s, inst, 1 + i, arg);
}
try s.writeAll("]");
}
fn writeBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
const br = w.air.instructions.items(.data)[inst].br;
try w.writeInstIndex(s, br.block_inst);
try w.writeInstIndex(s, br.block_inst, false);
try s.writeAll(", ");
try w.writeInstRef(s, br.operand);
try w.writeOperand(s, inst, 0, br.operand);
}
fn writeCondBr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
@ -281,16 +285,35 @@ const Writer = struct {
const extra = w.air.extraData(Air.CondBr, pl_op.payload);
const then_body = w.air.extra[extra.end..][0..extra.data.then_body_len];
const else_body = w.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
const liveness_condbr = w.liveness.getCondBr(inst);
try w.writeInstRef(s, pl_op.operand);
try w.writeOperand(s, inst, 0, pl_op.operand);
try s.writeAll(", {\n");
const old_indent = w.indent;
w.indent += 2;
if (liveness_condbr.then_deaths.len != 0) {
try s.writeByteNTimes(' ', w.indent);
for (liveness_condbr.then_deaths) |operand, i| {
if (i != 0) try s.writeAll(" ");
try s.print("%{d}!", .{operand});
}
try s.writeAll("\n");
}
try w.writeBody(s, then_body);
try s.writeByteNTimes(' ', old_indent);
try s.writeAll("}, {\n");
if (liveness_condbr.else_deaths.len != 0) {
try s.writeByteNTimes(' ', w.indent);
for (liveness_condbr.else_deaths) |operand, i| {
if (i != 0) try s.writeAll(" ");
try s.print("%{d}!", .{operand});
}
try s.writeAll("\n");
}
try w.writeBody(s, else_body);
w.indent = old_indent;
@ -304,7 +327,7 @@ const Writer = struct {
var extra_index: usize = switch_br.end;
var case_i: u32 = 0;
try w.writeInstRef(s, pl_op.operand);
try w.writeOperand(s, inst, 0, pl_op.operand);
const old_indent = w.indent;
w.indent += 2;
@ -317,7 +340,7 @@ const Writer = struct {
try s.writeAll(", [");
for (items) |item, item_i| {
if (item_i != 0) try s.writeAll(", ");
try w.writeInstRef(s, item);
try w.writeInstRef(s, item, false);
}
try s.writeAll("] => {\n");
w.indent += 2;
@ -342,19 +365,49 @@ const Writer = struct {
try s.writeAll("}");
}
fn writeInstRef(w: *Writer, s: anytype, inst: Air.Inst.Ref) @TypeOf(s).Error!void {
var i: usize = @enumToInt(inst);
fn writeOperand(
w: *Writer,
s: anytype,
inst: Air.Inst.Index,
op_index: usize,
operand: Air.Inst.Ref,
) @TypeOf(s).Error!void {
const dies = if (op_index < Liveness.bpi - 1)
w.liveness.operandDies(inst, @intCast(Liveness.OperandInt, op_index))
else blk: {
// TODO
break :blk false;
};
return w.writeInstRef(s, operand, dies);
}
fn writeInstRef(
w: *Writer,
s: anytype,
operand: Air.Inst.Ref,
dies: bool,
) @TypeOf(s).Error!void {
var i: usize = @enumToInt(operand);
if (i < Air.Inst.Ref.typed_value_map.len) {
return s.print("@{}", .{inst});
return s.print("@{}", .{operand});
}
i -= Air.Inst.Ref.typed_value_map.len;
return w.writeInstIndex(s, @intCast(Air.Inst.Index, i));
return w.writeInstIndex(s, @intCast(Air.Inst.Index, i), dies);
}
fn writeInstIndex(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
fn writeInstIndex(
w: *Writer,
s: anytype,
inst: Air.Inst.Index,
dies: bool,
) @TypeOf(s).Error!void {
_ = w;
return s.print("%{d}", .{inst});
if (dies) {
try s.print("%{d}!", .{inst});
} else {
try s.print("%{d}", .{inst});
}
}
};