This commit is contained in:
Chuck Rasbold 2008-03-21 08:32:17 -07:00
commit 446895de2d
45 changed files with 3514 additions and 759 deletions

@ -35,7 +35,7 @@ HOTSPOT_VM_COPYRIGHT=Copyright 2007
HS_MAJOR_VER=12
HS_MINOR_VER=0
HS_BUILD_NUMBER=01
HS_BUILD_NUMBER=02
JDK_MAJOR_VER=1
JDK_MINOR_VER=7

@ -2672,6 +2672,22 @@ void Assembler::movlpd(XMMRegister dst, Address src) {
emit_sse_operand(dst, src);
}
void Assembler::cvtdq2pd(XMMRegister dst, XMMRegister src) {
assert(VM_Version::supports_sse2(), "");
emit_byte(0xF3);
emit_byte(0x0F);
emit_byte(0xE6);
emit_sse_operand(dst, src);
}
void Assembler::cvtdq2ps(XMMRegister dst, XMMRegister src) {
assert(VM_Version::supports_sse2(), "");
emit_byte(0x0F);
emit_byte(0x5B);
emit_sse_operand(dst, src);
}
emit_sse_instruction(andps, sse, 0, 0x54, XMMRegister, XMMRegister);
emit_sse_instruction(andpd, sse2, 0x66, 0x54, XMMRegister, XMMRegister);

