| author | |
| committer | |
| log | 0c5faa61aebca4215683d233dd52bf3a7a5d1db6 |
| tree | 2ed6e829c2a74c5bb88384b420e11299eed6927f |
| parent | 237d9a105d5eec82aefc63da8e844c47d8990eea |
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); |
| 953 | 954 | ||
| 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 | } |
| 956 | 961 | ||
| ... | @@ -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 { |
| 954 | 954 | ||
| 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 | }; |
| 958 | 959 | ||
| 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 { |
| 2417 | 2424 | ||
| 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 | ); |
| 696 | 696 | ||
| 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 { |