x86_64: implement integer @divFloor and @mod

Closes #24039
This commit is contained in:
Jacob Young 2025-06-02 01:18:21 -04:00 committed by Jacob Young
parent f4f4460e17
commit 2543e2d97c
7 changed files with 1024 additions and 214 deletions

File diff suppressed because it is too large Load diff

View file

@ -575,22 +575,22 @@ const RegisterBitSet = RegisterManager.RegisterBitSet;
pub const RegisterClass = struct {
pub const gp: RegisterBitSet = blk: {
var set = RegisterBitSet.initEmpty();
for (allocatable_regs, 0..) |reg, index| if (reg.class() == .general_purpose) set.set(index);
for (allocatable_regs, 0..) |reg, index| if (reg.isClass(.general_purpose)) set.set(index);
break :blk set;
};
pub const gphi: RegisterBitSet = blk: {
var set = RegisterBitSet.initEmpty();
for (allocatable_regs, 0..) |reg, index| if (reg.hasHi8()) set.set(index);
for (allocatable_regs, 0..) |reg, index| if (reg.isClass(.gphi)) set.set(index);
break :blk set;
};
pub const x87: RegisterBitSet = blk: {
var set = RegisterBitSet.initEmpty();
for (allocatable_regs, 0..) |reg, index| if (reg.class() == .x87) set.set(index);
for (allocatable_regs, 0..) |reg, index| if (reg.isClass(.x87)) set.set(index);
break :blk set;
};
pub const sse: RegisterBitSet = blk: {
var set = RegisterBitSet.initEmpty();
for (allocatable_regs, 0..) |reg, index| if (reg.class() == .sse) set.set(index);
for (allocatable_regs, 0..) |reg, index| if (reg.isClass(.sse)) set.set(index);
break :blk set;
};
};

View file

