about summary refs log tree commit diff
path: root/sysdeps/aarch64/fpu
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/aarch64/fpu')
-rw-r--r--sysdeps/aarch64/fpu/Makefile4
-rw-r--r--sysdeps/aarch64/fpu/Versions4
-rw-r--r--sysdeps/aarch64/fpu/bits/math-vector.h4
-rw-r--r--sysdeps/aarch64/fpu/log10_advsimd.c119
-rw-r--r--sysdeps/aarch64/fpu/log10_sve.c76
-rw-r--r--sysdeps/aarch64/fpu/log10f_advsimd.c82
-rw-r--r--sysdeps/aarch64/fpu/log10f_sve.c94
-rw-r--r--sysdeps/aarch64/fpu/test-double-advsimd-wrappers.c1
-rw-r--r--sysdeps/aarch64/fpu/test-double-sve-wrappers.c1
-rw-r--r--sysdeps/aarch64/fpu/test-float-advsimd-wrappers.c1
-rw-r--r--sysdeps/aarch64/fpu/test-float-sve-wrappers.c1
-rw-r--r--sysdeps/aarch64/fpu/v_log10_data.c175
-rw-r--r--sysdeps/aarch64/fpu/vecmath_config.h11
13 files changed, 572 insertions, 1 deletions
diff --git a/sysdeps/aarch64/fpu/Makefile b/sysdeps/aarch64/fpu/Makefile
index c3f204ff0d..0a047a150d 100644
--- a/sysdeps/aarch64/fpu/Makefile
+++ b/sysdeps/aarch64/fpu/Makefile
@@ -2,6 +2,7 @@ libmvec-supported-funcs = cos \
                           exp \
                           exp2 \
                           log \
+                          log10 \
                           log2 \
                           sin \
                           tan
@@ -18,7 +19,8 @@ libmvec-support = $(addsuffix f_advsimd,$(float-advsimd-funcs)) \
                   $(addsuffix _sve,$(double-sve-funcs)) \
                   v_log_data \
                   v_exp_data \
-                  v_log2_data
+                  v_log2_data \
+                  v_log10_data
 endif
 
 sve-cflags = -march=armv8-a+sve
