mirror of
				https://github.com/qemu/qemu.git
				synced 2025-10-31 04:06:46 +00:00 
			
		
		
		
	 8905770b27
			
		
	
	
		8905770b27
		
	
	
	
	
		
			
			G_NORETURN was introduced in glib 2.68, fallback to G_GNUC_NORETURN in glib-compat. Note that this attribute must be placed before the function declaration (bringing a bit of consistency in qemu codebase usage). Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Reviewed-by: Warner Losh <imp@bsdimp.com> Message-Id: <20220420132624.2439741-20-marcandre.lureau@redhat.com>
		
			
				
	
	
		
			711 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			711 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * fp-bench.c - A collection of simple floating point microbenchmarks.
 | |
|  *
 | |
|  * Copyright (C) 2018, Emilio G. Cota <cota@braap.org>
 | |
|  *
 | |
|  * License: GNU GPL, version 2 or later.
 | |
|  *   See the COPYING file in the top-level directory.
 | |
|  */
 | |
| #ifndef HW_POISON_H
 | |
| #error Must define HW_POISON_H to work around TARGET_* poisoning
 | |
| #endif
 | |
| 
 | |
| #include "qemu/osdep.h"
 | |
| #include <math.h>
 | |
| #include <fenv.h>
 | |
| #include "qemu/timer.h"
 | |
| #include "qemu/int128.h"
 | |
| #include "fpu/softfloat.h"
 | |
| 
 | |
| /* amortize the computation of random inputs */
 | |
| #define OPS_PER_ITER     50000
 | |
| 
 | |
| #define MAX_OPERANDS 3
 | |
| 
 | |
| #define SEED_A 0xdeadfacedeadface
 | |
| #define SEED_B 0xbadc0feebadc0fee
 | |
| #define SEED_C 0xbeefdeadbeefdead
 | |
| 
 | |
| enum op {
 | |
|     OP_ADD,
 | |
|     OP_SUB,
 | |
|     OP_MUL,
 | |
|     OP_DIV,
 | |
|     OP_FMA,
 | |
|     OP_SQRT,
 | |
|     OP_CMP,
 | |
|     OP_MAX_NR,
 | |
| };
 | |
| 
 | |
| static const char * const op_names[] = {
 | |
|     [OP_ADD] = "add",
 | |
|     [OP_SUB] = "sub",
 | |
|     [OP_MUL] = "mul",
 | |
|     [OP_DIV] = "div",
 | |
|     [OP_FMA] = "mulAdd",
 | |
|     [OP_SQRT] = "sqrt",
 | |
|     [OP_CMP] = "cmp",
 | |
|     [OP_MAX_NR] = NULL,
 | |
| };
 | |
| 
 | |
| enum precision {
 | |
|     PREC_SINGLE,
 | |
|     PREC_DOUBLE,
 | |
|     PREC_QUAD,
 | |
|     PREC_FLOAT32,
 | |
|     PREC_FLOAT64,
 | |
|     PREC_FLOAT128,
 | |
|     PREC_MAX_NR,
 | |
| };
 | |
| 
 | |
| enum rounding {
 | |
|     ROUND_EVEN,
 | |
|     ROUND_ZERO,
 | |
|     ROUND_DOWN,
 | |
|     ROUND_UP,
 | |
|     ROUND_TIEAWAY,
 | |
|     N_ROUND_MODES,
 | |
| };
 | |
| 
 | |
| static const char * const round_names[] = {
 | |
|     [ROUND_EVEN] = "even",
 | |
|     [ROUND_ZERO] = "zero",
 | |
|     [ROUND_DOWN] = "down",
 | |
|     [ROUND_UP] = "up",
 | |
|     [ROUND_TIEAWAY] = "tieaway",
 | |
| };
 | |
| 
 | |
| enum tester {
 | |
|     TESTER_SOFT,
 | |
|     TESTER_HOST,
 | |
|     TESTER_MAX_NR,
 | |
| };
 | |
