6395208: Elide autoboxing for calls to HashMap.get(int) and HashMap.get(long)

Reviewed-by: kvn, rasbold
This commit is contained in:
Tom Rodriguez 2007-12-05 09:01:00 -08:00
parent 5fa349cc42
commit 10c473e425
16 changed files with 540 additions and 67 deletions

View File

@ -0,0 +1,43 @@
/*
* Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*
*/
#include "incls/_precompiled.incl"
#include "incls/_ciObjArray.cpp.incl"
// ciObjArray
//
// This class represents an objArrayOop in the HotSpot virtual
// machine.
ciObject* ciObjArray::obj_at(int index) {
VM_ENTRY_MARK;
objArrayOop array = get_objArrayOop();
if (index < 0 || index >= array->length()) return NULL;
oop o = array->obj_at(index);
if (o == NULL) {
return ciNullObject::make();
} else {
return CURRENT_ENV->get_object(o);
}
}

View File

@ -43,4 +43,6 @@ protected:
public: public:
// What kind of ciObject is this? // What kind of ciObject is this?
bool is_obj_array() { return true; } bool is_obj_array() { return true; }
ciObject* obj_at(int index);
}; };

View File

@ -58,12 +58,17 @@
template(java_lang_ThreadDeath, "java/lang/ThreadDeath") \ template(java_lang_ThreadDeath, "java/lang/ThreadDeath") \
template(java_lang_Boolean, "java/lang/Boolean") \ template(java_lang_Boolean, "java/lang/Boolean") \
template(java_lang_Character, "java/lang/Character") \ template(java_lang_Character, "java/lang/Character") \
template(java_lang_Character_CharacterCache, "java/lang/Character$CharacterCache") \
template(java_lang_Float, "java/lang/Float") \ template(java_lang_Float, "java/lang/Float") \
template(java_lang_Double, "java/lang/Double") \ template(java_lang_Double, "java/lang/Double") \
template(java_lang_Byte, "java/lang/Byte") \ template(java_lang_Byte, "java/lang/Byte") \
template(java_lang_Byte_Cache, "java/lang/Byte$ByteCache") \
template(java_lang_Short, "java/lang/Short") \ template(java_lang_Short, "java/lang/Short") \
template(java_lang_Short_ShortCache, "java/lang/Short$ShortCache") \
template(java_lang_Integer, "java/lang/Integer") \ template(java_lang_Integer, "java/lang/Integer") \
template(java_lang_Integer_IntegerCache, "java/lang/Integer$IntegerCache") \
template(java_lang_Long, "java/lang/Long") \ template(java_lang_Long, "java/lang/Long") \
template(java_lang_Long_LongCache, "java/lang/Long$LongCache") \
template(java_lang_Shutdown, "java/lang/Shutdown") \ template(java_lang_Shutdown, "java/lang/Shutdown") \
template(java_lang_ref_Reference, "java/lang/ref/Reference") \ template(java_lang_ref_Reference, "java/lang/ref/Reference") \
template(java_lang_ref_SoftReference, "java/lang/ref/SoftReference") \ template(java_lang_ref_SoftReference, "java/lang/ref/SoftReference") \
@ -274,6 +279,7 @@
template(exclusive_owner_thread_name, "exclusiveOwnerThread") \ template(exclusive_owner_thread_name, "exclusiveOwnerThread") \
template(park_blocker_name, "parkBlocker") \ template(park_blocker_name, "parkBlocker") \
template(park_event_name, "nativeParkEventPointer") \ template(park_event_name, "nativeParkEventPointer") \
template(cache_field_name, "cache") \
template(value_name, "value") \ template(value_name, "value") \
\ \
/* non-intrinsic name/signature pairs: */ \ /* non-intrinsic name/signature pairs: */ \

View File

