Skip to content

Commit 35a9f68

Browse files
committed
8226705: [REDO] Deoptimize with handshakes
Reviewed-by: eosterlund, dcubed, dlong, pchilanomate
1 parent 336b741 commit 35a9f68

33 files changed

+398
-326
lines changed

src/hotspot/share/aot/aotCodeHeap.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "memory/universe.hpp"
3939
#include "oops/compressedOops.hpp"
4040
#include "oops/method.inline.hpp"
41+
#include "runtime/deoptimization.hpp"
4142
#include "runtime/handles.inline.hpp"
4243
#include "runtime/os.hpp"
4344
#include "runtime/safepointVerifiers.hpp"
@@ -351,7 +352,10 @@ void AOTCodeHeap::publish_aot(const methodHandle& mh, AOTMethodData* method_data
351352
#ifdef TIERED
352353
mh->set_aot_code(aot);
353354
#endif
354-
Method::set_code(mh, aot);
355+
{
356+
MutexLocker pl(CompiledMethod_lock, Mutex::_no_safepoint_check_flag);
357+
Method::set_code(mh, aot);
358+
}
355359
if (PrintAOT || (PrintCompilation && PrintAOT)) {
356360
PauseNoSafepointVerifier pnsv(&nsv); // aot code is registered already
357361
aot->print_on(tty, NULL);
@@ -731,8 +735,7 @@ void AOTCodeHeap::sweep_dependent_methods(int* indexes, int methods_cnt) {
731735
}
732736
}
733737
if (marked > 0) {
734-
VM_Deoptimize op;
735-
VMThread::execute(&op);
738+
Deoptimization::deoptimize_all_marked();
736739
}
737740
}
738741

