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