Merge pull request #22932 from jacobly0/x86_64-rewrite

x86_64: start rewriting bit counting operations
This commit is contained in:
Andrew Kelley 2025-02-19 11:37:40 -08:00 committed by GitHub
commit bd237bced4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 1543 additions and 6 deletions

View File

@ -2544,8 +2544,7 @@ pub const Const = struct {
const bits_per_limb = @bitSizeOf(Limb);
while (i != 0) {
i -= 1;
const limb = a.limbs[i];
const this_limb_lz = @clz(limb);
const this_limb_lz = @clz(a.limbs[i]);
total_limb_lz += this_limb_lz;
if (this_limb_lz != bits_per_limb) break;
}
@ -2557,6 +2556,7 @@ pub const Const = struct {
pub fn ctz(a: Const, bits: Limb) Limb {
// Limbs are stored in little-endian order. Converting a negative number to twos-complement
// flips all bits above the lowest set bit, which does not affect the trailing zero count.
if (a.eqlZero()) return bits;
var result: Limb = 0;
for (a.limbs) |limb| {
const limb_tz = @ctz(limb);

View File

@ -3332,3 +3332,227 @@ test "(BigInt) negative" {
try testing.expect(mem.eql(u8, a_fmt, "(BigInt)"));
try testing.expect(!mem.eql(u8, b_fmt, "(BigInt)"));
}
test "clz" {
const neg_limb_max_squared: std.math.big.int.Const = .{
.limbs = &.{ 1, maxInt(Limb) - 1 },
.positive = false,
};
try testing.expect(neg_limb_max_squared.clz(@bitSizeOf(Limb) * 2 + 1) == 0);
const neg_limb_max_squared_plus_one: std.math.big.int.Const = .{
.limbs = &.{ 0, maxInt(Limb) - 1 },
.positive = false,
};
try testing.expect(neg_limb_max_squared_plus_one.clz(@bitSizeOf(Limb) * 2 + 1) == 0);
const neg_limb_msb_squared: std.math.big.int.Const = .{
.limbs = &.{ 0, 1 << @bitSizeOf(Limb) - 2 },
.positive = false,
};
try testing.expect(neg_limb_msb_squared.clz(@bitSizeOf(Limb) * 2) == 0);
try testing.expect(neg_limb_msb_squared.clz(@bitSizeOf(Limb) * 2 + 1) == 0);
const neg_limb_max: std.math.big.int.Const = .{
.limbs = &.{maxInt(Limb)},
.positive = false,
};
try testing.expect(neg_limb_max.clz(@bitSizeOf(Limb) + 1) == 0);
try testing.expect(neg_limb_max.clz(@bitSizeOf(Limb) * 2 - 1) == 0);
try testing.expect(neg_limb_max.clz(@bitSizeOf(Limb) * 2) == 0);
try testing.expect(neg_limb_max.clz(@bitSizeOf(Limb) * 2 + 1) == 0);
const neg_limb_msb: std.math.big.int.Const = .{
.limbs = &.{1 << @bitSizeOf(Limb) - 1},
.positive = false,
};
try testing.expect(neg_limb_msb.clz(@bitSizeOf(Limb)) == 0);
try testing.expect(neg_limb_msb.clz(@bitSizeOf(Limb) + 1) == 0);
try testing.expect(neg_limb_msb.clz(@bitSizeOf(Limb) * 2 - 1) == 0);
try testing.expect(neg_limb_msb.clz(@bitSizeOf(Limb) * 2) == 0);
try testing.expect(neg_limb_msb.clz(@bitSizeOf(Limb) * 2 + 1) == 0);
const neg_one: std.math.big.int.Const = .{
.limbs = &.{1},
.positive = false,
};
try testing.expect(neg_one.clz(@bitSizeOf(Limb)) == 0);
try testing.expect(neg_one.clz(@bitSizeOf(Limb) + 1) == 0);
try testing.expect(neg_one.clz(@bitSizeOf(Limb) * 2 - 1) == 0);
try testing.expect(neg_one.clz(@bitSizeOf(Limb) * 2) == 0);
try testing.expect(neg_one.clz(@bitSizeOf(Limb) * 2 + 1) == 0);
const zero: std.math.big.int.Const = .{
.limbs = &.{0},
.positive = true,
};
try testing.expect(zero.clz(@bitSizeOf(Limb)) == @bitSizeOf(Limb));
try testing.expect(zero.clz(@bitSizeOf(Limb) + 1) == @bitSizeOf(Limb) + 1);
try testing.expect(zero.clz(@bitSizeOf(Limb) * 2 - 1) == @bitSizeOf(Limb) * 2 - 1);
try testing.expect(zero.clz(@bitSizeOf(Limb) * 2) == @bitSizeOf(Limb) * 2);
try testing.expect(zero.clz(@bitSizeOf(Limb) * 2 + 1) == @bitSizeOf(Limb) * 2 + 1);
const one: std.math.big.int.Const = .{
.limbs = &.{1},
.positive = true,
};
try testing.expect(one.clz(@bitSizeOf(Limb)) == @bitSizeOf(Limb) - 1);
try testing.expect(one.clz(@bitSizeOf(Limb) + 1) == @bitSizeOf(Limb));
try testing.expect(one.clz(@bitSizeOf(Limb) * 2 - 1) == @bitSizeOf(Limb) * 2 - 2);
try testing.expect(one.clz(@bitSizeOf(Limb) * 2) == @bitSizeOf(Limb) * 2 - 1);
try testing.expect(one.clz(@bitSizeOf(Limb) * 2 + 1) == @bitSizeOf(Limb) * 2);
const limb_msb: std.math.big.int.Const = .{
.limbs = &.{1 << @bitSizeOf(Limb) - 1},
.positive = true,
};
try testing.expect(limb_msb.clz(@bitSizeOf(Limb)) == 0);
try testing.expect(limb_msb.clz(@bitSizeOf(Limb) + 1) == 1);
try testing.expect(limb_msb.clz(@bitSizeOf(Limb) * 2 - 1) == @bitSizeOf(Limb) - 1);
try testing.expect(limb_msb.clz(@bitSizeOf(Limb) * 2) == @bitSizeOf(Limb));
try testing.expect(limb_msb.clz(@bitSizeOf(Limb) * 2 + 1) == @bitSizeOf(Limb) + 1);
const limb_max: std.math.big.int.Const = .{
.limbs = &.{maxInt(Limb)},
.positive = true,
};
try testing.expect(limb_max.clz(@bitSizeOf(Limb)) == 0);
try testing.expect(limb_max.clz(@bitSizeOf(Limb) + 1) == 1);
try testing.expect(limb_max.clz(@bitSizeOf(Limb) * 2 - 1) == @bitSizeOf(Limb) - 1);
try testing.expect(limb_max.clz(@bitSizeOf(Limb) * 2) == @bitSizeOf(Limb));
try testing.expect(limb_max.clz(@bitSizeOf(Limb) * 2 + 1) == @bitSizeOf(Limb) + 1);
const limb_msb_squared: std.math.big.int.Const = .{
.limbs = &.{ 0, 1 << @bitSizeOf(Limb) - 2 },
.positive = true,
};
try testing.expect(limb_msb_squared.clz(@bitSizeOf(Limb) * 2 - 1) == 0);
try testing.expect(limb_msb_squared.clz(@bitSizeOf(Limb) * 2) == 1);
try testing.expect(limb_msb_squared.clz(@bitSizeOf(Limb) * 2 + 1) == 2);
const limb_max_squared_minus_one: std.math.big.int.Const = .{
.limbs = &.{ 0, maxInt(Limb) - 1 },
.positive = true,
};
try testing.expect(limb_max_squared_minus_one.clz(@bitSizeOf(Limb) * 2) == 0);
try testing.expect(limb_max_squared_minus_one.clz(@bitSizeOf(Limb) * 2 + 1) == 1);
const limb_max_squared: std.math.big.int.Const = .{
.limbs = &.{ 1, maxInt(Limb) - 1 },
.positive = true,
};
try testing.expect(limb_max_squared.clz(@bitSizeOf(Limb) * 2) == 0);
try testing.expect(limb_max_squared.clz(@bitSizeOf(Limb) * 2 + 1) == 1);
}
test "ctz" {
const neg_limb_max_squared: std.math.big.int.Const = .{
.limbs = &.{ 1, maxInt(Limb) - 1 },
.positive = false,
};
try testing.expect(neg_limb_max_squared.ctz(@bitSizeOf(Limb) * 2 + 1) == 0);
const neg_limb_max_squared_plus_one: std.math.big.int.Const = .{
.limbs = &.{ 0, maxInt(Limb) - 1 },
.positive = false,
};
try testing.expect(neg_limb_max_squared_plus_one.ctz(@bitSizeOf(Limb) * 2 + 1) == @bitSizeOf(Limb) + 1);
const neg_limb_msb_squared: std.math.big.int.Const = .{
.limbs = &.{ 0, 1 << @bitSizeOf(Limb) - 2 },
.positive = false,
};
try testing.expect(neg_limb_msb_squared.ctz(@bitSizeOf(Limb) * 2) == @bitSizeOf(Limb) * 2 - 2);
try testing.expect(neg_limb_msb_squared.ctz(@bitSizeOf(Limb) * 2 + 1) == @bitSizeOf(Limb) * 2 - 2);
const neg_limb_max: std.math.big.int.Const = .{
.limbs = &.{maxInt(Limb)},
.positive = false,
};
try testing.expect(neg_limb_max.ctz(@bitSizeOf(Limb) + 1) == 0);
try testing.expect(neg_limb_max.ctz(@bitSizeOf(Limb) * 2 - 1) == 0);
try testing.expect(neg_limb_max.ctz(@bitSizeOf(Limb) * 2) == 0);
try testing.expect(neg_limb_max.ctz(@bitSizeOf(Limb) * 2 + 1) == 0);
const neg_limb_msb: std.math.big.int.Const = .{
.limbs = &.{1 << @bitSizeOf(Limb) - 1},
.positive = false,
};
try testing.expect(neg_limb_msb.ctz(@bitSizeOf(Limb)) == @bitSizeOf(Limb) - 1);
try testing.expect(neg_limb_msb.ctz(@bitSizeOf(Limb) + 1) == @bitSizeOf(Limb) - 1);
try testing.expect(neg_limb_msb.ctz(@bitSizeOf(Limb) * 2 - 1) == @bitSizeOf(Limb) - 1);
try testing.expect(neg_limb_msb.ctz(@bitSizeOf(Limb) * 2) == @bitSizeOf(Limb) - 1);
try testing.expect(neg_limb_msb.ctz(@bitSizeOf(Limb) * 2 + 1) == @bitSizeOf(Limb) - 1);
const neg_one: std.math.big.int.Const = .{
.limbs = &.{1},
.positive = false,
};
try testing.expect(neg_one.ctz(@bitSizeOf(Limb)) == 0);
try testing.expect(neg_one.ctz(@bitSizeOf(Limb) + 1) == 0);
try testing.expect(neg_one.ctz(@bitSizeOf(Limb) * 2 - 1) == 0);
try testing.expect(neg_one.ctz(@bitSizeOf(Limb) * 2) == 0);
try testing.expect(neg_one.ctz(@bitSizeOf(Limb) * 2 + 1) == 0);
const zero: std.math.big.int.Const = .{
.limbs = &.{0},
.positive = true,
};
try testing.expect(zero.ctz(@bitSizeOf(Limb)) == @bitSizeOf(Limb));
try testing.expect(zero.ctz(@bitSizeOf(Limb) + 1) == @bitSizeOf(Limb) + 1);
try testing.expect(zero.ctz(@bitSizeOf(Limb) * 2 - 1) == @bitSizeOf(Limb) * 2 - 1);
try testing.expect(zero.ctz(@bitSizeOf(Limb) * 2) == @bitSizeOf(Limb) * 2);
try testing.expect(zero.ctz(@bitSizeOf(Limb) * 2 + 1) == @bitSizeOf(Limb) * 2 + 1);
const one: std.math.big.int.Const = .{
.limbs = &.{1},
.positive = true,
};
try testing.expect(one.ctz(@bitSizeOf(Limb)) == 0);
try testing.expect(one.ctz(@bitSizeOf(Limb) + 1) == 0);
try testing.expect(one.ctz(@bitSizeOf(Limb) * 2 - 1) == 0);
try testing.expect(one.ctz(@bitSizeOf(Limb) * 2) == 0);
try testing.expect(one.ctz(@bitSizeOf(Limb) * 2 + 1) == 0);
const limb_msb: std.math.big.int.Const = .{
.limbs = &.{1 << @bitSizeOf(Limb) - 1},
.positive = true,
};
try testing.expect(limb_msb.ctz(@bitSizeOf(Limb)) == @bitSizeOf(Limb) - 1);
try testing.expect(limb_msb.ctz(@bitSizeOf(Limb) + 1) == @bitSizeOf(Limb) - 1);
try testing.expect(limb_msb.ctz(@bitSizeOf(Limb) * 2 - 1) == @bitSizeOf(Limb) - 1);
try testing.expect(limb_msb.ctz(@bitSizeOf(Limb) * 2) == @bitSizeOf(Limb) - 1);
try testing.expect(limb_msb.ctz(@bitSizeOf(Limb) * 2 + 1) == @bitSizeOf(Limb) - 1);
const limb_max: std.math.big.int.Const = .{
.limbs = &.{maxInt(Limb)},
.positive = true,
};
try testing.expect(limb_max.ctz(@bitSizeOf(Limb)) == 0);
try testing.expect(limb_max.ctz(@bitSizeOf(Limb) + 1) == 0);
try testing.expect(limb_max.ctz(@bitSizeOf(Limb) * 2 - 1) == 0);
try testing.expect(limb_max.ctz(@bitSizeOf(Limb) * 2) == 0);
try testing.expect(limb_max.ctz(@bitSizeOf(Limb) * 2 + 1) == 0);
const limb_msb_squared: std.math.big.int.Const = .{
.limbs = &.{ 0, 1 << @bitSizeOf(Limb) - 2 },
.positive = true,
};
try testing.expect(limb_msb_squared.ctz(@bitSizeOf(Limb) * 2 - 1) == @bitSizeOf(Limb) * 2 - 2);
try testing.expect(limb_msb_squared.ctz(@bitSizeOf(Limb) * 2) == @bitSizeOf(Limb) * 2 - 2);
try testing.expect(limb_msb_squared.ctz(@bitSizeOf(Limb) * 2 + 1) == @bitSizeOf(Limb) * 2 - 2);
const limb_max_squared_minus_one: std.math.big.int.Const = .{
.limbs = &.{ 0, maxInt(Limb) - 1 },
.positive = true,
};
try testing.expect(limb_max_squared_minus_one.ctz(@bitSizeOf(Limb) * 2) == @bitSizeOf(Limb) + 1);
try testing.expect(limb_max_squared_minus_one.ctz(@bitSizeOf(Limb) * 2 + 1) == @bitSizeOf(Limb) + 1);
const limb_max_squared: std.math.big.int.Const = .{
.limbs = &.{ 1, maxInt(Limb) - 1 },
.positive = true,
};
try testing.expect(limb_max_squared.ctz(@bitSizeOf(Limb) * 2) == 0);
try testing.expect(limb_max_squared.ctz(@bitSizeOf(Limb) * 2 + 1) == 0);
}

File diff suppressed because it is too large Load Diff

View File

@ -19273,6 +19273,22 @@ test clz {
try test_clz.testIntVectors();
}
inline fn ctz(comptime Type: type, rhs: Type) @TypeOf(@ctz(rhs)) {
return @ctz(rhs);
}
test ctz {
const test_ctz = unary(ctz, .{});
try test_ctz.testInts();
}
inline fn popCount(comptime Type: type, rhs: Type) @TypeOf(@popCount(rhs)) {
return @popCount(rhs);
}
test popCount {
const test_pop_count = unary(popCount, .{});
try test_pop_count.testInts();
}
inline fn byteSwap(comptime Type: type, rhs: Type) RoundBitsUp(Type, 8) {
return @byteSwap(@as(RoundBitsUp(Type, 8), rhs));
}