| ... | ... | @@ -105,6 +105,13 @@ frame_allocs: std.MultiArrayList(FrameAlloc) = .{}, |
| 105 | 105 | free_frame_indices: std.AutoArrayHashMapUnmanaged(FrameIndex, void) = .{}, |
| 106 | 106 | frame_locs: std.MultiArrayList(Mir.FrameLoc) = .{}, |
| 107 | 107 | |
| 108 | loop_repeat_info: std.AutoHashMapUnmanaged(Air.Inst.Index, struct { |
| 109 | /// The state to restore before branching. |
| 110 | state: State, |
| 111 | /// The branch target. |
| 112 | jmp_target: Mir.Inst.Index, |
| 113 | }) = .{}, |
| 114 | |
| 108 | 115 | /// Debug field, used to find bugs in the compiler. |
| 109 | 116 | air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, |
| 110 | 117 | |
| ... | ... | @@ -815,6 +822,7 @@ pub fn generate( |
| 815 | 822 | function.frame_allocs.deinit(gpa); |
| 816 | 823 | function.free_frame_indices.deinit(gpa); |
| 817 | 824 | function.frame_locs.deinit(gpa); |
| 825 | function.loop_repeat_info.deinit(gpa); |
| 818 | 826 | var block_it = function.blocks.valueIterator(); |
| 819 | 827 | while (block_it.next()) |block| block.deinit(gpa); |
| 820 | 828 | function.blocks.deinit(gpa); |
| ... | ... | @@ -2247,7 +2255,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 2247 | 2255 | .bitcast => try self.airBitCast(inst), |
| 2248 | 2256 | .block => try self.airBlock(inst), |
| 2249 | 2257 | .br => try self.airBr(inst), |
| 2250 | | .repeat => return self.fail("TODO implement `repeat`", .{}), |
| 2258 | .repeat => try self.airRepeat(inst), |
| 2251 | 2259 | .switch_dispatch => return self.fail("TODO implement `switch_dispatch`", .{}), |
| 2252 | 2260 | .trap => try self.airTrap(), |
| 2253 | 2261 | .breakpoint => try self.airBreakpoint(), |
| ... | ... | @@ -13629,15 +13637,13 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { |
| 13629 | 13637 | self.scope_generation += 1; |
| 13630 | 13638 | const state = try self.saveState(); |
| 13631 | 13639 | |
| 13632 | | const jmp_target: Mir.Inst.Index = @intCast(self.mir_instructions.len); |
| 13633 | | try self.genBody(body); |
| 13634 | | try self.restoreState(state, &.{}, .{ |
| 13635 | | .emit_instructions = true, |
| 13636 | | .update_tracking = false, |
| 13637 | | .resurrect = false, |
| 13638 | | .close_scope = true, |
| 13640 | try self.loop_repeat_info.putNoClobber(self.gpa, inst, .{ |
| 13641 | .state = state, |
| 13642 | .jmp_target = @intCast(self.mir_instructions.len), |
| 13639 | 13643 | }); |
| 13640 | | _ = try self.asmJmpReloc(jmp_target); |
| 13644 | defer assert(self.loop_repeat_info.remove(inst)); |
| 13645 | |
| 13646 | try self.genBody(body); |
| 13641 | 13647 | |
| 13642 | 13648 | self.finishAirBookkeeping(); |
| 13643 | 13649 | } |
| ... | ... | @@ -13680,12 +13686,19 @@ fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) ! |
| 13680 | 13686 | } |
| 13681 | 13687 | |
| 13682 | 13688 | fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void { |
| 13689 | const zcu = self.pt.zcu; |
| 13683 | 13690 | const switch_br = self.air.unwrapSwitch(inst); |
| 13684 | 13691 | const condition = try self.resolveInst(switch_br.operand); |
| 13685 | 13692 | const condition_ty = self.typeOf(switch_br.operand); |
| 13686 | 13693 | const liveness = try self.liveness.getSwitchBr(self.gpa, inst, switch_br.cases_len + 1); |
| 13687 | 13694 | defer self.gpa.free(liveness.deaths); |
| 13688 | 13695 | |
| 13696 | const signedness = switch (condition_ty.zigTypeTag(zcu)) { |
| 13697 | .bool, .pointer => .unsigned, |
| 13698 | .int, .@"enum", .error_set => condition_ty.intInfo(zcu).signedness, |
| 13699 | else => unreachable, |
| 13700 | }; |
| 13701 | |
| 13689 | 13702 | // If the condition dies here in this switch instruction, process |
| 13690 | 13703 | // that death now instead of later as this has an effect on |
| 13691 | 13704 | // whether it needs to be spilled in the branches |
| ... | ... | @@ -13698,13 +13711,11 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void { |
| 13698 | 13711 | |
| 13699 | 13712 | var it = switch_br.iterateCases(); |
| 13700 | 13713 | while (it.next()) |case| { |
| 13701 | | if (case.ranges.len > 0) return self.fail("TODO: switch with ranges", .{}); |
| 13702 | | |
| 13703 | | var relocs = try self.gpa.alloc(Mir.Inst.Index, case.items.len); |
| 13714 | var relocs = try self.gpa.alloc(Mir.Inst.Index, case.items.len + case.ranges.len); |
| 13704 | 13715 | defer self.gpa.free(relocs); |
| 13705 | 13716 | |
| 13706 | 13717 | try self.spillEflagsIfOccupied(); |
| 13707 | | for (case.items, relocs, 0..) |item, *reloc, i| { |
| 13718 | for (case.items, relocs[0..case.items.len]) |item, *reloc| { |
| 13708 | 13719 | const item_mcv = try self.resolveInst(item); |
| 13709 | 13720 | const cc: Condition = switch (condition) { |
| 13710 | 13721 | .eflags => |cc| switch (item_mcv.immediate) { |
| ... | ... | @@ -13717,12 +13728,62 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void { |
| 13717 | 13728 | break :cc .e; |
| 13718 | 13729 | }, |
| 13719 | 13730 | }; |
| 13720 | | reloc.* = try self.asmJccReloc(if (i < relocs.len - 1) cc else cc.negate(), undefined); |
| 13731 | reloc.* = try self.asmJccReloc(cc, undefined); |
| 13721 | 13732 | } |
| 13722 | 13733 | |
| 13734 | for (case.ranges, relocs[case.items.len..]) |range, *reloc| { |
| 13735 | const min_mcv = try self.resolveInst(range[0]); |
| 13736 | const max_mcv = try self.resolveInst(range[1]); |
| 13737 | // `null` means always false. |
| 13738 | const lt_min: ?Condition = switch (condition) { |
| 13739 | .eflags => |cc| switch (min_mcv.immediate) { |
| 13740 | 0 => null, // condition never <0 |
| 13741 | 1 => cc.negate(), |
| 13742 | else => unreachable, |
| 13743 | }, |
| 13744 | else => cc: { |
| 13745 | try self.genBinOpMir(.{ ._, .cmp }, condition_ty, condition, min_mcv); |
| 13746 | break :cc switch (signedness) { |
| 13747 | .unsigned => .b, |
| 13748 | .signed => .l, |
| 13749 | }; |
| 13750 | }, |
| 13751 | }; |
| 13752 | const lt_min_reloc = if (lt_min) |cc| r: { |
| 13753 | break :r try self.asmJccReloc(cc, undefined); |
| 13754 | } else null; |
| 13755 | // `null` means always true. |
| 13756 | const lte_max: ?Condition = switch (condition) { |
| 13757 | .eflags => |cc| switch (max_mcv.immediate) { |
| 13758 | 0 => cc.negate(), |
| 13759 | 1 => null, // condition always >=1 |
| 13760 | else => unreachable, |
| 13761 | }, |
| 13762 | else => cc: { |
| 13763 | try self.genBinOpMir(.{ ._, .cmp }, condition_ty, condition, max_mcv); |
| 13764 | break :cc switch (signedness) { |
| 13765 | .unsigned => .be, |
| 13766 | .signed => .le, |
| 13767 | }; |
| 13768 | }, |
| 13769 | }; |
| 13770 | // "Success" case is in `reloc`.... |
| 13771 | if (lte_max) |cc| { |
| 13772 | reloc.* = try self.asmJccReloc(cc, undefined); |
| 13773 | } else { |
| 13774 | reloc.* = try self.asmJmpReloc(undefined); |
| 13775 | } |
| 13776 | // ...and "fail" case falls through to next checks. |
| 13777 | if (lt_min_reloc) |r| self.performReloc(r); |
| 13778 | } |
| 13779 | |
| 13780 | // The jump to skip this case if the conditions all failed. |
| 13781 | const skip_case_reloc = try self.asmJmpReloc(undefined); |
| 13782 | |
| 13723 | 13783 | for (liveness.deaths[case.idx]) |operand| try self.processDeath(operand); |
| 13724 | 13784 | |
| 13725 | | for (relocs[0 .. relocs.len - 1]) |reloc| self.performReloc(reloc); |
| 13785 | // Relocate all success cases to the body we're about to generate. |
| 13786 | for (relocs) |reloc| self.performReloc(reloc); |
| 13726 | 13787 | try self.genBody(case.body); |
| 13727 | 13788 | try self.restoreState(state, &.{}, .{ |
| 13728 | 13789 | .emit_instructions = false, |
| ... | ... | @@ -13731,7 +13792,8 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void { |
| 13731 | 13792 | .close_scope = true, |
| 13732 | 13793 | }); |
| 13733 | 13794 | |
| 13734 | | self.performReloc(relocs[relocs.len - 1]); |
| 13795 | // Relocate the "skip" branch to fall through to the next case. |
| 13796 | self.performReloc(skip_case_reloc); |
| 13735 | 13797 | } |
| 13736 | 13798 | |
| 13737 | 13799 | if (switch_br.else_body_len > 0) { |
| ... | ... | @@ -13827,6 +13889,19 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void { |
| 13827 | 13889 | self.finishAirBookkeeping(); |
| 13828 | 13890 | } |
| 13829 | 13891 | |
| 13892 | fn airRepeat(self: *Self, inst: Air.Inst.Index) !void { |
| 13893 | const loop_inst = self.air.instructions.items(.data)[@intFromEnum(inst)].repeat.loop_inst; |
| 13894 | const repeat_info = self.loop_repeat_info.get(loop_inst).?; |
| 13895 | try self.restoreState(repeat_info.state, &.{}, .{ |
| 13896 | .emit_instructions = true, |
| 13897 | .update_tracking = false, |
| 13898 | .resurrect = false, |
| 13899 | .close_scope = true, |
| 13900 | }); |
| 13901 | _ = try self.asmJmpReloc(repeat_info.jmp_target); |
| 13902 | self.finishAirBookkeeping(); |
| 13903 | } |
| 13904 | |
| 13830 | 13905 | fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 13831 | 13906 | const pt = self.pt; |
| 13832 | 13907 | const zcu = pt.zcu; |