| ... | @@ -34,7 +34,7 @@ fn analyzeInstGeneric(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Ins | ... | @@ -34,7 +34,7 @@ fn analyzeInstGeneric(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Ins |
| 34 | inline for (std.meta.declarations(ir.Inst)) |decl| { | 34 | inline for (std.meta.declarations(ir.Inst)) |decl| { |
| 35 | switch (decl.data) { | 35 | switch (decl.data) { |
| 36 | .Type => |T| { | 36 | .Type => |T| { |
| 37 | if (@hasDecl(T, "base_tag")) { | 37 | if (@typeInfo(T) == .Struct and @hasDecl(T, "base_tag")) { |
| 38 | if (T.base_tag == base.tag) { | 38 | if (T.base_tag == base.tag) { |
| 39 | return analyzeInst(arena, table, T, @fieldParentPtr(T, "base", base)); | 39 | return analyzeInst(arena, table, T, @fieldParentPtr(T, "base", base)); |
| 40 | } | 40 | } |
| ... | @@ -47,7 +47,13 @@ fn analyzeInstGeneric(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Ins | ... | @@ -47,7 +47,13 @@ fn analyzeInstGeneric(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Ins |
| 47 | } | 47 | } |
| 48 | | 48 | |
| 49 | fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void), comptime T: type, inst: *T) error{OutOfMemory}!void { | 49 | fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void), comptime T: type, inst: *T) error{OutOfMemory}!void { |
| 50 | inst.base.deaths = 0; | 50 | if (table.contains(&inst.base)) { |
| | 51 | inst.base.deaths = 0; |
| | 52 | } else { |
| | 53 | // No tombstone for this instruction means it is never referenced, |
| | 54 | // and its birth marks its own death. Very metal 🤘 |
| | 55 | inst.base.deaths = 1 << ir.Inst.unreferenced_bit_index; |
| | 56 | } |
| 51 | | 57 | |
| 52 | switch (T) { | 58 | switch (T) { |
| 53 | ir.Inst.Constant => return, | 59 | ir.Inst.Constant => return, |
| ... | @@ -106,15 +112,28 @@ fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void | ... | @@ -106,15 +112,28 @@ fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void |
| 106 | // instruction, and the deaths flag for the CondBr instruction will indicate whether the | 112 | // instruction, and the deaths flag for the CondBr instruction will indicate whether the |
| 107 | // condition's lifetime ends immediately before entering any branch. | 113 | // condition's lifetime ends immediately before entering any branch. |
| 108 | }, | 114 | }, |
| | 115 | ir.Inst.Call => { |
| | 116 | // Call instructions have a runtime-known number of operands so we have to handle them ourselves here. |
| | 117 | const needed_bits = 1 + inst.args.args.len; |
| | 118 | if (needed_bits <= ir.Inst.deaths_bits) { |
| | 119 | var bit_i: ir.Inst.DeathsBitIndex = 0; |
| | 120 | { |
| | 121 | const prev = try table.fetchPut(inst.args.func, {}); |
| | 122 | if (prev == null) inst.base.deaths |= @as(ir.Inst.DeathsInt, 1) << bit_i; |
| | 123 | bit_i += 1; |
| | 124 | } |
| | 125 | for (inst.args.args) |arg| { |
| | 126 | const prev = try table.fetchPut(arg, {}); |
| | 127 | if (prev == null) inst.base.deaths |= @as(ir.Inst.DeathsInt, 1) << bit_i; |
| | 128 | bit_i += 1; |
| | 129 | } |
| | 130 | } else { |
| | 131 | @panic("Handle liveness analysis for function calls with many parameters"); |
| | 132 | } |
| | 133 | }, |
| 109 | else => {}, | 134 | else => {}, |
| 110 | } | 135 | } |
| 111 | | 136 | |
| 112 | if (!table.contains(&inst.base)) { | | |
| 113 | // No tombstone for this instruction means it is never referenced, | | |
| 114 | // and its birth marks its own death. Very metal 🤘 | | |
| 115 | inst.base.deaths |= 1 << 7; | | |
| 116 | } | | |
| 117 | | | |
| 118 | const Args = ir.Inst.Args(T); | 137 | const Args = ir.Inst.Args(T); |
| 119 | if (Args == void) { | 138 | if (Args == void) { |
| 120 | return; | 139 | return; |