@ -901,6 +901,8 @@ class Assembler : public AbstractAssembler {
void cvtss2sd(XMMRegister dst, XMMRegister src);
void cvtsd2ss(XMMRegister dst, Address src); // Convert Scalar Double-Precision Floating-Point Value to Scalar Single-Precision Floating-Point Value
void cvtsd2ss(XMMRegister dst, XMMRegister src);
void cvtdq2pd(XMMRegister dst, XMMRegister src);
void cvtdq2ps(XMMRegister dst, XMMRegister src);
void cvtsi2ss(XMMRegister dst, Address src); // Convert Doubleword Integer to Scalar Single-Precision Floating-Point Value
void cvtsi2ss(XMMRegister dst, Register src);

@ -3372,6 +3372,21 @@ void Assembler::cvtss2sd(XMMRegister dst, XMMRegister src) {
emit_byte(0xC0 | encode);
}
void Assembler::cvtdq2pd(XMMRegister dst, XMMRegister src) {
emit_byte(0xF3);
int encode = prefix_and_encode(dst->encoding(), src->encoding());
emit_byte(0x0F);
emit_byte(0xE6);
emit_byte(0xC0 | encode);
}
void Assembler::cvtdq2ps(XMMRegister dst, XMMRegister src) {
int encode = prefix_and_encode(dst->encoding(), src->encoding());
emit_byte(0x0F);
emit_byte(0x5B);
emit_byte(0xC0 | encode);
}
void Assembler::cvtsd2ss(XMMRegister dst, XMMRegister src) {
emit_byte(0xF2);
int encode = prefix_and_encode(dst->encoding(), src->encoding());

@ -922,6 +922,8 @@ class Assembler : public AbstractAssembler {
void cvttsd2siq(Register dst, XMMRegister src); // truncates
void cvtss2sd(XMMRegister dst, XMMRegister src);
void cvtsd2ss(XMMRegister dst, XMMRegister src);
void cvtdq2pd(XMMRegister dst, XMMRegister src);
void cvtdq2ps(XMMRegister dst, XMMRegister src);
void pxor(XMMRegister dst, Address src); // Xor Packed Byte Integer Values
void pxor(XMMRegister dst, XMMRegister src); // Xor Packed Byte Integer Values

@ -321,6 +321,20 @@ void VM_Version::get_processor_features() {
UseXmmRegToRegMoveAll = false;
}
}
if( FLAG_IS_DEFAULT(UseXmmI2F) ) {
if( supports_sse4a() ) {
UseXmmI2F = true;
} else {
UseXmmI2F = false;
}
}
if( FLAG_IS_DEFAULT(UseXmmI2D) ) {
if( supports_sse4a() ) {
UseXmmI2D = true;
} else {
UseXmmI2D = false;
}
}
}
if( is_intel() ) { // Intel cpus specific settings

@ -265,6 +265,20 @@ void VM_Version::get_processor_features() {
UseXmmRegToRegMoveAll = false;
}
}
if( FLAG_IS_DEFAULT(UseXmmI2F) ) {
if( supports_sse4a() ) {
UseXmmI2F = true;
} else {
UseXmmI2F = false;
}
}
if( FLAG_IS_DEFAULT(UseXmmI2D) ) {
if( supports_sse4a() ) {
UseXmmI2D = true;
} else {
UseXmmI2D = false;
}
}
}
if( is_intel() ) { // Intel cpus specific settings

@ -10970,7 +10970,7 @@ instruct convI2D_reg(regD dst, stackSlotI src) %{
%}
instruct convI2XD_reg(regXD dst, eRegI src) %{
predicate( UseSSE>=2 );
predicate( UseSSE>=2 && !UseXmmI2D );
match(Set dst (ConvI2D src));
format %{ "CVTSI2SD $dst,$src" %}
opcode(0xF2, 0x0F, 0x2A);
@ -10987,6 +10987,20 @@ instruct convI2XD_mem(regXD dst, memory mem) %{
ins_pipe( pipe_slow );
%}
instruct convXI2XD_reg(regXD dst, eRegI src)
%{
predicate( UseSSE>=2 && UseXmmI2D );
match(Set dst (ConvI2D src));
format %{ "MOVD $dst,$src\n\t"
"CVTDQ2PD $dst,$dst\t# i2d" %}
ins_encode %{
__ movd($dst$$XMMRegister, $src$$Register);
__ cvtdq2pd($dst$$XMMRegister, $dst$$XMMRegister);
%}
ins_pipe(pipe_slow); // XXX
%}
instruct convI2D_mem(regD dst, memory mem) %{
predicate( UseSSE<=1 && !Compile::current()->select_24_bit_instr());
match(Set dst (ConvI2D (LoadI mem)));
@ -11062,7 +11076,7 @@ instruct convI2F_mem(regF dst, memory mem) %{
// Convert an int to a float in xmm; no rounding step needed.
instruct convI2X_reg(regX dst, eRegI src) %{
predicate(UseSSE>=1);
predicate( UseSSE==1 || UseSSE>=2 && !UseXmmI2F );
match(Set dst (ConvI2F src));
format %{ "CVTSI2SS $dst, $src" %}
@ -11071,6 +11085,20 @@ instruct convI2X_reg(regX dst, eRegI src) %{
ins_pipe( pipe_slow );
%}
instruct convXI2X_reg(regX dst, eRegI src)
%{
predicate( UseSSE>=2 && UseXmmI2F );
match(Set dst (ConvI2F src));
format %{ "MOVD $dst,$src\n\t"
"CVTDQ2PS $dst,$dst\t# i2f" %}
ins_encode %{
__ movd($dst$$XMMRegister, $src$$Register);
__ cvtdq2ps($dst$$XMMRegister, $dst$$XMMRegister);
%}
ins_pipe(pipe_slow); // XXX
%}
instruct convI2L_reg( eRegL dst, eRegI src, eFlagsReg cr) %{
match(Set dst (ConvI2L src));
effect(KILL cr);

@ -10098,6 +10098,7 @@ instruct convD2L_reg_reg(rRegL dst, regD src, rFlagsReg cr)
instruct convI2F_reg_reg(regF dst, rRegI src)
%{
predicate(!UseXmmI2F);
match(Set dst (ConvI2F src));
format %{ "cvtsi2ssl $dst, $src\t# i2f" %}
@ -10118,6 +10119,7 @@ instruct convI2F_reg_mem(regF dst, memory src)
instruct convI2D_reg_reg(regD dst, rRegI src)
%{
predicate(!UseXmmI2D);
match(Set dst (ConvI2D src));
format %{ "cvtsi2sdl $dst, $src\t# i2d" %}
@ -10136,6 +10138,34 @@ instruct convI2D_reg_mem(regD dst, memory src)
ins_pipe(pipe_slow); // XXX
%}
instruct convXI2F_reg(regF dst, rRegI src)
%{
predicate(UseXmmI2F);
match(Set dst (ConvI2F src));
format %{ "movdl $dst, $src\n\t"
"cvtdq2psl $dst, $dst\t# i2f" %}
ins_encode %{
__ movdl($dst$$XMMRegister, $src$$Register);
__ cvtdq2ps($dst$$XMMRegister, $dst$$XMMRegister);
%}
ins_pipe(pipe_slow); // XXX
%}
instruct convXI2D_reg(regD dst, rRegI src)
%{
predicate(UseXmmI2D);
match(Set dst (ConvI2D src));
format %{ "movdl $dst, $src\n\t"
"cvtdq2pdl $dst, $dst\t# i2d" %}
ins_encode %{
__ movdl($dst$$XMMRegister, $src$$Register);
__ cvtdq2pd($dst$$XMMRegister, $dst$$XMMRegister);
%}
ins_pipe(pipe_slow); // XXX
%}
instruct convL2F_reg_reg(regF dst, rRegL src)
%{
match(Set dst (ConvL2F src));

@ -164,6 +164,7 @@ callGenerator.hpp deoptimization.hpp
callGenerator.hpp type.hpp
callnode.cpp callnode.hpp
callnode.cpp bcEscapeAnalyzer.hpp
callnode.cpp escape.hpp
callnode.cpp locknode.hpp
callnode.cpp machnode.hpp
@ -176,7 +177,6 @@ callnode.cpp rootnode.hpp
callnode.cpp runtime.hpp
callnode.hpp connode.hpp
callnode.hpp escape.hpp
callnode.hpp mulnode.hpp
callnode.hpp multnode.hpp
callnode.hpp opcodes.hpp
@ -347,7 +347,6 @@ connode.cpp addnode.hpp
connode.cpp allocation.inline.hpp
connode.cpp compile.hpp
connode.cpp connode.hpp
connode.cpp escape.hpp
connode.cpp machnode.hpp
connode.cpp matcher.hpp
connode.cpp memnode.hpp
@ -844,7 +843,6 @@ phaseX.cpp block.hpp
phaseX.cpp callnode.hpp
phaseX.cpp cfgnode.hpp
phaseX.cpp connode.hpp
phaseX.cpp escape.hpp
phaseX.cpp loopnode.hpp
phaseX.cpp machnode.hpp
phaseX.cpp opcodes.hpp

@ -382,6 +382,12 @@
product(bool, EliminateAllocations, true, \
"Use escape analysis to eliminate allocations") \
\
notproduct(bool, PrintEliminateAllocations, false, \
"Print out when allocations are eliminated") \
\
product(intx, EliminateAllocationArraySizeLimit, 64, \
"Array size (number of elements) limit for scalar replacement") \
\
product(intx, MaxLabelRootDepth, 1100, \
"Maximum times call Label_Root to prevent stack overflow") \

@ -230,6 +230,7 @@ JVMState::JVMState(ciMethod* method, JVMState* caller) {
_locoff = TypeFunc::Parms;
_stkoff = _locoff + _method->max_locals();
_monoff = _stkoff + _method->max_stack();
_scloff = _monoff;
_endoff = _monoff;
_sp = 0;
}
@ -242,6 +243,7 @@ JVMState::JVMState(int stack_size) {
_locoff = TypeFunc::Parms;
_stkoff = _locoff;
_monoff = _stkoff + stack_size;
_scloff = _monoff;
_endoff = _monoff;
_sp = 0;
}
@ -297,12 +299,22 @@ uint JVMState::debug_depth() const {
return total;
}
#ifndef PRODUCT
//------------------------------format_helper----------------------------------
// Given an allocation (a Chaitin object) and a Node decide if the Node carries
// any defined value or not. If it does, print out the register or constant.
#ifndef PRODUCT
static void format_helper( PhaseRegAlloc *regalloc, outputStream* st, Node *n, const char *msg, uint i ) {
static void format_helper( PhaseRegAlloc *regalloc, outputStream* st, Node *n, const char *msg, uint i, GrowableArray<SafePointScalarObjectNode*> *scobjs ) {
if (n == NULL) { st->print(" NULL"); return; }
if (n->is_SafePointScalarObject()) {
// Scalar replacement.
SafePointScalarObjectNode* spobj = n->as_SafePointScalarObject();
scobjs->append_if_missing(spobj);
int sco_n = scobjs->find(spobj);
assert(sco_n >= 0, "");
st->print(" %s%d]=#ScObj" INT32_FORMAT, msg, i, sco_n);
return;
}
if( OptoReg::is_valid(regalloc->get_reg_first(n))) { // Check for undefined
char buf[50];
regalloc->dump_register(n,buf);
@ -342,10 +354,8 @@ static void format_helper( PhaseRegAlloc *regalloc, outputStream* st, Node *n, c
}
}
}
#endif
//------------------------------format-----------------------------------------
#ifndef PRODUCT
void JVMState::format(PhaseRegAlloc *regalloc, const Node *n, outputStream* st) const {
st->print(" #");
if( _method ) {
@ -356,24 +366,25 @@ void JVMState::format(PhaseRegAlloc *regalloc, const Node *n, outputStream* st)
return;
}
if (n->is_MachSafePoint()) {
GrowableArray<SafePointScalarObjectNode*> scobjs;
MachSafePointNode *mcall = n->as_MachSafePoint();
uint i;
// Print locals
for( i = 0; i < (uint)loc_size(); i++ )
format_helper( regalloc, st, mcall->local(this, i), "L[", i );
format_helper( regalloc, st, mcall->local(this, i), "L[", i, &scobjs );
// Print stack
for (i = 0; i < (uint)stk_size(); i++) {
if ((uint)(_stkoff + i) >= mcall->len())
st->print(" oob ");
else
format_helper( regalloc, st, mcall->stack(this, i), "STK[", i );
format_helper( regalloc, st, mcall->stack(this, i), "STK[", i, &scobjs );
}
for (i = 0; (int)i < nof_monitors(); i++) {
Node *box = mcall->monitor_box(this, i);
Node *obj = mcall->monitor_obj(this, i);
if ( OptoReg::is_valid(regalloc->get_reg_first(box)) ) {
while( !box->is_BoxLock() ) box = box->in(1);
format_helper( regalloc, st, box, "MON-BOX[", i );
format_helper( regalloc, st, box, "MON-BOX[", i, &scobjs );
} else {
OptoReg::Name box_reg = BoxLockNode::stack_slot(box);
st->print(" MON-BOX%d=%s+%d",
@ -381,15 +392,71 @@ void JVMState::format(PhaseRegAlloc *regalloc, const Node *n, outputStream* st)
OptoReg::regname(OptoReg::c_frame_pointer),
regalloc->reg2offset(box_reg));
}
format_helper( regalloc, st, obj, "MON-OBJ[", i );
format_helper( regalloc, st, obj, "MON-OBJ[", i, &scobjs );
}
for (i = 0; i < (uint)scobjs.length(); i++) {
// Scalar replaced objects.
st->print_cr("");
st->print(" # ScObj" INT32_FORMAT " ", i);
SafePointScalarObjectNode* spobj = scobjs.at(i);
ciKlass* cik = spobj->bottom_type()->is_oopptr()->klass();
assert(cik->is_instance_klass() ||
cik->is_array_klass(), "Not supported allocation.");
ciInstanceKlass *iklass = NULL;
if (cik->is_instance_klass()) {
cik->print_name_on(st);
iklass = cik->as_instance_klass();
} else if (cik->is_type_array_klass()) {
cik->as_array_klass()->base_element_type()->print_name_on(st);
st->print("[%d]=", spobj->n_fields());
} else if (cik->is_obj_array_klass()) {
ciType* cie = cik->as_array_klass()->base_element_type();
int ndim = 1;
while (cie->is_obj_array_klass()) {
ndim += 1;
cie = cie->as_array_klass()->base_element_type();
}
cie->print_name_on(st);
while (ndim-- > 0) {
st->print("[]");
}
st->print("[%d]=", spobj->n_fields());
}
st->print("{");
uint nf = spobj->n_fields();
if (nf > 0) {
uint first_ind = spobj->first_index();
Node* fld_node = mcall->in(first_ind);
ciField* cifield;
if (iklass != NULL) {
st->print(" [");
cifield = iklass->nonstatic_field_at(0);
cifield->print_name_on(st);
format_helper( regalloc, st, fld_node, ":", 0, &scobjs );
} else {
format_helper( regalloc, st, fld_node, "[", 0, &scobjs );
}
for (uint j = 1; j < nf; j++) {
fld_node = mcall->in(first_ind+j);
if (iklass != NULL) {
st->print(", [");
cifield = iklass->nonstatic_field_at(j);
cifield->print_name_on(st);
format_helper( regalloc, st, fld_node, ":", j, &scobjs );
} else {
format_helper( regalloc, st, fld_node, ", [", j, &scobjs );
}
}
}
st->print(" }");
}
}
st->print_cr("");
if (caller() != NULL) caller()->format(regalloc, n, st);
}
#endif
#ifndef PRODUCT
void JVMState::dump_spec(outputStream *st) const {
if (_method != NULL) {
bool printed = false;
@ -419,9 +486,8 @@ void JVMState::dump_spec(outputStream *st) const {
}
if (caller() != NULL) caller()->dump_spec(st);
}
#endif
#ifndef PRODUCT
void JVMState::dump_on(outputStream* st) const {
if (_map && !((uintptr_t)_map & 1)) {
if (_map->len() > _map->req()) { // _map->has_exceptions()
@ -434,8 +500,8 @@ void JVMState::dump_on(outputStream* st) const {
}
_map->dump(2);
}
st->print("JVMS depth=%d loc=%d stk=%d mon=%d end=%d mondepth=%d sp=%d bci=%d method=",
depth(), locoff(), stkoff(), monoff(), endoff(), monitor_depth(), sp(), bci());
st->print("JVMS depth=%d loc=%d stk=%d mon=%d scalar=%d end=%d mondepth=%d sp=%d bci=%d method=",
depth(), locoff(), stkoff(), monoff(), scloff(), endoff(), monitor_depth(), sp(), bci());
if (_method == NULL) {
st->print_cr("(none)");
} else {
@ -465,6 +531,7 @@ JVMState* JVMState::clone_shallow(Compile* C) const {
n->set_locoff(_locoff);
n->set_stkoff(_stkoff);
n->set_monoff(_monoff);
n->set_scloff(_scloff);
n->set_endoff(_endoff);
n->set_sp(_sp);
n->set_map(_map);
@ -557,6 +624,107 @@ uint CallNode::match_edge(uint idx) const {
return 0;
}
//
// Determine whether the call could modify the field of the specified
// instance at the specified offset.
//
bool CallNode::may_modify(const TypePtr *addr_t, PhaseTransform *phase) {
const TypeOopPtr *adrInst_t = addr_t->isa_oopptr();
// if not an InstPtr or not an instance type, assume the worst
if (adrInst_t == NULL || !adrInst_t->is_instance_field()) {
return true;
}
Compile *C = phase->C;
int offset = adrInst_t->offset();
assert(offset >= 0, "should be valid offset");
ciKlass* adr_k = adrInst_t->klass();
assert(adr_k->is_loaded() &&
adr_k->is_java_klass() &&
!adr_k->is_interface(),
"only non-abstract classes are expected");
int base_idx = C->get_alias_index(adrInst_t);
int size = BytesPerLong; // If we don't know the size, assume largest.
if (adrInst_t->isa_instptr()) {
ciField* field = C->alias_type(base_idx)->field();
if (field != NULL) {
size = field->size_in_bytes();
}
} else {
assert(adrInst_t->isa_aryptr(), "only arrays are expected");
size = type2aelembytes(adr_k->as_array_klass()->element_type()->basic_type());
}
ciMethod * meth = is_CallStaticJava() ? as_CallStaticJava()->method() : NULL;
BCEscapeAnalyzer *bcea = (meth != NULL) ? meth->get_bcea() : NULL;
const TypeTuple * d = tf()->domain();
for (uint i = TypeFunc::Parms; i < d->cnt(); i++) {
const Type* t = d->field_at(i);
Node *arg = in(i);
const Type *at = phase->type(arg);
if (at == TypePtr::NULL_PTR || at == Type::TOP)
continue; // null can't affect anything
const TypeOopPtr *at_ptr = at->isa_oopptr();
if (!arg->is_top() && (t->isa_oopptr() != NULL ||
t->isa_ptr() && at_ptr != NULL)) {
assert(at_ptr != NULL, "expecting an OopPtr");
ciKlass* at_k = at_ptr->klass();
if ((adrInst_t->base() == at_ptr->base()) &&
at_k->is_loaded() &&
at_k->is_java_klass() &&
!at_k->is_interface()) {
// If we have found an argument matching addr_t, check if the field
// at the specified offset is modified.
int at_idx = C->get_alias_index(at_ptr->add_offset(offset)->isa_oopptr());
if (base_idx == at_idx &&
(bcea == NULL ||
bcea->is_arg_modified(i - TypeFunc::Parms, offset, size))) {
return true;
}
}
}
}
return false;
}
// Does this call have a direct reference to n other than debug information?
bool CallNode::has_non_debug_use(Node *n) {
const TypeTuple * d = tf()->domain();
for (uint i = TypeFunc::Parms; i < d->cnt(); i++) {
Node *arg = in(i);
if (arg == n) {
return true;
}
}
return false;
}
// Returns the unique CheckCastPP of a call
// or 'this' if there are several CheckCastPP
// or returns NULL if there is no one.
Node *CallNode::result_cast() {
Node *cast = NULL;
Node *p = proj_out(TypeFunc::Parms);
if (p == NULL)
return NULL;
for (DUIterator_Fast imax, i = p->fast_outs(imax); i < imax; i++) {
Node *use = p->fast_out(i);
if (use->is_CheckCastPP()) {
if (cast != NULL) {
return this; // more than 1 CheckCastPP
}
cast = use;
}
}
return cast;
}
//=============================================================================
uint CallJavaNode::size_of() const { return sizeof(*this); }
uint CallJavaNode::cmp( const Node &n ) const {
@ -765,6 +933,7 @@ const RegMask &SafePointNode::out_RegMask() const {
void SafePointNode::grow_stack(JVMState* jvms, uint grow_by) {
assert((int)grow_by > 0, "sanity");
int monoff = jvms->monoff();
int scloff = jvms->scloff();
int endoff = jvms->endoff();
assert(endoff == (int)req(), "no other states or debug info after me");
Node* top = Compile::current()->top();
@ -772,6 +941,7 @@ void SafePointNode::grow_stack(JVMState* jvms, uint grow_by) {
ins_req(monoff, top);
}
jvms->set_monoff(monoff + grow_by);
jvms->set_scloff(scloff + grow_by);
jvms->set_endoff(endoff + grow_by);
}
@ -781,6 +951,7 @@ void SafePointNode::push_monitor(const FastLockNode *lock) {
const int MonitorEdges = 2;
assert(JVMState::logMonitorEdges == exact_log2(MonitorEdges), "correct MonitorEdges");
assert(req() == jvms()->endoff(), "correct sizing");
int nextmon = jvms()->scloff();
if (GenerateSynchronizationCode) {
add_req(lock->box_node());
add_req(lock->obj_node());
@ -788,6 +959,7 @@ void SafePointNode::push_monitor(const FastLockNode *lock) {
add_req(NULL);
add_req(NULL);
}
jvms()->set_scloff(nextmon+MonitorEdges);
jvms()->set_endoff(req());
}
@ -795,10 +967,13 @@ void SafePointNode::pop_monitor() {
// Delete last monitor from debug info
debug_only(int num_before_pop = jvms()->nof_monitors());
const int MonitorEdges = (1<<JVMState::logMonitorEdges);
int scloff = jvms()->scloff();
int endoff = jvms()->endoff();
int new_scloff = scloff - MonitorEdges;
int new_endoff = endoff - MonitorEdges;
jvms()->set_scloff(new_scloff);
jvms()->set_endoff(new_endoff);
while (endoff > new_endoff) del_req(--endoff);
while (scloff > new_scloff) del_req(--scloff);
assert(jvms()->nof_monitors() == num_before_pop-1, "");
}
@ -822,6 +997,63 @@ uint SafePointNode::match_edge(uint idx) const {
return (TypeFunc::Parms == idx);
}
//============== SafePointScalarObjectNode ==============
SafePointScalarObjectNode::SafePointScalarObjectNode(const TypeOopPtr* tp,
#ifdef ASSERT
AllocateNode* alloc,
#endif
uint first_index,
uint n_fields) :
TypeNode(tp, 1), // 1 control input -- seems required. Get from root.
#ifdef ASSERT
_alloc(alloc),
#endif
_first_index(first_index),
_n_fields(n_fields)
{
init_class_id(Class_SafePointScalarObject);
}
uint SafePointScalarObjectNode::ideal_reg() const {
return 0; // No matching to machine instruction
}
const RegMask &SafePointScalarObjectNode::in_RegMask(uint idx) const {
return *(Compile::current()->matcher()->idealreg2debugmask[in(idx)->ideal_reg()]);
}
const RegMask &SafePointScalarObjectNode::out_RegMask() const {
return RegMask::Empty;
}
uint SafePointScalarObjectNode::match_edge(uint idx) const {
return 0;
}
SafePointScalarObjectNode*
SafePointScalarObjectNode::clone(int jvms_adj, Dict* sosn_map) const {
void* cached = (*sosn_map)[(void*)this];
if (cached != NULL) {
return (SafePointScalarObjectNode*)cached;
}
Compile* C = Compile::current();
SafePointScalarObjectNode* res = (SafePointScalarObjectNode*)Node::clone();
res->_first_index += jvms_adj;
sosn_map->Insert((void*)this, (void*)res);
return res;
}
#ifndef PRODUCT
void SafePointScalarObjectNode::dump_spec(outputStream *st) const {
st->print(" # fields@[%d..%d]", first_index(),
first_index() + n_fields() - 1);
}
#endif
//=============================================================================
uint AllocateNode::size_of() const { return sizeof(*this); }
@ -1152,7 +1384,7 @@ void AbstractLockNode::set_eliminated() {
//=============================================================================
Node *LockNode::Ideal(PhaseGVN *phase, bool can_reshape) {
// perform any generic optimizations first
// perform any generic optimizations first (returns 'this' or NULL)
Node *result = SafePointNode::Ideal(phase, can_reshape);
// Now see if we can optimize away this lock. We don't actually
@ -1160,7 +1392,20 @@ Node *LockNode::Ideal(PhaseGVN *phase, bool can_reshape) {
// prevents macro expansion from expanding the lock. Since we don't
// modify the graph, the value returned from this function is the
// one computed above.
if (EliminateLocks && !is_eliminated()) {
if (result == NULL && can_reshape && EliminateLocks && !is_eliminated()) {
//
// If we are locking an unescaped object, the lock/unlock is unnecessary
//
ConnectionGraph *cgr = Compile::current()->congraph();
PointsToNode::EscapeState es = PointsToNode::GlobalEscape;
if (cgr != NULL)
es = cgr->escape_state(obj_node(), phase);
if (es != PointsToNode::UnknownEscape && es != PointsToNode::GlobalEscape) {
// Mark it eliminated to update any counters
this->set_eliminated();
return result;
}
//
// Try lock coarsening
//
@ -1200,8 +1445,10 @@ Node *LockNode::Ideal(PhaseGVN *phase, bool can_reshape) {
int unlocks = 0;
for (int i = 0; i < lock_ops.length(); i++) {
AbstractLockNode* lock = lock_ops.at(i);
if (lock->Opcode() == Op_Lock) locks++;
else unlocks++;
if (lock->Opcode() == Op_Lock)
locks++;
else
unlocks++;
if (Verbose) {
lock->dump(1);
}
@ -1238,7 +1485,7 @@ uint UnlockNode::size_of() const { return sizeof(*this); }
//=============================================================================
Node *UnlockNode::Ideal(PhaseGVN *phase, bool can_reshape) {
// perform any generic optimizations first
// perform any generic optimizations first (returns 'this' or NULL)
Node * result = SafePointNode::Ideal(phase, can_reshape);
// Now see if we can optimize away this unlock. We don't actually
@ -1246,66 +1493,18 @@ Node *UnlockNode::Ideal(PhaseGVN *phase, bool can_reshape) {
// prevents macro expansion from expanding the unlock. Since we don't
// modify the graph, the value returned from this function is the
// one computed above.
if (EliminateLocks && !is_eliminated()) {
// Escape state is defined after Parse phase.
if (result == NULL && can_reshape && EliminateLocks && !is_eliminated()) {
//
// If we are unlocking an unescaped object, the lock/unlock is unnecessary
// We can eliminate them if there are no safepoints in the locked region.
// If we are unlocking an unescaped object, the lock/unlock is unnecessary.
//
ConnectionGraph *cgr = Compile::current()->congraph();
if (cgr != NULL && cgr->escape_state(obj_node(), phase) == PointsToNode::NoEscape) {
GrowableArray<AbstractLockNode*> lock_ops;
LockNode *lock = find_matching_lock(this);
if (lock != NULL) {
lock_ops.append(this);
lock_ops.append(lock);
// find other unlocks which pair with the lock we found and add them
// to the list
Node * box = box_node();
for (DUIterator_Fast imax, i = box->fast_outs(imax); i < imax; i++) {
Node *use = box->fast_out(i);
if (use->is_Unlock() && use != this) {
UnlockNode *unlock1 = use->as_Unlock();
if (!unlock1->is_eliminated()) {
LockNode *lock1 = find_matching_lock(unlock1);
if (lock == lock1)
lock_ops.append(unlock1);
else if (lock1 == NULL) {
// we can't find a matching lock, we must assume the worst
lock_ops.trunc_to(0);
break;
}
}
}
}
if (lock_ops.length() > 0) {
#ifndef PRODUCT
if (PrintEliminateLocks) {
int locks = 0;
int unlocks = 0;
for (int i = 0; i < lock_ops.length(); i++) {
AbstractLockNode* lock = lock_ops.at(i);
if (lock->Opcode() == Op_Lock) locks++;
else unlocks++;
if (Verbose) {
lock->dump(1);
}
}
tty->print_cr("***Eliminated %d unescaped unlocks and %d unescaped locks", unlocks, locks);
}
#endif
// for each of the identified locks, mark them
// as eliminatable
for (int i = 0; i < lock_ops.length(); i++) {
AbstractLockNode* lock = lock_ops.at(i);
// Mark it eliminated to update any counters
lock->set_eliminated();
}
}
}
PointsToNode::EscapeState es = PointsToNode::GlobalEscape;
if (cgr != NULL)
es = cgr->escape_state(obj_node(), phase);
if (es != PointsToNode::UnknownEscape && es != PointsToNode::GlobalEscape) {
// Mark it eliminated to update any counters
this->set_eliminated();
}
}
return result;

@ -184,6 +184,7 @@ private:
uint _locoff; // Offset to locals in input edge mapping
uint _stkoff; // Offset to stack in input edge mapping
uint _monoff; // Offset to monitors in input edge mapping
uint _scloff; // Offset to fields of scalar objs in input edge mapping
uint _endoff; // Offset to end of input edge mapping
uint _sp; // Jave Expression Stack Pointer for this state
int _bci; // Byte Code Index of this JVM point
@ -207,16 +208,19 @@ public:
uint stkoff() const { return _stkoff; }
uint argoff() const { return _stkoff + _sp; }
uint monoff() const { return _monoff; }
uint scloff() const { return _scloff; }
uint endoff() const { return _endoff; }
uint oopoff() const { return debug_end(); }
int loc_size() const { return _stkoff - _locoff; }
int stk_size() const { return _monoff - _stkoff; }
int mon_size() const { return _endoff - _monoff; }
int mon_size() const { return _scloff - _monoff; }
int scl_size() const { return _endoff - _scloff; }
bool is_loc(uint i) const { return i >= _locoff && i < _stkoff; }
bool is_stk(uint i) const { return i >= _stkoff && i < _monoff; }
bool is_mon(uint i) const { return i >= _monoff && i < _endoff; }
bool is_mon(uint i) const { return i >= _monoff && i < _scloff; }
bool is_scl(uint i) const { return i >= _scloff && i < _endoff; }
uint sp() const { return _sp; }
int bci() const { return _bci; }
@ -227,7 +231,9 @@ public:
uint depth() const { return _depth; }
uint debug_start() const; // returns locoff of root caller
uint debug_end() const; // returns endoff of self
uint debug_size() const { return loc_size() + sp() + mon_size(); }
uint debug_size() const {
return loc_size() + sp() + mon_size() + scl_size();
}
uint debug_depth() const; // returns sum of debug_size values at all depths
// Returns the JVM state at the desired depth (1 == root).
@ -254,8 +260,11 @@ public:
void set_locoff(uint off) { _locoff = off; }
void set_stkoff(uint off) { _stkoff = off; }
void set_monoff(uint off) { _monoff = off; }
void set_scloff(uint off) { _scloff = off; }
void set_endoff(uint off) { _endoff = off; }
void set_offsets(uint off) { _locoff = _stkoff = _monoff = _endoff = off; }
void set_offsets(uint off) {
_locoff = _stkoff = _monoff = _scloff = _endoff = off;
}
void set_map(SafePointNode *map) { _map = map; }
void set_sp(uint sp) { _sp = sp; }
void set_bci(int bci) { _bci = bci; }
@ -379,6 +388,9 @@ public:
void set_next_exception(SafePointNode* n);
bool has_exceptions() const { return next_exception() != NULL; }
// Does this node have a use of n other than in debug information?
virtual bool has_non_debug_use(Node *n) {return false; }
// Standard Node stuff
virtual int Opcode() const;
virtual bool pinned() const { return true; }
@ -399,6 +411,47 @@ public:
#endif
};
//------------------------------SafePointScalarObjectNode----------------------
// A SafePointScalarObjectNode represents the state of a scalarized object
// at a safepoint.
class SafePointScalarObjectNode: public TypeNode {
uint _first_index; // First input edge index of a SafePoint node where
// states of the scalarized object fields are collected.
uint _n_fields; // Number of non-static fields of the scalarized object.
DEBUG_ONLY(AllocateNode* _alloc;)
public:
SafePointScalarObjectNode(const TypeOopPtr* tp,
#ifdef ASSERT
AllocateNode* alloc,
#endif
uint first_index, uint n_fields);
virtual int Opcode() const;
virtual uint ideal_reg() const;
virtual const RegMask &in_RegMask(uint) const;
virtual const RegMask &out_RegMask() const;
virtual uint match_edge(uint idx) const;
uint first_index() const { return _first_index; }
uint n_fields() const { return _n_fields; }
DEBUG_ONLY(AllocateNode* alloc() const { return _alloc; })
virtual uint size_of() const { return sizeof(*this); }
// Assumes that "this" is an argument to a safepoint node "s", and that
// "new_call" is being created to correspond to "s". But the difference
// between the start index of the jvmstates of "new_call" and "s" is
// "jvms_adj". Produce and return a SafePointScalarObjectNode that
// corresponds appropriately to "this" in "new_call". Assumes that
// "sosn_map" is a map, specific to the translation of "s" to "new_call",
// mapping old SafePointScalarObjectNodes to new, to avoid multiple copies.
SafePointScalarObjectNode* clone(int jvms_adj, Dict* sosn_map) const;
#ifndef PRODUCT
virtual void dump_spec(outputStream *st) const;
#endif
};
//------------------------------CallNode---------------------------------------
// Call nodes now subsume the function of debug nodes at callsites, so they
// contain the functionality of a full scope chain of debug nodes.
@ -407,7 +460,6 @@ public:
const TypeFunc *_tf; // Function type
address _entry_point; // Address of method being called
float _cnt; // Estimate of number of times called
PointsToNode::EscapeState _escape_state;
CallNode(const TypeFunc* tf, address addr, const TypePtr* adr_type)
: SafePointNode(tf->domain()->cnt(), NULL, adr_type),
@ -417,7 +469,6 @@ public:
{
init_class_id(Class_Call);
init_flags(Flag_is_Call);
_escape_state = PointsToNode::UnknownEscape;
}
const TypeFunc* tf() const { return _tf; }
@ -443,6 +494,15 @@ public:
// the node the JVMState must be cloned.
virtual void clone_jvms() { } // default is not to clone
// Returns true if the call may modify n
virtual bool may_modify(const TypePtr *addr_t, PhaseTransform *phase);
// Does this node have a use of n other than in debug information?
virtual bool has_non_debug_use(Node *n);
// Returns the unique CheckCastPP of a call
// or result projection is there are several CheckCastPP
// or returns NULL if there is no one.
Node *result_cast();
virtual uint match_edge(uint idx) const;
#ifndef PRODUCT
@ -639,6 +699,9 @@ public:
virtual uint ideal_reg() const { return Op_RegP; }
virtual bool guaranteed_safepoint() { return false; }
// allocations do not modify their arguments
virtual bool may_modify(const TypePtr *addr_t, PhaseTransform *phase) { return false;}
// Pattern-match a possible usage of AllocateNode.
// Return null if no allocation is recognized.
// The operand is the pointer produced by the (possible) allocation.
@ -751,6 +814,9 @@ public:
// mark node as eliminated and update the counter if there is one
void set_eliminated();
// locking does not modify its arguments
virtual bool may_modify(const TypePtr *addr_t, PhaseTransform *phase){ return false;}
#ifndef PRODUCT
void create_lock_counter(JVMState* s);
NamedCounter* counter() const { return _counter; }

@ -704,6 +704,61 @@ PhiNode* PhiNode::slice_memory(const TypePtr* adr_type) const {
return mem;
}
//------------------------split_out_instance-----------------------------------
// Split out an instance type from a bottom phi.
PhiNode* PhiNode::split_out_instance(const TypePtr* at, PhaseIterGVN *igvn) const {
assert(type() == Type::MEMORY && (adr_type() == TypePtr::BOTTOM ||
adr_type() == TypeRawPtr::BOTTOM) , "bottom or raw memory required");
// Check if an appropriate node already exists.
Node *region = in(0);
for (DUIterator_Fast kmax, k = region->fast_outs(kmax); k < kmax; k++) {
Node* use = region->fast_out(k);
if( use->is_Phi()) {
PhiNode *phi2 = use->as_Phi();
if (phi2->type() == Type::MEMORY && phi2->adr_type() == at) {
return phi2;
}
}
}
Compile *C = igvn->C;
Arena *a = Thread::current()->resource_area();
Node_Array node_map = new Node_Array(a);
Node_Stack stack(a, C->unique() >> 4);
PhiNode *nphi = slice_memory(at);
igvn->register_new_node_with_optimizer( nphi );
node_map.map(_idx, nphi);
stack.push((Node *)this, 1);
while(!stack.is_empty()) {
PhiNode *ophi = stack.node()->as_Phi();
uint i = stack.index();
assert(i >= 1, "not control edge");
stack.pop();
nphi = node_map[ophi->_idx]->as_Phi();
for (; i < ophi->req(); i++) {
Node *in = ophi->in(i);
if (in == NULL || igvn->type(in) == Type::TOP)
continue;
Node *opt = MemNode::optimize_simple_memory_chain(in, at, igvn);
PhiNode *optphi = opt->is_Phi() ? opt->as_Phi() : NULL;
if (optphi != NULL && optphi->adr_type() == TypePtr::BOTTOM) {
opt = node_map[optphi->_idx];
if (opt == NULL) {
stack.push(ophi, i);
nphi = optphi->slice_memory(at);
igvn->register_new_node_with_optimizer( nphi );
node_map.map(optphi->_idx, nphi);
ophi = optphi;
i = 0; // will get incremented at top of loop
continue;
}
}
nphi->set_req(i, opt);
}
}
return nphi;
}
//------------------------verify_adr_type--------------------------------------
#ifdef ASSERT
void PhiNode::verify_adr_type(VectorSet& visited, const TypePtr* at) const {
@ -1736,6 +1791,18 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) {
return result;
}
}
//
// Other optimizations on the memory chain
//
const TypePtr* at = adr_type();
for( uint i=1; i<req(); ++i ) {// For all paths in
Node *ii = in(i);
Node *new_in = MemNode::optimize_memory_chain(ii, at, phase);
if (ii != new_in ) {
set_req(i, new_in);
progress = this;
}
}
}
return progress; // Return any progress

@ -110,14 +110,15 @@ class JProjNode : public ProjNode {
// input in slot 0.
class PhiNode : public TypeNode {
const TypePtr* const _adr_type; // non-null only for Type::MEMORY nodes.
const int _inst_id; // Instance id of the memory slice.
const int _inst_index; // Alias index of the instance memory slice.
// Array elements references have the same alias_idx but different offset.
const int _inst_offset; // Offset of the instance memory slice.
// Size is bigger to hold the _adr_type field.
virtual uint hash() const; // Check the type
virtual uint cmp( const Node &n ) const;
virtual uint size_of() const { return sizeof(*this); }
// Determine a unique non-trivial input, if any.
// Ignore casts if it helps. Return NULL on failure.
Node* unique_input(PhaseTransform *phase);
// Determine if CMoveNode::is_cmove_id can be used at this join point.
Node* is_cmove_id(PhaseTransform* phase, int true_path);
@ -127,8 +128,16 @@ public:
Input // Input values are [1..len)
};
PhiNode( Node *r, const Type *t, const TypePtr* at = NULL )
: TypeNode(t,r->req()), _adr_type(at) {
PhiNode( Node *r, const Type *t, const TypePtr* at = NULL,
const int iid = TypeOopPtr::UNKNOWN_INSTANCE,
const int iidx = Compile::AliasIdxTop,
const int ioffs = Type::OffsetTop )
: TypeNode(t,r->req()),
_adr_type(at),
_inst_id(iid),
_inst_index(iidx),
_inst_offset(ioffs)
{
init_class_id(Class_Phi);
init_req(0, r);
verify_adr_type();
@ -139,6 +148,7 @@ public:
static PhiNode* make( Node* r, Node* x, const Type *t, const TypePtr* at = NULL );
// create a new phi with narrowed memory type
PhiNode* slice_memory(const TypePtr* adr_type) const;
PhiNode* split_out_instance(const TypePtr* at, PhaseIterGVN *igvn) const;
// like make(r, x), but does not initialize the in edges to x
static PhiNode* make_blank( Node* r, Node* x );
@ -152,6 +162,10 @@ public:
return NULL; // not a copy!
}
// Determine a unique non-trivial input, if any.
// Ignore casts if it helps. Return NULL on failure.
Node* unique_input(PhaseTransform *phase);
// Check for a simple dead loop.
enum LoopSafety { Safe = 0, Unsafe, UnsafeLoop };
LoopSafety simple_data_loop_check(Node *in) const;
@ -161,6 +175,18 @@ public:
virtual int Opcode() const;
virtual bool pinned() const { return in(0) != 0; }
virtual const TypePtr *adr_type() const { verify_adr_type(true); return _adr_type; }
const int inst_id() const { return _inst_id; }
const int inst_index() const { return _inst_index; }
const int inst_offset() const { return _inst_offset; }
bool is_same_inst_field(const Type* tp, int id, int index, int offset) {
return type()->basic_type() == tp->basic_type() &&
inst_id() == id &&
inst_index() == index &&
inst_offset() == offset &&
type()->higher_equal(tp);
}
virtual const Type *Value( PhaseTransform *phase ) const;
virtual Node *Identity( PhaseTransform *phase );
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);

@ -457,7 +457,8 @@ private:
bool may_be_copy_of_callee( Node *def ) const;
// If nreg already contains the same constant as val then eliminate it
bool eliminate_copy_of_constant(Node* val, Block *current_block, Node_List& value, Node_List &regnd,
bool eliminate_copy_of_constant(Node* val, Node* n,
Block *current_block, Node_List& value, Node_List &regnd,
OptoReg::Name nreg, OptoReg::Name nreg2);
// Extend the node to LRG mapping
void add_reference( const Node *node, const Node *old_node);

@ -185,6 +185,7 @@ macro(Root)
macro(RoundDouble)
macro(RoundFloat)
macro(SafePoint)
macro(SafePointScalarObject)
macro(SCMemProj)
macro(SinD)
macro(SqrtD)

@ -407,11 +407,6 @@ uint Compile::scratch_emit_size(const Node* n) {
return buf.code_size();
}
void Compile::record_for_escape_analysis(Node* n) {
if (_congraph != NULL)
_congraph->record_for_escape_analysis(n);
}
// ============================================================================
//------------------------------Compile standard-------------------------------
@ -494,9 +489,6 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr
PhaseGVN gvn(node_arena(), estimated_size);
set_initial_gvn(&gvn);
if (_do_escape_analysis)
_congraph = new ConnectionGraph(this);
{ // Scope for timing the parser
TracePhase t3("parse", &_t_parser, true);
@ -581,6 +573,8 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr
NOT_PRODUCT( verify_graph_edges(); )
// Perform escape analysis
if (_do_escape_analysis)
_congraph = new ConnectionGraph(this);
if (_congraph != NULL) {
NOT_PRODUCT( TracePhase t2("escapeAnalysis", &_t_escapeAnalysis, TimeCompiler); )
_congraph->compute_escape();

@ -485,7 +485,6 @@ class Compile : public Phase {
PhaseGVN* initial_gvn() { return _initial_gvn; }
Unique_Node_List* for_igvn() { return _for_igvn; }
inline void record_for_igvn(Node* n); // Body is after class Unique_Node_List.
void record_for_escape_analysis(Node* n);
void set_initial_gvn(PhaseGVN *gvn) { _initial_gvn = gvn; }
void set_for_igvn(Unique_Node_List *for_igvn) { _for_igvn = for_igvn; }
@ -606,8 +605,20 @@ class Compile : public Phase {
// Build OopMaps for each GC point
void BuildOopMaps();
// Append debug info for the node to the array
void FillLocArray( int idx, Node *local, GrowableArray<ScopeValue*> *array );
// Append debug info for the node "local" at safepoint node "sfpt" to the
// "array", May also consult and add to "objs", which describes the
// scalar-replaced objects.
void FillLocArray( int idx, MachSafePointNode* sfpt,
Node *local, GrowableArray<ScopeValue*> *array,
GrowableArray<ScopeValue*> *objs );
// If "objs" contains an ObjectValue whose id is "id", returns it, else NULL.
static ObjectValue* sv_for_node_id(GrowableArray<ScopeValue*> *objs, int id);
// Requres that "objs" does not contains an ObjectValue whose id matches
// that of "sv. Appends "sv".
static void set_sv_for_object_node(GrowableArray<ScopeValue*> *objs,
ObjectValue* sv );
// Process an OopMap Element while emitting nodes
void Process_OopMap_Node(MachNode *mach, int code_offset);

@ -390,6 +390,8 @@ void Parse::do_call() {
}
if (cg->is_inline()) {
// Accumulate has_loops estimate
C->set_has_loops(C->has_loops() || call_method->has_loops());
C->env()->notice_inlined_method(call_method);
}

File diff suppressed because it is too large Load Diff

@ -25,14 +25,15 @@
//
// Adaptation for C2 of the escape analysis algorithm described in:
//
// [Choi99] Jong-Deok Shoi, Manish Gupta, Mauricio Seffano, Vugranam C. Sreedhar,
// Sam Midkiff, "Escape Analysis for Java", Procedings of ACM SIGPLAN
// OOPSLA Conference, November 1, 1999
// [Choi99] Jong-Deok Shoi, Manish Gupta, Mauricio Seffano,
// Vugranam C. Sreedhar, Sam Midkiff,
// "Escape Analysis for Java", Procedings of ACM SIGPLAN
// OOPSLA Conference, November 1, 1999
//
// The flow-insensitive analysis described in the paper has been implemented.
//
// The analysis requires construction of a "connection graph" (CG) for the method being
// analyzed. The nodes of the connection graph are:
// The analysis requires construction of a "connection graph" (CG) for
// the method being analyzed. The nodes of the connection graph are:
//
// - Java objects (JO)
// - Local variables (LV)
@ -40,47 +41,51 @@
//
// The CG contains 3 types of edges:
//
// - PointsTo (-P>) {LV,OF} to JO
// - Deferred (-D>) from {LV, OF} to {LV, OF}
// - PointsTo (-P>) {LV, OF} to JO
// - Deferred (-D>) from {LV, OF} to {LV, OF}
// - Field (-F>) from JO to OF
//
// The following utility functions is used by the algorithm:
//
// PointsTo(n) - n is any CG node, it returns the set of JO that n could
// point to.
// PointsTo(n) - n is any CG node, it returns the set of JO that n could
// point to.
//
// The algorithm describes how to construct the connection graph in the following 4 cases:
// The algorithm describes how to construct the connection graph
// in the following 4 cases:
//
// Case Edges Created
//
// (1) p = new T() LV -P> JO
// (2) p = q LV -D> LV
// (3) p.f = q JO -F> OF, OF -D> LV
// (4) p = q.f JO -F> OF, LV -D> OF
// (1) p = new T() LV -P> JO
// (2) p = q LV -D> LV
// (3) p.f = q JO -F> OF, OF -D> LV
// (4) p = q.f JO -F> OF, LV -D> OF
//
// In all these cases, p and q are local variables. For static field references, we can
// construct a local variable containing a reference to the static memory.
// In all these cases, p and q are local variables. For static field
// references, we can construct a local variable containing a reference
// to the static memory.
//
// C2 does not have local variables. However for the purposes of constructing
// the connection graph, the following IR nodes are treated as local variables:
// Phi (pointer values)
// LoadP
// Proj (value returned from callnodes including allocations)
// CheckCastPP
// Proj#5 (value returned from callnodes including allocations)
// CheckCastPP, CastPP
//
// The LoadP, Proj and CheckCastPP behave like variables assigned to only once. Only
// a Phi can have multiple assignments. Each input to a Phi is treated
// The LoadP, Proj and CheckCastPP behave like variables assigned to only once.
// Only a Phi can have multiple assignments. Each input to a Phi is treated
// as an assignment to it.
//
// The following note types are JavaObject:
// The following node types are JavaObject:
//
// top()
// Allocate
// AllocateArray
// Parm (for incoming arguments)
// CastX2P ("unsafe" operations)
// CreateEx
// ConP
// LoadKlass
// ThreadLocal
//
// AddP nodes are fields.
//
@ -89,7 +94,7 @@
// source. This results in a graph with no deferred edges, only:
//
// LV -P> JO
// OF -P> JO
// OF -P> JO (the object whose oop is stored in the field)
// JO -F> OF
//
// Then, for each node which is GlobalEscape, anything it could point to
@ -110,17 +115,18 @@ class PointsToNode {
friend class ConnectionGraph;
public:
typedef enum {
UnknownType = 0,
JavaObject = 1,
LocalVar = 2,
Field = 3
UnknownType = 0,
JavaObject = 1,
LocalVar = 2,
Field = 3
} NodeType;
typedef enum {
UnknownEscape = 0,
NoEscape = 1,
ArgEscape = 2,
GlobalEscape = 3
NoEscape = 1, // A scalar replaceable object with unique type.
ArgEscape = 2, // An object passed as argument or referenced by
// argument (and not globally escape during call).
GlobalEscape = 3 // An object escapes the method and thread.
} EscapeState;
typedef enum {
@ -140,18 +146,24 @@ private:
NodeType _type;
EscapeState _escape;
GrowableArray<uint>* _edges; // outgoing edges
int _offset; // for fields
GrowableArray<uint>* _edges; // outgoing edges
bool _unique_type; // For allocated objects, this node may be a unique type
public:
Node* _node; // Ideal node corresponding to this PointsTo node
int _inputs_processed; // the number of Phi inputs that have been processed so far
bool _hidden_alias; // this node is an argument to a function which may return it
// creating a hidden alias
Node* _node; // Ideal node corresponding to this PointsTo node.
int _offset; // Object fields offsets.
bool _scalar_replaceable;// Not escaped object could be replaced with scalar
bool _hidden_alias; // This node is an argument to a function.
// which may return it creating a hidden alias.
PointsToNode():
_type(UnknownType),
_escape(UnknownEscape),
_edges(NULL),
_node(NULL),
_offset(-1),
_scalar_replaceable(true),
_hidden_alias(false) {}
PointsToNode(): _offset(-1), _type(UnknownType), _escape(UnknownEscape), _edges(NULL), _node(NULL), _inputs_processed(0), _hidden_alias(false), _unique_type(true) {}
EscapeState escape_state() const { return _escape; }
NodeType node_type() const { return _type;}
@ -182,22 +194,28 @@ public:
class ConnectionGraph: public ResourceObj {
private:
enum {
INITIAL_NODE_COUNT = 100 // initial size of _nodes array
};
GrowableArray<PointsToNode>* _nodes; // Connection graph nodes indexed
// by ideal node index.
Unique_Node_List _delayed_worklist; // Nodes to be processed before
// the call build_connection_graph().
GrowableArray<PointsToNode>* _nodes; // connection graph nodes Indexed by ideal
// node index
Unique_Node_List _deferred; // Phi's to be processed after parsing
VectorSet _processed; // records which nodes have been processed
bool _collecting; // indicates whether escape information is
// still being collected. If false, no new
// nodes will be processed
uint _phantom_object; // index of globally escaping object that
// pointer values loaded from a field which
// has not been set are assumed to point to
Compile * _compile; // Compile object for current compilation
VectorSet _processed; // Records which nodes have been
// processed.
bool _collecting; // Indicates whether escape information
// is still being collected. If false,
// no new nodes will be processed.
bool _has_allocations; // Indicates whether method has any
// non-escaping allocations.
uint _phantom_object; // Index of globally escaping object
// that pointer values loaded from
// a field which has not been set
// are assumed to point to.
Compile * _compile; // Compile object for current compilation
// address of an element in _nodes. Used when the element is to be modified
PointsToNode *ptnode_adr(uint idx) {
@ -208,8 +226,11 @@ private:
return _nodes->adr_at(idx);
}
// Add node to ConnectionGraph.
void add_node(Node *n, PointsToNode::NodeType nt, PointsToNode::EscapeState es, bool done);
// offset of a field reference
int type_to_offset(const Type *t);
int address_offset(Node* adr, PhaseTransform *phase);
// compute the escape state for arguments to a call
void process_call_arguments(CallNode *call, PhaseTransform *phase);
@ -217,12 +238,11 @@ private:
// compute the escape state for the return value of a call
void process_call_result(ProjNode *resproj, PhaseTransform *phase);
// compute the escape state of a Phi. This may be called multiple
// times as new inputs are added to the Phi.
void process_phi_escape(PhiNode *phi, PhaseTransform *phase);
// Populate Connection Graph with Ideal nodes.
void record_for_escape_analysis(Node *n, PhaseTransform *phase);
// compute the escape state of an ideal node.
void record_escape_work(Node *n, PhaseTransform *phase);
// Build Connection Graph and set nodes escape state.
void build_connection_graph(Node *n, PhaseTransform *phase);
// walk the connection graph starting at the node corresponding to "n" and
// add the index of everything it could point to, to "ptset". This may cause
@ -241,8 +261,8 @@ private:
// a pointsto edge is added if it is a JavaObject
void add_edge_from_fields(uint adr, uint to_i, int offs);
// Add a deferred edge from node given by "from_i" to any field of adr_i whose offset
// matches "offset"
// Add a deferred edge from node given by "from_i" to any field
// of adr_i whose offset matches "offset"
void add_deferred_edge_to_fields(uint from_i, uint adr, int offs);
@ -262,6 +282,8 @@ private:
PhiNode *create_split_phi(PhiNode *orig_phi, int alias_idx, GrowableArray<PhiNode *> &orig_phi_worklist, PhaseGVN *igvn, bool &new_created);
PhiNode *split_memory_phi(PhiNode *orig_phi, int alias_idx, GrowableArray<PhiNode *> &orig_phi_worklist, PhaseGVN *igvn);
Node *find_mem(Node *mem, int alias_idx, PhaseGVN *igvn);
Node *find_inst_mem(Node *mem, int alias_idx,GrowableArray<PhiNode *> &orig_phi_worklist, PhaseGVN *igvn);
// Propagate unique types created for unescaped allocated objects
// through the graph
void split_unique_types(GrowableArray<Node *> &alloc_worklist);
@ -285,26 +307,24 @@ private:
// Set the escape state of a node
void set_escape_state(uint ni, PointsToNode::EscapeState es);
// bypass any casts and return the node they refer to
Node * skip_casts(Node *n);
// Get Compile object for current compilation.
Compile *C() const { return _compile; }
public:
ConnectionGraph(Compile *C);
// record a Phi for later processing.
void record_for_escape_analysis(Node *n);
// process a node and fill in its connection graph node
void record_escape(Node *n, PhaseTransform *phase);
// All nodes have been recorded, compute the escape information
// Compute the escape information
void compute_escape();
// escape state of a node
PointsToNode::EscapeState escape_state(Node *n, PhaseTransform *phase);
// other information we have collected
bool is_scalar_replaceable(Node *n) {
if (_collecting)
return false;
PointsToNode ptn = _nodes->at_grow(n->_idx);
return ptn.escape_state() == PointsToNode::NoEscape && ptn._scalar_replaceable;
}
bool hidden_alias(Node *n) {
if (_collecting)

@ -857,6 +857,13 @@ void GraphKit::add_safepoint_edges(SafePointNode* call, bool must_throw) {
for (j = 0; j < l; j++)
call->set_req(p++, in_map->in(k+j));
// Copy any scalar object fields.
k = in_jvms->scloff();
l = in_jvms->scl_size();
out_jvms->set_scloff(p);
for (j = 0; j < l; j++)
call->set_req(p++, in_map->in(k+j));
// Finish the new jvms.
out_jvms->set_endoff(p);
@ -864,6 +871,7 @@ void GraphKit::add_safepoint_edges(SafePointNode* call, bool must_throw) {
assert(out_jvms->depth() == in_jvms->depth(), "depth must match");
assert(out_jvms->loc_size() == in_jvms->loc_size(), "size must match");
assert(out_jvms->mon_size() == in_jvms->mon_size(), "size must match");
assert(out_jvms->scl_size() == in_jvms->scl_size(), "size must match");
assert(out_jvms->debug_size() == in_jvms->debug_size(), "size must match");
// Update the two tail pointers in parallel.
@ -2914,10 +2922,22 @@ Node* GraphKit::new_instance(Node* klass_node,
const TypeOopPtr* oop_type = tklass->as_instance_type();
// Now generate allocation code
// With escape analysis, the entire memory state is needed to be able to
// eliminate the allocation. If the allocations cannot be eliminated, this
// will be optimized to the raw slice when the allocation is expanded.
Node *mem;
if (C->do_escape_analysis()) {
mem = reset_memory();
set_all_memory(mem);
} else {
mem = memory(Compile::AliasIdxRaw);
}
AllocateNode* alloc
= new (C, AllocateNode::ParmLimit)
AllocateNode(C, AllocateNode::alloc_type(),
control(), memory(Compile::AliasIdxRaw), i_o(),
control(), mem, i_o(),
size, klass_node,
initial_slow_test);
@ -3048,11 +3068,23 @@ Node* GraphKit::new_array(Node* klass_node, // array klass (maybe variable)
}
// Now generate allocation code
// With escape analysis, the entire memory state is needed to be able to
// eliminate the allocation. If the allocations cannot be eliminated, this
// will be optimized to the raw slice when the allocation is expanded.
Node *mem;
if (C->do_escape_analysis()) {
mem = reset_memory();
set_all_memory(mem);
} else {
mem = memory(Compile::AliasIdxRaw);
}
// Create the AllocateArrayNode and its result projections
AllocateArrayNode* alloc
= new (C, AllocateArrayNode::ParmLimit)
AllocateArrayNode(C, AllocateArrayNode::alloc_type(),
control(), memory(Compile::AliasIdxRaw), i_o(),
control(), mem, i_o(),
size, klass_node,
initial_slow_test,
length);

@ -36,7 +36,8 @@ const RegMask &BoxLockNode::out_RegMask() const {
uint BoxLockNode::size_of() const { return sizeof(*this); }
BoxLockNode::BoxLockNode( int slot ) : Node( Compile::current()->root() ), _slot(slot) {
BoxLockNode::BoxLockNode( int slot ) : Node( Compile::current()->root() ),
_slot(slot), _is_eliminated(false) {
init_class_id(Class_BoxLock);
init_flags(Flag_rematerialize);
OptoReg::Name reg = OptoReg::stack2reg(_slot);

@ -27,6 +27,7 @@ class BoxLockNode : public Node {
public:
const int _slot;
RegMask _inmask;
bool _is_eliminated; // indicates this lock was safely eliminated
BoxLockNode( int lock );
virtual int Opcode() const;
@ -42,6 +43,10 @@ public:
static OptoReg::Name stack_slot(Node* box_node);
bool is_eliminated() { return _is_eliminated; }
// mark lock as eliminated.
void set_eliminated() { _is_eliminated = true; }
#ifndef PRODUCT
virtual void format( PhaseRegAlloc *, outputStream *st ) const;
virtual void dump_spec(outputStream *st) const { st->print(" Lock %d",_slot); }

@ -29,10 +29,26 @@
//------------------------------split_thru_phi---------------------------------
// Split Node 'n' through merge point if there is enough win.
Node *PhaseIdealLoop::split_thru_phi( Node *n, Node *region, int policy ) {
if (n->Opcode() == Op_ConvI2L && n->bottom_type() != TypeLong::LONG) {
// ConvI2L may have type information on it which is unsafe to push up
// so disable this for now
return NULL;
}
int wins = 0;
assert( !n->is_CFG(), "" );
assert( region->is_Region(), "" );
Node *phi = new (C, region->req()) PhiNode( region, n->bottom_type() );
const Type* type = n->bottom_type();
const TypeOopPtr *t_oop = _igvn.type(n)->isa_oopptr();
Node *phi;
if( t_oop != NULL && t_oop->is_instance_field() ) {
int iid = t_oop->instance_id();
int index = C->get_alias_index(t_oop);
int offset = t_oop->offset();
phi = new (C,region->req()) PhiNode(region, type, NULL, iid, index, offset);
} else {
phi = new (C,region->req()) PhiNode(region, type);
}
uint old_unique = C->unique();
for( uint i = 1; i < region->req(); i++ ) {
Node *x;

@ -54,15 +54,30 @@ void PhaseMacroExpand::copy_call_debug_info(CallNode *oldcall, CallNode * newcal
uint new_dbg_start = newcall->tf()->domain()->cnt();
int jvms_adj = new_dbg_start - old_dbg_start;
assert (new_dbg_start == newcall->req(), "argument count mismatch");
Dict* sosn_map = new Dict(cmpkey,hashkey);
for (uint i = old_dbg_start; i < oldcall->req(); i++) {
newcall->add_req(oldcall->in(i));
Node* old_in = oldcall->in(i);
// Clone old SafePointScalarObjectNodes, adjusting their field contents.
if (old_in->is_SafePointScalarObject()) {
SafePointScalarObjectNode* old_sosn = old_in->as_SafePointScalarObject();
uint old_unique = C->unique();
Node* new_in = old_sosn->clone(jvms_adj, sosn_map);
if (old_unique != C->unique()) {
new_in = transform_later(new_in); // Register new node.
}
old_in = new_in;
}
newcall->add_req(old_in);
}
newcall->set_jvms(oldcall->jvms());
for (JVMState *jvms = newcall->jvms(); jvms != NULL; jvms = jvms->caller()) {
jvms->set_map(newcall);
jvms->set_locoff(jvms->locoff()+jvms_adj);
jvms->set_stkoff(jvms->stkoff()+jvms_adj);
jvms->set_monoff(jvms->monoff()+jvms_adj);
jvms->set_scloff(jvms->scloff()+jvms_adj);
jvms->set_endoff(jvms->endoff()+jvms_adj);
}
}
@ -166,6 +181,622 @@ void PhaseMacroExpand::extract_call_projections(CallNode *call) {
}
// Eliminate a card mark sequence. p2x is a ConvP2XNode
void PhaseMacroExpand::eliminate_card_mark(Node *p2x) {
assert(p2x->Opcode() == Op_CastP2X, "ConvP2XNode required");
Node *shift = p2x->unique_out();
Node *addp = shift->unique_out();
for (DUIterator_Last jmin, j = addp->last_outs(jmin); j >= jmin; --j) {
Node *st = addp->last_out(j);
assert(st->is_Store(), "store required");
_igvn.replace_node(st, st->in(MemNode::Memory));
}
}
// Search for a memory operation for the specified memory slice.
static Node *scan_mem_chain(Node *mem, int alias_idx, int offset, Node *start_mem, Node *alloc) {
Node *orig_mem = mem;
Node *alloc_mem = alloc->in(TypeFunc::Memory);
while (true) {
if (mem == alloc_mem || mem == start_mem ) {
return mem; // hit one of our sentinals
} else if (mem->is_MergeMem()) {
mem = mem->as_MergeMem()->memory_at(alias_idx);
} else if (mem->is_Proj() && mem->as_Proj()->_con == TypeFunc::Memory) {
Node *in = mem->in(0);
// we can safely skip over safepoints, calls, locks and membars because we
// already know that the object is safe to eliminate.
if (in->is_Initialize() && in->as_Initialize()->allocation() == alloc) {
return in;
} else if (in->is_Call() || in->is_MemBar()) {
mem = in->in(TypeFunc::Memory);
} else {
assert(false, "unexpected projection");
}
} else if (mem->is_Store()) {
const TypePtr* atype = mem->as_Store()->adr_type();
int adr_idx = Compile::current()->get_alias_index(atype);
if (adr_idx == alias_idx) {
assert(atype->isa_oopptr(), "address type must be oopptr");
int adr_offset = atype->offset();
uint adr_iid = atype->is_oopptr()->instance_id();
// Array elements references have the same alias_idx
// but different offset and different instance_id.
if (adr_offset == offset && adr_iid == alloc->_idx)
return mem;
} else {
assert(adr_idx == Compile::AliasIdxRaw, "address must match or be raw");
}
mem = mem->in(MemNode::Memory);
} else {
return mem;
}
if (mem == orig_mem)
return mem;
}
}
//
// Given a Memory Phi, compute a value Phi containing the values from stores
// on the input paths.
// Note: this function is recursive, its depth is limied by the "level" argument
// Returns the computed Phi, or NULL if it cannot compute it.
Node *PhaseMacroExpand::value_from_mem_phi(Node *mem, BasicType ft, const Type *phi_type, const TypeOopPtr *adr_t, Node *alloc, int level) {
if (level <= 0) {
return NULL;
}
int alias_idx = C->get_alias_index(adr_t);
int offset = adr_t->offset();
int instance_id = adr_t->instance_id();
Node *start_mem = C->start()->proj_out(TypeFunc::Memory);
Node *alloc_mem = alloc->in(TypeFunc::Memory);
uint length = mem->req();
GrowableArray <Node *> values(length, length, NULL);
for (uint j = 1; j < length; j++) {
Node *in = mem->in(j);
if (in == NULL || in->is_top()) {
values.at_put(j, in);
} else {
Node *val = scan_mem_chain(in, alias_idx, offset, start_mem, alloc);
if (val == start_mem || val == alloc_mem) {
// hit a sentinel, return appropriate 0 value
values.at_put(j, _igvn.zerocon(ft));
continue;
}
if (val->is_Initialize()) {
val = val->as_Initialize()->find_captured_store(offset, type2aelembytes(ft), &_igvn);
}
if (val == NULL) {
return NULL; // can't find a value on this path
}
if (val == mem) {
values.at_put(j, mem);
} else if (val->is_Store()) {
values.at_put(j, val->in(MemNode::ValueIn));
} else if(val->is_Proj() && val->in(0) == alloc) {
values.at_put(j, _igvn.zerocon(ft));
} else if (val->is_Phi()) {
// Check if an appropriate node already exists.
Node* region = val->in(0);
Node* old_phi = NULL;
for (DUIterator_Fast kmax, k = region->fast_outs(kmax); k < kmax; k++) {
Node* phi = region->fast_out(k);
if (phi->is_Phi() && phi != val &&
phi->as_Phi()->is_same_inst_field(phi_type, instance_id, alias_idx, offset)) {
old_phi = phi;
break;
}
}
if (old_phi == NULL) {
val = value_from_mem_phi(val, ft, phi_type, adr_t, alloc, level-1);
if (val == NULL) {
return NULL;
}
values.at_put(j, val);
} else {
values.at_put(j, old_phi);
}
} else {
return NULL; // unknown node on this path
}
}
}
// create a new Phi for the value
PhiNode *phi = new (C, length) PhiNode(mem->in(0), phi_type, NULL, instance_id, alias_idx, offset);
for (uint j = 1; j < length; j++) {
if (values.at(j) == mem) {
phi->init_req(j, phi);
} else {
phi->init_req(j, values.at(j));
}
}
transform_later(phi);
return phi;
}
// Search the last value stored into the object's field.
Node *PhaseMacroExpand::value_from_mem(Node *sfpt_mem, BasicType ft, const Type *ftype, const TypeOopPtr *adr_t, Node *alloc) {
assert(adr_t->is_instance_field(), "instance required");
uint instance_id = adr_t->instance_id();
assert(instance_id == alloc->_idx, "wrong allocation");
int alias_idx = C->get_alias_index(adr_t);
int offset = adr_t->offset();
Node *start_mem = C->start()->proj_out(TypeFunc::Memory);
Node *alloc_ctrl = alloc->in(TypeFunc::Control);
Node *alloc_mem = alloc->in(TypeFunc::Memory);
VectorSet visited(Thread::current()->resource_area());
bool done = sfpt_mem == alloc_mem;
Node *mem = sfpt_mem;
while (!done) {
if (visited.test_set(mem->_idx)) {
return NULL; // found a loop, give up
}
mem = scan_mem_chain(mem, alias_idx, offset, start_mem, alloc);
if (mem == start_mem || mem == alloc_mem) {
done = true; // hit a sentinel, return appropriate 0 value
} else if (mem->is_Initialize()) {
mem = mem->as_Initialize()->find_captured_store(offset, type2aelembytes(ft), &_igvn);
if (mem == NULL) {
done = true; // Something go wrong.
} else if (mem->is_Store()) {
const TypePtr* atype = mem->as_Store()->adr_type();
assert(C->get_alias_index(atype) == Compile::AliasIdxRaw, "store is correct memory slice");
done = true;
}
} else if (mem->is_Store()) {
const TypeOopPtr* atype = mem->as_Store()->adr_type()->isa_oopptr();
assert(atype != NULL, "address type must be oopptr");
assert(C->get_alias_index(atype) == alias_idx &&
atype->is_instance_field() && atype->offset() == offset &&
atype->instance_id() == instance_id, "store is correct memory slice");
done = true;
} else if (mem->is_Phi()) {
// try to find a phi's unique input
Node *unique_input = NULL;
Node *top = C->top();
for (uint i = 1; i < mem->req(); i++) {
Node *n = scan_mem_chain(mem->in(i), alias_idx, offset, start_mem, alloc);
if (n == NULL || n == top || n == mem) {
continue;
} else if (unique_input == NULL) {
unique_input = n;
} else if (unique_input != n) {
unique_input = top;
break;
}
}
if (unique_input != NULL && unique_input != top) {
mem = unique_input;
} else {
done = true;
}
} else {
assert(false, "unexpected node");
}
}
if (mem != NULL) {
if (mem == start_mem || mem == alloc_mem) {
// hit a sentinel, return appropriate 0 value
return _igvn.zerocon(ft);
} else if (mem->is_Store()) {
return mem->in(MemNode::ValueIn);
} else if (mem->is_Phi()) {
// attempt to produce a Phi reflecting the values on the input paths of the Phi
Node * phi = value_from_mem_phi(mem, ft, ftype, adr_t, alloc, 8);
if (phi != NULL) {
return phi;
}
}
}
// Something go wrong.
return NULL;
}
// Check the possibility of scalar replacement.
bool PhaseMacroExpand::can_eliminate_allocation(AllocateNode *alloc, GrowableArray <SafePointNode *>& safepoints) {
// Scan the uses of the allocation to check for anything that would
// prevent us from eliminating it.
NOT_PRODUCT( const char* fail_eliminate = NULL; )
DEBUG_ONLY( Node* disq_node = NULL; )
bool can_eliminate = true;
Node* res = alloc->result_cast();
const TypeOopPtr* res_type = NULL;
if (res == NULL) {
// All users were eliminated.
} else if (!res->is_CheckCastPP()) {
alloc->_is_scalar_replaceable = false; // don't try again
NOT_PRODUCT(fail_eliminate = "Allocation does not have unique CheckCastPP";)
can_eliminate = false;
} else {
res_type = _igvn.type(res)->isa_oopptr();
if (res_type == NULL) {
NOT_PRODUCT(fail_eliminate = "Neither instance or array allocation";)
can_eliminate = false;
} else if (res_type->isa_aryptr()) {
int length = alloc->in(AllocateNode::ALength)->find_int_con(-1);
if (length < 0) {
NOT_PRODUCT(fail_eliminate = "Array's size is not constant";)
can_eliminate = false;
}
}
}
if (can_eliminate && res != NULL) {
for (DUIterator_Fast jmax, j = res->fast_outs(jmax);
j < jmax && can_eliminate; j++) {
Node* use = res->fast_out(j);
if (use->is_AddP()) {
const TypePtr* addp_type = _igvn.type(use)->is_ptr();
int offset = addp_type->offset();
if (offset == Type::OffsetTop || offset == Type::OffsetBot) {
NOT_PRODUCT(fail_eliminate = "Undefined field referrence";)
can_eliminate = false;
break;
}
for (DUIterator_Fast kmax, k = use->fast_outs(kmax);
k < kmax && can_eliminate; k++) {
Node* n = use->fast_out(k);
if (!n->is_Store() && n->Opcode() != Op_CastP2X) {
DEBUG_ONLY(disq_node = n;)
if (n->is_Load()) {
NOT_PRODUCT(fail_eliminate = "Field load";)
} else {
NOT_PRODUCT(fail_eliminate = "Not store field referrence";)
}
can_eliminate = false;
}
}
} else if (use->is_SafePoint()) {
SafePointNode* sfpt = use->as_SafePoint();
if (sfpt->has_non_debug_use(res)) {
// Object is passed as argument.
DEBUG_ONLY(disq_node = use;)
NOT_PRODUCT(fail_eliminate = "Object is passed as argument";)
can_eliminate = false;
}
Node* sfptMem = sfpt->memory();
if (sfptMem == NULL || sfptMem->is_top()) {
DEBUG_ONLY(disq_node = use;)
NOT_PRODUCT(fail_eliminate = "NULL or TOP memory";)
can_eliminate = false;
} else {
safepoints.append_if_missing(sfpt);
}
} else if (use->Opcode() != Op_CastP2X) { // CastP2X is used by card mark
if (use->is_Phi()) {
if (use->outcnt() == 1 && use->unique_out()->Opcode() == Op_Return) {
NOT_PRODUCT(fail_eliminate = "Object is return value";)
} else {
NOT_PRODUCT(fail_eliminate = "Object is referenced by Phi";)
}
DEBUG_ONLY(disq_node = use;)
} else {
if (use->Opcode() == Op_Return) {
NOT_PRODUCT(fail_eliminate = "Object is return value";)
}else {
NOT_PRODUCT(fail_eliminate = "Object is referenced by node";)
}
DEBUG_ONLY(disq_node = use;)
}
can_eliminate = false;
}
}
}
#ifndef PRODUCT
if (PrintEliminateAllocations) {
if (can_eliminate) {
tty->print("Scalar ");
if (res == NULL)
alloc->dump();
else
res->dump();
} else {
tty->print("NotScalar (%s)", fail_eliminate);
if (res == NULL)
alloc->dump();
else
res->dump();
#ifdef ASSERT
if (disq_node != NULL) {
tty->print(" >>>> ");
disq_node->dump();
}
#endif /*ASSERT*/
}
}
#endif
return can_eliminate;
}
// Do scalar replacement.
bool PhaseMacroExpand::scalar_replacement(AllocateNode *alloc, GrowableArray <SafePointNode *>& safepoints) {
GrowableArray <SafePointNode *> safepoints_done;
ciKlass* klass = NULL;
ciInstanceKlass* iklass = NULL;
int nfields = 0;
int array_base;
int element_size;
BasicType basic_elem_type;
ciType* elem_type;
Node* res = alloc->result_cast();
const TypeOopPtr* res_type = NULL;
if (res != NULL) { // Could be NULL when there are no users
res_type = _igvn.type(res)->isa_oopptr();
}
if (res != NULL) {
klass = res_type->klass();
if (res_type->isa_instptr()) {
// find the fields of the class which will be needed for safepoint debug information
assert(klass->is_instance_klass(), "must be an instance klass.");
iklass = klass->as_instance_klass();
nfields = iklass->nof_nonstatic_fields();
} else {
// find the array's elements which will be needed for safepoint debug information
nfields = alloc->in(AllocateNode::ALength)->find_int_con(-1);
assert(klass->is_array_klass() && nfields >= 0, "must be an array klass.");
elem_type = klass->as_array_klass()->element_type();
basic_elem_type = elem_type->basic_type();
array_base = arrayOopDesc::base_offset_in_bytes(basic_elem_type);
element_size = type2aelembytes(basic_elem_type);
}
}
//
// Process the safepoint uses
//
while (safepoints.length() > 0) {
SafePointNode* sfpt = safepoints.pop();
Node* mem = sfpt->memory();
uint first_ind = sfpt->req();
SafePointScalarObjectNode* sobj = new (C, 1) SafePointScalarObjectNode(res_type,
#ifdef ASSERT
alloc,
#endif
first_ind, nfields);
sobj->init_req(0, sfpt->in(TypeFunc::Control));
transform_later(sobj);
// Scan object's fields adding an input to the safepoint for each field.
for (int j = 0; j < nfields; j++) {
int offset;
ciField* field = NULL;
if (iklass != NULL) {
field = iklass->nonstatic_field_at(j);
offset = field->offset();
elem_type = field->type();
basic_elem_type = field->layout_type();
} else {
offset = array_base + j * element_size;
}
const Type *field_type;
// The next code is taken from Parse::do_get_xxx().
if (basic_elem_type == T_OBJECT) {
if (!elem_type->is_loaded()) {
field_type = TypeInstPtr::BOTTOM;
} else if (field != NULL && field->is_constant()) {
// This can happen if the constant oop is non-perm.
ciObject* con = field->constant_value().as_object();
// Do not "join" in the previous type; it doesn't add value,
// and may yield a vacuous result if the field is of interface type.
field_type = TypeOopPtr::make_from_constant(con)->isa_oopptr();
assert(field_type != NULL, "field singleton type must be consistent");
} else {
field_type = TypeOopPtr::make_from_klass(elem_type->as_klass());
}
} else {
field_type = Type::get_const_basic_type(basic_elem_type);
}
const TypeOopPtr *field_addr_type = res_type->add_offset(offset)->isa_oopptr();
Node *field_val = value_from_mem(mem, basic_elem_type, field_type, field_addr_type, alloc);
if (field_val == NULL) {
// we weren't able to find a value for this field,
// give up on eliminating this allocation
alloc->_is_scalar_replaceable = false; // don't try again
// remove any extra entries we added to the safepoint
uint last = sfpt->req() - 1;
for (int k = 0; k < j; k++) {
sfpt->del_req(last--);
}
// rollback processed safepoints
while (safepoints_done.length() > 0) {
SafePointNode* sfpt_done = safepoints_done.pop();
// remove any extra entries we added to the safepoint
last = sfpt_done->req() - 1;
for (int k = 0; k < nfields; k++) {
sfpt_done->del_req(last--);
}
JVMState *jvms = sfpt_done->jvms();
jvms->set_endoff(sfpt_done->req());
// Now make a pass over the debug information replacing any references
// to SafePointScalarObjectNode with the allocated object.
int start = jvms->debug_start();
int end = jvms->debug_end();
for (int i = start; i < end; i++) {
if (sfpt_done->in(i)->is_SafePointScalarObject()) {
SafePointScalarObjectNode* scobj = sfpt_done->in(i)->as_SafePointScalarObject();
if (scobj->first_index() == sfpt_done->req() &&
scobj->n_fields() == (uint)nfields) {
assert(scobj->alloc() == alloc, "sanity");
sfpt_done->set_req(i, res);
}
}
}
}
#ifndef PRODUCT
if (PrintEliminateAllocations) {
if (field != NULL) {
tty->print("=== At SafePoint node %d can't find value of Field: ",
sfpt->_idx);
field->print();
int field_idx = C->get_alias_index(field_addr_type);
tty->print(" (alias_idx=%d)", field_idx);
} else { // Array's element
tty->print("=== At SafePoint node %d can't find value of array element [%d]",
sfpt->_idx, j);
}
tty->print(", which prevents elimination of: ");
if (res == NULL)
alloc->dump();
else
res->dump();
}
#endif
return false;
}
sfpt->add_req(field_val);
}
JVMState *jvms = sfpt->jvms();
jvms->set_endoff(sfpt->req());
// Now make a pass over the debug information replacing any references
// to the allocated object with "sobj"
int start = jvms->debug_start();
int end = jvms->debug_end();
for (int i = start; i < end; i++) {
if (sfpt->in(i) == res) {
sfpt->set_req(i, sobj);
}
}
safepoints_done.append_if_missing(sfpt); // keep it for rollback
}
return true;
}
// Process users of eliminated allocation.
void PhaseMacroExpand::process_users_of_allocation(AllocateNode *alloc) {
Node* res = alloc->result_cast();
if (res != NULL) {
for (DUIterator_Last jmin, j = res->last_outs(jmin); j >= jmin; ) {
Node *use = res->last_out(j);
uint oc1 = res->outcnt();
if (use->is_AddP()) {
for (DUIterator_Last kmin, k = use->last_outs(kmin); k >= kmin; ) {
Node *n = use->last_out(k);
uint oc2 = use->outcnt();
if (n->is_Store()) {
_igvn.replace_node(n, n->in(MemNode::Memory));
} else {
assert( n->Opcode() == Op_CastP2X, "CastP2X required");
eliminate_card_mark(n);
}
k -= (oc2 - use->outcnt());
}
} else {
assert( !use->is_SafePoint(), "safepoint uses must have been already elimiated");
assert( use->Opcode() == Op_CastP2X, "CastP2X required");
eliminate_card_mark(use);
}
j -= (oc1 - res->outcnt());
}
assert(res->outcnt() == 0, "all uses of allocated objects must be deleted");
_igvn.remove_dead_node(res);
}
//
// Process other users of allocation's projections
//
if (_resproj != NULL && _resproj->outcnt() != 0) {
for (DUIterator_Last jmin, j = _resproj->last_outs(jmin); j >= jmin; ) {
Node *use = _resproj->last_out(j);
uint oc1 = _resproj->outcnt();
if (use->is_Initialize()) {
// Eliminate Initialize node.
InitializeNode *init = use->as_Initialize();
assert(init->outcnt() <= 2, "only a control and memory projection expected");
Node *ctrl_proj = init->proj_out(TypeFunc::Control);
if (ctrl_proj != NULL) {
assert(init->in(TypeFunc::Control) == _fallthroughcatchproj, "allocation control projection");
_igvn.replace_node(ctrl_proj, _fallthroughcatchproj);
}
Node *mem_proj = init->proj_out(TypeFunc::Memory);
if (mem_proj != NULL) {
Node *mem = init->in(TypeFunc::Memory);
#ifdef ASSERT
if (mem->is_MergeMem()) {
assert(mem->in(TypeFunc::Memory) == _memproj_fallthrough, "allocation memory projection");
} else {
assert(mem == _memproj_fallthrough, "allocation memory projection");
}
#endif
_igvn.replace_node(mem_proj, mem);
}
} else if (use->is_AddP()) {
// raw memory addresses used only by the initialization
_igvn.hash_delete(use);
_igvn.subsume_node(use, C->top());
} else {
assert(false, "only Initialize or AddP expected");
}
j -= (oc1 - _resproj->outcnt());
}
}
if (_fallthroughcatchproj != NULL) {
_igvn.replace_node(_fallthroughcatchproj, alloc->in(TypeFunc::Control));
}
if (_memproj_fallthrough != NULL) {
_igvn.replace_node(_memproj_fallthrough, alloc->in(TypeFunc::Memory));
}
if (_memproj_catchall != NULL) {
_igvn.replace_node(_memproj_catchall, C->top());
}
if (_ioproj_fallthrough != NULL) {
_igvn.replace_node(_ioproj_fallthrough, alloc->in(TypeFunc::I_O));
}
if (_ioproj_catchall != NULL) {
_igvn.replace_node(_ioproj_catchall, C->top());
}
if (_catchallcatchproj != NULL) {
_igvn.replace_node(_catchallcatchproj, C->top());
}
}
bool PhaseMacroExpand::eliminate_allocate_node(AllocateNode *alloc) {
if (!EliminateAllocations || !alloc->_is_scalar_replaceable) {
return false;
}
extract_call_projections(alloc);
GrowableArray <SafePointNode *> safepoints;
if (!can_eliminate_allocation(alloc, safepoints)) {
return false;
}
if (!scalar_replacement(alloc, safepoints)) {
return false;
}
process_users_of_allocation(alloc);
#ifndef PRODUCT
if (PrintEliminateAllocations) {
if (alloc->is_AllocateArray())
tty->print_cr("++++ Eliminated: %d AllocateArray", alloc->_idx);
else
tty->print_cr("++++ Eliminated: %d Allocate", alloc->_idx);
}
#endif
return true;
}
//---------------------------set_eden_pointers-------------------------
void PhaseMacroExpand::set_eden_pointers(Node* &eden_top_adr, Node* &eden_end_adr) {
@ -270,6 +901,13 @@ void PhaseMacroExpand::expand_allocate_common(
Node* klass_node = alloc->in(AllocateNode::KlassNode);
Node* initial_slow_test = alloc->in(AllocateNode::InitialTest);
// With escape analysis, the entire memory state was needed to be able to
// eliminate the allocation. Since the allocations cannot be eliminated,
// optimize it to the raw slice.
if (mem->is_MergeMem()) {
mem = mem->as_MergeMem()->memory_at(Compile::AliasIdxRaw);
}
Node* eden_top_adr;
Node* eden_end_adr;
set_eden_pointers(eden_top_adr, eden_end_adr);
@ -813,27 +1451,87 @@ void PhaseMacroExpand::expand_allocate_array(AllocateArrayNode *alloc) {
// Note: The membar's associated with the lock/unlock are currently not
// eliminated. This should be investigated as a future enhancement.
//
void PhaseMacroExpand::eliminate_locking_node(AbstractLockNode *alock) {
Node* mem = alock->in(TypeFunc::Memory);
bool PhaseMacroExpand::eliminate_locking_node(AbstractLockNode *alock) {
if (!alock->is_eliminated()) {
return false;
}
// Mark the box lock as eliminated if all correspondent locks are eliminated
// to construct correct debug info.
BoxLockNode* box = alock->box_node()->as_BoxLock();
if (!box->is_eliminated()) {
bool eliminate = true;
for (DUIterator_Fast imax, i = box->fast_outs(imax); i < imax; i++) {
Node *lck = box->fast_out(i);
if (lck->is_Lock() && !lck->as_AbstractLock()->is_eliminated()) {
eliminate = false;
break;
}
}
if (eliminate)
box->set_eliminated();
}
#ifndef PRODUCT
if (PrintEliminateLocks) {
if (alock->is_Lock()) {
tty->print_cr("++++ Eliminating: %d Lock", alock->_idx);
} else {
tty->print_cr("++++ Eliminating: %d Unlock", alock->_idx);
}
}
#endif
Node* mem = alock->in(TypeFunc::Memory);
Node* ctrl = alock->in(TypeFunc::Control);
extract_call_projections(alock);
// There are 2 projections from the lock. The lock node will
// be deleted when its last use is subsumed below.
assert(alock->outcnt() == 2 &&
_fallthroughproj != NULL &&
_memproj_fallthrough != NULL,
"Unexpected projections from Lock/Unlock");
Node* fallthroughproj = _fallthroughproj;
Node* memproj_fallthrough = _memproj_fallthrough;
// The memory projection from a lock/unlock is RawMem
// The input to a Lock is merged memory, so extract its RawMem input
// (unless the MergeMem has been optimized away.)
if (alock->is_Lock()) {
if (mem->is_MergeMem())
mem = mem->as_MergeMem()->in(Compile::AliasIdxRaw);
// Seach for MemBarAcquire node and delete it also.
MemBarNode* membar = fallthroughproj->unique_ctrl_out()->as_MemBar();
assert(membar != NULL && membar->Opcode() == Op_MemBarAcquire, "");
Node* ctrlproj = membar->proj_out(TypeFunc::Control);
Node* memproj = membar->proj_out(TypeFunc::Memory);
_igvn.hash_delete(ctrlproj);
_igvn.subsume_node(ctrlproj, fallthroughproj);
_igvn.hash_delete(memproj);
_igvn.subsume_node(memproj, memproj_fallthrough);
}
extract_call_projections(alock);
// There are 2 projections from the lock. The lock node will
// be deleted when its last use is subsumed below.
assert(alock->outcnt() == 2 && _fallthroughproj != NULL &&
_memproj_fallthrough != NULL, "Unexpected projections from Lock/Unlock");
_igvn.hash_delete(_fallthroughproj);
_igvn.subsume_node(_fallthroughproj, alock->in(TypeFunc::Control));
_igvn.hash_delete(_memproj_fallthrough);
_igvn.subsume_node(_memproj_fallthrough, mem);
return;
// Seach for MemBarRelease node and delete it also.
if (alock->is_Unlock() && ctrl != NULL && ctrl->is_Proj() &&
ctrl->in(0)->is_MemBar()) {
MemBarNode* membar = ctrl->in(0)->as_MemBar();
assert(membar->Opcode() == Op_MemBarRelease &&
mem->is_Proj() && membar == mem->in(0), "");
_igvn.hash_delete(fallthroughproj);
_igvn.subsume_node(fallthroughproj, ctrl);
_igvn.hash_delete(memproj_fallthrough);
_igvn.subsume_node(memproj_fallthrough, mem);
fallthroughproj = ctrl;
memproj_fallthrough = mem;
ctrl = membar->in(TypeFunc::Control);
mem = membar->in(TypeFunc::Memory);
}
_igvn.hash_delete(fallthroughproj);
_igvn.subsume_node(fallthroughproj, ctrl);
_igvn.hash_delete(memproj_fallthrough);
_igvn.subsume_node(memproj_fallthrough, mem);
return true;
}
@ -844,12 +1542,7 @@ void PhaseMacroExpand::expand_lock_node(LockNode *lock) {
Node* mem = lock->in(TypeFunc::Memory);
Node* obj = lock->obj_node();
Node* box = lock->box_node();
Node *flock = lock->fastlock_node();
if (lock->is_eliminated()) {
eliminate_locking_node(lock);
return;
}
Node* flock = lock->fastlock_node();
// Make the merge point
Node *region = new (C, 3) RegionNode(3);
@ -898,17 +1591,11 @@ void PhaseMacroExpand::expand_lock_node(LockNode *lock) {
//------------------------------expand_unlock_node----------------------
void PhaseMacroExpand::expand_unlock_node(UnlockNode *unlock) {
Node *ctrl = unlock->in(TypeFunc::Control);
Node* ctrl = unlock->in(TypeFunc::Control);
Node* mem = unlock->in(TypeFunc::Memory);
Node* obj = unlock->obj_node();
Node* box = unlock->box_node();
if (unlock->is_eliminated()) {
eliminate_locking_node(unlock);
return;
}
// No need for a null check on unlock
// Make the merge point
@ -958,14 +1645,41 @@ void PhaseMacroExpand::expand_unlock_node(UnlockNode *unlock) {
bool PhaseMacroExpand::expand_macro_nodes() {
if (C->macro_count() == 0)
return false;
// Make sure expansion will not cause node limit to be exceeded. Worst case is a
// macro node gets expanded into about 50 nodes. Allow 50% more for optimization
// attempt to eliminate allocations
bool progress = true;
while (progress) {
progress = false;
for (int i = C->macro_count(); i > 0; i--) {
Node * n = C->macro_node(i-1);
bool success = false;
debug_only(int old_macro_count = C->macro_count(););
switch (n->class_id()) {
case Node::Class_Allocate:
case Node::Class_AllocateArray:
success = eliminate_allocate_node(n->as_Allocate());
break;
case Node::Class_Lock:
case Node::Class_Unlock:
success = eliminate_locking_node(n->as_AbstractLock());
break;
default:
assert(false, "unknown node type in macro list");
}
assert(success == (C->macro_count() < old_macro_count), "elimination reduces macro count");
progress = progress || success;
}
}
// Make sure expansion will not cause node limit to be exceeded.
// Worst case is a macro node gets expanded into about 50 nodes.
// Allow 50% more for optimization.
if (C->check_node_count(C->macro_count() * 75, "out of nodes before macro expansion" ) )
return true;
// expand "macro" nodes
// nodes are removed from the macro list as they are processed
while (C->macro_count() > 0) {
Node * n = C->macro_node(0);
int macro_count = C->macro_count();
Node * n = C->macro_node(macro_count-1);
assert(n->is_macro(), "only macro nodes expected here");
if (_igvn.type(n) == Type::TOP || n->in(0)->is_top() ) {
// node is unreachable, so don't try to expand it
@ -988,6 +1702,7 @@ bool PhaseMacroExpand::expand_macro_nodes() {
default:
assert(false, "unknown node type in macro list");
}
assert(C->macro_count() < macro_count, "must have deleted a node from macro list");
if (C->failing()) return true;
}
_igvn.optimize();

@ -78,7 +78,16 @@ private:
Node* length,
const TypeFunc* slow_call_type,
address slow_call_address);
void eliminate_locking_node(AbstractLockNode *alock);
Node *value_from_mem(Node *mem, BasicType ft, const Type *ftype, const TypeOopPtr *adr_t, Node *alloc);
Node *value_from_mem_phi(Node *mem, BasicType ft, const Type *ftype, const TypeOopPtr *adr_t, Node *alloc, int level);
bool eliminate_allocate_node(AllocateNode *alloc);
bool can_eliminate_allocation(AllocateNode *alloc, GrowableArray <SafePointNode *>& safepoints);
bool scalar_replacement(AllocateNode *alloc, GrowableArray <SafePointNode *>& safepoints_done);
void process_users_of_allocation(AllocateNode *alloc);
void eliminate_card_mark(Node *cm);
bool eliminate_locking_node(AbstractLockNode *alock);
void expand_lock_node(LockNode *lock);
void expand_unlock_node(UnlockNode *unlock);

@ -1647,6 +1647,7 @@ void Matcher::find_shared( Node *n ) {
case Op_Phi: // Treat Phis as shared roots
case Op_Parm:
case Op_Proj: // All handled specially during matching
case Op_SafePointScalarObject:
set_shared(n);
set_dontcare(n);
break;

@ -29,6 +29,8 @@
#include "incls/_precompiled.incl"
#include "incls/_memnode.cpp.incl"
static Node *step_through_mergemem(PhaseGVN *phase, MergeMemNode *mmem, const TypePtr *tp, const TypePtr *adr_check, outputStream *st);
//=============================================================================
uint MemNode::size_of() const { return sizeof(*this); }
@ -87,6 +89,112 @@ extern void print_alias_types();
#endif
Node *MemNode::optimize_simple_memory_chain(Node *mchain, const TypePtr *t_adr, PhaseGVN *phase) {
const TypeOopPtr *tinst = t_adr->isa_oopptr();
if (tinst == NULL || !tinst->is_instance_field())
return mchain; // don't try to optimize non-instance types
uint instance_id = tinst->instance_id();
Node *prev = NULL;
Node *result = mchain;
while (prev != result) {
prev = result;
// skip over a call which does not affect this memory slice
if (result->is_Proj() && result->as_Proj()->_con == TypeFunc::Memory) {
Node *proj_in = result->in(0);
if (proj_in->is_Call()) {
CallNode *call = proj_in->as_Call();
if (!call->may_modify(t_adr, phase)) {
result = call->in(TypeFunc::Memory);
}
} else if (proj_in->is_Initialize()) {
AllocateNode* alloc = proj_in->as_Initialize()->allocation();
// Stop if this is the initialization for the object instance which
// which contains this memory slice, otherwise skip over it.
if (alloc != NULL && alloc->_idx != instance_id) {
result = proj_in->in(TypeFunc::Memory);
}
} else if (proj_in->is_MemBar()) {
result = proj_in->in(TypeFunc::Memory);
}
} else if (result->is_MergeMem()) {
result = step_through_mergemem(phase, result->as_MergeMem(), t_adr, NULL, tty);
}
}
return result;
}
Node *MemNode::optimize_memory_chain(Node *mchain, const TypePtr *t_adr, PhaseGVN *phase) {
const TypeOopPtr *t_oop = t_adr->isa_oopptr();
bool is_instance = (t_oop != NULL) && t_oop->is_instance_field();
PhaseIterGVN *igvn = phase->is_IterGVN();
Node *result = mchain;
result = optimize_simple_memory_chain(result, t_adr, phase);
if (is_instance && igvn != NULL && result->is_Phi()) {
PhiNode *mphi = result->as_Phi();
assert(mphi->bottom_type() == Type::MEMORY, "memory phi required");
const TypePtr *t = mphi->adr_type();
if (t == TypePtr::BOTTOM || t == TypeRawPtr::BOTTOM) {
// clone the Phi with our address type
result = mphi->split_out_instance(t_adr, igvn);
} else {
assert(phase->C->get_alias_index(t) == phase->C->get_alias_index(t_adr), "correct memory chain");
}
}
return result;
}
static Node *step_through_mergemem(PhaseGVN *phase, MergeMemNode *mmem, const TypePtr *tp, const TypePtr *adr_check, outputStream *st) {
uint alias_idx = phase->C->get_alias_index(tp);
Node *mem = mmem;
#ifdef ASSERT
{
// Check that current type is consistent with the alias index used during graph construction
assert(alias_idx >= Compile::AliasIdxRaw, "must not be a bad alias_idx");
bool consistent = adr_check == NULL || adr_check->empty() ||
phase->C->must_alias(adr_check, alias_idx );
// Sometimes dead array references collapse to a[-1], a[-2], or a[-3]
if( !consistent && adr_check != NULL && !adr_check->empty() &&
tp->isa_aryptr() && tp->offset() == Type::OffsetBot &&
adr_check->isa_aryptr() && adr_check->offset() != Type::OffsetBot &&
( adr_check->offset() == arrayOopDesc::length_offset_in_bytes() ||
adr_check->offset() == oopDesc::klass_offset_in_bytes() ||
adr_check->offset() == oopDesc::mark_offset_in_bytes() ) ) {
// don't assert if it is dead code.
consistent = true;
}
if( !consistent ) {
st->print("alias_idx==%d, adr_check==", alias_idx);
if( adr_check == NULL ) {
st->print("NULL");
} else {
adr_check->dump();
}
st->cr();
print_alias_types();
assert(consistent, "adr_check must match alias idx");
}
}
#endif
// TypeInstPtr::NOTNULL+any is an OOP with unknown offset - generally
// means an array I have not precisely typed yet. Do not do any
// alias stuff with it any time soon.
const TypeOopPtr *tinst = tp->isa_oopptr();
if( tp->base() != Type::AnyPtr &&
!(tinst &&
tinst->klass()->is_java_lang_Object() &&
tinst->offset() == Type::OffsetBot) ) {
// compress paths and change unreachable cycles to TOP
// If not, we can update the input infinitely along a MergeMem cycle
// Equivalent code in PhiNode::Ideal
Node* m = phase->transform(mmem);
// If tranformed to a MergeMem, get the desired slice
// Otherwise the returned node represents memory for every slice
mem = (m->is_MergeMem())? m->as_MergeMem()->memory_at(alias_idx) : m;
// Update input if it is progress over what we have now
}
return mem;
}
//--------------------------Ideal_common---------------------------------------
// Look for degenerate control and memory inputs. Bypass MergeMem inputs.
// Unhook non-raw memories from complete (macro-expanded) initializations.
@ -119,48 +227,8 @@ Node *MemNode::Ideal_common(PhaseGVN *phase, bool can_reshape) {
if (mem->is_MergeMem()) {
MergeMemNode* mmem = mem->as_MergeMem();
const TypePtr *tp = t_adr->is_ptr();
uint alias_idx = phase->C->get_alias_index(tp);
#ifdef ASSERT
{
// Check that current type is consistent with the alias index used during graph construction
assert(alias_idx >= Compile::AliasIdxRaw, "must not be a bad alias_idx");
const TypePtr *adr_t = adr_type();
bool consistent = adr_t == NULL || adr_t->empty() || phase->C->must_alias(adr_t, alias_idx );
// Sometimes dead array references collapse to a[-1], a[-2], or a[-3]
if( !consistent && adr_t != NULL && !adr_t->empty() &&
tp->isa_aryptr() && tp->offset() == Type::OffsetBot &&
adr_t->isa_aryptr() && adr_t->offset() != Type::OffsetBot &&
( adr_t->offset() == arrayOopDesc::length_offset_in_bytes() ||
adr_t->offset() == oopDesc::klass_offset_in_bytes() ||
adr_t->offset() == oopDesc::mark_offset_in_bytes() ) ) {
// don't assert if it is dead code.
consistent = true;
}
if( !consistent ) {
tty->print("alias_idx==%d, adr_type()==", alias_idx); if( adr_t == NULL ) { tty->print("NULL"); } else { adr_t->dump(); }
tty->cr();
print_alias_types();
assert(consistent, "adr_type must match alias idx");
}
}
#endif
// TypeInstPtr::NOTNULL+any is an OOP with unknown offset - generally
// means an array I have not precisely typed yet. Do not do any
// alias stuff with it any time soon.
const TypeInstPtr *tinst = tp->isa_instptr();
if( tp->base() != Type::AnyPtr &&
!(tinst &&
tinst->klass()->is_java_lang_Object() &&
tinst->offset() == Type::OffsetBot) ) {
// compress paths and change unreachable cycles to TOP
// If not, we can update the input infinitely along a MergeMem cycle
// Equivalent code in PhiNode::Ideal
Node* m = phase->transform(mmem);
// If tranformed to a MergeMem, get the desired slice
// Otherwise the returned node represents memory for every slice
mem = (m->is_MergeMem())? m->as_MergeMem()->memory_at(alias_idx) : m;
// Update input if it is progress over what we have now
}
mem = step_through_mergemem(phase, mmem, tp, adr_type(), tty);
}
if (mem != old_mem) {
@ -254,6 +322,8 @@ Node* MemNode::find_previous_store(PhaseTransform* phase) {
if (offset == Type::OffsetBot)
return NULL; // cannot unalias unless there are precise offsets
const TypeOopPtr *addr_t = adr->bottom_type()->isa_oopptr();
intptr_t size_in_bytes = memory_size();
Node* mem = in(MemNode::Memory); // start searching here...
@ -333,6 +403,22 @@ Node* MemNode::find_previous_store(PhaseTransform* phase) {
return mem; // let caller handle steps (c), (d)
}
} else if (addr_t != NULL && addr_t->is_instance_field()) {
// Can't use optimize_simple_memory_chain() since it needs PhaseGVN.
if (mem->is_Proj() && mem->in(0)->is_Call()) {
CallNode *call = mem->in(0)->as_Call();
if (!call->may_modify(addr_t, phase)) {
mem = call->in(TypeFunc::Memory);
continue; // (a) advance through independent call memory
}
} else if (mem->is_Proj() && mem->in(0)->is_MemBar()) {
mem = mem->in(0)->in(TypeFunc::Memory);
continue; // (a) advance through independent MemBar memory
} else if (mem->is_MergeMem()) {
int alias_idx = phase->C->get_alias_index(adr_type());
mem = mem->as_MergeMem()->memory_at(alias_idx);
continue; // (a) advance through independent MergeMem memory
}
}
// Unless there is an explicit 'continue', we must bail out here,
@ -534,7 +620,10 @@ Node *MemNode::Ideal_DU_postCCP( PhaseCCP *ccp ) {
const Node* call = adr->in(0);
if (call->is_CallStaticJava()) {
const CallStaticJavaNode* call_java = call->as_CallStaticJava();
assert(call_java && call_java->method() == NULL, "must be runtime call");
const TypeTuple *r = call_java->tf()->range();
assert(r->cnt() > TypeFunc::Parms, "must return value");
const Type* ret_type = r->field_at(TypeFunc::Parms);
assert(ret_type && ret_type->isa_ptr(), "must return pointer");
// We further presume that this is one of
// new_instance_Java, new_array_Java, or
// the like, but do not assert for this.
@ -732,6 +821,21 @@ Node* MemNode::can_see_stored_value(Node* st, PhaseTransform* phase) const {
return NULL;
}
//----------------------is_instance_field_load_with_local_phi------------------
bool LoadNode::is_instance_field_load_with_local_phi(Node* ctrl) {
if( in(MemNode::Memory)->is_Phi() && in(MemNode::Memory)->in(0) == ctrl &&
in(MemNode::Address)->is_AddP() ) {
const TypeOopPtr* t_oop = in(MemNode::Address)->bottom_type()->isa_oopptr();
// Only instances.
if( t_oop != NULL && t_oop->is_instance_field() &&
t_oop->offset() != Type::OffsetBot &&
t_oop->offset() != Type::OffsetTop) {
return true;
}
}
return false;
}
//------------------------------Identity---------------------------------------
// Loads are identity if previous store is to same address
Node *LoadNode::Identity( PhaseTransform *phase ) {
@ -754,6 +858,25 @@ Node *LoadNode::Identity( PhaseTransform *phase ) {
// usually runs first, producing the singleton type of the Con.)
return value;
}
// Search for an existing data phi which was generated before for the same
// instance's field to avoid infinite genertion of phis in a loop.
Node *region = mem->in(0);
if (is_instance_field_load_with_local_phi(region)) {
const TypePtr *addr_t = in(MemNode::Address)->bottom_type()->isa_ptr();
int this_index = phase->C->get_alias_index(addr_t);
int this_offset = addr_t->offset();
int this_id = addr_t->is_oopptr()->instance_id();
const Type* this_type = bottom_type();
for (DUIterator_Fast imax, i = region->fast_outs(imax); i < imax; i++) {
Node* phi = region->fast_out(i);
if (phi->is_Phi() && phi != mem &&
phi->as_Phi()->is_same_inst_field(this_type, this_id, this_index, this_offset)) {
return phi;
}
}
}
return this;
}
@ -962,6 +1085,122 @@ Node *LoadNode::Ideal(PhaseGVN *phase, bool can_reshape) {
}
}
Node* mem = in(MemNode::Memory);
const TypePtr *addr_t = phase->type(address)->isa_ptr();
if (addr_t != NULL) {
// try to optimize our memory input
Node* opt_mem = MemNode::optimize_memory_chain(mem, addr_t, phase);
if (opt_mem != mem) {
set_req(MemNode::Memory, opt_mem);
return this;
}
const TypeOopPtr *t_oop = addr_t->isa_oopptr();
if (can_reshape && opt_mem->is_Phi() &&
(t_oop != NULL) && t_oop->is_instance_field()) {
assert(t_oop->offset() != Type::OffsetBot && t_oop->offset() != Type::OffsetTop, "");
Node *region = opt_mem->in(0);
uint cnt = opt_mem->req();
for( uint i = 1; i < cnt; i++ ) {
Node *in = opt_mem->in(i);
if( in == NULL ) {
region = NULL; // Wait stable graph
break;
}
}
if (region != NULL) {
// Check for loop invariant.
if (cnt == 3) {
for( uint i = 1; i < cnt; i++ ) {
Node *in = opt_mem->in(i);
Node* m = MemNode::optimize_memory_chain(in, addr_t, phase);
if (m == opt_mem) {
set_req(MemNode::Memory, opt_mem->in(cnt - i)); // Skip this phi.
return this;
}
}
}
// Split through Phi (see original code in loopopts.cpp).
assert(phase->C->have_alias_type(addr_t), "instance should have alias type");
const Type* this_type = this->bottom_type();
int this_index = phase->C->get_alias_index(addr_t);
int this_offset = addr_t->offset();
int this_iid = addr_t->is_oopptr()->instance_id();
int wins = 0;
PhaseIterGVN *igvn = phase->is_IterGVN();
Node *phi = new (igvn->C, region->req()) PhiNode(region, this_type, NULL, this_iid, this_index, this_offset);
for( uint i = 1; i < region->req(); i++ ) {
Node *x;
Node* the_clone = NULL;
if( region->in(i) == phase->C->top() ) {
x = phase->C->top(); // Dead path? Use a dead data op
} else {
x = this->clone(); // Else clone up the data op
the_clone = x; // Remember for possible deletion.
// Alter data node to use pre-phi inputs
if( this->in(0) == region ) {
x->set_req( 0, region->in(i) );
} else {
x->set_req( 0, NULL );
}
for( uint j = 1; j < this->req(); j++ ) {
Node *in = this->in(j);
if( in->is_Phi() && in->in(0) == region )
x->set_req( j, in->in(i) ); // Use pre-Phi input for the clone
}
}
// Check for a 'win' on some paths
const Type *t = x->Value(igvn);
bool singleton = t->singleton();
// See comments in PhaseIdealLoop::split_thru_phi().
if( singleton && t == Type::TOP ) {
singleton &= region->is_Loop() && (i != LoopNode::EntryControl);
}
if( singleton ) {
wins++;
x = igvn->makecon(t);
} else {
// We now call Identity to try to simplify the cloned node.
// Note that some Identity methods call phase->type(this).
// Make sure that the type array is big enough for
// our new node, even though we may throw the node away.
// (This tweaking with igvn only works because x is a new node.)
igvn->set_type(x, t);
Node *y = x->Identity(igvn);
if( y != x ) {
wins++;
x = y;
} else {
y = igvn->hash_find(x);
if( y ) {
wins++;
x = y;
} else {
// Else x is a new node we are keeping
// We do not need register_new_node_with_optimizer
// because set_type has already been called.
igvn->_worklist.push(x);
}
}
}
if (x != the_clone && the_clone != NULL)
igvn->remove_dead_node(the_clone);
phi->set_req(i, x);
}
if( wins > 0 ) {
// Record Phi
igvn->register_new_node_with_optimizer(phi);
return phi;
} else {
igvn->remove_dead_node(phi);
}
}
}
}
// 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
@ -1189,6 +1428,17 @@ const Type *LoadNode::Value( PhaseTransform *phase ) const {
return value->bottom_type();
}
const TypeOopPtr *tinst = tp->isa_oopptr();
if (tinst != NULL && tinst->is_instance_field()) {
// If we have an instance type and our memory input is the
// programs's initial memory state, there is no matching store,
// so just return a zero of the appropriate type
Node *mem = in(MemNode::Memory);
if (mem->is_Parm() && mem->in(0)->is_Start()) {
assert(mem->as_Parm()->_con == TypeFunc::Memory, "must be memory Parm");
return Type::get_zero_type(_type->basic_type());
}
}
return _type;
}
@ -1712,7 +1962,7 @@ bool StoreNode::value_never_loaded( PhaseTransform *phase) const {
const TypeOopPtr *adr_oop = phase->type(adr)->isa_oopptr();
if (adr_oop == NULL)
return false;
if (!adr_oop->is_instance())
if (!adr_oop->is_instance_field())
return false; // if not a distinct instance, there may be aliases of the address
for (DUIterator_Fast imax, i = adr->fast_outs(imax); i < imax; i++) {
Node *use = adr->fast_out(i);
@ -1821,7 +2071,7 @@ uint ClearArrayNode::match_edge(uint idx) const {
//------------------------------Identity---------------------------------------
// Clearing a zero length array does nothing
Node *ClearArrayNode::Identity( PhaseTransform *phase ) {
return phase->type(in(2))->higher_equal(TypeInt::ZERO) ? in(1) : this;
return phase->type(in(2))->higher_equal(TypeX::ZERO) ? in(1) : this;
}
//------------------------------Idealize---------------------------------------
@ -1894,6 +2144,11 @@ Node* ClearArrayNode::clear_memory(Node* ctl, Node* mem, Node* dest,
Node* start_offset,
Node* end_offset,
PhaseGVN* phase) {
if (start_offset == end_offset) {
// nothing to do
return mem;
}
Compile* C = phase->C;
int unit = BytesPerLong;
Node* zbase = start_offset;
@ -1919,6 +2174,11 @@ Node* ClearArrayNode::clear_memory(Node* ctl, Node* mem, Node* dest,
intptr_t start_offset,
intptr_t end_offset,
PhaseGVN* phase) {
if (start_offset == end_offset) {
// nothing to do
return mem;
}
Compile* C = phase->C;
assert((end_offset % BytesPerInt) == 0, "odd end offset");
intptr_t done_offset = end_offset;
@ -3244,7 +3504,7 @@ Node *MergeMemNode::Ideal(PhaseGVN *phase, bool can_reshape) {
}
}
assert(verify_sparse(), "please, no dups of base");
assert(progress || verify_sparse(), "please, no dups of base");
return progress;
}

@ -67,6 +67,8 @@ public:
PhaseTransform* phase);
static bool adr_phi_is_loop_invariant(Node* adr_phi, Node* cast);
static Node *optimize_simple_memory_chain(Node *mchain, const TypePtr *t_adr, PhaseGVN *phase);
static Node *optimize_memory_chain(Node *mchain, const TypePtr *t_adr, PhaseGVN *phase);
// This one should probably be a phase-specific function:
static bool detect_dominating_control(Node* dom, Node* sub);
@ -172,6 +174,9 @@ public:
// Map a load opcode to its corresponding store opcode.
virtual int store_Opcode() const = 0;
// Check if the load's memory input is a Phi node with the same control.
bool is_instance_field_load_with_local_phi(Node* ctrl);
#ifndef PRODUCT
virtual void dump_spec(outputStream *st) const;
#endif

@ -812,8 +812,7 @@ int Node::disconnect_inputs(Node *n) {
Node* Node::uncast() const {
// Should be inline:
//return is_ConstraintCast() ? uncast_helper(this) : (Node*) this;
if (is_ConstraintCast() ||
(is_Type() && req() == 2 && Opcode() == Op_CheckCastPP))
if (is_ConstraintCast() || is_CheckCastPP())
return uncast_helper(this);
else
return (Node*) this;
@ -827,7 +826,7 @@ Node* Node::uncast_helper(const Node* p) {
break;
} else if (p->is_ConstraintCast()) {
p = p->in(1);
} else if (p->is_Type() && p->Opcode() == Op_CheckCastPP) {
} else if (p->is_CheckCastPP()) {
p = p->in(1);
} else {
break;

@ -106,6 +106,7 @@ class RegMask;
class RegionNode;
class RootNode;
class SafePointNode;
class SafePointScalarObjectNode;
class StartNode;
class State;
class StoreNode;
@ -575,6 +576,7 @@ public:
DEFINE_CLASS_ID(ConstraintCast, Type, 1)
DEFINE_CLASS_ID(CheckCastPP, Type, 2)
DEFINE_CLASS_ID(CMove, Type, 3)
DEFINE_CLASS_ID(SafePointScalarObject, Type, 4)
DEFINE_CLASS_ID(Mem, Node, 6)
DEFINE_CLASS_ID(Load, Mem, 0)
@ -721,6 +723,7 @@ public:
DEFINE_CLASS_QUERY(Region)
DEFINE_CLASS_QUERY(Root)
DEFINE_CLASS_QUERY(SafePoint)
DEFINE_CLASS_QUERY(SafePointScalarObject)
DEFINE_CLASS_QUERY(Start)
DEFINE_CLASS_QUERY(Store)
DEFINE_CLASS_QUERY(Sub)
@ -1325,7 +1328,6 @@ public:
// Inline definition of Compile::record_for_igvn must be deferred to this point.
inline void Compile::record_for_igvn(Node* n) {
_for_igvn->push(n);
record_for_escape_analysis(n);
}
//------------------------------Node_Stack-------------------------------------

@ -561,7 +561,30 @@ static LocationValue *new_loc_value( PhaseRegAlloc *ra, OptoReg::Name regnum, Lo
: new LocationValue(Location::new_stk_loc(l_type, ra->reg2offset(regnum)));
}
void Compile::FillLocArray( int idx, Node *local, GrowableArray<ScopeValue*> *array ) {
ObjectValue*
Compile::sv_for_node_id(GrowableArray<ScopeValue*> *objs, int id) {
for (int i = 0; i < objs->length(); i++) {
assert(objs->at(i)->is_object(), "corrupt object cache");
ObjectValue* sv = (ObjectValue*) objs->at(i);
if (sv->id() == id) {
return sv;
}
}
// Otherwise..
return NULL;
}
void Compile::set_sv_for_object_node(GrowableArray<ScopeValue*> *objs,
ObjectValue* sv ) {
assert(sv_for_node_id(objs, sv->id()) == NULL, "Precondition");
objs->append(sv);
}
void Compile::FillLocArray( int idx, MachSafePointNode* sfpt, Node *local,
GrowableArray<ScopeValue*> *array,
GrowableArray<ScopeValue*> *objs ) {
assert( local, "use _top instead of null" );
if (array->length() != idx) {
assert(array->length() == idx + 1, "Unexpected array count");
@ -578,6 +601,29 @@ void Compile::FillLocArray( int idx, Node *local, GrowableArray<ScopeValue*> *ar
}
const Type *t = local->bottom_type();
// Is it a safepoint scalar object node?
if (local->is_SafePointScalarObject()) {
SafePointScalarObjectNode* spobj = local->as_SafePointScalarObject();
ObjectValue* sv = Compile::sv_for_node_id(objs, spobj->_idx);
if (sv == NULL) {
ciKlass* cik = t->is_oopptr()->klass();
assert(cik->is_instance_klass() ||
cik->is_array_klass(), "Not supported allocation.");
sv = new ObjectValue(spobj->_idx,
new ConstantOopWriteValue(cik->encoding()));
Compile::set_sv_for_object_node(objs, sv);
uint first_ind = spobj->first_index();
for (uint i = 0; i < spobj->n_fields(); i++) {
Node* fld_node = sfpt->in(first_ind+i);
(void)FillLocArray(sv->field_values()->length(), sfpt, fld_node, sv->field_values(), objs);
}
}
array->append(sv);
return;
}
// Grab the register number for the local
OptoReg::Name regnum = _regalloc->get_reg_first(local);
if( OptoReg::is_valid(regnum) ) {// Got a register/stack?
@ -755,6 +801,11 @@ void Compile::Process_OopMap_Node(MachNode *mach, int current_offset) {
JVMState* youngest_jvms = sfn->jvms();
int max_depth = youngest_jvms->depth();
// Allocate the object pool for scalar-replaced objects -- the map from
// small-integer keys (which can be recorded in the local and ostack
// arrays) to descriptions of the object state.
GrowableArray<ScopeValue*> *objs = new GrowableArray<ScopeValue*>();
// Visit scopes from oldest to youngest.
for (int depth = 1; depth <= max_depth; depth++) {
JVMState* jvms = youngest_jvms->of_depth(depth);
@ -773,13 +824,13 @@ void Compile::Process_OopMap_Node(MachNode *mach, int current_offset) {
// Insert locals into the locarray
GrowableArray<ScopeValue*> *locarray = new GrowableArray<ScopeValue*>(num_locs);
for( idx = 0; idx < num_locs; idx++ ) {
FillLocArray( idx, sfn->local(jvms, idx), locarray );
FillLocArray( idx, sfn, sfn->local(jvms, idx), locarray, objs );
}
// Insert expression stack entries into the exparray
GrowableArray<ScopeValue*> *exparray = new GrowableArray<ScopeValue*>(num_exps);
for( idx = 0; idx < num_exps; idx++ ) {
FillLocArray( idx, sfn->stack(jvms, idx), exparray );
FillLocArray( idx, sfn, sfn->stack(jvms, idx), exparray, objs );
}
// Add in mappings of the monitors
@ -803,7 +854,27 @@ void Compile::Process_OopMap_Node(MachNode *mach, int current_offset) {
// Create ScopeValue for object
ScopeValue *scval = NULL;
if( !obj_node->is_Con() ) {
if( obj_node->is_SafePointScalarObject() ) {
SafePointScalarObjectNode* spobj = obj_node->as_SafePointScalarObject();
scval = Compile::sv_for_node_id(objs, spobj->_idx);
if (scval == NULL) {
const Type *t = obj_node->bottom_type();
ciKlass* cik = t->is_oopptr()->klass();
assert(cik->is_instance_klass() ||
cik->is_array_klass(), "Not supported allocation.");
ObjectValue* sv = new ObjectValue(spobj->_idx,
new ConstantOopWriteValue(cik->encoding()));
Compile::set_sv_for_object_node(objs, sv);
uint first_ind = spobj->first_index();
for (uint i = 0; i < spobj->n_fields(); i++) {
Node* fld_node = sfn->in(first_ind+i);
(void)FillLocArray(sv->field_values()->length(), sfn, fld_node, sv->field_values(), objs);
}
scval = sv;
}
} else if( !obj_node->is_Con() ) {
OptoReg::Name obj_reg = _regalloc->get_reg_first(obj_node);
scval = new_loc_value( _regalloc, obj_reg, Location::oop );
} else {
@ -811,9 +882,13 @@ void Compile::Process_OopMap_Node(MachNode *mach, int current_offset) {
}
OptoReg::Name box_reg = BoxLockNode::stack_slot(box_node);
monarray->append(new MonitorValue(scval, Location::new_stk_loc(Location::normal,_regalloc->reg2offset(box_reg))));
Location basic_lock = Location::new_stk_loc(Location::normal,_regalloc->reg2offset(box_reg));
monarray->append(new MonitorValue(scval, basic_lock, box_node->as_BoxLock()->is_eliminated()));
}
// We dump the object pool first, since deoptimization reads it in first.
debug_info()->dump_object_pool(objs);
// Build first class objects to pass to scope
DebugToken *locvals = debug_info()->create_scope_values(locarray);
DebugToken *expvals = debug_info()->create_scope_values(exparray);
@ -823,6 +898,7 @@ void Compile::Process_OopMap_Node(MachNode *mach, int current_offset) {
ciMethod* scope_method = method ? method : _method;
// Describe the scope here
assert(jvms->bci() >= InvocationEntryBci && jvms->bci() <= 0x10000, "must be a valid or entry BCI");
// Now we can describe the scope.
debug_info()->describe_scope(safepoint_pc_offset,scope_method,jvms->bci(),locvals,expvals,monvals);
} // End jvms loop

@ -587,11 +587,6 @@ ConNode* PhaseValues::uncached_makecon(const Type *t) {
Node_Notes* loc = C->locate_node_notes(nna, x->_idx, true);
loc->clear(); // do not put debug info on constants
}
// Collect points-to information for escape analysys
ConnectionGraph *cgr = C->congraph();
if (cgr != NULL) {
cgr->record_escape(x, this);
}
} else {
x->destruct(); // Hit, destroy duplicate constant
x = k; // use existing constant
@ -714,12 +709,6 @@ Node *PhaseGVN::transform_no_reclaim( Node *n ) {
return i;
}
// Collect points-to information for escape analysys
ConnectionGraph *cgr = C->congraph();
if (cgr != NULL) {
cgr->record_escape(k, this);
}
// Return Idealized original
return k;
}
@ -1245,7 +1234,7 @@ void PhaseIterGVN::add_users_to_worklist( Node *n ) {
uint use_op = use->Opcode();
// If changed Cast input, check Phi users for simple cycles
if( use->is_ConstraintCast() || use->Opcode() == Op_CheckCastPP ) {
if( use->is_ConstraintCast() || use->is_CheckCastPP() ) {
for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) {
Node* u = use->fast_out(i2);
if (u->is_Phi())

@ -439,6 +439,13 @@ public:
void add_users_to_worklist0( Node *n );
void add_users_to_worklist ( Node *n );
// Replace old node with new one.
void replace_node( Node *old, Node *nn ) {
add_users_to_worklist(old);
hash_delete(old);
subsume_node(old, nn);
}
#ifndef PRODUCT
protected:
// Sub-quadratic implementation of VerifyIterativeGVN.

@ -253,7 +253,8 @@ int PhaseChaitin::elide_copy( Node *n, int k, Block *current_block, Node_List &v
// nodes can represent the same constant so the type and rule of the
// MachNode must be checked to ensure equivalence.
//
bool PhaseChaitin::eliminate_copy_of_constant(Node* val, Block *current_block,
bool PhaseChaitin::eliminate_copy_of_constant(Node* val, Node* n,
Block *current_block,
Node_List& value, Node_List& regnd,
OptoReg::Name nreg, OptoReg::Name nreg2) {
if (value[nreg] != val && val->is_Con() &&
@ -269,12 +270,12 @@ bool PhaseChaitin::eliminate_copy_of_constant(Node* val, Block *current_block,
// Since they are equivalent the second one if redundant and can
// be removed.
//
// val will be replaced with the old value but val might have
// n will be replaced with the old value but n might have
// kills projections associated with it so remove them now so that
// yank_if_dead will be able to elminate the copy once the uses
// have been transferred to the old[value].
for (DUIterator_Fast imax, i = val->fast_outs(imax); i < imax; i++) {
Node* use = val->fast_out(i);
for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) {
Node* use = n->fast_out(i);
if (use->is_Proj() && use->outcnt() == 0) {
// Kill projections have no users and one input
use->set_req(0, C->top());
@ -521,7 +522,7 @@ void PhaseChaitin::post_allocate_copy_removal() {
// then 'n' is a useless copy. Do not update the register->node
// mapping so 'n' will go dead.
if( value[nreg] != val ) {
if (eliminate_copy_of_constant(val, b, value, regnd, nreg, OptoReg::Bad)) {
if (eliminate_copy_of_constant(val, n, b, value, regnd, nreg, OptoReg::Bad)) {
n->replace_by(regnd[nreg]);
j -= yank_if_dead(n,b,&value,&regnd);
} else {
@ -549,7 +550,7 @@ void PhaseChaitin::post_allocate_copy_removal() {
nreg_lo = tmp.find_first_elem();
}
if( value[nreg] != val || value[nreg_lo] != val ) {
if (eliminate_copy_of_constant(n, b, value, regnd, nreg, nreg_lo)) {
if (eliminate_copy_of_constant(val, n, b, value, regnd, nreg, nreg_lo)) {
n->replace_by(regnd[nreg]);
j -= yank_if_dead(n,b,&value,&regnd);
} else {

@ -183,8 +183,8 @@ void SuperWord::find_adjacent_refs() {
#ifndef PRODUCT
if (TraceSuperWord)
tty->print_cr("\noffset = %d iv_adjustment = %d elt_align = %d",
offset, iv_adjustment, align_to_ref_p.memory_size());
tty->print_cr("\noffset = %d iv_adjustment = %d elt_align = %d scale = %d iv_stride = %d",
offset, iv_adjustment, align_to_ref_p.memory_size(), align_to_ref_p.scale_in_bytes(), iv_stride());
#endif
// Set alignment relative to "align_to_ref"
@ -1543,7 +1543,7 @@ void SuperWord::align_initial_loop_index(MemNode* align_to_ref) {
Node *pre_opaq1 = pre_end->limit();
assert(pre_opaq1->Opcode() == Op_Opaque1, "");
Opaque1Node *pre_opaq = (Opaque1Node*)pre_opaq1;
Node *pre_limit = pre_opaq->in(1);
Node *lim0 = pre_opaq->in(1);
// Where we put new limit calculations
Node *pre_ctrl = pre_end->loopnode()->in(LoopNode::EntryControl);
@ -1555,64 +1555,116 @@ void SuperWord::align_initial_loop_index(MemNode* align_to_ref) {
SWPointer align_to_ref_p(align_to_ref, this);
// Let l0 == original pre_limit, l == new pre_limit, V == v_align
// Given:
// lim0 == original pre loop limit
// V == v_align (power of 2)
// invar == extra invariant piece of the address expression
// e == k [ +/- invar ]
//
// For stride > 0
// Need l such that l > l0 && (l+k)%V == 0
// Find n such that l = (l0 + n)
// (l0 + n + k) % V == 0
// n = [V - (l0 + k)%V]%V
// new limit = l0 + [V - (l0 + k)%V]%V
// For stride < 0
// Need l such that l < l0 && (l+k)%V == 0
// Find n such that l = (l0 - n)
// (l0 - n + k) % V == 0
// n = (l0 + k)%V
// new limit = l0 - (l0 + k)%V
// When reassociating expressions involving '%' the basic rules are:
// (a - b) % k == 0 => a % k == b % k
// and:
// (a + b) % k == 0 => a % k == (k - b) % k
//
// For stride > 0 && scale > 0,
// Derive the new pre-loop limit "lim" such that the two constraints:
// (1) lim = lim0 + N (where N is some positive integer < V)
// (2) (e + lim) % V == 0
// are true.
//
// Substituting (1) into (2),
// (e + lim0 + N) % V == 0
// solve for N:
// N = (V - (e + lim0)) % V
// substitute back into (1), so that new limit
// lim = lim0 + (V - (e + lim0)) % V
//
// For stride > 0 && scale < 0
// Constraints:
// lim = lim0 + N
// (e - lim) % V == 0
// Solving for lim:
// (e - lim0 - N) % V == 0
// N = (e - lim0) % V
// lim = lim0 + (e - lim0) % V
//
// For stride < 0 && scale > 0
// Constraints:
// lim = lim0 - N
// (e + lim) % V == 0
// Solving for lim:
// (e + lim0 - N) % V == 0
// N = (e + lim0) % V
// lim = lim0 - (e + lim0) % V
//
// For stride < 0 && scale < 0
// Constraints:
// lim = lim0 - N
// (e - lim) % V == 0
// Solving for lim:
// (e - lim0 + N) % V == 0
// N = (V - (e - lim0)) % V
// lim = lim0 - (V - (e - lim0)) % V
int stride = iv_stride();
int scale = align_to_ref_p.scale_in_bytes();
int elt_size = align_to_ref_p.memory_size();
int v_align = vector_width_in_bytes() / elt_size;
int k = align_to_ref_p.offset_in_bytes() / elt_size;
Node *kn = _igvn.intcon(k);
Node *limk = new (_phase->C, 3) AddINode(pre_limit, kn);
_phase->_igvn.register_new_node_with_optimizer(limk);
_phase->set_ctrl(limk, pre_ctrl);
Node *e = kn;
if (align_to_ref_p.invar() != NULL) {
// incorporate any extra invariant piece producing k +/- invar >>> log2(elt)
Node* log2_elt = _igvn.intcon(exact_log2(elt_size));
Node* aref = new (_phase->C, 3) URShiftINode(align_to_ref_p.invar(), log2_elt);
_phase->_igvn.register_new_node_with_optimizer(aref);
_phase->set_ctrl(aref, pre_ctrl);
if (!align_to_ref_p.negate_invar()) {
limk = new (_phase->C, 3) AddINode(limk, aref);
if (align_to_ref_p.negate_invar()) {
e = new (_phase->C, 3) SubINode(e, aref);
} else {
limk = new (_phase->C, 3) SubINode(limk, aref);
e = new (_phase->C, 3) AddINode(e, aref);
}
_phase->_igvn.register_new_node_with_optimizer(limk);
_phase->set_ctrl(limk, pre_ctrl);
_phase->_igvn.register_new_node_with_optimizer(e);
_phase->set_ctrl(e, pre_ctrl);
}
Node* va_msk = _igvn.intcon(v_align - 1);
Node* n = new (_phase->C, 3) AndINode(limk, va_msk);
_phase->_igvn.register_new_node_with_optimizer(n);
_phase->set_ctrl(n, pre_ctrl);
Node* newlim;
if (iv_stride() > 0) {
Node* va = _igvn.intcon(v_align);
Node* adj = new (_phase->C, 3) SubINode(va, n);
_phase->_igvn.register_new_node_with_optimizer(adj);
_phase->set_ctrl(adj, pre_ctrl);
Node* adj2 = new (_phase->C, 3) AndINode(adj, va_msk);
_phase->_igvn.register_new_node_with_optimizer(adj2);
_phase->set_ctrl(adj2, pre_ctrl);
newlim = new (_phase->C, 3) AddINode(pre_limit, adj2);
// compute e +/- lim0
if (scale < 0) {
e = new (_phase->C, 3) SubINode(e, lim0);
} else {
newlim = new (_phase->C, 3) SubINode(pre_limit, n);
e = new (_phase->C, 3) AddINode(e, lim0);
}
_phase->_igvn.register_new_node_with_optimizer(newlim);
_phase->set_ctrl(newlim, pre_ctrl);
_phase->_igvn.register_new_node_with_optimizer(e);
_phase->set_ctrl(e, pre_ctrl);
if (stride * scale > 0) {
// compute V - (e +/- lim0)
Node* va = _igvn.intcon(v_align);
e = new (_phase->C, 3) SubINode(va, e);
_phase->_igvn.register_new_node_with_optimizer(e);
_phase->set_ctrl(e, pre_ctrl);
}
// compute N = (exp) % V
Node* va_msk = _igvn.intcon(v_align - 1);
Node* N = new (_phase->C, 3) AndINode(e, va_msk);
_phase->_igvn.register_new_node_with_optimizer(N);
_phase->set_ctrl(N, pre_ctrl);
// substitute back into (1), so that new limit
// lim = lim0 + N
Node* lim;
if (stride < 0) {
lim = new (_phase->C, 3) SubINode(lim0, N);
} else {
lim = new (_phase->C, 3) AddINode(lim0, N);
}
_phase->_igvn.register_new_node_with_optimizer(lim);
_phase->set_ctrl(lim, pre_ctrl);
Node* constrained =
(iv_stride() > 0) ? (Node*) new (_phase->C,3) MinINode(newlim, orig_limit)
: (Node*) new (_phase->C,3) MaxINode(newlim, orig_limit);
(stride > 0) ? (Node*) new (_phase->C,3) MinINode(lim, orig_limit)
: (Node*) new (_phase->C,3) MaxINode(lim, orig_limit);
_phase->_igvn.register_new_node_with_optimizer(constrained);
_phase->set_ctrl(constrained, pre_ctrl);
_igvn.hash_delete(pre_opaq);

@ -3164,7 +3164,7 @@ const Type *TypeAryPtr::xmeet( const Type *t ) const {
case TopPTR:
// Compute new klass on demand, do not use tap->_klass
xk = (tap->_klass_is_exact | this->_klass_is_exact);
return make( ptr, const_oop(), tary, lazy_klass, xk, off );
return make( ptr, const_oop(), tary, lazy_klass, xk, off, iid );
case Constant: {
ciObject* o = const_oop();
if( _ptr == Constant ) {
@ -3176,7 +3176,7 @@ const Type *TypeAryPtr::xmeet( const Type *t ) const {
o = tap->const_oop();
}
xk = true;
return TypeAryPtr::make( ptr, o, tary, tap->_klass, xk, off );
return TypeAryPtr::make( ptr, o, tary, tap->_klass, xk, off, iid );
}
case NotNull:
case BotPTR:
@ -3263,14 +3263,21 @@ void TypeAryPtr::dump2( Dict &d, uint depth, outputStream *st ) const {
break;
}
st->print("*");
if( _offset != 0 ) {
int header_size = objArrayOopDesc::header_size() * wordSize;
if( _offset == OffsetTop ) st->print("+undefined");
else if( _offset == OffsetBot ) st->print("+any");
else if( _offset < header_size ) st->print("+%d", _offset);
else {
BasicType basic_elem_type = elem()->basic_type();
int array_base = arrayOopDesc::base_offset_in_bytes(basic_elem_type);
int elem_size = type2aelembytes(basic_elem_type);
st->print("[%d]", (_offset - array_base)/elem_size);
}
}
st->print(" *");
if (_instance_id != UNKNOWN_INSTANCE)
st->print(",iid=%d",_instance_id);
if( !_offset ) return;
if( _offset == OffsetTop ) st->print("+undefined");
else if( _offset == OffsetBot ) st->print("+any");
else if( _offset < 12 ) st->print("+%d",_offset);
else st->print("[%d]", (_offset-12)/4 );
}
#endif

@ -686,6 +686,7 @@ public:
bool klass_is_exact() const { return _klass_is_exact; }
bool is_instance() const { return _instance_id != UNKNOWN_INSTANCE; }
uint instance_id() const { return _instance_id; }
bool is_instance_field() const { return _instance_id != UNKNOWN_INSTANCE && _offset >= 0; }
virtual intptr_t get_con() const;

@ -1276,6 +1276,9 @@ void Arguments::set_aggressive_opts_flags() {
sprintf(buffer, "java.lang.Integer.IntegerCache.high=%d", AutoBoxCacheMax);
add_property(buffer);
}
if (AggressiveOpts && FLAG_IS_DEFAULT(DoEscapeAnalysis)) {
FLAG_SET_DEFAULT(DoEscapeAnalysis, true);
}
#endif
if (AggressiveOpts) {

@ -943,6 +943,12 @@ class CommandLineFlags {
product(bool, UseXmmRegToRegMoveAll, false, \
"Copy all XMM register bits when moving value between registers") \
\
product(bool, UseXmmI2D, false, \
"Use SSE2 CVTDQ2PD instruction to convert Integer to Double") \
\
product(bool, UseXmmI2F, false, \
"Use SSE2 CVTDQ2PS instruction to convert Integer to Float") \
\
product(intx, FieldsAllocationStyle, 1, \
"0 - type based with oops first, 1 - with oops last") \
\

@ -0,0 +1,60 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
/*
* @test
* @bug 6659207
* @summary access violation in CompilerThread0
*/
public class Test {
static int[] array = new int[12];
static int index(int i) {
if (i == 0) return 0;
for (int n = 0; n < array.length; n++)
if (i < array[n]) return n;
return -1;
}
static int test(int i) {
int result = 0;
i = index(i);
if (i >= 0)
if (array[i] != 0)
result++;
if (i != -1)
array[i]++;
return result;
}
public static void main(String[] args) {
int total = 0;
for (int i = 0; i < 100000; i++) {
total += test(10);
}
System.out.println(total);
}
}

@ -0,0 +1,155 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
/*
* @test
* @bug 6661247
* @summary Internal bug in 32-bit HotSpot optimizer while bit manipulations
*/
import java.util.Random;
import java.nio.*;
// This isn't a completely reliable test for 6661247 since the results
// depend on what the local schedule looks like but it does reproduce
// the issue in current builds.
public class Test {
public static void test(boolean[] src, int srcPos, LongBuffer dest, long destPos, int count) {
int countStart = (destPos & 63) == 0 ? 0 : 64 - (int)(destPos & 63);
if (countStart > count)
countStart = count;
for (int srcPosMax = srcPos + countStart; srcPos < srcPosMax; srcPos++, destPos++) {
if (src[srcPos])
dest.put((int)(destPos >>> 6), dest.get((int)(destPos >>> 6)) | 1L << (destPos & 63));
else
dest.put((int)(destPos >>> 6), dest.get((int)(destPos >>> 6)) & ~(1L << (destPos & 63)));
}
count -= countStart;
int cnt = count >>> 6;
for (int k = (int)(destPos >>> 6), kMax = k + cnt; k < kMax; k++) {
int low = (src[srcPos] ? 1 : 0)
| (src[srcPos + 1] ? 1 << 1 : 0)
| (src[srcPos + 2] ? 1 << 2 : 0)
| (src[srcPos + 3] ? 1 << 3 : 0)
| (src[srcPos + 4] ? 1 << 4 : 0)
| (src[srcPos + 5] ? 1 << 5 : 0)
| (src[srcPos + 6] ? 1 << 6 : 0)
| (src[srcPos + 7] ? 1 << 7 : 0)
| (src[srcPos + 8] ? 1 << 8 : 0)
| (src[srcPos + 9] ? 1 << 9 : 0)
| (src[srcPos + 10] ? 1 << 10 : 0)
| (src[srcPos + 11] ? 1 << 11 : 0)
| (src[srcPos + 12] ? 1 << 12 : 0)
| (src[srcPos + 13] ? 1 << 13 : 0)
| (src[srcPos + 14] ? 1 << 14 : 0)
| (src[srcPos + 15] ? 1 << 15 : 0)
| (src[srcPos + 16] ? 1 << 16 : 0)
| (src[srcPos + 17] ? 1 << 17 : 0)
| (src[srcPos + 18] ? 1 << 18 : 0)
| (src[srcPos + 19] ? 1 << 19 : 0)
| (src[srcPos + 20] ? 1 << 20 : 0)
| (src[srcPos + 21] ? 1 << 21 : 0)
| (src[srcPos + 22] ? 1 << 22 : 0)
| (src[srcPos + 23] ? 1 << 23 : 0)
| (src[srcPos + 24] ? 1 << 24 : 0)
| (src[srcPos + 25] ? 1 << 25 : 0)
| (src[srcPos + 26] ? 1 << 26 : 0)
| (src[srcPos + 27] ? 1 << 27 : 0)
| (src[srcPos + 28] ? 1 << 28 : 0)
| (src[srcPos + 29] ? 1 << 29 : 0)
| (src[srcPos + 30] ? 1 << 30 : 0)
| (src[srcPos + 31] ? 1 << 31 : 0)
;
srcPos += 32;
int high = (src[srcPos] ? 1 : 0) // PROBLEM!
| (src[srcPos + 1] ? 1 << 1 : 0)
| (src[srcPos + 2] ? 1 << 2 : 0)
| (src[srcPos + 3] ? 1 << 3 : 0)
| (src[srcPos + 4] ? 1 << 4 : 0)
| (src[srcPos + 5] ? 1 << 5 : 0)
| (src[srcPos + 6] ? 1 << 6 : 0)
| (src[srcPos + 7] ? 1 << 7 : 0)
| (src[srcPos + 8] ? 1 << 8 : 0)
| (src[srcPos + 9] ? 1 << 9 : 0)
| (src[srcPos + 10] ? 1 << 10 : 0)
| (src[srcPos + 11] ? 1 << 11 : 0)
| (src[srcPos + 12] ? 1 << 12 : 0)
| (src[srcPos + 13] ? 1 << 13 : 0)
| (src[srcPos + 14] ? 1 << 14 : 0)
| (src[srcPos + 15] ? 1 << 15 : 0)
| (src[srcPos + 16] ? 1 << 16 : 0)
| (src[srcPos + 17] ? 1 << 17 : 0)
| (src[srcPos + 18] ? 1 << 18 : 0)
| (src[srcPos + 19] ? 1 << 19 : 0)
| (src[srcPos + 20] ? 1 << 20 : 0)
| (src[srcPos + 21] ? 1 << 21 : 0)
| (src[srcPos + 22] ? 1 << 22 : 0)
| (src[srcPos + 23] ? 1 << 23 : 0)
| (src[srcPos + 24] ? 1 << 24 : 0)
| (src[srcPos + 25] ? 1 << 25 : 0)
| (src[srcPos + 26] ? 1 << 26 : 0)
| (src[srcPos + 27] ? 1 << 27 : 0)
| (src[srcPos + 28] ? 1 << 28 : 0)
| (src[srcPos + 29] ? 1 << 29 : 0)
| (src[srcPos + 30] ? 1 << 30 : 0)
| (src[srcPos + 31] ? 1 << 31 : 0)
;
srcPos += 32;
dest.put(k, ((long)low & 0xFFFFFFFFL) | (((long)high) << 32));
destPos += 64;
}
int countFinish = count & 63;
for (int srcPosMax = srcPos + countFinish; srcPos < srcPosMax; srcPos++, destPos++) {
if (src[srcPos])
dest.put((int)(destPos >>> 6), dest.get((int)(destPos >>> 6)) | 1L << (destPos & 63));
else
dest.put((int)(destPos >>> 6), dest.get((int)(destPos >>> 6)) & ~(1L << (destPos & 63)));
}
}
public static void main(String[] args) {
Random r = new Random();
int entries = 1000;
boolean[] src = new boolean[entries * 64];
long[] dest = new long[entries];
long[] result = new long[entries];
for (int c = 0; c < 2000; c++) {
for (int i = 0; i < entries; i++) {
long l = r.nextLong();
for (int bit = 0; bit < 64; bit++) {
src[i * 64 + bit] = (l & (1L << bit)) != 0;
}
dest[i] = 0;
result[i] = l;
}
test(src, 0, LongBuffer.wrap(dest, 0, dest.length), 0, src.length);
for (int i = 0; i < entries; i++) {
if (dest[i] != result[i]) {
throw new InternalError(i + ": " + Long.toHexString(dest[i]) + " != " + Long.toHexString(result[i]));
}
}
}
}
}

@ -0,0 +1,116 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
/**
* @test
* @bug 6663621
* @summary JVM crashes while trying to execute api/java_security/Signature/SignatureTests.html#initSign tests.
*/
public class IVTest {
static int paddedSize;
static void padV15(byte[] padded) {
int psSize = padded.length;
int k = 0;
while (psSize-- > 0) {
padded[k++] = (byte)0xff;
}
}
static void padV15_2(int paddedSize) {
byte[] padded = new byte[paddedSize];
int psSize = padded.length;
int k = 0;
while (psSize-- > 0) {
padded[k++] = (byte)0xff;
}
}
static void padV15_3() {
byte[] padded = new byte[paddedSize];
int psSize = padded.length;
int k = 0;
while (psSize-- > 0) {
padded[k++] = (byte)0xff;
}
}
static void padV15_4() {
byte[] padded = new byte[paddedSize];
int psSize = padded.length;
for (int k = 0;psSize > 0; psSize--) {
int i = padded.length - psSize;
padded[i] = (byte)0xff;
}
}
static void padV15_5() {
byte[] padded = new byte[paddedSize];
int psSize = padded.length;
int k = psSize - 1;
for (int i = 0; i < psSize; i++) {
padded[k--] = (byte)0xff;
}
}
public static void main(String argv[]) {
int bounds = 1024;
int lim = 500000;
long start = System.currentTimeMillis();
for (int j = 0; j < lim; j++) {
paddedSize = j % bounds;
padV15(new byte[paddedSize]);
}
long end = System.currentTimeMillis();
System.out.println(end - start);
start = System.currentTimeMillis();
for (int j = 0; j < lim; j++) {
paddedSize = j % bounds;
padV15_2(paddedSize);
}
end = System.currentTimeMillis();
System.out.println(end - start);
start = System.currentTimeMillis();
for (int j = 0; j < lim; j++) {
paddedSize = j % bounds;
padV15_3();
}
end = System.currentTimeMillis();
System.out.println(end - start);
start = System.currentTimeMillis();
for (int j = 0; j < lim; j++) {
paddedSize = j % bounds;
padV15_4();
}
end = System.currentTimeMillis();
System.out.println(end - start);
start = System.currentTimeMillis();
for (int j = 0; j < lim; j++) {
paddedSize = j % bounds;
padV15_5();
}
end = System.currentTimeMillis();
System.out.println(end - start);
}
}