From: Chris Duncan Date: Sun, 4 Oct 2026 06:40:07 +0000 (-0700) Subject: Revert to scalar carry for multiplication. X-Git-Url: https://git.codecow.com/?a=commitdiff_plain;h=16f77a420695a830435a1b3d790b59596f180527;p=nano25519.git Revert to scalar carry for multiplication. --- diff --git a/src/assembly/ed25519/fe.ts b/src/assembly/ed25519/fe.ts index ec0660e..919a414 100644 --- a/src/assembly/ed25519/fe.ts +++ b/src/assembly/ed25519/fe.ts @@ -430,65 +430,89 @@ export function fe_mul (h: FieldElement, f: FieldElement, g: FieldElement): void h56 = i64x2.add(h56, t3) h78 = i64x2.add(h78, t4) - // sum related accumulators and shuffle into ref10 carry schedule - let h04_sum = v128.add(v128.shuffle(h01, h45, 0, 2), v128.shuffle(h90, h34, 1, 3)) - let h15_sum = v128.add(v128.shuffle(h12, h56, 0, 2), v128.shuffle(h01, h45, 1, 3)) - let h26_sum = v128.add(v128.shuffle(h23, h67, 0, 2), v128.shuffle(h12, h56, 1, 3)) - let h37_sum = v128.add(v128.shuffle(h34, h78, 0, 2), v128.shuffle(h23, h67, 1, 3)) - let h89_sum = v128.add(v128.shuffle(h89, h90, 0, 2), v128.shuffle(h78, h89, 1, 3)) - - // Signed i64x2 shifts are emulated on x64, unsigned ones are native. Adding - // 2⁶² keeps every sum non-negative (|h| ≤ 2⁶⁰·⁴), and the unsigned shift then - // floors in fewer instructions. The bias is removed by subtraction, and the - // value to remove is the bias minus the original size: 2⁶²⁻²⁶ = 2³⁶ (or - // 2⁶²⁻²⁵ = 2³⁷). After unbiasing, the result is the signed carry. - const bias24 = v128.splat((1 << 24) + (1 << 62)) - const bias25 = v128.splat((1 << 25) + (1 << 62)) - const unbias25 = v128.splat(1 << 37) - const unbias26 = v128.splat(1 << 36) - let carryA: v128 - let carryB: v128 - - carryA = v128.sub(v128.shr(v128.add(h04_sum, bias25), 26), unbias26) - h04_sum = v128.sub(h04_sum, v128.shl(carryA, 26)) - h15_sum = v128.add(h15_sum, carryA) - - carryB = v128.sub(v128.shr(v128.add(h15_sum, bias24), 25), unbias25) - h15_sum = v128.sub(h15_sum, v128.shl(carryB, 25)) - h26_sum = v128.add(h26_sum, carryB) - - carryA = v128.sub(v128.shr(v128.add(h26_sum, bias25), 26), unbias26) - h26_sum = v128.sub(h26_sum, v128.shl(carryA, 26)) - h37_sum = v128.add(h37_sum, carryA) - - carryB = v128.sub(v128.shr(v128.add(h37_sum, bias24), 25), unbias25) - h37_sum = v128.sub(h37_sum, v128.shl(carryB, 25)) - let h48_sum = v128.add(v128.shuffle(h04_sum, h89_sum, 1, 2), carryB) - - carryA = v128.sub(v128.shr(v128.add(h48_sum, bias25), 26), unbias26) - h48_sum = v128.sub(h48_sum, v128.shl(carryA, 26)) - let h59_sum = v128.add(v128.shuffle(h15_sum, h89_sum, 1, 3), carryA) - - carryB = v128.sub(v128.shr(v128.add(h59_sum, bias24), 25), unbias25) - h59_sum = v128.sub(h59_sum, v128.shl(carryB, 25)) - // wrap requires multiplying carry from h9 by 19 - let h60_sum = v128.add(v128.shuffle(h26_sum, h04_sum, 1, 2), v128.mul(carryB, i64x2(1, 19))) - - carryA = v128.sub(v128.shr(v128.add(h60_sum, bias25), 26), unbias26) - h60_sum = v128.sub(h60_sum, v128.shl(carryA, 26)) - let h71_sum = v128.add(v128.shuffle(h37_sum, h15_sum, 1, 2), carryA) - - // assign scalar results to output - v128.store_lane(h_ptr, h60_sum, 2, 0) - v128.store_lane(h_ptr, h71_sum, 2, 4) - v128.store_lane(h_ptr, h26_sum, 0, 8) - v128.store_lane(h_ptr, h37_sum, 0, 12) - v128.store_lane(h_ptr, h48_sum, 0, 16) - v128.store_lane(h_ptr, h59_sum, 0, 20) - v128.store_lane(h_ptr, h60_sum, 0, 24) - v128.store_lane(h_ptr, h71_sum, 0, 28) - v128.store_lane(h_ptr, h48_sum, 2, 32) - v128.store_lane(h_ptr, h59_sum, 2, 36) + // extract scalars from vectors + let h0: i64 = i64x2.extract_lane(h90, 1) + i64x2.extract_lane(h01, 0) + let h1: i64 = i64x2.extract_lane(h01, 1) + i64x2.extract_lane(h12, 0) + let h2: i64 = i64x2.extract_lane(h12, 1) + i64x2.extract_lane(h23, 0) + let h3: i64 = i64x2.extract_lane(h23, 1) + i64x2.extract_lane(h34, 0) + let h4: i64 = i64x2.extract_lane(h34, 1) + i64x2.extract_lane(h45, 0) + let h5: i64 = i64x2.extract_lane(h45, 1) + i64x2.extract_lane(h56, 0) + let h6: i64 = i64x2.extract_lane(h56, 1) + i64x2.extract_lane(h67, 0) + let h7: i64 = i64x2.extract_lane(h67, 1) + i64x2.extract_lane(h78, 0) + let h8: i64 = i64x2.extract_lane(h78, 1) + i64x2.extract_lane(h89, 0) + let h9: i64 = i64x2.extract_lane(h89, 1) + i64x2.extract_lane(h90, 0) + + // reduce scalars with carry + let carry0: i64 + let carry1: i64 + let carry2: i64 + let carry3: i64 + let carry4: i64 + let carry5: i64 + let carry6: i64 + let carry7: i64 + let carry8: i64 + let carry9: i64 + + carry0 = (h0 + (1 << 25)) >> 26 + h0 -= carry0 << 26 + h1 += carry0 + + carry4 = (h4 + (1 << 25)) >> 26 + h4 -= carry4 << 26 + h5 += carry4 + + carry1 = (h1 + (1 << 24)) >> 25 + h1 -= carry1 << 25 + h2 += carry1 + + carry5 = (h5 + (1 << 24)) >> 25 + h5 -= carry5 << 25 + h6 += carry5 + + carry2 = (h2 + (1 << 25)) >> 26 + h2 -= carry2 << 26 + h3 += carry2 + + carry6 = (h6 + (1 << 25)) >> 26 + h6 -= carry6 << 26 + h7 += carry6 + + carry3 = (h3 + (1 << 24)) >> 25 + h3 -= carry3 << 25 + h4 += carry3 + + carry7 = (h7 + (1 << 24)) >> 25 + h7 -= carry7 << 25 + h8 += carry7 + + carry4 = (h4 + (1 << 25)) >> 26 + h4 -= carry4 << 26 + h5 += carry4 + + carry8 = (h8 + (1 << 25)) >> 26 + h8 -= carry8 << 26 + h9 += carry8 + + carry9 = (h9 + (1 << 24)) >> 25 + h9 -= carry9 << 25 + h0 += carry9 * 19 + + carry0 = (h0 + (1 << 25)) >> 26 + h0 -= carry0 << 26 + h1 += carry0 + + // assign results to output + store(h_ptr, h0, 0) + store(h_ptr, h1, 4) + store(h_ptr, h2, 8) + store(h_ptr, h3, 12) + store(h_ptr, h4, 16) + store(h_ptr, h5, 20) + store(h_ptr, h6, 24) + store(h_ptr, h7, 28) + store(h_ptr, h8, 32) + store(h_ptr, h9, 36) } /**