]> git.codecow.com Git - nano25519.git/commitdiff
Improve performance by eliminating extra load/store calls when loading base point...
authorChris Duncan <chris@codecow.com>
Mon, 28 Sep 2026 15:02:10 +0000 (08:02 -0700)
committerChris Duncan <chris@codecow.com>
Mon, 28 Sep 2026 15:02:10 +0000 (08:02 -0700)
src/assembly/ed25519/ge.ts

index d2b6a233e51448469bd7879748738893b152f4d5..37e3f67b0be1f52a5e4caab9585d107dd1b85c98 100644 (file)
@@ -18,7 +18,7 @@
 */
 import { base } from './base'
 import { base2 } from './base2'
-import { FieldElement, fe, fe_1, fe_add, fe_cmov, fe_frombytes, fe_isnegative, fe_iszero, fe_memcmov, fe_memcopy, fe_memneg, fe_mul, fe_neg, fe_pow22523, fe_sq, fe_sub } from './fe'
+import { FieldElement, fe, fe_1, fe_add, fe_cmov, fe_frombytes, fe_isnegative, fe_iszero, fe_mul, fe_neg, fe_pow22523, fe_sq, fe_sub } from './fe'
 import { ge_add_cached, ge_add_precomp, ge_cached, ge_p1p1, ge_p1p1_0, ge_p1p1_to_p2, ge_p1p1_to_p3, ge_p2, ge_p2_0, ge_p2_dbl, ge_p2_to_p3, ge_p3, ge_p3_0, ge_p3_dbl, ge_p3_to_cached, ge_p3_tobytes, ge_precomp, ge_precomp_0, ge_sub_cached, ge_sub_precomp } from './p'
 
 /**
@@ -36,34 +36,55 @@ const ed25519_d: FieldElement = StaticArray.fromArray<i32>([
        -10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116, 0, 0
 ])
 
-/** Scratch array equivalent to a `ge_precomp`: 3x 12-limb FieldElements */
-const minust = new StaticArray<i32>(36)
 //@ts-expect-error
 @inline
 function ge_cmov8_base (t: ge_precomp, bp: usize, b: i8): void {
-       const bnegative: i8 = b >> 7 /* retains only the sign bit of `b` */
-       const babs: i8 = b - (((-bnegative) & b) * (i8(1) << 1))
+       const isbneg: i8 = (b >> 7) & 1 /* 0: positive; 1: negative */
+       const babs: i8 = b - (((-isbneg) & b) * (i8(1) << 1))
 
-       ge_precomp_0(t)
+       // identity: (1, 1, 0)
+       // yplusx = 1
+       let p0 = i32x4(1, 0, 0, 0), p1 = i32x4(0, 0, 0, 0), p2 = i32x4(0, 0, 0, 0)
+       // yminusx = 1
+       let m0 = i32x4(1, 0, 0, 0), m1 = i32x4(0, 0, 0, 0), m2 = i32x4(0, 0, 0, 0)
+       // xy2d = 0
+       let d0 = i32x4(0, 0, 0, 0), d1 = i32x4(0, 0, 0, 0), d2 = i32x4(0, 0, 0, 0)
 
        // base pointer offset += 144n = (4 bytes * 12 values * 3 fields) * n
-       // for values 1..8, conditionally move if `abs(b)` equals the current index
        for (let i: i8 = 1; i <= 8; i++, bp += 144) {
-               const iEqualsBabs: i8 = ((babs ^ i) - 1) >> 7 /* xor == 0: yes */
-               fe_memcmov(changetype<usize>(t.yplusx), bp, iEqualsBabs)
-               fe_memcmov(changetype<usize>(t.yminusx), bp + 48, iEqualsBabs)
-               fe_memcmov(changetype<usize>(t.xy2d), bp + 96, iEqualsBabs)
+               const isbabs: i8 = (((babs ^ i) - 1) >> 7) & 1 /* xor == 0: yes; xor == 1..255: no */
+               const c = v128.splat<u64>(0 - u64(isbabs))
+               p0 = v128.bitselect(v128.load(bp), p0, c)
+               p1 = v128.bitselect(v128.load(bp, 16), p1, c)
+               p2 = v128.bitselect(v128.load(bp, 32), p2, c)
+               m0 = v128.bitselect(v128.load(bp, 48), m0, c)
+               m1 = v128.bitselect(v128.load(bp, 64), m1, c)
+               m2 = v128.bitselect(v128.load(bp, 80), m2, c)
+               d0 = v128.bitselect(v128.load(bp, 96), d0, c)
+               d1 = v128.bitselect(v128.load(bp, 112), d1, c)
+               d2 = v128.bitselect(v128.load(bp, 128), d2, c)
        }
 
-       fe_memcopy(changetype<usize>(minust), changetype<usize>(t.yminusx))
-       fe_memcopy(changetype<usize>(minust) + 48, changetype<usize>(t.yplusx))
-       fe_memneg(changetype<usize>(minust) + 96, changetype<usize>(t.xy2d))
-
-       fe_memcmov(changetype<usize>(t.yplusx), changetype<usize>(minust), bnegative)
-       fe_memcmov(changetype<usize>(t.yminusx), changetype<usize>(minust) + 48, bnegative)
-       fe_memcmov(changetype<usize>(t.xy2d), changetype<usize>(minust) + 96, bnegative)
-
-       minust.fill(0)
+       // -(x, y) == (-x, y); for -x, y+x <-> y-x and negate xy2d
+       const n = v128.splat<u64>(0 - u64(isbneg))
+       const tp = changetype<usize>(t.yplusx)
+       const tm = changetype<usize>(t.yminusx)
+       const td = changetype<usize>(t.xy2d)
+
+       // y+x = y-x if n, else y+x
+       v128.store(tp, v128.bitselect(m0, p0, n))
+       v128.store(tp, v128.bitselect(m1, p1, n), 16)
+       v128.store_lane<u64>(tp, v128.bitselect(m2, p2, n), 0, 32)
+
+       // y-x = y+x if n, else y-x
+       v128.store(tm, v128.bitselect(p0, m0, n))
+       v128.store(tm, v128.bitselect(p1, m1, n), 16)
+       v128.store_lane<u64>(tm, v128.bitselect(p2, m2, n), 0, 32)
+
+       // xy2d = -xy2d if n, else xy2d
+       v128.store(td, v128.bitselect(v128.neg<i32>(d0), d0, n))
+       v128.store(td, v128.bitselect(v128.neg<i32>(d1), d1, n), 16)
+       v128.store_lane<u64>(td, v128.bitselect(v128.neg<i32>(d2), d2, n), 0, 32)
 }
 
 const u: FieldElement = fe()