src/hotspot/share/aot/aotCompiledMethod.cpp

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ bool AOTCompiledMethod::make_not_entrant_helper(int new_state) {
165165

166166
{
167167
// Enter critical section. Does not block for safepoint.
168-
MutexLocker pl(Patching_lock, Mutex::_no_safepoint_check_flag);
168+
MutexLocker pl(CompiledMethod_lock, Mutex::_no_safepoint_check_flag);
169169

170170
if (*_state_adr == new_state) {
171171
// another thread already performed this transition so nothing
@@ -188,12 +188,10 @@ bool AOTCompiledMethod::make_not_entrant_helper(int new_state) {
188188
#endif
189189

190190
// Remove AOTCompiledMethod from method.
191-
if (method() != NULL && (method()->code() == this ||
192-
method()->from_compiled_entry() == verified_entry_point())) {
193-
HandleMark hm;
194-
method()->clear_code(false /* already owns Patching_lock */);
191+
if (method() != NULL) {
192+
method()->unlink_code(this);
195193
}
196-
} // leave critical region under Patching_lock
194+
} // leave critical region under CompiledMethod_lock
197195

198196

199197
if (TraceCreateZombies) {
@@ -208,17 +206,16 @@ bool AOTCompiledMethod::make_not_entrant_helper(int new_state) {
208206
#ifdef TIERED
209207
bool AOTCompiledMethod::make_entrant() {
210208
assert(!method()->is_old(), "reviving evolved method!");
211-
assert(*_state_adr != not_entrant, "%s", method()->has_aot_code() ? "has_aot_code() not cleared" : "caller didn't check has_aot_code()");
212209

213210
// Make sure the method is not flushed in case of a safepoint in code below.
214211
methodHandle the_method(method());
215212
NoSafepointVerifier nsv;
216213

217214
{
218215
// Enter critical section. Does not block for safepoint.
219-
MutexLocker pl(Patching_lock, Mutex::_no_safepoint_check_flag);
216+
MutexLocker pl(CompiledMethod_lock, Mutex::_no_safepoint_check_flag);
220217

221-
if (*_state_adr == in_use) {
218+
if (*_state_adr == in_use || *_state_adr == not_entrant) {
222219
// another thread already performed this transition so nothing
223220
// to do, but return false to indicate this.
224221
return false;
@@ -230,7 +227,7 @@ bool AOTCompiledMethod::make_entrant() {
230227

231228
// Log the transition once
232229
log_state_change();
233-
} // leave critical region under Patching_lock
230+
} // leave critical region under CompiledMethod_lock
234231

235232

236233
if (TraceCreateZombies) {

src/hotspot/share/ci/ciEnv.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,7 +1072,10 @@ void ciEnv::register_method(ciMethod* target,
10721072
task()->comp_level(), method_name);
10731073
}
10741074
// Allow the code to be executed
1075-
method->set_code(method, nm);
1075+
MutexLocker ml(CompiledMethod_lock, Mutex::_no_safepoint_check_flag);
1076+
if (nm->make_in_use()) {
1077+
method->set_code(method, nm);
1078+
}
10761079
} else {
10771080
LogTarget(Info, nmethod, install) lt;
10781081
if (lt.is_enabled()) {
@@ -1081,9 +1084,11 @@ void ciEnv::register_method(ciMethod* target,
10811084
lt.print("Installing osr method (%d) %s @ %d",
10821085
task()->comp_level(), method_name, entry_bci);
10831086
}
1084-
method->method_holder()->add_osr_nmethod(nm);
1087+
MutexLocker ml(CompiledMethod_lock, Mutex::_no_safepoint_check_flag);
1088+
if (nm->make_in_use()) {
1089+
method->method_holder()->add_osr_nmethod(nm);
1090+
}
10851091
}
1086-
nm->make_in_use();
10871092
}
10881093
} // safepoints are allowed again
10891094

src/hotspot/share/code/codeCache.cpp

Lines changed: 6 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,28 +1143,17 @@ void CodeCache::flush_evol_dependents() {
11431143

11441144
// At least one nmethod has been marked for deoptimization
11451145

1146-
// All this already happens inside a VM_Operation, so we'll do all the work here.
1147-
// Stuff copied from VM_Deoptimize and modified slightly.
1148-
1149-
// We do not want any GCs to happen while we are in the middle of this VM operation
1150-
ResourceMark rm;
1151-
DeoptimizationMarker dm;
1152-
1153-
// Deoptimize all activations depending on marked nmethods
1154-
Deoptimization::deoptimize_dependents();
1155-
1156-
// Make the dependent methods not entrant
1157-
make_marked_nmethods_not_entrant();
1146+
Deoptimization::deoptimize_all_marked();
11581147
}
11591148
#endif // INCLUDE_JVMTI
11601149

1161-
// Deoptimize all methods
1150+
// Mark methods for deopt (if safe or possible).
11621151
void CodeCache::mark_all_nmethods_for_deoptimization() {
11631152
MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
11641153
CompiledMethodIterator iter(CompiledMethodIterator::only_alive_and_not_unloading);
11651154
while(iter.next()) {
11661155
CompiledMethod* nm = iter.method();
1167-
if (!nm->method()->is_method_handle_intrinsic()) {
1156+
if (!nm->is_native_method()) {
11681157
nm->mark_for_deoptimization();
11691158
}
11701159
}
@@ -1192,7 +1181,7 @@ void CodeCache::make_marked_nmethods_not_entrant() {
11921181
CompiledMethodIterator iter(CompiledMethodIterator::only_alive_and_not_unloading);
11931182
while(iter.next()) {
11941183
CompiledMethod* nm = iter.method();
1195-
if (nm->is_marked_for_deoptimization() && !nm->is_not_entrant()) {
1184+
if (nm->is_marked_for_deoptimization()) {
11961185
nm->make_not_entrant();
11971186
}
11981187
}
@@ -1204,17 +1193,12 @@ void CodeCache::flush_dependents_on(InstanceKlass* dependee) {
12041193

12051194
if (number_of_nmethods_with_dependencies() == 0) return;
12061195

1207-
// CodeCache can only be updated by a thread_in_VM and they will all be
1208-
// stopped during the safepoint so CodeCache will be safe to update without
1209-
// holding the CodeCache_lock.
1210-
12111196
KlassDepChange changes(dependee);
12121197

12131198
// Compute the dependent nmethods
12141199
if (mark_for_deoptimization(changes) > 0) {
12151200
// At least one nmethod has been marked for deoptimization
1216-
VM_Deoptimize op;
1217-
VMThread::execute(&op);
1201+
Deoptimization::deoptimize_all_marked();
12181202
}
12191203
}
12201204

@@ -1223,26 +1207,9 @@ void CodeCache::flush_dependents_on_method(const methodHandle& m_h) {
12231207
// --- Compile_lock is not held. However we are at a safepoint.
12241208
assert_locked_or_safepoint(Compile_lock);
12251209

1226-
// CodeCache can only be updated by a thread_in_VM and they will all be
1227-
// stopped dring the safepoint so CodeCache will be safe to update without
1228-
// holding the CodeCache_lock.
1229-
12301210
// Compute the dependent nmethods
12311211
if (mark_for_deoptimization(m_h()) > 0) {
1232-
// At least one nmethod has been marked for deoptimization
1233-
1234-
// All this already happens inside a VM_Operation, so we'll do all the work here.
1235-
// Stuff copied from VM_Deoptimize and modified slightly.
1236-
1237-
// We do not want any GCs to happen while we are in the middle of this VM operation
1238-
ResourceMark rm;
1239-
DeoptimizationMarker dm;
1240-
1241-
// Deoptimize all activations depending on marked nmethods
1242-
Deoptimization::deoptimize_dependents();
1243-
1244-
// Make the dependent methods not entrant
1245-
make_marked_nmethods_not_entrant();
1212+
Deoptimization::deoptimize_all_marked();
12461213
}
12471214
}
12481215

src/hotspot/share/code/compiledMethod.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ const char* CompiledMethod::state() const {
103103
}
104104
}
105105

106+
//-----------------------------------------------------------------------------
107+
void CompiledMethod::mark_for_deoptimization(bool inc_recompile_counts) {
108+
MutexLocker ml(CompiledMethod_lock->owned_by_self() ? NULL : CompiledMethod_lock,
109+
Mutex::_no_safepoint_check_flag);
110+
_mark_for_deoptimization_status = (inc_recompile_counts ? deoptimize : deoptimize_noupdate);
111+
}
112+
106113
//-----------------------------------------------------------------------------
107114

108115
ExceptionCache* CompiledMethod::exception_cache_acquire() const {

src/hotspot/share/code/compiledMethod.hpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -244,10 +244,9 @@ class CompiledMethod : public CodeBlob {
244244
bool is_at_poll_return(address pc);
245245
bool is_at_poll_or_poll_return(address pc);
246246

247-
bool is_marked_for_deoptimization() const { return _mark_for_deoptimization_status != not_marked; }
248-
void mark_for_deoptimization(bool inc_recompile_counts = true) {
249-
_mark_for_deoptimization_status = (inc_recompile_counts ? deoptimize : deoptimize_noupdate);
250-
}
247+
bool is_marked_for_deoptimization() const { return _mark_for_deoptimization_status != not_marked; }
248+
void mark_for_deoptimization(bool inc_recompile_counts = true);
249+
251250
bool update_recompile_counts() const {
252251
// Update recompile counts when either the update is explicitly requested (deoptimize)
253252
// or the nmethod is not marked for deoptimization at all (not_marked).

src/hotspot/share/code/nmethod.cpp

Lines changed: 25 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#include "oops/oop.inline.hpp"
5151
#include "prims/jvmtiImpl.hpp"
5252
#include "runtime/atomic.hpp"
53+
#include "runtime/deoptimization.hpp"
5354
#include "runtime/flags/flagSetting.hpp"
5455
#include "runtime/frame.inline.hpp"
5556
#include "runtime/handles.inline.hpp"
@@ -476,7 +477,6 @@ nmethod* nmethod::new_native_nmethod(const methodHandle& method,
476477
debug_only(nm->verify();) // might block
477478

478479
nm->log_new_nmethod();
479-
nm->make_in_use();
480480
}
481481
return nm;
482482
}
@@ -1138,6 +1138,11 @@ void nmethod::inc_decompile_count() {
11381138

11391139
bool nmethod::try_transition(int new_state_int) {
11401140
signed char new_state = new_state_int;
1141+
#ifdef DEBUG
1142+
if (new_state != unloaded) {
1143+
assert_lock_strong(CompiledMethod_lock);
1144+
}
1145+
#endif
11411146
for (;;) {
11421147
signed char old_state = Atomic::load(&_state);
11431148
if (old_state >= new_state) {
@@ -1193,11 +1198,7 @@ void nmethod::make_unloaded() {
11931198
// have the Method* live here, in case we unload the nmethod because
11941199
// it is pointing to some oop (other than the Method*) being unloaded.
11951200
if (_method != NULL) {
1196-
// OSR methods point to the Method*, but the Method* does not
1197-
// point back!
1198-
if (_method->code() == this) {
1199-
_method->clear_code(); // Break a cycle
1200-
}
1201+
_method->unlink_code(this);
12011202
}
12021203

12031204
// Make the class unloaded - i.e., change state and notify sweeper
@@ -1281,16 +1282,9 @@ void nmethod::log_state_change() const {
12811282
}
12821283
}
12831284

1284-
void nmethod::unlink_from_method(bool acquire_lock) {
1285-
// We need to check if both the _code and _from_compiled_code_entry_point
1286-
// refer to this nmethod because there is a race in setting these two fields
1287-
// in Method* as seen in bugid 4947125.
1288-
// If the vep() points to the zombie nmethod, the memory for the nmethod
1289-
// could be flushed and the compiler and vtable stubs could still call
1290-
// through it.
1291-
if (method() != NULL && (method()->code() == this ||
1292-
method()->from_compiled_entry() == verified_entry_point())) {
1293-
method()->clear_code(acquire_lock);
1285+
void nmethod::unlink_from_method() {
1286+
if (method() != NULL) {
1287+
method()->unlink_code(this);
12941288
}
12951289
}
12961290

@@ -1317,24 +1311,24 @@ bool nmethod::make_not_entrant_or_zombie(int state) {
13171311

13181312
// during patching, depending on the nmethod state we must notify the GC that
13191313
// code has been unloaded, unregistering it. We cannot do this right while
1320-
// holding the Patching_lock because we need to use the CodeCache_lock. This
1314+
// holding the CompiledMethod_lock because we need to use the CodeCache_lock. This
13211315
// would be prone to deadlocks.
13221316
// This flag is used to remember whether we need to later lock and unregister.
13231317
bool nmethod_needs_unregister = false;
13241318

1325-
{
1326-
// invalidate osr nmethod before acquiring the patching lock since
1327-
// they both acquire leaf locks and we don't want a deadlock.
1328-
// This logic is equivalent to the logic below for patching the
1329-
// verified entry point of regular methods. We check that the
1330-
// nmethod is in use to ensure that it is invalidated only once.
1331-
if (is_osr_method() && is_in_use()) {
1332-
// this effectively makes the osr nmethod not entrant
1333-
invalidate_osr_method();
1334-
}
1319+
// invalidate osr nmethod before acquiring the patching lock since
1320+
// they both acquire leaf locks and we don't want a deadlock.
1321+
// This logic is equivalent to the logic below for patching the
1322+
// verified entry point of regular methods. We check that the
1323+
// nmethod is in use to ensure that it is invalidated only once.
1324+
if (is_osr_method() && is_in_use()) {
1325+
// this effectively makes the osr nmethod not entrant
1326+
invalidate_osr_method();
1327+
}
13351328

1329+
{
13361330
// Enter critical section. Does not block for safepoint.
1337-
MutexLocker pl(Patching_lock, Mutex::_no_safepoint_check_flag);
1331+
MutexLocker ml(CompiledMethod_lock->owned_by_self() ? NULL : CompiledMethod_lock, Mutex::_no_safepoint_check_flag);
13381332

13391333
if (Atomic::load(&_state) >= state) {
13401334
// another thread already performed this transition so nothing
@@ -1389,8 +1383,9 @@ bool nmethod::make_not_entrant_or_zombie(int state) {
13891383
log_state_change();
13901384

13911385
// Remove nmethod from method.
1392-
unlink_from_method(false /* already owns Patching_lock */);
1393-
} // leave critical region under Patching_lock
1386+
unlink_from_method();
1387+
1388+
} // leave critical region under CompiledMethod_lock
13941389

13951390
#if INCLUDE_JVMCI
13961391
// Invalidate can't occur while holding the Patching lock

src/hotspot/share/code/nmethod.hpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ class nmethod : public CompiledMethod {
119119
// used by jvmti to track if an unload event has been posted for this nmethod.
120120
bool _unload_reported;
121121

122-
// Protected by Patching_lock
122+
// Protected by CompiledMethod_lock
123123
volatile signed char _state; // {not_installed, in_use, not_entrant, zombie, unloaded}
124124

125125
#ifdef ASSERT
@@ -357,7 +357,9 @@ class nmethod : public CompiledMethod {
357357
void set_rtm_state(RTMState state) { _rtm_state = state; }
358358
#endif
359359

360-
void make_in_use() { _state = in_use; }
360+
bool make_in_use() {
361+
return try_transition(in_use);
362+
}
361363
// Make the nmethod non entrant. The nmethod will continue to be
362364
// alive. It is used when an uncommon trap happens. Returns true
363365
// if this thread changed the state of the nmethod or false if
@@ -390,7 +392,7 @@ class nmethod : public CompiledMethod {
390392

391393
int comp_level() const { return _comp_level; }
392394

393-
void unlink_from_method(bool acquire_lock);
395+
void unlink_from_method();
394396

395397
// Support for oops in scopes and relocs:
396398
// Note: index 0 is reserved for null.

src/hotspot/share/gc/z/zBarrierSetNMethod.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -45,7 +45,7 @@ bool ZBarrierSetNMethod::nmethod_entry_barrier(nmethod* nm) {
4545
// We don't need to take the lock when unlinking nmethods from
4646
// the Method, because it is only concurrently unlinked by
4747
// the entry barrier, which acquires the per nmethod lock.
48-
nm->unlink_from_method(false /* acquire_lock */);
48+
nm->unlink_from_method();
4949

5050
// We can end up calling nmethods that are unloading
5151
// since we clear compiled ICs lazily. Returning false

0 commit comments

Comments
 (0)