/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* Multiplication performance enhancements for sparc v8+vis CPUs. */ #include "mpi-priv.h" #include #include #include /* In the functions below, */ /* vector y must be 8-byte aligned, and n must be even */ /* returns carry out of high order word of result */ /* maximum n is 256 */ /* vector x += vector y * scaler a; where y is of length n words. */ extern mp_digit mul_add_inp(mp_digit *x, const mp_digit *y, int n, mp_digit a); /* vector z = vector x + vector y * scaler a; where y is of length n words. */ extern mp_digit mul_add(mp_digit *z, const mp_digit *x, const mp_digit *y, int n, mp_digit a); /* v8 versions of these functions run on any Sparc v8 CPU. */ /* This trick works on Sparc V8 CPUs with the Workshop compilers. */ #define MP_MUL_DxD(a, b, Phi, Plo) \ { unsigned long long product = (unsigned long long)a * b; \ Plo = (mp_digit)product; \ Phi = (mp_digit)(product >> MP_DIGIT_BIT); } /* c = a * b */ static void v8_mpv_mul_d(const mp_digit *a, mp_size a_len, mp_digit b, mp_digit *c) { #if !defined(MP_NO_MP_WORD) mp_digit d = 0; /* Inner product: Digits of a */ while (a_len--) { mp_word w = ((mp_word)b * *a++) + d; *c++ = ACCUM(w); d = CARRYOUT(w); } *c = d; #else mp_digit carry = 0; while (a_len--) { mp_digit a_i = *a++; mp_digit a0b0, a1b1; MP_MUL_DxD(a_i, b, a1b1, a0b0); a0b0 += carry; if (a0b0 < carry) ++a1b1; *c++ = a0b0; carry = a1b1; } *c = carry; #endif } /* c += a * b */ static void v8_mpv_mul_d_add(const mp_digit *a, mp_size a_len, mp_digit b, mp_digit *c) { #if !defined(MP_NO_MP_WORD) mp_digit d = 0; /* Inner product: Digits of a */ while (a_len--) { mp_word w = ((mp_word)b * *a++) + *c + d; *c++ = ACCUM(w); d = CARRYOUT(w); } *c = d; #else mp_digit carry = 0; while (a_len--) { mp_digit a_i = *a++; mp_digit a0b0, a1b1; MP_MUL_DxD(a_i, b, a1b1, a0b0); a0b0 += carry; if (a0b0 < carry) ++a1b1; a0b0 += a_i = *c; if (a0b0 < a_i) ++a1b1; *c++ = a0b0; carry = a1b1; } *c = carry; #endif } /* Presently, this is only used by the Montgomery arithmetic code. */ /* c += a * b */ static void v8_mpv_mul_d_add_prop(const mp_digit *a, mp_size a_len, mp_digit b, mp_digit *c) { #if !defined(MP_NO_MP_WORD) mp_digit d = 0; /* Inner product: Digits of a */ while (a_len--) { mp_word w = ((mp_word)b * *a++) + *c + d; *c++ = ACCUM(w); d = CARRYOUT(w); } while (d) { mp_word w = (mp_word)*c + d; *c++ = ACCUM(w); d = CARRYOUT(w); } #else mp_digit carry = 0; while (a_len--) { mp_digit a_i = *a++; mp_digit a0b0, a1b1; MP_MUL_DxD(a_i, b, a1b1, a0b0); a0b0 += carry; if (a0b0 < carry) ++a1b1; a0b0 += a_i = *c; if (a0b0 < a_i) ++a1b1; *c++ = a0b0; carry = a1b1; } while (carry) { mp_digit c_i = *c; carry += c_i; *c++ = carry; carry = carry < c_i; } #endif } /* These functions run only on v8plus+vis or v9+vis CPUs. */ /* c = a * b */ void s_mpv_mul_d(const mp_digit *a, mp_size a_len, mp_digit b, mp_digit *c) { mp_digit d; mp_digit x[258]; if (a_len <= 256) { if (a == c || ((ptrdiff_t)a & 0x7) != 0 || (a_len & 1) != 0) { mp_digit * px; px = (((ptrdiff_t)x & 0x7) != 0) ? x + 1 : x; memcpy(px, a, a_len * sizeof(*a)); a = px; if (a_len & 1) { px[a_len] = 0; } } s_mp_setz(c, a_len + 1); d = mul_add_inp(c, a, a_len, b); c[a_len] = d; } else { v8_mpv_mul_d(a, a_len, b, c); } } /* c += a * b, where a is a_len words long. */ void s_mpv_mul_d_add(const mp_digit *a, mp_size a_len, mp_digit b, mp_digit *c) { mp_digit d; mp_digit x[258]; if (a_len <= 256) { if (((ptrdiff_t)a & 0x7) != 0 || (a_len & 1) != 0) { mp_digit * px; px = (((ptrdiff_t)x & 0x7) != 0) ? x + 1 : x; memcpy(px, a, a_len * sizeof(*a)); a = px; if (a_len & 1) { px[a_len] = 0; } } d = mul_add_inp(c, a, a_len, b); c[a_len] = d; } else { v8_mpv_mul_d_add(a, a_len, b, c); } } /* c += a * b, where a is y words long. */ void s_mpv_mul_d_add_prop(const mp_digit *a, mp_size a_len, mp_digit b, mp_digit *c) { mp_digit d; mp_digit x[258]; if (a_len <= 256) { if (((ptrdiff_t)a & 0x7) != 0 || (a_len & 1) != 0) { mp_digit * px; px = (((ptrdiff_t)x & 0x7) != 0) ? x + 1 : x; memcpy(px, a, a_len * sizeof(*a)); a = px; if (a_len & 1) { px[a_len] = 0; } } d = mul_add_inp(c, a, a_len, b); if (d) { c += a_len; do { mp_digit sum = d + *c; *c++ = sum; d = sum < d; } while (d); } } else { v8_mpv_mul_d_add_prop(a, a_len, b, c); } }