@ -429,6 +429,16 @@ pub const Register = enum(u8) {
};
}
pub inline fn isClass(reg: Register, rc: Class) bool {
switch (rc) {
else => return reg.class() == rc,
.gphi => {
const reg_id = reg.id();
return (reg_id >= comptime Register.ah.id()) and reg_id <= comptime Register.bh.id();
},
}
}
pub fn id(reg: Register) u7 {
const base = switch (@intFromEnum(reg)) {
// zig fmt: off
@ -615,22 +625,17 @@ pub const Register = enum(u8) {
}
pub fn toHi8(reg: Register) Register {
assert(reg.hasHi8());
assert(reg.isClass(.gphi));
return @enumFromInt(@intFromEnum(reg) - reg.gpBase() + @intFromEnum(Register.ah));
}
pub fn hasHi8(reg: Register) bool {
const reg_id = reg.id();
return (reg_id >= comptime Register.ah.id()) and reg_id <= comptime Register.bh.id();
}
pub fn to80(reg: Register) Register {
assert(reg.class() == .x87);
assert(reg.isClass(.x87));
return reg;
}
fn sseBase(reg: Register) u8 {
assert(reg.class() == .sse);
assert(reg.isClass(.sse));
return switch (@intFromEnum(reg)) {
@intFromEnum(Register.zmm0)...@intFromEnum(Register.zmm31) => @intFromEnum(Register.zmm0),
@intFromEnum(Register.ymm0)...@intFromEnum(Register.ymm31) => @intFromEnum(Register.ymm0),
@ -694,11 +699,13 @@ test "Register enc - different classes" {
}
test "Register classes" {
try expect(Register.r11.class() == .general_purpose);
try expect(Register.ymm11.class() == .sse);
try expect(Register.mm3.class() == .mmx);
try expect(Register.st3.class() == .x87);
try expect(Register.fs.class() == .segment);
try expect(Register.r11.isClass(.general_purpose));
try expect(Register.rdx.isClass(.gphi));
try expect(!Register.dil.isClass(.gphi));
try expect(Register.ymm11.isClass(.sse));
try expect(Register.mm3.isClass(.mmx));
try expect(Register.st3.isClass(.x87));
try expect(Register.fs.isClass(.segment));
}
pub const FrameIndex = enum(u32) {

View file

@ -111,7 +111,7 @@ pub const Instruction = struct {
};
pub fn initMoffs(reg: Register, offset: u64) Memory {
assert(reg.class() == .segment);
assert(reg.isClass(.segment));
return .{ .moffs = .{ .seg = reg, .offset = offset } };
}
@ -139,7 +139,7 @@ pub const Instruction = struct {
.rip => false,
.sib => |s| switch (s.base) {
.none, .frame, .table, .reloc, .rip_inst => false,
.reg => |reg| reg.class() == .segment,
.reg => |reg| reg.isClass(.segment),
},
};
}
@ -199,7 +199,7 @@ pub const Instruction = struct {
pub fn isSegmentRegister(op: Operand) bool {
return switch (op) {
.none => unreachable,
.reg => |reg| reg.class() == .segment,
.reg => |reg| reg.isClass(.segment),
.mem => |mem| mem.isSegmentRegister(),
.imm => unreachable,
.bytes => unreachable,
@ -776,7 +776,7 @@ pub const LegacyPrefixes = packed struct {
padding: u5 = 0,
pub fn setSegmentOverride(self: *LegacyPrefixes, reg: Register) void {
assert(reg.class() == .segment);
assert(reg.isClass(.segment));
switch (reg) {
.cs => self.prefix_2e = true,
.ss => self.prefix_36 = true,
@ -2457,7 +2457,7 @@ const Assembler = struct {
.general_purpose, .segment => {
const tok = try as.expect(.string);
const base = registerFromString(as.source(tok)) orelse return error.InvalidMemoryOperand;
if (base.class() != cond) return error.InvalidMemoryOperand;
if (!base.isClass(cond)) return error.InvalidMemoryOperand;
res.base = base;
},
.rip => {
@ -2498,7 +2498,7 @@ const Assembler = struct {
error.Overflow => {
if (is_neg) return err;
if (res.base) |base| {
if (base.class() != .segment) return err;
if (!base.isClass(.segment)) return err;
}
const offset = try std.fmt.parseInt(u64, as.source(tok), 0);
res.offset = offset;

View file

@ -1893,13 +1893,13 @@ test "float divide by zero" {
test "partially-runtime integer vector division would be illegal if vector elements were reordered" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
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
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .hexagon) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
var lhs: @Vector(2, i8) = .{ -128, 5 };
const rhs: @Vector(2, i8) = .{ 1, -1 };

View file

@ -559,12 +559,12 @@ test "vector comparison operators" {
test "vector division operators" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
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
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
const S = struct {
fn doTheTestDiv(comptime T: type, x: @Vector(4, T), y: @Vector(4, T)) !void {

View file

@ -5181,6 +5181,8 @@ inline fn divFloor(comptime Type: type, lhs: Type, rhs: Type) @TypeOf(@divFloor(
}
test divFloor {
const test_div_floor = binary(divFloor, .{ .compare = .approx_int });
try test_div_floor.testInts();
try test_div_floor.testIntVectors();
try test_div_floor.testFloats();
try test_div_floor.testFloatVectors();
}
@ -5198,7 +5200,7 @@ test rem {
inline fn mod(comptime Type: type, lhs: Type, rhs: Type) @TypeOf(@mod(lhs, rhs)) {
// workaround llvm backend bugs
if (@inComptime()) {
if (@inComptime() and @typeInfo(Scalar(Type)) == .float) {
const scalarMod = struct {
fn scalarMod(scalar_lhs: Scalar(Type), scalar_rhs: Scalar(Type)) Scalar(Type) {
const scalar_rem = @rem(scalar_lhs, scalar_rhs);
@ -5218,6 +5220,8 @@ inline fn mod(comptime Type: type, lhs: Type, rhs: Type) @TypeOf(@mod(lhs, rhs))
}
test mod {
const test_mod = binary(mod, .{});
try test_mod.testInts();
try test_mod.testIntVectors();
try test_mod.testFloats();
try test_mod.testFloatVectors();
}