| 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 | 632 | /// Asserts there is already capacity to insert into top branch inst_table. |
| 633 | 633 | fn processDeath(self: *Self, inst: *ir.Inst) void { |
| 634 | 634 | if (inst.tag == .constant) return; // Constants are immortal. |
| 635 | // When editing this function, note that the logic must synchronize with `reuseOperand`. | |
| 635 | 636 | const prev_value = self.getResolvedInstValue(inst); |
| 636 | 637 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 637 | 638 | branch.inst_table.putAssumeCapacity(inst, .dead); |
| ... | ... | @@ -951,6 +952,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 951 | 952 | // Prevent the operand deaths processing code from deallocating it. |
| 952 | 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 | 959 | return true; |
| 955 | 960 | } |
| 956 | 961 | |
| ... | ... | @@ -1666,7 +1671,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1666 | 1671 | // The instruction is only overridden in the else branch. |
| 1667 | 1672 | var i: usize = self.branch_stack.items.len - 2; |
| 1668 | 1673 | while (true) { |
| 1669 | i -= 1; | |
| 1674 | i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead? | |
| 1670 | 1675 | if (self.branch_stack.items[i].inst_table.get(else_entry.key)) |mcv| { |
| 1671 | 1676 | assert(mcv != .dead); |
| 1672 | 1677 | break :blk mcv; |
src-self-hosted/zir.zig+11-1| ... | ... | @@ -954,6 +954,7 @@ pub const Module = struct { |
| 954 | 954 | |
| 955 | 955 | pub const MetaData = struct { |
| 956 | 956 | deaths: ir.Inst.DeathsInt, |
| 957 | addr: usize, | |
| 957 | 958 | }; |
| 958 | 959 | |
| 959 | 960 | pub const BodyMetaData = struct { |
| ... | ... | @@ -1152,6 +1153,12 @@ const Writer = struct { |
| 1152 | 1153 | try self.writeInstToStream(stream, inst); |
| 1153 | 1154 | if (self.module.metadata.get(inst)) |metadata| { |
| 1154 | 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 | 1163 | self.indent -= 2; |
| 1157 | 1164 | try stream.writeByte('\n'); |
| ... | ... | @@ -2417,7 +2424,10 @@ const EmitZIR = struct { |
| 2417 | 2424 | |
| 2418 | 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 | 2431 | try instructions.append(new_inst); |
| 2422 | 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 | 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 | 759 | // Character literals and multiline strings. |
| 698 | 760 | case.addCompareOutput( |
| 699 | 761 | \\export fn _start() noreturn { |