diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/arch/x86_64/CodeGen.zig | 107 |
1 files changed, 91 insertions, 16 deletions
diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index e27e04782d..dcf51c2514 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -105,6 +105,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, @@ -815,6 +822,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); @@ -2247,7 +2255,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .bitcast => try self.airBitCast(inst), .block => try self.airBlock(inst), .br => try self.airBr(inst), - .repeat => return self.fail("TODO implement `repeat`", .{}), + .repeat => try self.airRepeat(inst), .switch_dispatch => return self.fail("TODO implement `switch_dispatch`", .{}), .trap => try self.airTrap(), .breakpoint => try self.airBreakpoint(), @@ -13629,15 +13637,13 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { self.scope_generation += 1; const state = try self.saveState(); - const jmp_target: Mir.Inst.Index = @intCast(self.mir_instructions.len); - try self.genBody(body); - try self.restoreState(state, &.{}, .{ - .emit_instructions = true, - .update_tracking = false, - .resurrect = false, - .close_scope = true, + try self.loop_repeat_info.putNoClobber(self.gpa, inst, .{ + .state = state, + .jmp_target = @intCast(self.mir_instructions.len), }); - _ = try self.asmJmpReloc(jmp_target); + defer assert(self.loop_repeat_info.remove(inst)); + + try self.genBody(body); self.finishAirBookkeeping(); } @@ -13680,12 +13686,19 @@ fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) ! } fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void { + const zcu = self.pt.zcu; const switch_br = self.air.unwrapSwitch(inst); const condition = try self.resolveInst(switch_br.operand); const condition_ty = self.typeOf(switch_br.operand); const liveness = try self.liveness.getSwitchBr(self.gpa, inst, switch_br.cases_len + 1); defer self.gpa.free(liveness.deaths); + const signedness = switch (condition_ty.zigTypeTag(zcu)) { + .bool, .pointer => .unsigned, + .int, .@"enum", .error_set => condition_ty.intInfo(zcu).signedness, + else => unreachable, + }; + // If the condition dies here in this switch instruction, process // that death now instead of later as this has an effect on // whether it needs to be spilled in the branches @@ -13698,13 +13711,11 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void { var it = switch_br.iterateCases(); while (it.next()) |case| { - if (case.ranges.len > 0) return self.fail("TODO: switch with ranges", .{}); - - var relocs = try self.gpa.alloc(Mir.Inst.Index, case.items.len); + var relocs = try self.gpa.alloc(Mir.Inst.Index, case.items.len + case.ranges.len); defer self.gpa.free(relocs); try self.spillEflagsIfOccupied(); - for (case.items, relocs, 0..) |item, *reloc, i| { + for (case.items, relocs[0..case.items.len]) |item, *reloc| { const item_mcv = try self.resolveInst(item); const cc: Condition = switch (condition) { .eflags => |cc| switch (item_mcv.immediate) { @@ -13717,12 +13728,62 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void { break :cc .e; }, }; - reloc.* = try self.asmJccReloc(if (i < relocs.len - 1) cc else cc.negate(), undefined); + reloc.* = try self.asmJccReloc(cc, undefined); } + for (case.ranges, relocs[case.items.len..]) |range, *reloc| { + const min_mcv = try self.resolveInst(range[0]); + const max_mcv = try self.resolveInst(range[1]); + // `null` means always false. + const lt_min: ?Condition = switch (condition) { + .eflags => |cc| switch (min_mcv.immediate) { + 0 => null, // condition never <0 + 1 => cc.negate(), + else => unreachable, + }, + else => cc: { + try self.genBinOpMir(.{ ._, .cmp }, condition_ty, condition, min_mcv); + break :cc switch (signedness) { + .unsigned => .b, + .signed => .l, + }; + }, + }; + const lt_min_reloc = if (lt_min) |cc| r: { + break :r try self.asmJccReloc(cc, undefined); + } else null; + // `null` means always true. + const lte_max: ?Condition = switch (condition) { + .eflags => |cc| switch (max_mcv.immediate) { + 0 => cc.negate(), + 1 => null, // condition always >=1 + else => unreachable, + }, + else => cc: { + try self.genBinOpMir(.{ ._, .cmp }, condition_ty, condition, max_mcv); + break :cc switch (signedness) { + .unsigned => .be, + .signed => .le, + }; + }, + }; + // "Success" case is in `reloc`.... + if (lte_max) |cc| { + reloc.* = try self.asmJccReloc(cc, undefined); + } else { + reloc.* = try self.asmJmpReloc(undefined); + } + // ...and "fail" case falls through to next checks. + if (lt_min_reloc) |r| self.performReloc(r); + } + + // The jump to skip this case if the conditions all failed. + const skip_case_reloc = try self.asmJmpReloc(undefined); + for (liveness.deaths[case.idx]) |operand| try self.processDeath(operand); - for (relocs[0 .. relocs.len - 1]) |reloc| self.performReloc(reloc); + // Relocate all success cases to the body we're about to generate. + for (relocs) |reloc| self.performReloc(reloc); try self.genBody(case.body); try self.restoreState(state, &.{}, .{ .emit_instructions = false, @@ -13731,7 +13792,8 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void { .close_scope = true, }); - self.performReloc(relocs[relocs.len - 1]); + // Relocate the "skip" branch to fall through to the next case. + self.performReloc(skip_case_reloc); } if (switch_br.else_body_len > 0) { @@ -13827,6 +13889,19 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void { self.finishAirBookkeeping(); } +fn airRepeat(self: *Self, inst: Air.Inst.Index) !void { + const loop_inst = self.air.instructions.items(.data)[@intFromEnum(inst)].repeat.loop_inst; + const repeat_info = self.loop_repeat_info.get(loop_inst).?; + try self.restoreState(repeat_info.state, &.{}, .{ + .emit_instructions = true, + .update_tracking = false, + .resurrect = false, + .close_scope = true, + }); + _ = try self.asmJmpReloc(repeat_info.jmp_target); + self.finishAirBookkeeping(); +} + fn airAsm(self: *Self, inst: Air.Inst.Index) !void { const pt = self.pt; const zcu = pt.zcu; |
