Commit 0c5faa61ae

Andrew Kelley <andrew@ziglang.org>
2020-08-26 10:00:04
stage2: codegen: fix reuseOperand not doing death bookkeeping
1 parent 237d9a1
Changed files (3)
src-self-hosted
test
stage2
src-self-hosted/codegen.zig
@@ -632,6 +632,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
         /// Asserts there is already capacity to insert into top branch inst_table.
         fn processDeath(self: *Self, inst: *ir.Inst) void {
             if (inst.tag == .constant) return; // Constants are immortal.
+            // When editing this function, note that the logic must synchronize with `reuseOperand`.
             const prev_value = self.getResolvedInstValue(inst);
             const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
             branch.inst_table.putAssumeCapacity(inst, .dead);
@@ -951,6 +952,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
             // Prevent the operand deaths processing code from deallocating it.
             inst.clearOperandDeath(op_index);
 
+            // That makes us responsible for doing the rest of the stuff that processDeath would have done.
+            const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
+            branch.inst_table.putAssumeCapacity(inst.getOperand(op_index).?, .dead);
+
             return true;
         }
 
@@ -1666,7 +1671,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
                     // The instruction is only overridden in the else branch.
                     var i: usize = self.branch_stack.items.len - 2;
                     while (true) {
-                        i -= 1;
+                        i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead?
                         if (self.branch_stack.items[i].inst_table.get(else_entry.key)) |mcv| {
                             assert(mcv != .dead);
                             break :blk mcv;
src-self-hosted/zir.zig
@@ -954,6 +954,7 @@ pub const Module = struct {
 
     pub const MetaData = struct {
         deaths: ir.Inst.DeathsInt,
+        addr: usize,
     };
 
     pub const BodyMetaData = struct {
@@ -1152,6 +1153,12 @@ const Writer = struct {
                     try self.writeInstToStream(stream, inst);
                     if (self.module.metadata.get(inst)) |metadata| {
                         try stream.print(" ; deaths=0b{b}", .{metadata.deaths});
+                        // This is conditionally compiled in because addresses mess up the tests due
+                        // to Address Space Layout Randomization. It's super useful when debugging
+                        // codegen.zig though.
+                        if (!std.builtin.is_test) {
+                            try stream.print(" 0x{x}", .{metadata.addr});
+                        }
                     }
                     self.indent -= 2;
                     try stream.writeByte('\n');
@@ -2417,7 +2424,10 @@ const EmitZIR = struct {
 
                 .varptr => @panic("TODO"),
             };
-            try self.metadata.put(new_inst, .{ .deaths = inst.deaths });
+            try self.metadata.put(new_inst, .{
+                .deaths = inst.deaths,
+                .addr = @ptrToInt(inst),
+            });
             try instructions.append(new_inst);
             try inst_table.put(inst, new_inst);
         }
test/stage2/test.zig
@@ -694,6 +694,68 @@ pub fn addCases(ctx: *TestContext) !void {
             "",
         );
 
+        // Reusing the registers of dead operands playing nicely with conditional branching.
+        case.addCompareOutput(
+            \\export fn _start() noreturn {
+            \\    assert(add(3, 4) == 791);
+            \\    assert(add(4, 3) == 79);
+            \\
+            \\    exit();
+            \\}
+            \\
+            \\fn add(a: u32, b: u32) u32 {
+            \\    const x: u32 = if (a < b) blk: {
+            \\        const c = a + b; // 7
+            \\        const d = a + c; // 10
+            \\        const e = d + b; // 14
+            \\        const f = d + e; // 24
+            \\        const g = e + f; // 38
+            \\        const h = f + g; // 62
+            \\        const i = g + h; // 100
+            \\        const j = i + d; // 110
+            \\        const k = i + j; // 210
+            \\        const l = k + c; // 217
+            \\        const m = l + d; // 227
+            \\        const n = m + e; // 241
+            \\        const o = n + f; // 265
+            \\        const p = o + g; // 303
+            \\        const q = p + h; // 365
+            \\        const r = q + i; // 465
+            \\        const s = r + j; // 575
+            \\        const t = s + k; // 785
+            \\        break :blk t;
+            \\    } else blk: {
+            \\        const t = b + b + a; // 10
+            \\        const c = a + t; // 14
+            \\        const d = c + t; // 24
+            \\        const e = d + t; // 34
+            \\        const f = e + t; // 44
+            \\        const g = f + t; // 54
+            \\        const h = c + g; // 68
+            \\        break :blk h + b; // 71
+            \\    };
+            \\    const y = x + a; // 788, 75
+            \\    const z = y + a; // 791, 79
+            \\    return z;
+            \\}
+            \\
+            \\pub fn assert(ok: bool) void {
+            \\    if (!ok) unreachable; // assertion failure
+            \\}
+            \\
+            \\fn exit() noreturn {
+            \\    asm volatile ("syscall"
+            \\        :
+            \\        : [number] "{rax}" (231),
+            \\          [arg1] "{rdi}" (0)
+            \\        : "rcx", "r11", "memory"
+            \\    );
+            \\    unreachable;
+            \\}
+        ,
+            "",
+        );
+
         // Character literals and multiline strings.
         case.addCompareOutput(
             \\export fn _start() noreturn {