# HG changeset patch # User trims # Date 1278660935 25200 # Node ID 4a7b48382e77c02ba78b7144106f223378e69fa4 # Parent 0d39bf49436e13a13ebb4e1f03d12304c022445d# Parent 406168b53eb05e8cb3fd69154ed4a745a1ff32f8 Merge diff -r 0d39bf49436e -r 4a7b48382e77 hotspot/src/share/vm/opto/callnode.cpp --- a/hotspot/src/share/vm/opto/callnode.cpp Fri Jul 09 00:32:48 2010 -0700 +++ b/hotspot/src/share/vm/opto/callnode.cpp Fri Jul 09 00:35:35 2010 -0700 @@ -1524,7 +1524,7 @@ ConnectionGraph *cgr = phase->C->congraph(); PointsToNode::EscapeState es = PointsToNode::GlobalEscape; if (cgr != NULL) - es = cgr->escape_state(obj_node(), phase); + es = cgr->escape_state(obj_node()); if (es != PointsToNode::UnknownEscape && es != PointsToNode::GlobalEscape) { // Mark it eliminated to update any counters this->set_eliminated(); @@ -1627,7 +1627,7 @@ ConnectionGraph *cgr = phase->C->congraph(); PointsToNode::EscapeState es = PointsToNode::GlobalEscape; if (cgr != NULL) - es = cgr->escape_state(obj_node(), phase); + es = cgr->escape_state(obj_node()); if (es != PointsToNode::UnknownEscape && es != PointsToNode::GlobalEscape) { // Mark it eliminated to update any counters this->set_eliminated(); diff -r 0d39bf49436e -r 4a7b48382e77 hotspot/src/share/vm/opto/compile.cpp --- a/hotspot/src/share/vm/opto/compile.cpp Fri Jul 09 00:32:48 2010 -0700 +++ b/hotspot/src/share/vm/opto/compile.cpp Fri Jul 09 00:35:35 2010 -0700 @@ -637,34 +637,6 @@ if (failing()) return; NOT_PRODUCT( verify_graph_edges(); ) - // Perform escape analysis - if (_do_escape_analysis && ConnectionGraph::has_candidates(this)) { - TracePhase t2("escapeAnalysis", &_t_escapeAnalysis, true); - // Add ConP#NULL and ConN#NULL nodes before ConnectionGraph construction. - PhaseGVN* igvn = initial_gvn(); - Node* oop_null = igvn->zerocon(T_OBJECT); - Node* noop_null = igvn->zerocon(T_NARROWOOP); - - _congraph = new(comp_arena()) ConnectionGraph(this); - bool has_non_escaping_obj = _congraph->compute_escape(); - -#ifndef PRODUCT - if (PrintEscapeAnalysis) { - _congraph->dump(); - } -#endif - // Cleanup. - if (oop_null->outcnt() == 0) - igvn->hash_delete(oop_null); - if (noop_null->outcnt() == 0) - igvn->hash_delete(noop_null); - - if (!has_non_escaping_obj) { - _congraph = NULL; - } - - if (failing()) return; - } // Now optimize Optimize(); if (failing()) return; @@ -1601,6 +1573,20 @@ if (failing()) return; + // Perform escape analysis + if (_do_escape_analysis && ConnectionGraph::has_candidates(this)) { + TracePhase t2("escapeAnalysis", &_t_escapeAnalysis, true); + ConnectionGraph::do_analysis(this, &igvn); + + if (failing()) return; + + igvn.optimize(); + print_method("Iter GVN 3", 2); + + if (failing()) return; + + } + // Loop transforms on the ideal graph. Range Check Elimination, // peeling, unrolling, etc. diff -r 0d39bf49436e -r 4a7b48382e77 hotspot/src/share/vm/opto/compile.hpp --- a/hotspot/src/share/vm/opto/compile.hpp Fri Jul 09 00:32:48 2010 -0700 +++ b/hotspot/src/share/vm/opto/compile.hpp Fri Jul 09 00:35:35 2010 -0700 @@ -362,6 +362,7 @@ Node* macro_node(int idx) { return _macro_nodes->at(idx); } Node* predicate_opaque1_node(int idx) { return _predicate_opaqs->at(idx);} ConnectionGraph* congraph() { return _congraph;} + void set_congraph(ConnectionGraph* congraph) { _congraph = congraph;} void add_macro_node(Node * n) { //assert(n->is_macro(), "must be a macro node"); assert(!_macro_nodes->contains(n), " duplicate entry in expand list"); diff -r 0d39bf49436e -r 4a7b48382e77 hotspot/src/share/vm/opto/escape.cpp --- a/hotspot/src/share/vm/opto/escape.cpp Fri Jul 09 00:32:48 2010 -0700 +++ b/hotspot/src/share/vm/opto/escape.cpp Fri Jul 09 00:35:35 2010 -0700 @@ -81,18 +81,18 @@ } #endif -ConnectionGraph::ConnectionGraph(Compile * C) : +ConnectionGraph::ConnectionGraph(Compile * C, PhaseIterGVN *igvn) : _nodes(C->comp_arena(), C->unique(), C->unique(), PointsToNode()), _processed(C->comp_arena()), _collecting(true), _compile(C), + _igvn(igvn), _node_map(C->comp_arena()) { _phantom_object = C->top()->_idx, add_node(C->top(), PointsToNode::JavaObject, PointsToNode::GlobalEscape,true); // Add ConP(#NULL) and ConN(#NULL) nodes. - PhaseGVN* igvn = C->initial_gvn(); Node* oop_null = igvn->zerocon(T_OBJECT); _oop_null = oop_null->_idx; assert(_oop_null < C->unique(), "should be created already"); @@ -182,7 +182,7 @@ _processed.set(n->_idx); } -PointsToNode::EscapeState ConnectionGraph::escape_state(Node *n, PhaseTransform *phase) { +PointsToNode::EscapeState ConnectionGraph::escape_state(Node *n) { uint idx = n->_idx; PointsToNode::EscapeState es; @@ -207,22 +207,26 @@ if (n->uncast()->_idx >= nodes_size()) return PointsToNode::UnknownEscape; + PointsToNode::EscapeState orig_es = es; + // compute max escape state of anything this node could point to VectorSet ptset(Thread::current()->resource_area()); - PointsTo(ptset, n, phase); + PointsTo(ptset, n); for(VectorSetI i(&ptset); i.test() && es != PointsToNode::GlobalEscape; ++i) { uint pt = i.elem; PointsToNode::EscapeState pes = ptnode_adr(pt)->escape_state(); if (pes > es) es = pes; } - // cache the computed escape state - assert(es != PointsToNode::UnknownEscape, "should have computed an escape state"); - ptnode_adr(idx)->set_escape_state(es); + if (orig_es != es) { + // cache the computed escape state + assert(es != PointsToNode::UnknownEscape, "should have computed an escape state"); + ptnode_adr(idx)->set_escape_state(es); + } // orig_es could be PointsToNode::UnknownEscape return es; } -void ConnectionGraph::PointsTo(VectorSet &ptset, Node * n, PhaseTransform *phase) { +void ConnectionGraph::PointsTo(VectorSet &ptset, Node * n) { VectorSet visited(Thread::current()->resource_area()); GrowableArray worklist; @@ -990,7 +994,7 @@ GrowableArray memnode_worklist; GrowableArray orig_phis; - PhaseGVN *igvn = _compile->initial_gvn(); + PhaseGVN *igvn = _igvn; uint new_index_start = (uint) _compile->num_alias_types(); Arena* arena = Thread::current()->resource_area(); VectorSet visited(arena); @@ -1012,7 +1016,7 @@ CallNode *alloc = n->as_Call(); // copy escape information to call node PointsToNode* ptn = ptnode_adr(alloc->_idx); - PointsToNode::EscapeState es = escape_state(alloc, igvn); + PointsToNode::EscapeState es = escape_state(alloc); // We have an allocation or call which returns a Java object, // see if it is unescaped. if (es != PointsToNode::NoEscape || !ptn->_scalar_replaceable) @@ -1123,7 +1127,7 @@ } } else if (n->is_AddP()) { ptset.Clear(); - PointsTo(ptset, get_addp_base(n), igvn); + PointsTo(ptset, get_addp_base(n)); assert(ptset.Size() == 1, "AddP address is unique"); uint elem = ptset.getelem(); // Allocation node's index if (elem == _phantom_object) { @@ -1143,7 +1147,7 @@ continue; // already processed } ptset.Clear(); - PointsTo(ptset, n, igvn); + PointsTo(ptset, n); if (ptset.Size() == 1) { uint elem = ptset.getelem(); // Allocation node's index if (elem == _phantom_object) { @@ -1478,6 +1482,26 @@ return false; } +void ConnectionGraph::do_analysis(Compile *C, PhaseIterGVN *igvn) { + // Add ConP#NULL and ConN#NULL nodes before ConnectionGraph construction + // to create space for them in ConnectionGraph::_nodes[]. + Node* oop_null = igvn->zerocon(T_OBJECT); + Node* noop_null = igvn->zerocon(T_NARROWOOP); + + ConnectionGraph* congraph = new(C->comp_arena()) ConnectionGraph(C, igvn); + // Perform escape analysis + if (congraph->compute_escape()) { + // There are non escaping objects. + C->set_congraph(congraph); + } + + // Cleanup. + if (oop_null->outcnt() == 0) + igvn->hash_delete(oop_null); + if (noop_null->outcnt() == 0) + igvn->hash_delete(noop_null); +} + bool ConnectionGraph::compute_escape() { Compile* C = _compile; @@ -1492,7 +1516,7 @@ } GrowableArray cg_worklist; - PhaseGVN* igvn = C->initial_gvn(); + PhaseGVN* igvn = _igvn; bool has_allocations = false; // Push all useful nodes onto CG list and set their type. @@ -1661,6 +1685,12 @@ _collecting = false; assert(C->unique() == nodes_size(), "there should be no new ideal nodes during ConnectionGraph build"); +#ifndef PRODUCT + if (PrintEscapeAnalysis) { + dump(); // Dump ConnectionGraph + } +#endif + bool has_scalar_replaceable_candidates = alloc_worklist.length() > 0; if ( has_scalar_replaceable_candidates && C->AliasLevel() >= 3 && EliminateAllocations ) { @@ -1671,10 +1701,6 @@ if (C->failing()) return false; - // Clean up after split unique types. - ResourceMark rm; - PhaseRemoveUseless pru(C->initial_gvn(), C->for_igvn()); - C->print_method("After Escape Analysis", 2); #ifdef ASSERT @@ -1711,7 +1737,7 @@ int offset = ptn->offset(); Node* base = get_addp_base(n); ptset.Clear(); - PointsTo(ptset, base, phase); + PointsTo(ptset, base); int ptset_size = ptset.Size(); // Check if a oop field's initializing value is recorded and add @@ -1889,7 +1915,7 @@ arg = get_addp_base(arg); } ptset.Clear(); - PointsTo(ptset, arg, phase); + PointsTo(ptset, arg); for( VectorSetI j(&ptset); j.test(); ++j ) { uint pt = j.elem; set_escape_state(pt, PointsToNode::ArgEscape); @@ -1934,7 +1960,7 @@ } ptset.Clear(); - PointsTo(ptset, arg, phase); + PointsTo(ptset, arg); for( VectorSetI j(&ptset); j.test(); ++j ) { uint pt = j.elem; if (global_escapes) { @@ -1970,7 +1996,7 @@ Node *arg = call->in(i)->uncast(); set_escape_state(arg->_idx, PointsToNode::GlobalEscape); ptset.Clear(); - PointsTo(ptset, arg, phase); + PointsTo(ptset, arg); for( VectorSetI j(&ptset); j.test(); ++j ) { uint pt = j.elem; set_escape_state(pt, PointsToNode::GlobalEscape); @@ -2433,7 +2459,7 @@ Node *base = get_addp_base(n); // Create a field edge to this node from everything base could point to. VectorSet ptset(Thread::current()->resource_area()); - PointsTo(ptset, base, phase); + PointsTo(ptset, base); for( VectorSetI i(&ptset); i.test(); ++i ) { uint pt = i.elem; add_field_edge(pt, n_idx, address_offset(n, phase)); @@ -2501,7 +2527,7 @@ // For everything "adr_base" could point to, create a deferred edge from // this node to each field with the same offset. VectorSet ptset(Thread::current()->resource_area()); - PointsTo(ptset, adr_base, phase); + PointsTo(ptset, adr_base); int offset = address_offset(adr, phase); for( VectorSetI i(&ptset); i.test(); ++i ) { uint pt = i.elem; @@ -2594,7 +2620,7 @@ // For everything "adr_base" could point to, create a deferred edge // to "val" from each field with the same offset. VectorSet ptset(Thread::current()->resource_area()); - PointsTo(ptset, adr_base, phase); + PointsTo(ptset, adr_base); for( VectorSetI i(&ptset); i.test(); ++i ) { uint pt = i.elem; add_edge_from_fields(pt, val->_idx, address_offset(adr, phase)); @@ -2638,7 +2664,6 @@ #ifndef PRODUCT void ConnectionGraph::dump() { - PhaseGVN *igvn = _compile->initial_gvn(); bool first = true; uint size = nodes_size(); @@ -2648,7 +2673,7 @@ if (ptn_type != PointsToNode::JavaObject || ptn->_node == NULL) continue; - PointsToNode::EscapeState es = escape_state(ptn->_node, igvn); + PointsToNode::EscapeState es = escape_state(ptn->_node); if (ptn->_node->is_Allocate() && (es == PointsToNode::NoEscape || Verbose)) { if (first) { tty->cr(); diff -r 0d39bf49436e -r 4a7b48382e77 hotspot/src/share/vm/opto/escape.hpp --- a/hotspot/src/share/vm/opto/escape.hpp Fri Jul 09 00:32:48 2010 -0700 +++ b/hotspot/src/share/vm/opto/escape.hpp Fri Jul 09 00:35:35 2010 -0700 @@ -227,6 +227,7 @@ uint _noop_null; // ConN(#NULL) Compile * _compile; // Compile object for current compilation + PhaseIterGVN * _igvn; // Value numbering // Address of an element in _nodes. Used when the element is to be modified PointsToNode *ptnode_adr(uint idx) const { @@ -257,7 +258,7 @@ // walk the connection graph starting at the node corresponding to "n" and // add the index of everything it could point to, to "ptset". This may cause // Phi's encountered to get (re)processed (which requires "phase".) - void PointsTo(VectorSet &ptset, Node * n, PhaseTransform *phase); + void PointsTo(VectorSet &ptset, Node * n); // Edge manipulation. The "from_i" and "to_i" arguments are the // node indices of the source and destination of the edge @@ -310,7 +311,7 @@ // Node: This assumes that escape analysis is run before // PhaseIterGVN creation void record_for_optimizer(Node *n) { - _compile->record_for_igvn(n); + _igvn->_worklist.push(n); } // Set the escape state of a node @@ -320,16 +321,20 @@ void verify_escape_state(int nidx, VectorSet& ptset, PhaseTransform* phase); public: - ConnectionGraph(Compile *C); + ConnectionGraph(Compile *C, PhaseIterGVN *igvn); // Check for non-escaping candidates static bool has_candidates(Compile *C); + // Perform escape analysis + static void do_analysis(Compile *C, PhaseIterGVN *igvn); + // Compute the escape information bool compute_escape(); // escape state of a node - PointsToNode::EscapeState escape_state(Node *n, PhaseTransform *phase); + PointsToNode::EscapeState escape_state(Node *n); + // other information we have collected bool is_scalar_replaceable(Node *n) { if (_collecting || (n->_idx >= nodes_size())) diff -r 0d39bf49436e -r 4a7b48382e77 hotspot/src/share/vm/prims/jvmtiCodeBlobEvents.cpp --- a/hotspot/src/share/vm/prims/jvmtiCodeBlobEvents.cpp Fri Jul 09 00:32:48 2010 -0700 +++ b/hotspot/src/share/vm/prims/jvmtiCodeBlobEvents.cpp Fri Jul 09 00:35:35 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. 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 @@ -118,34 +118,13 @@ for (int i=0; i<_global_code_blobs->length(); i++) { JvmtiCodeBlobDesc* scb = _global_code_blobs->at(i); if (addr == scb->code_begin()) { + ShouldNotReachHere(); return; } } - // we must name the CodeBlob - some CodeBlobs already have names :- - // - stubs used by compiled code to call a (static) C++ runtime routine - // - non-relocatable machine code such as the interpreter, stubroutines, etc. - // - various singleton blobs - // - // others are unnamed so we create a name :- - // - OSR adapter (interpreter frame that has been on-stack replaced) - // - I2C and C2I adapters - const char* name = NULL; - if (cb->is_runtime_stub()) { - name = ((RuntimeStub*)cb)->name(); - } - if (cb->is_buffer_blob()) { - name = ((BufferBlob*)cb)->name(); - } - if (cb->is_deoptimization_stub() || cb->is_safepoint_stub()) { - name = ((SingletonBlob*)cb)->name(); - } - if (cb->is_uncommon_trap_stub() || cb->is_exception_stub()) { - name = ((SingletonBlob*)cb)->name(); - } - // record the CodeBlob details as a JvmtiCodeBlobDesc - JvmtiCodeBlobDesc* scb = new JvmtiCodeBlobDesc(name, cb->instructions_begin(), + JvmtiCodeBlobDesc* scb = new JvmtiCodeBlobDesc(cb->name(), cb->instructions_begin(), cb->instructions_end()); _global_code_blobs->append(scb); } @@ -197,7 +176,10 @@ jvmtiError JvmtiCodeBlobEvents::generate_dynamic_code_events(JvmtiEnv* env) { CodeBlobCollector collector; - // first collect all the code blobs + // First collect all the code blobs. This has to be done in a + // single pass over the code cache with CodeCache_lock held because + // there isn't any safe way to iterate over regular CodeBlobs since + // they can be freed at any point. { MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); collector.collect(); @@ -213,166 +195,28 @@ } -// Support class to describe a nmethod in the CodeCache - -class nmethodDesc: public CHeapObj { - private: - jmethodID _jmethod_id; - address _code_begin; - address _code_end; - jvmtiAddrLocationMap* _map; - jint _map_length; - public: - nmethodDesc(jmethodID jmethod_id, address code_begin, address code_end, - jvmtiAddrLocationMap* map, jint map_length) { - _jmethod_id = jmethod_id; - _code_begin = code_begin; - _code_end = code_end; - _map = map; - _map_length = map_length; - } - jmethodID jmethod_id() const { return _jmethod_id; } - address code_begin() const { return _code_begin; } - address code_end() const { return _code_end; } - jvmtiAddrLocationMap* map() const { return _map; } - jint map_length() const { return _map_length; } -}; - - -// Support class to collect a list of the nmethod CodeBlobs in -// the CodeCache. -// -// Usage :- -// -// nmethodCollector collector; -// -// collector.collect(); -// JvmtiCodeBlobDesc* blob = collector.first(); -// while (blob != NULL) { -// : -// blob = collector.next(); -// } -// -class nmethodCollector : StackObj { - private: - GrowableArray* _nmethods; // collect nmethods - int _pos; // iteration support - - // used during a collection - static GrowableArray* _global_nmethods; - static void do_nmethod(nmethod* nm); - public: - nmethodCollector() { - _nmethods = NULL; - _pos = -1; - } - ~nmethodCollector() { - if (_nmethods != NULL) { - for (int i=0; i<_nmethods->length(); i++) { - nmethodDesc* blob = _nmethods->at(i); - if (blob->map()!= NULL) { - FREE_C_HEAP_ARRAY(jvmtiAddrLocationMap, blob->map()); - } - } - delete _nmethods; - } - } - - // collect list of nmethods in the cache - void collect(); - - // iteration support - return first code blob - nmethodDesc* first() { - assert(_nmethods != NULL, "not collected"); - if (_nmethods->length() == 0) { - return NULL; - } - _pos = 0; - return _nmethods->at(0); - } - - // iteration support - return next code blob - nmethodDesc* next() { - assert(_pos >= 0, "iteration not started"); - if (_pos+1 >= _nmethods->length()) { - return NULL; - } - return _nmethods->at(++_pos); - } -}; - -// used during collection -GrowableArray* nmethodCollector::_global_nmethods; - - -// called for each nmethod in the CodeCache -// -// This function simply adds a descriptor for each nmethod to the global list. - -void nmethodCollector::do_nmethod(nmethod* nm) { - // ignore zombies - if (!nm->is_alive()) { - return; - } - - assert(nm->method() != NULL, "checking"); - - // create the location map for the nmethod. - jvmtiAddrLocationMap* map; - jint map_length; - JvmtiCodeBlobEvents::build_jvmti_addr_location_map(nm, &map, &map_length); - - // record the nmethod details - nmethodDesc* snm = new nmethodDesc(nm->get_and_cache_jmethod_id(), - nm->code_begin(), - nm->code_end(), - map, - map_length); - _global_nmethods->append(snm); -} - -// collects a list of nmethod in the CodeCache. -// -// The created list is growable array of nmethodDesc - each one describes -// a nmethod and includs its JVMTI address location map. - -void nmethodCollector::collect() { - assert_locked_or_safepoint(CodeCache_lock); - assert(_global_nmethods == NULL, "checking"); - - // create the list - _global_nmethods = new (ResourceObj::C_HEAP) GrowableArray(100,true); - - // any a descriptor for each nmethod to the list. - CodeCache::nmethods_do(do_nmethod); - - // make the list the instance list - _nmethods = _global_nmethods; - _global_nmethods = NULL; -} - // Generate a COMPILED_METHOD_LOAD event for each nnmethod - jvmtiError JvmtiCodeBlobEvents::generate_compiled_method_load_events(JvmtiEnv* env) { HandleMark hm; - nmethodCollector collector; - - // first collect all nmethods - { - MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - collector.collect(); - } - // iterate over the list and post an event for each nmethod - nmethodDesc* nm_desc = collector.first(); - while (nm_desc != NULL) { - jmethodID mid = nm_desc->jmethod_id(); - assert(mid != NULL, "checking"); - JvmtiExport::post_compiled_method_load(env, mid, - (jint)(nm_desc->code_end() - nm_desc->code_begin()), - nm_desc->code_begin(), nm_desc->map_length(), - nm_desc->map()); - nm_desc = collector.next(); + // Walk the CodeCache notifying for live nmethods. The code cache + // may be changing while this is happening which is ok since newly + // created nmethod will notify normally and nmethods which are freed + // can be safely skipped. + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + nmethod* current = CodeCache::first_nmethod(); + while (current != NULL) { + // Lock the nmethod so it can't be freed + nmethodLocker nml(current); + + // Only notify for live nmethods + if (current->is_alive()) { + // Don't hold the lock over the notify or jmethodID creation + MutexUnlockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + current->get_and_cache_jmethod_id(); + JvmtiExport::post_compiled_method_load(current); + } + current = CodeCache::next_nmethod(current); } return JVMTI_ERROR_NONE; }