| ... | @@ -105,6 +105,13 @@ frame_allocs: std.MultiArrayList(FrameAlloc) = .{}, | ... | @@ -105,6 +105,13 @@ frame_allocs: std.MultiArrayList(FrameAlloc) = .{}, |
| 105 | free_frame_indices: std.AutoArrayHashMapUnmanaged(FrameIndex, void) = .{}, | 105 | free_frame_indices: std.AutoArrayHashMapUnmanaged(FrameIndex, void) = .{}, |
| 106 | frame_locs: std.MultiArrayList(Mir.FrameLoc) = .{}, | 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 | /// Debug field, used to find bugs in the compiler. | 115 | /// Debug field, used to find bugs in the compiler. |
| 109 | air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, | 116 | air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, |
| 110 | | 117 | |
| ... | @@ -815,6 +822,7 @@ pub fn generate( | ... | @@ -815,6 +822,7 @@ pub fn generate( |
| 815 | function.frame_allocs.deinit(gpa); | 822 | function.frame_allocs.deinit(gpa); |
| 816 | function.free_frame_indices.deinit(gpa); | 823 | function.free_frame_indices.deinit(gpa); |
| 817 | function.frame_locs.deinit(gpa); | 824 | function.frame_locs.deinit(gpa); |
| | 825 | function.loop_repeat_info.deinit(gpa); |
| 818 | var block_it = function.blocks.valueIterator(); | 826 | var block_it = function.blocks.valueIterator(); |
| 819 | while (block_it.next()) |block| block.deinit(gpa); | 827 | while (block_it.next()) |block| block.deinit(gpa); |
| 820 | function.blocks.deinit(gpa); | 828 | function.blocks.deinit(gpa); |
| ... | @@ -2247,7 +2255,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -2247,7 +2255,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 2247 | .bitcast => try self.airBitCast(inst), | 2255 | .bitcast => try self.airBitCast(inst), |
| 2248 | .block => try self.airBlock(inst), | 2256 | .block => try self.airBlock(inst), |
| 2249 | .br => try self.airBr(inst), | 2257 | .br => try self.airBr(inst), |
| 2250 | .repeat => return self.fail("TODO implement `repeat`", .{}), | 2258 | .repeat => try self.airRepeat(inst), |
| 2251 | .switch_dispatch => return self.fail("TODO implement `switch_dispatch`", .{}), | 2259 | .switch_dispatch => return self.fail("TODO implement `switch_dispatch`", .{}), |
| 2252 | .trap => try self.airTrap(), | 2260 | .trap => try self.airTrap(), |
| 2253 | .breakpoint => try self.airBreakpoint(), | 2261 | .breakpoint => try self.airBreakpoint(), |
| ... | @@ -13629,15 +13637,13 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -13629,15 +13637,13 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { |
| 13629 | self.scope_generation += 1; | 13637 | self.scope_generation += 1; |
| 13630 | const state = try self.saveState(); | 13638 | const state = try self.saveState(); |
| 13631 | | 13639 | |
| 13632 | const jmp_target: Mir.Inst.Index = @intCast(self.mir_instructions.len); | 13640 | try self.loop_repeat_info.putNoClobber(self.gpa, inst, .{ |
| 13633 | try self.genBody(body); | 13641 | .state = state, |
| 13634 | try self.restoreState(state, &.{}, .{ | 13642 | .jmp_target = @intCast(self.mir_instructions.len), |
| 13635 | .emit_instructions = true, | | |
| 13636 | .update_tracking = false, | | |
| 13637 | .resurrect = false, | | |
| 13638 | .close_scope = true, | | |
| 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 | self.finishAirBookkeeping(); | 13648 | self.finishAirBookkeeping(); |
| 13643 | } | 13649 | } |
| ... | @@ -13680,12 +13686,19 @@ fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) ! | ... | @@ -13680,12 +13686,19 @@ fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) ! |
| 13680 | } | 13686 | } |
| 13681 | | 13687 | |
| 13682 | fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void { | 13688 | fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void { |
| | 13689 | const zcu = self.pt.zcu; |
| 13683 | const switch_br = self.air.unwrapSwitch(inst); | 13690 | const switch_br = self.air.unwrapSwitch(inst); |
| 13684 | const condition = try self.resolveInst(switch_br.operand); | 13691 | const condition = try self.resolveInst(switch_br.operand); |
| 13685 | const condition_ty = self.typeOf(switch_br.operand); | 13692 | const condition_ty = self.typeOf(switch_br.operand); |
| 13686 | const liveness = try self.liveness.getSwitchBr(self.gpa, inst, switch_br.cases_len + 1); | 13693 | const liveness = try self.liveness.getSwitchBr(self.gpa, inst, switch_br.cases_len + 1); |
| 13687 | defer self.gpa.free(liveness.deaths); | 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 | // If the condition dies here in this switch instruction, process | 13702 | // If the condition dies here in this switch instruction, process |
| 13690 | // that death now instead of later as this has an effect on | 13703 | // that death now instead of later as this has an effect on |
| 13691 | // whether it needs to be spilled in the branches | 13704 | // whether it needs to be spilled in the branches |
| ... | @@ -13698,13 +13711,11 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -13698,13 +13711,11 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void { |
| 13698 | | 13711 | |
| 13699 | var it = switch_br.iterateCases(); | 13712 | var it = switch_br.iterateCases(); |
| 13700 | while (it.next()) |case| { | 13713 | while (it.next()) |case| { |
| 13701 | if (case.ranges.len > 0) return self.fail("TODO: switch with ranges", .{}); | 13714 | var relocs = try self.gpa.alloc(Mir.Inst.Index, case.items.len + case.ranges.len); |
| 13702 | | | |
| 13703 | var relocs = try self.gpa.alloc(Mir.Inst.Index, case.items.len); | | |
| 13704 | defer self.gpa.free(relocs); | 13715 | defer self.gpa.free(relocs); |
| 13705 | | 13716 | |
| 13706 | try self.spillEflagsIfOccupied(); | 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 | const item_mcv = try self.resolveInst(item); | 13719 | const item_mcv = try self.resolveInst(item); |
| 13709 | const cc: Condition = switch (condition) { | 13720 | const cc: Condition = switch (condition) { |
| 13710 | .eflags => |cc| switch (item_mcv.immediate) { | 13721 | .eflags => |cc| switch (item_mcv.immediate) { |
| ... | @@ -13717,12 +13728,62 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -13717,12 +13728,62 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void { |
| 13717 | break :cc .e; | 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 | for (liveness.deaths[case.idx]) |operand| try self.processDeath(operand); | 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 | try self.genBody(case.body); | 13787 | try self.genBody(case.body); |
| 13727 | try self.restoreState(state, &.{}, .{ | 13788 | try self.restoreState(state, &.{}, .{ |
| 13728 | .emit_instructions = false, | 13789 | .emit_instructions = false, |
| ... | @@ -13731,7 +13792,8 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -13731,7 +13792,8 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void { |
| 13731 | .close_scope = true, | 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 | if (switch_br.else_body_len > 0) { | 13799 | if (switch_br.else_body_len > 0) { |
| ... | @@ -13827,6 +13889,19 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -13827,6 +13889,19 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void { |
| 13827 | self.finishAirBookkeeping(); | 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 | fn airAsm(self: *Self, inst: Air.Inst.Index) !void { | 13905 | fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 13831 | const pt = self.pt; | 13906 | const pt = self.pt; |
| 13832 | const zcu = pt.zcu; | 13907 | const zcu = pt.zcu; |