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