| ... | ... | @@ -213,7 +213,15 @@ const StackAllocation = struct { |
| 213 | 213 | }; |
| 214 | 214 | |
| 215 | 215 | const BlockData = struct { |
| 216 | | relocs: std.ArrayListUnmanaged(Mir.Inst.Index), |
| 216 | relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .{}, |
| 217 | branch: ?Branch = null, |
| 218 | branch_depth: u32, |
| 219 | |
| 220 | fn deinit(self: *BlockData, gpa: Allocator) void { |
| 221 | if (self.branch) |*branch| branch.deinit(gpa); |
| 222 | self.relocs.deinit(gpa); |
| 223 | self.* = undefined; |
| 224 | } |
| 217 | 225 | }; |
| 218 | 226 | |
| 219 | 227 | const BigTomb = struct { |
| ... | ... | @@ -5233,11 +5241,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 5233 | 5241 | // that death now instead of later as this has an effect on |
| 5234 | 5242 | // whether it needs to be spilled in the branches |
| 5235 | 5243 | if (self.liveness.operandDies(inst, 0)) { |
| 5236 | | const op_int = @enumToInt(pl_op.operand); |
| 5237 | | if (op_int >= Air.Inst.Ref.typed_value_map.len) { |
| 5238 | | const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); |
| 5239 | | self.processDeath(op_index); |
| 5240 | | } |
| 5244 | if (Air.refToIndex(pl_op.operand)) |op_inst| self.processDeath(op_inst); |
| 5241 | 5245 | } |
| 5242 | 5246 | |
| 5243 | 5247 | // Capture the state of register and stack allocation state so that we can revert to it. |
| ... | ... | @@ -5290,10 +5294,10 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 5290 | 5294 | for (self.branch_stack.items) |bs| { |
| 5291 | 5295 | log.debug("{}", .{bs.fmtDebug()}); |
| 5292 | 5296 | } |
| 5293 | | |
| 5294 | 5297 | log.debug("Then branch: {}", .{then_branch.fmtDebug()}); |
| 5295 | 5298 | log.debug("Else branch: {}", .{else_branch.fmtDebug()}); |
| 5296 | | try self.canonicaliseBranches(true, &then_branch, &else_branch); |
| 5299 | |
| 5300 | try self.canonicaliseBranches(true, &then_branch, &else_branch, true); |
| 5297 | 5301 | |
| 5298 | 5302 | // We already took care of pl_op.operand earlier, so we're going |
| 5299 | 5303 | // to pass .none here |
| ... | ... | @@ -5599,11 +5603,16 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { |
| 5599 | 5603 | } |
| 5600 | 5604 | |
| 5601 | 5605 | fn airBlock(self: *Self, inst: Air.Inst.Index) !void { |
| 5602 | | try self.blocks.putNoClobber(self.gpa, inst, .{ |
| 5603 | | // A block is a setup to be able to jump to the end. |
| 5604 | | .relocs = .{}, |
| 5605 | | }); |
| 5606 | | defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa); |
| 5606 | // A block is a setup to be able to jump to the end. |
| 5607 | const branch_depth = @intCast(u32, self.branch_stack.items.len); |
| 5608 | try self.blocks.putNoClobber(self.gpa, inst, .{ .branch_depth = branch_depth }); |
| 5609 | defer { |
| 5610 | var block_data = self.blocks.fetchRemove(inst).?.value; |
| 5611 | block_data.deinit(self.gpa); |
| 5612 | } |
| 5613 | |
| 5614 | try self.branch_stack.append(.{}); |
| 5615 | defer _ = self.branch_stack.pop(); |
| 5607 | 5616 | |
| 5608 | 5617 | const ty = self.air.typeOfIndex(inst); |
| 5609 | 5618 | const unused = !ty.hasRuntimeBitsIgnoreComptime() or self.liveness.isUnused(inst); |
| ... | ... | @@ -5613,8 +5622,8 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { |
| 5613 | 5622 | // this field. Following break instructions will use that MCValue to put |
| 5614 | 5623 | // their block results. |
| 5615 | 5624 | const result: MCValue = if (unused) .dead else .none; |
| 5616 | | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 5617 | | branch.inst_table.putAssumeCapacityNoClobber(inst, result); |
| 5625 | const branch = &self.branch_stack.items[branch_depth]; |
| 5626 | try branch.inst_table.putNoClobber(self.gpa, inst, result); |
| 5618 | 5627 | } |
| 5619 | 5628 | |
| 5620 | 5629 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| ... | ... | @@ -5622,7 +5631,23 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { |
| 5622 | 5631 | const body = self.air.extra[extra.end..][0..extra.data.body_len]; |
| 5623 | 5632 | try self.genBody(body); |
| 5624 | 5633 | |
| 5625 | | for (self.blocks.getPtr(inst).?.relocs.items) |reloc| try self.performReloc(reloc); |
| 5634 | const block_data = self.blocks.getPtr(inst).?; |
| 5635 | { |
| 5636 | const src_branch = block_data.branch orelse self.branch_stack.items[branch_depth]; |
| 5637 | const dst_branch = &self.branch_stack.items[branch_depth - 1]; |
| 5638 | try dst_branch.inst_table.ensureUnusedCapacity(self.gpa, src_branch.inst_table.count()); |
| 5639 | var it = src_branch.inst_table.iterator(); |
| 5640 | while (it.next()) |entry| { |
| 5641 | const tracked_inst = entry.key_ptr.*; |
| 5642 | const tracked_value = entry.value_ptr.*; |
| 5643 | if (dst_branch.inst_table.fetchPutAssumeCapacity(tracked_inst, tracked_value)) |old_entry| { |
| 5644 | self.freeValue(old_entry.value); |
| 5645 | } |
| 5646 | self.getValue(tracked_value, tracked_inst); |
| 5647 | } |
| 5648 | } |
| 5649 | |
| 5650 | for (block_data.relocs.items) |reloc| try self.performReloc(reloc); |
| 5626 | 5651 | |
| 5627 | 5652 | const result = if (unused) .dead else self.getResolvedInstValue(inst).?.*; |
| 5628 | 5653 | self.getValue(result, inst); |
| ... | ... | @@ -5647,11 +5672,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5647 | 5672 | // that death now instead of later as this has an effect on |
| 5648 | 5673 | // whether it needs to be spilled in the branches |
| 5649 | 5674 | if (self.liveness.operandDies(inst, 0)) { |
| 5650 | | const op_int = @enumToInt(pl_op.operand); |
| 5651 | | if (op_int >= Air.Inst.Ref.typed_value_map.len) { |
| 5652 | | const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); |
| 5653 | | self.processDeath(op_index); |
| 5654 | | } |
| 5675 | if (Air.refToIndex(pl_op.operand)) |op_inst| self.processDeath(op_inst); |
| 5655 | 5676 | } |
| 5656 | 5677 | |
| 5657 | 5678 | log.debug("airSwitch: %{d}", .{inst}); |
| ... | ... | @@ -5704,8 +5725,9 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5704 | 5725 | errdefer case_branch.deinit(self.gpa); |
| 5705 | 5726 | |
| 5706 | 5727 | log.debug("Case-{d} branch: {}", .{ case_i, case_branch.fmtDebug() }); |
| 5728 | const final = case_i == cases_len - 1; |
| 5707 | 5729 | if (prev_branch) |*canon_branch| { |
| 5708 | | try self.canonicaliseBranches(case_i == cases_len - 1, canon_branch, &case_branch); |
| 5730 | try self.canonicaliseBranches(final, canon_branch, &case_branch, true); |
| 5709 | 5731 | canon_branch.deinit(self.gpa); |
| 5710 | 5732 | } |
| 5711 | 5733 | prev_branch = case_branch; |
| ... | ... | @@ -5740,7 +5762,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5740 | 5762 | |
| 5741 | 5763 | log.debug("Else branch: {}", .{else_branch.fmtDebug()}); |
| 5742 | 5764 | if (prev_branch) |*canon_branch| { |
| 5743 | | try self.canonicaliseBranches(true, canon_branch, &else_branch); |
| 5765 | try self.canonicaliseBranches(true, canon_branch, &else_branch, true); |
| 5744 | 5766 | canon_branch.deinit(self.gpa); |
| 5745 | 5767 | } |
| 5746 | 5768 | prev_branch = else_branch; |
| ... | ... | @@ -5756,27 +5778,30 @@ fn canonicaliseBranches( |
| 5756 | 5778 | update_parent: bool, |
| 5757 | 5779 | canon_branch: *Branch, |
| 5758 | 5780 | target_branch: *const Branch, |
| 5781 | comptime assert_same_deaths: bool, |
| 5759 | 5782 | ) !void { |
| 5760 | 5783 | const parent_branch = |
| 5761 | 5784 | if (update_parent) &self.branch_stack.items[self.branch_stack.items.len - 1] else undefined; |
| 5762 | | if (update_parent) try self.ensureProcessDeathCapacity(target_branch.inst_table.count()); |
| 5763 | 5785 | |
| 5764 | | const target_slice = target_branch.inst_table.entries.slice(); |
| 5765 | | for (target_slice.items(.key), target_slice.items(.value)) |target_key, target_value| { |
| 5786 | if (update_parent) try self.ensureProcessDeathCapacity(target_branch.inst_table.count()); |
| 5787 | var target_it = target_branch.inst_table.iterator(); |
| 5788 | while (target_it.next()) |target_entry| { |
| 5789 | const target_key = target_entry.key_ptr.*; |
| 5790 | const target_value = target_entry.value_ptr.*; |
| 5766 | 5791 | const canon_mcv = if (canon_branch.inst_table.fetchSwapRemove(target_key)) |canon_entry| blk: { |
| 5767 | 5792 | // The instruction's MCValue is overridden in both branches. |
| 5768 | 5793 | if (update_parent) { |
| 5769 | 5794 | parent_branch.inst_table.putAssumeCapacity(target_key, canon_entry.value); |
| 5770 | 5795 | } |
| 5771 | 5796 | if (target_value == .dead) { |
| 5772 | | assert(canon_entry.value == .dead); |
| 5797 | if (assert_same_deaths) assert(canon_entry.value == .dead); |
| 5773 | 5798 | continue; |
| 5774 | 5799 | } |
| 5775 | 5800 | break :blk canon_entry.value; |
| 5776 | 5801 | } else blk: { |
| 5777 | 5802 | if (target_value == .dead) continue; |
| 5778 | 5803 | // The instruction is only overridden in the else branch. |
| 5779 | | // If integer overflows occurs, the question is: why wasn't the instruction marked dead? |
| 5804 | // If integer overflow occurs, the question is: why wasn't the instruction marked dead? |
| 5780 | 5805 | break :blk self.getResolvedInstValue(target_key).?.*; |
| 5781 | 5806 | }; |
| 5782 | 5807 | log.debug("consolidating target_entry {d} {}=>{}", .{ target_key, target_value, canon_mcv }); |
| ... | ... | @@ -5786,9 +5811,12 @@ fn canonicaliseBranches( |
| 5786 | 5811 | self.freeValue(target_value); |
| 5787 | 5812 | // TODO track the new register / stack allocation |
| 5788 | 5813 | } |
| 5814 | |
| 5789 | 5815 | if (update_parent) try self.ensureProcessDeathCapacity(canon_branch.inst_table.count()); |
| 5790 | | const canon_slice = canon_branch.inst_table.entries.slice(); |
| 5791 | | for (canon_slice.items(.key), canon_slice.items(.value)) |canon_key, canon_value| { |
| 5816 | var canon_it = canon_branch.inst_table.iterator(); |
| 5817 | while (canon_it.next()) |canon_entry| { |
| 5818 | const canon_key = canon_entry.key_ptr.*; |
| 5819 | const canon_value = canon_entry.value_ptr.*; |
| 5792 | 5820 | // We already deleted the items from this table that matched the target_branch. |
| 5793 | 5821 | // So these are all instructions that are only overridden in the canon branch. |
| 5794 | 5822 | const parent_mcv = |
| ... | ... | @@ -5821,22 +5849,19 @@ fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void { |
| 5821 | 5849 | } |
| 5822 | 5850 | |
| 5823 | 5851 | fn airBr(self: *Self, inst: Air.Inst.Index) !void { |
| 5824 | | const branch = self.air.instructions.items(.data)[inst].br; |
| 5825 | | try self.br(inst, branch.block_inst, branch.operand); |
| 5826 | | return self.finishAir(inst, .dead, .{ branch.operand, .none, .none }); |
| 5827 | | } |
| 5852 | const br = self.air.instructions.items(.data)[inst].br; |
| 5853 | const block = br.block_inst; |
| 5828 | 5854 | |
| 5829 | | fn br(self: *Self, inst: Air.Inst.Index, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { |
| 5830 | 5855 | // The first break instruction encounters `.none` here and chooses a |
| 5831 | 5856 | // machine code value for the block result, populating this field. |
| 5832 | 5857 | // Following break instructions encounter that value and use it for |
| 5833 | 5858 | // the location to store their block results. |
| 5834 | 5859 | if (self.getResolvedInstValue(block)) |dst_mcv| { |
| 5835 | | const src_mcv = try self.resolveInst(operand); |
| 5860 | const src_mcv = try self.resolveInst(br.operand); |
| 5836 | 5861 | switch (dst_mcv.*) { |
| 5837 | 5862 | .none => { |
| 5838 | 5863 | const result = result: { |
| 5839 | | if (self.reuseOperand(inst, operand, 0, src_mcv)) break :result src_mcv; |
| 5864 | if (self.reuseOperand(inst, br.operand, 0, src_mcv)) break :result src_mcv; |
| 5840 | 5865 | |
| 5841 | 5866 | const new_mcv = try self.allocRegOrMem(block, true); |
| 5842 | 5867 | try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, src_mcv); |
| ... | ... | @@ -5848,16 +5873,50 @@ fn br(self: *Self, inst: Air.Inst.Index, block: Air.Inst.Index, operand: Air.Ins |
| 5848 | 5873 | else => try self.setRegOrMem(self.air.typeOfIndex(block), dst_mcv.*, src_mcv), |
| 5849 | 5874 | } |
| 5850 | 5875 | } |
| 5851 | | return self.brVoid(block); |
| 5852 | | } |
| 5853 | 5876 | |
| 5854 | | fn brVoid(self: *Self, block: Air.Inst.Index) !void { |
| 5877 | // Process operand death early so that it is properly accounted for in the Branch below. |
| 5878 | if (self.liveness.operandDies(inst, 0)) { |
| 5879 | if (Air.refToIndex(br.operand)) |op_inst| self.processDeath(op_inst); |
| 5880 | } |
| 5881 | |
| 5855 | 5882 | const block_data = self.blocks.getPtr(block).?; |
| 5883 | { |
| 5884 | var branch = Branch{}; |
| 5885 | errdefer branch.deinit(self.gpa); |
| 5886 | |
| 5887 | var branch_i = self.branch_stack.items.len - 1; |
| 5888 | while (branch_i >= block_data.branch_depth) : (branch_i -= 1) { |
| 5889 | const table = &self.branch_stack.items[branch_i].inst_table; |
| 5890 | try branch.inst_table.ensureUnusedCapacity(self.gpa, table.count()); |
| 5891 | var it = table.iterator(); |
| 5892 | while (it.next()) |entry| { |
| 5893 | const gop = branch.inst_table.getOrPutAssumeCapacity(entry.key_ptr.*); |
| 5894 | if (!gop.found_existing) gop.value_ptr.* = entry.value_ptr.*; |
| 5895 | } |
| 5896 | } |
| 5897 | |
| 5898 | if (block_data.branch) |*prev_branch| { |
| 5899 | log.debug("brVoid: %{d}", .{inst}); |
| 5900 | log.debug("Upper branches:", .{}); |
| 5901 | for (self.branch_stack.items) |bs| { |
| 5902 | log.debug("{}", .{bs.fmtDebug()}); |
| 5903 | } |
| 5904 | log.debug("Prev branch: {}", .{prev_branch.fmtDebug()}); |
| 5905 | log.debug("Cur branch: {}", .{branch.fmtDebug()}); |
| 5906 | |
| 5907 | try self.canonicaliseBranches(false, prev_branch, &branch, false); |
| 5908 | prev_branch.deinit(self.gpa); |
| 5909 | } |
| 5910 | block_data.branch = branch; |
| 5911 | } |
| 5912 | |
| 5856 | 5913 | // Emit a jump with a relocation. It will be patched up after the block ends. |
| 5857 | 5914 | try block_data.relocs.ensureUnusedCapacity(self.gpa, 1); |
| 5858 | 5915 | // Leave the jump offset undefined |
| 5859 | 5916 | const jmp_reloc = try self.asmJmpReloc(undefined); |
| 5860 | 5917 | block_data.relocs.appendAssumeCapacity(jmp_reloc); |
| 5918 | |
| 5919 | self.finishAirBookkeeping(); |
| 5861 | 5920 | } |
| 5862 | 5921 | |
| 5863 | 5922 | fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |