xref: /qemu/tests/fp/fp-bench.c (revision f2b84b9edb0788eb25902c4ca268476b42fceb20)
125f539f3SEmilio G. Cota /*
225f539f3SEmilio G. Cota  * fp-bench.c - A collection of simple floating point microbenchmarks.
325f539f3SEmilio G. Cota  *
425f539f3SEmilio G. Cota  * Copyright (C) 2018, Emilio G. Cota <cota@braap.org>
525f539f3SEmilio G. Cota  *
625f539f3SEmilio G. Cota  * License: GNU GPL, version 2 or later.
725f539f3SEmilio G. Cota  *   See the COPYING file in the top-level directory.
825f539f3SEmilio G. Cota  */
925f539f3SEmilio G. Cota #ifndef HW_POISON_H
1025f539f3SEmilio G. Cota #error Must define HW_POISON_H to work around TARGET_* poisoning
1125f539f3SEmilio G. Cota #endif
1225f539f3SEmilio G. Cota 
1325f539f3SEmilio G. Cota #include "qemu/osdep.h"
1425f539f3SEmilio G. Cota #include <math.h>
1525f539f3SEmilio G. Cota #include <fenv.h>
1625f539f3SEmilio G. Cota #include "qemu/timer.h"
17*f2b84b9eSAlex Bennée #include "qemu/int128.h"
1825f539f3SEmilio G. Cota #include "fpu/softfloat.h"
1925f539f3SEmilio G. Cota 
2025f539f3SEmilio G. Cota /* amortize the computation of random inputs */
2125f539f3SEmilio G. Cota #define OPS_PER_ITER     50000
2225f539f3SEmilio G. Cota 
2325f539f3SEmilio G. Cota #define MAX_OPERANDS 3
2425f539f3SEmilio G. Cota 
2525f539f3SEmilio G. Cota #define SEED_A 0xdeadfacedeadface
2625f539f3SEmilio G. Cota #define SEED_B 0xbadc0feebadc0fee
2725f539f3SEmilio G. Cota #define SEED_C 0xbeefdeadbeefdead
2825f539f3SEmilio G. Cota 
2925f539f3SEmilio G. Cota enum op {
3025f539f3SEmilio G. Cota     OP_ADD,
3125f539f3SEmilio G. Cota     OP_SUB,
3225f539f3SEmilio G. Cota     OP_MUL,
3325f539f3SEmilio G. Cota     OP_DIV,
3425f539f3SEmilio G. Cota     OP_FMA,
3525f539f3SEmilio G. Cota     OP_SQRT,
3625f539f3SEmilio G. Cota     OP_CMP,
3725f539f3SEmilio G. Cota     OP_MAX_NR,
3825f539f3SEmilio G. Cota };
3925f539f3SEmilio G. Cota 
4025f539f3SEmilio G. Cota static const char * const op_names[] = {
4125f539f3SEmilio G. Cota     [OP_ADD] = "add",
4225f539f3SEmilio G. Cota     [OP_SUB] = "sub",
4325f539f3SEmilio G. Cota     [OP_MUL] = "mul",
4425f539f3SEmilio G. Cota     [OP_DIV] = "div",
4525f539f3SEmilio G. Cota     [OP_FMA] = "mulAdd",
4625f539f3SEmilio G. Cota     [OP_SQRT] = "sqrt",
4725f539f3SEmilio G. Cota     [OP_CMP] = "cmp",
4825f539f3SEmilio G. Cota     [OP_MAX_NR] = NULL,
4925f539f3SEmilio G. Cota };
5025f539f3SEmilio G. Cota 
5125f539f3SEmilio G. Cota enum precision {
5225f539f3SEmilio G. Cota     PREC_SINGLE,
5325f539f3SEmilio G. Cota     PREC_DOUBLE,
54*f2b84b9eSAlex Bennée     PREC_QUAD,
5525f539f3SEmilio G. Cota     PREC_FLOAT32,
5625f539f3SEmilio G. Cota     PREC_FLOAT64,
57*f2b84b9eSAlex Bennée     PREC_FLOAT128,
5825f539f3SEmilio G. Cota     PREC_MAX_NR,
5925f539f3SEmilio G. Cota };
6025f539f3SEmilio G. Cota 
6125f539f3SEmilio G. Cota enum rounding {
6225f539f3SEmilio G. Cota     ROUND_EVEN,
6325f539f3SEmilio G. Cota     ROUND_ZERO,
6425f539f3SEmilio G. Cota     ROUND_DOWN,
6525f539f3SEmilio G. Cota     ROUND_UP,
6625f539f3SEmilio G. Cota     ROUND_TIEAWAY,
6725f539f3SEmilio G. Cota     N_ROUND_MODES,
6825f539f3SEmilio G. Cota };
6925f539f3SEmilio G. Cota 
7025f539f3SEmilio G. Cota static const char * const round_names[] = {
7125f539f3SEmilio G. Cota     [ROUND_EVEN] = "even",
7225f539f3SEmilio G. Cota     [ROUND_ZERO] = "zero",
7325f539f3SEmilio G. Cota     [ROUND_DOWN] = "down",
7425f539f3SEmilio G. Cota     [ROUND_UP] = "up",
7525f539f3SEmilio G. Cota     [ROUND_TIEAWAY] = "tieaway",
7625f539f3SEmilio G. Cota };
7725f539f3SEmilio G. Cota 
7825f539f3SEmilio G. Cota enum tester {
7925f539f3SEmilio G. Cota     TESTER_SOFT,
8025f539f3SEmilio G. Cota     TESTER_HOST,
8125f539f3SEmilio G. Cota     TESTER_MAX_NR,
8225f539f3SEmilio G. Cota };
8325f539f3SEmilio G. Cota 
8425f539f3SEmilio G. Cota static const char * const tester_names[] = {
8525f539f3SEmilio G. Cota     [TESTER_SOFT] = "soft",
8625f539f3SEmilio G. Cota     [TESTER_HOST] = "host",
8725f539f3SEmilio G. Cota     [TESTER_MAX_NR] = NULL,
8825f539f3SEmilio G. Cota };
8925f539f3SEmilio G. Cota 
9025f539f3SEmilio G. Cota union fp {
9125f539f3SEmilio G. Cota     float f;
9225f539f3SEmilio G. Cota     double d;
9325f539f3SEmilio G. Cota     float32 f32;
9425f539f3SEmilio G. Cota     float64 f64;
95*f2b84b9eSAlex Bennée     float128 f128;
9625f539f3SEmilio G. Cota     uint64_t u64;
9725f539f3SEmilio G. Cota };
9825f539f3SEmilio G. Cota 
9925f539f3SEmilio G. Cota struct op_state;
10025f539f3SEmilio G. Cota 
10125f539f3SEmilio G. Cota typedef float (*float_func_t)(const struct op_state *s);
10225f539f3SEmilio G. Cota typedef double (*double_func_t)(const struct op_state *s);
10325f539f3SEmilio G. Cota 
10425f539f3SEmilio G. Cota union fp_func {
10525f539f3SEmilio G. Cota     float_func_t float_func;
10625f539f3SEmilio G. Cota     double_func_t double_func;
10725f539f3SEmilio G. Cota };
10825f539f3SEmilio G. Cota 
10925f539f3SEmilio G. Cota typedef void (*bench_func_t)(void);
11025f539f3SEmilio G. Cota 
11125f539f3SEmilio G. Cota struct op_desc {
11225f539f3SEmilio G. Cota     const char * const name;
11325f539f3SEmilio G. Cota };
11425f539f3SEmilio G. Cota 
11525f539f3SEmilio G. Cota #define DEFAULT_DURATION_SECS 1
11625f539f3SEmilio G. Cota 
11725f539f3SEmilio G. Cota static uint64_t random_ops[MAX_OPERANDS] = {
11825f539f3SEmilio G. Cota     SEED_A, SEED_B, SEED_C,
11925f539f3SEmilio G. Cota };
120*f2b84b9eSAlex Bennée 
121*f2b84b9eSAlex Bennée static float128 random_quad_ops[MAX_OPERANDS] = {
122*f2b84b9eSAlex Bennée     {SEED_A, SEED_B}, {SEED_B, SEED_C}, {SEED_C, SEED_A},
123*f2b84b9eSAlex Bennée };
12425f539f3SEmilio G. Cota static float_status soft_status;
12525f539f3SEmilio G. Cota static enum precision precision;
12625f539f3SEmilio G. Cota static enum op operation;
12725f539f3SEmilio G. Cota static enum tester tester;
12825f539f3SEmilio G. Cota static uint64_t n_completed_ops;
12925f539f3SEmilio G. Cota static unsigned int duration = DEFAULT_DURATION_SECS;
13025f539f3SEmilio G. Cota static int64_t ns_elapsed;
13125f539f3SEmilio G. Cota /* disable optimizations with volatile */
13225f539f3SEmilio G. Cota static volatile union fp res;
13325f539f3SEmilio G. Cota 
13425f539f3SEmilio G. Cota /*
13525f539f3SEmilio G. Cota  * From: https://en.wikipedia.org/wiki/Xorshift
13625f539f3SEmilio G. Cota  * This is faster than rand_r(), and gives us a wider range (RAND_MAX is only
13725f539f3SEmilio G. Cota  * guaranteed to be >= INT_MAX).
13825f539f3SEmilio G. Cota  */
13925f539f3SEmilio G. Cota static uint64_t xorshift64star(uint64_t x)
14025f539f3SEmilio G. Cota {
14125f539f3SEmilio G. Cota     x ^= x >> 12; /* a */
14225f539f3SEmilio G. Cota     x ^= x << 25; /* b */
14325f539f3SEmilio G. Cota     x ^= x >> 27; /* c */
14425f539f3SEmilio G. Cota     return x * UINT64_C(2685821657736338717);
14525f539f3SEmilio G. Cota }
14625f539f3SEmilio G. Cota 
14725f539f3SEmilio G. Cota static void update_random_ops(int n_ops, enum precision prec)
14825f539f3SEmilio G. Cota {
14925f539f3SEmilio G. Cota     int i;
15025f539f3SEmilio G. Cota 
15125f539f3SEmilio G. Cota     for (i = 0; i < n_ops; i++) {
15225f539f3SEmilio G. Cota 
153446cfb0dSEmilio G. Cota         switch (prec) {
154446cfb0dSEmilio G. Cota         case PREC_SINGLE:
155446cfb0dSEmilio G. Cota         case PREC_FLOAT32:
156*f2b84b9eSAlex Bennée         {
157*f2b84b9eSAlex Bennée             uint64_t r = random_ops[i];
15825f539f3SEmilio G. Cota             do {
15925f539f3SEmilio G. Cota                 r = xorshift64star(r);
16025f539f3SEmilio G. Cota             } while (!float32_is_normal(r));
161*f2b84b9eSAlex Bennée             random_ops[i] = r;
162446cfb0dSEmilio G. Cota             break;
163*f2b84b9eSAlex Bennée         }
164446cfb0dSEmilio G. Cota         case PREC_DOUBLE:
165446cfb0dSEmilio G. Cota         case PREC_FLOAT64:
166*f2b84b9eSAlex Bennée         {
167*f2b84b9eSAlex Bennée             uint64_t r = random_ops[i];
16825f539f3SEmilio G. Cota             do {
16925f539f3SEmilio G. Cota                 r = xorshift64star(r);
17025f539f3SEmilio G. Cota             } while (!float64_is_normal(r));
171*f2b84b9eSAlex Bennée             random_ops[i] = r;
172446cfb0dSEmilio G. Cota             break;
173*f2b84b9eSAlex Bennée         }
174*f2b84b9eSAlex Bennée         case PREC_QUAD:
175*f2b84b9eSAlex Bennée         case PREC_FLOAT128:
176*f2b84b9eSAlex Bennée         {
177*f2b84b9eSAlex Bennée             float128 r = random_quad_ops[i];
178*f2b84b9eSAlex Bennée             uint64_t hi = r.high;
179*f2b84b9eSAlex Bennée             uint64_t lo = r.low;
180*f2b84b9eSAlex Bennée             do {
181*f2b84b9eSAlex Bennée                 hi = xorshift64star(hi);
182*f2b84b9eSAlex Bennée                 lo = xorshift64star(lo);
183*f2b84b9eSAlex Bennée                 r = make_float128(hi, lo);
184*f2b84b9eSAlex Bennée             } while (!float128_is_normal(r));
185*f2b84b9eSAlex Bennée             random_quad_ops[i] = r;
186*f2b84b9eSAlex Bennée             break;
187*f2b84b9eSAlex Bennée         }
188446cfb0dSEmilio G. Cota         default:
18925f539f3SEmilio G. Cota             g_assert_not_reached();
19025f539f3SEmilio G. Cota         }
19125f539f3SEmilio G. Cota     }
19225f539f3SEmilio G. Cota }
19325f539f3SEmilio G. Cota 
19425f539f3SEmilio G. Cota static void fill_random(union fp *ops, int n_ops, enum precision prec,
19525f539f3SEmilio G. Cota                         bool no_neg)
19625f539f3SEmilio G. Cota {
19725f539f3SEmilio G. Cota     int i;
19825f539f3SEmilio G. Cota 
19925f539f3SEmilio G. Cota     for (i = 0; i < n_ops; i++) {
20025f539f3SEmilio G. Cota         switch (prec) {
20125f539f3SEmilio G. Cota         case PREC_SINGLE:
20225f539f3SEmilio G. Cota         case PREC_FLOAT32:
20325f539f3SEmilio G. Cota             ops[i].f32 = make_float32(random_ops[i]);
20425f539f3SEmilio G. Cota             if (no_neg && float32_is_neg(ops[i].f32)) {
20525f539f3SEmilio G. Cota                 ops[i].f32 = float32_chs(ops[i].f32);
20625f539f3SEmilio G. Cota             }
20725f539f3SEmilio G. Cota             break;
20825f539f3SEmilio G. Cota         case PREC_DOUBLE:
20925f539f3SEmilio G. Cota         case PREC_FLOAT64:
21025f539f3SEmilio G. Cota             ops[i].f64 = make_float64(random_ops[i]);
21125f539f3SEmilio G. Cota             if (no_neg && float64_is_neg(ops[i].f64)) {
21225f539f3SEmilio G. Cota                 ops[i].f64 = float64_chs(ops[i].f64);
21325f539f3SEmilio G. Cota             }
21425f539f3SEmilio G. Cota             break;
215*f2b84b9eSAlex Bennée         case PREC_QUAD:
216*f2b84b9eSAlex Bennée         case PREC_FLOAT128:
217*f2b84b9eSAlex Bennée             ops[i].f128 = random_quad_ops[i];
218*f2b84b9eSAlex Bennée             if (no_neg && float128_is_neg(ops[i].f128)) {
219*f2b84b9eSAlex Bennée                 ops[i].f128 = float128_chs(ops[i].f128);
220*f2b84b9eSAlex Bennée             }
221*f2b84b9eSAlex Bennée             break;
22225f539f3SEmilio G. Cota         default:
22325f539f3SEmilio G. Cota             g_assert_not_reached();
22425f539f3SEmilio G. Cota         }
22525f539f3SEmilio G. Cota     }
22625f539f3SEmilio G. Cota }
22725f539f3SEmilio G. Cota 
22825f539f3SEmilio G. Cota /*
22925f539f3SEmilio G. Cota  * The main benchmark function. Instead of (ab)using macros, we rely
23025f539f3SEmilio G. Cota  * on the compiler to unfold this at compile-time.
23125f539f3SEmilio G. Cota  */
23225f539f3SEmilio G. Cota static void bench(enum precision prec, enum op op, int n_ops, bool no_neg)
23325f539f3SEmilio G. Cota {
23425f539f3SEmilio G. Cota     int64_t tf = get_clock() + duration * 1000000000LL;
23525f539f3SEmilio G. Cota 
23625f539f3SEmilio G. Cota     while (get_clock() < tf) {
23725f539f3SEmilio G. Cota         union fp ops[MAX_OPERANDS];
23825f539f3SEmilio G. Cota         int64_t t0;
23925f539f3SEmilio G. Cota         int i;
24025f539f3SEmilio G. Cota 
24125f539f3SEmilio G. Cota         update_random_ops(n_ops, prec);
24225f539f3SEmilio G. Cota         switch (prec) {
24325f539f3SEmilio G. Cota         case PREC_SINGLE:
24425f539f3SEmilio G. Cota             fill_random(ops, n_ops, prec, no_neg);
24525f539f3SEmilio G. Cota             t0 = get_clock();
24625f539f3SEmilio G. Cota             for (i = 0; i < OPS_PER_ITER; i++) {
24725f539f3SEmilio G. Cota                 float a = ops[0].f;
24825f539f3SEmilio G. Cota                 float b = ops[1].f;
24925f539f3SEmilio G. Cota                 float c = ops[2].f;
25025f539f3SEmilio G. Cota 
25125f539f3SEmilio G. Cota                 switch (op) {
25225f539f3SEmilio G. Cota                 case OP_ADD:
25325f539f3SEmilio G. Cota                     res.f = a + b;
25425f539f3SEmilio G. Cota                     break;
25525f539f3SEmilio G. Cota                 case OP_SUB:
25625f539f3SEmilio G. Cota                     res.f = a - b;
25725f539f3SEmilio G. Cota                     break;
25825f539f3SEmilio G. Cota                 case OP_MUL:
25925f539f3SEmilio G. Cota                     res.f = a * b;
26025f539f3SEmilio G. Cota                     break;
26125f539f3SEmilio G. Cota                 case OP_DIV:
26225f539f3SEmilio G. Cota                     res.f = a / b;
26325f539f3SEmilio G. Cota                     break;
26425f539f3SEmilio G. Cota                 case OP_FMA:
26525f539f3SEmilio G. Cota                     res.f = fmaf(a, b, c);
26625f539f3SEmilio G. Cota                     break;
26725f539f3SEmilio G. Cota                 case OP_SQRT:
26825f539f3SEmilio G. Cota                     res.f = sqrtf(a);
26925f539f3SEmilio G. Cota                     break;
27025f539f3SEmilio G. Cota                 case OP_CMP:
27125f539f3SEmilio G. Cota                     res.u64 = isgreater(a, b);
27225f539f3SEmilio G. Cota                     break;
27325f539f3SEmilio G. Cota                 default:
27425f539f3SEmilio G. Cota                     g_assert_not_reached();
27525f539f3SEmilio G. Cota                 }
27625f539f3SEmilio G. Cota             }
27725f539f3SEmilio G. Cota             break;
27825f539f3SEmilio G. Cota         case PREC_DOUBLE:
27925f539f3SEmilio G. Cota             fill_random(ops, n_ops, prec, no_neg);
28025f539f3SEmilio G. Cota             t0 = get_clock();
28125f539f3SEmilio G. Cota             for (i = 0; i < OPS_PER_ITER; i++) {
28225f539f3SEmilio G. Cota                 double a = ops[0].d;
28325f539f3SEmilio G. Cota                 double b = ops[1].d;
28425f539f3SEmilio G. Cota                 double c = ops[2].d;
28525f539f3SEmilio G. Cota 
28625f539f3SEmilio G. Cota                 switch (op) {
28725f539f3SEmilio G. Cota                 case OP_ADD:
28825f539f3SEmilio G. Cota                     res.d = a + b;
28925f539f3SEmilio G. Cota                     break;
29025f539f3SEmilio G. Cota                 case OP_SUB:
29125f539f3SEmilio G. Cota                     res.d = a - b;
29225f539f3SEmilio G. Cota                     break;
29325f539f3SEmilio G. Cota                 case OP_MUL:
29425f539f3SEmilio G. Cota                     res.d = a * b;
29525f539f3SEmilio G. Cota                     break;
29625f539f3SEmilio G. Cota                 case OP_DIV:
29725f539f3SEmilio G. Cota                     res.d = a / b;
29825f539f3SEmilio G. Cota                     break;
29925f539f3SEmilio G. Cota                 case OP_FMA:
30025f539f3SEmilio G. Cota                     res.d = fma(a, b, c);
30125f539f3SEmilio G. Cota                     break;
30225f539f3SEmilio G. Cota                 case OP_SQRT:
30325f539f3SEmilio G. Cota                     res.d = sqrt(a);
30425f539f3SEmilio G. Cota                     break;
30525f539f3SEmilio G. Cota                 case OP_CMP:
30625f539f3SEmilio G. Cota                     res.u64 = isgreater(a, b);
30725f539f3SEmilio G. Cota                     break;
30825f539f3SEmilio G. Cota                 default:
30925f539f3SEmilio G. Cota                     g_assert_not_reached();
31025f539f3SEmilio G. Cota                 }
31125f539f3SEmilio G. Cota             }
31225f539f3SEmilio G. Cota             break;
31325f539f3SEmilio G. Cota         case PREC_FLOAT32:
31425f539f3SEmilio G. Cota             fill_random(ops, n_ops, prec, no_neg);
31525f539f3SEmilio G. Cota             t0 = get_clock();
31625f539f3SEmilio G. Cota             for (i = 0; i < OPS_PER_ITER; i++) {
31725f539f3SEmilio G. Cota                 float32 a = ops[0].f32;
31825f539f3SEmilio G. Cota                 float32 b = ops[1].f32;
31925f539f3SEmilio G. Cota                 float32 c = ops[2].f32;
32025f539f3SEmilio G. Cota 
32125f539f3SEmilio G. Cota                 switch (op) {
32225f539f3SEmilio G. Cota                 case OP_ADD:
32325f539f3SEmilio G. Cota                     res.f32 = float32_add(a, b, &soft_status);
32425f539f3SEmilio G. Cota                     break;
32525f539f3SEmilio G. Cota                 case OP_SUB:
32625f539f3SEmilio G. Cota                     res.f32 = float32_sub(a, b, &soft_status);
32725f539f3SEmilio G. Cota                     break;
32825f539f3SEmilio G. Cota                 case OP_MUL:
32925f539f3SEmilio G. Cota                     res.f = float32_mul(a, b, &soft_status);
33025f539f3SEmilio G. Cota                     break;
33125f539f3SEmilio G. Cota                 case OP_DIV:
33225f539f3SEmilio G. Cota                     res.f32 = float32_div(a, b, &soft_status);
33325f539f3SEmilio G. Cota                     break;
33425f539f3SEmilio G. Cota                 case OP_FMA:
33525f539f3SEmilio G. Cota                     res.f32 = float32_muladd(a, b, c, 0, &soft_status);
33625f539f3SEmilio G. Cota                     break;
33725f539f3SEmilio G. Cota                 case OP_SQRT:
33825f539f3SEmilio G. Cota                     res.f32 = float32_sqrt(a, &soft_status);
33925f539f3SEmilio G. Cota                     break;
34025f539f3SEmilio G. Cota                 case OP_CMP:
34125f539f3SEmilio G. Cota                     res.u64 = float32_compare_quiet(a, b, &soft_status);
34225f539f3SEmilio G. Cota                     break;
34325f539f3SEmilio G. Cota                 default:
34425f539f3SEmilio G. Cota                     g_assert_not_reached();
34525f539f3SEmilio G. Cota                 }
34625f539f3SEmilio G. Cota             }
34725f539f3SEmilio G. Cota             break;
34825f539f3SEmilio G. Cota         case PREC_FLOAT64:
34925f539f3SEmilio G. Cota             fill_random(ops, n_ops, prec, no_neg);
35025f539f3SEmilio G. Cota             t0 = get_clock();
35125f539f3SEmilio G. Cota             for (i = 0; i < OPS_PER_ITER; i++) {
35225f539f3SEmilio G. Cota                 float64 a = ops[0].f64;
35325f539f3SEmilio G. Cota                 float64 b = ops[1].f64;
35425f539f3SEmilio G. Cota                 float64 c = ops[2].f64;
35525f539f3SEmilio G. Cota 
35625f539f3SEmilio G. Cota                 switch (op) {
35725f539f3SEmilio G. Cota                 case OP_ADD:
35825f539f3SEmilio G. Cota                     res.f64 = float64_add(a, b, &soft_status);
35925f539f3SEmilio G. Cota                     break;
36025f539f3SEmilio G. Cota                 case OP_SUB:
36125f539f3SEmilio G. Cota                     res.f64 = float64_sub(a, b, &soft_status);
36225f539f3SEmilio G. Cota                     break;
36325f539f3SEmilio G. Cota                 case OP_MUL:
36425f539f3SEmilio G. Cota                     res.f = float64_mul(a, b, &soft_status);
36525f539f3SEmilio G. Cota                     break;
36625f539f3SEmilio G. Cota                 case OP_DIV:
36725f539f3SEmilio G. Cota                     res.f64 = float64_div(a, b, &soft_status);
36825f539f3SEmilio G. Cota                     break;
36925f539f3SEmilio G. Cota                 case OP_FMA:
37025f539f3SEmilio G. Cota                     res.f64 = float64_muladd(a, b, c, 0, &soft_status);
37125f539f3SEmilio G. Cota                     break;
37225f539f3SEmilio G. Cota                 case OP_SQRT:
37325f539f3SEmilio G. Cota                     res.f64 = float64_sqrt(a, &soft_status);
37425f539f3SEmilio G. Cota                     break;
37525f539f3SEmilio G. Cota                 case OP_CMP:
37625f539f3SEmilio G. Cota                     res.u64 = float64_compare_quiet(a, b, &soft_status);
37725f539f3SEmilio G. Cota                     break;
37825f539f3SEmilio G. Cota                 default:
37925f539f3SEmilio G. Cota                     g_assert_not_reached();
38025f539f3SEmilio G. Cota                 }
38125f539f3SEmilio G. Cota             }
38225f539f3SEmilio G. Cota             break;
383*f2b84b9eSAlex Bennée         case PREC_FLOAT128:
384*f2b84b9eSAlex Bennée             fill_random(ops, n_ops, prec, no_neg);
385*f2b84b9eSAlex Bennée             t0 = get_clock();
386*f2b84b9eSAlex Bennée             for (i = 0; i < OPS_PER_ITER; i++) {
387*f2b84b9eSAlex Bennée                 float128 a = ops[0].f128;
388*f2b84b9eSAlex Bennée                 float128 b = ops[1].f128;
389*f2b84b9eSAlex Bennée                 /* float128 c = ops[2].f128; */
390*f2b84b9eSAlex Bennée 
391*f2b84b9eSAlex Bennée                 switch (op) {
392*f2b84b9eSAlex Bennée                 case OP_ADD:
393*f2b84b9eSAlex Bennée                     res.f128 = float128_add(a, b, &soft_status);
394*f2b84b9eSAlex Bennée                     break;
395*f2b84b9eSAlex Bennée                 case OP_SUB:
396*f2b84b9eSAlex Bennée                     res.f128 = float128_sub(a, b, &soft_status);
397*f2b84b9eSAlex Bennée                     break;
398*f2b84b9eSAlex Bennée                 case OP_MUL:
399*f2b84b9eSAlex Bennée                     res.f128 = float128_mul(a, b, &soft_status);
400*f2b84b9eSAlex Bennée                     break;
401*f2b84b9eSAlex Bennée                 case OP_DIV:
402*f2b84b9eSAlex Bennée                     res.f128 = float128_div(a, b, &soft_status);
403*f2b84b9eSAlex Bennée                     break;
404*f2b84b9eSAlex Bennée                 /* case OP_FMA: */
405*f2b84b9eSAlex Bennée                 /*     res.f128 = float128_muladd(a, b, c, 0, &soft_status); */
406*f2b84b9eSAlex Bennée                 /*     break; */
407*f2b84b9eSAlex Bennée                 case OP_SQRT:
408*f2b84b9eSAlex Bennée                     res.f128 = float128_sqrt(a, &soft_status);
409*f2b84b9eSAlex Bennée                     break;
410*f2b84b9eSAlex Bennée                 case OP_CMP:
411*f2b84b9eSAlex Bennée                     res.u64 = float128_compare_quiet(a, b, &soft_status);
412*f2b84b9eSAlex Bennée                     break;
413*f2b84b9eSAlex Bennée                 default:
414*f2b84b9eSAlex Bennée                     g_assert_not_reached();
415*f2b84b9eSAlex Bennée                 }
416*f2b84b9eSAlex Bennée             }
417*f2b84b9eSAlex Bennée             break;
41825f539f3SEmilio G. Cota         default:
41925f539f3SEmilio G. Cota             g_assert_not_reached();
42025f539f3SEmilio G. Cota         }
42125f539f3SEmilio G. Cota         ns_elapsed += get_clock() - t0;
42225f539f3SEmilio G. Cota         n_completed_ops += OPS_PER_ITER;
42325f539f3SEmilio G. Cota     }
42425f539f3SEmilio G. Cota }
42525f539f3SEmilio G. Cota 
42625f539f3SEmilio G. Cota #define GEN_BENCH(name, type, prec, op, n_ops)          \
42725f539f3SEmilio G. Cota     static void __attribute__((flatten)) name(void)     \
42825f539f3SEmilio G. Cota     {                                                   \
42925f539f3SEmilio G. Cota         bench(prec, op, n_ops, false);                  \
43025f539f3SEmilio G. Cota     }
43125f539f3SEmilio G. Cota 
43225f539f3SEmilio G. Cota #define GEN_BENCH_NO_NEG(name, type, prec, op, n_ops)   \
43325f539f3SEmilio G. Cota     static void __attribute__((flatten)) name(void)     \
43425f539f3SEmilio G. Cota     {                                                   \
43525f539f3SEmilio G. Cota         bench(prec, op, n_ops, true);                   \
43625f539f3SEmilio G. Cota     }
43725f539f3SEmilio G. Cota 
43825f539f3SEmilio G. Cota #define GEN_BENCH_ALL_TYPES(opname, op, n_ops)                          \
43925f539f3SEmilio G. Cota     GEN_BENCH(bench_ ## opname ## _float, float, PREC_SINGLE, op, n_ops) \
44025f539f3SEmilio G. Cota     GEN_BENCH(bench_ ## opname ## _double, double, PREC_DOUBLE, op, n_ops) \
44125f539f3SEmilio G. Cota     GEN_BENCH(bench_ ## opname ## _float32, float32, PREC_FLOAT32, op, n_ops) \
442*f2b84b9eSAlex Bennée     GEN_BENCH(bench_ ## opname ## _float64, float64, PREC_FLOAT64, op, n_ops) \
443*f2b84b9eSAlex Bennée     GEN_BENCH(bench_ ## opname ## _float128, float128, PREC_FLOAT128, op, n_ops)
44425f539f3SEmilio G. Cota 
44525f539f3SEmilio G. Cota GEN_BENCH_ALL_TYPES(add, OP_ADD, 2)
44625f539f3SEmilio G. Cota GEN_BENCH_ALL_TYPES(sub, OP_SUB, 2)
44725f539f3SEmilio G. Cota GEN_BENCH_ALL_TYPES(mul, OP_MUL, 2)
44825f539f3SEmilio G. Cota GEN_BENCH_ALL_TYPES(div, OP_DIV, 2)
44925f539f3SEmilio G. Cota GEN_BENCH_ALL_TYPES(fma, OP_FMA, 3)
45025f539f3SEmilio G. Cota GEN_BENCH_ALL_TYPES(cmp, OP_CMP, 2)
45125f539f3SEmilio G. Cota #undef GEN_BENCH_ALL_TYPES
45225f539f3SEmilio G. Cota 
45325f539f3SEmilio G. Cota #define GEN_BENCH_ALL_TYPES_NO_NEG(name, op, n)                         \
45425f539f3SEmilio G. Cota     GEN_BENCH_NO_NEG(bench_ ## name ## _float, float, PREC_SINGLE, op, n) \
45525f539f3SEmilio G. Cota     GEN_BENCH_NO_NEG(bench_ ## name ## _double, double, PREC_DOUBLE, op, n) \
45625f539f3SEmilio G. Cota     GEN_BENCH_NO_NEG(bench_ ## name ## _float32, float32, PREC_FLOAT32, op, n) \
457*f2b84b9eSAlex Bennée     GEN_BENCH_NO_NEG(bench_ ## name ## _float64, float64, PREC_FLOAT64, op, n) \
458*f2b84b9eSAlex Bennée     GEN_BENCH_NO_NEG(bench_ ## name ## _float128, float128, PREC_FLOAT128, op, n)
45925f539f3SEmilio G. Cota 
46025f539f3SEmilio G. Cota GEN_BENCH_ALL_TYPES_NO_NEG(sqrt, OP_SQRT, 1)
46125f539f3SEmilio G. Cota #undef GEN_BENCH_ALL_TYPES_NO_NEG
46225f539f3SEmilio G. Cota 
46325f539f3SEmilio G. Cota #undef GEN_BENCH_NO_NEG
46425f539f3SEmilio G. Cota #undef GEN_BENCH
46525f539f3SEmilio G. Cota 
46625f539f3SEmilio G. Cota #define GEN_BENCH_FUNCS(opname, op)                             \
46725f539f3SEmilio G. Cota     [op] = {                                                    \
46825f539f3SEmilio G. Cota         [PREC_SINGLE]    = bench_ ## opname ## _float,          \
46925f539f3SEmilio G. Cota         [PREC_DOUBLE]    = bench_ ## opname ## _double,         \
47025f539f3SEmilio G. Cota         [PREC_FLOAT32]   = bench_ ## opname ## _float32,        \
47125f539f3SEmilio G. Cota         [PREC_FLOAT64]   = bench_ ## opname ## _float64,        \
472*f2b84b9eSAlex Bennée         [PREC_FLOAT128]   = bench_ ## opname ## _float128,      \
47325f539f3SEmilio G. Cota     }
47425f539f3SEmilio G. Cota 
47525f539f3SEmilio G. Cota static const bench_func_t bench_funcs[OP_MAX_NR][PREC_MAX_NR] = {
47625f539f3SEmilio G. Cota     GEN_BENCH_FUNCS(add, OP_ADD),
47725f539f3SEmilio G. Cota     GEN_BENCH_FUNCS(sub, OP_SUB),
47825f539f3SEmilio G. Cota     GEN_BENCH_FUNCS(mul, OP_MUL),
47925f539f3SEmilio G. Cota     GEN_BENCH_FUNCS(div, OP_DIV),
48025f539f3SEmilio G. Cota     GEN_BENCH_FUNCS(fma, OP_FMA),
48125f539f3SEmilio G. Cota     GEN_BENCH_FUNCS(sqrt, OP_SQRT),
48225f539f3SEmilio G. Cota     GEN_BENCH_FUNCS(cmp, OP_CMP),
48325f539f3SEmilio G. Cota };
48425f539f3SEmilio G. Cota 
48525f539f3SEmilio G. Cota #undef GEN_BENCH_FUNCS
48625f539f3SEmilio G. Cota 
48725f539f3SEmilio G. Cota static void run_bench(void)
48825f539f3SEmilio G. Cota {
48925f539f3SEmilio G. Cota     bench_func_t f;
49025f539f3SEmilio G. Cota 
49125f539f3SEmilio G. Cota     f = bench_funcs[operation][precision];
49225f539f3SEmilio G. Cota     g_assert(f);
49325f539f3SEmilio G. Cota     f();
49425f539f3SEmilio G. Cota }
49525f539f3SEmilio G. Cota 
49625f539f3SEmilio G. Cota /* @arr must be NULL-terminated */
49725f539f3SEmilio G. Cota static int find_name(const char * const *arr, const char *name)
49825f539f3SEmilio G. Cota {
49925f539f3SEmilio G. Cota     int i;
50025f539f3SEmilio G. Cota 
50125f539f3SEmilio G. Cota     for (i = 0; arr[i] != NULL; i++) {
50225f539f3SEmilio G. Cota         if (strcmp(name, arr[i]) == 0) {
50325f539f3SEmilio G. Cota             return i;
50425f539f3SEmilio G. Cota         }
50525f539f3SEmilio G. Cota     }
50625f539f3SEmilio G. Cota     return -1;
50725f539f3SEmilio G. Cota }
50825f539f3SEmilio G. Cota 
50925f539f3SEmilio G. Cota static void usage_complete(int argc, char *argv[])
51025f539f3SEmilio G. Cota {
51125f539f3SEmilio G. Cota     gchar *op_list = g_strjoinv(", ", (gchar **)op_names);
51225f539f3SEmilio G. Cota     gchar *tester_list = g_strjoinv(", ", (gchar **)tester_names);
51325f539f3SEmilio G. Cota 
51425f539f3SEmilio G. Cota     fprintf(stderr, "Usage: %s [options]\n", argv[0]);
51525f539f3SEmilio G. Cota     fprintf(stderr, "options:\n");
51625f539f3SEmilio G. Cota     fprintf(stderr, " -d = duration, in seconds. Default: %d\n",
51725f539f3SEmilio G. Cota             DEFAULT_DURATION_SECS);
51825f539f3SEmilio G. Cota     fprintf(stderr, " -h = show this help message.\n");
51925f539f3SEmilio G. Cota     fprintf(stderr, " -o = floating point operation (%s). Default: %s\n",
52025f539f3SEmilio G. Cota             op_list, op_names[0]);
521*f2b84b9eSAlex Bennée     fprintf(stderr, " -p = floating point precision (single, double, quad[soft only]). "
52225f539f3SEmilio G. Cota             "Default: single\n");
52325f539f3SEmilio G. Cota     fprintf(stderr, " -r = rounding mode (even, zero, down, up, tieaway). "
52425f539f3SEmilio G. Cota             "Default: even\n");
52525f539f3SEmilio G. Cota     fprintf(stderr, " -t = tester (%s). Default: %s\n",
52625f539f3SEmilio G. Cota             tester_list, tester_names[0]);
52725f539f3SEmilio G. Cota     fprintf(stderr, " -z = flush inputs to zero (soft tester only). "
52825f539f3SEmilio G. Cota             "Default: disabled\n");
52925f539f3SEmilio G. Cota     fprintf(stderr, " -Z = flush output to zero (soft tester only). "
53025f539f3SEmilio G. Cota             "Default: disabled\n");
53125f539f3SEmilio G. Cota 
53225f539f3SEmilio G. Cota     g_free(tester_list);
53325f539f3SEmilio G. Cota     g_free(op_list);
53425f539f3SEmilio G. Cota }
53525f539f3SEmilio G. Cota 
53625f539f3SEmilio G. Cota static int round_name_to_mode(const char *name)
53725f539f3SEmilio G. Cota {
53825f539f3SEmilio G. Cota     int i;
53925f539f3SEmilio G. Cota 
54025f539f3SEmilio G. Cota     for (i = 0; i < N_ROUND_MODES; i++) {
54125f539f3SEmilio G. Cota         if (!strcmp(round_names[i], name)) {
54225f539f3SEmilio G. Cota             return i;
54325f539f3SEmilio G. Cota         }
54425f539f3SEmilio G. Cota     }
54525f539f3SEmilio G. Cota     return -1;
54625f539f3SEmilio G. Cota }
54725f539f3SEmilio G. Cota 
54825f539f3SEmilio G. Cota static void QEMU_NORETURN die_host_rounding(enum rounding rounding)
54925f539f3SEmilio G. Cota {
55025f539f3SEmilio G. Cota     fprintf(stderr, "fatal: '%s' rounding not supported on this host\n",
55125f539f3SEmilio G. Cota             round_names[rounding]);
55225f539f3SEmilio G. Cota     exit(EXIT_FAILURE);
55325f539f3SEmilio G. Cota }
55425f539f3SEmilio G. Cota 
55525f539f3SEmilio G. Cota static void set_host_precision(enum rounding rounding)
55625f539f3SEmilio G. Cota {
55725f539f3SEmilio G. Cota     int rhost;
55825f539f3SEmilio G. Cota 
55925f539f3SEmilio G. Cota     switch (rounding) {
56025f539f3SEmilio G. Cota     case ROUND_EVEN:
56125f539f3SEmilio G. Cota         rhost = FE_TONEAREST;
56225f539f3SEmilio G. Cota         break;
56325f539f3SEmilio G. Cota     case ROUND_ZERO:
56425f539f3SEmilio G. Cota         rhost = FE_TOWARDZERO;
56525f539f3SEmilio G. Cota         break;
56625f539f3SEmilio G. Cota     case ROUND_DOWN:
56725f539f3SEmilio G. Cota         rhost = FE_DOWNWARD;
56825f539f3SEmilio G. Cota         break;
56925f539f3SEmilio G. Cota     case ROUND_UP:
57025f539f3SEmilio G. Cota         rhost = FE_UPWARD;
57125f539f3SEmilio G. Cota         break;
57225f539f3SEmilio G. Cota     case ROUND_TIEAWAY:
57325f539f3SEmilio G. Cota         die_host_rounding(rounding);
57425f539f3SEmilio G. Cota         return;
57525f539f3SEmilio G. Cota     default:
57625f539f3SEmilio G. Cota         g_assert_not_reached();
57725f539f3SEmilio G. Cota     }
57825f539f3SEmilio G. Cota 
57925f539f3SEmilio G. Cota     if (fesetround(rhost)) {
58025f539f3SEmilio G. Cota         die_host_rounding(rounding);
58125f539f3SEmilio G. Cota     }
58225f539f3SEmilio G. Cota }
58325f539f3SEmilio G. Cota 
58425f539f3SEmilio G. Cota static void set_soft_precision(enum rounding rounding)
58525f539f3SEmilio G. Cota {
58625f539f3SEmilio G. Cota     signed char mode;
58725f539f3SEmilio G. Cota 
58825f539f3SEmilio G. Cota     switch (rounding) {
58925f539f3SEmilio G. Cota     case ROUND_EVEN:
59025f539f3SEmilio G. Cota         mode = float_round_nearest_even;
59125f539f3SEmilio G. Cota         break;
59225f539f3SEmilio G. Cota     case ROUND_ZERO:
59325f539f3SEmilio G. Cota         mode = float_round_to_zero;
59425f539f3SEmilio G. Cota         break;
59525f539f3SEmilio G. Cota     case ROUND_DOWN:
59625f539f3SEmilio G. Cota         mode = float_round_down;
59725f539f3SEmilio G. Cota         break;
59825f539f3SEmilio G. Cota     case ROUND_UP:
59925f539f3SEmilio G. Cota         mode = float_round_up;
60025f539f3SEmilio G. Cota         break;
60125f539f3SEmilio G. Cota     case ROUND_TIEAWAY:
60225f539f3SEmilio G. Cota         mode = float_round_ties_away;
60325f539f3SEmilio G. Cota         break;
60425f539f3SEmilio G. Cota     default:
60525f539f3SEmilio G. Cota         g_assert_not_reached();
60625f539f3SEmilio G. Cota     }
60725f539f3SEmilio G. Cota     soft_status.float_rounding_mode = mode;
60825f539f3SEmilio G. Cota }
60925f539f3SEmilio G. Cota 
61025f539f3SEmilio G. Cota static void parse_args(int argc, char *argv[])
61125f539f3SEmilio G. Cota {
61225f539f3SEmilio G. Cota     int c;
61325f539f3SEmilio G. Cota     int val;
61425f539f3SEmilio G. Cota     int rounding = ROUND_EVEN;
61525f539f3SEmilio G. Cota 
61625f539f3SEmilio G. Cota     for (;;) {
61725f539f3SEmilio G. Cota         c = getopt(argc, argv, "d:ho:p:r:t:zZ");
61825f539f3SEmilio G. Cota         if (c < 0) {
61925f539f3SEmilio G. Cota             break;
62025f539f3SEmilio G. Cota         }
62125f539f3SEmilio G. Cota         switch (c) {
62225f539f3SEmilio G. Cota         case 'd':
62325f539f3SEmilio G. Cota             duration = atoi(optarg);
62425f539f3SEmilio G. Cota             break;
62525f539f3SEmilio G. Cota         case 'h':
62625f539f3SEmilio G. Cota             usage_complete(argc, argv);
62725f539f3SEmilio G. Cota             exit(EXIT_SUCCESS);
62825f539f3SEmilio G. Cota         case 'o':
62925f539f3SEmilio G. Cota             val = find_name(op_names, optarg);
63025f539f3SEmilio G. Cota             if (val < 0) {
63125f539f3SEmilio G. Cota                 fprintf(stderr, "Unsupported op '%s'\n", optarg);
63225f539f3SEmilio G. Cota                 exit(EXIT_FAILURE);
63325f539f3SEmilio G. Cota             }
63425f539f3SEmilio G. Cota             operation = val;
63525f539f3SEmilio G. Cota             break;
63625f539f3SEmilio G. Cota         case 'p':
63725f539f3SEmilio G. Cota             if (!strcmp(optarg, "single")) {
63825f539f3SEmilio G. Cota                 precision = PREC_SINGLE;
63925f539f3SEmilio G. Cota             } else if (!strcmp(optarg, "double")) {
64025f539f3SEmilio G. Cota                 precision = PREC_DOUBLE;
641*f2b84b9eSAlex Bennée             } else if (!strcmp(optarg, "quad")) {
642*f2b84b9eSAlex Bennée                 precision = PREC_QUAD;
64325f539f3SEmilio G. Cota             } else {
64425f539f3SEmilio G. Cota                 fprintf(stderr, "Unsupported precision '%s'\n", optarg);
64525f539f3SEmilio G. Cota                 exit(EXIT_FAILURE);
64625f539f3SEmilio G. Cota             }
64725f539f3SEmilio G. Cota             break;
64825f539f3SEmilio G. Cota         case 'r':
64925f539f3SEmilio G. Cota             rounding = round_name_to_mode(optarg);
65025f539f3SEmilio G. Cota             if (rounding < 0) {
65125f539f3SEmilio G. Cota                 fprintf(stderr, "fatal: invalid rounding mode '%s'\n", optarg);
65225f539f3SEmilio G. Cota                 exit(EXIT_FAILURE);
65325f539f3SEmilio G. Cota             }
65425f539f3SEmilio G. Cota             break;
65525f539f3SEmilio G. Cota         case 't':
65625f539f3SEmilio G. Cota             val = find_name(tester_names, optarg);
65725f539f3SEmilio G. Cota             if (val < 0) {
65825f539f3SEmilio G. Cota                 fprintf(stderr, "Unsupported tester '%s'\n", optarg);
65925f539f3SEmilio G. Cota                 exit(EXIT_FAILURE);
66025f539f3SEmilio G. Cota             }
66125f539f3SEmilio G. Cota             tester = val;
66225f539f3SEmilio G. Cota             break;
66325f539f3SEmilio G. Cota         case 'z':
66425f539f3SEmilio G. Cota             soft_status.flush_inputs_to_zero = 1;
66525f539f3SEmilio G. Cota             break;
66625f539f3SEmilio G. Cota         case 'Z':
66725f539f3SEmilio G. Cota             soft_status.flush_to_zero = 1;
66825f539f3SEmilio G. Cota             break;
66925f539f3SEmilio G. Cota         }
67025f539f3SEmilio G. Cota     }
67125f539f3SEmilio G. Cota 
67225f539f3SEmilio G. Cota     /* set precision and rounding mode based on the tester */
67325f539f3SEmilio G. Cota     switch (tester) {
67425f539f3SEmilio G. Cota     case TESTER_HOST:
67525f539f3SEmilio G. Cota         set_host_precision(rounding);
67625f539f3SEmilio G. Cota         break;
67725f539f3SEmilio G. Cota     case TESTER_SOFT:
67825f539f3SEmilio G. Cota         set_soft_precision(rounding);
67925f539f3SEmilio G. Cota         switch (precision) {
68025f539f3SEmilio G. Cota         case PREC_SINGLE:
68125f539f3SEmilio G. Cota             precision = PREC_FLOAT32;
68225f539f3SEmilio G. Cota             break;
68325f539f3SEmilio G. Cota         case PREC_DOUBLE:
68425f539f3SEmilio G. Cota             precision = PREC_FLOAT64;
68525f539f3SEmilio G. Cota             break;
686*f2b84b9eSAlex Bennée         case PREC_QUAD:
687*f2b84b9eSAlex Bennée             precision = PREC_FLOAT128;
688*f2b84b9eSAlex Bennée             break;
68925f539f3SEmilio G. Cota         default:
69025f539f3SEmilio G. Cota             g_assert_not_reached();
69125f539f3SEmilio G. Cota         }
69225f539f3SEmilio G. Cota         break;
69325f539f3SEmilio G. Cota     default:
69425f539f3SEmilio G. Cota         g_assert_not_reached();
69525f539f3SEmilio G. Cota     }
69625f539f3SEmilio G. Cota }
69725f539f3SEmilio G. Cota 
69825f539f3SEmilio G. Cota static void pr_stats(void)
69925f539f3SEmilio G. Cota {
70025f539f3SEmilio G. Cota     printf("%.2f MFlops\n", (double)n_completed_ops / ns_elapsed * 1e3);
70125f539f3SEmilio G. Cota }
70225f539f3SEmilio G. Cota 
70325f539f3SEmilio G. Cota int main(int argc, char *argv[])
70425f539f3SEmilio G. Cota {
70525f539f3SEmilio G. Cota     parse_args(argc, argv);
70625f539f3SEmilio G. Cota     run_bench();
70725f539f3SEmilio G. Cota     pr_stats();
70825f539f3SEmilio G. Cota     return 0;
70925f539f3SEmilio G. Cota }
710