@ -720,6 +720,11 @@ ciObjArray.hpp ciArray.hpp
ciObjArray.hpp ciClassList.hpp ciObjArray.hpp ciClassList.hpp
ciObjArray.hpp objArrayOop.hpp ciObjArray.hpp objArrayOop.hpp
ciObjArray.cpp ciObjArray.hpp
ciObjArray.cpp ciNullObject.hpp
ciObjArray.cpp ciUtilities.hpp
ciObjArray.cpp objArrayOop.hpp
ciObjArrayKlass.cpp ciInstanceKlass.hpp ciObjArrayKlass.cpp ciInstanceKlass.hpp
ciObjArrayKlass.cpp ciObjArrayKlass.hpp ciObjArrayKlass.cpp ciObjArrayKlass.hpp
ciObjArrayKlass.cpp ciObjArrayKlassKlass.hpp ciObjArrayKlass.cpp ciObjArrayKlassKlass.hpp

View File

@ -608,6 +608,28 @@ Node* AddPNode::Ideal_base_and_offset(Node* ptr, PhaseTransform* phase,
return NULL; return NULL;
} }
//------------------------------unpack_offsets----------------------------------
// Collect the AddP offset values into the elements array, giving up
// if there are more than length.
int AddPNode::unpack_offsets(Node* elements[], int length) {
int count = 0;
Node* addr = this;
Node* base = addr->in(AddPNode::Base);
while (addr->is_AddP()) {
if (addr->in(AddPNode::Base) != base) {
// give up
return -1;
}
elements[count++] = addr->in(AddPNode::Offset);
if (count == length) {
// give up
return -1;
}
addr = addr->in(AddPNode::Address);
}
return count;
}
//------------------------------match_edge------------------------------------- //------------------------------match_edge-------------------------------------
// Do we Match on this edge index or not? Do not match base pointer edge // Do we Match on this edge index or not? Do not match base pointer edge
uint AddPNode::match_edge(uint idx) const { uint AddPNode::match_edge(uint idx) const {

View File

@ -144,6 +144,11 @@ public:
static Node* Ideal_base_and_offset(Node* ptr, PhaseTransform* phase, static Node* Ideal_base_and_offset(Node* ptr, PhaseTransform* phase,
// second return value: // second return value:
intptr_t& offset); intptr_t& offset);
// Collect the AddP offset values into the elements array, giving up
// if there are more than length.
int unpack_offsets(Node* elements[], int length);
// Do not match base-ptr edge // Do not match base-ptr edge
virtual uint match_edge(uint idx) const; virtual uint match_edge(uint idx) const;
static const Type *mach_bottom_type(const MachNode* n); // used by ad_<arch>.hpp static const Type *mach_bottom_type(const MachNode* n); // used by ad_<arch>.hpp

View File

@ -367,6 +367,12 @@
notproduct(bool, PrintEliminateLocks, false, \ notproduct(bool, PrintEliminateLocks, false, \
"Print out when locks are eliminated") \ "Print out when locks are eliminated") \
\ \
diagnostic(bool, EliminateAutoBox, false, \
"Private flag to control optimizations for autobox elimination") \
\
product(intx, AutoBoxCacheMax, 128, \
"Sets max value cached by the java.lang.Integer autobox cache") \
\
product(bool, DoEscapeAnalysis, false, \ product(bool, DoEscapeAnalysis, false, \
"Perform escape analysis") \ "Perform escape analysis") \
\ \

View File

@ -310,8 +310,14 @@ public:
virtual const RegMask &out_RegMask() const; virtual const RegMask &out_RegMask() const;
void dominated_by(Node* prev_dom, PhaseIterGVN* igvn); void dominated_by(Node* prev_dom, PhaseIterGVN* igvn);
int is_range_check(Node* &range, Node* &index, jint &offset); int is_range_check(Node* &range, Node* &index, jint &offset);
Node* fold_compares(PhaseGVN* phase);
static Node* up_one_dom(Node* curr, bool linear_only = false); static Node* up_one_dom(Node* curr, bool linear_only = false);
// Takes the type of val and filters it through the test represented
// by if_proj and returns a more refined type if one is produced.
// Returns NULL is it couldn't improve the type.
static const TypeInt* filtered_int_type(PhaseGVN* phase, Node* val, Node* if_proj);
#ifndef PRODUCT #ifndef PRODUCT
virtual void dump_spec(outputStream *st) const; virtual void dump_spec(outputStream *st) const;
#endif #endif

View File

@ -543,6 +543,159 @@ Node* IfNode::up_one_dom(Node *curr, bool linear_only) {
return NULL; // Dead loop? Or hit root? return NULL; // Dead loop? Or hit root?
} }
//------------------------------filtered_int_type--------------------------------
// Return a possibly more restrictive type for val based on condition control flow for an if
const TypeInt* IfNode::filtered_int_type(PhaseGVN* gvn, Node *val, Node* if_proj) {
assert(if_proj &&
(if_proj->Opcode() == Op_IfTrue || if_proj->Opcode() == Op_IfFalse), "expecting an if projection");
if (if_proj->in(0) && if_proj->in(0)->is_If()) {
IfNode* iff = if_proj->in(0)->as_If();
if (iff->in(1) && iff->in(1)->is_Bool()) {
BoolNode* bol = iff->in(1)->as_Bool();
if (bol->in(1) && bol->in(1)->is_Cmp()) {
const CmpNode* cmp = bol->in(1)->as_Cmp();
if (cmp->in(1) == val) {
const TypeInt* cmp2_t = gvn->type(cmp->in(2))->isa_int();
if (cmp2_t != NULL) {
jint lo = cmp2_t->_lo;
jint hi = cmp2_t->_hi;
BoolTest::mask msk = if_proj->Opcode() == Op_IfTrue ? bol->_test._test : bol->_test.negate();
switch (msk) {
case BoolTest::ne:
// Can't refine type
return NULL;
case BoolTest::eq:
return cmp2_t;
case BoolTest::lt:
lo = TypeInt::INT->_lo;
if (hi - 1 < hi) {
hi = hi - 1;
}
break;
case BoolTest::le:
lo = TypeInt::INT->_lo;
break;
case BoolTest::gt:
if (lo + 1 > lo) {
lo = lo + 1;
}
hi = TypeInt::INT->_hi;
break;
case BoolTest::ge:
// lo unchanged
hi = TypeInt::INT->_hi;
break;
}
const TypeInt* rtn_t = TypeInt::make(lo, hi, cmp2_t->_widen);
return rtn_t;
}
}
}
}
}
return NULL;
}
//------------------------------fold_compares----------------------------
// See if a pair of CmpIs can be converted into a CmpU. In some cases
// the direction of this if is determined by the preciding if so it
// can be eliminate entirely. Given an if testing (CmpI n c) check
// for an immediately control dependent if that is testing (CmpI n c2)
// and has one projection leading to this if and the other projection
// leading to a region that merges one of this ifs control
// projections.
//
// If
// / |
// / |
// / |
// If |
// /\ |
// / \ |
// / \ |
// / Region
//
Node* IfNode::fold_compares(PhaseGVN* phase) {
if (!EliminateAutoBox || Opcode() != Op_If) return NULL;
Node* this_cmp = in(1)->in(1);
if (this_cmp != NULL && this_cmp->Opcode() == Op_CmpI &&
this_cmp->in(2)->is_Con() && this_cmp->in(2) != phase->C->top()) {
Node* ctrl = in(0);
BoolNode* this_bool = in(1)->as_Bool();
Node* n = this_cmp->in(1);
int hi = this_cmp->in(2)->get_int();
if (ctrl != NULL && ctrl->is_Proj() && ctrl->outcnt() == 1 &&
ctrl->in(0)->is_If() &&
ctrl->in(0)->outcnt() == 2 &&
ctrl->in(0)->in(1)->is_Bool() &&
ctrl->in(0)->in(1)->in(1)->Opcode() == Op_CmpI &&
ctrl->in(0)->in(1)->in(1)->in(2)->is_Con() &&
ctrl->in(0)->in(1)->in(1)->in(1) == n) {
IfNode* dom_iff = ctrl->in(0)->as_If();
Node* otherproj = dom_iff->proj_out(!ctrl->as_Proj()->_con);
if (otherproj->outcnt() == 1 && otherproj->unique_out()->is_Region() &&
this_bool->_test._test != BoolTest::ne && this_bool->_test._test != BoolTest::eq) {
// Identify which proj goes to the region and which continues on
RegionNode* region = otherproj->unique_out()->as_Region();
Node* success = NULL;
Node* fail = NULL;
for (int i = 0; i < 2; i++) {
Node* proj = proj_out(i);
if (success == NULL && proj->outcnt() == 1 && proj->unique_out() == region) {
success = proj;
} else if (fail == NULL) {
fail = proj;
} else {
success = fail = NULL;
}
}
if (success != NULL && fail != NULL && !region->has_phi()) {
int lo = dom_iff->in(1)->in(1)->in(2)->get_int();
BoolNode* dom_bool = dom_iff->in(1)->as_Bool();
Node* dom_cmp = dom_bool->in(1);
const TypeInt* failtype = filtered_int_type(phase, n, ctrl);
if (failtype != NULL) {
const TypeInt* type2 = filtered_int_type(phase, n, fail);
if (type2 != NULL) {
failtype = failtype->join(type2)->is_int();
} else {
failtype = NULL;
}
}
if (failtype != NULL &&
dom_bool->_test._test != BoolTest::ne && dom_bool->_test._test != BoolTest::eq) {
int bound = failtype->_hi - failtype->_lo + 1;
if (failtype->_hi != max_jint && failtype->_lo != min_jint && bound > 1) {
// Merge the two compares into a single unsigned compare by building (CmpU (n - lo) hi)
BoolTest::mask cond = fail->as_Proj()->_con ? BoolTest::lt : BoolTest::ge;
Node* adjusted = phase->transform(new (phase->C, 3) SubINode(n, phase->intcon(failtype->_lo)));
Node* newcmp = phase->transform(new (phase->C, 3) CmpUNode(adjusted, phase->intcon(bound)));
Node* newbool = phase->transform(new (phase->C, 2) BoolNode(newcmp, cond));
phase->hash_delete(dom_iff);
dom_iff->set_req(1, phase->intcon(ctrl->as_Proj()->_con));
phase->is_IterGVN()->_worklist.push(dom_iff);
phase->hash_delete(this);
set_req(1, newbool);
return this;
}
if (failtype->_lo > failtype->_hi) {
// previous if determines the result of this if so
// replace Bool with constant
phase->hash_delete(this);
set_req(1, phase->intcon(success->as_Proj()->_con));
return this;
}
}
}
}
}
}
return NULL;
}
//------------------------------remove_useless_bool---------------------------- //------------------------------remove_useless_bool----------------------------
// Check for people making a useless boolean: things like // Check for people making a useless boolean: things like
// if( (x < y ? true : false) ) { ... } // if( (x < y ? true : false) ) { ... }
@ -744,6 +897,11 @@ Node *IfNode::Ideal(PhaseGVN *phase, bool can_reshape) {
// Normal equivalent-test check. // Normal equivalent-test check.
if( !dom ) return NULL; // Dead loop? if( !dom ) return NULL; // Dead loop?
Node* result = fold_compares(phase);
if (result != NULL) {
return result;
}
// Search up the dominator tree for an If with an identical test // Search up the dominator tree for an If with an identical test
while( dom->Opcode() != op || // Not same opcode? while( dom->Opcode() != op || // Not same opcode?
dom->in(1) != in(1) || // Not same input 1? dom->in(1) != in(1) || // Not same input 1?

View File

@ -651,7 +651,7 @@ const TypeInt* PhaseIdealLoop::filtered_type_from_dominators( Node* val, Node *u
while (if_cnt < if_limit) { while (if_cnt < if_limit) {
if ((pred->Opcode() == Op_IfTrue || pred->Opcode() == Op_IfFalse)) { if ((pred->Opcode() == Op_IfTrue || pred->Opcode() == Op_IfFalse)) {
if_cnt++; if_cnt++;
const TypeInt* if_t = filtered_type_at_if(val, pred); const TypeInt* if_t = IfNode::filtered_int_type(&_igvn, val, pred);
if (if_t != NULL) { if (if_t != NULL) {
if (rtn_t == NULL) { if (rtn_t == NULL) {
rtn_t = if_t; rtn_t = if_t;
@ -674,59 +674,6 @@ const TypeInt* PhaseIdealLoop::filtered_type_from_dominators( Node* val, Node *u
} }
//------------------------------filtered_type_at_if--------------------------------
// Return a possibly more restrictive type for val based on condition control flow for an if
const TypeInt* PhaseIdealLoop::filtered_type_at_if( Node* val, Node *if_proj) {
assert(if_proj &&
(if_proj->Opcode() == Op_IfTrue || if_proj->Opcode() == Op_IfFalse), "expecting an if projection");
if (if_proj->in(0) && if_proj->in(0)->is_If()) {
IfNode* iff = if_proj->in(0)->as_If();
if (iff->in(1) && iff->in(1)->is_Bool()) {
BoolNode* bol = iff->in(1)->as_Bool();
if (bol->in(1) && bol->in(1)->is_Cmp()) {
const CmpNode* cmp = bol->in(1)->as_Cmp();
if (cmp->in(1) == val) {
const TypeInt* cmp2_t = _igvn.type(cmp->in(2))->isa_int();
if (cmp2_t != NULL) {
jint lo = cmp2_t->_lo;
jint hi = cmp2_t->_hi;
BoolTest::mask msk = if_proj->Opcode() == Op_IfTrue ? bol->_test._test : bol->_test.negate();
switch (msk) {
case BoolTest::ne:
// Can't refine type
return NULL;
case BoolTest::eq:
return cmp2_t;
case BoolTest::lt:
lo = TypeInt::INT->_lo;
if (hi - 1 < hi) {
hi = hi - 1;
}
break;
case BoolTest::le:
lo = TypeInt::INT->_lo;
break;
case BoolTest::gt:
if (lo + 1 > lo) {
lo = lo + 1;
}
hi = TypeInt::INT->_hi;
break;
case BoolTest::ge:
// lo unchanged
hi = TypeInt::INT->_hi;
break;
}
const TypeInt* rtn_t = TypeInt::make(lo, hi, cmp2_t->_widen);
return rtn_t;
}
}
}
}
}
return NULL;
}
//------------------------------dump_spec-------------------------------------- //------------------------------dump_spec--------------------------------------
// Dump special per-node info // Dump special per-node info
#ifndef PRODUCT #ifndef PRODUCT

View File

@ -850,7 +850,6 @@ private:
const TypeInt* filtered_type( Node *n ) { return filtered_type(n, NULL); } const TypeInt* filtered_type( Node *n ) { return filtered_type(n, NULL); }
// Helpers for filtered type // Helpers for filtered type
const TypeInt* filtered_type_from_dominators( Node* val, Node *val_ctrl); const TypeInt* filtered_type_from_dominators( Node* val, Node *val_ctrl);
const TypeInt* filtered_type_at_if( Node* val, Node *if_proj);
// Helper functions // Helper functions
void register_new_node( Node *n, Node *blk ); void register_new_node( Node *n, Node *blk );

View File

@ -634,6 +634,46 @@ uint LoadNode::hash() const {
Node* MemNode::can_see_stored_value(Node* st, PhaseTransform* phase) const { Node* MemNode::can_see_stored_value(Node* st, PhaseTransform* phase) const {
Node* ld_adr = in(MemNode::Address); 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. // Loop around twice in the case Load -> Initialize -> Store.
// (See PhaseIterGVN::add_users_to_worklist, which knows about this case.) // (See PhaseIterGVN::add_users_to_worklist, which knows about this case.)
for (int trip = 0; trip <= 1; trip++) { for (int trip = 0; trip <= 1; trip++) {
@ -723,6 +763,168 @@ Node *LoadNode::Identity( PhaseTransform *phase ) {
return this; 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------------------------------------------ //------------------------------Ideal------------------------------------------
// If the load is from Field memory and the pointer is non-null, we can // If the load is from Field memory and the pointer is non-null, we can
// zero out the control input. // 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 // Check for prior store with a different base or offset; make Load
// independent. Skip through any number of them. Bail out if the stores // 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 // 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. // This can happen if a interface-typed array narrows to a class type.
jt = _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; return jt;
} }
} }

View File

@ -141,6 +141,9 @@ public:
// zero out the control input. // zero out the control input.
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
// Recover original value from boxed values
Node *eliminate_autobox(PhaseGVN *phase);
// Compute a new Type for this node. Basically we just do the pre-check, // Compute a new Type for this node. Basically we just do the pre-check,
// then call the virtual add() to set the type. // then call the virtual add() to set the type.
virtual const Type *Value( PhaseTransform *phase ) const; virtual const Type *Value( PhaseTransform *phase ) const;

View File

@ -885,6 +885,9 @@ inline void Parse::repush_if_args() {
void Parse::do_ifnull(BoolTest::mask btest) { void Parse::do_ifnull(BoolTest::mask btest) {
int target_bci = iter().get_dest(); int target_bci = iter().get_dest();
Block* branch_block = successor_for_bci(target_bci);
Block* next_block = successor_for_bci(iter().next_bci());
float cnt; float cnt;
float prob = branch_prediction(cnt, btest, target_bci); float prob = branch_prediction(cnt, btest, target_bci);
if (prob == PROB_UNKNOWN) { if (prob == PROB_UNKNOWN) {
@ -902,13 +905,16 @@ void Parse::do_ifnull(BoolTest::mask btest) {
uncommon_trap(Deoptimization::Reason_unreached, uncommon_trap(Deoptimization::Reason_unreached,
Deoptimization::Action_reinterpret, Deoptimization::Action_reinterpret,
NULL, "cold"); NULL, "cold");
if (EliminateAutoBox) {
// Mark the successor blocks as parsed
branch_block->next_path_num();
next_block->next_path_num();
}
return; return;
} }
// If this is a backwards branch in the bytecodes, add Safepoint // If this is a backwards branch in the bytecodes, add Safepoint
maybe_add_safepoint(target_bci); maybe_add_safepoint(target_bci);
Block* branch_block = successor_for_bci(target_bci);
Block* next_block = successor_for_bci(iter().next_bci());
explicit_null_checks_inserted++; explicit_null_checks_inserted++;
Node* a = null(); Node* a = null();
@ -935,6 +941,10 @@ void Parse::do_ifnull(BoolTest::mask btest) {
if (stopped()) { // Path is dead? if (stopped()) { // Path is dead?
explicit_null_checks_elided++; explicit_null_checks_elided++;
if (EliminateAutoBox) {
// Mark the successor block as parsed
branch_block->next_path_num();
}
} else { // Path is live. } else { // Path is live.
// Update method data // Update method data
profile_taken_branch(target_bci); profile_taken_branch(target_bci);
@ -950,6 +960,10 @@ void Parse::do_ifnull(BoolTest::mask btest) {
if (stopped()) { // Path is dead? if (stopped()) { // Path is dead?
explicit_null_checks_elided++; explicit_null_checks_elided++;
if (EliminateAutoBox) {
// Mark the successor block as parsed
next_block->next_path_num();
}
} else { // Path is live. } else { // Path is live.
// Update method data // Update method data
profile_not_taken_branch(); profile_not_taken_branch();
@ -962,6 +976,9 @@ void Parse::do_ifnull(BoolTest::mask btest) {
void Parse::do_if(BoolTest::mask btest, Node* c) { void Parse::do_if(BoolTest::mask btest, Node* c) {
int target_bci = iter().get_dest(); int target_bci = iter().get_dest();
Block* branch_block = successor_for_bci(target_bci);
Block* next_block = successor_for_bci(iter().next_bci());
float cnt; float cnt;
float prob = branch_prediction(cnt, btest, target_bci); float prob = branch_prediction(cnt, btest, target_bci);
float untaken_prob = 1.0 - prob; float untaken_prob = 1.0 - prob;
@ -980,6 +997,11 @@ void Parse::do_if(BoolTest::mask btest, Node* c) {
uncommon_trap(Deoptimization::Reason_unreached, uncommon_trap(Deoptimization::Reason_unreached,
Deoptimization::Action_reinterpret, Deoptimization::Action_reinterpret,
NULL, "cold"); NULL, "cold");
if (EliminateAutoBox) {
// Mark the successor blocks as parsed
branch_block->next_path_num();
next_block->next_path_num();
}
return; return;
} }
@ -1018,15 +1040,17 @@ void Parse::do_if(BoolTest::mask btest, Node* c) {
untaken_branch = tmp; untaken_branch = tmp;
} }
Block* branch_block = successor_for_bci(target_bci);
Block* next_block = successor_for_bci(iter().next_bci());
// Branch is taken: // Branch is taken:
{ PreserveJVMState pjvms(this); { PreserveJVMState pjvms(this);
taken_branch = _gvn.transform(taken_branch); taken_branch = _gvn.transform(taken_branch);
set_control(taken_branch); set_control(taken_branch);
if (!stopped()) { if (stopped()) {
if (EliminateAutoBox) {
// Mark the successor block as parsed
branch_block->next_path_num();
}
} else {
// Update method data // Update method data
profile_taken_branch(target_bci); profile_taken_branch(target_bci);
adjust_map_after_if(taken_btest, c, prob, branch_block, next_block); adjust_map_after_if(taken_btest, c, prob, branch_block, next_block);
@ -1039,7 +1063,12 @@ void Parse::do_if(BoolTest::mask btest, Node* c) {
set_control(untaken_branch); set_control(untaken_branch);
// Branch not taken. // Branch not taken.
if (!stopped()) { if (stopped()) {
if (EliminateAutoBox) {
// Mark the successor block as parsed
next_block->next_path_num();
}
} else {
// Update method data // Update method data
profile_not_taken_branch(); profile_not_taken_branch();
adjust_map_after_if(untaken_btest, c, untaken_prob, adjust_map_after_if(untaken_btest, c, untaken_prob,

View File

@ -1070,6 +1070,7 @@ inline bool Type::is_floatingpoint() const {
#define LShiftXNode LShiftLNode #define LShiftXNode LShiftLNode
// For object size computation: // For object size computation:
#define AddXNode AddLNode #define AddXNode AddLNode
#define RShiftXNode RShiftLNode
// For card marks and hashcodes // For card marks and hashcodes
#define URShiftXNode URShiftLNode #define URShiftXNode URShiftLNode
// Opcodes // Opcodes
@ -1108,6 +1109,7 @@ inline bool Type::is_floatingpoint() const {
#define LShiftXNode LShiftINode #define LShiftXNode LShiftINode
// For object size computation: // For object size computation:
#define AddXNode AddINode #define AddXNode AddINode
#define RShiftXNode RShiftINode
// For card marks and hashcodes // For card marks and hashcodes
#define URShiftXNode URShiftINode #define URShiftXNode URShiftINode
// Opcodes // Opcodes

View File

@ -1254,6 +1254,22 @@ void Arguments::set_bytecode_flags() {
// Aggressive optimization flags -XX:+AggressiveOpts // Aggressive optimization flags -XX:+AggressiveOpts
void Arguments::set_aggressive_opts_flags() { void Arguments::set_aggressive_opts_flags() {
#ifdef COMPILER2
if (AggressiveOpts || !FLAG_IS_DEFAULT(AutoBoxCacheMax)) {
if (FLAG_IS_DEFAULT(EliminateAutoBox)) {
FLAG_SET_DEFAULT(EliminateAutoBox, true);
}
if (FLAG_IS_DEFAULT(AutoBoxCacheMax)) {
FLAG_SET_DEFAULT(AutoBoxCacheMax, 20000);
}
// Feed the cache size setting into the JDK
char buffer[1024];
sprintf(buffer, "java.lang.Integer.IntegerCache.high=%d", AutoBoxCacheMax);
add_property(buffer);
}
#endif
if (AggressiveOpts) { if (AggressiveOpts) {
NOT_WINDOWS( NOT_WINDOWS(
// No measured benefit on Windows // No measured benefit on Windows