--- old/src/share/vm/opto/graphKit.cpp 2014-09-15 15:29:39.748521301 -0700 +++ new/src/share/vm/opto/graphKit.cpp 2014-09-15 15:29:39.660521304 -0700 @@ -3827,6 +3827,114 @@ final_sync(ideal); } +/* + * Determine if the G1 pre-barrier can be removed. The pre-barrier is + * required by SATB to make sure all objects live at the start of the + * marking are kept alive, all reference updates need to any previous + * reference stored before writing. + * + * If the previous value is NULL there is no need to save the old value. + * References that are NULL are filtered during runtime by the barrier + * code to avoid unnecessary queuing. + * + * However in the case of newly allocated objects it might be possible to + * prove that the reference about to be overwritten is NULL during compile + * time and avoid adding the barrier code completely. + * + * The compiler needs to determine that the object in which a field is about + * to be written is newly allocated, and that no prior store to the same field + * has happened since the allocation. + * + * Returns true if the pre-barrier can be removed + */ +bool GraphKit::g1_can_remove_pre_barrier(PhaseTransform* phase, Node* adr, + BasicType bt, uint adr_idx) { + intptr_t offset = 0; + Node* base = AddPNode::Ideal_base_and_offset(adr, phase, offset); + AllocateNode* alloc = AllocateNode::Ideal_allocation(base, phase); + + if (offset == Type::OffsetBot) { + return false; // cannot unalias unless there are precise offsets + } + + if (alloc == NULL) { + return false; // No allocation found + } + + intptr_t size_in_bytes = type2aelembytes(bt); + + Node* mem = memory(adr_idx); // start searching here... + + for (int cnt = 0; cnt < 50; cnt++) { + + if (mem->is_Store()) { + + Node* st_adr = mem->in(MemNode::Address); + intptr_t st_offset = 0; + Node* st_base = AddPNode::Ideal_base_and_offset(st_adr, phase, st_offset); + + if (st_base == NULL) { + break; // inscrutable pointer + } + + // Break we have found a store with same base and offset as ours so break + if (st_base == base && st_offset == offset) { + break; + } + + if (st_offset != offset && st_offset != Type::OffsetBot) { + const int MAX_STORE = BytesPerLong; + if (st_offset >= offset + size_in_bytes || + st_offset <= offset - MAX_STORE || + st_offset <= offset - mem->as_Store()->memory_size()) { + // Success: The offsets are provably independent. + // (You may ask, why not just test st_offset != offset and be done? + // The answer is that stores of different sizes can co-exist + // in the same sequence of RawMem effects. We sometimes initialize + // a whole 'tile' of array elements with a single jint or jlong.) + mem = mem->in(MemNode::Memory); + continue; // advance through independent store memory + } + } + + if (st_base != base && + MemNode::detect_ptr_independence(base, alloc, st_base, + AllocateNode::Ideal_allocation(st_base, phase), + phase)) { + // Success: The bases are provably independent. + mem = mem->in(MemNode::Memory); + continue; // advance through independent store memory + } + } else if (mem->is_Proj() && mem->in(0)->is_Initialize()) { + + InitializeNode* st_init = mem->in(0)->as_Initialize(); + AllocateNode* st_alloc = st_init->allocation(); + + if (st_alloc == NULL) { + break; // something degenerated + } + + // Make sure that we are looking at the same allocation site + if (alloc == st_alloc) { + // Check that the initialization is storing NULL so that no previous store + // has been moved up and directly write a reference + Node* captured_store = st_init->find_captured_store(offset, type2aelembytes(T_OBJECT), phase); + if (captured_store != NULL && captured_store != st_init->zero_memory()) { + return false; + } + + return true; + } + } + + // Unless there is an explicit 'continue', we must bail out here, + // because 'mem' is an inscrutable memory state (e.g., a call). + break; + } + + return false; +} + // G1 pre/post barriers void GraphKit::g1_write_barrier_pre(bool do_load, Node* obj, @@ -3846,6 +3954,12 @@ assert(adr != NULL, "where are loading from?"); assert(pre_val == NULL, "loaded already?"); assert(val_type != NULL, "need a type"); + + if (use_ReduceInitialCardMarks() + && g1_can_remove_pre_barrier(&_gvn, adr, bt, alias_idx)) { + return; + } + } else { // In this case both val_type and alias_idx are unused. assert(pre_val != NULL, "must be loaded already"); @@ -3927,6 +4041,64 @@ final_sync(ideal); } +/* + * G1 similar to any GC with a Young Generation requires a way to keep track of + * references from Old Generation to Young Generation to make sure all live + * objects are found. G1 also requires to keep track of object references + * between different regions to enable evacuation of old regions, which is done + * as part of mixed collections. References are tracked in remembered sets and + * is continuously updated as reference are written to with the help of the + * post-barrier. + * + * To reduce the number of updates to the remembered set the post-barrier + * filters updates to fields in objects located in the Young Generation, + * the same region as the reference, when the NULL is being written or + * if the card is already marked as dirty by an earlier write. + * + * Under certain circumstances it is possible to avoid generating the + * post-barrier completely if it is possible during compile time to prove + * the object is newly allocated and that no safepoint exists between the + * allocation and the store. + * + * In the case of slow allocation the allocation code must handle the barrier + * as part of the allocation in the case the allocated object is not located + * in the nursery, this would happen for humongous objects. This is similar to + * how CMS is required to handle this case, see the comments for the method + * CollectedHeap::new_store_pre_barrier and OptoRuntime::new_store_pre_barrier. + * A deferred card mark is required for these objects and handled in the above + * mentioned methods. + * + * Returns true if the post barrier can be removed + */ +bool GraphKit::g1_can_remove_post_barrier(PhaseTransform* phase, Node* store, + Node* adr) { + intptr_t offset = 0; + Node* base = AddPNode::Ideal_base_and_offset(adr, phase, offset); + AllocateNode* alloc = AllocateNode::Ideal_allocation(base, phase); + + if (offset == Type::OffsetBot) { + return false; // cannot unalias unless there are precise offsets + } + + if (alloc == NULL) { + return false; // No allocation found + } + + // Start search from Store node + Node* mem = store->in(MemNode::Control); + if (mem->is_Proj() && mem->in(0)->is_Initialize()) { + + InitializeNode* st_init = mem->in(0)->as_Initialize(); + AllocateNode* st_alloc = st_init->allocation(); + // Make sure we are looking at the same allocation + if (st_alloc != NULL && alloc == st_alloc) { + return true; + } + } + + return false; +} + // // Update the card table and add card address to the queue // @@ -3979,6 +4151,20 @@ return; } + if (use_ReduceInitialCardMarks() && obj == just_allocated_object(control())) { + // We can skip marks on a freshly-allocated object in Eden. + // Keep this code in sync with new_store_pre_barrier() in runtime.cpp. + // That routine informs GC to take appropriate compensating steps, + // upon a slow-path allocation, so as to make this card-mark + // elision safe. + return; + } + + if (use_ReduceInitialCardMarks() + && g1_can_remove_post_barrier(&_gvn, oop_store, adr)) { + return; + } + if (!use_precise) { // All card marks for a (non-array) instance are in one place: adr = obj; --- old/src/share/vm/opto/graphKit.hpp 2014-09-15 15:29:40.092521289 -0700 +++ new/src/share/vm/opto/graphKit.hpp 2014-09-15 15:29:40.008521292 -0700 @@ -771,6 +771,10 @@ Node* index, Node* index_adr, Node* buffer, const TypeFunc* tf); + bool g1_can_remove_pre_barrier(PhaseTransform* phase, Node* adr, BasicType bt, uint adr_idx); + + bool g1_can_remove_post_barrier(PhaseTransform* phase, Node* store, Node* adr); + public: // Helper function to round double arguments before a call void round_double_arguments(ciMethod* dest_method);