diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index e810c6ee30..e29b12e8a4 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -108,6 +108,13 @@ frame_allocs: std.MultiArrayList(FrameAlloc) = .{}, free_frame_indices: std.AutoArrayHashMapUnmanaged(FrameIndex, void) = .{}, frame_locs: std.MultiArrayList(Mir.FrameLoc) = .{}, +loop_repeat_info: std.AutoHashMapUnmanaged(Air.Inst.Index, struct { + /// The state to restore before branching. + state: State, + /// The branch target. + jmp_target: Mir.Inst.Index, +}) = .{}, + /// Debug field, used to find bugs in the compiler. air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, @@ -797,6 +804,7 @@ pub fn generate( function.frame_allocs.deinit(gpa); function.free_frame_indices.deinit(gpa); function.frame_locs.deinit(gpa); + function.loop_repeat_info.deinit(gpa); var block_it = function.blocks.valueIterator(); while (block_it.next()) |block| block.deinit(gpa); function.blocks.deinit(gpa); @@ -1579,7 +1587,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { .bitcast => try func.airBitCast(inst), .block => try func.airBlock(inst), .br => try func.airBr(inst), - .repeat => return func.fail("TODO implement `repeat`", .{}), + .repeat => try func.airRepeat(inst), .switch_dispatch => return func.fail("TODO implement `switch_dispatch`", .{}), .trap => try func.airTrap(), .breakpoint => try func.airBreakpoint(), @@ -5602,15 +5610,13 @@ fn airLoop(func: *Func, inst: Air.Inst.Index) !void { func.scope_generation += 1; const state = try func.saveState(); - const jmp_target: Mir.Inst.Index = @intCast(func.mir_instructions.len); - try func.genBody(body); - try func.restoreState(state, &.{}, .{ - .emit_instructions = true, - .update_tracking = false, - .resurrect = false, - .close_scope = true, + try func.loop_repeat_info.putNoClobber(func.gpa, inst, .{ + .state = state, + .jmp_target = @intCast(func.mir_instructions.len), }); - _ = try func.jump(jmp_target); + defer assert(func.loop_repeat_info.remove(inst)); + + try func.genBody(body); func.finishAirBookkeeping(); } @@ -5684,12 +5690,10 @@ fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void { var it = switch_br.iterateCases(); while (it.next()) |case| { - if (case.ranges.len > 0) return func.fail("TODO: switch with ranges", .{}); - - var relocs = try func.gpa.alloc(Mir.Inst.Index, case.items.len); + var relocs = try func.gpa.alloc(Mir.Inst.Index, case.items.len + case.ranges.len); defer func.gpa.free(relocs); - for (case.items, relocs, 0..) |item, *reloc, i| { + for (case.items, relocs[0..case.items.len]) |item, *reloc| { const item_mcv = try func.resolveInst(item); const cond_lock = switch (condition) { @@ -5710,22 +5714,52 @@ fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void { cmp_reg, ); - if (!(i < relocs.len - 1)) { - _ = try func.addInst(.{ - .tag = .pseudo_not, - .data = .{ .rr = .{ - .rd = cmp_reg, - .rs = cmp_reg, - } }, - }); - } - reloc.* = try func.condBr(condition_ty, .{ .register = cmp_reg }); } + for (case.ranges, relocs[case.items.len..]) |range, *reloc| { + const min_mcv = try func.resolveInst(range[0]); + const max_mcv = try func.resolveInst(range[1]); + const cond_lock = switch (condition) { + .register => func.register_manager.lockRegAssumeUnused(condition.register), + else => null, + }; + defer if (cond_lock) |lock| func.register_manager.unlockReg(lock); + + const temp_cmp_reg, const temp_cmp_lock = try func.allocReg(.int); + defer func.register_manager.unlockReg(temp_cmp_lock); + + // is `condition` less than `min`? is "true", we've failed + try func.genBinOp( + .cmp_gte, + condition, + condition_ty, + min_mcv, + condition_ty, + temp_cmp_reg, + ); + + // if the compare was true, we will jump to the fail case and fall through + // to the next checks + const lt_fail_reloc = try func.condBr(condition_ty, .{ .register = temp_cmp_reg }); + try func.genBinOp( + .cmp_gt, + condition, + condition_ty, + max_mcv, + condition_ty, + temp_cmp_reg, + ); + + reloc.* = try func.condBr(condition_ty, .{ .register = temp_cmp_reg }); + func.performReloc(lt_fail_reloc); + } + + const skip_case_reloc = try func.jump(undefined); + for (liveness.deaths[case.idx]) |operand| try func.processDeath(operand); - for (relocs[0 .. relocs.len - 1]) |reloc| func.performReloc(reloc); + for (relocs) |reloc| func.performReloc(reloc); try func.genBody(case.body); try func.restoreState(state, &.{}, .{ .emit_instructions = false, @@ -5734,7 +5768,7 @@ fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void { .close_scope = true, }); - func.performReloc(relocs[relocs.len - 1]); + func.performReloc(skip_case_reloc); } if (switch_br.else_body_len > 0) { @@ -5831,6 +5865,19 @@ fn airBr(func: *Func, inst: Air.Inst.Index) !void { func.finishAirBookkeeping(); } +fn airRepeat(func: *Func, inst: Air.Inst.Index) !void { + const loop_inst = func.air.instructions.items(.data)[@intFromEnum(inst)].repeat.loop_inst; + const repeat_info = func.loop_repeat_info.get(loop_inst).?; + try func.restoreState(repeat_info.state, &.{}, .{ + .emit_instructions = true, + .update_tracking = false, + .resurrect = false, + .close_scope = true, + }); + _ = try func.jump(repeat_info.jmp_target); + func.finishAirBookkeeping(); +} + fn airBoolOp(func: *Func, inst: Air.Inst.Index) !void { const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const tag: Air.Inst.Tag = func.air.instructions.items(.tag)[@intFromEnum(inst)];