From 933e701dac1936c6f15c765b5687fbc623464ec7 Mon Sep 17 00:00:00 2001 From: Mike Sperber Date: Thu, 22 Mar 2012 18:00:31 +0100 Subject: Unbreak floating point on middle-endian machines. On some ARMs (and maybe other platforms), doubles are stored with the the two 32-bit words reversed with respect to more common architectures. The symptom is this: > io_lib:write(1.0). "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005299808824" Detect that and account for it when decoding floats. --- erts/aclocal.m4 | 63 +++++++++++++++++++++++++++++++++++++++++++ erts/configure.in | 1 + erts/emulator/beam/erl_bits.c | 10 +++++++ 3 files changed, 74 insertions(+) (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 339a15a2bb..9c5bcd6dc6 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -59,6 +59,7 @@ AC_ARG_VAR(erl_xcomp_isysroot, [Absolute cross system root include path (only us dnl Cross compilation variables AC_ARG_VAR(erl_xcomp_bigendian, [big endian system: yes|no (only used when cross compiling)]) +AC_ARG_VAR(erl_xcomp_double_middle_endian, [double-middle-endian system: yes|no (only used when cross compiling)]) AC_ARG_VAR(erl_xcomp_linux_clock_gettime_correction, [clock_gettime() can be used for time correction: yes|no (only used when cross compiling)]) AC_ARG_VAR(erl_xcomp_linux_nptl, [have Native POSIX Thread Library: yes|no (only used when cross compiling)]) AC_ARG_VAR(erl_xcomp_linux_usable_sigusrx, [SIGUSR1 and SIGUSR2 can be used: yes|no (only used when cross compiling)]) @@ -606,6 +607,60 @@ ifelse([$5], , , [$5 fi ]) +dnl ---------------------------------------------------------------------- +dnl +dnl AC_DOUBLE_MIDDLE_ENDIAN +dnl +dnl Checks whether doubles are represented in "middle-endian" format. +dnl Sets ac_cv_double_middle_endian={no,yes,unknown} accordingly, +dnl as well as DOUBLE_MIDDLE_ENDIAN. +dnl +dnl + +AC_DEFUN([AC_C_DOUBLE_MIDDLE_ENDIAN], +[AC_CACHE_CHECK(whether double word ordering is middle-endian, ac_cv_c_double_middle_endian, +[# It does not; compile a test program. +AC_RUN_IFELSE( +[AC_LANG_SOURCE([[int +main(void) +{ + int i = 0; + int zero = 0; + union + { + double d; + char c[sizeof (double)]; + } v; + v.d = 1.0; + + + while (i < sizeof(double) / 2) + { + if (v.c[i] != 0) + zero = 1; + ++i; + } + exit (zero); +} +]])], + [ac_cv_c_double_middle_endian=no], + [ac_cv_c_double_middle_endian=yes], + [ac_cv_c_double_middle=unknown])]) +case $ac_cv_c_double_middle_endian in + yes) + m4_default([$1], + [AC_DEFINE([DOUBLE_MIDDLE_ENDIAN], 1, + [Define to 1 if your processor stores the words in a double in + middle-endian format (like some ARMs).])]) ;; + no) + $2 ;; + *) + m4_default([$3], + [AC_MSG_ERROR([unknown double endianness +presetting ac_cv_c_double_middle_endian=no (or yes) will help])]) ;; +esac +])# AC_C_DOUBLE_MIDDLE_ENDIAN + dnl ---------------------------------------------------------------------- dnl @@ -1337,6 +1392,14 @@ if test "$ac_cv_c_bigendian" = "yes"; then AC_DEFINE(ETHR_BIGENDIAN, 1, [Define if bigendian]) fi +case X$erl_xcomp_double_middle_endian in + X) ;; + Xyes|Xno) ac_cv_c_double_middle_endian=$erl_xcomp_double_middle_endian;; + *) AC_MSG_ERROR([Bad erl_xcomp_double_middle_endian value: $erl_xcomp_double_middle_endian]);; +esac + +AC_C_DOUBLE_MIDDLE_ENDIAN + AC_ARG_ENABLE(native-ethr-impls, AS_HELP_STRING([--disable-native-ethr-impls], [disable native ethread implementations]), diff --git a/erts/configure.in b/erts/configure.in index b801994e14..6583db8116 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -1681,6 +1681,7 @@ case X$erl_xcomp_bigendian in esac AC_C_BIGENDIAN +AC_C_DOUBLE_MIDDLE_ENDIAN dnl fdatasync syscall (Unix only) AC_CHECK_FUNCS([fdatasync]) diff --git a/erts/emulator/beam/erl_bits.c b/erts/emulator/beam/erl_bits.c index 6f7309f493..b7c82935ff 100644 --- a/erts/emulator/beam/erl_bits.c +++ b/erts/emulator/beam/erl_bits.c @@ -1005,8 +1005,13 @@ erts_new_bs_put_float(Process *c_p, Eterm arg, Uint num_bits, int flags) if (is_float(arg)) { FloatDef *fdp = (FloatDef*)(float_val(arg) + 1); +#ifdef DOUBLE_MIDDLE_ENDIAN + a = fdp->fw[1]; + b = fdp->fw[0]; +#else a = fdp->fw[0]; b = fdp->fw[1]; +#endif } else if (is_small(arg)) { u.f64 = (double) signed_val(arg); a = u.i32[0]; @@ -1015,8 +1020,13 @@ erts_new_bs_put_float(Process *c_p, Eterm arg, Uint num_bits, int flags) if (big_to_double(arg, &u.f64) < 0) { return 0; } +#ifdef DOUBLE_MIDDLE_ENDIAN + a = u.i32[1]; + b = u.i32[0]; +#else a = u.i32[0]; b = u.i32[1]; +#endif } else { return 0; } -- cgit v1.2.3 From cab9edd0a6f1fad2c3ec9661b39627756ad9d2ba Mon Sep 17 00:00:00 2001 From: Mike Sperber Date: Tue, 17 Jul 2012 10:03:49 +0200 Subject: Add test for floating-point output to float_SUITE. This catches endianness problems, such as recently reported for ARMs. --- erts/emulator/test/float_SUITE.erl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/float_SUITE.erl b/erts/emulator/test/float_SUITE.erl index 8e6923ce9f..abe01bf5ec 100644 --- a/erts/emulator/test/float_SUITE.erl +++ b/erts/emulator/test/float_SUITE.erl @@ -25,7 +25,7 @@ init_per_group/2,end_per_group/2, init_per_testcase/2,end_per_testcase/2, fpe/1,fp_drv/1,fp_drv_thread/1,denormalized/1,match/1, - bad_float_unpack/1,cmp_zero/1, cmp_integer/1, cmp_bignum/1]). + bad_float_unpack/1, write/1, cmp_zero/1, cmp_integer/1, cmp_bignum/1]). -export([otp_7178/1]). -export([hidden_inf/1]). @@ -42,7 +42,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [fpe, fp_drv, fp_drv_thread, otp_7178, denormalized, - match, bad_float_unpack, {group, comparison} + match, bad_float_unpack, write, {group, comparison} ,hidden_inf ]. @@ -190,6 +190,11 @@ bad_float_unpack(Config) when is_list(Config) -> bad_float_unpack_match(<>) -> F; bad_float_unpack_match(<>) -> I. +%% Exposes endianness issues. + +write(Config) when is_list(Config) -> + "1.0" = io_lib:write(1.0). + cmp_zero(_Config) -> cmp(0.5e-323,0). -- cgit v1.2.3 From eba4d247740189c3b8045edeaf8224651e77a3e0 Mon Sep 17 00:00:00 2001 From: Mike Sperber Date: Fri, 20 Jul 2012 11:27:47 +0200 Subject: Revise the autoconf tests for double middle endianness. The previous iteration didn't work for big-endian systems. Now use code very close to what Erts does internally. Also, only warn when the double endianness is unknown - i.e. when we're cross-compiling. --- erts/aclocal.m4 | 65 +++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 54 insertions(+), 11 deletions(-) (limited to 'erts') diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 9c5bcd6dc6..a76594d86f 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -621,25 +621,68 @@ AC_DEFUN([AC_C_DOUBLE_MIDDLE_ENDIAN], [AC_CACHE_CHECK(whether double word ordering is middle-endian, ac_cv_c_double_middle_endian, [# It does not; compile a test program. AC_RUN_IFELSE( -[AC_LANG_SOURCE([[int +[AC_LANG_SOURCE([[#include + +int main(void) { int i = 0; int zero = 0; + int bigendian; + int zero_index = 0; + + union + { + long int l; + char c[sizeof (long int)]; + } u; + + /* we'll use the one with 32-bit words */ + union + { + double d; + unsigned int c[2]; + } vint; + + union + { + double d; + unsigned long c[2]; + } vlong; + union { double d; - char c[sizeof (double)]; - } v; - v.d = 1.0; - - - while (i < sizeof(double) / 2) + unsigned short c[2]; + } vshort; + + + /* Are we little or big endian? From Harbison&Steele. */ + u.l = 1; + bigendian = (u.c[sizeof (long int) - 1] == 1); + + zero_index = bigendian ? 1 : 0; + + vint.d = 1.0; + vlong.d = 1.0; + vshort.d = 1.0; + + if (sizeof(unsigned int) == 4) + { + if (vint.c[zero_index] != 0) + zero = 1; + } + else if (sizeof(unsigned long) == 4) + { + if (vlong.c[zero_index] != 0) + zero = 1; + } + else if (sizeof(unsigned short) == 4) { - if (v.c[i] != 0) + if (vshort.c[zero_index] != 0) zero = 1; - ++i; } + exit (zero); } ]])], @@ -656,7 +699,7 @@ case $ac_cv_c_double_middle_endian in $2 ;; *) m4_default([$3], - [AC_MSG_ERROR([unknown double endianness + [AC_MSG_WARN([unknown double endianness presetting ac_cv_c_double_middle_endian=no (or yes) will help])]) ;; esac ])# AC_C_DOUBLE_MIDDLE_ENDIAN @@ -1394,7 +1437,7 @@ fi case X$erl_xcomp_double_middle_endian in X) ;; - Xyes|Xno) ac_cv_c_double_middle_endian=$erl_xcomp_double_middle_endian;; + Xyes|Xno|Xunknown) ac_cv_c_double_middle_endian=$erl_xcomp_double_middle_endian;; *) AC_MSG_ERROR([Bad erl_xcomp_double_middle_endian value: $erl_xcomp_double_middle_endian]);; esac -- cgit v1.2.3