RFR(m): 8220351: Cross-modifying code
David Holmes
david.holmes at oracle.com
Tue Mar 12 08:00:04 UTC 2019
Hi Robbin,
I have comments on the various changes below, but now I've gone through
that all I have a meta-comment on the overall approach.
AFAICS the only time you have to sync the i-stream is when returning to
_thread_in_Java, as that is the only state in which you can execute the
modified code - no? Handling this at only those thread-state transitions
would seem a lot simpler/cleaner.
On 9/03/2019 1:24 am, Robbin Ehn wrote:
> Hi all, please review.
>
> Issue:
> https://bugs.openjdk.java.net/browse/JDK-8220351
> Changeset:
> http://cr.openjdk.java.net/~rehn/8220351/webrev/
src/hotspot/cpu/x86/assembler_x86.hpp
As discussed elsewhere mfence() is no longer the advised mechanism, but
instead a serializing instruction like cpuid must be used.
---
src/hotspot/cpu/x86/macroAssembler_x86.cpp
MacroAssembler::debug32 seems quite bizarre. First, pre-existing:
412 JavaThread* thread = JavaThread::current();
413 JavaThreadState saved_state = thread->thread_state();
414 thread->set_thread_state(_thread_in_vm);
415 if (ShowMessageBoxOnError) {
416 JavaThread* thread = JavaThread::current();
417 JavaThreadState saved_state = thread->thread_state();
418 thread->set_thread_state(_thread_in_vm);
Lines 416-418 are redundant.
IIUC you need to serialize after leaving a safepoint but this code
cannot enter a safepoint. The state is _thread_in_vm, which prevents a
safepoint from being reached and it does not do anything to change state
(the ttyLocker locks without a safepoint check and so will not
transition to _thread_blocked). So your change seems unnecessary.
Further if this code could hit a safepoint check then the place for the
new code would be in that return path from the safepoint not in this code.
---
src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp
2562 __ lock();
2563 __ addl(Address(r15_thread, JavaThread::thread_state_offset()), 1
/* _thread_in_native_trans */);
This changes seems incorrect. You need a storeload barrier (at least)
after the update to thread_state but your lock() prefix would place
barriers before I think. In any case I find the existing code clearer
and more obviously correct.
---
src/hotspot/cpu/x86/stubGenerator_x86_64.cpp
src/hotspot/os_cpu/windows_x86/orderAccess_windows_x86.hpp
src/hotspot/share/runtime/orderAccess.cpp
src/hotspot/share/runtime/stubRoutines.cpp
src/hotspot/share/runtime/stubRoutines.hpp
I don't understand why we need a stub for this (or for fence() for that
matter) ??
---
src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp
1090 __ lock();
1091 __ addl(Address(thread, JavaThread::thread_state_offset()), 1
/*_thread_in_native_trans*/);
Same comment as above.
---
src/hotspot/os_cpu/aix_ppc/orderAccess_aix_ppc.hpp
src/hotspot/os_cpu/linux_ppc/orderAccess_linux_ppc.hpp
PPC has isync for instruction stream synchronization, so I'd expect to
see that used in OrderAccess::instruction_pipeline()
---
src/hotspot/os_cpu/bsd_x86/orderAccess_bsd_x86.hpp
src/hotspot/os_cpu/linux_x86/orderAccess_linux_x86.hpp
src/hotspot/os_cpu/solaris_x86/orderAccess_solaris_x86.hpp
s/mfence/cpuid
---
src/hotspot/os_cpu/linux_aarch64/orderAccess_linux_aarch64.hpp
src/hotspot/os_cpu/linux_arm/orderAccess_linux_arm.hpp
Aarch64/ARM has ISB for instruction stream sync.
---
src/hotspot/os_cpu/linux_s390/orderAccess_linux_s390.hpp
Need input from s390 folk.
---
src/hotspot/os_cpu/linux_sparc/orderAccess_linux_sparc.hpp
src/hotspot/os_cpu/solaris_sparc/orderAccess_solaris_sparc.hpp
SPARC needs a FLUSH
"H.1.6 Self-Modifying Code
If a program includes self-modifying code, it must issue a FLUSH
instruction for each modified doubleword of instructions (or a call to
supervisor software having an equivalent effect)." [SPARCV9 Architecture
manual]
---
src/hotspot/share/classfile/classFileParser.cpp
Change is unrelated to this bug.
---
src/hotspot/share/prims/jvmtiEnv.cpp
The changes seem unnecessary. As the code comment states we are going
from one safepoint unsafe state to another. There is no way to execute
any JIT'd code before returning to Java, and that return is where we
should sync if needed.
---
src/hotspot/share/prims/jvmtiExport.cpp
The change is unnecessary as we are executing in the VMThread. Not only
would it be the one doing the code changes at a safepoint, it never
executes compiled Java code anyway.
---
src/hotspot/share/runtime/interfaceSupport.inline.hpp
src/hotspot/share/runtime/safepoint.cpp
My initial thought was that the OrderAccess::instruction_pipeline()
should be buried in the safepoint blocking code, but in some cases we
may not have actually blocked at the safepoint and so (theoretically at
least) may need to sync with the i-stream in case an intervening
safepoint changed some code. Can't help but worry that unconditionally
doing this on native transitions is going to hurt JNI performance. It
also looks like we will double up on the instruction_pipeline() in some
cases.
---
src/hotspot/share/runtime/thread.cpp
1839 OrderAccess::instruction_pipeline();
Can't see why this is necessary here, we should sync when returning to
_thread_in_Java.
2471 if (thread_state() != _thread_blocked) {
2472 OrderAccess::instruction_pipeline();
2473 }
Again not sure why this is necessary. The call to java_suspend_self in
the not-blocked case comes from
JavaThread::handle_special_runtime_exit_condition, which itself is
called from the transition code, so I'd expect this to be done in the
transition code.
thread->set_thread_state(_thread_blocked);
thread->java_suspend_self();
thread->set_thread_state(state);
+ OrderAccess::instruction_pipeline();
Again seems unnecessary - especially given you have one in
java_suspend_self() already! (Notwithstanding I think that one should be
removed.)
Thanks,
David
-----
> After a JavaThread have been in a safepoint/(handshake) safe state it
> can start
> executing updated code. E.g. an oop in the instruction stream can have been
> updated.
>
> Most hardware's require a barrier or that the code cross modified is far
> away to
> guarantee that the thread executing the updated instruction stream sees the
> modification.
>
> What far away is and how far an update instruction stream is from a
> safepoint
> safe state is not clear.
>
> To be compliant with those architectures an instruction stream barrier
> must be
> added when leaving the safepoint safe state.
>
> There may be crashes today due to this missing barrier.
> A new CPU with deeper pipeline or changes to the VM which moves a
> safepoint safe
> state closer to a nmethod can increase changes of a crash.
>
> A few benchmarks will see a performance regression with the extra
> serializing,
> such as Octane-Crypto -5% (x86).
>
> With a much more elaborate fix, such as changing the local poll to have
> more
> than two states (armed/disarmed), it would be possible to only emit such
> instruction stream barrier when the poll have been armed while the
> thread was
> safepoint safe.
>
> Passes t1-3 on our platforms, and test built what I can.
>
> Thanks, Robbin
More information about the hotspot-dev
mailing list