authorgravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-09-01 02:32:33-07:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-09-01 18:31:01+01:00
log0d295d76358037c15d9dc1f56b7b43de29a94d8d
tree1a43fbd65ec21c5eb31cfa40b9c6fb25592db4e1
parent97ed2392033ba1713b6ab16d88d84dd019d6bb2e
signaturelock-open Commit is signed but in an unrecognized format.

riscv: implement `switch_dispatch` & `loop_switch_br`


2 files changed, 115 insertions(+), 15 deletions(-)

src/arch/riscv64/CodeGen.zig+114-15
...@@ -108,7 +108,7 @@ frame_allocs: std.MultiArrayList(FrameAlloc) = .{},...@@ -108,7 +108,7 @@ frame_allocs: std.MultiArrayList(FrameAlloc) = .{},
108free_frame_indices: std.AutoArrayHashMapUnmanaged(FrameIndex, void) = .{},108free_frame_indices: std.AutoArrayHashMapUnmanaged(FrameIndex, void) = .{},
109frame_locs: std.MultiArrayList(Mir.FrameLoc) = .{},109frame_locs: std.MultiArrayList(Mir.FrameLoc) = .{},
110110
111loop_repeat_info: std.AutoHashMapUnmanaged(Air.Inst.Index, struct {111loops: std.AutoHashMapUnmanaged(Air.Inst.Index, struct {
112 /// The state to restore before branching.112 /// The state to restore before branching.
113 state: State,113 state: State,
114 /// The branch target.114 /// The branch target.
...@@ -232,11 +232,12 @@ const MCValue = union(enum) {...@@ -232,11 +232,12 @@ const MCValue = union(enum) {
232 .register,232 .register,
233 .register_pair,233 .register_pair,
234 .register_offset,234 .register_offset,
235 .load_frame,
236 .load_symbol,235 .load_symbol,
237 .load_tlv,236 .load_tlv,
238 .indirect,237 .indirect,
239 => true,238 => true,
239
240 .load_frame => |frame_addr| !frame_addr.index.isNamed(),
240 };241 };
241 }242 }
242243
...@@ -804,7 +805,7 @@ pub fn generate(...@@ -804,7 +805,7 @@ pub fn generate(
804 function.frame_allocs.deinit(gpa);805 function.frame_allocs.deinit(gpa);
805 function.free_frame_indices.deinit(gpa);806 function.free_frame_indices.deinit(gpa);
806 function.frame_locs.deinit(gpa);807 function.frame_locs.deinit(gpa);
807 function.loop_repeat_info.deinit(gpa);808 function.loops.deinit(gpa);
808 var block_it = function.blocks.valueIterator();809 var block_it = function.blocks.valueIterator();
809 while (block_it.next()) |block| block.deinit(gpa);810 while (block_it.next()) |block| block.deinit(gpa);
810 function.blocks.deinit(gpa);811 function.blocks.deinit(gpa);
...@@ -1588,7 +1589,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {...@@ -1588,7 +1589,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
1588 .block => try func.airBlock(inst),1589 .block => try func.airBlock(inst),
1589 .br => try func.airBr(inst),1590 .br => try func.airBr(inst),
1590 .repeat => try func.airRepeat(inst),1591 .repeat => try func.airRepeat(inst),
1591 .switch_dispatch => return func.fail("TODO implement `switch_dispatch`", .{}),1592 .switch_dispatch => try func.airSwitchDispatch(inst),
1592 .trap => try func.airTrap(),1593 .trap => try func.airTrap(),
1593 .breakpoint => try func.airBreakpoint(),1594 .breakpoint => try func.airBreakpoint(),
1594 .ret_addr => try func.airRetAddr(inst),1595 .ret_addr => try func.airRetAddr(inst),
...@@ -1678,7 +1679,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {...@@ -1678,7 +1679,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
1678 .field_parent_ptr => try func.airFieldParentPtr(inst),1679 .field_parent_ptr => try func.airFieldParentPtr(inst),
16791680
1680 .switch_br => try func.airSwitchBr(inst),1681 .switch_br => try func.airSwitchBr(inst),
1681 .loop_switch_br => return func.fail("TODO implement `loop_switch_br`", .{}),1682 .loop_switch_br => try func.airLoopSwitchBr(inst),
16821683
1683 .ptr_slice_len_ptr => try func.airPtrSliceLenPtr(inst),1684 .ptr_slice_len_ptr => try func.airPtrSliceLenPtr(inst),
1684 .ptr_slice_ptr_ptr => try func.airPtrSlicePtrPtr(inst),1685 .ptr_slice_ptr_ptr => try func.airPtrSlicePtrPtr(inst),
...@@ -5610,11 +5611,11 @@ fn airLoop(func: *Func, inst: Air.Inst.Index) !void {...@@ -5610,11 +5611,11 @@ fn airLoop(func: *Func, inst: Air.Inst.Index) !void {
5610 func.scope_generation += 1;5611 func.scope_generation += 1;
5611 const state = try func.saveState();5612 const state = try func.saveState();
56125613
5613 try func.loop_repeat_info.putNoClobber(func.gpa, inst, .{5614 try func.loops.putNoClobber(func.gpa, inst, .{
5614 .state = state,5615 .state = state,
5615 .jmp_target = @intCast(func.mir_instructions.len),5616 .jmp_target = @intCast(func.mir_instructions.len),
5616 });5617 });
5617 defer assert(func.loop_repeat_info.remove(inst));5618 defer assert(func.loops.remove(inst));
56185619
5619 try func.genBody(body);5620 try func.genBody(body);
56205621
...@@ -5671,12 +5672,7 @@ fn lowerBlock(func: *Func, inst: Air.Inst.Index, body: []const Air.Inst.Index) !...@@ -5671,12 +5672,7 @@ fn lowerBlock(func: *Func, inst: Air.Inst.Index, body: []const Air.Inst.Index) !
56715672
5672fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void {5673fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void {
5673 const switch_br = func.air.unwrapSwitch(inst);5674 const switch_br = func.air.unwrapSwitch(inst);
5674
5675 const liveness = try func.liveness.getSwitchBr(func.gpa, inst, switch_br.cases_len + 1);
5676 defer func.gpa.free(liveness.deaths);
5677
5678 const condition = try func.resolveInst(switch_br.operand);5675 const condition = try func.resolveInst(switch_br.operand);
5679 const condition_ty = func.typeOf(switch_br.operand);
56805676
5681 // If the condition dies here in this switch instruction, process5677 // If the condition dies here in this switch instruction, process
5682 // that death now instead of later as this has an effect on5678 // that death now instead of later as this has an effect on
...@@ -5685,6 +5681,22 @@ fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void {...@@ -5685,6 +5681,22 @@ fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void {
5685 if (switch_br.operand.toIndex()) |op_inst| try func.processDeath(op_inst);5681 if (switch_br.operand.toIndex()) |op_inst| try func.processDeath(op_inst);
5686 }5682 }
56875683
5684 try func.lowerSwitchBr(inst, switch_br, condition);
5685
5686 // We already took care of pl_op.operand earlier, so there's nothing left to do
5687 func.finishAirBookkeeping();
5688}
5689
5690fn lowerSwitchBr(
5691 func: *Func,
5692 inst: Air.Inst.Index,
5693 switch_br: Air.UnwrappedSwitch,
5694 condition: MCValue,
5695) !void {
5696 const condition_ty = func.typeOf(switch_br.operand);
5697 const liveness = try func.liveness.getSwitchBr(func.gpa, inst, switch_br.cases_len + 1);
5698 defer func.gpa.free(liveness.deaths);
5699
5688 func.scope_generation += 1;5700 func.scope_generation += 1;
5689 const state = try func.saveState();5701 const state = try func.saveState();
56905702
...@@ -5785,8 +5797,92 @@ fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void {...@@ -5785,8 +5797,92 @@ fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void {
5785 .close_scope = true,5797 .close_scope = true,
5786 });5798 });
5787 }5799 }
5800}
5801
5802fn airLoopSwitchBr(func: *Func, inst: Air.Inst.Index) !void {
5803 const switch_br = func.air.unwrapSwitch(inst);
5804 const condition = try func.resolveInst(switch_br.operand);
5805
5806 const mat_cond = if (condition.isMutable() and
5807 func.reuseOperand(inst, switch_br.operand, 0, condition))
5808 condition
5809 else mat_cond: {
5810 const ty = func.typeOf(switch_br.operand);
5811 const mat_cond = try func.allocRegOrMem(ty, inst, true);
5812 try func.genCopy(ty, mat_cond, condition);
5813 break :mat_cond mat_cond;
5814 };
5815 func.inst_tracking.putAssumeCapacityNoClobber(inst, InstTracking.init(mat_cond));
5816
5817 // If the condition dies here in this switch instruction, process
5818 // that death now instead of later as this has an effect on
5819 // whether it needs to be spilled in the branches
5820 if (func.liveness.operandDies(inst, 0)) {
5821 if (switch_br.operand.toIndex()) |op_inst| try func.processDeath(op_inst);
5822 }
5823
5824 func.scope_generation += 1;
5825 const state = try func.saveState();
5826
5827 try func.loops.putNoClobber(func.gpa, inst, .{
5828 .state = state,
5829 .jmp_target = @intCast(func.mir_instructions.len),
5830 });
5831 defer assert(func.loops.remove(inst));
5832
5833 // Stop tracking block result without forgetting tracking info
5834 try func.freeValue(mat_cond);
5835
5836 try func.lowerSwitchBr(inst, switch_br, mat_cond);
5837
5838 try func.processDeath(inst);
5839 func.finishAirBookkeeping();
5840}
5841
5842fn airSwitchDispatch(func: *Func, inst: Air.Inst.Index) !void {
5843 const br = func.air.instructions.items(.data)[@intFromEnum(inst)].br;
5844
5845 const block_ty = func.typeOfIndex(br.block_inst);
5846 const block_tracking = func.inst_tracking.getPtr(br.block_inst).?;
5847 const loop_data = func.loops.getPtr(br.block_inst).?;
5848 done: {
5849 try func.getValue(block_tracking.short, null);
5850 const src_mcv = try func.resolveInst(br.operand);
5851
5852 if (func.reuseOperandAdvanced(inst, br.operand, 0, src_mcv, br.block_inst)) {
5853 try func.getValue(block_tracking.short, br.block_inst);
5854 // .long = .none to avoid merging operand and block result stack frames.
5855 const current_tracking: InstTracking = .{ .long = .none, .short = src_mcv };
5856 try current_tracking.materializeUnsafe(func, br.block_inst, block_tracking.*);
5857 for (current_tracking.getRegs()) |src_reg| func.register_manager.freeReg(src_reg);
5858 break :done;
5859 }
5860
5861 try func.getValue(block_tracking.short, br.block_inst);
5862 const dst_mcv = block_tracking.short;
5863 try func.genCopy(block_ty, dst_mcv, try func.resolveInst(br.operand));
5864 break :done;
5865 }
5866
5867 // Process operand death so that it is properly accounted for in the State below.
5868 if (func.liveness.operandDies(inst, 0)) {
5869 if (br.operand.toIndex()) |op_inst| try func.processDeath(op_inst);
5870 }
5871
5872 try func.restoreState(loop_data.state, &.{}, .{
5873 .emit_instructions = true,
5874 .update_tracking = false,
5875 .resurrect = false,
5876 .close_scope = false,
5877 });
5878
5879 // Emit a jump with a relocation. It will be patched up after the block ends.
5880 // Leave the jump offset undefined
5881 _ = try func.jump(loop_data.jmp_target);
5882
5883 // Stop tracking block result without forgetting tracking info
5884 try func.freeValue(block_tracking.short);
57885885
5789 // We already took care of pl_op.operand earlier, so there's nothing left to do
5790 func.finishAirBookkeeping();5886 func.finishAirBookkeeping();
5791}5887}
57925888
...@@ -5867,7 +5963,7 @@ fn airBr(func: *Func, inst: Air.Inst.Index) !void {...@@ -5867,7 +5963,7 @@ fn airBr(func: *Func, inst: Air.Inst.Index) !void {
58675963
5868fn airRepeat(func: *Func, inst: Air.Inst.Index) !void {5964fn airRepeat(func: *Func, inst: Air.Inst.Index) !void {
5869 const loop_inst = func.air.instructions.items(.data)[@intFromEnum(inst)].repeat.loop_inst;5965 const loop_inst = func.air.instructions.items(.data)[@intFromEnum(inst)].repeat.loop_inst;
5870 const repeat_info = func.loop_repeat_info.get(loop_inst).?;5966 const repeat_info = func.loops.get(loop_inst).?;
5871 try func.restoreState(repeat_info.state, &.{}, .{5967 try func.restoreState(repeat_info.state, &.{}, .{
5872 .emit_instructions = true,5968 .emit_instructions = true,
5873 .update_tracking = false,5969 .update_tracking = false,
...@@ -8298,7 +8394,10 @@ fn typeOf(func: *Func, inst: Air.Inst.Ref) Type {...@@ -8298,7 +8394,10 @@ fn typeOf(func: *Func, inst: Air.Inst.Ref) Type {
82988394
8299fn typeOfIndex(func: *Func, inst: Air.Inst.Index) Type {8395fn typeOfIndex(func: *Func, inst: Air.Inst.Index) Type {
8300 const zcu = func.pt.zcu;8396 const zcu = func.pt.zcu;
8301 return func.air.typeOfIndex(inst, &zcu.intern_pool);8397 return switch (func.air.instructions.items(.tag)[@intFromEnum(inst)]) {
8398 .loop_switch_br => func.typeOf(func.air.unwrapSwitch(inst).operand),
8399 else => func.air.typeOfIndex(inst, &zcu.intern_pool),
8400 };
8302}8401}
83038402
8304fn hasFeature(func: *Func, feature: Target.riscv.Feature) bool {8403fn hasFeature(func: *Func, feature: Target.riscv.Feature) bool {
test/behavior/switch_loop.zig+1
...@@ -80,6 +80,7 @@ test "switch loop on tagged union" {...@@ -80,6 +80,7 @@ test "switch loop on tagged union" {
80 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO80 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
81 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO81 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
82 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO82 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
83 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
8384
84 const S = struct {85 const S = struct {
85 const U = union(enum) {86 const U = union(enum) {