< prev index next > src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp
Print this page
* questions.
*
*/
+ #include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp"
#include "gc/shenandoah/shenandoahClosures.inline.hpp"
#include "gc/shenandoah/shenandoahHeap.inline.hpp"
#include "gc/shenandoah/shenandoahNMethod.inline.hpp"
#include "memory/resourceArea.hpp"
#include "runtime/continuation.hpp"
#include "runtime/safepointVerifiers.hpp"
- ShenandoahNMethod::ShenandoahNMethod(nmethod* nm, GrowableArray<oop*>& oops, bool non_immediate_oops) :
- _nm(nm), _oops(nullptr), _oops_count(0), _unregistered(false), _lock(), _ic_lock() {
-
- if (!oops.is_empty()) {
- _oops_count = oops.length();
- _oops = NEW_C_HEAP_ARRAY(oop*, _oops_count, mtGC);
- for (int c = 0; c < _oops_count; c++) {
- _oops[c] = oops.at(c);
- }
- }
- _has_non_immed_oops = non_immediate_oops;
-
- assert_same_oops();
+ ShenandoahNMethod::ShenandoahNMethod(nmethod* nm) :
+ _nm(nm), _oops(nullptr), _oops_count(0), _barriers(nullptr), _barriers_count(0), _unregistered(false), _lock(), _ic_lock() {
+ init_from(nm);
}
ShenandoahNMethod::~ShenandoahNMethod() {
if (_oops != nullptr) {
FREE_C_HEAP_ARRAY(_oops);
}
+ if (_barriers != nullptr) {
+ FREE_C_HEAP_ARRAY(_barriers);
+ }
}
void ShenandoahNMethod::update() {
+ init_from(nm());
+ }
+
+ void ShenandoahNMethod::init_from(nmethod* nm) {
ResourceMark rm;
bool non_immediate_oops = false;
GrowableArray<oop*> oops;
+ GrowableArray<ShenandoahNMethodBarrier> barriers;
- detect_reloc_oops(nm(), oops, non_immediate_oops);
- if (oops.length() != _oops_count) {
+ parse(nm, oops, non_immediate_oops, barriers);
+
+ int new_oops_count = oops.length();
+ if (new_oops_count > 0 && _oops_count != new_oops_count) {
if (_oops != nullptr) {
FREE_C_HEAP_ARRAY(_oops);
- _oops = nullptr;
}
+ _oops = NEW_C_HEAP_ARRAY(oop*, new_oops_count, mtGC);
+ }
+ _oops_count = new_oops_count;
+ for (int c = 0; c < _oops_count; c++) {
+ _oops[c] = oops.at(c);
+ }
+ assert_same_oops();
- _oops_count = oops.length();
- if (_oops_count > 0) {
- _oops = NEW_C_HEAP_ARRAY(oop*, _oops_count, mtGC);
+ int new_barriers_count = barriers.length();
+ if (new_barriers_count > 0 && _barriers_count != new_barriers_count) {
+ if (_barriers != nullptr) {
+ FREE_C_HEAP_ARRAY(_barriers);
}
+ _barriers = NEW_C_HEAP_ARRAY(ShenandoahNMethodBarrier, new_barriers_count, mtGC);
}
-
- for (int index = 0; index < _oops_count; index ++) {
- _oops[index] = oops.at(index);
+ _barriers_count = new_barriers_count;
+ for (int c = 0; c < _barriers_count; c++) {
+ _barriers[c] = barriers.at(c);
}
- _has_non_immed_oops = non_immediate_oops;
- assert_same_oops();
+ _has_non_immed_oops = non_immediate_oops;
}
- void ShenandoahNMethod::detect_reloc_oops(nmethod* nm, GrowableArray<oop*>& oops, bool& has_non_immed_oops) {
+ void ShenandoahNMethod::parse(nmethod* nm, GrowableArray<oop*>& oops, bool& has_non_immed_oops, GrowableArray<ShenandoahNMethodBarrier>& barriers) {
has_non_immed_oops = false;
- // Find all oops relocations
+ address code_begin = nm->code_begin();
RelocIterator iter(nm);
while (iter.next()) {
- if (iter.type() != relocInfo::oop_type) {
- // Not an oop
- continue;
- }
-
- oop_Relocation* r = iter.oop_reloc();
- if (!r->oop_is_immediate()) {
- // Non-immediate oop found
- has_non_immed_oops = true;
- continue;
- }
-
- oop value = r->oop_value();
- if (value != nullptr) {
- oop* addr = r->oop_addr();
- shenandoah_assert_correct(addr, value);
- shenandoah_assert_not_in_cset_except(addr, value, ShenandoahHeap::heap()->cancelled_gc());
- shenandoah_assert_not_forwarded(addr, value);
- // Non-null immediate oop found. null oops can safely be
- // ignored since the method will be re-registered if they
- // are later patched to be non-null.
- oops.push(addr);
+ switch (iter.type()) {
+ case relocInfo::oop_type: {
+ oop_Relocation* r = iter.oop_reloc();
+ if (!r->oop_is_immediate()) {
+ // Non-immediate oop found
+ has_non_immed_oops = true;
+ break;
+ }
+
+ oop value = r->oop_value();
+ if (value != nullptr) {
+ oop* addr = r->oop_addr();
+ shenandoah_assert_correct(addr, value);
+ shenandoah_assert_not_in_cset_except(addr, value, ShenandoahHeap::heap()->cancelled_gc());
+ shenandoah_assert_not_forwarded(addr, value);
+ // Non-null immediate oop found. null oops can safely be
+ // ignored since the method will be re-registered if they
+ // are later patched to be non-null.
+ oops.push(addr);
+ }
+ break;
+ }
+ #ifdef COMPILER2
+ case relocInfo::patchable_barrier_type: {
+ patchable_barrier_Relocation* r = iter.patchable_barrier_reloc();
+
+ ShenandoahNMethodBarrier b;
+ b._rel_pc = pointer_delta(r->addr(), code_begin, 1);
+ b._rel_target = r->target_offset();
+ b._metadata = r->metadata();
+ barriers.push(b);
+ break;
+ }
+ #endif
+ default:
+ // We do not care about other relocations.
+ break;
}
}
}
ShenandoahNMethod* ShenandoahNMethod::for_nmethod(nmethod* nm) {
- ResourceMark rm;
- bool non_immediate_oops = false;
- GrowableArray<oop*> oops;
-
- detect_reloc_oops(nm, oops, non_immediate_oops);
- return new ShenandoahNMethod(nm, oops, non_immediate_oops);
+ return new ShenandoahNMethod(nm);
}
- void ShenandoahNMethod::heal_nmethod(nmethod* nm) {
+ bool ShenandoahNMethod::handle_oops(nmethod* nm) {
ShenandoahNMethod* data = gc_data(nm);
assert(data != nullptr, "Sanity");
assert(data->lock()->owned_by_self(), "Must hold the lock");
ShenandoahHeap* const heap = ShenandoahHeap::heap();
if ((heap->is_concurrent_weak_root_in_progress() && heap->is_evacuation_in_progress()) ||
heap->is_concurrent_strong_root_in_progress()) {
heal_nmethod_metadata(data);
+ // Assume healing changed the code.
+ return true;
} else if (heap->is_concurrent_mark_in_progress()) {
ShenandoahKeepAliveClosure cl;
data->oops_do(&cl);
} else {
// There is possibility that GC is cancelled when it arrives final mark.
// In this case, concurrent root phase is skipped and degenerated GC should be
// followed, where nmethods are disarmed.
}
+
+ // No code modifications happened
+ return false;
+ }
+
+ bool ShenandoahNMethod::handle_barriers(nmethod* nm) {
+ bool changed = false;
+ ShenandoahNMethod* data = gc_data(nm);
+ assert(data != nullptr, "Sanity");
+ assert(data->lock()->owned_by_self(), "Must hold the lock");
+
+ char gc_state = ShenandoahHeap::heap()->gc_state();
+ address code_begin = nm->code_begin();
+ for (int c = 0; c < data->_barriers_count; c++) {
+ ShenandoahNMethodBarrier& b = data->_barriers[c];
+ address pc = code_begin + b._rel_pc;
+ address target = pc + b._rel_target;
+ char rel_gc_state = decode_reloc_gc_state(b._metadata);
+ bool rel_inverted = decode_reloc_inverted(b._metadata);
+ bool active = ((gc_state & rel_gc_state) != 0) ^ rel_inverted;
+ changed |= patch_barrier(pc, target, active);
+ }
+ return changed;
+ }
+
+ bool ShenandoahNMethod::patch_barrier(address pc, address stub_pc, bool active) {
+ #ifdef COMPILER2
+ // Use precise instruction rewrite code, and only when it recognizes the current insns.
+ // This patching code is non-atomic, but it runs in the nmethod entry barrier context,
+ // which guarantee the updates are not interleaved with execution. The icache flushing
+ // is also handled in nmethod entry barriers.
+ bool patched = true;
+ if (active && ShenandoahBarrierSetAssembler::is_patchable_nop(pc)) {
+ ShenandoahBarrierSetAssembler::insert_patchable_jump(pc, stub_pc);
+ } else if (!active && ShenandoahBarrierSetAssembler::is_patchable_jump(pc, stub_pc)) {
+ ShenandoahBarrierSetAssembler::insert_patchable_nop(pc);
+ } else {
+ patched = false;
+ }
+
+ if (active) {
+ // Failing to activate the barrier is catastrophic for correctness,
+ // so prefer to crash hard even in product.
+ guarantee(ShenandoahBarrierSetAssembler::is_patchable_jump(pc, stub_pc),
+ "Active barrier: should be jump to the same address");
+ assert(ShenandoahBarrierSetAssembler::parse_jump_address(pc) == stub_pc,
+ "Active barrier: cross-checking, jump should be to the same address");
+ } else {
+ assert(ShenandoahBarrierSetAssembler::is_patchable_nop(pc),
+ "Inactive barrier: should be patchable nop");
+ }
+ return patched;
+ #else
+ return false;
+ #endif
}
#ifdef ASSERT
void ShenandoahNMethod::assert_correct() {
ShenandoahHeap* heap = ShenandoahHeap::heap();
debug_stream.print_cr("recorded oops: %d", _oops_count);
for (int i = 0; i < _oops_count; i++) {
debug_stream.print_cr("-> " PTR_FORMAT, p2i(_oops[i]));
}
GrowableArray<oop*> check;
+ GrowableArray<ShenandoahNMethodBarrier> barriers;
bool non_immed;
- detect_reloc_oops(nm(), check, non_immed);
+ parse(nm(), check, non_immed, barriers);
debug_stream.print_cr("check oops: %d", check.length());
for (int i = 0; i < check.length(); i++) {
debug_stream.print_cr("-> " PTR_FORMAT, p2i(check.at(i)));
}
fatal("Must match #detected: %d, #recorded: %d, #total: %d, begin: " PTR_FORMAT ", end: " PTR_FORMAT "\n%s",
assert(nm == data->nm(), "Must be same nmethod");
// Prevent updating a nmethod while concurrent iteration is in progress.
wait_until_concurrent_iteration_done();
ShenandoahNMethodLocker data_locker(data->lock());
data->update();
+ ShenandoahNMethod::handle_barriers(nm);
} else {
// For a new nmethod, we can safely append it to the list, because
// concurrent iteration will not touch it.
data = ShenandoahNMethod::for_nmethod(nm);
assert(data != nullptr, "Sanity");
ShenandoahNMethod::attach_gc_data(nm, data);
ShenandoahLocker locker(&_lock);
log_register_nmethod(nm);
append(data);
+ ShenandoahNMethodLocker data_locker(data->lock());
+ ShenandoahNMethod::handle_barriers(nm);
}
+ // Fix ups might have happened, flush the nmethod now.
+ ICache::invalidate_range(nm->code_begin(), nm->code_size());
// Disarm new nmethod
ShenandoahNMethod::disarm_nmethod(nm);
}
void ShenandoahNMethodTable::unregister_nmethod(nmethod* nm) {
< prev index next >