8295159: DSO created with -ffast-math breaks Java floating-point arithmetic
Reviewed-by: ihse, dholmes, stuefe
This commit is contained in:
parent
c099cf53f2
commit
df599dbb9b
@ -851,6 +851,7 @@ ifeq ($(call isTargetOs, linux), true)
|
||||
BUILD_TEST_exeinvoke_exeinvoke.c_OPTIMIZATION := NONE
|
||||
BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exeFPRegs := -ldl
|
||||
BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libAsyncGetCallTraceTest := -ldl
|
||||
BUILD_HOTSPOT_JTREG_LIBRARIES_LDFLAGS_libfast-math := -ffast-math
|
||||
else
|
||||
BUILD_HOTSPOT_JTREG_EXCLUDE += libtest-rw.c libtest-rwx.c \
|
||||
exeinvoke.c exestack-gap.c exestack-tls.c libAsyncGetCallTraceTest.cpp
|
||||
|
@ -78,6 +78,7 @@
|
||||
# include <dlfcn.h>
|
||||
# include <errno.h>
|
||||
# include <fcntl.h>
|
||||
# include <fenv.h>
|
||||
# include <inttypes.h>
|
||||
# include <poll.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
|
||||
// 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__
|
||||
void * os::dll_load(const char *filename, char *ebuf, int ebuflen) {
|
||||
#ifdef STATIC_BUILD
|
||||
@ -984,7 +1020,7 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) {
|
||||
|
||||
void* result;
|
||||
JFR_ONLY(NativeLibraryLoadEvent load_event(filename, &result);)
|
||||
result = ::dlopen(filename, RTLD_LAZY);
|
||||
result = os::Bsd::dlopen_helper(filename, RTLD_LAZY);
|
||||
if (result != nullptr) {
|
||||
Events::log_dll_message(nullptr, "Loaded shared library %s", filename);
|
||||
// Successful loading
|
||||
@ -1017,7 +1053,7 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) {
|
||||
|
||||
void* result;
|
||||
JFR_ONLY(NativeLibraryLoadEvent load_event(filename, &result);)
|
||||
result = ::dlopen(filename, RTLD_LAZY);
|
||||
result = os::Bsd::dlopen_helper(filename, RTLD_LAZY);
|
||||
if (result != nullptr) {
|
||||
Events::log_dll_message(nullptr, "Loaded shared library %s", filename);
|
||||
// Successful loading
|
||||
|
@ -70,6 +70,8 @@ class os::Bsd {
|
||||
// Real-time clock functions
|
||||
static void clock_init(void);
|
||||
|
||||
static void *dlopen_helper(const char *path, int mode);
|
||||
|
||||
// Stack repair handling
|
||||
|
||||
// none present
|
||||
|
@ -95,6 +95,7 @@
|
||||
# include <signal.h>
|
||||
# include <endian.h>
|
||||
# include <errno.h>
|
||||
# include <fenv.h>
|
||||
# include <dlfcn.h>
|
||||
# include <stdio.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) {
|
||||
#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;
|
||||
JFR_ONLY(NativeLibraryLoadEvent load_event(filename, &result);)
|
||||
result = ::dlopen(filename, RTLD_LAZY);
|
||||
@ -1820,6 +1840,16 @@ void * os::Linux::dlopen_helper(const char *filename, char *ebuf, int ebuflen) {
|
||||
} else {
|
||||
Events::log_dll_message(nullptr, "Loaded shared library %s", 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;
|
||||
}
|
||||
|
@ -409,3 +409,32 @@ STATIC_ASSERT(nth_bit(1|2) == 0x8);
|
||||
|
||||
STATIC_ASSERT(right_n_bits(3) == 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);
|
||||
}
|
||||
|
@ -1335,4 +1335,8 @@ template<typename K> int primitive_compare(const K& k0, const K& k1) {
|
||||
template<typename T>
|
||||
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
|
||||
|
@ -75,6 +75,9 @@ compiler/jvmci/TestUncaughtErrorInCompileMethod.java 8309073 generic-all
|
||||
|
||||
compiler/codecache/CheckLargePages.java 8317831 linux-x64
|
||||
|
||||
compiler/floatingpoint/TestSubnormalFloat.java 8317810 generic-i586
|
||||
compiler/floatingpoint/TestSubnormalDouble.java 8317810 generic-i586
|
||||
|
||||
#############################################################################
|
||||
|
||||
# :hotspot_gc
|
||||
|
@ -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.");
|
||||
}
|
||||
}
|
@ -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.");
|
||||
}
|
||||
}
|
60
test/hotspot/jtreg/compiler/floatingpoint/libfast-math.c
Normal file
60
test/hotspot/jtreg/compiler/floatingpoint/libfast-math.c
Normal 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__)
|
Loading…
Reference in New Issue
Block a user