This combines the following upstream commits: e45af510bc AArch64: Fix instability in AdvSIMD sinh 6c22823da5 AArch64: Fix instability in AdvSIMD tan aebaeb2c33 AArch64: Update math-vector-fortran.h e20ca759af AArch64: add optimised strspn/strcspn aac077645a AArch64: Fix SVE powf routine [BZ #33299] 1e3d1ddf97 AArch64: Optimize SVE exp functions dee22d2a81 AArch64: Optimise SVE FP64 Hyperbolics 6849c5b791 AArch64: Improve codegen SVE log1p helper 09795c5612 AArch64: Fix builderror with GCC 12.1/12.2 aa18367c11 AArch64: Improve enabling of SVE for libmvec 691edbdf77 aarch64: fix unwinding in longjmp 4352e2cc93 aarch64: Fix _dl_tlsdesc_dynamic unwind for pac-ret (BZ 32612) cf56eb28fa AArch64: Optimize algorithm in users of SVE expf helper ce2f26a22e AArch64: Remove PTR_ARG/SIZE_ARG defines 8f0e7fe61e Aarch64: Improve codegen in SVE asinh c0ff447edf Aarch64: Improve codegen in SVE exp and users, and update expf_inline f5ff34cb3c AArch64: Improve codegen for SVE erfcf 0b195651db AArch64: Improve codegen for SVE pow 95e807209b AArch64: Improve codegen for SVE powf d3f2b71ef1 aarch64: Fix tests not compatible with targets supporting GCS f86b4cf875 AArch64: Improve codegen in SVE expm1f and users 140b985e5a AArch64: Improve codegen in AdvSIMD asinh 91c1fadba3 AArch64: Improve codegen for SVE log1pf users cff9648d0b AArch64: Improve codegen of AdvSIMD expf family 569cfaaf49 AArch64: Improve codegen in AdvSIMD pow ca0c0d0f26 AArch64: Improve codegen in users of ADVSIMD log1p helper 13a7ef5999 AArch64: Improve codegen in users of ADVSIMD expm1 helper 2d82d781a5 AArch64: Remove SVE erf and erfc tables 1cf29fbc5b AArch64: Small optimisation in AdvSIMD erf and erfc 7b8c134b54 AArch64: Improve codegen in SVE expf & related routines a15b1394b5 AArch64: Improve codegen in SVE F32 logs 5bc100bd4b AArch64: Improve codegen in users of AdvSIMD log1pf helper 7900ac490d AArch64: Improve codegen in users of ADVSIMD expm1f helper 0fed0b250f aarch64/fpu: Add vector variants of pow 75207bde68 aarch64/fpu: Add vector variants of cbrt 157f89fa3d aarch64/fpu: Add vector variants of hypot 90a6ca8b28 aarch64: Fix AdvSIMD libmvec routines for big-endian 87cb1dfcd6 aarch64/fpu: Add vector variants of erfc 3d3a4fb8e4 aarch64/fpu: Add vector variants of tanh eedbbca0bf aarch64/fpu: Add vector variants of sinh 8b67920528 aarch64/fpu: Add vector variants of atanh 81406ea3c5 aarch64/fpu: Add vector variants of asinh b09fee1d21 aarch64/fpu: Add vector variants of acosh bdb5705b7b aarch64/fpu: Add vector variants of cosh cb5d84f1f8 aarch64/fpu: Add vector variants of erf Resolves: RHEL-118273
522 lines
20 KiB
Diff
522 lines
20 KiB
Diff
commit 1e3d1ddf977ecd653de8d0d10eb083d80ac21cf3
|
|
Author: Dylan Fleming <Dylan.Fleming@arm.com>
|
|
Date: Wed Jun 18 16:17:12 2025 +0000
|
|
|
|
AArch64: Optimize SVE exp functions
|
|
|
|
Improve performance of SVE exps by making better use
|
|
of the SVE FEXPA instruction.
|
|
|
|
Performance improvement on Neoverse V1:
|
|
exp2_sve: 21%
|
|
exp2f_sve: 24%
|
|
exp10f_sve: 23%
|
|
expm1_sve: 25%
|
|
|
|
Reviewed-by: Wilco Dijkstra <Wilco.Dijkstra@arm.com>
|
|
|
|
diff --git a/sysdeps/aarch64/fpu/exp10f_sve.c b/sysdeps/aarch64/fpu/exp10f_sve.c
|
|
index 8aa3fa9c4335cfb8..0a4c26450601a1db 100644
|
|
--- a/sysdeps/aarch64/fpu/exp10f_sve.c
|
|
+++ b/sysdeps/aarch64/fpu/exp10f_sve.c
|
|
@@ -19,26 +19,19 @@
|
|
|
|
#include "sv_math.h"
|
|
|
|
-/* For x < -Thres, the result is subnormal and not handled correctly by
|
|
- FEXPA. */
|
|
-#define Thres 37.9
|
|
+/* For x < -Thres (-log10(2^126)), the result is subnormal and not handled
|
|
+ correctly by FEXPA. */
|
|
+#define Thres 0x1.2f702p+5
|
|
|
|
static const struct data
|
|
{
|
|
- float log2_10_lo, c0, c2, c4;
|
|
- float c1, c3, log10_2;
|
|
- float shift, log2_10_hi, thres;
|
|
+ float log10_2, log2_10_hi, log2_10_lo, c1;
|
|
+ float c0, shift, thres;
|
|
} data = {
|
|
/* Coefficients generated using Remez algorithm with minimisation of relative
|
|
- error.
|
|
- rel error: 0x1.89dafa3p-24
|
|
- abs error: 0x1.167d55p-23 in [-log10(2)/2, log10(2)/2]
|
|
- maxerr: 0.52 +0.5 ulp. */
|
|
- .c0 = 0x1.26bb16p+1f,
|
|
- .c1 = 0x1.5350d2p+1f,
|
|
- .c2 = 0x1.04744ap+1f,
|
|
- .c3 = 0x1.2d8176p+0f,
|
|
- .c4 = 0x1.12b41ap-1f,
|
|
+ error. */
|
|
+ .c0 = 0x1.26bb62p1,
|
|
+ .c1 = 0x1.53524cp1,
|
|
/* 1.5*2^17 + 127, a shift value suitable for FEXPA. */
|
|
.shift = 0x1.803f8p17f,
|
|
.log10_2 = 0x1.a934fp+1,
|
|
@@ -53,28 +46,23 @@ sv_exp10f_inline (svfloat32_t x, const svbool_t pg, const struct data *d)
|
|
/* exp10(x) = 2^(n/N) * 10^r = 2^n * (1 + poly (r)),
|
|
with poly(r) in [1/sqrt(2), sqrt(2)] and
|
|
x = r + n * log10(2) / N, with r in [-log10(2)/2N, log10(2)/2N]. */
|
|
-
|
|
- svfloat32_t lane_consts = svld1rq (svptrue_b32 (), &d->log2_10_lo);
|
|
+ svfloat32_t lane_consts = svld1rq (svptrue_b32 (), &d->log10_2);
|
|
|
|
/* n = round(x/(log10(2)/N)). */
|
|
svfloat32_t shift = sv_f32 (d->shift);
|
|
- svfloat32_t z = svmad_x (pg, sv_f32 (d->log10_2), x, shift);
|
|
- svfloat32_t n = svsub_x (svptrue_b32 (), z, shift);
|
|
+ svfloat32_t z = svmla_lane (shift, x, lane_consts, 0);
|
|
+ svfloat32_t n = svsub_x (pg, z, shift);
|
|
|
|
/* r = x - n*log10(2)/N. */
|
|
- svfloat32_t r = svmsb_x (pg, sv_f32 (d->log2_10_hi), n, x);
|
|
- r = svmls_lane (r, n, lane_consts, 0);
|
|
+ svfloat32_t r = x;
|
|
+ r = svmls_lane (r, n, lane_consts, 1);
|
|
+ r = svmls_lane (r, n, lane_consts, 2);
|
|
|
|
svfloat32_t scale = svexpa (svreinterpret_u32 (z));
|
|
|
|
/* Polynomial evaluation: poly(r) ~ exp10(r)-1. */
|
|
- svfloat32_t p12 = svmla_lane (sv_f32 (d->c1), r, lane_consts, 2);
|
|
- svfloat32_t p34 = svmla_lane (sv_f32 (d->c3), r, lane_consts, 3);
|
|
- svfloat32_t r2 = svmul_x (svptrue_b32 (), r, r);
|
|
- svfloat32_t p14 = svmla_x (pg, p12, p34, r2);
|
|
- svfloat32_t p0 = svmul_lane (r, lane_consts, 1);
|
|
- svfloat32_t poly = svmla_x (pg, p0, r2, p14);
|
|
-
|
|
+ svfloat32_t poly = svmla_lane (sv_f32 (d->c0), r, lane_consts, 3);
|
|
+ poly = svmul_x (pg, poly, r);
|
|
return svmla_x (pg, scale, scale, poly);
|
|
}
|
|
|
|
@@ -85,11 +73,10 @@ special_case (svfloat32_t x, svbool_t special, const struct data *d)
|
|
special);
|
|
}
|
|
|
|
-/* Single-precision SVE exp10f routine. Implements the same algorithm
|
|
- as AdvSIMD exp10f.
|
|
- Worst case error is 1.02 ULPs.
|
|
- _ZGVsMxv_exp10f(-0x1.040488p-4) got 0x1.ba5f9ep-1
|
|
- want 0x1.ba5f9cp-1. */
|
|
+/* Single-precision SVE exp10f routine. Based on the FEXPA instruction.
|
|
+ Worst case error is 1.10 ULP.
|
|
+ _ZGVsMxv_exp10f (0x1.cc76dep+3) got 0x1.be0172p+47
|
|
+ want 0x1.be017p+47. */
|
|
svfloat32_t SV_NAME_F1 (exp10) (svfloat32_t x, const svbool_t pg)
|
|
{
|
|
const struct data *d = ptr_barrier (&data);
|
|
diff --git a/sysdeps/aarch64/fpu/exp2_sve.c b/sysdeps/aarch64/fpu/exp2_sve.c
|
|
index 5dfb77cdbc2f6a51..ed11423e45059133 100644
|
|
--- a/sysdeps/aarch64/fpu/exp2_sve.c
|
|
+++ b/sysdeps/aarch64/fpu/exp2_sve.c
|
|
@@ -19,23 +19,21 @@
|
|
|
|
#include "sv_math.h"
|
|
|
|
-#define N (1 << V_EXP_TABLE_BITS)
|
|
-
|
|
#define BigBound 1022
|
|
#define UOFlowBound 1280
|
|
|
|
static const struct data
|
|
{
|
|
- double c0, c2;
|
|
- double c1, c3;
|
|
+ double c2, c4;
|
|
+ double c0, c1, c3;
|
|
double shift, big_bound, uoflow_bound;
|
|
} data = {
|
|
/* Coefficients are computed using Remez algorithm with
|
|
minimisation of the absolute error. */
|
|
- .c0 = 0x1.62e42fefa3686p-1, .c1 = 0x1.ebfbdff82c241p-3,
|
|
- .c2 = 0x1.c6b09b16de99ap-5, .c3 = 0x1.3b2abf5571ad8p-7,
|
|
- .shift = 0x1.8p52 / N, .uoflow_bound = UOFlowBound,
|
|
- .big_bound = BigBound,
|
|
+ .c0 = 0x1.62e42fefa39efp-1, .c1 = 0x1.ebfbdff82a31bp-3,
|
|
+ .c2 = 0x1.c6b08d706c8a5p-5, .c3 = 0x1.3b2ad2ff7d2f3p-7,
|
|
+ .c4 = 0x1.5d8761184beb3p-10, .shift = 0x1.800000000ffc0p+46,
|
|
+ .uoflow_bound = UOFlowBound, .big_bound = BigBound,
|
|
};
|
|
|
|
#define SpecialOffset 0x6000000000000000 /* 0x1p513. */
|
|
@@ -64,50 +62,52 @@ special_case (svbool_t pg, svfloat64_t s, svfloat64_t y, svfloat64_t n,
|
|
svadd_x (pg, svsub_x (pg, svreinterpret_u64 (s), SpecialBias2), b));
|
|
|
|
/* |n| > 1280 => 2^(n) overflows. */
|
|
- svbool_t p_cmp = svacgt (pg, n, d->uoflow_bound);
|
|
+ svbool_t p_cmp = svacle (pg, n, d->uoflow_bound);
|
|
|
|
svfloat64_t r1 = svmul_x (svptrue_b64 (), s1, s1);
|
|
svfloat64_t r2 = svmla_x (pg, s2, s2, y);
|
|
svfloat64_t r0 = svmul_x (svptrue_b64 (), r2, s1);
|
|
|
|
- return svsel (p_cmp, r1, r0);
|
|
+ return svsel (p_cmp, r0, r1);
|
|
}
|
|
|
|
/* Fast vector implementation of exp2.
|
|
- Maximum measured error is 1.65 ulp.
|
|
- _ZGVsMxv_exp2(-0x1.4c264ab5b559bp-6) got 0x1.f8db0d4df721fp-1
|
|
- want 0x1.f8db0d4df721dp-1. */
|
|
+ Maximum measured error is 0.52 + 0.5 ulp.
|
|
+ _ZGVsMxv_exp2 (0x1.3b72ad5b701bfp-1) got 0x1.8861641b49e08p+0
|
|
+ want 0x1.8861641b49e07p+0. */
|
|
svfloat64_t SV_NAME_D1 (exp2) (svfloat64_t x, svbool_t pg)
|
|
{
|
|
const struct data *d = ptr_barrier (&data);
|
|
- svbool_t no_big_scale = svacle (pg, x, d->big_bound);
|
|
- svbool_t special = svnot_z (pg, no_big_scale);
|
|
-
|
|
- /* Reduce x to k/N + r, where k is integer and r in [-1/2N, 1/2N]. */
|
|
- svfloat64_t shift = sv_f64 (d->shift);
|
|
- svfloat64_t kd = svadd_x (pg, x, shift);
|
|
- svuint64_t ki = svreinterpret_u64 (kd);
|
|
- /* kd = k/N. */
|
|
- kd = svsub_x (pg, kd, shift);
|
|
- svfloat64_t r = svsub_x (pg, x, kd);
|
|
-
|
|
- /* scale ~= 2^(k/N). */
|
|
- svuint64_t idx = svand_x (pg, ki, N - 1);
|
|
- svuint64_t sbits = svld1_gather_index (pg, __v_exp_data, idx);
|
|
- /* This is only a valid scale when -1023*N < k < 1024*N. */
|
|
- svuint64_t top = svlsl_x (pg, ki, 52 - V_EXP_TABLE_BITS);
|
|
- svfloat64_t scale = svreinterpret_f64 (svadd_x (pg, sbits, top));
|
|
-
|
|
- svfloat64_t c13 = svld1rq (svptrue_b64 (), &d->c1);
|
|
- /* Approximate exp2(r) using polynomial. */
|
|
- /* y = exp2(r) - 1 ~= C0 r + C1 r^2 + C2 r^3 + C3 r^4. */
|
|
+ svbool_t special = svacge (pg, x, d->big_bound);
|
|
+
|
|
+ svfloat64_t z = svadd_x (svptrue_b64 (), x, d->shift);
|
|
+ svfloat64_t n = svsub_x (svptrue_b64 (), z, d->shift);
|
|
+ svfloat64_t r = svsub_x (svptrue_b64 (), x, n);
|
|
+
|
|
+ svfloat64_t scale = svexpa (svreinterpret_u64 (z));
|
|
+
|
|
svfloat64_t r2 = svmul_x (svptrue_b64 (), r, r);
|
|
- svfloat64_t p01 = svmla_lane (sv_f64 (d->c0), r, c13, 0);
|
|
- svfloat64_t p23 = svmla_lane (sv_f64 (d->c2), r, c13, 1);
|
|
- svfloat64_t p = svmla_x (pg, p01, p23, r2);
|
|
+ svfloat64_t c24 = svld1rq (svptrue_b64 (), &d->c2);
|
|
+
|
|
+ /* Approximate exp2(r) using polynomial. */
|
|
+ /* y = exp2(r) - 1 ~= r * (C0 + C1 r + C2 r^2 + C3 r^3 + C4 r^4). */
|
|
+ svfloat64_t p12 = svmla_lane (sv_f64 (d->c1), r, c24, 0);
|
|
+ svfloat64_t p34 = svmla_lane (sv_f64 (d->c3), r, c24, 1);
|
|
+ svfloat64_t p = svmla_x (pg, p12, p34, r2);
|
|
+ p = svmad_x (pg, p, r, d->c0);
|
|
svfloat64_t y = svmul_x (svptrue_b64 (), r, p);
|
|
+
|
|
/* Assemble exp2(x) = exp2(r) * scale. */
|
|
if (__glibc_unlikely (svptest_any (pg, special)))
|
|
- return special_case (pg, scale, y, kd, d);
|
|
+ {
|
|
+ /* FEXPA zeroes the sign bit, however the sign is meaningful to the
|
|
+ special case function so needs to be copied.
|
|
+ e = sign bit of u << 46. */
|
|
+ svuint64_t e = svand_x (pg, svlsl_x (pg, svreinterpret_u64 (z), 46),
|
|
+ 0x8000000000000000);
|
|
+ scale = svreinterpret_f64 (svadd_x (pg, e, svreinterpret_u64 (scale)));
|
|
+ return special_case (pg, scale, y, n, d);
|
|
+ }
|
|
+
|
|
return svmla_x (pg, scale, scale, y);
|
|
}
|
|
diff --git a/sysdeps/aarch64/fpu/exp2f_sve.c b/sysdeps/aarch64/fpu/exp2f_sve.c
|
|
index c6216bed9e9e7538..cf01820288f1855c 100644
|
|
--- a/sysdeps/aarch64/fpu/exp2f_sve.c
|
|
+++ b/sysdeps/aarch64/fpu/exp2f_sve.c
|
|
@@ -18,21 +18,17 @@
|
|
<https://www.gnu.org/licenses/>. */
|
|
|
|
#include "sv_math.h"
|
|
-#include "poly_sve_f32.h"
|
|
|
|
#define Thres 0x1.5d5e2ap+6f
|
|
|
|
static const struct data
|
|
{
|
|
- float c0, c2, c4, c1, c3;
|
|
- float shift, thres;
|
|
+ float c0, c1, shift, thres;
|
|
} data = {
|
|
- /* Coefficients copied from the polynomial in AdvSIMD variant. */
|
|
- .c0 = 0x1.62e422p-1f,
|
|
- .c1 = 0x1.ebf9bcp-3f,
|
|
- .c2 = 0x1.c6bd32p-5f,
|
|
- .c3 = 0x1.3ce9e4p-7f,
|
|
- .c4 = 0x1.59977ap-10f,
|
|
+ /* Coefficients generated using Remez algorithm with minimisation of relative
|
|
+ error. */
|
|
+ .c0 = 0x1.62e485p-1,
|
|
+ .c1 = 0x1.ebfbe0p-3,
|
|
/* 1.5*2^17 + 127. */
|
|
.shift = 0x1.803f8p17f,
|
|
/* Roughly 87.3. For x < -Thres, the result is subnormal and not handled
|
|
@@ -51,16 +47,8 @@ sv_exp2f_inline (svfloat32_t x, const svbool_t pg, const struct data *d)
|
|
|
|
svfloat32_t scale = svexpa (svreinterpret_u32 (z));
|
|
|
|
- /* Polynomial evaluation: poly(r) ~ exp2(r)-1.
|
|
- Evaluate polynomial use hybrid scheme - offset ESTRIN by 1 for
|
|
- coefficients 1 to 4, and apply most significant coefficient directly. */
|
|
- svfloat32_t even_coeffs = svld1rq (svptrue_b32 (), &d->c0);
|
|
- svfloat32_t r2 = svmul_x (svptrue_b32 (), r, r);
|
|
- svfloat32_t p12 = svmla_lane (sv_f32 (d->c1), r, even_coeffs, 1);
|
|
- svfloat32_t p34 = svmla_lane (sv_f32 (d->c3), r, even_coeffs, 2);
|
|
- svfloat32_t p14 = svmla_x (pg, p12, r2, p34);
|
|
- svfloat32_t p0 = svmul_lane (r, even_coeffs, 0);
|
|
- svfloat32_t poly = svmla_x (pg, p0, r2, p14);
|
|
+ svfloat32_t poly = svmla_x (pg, sv_f32 (d->c0), r, sv_f32 (d->c1));
|
|
+ poly = svmul_x (svptrue_b32 (), poly, r);
|
|
|
|
return svmla_x (pg, scale, scale, poly);
|
|
}
|
|
@@ -72,11 +60,10 @@ special_case (svfloat32_t x, svbool_t special, const struct data *d)
|
|
special);
|
|
}
|
|
|
|
-/* Single-precision SVE exp2f routine. Implements the same algorithm
|
|
- as AdvSIMD exp2f.
|
|
- Worst case error is 1.04 ULPs.
|
|
- _ZGVsMxv_exp2f(-0x1.af994ap-3) got 0x1.ba6a66p-1
|
|
- want 0x1.ba6a64p-1. */
|
|
+/* Single-precision SVE exp2f routine, based on the FEXPA instruction.
|
|
+ Worst case error is 1.09 ULPs.
|
|
+ _ZGVsMxv_exp2f (0x1.9a2a94p-1) got 0x1.be1054p+0
|
|
+ want 0x1.be1052p+0. */
|
|
svfloat32_t SV_NAME_F1 (exp2) (svfloat32_t x, const svbool_t pg)
|
|
{
|
|
const struct data *d = ptr_barrier (&data);
|
|
diff --git a/sysdeps/aarch64/fpu/expm1_sve.c b/sysdeps/aarch64/fpu/expm1_sve.c
|
|
index c933cf9c0eb2406b..4c35e0341d34aee0 100644
|
|
--- a/sysdeps/aarch64/fpu/expm1_sve.c
|
|
+++ b/sysdeps/aarch64/fpu/expm1_sve.c
|
|
@@ -18,82 +18,164 @@
|
|
<https://www.gnu.org/licenses/>. */
|
|
|
|
#include "sv_math.h"
|
|
-#include "poly_sve_f64.h"
|
|
|
|
-#define SpecialBound 0x1.62b7d369a5aa9p+9
|
|
-#define ExponentBias 0x3ff0000000000000
|
|
+#define FexpaBound 0x1.4cb5ecef28adap-3 /* 15*ln2/64. */
|
|
+#define SpecialBound 0x1.628c2855bfaddp+9 /* ln(2^(1023 + 1/128)). */
|
|
|
|
static const struct data
|
|
{
|
|
- double poly[11];
|
|
- double shift, inv_ln2, special_bound;
|
|
- /* To be loaded in one quad-word. */
|
|
+ double c2, c4;
|
|
+ double inv_ln2;
|
|
double ln2_hi, ln2_lo;
|
|
+ double c0, c1, c3;
|
|
+ double shift, thres;
|
|
+ uint64_t expm1_data[32];
|
|
} data = {
|
|
- /* Generated using fpminimax. */
|
|
- .poly = { 0x1p-1, 0x1.5555555555559p-3, 0x1.555555555554bp-5,
|
|
- 0x1.111111110f663p-7, 0x1.6c16c16c1b5f3p-10, 0x1.a01a01affa35dp-13,
|
|
- 0x1.a01a018b4ecbbp-16, 0x1.71ddf82db5bb4p-19, 0x1.27e517fc0d54bp-22,
|
|
- 0x1.af5eedae67435p-26, 0x1.1f143d060a28ap-29, },
|
|
-
|
|
- .special_bound = SpecialBound,
|
|
- .inv_ln2 = 0x1.71547652b82fep0,
|
|
- .ln2_hi = 0x1.62e42fefa39efp-1,
|
|
- .ln2_lo = 0x1.abc9e3b39803fp-56,
|
|
- .shift = 0x1.8p52,
|
|
+ /* Table emulating FEXPA - 1, for values of FEXPA close to 1.
|
|
+ The table holds values of 2^(i/64) - 1, computed in arbitrary precision.
|
|
+ The first half of the table stores values associated to i from 0 to 15.
|
|
+ The second half of the table stores values associated to i from 0 to -15. */
|
|
+ .expm1_data = {
|
|
+ 0x0000000000000000, 0x3f864d1f3bc03077, 0x3f966c34c5615d0f, 0x3fa0e8a30eb37901,
|
|
+ 0x3fa6ab0d9f3121ec, 0x3fac7d865a7a3440, 0x3fb1301d0125b50a, 0x3fb429aaea92ddfb,
|
|
+ 0x3fb72b83c7d517ae, 0x3fba35beb6fcb754, 0x3fbd4873168b9aa8, 0x3fc031dc431466b2,
|
|
+ 0x3fc1c3d373ab11c3, 0x3fc35a2b2f13e6e9, 0x3fc4f4efa8fef709, 0x3fc6942d3720185a,
|
|
+ 0x0000000000000000, 0xbfc331751ec3a814, 0xbfc20224341286e4, 0xbfc0cf85bed0f8b7,
|
|
+ 0xbfbf332113d56b1f, 0xbfbcc0768d4175a6, 0xbfba46f918837cb7, 0xbfb7c695afc3b424,
|
|
+ 0xbfb53f391822dbc7, 0xbfb2b0cfe1266bd4, 0xbfb01b466423250a, 0xbfaafd11874c009e,
|
|
+ 0xbfa5b505d5b6f268, 0xbfa05e4119ea5d89, 0xbf95f134923757f3, 0xbf860f9f985bc9f4,
|
|
+ },
|
|
+
|
|
+ /* Generated using Remez, in [-log(2)/128, log(2)/128]. */
|
|
+ .c0 = 0x1p-1,
|
|
+ .c1 = 0x1.55555555548f9p-3,
|
|
+ .c2 = 0x1.5555555554c22p-5,
|
|
+ .c3 = 0x1.111123aaa2fb2p-7,
|
|
+ .c4 = 0x1.6c16d77d98e5bp-10,
|
|
+ .ln2_hi = 0x1.62e42fefa3800p-1,
|
|
+ .ln2_lo = 0x1.ef35793c76730p-45,
|
|
+ .inv_ln2 = 0x1.71547652b82fep+0,
|
|
+ .shift = 0x1.800000000ffc0p+46, /* 1.5*2^46+1023. */
|
|
+ .thres = SpecialBound,
|
|
};
|
|
|
|
-static svfloat64_t NOINLINE
|
|
-special_case (svfloat64_t x, svfloat64_t y, svbool_t pg)
|
|
+#define SpecialOffset 0x6000000000000000 /* 0x1p513. */
|
|
+/* SpecialBias1 + SpecialBias1 = asuint(1.0). */
|
|
+#define SpecialBias1 0x7000000000000000 /* 0x1p769. */
|
|
+#define SpecialBias2 0x3010000000000000 /* 0x1p-254. */
|
|
+
|
|
+static NOINLINE svfloat64_t
|
|
+special_case (svbool_t pg, svfloat64_t y, svfloat64_t s, svfloat64_t p,
|
|
+ svfloat64_t n)
|
|
{
|
|
- return sv_call_f64 (expm1, x, y, pg);
|
|
+ /* s=2^n may overflow, break it up into s=s1*s2,
|
|
+ such that exp = s + s*y can be computed as s1*(s2+s2*y)
|
|
+ and s1*s1 overflows only if n>0. */
|
|
+
|
|
+ /* If n<=0 then set b to 0x6, 0 otherwise. */
|
|
+ svbool_t p_sign = svcmple (pg, n, 0.0); /* n <= 0. */
|
|
+ svuint64_t b
|
|
+ = svdup_u64_z (p_sign, SpecialOffset); /* Inactive lanes set to 0. */
|
|
+
|
|
+ /* Set s1 to generate overflow depending on sign of exponent n,
|
|
+ ie. s1 = 0x70...0 - b. */
|
|
+ svfloat64_t s1 = svreinterpret_f64 (svsubr_x (pg, b, SpecialBias1));
|
|
+ /* Offset s to avoid overflow in final result if n is below threshold.
|
|
+ ie. s2 = as_u64 (s) - 0x3010...0 + b. */
|
|
+ svfloat64_t s2 = svreinterpret_f64 (
|
|
+ svadd_x (pg, svsub_x (pg, svreinterpret_u64 (s), SpecialBias2), b));
|
|
+
|
|
+ /* |n| > 1280 => 2^(n) overflows. */
|
|
+ svbool_t p_cmp = svacgt (pg, n, 1280.0);
|
|
+
|
|
+ svfloat64_t r1 = svmul_x (svptrue_b64 (), s1, s1);
|
|
+ svfloat64_t r2 = svmla_x (pg, s2, s2, p);
|
|
+ svfloat64_t r0 = svmul_x (svptrue_b64 (), r2, s1);
|
|
+
|
|
+ svbool_t is_safe = svacle (pg, n, 1023); /* Only correct special lanes. */
|
|
+ return svsel (is_safe, y, svsub_x (pg, svsel (p_cmp, r1, r0), 1.0));
|
|
}
|
|
|
|
-/* Double-precision vector exp(x) - 1 function.
|
|
- The maximum error observed error is 2.18 ULP:
|
|
- _ZGVsMxv_expm1(0x1.634ba0c237d7bp-2) got 0x1.a8b9ea8d66e22p-2
|
|
- want 0x1.a8b9ea8d66e2p-2. */
|
|
+/* FEXPA based SVE expm1 algorithm.
|
|
+ Maximum measured error is 2.81 + 0.5 ULP:
|
|
+ _ZGVsMxv_expm1 (0x1.974060e619bfp-3) got 0x1.c290e5858bb53p-3
|
|
+ want 0x1.c290e5858bb5p-3. */
|
|
svfloat64_t SV_NAME_D1 (expm1) (svfloat64_t x, svbool_t pg)
|
|
{
|
|
const struct data *d = ptr_barrier (&data);
|
|
|
|
- /* Large, Nan/Inf. */
|
|
- svbool_t special = svnot_z (pg, svaclt (pg, x, d->special_bound));
|
|
-
|
|
- /* Reduce argument to smaller range:
|
|
- Let i = round(x / ln2)
|
|
- and f = x - i * ln2, then f is in [-ln2/2, ln2/2].
|
|
- exp(x) - 1 = 2^i * (expm1(f) + 1) - 1
|
|
- where 2^i is exact because i is an integer. */
|
|
- svfloat64_t shift = sv_f64 (d->shift);
|
|
- svfloat64_t n = svsub_x (pg, svmla_x (pg, shift, x, d->inv_ln2), shift);
|
|
- svint64_t i = svcvt_s64_x (pg, n);
|
|
- svfloat64_t ln2 = svld1rq (svptrue_b64 (), &d->ln2_hi);
|
|
- svfloat64_t f = svmls_lane (x, n, ln2, 0);
|
|
- f = svmls_lane (f, n, ln2, 1);
|
|
-
|
|
- /* Approximate expm1(f) using polynomial.
|
|
- Taylor expansion for expm1(x) has the form:
|
|
- x + ax^2 + bx^3 + cx^4 ....
|
|
- So we calculate the polynomial P(f) = a + bf + cf^2 + ...
|
|
- and assemble the approximation expm1(f) ~= f + f^2 * P(f). */
|
|
- svfloat64_t f2 = svmul_x (pg, f, f);
|
|
- svfloat64_t f4 = svmul_x (pg, f2, f2);
|
|
- svfloat64_t f8 = svmul_x (pg, f4, f4);
|
|
- svfloat64_t p
|
|
- = svmla_x (pg, f, f2, sv_estrin_10_f64_x (pg, f, f2, f4, f8, d->poly));
|
|
-
|
|
- /* Assemble the result.
|
|
- expm1(x) ~= 2^i * (p + 1) - 1
|
|
- Let t = 2^i. */
|
|
- svint64_t u = svadd_x (pg, svlsl_x (pg, i, 52), ExponentBias);
|
|
- svfloat64_t t = svreinterpret_f64 (u);
|
|
-
|
|
- /* expm1(x) ~= p * t + (t - 1). */
|
|
- svfloat64_t y = svmla_x (pg, svsub_x (pg, t, 1), p, t);
|
|
+ svbool_t special = svacgt (pg, x, d->thres);
|
|
|
|
- if (__glibc_unlikely (svptest_any (pg, special)))
|
|
- return special_case (x, y, special);
|
|
+ svfloat64_t z = svmla_x (pg, sv_f64 (d->shift), x, d->inv_ln2);
|
|
+ svuint64_t u = svreinterpret_u64 (z);
|
|
+ svfloat64_t n = svsub_x (pg, z, d->shift);
|
|
|
|
+ /* r = x - n * ln2, r is in [-ln2/128, ln2/128]. */
|
|
+ svfloat64_t ln2 = svld1rq (svptrue_b64 (), &d->ln2_hi);
|
|
+ svfloat64_t r = x;
|
|
+ r = svmls_lane (r, n, ln2, 0);
|
|
+ r = svmls_lane (r, n, ln2, 1);
|
|
+
|
|
+ /* y = exp(r) - 1 ~= r + C0 r^2 + C1 r^3 + C2 r^4 + C3 r^5 + C4 r^6. */
|
|
+ svfloat64_t r2 = svmul_x (svptrue_b64 (), r, r);
|
|
+ svfloat64_t c24 = svld1rq (svptrue_b64 (), &d->c2);
|
|
+
|
|
+ svfloat64_t p;
|
|
+ svfloat64_t c12 = svmla_lane (sv_f64 (d->c1), r, c24, 0);
|
|
+ svfloat64_t c34 = svmla_lane (sv_f64 (d->c3), r, c24, 1);
|
|
+ p = svmad_x (pg, c34, r2, c12);
|
|
+ p = svmad_x (pg, p, r, sv_f64 (d->c0));
|
|
+ p = svmad_x (pg, p, r2, r);
|
|
+
|
|
+ svfloat64_t scale = svexpa (u);
|
|
+ svfloat64_t scalem1 = svsub_x (pg, scale, sv_f64 (1.0));
|
|
+
|
|
+ /* We want to construct expm1(x) = (scale - 1) + scale * poly.
|
|
+ However, for values of scale close to 1, scale-1 causes large ULP errors
|
|
+ due to cancellation.
|
|
+
|
|
+ This can be circumvented by using a small lookup for scale-1
|
|
+ when our input is below a certain bound, otherwise we can use FEXPA.
|
|
+
|
|
+ This bound is based upon the table size:
|
|
+ Bound = (TableSize-1/64) * ln2.
|
|
+ The current bound is based upon a table size of 16. */
|
|
+ svbool_t is_small = svaclt (pg, x, FexpaBound);
|
|
+
|
|
+ if (svptest_any (pg, is_small))
|
|
+ {
|
|
+ /* Index via the input of FEXPA, but we only care about the lower 4 bits.
|
|
+ */
|
|
+ svuint64_t base_idx = svand_x (pg, u, 0xf);
|
|
+
|
|
+ /* We can use the sign of x as a fifth bit to account for the asymmetry
|
|
+ of e^x around 0. */
|
|
+ svuint64_t signBit
|
|
+ = svlsl_x (pg, svlsr_x (pg, svreinterpret_u64 (x), 63), 4);
|
|
+ svuint64_t idx = svorr_x (pg, base_idx, signBit);
|
|
+
|
|
+ /* Lookup values for scale - 1 for small x. */
|
|
+ svfloat64_t lookup = svreinterpret_f64 (
|
|
+ svld1_gather_index (is_small, d->expm1_data, idx));
|
|
+
|
|
+ /* Select the appropriate scale - 1 value based on x. */
|
|
+ scalem1 = svsel (is_small, lookup, scalem1);
|
|
+ }
|
|
+
|
|
+ svfloat64_t y = svmla_x (pg, scalem1, scale, p);
|
|
+
|
|
+ /* FEXPA returns nan for large inputs so we special case those. */
|
|
+ if (__glibc_unlikely (svptest_any (pg, special)))
|
|
+ {
|
|
+ /* FEXPA zeroes the sign bit, however the sign is meaningful to the
|
|
+ special case function so needs to be copied.
|
|
+ e = sign bit of u << 46. */
|
|
+ svuint64_t e = svand_x (pg, svlsl_x (pg, u, 46), 0x8000000000000000);
|
|
+ /* Copy sign to s. */
|
|
+ scale = svreinterpret_f64 (svadd_x (pg, e, svreinterpret_u64 (scale)));
|
|
+ return special_case (pg, y, scale, p, n);
|
|
+ }
|
|
+
|
|
+ /* return expm1 = (scale - 1) + (scale * poly). */
|
|
return y;
|
|
}
|