diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 298873e618..b282c2011b 100644 --- a/src-self-hosted/codegen.zig +++ b/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; diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig index 3557c88f4e..c552f28553 100644 --- a/src-self-hosted/zir.zig +++ b/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); } diff --git a/test/stage2/test.zig b/test/stage2/test.zig index fee8886ddb..1a0d801ac3 100644 --- a/test/stage2/test.zig +++ b/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 {