use core::arch::asm;
#[path = "portable.rs"]
mod portable;
#[target_feature(enable = "bmi2")]
unsafe fn mul_1_bmi2(r: *mut u64, x: *const u64, n: usize, y: u64) -> u64 {
let carry: u64;
unsafe {
asm!(
"mov {h3}, {n}",
"and {h3}, 3",
"inc {h3}",
"shr {n}, 2",
"inc {n}",
"xor {hp:e}, {hp:e}",
"jmp 3f",
"2:",
"mulx {h0}, {l0}, qword ptr [{xp}]",
"adc {l0}, {hp}",
"mov qword ptr [{rp}], {l0}",
"mov {hp}, {h0}",
"lea {xp}, [{xp} + 8]",
"lea {rp}, [{rp} + 8]",
"3:",
"dec {h3}",
"jnz 2b",
"jmp 5f",
"4:",
"mulx {h0}, {l0}, qword ptr [{xp}]",
"mulx {h1}, {l1}, qword ptr [{xp} + 8]",
"adc {l0}, {hp}",
"adc {l1}, {h0}",
"mulx {h2}, {l2}, qword ptr [{xp} + 16]",
"mulx {hp}, {l3}, qword ptr [{xp} + 24]",
"adc {l2}, {h1}",
"adc {l3}, {h2}",
"mov qword ptr [{rp}], {l0}",
"mov qword ptr [{rp} + 8], {l1}",
"mov qword ptr [{rp} + 16], {l2}",
"mov qword ptr [{rp} + 24], {l3}",
"lea {xp}, [{xp} + 32]",
"lea {rp}, [{rp} + 32]",
"5:",
"dec {n}",
"jnz 4b",
"adc {hp}, 0",
rp = inout(reg) r => _, xp = inout(reg) x => _, n = inout(reg) n => _,
in("rdx") y,
hp = out(reg) carry,
l0 = out(reg) _, l1 = out(reg) _, l2 = out(reg) _, l3 = out(reg) _,
h0 = out(reg) _, h1 = out(reg) _, h2 = out(reg) _, h3 = out(reg) _,
options(nostack),
);
}
carry
}
#[target_feature(enable = "bmi2")]
unsafe fn addmul_1(r: *mut u64, x: *const u64, n: usize, y: u64) -> u64 {
let carry: u64;
unsafe {
asm!(
"mov {h3}, {n}",
"and {h3}, 3",
"inc {h3}",
"shr {n}, 2",
"inc {n}",
"xor {hp:e}, {hp:e}",
"jmp 3f",
"2:",
"mulx {h0}, {l0}, qword ptr [{xp}]",
"adc {l0}, qword ptr [{rp}]",
"adc {h0}, 0",
"add {l0}, {hp}",
"mov qword ptr [{rp}], {l0}",
"mov {hp}, {h0}",
"lea {xp}, [{xp} + 8]",
"lea {rp}, [{rp} + 8]",
"3:",
"dec {h3}",
"jnz 2b",
"jmp 5f",
"4:",
"mulx {h0}, {l0}, qword ptr [{xp}]",
"mulx {h1}, {l1}, qword ptr [{xp} + 8]",
"mulx {h2}, {l2}, qword ptr [{xp} + 16]",
"mulx {h3}, {l3}, qword ptr [{xp} + 24]",
"adc {l0}, qword ptr [{rp}]",
"adc {l1}, qword ptr [{rp} + 8]",
"adc {l2}, qword ptr [{rp} + 16]",
"adc {l3}, qword ptr [{rp} + 24]",
"adc {h3}, 0",
"add {l0}, {hp}",
"adc {l1}, {h0}",
"adc {l2}, {h1}",
"adc {l3}, {h2}",
"mov qword ptr [{rp}], {l0}",
"mov qword ptr [{rp} + 8], {l1}",
"mov qword ptr [{rp} + 16], {l2}",
"mov qword ptr [{rp} + 24], {l3}",
"mov {hp}, {h3}",
"lea {xp}, [{xp} + 32]",
"lea {rp}, [{rp} + 32]",
"5:",
"dec {n}",
"jnz 4b",
"adc {hp}, 0",
rp = inout(reg) r => _, xp = inout(reg) x => _, n = inout(reg) n => _,
in("rdx") y,
hp = out(reg) carry,
l0 = out(reg) _, l1 = out(reg) _, l2 = out(reg) _, l3 = out(reg) _,
h0 = out(reg) _, h1 = out(reg) _, h2 = out(reg) _, h3 = out(reg) _,
options(nostack),
);
}
carry
}
#[inline]
pub(super) unsafe fn mul_1(r: *mut u64, x: *const u64, n: usize, y: u64) -> u64 {
unsafe {
if bmi2() {
mul_1_bmi2(r, x, n, y)
} else {
portable::mul_1(r, x, n, y)
}
}
}
#[inline]
pub(crate) unsafe fn mul_1_bytes(r: *mut u8, x: *const u8, n: usize, y: u64) -> u64 {
unsafe {
if bmi2() {
mul_1_bmi2(r.cast(), x.cast(), n, y)
} else {
portable::mul_1_bytes(r, x, n, y)
}
}
}
#[inline]
pub(super) unsafe fn divexact_finish(r: *mut u64, n: usize) {
unsafe { portable::divexact_finish(r, n) }
}
#[inline(always)]
fn bmi2() -> bool {
std::is_x86_feature_detected!("bmi2")
}
#[inline]
pub(super) unsafe fn mul_basecase(r: *mut u64, x: *const u64, nx: usize, y: *const u64, ny: usize) {
unsafe {
if !bmi2() || nx <= 3 || (ny == 1 && nx <= 8) {
portable::mul_basecase(r, x, nx, y, ny);
return;
}
*r.add(nx) = mul_1_bmi2(r, x, nx, *y);
for j in 1..ny {
*r.add(nx + j) = addmul_1(r.add(j), x, nx, *y.add(j));
}
}
}
#[inline]
pub(super) unsafe fn sqr_basecase(r: *mut u64, x: *const u64, n: usize) {
unsafe {
if !bmi2() || n <= 3 {
portable::sqr_basecase(r, x, n);
return;
}
*r.add(n) = mul_1_bmi2(r.add(1), x.add(1), n - 1, *x);
for i in 1..n - 1 {
*r.add(n + i) = addmul_1(r.add(2 * i + 1), x.add(i + 1), n - i - 1, *x.add(i));
}
*r = 0;
*r.add(2 * n - 1) = 0;
portable::double_and_add_squares(r, x, n);
}
}