# HG changeset patch # User kvn # Date 1211391907 25200 # Node ID 885ed790ecf0a8e65101f42aee5c9cf2567dfe4a # Parent 8aa010f60e0f386fba715eab2175a4c2540fb6ee 6695810: null oop passed to encode_heap_oop_not_null Summary: fix several problems in C2 related to Escape Analysis and Compressed Oops. Reviewed-by: never, jrose diff -r 8aa010f60e0f -r 885ed790ecf0 src/cpu/sparc/vm/sparc.ad --- a/src/cpu/sparc/vm/sparc.ad Tue May 20 06:32:58 2008 -0700 +++ b/src/cpu/sparc/vm/sparc.ad Wed May 21 10:45:07 2008 -0700 @@ -5471,7 +5471,7 @@ // Load Klass Pointer instruct loadKlass(iRegP dst, memory mem) %{ match(Set dst (LoadKlass mem)); - predicate(!n->in(MemNode::Address)->bottom_type()->is_narrow()); + predicate(!n->in(MemNode::Address)->bottom_type()->is_ptr_to_narrowoop()); ins_cost(MEMORY_REF_COST); size(4); @@ -5489,7 +5489,7 @@ // Load Klass Pointer instruct loadKlassComp(iRegP dst, memory mem) %{ match(Set dst (LoadKlass mem)); - predicate(n->in(MemNode::Address)->bottom_type()->is_narrow()); + predicate(n->in(MemNode::Address)->bottom_type()->is_ptr_to_narrowoop()); ins_cost(MEMORY_REF_COST); format %{ "LDUW $mem,$dst\t! compressed klass ptr" %} diff -r 8aa010f60e0f -r 885ed790ecf0 src/cpu/x86/vm/x86_64.ad --- a/src/cpu/x86/vm/x86_64.ad Tue May 20 06:32:58 2008 -0700 +++ b/src/cpu/x86/vm/x86_64.ad Wed May 21 10:45:07 2008 -0700 @@ -6044,10 +6044,9 @@ %} // Load Compressed Pointer -instruct loadN(rRegN dst, memory mem, rFlagsReg cr) +instruct loadN(rRegN dst, memory mem) %{ match(Set dst (LoadN mem)); - effect(KILL cr); ins_cost(125); // XXX format %{ "movl $dst, $mem\t# compressed ptr" %} @@ -6064,7 +6063,7 @@ instruct loadKlass(rRegP dst, memory mem) %{ match(Set dst (LoadKlass mem)); - predicate(!n->in(MemNode::Address)->bottom_type()->is_narrow()); + predicate(!n->in(MemNode::Address)->bottom_type()->is_ptr_to_narrowoop()); ins_cost(125); // XXX format %{ "movq $dst, $mem\t# class" %} @@ -6074,10 +6073,11 @@ %} // Load Klass Pointer -instruct loadKlassComp(rRegP dst, memory mem) +instruct loadKlassComp(rRegP dst, memory mem, rFlagsReg cr) %{ match(Set dst (LoadKlass mem)); - predicate(n->in(MemNode::Address)->bottom_type()->is_narrow()); + predicate(n->in(MemNode::Address)->bottom_type()->is_ptr_to_narrowoop()); + effect(KILL cr); ins_cost(125); // XXX format %{ "movl $dst, $mem\t# compressed class\n\t" @@ -6358,8 +6358,9 @@ ins_pipe(ialu_reg); %} -instruct loadConN(rRegN dst, immN src) %{ +instruct loadConN(rRegN dst, immN src, rFlagsReg cr) %{ match(Set dst src); + effect(KILL cr); ins_cost(125); format %{ "movq $dst, $src\t# compressed ptr\n\t" @@ -6633,10 +6634,9 @@ %} // Store Compressed Pointer -instruct storeN(memory mem, rRegN src, rFlagsReg cr) +instruct storeN(memory mem, rRegN src) %{ match(Set mem (StoreN mem src)); - effect(KILL cr); ins_cost(125); // XXX format %{ "movl $mem, $src\t# ptr" %} diff -r 8aa010f60e0f -r 885ed790ecf0 src/share/vm/opto/callnode.cpp --- a/src/share/vm/opto/callnode.cpp Tue May 20 06:32:58 2008 -0700 +++ b/src/share/vm/opto/callnode.cpp Wed May 21 10:45:07 2008 -0700 @@ -637,7 +637,7 @@ } Compile *C = phase->C; int offset = adrInst_t->offset(); - assert(offset >= 0, "should be valid offset"); + assert(adrInst_t->klass_is_exact() && offset >= 0, "should be valid offset"); ciKlass* adr_k = adrInst_t->klass(); assert(adr_k->is_loaded() && adr_k->is_java_klass() && @@ -674,12 +674,11 @@ 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()) { + at_k->is_java_klass()) { // 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 && + if ((at_k->is_interface() || adr_k == at_k || + adr_k->is_subclass_of(at_k) && !at_ptr->klass_is_exact()) && (bcea == NULL || bcea->is_arg_modified(i - TypeFunc::Parms, offset, size))) { return true; diff -r 8aa010f60e0f -r 885ed790ecf0 src/share/vm/opto/cfgnode.cpp --- a/src/share/vm/opto/cfgnode.cpp Tue May 20 06:32:58 2008 -0700 +++ b/src/share/vm/opto/cfgnode.cpp Wed May 21 10:45:07 2008 -0700 @@ -707,8 +707,14 @@ //------------------------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"); + const TypeOopPtr *t_oop = at->isa_oopptr(); + assert(t_oop != NULL && t_oop->is_instance(), "expecting instance oopptr"); + const TypePtr *t = adr_type(); + assert(type() == Type::MEMORY && + (t == TypePtr::BOTTOM || t == TypeRawPtr::BOTTOM || + t->isa_oopptr() && !t->is_oopptr()->is_instance() && + t->is_oopptr()->cast_to_instance(t_oop->instance_id()) == t_oop), + "bottom or raw memory required"); // Check if an appropriate node already exists. Node *region = in(0); @@ -1342,7 +1348,7 @@ Node *n = phi->in(i); if( !n ) return NULL; if( phase->type(n) == Type::TOP ) return NULL; - if( n->Opcode() == Op_ConP ) + if( n->Opcode() == Op_ConP || n->Opcode() == Op_ConN ) break; } if( i >= phi->req() ) // Only split for constants diff -r 8aa010f60e0f -r 885ed790ecf0 src/share/vm/opto/compile.cpp --- a/src/share/vm/opto/compile.cpp Tue May 20 06:32:58 2008 -0700 +++ b/src/share/vm/opto/compile.cpp Wed May 21 10:45:07 2008 -0700 @@ -368,7 +368,12 @@ BufferBlob* blob = BufferBlob::create("Compile::scratch_buffer", size); // Record the buffer blob for next time. set_scratch_buffer_blob(blob); - guarantee(scratch_buffer_blob() != NULL, "Need BufferBlob for code generation"); + // Have we run out of code space? + if (scratch_buffer_blob() == NULL) { + // Let CompilerBroker disable further compilations. + record_failure("Not enough space for scratch buffer in CodeCache"); + return; + } // Initialize the relocation buffers relocInfo* locs_buf = (relocInfo*) blob->instructions_end() - MAX_locs_size; @@ -1065,6 +1070,8 @@ // No constant oop pointers (such as Strings); they alias with // unknown strings. tj = to = TypeInstPtr::make(TypePtr::BotPTR,to->klass(),false,0,offset); + } else if( to->is_instance_field() ) { + tj = to; // Keep NotNull and klass_is_exact for instance type } else if( ptr == TypePtr::NotNull || to->klass_is_exact() ) { // During the 2nd round of IterGVN, NotNull castings are removed. // Make sure the Bottom and NotNull variants alias the same. @@ -1084,7 +1091,7 @@ } else { ciInstanceKlass *canonical_holder = k->get_canonical_holder(offset); if (!k->equals(canonical_holder) || tj->offset() != offset) { - tj = to = TypeInstPtr::make(TypePtr::BotPTR, canonical_holder, false, NULL, offset, to->instance_id()); + tj = to = TypeInstPtr::make(to->ptr(), canonical_holder, false, NULL, offset, to->instance_id()); } } } diff -r 8aa010f60e0f -r 885ed790ecf0 src/share/vm/opto/connode.cpp --- a/src/share/vm/opto/connode.cpp Tue May 20 06:32:58 2008 -0700 +++ b/src/share/vm/opto/connode.cpp Wed May 21 10:45:07 2008 -0700 @@ -578,8 +578,11 @@ const Type* newtype = value->bottom_type(); if (newtype == TypeNarrowOop::NULL_PTR) { return phase->transform(new (phase->C, 1) ConPNode(TypePtr::NULL_PTR)); + } else if (newtype->isa_narrowoop()) { + return phase->transform(new (phase->C, 2) DecodeNNode(value, newtype->is_narrowoop()->make_oopptr())); } else { - return phase->transform(new (phase->C, 2) DecodeNNode(value, newtype->is_narrowoop()->make_oopptr())); + ShouldNotReachHere(); + return NULL; // to make C++ compiler happy. } } @@ -617,6 +620,9 @@ } } +Node *EncodePNode::Ideal_DU_postCCP( PhaseCCP *ccp ) { + return MemNode::Ideal_common_DU_postCCP(ccp, this, in(1)); +} //============================================================================= //------------------------------Identity--------------------------------------- diff -r 8aa010f60e0f -r 885ed790ecf0 src/share/vm/opto/connode.hpp --- a/src/share/vm/opto/connode.hpp Tue May 20 06:32:58 2008 -0700 +++ b/src/share/vm/opto/connode.hpp Wed May 21 10:45:07 2008 -0700 @@ -283,6 +283,7 @@ virtual uint ideal_reg() const { return Op_RegN; } static Node* encode(PhaseGVN* phase, Node* value); + virtual Node *Ideal_DU_postCCP( PhaseCCP *ccp ); }; //------------------------------DecodeN-------------------------------- diff -r 8aa010f60e0f -r 885ed790ecf0 src/share/vm/opto/escape.cpp --- a/src/share/vm/opto/escape.cpp Tue May 20 06:32:58 2008 -0700 +++ b/src/share/vm/opto/escape.cpp Wed May 21 10:45:07 2008 -0700 @@ -888,6 +888,23 @@ record_for_optimizer(n); if (alloc->is_Allocate() && ptn->_scalar_replaceable && (t->isa_instptr() || t->isa_aryptr())) { + + // First, put on the worklist all Field edges from Connection Graph + // which is more accurate then putting immediate users from Ideal Graph. + for (uint e = 0; e < ptn->edge_count(); e++) { + Node *use = _nodes->adr_at(ptn->edge_target(e))->_node; + assert(ptn->edge_type(e) == PointsToNode::FieldEdge && use->is_AddP(), + "only AddP nodes are Field edges in CG"); + if (use->outcnt() > 0) { // Don't process dead nodes + Node* addp2 = find_second_addp(use, use->in(AddPNode::Base)); + if (addp2 != NULL) { + assert(alloc->is_AllocateArray(),"array allocation was expected"); + alloc_worklist.append_if_missing(addp2); + } + alloc_worklist.append_if_missing(use); + } + } + // An allocation may have an Initialize which has raw stores. Scan // the users of the raw allocation result and push AddP users // on alloc_worklist. @@ -919,6 +936,8 @@ tinst = igvn->type(base)->isa_oopptr(); } else if (n->is_Phi() || n->is_CheckCastPP() || + n->Opcode() == Op_EncodeP || + n->Opcode() == Op_DecodeN || (n->is_ConstraintCast() && n->Opcode() == Op_CastPP)) { if (visited.test_set(n->_idx)) { assert(n->is_Phi(), "loops only through Phi's"); @@ -935,13 +954,25 @@ tinst = igvn->type(val)->isa_oopptr(); assert(tinst != NULL && tinst->is_instance() && tinst->instance_id() == elem , "instance type expected."); - const TypeOopPtr *tn_t = igvn->type(tn)->isa_oopptr(); + + const TypeOopPtr *tn_t = NULL; + const Type *tn_type = igvn->type(tn); + if (tn_type->isa_narrowoop()) { + tn_t = tn_type->is_narrowoop()->make_oopptr()->isa_oopptr(); + } else { + tn_t = tn_type->isa_oopptr(); + } if (tn_t != NULL && tinst->cast_to_instance(TypeOopPtr::UNKNOWN_INSTANCE)->higher_equal(tn_t)) { + if (tn_type->isa_narrowoop()) { + tn_type = tinst->make_narrowoop(); + } else { + tn_type = tinst; + } igvn->hash_delete(tn); - igvn->set_type(tn, tinst); - tn->set_type(tinst); + igvn->set_type(tn, tn_type); + tn->set_type(tn_type); igvn->hash_insert(tn); record_for_optimizer(n); } @@ -978,6 +1009,8 @@ alloc_worklist.append_if_missing(use); } else if (use->is_Phi() || use->is_CheckCastPP() || + use->Opcode() == Op_EncodeP || + use->Opcode() == Op_DecodeN || (use->is_ConstraintCast() && use->Opcode() == Op_CastPP)) { alloc_worklist.append_if_missing(use); } @@ -1199,7 +1232,7 @@ void ConnectionGraph::compute_escape() { - // 1. Populate Connection Graph with Ideal nodes. + // 1. Populate Connection Graph (CG) with Ideal nodes. Unique_Node_List worklist_init; worklist_init.map(_compile->unique(), NULL); // preallocate space @@ -1281,11 +1314,13 @@ remove_deferred(ni, &deferred_edges, &visited); if (n->is_AddP()) { // If this AddP computes an address which may point to more that one - // object, nothing the address points to can be scalar replaceable. + // object or more then one field (array's element), nothing the address + // points to can be scalar replaceable. Node *base = get_addp_base(n); ptset.Clear(); PointsTo(ptset, base, igvn); - if (ptset.Size() > 1) { + if (ptset.Size() > 1 || + (ptset.Size() != 0 && ptn->offset() == Type::OffsetBot)) { for( VectorSetI j(&ptset); j.test(); ++j ) { uint pt = j.elem; ptnode_adr(pt)->_scalar_replaceable = false; @@ -1979,6 +2014,11 @@ assert(false, "Op_ConP"); break; } + case Op_ConN: + { + assert(false, "Op_ConN"); + break; + } case Op_CreateEx: { assert(false, "Op_CreateEx"); diff -r 8aa010f60e0f -r 885ed790ecf0 src/share/vm/opto/library_call.cpp --- a/src/share/vm/opto/library_call.cpp Tue May 20 06:32:58 2008 -0700 +++ b/src/share/vm/opto/library_call.cpp Wed May 21 10:45:07 2008 -0700 @@ -2168,7 +2168,7 @@ // (They don't if CAS fails, but it isn't worth checking.) pre_barrier(control(), base, adr, alias_idx, newval, value_type, T_OBJECT); #ifdef _LP64 - if (adr->bottom_type()->is_narrow()) { + if (adr->bottom_type()->is_ptr_to_narrowoop()) { cas = _gvn.transform(new (C, 5) CompareAndSwapNNode(control(), mem, adr, EncodePNode::encode(&_gvn, newval), EncodePNode::encode(&_gvn, oldval))); @@ -2838,6 +2838,8 @@ _sp += nargs; // set original stack for use by uncommon_trap mirror = do_null_check(mirror, T_OBJECT); _sp -= nargs; + // If mirror or obj is dead, only null-path is taken. + if (stopped()) return true; enum { _normal_path = 1, _slow_path = 2, PATH_LIMIT }; RegionNode* result_reg = new(C, PATH_LIMIT) RegionNode(PATH_LIMIT); @@ -3827,24 +3829,22 @@ if (!stopped()) { // Copy the fastest available way. // (No need for PreserveJVMState, since we're using it all up now.) + // TODO: generate fields/elements copies for small objects instead. Node* src = obj; Node* dest = raw_obj; - Node* end = dest; Node* size = _gvn.transform(alloc_siz); // Exclude the header. int base_off = instanceOopDesc::base_offset_in_bytes(); if (UseCompressedOops) { - // copy the header gap though. - Node* sptr = basic_plus_adr(src, base_off); - Node* dptr = basic_plus_adr(dest, base_off); - Node* sval = make_load(control(), sptr, TypeInt::INT, T_INT, raw_adr_type); - store_to_memory(control(), dptr, sval, T_INT, raw_adr_type); - base_off += sizeof(int); + assert(base_off % BytesPerLong != 0, "base with compressed oops"); + // With compressed oops base_offset_in_bytes is 12 which creates + // the gap since countx is rounded by 8 bytes below. + // Copy klass and the gap. + base_off = instanceOopDesc::klass_offset_in_bytes(); } src = basic_plus_adr(src, base_off); dest = basic_plus_adr(dest, base_off); - end = basic_plus_adr(end, size); // Compute the length also, if needed: Node* countx = size; diff -r 8aa010f60e0f -r 885ed790ecf0 src/share/vm/opto/macro.cpp --- a/src/share/vm/opto/macro.cpp Tue May 20 06:32:58 2008 -0700 +++ b/src/share/vm/opto/macro.cpp Wed May 21 10:45:07 2008 -0700 @@ -1282,12 +1282,6 @@ } rawmem = make_store(control, rawmem, object, oopDesc::mark_offset_in_bytes(), mark_node, T_ADDRESS); - if (UseCompressedOops) { - Node *zeronode = makecon(TypeInt::ZERO); - // store uncompressed 0 into klass ptr to zero out gap. The gap is - // used for primitive fields and has to be zeroed. - rawmem = make_store(control, rawmem, object, oopDesc::klass_gap_offset_in_bytes(), zeronode, T_INT); - } rawmem = make_store(control, rawmem, object, oopDesc::klass_offset_in_bytes(), klass_node, T_OBJECT); int header_size = alloc->minimum_header_size(); // conservatively small diff -r 8aa010f60e0f -r 885ed790ecf0 src/share/vm/opto/matcher.cpp --- a/src/share/vm/opto/matcher.cpp Tue May 20 06:32:58 2008 -0700 +++ b/src/share/vm/opto/matcher.cpp Wed May 21 10:45:07 2008 -0700 @@ -880,7 +880,7 @@ Node *m = n->in(i); // Get input int op = m->Opcode(); assert((op == Op_BoxLock) == jvms->is_monitor_use(i), "boxes only at monitor sites"); - if( op == Op_ConI || op == Op_ConP || + if( op == Op_ConI || op == Op_ConP || op == Op_ConN || op == Op_ConF || op == Op_ConD || op == Op_ConL // || op == Op_BoxLock // %%%% enable this and remove (+++) in chaitin.cpp ) { @@ -1726,6 +1726,14 @@ } break; } + case Op_ConN: { // Convert narrow pointers above the centerline to NUL + TypeNode *tn = n->as_Type(); // Constants derive from type nodes + const TypePtr* tp = tn->type()->is_narrowoop()->make_oopptr(); + if (tp->_ptr == TypePtr::AnyNull) { + tn->set_type(TypeNarrowOop::NULL_PTR); + } + break; + } case Op_Binary: // These are introduced in the Post_Visit state. ShouldNotReachHere(); break; diff -r 8aa010f60e0f -r 885ed790ecf0 src/share/vm/opto/memnode.cpp --- a/src/share/vm/opto/memnode.cpp Tue May 20 06:32:58 2008 -0700 +++ b/src/share/vm/opto/memnode.cpp Wed May 21 10:45:07 2008 -0700 @@ -133,7 +133,9 @@ 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) { + if (t == TypePtr::BOTTOM || t == TypeRawPtr::BOTTOM || + t->isa_oopptr() && !t->is_oopptr()->is_instance() && + t->is_oopptr()->cast_to_instance(t_oop->instance_id()) == t_oop) { // clone the Phi with our address type result = mphi->split_out_instance(t_adr, igvn); } else { @@ -263,7 +265,10 @@ // of all its inputs dominate or equal to sub's control edge. // Currently 'sub' is either Allocate, Initialize or Start nodes. - assert(sub->is_Allocate() || sub->is_Initialize() || sub->is_Start(), "expecting only these nodes"); + // Or Region for the check in LoadNode::Ideal(); + // 'sub' should have sub->in(0) != NULL. + assert(sub->is_Allocate() || sub->is_Initialize() || sub->is_Start() || + sub->is_Region(), "expecting only these nodes"); // Get control edge of 'sub'. sub = sub->find_exact_control(sub->in(0)); @@ -576,6 +581,9 @@ // Find any cast-away of null-ness and keep its control. Null cast-aways are // going away in this pass and we need to make this memory op depend on the // gating null check. +Node *MemNode::Ideal_DU_postCCP( PhaseCCP *ccp ) { + return Ideal_common_DU_postCCP(ccp, this, in(MemNode::Address)); +} // I tried to leave the CastPP's in. This makes the graph more accurate in // some sense; we get to keep around the knowledge that an oop is not-null @@ -585,15 +593,14 @@ // some of the more trivial cases in the optimizer. Removing more useless // Phi's started allowing Loads to illegally float above null checks. I gave // up on this approach. CNC 10/20/2000 -Node *MemNode::Ideal_DU_postCCP( PhaseCCP *ccp ) { - Node *ctr = in(MemNode::Control); - Node *mem = in(MemNode::Memory); - Node *adr = in(MemNode::Address); +// This static method may be called not from MemNode (EncodePNode calls it). +// Only the control edge of the node 'n' might be updated. +Node *MemNode::Ideal_common_DU_postCCP( PhaseCCP *ccp, Node* n, Node* adr ) { Node *skipped_cast = NULL; // Need a null check? Regular static accesses do not because they are // from constant addresses. Array ops are gated by the range check (which // always includes a NULL check). Just check field ops. - if( !ctr ) { + if( n->in(MemNode::Control) == NULL ) { // Scan upwards for the highest location we can place this memory op. while( true ) { switch( adr->Opcode() ) { @@ -618,10 +625,10 @@ } // CastPP is going away in this pass! We need this memory op to be // control-dependent on the test that is guarding the CastPP. - ccp->hash_delete(this); - set_req(MemNode::Control, adr->in(0)); - ccp->hash_insert(this); - return this; + ccp->hash_delete(n); + n->set_req(MemNode::Control, adr->in(0)); + ccp->hash_insert(n); + return n; case Op_Phi: // Attempt to float above a Phi to some dominating point. @@ -652,10 +659,10 @@ adr = adr->in(1); continue; } - ccp->hash_delete(this); - set_req(MemNode::Control, adr->in(0)); - ccp->hash_insert(this); - return this; + ccp->hash_delete(n); + n->set_req(MemNode::Control, adr->in(0)); + ccp->hash_insert(n); + return n; // List of "safe" opcodes; those that implicitly block the memory // op below any null check. @@ -665,6 +672,7 @@ case Op_LoadN: // Loading from within a klass case Op_LoadKlass: // Loading from within a klass case Op_ConP: // Loading from a klass + case Op_ConN: // Loading from a klass case Op_CreateEx: // Sucking up the guts of an exception oop case Op_Con: // Reading from TLS case Op_CMoveP: // CMoveP is pinned @@ -676,8 +684,8 @@ { assert(adr->as_Proj()->_con == TypeFunc::Parms, "must be return value"); const Node* call = adr->in(0); - if (call->is_CallStaticJava()) { - const CallStaticJavaNode* call_java = call->as_CallStaticJava(); + if (call->is_CallJava()) { + const CallJavaNode* call_java = call->as_CallJava(); const TypeTuple *r = call_java->tf()->range(); assert(r->cnt() > TypeFunc::Parms, "must return value"); const Type* ret_type = r->field_at(TypeFunc::Parms); @@ -749,7 +757,7 @@ case T_ADDRESS: return new (C, 3) LoadPNode(ctl, mem, adr, adr_type, rt->is_ptr() ); case T_OBJECT: #ifdef _LP64 - if (adr->bottom_type()->is_narrow()) { + if (adr->bottom_type()->is_ptr_to_narrowoop()) { const TypeNarrowOop* narrowtype; if (rt->isa_narrowoop()) { narrowtype = rt->is_narrowoop(); @@ -761,10 +769,10 @@ return DecodeNNode::decode(&gvn, load); } else #endif - { - assert(!adr->bottom_type()->is_narrow(), "should have got back a narrow oop"); - return new (C, 3) LoadPNode(ctl, mem, adr, adr_type, rt->is_oopptr()); - } + { + assert(!adr->bottom_type()->is_ptr_to_narrowoop(), "should have got back a narrow oop"); + return new (C, 3) LoadPNode(ctl, mem, adr, adr_type, rt->is_oopptr()); + } } ShouldNotReachHere(); return (LoadNode*)NULL; @@ -1118,6 +1126,127 @@ return NULL; } +//------------------------------split_through_phi------------------------------ +// Split instance field load through Phi. +Node *LoadNode::split_through_phi(PhaseGVN *phase) { + Node* mem = in(MemNode::Memory); + Node* address = in(MemNode::Address); + const TypePtr *addr_t = phase->type(address)->isa_ptr(); + const TypeOopPtr *t_oop = addr_t->isa_oopptr(); + + assert(mem->is_Phi() && (t_oop != NULL) && + t_oop->is_instance_field(), "invalide conditions"); + + Node *region = mem->in(0); + if (region == NULL) { + return NULL; // Wait stable graph + } + uint cnt = mem->req(); + for( uint i = 1; i < cnt; i++ ) { + Node *in = mem->in(i); + if( in == NULL ) { + return NULL; // Wait stable graph + } + } + // Check for loop invariant. + if (cnt == 3) { + for( uint i = 1; i < cnt; i++ ) { + Node *in = mem->in(i); + Node* m = MemNode::optimize_memory_chain(in, addr_t, phase); + if (m == mem) { + set_req(MemNode::Memory, 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"); + + // Do nothing here if Identity will find a value + // (to avoid infinite chain of value phis generation). + if ( !phase->eqv(this, this->Identity(phase)) ) + return NULL; + + // Skip the split if the region dominates some control edge of the address. + if (cnt == 3 && !MemNode::all_controls_dominate(address, region)) + return NULL; + + 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; + } + igvn->remove_dead_node(phi); + return NULL; +} //------------------------------Ideal------------------------------------------ // If the load is from Field memory and the pointer is non-null, we can @@ -1175,112 +1304,9 @@ 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"); - - // Do nothing here if Identity will find a value - // (to avoid infinite chain of value phis generation). - if ( !phase->eqv(this, this->Identity(phase)) ) - return NULL; - - 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); - } - } + // Split instance field load through Phi. + Node* result = split_through_phi(phase); + if (result != NULL) return result; } } @@ -1835,7 +1861,7 @@ case T_ADDRESS: case T_OBJECT: #ifdef _LP64 - if (adr->bottom_type()->is_narrow() || + if (adr->bottom_type()->is_ptr_to_narrowoop() || (UseCompressedOops && val->bottom_type()->isa_klassptr() && adr->bottom_type()->isa_rawptr())) { const TypePtr* type = val->bottom_type()->is_ptr(); diff -r 8aa010f60e0f -r 885ed790ecf0 src/share/vm/opto/memnode.hpp --- a/src/share/vm/opto/memnode.hpp Tue May 20 06:32:58 2008 -0700 +++ b/src/share/vm/opto/memnode.hpp Wed May 21 10:45:07 2008 -0700 @@ -72,7 +72,8 @@ // This one should probably be a phase-specific function: static bool all_controls_dominate(Node* dom, Node* sub); - // Is this Node a MemNode or some descendent? Default is YES. + // Find any cast-away of null-ness and keep its control. + static Node *Ideal_common_DU_postCCP( PhaseCCP *ccp, Node* n, Node* adr ); virtual Node *Ideal_DU_postCCP( PhaseCCP *ccp ); virtual const class TypePtr *adr_type() const; // returns bottom_type of address @@ -150,6 +151,9 @@ // zero out the control input. virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + // Split instance field load through Phi. + Node* split_through_phi(PhaseGVN *phase); + // Recover original value from boxed values Node *eliminate_autobox(PhaseGVN *phase); diff -r 8aa010f60e0f -r 885ed790ecf0 src/share/vm/opto/node.cpp --- a/src/share/vm/opto/node.cpp Tue May 20 06:32:58 2008 -0700 +++ b/src/share/vm/opto/node.cpp Wed May 21 10:45:07 2008 -0700 @@ -1049,51 +1049,80 @@ Node* orig_sub = sub; nlist.clear(); bool this_dominates = false; - uint region_input = 0; + bool result = false; // Conservative answer + while (sub != NULL) { // walk 'sub' up the chain to 'this' if (sub == this) { if (nlist.size() == 0) { // No Region nodes except loops were visited before and the EntryControl // path was taken for loops: it did not walk in a cycle. - return true; - } else if (!this_dominates) { + result = true; + break; + } else if (this_dominates) { + result = false; // already met before: walk in a cycle + break; + } else { // Region nodes were visited. Continue walk up to Start or Root // to make sure that it did not walk in a cycle. this_dominates = true; // first time meet iterations_without_region_limit = DominatorSearchLimit; // Reset - } else { - return false; // already met before: walk in a cycle - } + } } - if (sub->is_Start() || sub->is_Root()) - return this_dominates; - + if (sub->is_Start() || sub->is_Root()) { + result = this_dominates; + break; + } Node* up = sub->find_exact_control(sub->in(0)); - if (up == NULL || up->is_top()) - return false; // Conservative answer for dead code + if (up == NULL || up->is_top()) { + result = false; // Conservative answer for dead code + break; + } + if (sub == up && (sub->is_Loop() || sub->is_Region() && sub->req() != 3)) { + // Take first valid path on the way up to 'this'. + up = sub->in(1); // in(LoopNode::EntryControl); + } else if (sub == up && sub->is_Region()) { + assert(sub->req() == 3, "sanity"); + iterations_without_region_limit = DominatorSearchLimit; // Reset - if (sub == up && sub->is_Loop()) { - up = sub->in(1); // in(LoopNode::EntryControl); - } else if (sub == up && sub->is_Region() && sub->req() == 3) { - iterations_without_region_limit = DominatorSearchLimit; // Reset + // Try both paths for such Regions. + // It is not accurate without regions dominating information. + // With such information the other path should be checked for + // the most dominating Region which was visited before. + bool region_was_visited_before = false; uint i = 1; uint size = nlist.size(); if (size == 0) { - // No Region nodes (except Loops) were visited before. + // No such Region nodes were visited before. // Take first valid path on the way up to 'this'. - } else if (nlist.at(size - 1) == sub) { - // This Region node was just visited. Take other path. - i = region_input + 1; - nlist.pop(); } else { // Was this Region node visited before? - for (uint j = 0; j < size; j++) { - if (nlist.at(j) == sub) { - return false; // The Region node was visited before. Give up. + intptr_t ni; + int j = size - 1; + for (; j >= 0; j--) { + ni = (intptr_t)nlist.at(j); + if ((Node*)(ni & ~1) == sub) { + if ((ni & 1) != 0) { + break; // Visited 2 paths. Give up. + } else { + // The Region node was visited before only once. + nlist.remove(j); + region_was_visited_before = true; + for (; i < sub->req(); i++) { + Node* in = sub->in(i); + if (in != NULL && !in->is_top() && in != sub) { + break; + } + } + i++; // Take other path. + break; + } } } + if (j >= 0 && (ni & 1) != 0) { + result = false; // Visited 2 paths. Give up. + break; + } // The Region node was not visited before. - // Take first valid path on the way up to 'this'. } for (; i < sub->req(); i++) { Node* in = sub->in(i); @@ -1102,20 +1131,26 @@ } } if (i < sub->req()) { - nlist.push(sub); up = sub->in(i); - region_input = i; + if (region_was_visited_before && sub != up) { + // Set 0 bit to indicate that both paths were taken. + nlist.push((Node*)((intptr_t)sub + 1)); + } else { + nlist.push(sub); + } } } - if (sub == up) - return false; // some kind of tight cycle - - if (--iterations_without_region_limit < 0) - return false; // dead cycle - + if (sub == up) { + result = false; // some kind of tight cycle + break; + } + if (--iterations_without_region_limit < 0) { + result = false; // dead cycle + break; + } sub = up; } - return false; + return result; } //------------------------------remove_dead_region----------------------------- diff -r 8aa010f60e0f -r 885ed790ecf0 src/share/vm/opto/output.cpp --- a/src/share/vm/opto/output.cpp Tue May 20 06:32:58 2008 -0700 +++ b/src/share/vm/opto/output.cpp Wed May 21 10:45:07 2008 -0700 @@ -48,6 +48,7 @@ // Initialize the space for the BufferBlob used to find and verify // instruction size in MachNode::emit_size() init_scratch_buffer_blob(); + if (failing()) return; // Out of memory // Make sure I can find the Start Node Block_Array& bbs = _cfg->_bbs; diff -r 8aa010f60e0f -r 885ed790ecf0 src/share/vm/opto/type.cpp --- a/src/share/vm/opto/type.cpp Tue May 20 06:32:58 2008 -0700 +++ b/src/share/vm/opto/type.cpp Wed May 21 10:45:07 2008 -0700 @@ -311,8 +311,18 @@ mreg2type[Op_RegFlags] = TypeInt::CC; TypeAryPtr::RANGE = TypeAryPtr::make( TypePtr::BotPTR, TypeAry::make(Type::BOTTOM,TypeInt::POS), current->env()->Object_klass(), false, arrayOopDesc::length_offset_in_bytes()); - // There is no shared klass for Object[]. See note in TypeAryPtr::klass(). - TypeAryPtr::OOPS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInstPtr::BOTTOM,TypeInt::POS), NULL /*ciArrayKlass::make(o)*/, false, Type::OffsetBot); + + TypeAryPtr::NARROWOOPS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeNarrowOop::BOTTOM, TypeInt::POS), NULL /*ciArrayKlass::make(o)*/, false, Type::OffsetBot); + +#ifdef _LP64 + if (UseCompressedOops) { + TypeAryPtr::OOPS = TypeAryPtr::NARROWOOPS; + } else +#endif + { + // There is no shared klass for Object[]. See note in TypeAryPtr::klass(). + TypeAryPtr::OOPS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInstPtr::BOTTOM,TypeInt::POS), NULL /*ciArrayKlass::make(o)*/, false, Type::OffsetBot); + } TypeAryPtr::BYTES = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInt::BYTE ,TypeInt::POS), ciTypeArrayKlass::make(T_BYTE), true, Type::OffsetBot); TypeAryPtr::SHORTS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInt::SHORT ,TypeInt::POS), ciTypeArrayKlass::make(T_SHORT), true, Type::OffsetBot); TypeAryPtr::CHARS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(TypeInt::CHAR ,TypeInt::POS), ciTypeArrayKlass::make(T_CHAR), true, Type::OffsetBot); @@ -321,9 +331,10 @@ TypeAryPtr::FLOATS = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(Type::FLOAT ,TypeInt::POS), ciTypeArrayKlass::make(T_FLOAT), true, Type::OffsetBot); TypeAryPtr::DOUBLES = TypeAryPtr::make(TypePtr::BotPTR, TypeAry::make(Type::DOUBLE ,TypeInt::POS), ciTypeArrayKlass::make(T_DOUBLE), true, Type::OffsetBot); - TypeAryPtr::_array_body_type[T_NARROWOOP] = NULL; // what should this be? + // Nobody should ask _array_body_type[T_NARROWOOP]. Use NULL as assert. + TypeAryPtr::_array_body_type[T_NARROWOOP] = NULL; TypeAryPtr::_array_body_type[T_OBJECT] = TypeAryPtr::OOPS; - TypeAryPtr::_array_body_type[T_ARRAY] = TypeAryPtr::OOPS; // arrays are stored in oop arrays + TypeAryPtr::_array_body_type[T_ARRAY] = TypeAryPtr::OOPS; // arrays are stored in oop arrays TypeAryPtr::_array_body_type[T_BYTE] = TypeAryPtr::BYTES; TypeAryPtr::_array_body_type[T_BOOLEAN] = TypeAryPtr::BYTES; // boolean[] is a byte array TypeAryPtr::_array_body_type[T_SHORT] = TypeAryPtr::SHORTS; @@ -696,7 +707,7 @@ ResourceMark rm; Dict d(cmpkey,hashkey); // Stop recursive type dumping dump2(d,1, st); - if (isa_ptr() && is_ptr()->is_narrow()) { + if (is_ptr_to_narrowoop()) { st->print(" [narrow]"); } } @@ -2146,6 +2157,67 @@ // Convenience common pre-built type. const TypeOopPtr *TypeOopPtr::BOTTOM; +//------------------------------TypeOopPtr------------------------------------- +TypeOopPtr::TypeOopPtr( TYPES t, PTR ptr, ciKlass* k, bool xk, ciObject* o, int offset, int instance_id ) + : TypePtr(t, ptr, offset), + _const_oop(o), _klass(k), + _klass_is_exact(xk), + _is_ptr_to_narrowoop(false), + _instance_id(instance_id) { +#ifdef _LP64 + if (UseCompressedOops && _offset != 0) { + if (klass() == NULL) { + assert(this->isa_aryptr(), "only arrays without klass"); + _is_ptr_to_narrowoop = true; + } else if (_offset == oopDesc::klass_offset_in_bytes()) { + _is_ptr_to_narrowoop = true; + } else if (this->isa_aryptr()) { + _is_ptr_to_narrowoop = (klass()->is_obj_array_klass() && + _offset != arrayOopDesc::length_offset_in_bytes()); + } else if (klass() == ciEnv::current()->Class_klass() && + (_offset == java_lang_Class::klass_offset_in_bytes() || + _offset == java_lang_Class::array_klass_offset_in_bytes())) { + // Special hidden fields from the Class. + assert(this->isa_instptr(), "must be an instance ptr."); + _is_ptr_to_narrowoop = true; + } else if (klass()->is_instance_klass()) { + ciInstanceKlass* ik = klass()->as_instance_klass(); + ciField* field = NULL; + if (this->isa_klassptr()) { + // Perm objects don't use compressed references, except for + // static fields which are currently compressed. + field = ik->get_field_by_offset(_offset, true); + if (field != NULL) { + BasicType basic_elem_type = field->layout_type(); + _is_ptr_to_narrowoop = (basic_elem_type == T_OBJECT || + basic_elem_type == T_ARRAY); + } + } else if (_offset == OffsetBot || _offset == OffsetTop) { + // unsafe access + _is_ptr_to_narrowoop = true; + } else { // exclude unsafe ops + assert(this->isa_instptr(), "must be an instance ptr."); + // Field which contains a compressed oop references. + field = ik->get_field_by_offset(_offset, false); + if (field != NULL) { + BasicType basic_elem_type = field->layout_type(); + _is_ptr_to_narrowoop = (basic_elem_type == T_OBJECT || + basic_elem_type == T_ARRAY); + } else if (klass()->equals(ciEnv::current()->Object_klass())) { + // Compile::find_alias_type() cast exactness on all types to verify + // that it does not affect alias type. + _is_ptr_to_narrowoop = true; + } else { + // Type for the copy start in LibraryCallKit::inline_native_clone(). + assert(!klass_is_exact(), "only non-exact klass"); + _is_ptr_to_narrowoop = true; + } + } + } + } +#endif +} + //------------------------------make------------------------------------------- const TypeOopPtr *TypeOopPtr::make(PTR ptr, int offset) { @@ -2593,9 +2665,13 @@ //-----------------------------cast_to_instance------------------------------- const TypeOopPtr *TypeInstPtr::cast_to_instance(int instance_id) const { if( instance_id == _instance_id) return this; - bool exact = (instance_id == UNKNOWN_INSTANCE) ? _klass_is_exact : true; - - return make(ptr(), klass(), exact, const_oop(), _offset, instance_id); + bool exact = true; + PTR ptr_t = NotNull; + if (instance_id == UNKNOWN_INSTANCE) { + exact = _klass_is_exact; + ptr_t = _ptr; + } + return make(ptr_t, klass(), exact, const_oop(), _offset, instance_id); } //------------------------------xmeet_unloaded--------------------------------- @@ -3014,6 +3090,7 @@ // Convenience common pre-built types. const TypeAryPtr *TypeAryPtr::RANGE; const TypeAryPtr *TypeAryPtr::OOPS; +const TypeAryPtr *TypeAryPtr::NARROWOOPS; const TypeAryPtr *TypeAryPtr::BYTES; const TypeAryPtr *TypeAryPtr::SHORTS; const TypeAryPtr *TypeAryPtr::CHARS; @@ -3063,8 +3140,13 @@ //-----------------------------cast_to_instance------------------------------- const TypeOopPtr *TypeAryPtr::cast_to_instance(int instance_id) const { if( instance_id == _instance_id) return this; - bool exact = (instance_id == UNKNOWN_INSTANCE) ? _klass_is_exact : true; - return make(ptr(), const_oop(), _ary, klass(), exact, _offset, instance_id); + bool exact = true; + PTR ptr_t = NotNull; + if (instance_id == UNKNOWN_INSTANCE) { + exact = _klass_is_exact; + ptr_t = _ptr; + } + return make(ptr_t, const_oop(), _ary, klass(), exact, _offset, instance_id); } //-----------------------------narrow_size_type------------------------------- @@ -3547,7 +3629,7 @@ k_ary = ciTypeArrayKlass::make(el->basic_type()); } - if( this != TypeAryPtr::OOPS ) + if( this != TypeAryPtr::OOPS ) { // The _klass field acts as a cache of the underlying // ciKlass for this array type. In order to set the field, // we need to cast away const-ness. @@ -3562,6 +3644,11 @@ // a bit less efficient than caching, but calls to // TypeAryPtr::OOPS->klass() are not common enough to matter. ((TypeAryPtr*)this)->_klass = k_ary; + if (UseCompressedOops && k_ary != NULL && k_ary->is_obj_array_klass() && + _offset != 0 && _offset != arrayOopDesc::length_offset_in_bytes()) { + ((TypeAryPtr*)this)->_is_ptr_to_narrowoop = true; + } + } return k_ary; } diff -r 8aa010f60e0f -r 885ed790ecf0 src/share/vm/opto/type.hpp --- a/src/share/vm/opto/type.hpp Tue May 20 06:32:58 2008 -0700 +++ b/src/share/vm/opto/type.hpp Wed May 21 10:45:07 2008 -0700 @@ -191,9 +191,8 @@ virtual const Type *filter( const Type *kills ) const; // Returns true if this pointer points at memory which contains a - // compressed oop references. In 32-bit builds it's non-virtual - // since we don't support compressed oops at all in the mode. - LP64_ONLY(virtual) bool is_narrow() const { return false; } + // compressed oop references. + bool is_ptr_to_narrowoop() const; // Convenience access float getf() const; @@ -213,8 +212,8 @@ const TypePtr *isa_ptr() const; // Returns NULL if not ptr type const TypeRawPtr *isa_rawptr() const; // NOT Java oop const TypeRawPtr *is_rawptr() const; // Asserts is rawptr - const TypeNarrowOop *is_narrowoop() const; // Java-style GC'd pointer - const TypeNarrowOop *isa_narrowoop() const; // Returns NULL if not oop ptr type + const TypeNarrowOop *is_narrowoop() const; // Java-style GC'd pointer + const TypeNarrowOop *isa_narrowoop() const; // Returns NULL if not oop ptr type const TypeOopPtr *isa_oopptr() const; // Returns NULL if not oop ptr type const TypeOopPtr *is_oopptr() const; // Java-style GC'd pointer const TypeKlassPtr *isa_klassptr() const; // Returns NULL if not KlassPtr @@ -643,7 +642,7 @@ // Some kind of oop (Java pointer), either klass or instance or array. class TypeOopPtr : public TypePtr { protected: - TypeOopPtr( TYPES t, PTR ptr, ciKlass* k, bool xk, ciObject* o, int offset, int instance_id ) : TypePtr(t, ptr, offset), _const_oop(o), _klass(k), _klass_is_exact(xk), _instance_id(instance_id) { } + TypeOopPtr( TYPES t, PTR ptr, ciKlass* k, bool xk, ciObject* o, int offset, int instance_id ); public: virtual bool eq( const Type *t ) const; virtual int hash() const; // Type specific hashing @@ -660,8 +659,9 @@ ciKlass* _klass; // Klass object // Does the type exclude subclasses of the klass? (Inexact == polymorphic.) bool _klass_is_exact; + bool _is_ptr_to_narrowoop; - int _instance_id; // if not UNKNOWN_INSTANCE, indicates that this is a particular instance + int _instance_id; // if not UNKNOWN_INSTANCE, indicates that this is a particular instance // of this type which is distinct. This is the the node index of the // node creating this instance @@ -696,6 +696,11 @@ ciObject* const_oop() const { return _const_oop; } virtual ciKlass* klass() const { return _klass; } bool klass_is_exact() const { return _klass_is_exact; } + + // Returns true if this pointer points at memory which contains a + // compressed oop references. + bool is_ptr_to_narrowoop_nv() const { return _is_ptr_to_narrowoop; } + 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; } @@ -716,12 +721,6 @@ // returns the equivalent compressed version of this pointer type virtual const TypeNarrowOop* make_narrowoop() const; -#ifdef _LP64 - virtual bool is_narrow() const { - return (UseCompressedOops && _offset != 0); - } -#endif - virtual const Type *xmeet( const Type *t ) const; virtual const Type *xdual() const; // Compute dual right now. @@ -843,15 +842,10 @@ virtual const Type *xmeet( const Type *t ) const; virtual const Type *xdual() const; // Compute dual right now. -#ifdef _LP64 - virtual bool is_narrow() const { - return (UseCompressedOops && klass() != NULL && _offset != 0); - } -#endif - // Convenience common pre-built types. static const TypeAryPtr *RANGE; static const TypeAryPtr *OOPS; + static const TypeAryPtr *NARROWOOPS; static const TypeAryPtr *BYTES; static const TypeAryPtr *SHORTS; static const TypeAryPtr *CHARS; @@ -901,18 +895,6 @@ virtual const Type *xmeet( const Type *t ) const; virtual const Type *xdual() const; // Compute dual right now. -#ifdef _LP64 - // Perm objects don't use compressed references, except for static fields - // which are currently compressed - virtual bool is_narrow() const { - if (UseCompressedOops && _offset != 0 && _klass->is_instance_klass()) { - ciInstanceKlass* ik = _klass->as_instance_klass(); - return ik != NULL && ik->get_field_by_offset(_offset, true) != NULL; - } - return false; - } -#endif - // Convenience common pre-built types. static const TypeKlassPtr* OBJECT; // Not-null object klass or below static const TypeKlassPtr* OBJECT_OR_NULL; // Maybe-null version of same @@ -921,7 +903,7 @@ #endif }; -//------------------------------TypeNarrowOop---------------------------------------- +//------------------------------TypeNarrowOop---------------------------------- // A compressed reference to some kind of Oop. This type wraps around // a preexisting TypeOopPtr and forwards most of it's operations to // the underlying type. It's only real purpose is to track the @@ -1013,6 +995,14 @@ }; //------------------------------accessors-------------------------------------- +inline bool Type::is_ptr_to_narrowoop() const { +#ifdef _LP64 + return (isa_oopptr() != NULL && is_oopptr()->is_ptr_to_narrowoop_nv()); +#else + return false; +#endif +} + inline float Type::getf() const { assert( _base == FloatCon, "Not a FloatCon" ); return ((TypeF*)this)->_f; diff -r 8aa010f60e0f -r 885ed790ecf0 test/compiler/6689060/Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/6689060/Test.java Wed May 21 10:45:07 2008 -0700 @@ -0,0 +1,577 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + +/* + * @test + * @bug 6689060 + * @summary Escape Analysis does not work with Compressed Oops + * @run main/othervm -Xbatch -XX:CompileCommand=exclude,Test.dummy -XX:+AggressiveOpts Test + */ + +import java.lang.reflect.Array; + +class Point { + int x; + int y; + Point next; + int ax[]; + int ay[]; + Point pax[]; + Point pay[]; + public Point getNext() { + return next; + } +} + +public class Test { + + void dummy() { + // Empty method to verify correctness of DebugInfo. + // Use -XX:CompileCommand=exclude,Test.dummy + } + + int ival(int i) { + return i*2; + } + + int test80(int y, int l, int i) { + Point p = new Point(); + p.ax = new int[2]; + p.ay = new int[2]; + int x = 3; + p.ax[0] = x; + p.ay[1] = 3 * x + y; + dummy(); + return p.ax[0] * p.ay[1]; + } + + int test81(int y, int l, int i) { + Point p = new Point(); + p.ax = new int[2]; + p.ay = new int[2]; + int x = 3; + p.ax[0] = x; + p.ay[1] = 3 * x + y; + dummy(); + return p.ax[0] * p.ay[1]; + } + + + int test44(int y) { + Point p1 = new Point(); + p1.x = ival(3); + dummy(); + p1.y = 3 * p1.x + y; + return p1.y; + } + + int test43(int y) { + Point p1 = new Point(); + if ( (y & 1) == 1 ) { + p1.x = ival(3); + } else { + p1.x = ival(5); + } + dummy(); + p1.y = 3 * p1.x + y; + return p1.y; + } + + int test42(int y) { + Point p1 = new Point(); + p1.x = 3; + for (int i = 0; i < y; i++) { + if ( (i & 1) == 1 ) { + p1.x += 4; + } + } + p1.y = 3 * y + p1.x; + return p1.y; + } + + int test40(int y) { + Point p1 = new Point(); + if ( (y & 1) == 1 ) { + p1.x = 3; + } else { + p1.x = 5; + } + p1.y = 3 * p1.x + y; + return p1.y; + } + + int test41(int y) { + Point p1 = new Point(); + if ( (y & 1) == 1 ) { + p1.x += 4; + } else { + p1.x += 5; + } + p1.y = 3 * p1.x + y; + return p1.y; + } + + Point test00(int y) { + int x = 3; + Point p = new Point(); + p.x = x; + p.y = 3 * x + y; + return p; + } + + Point test01(int y) { + int x = 3; + Point p = new Point(); + p.x = x; + p.y = 3 * x + y; + dummy(); + return p; + } + + Point test02(int y) { + int x = 3; + Point p1 = null; + for (int i = 0; i < y; i++) { + Point p2 = new Point(); + p2.x = x; + p2.y = 3 * y + x; + p2.next = p1; + p1 = p2; + } + return p1; + } + + Point test03(int y) { + int x = 3; + Point p1 = null; + for (int i = 0; i < y; i++) { + Point p2 = new Point(); + p2.x = x; + p2.y = 3 * y + x; + p2.next = p1; + p1 = p2; + } + dummy(); + return p1; + } + + Point test04(int y) { + int x = 3; + Point p1 = null; + for (int i = 0; i < y; i++) { + Point p2 = new Point(); + p2.x = x; + p2.y = 3 * y + x; + p2.next = p1; + dummy(); + p1 = p2; + } + return p1; + } + + int test05(int y) { + int x = 3; + Point p1 = new Point(); + for (int i = 0; i < y; i++) { + Point p2 = new Point(); + p2.x = x; + p2.y = 3 * y + x; + p1.next = p2; + p1 = p2; + } + return p1.y; + } + + int test0(int y) { + int x = 3; + Point p = new Point(); + p.x = x; + p.y = 3 * x + y; + dummy(); + return p.x * p.y; + } + + int test1(int y) { + Point p = new Point(); + if ( (y & 1) == 1 ) { + p = new Point(); // Kill previous + } + int x = 3; + p.x = x; + p.y = 3 * x + y; + dummy(); + return p.x * p.y; + } + + int test2(int y) { + Point p1 = new Point(); + Point p2 = new Point(); + p1.x = 3; + p2.x = 4; + p1.y = 3 * p2.x + y; + p2.y = 3 * p1.x + y; + dummy(); + return p1.y * p2.y; + } + + int test3(int y, Point p1) { + Point p2 = new Point(); + p1.x = 3; + p2.x = 4; + p1.y = 3 * p2.x + y; + p2.y = 3 * p1.x + y; + dummy(); + return p1.y * p2.y; + } + + int test4(int y) { + Point p1 = new Point(); + Point p2 = new Point(); + if ( (y & 1) == 1 ) { + p1.x = 3; + p2.x = 4; + } else { + p1.x = 5; + p2.x = 6; + } + p1.y = 3 * p2.x + y; + p2.y = 3 * p1.x + y; + dummy(); + return p1.y * p2.y; + } + + int test5(int y, Point p1) { + Point p2 = new Point(); + if ( (y & 1) == 1 ) { + p1.x = 3; + p2.x = 4; + } else { + p1.x = 5; + p2.x = 6; + } + p1.y = 3 * p2.x + y; + p2.y = 3 * p1.x + y; + dummy(); + return p1.y * p2.y; + } + + int test6(int y) { + Point p1 = new Point(); + Point p2 = new Point(); + p1.next = p2; + if ( (y & 1) == 1 ) { + p1.x = 3; + p1.getNext().x = 4; + } else { + p1.x = 5; + p1.getNext().x = 6; + } + p1.y = 3 * p2.x + y; + p2.y = 3 * p1.x + y; + dummy(); + return p1.y * p2.y; + } + + int test7(int y, Point p1) { + Point p2 = new Point(); + p1.next = p2; + if ( (y & 1) == 1 ) { + p1.x = 3; + p1.getNext().x = 4; + } else { + p1.x = 5; + p1.getNext().x = 6; + } + p1.y = 3 * p2.x + y; + p2.y = 3 * p1.x + y; + dummy(); + return p1.y * p2.y; + } + + int test8(int y, int l, int i) { + Point p = new Point(); + p.ax = new int[l]; + p.ay = new int[l]; + int x = 3; + p.ax[i] = x; + p.ay[i] = 3 * x + y; + dummy(); + return p.ax[i] * p.ay[i]; + } + + int test9(int y, int l, int i) { + Point p = new Point(); + p.pax = new Point[l]; + p.pay = new Point[l]; + p.pax[i] = new Point(); + p.pay[i] = new Point(); + p.pax[i].x = 3; + p.pay[i].x = 4; + p.pax[i].y = 3 * p.pay[i].x + y; + p.pay[i].y = 3 * p.pax[i].x + y; + dummy(); + return p.pax[i].y * p.pay[i].y; + } + + int test10(int y, int l, int i, Class cls) { + Point p = new Point(); + try { + p.pax = (Point[])Array.newInstance(cls, l); + p.pax[i] = (Point)cls.newInstance(); + } + catch(java.lang.InstantiationException ex) { + return 0; + } + catch(java.lang.IllegalAccessException ex) { + return 0; + } + p.pax[i].x = 3; + p.pax[i].y = 3 * p.pax[i].x + y; + dummy(); + return p.pax[i].x * p.pax[i].y; + } + + int test11(int y) { + Point p1 = new Point(); + Point p2 = new Point(); + p1.next = p2; + if ( (y & 1) == 1 ) { + p1.x = 3; + p1.next.x = 4; + } else { + p1.x = 5; + p1.next.x = 6; + } + p1.y = 3 * p1.next.x + y; + p1.next.y = 3 * p1.x + y; + dummy(); + return p1.y * p1.next.y; + } + + int test12(int y) { + Point p1 = new Point(); + p1.next = p1; + if ( (y & 1) == 1 ) { + p1.x = 3; + p1.next.x = 4; + } else { + p1.x = 5; + p1.next.x = 6; + } + p1.y = 3 * p1.next.x + y; + p1.next.y = 3 * p1.x + y; + dummy(); + return p1.y * p1.next.y; + } + + + public static void main(String args[]) { + Test tsr = new Test(); + Point p = new Point(); + Point ptmp = p; + Class cls = Point.class; + int y = 0; + for (int i=0; i<10000; i++) { + ptmp.next = tsr.test00(1); + ptmp.next = tsr.test01(1); + ptmp.next = tsr.test02(1); + ptmp.next = tsr.test03(1); + ptmp.next = tsr.test04(1); + + y = tsr.test05(1); + + y = tsr.test80(y, 1, 0); + y = tsr.test81(y, 1, 0); + + y = tsr.test44(y); + y = tsr.test43(y); + y = tsr.test42(y); + y = tsr.test40(y); + y = tsr.test41(y); + + y = tsr.test0(y); + y = tsr.test1(y); + y = tsr.test2(y); + y = tsr.test3(y, p); + y = tsr.test4(y); + y = tsr.test5(y, p); + y = tsr.test6(y); + y = tsr.test7(y, p); + y = tsr.test8(y, 1, 0); + y = tsr.test9(y, 1, 0); + y = tsr.test10(y, 1, 0, cls); + y = tsr.test11(y); + y = tsr.test12(y); + } + for (int i=0; i<10000; i++) { + ptmp.next = tsr.test00(1); + ptmp.next = tsr.test01(1); + ptmp.next = tsr.test02(1); + ptmp.next = tsr.test03(1); + ptmp.next = tsr.test04(1); + + y = tsr.test05(1); + + y = tsr.test80(y, 1, 0); + y = tsr.test81(y, 1, 0); + + y = tsr.test44(y); + y = tsr.test43(y); + y = tsr.test42(y); + y = tsr.test40(y); + y = tsr.test41(y); + + y = tsr.test0(y); + y = tsr.test1(y); + y = tsr.test2(y); + y = tsr.test3(y, p); + y = tsr.test4(y); + y = tsr.test5(y, p); + y = tsr.test6(y); + y = tsr.test7(y, p); + y = tsr.test8(y, 1, 0); + y = tsr.test9(y, 1, 0); + y = tsr.test10(y, 1, 0, cls); + y = tsr.test11(y); + y = tsr.test12(y); + } + for (int i=0; i<10000; i++) { + ptmp.next = tsr.test00(1); + ptmp.next = tsr.test01(1); + ptmp.next = tsr.test02(1); + ptmp.next = tsr.test03(1); + ptmp.next = tsr.test04(1); + + y = tsr.test05(1); + + y = tsr.test80(y, 1, 0); + y = tsr.test81(y, 1, 0); + + y = tsr.test44(y); + y = tsr.test43(y); + y = tsr.test42(y); + y = tsr.test40(y); + y = tsr.test41(y); + + y = tsr.test0(y); + y = tsr.test1(y); + y = tsr.test2(y); + y = tsr.test3(y, p); + y = tsr.test4(y); + y = tsr.test5(y, p); + y = tsr.test6(y); + y = tsr.test7(y, p); + y = tsr.test8(y, 1, 0); + y = tsr.test9(y, 1, 0); + y = tsr.test10(y, 1, 0, cls); + y = tsr.test11(y); + y = tsr.test12(y); + } + + int z = 0; + y = tsr.test80(0, 1, 0); + z += y; + System.out.println("After 'test80' y=" + y); + y = tsr.test81(0, 1, 0); + z += y; + System.out.println("After 'test81' y=" + y); + + y = tsr.test44(0); + z += y; + System.out.println("After 'test44' y=" + y); + y = tsr.test43(0); + z += y; + System.out.println("After 'test43' y=" + y); + y = tsr.test42(0); + z += y; + System.out.println("After 'test42' y=" + y); + y = tsr.test40(0); + z += y; + System.out.println("After 'test40' y=" + y); + y = tsr.test41(0); + z += y; + System.out.println("After 'test41' y=" + y); + + ptmp.next = tsr.test00(1); + z += y; + System.out.println("After 'test00' p.y=" + ptmp.next.y); + ptmp.next = tsr.test01(1); + z += y; + System.out.println("After 'test01' p.y=" + ptmp.next.y); + ptmp.next = tsr.test02(1); + z += y; + System.out.println("After 'test02' p.y=" + ptmp.next.y); + ptmp.next = tsr.test03(1); + z += y; + System.out.println("After 'test03' p.y=" + ptmp.next.y); + ptmp.next = tsr.test04(1); + z += y; + System.out.println("After 'test04' p.y=" + ptmp.next.y); + + y = tsr.test05(1); + z += y; + System.out.println("After 'test05' y=" + y); + + y = tsr.test0(0); + z += y; + System.out.println("After 'test0' y=" + y); + y = tsr.test1(0); + z += y; + System.out.println("After 'test1' y=" + y); + y = tsr.test2(0); + z += y; + System.out.println("After 'test2' y=" + y); + y = tsr.test3(0, new Point()); + z += y; + System.out.println("After 'test3' y=" + y); + y = tsr.test4(0); + z += y; + System.out.println("After 'test4' y=" + y); + y = tsr.test5(0, new Point()); + z += y; + System.out.println("After 'test5' y=" + y); + y = tsr.test6(0); + z += y; + System.out.println("After 'test6' y=" + y); + y = tsr.test7(0, new Point()); + z += y; + System.out.println("After 'test7' y=" + y); + y = tsr.test8(0, 1, 0); + z += y; + System.out.println("After 'test8' y=" + y); + y = tsr.test9(0, 1, 0); + z += y; + System.out.println("After 'test9' y=" + y); + y = tsr.test10(0, 1, 0, cls); + z += y; + System.out.println("After 'test10' y=" + y); + y = tsr.test11(0); + z += y; + System.out.println("After 'test11' y=" + y); + y = tsr.test12(0); + z += y; + System.out.println("After 'test12' y=" + y); + System.out.println("Sum of y =" + z); + } +} diff -r 8aa010f60e0f -r 885ed790ecf0 test/compiler/6695810/Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/6695810/Test.java Wed May 21 10:45:07 2008 -0700 @@ -0,0 +1,56 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + +/* + * @test + * @bug 6695810 + * @summary null oop passed to encode_heap_oop_not_null + * @run main/othervm -Xbatch Test + */ + +public class Test { + Test _t; + + static void test(Test t1, Test t2) { + if (t2 != null) + t1._t = t2; + + if (t2 != null) + t1._t = t2; + } + + public static void main(String[] args) { + Test t = new Test(); + for (int i = 0; i < 50; i++) { + for (int j = 0; j < 100; j++) { + test(t, t); + } + test(t, null); + } + for (int i = 0; i < 10000; i++) { + test(t, t); + } + test(t, null); + } +}