diff --git a/sysdeps/aarch64/fpu/Versions b/sysdeps/aarch64/fpu/Versions
index ffe62a6f65..358efed5ee 100644
--- a/sysdeps/aarch64/fpu/Versions
+++ b/sysdeps/aarch64/fpu/Versions
@@ -22,6 +22,10 @@ libmvec {
     _ZGVnN2v_exp2;
     _ZGVsMxv_exp2f;
     _ZGVsMxv_exp2;
+    _ZGVnN4v_log10f;
+    _ZGVnN2v_log10;
+    _ZGVsMxv_log10f;
+    _ZGVsMxv_log10;
     _ZGVnN4v_log2f;
     _ZGVnN2v_log2;
     _ZGVsMxv_log2f;
diff --git a/sysdeps/aarch64/fpu/bits/math-vector.h b/sysdeps/aarch64/fpu/bits/math-vector.h
index 92f214b194..59f2efa6d7 100644
--- a/sysdeps/aarch64/fpu/bits/math-vector.h
+++ b/sysdeps/aarch64/fpu/bits/math-vector.h
@@ -53,6 +53,7 @@ __vpcs __f32x4_t _ZGVnN4v_cosf (__f32x4_t);
 __vpcs __f32x4_t _ZGVnN4v_expf (__f32x4_t);
 __vpcs __f32x4_t _ZGVnN4v_exp2f (__f32x4_t);
 __vpcs __f32x4_t _ZGVnN4v_logf (__f32x4_t);
+__vpcs __f32x4_t _ZGVnN4v_log10f (__f32x4_t);
 __vpcs __f32x4_t _ZGVnN4v_log2f (__f32x4_t);
 __vpcs __f32x4_t _ZGVnN4v_sinf (__f32x4_t);
 __vpcs __f32x4_t _ZGVnN4v_tanf (__f32x4_t);
@@ -61,6 +62,7 @@ __vpcs __f64x2_t _ZGVnN2v_cos (__f64x2_t);
 __vpcs __f64x2_t _ZGVnN2v_exp (__f64x2_t);
 __vpcs __f64x2_t _ZGVnN2v_exp2 (__f64x2_t);
 __vpcs __f64x2_t _ZGVnN2v_log (__f64x2_t);
+__vpcs __f64x2_t _ZGVnN2v_log10 (__f64x2_t);
 __vpcs __f64x2_t _ZGVnN2v_log2 (__f64x2_t);
 __vpcs __f64x2_t _ZGVnN2v_sin (__f64x2_t);
 __vpcs __f64x2_t _ZGVnN2v_tan (__f64x2_t);
@@ -74,6 +76,7 @@ __sv_f32_t _ZGVsMxv_cosf (__sv_f32_t, __sv_bool_t);
 __sv_f32_t _ZGVsMxv_expf (__sv_f32_t, __sv_bool_t);
 __sv_f32_t _ZGVsMxv_exp2f (__sv_f32_t, __sv_bool_t);
 __sv_f32_t _ZGVsMxv_logf (__sv_f32_t, __sv_bool_t);
+__sv_f32_t _ZGVsMxv_log10f (__sv_f32_t, __sv_bool_t);
 __sv_f32_t _ZGVsMxv_log2f (__sv_f32_t, __sv_bool_t);
 __sv_f32_t _ZGVsMxv_sinf (__sv_f32_t, __sv_bool_t);
 __sv_f32_t _ZGVsMxv_tanf (__sv_f32_t, __sv_bool_t);
@@ -82,6 +85,7 @@ __sv_f64_t _ZGVsMxv_cos (__sv_f64_t, __sv_bool_t);
 __sv_f64_t _ZGVsMxv_exp (__sv_f64_t, __sv_bool_t);
 __sv_f64_t _ZGVsMxv_exp2 (__sv_f64_t, __sv_bool_t);
 __sv_f64_t _ZGVsMxv_log (__sv_f64_t, __sv_bool_t);
+__sv_f64_t _ZGVsMxv_log10 (__sv_f64_t, __sv_bool_t);
 __sv_f64_t _ZGVsMxv_log2 (__sv_f64_t, __sv_bool_t);
 __sv_f64_t _ZGVsMxv_sin (__sv_f64_t, __sv_bool_t);
 __sv_f64_t _ZGVsMxv_tan (__sv_f64_t, __sv_bool_t);
diff --git a/sysdeps/aarch64/fpu/log10_advsimd.c b/sysdeps/aarch64/fpu/log10_advsimd.c
new file mode 100644
index 0000000000..05b509f134
--- /dev/null
+++ b/sysdeps/aarch64/fpu/log10_advsimd.c
@@ -0,0 +1,119 @@
+/* Double-precision vector (AdvSIMD) log10 function
+
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include "v_math.h"
+#include "poly_advsimd_f64.h"
+
+#define N (1 << V_LOG10_TABLE_BITS)
+
+static const struct data
+{
+  uint64x2_t min_norm;
+  uint32x4_t special_bound;
+  float64x2_t poly[5];
+  float64x2_t invln10, log10_2, ln2;
+  uint64x2_t sign_exp_mask;
+} data = {
+  /* Computed from log coefficients divided by log(10) then rounded to double
+     precision.  */
+  .poly = { V2 (-0x1.bcb7b1526e506p-3), V2 (0x1.287a7636be1d1p-3),
+	    V2 (-0x1.bcb7b158af938p-4), V2 (0x1.63c78734e6d07p-4),
+	    V2 (-0x1.287461742fee4p-4) },
+  .ln2 = V2 (0x1.62e42fefa39efp-1),
+  .invln10 = V2 (0x1.bcb7b1526e50ep-2),
+  .log10_2 = V2 (0x1.34413509f79ffp-2),
+  .min_norm = V2 (0x0010000000000000), /* asuint64(0x1p-1022).  */
+  .special_bound = V4 (0x7fe00000),    /* asuint64(inf) - min_norm.  */
+  .sign_exp_mask = V2 (0xfff0000000000000),
+};
+
+#define Off v_u64 (0x3fe6900900000000)
+#define IndexMask (N - 1)
+
+#define T(s, i) __v_log10_data.s[i]
+
+struct entry
+{
+  float64x2_t invc;
+  float64x2_t log10c;
+};
+
+static inline struct entry
+lookup (uint64x2_t i)
+{
+  struct entry e;
+  uint64_t i0 = (i[0] >> (52 - V_LOG10_TABLE_BITS)) & IndexMask;
+  uint64_t i1 = (i[1] >> (52 - V_LOG10_TABLE_BITS)) & IndexMask;
+  float64x2_t e0 = vld1q_f64 (&__v_log10_data.table[i0].invc);
+  float64x2_t e1 = vld1q_f64 (&__v_log10_data.table[i1].invc);
+  e.invc = vuzp1q_f64 (e0, e1);
+  e.log10c = vuzp2q_f64 (e0, e1);
+  return e;
+}
+
+static float64x2_t VPCS_ATTR NOINLINE
+special_case (float64x2_t x, float64x2_t y, float64x2_t hi, float64x2_t r2,
+	      uint32x2_t special)
+{
+  return v_call_f64 (log10, x, vfmaq_f64 (hi, r2, y), vmovl_u32 (special));
+}
+
+/* Fast implementation of double-precision vector log10
+   is a slight modification of double-precision vector log.
+   Max ULP error: < 2.5 ulp (nearest rounding.)
+   Maximum measured at 2.46 ulp for x in [0.96, 0.97]
+   _ZGVnN2v_log10(0x1.13192407fcb46p+0) got 0x1.fff6be3cae4bbp-6
+				       want 0x1.fff6be3cae4b9p-6.  */
+float64x2_t VPCS_ATTR V_NAME_D1 (log10) (float64x2_t x)
+{
+  const struct data *d = ptr_barrier (&data);
+  uint64x2_t ix = vreinterpretq_u64_f64 (x);
+  uint32x2_t special = vcge_u32 (vsubhn_u64 (ix, d->min_norm),
+				 vget_low_u32 (d->special_bound));
+
+  /* x = 2^k z; where z is in range [OFF,2*OFF) and exact.
+     The range is split into N subintervals.
+     The ith subinterval contains z and c is near its center.  */
+  uint64x2_t tmp = vsubq_u64 (ix, Off);
+  int64x2_t k = vshrq_n_s64 (vreinterpretq_s64_u64 (tmp), 52);
+  uint64x2_t iz = vsubq_u64 (ix, vandq_u64 (tmp, d->sign_exp_mask));
+  float64x2_t z = vreinterpretq_f64_u64 (iz);
+
+  struct entry e = lookup (tmp);
+
+  /* log10(x) = log1p(z/c-1)/log(10) + log10(c) + k*log10(2).  */
+  float64x2_t r = vfmaq_f64 (v_f64 (-1.0), z, e.invc);
+  float64x2_t kd = vcvtq_f64_s64 (k);
+
+  /* hi = r / log(10) + log10(c) + k*log10(2).
+     Constants in v_log10_data.c are computed (in extended precision) as
+     e.log10c := e.logc * ivln10.  */
+  float64x2_t w = vfmaq_f64 (e.log10c, r, d->invln10);
+
+  /* y = log10(1+r) + n * log10(2).  */
+  float64x2_t hi = vfmaq_f64 (w, kd, d->log10_2);
+
+  /* y = r2*(A0 + r*A1 + r2*(A2 + r*A3 + r2*A4)) + hi.  */
+  float64x2_t r2 = vmulq_f64 (r, r);
+  float64x2_t y = v_pw_horner_4_f64 (r, r2, d->poly);
+
+  if (__glibc_unlikely (v_any_u32h (special)))
+    return special_case (x, y, hi, r2, special);
+  return vfmaq_f64 (hi, r2, y);
+}
diff --git a/sysdeps/aarch64/fpu/log10_sve.c b/sysdeps/aarch64/fpu/log10_sve.c
new file mode 100644
index 0000000000..91060ab4a2
--- /dev/null
+++ b/sysdeps/aarch64/fpu/log10_sve.c
@@ -0,0 +1,76 @@
+/* Double-precision vector (SVE) log10 function
+
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include "sv_math.h"
+#include "poly_sve_f64.h"
+
+#define Min 0x0010000000000000
+#define Max 0x7ff0000000000000
+#define Thres 0x7fe0000000000000 /* Max - Min.  */
+#define Off 0x3fe6900900000000
+#define N (1 << V_LOG10_TABLE_BITS)
+
+static svfloat64_t NOINLINE
+special_case (svfloat64_t x, svfloat64_t y, svbool_t special)
+{
+  return sv_call_f64 (log10, x, y, special);
+}
+
+/* SVE log10 algorithm.
+   Maximum measured error is 2.46 ulps.
+   SV_NAME_D1 (log10)(0x1.131956cd4b627p+0) got 0x1.fffbdf6eaa669p-6
+					   want 0x1.fffbdf6eaa667p-6.  */
+svfloat64_t SV_NAME_D1 (log10) (svfloat64_t x, const svbool_t pg)
+{
+  svuint64_t ix = svreinterpret_u64 (x);
+  svbool_t special = svcmpge (pg, svsub_x (pg, ix, Min), Thres);
+
+  /* x = 2^k z; where z is in range [Off,2*Off) and exact.
+     The range is split into N subintervals.
+     The ith subinterval contains z and c is near its center.  */
+  svuint64_t tmp = svsub_x (pg, ix, Off);
+  svuint64_t i = svlsr_x (pg, tmp, 51 - V_LOG10_TABLE_BITS);
+  i = svand_x (pg, i, (N - 1) << 1);
+  svfloat64_t k = svcvt_f64_x (pg, svasr_x (pg, svreinterpret_s64 (tmp), 52));
+  svfloat64_t z = svreinterpret_f64 (
+      svsub_x (pg, ix, svand_x (pg, tmp, 0xfffULL << 52)));
+
+  /* log(x) = k*log(2) + log(c) + log(z/c).  */
+  svfloat64_t invc = svld1_gather_index (pg, &__v_log10_data.table[0].invc, i);
+  svfloat64_t logc
+      = svld1_gather_index (pg, &__v_log10_data.table[0].log10c, i);
+
+  /* We approximate log(z/c) with a polynomial P(x) ~= log(x + 1):
+     r = z/c - 1 (we look up precomputed 1/c)
+     log(z/c) ~= P(r).  */
+  svfloat64_t r = svmad_x (pg, invc, z, -1.0);
+
+  /* hi = log(c) + k*log(2).  */
+  svfloat64_t w = svmla_x (pg, logc, r, __v_log10_data.invln10);
+  svfloat64_t hi = svmla_x (pg, w, k, __v_log10_data.log10_2);
+
+  /* y = r2*(A0 + r*A1 + r2*(A2 + r*A3 + r2*A4)) + hi.  */
+  svfloat64_t r2 = svmul_x (pg, r, r);
+  svfloat64_t y = sv_pw_horner_4_f64_x (pg, r, r2, __v_log10_data.poly);
+
+  if (__glibc_unlikely (svptest_any (pg, special)))
+    return special_case (x, svmla_x (svnot_z (pg, special), hi, r2, y),
+			 special);
+  return svmla_x (pg, hi, r2, y);
+}
diff --git a/sysdeps/aarch64/fpu/log10f_advsimd.c b/sysdeps/aarch64/fpu/log10f_advsimd.c
new file mode 100644
index 0000000000..ba02060bbe
--- /dev/null
+++ b/sysdeps/aarch64/fpu/log10f_advsimd.c
@@ -0,0 +1,82 @@
+/* Single-precision vector (AdvSIMD) log10 function
+
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include "v_math.h"
+#include "poly_advsimd_f32.h"
+
+static const struct data
+{
+  uint32x4_t min_norm;
+  uint16x8_t special_bound;
+  float32x4_t poly[8];
+  float32x4_t inv_ln10, ln2;
+  uint32x4_t off, mantissa_mask;
+} data = {
+  /* Use order 9 for log10(1+x), i.e. order 8 for log10(1+x)/x, with x in
+      [-1/3, 1/3] (offset=2/3). Max. relative error: 0x1.068ee468p-25.  */
+  .poly = { V4 (-0x1.bcb79cp-3f), V4 (0x1.2879c8p-3f), V4 (-0x1.bcd472p-4f),
+	    V4 (0x1.6408f8p-4f), V4 (-0x1.246f8p-4f), V4 (0x1.f0e514p-5f),
+	    V4 (-0x1.0fc92cp-4f), V4 (0x1.f5f76ap-5f) },
+  .ln2 = V4 (0x1.62e43p-1f),
+  .inv_ln10 = V4 (0x1.bcb7b2p-2f),
+  .min_norm = V4 (0x00800000),
+  .special_bound = V8 (0x7f00), /* asuint32(inf) - min_norm.  */
+  .off = V4 (0x3f2aaaab),	/* 0.666667.  */
+  .mantissa_mask = V4 (0x007fffff),
+};
+
+static float32x4_t VPCS_ATTR NOINLINE
+special_case (float32x4_t x, float32x4_t y, float32x4_t p, float32x4_t r2,
+	      uint16x4_t cmp)
+{
+  /* Fall back to scalar code.  */
+  return v_call_f32 (log10f, x, vfmaq_f32 (y, p, r2), vmovl_u16 (cmp));
+}
+
+/* Fast implementation of AdvSIMD log10f,
+   uses a similar approach as AdvSIMD logf with the same offset (i.e., 2/3) and
+   an order 9 polynomial.
+   Maximum error: 3.305ulps (nearest rounding.)
+   _ZGVnN4v_log10f(0x1.555c16p+0) got 0x1.ffe2fap-4
+				 want 0x1.ffe2f4p-4.  */
+float32x4_t VPCS_ATTR V_NAME_F1 (log10) (float32x4_t x)
+{
+  const struct data *d = ptr_barrier (&data);
+  uint32x4_t u = vreinterpretq_u32_f32 (x);
+  uint16x4_t special = vcge_u16 (vsubhn_u32 (u, d->min_norm),
+				 vget_low_u16 (d->special_bound));
+
+  /* x = 2^n * (1+r), where 2/3 < 1+r < 4/3.  */
+  u = vsubq_u32 (u, d->off);
+  float32x4_t n = vcvtq_f32_s32 (
+      vshrq_n_s32 (vreinterpretq_s32_u32 (u), 23)); /* signextend.  */
+  u = vaddq_u32 (vandq_u32 (u, d->mantissa_mask), d->off);
+  float32x4_t r = vsubq_f32 (vreinterpretq_f32_u32 (u), v_f32 (1.0f));
+
+  /* y = log10(1+r) + n * log10(2).  */
+  float32x4_t r2 = vmulq_f32 (r, r);
+  float32x4_t poly = v_pw_horner_7_f32 (r, r2, d->poly);
+  /* y = Log10(2) * n + poly * InvLn(10).  */
+  float32x4_t y = vfmaq_f32 (r, d->ln2, n);
+  y = vmulq_f32 (y, d->inv_ln10);
+
+  if (__glibc_unlikely (v_any_u16h (special)))
+    return special_case (x, y, poly, r2, special);
+  return vfmaq_f32 (y, poly, r2);
+}
diff --git a/sysdeps/aarch64/fpu/log10f_sve.c b/sysdeps/aarch64/fpu/log10f_sve.c
new file mode 100644
index 0000000000..8729b17ef9
--- /dev/null
+++ b/sysdeps/aarch64/fpu/log10f_sve.c
@@ -0,0 +1,94 @@
+/* Single-precision vector (SVE) log10 function
+
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include "sv_math.h"
+
+static const struct data
+{
+  float poly_0246[4];
+  float poly_1357[4];
+  float ln2, inv_ln10;
+} data = {
+  .poly_1357 = {
+    /* Coefficients copied from the AdvSIMD routine, then rearranged so that coeffs
+       1, 3, 5 and 7 can be loaded as a single quad-word, hence used with _lane
+       variant of MLA intrinsic.  */
+    0x1.2879c8p-3f, 0x1.6408f8p-4f, 0x1.f0e514p-5f, 0x1.f5f76ap-5f
+  },
+  .poly_0246 = { -0x1.bcb79cp-3f, -0x1.bcd472p-4f, -0x1.246f8p-4f,
+		 -0x1.0fc92cp-4f },
+  .ln2 = 0x1.62e43p-1f,
+  .inv_ln10 = 0x1.bcb7b2p-2f,
+};
+
+#define Min 0x00800000
+#define Max 0x7f800000
+#define Thres 0x7f000000  /* Max - Min.  */
+#define Offset 0x3f2aaaab /* 0.666667.  */
+#define MantissaMask 0x007fffff
+
+static svfloat32_t NOINLINE
+special_case (svfloat32_t x, svfloat32_t y, svbool_t special)
+{
+  return sv_call_f32 (log10f, x, y, special);
+}
+
+/* Optimised implementation of SVE log10f using the same algorithm and
+   polynomial as AdvSIMD log10f.
+   Maximum error is 3.31ulps:
+   SV_NAME_F1 (log10)(0x1.555c16p+0) got 0x1.ffe2fap-4
+				    want 0x1.ffe2f4p-4.  */
+svfloat32_t SV_NAME_F1 (log10) (svfloat32_t x, const svbool_t pg)
+{
+  const struct data *d = ptr_barrier (&data);
+  svuint32_t ix = svreinterpret_u32 (x);
+  svbool_t special = svcmpge (pg, svsub_x (pg, ix, Min), Thres);
+
+  /* x = 2^n * (1+r), where 2/3 < 1+r < 4/3.  */
+  ix = svsub_x (pg, ix, Offset);
+  svfloat32_t n = svcvt_f32_x (
+      pg, svasr_x (pg, svreinterpret_s32 (ix), 23)); /* signextend.  */
+  ix = svand_x (pg, ix, MantissaMask);
+  ix = svadd_x (pg, ix, Offset);
+  svfloat32_t r = svsub_x (pg, svreinterpret_f32 (ix), 1.0f);
+
+  /* y = log10(1+r) + n*log10(2)
+     log10(1+r) ~ r * InvLn(10) + P(r)
+     where P(r) is a polynomial. Use order 9 for log10(1+x), i.e. order 8 for
+     log10(1+x)/x, with x in [-1/3, 1/3] (offset=2/3).  */
+  svfloat32_t r2 = svmul_x (pg, r, r);
+  svfloat32_t r4 = svmul_x (pg, r2, r2);
+  svfloat32_t p_1357 = svld1rq (svptrue_b32 (), &d->poly_1357[0]);
+  svfloat32_t q_01 = svmla_lane (sv_f32 (d->poly_0246[0]), r, p_1357, 0);
+  svfloat32_t q_23 = svmla_lane (sv_f32 (d->poly_0246[1]), r, p_1357, 1);
+  svfloat32_t q_45 = svmla_lane (sv_f32 (d->poly_0246[2]), r, p_1357, 2);
+  svfloat32_t q_67 = svmla_lane (sv_f32 (d->poly_0246[3]), r, p_1357, 3);
+  svfloat32_t q_47 = svmla_x (pg, q_45, r2, q_67);
+  svfloat32_t q_03 = svmla_x (pg, q_01, r2, q_23);
+  svfloat32_t y = svmla_x (pg, q_03, r4, q_47);
+
+  /* Using hi = Log10(2)*n + r*InvLn(10) is faster but less accurate.  */
+  svfloat32_t hi = svmla_x (pg, r, n, d->ln2);
+  hi = svmul_x (pg, hi, d->inv_ln10);
+
+  if (__glibc_unlikely (svptest_any (pg, special)))
+    return special_case (x, svmla_x (svnot_z (pg, special), hi, r2, y),
+			 special);
+  return svmla_x (pg, hi, r2, y);
+}
diff --git a/sysdeps/aarch64/fpu/test-double-advsimd-wrappers.c b/sysdeps/aarch64/fpu/test-double-advsimd-wrappers.c
index d30dcd6f95..8d05498ec9 100644
--- a/sysdeps/aarch64/fpu/test-double-advsimd-wrappers.c
+++ b/sysdeps/aarch64/fpu/test-double-advsimd-wrappers.c
@@ -27,6 +27,7 @@ VPCS_VECTOR_WRAPPER (cos_advsimd, _ZGVnN2v_cos)
 VPCS_VECTOR_WRAPPER (exp_advsimd, _ZGVnN2v_exp)
 VPCS_VECTOR_WRAPPER (exp2_advsimd, _ZGVnN2v_exp2)
 VPCS_VECTOR_WRAPPER (log_advsimd, _ZGVnN2v_log)
