Commit 0c5faa61ae
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 {