[libc-commits] [libc] 65dc901 - [libc][AArch64] Make mathvec/aarch64/expf.cpp compile big-endian (#218945)
via libc-commits
libc-commits at lists.llvm.org
Thu Aug 27 01:11:21 PDT 2026
Author: Simon Tatham
Date: 2026-08-27T09:11:16+01:00
New Revision: 65dc9011cedef7236feaec832d37813167c19f10
URL: https://github.com/llvm/llvm-project/commit/65dc9011cedef7236feaec832d37813167c19f10
DIFF: https://github.com/llvm/llvm-project/commit/65dc9011cedef7236feaec832d37813167c19f10.diff
LOG: [libc][AArch64] Make mathvec/aarch64/expf.cpp compile big-endian (#218945)
The macros `V2` and `V4`, defined in `common.h`, are intended to
construct an initializer expression for a vector of 2 or 4 copies of the
same value, used in constant data initialization. But they provoke a
compiler warning when used in a big-endian build: "vector initializers
are not compatible with NEON intrinsics in big endian mode". Usually
this warning is upgraded to an error, and causes the libc compilation to
fail, or at least the compilation of `mathvec/aarch64/expf.cpp` in the
tests.
ACLE doesn't specify any `constexpr`-compatible way to initialize a
vector. The next best thing is to store the lane value just once in the
expf data structure, and use a `vdupq_n` intrinsic to splat it across
multiple lanes.
In this patch, I've introduced a system of macros that does that in a
big-endian build, but keeps the existing strategy little-endian, in case
it's faster.
Added:
Modified:
libc/src/mathvec/aarch64/common.h
libc/src/mathvec/aarch64/expf.cpp
Removed:
################################################################################
diff --git a/libc/src/mathvec/aarch64/common.h b/libc/src/mathvec/aarch64/common.h
index 9b69167b363ac..0fbeb74fa01e8 100644
--- a/libc/src/mathvec/aarch64/common.h
+++ b/libc/src/mathvec/aarch64/common.h
@@ -26,6 +26,38 @@ using AdvSIMDFP64Vector = LIBC_NAMESPACE::cpp::simd<double, 2>;
__ptr; \
})
-// Helpers for declaring vector constants.
-#define V2(X) {X, X}
-#define V4(X) {X, X, X, X}
+// Helpers for declaring vector constants containing all lanes the same, in a
+// way that varies between little- and big-endian AArch64. Use as follows:
+//
+// - define a type for the constant using V2_SPLAT_TYPE or V4_SPLAT_TYPE, with
+// a type-prefix parameter like `float64` or `int32` which the macro will
+// extend into a full scalar or vector type name.
+//
+// - define the constant using V2_SPLAT_INITIALIZER or V4_SPLAT_INITIALIZER
+//
+// - to get the actual vector, use MAKE_SPLAT_VECTOR(constant, suffix), where
+// 'suffix' is something like `f64` or `u32` which appears in the name of a
+// NEON intrinsic to specify its element type.
+
+#if __LITTLE_ENDIAN__
+
+// Use the gcc language extension of defining a vector using an initializer
+// list, which allows the whole vector to be statically defined in const data.
+#define V2_SPLAT_TYPE(TYPE_PREFIX) TYPE_PREFIX##x2_t
+#define V2_SPLAT_INITIALIZER(LANE) {LANE, LANE}
+#define V4_SPLAT_TYPE(TYPE_PREFIX) TYPE_PREFIX##x4_t
+#define V4_SPLAT_INITIALIZER(LANE) {LANE, LANE, LANE, LANE}
+#define MAKE_SPLAT_VECTOR(VEC, FN_SUFFIX) VEC
+
+#else
+
+// Big-endian, that gcc language extension provokes a compiler diagnostic, so
+// instead we store just one copy of the data to be duplicated across lanes,
+// and perform the duplication using vdupq when loading it.
+#define V2_SPLAT_TYPE(TYPE_PREFIX) TYPE_PREFIX##_t
+#define V2_SPLAT_INITIALIZER(LANE) LANE
+#define V4_SPLAT_TYPE(TYPE_PREFIX) TYPE_PREFIX##_t
+#define V4_SPLAT_INITIALIZER(LANE) LANE
+#define MAKE_SPLAT_VECTOR(LANE, FN_SUFFIX) vdupq_n_##FN_SUFFIX(LANE)
+
+#endif
diff --git a/libc/src/mathvec/aarch64/expf.cpp b/libc/src/mathvec/aarch64/expf.cpp
index b7050f44b751c..e7576e38a8db6 100644
--- a/libc/src/mathvec/aarch64/expf.cpp
+++ b/libc/src/mathvec/aarch64/expf.cpp
@@ -20,35 +20,36 @@
namespace LIBC_NAMESPACE_DECL {
struct expf_data {
- float64x2_t shift, inv_ln2;
+ V2_SPLAT_TYPE(float64) shift, inv_ln2;
double ln2_hi, ln2_lo;
double c1, c3;
- float64x2_t c0, c2;
- float32x4_t range_val;
- uint32x4_t inf;
- uint64x2_t idx_mask;
+ V2_SPLAT_TYPE(float64) c0, c2;
+ V4_SPLAT_TYPE(float32) range_val;
+ V4_SPLAT_TYPE(uint32) inf;
+ V2_SPLAT_TYPE(uint64) idx_mask;
const uint64_t *mantissa;
};
static constexpr expf_data EXPF_DATA = {
- V2(0x1.800000000ffc0p+46), // shift
- V2(0x1.71547652b82fep+0), // inv_ln2
- 0x1.62e42fefa39efp-1, // ln2_hi
- 0x1.abc9e3b39803fp-56, // ln2_lo
- 0x1.55555555543c2p-3, // c1
- 0x1.111126b4eff73p-7, // c3
- V2(0x1.fffffffffdbcep-2), // c0
- V2(0x1.555573c64f2e3p-5), // c2
- V4(0x1p+9), // range_val
- V4(0x7f800000), // inf
- V2(0x3f), // idx_mask
- mathvec::EXP_MANTISSA, // mantissa
+ V2_SPLAT_INITIALIZER(0x1.800000000ffc0p+46), // shift
+ V2_SPLAT_INITIALIZER(0x1.71547652b82fep+0), // inv_ln2
+ 0x1.62e42fefa39efp-1, // ln2_hi
+ 0x1.abc9e3b39803fp-56, // ln2_lo
+ 0x1.55555555543c2p-3, // c1
+ 0x1.111126b4eff73p-7, // c3
+ V2_SPLAT_INITIALIZER(0x1.fffffffffdbcep-2), // c0
+ V2_SPLAT_INITIALIZER(0x1.555573c64f2e3p-5), // c2
+ V4_SPLAT_INITIALIZER(0x1p+9), // range_val
+ V4_SPLAT_INITIALIZER(0x7f800000), // inf
+ V2_SPLAT_INITIALIZER(0x3f), // idx_mask
+ mathvec::EXP_MANTISSA, // mantissa
};
LIBC_INLINE static float64x2_t exp_lookup(uint64x2_t u, const expf_data &data) {
// The low 6 bits of u index the 64 element mantissa table.
- uint64_t idx0 = vgetq_lane_u64(u & data.idx_mask, 0);
- uint64_t idx1 = vgetq_lane_u64(u & data.idx_mask, 1);
+ uint64x2_t vidx_mask = MAKE_SPLAT_VECTOR(data.idx_mask, u64);
+ uint64_t idx0 = vgetq_lane_u64(u & vidx_mask, 0);
+ uint64_t idx1 = vgetq_lane_u64(u & vidx_mask, 1);
uint64_t mant0 = data.mantissa[idx0];
uint64_t mant1 = data.mantissa[idx1];
@@ -67,8 +68,10 @@ LIBC_INLINE static float64x2_t exp_lookup(uint64x2_t u, const expf_data &data) {
LIBC_INLINE static float64x2_t inline_exp(float64x2_t x,
const expf_data &data) {
- float64x2_t z = vfmaq_f64(data.shift, x, data.inv_ln2);
- float64x2_t n = vsubq_f64(z, data.shift);
+ float64x2_t vshift = MAKE_SPLAT_VECTOR(data.shift, f64);
+ float64x2_t vinv_ln2 = MAKE_SPLAT_VECTOR(data.inv_ln2, f64);
+ float64x2_t z = vfmaq_f64(vshift, x, vinv_ln2);
+ float64x2_t n = vsubq_f64(z, vshift);
float64x2_t ln2 = vld1q_f64(&data.ln2_hi);
@@ -80,8 +83,10 @@ LIBC_INLINE static float64x2_t inline_exp(float64x2_t x,
// poly(r) = exp(r) - 1 ~= r + c0*r^2 + c1*r^3 + c2*r^4 + c3*r^5
float64x2_t r2 = r * r;
- float64x2_t p01 = vfmaq_laneq_f64(data.c0, r, coeffs, 0);
- float64x2_t p23 = vfmaq_laneq_f64(data.c2, r, coeffs, 1);
+ float64x2_t vc0 = MAKE_SPLAT_VECTOR(data.c0, f64);
+ float64x2_t vc2 = MAKE_SPLAT_VECTOR(data.c2, f64);
+ float64x2_t p01 = vfmaq_laneq_f64(vc0, r, coeffs, 0);
+ float64x2_t p23 = vfmaq_laneq_f64(vc2, r, coeffs, 1);
float64x2_t p04 = vfmaq_f64(p01, r2, p23);
float64x2_t y = vfmaq_f64(r, r2, p04);
@@ -107,11 +112,13 @@ LLVM_LIBC_FUNCTION(AdvSIMDFP32Vector, expf, (AdvSIMDFP32Vector x),
float32x4_t ret = vcombine_f32(vcvt_f32_f64(y_lo), vcvt_f32_f64(y_hi));
// Handle special cases for overflow and underflow.
- uint32x4_t special = vcagtq_f32(x, data.range_val);
+ float32x4_t vrange_val = MAKE_SPLAT_VECTOR(data.range_val, f32);
+ uint32x4_t special = vcagtq_f32(x, vrange_val);
bool has_special = vmaxvq_u32(special) != 0;
if (LIBC_UNLIKELY(has_special)) {
uint32x4_t is_inf = vcgtzq_f32(x);
- uint32x4_t inf_or_zero = vandq_u32(is_inf, data.inf);
+ uint32x4_t vinf = MAKE_SPLAT_VECTOR(data.inf, u32);
+ uint32x4_t inf_or_zero = vandq_u32(is_inf, vinf);
float32x4_t special_res = vreinterpretq_f32_u32(inf_or_zero);
// Combine the results for normal and special cases and return.
More information about the libc-commits
mailing list