8295159: DSO created with -ffast-math breaks Java floating-point arithmetic

Reviewed-by: ihse, dholmes, stuefe
This commit is contained in:
Andrew Haley 2023-11-04 09:17:11 +00:00
parent c099cf53f2
commit df599dbb9b
10 changed files with 277 additions and 2 deletions

View File

@ -851,6 +851,7 @@ ifeq ($(call isTargetOs, linux), true)
BUILD_TEST_exeinvoke_exeinvoke.c_OPTIMIZATION := NONE BUILD_TEST_exeinvoke_exeinvoke.c_OPTIMIZATION := NONE
BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exeFPRegs := -ldl BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exeFPRegs := -ldl
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libAsyncGetCallTraceTest := -ldl BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libAsyncGetCallTraceTest := -ldl
BUILD_HOTSPOT_JTREG_LIBRARIES_LDFLAGS_libfast-math := -ffast-math
else else
BUILD_HOTSPOT_JTREG_EXCLUDE += libtest-rw.c libtest-rwx.c \ BUILD_HOTSPOT_JTREG_EXCLUDE += libtest-rw.c libtest-rwx.c \
exeinvoke.c exestack-gap.c exestack-tls.c libAsyncGetCallTraceTest.cpp exeinvoke.c exestack-gap.c exestack-tls.c libAsyncGetCallTraceTest.cpp

View File

