mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
6395208: Elide autoboxing for calls to HashMap.get(int) and HashMap.get(long)
Reviewed-by: kvn, rasbold
This commit is contained in:
parent
5fa349cc42
commit
10c473e425
16 changed files with 540 additions and 67 deletions
|
@ -634,6 +634,46 @@ uint LoadNode::hash() const {
|
|||
Node* MemNode::can_see_stored_value(Node* st, PhaseTransform* phase) const {
|
||||
Node* ld_adr = in(MemNode::Address);
|
||||
|
||||
const TypeInstPtr* tp = phase->type(ld_adr)->isa_instptr();
|
||||
Compile::AliasType* atp = tp != NULL ? phase->C->alias_type(tp) : NULL;
|
||||
if (EliminateAutoBox && atp != NULL && atp->index() >= Compile::AliasIdxRaw &&
|
||||
atp->field() != NULL && !atp->field()->is_volatile()) {
|
||||
uint alias_idx = atp->index();
|
||||
bool final = atp->field()->is_final();
|
||||
Node* result = NULL;
|
||||
Node* current = st;
|
||||
// Skip through chains of MemBarNodes checking the MergeMems for
|
||||
// new states for the slice of this load. Stop once any other
|
||||
// kind of node is encountered. Loads from final memory can skip
|
||||
// through any kind of MemBar but normal loads shouldn't skip
|
||||
// through MemBarAcquire since the could allow them to move out of
|
||||
// a synchronized region.
|
||||
while (current->is_Proj()) {
|
||||
int opc = current->in(0)->Opcode();
|
||||
if ((final && opc == Op_MemBarAcquire) ||
|
||||
opc == Op_MemBarRelease || opc == Op_MemBarCPUOrder) {
|
||||
Node* mem = current->in(0)->in(TypeFunc::Memory);
|
||||
if (mem->is_MergeMem()) {
|
||||
MergeMemNode* merge = mem->as_MergeMem();
|
||||
Node* new_st = merge->memory_at(alias_idx);
|
||||
if (new_st == merge->base_memory()) {
|
||||
// Keep searching
|
||||
current = merge->base_memory();
|
||||
continue;
|
||||
}
|
||||
// Save the new memory state for the slice and fall through
|
||||
// to exit.
|
||||
result = new_st;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (result != NULL) {
|
||||
st = result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Loop around twice in the case Load -> Initialize -> Store.
|
||||
// (See PhaseIterGVN::add_users_to_worklist, which knows about this case.)
|
||||
for (int trip = 0; trip <= 1; trip++) {
|
||||
|
@ -723,6 +763,168 @@ Node *LoadNode::Identity( PhaseTransform *phase ) {
|
|||
return this;
|
||||
}
|
||||
|
||||
|
||||
// Returns true if the AliasType refers to the field that holds the
|
||||
// cached box array. Currently only handles the IntegerCache case.
|
||||
static bool is_autobox_cache(Compile::AliasType* atp) {
|
||||
if (atp != NULL && atp->field() != NULL) {
|
||||
ciField* field = atp->field();
|
||||
ciSymbol* klass = field->holder()->name();
|
||||
if (field->name() == ciSymbol::cache_field_name() &&
|
||||
field->holder()->uses_default_loader() &&
|
||||
klass == ciSymbol::java_lang_Integer_IntegerCache()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fetch the base value in the autobox array
|
||||
static bool fetch_autobox_base(Compile::AliasType* atp, int& cache_offset) {
|
||||
if (atp != NULL && atp->field() != NULL) {
|
||||
ciField* field = atp->field();
|
||||
ciSymbol* klass = field->holder()->name();
|
||||
if (field->name() == ciSymbol::cache_field_name() &&
|
||||
field->holder()->uses_default_loader() &&
|
||||
klass == ciSymbol::java_lang_Integer_IntegerCache()) {
|
||||
assert(field->is_constant(), "what?");
|
||||
ciObjArray* array = field->constant_value().as_object()->as_obj_array();
|
||||
// Fetch the box object at the base of the array and get its value
|
||||
ciInstance* box = array->obj_at(0)->as_instance();
|
||||
ciInstanceKlass* ik = box->klass()->as_instance_klass();
|
||||
if (ik->nof_nonstatic_fields() == 1) {
|
||||
// This should be true nonstatic_field_at requires calling
|
||||
// nof_nonstatic_fields so check it anyway
|
||||
ciConstant c = box->field_value(ik->nonstatic_field_at(0));
|
||||
cache_offset = c.as_int();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns true if the AliasType refers to the value field of an
|
||||
// autobox object. Currently only handles Integer.
|
||||
static bool is_autobox_object(Compile::AliasType* atp) {
|
||||
if (atp != NULL && atp->field() != NULL) {
|
||||
ciField* field = atp->field();
|
||||
ciSymbol* klass = field->holder()->name();
|
||||
if (field->name() == ciSymbol::value_name() &&
|
||||
field->holder()->uses_default_loader() &&
|
||||
klass == ciSymbol::java_lang_Integer()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// We're loading from an object which has autobox behaviour.
|
||||
// If this object is result of a valueOf call we'll have a phi
|
||||
// merging a newly allocated object and a load from the cache.
|
||||
// We want to replace this load with the original incoming
|
||||
// argument to the valueOf call.
|
||||
Node* LoadNode::eliminate_autobox(PhaseGVN* phase) {
|
||||
Node* base = in(Address)->in(AddPNode::Base);
|
||||
if (base->is_Phi() && base->req() == 3) {
|
||||
AllocateNode* allocation = NULL;
|
||||
int allocation_index = -1;
|
||||
int load_index = -1;
|
||||
for (uint i = 1; i < base->req(); i++) {
|
||||
allocation = AllocateNode::Ideal_allocation(base->in(i), phase);
|
||||
if (allocation != NULL) {
|
||||
allocation_index = i;
|
||||
load_index = 3 - allocation_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
LoadNode* load = NULL;
|
||||
if (allocation != NULL && base->in(load_index)->is_Load()) {
|
||||
load = base->in(load_index)->as_Load();
|
||||
}
|
||||
if (load != NULL && in(Memory)->is_Phi() && in(Memory)->in(0) == base->in(0)) {
|
||||
// Push the loads from the phi that comes from valueOf up
|
||||
// through it to allow elimination of the loads and the recovery
|
||||
// of the original value.
|
||||
Node* mem_phi = in(Memory);
|
||||
Node* offset = in(Address)->in(AddPNode::Offset);
|
||||
|
||||
Node* in1 = clone();
|
||||
Node* in1_addr = in1->in(Address)->clone();
|
||||
in1_addr->set_req(AddPNode::Base, base->in(allocation_index));
|
||||
in1_addr->set_req(AddPNode::Address, base->in(allocation_index));
|
||||
in1_addr->set_req(AddPNode::Offset, offset);
|
||||
in1->set_req(0, base->in(allocation_index));
|
||||
in1->set_req(Address, in1_addr);
|
||||
in1->set_req(Memory, mem_phi->in(allocation_index));
|
||||
|
||||
Node* in2 = clone();
|
||||
Node* in2_addr = in2->in(Address)->clone();
|
||||
in2_addr->set_req(AddPNode::Base, base->in(load_index));
|
||||
in2_addr->set_req(AddPNode::Address, base->in(load_index));
|
||||
in2_addr->set_req(AddPNode::Offset, offset);
|
||||
in2->set_req(0, base->in(load_index));
|
||||
in2->set_req(Address, in2_addr);
|
||||
in2->set_req(Memory, mem_phi->in(load_index));
|
||||
|
||||
in1_addr = phase->transform(in1_addr);
|
||||
in1 = phase->transform(in1);
|
||||
in2_addr = phase->transform(in2_addr);
|
||||
in2 = phase->transform(in2);
|
||||
|
||||
PhiNode* result = PhiNode::make_blank(base->in(0), this);
|
||||
result->set_req(allocation_index, in1);
|
||||
result->set_req(load_index, in2);
|
||||
return result;
|
||||
}
|
||||
} else if (base->is_Load()) {
|
||||
// Eliminate the load of Integer.value for integers from the cache
|
||||
// array by deriving the value from the index into the array.
|
||||
// Capture the offset of the load and then reverse the computation.
|
||||
Node* load_base = base->in(Address)->in(AddPNode::Base);
|
||||
if (load_base != NULL) {
|
||||
Compile::AliasType* atp = phase->C->alias_type(load_base->adr_type());
|
||||
intptr_t cache_offset;
|
||||
int shift = -1;
|
||||
Node* cache = NULL;
|
||||
if (is_autobox_cache(atp)) {
|
||||
shift = exact_log2(type2aelembytes[T_OBJECT]);
|
||||
cache = AddPNode::Ideal_base_and_offset(load_base->in(Address), phase, cache_offset);
|
||||
}
|
||||
if (cache != NULL && base->in(Address)->is_AddP()) {
|
||||
Node* elements[4];
|
||||
int count = base->in(Address)->as_AddP()->unpack_offsets(elements, ARRAY_SIZE(elements));
|
||||
int cache_low;
|
||||
if (count > 0 && fetch_autobox_base(atp, cache_low)) {
|
||||
int offset = arrayOopDesc::base_offset_in_bytes(memory_type()) - (cache_low << shift);
|
||||
// Add up all the offsets making of the address of the load
|
||||
Node* result = elements[0];
|
||||
for (int i = 1; i < count; i++) {
|
||||
result = phase->transform(new (phase->C, 3) AddXNode(result, elements[i]));
|
||||
}
|
||||
// Remove the constant offset from the address and then
|
||||
// remove the scaling of the offset to recover the original index.
|
||||
result = phase->transform(new (phase->C, 3) AddXNode(result, phase->MakeConX(-offset)));
|
||||
if (result->Opcode() == Op_LShiftX && result->in(2) == phase->intcon(shift)) {
|
||||
// Peel the shift off directly but wrap it in a dummy node
|
||||
// since Ideal can't return existing nodes
|
||||
result = new (phase->C, 3) RShiftXNode(result->in(1), phase->intcon(0));
|
||||
} else {
|
||||
result = new (phase->C, 3) RShiftXNode(result, phase->intcon(shift));
|
||||
}
|
||||
#ifdef _LP64
|
||||
result = new (phase->C, 2) ConvL2INode(phase->transform(result));
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------Ideal------------------------------------------
|
||||
// If the load is from Field memory and the pointer is non-null, we can
|
||||
// zero out the control input.
|
||||
|
@ -755,6 +957,17 @@ Node *LoadNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
|||
}
|
||||
}
|
||||
|
||||
if (EliminateAutoBox && can_reshape && in(Address)->is_AddP()) {
|
||||
Node* base = in(Address)->in(AddPNode::Base);
|
||||
if (base != NULL) {
|
||||
Compile::AliasType* atp = phase->C->alias_type(adr_type());
|
||||
if (is_autobox_object(atp)) {
|
||||
Node* result = eliminate_autobox(phase);
|
||||
if (result != NULL) return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for prior store with a different base or offset; make Load
|
||||
// independent. Skip through any number of them. Bail out if the stores
|
||||
// are in an endless dead cycle and report no progress. This is a key
|
||||
|
@ -858,6 +1071,17 @@ const Type *LoadNode::Value( PhaseTransform *phase ) const {
|
|||
// This can happen if a interface-typed array narrows to a class type.
|
||||
jt = _type;
|
||||
}
|
||||
|
||||
if (EliminateAutoBox) {
|
||||
// The pointers in the autobox arrays are always non-null
|
||||
Node* base = in(Address)->in(AddPNode::Base);
|
||||
if (base != NULL) {
|
||||
Compile::AliasType* atp = phase->C->alias_type(base->adr_type());
|
||||
if (is_autobox_cache(atp)) {
|
||||
return jt->join(TypePtr::NOTNULL)->is_ptr();
|
||||
}
|
||||
}
|
||||
}
|
||||
return jt;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue