mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 12:59:04 +00:00
stage2: fix liveness analysis of Call instructions
This commit is contained in:
parent
5da5ded743
commit
135580c162
@ -14,30 +14,35 @@ pub const Inst = struct {
|
||||
tag: Tag,
|
||||
/// Each bit represents the index of an `Inst` parameter in the `args` field.
|
||||
/// If a bit is set, it marks the end of the lifetime of the corresponding
|
||||
/// instruction parameter. For example, 0b000_00101 means that the first and
|
||||
/// instruction parameter. For example, 0b101 means that the first and
|
||||
/// third `Inst` parameters' lifetimes end after this instruction, and will
|
||||
/// not have any more following references.
|
||||
/// The most significant bit being set means that the instruction itself is
|
||||
/// never referenced, in other words its lifetime ends as soon as it finishes.
|
||||
/// If bit 7 (0b1xxx_xxxx) is set, it means this instruction itself is unreferenced.
|
||||
/// If bit 6 (0bx1xx_xxxx) is set, it means this is a special case and the
|
||||
/// If bit 15 (0b1xxx_xxxx_xxxx_xxxx) is set, it means this instruction itself is unreferenced.
|
||||
/// If bit 14 (0bx1xx_xxxx_xxxx_xxxx) is set, it means this is a special case and the
|
||||
/// lifetimes of operands are encoded elsewhere.
|
||||
deaths: u8 = undefined,
|
||||
deaths: DeathsInt = undefined,
|
||||
ty: Type,
|
||||
/// Byte offset into the source.
|
||||
src: usize,
|
||||
|
||||
pub const DeathsInt = u16;
|
||||
pub const DeathsBitIndex = std.math.Log2Int(DeathsInt);
|
||||
pub const unreferenced_bit_index = @typeInfo(DeathsInt).Int.bits - 1;
|
||||
pub const deaths_bits = unreferenced_bit_index - 1;
|
||||
|
||||
pub fn isUnused(self: Inst) bool {
|
||||
return (self.deaths & 0b1000_0000) != 0;
|
||||
return (self.deaths & (1 << unreferenced_bit_index)) != 0;
|
||||
}
|
||||
|
||||
pub fn operandDies(self: Inst, index: u3) bool {
|
||||
assert(index < 6);
|
||||
pub fn operandDies(self: Inst, index: DeathsBitIndex) bool {
|
||||
assert(index < deaths_bits);
|
||||
return @truncate(u1, self.deaths << index) != 0;
|
||||
}
|
||||
|
||||
pub fn specialOperandDeaths(self: Inst) bool {
|
||||
return (self.deaths & 0b1000_0000) != 0;
|
||||
return (self.deaths & (1 << deaths_bits)) != 0;
|
||||
}
|
||||
|
||||
pub const Tag = enum {
|
||||
|
||||
@ -34,7 +34,7 @@ fn analyzeInstGeneric(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Ins
|
||||
inline for (std.meta.declarations(ir.Inst)) |decl| {
|
||||
switch (decl.data) {
|
||||
.Type => |T| {
|
||||
if (@hasDecl(T, "base_tag")) {
|
||||
if (@typeInfo(T) == .Struct and @hasDecl(T, "base_tag")) {
|
||||
if (T.base_tag == base.tag) {
|
||||
return analyzeInst(arena, table, T, @fieldParentPtr(T, "base", base));
|
||||
}
|
||||
@ -47,7 +47,13 @@ fn analyzeInstGeneric(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Ins
|
||||
}
|
||||
|
||||
fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void), comptime T: type, inst: *T) error{OutOfMemory}!void {
|
||||
inst.base.deaths = 0;
|
||||
if (table.contains(&inst.base)) {
|
||||
inst.base.deaths = 0;
|
||||
} else {
|
||||
// No tombstone for this instruction means it is never referenced,
|
||||
// and its birth marks its own death. Very metal 🤘
|
||||
inst.base.deaths = 1 << ir.Inst.unreferenced_bit_index;
|
||||
}
|
||||
|
||||
switch (T) {
|
||||
ir.Inst.Constant => return,
|
||||
@ -106,15 +112,28 @@ fn analyzeInst(arena: *std.mem.Allocator, table: *std.AutoHashMap(*ir.Inst, void
|
||||
// instruction, and the deaths flag for the CondBr instruction will indicate whether the
|
||||
// condition's lifetime ends immediately before entering any branch.
|
||||
},
|
||||
ir.Inst.Call => {
|
||||
// Call instructions have a runtime-known number of operands so we have to handle them ourselves here.
|
||||
const needed_bits = 1 + inst.args.args.len;
|
||||
if (needed_bits <= ir.Inst.deaths_bits) {
|
||||
var bit_i: ir.Inst.DeathsBitIndex = 0;
|
||||
{
|
||||
const prev = try table.fetchPut(inst.args.func, {});
|
||||
if (prev == null) inst.base.deaths |= @as(ir.Inst.DeathsInt, 1) << bit_i;
|
||||
bit_i += 1;
|
||||
}
|
||||
for (inst.args.args) |arg| {
|
||||
const prev = try table.fetchPut(arg, {});
|
||||
if (prev == null) inst.base.deaths |= @as(ir.Inst.DeathsInt, 1) << bit_i;
|
||||
bit_i += 1;
|
||||
}
|
||||
} else {
|
||||
@panic("Handle liveness analysis for function calls with many parameters");
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
if (!table.contains(&inst.base)) {
|
||||
// No tombstone for this instruction means it is never referenced,
|
||||
// and its birth marks its own death. Very metal 🤘
|
||||
inst.base.deaths |= 1 << 7;
|
||||
}
|
||||
|
||||
const Args = ir.Inst.Args(T);
|
||||
if (Args == void) {
|
||||
return;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user