commit 9416b4d993e5e386c9208e9a52e4e881eee8cf72 (tree)
parent aa05cd48097b42d0c52ff5e0f3dd1ece68d1cdcd
Author: Jakub Konka <kubkon@jakubkonka.com>
Date: Sun, 8 May 2022 11:20:14 +0200
Merge pull request #11608 from ziglang/stage2-regalloc
Diffstat:
7 files changed, 1092 insertions(+), 686 deletions(-)
diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig
@@ -23,6 +23,7 @@ const log = std.log.scoped(.codegen);
const build_options = @import("build_options");
const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
const RegisterManager = RegisterManagerFn(Self, Register, &callee_preserved_regs);
+const RegisterLock = RegisterManager.RegisterLock;
const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
const FnResult = @import("../../codegen.zig").FnResult;
@@ -727,7 +728,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
// zig fmt: on
}
- assert(!self.register_manager.frozenRegsExist());
+ assert(!self.register_manager.lockedRegsExist());
if (std.debug.runtime_safety) {
if (self.air_bookkeeping < old_air_bookkeeping + 1) {
@@ -910,16 +911,16 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
fn spillCompareFlagsIfOccupied(self: *Self) !void {
if (self.compare_flags_inst) |inst_to_save| {
const mcv = self.getResolvedInstValue(inst_to_save);
- switch (mcv) {
+ const new_mcv = switch (mcv) {
.compare_flags_signed,
.compare_flags_unsigned,
+ => try self.allocRegOrMem(inst_to_save, true),
.register_c_flag,
.register_v_flag,
- => {},
+ => try self.allocRegOrMem(inst_to_save, false),
else => unreachable, // mcv doesn't occupy the compare flags
- }
+ };
- const new_mcv = try self.allocRegOrMem(inst_to_save, true);
try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv);
log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv });
@@ -927,6 +928,15 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void {
try branch.inst_table.put(self.gpa, inst_to_save, new_mcv);
self.compare_flags_inst = null;
+
+ // TODO consolidate with register manager and spillInstruction
+ // this call should really belong in the register manager!
+ switch (mcv) {
+ .register_c_flag,
+ .register_v_flag,
+ => |reg| self.register_manager.freeReg(reg),
+ else => {},
+ }
}
}
@@ -1048,8 +1058,8 @@ fn trunc(
}
},
};
- self.register_manager.freezeRegs(&.{operand_reg});
- defer self.register_manager.unfreezeRegs(&.{operand_reg});
+ const lock = self.register_manager.lockReg(operand_reg);
+ defer if (lock) |reg| self.register_manager.unlockReg(reg);
const dest_reg = if (maybe_inst) |inst| blk: {
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
@@ -1135,8 +1145,8 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
.register => |r| r,
else => try self.copyToTmpRegister(operand_ty, operand),
};
- self.register_manager.freezeRegs(&.{op_reg});
- defer self.register_manager.unfreezeRegs(&.{op_reg});
+ const reg_lock = self.register_manager.lockRegAssumeUnused(op_reg);
+ defer self.register_manager.unlockReg(reg_lock);
const dest_reg = blk: {
if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
@@ -1168,8 +1178,8 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
.register => |r| r,
else => try self.copyToTmpRegister(operand_ty, operand),
};
- self.register_manager.freezeRegs(&.{op_reg});
- defer self.register_manager.unfreezeRegs(&.{op_reg});
+ const reg_lock = self.register_manager.lockRegAssumeUnused(op_reg);
+ defer self.register_manager.unlockReg(reg_lock);
const dest_reg = blk: {
if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
@@ -1257,8 +1267,17 @@ fn binOpRegister(
const lhs_is_register = lhs == .register;
const rhs_is_register = rhs == .register;
- if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register});
- if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register});
+ const lhs_lock: ?RegisterLock = if (lhs_is_register)
+ self.register_manager.lockReg(lhs.register)
+ else
+ null;
+ defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
+
+ const rhs_lock: ?RegisterLock = if (rhs_is_register)
+ self.register_manager.lockReg(rhs.register)
+ else
+ null;
+ defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg);
const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
@@ -1270,13 +1289,13 @@ fn binOpRegister(
const raw_reg = try self.register_manager.allocReg(track_inst);
const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
- self.register_manager.freezeRegs(&.{reg});
if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
break :blk reg;
};
- defer self.register_manager.unfreezeRegs(&.{lhs_reg});
+ const new_lhs_lock = self.register_manager.lockReg(lhs_reg);
+ defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
const rhs_reg = if (rhs_is_register) rhs.register else blk: {
const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
@@ -1286,13 +1305,13 @@ fn binOpRegister(
const raw_reg = try self.register_manager.allocReg(track_inst);
const reg = registerAlias(raw_reg, rhs_ty.abiAlignment(self.target.*));
- self.register_manager.freezeRegs(&.{reg});
if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
break :blk reg;
};
- defer self.register_manager.unfreezeRegs(&.{rhs_reg});
+ const new_rhs_lock = self.register_manager.lockReg(rhs_reg);
+ defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg);
const dest_reg = switch (mir_tag) {
.cmp_shifted_register => undefined, // cmp has no destination register
@@ -1394,7 +1413,11 @@ fn binOpImmediate(
) !MCValue {
const lhs_is_register = lhs == .register;
- if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register});
+ const lhs_lock: ?RegisterLock = if (lhs_is_register)
+ self.register_manager.lockReg(lhs.register)
+ else
+ null;
+ defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
@@ -1408,13 +1431,13 @@ fn binOpImmediate(
const raw_reg = try self.register_manager.allocReg(track_inst);
const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
- self.register_manager.freezeRegs(&.{reg});
if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
break :blk reg;
};
- defer self.register_manager.unfreezeRegs(&.{lhs_reg});
+ const new_lhs_lock = self.register_manager.lockReg(lhs_reg);
+ defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
const dest_reg = switch (mir_tag) {
.cmp_immediate => undefined, // cmp has no destination register
@@ -1758,7 +1781,10 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index) !void {
const lhs_ty = self.air.typeOf(bin_op.lhs);
const rhs_ty = self.air.typeOf(bin_op.rhs);
- const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty);
+ const result: MCValue = if (self.liveness.isUnused(inst))
+ .dead
+ else
+ try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty);
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
}
@@ -1815,13 +1841,13 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
};
const dest = try self.binOp(base_tag, null, lhs, rhs, lhs_ty, rhs_ty);
const dest_reg = dest.register;
- self.register_manager.freezeRegs(&.{dest_reg});
- defer self.register_manager.unfreezeRegs(&.{dest_reg});
+ const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
+ defer self.register_manager.unlockReg(dest_reg_lock);
const raw_truncated_reg = try self.register_manager.allocReg(null);
const truncated_reg = registerAlias(raw_truncated_reg, lhs_ty.abiSize(self.target.*));
- self.register_manager.freezeRegs(&.{truncated_reg});
- defer self.register_manager.unfreezeRegs(&.{truncated_reg});
+ const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg);
+ defer self.register_manager.unlockReg(truncated_reg_lock);
// sbfx/ubfx truncated, dest, #0, #bits
try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);
@@ -1922,12 +1948,12 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
const dest = try self.binOpRegister(base_tag, null, lhs, rhs, lhs_ty, rhs_ty);
const dest_reg = dest.register;
- self.register_manager.freezeRegs(&.{dest_reg});
- defer self.register_manager.unfreezeRegs(&.{dest_reg});
+ const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
+ defer self.register_manager.unlockReg(dest_reg_lock);
const truncated_reg = try self.register_manager.allocReg(null);
- self.register_manager.freezeRegs(&.{truncated_reg});
- defer self.register_manager.unfreezeRegs(&.{truncated_reg});
+ const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg);
+ defer self.register_manager.unlockReg(truncated_reg_lock);
try self.truncRegister(
dest_reg.to32(),
@@ -1977,36 +2003,44 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
const lhs_is_register = lhs == .register;
const rhs_is_register = rhs == .register;
- if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register});
- if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register});
+ const lhs_lock: ?RegisterLock = if (lhs_is_register)
+ self.register_manager.lockRegAssumeUnused(lhs.register)
+ else
+ null;
+ defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
+
+ const rhs_lock: ?RegisterLock = if (rhs_is_register)
+ self.register_manager.lockRegAssumeUnused(rhs.register)
+ else
+ null;
+ defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg);
const lhs_reg = if (lhs_is_register) lhs.register else blk: {
const raw_reg = try self.register_manager.allocReg(null);
const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
- self.register_manager.freezeRegs(&.{reg});
break :blk reg;
};
- defer self.register_manager.unfreezeRegs(&.{lhs_reg});
+ const new_lhs_lock = self.register_manager.lockReg(lhs_reg);
+ defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
const rhs_reg = if (rhs_is_register) rhs.register else blk: {
const raw_reg = try self.register_manager.allocReg(null);
const reg = registerAlias(raw_reg, rhs_ty.abiAlignment(self.target.*));
- self.register_manager.freezeRegs(&.{reg});
break :blk reg;
};
- defer self.register_manager.unfreezeRegs(&.{rhs_reg});
+ const new_rhs_lock = self.register_manager.lockReg(rhs_reg);
+ defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg);
if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);
- // TODO reuse operands
const dest_reg = blk: {
const raw_reg = try self.register_manager.allocReg(null);
const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
- self.register_manager.freezeRegs(&.{reg});
break :blk reg;
};
- defer self.register_manager.unfreezeRegs(&.{dest_reg});
+ const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
+ defer self.register_manager.unlockReg(dest_reg_lock);
switch (int_info.signedness) {
.signed => {
@@ -2021,8 +2055,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
});
const dest_high_reg = try self.register_manager.allocReg(null);
- self.register_manager.freezeRegs(&.{dest_high_reg});
- defer self.register_manager.unfreezeRegs(&.{dest_high_reg});
+ const dest_high_reg_lock = self.register_manager.lockRegAssumeUnused(dest_high_reg);
+ defer self.register_manager.unlockReg(dest_high_reg_lock);
// smulh dest_high, lhs, rhs
_ = try self.addInst(.{
@@ -2071,8 +2105,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
},
.unsigned => {
const dest_high_reg = try self.register_manager.allocReg(null);
- self.register_manager.freezeRegs(&.{dest_high_reg});
- defer self.register_manager.unfreezeRegs(&.{dest_high_reg});
+ const dest_high_reg_lock = self.register_manager.lockRegAssumeUnused(dest_high_reg);
+ defer self.register_manager.unlockReg(dest_high_reg_lock);
// umulh dest_high, lhs, rhs
_ = try self.addInst(.{
@@ -2127,8 +2161,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
}
const truncated_reg = try self.register_manager.allocReg(null);
- self.register_manager.freezeRegs(&.{truncated_reg});
- defer self.register_manager.unfreezeRegs(&.{truncated_reg});
+ const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg);
+ defer self.register_manager.unlockReg(truncated_reg_lock);
try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);
@@ -2168,14 +2202,20 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
if (int_info.bits <= 64) {
const stack_offset = try self.allocMem(inst, tuple_size, tuple_align);
- if (lhs == .register) self.register_manager.freezeRegs(&.{lhs.register});
- defer if (lhs == .register) self.register_manager.unfreezeRegs(&.{lhs.register});
+ const lhs_lock: ?RegisterLock = if (lhs == .register)
+ self.register_manager.lockRegAssumeUnused(lhs.register)
+ else
+ null;
+ defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
try self.spillCompareFlagsIfOccupied();
self.compare_flags_inst = null;
// lsl dest, lhs, rhs
const dest = try self.binOp(.shl, null, lhs, rhs, lhs_ty, rhs_ty);
+ const dest_reg = dest.register;
+ const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
+ defer self.register_manager.unlockReg(dest_reg_lock);
// asr/lsr reconstructed, dest, rhs
const reconstructed = try self.binOp(.shr, null, dest, rhs, lhs_ty, rhs_ty);
@@ -2184,7 +2224,9 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
_ = try self.binOp(.cmp_eq, null, lhs, reconstructed, lhs_ty, lhs_ty);
try self.genSetStack(lhs_ty, stack_offset, dest);
- try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq });
+ try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{
+ .compare_flags_unsigned = .neq,
+ });
break :result MCValue{ .stack_offset = stack_offset };
} else {
@@ -2411,14 +2453,18 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
var buf: Type.SlicePtrFieldTypeBuffer = undefined;
const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf);
- if (index_is_register) self.register_manager.freezeRegs(&.{index_mcv.register});
- defer if (index_is_register) self.register_manager.unfreezeRegs(&.{index_mcv.register});
+ const index_lock: ?RegisterLock = if (index_is_register)
+ self.register_manager.lockRegAssumeUnused(index_mcv.register)
+ else
+ null;
+ defer if (index_lock) |reg| self.register_manager.unlockReg(reg);
const base_mcv: MCValue = switch (slice_mcv) {
.stack_offset => |off| .{ .register = try self.copyToTmpRegister(slice_ptr_field_type, .{ .stack_offset = off }) },
else => return self.fail("TODO slice_elem_val when slice is {}", .{slice_mcv}),
};
- self.register_manager.freezeRegs(&.{base_mcv.register});
+ const base_lock = self.register_manager.lockRegAssumeUnused(base_mcv.register);
+ defer self.register_manager.unlockReg(base_lock);
switch (elem_size) {
else => {
@@ -2559,8 +2605,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
.immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),
.ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }),
.register => |addr_reg| {
- self.register_manager.freezeRegs(&.{addr_reg});
- defer self.register_manager.unfreezeRegs(&.{addr_reg});
+ const addr_reg_lock = self.register_manager.lockReg(addr_reg);
+ defer if (addr_reg_lock) |reg| self.register_manager.unlockReg(reg);
switch (dst_mcv) {
.dead => unreachable,
@@ -2573,16 +2619,18 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
if (elem_size <= 8) {
const raw_tmp_reg = try self.register_manager.allocReg(null);
const tmp_reg = registerAlias(raw_tmp_reg, elem_size);
- self.register_manager.freezeRegs(&.{tmp_reg});
- defer self.register_manager.unfreezeRegs(&.{tmp_reg});
+ const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
+ defer self.register_manager.unlockReg(tmp_reg_lock);
try self.load(.{ .register = tmp_reg }, ptr, ptr_ty);
try self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg });
} else {
// TODO optimize the register allocation
const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null });
- self.register_manager.freezeRegs(®s);
- defer self.register_manager.unfreezeRegs(®s);
+ const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs);
+ defer for (regs_locks) |reg| {
+ self.register_manager.unlockReg(reg);
+ };
const src_reg = addr_reg;
const dst_reg = regs[0];
@@ -2784,8 +2832,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
try self.genSetStack(value_ty, off, value);
},
.register => |addr_reg| {
- self.register_manager.freezeRegs(&.{addr_reg});
- defer self.register_manager.unfreezeRegs(&.{addr_reg});
+ const addr_reg_lock = self.register_manager.lockReg(addr_reg);
+ defer if (addr_reg_lock) |reg| self.register_manager.unlockReg(reg);
switch (value) {
.register => |value_reg| {
@@ -2795,8 +2843,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
if (abi_size <= 8) {
const raw_tmp_reg = try self.register_manager.allocReg(null);
const tmp_reg = registerAlias(raw_tmp_reg, abi_size);
- self.register_manager.freezeRegs(&.{tmp_reg});
- defer self.register_manager.unfreezeRegs(&.{tmp_reg});
+ const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
+ defer self.register_manager.unlockReg(tmp_reg_lock);
try self.genSetReg(value_ty, tmp_reg, value);
try self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty);
@@ -2856,12 +2904,12 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
const offset_reg = try self.copyToTmpRegister(ptr_ty, .{
.immediate = struct_field_offset,
});
- self.register_manager.freezeRegs(&.{offset_reg});
- defer self.register_manager.unfreezeRegs(&.{offset_reg});
+ const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg);
+ defer self.register_manager.unlockReg(offset_reg_lock);
const addr_reg = try self.copyToTmpRegister(ptr_ty, mcv);
- self.register_manager.freezeRegs(&.{addr_reg});
- defer self.register_manager.unfreezeRegs(&.{addr_reg});
+ const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
+ defer self.register_manager.unlockReg(addr_reg_lock);
const dest = try self.binOp(
.add,
@@ -3369,6 +3417,9 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
const parent_compare_flags_inst = self.compare_flags_inst;
try self.branch_stack.append(.{});
+ errdefer {
+ _ = self.branch_stack.pop();
+ }
try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len);
for (liveness_condbr.then_deaths) |operand| {
@@ -3955,8 +4006,38 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
},
.register_c_flag,
.register_v_flag,
- => {
- return self.fail("TODO implement genSetStack {}", .{mcv});
+ => |reg| {
+ const reg_lock = self.register_manager.lockReg(reg);
+ defer if (reg_lock) |locked_reg| self.register_manager.unlockReg(locked_reg);
+
+ const wrapped_ty = ty.structFieldType(0);
+ try self.genSetStack(wrapped_ty, stack_offset, .{ .register = reg });
+
+ const overflow_bit_ty = ty.structFieldType(1);
+ const overflow_bit_offset = @intCast(u32, ty.structFieldOffset(1, self.target.*));
+ const raw_cond_reg = try self.register_manager.allocReg(null);
+ const cond_reg = registerAlias(
+ raw_cond_reg,
+ @intCast(u32, overflow_bit_ty.abiSize(self.target.*)),
+ );
+
+ // C flag: cset reg, cs
+ // V flag: cset reg, vs
+ _ = try self.addInst(.{
+ .tag = .cset,
+ .data = .{ .r_cond = .{
+ .rd = cond_reg,
+ .cond = switch (mcv) {
+ .register_c_flag => .cs,
+ .register_v_flag => .vs,
+ else => unreachable,
+ },
+ } },
+ });
+
+ try self.genSetStack(overflow_bit_ty, stack_offset - overflow_bit_offset, .{
+ .register = cond_reg,
+ });
},
.got_load,
.direct_load,
@@ -3983,8 +4064,10 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
// TODO call extern memcpy
const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null });
- self.register_manager.freezeRegs(®s);
- defer self.register_manager.unfreezeRegs(®s);
+ const regs_locks = self.register_manager.lockRegsAssumeUnused(5, regs);
+ defer for (regs_locks) |reg| {
+ self.register_manager.unlockReg(reg);
+ };
const src_reg = regs[0];
const dst_reg = regs[1];
diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig
@@ -23,6 +23,7 @@ const log = std.log.scoped(.codegen);
const build_options = @import("build_options");
const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
const RegisterManager = RegisterManagerFn(Self, Register, &allocatable_registers);
+const RegisterLock = RegisterManager.RegisterLock;
const FnResult = @import("../../codegen.zig").FnResult;
const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
@@ -734,7 +735,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
// zig fmt: on
}
- assert(!self.register_manager.frozenRegsExist());
+ assert(!self.register_manager.lockedRegsExist());
if (std.debug.runtime_safety) {
if (self.air_bookkeeping < old_air_bookkeeping + 1) {
@@ -897,16 +898,16 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
fn spillCompareFlagsIfOccupied(self: *Self) !void {
if (self.compare_flags_inst) |inst_to_save| {
const mcv = self.getResolvedInstValue(inst_to_save);
- switch (mcv) {
+ const new_mcv = switch (mcv) {
.compare_flags_signed,
.compare_flags_unsigned,
+ => try self.allocRegOrMem(inst_to_save, true),
.register_c_flag,
.register_v_flag,
- => {},
+ => try self.allocRegOrMem(inst_to_save, false),
else => unreachable, // mcv doesn't occupy the compare flags
- }
+ };
- const new_mcv = try self.allocRegOrMem(inst_to_save, true);
try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv);
log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv });
@@ -914,6 +915,15 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void {
try branch.inst_table.put(self.gpa, inst_to_save, new_mcv);
self.compare_flags_inst = null;
+
+ // TODO consolidate with register manager and spillInstruction
+ // this call should really belong in the register manager!
+ switch (mcv) {
+ .register_c_flag,
+ .register_v_flag,
+ => |reg| self.register_manager.freeReg(reg),
+ else => {},
+ }
}
}
@@ -1038,8 +1048,8 @@ fn trunc(
}
},
};
- self.register_manager.freezeRegs(&.{operand_reg});
- defer self.register_manager.unfreezeRegs(&.{operand_reg});
+ const operand_reg_lock = self.register_manager.lockReg(operand_reg);
+ defer if (operand_reg_lock) |reg| self.register_manager.unlockReg(reg);
const dest_reg = if (maybe_inst) |inst| blk: {
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
@@ -1127,8 +1137,8 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
.register => |r| r,
else => try self.copyToTmpRegister(operand_ty, operand),
};
- self.register_manager.freezeRegs(&.{op_reg});
- defer self.register_manager.unfreezeRegs(&.{op_reg});
+ const op_reg_lock = self.register_manager.lockRegAssumeUnused(op_reg);
+ defer self.register_manager.unlockReg(op_reg_lock);
const dest_reg = blk: {
if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
@@ -1157,8 +1167,8 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
.register => |r| r,
else => try self.copyToTmpRegister(operand_ty, operand),
};
- self.register_manager.freezeRegs(&.{op_reg});
- defer self.register_manager.unfreezeRegs(&.{op_reg});
+ const op_reg_lock = self.register_manager.lockRegAssumeUnused(op_reg);
+ defer self.register_manager.unlockReg(op_reg_lock);
const dest_reg = blk: {
if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
@@ -1218,15 +1228,15 @@ fn minMax(
.register => |r| r,
else => try self.copyToTmpRegister(lhs_ty, lhs),
};
- self.register_manager.freezeRegs(&.{lhs_reg});
- defer self.register_manager.unfreezeRegs(&.{lhs_reg});
+ const lhs_reg_lock = self.register_manager.lockReg(lhs_reg);
+ defer if (lhs_reg_lock) |reg| self.register_manager.unlockReg(reg);
const rhs_reg = switch (rhs) {
.register => |r| r,
else => try self.copyToTmpRegister(rhs_ty, rhs),
};
- self.register_manager.freezeRegs(&.{rhs_reg});
- defer self.register_manager.unfreezeRegs(&.{rhs_reg});
+ const rhs_reg_lock = self.register_manager.lockReg(rhs_reg);
+ defer if (rhs_reg_lock) |reg| self.register_manager.unlockReg(reg);
const dest_reg = if (maybe_inst) |inst| blk: {
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
@@ -1392,12 +1402,12 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
};
const dest = try self.binOp(base_tag, null, lhs, rhs, lhs_ty, rhs_ty);
const dest_reg = dest.register;
- self.register_manager.freezeRegs(&.{dest_reg});
- defer self.register_manager.unfreezeRegs(&.{dest_reg});
+ const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
+ defer self.register_manager.unlockReg(dest_reg_lock);
const truncated_reg = try self.register_manager.allocReg(null);
- self.register_manager.freezeRegs(&.{truncated_reg});
- defer self.register_manager.unfreezeRegs(&.{truncated_reg});
+ const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg);
+ defer self.register_manager.unlockReg(truncated_reg_lock);
// sbfx/ubfx truncated, dest, #0, #bits
try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);
@@ -1493,12 +1503,12 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
const dest = try self.binOpRegister(base_tag, null, lhs, rhs, lhs_ty, rhs_ty);
const dest_reg = dest.register;
- self.register_manager.freezeRegs(&.{dest_reg});
- defer self.register_manager.unfreezeRegs(&.{dest_reg});
+ const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
+ defer self.register_manager.unlockReg(dest_reg_lock);
const truncated_reg = try self.register_manager.allocReg(null);
- self.register_manager.freezeRegs(&.{truncated_reg});
- defer self.register_manager.unfreezeRegs(&.{truncated_reg});
+ const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg);
+ defer self.register_manager.unlockReg(truncated_reg_lock);
// sbfx/ubfx truncated, dest, #0, #bits
try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);
@@ -1526,28 +1536,31 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
const lhs_is_register = lhs == .register;
const rhs_is_register = rhs == .register;
- if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register});
- if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register});
-
- const lhs_reg = if (lhs_is_register) lhs.register else blk: {
- const reg = try self.register_manager.allocReg(null);
- self.register_manager.freezeRegs(&.{reg});
-
- break :blk reg;
- };
- defer self.register_manager.unfreezeRegs(&.{lhs_reg});
+ const lhs_lock: ?RegisterLock = if (lhs_is_register)
+ self.register_manager.lockReg(lhs.register)
+ else
+ null;
+ defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
- const rhs_reg = if (rhs_is_register) rhs.register else blk: {
- const reg = try self.register_manager.allocReg(null);
- self.register_manager.freezeRegs(&.{reg});
+ const lhs_reg = if (lhs_is_register)
+ lhs.register
+ else
+ try self.register_manager.allocReg(null);
+ const new_lhs_lock = self.register_manager.lockReg(lhs_reg);
+ defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
- break :blk reg;
- };
- defer self.register_manager.unfreezeRegs(&.{rhs_reg});
+ const rhs_reg = if (rhs_is_register)
+ rhs.register
+ else
+ try self.register_manager.allocReg(null);
+ const new_rhs_lock = self.register_manager.lockReg(rhs_reg);
+ defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg);
const dest_regs = try self.register_manager.allocRegs(2, .{ null, null });
- self.register_manager.freezeRegs(&dest_regs);
- defer self.register_manager.unfreezeRegs(&dest_regs);
+ const dest_regs_locks = self.register_manager.lockRegsAssumeUnused(2, dest_regs);
+ defer for (dest_regs_locks) |reg| {
+ self.register_manager.unlockReg(reg);
+ };
const rdlo = dest_regs[0];
const rdhi = dest_regs[1];
@@ -1555,8 +1568,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);
const truncated_reg = try self.register_manager.allocReg(null);
- self.register_manager.freezeRegs(&.{truncated_reg});
- defer self.register_manager.unfreezeRegs(&.{truncated_reg});
+ const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg);
+ defer self.register_manager.unlockReg(truncated_reg_lock);
_ = try self.addInst(.{
.tag = base_tag,
@@ -1648,14 +1661,20 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
if (int_info.bits <= 32) {
const stack_offset = try self.allocMem(inst, tuple_size, tuple_align);
- if (lhs == .register) self.register_manager.freezeRegs(&.{lhs.register});
- defer if (lhs == .register) self.register_manager.unfreezeRegs(&.{lhs.register});
+ const lhs_lock: ?RegisterLock = if (lhs == .register)
+ self.register_manager.lockRegAssumeUnused(lhs.register)
+ else
+ null;
+ defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
try self.spillCompareFlagsIfOccupied();
self.compare_flags_inst = null;
// lsl dest, lhs, rhs
const dest = try self.binOp(.shl, null, lhs, rhs, lhs_ty, rhs_ty);
+ const dest_reg = dest.register;
+ const dest_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
+ defer self.register_manager.unlockReg(dest_lock);
// asr/lsr reconstructed, dest, rhs
const reconstructed = try self.binOp(.shr, null, dest, rhs, lhs_ty, rhs_ty);
@@ -1939,8 +1958,11 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
var buf: Type.SlicePtrFieldTypeBuffer = undefined;
const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf);
- if (index_is_register) self.register_manager.freezeRegs(&.{index_mcv.register});
- defer if (index_is_register) self.register_manager.unfreezeRegs(&.{index_mcv.register});
+ const index_lock: ?RegisterLock = if (index_is_register)
+ self.register_manager.lockRegAssumeUnused(index_mcv.register)
+ else
+ null;
+ defer if (index_lock) |reg| self.register_manager.unlockReg(reg);
const base_mcv = slicePtr(slice_mcv);
@@ -1950,20 +1972,20 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
.register => |r| r,
else => try self.copyToTmpRegister(slice_ptr_field_type, base_mcv),
};
- self.register_manager.freezeRegs(&.{base_reg});
- defer self.register_manager.unfreezeRegs(&.{base_reg});
+ const base_reg_lock = self.register_manager.lockRegAssumeUnused(base_reg);
+ defer self.register_manager.unlockReg(base_reg_lock);
const dst_reg = try self.register_manager.allocReg(inst);
const dst_mcv = MCValue{ .register = dst_reg };
- self.register_manager.freezeRegs(&.{dst_reg});
- defer self.register_manager.unfreezeRegs(&.{dst_reg});
+ const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
+ defer self.register_manager.unlockReg(dst_reg_lock);
const index_reg: Register = switch (index_mcv) {
.register => |reg| reg,
else => try self.copyToTmpRegister(Type.usize, index_mcv),
};
- self.register_manager.freezeRegs(&.{index_reg});
- defer self.register_manager.unfreezeRegs(&.{index_reg});
+ const index_reg_lock = self.register_manager.lockReg(index_reg);
+ defer if (index_reg_lock) |lock| self.register_manager.unlockReg(lock);
const tag: Mir.Inst.Tag = switch (elem_size) {
1 => .ldrb,
@@ -2149,8 +2171,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
.immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),
.ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }),
.register => |reg| {
- self.register_manager.freezeRegs(&.{reg});
- defer self.register_manager.unfreezeRegs(&.{reg});
+ const reg_lock = self.register_manager.lockReg(reg);
+ defer if (reg_lock) |reg_locked| self.register_manager.unlockReg(reg_locked);
switch (dst_mcv) {
.dead => unreachable,
@@ -2162,16 +2184,18 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
.stack_offset => |off| {
if (elem_size <= 4) {
const tmp_reg = try self.register_manager.allocReg(null);
- self.register_manager.freezeRegs(&.{tmp_reg});
- defer self.register_manager.unfreezeRegs(&.{tmp_reg});
+ const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
+ defer self.register_manager.unlockReg(tmp_reg_lock);
try self.load(.{ .register = tmp_reg }, ptr, ptr_ty);
try self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg });
} else {
// TODO optimize the register allocation
const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null });
- self.register_manager.freezeRegs(®s);
- defer self.register_manager.unfreezeRegs(®s);
+ const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs);
+ defer for (regs_locks) |reg_locked| {
+ self.register_manager.unlockReg(reg_locked);
+ };
const src_reg = reg;
const dst_reg = regs[0];
@@ -2197,8 +2221,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
.stack_argument_offset,
=> {
const reg = try self.register_manager.allocReg(null);
- self.register_manager.freezeRegs(&.{reg});
- defer self.register_manager.unfreezeRegs(&.{reg});
+ const reg_lock = self.register_manager.lockRegAssumeUnused(reg);
+ defer self.register_manager.unlockReg(reg_lock);
try self.genSetReg(ptr_ty, reg, ptr);
try self.load(dst_mcv, .{ .register = reg }, ptr_ty);
@@ -2252,8 +2276,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
try self.genSetStack(value_ty, off, value);
},
.register => |addr_reg| {
- self.register_manager.freezeRegs(&.{addr_reg});
- defer self.register_manager.unfreezeRegs(&.{addr_reg});
+ const addr_reg_lock = self.register_manager.lockReg(addr_reg);
+ defer if (addr_reg_lock) |reg| self.register_manager.unlockReg(reg);
switch (value) {
.dead => unreachable,
@@ -2264,15 +2288,17 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
else => {
if (elem_size <= 4) {
const tmp_reg = try self.register_manager.allocReg(null);
- self.register_manager.freezeRegs(&.{tmp_reg});
- defer self.register_manager.unfreezeRegs(&.{tmp_reg});
+ const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
+ defer self.register_manager.unlockReg(tmp_reg_lock);
try self.genSetReg(value_ty, tmp_reg, value);
try self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty);
} else {
const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null });
- self.register_manager.freezeRegs(®s);
- defer self.register_manager.unfreezeRegs(®s);
+ const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs);
+ defer for (regs_locks) |reg| {
+ self.register_manager.unlockReg(reg);
+ };
const src_reg = regs[0];
const dst_reg = addr_reg;
@@ -2356,12 +2382,12 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
const offset_reg = try self.copyToTmpRegister(ptr_ty, .{
.immediate = struct_field_offset,
});
- self.register_manager.freezeRegs(&.{offset_reg});
- defer self.register_manager.unfreezeRegs(&.{offset_reg});
+ const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg);
+ defer self.register_manager.unlockReg(offset_reg_lock);
const addr_reg = try self.copyToTmpRegister(ptr_ty, mcv);
- self.register_manager.freezeRegs(&.{addr_reg});
- defer self.register_manager.unfreezeRegs(&.{addr_reg});
+ const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
+ defer self.register_manager.unlockReg(addr_reg_lock);
const dest = try self.binOp(
.add,
@@ -2477,8 +2503,11 @@ fn binOpRegister(
const lhs_is_register = lhs == .register;
const rhs_is_register = rhs == .register;
- if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register});
- if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register});
+ const lhs_lock: ?RegisterLock = if (lhs_is_register)
+ self.register_manager.lockReg(lhs.register)
+ else
+ null;
+ defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
@@ -2489,13 +2518,13 @@ fn binOpRegister(
} else null;
const reg = try self.register_manager.allocReg(track_inst);
- self.register_manager.freezeRegs(&.{reg});
if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
break :blk reg;
};
- defer self.register_manager.unfreezeRegs(&.{lhs_reg});
+ const new_lhs_lock = self.register_manager.lockReg(lhs_reg);
+ defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
const rhs_reg = if (rhs_is_register) rhs.register else blk: {
const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
@@ -2504,13 +2533,13 @@ fn binOpRegister(
} else null;
const reg = try self.register_manager.allocReg(track_inst);
- self.register_manager.freezeRegs(&.{reg});
if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
break :blk reg;
};
- defer self.register_manager.unfreezeRegs(&.{rhs_reg});
+ const new_rhs_lock = self.register_manager.lockReg(rhs_reg);
+ defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg);
const dest_reg = switch (mir_tag) {
.cmp => .r0, // cmp has no destination regardless
@@ -2593,7 +2622,11 @@ fn binOpImmediate(
) !MCValue {
const lhs_is_register = lhs == .register;
- if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register});
+ const lhs_lock: ?RegisterLock = if (lhs_is_register)
+ self.register_manager.lockReg(lhs.register)
+ else
+ null;
+ defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
@@ -2606,13 +2639,13 @@ fn binOpImmediate(
} else null;
const reg = try self.register_manager.allocReg(track_inst);
- self.register_manager.freezeRegs(&.{reg});
if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
break :blk reg;
};
- defer self.register_manager.unfreezeRegs(&.{lhs_reg});
+ const new_lhs_lock = self.register_manager.lockReg(lhs_reg);
+ defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
const dest_reg = switch (mir_tag) {
.cmp => .r0, // cmp has no destination reg
@@ -3656,6 +3689,9 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
const parent_compare_flags_inst = self.compare_flags_inst;
try self.branch_stack.append(.{});
+ errdefer {
+ _ = self.branch_stack.pop();
+ }
try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len);
for (liveness_condbr.then_deaths) |operand| {
@@ -4264,8 +4300,36 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
},
.register_c_flag,
.register_v_flag,
- => {
- return self.fail("TODO implement genSetStack {}", .{mcv});
+ => |reg| {
+ const reg_lock = self.register_manager.lockReg(reg);
+ defer if (reg_lock) |locked_reg| self.register_manager.unlockReg(locked_reg);
+
+ const wrapped_ty = ty.structFieldType(0);
+ try self.genSetStack(wrapped_ty, stack_offset, .{ .register = reg });
+
+ const overflow_bit_ty = ty.structFieldType(1);
+ const overflow_bit_offset = @intCast(u32, ty.structFieldOffset(1, self.target.*));
+ const cond_reg = try self.register_manager.allocReg(null);
+
+ // C flag: movcs reg, #1
+ // V flag: movvs reg, #1
+ _ = try self.addInst(.{
+ .tag = .mov,
+ .cond = switch (mcv) {
+ .register_c_flag => .cs,
+ .register_v_flag => .vs,
+ else => unreachable,
+ },
+ .data = .{ .rr_op = .{
+ .rd = cond_reg,
+ .rn = .r0,
+ .op = Instruction.Operand.fromU32(1).?,
+ } },
+ });
+
+ try self.genSetStack(overflow_bit_ty, stack_offset - overflow_bit_offset, .{
+ .register = cond_reg,
+ });
},
.memory,
.stack_argument_offset,
diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig
@@ -23,6 +23,7 @@ const log = std.log.scoped(.codegen);
const build_options = @import("build_options");
const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
const RegisterManager = RegisterManagerFn(Self, Register, &callee_preserved_regs);
+const RegisterLock = RegisterManager.RegisterLock;
const FnResult = @import("../../codegen.zig").FnResult;
const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
@@ -937,8 +938,11 @@ fn binOpRegister(
const lhs_is_register = lhs == .register;
const rhs_is_register = rhs == .register;
- if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register});
- if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register});
+ const lhs_lock: ?RegisterLock = if (lhs_is_register)
+ self.register_manager.lockReg(lhs.register)
+ else
+ null;
+ defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
@@ -949,13 +953,13 @@ fn binOpRegister(
} else null;
const reg = try self.register_manager.allocReg(track_inst);
- self.register_manager.freezeRegs(&.{reg});
if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
break :blk reg;
};
- defer self.register_manager.unfreezeRegs(&.{lhs_reg});
+ const new_lhs_lock = self.register_manager.lockReg(lhs_reg);
+ defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
const rhs_reg = if (rhs_is_register) rhs.register else blk: {
const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: {
@@ -964,13 +968,13 @@ fn binOpRegister(
} else null;
const reg = try self.register_manager.allocReg(track_inst);
- self.register_manager.freezeRegs(&.{reg});
if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
break :blk reg;
};
- defer self.register_manager.unfreezeRegs(&.{rhs_reg});
+ const new_rhs_lock = self.register_manager.lockReg(rhs_reg);
+ defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg);
const dest_reg = if (maybe_inst) |inst| blk: {
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
@@ -1448,8 +1452,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
.stack_offset,
=> {
const reg = try self.register_manager.allocReg(null);
- self.register_manager.freezeRegs(&.{reg});
- defer self.register_manager.unfreezeRegs(&.{reg});
+ const reg_lock = self.register_manager.lockRegAssumeUnused(reg);
+ defer self.register_manager.unlockReg(reg_lock);
try self.genSetReg(ptr_ty, reg, ptr);
try self.load(dst_mcv, .{ .register = reg }, ptr_ty);
diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig
@@ -22,6 +22,8 @@ const Liveness = @import("../../Liveness.zig");
const Mir = @import("Mir.zig");
const Module = @import("../../Module.zig");
const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
+const RegisterManager = RegisterManagerFn(Self, Register, &allocatable_registers);
+const RegisterLock = RegisterManager.RegisterLock;
const Target = std.Target;
const Type = @import("../../type.zig").Type;
const TypedValue = @import("../../TypedValue.zig");
@@ -42,8 +44,6 @@ const InnerError = error{
OutOfRegisters,
};
-const RegisterManager = RegisterManagerFn(Self, Register, &allocatable_registers);
-
gpa: Allocator,
air: Air,
liveness: Liveness,
@@ -191,60 +191,12 @@ pub const MCValue = union(enum) {
};
}
- fn usesCompareFlags(mcv: MCValue) bool {
- return switch (mcv) {
- .compare_flags_unsigned,
- .compare_flags_signed,
- .register_overflow_unsigned,
- .register_overflow_signed,
- => true,
- else => false,
- };
- }
-
fn isRegister(mcv: MCValue) bool {
return switch (mcv) {
- .register,
- .register_overflow_unsigned,
- .register_overflow_signed,
- => true,
+ .register => true,
else => false,
};
}
-
- fn freezeIfRegister(mcv: MCValue, mgr: *RegisterManager) void {
- switch (mcv) {
- .register,
- .register_overflow_signed,
- .register_overflow_unsigned,
- => |reg| {
- mgr.freezeRegs(&.{reg});
- },
- else => {},
- }
- }
-
- fn unfreezeIfRegister(mcv: MCValue, mgr: *RegisterManager) void {
- switch (mcv) {
- .register,
- .register_overflow_signed,
- .register_overflow_unsigned,
- => |reg| {
- mgr.unfreezeRegs(&.{reg});
- },
- else => {},
- }
- }
-
- fn asRegister(mcv: MCValue) ?Register {
- return switch (mcv) {
- .register,
- .register_overflow_signed,
- .register_overflow_unsigned,
- => |reg| reg,
- else => null,
- };
- }
};
const Branch = struct {
@@ -819,7 +771,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
// zig fmt: on
}
- assert(!self.register_manager.frozenRegsExist());
+ assert(!self.register_manager.lockedRegsExist());
if (std.debug.runtime_safety) {
if (self.air_bookkeeping < old_air_bookkeeping + 1) {
@@ -876,15 +828,21 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live
const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
branch.inst_table.putAssumeCapacityNoClobber(inst, result);
- if (result.asRegister()) |reg| {
- // In some cases (such as bitcast), an operand
- // may be the same MCValue as the result. If
- // that operand died and was a register, it
- // was freed by processDeath. We have to
- // "re-allocate" the register.
- if (self.register_manager.isRegFree(reg)) {
- self.register_manager.getRegAssumeFree(reg, inst);
- }
+ switch (result) {
+ .register,
+ .register_overflow_signed,
+ .register_overflow_unsigned,
+ => |reg| {
+ // In some cases (such as bitcast), an operand
+ // may be the same MCValue as the result. If
+ // that operand died and was a register, it
+ // was freed by processDeath. We have to
+ // "re-allocate" the register.
+ if (self.register_manager.isRegFree(reg)) {
+ self.register_manager.getRegAssumeFree(reg, inst);
+ }
+ },
+ else => {},
}
}
self.finishAirBookkeeping();
@@ -955,7 +913,15 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
const stack_mcv = try self.allocRegOrMem(inst, false);
log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
const reg_mcv = self.getResolvedInstValue(inst);
- assert(reg.to64() == reg_mcv.asRegister().?.to64());
+ switch (reg_mcv) {
+ .register,
+ .register_overflow_unsigned,
+ .register_overflow_signed,
+ => |other| {
+ assert(reg.to64() == other.to64());
+ },
+ else => {},
+ }
const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
try branch.inst_table.put(self.gpa, inst, stack_mcv);
try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv, .{});
@@ -964,18 +930,32 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
pub fn spillCompareFlagsIfOccupied(self: *Self) !void {
if (self.compare_flags_inst) |inst_to_save| {
const mcv = self.getResolvedInstValue(inst_to_save);
- assert(mcv.usesCompareFlags());
+ const new_mcv = switch (mcv) {
+ .register_overflow_signed,
+ .register_overflow_unsigned,
+ => try self.allocRegOrMem(inst_to_save, false),
+ .compare_flags_signed,
+ .compare_flags_unsigned,
+ => try self.allocRegOrMem(inst_to_save, true),
+ else => unreachable,
+ };
- const new_mcv = try self.allocRegOrMem(inst_to_save, !mcv.isRegister());
try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv);
log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv });
+
const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
try branch.inst_table.put(self.gpa, inst_to_save, new_mcv);
self.compare_flags_inst = null;
+
// TODO consolidate with register manager and spillInstruction
// this call should really belong in the register manager!
- if (mcv.isRegister()) self.register_manager.freeReg(mcv.asRegister().?);
+ switch (mcv) {
+ .register_overflow_signed,
+ .register_overflow_unsigned,
+ => |reg| self.register_manager.freeReg(reg),
+ else => {},
+ }
}
}
@@ -1043,8 +1023,11 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
return self.fail("TODO implement intCast for abi sizes larger than 8", .{});
}
- operand.freezeIfRegister(&self.register_manager);
- defer operand.unfreezeIfRegister(&self.register_manager);
+ const operand_lock: ?RegisterLock = switch (operand) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (operand_lock) |lock| self.register_manager.unlockReg(lock);
const reg = try self.register_manager.allocReg(inst);
try self.genSetReg(dest_ty, reg, .{ .immediate = 0 });
@@ -1071,8 +1054,11 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
return self.fail("TODO implement trunc for abi sizes larger than 8", .{});
}
- operand.freezeIfRegister(&self.register_manager);
- defer operand.unfreezeIfRegister(&self.register_manager);
+ const operand_lock: ?RegisterLock = switch (operand) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (operand_lock) |lock| self.register_manager.unlockReg(lock);
const reg: Register = blk: {
if (operand.isRegister()) {
@@ -1156,16 +1142,22 @@ fn airMin(self: *Self, inst: Air.Inst.Index) !void {
// TODO improve by checking if any operand can be reused.
// TODO audit register allocation
const lhs = try self.resolveInst(bin_op.lhs);
- lhs.freezeIfRegister(&self.register_manager);
- defer lhs.unfreezeIfRegister(&self.register_manager);
+ const lhs_lock: ?RegisterLock = switch (lhs) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock);
const lhs_reg = try self.copyToTmpRegister(ty, lhs);
- self.register_manager.freezeRegs(&.{lhs_reg});
- defer self.register_manager.unfreezeRegs(&.{lhs_reg});
+ const lhs_reg_lock = self.register_manager.lockRegAssumeUnused(lhs_reg);
+ defer self.register_manager.unlockReg(lhs_reg_lock);
const rhs_mcv = try self.limitImmediateType(bin_op.rhs, i32);
- rhs_mcv.freezeIfRegister(&self.register_manager);
- defer rhs_mcv.unfreezeIfRegister(&self.register_manager);
+ const rhs_lock: ?RegisterLock = switch (rhs_mcv) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
try self.genBinMathOpMir(.cmp, ty, .{ .register = lhs_reg }, rhs_mcv);
@@ -1200,28 +1192,37 @@ fn genPtrBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_r
const offset = try self.resolveInst(op_rhs);
const offset_ty = self.air.typeOf(op_rhs);
- offset.freezeIfRegister(&self.register_manager);
- defer offset.unfreezeIfRegister(&self.register_manager);
+ const offset_lock: ?RegisterLock = switch (offset) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (offset_lock) |lock| self.register_manager.unlockReg(lock);
- const dst_mcv = blk: {
+ const dst_mcv: MCValue = blk: {
if (self.reuseOperand(inst, op_lhs, 0, ptr)) {
if (ptr.isMemory() or ptr.isRegister()) break :blk ptr;
}
break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, ptr) };
};
- dst_mcv.freezeIfRegister(&self.register_manager);
- defer dst_mcv.unfreezeIfRegister(&self.register_manager);
+ const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock);
- const offset_mcv = blk: {
+ const offset_mcv: MCValue = blk: {
if (self.reuseOperand(inst, op_rhs, 1, offset)) {
if (offset.isRegister()) break :blk offset;
}
break :blk MCValue{ .register = try self.copyToTmpRegister(offset_ty, offset) };
};
- offset_mcv.freezeIfRegister(&self.register_manager);
- defer offset_mcv.unfreezeIfRegister(&self.register_manager);
+ const offset_mcv_lock: ?RegisterLock = switch (offset_mcv) {
+ .register => |reg| self.register_manager.lockReg(reg),
+ else => null,
+ };
+ defer if (offset_mcv_lock) |lock| self.register_manager.unlockReg(lock);
try self.genIntMulComplexOpMir(offset_ty, offset_mcv, .{ .immediate = elem_size });
@@ -1306,30 +1307,40 @@ fn genSubOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air
const dst_ty = self.air.typeOf(op_lhs);
const lhs = try self.resolveInst(op_lhs);
- lhs.freezeIfRegister(&self.register_manager);
- defer lhs.unfreezeIfRegister(&self.register_manager);
+ const lhs_lock: ?RegisterLock = switch (lhs) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock);
const rhs = try self.resolveInst(op_rhs);
- rhs.freezeIfRegister(&self.register_manager);
- defer rhs.unfreezeIfRegister(&self.register_manager);
+ const rhs_lock: ?RegisterLock = switch (rhs) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
- const dst_mcv = blk: {
+ const dst_mcv: MCValue = blk: {
if (self.reuseOperand(inst, op_lhs, 0, lhs) and lhs.isRegister()) {
break :blk lhs;
}
break :blk try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs);
};
+ const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) {
+ .register => |reg| self.register_manager.lockReg(reg),
+ else => null,
+ };
+ defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock);
- dst_mcv.freezeIfRegister(&self.register_manager);
- defer dst_mcv.unfreezeIfRegister(&self.register_manager);
-
- const rhs_mcv = blk: {
+ const rhs_mcv: MCValue = blk: {
if (rhs.isMemory() or rhs.isRegister()) break :blk rhs;
break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, rhs) };
};
-
- rhs_mcv.freezeIfRegister(&self.register_manager);
- defer rhs_mcv.unfreezeIfRegister(&self.register_manager);
+ const rhs_mcv_lock: ?RegisterLock = switch (rhs_mcv) {
+ .register => |reg| self.register_manager.lockReg(reg),
+ else => null,
+ };
+ defer if (rhs_mcv_lock) |lock| self.register_manager.unlockReg(lock);
try self.genBinMathOpMir(.sub, dst_ty, dst_mcv, rhs_mcv);
@@ -1366,8 +1377,10 @@ fn airMul(self: *Self, inst: Air.Inst.Index) !void {
// Spill .rax and .rdx upfront to ensure we don't spill the operands too late.
try self.register_manager.getReg(.rax, inst);
try self.register_manager.getReg(.rdx, null);
- self.register_manager.freezeRegs(&.{ .rax, .rdx });
- defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx });
+ const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx });
+ defer for (reg_locks) |reg| {
+ self.register_manager.unlockReg(reg);
+ };
const lhs = try self.resolveInst(bin_op.lhs);
const rhs = try self.resolveInst(bin_op.rhs);
@@ -1458,9 +1471,13 @@ fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
- const result = if (self.liveness.isUnused(inst)) .dead else result: {
- const ty = self.air.typeOf(bin_op.lhs);
+ if (self.liveness.isUnused(inst)) {
+ return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
+ }
+
+ const ty = self.air.typeOf(bin_op.lhs);
+ const result: MCValue = result: {
switch (ty.zigTypeTag()) {
.Vector => return self.fail("TODO implement mul_with_overflow for Vector type", .{}),
.Int => {
@@ -1477,8 +1494,10 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
// Spill .rax and .rdx upfront to ensure we don't spill the operands too late.
try self.register_manager.getReg(.rax, inst);
try self.register_manager.getReg(.rdx, null);
- self.register_manager.freezeRegs(&.{ .rax, .rdx });
- defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx });
+ const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx });
+ defer for (reg_locks) |reg| {
+ self.register_manager.unlockReg(reg);
+ };
const lhs = try self.resolveInst(bin_op.lhs);
const rhs = try self.resolveInst(bin_op.rhs);
@@ -1504,21 +1523,28 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
const lhs = try self.resolveInst(bin_op.lhs);
const rhs = try self.resolveInst(bin_op.rhs);
- rhs.freezeIfRegister(&self.register_manager);
- defer rhs.unfreezeIfRegister(&self.register_manager);
+ const rhs_lock: ?RegisterLock = switch (rhs) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
const dst_reg: Register = blk: {
if (lhs.isRegister()) break :blk lhs.register;
break :blk try self.copyToTmpRegister(ty, lhs);
};
- self.register_manager.freezeRegs(&.{dst_reg});
+ const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
+ defer self.register_manager.unlockReg(dst_reg_lock);
- const rhs_mcv = blk: {
+ const rhs_mcv: MCValue = blk: {
if (rhs.isRegister() or rhs.isMemory()) break :blk rhs;
break :blk MCValue{ .register = try self.copyToTmpRegister(ty, rhs) };
};
- rhs_mcv.freezeIfRegister(&self.register_manager);
- defer rhs_mcv.unfreezeIfRegister(&self.register_manager);
+ const rhs_mcv_lock: ?RegisterLock = switch (rhs_mcv) {
+ .register => |reg| self.register_manager.lockReg(reg),
+ else => null,
+ };
+ defer if (rhs_mcv_lock) |lock| self.register_manager.unlockReg(lock);
try self.genIntMulComplexOpMir(Type.isize, .{ .register = dst_reg }, rhs_mcv);
@@ -1528,8 +1554,10 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
// Spill .rax and .rdx upfront to ensure we don't spill the operands too late.
try self.register_manager.getReg(.rax, null);
try self.register_manager.getReg(.rdx, null);
- self.register_manager.freezeRegs(&.{ .rax, .rdx });
- defer self.register_manager.unfreezeRegs(&.{.rdx});
+ const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx });
+ defer for (reg_locks) |reg| {
+ self.register_manager.unlockReg(reg);
+ };
const lhs = try self.resolveInst(bin_op.lhs);
const rhs = try self.resolveInst(bin_op.rhs);
@@ -1540,7 +1568,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
},
}
};
- defer self.register_manager.unfreezeRegs(&.{dst_reg});
+ const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
+ defer self.register_manager.unlockReg(dst_reg_lock);
const tuple_ty = self.air.typeOfIndex(inst);
const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*));
@@ -1554,8 +1583,10 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
};
const temp_regs = try self.register_manager.allocRegs(3, .{ null, null, null });
- self.register_manager.freezeRegs(&temp_regs);
- defer self.register_manager.unfreezeRegs(&temp_regs);
+ const temp_regs_locks = self.register_manager.lockRegsAssumeUnused(3, temp_regs);
+ defer for (temp_regs_locks) |reg| {
+ self.register_manager.unlockReg(reg);
+ };
const overflow_reg = temp_regs[0];
const flags: u2 = switch (int_info.signedness) {
@@ -1699,18 +1730,19 @@ fn genIntMulDivOpMir(
/// Clobbers .rax and .rdx registers.
fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCValue {
const signedness = ty.intInfo(self.target.*).signedness;
- const dividend = switch (lhs) {
+ const dividend: Register = switch (lhs) {
.register => |reg| reg,
else => try self.copyToTmpRegister(ty, lhs),
};
- self.register_manager.freezeRegs(&.{dividend});
+ const dividend_lock = self.register_manager.lockReg(dividend);
+ defer if (dividend_lock) |lock| self.register_manager.unlockReg(lock);
- const divisor = switch (rhs) {
+ const divisor: Register = switch (rhs) {
.register => |reg| reg,
else => try self.copyToTmpRegister(ty, rhs),
};
- self.register_manager.freezeRegs(&.{divisor});
- defer self.register_manager.unfreezeRegs(&.{ dividend, divisor });
+ const divisor_lock = self.register_manager.lockReg(divisor);
+ defer if (divisor_lock) |lock| self.register_manager.unlockReg(lock);
try self.genIntMulDivOpMir(switch (signedness) {
.signed => .idiv,
@@ -1755,54 +1787,71 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa
fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
- const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
- const tag = self.air.instructions.items(.tag)[inst];
- const ty = self.air.typeOfIndex(inst);
- if (ty.zigTypeTag() != .Int) {
- return self.fail("TODO implement {} for operands of dst type {}", .{ tag, ty.zigTypeTag() });
- }
+ if (self.liveness.isUnused(inst)) {
+ return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
+ }
+
+ const tag = self.air.instructions.items(.tag)[inst];
+ const ty = self.air.typeOfIndex(inst);
- if (tag == .div_float) {
- return self.fail("TODO implement {}", .{tag});
+ if (ty.zigTypeTag() != .Int) {
+ return self.fail("TODO implement {} for operands of dst type {}", .{ tag, ty.zigTypeTag() });
+ }
+
+ if (tag == .div_float) {
+ return self.fail("TODO implement {}", .{tag});
+ }
+
+ const signedness = ty.intInfo(self.target.*).signedness;
+
+ // Spill .rax and .rdx upfront to ensure we don't spill the operands too late.
+ const track_rax: ?Air.Inst.Index = blk: {
+ if (signedness == .unsigned) break :blk inst;
+ switch (tag) {
+ .div_exact, .div_trunc => break :blk inst,
+ else => break :blk null,
}
+ };
+ try self.register_manager.getReg(.rax, track_rax);
+ try self.register_manager.getReg(.rdx, null);
+ const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx });
+ defer for (reg_locks) |reg| {
+ self.register_manager.unlockReg(reg);
+ };
- const signedness = ty.intInfo(self.target.*).signedness;
+ const lhs = try self.resolveInst(bin_op.lhs);
+ const lhs_lock: ?RegisterLock = switch (lhs) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock);
- // Spill .rax and .rdx upfront to ensure we don't spill the operands too late.
- const track_rax: ?Air.Inst.Index = blk: {
- if (signedness == .unsigned) break :blk inst;
+ const rhs: MCValue = blk: {
+ const rhs = try self.resolveInst(bin_op.rhs);
+ if (signedness == .signed) {
switch (tag) {
- .div_exact, .div_trunc => break :blk inst,
- else => break :blk null,
- }
- };
- try self.register_manager.getReg(.rax, track_rax);
- try self.register_manager.getReg(.rdx, null);
- self.register_manager.freezeRegs(&.{ .rax, .rdx });
- defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx });
+ .div_floor => {
+ const rhs_lock: ?RegisterLock = switch (rhs) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
- const lhs = try self.resolveInst(bin_op.lhs);
- lhs.freezeIfRegister(&self.register_manager);
- defer lhs.unfreezeIfRegister(&self.register_manager);
-
- const rhs = blk: {
- const rhs = try self.resolveInst(bin_op.rhs);
- if (signedness == .signed) {
- switch (tag) {
- .div_floor => {
- rhs.freezeIfRegister(&self.register_manager);
- defer rhs.unfreezeIfRegister(&self.register_manager);
- break :blk try self.copyToRegisterWithInstTracking(inst, ty, rhs);
- },
- else => {},
- }
+ break :blk try self.copyToRegisterWithInstTracking(inst, ty, rhs);
+ },
+ else => {},
}
- break :blk rhs;
- };
- rhs.freezeIfRegister(&self.register_manager);
- defer rhs.unfreezeIfRegister(&self.register_manager);
+ }
+ break :blk rhs;
+ };
+ const rhs_lock: ?RegisterLock = switch (rhs) {
+ .register => |reg| self.register_manager.lockReg(reg),
+ else => null,
+ };
+ defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
+ const result: MCValue = result: {
if (signedness == .unsigned) {
try self.genIntMulDivOpMir(.div, ty, signedness, lhs, rhs);
break :result MCValue{ .register = .rax };
@@ -1822,53 +1871,67 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
else => unreachable,
}
};
+
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
}
fn airRem(self: *Self, inst: Air.Inst.Index) !void {
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
- const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
- const ty = self.air.typeOfIndex(inst);
- if (ty.zigTypeTag() != .Int) {
- return self.fail("TODO implement .rem for operands of dst type {}", .{ty.zigTypeTag()});
- }
- // Spill .rax and .rdx upfront to ensure we don't spill the operands too late.
- try self.register_manager.getReg(.rax, null);
- try self.register_manager.getReg(.rdx, inst);
- self.register_manager.freezeRegs(&.{ .rax, .rdx });
- defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx });
-
- const lhs = try self.resolveInst(bin_op.lhs);
- const rhs = try self.resolveInst(bin_op.rhs);
- const signedness = ty.intInfo(self.target.*).signedness;
- try self.genIntMulDivOpMir(switch (signedness) {
- .signed => .idiv,
- .unsigned => .div,
- }, ty, signedness, lhs, rhs);
- break :result MCValue{ .register = .rdx };
+ if (self.liveness.isUnused(inst)) {
+ return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
+ }
+ const ty = self.air.typeOfIndex(inst);
+ if (ty.zigTypeTag() != .Int) {
+ return self.fail("TODO implement .rem for operands of dst type {}", .{ty.zigTypeTag()});
+ }
+ // Spill .rax and .rdx upfront to ensure we don't spill the operands too late.
+ try self.register_manager.getReg(.rax, null);
+ try self.register_manager.getReg(.rdx, inst);
+ const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx });
+ defer for (reg_locks) |reg| {
+ self.register_manager.unlockReg(reg);
};
+
+ const lhs = try self.resolveInst(bin_op.lhs);
+ const rhs = try self.resolveInst(bin_op.rhs);
+
+ const signedness = ty.intInfo(self.target.*).signedness;
+ try self.genIntMulDivOpMir(switch (signedness) {
+ .signed => .idiv,
+ .unsigned => .div,
+ }, ty, signedness, lhs, rhs);
+
+ const result: MCValue = .{ .register = .rdx };
+
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
}
fn airMod(self: *Self, inst: Air.Inst.Index) !void {
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
- const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
- const ty = self.air.typeOfIndex(inst);
- if (ty.zigTypeTag() != .Int) {
- return self.fail("TODO implement .mod for operands of dst type {}", .{ty.zigTypeTag()});
- }
- const signedness = ty.intInfo(self.target.*).signedness;
- // Spill .rax and .rdx upfront to ensure we don't spill the operands too late.
- try self.register_manager.getReg(.rax, null);
- try self.register_manager.getReg(.rdx, if (signedness == .unsigned) inst else null);
- self.register_manager.freezeRegs(&.{ .rax, .rdx });
- defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx });
+ if (self.liveness.isUnused(inst)) {
+ return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
+ }
- const lhs = try self.resolveInst(bin_op.lhs);
- const rhs = try self.resolveInst(bin_op.rhs);
+ const ty = self.air.typeOfIndex(inst);
+ if (ty.zigTypeTag() != .Int) {
+ return self.fail("TODO implement .mod for operands of dst type {}", .{ty.zigTypeTag()});
+ }
+ const signedness = ty.intInfo(self.target.*).signedness;
+
+ // Spill .rax and .rdx upfront to ensure we don't spill the operands too late.
+ try self.register_manager.getReg(.rax, null);
+ try self.register_manager.getReg(.rdx, if (signedness == .unsigned) inst else null);
+ const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx });
+ defer for (reg_locks) |reg| {
+ self.register_manager.unlockReg(reg);
+ };
+
+ const lhs = try self.resolveInst(bin_op.lhs);
+ const rhs = try self.resolveInst(bin_op.rhs);
+ const result: MCValue = result: {
switch (signedness) {
.unsigned => {
try self.genIntMulDivOpMir(switch (signedness) {
@@ -1888,6 +1951,7 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void {
},
}
};
+
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
}
@@ -1954,12 +2018,15 @@ fn airShl(self: *Self, inst: Air.Inst.Index) !void {
try self.register_manager.getReg(.rcx, null);
try self.genSetReg(shift_ty, .rcx, shift);
}
- self.register_manager.freezeRegs(&.{.rcx});
- defer self.register_manager.unfreezeRegs(&.{.rcx});
+ const rcx_lock = self.register_manager.lockRegAssumeUnused(.rcx);
+ defer self.register_manager.unlockReg(rcx_lock);
const value = try self.resolveInst(bin_op.lhs);
- value.freezeIfRegister(&self.register_manager);
- defer value.unfreezeIfRegister(&self.register_manager);
+ const value_lock: ?RegisterLock = switch (value) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (value_lock) |lock| self.register_manager.unlockReg(lock);
const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, value);
_ = try self.addInst(.{
@@ -2055,8 +2122,11 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
const err_ty = err_union_ty.errorUnionSet();
const payload_ty = err_union_ty.errorUnionPayload();
const operand = try self.resolveInst(ty_op.operand);
- operand.freezeIfRegister(&self.register_manager);
- defer operand.unfreezeIfRegister(&self.register_manager);
+ const operand_lock: ?RegisterLock = switch (operand) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (operand_lock) |lock| self.register_manager.unlockReg(lock);
const result: MCValue = result: {
if (!payload_ty.hasRuntimeBits()) break :result operand;
@@ -2085,8 +2155,11 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
if (!payload_ty.hasRuntimeBits()) break :result MCValue.none;
const operand = try self.resolveInst(ty_op.operand);
- operand.freezeIfRegister(&self.register_manager);
- defer operand.unfreezeIfRegister(&self.register_manager);
+ const operand_lock: ?RegisterLock = switch (operand) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (operand_lock) |lock| self.register_manager.unlockReg(lock);
const abi_align = err_union_ty.abiAlignment(self.target.*);
const err_ty = err_union_ty.errorUnionSet();
@@ -2154,8 +2227,11 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
const optional_ty = self.air.typeOfIndex(inst);
const operand = try self.resolveInst(ty_op.operand);
- operand.freezeIfRegister(&self.register_manager);
- defer operand.unfreezeIfRegister(&self.register_manager);
+ const operand_lock: ?RegisterLock = switch (operand) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (operand_lock) |lock| self.register_manager.unlockReg(lock);
if (optional_ty.isPtrLikeOptional()) {
// TODO should we check if we can reuse the operand?
@@ -2288,8 +2364,11 @@ fn elemOffset(self: *Self, index_ty: Type, index: MCValue, elem_size: u64) !Regi
fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue {
const slice_ty = self.air.typeOf(lhs);
const slice_mcv = try self.resolveInst(lhs);
- slice_mcv.freezeIfRegister(&self.register_manager);
- defer slice_mcv.unfreezeIfRegister(&self.register_manager);
+ const slice_mcv_lock: ?RegisterLock = switch (slice_mcv) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (slice_mcv_lock) |lock| self.register_manager.unlockReg(lock);
const elem_ty = slice_ty.childType();
const elem_size = elem_ty.abiSize(self.target.*);
@@ -2298,12 +2377,15 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue {
const index_ty = self.air.typeOf(rhs);
const index_mcv = try self.resolveInst(rhs);
- index_mcv.freezeIfRegister(&self.register_manager);
- defer index_mcv.unfreezeIfRegister(&self.register_manager);
+ const index_mcv_lock: ?RegisterLock = switch (index_mcv) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (index_mcv_lock) |lock| self.register_manager.unlockReg(lock);
const offset_reg = try self.elemOffset(index_ty, index_mcv, elem_size);
- self.register_manager.freezeRegs(&.{offset_reg});
- defer self.register_manager.unfreezeRegs(&.{offset_reg});
+ const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg);
+ defer self.register_manager.unlockReg(offset_reg_lock);
const addr_reg = try self.register_manager.allocReg(null);
switch (slice_mcv) {
@@ -2356,98 +2438,119 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {
fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
- const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
- const array_ty = self.air.typeOf(bin_op.lhs);
- const array = try self.resolveInst(bin_op.lhs);
- array.freezeIfRegister(&self.register_manager);
- defer array.unfreezeIfRegister(&self.register_manager);
-
- const elem_ty = array_ty.childType();
- const elem_abi_size = elem_ty.abiSize(self.target.*);
- const index_ty = self.air.typeOf(bin_op.rhs);
- const index = try self.resolveInst(bin_op.rhs);
- index.freezeIfRegister(&self.register_manager);
- defer index.unfreezeIfRegister(&self.register_manager);
+ if (self.liveness.isUnused(inst)) {
+ return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
+ }
- const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size);
- self.register_manager.freezeRegs(&.{offset_reg});
- defer self.register_manager.unfreezeRegs(&.{offset_reg});
+ const array_ty = self.air.typeOf(bin_op.lhs);
+ const array = try self.resolveInst(bin_op.lhs);
+ const array_lock: ?RegisterLock = switch (array) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (array_lock) |lock| self.register_manager.unlockReg(lock);
- const addr_reg = try self.register_manager.allocReg(null);
- switch (array) {
- .register => {
- const off = @intCast(i32, try self.allocMem(
- inst,
- @intCast(u32, array_ty.abiSize(self.target.*)),
- array_ty.abiAlignment(self.target.*),
- ));
- try self.genSetStack(array_ty, off, array, .{});
- // lea reg, [rbp]
- _ = try self.addInst(.{
- .tag = .lea,
- .ops = (Mir.Ops{
- .reg1 = addr_reg.to64(),
- .reg2 = .rbp,
- }).encode(),
- .data = .{ .imm = @bitCast(u32, -off) },
- });
- },
- .stack_offset => |off| {
- // lea reg, [rbp]
- _ = try self.addInst(.{
- .tag = .lea,
- .ops = (Mir.Ops{
- .reg1 = addr_reg.to64(),
- .reg2 = .rbp,
- }).encode(),
- .data = .{ .imm = @bitCast(u32, -off) },
- });
- },
- .memory,
- .got_load,
- .direct_load,
- => {
- try self.loadMemPtrIntoRegister(addr_reg, Type.usize, array);
- },
- else => return self.fail("TODO implement array_elem_val when array is {}", .{array}),
- }
+ const elem_ty = array_ty.childType();
+ const elem_abi_size = elem_ty.abiSize(self.target.*);
- // TODO we could allocate register here, but need to expect addr register and potentially
- // offset register.
- const dst_mcv = try self.allocRegOrMem(inst, false);
- try self.genBinMathOpMir(.add, Type.usize, .{ .register = addr_reg }, .{ .register = offset_reg });
- try self.load(dst_mcv, .{ .register = addr_reg.to64() }, array_ty);
- break :result dst_mcv;
+ const index_ty = self.air.typeOf(bin_op.rhs);
+ const index = try self.resolveInst(bin_op.rhs);
+ const index_lock: ?RegisterLock = switch (index) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
};
- return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
+ defer if (index_lock) |lock| self.register_manager.unlockReg(lock);
+
+ const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size);
+ const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg);
+ defer self.register_manager.unlockReg(offset_reg_lock);
+
+ const addr_reg = try self.register_manager.allocReg(null);
+ switch (array) {
+ .register => {
+ const off = @intCast(i32, try self.allocMem(
+ inst,
+ @intCast(u32, array_ty.abiSize(self.target.*)),
+ array_ty.abiAlignment(self.target.*),
+ ));
+ try self.genSetStack(array_ty, off, array, .{});
+ // lea reg, [rbp]
+ _ = try self.addInst(.{
+ .tag = .lea,
+ .ops = (Mir.Ops{
+ .reg1 = addr_reg.to64(),
+ .reg2 = .rbp,
+ }).encode(),
+ .data = .{ .imm = @bitCast(u32, -off) },
+ });
+ },
+ .stack_offset => |off| {
+ // lea reg, [rbp]
+ _ = try self.addInst(.{
+ .tag = .lea,
+ .ops = (Mir.Ops{
+ .reg1 = addr_reg.to64(),
+ .reg2 = .rbp,
+ }).encode(),
+ .data = .{ .imm = @bitCast(u32, -off) },
+ });
+ },
+ .memory,
+ .got_load,
+ .direct_load,
+ => {
+ try self.loadMemPtrIntoRegister(addr_reg, Type.usize, array);
+ },
+ else => return self.fail("TODO implement array_elem_val when array is {}", .{array}),
+ }
+
+ // TODO we could allocate register here, but need to expect addr register and potentially
+ // offset register.
+ const dst_mcv = try self.allocRegOrMem(inst, false);
+ try self.genBinMathOpMir(.add, Type.usize, .{ .register = addr_reg }, .{ .register = offset_reg });
+ try self.load(dst_mcv, .{ .register = addr_reg.to64() }, array_ty);
+
+ return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none });
}
fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
const is_volatile = false; // TODO
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
- const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else result: {
- // this is identical to the `airPtrElemPtr` codegen expect here an
- // additional `mov` is needed at the end to get the actual value
-
- const ptr_ty = self.air.typeOf(bin_op.lhs);
- const ptr = try self.resolveInst(bin_op.lhs);
- ptr.freezeIfRegister(&self.register_manager);
- defer ptr.unfreezeIfRegister(&self.register_manager);
-
- const elem_ty = ptr_ty.elemType2();
- const elem_abi_size = elem_ty.abiSize(self.target.*);
- const index_ty = self.air.typeOf(bin_op.rhs);
- const index = try self.resolveInst(bin_op.rhs);
- index.freezeIfRegister(&self.register_manager);
- defer index.unfreezeIfRegister(&self.register_manager);
-
- const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size);
- self.register_manager.freezeRegs(&.{offset_reg});
- defer self.register_manager.unfreezeRegs(&.{offset_reg});
-
- const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr);
- try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg });
+
+ if (!is_volatile and self.liveness.isUnused(inst)) {
+ return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
+ }
+
+ // this is identical to the `airPtrElemPtr` codegen expect here an
+ // additional `mov` is needed at the end to get the actual value
+
+ const ptr_ty = self.air.typeOf(bin_op.lhs);
+ const ptr = try self.resolveInst(bin_op.lhs);
+ const ptr_lock: ?RegisterLock = switch (ptr) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock);
+
+ const elem_ty = ptr_ty.elemType2();
+ const elem_abi_size = elem_ty.abiSize(self.target.*);
+ const index_ty = self.air.typeOf(bin_op.rhs);
+ const index = try self.resolveInst(bin_op.rhs);
+ const index_lock: ?RegisterLock = switch (index) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (index_lock) |lock| self.register_manager.unlockReg(lock);
+
+ const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size);
+ const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg);
+ defer self.register_manager.unlockReg(offset_reg_lock);
+
+ const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr);
+ try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg });
+
+ const result: MCValue = result: {
if (elem_abi_size > 8) {
return self.fail("TODO copy value with size {} from pointer", .{elem_abi_size});
} else {
@@ -2464,34 +2567,44 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
break :result .{ .register = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)) };
}
};
+
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
}
fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
- const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
- const ptr_ty = self.air.typeOf(extra.lhs);
- const ptr = try self.resolveInst(extra.lhs);
- ptr.freezeIfRegister(&self.register_manager);
- defer ptr.unfreezeIfRegister(&self.register_manager);
-
- const elem_ty = ptr_ty.elemType2();
- const elem_abi_size = elem_ty.abiSize(self.target.*);
- const index_ty = self.air.typeOf(extra.rhs);
- const index = try self.resolveInst(extra.rhs);
- index.freezeIfRegister(&self.register_manager);
- defer index.unfreezeIfRegister(&self.register_manager);
-
- const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size);
- self.register_manager.freezeRegs(&.{offset_reg});
- defer self.register_manager.unfreezeRegs(&.{offset_reg});
-
- const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr);
- try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg });
- break :result dst_mcv;
+
+ if (self.liveness.isUnused(inst)) {
+ return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });
+ }
+
+ const ptr_ty = self.air.typeOf(extra.lhs);
+ const ptr = try self.resolveInst(extra.lhs);
+ const ptr_lock: ?RegisterLock = switch (ptr) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
};
- return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
+ defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock);
+
+ const elem_ty = ptr_ty.elemType2();
+ const elem_abi_size = elem_ty.abiSize(self.target.*);
+ const index_ty = self.air.typeOf(extra.rhs);
+ const index = try self.resolveInst(extra.rhs);
+ const index_lock: ?RegisterLock = switch (index) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (index_lock) |lock| self.register_manager.unlockReg(lock);
+
+ const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size);
+ const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg);
+ defer self.register_manager.unlockReg(offset_reg_lock);
+
+ const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr);
+ try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg });
+
+ return self.finishAir(inst, dst_mcv, .{ extra.lhs, extra.rhs, .none });
}
fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
@@ -2506,12 +2619,18 @@ fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
}
const ptr = try self.resolveInst(bin_op.lhs);
- ptr.freezeIfRegister(&self.register_manager);
- defer ptr.unfreezeIfRegister(&self.register_manager);
+ const ptr_lock: ?RegisterLock = switch (ptr) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock);
const tag = try self.resolveInst(bin_op.rhs);
- tag.freezeIfRegister(&self.register_manager);
- defer tag.unfreezeIfRegister(&self.register_manager);
+ const tag_lock: ?RegisterLock = switch (tag) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (tag_lock) |lock| self.register_manager.unlockReg(lock);
const adjusted_ptr: MCValue = if (layout.payload_size > 0 and layout.tag_align < layout.payload_align) blk: {
// TODO reusing the operand
@@ -2541,8 +2660,11 @@ fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
// TODO reusing the operand
const operand = try self.resolveInst(ty_op.operand);
- operand.freezeIfRegister(&self.register_manager);
- defer operand.unfreezeIfRegister(&self.register_manager);
+ const operand_lock: ?RegisterLock = switch (operand) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (operand_lock) |lock| self.register_manager.unlockReg(lock);
const tag_abi_size = tag_ty.abiSize(self.target.*);
const dst_mcv: MCValue = blk: {
@@ -2689,8 +2811,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off });
},
.register => |reg| {
- self.register_manager.freezeRegs(&.{reg});
- defer self.register_manager.unfreezeRegs(&.{reg});
+ const reg_lock = self.register_manager.lockReg(reg);
+ defer if (reg_lock) |lock| self.register_manager.unlockReg(lock);
switch (dst_mcv) {
.dead => unreachable,
@@ -2815,8 +2937,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
try self.genSetStack(value_ty, off, value, .{});
},
.register => |reg| {
- self.register_manager.freezeRegs(&.{reg});
- defer self.register_manager.unfreezeRegs(&.{reg});
+ const reg_lock = self.register_manager.lockReg(reg);
+ defer if (reg_lock) |lock| self.register_manager.unlockReg(lock);
switch (value) {
.none => unreachable,
@@ -2906,12 +3028,15 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
.direct_load,
.memory,
=> {
- value.freezeIfRegister(&self.register_manager);
- defer value.unfreezeIfRegister(&self.register_manager);
+ const value_lock: ?RegisterLock = switch (value) {
+ .register => |reg| self.register_manager.lockReg(reg),
+ else => null,
+ };
+ defer if (value_lock) |lock| self.register_manager.unlockReg(lock);
const addr_reg = try self.register_manager.allocReg(null);
- self.register_manager.freezeRegs(&.{addr_reg});
- defer self.register_manager.unfreezeRegs(&.{addr_reg});
+ const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
+ defer self.register_manager.unlockReg(addr_reg_lock);
try self.loadMemPtrIntoRegister(addr_reg, ptr_ty, ptr);
@@ -2982,8 +3107,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
=> {
if (abi_size <= 8) {
const tmp_reg = try self.register_manager.allocReg(null);
- self.register_manager.freezeRegs(&.{tmp_reg});
- defer self.register_manager.unfreezeRegs(&.{tmp_reg});
+ const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
+ defer self.register_manager.unlockReg(tmp_reg_lock);
try self.loadMemPtrIntoRegister(tmp_reg, value_ty, value);
@@ -3073,8 +3198,8 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
const offset_reg = try self.copyToTmpRegister(ptr_ty, .{
.immediate = struct_field_offset,
});
- self.register_manager.freezeRegs(&.{offset_reg});
- defer self.register_manager.unfreezeRegs(&.{offset_reg});
+ const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg);
+ defer self.register_manager.unlockReg(offset_reg_lock);
const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, mcv);
try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg });
@@ -3085,24 +3210,27 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
break :result MCValue{ .ptr_stack_offset = ptr_stack_offset };
},
.register => |reg| {
+ const reg_lock = self.register_manager.lockRegAssumeUnused(reg);
+ defer self.register_manager.unlockReg(reg_lock);
+
const offset_reg = try self.copyToTmpRegister(ptr_ty, .{
.immediate = struct_field_offset,
});
- self.register_manager.freezeRegs(&.{offset_reg});
- defer self.register_manager.unfreezeRegs(&.{offset_reg});
+ const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg);
+ defer self.register_manager.unlockReg(offset_reg_lock);
const can_reuse_operand = self.reuseOperand(inst, operand, 0, mcv);
- const result_reg = blk: {
+ const result_reg: Register = blk: {
if (can_reuse_operand) {
break :blk reg;
} else {
- self.register_manager.freezeRegs(&.{reg});
const result_reg = try self.register_manager.allocReg(inst);
try self.genSetReg(ptr_ty, result_reg, mcv);
break :blk result_reg;
}
};
- defer if (!can_reuse_operand) self.register_manager.unfreezeRegs(&.{reg});
+ const result_reg_lock = self.register_manager.lockReg(result_reg);
+ defer if (result_reg_lock) |lock| self.register_manager.unlockReg(lock);
try self.genBinMathOpMir(.add, ptr_ty, .{ .register = result_reg }, .{ .register = offset_reg });
break :result MCValue{ .register = result_reg };
@@ -3118,22 +3246,27 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
const operand = extra.struct_operand;
const index = extra.field_index;
- const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
- const mcv = try self.resolveInst(operand);
- const struct_ty = self.air.typeOf(operand);
- const struct_field_offset = struct_ty.structFieldOffset(index, self.target.*);
- const struct_field_ty = struct_ty.structFieldType(index);
+ if (self.liveness.isUnused(inst)) {
+ return self.finishAir(inst, .dead, .{ extra.struct_operand, .none, .none });
+ }
+
+ const mcv = try self.resolveInst(operand);
+ const struct_ty = self.air.typeOf(operand);
+ const struct_field_offset = struct_ty.structFieldOffset(index, self.target.*);
+ const struct_field_ty = struct_ty.structFieldType(index);
+
+ const result: MCValue = result: {
switch (mcv) {
.stack_offset => |off| {
const stack_offset = off - @intCast(i32, struct_field_offset);
break :result MCValue{ .stack_offset = stack_offset };
},
.register => |reg| {
- self.register_manager.freezeRegs(&.{reg});
- defer self.register_manager.unfreezeRegs(&.{reg});
+ const reg_lock = self.register_manager.lockRegAssumeUnused(reg);
+ defer self.register_manager.unlockReg(reg_lock);
- const dst_mcv = blk: {
+ const dst_mcv: MCValue = blk: {
if (self.reuseOperand(inst, operand, 0, mcv)) {
break :blk mcv;
} else {
@@ -3143,8 +3276,11 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
break :blk dst_mcv;
}
};
- dst_mcv.freezeIfRegister(&self.register_manager);
- defer dst_mcv.unfreezeIfRegister(&self.register_manager);
+ const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) {
+ .register => |a_reg| self.register_manager.lockReg(a_reg),
+ else => null,
+ };
+ defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock);
// Shift by struct_field_offset.
const shift = @intCast(u8, struct_field_offset * @sizeOf(usize));
@@ -3186,8 +3322,8 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
},
1 => {
// Get overflow bit.
- mcv.freezeIfRegister(&self.register_manager);
- defer mcv.unfreezeIfRegister(&self.register_manager);
+ const reg_lock = self.register_manager.lockRegAssumeUnused(reg);
+ defer self.register_manager.unlockReg(reg_lock);
const dst_reg = try self.register_manager.allocReg(inst);
const flags: u2 = switch (mcv) {
@@ -3229,15 +3365,21 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:
const dst_ty = self.air.typeOf(op_lhs);
const lhs = try self.resolveInst(op_lhs);
- lhs.freezeIfRegister(&self.register_manager);
- defer lhs.unfreezeIfRegister(&self.register_manager);
+ const lhs_lock: ?RegisterLock = switch (lhs) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock);
const rhs = try self.resolveInst(op_rhs);
- rhs.freezeIfRegister(&self.register_manager);
- defer rhs.unfreezeIfRegister(&self.register_manager);
+ const rhs_lock: ?RegisterLock = switch (rhs) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock);
var flipped: bool = false;
- const dst_mcv = blk: {
+ const dst_mcv: MCValue = blk: {
if (self.reuseOperand(inst, op_lhs, 0, lhs) and lhs.isRegister()) {
break :blk lhs;
}
@@ -3247,16 +3389,22 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:
}
break :blk try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs);
};
- dst_mcv.freezeIfRegister(&self.register_manager);
- defer dst_mcv.unfreezeIfRegister(&self.register_manager);
+ const dst_mcv_lock: ?RegisterLock = switch (dst_mcv) {
+ .register => |reg| self.register_manager.lockReg(reg),
+ else => null,
+ };
+ defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock);
- const src_mcv = blk: {
+ const src_mcv: MCValue = blk: {
const mcv = if (flipped) lhs else rhs;
if (mcv.isRegister() or mcv.isMemory()) break :blk mcv;
break :blk MCValue{ .register = try self.copyToTmpRegister(dst_ty, mcv) };
};
- src_mcv.freezeIfRegister(&self.register_manager);
- defer src_mcv.unfreezeIfRegister(&self.register_manager);
+ const src_mcv_lock: ?RegisterLock = switch (src_mcv) {
+ .register => |reg| self.register_manager.lockReg(reg),
+ else => null,
+ };
+ defer if (src_mcv_lock) |lock| self.register_manager.unlockReg(lock);
const tag = self.air.instructions.items(.tag)[inst];
switch (tag) {
@@ -3287,8 +3435,9 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC
.register_overflow_unsigned => unreachable,
.register_overflow_signed => unreachable,
.ptr_stack_offset => {
- self.register_manager.freezeRegs(&.{dst_reg});
- defer self.register_manager.unfreezeRegs(&.{dst_reg});
+ const dst_reg_lock = self.register_manager.lockReg(dst_reg);
+ defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock);
+
const reg = try self.copyToTmpRegister(dst_ty, src_mcv);
return self.genBinMathOpMir(mir_tag, dst_ty, dst_mcv, .{ .register = reg });
},
@@ -3318,8 +3467,9 @@ fn genBinMathOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MC
.compare_flags_unsigned,
=> {
assert(abi_size <= 8);
- self.register_manager.freezeRegs(&.{dst_reg});
- defer self.register_manager.unfreezeRegs(&.{dst_reg});
+ const dst_reg_lock = self.register_manager.lockReg(dst_reg);
+ defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock);
+
const reg = try self.copyToTmpRegister(dst_ty, src_mcv);
return self.genBinMathOpMir(mir_tag, dst_ty, dst_mcv, .{ .register = reg });
},
@@ -3659,20 +3809,25 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
try self.register_manager.getReg(reg, null);
}
- if (info.return_value == .stack_offset) {
- const ret_ty = fn_ty.fnReturnType();
- const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*));
- const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*));
- const stack_offset = @intCast(i32, try self.allocMem(inst, ret_abi_size, ret_abi_align));
- log.debug("airCall: return value on stack at offset {}", .{stack_offset});
+ const rdi_lock: ?RegisterLock = blk: {
+ if (info.return_value == .stack_offset) {
+ const ret_ty = fn_ty.fnReturnType();
+ const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*));
+ const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*));
+ const stack_offset = @intCast(i32, try self.allocMem(inst, ret_abi_size, ret_abi_align));
+ log.debug("airCall: return value on stack at offset {}", .{stack_offset});
- try self.register_manager.getReg(.rdi, null);
- try self.genSetReg(Type.usize, .rdi, .{ .ptr_stack_offset = stack_offset });
- self.register_manager.freezeRegs(&.{.rdi});
+ try self.register_manager.getReg(.rdi, null);
+ try self.genSetReg(Type.usize, .rdi, .{ .ptr_stack_offset = stack_offset });
+ const rdi_lock = self.register_manager.lockRegAssumeUnused(.rdi);
- info.return_value.stack_offset = stack_offset;
- }
- defer if (info.return_value == .stack_offset) self.register_manager.unfreezeRegs(&.{.rdi});
+ info.return_value.stack_offset = stack_offset;
+
+ break :blk rdi_lock;
+ }
+ break :blk null;
+ };
+ defer if (rdi_lock) |lock| self.register_manager.unlockReg(lock);
for (args) |arg, arg_i| {
const mc_arg = info.args[arg_i];
@@ -3891,11 +4046,10 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void {
const ret_ty = self.fn_type.fnReturnType();
switch (self.ret_mcv) {
.stack_offset => {
- self.register_manager.freezeRegs(&.{ .rax, .rcx });
- defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx });
const reg = try self.copyToTmpRegister(Type.usize, self.ret_mcv);
- self.register_manager.freezeRegs(&.{reg});
- defer self.register_manager.unfreezeRegs(&.{reg});
+ const reg_lock = self.register_manager.lockRegAssumeUnused(reg);
+ defer self.register_manager.unlockReg(reg_lock);
+
try self.genSetStack(ret_ty, 0, operand, .{
.source_stack_base = .rbp,
.dest_stack_base = reg,
@@ -3926,11 +4080,10 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
const elem_ty = ptr_ty.elemType();
switch (self.ret_mcv) {
.stack_offset => {
- self.register_manager.freezeRegs(&.{ .rax, .rcx });
- defer self.register_manager.unfreezeRegs(&.{ .rax, .rcx });
const reg = try self.copyToTmpRegister(Type.usize, self.ret_mcv);
- self.register_manager.freezeRegs(&.{reg});
- defer self.register_manager.unfreezeRegs(&.{reg});
+ const reg_lock = self.register_manager.lockRegAssumeUnused(reg);
+ defer self.register_manager.unlockReg(reg_lock);
+
try self.genInlineMemcpy(.{ .stack_offset = 0 }, ptr, .{ .immediate = elem_ty.abiSize(self.target.*) }, .{
.source_stack_base = .rbp,
.dest_stack_base = reg,
@@ -3980,12 +4133,15 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
// Source operand can be an immediate, 8 bits or 32 bits.
// TODO look into reusing the operand
const lhs = try self.resolveInst(bin_op.lhs);
- lhs.freezeIfRegister(&self.register_manager);
- defer lhs.unfreezeIfRegister(&self.register_manager);
+ const lhs_lock: ?RegisterLock = switch (lhs) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock);
const dst_reg = try self.copyToTmpRegister(ty, lhs);
- self.register_manager.freezeRegs(&.{dst_reg});
- defer self.register_manager.unfreezeRegs(&.{dst_reg});
+ const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
+ defer self.register_manager.unlockReg(dst_reg_lock);
const dst_mcv = MCValue{ .register = dst_reg };
@@ -4446,22 +4602,31 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
const un_op = self.air.instructions.items(.data)[inst].un_op;
- const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
- const operand_ptr = try self.resolveInst(un_op);
- operand_ptr.freezeIfRegister(&self.register_manager);
- defer operand_ptr.unfreezeIfRegister(&self.register_manager);
- const operand: MCValue = blk: {
- if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
- // The MCValue that holds the pointer can be re-used as the value.
- break :blk operand_ptr;
- } else {
- break :blk try self.allocRegOrMem(inst, true);
- }
- };
- const ptr_ty = self.air.typeOf(un_op);
- try self.load(operand, operand_ptr, ptr_ty);
- break :result try self.isNull(inst, ptr_ty.elemType(), operand);
+
+ if (self.liveness.isUnused(inst)) {
+ return self.finishAir(inst, .dead, .{ un_op, .none, .none });
+ }
+
+ const operand_ptr = try self.resolveInst(un_op);
+ const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (operand_ptr_lock) |lock| self.register_manager.unlockReg(lock);
+
+ const operand: MCValue = blk: {
+ if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
+ // The MCValue that holds the pointer can be re-used as the value.
+ break :blk operand_ptr;
+ } else {
+ break :blk try self.allocRegOrMem(inst, true);
+ }
};
+ const ptr_ty = self.air.typeOf(un_op);
+ try self.load(operand, operand_ptr, ptr_ty);
+
+ const result = try self.isNull(inst, ptr_ty.elemType(), operand);
+
return self.finishAir(inst, result, .{ un_op, .none, .none });
}
@@ -4477,22 +4642,31 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
const un_op = self.air.instructions.items(.data)[inst].un_op;
- const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
- const operand_ptr = try self.resolveInst(un_op);
- operand_ptr.freezeIfRegister(&self.register_manager);
- defer operand_ptr.unfreezeIfRegister(&self.register_manager);
- const operand: MCValue = blk: {
- if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
- // The MCValue that holds the pointer can be re-used as the value.
- break :blk operand_ptr;
- } else {
- break :blk try self.allocRegOrMem(inst, true);
- }
- };
- const ptr_ty = self.air.typeOf(un_op);
- try self.load(operand, operand_ptr, ptr_ty);
- break :result try self.isNonNull(inst, ptr_ty.elemType(), operand);
+
+ if (self.liveness.isUnused(inst)) {
+ return self.finishAir(inst, .dead, .{ un_op, .none, .none });
+ }
+
+ const operand_ptr = try self.resolveInst(un_op);
+ const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (operand_ptr_lock) |lock| self.register_manager.unlockReg(lock);
+
+ const operand: MCValue = blk: {
+ if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
+ // The MCValue that holds the pointer can be re-used as the value.
+ break :blk operand_ptr;
+ } else {
+ break :blk try self.allocRegOrMem(inst, true);
+ }
};
+ const ptr_ty = self.air.typeOf(un_op);
+ try self.load(operand, operand_ptr, ptr_ty);
+
+ const result = try self.isNonNull(inst, ptr_ty.elemType(), operand);
+
return self.finishAir(inst, result, .{ un_op, .none, .none });
}
@@ -4508,22 +4682,31 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
const un_op = self.air.instructions.items(.data)[inst].un_op;
- const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
- const operand_ptr = try self.resolveInst(un_op);
- operand_ptr.freezeIfRegister(&self.register_manager);
- defer operand_ptr.unfreezeIfRegister(&self.register_manager);
- const operand: MCValue = blk: {
- if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
- // The MCValue that holds the pointer can be re-used as the value.
- break :blk operand_ptr;
- } else {
- break :blk try self.allocRegOrMem(inst, true);
- }
- };
- const ptr_ty = self.air.typeOf(un_op);
- try self.load(operand, operand_ptr, ptr_ty);
- break :result try self.isErr(inst, ptr_ty.elemType(), operand);
+
+ if (self.liveness.isUnused(inst)) {
+ return self.finishAir(inst, .dead, .{ un_op, .none, .none });
+ }
+
+ const operand_ptr = try self.resolveInst(un_op);
+ const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
};
+ defer if (operand_ptr_lock) |lock| self.register_manager.unlockReg(lock);
+
+ const operand: MCValue = blk: {
+ if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
+ // The MCValue that holds the pointer can be re-used as the value.
+ break :blk operand_ptr;
+ } else {
+ break :blk try self.allocRegOrMem(inst, true);
+ }
+ };
+ const ptr_ty = self.air.typeOf(un_op);
+ try self.load(operand, operand_ptr, ptr_ty);
+
+ const result = try self.isErr(inst, ptr_ty.elemType(), operand);
+
return self.finishAir(inst, result, .{ un_op, .none, .none });
}
@@ -4539,22 +4722,31 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
const un_op = self.air.instructions.items(.data)[inst].un_op;
- const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
- const operand_ptr = try self.resolveInst(un_op);
- operand_ptr.freezeIfRegister(&self.register_manager);
- defer operand_ptr.unfreezeIfRegister(&self.register_manager);
- const operand: MCValue = blk: {
- if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
- // The MCValue that holds the pointer can be re-used as the value.
- break :blk operand_ptr;
- } else {
- break :blk try self.allocRegOrMem(inst, true);
- }
- };
- const ptr_ty = self.air.typeOf(un_op);
- try self.load(operand, operand_ptr, ptr_ty);
- break :result try self.isNonErr(inst, ptr_ty.elemType(), operand);
+
+ if (self.liveness.isUnused(inst)) {
+ return self.finishAir(inst, .dead, .{ un_op, .none, .none });
+ }
+
+ const operand_ptr = try self.resolveInst(un_op);
+ const operand_ptr_lock: ?RegisterLock = switch (operand_ptr) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (operand_ptr_lock) |lock| self.register_manager.unlockReg(lock);
+
+ const operand: MCValue = blk: {
+ if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
+ // The MCValue that holds the pointer can be re-used as the value.
+ break :blk operand_ptr;
+ } else {
+ break :blk try self.allocRegOrMem(inst, true);
+ }
};
+ const ptr_ty = self.air.typeOf(un_op);
+ try self.load(operand, operand_ptr, ptr_ty);
+
+ const result = try self.isNonErr(inst, ptr_ty.elemType(), operand);
+
return self.finishAir(inst, result, .{ un_op, .none, .none });
}
@@ -4610,8 +4802,8 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u
.register => |cond_reg| {
try self.spillCompareFlagsIfOccupied();
- self.register_manager.freezeRegs(&.{cond_reg});
- defer self.register_manager.unfreezeRegs(&.{cond_reg});
+ const cond_reg_lock = self.register_manager.lockReg(cond_reg);
+ defer if (cond_reg_lock) |lock| self.register_manager.unlockReg(lock);
switch (case) {
.none => unreachable,
@@ -4670,8 +4862,8 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u
if (abi_size <= 8) {
const reg = try self.copyToTmpRegister(ty, condition);
- self.register_manager.freezeRegs(&.{reg});
- defer self.register_manager.unfreezeRegs(&.{reg});
+ const reg_lock = self.register_manager.lockRegAssumeUnused(reg);
+ defer self.register_manager.unlockReg(reg_lock);
return self.genCondSwitchMir(ty, .{ .register = reg }, case);
}
@@ -5158,8 +5350,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
.register_overflow_unsigned,
.register_overflow_signed,
=> |reg| {
- self.register_manager.freezeRegs(&.{reg});
- defer self.register_manager.unfreezeRegs(&.{reg});
+ const reg_lock = self.register_manager.lockReg(reg);
+ defer if (reg_lock) |lock| self.register_manager.unlockReg(lock);
const wrapped_ty = ty.structFieldType(0);
try self.genSetStack(wrapped_ty, stack_offset, .{ .register = reg }, .{});
@@ -5260,8 +5452,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
const base_reg = opts.dest_stack_base orelse .rbp;
if (!math.isPowerOfTwo(abi_size)) {
- self.register_manager.freezeRegs(&.{reg});
- defer self.register_manager.unfreezeRegs(&.{reg});
+ const reg_lock = self.register_manager.lockReg(reg);
+ defer if (reg_lock) |lock| self.register_manager.unlockReg(lock);
const tmp_reg = try self.copyToTmpRegister(ty, mcv);
@@ -5350,13 +5542,25 @@ fn genInlineMemcpy(
len: MCValue,
opts: InlineMemcpyOpts,
) InnerError!void {
- self.register_manager.freezeRegs(&.{ .rax, .rcx });
+ try self.register_manager.getReg(.rax, null);
+ try self.register_manager.getReg(.rcx, null);
- if (opts.source_stack_base) |reg| self.register_manager.freezeRegs(&.{reg});
- defer if (opts.source_stack_base) |reg| self.register_manager.unfreezeRegs(&.{reg});
+ const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rcx });
+ defer for (reg_locks) |lock| {
+ self.register_manager.unlockReg(lock);
+ };
+
+ const ssbase_lock: ?RegisterLock = if (opts.source_stack_base) |reg|
+ self.register_manager.lockReg(reg)
+ else
+ null;
+ defer if (ssbase_lock) |reg| self.register_manager.unlockReg(reg);
- if (opts.dest_stack_base) |reg| self.register_manager.freezeRegs(&.{reg});
- defer if (opts.dest_stack_base) |reg| self.register_manager.unfreezeRegs(&.{reg});
+ const dsbase_lock: ?RegisterLock = if (opts.dest_stack_base) |reg|
+ self.register_manager.lockReg(reg)
+ else
+ null;
+ defer if (dsbase_lock) |lock| self.register_manager.unlockReg(lock);
const dst_addr_reg = try self.register_manager.allocReg(null);
switch (dst_ptr) {
@@ -5390,8 +5594,8 @@ fn genInlineMemcpy(
return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr});
},
}
- self.register_manager.freezeRegs(&.{dst_addr_reg});
- defer self.register_manager.unfreezeRegs(&.{dst_addr_reg});
+ const dst_addr_reg_lock = self.register_manager.lockRegAssumeUnused(dst_addr_reg);
+ defer self.register_manager.unlockReg(dst_addr_reg_lock);
const src_addr_reg = try self.register_manager.allocReg(null);
switch (src_ptr) {
@@ -5425,18 +5629,13 @@ fn genInlineMemcpy(
return self.fail("TODO implement memcpy for setting stack when src is {}", .{src_ptr});
},
}
- self.register_manager.freezeRegs(&.{src_addr_reg});
- defer self.register_manager.unfreezeRegs(&.{src_addr_reg});
+ const src_addr_reg_lock = self.register_manager.lockRegAssumeUnused(src_addr_reg);
+ defer self.register_manager.unlockReg(src_addr_reg_lock);
const regs = try self.register_manager.allocRegs(2, .{ null, null });
const count_reg = regs[0].to64();
const tmp_reg = regs[1].to8();
- self.register_manager.unfreezeRegs(&.{ .rax, .rcx });
-
- try self.register_manager.getReg(.rax, null);
- try self.register_manager.getReg(.rcx, null);
-
try self.genSetReg(Type.usize, count_reg, len);
// mov rcx, 0
@@ -5540,7 +5739,9 @@ fn genInlineMemset(
len: MCValue,
opts: InlineMemcpyOpts,
) InnerError!void {
- self.register_manager.freezeRegs(&.{.rax});
+ try self.register_manager.getReg(.rax, null);
+ const rax_lock = self.register_manager.lockRegAssumeUnused(.rax);
+ defer self.register_manager.unlockReg(rax_lock);
const addr_reg = try self.register_manager.allocReg(null);
switch (dst_ptr) {
@@ -5574,11 +5775,8 @@ fn genInlineMemset(
return self.fail("TODO implement memcpy for setting stack when dest is {}", .{dst_ptr});
},
}
- self.register_manager.freezeRegs(&.{addr_reg});
- defer self.register_manager.unfreezeRegs(&.{addr_reg});
-
- self.register_manager.unfreezeRegs(&.{.rax});
- try self.register_manager.getReg(.rax, null);
+ const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
+ defer self.register_manager.unlockReg(addr_reg_lock);
try self.genSetReg(Type.usize, .rax, len);
try self.genBinMathOpMir(.sub, Type.usize, .{ .register = .rax }, .{ .immediate = 1 });
@@ -6017,16 +6215,25 @@ fn airMemset(self: *Self, inst: Air.Inst.Index) !void {
const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
const dst_ptr = try self.resolveInst(pl_op.operand);
- dst_ptr.freezeIfRegister(&self.register_manager);
- defer dst_ptr.unfreezeIfRegister(&self.register_manager);
+ const dst_ptr_lock: ?RegisterLock = switch (dst_ptr) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (dst_ptr_lock) |lock| self.register_manager.unlockReg(lock);
const src_val = try self.resolveInst(extra.lhs);
- src_val.freezeIfRegister(&self.register_manager);
- defer src_val.unfreezeIfRegister(&self.register_manager);
+ const src_val_lock: ?RegisterLock = switch (src_val) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (src_val_lock) |lock| self.register_manager.unlockReg(lock);
const len = try self.resolveInst(extra.rhs);
- len.freezeIfRegister(&self.register_manager);
- defer len.unfreezeIfRegister(&self.register_manager);
+ const len_lock: ?RegisterLock = switch (len) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (len_lock) |lock| self.register_manager.unlockReg(lock);
try self.genInlineMemset(dst_ptr, src_val, len, .{});
@@ -6038,17 +6245,26 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
const dst_ptr = try self.resolveInst(pl_op.operand);
- dst_ptr.freezeIfRegister(&self.register_manager);
- defer dst_ptr.unfreezeIfRegister(&self.register_manager);
+ const dst_ptr_lock: ?RegisterLock = switch (dst_ptr) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (dst_ptr_lock) |lock| self.register_manager.unlockReg(lock);
const src_ty = self.air.typeOf(extra.lhs);
const src_ptr = try self.resolveInst(extra.lhs);
- src_ptr.freezeIfRegister(&self.register_manager);
- defer src_ptr.unfreezeIfRegister(&self.register_manager);
+ const src_ptr_lock: ?RegisterLock = switch (src_ptr) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (src_ptr_lock) |lock| self.register_manager.unlockReg(lock);
const len = try self.resolveInst(extra.rhs);
- len.freezeIfRegister(&self.register_manager);
- defer len.unfreezeIfRegister(&self.register_manager);
+ const len_lock: ?RegisterLock = switch (len) {
+ .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
+ else => null,
+ };
+ defer if (len_lock) |lock| self.register_manager.unlockReg(lock);
// TODO Is this the only condition for pointer dereference for memcpy?
const src: MCValue = blk: {
@@ -6070,8 +6286,11 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
else => break :blk src_ptr,
}
};
- src.freezeIfRegister(&self.register_manager);
- defer src.unfreezeIfRegister(&self.register_manager);
+ const src_lock: ?RegisterLock = switch (src) {
+ .register => |reg| self.register_manager.lockReg(reg),
+ else => null,
+ };
+ defer if (src_lock) |lock| self.register_manager.unlockReg(lock);
try self.genInlineMemcpy(dst_ptr, src, len, .{});
diff --git a/src/register_manager.zig b/src/register_manager.zig
@@ -45,9 +45,8 @@ pub fn RegisterManager(
/// Tracks all registers allocated in the course of this
/// function
allocated_registers: FreeRegInt = 0,
- /// Tracks registers which are temporarily blocked from being
- /// allocated
- frozen_registers: FreeRegInt = 0,
+ /// Tracks registers which are locked from being allocated
+ locked_registers: FreeRegInt = 0,
const Self = @This();
@@ -108,34 +107,70 @@ pub fn RegisterManager(
return self.allocated_registers & mask != 0;
}
- /// Returns whether this register is frozen
+ /// Returns whether this register is locked
///
/// Returns false when this register is not tracked
- pub fn isRegFrozen(self: Self, reg: Register) bool {
+ pub fn isRegLocked(self: Self, reg: Register) bool {
const mask = getRegisterMask(reg) orelse return false;
- return self.frozen_registers & mask != 0;
+ return self.locked_registers & mask != 0;
}
- /// Prevents the registers from being allocated until they are
- /// unfrozen again
- pub fn freezeRegs(self: *Self, regs: []const Register) void {
- for (regs) |reg| {
- const mask = getRegisterMask(reg) orelse continue;
- self.frozen_registers |= mask;
+ pub const RegisterLock = struct {
+ register: Register,
+ };
+
+ /// Prevents the register from being allocated until they are
+ /// unlocked again.
+ /// Returns `RegisterLock` if the register was not already
+ /// locked, or `null` otherwise.
+ /// Only the owner of the `RegisterLock` can unlock the
+ /// register later.
+ pub fn lockReg(self: *Self, reg: Register) ?RegisterLock {
+ log.debug("locking {}", .{reg});
+ if (self.isRegLocked(reg)) {
+ log.debug(" register already locked", .{});
+ return null;
}
+ const mask = getRegisterMask(reg) orelse return null;
+ self.locked_registers |= mask;
+ return RegisterLock{ .register = reg };
}
- /// Enables the allocation of the registers
- pub fn unfreezeRegs(self: *Self, regs: []const Register) void {
- for (regs) |reg| {
- const mask = getRegisterMask(reg) orelse continue;
- self.frozen_registers &= ~mask;
+ /// Like `lockReg` but asserts the register was unused always
+ /// returning a valid lock.
+ pub fn lockRegAssumeUnused(self: *Self, reg: Register) RegisterLock {
+ log.debug("locking asserting free {}", .{reg});
+ assert(!self.isRegLocked(reg));
+ const mask = getRegisterMask(reg) orelse unreachable;
+ self.locked_registers |= mask;
+ return RegisterLock{ .register = reg };
+ }
+
+ /// Like `lockRegAssumeUnused` but locks multiple registers.
+ pub fn lockRegsAssumeUnused(
+ self: *Self,
+ comptime count: comptime_int,
+ regs: [count]Register,
+ ) [count]RegisterLock {
+ var buf: [count]RegisterLock = undefined;
+ for (regs) |reg, i| {
+ buf[i] = self.lockRegAssumeUnused(reg);
}
+ return buf;
+ }
+
+ /// Unlocks the register allowing its re-allocation and re-use.
+ /// Requires `RegisterLock` to unlock a register.
+ /// Call `lockReg` to obtain the lock first.
+ pub fn unlockReg(self: *Self, lock: RegisterLock) void {
+ log.debug("unlocking {}", .{lock.register});
+ const mask = getRegisterMask(lock.register) orelse return;
+ self.locked_registers &= ~mask;
}
- /// Returns true when at least one register is frozen
- pub fn frozenRegsExist(self: Self) bool {
- return self.frozen_registers != 0;
+ /// Returns true when at least one register is locked
+ pub fn lockedRegsExist(self: Self) bool {
+ return self.locked_registers != 0;
}
/// Allocates a specified number of registers, optionally
@@ -148,15 +183,15 @@ pub fn RegisterManager(
) ?[count]Register {
comptime assert(count > 0 and count <= tracked_registers.len);
- const free_and_not_frozen_registers = self.free_registers & ~self.frozen_registers;
- const free_and_not_frozen_registers_count = @popCount(FreeRegInt, free_and_not_frozen_registers);
- if (free_and_not_frozen_registers_count < count) return null;
+ const free_and_not_locked_registers = self.free_registers & ~self.locked_registers;
+ const free_and_not_locked_registers_count = @popCount(FreeRegInt, free_and_not_locked_registers);
+ if (free_and_not_locked_registers_count < count) return null;
var regs: [count]Register = undefined;
var i: usize = 0;
for (tracked_registers) |reg| {
if (i >= count) break;
- if (self.isRegFrozen(reg)) continue;
+ if (self.isRegLocked(reg)) continue;
if (!self.isRegFree(reg)) continue;
regs[i] = reg;
@@ -194,8 +229,8 @@ pub fn RegisterManager(
insts: [count]?Air.Inst.Index,
) AllocateRegistersError![count]Register {
comptime assert(count > 0 and count <= tracked_registers.len);
- const frozen_registers_count = @popCount(FreeRegInt, self.frozen_registers);
- if (count > tracked_registers.len - frozen_registers_count) return error.OutOfRegisters;
+ const locked_registers_count = @popCount(FreeRegInt, self.locked_registers);
+ if (count > tracked_registers.len - locked_registers_count) return error.OutOfRegisters;
const result = self.tryAllocRegs(count, insts) orelse blk: {
// We'll take over the first count registers. Spill
@@ -205,7 +240,7 @@ pub fn RegisterManager(
var i: usize = 0;
for (tracked_registers) |reg| {
if (i >= count) break;
- if (self.isRegFrozen(reg)) continue;
+ if (self.isRegLocked(reg)) continue;
regs[i] = reg;
self.markRegAllocated(reg);
@@ -416,15 +451,15 @@ test "allocReg: spilling" {
try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction));
try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items);
- // Frozen registers
+ // Locked registers
function.register_manager.freeReg(.r3);
{
- function.register_manager.freezeRegs(&.{.r2});
- defer function.register_manager.unfreezeRegs(&.{.r2});
+ const lock = function.register_manager.lockReg(.r2);
+ defer if (lock) |reg| function.register_manager.unlockReg(reg);
try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction));
}
- try expect(!function.register_manager.frozenRegsExist());
+ try expect(!function.register_manager.lockedRegsExist());
}
test "tryAllocRegs" {
@@ -442,17 +477,17 @@ test "tryAllocRegs" {
try expect(function.register_manager.isRegAllocated(.r2));
try expect(!function.register_manager.isRegAllocated(.r3));
- // Frozen registers
+ // Locked registers
function.register_manager.freeReg(.r0);
function.register_manager.freeReg(.r2);
function.register_manager.freeReg(.r3);
{
- function.register_manager.freezeRegs(&.{.r1});
- defer function.register_manager.unfreezeRegs(&.{.r1});
+ const lock = function.register_manager.lockReg(.r1);
+ defer if (lock) |reg| function.register_manager.unlockReg(reg);
try expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }).?);
}
- try expect(!function.register_manager.frozenRegsExist());
+ try expect(!function.register_manager.lockedRegsExist());
try expect(function.register_manager.isRegAllocated(.r0));
try expect(function.register_manager.isRegAllocated(.r1));
@@ -475,19 +510,19 @@ test "allocRegs: normal usage" {
// The result register is known and fixed at this point, we
// don't want to accidentally allocate lhs or rhs to the
- // result register, this is why we freeze it.
+ // result register, this is why we lock it.
//
- // Using defer unfreeze right after freeze is a good idea in
- // most cases as you probably are using the frozen registers
+ // Using defer unlock right after lock is a good idea in
+ // most cases as you probably are using the locked registers
// in the remainder of this scope and don't need to use it
// after the end of this scope. However, in some situations,
- // it may make sense to manually unfreeze registers before the
+ // it may make sense to manually unlock registers before the
// end of the scope when you are certain that they don't
// contain any valuable data anymore and can be reused. For an
// example of that, see `selectively reducing register
// pressure`.
- function.register_manager.freezeRegs(&.{result_reg});
- defer function.register_manager.unfreezeRegs(&.{result_reg});
+ const lock = function.register_manager.lockReg(result_reg);
+ defer if (lock) |reg| function.register_manager.unlockReg(reg);
const regs = try function.register_manager.allocRegs(2, .{ null, null });
try function.genAdd(result_reg, regs[0], regs[1]);
@@ -507,16 +542,14 @@ test "allocRegs: selectively reducing register pressure" {
{
const result_reg: MockRegister2 = .r1;
- function.register_manager.freezeRegs(&.{result_reg});
- defer function.register_manager.unfreezeRegs(&.{result_reg});
+ const lock = function.register_manager.lockReg(result_reg);
- // Here, we don't defer unfreeze because we manually unfreeze
+ // Here, we don't defer unlock because we manually unlock
// after genAdd
const regs = try function.register_manager.allocRegs(2, .{ null, null });
- function.register_manager.freezeRegs(&.{result_reg});
try function.genAdd(result_reg, regs[0], regs[1]);
- function.register_manager.unfreezeRegs(®s);
+ function.register_manager.unlockReg(lock.?);
const extra_summand_reg = try function.register_manager.allocReg(null);
try function.genAdd(result_reg, result_reg, extra_summand_reg);
diff --git a/test/behavior/align.zig b/test/behavior/align.zig
@@ -7,6 +7,8 @@ var foo: u8 align(4) = 100;
test "global variable alignment" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
comptime try expect(@typeInfo(@TypeOf(&foo)).Pointer.alignment == 4);
comptime try expect(@TypeOf(&foo) == *align(4) u8);
diff --git a/test/behavior/byval_arg_var.zig b/test/behavior/byval_arg_var.zig
@@ -6,6 +6,7 @@ var result: []const u8 = "wrong";
test "pass string literal byvalue to a generic var param" {
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
start();
blowUpStack(10);