From: Chris Duncan Date: Mon, 28 Sep 2026 15:02:10 +0000 (-0700) Subject: Improve performance by eliminating extra load/store calls when loading base point... X-Git-Url: https://git.codecow.com/?a=commitdiff_plain;h=1bd9f4847dbe2e828f0c31bed4df52b8abc78a43;p=nano25519.git Improve performance by eliminating extra load/store calls when loading base point precomputed values. --- diff --git a/src/assembly/ed25519/ge.ts b/src/assembly/ed25519/ge.ts index d2b6a23..37e3f67 100644 --- a/src/assembly/ed25519/ge.ts +++ b/src/assembly/ed25519/ge.ts @@ -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([ -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(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(t.yplusx), bp, iEqualsBabs) - fe_memcmov(changetype(t.yminusx), bp + 48, iEqualsBabs) - fe_memcmov(changetype(t.xy2d), bp + 96, iEqualsBabs) + const isbabs: i8 = (((babs ^ i) - 1) >> 7) & 1 /* xor == 0: yes; xor == 1..255: no */ + const c = v128.splat(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(minust), changetype(t.yminusx)) - fe_memcopy(changetype(minust) + 48, changetype(t.yplusx)) - fe_memneg(changetype(minust) + 96, changetype(t.xy2d)) - - fe_memcmov(changetype(t.yplusx), changetype(minust), bnegative) - fe_memcmov(changetype(t.yminusx), changetype(minust) + 48, bnegative) - fe_memcmov(changetype(t.xy2d), changetype(minust) + 96, bnegative) - - minust.fill(0) + // -(x, y) == (-x, y); for -x, y+x <-> y-x and negate xy2d + const n = v128.splat(0 - u64(isbneg)) + const tp = changetype(t.yplusx) + const tm = changetype(t.yminusx) + const td = changetype(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(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(tm, v128.bitselect(p2, m2, n), 0, 32) + + // xy2d = -xy2d if n, else xy2d + v128.store(td, v128.bitselect(v128.neg(d0), d0, n)) + v128.store(td, v128.bitselect(v128.neg(d1), d1, n), 16) + v128.store_lane(td, v128.bitselect(v128.neg(d2), d2, n), 0, 32) } const u: FieldElement = fe()