Hardfloat + maintainers and gitdm
-----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEEZoWumedRZ7yvyN81+9DbCVqeKkQFAlwXgJcACgkQ+9DbCVqe KkTwwwf7BDbROStJi5ZpCQmhXfpN/w0Ol0JO21Ic9jAT4WZq82IU7LFkOuwT9Mx4 Km88ZOpBNE+zRsITgjHNGAR2vLe9VJGzsOzF6/wJfgFILsT0R9gjIyCT0I3uzQjh dgArCNJvVvt2MJQ0tLYiGX8+oIXpMrHFA2t1XwTrm2vRrM3F7ZrayucuEBop9Qko o5+HeJ8Jdp3vJpMMXZw7moXP5ZpizlmpaaCQAWY3IeFWlZdE9W7ctHQRuKG45m09 ab69IfEdnKqqjmlqIcta4wute9pBNeXqeOsdyfF4NPq1Vecgv1GMywBA+hiFBpYx b4eDCZl7LtNwjGnGf4geOUQzaCM1Uw== =+aD7 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/stsquad/tags/pull-hardfloat-and-gitdm-171218-3' into staging Hardfloat + maintainers and gitdm # gpg: Signature made Mon 17 Dec 2018 10:55:19 GMT # gpg: using RSA key FBD0DB095A9E2A44 # gpg: Good signature from "Alex Bennée (Master Work Key) <alex.bennee@linaro.org>" # Primary key fingerprint: 6685 AE99 E751 67BC AFC8 DF35 FBD0 DB09 5A9E 2A44 * remotes/stsquad/tags/pull-hardfloat-and-gitdm-171218-3: hardfloat: implement float32/64 comparison hardfloat: implement float32/64 square root hardfloat: implement float32/64 fused multiply-add hardfloat: implement float32/64 division hardfloat: implement float32/64 multiplication hardfloat: implement float32/64 addition and subtraction fpu: introduce hardfloat tests/fp: add fp-bench softfloat: add float{32,64}_is_zero_or_normal softfloat: rename canonicalize to sf_canonicalize target/tricore: use float32_is_denormal softfloat: add float{32,64}_is_{de,}normal fp-test: pick TARGET_ARM to get its specialization MAINTAINERS: update status of FPU emulation contrib: add a basic gitdm config Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
ec3c927f3d
|
@ -127,9 +127,11 @@ F: include/sysemu/cpus.h
|
|||
FPU emulation
|
||||
M: Aurelien Jarno <aurelien@aurel32.net>
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
S: Odd Fixes
|
||||
M: Alex Bennée <alex.bennee@linaro.org>
|
||||
S: Maintained
|
||||
F: fpu/
|
||||
F: include/fpu/
|
||||
F: tests/fp/
|
||||
|
||||
Alpha
|
||||
M: Richard Henderson <rth@twiddle.net>
|
||||
|
|
27
contrib/gitdm/aliases
Normal file
27
contrib/gitdm/aliases
Normal file
|
@ -0,0 +1,27 @@
|
|||
#
|
||||
# This is the email aliases file, mapping secondary addresses
|
||||
# onto a single, canonical address. Duplicates some info from .mailmap
|
||||
#
|
||||
|
||||
# weird commits
|
||||
balrog@c046a42c-6fe2-441c-8c8c-71466251a162 balrogg@gmail.com
|
||||
aliguori@c046a42c-6fe2-441c-8c8c-71466251a162 anthony@codemonkey.ws
|
||||
aurel32@c046a42c-6fe2-441c-8c8c-71466251a162 aurelien@aurel32.net
|
||||
blueswir1@c046a42c-6fe2-441c-8c8c-71466251a162 blauwirbel@gmail.com
|
||||
edgar_igl@c046a42c-6fe2-441c-8c8c-71466251a162 edgar.iglesias@gmail.com
|
||||
bellard@c046a42c-6fe2-441c-8c8c-71466251a162 fabrice@bellard.org
|
||||
j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162 l_indien@magic.fr
|
||||
pbrook@c046a42c-6fe2-441c-8c8c-71466251a162 paul@codesourcery.com
|
||||
ths@c046a42c-6fe2-441c-8c8c-71466251a162 ths@networkno.de
|
||||
malc@c046a42c-6fe2-441c-8c8c-71466251a162 av1474@comtv.ru
|
||||
|
||||
# There is also a:
|
||||
# (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
# for the cvs2svn initialization commit e63c3dc74bf.
|
||||
|
||||
# Next, translate a few commits where mailman rewrote the From: line due
|
||||
# to strict SPF, although we prefer to avoid adding more entries like that.
|
||||
"Ed Swierk via Qemu-devel" eswierk@skyportsystems.com
|
||||
"Ian McKellar via Qemu-devel" ianloic@google.com
|
||||
"Julia Suvorova via Qemu-devel" jusual@mail.ru
|
||||
"Justin Terry (VM) via Qemu-devel" juterry@microsoft.com
|
19
contrib/gitdm/domain-map
Normal file
19
contrib/gitdm/domain-map
Normal file
|
@ -0,0 +1,19 @@
|
|||
#
|
||||
# QEMU gitdm domain-map
|
||||
#
|
||||
# This maps email domains to nice easy to read company names
|
||||
#
|
||||
|
||||
amd.com AMD
|
||||
greensocs.com GreenSocs
|
||||
ibm.com IBM
|
||||
igalia.com Igalia
|
||||
linaro.org Linaro
|
||||
oracle.com Oracle
|
||||
redhat.com Red Hat
|
||||
siemens.com Siemens
|
||||
sifive.com SiFive
|
||||
suse.de SUSE
|
||||
virtuozzo.com Virtuozzo
|
||||
wdc.com Western Digital
|
||||
xilinx.com Xilinx
|
146
contrib/gitdm/filetypes.txt
Normal file
146
contrib/gitdm/filetypes.txt
Normal file
|
@ -0,0 +1,146 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
# Copyright (C) 2006 Libresoft
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Library General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
# Authors : Gregorio Robles <grex@gsyc.escet.urjc.es>
|
||||
# Authors : Germán Póo-Caamaño <gpoo@gnome.org>
|
||||
#
|
||||
# This QEMU version is a cut-down version of what originally shipped
|
||||
# in the gitdm sample-config directory.
|
||||
#
|
||||
# This file contains associations parameters regarding filetypes
|
||||
# (documentation, develompent, multimedia, images...)
|
||||
#
|
||||
# format:
|
||||
# filetype <type> <regex> [<comment>]
|
||||
#
|
||||
# Order:
|
||||
# The list should keep an order, so filetypes can be counted properly.
|
||||
# ie. we want ltmain.sh -> 'build' instead of 'code'.
|
||||
#
|
||||
# If there is an filetype which is not in order but has values, it will
|
||||
# be added at the end.
|
||||
#
|
||||
order build,tests,code,documentation,devel-doc,blobs
|
||||
|
||||
#
|
||||
#
|
||||
# Code files (headers and the like included
|
||||
# (most common languages first
|
||||
#
|
||||
filetype code \.c$ # C
|
||||
filetype code \.inc.c$ # C
|
||||
filetype code \.C$ # C++
|
||||
filetype code \.cpp$ # C++
|
||||
filetype code \.c\+\+$ # C++
|
||||
filetype code \.cxx$ # C++
|
||||
filetype code \.cc$ # C++
|
||||
filetype code \.h$ # C or C++ header
|
||||
filetype code \.hh$ # C++ header
|
||||
filetype code \.hpp$ # C++ header
|
||||
filetype code \.hxx$ # C++ header
|
||||
filetype code \.sh$ # Shell
|
||||
filetype code \.pl$ # Perl
|
||||
filetype code \.py$ # Python
|
||||
filetype code \.s$ # Assembly
|
||||
filetype code \.S$ # Assembly
|
||||
filetype code \.asm$ # Assembly
|
||||
filetype code \.awk$ # awk
|
||||
filetype code ^common$ # script fragements
|
||||
filetype code ^common.*$ # script fragements
|
||||
filetype code (qom|qmp)-\w+$ # python script fragments
|
||||
|
||||
#
|
||||
# Interface/api files
|
||||
#
|
||||
filetype interface \.json$ # json
|
||||
filetype interface \.hx$ # documented options
|
||||
|
||||
#
|
||||
# Test related blobs (unfortunately we can't filter out test code)
|
||||
#
|
||||
filetype tests \.hex$
|
||||
filetype tests \d{2,3}$ # test data 00-999
|
||||
filetype tests ^[A-Z]{4}$ # ACPI test data
|
||||
filetype tests ^[A-Z]{4}\.*$ # ACPI test data
|
||||
filetype tests \.out$
|
||||
filetype tests \.out\.nocache$
|
||||
filetype tests \.err$
|
||||
filetype tests \.exit$ # bad-if-FOO.exit etc
|
||||
filetype tests \.decode$
|
||||
filetype tests \.yml$ # travis/shippable config
|
||||
|
||||
#
|
||||
# Development documentation files (for hacking generally)
|
||||
#
|
||||
filetype devel-doc ^readme.*$
|
||||
filetype devel-doc ^changelog.*
|
||||
filetype devel-doc ^hacking.*$
|
||||
filetype devel-doc ^licen(s|c)e.*$
|
||||
filetype devel-doc ^copying.*$
|
||||
filetype devel-doc ^MAINTAINERS$
|
||||
filetype devel-doc ^BSD-2-Clause$
|
||||
filetype devel-doc ^BSD-3-Clause$
|
||||
filetype devel-doc ^GPL-2.0$
|
||||
filetype devel-doc \.txt$
|
||||
filetype devel-doc \.rst$
|
||||
filetype devel-doc \.texi$
|
||||
filetype devel-doc \.pod$
|
||||
|
||||
#
|
||||
# Building, compiling, and configuration admin files
|
||||
#
|
||||
filetype build configure.*$
|
||||
filetype build Makefile$
|
||||
filetype build Makefile\.*$
|
||||
filetype build config$
|
||||
filetype build conf$
|
||||
filetype build \.cfg$
|
||||
filetype build \.mk$
|
||||
filetype build \.mak$
|
||||
filetype build \.docker$
|
||||
filetype build \.pre$
|
||||
filetype build ^.gitignore$
|
||||
filetype build ^.gitmodules$
|
||||
filetype build ^.gitpublish$
|
||||
filetype build ^.mailmap$
|
||||
filetype build ^.dir-locals.el$
|
||||
filetype build ^.editorconfig$
|
||||
filetype build ^.exrc$
|
||||
filetype build ^.gdbinit$
|
||||
filetype build \.cocci$ # Coccinelle semantic patches
|
||||
|
||||
#
|
||||
# Misc blobs
|
||||
#
|
||||
filetype blobs \.bin$
|
||||
filetype blobs \.dtb$
|
||||
filetype blobs \.dts$
|
||||
filetype blobs \.rom$
|
||||
filetype blobs \.img$
|
||||
filetype blobs \.ndrv$
|
||||
filetype blobs \.bmp$
|
||||
filetype blobs \.svg$
|
||||
filetype blobs ^pi_10.com$
|
||||
|
||||
|
||||
#
|
||||
# Documentation files
|
||||
#
|
||||
filetype documentation \.html$
|
||||
filetype documentation \.txt$
|
||||
filetype documentation \.texi$
|
||||
filetype documentation \.po$ # translation files
|
14
contrib/gitdm/group-map-academics
Normal file
14
contrib/gitdm/group-map-academics
Normal file
|
@ -0,0 +1,14 @@
|
|||
#
|
||||
# QEMU is quite often used for academic research purposes and we like
|
||||
# it even better when the work is up-streamed so the project can
|
||||
# benefit.
|
||||
#
|
||||
# We group our academic contributors here
|
||||
#
|
||||
|
||||
# Institute for System Programming of Russian Academy of Science
|
||||
ispras.ru
|
||||
|
||||
# Columbia University
|
||||
cs.columbia.edu
|
||||
cota@braap.org
|
3
contrib/gitdm/group-map-cadence
Normal file
3
contrib/gitdm/group-map-cadence
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Cadence Design Systems
|
||||
|
||||
jcmvbkbc@gmail.com
|
1
contrib/gitdm/group-map-codeweavers
Normal file
1
contrib/gitdm/group-map-codeweavers
Normal file
|
@ -0,0 +1 @@
|
|||
sergio.g.delreal@gmail.com
|
6
contrib/gitdm/group-map-ibm
Normal file
6
contrib/gitdm/group-map-ibm
Normal file
|
@ -0,0 +1,6 @@
|
|||
#
|
||||
# Some IBM contributors submit via another domain
|
||||
#
|
||||
|
||||
clg@kaod.org
|
||||
groug@kaod.org
|
10
contrib/gitdm/group-map-individuals
Normal file
10
contrib/gitdm/group-map-individuals
Normal file
|
@ -0,0 +1,10 @@
|
|||
#
|
||||
# Individual and personal contributors
|
||||
#
|
||||
# This is simply to allow prolific developers with no company
|
||||
# affiliations to be grouped together in the summary stats.
|
||||
#
|
||||
|
||||
f4bug@amsat.org
|
||||
mjt@tls.msk.ru
|
||||
mark.cave-ayland@ilande.co.uk
|
7
contrib/gitdm/group-map-redhat
Normal file
7
contrib/gitdm/group-map-redhat
Normal file
|
@ -0,0 +1,7 @@
|
|||
#
|
||||
# Red Hat contributors using non-corporate email
|
||||
#
|
||||
|
||||
david@gibson.dropbear.id.au
|
||||
laurent@vivier.eu
|
||||
pjp@fedoraproject.org
|
18
contrib/gitdm/group-map-wavecomp
Normal file
18
contrib/gitdm/group-map-wavecomp
Normal file
|
@ -0,0 +1,18 @@
|
|||
#
|
||||
# Wave Computing acquired MIPS in June 2018. Also, from February 2013
|
||||
# to October 2017, MIPS was owned by Imagination Technologies.
|
||||
#
|
||||
|
||||
aleksandar.markovic@imgtec.com
|
||||
aleksandar.markovic@mips.com
|
||||
amarkovic@wavecomp.com
|
||||
arikalo@wavecomp.com
|
||||
dnikolic@wavecomp.com
|
||||
james.hogan@mips.com
|
||||
matthew.fortune@mips.com
|
||||
paul.burton@imgtec.com
|
||||
pburton@wavecomp.com
|
||||
smarkovic@wavecomp.com
|
||||
yongbok.kim@imgtec.com
|
||||
yongbok.kim@mips.com
|
||||
ysu@wavecomp.com
|
871
fpu/softfloat.c
871
fpu/softfloat.c
File diff suppressed because it is too large
Load diff
50
gitdm.config
Normal file
50
gitdm.config
Normal file
|
@ -0,0 +1,50 @@
|
|||
#
|
||||
# This is the gitdm configuration file for QEMU.
|
||||
#
|
||||
# It is to be used with LWN's git dataminer tool for generating
|
||||
# reports about development activity in the QEMU repo. The LWN gitdm
|
||||
# tool can be found at:
|
||||
#
|
||||
# git://git.lwn.net/gitdm.git
|
||||
#
|
||||
# A run to generate a report for the last year of activity would be
|
||||
#
|
||||
# git log --numstat --since "Last Year" | gitdm -n -l 10
|
||||
#
|
||||
|
||||
# EmailAliases lets us cope with developers who use more
|
||||
# than one address or have changed addresses. This duplicates some of
|
||||
# the information in the existing .mailmap but in a slightly different
|
||||
# form.
|
||||
#
|
||||
EmailAliases contrib/gitdm/aliases
|
||||
|
||||
#
|
||||
# EmailMap does the main work of mapping addresses onto
|
||||
# employers.
|
||||
#
|
||||
EmailMap contrib/gitdm/domain-map
|
||||
|
||||
#
|
||||
# Use GroupMap to map a file full of addresses to the
|
||||
# same employer. This is used for people that don't post from easily
|
||||
# identifiable corporate emails.
|
||||
#
|
||||
|
||||
GroupMap contrib/gitdm/group-map-redhat Red Hat
|
||||
GroupMap contrib/gitdm/group-map-wavecomp Wave Computing
|
||||
GroupMap contrib/gitdm/group-map-cadence Cadence Design Systems
|
||||
GroupMap contrib/gitdm/group-map-codeweavers CodeWeavers
|
||||
GroupMap contrib/gitdm/group-map-ibm IBM
|
||||
|
||||
# Also group together our prolific individual contributors
|
||||
# and those working under academic auspices
|
||||
GroupMap contrib/gitdm/group-map-individuals (None)
|
||||
GroupMap contrib/gitdm/group-map-academics Academics (various)
|
||||
|
||||
#
|
||||
#
|
||||
# Use FileTypeMap to map a file types to file names using regular
|
||||
# regular expressions.
|
||||
#
|
||||
FileTypeMap contrib/gitdm/filetypes.txt
|
|
@ -464,6 +464,21 @@ static inline int float32_is_zero_or_denormal(float32 a)
|
|||
return (float32_val(a) & 0x7f800000) == 0;
|
||||
}
|
||||
|
||||
static inline bool float32_is_normal(float32 a)
|
||||
{
|
||||
return ((float32_val(a) + 0x00800000) & 0x7fffffff) >= 0x01000000;
|
||||
}
|
||||
|
||||
static inline bool float32_is_denormal(float32 a)
|
||||
{
|
||||
return float32_is_zero_or_denormal(a) && !float32_is_zero(a);
|
||||
}
|
||||
|
||||
static inline bool float32_is_zero_or_normal(float32 a)
|
||||
{
|
||||
return float32_is_normal(a) || float32_is_zero(a);
|
||||
}
|
||||
|
||||
static inline float32 float32_set_sign(float32 a, int sign)
|
||||
{
|
||||
return make_float32((float32_val(a) & 0x7fffffff) | (sign << 31));
|
||||
|
@ -605,6 +620,21 @@ static inline int float64_is_zero_or_denormal(float64 a)
|
|||
return (float64_val(a) & 0x7ff0000000000000LL) == 0;
|
||||
}
|
||||
|
||||
static inline bool float64_is_normal(float64 a)
|
||||
{
|
||||
return ((float64_val(a) + (1ULL << 52)) & -1ULL >> 1) >= 1ULL << 53;
|
||||
}
|
||||
|
||||
static inline bool float64_is_denormal(float64 a)
|
||||
{
|
||||
return float64_is_zero_or_denormal(a) && !float64_is_zero(a);
|
||||
}
|
||||
|
||||
static inline bool float64_is_zero_or_normal(float64 a)
|
||||
{
|
||||
return float64_is_normal(a) || float64_is_zero(a);
|
||||
}
|
||||
|
||||
static inline float64 float64_set_sign(float64 a, int sign)
|
||||
{
|
||||
return make_float64((float64_val(a) & 0x7fffffffffffffffULL)
|
||||
|
|
|
@ -44,11 +44,6 @@ static inline uint8_t f_get_excp_flags(CPUTriCoreState *env)
|
|||
| float_flag_inexact);
|
||||
}
|
||||
|
||||
static inline bool f_is_denormal(float32 arg)
|
||||
{
|
||||
return float32_is_zero_or_denormal(arg) && !float32_is_zero(arg);
|
||||
}
|
||||
|
||||
static inline float32 f_maddsub_nan_result(float32 arg1, float32 arg2,
|
||||
float32 arg3, float32 result,
|
||||
uint32_t muladd_negate_c)
|
||||
|
@ -260,8 +255,8 @@ uint32_t helper_fcmp(CPUTriCoreState *env, uint32_t r1, uint32_t r2)
|
|||
set_flush_inputs_to_zero(0, &env->fp_status);
|
||||
|
||||
result = 1 << (float32_compare_quiet(arg1, arg2, &env->fp_status) + 1);
|
||||
result |= f_is_denormal(arg1) << 4;
|
||||
result |= f_is_denormal(arg2) << 5;
|
||||
result |= float32_is_denormal(arg1) << 4;
|
||||
result |= float32_is_denormal(arg2) << 5;
|
||||
|
||||
flags = f_get_excp_flags(env);
|
||||
if (flags) {
|
||||
|
|
1
tests/fp/.gitignore
vendored
1
tests/fp/.gitignore
vendored
|
@ -1 +1,2 @@
|
|||
fp-test
|
||||
fp-bench
|
||||
|
|
|
@ -29,6 +29,9 @@ QEMU_INCLUDES += -I$(TF_SOURCE_DIR)
|
|||
|
||||
# work around TARGET_* poisoning
|
||||
QEMU_CFLAGS += -DHW_POISON_H
|
||||
# define a target to match testfloat's implementation-defined choices, such as
|
||||
# whether to raise the invalid flag when dealing with NaNs in muladd.
|
||||
QEMU_CFLAGS += -DTARGET_ARM
|
||||
|
||||
# capstone has a platform.h file that clashes with softfloat's
|
||||
QEMU_CFLAGS := $(filter-out %capstone, $(QEMU_CFLAGS))
|
||||
|
@ -550,7 +553,7 @@ TF_OBJS_LIB += $(TF_OBJS_WRITECASE)
|
|||
TF_OBJS_LIB += testLoops_common.o
|
||||
TF_OBJS_LIB += $(TF_OBJS_TEST)
|
||||
|
||||
BINARIES := fp-test$(EXESUF)
|
||||
BINARIES := fp-test$(EXESUF) fp-bench$(EXESUF)
|
||||
|
||||
# everything depends on config-host.h because platform.h includes it
|
||||
all: $(BUILD_DIR)/config-host.h
|
||||
|
@ -587,10 +590,13 @@ $(TF_OBJS_LIB) slowfloat.o: %.o: $(TF_SOURCE_DIR)/%.c
|
|||
|
||||
libtestfloat.a: $(TF_OBJS_LIB)
|
||||
|
||||
fp-bench$(EXESUF): fp-bench.o $(QEMU_SOFTFLOAT_OBJ) $(LIBQEMUUTIL)
|
||||
|
||||
clean:
|
||||
rm -f *.o *.d $(BINARIES)
|
||||
rm -f *.gcno *.gcda *.gcov
|
||||
rm -f fp-test$(EXESUF)
|
||||
rm -f fp-bench$(EXESUF)
|
||||
rm -f libsoftfloat.a
|
||||
rm -f libtestfloat.a
|
||||
|
||||
|
|
630
tests/fp/fp-bench.c
Normal file
630
tests/fp/fp-bench.c
Normal file
|
@ -0,0 +1,630 @@
|
|||
/*
|
||||
* 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 "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_FLOAT32,
|
||||
PREC_FLOAT64,
|
||||
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;
|
||||
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 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++) {
|
||||
uint64_t r = random_ops[i];
|
||||
|
||||
if (prec == PREC_SINGLE || PREC_FLOAT32) {
|
||||
do {
|
||||
r = xorshift64star(r);
|
||||
} while (!float32_is_normal(r));
|
||||
} else if (prec == PREC_DOUBLE || PREC_FLOAT64) {
|
||||
do {
|
||||
r = xorshift64star(r);
|
||||
} while (!float64_is_normal(r));
|
||||
} else {
|
||||
g_assert_not_reached();
|
||||
}
|
||||
random_ops[i] = r;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
/* raise the exponent to limit the frequency of denormal results */
|
||||
ops[i].f32 |= 0x40000000;
|
||||
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);
|
||||
}
|
||||
/* raise the exponent to limit the frequency of denormal results */
|
||||
ops[i].f64 |= LIT64(0x4000000000000000);
|
||||
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;
|
||||
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_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_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, \
|
||||
}
|
||||
|
||||
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). "
|
||||
"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 void QEMU_NORETURN 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 {
|
||||
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;
|
||||
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;
|
||||
}
|
Loading…
Reference in a new issue