| ... | ... | @@ -214,11 +214,6 @@ const StackAllocation = struct { |
| 214 | 214 | |
| 215 | 215 | const BlockData = struct { |
| 216 | 216 | relocs: std.ArrayListUnmanaged(Mir.Inst.Index), |
| 217 | | /// The first break instruction encounters `null` here and chooses a |
| 218 | | /// machine code value for the block result, populating this field. |
| 219 | | /// Following break instructions encounter that value and use it for |
| 220 | | /// the location to store their block results. |
| 221 | | mcv: MCValue, |
| 222 | 217 | }; |
| 223 | 218 | |
| 224 | 219 | const BigTomb = struct { |
| ... | ... | @@ -1078,16 +1073,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 1078 | 1073 | var it = self.register_manager.free_registers.iterator(.{ .kind = .unset }); |
| 1079 | 1074 | while (it.next()) |index| { |
| 1080 | 1075 | const tracked_inst = self.register_manager.registers[index]; |
| 1081 | | switch (air_tags[tracked_inst]) { |
| 1082 | | .block => {}, |
| 1083 | | else => assert(RegisterManager.indexOfRegIntoTracked( |
| 1084 | | switch (self.getResolvedInstValue(tracked_inst).?) { |
| 1085 | | .register => |reg| reg, |
| 1086 | | .register_overflow => |ro| ro.reg, |
| 1087 | | else => unreachable, |
| 1088 | | }, |
| 1089 | | ).? == index), |
| 1090 | | } |
| 1076 | const tracked_mcv = self.getResolvedInstValue(tracked_inst).?.*; |
| 1077 | assert(RegisterManager.indexOfRegIntoTracked(switch (tracked_mcv) { |
| 1078 | .register => |reg| reg, |
| 1079 | .register_overflow => |ro| ro.reg, |
| 1080 | else => unreachable, |
| 1081 | }).? == index); |
| 1091 | 1082 | } |
| 1092 | 1083 | } |
| 1093 | 1084 | } |
| ... | ... | @@ -1114,7 +1105,7 @@ fn freeValue(self: *Self, value: MCValue) void { |
| 1114 | 1105 | fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 1115 | 1106 | const air_tags = self.air.instructions.items(.tag); |
| 1116 | 1107 | if (air_tags[inst] == .constant) return; // Constants are immortal. |
| 1117 | | const prev_value = self.getResolvedInstValue(inst) orelse return; |
| 1108 | const prev_value = (self.getResolvedInstValue(inst) orelse return).*; |
| 1118 | 1109 | log.debug("%{d} => {}", .{ inst, MCValue.dead }); |
| 1119 | 1110 | // When editing this function, note that the logic must synchronize with `reuseOperand`. |
| 1120 | 1111 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| ... | ... | @@ -1259,45 +1250,29 @@ fn allocRegOrMemAdvanced(self: *Self, elem_ty: Type, inst: ?Air.Inst.Index, reg_ |
| 1259 | 1250 | } |
| 1260 | 1251 | |
| 1261 | 1252 | const State = struct { |
| 1262 | | next_stack_offset: u32, |
| 1263 | 1253 | registers: abi.RegisterManager.TrackedRegisters, |
| 1264 | 1254 | free_registers: abi.RegisterManager.RegisterBitSet, |
| 1265 | 1255 | eflags_inst: ?Air.Inst.Index, |
| 1266 | | stack: std.AutoHashMapUnmanaged(u32, StackAllocation), |
| 1267 | | |
| 1268 | | fn deinit(state: *State, gpa: Allocator) void { |
| 1269 | | state.stack.deinit(gpa); |
| 1270 | | } |
| 1271 | 1256 | }; |
| 1272 | 1257 | |
| 1273 | | fn captureState(self: *Self) !State { |
| 1258 | fn captureState(self: *Self) State { |
| 1274 | 1259 | return State{ |
| 1275 | | .next_stack_offset = self.next_stack_offset, |
| 1276 | 1260 | .registers = self.register_manager.registers, |
| 1277 | 1261 | .free_registers = self.register_manager.free_registers, |
| 1278 | 1262 | .eflags_inst = self.eflags_inst, |
| 1279 | | .stack = try self.stack.clone(self.gpa), |
| 1280 | 1263 | }; |
| 1281 | 1264 | } |
| 1282 | 1265 | |
| 1283 | | fn revertState(self: *Self, state: State) !void { |
| 1284 | | var stack = try state.stack.clone(self.gpa); |
| 1285 | | errdefer stack.deinit(self.gpa); |
| 1286 | | |
| 1287 | | self.register_manager.registers = state.registers; |
| 1266 | fn revertState(self: *Self, state: State) void { |
| 1288 | 1267 | self.eflags_inst = state.eflags_inst; |
| 1289 | | |
| 1290 | | self.stack.deinit(self.gpa); |
| 1291 | | self.stack = stack; |
| 1292 | | |
| 1293 | | self.next_stack_offset = state.next_stack_offset; |
| 1294 | 1268 | self.register_manager.free_registers = state.free_registers; |
| 1269 | self.register_manager.registers = state.registers; |
| 1295 | 1270 | } |
| 1296 | 1271 | |
| 1297 | 1272 | pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { |
| 1298 | 1273 | const stack_mcv = try self.allocRegOrMem(inst, false); |
| 1299 | 1274 | log.debug("spilling %{d} to stack mcv {any}", .{ inst, stack_mcv }); |
| 1300 | | const reg_mcv = self.getResolvedInstValue(inst).?; |
| 1275 | const reg_mcv = self.getResolvedInstValue(inst).?.*; |
| 1301 | 1276 | switch (reg_mcv) { |
| 1302 | 1277 | .register => |other| { |
| 1303 | 1278 | assert(reg.to64() == other.to64()); |
| ... | ... | @@ -1314,7 +1289,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void |
| 1314 | 1289 | |
| 1315 | 1290 | pub fn spillEflagsIfOccupied(self: *Self) !void { |
| 1316 | 1291 | if (self.eflags_inst) |inst_to_save| { |
| 1317 | | const mcv = self.getResolvedInstValue(inst_to_save).?; |
| 1292 | const mcv = self.getResolvedInstValue(inst_to_save).?.*; |
| 1318 | 1293 | const new_mcv = switch (mcv) { |
| 1319 | 1294 | .register_overflow => try self.allocRegOrMem(inst_to_save, false), |
| 1320 | 1295 | .eflags => try self.allocRegOrMem(inst_to_save, true), |
| ... | ... | @@ -2607,7 +2582,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2607 | 2582 | const index_ty = self.air.typeOf(bin_op.rhs); |
| 2608 | 2583 | const index = try self.resolveInst(bin_op.rhs); |
| 2609 | 2584 | const index_lock: ?RegisterLock = switch (index) { |
| 2610 | | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 2585 | .register => |reg| self.register_manager.lockReg(reg), |
| 2611 | 2586 | else => null, |
| 2612 | 2587 | }; |
| 2613 | 2588 | defer if (index_lock) |lock| self.register_manager.unlockReg(lock); |
| ... | ... | @@ -2656,7 +2631,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2656 | 2631 | const index_ty = self.air.typeOf(extra.rhs); |
| 2657 | 2632 | const index = try self.resolveInst(extra.rhs); |
| 2658 | 2633 | const index_lock: ?RegisterLock = switch (index) { |
| 2659 | | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 2634 | .register => |reg| self.register_manager.lockReg(reg), |
| 2660 | 2635 | else => null, |
| 2661 | 2636 | }; |
| 2662 | 2637 | defer if (index_lock) |lock| self.register_manager.unlockReg(lock); |
| ... | ... | @@ -3202,8 +3177,8 @@ fn reuseOperand( |
| 3202 | 3177 | .register => |reg| { |
| 3203 | 3178 | // If it's in the registers table, need to associate the register with the |
| 3204 | 3179 | // new instruction. |
| 3205 | | if (RegisterManager.indexOfRegIntoTracked(reg)) |index| { |
| 3206 | | if (!self.register_manager.isRegFree(reg)) { |
| 3180 | if (!self.register_manager.isRegFree(reg)) { |
| 3181 | if (RegisterManager.indexOfRegIntoTracked(reg)) |index| { |
| 3207 | 3182 | self.register_manager.registers[index] = inst; |
| 3208 | 3183 | } |
| 3209 | 3184 | } |
| ... | ... | @@ -3542,7 +3517,7 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void { |
| 3542 | 3517 | const value_ty = self.air.typeOf(bin_op.rhs); |
| 3543 | 3518 | log.debug("airStore(%{d}): {} <- {}", .{ inst, ptr, value }); |
| 3544 | 3519 | try self.store(ptr, value, ptr_ty, value_ty); |
| 3545 | | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 3520 | return self.finishAir(inst, .none, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 3546 | 3521 | } |
| 3547 | 3522 | |
| 3548 | 3523 | fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -5270,8 +5245,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 5270 | 5245 | } |
| 5271 | 5246 | |
| 5272 | 5247 | // Capture the state of register and stack allocation state so that we can revert to it. |
| 5273 | | var saved_state = try self.captureState(); |
| 5274 | | defer saved_state.deinit(self.gpa); |
| 5248 | const saved_state = self.captureState(); |
| 5275 | 5249 | |
| 5276 | 5250 | { |
| 5277 | 5251 | try self.branch_stack.append(.{}); |
| ... | ... | @@ -5289,7 +5263,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 5289 | 5263 | var then_branch = self.branch_stack.pop(); |
| 5290 | 5264 | defer then_branch.deinit(self.gpa); |
| 5291 | 5265 | |
| 5292 | | try self.revertState(saved_state); |
| 5266 | self.revertState(saved_state); |
| 5293 | 5267 | |
| 5294 | 5268 | try self.performReloc(reloc); |
| 5295 | 5269 | |
| ... | ... | @@ -5632,24 +5606,28 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { |
| 5632 | 5606 | try self.blocks.putNoClobber(self.gpa, inst, .{ |
| 5633 | 5607 | // A block is a setup to be able to jump to the end. |
| 5634 | 5608 | .relocs = .{}, |
| 5635 | | // It also acts as a receptacle for break operands. |
| 5636 | | // Here we use `MCValue.none` to represent a null value so that the first |
| 5637 | | // break instruction will choose a MCValue for the block result and overwrite |
| 5638 | | // this field. Following break instructions will use that MCValue to put their |
| 5639 | | // block results. |
| 5640 | | .mcv = if (self.liveness.isUnused(inst)) .dead else .none, |
| 5641 | 5609 | }); |
| 5642 | 5610 | defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa); |
| 5643 | 5611 | |
| 5612 | { |
| 5613 | // Here we use `.none` to represent a null value so that the first break |
| 5614 | // instruction will choose a MCValue for the block result and overwrite |
| 5615 | // this field. Following break instructions will use that MCValue to put |
| 5616 | // their block results. |
| 5617 | const ty = self.air.typeOfIndex(inst); |
| 5618 | const result: MCValue = |
| 5619 | if (!ty.hasRuntimeBitsIgnoreComptime() or self.liveness.isUnused(inst)) .dead else .none; |
| 5620 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 5621 | branch.inst_table.putAssumeCapacityNoClobber(inst, result); |
| 5622 | } |
| 5623 | |
| 5644 | 5624 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 5645 | 5625 | const extra = self.air.extraData(Air.Block, ty_pl.payload); |
| 5646 | 5626 | const body = self.air.extra[extra.end..][0..extra.data.body_len]; |
| 5647 | 5627 | try self.genBody(body); |
| 5648 | 5628 | |
| 5649 | 5629 | for (self.blocks.getPtr(inst).?.relocs.items) |reloc| try self.performReloc(reloc); |
| 5650 | | |
| 5651 | | const result = self.blocks.getPtr(inst).?.mcv; |
| 5652 | | return self.finishAir(inst, result, .{ .none, .none, .none }); |
| 5630 | self.finishAirBookkeeping(); |
| 5653 | 5631 | } |
| 5654 | 5632 | |
| 5655 | 5633 | fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -5687,8 +5665,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5687 | 5665 | defer if (prev_branch) |*branch| branch.deinit(self.gpa); |
| 5688 | 5666 | |
| 5689 | 5667 | // Capture the state of register and stack allocation state so that we can revert to it. |
| 5690 | | var saved_state = try self.captureState(); |
| 5691 | | defer saved_state.deinit(self.gpa); |
| 5668 | const saved_state = self.captureState(); |
| 5692 | 5669 | |
| 5693 | 5670 | const cases_len = switch_br.data.cases_len + @boolToInt(switch_br.data.else_body_len > 0); |
| 5694 | 5671 | while (case_i < switch_br.data.cases_len) : (case_i += 1) { |
| ... | ... | @@ -5698,7 +5675,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5698 | 5675 | extra_index = case.end + items.len + case_body.len; |
| 5699 | 5676 | |
| 5700 | 5677 | // Revert to the previous register and stack allocation state. |
| 5701 | | if (prev_branch) |_| try self.revertState(saved_state); |
| 5678 | if (prev_branch) |_| self.revertState(saved_state); |
| 5702 | 5679 | |
| 5703 | 5680 | var relocs = try self.gpa.alloc(u32, items.len); |
| 5704 | 5681 | defer self.gpa.free(relocs); |
| ... | ... | @@ -5742,7 +5719,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5742 | 5719 | const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len]; |
| 5743 | 5720 | |
| 5744 | 5721 | // Revert to the previous register and stack allocation state. |
| 5745 | | if (prev_branch) |_| try self.revertState(saved_state); |
| 5722 | if (prev_branch) |_| self.revertState(saved_state); |
| 5746 | 5723 | |
| 5747 | 5724 | { |
| 5748 | 5725 | if (cases_len > 1) try self.branch_stack.append(.{}); |
| ... | ... | @@ -5801,7 +5778,7 @@ fn canonicaliseBranches( |
| 5801 | 5778 | if (target_value == .dead) continue; |
| 5802 | 5779 | // The instruction is only overridden in the else branch. |
| 5803 | 5780 | // If integer overflows occurs, the question is: why wasn't the instruction marked dead? |
| 5804 | | break :blk self.getResolvedInstValue(target_key).?; |
| 5781 | break :blk self.getResolvedInstValue(target_key).?.*; |
| 5805 | 5782 | }; |
| 5806 | 5783 | log.debug("consolidating target_entry {d} {}=>{}", .{ target_key, target_value, canon_mcv }); |
| 5807 | 5784 | // TODO make sure the destination stack offset / register does not already have something |
| ... | ... | @@ -5816,17 +5793,18 @@ fn canonicaliseBranches( |
| 5816 | 5793 | // We already deleted the items from this table that matched the target_branch. |
| 5817 | 5794 | // So these are all instructions that are only overridden in the canon branch. |
| 5818 | 5795 | const parent_mcv = |
| 5819 | | if (canon_value != .dead) self.getResolvedInstValue(canon_key).? else undefined; |
| 5796 | if (canon_value != .dead) self.getResolvedInstValue(canon_key).?.* else undefined; |
| 5797 | if (canon_value != .dead) { |
| 5798 | log.debug("consolidating canon_entry {d} {}=>{}", .{ canon_key, parent_mcv, canon_value }); |
| 5799 | // TODO make sure the destination stack offset / register does not already have something |
| 5800 | // going on there. |
| 5801 | try self.setRegOrMem(self.air.typeOfIndex(canon_key), canon_value, parent_mcv); |
| 5802 | self.freeValue(parent_mcv); |
| 5803 | // TODO track the new register / stack allocation |
| 5804 | } |
| 5820 | 5805 | if (update_parent) { |
| 5821 | 5806 | parent_branch.inst_table.putAssumeCapacity(canon_key, canon_value); |
| 5822 | 5807 | } |
| 5823 | | if (canon_value == .dead) continue; |
| 5824 | | log.debug("consolidating canon_entry {d} {}=>{}", .{ canon_key, parent_mcv, canon_value }); |
| 5825 | | // TODO make sure the destination stack offset / register does not already have something |
| 5826 | | // going on there. |
| 5827 | | try self.setRegOrMem(self.air.typeOfIndex(canon_key), canon_value, parent_mcv); |
| 5828 | | self.freeValue(parent_mcv); |
| 5829 | | // TODO track the new register / stack allocation |
| 5830 | 5808 | } |
| 5831 | 5809 | } |
| 5832 | 5810 | |
| ... | ... | @@ -5845,27 +5823,41 @@ fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void { |
| 5845 | 5823 | |
| 5846 | 5824 | fn airBr(self: *Self, inst: Air.Inst.Index) !void { |
| 5847 | 5825 | const branch = self.air.instructions.items(.data)[inst].br; |
| 5848 | | try self.br(branch.block_inst, branch.operand); |
| 5826 | try self.br(inst, branch.block_inst, branch.operand); |
| 5849 | 5827 | return self.finishAir(inst, .dead, .{ branch.operand, .none, .none }); |
| 5850 | 5828 | } |
| 5851 | 5829 | |
| 5852 | | fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { |
| 5853 | | const block_data = self.blocks.getPtr(block).?; |
| 5854 | | if (block_data.mcv != .dead and self.air.typeOf(operand).hasRuntimeBits()) { |
| 5855 | | const operand_mcv = try self.resolveInst(operand); |
| 5856 | | if (block_data.mcv == .none) { |
| 5857 | | block_data.mcv = switch (operand_mcv) { |
| 5858 | | .none, .dead, .unreach => unreachable, |
| 5859 | | .register, .stack_offset, .memory => operand_mcv, |
| 5860 | | .eflags, .immediate, .ptr_stack_offset => blk: { |
| 5861 | | const new_mcv = try self.allocRegOrMem(block, true); |
| 5862 | | try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); |
| 5863 | | break :blk new_mcv; |
| 5864 | | }, |
| 5865 | | else => return self.fail("TODO implement block_data.mcv = operand_mcv for {}", .{operand_mcv}), |
| 5866 | | }; |
| 5867 | | } else { |
| 5868 | | try self.setRegOrMem(self.air.typeOfIndex(block), block_data.mcv, operand_mcv); |
| 5830 | fn br(self: *Self, inst: Air.Inst.Index, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { |
| 5831 | // The first break instruction encounters `.none` here and chooses a |
| 5832 | // machine code value for the block result, populating this field. |
| 5833 | // Following break instructions encounter that value and use it for |
| 5834 | // the location to store their block results. |
| 5835 | if (self.getResolvedInstValue(block)) |dst_mcv| { |
| 5836 | const src_mcv = try self.resolveInst(operand); |
| 5837 | switch (dst_mcv.*) { |
| 5838 | .none => { |
| 5839 | const result = result: { |
| 5840 | if (!self.reuseOperand(inst, operand, 0, src_mcv)) { |
| 5841 | const new_mcv = try self.allocRegOrMem(block, true); |
| 5842 | try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, src_mcv); |
| 5843 | break :result new_mcv; |
| 5844 | } |
| 5845 | |
| 5846 | // the value is actually tracked with block, not inst |
| 5847 | switch (src_mcv) { |
| 5848 | .register => |reg| if (!self.register_manager.isRegFree(reg)) { |
| 5849 | if (RegisterManager.indexOfRegIntoTracked(reg)) |index| { |
| 5850 | self.register_manager.registers[index] = block; |
| 5851 | } |
| 5852 | }, |
| 5853 | .stack_offset => {}, |
| 5854 | else => unreachable, |
| 5855 | } |
| 5856 | break :result src_mcv; |
| 5857 | }; |
| 5858 | dst_mcv.* = result; |
| 5859 | }, |
| 5860 | else => try self.setRegOrMem(self.air.typeOfIndex(block), dst_mcv.*, src_mcv), |
| 5869 | 5861 | } |
| 5870 | 5862 | } |
| 5871 | 5863 | return self.brVoid(block); |
| ... | ... | @@ -7243,17 +7235,17 @@ fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { |
| 7243 | 7235 | return gop.value_ptr.*; |
| 7244 | 7236 | }, |
| 7245 | 7237 | .const_ty => unreachable, |
| 7246 | | else => return self.getResolvedInstValue(inst_index).?, |
| 7238 | else => return self.getResolvedInstValue(inst_index).?.*, |
| 7247 | 7239 | } |
| 7248 | 7240 | } |
| 7249 | 7241 | |
| 7250 | | fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) ?MCValue { |
| 7242 | fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) ?*MCValue { |
| 7251 | 7243 | // Treat each stack item as a "layer" on top of the previous one. |
| 7252 | 7244 | var i: usize = self.branch_stack.items.len; |
| 7253 | 7245 | while (true) { |
| 7254 | 7246 | i -= 1; |
| 7255 | | if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| { |
| 7256 | | return if (mcv != .dead) mcv else null; |
| 7247 | if (self.branch_stack.items[i].inst_table.getPtr(inst)) |mcv| { |
| 7248 | return if (mcv.* != .dead) mcv else null; |
| 7257 | 7249 | } |
| 7258 | 7250 | } |
| 7259 | 7251 | } |