| ... | @@ -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) = .{}, |
| 105 | | 105 | |
| 106 | /// The set of deaths which should be made to occur at the earliest possible point in | 106 | /// The set of instructions currently alive in the current control |
| 107 | /// this control flow branch. These instructions die when they are last referenced in | 107 | /// 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) = .{}, |
| 117 | | 109 | |
| 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 | } |
| 177 | | 168 | |
| 178 | return .{ | 169 | return .{ |
| ... | @@ -870,47 +861,6 @@ fn analyzeBody( | ... | @@ -870,47 +861,6 @@ fn analyzeBody( |
| 870 | } | 861 | } |
| 871 | } | 862 | } |
| 872 | | 863 | |
| 873 | const 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. | | |
| 881 | fn 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 | | | |
| 914 | fn analyzeInst( | 864 | fn 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); |
| 1326 | | 1276 | |
| 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 | }; |
| 1339 | | 1285 | |
| 1340 | var tomb_bits: Bpi = @as(Bpi, @boolToInt(immediate_death)) << (bpi - 1); | 1286 | var tomb_bits: Bpi = @as(Bpi, @boolToInt(immediate_death)) << (bpi - 1); |
| 1341 | | 1287 | |
| ... | @@ -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 | }, |
| 1392 | | 1335 | |
| 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 block | 1360 | const block_scope = data.block_scopes.get(br.block_inst).?; // we should always be breaking from an enclosing block |
| 1429 | | 1361 | |
| 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 | } |
| 1615 | | 1523 | |
| 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 | }, |
| 1676 | | 1584 | |
| 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); |
| 1701 | | 1592 | |
| 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); | | |
| 1709 | | 1596 | |
| 1710 | // Any queued deaths shared between both branches can be queued for us instead | 1597 | // 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. | | |
| 1733 | | 1599 | |
| 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); |
| 1739 | | 1605 | |
| 1740 | // Note: this invalidates `else_info.live_set`, but expands `then_info.live_set` to | 1606 | // 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 faster | 1611 | if (else_live.remove(death)) continue; // removing makes the loop below faster |
| 1747 | if (else_info.branch_deaths.contains(death)) continue; | | |
| 1748 | | 1612 | |
| 1749 | // If this is a `try`, the "then body" (rest of the branch) might have | 1613 | // 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 branch | 1614 | // 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( |
| 1756 | | 1620 | |
| 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 operands | 1623 | // Since we removed common stuff above, `else_live` is now only operands |
| 1760 | // which are *only* alive in the else branch | 1624 | // 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 | } |
| 1771 | | 1633 | |
| ... | @@ -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) }); |
| 1774 | | 1636 | |
| 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 |
| 1777 | | 1639 | |
| 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) }); |
| 1779 | | 1641 | |
| 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); |
| 1840 | | 1693 | |
| 1841 | var case_infos = try gpa.alloc(ControlBranchInfo, ncases + 1); // +1 for else | 1694 | 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); |
| 1843 | | 1696 | |
| 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 | }; | | |
| 1849 | | 1699 | |
| 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 | { // else | 1708 | { // 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 | } |
| 1897 | | 1713 | |
| 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); |
| 1907 | | 1723 | |
| 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 | } |
| 1916 | | 1732 | |
| 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 branch | 1738 | // 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 | } |
| 1937 | | 1753 | |
| 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 { |
| 1997 | | 1804 | |
| 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 yet | 1806 | .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 | }; |
| 2002 | | 1809 | |
| 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 | } |