Skip to content

Commit 0d295d7

Browse files
Rexicon226mlugg
authored andcommitted
riscv: implement switch_dispatch & loop_switch_br
1 parent 97ed239 commit 0d295d7

File tree

2 files changed

+115
-15
lines changed

2 files changed

+115
-15
lines changed

src/arch/riscv64/CodeGen.zig

Lines changed: 114 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ frame_allocs: std.MultiArrayList(FrameAlloc) = .{},
108108
free_frame_indices: std.AutoArrayHashMapUnmanaged(FrameIndex, void) = .{},
109109
frame_locs: std.MultiArrayList(Mir.FrameLoc) = .{},
110110

111-
loop_repeat_info: std.AutoHashMapUnmanaged(Air.Inst.Index, struct {
111+
loops: std.AutoHashMapUnmanaged(Air.Inst.Index, struct {
112112
/// The state to restore before branching.
113113
state: State,
114114
/// The branch target.
@@ -232,11 +232,12 @@ const MCValue = union(enum) {
232232
.register,
233233
.register_pair,
234234
.register_offset,
235-
.load_frame,
236235
.load_symbol,
237236
.load_tlv,
238237
.indirect,
239238
=> true,
239+
240+
.load_frame => |frame_addr| !frame_addr.index.isNamed(),
240241
};
241242
}
242243

@@ -804,7 +805,7 @@ pub fn generate(
804805
function.frame_allocs.deinit(gpa);
805806
function.free_frame_indices.deinit(gpa);
806807
function.frame_locs.deinit(gpa);
807-
function.loop_repeat_info.deinit(gpa);
808+
function.loops.deinit(gpa);
808809
var block_it = function.blocks.valueIterator();
809810
while (block_it.next()) |block| block.deinit(gpa);
810811
function.blocks.deinit(gpa);
@@ -1588,7 +1589,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
15881589
.block => try func.airBlock(inst),
15891590
.br => try func.airBr(inst),
15901591
.repeat => try func.airRepeat(inst),
1591-
.switch_dispatch => return func.fail("TODO implement `switch_dispatch`", .{}),
1592+
.switch_dispatch => try func.airSwitchDispatch(inst),
15921593
.trap => try func.airTrap(),
15931594
.breakpoint => try func.airBreakpoint(),
15941595
.ret_addr => try func.airRetAddr(inst),
@@ -1678,7 +1679,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
16781679
.field_parent_ptr => try func.airFieldParentPtr(inst),
16791680

16801681
.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

16831684
.ptr_slice_len_ptr => try func.airPtrSliceLenPtr(inst),
16841685
.ptr_slice_ptr_ptr => try func.airPtrSlicePtrPtr(inst),
@@ -5610,11 +5611,11 @@ fn airLoop(func: *Func, inst: Air.Inst.Index) !void {
56105611
func.scope_generation += 1;
56115612
const state = try func.saveState();
56125613

5613-
try func.loop_repeat_info.putNoClobber(func.gpa, inst, .{
5614+
try func.loops.putNoClobber(func.gpa, inst, .{
56145615
.state = state,
56155616
.jmp_target = @intCast(func.mir_instructions.len),
56165617
});
5617-
defer assert(func.loop_repeat_info.remove(inst));
5618+
defer assert(func.loops.remove(inst));
56185619

56195620
try func.genBody(body);
56205621

@@ -5671,12 +5672,7 @@ fn lowerBlock(func: *Func, inst: Air.Inst.Index, body: []const Air.Inst.Index) !
56715672

56725673
fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void {
56735674
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-
56785675
const condition = try func.resolveInst(switch_br.operand);
5679-
const condition_ty = func.typeOf(switch_br.operand);
56805676

56815677
// If the condition dies here in this switch instruction, process
56825678
// 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 {
56855681
if (switch_br.operand.toIndex()) |op_inst| try func.processDeath(op_inst);
56865682
}
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+
5690+
fn 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+
56885700
func.scope_generation += 1;
56895701
const state = try func.saveState();
56905702

@@ -5785,8 +5797,92 @@ fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void {
57855797
.close_scope = true,
57865798
});
57875799
}
5800+
}
5801+
5802+
fn 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+
5842+
fn 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
57905886
func.finishAirBookkeeping();
57915887
}
57925888

@@ -5867,7 +5963,7 @@ fn airBr(func: *Func, inst: Air.Inst.Index) !void {
58675963

58685964
fn airRepeat(func: *Func, inst: Air.Inst.Index) !void {
58695965
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).?;
58715967
try func.restoreState(repeat_info.state, &.{}, .{
58725968
.emit_instructions = true,
58735969
.update_tracking = false,
@@ -8298,7 +8394,10 @@ fn typeOf(func: *Func, inst: Air.Inst.Ref) Type {
82988394

82998395
fn typeOfIndex(func: *Func, inst: Air.Inst.Index) Type {
83008396
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+
};
83028401
}
83038402

83048403
fn hasFeature(func: *Func, feature: Target.riscv.Feature) bool {

test/behavior/switch_loop.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ test "switch loop on tagged union" {
8080
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
8181
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
8282
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
83+
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
8384

8485
const S = struct {
8586
const U = union(enum) {

0 commit comments

Comments
 (0)