8037226: compiler/7196199/Test7196199.java fails on 32-bit linux with MaxVectorSize > 16
Summary: verify YMM registers after signal processing and set limit on vector's size.
Reviewed-by: iveresov, twisti
--- a/hotspot/src/cpu/x86/vm/vm_version_x86.cpp Thu Mar 13 14:55:34 2014 -0700
+++ b/hotspot/src/cpu/x86/vm/vm_version_x86.cpp Fri Mar 14 17:28:58 2014 -0700
@@ -50,8 +50,13 @@
const char* VM_Version::_features_str = "";
VM_Version::CpuidInfo VM_Version::_cpuid_info = { 0, };
+// Address of instruction which causes SEGV
+address VM_Version::_cpuinfo_segv_addr = 0;
+// Address of instruction after the one which causes SEGV
+address VM_Version::_cpuinfo_cont_addr = 0;
+
static BufferBlob* stub_blob;
-static const int stub_size = 550;
+static const int stub_size = 600;
extern "C" {
typedef void (*getPsrInfo_stub_t)(void*);
@@ -234,9 +239,9 @@
// Check if OS has enabled XGETBV instruction to access XCR0
// (OSXSAVE feature flag) and CPU supports AVX
//
- __ andl(rcx, 0x18000000);
+ __ andl(rcx, 0x18000000); // cpuid1 bits osxsave | avx
__ cmpl(rcx, 0x18000000);
- __ jccb(Assembler::notEqual, sef_cpuid);
+ __ jccb(Assembler::notEqual, sef_cpuid); // jump if AVX is not supported
//
// XCR0, XFEATURE_ENABLED_MASK register
@@ -247,6 +252,47 @@
__ movl(Address(rsi, 0), rax);
__ movl(Address(rsi, 4), rdx);
+ __ andl(rax, 0x6); // xcr0 bits sse | ymm
+ __ cmpl(rax, 0x6);
+ __ jccb(Assembler::notEqual, sef_cpuid); // jump if AVX is not supported
+
+ //
+ // Some OSs have a bug when upper 128bits of YMM
+ // registers are not restored after a signal processing.
+ // Generate SEGV here (reference through NULL)
+ // and check upper YMM bits after it.
+ //
+ VM_Version::set_avx_cpuFeatures(); // Enable temporary to pass asserts
+
+ // load value into all 32 bytes of ymm7 register
+ __ movl(rcx, VM_Version::ymm_test_value());
+
+ __ movdl(xmm0, rcx);
+ __ pshufd(xmm0, xmm0, 0x00);
+ __ vinsertf128h(xmm0, xmm0, xmm0);
+ __ vmovdqu(xmm7, xmm0);
+#ifdef _LP64
+ __ vmovdqu(xmm8, xmm0);
+ __ vmovdqu(xmm15, xmm0);
+#endif
+
+ __ xorl(rsi, rsi);
+ VM_Version::set_cpuinfo_segv_addr( __ pc() );
+ // Generate SEGV
+ __ movl(rax, Address(rsi, 0));
+
+ VM_Version::set_cpuinfo_cont_addr( __ pc() );
+ // Returns here after signal. Save xmm0 to check it later.
+ __ lea(rsi, Address(rbp, in_bytes(VM_Version::ymm_save_offset())));
+ __ vmovdqu(Address(rsi, 0), xmm0);
+ __ vmovdqu(Address(rsi, 32), xmm7);
+#ifdef _LP64
+ __ vmovdqu(Address(rsi, 64), xmm8);
+ __ vmovdqu(Address(rsi, 96), xmm15);
+#endif
+
+ VM_Version::clean_cpuFeatures();
+
//
// cpuid(0x7) Structured Extended Features
//
@@ -540,14 +586,28 @@
if (MaxVectorSize > 32) {
FLAG_SET_DEFAULT(MaxVectorSize, 32);
}
- if (MaxVectorSize > 16 && UseAVX == 0) {
- // Only supported with AVX+
+ if (MaxVectorSize > 16 && (UseAVX == 0 || !os_supports_avx_vectors())) {
+ // 32 bytes vectors (in YMM) are only supported with AVX+
FLAG_SET_DEFAULT(MaxVectorSize, 16);
}
if (UseSSE < 2) {
- // Only supported with SSE2+
+ // Vectors (in XMM) are only supported with SSE2+
FLAG_SET_DEFAULT(MaxVectorSize, 0);
}
+#ifdef ASSERT
+ if (supports_avx() && PrintMiscellaneous && Verbose && TraceNewVectors) {
+ tty->print_cr("State of YMM registers after signal handle:");
+ int nreg = 2 LP64_ONLY(+2);
+ const char* ymm_name[4] = {"0", "7", "8", "15"};
+ for (int i = 0; i < nreg; i++) {
+ tty->print("YMM%s:", ymm_name[i]);
+ for (int j = 7; j >=0; j--) {
+ tty->print(" %x", _cpuid_info.ymm_save[i*8 + j]);
+ }
+ tty->cr();
+ }
+ }
+#endif
}
#endif
@@ -678,14 +738,6 @@
}
}
}
-#if defined(COMPILER2) && defined(_ALLBSD_SOURCE)
- if (MaxVectorSize > 16) {
- // Limit vectors size to 16 bytes on BSD until it fixes
- // restoring upper 128bit of YMM registers on return
- // from signal handler.
- FLAG_SET_DEFAULT(MaxVectorSize, 16);
- }
-#endif // COMPILER2
// Use count leading zeros count instruction if available.
if (supports_lzcnt()) {
@@ -814,6 +866,11 @@
if (UseAES) {
tty->print(" UseAES=1");
}
+#ifdef COMPILER2
+ if (MaxVectorSize > 0) {
+ tty->print(" MaxVectorSize=%d", MaxVectorSize);
+ }
+#endif
tty->cr();
tty->print("Allocation");
if (AllocatePrefetchStyle <= 0 || UseSSE == 0 && !supports_3dnow_prefetch()) {
--- a/hotspot/src/cpu/x86/vm/vm_version_x86.hpp Thu Mar 13 14:55:34 2014 -0700
+++ b/hotspot/src/cpu/x86/vm/vm_version_x86.hpp Fri Mar 14 17:28:58 2014 -0700
@@ -229,6 +229,9 @@
// 0 if this instruction is not available
static const char* _features_str;
+ static address _cpuinfo_segv_addr; // address of instruction which causes SEGV
+ static address _cpuinfo_cont_addr; // address of instruction after the one which causes SEGV
+
enum {
CPU_CX8 = (1 << 0), // next bits are from cpuid 1 (EDX)
CPU_CMOV = (1 << 1),
@@ -361,6 +364,9 @@
// extended control register XCR0 (the XFEATURE_ENABLED_MASK register)
XemXcr0Eax xem_xcr0_eax;
uint32_t xem_xcr0_edx; // reserved
+
+ // Space to save ymm registers after signal handle
+ int ymm_save[8*4]; // Save ymm0, ymm7, ymm8, ymm15
};
// The actual cpuid info block
@@ -460,6 +466,21 @@
return result;
}
+ static bool os_supports_avx_vectors() {
+ if (!supports_avx()) {
+ return false;
+ }
+ // Verify that OS save/restore all bits of AVX registers
+ // during signal processing.
+ int nreg = 2 LP64_ONLY(+2);
+ for (int i = 0; i < 8 * nreg; i++) { // 32 bytes per ymm register
+ if (_cpuid_info.ymm_save[i] != ymm_test_value()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
static void get_processor_features();
public:
@@ -476,6 +497,19 @@
static ByteSize tpl_cpuidB1_offset() { return byte_offset_of(CpuidInfo, tpl_cpuidB1_eax); }
static ByteSize tpl_cpuidB2_offset() { return byte_offset_of(CpuidInfo, tpl_cpuidB2_eax); }
static ByteSize xem_xcr0_offset() { return byte_offset_of(CpuidInfo, xem_xcr0_eax); }
+ static ByteSize ymm_save_offset() { return byte_offset_of(CpuidInfo, ymm_save); }
+
+ // The value used to check ymm register after signal handle
+ static int ymm_test_value() { return 0xCAFEBABE; }
+
+ static void set_cpuinfo_segv_addr(address pc) { _cpuinfo_segv_addr = pc; }
+ static bool is_cpuinfo_segv_addr(address pc) { return _cpuinfo_segv_addr == pc; }
+ static void set_cpuinfo_cont_addr(address pc) { _cpuinfo_cont_addr = pc; }
+ static address cpuinfo_cont_addr() { return _cpuinfo_cont_addr; }
+
+ static void clean_cpuFeatures() { _cpuFeatures = 0; }
+ static void set_avx_cpuFeatures() { _cpuFeatures = (CPU_SSE | CPU_SSE2 | CPU_AVX); }
+
// Initialization
static void initialize();
--- a/hotspot/src/os/windows/vm/os_windows.cpp Thu Mar 13 14:55:34 2014 -0700
+++ b/hotspot/src/os/windows/vm/os_windows.cpp Fri Mar 14 17:28:58 2014 -0700
@@ -2425,6 +2425,12 @@
}
}
+ if ((exception_code == EXCEPTION_ACCESS_VIOLATION) &&
+ VM_Version::is_cpuinfo_segv_addr(pc)) {
+ // Verify that OS save/restore AVX registers.
+ return Handle_Exception(exceptionInfo, VM_Version::cpuinfo_cont_addr());
+ }
+
if (t != NULL && t->is_Java_thread()) {
JavaThread* thread = (JavaThread*) t;
bool in_java = thread->thread_state() == _thread_in_Java;
--- a/hotspot/src/os_cpu/bsd_x86/vm/os_bsd_x86.cpp Thu Mar 13 14:55:34 2014 -0700
+++ b/hotspot/src/os_cpu/bsd_x86/vm/os_bsd_x86.cpp Fri Mar 14 17:28:58 2014 -0700
@@ -492,6 +492,11 @@
}
}
+ if ((sig == SIGSEGV || sig == SIGBUS) && VM_Version::is_cpuinfo_segv_addr(pc)) {
+ // Verify that OS save/restore AVX registers.
+ stub = VM_Version::cpuinfo_cont_addr();
+ }
+
// We test if stub is already set (by the stack overflow code
// above) so it is not overwritten by the code that follows. This
// check is not required on other platforms, because on other
--- a/hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.cpp Thu Mar 13 14:55:34 2014 -0700
+++ b/hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.cpp Fri Mar 14 17:28:58 2014 -0700
@@ -338,6 +338,11 @@
}
}
+ if ((sig == SIGSEGV) && VM_Version::is_cpuinfo_segv_addr(pc)) {
+ // Verify that OS save/restore AVX registers.
+ stub = VM_Version::cpuinfo_cont_addr();
+ }
+
if (thread->thread_state() == _thread_in_Java) {
// Java thread running in Java code => find exception handler if any
// a fault inside compiled code, the interpreter, or a stub
--- a/hotspot/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp Thu Mar 13 14:55:34 2014 -0700
+++ b/hotspot/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp Fri Mar 14 17:28:58 2014 -0700
@@ -459,6 +459,11 @@
}
}
+ if ((sig == SIGSEGV) && VM_Version::is_cpuinfo_segv_addr(pc)) {
+ // Verify that OS save/restore AVX registers.
+ stub = VM_Version::cpuinfo_cont_addr();
+ }
+
if (thread->thread_state() == _thread_in_vm) {
if (sig == SIGBUS && info->si_code == BUS_OBJERR && thread->doing_unsafe_access()) {
stub = StubRoutines::handler_for_unsafe_access();