| ... | @@ -176,7 +176,10 @@ pub fn analyze(zcu: *Zcu, air: Air, intern_pool: *InternPool) Allocator.Error!Li | ... | @@ -176,7 +176,10 @@ pub fn analyze(zcu: *Zcu, air: Air, intern_pool: *InternPool) Allocator.Error!Li |
| 176 | data.old_extra = a.extra; | 176 | data.old_extra = a.extra; |
| 177 | a.extra = .{}; | 177 | a.extra = .{}; |
| 178 | try analyzeBody(&a, .main_analysis, &data, main_body); | 178 | try analyzeBody(&a, .main_analysis, &data, main_body); |
| 179 | assert(data.live_set.count() == 0); | 179 | if (std.debug.runtime_safety and data.live_set.count() != 0) { |
| | 180 | log.debug("instructions still in live set after analysis: {f}", .{fmtInstSet(&data.live_set)}); |
| | 181 | @panic("liveness analysis failed"); |
| | 182 | } |
| 180 | } | 183 | } |
| 181 | | 184 | |
| 182 | return .{ | 185 | return .{ |
| ... | @@ -825,10 +828,10 @@ fn analyzeOperands( | ... | @@ -825,10 +828,10 @@ fn analyzeOperands( |
| 825 | | 828 | |
| 826 | // This logic must synchronize with `will_die_immediately` in `AnalyzeBigOperands.init`. | 829 | // This logic must synchronize with `will_die_immediately` in `AnalyzeBigOperands.init`. |
| 827 | const immediate_death = if (data.live_set.remove(inst)) blk: { | 830 | const immediate_death = if (data.live_set.remove(inst)) blk: { |
| 828 | log.debug("[{}] %{d}: removed from live set", .{ pass, @intFromEnum(inst) }); | 831 | log.debug("[{t}] {f}: removed from live set", .{ pass, inst }); |
| 829 | break :blk false; | 832 | break :blk false; |
| 830 | } else blk: { | 833 | } else blk: { |
| 831 | log.debug("[{}] %{d}: immediate death", .{ pass, @intFromEnum(inst) }); | 834 | log.debug("[{t}] {f}: immediate death", .{ pass, inst }); |
| 832 | break :blk true; | 835 | break :blk true; |
| 833 | }; | 836 | }; |
| 834 | | 837 | |
| ... | @@ -849,7 +852,7 @@ fn analyzeOperands( | ... | @@ -849,7 +852,7 @@ fn analyzeOperands( |
| 849 | const mask = @as(Bpi, 1) << @as(OperandInt, @intCast(i)); | 852 | const mask = @as(Bpi, 1) << @as(OperandInt, @intCast(i)); |
| 850 | | 853 | |
| 851 | if ((try data.live_set.fetchPut(gpa, operand, {})) == null) { | 854 | if ((try data.live_set.fetchPut(gpa, operand, {})) == null) { |
| 852 | log.debug("[{}] %{d}: added %{d} to live set (operand dies here)", .{ pass, @intFromEnum(inst), operand }); | 855 | log.debug("[{t}] {f}: added {f} to live set (operand dies here)", .{ pass, inst, operand }); |
| 853 | tomb_bits |= mask; | 856 | tomb_bits |= mask; |
| 854 | } | 857 | } |
| 855 | } | 858 | } |
| ... | @@ -988,19 +991,19 @@ fn analyzeInstBlock( | ... | @@ -988,19 +991,19 @@ fn analyzeInstBlock( |
| 988 | }, | 991 | }, |
| 989 | | 992 | |
| 990 | .main_analysis => { | 993 | .main_analysis => { |
| 991 | log.debug("[{}] %{f}: block live set is {f}", .{ pass, inst, fmtInstSet(&data.live_set) }); | 994 | log.debug("[{t}] {f}: block live set is {f}", .{ pass, inst, fmtInstSet(&data.live_set) }); |
| 992 | // We can move the live set because the body should have a noreturn | 995 | // We can move the live set because the body should have a noreturn |
| 993 | // instruction which overrides the set. | 996 | // instruction which overrides the set. |
| 994 | try data.block_scopes.put(gpa, inst, .{ | 997 | try data.block_scopes.put(gpa, inst, .{ |
| 995 | .live_set = data.live_set.move(), | 998 | .live_set = data.live_set.move(), |
| 996 | }); | 999 | }); |
| 997 | defer { | 1000 | defer { |
| 998 | log.debug("[{}] %{f}: popped block scope", .{ pass, inst }); | 1001 | log.debug("[{t}] {f}: popped block scope", .{ pass, inst }); |
| 999 | var scope = data.block_scopes.fetchRemove(inst).?.value; | 1002 | var scope = data.block_scopes.fetchRemove(inst).?.value; |
| 1000 | scope.live_set.deinit(gpa); | 1003 | scope.live_set.deinit(gpa); |
| 1001 | } | 1004 | } |
| 1002 | | 1005 | |
| 1003 | log.debug("[{}] %{f}: pushed new block scope", .{ pass, inst }); | 1006 | log.debug("[{t}] {f}: pushed new block scope", .{ pass, inst }); |
| 1004 | try analyzeBody(a, pass, data, body); | 1007 | try analyzeBody(a, pass, data, body); |
| 1005 | | 1008 | |
| 1006 | // If the block is noreturn, block deaths not only aren't useful, they're impossible to | 1009 | // If the block is noreturn, block deaths not only aren't useful, they're impossible to |
| ... | @@ -1027,7 +1030,7 @@ fn analyzeInstBlock( | ... | @@ -1027,7 +1030,7 @@ fn analyzeInstBlock( |
| 1027 | } | 1030 | } |
| 1028 | assert(measured_num == num_deaths); // post-live-set should be a subset of pre-live-set | 1031 | assert(measured_num == num_deaths); // post-live-set should be a subset of pre-live-set |
| 1029 | try a.special.put(gpa, inst, extra_index); | 1032 | try a.special.put(gpa, inst, extra_index); |
| 1030 | log.debug("[{}] %{f}: block deaths are {f}", .{ | 1033 | log.debug("[{t}] {f}: block deaths are {f}", .{ |
| 1031 | pass, | 1034 | pass, |
| 1032 | inst, | 1035 | inst, |
| 1033 | fmtInstList(@ptrCast(a.extra.items[extra_index + 1 ..][0..num_deaths])), | 1036 | fmtInstList(@ptrCast(a.extra.items[extra_index + 1 ..][0..num_deaths])), |
| ... | @@ -1064,7 +1067,7 @@ fn writeLoopInfo( | ... | @@ -1064,7 +1067,7 @@ fn writeLoopInfo( |
| 1064 | const block_inst = key.*; | 1067 | const block_inst = key.*; |
| 1065 | a.extra.appendAssumeCapacity(@intFromEnum(block_inst)); | 1068 | a.extra.appendAssumeCapacity(@intFromEnum(block_inst)); |
| 1066 | } | 1069 | } |
| 1067 | log.debug("[{}] %{f}: includes breaks to {f}", .{ LivenessPass.loop_analysis, inst, fmtInstSet(&data.breaks) }); | 1070 | log.debug("[{t}] {f}: includes breaks to {f}", .{ LivenessPass.loop_analysis, inst, fmtInstSet(&data.breaks) }); |
| 1068 | | 1071 | |
| 1069 | // Now we put the live operands from the loop body in too | 1072 | // Now we put the live operands from the loop body in too |
| 1070 | const num_live = data.live_set.count(); | 1073 | const num_live = data.live_set.count(); |
| ... | @@ -1076,7 +1079,7 @@ fn writeLoopInfo( | ... | @@ -1076,7 +1079,7 @@ fn writeLoopInfo( |
| 1076 | const alive = key.*; | 1079 | const alive = key.*; |
| 1077 | a.extra.appendAssumeCapacity(@intFromEnum(alive)); | 1080 | a.extra.appendAssumeCapacity(@intFromEnum(alive)); |
| 1078 | } | 1081 | } |
| 1079 | log.debug("[{}] %{f}: maintain liveness of {f}", .{ LivenessPass.loop_analysis, inst, fmtInstSet(&data.live_set) }); | 1082 | log.debug("[{t}] {f}: maintain liveness of {f}", .{ LivenessPass.loop_analysis, inst, fmtInstSet(&data.live_set) }); |
| 1080 | | 1083 | |
| 1081 | try a.special.put(gpa, inst, extra_index); | 1084 | try a.special.put(gpa, inst, extra_index); |
| 1082 | | 1085 | |
| ... | @@ -1117,7 +1120,7 @@ fn resolveLoopLiveSet( | ... | @@ -1117,7 +1120,7 @@ fn resolveLoopLiveSet( |
| 1117 | try data.live_set.ensureUnusedCapacity(gpa, @intCast(loop_live.len)); | 1120 | try data.live_set.ensureUnusedCapacity(gpa, @intCast(loop_live.len)); |
| 1118 | for (loop_live) |alive| data.live_set.putAssumeCapacity(alive, {}); | 1121 | for (loop_live) |alive| data.live_set.putAssumeCapacity(alive, {}); |
| 1119 | | 1122 | |
| 1120 | log.debug("[{}] %{f}: block live set is {f}", .{ LivenessPass.main_analysis, inst, fmtInstSet(&data.live_set) }); | 1123 | log.debug("[{t}] {f}: block live set is {f}", .{ LivenessPass.main_analysis, inst, fmtInstSet(&data.live_set) }); |
| 1121 | | 1124 | |
| 1122 | for (breaks) |block_inst| { | 1125 | for (breaks) |block_inst| { |
| 1123 | // We might break to this block, so include every operand that the block needs alive | 1126 | // We might break to this block, so include every operand that the block needs alive |
| ... | @@ -1130,7 +1133,7 @@ fn resolveLoopLiveSet( | ... | @@ -1130,7 +1133,7 @@ fn resolveLoopLiveSet( |
| 1130 | } | 1133 | } |
| 1131 | } | 1134 | } |
| 1132 | | 1135 | |
| 1133 | log.debug("[{}] %{f}: loop live set is {f}", .{ LivenessPass.main_analysis, inst, fmtInstSet(&data.live_set) }); | 1136 | log.debug("[{t}] {f}: loop live set is {f}", .{ LivenessPass.main_analysis, inst, fmtInstSet(&data.live_set) }); |
| 1134 | } | 1137 | } |
| 1135 | | 1138 | |
| 1136 | fn analyzeInstLoop( | 1139 | fn analyzeInstLoop( |
| ... | @@ -1168,7 +1171,7 @@ fn analyzeInstLoop( | ... | @@ -1168,7 +1171,7 @@ fn analyzeInstLoop( |
| 1168 | .live_set = data.live_set.move(), | 1171 | .live_set = data.live_set.move(), |
| 1169 | }); | 1172 | }); |
| 1170 | defer { | 1173 | defer { |
| 1171 | log.debug("[{}] %{f}: popped loop block scop", .{ pass, inst }); | 1174 | log.debug("[{t}] {f}: popped loop block scop", .{ pass, inst }); |
| 1172 | var scope = data.block_scopes.fetchRemove(inst).?.value; | 1175 | var scope = data.block_scopes.fetchRemove(inst).?.value; |
| 1173 | scope.live_set.deinit(gpa); | 1176 | scope.live_set.deinit(gpa); |
| 1174 | } | 1177 | } |
| ... | @@ -1269,13 +1272,13 @@ fn analyzeInstCondBr( | ... | @@ -1269,13 +1272,13 @@ fn analyzeInstCondBr( |
| 1269 | } | 1272 | } |
| 1270 | } | 1273 | } |
| 1271 | | 1274 | |
| 1272 | log.debug("[{}] %{f}: 'then' branch mirrored deaths are {f}", .{ pass, inst, fmtInstList(then_mirrored_deaths.items) }); | 1275 | log.debug("[{t}] {f}: 'then' branch mirrored deaths are {f}", .{ pass, inst, fmtInstList(then_mirrored_deaths.items) }); |
| 1273 | log.debug("[{}] %{f}: 'else' branch mirrored deaths are {f}", .{ pass, inst, fmtInstList(else_mirrored_deaths.items) }); | 1276 | log.debug("[{t}] {f}: 'else' branch mirrored deaths are {f}", .{ pass, inst, fmtInstList(else_mirrored_deaths.items) }); |
| 1274 | | 1277 | |
| 1275 | data.live_set.deinit(gpa); | 1278 | data.live_set.deinit(gpa); |
| 1276 | data.live_set = then_live.move(); // Really the union of both live sets | 1279 | data.live_set = then_live.move(); // Really the union of both live sets |
| 1277 | | 1280 | |
| 1278 | log.debug("[{}] %{f}: new live set is {f}", .{ pass, inst, fmtInstSet(&data.live_set) }); | 1281 | log.debug("[{t}] {f}: new live set is {f}", .{ pass, inst, fmtInstSet(&data.live_set) }); |
| 1279 | | 1282 | |
| 1280 | // Write the mirrored deaths to `extra` | 1283 | // Write the mirrored deaths to `extra` |
| 1281 | const then_death_count = @as(u32, @intCast(then_mirrored_deaths.items.len)); | 1284 | const then_death_count = @as(u32, @intCast(then_mirrored_deaths.items.len)); |
| ... | @@ -1343,7 +1346,7 @@ fn analyzeInstSwitchBr( | ... | @@ -1343,7 +1346,7 @@ fn analyzeInstSwitchBr( |
| 1343 | }); | 1346 | }); |
| 1344 | } | 1347 | } |
| 1345 | defer if (is_dispatch_loop) { | 1348 | defer if (is_dispatch_loop) { |
| 1346 | log.debug("[{}] %{f}: popped loop block scop", .{ pass, inst }); | 1349 | log.debug("[{t}] {f}: popped loop block scope", .{ pass, inst }); |
| 1347 | var scope = data.block_scopes.fetchRemove(inst).?.value; | 1350 | var scope = data.block_scopes.fetchRemove(inst).?.value; |
| 1348 | scope.live_set.deinit(gpa); | 1351 | scope.live_set.deinit(gpa); |
| 1349 | }; | 1352 | }; |
| ... | @@ -1401,13 +1404,13 @@ fn analyzeInstSwitchBr( | ... | @@ -1401,13 +1404,13 @@ fn analyzeInstSwitchBr( |
| 1401 | } | 1404 | } |
| 1402 | | 1405 | |
| 1403 | for (mirrored_deaths, 0..) |mirrored, i| { | 1406 | for (mirrored_deaths, 0..) |mirrored, i| { |
| 1404 | log.debug("[{}] %{f}: case {} mirrored deaths are {f}", .{ pass, inst, i, fmtInstList(mirrored.items) }); | 1407 | log.debug("[{t}] {f}: case {} mirrored deaths are {f}", .{ pass, inst, i, fmtInstList(mirrored.items) }); |
| 1405 | } | 1408 | } |
| 1406 | | 1409 | |
| 1407 | data.live_set.deinit(gpa); | 1410 | data.live_set.deinit(gpa); |
| 1408 | data.live_set = all_alive.move(); | 1411 | data.live_set = all_alive.move(); |
| 1409 | | 1412 | |
| 1410 | log.debug("[{}] %{f}: new live set is {f}", .{ pass, inst, fmtInstSet(&data.live_set) }); | 1413 | log.debug("[{t}] {f}: new live set is {f}", .{ pass, inst, fmtInstSet(&data.live_set) }); |
| 1411 | } | 1414 | } |
| 1412 | | 1415 | |
| 1413 | const else_death_count = @as(u32, @intCast(mirrored_deaths[ncases].items.len)); | 1416 | const else_death_count = @as(u32, @intCast(mirrored_deaths[ncases].items.len)); |
| ... | @@ -1506,7 +1509,7 @@ fn AnalyzeBigOperands(comptime pass: LivenessPass) type { | ... | @@ -1506,7 +1509,7 @@ fn AnalyzeBigOperands(comptime pass: LivenessPass) type { |
| 1506 | | 1509 | |
| 1507 | .main_analysis => { | 1510 | .main_analysis => { |
| 1508 | if ((try big.data.live_set.fetchPut(gpa, operand, {})) == null) { | 1511 | if ((try big.data.live_set.fetchPut(gpa, operand, {})) == null) { |
| 1509 | log.debug("[{}] %{f}: added %{f} to live set (operand dies here)", .{ pass, big.inst, operand }); | 1512 | log.debug("[{t}] {f}: added {f} to live set (operand dies here)", .{ pass, big.inst, operand }); |
| 1510 | big.extra_tombs[extra_byte] |= @as(u32, 1) << extra_bit; | 1513 | big.extra_tombs[extra_byte] |= @as(u32, 1) << extra_bit; |
| 1511 | } | 1514 | } |
| 1512 | }, | 1515 | }, |
| ... | @@ -1568,9 +1571,9 @@ const FmtInstSet = struct { | ... | @@ -1568,9 +1571,9 @@ const FmtInstSet = struct { |
| 1568 | return; | 1571 | return; |
| 1569 | } | 1572 | } |
| 1570 | var it = val.set.keyIterator(); | 1573 | var it = val.set.keyIterator(); |
| 1571 | try w.print("%{f}", .{it.next().?.*}); | 1574 | try w.print("{f}", .{it.next().?.*}); |
| 1572 | while (it.next()) |key| { | 1575 | while (it.next()) |key| { |
| 1573 | try w.print(" %{f}", .{key.*}); | 1576 | try w.print(" {f}", .{key.*}); |
| 1574 | } | 1577 | } |
| 1575 | } | 1578 | } |
| 1576 | }; | 1579 | }; |
| ... | @@ -1587,9 +1590,9 @@ const FmtInstList = struct { | ... | @@ -1587,9 +1590,9 @@ const FmtInstList = struct { |
| 1587 | try w.writeAll("[no instructions]"); | 1590 | try w.writeAll("[no instructions]"); |
| 1588 | return; | 1591 | return; |
| 1589 | } | 1592 | } |
| 1590 | try w.print("%{f}", .{val.list[0]}); | 1593 | try w.print("{f}", .{val.list[0]}); |
| 1591 | for (val.list[1..]) |inst| { | 1594 | for (val.list[1..]) |inst| { |
| 1592 | try w.print(" %{f}", .{inst}); | 1595 | try w.print(" {f}", .{inst}); |
| 1593 | } | 1596 | } |
| 1594 | } | 1597 | } |
| 1595 | }; | 1598 | }; |