| 
 | |
| static const char * const tester_names[] = {
 | |
|     [TESTER_SOFT] = "soft",
 | |
|     [TESTER_HOST] = "host",
 | |
|     [TESTER_MAX_NR] = NULL,
 | |
| };
 | |
| 
 | |
| union fp {
 | |
|     float f;
 | |
|     double d;
 | |
|     float32 f32;
 | |
|     float64 f64;
 | |
|     float128 f128;
 | |
|     uint64_t u64;
 | |
| };
 | |
| 
 | |
| struct op_state;
 | |
| 
 | |
| typedef float (*float_func_t)(const struct op_state *s);
 | |
| typedef double (*double_func_t)(const struct op_state *s);
 | |
| 
 | |
| union fp_func {
 | |
|     float_func_t float_func;
 | |
|     double_func_t double_func;
 | |
| };
 | |
| 
 | |
| typedef void (*bench_func_t)(void);
 | |
| 
 | |
| struct op_desc {
 | |
|     const char * const name;
 | |
| };
 | |
| 
 | |
| #define DEFAULT_DURATION_SECS 1
 | |
| 
 | |
| static uint64_t random_ops[MAX_OPERANDS] = {
 | |
|     SEED_A, SEED_B, SEED_C,
 | |
| };
 | |
| 
 | |
| static float128 random_quad_ops[MAX_OPERANDS] = {
 | |
|     {SEED_A, SEED_B}, {SEED_B, SEED_C}, {SEED_C, SEED_A},
 | |
| };
 | |
| static float_status soft_status;
 | |
| static enum precision precision;
 | |
| static enum op operation;
 | |
| static enum tester tester;
 | |
| static uint64_t n_completed_ops;
 | |
| static unsigned int duration = DEFAULT_DURATION_SECS;
 | |
| static int64_t ns_elapsed;
 | |
| /* disable optimizations with volatile */
 | |
| static volatile union fp res;
 | |
| 
 | |
| /*
 | |
|  * From: https://en.wikipedia.org/wiki/Xorshift
 | |
|  * This is faster than rand_r(), and gives us a wider range (RAND_MAX is only
 | |
|  * guaranteed to be >= INT_MAX).
 | |
|  */
 | |
| static uint64_t xorshift64star(uint64_t x)
 | |
| {
 | |
|     x ^= x >> 12; /* a */
 | |
|     x ^= x << 25; /* b */
 | |
|     x ^= x >> 27; /* c */
 | |
|     return x * UINT64_C(2685821657736338717);
 | |
| }
 | |
| 
 | |
| static void update_random_ops(int n_ops, enum precision prec)
 | |
| {
 | |
|     int i;
 | |
| 
 | |
|     for (i = 0; i < n_ops; i++) {
 | |
| 
 | |
|         switch (prec) {
 | |
|         case PREC_SINGLE:
 | |
|         case PREC_FLOAT32:
 | |
|         {
 | |
|             uint64_t r = random_ops[i];
 | |
|             do {
 | |
|                 r = xorshift64star(r);
 | |
|             } while (!float32_is_normal(r));
 | |
|             random_ops[i] = r;
 | |
|             break;
 | |
|         }
 | |
|         case PREC_DOUBLE:
 | |
|         case PREC_FLOAT64:
 | |
|         {
 | |
|             uint64_t r = random_ops[i];
 | |
|             do {
 | |
|                 r = xorshift64star(r);
 | |
|             } while (!float64_is_normal(r));
 | |
|             random_ops[i] = r;
 | |
|             break;
 | |
|         }
 | |
|         case PREC_QUAD:
 | |
|         case PREC_FLOAT128:
 | |
|         {
 | |
|             float128 r = random_quad_ops[i];
 | |
|             uint64_t hi = r.high;
 | |
|             uint64_t lo = r.low;
 | |
|             do {
 | |
|                 hi = xorshift64star(hi);
 | |
|                 lo = xorshift64star(lo);
 | |
|                 r = make_float128(hi, lo);
 | |
|             } while (!float128_is_normal(r));
 | |
|             random_quad_ops[i] = r;
 | |
|             break;
 | |
|         }
 | |
|         default:
 | |
|             g_assert_not_reached();
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void fill_random(union fp *ops, int n_ops, enum precision prec,
 | |
|                         bool no_neg)
 | |
| {
 | |
|     int i;
 | |
| 
 | |
|     for (i = 0; i < n_ops; i++) {
 | |
|         switch (prec) {
 | |
|         case PREC_SINGLE:
 | |
|         case PREC_FLOAT32:
 | |
|             ops[i].f32 = make_float32(random_ops[i]);
 | |
|             if (no_neg && float32_is_neg(ops[i].f32)) {
 | |
|                 ops[i].f32 = float32_chs(ops[i].f32);
 | |
|             }
 | |
|             break;
 | |
|         case PREC_DOUBLE:
 | |
|         case PREC_FLOAT64:
 | |
|             ops[i].f64 = make_float64(random_ops[i]);
 | |
|             if (no_neg && float64_is_neg(ops[i].f64)) {
 | |
|                 ops[i].f64 = float64_chs(ops[i].f64);
 | |
|             }
 | |
|             break;
 | |
|         case PREC_QUAD:
 | |
|         case PREC_FLOAT128:
 | |
|             ops[i].f128 = random_quad_ops[i];
 | |
|             if (no_neg && float128_is_neg(ops[i].f128)) {
 | |
|                 ops[i].f128 = float128_chs(ops[i].f128);
 | |
|             }
 | |
|             break;
 | |
|         default:
 | |
|             g_assert_not_reached();
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * The main benchmark function. Instead of (ab)using macros, we rely
 | |
|  * on the compiler to unfold this at compile-time.
 | |
|  */
 | |
| static void bench(enum precision prec, enum op op, int n_ops, bool no_neg)
 | |
| {
 | |
|     int64_t tf = get_clock() + duration * 1000000000LL;
 | |
| 
 | |
|     while (get_clock() < tf) {
 | |
|         union fp ops[MAX_OPERANDS];
 | |
|         int64_t t0;
 | |
|         int i;
 | |
| 
 | |
|         update_random_ops(n_ops, prec);
 | |
|         switch (prec) {
 | |
|         case PREC_SINGLE:
 | |
|             fill_random(ops, n_ops, prec, no_neg);
 | |
|             t0 = get_clock();
 | |
|             for (i = 0; i < OPS_PER_ITER; i++) {
 | |
|                 float a = ops[0].f;
 | |
|                 float b = ops[1].f;
 | |
|                 float c = ops[2].f;
 | |
| 
 | |
|                 switch (op) {
 | |
|                 case OP_ADD:
 | |
|                     res.f = a + b;
 | |
|                     break;
 | |
|                 case OP_SUB:
 | |
|                     res.f = a - b;
 | |
|                     break;
 | |
|                 case OP_MUL:
 | |
|                     res.f = a * b;
 | |
|                     break;
 | |
|                 case OP_DIV:
 | |
|                     res.f = a / b;
 | |
|                     break;
 | |
|                 case OP_FMA:
 | |
|                     res.f = fmaf(a, b, c);
 | |
|                     break;
 | |
|                 case OP_SQRT:
 | |
|                     res.f = sqrtf(a);
 | |
|                     break;
 | |
|                 case OP_CMP:
 | |
|                     res.u64 = isgreater(a, b);
 | |
|                     break;
 | |
|                 default:
 | |
|                     g_assert_not_reached();
 | |
|                 }
 | |
|             }
 | |
|             break;
 | |
|         case PREC_DOUBLE:
 | |
|             fill_random(ops, n_ops, prec, no_neg);
 | |
|             t0 = get_clock();
 | |
|             for (i = 0; i < OPS_PER_ITER; i++) {
 | |
|                 double a = ops[0].d;
 | |
|                 double b = ops[1].d;
 | |
|                 double c = ops[2].d;
 | |
| 
 | |
|                 switch (op) {
 | |
|                 case OP_ADD:
 | |
|                     res.d = a + b;
 | |
|                     break;
 | |
|                 case OP_SUB:
 | |
|                     res.d = a - b;
 | |
|                     break;
 | |
|                 case OP_MUL:
 | |
|                     res.d = a * b;
 | |
|                     break;
 | |
|                 case OP_DIV:
 | |
|                     res.d = a / b;
 | |
|                     break;
 | |
|                 case OP_FMA:
 | |
|                     res.d = fma(a, b, c);
 | |
|                     break;
 | |
|                 case OP_SQRT:
 | |
|                     res.d = sqrt(a);
 | |
|                     break;
 | |
|                 case OP_CMP:
 | |
|                     res.u64 = isgreater(a, b);
 | |
|                     break;
 | |
|                 default:
 | |
|                     g_assert_not_reached();
 | |
|                 }
 | |
|             }
 | |
|             break;
 | |
|         case PREC_FLOAT32:
 | |
|             fill_random(ops, n_ops, prec, no_neg);
 | |
|             t0 = get_clock();
 | |
|             for (i = 0; i < OPS_PER_ITER; i++) {
 | |
|                 float32 a = ops[0].f32;
 | |
|                 float32 b = ops[1].f32;
 | |
|                 float32 c = ops[2].f32;
 | |
| 
 | |
|                 switch (op) {
 | |
|                 case OP_ADD:
 | |
|                     res.f32 = float32_add(a, b, &soft_status);
 | |
|                     break;
 | |
|                 case OP_SUB:
 | |
|                     res.f32 = float32_sub(a, b, &soft_status);
 | |
|                     break;
 | |
|                 case OP_MUL:
 | |
|                     res.f = float32_mul(a, b, &soft_status);
 | |
|                     break;
 | |
|                 case OP_DIV:
 | |
|                     res.f32 = float32_div(a, b, &soft_status);
 | |
|                     break;
 | |
|                 case OP_FMA:
 | |
|                     res.f32 = float32_muladd(a, b, c, 0, &soft_status);
 | |
|                     break;
 | |
|                 case OP_SQRT:
 | |
|                     res.f32 = float32_sqrt(a, &soft_status);
 | |
|                     break;
 | |
|                 case OP_CMP:
 | |
|                     res.u64 = float32_compare_quiet(a, b, &soft_status);
 | |
|                     break;
 | |
|                 default:
 | |
|                     g_assert_not_reached();
 | |
|                 }
 | |
|             }
 | |
|             break;
 | |
|         case PREC_FLOAT64:
 | |
|             fill_random(ops, n_ops, prec, no_neg);
 | |
|             t0 = get_clock();
 | |
|             for (i = 0; i < OPS_PER_ITER; i++) {
 | |
|                 float64 a = ops[0].f64;
 | |
|                 float64 b = ops[1].f64;
 | |
|                 float64 c = ops[2].f64;
 | |
| 
 | |
|                 switch (op) {
 | |
|                 case OP_ADD:
 | |
|                     res.f64 = float64_add(a, b, &soft_status);
 | |
|                     break;
 | |
|                 case OP_SUB:
 | |
|                     res.f64 = float64_sub(a, b, &soft_status);
 | |
|                     break;
 | |
|                 case OP_MUL:
 | |
|                     res.f = float64_mul(a, b, &soft_status);
 | |
|                     break;
 | |
|                 case OP_DIV:
 | |
|                     res.f64 = float64_div(a, b, &soft_status);
 | |
|                     break;
 | |
|                 case OP_FMA:
 | |
|                     res.f64 = float64_muladd(a, b, c, 0, &soft_status);
 | |
|                     break;
 | |
|                 case OP_SQRT:
 | |
|                     res.f64 = float64_sqrt(a, &soft_status);
 | |
|                     break;
 | |
|                 case OP_CMP:
 | |
|                     res.u64 = float64_compare_quiet(a, b, &soft_status);
 | |
|                     break;
 | |
|                 default:
 | |
|                     g_assert_not_reached();
 | |
|                 }
 | |
|             }
 | |
|             break;
 | |
|         case PREC_FLOAT128:
 | |
|             fill_random(ops, n_ops, prec, no_neg);
 | |
|             t0 = get_clock();
 | |
|             for (i = 0; i < OPS_PER_ITER; i++) {
 | |
|                 float128 a = ops[0].f128;
 | |
|                 float128 b = ops[1].f128;
 | |
|                 float128 c = ops[2].f128;
 | |
| 
 | |
|                 switch (op) {
 | |
|                 case OP_ADD:
 | |
|                     res.f128 = float128_add(a, b, &soft_status);
 | |
|                     break;
 | |
|                 case OP_SUB:
 | |
|                     res.f128 = float128_sub(a, b, &soft_status);
 | |
|                     break;
 | |
|                 case OP_MUL:
 | |
|                     res.f128 = float128_mul(a, b, &soft_status);
 | |
|                     break;
 | |
|                 case OP_DIV:
 | |
|                     res.f128 = float128_div(a, b, &soft_status);
 | |
|                     break;
 | |
|                 case OP_FMA:
 | |
|                     res.f128 = float128_muladd(a, b, c, 0, &soft_status);
 | |
|                     break;
 | |
|                 case OP_SQRT:
 | |
|                     res.f128 = float128_sqrt(a, &soft_status);
 | |
|                     break;
 | |
|                 case OP_CMP:
 | |
|                     res.u64 = float128_compare_quiet(a, b, &soft_status);
 | |
|                     break;
 | |
|                 default:
 | |
|                     g_assert_not_reached();
 | |
|                 }
 | |
|             }
 | |
|             break;
 | |
|         default:
 | |
|             g_assert_not_reached();
 | |
|         }
 | |
|         ns_elapsed += get_clock() - t0;
 | |
|         n_completed_ops += OPS_PER_ITER;
 | |
|     }
 | |
| }
 | |
| 
 | |
| #define GEN_BENCH(name, type, prec, op, n_ops)          \
 | |
|     static void __attribute__((flatten)) name(void)     \
 | |
|     {                                                   \
 | |
|         bench(prec, op, n_ops, false);                  \
 | |
|     }
 | |
| 
 | |
| #define GEN_BENCH_NO_NEG(name, type, prec, op, n_ops)   \
 | |
|     static void __attribute__((flatten)) name(void)     \
 | |
|     {                                                   \
 | |
|         bench(prec, op, n_ops, true);                   \
 | |
|     }
 | |
| 
 | |
| #define GEN_BENCH_ALL_TYPES(opname, op, n_ops)                          \
 | |
|     GEN_BENCH(bench_ ## opname ## _float, float, PREC_SINGLE, op, n_ops) \
 | |
|     GEN_BENCH(bench_ ## opname ## _double, double, PREC_DOUBLE, op, n_ops) \
 | |
|     GEN_BENCH(bench_ ## opname ## _float32, float32, PREC_FLOAT32, op, n_ops) \
 | |
|     GEN_BENCH(bench_ ## opname ## _float64, float64, PREC_FLOAT64, op, n_ops) \
 | |
|     GEN_BENCH(bench_ ## opname ## _float128, float128, PREC_FLOAT128, op, n_ops)
 | |
| 
 | |
| GEN_BENCH_ALL_TYPES(add, OP_ADD, 2)
 | |
| GEN_BENCH_ALL_TYPES(sub, OP_SUB, 2)
 | |
| GEN_BENCH_ALL_TYPES(mul, OP_MUL, 2)
 | |
| GEN_BENCH_ALL_TYPES(div, OP_DIV, 2)
 | |
| GEN_BENCH_ALL_TYPES(fma, OP_FMA, 3)
 | |
| GEN_BENCH_ALL_TYPES(cmp, OP_CMP, 2)
 | |
| #undef GEN_BENCH_ALL_TYPES
 | |
| 
 | |
| #define GEN_BENCH_ALL_TYPES_NO_NEG(name, op, n)                         \
 | |
|     GEN_BENCH_NO_NEG(bench_ ## name ## _float, float, PREC_SINGLE, op, n) \
 | |
|     GEN_BENCH_NO_NEG(bench_ ## name ## _double, double, PREC_DOUBLE, op, n) \
 | |
|     GEN_BENCH_NO_NEG(bench_ ## name ## _float32, float32, PREC_FLOAT32, op, n) \
 | |
|     GEN_BENCH_NO_NEG(bench_ ## name ## _float64, float64, PREC_FLOAT64, op, n) \
 | |
|     GEN_BENCH_NO_NEG(bench_ ## name ## _float128, float128, PREC_FLOAT128, op, n)
 | |
| 
 | |
| GEN_BENCH_ALL_TYPES_NO_NEG(sqrt, OP_SQRT, 1)
 | |
| #undef GEN_BENCH_ALL_TYPES_NO_NEG
 | |
| 
 | |
| #undef GEN_BENCH_NO_NEG
 | |
| #undef GEN_BENCH
 | |
| 
 | |
| #define GEN_BENCH_FUNCS(opname, op)                             \
 | |
|     [op] = {                                                    \
 | |
|         [PREC_SINGLE]    = bench_ ## opname ## _float,          \
 | |
|         [PREC_DOUBLE]    = bench_ ## opname ## _double,         \
 | |
|         [PREC_FLOAT32]   = bench_ ## opname ## _float32,        \
 | |
|         [PREC_FLOAT64]   = bench_ ## opname ## _float64,        \
 | |
|         [PREC_FLOAT128]   = bench_ ## opname ## _float128,      \
 | |
|     }
 | |
| 
 | |
| static const bench_func_t bench_funcs[OP_MAX_NR][PREC_MAX_NR] = {
 | |
|     GEN_BENCH_FUNCS(add, OP_ADD),
 | |
|     GEN_BENCH_FUNCS(sub, OP_SUB),
 | |
|     GEN_BENCH_FUNCS(mul, OP_MUL),
 | |
|     GEN_BENCH_FUNCS(div, OP_DIV),
 | |
|     GEN_BENCH_FUNCS(fma, OP_FMA),
 | |
|     GEN_BENCH_FUNCS(sqrt, OP_SQRT),
 | |
|     GEN_BENCH_FUNCS(cmp, OP_CMP),
 | |
| };
 | |
| 
 | |
| #undef GEN_BENCH_FUNCS
 | |
| 
 | |
| static void run_bench(void)
 | |
| {
 | |
|     bench_func_t f;
 | |
| 
 | |
|     f = bench_funcs[operation][precision];
 | |
|     g_assert(f);
 | |
|     f();
 | |
| }
 | |
| 
 | |
| /* @arr must be NULL-terminated */
 | |
| static int find_name(const char * const *arr, const char *name)
 | |
| {
 | |
|     int i;
 | |
| 
 | |
|     for (i = 0; arr[i] != NULL; i++) {
 | |
|         if (strcmp(name, arr[i]) == 0) {
 | |
|             return i;
 | |
|         }
 | |
|     }
 | |
|     return -1;
 | |
| }
 | |
| 
 | |
| static void usage_complete(int argc, char *argv[])
 | |
| {
 | |
|     gchar *op_list = g_strjoinv(", ", (gchar **)op_names);
 | |
|     gchar *tester_list = g_strjoinv(", ", (gchar **)tester_names);
 | |
| 
 | |
|     fprintf(stderr, "Usage: %s [options]\n", argv[0]);
 | |
|     fprintf(stderr, "options:\n");
 | |
|     fprintf(stderr, " -d = duration, in seconds. Default: %d\n",
 | |
|             DEFAULT_DURATION_SECS);
 | |
|     fprintf(stderr, " -h = show this help message.\n");
 | |
|     fprintf(stderr, " -o = floating point operation (%s). Default: %s\n",
 | |
|             op_list, op_names[0]);
 | |
|     fprintf(stderr, " -p = floating point precision (single, double, quad[soft only]). "
 | |
|             "Default: single\n");
 | |
|     fprintf(stderr, " -r = rounding mode (even, zero, down, up, tieaway). "
 | |
|             "Default: even\n");
 | |
|     fprintf(stderr, " -t = tester (%s). Default: %s\n",
 | |
|             tester_list, tester_names[0]);
 | |
|     fprintf(stderr, " -z = flush inputs to zero (soft tester only). "
 | |
|             "Default: disabled\n");
 | |
|     fprintf(stderr, " -Z = flush output to zero (soft tester only). "
 | |
|             "Default: disabled\n");
 | |
| 
 | |
|     g_free(tester_list);
 | |
|     g_free(op_list);
 | |
| }
 | |
| 
 | |
| static int round_name_to_mode(const char *name)
 | |
| {
 | |
|     int i;
 | |
| 
 | |
|     for (i = 0; i < N_ROUND_MODES; i++) {
 | |
|         if (!strcmp(round_names[i], name)) {
 | |
|             return i;
 | |
|         }
 | |
|     }
 | |
|     return -1;
 | |
| }
 | |
| 
 | |
| static G_NORETURN
 | |
| void die_host_rounding(enum rounding rounding)
 | |
| {
 | |
|     fprintf(stderr, "fatal: '%s' rounding not supported on this host\n",
 | |
|             round_names[rounding]);
 | |
|     exit(EXIT_FAILURE);
 | |
| }
 | |
| 
 | |
| static void set_host_precision(enum rounding rounding)
 | |
| {
 | |
|     int rhost;
 | |
| 
 | |
|     switch (rounding) {
 | |
|     case ROUND_EVEN:
 | |
|         rhost = FE_TONEAREST;
 | |
|         break;
 | |
|     case ROUND_ZERO:
 | |
|         rhost = FE_TOWARDZERO;
 | |
|         break;
 | |
|     case ROUND_DOWN:
 | |
|         rhost = FE_DOWNWARD;
 | |
|         break;
 | |
|     case ROUND_UP:
 | |
|         rhost = FE_UPWARD;
 | |
|         break;
 | |
|     case ROUND_TIEAWAY:
 | |
|         die_host_rounding(rounding);
 | |
|         return;
 | |
|     default:
 | |
|         g_assert_not_reached();
 | |
|     }
 | |
| 
 | |
|     if (fesetround(rhost)) {
 | |
|         die_host_rounding(rounding);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void set_soft_precision(enum rounding rounding)
 | |
| {
 | |
|     signed char mode;
 | |
| 
 | |
|     switch (rounding) {
 | |
|     case ROUND_EVEN:
 | |
|         mode = float_round_nearest_even;
 | |
|         break;
 | |
|     case ROUND_ZERO:
 | |
|         mode = float_round_to_zero;
 | |
|         break;
 | |
|     case ROUND_DOWN:
 | |
|         mode = float_round_down;
 | |
|         break;
 | |
|     case ROUND_UP:
 | |
|         mode = float_round_up;
 | |
|         break;
 | |
|     case ROUND_TIEAWAY:
 | |
|         mode = float_round_ties_away;
 | |
|         break;
 | |
|     default:
 | |
|         g_assert_not_reached();
 | |
|     }
 | |
|     soft_status.float_rounding_mode = mode;
 | |
| }
 | |
| 
 | |
| static void parse_args(int argc, char *argv[])
 | |
| {
 | |
|     int c;
 | |
|     int val;
 | |
|     int rounding = ROUND_EVEN;
 | |
| 
 | |
|     for (;;) {
 | |
|         c = getopt(argc, argv, "d:ho:p:r:t:zZ");
 | |
|         if (c < 0) {
 | |
|             break;
 | |
|         }
 | |
|         switch (c) {
 | |
|         case 'd':
 | |
|             duration = atoi(optarg);
 | |
|             break;
 | |
|         case 'h':
 | |
|             usage_complete(argc, argv);
 | |
|             exit(EXIT_SUCCESS);
 | |
|         case 'o':
 | |
|             val = find_name(op_names, optarg);
 | |
|             if (val < 0) {
 | |
|                 fprintf(stderr, "Unsupported op '%s'\n", optarg);
 | |
|                 exit(EXIT_FAILURE);
 | |
|             }
 | |
|             operation = val;
 | |
|             break;
 | |
|         case 'p':
 | |
|             if (!strcmp(optarg, "single")) {
 | |
|                 precision = PREC_SINGLE;
 | |
|             } else if (!strcmp(optarg, "double")) {
 | |
|                 precision = PREC_DOUBLE;
 | |
|             } else if (!strcmp(optarg, "quad")) {
 | |
|                 precision = PREC_QUAD;
 | |
|             } else {
 | |
|                 fprintf(stderr, "Unsupported precision '%s'\n", optarg);
 | |
|                 exit(EXIT_FAILURE);
 | |
|             }
 | |
|             break;
 | |
|         case 'r':
 | |
|             rounding = round_name_to_mode(optarg);
 | |
|             if (rounding < 0) {
 | |
|                 fprintf(stderr, "fatal: invalid rounding mode '%s'\n", optarg);
 | |
|                 exit(EXIT_FAILURE);
 | |
|             }
 | |
|             break;
 | |
|         case 't':
 | |
|             val = find_name(tester_names, optarg);
 | |
|             if (val < 0) {
 | |
|                 fprintf(stderr, "Unsupported tester '%s'\n", optarg);
 | |
|                 exit(EXIT_FAILURE);
 | |
|             }
 | |
|             tester = val;
 | |
|             break;
 | |
|         case 'z':
 | |
|             soft_status.flush_inputs_to_zero = 1;
 | |
|             break;
 | |
|         case 'Z':
 | |
|             soft_status.flush_to_zero = 1;
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* set precision and rounding mode based on the tester */
 | |
|     switch (tester) {
 | |
|     case TESTER_HOST:
 | |
|         set_host_precision(rounding);
 | |
|         break;
 | |
|     case TESTER_SOFT:
 | |
|         set_soft_precision(rounding);
 | |
|         switch (precision) {
 | |
|         case PREC_SINGLE:
 | |
|             precision = PREC_FLOAT32;
 | |
|             break;
 | |
|         case PREC_DOUBLE:
 | |
|             precision = PREC_FLOAT64;
 | |
|             break;
 | |
|         case PREC_QUAD:
 | |
|             precision = PREC_FLOAT128;
 | |
|             break;
 | |
|         default:
 | |
|             g_assert_not_reached();
 | |
|         }
 | |
|         break;
 | |
|     default:
 | |
|         g_assert_not_reached();
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void pr_stats(void)
 | |
| {
 | |
|     printf("%.2f MFlops\n", (double)n_completed_ops / ns_elapsed * 1e3);
 | |
| }
 | |
| 
 | |
| int main(int argc, char *argv[])
 | |
| {
 | |
|     parse_args(argc, argv);
 | |
|     run_bench();
 | |
|     pr_stats();
 | |
|     return 0;
 | |
| }
 |