#[cfg(feature = "std")]
use crate::ed25519::AffineNiels;
use core::arch::asm;
pub mod slot {
pub const X: usize = 0;
pub const Y: usize = 4;
pub const Z: usize = 8;
pub const T: usize = 12;
pub const YPX: usize = 32;
pub const YMX: usize = 36;
pub const NZ: usize = 40;
pub const T2D: usize = 44;
}
pub struct Ws(pub [u64; 64]);
pub struct Ws2(pub [u64; 128]);
macro_rules! block2 {
($ws:expr, $($text:expr),+ $(,)?) => {{
let ws: &mut Ws2 = $ws;
unsafe {
asm!(
".p2align 6",
$($text),+,
in("rdi") ws.0.as_mut_ptr(),
out("rax") _,
out("rcx") _,
out("rdx") _,
out("rsi") _,
out("r8") _,
out("r9") _,
out("r10") _,
out("r11") _,
out("r12") _,
out("r13") _,
out("r14") _,
out("r15") _,
options(nostack),
);
}
}};
}
#[inline(never)]
pub fn madd_ext2(ws: &mut Ws2) {
block2!(
ws,
fe_add_once!(384, 32, 0),
fe_add_once!(896, 544, 512),
fe_sub_once!(416, 32, 0),
fe_sub_once!(928, 544, 512),
fe_mul!(384, 384, 256),
fe_mul!(896, 896, 768),
fe_mul!(416, 416, 288),
fe_mul!(928, 928, 800),
fe_mul!(448, 96, 352),
fe_mul!(960, 608, 864),
fe_add_once!(480, 64, 64),
fe_add_once!(992, 576, 576),
fe_sub_once!(128, 384, 416),
fe_sub_once!(640, 896, 928),
fe_add_once!(160, 384, 416),
fe_add_once!(672, 896, 928),
fe_add_once!(192, 480, 448),
fe_add_once!(704, 992, 960),
fe_sub_once!(224, 480, 448),
fe_sub_once!(736, 992, 960),
fe_mul!(0, 128, 224),
fe_mul!(512, 640, 736),
fe_mul!(32, 160, 192),
fe_mul!(544, 672, 704),
fe_mul!(64, 192, 224),
fe_mul!(576, 704, 736),
fe_mul!(96, 128, 160),
fe_mul!(608, 640, 672),
);
}
macro_rules! block {
($ws:expr, $($text:expr),+ $(,)?) => {{
let ws: &mut Ws = $ws;
unsafe {
asm!(
".p2align 6",
$($text),+,
in("rdi") ws.0.as_mut_ptr(),
out("rax") _,
out("rcx") _,
out("rdx") _,
out("rsi") _,
out("r8") _,
out("r9") _,
out("r10") _,
out("r11") _,
out("r12") _,
out("r13") _,
out("r14") _,
out("r15") _,
options(nostack),
);
}
}};
}
#[inline(never)]
pub fn dbl(ws: &mut Ws) {
block!(
ws,
fe_sqr!(384, 0), fe_sqr!(416, 32), fe_sqr!(448, 64), fe_add_once!(448, 448, 448), fe_add_once!(480, 0, 32), fe_sqr!(480, 480), fe_add_once!(160, 416, 384), fe_sub_once!(192, 416, 384), fe_sub!(128, 480, 160), fe_sub_once!(224, 448, 192), );
}
#[inline(never)]
pub fn to_ext(ws: &mut Ws) {
block!(
ws,
fe_mul!(0, 128, 224), fe_mul!(32, 160, 192), fe_mul!(64, 192, 224), fe_mul!(96, 128, 160), );
}
#[inline(never)]
pub fn to_proj(ws: &mut Ws) {
block!(
ws,
fe_mul!(0, 128, 224), fe_mul!(32, 160, 192), fe_mul!(64, 192, 224), );
}
#[inline(never)]
pub fn madd(ws: &mut Ws) {
block!(
ws,
fe_add_once!(384, 32, 0), fe_sub_once!(416, 32, 0), fe_mul!(384, 384, 256), fe_mul!(416, 416, 288), fe_mul!(448, 96, 352), fe_add_once!(480, 64, 64), fe_sub_once!(128, 384, 416), fe_add_once!(160, 384, 416), fe_add_once!(192, 480, 448), fe_sub_once!(224, 480, 448), );
}
#[inline(never)]
pub fn msub(ws: &mut Ws) {
block!(
ws,
fe_add_once!(384, 32, 0), fe_sub_once!(416, 32, 0), fe_mul!(384, 384, 288), fe_mul!(416, 416, 256), fe_mul!(448, 96, 352), fe_add_once!(480, 64, 64), fe_sub_once!(128, 384, 416), fe_add_once!(160, 384, 416), fe_sub_once!(192, 480, 448), fe_add_once!(224, 480, 448), );
}
#[inline(never)]
pub fn nadd(ws: &mut Ws) {
block!(
ws,
fe_add_once!(384, 32, 0), fe_sub_once!(416, 32, 0), fe_mul!(384, 384, 256), fe_mul!(416, 416, 288), fe_mul!(448, 96, 352), fe_mul!(480, 64, 320), fe_add_once!(480, 480, 480), fe_sub_once!(128, 384, 416), fe_add_once!(160, 384, 416), fe_add_once!(192, 480, 448), fe_sub_once!(224, 480, 448), );
}
#[inline(never)]
pub fn nsub(ws: &mut Ws) {
block!(
ws,
fe_add_once!(384, 32, 0), fe_sub_once!(416, 32, 0), fe_mul!(384, 384, 288), fe_mul!(416, 416, 256), fe_mul!(448, 96, 352), fe_mul!(480, 64, 320), fe_add_once!(480, 480, 480), fe_sub_once!(128, 384, 416), fe_add_once!(160, 384, 416), fe_sub_once!(192, 480, 448), fe_add_once!(224, 480, 448), );
}
#[cfg(feature = "std")]
pub fn limbs(table: &[AffineNiels<super::Fe64>; 8]) -> &[[u64; 12]; 8] {
const _: () = assert!(core::mem::size_of::<[AffineNiels<super::Fe64>; 8]>() == 768);
const _: () = assert!(core::mem::align_of::<AffineNiels<super::Fe64>>() == 8);
unsafe { &*(table as *const [AffineNiels<super::Fe64>; 8]).cast::<[[u64; 12]; 8]>() }
}
macro_rules! sel_entry {
($n:literal, $base:literal, $off:literal, $r0:literal, $r1:literal, $r2:literal, $r3:literal) => {
concat!(
"cmp rcx, ",
$n,
"\n",
"cmove ",
$r0,
", qword ptr [rsi + ",
$base,
" + ",
$off,
"]\n",
"cmove ",
$r1,
", qword ptr [rsi + ",
$base,
" + ",
$off,
" + 8]\n",
"cmove ",
$r2,
", qword ptr [rsi + ",
$base,
" + ",
$off,
" + 16]\n",
"cmove ",
$r3,
", qword ptr [rsi + ",
$base,
" + ",
$off,
" + 24]\n",
)
};
}
macro_rules! sel_pass {
($off:literal, $r0:literal, $r1:literal, $r2:literal, $r3:literal) => {
concat!(
sel_entry!("1", "0", $off, $r0, $r1, $r2, $r3),
sel_entry!("2", "96", $off, $r0, $r1, $r2, $r3),
sel_entry!("3", "192", $off, $r0, $r1, $r2, $r3),
sel_entry!("4", "288", $off, $r0, $r1, $r2, $r3),
sel_entry!("5", "384", $off, $r0, $r1, $r2, $r3),
sel_entry!("6", "480", $off, $r0, $r1, $r2, $r3),
sel_entry!("7", "576", $off, $r0, $r1, $r2, $r3),
sel_entry!("8", "672", $off, $r0, $r1, $r2, $r3),
)
};
}
#[inline(never)]
pub fn select(dst: &mut [u64; 16], table: &[[u64; 12]; 8], digit: i8) {
unsafe {
asm!(
".p2align 6",
"mov rdx, rcx",
"sar rdx, 63",
"xor rcx, rdx",
"sub rcx, rdx",
"mov r8d, 1",
"xor r9d, r9d",
"xor r10d, r10d",
"xor r11d, r11d",
"mov r12d, 1",
"xor r13d, r13d",
"xor r14d, r14d",
"xor r15d, r15d",
sel_pass!("0", "r8", "r9", "r10", "r11"),
sel_pass!("32", "r12", "r13", "r14", "r15"),
"test rdx, rdx",
"mov rax, r8",
"cmovnz r8, r12",
"cmovnz r12, rax",
"mov rax, r9",
"cmovnz r9, r13",
"cmovnz r13, rax",
"mov rax, r10",
"cmovnz r10, r14",
"cmovnz r14, rax",
"mov rax, r11",
"cmovnz r11, r15",
"cmovnz r15, rax",
"mov qword ptr [rdi], r8",
"mov qword ptr [rdi + 8], r9",
"mov qword ptr [rdi + 16], r10",
"mov qword ptr [rdi + 24], r11",
"mov qword ptr [rdi + 32], r12",
"mov qword ptr [rdi + 40], r13",
"mov qword ptr [rdi + 48], r14",
"mov qword ptr [rdi + 56], r15",
"xor r8d, r8d",
"xor r9d, r9d",
"xor r10d, r10d",
"xor r11d, r11d",
sel_pass!("64", "r8", "r9", "r10", "r11"),
"xor r12d, r12d",
"xor r13d, r13d",
"xor r14d, r14d",
"xor r15d, r15d",
"sub r12, r8",
"sbb r13, r9",
"sbb r14, r10",
"sbb r15, r11",
"sbb rax, rax",
"and eax, 38",
"sub r12, rax",
"sbb r13, 0",
"sbb r14, 0",
"sbb r15, 0",
"sbb rax, rax",
"and eax, 38",
"sub r12, rax",
"test rdx, rdx",
"cmovnz r8, r12",
"cmovnz r9, r13",
"cmovnz r10, r14",
"cmovnz r11, r15",
"mov qword ptr [rdi + 96], r8",
"mov qword ptr [rdi + 104], r9",
"mov qword ptr [rdi + 112], r10",
"mov qword ptr [rdi + 120], r11",
in("rdi") dst.as_mut_ptr(),
in("rsi") table.as_ptr(),
inout("rcx") i64::from(digit) => _,
out("rax") _,
out("rdx") _,
out("r8") _,
out("r9") _,
out("r10") _,
out("r11") _,
out("r12") _,
out("r13") _,
out("r14") _,
out("r15") _,
options(nostack),
);
}
}
#[derive(Clone, Copy)]
pub struct Avx2(());
impl Avx2 {
#[inline]
#[cfg_attr(not(feature = "std"), allow(dead_code))]
pub fn get() -> Option<Avx2> {
crate::avx512::avx2().then_some(Avx2(()))
}
}
#[repr(C)]
pub struct Ws2Scan {
pub two: Ws2,
pub ctl: [u64; 8],
}
const _: () = assert!(core::mem::size_of::<Ws2>() == 1024);
const _: () = assert!(core::mem::size_of::<Ws2Scan>() == 1088);
#[cfg(feature = "std")]
pub fn limbs16(table: &[AffineNiels<super::Fe64>; 16]) -> &[[u64; 12]; 16] {
const _: () = assert!(core::mem::size_of::<[AffineNiels<super::Fe64>; 16]>() == 1536);
const _: () = assert!(core::mem::align_of::<AffineNiels<super::Fe64>>() == 8);
unsafe { &*(table as *const [AffineNiels<super::Fe64>; 16]).cast::<[[u64; 12]; 16]>() }
}
macro_rules! scan_setup {
() => {
concat!(
"mov rsi, qword ptr [rdi + 1024]\n",
"mov rax, qword ptr [rdi + 1032]\n",
"mov rdx, rax\n",
"sar rdx, 63\n",
"xor rax, rdx\n",
"sub rax, rdx\n",
"mov qword ptr [rdi + 1048], rdx\n",
"vmovq xmm6, rax\n",
"vpbroadcastq ymm6, xmm6\n",
"mov rax, qword ptr [rdi + 1040]\n",
"mov rdx, rax\n",
"sar rdx, 63\n",
"xor rax, rdx\n",
"sub rax, rdx\n",
"mov qword ptr [rdi + 1056], rdx\n",
"vmovq xmm7, rax\n",
"vpbroadcastq ymm7, xmm7\n",
"vpcmpeqq ymm9, ymm9, ymm9\n",
"vpsrlq ymm9, ymm9, 63\n",
"vmovdqa ymm8, ymm9\n",
"mov eax, 1\n",
"vmovq xmm0, rax\n",
"vmovdqa ymm1, ymm0\n",
"vpxor xmm2, xmm2, xmm2\n",
"vmovdqa ymm3, ymm0\n",
"vmovdqa ymm4, ymm0\n",
"vpxor xmm5, xmm5, xmm5\n",
)
};
}
macro_rules! scan_entry {
($off:literal) => {
concat!(
"vpcmpeqq ymm10, ymm6, ymm8\n",
"vpcmpeqq ymm11, ymm7, ymm8\n",
"vpaddq ymm8, ymm8, ymm9\n",
"vmovdqu ymm12, ymmword ptr [rsi + ",
$off,
"]\n",
"vmovdqu ymm13, ymmword ptr [rsi + ",
$off,
" + 32]\n",
"vmovdqu ymm14, ymmword ptr [rsi + ",
$off,
" + 64]\n",
"vpblendvb ymm0, ymm0, ymm12, ymm10\n",
"vpblendvb ymm1, ymm1, ymm13, ymm10\n",
"vpblendvb ymm2, ymm2, ymm14, ymm10\n",
"vpblendvb ymm3, ymm3, ymm12, ymm11\n",
"vpblendvb ymm4, ymm4, ymm13, ymm11\n",
"vpblendvb ymm5, ymm5, ymm14, ymm11\n",
)
};
}
macro_rules! scan_quarter {
(0) => {
concat!(
"mov rsi, qword ptr [rdi + 1024]\n",
scan_entry!("0"),
scan_entry!("96"),
scan_entry!("192"),
scan_entry!("288"),
)
};
(1) => {
concat!(
"mov rsi, qword ptr [rdi + 1024]\n",
scan_entry!("384"),
scan_entry!("480"),
scan_entry!("576"),
scan_entry!("672"),
)
};
(2) => {
concat!(
"mov rsi, qword ptr [rdi + 1024]\n",
scan_entry!("768"),
scan_entry!("864"),
scan_entry!("960"),
scan_entry!("1056"),
)
};
(3) => {
concat!(
"mov rsi, qword ptr [rdi + 1024]\n",
scan_entry!("1152"),
scan_entry!("1248"),
scan_entry!("1344"),
scan_entry!("1440"),
)
};
}
macro_rules! scan_store {
() => {
concat!(
"vpbroadcastq ymm10, qword ptr [rdi + 1048]\n",
"vpxor ymm12, ymm0, ymm1\n",
"vpand ymm12, ymm12, ymm10\n",
"vpxor ymm0, ymm0, ymm12\n",
"vpxor ymm1, ymm1, ymm12\n",
"vmovdqu ymmword ptr [rdi + 256], ymm0\n",
"vmovdqu ymmword ptr [rdi + 288], ymm1\n",
"vmovdqu ymmword ptr [rdi + 352], ymm2\n",
"vpbroadcastq ymm11, qword ptr [rdi + 1056]\n",
"vpxor ymm12, ymm3, ymm4\n",
"vpand ymm12, ymm12, ymm11\n",
"vpxor ymm3, ymm3, ymm12\n",
"vpxor ymm4, ymm4, ymm12\n",
"vmovdqu ymmword ptr [rdi + 768], ymm3\n",
"vmovdqu ymmword ptr [rdi + 800], ymm4\n",
"vmovdqu ymmword ptr [rdi + 864], ymm5\n",
)
};
}
macro_rules! scan_negate {
($t:literal, $sign:literal) => {
concat!(
"mov r8, qword ptr [rdi + ",
$t,
"]\n",
"mov r9, qword ptr [rdi + ",
$t,
" + 8]\n",
"mov r10, qword ptr [rdi + ",
$t,
" + 16]\n",
"mov r11, qword ptr [rdi + ",
$t,
" + 24]\n",
"xor r12d, r12d\n",
"xor r13d, r13d\n",
"xor r14d, r14d\n",
"xor r15d, r15d\n",
"sub r12, r8\n",
"sbb r13, r9\n",
"sbb r14, r10\n",
"sbb r15, r11\n",
"sbb rax, rax\n",
"and eax, 38\n",
"sub r12, rax\n",
"sbb r13, 0\n",
"sbb r14, 0\n",
"sbb r15, 0\n",
"sbb rax, rax\n",
"and eax, 38\n",
"sub r12, rax\n",
"mov rdx, qword ptr [rdi + ",
$sign,
"]\n",
"test rdx, rdx\n",
"cmovnz r8, r12\n",
"cmovnz r9, r13\n",
"cmovnz r10, r14\n",
"cmovnz r11, r15\n",
"mov qword ptr [rdi + ",
$t,
"], r8\n",
"mov qword ptr [rdi + ",
$t,
" + 8], r9\n",
"mov qword ptr [rdi + ",
$t,
" + 16], r10\n",
"mov qword ptr [rdi + ",
$t,
" + 24], r11\n",
)
};
}
macro_rules! block_scan {
($ws:expr, $($text:expr),+ $(,)?) => {{
let ws: *mut Ws2Scan = $ws;
unsafe {
asm!(
".p2align 6",
$($text),+,
"vzeroall",
in("rdi") ws.cast::<u64>(),
out("rax") _,
out("rcx") _,
out("rdx") _,
out("rsi") _,
out("r8") _,
out("r9") _,
out("r10") _,
out("r11") _,
out("r12") _,
out("r13") _,
out("r14") _,
out("r15") _,
out("ymm0") _,
out("ymm1") _,
out("ymm2") _,
out("ymm3") _,
out("ymm4") _,
out("ymm5") _,
out("ymm6") _,
out("ymm7") _,
out("ymm8") _,
out("ymm9") _,
out("ymm10") _,
out("ymm11") _,
out("ymm12") _,
out("ymm13") _,
out("ymm14") _,
out("ymm15") _,
options(nostack),
);
}
}};
}
pub fn scan(t: Avx2, ws: &mut Ws2Scan, table: &[[u64; 12]; 16], d0: i8, d1: i8) {
set_scan(ws, table, d0, d1);
unsafe { scan_avx2(t, ws) }
}
pub fn madd_ext2_scan(t: Avx2, ws: &mut Ws2Scan, table: &[[u64; 12]; 16], d0: i8, d1: i8) {
set_scan(ws, table, d0, d1);
unsafe { madd_ext2_scan_avx2(t, ws) }
}
fn set_scan(ws: &mut Ws2Scan, table: &[[u64; 12]; 16], d0: i8, d1: i8) {
ws.ctl[0] = table.as_ptr() as u64;
ws.ctl[1] = i64::from(d0) as u64;
ws.ctl[2] = i64::from(d1) as u64;
}
#[inline(never)]
#[target_feature(enable = "avx2")]
unsafe fn scan_avx2(_: Avx2, ws: &mut Ws2Scan) {
block_scan!(
ws,
scan_setup!(),
scan_quarter!(0),
scan_quarter!(1),
scan_quarter!(2),
scan_quarter!(3),
scan_store!(),
scan_negate!(352, 1048),
scan_negate!(864, 1056),
);
}
#[inline(never)]
#[target_feature(enable = "avx2")]
unsafe fn madd_ext2_scan_avx2(_: Avx2, ws: &mut Ws2Scan) {
block_scan!(
ws,
scan_setup!(),
scan_quarter!(0),
fe_add_once!(384, 32, 0),
scan_quarter!(1),
fe_add_once!(896, 544, 512),
scan_quarter!(2),
fe_sub_once!(416, 32, 0),
scan_quarter!(3),
fe_sub_once!(928, 544, 512),
fe_mul!(384, 384, 256),
fe_mul!(896, 896, 768),
fe_mul!(416, 416, 288),
fe_mul!(928, 928, 800),
fe_mul!(448, 96, 352),
fe_mul!(960, 608, 864),
scan_store!(),
fe_add_once!(480, 64, 64),
fe_add_once!(992, 576, 576),
scan_negate!(352, 1048),
fe_sub_once!(128, 384, 416),
fe_sub_once!(640, 896, 928),
fe_add_once!(160, 384, 416),
fe_add_once!(672, 896, 928),
scan_negate!(864, 1056),
fe_add_once!(192, 480, 448),
fe_add_once!(704, 992, 960),
fe_sub_once!(224, 480, 448),
fe_sub_once!(736, 992, 960),
fe_mul!(0, 128, 224),
fe_mul!(512, 640, 736),
fe_mul!(32, 160, 192),
fe_mul!(544, 672, 704),
fe_mul!(64, 192, 224),
fe_mul!(576, 704, 736),
fe_mul!(96, 128, 160),
fe_mul!(608, 640, 672),
);
}
#[cfg(feature = "ct-probes")]
pub mod probes {
use super::{Avx2, Ws2Scan};
#[inline(always)]
pub unsafe fn scan(ws: &mut Ws2Scan, table: &[[u64; 12]; 16], d0: i8, d1: i8) {
super::scan(Avx2(()), ws, table, d0, d1)
}
#[inline(always)]
pub unsafe fn madd_ext2_scan(ws: &mut Ws2Scan, table: &[[u64; 12]; 16], d0: i8, d1: i8) {
super::madd_ext2_scan(Avx2(()), ws, table, d0, d1)
}
}