# HG changeset patch # User kvn # Date 1222198146 25200 # Node ID 36ccc817fca4894becf4c75a51324d4a77790890 # Parent 194b8e3a2fc4cbee8f16ddad5ba22d4d4d40393f 6747051: Improve code and implicit null check generation for compressed oops Summary: Push DecodeN node below the Null check to the non-null path to use the mach node without 0 test. Reviewed-by: rasbold, never diff -r 194b8e3a2fc4 -r 36ccc817fca4 src/share/vm/asm/assembler.cpp --- a/src/share/vm/asm/assembler.cpp Wed Sep 17 12:59:52 2008 -0700 +++ b/src/share/vm/asm/assembler.cpp Tue Sep 23 12:29:06 2008 -0700 @@ -249,8 +249,6 @@ bool MacroAssembler::needs_explicit_null_check(intptr_t offset) { // Exception handler checks the nmethod's implicit null checks table // only when this method returns false. -#ifndef SPARC - // Sparc does not have based addressing if (UseCompressedOops) { // The first page after heap_base is unmapped and // the 'offset' is equal to [heap_base + offset] for @@ -261,7 +259,6 @@ offset = (intptr_t)(pointer_delta((void*)offset, (void*)heap_base, 1)); } } -#endif // SPARC return offset < 0 || os::vm_page_size() <= offset; } diff -r 194b8e3a2fc4 -r 36ccc817fca4 src/share/vm/opto/cfgnode.cpp --- a/src/share/vm/opto/cfgnode.cpp Wed Sep 17 12:59:52 2008 -0700 +++ b/src/share/vm/opto/cfgnode.cpp Tue Sep 23 12:29:06 2008 -0700 @@ -1769,6 +1769,51 @@ } } +#ifdef _LP64 + // Push DecodeN down through phi. + // The rest of phi graph will transform by split EncodeP node though phis up. + if (UseCompressedOops && can_reshape && progress == NULL) { + bool may_push = true; + bool has_decodeN = false; + Node* in_decodeN = NULL; + for (uint i=1; iis_DecodeN() && ii->bottom_type() == bottom_type()) { + has_decodeN = true; + in_decodeN = ii->in(1); + } else if (!ii->is_Phi()) { + may_push = false; + } + } + + if (has_decodeN && may_push) { + PhaseIterGVN *igvn = phase->is_IterGVN(); + // Note: in_decodeN is used only to define the type of new phi here. + PhiNode *new_phi = PhiNode::make_blank(in(0), in_decodeN); + uint orig_cnt = req(); + for (uint i=1; iis_DecodeN()) { + assert(ii->bottom_type() == bottom_type(), "sanity"); + new_ii = ii->in(1); + } else { + assert(ii->is_Phi(), "sanity"); + if (ii->as_Phi() == this) { + new_ii = new_phi; + } else { + new_ii = new (phase->C, 2) EncodePNode(ii, in_decodeN->bottom_type()); + igvn->register_new_node_with_optimizer(new_ii); + } + } + new_phi->set_req(i, new_ii); + } + igvn->register_new_node_with_optimizer(new_phi, this); + progress = new (phase->C, 2) DecodeNNode(new_phi, bottom_type()); + } + } +#endif + return progress; // Return any progress } diff -r 194b8e3a2fc4 -r 36ccc817fca4 src/share/vm/opto/compile.cpp --- a/src/share/vm/opto/compile.cpp Wed Sep 17 12:59:52 2008 -0700 +++ b/src/share/vm/opto/compile.cpp Tue Sep 23 12:29:06 2008 -0700 @@ -2075,6 +2075,44 @@ } #ifdef _LP64 + case Op_CastPP: + if (n->in(1)->is_DecodeN() && UseImplicitNullCheckForNarrowOop) { + Compile* C = Compile::current(); + Node* in1 = n->in(1); + const Type* t = n->bottom_type(); + Node* new_in1 = in1->clone(); + new_in1->as_DecodeN()->set_type(t); + + if (!Matcher::clone_shift_expressions) { + // + // x86, ARM and friends can handle 2 adds in addressing mode + // and Matcher can fold a DecodeN node into address by using + // a narrow oop directly and do implicit NULL check in address: + // + // [R12 + narrow_oop_reg<<3 + offset] + // NullCheck narrow_oop_reg + // + // On other platforms (Sparc) we have to keep new DecodeN node and + // use it to do implicit NULL check in address: + // + // decode_not_null narrow_oop_reg, base_reg + // [base_reg + offset] + // NullCheck base_reg + // + // Pin the new DecodeN node to non-null path on these patforms (Sparc) + // to keep the information to which NULL check the new DecodeN node + // corresponds to use it as value in implicit_null_check(). + // + new_in1->set_req(0, n->in(0)); + } + + n->subsume_by(new_in1); + if (in1->outcnt() == 0) { + in1->disconnect_inputs(NULL); + } + } + break; + case Op_CmpP: // Do this transformation here to preserve CmpPNode::sub() and // other TypePtr related Ideal optimizations (for example, ptr nullness). @@ -2094,24 +2132,44 @@ } else if (in2->Opcode() == Op_ConP) { const Type* t = in2->bottom_type(); if (t == TypePtr::NULL_PTR && UseImplicitNullCheckForNarrowOop) { - if (Matcher::clone_shift_expressions) { - // x86, ARM and friends can handle 2 adds in addressing mode. - // Decode a narrow oop and do implicit NULL check in address - // [R12 + narrow_oop_reg<<3 + offset] - new_in2 = ConNode::make(C, TypeNarrowOop::NULL_PTR); - } else { - // Don't replace CmpP(o ,null) if 'o' is used in AddP - // to generate implicit NULL check on Sparc where - // narrow oops can't be used in address. - uint i = 0; - for (; i < in1->outcnt(); i++) { - if (in1->raw_out(i)->is_AddP()) - break; - } - if (i >= in1->outcnt()) { - new_in2 = ConNode::make(C, TypeNarrowOop::NULL_PTR); - } - } + new_in2 = ConNode::make(C, TypeNarrowOop::NULL_PTR); + // + // This transformation together with CastPP transformation above + // will generated code for implicit NULL checks for compressed oops. + // + // The original code after Optimize() + // + // LoadN memory, narrow_oop_reg + // decode narrow_oop_reg, base_reg + // CmpP base_reg, NULL + // CastPP base_reg // NotNull + // Load [base_reg + offset], val_reg + // + // after these transformations will be + // + // LoadN memory, narrow_oop_reg + // CmpN narrow_oop_reg, NULL + // decode_not_null narrow_oop_reg, base_reg + // Load [base_reg + offset], val_reg + // + // and the uncommon path (== NULL) will use narrow_oop_reg directly + // since narrow oops can be used in debug info now (see the code in + // final_graph_reshaping_walk()). + // + // At the end the code will be matched to + // on x86: + // + // Load_narrow_oop memory, narrow_oop_reg + // Load [R12 + narrow_oop_reg<<3 + offset], val_reg + // NullCheck narrow_oop_reg + // + // and on sparc: + // + // Load_narrow_oop memory, narrow_oop_reg + // decode_not_null narrow_oop_reg, base_reg + // Load [base_reg + offset], val_reg + // NullCheck base_reg + // } else if (t->isa_oopptr()) { new_in2 = ConNode::make(C, t->make_narrowoop()); } @@ -2128,6 +2186,49 @@ } } break; + + case Op_DecodeN: + assert(!n->in(1)->is_EncodeP(), "should be optimized out"); + break; + + case Op_EncodeP: { + Node* in1 = n->in(1); + if (in1->is_DecodeN()) { + n->subsume_by(in1->in(1)); + } else if (in1->Opcode() == Op_ConP) { + Compile* C = Compile::current(); + const Type* t = in1->bottom_type(); + if (t == TypePtr::NULL_PTR) { + n->subsume_by(ConNode::make(C, TypeNarrowOop::NULL_PTR)); + } else if (t->isa_oopptr()) { + n->subsume_by(ConNode::make(C, t->make_narrowoop())); + } + } + if (in1->outcnt() == 0) { + in1->disconnect_inputs(NULL); + } + break; + } + + case Op_Phi: + if (n->as_Phi()->bottom_type()->isa_narrowoop()) { + // The EncodeP optimization may create Phi with the same edges + // for all paths. It is not handled well by Register Allocator. + Node* unique_in = n->in(1); + assert(unique_in != NULL, ""); + uint cnt = n->req(); + for (uint i = 2; i < cnt; i++) { + Node* m = n->in(i); + assert(m != NULL, ""); + if (unique_in != m) + unique_in = NULL; + } + if (unique_in != NULL) { + n->subsume_by(unique_in); + } + } + break; + #endif case Op_ModI: diff -r 194b8e3a2fc4 -r 36ccc817fca4 src/share/vm/opto/connode.cpp --- a/src/share/vm/opto/connode.cpp Wed Sep 17 12:59:52 2008 -0700 +++ b/src/share/vm/opto/connode.cpp Tue Sep 23 12:29:06 2008 -0700 @@ -433,8 +433,8 @@ // If not converting int->oop, throw away cast after constant propagation Node *CastPPNode::Ideal_DU_postCCP( PhaseCCP *ccp ) { const Type *t = ccp->type(in(1)); - if (!t->isa_oop_ptr()) { - return NULL; // do not transform raw pointers + if (!t->isa_oop_ptr() || in(1)->is_DecodeN()) { + return NULL; // do not transform raw pointers or narrow oops } return ConstraintCastNode::Ideal_DU_postCCP(ccp); } diff -r 194b8e3a2fc4 -r 36ccc817fca4 src/share/vm/opto/matcher.cpp --- a/src/share/vm/opto/matcher.cpp Wed Sep 17 12:59:52 2008 -0700 +++ b/src/share/vm/opto/matcher.cpp Tue Sep 23 12:29:06 2008 -0700 @@ -840,7 +840,7 @@ _new2old_map.map(m->_idx, n); #endif if (m->in(0) != NULL) // m might be top - collect_null_checks(m); + collect_null_checks(m, n); } else { // Else just a regular 'ol guy m = n->clone(); // So just clone into new-space #ifdef ASSERT @@ -1478,12 +1478,19 @@ m = _mem_node; assert(m != NULL && m->is_Mem(), "expecting memory node"); } - if (m->adr_type() != mach->adr_type()) { + const Type* mach_at = mach->adr_type(); + // DecodeN node consumed by an address may have different type + // then its input. Don't compare types for such case. + if (m->adr_type() != mach_at && m->in(MemNode::Address)->is_AddP() && + m->in(MemNode::Address)->in(AddPNode::Address)->is_DecodeN()) { + mach_at = m->adr_type(); + } + if (m->adr_type() != mach_at) { m->dump(); tty->print_cr("mach:"); mach->dump(1); } - assert(m->adr_type() == mach->adr_type(), "matcher should not change adr type"); + assert(m->adr_type() == mach_at, "matcher should not change adr type"); } #endif } @@ -1995,7 +2002,7 @@ // it. Used by later implicit-null-check handling. Actually collects // either an IfTrue or IfFalse for the common NOT-null path, AND the ideal // value being tested. -void Matcher::collect_null_checks( Node *proj ) { +void Matcher::collect_null_checks( Node *proj, Node *orig_proj ) { Node *iff = proj->in(0); if( iff->Opcode() == Op_If ) { // During matching If's have Bool & Cmp side-by-side @@ -2008,20 +2015,47 @@ if (ct == TypePtr::NULL_PTR || (opc == Op_CmpN && ct == TypeNarrowOop::NULL_PTR)) { + bool push_it = false; if( proj->Opcode() == Op_IfTrue ) { extern int all_null_checks_found; all_null_checks_found++; if( b->_test._test == BoolTest::ne ) { - _null_check_tests.push(proj); - _null_check_tests.push(cmp->in(1)); + push_it = true; } } else { assert( proj->Opcode() == Op_IfFalse, "" ); if( b->_test._test == BoolTest::eq ) { - _null_check_tests.push(proj); - _null_check_tests.push(cmp->in(1)); + push_it = true; } } + if( push_it ) { + _null_check_tests.push(proj); + Node* val = cmp->in(1); +#ifdef _LP64 + if (UseCompressedOops && !Matcher::clone_shift_expressions && + val->bottom_type()->isa_narrowoop()) { + // + // Look for DecodeN node which should be pinned to orig_proj. + // On platforms (Sparc) which can not handle 2 adds + // in addressing mode we have to keep a DecodeN node and + // use it to do implicit NULL check in address. + // + // DecodeN node was pinned to non-null path (orig_proj) during + // CastPP transformation in final_graph_reshaping_impl(). + // + uint cnt = orig_proj->outcnt(); + for (uint i = 0; i < orig_proj->outcnt(); i++) { + Node* d = orig_proj->raw_out(i); + if (d->is_DecodeN() && d->in(1) == val) { + val = d; + val->set_req(0, NULL); // Unpin now. + break; + } + } + } +#endif + _null_check_tests.push(val); + } } } } diff -r 194b8e3a2fc4 -r 36ccc817fca4 src/share/vm/opto/matcher.hpp --- a/src/share/vm/opto/matcher.hpp Wed Sep 17 12:59:52 2008 -0700 +++ b/src/share/vm/opto/matcher.hpp Tue Sep 23 12:29:06 2008 -0700 @@ -166,7 +166,7 @@ // List of IfFalse or IfTrue Nodes that indicate a taken null test. // List is valid in the post-matching space. Node_List _null_check_tests; - void collect_null_checks( Node *proj ); + void collect_null_checks( Node *proj, Node *orig_proj ); void validate_null_checks( ); Matcher( Node_List &proj_list );