mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-03-08 02:24:33 +01:00
Liveness: improve logging
This commit is contained in:
parent
42dea36ce9
commit
00d4f3c001
2 changed files with 43 additions and 35 deletions
|
|
@ -176,7 +176,10 @@ pub fn analyze(zcu: *Zcu, air: Air, intern_pool: *InternPool) Allocator.Error!Li
|
|||
data.old_extra = a.extra;
|
||||
a.extra = .{};
|
||||
try analyzeBody(&a, .main_analysis, &data, main_body);
|
||||
assert(data.live_set.count() == 0);
|
||||
if (std.debug.runtime_safety and data.live_set.count() != 0) {
|
||||
log.debug("instructions still in live set after analysis: {f}", .{fmtInstSet(&data.live_set)});
|
||||
@panic("liveness analysis failed");
|
||||
}
|
||||
}
|
||||
|
||||
return .{
|
||||
|
|
@ -825,10 +828,10 @@ fn analyzeOperands(
|
|||
|
||||
// This logic must synchronize with `will_die_immediately` in `AnalyzeBigOperands.init`.
|
||||
const immediate_death = if (data.live_set.remove(inst)) blk: {
|
||||
log.debug("[{}] %{d}: removed from live set", .{ pass, @intFromEnum(inst) });
|
||||
log.debug("[{t}] {f}: removed from live set", .{ pass, inst });
|
||||
break :blk false;
|
||||
} else blk: {
|
||||
log.debug("[{}] %{d}: immediate death", .{ pass, @intFromEnum(inst) });
|
||||
log.debug("[{t}] {f}: immediate death", .{ pass, inst });
|
||||
break :blk true;
|
||||
};
|
||||
|
||||
|
|
@ -849,7 +852,7 @@ fn analyzeOperands(
|
|||
const mask = @as(Bpi, 1) << @as(OperandInt, @intCast(i));
|
||||
|
||||
if ((try data.live_set.fetchPut(gpa, operand, {})) == null) {
|
||||
log.debug("[{}] %{d}: added %{d} to live set (operand dies here)", .{ pass, @intFromEnum(inst), operand });
|
||||
log.debug("[{t}] {f}: added {f} to live set (operand dies here)", .{ pass, inst, operand });
|
||||
tomb_bits |= mask;
|
||||
}
|
||||
}
|
||||
|
|
@ -988,19 +991,19 @@ fn analyzeInstBlock(
|
|||
},
|
||||
|
||||
.main_analysis => {
|
||||
log.debug("[{}] %{f}: block live set is {f}", .{ pass, inst, fmtInstSet(&data.live_set) });
|
||||
log.debug("[{t}] {f}: block live set is {f}", .{ pass, inst, fmtInstSet(&data.live_set) });
|
||||
// We can move the live set because the body should have a noreturn
|
||||
// instruction which overrides the set.
|
||||
try data.block_scopes.put(gpa, inst, .{
|
||||
.live_set = data.live_set.move(),
|
||||
});
|
||||
defer {
|
||||
log.debug("[{}] %{f}: popped block scope", .{ pass, inst });
|
||||
log.debug("[{t}] {f}: popped block scope", .{ pass, inst });
|
||||
var scope = data.block_scopes.fetchRemove(inst).?.value;
|
||||
scope.live_set.deinit(gpa);
|
||||
}
|
||||
|
||||
log.debug("[{}] %{f}: pushed new block scope", .{ pass, inst });
|
||||
log.debug("[{t}] {f}: pushed new block scope", .{ pass, inst });
|
||||
try analyzeBody(a, pass, data, body);
|
||||
|
||||
// If the block is noreturn, block deaths not only aren't useful, they're impossible to
|
||||
|
|
@ -1027,7 +1030,7 @@ fn analyzeInstBlock(
|
|||
}
|
||||
assert(measured_num == num_deaths); // post-live-set should be a subset of pre-live-set
|
||||
try a.special.put(gpa, inst, extra_index);
|
||||
log.debug("[{}] %{f}: block deaths are {f}", .{
|
||||
log.debug("[{t}] {f}: block deaths are {f}", .{
|
||||
pass,
|
||||
inst,
|
||||
fmtInstList(@ptrCast(a.extra.items[extra_index + 1 ..][0..num_deaths])),
|
||||
|
|
@ -1064,7 +1067,7 @@ fn writeLoopInfo(
|
|||
const block_inst = key.*;
|
||||
a.extra.appendAssumeCapacity(@intFromEnum(block_inst));
|
||||
}
|
||||
log.debug("[{}] %{f}: includes breaks to {f}", .{ LivenessPass.loop_analysis, inst, fmtInstSet(&data.breaks) });
|
||||
log.debug("[{t}] {f}: includes breaks to {f}", .{ LivenessPass.loop_analysis, inst, fmtInstSet(&data.breaks) });
|
||||
|
||||
// Now we put the live operands from the loop body in too
|
||||
const num_live = data.live_set.count();
|
||||
|
|
@ -1076,7 +1079,7 @@ fn writeLoopInfo(
|
|||
const alive = key.*;
|
||||
a.extra.appendAssumeCapacity(@intFromEnum(alive));
|
||||
}
|
||||
log.debug("[{}] %{f}: maintain liveness of {f}", .{ LivenessPass.loop_analysis, inst, fmtInstSet(&data.live_set) });
|
||||
log.debug("[{t}] {f}: maintain liveness of {f}", .{ LivenessPass.loop_analysis, inst, fmtInstSet(&data.live_set) });
|
||||
|
||||
try a.special.put(gpa, inst, extra_index);
|
||||
|
||||
|
|
@ -1117,7 +1120,7 @@ fn resolveLoopLiveSet(
|
|||
try data.live_set.ensureUnusedCapacity(gpa, @intCast(loop_live.len));
|
||||
for (loop_live) |alive| data.live_set.putAssumeCapacity(alive, {});
|
||||
|
||||
log.debug("[{}] %{f}: block live set is {f}", .{ LivenessPass.main_analysis, inst, fmtInstSet(&data.live_set) });
|
||||
log.debug("[{t}] {f}: block live set is {f}", .{ LivenessPass.main_analysis, inst, fmtInstSet(&data.live_set) });
|
||||
|
||||
for (breaks) |block_inst| {
|
||||
// We might break to this block, so include every operand that the block needs alive
|
||||
|
|
@ -1130,7 +1133,7 @@ fn resolveLoopLiveSet(
|
|||
}
|
||||
}
|
||||
|
||||
log.debug("[{}] %{f}: loop live set is {f}", .{ LivenessPass.main_analysis, inst, fmtInstSet(&data.live_set) });
|
||||
log.debug("[{t}] {f}: loop live set is {f}", .{ LivenessPass.main_analysis, inst, fmtInstSet(&data.live_set) });
|
||||
}
|
||||
|
||||
fn analyzeInstLoop(
|
||||
|
|
@ -1168,7 +1171,7 @@ fn analyzeInstLoop(
|
|||
.live_set = data.live_set.move(),
|
||||
});
|
||||
defer {
|
||||
log.debug("[{}] %{f}: popped loop block scop", .{ pass, inst });
|
||||
log.debug("[{t}] {f}: popped loop block scop", .{ pass, inst });
|
||||
var scope = data.block_scopes.fetchRemove(inst).?.value;
|
||||
scope.live_set.deinit(gpa);
|
||||
}
|
||||
|
|
@ -1269,13 +1272,13 @@ fn analyzeInstCondBr(
|
|||
}
|
||||
}
|
||||
|
||||
log.debug("[{}] %{f}: 'then' branch mirrored deaths are {f}", .{ pass, inst, fmtInstList(then_mirrored_deaths.items) });
|
||||
log.debug("[{}] %{f}: 'else' branch mirrored deaths are {f}", .{ pass, inst, fmtInstList(else_mirrored_deaths.items) });
|
||||
log.debug("[{t}] {f}: 'then' branch mirrored deaths are {f}", .{ pass, inst, fmtInstList(then_mirrored_deaths.items) });
|
||||
log.debug("[{t}] {f}: 'else' branch mirrored deaths are {f}", .{ pass, inst, fmtInstList(else_mirrored_deaths.items) });
|
||||
|
||||
data.live_set.deinit(gpa);
|
||||
data.live_set = then_live.move(); // Really the union of both live sets
|
||||
|
||||
log.debug("[{}] %{f}: new live set is {f}", .{ pass, inst, fmtInstSet(&data.live_set) });
|
||||
log.debug("[{t}] {f}: new live set is {f}", .{ pass, inst, fmtInstSet(&data.live_set) });
|
||||
|
||||
// Write the mirrored deaths to `extra`
|
||||
const then_death_count = @as(u32, @intCast(then_mirrored_deaths.items.len));
|
||||
|
|
@ -1343,7 +1346,7 @@ fn analyzeInstSwitchBr(
|
|||
});
|
||||
}
|
||||
defer if (is_dispatch_loop) {
|
||||
log.debug("[{}] %{f}: popped loop block scop", .{ pass, inst });
|
||||
log.debug("[{t}] {f}: popped loop block scope", .{ pass, inst });
|
||||
var scope = data.block_scopes.fetchRemove(inst).?.value;
|
||||
scope.live_set.deinit(gpa);
|
||||
};
|
||||
|
|
@ -1401,13 +1404,13 @@ fn analyzeInstSwitchBr(
|
|||
}
|
||||
|
||||
for (mirrored_deaths, 0..) |mirrored, i| {
|
||||
log.debug("[{}] %{f}: case {} mirrored deaths are {f}", .{ pass, inst, i, fmtInstList(mirrored.items) });
|
||||
log.debug("[{t}] {f}: case {} mirrored deaths are {f}", .{ pass, inst, i, fmtInstList(mirrored.items) });
|
||||
}
|
||||
|
||||
data.live_set.deinit(gpa);
|
||||
data.live_set = all_alive.move();
|
||||
|
||||
log.debug("[{}] %{f}: new live set is {f}", .{ pass, inst, fmtInstSet(&data.live_set) });
|
||||
log.debug("[{t}] {f}: new live set is {f}", .{ pass, inst, fmtInstSet(&data.live_set) });
|
||||
}
|
||||
|
||||
const else_death_count = @as(u32, @intCast(mirrored_deaths[ncases].items.len));
|
||||
|
|
@ -1506,7 +1509,7 @@ fn AnalyzeBigOperands(comptime pass: LivenessPass) type {
|
|||
|
||||
.main_analysis => {
|
||||
if ((try big.data.live_set.fetchPut(gpa, operand, {})) == null) {
|
||||
log.debug("[{}] %{f}: added %{f} to live set (operand dies here)", .{ pass, big.inst, operand });
|
||||
log.debug("[{t}] {f}: added {f} to live set (operand dies here)", .{ pass, big.inst, operand });
|
||||
big.extra_tombs[extra_byte] |= @as(u32, 1) << extra_bit;
|
||||
}
|
||||
},
|
||||
|
|
@ -1568,9 +1571,9 @@ const FmtInstSet = struct {
|
|||
return;
|
||||
}
|
||||
var it = val.set.keyIterator();
|
||||
try w.print("%{f}", .{it.next().?.*});
|
||||
try w.print("{f}", .{it.next().?.*});
|
||||
while (it.next()) |key| {
|
||||
try w.print(" %{f}", .{key.*});
|
||||
try w.print(" {f}", .{key.*});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -1587,9 +1590,9 @@ const FmtInstList = struct {
|
|||
try w.writeAll("[no instructions]");
|
||||
return;
|
||||
}
|
||||
try w.print("%{f}", .{val.list[0]});
|
||||
try w.print("{f}", .{val.list[0]});
|
||||
for (val.list[1..]) |inst| {
|
||||
try w.print(" %{f}", .{inst});
|
||||
try w.print(" {f}", .{inst});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
|
|||
.trap, .unreach => {
|
||||
try self.verifyInstOperands(inst, .{ .none, .none, .none });
|
||||
// This instruction terminates the function, so everything should be dead
|
||||
if (self.live.count() > 0) return invalid("%{f}: instructions still alive", .{inst});
|
||||
if (self.live.count() > 0) return invalid("{f}: instructions still alive", .{inst});
|
||||
},
|
||||
|
||||
// unary
|
||||
|
|
@ -166,7 +166,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
|
|||
const un_op = data[@intFromEnum(inst)].un_op;
|
||||
try self.verifyInstOperands(inst, .{ un_op, .none, .none });
|
||||
// This instruction terminates the function, so everything should be dead
|
||||
if (self.live.count() > 0) return invalid("%{f}: instructions still alive", .{inst});
|
||||
if (self.live.count() > 0) return invalid("{f}: instructions still alive", .{inst});
|
||||
},
|
||||
.dbg_var_ptr,
|
||||
.dbg_var_val,
|
||||
|
|
@ -441,7 +441,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
|
|||
.repeat => {
|
||||
const repeat = data[@intFromEnum(inst)].repeat;
|
||||
const expected_live = self.loops.get(repeat.loop_inst) orelse
|
||||
return invalid("%{d}: loop %{d} not in scope", .{ @intFromEnum(inst), @intFromEnum(repeat.loop_inst) });
|
||||
return invalid("{f}: loop {f} not in scope", .{ inst, repeat.loop_inst });
|
||||
|
||||
try self.verifyMatchingLiveness(repeat.loop_inst, expected_live);
|
||||
},
|
||||
|
|
@ -451,7 +451,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
|
|||
try self.verifyOperand(inst, br.operand, self.liveness.operandDies(inst, 0));
|
||||
|
||||
const expected_live = self.loops.get(br.block_inst) orelse
|
||||
return invalid("%{d}: loop %{d} not in scope", .{ @intFromEnum(inst), @intFromEnum(br.block_inst) });
|
||||
return invalid("{f}: loop {f} not in scope", .{ inst, br.block_inst });
|
||||
|
||||
try self.verifyMatchingLiveness(br.block_inst, expected_live);
|
||||
},
|
||||
|
|
@ -487,7 +487,12 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
|
|||
if (ip.isNoReturn(block_ty.toIntern())) {
|
||||
assert(!self.blocks.contains(inst));
|
||||
} else {
|
||||
var live = self.blocks.fetchRemove(inst).?.value;
|
||||
var live = if (self.blocks.fetchRemove(inst)) |kv| kv.value else {
|
||||
return invalid(
|
||||
"{f}: block of type '{f}' not terminated correctly",
|
||||
.{ inst, block_ty.fmtDebug() },
|
||||
);
|
||||
};
|
||||
defer live.deinit(self.gpa);
|
||||
|
||||
try self.verifyMatchingLiveness(inst, live);
|
||||
|
|
@ -502,7 +507,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
|
|||
|
||||
// The same stuff should be alive after the loop as before it.
|
||||
const gop = try self.loops.getOrPut(self.gpa, inst);
|
||||
if (gop.found_existing) return invalid("%{d}: loop already exists", .{@intFromEnum(inst)});
|
||||
if (gop.found_existing) return invalid("{f}: loop already exists", .{inst});
|
||||
defer {
|
||||
var live = self.loops.fetchRemove(inst).?;
|
||||
live.value.deinit(self.gpa);
|
||||
|
|
@ -551,7 +556,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
|
|||
// after the loop as before it.
|
||||
{
|
||||
const gop = try self.loops.getOrPut(self.gpa, inst);
|
||||
if (gop.found_existing) return invalid("%{d}: loop already exists", .{@intFromEnum(inst)});
|
||||
if (gop.found_existing) return invalid("{f}: loop already exists", .{inst});
|
||||
gop.value_ptr.* = self.live.move();
|
||||
}
|
||||
defer {
|
||||
|
|
@ -606,11 +611,11 @@ fn verifyOperand(self: *Verify, inst: Air.Inst.Index, op_ref: Air.Inst.Ref, dies
|
|||
return;
|
||||
};
|
||||
if (dies) {
|
||||
if (!self.live.remove(operand)) return invalid("%{f}: dead operand %{f} reused and killed again", .{
|
||||
if (!self.live.remove(operand)) return invalid("{f}: dead operand {f} reused and killed again", .{
|
||||
inst, operand,
|
||||
});
|
||||
} else {
|
||||
if (!self.live.contains(operand)) return invalid("%{f}: dead operand %{f} reused", .{ inst, operand });
|
||||
if (!self.live.contains(operand)) return invalid("{f}: dead operand {f} reused", .{ inst, operand });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -635,9 +640,9 @@ fn verifyInst(self: *Verify, inst: Air.Inst.Index) Error!void {
|
|||
}
|
||||
|
||||
fn verifyMatchingLiveness(self: *Verify, block: Air.Inst.Index, live: LiveMap) Error!void {
|
||||
if (self.live.count() != live.count()) return invalid("%{f}: different deaths across branches", .{block});
|
||||
if (self.live.count() != live.count()) return invalid("{f}: different deaths across branches", .{block});
|
||||
var live_it = self.live.keyIterator();
|
||||
while (live_it.next()) |live_inst| if (!live.contains(live_inst.*)) return invalid("%{f}: different deaths across branches", .{block});
|
||||
while (live_it.next()) |live_inst| if (!live.contains(live_inst.*)) return invalid("{f}: different deaths across branches", .{block});
|
||||
}
|
||||
|
||||
fn invalid(comptime fmt: []const u8, args: anytype) error{LivenessInvalid} {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue