Commit 1097b0ec77

Andrew Kelley <andrew@ziglang.org>
2021-07-21 03:51:40
codegen: fix lowering of AIR return instruction
It incorrectly did not process the death of its operand. Additionally: * delete dead code accidentally introduced in fe14e339458a578657f3890f00d654a15c84422c * improve AIR printing code to include liveness data for operands. Now an exclamation point ("!") indicates the tombstone of an AIR instruction.
1 parent 91c4e28
src/codegen.zig
@@ -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 {
src/Compilation.zig
@@ -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(),
src/print_air.zig
@@ -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});
+        }
     }
 };