authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-26 01:00:04-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-26 01:00:04-07:00
log0c5faa61aebca4215683d233dd52bf3a7a5d1db6
tree2ed6e829c2a74c5bb88384b420e11299eed6927f
parent237d9a105d5eec82aefc63da8e844c47d8990eea

stage2: codegen: fix reuseOperand not doing death bookkeeping


3 files changed, 79 insertions(+), 2 deletions(-)

src-self-hosted/codegen.zig+6-1
...@@ -632,6 +632,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -632,6 +632,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
632 /// Asserts there is already capacity to insert into top branch inst_table.632 /// Asserts there is already capacity to insert into top branch inst_table.
633 fn processDeath(self: *Self, inst: *ir.Inst) void {633 fn processDeath(self: *Self, inst: *ir.Inst) void {
634 if (inst.tag == .constant) return; // Constants are immortal.634 if (inst.tag == .constant) return; // Constants are immortal.
635 // When editing this function, note that the logic must synchronize with `reuseOperand`.
635 const prev_value = self.getResolvedInstValue(inst);636 const prev_value = self.getResolvedInstValue(inst);
636 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];637 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
637 branch.inst_table.putAssumeCapacity(inst, .dead);638 branch.inst_table.putAssumeCapacity(inst, .dead);
...@@ -951,6 +952,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -951,6 +952,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
951 // Prevent the operand deaths processing code from deallocating it.952 // Prevent the operand deaths processing code from deallocating it.
952 inst.clearOperandDeath(op_index);953 inst.clearOperandDeath(op_index);
953954
955 // That makes us responsible for doing the rest of the stuff that processDeath would have done.
956 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
957 branch.inst_table.putAssumeCapacity(inst.getOperand(op_index).?, .dead);
958
954 return true;959 return true;
955 }960 }
956961
...@@ -1666,7 +1671,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1666,7 +1671,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1666 // The instruction is only overridden in the else branch.1671 // The instruction is only overridden in the else branch.
1667 var i: usize = self.branch_stack.items.len - 2;1672 var i: usize = self.branch_stack.items.len - 2;
1668 while (true) {1673 while (true) {
1669 i -= 1;1674 i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead?
1670 if (self.branch_stack.items[i].inst_table.get(else_entry.key)) |mcv| {1675 if (self.branch_stack.items[i].inst_table.get(else_entry.key)) |mcv| {
1671 assert(mcv != .dead);1676 assert(mcv != .dead);
1672 break :blk mcv;1677 break :blk mcv;
src-self-hosted/zir.zig+11-1
...@@ -954,6 +954,7 @@ pub const Module = struct {...@@ -954,6 +954,7 @@ pub const Module = struct {
954954
955 pub const MetaData = struct {955 pub const MetaData = struct {
956 deaths: ir.Inst.DeathsInt,956 deaths: ir.Inst.DeathsInt,
957 addr: usize,
957 };958 };
958959
959 pub const BodyMetaData = struct {960 pub const BodyMetaData = struct {
...@@ -1152,6 +1153,12 @@ const Writer = struct {...@@ -1152,6 +1153,12 @@ const Writer = struct {
1152 try self.writeInstToStream(stream, inst);1153 try self.writeInstToStream(stream, inst);
1153 if (self.module.metadata.get(inst)) |metadata| {1154 if (self.module.metadata.get(inst)) |metadata| {
1154 try stream.print(" ; deaths=0b{b}", .{metadata.deaths});1155 try stream.print(" ; deaths=0b{b}", .{metadata.deaths});
1156 // This is conditionally compiled in because addresses mess up the tests due
1157 // to Address Space Layout Randomization. It's super useful when debugging
1158 // codegen.zig though.
1159 if (!std.builtin.is_test) {
1160 try stream.print(" 0x{x}", .{metadata.addr});
1161 }
1155 }1162 }
1156 self.indent -= 2;1163 self.indent -= 2;
1157 try stream.writeByte('\n');1164 try stream.writeByte('\n');
...@@ -2417,7 +2424,10 @@ const EmitZIR = struct {...@@ -2417,7 +2424,10 @@ const EmitZIR = struct {
24172424
2418 .varptr => @panic("TODO"),2425 .varptr => @panic("TODO"),
2419 };2426 };
2420 try self.metadata.put(new_inst, .{ .deaths = inst.deaths });2427 try self.metadata.put(new_inst, .{
2428 .deaths = inst.deaths,
2429 .addr = @ptrToInt(inst),
2430 });
2421 try instructions.append(new_inst);2431 try instructions.append(new_inst);
2422 try inst_table.put(inst, new_inst);2432 try inst_table.put(inst, new_inst);
2423 }2433 }
test/stage2/test.zig+62
...@@ -694,6 +694,68 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -694,6 +694,68 @@ pub fn addCases(ctx: *TestContext) !void {
694 "",694 "",
695 );695 );
696696
697 // Reusing the registers of dead operands playing nicely with conditional branching.
698 case.addCompareOutput(
699 \\export fn _start() noreturn {
700 \\ assert(add(3, 4) == 791);
701 \\ assert(add(4, 3) == 79);
702 \\
703 \\ exit();
704 \\}
705 \\
706 \\fn add(a: u32, b: u32) u32 {
707 \\ const x: u32 = if (a < b) blk: {
708 \\ const c = a + b; // 7
709 \\ const d = a + c; // 10
710 \\ const e = d + b; // 14
711 \\ const f = d + e; // 24
712 \\ const g = e + f; // 38
713 \\ const h = f + g; // 62
714 \\ const i = g + h; // 100
715 \\ const j = i + d; // 110
716 \\ const k = i + j; // 210
717 \\ const l = k + c; // 217
718 \\ const m = l + d; // 227
719 \\ const n = m + e; // 241
720 \\ const o = n + f; // 265
721 \\ const p = o + g; // 303
722 \\ const q = p + h; // 365
723 \\ const r = q + i; // 465
724 \\ const s = r + j; // 575
725 \\ const t = s + k; // 785
726 \\ break :blk t;
727 \\ } else blk: {
728 \\ const t = b + b + a; // 10
729 \\ const c = a + t; // 14
730 \\ const d = c + t; // 24
731 \\ const e = d + t; // 34
732 \\ const f = e + t; // 44
733 \\ const g = f + t; // 54
734 \\ const h = c + g; // 68
735 \\ break :blk h + b; // 71
736 \\ };
737 \\ const y = x + a; // 788, 75
738 \\ const z = y + a; // 791, 79
739 \\ return z;
740 \\}
741 \\
742 \\pub fn assert(ok: bool) void {
743 \\ if (!ok) unreachable; // assertion failure
744 \\}
745 \\
746 \\fn exit() noreturn {
747 \\ asm volatile ("syscall"
748 \\ :
749 \\ : [number] "{rax}" (231),
750 \\ [arg1] "{rdi}" (0)
751 \\ : "rcx", "r11", "memory"
752 \\ );
753 \\ unreachable;
754 \\}
755 ,
756 "",
757 );
758
697 // Character literals and multiline strings.759 // Character literals and multiline strings.
698 case.addCompareOutput(760 case.addCompareOutput(
699 \\export fn _start() noreturn {761 \\export fn _start() noreturn {