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;
 | 
						|
}
 |