| ... | @@ -200,6 +200,34 @@ const Branch = struct { | ... | @@ -200,6 +200,34 @@ const Branch = struct { |
| 200 | self.inst_table.deinit(gpa); | 200 | self.inst_table.deinit(gpa); |
| 201 | self.* = undefined; | 201 | self.* = undefined; |
| 202 | } | 202 | } |
| | 203 | |
| | 204 | const FormatContext = struct { |
| | 205 | insts: []const Air.Inst.Index, |
| | 206 | mcvs: []const MCValue, |
| | 207 | }; |
| | 208 | |
| | 209 | fn fmt( |
| | 210 | ctx: FormatContext, |
| | 211 | comptime unused_format_string: []const u8, |
| | 212 | options: std.fmt.FormatOptions, |
| | 213 | writer: anytype, |
| | 214 | ) @TypeOf(writer).Error!void { |
| | 215 | _ = options; |
| | 216 | comptime assert(unused_format_string.len == 0); |
| | 217 | try writer.writeAll("Branch {\n"); |
| | 218 | for (ctx.insts) |inst, i| { |
| | 219 | const mcv = ctx.mcvs[i]; |
| | 220 | try writer.print(" %{d} => {}\n", .{ inst, mcv }); |
| | 221 | } |
| | 222 | try writer.writeAll("}"); |
| | 223 | } |
| | 224 | |
| | 225 | fn fmtDebug(self: @This()) std.fmt.Formatter(fmt) { |
| | 226 | return .{ .data = .{ |
| | 227 | .insts = self.inst_table.keys(), |
| | 228 | .mcvs = self.inst_table.values(), |
| | 229 | } }; |
| | 230 | } |
| 203 | }; | 231 | }; |
| 204 | | 232 | |
| 205 | const StackAllocation = struct { | 233 | const StackAllocation = struct { |
| ... | @@ -232,7 +260,7 @@ const BigTomb = struct { | ... | @@ -232,7 +260,7 @@ const BigTomb = struct { |
| 232 | fn finishAir(bt: *BigTomb, result: MCValue) void { | 260 | fn finishAir(bt: *BigTomb, result: MCValue) void { |
| 233 | const is_used = !bt.function.liveness.isUnused(bt.inst); | 261 | const is_used = !bt.function.liveness.isUnused(bt.inst); |
| 234 | if (is_used) { | 262 | if (is_used) { |
| 235 | log.debug("%{d} => {}", .{ bt.inst, result }); | 263 | log.debug(" (saving %{d} => {})", .{ bt.inst, result }); |
| 236 | const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1]; | 264 | const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1]; |
| 237 | branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result); | 265 | branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result); |
| 238 | } | 266 | } |
| ... | @@ -795,6 +823,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -795,6 +823,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 795 | fn processDeath(self: *Self, inst: Air.Inst.Index) void { | 823 | fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 796 | const air_tags = self.air.instructions.items(.tag); | 824 | const air_tags = self.air.instructions.items(.tag); |
| 797 | if (air_tags[inst] == .constant) return; // Constants are immortal. | 825 | if (air_tags[inst] == .constant) return; // Constants are immortal. |
| | 826 | log.debug(" (processing death of %{d})", .{inst}); |
| 798 | // When editing this function, note that the logic must synchronize with `reuseOperand`. | 827 | // When editing this function, note that the logic must synchronize with `reuseOperand`. |
| 799 | const prev_value = self.getResolvedInstValue(inst); | 828 | const prev_value = self.getResolvedInstValue(inst); |
| 800 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | 829 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| ... | @@ -822,8 +851,10 @@ fn finishAirBookkeeping(self: *Self) void { | ... | @@ -822,8 +851,10 @@ fn finishAirBookkeeping(self: *Self) void { |
| 822 | } | 851 | } |
| 823 | | 852 | |
| 824 | fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void { | 853 | fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void { |
| | 854 | log.debug("finishAir: %{d}, {}, {any}", .{ inst, result, operands }); |
| 825 | var tomb_bits = self.liveness.getTombBits(inst); | 855 | var tomb_bits = self.liveness.getTombBits(inst); |
| 826 | for (operands) |op| { | 856 | for (operands) |op| { |
| | 857 | log.debug(" (processing {})", .{op}); |
| 827 | const dies = @truncate(u1, tomb_bits) != 0; | 858 | const dies = @truncate(u1, tomb_bits) != 0; |
| 828 | tomb_bits >>= 1; | 859 | tomb_bits >>= 1; |
| 829 | if (!dies) continue; | 860 | if (!dies) continue; |
| ... | @@ -834,7 +865,7 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live | ... | @@ -834,7 +865,7 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live |
| 834 | } | 865 | } |
| 835 | const is_used = @truncate(u1, tomb_bits) == 0; | 866 | const is_used = @truncate(u1, tomb_bits) == 0; |
| 836 | if (is_used) { | 867 | if (is_used) { |
| 837 | log.debug("%{d} => {}", .{ inst, result }); | 868 | log.debug(" (saving %{d} => {})", .{ inst, result }); |
| 838 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | 869 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 839 | branch.inst_table.putAssumeCapacityNoClobber(inst, result); | 870 | branch.inst_table.putAssumeCapacityNoClobber(inst, result); |
| 840 | | 871 | |
| ... | @@ -4647,6 +4678,8 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4647,6 +4678,8 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4647 | | 4678 | |
| 4648 | const reloc = try self.genCondBrMir(cond_ty, cond); | 4679 | const reloc = try self.genCondBrMir(cond_ty, cond); |
| 4649 | | 4680 | |
| | 4681 | log.debug("airCondBr: %{d}", .{inst}); |
| | 4682 | |
| 4650 | // If the condition dies here in this condbr instruction, process | 4683 | // If the condition dies here in this condbr instruction, process |
| 4651 | // that death now instead of later as this has an effect on | 4684 | // that death now instead of later as this has an effect on |
| 4652 | // whether it needs to be spilled in the branches | 4685 | // whether it needs to be spilled in the branches |
| ... | @@ -4674,15 +4707,17 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4674,15 +4707,17 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4674 | | 4707 | |
| 4675 | // Revert to the previous register and stack allocation state. | 4708 | // Revert to the previous register and stack allocation state. |
| 4676 | | 4709 | |
| 4677 | var saved_then_branch = self.branch_stack.pop(); | 4710 | var then_branch = self.branch_stack.pop(); |
| 4678 | defer saved_then_branch.deinit(self.gpa); | 4711 | defer then_branch.deinit(self.gpa); |
| 4679 | | 4712 | |
| 4680 | self.revertState(saved_state); | 4713 | self.revertState(saved_state); |
| 4681 | | 4714 | |
| 4682 | try self.performReloc(reloc); | 4715 | try self.performReloc(reloc); |
| 4683 | | 4716 | |
| 4684 | const else_branch = self.branch_stack.addOneAssumeCapacity(); | 4717 | try self.branch_stack.append(.{}); |
| 4685 | else_branch.* = .{}; | 4718 | errdefer { |
| | 4719 | _ = self.branch_stack.pop(); |
| | 4720 | } |
| 4686 | | 4721 | |
| 4687 | try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len); | 4722 | try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len); |
| 4688 | for (liveness_condbr.else_deaths) |operand| { | 4723 | for (liveness_condbr.else_deaths) |operand| { |
| ... | @@ -4690,6 +4725,9 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4690,6 +4725,9 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4690 | } | 4725 | } |
| 4691 | try self.genBody(else_body); | 4726 | try self.genBody(else_body); |
| 4692 | | 4727 | |
| | 4728 | var else_branch = self.branch_stack.pop(); |
| | 4729 | defer else_branch.deinit(self.gpa); |
| | 4730 | |
| 4693 | // At this point, each branch will possibly have conflicting values for where | 4731 | // At this point, each branch will possibly have conflicting values for where |
| 4694 | // each instruction is stored. They agree, however, on which instructions are alive/dead. | 4732 | // each instruction is stored. They agree, however, on which instructions are alive/dead. |
| 4695 | // We use the first ("then") branch as canonical, and here emit | 4733 | // We use the first ("then") branch as canonical, and here emit |
| ... | @@ -4698,15 +4736,23 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4698,15 +4736,23 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4698 | // that we can use all the code emitting abstractions. This is why at the bottom we | 4736 | // that we can use all the code emitting abstractions. This is why at the bottom we |
| 4699 | // assert that parent_branch.free_registers equals the saved_then_branch.free_registers | 4737 | // assert that parent_branch.free_registers equals the saved_then_branch.free_registers |
| 4700 | // rather than assigning it. | 4738 | // rather than assigning it. |
| 4701 | const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2]; | 4739 | const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 4702 | try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, else_branch.inst_table.count()); | 4740 | try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, else_branch.inst_table.count()); |
| 4703 | | 4741 | |
| | 4742 | log.debug("Upper branches:", .{}); |
| | 4743 | for (self.branch_stack.items) |bs| { |
| | 4744 | log.debug("{}", .{bs.fmtDebug()}); |
| | 4745 | } |
| | 4746 | |
| | 4747 | log.debug("Then branch: {}", .{then_branch.fmtDebug()}); |
| | 4748 | log.debug("Else branch: {}", .{else_branch.fmtDebug()}); |
| | 4749 | |
| 4704 | const else_slice = else_branch.inst_table.entries.slice(); | 4750 | const else_slice = else_branch.inst_table.entries.slice(); |
| 4705 | const else_keys = else_slice.items(.key); | 4751 | const else_keys = else_slice.items(.key); |
| 4706 | const else_values = else_slice.items(.value); | 4752 | const else_values = else_slice.items(.value); |
| 4707 | for (else_keys) |else_key, else_idx| { | 4753 | for (else_keys) |else_key, else_idx| { |
| 4708 | const else_value = else_values[else_idx]; | 4754 | const else_value = else_values[else_idx]; |
| 4709 | const canon_mcv = if (saved_then_branch.inst_table.fetchSwapRemove(else_key)) |then_entry| blk: { | 4755 | const canon_mcv = if (then_branch.inst_table.fetchSwapRemove(else_key)) |then_entry| blk: { |
| 4710 | // The instruction's MCValue is overridden in both branches. | 4756 | // The instruction's MCValue is overridden in both branches. |
| 4711 | parent_branch.inst_table.putAssumeCapacity(else_key, then_entry.value); | 4757 | parent_branch.inst_table.putAssumeCapacity(else_key, then_entry.value); |
| 4712 | if (else_value == .dead) { | 4758 | if (else_value == .dead) { |
| ... | @@ -4718,7 +4764,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4718,7 +4764,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4718 | if (else_value == .dead) | 4764 | if (else_value == .dead) |
| 4719 | continue; | 4765 | continue; |
| 4720 | // The instruction is only overridden in the else branch. | 4766 | // The instruction is only overridden in the else branch. |
| 4721 | var i: usize = self.branch_stack.items.len - 2; | 4767 | var i: usize = self.branch_stack.items.len - 1; |
| 4722 | while (true) { | 4768 | while (true) { |
| 4723 | i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead? | 4769 | i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead? |
| 4724 | if (self.branch_stack.items[i].inst_table.get(else_key)) |mcv| { | 4770 | if (self.branch_stack.items[i].inst_table.get(else_key)) |mcv| { |
| ... | @@ -4733,8 +4779,8 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4733,8 +4779,8 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4733 | try self.setRegOrMem(self.air.typeOfIndex(else_key), canon_mcv, else_value); | 4779 | try self.setRegOrMem(self.air.typeOfIndex(else_key), canon_mcv, else_value); |
| 4734 | // TODO track the new register / stack allocation | 4780 | // TODO track the new register / stack allocation |
| 4735 | } | 4781 | } |
| 4736 | try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count()); | 4782 | try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, then_branch.inst_table.count()); |
| 4737 | const then_slice = saved_then_branch.inst_table.entries.slice(); | 4783 | const then_slice = then_branch.inst_table.entries.slice(); |
| 4738 | const then_keys = then_slice.items(.key); | 4784 | const then_keys = then_slice.items(.key); |
| 4739 | const then_values = then_slice.items(.value); | 4785 | const then_values = then_slice.items(.value); |
| 4740 | for (then_keys) |then_key, then_idx| { | 4786 | for (then_keys) |then_key, then_idx| { |
| ... | @@ -4746,7 +4792,8 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4746,7 +4792,8 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4746 | if (then_value == .dead) | 4792 | if (then_value == .dead) |
| 4747 | continue; | 4793 | continue; |
| 4748 | const parent_mcv = blk: { | 4794 | const parent_mcv = blk: { |
| 4749 | var i: usize = self.branch_stack.items.len - 2; | 4795 | log.debug("{d}", .{self.branch_stack.items.len}); |
| | 4796 | var i: usize = self.branch_stack.items.len - 1; |
| 4750 | while (true) { | 4797 | while (true) { |
| 4751 | i -= 1; | 4798 | i -= 1; |
| 4752 | if (self.branch_stack.items[i].inst_table.get(then_key)) |mcv| { | 4799 | if (self.branch_stack.items[i].inst_table.get(then_key)) |mcv| { |
| ... | @@ -4762,11 +4809,6 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4762,11 +4809,6 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4762 | // TODO track the new register / stack allocation | 4809 | // TODO track the new register / stack allocation |
| 4763 | } | 4810 | } |
| 4764 | | 4811 | |
| 4765 | { | | |
| 4766 | var item = self.branch_stack.pop(); | | |
| 4767 | item.deinit(self.gpa); | | |
| 4768 | } | | |
| 4769 | | | |
| 4770 | // We already took care of pl_op.operand earlier, so we're going | 4812 | // We already took care of pl_op.operand earlier, so we're going |
| 4771 | // to pass .none here | 4813 | // to pass .none here |
| 4772 | return self.finishAir(inst, .unreach, .{ .none, .none, .none }); | 4814 | return self.finishAir(inst, .unreach, .{ .none, .none, .none }); |
| ... | @@ -5139,6 +5181,8 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5139,6 +5181,8 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5139 | ); | 5181 | ); |
| 5140 | defer self.gpa.free(liveness.deaths); | 5182 | defer self.gpa.free(liveness.deaths); |
| 5141 | | 5183 | |
| | 5184 | log.debug("airSwitch: %{d}", .{inst}); |
| | 5185 | |
| 5142 | // If the condition dies here in this switch instruction, process | 5186 | // If the condition dies here in this switch instruction, process |
| 5143 | // that death now instead of later as this has an effect on | 5187 | // that death now instead of later as this has an effect on |
| 5144 | // whether it needs to be spilled in the branches | 5188 | // whether it needs to be spilled in the branches |
| ... | @@ -5150,6 +5194,15 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5150,6 +5194,15 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5150 | } | 5194 | } |
| 5151 | } | 5195 | } |
| 5152 | | 5196 | |
| | 5197 | var branch_stack = std.ArrayList(Branch).init(self.gpa); |
| | 5198 | defer { |
| | 5199 | for (branch_stack.items) |*bs| { |
| | 5200 | bs.deinit(self.gpa); |
| | 5201 | } |
| | 5202 | branch_stack.deinit(); |
| | 5203 | } |
| | 5204 | try branch_stack.ensureTotalCapacityPrecise(switch_br.data.cases_len + 1); |
| | 5205 | |
| 5153 | while (case_i < switch_br.data.cases_len) : (case_i += 1) { | 5206 | while (case_i < switch_br.data.cases_len) : (case_i += 1) { |
| 5154 | const case = self.air.extraData(Air.SwitchBr.Case, extra_index); | 5207 | const case = self.air.extraData(Air.SwitchBr.Case, extra_index); |
| 5155 | const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); | 5208 | const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); |
| ... | @@ -5179,10 +5232,9 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5179,10 +5232,9 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5179 | | 5232 | |
| 5180 | try self.genBody(case_body); | 5233 | try self.genBody(case_body); |
| 5181 | | 5234 | |
| 5182 | // Revert to the previous register and stack allocation state. | 5235 | branch_stack.appendAssumeCapacity(self.branch_stack.pop()); |
| 5183 | var saved_case_branch = self.branch_stack.pop(); | | |
| 5184 | defer saved_case_branch.deinit(self.gpa); | | |
| 5185 | | 5236 | |
| | 5237 | // Revert to the previous register and stack allocation state. |
| 5186 | self.revertState(saved_state); | 5238 | self.revertState(saved_state); |
| 5187 | | 5239 | |
| 5188 | for (relocs) |reloc| { | 5240 | for (relocs) |reloc| { |
| ... | @@ -5192,10 +5244,13 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5192,10 +5244,13 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5192 | | 5244 | |
| 5193 | if (switch_br.data.else_body_len > 0) { | 5245 | if (switch_br.data.else_body_len > 0) { |
| 5194 | const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len]; | 5246 | const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len]; |
| | 5247 | |
| | 5248 | // Capture the state of register and stack allocation state so that we can revert to it. |
| | 5249 | const saved_state = try self.captureState(); |
| | 5250 | |
| 5195 | try self.branch_stack.append(.{}); | 5251 | try self.branch_stack.append(.{}); |
| 5196 | defer { | 5252 | errdefer { |
| 5197 | var item = self.branch_stack.pop(); | 5253 | _ = self.branch_stack.pop(); |
| 5198 | item.deinit(self.gpa); | | |
| 5199 | } | 5254 | } |
| 5200 | | 5255 | |
| 5201 | const else_deaths = liveness.deaths.len - 1; | 5256 | const else_deaths = liveness.deaths.len - 1; |
| ... | @@ -5206,8 +5261,29 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5206,8 +5261,29 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5206 | | 5261 | |
| 5207 | try self.genBody(else_body); | 5262 | try self.genBody(else_body); |
| 5208 | | 5263 | |
| 5209 | // TODO consolidate returned MCValues between prongs and else branch like we do | 5264 | branch_stack.appendAssumeCapacity(self.branch_stack.pop()); |
| 5210 | // in airCondBr. | 5265 | |
| | 5266 | // Revert to the previous register and stack allocation state. |
| | 5267 | self.revertState(saved_state); |
| | 5268 | } |
| | 5269 | |
| | 5270 | // Consolidate returned MCValues between prongs and else branch like we do |
| | 5271 | // in airCondBr. |
| | 5272 | log.debug("Upper branches:", .{}); |
| | 5273 | for (self.branch_stack.items) |bs| { |
| | 5274 | log.debug("{}", .{bs.fmtDebug()}); |
| | 5275 | } |
| | 5276 | for (branch_stack.items) |bs, i| { |
| | 5277 | log.debug("Case-{d} branch: {}", .{ i, bs.fmtDebug() }); |
| | 5278 | } |
| | 5279 | |
| | 5280 | // TODO: can we reduce the complexity of this algorithm? |
| | 5281 | const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| | 5282 | var i: usize = branch_stack.items.len; |
| | 5283 | while (i > 1) : (i -= 1) { |
| | 5284 | const canon_branch = &branch_stack.items[i - 2]; |
| | 5285 | const target_branch = &branch_stack.items[i - 1]; |
| | 5286 | try self.canonicaliseBranches(parent_branch, canon_branch, target_branch); |
| 5211 | } | 5287 | } |
| 5212 | | 5288 | |
| 5213 | // We already took care of pl_op.operand earlier, so we're going | 5289 | // We already took care of pl_op.operand earlier, so we're going |
| ... | @@ -5215,6 +5291,72 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5215,6 +5291,72 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 5215 | return self.finishAir(inst, .unreach, .{ .none, .none, .none }); | 5291 | return self.finishAir(inst, .unreach, .{ .none, .none, .none }); |
| 5216 | } | 5292 | } |
| 5217 | | 5293 | |
| | 5294 | fn canonicaliseBranches(self: *Self, parent_branch: *Branch, canon_branch: *Branch, target_branch: *Branch) !void { |
| | 5295 | try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, target_branch.inst_table.count()); |
| | 5296 | |
| | 5297 | const target_slice = target_branch.inst_table.entries.slice(); |
| | 5298 | const target_keys = target_slice.items(.key); |
| | 5299 | const target_values = target_slice.items(.value); |
| | 5300 | |
| | 5301 | for (target_keys) |target_key, target_idx| { |
| | 5302 | const target_value = target_values[target_idx]; |
| | 5303 | const canon_mcv = if (canon_branch.inst_table.fetchSwapRemove(target_key)) |canon_entry| blk: { |
| | 5304 | // The instruction's MCValue is overridden in both branches. |
| | 5305 | parent_branch.inst_table.putAssumeCapacity(target_key, canon_entry.value); |
| | 5306 | if (target_value == .dead) { |
| | 5307 | assert(canon_entry.value == .dead); |
| | 5308 | continue; |
| | 5309 | } |
| | 5310 | break :blk canon_entry.value; |
| | 5311 | } else blk: { |
| | 5312 | if (target_value == .dead) |
| | 5313 | continue; |
| | 5314 | // The instruction is only overridden in the else branch. |
| | 5315 | var i: usize = self.branch_stack.items.len - 1; |
| | 5316 | while (true) { |
| | 5317 | i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead? |
| | 5318 | if (self.branch_stack.items[i].inst_table.get(target_key)) |mcv| { |
| | 5319 | assert(mcv != .dead); |
| | 5320 | break :blk mcv; |
| | 5321 | } |
| | 5322 | } |
| | 5323 | }; |
| | 5324 | log.debug("consolidating target_entry {d} {}=>{}", .{ target_key, target_value, canon_mcv }); |
| | 5325 | // TODO make sure the destination stack offset / register does not already have something |
| | 5326 | // going on there. |
| | 5327 | try self.setRegOrMem(self.air.typeOfIndex(target_key), canon_mcv, target_value); |
| | 5328 | // TODO track the new register / stack allocation |
| | 5329 | } |
| | 5330 | try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, canon_branch.inst_table.count()); |
| | 5331 | const canon_slice = canon_branch.inst_table.entries.slice(); |
| | 5332 | const canon_keys = canon_slice.items(.key); |
| | 5333 | const canon_values = canon_slice.items(.value); |
| | 5334 | for (canon_keys) |canon_key, canon_idx| { |
| | 5335 | const canon_value = canon_values[canon_idx]; |
| | 5336 | // We already deleted the items from this table that matched the target_branch. |
| | 5337 | // So these are all instructions that are only overridden in the canon branch. |
| | 5338 | parent_branch.inst_table.putAssumeCapacity(canon_key, canon_value); |
| | 5339 | log.debug("canon_value = {}", .{canon_value}); |
| | 5340 | if (canon_value == .dead) |
| | 5341 | continue; |
| | 5342 | const parent_mcv = blk: { |
| | 5343 | var i: usize = self.branch_stack.items.len - 1; |
| | 5344 | while (true) { |
| | 5345 | i -= 1; |
| | 5346 | if (self.branch_stack.items[i].inst_table.get(canon_key)) |mcv| { |
| | 5347 | assert(mcv != .dead); |
| | 5348 | break :blk mcv; |
| | 5349 | } |
| | 5350 | } |
| | 5351 | }; |
| | 5352 | log.debug("consolidating canon_entry {d} {}=>{}", .{ canon_key, parent_mcv, canon_value }); |
| | 5353 | // TODO make sure the destination stack offset / register does not already have something |
| | 5354 | // going on there. |
| | 5355 | try self.setRegOrMem(self.air.typeOfIndex(canon_key), parent_mcv, canon_value); |
| | 5356 | // TODO track the new register / stack allocation |
| | 5357 | } |
| | 5358 | } |
| | 5359 | |
| 5218 | fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void { | 5360 | fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void { |
| 5219 | const next_inst = @intCast(u32, self.mir_instructions.len); | 5361 | const next_inst = @intCast(u32, self.mir_instructions.len); |
| 5220 | switch (self.mir_instructions.items(.tag)[reloc]) { | 5362 | switch (self.mir_instructions.items(.tag)[reloc]) { |