use crate::arith::{E, P};
pub(crate) trait Arith {
fn mul(a: u64, b: u64) -> u64;
fn mul_t(a: u64, b: u64) -> u64;
fn mul_add(a: u64, b: u64, c: u64) -> u64;
fn mul_add_t(a: u64, b: u64, c: u64) -> u64;
fn add(a: u64, b: u64) -> u64;
fn canon(x: u64) -> u64;
}
#[derive(Debug)]
pub(crate) struct Fast;
impl Arith for Fast {
#[inline(always)]
fn mul(a: u64, b: u64) -> u64 {
crate::arith::mul(a, b)
}
#[inline(always)]
fn mul_t(a: u64, b: u64) -> u64 {
crate::arith::mul_t(a, b)
}
#[inline(always)]
fn mul_add(a: u64, b: u64, c: u64) -> u64 {
crate::arith::mul_add(a, b, c)
}
#[inline(always)]
fn mul_add_t(a: u64, b: u64, c: u64) -> u64 {
crate::arith::mul_add_t(a, b, c)
}
#[inline(always)]
fn add(a: u64, b: u64) -> u64 {
crate::arith::add(a, b)
}
#[inline(always)]
fn canon(x: u64) -> u64 {
crate::arith::canon(x)
}
}
#[derive(Debug)]
pub(crate) struct Ct;
#[inline(always)]
fn mask(b: bool) -> u64 {
u64::from(b).wrapping_neg()
}
#[cfg(not(all(target_arch = "aarch64", not(hemera_portable))))]
#[inline(always)]
fn reduce_ct(lo: u64, hi: u64) -> u64 {
let (t0, borrow) = lo.overflowing_sub(hi >> 32);
let t0 = t0.wrapping_sub(E & mask(borrow));
let t1 = (hi << 32).wrapping_sub(hi & E);
let (r, carry) = t0.overflowing_add(t1);
r.wrapping_add(E & mask(carry))
}
#[inline(always)]
fn mul_ct(a: u64, b: u64) -> u64 {
#[cfg(all(target_arch = "aarch64", not(hemera_portable)))]
{
let r: u64;
unsafe {
core::arch::asm!(
"mul {lo}, {a}, {b}",
"umulh {hi}, {a}, {b}",
"subs {lo}, {lo}, {hi}, lsr #32",
"csel {t}, {e}, xzr, cc",
"sub {lo}, {lo}, {t}",
"lsl {u}, {hi}, #32",
"and {t}, {hi}, {e}",
"bic {hi}, {e}, {hi}",
"sub {t}, {u}, {t}",
"add {u}, {u}, {hi}",
"add {u}, {lo}, {u}",
"adds {r}, {lo}, {t}",
"csel {r}, {u}, {r}, cs",
a = in(reg) a,
b = in(reg) b,
e = in(reg) E,
lo = out(reg) _,
hi = out(reg) _,
t = out(reg) _,
u = out(reg) _,
r = out(reg) r,
options(pure, nomem, nostack),
);
}
r
}
#[cfg(not(all(target_arch = "aarch64", not(hemera_portable))))]
{
let x = u128::from(a) * u128::from(b);
reduce_ct(x as u64, (x >> 64) as u64)
}
}
#[inline(always)]
fn mul_add_ct(a: u64, b: u64, c: u64) -> u64 {
#[cfg(all(target_arch = "aarch64", not(hemera_portable)))]
{
let r: u64;
unsafe {
core::arch::asm!(
"mul {lo}, {a}, {b}",
"umulh {hi}, {a}, {b}",
"adds {lo}, {lo}, {c}",
"adc {hi}, {hi}, xzr",
"subs {lo}, {lo}, {hi}, lsr #32",
"csel {u}, {e}, xzr, cc",
"sub {lo}, {lo}, {u}",
"lsl {u}, {hi}, #32",
"sub {u}, {u}, {hi:w}, uxtw",
"adds {r}, {lo}, {u}",
"csel {u}, {e}, xzr, cs",
"add {r}, {r}, {u}",
a = in(reg) a,
b = in(reg) b,
c = in(reg) c,
e = in(reg) E,
lo = out(reg) _,
hi = out(reg) _,
u = out(reg) _,
r = out(reg) r,
options(pure, nomem, nostack),
);
}
r
}
#[cfg(not(all(target_arch = "aarch64", not(hemera_portable))))]
{
let x = u128::from(a) * u128::from(b) + u128::from(c);
reduce_ct(x as u64, (x >> 64) as u64)
}
}
impl Arith for Ct {
#[inline(always)]
fn mul(a: u64, b: u64) -> u64 {
mul_ct(a, b)
}
#[inline(always)]
fn mul_t(a: u64, b: u64) -> u64 {
mul_ct(a, b)
}
#[inline(always)]
fn mul_add(a: u64, b: u64, c: u64) -> u64 {
mul_add_ct(a, b, c)
}
#[inline(always)]
fn mul_add_t(a: u64, b: u64, c: u64) -> u64 {
mul_add_ct(a, b, c)
}
#[inline(always)]
fn add(a: u64, b: u64) -> u64 {
let (s, o1) = a.overflowing_add(b);
let (s, o2) = s.overflowing_add(E & mask(o1));
s.wrapping_add(E & mask(o2))
}
#[inline(always)]
fn canon(x: u64) -> u64 {
x.wrapping_sub(P & mask(x >= P))
}
}