+VPCS_VECTOR_WRAPPER (log10_advsimd, _ZGVnN2v_log10)
 VPCS_VECTOR_WRAPPER (log2_advsimd, _ZGVnN2v_log2)
 VPCS_VECTOR_WRAPPER (sin_advsimd, _ZGVnN2v_sin)
 VPCS_VECTOR_WRAPPER (tan_advsimd, _ZGVnN2v_tan)
diff --git a/sysdeps/aarch64/fpu/test-double-sve-wrappers.c b/sysdeps/aarch64/fpu/test-double-sve-wrappers.c
index 22a8479100..b65bc6f1e6 100644
--- a/sysdeps/aarch64/fpu/test-double-sve-wrappers.c
+++ b/sysdeps/aarch64/fpu/test-double-sve-wrappers.c
@@ -36,6 +36,7 @@ SVE_VECTOR_WRAPPER (cos_sve, _ZGVsMxv_cos)
 SVE_VECTOR_WRAPPER (exp_sve, _ZGVsMxv_exp)
 SVE_VECTOR_WRAPPER (exp2_sve, _ZGVsMxv_exp2)
 SVE_VECTOR_WRAPPER (log_sve, _ZGVsMxv_log)
+SVE_VECTOR_WRAPPER (log10_sve, _ZGVsMxv_log10)
 SVE_VECTOR_WRAPPER (log2_sve, _ZGVsMxv_log2)
 SVE_VECTOR_WRAPPER (sin_sve, _ZGVsMxv_sin)
 SVE_VECTOR_WRAPPER (tan_sve, _ZGVsMxv_tan)
