Skip to content

Commit

Permalink
Merge pull request #22932 from jacobly0/x86_64-rewrite
Browse files Browse the repository at this point in the history
x86_64: start rewriting bit counting operations
  • Loading branch information
andrewrk authored Feb 19, 2025
2 parents 0cf6ae2 + 7d70d7b commit bd237bc
Show file tree
Hide file tree
Showing 4 changed files with 1,543 additions and 6 deletions.
4 changes: 2 additions & 2 deletions lib/std/math/big/int.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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);
Expand Down
224 changes: 224 additions & 0 deletions lib/std/math/big/int_test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Loading

0 comments on commit bd237bc

Please sign in to comment.