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