hotspot/src/share/vm/opto/macro.cpp

Print this page
rev 611 : Merge

*** 1,10 **** #ifdef USE_PRAGMA_IDENT_SRC #pragma ident "@(#)macro.cpp 1.33 07/10/04 14:36:00 JVM" #endif /* ! * Copyright 2005-2007 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. --- 1,10 ---- #ifdef USE_PRAGMA_IDENT_SRC #pragma ident "@(#)macro.cpp 1.33 07/10/04 14:36:00 JVM" #endif /* ! * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.
*** 55,87 **** // Copy debug information and adjust JVMState information uint old_dbg_start = oldcall->tf()->domain()->cnt(); 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"); for (uint i = old_dbg_start; i < oldcall->req(); i++) { ! newcall->add_req(oldcall->in(i)); } 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_endoff(jvms->endoff()+jvms_adj); } } ! Node* PhaseMacroExpand::opt_iff(Node* region, Node* iff) { ! IfNode *opt_iff = transform_later(iff)->as_If(); ! // Fast path taken; set region slot 2 ! Node *fast_taken = transform_later( new (C, 1) IfFalseNode(opt_iff) ); ! region->init_req(2,fast_taken); // Capture fast-control // Fast path not-taken, i.e. slow path ! Node *slow_taken = transform_later( new (C, 1) IfTrueNode(opt_iff) ); return slow_taken; } //--------------------copy_predefined_input_for_runtime_call-------------------- void PhaseMacroExpand::copy_predefined_input_for_runtime_call(Node * ctrl, CallNode* oldcall, CallNode* call) { // Set fixed predefined input arguments --- 55,117 ---- // Copy debug information and adjust JVMState information uint old_dbg_start = oldcall->tf()->domain()->cnt(); 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++) { ! Node* old_in = oldcall->in(i); ! // Clone old SafePointScalarObjectNodes, adjusting their field contents. ! if (old_in != NULL && 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); } } ! Node* PhaseMacroExpand::opt_bits_test(Node* ctrl, Node* region, int edge, Node* word, int mask, int bits, bool return_fast_path) { ! Node* cmp; ! if (mask != 0) { ! Node* and_node = transform_later(new (C, 3) AndXNode(word, MakeConX(mask))); ! cmp = transform_later(new (C, 3) CmpXNode(and_node, MakeConX(bits))); ! } else { ! cmp = word; ! } ! Node* bol = transform_later(new (C, 2) BoolNode(cmp, BoolTest::ne)); ! IfNode* iff = new (C, 2) IfNode( ctrl, bol, PROB_MIN, COUNT_UNKNOWN ); ! transform_later(iff); ! // Fast path taken. ! Node *fast_taken = transform_later( new (C, 1) IfFalseNode(iff) ); // Fast path not-taken, i.e. slow path ! Node *slow_taken = transform_later( new (C, 1) IfTrueNode(iff) ); ! ! if (return_fast_path) { ! region->init_req(edge, slow_taken); // Capture slow-control ! return fast_taken; ! } else { ! region->init_req(edge, fast_taken); // Capture fast-control return slow_taken; + } } //--------------------copy_predefined_input_for_runtime_call-------------------- void PhaseMacroExpand::copy_predefined_input_for_runtime_call(Node * ctrl, CallNode* oldcall, CallNode* call) { // Set fixed predefined input arguments
*** 167,176 **** --- 197,858 ---- } } } + // 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, PhaseGVN *phase) { + Node *orig_mem = mem; + Node *alloc_mem = alloc->in(TypeFunc::Memory); + const TypeOopPtr *tinst = phase->C->get_adr_type(alias_idx)->isa_oopptr(); + 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()) { + CallNode *call = in->as_Call(); + if (!call->may_modify(tinst, phase)) { + mem = call->in(TypeFunc::Memory); + } + mem = in->in(TypeFunc::Memory); + } else if (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; + } + assert(mem != orig_mem, "dead memory loop"); + } + } + + // + // 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, Node_Stack *value_phis, int level) { + assert(mem->is_Phi(), "sanity"); + int alias_idx = C->get_alias_index(adr_t); + int offset = adr_t->offset(); + int instance_id = adr_t->instance_id(); + + // Check if an appropriate value phi already exists. + Node* region = mem->in(0); + for (DUIterator_Fast kmax, k = region->fast_outs(kmax); k < kmax; k++) { + Node* phi = region->fast_out(k); + if (phi->is_Phi() && phi != mem && + phi->as_Phi()->is_same_inst_field(phi_type, instance_id, alias_idx, offset)) { + return phi; + } + } + // Check if an appropriate new value phi already exists. + Node* new_phi = NULL; + uint size = value_phis->size(); + for (uint i=0; i < size; i++) { + if ( mem->_idx == value_phis->index_at(i) ) { + return value_phis->node_at(i); + } + } + + if (level <= 0) { + return NULL; // Give up: phi tree too deep + } + 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); + + // create a new Phi for the value + PhiNode *phi = new (C, length) PhiNode(mem->in(0), phi_type, NULL, instance_id, alias_idx, offset); + transform_later(phi); + value_phis->push(phi, mem->_idx); + + 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, &_igvn); + 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()) { + val = value_from_mem_phi(val, ft, phi_type, adr_t, alloc, value_phis, level-1); + if (val == NULL) { + return NULL; + } + values.at_put(j, val); + } else { + assert(false, "unknown node on this path"); + return NULL; // unknown node on this path + } + } + } + // Set Phi's inputs + 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)); + } + } + 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_known_instance_field(), "instance required"); + int instance_id = adr_t->instance_id(); + assert((uint)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); + Arena *a = Thread::current()->resource_area(); + VectorSet visited(a); + + + 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, &_igvn); + 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_known_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, &_igvn); + 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_Stack value_phis(a, 8); + Node * phi = value_from_mem_phi(mem, ft, ftype, adr_t, alloc, &value_phis, ValueSearchLimit); + if (phi != NULL) { + return phi; + } else { + // Kill all new Phis + while(value_phis.is_nonempty()) { + Node* n = value_phis.node(); + _igvn.hash_delete(n); + _igvn.subsume_node(n, C->top()); + value_phis.pop(); + } + } + } + } + // 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() || n->is_LoadStore()) { + 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->is_Call() && sfpt->as_Call()->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++) { + intptr_t 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 * (intptr_t)element_size; + } + + const Type *field_type; + // The next code is taken from Parse::do_get_xxx(). + if (basic_elem_type == T_OBJECT || basic_elem_type == T_ARRAY) { + 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()); + } + if (UseCompressedOops) { + field_type = field_type->make_narrowoop(); + basic_elem_type = T_NARROWOOP; + } + } 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; + } + if (UseCompressedOops && field_type->isa_narrowoop()) { + // Enable "DecodeN(EncodeP(Allocate)) --> Allocate" transformation + // to be able scalar replace the allocation. + if (field_val->is_EncodeP()) { + field_val = field_val->in(1); + } else { + field_val = transform_later(new (C, 2) DecodeNNode(field_val, field_val->bottom_type()->make_ptr())); + } + } + 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) { if (UseTLAB) { // Private allocation: load from TLS Node* thread = transform_later(new (C, 1) ThreadLocalNode());
*** 188,207 **** } Node* PhaseMacroExpand::make_load(Node* ctl, Node* mem, Node* base, int offset, const Type* value_type, BasicType bt) { Node* adr = basic_plus_adr(base, offset); ! const TypePtr* adr_type = TypeRawPtr::BOTTOM; ! Node* value = LoadNode::make(C, ctl, mem, adr, adr_type, value_type, bt); transform_later(value); return value; } Node* PhaseMacroExpand::make_store(Node* ctl, Node* mem, Node* base, int offset, Node* value, BasicType bt) { Node* adr = basic_plus_adr(base, offset); ! mem = StoreNode::make(C, ctl, mem, adr, NULL, value, bt); transform_later(mem); return mem; } //============================================================================= --- 870,889 ---- } Node* PhaseMacroExpand::make_load(Node* ctl, Node* mem, Node* base, int offset, const Type* value_type, BasicType bt) { Node* adr = basic_plus_adr(base, offset); ! const TypePtr* adr_type = adr->bottom_type()->is_ptr(); ! Node* value = LoadNode::make(_igvn, ctl, mem, adr, adr_type, value_type, bt); transform_later(value); return value; } Node* PhaseMacroExpand::make_store(Node* ctl, Node* mem, Node* base, int offset, Node* value, BasicType bt) { Node* adr = basic_plus_adr(base, offset); ! mem = StoreNode::make(_igvn, ctl, mem, adr, NULL, value, bt); transform_later(mem); return mem; } //=============================================================================
*** 271,299 **** Node* i_o = alloc->in(TypeFunc::I_O); Node* size_in_bytes = alloc->in(AllocateNode::AllocSize); Node* klass_node = alloc->in(AllocateNode::KlassNode); Node* initial_slow_test = alloc->in(AllocateNode::InitialTest); ! Node* eden_top_adr; ! Node* eden_end_adr; ! set_eden_pointers(eden_top_adr, eden_end_adr); - uint raw_idx = C->get_alias_index(TypeRawPtr::BOTTOM); assert(ctrl != NULL, "must have control"); - - // Load Eden::end. Loop invariant and hoisted. - // - // Note: We set the control input on "eden_end" and "old_eden_top" when using - // a TLAB to work around a bug where these values were being moved across - // a safepoint. These are not oops, so they cannot be include in the oop - // map, but the can be changed by a GC. The proper way to fix this would - // be to set the raw memory state when generating a SafepointNode. However - // this will require extensive changes to the loop optimization in order to - // prevent a degradation of the optimization. - // See comment in memnode.hpp, around line 227 in class LoadPNode. - Node* eden_end = make_load(ctrl, mem, eden_end_adr, 0, TypeRawPtr::BOTTOM, T_ADDRESS); - // We need a Region and corresponding Phi's to merge the slow-path and fast-path results. // they will not be used if "always_slow" is set enum { slow_result_path = 1, fast_result_path = 2 }; Node *result_region; Node *result_phi_rawmem; --- 953,970 ---- Node* i_o = alloc->in(TypeFunc::I_O); Node* size_in_bytes = alloc->in(AllocateNode::AllocSize); 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); ! } assert(ctrl != NULL, "must have control"); // We need a Region and corresponding Phi's to merge the slow-path and fast-path results. // they will not be used if "always_slow" is set enum { slow_result_path = 1, fast_result_path = 2 }; Node *result_region; Node *result_phi_rawmem;
*** 309,324 **** initial_slow_test = NULL; } else { initial_slow_test = BoolNode::make_predicate(initial_slow_test, &_igvn); } ! if (DTraceAllocProbes) { // Force slow-path allocation always_slow = true; initial_slow_test = NULL; } enum { too_big_or_final_path = 1, need_gc_path = 2 }; Node *slow_region = NULL; Node *toobig_false = ctrl; assert (initial_slow_test == NULL || !always_slow, "arguments must be consistent"); --- 980,998 ---- initial_slow_test = NULL; } else { initial_slow_test = BoolNode::make_predicate(initial_slow_test, &_igvn); } ! if (DTraceAllocProbes || ! !UseTLAB && (!Universe::heap()->supports_inline_contig_alloc() || ! (UseConcMarkSweepGC && CMSIncrementalMode))) { // Force slow-path allocation always_slow = true; initial_slow_test = NULL; } + enum { too_big_or_final_path = 1, need_gc_path = 2 }; Node *slow_region = NULL; Node *toobig_false = ctrl; assert (initial_slow_test == NULL || !always_slow, "arguments must be consistent");
*** 343,352 **** --- 1017,1043 ---- } Node *slow_mem = mem; // save the current memory state for slow path // generate the fast allocation code unless we know that the initial test will always go slow if (!always_slow) { + Node* eden_top_adr; + Node* eden_end_adr; + + set_eden_pointers(eden_top_adr, eden_end_adr); + + // Load Eden::end. Loop invariant and hoisted. + // + // Note: We set the control input on "eden_end" and "old_eden_top" when using + // a TLAB to work around a bug where these values were being moved across + // a safepoint. These are not oops, so they cannot be include in the oop + // map, but the can be changed by a GC. The proper way to fix this would + // be to set the raw memory state when generating a SafepointNode. However + // this will require extensive changes to the loop optimization in order to + // prevent a degradation of the optimization. + // See comment in memnode.hpp, around line 227 in class LoadPNode. + Node *eden_end = make_load(ctrl, mem, eden_end_adr, 0, TypeRawPtr::BOTTOM, T_ADDRESS); + // allocate the Region and Phi nodes for the result result_region = new (C, 3) RegionNode(3); result_phi_rawmem = new (C, 3) PhiNode( result_region, Type::MEMORY, TypeRawPtr::BOTTOM ); result_phi_rawoop = new (C, 3) PhiNode( result_region, TypeRawPtr::BOTTOM ); result_phi_i_o = new (C, 3) PhiNode( result_region, Type::ABIO ); // I/O is used for Prefetch
*** 633,650 **** mark_node = make_load(NULL, rawmem, klass_node, Klass::prototype_header_offset_in_bytes() + sizeof(oopDesc), TypeRawPtr::BOTTOM, T_ADDRESS); } else { mark_node = makecon(TypeRawPtr::make((address)markOopDesc::prototype())); } rawmem = make_store(control, rawmem, object, oopDesc::mark_offset_in_bytes(), mark_node, T_ADDRESS); rawmem = make_store(control, rawmem, object, oopDesc::klass_offset_in_bytes(), klass_node, T_OBJECT); int header_size = alloc->minimum_header_size(); // conservatively small // Array length if (length != NULL) { // Arrays need length field rawmem = make_store(control, rawmem, object, arrayOopDesc::length_offset_in_bytes(), length, T_INT); // conservatively small header size: ! header_size = sizeof(arrayOopDesc); ciKlass* k = _igvn.type(klass_node)->is_klassptr()->klass(); if (k->is_array_klass()) // we know the exact header size in most cases: header_size = Klass::layout_helper_header_size(k->layout_helper()); } --- 1324,1342 ---- mark_node = make_load(NULL, rawmem, klass_node, Klass::prototype_header_offset_in_bytes() + sizeof(oopDesc), TypeRawPtr::BOTTOM, T_ADDRESS); } else { mark_node = makecon(TypeRawPtr::make((address)markOopDesc::prototype())); } rawmem = make_store(control, rawmem, object, oopDesc::mark_offset_in_bytes(), mark_node, T_ADDRESS); + rawmem = make_store(control, rawmem, object, oopDesc::klass_offset_in_bytes(), klass_node, T_OBJECT); int header_size = alloc->minimum_header_size(); // conservatively small // Array length if (length != NULL) { // Arrays need length field rawmem = make_store(control, rawmem, object, arrayOopDesc::length_offset_in_bytes(), length, T_INT); // conservatively small header size: ! header_size = arrayOopDesc::base_offset_in_bytes(T_BYTE); ciKlass* k = _igvn.type(klass_node)->is_klassptr()->klass(); if (k->is_array_klass()) // we know the exact header size in most cases: header_size = Klass::layout_helper_header_size(k->layout_helper()); }
*** 669,679 **** // We can also try to do some peephole optimizations, // such as combining some adjacent subword stores. rawmem = init->complete_stores(control, rawmem, object, header_size, size_in_bytes, &_igvn); } - // We have no more use for this link, since the AllocateNode goes away: init->set_req(InitializeNode::RawAddress, top()); // (If we keep the link, it just confuses the register allocator, // who thinks he sees a real use of the address by the membar.) } --- 1361,1370 ----
*** 814,868 **** // eliminate the node without expanding it. // // 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); // 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); } ! 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; } //------------------------------expand_lock_node---------------------- void PhaseMacroExpand::expand_lock_node(LockNode *lock) { Node* ctrl = lock->in(TypeFunc::Control); 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; } ! // Make the merge point ! Node *region = new (C, 3) RegionNode(3); - Node *bol = transform_later(new (C, 2) BoolNode(flock,BoolTest::ne)); - Node *iff = new (C, 2) IfNode( ctrl, bol, PROB_MIN, COUNT_UNKNOWN ); // Optimize test; set region slot 2 ! Node *slow_path = opt_iff(region,iff); // Make slow path call CallNode *call = make_slow_call( (CallNode *) lock, OptoRuntime::complete_monitor_enter_Type(), OptoRuntime::complete_monitor_locking_Java(), NULL, slow_path, obj, box ); extract_call_projections(call); --- 1505,1853 ---- // eliminate the node without expanding it. // // Note: The membar's associated with the lock/unlock are currently not // eliminated. This should be investigated as a future enhancement. // ! bool PhaseMacroExpand::eliminate_locking_node(AbstractLockNode *alock) { ! ! if (!alock->is_eliminated()) { ! return false; ! } ! if (alock->is_Lock() && !alock->is_coarsened()) { ! // Create new "eliminated" BoxLock node and use it ! // in monitor debug info for the same object. ! BoxLockNode* oldbox = alock->box_node()->as_BoxLock(); ! Node* obj = alock->obj_node(); ! if (!oldbox->is_eliminated()) { ! BoxLockNode* newbox = oldbox->clone()->as_BoxLock(); ! newbox->set_eliminated(); ! transform_later(newbox); ! // Replace old box node with new box for all users ! // of the same object. ! for (uint i = 0; i < oldbox->outcnt();) { ! ! bool next_edge = true; ! Node* u = oldbox->raw_out(i); ! if (u == alock) { ! i++; ! continue; // It will be removed below ! } ! if (u->is_Lock() && ! u->as_Lock()->obj_node() == obj && ! // oldbox could be referenced in debug info also ! u->as_Lock()->box_node() == oldbox) { ! assert(u->as_Lock()->is_eliminated(), "sanity"); ! _igvn.hash_delete(u); ! u->set_req(TypeFunc::Parms + 1, newbox); ! next_edge = false; ! #ifdef ASSERT ! } else if (u->is_Unlock() && u->as_Unlock()->obj_node() == obj) { ! assert(u->as_Unlock()->is_eliminated(), "sanity"); ! #endif ! } ! // Replace old box in monitor debug info. ! if (u->is_SafePoint() && u->as_SafePoint()->jvms()) { ! SafePointNode* sfn = u->as_SafePoint(); ! JVMState* youngest_jvms = sfn->jvms(); ! int max_depth = youngest_jvms->depth(); ! for (int depth = 1; depth <= max_depth; depth++) { ! JVMState* jvms = youngest_jvms->of_depth(depth); ! int num_mon = jvms->nof_monitors(); ! // Loop over monitors ! for (int idx = 0; idx < num_mon; idx++) { ! Node* obj_node = sfn->monitor_obj(jvms, idx); ! Node* box_node = sfn->monitor_box(jvms, idx); ! if (box_node == oldbox && obj_node == obj) { ! int j = jvms->monitor_box_offset(idx); ! _igvn.hash_delete(u); ! u->set_req(j, newbox); ! next_edge = false; ! } ! } // for (int idx = 0; ! } // for (int depth = 1; ! } // if (u->is_SafePoint() ! if (next_edge) i++; ! } // for (uint i = 0; i < oldbox->outcnt();) ! } // if (!oldbox->is_eliminated()) ! } // if (alock->is_Lock() && !lock->is_coarsened()) ! ! #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()) { ! // 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); ! ! // Delete FastLock node also if this Lock node is unique user ! // (a loop peeling may clone a Lock node). ! Node* flock = alock->as_Lock()->fastlock_node(); ! if (flock->outcnt() == 1) { ! assert(flock->unique_out() == alock, "sanity"); ! _igvn.hash_delete(flock); ! _igvn.subsume_node(flock, top()); ! } } ! // 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; } //------------------------------expand_lock_node---------------------- void PhaseMacroExpand::expand_lock_node(LockNode *lock) { Node* ctrl = lock->in(TypeFunc::Control); Node* mem = lock->in(TypeFunc::Memory); Node* obj = lock->obj_node(); Node* box = lock->box_node(); ! Node* flock = lock->fastlock_node(); ! // Make the merge point ! Node *region; ! Node *mem_phi; ! Node *slow_path; ! ! if (UseOptoBiasInlining) { ! /* ! * See the full descrition in MacroAssembler::biased_locking_enter(). ! * ! * if( (mark_word & biased_lock_mask) == biased_lock_pattern ) { ! * // The object is biased. ! * proto_node = klass->prototype_header; ! * o_node = thread | proto_node; ! * x_node = o_node ^ mark_word; ! * if( (x_node & ~age_mask) == 0 ) { // Biased to the current thread ? ! * // Done. ! * } else { ! * if( (x_node & biased_lock_mask) != 0 ) { ! * // The klass's prototype header is no longer biased. ! * cas(&mark_word, mark_word, proto_node) ! * goto cas_lock; ! * } else { ! * // The klass's prototype header is still biased. ! * if( (x_node & epoch_mask) != 0 ) { // Expired epoch? ! * old = mark_word; ! * new = o_node; ! * } else { ! * // Different thread or anonymous biased. ! * old = mark_word & (epoch_mask | age_mask | biased_lock_mask); ! * new = thread | old; ! * } ! * // Try to rebias. ! * if( cas(&mark_word, old, new) == 0 ) { ! * // Done. ! * } else { ! * goto slow_path; // Failed. ! * } ! * } ! * } ! * } else { ! * // The object is not biased. ! * cas_lock: ! * if( FastLock(obj) == 0 ) { ! * // Done. ! * } else { ! * slow_path: ! * OptoRuntime::complete_monitor_locking_Java(obj); ! * } ! * } ! */ ! ! region = new (C, 5) RegionNode(5); ! // create a Phi for the memory state ! mem_phi = new (C, 5) PhiNode( region, Type::MEMORY, TypeRawPtr::BOTTOM); ! ! Node* fast_lock_region = new (C, 3) RegionNode(3); ! Node* fast_lock_mem_phi = new (C, 3) PhiNode( fast_lock_region, Type::MEMORY, TypeRawPtr::BOTTOM); ! ! // First, check mark word for the biased lock pattern. ! Node* mark_node = make_load(ctrl, mem, obj, oopDesc::mark_offset_in_bytes(), TypeX_X, TypeX_X->basic_type()); ! ! // Get fast path - mark word has the biased lock pattern. ! ctrl = opt_bits_test(ctrl, fast_lock_region, 1, mark_node, ! markOopDesc::biased_lock_mask_in_place, ! markOopDesc::biased_lock_pattern, true); ! // fast_lock_region->in(1) is set to slow path. ! fast_lock_mem_phi->init_req(1, mem); ! ! // Now check that the lock is biased to the current thread and has ! // the same epoch and bias as Klass::_prototype_header. ! ! // Special-case a fresh allocation to avoid building nodes: ! Node* klass_node = AllocateNode::Ideal_klass(obj, &_igvn); ! if (klass_node == NULL) { ! Node* k_adr = basic_plus_adr(obj, oopDesc::klass_offset_in_bytes()); ! klass_node = transform_later( LoadKlassNode::make(_igvn, mem, k_adr, _igvn.type(k_adr)->is_ptr()) ); ! #ifdef _LP64 ! if (UseCompressedOops && klass_node->is_DecodeN()) { ! assert(klass_node->in(1)->Opcode() == Op_LoadNKlass, "sanity"); ! klass_node->in(1)->init_req(0, ctrl); ! } else ! #endif ! klass_node->init_req(0, ctrl); } + Node *proto_node = make_load(ctrl, mem, klass_node, Klass::prototype_header_offset_in_bytes() + sizeof(oopDesc), TypeX_X, TypeX_X->basic_type()); ! Node* thread = transform_later(new (C, 1) ThreadLocalNode()); ! Node* cast_thread = transform_later(new (C, 2) CastP2XNode(ctrl, thread)); ! Node* o_node = transform_later(new (C, 3) OrXNode(cast_thread, proto_node)); ! Node* x_node = transform_later(new (C, 3) XorXNode(o_node, mark_node)); ! ! // Get slow path - mark word does NOT match the value. ! Node* not_biased_ctrl = opt_bits_test(ctrl, region, 3, x_node, ! (~markOopDesc::age_mask_in_place), 0); ! // region->in(3) is set to fast path - the object is biased to the current thread. ! mem_phi->init_req(3, mem); ! ! ! // Mark word does NOT match the value (thread | Klass::_prototype_header). ! ! ! // First, check biased pattern. ! // Get fast path - _prototype_header has the same biased lock pattern. ! ctrl = opt_bits_test(not_biased_ctrl, fast_lock_region, 2, x_node, ! markOopDesc::biased_lock_mask_in_place, 0, true); ! ! not_biased_ctrl = fast_lock_region->in(2); // Slow path ! // fast_lock_region->in(2) - the prototype header is no longer biased ! // and we have to revoke the bias on this object. ! // We are going to try to reset the mark of this object to the prototype ! // value and fall through to the CAS-based locking scheme. ! Node* adr = basic_plus_adr(obj, oopDesc::mark_offset_in_bytes()); ! Node* cas = new (C, 5) StoreXConditionalNode(not_biased_ctrl, mem, adr, ! proto_node, mark_node); ! transform_later(cas); ! Node* proj = transform_later( new (C, 1) SCMemProjNode(cas)); ! fast_lock_mem_phi->init_req(2, proj); ! ! ! // Second, check epoch bits. ! Node* rebiased_region = new (C, 3) RegionNode(3); ! Node* old_phi = new (C, 3) PhiNode( rebiased_region, TypeX_X); ! Node* new_phi = new (C, 3) PhiNode( rebiased_region, TypeX_X); ! ! // Get slow path - mark word does NOT match epoch bits. ! Node* epoch_ctrl = opt_bits_test(ctrl, rebiased_region, 1, x_node, ! markOopDesc::epoch_mask_in_place, 0); ! // The epoch of the current bias is not valid, attempt to rebias the object ! // toward the current thread. ! rebiased_region->init_req(2, epoch_ctrl); ! old_phi->init_req(2, mark_node); ! new_phi->init_req(2, o_node); ! ! // rebiased_region->in(1) is set to fast path. ! // The epoch of the current bias is still valid but we know ! // nothing about the owner; it might be set or it might be clear. ! Node* cmask = MakeConX(markOopDesc::biased_lock_mask_in_place | ! markOopDesc::age_mask_in_place | ! markOopDesc::epoch_mask_in_place); ! Node* old = transform_later(new (C, 3) AndXNode(mark_node, cmask)); ! cast_thread = transform_later(new (C, 2) CastP2XNode(ctrl, thread)); ! Node* new_mark = transform_later(new (C, 3) OrXNode(cast_thread, old)); ! old_phi->init_req(1, old); ! new_phi->init_req(1, new_mark); ! ! transform_later(rebiased_region); ! transform_later(old_phi); ! transform_later(new_phi); ! ! // Try to acquire the bias of the object using an atomic operation. ! // If this fails we will go in to the runtime to revoke the object's bias. ! cas = new (C, 5) StoreXConditionalNode(rebiased_region, mem, adr, ! new_phi, old_phi); ! transform_later(cas); ! proj = transform_later( new (C, 1) SCMemProjNode(cas)); ! ! // Get slow path - Failed to CAS. ! not_biased_ctrl = opt_bits_test(rebiased_region, region, 4, cas, 0, 0); ! mem_phi->init_req(4, proj); ! // region->in(4) is set to fast path - the object is rebiased to the current thread. ! ! // Failed to CAS. ! slow_path = new (C, 3) RegionNode(3); ! Node *slow_mem = new (C, 3) PhiNode( slow_path, Type::MEMORY, TypeRawPtr::BOTTOM); ! ! slow_path->init_req(1, not_biased_ctrl); // Capture slow-control ! slow_mem->init_req(1, proj); ! ! // Call CAS-based locking scheme (FastLock node). ! ! transform_later(fast_lock_region); ! transform_later(fast_lock_mem_phi); ! ! // Get slow path - FastLock failed to lock the object. ! ctrl = opt_bits_test(fast_lock_region, region, 2, flock, 0, 0); ! mem_phi->init_req(2, fast_lock_mem_phi); ! // region->in(2) is set to fast path - the object is locked to the current thread. ! ! slow_path->init_req(2, ctrl); // Capture slow-control ! slow_mem->init_req(2, fast_lock_mem_phi); ! ! transform_later(slow_path); ! transform_later(slow_mem); ! // Reset lock's memory edge. ! lock->set_req(TypeFunc::Memory, slow_mem); ! ! } else { ! region = new (C, 3) RegionNode(3); ! // create a Phi for the memory state ! mem_phi = new (C, 3) PhiNode( region, Type::MEMORY, TypeRawPtr::BOTTOM); // Optimize test; set region slot 2 ! slow_path = opt_bits_test(ctrl, region, 2, flock, 0, 0); ! mem_phi->init_req(2, mem); ! } // Make slow path call CallNode *call = make_slow_call( (CallNode *) lock, OptoRuntime::complete_monitor_enter_Type(), OptoRuntime::complete_monitor_locking_Java(), NULL, slow_path, obj, box ); extract_call_projections(call);
*** 884,930 **** region->init_req(1, slow_ctrl); // region inputs are now complete transform_later(region); _igvn.subsume_node(_fallthroughproj, region); ! // create a Phi for the memory state ! Node *mem_phi = new (C, 3) PhiNode( region, Type::MEMORY, TypeRawPtr::BOTTOM); ! Node *memproj = transform_later( new (C, 1) ProjNode(call, TypeFunc::Memory) ); mem_phi->init_req(1, memproj ); - mem_phi->init_req(2, mem); transform_later(mem_phi); _igvn.hash_delete(_memproj_fallthrough); _igvn.subsume_node(_memproj_fallthrough, mem_phi); - - } //------------------------------expand_unlock_node---------------------- void PhaseMacroExpand::expand_unlock_node(UnlockNode *unlock) { ! 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 ! RegionNode *region = new (C, 3) RegionNode(3); FastUnlockNode *funlock = new (C, 3) FastUnlockNode( ctrl, obj, box ); funlock = transform_later( funlock )->as_FastUnlock(); - Node *bol = transform_later(new (C, 2) BoolNode(funlock,BoolTest::ne)); - Node *iff = new (C, 2) IfNode( ctrl, bol, PROB_MIN, COUNT_UNKNOWN ); // Optimize test; set region slot 2 ! Node *slow_path = opt_iff(region,iff); CallNode *call = make_slow_call( (CallNode *) unlock, OptoRuntime::complete_monitor_exit_Type(), CAST_FROM_FN_PTR(address, SharedRuntime::complete_monitor_unlocking_C), "complete_monitor_unlocking_C", slow_path, obj, box ); extract_call_projections(call); --- 1869,1921 ---- region->init_req(1, slow_ctrl); // region inputs are now complete transform_later(region); _igvn.subsume_node(_fallthroughproj, region); ! Node *memproj = transform_later( new(C, 1) ProjNode(call, TypeFunc::Memory) ); mem_phi->init_req(1, memproj ); transform_later(mem_phi); _igvn.hash_delete(_memproj_fallthrough); _igvn.subsume_node(_memproj_fallthrough, mem_phi); } //------------------------------expand_unlock_node---------------------- void PhaseMacroExpand::expand_unlock_node(UnlockNode *unlock) { ! Node* ctrl = unlock->in(TypeFunc::Control); Node* mem = unlock->in(TypeFunc::Memory); Node* obj = unlock->obj_node(); Node* box = unlock->box_node(); // No need for a null check on unlock // Make the merge point ! Node *region; ! Node *mem_phi; ! ! if (UseOptoBiasInlining) { ! // Check for biased locking unlock case, which is a no-op. ! // See the full descrition in MacroAssembler::biased_locking_exit(). ! region = new (C, 4) RegionNode(4); ! // create a Phi for the memory state ! mem_phi = new (C, 4) PhiNode( region, Type::MEMORY, TypeRawPtr::BOTTOM); ! mem_phi->init_req(3, mem); ! ! Node* mark_node = make_load(ctrl, mem, obj, oopDesc::mark_offset_in_bytes(), TypeX_X, TypeX_X->basic_type()); ! ctrl = opt_bits_test(ctrl, region, 3, mark_node, ! markOopDesc::biased_lock_mask_in_place, ! markOopDesc::biased_lock_pattern); ! } else { ! region = new (C, 3) RegionNode(3); ! // create a Phi for the memory state ! mem_phi = new (C, 3) PhiNode( region, Type::MEMORY, TypeRawPtr::BOTTOM); ! } FastUnlockNode *funlock = new (C, 3) FastUnlockNode( ctrl, obj, box ); funlock = transform_later( funlock )->as_FastUnlock(); // Optimize test; set region slot 2 ! Node *slow_path = opt_bits_test(ctrl, region, 2, funlock, 0, 0); CallNode *call = make_slow_call( (CallNode *) unlock, OptoRuntime::complete_monitor_exit_Type(), CAST_FROM_FN_PTR(address, SharedRuntime::complete_monitor_unlocking_C), "complete_monitor_unlocking_C", slow_path, obj, box ); extract_call_projections(call);
*** 942,976 **** region->init_req(1, slow_ctrl); // region inputs are now complete transform_later(region); _igvn.subsume_node(_fallthroughproj, region); - // create a Phi for the memory state - Node *mem_phi = new (C, 3) PhiNode( region, Type::MEMORY, TypeRawPtr::BOTTOM); Node *memproj = transform_later( new(C, 1) ProjNode(call, TypeFunc::Memory) ); mem_phi->init_req(1, memproj ); mem_phi->init_req(2, mem); transform_later(mem_phi); _igvn.hash_delete(_memproj_fallthrough); _igvn.subsume_node(_memproj_fallthrough, mem_phi); - - } //------------------------------expand_macro_nodes---------------------- // Returns true if a failure occurred. 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 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); 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 C->remove_macro_node(n); continue; --- 1933,2010 ---- region->init_req(1, slow_ctrl); // region inputs are now complete transform_later(region); _igvn.subsume_node(_fallthroughproj, region); Node *memproj = transform_later( new(C, 1) ProjNode(call, TypeFunc::Memory) ); mem_phi->init_req(1, memproj ); mem_phi->init_req(2, mem); transform_later(mem_phi); _igvn.hash_delete(_memproj_fallthrough); _igvn.subsume_node(_memproj_fallthrough, mem_phi); } //------------------------------expand_macro_nodes---------------------- // Returns true if a failure occurred. bool PhaseMacroExpand::expand_macro_nodes() { if (C->macro_count() == 0) return false; ! // First, attempt to eliminate locks ! 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();); ! if (n->is_AbstractLock()) { ! success = eliminate_locking_node(n->as_AbstractLock()); ! } else if (n->Opcode() == Op_Opaque1 || n->Opcode() == Op_Opaque2) { ! _igvn.add_users_to_worklist(n); ! _igvn.hash_delete(n); ! _igvn.subsume_node(n, n->in(1)); ! success = true; ! } ! assert(success == (C->macro_count() < old_macro_count), "elimination reduces macro count"); ! progress = progress || success; ! } ! } ! // Next, attempt to eliminate allocations ! 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: ! assert(!n->as_AbstractLock()->is_eliminated(), "sanity"); ! 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) { ! 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 C->remove_macro_node(n); continue;
*** 989,999 **** --- 2023,2036 ---- expand_unlock_node(n->as_Unlock()); break; 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.set_delay_transform(false); _igvn.optimize(); return false; }