[PATCH 1/3] aarch64: Optimise AdvSIMD acoshf
Adhemerval Zanella Netto
adhemerval.zanella@linaro.org
Thu Dec 4 13:43:03 GMT 2025
On 28/11/25 08:18, James Chesterman wrote:
> Optimise AdvSIMD acoshf by vectorising the special case.
> For values greater than 0x1p64, scale the input down first.
> This is because the output will overflow with inputs greater than
> or equal to this value as there is a squaring operation in the
> algorithm.
> To scale, do:
> 2acosh(sqrt[(x+1)/2])
> Because:
> acosh(x) = 1/2acosh(2x^2 - 1) for x>=1.
> Apply opposite operations in opposite order for x, and you get:
> acosh(x) = 2acosh(sqrt[(x+1)/2]).
>
> R.Throughput difference on V2 with GCC@15:
> 30-49% improvement in special cases.
> 2% regression in fast pass.
> ---
> OK for master? If so, please commit for me as I don't have commit rights.
> Thanks,
> James
LGTM, thanks.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
> sysdeps/aarch64/fpu/acoshf_advsimd.c | 79 +++++++++++++++++++++-------
> 1 file changed, 61 insertions(+), 18 deletions(-)
>
> diff --git a/sysdeps/aarch64/fpu/acoshf_advsimd.c b/sysdeps/aarch64/fpu/acoshf_advsimd.c
> index 7c3f590723..1d0ff5ad19 100644
> --- a/sysdeps/aarch64/fpu/acoshf_advsimd.c
> +++ b/sysdeps/aarch64/fpu/acoshf_advsimd.c
> @@ -19,21 +19,66 @@
>
> #include "v_log1pf_inline.h"
>
> -#define SquareLim 0x1p64
> -
> const static struct data
> {
> struct v_log1pf_data log1pf_consts;
> uint32x4_t one;
> -} data = { .log1pf_consts = V_LOG1PF_CONSTANTS_TABLE, .one = V4 (0x3f800000) };
> + uint16x4_t special_bound_u16;
> + uint32x4_t special_bound_u32;
> + float32x4_t pinf, nan;
> +} data = {
> + .log1pf_consts = V_LOG1PF_CONSTANTS_TABLE,
> + .one = V4 (0x3f800000),
> + .special_bound_u16 = V4 (0x2000),
> + /* asuint(sqrt(FLT_MAX)) - asuint(1). */
> + .special_bound_u32 = V4 (0x20000000),
> + .pinf = V4 (INFINITY),
> + .nan = V4 (NAN),
> +};
>
> -#define Thresh vdup_n_u16 (0x2000) /* top(asuint(SquareLim) - asuint(1)). */
> +static inline float32x4_t VPCS_ATTR
> +inline_acoshf (float32x4_t x, const struct data *d)
> +{
> + /* acosh(x) = ln(x + sqrt[x^2 -1]).
> + So acosh(x) = log1p (x + sqrt[x^2 - 1] - 1). */
> + float32x4_t xm1 = vsubq_f32 (x, vreinterpretq_f32_u32 (d->one));
> + float32x4_t u
> + = vmulq_f32 (xm1, vaddq_f32 (x, vreinterpretq_f32_u32 (d->one)));
>
> -static float32x4_t NOINLINE VPCS_ATTR
> -special_case (float32x4_t x, float32x4_t y, uint16x4_t special,
> - const struct v_log1pf_data *d)
> + float32x4_t y = vaddq_f32 (xm1, vsqrtq_f32 (u));
> +
> + return log1pf_inline (y, &d->log1pf_consts);
> +}
> +
> +static float32x4_t VPCS_ATTR NOINLINE
> +special_case (float32x4_t x, const struct data *d)
> {
> - return v_call_f32 (acoshf, x, log1pf_inline (y, d), vmovl_u16 (special));
> + uint32x4_t special = vcgeq_u32 (
> + vsubq_u32 (vreinterpretq_u32_f32 (x), d->one), d->special_bound_u32);
> +
> + /* To avoid the overflow in x^2 (so the x < sqrt(FLT_MAX) constraint), we
> + reduce the input of acosh to a narrower interval by relying on the identity
> + acosh(t) = 1/2acosh(2t^2 - 1) for t>=1.
> + If we set t=sqrt((x+1)/2), since x>=1 then t>=sqrt(2/2)=1, and therefore
> + acosh(x) = 2acosh(sqrt((x+1)/2)). */
> + float32x4_t r = vaddq_f32 (x, vreinterpretq_f32_u32 (d->one));
> + r = vmulq_f32 (r, v_f32 (0.5f));
> + r = vbslq_f32 (special, vsqrtq_f32 (r), x);
> +
> + float32x4_t y = inline_acoshf (r, d);
> +
> + y = vbslq_f32 (special, vmulq_f32 (y, v_f32 (2.0f)), y);
> +
> + /* Check whether x is less than 1, or x is inf or nan. */
> + uint32x4_t inf_minus_one
> + = vsubq_u32 (vreinterpretq_u32_f32 (d->pinf), d->one);
> + uint32x4_t is_infnan = vcgeq_u32 (
> + vsubq_u32 (vreinterpretq_u32_f32 (x), d->one), inf_minus_one);
> +
> + y = vbslq_f32 (is_infnan, d->nan, y);
> + uint32x4_t ret_pinf = vceqq_f32 (x, d->pinf);
> + y = vbslq_f32 (ret_pinf, d->pinf, y);
> + return y;
> }
>
> /* Vector approximation for single-precision acosh, based on log1p.
> @@ -41,21 +86,19 @@ special_case (float32x4_t x, float32x4_t y, uint16x4_t special,
> _ZGVnN4v_acoshf(0x1.007ef2p+0) got 0x1.fdcdccp-5
> want 0x1.fdcdd2p-5. */
>
> -VPCS_ATTR float32x4_t NOINLINE V_NAME_F1 (acosh) (float32x4_t x)
> +float32x4_t VPCS_ATTR NOINLINE V_NAME_F1 (acosh) (float32x4_t x)
> {
> const struct data *d = ptr_barrier (&data);
> uint32x4_t ix = vreinterpretq_u32_f32 (x);
> - uint16x4_t special = vcge_u16 (vsubhn_u32 (ix, d->one), Thresh);
> -
> - float32x4_t xm1 = vsubq_f32 (x, vreinterpretq_f32_u32 (d->one));
> - float32x4_t u
> - = vmulq_f32 (xm1, vaddq_f32 (x, vreinterpretq_f32_u32 (d->one)));
> -
> - float32x4_t y = vaddq_f32 (xm1, vsqrtq_f32 (u));
> + /* Inputs greater than or equal to special_bound will cause the output to
> + overflow. This is because there is a square operation in log1pf_inline.
> + This also captures inf, nan and any input less than or equal to 1. */
> + uint16x4_t special
> + = vcge_u16 (vsubhn_u32 (ix, d->one), d->special_bound_u16);
>
> if (__glibc_unlikely (v_any_u16h (special)))
> - return special_case (x, y, special, &d->log1pf_consts);
> - return log1pf_inline (y, &d->log1pf_consts);
> + return special_case (x, d);
> + return inline_acoshf (x, d);
> }
> libmvec_hidden_def (V_NAME_F1 (acosh))
> HALF_WIDTH_ALIAS_F1 (acosh)
More information about the Libc-alpha
mailing list