diff options
34 files changed, 2049 insertions, 285 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 83c127f0d6..d676c73f88 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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> diff --git a/contrib/gitdm/aliases b/contrib/gitdm/aliases new file mode 100644 index 0000000000..07fd3391a5 --- /dev/null +++ b/contrib/gitdm/aliases @@ -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 diff --git a/contrib/gitdm/domain-map b/contrib/gitdm/domain-map new file mode 100644 index 0000000000..8cbbcfe93d --- /dev/null +++ b/contrib/gitdm/domain-map @@ -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 diff --git a/contrib/gitdm/filetypes.txt b/contrib/gitdm/filetypes.txt new file mode 100644 index 0000000000..15d6f803b9 --- /dev/null +++ b/contrib/gitdm/filetypes.txt @@ -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 diff --git a/contrib/gitdm/group-map-academics b/contrib/gitdm/group-map-academics new file mode 100644 index 0000000000..08f9d81d13 --- /dev/null +++ b/contrib/gitdm/group-map-academics @@ -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 diff --git a/contrib/gitdm/group-map-cadence b/contrib/gitdm/group-map-cadence new file mode 100644 index 0000000000..ab97dd2fc3 --- /dev/null +++ b/contrib/gitdm/group-map-cadence @@ -0,0 +1,3 @@ +# Cadence Design Systems + +jcmvbkbc@gmail.com diff --git a/contrib/gitdm/group-map-codeweavers b/contrib/gitdm/group-map-codeweavers new file mode 100644 index 0000000000..c4803489e2 --- /dev/null +++ b/contrib/gitdm/group-map-codeweavers @@ -0,0 +1 @@ +sergio.g.delreal@gmail.com diff --git a/contrib/gitdm/group-map-ibm b/contrib/gitdm/group-map-ibm new file mode 100644 index 0000000000..b66db5f4a8 --- /dev/null +++ b/contrib/gitdm/group-map-ibm @@ -0,0 +1,6 @@ +# +# Some IBM contributors submit via another domain +# + +clg@kaod.org +groug@kaod.org diff --git a/contrib/gitdm/group-map-individuals b/contrib/gitdm/group-map-individuals new file mode 100644 index 0000000000..afdbe7d460 --- /dev/null +++ b/contrib/gitdm/group-map-individuals @@ -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 diff --git a/contrib/gitdm/group-map-redhat b/contrib/gitdm/group-map-redhat new file mode 100644 index 0000000000..6d05c6b54f --- /dev/null +++ b/contrib/gitdm/group-map-redhat @@ -0,0 +1,7 @@ +# +# Red Hat contributors using non-corporate email +# + +david@gibson.dropbear.id.au +laurent@vivier.eu +pjp@fedoraproject.org diff --git a/contrib/gitdm/group-map-wavecomp b/contrib/gitdm/group-map-wavecomp new file mode 100644 index 0000000000..c571a52c65 --- /dev/null +++ b/contrib/gitdm/group-map-wavecomp @@ -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 diff --git a/fpu/softfloat.c b/fpu/softfloat.c index e1eef954e6..59eac97d10 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -83,6 +83,7 @@ this code that are retained. * target-dependent and needs the TARGET_* macros. */ #include "qemu/osdep.h" +#include <math.h> #include "qemu/bitops.h" #include "fpu/softfloat.h" @@ -95,6 +96,324 @@ this code that are retained. *----------------------------------------------------------------------------*/ #include "fpu/softfloat-macros.h" +/* + * Hardfloat + * + * Fast emulation of guest FP instructions is challenging for two reasons. + * First, FP instruction semantics are similar but not identical, particularly + * when handling NaNs. Second, emulating at reasonable speed the guest FP + * exception flags is not trivial: reading the host's flags register with a + * feclearexcept & fetestexcept pair is slow [slightly slower than soft-fp], + * and trapping on every FP exception is not fast nor pleasant to work with. + * + * We address these challenges by leveraging the host FPU for a subset of the + * operations. To do this we expand on the idea presented in this paper: + * + * Guo, Yu-Chuan, et al. "Translating the ARM Neon and VFP instructions in a + * binary translator." Software: Practice and Experience 46.12 (2016):1591-1615. + * + * The idea is thus to leverage the host FPU to (1) compute FP operations + * and (2) identify whether FP exceptions occurred while avoiding + * expensive exception flag register accesses. + * + * An important optimization shown in the paper is that given that exception + * flags are rarely cleared by the guest, we can avoid recomputing some flags. + * This is particularly useful for the inexact flag, which is very frequently + * raised in floating-point workloads. + * + * We optimize the code further by deferring to soft-fp whenever FP exception + * detection might get hairy. Two examples: (1) when at least one operand is + * denormal/inf/NaN; (2) when operands are not guaranteed to lead to a 0 result + * and the result is < the minimum normal. + */ +#define GEN_INPUT_FLUSH__NOCHECK(name, soft_t) \ + static inline void name(soft_t *a, float_status *s) \ + { \ + if (unlikely(soft_t ## _is_denormal(*a))) { \ + *a = soft_t ## _set_sign(soft_t ## _zero, \ + soft_t ## _is_neg(*a)); \ + s->float_exception_flags |= float_flag_input_denormal; \ + } \ + } + +GEN_INPUT_FLUSH__NOCHECK(float32_input_flush__nocheck, float32) +GEN_INPUT_FLUSH__NOCHECK(float64_input_flush__nocheck, float64) +#undef GEN_INPUT_FLUSH__NOCHECK + +#define GEN_INPUT_FLUSH1(name, soft_t) \ + static inline void name(soft_t *a, float_status *s) \ + { \ + if (likely(!s->flush_inputs_to_zero)) { \ + return; \ + } \ + soft_t ## _input_flush__nocheck(a, s); \ + } + +GEN_INPUT_FLUSH1(float32_input_flush1, float32) +GEN_INPUT_FLUSH1(float64_input_flush1, float64) +#undef GEN_INPUT_FLUSH1 + +#define GEN_INPUT_FLUSH2(name, soft_t) \ + static inline void name(soft_t *a, soft_t *b, float_status *s) \ + { \ + if (likely(!s->flush_inputs_to_zero)) { \ + return; \ + } \ + soft_t ## _input_flush__nocheck(a, s); \ + soft_t ## _input_flush__nocheck(b, s); \ + } + +GEN_INPUT_FLUSH2(float32_input_flush2, float32) +GEN_INPUT_FLUSH2(float64_input_flush2, float64) +#undef GEN_INPUT_FLUSH2 + +#define GEN_INPUT_FLUSH3(name, soft_t) \ + static inline void name(soft_t *a, soft_t *b, soft_t *c, float_status *s) \ + { \ + if (likely(!s->flush_inputs_to_zero)) { \ + return; \ + } \ + soft_t ## _input_flush__nocheck(a, s); \ + soft_t ## _input_flush__nocheck(b, s); \ + soft_t ## _input_flush__nocheck(c, s); \ + } + +GEN_INPUT_FLUSH3(float32_input_flush3, float32) +GEN_INPUT_FLUSH3(float64_input_flush3, float64) +#undef GEN_INPUT_FLUSH3 + +/* + * Choose whether to use fpclassify or float32/64_* primitives in the generated + * hardfloat functions. Each combination of number of inputs and float size + * gets its own value. + */ +#if defined(__x86_64__) +# define QEMU_HARDFLOAT_1F32_USE_FP 0 +# define QEMU_HARDFLOAT_1F64_USE_FP 1 +# define QEMU_HARDFLOAT_2F32_USE_FP 0 +# define QEMU_HARDFLOAT_2F64_USE_FP 1 +# define QEMU_HARDFLOAT_3F32_USE_FP 0 +# define QEMU_HARDFLOAT_3F64_USE_FP 1 +#else +# define QEMU_HARDFLOAT_1F32_USE_FP 0 +# define QEMU_HARDFLOAT_1F64_USE_FP 0 +# define QEMU_HARDFLOAT_2F32_USE_FP 0 +# define QEMU_HARDFLOAT_2F64_USE_FP 0 +# define QEMU_HARDFLOAT_3F32_USE_FP 0 +# define QEMU_HARDFLOAT_3F64_USE_FP 0 +#endif + +/* + * QEMU_HARDFLOAT_USE_ISINF chooses whether to use isinf() over + * float{32,64}_is_infinity when !USE_FP. + * On x86_64/aarch64, using the former over the latter can yield a ~6% speedup. + * On power64 however, using isinf() reduces fp-bench performance by up to 50%. + */ +#if defined(__x86_64__) || defined(__aarch64__) +# define QEMU_HARDFLOAT_USE_ISINF 1 +#else +# define QEMU_HARDFLOAT_USE_ISINF 0 +#endif + +/* + * Some targets clear the FP flags before most FP operations. This prevents + * the use of hardfloat, since hardfloat relies on the inexact flag being + * already set. + */ +#if defined(TARGET_PPC) || defined(__FAST_MATH__) +# if defined(__FAST_MATH__) +# warning disabling hardfloat due to -ffast-math: hardfloat requires an exact \ + IEEE implementation +# endif +# define QEMU_NO_HARDFLOAT 1 +# define QEMU_SOFTFLOAT_ATTR QEMU_FLATTEN +#else +# define QEMU_NO_HARDFLOAT 0 +# define QEMU_SOFTFLOAT_ATTR QEMU_FLATTEN __attribute__((noinline)) +#endif + +static inline bool can_use_fpu(const float_status *s) +{ + if (QEMU_NO_HARDFLOAT) { + return false; + } + return likely(s->float_exception_flags & float_flag_inexact && + s->float_rounding_mode == float_round_nearest_even); +} + +/* + * Hardfloat generation functions. Each operation can have two flavors: + * either using softfloat primitives (e.g. float32_is_zero_or_normal) for + * most condition checks, or native ones (e.g. fpclassify). + * + * The flavor is chosen by the callers. Instead of using macros, we rely on the + * compiler to propagate constants and inline everything into the callers. + * + * We only generate functions for operations with two inputs, since only + * these are common enough to justify consolidating them into common code. + */ + +typedef union { + float32 s; + float h; +} union_float32; + +typedef union { + float64 s; + double h; +} union_float64; + +typedef bool (*f32_check_fn)(union_float32 a, union_float32 b); +typedef bool (*f64_check_fn)(union_float64 a, union_float64 b); + +typedef float32 (*soft_f32_op2_fn)(float32 a, float32 b, float_status *s); +typedef float64 (*soft_f64_op2_fn)(float64 a, float64 b, float_status *s); +typedef float (*hard_f32_op2_fn)(float a, float b); +typedef double (*hard_f64_op2_fn)(double a, double b); + +/* 2-input is-zero-or-normal */ +static inline bool f32_is_zon2(union_float32 a, union_float32 b) +{ + if (QEMU_HARDFLOAT_2F32_USE_FP) { + /* + * Not using a temp variable for consecutive fpclassify calls ends up + * generating faster code. + */ + return (fpclassify(a.h) == FP_NORMAL || fpclassify(a.h) == FP_ZERO) && + (fpclassify(b.h) == FP_NORMAL || fpclassify(b.h) == FP_ZERO); + } + return float32_is_zero_or_normal(a.s) && + float32_is_zero_or_normal(b.s); +} + +static inline bool f64_is_zon2(union_float64 a, union_float64 b) +{ + if (QEMU_HARDFLOAT_2F64_USE_FP) { + return (fpclassify(a.h) == FP_NORMAL || fpclassify(a.h) == FP_ZERO) && + (fpclassify(b.h) == FP_NORMAL || fpclassify(b.h) == FP_ZERO); + } + return float64_is_zero_or_normal(a.s) && + float64_is_zero_or_normal(b.s); +} + +/* 3-input is-zero-or-normal */ +static inline +bool f32_is_zon3(union_float32 a, union_float32 b, union_float32 c) +{ + if (QEMU_HARDFLOAT_3F32_USE_FP) { + return (fpclassify(a.h) == FP_NORMAL || fpclassify(a.h) == FP_ZERO) && + (fpclassify(b.h) == FP_NORMAL || fpclassify(b.h) == FP_ZERO) && + (fpclassify(c.h) == FP_NORMAL || fpclassify(c.h) == FP_ZERO); + } + return float32_is_zero_or_normal(a.s) && + float32_is_zero_or_normal(b.s) && + float32_is_zero_or_normal(c.s); +} + +static inline +bool f64_is_zon3(union_float64 a, union_float64 b, union_float64 c) +{ + if (QEMU_HARDFLOAT_3F64_USE_FP) { + return (fpclassify(a.h) == FP_NORMAL || fpclassify(a.h) == FP_ZERO) && + (fpclassify(b.h) == FP_NORMAL || fpclassify(b.h) == FP_ZERO) && + (fpclassify(c.h) == FP_NORMAL || fpclassify(c.h) == FP_ZERO); + } + return float64_is_zero_or_normal(a.s) && + float64_is_zero_or_normal(b.s) && + float64_is_zero_or_normal(c.s); +} + +static inline bool f32_is_inf(union_float32 a) +{ + if (QEMU_HARDFLOAT_USE_ISINF) { + return isinf(a.h); + } + return float32_is_infinity(a.s); +} + +static inline bool f64_is_inf(union_float64 a) +{ + if (QEMU_HARDFLOAT_USE_ISINF) { + return isinf(a.h); + } + return float64_is_infinity(a.s); +} + +/* Note: @fast_test and @post can be NULL */ +static inline float32 +float32_gen2(float32 xa, float32 xb, float_status *s, + hard_f32_op2_fn hard, soft_f32_op2_fn soft, + f32_check_fn pre, f32_check_fn post, + f32_check_fn fast_test, soft_f32_op2_fn fast_op) +{ + union_float32 ua, ub, ur; + + ua.s = xa; + ub.s = xb; + + if (unlikely(!can_use_fpu(s))) { + goto soft; + } + + float32_input_flush2(&ua.s, &ub.s, s); + if (unlikely(!pre(ua, ub))) { + goto soft; + } + if (fast_test && fast_test(ua, ub)) { + return fast_op(ua.s, ub.s, s); + } + + ur.h = hard(ua.h, ub.h); + if (unlikely(f32_is_inf(ur))) { + s->float_exception_flags |= float_flag_overflow; + } else if (unlikely(fabsf(ur.h) <= FLT_MIN)) { + if (post == NULL || post(ua, ub)) { + goto soft; + } + } + return ur.s; + + soft: + return soft(ua.s, ub.s, s); +} + +static inline float64 +float64_gen2(float64 xa, float64 xb, float_status *s, + hard_f64_op2_fn hard, soft_f64_op2_fn soft, + f64_check_fn pre, f64_check_fn post, + f64_check_fn fast_test, soft_f64_op2_fn fast_op) +{ + union_float64 ua, ub, ur; + + ua.s = xa; + ub.s = xb; + + if (unlikely(!can_use_fpu(s))) { + goto soft; + } + + float64_input_flush2(&ua.s, &ub.s, s); + if (unlikely(!pre(ua, ub))) { + goto soft; + } + if (fast_test && fast_test(ua, ub)) { + return fast_op(ua.s, ub.s, s); + } + + ur.h = hard(ua.h, ub.h); + if (unlikely(f64_is_inf(ur))) { + s->float_exception_flags |= float_flag_overflow; + } else if (unlikely(fabs(ur.h) <= DBL_MIN)) { + if (post == NULL || post(ua, ub)) { + goto soft; + } + } + return ur.s; + + soft: + return soft(ua.s, ub.s, s); +} + /*---------------------------------------------------------------------------- | Returns the fraction bits of the half-precision floating-point value `a'. *----------------------------------------------------------------------------*/ @@ -336,8 +655,8 @@ static inline float64 float64_pack_raw(FloatParts p) #include "softfloat-specialize.h" /* Canonicalize EXP and FRAC, setting CLS. */ -static FloatParts canonicalize(FloatParts part, const FloatFmt *parm, - float_status *status) +static FloatParts sf_canonicalize(FloatParts part, const FloatFmt *parm, + float_status *status) { if (part.exp == parm->exp_max && !parm->arm_althp) { if (part.frac == 0) { @@ -513,7 +832,7 @@ static FloatParts round_canonical(FloatParts p, float_status *s, static FloatParts float16a_unpack_canonical(float16 f, float_status *s, const FloatFmt *params) { - return canonicalize(float16_unpack_raw(f), params, s); + return sf_canonicalize(float16_unpack_raw(f), params, s); } static FloatParts float16_unpack_canonical(float16 f, float_status *s) @@ -534,7 +853,7 @@ static float16 float16_round_pack_canonical(FloatParts p, float_status *s) static FloatParts float32_unpack_canonical(float32 f, float_status *s) { - return canonicalize(float32_unpack_raw(f), &float32_params, s); + return sf_canonicalize(float32_unpack_raw(f), &float32_params, s); } static float32 float32_round_pack_canonical(FloatParts p, float_status *s) @@ -544,7 +863,7 @@ static float32 float32_round_pack_canonical(FloatParts p, float_status *s) static FloatParts float64_unpack_canonical(float64 f, float_status *s) { - return canonicalize(float64_unpack_raw(f), &float64_params, s); + return sf_canonicalize(float64_unpack_raw(f), &float64_params, s); } static float64 float64_round_pack_canonical(FloatParts p, float_status *s) @@ -735,49 +1054,128 @@ float16 QEMU_FLATTEN float16_add(float16 a, float16 b, float_status *status) return float16_round_pack_canonical(pr, status); } -float32 QEMU_FLATTEN float32_add(float32 a, float32 b, float_status *status) +float16 QEMU_FLATTEN float16_sub(float16 a, float16 b, float_status *status) +{ + FloatParts pa = float16_unpack_canonical(a, status); + FloatParts pb = float16_unpack_canonical(b, status); + FloatParts pr = addsub_floats(pa, pb, true, status); + + return float16_round_pack_canonical(pr, status); +} + +static float32 QEMU_SOFTFLOAT_ATTR +soft_f32_addsub(float32 a, float32 b, bool subtract, float_status *status) { FloatParts pa = float32_unpack_canonical(a, status); FloatParts pb = float32_unpack_canonical(b, status); - FloatParts pr = addsub_floats(pa, pb, false, status); + FloatParts pr = addsub_floats(pa, pb, subtract, status); return float32_round_pack_canonical(pr, status); } -float64 QEMU_FLATTEN float64_add(float64 a, float64 b, float_status *status) +static inline float32 soft_f32_add(float32 a, float32 b, float_status *status) +{ + return soft_f32_addsub(a, b, false, status); +} + +static inline float32 soft_f32_sub(float32 a, float32 b, float_status *status) +{ + return soft_f32_addsub(a, b, true, status); +} + +static float64 QEMU_SOFTFLOAT_ATTR +soft_f64_addsub(float64 a, float64 b, bool subtract, float_status *status) { FloatParts pa = float64_unpack_canonical(a, status); FloatParts pb = float64_unpack_canonical(b, status); - FloatParts pr = addsub_floats(pa, pb, false, status); + FloatParts pr = addsub_floats(pa, pb, subtract, status); return float64_round_pack_canonical(pr, status); } -float16 QEMU_FLATTEN float16_sub(float16 a, float16 b, float_status *status) +static inline float64 soft_f64_add(float64 a, float64 b, float_status *status) { - FloatParts pa = float16_unpack_canonical(a, status); - FloatParts pb = float16_unpack_canonical(b, status); - FloatParts pr = addsub_floats(pa, pb, true, status); + return soft_f64_addsub(a, b, false, status); +} - return float16_round_pack_canonical(pr, status); +static inline float64 soft_f64_sub(float64 a, float64 b, float_status *status) +{ + return soft_f64_addsub(a, b, true, status); } -float32 QEMU_FLATTEN float32_sub(float32 a, float32 b, float_status *status) +static float hard_f32_add(float a, float b) { - FloatParts pa = float32_unpack_canonical(a, status); - FloatParts pb = float32_unpack_canonical(b, status); - FloatParts pr = addsub_floats(pa, pb, true, status); + return a + b; +} - return float32_round_pack_canonical(pr, status); +static float hard_f32_sub(float a, float b) +{ + return a - b; } -float64 QEMU_FLATTEN float64_sub(float64 a, float64 b, float_status *status) +static double hard_f64_add(double a, double b) { - FloatParts pa = float64_unpack_canonical(a, status); - FloatParts pb = float64_unpack_canonical(b, status); - FloatParts pr = addsub_floats(pa, pb, true, status); + return a + b; +} - return float64_round_pack_canonical(pr, status); +static double hard_f64_sub(double a, double b) +{ + return a - b; +} + +static bool f32_addsub_post(union_float32 a, union_float32 b) +{ + if (QEMU_HARDFLOAT_2F32_USE_FP) { + return !(fpclassify(a.h) == FP_ZERO && fpclassify(b.h) == FP_ZERO); + } + return !(float32_is_zero(a.s) && float32_is_zero(b.s)); +} + +static bool f64_addsub_post(union_float64 a, union_float64 b) +{ + if (QEMU_HARDFLOAT_2F64_USE_FP) { + return !(fpclassify(a.h) == FP_ZERO && fpclassify(b.h) == FP_ZERO); + } else { + return !(float64_is_zero(a.s) && float64_is_zero(b.s)); + } +} + +static float32 float32_addsub(float32 a, float32 b, float_status *s, + hard_f32_op2_fn hard, soft_f32_op2_fn soft) +{ + return float32_gen2(a, b, s, hard, soft, + f32_is_zon2, f32_addsub_post, NULL, NULL); +} + +static float64 float64_addsub(float64 a, float64 b, float_status *s, + hard_f64_op2_fn hard, soft_f64_op2_fn soft) +{ + return float64_gen2(a, b, s, hard, soft, + f64_is_zon2, f64_addsub_post, NULL, NULL); +} + +float32 QEMU_FLATTEN +float32_add(float32 a, float32 b, float_status *s) +{ + return float32_addsub(a, b, s, hard_f32_add, soft_f32_add); +} + +float32 QEMU_FLATTEN +float32_sub(float32 a, float32 b, float_status *s) +{ + return float32_addsub(a, b, s, hard_f32_sub, soft_f32_sub); +} + +float64 QEMU_FLATTEN +float64_add(float64 a, float64 b, float_status *s) +{ + return float64_addsub(a, b, s, hard_f64_add, soft_f64_add); +} + +float64 QEMU_FLATTEN +float64_sub(float64 a, float64 b, float_status *s) +{ + return float64_addsub(a, b, s, hard_f64_sub, soft_f64_sub); } /* @@ -838,7 +1236,8 @@ float16 QEMU_FLATTEN float16_mul(float16 a, float16 b, float_status *status) return float16_round_pack_canonical(pr, status); } -float32 QEMU_FLATTEN float32_mul(float32 a, float32 b, float_status *status) +static float32 QEMU_SOFTFLOAT_ATTR +soft_f32_mul(float32 a, float32 b, float_status *status) { FloatParts pa = float32_unpack_canonical(a, status); FloatParts pb = float32_unpack_canonical(b, status); @@ -847,7 +1246,8 @@ float32 QEMU_FLATTEN float32_mul(float32 a, float32 b, float_status *status) return float32_round_pack_canonical(pr, status); } -float64 QEMU_FLATTEN float64_mul(float64 a, float64 b, float_status *status) +static float64 QEMU_SOFTFLOAT_ATTR +soft_f64_mul(float64 a, float64 b, float_status *status) { FloatParts pa = float64_unpack_canonical(a, status); FloatParts pb = float64_unpack_canonical(b, status); @@ -856,6 +1256,54 @@ float64 QEMU_FLATTEN float64_mul(float64 a, float64 b, float_status *status) return float64_round_pack_canonical(pr, status); } +static float hard_f32_mul(float a, float b) +{ + return a * b; +} + +static double hard_f64_mul(double a, double b) +{ + return a * b; +} + +static bool f32_mul_fast_test(union_float32 a, union_float32 b) +{ + return float32_is_zero(a.s) || float32_is_zero(b.s); +} + +static bool f64_mul_fast_test(union_float64 a, union_float64 b) +{ + return float64_is_zero(a.s) || float64_is_zero(b.s); +} + +static float32 f32_mul_fast_op(float32 a, float32 b, float_status *s) +{ + bool signbit = float32_is_neg(a) ^ float32_is_neg(b); + + return float32_set_sign(float32_zero, signbit); +} + +static float64 f64_mul_fast_op(float64 a, float64 b, float_status *s) +{ + bool signbit = float64_is_neg(a) ^ float64_is_neg(b); + + return float64_set_sign(float64_zero, signbit); +} + +float32 QEMU_FLATTEN +float32_mul(float32 a, float32 b, float_status *s) +{ + return float32_gen2(a, b, s, hard_f32_mul, soft_f32_mul, + f32_is_zon2, NULL, f32_mul_fast_test, f32_mul_fast_op); +} + +float64 QEMU_FLATTEN +float64_mul(float64 a, float64 b, float_status *s) +{ + return float64_gen2(a, b, s, hard_f64_mul, soft_f64_mul, + f64_is_zon2, NULL, f64_mul_fast_test, f64_mul_fast_op); +} + /* * Returns the result of multiplying the floating-point values `a' and * `b' then adding 'c', with no intermediate rounding step after the @@ -1070,8 +1518,9 @@ float16 QEMU_FLATTEN float16_muladd(float16 a, float16 b, float16 c, return float16_round_pack_canonical(pr, status); } -float32 QEMU_FLATTEN float32_muladd(float32 a, float32 b, float32 c, - int flags, float_status *status) +static float32 QEMU_SOFTFLOAT_ATTR +soft_f32_muladd(float32 a, float32 b, float32 c, int flags, + float_status *status) { FloatParts pa = float32_unpack_canonical(a, status); FloatParts pb = float32_unpack_canonical(b, status); @@ -1081,8 +1530,9 @@ float32 QEMU_FLATTEN float32_muladd(float32 a, float32 b, float32 c, return float32_round_pack_canonical(pr, status); } -float64 QEMU_FLATTEN float64_muladd(float64 a, float64 b, float64 c, - int flags, float_status *status) +static float64 QEMU_SOFTFLOAT_ATTR +soft_f64_muladd(float64 a, float64 b, float64 c, int flags, + float_status *status) { FloatParts pa = float64_unpack_canonical(a, status); FloatParts pb = float64_unpack_canonical(b, status); @@ -1092,6 +1542,128 @@ float64 QEMU_FLATTEN float64_muladd(float64 a, float64 b, float64 c, return float64_round_pack_canonical(pr, status); } +float32 QEMU_FLATTEN +float32_muladd(float32 xa, float32 xb, float32 xc, int flags, float_status *s) +{ + union_float32 ua, ub, uc, ur; + + ua.s = xa; + ub.s = xb; + uc.s = xc; + + if (unlikely(!can_use_fpu(s))) { + goto soft; + } + if (unlikely(flags & float_muladd_halve_result)) { + goto soft; + } + + float32_input_flush3(&ua.s, &ub.s, &uc.s, s); + if (unlikely(!f32_is_zon3(ua, ub, uc))) { + goto soft; + } + /* + * When (a || b) == 0, there's no need to check for under/over flow, + * since we know the addend is (normal || 0) and the product is 0. + */ + if (float32_is_zero(ua.s) || float32_is_zero(ub.s)) { + union_float32 up; + bool prod_sign; + + prod_sign = float32_is_neg(ua.s) ^ float32_is_neg(ub.s); + prod_sign ^= !!(flags & float_muladd_negate_product); + up.s = float32_set_sign(float32_zero, prod_sign); + + if (flags & float_muladd_negate_c) { + uc.h = -uc.h; + } + ur.h = up.h + uc.h; + } else { + if (flags & float_muladd_negate_product) { + ua.h = -ua.h; + } + if (flags & float_muladd_negate_c) { + uc.h = -uc.h; + } + + ur.h = fmaf(ua.h, ub.h, uc.h); + + if (unlikely(f32_is_inf(ur))) { + s->float_exception_flags |= float_flag_overflow; + } else if (unlikely(fabsf(ur.h) <= FLT_MIN)) { + goto soft; + } + } + if (flags & float_muladd_negate_result) { + return float32_chs(ur.s); + } + return ur.s; + + soft: + return soft_f32_muladd(ua.s, ub.s, uc.s, flags, s); +} + +float64 QEMU_FLATTEN +float64_muladd(float64 xa, float64 xb, float64 xc, int flags, float_status *s) +{ + union_float64 ua, ub, uc, ur; + + ua.s = xa; + ub.s = xb; + uc.s = xc; + + if (unlikely(!can_use_fpu(s))) { + goto soft; + } + if (unlikely(flags & float_muladd_halve_result)) { + goto soft; + } + + float64_input_flush3(&ua.s, &ub.s, &uc.s, s); + if (unlikely(!f64_is_zon3(ua, ub, uc))) { + goto soft; + } + /* + * When (a || b) == 0, there's no need to check for under/over flow, + * since we know the addend is (normal || 0) and the product is 0. + */ + if (float64_is_zero(ua.s) || float64_is_zero(ub.s)) { + union_float64 up; + bool prod_sign; + + prod_sign = float64_is_neg(ua.s) ^ float64_is_neg(ub.s); + prod_sign ^= !!(flags & float_muladd_negate_product); + up.s = float64_set_sign(float64_zero, prod_sign); + + if (flags & float_muladd_negate_c) { + uc.h = -uc.h; + } + ur.h = up.h + uc.h; + } else { + if (flags & float_muladd_negate_product) { + ua.h = -ua.h; + } + if (flags & float_muladd_negate_c) { + uc.h = -uc.h; + } + + ur.h = fma(ua.h, ub.h, uc.h); + + if (unlikely(f64_is_inf(ur))) { + s->float_exception_flags |= float_flag_overflow; + } else if (unlikely(fabs(ur.h) <= FLT_MIN)) { + goto soft; + } + } + if (flags & float_muladd_negate_result) { + return float64_chs(ur.s); + } + return ur.s; + + soft: + return soft_f64_muladd(ua.s, ub.s, uc.s, flags, s); +} + /* * Returns the result of dividing the floating-point value `a' by the * corresponding value `b'. The operation is performed according to @@ -1180,7 +1752,8 @@ float16 float16_div(float16 a, float16 b, float_status *status) return float16_round_pack_canonical(pr, status); } -float32 float32_div(float32 a, float32 b, float_status *status) +static float32 QEMU_SOFTFLOAT_ATTR +soft_f32_div(float32 a, float32 b, float_status *status) { FloatParts pa = float32_unpack_canonical(a, status); FloatParts pb = float32_unpack_canonical(b, status); @@ -1189,7 +1762,8 @@ float32 float32_div(float32 a, float32 b, float_status *status) return float32_round_pack_canonical(pr, status); } -float64 float64_div(float64 a, float64 b, float_status *status) +static float64 QEMU_SOFTFLOAT_ATTR +soft_f64_div(float64 a, float64 b, float_status *status) { FloatParts pa = float64_unpack_canonical(a, status); FloatParts pb = float64_unpack_canonical(b, status); @@ -1198,6 +1772,64 @@ float64 float64_div(float64 a, float64 b, float_status *status) return float64_round_pack_canonical(pr, status); } +static float hard_f32_div(float a, float b) +{ + return a / b; +} + +static double hard_f64_div(double a, double b) +{ + return a / b; +} + +static bool f32_div_pre(union_float32 a, union_float32 b) +{ + if (QEMU_HARDFLOAT_2F32_USE_FP) { + return (fpclassify(a.h) == FP_NORMAL || fpclassify(a.h) == FP_ZERO) && + fpclassify(b.h) == FP_NORMAL; + } + return float32_is_zero_or_normal(a.s) && float32_is_normal(b.s); +} + +static bool f64_div_pre(union_float64 a, union_float64 b) +{ + if (QEMU_HARDFLOAT_2F64_USE_FP) { + return (fpclassify(a.h) == FP_NORMAL || fpclassify(a.h) == FP_ZERO) && + fpclassify(b.h) == FP_NORMAL; + } + return float64_is_zero_or_normal(a.s) && float64_is_normal(b.s); +} + +static bool f32_div_post(union_float32 a, union_float32 b) +{ + if (QEMU_HARDFLOAT_2F32_USE_FP) { + return fpclassify(a.h) != FP_ZERO; + } + return !float32_is_zero(a.s); +} + +static bool f64_div_post(union_float64 a, union_float64 b) +{ + if (QEMU_HARDFLOAT_2F64_USE_FP) { + return fpclassify(a.h) != FP_ZERO; + } + return !float64_is_zero(a.s); +} + +float32 QEMU_FLATTEN +float32_div(float32 a, float32 b, float_status *s) +{ + return float32_gen2(a, b, s, hard_f32_div, soft_f32_div, + f32_div_pre, f32_div_post, NULL, NULL); +} + +float64 QEMU_FLATTEN +float64_div(float64 a, float64 b, float_status *s) +{ + return float64_gen2(a, b, s, hard_f64_div, soft_f64_div, + f64_div_pre, f64_div_post, NULL, NULL); +} + /* * Float to Float conversions * @@ -2271,28 +2903,109 @@ static int compare_floats(FloatParts a, FloatParts b, bool is_quiet, } } -#define COMPARE(sz) \ -int float ## sz ## _compare(float ## sz a, float ## sz b, \ - float_status *s) \ +#define COMPARE(name, attr, sz) \ +static int attr \ +name(float ## sz a, float ## sz b, bool is_quiet, float_status *s) \ { \ FloatParts pa = float ## sz ## _unpack_canonical(a, s); \ FloatParts pb = float ## sz ## _unpack_canonical(b, s); \ - return compare_floats(pa, pb, false, s); \ -} \ -int float ## sz ## _compare_quiet(float ## sz a, float ## sz b, \ - float_status *s) \ -{ \ - FloatParts pa = float ## sz ## _unpack_canonical(a, s); \ - FloatParts pb = float ## sz ## _unpack_canonical(b, s); \ - return compare_floats(pa, pb, true, s); \ + return compare_floats(pa, pb, is_quiet, s); \ } -COMPARE(16) -COMPARE(32) -COMPARE(64) +COMPARE(soft_f16_compare, QEMU_FLATTEN, 16) +COMPARE(soft_f32_compare, QEMU_SOFTFLOAT_ATTR, 32) +COMPARE(soft_f64_compare, QEMU_SOFTFLOAT_ATTR, 64) #undef COMPARE +int float16_compare(float16 a, float16 b, float_status *s) +{ + return soft_f16_compare(a, b, false, s); +} + +int float16_compare_quiet(float16 a, float16 b, float_status *s) +{ + return soft_f16_compare(a, b, true, s); +} + +static int QEMU_FLATTEN +f32_compare(float32 xa, float32 xb, bool is_quiet, float_status *s) +{ + union_float32 ua, ub; + + ua.s = xa; + ub.s = xb; + + if (QEMU_NO_HARDFLOAT) { + goto soft; + } + + float32_input_flush2(&ua.s, &ub.s, s); + if (isgreaterequal(ua.h, ub.h)) { + if (isgreater(ua.h, ub.h)) { + return float_relation_greater; + } + return float_relation_equal; + } + if (likely(isless(ua.h, ub.h))) { + return float_relation_less; + } + /* The only condition remaining is unordered. + * Fall through to set flags. + */ + soft: + return soft_f32_compare(ua.s, ub.s, is_quiet, s); +} + +int float32_compare(float32 a, float32 b, float_status *s) +{ + return f32_compare(a, b, false, s); +} + +int float32_compare_quiet(float32 a, float32 b, float_status *s) +{ + return f32_compare(a, b, true, s); +} + +static int QEMU_FLATTEN +f64_compare(float64 xa, float64 xb, bool is_quiet, float_status *s) +{ + union_float64 ua, ub; + + ua.s = xa; + ub.s = xb; + + if (QEMU_NO_HARDFLOAT) { + goto soft; + } + + float64_input_flush2(&ua.s, &ub.s, s); + if (isgreaterequal(ua.h, ub.h)) { + if (isgreater(ua.h, ub.h)) { + return float_relation_greater; + } + return float_relation_equal; + } + if (likely(isless(ua.h, ub.h))) { + return float_relation_less; + } + /* The only condition remaining is unordered. + * Fall through to set flags. + */ + soft: + return soft_f64_compare(ua.s, ub.s, is_quiet, s); +} + +int float64_compare(float64 a, float64 b, float_status *s) +{ + return f64_compare(a, b, false, s); +} + +int float64_compare_quiet(float64 a, float64 b, float_status *s) +{ + return f64_compare(a, b, true, s); +} + /* Multiply A by 2 raised to the power N. */ static FloatParts scalbn_decomposed(FloatParts a, int n, float_status *s) { @@ -2412,20 +3125,76 @@ float16 QEMU_FLATTEN float16_sqrt(float16 a, float_status *status) return float16_round_pack_canonical(pr, status); } -float32 QEMU_FLATTEN float32_sqrt(float32 a, float_status *status) +static float32 QEMU_SOFTFLOAT_ATTR +soft_f32_sqrt(float32 a, float_status *status) { FloatParts pa = float32_unpack_canonical(a, status); FloatParts pr = sqrt_float(pa, status, &float32_params); return float32_round_pack_canonical(pr, status); } -float64 QEMU_FLATTEN float64_sqrt(float64 a, float_status *status) +static float64 QEMU_SOFTFLOAT_ATTR +soft_f64_sqrt(float64 a, float_status *status) { FloatParts pa = float64_unpack_canonical(a, status); FloatParts pr = sqrt_float(pa, status, &float64_params); return float64_round_pack_canonical(pr, status); } +float32 QEMU_FLATTEN float32_sqrt(float32 xa, float_status *s) +{ + union_float32 ua, ur; + + ua.s = xa; + if (unlikely(!can_use_fpu(s))) { + goto soft; + } + + float32_input_flush1(&ua.s, s); + if (QEMU_HARDFLOAT_1F32_USE_FP) { + if (unlikely(!(fpclassify(ua.h) == FP_NORMAL || + fpclassify(ua.h) == FP_ZERO) || + signbit(ua.h))) { + goto soft; + } + } else if (unlikely(!float32_is_zero_or_normal(ua.s) || + float32_is_neg(ua.s))) { + goto soft; + } + ur.h = sqrtf(ua.h); + return ur.s; + + soft: + return soft_f32_sqrt(ua.s, s); +} + +float64 QEMU_FLATTEN float64_sqrt(float64 xa, float_status *s) +{ + union_float64 ua, ur; + + ua.s = xa; + if (unlikely(!can_use_fpu(s))) { + goto soft; + } + + float64_input_flush1(&ua.s, s); + if (QEMU_HARDFLOAT_1F64_USE_FP) { + if (unlikely(!(fpclassify(ua.h) == FP_NORMAL || + fpclassify(ua.h) == FP_ZERO) || + signbit(ua.h))) { + goto soft; + } + } else if (unlikely(!float64_is_zero_or_normal(ua.s) || + float64_is_neg(ua.s))) { + goto soft; + } + ur.h = sqrt(ua.h); + return ur.s; + + soft: + return soft_f64_sqrt(ua.s, s); +} + /*---------------------------------------------------------------------------- | The pattern for a default generated NaN. *----------------------------------------------------------------------------*/ diff --git a/gitdm.config b/gitdm.config new file mode 100644 index 0000000000..7472d4b8be --- /dev/null +++ b/gitdm.config @@ -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 diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index 8fd9f9bbae..38a5e99cf3 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -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) diff --git a/target/tricore/fpu_helper.c b/target/tricore/fpu_helper.c index df162902d6..31df462e4a 100644 --- a/target/tricore/fpu_helper.c +++ b/target/tricore/fpu_helper.c @@ -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) { diff --git a/tests/acpi-utils.c b/tests/acpi-utils.c index 41dc1ea9b4..6dc8ca1a8c 100644 --- a/tests/acpi-utils.c +++ b/tests/acpi-utils.c @@ -32,7 +32,7 @@ uint8_t acpi_calc_checksum(const uint8_t *data, int len) return sum; } -uint32_t acpi_find_rsdp_address(void) +uint32_t acpi_find_rsdp_address(QTestState *qts) { uint32_t off; @@ -42,7 +42,7 @@ uint32_t acpi_find_rsdp_address(void) int i; for (i = 0; i < sizeof sig - 1; ++i) { - sig[i] = readb(off + i); + sig[i] = qtest_readb(qts, off + i); } if (!memcmp(sig, "RSD PTR ", sizeof sig)) { @@ -52,14 +52,15 @@ uint32_t acpi_find_rsdp_address(void) return off; } -void acpi_parse_rsdp_table(uint32_t addr, AcpiRsdpDescriptor *rsdp_table) +void acpi_parse_rsdp_table(QTestState *qts, uint32_t addr, + AcpiRsdpDescriptor *rsdp_table) { - ACPI_READ_FIELD(rsdp_table->signature, addr); + ACPI_READ_FIELD(qts, rsdp_table->signature, addr); ACPI_ASSERT_CMP64(rsdp_table->signature, "RSD PTR "); - ACPI_READ_FIELD(rsdp_table->checksum, addr); - ACPI_READ_ARRAY(rsdp_table->oem_id, addr); - ACPI_READ_FIELD(rsdp_table->revision, addr); - ACPI_READ_FIELD(rsdp_table->rsdt_physical_address, addr); - ACPI_READ_FIELD(rsdp_table->length, addr); + ACPI_READ_FIELD(qts, rsdp_table->checksum, addr); + ACPI_READ_ARRAY(qts, rsdp_table->oem_id, addr); + ACPI_READ_FIELD(qts, rsdp_table->revision, addr); + ACPI_READ_FIELD(qts, rsdp_table->rsdt_physical_address, addr); + ACPI_READ_FIELD(qts, rsdp_table->length, addr); } diff --git a/tests/acpi-utils.h b/tests/acpi-utils.h index ac52abd0dd..791bd5410e 100644 --- a/tests/acpi-utils.h +++ b/tests/acpi-utils.h @@ -28,34 +28,34 @@ typedef struct { bool tmp_files_retain; /* do not delete the temp asl/aml */ } AcpiSdtTable; -#define ACPI_READ_FIELD(field, addr) \ - do { \ - memread(addr, &field, sizeof(field)); \ - addr += sizeof(field); \ +#define ACPI_READ_FIELD(qts, field, addr) \ + do { \ + qtest_memread(qts, addr, &field, sizeof(field)); \ + addr += sizeof(field); \ } while (0) -#define ACPI_READ_ARRAY_PTR(arr, length, addr) \ - do { \ - int idx; \ - for (idx = 0; idx < length; ++idx) { \ - ACPI_READ_FIELD(arr[idx], addr); \ - } \ +#define ACPI_READ_ARRAY_PTR(qts, arr, length, addr) \ + do { \ + int idx; \ + for (idx = 0; idx < length; ++idx) { \ + ACPI_READ_FIELD(qts, arr[idx], addr); \ + } \ } while (0) -#define ACPI_READ_ARRAY(arr, addr) \ - ACPI_READ_ARRAY_PTR(arr, sizeof(arr) / sizeof(arr[0]), addr) +#define ACPI_READ_ARRAY(qts, arr, addr) \ + ACPI_READ_ARRAY_PTR(qts, arr, sizeof(arr) / sizeof(arr[0]), addr) -#define ACPI_READ_TABLE_HEADER(table, addr) \ +#define ACPI_READ_TABLE_HEADER(qts, table, addr) \ do { \ - ACPI_READ_FIELD((table)->signature, addr); \ - ACPI_READ_FIELD((table)->length, addr); \ - ACPI_READ_FIELD((table)->revision, addr); \ - ACPI_READ_FIELD((table)->checksum, addr); \ - ACPI_READ_ARRAY((table)->oem_id, addr); \ - ACPI_READ_ARRAY((table)->oem_table_id, addr); \ - ACPI_READ_FIELD((table)->oem_revision, addr); \ - ACPI_READ_ARRAY((table)->asl_compiler_id, addr); \ - ACPI_READ_FIELD((table)->asl_compiler_revision, addr); \ + ACPI_READ_FIELD(qts, (table)->signature, addr); \ + ACPI_READ_FIELD(qts, (table)->length, addr); \ + ACPI_READ_FIELD(qts, (table)->revision, addr); \ + ACPI_READ_FIELD(qts, (table)->checksum, addr); \ + ACPI_READ_ARRAY(qts, (table)->oem_id, addr); \ + ACPI_READ_ARRAY(qts, (table)->oem_table_id, addr); \ + ACPI_READ_FIELD(qts, (table)->oem_revision, addr); \ + ACPI_READ_ARRAY(qts, (table)->asl_compiler_id, addr); \ + ACPI_READ_FIELD(qts, (table)->asl_compiler_revision, addr); \ } while (0) #define ACPI_ASSERT_CMP(actual, expected) do { \ @@ -70,18 +70,11 @@ typedef struct { g_assert_cmpstr(ACPI_ASSERT_CMP_str, ==, expected); \ } while (0) -#define ACPI_READ_GENERIC_ADDRESS(field, addr) \ - do { \ - ACPI_READ_FIELD((field).space_id, addr); \ - ACPI_READ_FIELD((field).bit_width, addr); \ - ACPI_READ_FIELD((field).bit_offset, addr); \ - ACPI_READ_FIELD((field).access_width, addr); \ - ACPI_READ_FIELD((field).address, addr); \ - } while (0) uint8_t acpi_calc_checksum(const uint8_t *data, int len); -uint32_t acpi_find_rsdp_address(void); -void acpi_parse_rsdp_table(uint32_t addr, AcpiRsdpDescriptor *rsdp_table); +uint32_t acpi_find_rsdp_address(QTestState *qts); +void acpi_parse_rsdp_table(QTestState *qts, uint32_t addr, + AcpiRsdpDescriptor *rsdp_table); #endif /* TEST_ACPI_UTILS_H */ diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c index d661d9be62..bac4b0171b 100644 --- a/tests/bios-tables-test.c +++ b/tests/bios-tables-test.c @@ -39,6 +39,7 @@ typedef struct { struct smbios_21_entry_point smbios_ep_table; uint8_t *required_struct_types; int required_struct_types_len; + QTestState *qts; } test_data; static char disk[] = "tests/acpi-test-disk-XXXXXX"; @@ -78,7 +79,7 @@ static void free_test_data(test_data *data) static void test_acpi_rsdp_address(test_data *data) { - uint32_t off = acpi_find_rsdp_address(); + uint32_t off = acpi_find_rsdp_address(data->qts); g_assert_cmphex(off, <, 0x100000); data->rsdp_addr = off; } @@ -88,7 +89,7 @@ static void test_acpi_rsdp_table(test_data *data) AcpiRsdpDescriptor *rsdp_table = &data->rsdp_table; uint32_t addr = data->rsdp_addr; - acpi_parse_rsdp_table(addr, rsdp_table); + acpi_parse_rsdp_table(data->qts, addr, rsdp_table); /* rsdp checksum is not for the whole table, but for the first 20 bytes */ g_assert(!acpi_calc_checksum((uint8_t *)rsdp_table, 20)); @@ -104,7 +105,7 @@ static void test_acpi_rsdt_table(test_data *data) uint32_t rsdt_table_length; /* read the header */ - ACPI_READ_TABLE_HEADER(rsdt_table, addr); + ACPI_READ_TABLE_HEADER(data->qts, rsdt_table, addr); ACPI_ASSERT_CMP(rsdt_table->signature, "RSDT"); rsdt_table_length = le32_to_cpu(rsdt_table->length); @@ -116,7 +117,7 @@ static void test_acpi_rsdt_table(test_data *data) /* get the addresses of the tables pointed by rsdt */ tables = g_new0(uint32_t, tables_nr); - ACPI_READ_ARRAY_PTR(tables, tables_nr, addr); + ACPI_READ_ARRAY_PTR(data->qts, tables, tables_nr, addr); checksum = acpi_calc_checksum((uint8_t *)rsdt_table, rsdt_table_length) + acpi_calc_checksum((uint8_t *)tables, @@ -135,11 +136,11 @@ static void fadt_fetch_facs_and_dsdt_ptrs(test_data *data) /* FADT table comes first */ addr = le32_to_cpu(data->rsdt_tables_addr[0]); - ACPI_READ_TABLE_HEADER(&hdr, addr); + ACPI_READ_TABLE_HEADER(data->qts, &hdr, addr); ACPI_ASSERT_CMP(hdr.signature, "FACP"); - ACPI_READ_FIELD(data->facs_addr, addr); - ACPI_READ_FIELD(data->dsdt_addr, addr); + ACPI_READ_FIELD(data->qts, data->facs_addr, addr); + ACPI_READ_FIELD(data->qts, data->dsdt_addr, addr); } static void sanitize_fadt_ptrs(test_data *data) @@ -182,13 +183,13 @@ static void test_acpi_facs_table(test_data *data) AcpiFacsDescriptorRev1 *facs_table = &data->facs_table; uint32_t addr = le32_to_cpu(data->facs_addr); - ACPI_READ_FIELD(facs_table->signature, addr); - ACPI_READ_FIELD(facs_table->length, addr); - ACPI_READ_FIELD(facs_table->hardware_signature, addr); - ACPI_READ_FIELD(facs_table->firmware_waking_vector, addr); - ACPI_READ_FIELD(facs_table->global_lock, addr); - ACPI_READ_FIELD(facs_table->flags, addr); - ACPI_READ_ARRAY(facs_table->resverved3, addr); + ACPI_READ_FIELD(data->qts, facs_table->signature, addr); + ACPI_READ_FIELD(data->qts, facs_table->length, addr); + ACPI_READ_FIELD(data->qts, facs_table->hardware_signature, addr); + ACPI_READ_FIELD(data->qts, facs_table->firmware_waking_vector, addr); + ACPI_READ_FIELD(data->qts, facs_table->global_lock, addr); + ACPI_READ_FIELD(data->qts, facs_table->flags, addr); + ACPI_READ_ARRAY(data->qts, facs_table->resverved3, addr); ACPI_ASSERT_CMP(facs_table->signature, "FACS"); } @@ -197,17 +198,17 @@ static void test_acpi_facs_table(test_data *data) * load ACPI table at @addr into table descriptor @sdt_table * and check that header checksum matches actual one. */ -static void fetch_table(AcpiSdtTable *sdt_table, uint32_t addr) +static void fetch_table(QTestState *qts, AcpiSdtTable *sdt_table, uint32_t addr) { uint8_t checksum; memset(sdt_table, 0, sizeof(*sdt_table)); - ACPI_READ_TABLE_HEADER(&sdt_table->header, addr); + ACPI_READ_TABLE_HEADER(qts, &sdt_table->header, addr); sdt_table->aml_len = le32_to_cpu(sdt_table->header.length) - sizeof(AcpiTableHeader); sdt_table->aml = g_malloc0(sdt_table->aml_len); - ACPI_READ_ARRAY_PTR(sdt_table->aml, sdt_table->aml_len, addr); + ACPI_READ_ARRAY_PTR(qts, sdt_table->aml, sdt_table->aml_len, addr); checksum = acpi_calc_checksum((uint8_t *)sdt_table, sizeof(AcpiTableHeader)) + @@ -221,7 +222,7 @@ static void test_acpi_dsdt_table(test_data *data) AcpiSdtTable dsdt_table; uint32_t addr = le32_to_cpu(data->dsdt_addr); - fetch_table(&dsdt_table, addr); + fetch_table(data->qts, &dsdt_table, addr); ACPI_ASSERT_CMP(dsdt_table.header.signature, "DSDT"); /* Since DSDT isn't in RSDT, add DSDT to ASL test tables list manually */ @@ -239,7 +240,7 @@ static void fetch_rsdt_referenced_tables(test_data *data) uint32_t addr; addr = le32_to_cpu(data->rsdt_tables_addr[i]); - fetch_table(&ssdt_table, addr); + fetch_table(data->qts, &ssdt_table, addr); /* Add table to ASL test tables list */ g_array_append_val(data->tables, ssdt_table); @@ -371,6 +372,9 @@ static GArray *load_expected_aml(test_data *data) gboolean ret; GArray *exp_tables = g_array_new(false, true, sizeof(AcpiSdtTable)); + if (getenv("V")) { + fputc('\n', stderr); + } for (i = 0; i < data->tables->len; ++i) { AcpiSdtTable exp_sdt; gchar *aml_file = NULL; @@ -385,7 +389,7 @@ try_again: aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir, data->machine, (gchar *)&sdt->header.signature, ext); if (getenv("V")) { - fprintf(stderr, "\nLooking for expected file '%s'\n", aml_file); + fprintf(stderr, "Looking for expected file '%s'\n", aml_file); } if (g_file_test(aml_file, G_FILE_TEST_EXISTS)) { exp_sdt.aml_file = aml_file; @@ -397,7 +401,7 @@ try_again: } g_assert(exp_sdt.aml_file); if (getenv("V")) { - fprintf(stderr, "\nUsing expected file '%s'\n", aml_file); + fprintf(stderr, "Using expected file '%s'\n", aml_file); } ret = g_file_get_contents(aml_file, &exp_sdt.aml, &exp_sdt.aml_len, &error); @@ -482,32 +486,32 @@ static bool smbios_ep_table_ok(test_data *data) struct smbios_21_entry_point *ep_table = &data->smbios_ep_table; uint32_t addr = data->smbios_ep_addr; - ACPI_READ_ARRAY(ep_table->anchor_string, addr); + ACPI_READ_ARRAY(data->qts, ep_table->anchor_string, addr); if (memcmp(ep_table->anchor_string, "_SM_", 4)) { return false; } - ACPI_READ_FIELD(ep_table->checksum, addr); - ACPI_READ_FIELD(ep_table->length, addr); - ACPI_READ_FIELD(ep_table->smbios_major_version, addr); - ACPI_READ_FIELD(ep_table->smbios_minor_version, addr); - ACPI_READ_FIELD(ep_table->max_structure_size, addr); - ACPI_READ_FIELD(ep_table->entry_point_revision, addr); - ACPI_READ_ARRAY(ep_table->formatted_area, addr); - ACPI_READ_ARRAY(ep_table->intermediate_anchor_string, addr); + ACPI_READ_FIELD(data->qts, ep_table->checksum, addr); + ACPI_READ_FIELD(data->qts, ep_table->length, addr); + ACPI_READ_FIELD(data->qts, ep_table->smbios_major_version, addr); + ACPI_READ_FIELD(data->qts, ep_table->smbios_minor_version, addr); + ACPI_READ_FIELD(data->qts, ep_table->max_structure_size, addr); + ACPI_READ_FIELD(data->qts, ep_table->entry_point_revision, addr); + ACPI_READ_ARRAY(data->qts, ep_table->formatted_area, addr); + ACPI_READ_ARRAY(data->qts, ep_table->intermediate_anchor_string, addr); if (memcmp(ep_table->intermediate_anchor_string, "_DMI_", 5)) { return false; } - ACPI_READ_FIELD(ep_table->intermediate_checksum, addr); - ACPI_READ_FIELD(ep_table->structure_table_length, addr); + ACPI_READ_FIELD(data->qts, ep_table->intermediate_checksum, addr); + ACPI_READ_FIELD(data->qts, ep_table->structure_table_length, addr); if (ep_table->structure_table_length == 0) { return false; } - ACPI_READ_FIELD(ep_table->structure_table_address, addr); - ACPI_READ_FIELD(ep_table->number_of_structures, addr); + ACPI_READ_FIELD(data->qts, ep_table->structure_table_address, addr); + ACPI_READ_FIELD(data->qts, ep_table->number_of_structures, addr); if (ep_table->number_of_structures == 0) { return false; } - ACPI_READ_FIELD(ep_table->smbios_bcd_revision, addr); + ACPI_READ_FIELD(data->qts, ep_table->smbios_bcd_revision, addr); if (acpi_calc_checksum((uint8_t *)ep_table, sizeof *ep_table) || acpi_calc_checksum((uint8_t *)ep_table + 0x10, sizeof *ep_table - 0x10)) { @@ -526,7 +530,7 @@ static void test_smbios_entry_point(test_data *data) int i; for (i = 0; i < sizeof sig - 1; ++i) { - sig[i] = readb(off + i); + sig[i] = qtest_readb(data->qts, off + i); } if (!memcmp(sig, "_SM_", sizeof sig)) { @@ -569,9 +573,9 @@ static void test_smbios_structs(test_data *data) for (i = 0; i < le16_to_cpu(ep_table->number_of_structures); i++) { /* grab type and formatted area length from struct header */ - type = readb(addr); + type = qtest_readb(data->qts, addr); g_assert_cmpuint(type, <=, SMBIOS_MAX_TYPE); - len = readb(addr + 1); + len = qtest_readb(data->qts, addr + 1); /* single-instance structs must not have been encountered before */ if (smbios_single_instance(type)) { @@ -583,7 +587,7 @@ static void test_smbios_structs(test_data *data) prv = crt = 1; while (prv || crt) { prv = crt; - crt = readb(addr + len); + crt = qtest_readb(data->qts, addr + len); len++; } @@ -620,9 +624,9 @@ static void test_acpi_one(const char *params, test_data *data) data->machine, "kvm:tcg", params ? params : "", disk); - qtest_start(args); + data->qts = qtest_init(args); - boot_sector_test(global_qtest); + boot_sector_test(data->qts); data->tables = g_array_new(false, true, sizeof(AcpiSdtTable)); test_acpi_rsdp_address(data); @@ -646,7 +650,8 @@ static void test_acpi_one(const char *params, test_data *data) test_smbios_entry_point(data); test_smbios_structs(data); - qtest_quit(global_qtest); + assert(!global_qtest); + qtest_quit(data->qts); g_free(args); } diff --git a/tests/boot-serial-test.c b/tests/boot-serial-test.c index 8ec6aed35d..58a48f39bf 100644 --- a/tests/boot-serial-test.c +++ b/tests/boot-serial-test.c @@ -128,13 +128,14 @@ static testdef_t tests[] = { { NULL } }; -static bool check_guest_output(const testdef_t *test, int fd) +static bool check_guest_output(QTestState *qts, const testdef_t *test, int fd) { - int i, nbr = 0, pos = 0, ccnt; + int nbr = 0, pos = 0, ccnt; + time_t now, start = time(NULL); char ch; - /* Poll serial output... Wait at most 360 seconds */ - for (i = 0; i < 36000; ++i) { + /* Poll serial output... */ + while (1) { ccnt = 0; while (ccnt++ < 512 && (nbr = read(fd, &ch, 1)) == 1) { if (ch == test->expect[pos]) { @@ -148,6 +149,15 @@ static bool check_guest_output(const testdef_t *test, int fd) } } g_assert(nbr >= 0); + /* Wait only if the child is still alive. */ + if (!qtest_probe_child(qts)) { + break; + } + /* Wait at most 360 seconds. */ + now = time(NULL); + if (now - start >= 360) { + break; + } g_usleep(10000); } @@ -161,6 +171,7 @@ static void test_machine(const void *data) char codetmp[] = "/tmp/qtest-boot-serial-cXXXXXX"; const char *codeparam = ""; const uint8_t *code = NULL; + QTestState *qts; int ser_fd; ser_fd = mkstemp(serialtmp); @@ -189,22 +200,22 @@ static void test_machine(const void *data) * Make sure that this test uses tcg if available: It is used as a * fast-enough smoketest for that. */ - global_qtest = qtest_initf("%s %s -M %s,accel=tcg:kvm " - "-chardev file,id=serial0,path=%s " - "-no-shutdown -serial chardev:serial0 %s", - codeparam, code ? codetmp : "", - test->machine, serialtmp, test->extra); + qts = qtest_initf("%s %s -M %s,accel=tcg:kvm -no-shutdown " + "-chardev file,id=serial0,path=%s " + "-serial chardev:serial0 %s", + codeparam, code ? codetmp : "", test->machine, + serialtmp, test->extra); if (code) { unlink(codetmp); } - if (!check_guest_output(test, ser_fd)) { + if (!check_guest_output(qts, test, ser_fd)) { g_error("Failed to find expected string. Please check '%s'", serialtmp); } unlink(serialtmp); - qtest_quit(global_qtest); + qtest_quit(qts); close(ser_fd); } diff --git a/tests/fp/.gitignore b/tests/fp/.gitignore index 8d45d18ac4..704fd42992 100644 --- a/tests/fp/.gitignore +++ b/tests/fp/.gitignore @@ -1 +1,2 @@ fp-test +fp-bench diff --git a/tests/fp/Makefile b/tests/fp/Makefile index d649a5a1db..5019dcdca0 100644 --- a/tests/fp/Makefile +++ b/tests/fp/Makefile @@ -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 diff --git a/tests/fp/fp-bench.c b/tests/fp/fp-bench.c new file mode 100644 index 0000000000..f5bc5edebf --- /dev/null +++ b/tests/fp/fp-bench.c @@ -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; +} diff --git a/tests/ivshmem-test.c b/tests/ivshmem-test.c index c37b196b32..089e268154 100644 --- a/tests/ivshmem-test.c +++ b/tests/ivshmem-test.c @@ -71,13 +71,10 @@ static const char* reg2str(enum Reg reg) { static inline unsigned in_reg(IVState *s, enum Reg reg) { const char *name = reg2str(reg); - QTestState *qtest = global_qtest; unsigned res; - global_qtest = s->qs->qts; res = qpci_io_readl(s->dev, s->reg_bar, reg); g_test_message("*%s -> %x\n", name, res); - global_qtest = qtest; return res; } @@ -85,35 +82,25 @@ static inline unsigned in_reg(IVState *s, enum Reg reg) static inline void out_reg(IVState *s, enum Reg reg, unsigned v) { const char *name = reg2str(reg); - QTestState *qtest = global_qtest; - global_qtest = s->qs->qts; g_test_message("%x -> *%s\n", v, name); qpci_io_writel(s->dev, s->reg_bar, reg, v); - global_qtest = qtest; } static inline void read_mem(IVState *s, uint64_t off, void *buf, size_t len) { - QTestState *qtest = global_qtest; - - global_qtest = s->qs->qts; qpci_memread(s->dev, s->mem_bar, off, buf, len); - global_qtest = qtest; } static inline void write_mem(IVState *s, uint64_t off, const void *buf, size_t len) { - QTestState *qtest = global_qtest; - - global_qtest = s->qs->qts; qpci_memwrite(s->dev, s->mem_bar, off, buf, len); - global_qtest = qtest; } static void cleanup_vm(IVState *s) { + assert(!global_qtest); g_free(s->dev); qtest_shutdown(s->qs); } @@ -131,7 +118,6 @@ static void setup_vm_cmd(IVState *s, const char *cmd, bool msix) g_printerr("ivshmem-test tests are only available on x86 or ppc64\n"); exit(EXIT_FAILURE); } - global_qtest = s->qs->qts; s->dev = get_device(s->qs->pcibus); s->reg_bar = qpci_iomap(s->dev, 0, &barsize); @@ -354,7 +340,6 @@ static void test_ivshmem_server(bool msi) g_assert_cmpint(vm1, !=, vm2); /* check number of MSI-X vectors */ - global_qtest = s1->qs->qts; if (msi) { ret = qpci_msix_table_size(s1->dev); g_assert_cmpuint(ret, ==, nvectors); @@ -377,7 +362,6 @@ static void test_ivshmem_server(bool msi) g_assert_cmpuint(ret, !=, 0); /* ping vm1 -> vm2 on vector 1 */ - global_qtest = s2->qs->qts; if (msi) { ret = qpci_msix_pending(s2->dev, 1); g_assert_cmpuint(ret, ==, 0); diff --git a/tests/libqos/pci-pc.c b/tests/libqos/pci-pc.c index 585f5289ec..a4fc02b5d8 100644 --- a/tests/libqos/pci-pc.c +++ b/tests/libqos/pci-pc.c @@ -29,90 +29,91 @@ typedef struct QPCIBusPC static uint8_t qpci_pc_pio_readb(QPCIBus *bus, uint32_t addr) { - return inb(addr); + return qtest_inb(bus->qts, addr); } static void qpci_pc_pio_writeb(QPCIBus *bus, uint32_t addr, uint8_t val) { - outb(addr, val); + qtest_outb(bus->qts, addr, val); } static uint16_t qpci_pc_pio_readw(QPCIBus *bus, uint32_t addr) { - return inw(addr); + return qtest_inw(bus->qts, addr); } static void qpci_pc_pio_writew(QPCIBus *bus, uint32_t addr, uint16_t val) { - outw(addr, val); + qtest_outw(bus->qts, addr, val); } static uint32_t qpci_pc_pio_readl(QPCIBus *bus, uint32_t addr) { - return inl(addr); + return qtest_inl(bus->qts, addr); } static void qpci_pc_pio_writel(QPCIBus *bus, uint32_t addr, uint32_t val) { - outl(addr, val); + qtest_outl(bus->qts, addr, val); } static uint64_t qpci_pc_pio_readq(QPCIBus *bus, uint32_t addr) { - return (uint64_t)inl(addr) + ((uint64_t)inl(addr + 4) << 32); + return (uint64_t)qtest_inl(bus->qts, addr) + + ((uint64_t)qtest_inl(bus->qts, addr + 4) << 32); } static void qpci_pc_pio_writeq(QPCIBus *bus, uint32_t addr, uint64_t val) { - outl(addr, val & 0xffffffff); - outl(addr + 4, val >> 32); + qtest_outl(bus->qts, addr, val & 0xffffffff); + qtest_outl(bus->qts, addr + 4, val >> 32); } static void qpci_pc_memread(QPCIBus *bus, uint32_t addr, void *buf, size_t len) { - memread(addr, buf, len); + qtest_memread(bus->qts, addr, buf, len); } static void qpci_pc_memwrite(QPCIBus *bus, uint32_t addr, const void *buf, size_t len) { - memwrite(addr, buf, len); + qtest_memwrite(bus->qts, addr, buf, len); } static uint8_t qpci_pc_config_readb(QPCIBus *bus, int devfn, uint8_t offset) { - outl(0xcf8, (1U << 31) | (devfn << 8) | offset); - return inb(0xcfc); + qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset); + return qtest_inb(bus->qts, 0xcfc); } static uint16_t qpci_pc_config_readw(QPCIBus *bus, int devfn, uint8_t offset) { - outl(0xcf8, (1U << 31) | (devfn << 8) | offset); - return inw(0xcfc); + qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset); + return qtest_inw(bus->qts, 0xcfc); } static uint32_t qpci_pc_config_readl(QPCIBus *bus, int devfn, uint8_t offset) { - outl(0xcf8, (1U << 31) | (devfn << 8) | offset); - return inl(0xcfc); + qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset); + return qtest_inl(bus->qts, 0xcfc); } static void qpci_pc_config_writeb(QPCIBus *bus, int devfn, uint8_t offset, uint8_t value) { - outl(0xcf8, (1U << 31) | (devfn << 8) | offset); - outb(0xcfc, value); + qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset); + qtest_outb(bus->qts, 0xcfc, value); } static void qpci_pc_config_writew(QPCIBus *bus, int devfn, uint8_t offset, uint16_t value) { - outl(0xcf8, (1U << 31) | (devfn << 8) | offset); - outw(0xcfc, value); + qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset); + qtest_outw(bus->qts, 0xcfc, value); } static void qpci_pc_config_writel(QPCIBus *bus, int devfn, uint8_t offset, uint32_t value) { - outl(0xcf8, (1U << 31) | (devfn << 8) | offset); - outl(0xcfc, value); + qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset); + qtest_outl(bus->qts, 0xcfc, value); } QPCIBus *qpci_init_pc(QTestState *qts, QGuestAllocator *alloc) diff --git a/tests/libqos/pci-spapr.c b/tests/libqos/pci-spapr.c index c0f7e6db9b..4c29889b0b 100644 --- a/tests/libqos/pci-spapr.c +++ b/tests/libqos/pci-spapr.c @@ -45,63 +45,63 @@ typedef struct QPCIBusSPAPR { static uint8_t qpci_spapr_pio_readb(QPCIBus *bus, uint32_t addr) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - return readb(s->pio_cpu_base + addr); + return qtest_readb(bus->qts, s->pio_cpu_base + addr); } static void qpci_spapr_pio_writeb(QPCIBus *bus, uint32_t addr, uint8_t val) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - writeb(s->pio_cpu_base + addr, val); + qtest_writeb(bus->qts, s->pio_cpu_base + addr, val); } static uint16_t qpci_spapr_pio_readw(QPCIBus *bus, uint32_t addr) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - return bswap16(readw(s->pio_cpu_base + addr)); + return bswap16(qtest_readw(bus->qts, s->pio_cpu_base + addr)); } static void qpci_spapr_pio_writew(QPCIBus *bus, uint32_t addr, uint16_t val) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - writew(s->pio_cpu_base + addr, bswap16(val)); + qtest_writew(bus->qts, s->pio_cpu_base + addr, bswap16(val)); } static uint32_t qpci_spapr_pio_readl(QPCIBus *bus, uint32_t addr) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - return bswap32(readl(s->pio_cpu_base + addr)); + return bswap32(qtest_readl(bus->qts, s->pio_cpu_base + addr)); } static void qpci_spapr_pio_writel(QPCIBus *bus, uint32_t addr, uint32_t val) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - writel(s->pio_cpu_base + addr, bswap32(val)); + qtest_writel(bus->qts, s->pio_cpu_base + addr, bswap32(val)); } static uint64_t qpci_spapr_pio_readq(QPCIBus *bus, uint32_t addr) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - return bswap64(readq(s->pio_cpu_base + addr)); + return bswap64(qtest_readq(bus->qts, s->pio_cpu_base + addr)); } static void qpci_spapr_pio_writeq(QPCIBus *bus, uint32_t addr, uint64_t val) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - writeq(s->pio_cpu_base + addr, bswap64(val)); + qtest_writeq(bus->qts, s->pio_cpu_base + addr, bswap64(val)); } static void qpci_spapr_memread(QPCIBus *bus, uint32_t addr, void *buf, size_t len) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - memread(s->mmio32_cpu_base + addr, buf, len); + qtest_memread(bus->qts, s->mmio32_cpu_base + addr, buf, len); } static void qpci_spapr_memwrite(QPCIBus *bus, uint32_t addr, const void *buf, size_t len) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - memwrite(s->mmio32_cpu_base + addr, buf, len); + qtest_memwrite(bus->qts, s->mmio32_cpu_base + addr, buf, len); } static uint8_t qpci_spapr_config_readb(QPCIBus *bus, int devfn, uint8_t offset) diff --git a/tests/libqtest.c b/tests/libqtest.c index 43be078518..1d75d3c936 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -39,10 +39,11 @@ struct QTestState { int fd; int qmp_fd; - bool irq_level[MAX_IRQ]; - GString *rx; pid_t qemu_pid; /* our child QEMU process */ + int wstatus; bool big_endian; + bool irq_level[MAX_IRQ]; + GString *rx; }; static GHookList abrt_hooks; @@ -96,36 +97,52 @@ static int socket_accept(int sock) return ret; } -static void kill_qemu(QTestState *s) +bool qtest_probe_child(QTestState *s) { - if (s->qemu_pid != -1) { - int wstatus = 0; - pid_t pid; + pid_t pid = s->qemu_pid; - kill(s->qemu_pid, SIGTERM); - TFR(pid = waitpid(s->qemu_pid, &wstatus, 0)); + if (pid != -1) { + pid = waitpid(pid, &s->wstatus, WNOHANG); + if (pid == 0) { + return true; + } + s->qemu_pid = -1; + } + return false; +} +static void kill_qemu(QTestState *s) +{ + pid_t pid = s->qemu_pid; + int wstatus; + + /* Skip wait if qtest_probe_child already reaped. */ + if (pid != -1) { + kill(pid, SIGTERM); + TFR(pid = waitpid(s->qemu_pid, &s->wstatus, 0)); assert(pid == s->qemu_pid); - /* - * We expect qemu to exit with status 0; anything else is - * fishy and should be logged with as much detail as possible. - */ - if (wstatus) { - if (WIFEXITED(wstatus)) { - fprintf(stderr, "%s:%d: kill_qemu() tried to terminate QEMU " - "process but encountered exit status %d\n", - __FILE__, __LINE__, WEXITSTATUS(wstatus)); - } else if (WIFSIGNALED(wstatus)) { - int sig = WTERMSIG(wstatus); - const char *signame = strsignal(sig) ?: "unknown ???"; - const char *dump = WCOREDUMP(wstatus) ? " (core dumped)" : ""; - - fprintf(stderr, "%s:%d: kill_qemu() detected QEMU death " - "from signal %d (%s)%s\n", - __FILE__, __LINE__, sig, signame, dump); - } - abort(); + } + + /* + * We expect qemu to exit with status 0; anything else is + * fishy and should be logged with as much detail as possible. + */ + wstatus = s->wstatus; + if (wstatus) { + if (WIFEXITED(wstatus)) { + fprintf(stderr, "%s:%d: kill_qemu() tried to terminate QEMU " + "process but encountered exit status %d\n", + __FILE__, __LINE__, WEXITSTATUS(wstatus)); + } else if (WIFSIGNALED(wstatus)) { + int sig = WTERMSIG(wstatus); + const char *signame = strsignal(sig) ?: "unknown ???"; + const char *dump = WCOREDUMP(wstatus) ? " (core dumped)" : ""; + + fprintf(stderr, "%s:%d: kill_qemu() detected QEMU death " + "from signal %d (%s)%s\n", + __FILE__, __LINE__, sig, signame, dump); } + abort(); } } @@ -228,6 +245,7 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args) g_test_message("starting QEMU: %s", command); + s->wstatus = 0; s->qemu_pid = fork(); if (s->qemu_pid == 0) { setenv("QEMU_AUDIO_DRV", "none", true); diff --git a/tests/libqtest.h b/tests/libqtest.h index 96a6c01352..9758c51be6 100644 --- a/tests/libqtest.h +++ b/tests/libqtest.h @@ -1011,4 +1011,12 @@ bool qmp_rsp_is_err(QDict *rsp); */ void qmp_assert_error_class(QDict *rsp, const char *class); +/** + * qtest_probe_child: + * @s: QTestState instance to operate on. + * + * Returns: true if the child is still alive. + */ +bool qtest_probe_child(QTestState *s); + #endif diff --git a/tests/machine-none-test.c b/tests/machine-none-test.c index 2b3b750500..4c6d470798 100644 --- a/tests/machine-none-test.c +++ b/tests/machine-none-test.c @@ -75,6 +75,7 @@ static void test_machine_cpu_cli(void) QDict *response; const char *arch = qtest_get_arch(); const char *cpu_model = get_cpu_model_by_arch(arch); + QTestState *qts; if (!cpu_model) { if (!(!strcmp(arch, "microblaze") || !strcmp(arch, "microblazeel"))) { @@ -83,13 +84,13 @@ static void test_machine_cpu_cli(void) } return; /* TODO: die here to force all targets have a test */ } - global_qtest = qtest_initf("-machine none -cpu '%s'", cpu_model); + qts = qtest_initf("-machine none -cpu '%s'", cpu_model); - response = qmp("{ 'execute': 'quit' }"); + response = qtest_qmp(qts, "{ 'execute': 'quit' }"); g_assert(qdict_haskey(response, "return")); qobject_unref(response); - qtest_quit(global_qtest); + qtest_quit(qts); } int main(int argc, char **argv) diff --git a/tests/prom-env-test.c b/tests/prom-env-test.c index 198d007f1b..4821254b7e 100644 --- a/tests/prom-env-test.c +++ b/tests/prom-env-test.c @@ -25,14 +25,14 @@ #define MAGIC 0xcafec0de #define ADDRESS 0x4000 -static void check_guest_memory(void) +static void check_guest_memory(QTestState *qts) { uint32_t signature; int i; /* Poll until code has run and modified memory. Wait at most 600 seconds */ for (i = 0; i < 60000; ++i) { - signature = readl(ADDRESS); + signature = qtest_readl(qts, ADDRESS); if (signature == MAGIC) { break; } @@ -45,17 +45,16 @@ static void check_guest_memory(void) static void test_machine(const void *machine) { const char *extra_args; + QTestState *qts; /* The pseries firmware boots much faster without the default devices */ extra_args = strcmp(machine, "pseries") == 0 ? "-nodefaults" : ""; - global_qtest = qtest_initf("-M %s,accel=tcg %s " - "-prom-env 'use-nvramrc?=true' " - "-prom-env 'nvramrc=%x %x l!' ", - (const char *)machine, extra_args, - MAGIC, ADDRESS); - check_guest_memory(); - qtest_quit(global_qtest); + qts = qtest_initf("-M %s,accel=tcg %s -prom-env 'use-nvramrc?=true' " + "-prom-env 'nvramrc=%x %x l!' ", (const char *)machine, + extra_args, MAGIC, ADDRESS); + check_guest_memory(qts); + qtest_quit(qts); } static void add_tests(const char *machines[]) diff --git a/tests/pvpanic-test.c b/tests/pvpanic-test.c index 7461a7254f..ff9176adf3 100644 --- a/tests/pvpanic-test.c +++ b/tests/pvpanic-test.c @@ -15,13 +15,16 @@ static void test_panic(void) { uint8_t val; QDict *response, *data; + QTestState *qts; - val = inb(0x505); + qts = qtest_init("-device pvpanic"); + + val = qtest_inb(qts, 0x505); g_assert_cmpuint(val, ==, 1); - outb(0x505, 0x1); + qtest_outb(qts, 0x505, 0x1); - response = qmp_receive(); + response = qtest_qmp_receive(qts); g_assert(qdict_haskey(response, "event")); g_assert_cmpstr(qdict_get_str(response, "event"), ==, "GUEST_PANICKED"); g_assert(qdict_haskey(response, "data")); @@ -29,6 +32,8 @@ static void test_panic(void) g_assert(qdict_haskey(data, "action")); g_assert_cmpstr(qdict_get_str(data, "action"), ==, "pause"); qobject_unref(response); + + qtest_quit(qts); } int main(int argc, char **argv) @@ -38,10 +43,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); qtest_add_func("/pvpanic/panic", test_panic); - qtest_start("-device pvpanic"); ret = g_test_run(); - qtest_end(); - return ret; } diff --git a/tests/pxe-test.c b/tests/pxe-test.c index 6e3679672c..73ac1d1c61 100644 --- a/tests/pxe-test.c +++ b/tests/pxe-test.c @@ -61,6 +61,7 @@ static testdef_t s390x_tests[] = { static void test_pxe_one(const testdef_t *test, bool ipv6) { + QTestState *qts; char *args; args = g_strdup_printf( @@ -70,9 +71,9 @@ static void test_pxe_one(const testdef_t *test, bool ipv6) test->machine, disk, ipv6 ? "off" : "on", ipv6 ? "on" : "off", test->model); - qtest_start(args); - boot_sector_test(global_qtest); - qtest_quit(global_qtest); + qts = qtest_init(args); + boot_sector_test(qts); + qtest_quit(qts); g_free(args); } diff --git a/tests/test-filter-mirror.c b/tests/test-filter-mirror.c index d15917e2cf..7ab2aed8a0 100644 --- a/tests/test-filter-mirror.c +++ b/tests/test-filter-mirror.c @@ -17,7 +17,7 @@ #include "qemu/main-loop.h" /* TODO actually test the results and get rid of this */ -#define qmp_discard_response(...) qobject_unref(qmp(__VA_ARGS__)) +#define qmp_discard_response(qs, ...) qobject_unref(qtest_qmp(qs, __VA_ARGS__)) static void test_mirror(void) { @@ -29,6 +29,7 @@ static void test_mirror(void) uint32_t size = sizeof(send_buf); size = htonl(size); const char *devstr = "e1000"; + QTestState *qts; if (g_str_equal(qtest_get_arch(), "s390x")) { devstr = "virtio-net-ccw"; @@ -40,7 +41,7 @@ static void test_mirror(void) ret = mkstemp(sock_path); g_assert_cmpint(ret, !=, -1); - global_qtest = qtest_initf( + qts = qtest_initf( "-netdev socket,id=qtest-bn0,fd=%d " "-device %s,netdev=qtest-bn0,id=qtest-e0 " "-chardev socket,id=mirror0,path=%s,server,nowait " @@ -61,7 +62,7 @@ static void test_mirror(void) }; /* send a qmp command to guarantee that 'connected' is setting to true. */ - qmp_discard_response("{ 'execute' : 'query-status'}"); + qmp_discard_response(qts, "{ 'execute' : 'query-status'}"); ret = iov_send(send_sock[0], iov, 2, 0, sizeof(size) + sizeof(send_buf)); g_assert_cmpint(ret, ==, sizeof(send_buf) + sizeof(size)); close(send_sock[0]); @@ -78,6 +79,7 @@ static void test_mirror(void) g_free(recv_buf); close(recv_sock); unlink(sock_path); + qtest_quit(qts); } int main(int argc, char **argv) @@ -88,7 +90,6 @@ int main(int argc, char **argv) qtest_add_func("/netfilter/mirror", test_mirror); ret = g_test_run(); - qtest_end(); return ret; } diff --git a/tests/test-filter-redirector.c b/tests/test-filter-redirector.c index 615ff5cb9f..9ca9feabf8 100644 --- a/tests/test-filter-redirector.c +++ b/tests/test-filter-redirector.c @@ -59,7 +59,7 @@ #include "qemu/main-loop.h" /* TODO actually test the results and get rid of this */ -#define qmp_discard_response(...) qobject_unref(qmp(__VA_ARGS__)) +#define qmp_discard_response(qs, ...) qobject_unref(qtest_qmp(qs, __VA_ARGS__)) static const char *get_devstr(void) { @@ -81,6 +81,7 @@ static void test_redirector_tx(void) char *recv_buf; uint32_t size = sizeof(send_buf); size = htonl(size); + QTestState *qts; ret = socketpair(PF_UNIX, SOCK_STREAM, 0, backend_sock); g_assert_cmpint(ret, !=, -1); @@ -90,7 +91,7 @@ static void test_redirector_tx(void) ret = mkstemp(sock_path1); g_assert_cmpint(ret, !=, -1); - global_qtest = qtest_initf( + qts = qtest_initf( "-netdev socket,id=qtest-bn0,fd=%d " "-device %s,netdev=qtest-bn0,id=qtest-e0 " "-chardev socket,id=redirector0,path=%s,server,nowait " @@ -108,7 +109,7 @@ static void test_redirector_tx(void) g_assert_cmpint(recv_sock, !=, -1); /* send a qmp command to guarantee that 'connected' is setting to true. */ - qmp_discard_response("{ 'execute' : 'query-status'}"); + qmp_discard_response(qts, "{ 'execute' : 'query-status'}"); struct iovec iov[] = { { @@ -137,7 +138,7 @@ static void test_redirector_tx(void) close(recv_sock); unlink(sock_path0); unlink(sock_path1); - qtest_end(); + qtest_quit(qts); } static void test_redirector_rx(void) @@ -150,6 +151,7 @@ static void test_redirector_rx(void) char *recv_buf; uint32_t size = sizeof(send_buf); size = htonl(size); + QTestState *qts; ret = socketpair(PF_UNIX, SOCK_STREAM, 0, backend_sock); g_assert_cmpint(ret, !=, -1); @@ -159,7 +161,7 @@ static void test_redirector_rx(void) ret = mkstemp(sock_path1); g_assert_cmpint(ret, !=, -1); - global_qtest = qtest_initf( + qts = qtest_initf( "-netdev socket,id=qtest-bn0,fd=%d " "-device %s,netdev=qtest-bn0,id=qtest-e0 " "-chardev socket,id=redirector0,path=%s,server,nowait " @@ -186,7 +188,7 @@ static void test_redirector_rx(void) send_sock = unix_connect(sock_path1, NULL); g_assert_cmpint(send_sock, !=, -1); /* send a qmp command to guarantee that 'connected' is setting to true. */ - qmp_discard_response("{ 'execute' : 'query-status'}"); + qmp_discard_response(qts, "{ 'execute' : 'query-status'}"); ret = iov_send(send_sock, iov, 2, 0, sizeof(size) + sizeof(send_buf)); g_assert_cmpint(ret, ==, sizeof(send_buf) + sizeof(size)); @@ -204,7 +206,7 @@ static void test_redirector_rx(void) g_free(recv_buf); unlink(sock_path0); unlink(sock_path1); - qtest_end(); + qtest_quit(qts); } int main(int argc, char **argv) diff --git a/tests/vmgenid-test.c b/tests/vmgenid-test.c index 0a6fb55f2e..84449ce8ae 100644 --- a/tests/vmgenid-test.c +++ b/tests/vmgenid-test.c @@ -31,7 +31,7 @@ typedef struct { uint32_t vgia_val; } QEMU_PACKED VgidTable; -static uint32_t acpi_find_vgia(void) +static uint32_t acpi_find_vgia(QTestState *qts) { uint32_t rsdp_offset; uint32_t guid_offset = 0; @@ -45,18 +45,18 @@ static uint32_t acpi_find_vgia(void) int i; /* Wait for guest firmware to finish and start the payload. */ - boot_sector_test(global_qtest); + boot_sector_test(qts); /* Tables should be initialized now. */ - rsdp_offset = acpi_find_rsdp_address(); + rsdp_offset = acpi_find_rsdp_address(qts); g_assert_cmphex(rsdp_offset, <, RSDP_ADDR_INVALID); - acpi_parse_rsdp_table(rsdp_offset, &rsdp_table); + acpi_parse_rsdp_table(qts, rsdp_offset, &rsdp_table); rsdt = le32_to_cpu(rsdp_table.rsdt_physical_address); /* read the header */ - ACPI_READ_TABLE_HEADER(&rsdt_table, rsdt); + ACPI_READ_TABLE_HEADER(qts, &rsdt_table, rsdt); ACPI_ASSERT_CMP(rsdt_table.signature, "RSDT"); rsdt_table_length = le32_to_cpu(rsdt_table.length); @@ -67,22 +67,22 @@ static uint32_t acpi_find_vgia(void) /* get the addresses of the tables pointed by rsdt */ tables = g_new0(uint32_t, tables_nr); - ACPI_READ_ARRAY_PTR(tables, tables_nr, rsdt); + ACPI_READ_ARRAY_PTR(qts, tables, tables_nr, rsdt); for (i = 0; i < tables_nr; i++) { uint32_t addr = le32_to_cpu(tables[i]); - ACPI_READ_TABLE_HEADER(&ssdt_table, addr); + ACPI_READ_TABLE_HEADER(qts, &ssdt_table, addr); if (!strncmp((char *)ssdt_table.oem_table_id, "VMGENID", 7)) { /* the first entry in the table should be VGIA * That's all we need */ - ACPI_READ_FIELD(vgid_table.name_op, addr); + ACPI_READ_FIELD(qts, vgid_table.name_op, addr); g_assert(vgid_table.name_op == 0x08); /* name */ - ACPI_READ_ARRAY(vgid_table.vgia, addr); + ACPI_READ_ARRAY(qts, vgid_table.vgia, addr); g_assert(memcmp(vgid_table.vgia, "VGIA", 4) == 0); - ACPI_READ_FIELD(vgid_table.val_op, addr); + ACPI_READ_FIELD(qts, vgid_table.val_op, addr); g_assert(vgid_table.val_op == 0x0C); /* dword */ - ACPI_READ_FIELD(vgid_table.vgia_val, addr); + ACPI_READ_FIELD(qts, vgid_table.vgia_val, addr); /* The GUID is written at a fixed offset into the fw_cfg file * in order to implement the "OVMF SDT Header probe suppressor" * see docs/specs/vmgenid.txt for more details @@ -95,17 +95,17 @@ static uint32_t acpi_find_vgia(void) return guid_offset; } -static void read_guid_from_memory(QemuUUID *guid) +static void read_guid_from_memory(QTestState *qts, QemuUUID *guid) { uint32_t vmgenid_addr; int i; - vmgenid_addr = acpi_find_vgia(); + vmgenid_addr = acpi_find_vgia(qts); g_assert(vmgenid_addr); /* Read the GUID directly from guest memory */ for (i = 0; i < 16; i++) { - guid->data[i] = readb(vmgenid_addr + i); + guid->data[i] = qtest_readb(qts, vmgenid_addr + i); } /* The GUID is in little-endian format in the guest, while QEMU * uses big-endian. Swap after reading. @@ -113,12 +113,12 @@ static void read_guid_from_memory(QemuUUID *guid) qemu_uuid_bswap(guid); } -static void read_guid_from_monitor(QemuUUID *guid) +static void read_guid_from_monitor(QTestState *qts, QemuUUID *guid) { QDict *rsp, *rsp_ret; const char *guid_str; - rsp = qmp("{ 'execute': 'query-vm-generation-id' }"); + rsp = qtest_qmp(qts, "{ 'execute': 'query-vm-generation-id' }"); if (qdict_haskey(rsp, "return")) { rsp_ret = qdict_get_qdict(rsp, "return"); g_assert(qdict_haskey(rsp_ret, "guid")); @@ -139,45 +139,48 @@ static char disk[] = "tests/vmgenid-test-disk-XXXXXX"; static void vmgenid_set_guid_test(void) { QemuUUID expected, measured; + QTestState *qts; g_assert(qemu_uuid_parse(VGID_GUID, &expected) == 0); - global_qtest = qtest_initf(GUID_CMD(VGID_GUID)); + qts = qtest_initf(GUID_CMD(VGID_GUID)); /* Read the GUID from accessing guest memory */ - read_guid_from_memory(&measured); + read_guid_from_memory(qts, &measured); g_assert(memcmp(measured.data, expected.data, sizeof(measured.data)) == 0); - qtest_quit(global_qtest); + qtest_quit(qts); } static void vmgenid_set_guid_auto_test(void) { QemuUUID measured; + QTestState *qts; - global_qtest = qtest_initf(GUID_CMD("auto")); + qts = qtest_initf(GUID_CMD("auto")); - read_guid_from_memory(&measured); + read_guid_from_memory(qts, &measured); /* Just check that the GUID is non-null */ g_assert(!qemu_uuid_is_null(&measured)); - qtest_quit(global_qtest); + qtest_quit(qts); } static void vmgenid_query_monitor_test(void) { QemuUUID expected, measured; + QTestState *qts; g_assert(qemu_uuid_parse(VGID_GUID, &expected) == 0); - global_qtest = qtest_initf(GUID_CMD(VGID_GUID)); + qts = qtest_initf(GUID_CMD(VGID_GUID)); /* Read the GUID via the monitor */ - read_guid_from_monitor(&measured); + read_guid_from_monitor(qts, &measured); g_assert(memcmp(measured.data, expected.data, sizeof(measured.data)) == 0); - qtest_quit(global_qtest); + qtest_quit(qts); } int main(int argc, char **argv) |