# HG changeset patch # Parent 5b2dc0283d5545603c23bcfcae996ecd5f3acd9c diff -r 5b2dc0283d55 src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp Wed Mar 13 21:00:34 2019 +0100 +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp Thu Mar 14 09:29:53 2019 +0100 @@ -23,6 +23,7 @@ #include "precompiled.hpp" #include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp" +#include "gc/shenandoah/shenandoahConnectionMatrix.hpp" #include "gc/shenandoah/shenandoahHeap.hpp" #include "gc/shenandoah/shenandoahHeapRegion.hpp" #include "gc/shenandoah/shenandoahHeuristics.hpp" @@ -296,6 +297,46 @@ __ bind(done); } +void ShenandoahBarrierSetAssembler::shenandoah_write_barrier_post(MacroAssembler* masm, + Register store_addr, + Register new_val, + Register thread, + Register tmp, + Register tmp2) { + assert(UseShenandoahGC, "why else should we be here?"); + + if (! UseShenandoahMatrix) { + // No need for that barrier if not using matrix. + return; + } + + Label done; + __ testptr(new_val, new_val); + __ jcc(Assembler::zero, done); + ShenandoahConnectionMatrix* matrix = ShenandoahHeap::heap()->connection_matrix(); + address matrix_addr = matrix->matrix_addr(); + __ movptr(rscratch1, (intptr_t) ShenandoahHeap::heap()->base()); + // Compute to-region index + __ movptr(tmp, new_val); + __ subptr(tmp, rscratch1); + __ shrptr(tmp, ShenandoahHeapRegion::region_size_bytes_shift_jint()); + // Compute from-region index + __ movptr(tmp2, store_addr); + __ subptr(tmp2, rscratch1); + __ shrptr(tmp2, ShenandoahHeapRegion::region_size_bytes_shift_jint()); + // Compute matrix index + __ imulptr(tmp, tmp, matrix->stride_jint()); + __ addptr(tmp, tmp2); + // Address is _matrix[to * stride + from] + __ movptr(rscratch1, (intptr_t) matrix_addr); + // Test if the element is already set. + __ cmpb(Address(rscratch1, tmp, Address::times_1), 0); + __ jcc(Assembler::notEqual, done); + // Store true, if not yet set. + __ movb(Address(rscratch1, tmp, Address::times_1), 1); + __ bind(done); +} + void ShenandoahBarrierSetAssembler::resolve_forward_pointer(MacroAssembler* masm, Register dst) { assert(ShenandoahCASBarrier, "should be enabled"); Label is_null; @@ -418,6 +459,7 @@ bool as_normal = (decorators & AS_NORMAL) != 0; if (on_oop && in_heap) { bool needs_pre_barrier = as_normal; + bool needs_post_barrier = val != noreg && in_heap && UseShenandoahMatrix; Register tmp3 = LP64_ONLY(r8) NOT_LP64(rsi); Register rthread = LP64_ONLY(r15_thread) NOT_LP64(rcx); @@ -451,7 +493,22 @@ BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp1, 0), val, noreg, noreg); } else { storeval_barrier(masm, val, tmp3); + Register new_val = val; + if (needs_post_barrier) { + if (UseCompressedOops) { + new_val = tmp2; + __ movptr(new_val, val); + } + } BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp1, 0), val, noreg, noreg); + if (needs_post_barrier) { + shenandoah_write_barrier_post(masm /*masm*/, + tmp1 /* store_adr */, + new_val /* new_val */, + rthread /* thread */, + tmp3 /* tmp */, + tmp2 /* tmp2 */); + } } NOT_LP64(imasm->restore_bcp()); } else { diff -r 5b2dc0283d55 src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp Wed Mar 13 21:00:34 2019 +0100 +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.hpp Thu Mar 14 09:29:53 2019 +0100 @@ -55,6 +55,13 @@ bool tosca_live, bool expand_call); + void shenandoah_write_barrier_post(MacroAssembler* masm, + Register store_addr, + Register new_val, + Register thread, + Register tmp, + Register tmp2); + void resolve_forward_pointer(MacroAssembler* masm, Register dst); void resolve_forward_pointer_not_null(MacroAssembler* masm, Register dst); diff -r 5b2dc0283d55 src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetC1_x86.cpp --- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetC1_x86.cpp Wed Mar 13 21:00:34 2019 +0100 +++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetC1_x86.cpp Thu Mar 14 09:29:53 2019 +0100 @@ -82,6 +82,11 @@ LIR_Opr result = gen->new_register(T_INT); __ append(new LIR_OpShenandoahCompareAndSwap(addr, cmp_value.result(), new_value.result(), t1, t2, result)); + + if (UseShenandoahMatrix) { + post_barrier(access.gen(), access.resolved_addr(), new_value.result()); + } + return result; } } @@ -112,6 +117,9 @@ pre_barrier(access.gen(), access.access_emit_info(), access.decorators(), LIR_OprFact::illegalOpr, result /* pre_val */); } + if (UseShenandoahMatrix) { + post_barrier(access.gen(), access.resolved_addr(), value.result()); + } } return result; diff -r 5b2dc0283d55 src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp --- a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp Wed Mar 13 21:00:34 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.cpp Thu Mar 14 09:29:53 2019 +0100 @@ -26,6 +26,7 @@ #include "gc/shared/satbMarkQueue.hpp" #include "gc/shenandoah/shenandoahBarrierSetAssembler.hpp" #include "gc/shenandoah/shenandoahBrooksPointer.hpp" +#include "gc/shenandoah/shenandoahConnectionMatrix.hpp" #include "gc/shenandoah/shenandoahHeap.hpp" #include "gc/shenandoah/shenandoahHeapRegion.hpp" #include "gc/shenandoah/shenandoahThreadLocalData.hpp" @@ -105,6 +106,73 @@ __ branch_destination(slow->continuation()); } +void ShenandoahBarrierSetC1::post_barrier(LIRGenerator* gen, LIR_OprDesc* addr, LIR_OprDesc* new_val) { + if (! UseShenandoahMatrix) { + // No need for that barrier if not using matrix. + return; + } + + // If the "new_val" is a constant NULL, no barrier is necessary. + if (new_val->is_constant() && + new_val->as_constant_ptr()->as_jobject() == NULL) return; + + new_val = ensure_in_register(gen, new_val); + assert(new_val->is_register(), "must be a register at this point"); + + if (addr->is_address()) { + LIR_Address* address = addr->as_address_ptr(); + LIR_Opr ptr = gen->new_pointer_register(); + if (!address->index()->is_valid() && address->disp() == 0) { + __ move(address->base(), ptr); + } else { + // assert(address->disp() != max_jint, "lea doesn't support patched addresses!"); + __ leal(addr, ptr); + } + addr = ptr; + } + assert(addr->is_register(), "must be a register at this point"); + + LabelObj* L_done = new LabelObj(); + __ cmp(lir_cond_equal, new_val, LIR_OprFact::oopConst(NULL_WORD)); + __ branch(lir_cond_equal, T_OBJECT, L_done->label()); + + ShenandoahConnectionMatrix* matrix = ShenandoahHeap::heap()->connection_matrix(); + + LIR_Opr heap_base = gen->new_pointer_register(); + __ move(LIR_OprFact::intptrConst(ShenandoahHeap::heap()->base()), heap_base); + + LIR_Opr tmp1 = gen->new_pointer_register(); + __ move(new_val, tmp1); + __ sub(tmp1, heap_base, tmp1); + __ unsigned_shift_right(tmp1, LIR_OprFact::intConst(ShenandoahHeapRegion::region_size_bytes_shift_jint()), tmp1, LIR_OprDesc::illegalOpr()); + + LIR_Opr tmp2 = gen->new_pointer_register(); + __ move(addr, tmp2); + __ sub(tmp2, heap_base, tmp2); + __ unsigned_shift_right(tmp2, LIR_OprFact::intConst(ShenandoahHeapRegion::region_size_bytes_shift_jint()), tmp2, LIR_OprDesc::illegalOpr()); + + LIR_Opr tmp3 = gen->new_pointer_register(); + __ move(LIR_OprFact::longConst(matrix->stride_jint()), tmp3); + __ mul(tmp1, tmp3, tmp1); + __ add(tmp1, tmp2, tmp1); + + LIR_Opr tmp4 = gen->new_pointer_register(); + __ move(LIR_OprFact::intptrConst((intptr_t) matrix->matrix_addr()), tmp4); + LIR_Address* matrix_elem_addr = new LIR_Address(tmp4, tmp1, T_BYTE); + + LIR_Opr tmp5 = gen->new_register(T_INT); + __ move(matrix_elem_addr, tmp5); + __ cmp(lir_cond_notEqual, tmp5, LIR_OprFact::intConst(0)); + __ branch(lir_cond_notEqual, T_BYTE, L_done->label()); + + // Aarch64 cannot move constant 1. Load it into a register. + LIR_Opr one = gen->new_register(T_INT); + __ move(LIR_OprFact::intConst(1), one); + __ move(one, matrix_elem_addr); + + __ branch_destination(L_done->label()); +} + LIR_Opr ShenandoahBarrierSetC1::load_reference_barrier(LIRGenerator* gen, LIR_Opr obj, CodeEmitInfo* info, bool need_null_check) { if (ShenandoahLoadRefBarrier) { return load_reference_barrier_impl(gen, obj, info, need_null_check); @@ -180,6 +248,10 @@ value = storeval_barrier(access.gen(), value, access.access_emit_info(), access.decorators()); } BarrierSetC1::store_at_resolved(access, value); + + if (access.is_oop() && UseShenandoahMatrix) { + post_barrier(access.gen(), access.resolved_addr(), value); + } } void ShenandoahBarrierSetC1::load_at_resolved(LIRAccess& access, LIR_Opr result) { diff -r 5b2dc0283d55 src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp --- a/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp Wed Mar 13 21:00:34 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/c1/shenandoahBarrierSetC1.hpp Thu Mar 14 09:29:53 2019 +0100 @@ -180,6 +180,7 @@ CodeBlob* _pre_barrier_c1_runtime_code_blob; void pre_barrier(LIRGenerator* gen, CodeEmitInfo* info, DecoratorSet decorators, LIR_Opr addr_opr, LIR_Opr pre_val); + void post_barrier(LIRGenerator* gen, LIR_OprDesc* addr, LIR_OprDesc* new_val); LIR_Opr load_reference_barrier(LIRGenerator* gen, LIR_Opr obj, CodeEmitInfo* info, bool need_null_check); LIR_Opr storeval_barrier(LIRGenerator* gen, LIR_Opr obj, CodeEmitInfo* info, DecoratorSet decorators); diff -r 5b2dc0283d55 src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp Wed Mar 13 21:00:34 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.cpp Thu Mar 14 09:29:53 2019 +0100 @@ -179,6 +179,93 @@ return false; } +void ShenandoahBarrierSetC2::shenandoah_update_matrix(GraphKit* kit, Node* adr, Node* val) const { + if (!UseShenandoahMatrix) { + return; + } + + assert(val != NULL, "checked before"); + if (adr == NULL) { + return; // Nothing to do + } + assert(adr != NULL, "must not happen"); + if (val->bottom_type()->higher_equal(TypePtr::NULL_PTR)) { + // Nothing to do. + return; + } + + ShenandoahConnectionMatrix* matrix = ShenandoahHeap::heap()->connection_matrix(); + + enum { _set_path = 1, _already_set_path, _val_null_path, PATH_LIMIT }; + RegionNode* region = new RegionNode(PATH_LIMIT); + Node* prev_mem = __ memory(Compile::AliasIdxRaw); + Node* memphi = PhiNode::make(region, prev_mem, Type::MEMORY, TypeRawPtr::BOTTOM); + Node* null_ctrl = __ top(); + Node* not_null_val = __ null_check_oop(val, &null_ctrl); + + // Null path: nothing to do. + region->init_req(_val_null_path, null_ctrl); + memphi->init_req(_val_null_path, prev_mem); + + // Not null path. Update the matrix. + + // This uses a fast calculation for the matrix address. For a description, + // see src/share/vm/gc/shenandoah/shenandoahConnectionMatrix.inline.hpp, + // ShenandoahConnectionMatrix::compute_address(const void* from, const void* to). + address heap_base = ShenandoahHeap::heap()->base(); + jint stride = matrix->stride_jint(); + jint rs = ShenandoahHeapRegion::region_size_bytes_shift_jint(); + + guarantee(stride < ShenandoahHeapRegion::region_size_bytes_jint(), "sanity"); + guarantee(is_aligned(heap_base, ShenandoahHeapRegion::region_size_bytes()), "sanity"); + + Node* magic_con = __ MakeConX((jlong) matrix->matrix_addr() - ((jlong) heap_base >> rs) * (stride + 1)); + + // Compute addr part + Node* adr_idx = __ gvn().transform(new CastP2XNode(__ control(), adr)); + adr_idx = __ gvn().transform(new URShiftXNode(adr_idx, __ intcon(rs))); + + // Compute new_val part + Node* val_idx = __ gvn().transform(new CastP2XNode(__ control(), not_null_val)); + val_idx = __ gvn().transform(new URShiftXNode(val_idx, __ intcon(rs))); + val_idx = __ gvn().transform(new MulXNode(val_idx, __ MakeConX(stride))); + + // Add everything up + adr_idx = __ gvn().transform(new AddXNode(adr_idx, val_idx)); + adr_idx = __ gvn().transform(new CastX2PNode(adr_idx)); + Node* matrix_adr = __ gvn().transform(new AddPNode(__ top(), adr_idx, magic_con)); + + // Load current value + const TypePtr* adr_type = TypeRawPtr::BOTTOM; + Node* current = __ gvn().transform(LoadNode::make(__ gvn(), __ control(), __ memory(Compile::AliasIdxRaw), + matrix_adr, adr_type, TypeInt::INT, T_BYTE, MemNode::unordered)); + + // Check if already set + Node* cmp_set = __ gvn().transform(new CmpINode(current, __ intcon(0))); + Node* cmp_set_bool = __ gvn().transform(new BoolNode(cmp_set, BoolTest::eq)); + IfNode* cmp_iff = __ create_and_map_if(__ control(), cmp_set_bool, PROB_MIN, COUNT_UNKNOWN); + + Node* if_not_set = __ gvn().transform(new IfTrueNode(cmp_iff)); + Node* if_set = __ gvn().transform(new IfFalseNode(cmp_iff)); + + // Already set, exit + __ set_control(if_set); + region->init_req(_already_set_path, __ control()); + memphi->init_req(_already_set_path, prev_mem); + + // Not set: do the store, and finish up + __ set_control(if_not_set); + Node* store = __ gvn().transform(StoreNode::make(__ gvn(), __ control(), __ memory(Compile::AliasIdxRaw), + matrix_adr, adr_type, __ intcon(1), T_BYTE, MemNode::unordered)); + region->init_req(_set_path, __ control()); + memphi->init_req(_set_path, store); + + // Merge control flows and memory. + __ set_control(__ gvn().transform(region)); + __ record_for_igvn(region); + __ set_memory(__ gvn().transform(memphi), Compile::AliasIdxRaw); +} + #undef __ #define __ ideal. @@ -335,15 +422,20 @@ const TypeOopPtr* val_type, Node* pre_val, BasicType bt) const { + IdealKit ideal(kit); + + if (val != NULL) { + shenandoah_update_matrix(kit, adr, val); + ideal.sync_kit(kit); + } + + kit->sync_kit(ideal); if (ShenandoahSATBBarrier) { - IdealKit ideal(kit); - kit->sync_kit(ideal); + satb_write_barrier_pre(kit, do_load, obj, adr, alias_idx, val, val_type, pre_val, bt); + } + ideal.sync_kit(kit); - satb_write_barrier_pre(kit, do_load, obj, adr, alias_idx, val, val_type, pre_val, bt); - - ideal.sync_kit(kit); - kit->final_sync(ideal); - } + kit->final_sync(ideal); } Node* ShenandoahBarrierSetC2::shenandoah_enqueue_barrier(GraphKit* kit, Node* pre_val) const { @@ -784,7 +876,7 @@ } if (tightly_coupled_alloc) { if (phase == Optimization) { - return false; + return UseShenandoahMatrix; } return !is_clone; } @@ -879,10 +971,35 @@ } } +void ShenandoahBarrierSetC2::shenandoah_eliminate_matrix_update(Node* p2x, PhaseIterGVN* igvn) const { + assert(p2x->Opcode() == Op_CastP2X, ""); + ResourceMark rm; + Unique_Node_List wq; + + wq.push(p2x); + for (uint next = 0; next < wq.size(); next++) { + Node *n = wq.at(next); + if (n->is_Store()) { + // do nothing + } else if (n->is_Load()) { + igvn->replace_node(n, igvn->intcon(1)); + } else { + for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { + Node* u = n->fast_out(i); + wq.push(u); + } + } + } + igvn->replace_node(p2x, igvn->C->top()); +} + void ShenandoahBarrierSetC2::eliminate_gc_barrier(PhaseMacroExpand* macro, Node* n) const { if (is_shenandoah_wb_pre_call(n)) { shenandoah_eliminate_wb_pre(n, ¯o->igvn()); } + if (UseShenandoahMatrix && n->Opcode() == Op_CastP2X) { + shenandoah_eliminate_matrix_update(n, ¯o->igvn()); + } } void ShenandoahBarrierSetC2::shenandoah_eliminate_wb_pre(Node* call, PhaseIterGVN* igvn) const { diff -r 5b2dc0283d55 src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp Wed Mar 13 21:00:34 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahBarrierSetC2.hpp Thu Mar 14 09:29:53 2019 +0100 @@ -72,6 +72,8 @@ Node* pre_val, BasicType bt) const; + void shenandoah_update_matrix(GraphKit* kit, Node* adr, Node* val) const; + void shenandoah_eliminate_matrix_update(Node* p2x, PhaseIterGVN* igvn) const; Node* shenandoah_enqueue_barrier(GraphKit* kit, Node* val) const; Node* shenandoah_storeval_barrier(GraphKit* kit, Node* obj) const; diff -r 5b2dc0283d55 src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp --- a/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp Wed Mar 13 21:00:34 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/c2/shenandoahSupport.cpp Thu Mar 14 09:29:53 2019 +0100 @@ -582,7 +582,8 @@ || n->is_CallJava() || n->Opcode() == Op_Unlock || n->Opcode() == Op_EncodeP - || n->Opcode() == Op_DecodeN) { + || n->Opcode() == Op_DecodeN + || (n->Opcode() == Op_CastP2X && UseShenandoahMatrix)) { // nothing to do } else { static struct { diff -r 5b2dc0283d55 src/hotspot/share/gc/shenandoah/heuristics/shenandoahTraversalHeuristics.cpp --- a/src/hotspot/share/gc/shenandoah/heuristics/shenandoahTraversalHeuristics.cpp Wed Mar 13 21:00:34 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/heuristics/shenandoahTraversalHeuristics.cpp Thu Mar 14 09:29:53 2019 +0100 @@ -40,6 +40,7 @@ FLAG_SET_DEFAULT(ShenandoahStoreValEnqueueBarrier, true); FLAG_SET_DEFAULT(ShenandoahKeepAliveBarrier, false); FLAG_SET_DEFAULT(ShenandoahAllowMixedAllocs, false); + FLAG_SET_DEFAULT(UseShenandoahMatrix, true); SHENANDOAH_ERGO_OVERRIDE_DEFAULT(ShenandoahRefProcFrequency, 1); diff -r 5b2dc0283d55 src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp --- a/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp Wed Mar 13 21:00:34 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahAsserts.cpp Thu Mar 14 09:29:53 2019 +0100 @@ -25,6 +25,7 @@ #include "gc/shenandoah/shenandoahAsserts.hpp" #include "gc/shenandoah/shenandoahBrooksPointer.hpp" +#include "gc/shenandoah/shenandoahConnectionMatrix.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahHeapRegionSet.inline.hpp" #include "gc/shenandoah/shenandoahMarkingContext.inline.hpp" @@ -67,6 +68,7 @@ msg.append(" %3s marked \n", ctx->is_marked(obj) ? "" : "not"); msg.append(" %3s in collection set\n", heap->in_collection_set(obj) ? "" : "not"); if (heap->traversal_gc() != NULL) { + msg.append(" %3s in root set\n", heap->traversal_gc()->root_regions()->is_in((HeapWord*) obj) ? "" : "not"); msg.append(" %3s in traversal set\n", heap->traversal_gc()->traversal_set()->is_in((HeapWord*) obj) ? "" : "not"); } msg.append(" region: %s", ss.as_string()); @@ -160,6 +162,43 @@ } } + if (loc_in_heap && UseShenandoahMatrix && (level == _safe_all)) { + bool interior_loc_in_heap = (interior_loc != NULL && heap->is_in(interior_loc)); + msg.append("Matrix connections:\n"); + + oop fwd_to = (oop) ShenandoahBrooksPointer::get_raw_unchecked(obj); + oop fwd_from = (oop) ShenandoahBrooksPointer::get_raw_unchecked(loc); + + size_t from_idx = heap->heap_region_index_containing(loc); + size_t to_idx = heap->heap_region_index_containing(obj); + size_t fwd_from_idx = heap->heap_region_index_containing(fwd_from); + size_t fwd_to_idx = heap->heap_region_index_containing(fwd_to); + + ShenandoahConnectionMatrix* matrix = heap->connection_matrix(); + msg.append(" %35s %3s connected\n", + "reference and object", + matrix->is_connected(from_idx, to_idx) ? "" : "not"); + msg.append(" %35s %3s connected\n", + "fwd(reference) and object", + matrix->is_connected(fwd_from_idx, to_idx) ? "" : "not"); + msg.append(" %35s %3s connected\n", + "reference and fwd(object)", + matrix->is_connected(from_idx, fwd_to_idx) ? "" : "not"); + msg.append(" %35s %3s connected\n", + "fwd(reference) and fwd(object)", + matrix->is_connected(fwd_from_idx, fwd_to_idx) ? "" : "not"); + + if (interior_loc_in_heap) { + size_t from_interior_idx = heap->heap_region_index_containing(interior_loc); + msg.append(" %35s %3s connected\n", + "interior-reference and object", + matrix->is_connected(from_interior_idx, to_idx) ? "" : "not"); + msg.append(" %35s %3s connected\n", + "interior-reference and fwd(object)", + matrix->is_connected(from_interior_idx, fwd_to_idx) ? "" : "not"); + } + } + report_vm_error(file, line, msg.buffer()); } diff -r 5b2dc0283d55 src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp Wed Mar 13 21:00:34 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.cpp Thu Mar 14 09:29:53 2019 +0100 @@ -42,7 +42,7 @@ class ShenandoahBarrierSetC1; class ShenandoahBarrierSetC2; -template +template class ShenandoahUpdateRefsForOopClosure: public BasicOopIterateClosure { private: ShenandoahHeap* _heap; @@ -57,7 +57,10 @@ _bs->enqueue(o); } } else { - _heap->maybe_update_with_forwarded(p); + o = _heap->maybe_update_with_forwarded(p); + } + if (UPDATE_MATRIX && !CompressedOops::is_null(o)) { + _heap->connection_matrix()->set_connected(p, o); } } public: @@ -97,10 +100,10 @@ return true; } -template +template void ShenandoahBarrierSet::write_ref_array_loop(HeapWord* start, size_t count) { assert(UseShenandoahGC && ShenandoahCloneBarrier, "should be enabled"); - ShenandoahUpdateRefsForOopClosure cl; + ShenandoahUpdateRefsForOopClosure cl; T* dst = (T*) start; for (size_t i = 0; i < count; i++) { cl.do_oop(dst++); @@ -116,16 +119,32 @@ if (_heap->is_concurrent_traversal_in_progress()) { ShenandoahEvacOOMScope oom_evac_scope; - if (UseCompressedOops) { - write_ref_array_loop(start, count); + if (UseShenandoahMatrix) { + if (UseCompressedOops) { + write_ref_array_loop(start, count); + } else { + write_ref_array_loop(start, count); + } } else { - write_ref_array_loop(start, count); + if (UseCompressedOops) { + write_ref_array_loop(start, count); + } else { + write_ref_array_loop(start, count); + } } } else { - if (UseCompressedOops) { - write_ref_array_loop(start, count); + if (UseShenandoahMatrix) { + if (UseCompressedOops) { + write_ref_array_loop(start, count); + } else { + write_ref_array_loop(start, count); + } } else { - write_ref_array_loop(start, count); + if (UseCompressedOops) { + write_ref_array_loop(start, count); + } else { + write_ref_array_loop(start, count); + } } } } @@ -165,6 +184,10 @@ enqueue(CompressedOops::decode(heap_oop)); } } + if (UseShenandoahMatrix && ! CompressedOops::is_null(new_val)) { + ShenandoahConnectionMatrix* matrix = _heap->connection_matrix(); + matrix->set_connected(field, new_val); + } } // These are the more general virtual versions. @@ -200,11 +223,21 @@ shenandoah_assert_correct(NULL, obj); if (_heap->is_concurrent_traversal_in_progress()) { ShenandoahEvacOOMScope oom_evac_scope; - ShenandoahUpdateRefsForOopClosure cl; - obj->oop_iterate(&cl); + if (UseShenandoahMatrix) { + ShenandoahUpdateRefsForOopClosure cl; + obj->oop_iterate(&cl); + } else { + ShenandoahUpdateRefsForOopClosure cl; + obj->oop_iterate(&cl); + } } else { - ShenandoahUpdateRefsForOopClosure cl; - obj->oop_iterate(&cl); + if (UseShenandoahMatrix) { + ShenandoahUpdateRefsForOopClosure cl; + obj->oop_iterate(&cl); + } else { + ShenandoahUpdateRefsForOopClosure cl; + obj->oop_iterate(&cl); + } } } diff -r 5b2dc0283d55 src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp Wed Mar 13 21:00:34 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp Thu Mar 14 09:29:53 2019 +0100 @@ -102,7 +102,7 @@ private: inline bool need_update_refs_barrier(); - template + template void write_ref_array_loop(HeapWord* start, size_t count); oop load_reference_barrier_impl(oop obj); @@ -118,20 +118,24 @@ template bool arraycopy_loop_1(T* src, T* dst, size_t length, Klass* bound, - bool checkcast, bool satb, bool disjoint, ShenandoahBarrierSet::ArrayCopyStoreValMode storeval_mode); + bool checkcast, bool satb, bool matrix, bool disjoint, ShenandoahBarrierSet::ArrayCopyStoreValMode storeval_mode); template bool arraycopy_loop_2(T* src, T* dst, size_t length, Klass* bound, - bool satb, bool disjoint, ShenandoahBarrierSet::ArrayCopyStoreValMode storeval_mode); + bool satb, bool matrix, bool disjoint, ShenandoahBarrierSet::ArrayCopyStoreValMode storeval_mode); template bool arraycopy_loop_3(T* src, T* dst, size_t length, Klass* bound, + bool matrix, bool disjoint, ShenandoahBarrierSet::ArrayCopyStoreValMode storeval_mode); + + template + bool arraycopy_loop_4(T* src, T* dst, size_t length, Klass* bound, bool disjoint, ShenandoahBarrierSet::ArrayCopyStoreValMode storeval_mode); - template + template bool arraycopy_loop(T* src, T* dst, size_t length, Klass* bound, bool disjoint); - template + template bool arraycopy_element(T* cur_src, T* cur_dst, Klass* bound, Thread* thread); public: diff -r 5b2dc0283d55 src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp --- a/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp Wed Mar 13 21:00:34 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.inline.hpp Thu Mar 14 09:29:53 2019 +0100 @@ -26,11 +26,13 @@ #include "gc/shared/barrierSet.hpp" #include "gc/shenandoah/shenandoahBarrierSet.hpp" +#include "gc/shenandoah/shenandoahConnectionMatrix.inline.hpp" #include "gc/shenandoah/shenandoahBrooksPointer.inline.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" bool ShenandoahBarrierSet::need_update_refs_barrier() { - return _heap->is_update_refs_in_progress() || + return UseShenandoahMatrix || + _heap->is_update_refs_in_progress() || _heap->is_concurrent_traversal_in_progress() || (_heap->is_concurrent_mark_in_progress() && _heap->has_forwarded_objects()); } @@ -111,6 +113,10 @@ if (ShenandoahSATBBarrier && !CompressedOops::is_null(result)) { ShenandoahBarrierSet::barrier_set()->enqueue(result); } + if (UseShenandoahMatrix && !CompressedOops::is_null(new_value)) { + ShenandoahConnectionMatrix* matrix = ShenandoahHeap::heap()->connection_matrix(); + matrix->set_connected(addr, new_value); + } return result; } @@ -148,6 +154,10 @@ if (ShenandoahSATBBarrier && !CompressedOops::is_null(result)) { ShenandoahBarrierSet::barrier_set()->enqueue(result); } + if (UseShenandoahMatrix && !CompressedOops::is_null(new_value)) { + ShenandoahConnectionMatrix* matrix = ShenandoahHeap::heap()->connection_matrix(); + matrix->set_connected(addr, new_value); + } return result; } @@ -168,43 +178,51 @@ template bool ShenandoahBarrierSet::arraycopy_loop_1(T* src, T* dst, size_t length, Klass* bound, - bool checkcast, bool satb, bool disjoint, - ShenandoahBarrierSet::ArrayCopyStoreValMode storeval_mode) { + bool checkcast, bool satb, bool matrix, bool disjoint, ShenandoahBarrierSet::ArrayCopyStoreValMode storeval_mode) { if (checkcast) { - return arraycopy_loop_2(src, dst, length, bound, satb, disjoint, storeval_mode); + return arraycopy_loop_2(src, dst, length, bound, satb, matrix, disjoint, storeval_mode); } else { - return arraycopy_loop_2(src, dst, length, bound, satb, disjoint, storeval_mode); + return arraycopy_loop_2(src, dst, length, bound, satb, matrix, disjoint, storeval_mode); } } template bool ShenandoahBarrierSet::arraycopy_loop_2(T* src, T* dst, size_t length, Klass* bound, - bool satb, bool disjoint, - ShenandoahBarrierSet::ArrayCopyStoreValMode storeval_mode) { + bool satb, bool matrix, bool disjoint, ShenandoahBarrierSet::ArrayCopyStoreValMode storeval_mode) { if (satb) { - return arraycopy_loop_3(src, dst, length, bound, disjoint, storeval_mode); + return arraycopy_loop_3(src, dst, length, bound, matrix, disjoint, storeval_mode); } else { - return arraycopy_loop_3(src, dst, length, bound, disjoint, storeval_mode); + return arraycopy_loop_3(src, dst, length, bound, matrix, disjoint, storeval_mode); } } template -bool ShenandoahBarrierSet::arraycopy_loop_3(T* src, T* dst, size_t length, Klass* bound, bool disjoint, +bool ShenandoahBarrierSet::arraycopy_loop_3(T* src, T* dst, size_t length, Klass* bound, + bool matrix, bool disjoint, ShenandoahBarrierSet::ArrayCopyStoreValMode storeval_mode) { + if (matrix) { + return arraycopy_loop_4(src, dst, length, bound, disjoint, storeval_mode); + } else { + return arraycopy_loop_4(src, dst, length, bound, disjoint, storeval_mode); + } +} + +template +bool ShenandoahBarrierSet::arraycopy_loop_4(T* src, T* dst, size_t length, Klass* bound, bool disjoint, ShenandoahBarrierSet::ArrayCopyStoreValMode storeval_mode) { switch (storeval_mode) { case NONE: - return arraycopy_loop(src, dst, length, bound, disjoint); + return arraycopy_loop(src, dst, length, bound, disjoint); case READ_BARRIER: - return arraycopy_loop(src, dst, length, bound, disjoint); + return arraycopy_loop(src, dst, length, bound, disjoint); case WRITE_BARRIER: - return arraycopy_loop(src, dst, length, bound, disjoint); + return arraycopy_loop(src, dst, length, bound, disjoint); default: ShouldNotReachHere(); return true; // happy compiler } } -template +template bool ShenandoahBarrierSet::arraycopy_loop(T* src, T* dst, size_t length, Klass* bound, bool disjoint) { Thread* thread = Thread::current(); @@ -234,7 +252,7 @@ T* cur_dst = dst; T* src_end = src + length; for (; cur_src < src_end; cur_src++, cur_dst++) { - if (!arraycopy_element(cur_src, cur_dst, bound, thread)) { + if (!arraycopy_element(cur_src, cur_dst, bound, thread)) { return false; } } @@ -243,7 +261,7 @@ T* cur_src = src + length - 1; T* cur_dst = dst + length - 1; for (; cur_src >= src; cur_src--, cur_dst--) { - if (!arraycopy_element(cur_src, cur_dst, bound, thread)) { + if (!arraycopy_element(cur_src, cur_dst, bound, thread)) { return false; } } @@ -251,7 +269,7 @@ return true; } -template +template bool ShenandoahBarrierSet::arraycopy_element(T* cur_src, T* cur_dst, Klass* bound, Thread* thread) { T o = RawAccess<>::oop_load(cur_src); @@ -293,6 +311,10 @@ ShouldNotReachHere(); } + if (MATRIX) { + _heap->connection_matrix()->set_connected(cur_dst, obj); + } + RawAccess::oop_store(cur_dst, obj); } else { // Store null. @@ -332,7 +354,7 @@ storeval_mode = NONE; } - if (!satb && !checkcast && storeval_mode == NONE) { + if (!satb && !checkcast && !UseShenandoahMatrix && storeval_mode == NONE) { // Short-circuit to bulk copy. return Raw::oop_arraycopy(src_obj, src_offset_in_bytes, src_raw, dst_obj, dst_offset_in_bytes, dst_raw, length); } @@ -342,7 +364,7 @@ Klass* bound = objArrayOop(dst_obj)->element_klass(); ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set(); - return bs->arraycopy_loop_1(src_raw, dst_raw, length, bound, checkcast, satb, disjoint, storeval_mode); + return bs->arraycopy_loop_1(src_raw, dst_raw, length, bound, checkcast, satb, UseShenandoahMatrix, disjoint, storeval_mode); } #endif // SHARE_GC_SHENANDOAH_SHENANDOAHBARRIERSET_INLINE_HPP diff -r 5b2dc0283d55 src/hotspot/share/gc/shenandoah/shenandoahConnectionMatrix.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hotspot/share/gc/shenandoah/shenandoahConnectionMatrix.cpp Thu Mar 14 09:29:53 2019 +0100 @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2017, Red Hat, Inc. and/or its affiliates. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "gc/shenandoah/shenandoahConnectionMatrix.hpp" +#include "gc/shenandoah/shenandoahConnectionMatrix.inline.hpp" +#include "gc/shenandoah/shenandoahHeap.hpp" +#include "gc/shenandoah/shenandoahHeapRegion.hpp" +#include "gc/shenandoah/shenandoahHeapRegionSet.hpp" +#include "memory/allocation.inline.hpp" +#include "utilities/copy.hpp" +#include "utilities/align.hpp" +#include "runtime/atomic.hpp" +#include "runtime/os.hpp" + +ShenandoahConnectionMatrix::ShenandoahConnectionMatrix(size_t max_regions) : + _stride(max_regions), + _region_shift(ShenandoahHeapRegion::region_size_bytes_shift()) +{ + // Use 1-byte data type + STATIC_ASSERT(sizeof(jbyte) == 1); + assert(UseShenandoahMatrix, "Call only when matrix is enabled"); + + size_t page_size = UseLargePages ? (size_t)os::large_page_size() : (size_t)os::vm_page_size(); + size_t granularity = os::vm_allocation_granularity(); + granularity = MAX2(granularity, page_size); + size_t matrix_size = align_up(max_regions * max_regions, granularity); + + ReservedSpace matrix_bitmap(matrix_size, page_size); + os::commit_memory_or_exit(matrix_bitmap.base(), matrix_bitmap.size(), false, + "couldn't allocate matrix bitmap"); + MemTracker::record_virtual_memory_type(matrix_bitmap.base(), mtGC); + + _matrix = (jbyte*)matrix_bitmap.base(); + _magic_offset = ((uintptr_t) _matrix) - ( ((uintptr_t) ShenandoahHeap::heap()->base()) >> _region_shift) * (_stride + 1); + clear_all(); +} + +void ShenandoahConnectionMatrix::clear_all() { + assert (UseShenandoahMatrix, "call only when matrix is enabled"); + size_t count = sizeof(char) * _stride * _stride; + Copy::zero_to_bytes(_matrix, count); +} + +void ShenandoahConnectionMatrix::print_on(outputStream* st) const { + assert (UseShenandoahMatrix, "call only when matrix is enabled"); + st->print_cr("Connection Matrix:"); + st->print_cr("%8s, %10s, %10s, %10s, %8s, %8s, %8s, %8s", + "Region", "Live", "Used", "Garbage", + "TS_Start", "TS_End", "Refcnt", "Referenced by"); + + ShenandoahHeap* heap = ShenandoahHeap::heap(); + for (uint from_idx = 0; from_idx < heap->num_regions(); from_idx++) { + ShenandoahHeapRegion* r = heap->get_region(from_idx); + if (r->is_active()) { + uint count = 0; + for (uint to_idx = 0; to_idx < _stride; to_idx++) { + if (is_connected(to_idx, from_idx)) { + count++; + } + } + + if (count > 0) { + st->print("%8u, " SIZE_FORMAT_W(10) ", " SIZE_FORMAT_W(10) ", " SIZE_FORMAT_W(10) ", " + UINT64_FORMAT_W(8) ", " UINT64_FORMAT_W(8) ", %8u, {", + from_idx, r->get_live_data_bytes(), r->used(), r->garbage(), + r->seqnum_first_alloc(), r->seqnum_last_alloc(), count); + for (uint to_idx = 0; to_idx < _stride; to_idx++) { + if (is_connected(to_idx, from_idx)) { + st->print("%u, ", to_idx); + } + } + st->print_cr("}"); + } + } + } +} + diff -r 5b2dc0283d55 src/hotspot/share/gc/shenandoah/shenandoahConnectionMatrix.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hotspot/share/gc/shenandoah/shenandoahConnectionMatrix.hpp Thu Mar 14 09:29:53 2019 +0100 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2017, Red Hat, Inc. and/or its affiliates. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_GC_SHENANDOAH_SHENANDOAHCONNECTIONMATRIX_HPP +#define SHARE_VM_GC_SHENANDOAH_SHENANDOAHCONNECTIONMATRIX_HPP + +#include "memory/allocation.hpp" + +class ShenandoahConnectionMatrix : public CHeapObj { +private: + const size_t _stride; + const size_t _region_shift; + jbyte* _matrix; + uintptr_t _magic_offset; + + inline size_t index_of(size_t from_idx, size_t to_idx) const; + inline jbyte* compute_address(const void* from_addr, const void* to_addr) const; + +public: + ShenandoahConnectionMatrix(size_t max_regions); + + inline bool is_connected(size_t from_idx, size_t to_idx) const; + inline uint count_connected_to(size_t to_idx, size_t count) const; + + /** + * Enumerate regions connected to this one, and terminates early if more + * than from_idx_max connections are found. + * + * @param to_idx to region to scan + * @param count scan from zero to this regions + * @param from_idxs array of from-idx indices + * @param from_idx_count (out) number of from-idx indices + * @param from_idx_max max number of from-inx indices + * @return true if from_idx_count < from_idx_max + */ + inline bool enumerate_connected_to(size_t to_idx, size_t count, size_t* from_idxs, size_t& from_idx_count, size_t from_idx_max) const; + + inline void clear_connected(size_t from_idx, size_t to_idx); + inline void set_connected(const void* from_addr, const void* to_addr); + inline void clear_region_inbound(size_t idx); + inline void clear_region_outbound(size_t idx); + inline void clear_region(size_t idx); + void clear_all(); + + address matrix_addr() const { return (address) _matrix; } + size_t stride() const { return _stride; } + jint stride_jint() const { + assert (_stride <= max_jint, "sanity"); + return (jint)_stride; + } + + uintptr_t magic_offset() const { + return _magic_offset; + } + + void print_on(outputStream* st) const; +}; + +#endif // SHARE_VM_GC_SHENANDOAH_SHENANDOAHCONNECTIONMATRIX_HPP diff -r 5b2dc0283d55 src/hotspot/share/gc/shenandoah/shenandoahConnectionMatrix.inline.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hotspot/share/gc/shenandoah/shenandoahConnectionMatrix.inline.hpp Thu Mar 14 09:29:53 2019 +0100 @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2017, Red Hat, Inc. and/or its affiliates. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_GC_SHENANDOAH_SHENANDOAHCONNECTIONMATRIX_INLINE_HPP +#define SHARE_VM_GC_SHENANDOAH_SHENANDOAHCONNECTIONMATRIX_INLINE_HPP + +#include "gc/shenandoah/shenandoahConnectionMatrix.hpp" +#include "gc/shenandoah/shenandoahHeap.hpp" +#include "gc/shenandoah/shenandoahHeap.inline.hpp" + + /* + Compute matrix index. + + Practically, we are most frequently scanning for all incoming connections to a particular + region. I.e. we iterate from_idx for some to_idx. Makes sense to keep matrix grouped by to_idx. + matrix subindex is the address minus heap base shifted by region size. + + This means we want to update the matrix element at: + + MATRIX_BASE + (from_addr - HEAP_BASE) >> RS) + ((to_addr - HEAP_BASE) >> RS) * STRIDE + + ...where: + MATRIX_BASE is native matrix address + STRIDE is matrix stride + HEAP_BASE is lowest heap address + RS is region size shift + + This is what interpreter and C1 are doing. But in C2, we can make it more aggressive + by restructuring the expression like this: + + (from_addr >> RS) + (to_addr >> RS) * STRIDE + [MATRIX_BASE - (HEAP_BASE >> RS) * (STRIDE + 1)] + + Notice that first two parts can be computed out-of-order, and only then merged with addition, + which helps scheduling. If STRIDE is a power of two, then from_addr computation can be folded with + region size shift. The third constant can be folded at compile time. + + As long as STRIDE is less than 2^RS, we never overflow. As long as HEAP_BASE is aligned to + region size, we are safe with doing RS shifts. Guarantee both: + */ +jbyte* ShenandoahConnectionMatrix::compute_address(const void* from_addr, const void* to_addr) const { + intptr_t from_idx = (((uintptr_t) from_addr) >> _region_shift); + intptr_t to_idx = (((uintptr_t) to_addr) >> _region_shift) * _stride; + jbyte* addr = (jbyte*) (from_idx + to_idx + _magic_offset); + +#ifdef ASSERT + // Check that computed address matches the address that we would get with the slow path. + ShenandoahHeap* heap = ShenandoahHeap::heap(); + assert(heap->is_in(from_addr), "from is in heap: " PTR_FORMAT, p2i(from_addr)); + assert(heap->is_in(to_addr), "to is in heap: " PTR_FORMAT, p2i(to_addr)); + size_t from_region_idx = heap->heap_region_index_containing(from_addr); + size_t to_region_idx = heap->heap_region_index_containing(to_addr); + size_t matrix_idx = index_of(from_region_idx, to_region_idx); + assert(&_matrix[matrix_idx] == addr, "fast and slow matrix address must match slow: "PTR_FORMAT", fast: "PTR_FORMAT, p2i(&_matrix[matrix_idx]), p2i(addr)); +#endif + + return addr; +} + +size_t ShenandoahConnectionMatrix::index_of(size_t from_idx, size_t to_idx) const { + assert (UseShenandoahMatrix, "call only when matrix is enabled"); + assert (from_idx < _stride, "from is sane: " SIZE_FORMAT, from_idx); + assert (to_idx < _stride, "to is sane: " SIZE_FORMAT, to_idx); + return from_idx + to_idx * _stride; +} + +bool ShenandoahConnectionMatrix::is_connected(size_t from_idx, size_t to_idx) const { + assert (UseShenandoahMatrix, "call only when matrix is enabled"); + return _matrix[index_of(from_idx, to_idx)] > 0; +} + +uint ShenandoahConnectionMatrix::count_connected_to(size_t to_idx, size_t count) const { + assert (UseShenandoahMatrix, "call only when matrix is enabled"); + uint num_incoming = 0; + jbyte* matrix = _matrix; + size_t start = to_idx * _stride; + for (uint from_idx = 0; from_idx < count; from_idx++) { + num_incoming += matrix[start + from_idx]; + } + +#ifdef ASSERT + { + uint check_incoming = 0; + for (uint from_idx = 0; from_idx < count; from_idx++) { + if (is_connected(from_idx, to_idx)) { + check_incoming++; + } + } + assert(num_incoming == check_incoming, + "fast path and slow path agree: %d vs %d", num_incoming, check_incoming); + } +#endif + + return num_incoming; +} + +inline bool ShenandoahConnectionMatrix::enumerate_connected_to(size_t to_idx, size_t count, + size_t* from_idxs, size_t& from_idx_count, size_t from_idx_max) const { + assert (UseShenandoahMatrix, "call only when matrix is enabled"); + uint num = 0; + jbyte *matrix = _matrix; + size_t start = to_idx * _stride; + for (size_t from_idx = 0; from_idx < count; from_idx++) { + if (matrix[start + from_idx] > 0) { + if (num < from_idx_max) { + from_idxs[num] = from_idx; + num++; + } else { + return false; + } + } + } + +#ifdef ASSERT + { + uint cnt = count_connected_to(to_idx, count); + assert (num == cnt, "counted the correct number of regions: %d vs %d", num, cnt); + for (uint i = 0; i < num; i++) { + assert (is_connected(from_idxs[i], to_idx), "should be connected"); + } + } +#endif + + from_idx_count = num; + return true; +} + + +void ShenandoahConnectionMatrix::set_connected(const void* from, const void* to) { + assert (UseShenandoahMatrix, "call only when matrix is enabled"); + jbyte* addr = compute_address(from, to); + if (*addr == 0) { + *addr = 1; + } +} + +inline void ShenandoahConnectionMatrix::clear_connected(size_t from_idx, size_t to_idx) { + assert (UseShenandoahMatrix, "call only when matrix is enabled"); + _matrix[index_of(from_idx, to_idx)] = 0; +} + +inline void ShenandoahConnectionMatrix::clear_region(size_t idx) { + assert (UseShenandoahMatrix, "call only when matrix is enabled"); + clear_region_inbound(idx); + clear_region_outbound(idx); + +#ifdef ASSERT + for (size_t c = 0; c < _stride; c++) { + assert (!is_connected(c, idx), "should not be connected"); + assert (!is_connected(idx, c), "should not be connected"); + } +#endif +} + +inline void ShenandoahConnectionMatrix::clear_region_outbound(size_t idx) { + assert (UseShenandoahMatrix, "call only when matrix is enabled"); + jbyte* matrix = _matrix; + size_t stride = _stride; + size_t count = stride * stride; + for (size_t i = idx; i < count; i += stride) { + if (matrix[i] != 0) matrix[i] = 0; + } + +#ifdef ASSERT + for (size_t c = 0; c < _stride; c++) { + assert (!is_connected(idx, c), "should not be connected"); + } +#endif +} + +inline void ShenandoahConnectionMatrix::clear_region_inbound(size_t idx) { + assert (UseShenandoahMatrix, "call only when matrix is enabled"); + jbyte* matrix = _matrix; + size_t stride = _stride; + size_t start = idx * stride; + size_t end = start + stride; + for (size_t i = start; i < end; i++) { + if (matrix[i] != 0) matrix[i] = 0; + } + +#ifdef ASSERT + for (size_t c = 0; c < _stride; c++) { + assert (!is_connected(c, idx), "should not be connected"); + } +#endif +} + +#endif // SHARE_VM_GC_SHENANDOAH_SHENANDOAHCONNECTIONMATRIX_INLINE_HPP diff -r 5b2dc0283d55 src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp Wed Mar 13 21:00:34 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp Thu Mar 14 09:29:53 2019 +0100 @@ -276,6 +276,12 @@ _aux_bitmap_region = MemRegion((HeapWord*) aux_bitmap.base(), aux_bitmap.size() / HeapWordSize); _aux_bit_map.initialize(_heap_region, _aux_bitmap_region); + if (UseShenandoahMatrix) { + _connection_matrix = new ShenandoahConnectionMatrix(_num_regions); + } else { + _connection_matrix = NULL; + } + _traversal_gc = heuristics()->can_do_traversal_gc() ? new ShenandoahTraversalGC(this, _num_regions) : NULL; @@ -369,6 +375,7 @@ _full_gc(new ShenandoahMarkCompact()), _pacer(NULL), _verifier(NULL), + _connection_matrix(NULL), _alloc_tracker(NULL), _phase_timings(NULL), _monitoring_support(NULL), @@ -469,6 +476,19 @@ st->cr(); MetaspaceUtils::print_on(st); + if (UseShenandoahMatrix) { + st->print_cr("Matrix:"); + + ShenandoahConnectionMatrix* matrix = connection_matrix(); + if (matrix != NULL) { + st->print_cr(" - base: " PTR_FORMAT, p2i(matrix->matrix_addr())); + st->print_cr(" - stride: " SIZE_FORMAT, matrix->stride()); + st->print_cr(" - magic: " PTR_FORMAT, matrix->magic_offset()); + } else { + st->print_cr(" No matrix."); + } + } + if (Verbose) { print_heap_regions_on(st); } diff -r 5b2dc0283d55 src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp --- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp Wed Mar 13 21:00:34 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp Thu Mar 14 09:29:53 2019 +0100 @@ -38,6 +38,7 @@ class ReferenceProcessor; class ShenandoahAllocTracker; class ShenandoahCollectorPolicy; +class ShenandoahConnectionMatrix; class ShenandoahControlThread; class ShenandoahGCSession; class ShenandoahHeuristics; @@ -473,6 +474,7 @@ ShenandoahMarkCompact* _full_gc; ShenandoahPacer* _pacer; ShenandoahVerifier* _verifier; + ShenandoahConnectionMatrix* _connection_matrix; ShenandoahAllocTracker* _alloc_tracker; ShenandoahPhaseTimings* _phase_timings; @@ -488,6 +490,8 @@ ShenandoahTraversalGC* traversal_gc() { return _traversal_gc; } ShenandoahPacer* pacer() const { return _pacer; } + ShenandoahConnectionMatrix* connection_matrix() const { return _connection_matrix; } + ShenandoahPhaseTimings* phase_timings() const { return _phase_timings; } ShenandoahAllocTracker* alloc_tracker() const { return _alloc_tracker; } diff -r 5b2dc0283d55 src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp --- a/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp Wed Mar 13 21:00:34 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahHeapRegion.cpp Thu Mar 14 09:29:53 2019 +0100 @@ -435,6 +435,11 @@ st->print("|G " SIZE_FORMAT_W(5) "%1s", byte_size_in_proper_unit(get_gclab_allocs()), proper_unit_for_byte_size(get_gclab_allocs())); st->print("|S " SIZE_FORMAT_W(5) "%1s", byte_size_in_proper_unit(get_shared_allocs()), proper_unit_for_byte_size(get_shared_allocs())); st->print("|L " SIZE_FORMAT_W(5) "%1s", byte_size_in_proper_unit(get_live_data_bytes()), proper_unit_for_byte_size(get_live_data_bytes())); + if (_heap->traversal_gc() != NULL && _heap->traversal_gc()->root_regions()->is_in(region_number())) { + st->print("|R"); + } else { + st->print("| "); + } st->print("|CP " SIZE_FORMAT_W(3), _critical_pins); st->print("|SN " UINT64_FORMAT_X_W(12) ", " UINT64_FORMAT_X_W(8) ", " UINT64_FORMAT_X_W(8) ", " UINT64_FORMAT_X_W(8), seqnum_first_alloc_mutator(), seqnum_last_alloc_mutator(), @@ -496,6 +501,10 @@ _heap->marking_context()->reset_top_at_mark_start(this); + if (UseShenandoahMatrix) { + _heap->connection_matrix()->clear_region(region_number()); + } + make_empty(); } diff -r 5b2dc0283d55 src/hotspot/share/gc/shenandoah/shenandoahOopClosures.hpp --- a/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.hpp Wed Mar 13 21:00:34 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.hpp Thu Mar 14 09:29:53 2019 +0100 @@ -212,7 +212,7 @@ _mark_context(ShenandoahHeap::heap()->marking_context()) { } - template + template void work(T* p); }; @@ -220,7 +220,7 @@ class ShenandoahTraversalClosure : public ShenandoahTraversalSuperClosure { private: template - inline void do_oop_work(T* p) { work(p); } + inline void do_oop_work(T* p) { work(p); } public: ShenandoahTraversalClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp) : @@ -235,7 +235,7 @@ class ShenandoahTraversalMetadataClosure : public ShenandoahTraversalSuperClosure { private: template - inline void do_oop_work(T* p) { work(p); } + inline void do_oop_work(T* p) { work(p); } public: ShenandoahTraversalMetadataClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp) : @@ -250,7 +250,7 @@ class ShenandoahTraversalDedupClosure : public ShenandoahTraversalSuperClosure { private: template - inline void do_oop_work(T* p) { work(p); } + inline void do_oop_work(T* p) { work(p); } public: ShenandoahTraversalDedupClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp) : @@ -265,7 +265,7 @@ class ShenandoahTraversalMetadataDedupClosure : public ShenandoahTraversalSuperClosure { private: template - inline void do_oop_work(T* p) { work(p); } + inline void do_oop_work(T* p) { work(p); } public: ShenandoahTraversalMetadataDedupClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp) : @@ -280,7 +280,7 @@ class ShenandoahTraversalDegenClosure : public ShenandoahTraversalSuperClosure { private: template - inline void do_oop_work(T* p) { work(p); } + inline void do_oop_work(T* p) { work(p); } public: ShenandoahTraversalDegenClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp) : @@ -295,7 +295,7 @@ class ShenandoahTraversalMetadataDegenClosure : public ShenandoahTraversalSuperClosure { private: template - inline void do_oop_work(T* p) { work(p); } + inline void do_oop_work(T* p) { work(p); } public: ShenandoahTraversalMetadataDegenClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp) : @@ -310,7 +310,7 @@ class ShenandoahTraversalDedupDegenClosure : public ShenandoahTraversalSuperClosure { private: template - inline void do_oop_work(T* p) { work(p); } + inline void do_oop_work(T* p) { work(p); } public: ShenandoahTraversalDedupDegenClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp) : @@ -325,7 +325,7 @@ class ShenandoahTraversalMetadataDedupDegenClosure : public ShenandoahTraversalSuperClosure { private: template - inline void do_oop_work(T* p) { work(p); } + inline void do_oop_work(T* p) { work(p); } public: ShenandoahTraversalMetadataDedupDegenClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp) : @@ -337,4 +337,124 @@ virtual bool do_metadata() { return true; } }; +class ShenandoahTraversalMatrixClosure : public ShenandoahTraversalSuperClosure { +private: + template + inline void do_oop_work(T* p) { work(p); } + +public: + ShenandoahTraversalMatrixClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp) : + ShenandoahTraversalSuperClosure(q, rp) {} + + virtual void do_oop(narrowOop* p) { do_oop_work(p); } + virtual void do_oop(oop* p) { do_oop_work(p); } + + virtual bool do_metadata() { return false; } +}; + +class ShenandoahTraversalMetadataMatrixClosure : public ShenandoahTraversalSuperClosure { +private: + template + inline void do_oop_work(T* p) { work(p); } + +public: + ShenandoahTraversalMetadataMatrixClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp) : + ShenandoahTraversalSuperClosure(q, rp) {} + + virtual void do_oop(narrowOop* p) { do_oop_work(p); } + virtual void do_oop(oop* p) { do_oop_work(p); } + + virtual bool do_metadata() { return true; } +}; + +class ShenandoahTraversalDedupMatrixClosure : public ShenandoahTraversalSuperClosure { +private: + template + inline void do_oop_work(T* p) { work(p); } + +public: + ShenandoahTraversalDedupMatrixClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp) : + ShenandoahTraversalSuperClosure(q, rp) {} + + virtual void do_oop(narrowOop* p) { do_oop_work(p); } + virtual void do_oop(oop* p) { do_oop_work(p); } + + virtual bool do_metadata() { return false; } +}; + +class ShenandoahTraversalMetadataDedupMatrixClosure : public ShenandoahTraversalSuperClosure { +private: + template + inline void do_oop_work(T* p) { work(p); } + +public: + ShenandoahTraversalMetadataDedupMatrixClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp) : + ShenandoahTraversalSuperClosure(q, rp) {} + + virtual void do_oop(narrowOop* p) { do_oop_work(p); } + virtual void do_oop(oop* p) { do_oop_work(p); } + + virtual bool do_metadata() { return true; } +}; + +class ShenandoahTraversalDegenMatrixClosure : public ShenandoahTraversalSuperClosure { +private: + template + inline void do_oop_work(T* p) { work(p); } + +public: + ShenandoahTraversalDegenMatrixClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp) : + ShenandoahTraversalSuperClosure(q, rp) {} + + virtual void do_oop(narrowOop* p) { do_oop_work(p); } + virtual void do_oop(oop* p) { do_oop_work(p); } + + virtual bool do_metadata() { return false; } +}; + +class ShenandoahTraversalMetadataDegenMatrixClosure : public ShenandoahTraversalSuperClosure { +private: + template + inline void do_oop_work(T* p) { work(p); } + +public: + ShenandoahTraversalMetadataDegenMatrixClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp) : + ShenandoahTraversalSuperClosure(q, rp) {} + + virtual void do_oop(narrowOop* p) { do_oop_work(p); } + virtual void do_oop(oop* p) { do_oop_work(p); } + + virtual bool do_metadata() { return true; } +}; + +class ShenandoahTraversalDedupDegenMatrixClosure : public ShenandoahTraversalSuperClosure { +private: + template + inline void do_oop_work(T* p) { work(p); } + +public: + ShenandoahTraversalDedupDegenMatrixClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp) : + ShenandoahTraversalSuperClosure(q, rp) {} + + virtual void do_oop(narrowOop* p) { do_oop_work(p); } + virtual void do_oop(oop* p) { do_oop_work(p); } + + virtual bool do_metadata() { return false; } +}; + +class ShenandoahTraversalMetadataDedupDegenMatrixClosure : public ShenandoahTraversalSuperClosure { +private: + template + inline void do_oop_work(T* p) { work(p); } + +public: + ShenandoahTraversalMetadataDedupDegenMatrixClosure(ShenandoahObjToScanQueue* q, ReferenceProcessor* rp) : + ShenandoahTraversalSuperClosure(q, rp) {} + + virtual void do_oop(narrowOop* p) { do_oop_work(p); } + virtual void do_oop(oop* p) { do_oop_work(p); } + + virtual bool do_metadata() { return true; } +}; + #endif // SHARE_GC_SHENANDOAH_SHENANDOAHOOPCLOSURES_HPP diff -r 5b2dc0283d55 src/hotspot/share/gc/shenandoah/shenandoahOopClosures.inline.hpp --- a/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.inline.hpp Wed Mar 13 21:00:34 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahOopClosures.inline.hpp Thu Mar 14 09:29:53 2019 +0100 @@ -38,9 +38,9 @@ _heap->maybe_update_with_forwarded(p); } -template +template inline void ShenandoahTraversalSuperClosure::work(T* p) { - _traversal_gc->process_oop(p, _thread, _queue, _mark_context); + _traversal_gc->process_oop(p, _thread, _queue, _mark_context); } #endif // SHARE_GC_SHENANDOAH_SHENANDOAHOOPCLOSURES_INLINE_HPP diff -r 5b2dc0283d55 src/hotspot/share/gc/shenandoah/shenandoahTraversalGC.cpp --- a/src/hotspot/share/gc/shenandoah/shenandoahTraversalGC.cpp Wed Mar 13 21:00:34 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahTraversalGC.cpp Thu Mar 14 09:29:53 2019 +0100 @@ -33,6 +33,7 @@ #include "gc/shenandoah/shenandoahCodeRoots.hpp" #include "gc/shenandoah/shenandoahCollectionSet.hpp" #include "gc/shenandoah/shenandoahCollectorPolicy.hpp" +#include "gc/shenandoah/shenandoahConnectionMatrix.hpp" #include "gc/shenandoah/shenandoahFreeSet.hpp" #include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" @@ -184,7 +185,7 @@ // Step 1: Process ordinary GC roots. { - ShenandoahTraversalClosure roots_cl(q, rp); + ShenandoahTraversalClosure roots_cl(q, rp); ShenandoahMarkCLDClosure cld_cl(&roots_cl); MarkingCodeBlobClosure code_cl(&roots_cl, CodeBlobToOopClosure::FixRelocations); if (unload_classes) { @@ -265,7 +266,7 @@ // in similar way during nmethod-register process. Therefore, we don't need to rescan code // roots here. if (!_heap->is_degenerated_gc_in_progress()) { - ShenandoahTraversalClosure roots_cl(q, rp); + ShenandoahTraversalClosure roots_cl(q, rp); CLDToOopClosure cld_cl(&roots_cl, ClassLoaderData::_claim_strong); ShenandoahTraversalSATBThreadsClosure tc(&satb_cl); if (unload_classes) { @@ -275,7 +276,7 @@ _rp->process_all_roots(&roots_cl, process_refs ? NULL : &roots_cl, &cld_cl, NULL, &tc, worker_id); } } else { - ShenandoahTraversalDegenClosure roots_cl(q, rp); + ShenandoahTraversalClosure roots_cl(q, rp); CLDToOopClosure cld_cl(&roots_cl, ClassLoaderData::_claim_strong); ShenandoahTraversalSATBThreadsClosure tc(&satb_cl); if (unload_classes) { @@ -300,9 +301,13 @@ ShenandoahTraversalGC::ShenandoahTraversalGC(ShenandoahHeap* heap, size_t num_regions) : _heap(heap), _task_queues(new ShenandoahObjToScanQueueSet(heap->max_workers())), - _traversal_set(ShenandoahHeapRegionSet()) { + _traversal_set(ShenandoahHeapRegionSet()), + _root_regions(ShenandoahHeapRegionSet()), + _root_regions_iterator(&_root_regions), + _matrix(heap->connection_matrix()) { - uint num_queues = heap->max_workers(); + +uint num_queues = heap->max_workers(); for (uint i = 0; i < num_queues; ++i) { ShenandoahObjToScanQueue* task_queue = new ShenandoahObjToScanQueue(); task_queue->initialize(); @@ -315,6 +320,7 @@ void ShenandoahTraversalGC::prepare_regions() { size_t num_regions = _heap->num_regions(); + ShenandoahConnectionMatrix* matrix = _heap->connection_matrix(); ShenandoahMarkingContext* const ctx = _heap->marking_context(); for (size_t i = 0; i < num_regions; i++) { ShenandoahHeapRegion* region = _heap->get_region(i); @@ -327,6 +333,15 @@ // Everything outside the traversal set is always considered live. ctx->reset_top_at_mark_start(region); } + if (_root_regions.is_in(i)) { + assert(!_heap->in_collection_set(region->bottom()), "roots must not overlap with cset"); + matrix->clear_region_outbound(i); + // Since root region can be allocated at, we should bound the scans + // in it at current top. Otherwise, one thread may evacuate objects + // to that root region, while another would try to scan newly evac'ed + // objects under the race. + region->set_concurrent_iteration_safe_limit(region->top()); + } } else { // FreeSet may contain uncommitted empty regions, once they are recommitted, // their TAMS may have old values, so reset them here. @@ -364,6 +379,9 @@ log_info(gc, ergo)("Collectable Garbage: " SIZE_FORMAT "M, " SIZE_FORMAT "M CSet, " SIZE_FORMAT " CSet regions", collection_set->garbage() / M, collection_set->live_data() / M, collection_set->count()); + if (_root_regions.count() > 0) { + log_info(gc, ergo)("Root set regions: " SIZE_FORMAT, _root_regions.count()); + } } void ShenandoahTraversalGC::init_traversal_collection() { @@ -420,6 +438,8 @@ if (ShenandoahPacing) { _heap->pacer()->setup_for_traversal(); } + + _root_regions_iterator.reset(&_root_regions); } void ShenandoahTraversalGC::main_loop(uint w, ShenandoahTaskTerminator* t, bool sts_yield) { @@ -432,7 +452,45 @@ if (_heap->process_references()) { rp = _heap->ref_processor(); } - { + if (UseShenandoahMatrix) { + if (!_heap->is_degenerated_gc_in_progress()) { + if (_heap->unload_classes()) { + if (ShenandoahStringDedup::is_enabled()) { + ShenandoahTraversalClosure cl(q, rp); + main_loop_work<>(&cl, ld, w, t, sts_yield); + } else { + ShenandoahTraversalClosure cl(q, rp); + main_loop_work<>(&cl, ld, w, t, sts_yield); + } + } else { + if (ShenandoahStringDedup::is_enabled()) { + ShenandoahTraversalClosure cl(q, rp); + main_loop_work<>(&cl, ld, w, t, sts_yield); + } else { + ShenandoahTraversalClosure cl(q, rp); + main_loop_work<>(&cl, ld, w, t, sts_yield); + } + } + } else { + if (_heap->unload_classes()) { + if (ShenandoahStringDedup::is_enabled()) { + ShenandoahTraversalClosure cl(q, rp); + main_loop_work<>(&cl, ld, w, t, sts_yield); + } else { + ShenandoahTraversalClosure cl(q, rp); + main_loop_work<>(&cl, ld, w, t, sts_yield); + } + } else { + if (ShenandoahStringDedup::is_enabled()) { + ShenandoahTraversalDedupDegenMatrixClosure cl(q, rp); + main_loop_work(&cl, ld, w, t, sts_yield); + } else { + ShenandoahTraversalDegenMatrixClosure cl(q, rp); + main_loop_work(&cl, ld, w, t, sts_yield); + } + } + } + } else { if (!_heap->is_degenerated_gc_in_progress()) { if (_heap->unload_classes()) { if (ShenandoahStringDedup::is_enabled()) { @@ -505,6 +563,20 @@ if (check_and_handle_cancelled_gc(terminator, sts_yield)) return; + // Process all root regions. + // TODO: Interleave this in the normal mark loop below. + ShenandoahHeapRegion* r = _root_regions_iterator.claim_next(); + while (r != NULL) { + _heap->marked_object_oop_iterate(r, cl, r->top()); + // if (ShenandoahPacing) { + // _heap->pacer()->report_partial(r->get_live_data_words()); + // } + if (check_and_handle_cancelled_gc(terminator, sts_yield)) return; + r = _root_regions_iterator.claim_next(); + } + + if (check_and_handle_cancelled_gc(terminator, sts_yield)) return; + // Normal loop. q = queues->queue(worker_id); @@ -751,7 +823,7 @@ template inline void do_oop_work(T* p) { - _traversal_gc->process_oop(p, _thread, _queue, _mark_context); + _traversal_gc->process_oop(p, _thread, _queue, _mark_context); } public: @@ -796,7 +868,7 @@ template inline void do_oop_work(T* p) { - _traversal_gc->process_oop(p, _thread, _queue, _mark_context); + _traversal_gc->process_oop(p, _thread, _queue, _mark_context); } public: @@ -819,7 +891,7 @@ template inline void do_oop_work(T* p) { ShenandoahEvacOOMScope evac_scope; - _traversal_gc->process_oop(p, _thread, _queue, _mark_context); + _traversal_gc->process_oop(p, _thread, _queue, _mark_context); } public: @@ -842,7 +914,7 @@ template inline void do_oop_work(T* p) { ShenandoahEvacOOMScope evac_scope; - _traversal_gc->process_oop(p, _thread, _queue, _mark_context); + _traversal_gc->process_oop(p, _thread, _queue, _mark_context); } public: @@ -855,6 +927,50 @@ void do_oop(oop* p) { do_oop_work(p); } }; +class ShenandoahTraversalKeepAliveUpdateMatrixClosure : public OopClosure { +private: + ShenandoahObjToScanQueue* _queue; + Thread* _thread; + ShenandoahTraversalGC* _traversal_gc; + ShenandoahMarkingContext* const _mark_context; + + template + inline void do_oop_work(T* p) { + _traversal_gc->process_oop(p, _thread, _queue, _mark_context); + } + +public: + ShenandoahTraversalKeepAliveUpdateMatrixClosure(ShenandoahObjToScanQueue* q) : + _queue(q), _thread(Thread::current()), + _traversal_gc(ShenandoahHeap::heap()->traversal_gc()), + _mark_context(ShenandoahHeap::heap()->marking_context()) {} + + void do_oop(narrowOop* p) { do_oop_work(p); } + void do_oop(oop* p) { do_oop_work(p); } +}; + +class ShenandoahTraversalKeepAliveUpdateDegenMatrixClosure : public OopClosure { +private: + ShenandoahObjToScanQueue* _queue; + Thread* _thread; + ShenandoahTraversalGC* _traversal_gc; + ShenandoahMarkingContext* const _mark_context; + + template + inline void do_oop_work(T* p) { + _traversal_gc->process_oop(p, _thread, _queue, _mark_context); + } + +public: + ShenandoahTraversalKeepAliveUpdateDegenMatrixClosure(ShenandoahObjToScanQueue* q) : + _queue(q), _thread(Thread::current()), + _traversal_gc(ShenandoahHeap::heap()->traversal_gc()), + _mark_context(ShenandoahHeap::heap()->marking_context()) {} + + void do_oop(narrowOop* p) { do_oop_work(p); } + void do_oop(oop* p) { do_oop_work(p); } +}; + class ShenandoahTraversalPrecleanTask : public AbstractGangTask { private: ReferenceProcessor* _rp; @@ -872,16 +988,24 @@ ShenandoahHeap* sh = ShenandoahHeap::heap(); - ShenandoahObjToScanQueue* q = sh->traversal_gc()->task_queues()->queue(worker_id); + ShenandoahObjToScanQueue* queue = sh->traversal_gc()->task_queues()->queue(worker_id); ShenandoahForwardedIsAliveClosure is_alive; ShenandoahTraversalCancelledGCYieldClosure yield; ShenandoahTraversalPrecleanCompleteGCClosure complete_gc; - ShenandoahTraversalKeepAliveUpdateClosure keep_alive(q); - ResourceMark rm; - _rp->preclean_discovered_references(&is_alive, &keep_alive, - &complete_gc, &yield, - NULL); + if (UseShenandoahMatrix) { + ShenandoahTraversalKeepAliveUpdateMatrixClosure keep_alive(queue); + ResourceMark rm; + _rp->preclean_discovered_references(&is_alive, &keep_alive, + &complete_gc, &yield, + NULL); + } else { + ShenandoahTraversalKeepAliveUpdateClosure keep_alive(queue); + ResourceMark rm; + _rp->preclean_discovered_references(&is_alive, &keep_alive, + &complete_gc, &yield, + NULL); + } } }; @@ -1023,12 +1147,22 @@ ShenandoahTraversalDrainMarkingStackClosure complete_gc(worker_id, _terminator); ShenandoahForwardedIsAliveClosure is_alive; - if (!heap->is_degenerated_gc_in_progress()) { - ShenandoahTraversalKeepAliveUpdateClosure keep_alive(heap->traversal_gc()->task_queues()->queue(worker_id)); - _proc_task.work(worker_id, is_alive, keep_alive, complete_gc); + if (UseShenandoahMatrix) { + if (!heap->is_degenerated_gc_in_progress()) { + ShenandoahTraversalKeepAliveUpdateMatrixClosure keep_alive(heap->traversal_gc()->task_queues()->queue(worker_id)); + _proc_task.work(worker_id, is_alive, keep_alive, complete_gc); + } else { + ShenandoahTraversalKeepAliveUpdateDegenMatrixClosure keep_alive(heap->traversal_gc()->task_queues()->queue(worker_id)); + _proc_task.work(worker_id, is_alive, keep_alive, complete_gc); + } } else { - ShenandoahTraversalKeepAliveUpdateDegenClosure keep_alive(heap->traversal_gc()->task_queues()->queue(worker_id)); - _proc_task.work(worker_id, is_alive, keep_alive, complete_gc); + if (!heap->is_degenerated_gc_in_progress()) { + ShenandoahTraversalKeepAliveUpdateClosure keep_alive(heap->traversal_gc()->task_queues()->queue(worker_id)); + _proc_task.work(worker_id, is_alive, keep_alive, complete_gc); + } else { + ShenandoahTraversalKeepAliveUpdateDegenClosure keep_alive(heap->traversal_gc()->task_queues()->queue(worker_id)); + _proc_task.work(worker_id, is_alive, keep_alive, complete_gc); + } } } }; diff -r 5b2dc0283d55 src/hotspot/share/gc/shenandoah/shenandoahTraversalGC.hpp --- a/src/hotspot/share/gc/shenandoah/shenandoahTraversalGC.hpp Wed Mar 13 21:00:34 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahTraversalGC.hpp Thu Mar 14 09:29:53 2019 +0100 @@ -35,12 +35,18 @@ ShenandoahHeap* const _heap; ShenandoahObjToScanQueueSet* const _task_queues; ShenandoahHeapRegionSet _traversal_set; + ShenandoahHeapRegionSet _root_regions; + + ShenandoahHeapRegionSetIterator _root_regions_iterator; + + ShenandoahConnectionMatrix* const _matrix; public: ShenandoahTraversalGC(ShenandoahHeap* heap, size_t num_regions); ~ShenandoahTraversalGC(); ShenandoahHeapRegionSet* traversal_set() { return &_traversal_set; } + ShenandoahHeapRegionSet* root_regions() { return &_root_regions;} void reset(); void prepare(); @@ -48,7 +54,7 @@ void concurrent_traversal_collection(); void final_traversal_collection(); - template + template inline void process_oop(T* p, Thread* thread, ShenandoahObjToScanQueue* queue, ShenandoahMarkingContext* const mark_context); bool check_and_handle_cancelled_gc(ShenandoahTaskTerminator* terminator, bool sts_yield); diff -r 5b2dc0283d55 src/hotspot/share/gc/shenandoah/shenandoahTraversalGC.inline.hpp --- a/src/hotspot/share/gc/shenandoah/shenandoahTraversalGC.inline.hpp Wed Mar 13 21:00:34 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahTraversalGC.inline.hpp Thu Mar 14 09:29:53 2019 +0100 @@ -34,11 +34,12 @@ #include "gc/shenandoah/shenandoahTaskqueue.inline.hpp" #include "oops/oop.inline.hpp" -template +template void ShenandoahTraversalGC::process_oop(T* p, Thread* thread, ShenandoahObjToScanQueue* queue, ShenandoahMarkingContext* const mark_context) { T o = RawAccess<>::oop_load(p); if (!CompressedOops::is_null(o)) { oop obj = CompressedOops::decode_not_null(o); + bool update_matrix = true; if (DEGEN) { oop forw = ShenandoahBarrierSet::resolve_forwarded_not_null(obj); if (!oopDesc::equals_raw(obj, forw)) { @@ -53,13 +54,20 @@ } shenandoah_assert_forwarded_except(p, obj, _heap->cancelled_gc()); // Update reference. - _heap->atomic_compare_exchange_oop(forw, p, obj); + oop previous = _heap->atomic_compare_exchange_oop(forw, p, obj); + if (UPDATE_MATRIX && !oopDesc::equals_raw(previous, obj)) { + update_matrix = false; + } obj = forw; } shenandoah_assert_not_forwarded(p, obj); shenandoah_assert_not_in_cset_except(p, obj, _heap->cancelled_gc()); + if (UPDATE_MATRIX && update_matrix && _heap->is_in_reserved(p)) { + _matrix->set_connected(p, obj); + } + if (mark_context->mark(obj)) { bool succeeded = queue->push(ShenandoahMarkTask(obj)); assert(succeeded, "must succeed to push to task queue"); diff -r 5b2dc0283d55 src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp --- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp Wed Mar 13 21:00:34 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp Thu Mar 14 09:29:53 2019 +0100 @@ -25,6 +25,7 @@ #include "gc/shenandoah/shenandoahAsserts.hpp" #include "gc/shenandoah/shenandoahBrooksPointer.hpp" +#include "gc/shenandoah/shenandoahConnectionMatrix.hpp" #include "gc/shenandoah/shenandoahPhaseTimings.hpp" #include "gc/shenandoah/shenandoahHeap.inline.hpp" #include "gc/shenandoah/shenandoahRootProcessor.hpp" @@ -80,12 +81,16 @@ // methods. // // For performance reasons, only fully verify non-marked field values. - // We are here when the host object for *p is already marked. + // We are here when the host object for *p is already marked. If field value + // is marked already, then we have to verify the matrix connection between + // host object and field value, regardless. HeapWord* addr = (HeapWord*) obj; if (_map->par_mark(addr)) { verify_oop_at(p, obj); _stack->push(ShenandoahVerifierTask(obj)); + } else { + verify_matrix(p, obj); } } } @@ -244,6 +249,26 @@ assert(false, "Unhandled cset verification"); } + verify_matrix(_interior_loc, obj); + } + + void verify_matrix(void* interior, oop obj) { + if (!UseShenandoahMatrix || !_heap->is_in(interior)) return; + switch (_options._verify_matrix) { + case ShenandoahVerifier::_verify_matrix_conservative: { + size_t from_idx = _heap->heap_region_index_containing(interior); + size_t to_idx = _heap->heap_region_index_containing(obj); + _interior_loc = interior; + check(ShenandoahAsserts::_safe_all, obj, _heap->connection_matrix()->is_connected(from_idx, to_idx), + "Must be connected"); + _interior_loc = NULL; + break; + } + case ShenandoahVerifier::_verify_matrix_disable: + break; + default: + assert(false, "Unhandled matrix verification"); + } } public: @@ -605,7 +630,7 @@ void ShenandoahVerifier::verify_at_safepoint(const char *label, VerifyForwarded forwarded, VerifyMarked marked, - VerifyCollectionSet cset, + VerifyMatrix matrix, VerifyCollectionSet cset, VerifyLiveness liveness, VerifyRegions regions, VerifyGCState gcstate) { guarantee(ShenandoahSafepoint::is_at_shenandoah_safepoint(), "only when nothing else happens"); @@ -681,7 +706,7 @@ ShenandoahLivenessData* ld = NEW_C_HEAP_ARRAY(ShenandoahLivenessData, _heap->num_regions(), mtGC); Copy::fill_to_bytes((void*)ld, _heap->num_regions()*sizeof(ShenandoahLivenessData), 0); - const VerifyOptions& options = ShenandoahVerifier::VerifyOptions(forwarded, marked, cset, liveness, regions, gcstate); + const VerifyOptions& options = ShenandoahVerifier::VerifyOptions(forwarded, marked, matrix, cset, liveness, regions, gcstate); // Steps 1-2. Scan root set to get initial reachable set. Finish walking the reachable heap. // This verifies what application can see, since it only cares about reachable objects. @@ -753,6 +778,7 @@ "Generic Verification", _verify_forwarded_allow, // conservatively allow forwarded _verify_marked_disable, // do not verify marked: lots ot time wasted checking dead allocations + _verify_matrix_disable, // matrix can be inconsistent here _verify_cset_disable, // cset may be inconsistent _verify_liveness_disable, // no reliable liveness data _verify_regions_disable, // no reliable region data @@ -766,6 +792,7 @@ "Before Mark", _verify_forwarded_allow, // may have forwarded references _verify_marked_disable, // do not verify marked: lots ot time wasted checking dead allocations + _verify_matrix_disable, // matrix is not used in regular GC _verify_cset_forwarded, // allow forwarded references to cset _verify_liveness_disable, // no reliable liveness data _verify_regions_notrash, // no trash regions @@ -776,6 +803,7 @@ "Before Mark", _verify_forwarded_none, // UR should have fixed up _verify_marked_disable, // do not verify marked: lots ot time wasted checking dead allocations + _verify_matrix_disable, // matrix is not used in regular GC _verify_cset_none, // UR should have fixed this _verify_liveness_disable, // no reliable liveness data _verify_regions_notrash, // no trash regions @@ -789,6 +817,7 @@ "After Mark", _verify_forwarded_none, // no forwarded references _verify_marked_complete, // bitmaps as precise as we can get + _verify_matrix_disable, // matrix is not used in regular GC _verify_cset_none, // no references to cset anymore _verify_liveness_complete, // liveness data must be complete here _verify_regions_disable, // trash regions not yet recycled @@ -801,6 +830,7 @@ "Before Evacuation", _verify_forwarded_none, // no forwarded references _verify_marked_complete, // walk over marked objects too + _verify_matrix_disable, // matrix is not used in regular GC _verify_cset_disable, // non-forwarded references to cset expected _verify_liveness_complete, // liveness data must be complete here _verify_regions_disable, // trash regions not yet recycled @@ -813,6 +843,7 @@ "After Evacuation", _verify_forwarded_allow, // objects are still forwarded _verify_marked_complete, // bitmaps might be stale, but alloc-after-mark should be well + _verify_matrix_disable, // matrix is not used in regular GC _verify_cset_forwarded, // all cset refs are fully forwarded _verify_liveness_disable, // no reliable liveness data anymore _verify_regions_notrash, // trash regions have been recycled already @@ -825,6 +856,7 @@ "Before Updating References", _verify_forwarded_allow, // forwarded references allowed _verify_marked_complete, // bitmaps might be stale, but alloc-after-mark should be well + _verify_matrix_disable, // matrix is not used in regular GC _verify_cset_forwarded, // all cset refs are fully forwarded _verify_liveness_disable, // no reliable liveness data anymore _verify_regions_notrash, // trash regions have been recycled already @@ -837,6 +869,7 @@ "After Updating References", _verify_forwarded_none, // no forwarded references _verify_marked_complete, // bitmaps might be stale, but alloc-after-mark should be well + _verify_matrix_disable, // matrix is not used in regular GC _verify_cset_none, // no cset references, all updated _verify_liveness_disable, // no reliable liveness data anymore _verify_regions_nocset, // no cset regions, trash regions have appeared @@ -849,6 +882,7 @@ "After Degenerated GC", _verify_forwarded_none, // all objects are non-forwarded _verify_marked_complete, // all objects are marked in complete bitmap + _verify_matrix_conservative, // matrix is conservatively consistent _verify_cset_none, // no cset references _verify_liveness_disable, // no reliable liveness data anymore _verify_regions_notrash_nocset, // no trash, no cset @@ -861,6 +895,7 @@ "Before Traversal", _verify_forwarded_none, // cannot have forwarded objects _verify_marked_disable, // bitmaps are not relevant before traversal + _verify_matrix_conservative, // matrix is conservatively consistent _verify_cset_none, // no cset references before traversal _verify_liveness_disable, // no reliable liveness data anymore _verify_regions_notrash_nocset, // no trash and no cset regions @@ -873,6 +908,7 @@ "After Traversal", _verify_forwarded_none, // cannot have forwarded objects _verify_marked_complete, // should have complete marking after traversal + _verify_matrix_conservative, // matrix is conservatively consistent _verify_cset_none, // no cset references left after traversal _verify_liveness_disable, // liveness data is not collected for new allocations _verify_regions_nocset, // no cset regions, trash regions allowed @@ -885,6 +921,7 @@ "Before Full GC", _verify_forwarded_allow, // can have forwarded objects _verify_marked_disable, // do not verify marked: lots ot time wasted checking dead allocations + _verify_matrix_disable, // matrix might be foobared _verify_cset_disable, // cset might be foobared _verify_liveness_disable, // no reliable liveness data anymore _verify_regions_disable, // no reliable region data here @@ -897,6 +934,7 @@ "After Full GC", _verify_forwarded_none, // all objects are non-forwarded _verify_marked_complete, // all objects are marked in complete bitmap + _verify_matrix_conservative, // matrix is conservatively consistent _verify_cset_none, // no cset references _verify_liveness_disable, // no reliable liveness data anymore _verify_regions_notrash_nocset, // no trash, no cset diff -r 5b2dc0283d55 src/hotspot/share/gc/shenandoah/shenandoahVerifier.hpp --- a/src/hotspot/share/gc/shenandoah/shenandoahVerifier.hpp Wed Mar 13 21:00:34 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoahVerifier.hpp Thu Mar 14 09:29:53 2019 +0100 @@ -77,6 +77,21 @@ } VerifyMarked; typedef enum { + // Disable matrix verification completely + _verify_matrix_disable, + + // Conservative matrix verification: all connected objects should have matrix + // connections. The verification is conservative, because it allows matrix + // connection that do not have actual heap connections. + _verify_matrix_conservative, + + // Precise matrix verification: all connected objects should have matrix connections, + // *and* every matrix connection should have at least a pair a connected objects. + // TODO: implement this, if needed + _verify_matrix_precise, + } VerifyMatrix; + + typedef enum { // Disable forwarded objects verification. _verify_forwarded_disable, @@ -140,6 +155,7 @@ struct VerifyOptions { VerifyForwarded _verify_forwarded; VerifyMarked _verify_marked; + VerifyMatrix _verify_matrix; VerifyCollectionSet _verify_cset; VerifyLiveness _verify_liveness; VerifyRegions _verify_regions; @@ -147,6 +163,7 @@ VerifyOptions(VerifyForwarded verify_forwarded, VerifyMarked verify_marked, + VerifyMatrix verify_matrix, VerifyCollectionSet verify_collection_set, VerifyLiveness verify_liveness, VerifyRegions verify_regions, @@ -161,6 +178,7 @@ void verify_at_safepoint(const char *label, VerifyForwarded forwarded, VerifyMarked marked, + VerifyMatrix matrix, VerifyCollectionSet cset, VerifyLiveness liveness, VerifyRegions regions, diff -r 5b2dc0283d55 src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp --- a/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp Wed Mar 13 21:00:34 2019 +0100 +++ b/src/hotspot/share/gc/shenandoah/shenandoah_globals.hpp Thu Mar 14 09:29:53 2019 +0100 @@ -364,6 +364,12 @@ diagnostic(bool, ShenandoahLoadRefBarrier, true, \ "Turn on/off load-reference barriers in Shenandoah") \ \ + diagnostic(bool, UseShenandoahMatrix, false, \ + "Turn on/off Shenandoah connection matrix collection") \ + \ + diagnostic(bool, PrintShenandoahMatrix, false, \ + "Print connection matrix after marking") \ + \ diagnostic(bool, ShenandoahStoreCheck, false, \ "Emit additional code that checks objects are written to only" \ " in to-space") \