diff --git a/sysdeps/aarch64/fpu/test-float-advsimd-wrappers.c b/sysdeps/aarch64/fpu/test-float-advsimd-wrappers.c
index e8f7f47c67..6ced0d4488 100644
--- a/sysdeps/aarch64/fpu/test-float-advsimd-wrappers.c
+++ b/sysdeps/aarch64/fpu/test-float-advsimd-wrappers.c
@@ -27,6 +27,7 @@ VPCS_VECTOR_WRAPPER (cosf_advsimd, _ZGVnN4v_cosf)
 VPCS_VECTOR_WRAPPER (expf_advsimd, _ZGVnN4v_expf)
 VPCS_VECTOR_WRAPPER (exp2f_advsimd, _ZGVnN4v_exp2f)
 VPCS_VECTOR_WRAPPER (logf_advsimd, _ZGVnN4v_logf)
+VPCS_VECTOR_WRAPPER (log10f_advsimd, _ZGVnN4v_log10f)
 VPCS_VECTOR_WRAPPER (log2f_advsimd, _ZGVnN4v_log2f)
 VPCS_VECTOR_WRAPPER (sinf_advsimd, _ZGVnN4v_sinf)
 VPCS_VECTOR_WRAPPER (tanf_advsimd, _ZGVnN4v_tanf)
