stage2: codegen: fix reuseOperand not doing death bookkeeping

This commit is contained in:
Andrew Kelley 2020-08-26 01:00:04 -07:00
parent 237d9a105d
commit 0c5faa61ae
3 changed files with 79 additions and 2 deletions

View File

@ -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;

View File

@ -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);
}

View File

@ -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 {