Skip to content

Commit bd237bc

Browse files
authored
Merge pull request #22932 from jacobly0/x86_64-rewrite
x86_64: start rewriting bit counting operations
2 parents 0cf6ae2 + 7d70d7b commit bd237bc

File tree

4 files changed

+1543
-6
lines changed

4 files changed

+1543
-6
lines changed

lib/std/math/big/int.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2544,8 +2544,7 @@ pub const Const = struct {
25442544
const bits_per_limb = @bitSizeOf(Limb);
25452545
while (i != 0) {
25462546
i -= 1;
2547-
const limb = a.limbs[i];
2548-
const this_limb_lz = @clz(limb);
2547+
const this_limb_lz = @clz(a.limbs[i]);
25492548
total_limb_lz += this_limb_lz;
25502549
if (this_limb_lz != bits_per_limb) break;
25512550
}
@@ -2557,6 +2556,7 @@ pub const Const = struct {
25572556
pub fn ctz(a: Const, bits: Limb) Limb {
25582557
// Limbs are stored in little-endian order. Converting a negative number to twos-complement
25592558
// flips all bits above the lowest set bit, which does not affect the trailing zero count.
2559+
if (a.eqlZero()) return bits;
25602560
var result: Limb = 0;
25612561
for (a.limbs) |limb| {
25622562
const limb_tz = @ctz(limb);

lib/std/math/big/int_test.zig

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3332,3 +3332,227 @@ test "(BigInt) negative" {
33323332
try testing.expect(mem.eql(u8, a_fmt, "(BigInt)"));
33333333
try testing.expect(!mem.eql(u8, b_fmt, "(BigInt)"));
33343334
}
3335+
3336+
test "clz" {
3337+
const neg_limb_max_squared: std.math.big.int.Const = .{
3338+
.limbs = &.{ 1, maxInt(Limb) - 1 },
3339+
.positive = false,
3340+
};
3341+
try testing.expect(neg_limb_max_squared.clz(@bitSizeOf(Limb) * 2 + 1) == 0);
3342+
3343+
const neg_limb_max_squared_plus_one: std.math.big.int.Const = .{
3344+
.limbs = &.{ 0, maxInt(Limb) - 1 },
3345+
.positive = false,
3346+
};
3347+
try testing.expect(neg_limb_max_squared_plus_one.clz(@bitSizeOf(Limb) * 2 + 1) == 0);
3348+
3349+
const neg_limb_msb_squared: std.math.big.int.Const = .{
3350+
.limbs = &.{ 0, 1 << @bitSizeOf(Limb) - 2 },
3351+
.positive = false,
3352+
};
3353+
try testing.expect(neg_limb_msb_squared.clz(@bitSizeOf(Limb) * 2) == 0);
3354+
try testing.expect(neg_limb_msb_squared.clz(@bitSizeOf(Limb) * 2 + 1) == 0);
3355+
3356+
const neg_limb_max: std.math.big.int.Const = .{
3357+
.limbs = &.{maxInt(Limb)},
3358+
.positive = false,
3359+
};
3360+
try testing.expect(neg_limb_max.clz(@bitSizeOf(Limb) + 1) == 0);
3361+
try testing.expect(neg_limb_max.clz(@bitSizeOf(Limb) * 2 - 1) == 0);
3362+
try testing.expect(neg_limb_max.clz(@bitSizeOf(Limb) * 2) == 0);
3363+
try testing.expect(neg_limb_max.clz(@bitSizeOf(Limb) * 2 + 1) == 0);
3364+
3365+
const neg_limb_msb: std.math.big.int.Const = .{
3366+
.limbs = &.{1 << @bitSizeOf(Limb) - 1},
3367+
.positive = false,
3368+
};
3369+
try testing.expect(neg_limb_msb.clz(@bitSizeOf(Limb)) == 0);
3370+
try testing.expect(neg_limb_msb.clz(@bitSizeOf(Limb) + 1) == 0);
3371+
try testing.expect(neg_limb_msb.clz(@bitSizeOf(Limb) * 2 - 1) == 0);
3372+
try testing.expect(neg_limb_msb.clz(@bitSizeOf(Limb) * 2) == 0);
3373+
try testing.expect(neg_limb_msb.clz(@bitSizeOf(Limb) * 2 + 1) == 0);
3374+
3375+
const neg_one: std.math.big.int.Const = .{
3376+
.limbs = &.{1},
3377+
.positive = false,
3378+
};
3379+
try testing.expect(neg_one.clz(@bitSizeOf(Limb)) == 0);
3380+
try testing.expect(neg_one.clz(@bitSizeOf(Limb) + 1) == 0);
3381+
try testing.expect(neg_one.clz(@bitSizeOf(Limb) * 2 - 1) == 0);
3382+
try testing.expect(neg_one.clz(@bitSizeOf(Limb) * 2) == 0);
3383+
try testing.expect(neg_one.clz(@bitSizeOf(Limb) * 2 + 1) == 0);
3384+
3385+
const zero: std.math.big.int.Const = .{
3386+
.limbs = &.{0},
3387+
.positive = true,
3388+
};
3389+
try testing.expect(zero.clz(@bitSizeOf(Limb)) == @bitSizeOf(Limb));
3390+
try testing.expect(zero.clz(@bitSizeOf(Limb) + 1) == @bitSizeOf(Limb) + 1);
3391+
try testing.expect(zero.clz(@bitSizeOf(Limb) * 2 - 1) == @bitSizeOf(Limb) * 2 - 1);
3392+
try testing.expect(zero.clz(@bitSizeOf(Limb) * 2) == @bitSizeOf(Limb) * 2);
3393+
try testing.expect(zero.clz(@bitSizeOf(Limb) * 2 + 1) == @bitSizeOf(Limb) * 2 + 1);
3394+
3395+
const one: std.math.big.int.Const = .{
3396+
.limbs = &.{1},
3397+
.positive = true,
3398+
};
3399+
try testing.expect(one.clz(@bitSizeOf(Limb)) == @bitSizeOf(Limb) - 1);
3400+
try testing.expect(one.clz(@bitSizeOf(Limb) + 1) == @bitSizeOf(Limb));
3401+
try testing.expect(one.clz(@bitSizeOf(Limb) * 2 - 1) == @bitSizeOf(Limb) * 2 - 2);
3402+
try testing.expect(one.clz(@bitSizeOf(Limb) * 2) == @bitSizeOf(Limb) * 2 - 1);
3403+
try testing.expect(one.clz(@bitSizeOf(Limb) * 2 + 1) == @bitSizeOf(Limb) * 2);
3404+
3405+
const limb_msb: std.math.big.int.Const = .{
3406+
.limbs = &.{1 << @bitSizeOf(Limb) - 1},
3407+
.positive = true,
3408+
};
3409+
try testing.expect(limb_msb.clz(@bitSizeOf(Limb)) == 0);
3410+
try testing.expect(limb_msb.clz(@bitSizeOf(Limb) + 1) == 1);
3411+
try testing.expect(limb_msb.clz(@bitSizeOf(Limb) * 2 - 1) == @bitSizeOf(Limb) - 1);
3412+
try testing.expect(limb_msb.clz(@bitSizeOf(Limb) * 2) == @bitSizeOf(Limb));
3413+
try testing.expect(limb_msb.clz(@bitSizeOf(Limb) * 2 + 1) == @bitSizeOf(Limb) + 1);
3414+
3415+
const limb_max: std.math.big.int.Const = .{
3416+
.limbs = &.{maxInt(Limb)},
3417+
.positive = true,
3418+
};
3419+
try testing.expect(limb_max.clz(@bitSizeOf(Limb)) == 0);
3420+
try testing.expect(limb_max.clz(@bitSizeOf(Limb) + 1) == 1);
3421+
try testing.expect(limb_max.clz(@bitSizeOf(Limb) * 2 - 1) == @bitSizeOf(Limb) - 1);
3422+
try testing.expect(limb_max.clz(@bitSizeOf(Limb) * 2) == @bitSizeOf(Limb));
3423+
try testing.expect(limb_max.clz(@bitSizeOf(Limb) * 2 + 1) == @bitSizeOf(Limb) + 1);
3424+
3425+
const limb_msb_squared: std.math.big.int.Const = .{
3426+
.limbs = &.{ 0, 1 << @bitSizeOf(Limb) - 2 },
3427+
.positive = true,
3428+
};
3429+
try testing.expect(limb_msb_squared.clz(@bitSizeOf(Limb) * 2 - 1) == 0);
3430+
try testing.expect(limb_msb_squared.clz(@bitSizeOf(Limb) * 2) == 1);
3431+
try testing.expect(limb_msb_squared.clz(@bitSizeOf(Limb) * 2 + 1) == 2);
3432+
3433+
const limb_max_squared_minus_one: std.math.big.int.Const = .{
3434+
.limbs = &.{ 0, maxInt(Limb) - 1 },
3435+
.positive = true,
3436+
};
3437+
try testing.expect(limb_max_squared_minus_one.clz(@bitSizeOf(Limb) * 2) == 0);
3438+
try testing.expect(limb_max_squared_minus_one.clz(@bitSizeOf(Limb) * 2 + 1) == 1);
3439+
3440+
const limb_max_squared: std.math.big.int.Const = .{
3441+
.limbs = &.{ 1, maxInt(Limb) - 1 },
3442+
.positive = true,
3443+
};
3444+
try testing.expect(limb_max_squared.clz(@bitSizeOf(Limb) * 2) == 0);
3445+
try testing.expect(limb_max_squared.clz(@bitSizeOf(Limb) * 2 + 1) == 1);
3446+
}
3447+
3448+
test "ctz" {
3449+
const neg_limb_max_squared: std.math.big.int.Const = .{
3450+
.limbs = &.{ 1, maxInt(Limb) - 1 },
3451+
.positive = false,
3452+
};
3453+
try testing.expect(neg_limb_max_squared.ctz(@bitSizeOf(Limb) * 2 + 1) == 0);
3454+
3455+
const neg_limb_max_squared_plus_one: std.math.big.int.Const = .{
3456+
.limbs = &.{ 0, maxInt(Limb) - 1 },
3457+
.positive = false,
3458+
};
3459+
try testing.expect(neg_limb_max_squared_plus_one.ctz(@bitSizeOf(Limb) * 2 + 1) == @bitSizeOf(Limb) + 1);
3460+
3461+
const neg_limb_msb_squared: std.math.big.int.Const = .{
3462+
.limbs = &.{ 0, 1 << @bitSizeOf(Limb) - 2 },
3463+
.positive = false,
3464+
};
3465+
try testing.expect(neg_limb_msb_squared.ctz(@bitSizeOf(Limb) * 2) == @bitSizeOf(Limb) * 2 - 2);
3466+
try testing.expect(neg_limb_msb_squared.ctz(@bitSizeOf(Limb) * 2 + 1) == @bitSizeOf(Limb) * 2 - 2);
3467+
3468+
const neg_limb_max: std.math.big.int.Const = .{
3469+
.limbs = &.{maxInt(Limb)},
3470+
.positive = false,
3471+
};
3472+
try testing.expect(neg_limb_max.ctz(@bitSizeOf(Limb) + 1) == 0);
3473+
try testing.expect(neg_limb_max.ctz(@bitSizeOf(Limb) * 2 - 1) == 0);
3474+
try testing.expect(neg_limb_max.ctz(@bitSizeOf(Limb) * 2) == 0);
3475+
try testing.expect(neg_limb_max.ctz(@bitSizeOf(Limb) * 2 + 1) == 0);
3476+
3477+
const neg_limb_msb: std.math.big.int.Const = .{
3478+
.limbs = &.{1 << @bitSizeOf(Limb) - 1},
3479+
.positive = false,
3480+
};
3481+
try testing.expect(neg_limb_msb.ctz(@bitSizeOf(Limb)) == @bitSizeOf(Limb) - 1);
3482+
try testing.expect(neg_limb_msb.ctz(@bitSizeOf(Limb) + 1) == @bitSizeOf(Limb) - 1);
3483+
try testing.expect(neg_limb_msb.ctz(@bitSizeOf(Limb) * 2 - 1) == @bitSizeOf(Limb) - 1);
3484+
try testing.expect(neg_limb_msb.ctz(@bitSizeOf(Limb) * 2) == @bitSizeOf(Limb) - 1);
3485+
try testing.expect(neg_limb_msb.ctz(@bitSizeOf(Limb) * 2 + 1) == @bitSizeOf(Limb) - 1);
3486+
3487+
const neg_one: std.math.big.int.Const = .{
3488+
.limbs = &.{1},
3489+
.positive = false,
3490+
};
3491+
try testing.expect(neg_one.ctz(@bitSizeOf(Limb)) == 0);
3492+
try testing.expect(neg_one.ctz(@bitSizeOf(Limb) + 1) == 0);
3493+
try testing.expect(neg_one.ctz(@bitSizeOf(Limb) * 2 - 1) == 0);
3494+
try testing.expect(neg_one.ctz(@bitSizeOf(Limb) * 2) == 0);
3495+
try testing.expect(neg_one.ctz(@bitSizeOf(Limb) * 2 + 1) == 0);
3496+
3497+
const zero: std.math.big.int.Const = .{
3498+
.limbs = &.{0},
3499+
.positive = true,
3500+
};
3501+
try testing.expect(zero.ctz(@bitSizeOf(Limb)) == @bitSizeOf(Limb));
3502+
try testing.expect(zero.ctz(@bitSizeOf(Limb) + 1) == @bitSizeOf(Limb) + 1);
3503+
try testing.expect(zero.ctz(@bitSizeOf(Limb) * 2 - 1) == @bitSizeOf(Limb) * 2 - 1);
3504+
try testing.expect(zero.ctz(@bitSizeOf(Limb) * 2) == @bitSizeOf(Limb) * 2);
3505+
try testing.expect(zero.ctz(@bitSizeOf(Limb) * 2 + 1) == @bitSizeOf(Limb) * 2 + 1);
3506+
3507+
const one: std.math.big.int.Const = .{
3508+
.limbs = &.{1},
3509+
.positive = true,
3510+
};
3511+
try testing.expect(one.ctz(@bitSizeOf(Limb)) == 0);
3512+
try testing.expect(one.ctz(@bitSizeOf(Limb) + 1) == 0);
3513+
try testing.expect(one.ctz(@bitSizeOf(Limb) * 2 - 1) == 0);
3514+
try testing.expect(one.ctz(@bitSizeOf(Limb) * 2) == 0);
3515+
try testing.expect(one.ctz(@bitSizeOf(Limb) * 2 + 1) == 0);
3516+
3517+
const limb_msb: std.math.big.int.Const = .{
3518+
.limbs = &.{1 << @bitSizeOf(Limb) - 1},
3519+
.positive = true,
3520+
};
3521+
try testing.expect(limb_msb.ctz(@bitSizeOf(Limb)) == @bitSizeOf(Limb) - 1);
3522+
try testing.expect(limb_msb.ctz(@bitSizeOf(Limb) + 1) == @bitSizeOf(Limb) - 1);
3523+
try testing.expect(limb_msb.ctz(@bitSizeOf(Limb) * 2 - 1) == @bitSizeOf(Limb) - 1);
3524+
try testing.expect(limb_msb.ctz(@bitSizeOf(Limb) * 2) == @bitSizeOf(Limb) - 1);
3525+
try testing.expect(limb_msb.ctz(@bitSizeOf(Limb) * 2 + 1) == @bitSizeOf(Limb) - 1);
3526+
3527+
const limb_max: std.math.big.int.Const = .{
3528+
.limbs = &.{maxInt(Limb)},
3529+
.positive = true,
3530+
};
3531+
try testing.expect(limb_max.ctz(@bitSizeOf(Limb)) == 0);
3532+
try testing.expect(limb_max.ctz(@bitSizeOf(Limb) + 1) == 0);
3533+
try testing.expect(limb_max.ctz(@bitSizeOf(Limb) * 2 - 1) == 0);
3534+
try testing.expect(limb_max.ctz(@bitSizeOf(Limb) * 2) == 0);
3535+
try testing.expect(limb_max.ctz(@bitSizeOf(Limb) * 2 + 1) == 0);
3536+
3537+
const limb_msb_squared: std.math.big.int.Const = .{
3538+
.limbs = &.{ 0, 1 << @bitSizeOf(Limb) - 2 },
3539+
.positive = true,
3540+
};
3541+
try testing.expect(limb_msb_squared.ctz(@bitSizeOf(Limb) * 2 - 1) == @bitSizeOf(Limb) * 2 - 2);
3542+
try testing.expect(limb_msb_squared.ctz(@bitSizeOf(Limb) * 2) == @bitSizeOf(Limb) * 2 - 2);
3543+
try testing.expect(limb_msb_squared.ctz(@bitSizeOf(Limb) * 2 + 1) == @bitSizeOf(Limb) * 2 - 2);
3544+
3545+
const limb_max_squared_minus_one: std.math.big.int.Const = .{
3546+
.limbs = &.{ 0, maxInt(Limb) - 1 },
3547+
.positive = true,
3548+
};
3549+
try testing.expect(limb_max_squared_minus_one.ctz(@bitSizeOf(Limb) * 2) == @bitSizeOf(Limb) + 1);
3550+
try testing.expect(limb_max_squared_minus_one.ctz(@bitSizeOf(Limb) * 2 + 1) == @bitSizeOf(Limb) + 1);
3551+
3552+
const limb_max_squared: std.math.big.int.Const = .{
3553+
.limbs = &.{ 1, maxInt(Limb) - 1 },
3554+
.positive = true,
3555+
};
3556+
try testing.expect(limb_max_squared.ctz(@bitSizeOf(Limb) * 2) == 0);
3557+
try testing.expect(limb_max_squared.ctz(@bitSizeOf(Limb) * 2 + 1) == 0);
3558+
}

0 commit comments

Comments
 (0)