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"
17f2b84b9eSAlex 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,
54f2b84b9eSAlex Bennée PREC_QUAD,
5525f539f3SEmilio G. Cota PREC_FLOAT32,
5625f539f3SEmilio G. Cota PREC_FLOAT64,
57f2b84b9eSAlex 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;
95f2b84b9eSAlex 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 };
120f2b84b9eSAlex Bennée
121f2b84b9eSAlex Bennée static float128 random_quad_ops[MAX_OPERANDS] = {
122f2b84b9eSAlex Bennée {SEED_A, SEED_B}, {SEED_B, SEED_C}, {SEED_C, SEED_A},
123f2b84b9eSAlex 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 */
xorshift64star(uint64_t x)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
update_random_ops(int n_ops,enum precision prec)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:
156f2b84b9eSAlex Bennée {
157f2b84b9eSAlex 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));
161f2b84b9eSAlex Bennée random_ops[i] = r;
162446cfb0dSEmilio G. Cota break;
163f2b84b9eSAlex Bennée }
164446cfb0dSEmilio G. Cota case PREC_DOUBLE:
165446cfb0dSEmilio G. Cota case PREC_FLOAT64:
166f2b84b9eSAlex Bennée {
167f2b84b9eSAlex 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));
171f2b84b9eSAlex Bennée random_ops[i] = r;
172446cfb0dSEmilio G. Cota break;
173f2b84b9eSAlex Bennée }
174f2b84b9eSAlex Bennée case PREC_QUAD:
175f2b84b9eSAlex Bennée case PREC_FLOAT128:
176f2b84b9eSAlex Bennée {
177f2b84b9eSAlex Bennée float128 r = random_quad_ops[i];
178f2b84b9eSAlex Bennée uint64_t hi = r.high;
179f2b84b9eSAlex Bennée uint64_t lo = r.low;
180f2b84b9eSAlex Bennée do {
181f2b84b9eSAlex Bennée hi = xorshift64star(hi);
182f2b84b9eSAlex Bennée lo = xorshift64star(lo);
183f2b84b9eSAlex Bennée r = make_float128(hi, lo);
184f2b84b9eSAlex Bennée } while (!float128_is_normal(r));
185f2b84b9eSAlex Bennée random_quad_ops[i] = r;
186f2b84b9eSAlex Bennée break;
187f2b84b9eSAlex 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
fill_random(union fp * ops,int n_ops,enum precision prec,bool no_neg)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;
215f2b84b9eSAlex Bennée case PREC_QUAD:
216f2b84b9eSAlex Bennée case PREC_FLOAT128:
217f2b84b9eSAlex Bennée ops[i].f128 = random_quad_ops[i];
218f2b84b9eSAlex Bennée if (no_neg && float128_is_neg(ops[i].f128)) {
219f2b84b9eSAlex Bennée ops[i].f128 = float128_chs(ops[i].f128);
220f2b84b9eSAlex Bennée }
221f2b84b9eSAlex 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 */
bench(enum precision prec,enum op op,int n_ops,bool no_neg)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;
383f2b84b9eSAlex Bennée case PREC_FLOAT128:
384f2b84b9eSAlex Bennée fill_random(ops, n_ops, prec, no_neg);
385f2b84b9eSAlex Bennée t0 = get_clock();
386f2b84b9eSAlex Bennée for (i = 0; i < OPS_PER_ITER; i++) {
387f2b84b9eSAlex Bennée float128 a = ops[0].f128;
388f2b84b9eSAlex Bennée float128 b = ops[1].f128;
389dedd123cSRichard Henderson float128 c = ops[2].f128;
390f2b84b9eSAlex Bennée
391f2b84b9eSAlex Bennée switch (op) {
392f2b84b9eSAlex Bennée case OP_ADD:
393f2b84b9eSAlex Bennée res.f128 = float128_add(a, b, &soft_status);
394f2b84b9eSAlex Bennée break;
395f2b84b9eSAlex Bennée case OP_SUB:
396f2b84b9eSAlex Bennée res.f128 = float128_sub(a, b, &soft_status);
397f2b84b9eSAlex Bennée break;
398f2b84b9eSAlex Bennée case OP_MUL:
399f2b84b9eSAlex Bennée res.f128 = float128_mul(a, b, &soft_status);
400f2b84b9eSAlex Bennée break;
401f2b84b9eSAlex Bennée case OP_DIV:
402f2b84b9eSAlex Bennée res.f128 = float128_div(a, b, &soft_status);
403f2b84b9eSAlex Bennée break;
404dedd123cSRichard Henderson case OP_FMA:
405dedd123cSRichard Henderson res.f128 = float128_muladd(a, b, c, 0, &soft_status);
406dedd123cSRichard Henderson break;
407f2b84b9eSAlex Bennée case OP_SQRT:
408f2b84b9eSAlex Bennée res.f128 = float128_sqrt(a, &soft_status);
409f2b84b9eSAlex Bennée break;
410f2b84b9eSAlex Bennée case OP_CMP:
411f2b84b9eSAlex Bennée res.u64 = float128_compare_quiet(a, b, &soft_status);
412f2b84b9eSAlex Bennée break;
413f2b84b9eSAlex Bennée default:
414f2b84b9eSAlex Bennée g_assert_not_reached();
415f2b84b9eSAlex Bennée }
416f2b84b9eSAlex Bennée }
417f2b84b9eSAlex 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) \
442f2b84b9eSAlex Bennée GEN_BENCH(bench_ ## opname ## _float64, float64, PREC_FLOAT64, op, n_ops) \
443f2b84b9eSAlex 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) \
457f2b84b9eSAlex Bennée GEN_BENCH_NO_NEG(bench_ ## name ## _float64, float64, PREC_FLOAT64, op, n) \
458f2b84b9eSAlex 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, \
472f2b84b9eSAlex 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
run_bench(void)48725f539f3SEmilio G. Cota static void run_bench(void)
48825f539f3SEmilio G. Cota {
48925f539f3SEmilio G. Cota bench_func_t f;
49025f539f3SEmilio G. Cota
49127aedf7dSPeter Maydell /*
49227aedf7dSPeter Maydell * These implementation-defined choices for various things IEEE
49327aedf7dSPeter Maydell * doesn't specify match those used by the Arm architecture.
49427aedf7dSPeter Maydell */
495d22c9949SPeter Maydell set_float_2nan_prop_rule(float_2nan_prop_s_ab, &soft_status);
49643e51128SPeter Maydell set_float_3nan_prop_rule(float_3nan_prop_s_cab, &soft_status);
49727aedf7dSPeter Maydell set_float_infzeronan_rule(float_infzeronan_dnan_if_qnan, &soft_status);
498a264fd5aSPeter Maydell set_float_default_nan_pattern(0b01000000, &soft_status);
499*28f13bccSPeter Maydell set_float_ftz_detection(float_ftz_before_rounding, &soft_status);
500d22c9949SPeter Maydell
50125f539f3SEmilio G. Cota f = bench_funcs[operation][precision];
50225f539f3SEmilio G. Cota g_assert(f);
50325f539f3SEmilio G. Cota f();
50425f539f3SEmilio G. Cota }
50525f539f3SEmilio G. Cota
50625f539f3SEmilio G. Cota /* @arr must be NULL-terminated */
find_name(const char * const * arr,const char * name)50725f539f3SEmilio G. Cota static int find_name(const char * const *arr, const char *name)
50825f539f3SEmilio G. Cota {
50925f539f3SEmilio G. Cota int i;
51025f539f3SEmilio G. Cota
51125f539f3SEmilio G. Cota for (i = 0; arr[i] != NULL; i++) {
51225f539f3SEmilio G. Cota if (strcmp(name, arr[i]) == 0) {
51325f539f3SEmilio G. Cota return i;
51425f539f3SEmilio G. Cota }
51525f539f3SEmilio G. Cota }
51625f539f3SEmilio G. Cota return -1;
51725f539f3SEmilio G. Cota }
51825f539f3SEmilio G. Cota
usage_complete(int argc,char * argv[])51925f539f3SEmilio G. Cota static void usage_complete(int argc, char *argv[])
52025f539f3SEmilio G. Cota {
52125f539f3SEmilio G. Cota gchar *op_list = g_strjoinv(", ", (gchar **)op_names);
52225f539f3SEmilio G. Cota gchar *tester_list = g_strjoinv(", ", (gchar **)tester_names);
52325f539f3SEmilio G. Cota
52425f539f3SEmilio G. Cota fprintf(stderr, "Usage: %s [options]\n", argv[0]);
52525f539f3SEmilio G. Cota fprintf(stderr, "options:\n");
52625f539f3SEmilio G. Cota fprintf(stderr, " -d = duration, in seconds. Default: %d\n",
52725f539f3SEmilio G. Cota DEFAULT_DURATION_SECS);
52825f539f3SEmilio G. Cota fprintf(stderr, " -h = show this help message.\n");
52925f539f3SEmilio G. Cota fprintf(stderr, " -o = floating point operation (%s). Default: %s\n",
53025f539f3SEmilio G. Cota op_list, op_names[0]);
531f2b84b9eSAlex Bennée fprintf(stderr, " -p = floating point precision (single, double, quad[soft only]). "
53225f539f3SEmilio G. Cota "Default: single\n");
53325f539f3SEmilio G. Cota fprintf(stderr, " -r = rounding mode (even, zero, down, up, tieaway). "
53425f539f3SEmilio G. Cota "Default: even\n");
53525f539f3SEmilio G. Cota fprintf(stderr, " -t = tester (%s). Default: %s\n",
53625f539f3SEmilio G. Cota tester_list, tester_names[0]);
53725f539f3SEmilio G. Cota fprintf(stderr, " -z = flush inputs to zero (soft tester only). "
53825f539f3SEmilio G. Cota "Default: disabled\n");
53925f539f3SEmilio G. Cota fprintf(stderr, " -Z = flush output to zero (soft tester only). "
54025f539f3SEmilio G. Cota "Default: disabled\n");
54125f539f3SEmilio G. Cota
54225f539f3SEmilio G. Cota g_free(tester_list);
54325f539f3SEmilio G. Cota g_free(op_list);
54425f539f3SEmilio G. Cota }
54525f539f3SEmilio G. Cota
round_name_to_mode(const char * name)54625f539f3SEmilio G. Cota static int round_name_to_mode(const char *name)
54725f539f3SEmilio G. Cota {
54825f539f3SEmilio G. Cota int i;
54925f539f3SEmilio G. Cota
55025f539f3SEmilio G. Cota for (i = 0; i < N_ROUND_MODES; i++) {
55125f539f3SEmilio G. Cota if (!strcmp(round_names[i], name)) {
55225f539f3SEmilio G. Cota return i;
55325f539f3SEmilio G. Cota }
55425f539f3SEmilio G. Cota }
55525f539f3SEmilio G. Cota return -1;
55625f539f3SEmilio G. Cota }
55725f539f3SEmilio G. Cota
5588905770bSMarc-André Lureau static G_NORETURN
die_host_rounding(enum rounding rounding)5598905770bSMarc-André Lureau void die_host_rounding(enum rounding rounding)
56025f539f3SEmilio G. Cota {
56125f539f3SEmilio G. Cota fprintf(stderr, "fatal: '%s' rounding not supported on this host\n",
56225f539f3SEmilio G. Cota round_names[rounding]);
56325f539f3SEmilio G. Cota exit(EXIT_FAILURE);
56425f539f3SEmilio G. Cota }
56525f539f3SEmilio G. Cota
set_host_precision(enum rounding rounding)56625f539f3SEmilio G. Cota static void set_host_precision(enum rounding rounding)
56725f539f3SEmilio G. Cota {
56825f539f3SEmilio G. Cota int rhost;
56925f539f3SEmilio G. Cota
57025f539f3SEmilio G. Cota switch (rounding) {
57125f539f3SEmilio G. Cota case ROUND_EVEN:
57225f539f3SEmilio G. Cota rhost = FE_TONEAREST;
57325f539f3SEmilio G. Cota break;
57425f539f3SEmilio G. Cota case ROUND_ZERO:
57525f539f3SEmilio G. Cota rhost = FE_TOWARDZERO;
57625f539f3SEmilio G. Cota break;
57725f539f3SEmilio G. Cota case ROUND_DOWN:
57825f539f3SEmilio G. Cota rhost = FE_DOWNWARD;
57925f539f3SEmilio G. Cota break;
58025f539f3SEmilio G. Cota case ROUND_UP:
58125f539f3SEmilio G. Cota rhost = FE_UPWARD;
58225f539f3SEmilio G. Cota break;
58325f539f3SEmilio G. Cota case ROUND_TIEAWAY:
58425f539f3SEmilio G. Cota die_host_rounding(rounding);
58525f539f3SEmilio G. Cota return;
58625f539f3SEmilio G. Cota default:
58725f539f3SEmilio G. Cota g_assert_not_reached();
58825f539f3SEmilio G. Cota }
58925f539f3SEmilio G. Cota
59025f539f3SEmilio G. Cota if (fesetround(rhost)) {
59125f539f3SEmilio G. Cota die_host_rounding(rounding);
59225f539f3SEmilio G. Cota }
59325f539f3SEmilio G. Cota }
59425f539f3SEmilio G. Cota
set_soft_precision(enum rounding rounding)59525f539f3SEmilio G. Cota static void set_soft_precision(enum rounding rounding)
59625f539f3SEmilio G. Cota {
59725f539f3SEmilio G. Cota signed char mode;
59825f539f3SEmilio G. Cota
59925f539f3SEmilio G. Cota switch (rounding) {
60025f539f3SEmilio G. Cota case ROUND_EVEN:
60125f539f3SEmilio G. Cota mode = float_round_nearest_even;
60225f539f3SEmilio G. Cota break;
60325f539f3SEmilio G. Cota case ROUND_ZERO:
60425f539f3SEmilio G. Cota mode = float_round_to_zero;
60525f539f3SEmilio G. Cota break;
60625f539f3SEmilio G. Cota case ROUND_DOWN:
60725f539f3SEmilio G. Cota mode = float_round_down;
60825f539f3SEmilio G. Cota break;
60925f539f3SEmilio G. Cota case ROUND_UP:
61025f539f3SEmilio G. Cota mode = float_round_up;
61125f539f3SEmilio G. Cota break;
61225f539f3SEmilio G. Cota case ROUND_TIEAWAY:
61325f539f3SEmilio G. Cota mode = float_round_ties_away;
61425f539f3SEmilio G. Cota break;
61525f539f3SEmilio G. Cota default:
61625f539f3SEmilio G. Cota g_assert_not_reached();
61725f539f3SEmilio G. Cota }
61825f539f3SEmilio G. Cota soft_status.float_rounding_mode = mode;
61925f539f3SEmilio G. Cota }
62025f539f3SEmilio G. Cota
parse_args(int argc,char * argv[])62125f539f3SEmilio G. Cota static void parse_args(int argc, char *argv[])
62225f539f3SEmilio G. Cota {
62325f539f3SEmilio G. Cota int c;
62425f539f3SEmilio G. Cota int val;
62525f539f3SEmilio G. Cota int rounding = ROUND_EVEN;
62625f539f3SEmilio G. Cota
62725f539f3SEmilio G. Cota for (;;) {
62825f539f3SEmilio G. Cota c = getopt(argc, argv, "d:ho:p:r:t:zZ");
62925f539f3SEmilio G. Cota if (c < 0) {
63025f539f3SEmilio G. Cota break;
63125f539f3SEmilio G. Cota }
63225f539f3SEmilio G. Cota switch (c) {
63325f539f3SEmilio G. Cota case 'd':
63425f539f3SEmilio G. Cota duration = atoi(optarg);
63525f539f3SEmilio G. Cota break;
63625f539f3SEmilio G. Cota case 'h':
63725f539f3SEmilio G. Cota usage_complete(argc, argv);
63825f539f3SEmilio G. Cota exit(EXIT_SUCCESS);
63925f539f3SEmilio G. Cota case 'o':
64025f539f3SEmilio G. Cota val = find_name(op_names, optarg);
64125f539f3SEmilio G. Cota if (val < 0) {
64225f539f3SEmilio G. Cota fprintf(stderr, "Unsupported op '%s'\n", optarg);
64325f539f3SEmilio G. Cota exit(EXIT_FAILURE);
64425f539f3SEmilio G. Cota }
64525f539f3SEmilio G. Cota operation = val;
64625f539f3SEmilio G. Cota break;
64725f539f3SEmilio G. Cota case 'p':
64825f539f3SEmilio G. Cota if (!strcmp(optarg, "single")) {
64925f539f3SEmilio G. Cota precision = PREC_SINGLE;
65025f539f3SEmilio G. Cota } else if (!strcmp(optarg, "double")) {
65125f539f3SEmilio G. Cota precision = PREC_DOUBLE;
652f2b84b9eSAlex Bennée } else if (!strcmp(optarg, "quad")) {
653f2b84b9eSAlex Bennée precision = PREC_QUAD;
65425f539f3SEmilio G. Cota } else {
65525f539f3SEmilio G. Cota fprintf(stderr, "Unsupported precision '%s'\n", optarg);
65625f539f3SEmilio G. Cota exit(EXIT_FAILURE);
65725f539f3SEmilio G. Cota }
65825f539f3SEmilio G. Cota break;
65925f539f3SEmilio G. Cota case 'r':
66025f539f3SEmilio G. Cota rounding = round_name_to_mode(optarg);
66125f539f3SEmilio G. Cota if (rounding < 0) {
66225f539f3SEmilio G. Cota fprintf(stderr, "fatal: invalid rounding mode '%s'\n", optarg);
66325f539f3SEmilio G. Cota exit(EXIT_FAILURE);
66425f539f3SEmilio G. Cota }
66525f539f3SEmilio G. Cota break;
66625f539f3SEmilio G. Cota case 't':
66725f539f3SEmilio G. Cota val = find_name(tester_names, optarg);
66825f539f3SEmilio G. Cota if (val < 0) {
66925f539f3SEmilio G. Cota fprintf(stderr, "Unsupported tester '%s'\n", optarg);
67025f539f3SEmilio G. Cota exit(EXIT_FAILURE);
67125f539f3SEmilio G. Cota }
67225f539f3SEmilio G. Cota tester = val;
67325f539f3SEmilio G. Cota break;
67425f539f3SEmilio G. Cota case 'z':
67525f539f3SEmilio G. Cota soft_status.flush_inputs_to_zero = 1;
67625f539f3SEmilio G. Cota break;
67725f539f3SEmilio G. Cota case 'Z':
67825f539f3SEmilio G. Cota soft_status.flush_to_zero = 1;
67925f539f3SEmilio G. Cota break;
68025f539f3SEmilio G. Cota }
68125f539f3SEmilio G. Cota }
68225f539f3SEmilio G. Cota
68325f539f3SEmilio G. Cota /* set precision and rounding mode based on the tester */
68425f539f3SEmilio G. Cota switch (tester) {
68525f539f3SEmilio G. Cota case TESTER_HOST:
68625f539f3SEmilio G. Cota set_host_precision(rounding);
68725f539f3SEmilio G. Cota break;
68825f539f3SEmilio G. Cota case TESTER_SOFT:
68925f539f3SEmilio G. Cota set_soft_precision(rounding);
69025f539f3SEmilio G. Cota switch (precision) {
69125f539f3SEmilio G. Cota case PREC_SINGLE:
69225f539f3SEmilio G. Cota precision = PREC_FLOAT32;
69325f539f3SEmilio G. Cota break;
69425f539f3SEmilio G. Cota case PREC_DOUBLE:
69525f539f3SEmilio G. Cota precision = PREC_FLOAT64;
69625f539f3SEmilio G. Cota break;
697f2b84b9eSAlex Bennée case PREC_QUAD:
698f2b84b9eSAlex Bennée precision = PREC_FLOAT128;
699f2b84b9eSAlex Bennée break;
70025f539f3SEmilio G. Cota default:
70125f539f3SEmilio G. Cota g_assert_not_reached();
70225f539f3SEmilio G. Cota }
70325f539f3SEmilio G. Cota break;
70425f539f3SEmilio G. Cota default:
70525f539f3SEmilio G. Cota g_assert_not_reached();
70625f539f3SEmilio G. Cota }
70725f539f3SEmilio G. Cota }
70825f539f3SEmilio G. Cota
pr_stats(void)70925f539f3SEmilio G. Cota static void pr_stats(void)
71025f539f3SEmilio G. Cota {
71125f539f3SEmilio G. Cota printf("%.2f MFlops\n", (double)n_completed_ops / ns_elapsed * 1e3);
71225f539f3SEmilio G. Cota }
71325f539f3SEmilio G. Cota
main(int argc,char * argv[])71425f539f3SEmilio G. Cota int main(int argc, char *argv[])
71525f539f3SEmilio G. Cota {
71625f539f3SEmilio G. Cota parse_args(argc, argv);
71725f539f3SEmilio G. Cota run_bench();
71825f539f3SEmilio G. Cota pr_stats();
71925f539f3SEmilio G. Cota return 0;
72025f539f3SEmilio G. Cota }
721