| ... | ... | @@ -89,8 +89,8 @@ register_manager: RegisterManager = .{}, |
| 89 | 89 | /// Maps offset to what is stored there. |
| 90 | 90 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, |
| 91 | 91 | |
| 92 | | /// Index of the current scope. |
| 93 | | scope_index: u32 = 0, |
| 92 | /// Generation of the current scope, increments by 1 for every entered scope. |
| 93 | scope_generation: u32 = 0, |
| 94 | 94 | |
| 95 | 95 | /// Offset from the stack base, representing the end of the stack frame. |
| 96 | 96 | max_end_stack: u32 = 0, |
| ... | ... | @@ -116,7 +116,7 @@ pub const MCValue = union(enum) { |
| 116 | 116 | /// Control flow will not allow this value to be observed. |
| 117 | 117 | unreach, |
| 118 | 118 | /// No more references to this value remain. |
| 119 | | /// The payload is the value of scope_index at the point where the death occurred |
| 119 | /// The payload is the value of scope_generation at the point where the death occurred |
| 120 | 120 | dead: u32, |
| 121 | 121 | /// The value is undefined. |
| 122 | 122 | undef, |
| ... | ... | @@ -253,9 +253,9 @@ const InstTracking = struct { |
| 253 | 253 | self.short = .{ .register = reg }; |
| 254 | 254 | } |
| 255 | 255 | |
| 256 | | fn resurrect(self: *InstTracking, scope_index: u32) void { |
| 256 | fn resurrect(self: *InstTracking, scope_generation: u32) void { |
| 257 | 257 | switch (self.short) { |
| 258 | | .dead => |die_index| if (die_index >= scope_index) { |
| 258 | .dead => |die_generation| if (die_generation >= scope_generation) { |
| 259 | 259 | self.short = self.long; |
| 260 | 260 | }, |
| 261 | 261 | else => {}, |
| ... | ... | @@ -268,7 +268,7 @@ const InstTracking = struct { |
| 268 | 268 | } |
| 269 | 269 | |
| 270 | 270 | fn reuse(self: *InstTracking, function: *Self) void { |
| 271 | | self.short = .{ .dead = function.scope_index }; |
| 271 | self.short = .{ .dead = function.scope_generation }; |
| 272 | 272 | } |
| 273 | 273 | }; |
| 274 | 274 | |
| ... | ... | @@ -280,10 +280,12 @@ const StackAllocation = struct { |
| 280 | 280 | |
| 281 | 281 | const BlockData = struct { |
| 282 | 282 | relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .{}, |
| 283 | deaths: std.ArrayListUnmanaged(u32) = .{}, // inst_tracking indices |
| 283 | 284 | state: State, |
| 284 | 285 | |
| 285 | 286 | fn deinit(self: *BlockData, gpa: Allocator) void { |
| 286 | 287 | self.relocs.deinit(gpa); |
| 288 | self.deaths.deinit(gpa); |
| 287 | 289 | self.* = undefined; |
| 288 | 290 | } |
| 289 | 291 | }; |
| ... | ... | @@ -1354,30 +1356,25 @@ const State = struct { |
| 1354 | 1356 | registers: RegisterManager.TrackedRegisters, |
| 1355 | 1357 | free_registers: RegisterManager.RegisterBitSet, |
| 1356 | 1358 | inst_tracking_len: u32, |
| 1357 | | scope_index: u32, |
| 1359 | scope_generation: u32, |
| 1358 | 1360 | }; |
| 1359 | 1361 | |
| 1360 | 1362 | fn initRetroactiveState(self: *Self) State { |
| 1361 | 1363 | var state: State = undefined; |
| 1362 | 1364 | state.inst_tracking_len = @intCast(u32, self.inst_tracking.count()); |
| 1363 | | state.scope_index = self.scope_index; |
| 1365 | state.scope_generation = self.scope_generation; |
| 1364 | 1366 | return state; |
| 1365 | 1367 | } |
| 1366 | 1368 | |
| 1367 | | fn saveRetroactiveState(self: *Self, state: *State, comptime hack_around_liveness_bug: bool) !void { |
| 1369 | fn saveRetroactiveState(self: *Self, state: *State) !void { |
| 1368 | 1370 | try self.spillEflagsIfOccupied(); |
| 1369 | 1371 | state.registers = self.register_manager.registers; |
| 1370 | 1372 | state.free_registers = self.register_manager.free_registers; |
| 1371 | | if (hack_around_liveness_bug) for (0..state.registers.len) |index| { |
| 1372 | | if (state.free_registers.isSet(index)) continue; |
| 1373 | | if (self.inst_tracking.getIndex(state.registers[index]).? < state.inst_tracking_len) continue; |
| 1374 | | state.free_registers.set(index); |
| 1375 | | }; |
| 1376 | 1373 | } |
| 1377 | 1374 | |
| 1378 | 1375 | fn saveState(self: *Self) !State { |
| 1379 | 1376 | var state = self.initRetroactiveState(); |
| 1380 | | try self.saveRetroactiveState(&state, false); |
| 1377 | try self.saveRetroactiveState(&state); |
| 1381 | 1378 | return state; |
| 1382 | 1379 | } |
| 1383 | 1380 | |
| ... | ... | @@ -1388,19 +1385,12 @@ fn restoreState(self: *Self, state: State, comptime opts: struct { |
| 1388 | 1385 | close_scope: bool, |
| 1389 | 1386 | }) !void { |
| 1390 | 1387 | if (opts.close_scope) { |
| 1391 | | if (std.debug.runtime_safety) { |
| 1392 | | for (self.inst_tracking.values()[state.inst_tracking_len..]) |tracking| { |
| 1393 | | switch (tracking.short) { |
| 1394 | | .dead, .unreach => {}, |
| 1395 | | else => unreachable, |
| 1396 | | } |
| 1397 | | } |
| 1398 | | } |
| 1388 | for (self.inst_tracking.values()[state.inst_tracking_len..]) |*tracking| tracking.die(self); |
| 1399 | 1389 | self.inst_tracking.shrinkRetainingCapacity(state.inst_tracking_len); |
| 1400 | 1390 | } |
| 1401 | 1391 | |
| 1402 | | if (opts.resurrect) |
| 1403 | | for (self.inst_tracking.values()) |*tracking| tracking.resurrect(state.scope_index); |
| 1392 | if (opts.resurrect) for (self.inst_tracking.values()) |*tracking| |
| 1393 | tracking.resurrect(state.scope_generation); |
| 1404 | 1394 | |
| 1405 | 1395 | for (0..state.registers.len) |index| { |
| 1406 | 1396 | const current_maybe_inst = if (self.register_manager.free_registers.isSet(index)) |
| ... | ... | @@ -6146,7 +6136,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 6146 | 6136 | |
| 6147 | 6137 | const outer_state = try self.saveState(); |
| 6148 | 6138 | { |
| 6149 | | self.scope_index += 1; |
| 6139 | self.scope_generation += 1; |
| 6150 | 6140 | const inner_state = try self.saveState(); |
| 6151 | 6141 | |
| 6152 | 6142 | for (liveness_condbr.then_deaths) |operand| self.processDeath(operand); |
| ... | ... | @@ -6162,6 +6152,12 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 6162 | 6152 | |
| 6163 | 6153 | for (liveness_condbr.else_deaths) |operand| self.processDeath(operand); |
| 6164 | 6154 | try self.genBody(else_body); |
| 6155 | try self.restoreState(inner_state, .{ |
| 6156 | .emit_instructions = false, |
| 6157 | .update_tracking = true, |
| 6158 | .resurrect = true, |
| 6159 | .close_scope = true, |
| 6160 | }); |
| 6165 | 6161 | } |
| 6166 | 6162 | try self.restoreState(outer_state, .{ |
| 6167 | 6163 | .emit_instructions = false, |
| ... | ... | @@ -6473,7 +6469,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { |
| 6473 | 6469 | const body = self.air.extra[loop.end..][0..loop.data.body_len]; |
| 6474 | 6470 | const jmp_target = @intCast(u32, self.mir_instructions.len); |
| 6475 | 6471 | |
| 6476 | | self.scope_index += 1; |
| 6472 | self.scope_generation += 1; |
| 6477 | 6473 | const state = try self.saveState(); |
| 6478 | 6474 | |
| 6479 | 6475 | try self.genBody(body); |
| ... | ... | @@ -6501,7 +6497,7 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { |
| 6501 | 6497 | .short = if (ty.isNoReturn()) .unreach else .none, |
| 6502 | 6498 | }); |
| 6503 | 6499 | |
| 6504 | | self.scope_index += 1; |
| 6500 | self.scope_generation += 1; |
| 6505 | 6501 | try self.blocks.putNoClobber(self.gpa, inst, .{ .state = self.initRetroactiveState() }); |
| 6506 | 6502 | defer { |
| 6507 | 6503 | var block_data = self.blocks.fetchRemove(inst).?.value; |
| ... | ... | @@ -6515,6 +6511,7 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { |
| 6515 | 6511 | |
| 6516 | 6512 | const tracking = self.inst_tracking.getPtr(inst).?; |
| 6517 | 6513 | const block_data = self.blocks.getPtr(inst).?; |
| 6514 | for (block_data.deaths.items) |tracking_index| self.inst_tracking.values()[tracking_index].die(self); |
| 6518 | 6515 | if (tracking.short != .unreach) try self.restoreState(block_data.state, .{ |
| 6519 | 6516 | .emit_instructions = false, |
| 6520 | 6517 | .update_tracking = true, |
| ... | ... | @@ -6547,7 +6544,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void { |
| 6547 | 6544 | |
| 6548 | 6545 | const outer_state = try self.saveState(); |
| 6549 | 6546 | { |
| 6550 | | self.scope_index += 1; |
| 6547 | self.scope_generation += 1; |
| 6551 | 6548 | const inner_state = try self.saveState(); |
| 6552 | 6549 | |
| 6553 | 6550 | while (case_i < switch_br.data.cases_len) : (case_i += 1) { |
| ... | ... | @@ -6572,13 +6569,12 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void { |
| 6572 | 6569 | for (liveness.deaths[case_i]) |operand| self.processDeath(operand); |
| 6573 | 6570 | |
| 6574 | 6571 | try self.genBody(case_body); |
| 6575 | | if (case_i < switch_br.data.cases_len - 1 or switch_br.data.else_body_len > 0) |
| 6576 | | try self.restoreState(inner_state, .{ |
| 6577 | | .emit_instructions = false, |
| 6578 | | .update_tracking = true, |
| 6579 | | .resurrect = true, |
| 6580 | | .close_scope = true, |
| 6581 | | }); |
| 6572 | try self.restoreState(inner_state, .{ |
| 6573 | .emit_instructions = false, |
| 6574 | .update_tracking = true, |
| 6575 | .resurrect = true, |
| 6576 | .close_scope = true, |
| 6577 | }); |
| 6582 | 6578 | |
| 6583 | 6579 | for (relocs) |reloc| try self.performReloc(reloc); |
| 6584 | 6580 | } |
| ... | ... | @@ -6590,6 +6586,12 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void { |
| 6590 | 6586 | for (liveness.deaths[else_deaths]) |operand| self.processDeath(operand); |
| 6591 | 6587 | |
| 6592 | 6588 | try self.genBody(else_body); |
| 6589 | try self.restoreState(inner_state, .{ |
| 6590 | .emit_instructions = false, |
| 6591 | .update_tracking = true, |
| 6592 | .resurrect = true, |
| 6593 | .close_scope = true, |
| 6594 | }); |
| 6593 | 6595 | } |
| 6594 | 6596 | } |
| 6595 | 6597 | try self.restoreState(outer_state, .{ |
| ... | ... | @@ -6631,6 +6633,19 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void { |
| 6631 | 6633 | const block_tracking = self.inst_tracking.getPtr(br.block_inst).?; |
| 6632 | 6634 | const block_data = self.blocks.getPtr(br.block_inst).?; |
| 6633 | 6635 | if (block_tracking.long == .unreach) { |
| 6636 | // .unreach is used to mean that we are the first branch |
| 6637 | |
| 6638 | // We need to compute a list of deaths for later. This list needs to include |
| 6639 | // instructions that was born before, and has died since, the target block. |
| 6640 | for (self.inst_tracking.values()[0..block_data.state.inst_tracking_len], 0..) | |
| 6641 | *tracking, |
| 6642 | tracked_index, |
| 6643 | | switch (tracking.short) { |
| 6644 | .dead => |die_generation| if (die_generation >= block_data.state.scope_generation) |
| 6645 | try block_data.deaths.append(self.gpa, @intCast(u32, tracked_index)), |
| 6646 | else => {}, |
| 6647 | }; |
| 6648 | |
| 6634 | 6649 | const result = result: { |
| 6635 | 6650 | if (block_unused) break :result .none; |
| 6636 | 6651 | if (self.reuseOperand(inst, br.operand, 0, src_mcv)) break :result src_mcv; |
| ... | ... | @@ -6640,7 +6655,7 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void { |
| 6640 | 6655 | break :result new_mcv; |
| 6641 | 6656 | }; |
| 6642 | 6657 | block_tracking.* = InstTracking.init(result); |
| 6643 | | try self.saveRetroactiveState(&block_data.state, true); |
| 6658 | try self.saveRetroactiveState(&block_data.state); |
| 6644 | 6659 | self.freeValue(result); |
| 6645 | 6660 | } else { |
| 6646 | 6661 | if (!block_unused) try self.setRegOrMem(block_ty, block_tracking.short, src_mcv); |