< prev index next > src/hotspot/share/gc/shenandoah/shenandoahNMethod.cpp
Print this page
#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++) {
#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, GrowableArray<ShenandoahNMethodBarrier>& barriers) :
! _nm(nm), _oops(nullptr), _oops_count(0), _barriers(nullptr), _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++) {
}
}
_has_non_immed_oops = non_immediate_oops;
assert_same_oops();
}
ShenandoahNMethod::~ShenandoahNMethod() {
if (_oops != nullptr) {
FREE_C_HEAP_ARRAY(oop*, _oops);
}
}
void ShenandoahNMethod::update() {
ResourceMark rm;
bool non_immediate_oops = false;
GrowableArray<oop*> oops;
! detect_reloc_oops(nm(), oops, non_immediate_oops);
if (oops.length() != _oops_count) {
if (_oops != nullptr) {
FREE_C_HEAP_ARRAY(oop*, _oops);
_oops = nullptr;
}
}
}
_has_non_immed_oops = non_immediate_oops;
assert_same_oops();
+
+ if (!barriers.is_empty()) {
+ _barriers_count = barriers.length();
+ _barriers = NEW_C_HEAP_ARRAY(ShenandoahNMethodBarrier, _barriers_count, mtGC);
+ for (int c = 0; c < _barriers_count; c++) {
+ _barriers[c] = barriers.at(c);
+ }
+ }
}
ShenandoahNMethod::~ShenandoahNMethod() {
if (_oops != nullptr) {
FREE_C_HEAP_ARRAY(oop*, _oops);
}
+ if (_barriers != nullptr) {
+ FREE_C_HEAP_ARRAY(ShenandoahNMethodBarrier, _barriers);
+ }
}
void ShenandoahNMethod::update() {
ResourceMark rm;
bool non_immediate_oops = false;
GrowableArray<oop*> oops;
+ GrowableArray<ShenandoahNMethodBarrier> barriers;
! parse(nm(), oops, non_immediate_oops, barriers);
if (oops.length() != _oops_count) {
if (_oops != nullptr) {
FREE_C_HEAP_ARRAY(oop*, _oops);
_oops = nullptr;
}
_has_non_immed_oops = non_immediate_oops;
assert_same_oops();
}
! void ShenandoahNMethod::detect_reloc_oops(nmethod* nm, GrowableArray<oop*>& oops, bool& has_non_immed_oops) {
has_non_immed_oops = false;
- // Find all oops relocations
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);
}
}
}
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);
}
void ShenandoahNMethod::heal_nmethod(nmethod* nm) {
ShenandoahNMethod* data = gc_data(nm);
assert(data != nullptr, "Sanity");
_has_non_immed_oops = non_immediate_oops;
assert_same_oops();
}
! void ShenandoahNMethod::parse(nmethod* nm, GrowableArray<oop*>& oops, bool& has_non_immed_oops, GrowableArray<ShenandoahNMethodBarrier>& barriers) {
has_non_immed_oops = false;
RelocIterator iter(nm);
while (iter.next()) {
! 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;
+ }
+ case relocInfo::barrier_type: {
+ assert(ShenandoahGCStateCheckHotpatch, "Who emits these?");
+ barrier_Relocation* r = iter.barrier_reloc();
+
+ // TODO: Move to assembler?
+ #ifdef AMD64
+ NativeInstruction* ni = nativeInstruction_at(r->addr());
+ assert(ni->is_jump(), "Initial code version: GC barrier fastpath must be a jump");
+ NativeJump* jmp = nativeJump_at(r->addr());
+
+ ShenandoahNMethodBarrier b;
+ b._barrier_pc = r->addr();
+ b._stub_addr = jmp->jump_destination();
+ // TODO: Can technically figure out which GC state we care about in this reloc.
+ // b._gc_state_fast_bit = r->format();
+ barriers.push(b);
+ #else
+ Unimplemented();
+ #endif
! break;
! }
! 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;
+ GrowableArray<ShenandoahNMethodBarrier> barriers;
! parse(nm, oops, non_immediate_oops, barriers);
! return new ShenandoahNMethod(nm, oops, non_immediate_oops, barriers);
}
void ShenandoahNMethod::heal_nmethod(nmethod* nm) {
ShenandoahNMethod* data = gc_data(nm);
assert(data != nullptr, "Sanity");
} 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.
}
+
+ // Update all barriers
+ data->update_barriers();
+ }
+
+ #ifdef AMD64
+ void insert_5_byte_nop(address pc) {
+ *(pc + 0) = 0x0F;
+ *(pc + 1) = 0x1F;
+ *(pc + 2) = 0x44;
+ *(pc + 3) = 0x00;
+ *(pc + 4) = 0x00;
+ ICache::invalidate_range(pc, 5);
+ }
+
+ bool is_5_byte_nop(address pc) {
+ if (*(pc + 0) != 0x0F) return false;
+ if (*(pc + 1) != 0x1F) return false;
+ if (*(pc + 2) != 0x44) return false;
+ if (*(pc + 3) != 0x00) return false;
+ if (*(pc + 4) != 0x00) return false;
+ return true;
+ }
+ #endif
+
+ void ShenandoahNMethod::update_barriers() {
+ if (!ShenandoahGCStateCheckHotpatch) return;
+
+ ShenandoahHeap* heap = ShenandoahHeap::heap();
+
+ for (int c = 0; c < _barriers_count; c++) {
+ ShenandoahNMethodBarrier& barrier = _barriers[c];
+
+ address pc = barrier._barrier_pc;
+ NativeInstruction* ni = nativeInstruction_at(pc);
+
+ // TODO: This should really be in assembler?
+ #ifdef AMD64
+ if (heap->is_idle()) {
+ // Heap is idle: insert nops
+ if (ni->is_jump()) {
+ insert_5_byte_nop(pc);
+ } else {
+ assert(is_5_byte_nop(pc), "Sanity: should already be nop at PC " PTR_FORMAT ": %02x%02x%02x%02x%02x",
+ p2i(pc), *(pc + 0), *(pc + 1), *(pc + 2), *(pc + 3), *(pc + 4));
+ }
+ } else {
+ // Heap is active: insert jumps to barrier stubs
+ if (is_5_byte_nop(pc)) {
+ NativeJump::insert(pc, barrier._stub_addr);
+ } else {
+ assert(ni->is_jump(), "Sanity: should already be jump at PC " PTR_FORMAT ": %02x%02x%02x%02x%02x",
+ p2i(pc), *(pc + 0), *(pc + 1), *(pc + 2), *(pc + 3), *(pc + 4));
+ }
+ }
+ #else
+ Unimplemented();
+ #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;
bool non_immed;
! detect_reloc_oops(nm(), check, non_immed);
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",
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;
! 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();
+ data->update_barriers();
} 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);
+ data->update_barriers();
assert(data != nullptr, "Sanity");
ShenandoahNMethod::attach_gc_data(nm, data);
ShenandoahLocker locker(&_lock);
log_register_nmethod(nm);
append(data);
< prev index next >