mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-24 04:54:40 +02:00
8057737: Avoid G1 write barriers on newly allocated objects
Reviewed-by: mgerdin, kvn, iveresov
This commit is contained in:
parent
60f3ade82b
commit
8b424b422f
2 changed files with 187 additions and 0 deletions
|
@ -3826,6 +3826,110 @@ void GraphKit::write_barrier_post(Node* oop_store,
|
||||||
// Final sync IdealKit and GraphKit.
|
// Final sync IdealKit and GraphKit.
|
||||||
final_sync(ideal);
|
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();
|
||||||
|
|
||||||
|
// Make sure that we are looking at the same allocation site.
|
||||||
|
// The alloc variable is guaranteed to not be null here from earlier check.
|
||||||
|
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 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
|
// G1 pre/post barriers
|
||||||
void GraphKit::g1_write_barrier_pre(bool do_load,
|
void GraphKit::g1_write_barrier_pre(bool do_load,
|
||||||
|
@ -3846,6 +3950,12 @@ void GraphKit::g1_write_barrier_pre(bool do_load,
|
||||||
assert(adr != NULL, "where are loading from?");
|
assert(adr != NULL, "where are loading from?");
|
||||||
assert(pre_val == NULL, "loaded already?");
|
assert(pre_val == NULL, "loaded already?");
|
||||||
assert(val_type != NULL, "need a type");
|
assert(val_type != NULL, "need a type");
|
||||||
|
|
||||||
|
if (use_ReduceInitialCardMarks()
|
||||||
|
&& g1_can_remove_pre_barrier(&_gvn, adr, bt, alias_idx)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// In this case both val_type and alias_idx are unused.
|
// In this case both val_type and alias_idx are unused.
|
||||||
assert(pre_val != NULL, "must be loaded already");
|
assert(pre_val != NULL, "must be loaded already");
|
||||||
|
@ -3927,6 +4037,65 @@ void GraphKit::g1_write_barrier_pre(bool do_load,
|
||||||
final_sync(ideal);
|
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 (alloc == st_alloc) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Update the card table and add card address to the queue
|
// Update the card table and add card address to the queue
|
||||||
//
|
//
|
||||||
|
@ -3979,6 +4148,20 @@ void GraphKit::g1_write_barrier_post(Node* oop_store,
|
||||||
return;
|
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) {
|
if (!use_precise) {
|
||||||
// All card marks for a (non-array) instance are in one place:
|
// All card marks for a (non-array) instance are in one place:
|
||||||
adr = obj;
|
adr = obj;
|
||||||
|
|
|
@ -771,6 +771,10 @@ class GraphKit : public Phase {
|
||||||
Node* index, Node* index_adr,
|
Node* index, Node* index_adr,
|
||||||
Node* buffer, const TypeFunc* tf);
|
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:
|
public:
|
||||||
// Helper function to round double arguments before a call
|
// Helper function to round double arguments before a call
|
||||||
void round_double_arguments(ciMethod* dest_method);
|
void round_double_arguments(ciMethod* dest_method);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue