--- old/src/share/vm/opto/graphKit.cpp 2015-10-14 13:54:17.590807931 +0200 +++ new/src/share/vm/opto/graphKit.cpp 2015-10-14 13:54:17.477810663 +0200 @@ -29,6 +29,7 @@ #include "gc/shared/barrierSet.hpp" #include "gc/shared/cardTableModRefBS.hpp" #include "gc/shared/collectedHeap.hpp" +#include "gc/shenandoah/brooksPointer.hpp" #include "opto/addnode.hpp" #include "opto/castnode.hpp" #include "opto/convertnode.hpp" @@ -41,6 +42,7 @@ #include "opto/parse.hpp" #include "opto/rootnode.hpp" #include "opto/runtime.hpp" +#include "opto/shenandoahSupport.hpp" #include "runtime/deoptimization.hpp" #include "runtime/sharedRuntime.hpp" @@ -1153,6 +1155,9 @@ // Special-case a fresh allocation to avoid building nodes: Node* akls = AllocateNode::Ideal_klass(obj, &_gvn); if (akls != NULL) return akls; + if (ShenandoahVerifyReadsToFromSpace) { + obj = shenandoah_read_barrier(obj); + } Node* k_adr = basic_plus_adr(obj, oopDesc::klass_offset_in_bytes()); return _gvn.transform(LoadKlassNode::make(_gvn, NULL, immutable_memory(), k_adr, TypeInstPtr::KLASS)); } @@ -1163,6 +1168,10 @@ AllocateArrayNode* alloc = AllocateArrayNode::Ideal_array_allocation(array, &_gvn); Node *alen; if (alloc == NULL) { + if (ShenandoahVerifyReadsToFromSpace) { + array = shenandoah_read_barrier(array); + } + Node *r_adr = basic_plus_adr(array, arrayOopDesc::length_offset_in_bytes()); alen = _gvn.transform( new LoadRangeNode(0, immutable_memory(), r_adr, TypeInt::POS)); } else { @@ -1519,6 +1528,7 @@ set_control(ctl); switch (bs->kind()) { case BarrierSet::G1SATBCTLogging: + case BarrierSet::ShenandoahBarrierSet: g1_write_barrier_pre(do_load, obj, adr, adr_idx, val, val_type, pre_val, bt); break; @@ -1537,6 +1547,7 @@ BarrierSet* bs = Universe::heap()->barrier_set(); switch (bs->kind()) { case BarrierSet::G1SATBCTLogging: + case BarrierSet::ShenandoahBarrierSet: return true; // Can move it if no safepoint case BarrierSet::CardTableForRS: @@ -1571,6 +1582,7 @@ break; case BarrierSet::ModRef: + case BarrierSet::ShenandoahBarrierSet: break; default : @@ -1678,6 +1690,9 @@ uint nargs = call->method()->arg_size(); for (uint i = 0; i < nargs; i++) { Node* arg = argument(i); + if (ShenandoahVerifyReadsToFromSpace && call->is_CallDynamicJava() && i == 0) { + arg = shenandoah_read_barrier(arg); + } call->init_req(i + TypeFunc::Parms, arg); } } @@ -2919,6 +2934,10 @@ } } + if (ShenandoahVerifyReadsToFromSpace) { + not_null_obj = shenandoah_read_barrier(not_null_obj); + } + // Load the object's klass Node* obj_klass = load_object_klass(not_null_obj); @@ -3000,6 +3019,10 @@ Node* null_ctl = top(); Node* not_null_obj = null_check_oop(obj, &null_ctl, never_see_null, safe_for_replace, speculative_not_null); + if (ShenandoahVerifyReadsToFromSpace) { + not_null_obj = shenandoah_read_barrier(not_null_obj); + } + // If not_null_obj is dead, only null-path is taken if (stopped()) { // Doing instance-of on a NULL? set_control(null_ctl); @@ -3149,6 +3172,8 @@ assert(dead_locals_are_killed(), "should kill locals before sync. point"); + obj = shenandoah_write_barrier(obj); + // Box the stack location Node* box = _gvn.transform(new BoxLockNode(next_monitor())); Node* mem = reset_memory(); @@ -3622,6 +3647,10 @@ if (ptr == NULL) { // reduce dumb test in callers return NULL; } + + // Attempt to see through Shenandoah barriers. + ptr = ShenandoahBarrierNode::skip_through_barrier(ptr); + if (ptr->is_CheckCastPP()) { // strip only one raw-to-oop cast ptr = ptr->in(1); if (ptr == NULL) return NULL; @@ -4274,6 +4303,9 @@ false, NULL, 0); const TypePtr* offset_field_type = string_type->add_offset(offset_offset); int offset_field_idx = C->get_alias_index(offset_field_type); + + str = shenandoah_read_barrier(str); + return make_load(ctrl, basic_plus_adr(str, str, offset_offset), TypeInt::INT, T_INT, offset_field_idx, MemNode::unordered); @@ -4289,6 +4321,9 @@ false, NULL, 0); const TypePtr* count_field_type = string_type->add_offset(count_offset); int count_field_idx = C->get_alias_index(count_field_type); + + str = shenandoah_read_barrier(str); + return make_load(ctrl, basic_plus_adr(str, str, count_offset), TypeInt::INT, T_INT, count_field_idx, MemNode::unordered); @@ -4306,6 +4341,9 @@ TypeAry::make(TypeInt::CHAR,TypeInt::POS), ciTypeArrayKlass::make(T_CHAR), true, 0); int value_field_idx = C->get_alias_index(value_field_type); + + str = shenandoah_read_barrier(str); + Node* load = make_load(ctrl, basic_plus_adr(str, str, value_offset), value_type, T_OBJECT, value_field_idx, MemNode::unordered); // String.value field is known to be @Stable. @@ -4321,7 +4359,10 @@ false, NULL, 0); const TypePtr* offset_field_type = string_type->add_offset(offset_offset); int offset_field_idx = C->get_alias_index(offset_field_type); - store_to_memory(ctrl, basic_plus_adr(str, offset_offset), + + str = shenandoah_write_barrier(str); + + store_to_memory(control(), basic_plus_adr(str, offset_offset), value, T_INT, offset_field_idx, MemNode::unordered); } @@ -4331,7 +4372,10 @@ false, NULL, 0); const TypePtr* value_field_type = string_type->add_offset(value_offset); - store_oop_to_object(ctrl, str, basic_plus_adr(str, value_offset), value_field_type, + str = shenandoah_write_barrier(str); + value = shenandoah_read_barrier_nomem(value); + + store_oop_to_object(control(), str, basic_plus_adr(str, value_offset), value_field_type, value, TypeAryPtr::CHARS, T_OBJECT, MemNode::unordered); } @@ -4341,7 +4385,10 @@ false, NULL, 0); const TypePtr* count_field_type = string_type->add_offset(count_offset); int count_field_idx = C->get_alias_index(count_field_type); - store_to_memory(ctrl, basic_plus_adr(str, count_offset), + + str = shenandoah_write_barrier(str); + + store_to_memory(control(), basic_plus_adr(str, count_offset), value, T_INT, count_field_idx, MemNode::unordered); } @@ -4350,3 +4397,189 @@ // assumption of CCP analysis. return _gvn.transform(new CastPPNode(ary, ary_type->cast_to_stable(true))); } + +Node* GraphKit::shenandoah_read_barrier(Node* obj) { + return shenandoah_read_barrier_impl(obj, false, true); +} + +Node* GraphKit::shenandoah_read_barrier_nomem(Node* obj) { + return shenandoah_read_barrier_impl(obj, false, false); +} + +Node* GraphKit::shenandoah_read_barrier_impl(Node* obj, bool use_ctrl, bool use_mem) { + + if (UseShenandoahGC && ShenandoahReadBarrier) { + const Type* obj_type = obj->bottom_type(); + if (obj_type->higher_equal(TypePtr::NULL_PTR)) { + return obj; + } + const TypePtr* adr_type = obj_type->is_ptr()->add_offset(BrooksPointer::BYTE_OFFSET); + Node* mem = use_mem ? memory(adr_type) : immutable_memory(); + + if (! ShenandoahBarrierNode::needs_barrier(&_gvn, NULL, obj, mem)) { + // We know it is null, no barrier needed. + return obj; + } + + + if (obj_type->meet(TypePtr::NULL_PTR) == obj_type->remove_speculative()) { + + // We don't know if it's null or not. Need null-check. + enum { _not_null_path = 1, _null_path, PATH_LIMIT }; + RegionNode* region = new RegionNode(PATH_LIMIT); + Node* phi = new PhiNode(region, obj_type); + Node* null_ctrl = top(); + Node* not_null_obj = null_check_oop(obj, &null_ctrl); + + region->init_req(_null_path, null_ctrl); + phi ->init_req(_null_path, obj); + + Node* ctrl = use_ctrl ? control() : NULL; + ShenandoahReadBarrierNode* rb = new ShenandoahReadBarrierNode(ctrl, mem, not_null_obj); + Node* n = _gvn.transform(rb); + + region->init_req(_not_null_path, control()); + phi ->init_req(_not_null_path, n); + + set_control(_gvn.transform(region)); + record_for_igvn(region); + return _gvn.transform(phi); + + } else { + // We know it is not null. Simple barrier is sufficient. + Node* ctrl = use_ctrl ? control() : NULL; + ShenandoahReadBarrierNode* rb = new ShenandoahReadBarrierNode(ctrl, mem, obj); + Node* n = _gvn.transform(rb); + record_for_igvn(n); + return n; + } + + } else { + return obj; + } +} + +Node* GraphKit::shenandoah_write_barrier(Node* obj) { + + if (UseShenandoahGC && ShenandoahWriteBarrier) { + + if (! ShenandoahBarrierNode::needs_barrier(&_gvn, NULL, obj, NULL)) { + return obj; + } + const Type* obj_type = obj->bottom_type(); + const TypePtr* adr_type = obj_type->is_ptr()->add_offset(BrooksPointer::BYTE_OFFSET); + if (obj_type->meet(TypePtr::NULL_PTR) == obj_type->remove_speculative()) { + // We don't know if it's null or not. Need null-check. + enum { _not_null_path = 1, _null_path, PATH_LIMIT }; + RegionNode* region = new RegionNode(PATH_LIMIT); + Node* phi = new PhiNode(region, obj_type); + Node* memphi = PhiNode::make(region, memory(adr_type), Type::MEMORY, C->alias_type(adr_type)->adr_type()); + + Node* prev_mem = memory(adr_type); + Node* null_ctrl = top(); + Node* not_null_obj = null_check_oop(obj, &null_ctrl); + + region->init_req(_null_path, null_ctrl); + phi ->init_req(_null_path, null()); + memphi->init_req(_null_path, prev_mem); + + ShenandoahWriteBarrierNode* wb = new ShenandoahWriteBarrierNode(NULL, memory(adr_type), not_null_obj); + Node* n = _gvn.transform(wb); + if (n == wb) { // New barrier needs memory projection. + Node* proj = _gvn.transform(new ShenandoahWBMemProjNode(n)); + set_memory(proj, adr_type); + } + + region->init_req(_not_null_path, control()); + phi ->init_req(_not_null_path, n); + memphi->init_req(_not_null_path, memory(adr_type)); + + set_control(_gvn.transform(region)); + record_for_igvn(region); + set_memory(_gvn.transform(memphi), adr_type); + + Node* res_val = _gvn.transform(phi); + // replace_in_map(obj, res_val); + return res_val; + } else { + // We know it is not null. Simple barrier is sufficient. + ShenandoahWriteBarrierNode* wb = new ShenandoahWriteBarrierNode(NULL, memory(adr_type), obj); + Node* n = _gvn.transform(wb); + if (n == wb) { + Node* proj = _gvn.transform(new ShenandoahWBMemProjNode(wb)); + set_memory(proj, adr_type); + } + // replace_in_map(obj, n); + record_for_igvn(n); + return n; + } + + } else { + return obj; + } +} + +/** + * We need barriers on acmp (and similar instructions that compare two + * oops) to avoid false negatives. If it compares a from-space and a to-space + * copy of an object, a regular acmp would return false, even though both are + * the same. The acmp barrier compares the two objects, and when they are + * *not equal* it does a read-barrier on both, and compares them again. When it + * failed because of different copies of the object, we know that the object + * must already have been evacuated (and therefore doesn't require a write-barrier). + */ +void GraphKit::shenandoah_acmp_barrier(Node*& a, Node*& b) { + if (UseShenandoahGC) { + const Type* a_type = a->bottom_type(); + const Type* b_type = b->bottom_type(); + if (a_type->higher_equal(TypePtr::NULL_PTR) || b_type->higher_equal(TypePtr::NULL_PTR)) { + // We know one arg is gonna be null. No need for barriers. + return; + } + if (a_type->is_oopptr()->const_oop() != NULL && b_type->is_oopptr()->const_oop() != NULL ) { + // We know one arg is inlined constant. No need for barriers. + return; + } + if (a->Opcode() == Op_ShenandoahWriteBarrier && b->Opcode() == Op_ShenandoahWriteBarrier) { + // We know one arg is already write-barrier'd. No need for barriers. + return; + } + if (AllocateNode::Ideal_allocation(a, &_gvn) != NULL || AllocateNode::Ideal_allocation(b, &_gvn) != NULL) { + // We know one arg is already in to-space. No need for barriers. + return; + } + + enum { _equal = 1, _not_equal, PATH_LIMIT }; + RegionNode* region = new RegionNode(PATH_LIMIT); + PhiNode* phiA = PhiNode::make(region, a); + PhiNode* phiB = PhiNode::make(region, b); + + Node* cmp = _gvn.transform(new CmpPNode(b, a)); + Node* tst = _gvn.transform(new BoolNode(cmp, BoolTest::eq)); + + // TODO: Use profiling data. + IfNode* iff = create_and_map_if(control(), tst, PROB_FAIR, COUNT_UNKNOWN); + Node* iftrue = _gvn.transform(new IfTrueNode(iff)); + Node* iffalse = _gvn.transform(new IfFalseNode(iff)); + + // Equal path: Use original values. + region->init_req(_equal, iftrue); + phiA->init_req(_equal, a); + phiB->init_req(_equal, b); + + // Unequal path: retry after read barriers. + set_control(iffalse); + a = shenandoah_read_barrier_impl(a, true, true); + b = shenandoah_read_barrier_impl(b, true, true); + + region->init_req(_not_equal, control()); + phiA->init_req(_not_equal, a); + phiB->init_req(_not_equal, b); + + set_control(_gvn.transform(region)); + record_for_igvn(region); + + a = _gvn.transform(phiA); + b = _gvn.transform(phiB); + } +}