x86_64: un-regress loop and switch_br

This does *not* yet implement the new `loop_switch_br` instruction.
This commit is contained in:
mlugg 2024-08-30 23:33:57 +01:00
parent cb68c0917a
commit fd70d9db99
No known key found for this signature in database
GPG Key ID: 3F5B7DCCBF4AF02E

View File

@ -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;