diff --git a/sysdeps/aarch64/fpu/test-float-sve-wrappers.c b/sysdeps/aarch64/fpu/test-float-sve-wrappers.c
index f5e9584265..2ed8d0659a 100644
--- a/sysdeps/aarch64/fpu/test-float-sve-wrappers.c
+++ b/sysdeps/aarch64/fpu/test-float-sve-wrappers.c
@@ -36,6 +36,7 @@ SVE_VECTOR_WRAPPER (cosf_sve, _ZGVsMxv_cosf)
 SVE_VECTOR_WRAPPER (expf_sve, _ZGVsMxv_expf)
 SVE_VECTOR_WRAPPER (exp2f_sve, _ZGVsMxv_exp2f)
 SVE_VECTOR_WRAPPER (logf_sve, _ZGVsMxv_logf)
+SVE_VECTOR_WRAPPER (log10f_sve, _ZGVsMxv_log10f)
 SVE_VECTOR_WRAPPER (log2f_sve, _ZGVsMxv_log2f)
 SVE_VECTOR_WRAPPER (sinf_sve, _ZGVsMxv_sinf)
 SVE_VECTOR_WRAPPER (tanf_sve, _ZGVsMxv_tanf)
diff --git a/sysdeps/aarch64/fpu/v_log10_data.c b/sysdeps/aarch64/fpu/v_log10_data.c
new file mode 100644
index 0000000000..564be8eebe
--- /dev/null
+++ b/sysdeps/aarch64/fpu/v_log10_data.c
@@ -0,0 +1,175 @@
+/* Lookup table for double-precision log10(x) vector function.
+
+   Copyright (C) 2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include "vecmath_config.h"
+
+const struct v_log10_data __v_log10_data = {
+  /* Computed from log's coefficients div by log(10) then rounded to double
+     precision.  */
+  .poly = { -0x1.bcb7b1526e506p-3, 0x1.287a7636be1d1p-3, -0x1.bcb7b158af938p-4,
+	    0x1.63c78734e6d07p-4, -0x1.287461742fee4p-4 },
+  .invln10 = 0x1.bcb7b1526e50ep-2,
+  .log10_2 = 0x1.34413509f79ffp-2,
+  /* Algorithm:
+
+	x = 2^k z
+	log10(x) = k log10(2) + log10(c) + poly(z/c - 1) / log(10)
+
+     where z is in [a;2a) which is split into N subintervals (a=0x1.69009p-1,
+     N=128) and log(c) and 1/c for the ith subinterval comes from lookup
+     tables:
+
+	table[i].invc = 1/c
+	table[i].log10c = (double)log10(c)
+
+     where c is near the center of the subinterval and is chosen by trying
+     several floating point invc candidates around 1/center and selecting one
+     for which the error in (double)log(c) is minimized (< 0x1p-74), except the
+     subinterval that contains 1 and the previous one got tweaked to avoid
+     cancellation. NB: invc should be optimized to minimize error in
+     (double)log10(c) instead.  */
+  .table = { { 0x1.6a133d0dec120p+0, -0x1.345825f221684p-3 },
+	     { 0x1.6815f2f3e42edp+0, -0x1.2f71a1f0c554ep-3 },
+	     { 0x1.661e39be1ac9ep+0, -0x1.2a91fdb30b1f4p-3 },
+	     { 0x1.642bfa30ac371p+0, -0x1.25b9260981a04p-3 },
+	     { 0x1.623f1d916f323p+0, -0x1.20e7081762193p-3 },
+	     { 0x1.60578da220f65p+0, -0x1.1c1b914aeefacp-3 },
+	     { 0x1.5e75349dea571p+0, -0x1.1756af5de404dp-3 },
+	     { 0x1.5c97fd387a75ap+0, -0x1.12985059c90bfp-3 },
+	     { 0x1.5abfd2981f200p+0, -0x1.0de0628f63df4p-3 },
+	     { 0x1.58eca051dc99cp+0, -0x1.092ed492e08eep-3 },
+	     { 0x1.571e526d9df12p+0, -0x1.0483954caf1dfp-3 },
+	     { 0x1.5554d555b3fcbp+0, -0x1.ffbd27a9adbcp-4 },
+	     { 0x1.539015e2a20cdp+0, -0x1.f67f7f2e3d1ap-4 },
+	     { 0x1.51d0014ee0164p+0, -0x1.ed4e1071ceebep-4 },
+	     { 0x1.50148538cd9eep+0, -0x1.e428bb47413c4p-4 },
+	     { 0x1.4e5d8f9f698a1p+0, -0x1.db0f6003028d6p-4 },
+	     { 0x1.4cab0edca66bep+0, -0x1.d201df6749831p-4 },
+	     { 0x1.4afcf1a9db874p+0, -0x1.c9001ac5c9672p-4 },
+	     { 0x1.495327136e16fp+0, -0x1.c009f3c78c79p-4 },
+	     { 0x1.47ad9e84af28fp+0, -0x1.b71f4cb642e53p-4 },
+	     { 0x1.460c47b39ae15p+0, -0x1.ae400818526b2p-4 },
+	     { 0x1.446f12b278001p+0, -0x1.a56c091954f87p-4 },
+	     { 0x1.42d5efdd720ecp+0, -0x1.9ca3332f096eep-4 },
+	     { 0x1.4140cfe001a0fp+0, -0x1.93e56a3f23e55p-4 },
+	     { 0x1.3fafa3b421f69p+0, -0x1.8b3292a3903bp-4 },
+	     { 0x1.3e225c9c8ece5p+0, -0x1.828a9112d9618p-4 },
+	     { 0x1.3c98ec29a211ap+0, -0x1.79ed4ac35f5acp-4 },
+	     { 0x1.3b13442a413fep+0, -0x1.715aa51ed28c4p-4 },
+	     { 0x1.399156baa3c54p+0, -0x1.68d2861c999e9p-4 },
+	     { 0x1.38131639b4cdbp+0, -0x1.6054d40ded21p-4 },
+	     { 0x1.36987540fbf53p+0, -0x1.57e17576bc9a2p-4 },
+	     { 0x1.352166b648f61p+0, -0x1.4f7851798bb0bp-4 },
+	     { 0x1.33adddb3eb575p+0, -0x1.47194f5690ae3p-4 },
+	     { 0x1.323dcd99fc1d3p+0, -0x1.3ec456d58ec47p-4 },
+	     { 0x1.30d129fefc7d2p+0, -0x1.36794ff3e5f55p-4 },
+	     { 0x1.2f67e6b72fe7dp+0, -0x1.2e382315725e4p-4 },
+	     { 0x1.2e01f7cf8b187p+0, -0x1.2600b8ed82e91p-4 },
+	     { 0x1.2c9f518ddc86ep+0, -0x1.1dd2fa85efc12p-4 },
+	     { 0x1.2b3fe86e5f413p+0, -0x1.15aed136e3961p-4 },
+	     { 0x1.29e3b1211b25cp+0, -0x1.0d94269d1a30dp-4 },
+	     { 0x1.288aa08b373cfp+0, -0x1.0582e4a7659f5p-4 },
+	     { 0x1.2734abcaa8467p+0, -0x1.faf5eb655742dp-5 },
+	     { 0x1.25e1c82459b81p+0, -0x1.eaf888487e8eep-5 },
+	     { 0x1.2491eb1ad59c5p+0, -0x1.db0d75ef25a82p-5 },
+	     { 0x1.23450a54048b5p+0, -0x1.cb348a49e6431p-5 },
+	     { 0x1.21fb1bb09e578p+0, -0x1.bb6d9c69acdd8p-5 },
+	     { 0x1.20b415346d8f7p+0, -0x1.abb88368aa7ap-5 },
+	     { 0x1.1f6fed179a1acp+0, -0x1.9c1517476af14p-5 },
+	     { 0x1.1e2e99b93c7b3p+0, -0x1.8c833051bfa4dp-5 },
+	     { 0x1.1cf011a7a882ap+0, -0x1.7d02a78e7fb31p-5 },
+	     { 0x1.1bb44b97dba5ap+0, -0x1.6d93565e97c5fp-5 },
+	     { 0x1.1a7b3e66cdd4fp+0, -0x1.5e351695db0c5p-5 },
+	     { 0x1.1944e11dc56cdp+0, -0x1.4ee7c2ba67adcp-5 },
+	     { 0x1.18112aebb1a6ep+0, -0x1.3fab35ba16c01p-5 },
+	     { 0x1.16e013231b7e9p+0, -0x1.307f4ad854bc9p-5 },
+	     { 0x1.15b1913f156cfp+0, -0x1.2163ddf4f988cp-5 },
+	     { 0x1.14859cdedde13p+0, -0x1.1258cb5d19e22p-5 },
+	     { 0x1.135c2dc68cfa4p+0, -0x1.035defdba3188p-5 },
+	     { 0x1.12353bdb01684p+0, -0x1.e8e651191bce4p-6 },
+	     { 0x1.1110bf25b85b4p+0, -0x1.cb30a62be444cp-6 },
+	     { 0x1.0feeafd2f8577p+0, -0x1.ad9a9b3043823p-6 },
+	     { 0x1.0ecf062c51c3bp+0, -0x1.9023ecda1ccdep-6 },
+	     { 0x1.0db1baa076c8bp+0, -0x1.72cc592bd82dp-6 },
+	     { 0x1.0c96c5bb3048ep+0, -0x1.55939eb1f9c6ep-6 },
+	     { 0x1.0b7e20263e070p+0, -0x1.38797ca6cc5ap-6 },
+	     { 0x1.0a67c2acd0ce3p+0, -0x1.1b7db35c2c072p-6 },
+	     { 0x1.0953a6391e982p+0, -0x1.fd400812ee9a2p-7 },
+	     { 0x1.0841c3caea380p+0, -0x1.c3c05fb4620f1p-7 },
+	     { 0x1.07321489b13eap+0, -0x1.8a7bf3c40e2e3p-7 },
+	     { 0x1.062491aee9904p+0, -0x1.517249c15a75cp-7 },
+	     { 0x1.05193497a7cc5p+0, -0x1.18a2ea5330c91p-7 },
+	     { 0x1.040ff6b5f5e9fp+0, -0x1.c01abc8cdc4e2p-8 },
+	     { 0x1.0308d19aa6127p+0, -0x1.4f6261750dec9p-8 },
+	     { 0x1.0203beedb0c67p+0, -0x1.be37b6612afa7p-9 },
+	     { 0x1.010037d38bcc2p+0, -0x1.bc3a8398ac26p-10 },
+	     { 1.0, 0.0 },
+	     { 0x1.fc06d493cca10p-1, 0x1.bb796219f30a5p-9 },
+	     { 0x1.f81e6ac3b918fp-1, 0x1.b984fdcba61cep-8 },
+	     { 0x1.f44546ef18996p-1, 0x1.49cf12adf8e8cp-7 },
+	     { 0x1.f07b10382c84bp-1, 0x1.b6075b5217083p-7 },
+	     { 0x1.ecbf7070e59d4p-1, 0x1.10b7466fc30ddp-6 },
+	     { 0x1.e91213f715939p-1, 0x1.4603e4db6a3a1p-6 },
+	     { 0x1.e572a9a75f7b7p-1, 0x1.7aeb10e99e105p-6 },
+	     { 0x1.e1e0e2c530207p-1, 0x1.af6e49b0f0e36p-6 },
+	     { 0x1.de5c72d8a8be3p-1, 0x1.e38f064f41179p-6 },
+	     { 0x1.dae50fa5658ccp-1, 0x1.0ba75abbb7623p-5 },
+	     { 0x1.d77a71145a2dap-1, 0x1.25575ee2dba86p-5 },
+	     { 0x1.d41c51166623ep-1, 0x1.3ed83f477f946p-5 },
+	     { 0x1.d0ca6ba0bb29fp-1, 0x1.582aa79af60efp-5 },
+	     { 0x1.cd847e8e59681p-1, 0x1.714f400fa83aep-5 },
+	     { 0x1.ca4a499693e00p-1, 0x1.8a46ad3901cb9p-5 },
+	     { 0x1.c71b8e399e821p-1, 0x1.a311903b6b87p-5 },
+	     { 0x1.c3f80faf19077p-1, 0x1.bbb086f216911p-5 },
+	     { 0x1.c0df92dc2b0ecp-1, 0x1.d4242bdda648ep-5 },
+	     { 0x1.bdd1de3cbb542p-1, 0x1.ec6d167c2af1p-5 },
+	     { 0x1.baceb9e1007a3p-1, 0x1.0245ed8221426p-4 },
+	     { 0x1.b7d5ef543e55ep-1, 0x1.0e40856c74f64p-4 },
+	     { 0x1.b4e749977d953p-1, 0x1.1a269a31120fep-4 },
+	     { 0x1.b20295155478ep-1, 0x1.25f8718fc076cp-4 },
+	     { 0x1.af279f8e82be2p-1, 0x1.31b64ffc95bfp-4 },
+	     { 0x1.ac5638197fdf3p-1, 0x1.3d60787ca5063p-4 },
+	     { 0x1.a98e2f102e087p-1, 0x1.48f72ccd187fdp-4 },
+	     { 0x1.a6cf5606d05c1p-1, 0x1.547aad6602f1cp-4 },
+	     { 0x1.a4197fc04d746p-1, 0x1.5feb3989d3acbp-4 },
+	     { 0x1.a16c80293dc01p-1, 0x1.6b490f3978c79p-4 },
+	     { 0x1.9ec82c4dc5bc9p-1, 0x1.76946b3f5e703p-4 },
+	     { 0x1.9c2c5a491f534p-1, 0x1.81cd895717c83p-4 },
+	     { 0x1.9998e1480b618p-1, 0x1.8cf4a4055c30ep-4 },
+	     { 0x1.970d9977c6c2dp-1, 0x1.9809f4c48c0ebp-4 },
+	     { 0x1.948a5c023d212p-1, 0x1.a30db3f9899efp-4 },
+	     { 0x1.920f0303d6809p-1, 0x1.ae001905458fcp-4 },
+	     { 0x1.8f9b698a98b45p-1, 0x1.b8e15a2e3a2cdp-4 },
+	     { 0x1.8d2f6b81726f6p-1, 0x1.c3b1ace2b0996p-4 },
+	     { 0x1.8acae5bb55badp-1, 0x1.ce71456edfa62p-4 },
+	     { 0x1.886db5d9275b8p-1, 0x1.d9205759882c4p-4 },
+	     { 0x1.8617ba567c13cp-1, 0x1.e3bf1513af0dfp-4 },
+	     { 0x1.83c8d27487800p-1, 0x1.ee4db0412c414p-4 },
+	     { 0x1.8180de3c5dbe7p-1, 0x1.f8cc5998de3a5p-4 },
+	     { 0x1.7f3fbe71cdb71p-1, 0x1.019da085eaeb1p-3 },
+	     { 0x1.7d055498071c1p-1, 0x1.06cd4acdb4e3dp-3 },
+	     { 0x1.7ad182e54f65ap-1, 0x1.0bf542bef813fp-3 },
+	     { 0x1.78a42c3c90125p-1, 0x1.11159f14da262p-3 },
+	     { 0x1.767d342f76944p-1, 0x1.162e761c10d1cp-3 },
+	     { 0x1.745c7ef26b00ap-1, 0x1.1b3fddc60d43ep-3 },
+	     { 0x1.7241f15769d0fp-1, 0x1.2049ebac86aa6p-3 },
+	     { 0x1.702d70d396e41p-1, 0x1.254cb4fb7836ap-3 },
+	     { 0x1.6e1ee3700cd11p-1, 0x1.2a484e8d0d252p-3 },
+	     { 0x1.6c162fc9cbe02p-1, 0x1.2f3ccce1c860bp-3 } }
+};
diff --git a/sysdeps/aarch64/fpu/vecmath_config.h b/sysdeps/aarch64/fpu/vecmath_config.h
index 3aa6c280aa..2c8e243236 100644
--- a/sysdeps/aarch64/fpu/vecmath_config.h
+++ b/sysdeps/aarch64/fpu/vecmath_config.h
@@ -62,4 +62,15 @@ extern const struct v_log2_data
   } table[1 << V_LOG2_TABLE_BITS];
 } __v_log2_data attribute_hidden;
 
+#define V_LOG10_TABLE_BITS 7
+extern const struct v_log10_data
+{
+  double poly[5];
+  double invln10, log10_2;
+  struct
+  {
+    double invc, log10c;
+  } table[1 << V_LOG10_TABLE_BITS];
+} __v_log10_data attribute_hidden;
+
 #endif