aboutsummaryrefslogtreecommitdiff
path: root/math/test/ulp.c
diff options
context:
space:
mode:
Diffstat (limited to 'math/test/ulp.c')
-rw-r--r--math/test/ulp.c328
1 files changed, 192 insertions, 136 deletions
diff --git a/math/test/ulp.c b/math/test/ulp.c
index 5ff29972e50e..0a75fe264630 100644
--- a/math/test/ulp.c
+++ b/math/test/ulp.c
@@ -1,10 +1,23 @@
/*
* ULP error checking tool for math functions.
*
- * Copyright (c) 2019-2023, Arm Limited.
+ * Copyright (c) 2019-2024, Arm Limited.
* SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception
*/
+#if WANT_SVE_TESTS
+# if __aarch64__ && __linux__
+# ifdef __clang__
+# pragma clang attribute push(__attribute__((target("sve"))), \
+ apply_to = any(function))
+# else
+# pragma GCC target("+sve")
+# endif
+# else
+# error "SVE not supported - please disable WANT_SVE_TESTS"
+# endif
+#endif
+
#define _GNU_SOURCE
#include <ctype.h>
#include <fenv.h>
@@ -16,6 +29,8 @@
#include <string.h>
#include "mathlib.h"
+#include "trigpi_references.h"
+
/* Don't depend on mpfr by default. */
#ifndef USE_MPFR
# define USE_MPFR 0
@@ -24,50 +39,6 @@
# include <mpfr.h>
#endif
-static inline uint64_t
-asuint64 (double f)
-{
- union
- {
- double f;
- uint64_t i;
- } u = {f};
- return u.i;
-}
-
-static inline double
-asdouble (uint64_t i)
-{
- union
- {
- uint64_t i;
- double f;
- } u = {i};
- return u.f;
-}
-
-static inline uint32_t
-asuint (float f)
-{
- union
- {
- float f;
- uint32_t i;
- } u = {f};
- return u.i;
-}
-
-static inline float
-asfloat (uint32_t i)
-{
- union
- {
- uint32_t i;
- float f;
- } u = {i};
- return u.f;
-}
-
static uint64_t seed = 0x0123456789abcdef;
static uint64_t
rand64 (void)
@@ -198,68 +169,96 @@ next_d2 (void *g)
return (struct args_d2){asdouble (x), asdouble (x2)};
}
-struct conf
-{
- int r;
- int rc;
- int quiet;
- int mpfr;
- int fenv;
- unsigned long long n;
- double softlim;
- double errlim;
- int ignore_zero_sign;
-};
-
/* A bit of a hack: call vector functions twice with the same
input in lane 0 but a different value in other lanes: once
with an in-range value and then with a special case value. */
static int secondcall;
/* Wrappers for vector functions. */
-#ifdef __vpcs
-typedef __f32x4_t v_float;
-typedef __f64x2_t v_double;
+#if __aarch64__ && __linux__
/* First element of fv and dv may be changed by -c argument. */
static float fv[2] = {1.0f, -INFINITY};
static double dv[2] = {1.0, -INFINITY};
-static inline v_float argf(float x) { return (v_float){x,x,x,fv[secondcall]}; }
-static inline v_double argd(double x) { return (v_double){x,dv[secondcall]}; }
-#if WANT_SVE_MATH
+static inline float32x4_t
+argf (float x)
+{
+ return (float32x4_t){ x, x, x, fv[secondcall] };
+}
+static inline float64x2_t
+argd (double x)
+{
+ return (float64x2_t){ x, dv[secondcall] };
+}
+#if WANT_SVE_TESTS
#include <arm_sve.h>
-typedef __SVFloat32_t sv_float;
-typedef __SVFloat64_t sv_double;
-
-static inline sv_float svargf(float x) {
- int n = svcntw();
- float base[n];
- for (int i=0; i<n; i++)
- base[i] = (float)x;
- base[n-1] = (float) fv[secondcall];
- return svld1(svptrue_b32(), base);
-}
-static inline sv_double svargd(double x) {
- int n = svcntd();
- double base[n];
- for (int i=0; i<n; i++)
- base[i] = x;
- base[n-1] = dv[secondcall];
- return svld1(svptrue_b64(), base);
-}
-static inline float svretf(sv_float vec) {
- int n = svcntw();
- float res[n];
- svst1(svptrue_b32(), res, vec);
- return res[0];
-}
-static inline double svretd(sv_double vec) {
- int n = svcntd();
- double res[n];
- svst1(svptrue_b64(), res, vec);
- return res[0];
+
+static inline svfloat32_t
+svargf (float x)
+{
+ int n = svcntw ();
+ float base[n];
+ for (int i = 0; i < n; i++)
+ base[i] = (float) x;
+ base[n - 1] = (float) fv[secondcall];
+ return svld1 (svptrue_b32 (), base);
+}
+static inline svfloat64_t
+svargd (double x)
+{
+ int n = svcntd ();
+ double base[n];
+ for (int i = 0; i < n; i++)
+ base[i] = x;
+ base[n - 1] = dv[secondcall];
+ return svld1 (svptrue_b64 (), base);
+}
+static inline float
+svretf (svfloat32_t vec, svbool_t pg)
+{
+ return svlastb_f32 (svpfirst (pg, svpfalse ()), vec);
}
+static inline double
+svretd (svfloat64_t vec, svbool_t pg)
+{
+ return svlastb_f64 (svpfirst (pg, svpfalse ()), vec);
+}
+
+static inline svbool_t
+parse_pg (uint64_t p, int is_single)
+{
+ if (is_single)
+ {
+ uint32_t tmp[svcntw ()];
+ for (unsigned i = 0; i < svcntw (); i++)
+ tmp[i] = (p >> i) & 1;
+ return svcmpne (svptrue_b32 (), svld1 (svptrue_b32 (), tmp), 0);
+ }
+ else
+ {
+ uint64_t tmp[svcntd ()];
+ for (unsigned i = 0; i < svcntd (); i++)
+ tmp[i] = (p >> i) & 1;
+ return svcmpne (svptrue_b64 (), svld1 (svptrue_b64 (), tmp), 0);
+ }
+}
+# endif
#endif
+
+struct conf
+{
+ int r;
+ int rc;
+ int quiet;
+ int mpfr;
+ int fenv;
+ unsigned long long n;
+ double softlim;
+ double errlim;
+ int ignore_zero_sign;
+#if WANT_SVE_TESTS
+ svbool_t *pg;
#endif
+};
#include "test/ulp_wrappers.h"
@@ -269,12 +268,19 @@ struct fun
int arity;
int singleprec;
int twice;
+ int is_predicated;
union
{
float (*f1) (float);
float (*f2) (float, float);
double (*d1) (double);
double (*d2) (double, double);
+#if WANT_SVE_TESTS
+ float (*f1_pred) (svbool_t, float);
+ float (*f2_pred) (svbool_t, float, float);
+ double (*d1_pred) (svbool_t, double);
+ double (*d2_pred) (svbool_t, double, double);
+#endif
} fun;
union
{
@@ -294,44 +300,33 @@ struct fun
#endif
};
+// clang-format off
static const struct fun fun[] = {
#if USE_MPFR
-# define F(x, x_wrap, x_long, x_mpfr, a, s, t, twice) \
- {#x, a, s, twice, {.t = x_wrap}, {.t = x_long}, {.t = x_mpfr}},
+# define F(x, x_wrap, x_long, x_mpfr, a, s, t, twice) \
+ { #x, a, s, twice, 0, { .t = x_wrap }, { .t = x_long }, { .t = x_mpfr } },
+# define SVF(x, x_wrap, x_long, x_mpfr, a, s, t, twice) \
+ { #x, a, s, twice, 1, { .t##_pred = x_wrap }, { .t = x_long }, { .t = x_mpfr } },
#else
-# define F(x, x_wrap, x_long, x_mpfr, a, s, t, twice) \
- {#x, a, s, twice, {.t = x_wrap}, {.t = x_long}},
+# define F(x, x_wrap, x_long, x_mpfr, a, s, t, twice) \
+ { #x, a, s, twice, 0, { .t = x_wrap }, { .t = x_long } },
+# define SVF(x, x_wrap, x_long, x_mpfr, a, s, t, twice) \
+ { #x, a, s, twice, 1, { .t##_pred = x_wrap }, { .t = x_long } },
#endif
#define F1(x) F (x##f, x##f, x, mpfr_##x, 1, 1, f1, 0)
#define F2(x) F (x##f, x##f, x, mpfr_##x, 2, 1, f2, 0)
#define D1(x) F (x, x, x##l, mpfr_##x, 1, 0, d1, 0)
#define D2(x) F (x, x, x##l, mpfr_##x, 2, 0, d2, 0)
/* Neon routines. */
-#define VF1(x) F (__v_##x##f, v_##x##f, x, mpfr_##x, 1, 1, f1, 0)
-#define VF2(x) F (__v_##x##f, v_##x##f, x, mpfr_##x, 2, 1, f2, 0)
-#define VD1(x) F (__v_##x, v_##x, x##l, mpfr_##x, 1, 0, d1, 0)
-#define VD2(x) F (__v_##x, v_##x, x##l, mpfr_##x, 2, 0, d2, 0)
-#define VNF1(x) F (__vn_##x##f, vn_##x##f, x, mpfr_##x, 1, 1, f1, 0)
-#define VNF2(x) F (__vn_##x##f, vn_##x##f, x, mpfr_##x, 2, 1, f2, 0)
-#define VND1(x) F (__vn_##x, vn_##x, x##l, mpfr_##x, 1, 0, d1, 0)
-#define VND2(x) F (__vn_##x, vn_##x, x##l, mpfr_##x, 2, 0, d2, 0)
-#define ZVF1(x) F (_ZGVnN4v_##x##f, Z_##x##f, x, mpfr_##x, 1, 1, f1, 0)
-#define ZVF2(x) F (_ZGVnN4vv_##x##f, Z_##x##f, x, mpfr_##x, 2, 1, f2, 0)
-#define ZVD1(x) F (_ZGVnN2v_##x, Z_##x, x##l, mpfr_##x, 1, 0, d1, 0)
-#define ZVD2(x) F (_ZGVnN2vv_##x, Z_##x, x##l, mpfr_##x, 2, 0, d2, 0)
-#define ZVNF1(x) VNF1 (x) ZVF1 (x)
-#define ZVNF2(x) VNF2 (x) ZVF2 (x)
-#define ZVND1(x) VND1 (x) ZVD1 (x)
-#define ZVND2(x) VND2 (x) ZVD2 (x)
+#define ZVNF1(x) F (_ZGVnN4v_##x##f, Z_##x##f, x, mpfr_##x, 1, 1, f1, 0)
+#define ZVNF2(x) F (_ZGVnN4vv_##x##f, Z_##x##f, x, mpfr_##x, 2, 1, f2, 0)
+#define ZVND1(x) F (_ZGVnN2v_##x, Z_##x, x##l, mpfr_##x, 1, 0, d1, 0)
+#define ZVND2(x) F (_ZGVnN2vv_##x, Z_##x, x##l, mpfr_##x, 2, 0, d2, 0)
/* SVE routines. */
-#define SVF1(x) F (__sv_##x##f, sv_##x##f, x, mpfr_##x, 1, 1, f1, 0)
-#define SVF2(x) F (__sv_##x##f, sv_##x##f, x, mpfr_##x, 2, 1, f2, 0)
-#define SVD1(x) F (__sv_##x, sv_##x, x##l, mpfr_##x, 1, 0, d1, 0)
-#define SVD2(x) F (__sv_##x, sv_##x, x##l, mpfr_##x, 2, 0, d2, 0)
-#define ZSVF1(x) F (_ZGVsMxv_##x##f, Z_sv_##x##f, x, mpfr_##x, 1, 1, f1, 0)
-#define ZSVF2(x) F (_ZGVsMxvv_##x##f, Z_sv_##x##f, x, mpfr_##x, 2, 1, f2, 0)
-#define ZSVD1(x) F (_ZGVsMxv_##x, Z_sv_##x, x##l, mpfr_##x, 1, 0, d1, 0)
-#define ZSVD2(x) F (_ZGVsMxvv_##x, Z_sv_##x, x##l, mpfr_##x, 2, 0, d2, 0)
+#define ZSVF1(x) SVF (_ZGVsMxv_##x##f, Z_sv_##x##f, x, mpfr_##x, 1, 1, f1, 0)
+#define ZSVF2(x) SVF (_ZGVsMxvv_##x##f, Z_sv_##x##f, x, mpfr_##x, 2, 1, f2, 0)
+#define ZSVD1(x) SVF (_ZGVsMxv_##x, Z_sv_##x, x##l, mpfr_##x, 1, 0, d1, 0)
+#define ZSVD2(x) SVF (_ZGVsMxvv_##x, Z_sv_##x, x##l, mpfr_##x, 2, 0, d2, 0)
#include "test/ulp_funcs.h"
@@ -340,11 +335,13 @@ static const struct fun fun[] = {
#undef F2
#undef D1
#undef D2
-#undef SVF1
-#undef SVF2
-#undef SVD1
-#undef SVD2
- {0}};
+#undef ZSVF1
+#undef ZSVF2
+#undef ZSVD1
+#undef ZSVD2
+ { 0 }
+};
+// clang-format on
/* Boilerplate for generic calls. */
@@ -365,24 +362,40 @@ ulpscale_d (double x)
return e - 0x3ff - 52;
}
static inline float
-call_f1 (const struct fun *f, struct args_f1 a)
+call_f1 (const struct fun *f, struct args_f1 a, const struct conf *conf)
{
+#if WANT_SVE_TESTS
+ if (f->is_predicated)
+ return f->fun.f1_pred (*conf->pg, a.x);
+#endif
return f->fun.f1 (a.x);
}
static inline float
-call_f2 (const struct fun *f, struct args_f2 a)
+call_f2 (const struct fun *f, struct args_f2 a, const struct conf *conf)
{
+#if WANT_SVE_TESTS
+ if (f->is_predicated)
+ return f->fun.f2_pred (*conf->pg, a.x, a.x2);
+#endif
return f->fun.f2 (a.x, a.x2);
}
static inline double
-call_d1 (const struct fun *f, struct args_d1 a)
+call_d1 (const struct fun *f, struct args_d1 a, const struct conf *conf)
{
+#if WANT_SVE_TESTS
+ if (f->is_predicated)
+ return f->fun.d1_pred (*conf->pg, a.x);
+#endif
return f->fun.d1 (a.x);
}
static inline double
-call_d2 (const struct fun *f, struct args_d2 a)
+call_d2 (const struct fun *f, struct args_d2 a, const struct conf *conf)
{
+#if WANT_SVE_TESTS
+ if (f->is_predicated)
+ return f->fun.d2_pred (*conf->pg, a.x, a.x2);
+#endif
return f->fun.d2 (a.x, a.x2);
}
static inline double
@@ -595,6 +608,11 @@ usage (void)
" This should be different from tested input in other lanes, and non-special \n"
" (i.e. should not trigger fenv exceptions). Default is 1.");
#endif
+#if WANT_SVE_TESTS
+ puts ("-p: integer input for controlling predicate passed to SVE function. "
+ "If bit N is set, lane N is activated (bits past the vector length "
+ "are ignored). Default is UINT64_MAX (ptrue).");
+#endif
puts ("-z: ignore sign of 0.");
puts ("Supported func:");
for (const struct fun *f = fun; f->name; f++)
@@ -633,9 +651,21 @@ getnum (const char *s, int singleprec)
sign = singleprec ? 1ULL << 31 : 1ULL << 63;
s++;
}
+
+ /* Sentinel value for failed parse. */
+ char *should_not_be_s = NULL;
+
/* 0xXXXX is treated as bit representation, '-' flips the sign bit. */
if (s[0] == '0' && tolower (s[1]) == 'x' && strchr (s, 'p') == 0)
- return sign ^ strtoull (s, 0, 0);
+ {
+ uint64_t out = sign ^ strtoull (s, &should_not_be_s, 0);
+ if (should_not_be_s == s)
+ {
+ printf ("ERROR: Could not parse '%s'\n", s);
+ exit (1);
+ }
+ return out;
+ }
// /* SNaN, QNaN, NaN, Inf. */
// for (i=0; s[i] && i < sizeof buf; i++)
// buf[i] = tolower(s[i]);
@@ -647,8 +677,16 @@ getnum (const char *s, int singleprec)
// if (strcmp(buf, "inf") == 0 || strcmp(buf, "infinity") == 0)
// return sign | (singleprec ? 0x7f800000 : 0x7ff0000000000000);
/* Otherwise assume it's a floating-point literal. */
- return sign
- | (singleprec ? asuint (strtof (s, 0)) : asuint64 (strtod (s, 0)));
+ uint64_t out = sign
+ | (singleprec ? asuint (strtof (s, &should_not_be_s))
+ : asuint64 (strtod (s, &should_not_be_s)));
+ if (should_not_be_s == s)
+ {
+ printf ("ERROR: Could not parse '%s'\n", s);
+ exit (1);
+ }
+
+ return out;
}
static void
@@ -720,6 +758,9 @@ main (int argc, char *argv[])
conf.softlim = 0;
conf.errlim = INFINITY;
conf.ignore_zero_sign = 0;
+#if WANT_SVE_TESTS
+ uint64_t pg_int = UINT64_MAX;
+#endif
for (;;)
{
argc--;
@@ -767,7 +808,7 @@ main (int argc, char *argv[])
case 'z':
conf.ignore_zero_sign = 1;
break;
-#ifdef __vpcs
+#if __aarch64__ && __linux__
case 'c':
argc--;
argv++;
@@ -775,6 +816,13 @@ main (int argc, char *argv[])
dv[0] = strtod(argv[0], 0);
break;
#endif
+#if WANT_SVE_TESTS
+ case 'p':
+ argc--;
+ argv++;
+ pg_int = strtoull (argv[0], 0, 0);
+ break;
+#endif
default:
usage ();
}
@@ -806,7 +854,7 @@ main (int argc, char *argv[])
if (strncmp (argv[0], "_ZGVnN", 6) == 0)
exit (0);
#endif
-#if !WANT_SVE_MATH
+#if !WANT_SVE_TESTS
if (strncmp (argv[0], "_ZGVsMxv", 8) == 0)
exit (0);
#endif
@@ -824,5 +872,13 @@ main (int argc, char *argv[])
argv++;
parsegen (&gen, argc, argv, f);
conf.n = gen.cnt;
+#if WANT_SVE_TESTS
+ svbool_t pg = parse_pg (pg_int, f->singleprec);
+ conf.pg = &pg;
+#endif
return cmp (f, &gen, &conf);
}
+
+#if __aarch64__ && __linux__ && WANT_SVE_TESTS && defined(__clang__)
+# pragma clang attribute pop
+#endif