@ -78,6 +78,7 @@
# include <dlfcn.h> # include <dlfcn.h>
# include <errno.h> # include <errno.h>
# include <fcntl.h> # include <fcntl.h>
# include <fenv.h>
# include <inttypes.h> # include <inttypes.h>
# include <poll.h> # include <poll.h>
# include <pthread.h> # include <pthread.h>
@ -975,6 +976,41 @@ bool os::dll_address_to_library_name(address addr, char* buf,
// in case of error it checks if .dll/.so was built for the // in case of error it checks if .dll/.so was built for the
// same architecture as Hotspot is running on // same architecture as Hotspot is running on
void *os::Bsd::dlopen_helper(const char *filename, int mode) {
#ifndef IA32
// Save and restore the floating-point environment around dlopen().
// There are known cases where global library initialization sets
// FPU flags that affect computation accuracy, for example, enabling
// Flush-To-Zero and Denormals-Are-Zero. Do not let those libraries
// break Java arithmetic. Unfortunately, this might affect libraries
// that might depend on these FPU features for performance and/or
// numerical "accuracy", but we need to protect Java semantics first
// and foremost. See JDK-8295159.
// This workaround is ineffective on IA32 systems because the MXCSR
// register (which controls flush-to-zero mode) is not stored in the
// legacy fenv.
fenv_t default_fenv;
int rtn = fegetenv(&default_fenv);
assert(rtn == 0, "fegetenv must succeed");
#endif // IA32
void * result= ::dlopen(filename, RTLD_LAZY);
#ifndef IA32
if (result != nullptr && ! IEEE_subnormal_handling_OK()) {
// We just dlopen()ed a library that mangled the floating-point
// flags. Silently fix things now.
int rtn = fesetenv(&default_fenv);
assert(rtn == 0, "fesetenv must succeed");
assert(IEEE_subnormal_handling_OK(), "fsetenv didn't work");
}
#endif // IA32
return result;
}
#ifdef __APPLE__ #ifdef __APPLE__
void * os::dll_load(const char *filename, char *ebuf, int ebuflen) { void * os::dll_load(const char *filename, char *ebuf, int ebuflen) {
#ifdef STATIC_BUILD #ifdef STATIC_BUILD
@ -984,7 +1020,7 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) {
void* result; void* result;
JFR_ONLY(NativeLibraryLoadEvent load_event(filename, &result);) JFR_ONLY(NativeLibraryLoadEvent load_event(filename, &result);)
result = ::dlopen(filename, RTLD_LAZY); result = os::Bsd::dlopen_helper(filename, RTLD_LAZY);
if (result != nullptr) { if (result != nullptr) {
Events::log_dll_message(nullptr, "Loaded shared library %s", filename); Events::log_dll_message(nullptr, "Loaded shared library %s", filename);
// Successful loading // Successful loading
@ -1017,7 +1053,7 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) {
void* result; void* result;
JFR_ONLY(NativeLibraryLoadEvent load_event(filename, &result);) JFR_ONLY(NativeLibraryLoadEvent load_event(filename, &result);)
result = ::dlopen(filename, RTLD_LAZY); result = os::Bsd::dlopen_helper(filename, RTLD_LAZY);
if (result != nullptr) { if (result != nullptr) {
Events::log_dll_message(nullptr, "Loaded shared library %s", filename); Events::log_dll_message(nullptr, "Loaded shared library %s", filename);
// Successful loading // Successful loading

View File

@ -70,6 +70,8 @@ class os::Bsd {
// Real-time clock functions // Real-time clock functions
static void clock_init(void); static void clock_init(void);
static void *dlopen_helper(const char *path, int mode);
// Stack repair handling // Stack repair handling
// none present // none present

View File

@ -95,6 +95,7 @@
# include <signal.h> # include <signal.h>
# include <endian.h> # include <endian.h>
# include <errno.h> # include <errno.h>
# include <fenv.h>
# include <dlfcn.h> # include <dlfcn.h>
# include <stdio.h> # include <stdio.h>
# include <unistd.h> # include <unistd.h>
@ -1802,6 +1803,25 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) {
} }
void * os::Linux::dlopen_helper(const char *filename, char *ebuf, int ebuflen) { void * os::Linux::dlopen_helper(const char *filename, char *ebuf, int ebuflen) {
#ifndef IA32
// Save and restore the floating-point environment around dlopen().
// There are known cases where global library initialization sets
// FPU flags that affect computation accuracy, for example, enabling
// Flush-To-Zero and Denormals-Are-Zero. Do not let those libraries
// break Java arithmetic. Unfortunately, this might affect libraries
// that might depend on these FPU features for performance and/or
// numerical "accuracy", but we need to protect Java semantics first
// and foremost. See JDK-8295159.
// This workaround is ineffective on IA32 systems because the MXCSR
// register (which controls flush-to-zero mode) is not stored in the
// legacy fenv.
fenv_t default_fenv;
int rtn = fegetenv(&default_fenv);
assert(rtn == 0, "fegetenv must succeed");
#endif // IA32
void* result; void* result;
JFR_ONLY(NativeLibraryLoadEvent load_event(filename, &result);) JFR_ONLY(NativeLibraryLoadEvent load_event(filename, &result);)
result = ::dlopen(filename, RTLD_LAZY); result = ::dlopen(filename, RTLD_LAZY);
@ -1820,6 +1840,16 @@ void * os::Linux::dlopen_helper(const char *filename, char *ebuf, int ebuflen) {
} else { } else {
Events::log_dll_message(nullptr, "Loaded shared library %s", filename); Events::log_dll_message(nullptr, "Loaded shared library %s", filename);
log_info(os)("shared library load of %s was successful", filename); log_info(os)("shared library load of %s was successful", filename);
#ifndef IA32
// Quickly test to make sure subnormals are correctly handled.
if (! IEEE_subnormal_handling_OK()) {
// We just dlopen()ed a library that mangled the floating-point
// flags. Silently fix things now.
int rtn = fesetenv(&default_fenv);
assert(rtn == 0, "fesetenv must succeed");
assert(IEEE_subnormal_handling_OK(), "fsetenv didn't work");
}
#endif // IA32
} }
return result; return result;
} }

View File

@ -409,3 +409,32 @@ STATIC_ASSERT(nth_bit(1|2) == 0x8);
STATIC_ASSERT(right_n_bits(3) == 0x7); STATIC_ASSERT(right_n_bits(3) == 0x7);
STATIC_ASSERT(right_n_bits(1|2) == 0x7); STATIC_ASSERT(right_n_bits(1|2) == 0x7);
// Check for Flush-To-Zero mode
// On some processors faster execution can be achieved by setting a
// mode to return zero for extremely small results, rather than an
// IEEE-754 subnormal number. This mode is not compatible with the
// Java Language Standard.
// We need the addition of _large_subnormal and _small_subnormal to be
// performed at runtime. _small_subnormal is volatile so that
// expressions involving it cannot be evaluated at compile time.
static const double large_subnormal_double
= jdouble_cast(0x0030000000000000); // 0x1.0p-1020;
static const volatile double small_subnormal_double
= jdouble_cast(0x0000000000000003); // 0x0.0000000000003p-1022;
// Quickly test to make sure IEEE-754 subnormal numbers are correctly
// handled.
bool IEEE_subnormal_handling_OK() {
// _small_subnormal is the smallest subnormal number that has two
// bits set. _large_subnormal is a number such that, when
// _small_subnormal is added to it, must be rounded according to the
// mode. These two tests detect the rounding mode in use. If
// subnormals are turned off (i.e. subnormals-are-zero) flush-to-
// zero mode is in use.
return (large_subnormal_double + small_subnormal_double > large_subnormal_double
&& -large_subnormal_double - small_subnormal_double < -large_subnormal_double);
}

View File

@ -1335,4 +1335,8 @@ template<typename K> int primitive_compare(const K& k0, const K& k1) {
template<typename T> template<typename T>
std::add_rvalue_reference_t<T> declval() noexcept; std::add_rvalue_reference_t<T> declval() noexcept;
// Quickly test to make sure IEEE-754 subnormal numbers are correctly
// handled.
bool IEEE_subnormal_handling_OK();
#endif // SHARE_UTILITIES_GLOBALDEFINITIONS_HPP #endif // SHARE_UTILITIES_GLOBALDEFINITIONS_HPP

View File

@ -75,6 +75,9 @@ compiler/jvmci/TestUncaughtErrorInCompileMethod.java 8309073 generic-all
compiler/codecache/CheckLargePages.java 8317831 linux-x64 compiler/codecache/CheckLargePages.java 8317831 linux-x64
compiler/floatingpoint/TestSubnormalFloat.java 8317810 generic-i586
compiler/floatingpoint/TestSubnormalDouble.java 8317810 generic-i586
############################################################################# #############################################################################
# :hotspot_gc # :hotspot_gc

View File

@ -0,0 +1,55 @@
/*
* Copyright (c) 2023, Red Hat, Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8295159
* @summary DSO created with -ffast-math breaks Java floating-point arithmetic
* @run main/othervm/native compiler.floatingpoint.TestSubnormalDouble
*/
package compiler.floatingpoint;
import static java.lang.System.loadLibrary;
public class TestSubnormalDouble {
static volatile double lastDouble;
private static void testDoubles() {
lastDouble = 0x1.0p-1074;
for (double x = lastDouble * 2; x <= 0x1.0p1022; x *= 2) {
if (x != x || x <= lastDouble) {
throw new RuntimeException("TEST FAILED: " + x);
}
lastDouble = x;
}
}
public static void main(String[] args) {
testDoubles();
System.out.println("Loading libfast-math.so");
loadLibrary("fast-math");
testDoubles();
System.out.println("Test passed.");
}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright (c) 2023, Red Hat, Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @bug 8295159
* @summary DSO created with -ffast-math breaks Java floating-point arithmetic
* @run main/othervm/native compiler.floatingpoint.TestSubnormalFloat
*/
package compiler.floatingpoint;
import static java.lang.System.loadLibrary;
public class TestSubnormalFloat {
static volatile float lastFloat;
private static void testFloats() {
lastFloat = 0x1.0p-149f;
for (float x = lastFloat * 2; x <= 0x1.0p127f; x *= 2) {
if (x != x || x <= lastFloat) {
throw new RuntimeException("TEST FAILED: " + x);
}
lastFloat = x;
}
}
public static void main(String[] args) {
testFloats();
System.out.println("Loading libfast-math.so");
loadLibrary("fast-math");
testFloats();
System.out.println("Test passed.");
}
}

View File

@ -0,0 +1,60 @@
/*
* Copyright (c) 2023, Red Hat, Inc. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "jni.h"
// See GCC bug 55522:
//
// "When used at link-time, [ GCC with -ffast-math ] may include
// libraries or startup files that change the default FPU control word
// or other similar optimizations."
//
// This breaks Java's floating point arithmetic.
#if defined(__GNUC__)
// On systems on which GCC bug 55522 has been fixed, this constructor
// serves to reproduce that bug for the purposes of testing HotSpot.
static void __attribute__((constructor)) set_flush_to_zero(void) {
#if defined(__x86_64__)
#define MXCSR_DAZ (1 << 6) /* Enable denormals are zero mode */
#define MXCSR_FTZ (1 << 15) /* Enable flush to zero mode */
unsigned int mxcsr = __builtin_ia32_stmxcsr ();
mxcsr |= MXCSR_DAZ | MXCSR_FTZ;
__builtin_ia32_ldmxcsr (mxcsr);
#elif defined(__aarch64__)
#define _FPU_FPCR_FZ (unsigned long)0x1000000
#define _FPU_SETCW(fpcr) \
__asm__ __volatile__ ("msr fpcr, %0" : : "r" (fpcr));
/* Flush to zero, round to nearest, IEEE exceptions disabled. */
_FPU_SETCW (_FPU_FPCR_FZ);
#endif // CPU arch
}
#endif // defined(__GNUC__)