diff options
-rw-r--r-- | fpu/softfloat.c | 33 | ||||
-rw-r--r-- | include/fpu/softfloat-macros.h | 2 | ||||
-rwxr-xr-x | scripts/archive-source.sh | 2 | ||||
-rw-r--r-- | tests/Makefile.include | 137 | ||||
-rw-r--r-- | tests/fp/Makefile | 3 | ||||
m--------- | tests/fp/berkeley-testfloat-3 | 0 | ||||
-rw-r--r-- | tests/fp/fp-bench.c | 15 | ||||
-rw-r--r-- | tests/fp/fp-test.c | 4 | ||||
-rw-r--r-- | tests/fp/platform.h | 1 |
9 files changed, 182 insertions, 15 deletions
diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 59eac97d10..9132d7a0b0 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -1542,6 +1542,8 @@ soft_f64_muladd(float64 a, float64 b, float64 c, int flags, return float64_round_pack_canonical(pr, status); } +static bool force_soft_fma; + float32 QEMU_FLATTEN float32_muladd(float32 xa, float32 xb, float32 xc, int flags, float_status *s) { @@ -1562,6 +1564,11 @@ float32_muladd(float32 xa, float32 xb, float32 xc, int flags, float_status *s) if (unlikely(!f32_is_zon3(ua, ub, uc))) { goto soft; } + + if (unlikely(force_soft_fma)) { + 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. @@ -1623,6 +1630,11 @@ float64_muladd(float64 xa, float64 xb, float64 xc, int flags, float_status *s) if (unlikely(!f64_is_zon3(ua, ub, uc))) { goto soft; } + + if (unlikely(force_soft_fma)) { + 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. @@ -7974,3 +7986,24 @@ float128 float128_scalbn(float128 a, int n, float_status *status) , status); } + +static void __attribute__((constructor)) softfloat_init(void) +{ + union_float64 ua, ub, uc, ur; + + if (QEMU_NO_HARDFLOAT) { + return; + } + /* + * Test that the host's FMA is not obviously broken. For example, + * glibc < 2.23 can perform an incorrect FMA on certain hosts; see + * https://sourceware.org/bugzilla/show_bug.cgi?id=13304 + */ + ua.s = 0x0020000000000001ULL; + ub.s = 0x3ca0000000000000ULL; + uc.s = 0x0020000000000000ULL; + ur.h = fma(ua.h, ub.h, uc.h); + if (ur.s != 0x0020000000000001ULL) { + force_soft_fma = true; + } +} diff --git a/include/fpu/softfloat-macros.h b/include/fpu/softfloat-macros.h index b1d772e6d4..bd5b6418e3 100644 --- a/include/fpu/softfloat-macros.h +++ b/include/fpu/softfloat-macros.h @@ -641,7 +641,7 @@ static inline uint64_t udiv_qrnnd(uint64_t *r, uint64_t n1, uint64_t q; asm("divq %4" : "=a"(q), "=d"(*r) : "0"(n0), "1"(n1), "rm"(d)); return q; -#elif defined(__s390x__) +#elif defined(__s390x__) && !defined(__clang__) /* Need to use a TImode type to get an even register pair for DLGR. */ unsigned __int128 n = (unsigned __int128)n1 << 64 | n0; asm("dlgr %0, %1" : "+r"(n) : "r"(d)); diff --git a/scripts/archive-source.sh b/scripts/archive-source.sh index 62bd22578b..6eed2a29bd 100755 --- a/scripts/archive-source.sh +++ b/scripts/archive-source.sh @@ -26,7 +26,7 @@ vroot_dir="${tar_file}.vroot" # independent of what the developer currently has initialized # in their checkout, because the build environment is completely # different to the host OS. -submodules="dtc ui/keycodemapdb" +submodules="dtc ui/keycodemapdb tests/fp/berkeley-softfloat-3 tests/fp/berkeley-testfloat-3" trap "status=$?; rm -rf \"$list_file\" \"$vroot_dir\"; exit \$status" 0 1 2 3 15 diff --git a/tests/Makefile.include b/tests/Makefile.include index 4eea38ae99..f5e6eb5152 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -12,6 +12,7 @@ check-help: @echo " $(MAKE) check-qapi-schema Run QAPI schema tests" @echo " $(MAKE) check-block Run block tests" @echo " $(MAKE) check-tcg Run TCG tests" + @echo " $(MAKE) check-softfloat Run FPU emulation tests" @echo " $(MAKE) check-acceptance Run all acceptance (functional) tests" @echo @echo " $(MAKE) check-report.html Generates an HTML test report" @@ -880,6 +881,138 @@ check-report-unit.tap: $(check-unit-y) check-report.tap: $(patsubst %,check-report-qtest-%.tap, $(QTEST_TARGETS)) check-report-unit.tap $(call quiet-command,./scripts/tap-merge.py $^ > $@,"GEN","$@") +# FPU Emulation tests (aka softfloat) +# +# As we still have some places that need fixing the rules are a little +# more complex than they need to be and have to override some of the +# generic Makefile expansions. Once we are cleanly passing all +# the tests we can simplify the make syntax. + +FP_TEST_BIN=$(BUILD_DIR)/tests/fp/fp-test + +# the build dir is created by configure +.PHONY: $(FP_TEST_BIN) +$(FP_TEST_BIN): + $(call quiet-command, \ + $(MAKE) $(SUBDIR_MAKEFLAGS) -C $(dir $@) V="$(V)" $(notdir $@), \ + "BUILD", "$(notdir $@)") + +# The full test suite can take a bit of time, default to a quick run +ifeq ($(SPEED), quick) +FP_TL=-l 1 +else +FP_TL=-l 2 -r all +endif + +# $1 = tests, $2 = description +test-softfloat = $(call quiet-command, \ + cd $(BUILD_DIR)/tests/fp && \ + ./fp-test -s $(FP_TL) $1 > $2.out 2>&1 || \ + (cat $2.out && exit 1;), \ + "FLOAT TEST", $2) + +# Conversion Routines: +# FIXME: i32_to_extF80 (broken), i64_to_extF80 (broken) +# ui32_to_f128 (not implemented), f128_to_ui32 (not implemented) +# extF80_roundToInt (broken) +# +check-softfloat-conv: $(FP_TEST_BIN) + $(call test-softfloat, \ + i32_to_f16 i64_to_f16 \ + i32_to_f32 i64_to_f32 \ + i32_to_f64 i64_to_f64 \ + i32_to_f128 i64_to_f128, int-to-float) + $(call test-softfloat, \ + ui32_to_f16 ui64_to_f16 \ + ui32_to_f32 ui64_to_f32 \ + ui32_to_f64 ui64_to_f64 \ + ui64_to_f128, uint-to-float) + $(call test-softfloat, \ + f16_to_i32 f16_to_i32_r_minMag \ + f32_to_i32 f32_to_i32_r_minMag \ + f64_to_i32 f64_to_i32_r_minMag \ + extF80_to_i32 extF80_to_i32_r_minMag \ + f128_to_i32 f128_to_i32_r_minMag \ + f16_to_i64 f16_to_i64_r_minMag \ + f32_to_i64 f32_to_i64_r_minMag \ + f64_to_i64 f64_to_i64_r_minMag \ + extF80_to_i64 extF80_to_i64_r_minMag \ + f128_to_i64 f128_to_i64_r_minMag, \ + float-to-int) + $(call test-softfloat, \ + f16_to_ui32 f16_to_ui32_r_minMag \ + f32_to_ui32 f32_to_ui32_r_minMag \ + f64_to_ui32 f64_to_ui32_r_minMag \ + f16_to_ui64 f16_to_ui64_r_minMag \ + f32_to_ui64 f32_to_ui64_r_minMag \ + f64_to_ui64 f64_to_ui64_r_minMag, \ + float-to-uint) + $(call test-softfloat, \ + f16_roundToInt f32_roundToInt \ + f64_roundToInt f128_roundToInt, \ + round-to-integer) + +# Generic rule for all float operations +# +# Some patterns are overidden due to broken or missing tests. +# Hopefully these can be removed over time. + +check-softfloat-%: $(FP_TEST_BIN) + $(call test-softfloat, f16_$* f32_$* f64_$* extF80_$* f128_$*, $*) + +# Float Compare routines +SF_COMPARE_OPS=eq eq_signaling le le_quiet lt_quiet +SF_COMPARE_RULES=$(patsubst %,check-softfloat-%, $(SF_COMPARE_OPS)) + +# FIXME: extF80_le_quiet (broken) +check-softfloat-le_quiet: $(FP_TEST_BIN) + $(call test-softfloat, \ + f16_le_quiet f32_le_quiet f64_le_quiet \ + f128_le_quiet, \ + le_quiet) + +# FIXME: extF80_lt_quiet (broken) +check-softfloat-lt_quiet: $(FP_TEST_BIN) + $(call test-softfloat, \ + f16_lt_quiet f32_lt_quiet f64_lt_quiet \ + f128_lt_quiet, \ + lt_quiet) + +.PHONY: check-softfloat-compare +check-softfloat-compare: $(SF_COMPARE_RULES) + +# Math Operations + +# FIXME: extF80_mulAdd (missing) +check-softfloat-mulAdd: $(FP_TEST_BIN) + $(call test-softfloat, \ + f16_mulAdd f32_mulAdd f64_mulAdd f128_mulAdd, \ + mulAdd) + +# FIXME: extF80_rem (broken) +check-softfloat-rem: $(FP_TEST_BIN) + $(call test-softfloat, \ + f16_rem f32_rem f64_rem f128_rem, \ + rem) + +SF_MATH_OPS=add sub mul mulAdd div rem sqrt +SF_MATH_RULES=$(patsubst %,check-softfloat-%, $(SF_MATH_OPS)) + +.PHONY: check-softfloat-ops +check-softfloat-ops: $(SF_MATH_RULES) + +# Finally a generic rule to test all of softfoat. If TCG isnt't +# enabled we define a null operation which skips the tests. + +.PHONY: check-softfloat +ifeq ($(CONFIG_TCG),y) +check-softfloat: check-softfloat-conv check-softfloat-compare check-softfloat-ops +else +check-softfloat: + $(call quiet-command, /bin/true, "FLOAT TEST", \ + "SKIPPED for non-TCG builds") +endif + # Per guest TCG tests LINUX_USER_TARGETS=$(filter %-linux-user,$(TARGET_DIRS)) @@ -912,7 +1045,7 @@ clean-tcg-tests-%: build-tcg: $(BUILD_TCG_TARGET_RULES) .PHONY: check-tcg -check-tcg: $(RUN_TCG_TARGET_RULES) +check-tcg: check-softfloat $(RUN_TCG_TARGET_RULES) .PHONY: clean-tcg clean-tcg: $(CLEAN_TCG_TARGET_RULES) @@ -993,7 +1126,7 @@ check-acceptance: check-venv $(TESTS_RESULTS_DIR) check-qapi-schema: $(patsubst %,check-%, $(check-qapi-schema-y)) check-tests/qapi-schema/doc-good.texi check-qtest: $(patsubst %,check-qtest-%, $(QTEST_TARGETS)) check-block: $(patsubst %,check-%, $(check-block-y)) -check: check-qapi-schema check-unit check-qtest check-decodetree +check: check-qapi-schema check-unit check-softfloat check-qtest check-decodetree check-clean: rm -rf $(check-unit-y) tests/*.o $(QEMU_IOTESTS_HELPERS-y) rm -rf $(sort $(foreach target,$(SYSEMU_TARGET_LIST), $(check-qtest-$(target)-y)) $(check-qtest-generic-y)) diff --git a/tests/fp/Makefile b/tests/fp/Makefile index 5019dcdca0..5a35e7c210 100644 --- a/tests/fp/Makefile +++ b/tests/fp/Makefile @@ -65,8 +65,7 @@ QEMU_CFLAGS += $(TF_OPTS) TF_CFLAGS := TF_CFLAGS += -Wno-strict-prototypes TF_CFLAGS += -Wno-unknown-pragmas -TF_CFLAGS += -Wno-discarded-qualifiers -TF_CFLAGS += -Wno-maybe-uninitialized +TF_CFLAGS += -Wno-uninitialized TF_CFLAGS += -Wno-missing-prototypes TF_CFLAGS += -Wno-return-type TF_CFLAGS += -Wno-unused-function diff --git a/tests/fp/berkeley-testfloat-3 b/tests/fp/berkeley-testfloat-3 -Subproject ca9fa2ba05625ba929958f163b01747e07dd39c +Subproject 5a59dcec19327396a011a17fd924aed4fec416b diff --git a/tests/fp/fp-bench.c b/tests/fp/fp-bench.c index f5bc5edebf..4ba5e1d2d4 100644 --- a/tests/fp/fp-bench.c +++ b/tests/fp/fp-bench.c @@ -143,15 +143,20 @@ static void update_random_ops(int n_ops, enum precision prec) for (i = 0; i < n_ops; i++) { uint64_t r = random_ops[i]; - if (prec == PREC_SINGLE || PREC_FLOAT32) { + switch (prec) { + case PREC_SINGLE: + case PREC_FLOAT32: do { r = xorshift64star(r); } while (!float32_is_normal(r)); - } else if (prec == PREC_DOUBLE || PREC_FLOAT64) { + break; + case PREC_DOUBLE: + case PREC_FLOAT64: do { r = xorshift64star(r); } while (!float64_is_normal(r)); - } else { + break; + default: g_assert_not_reached(); } random_ops[i] = r; @@ -171,8 +176,6 @@ static void fill_random(union fp *ops, int n_ops, enum precision prec, 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: @@ -180,8 +183,6 @@ static void fill_random(union fp *ops, int n_ops, enum precision prec, 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(); diff --git a/tests/fp/fp-test.c b/tests/fp/fp-test.c index fca576309c..2a35ef601d 100644 --- a/tests/fp/fp-test.c +++ b/tests/fp/fp-test.c @@ -789,7 +789,7 @@ static int set_init_flags(const char *flags) return 0; } -static uint8_t slow_clear_flags(void) +static uint_fast8_t slow_clear_flags(void) { uint8_t prev = slowfloat_exceptionFlags; @@ -797,7 +797,7 @@ static uint8_t slow_clear_flags(void) return prev; } -static uint8_t qemu_clear_flags(void) +static uint_fast8_t qemu_clear_flags(void) { uint8_t prev = qemu_flags_to_sf(qsf.float_exception_flags); diff --git a/tests/fp/platform.h b/tests/fp/platform.h index f8c423dde3..c20ba70baa 100644 --- a/tests/fp/platform.h +++ b/tests/fp/platform.h @@ -29,6 +29,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "config-host.h" #ifndef HOST_WORDS_BIGENDIAN #define LITTLEENDIAN 1 |