use core::arch::asm;
#[path = "portable.rs"]
mod portable;
#[inline(always)]
pub(super) unsafe fn mul_1(r: *mut u64, x: *const u64, n: usize, y: u64) -> u64 {
let carry: u64;
unsafe {
asm!(
"mov {hp}, #0",
"cmn xzr, xzr",
"tbz {n}, #0, 1f",
"ldr {a0}, [{xp}], #8",
"mul {l0}, {a0}, {y}", "umulh {h0}, {a0}, {y}",
"adcs {l0}, {l0}, {hp}",
"mov {hp}, {h0}",
"str {l0}, [{rp}], #8",
"1:",
"tbz {n}, #1, 1f",
"ldp {a0}, {a1}, [{xp}], #16",
"mul {l0}, {a0}, {y}", "umulh {h0}, {a0}, {y}",
"mul {l1}, {a1}, {y}", "umulh {h1}, {a1}, {y}",
"adcs {l0}, {l0}, {hp}",
"adcs {l1}, {l1}, {h0}",
"mov {hp}, {h1}",
"stp {l0}, {l1}, [{rp}], #16",
"1:",
"tbz {n}, #2, 1f",
"ldp {a0}, {a1}, [{xp}]",
"mul {l0}, {a0}, {y}", "umulh {h0}, {a0}, {y}",
"mul {l1}, {a1}, {y}", "umulh {h1}, {a1}, {y}",
"ldp {a0}, {a1}, [{xp}, #16]",
"mul {l2}, {a0}, {y}", "umulh {h2}, {a0}, {y}",
"mul {l3}, {a1}, {y}", "umulh {h3}, {a1}, {y}",
"adcs {l0}, {l0}, {hp}",
"adcs {l1}, {l1}, {h0}",
"adcs {l2}, {l2}, {h1}",
"adcs {l3}, {l3}, {h2}",
"mov {hp}, {h3}",
"stp {l0}, {l1}, [{rp}]",
"stp {l2}, {l3}, [{rp}, #16]",
"add {xp}, {xp}, #32",
"add {rp}, {rp}, #32",
"1:",
"lsr {n}, {n}, #3",
"cbz {n}, 3f",
"2:",
"ldp {a0}, {a1}, [{xp}]",
"mul {l0}, {a0}, {y}", "umulh {h0}, {a0}, {y}",
"mul {l1}, {a1}, {y}", "umulh {h1}, {a1}, {y}",
"ldp {a0}, {a1}, [{xp}, #16]",
"mul {l2}, {a0}, {y}", "umulh {h2}, {a0}, {y}",
"mul {l3}, {a1}, {y}", "umulh {h3}, {a1}, {y}",
"adcs {l0}, {l0}, {hp}",
"adcs {l1}, {l1}, {h0}",
"adcs {l2}, {l2}, {h1}",
"adcs {l3}, {l3}, {h2}",
"stp {l0}, {l1}, [{rp}]",
"stp {l2}, {l3}, [{rp}, #16]",
"ldp {a0}, {a1}, [{xp}, #32]",
"mul {l0}, {a0}, {y}", "umulh {h0}, {a0}, {y}",
"mul {l1}, {a1}, {y}", "umulh {h1}, {a1}, {y}",
"ldp {a0}, {a1}, [{xp}, #48]",
"mul {l2}, {a0}, {y}", "umulh {h2}, {a0}, {y}",
"mul {l4}, {a1}, {y}", "umulh {hp}, {a1}, {y}",
"adcs {l0}, {l0}, {h3}",
"adcs {l1}, {l1}, {h0}",
"adcs {l2}, {l2}, {h1}",
"adcs {l4}, {l4}, {h2}",
"stp {l0}, {l1}, [{rp}, #32]",
"stp {l2}, {l4}, [{rp}, #48]",
"add {xp}, {xp}, #64",
"add {rp}, {rp}, #64",
"sub {n}, {n}, #1",
"cbnz {n}, 2b",
"3:",
"adc {hp}, {hp}, xzr",
rp = inout(reg) r => _, xp = inout(reg) x => _, n = inout(reg) n => _, y = in(reg) y,
hp = out(reg) carry,
a0 = out(reg) _, a1 = out(reg) _,
l0 = out(reg) _, l1 = out(reg) _, l2 = out(reg) _, l3 = out(reg) _, l4 = out(reg) _,
h0 = out(reg) _, h1 = out(reg) _, h2 = out(reg) _, h3 = out(reg) _,
options(nostack),
);
}
carry
}
#[inline(always)]
pub(crate) unsafe fn mul_1_bytes(r: *mut u8, x: *const u8, n: usize, y: u64) -> u64 {
unsafe { mul_1(r.cast(), x.cast(), n, y) }
}
#[inline(always)]
pub(in super::super) unsafe fn addmul_1(r: *mut u64, x: *const u64, n: usize, y: u64) -> u64 {
let carry: u64;
unsafe {
asm!(
"mov {hp}, #0",
"cmn xzr, xzr",
"tbz {n}, #0, 1f",
"ldr {a0}, [{xp}], #8",
"ldr {u0}, [{rp}]",
"mul {l0}, {a0}, {y}", "umulh {h0}, {a0}, {y}",
"adcs {u0}, {u0}, {l0}",
"adc {h0}, {h0}, xzr",
"adds {u0}, {u0}, {hp}",
"mov {hp}, {h0}",
"str {u0}, [{rp}], #8",
"1:",
"tbz {n}, #1, 1f",
"ldp {a0}, {a1}, [{xp}], #16",
"ldp {u0}, {u1}, [{rp}]",
"mul {l0}, {a0}, {y}", "umulh {h0}, {a0}, {y}",
"mul {l1}, {a1}, {y}", "umulh {h1}, {a1}, {y}",
"adcs {u0}, {u0}, {l0}",
"adcs {u1}, {u1}, {l1}",
"adc {h1}, {h1}, xzr",
"adds {u0}, {u0}, {hp}",
"adcs {u1}, {u1}, {h0}",
"mov {hp}, {h1}",
"stp {u0}, {u1}, [{rp}], #16",
"1:",
"tbz {n}, #2, 1f",
"ldp {a0}, {a1}, [{xp}]",
"mul {l0}, {a0}, {y}", "umulh {h0}, {a0}, {y}",
"mul {l1}, {a1}, {y}", "umulh {h1}, {a1}, {y}",
"ldp {a0}, {a1}, [{xp}, #16]",
"mul {l2}, {a0}, {y}", "umulh {h2}, {a0}, {y}",
"mul {l3}, {a1}, {y}", "umulh {h3}, {a1}, {y}",
"ldp {u0}, {u1}, [{rp}]",
"ldp {u2}, {u3}, [{rp}, #16]",
"adcs {u0}, {u0}, {l0}",
"adcs {u1}, {u1}, {l1}",
"adcs {u2}, {u2}, {l2}",
"adcs {u3}, {u3}, {l3}",
"adc {h3}, {h3}, xzr",
"adds {u0}, {u0}, {hp}",
"adcs {u1}, {u1}, {h0}",
"adcs {u2}, {u2}, {h1}",
"adcs {u3}, {u3}, {h2}",
"mov {hp}, {h3}",
"stp {u0}, {u1}, [{rp}]",
"stp {u2}, {u3}, [{rp}, #16]",
"add {xp}, {xp}, #32",
"add {rp}, {rp}, #32",
"1:",
"lsr {n}, {n}, #3",
"cbz {n}, 3f",
"2:",
"ldp {a0}, {a1}, [{xp}]",
"mul {l0}, {a0}, {y}", "umulh {h0}, {a0}, {y}",
"mul {l1}, {a1}, {y}", "umulh {h1}, {a1}, {y}",
"ldp {a0}, {a1}, [{xp}, #16]",
"mul {l2}, {a0}, {y}", "umulh {h2}, {a0}, {y}",
"mul {l3}, {a1}, {y}", "umulh {h3}, {a1}, {y}",
"ldp {u0}, {u1}, [{rp}]",
"ldp {u2}, {u3}, [{rp}, #16]",
"adcs {u0}, {u0}, {l0}",
"adcs {u1}, {u1}, {l1}",
"adcs {u2}, {u2}, {l2}",
"adcs {u3}, {u3}, {l3}",
"ldp {a0}, {a1}, [{xp}, #32]",
"mul {l0}, {a0}, {y}", "umulh {h4}, {a0}, {y}",
"mul {l1}, {a1}, {y}", "umulh {h5}, {a1}, {y}",
"ldp {a0}, {a1}, [{xp}, #48]",
"mul {l2}, {a0}, {y}", "umulh {h6}, {a0}, {y}",
"mul {l3}, {a1}, {y}", "umulh {h7}, {a1}, {y}",
"ldp {u4}, {u5}, [{rp}, #32]",
"ldp {u6}, {u7}, [{rp}, #48]",
"adcs {u4}, {u4}, {l0}",
"adcs {u5}, {u5}, {l1}",
"adcs {u6}, {u6}, {l2}",
"adcs {u7}, {u7}, {l3}",
"adc {h7}, {h7}, xzr",
"adds {u0}, {u0}, {hp}",
"adcs {u1}, {u1}, {h0}",
"adcs {u2}, {u2}, {h1}",
"adcs {u3}, {u3}, {h2}",
"adcs {u4}, {u4}, {h3}",
"adcs {u5}, {u5}, {h4}",
"adcs {u6}, {u6}, {h5}",
"adcs {u7}, {u7}, {h6}",
"mov {hp}, {h7}",
"stp {u0}, {u1}, [{rp}]",
"stp {u2}, {u3}, [{rp}, #16]",
"stp {u4}, {u5}, [{rp}, #32]",
"stp {u6}, {u7}, [{rp}, #48]",
"add {xp}, {xp}, #64",
"add {rp}, {rp}, #64",
"sub {n}, {n}, #1",
"cbnz {n}, 2b",
"3:",
"adc {hp}, {hp}, xzr",
rp = inout(reg) r => _, xp = inout(reg) x => _, n = inout(reg) n => _, y = in(reg) y,
hp = out(reg) carry,
a0 = out(reg) _, a1 = out(reg) _,
l0 = out(reg) _, l1 = out(reg) _, l2 = out(reg) _, l3 = out(reg) _,
h0 = out(reg) _, h1 = out(reg) _, h2 = out(reg) _, h3 = out(reg) _,
h4 = out(reg) _, h5 = out(reg) _, h6 = out(reg) _, h7 = out(reg) _,
u0 = out(reg) _, u1 = out(reg) _, u2 = out(reg) _, u3 = out(reg) _,
u4 = out(reg) _, u5 = out(reg) _, u6 = out(reg) _, u7 = out(reg) _,
options(nostack),
);
}
carry
}
#[inline(always)]
pub(in super::super) unsafe fn addmul_2(
r: *mut u64,
x: *const u64,
n: usize,
y0: u64,
y1: u64,
c: u64,
) -> u64 {
let top: u64;
unsafe {
asm!(
"mov {l1p}, #0", "mov {h1pp}, #0", "mov {h1p}, #0", "mov {c2}, #0",
"cmn xzr, xzr",
"tbz {n}, #0, 1f",
"ldr {a0}, [{xp}], #8",
"ldr {u0}, [{rp}]",
"mul {l0}, {a0}, {y0}", "umulh {h0}, {a0}, {y0}",
"mul {l1}, {a0}, {y1}", "umulh {h1}, {a0}, {y1}",
"adcs {u0}, {u0}, {l0}",
"adc {h0}, {h0}, xzr",
"cmp {c2}, #1",
"adcs {u0}, {u0}, {h0p}",
"cset {c2}, cs",
"adds {u0}, {u0}, {l1p}",
"adc {h1p}, {h1p}, xzr",
"adds {u0}, {u0}, {h1pp}",
"str {u0}, [{rp}], #8",
"mov {h0p}, {h0}", "mov {l1p}, {l1}", "mov {h1pp}, {h1p}", "mov {h1p}, {h1}",
"1:",
"tbz {n}, #1, 1f",
"ldp {a0}, {a1}, [{xp}], #16",
"ldp {u0}, {u1}, [{rp}]",
"mul {l0}, {a0}, {y0}", "umulh {h0}, {a0}, {y0}",
"mul {l1}, {a1}, {y0}", "umulh {h1}, {a1}, {y0}",
"adcs {u0}, {u0}, {l0}",
"adcs {u1}, {u1}, {l1}",
"adc {h1}, {h1}, xzr",
"cmp {c2}, #1",
"adcs {u0}, {u0}, {h0p}",
"adcs {u1}, {u1}, {h0}",
"cset {c2}, cs",
"mov {h0p}, {h1}",
"mul {l0}, {a0}, {y1}", "umulh {h0}, {a0}, {y1}",
"mul {l1}, {a1}, {y1}", "umulh {h1}, {a1}, {y1}",
"adds {u0}, {u0}, {l1p}",
"adcs {u1}, {u1}, {l0}",
"adc {h0}, {h0}, xzr",
"adds {u0}, {u0}, {h1pp}",
"adcs {u1}, {u1}, {h1p}",
"stp {u0}, {u1}, [{rp}], #16",
"mov {l1p}, {l1}", "mov {h1pp}, {h0}", "mov {h1p}, {h1}",
"1:",
"lsr {n}, {n}, #2",
"cbz {n}, 3f",
"2:",
"ldp {a0}, {a1}, [{xp}]",
"ldp {a2}, {a3}, [{xp}, #16]",
"mul {l0}, {a0}, {y0}", "umulh {h0}, {a0}, {y0}",
"mul {l1}, {a1}, {y0}", "umulh {h1}, {a1}, {y0}",
"mul {l2}, {a2}, {y0}", "umulh {h2}, {a2}, {y0}",
"mul {l3}, {a3}, {y0}", "umulh {h3}, {a3}, {y0}",
"ldp {u0}, {u1}, [{rp}]",
"ldp {u2}, {u3}, [{rp}, #16]",
"adcs {u0}, {u0}, {l0}",
"adcs {u1}, {u1}, {l1}",
"adcs {u2}, {u2}, {l2}",
"adcs {u3}, {u3}, {l3}",
"adc {h3}, {h3}, xzr",
"cmp {c2}, #1",
"adcs {u0}, {u0}, {h0p}",
"adcs {u1}, {u1}, {h0}",
"adcs {u2}, {u2}, {h1}",
"adcs {u3}, {u3}, {h2}",
"cset {c2}, cs",
"mov {h0p}, {h3}",
"mul {l0}, {a0}, {y1}", "umulh {h0}, {a0}, {y1}",
"mul {l1}, {a1}, {y1}", "umulh {h1}, {a1}, {y1}",
"mul {l2}, {a2}, {y1}", "umulh {h2}, {a2}, {y1}",
"mul {l3}, {a3}, {y1}", "umulh {h3}, {a3}, {y1}",
"adds {u0}, {u0}, {l1p}",
"adcs {u1}, {u1}, {l0}",
"adcs {u2}, {u2}, {l1}",
"adcs {u3}, {u3}, {l2}",
"adc {h2}, {h2}, xzr",
"adds {u0}, {u0}, {h1pp}",
"adcs {u1}, {u1}, {h1p}",
"adcs {u2}, {u2}, {h0}",
"adcs {u3}, {u3}, {h1}",
"stp {u0}, {u1}, [{rp}]",
"stp {u2}, {u3}, [{rp}, #16]",
"mov {l1p}, {l3}", "mov {h1pp}, {h2}", "mov {h1p}, {h3}",
"add {xp}, {xp}, #32",
"add {rp}, {rp}, #32",
"sub {n}, {n}, #1",
"cbnz {n}, 2b",
"3:",
"adcs {u0}, {h0p}, {l1p}",
"adc {u1}, {h1p}, xzr",
"adds {u0}, {u0}, {h1pp}",
"adc {u1}, {u1}, xzr",
"adds {u0}, {u0}, {c2}",
"adc {u1}, {u1}, xzr",
"str {u0}, [{rp}]",
rp = inout(reg) r => _, xp = inout(reg) x => _, n = inout(reg) n => _,
y0 = in(reg) y0, y1 = in(reg) y1,
u1 = out(reg) top,
h0p = inout(reg) c => _, l1p = out(reg) _, h1pp = out(reg) _, h1p = out(reg) _, c2 = out(reg) _,
a0 = out(reg) _, a1 = out(reg) _, a2 = out(reg) _, a3 = out(reg) _,
u0 = out(reg) _, u2 = out(reg) _, u3 = out(reg) _,
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),
);
}
top
}
#[inline]
pub(super) unsafe fn divexact_finish(r: *mut u64, n: usize) {
unsafe {
asm!(
"mov {q}, #0",
"cmp xzr, xzr",
"tbz {n}, #0, 1f",
"ldr {a0}, [{rp}]",
"sbcs {q}, {q}, {a0}",
"str {q}, [{rp}], #8",
"1:",
"tbz {n}, #1, 1f",
"ldp {a0}, {a1}, [{rp}]",
"sbcs {a0}, {q}, {a0}",
"sbcs {q}, {a0}, {a1}",
"stp {a0}, {q}, [{rp}], #16",
"1:",
"lsr {n}, {n}, #2",
"cbz {n}, 3f",
"2:",
"ldp {a0}, {a1}, [{rp}]",
"ldp {a2}, {a3}, [{rp}, #16]",
"sbcs {a0}, {q}, {a0}",
"sbcs {a1}, {a0}, {a1}",
"sbcs {a2}, {a1}, {a2}",
"sbcs {q}, {a2}, {a3}",
"stp {a0}, {a1}, [{rp}]",
"stp {a2}, {q}, [{rp}, #16]",
"add {rp}, {rp}, #32",
"sub {n}, {n}, #1",
"cbnz {n}, 2b",
"3:",
rp = inout(reg) r => _, n = inout(reg) n => _,
q = out(reg) _, a0 = out(reg) _, a1 = out(reg) _, a2 = out(reg) _, a3 = out(reg) _,
options(nostack),
);
}
}
#[inline(always)]
unsafe fn diag_addlsh1(r: *mut u64, x: *const u64, n: usize) {
unsafe {
asm!(
"mov {prev}, #0",
"cmn xzr, xzr",
"2:",
"ldr {a}, [{xp}], #8",
"ldp {t0}, {t1}, [{rp}]",
"mul {lo}, {a}, {a}",
"umulh {hi}, {a}, {a}",
"extr {d0}, {t0}, {prev}, #63",
"extr {d1}, {t1}, {t0}, #63",
"mov {prev}, {t1}",
"adcs {d0}, {d0}, {lo}",
"adcs {d1}, {d1}, {hi}",
"stp {d0}, {d1}, [{rp}], #16",
"sub {n}, {n}, #1",
"cbnz {n}, 2b",
rp = inout(reg) r => _, xp = inout(reg) x => _, n = inout(reg) n => _,
prev = out(reg) _, a = out(reg) _, t0 = out(reg) _, t1 = out(reg) _,
lo = out(reg) _, hi = out(reg) _, d0 = out(reg) _, d1 = out(reg) _,
options(nostack),
);
}
}
pub(super) const SQR_ONE_ROW: usize = 10;
#[inline]
pub(super) unsafe fn mul_basecase(r: *mut u64, x: *const u64, nx: usize, y: *const u64, ny: usize) {
unsafe {
if nx <= 3 || (ny == 1 && nx <= 8) {
portable::mul_basecase(r, x, nx, y, ny);
return;
}
*r.add(nx) = mul_1(r, x, nx, *y);
let mut j = 1;
if nx <= 16 {
while j + 1 < ny {
*r.add(nx + j + 1) = addmul_2(r.add(j), x, nx, *y.add(j), *y.add(j + 1), 0);
j += 2;
}
}
while j < ny {
*r.add(nx + j) = addmul_1(r.add(j), x, nx, *y.add(j));
j += 1;
}
}
}
#[inline]
pub(super) unsafe fn sqr_basecase(r: *mut u64, x: *const u64, n: usize) {
unsafe {
if n <= 3 {
portable::sqr_basecase(r, x, n);
return;
}
*r.add(n) = mul_1(r.add(1), x.add(1), n - 1, *x);
let mut i = 1;
while i + 2 < n && n - i - 1 > SQR_ONE_ROW {
*r.add(n + i) = addmul_1(r.add(2 * i + 1), x.add(i + 1), n - i - 1, *x.add(i));
i += 1;
}
while i + 2 < n {
let (y0, y1) = (*x.add(i), *x.add(i + 1));
let p = u128::from(y0) * u128::from(y1);
let (low, carry) = (*r.add(2 * i + 1)).overflowing_add(p as u64);
*r.add(2 * i + 1) = low;
let c = (p >> 64) as u64 + u64::from(carry);
*r.add(n + i + 1) = addmul_2(r.add(2 * i + 2), x.add(i + 2), n - i - 2, y0, y1, c);
i += 2;
}
if i + 2 == n {
*r.add(n + i) = addmul_1(r.add(2 * i + 1), x.add(i + 1), 1, *x.add(i));
}
*r = 0;
*r.add(2 * n - 1) = 0;
diag_addlsh1(r, x, n);
}
}