xref: /qemu/tests/fp/fp-bench.c (revision 25f539f359447cd0dbfd5954aaa3c22b3921af79)
1*25f539f3SEmilio G. Cota /*
2*25f539f3SEmilio G. Cota  * fp-bench.c - A collection of simple floating point microbenchmarks.
3*25f539f3SEmilio G. Cota  *
4*25f539f3SEmilio G. Cota  * Copyright (C) 2018, Emilio G. Cota <cota@braap.org>
5*25f539f3SEmilio G. Cota  *
6*25f539f3SEmilio G. Cota  * License: GNU GPL, version 2 or later.
7*25f539f3SEmilio G. Cota  *   See the COPYING file in the top-level directory.
8*25f539f3SEmilio G. Cota  */
9*25f539f3SEmilio G. Cota #ifndef HW_POISON_H
10*25f539f3SEmilio G. Cota #error Must define HW_POISON_H to work around TARGET_* poisoning
11*25f539f3SEmilio G. Cota #endif
12*25f539f3SEmilio G. Cota 
13*25f539f3SEmilio G. Cota #include "qemu/osdep.h"
14*25f539f3SEmilio G. Cota #include <math.h>
15*25f539f3SEmilio G. Cota #include <fenv.h>
16*25f539f3SEmilio G. Cota #include "qemu/timer.h"
17*25f539f3SEmilio G. Cota #include "fpu/softfloat.h"
18*25f539f3SEmilio G. Cota 
19*25f539f3SEmilio G. Cota /* amortize the computation of random inputs */
20*25f539f3SEmilio G. Cota #define OPS_PER_ITER     50000
21*25f539f3SEmilio G. Cota 
22*25f539f3SEmilio G. Cota #define MAX_OPERANDS 3
23*25f539f3SEmilio G. Cota 
24*25f539f3SEmilio G. Cota #define SEED_A 0xdeadfacedeadface
25*25f539f3SEmilio G. Cota #define SEED_B 0xbadc0feebadc0fee
26*25f539f3SEmilio G. Cota #define SEED_C 0xbeefdeadbeefdead
27*25f539f3SEmilio G. Cota 
28*25f539f3SEmilio G. Cota enum op {
29*25f539f3SEmilio G. Cota     OP_ADD,
30*25f539f3SEmilio G. Cota     OP_SUB,
31*25f539f3SEmilio G. Cota     OP_MUL,
32*25f539f3SEmilio G. Cota     OP_DIV,
33*25f539f3SEmilio G. Cota     OP_FMA,
34*25f539f3SEmilio G. Cota     OP_SQRT,
35*25f539f3SEmilio G. Cota     OP_CMP,
36*25f539f3SEmilio G. Cota     OP_MAX_NR,
37*25f539f3SEmilio G. Cota };
38*25f539f3SEmilio G. Cota 
39*25f539f3SEmilio G. Cota static const char * const op_names[] = {
40*25f539f3SEmilio G. Cota     [OP_ADD] = "add",
41*25f539f3SEmilio G. Cota     [OP_SUB] = "sub",
42*25f539f3SEmilio G. Cota     [OP_MUL] = "mul",
43*25f539f3SEmilio G. Cota     [OP_DIV] = "div",
44*25f539f3SEmilio G. Cota     [OP_FMA] = "mulAdd",
45*25f539f3SEmilio G. Cota     [OP_SQRT] = "sqrt",
46*25f539f3SEmilio G. Cota     [OP_CMP] = "cmp",
47*25f539f3SEmilio G. Cota     [OP_MAX_NR] = NULL,
48*25f539f3SEmilio G. Cota };
49*25f539f3SEmilio G. Cota 
50*25f539f3SEmilio G. Cota enum precision {
51*25f539f3SEmilio G. Cota     PREC_SINGLE,
52*25f539f3SEmilio G. Cota     PREC_DOUBLE,
53*25f539f3SEmilio G. Cota     PREC_FLOAT32,
54*25f539f3SEmilio G. Cota     PREC_FLOAT64,
55*25f539f3SEmilio G. Cota     PREC_MAX_NR,
56*25f539f3SEmilio G. Cota };
57*25f539f3SEmilio G. Cota 
58*25f539f3SEmilio G. Cota enum rounding {
59*25f539f3SEmilio G. Cota     ROUND_EVEN,
60*25f539f3SEmilio G. Cota     ROUND_ZERO,
61*25f539f3SEmilio G. Cota     ROUND_DOWN,
62*25f539f3SEmilio G. Cota     ROUND_UP,
63*25f539f3SEmilio G. Cota     ROUND_TIEAWAY,
64*25f539f3SEmilio G. Cota     N_ROUND_MODES,
65*25f539f3SEmilio G. Cota };
66*25f539f3SEmilio G. Cota 
67*25f539f3SEmilio G. Cota static const char * const round_names[] = {
68*25f539f3SEmilio G. Cota     [ROUND_EVEN] = "even",
69*25f539f3SEmilio G. Cota     [ROUND_ZERO] = "zero",
70*25f539f3SEmilio G. Cota     [ROUND_DOWN] = "down",
71*25f539f3SEmilio G. Cota     [ROUND_UP] = "up",
72*25f539f3SEmilio G. Cota     [ROUND_TIEAWAY] = "tieaway",
73*25f539f3SEmilio G. Cota };
74*25f539f3SEmilio G. Cota 
75*25f539f3SEmilio G. Cota enum tester {
76*25f539f3SEmilio G. Cota     TESTER_SOFT,
77*25f539f3SEmilio G. Cota     TESTER_HOST,
78*25f539f3SEmilio G. Cota     TESTER_MAX_NR,
79*25f539f3SEmilio G. Cota };
80*25f539f3SEmilio G. Cota 
81*25f539f3SEmilio G. Cota static const char * const tester_names[] = {
82*25f539f3SEmilio G. Cota     [TESTER_SOFT] = "soft",
83*25f539f3SEmilio G. Cota     [TESTER_HOST] = "host",
84*25f539f3SEmilio G. Cota     [TESTER_MAX_NR] = NULL,
85*25f539f3SEmilio G. Cota };
86*25f539f3SEmilio G. Cota 
87*25f539f3SEmilio G. Cota union fp {
88*25f539f3SEmilio G. Cota     float f;
89*25f539f3SEmilio G. Cota     double d;
90*25f539f3SEmilio G. Cota     float32 f32;
91*25f539f3SEmilio G. Cota     float64 f64;
92*25f539f3SEmilio G. Cota     uint64_t u64;
93*25f539f3SEmilio G. Cota };
94*25f539f3SEmilio G. Cota 
95*25f539f3SEmilio G. Cota struct op_state;
96*25f539f3SEmilio G. Cota 
97*25f539f3SEmilio G. Cota typedef float (*float_func_t)(const struct op_state *s);
98*25f539f3SEmilio G. Cota typedef double (*double_func_t)(const struct op_state *s);
99*25f539f3SEmilio G. Cota 
100*25f539f3SEmilio G. Cota union fp_func {
101*25f539f3SEmilio G. Cota     float_func_t float_func;
102*25f539f3SEmilio G. Cota     double_func_t double_func;
103*25f539f3SEmilio G. Cota };
104*25f539f3SEmilio G. Cota 
105*25f539f3SEmilio G. Cota typedef void (*bench_func_t)(void);
106*25f539f3SEmilio G. Cota 
107*25f539f3SEmilio G. Cota struct op_desc {
108*25f539f3SEmilio G. Cota     const char * const name;
109*25f539f3SEmilio G. Cota };
110*25f539f3SEmilio G. Cota 
111*25f539f3SEmilio G. Cota #define DEFAULT_DURATION_SECS 1
112*25f539f3SEmilio G. Cota 
113*25f539f3SEmilio G. Cota static uint64_t random_ops[MAX_OPERANDS] = {
114*25f539f3SEmilio G. Cota     SEED_A, SEED_B, SEED_C,
115*25f539f3SEmilio G. Cota };
116*25f539f3SEmilio G. Cota static float_status soft_status;
117*25f539f3SEmilio G. Cota static enum precision precision;
118*25f539f3SEmilio G. Cota static enum op operation;
119*25f539f3SEmilio G. Cota static enum tester tester;
120*25f539f3SEmilio G. Cota static uint64_t n_completed_ops;
121*25f539f3SEmilio G. Cota static unsigned int duration = DEFAULT_DURATION_SECS;
122*25f539f3SEmilio G. Cota static int64_t ns_elapsed;
123*25f539f3SEmilio G. Cota /* disable optimizations with volatile */
124*25f539f3SEmilio G. Cota static volatile union fp res;
125*25f539f3SEmilio G. Cota 
126*25f539f3SEmilio G. Cota /*
127*25f539f3SEmilio G. Cota  * From: https://en.wikipedia.org/wiki/Xorshift
128*25f539f3SEmilio G. Cota  * This is faster than rand_r(), and gives us a wider range (RAND_MAX is only
129*25f539f3SEmilio G. Cota  * guaranteed to be >= INT_MAX).
130*25f539f3SEmilio G. Cota  */
131*25f539f3SEmilio G. Cota static uint64_t xorshift64star(uint64_t x)
132*25f539f3SEmilio G. Cota {
133*25f539f3SEmilio G. Cota     x ^= x >> 12; /* a */
134*25f539f3SEmilio G. Cota     x ^= x << 25; /* b */
135*25f539f3SEmilio G. Cota     x ^= x >> 27; /* c */
136*25f539f3SEmilio G. Cota     return x * UINT64_C(2685821657736338717);
137*25f539f3SEmilio G. Cota }
138*25f539f3SEmilio G. Cota 
139*25f539f3SEmilio G. Cota static void update_random_ops(int n_ops, enum precision prec)
140*25f539f3SEmilio G. Cota {
141*25f539f3SEmilio G. Cota     int i;
142*25f539f3SEmilio G. Cota 
143*25f539f3SEmilio G. Cota     for (i = 0; i < n_ops; i++) {
144*25f539f3SEmilio G. Cota         uint64_t r = random_ops[i];
145*25f539f3SEmilio G. Cota 
146*25f539f3SEmilio G. Cota         if (prec == PREC_SINGLE || PREC_FLOAT32) {
147*25f539f3SEmilio G. Cota             do {
148*25f539f3SEmilio G. Cota                 r = xorshift64star(r);
149*25f539f3SEmilio G. Cota             } while (!float32_is_normal(r));
150*25f539f3SEmilio G. Cota         } else if (prec == PREC_DOUBLE || PREC_FLOAT64) {
151*25f539f3SEmilio G. Cota             do {
152*25f539f3SEmilio G. Cota                 r = xorshift64star(r);
153*25f539f3SEmilio G. Cota             } while (!float64_is_normal(r));
154*25f539f3SEmilio G. Cota         } else {
155*25f539f3SEmilio G. Cota             g_assert_not_reached();
156*25f539f3SEmilio G. Cota         }
157*25f539f3SEmilio G. Cota         random_ops[i] = r;
158*25f539f3SEmilio G. Cota     }
159*25f539f3SEmilio G. Cota }
160*25f539f3SEmilio G. Cota 
161*25f539f3SEmilio G. Cota static void fill_random(union fp *ops, int n_ops, enum precision prec,
162*25f539f3SEmilio G. Cota                         bool no_neg)
163*25f539f3SEmilio G. Cota {
164*25f539f3SEmilio G. Cota     int i;
165*25f539f3SEmilio G. Cota 
166*25f539f3SEmilio G. Cota     for (i = 0; i < n_ops; i++) {
167*25f539f3SEmilio G. Cota         switch (prec) {
168*25f539f3SEmilio G. Cota         case PREC_SINGLE:
169*25f539f3SEmilio G. Cota         case PREC_FLOAT32:
170*25f539f3SEmilio G. Cota             ops[i].f32 = make_float32(random_ops[i]);
171*25f539f3SEmilio G. Cota             if (no_neg && float32_is_neg(ops[i].f32)) {
172*25f539f3SEmilio G. Cota                 ops[i].f32 = float32_chs(ops[i].f32);
173*25f539f3SEmilio G. Cota             }
174*25f539f3SEmilio G. Cota             /* raise the exponent to limit the frequency of denormal results */
175*25f539f3SEmilio G. Cota             ops[i].f32 |= 0x40000000;
176*25f539f3SEmilio G. Cota             break;
177*25f539f3SEmilio G. Cota         case PREC_DOUBLE:
178*25f539f3SEmilio G. Cota         case PREC_FLOAT64:
179*25f539f3SEmilio G. Cota             ops[i].f64 = make_float64(random_ops[i]);
180*25f539f3SEmilio G. Cota             if (no_neg && float64_is_neg(ops[i].f64)) {
181*25f539f3SEmilio G. Cota                 ops[i].f64 = float64_chs(ops[i].f64);
182*25f539f3SEmilio G. Cota             }
183*25f539f3SEmilio G. Cota             /* raise the exponent to limit the frequency of denormal results */
184*25f539f3SEmilio G. Cota             ops[i].f64 |= LIT64(0x4000000000000000);
185*25f539f3SEmilio G. Cota             break;
186*25f539f3SEmilio G. Cota         default:
187*25f539f3SEmilio G. Cota             g_assert_not_reached();
188*25f539f3SEmilio G. Cota         }
189*25f539f3SEmilio G. Cota     }
190*25f539f3SEmilio G. Cota }
191*25f539f3SEmilio G. Cota 
192*25f539f3SEmilio G. Cota /*
193*25f539f3SEmilio G. Cota  * The main benchmark function. Instead of (ab)using macros, we rely
194*25f539f3SEmilio G. Cota  * on the compiler to unfold this at compile-time.
195*25f539f3SEmilio G. Cota  */
196*25f539f3SEmilio G. Cota static void bench(enum precision prec, enum op op, int n_ops, bool no_neg)
197*25f539f3SEmilio G. Cota {
198*25f539f3SEmilio G. Cota     int64_t tf = get_clock() + duration * 1000000000LL;
199*25f539f3SEmilio G. Cota 
200*25f539f3SEmilio G. Cota     while (get_clock() < tf) {
201*25f539f3SEmilio G. Cota         union fp ops[MAX_OPERANDS];
202*25f539f3SEmilio G. Cota         int64_t t0;
203*25f539f3SEmilio G. Cota         int i;
204*25f539f3SEmilio G. Cota 
205*25f539f3SEmilio G. Cota         update_random_ops(n_ops, prec);
206*25f539f3SEmilio G. Cota         switch (prec) {
207*25f539f3SEmilio G. Cota         case PREC_SINGLE:
208*25f539f3SEmilio G. Cota             fill_random(ops, n_ops, prec, no_neg);
209*25f539f3SEmilio G. Cota             t0 = get_clock();
210*25f539f3SEmilio G. Cota             for (i = 0; i < OPS_PER_ITER; i++) {
211*25f539f3SEmilio G. Cota                 float a = ops[0].f;
212*25f539f3SEmilio G. Cota                 float b = ops[1].f;
213*25f539f3SEmilio G. Cota                 float c = ops[2].f;
214*25f539f3SEmilio G. Cota 
215*25f539f3SEmilio G. Cota                 switch (op) {
216*25f539f3SEmilio G. Cota                 case OP_ADD:
217*25f539f3SEmilio G. Cota                     res.f = a + b;
218*25f539f3SEmilio G. Cota                     break;
219*25f539f3SEmilio G. Cota                 case OP_SUB:
220*25f539f3SEmilio G. Cota                     res.f = a - b;
221*25f539f3SEmilio G. Cota                     break;
222*25f539f3SEmilio G. Cota                 case OP_MUL:
223*25f539f3SEmilio G. Cota                     res.f = a * b;
224*25f539f3SEmilio G. Cota                     break;
225*25f539f3SEmilio G. Cota                 case OP_DIV:
226*25f539f3SEmilio G. Cota                     res.f = a / b;
227*25f539f3SEmilio G. Cota                     break;
228*25f539f3SEmilio G. Cota                 case OP_FMA:
229*25f539f3SEmilio G. Cota                     res.f = fmaf(a, b, c);
230*25f539f3SEmilio G. Cota                     break;
231*25f539f3SEmilio G. Cota                 case OP_SQRT:
232*25f539f3SEmilio G. Cota                     res.f = sqrtf(a);
233*25f539f3SEmilio G. Cota                     break;
234*25f539f3SEmilio G. Cota                 case OP_CMP:
235*25f539f3SEmilio G. Cota                     res.u64 = isgreater(a, b);
236*25f539f3SEmilio G. Cota                     break;
237*25f539f3SEmilio G. Cota                 default:
238*25f539f3SEmilio G. Cota                     g_assert_not_reached();
239*25f539f3SEmilio G. Cota                 }
240*25f539f3SEmilio G. Cota             }
241*25f539f3SEmilio G. Cota             break;
242*25f539f3SEmilio G. Cota         case PREC_DOUBLE:
243*25f539f3SEmilio G. Cota             fill_random(ops, n_ops, prec, no_neg);
244*25f539f3SEmilio G. Cota             t0 = get_clock();
245*25f539f3SEmilio G. Cota             for (i = 0; i < OPS_PER_ITER; i++) {
246*25f539f3SEmilio G. Cota                 double a = ops[0].d;
247*25f539f3SEmilio G. Cota                 double b = ops[1].d;
248*25f539f3SEmilio G. Cota                 double c = ops[2].d;
249*25f539f3SEmilio G. Cota 
250*25f539f3SEmilio G. Cota                 switch (op) {
251*25f539f3SEmilio G. Cota                 case OP_ADD:
252*25f539f3SEmilio G. Cota                     res.d = a + b;
253*25f539f3SEmilio G. Cota                     break;
254*25f539f3SEmilio G. Cota                 case OP_SUB:
255*25f539f3SEmilio G. Cota                     res.d = a - b;
256*25f539f3SEmilio G. Cota                     break;
257*25f539f3SEmilio G. Cota                 case OP_MUL:
258*25f539f3SEmilio G. Cota                     res.d = a * b;
259*25f539f3SEmilio G. Cota                     break;
260*25f539f3SEmilio G. Cota                 case OP_DIV:
261*25f539f3SEmilio G. Cota                     res.d = a / b;
262*25f539f3SEmilio G. Cota                     break;
263*25f539f3SEmilio G. Cota                 case OP_FMA:
264*25f539f3SEmilio G. Cota                     res.d = fma(a, b, c);
265*25f539f3SEmilio G. Cota                     break;
266*25f539f3SEmilio G. Cota                 case OP_SQRT:
267*25f539f3SEmilio G. Cota                     res.d = sqrt(a);
268*25f539f3SEmilio G. Cota                     break;
269*25f539f3SEmilio G. Cota                 case OP_CMP:
270*25f539f3SEmilio G. Cota                     res.u64 = isgreater(a, b);
271*25f539f3SEmilio G. Cota                     break;
272*25f539f3SEmilio G. Cota                 default:
273*25f539f3SEmilio G. Cota                     g_assert_not_reached();
274*25f539f3SEmilio G. Cota                 }
275*25f539f3SEmilio G. Cota             }
276*25f539f3SEmilio G. Cota             break;
277*25f539f3SEmilio G. Cota         case PREC_FLOAT32:
278*25f539f3SEmilio G. Cota             fill_random(ops, n_ops, prec, no_neg);
279*25f539f3SEmilio G. Cota             t0 = get_clock();
280*25f539f3SEmilio G. Cota             for (i = 0; i < OPS_PER_ITER; i++) {
281*25f539f3SEmilio G. Cota                 float32 a = ops[0].f32;
282*25f539f3SEmilio G. Cota                 float32 b = ops[1].f32;
283*25f539f3SEmilio G. Cota                 float32 c = ops[2].f32;
284*25f539f3SEmilio G. Cota 
285*25f539f3SEmilio G. Cota                 switch (op) {
286*25f539f3SEmilio G. Cota                 case OP_ADD:
287*25f539f3SEmilio G. Cota                     res.f32 = float32_add(a, b, &soft_status);
288*25f539f3SEmilio G. Cota                     break;
289*25f539f3SEmilio G. Cota                 case OP_SUB:
290*25f539f3SEmilio G. Cota                     res.f32 = float32_sub(a, b, &soft_status);
291*25f539f3SEmilio G. Cota                     break;
292*25f539f3SEmilio G. Cota                 case OP_MUL:
293*25f539f3SEmilio G. Cota                     res.f = float32_mul(a, b, &soft_status);
294*25f539f3SEmilio G. Cota                     break;
295*25f539f3SEmilio G. Cota                 case OP_DIV:
296*25f539f3SEmilio G. Cota                     res.f32 = float32_div(a, b, &soft_status);
297*25f539f3SEmilio G. Cota                     break;
298*25f539f3SEmilio G. Cota                 case OP_FMA:
299*25f539f3SEmilio G. Cota                     res.f32 = float32_muladd(a, b, c, 0, &soft_status);
300*25f539f3SEmilio G. Cota                     break;
301*25f539f3SEmilio G. Cota                 case OP_SQRT:
302*25f539f3SEmilio G. Cota                     res.f32 = float32_sqrt(a, &soft_status);
303*25f539f3SEmilio G. Cota                     break;
304*25f539f3SEmilio G. Cota                 case OP_CMP:
305*25f539f3SEmilio G. Cota                     res.u64 = float32_compare_quiet(a, b, &soft_status);
306*25f539f3SEmilio G. Cota                     break;
307*25f539f3SEmilio G. Cota                 default:
308*25f539f3SEmilio G. Cota                     g_assert_not_reached();
309*25f539f3SEmilio G. Cota                 }
310*25f539f3SEmilio G. Cota             }
311*25f539f3SEmilio G. Cota             break;
312*25f539f3SEmilio G. Cota         case PREC_FLOAT64:
313*25f539f3SEmilio G. Cota             fill_random(ops, n_ops, prec, no_neg);
314*25f539f3SEmilio G. Cota             t0 = get_clock();
315*25f539f3SEmilio G. Cota             for (i = 0; i < OPS_PER_ITER; i++) {
316*25f539f3SEmilio G. Cota                 float64 a = ops[0].f64;
317*25f539f3SEmilio G. Cota                 float64 b = ops[1].f64;
318*25f539f3SEmilio G. Cota                 float64 c = ops[2].f64;
319*25f539f3SEmilio G. Cota 
320*25f539f3SEmilio G. Cota                 switch (op) {
321*25f539f3SEmilio G. Cota                 case OP_ADD:
322*25f539f3SEmilio G. Cota                     res.f64 = float64_add(a, b, &soft_status);
323*25f539f3SEmilio G. Cota                     break;
324*25f539f3SEmilio G. Cota                 case OP_SUB:
325*25f539f3SEmilio G. Cota                     res.f64 = float64_sub(a, b, &soft_status);
326*25f539f3SEmilio G. Cota                     break;
327*25f539f3SEmilio G. Cota                 case OP_MUL:
328*25f539f3SEmilio G. Cota                     res.f = float64_mul(a, b, &soft_status);
329*25f539f3SEmilio G. Cota                     break;
330*25f539f3SEmilio G. Cota                 case OP_DIV:
331*25f539f3SEmilio G. Cota                     res.f64 = float64_div(a, b, &soft_status);
332*25f539f3SEmilio G. Cota                     break;
333*25f539f3SEmilio G. Cota                 case OP_FMA:
334*25f539f3SEmilio G. Cota                     res.f64 = float64_muladd(a, b, c, 0, &soft_status);
335*25f539f3SEmilio G. Cota                     break;
336*25f539f3SEmilio G. Cota                 case OP_SQRT:
337*25f539f3SEmilio G. Cota                     res.f64 = float64_sqrt(a, &soft_status);
338*25f539f3SEmilio G. Cota                     break;
339*25f539f3SEmilio G. Cota                 case OP_CMP:
340*25f539f3SEmilio G. Cota                     res.u64 = float64_compare_quiet(a, b, &soft_status);
341*25f539f3SEmilio G. Cota                     break;
342*25f539f3SEmilio G. Cota                 default:
343*25f539f3SEmilio G. Cota                     g_assert_not_reached();
344*25f539f3SEmilio G. Cota                 }
345*25f539f3SEmilio G. Cota             }
346*25f539f3SEmilio G. Cota             break;
347*25f539f3SEmilio G. Cota         default:
348*25f539f3SEmilio G. Cota             g_assert_not_reached();
349*25f539f3SEmilio G. Cota         }
350*25f539f3SEmilio G. Cota         ns_elapsed += get_clock() - t0;
351*25f539f3SEmilio G. Cota         n_completed_ops += OPS_PER_ITER;
352*25f539f3SEmilio G. Cota     }
353*25f539f3SEmilio G. Cota }
354*25f539f3SEmilio G. Cota 
355*25f539f3SEmilio G. Cota #define GEN_BENCH(name, type, prec, op, n_ops)          \
356*25f539f3SEmilio G. Cota     static void __attribute__((flatten)) name(void)     \
357*25f539f3SEmilio G. Cota     {                                                   \
358*25f539f3SEmilio G. Cota         bench(prec, op, n_ops, false);                  \
359*25f539f3SEmilio G. Cota     }
360*25f539f3SEmilio G. Cota 
361*25f539f3SEmilio G. Cota #define GEN_BENCH_NO_NEG(name, type, prec, op, n_ops)   \
362*25f539f3SEmilio G. Cota     static void __attribute__((flatten)) name(void)     \
363*25f539f3SEmilio G. Cota     {                                                   \
364*25f539f3SEmilio G. Cota         bench(prec, op, n_ops, true);                   \
365*25f539f3SEmilio G. Cota     }
366*25f539f3SEmilio G. Cota 
367*25f539f3SEmilio G. Cota #define GEN_BENCH_ALL_TYPES(opname, op, n_ops)                          \
368*25f539f3SEmilio G. Cota     GEN_BENCH(bench_ ## opname ## _float, float, PREC_SINGLE, op, n_ops) \
369*25f539f3SEmilio G. Cota     GEN_BENCH(bench_ ## opname ## _double, double, PREC_DOUBLE, op, n_ops) \
370*25f539f3SEmilio G. Cota     GEN_BENCH(bench_ ## opname ## _float32, float32, PREC_FLOAT32, op, n_ops) \
371*25f539f3SEmilio G. Cota     GEN_BENCH(bench_ ## opname ## _float64, float64, PREC_FLOAT64, op, n_ops)
372*25f539f3SEmilio G. Cota 
373*25f539f3SEmilio G. Cota GEN_BENCH_ALL_TYPES(add, OP_ADD, 2)
374*25f539f3SEmilio G. Cota GEN_BENCH_ALL_TYPES(sub, OP_SUB, 2)
375*25f539f3SEmilio G. Cota GEN_BENCH_ALL_TYPES(mul, OP_MUL, 2)
376*25f539f3SEmilio G. Cota GEN_BENCH_ALL_TYPES(div, OP_DIV, 2)
377*25f539f3SEmilio G. Cota GEN_BENCH_ALL_TYPES(fma, OP_FMA, 3)
378*25f539f3SEmilio G. Cota GEN_BENCH_ALL_TYPES(cmp, OP_CMP, 2)
379*25f539f3SEmilio G. Cota #undef GEN_BENCH_ALL_TYPES
380*25f539f3SEmilio G. Cota 
381*25f539f3SEmilio G. Cota #define GEN_BENCH_ALL_TYPES_NO_NEG(name, op, n)                         \
382*25f539f3SEmilio G. Cota     GEN_BENCH_NO_NEG(bench_ ## name ## _float, float, PREC_SINGLE, op, n) \
383*25f539f3SEmilio G. Cota     GEN_BENCH_NO_NEG(bench_ ## name ## _double, double, PREC_DOUBLE, op, n) \
384*25f539f3SEmilio G. Cota     GEN_BENCH_NO_NEG(bench_ ## name ## _float32, float32, PREC_FLOAT32, op, n) \
385*25f539f3SEmilio G. Cota     GEN_BENCH_NO_NEG(bench_ ## name ## _float64, float64, PREC_FLOAT64, op, n)
386*25f539f3SEmilio G. Cota 
387*25f539f3SEmilio G. Cota GEN_BENCH_ALL_TYPES_NO_NEG(sqrt, OP_SQRT, 1)
388*25f539f3SEmilio G. Cota #undef GEN_BENCH_ALL_TYPES_NO_NEG
389*25f539f3SEmilio G. Cota 
390*25f539f3SEmilio G. Cota #undef GEN_BENCH_NO_NEG
391*25f539f3SEmilio G. Cota #undef GEN_BENCH
392*25f539f3SEmilio G. Cota 
393*25f539f3SEmilio G. Cota #define GEN_BENCH_FUNCS(opname, op)                             \
394*25f539f3SEmilio G. Cota     [op] = {                                                    \
395*25f539f3SEmilio G. Cota         [PREC_SINGLE]    = bench_ ## opname ## _float,          \
396*25f539f3SEmilio G. Cota         [PREC_DOUBLE]    = bench_ ## opname ## _double,         \
397*25f539f3SEmilio G. Cota         [PREC_FLOAT32]   = bench_ ## opname ## _float32,        \
398*25f539f3SEmilio G. Cota         [PREC_FLOAT64]   = bench_ ## opname ## _float64,        \
399*25f539f3SEmilio G. Cota     }
400*25f539f3SEmilio G. Cota 
401*25f539f3SEmilio G. Cota static const bench_func_t bench_funcs[OP_MAX_NR][PREC_MAX_NR] = {
402*25f539f3SEmilio G. Cota     GEN_BENCH_FUNCS(add, OP_ADD),
403*25f539f3SEmilio G. Cota     GEN_BENCH_FUNCS(sub, OP_SUB),
404*25f539f3SEmilio G. Cota     GEN_BENCH_FUNCS(mul, OP_MUL),
405*25f539f3SEmilio G. Cota     GEN_BENCH_FUNCS(div, OP_DIV),
406*25f539f3SEmilio G. Cota     GEN_BENCH_FUNCS(fma, OP_FMA),
407*25f539f3SEmilio G. Cota     GEN_BENCH_FUNCS(sqrt, OP_SQRT),
408*25f539f3SEmilio G. Cota     GEN_BENCH_FUNCS(cmp, OP_CMP),
409*25f539f3SEmilio G. Cota };
410*25f539f3SEmilio G. Cota 
411*25f539f3SEmilio G. Cota #undef GEN_BENCH_FUNCS
412*25f539f3SEmilio G. Cota 
413*25f539f3SEmilio G. Cota static void run_bench(void)
414*25f539f3SEmilio G. Cota {
415*25f539f3SEmilio G. Cota     bench_func_t f;
416*25f539f3SEmilio G. Cota 
417*25f539f3SEmilio G. Cota     f = bench_funcs[operation][precision];
418*25f539f3SEmilio G. Cota     g_assert(f);
419*25f539f3SEmilio G. Cota     f();
420*25f539f3SEmilio G. Cota }
421*25f539f3SEmilio G. Cota 
422*25f539f3SEmilio G. Cota /* @arr must be NULL-terminated */
423*25f539f3SEmilio G. Cota static int find_name(const char * const *arr, const char *name)
424*25f539f3SEmilio G. Cota {
425*25f539f3SEmilio G. Cota     int i;
426*25f539f3SEmilio G. Cota 
427*25f539f3SEmilio G. Cota     for (i = 0; arr[i] != NULL; i++) {
428*25f539f3SEmilio G. Cota         if (strcmp(name, arr[i]) == 0) {
429*25f539f3SEmilio G. Cota             return i;
430*25f539f3SEmilio G. Cota         }
431*25f539f3SEmilio G. Cota     }
432*25f539f3SEmilio G. Cota     return -1;
433*25f539f3SEmilio G. Cota }
434*25f539f3SEmilio G. Cota 
435*25f539f3SEmilio G. Cota static void usage_complete(int argc, char *argv[])
436*25f539f3SEmilio G. Cota {
437*25f539f3SEmilio G. Cota     gchar *op_list = g_strjoinv(", ", (gchar **)op_names);
438*25f539f3SEmilio G. Cota     gchar *tester_list = g_strjoinv(", ", (gchar **)tester_names);
439*25f539f3SEmilio G. Cota 
440*25f539f3SEmilio G. Cota     fprintf(stderr, "Usage: %s [options]\n", argv[0]);
441*25f539f3SEmilio G. Cota     fprintf(stderr, "options:\n");
442*25f539f3SEmilio G. Cota     fprintf(stderr, " -d = duration, in seconds. Default: %d\n",
443*25f539f3SEmilio G. Cota             DEFAULT_DURATION_SECS);
444*25f539f3SEmilio G. Cota     fprintf(stderr, " -h = show this help message.\n");
445*25f539f3SEmilio G. Cota     fprintf(stderr, " -o = floating point operation (%s). Default: %s\n",
446*25f539f3SEmilio G. Cota             op_list, op_names[0]);
447*25f539f3SEmilio G. Cota     fprintf(stderr, " -p = floating point precision (single, double). "
448*25f539f3SEmilio G. Cota             "Default: single\n");
449*25f539f3SEmilio G. Cota     fprintf(stderr, " -r = rounding mode (even, zero, down, up, tieaway). "
450*25f539f3SEmilio G. Cota             "Default: even\n");
451*25f539f3SEmilio G. Cota     fprintf(stderr, " -t = tester (%s). Default: %s\n",
452*25f539f3SEmilio G. Cota             tester_list, tester_names[0]);
453*25f539f3SEmilio G. Cota     fprintf(stderr, " -z = flush inputs to zero (soft tester only). "
454*25f539f3SEmilio G. Cota             "Default: disabled\n");
455*25f539f3SEmilio G. Cota     fprintf(stderr, " -Z = flush output to zero (soft tester only). "
456*25f539f3SEmilio G. Cota             "Default: disabled\n");
457*25f539f3SEmilio G. Cota 
458*25f539f3SEmilio G. Cota     g_free(tester_list);
459*25f539f3SEmilio G. Cota     g_free(op_list);
460*25f539f3SEmilio G. Cota }
461*25f539f3SEmilio G. Cota 
462*25f539f3SEmilio G. Cota static int round_name_to_mode(const char *name)
463*25f539f3SEmilio G. Cota {
464*25f539f3SEmilio G. Cota     int i;
465*25f539f3SEmilio G. Cota 
466*25f539f3SEmilio G. Cota     for (i = 0; i < N_ROUND_MODES; i++) {
467*25f539f3SEmilio G. Cota         if (!strcmp(round_names[i], name)) {
468*25f539f3SEmilio G. Cota             return i;
469*25f539f3SEmilio G. Cota         }
470*25f539f3SEmilio G. Cota     }
471*25f539f3SEmilio G. Cota     return -1;
472*25f539f3SEmilio G. Cota }
473*25f539f3SEmilio G. Cota 
474*25f539f3SEmilio G. Cota static void QEMU_NORETURN die_host_rounding(enum rounding rounding)
475*25f539f3SEmilio G. Cota {
476*25f539f3SEmilio G. Cota     fprintf(stderr, "fatal: '%s' rounding not supported on this host\n",
477*25f539f3SEmilio G. Cota             round_names[rounding]);
478*25f539f3SEmilio G. Cota     exit(EXIT_FAILURE);
479*25f539f3SEmilio G. Cota }
480*25f539f3SEmilio G. Cota 
481*25f539f3SEmilio G. Cota static void set_host_precision(enum rounding rounding)
482*25f539f3SEmilio G. Cota {
483*25f539f3SEmilio G. Cota     int rhost;
484*25f539f3SEmilio G. Cota 
485*25f539f3SEmilio G. Cota     switch (rounding) {
486*25f539f3SEmilio G. Cota     case ROUND_EVEN:
487*25f539f3SEmilio G. Cota         rhost = FE_TONEAREST;
488*25f539f3SEmilio G. Cota         break;
489*25f539f3SEmilio G. Cota     case ROUND_ZERO:
490*25f539f3SEmilio G. Cota         rhost = FE_TOWARDZERO;
491*25f539f3SEmilio G. Cota         break;
492*25f539f3SEmilio G. Cota     case ROUND_DOWN:
493*25f539f3SEmilio G. Cota         rhost = FE_DOWNWARD;
494*25f539f3SEmilio G. Cota         break;
495*25f539f3SEmilio G. Cota     case ROUND_UP:
496*25f539f3SEmilio G. Cota         rhost = FE_UPWARD;
497*25f539f3SEmilio G. Cota         break;
498*25f539f3SEmilio G. Cota     case ROUND_TIEAWAY:
499*25f539f3SEmilio G. Cota         die_host_rounding(rounding);
500*25f539f3SEmilio G. Cota         return;
501*25f539f3SEmilio G. Cota     default:
502*25f539f3SEmilio G. Cota         g_assert_not_reached();
503*25f539f3SEmilio G. Cota     }
504*25f539f3SEmilio G. Cota 
505*25f539f3SEmilio G. Cota     if (fesetround(rhost)) {
506*25f539f3SEmilio G. Cota         die_host_rounding(rounding);
507*25f539f3SEmilio G. Cota     }
508*25f539f3SEmilio G. Cota }
509*25f539f3SEmilio G. Cota 
510*25f539f3SEmilio G. Cota static void set_soft_precision(enum rounding rounding)
511*25f539f3SEmilio G. Cota {
512*25f539f3SEmilio G. Cota     signed char mode;
513*25f539f3SEmilio G. Cota 
514*25f539f3SEmilio G. Cota     switch (rounding) {
515*25f539f3SEmilio G. Cota     case ROUND_EVEN:
516*25f539f3SEmilio G. Cota         mode = float_round_nearest_even;
517*25f539f3SEmilio G. Cota         break;
518*25f539f3SEmilio G. Cota     case ROUND_ZERO:
519*25f539f3SEmilio G. Cota         mode = float_round_to_zero;
520*25f539f3SEmilio G. Cota         break;
521*25f539f3SEmilio G. Cota     case ROUND_DOWN:
522*25f539f3SEmilio G. Cota         mode = float_round_down;
523*25f539f3SEmilio G. Cota         break;
524*25f539f3SEmilio G. Cota     case ROUND_UP:
525*25f539f3SEmilio G. Cota         mode = float_round_up;
526*25f539f3SEmilio G. Cota         break;
527*25f539f3SEmilio G. Cota     case ROUND_TIEAWAY:
528*25f539f3SEmilio G. Cota         mode = float_round_ties_away;
529*25f539f3SEmilio G. Cota         break;
530*25f539f3SEmilio G. Cota     default:
531*25f539f3SEmilio G. Cota         g_assert_not_reached();
532*25f539f3SEmilio G. Cota     }
533*25f539f3SEmilio G. Cota     soft_status.float_rounding_mode = mode;
534*25f539f3SEmilio G. Cota }
535*25f539f3SEmilio G. Cota 
536*25f539f3SEmilio G. Cota static void parse_args(int argc, char *argv[])
537*25f539f3SEmilio G. Cota {
538*25f539f3SEmilio G. Cota     int c;
539*25f539f3SEmilio G. Cota     int val;
540*25f539f3SEmilio G. Cota     int rounding = ROUND_EVEN;
541*25f539f3SEmilio G. Cota 
542*25f539f3SEmilio G. Cota     for (;;) {
543*25f539f3SEmilio G. Cota         c = getopt(argc, argv, "d:ho:p:r:t:zZ");
544*25f539f3SEmilio G. Cota         if (c < 0) {
545*25f539f3SEmilio G. Cota             break;
546*25f539f3SEmilio G. Cota         }
547*25f539f3SEmilio G. Cota         switch (c) {
548*25f539f3SEmilio G. Cota         case 'd':
549*25f539f3SEmilio G. Cota             duration = atoi(optarg);
550*25f539f3SEmilio G. Cota             break;
551*25f539f3SEmilio G. Cota         case 'h':
552*25f539f3SEmilio G. Cota             usage_complete(argc, argv);
553*25f539f3SEmilio G. Cota             exit(EXIT_SUCCESS);
554*25f539f3SEmilio G. Cota         case 'o':
555*25f539f3SEmilio G. Cota             val = find_name(op_names, optarg);
556*25f539f3SEmilio G. Cota             if (val < 0) {
557*25f539f3SEmilio G. Cota                 fprintf(stderr, "Unsupported op '%s'\n", optarg);
558*25f539f3SEmilio G. Cota                 exit(EXIT_FAILURE);
559*25f539f3SEmilio G. Cota             }
560*25f539f3SEmilio G. Cota             operation = val;
561*25f539f3SEmilio G. Cota             break;
562*25f539f3SEmilio G. Cota         case 'p':
563*25f539f3SEmilio G. Cota             if (!strcmp(optarg, "single")) {
564*25f539f3SEmilio G. Cota                 precision = PREC_SINGLE;
565*25f539f3SEmilio G. Cota             } else if (!strcmp(optarg, "double")) {
566*25f539f3SEmilio G. Cota                 precision = PREC_DOUBLE;
567*25f539f3SEmilio G. Cota             } else {
568*25f539f3SEmilio G. Cota                 fprintf(stderr, "Unsupported precision '%s'\n", optarg);
569*25f539f3SEmilio G. Cota                 exit(EXIT_FAILURE);
570*25f539f3SEmilio G. Cota             }
571*25f539f3SEmilio G. Cota             break;
572*25f539f3SEmilio G. Cota         case 'r':
573*25f539f3SEmilio G. Cota             rounding = round_name_to_mode(optarg);
574*25f539f3SEmilio G. Cota             if (rounding < 0) {
575*25f539f3SEmilio G. Cota                 fprintf(stderr, "fatal: invalid rounding mode '%s'\n", optarg);
576*25f539f3SEmilio G. Cota                 exit(EXIT_FAILURE);
577*25f539f3SEmilio G. Cota             }
578*25f539f3SEmilio G. Cota             break;
579*25f539f3SEmilio G. Cota         case 't':
580*25f539f3SEmilio G. Cota             val = find_name(tester_names, optarg);
581*25f539f3SEmilio G. Cota             if (val < 0) {
582*25f539f3SEmilio G. Cota                 fprintf(stderr, "Unsupported tester '%s'\n", optarg);
583*25f539f3SEmilio G. Cota                 exit(EXIT_FAILURE);
584*25f539f3SEmilio G. Cota             }
585*25f539f3SEmilio G. Cota             tester = val;
586*25f539f3SEmilio G. Cota             break;
587*25f539f3SEmilio G. Cota         case 'z':
588*25f539f3SEmilio G. Cota             soft_status.flush_inputs_to_zero = 1;
589*25f539f3SEmilio G. Cota             break;
590*25f539f3SEmilio G. Cota         case 'Z':
591*25f539f3SEmilio G. Cota             soft_status.flush_to_zero = 1;
592*25f539f3SEmilio G. Cota             break;
593*25f539f3SEmilio G. Cota         }
594*25f539f3SEmilio G. Cota     }
595*25f539f3SEmilio G. Cota 
596*25f539f3SEmilio G. Cota     /* set precision and rounding mode based on the tester */
597*25f539f3SEmilio G. Cota     switch (tester) {
598*25f539f3SEmilio G. Cota     case TESTER_HOST:
599*25f539f3SEmilio G. Cota         set_host_precision(rounding);
600*25f539f3SEmilio G. Cota         break;
601*25f539f3SEmilio G. Cota     case TESTER_SOFT:
602*25f539f3SEmilio G. Cota         set_soft_precision(rounding);
603*25f539f3SEmilio G. Cota         switch (precision) {
604*25f539f3SEmilio G. Cota         case PREC_SINGLE:
605*25f539f3SEmilio G. Cota             precision = PREC_FLOAT32;
606*25f539f3SEmilio G. Cota             break;
607*25f539f3SEmilio G. Cota         case PREC_DOUBLE:
608*25f539f3SEmilio G. Cota             precision = PREC_FLOAT64;
609*25f539f3SEmilio G. Cota             break;
610*25f539f3SEmilio G. Cota         default:
611*25f539f3SEmilio G. Cota             g_assert_not_reached();
612*25f539f3SEmilio G. Cota         }
613*25f539f3SEmilio G. Cota         break;
614*25f539f3SEmilio G. Cota     default:
615*25f539f3SEmilio G. Cota         g_assert_not_reached();
616*25f539f3SEmilio G. Cota     }
617*25f539f3SEmilio G. Cota }
618*25f539f3SEmilio G. Cota 
619*25f539f3SEmilio G. Cota static void pr_stats(void)
620*25f539f3SEmilio G. Cota {
621*25f539f3SEmilio G. Cota     printf("%.2f MFlops\n", (double)n_completed_ops / ns_elapsed * 1e3);
622*25f539f3SEmilio G. Cota }
623*25f539f3SEmilio G. Cota 
624*25f539f3SEmilio G. Cota int main(int argc, char *argv[])
625*25f539f3SEmilio G. Cota {
626*25f539f3SEmilio G. Cota     parse_args(argc, argv);
627*25f539f3SEmilio G. Cota     run_bench();
628*25f539f3SEmilio G. Cota     pr_stats();
629*25f539f3SEmilio G. Cota     return 0;
630*25f539f3SEmilio G. Cota }
631