提交 eb7c3289 编写于 作者: K kvn

6674588: (Escape Analysis) Improve Escape Analysis code

Summary: Current EA code has several problems which have to be fixed.
Reviewed-by: jrose, sgoldman
上级 53f4ef4a
......@@ -164,6 +164,7 @@ callGenerator.hpp deoptimization.hpp
callGenerator.hpp type.hpp
callnode.cpp callnode.hpp
callnode.cpp bcEscapeAnalyzer.hpp
callnode.cpp escape.hpp
callnode.cpp locknode.hpp
callnode.cpp machnode.hpp
......@@ -176,7 +177,6 @@ callnode.cpp rootnode.hpp
callnode.cpp runtime.hpp
callnode.hpp connode.hpp
callnode.hpp escape.hpp
callnode.hpp mulnode.hpp
callnode.hpp multnode.hpp
callnode.hpp opcodes.hpp
......@@ -347,7 +347,6 @@ connode.cpp addnode.hpp
connode.cpp allocation.inline.hpp
connode.cpp compile.hpp
connode.cpp connode.hpp
connode.cpp escape.hpp
connode.cpp machnode.hpp
connode.cpp matcher.hpp
connode.cpp memnode.hpp
......@@ -844,7 +843,6 @@ phaseX.cpp block.hpp
phaseX.cpp callnode.hpp
phaseX.cpp cfgnode.hpp
phaseX.cpp connode.hpp
phaseX.cpp escape.hpp
phaseX.cpp loopnode.hpp
phaseX.cpp machnode.hpp
phaseX.cpp opcodes.hpp
......
......@@ -382,6 +382,12 @@
product(bool, EliminateAllocations, true, \
"Use escape analysis to eliminate allocations") \
\
notproduct(bool, PrintEliminateAllocations, false, \
"Print out when allocations are eliminated") \
\
product(intx, EliminateAllocationArraySizeLimit, 64, \
"Array size (number of elements) limit for scalar replacement") \
\
product(intx, MaxLabelRootDepth, 1100, \
"Maximum times call Label_Root to prevent stack overflow") \
......
......@@ -624,6 +624,87 @@ uint CallNode::match_edge(uint idx) const {
return 0;
}
//
// Determine whether the call could modify a memory value of the
// specified address type
//
bool CallNode::may_modify(const TypePtr *addr_t, PhaseTransform *phase) {
const TypeOopPtr *adrInst_t = addr_t->isa_oopptr();
// if not an InstPtr or not an instance type, assume the worst
if (adrInst_t == NULL || !adrInst_t->is_instance_field()) {
return true;
}
Compile *C = phase->C;
int offset = adrInst_t->offset();
assert(offset >= 0, "should be valid offset");
assert(addr_t->isa_instptr() || addr_t->isa_aryptr(), "only instances or arrays are expected");
int base_idx = C->get_alias_index(adrInst_t);
ciMethod * meth = is_CallStaticJava() ? as_CallStaticJava()->method() : NULL;
BCEscapeAnalyzer *bcea = (meth != NULL) ? meth->get_bcea() : NULL;
const TypeTuple * d = tf()->domain();
for (uint i = TypeFunc::Parms; i < d->cnt(); i++) {
const Type* t = d->field_at(i);
Node *arg = in(i);
const Type *at = phase->type(arg);
if (at == TypePtr::NULL_PTR || at == Type::TOP)
continue; // null can't affect anything
const TypeOopPtr *at_ptr = at->isa_oopptr();
if (!arg->is_top() && (t->isa_oopptr() != NULL ||
t->isa_ptr() && at_ptr != NULL)) {
assert(at_ptr != NULL, "expecting an OopPtr");
// If we have found an argument matching adr_base_t, check if the field
// at the specified offset is modified. Since we don't know the size,
// assume 8.
int at_idx = C->get_alias_index(at_ptr->add_offset(offset)->isa_oopptr());
if (base_idx == at_idx &&
(bcea == NULL ||
bcea->is_arg_modified(i - TypeFunc::Parms, offset, 8))) {
return true;
}
}
}
return false;
}
// Does this call have a direct reference to n other than debug information?
bool CallNode::has_non_debug_use(Node *n) {
const TypeTuple * d = tf()->domain();
for (uint i = TypeFunc::Parms; i < d->cnt(); i++) {
Node *arg = in(i);
if (arg == n) {
return true;
}
}
return false;
}
// Returns the unique CheckCastPP of a call
// or 'this' if there are several CheckCastPP
// or returns NULL if there is no one.
Node *CallNode::result_cast() {
Node *cast = NULL;
Node *p = proj_out(TypeFunc::Parms);
if (p == NULL)
return NULL;
for (DUIterator_Fast imax, i = p->fast_outs(imax); i < imax; i++) {
Node *use = p->fast_out(i);
if (use->is_CheckCastPP()) {
if (cast != NULL) {
return this; // more than 1 CheckCastPP
}
cast = use;
}
}
return cast;
}
//=============================================================================
uint CallJavaNode::size_of() const { return sizeof(*this); }
uint CallJavaNode::cmp( const Node &n ) const {
......
......@@ -388,6 +388,9 @@ public:
void set_next_exception(SafePointNode* n);
bool has_exceptions() const { return next_exception() != NULL; }
// Does this node have a use of n other than in debug information?
virtual bool has_non_debug_use(Node *n) {return false; }
// Standard Node stuff
virtual int Opcode() const;
virtual bool pinned() const { return true; }
......@@ -457,7 +460,6 @@ public:
const TypeFunc *_tf; // Function type
address _entry_point; // Address of method being called
float _cnt; // Estimate of number of times called
PointsToNode::EscapeState _escape_state;
CallNode(const TypeFunc* tf, address addr, const TypePtr* adr_type)
: SafePointNode(tf->domain()->cnt(), NULL, adr_type),
......@@ -467,7 +469,6 @@ public:
{
init_class_id(Class_Call);
init_flags(Flag_is_Call);
_escape_state = PointsToNode::UnknownEscape;
}
const TypeFunc* tf() const { return _tf; }
......@@ -493,6 +494,15 @@ public:
// the node the JVMState must be cloned.
virtual void clone_jvms() { } // default is not to clone
// Returns true if the call may modify n
virtual bool may_modify(const TypePtr *addr_t, PhaseTransform *phase);
// Does this node have a use of n other than in debug information?
virtual bool has_non_debug_use(Node *n);
// Returns the unique CheckCastPP of a call
// or result projection is there are several CheckCastPP
// or returns NULL if there is no one.
Node *result_cast();
virtual uint match_edge(uint idx) const;
#ifndef PRODUCT
......@@ -689,6 +699,9 @@ public:
virtual uint ideal_reg() const { return Op_RegP; }
virtual bool guaranteed_safepoint() { return false; }
// allocations do not modify their arguments
virtual bool may_modify(const TypePtr *addr_t, PhaseTransform *phase) { return false;}
// Pattern-match a possible usage of AllocateNode.
// Return null if no allocation is recognized.
// The operand is the pointer produced by the (possible) allocation.
......@@ -801,6 +814,9 @@ public:
// mark node as eliminated and update the counter if there is one
void set_eliminated();
// locking does not modify its arguments
virtual bool may_modify(const TypePtr *addr_t, PhaseTransform *phase){ return false;}
#ifndef PRODUCT
void create_lock_counter(JVMState* s);
NamedCounter* counter() const { return _counter; }
......
......@@ -407,11 +407,6 @@ uint Compile::scratch_emit_size(const Node* n) {
return buf.code_size();
}
void Compile::record_for_escape_analysis(Node* n) {
if (_congraph != NULL)
_congraph->record_for_escape_analysis(n);
}
// ============================================================================
//------------------------------Compile standard-------------------------------
......@@ -494,9 +489,6 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr
PhaseGVN gvn(node_arena(), estimated_size);
set_initial_gvn(&gvn);
if (_do_escape_analysis)
_congraph = new ConnectionGraph(this);
{ // Scope for timing the parser
TracePhase t3("parse", &_t_parser, true);
......@@ -581,6 +573,8 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr
NOT_PRODUCT( verify_graph_edges(); )
// Perform escape analysis
if (_do_escape_analysis)
_congraph = new ConnectionGraph(this);
if (_congraph != NULL) {
NOT_PRODUCT( TracePhase t2("escapeAnalysis", &_t_escapeAnalysis, TimeCompiler); )
_congraph->compute_escape();
......
......@@ -485,7 +485,6 @@ class Compile : public Phase {
PhaseGVN* initial_gvn() { return _initial_gvn; }
Unique_Node_List* for_igvn() { return _for_igvn; }
inline void record_for_igvn(Node* n); // Body is after class Unique_Node_List.
void record_for_escape_analysis(Node* n);
void set_initial_gvn(PhaseGVN *gvn) { _initial_gvn = gvn; }
void set_for_igvn(Unique_Node_List *for_igvn) { _for_igvn = for_igvn; }
......
此差异已折叠。
......@@ -25,14 +25,15 @@
//
// Adaptation for C2 of the escape analysis algorithm described in:
//
// [Choi99] Jong-Deok Shoi, Manish Gupta, Mauricio Seffano, Vugranam C. Sreedhar,
// Sam Midkiff, "Escape Analysis for Java", Procedings of ACM SIGPLAN
// OOPSLA Conference, November 1, 1999
// [Choi99] Jong-Deok Shoi, Manish Gupta, Mauricio Seffano,
// Vugranam C. Sreedhar, Sam Midkiff,
// "Escape Analysis for Java", Procedings of ACM SIGPLAN
// OOPSLA Conference, November 1, 1999
//
// The flow-insensitive analysis described in the paper has been implemented.
//
// The analysis requires construction of a "connection graph" (CG) for the method being
// analyzed. The nodes of the connection graph are:
// The analysis requires construction of a "connection graph" (CG) for
// the method being analyzed. The nodes of the connection graph are:
//
// - Java objects (JO)
// - Local variables (LV)
......@@ -40,47 +41,51 @@
//
// The CG contains 3 types of edges:
//
// - PointsTo (-P>) {LV,OF} to JO
// - Deferred (-D>) from {LV, OF} to {LV, OF}
// - PointsTo (-P>) {LV, OF} to JO
// - Deferred (-D>) from {LV, OF} to {LV, OF}
// - Field (-F>) from JO to OF
//
// The following utility functions is used by the algorithm:
//
// PointsTo(n) - n is any CG node, it returns the set of JO that n could
// point to.
// PointsTo(n) - n is any CG node, it returns the set of JO that n could
// point to.
//
// The algorithm describes how to construct the connection graph in the following 4 cases:
// The algorithm describes how to construct the connection graph
// in the following 4 cases:
//
// Case Edges Created
//
// (1) p = new T() LV -P> JO
// (2) p = q LV -D> LV
// (3) p.f = q JO -F> OF, OF -D> LV
// (4) p = q.f JO -F> OF, LV -D> OF
// (1) p = new T() LV -P> JO
// (2) p = q LV -D> LV
// (3) p.f = q JO -F> OF, OF -D> LV
// (4) p = q.f JO -F> OF, LV -D> OF
//
// In all these cases, p and q are local variables. For static field references, we can
// construct a local variable containing a reference to the static memory.
// In all these cases, p and q are local variables. For static field
// references, we can construct a local variable containing a reference
// to the static memory.
//
// C2 does not have local variables. However for the purposes of constructing
// the connection graph, the following IR nodes are treated as local variables:
// Phi (pointer values)
// LoadP
// Proj (value returned from callnodes including allocations)
// CheckCastPP
// Proj#5 (value returned from callnodes including allocations)
// CheckCastPP, CastPP
//
// The LoadP, Proj and CheckCastPP behave like variables assigned to only once. Only
// a Phi can have multiple assignments. Each input to a Phi is treated
// The LoadP, Proj and CheckCastPP behave like variables assigned to only once.
// Only a Phi can have multiple assignments. Each input to a Phi is treated
// as an assignment to it.
//
// The following note types are JavaObject:
// The following node types are JavaObject:
//
// top()
// Allocate
// AllocateArray
// Parm (for incoming arguments)
// CastX2P ("unsafe" operations)
// CreateEx
// ConP
// LoadKlass
// ThreadLocal
//
// AddP nodes are fields.
//
......@@ -89,7 +94,7 @@
// source. This results in a graph with no deferred edges, only:
//
// LV -P> JO
// OF -P> JO
// OF -P> JO (the object whose oop is stored in the field)
// JO -F> OF
//
// Then, for each node which is GlobalEscape, anything it could point to
......@@ -110,17 +115,18 @@ class PointsToNode {
friend class ConnectionGraph;
public:
typedef enum {
UnknownType = 0,
JavaObject = 1,
LocalVar = 2,
Field = 3
UnknownType = 0,
JavaObject = 1,
LocalVar = 2,
Field = 3
} NodeType;
typedef enum {
UnknownEscape = 0,
NoEscape = 1,
ArgEscape = 2,
GlobalEscape = 3
NoEscape = 1, // A scalar replaceable object with unique type.
ArgEscape = 2, // An object passed as argument or referenced by
// argument (and not globally escape during call).
GlobalEscape = 3 // An object escapes the method and thread.
} EscapeState;
typedef enum {
......@@ -140,19 +146,25 @@ private:
NodeType _type;
EscapeState _escape;
GrowableArray<uint>* _edges; // outgoing edges
int _offset; // for fields
GrowableArray<uint>* _edges; // outgoing edges
bool _unique_type; // For allocated objects, this node may be a unique type
public:
Node* _node; // Ideal node corresponding to this PointsTo node
int _inputs_processed; // the number of Phi inputs that have been processed so far
bool _hidden_alias; // this node is an argument to a function which may return it
// creating a hidden alias
Node* _node; // Ideal node corresponding to this PointsTo node.
int _offset; // Object fields offsets.
bool _scalar_replaceable;// Not escaped object could be replaced with scalar
bool _hidden_alias; // This node is an argument to a function.
// which may return it creating a hidden alias.
PointsToNode():
_type(UnknownType),
_escape(UnknownEscape),
_edges(NULL),
_node(NULL),
_offset(-1),
_scalar_replaceable(true),
_hidden_alias(false) {}
PointsToNode(): _offset(-1), _type(UnknownType), _escape(UnknownEscape), _edges(NULL), _node(NULL), _inputs_processed(0), _hidden_alias(false), _unique_type(true) {}
EscapeState escape_state() const { return _escape; }
NodeType node_type() const { return _type;}
int offset() { return _offset;}
......@@ -182,22 +194,28 @@ public:
class ConnectionGraph: public ResourceObj {
private:
enum {
INITIAL_NODE_COUNT = 100 // initial size of _nodes array
};
GrowableArray<PointsToNode>* _nodes; // Connection graph nodes indexed
// by ideal node index.
Unique_Node_List _delayed_worklist; // Nodes to be processed before
// the call build_connection_graph().
VectorSet _processed; // Records which nodes have been
// processed.
GrowableArray<PointsToNode>* _nodes; // connection graph nodes Indexed by ideal
// node index
Unique_Node_List _deferred; // Phi's to be processed after parsing
VectorSet _processed; // records which nodes have been processed
bool _collecting; // indicates whether escape information is
// still being collected. If false, no new
// nodes will be processed
uint _phantom_object; // index of globally escaping object that
// pointer values loaded from a field which
// has not been set are assumed to point to
Compile * _compile; // Compile object for current compilation
bool _collecting; // Indicates whether escape information
// is still being collected. If false,
// no new nodes will be processed.
bool _has_allocations; // Indicates whether method has any
// non-escaping allocations.
uint _phantom_object; // Index of globally escaping object
// that pointer values loaded from
// a field which has not been set
// are assumed to point to.
Compile * _compile; // Compile object for current compilation
// address of an element in _nodes. Used when the element is to be modified
PointsToNode *ptnode_adr(uint idx) {
......@@ -208,8 +226,11 @@ private:
return _nodes->adr_at(idx);
}
// Add node to ConnectionGraph.
void add_node(Node *n, PointsToNode::NodeType nt, PointsToNode::EscapeState es, bool done);
// offset of a field reference
int type_to_offset(const Type *t);
int address_offset(Node* adr, PhaseTransform *phase);
// compute the escape state for arguments to a call
void process_call_arguments(CallNode *call, PhaseTransform *phase);
......@@ -217,12 +238,11 @@ private:
// compute the escape state for the return value of a call
void process_call_result(ProjNode *resproj, PhaseTransform *phase);
// compute the escape state of a Phi. This may be called multiple
// times as new inputs are added to the Phi.
void process_phi_escape(PhiNode *phi, PhaseTransform *phase);
// Populate Connection Graph with Ideal nodes.
void record_for_escape_analysis(Node *n, PhaseTransform *phase);
// compute the escape state of an ideal node.
void record_escape_work(Node *n, PhaseTransform *phase);
// Build Connection Graph and set nodes escape state.
void build_connection_graph(Node *n, PhaseTransform *phase);
// walk the connection graph starting at the node corresponding to "n" and
// add the index of everything it could point to, to "ptset". This may cause
......@@ -241,8 +261,8 @@ private:
// a pointsto edge is added if it is a JavaObject
void add_edge_from_fields(uint adr, uint to_i, int offs);
// Add a deferred edge from node given by "from_i" to any field of adr_i whose offset
// matches "offset"
// Add a deferred edge from node given by "from_i" to any field
// of adr_i whose offset matches "offset"
void add_deferred_edge_to_fields(uint from_i, uint adr, int offs);
......@@ -262,6 +282,8 @@ private:
PhiNode *create_split_phi(PhiNode *orig_phi, int alias_idx, GrowableArray<PhiNode *> &orig_phi_worklist, PhaseGVN *igvn, bool &new_created);
PhiNode *split_memory_phi(PhiNode *orig_phi, int alias_idx, GrowableArray<PhiNode *> &orig_phi_worklist, PhaseGVN *igvn);
Node *find_mem(Node *mem, int alias_idx, PhaseGVN *igvn);
Node *find_inst_mem(Node *mem, int alias_idx,GrowableArray<PhiNode *> &orig_phi_worklist, PhaseGVN *igvn);
// Propagate unique types created for unescaped allocated objects
// through the graph
void split_unique_types(GrowableArray<Node *> &alloc_worklist);
......@@ -285,26 +307,24 @@ private:
// Set the escape state of a node
void set_escape_state(uint ni, PointsToNode::EscapeState es);
// bypass any casts and return the node they refer to
Node * skip_casts(Node *n);
// Get Compile object for current compilation.
Compile *C() const { return _compile; }
public:
ConnectionGraph(Compile *C);
// record a Phi for later processing.
void record_for_escape_analysis(Node *n);
// process a node and fill in its connection graph node
void record_escape(Node *n, PhaseTransform *phase);
// All nodes have been recorded, compute the escape information
// Compute the escape information
void compute_escape();
// escape state of a node
PointsToNode::EscapeState escape_state(Node *n, PhaseTransform *phase);
// other information we have collected
bool is_scalar_replaceable(Node *n) {
if (_collecting)
return false;
PointsToNode ptn = _nodes->at_grow(n->_idx);
return ptn.escape_state() == PointsToNode::NoEscape && ptn._scalar_replaceable;
}
bool hidden_alias(Node *n) {
if (_collecting)
......
......@@ -812,8 +812,7 @@ int Node::disconnect_inputs(Node *n) {
Node* Node::uncast() const {
// Should be inline:
//return is_ConstraintCast() ? uncast_helper(this) : (Node*) this;
if (is_ConstraintCast() ||
(is_Type() && req() == 2 && Opcode() == Op_CheckCastPP))
if (is_ConstraintCast() || is_CheckCastPP())
return uncast_helper(this);
else
return (Node*) this;
......@@ -827,7 +826,7 @@ Node* Node::uncast_helper(const Node* p) {
break;
} else if (p->is_ConstraintCast()) {
p = p->in(1);
} else if (p->is_Type() && p->Opcode() == Op_CheckCastPP) {
} else if (p->is_CheckCastPP()) {
p = p->in(1);
} else {
break;
......
......@@ -1328,7 +1328,6 @@ public:
// Inline definition of Compile::record_for_igvn must be deferred to this point.
inline void Compile::record_for_igvn(Node* n) {
_for_igvn->push(n);
record_for_escape_analysis(n);
}
//------------------------------Node_Stack-------------------------------------
......
......@@ -587,11 +587,6 @@ ConNode* PhaseValues::uncached_makecon(const Type *t) {
Node_Notes* loc = C->locate_node_notes(nna, x->_idx, true);
loc->clear(); // do not put debug info on constants
}
// Collect points-to information for escape analysys
ConnectionGraph *cgr = C->congraph();
if (cgr != NULL) {
cgr->record_escape(x, this);
}
} else {
x->destruct(); // Hit, destroy duplicate constant
x = k; // use existing constant
......@@ -714,12 +709,6 @@ Node *PhaseGVN::transform_no_reclaim( Node *n ) {
return i;
}
// Collect points-to information for escape analysys
ConnectionGraph *cgr = C->congraph();
if (cgr != NULL) {
cgr->record_escape(k, this);
}
// Return Idealized original
return k;
}
......@@ -1245,7 +1234,7 @@ void PhaseIterGVN::add_users_to_worklist( Node *n ) {
uint use_op = use->Opcode();
// If changed Cast input, check Phi users for simple cycles
if( use->is_ConstraintCast() || use->Opcode() == Op_CheckCastPP ) {
if( use->is_ConstraintCast() || use->is_CheckCastPP() ) {
for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) {
Node* u = use->fast_out(i2);
if (u->is_Phi())
......
......@@ -1276,6 +1276,9 @@ void Arguments::set_aggressive_opts_flags() {
sprintf(buffer, "java.lang.Integer.IntegerCache.high=%d", AutoBoxCacheMax);
add_property(buffer);
}
if (AggressiveOpts && FLAG_IS_DEFAULT(DoEscapeAnalysis)) {
FLAG_SET_DEFAULT(DoEscapeAnalysis, true);
}
#endif
if (AggressiveOpts) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册