mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 12:27:41 +00:00
Merge pull request #11681 from ziglang/x64-floats-tmp
x64: add prelim support for SSE/AVX-based floating-point ops
This commit is contained in:
commit
704d38ba49
@ -21,9 +21,6 @@ const DW = std.dwarf;
|
||||
const leb128 = std.leb;
|
||||
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;
|
||||
@ -31,11 +28,14 @@ const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
|
||||
|
||||
const bits = @import("bits.zig");
|
||||
const abi = @import("abi.zig");
|
||||
const RegisterManager = abi.RegisterManager;
|
||||
const RegisterLock = RegisterManager.RegisterLock;
|
||||
const Register = bits.Register;
|
||||
const Instruction = bits.Instruction;
|
||||
const callee_preserved_regs = abi.callee_preserved_regs;
|
||||
const c_abi_int_param_regs = abi.c_abi_int_param_regs;
|
||||
const c_abi_int_return_regs = abi.c_abi_int_return_regs;
|
||||
const gp = abi.RegisterClass.gp;
|
||||
|
||||
const InnerError = error{
|
||||
OutOfMemory,
|
||||
@ -888,7 +888,7 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
|
||||
if (reg_ok) {
|
||||
// Make sure the type can fit in a register before we try to allocate one.
|
||||
if (abi_size <= 8) {
|
||||
if (self.register_manager.tryAllocReg(inst)) |reg| {
|
||||
if (self.register_manager.tryAllocReg(inst, gp)) |reg| {
|
||||
return MCValue{ .register = registerAlias(reg, abi_size) };
|
||||
}
|
||||
}
|
||||
@ -951,7 +951,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void {
|
||||
/// allocated. A second call to `copyToTmpRegister` may return the same register.
|
||||
/// This can have a side effect of spilling instructions to the stack to free up a register.
|
||||
fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
|
||||
const raw_reg = try self.register_manager.allocReg(null);
|
||||
const raw_reg = try self.register_manager.allocReg(null, gp);
|
||||
const reg = registerAlias(raw_reg, ty.abiSize(self.target.*));
|
||||
try self.genSetReg(ty, reg, mcv);
|
||||
return reg;
|
||||
@ -961,7 +961,7 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
|
||||
/// `reg_owner` is the instruction that gets associated with the register in the register table.
|
||||
/// This can have a side effect of spilling instructions to the stack to free up a register.
|
||||
fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue {
|
||||
const raw_reg = try self.register_manager.allocReg(reg_owner);
|
||||
const raw_reg = try self.register_manager.allocReg(reg_owner, gp);
|
||||
const ty = self.air.typeOfIndex(reg_owner);
|
||||
const reg = registerAlias(raw_reg, ty.abiSize(self.target.*));
|
||||
try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv);
|
||||
@ -1074,11 +1074,11 @@ fn trunc(
|
||||
if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
|
||||
break :blk registerAlias(operand_reg, dest_ty.abiSize(self.target.*));
|
||||
} else {
|
||||
const raw_reg = try self.register_manager.allocReg(inst);
|
||||
const raw_reg = try self.register_manager.allocReg(inst, gp);
|
||||
break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*));
|
||||
}
|
||||
} else blk: {
|
||||
const raw_reg = try self.register_manager.allocReg(null);
|
||||
const raw_reg = try self.register_manager.allocReg(null, gp);
|
||||
break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*));
|
||||
};
|
||||
|
||||
@ -1160,7 +1160,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
|
||||
break :blk op_reg;
|
||||
}
|
||||
|
||||
const raw_reg = try self.register_manager.allocReg(null);
|
||||
const raw_reg = try self.register_manager.allocReg(null, gp);
|
||||
break :blk raw_reg.to32();
|
||||
};
|
||||
|
||||
@ -1193,7 +1193,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
|
||||
break :blk op_reg;
|
||||
}
|
||||
|
||||
const raw_reg = try self.register_manager.allocReg(null);
|
||||
const raw_reg = try self.register_manager.allocReg(null, gp);
|
||||
break :blk registerAlias(raw_reg, operand_ty.abiSize(self.target.*));
|
||||
};
|
||||
|
||||
@ -1293,7 +1293,7 @@ fn binOpRegister(
|
||||
break :inst Air.refToIndex(md.lhs).?;
|
||||
} else null;
|
||||
|
||||
const raw_reg = try self.register_manager.allocReg(track_inst);
|
||||
const raw_reg = try self.register_manager.allocReg(track_inst, gp);
|
||||
const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
|
||||
|
||||
if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
|
||||
@ -1308,7 +1308,7 @@ fn binOpRegister(
|
||||
break :inst Air.refToIndex(md.rhs).?;
|
||||
} else null;
|
||||
|
||||
const raw_reg = try self.register_manager.allocReg(track_inst);
|
||||
const raw_reg = try self.register_manager.allocReg(track_inst, gp);
|
||||
const reg = registerAlias(raw_reg, rhs_ty.abiAlignment(self.target.*));
|
||||
|
||||
if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
|
||||
@ -1326,11 +1326,11 @@ fn binOpRegister(
|
||||
} else if (rhs_is_register and self.reuseOperand(md.inst, md.rhs, 1, rhs)) {
|
||||
break :blk rhs_reg;
|
||||
} else {
|
||||
const raw_reg = try self.register_manager.allocReg(md.inst);
|
||||
const raw_reg = try self.register_manager.allocReg(md.inst, gp);
|
||||
break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
|
||||
}
|
||||
} else blk: {
|
||||
const raw_reg = try self.register_manager.allocReg(null);
|
||||
const raw_reg = try self.register_manager.allocReg(null, gp);
|
||||
break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
|
||||
},
|
||||
};
|
||||
@ -1431,7 +1431,7 @@ fn binOpImmediate(
|
||||
).?;
|
||||
} else null;
|
||||
|
||||
const raw_reg = try self.register_manager.allocReg(track_inst);
|
||||
const raw_reg = try self.register_manager.allocReg(track_inst, gp);
|
||||
const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
|
||||
|
||||
if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
|
||||
@ -1452,11 +1452,11 @@ fn binOpImmediate(
|
||||
)) {
|
||||
break :blk lhs_reg;
|
||||
} else {
|
||||
const raw_reg = try self.register_manager.allocReg(md.inst);
|
||||
const raw_reg = try self.register_manager.allocReg(md.inst, gp);
|
||||
break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
|
||||
}
|
||||
} else blk: {
|
||||
const raw_reg = try self.register_manager.allocReg(null);
|
||||
const raw_reg = try self.register_manager.allocReg(null, gp);
|
||||
break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
|
||||
},
|
||||
};
|
||||
@ -1872,7 +1872,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
|
||||
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 raw_truncated_reg = try self.register_manager.allocReg(null, gp);
|
||||
const truncated_reg = registerAlias(raw_truncated_reg, lhs_ty.abiSize(self.target.*));
|
||||
const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg);
|
||||
defer self.register_manager.unlockReg(truncated_reg_lock);
|
||||
@ -1983,7 +1983,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
|
||||
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);
|
||||
const truncated_reg = try self.register_manager.allocReg(null, gp);
|
||||
const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg);
|
||||
defer self.register_manager.unlockReg(truncated_reg_lock);
|
||||
|
||||
@ -2048,7 +2048,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
|
||||
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 raw_reg = try self.register_manager.allocReg(null, gp);
|
||||
const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
|
||||
break :blk reg;
|
||||
};
|
||||
@ -2056,7 +2056,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
|
||||
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 raw_reg = try self.register_manager.allocReg(null, gp);
|
||||
const reg = registerAlias(raw_reg, rhs_ty.abiAlignment(self.target.*));
|
||||
break :blk reg;
|
||||
};
|
||||
@ -2067,7 +2067,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
|
||||
if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);
|
||||
|
||||
const dest_reg = blk: {
|
||||
const raw_reg = try self.register_manager.allocReg(null);
|
||||
const raw_reg = try self.register_manager.allocReg(null, gp);
|
||||
const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*));
|
||||
break :blk reg;
|
||||
};
|
||||
@ -2086,7 +2086,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
|
||||
} },
|
||||
});
|
||||
|
||||
const dest_high_reg = try self.register_manager.allocReg(null);
|
||||
const dest_high_reg = try self.register_manager.allocReg(null, gp);
|
||||
const dest_high_reg_lock = self.register_manager.lockRegAssumeUnused(dest_high_reg);
|
||||
defer self.register_manager.unlockReg(dest_high_reg_lock);
|
||||
|
||||
@ -2136,7 +2136,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
|
||||
}
|
||||
},
|
||||
.unsigned => {
|
||||
const dest_high_reg = try self.register_manager.allocReg(null);
|
||||
const dest_high_reg = try self.register_manager.allocReg(null, gp);
|
||||
const dest_high_reg_lock = self.register_manager.lockRegAssumeUnused(dest_high_reg);
|
||||
defer self.register_manager.unlockReg(dest_high_reg_lock);
|
||||
|
||||
@ -2192,7 +2192,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
|
||||
},
|
||||
}
|
||||
|
||||
const truncated_reg = try self.register_manager.allocReg(null);
|
||||
const truncated_reg = try self.register_manager.allocReg(null, gp);
|
||||
const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg);
|
||||
defer self.register_manager.unlockReg(truncated_reg_lock);
|
||||
|
||||
@ -2663,7 +2663,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
|
||||
},
|
||||
.stack_offset => |off| {
|
||||
if (elem_size <= 8) {
|
||||
const raw_tmp_reg = try self.register_manager.allocReg(null);
|
||||
const raw_tmp_reg = try self.register_manager.allocReg(null, gp);
|
||||
const tmp_reg = registerAlias(raw_tmp_reg, elem_size);
|
||||
const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
|
||||
defer self.register_manager.unlockReg(tmp_reg_lock);
|
||||
@ -2672,7 +2672,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
|
||||
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 });
|
||||
const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, gp);
|
||||
const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs);
|
||||
defer for (regs_locks) |reg| {
|
||||
self.register_manager.unlockReg(reg);
|
||||
@ -2887,7 +2887,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
|
||||
},
|
||||
else => {
|
||||
if (abi_size <= 8) {
|
||||
const raw_tmp_reg = try self.register_manager.allocReg(null);
|
||||
const raw_tmp_reg = try self.register_manager.allocReg(null, gp);
|
||||
const tmp_reg = registerAlias(raw_tmp_reg, abi_size);
|
||||
const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
|
||||
defer self.register_manager.unlockReg(tmp_reg_lock);
|
||||
@ -3002,7 +3002,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
|
||||
// TODO return special MCValue condition flags
|
||||
// get overflow bit: set register to C flag
|
||||
// resp. V flag
|
||||
const raw_dest_reg = try self.register_manager.allocReg(null);
|
||||
const raw_dest_reg = try self.register_manager.allocReg(null, gp);
|
||||
const dest_reg = raw_dest_reg.to32();
|
||||
|
||||
// C flag: cset reg, cs
|
||||
@ -4065,7 +4065,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
|
||||
|
||||
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 raw_cond_reg = try self.register_manager.allocReg(null, gp);
|
||||
const cond_reg = registerAlias(
|
||||
raw_cond_reg,
|
||||
@intCast(u32, overflow_bit_ty.abiSize(self.target.*)),
|
||||
@ -4113,7 +4113,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
|
||||
const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
|
||||
|
||||
// TODO call extern memcpy
|
||||
const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null });
|
||||
const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, gp);
|
||||
const regs_locks = self.register_manager.lockRegsAssumeUnused(5, regs);
|
||||
defer for (regs_locks) |reg| {
|
||||
self.register_manager.unlockReg(reg);
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const bits = @import("bits.zig");
|
||||
const Register = bits.Register;
|
||||
const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
|
||||
|
||||
const callee_preserved_regs_impl = if (builtin.os.tag.isDarwin()) struct {
|
||||
pub const callee_preserved_regs = [_]Register{
|
||||
@ -18,3 +20,21 @@ pub const callee_preserved_regs = callee_preserved_regs_impl.callee_preserved_re
|
||||
|
||||
pub const c_abi_int_param_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 };
|
||||
pub const c_abi_int_return_regs = [_]Register{ .x0, .x1, .x2, .x3, .x4, .x5, .x6, .x7 };
|
||||
|
||||
const allocatable_registers = callee_preserved_regs;
|
||||
pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers);
|
||||
|
||||
// Register classes
|
||||
const RegisterBitSet = RegisterManager.RegisterBitSet;
|
||||
pub const RegisterClass = struct {
|
||||
pub const gp: RegisterBitSet = std.math.maxInt(RegisterBitSet);
|
||||
// TODO uncomment once #11680 is fixed.
|
||||
// pub const gp: RegisterBitSet = blk: {
|
||||
// var set = RegisterBitSet.initEmpty();
|
||||
// set.setRangeValue(.{
|
||||
// .start = 0,
|
||||
// .end = callee_preserved_regs.len,
|
||||
// }, true);
|
||||
// break :blk set;
|
||||
// };
|
||||
};
|
||||
|
||||
@ -21,9 +21,6 @@ const DW = std.dwarf;
|
||||
const leb128 = std.leb;
|
||||
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;
|
||||
@ -31,14 +28,16 @@ const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
|
||||
|
||||
const bits = @import("bits.zig");
|
||||
const abi = @import("abi.zig");
|
||||
const RegisterManager = abi.RegisterManager;
|
||||
const RegisterLock = RegisterManager.RegisterLock;
|
||||
const Register = bits.Register;
|
||||
const Instruction = bits.Instruction;
|
||||
const Condition = bits.Condition;
|
||||
const callee_preserved_regs = abi.callee_preserved_regs;
|
||||
const caller_preserved_regs = abi.caller_preserved_regs;
|
||||
const allocatable_registers = abi.allocatable_registers;
|
||||
const c_abi_int_param_regs = abi.c_abi_int_param_regs;
|
||||
const c_abi_int_return_regs = abi.c_abi_int_return_regs;
|
||||
const gp = abi.RegisterClass.gp;
|
||||
|
||||
const InnerError = error{
|
||||
OutOfMemory,
|
||||
@ -874,7 +873,7 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
|
||||
const ptr_bits = self.target.cpu.arch.ptrBitWidth();
|
||||
const ptr_bytes: u64 = @divExact(ptr_bits, 8);
|
||||
if (abi_size <= ptr_bytes) {
|
||||
if (self.register_manager.tryAllocReg(inst)) |reg| {
|
||||
if (self.register_manager.tryAllocReg(inst, gp)) |reg| {
|
||||
return MCValue{ .register = reg };
|
||||
}
|
||||
}
|
||||
@ -939,7 +938,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void {
|
||||
/// allocated. A second call to `copyToTmpRegister` may return the same register.
|
||||
/// This can have a side effect of spilling instructions to the stack to free up a register.
|
||||
fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
|
||||
const reg = try self.register_manager.allocReg(null);
|
||||
const reg = try self.register_manager.allocReg(null, gp);
|
||||
try self.genSetReg(ty, reg, mcv);
|
||||
return reg;
|
||||
}
|
||||
@ -948,7 +947,7 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
|
||||
/// `reg_owner` is the instruction that gets associated with the register in the register table.
|
||||
/// This can have a side effect of spilling instructions to the stack to free up a register.
|
||||
fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue {
|
||||
const reg = try self.register_manager.allocReg(reg_owner);
|
||||
const reg = try self.register_manager.allocReg(reg_owner, gp);
|
||||
try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv);
|
||||
return MCValue{ .register = reg };
|
||||
}
|
||||
@ -1065,9 +1064,9 @@ fn trunc(
|
||||
if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
|
||||
break :blk operand_reg;
|
||||
} else {
|
||||
break :blk try self.register_manager.allocReg(inst);
|
||||
break :blk try self.register_manager.allocReg(inst, gp);
|
||||
}
|
||||
} else try self.register_manager.allocReg(null);
|
||||
} else try self.register_manager.allocReg(null, gp);
|
||||
|
||||
switch (info_b.bits) {
|
||||
32 => {
|
||||
@ -1153,7 +1152,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
|
||||
break :blk op_reg;
|
||||
}
|
||||
|
||||
break :blk try self.register_manager.allocReg(null);
|
||||
break :blk try self.register_manager.allocReg(null, gp);
|
||||
};
|
||||
|
||||
_ = try self.addInst(.{
|
||||
@ -1183,7 +1182,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
|
||||
break :blk op_reg;
|
||||
}
|
||||
|
||||
break :blk try self.register_manager.allocReg(null);
|
||||
break :blk try self.register_manager.allocReg(null, gp);
|
||||
};
|
||||
|
||||
_ = try self.addInst(.{
|
||||
@ -1254,9 +1253,9 @@ fn minMax(
|
||||
} else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) {
|
||||
break :blk rhs_reg;
|
||||
} else {
|
||||
break :blk try self.register_manager.allocReg(inst);
|
||||
break :blk try self.register_manager.allocReg(inst, gp);
|
||||
}
|
||||
} else try self.register_manager.allocReg(null);
|
||||
} else try self.register_manager.allocReg(null, gp);
|
||||
|
||||
// lhs == reg should have been checked by airMinMax
|
||||
//
|
||||
@ -1438,7 +1437,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
|
||||
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);
|
||||
const truncated_reg = try self.register_manager.allocReg(null, gp);
|
||||
const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg);
|
||||
defer self.register_manager.unlockReg(truncated_reg_lock);
|
||||
|
||||
@ -1543,7 +1542,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
|
||||
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);
|
||||
const truncated_reg = try self.register_manager.allocReg(null, gp);
|
||||
const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg);
|
||||
defer self.register_manager.unlockReg(truncated_reg_lock);
|
||||
|
||||
@ -1582,18 +1581,18 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const lhs_reg = if (lhs_is_register)
|
||||
lhs.register
|
||||
else
|
||||
try self.register_manager.allocReg(null);
|
||||
try self.register_manager.allocReg(null, gp);
|
||||
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
|
||||
try self.register_manager.allocReg(null);
|
||||
try self.register_manager.allocReg(null, gp);
|
||||
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 });
|
||||
const dest_regs = try self.register_manager.allocRegs(2, .{ null, null }, gp);
|
||||
const dest_regs_locks = self.register_manager.lockRegsAssumeUnused(2, dest_regs);
|
||||
defer for (dest_regs_locks) |reg| {
|
||||
self.register_manager.unlockReg(reg);
|
||||
@ -1604,7 +1603,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
|
||||
if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
|
||||
if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);
|
||||
|
||||
const truncated_reg = try self.register_manager.allocReg(null);
|
||||
const truncated_reg = try self.register_manager.allocReg(null, gp);
|
||||
const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg);
|
||||
defer self.register_manager.unlockReg(truncated_reg_lock);
|
||||
|
||||
@ -2026,7 +2025,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
|
||||
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_reg = try self.register_manager.allocReg(inst, gp);
|
||||
const dst_mcv = MCValue{ .register = dst_reg };
|
||||
const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
|
||||
defer self.register_manager.unlockReg(dst_reg_lock);
|
||||
@ -2234,7 +2233,7 @@ 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);
|
||||
const tmp_reg = try self.register_manager.allocReg(null, gp);
|
||||
const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
|
||||
defer self.register_manager.unlockReg(tmp_reg_lock);
|
||||
|
||||
@ -2242,7 +2241,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
|
||||
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 });
|
||||
const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, gp);
|
||||
const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs);
|
||||
defer for (regs_locks) |reg_locked| {
|
||||
self.register_manager.unlockReg(reg_locked);
|
||||
@ -2271,7 +2270,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
|
||||
.stack_offset,
|
||||
.stack_argument_offset,
|
||||
=> {
|
||||
const reg = try self.register_manager.allocReg(null);
|
||||
const reg = try self.register_manager.allocReg(null, gp);
|
||||
const reg_lock = self.register_manager.lockRegAssumeUnused(reg);
|
||||
defer self.register_manager.unlockReg(reg_lock);
|
||||
|
||||
@ -2338,14 +2337,14 @@ 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);
|
||||
const tmp_reg = try self.register_manager.allocReg(null, gp);
|
||||
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 });
|
||||
const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, gp);
|
||||
const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs);
|
||||
defer for (regs_locks) |reg| {
|
||||
self.register_manager.unlockReg(reg);
|
||||
@ -2487,7 +2486,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
|
||||
1 => {
|
||||
// get overflow bit: set register to C flag
|
||||
// resp. V flag
|
||||
const dest_reg = try self.register_manager.allocReg(null);
|
||||
const dest_reg = try self.register_manager.allocReg(null, gp);
|
||||
|
||||
// mov reg, #0
|
||||
_ = try self.addInst(.{
|
||||
@ -2567,7 +2566,7 @@ fn binOpRegister(
|
||||
break :inst Air.refToIndex(md.lhs).?;
|
||||
} else null;
|
||||
|
||||
const reg = try self.register_manager.allocReg(track_inst);
|
||||
const reg = try self.register_manager.allocReg(track_inst, gp);
|
||||
|
||||
if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
|
||||
|
||||
@ -2581,7 +2580,7 @@ fn binOpRegister(
|
||||
break :inst Air.refToIndex(md.rhs).?;
|
||||
} else null;
|
||||
|
||||
const reg = try self.register_manager.allocReg(track_inst);
|
||||
const reg = try self.register_manager.allocReg(track_inst, gp);
|
||||
|
||||
if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
|
||||
|
||||
@ -2598,9 +2597,9 @@ fn binOpRegister(
|
||||
} else if (rhs_is_register and self.reuseOperand(md.inst, md.rhs, 1, rhs)) {
|
||||
break :blk rhs_reg;
|
||||
} else {
|
||||
break :blk try self.register_manager.allocReg(md.inst);
|
||||
break :blk try self.register_manager.allocReg(md.inst, gp);
|
||||
}
|
||||
} else try self.register_manager.allocReg(null),
|
||||
} else try self.register_manager.allocReg(null, gp),
|
||||
};
|
||||
|
||||
if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
|
||||
@ -2684,7 +2683,7 @@ fn binOpImmediate(
|
||||
).?;
|
||||
} else null;
|
||||
|
||||
const reg = try self.register_manager.allocReg(track_inst);
|
||||
const reg = try self.register_manager.allocReg(track_inst, gp);
|
||||
|
||||
if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
|
||||
|
||||
@ -2704,9 +2703,9 @@ fn binOpImmediate(
|
||||
)) {
|
||||
break :blk lhs_reg;
|
||||
} else {
|
||||
break :blk try self.register_manager.allocReg(md.inst);
|
||||
break :blk try self.register_manager.allocReg(md.inst, gp);
|
||||
}
|
||||
} else try self.register_manager.allocReg(null),
|
||||
} else try self.register_manager.allocReg(null, gp),
|
||||
};
|
||||
|
||||
if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
|
||||
@ -4363,7 +4362,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
|
||||
|
||||
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);
|
||||
const cond_reg = try self.register_manager.allocReg(null, gp);
|
||||
|
||||
// C flag: movcs reg, #1
|
||||
// V flag: movvs reg, #1
|
||||
@ -4408,7 +4407,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
|
||||
const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
|
||||
|
||||
// TODO call extern memcpy
|
||||
const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null });
|
||||
const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, gp);
|
||||
const src_reg = regs[0];
|
||||
const dst_reg = regs[1];
|
||||
const len_reg = regs[2];
|
||||
@ -4782,7 +4781,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I
|
||||
const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
|
||||
|
||||
// TODO call extern memcpy
|
||||
const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null });
|
||||
const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, gp);
|
||||
const src_reg = regs[0];
|
||||
const dst_reg = regs[1];
|
||||
const len_reg = regs[2];
|
||||
|
||||
@ -1,9 +1,28 @@
|
||||
const std = @import("std");
|
||||
const bits = @import("bits.zig");
|
||||
const Register = bits.Register;
|
||||
const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
|
||||
|
||||
pub const callee_preserved_regs = [_]Register{ .r4, .r5, .r6, .r7, .r8, .r10 };
|
||||
pub const caller_preserved_regs = [_]Register{ .r0, .r1, .r2, .r3 };
|
||||
pub const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs;
|
||||
|
||||
pub const c_abi_int_param_regs = [_]Register{ .r0, .r1, .r2, .r3 };
|
||||
pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 };
|
||||
|
||||
const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs;
|
||||
pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers);
|
||||
|
||||
// Register classes
|
||||
const RegisterBitSet = RegisterManager.RegisterBitSet;
|
||||
pub const RegisterClass = struct {
|
||||
pub const gp: RegisterBitSet = std.math.maxInt(RegisterBitSet);
|
||||
// TODO uncomment once #11680 is fixed.
|
||||
// pub const gp: RegisterBitSet = blk: {
|
||||
// var set = RegisterBitSet.initEmpty();
|
||||
// set.setRangeValue(.{
|
||||
// .start = 0,
|
||||
// .end = caller_preserved_regs.len + callee_preserved_regs.len,
|
||||
// }, true);
|
||||
// break :blk set;
|
||||
// };
|
||||
};
|
||||
|
||||
@ -21,9 +21,6 @@ const DW = std.dwarf;
|
||||
const leb128 = std.leb;
|
||||
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;
|
||||
@ -32,8 +29,11 @@ const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
|
||||
const bits = @import("bits.zig");
|
||||
const abi = @import("abi.zig");
|
||||
const Register = bits.Register;
|
||||
const RegisterManager = abi.RegisterManager;
|
||||
const RegisterLock = RegisterManager.RegisterLock;
|
||||
const Instruction = abi.Instruction;
|
||||
const callee_preserved_regs = abi.callee_preserved_regs;
|
||||
const gp = abi.RegisterClass.gp;
|
||||
|
||||
const InnerError = error{
|
||||
OutOfMemory,
|
||||
@ -803,7 +803,7 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
|
||||
const ptr_bits = self.target.cpu.arch.ptrBitWidth();
|
||||
const ptr_bytes: u64 = @divExact(ptr_bits, 8);
|
||||
if (abi_size <= ptr_bytes) {
|
||||
if (self.register_manager.tryAllocReg(inst)) |reg| {
|
||||
if (self.register_manager.tryAllocReg(inst, gp)) |reg| {
|
||||
return MCValue{ .register = reg };
|
||||
}
|
||||
}
|
||||
@ -826,7 +826,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
|
||||
/// allocated. A second call to `copyToTmpRegister` may return the same register.
|
||||
/// This can have a side effect of spilling instructions to the stack to free up a register.
|
||||
fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
|
||||
const reg = try self.register_manager.allocReg(null);
|
||||
const reg = try self.register_manager.allocReg(null, gp);
|
||||
try self.genSetReg(ty, reg, mcv);
|
||||
return reg;
|
||||
}
|
||||
@ -835,7 +835,7 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
|
||||
/// `reg_owner` is the instruction that gets associated with the register in the register table.
|
||||
/// This can have a side effect of spilling instructions to the stack to free up a register.
|
||||
fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue {
|
||||
const reg = try self.register_manager.allocReg(reg_owner);
|
||||
const reg = try self.register_manager.allocReg(reg_owner, gp);
|
||||
try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv);
|
||||
return MCValue{ .register = reg };
|
||||
}
|
||||
@ -958,7 +958,7 @@ fn binOpRegister(
|
||||
break :inst Air.refToIndex(bin_op.lhs).?;
|
||||
} else null;
|
||||
|
||||
const reg = try self.register_manager.allocReg(track_inst);
|
||||
const reg = try self.register_manager.allocReg(track_inst, gp);
|
||||
|
||||
if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
|
||||
|
||||
@ -973,7 +973,7 @@ fn binOpRegister(
|
||||
break :inst Air.refToIndex(bin_op.rhs).?;
|
||||
} else null;
|
||||
|
||||
const reg = try self.register_manager.allocReg(track_inst);
|
||||
const reg = try self.register_manager.allocReg(track_inst, gp);
|
||||
|
||||
if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
|
||||
|
||||
@ -990,9 +990,9 @@ fn binOpRegister(
|
||||
} else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) {
|
||||
break :blk rhs_reg;
|
||||
} else {
|
||||
break :blk try self.register_manager.allocReg(inst);
|
||||
break :blk try self.register_manager.allocReg(inst, gp);
|
||||
}
|
||||
} else try self.register_manager.allocReg(null);
|
||||
} else try self.register_manager.allocReg(null, gp);
|
||||
|
||||
if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
|
||||
if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);
|
||||
@ -1482,7 +1482,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
|
||||
.memory,
|
||||
.stack_offset,
|
||||
=> {
|
||||
const reg = try self.register_manager.allocReg(null);
|
||||
const reg = try self.register_manager.allocReg(null, gp);
|
||||
const reg_lock = self.register_manager.lockRegAssumeUnused(reg);
|
||||
defer self.register_manager.unlockReg(reg_lock);
|
||||
|
||||
|
||||
@ -1,6 +1,26 @@
|
||||
const std = @import("std");
|
||||
const bits = @import("bits.zig");
|
||||
const Register = bits.Register;
|
||||
const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
|
||||
|
||||
pub const callee_preserved_regs = [_]Register{
|
||||
.s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
|
||||
};
|
||||
|
||||
const allocatable_registers = callee_preserved_regs;
|
||||
pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers);
|
||||
|
||||
// Register classes
|
||||
const RegisterBitSet = RegisterManager.RegisterBitSet;
|
||||
pub const RegisterClass = struct {
|
||||
pub const gp: RegisterBitSet = std.math.maxInt(RegisterBitSet);
|
||||
// TODO uncomment once #11680 is fixed.
|
||||
// pub const gp: RegisterBitSet = blk: {
|
||||
// var set = RegisterBitSet.initEmpty();
|
||||
// set.setRangeValue(.{
|
||||
// .start = 0,
|
||||
// .end = callee_preserved_regs.len,
|
||||
// }, true);
|
||||
// break :blk set;
|
||||
// };
|
||||
};
|
||||
|
||||
@ -21,9 +21,6 @@ const Type = @import("../../type.zig").Type;
|
||||
const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
|
||||
const FnResult = @import("../../codegen.zig").FnResult;
|
||||
const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
|
||||
const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
|
||||
const RegisterManager = RegisterManagerFn(Self, Register, &abi.allocatable_regs);
|
||||
const RegisterLock = RegisterManager.RegisterLock;
|
||||
|
||||
const build_options = @import("build_options");
|
||||
|
||||
@ -31,7 +28,10 @@ const bits = @import("bits.zig");
|
||||
const abi = @import("abi.zig");
|
||||
const Instruction = bits.Instruction;
|
||||
const ShiftWidth = Instruction.ShiftWidth;
|
||||
const RegisterManager = abi.RegisterManager;
|
||||
const RegisterLock = RegisterManager.RegisterLock;
|
||||
const Register = bits.Register;
|
||||
const gp = abi.RegisterClass.gp;
|
||||
|
||||
const Self = @This();
|
||||
|
||||
@ -1613,7 +1613,7 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
|
||||
if (reg_ok) {
|
||||
// Make sure the type can fit in a register before we try to allocate one.
|
||||
if (abi_size <= 8) {
|
||||
if (self.register_manager.tryAllocReg(inst)) |reg| {
|
||||
if (self.register_manager.tryAllocReg(inst, gp)) |reg| {
|
||||
return MCValue{ .register = reg };
|
||||
}
|
||||
}
|
||||
@ -1854,7 +1854,7 @@ fn binOpImmediate(
|
||||
).?;
|
||||
} else null;
|
||||
|
||||
const reg = try self.register_manager.allocReg(track_inst);
|
||||
const reg = try self.register_manager.allocReg(track_inst, gp);
|
||||
|
||||
if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
|
||||
|
||||
@ -1873,10 +1873,10 @@ fn binOpImmediate(
|
||||
)) {
|
||||
break :blk lhs_reg;
|
||||
} else {
|
||||
break :blk try self.register_manager.allocReg(md.inst);
|
||||
break :blk try self.register_manager.allocReg(md.inst, gp);
|
||||
}
|
||||
} else blk: {
|
||||
break :blk try self.register_manager.allocReg(null);
|
||||
break :blk try self.register_manager.allocReg(null, gp);
|
||||
},
|
||||
};
|
||||
|
||||
@ -1953,7 +1953,7 @@ fn binOpRegister(
|
||||
break :inst Air.refToIndex(md.lhs).?;
|
||||
} else null;
|
||||
|
||||
const reg = try self.register_manager.allocReg(track_inst);
|
||||
const reg = try self.register_manager.allocReg(track_inst, gp);
|
||||
if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
|
||||
|
||||
break :blk reg;
|
||||
@ -1966,7 +1966,7 @@ fn binOpRegister(
|
||||
break :inst Air.refToIndex(md.rhs).?;
|
||||
} else null;
|
||||
|
||||
const reg = try self.register_manager.allocReg(track_inst);
|
||||
const reg = try self.register_manager.allocReg(track_inst, gp);
|
||||
if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
|
||||
|
||||
break :blk reg;
|
||||
@ -1981,10 +1981,10 @@ fn binOpRegister(
|
||||
} else if (rhs_is_register and self.reuseOperand(md.inst, md.rhs, 1, rhs)) {
|
||||
break :blk rhs_reg;
|
||||
} else {
|
||||
break :blk try self.register_manager.allocReg(md.inst);
|
||||
break :blk try self.register_manager.allocReg(md.inst, gp);
|
||||
}
|
||||
} else blk: {
|
||||
break :blk try self.register_manager.allocReg(null);
|
||||
break :blk try self.register_manager.allocReg(null, gp);
|
||||
},
|
||||
};
|
||||
|
||||
@ -2077,7 +2077,7 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
|
||||
/// allocated. A second call to `copyToTmpRegister` may return the same register.
|
||||
/// This can have a side effect of spilling instructions to the stack to free up a register.
|
||||
fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
|
||||
const reg = try self.register_manager.allocReg(null);
|
||||
const reg = try self.register_manager.allocReg(null, gp);
|
||||
try self.genSetReg(ty, reg, mcv);
|
||||
return reg;
|
||||
}
|
||||
@ -2364,7 +2364,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
|
||||
});
|
||||
} else {
|
||||
// Need to allocate a temporary register to load 64-bit immediates.
|
||||
const tmp_reg = try self.register_manager.allocReg(null);
|
||||
const tmp_reg = try self.register_manager.allocReg(null, gp);
|
||||
|
||||
try self.genSetReg(ty, tmp_reg, .{ .immediate = @truncate(u32, x) });
|
||||
try self.genSetReg(ty, reg, .{ .immediate = @truncate(u32, x >> 32) });
|
||||
@ -2478,7 +2478,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
|
||||
};
|
||||
const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
|
||||
|
||||
const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null });
|
||||
const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, gp);
|
||||
const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs);
|
||||
defer for (regs_locks) |reg| {
|
||||
self.register_manager.unlockReg(reg);
|
||||
@ -2717,14 +2717,14 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
|
||||
},
|
||||
.stack_offset => |off| {
|
||||
if (elem_size <= 8) {
|
||||
const tmp_reg = try self.register_manager.allocReg(null);
|
||||
const tmp_reg = try self.register_manager.allocReg(null, gp);
|
||||
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 {
|
||||
const regs = try self.register_manager.allocRegs(3, .{ null, null, null });
|
||||
const regs = try self.register_manager.allocRegs(3, .{ null, null, null }, gp);
|
||||
const regs_locks = self.register_manager.lockRegsAssumeUnused(3, regs);
|
||||
defer for (regs_locks) |reg| {
|
||||
self.register_manager.unlockReg(reg);
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
const std = @import("std");
|
||||
const bits = @import("bits.zig");
|
||||
const Register = bits.Register;
|
||||
const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
|
||||
|
||||
// SPARCv9 stack constants.
|
||||
// See: Registers and the Stack Frame, page 3P-8, SCD 2.4.1.
|
||||
@ -21,7 +23,7 @@ pub const stack_save_area = 176;
|
||||
pub const caller_preserved_regs = [_]Register{ .o0, .o1, .o2, .o3, .o4, .o5, .g1, .g4, .g5 };
|
||||
|
||||
// Try to allocate i, l, o, then g sets of registers, in order of priority.
|
||||
pub const allocatable_regs = [_]Register{
|
||||
const allocatable_regs = [_]Register{
|
||||
// zig fmt: off
|
||||
.@"i0", .@"i1", .@"i2", .@"i3", .@"i4", .@"i5",
|
||||
.l0, .l1, .l2, .l3, .l4, .l5, .l6, .l7,
|
||||
@ -35,3 +37,20 @@ pub const c_abi_int_param_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2"
|
||||
|
||||
pub const c_abi_int_return_regs_caller_view = [_]Register{ .o0, .o1, .o2, .o3 };
|
||||
pub const c_abi_int_return_regs_callee_view = [_]Register{ .@"i0", .@"i1", .@"i2", .@"i3" };
|
||||
|
||||
pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_regs);
|
||||
|
||||
// Register classes
|
||||
const RegisterBitSet = RegisterManager.RegisterBitSet;
|
||||
pub const RegisterClass = struct {
|
||||
pub const gp: RegisterBitSet = std.math.maxInt(RegisterBitSet);
|
||||
// TODO uncomment once #11680 is fixed.
|
||||
// pub const gp: RegisterBitSet = blk: {
|
||||
// var set = RegisterBitSet.initEmpty();
|
||||
// set.setRangeValue(.{
|
||||
// .start = 0,
|
||||
// .end = allocatable_regs.len,
|
||||
// }, true);
|
||||
// break :blk set;
|
||||
// };
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -22,11 +22,7 @@ extra: []const u32,
|
||||
|
||||
pub const Inst = struct {
|
||||
tag: Tag,
|
||||
/// This is 3 fields, and the meaning of each depends on `tag`.
|
||||
/// reg1: Register
|
||||
/// reg2: Register
|
||||
/// flags: u2
|
||||
ops: u16,
|
||||
ops: Ops,
|
||||
/// The meaning of this depends on `tag` and `ops`.
|
||||
data: Data,
|
||||
|
||||
@ -349,6 +345,42 @@ pub const Inst = struct {
|
||||
/// Nop
|
||||
nop,
|
||||
|
||||
/// SSE instructions
|
||||
/// ops flags: form:
|
||||
/// 0b00 reg1, qword ptr [reg2 + imm32]
|
||||
/// 0b01 qword ptr [reg1 + imm32], reg2
|
||||
/// 0b10 reg1, reg2
|
||||
mov_f64_sse,
|
||||
mov_f32_sse,
|
||||
|
||||
/// ops flags: form:
|
||||
/// 0b00 reg1, reg2
|
||||
add_f64_sse,
|
||||
add_f32_sse,
|
||||
|
||||
/// ops flags: form:
|
||||
/// 0b00 reg1, reg2
|
||||
cmp_f64_sse,
|
||||
cmp_f32_sse,
|
||||
|
||||
/// AVX instructions
|
||||
/// ops flags: form:
|
||||
/// 0b00 reg1, qword ptr [reg2 + imm32]
|
||||
/// 0b01 qword ptr [reg1 + imm32], reg2
|
||||
/// 0b10 reg1, reg1, reg2
|
||||
mov_f64_avx,
|
||||
mov_f32_avx,
|
||||
|
||||
/// ops flags: form:
|
||||
/// 0b00 reg1, reg1, reg2
|
||||
add_f64_avx,
|
||||
add_f32_avx,
|
||||
|
||||
/// ops flags: form:
|
||||
/// 0b00 reg1, reg1, reg2
|
||||
cmp_f64_avx,
|
||||
cmp_f32_avx,
|
||||
|
||||
/// Pseudo-instructions
|
||||
/// call extern function
|
||||
/// Notes:
|
||||
@ -381,6 +413,36 @@ pub const Inst = struct {
|
||||
/// The position of an MIR instruction within the `Mir` instructions array.
|
||||
pub const Index = u32;
|
||||
|
||||
pub const Ops = packed struct {
|
||||
reg1: u7,
|
||||
reg2: u7,
|
||||
flags: u2,
|
||||
|
||||
pub fn encode(vals: struct {
|
||||
reg1: Register = .none,
|
||||
reg2: Register = .none,
|
||||
flags: u2 = 0b00,
|
||||
}) Ops {
|
||||
return .{
|
||||
.reg1 = @enumToInt(vals.reg1),
|
||||
.reg2 = @enumToInt(vals.reg2),
|
||||
.flags = vals.flags,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn decode(ops: Ops) struct {
|
||||
reg1: Register,
|
||||
reg2: Register,
|
||||
flags: u2,
|
||||
} {
|
||||
return .{
|
||||
.reg1 = @intToEnum(Register, ops.reg1),
|
||||
.reg2 = @intToEnum(Register, ops.reg2),
|
||||
.flags = ops.flags,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// All instructions have a 4-byte payload, which is contained within
|
||||
/// this union. `Tag` determines which union field is active, as well as
|
||||
/// how to interpret the data within.
|
||||
@ -450,31 +512,6 @@ pub const DbgLineColumn = struct {
|
||||
column: u32,
|
||||
};
|
||||
|
||||
pub const Ops = struct {
|
||||
reg1: Register = .none,
|
||||
reg2: Register = .none,
|
||||
flags: u2 = 0b00,
|
||||
|
||||
pub fn encode(self: Ops) u16 {
|
||||
var ops: u16 = 0;
|
||||
ops |= @intCast(u16, @enumToInt(self.reg1)) << 9;
|
||||
ops |= @intCast(u16, @enumToInt(self.reg2)) << 2;
|
||||
ops |= self.flags;
|
||||
return ops;
|
||||
}
|
||||
|
||||
pub fn decode(ops: u16) Ops {
|
||||
const reg1 = @intToEnum(Register, @truncate(u7, ops >> 9));
|
||||
const reg2 = @intToEnum(Register, @truncate(u7, ops >> 2));
|
||||
const flags = @truncate(u2, ops);
|
||||
return .{
|
||||
.reg1 = reg1,
|
||||
.reg2 = reg2,
|
||||
.flags = flags,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void {
|
||||
mir.instructions.deinit(gpa);
|
||||
gpa.free(mir.extra);
|
||||
|
||||
@ -3,6 +3,7 @@ const Type = @import("../../type.zig").Type;
|
||||
const Target = std.Target;
|
||||
const assert = std.debug.assert;
|
||||
const Register = @import("bits.zig").Register;
|
||||
const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
|
||||
|
||||
pub const Class = enum { integer, sse, sseup, x87, x87up, complex_x87, memory, none };
|
||||
|
||||
@ -378,6 +379,40 @@ pub const callee_preserved_regs = [_]Register{ .rbx, .r12, .r13, .r14, .r15 };
|
||||
/// the caller relinquishes control to a subroutine via call instruction (or similar).
|
||||
/// In other words, these registers are free to use by the callee.
|
||||
pub const caller_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };
|
||||
pub const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs;
|
||||
|
||||
pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
|
||||
pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
|
||||
|
||||
const sse_avx_regs = [_]Register{
|
||||
.ymm0, .ymm1, .ymm2, .ymm3, .ymm4, .ymm5, .ymm6, .ymm7,
|
||||
.ymm8, .ymm9, .ymm10, .ymm11, .ymm12, .ymm13, .ymm14, .ymm15,
|
||||
};
|
||||
const allocatable_registers = callee_preserved_regs ++ caller_preserved_regs ++ sse_avx_regs;
|
||||
pub const RegisterManager = RegisterManagerFn(@import("CodeGen.zig"), Register, &allocatable_registers);
|
||||
|
||||
// Register classes
|
||||
const RegisterBitSet = RegisterManager.RegisterBitSet;
|
||||
pub const RegisterClass = struct {
|
||||
pub const gp: RegisterBitSet = @as(RegisterBitSet, std.math.maxInt(std.meta.Int(
|
||||
.unsigned,
|
||||
caller_preserved_regs.len + callee_preserved_regs.len,
|
||||
)));
|
||||
pub const sse: RegisterBitSet = std.math.maxInt(RegisterBitSet) - gp;
|
||||
// TODO uncomment once #11680 is fixed.
|
||||
// pub const gp: RegisterBitSet = blk: {
|
||||
// var set = RegisterBitSet.initEmpty();
|
||||
// set.setRangeValue(.{
|
||||
// .start = 0,
|
||||
// .end = caller_preserved_regs.len + callee_preserved_regs.len,
|
||||
// }, true);
|
||||
// break :blk set;
|
||||
// };
|
||||
// pub const sse: RegisterBitSet = blk: {
|
||||
// var set = RegisterBitSet.initEmpty();
|
||||
// set.setRangeValue(.{
|
||||
// .start = caller_preserved_regs.len + callee_preserved_regs.len,
|
||||
// .end = allocatable_registers.len,
|
||||
// }, true);
|
||||
// break :blk set;
|
||||
// };
|
||||
};
|
||||
|
||||
@ -8,7 +8,7 @@ const DW = std.dwarf;
|
||||
|
||||
// zig fmt: off
|
||||
|
||||
/// Definitions of all of the x64 registers. The order is semantically meaningful.
|
||||
/// Definitions of all of the general purpose x64 registers. The order is semantically meaningful.
|
||||
/// The registers are defined such that IDs go in descending order of 64-bit,
|
||||
/// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen
|
||||
/// registers. This results in some useful properties:
|
||||
@ -43,17 +43,36 @@ pub const Register = enum(u7) {
|
||||
al, cl, dl, bl, ah, ch, dh, bh,
|
||||
r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b,
|
||||
|
||||
// Pseudo, used only for MIR to signify that the
|
||||
// operand is not a register but an immediate, etc.
|
||||
// 64-79, 256-bit registers.
|
||||
// id is int value - 64.
|
||||
ymm0, ymm1, ymm2, ymm3, ymm4, ymm5, ymm6, ymm7,
|
||||
ymm8, ymm9, ymm10, ymm11, ymm12, ymm13, ymm14, ymm15,
|
||||
|
||||
// 80-95, 128-bit registers.
|
||||
// id is int value - 80.
|
||||
xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7,
|
||||
xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15,
|
||||
|
||||
// Pseudo-value for MIR instructions.
|
||||
none,
|
||||
|
||||
pub fn id(self: Register) u7 {
|
||||
return switch (@enumToInt(self)) {
|
||||
0...63 => @as(u7, @truncate(u4, @enumToInt(self))),
|
||||
64...79 => @enumToInt(self),
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns the bit-width of the register.
|
||||
pub fn size(self: Register) u7 {
|
||||
pub fn size(self: Register) u9 {
|
||||
return switch (@enumToInt(self)) {
|
||||
0...15 => 64,
|
||||
16...31 => 32,
|
||||
32...47 => 16,
|
||||
48...64 => 8,
|
||||
48...63 => 8,
|
||||
64...79 => 256,
|
||||
80...95 => 128,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
@ -72,33 +91,41 @@ pub const Register = enum(u7) {
|
||||
/// an instruction (@see isExtended), and requires special handling. The
|
||||
/// lower three bits are often embedded directly in instructions (such as
|
||||
/// the B8 variant of moves), or used in R/M bytes.
|
||||
pub fn id(self: Register) u4 {
|
||||
pub fn enc(self: Register) u4 {
|
||||
return @truncate(u4, @enumToInt(self));
|
||||
}
|
||||
|
||||
/// Like id, but only returns the lower 3 bits.
|
||||
pub fn lowId(self: Register) u3 {
|
||||
/// Like enc, but only returns the lower 3 bits.
|
||||
pub fn lowEnc(self: Register) u3 {
|
||||
return @truncate(u3, @enumToInt(self));
|
||||
}
|
||||
|
||||
pub fn to256(self: Register) Register {
|
||||
return @intToEnum(Register, @as(u8, self.enc()) + 64);
|
||||
}
|
||||
|
||||
pub fn to128(self: Register) Register {
|
||||
return @intToEnum(Register, @as(u8, self.enc()) + 80);
|
||||
}
|
||||
|
||||
/// Convert from any register to its 64 bit alias.
|
||||
pub fn to64(self: Register) Register {
|
||||
return @intToEnum(Register, self.id());
|
||||
return @intToEnum(Register, self.enc());
|
||||
}
|
||||
|
||||
/// Convert from any register to its 32 bit alias.
|
||||
pub fn to32(self: Register) Register {
|
||||
return @intToEnum(Register, @as(u8, self.id()) + 16);
|
||||
return @intToEnum(Register, @as(u8, self.enc()) + 16);
|
||||
}
|
||||
|
||||
/// Convert from any register to its 16 bit alias.
|
||||
pub fn to16(self: Register) Register {
|
||||
return @intToEnum(Register, @as(u8, self.id()) + 32);
|
||||
return @intToEnum(Register, @as(u8, self.enc()) + 32);
|
||||
}
|
||||
|
||||
/// Convert from any register to its 8 bit alias.
|
||||
pub fn to8(self: Register) Register {
|
||||
return @intToEnum(Register, @as(u8, self.id()) + 48);
|
||||
return @intToEnum(Register, @as(u8, self.enc()) + 48);
|
||||
}
|
||||
|
||||
pub fn dwarfLocOp(self: Register) u8 {
|
||||
@ -251,6 +278,115 @@ pub const Encoder = struct {
|
||||
self.code.appendAssumeCapacity(0x66);
|
||||
}
|
||||
|
||||
pub const Vex = struct {
|
||||
rex_prefix: Rex = .{},
|
||||
lead_opc: u5 = 0b0_0001,
|
||||
register: u4 = 0b1111,
|
||||
length: u1 = 0b0,
|
||||
simd_prefix: u2 = 0b00,
|
||||
wig_desc: bool = false,
|
||||
lig_desc: bool = false,
|
||||
lz_desc: bool = false,
|
||||
|
||||
pub fn rex(self: *Vex, r: Rex) void {
|
||||
self.rex_prefix = r;
|
||||
}
|
||||
|
||||
pub fn lead_opc_0f(self: *Vex) void {
|
||||
self.lead_opc = 0b0_0001;
|
||||
}
|
||||
|
||||
pub fn lead_opc_0f_38(self: *Vex) void {
|
||||
self.lead_opc = 0b0_0010;
|
||||
}
|
||||
|
||||
pub fn lead_opc_0f_3a(self: *Vex) void {
|
||||
self.lead_opc = 0b0_0011;
|
||||
}
|
||||
|
||||
pub fn reg(self: *Vex, register: u4) void {
|
||||
self.register = ~register;
|
||||
}
|
||||
|
||||
pub fn len_128(self: *Vex) void {
|
||||
self.length = 0;
|
||||
}
|
||||
|
||||
pub fn len_256(self: *Vex) void {
|
||||
assert(!self.lz_desc);
|
||||
self.length = 1;
|
||||
}
|
||||
|
||||
pub fn simd_prefix_66(self: *Vex) void {
|
||||
self.simd_prefix = 0b01;
|
||||
}
|
||||
|
||||
pub fn simd_prefix_f3(self: *Vex) void {
|
||||
self.simd_prefix = 0b10;
|
||||
}
|
||||
|
||||
pub fn simd_prefix_f2(self: *Vex) void {
|
||||
self.simd_prefix = 0b11;
|
||||
}
|
||||
|
||||
pub fn wig(self: *Vex) void {
|
||||
self.wig_desc = true;
|
||||
}
|
||||
|
||||
pub fn lig(self: *Vex) void {
|
||||
self.lig_desc = true;
|
||||
}
|
||||
|
||||
pub fn lz(self: *Vex) void {
|
||||
self.lz_desc = true;
|
||||
}
|
||||
|
||||
pub fn write(self: Vex, writer: anytype) usize {
|
||||
var buf: [3]u8 = .{0} ** 3;
|
||||
const form_3byte: bool = blk: {
|
||||
if (self.rex_prefix.w and !self.wig_desc) break :blk true;
|
||||
if (self.rex_prefix.x or self.rex_prefix.b) break :blk true;
|
||||
break :blk self.lead_opc != 0b0_0001;
|
||||
};
|
||||
|
||||
if (self.lz_desc) {
|
||||
assert(self.length == 0);
|
||||
}
|
||||
|
||||
if (form_3byte) {
|
||||
// First byte
|
||||
buf[0] = 0xc4;
|
||||
// Second byte
|
||||
const rxb_mask: u3 = @intCast(u3, @boolToInt(!self.rex_prefix.r)) << 2 |
|
||||
@intCast(u2, @boolToInt(!self.rex_prefix.x)) << 1 |
|
||||
@boolToInt(!self.rex_prefix.b);
|
||||
buf[1] |= @intCast(u8, rxb_mask) << 5;
|
||||
buf[1] |= self.lead_opc;
|
||||
// Third byte
|
||||
buf[2] |= @intCast(u8, @boolToInt(!self.rex_prefix.w)) << 7;
|
||||
buf[2] |= @intCast(u7, self.register) << 3;
|
||||
buf[2] |= @intCast(u3, self.length) << 2;
|
||||
buf[2] |= self.simd_prefix;
|
||||
} else {
|
||||
// First byte
|
||||
buf[0] = 0xc5;
|
||||
// Second byte
|
||||
buf[1] |= @intCast(u8, @boolToInt(!self.rex_prefix.r)) << 7;
|
||||
buf[1] |= @intCast(u7, self.register) << 3;
|
||||
buf[1] |= @intCast(u3, self.length) << 2;
|
||||
buf[1] |= self.simd_prefix;
|
||||
}
|
||||
|
||||
const count: usize = if (form_3byte) 3 else 2;
|
||||
_ = writer.writeAll(buf[0..count]) catch unreachable;
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn vex(self: Self, prefix: Vex) void {
|
||||
_ = prefix.write(self.code.writer());
|
||||
}
|
||||
|
||||
/// From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB
|
||||
pub const Rex = struct {
|
||||
/// Wide, enables 64-bit operation
|
||||
@ -305,6 +441,17 @@ pub const Encoder = struct {
|
||||
self.code.appendAssumeCapacity(opcode);
|
||||
}
|
||||
|
||||
/// Encodes a 3 byte opcode
|
||||
///
|
||||
/// e.g. MOVSD has the opcode 0xf2 0x0f 0x10
|
||||
///
|
||||
/// encoder.opcode_3byte(0xf2, 0x0f, 0x10);
|
||||
pub fn opcode_3byte(self: Self, prefix_1: u8, prefix_2: u8, opcode: u8) void {
|
||||
self.code.appendAssumeCapacity(prefix_1);
|
||||
self.code.appendAssumeCapacity(prefix_2);
|
||||
self.code.appendAssumeCapacity(opcode);
|
||||
}
|
||||
|
||||
/// Encodes a 1 byte opcode with a reg field
|
||||
///
|
||||
/// Remember to add a REX prefix byte if reg is extended!
|
||||
@ -543,7 +690,7 @@ pub const Encoder = struct {
|
||||
}
|
||||
};
|
||||
|
||||
test "x86_64 Encoder helpers" {
|
||||
test "Encoder helpers - general purpose registers" {
|
||||
var code = ArrayList(u8).init(testing.allocator);
|
||||
defer code.deinit();
|
||||
|
||||
@ -560,8 +707,8 @@ test "x86_64 Encoder helpers" {
|
||||
});
|
||||
encoder.opcode_2byte(0x0f, 0xaf);
|
||||
encoder.modRm_direct(
|
||||
Register.eax.lowId(),
|
||||
Register.edi.lowId(),
|
||||
Register.eax.lowEnc(),
|
||||
Register.edi.lowEnc(),
|
||||
);
|
||||
|
||||
try testing.expectEqualSlices(u8, &[_]u8{ 0x0f, 0xaf, 0xc7 }, code.items);
|
||||
@ -580,8 +727,8 @@ test "x86_64 Encoder helpers" {
|
||||
});
|
||||
encoder.opcode_1byte(0x89);
|
||||
encoder.modRm_direct(
|
||||
Register.edi.lowId(),
|
||||
Register.eax.lowId(),
|
||||
Register.edi.lowEnc(),
|
||||
Register.eax.lowEnc(),
|
||||
);
|
||||
|
||||
try testing.expectEqualSlices(u8, &[_]u8{ 0x89, 0xf8 }, code.items);
|
||||
@ -607,7 +754,7 @@ test "x86_64 Encoder helpers" {
|
||||
encoder.opcode_1byte(0x81);
|
||||
encoder.modRm_direct(
|
||||
0,
|
||||
Register.rcx.lowId(),
|
||||
Register.rcx.lowEnc(),
|
||||
);
|
||||
encoder.imm32(2147483647);
|
||||
|
||||
@ -615,6 +762,86 @@ test "x86_64 Encoder helpers" {
|
||||
}
|
||||
}
|
||||
|
||||
test "Encoder helpers - Vex prefix" {
|
||||
var buf: [3]u8 = undefined;
|
||||
var stream = std.io.fixedBufferStream(&buf);
|
||||
const writer = stream.writer();
|
||||
|
||||
{
|
||||
var vex_prefix = Encoder.Vex{};
|
||||
vex_prefix.rex(.{
|
||||
.r = true,
|
||||
});
|
||||
const nwritten = vex_prefix.write(writer);
|
||||
try testing.expectEqualSlices(u8, &[_]u8{ 0xc5, 0x78 }, buf[0..nwritten]);
|
||||
}
|
||||
|
||||
{
|
||||
stream.reset();
|
||||
var vex_prefix = Encoder.Vex{};
|
||||
vex_prefix.reg(Register.xmm15.enc());
|
||||
const nwritten = vex_prefix.write(writer);
|
||||
try testing.expectEqualSlices(u8, &[_]u8{ 0xc5, 0x80 }, buf[0..nwritten]);
|
||||
}
|
||||
|
||||
{
|
||||
stream.reset();
|
||||
var vex_prefix = Encoder.Vex{};
|
||||
vex_prefix.rex(.{
|
||||
.w = true,
|
||||
.x = true,
|
||||
});
|
||||
const nwritten = vex_prefix.write(writer);
|
||||
try testing.expectEqualSlices(u8, &[_]u8{ 0xc4, 0b101_0_0001, 0b0_1111_0_00 }, buf[0..nwritten]);
|
||||
}
|
||||
|
||||
{
|
||||
stream.reset();
|
||||
var vex_prefix = Encoder.Vex{};
|
||||
vex_prefix.rex(.{
|
||||
.w = true,
|
||||
.r = true,
|
||||
});
|
||||
vex_prefix.len_256();
|
||||
vex_prefix.lead_opc_0f();
|
||||
vex_prefix.simd_prefix_66();
|
||||
const nwritten = vex_prefix.write(writer);
|
||||
try testing.expectEqualSlices(u8, &[_]u8{ 0xc4, 0b011_0_0001, 0b0_1111_1_01 }, buf[0..nwritten]);
|
||||
}
|
||||
|
||||
var code = ArrayList(u8).init(testing.allocator);
|
||||
defer code.deinit();
|
||||
|
||||
{
|
||||
// vmovapd xmm1, xmm2
|
||||
const encoder = try Encoder.init(&code, 4);
|
||||
var vex = Encoder.Vex{};
|
||||
vex.simd_prefix_66();
|
||||
encoder.vex(vex); // use 64 bit operation
|
||||
encoder.opcode_1byte(0x28);
|
||||
encoder.modRm_direct(0, Register.xmm1.lowEnc());
|
||||
try testing.expectEqualSlices(u8, &[_]u8{ 0xC5, 0xF9, 0x28, 0xC1 }, code.items);
|
||||
}
|
||||
|
||||
{
|
||||
try code.resize(0);
|
||||
|
||||
// vmovhpd xmm13, xmm1, qword ptr [rip]
|
||||
const encoder = try Encoder.init(&code, 9);
|
||||
var vex = Encoder.Vex{};
|
||||
vex.len_128();
|
||||
vex.simd_prefix_66();
|
||||
vex.lead_opc_0f();
|
||||
vex.rex(.{ .r = true });
|
||||
vex.reg(Register.xmm1.enc());
|
||||
encoder.vex(vex);
|
||||
encoder.opcode_1byte(0x16);
|
||||
encoder.modRm_RIPDisp32(Register.xmm13.lowEnc());
|
||||
encoder.disp32(0);
|
||||
try testing.expectEqualSlices(u8, &[_]u8{ 0xC5, 0x71, 0x16, 0x2D, 0x00, 0x00, 0x00, 0x00 }, code.items);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO add these registers to the enum and populate dwarfLocOp
|
||||
// // Return Address register. This is stored in `0(%rsp, "")` and is not a physical register.
|
||||
// RA = (16, "RA"),
|
||||
|
||||
@ -41,28 +41,33 @@ pub fn RegisterManager(
|
||||
registers: [tracked_registers.len]Air.Inst.Index = undefined,
|
||||
/// Tracks which registers are free (in which case the
|
||||
/// corresponding bit is set to 1)
|
||||
free_registers: FreeRegInt = math.maxInt(FreeRegInt),
|
||||
free_registers: RegisterBitSet = math.maxInt(RegisterBitSet),
|
||||
/// Tracks all registers allocated in the course of this
|
||||
/// function
|
||||
allocated_registers: FreeRegInt = 0,
|
||||
allocated_registers: RegisterBitSet = 0,
|
||||
/// Tracks registers which are locked from being allocated
|
||||
locked_registers: FreeRegInt = 0,
|
||||
locked_registers: RegisterBitSet = 0,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
/// An integer whose bits represent all the registers and
|
||||
/// whether they are free.
|
||||
const FreeRegInt = std.meta.Int(.unsigned, tracked_registers.len);
|
||||
const ShiftInt = math.Log2Int(FreeRegInt);
|
||||
pub const RegisterBitSet = std.meta.Int(.unsigned, tracked_registers.len);
|
||||
const ShiftInt = math.Log2Int(RegisterBitSet);
|
||||
|
||||
fn getFunction(self: *Self) *Function {
|
||||
return @fieldParentPtr(Function, "register_manager", self);
|
||||
}
|
||||
|
||||
fn getRegisterMask(reg: Register) ?FreeRegInt {
|
||||
fn excludeRegister(reg: Register, register_class: RegisterBitSet) bool {
|
||||
const mask = getRegisterMask(reg) orelse return true;
|
||||
return mask & register_class == 0;
|
||||
}
|
||||
|
||||
fn getRegisterMask(reg: Register) ?RegisterBitSet {
|
||||
const index = indexOfRegIntoTracked(reg) orelse return null;
|
||||
const shift = @intCast(ShiftInt, index);
|
||||
const mask = @as(FreeRegInt, 1) << shift;
|
||||
const mask = @as(RegisterBitSet, 1) << shift;
|
||||
return mask;
|
||||
}
|
||||
|
||||
@ -81,7 +86,10 @@ pub fn RegisterManager(
|
||||
self.free_registers |= mask;
|
||||
}
|
||||
|
||||
pub fn indexOfReg(comptime registers: []const Register, reg: Register) ?std.math.IntFittingRange(0, registers.len - 1) {
|
||||
pub fn indexOfReg(
|
||||
comptime registers: []const Register,
|
||||
reg: Register,
|
||||
) ?std.math.IntFittingRange(0, registers.len - 1) {
|
||||
inline for (tracked_registers) |cpreg, i| {
|
||||
if (reg.id() == cpreg.id()) return i;
|
||||
}
|
||||
@ -180,17 +188,20 @@ pub fn RegisterManager(
|
||||
self: *Self,
|
||||
comptime count: comptime_int,
|
||||
insts: [count]?Air.Inst.Index,
|
||||
register_class: RegisterBitSet,
|
||||
) ?[count]Register {
|
||||
comptime assert(count > 0 and count <= tracked_registers.len);
|
||||
|
||||
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);
|
||||
const free_registers = self.free_registers & register_class;
|
||||
const free_and_not_locked_registers = free_registers & ~self.locked_registers;
|
||||
const free_and_not_locked_registers_count = @popCount(RegisterBitSet, 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 (excludeRegister(reg, register_class)) continue;
|
||||
if (self.isRegLocked(reg)) continue;
|
||||
if (!self.isRegFree(reg)) continue;
|
||||
|
||||
@ -216,8 +227,8 @@ pub fn RegisterManager(
|
||||
/// Allocates a register and optionally tracks it with a
|
||||
/// corresponding instruction. Returns `null` if all registers
|
||||
/// are allocated.
|
||||
pub fn tryAllocReg(self: *Self, inst: ?Air.Inst.Index) ?Register {
|
||||
return if (tryAllocRegs(self, 1, .{inst})) |regs| regs[0] else null;
|
||||
pub fn tryAllocReg(self: *Self, inst: ?Air.Inst.Index, register_class: RegisterBitSet) ?Register {
|
||||
return if (tryAllocRegs(self, 1, .{inst}, register_class)) |regs| regs[0] else null;
|
||||
}
|
||||
|
||||
/// Allocates a specified number of registers, optionally
|
||||
@ -227,12 +238,16 @@ pub fn RegisterManager(
|
||||
self: *Self,
|
||||
comptime count: comptime_int,
|
||||
insts: [count]?Air.Inst.Index,
|
||||
register_class: RegisterBitSet,
|
||||
) AllocateRegistersError![count]Register {
|
||||
comptime assert(count > 0 and count <= tracked_registers.len);
|
||||
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: {
|
||||
const available_registers_count = @popCount(RegisterBitSet, register_class);
|
||||
const locked_registers = self.locked_registers & register_class;
|
||||
const locked_registers_count = @popCount(RegisterBitSet, locked_registers);
|
||||
if (count > available_registers_count - locked_registers_count) return error.OutOfRegisters;
|
||||
|
||||
const result = self.tryAllocRegs(count, insts, register_class) orelse blk: {
|
||||
// We'll take over the first count registers. Spill
|
||||
// the instructions that were previously there to a
|
||||
// stack allocations.
|
||||
@ -240,6 +255,7 @@ pub fn RegisterManager(
|
||||
var i: usize = 0;
|
||||
for (tracked_registers) |reg| {
|
||||
if (i >= count) break;
|
||||
if (excludeRegister(reg, register_class)) continue;
|
||||
if (self.isRegLocked(reg)) continue;
|
||||
|
||||
regs[i] = reg;
|
||||
@ -275,8 +291,12 @@ pub fn RegisterManager(
|
||||
|
||||
/// Allocates a register and optionally tracks it with a
|
||||
/// corresponding instruction.
|
||||
pub fn allocReg(self: *Self, inst: ?Air.Inst.Index) AllocateRegistersError!Register {
|
||||
return (try self.allocRegs(1, .{inst}))[0];
|
||||
pub fn allocReg(
|
||||
self: *Self,
|
||||
inst: ?Air.Inst.Index,
|
||||
register_class: RegisterBitSet,
|
||||
) AllocateRegistersError!Register {
|
||||
return (try self.allocRegs(1, .{inst}, register_class))[0];
|
||||
}
|
||||
|
||||
/// Spills the register if it is currently allocated. If a
|
||||
@ -332,6 +352,334 @@ pub fn RegisterManager(
|
||||
};
|
||||
}
|
||||
|
||||
// TODO delete current implementation of RegisterManager above, and uncomment the one
|
||||
// below once #11680 is fixed:
|
||||
// https://github.com/ziglang/zig/issues/11680
|
||||
|
||||
//pub fn RegisterManager(
|
||||
// comptime Function: type,
|
||||
// comptime Register: type,
|
||||
// comptime tracked_registers: []const Register,
|
||||
//) type {
|
||||
// // architectures which do not have a concept of registers should
|
||||
// // refrain from using RegisterManager
|
||||
// assert(tracked_registers.len > 0); // see note above
|
||||
|
||||
// return struct {
|
||||
// /// Tracks the AIR instruction allocated to every register. If
|
||||
// /// no instruction is allocated to a register (i.e. the
|
||||
// /// register is free), the value in that slot is undefined.
|
||||
// ///
|
||||
// /// The key must be canonical register.
|
||||
// registers: [tracked_registers.len]Air.Inst.Index = undefined,
|
||||
// /// Tracks which registers are free (in which case the
|
||||
// /// corresponding bit is set to 1)
|
||||
// free_registers: RegisterBitSet = RegisterBitSet.initFull(),
|
||||
// /// Tracks all registers allocated in the course of this
|
||||
// /// function
|
||||
// allocated_registers: RegisterBitSet = RegisterBitSet.initEmpty(),
|
||||
// /// Tracks registers which are locked from being allocated
|
||||
// locked_registers: RegisterBitSet = RegisterBitSet.initEmpty(),
|
||||
|
||||
// const Self = @This();
|
||||
|
||||
// pub const RegisterBitSet = StaticBitSet(tracked_registers.len);
|
||||
|
||||
// fn getFunction(self: *Self) *Function {
|
||||
// return @fieldParentPtr(Function, "register_manager", self);
|
||||
// }
|
||||
|
||||
// fn excludeRegister(reg: Register, register_class: RegisterBitSet) bool {
|
||||
// const index = indexOfRegIntoTracked(reg) orelse return true;
|
||||
// return !register_class.isSet(index);
|
||||
// }
|
||||
|
||||
// fn markRegAllocated(self: *Self, reg: Register) void {
|
||||
// const index = indexOfRegIntoTracked(reg) orelse return;
|
||||
// self.allocated_registers.set(index);
|
||||
// }
|
||||
|
||||
// fn markRegUsed(self: *Self, reg: Register) void {
|
||||
// const index = indexOfRegIntoTracked(reg) orelse return;
|
||||
// self.free_registers.unset(index);
|
||||
// }
|
||||
|
||||
// fn markRegFree(self: *Self, reg: Register) void {
|
||||
// const index = indexOfRegIntoTracked(reg) orelse return;
|
||||
// self.free_registers.set(index);
|
||||
// }
|
||||
|
||||
// pub fn indexOfReg(
|
||||
// comptime registers: []const Register,
|
||||
// reg: Register,
|
||||
// ) ?std.math.IntFittingRange(0, registers.len - 1) {
|
||||
// inline for (tracked_registers) |cpreg, i| {
|
||||
// if (reg.id() == cpreg.id()) return i;
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// pub fn indexOfRegIntoTracked(reg: Register) ?RegisterBitSet.ShiftInt {
|
||||
// return indexOfReg(tracked_registers, reg);
|
||||
// }
|
||||
|
||||
// /// Returns true when this register is not tracked
|
||||
// pub fn isRegFree(self: Self, reg: Register) bool {
|
||||
// const index = indexOfRegIntoTracked(reg) orelse return true;
|
||||
// return self.free_registers.isSet(index);
|
||||
// }
|
||||
|
||||
// /// Returns whether this register was allocated in the course
|
||||
// /// of this function.
|
||||
// ///
|
||||
// /// Returns false when this register is not tracked
|
||||
// pub fn isRegAllocated(self: Self, reg: Register) bool {
|
||||
// const index = indexOfRegIntoTracked(reg) orelse return false;
|
||||
// return self.allocated_registers.isSet(index);
|
||||
// }
|
||||
|
||||
// /// Returns whether this register is locked
|
||||
// ///
|
||||
// /// Returns false when this register is not tracked
|
||||
// pub fn isRegLocked(self: Self, reg: Register) bool {
|
||||
// const index = indexOfRegIntoTracked(reg) orelse return false;
|
||||
// return self.locked_registers.isSet(index);
|
||||
// }
|
||||
|
||||
// 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 index = indexOfRegIntoTracked(reg) orelse return null;
|
||||
// self.locked_registers.set(index);
|
||||
// return RegisterLock{ .register = reg };
|
||||
// }
|
||||
|
||||
// /// 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 index = indexOfRegIntoTracked(reg) orelse unreachable;
|
||||
// self.locked_registers.set(index);
|
||||
// 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 index = indexOfRegIntoTracked(lock.register) orelse return;
|
||||
// self.locked_registers.unset(index);
|
||||
// }
|
||||
|
||||
// /// Returns true when at least one register is locked
|
||||
// pub fn lockedRegsExist(self: Self) bool {
|
||||
// return self.locked_registers.count() > 0;
|
||||
// }
|
||||
|
||||
// /// Allocates a specified number of registers, optionally
|
||||
// /// tracking them. Returns `null` if not enough registers are
|
||||
// /// free.
|
||||
// pub fn tryAllocRegs(
|
||||
// self: *Self,
|
||||
// comptime count: comptime_int,
|
||||
// insts: [count]?Air.Inst.Index,
|
||||
// register_class: RegisterBitSet,
|
||||
// ) ?[count]Register {
|
||||
// comptime assert(count > 0 and count <= tracked_registers.len);
|
||||
|
||||
// var free_and_not_locked_registers = self.free_registers;
|
||||
// free_and_not_locked_registers.setIntersection(register_class);
|
||||
|
||||
// var unlocked_registers = self.locked_registers;
|
||||
// unlocked_registers.toggleAll();
|
||||
|
||||
// free_and_not_locked_registers.setIntersection(unlocked_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 (excludeRegister(reg, register_class)) continue;
|
||||
// if (self.isRegLocked(reg)) continue;
|
||||
// if (!self.isRegFree(reg)) continue;
|
||||
|
||||
// regs[i] = reg;
|
||||
// i += 1;
|
||||
// }
|
||||
// assert(i == count);
|
||||
|
||||
// for (regs) |reg, j| {
|
||||
// self.markRegAllocated(reg);
|
||||
|
||||
// if (insts[j]) |inst| {
|
||||
// // Track the register
|
||||
// const index = indexOfRegIntoTracked(reg).?; // indexOfReg() on a callee-preserved reg should never return null
|
||||
// self.registers[index] = inst;
|
||||
// self.markRegUsed(reg);
|
||||
// }
|
||||
// }
|
||||
|
||||
// return regs;
|
||||
// }
|
||||
|
||||
// /// Allocates a register and optionally tracks it with a
|
||||
// /// corresponding instruction. Returns `null` if all registers
|
||||
// /// are allocated.
|
||||
// pub fn tryAllocReg(self: *Self, inst: ?Air.Inst.Index, register_class: RegisterBitSet) ?Register {
|
||||
// return if (tryAllocRegs(self, 1, .{inst}, register_class)) |regs| regs[0] else null;
|
||||
// }
|
||||
|
||||
// /// Allocates a specified number of registers, optionally
|
||||
// /// tracking them. Asserts that count is not
|
||||
// /// larger than the total number of registers available.
|
||||
// pub fn allocRegs(
|
||||
// self: *Self,
|
||||
// comptime count: comptime_int,
|
||||
// insts: [count]?Air.Inst.Index,
|
||||
// register_class: RegisterBitSet,
|
||||
// ) AllocateRegistersError![count]Register {
|
||||
// comptime assert(count > 0 and count <= tracked_registers.len);
|
||||
|
||||
// var locked_registers = self.locked_registers;
|
||||
// locked_registers.setIntersection(register_class);
|
||||
|
||||
// if (count > register_class.count() - locked_registers.count()) return error.OutOfRegisters;
|
||||
|
||||
// const result = self.tryAllocRegs(count, insts, register_class) orelse blk: {
|
||||
// // We'll take over the first count registers. Spill
|
||||
// // the instructions that were previously there to a
|
||||
// // stack allocations.
|
||||
// var regs: [count]Register = undefined;
|
||||
// var i: usize = 0;
|
||||
// for (tracked_registers) |reg| {
|
||||
// if (i >= count) break;
|
||||
// if (excludeRegister(reg, register_class)) break;
|
||||
// if (self.isRegLocked(reg)) continue;
|
||||
|
||||
// regs[i] = reg;
|
||||
// self.markRegAllocated(reg);
|
||||
// const index = indexOfRegIntoTracked(reg).?; // indexOfReg() on a callee-preserved reg should never return null
|
||||
// if (insts[i]) |inst| {
|
||||
// // Track the register
|
||||
// if (self.isRegFree(reg)) {
|
||||
// self.markRegUsed(reg);
|
||||
// } else {
|
||||
// const spilled_inst = self.registers[index];
|
||||
// try self.getFunction().spillInstruction(reg, spilled_inst);
|
||||
// }
|
||||
// self.registers[index] = inst;
|
||||
// } else {
|
||||
// // Don't track the register
|
||||
// if (!self.isRegFree(reg)) {
|
||||
// const spilled_inst = self.registers[index];
|
||||
// try self.getFunction().spillInstruction(reg, spilled_inst);
|
||||
// self.freeReg(reg);
|
||||
// }
|
||||
// }
|
||||
|
||||
// i += 1;
|
||||
// }
|
||||
|
||||
// break :blk regs;
|
||||
// };
|
||||
|
||||
// log.debug("allocated registers {any} for insts {any}", .{ result, insts });
|
||||
// return result;
|
||||
// }
|
||||
|
||||
// /// Allocates a register and optionally tracks it with a
|
||||
// /// corresponding instruction.
|
||||
// pub fn allocReg(
|
||||
// self: *Self,
|
||||
// inst: ?Air.Inst.Index,
|
||||
// register_class: RegisterBitSet,
|
||||
// ) AllocateRegistersError!Register {
|
||||
// return (try self.allocRegs(1, .{inst}, register_class))[0];
|
||||
// }
|
||||
|
||||
// /// Spills the register if it is currently allocated. If a
|
||||
// /// corresponding instruction is passed, will also track this
|
||||
// /// register.
|
||||
// pub fn getReg(self: *Self, reg: Register, inst: ?Air.Inst.Index) AllocateRegistersError!void {
|
||||
// const index = indexOfRegIntoTracked(reg) orelse return;
|
||||
// log.debug("getReg {} for inst {}", .{ reg, inst });
|
||||
// self.markRegAllocated(reg);
|
||||
|
||||
// if (inst) |tracked_inst|
|
||||
// if (!self.isRegFree(reg)) {
|
||||
// // Move the instruction that was previously there to a
|
||||
// // stack allocation.
|
||||
// const spilled_inst = self.registers[index];
|
||||
// self.registers[index] = tracked_inst;
|
||||
// try self.getFunction().spillInstruction(reg, spilled_inst);
|
||||
// } else {
|
||||
// self.getRegAssumeFree(reg, tracked_inst);
|
||||
// }
|
||||
// else {
|
||||
// if (!self.isRegFree(reg)) {
|
||||
// // Move the instruction that was previously there to a
|
||||
// // stack allocation.
|
||||
// const spilled_inst = self.registers[index];
|
||||
// try self.getFunction().spillInstruction(reg, spilled_inst);
|
||||
// self.freeReg(reg);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// Allocates the specified register with the specified
|
||||
// /// instruction. Asserts that the register is free and no
|
||||
// /// spilling is necessary.
|
||||
// pub fn getRegAssumeFree(self: *Self, reg: Register, inst: Air.Inst.Index) void {
|
||||
// const index = indexOfRegIntoTracked(reg) orelse return;
|
||||
// log.debug("getRegAssumeFree {} for inst {}", .{ reg, inst });
|
||||
// self.markRegAllocated(reg);
|
||||
|
||||
// assert(self.isRegFree(reg));
|
||||
// self.registers[index] = inst;
|
||||
// self.markRegUsed(reg);
|
||||
// }
|
||||
|
||||
// /// Marks the specified register as free
|
||||
// pub fn freeReg(self: *Self, reg: Register) void {
|
||||
// const index = indexOfRegIntoTracked(reg) orelse return;
|
||||
// log.debug("freeing register {}", .{reg});
|
||||
|
||||
// self.registers[index] = undefined;
|
||||
// self.markRegFree(reg);
|
||||
// }
|
||||
// };
|
||||
//}
|
||||
|
||||
const MockRegister1 = enum(u2) {
|
||||
r0,
|
||||
r1,
|
||||
@ -361,11 +709,15 @@ const MockRegister2 = enum(u2) {
|
||||
fn MockFunction(comptime Register: type) type {
|
||||
return struct {
|
||||
allocator: Allocator,
|
||||
register_manager: RegisterManager(Self, Register, &Register.allocatable_registers) = .{},
|
||||
register_manager: RegisterManagerT = .{},
|
||||
spilled: std.ArrayListUnmanaged(Register) = .{},
|
||||
|
||||
const Self = @This();
|
||||
|
||||
const RegisterManagerT = RegisterManager(Self, Register, &Register.allocatable_registers);
|
||||
|
||||
pub const reg_class: RegisterManagerT.RegisterBitSet = math.maxInt(RegisterManagerT.RegisterBitSet);
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.spilled.deinit(self.allocator);
|
||||
}
|
||||
@ -410,10 +762,20 @@ test "tryAllocReg: no spilling" {
|
||||
defer function.deinit();
|
||||
|
||||
const mock_instruction: Air.Inst.Index = 1;
|
||||
const reg_class = MockFunction1.reg_class;
|
||||
|
||||
try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg(mock_instruction));
|
||||
try expectEqual(@as(?MockRegister1, .r3), function.register_manager.tryAllocReg(mock_instruction));
|
||||
try expectEqual(@as(?MockRegister1, null), function.register_manager.tryAllocReg(mock_instruction));
|
||||
try expectEqual(@as(?MockRegister1, .r2), function.register_manager.tryAllocReg(
|
||||
mock_instruction,
|
||||
reg_class,
|
||||
));
|
||||
try expectEqual(@as(?MockRegister1, .r3), function.register_manager.tryAllocReg(
|
||||
mock_instruction,
|
||||
reg_class,
|
||||
));
|
||||
try expectEqual(@as(?MockRegister1, null), function.register_manager.tryAllocReg(
|
||||
mock_instruction,
|
||||
reg_class,
|
||||
));
|
||||
|
||||
try expect(function.register_manager.isRegAllocated(.r2));
|
||||
try expect(function.register_manager.isRegAllocated(.r3));
|
||||
@ -438,17 +800,30 @@ test "allocReg: spilling" {
|
||||
defer function.deinit();
|
||||
|
||||
const mock_instruction: Air.Inst.Index = 1;
|
||||
const reg_class = MockFunction1.reg_class;
|
||||
|
||||
try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction));
|
||||
try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction));
|
||||
try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(
|
||||
mock_instruction,
|
||||
reg_class,
|
||||
));
|
||||
try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(
|
||||
mock_instruction,
|
||||
reg_class,
|
||||
));
|
||||
|
||||
// Spill a register
|
||||
try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(mock_instruction));
|
||||
try expectEqual(@as(?MockRegister1, .r2), try function.register_manager.allocReg(
|
||||
mock_instruction,
|
||||
reg_class,
|
||||
));
|
||||
try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items);
|
||||
|
||||
// No spilling necessary
|
||||
function.register_manager.freeReg(.r3);
|
||||
try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(mock_instruction));
|
||||
try expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(
|
||||
mock_instruction,
|
||||
reg_class,
|
||||
));
|
||||
try expectEqualSlices(MockRegister1, &[_]MockRegister1{.r2}, function.spilled.items);
|
||||
|
||||
// Locked registers
|
||||
@ -457,7 +832,10 @@ test "allocReg: spilling" {
|
||||
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 expectEqual(@as(?MockRegister1, .r3), try function.register_manager.allocReg(
|
||||
mock_instruction,
|
||||
reg_class,
|
||||
));
|
||||
}
|
||||
try expect(!function.register_manager.lockedRegsExist());
|
||||
}
|
||||
@ -470,7 +848,13 @@ test "tryAllocRegs" {
|
||||
};
|
||||
defer function.deinit();
|
||||
|
||||
try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs(3, .{ null, null, null }).?);
|
||||
const reg_class = MockFunction2.reg_class;
|
||||
|
||||
try expectEqual([_]MockRegister2{ .r0, .r1, .r2 }, function.register_manager.tryAllocRegs(
|
||||
3,
|
||||
.{ null, null, null },
|
||||
reg_class,
|
||||
).?);
|
||||
|
||||
try expect(function.register_manager.isRegAllocated(.r0));
|
||||
try expect(function.register_manager.isRegAllocated(.r1));
|
||||
@ -485,7 +869,11 @@ test "tryAllocRegs" {
|
||||
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 expectEqual([_]MockRegister2{ .r0, .r2, .r3 }, function.register_manager.tryAllocRegs(
|
||||
3,
|
||||
.{ null, null, null },
|
||||
reg_class,
|
||||
).?);
|
||||
}
|
||||
try expect(!function.register_manager.lockedRegsExist());
|
||||
|
||||
@ -505,6 +893,8 @@ test "allocRegs: normal usage" {
|
||||
};
|
||||
defer function.deinit();
|
||||
|
||||
const reg_class = MockFunction2.reg_class;
|
||||
|
||||
{
|
||||
const result_reg: MockRegister2 = .r1;
|
||||
|
||||
@ -524,7 +914,7 @@ test "allocRegs: normal usage" {
|
||||
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 });
|
||||
const regs = try function.register_manager.allocRegs(2, .{ null, null }, reg_class);
|
||||
try function.genAdd(result_reg, regs[0], regs[1]);
|
||||
}
|
||||
}
|
||||
@ -539,6 +929,8 @@ test "allocRegs: selectively reducing register pressure" {
|
||||
};
|
||||
defer function.deinit();
|
||||
|
||||
const reg_class = MockFunction2.reg_class;
|
||||
|
||||
{
|
||||
const result_reg: MockRegister2 = .r1;
|
||||
|
||||
@ -546,12 +938,12 @@ test "allocRegs: selectively reducing register pressure" {
|
||||
|
||||
// Here, we don't defer unlock because we manually unlock
|
||||
// after genAdd
|
||||
const regs = try function.register_manager.allocRegs(2, .{ null, null });
|
||||
const regs = try function.register_manager.allocRegs(2, .{ null, null }, reg_class);
|
||||
|
||||
try function.genAdd(result_reg, regs[0], regs[1]);
|
||||
function.register_manager.unlockReg(lock.?);
|
||||
|
||||
const extra_summand_reg = try function.register_manager.allocReg(null);
|
||||
const extra_summand_reg = try function.register_manager.allocReg(null, reg_class);
|
||||
try function.genAdd(result_reg, result_reg, extra_summand_reg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -402,6 +402,7 @@ fn testPointerToVoidReturnType2() *const void {
|
||||
test "array 2D const double ptr" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
||||
|
||||
const rect_2d_vertexes = [_][1]f32{
|
||||
@ -414,6 +415,7 @@ test "array 2D const double ptr" {
|
||||
test "array 2D const double ptr with offset" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
|
||||
|
||||
const rect_2d_vertexes = [_][2]f32{
|
||||
@ -426,6 +428,7 @@ test "array 2D const double ptr with offset" {
|
||||
test "array 3D const double ptr with offset" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
||||
|
||||
const rect_3d_vertexes = [_][2][2]f32{
|
||||
|
||||
@ -198,7 +198,6 @@ test "const number literal" {
|
||||
const ten = 10;
|
||||
|
||||
test "float equality" {
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
|
||||
|
||||
@ -882,6 +882,7 @@ test "extern union doesn't trigger field check at comptime" {
|
||||
test "anonymous union literal syntax" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
|
||||
const S = struct {
|
||||
const Number = union {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user