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