authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-13 23:48:54-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-13 23:48:54-07:00
log135580c1621513e7cfeed8098c087d1d6941fa97
tree1a27c301ae4f98002cb9a54ff6d28d5f772ca742
parent5da5ded74326fce0ca52c6cb00f22824f7beb7eb

stage2: fix liveness analysis of Call instructions


2 files changed, 40 insertions(+), 16 deletions(-)

src-self-hosted/ir.zig+13-8
...@@ -14,30 +14,35 @@ pub const Inst = struct {...@@ -14,30 +14,35 @@ pub const Inst = struct {
14 tag: Tag,14 tag: Tag,
15 /// Each bit represents the index of an `Inst` parameter in the `args` field.15 /// Each bit represents the index of an `Inst` parameter in the `args` field.
16 /// If a bit is set, it marks the end of the lifetime of the corresponding16 /// If a bit is set, it marks the end of the lifetime of the corresponding
17 /// instruction parameter. For example, 0b000_00101 means that the first and17 /// instruction parameter. For example, 0b101 means that the first and
18 /// third `Inst` parameters' lifetimes end after this instruction, and will18 /// third `Inst` parameters' lifetimes end after this instruction, and will
19 /// not have any more following references.19 /// not have any more following references.
20 /// The most significant bit being set means that the instruction itself is20 /// The most significant bit being set means that the instruction itself is
21 /// never referenced, in other words its lifetime ends as soon as it finishes.21 /// never referenced, in other words its lifetime ends as soon as it finishes.
22 /// If bit 7 (0b1xxx_xxxx) is set, it means this instruction itself is unreferenced.22 /// If bit 15 (0b1xxx_xxxx_xxxx_xxxx) is set, it means this instruction itself is unreferenced.
23 /// If bit 6 (0bx1xx_xxxx) is set, it means this is a special case and the23 /// If bit 14 (0bx1xx_xxxx_xxxx_xxxx) is set, it means this is a special case and the
24 /// lifetimes of operands are encoded elsewhere.24 /// lifetimes of operands are encoded elsewhere.
25 deaths: u8 = undefined,25 deaths: DeathsInt = undefined,
26 ty: Type,26 ty: Type,
27 /// Byte offset into the source.27 /// Byte offset into the source.
28 src: usize,28 src: usize,
2929
30 pub const DeathsInt = u16;
31 pub const DeathsBitIndex = std.math.Log2Int(DeathsInt);
32 pub const unreferenced_bit_index = @typeInfo(DeathsInt).Int.bits - 1;
33 pub const deaths_bits = unreferenced_bit_index - 1;
34
30 pub fn isUnused(self: Inst) bool {35 pub fn isUnused(self: Inst) bool {
31 return (self.deaths & 0b1000_0000) != 0;36 return (self.deaths & (1 << unreferenced_bit_index)) != 0;
32 }37 }
3338
34 pub fn operandDies(self: Inst, index: u3) bool {39 pub fn operandDies(self: Inst, index: DeathsBitIndex) bool {
35 assert(index < 6);40 assert(index < deaths_bits);
36 return @truncate(u1, self.deaths << index) != 0;41 return @truncate(u1, self.deaths << index) != 0;
37 }42 }
3843
39 pub fn specialOperandDeaths(self: Inst) bool {44 pub fn specialOperandDeaths(self: Inst) bool {
40 return (self.deaths & 0b1000_0000) != 0;45 return (self.deaths & (1 << deaths_bits)) != 0;
41 }46 }
4247
43 pub const Tag = enum {48 pub const Tag = enum {
src-self-hosted/liveness.zig+27-8
...@@ -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}
4848
49fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void), comptime T: type, inst: *T) error{OutOfMemory}!void {49fn 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 }
5157
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 the112 // 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 }
111136
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;