authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-05-11 11:42:55+01:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2023-05-12 00:01:52+03:00
log214f2fc395da2e6e5ffa592b89ea20db47d3fca6
tree781db1654157a66fc0e3841011a34f434e33b80a
parent6c41d435194d1777e1a2e50879c8d293a1f1e65f

Liveness: simplify logic

`branch_deaths` was a relic from before I had a full understanding of AIR's control flow structure, and so was unnecessary. This change simplifies Liveness, fixes a bug exposed by #15235, and likely improves performance (due to cloning hashmaps less often).

1 files changed, 45 insertions(+), 241 deletions(-)

src/Liveness.zig+45-241
...@@ -103,16 +103,8 @@ fn LivenessPassData(comptime pass: LivenessPass) type {...@@ -103,16 +103,8 @@ fn LivenessPassData(comptime pass: LivenessPass) type {
103 /// Every `block` currently under analysis.103 /// Every `block` currently under analysis.
104 block_scopes: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockScope) = .{},104 block_scopes: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockScope) = .{},
105105
106 /// The set of deaths which should be made to occur at the earliest possible point in106 /// The set of instructions currently alive in the current control
107 /// this control flow branch. These instructions die when they are last referenced in107 /// flow branch.
108 /// the current branch; if unreferenced, they die at the start of the branch. Populated
109 /// when a `br` instruction is reached. If deaths are common to all branches of control
110 /// flow, they may be bubbled up to the parent branch.
111 branch_deaths: std.AutoHashMapUnmanaged(Air.Inst.Index, void) = .{},
112
113 /// The set of instructions currently alive. Instructions which must die in this branch
114 /// (i.e. those in `branch_deaths`) are not in this set, because they must die before
115 /// this point.
116 live_set: std.AutoHashMapUnmanaged(Air.Inst.Index, void) = .{},108 live_set: std.AutoHashMapUnmanaged(Air.Inst.Index, void) = .{},
117109
118 /// The extra data initialized by the `loop_analysis` pass for this pass to consume.110 /// The extra data initialized by the `loop_analysis` pass for this pass to consume.
...@@ -130,7 +122,6 @@ fn LivenessPassData(comptime pass: LivenessPass) type {...@@ -130,7 +122,6 @@ fn LivenessPassData(comptime pass: LivenessPass) type {
130 block.live_set.deinit(gpa);122 block.live_set.deinit(gpa);
131 }123 }
132 self.block_scopes.deinit(gpa);124 self.block_scopes.deinit(gpa);
133 self.branch_deaths.deinit(gpa);
134 self.live_set.deinit(gpa);125 self.live_set.deinit(gpa);
135 self.old_extra.deinit(gpa);126 self.old_extra.deinit(gpa);
136 }127 }
...@@ -172,7 +163,7 @@ pub fn analyze(gpa: Allocator, air: Air) Allocator.Error!Liveness {...@@ -172,7 +163,7 @@ pub fn analyze(gpa: Allocator, air: Air) Allocator.Error!Liveness {
172 data.old_extra = a.extra;163 data.old_extra = a.extra;
173 a.extra = .{};164 a.extra = .{};
174 try analyzeBody(&a, .main_analysis, &data, main_body);165 try analyzeBody(&a, .main_analysis, &data, main_body);
175 assert(data.branch_deaths.count() == 0);166 assert(data.live_set.count() == 0);
176 }167 }
177168
178 return .{169 return .{
...@@ -870,47 +861,6 @@ fn analyzeBody(...@@ -870,47 +861,6 @@ fn analyzeBody(
870 }861 }
871}862}
872863
873const ControlBranchInfo = struct {
874 branch_deaths: std.AutoHashMapUnmanaged(Air.Inst.Index, void) = .{},
875 live_set: std.AutoHashMapUnmanaged(Air.Inst.Index, void) = .{},
876};
877
878/// Helper function for running `analyzeBody`, but resetting `branch_deaths` and `live_set` to their
879/// original states before returning, returning the modified versions of them. Only makes sense in
880/// the `main_analysis` pass.
881fn analyzeBodyResetBranch(
882 a: *Analysis,
883 comptime pass: LivenessPass,
884 data: *LivenessPassData(pass),
885 body: []const Air.Inst.Index,
886) !ControlBranchInfo {
887 switch (pass) {
888 .main_analysis => {},
889 else => @compileError("Liveness.analyzeBodyResetBranch only makes sense in LivenessPass.main_analysis"),
890 }
891
892 const gpa = a.gpa;
893
894 const old_branch_deaths = try data.branch_deaths.clone(a.gpa);
895 defer {
896 data.branch_deaths.deinit(gpa);
897 data.branch_deaths = old_branch_deaths;
898 }
899
900 const old_live_set = try data.live_set.clone(a.gpa);
901 defer {
902 data.live_set.deinit(gpa);
903 data.live_set = old_live_set;
904 }
905
906 try analyzeBody(a, pass, data, body);
907
908 return .{
909 .branch_deaths = data.branch_deaths.move(),
910 .live_set = data.live_set.move(),
911 };
912}
913
914fn analyzeInst(864fn analyzeInst(
915 a: *Analysis,865 a: *Analysis,
916 comptime pass: LivenessPass,866 comptime pass: LivenessPass,
...@@ -1325,17 +1275,13 @@ fn analyzeOperands(...@@ -1325,17 +1275,13 @@ fn analyzeOperands(
1325 const usize_index = (inst * bpi) / @bitSizeOf(usize);1275 const usize_index = (inst * bpi) / @bitSizeOf(usize);
13261276
1327 // This logic must synchronize with `will_die_immediately` in `AnalyzeBigOperands.init`.1277 // This logic must synchronize with `will_die_immediately` in `AnalyzeBigOperands.init`.
1328 var immediate_death = false;1278 const immediate_death = if (data.live_set.remove(inst)) blk: {
1329 if (data.branch_deaths.remove(inst)) {
1330 log.debug("[{}] %{}: resolved branch death to birth (immediate death)", .{ pass, inst });
1331 immediate_death = true;
1332 assert(!data.live_set.contains(inst));
1333 } else if (data.live_set.remove(inst)) {
1334 log.debug("[{}] %{}: removed from live set", .{ pass, inst });1279 log.debug("[{}] %{}: removed from live set", .{ pass, inst });
1335 } else {1280 break :blk false;
1281 } else blk: {
1336 log.debug("[{}] %{}: immediate death", .{ pass, inst });1282 log.debug("[{}] %{}: immediate death", .{ pass, inst });
1337 immediate_death = true;1283 break :blk true;
1338 }1284 };
13391285
1340 var tomb_bits: Bpi = @as(Bpi, @boolToInt(immediate_death)) << (bpi - 1);1286 var tomb_bits: Bpi = @as(Bpi, @boolToInt(immediate_death)) << (bpi - 1);
13411287
...@@ -1362,9 +1308,6 @@ fn analyzeOperands(...@@ -1362,9 +1308,6 @@ fn analyzeOperands(
1362 if ((try data.live_set.fetchPut(gpa, operand, {})) == null) {1308 if ((try data.live_set.fetchPut(gpa, operand, {})) == null) {
1363 log.debug("[{}] %{}: added %{} to live set (operand dies here)", .{ pass, inst, operand });1309 log.debug("[{}] %{}: added %{} to live set (operand dies here)", .{ pass, inst, operand });
1364 tomb_bits |= mask;1310 tomb_bits |= mask;
1365 if (data.branch_deaths.remove(operand)) {
1366 log.debug("[{}] %{}: resolved branch death of %{} to this usage", .{ pass, inst, operand });
1367 }
1368 }1311 }
1369 }1312 }
1370 }1313 }
...@@ -1391,17 +1334,6 @@ fn analyzeFuncEnd(...@@ -1391,17 +1334,6 @@ fn analyzeFuncEnd(
1391 },1334 },
13921335
1393 .main_analysis => {1336 .main_analysis => {
1394 const gpa = a.gpa;
1395
1396 // Note that we preserve previous branch deaths - anything that needs to die in our
1397 // "parent" branch also needs to die for us.
1398
1399 try data.branch_deaths.ensureUnusedCapacity(gpa, data.live_set.count());
1400 var it = data.live_set.keyIterator();
1401 while (it.next()) |key| {
1402 const alive = key.*;
1403 data.branch_deaths.putAssumeCapacity(alive, {});
1404 }
1405 data.live_set.clearRetainingCapacity();1337 data.live_set.clearRetainingCapacity();
1406 },1338 },
1407 }1339 }
...@@ -1427,26 +1359,6 @@ fn analyzeInstBr(...@@ -1427,26 +1359,6 @@ fn analyzeInstBr(
1427 .main_analysis => {1359 .main_analysis => {
1428 const block_scope = data.block_scopes.get(br.block_inst).?; // we should always be breaking from an enclosing block1360 const block_scope = data.block_scopes.get(br.block_inst).?; // we should always be breaking from an enclosing block
14291361
1430 // We mostly preserve previous branch deaths - anything that should die for our
1431 // enclosing branch should die for us too. However, if our break target requires such an
1432 // operand to be alive, it's actually not something we want to kill, since its "last
1433 // use" (i.e. the point at which it should die) is outside of our scope.
1434 var it = block_scope.live_set.keyIterator();
1435 while (it.next()) |key| {
1436 const alive = key.*;
1437 _ = data.branch_deaths.remove(alive);
1438 }
1439 log.debug("[{}] %{}: preserved branch deaths are {}", .{ pass, inst, fmtInstSet(&data.branch_deaths) });
1440
1441 // Anything that's currently alive but our target doesn't need becomes a branch death.
1442 it = data.live_set.keyIterator();
1443 while (it.next()) |key| {
1444 const alive = key.*;
1445 if (!block_scope.live_set.contains(alive)) {
1446 _ = try data.branch_deaths.put(gpa, alive, {});
1447 log.debug("[{}] %{}: added branch death of {}", .{ pass, inst, alive });
1448 }
1449 }
1450 const new_live_set = try block_scope.live_set.clone(gpa);1362 const new_live_set = try block_scope.live_set.clone(gpa);
1451 data.live_set.deinit(gpa);1363 data.live_set.deinit(gpa);
1452 data.live_set = new_live_set;1364 data.live_set = new_live_set;
...@@ -1607,10 +1519,6 @@ fn analyzeInstLoop(...@@ -1607,10 +1519,6 @@ fn analyzeInstLoop(
1607 try data.live_set.ensureUnusedCapacity(gpa, @intCast(u32, loop_live.len));1519 try data.live_set.ensureUnusedCapacity(gpa, @intCast(u32, loop_live.len));
1608 for (loop_live) |alive| {1520 for (loop_live) |alive| {
1609 data.live_set.putAssumeCapacity(alive, {});1521 data.live_set.putAssumeCapacity(alive, {});
1610 // If the loop requires a branch death operand to be alive, it's not something we
1611 // want to kill: its "last use" (i.e. the point at which it should die) is the loop
1612 // body itself.
1613 _ = data.branch_deaths.remove(alive);
1614 }1522 }
16151523
1616 log.debug("[{}] %{}: block live set is {}", .{ pass, inst, fmtInstSet(&data.live_set) });1524 log.debug("[{}] %{}: block live set is {}", .{ pass, inst, fmtInstSet(&data.live_set) });
...@@ -1675,61 +1583,19 @@ fn analyzeInstCondBr(...@@ -1675,61 +1583,19 @@ fn analyzeInstCondBr(
1675 },1583 },
16761584
1677 .main_analysis => {1585 .main_analysis => {
1678 var then_info: ControlBranchInfo = switch (inst_type) {
1679 .cond_br => try analyzeBodyResetBranch(a, pass, data, then_body),
1680 .@"try", .try_ptr => blk: {
1681 var branch_deaths = try data.branch_deaths.clone(gpa);
1682 errdefer branch_deaths.deinit(gpa);
1683 var live_set = try data.live_set.clone(gpa);
1684 errdefer live_set.deinit(gpa);
1685 break :blk .{
1686 .branch_deaths = branch_deaths,
1687 .live_set = live_set,
1688 };
1689 },
1690 };
1691 defer then_info.branch_deaths.deinit(gpa);
1692 defer then_info.live_set.deinit(gpa);
1693
1694 // If this is a `try`, the "then body" (rest of the branch) might have referenced our
1695 // result. If so, we want to avoid this value being considered live while analyzing the
1696 // else branch.
1697 switch (inst_type) {1586 switch (inst_type) {
1698 .cond_br => {},1587 .cond_br => try analyzeBody(a, pass, data, then_body),
1699 .@"try", .try_ptr => _ = data.live_set.remove(inst),1588 .@"try", .try_ptr => {}, // The "then body" is just the remainder of this block
1700 }1589 }
1590 var then_live = data.live_set.move();
1591 defer then_live.deinit(gpa);
17011592
1702 try analyzeBody(a, pass, data, else_body);1593 try analyzeBody(a, pass, data, else_body);
1703 var else_info: ControlBranchInfo = .{1594 var else_live = data.live_set.move();
1704 .branch_deaths = data.branch_deaths.move(),1595 defer else_live.deinit(gpa);
1705 .live_set = data.live_set.move(),
1706 };
1707 defer else_info.branch_deaths.deinit(gpa);
1708 defer else_info.live_set.deinit(gpa);
17091596
1710 // Any queued deaths shared between both branches can be queued for us instead1597 // Operands which are alive in one branch but not the other need to die at the start of
1711 {1598 // the peer branch.
1712 var it = then_info.branch_deaths.keyIterator();
1713 while (it.next()) |key| {
1714 const death = key.*;
1715 if (else_info.branch_deaths.remove(death)) {
1716 // We'll remove it from then_deaths below
1717 try data.branch_deaths.put(gpa, death, {});
1718 }
1719 }
1720 log.debug("[{}] %{}: bubbled deaths {}", .{ pass, inst, fmtInstSet(&data.branch_deaths) });
1721 it = data.branch_deaths.keyIterator();
1722 while (it.next()) |key| {
1723 const death = key.*;
1724 assert(then_info.branch_deaths.remove(death));
1725 }
1726 }
1727
1728 log.debug("[{}] %{}: remaining 'then' branch deaths are {}", .{ pass, inst, fmtInstSet(&then_info.branch_deaths) });
1729 log.debug("[{}] %{}: remaining 'else' branch deaths are {}", .{ pass, inst, fmtInstSet(&else_info.branch_deaths) });
1730
1731 // Deaths that occur in one branch but not another need to be made to occur at the start
1732 // of the other branch.
17331599
1734 var then_mirrored_deaths: std.ArrayListUnmanaged(Air.Inst.Index) = .{};1600 var then_mirrored_deaths: std.ArrayListUnmanaged(Air.Inst.Index) = .{};
1735 defer then_mirrored_deaths.deinit(gpa);1601 defer then_mirrored_deaths.deinit(gpa);
...@@ -1737,14 +1603,12 @@ fn analyzeInstCondBr(...@@ -1737,14 +1603,12 @@ fn analyzeInstCondBr(
1737 var else_mirrored_deaths: std.ArrayListUnmanaged(Air.Inst.Index) = .{};1603 var else_mirrored_deaths: std.ArrayListUnmanaged(Air.Inst.Index) = .{};
1738 defer else_mirrored_deaths.deinit(gpa);1604 defer else_mirrored_deaths.deinit(gpa);
17391605
1740 // Note: this invalidates `else_info.live_set`, but expands `then_info.live_set` to1606 // Note: this invalidates `else_live`, but expands `then_live` to be their union
1741 // be their union
1742 {1607 {
1743 var it = then_info.live_set.keyIterator();1608 var it = then_live.keyIterator();
1744 while (it.next()) |key| {1609 while (it.next()) |key| {
1745 const death = key.*;1610 const death = key.*;
1746 if (else_info.live_set.remove(death)) continue; // removing makes the loop below faster1611 if (else_live.remove(death)) continue; // removing makes the loop below faster
1747 if (else_info.branch_deaths.contains(death)) continue;
17481612
1749 // If this is a `try`, the "then body" (rest of the branch) might have1613 // If this is a `try`, the "then body" (rest of the branch) might have
1750 // referenced our result. We want to avoid killing this value in the else branch1614 // referenced our result. We want to avoid killing this value in the else branch
...@@ -1756,16 +1620,14 @@ fn analyzeInstCondBr(...@@ -1756,16 +1620,14 @@ fn analyzeInstCondBr(
17561620
1757 try else_mirrored_deaths.append(gpa, death);1621 try else_mirrored_deaths.append(gpa, death);
1758 }1622 }
1759 // Since we removed common stuff above, `else_info.live_set` is now only operands1623 // Since we removed common stuff above, `else_live` is now only operands
1760 // which are *only* alive in the else branch1624 // which are *only* alive in the else branch
1761 it = else_info.live_set.keyIterator();1625 it = else_live.keyIterator();
1762 while (it.next()) |key| {1626 while (it.next()) |key| {
1763 const death = key.*;1627 const death = key.*;
1764 if (!then_info.branch_deaths.contains(death)) {1628 try then_mirrored_deaths.append(gpa, death);
1765 try then_mirrored_deaths.append(gpa, death);1629 // Make `then_live` contain the full live set (i.e. union of both)
1766 }1630 try then_live.put(gpa, death, {});
1767 // Make `then_info.live_set` contain the full live set (i.e. union of both)
1768 try then_info.live_set.put(gpa, death, {});
1769 }1631 }
1770 }1632 }
17711633
...@@ -1773,29 +1635,20 @@ fn analyzeInstCondBr(...@@ -1773,29 +1635,20 @@ fn analyzeInstCondBr(
1773 log.debug("[{}] %{}: 'else' branch mirrored deaths are {}", .{ pass, inst, fmtInstList(else_mirrored_deaths.items) });1635 log.debug("[{}] %{}: 'else' branch mirrored deaths are {}", .{ pass, inst, fmtInstList(else_mirrored_deaths.items) });
17741636
1775 data.live_set.deinit(gpa);1637 data.live_set.deinit(gpa);
1776 data.live_set = then_info.live_set.move();1638 data.live_set = then_live.move(); // Really the union of both live sets
17771639
1778 log.debug("[{}] %{}: new live set is {}", .{ pass, inst, fmtInstSet(&data.live_set) });1640 log.debug("[{}] %{}: new live set is {}", .{ pass, inst, fmtInstSet(&data.live_set) });
17791641
1780 // Write the branch deaths to `extra`1642 // Write the mirrored deaths to `extra`
1781 const then_death_count = then_info.branch_deaths.count() + @intCast(u32, then_mirrored_deaths.items.len);1643 const then_death_count = @intCast(u32, then_mirrored_deaths.items.len);
1782 const else_death_count = else_info.branch_deaths.count() + @intCast(u32, else_mirrored_deaths.items.len);1644 const else_death_count = @intCast(u32, else_mirrored_deaths.items.len);
1783
1784 try a.extra.ensureUnusedCapacity(gpa, std.meta.fields(CondBr).len + then_death_count + else_death_count);1645 try a.extra.ensureUnusedCapacity(gpa, std.meta.fields(CondBr).len + then_death_count + else_death_count);
1785 const extra_index = a.addExtraAssumeCapacity(CondBr{1646 const extra_index = a.addExtraAssumeCapacity(CondBr{
1786 .then_death_count = then_death_count,1647 .then_death_count = then_death_count,
1787 .else_death_count = else_death_count,1648 .else_death_count = else_death_count,
1788 });1649 });
1789 a.extra.appendSliceAssumeCapacity(then_mirrored_deaths.items);1650 a.extra.appendSliceAssumeCapacity(then_mirrored_deaths.items);
1790 {
1791 var it = then_info.branch_deaths.keyIterator();
1792 while (it.next()) |key| a.extra.appendAssumeCapacity(key.*);
1793 }
1794 a.extra.appendSliceAssumeCapacity(else_mirrored_deaths.items);1651 a.extra.appendSliceAssumeCapacity(else_mirrored_deaths.items);
1795 {
1796 var it = else_info.branch_deaths.keyIterator();
1797 while (it.next()) |key| a.extra.appendAssumeCapacity(key.*);
1798 }
1799 try a.special.put(gpa, inst, extra_index);1652 try a.special.put(gpa, inst, extra_index);
1800 },1653 },
1801 }1654 }
...@@ -1838,61 +1691,24 @@ fn analyzeInstSwitchBr(...@@ -1838,61 +1691,24 @@ fn analyzeInstSwitchBr(
1838 const DeathSet = std.AutoHashMapUnmanaged(Air.Inst.Index, void);1691 const DeathSet = std.AutoHashMapUnmanaged(Air.Inst.Index, void);
1839 const DeathList = std.ArrayListUnmanaged(Air.Inst.Index);1692 const DeathList = std.ArrayListUnmanaged(Air.Inst.Index);
18401693
1841 var case_infos = try gpa.alloc(ControlBranchInfo, ncases + 1); // +1 for else1694 var case_live_sets = try gpa.alloc(std.AutoHashMapUnmanaged(Air.Inst.Index, void), ncases + 1); // +1 for else
1842 defer gpa.free(case_infos);1695 defer gpa.free(case_live_sets);
18431696
1844 @memset(case_infos, .{});1697 @memset(case_live_sets, .{});
1845 defer for (case_infos) |*info| {1698 defer for (case_live_sets) |*live_set| live_set.deinit(gpa);
1846 info.branch_deaths.deinit(gpa);
1847 info.live_set.deinit(gpa);
1848 };
18491699
1850 var air_extra_index: usize = switch_br.end;1700 var air_extra_index: usize = switch_br.end;
1851 for (case_infos[0..ncases]) |*info| {1701 for (case_live_sets[0..ncases]) |*live_set| {
1852 const case = a.air.extraData(Air.SwitchBr.Case, air_extra_index);1702 const case = a.air.extraData(Air.SwitchBr.Case, air_extra_index);
1853 const case_body = a.air.extra[case.end + case.data.items_len ..][0..case.data.body_len];1703 const case_body = a.air.extra[case.end + case.data.items_len ..][0..case.data.body_len];
1854 air_extra_index = case.end + case.data.items_len + case_body.len;1704 air_extra_index = case.end + case.data.items_len + case_body.len;
1855 info.* = try analyzeBodyResetBranch(a, pass, data, case_body);1705 try analyzeBody(a, pass, data, case_body);
1706 live_set.* = data.live_set.move();
1856 }1707 }
1857 { // else1708 { // else
1858 const else_body = a.air.extra[air_extra_index..][0..switch_br.data.else_body_len];1709 const else_body = a.air.extra[air_extra_index..][0..switch_br.data.else_body_len];
1859 try analyzeBody(a, pass, data, else_body);1710 try analyzeBody(a, pass, data, else_body);
1860 case_infos[ncases] = .{1711 case_live_sets[ncases] = data.live_set.move();
1861 .branch_deaths = data.branch_deaths.move(),
1862 .live_set = data.live_set.move(),
1863 };
1864 }
1865
1866 // Queued deaths common to all cases can be bubbled up
1867 {
1868 // We can't remove from the set we're iterating over, so we'll store the shared deaths here
1869 // temporarily to remove them
1870 var shared_deaths: DeathSet = .{};
1871 defer shared_deaths.deinit(gpa);
1872
1873 var it = case_infos[0].branch_deaths.keyIterator();
1874 while (it.next()) |key| {
1875 const death = key.*;
1876 for (case_infos[1..]) |*info| {
1877 if (!info.branch_deaths.contains(death)) break;
1878 } else try shared_deaths.put(gpa, death, {});
1879 }
1880
1881 log.debug("[{}] %{}: bubbled deaths {}", .{ pass, inst, fmtInstSet(&shared_deaths) });
1882
1883 try data.branch_deaths.ensureUnusedCapacity(gpa, shared_deaths.count());
1884 it = shared_deaths.keyIterator();
1885 while (it.next()) |key| {
1886 const death = key.*;
1887 data.branch_deaths.putAssumeCapacity(death, {});
1888 for (case_infos) |*info| {
1889 _ = info.branch_deaths.remove(death);
1890 }
1891 }
1892
1893 for (case_infos, 0..) |*info, i| {
1894 log.debug("[{}] %{}: case {} remaining branch deaths are {}", .{ pass, inst, i, fmtInstSet(&info.branch_deaths) });
1895 }
1896 }1712 }
18971713
1898 const mirrored_deaths = try gpa.alloc(DeathList, ncases + 1);1714 const mirrored_deaths = try gpa.alloc(DeathList, ncases + 1);
...@@ -1905,20 +1721,20 @@ fn analyzeInstSwitchBr(...@@ -1905,20 +1721,20 @@ fn analyzeInstSwitchBr(
1905 var all_alive: DeathSet = .{};1721 var all_alive: DeathSet = .{};
1906 defer all_alive.deinit(gpa);1722 defer all_alive.deinit(gpa);
19071723
1908 for (case_infos) |*info| {1724 for (case_live_sets) |*live_set| {
1909 try all_alive.ensureUnusedCapacity(gpa, info.live_set.count());1725 try all_alive.ensureUnusedCapacity(gpa, live_set.count());
1910 var it = info.live_set.keyIterator();1726 var it = live_set.keyIterator();
1911 while (it.next()) |key| {1727 while (it.next()) |key| {
1912 const alive = key.*;1728 const alive = key.*;
1913 all_alive.putAssumeCapacity(alive, {});1729 all_alive.putAssumeCapacity(alive, {});
1914 }1730 }
1915 }1731 }
19161732
1917 for (mirrored_deaths, case_infos) |*mirrored, *info| {1733 for (mirrored_deaths, case_live_sets) |*mirrored, *live_set| {
1918 var it = all_alive.keyIterator();1734 var it = all_alive.keyIterator();
1919 while (it.next()) |key| {1735 while (it.next()) |key| {
1920 const alive = key.*;1736 const alive = key.*;
1921 if (!info.live_set.contains(alive) and !info.branch_deaths.contains(alive)) {1737 if (!live_set.contains(alive)) {
1922 // Should die at the start of this branch1738 // Should die at the start of this branch
1923 try mirrored.append(gpa, alive);1739 try mirrored.append(gpa, alive);
1924 }1740 }
...@@ -1935,27 +1751,18 @@ fn analyzeInstSwitchBr(...@@ -1935,27 +1751,18 @@ fn analyzeInstSwitchBr(
1935 log.debug("[{}] %{}: new live set is {}", .{ pass, inst, fmtInstSet(&data.live_set) });1751 log.debug("[{}] %{}: new live set is {}", .{ pass, inst, fmtInstSet(&data.live_set) });
1936 }1752 }
19371753
1938 const else_death_count = case_infos[ncases].branch_deaths.count() + @intCast(u32, mirrored_deaths[ncases].items.len);1754 const else_death_count = @intCast(u32, mirrored_deaths[ncases].items.len);
1939
1940 const extra_index = try a.addExtra(SwitchBr{1755 const extra_index = try a.addExtra(SwitchBr{
1941 .else_death_count = else_death_count,1756 .else_death_count = else_death_count,
1942 });1757 });
1943 for (mirrored_deaths[0..ncases], case_infos[0..ncases]) |mirrored, info| {1758 for (mirrored_deaths[0..ncases]) |mirrored| {
1944 const num = info.branch_deaths.count() + @intCast(u32, mirrored.items.len);1759 const num = @intCast(u32, mirrored.items.len);
1945 try a.extra.ensureUnusedCapacity(gpa, num + 1);1760 try a.extra.ensureUnusedCapacity(gpa, num + 1);
1946 a.extra.appendAssumeCapacity(num);1761 a.extra.appendAssumeCapacity(num);
1947 a.extra.appendSliceAssumeCapacity(mirrored.items);1762 a.extra.appendSliceAssumeCapacity(mirrored.items);
1948 {
1949 var it = info.branch_deaths.keyIterator();
1950 while (it.next()) |key| a.extra.appendAssumeCapacity(key.*);
1951 }
1952 }1763 }
1953 try a.extra.ensureUnusedCapacity(gpa, else_death_count);1764 try a.extra.ensureUnusedCapacity(gpa, else_death_count);
1954 a.extra.appendSliceAssumeCapacity(mirrored_deaths[ncases].items);1765 a.extra.appendSliceAssumeCapacity(mirrored_deaths[ncases].items);
1955 {
1956 var it = case_infos[ncases].branch_deaths.keyIterator();
1957 while (it.next()) |key| a.extra.appendAssumeCapacity(key.*);
1958 }
1959 try a.special.put(gpa, inst, extra_index);1766 try a.special.put(gpa, inst, extra_index);
1960 },1767 },
1961 }1768 }
...@@ -1997,7 +1804,7 @@ fn AnalyzeBigOperands(comptime pass: LivenessPass) type {...@@ -1997,7 +1804,7 @@ fn AnalyzeBigOperands(comptime pass: LivenessPass) type {
19971804
1998 const will_die_immediately: bool = switch (pass) {1805 const will_die_immediately: bool = switch (pass) {
1999 .loop_analysis => false, // track everything, since we don't have full liveness information yet1806 .loop_analysis => false, // track everything, since we don't have full liveness information yet
2000 .main_analysis => data.branch_deaths.contains(inst) and !data.live_set.contains(inst),1807 .main_analysis => !data.live_set.contains(inst),
2001 };1808 };
20021809
2003 return .{1810 return .{
...@@ -2048,9 +1855,6 @@ fn AnalyzeBigOperands(comptime pass: LivenessPass) type {...@@ -2048,9 +1855,6 @@ fn AnalyzeBigOperands(comptime pass: LivenessPass) type {
2048 if ((try big.data.live_set.fetchPut(gpa, operand, {})) == null) {1855 if ((try big.data.live_set.fetchPut(gpa, operand, {})) == null) {
2049 log.debug("[{}] %{}: added %{} to live set (operand dies here)", .{ pass, big.inst, operand });1856 log.debug("[{}] %{}: added %{} to live set (operand dies here)", .{ pass, big.inst, operand });
2050 big.extra_tombs[extra_byte] |= @as(u32, 1) << extra_bit;1857 big.extra_tombs[extra_byte] |= @as(u32, 1) << extra_bit;
2051 if (big.data.branch_deaths.remove(operand)) {
2052 log.debug("[{}] %{}: resolved branch death of %{} to this usage", .{ pass, big.inst, operand });
2053 }
2054 }1858 }
2055 },1859 },
2056 }1860 }