提交 97cbde7b 编写于 作者: Z zmajo

8129847: Compiling methods generated by Nashorn triggers high memory usage in C2

Summary: Add a new compiler phase, PhaseRenumberLive, that renumbers live nodes.
Reviewed-by: kvn, thartmann, vlivanov, shade
上级 16398e0a
......@@ -669,6 +669,9 @@
product_pd(bool, TrapBasedRangeChecks, \
"Generate code for range checks that uses a cmp and trap " \
"instruction raising SIGTRAP. Used on PPC64.") \
\
develop(bool, RenumberLiveNodes, true, \
"Renumber live nodes") \
C2_FLAGS(DECLARE_DEVELOPER_FLAG, DECLARE_PD_DEVELOPER_FLAG, DECLARE_PRODUCT_FLAG, DECLARE_PD_PRODUCT_FLAG, DECLARE_DIAGNOSTIC_FLAG, DECLARE_EXPERIMENTAL_FLAG, DECLARE_NOTPRODUCT_FLAG)
......
......@@ -2093,6 +2093,20 @@ void Compile::Optimize() {
// so keep only the actual candidates for optimizations.
cleanup_expensive_nodes(igvn);
if (!failing() && RenumberLiveNodes && live_nodes() + NodeLimitFudgeFactor < unique()) {
NOT_PRODUCT(Compile::TracePhase t2("", &_t_renumberLive, TimeCompiler);)
initial_gvn()->replace_with(&igvn);
for_igvn()->clear();
Unique_Node_List new_worklist(C->comp_arena());
{
ResourceMark rm;
PhaseRenumberLive prl = PhaseRenumberLive(initial_gvn(), for_igvn(), &new_worklist);
}
set_for_igvn(&new_worklist);
igvn = PhaseIterGVN(initial_gvn());
igvn.optimize();
}
// Perform escape analysis
if (_do_escape_analysis && ConnectionGraph::has_candidates(this)) {
if (has_loops()) {
......
......@@ -325,6 +325,9 @@ inline int Node::Init(int req, Compile* C) {
// Create a Node, with a given number of required edges.
Node::Node(uint req)
: _idx(IDX_INIT(req))
#ifdef ASSERT
, _parse_idx(_idx)
#endif
{
assert( req < Compile::current()->max_node_limit() - NodeLimitFudgeFactor, "Input limit exceeded" );
debug_only( verify_construction() );
......@@ -344,6 +347,9 @@ Node::Node(uint req)
//------------------------------Node-------------------------------------------
Node::Node(Node *n0)
: _idx(IDX_INIT(1))
#ifdef ASSERT
, _parse_idx(_idx)
#endif
{
debug_only( verify_construction() );
NOT_PRODUCT(nodes_created++);
......@@ -356,6 +362,9 @@ Node::Node(Node *n0)
//------------------------------Node-------------------------------------------
Node::Node(Node *n0, Node *n1)
: _idx(IDX_INIT(2))
#ifdef ASSERT
, _parse_idx(_idx)
#endif
{
debug_only( verify_construction() );
NOT_PRODUCT(nodes_created++);
......@@ -370,6 +379,9 @@ Node::Node(Node *n0, Node *n1)
//------------------------------Node-------------------------------------------
Node::Node(Node *n0, Node *n1, Node *n2)
: _idx(IDX_INIT(3))
#ifdef ASSERT
, _parse_idx(_idx)
#endif
{
debug_only( verify_construction() );
NOT_PRODUCT(nodes_created++);
......@@ -386,6 +398,9 @@ Node::Node(Node *n0, Node *n1, Node *n2)
//------------------------------Node-------------------------------------------
Node::Node(Node *n0, Node *n1, Node *n2, Node *n3)
: _idx(IDX_INIT(4))
#ifdef ASSERT
, _parse_idx(_idx)
#endif
{
debug_only( verify_construction() );
NOT_PRODUCT(nodes_created++);
......@@ -404,6 +419,9 @@ Node::Node(Node *n0, Node *n1, Node *n2, Node *n3)
//------------------------------Node-------------------------------------------
Node::Node(Node *n0, Node *n1, Node *n2, Node *n3, Node *n4)
: _idx(IDX_INIT(5))
#ifdef ASSERT
, _parse_idx(_idx)
#endif
{
debug_only( verify_construction() );
NOT_PRODUCT(nodes_created++);
......@@ -425,6 +443,9 @@ Node::Node(Node *n0, Node *n1, Node *n2, Node *n3, Node *n4)
Node::Node(Node *n0, Node *n1, Node *n2, Node *n3,
Node *n4, Node *n5)
: _idx(IDX_INIT(6))
#ifdef ASSERT
, _parse_idx(_idx)
#endif
{
debug_only( verify_construction() );
NOT_PRODUCT(nodes_created++);
......@@ -448,6 +469,9 @@ Node::Node(Node *n0, Node *n1, Node *n2, Node *n3,
Node::Node(Node *n0, Node *n1, Node *n2, Node *n3,
Node *n4, Node *n5, Node *n6)
: _idx(IDX_INIT(7))
#ifdef ASSERT
, _parse_idx(_idx)
#endif
{
debug_only( verify_construction() );
NOT_PRODUCT(nodes_created++);
......
......@@ -294,10 +294,16 @@ protected:
public:
// Each Node is assigned a unique small/dense number. This number is used
// to index into auxiliary arrays of data and bitvectors.
// It is declared const to defend against inadvertant assignment,
// since it is used by clients as a naked field.
// to index into auxiliary arrays of data and bit vectors.
// The field _idx is declared constant to defend against inadvertent assignments,
// since it is used by clients as a naked field. However, the field's value can be
// changed using the set_idx() method.
//
// The PhaseRenumberLive phase renumbers nodes based on liveness information.
// Therefore, it updates the value of the _idx field. The parse-time _idx is
// preserved in _parse_idx.
const node_idx_t _idx;
DEBUG_ONLY(const node_idx_t _parse_idx;)
// Get the (read-only) number of input edges
uint req() const { return _cnt; }
......
......@@ -67,6 +67,8 @@ elapsedTimer Phase::_t_idealLoopVerify;
elapsedTimer Phase::_t_iterGVN;
elapsedTimer Phase::_t_iterGVN2;
elapsedTimer Phase::_t_incrInline;
elapsedTimer Phase::_t_renumberLive;
// Subtimers for _t_registerAllocation
elapsedTimer Phase::_t_ctorChaitin;
......@@ -115,13 +117,14 @@ void Phase::print_timers() {
}
tty->print_cr (" iterGVN : %3.3f sec", Phase::_t_iterGVN.seconds());
tty->print_cr (" incrInline : %3.3f sec", Phase::_t_incrInline.seconds());
tty->print_cr (" renumberLive : %3.3f sec", Phase::_t_renumberLive.seconds());
tty->print_cr (" idealLoop : %3.3f sec", Phase::_t_idealLoop.seconds());
tty->print_cr (" idealLoopVerify: %3.3f sec", Phase::_t_idealLoopVerify.seconds());
tty->print_cr (" ccp : %3.3f sec", Phase::_t_ccp.seconds());
tty->print_cr (" iterGVN2 : %3.3f sec", Phase::_t_iterGVN2.seconds());
tty->print_cr (" macroExpand : %3.3f sec", Phase::_t_macroExpand.seconds());
tty->print_cr (" graphReshape : %3.3f sec", Phase::_t_graphReshaping.seconds());
double optimizer_subtotal = Phase::_t_iterGVN.seconds() + Phase::_t_iterGVN2.seconds() +
double optimizer_subtotal = Phase::_t_iterGVN.seconds() + Phase::_t_iterGVN2.seconds() + Phase::_t_renumberLive.seconds() +
Phase::_t_escapeAnalysis.seconds() + Phase::_t_macroEliminate.seconds() +
Phase::_t_idealLoop.seconds() + Phase::_t_ccp.seconds() +
Phase::_t_macroExpand.seconds() + Phase::_t_graphReshaping.seconds();
......
......@@ -40,22 +40,23 @@ class Compile;
class Phase : public StackObj {
public:
enum PhaseNumber {
Compiler, // Top-level compiler phase
Parser, // Parse bytecodes
Remove_Useless, // Remove useless nodes
Optimistic, // Optimistic analysis phase
GVN, // Pessimistic global value numbering phase
Ins_Select, // Instruction selection phase
CFG, // Build a CFG
BlockLayout, // Linear ordering of blocks
Register_Allocation, // Register allocation, duh
LIVE, // Dragon-book LIVE range problem
StringOpts, // StringBuilder related optimizations
Interference_Graph, // Building the IFG
Coalesce, // Coalescing copies
Ideal_Loop, // Find idealized trip-counted loops
Macro_Expand, // Expand macro nodes
Peephole, // Apply peephole optimizations
Compiler, // Top-level compiler phase
Parser, // Parse bytecodes
Remove_Useless, // Remove useless nodes
Remove_Useless_And_Renumber_Live, // First, remove useless nodes from the graph. Then, renumber live nodes.
Optimistic, // Optimistic analysis phase
GVN, // Pessimistic global value numbering phase
Ins_Select, // Instruction selection phase
CFG, // Build a CFG
BlockLayout, // Linear ordering of blocks
Register_Allocation, // Register allocation, duh
LIVE, // Dragon-book LIVE range problem
StringOpts, // StringBuilder related optimizations
Interference_Graph, // Building the IFG
Coalesce, // Coalescing copies
Ideal_Loop, // Find idealized trip-counted loops
Macro_Expand, // Expand macro nodes
Peephole, // Apply peephole optimizations
last_phase
};
protected:
......@@ -102,6 +103,7 @@ protected:
static elapsedTimer _t_iterGVN;
static elapsedTimer _t_iterGVN2;
static elapsedTimer _t_incrInline;
static elapsedTimer _t_renumberLive;
// Subtimers for _t_registerAllocation
static elapsedTimer _t_ctorChaitin;
......
......@@ -398,7 +398,7 @@ void NodeHash::operator=(const NodeHash& nh) {
//=============================================================================
//------------------------------PhaseRemoveUseless-----------------------------
// 1) Use a breadthfirst walk to collect useful nodes reachable from root.
PhaseRemoveUseless::PhaseRemoveUseless( PhaseGVN *gvn, Unique_Node_List *worklist ) : Phase(Remove_Useless),
PhaseRemoveUseless::PhaseRemoveUseless(PhaseGVN *gvn, Unique_Node_List *worklist, PhaseNumber phase_num) : Phase(phase_num),
_useful(Thread::current()->resource_area()) {
// Implementation requires 'UseLoopSafepoints == true' and an edge from root
......@@ -435,6 +435,82 @@ PhaseRemoveUseless::PhaseRemoveUseless( PhaseGVN *gvn, Unique_Node_List *worklis
}
}
//=============================================================================
//------------------------------PhaseRenumberLive------------------------------
// First, remove useless nodes (equivalent to identifying live nodes).
// Then, renumber live nodes.
//
// The set of live nodes is returned by PhaseRemoveUseless in the _useful structure.
// If the number of live nodes is 'x' (where 'x' == _useful.size()), then the
// PhaseRenumberLive updates the node ID of each node (the _idx field) with a unique
// value in the range [0, x).
//
// At the end of the PhaseRenumberLive phase, the compiler's count of unique nodes is
// updated to 'x' and the list of dead nodes is reset (as there are no dead nodes).
//
// The PhaseRenumberLive phase updates two data structures with the new node IDs.
// (1) The worklist is used by the PhaseIterGVN phase to identify nodes that must be
// processed. A new worklist (with the updated node IDs) is returned in 'new_worklist'.
// (2) Type information (the field PhaseGVN::_types) maps type information to each
// node ID. The mapping is updated to use the new node IDs as well. Updated type
// information is returned in PhaseGVN::_types.
//
// The PhaseRenumberLive phase does not preserve the order of elements in the worklist.
//
// Other data structures used by the compiler are not updated. The hash table for value
// numbering (the field PhaseGVN::_table) is not updated because computing the hash
// values is not based on node IDs. The field PhaseGVN::_nodes is not updated either
// because it is empty wherever PhaseRenumberLive is used.
PhaseRenumberLive::PhaseRenumberLive(PhaseGVN* gvn,
Unique_Node_List* worklist, Unique_Node_List* new_worklist,
PhaseNumber phase_num) :
PhaseRemoveUseless(gvn, worklist, Remove_Useless_And_Renumber_Live) {
assert(RenumberLiveNodes, "RenumberLiveNodes must be set to true for node renumbering to take place");
assert(C->live_nodes() == _useful.size(), "the number of live nodes must match the number of useful nodes");
assert(gvn->nodes_size() == 0, "GVN must not contain any nodes at this point");
uint old_unique_count = C->unique();
uint live_node_count = C->live_nodes();
uint worklist_size = worklist->size();
// Storage for the updated type information.
Type_Array new_type_array(C->comp_arena());
// Iterate over the set of live nodes.
uint current_idx = 0; // The current new node ID. Incremented after every assignment.
for (uint i = 0; i < _useful.size(); i++) {
Node* n = _useful.at(i);
const Type* type = gvn->type_or_null(n);
new_type_array.map(current_idx, type);
bool in_worklist = false;
if (worklist->member(n)) {
in_worklist = true;
}
n->set_idx(current_idx); // Update node ID.
if (in_worklist) {
new_worklist->push(n);
}
current_idx++;
}
assert(worklist_size == new_worklist->size(), "the new worklist must have the same size as the original worklist");
assert(live_node_count == current_idx, "all live nodes must be processed");
// Replace the compiler's type information with the updated type information.
gvn->replace_types(new_type_array);
// Update the unique node count of the compilation to the number of currently live nodes.
C->set_unique(live_node_count);
// Set the dead node count to 0 and reset dead node list.
C->reset_dead_node_list();
}
//=============================================================================
//------------------------------PhaseTransform---------------------------------
......
......@@ -148,11 +148,21 @@ protected:
Unique_Node_List _useful; // Nodes reachable from root
// list is allocated from current resource area
public:
PhaseRemoveUseless( PhaseGVN *gvn, Unique_Node_List *worklist );
PhaseRemoveUseless(PhaseGVN *gvn, Unique_Node_List *worklist, PhaseNumber phase_num = Remove_Useless);
Unique_Node_List *get_useful() { return &_useful; }
};
//------------------------------PhaseRenumber----------------------------------
// Phase that first performs a PhaseRemoveUseless, then it renumbers compiler
// structures accordingly.
class PhaseRenumberLive : public PhaseRemoveUseless {
public:
PhaseRenumberLive(PhaseGVN* gvn,
Unique_Node_List* worklist, Unique_Node_List* new_worklist,
PhaseNumber phase_num = Remove_Useless_And_Renumber_Live);
};
//------------------------------PhaseTransform---------------------------------
// Phases that analyze, then transform. Constructing the Phase object does any
......@@ -162,7 +172,7 @@ public:
class PhaseTransform : public Phase {
protected:
Arena* _arena;
Node_Array _nodes; // Map old node indices to new nodes.
Node_List _nodes; // Map old node indices to new nodes.
Type_Array _types; // Map old node indices to Types.
// ConNode caches:
......@@ -187,7 +197,13 @@ public:
Arena* arena() { return _arena; }
Type_Array& types() { return _types; }
void replace_types(Type_Array new_types) {
_types = new_types;
}
// _nodes is used in varying ways by subclasses, which define local accessors
uint nodes_size() {
return _nodes.size();
}
public:
// Get a previously recorded type for the node n.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册