Merge
This commit is contained in:
commit
446895de2d
hotspot
make
src
cpu/x86/vm
assembler_x86_32.cppassembler_x86_32.hppassembler_x86_64.cppassembler_x86_64.hppvm_version_x86_32.cppvm_version_x86_64.cppx86_32.adx86_64.ad
share/vm
includeDB_compiler2
opto
c2_globals.hppcallnode.cppcallnode.hppcfgnode.cppcfgnode.hppchaitin.hppclasses.hppcompile.cppcompile.hppdoCall.cppescape.cppescape.hppgraphKit.cpplocknode.cpplocknode.hpploopopts.cppmacro.cppmacro.hppmatcher.cppmemnode.cppmemnode.hppnode.cppnode.hppoutput.cppphaseX.cppphaseX.hpppostaloc.cppsuperword.cpptype.cpptype.hpp
runtime
test/compiler
@ -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 ®nd,
|
||||
bool eliminate_copy_of_constant(Node* val, Node* n,
|
||||
Block *current_block, Node_List& value, Node_List ®nd,
|
||||
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,®nd);
|
||||
} 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,®nd);
|
||||
} 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") \
|
||||
\
|
||||
|
60
hotspot/test/compiler/6659207/Test.java
Normal file
60
hotspot/test/compiler/6659207/Test.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
155
hotspot/test/compiler/6661247/Test.java
Normal file
155
hotspot/test/compiler/6661247/Test.java
Normal file
@ -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]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
116
hotspot/test/compiler/6663621/IVTest.java
Normal file
116
hotspot/test/compiler/6663621/IVTest.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user