Skip to content

Commit e7bdc01

Browse files
committed
rscrypto: guess what?
1 parent 40a543e commit e7bdc01

1 file changed

Lines changed: 53 additions & 51 deletions

File tree

crates/checksum/src/crc32/x86_64.rs

Lines changed: 53 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -47,18 +47,25 @@ mod crc32c_constants {
4747
/// Constants from crc32fast (MIT licensed, same as Intel paper constants):
4848
/// - K1/K2: 64-byte (512-bit) folding
4949
/// - K3/K4: 16-byte (128-bit) folding
50-
/// - MU/POLY: Barrett reduction
50+
/// - K5: 64→32 bit reduction
51+
/// - P_X/U_PRIME: Barrett reduction
5152
///
5253
/// These are 33-bit values with bit 32 set, as required by the Intel algorithm.
5354
mod crc32_ieee_constants {
5455
/// 64-byte block folding: (K1, K2) for 512-bit fold.
55-
pub const FOLD_64: (u64, u64) = (0x0000_0001_5444_2bd4, 0x0000_0001_c6e4_1596);
56+
pub const K1: u64 = 0x0000_0001_5444_2bd4;
57+
pub const K2: u64 = 0x0000_0001_c6e4_1596;
5658

5759
/// 16-byte lane folding: (K3, K4) for 128-bit fold.
58-
pub const FOLD_16: (u64, u64) = (0x0000_0001_7519_97d0, 0x0000_0000_ccaa_009e);
60+
pub const K3: u64 = 0x0000_0001_7519_97d0;
61+
pub const K4: u64 = 0x0000_0000_ccaa_009e;
5962

60-
/// Barrett reduction: (µ, P').
61-
pub const MU_POLY: (u64, u64) = (0x0000_0001_F701_1641, 0x0000_0001_DB71_0641);
63+
/// 64→32 bit reduction constant.
64+
pub const K5: u64 = 0x0000_0001_63cd_6124;
65+
66+
/// Barrett reduction: P_X (polynomial) and U_PRIME (µ).
67+
pub const P_X: u64 = 0x0000_0001_DB71_0641;
68+
pub const U_PRIME: u64 = 0x0000_0001_F701_1641;
6269
}
6370

6471
// ─────────────────────────────────────────────────────────────────────────────
@@ -402,37 +409,37 @@ pub fn crc32c_pclmul_safe(crc: u32, data: &[u8]) -> u32 {
402409
// CRC32-IEEE: Pure PCLMUL + Barrett (no hardware instruction)
403410
// ─────────────────────────────────────────────────────────────────────────────
404411

405-
/// Barrett reduction from 128 bits to 32 bits for CRC32-IEEE.
412+
/// Reduce 128 bits to 32 bits for CRC32-IEEE.
406413
///
407-
/// Algorithm based on Intel's "Fast CRC Computation" paper.
408-
/// Uses K3/K4 for 128→64→32 reduction, then Barrett for final step.
414+
/// Algorithm from crc32fast / Intel's "Fast CRC Computation" paper.
415+
/// Stages: 128→64 using K3, 64→32 using K5, then Barrett with P_X/U_PRIME.
409416
#[target_feature(enable = "pclmulqdq", enable = "sse4.1")]
410-
unsafe fn barrett_reduce_ieee(acc: __m128i) -> u32 {
417+
unsafe fn barrett_reduce_ieee(mut x: __m128i, k3k4: __m128i) -> u32 {
411418
// SAFETY: target_feature ensures PCLMULQDQ and SSE4.1 availability
412-
let k3_k4 = _mm_set_epi64x(
413-
crc32_ieee_constants::FOLD_16.1 as i64, // K4
414-
crc32_ieee_constants::FOLD_16.0 as i64, // K3
415-
);
416-
let mu_poly = _mm_set_epi64x(
417-
crc32_ieee_constants::MU_POLY.1 as i64, // POLY
418-
crc32_ieee_constants::MU_POLY.0 as i64, // MU
419+
420+
// Stage 1: 128→64 bits using K3 (selector 0x10 = x.hi × k3k4.lo = x.hi × K3)
421+
x = _mm_xor_si128(_mm_clmulepi64_si128(x, k3k4, 0x10), _mm_srli_si128(x, 8));
422+
423+
// Stage 2: 64→32 bits using K5
424+
// Mask to get lower 32 bits, multiply by K5, XOR with upper 32 bits
425+
let k5 = _mm_set_epi64x(0, crc32_ieee_constants::K5 as i64);
426+
x = _mm_xor_si128(
427+
_mm_clmulepi64_si128(_mm_and_si128(x, _mm_set_epi32(0, 0, 0, !0)), k5, 0x00),
428+
_mm_srli_si128(x, 4),
419429
);
420430

421-
// Fold 128→64 bits: multiply high 64 bits by K3, XOR with low 64 bits
422-
let t1 = _mm_clmulepi64_si128(acc, k3_k4, 0x10); // acc.hi × K3
423-
let t2 = _mm_xor_si128(t1, _mm_srli_si128(acc, 8));
424-
let t3 = _mm_xor_si128(t2, _mm_slli_si128(acc, 8));
431+
// Stage 3: Barrett reduction
432+
// pu = [U_PRIME, P_X] - note the order for selector usage
433+
let pu = _mm_set_epi64x(crc32_ieee_constants::U_PRIME as i64, crc32_ieee_constants::P_X as i64);
425434

426-
// Fold 64→32 bits: multiply bits [63:32] by K4
427-
let t4 = _mm_clmulepi64_si128(t3, k3_k4, 0x01); // t3.lo × K4
428-
let t5 = _mm_xor_si128(t4, t3);
435+
// t1 = (x & 0xFFFFFFFF) × U_PRIME (selector 0x10 = x.lo × pu.hi)
436+
let t1 = _mm_clmulepi64_si128(_mm_and_si128(x, _mm_set_epi32(0, 0, 0, !0)), pu, 0x10);
429437

430-
// Barrett reduction: 64→32 bits
431-
let t6 = _mm_clmulepi64_si128(_mm_and_si128(t5, _mm_set_epi32(0, 0, !0, !0)), mu_poly, 0x00);
432-
let t7 = _mm_clmulepi64_si128(t6, mu_poly, 0x10);
433-
let result = _mm_xor_si128(t5, t7);
438+
// t2 = (t1 & 0xFFFFFFFF) × P_X (selector 0x00 = t1.lo × pu.lo)
439+
let t2 = _mm_clmulepi64_si128(_mm_and_si128(t1, _mm_set_epi32(0, 0, 0, !0)), pu, 0x00);
434440

435-
_mm_extract_epi32(result, 1) as u32
441+
// Result is in bits [63:32] after XOR
442+
_mm_extract_epi32(_mm_xor_si128(x, t2), 1) as u32
436443
}
437444

438445
/// CRC32-IEEE using PCLMULQDQ folding + Barrett reduction.
@@ -453,14 +460,9 @@ pub unsafe fn crc32_ieee_pclmul(mut crc: u32, data: &[u8]) -> u32 {
453460
let mut ptr = data.as_ptr();
454461
let end = ptr.add(data.len());
455462

456-
let fold_k = _mm_set_epi64x(
457-
crc32_ieee_constants::FOLD_16.1 as i64,
458-
crc32_ieee_constants::FOLD_16.0 as i64,
459-
);
460-
let fold_64_k = _mm_set_epi64x(
461-
crc32_ieee_constants::FOLD_64.1 as i64,
462-
crc32_ieee_constants::FOLD_64.0 as i64,
463-
);
463+
// K1K2 for 64-byte fold, K3K4 for 16-byte fold
464+
let k1k2 = _mm_set_epi64x(crc32_ieee_constants::K2 as i64, crc32_ieee_constants::K1 as i64);
465+
let k3k4 = _mm_set_epi64x(crc32_ieee_constants::K4 as i64, crc32_ieee_constants::K3 as i64);
464466

465467
// Initialize 4 lanes with first 64 bytes
466468
let crc_vec = _mm_set_epi32(0, 0, 0, crc as i32);
@@ -472,45 +474,45 @@ pub unsafe fn crc32_ieee_pclmul(mut crc: u32, data: &[u8]) -> u32 {
472474
];
473475
ptr = ptr.add(FOLD_BYTES);
474476

475-
// Process 64-byte blocks
477+
// Process 64-byte blocks using K1K2
476478
while ptr.add(FOLD_BYTES) <= end {
477479
for (i, lane) in lanes.iter_mut().enumerate() {
478480
let new_data = _mm_loadu_si128(ptr.add(i * 16) as *const __m128i);
479-
let lo = clmul_lo(*lane, fold_64_k);
480-
let hi = clmul_hi(*lane, fold_64_k);
481+
let lo = clmul_lo(*lane, k1k2);
482+
let hi = clmul_hi(*lane, k1k2);
481483
*lane = _mm_xor_si128(_mm_xor_si128(lo, hi), new_data);
482484
}
483485
ptr = ptr.add(FOLD_BYTES);
484486
}
485487

486-
// Reduce 4 lanes to 1
488+
// Reduce 4 lanes to 1 using K3K4
487489
let fold_48 = {
488-
let lo = clmul_lo(lanes[0], fold_k);
489-
let hi = clmul_hi(lanes[0], fold_k);
490+
let lo = clmul_lo(lanes[0], k3k4);
491+
let hi = clmul_hi(lanes[0], k3k4);
490492
_mm_xor_si128(_mm_xor_si128(lo, hi), lanes[1])
491493
};
492494
let fold_32 = {
493-
let lo = clmul_lo(fold_48, fold_k);
494-
let hi = clmul_hi(fold_48, fold_k);
495+
let lo = clmul_lo(fold_48, k3k4);
496+
let hi = clmul_hi(fold_48, k3k4);
495497
_mm_xor_si128(_mm_xor_si128(lo, hi), lanes[2])
496498
};
497499
let mut acc = {
498-
let lo = clmul_lo(fold_32, fold_k);
499-
let hi = clmul_hi(fold_32, fold_k);
500+
let lo = clmul_lo(fold_32, k3k4);
501+
let hi = clmul_hi(fold_32, k3k4);
500502
_mm_xor_si128(_mm_xor_si128(lo, hi), lanes[3])
501503
};
502504

503-
// Process remaining 16-byte blocks
505+
// Process remaining 16-byte blocks using K3K4
504506
while ptr.add(16) <= end {
505507
let block = _mm_loadu_si128(ptr as *const __m128i);
506-
let lo = clmul_lo(acc, fold_k);
507-
let hi = clmul_hi(acc, fold_k);
508+
let lo = clmul_lo(acc, k3k4);
509+
let hi = clmul_hi(acc, k3k4);
508510
acc = _mm_xor_si128(_mm_xor_si128(lo, hi), block);
509511
ptr = ptr.add(16);
510512
}
511513

512-
// Barrett reduction
513-
crc = barrett_reduce_ieee(acc);
514+
// Reduce 128→32 bits
515+
crc = barrett_reduce_ieee(acc, k3k4);
514516

515517
// Handle remainder with portable
516518
let remainder_len = end.offset_from(ptr) as usize;

0 commit comments

Comments
 (0)