7129284: +DoEscapeAnalysis regression w/ early build of 7u4 (HotSpot 23) on Linux
Removed code which tried to create edges from fields of destination objects of arraycopy to fields of source objects. Added 30 sec time limit for EA graph construction. Reviewed-by: never
This commit is contained in:
parent
610e13c424
commit
2d533fa2a9
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -1687,12 +1687,23 @@ bool ConnectionGraph::compute_escape() {
|
|||||||
// Observed 8 passes in jvm2008 compiler.compiler.
|
// Observed 8 passes in jvm2008 compiler.compiler.
|
||||||
// Set limit to 20 to catch situation when something
|
// Set limit to 20 to catch situation when something
|
||||||
// did go wrong and recompile the method without EA.
|
// did go wrong and recompile the method without EA.
|
||||||
|
// Also limit build time to 30 sec (60 in debug VM).
|
||||||
|
|
||||||
#define CG_BUILD_ITER_LIMIT 20
|
#define CG_BUILD_ITER_LIMIT 20
|
||||||
|
|
||||||
|
#ifdef ASSERT
|
||||||
|
#define CG_BUILD_TIME_LIMIT 60.0
|
||||||
|
#else
|
||||||
|
#define CG_BUILD_TIME_LIMIT 30.0
|
||||||
|
#endif
|
||||||
|
|
||||||
uint length = worklist.length();
|
uint length = worklist.length();
|
||||||
int iterations = 0;
|
int iterations = 0;
|
||||||
while(_progress && (iterations++ < CG_BUILD_ITER_LIMIT)) {
|
elapsedTimer time;
|
||||||
|
while(_progress &&
|
||||||
|
(iterations++ < CG_BUILD_ITER_LIMIT) &&
|
||||||
|
(time.seconds() < CG_BUILD_TIME_LIMIT)) {
|
||||||
|
time.start();
|
||||||
_progress = false;
|
_progress = false;
|
||||||
for( uint next = 0; next < length; ++next ) {
|
for( uint next = 0; next < length; ++next ) {
|
||||||
int ni = worklist.at(next);
|
int ni = worklist.at(next);
|
||||||
@ -1701,18 +1712,19 @@ bool ConnectionGraph::compute_escape() {
|
|||||||
assert(n != NULL, "should be known node");
|
assert(n != NULL, "should be known node");
|
||||||
build_connection_graph(n, igvn);
|
build_connection_graph(n, igvn);
|
||||||
}
|
}
|
||||||
|
time.stop();
|
||||||
}
|
}
|
||||||
if (iterations >= CG_BUILD_ITER_LIMIT) {
|
if ((iterations >= CG_BUILD_ITER_LIMIT) ||
|
||||||
assert(iterations < CG_BUILD_ITER_LIMIT,
|
(time.seconds() >= CG_BUILD_TIME_LIMIT)) {
|
||||||
err_msg("infinite EA connection graph build with %d nodes and worklist size %d",
|
assert(false, err_msg("infinite EA connection graph build (%f sec, %d iterations) with %d nodes and worklist size %d",
|
||||||
nodes_size(), length));
|
time.seconds(), iterations, nodes_size(), length));
|
||||||
// Possible infinite build_connection_graph loop,
|
// Possible infinite build_connection_graph loop,
|
||||||
// retry compilation without escape analysis.
|
// bailout (no changes to ideal graph were made).
|
||||||
C->record_failure(C2Compiler::retry_no_escape_analysis());
|
|
||||||
_collecting = false;
|
_collecting = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#undef CG_BUILD_ITER_LIMIT
|
#undef CG_BUILD_ITER_LIMIT
|
||||||
|
#undef CG_BUILD_TIME_LIMIT
|
||||||
|
|
||||||
// 5. Propagate escaped states.
|
// 5. Propagate escaped states.
|
||||||
worklist.clear();
|
worklist.clear();
|
||||||
@ -2292,9 +2304,35 @@ void ConnectionGraph::process_call_arguments(CallNode *call, PhaseTransform *pha
|
|||||||
PointsToNode::EscapeState arg_esc = ptnode_adr(arg->_idx)->escape_state();
|
PointsToNode::EscapeState arg_esc = ptnode_adr(arg->_idx)->escape_state();
|
||||||
if (!arg->is_top() && at->isa_ptr() && aat->isa_ptr() &&
|
if (!arg->is_top() && at->isa_ptr() && aat->isa_ptr() &&
|
||||||
(is_arraycopy || arg_esc < PointsToNode::ArgEscape)) {
|
(is_arraycopy || arg_esc < PointsToNode::ArgEscape)) {
|
||||||
|
#ifdef ASSERT
|
||||||
assert(aat == Type::TOP || aat == TypePtr::NULL_PTR ||
|
assert(aat == Type::TOP || aat == TypePtr::NULL_PTR ||
|
||||||
aat->isa_ptr() != NULL, "expecting an Ptr");
|
aat->isa_ptr() != NULL, "expecting an Ptr");
|
||||||
|
if (!(is_arraycopy ||
|
||||||
|
call->as_CallLeaf()->_name != NULL &&
|
||||||
|
(strcmp(call->as_CallLeaf()->_name, "g1_wb_pre") == 0 ||
|
||||||
|
strcmp(call->as_CallLeaf()->_name, "g1_wb_post") == 0 ))
|
||||||
|
) {
|
||||||
|
call->dump();
|
||||||
|
assert(false, "EA: unexpected CallLeaf");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (arg_esc < PointsToNode::ArgEscape) {
|
||||||
|
set_escape_state(arg->_idx, PointsToNode::ArgEscape);
|
||||||
|
Node* arg_base = arg;
|
||||||
|
if (arg->is_AddP()) {
|
||||||
|
//
|
||||||
|
// The inline_native_clone() case when the arraycopy stub is called
|
||||||
|
// after the allocation before Initialize and CheckCastPP nodes.
|
||||||
|
// Or normal arraycopy for object arrays case.
|
||||||
|
//
|
||||||
|
// Set AddP's base (Allocate) as not scalar replaceable since
|
||||||
|
// pointer to the base (with offset) is passed as argument.
|
||||||
|
//
|
||||||
|
arg_base = get_addp_base(arg);
|
||||||
|
set_escape_state(arg_base->_idx, PointsToNode::ArgEscape);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool arg_has_oops = aat->isa_oopptr() &&
|
bool arg_has_oops = aat->isa_oopptr() &&
|
||||||
(aat->isa_oopptr()->klass() == NULL || aat->isa_instptr() ||
|
(aat->isa_oopptr()->klass() == NULL || aat->isa_instptr() ||
|
||||||
(aat->isa_aryptr() && aat->isa_aryptr()->klass()->is_obj_array_klass()));
|
(aat->isa_aryptr() && aat->isa_aryptr()->klass()->is_obj_array_klass()));
|
||||||
@ -2307,85 +2345,33 @@ void ConnectionGraph::process_call_arguments(CallNode *call, PhaseTransform *pha
|
|||||||
// arraycopy(char[],0,Object*,0,size);
|
// arraycopy(char[],0,Object*,0,size);
|
||||||
// arraycopy(Object*,0,char[],0,size);
|
// arraycopy(Object*,0,char[],0,size);
|
||||||
//
|
//
|
||||||
// Don't add edges from dst's fields in such cases.
|
// Do nothing special in such cases.
|
||||||
//
|
//
|
||||||
bool arg_is_arraycopy_dest = src_has_oops && is_arraycopy &&
|
if (is_arraycopy && (i > TypeFunc::Parms) &&
|
||||||
arg_has_oops && (i > TypeFunc::Parms);
|
src_has_oops && arg_has_oops) {
|
||||||
#ifdef ASSERT
|
// Destination object's fields reference an unknown object.
|
||||||
if (!(is_arraycopy ||
|
Node* arg_base = arg;
|
||||||
call->as_CallLeaf()->_name != NULL &&
|
if (arg->is_AddP()) {
|
||||||
(strcmp(call->as_CallLeaf()->_name, "g1_wb_pre") == 0 ||
|
arg_base = get_addp_base(arg);
|
||||||
strcmp(call->as_CallLeaf()->_name, "g1_wb_post") == 0 ))
|
}
|
||||||
) {
|
for (VectorSetI s(PointsTo(arg_base)); s.test(); ++s) {
|
||||||
call->dump();
|
uint ps = s.elem;
|
||||||
assert(false, "EA: unexpected CallLeaf");
|
set_escape_state(ps, PointsToNode::ArgEscape);
|
||||||
}
|
add_edge_from_fields(ps, _phantom_object, Type::OffsetBot);
|
||||||
#endif
|
}
|
||||||
// Always process arraycopy's destination object since
|
// Conservatively all values in source object fields globally escape
|
||||||
// we need to add all possible edges to references in
|
// since we don't know if values in destination object fields
|
||||||
// source object.
|
// escape (it could be traced but it is too expensive).
|
||||||
if (arg_esc >= PointsToNode::ArgEscape &&
|
Node* src = call->in(TypeFunc::Parms)->uncast();
|
||||||
!arg_is_arraycopy_dest) {
|
Node* src_base = src;
|
||||||
continue;
|
if (src->is_AddP()) {
|
||||||
}
|
src_base = get_addp_base(src);
|
||||||
set_escape_state(arg->_idx, PointsToNode::ArgEscape);
|
}
|
||||||
Node* arg_base = arg;
|
for (VectorSetI s(PointsTo(src_base)); s.test(); ++s) {
|
||||||
if (arg->is_AddP()) {
|
uint ps = s.elem;
|
||||||
//
|
set_escape_state(ps, PointsToNode::ArgEscape);
|
||||||
// The inline_native_clone() case when the arraycopy stub is called
|
// Use OffsetTop to indicate fields global escape.
|
||||||
// after the allocation before Initialize and CheckCastPP nodes.
|
add_edge_from_fields(ps, _phantom_object, Type::OffsetTop);
|
||||||
// Or normal arraycopy for object arrays case.
|
|
||||||
//
|
|
||||||
// Set AddP's base (Allocate) as not scalar replaceable since
|
|
||||||
// pointer to the base (with offset) is passed as argument.
|
|
||||||
//
|
|
||||||
arg_base = get_addp_base(arg);
|
|
||||||
}
|
|
||||||
VectorSet argset = *PointsTo(arg_base); // Clone set
|
|
||||||
for( VectorSetI j(&argset); j.test(); ++j ) {
|
|
||||||
uint pd = j.elem; // Destination object
|
|
||||||
set_escape_state(pd, PointsToNode::ArgEscape);
|
|
||||||
|
|
||||||
if (arg_is_arraycopy_dest) {
|
|
||||||
PointsToNode* ptd = ptnode_adr(pd);
|
|
||||||
// Conservatively reference an unknown object since
|
|
||||||
// not all source's fields/elements may be known.
|
|
||||||
add_edge_from_fields(pd, _phantom_object, Type::OffsetBot);
|
|
||||||
|
|
||||||
Node *src = call->in(TypeFunc::Parms)->uncast();
|
|
||||||
Node* src_base = src;
|
|
||||||
if (src->is_AddP()) {
|
|
||||||
src_base = get_addp_base(src);
|
|
||||||
}
|
|
||||||
// Create edges from destination's fields to
|
|
||||||
// everything known source's fields could point to.
|
|
||||||
for( VectorSetI s(PointsTo(src_base)); s.test(); ++s ) {
|
|
||||||
uint ps = s.elem;
|
|
||||||
bool has_bottom_offset = false;
|
|
||||||
for (uint fd = 0; fd < ptd->edge_count(); fd++) {
|
|
||||||
assert(ptd->edge_type(fd) == PointsToNode::FieldEdge, "expecting a field edge");
|
|
||||||
int fdi = ptd->edge_target(fd);
|
|
||||||
PointsToNode* pfd = ptnode_adr(fdi);
|
|
||||||
int offset = pfd->offset();
|
|
||||||
if (offset == Type::OffsetBot)
|
|
||||||
has_bottom_offset = true;
|
|
||||||
assert(offset != -1, "offset should be set");
|
|
||||||
add_deferred_edge_to_fields(fdi, ps, offset);
|
|
||||||
}
|
|
||||||
// Destination object may not have access (no field edge)
|
|
||||||
// to fields which are accessed in source object.
|
|
||||||
// As result no edges will be created to those source's
|
|
||||||
// fields and escape state of destination object will
|
|
||||||
// not be propagated to those fields.
|
|
||||||
//
|
|
||||||
// Mark source object as global escape except in
|
|
||||||
// the case with Type::OffsetBot field (which is
|
|
||||||
// common case for array elements access) when
|
|
||||||
// edges are created to all source's fields.
|
|
||||||
if (!has_bottom_offset) {
|
|
||||||
set_escape_state(ps, PointsToNode::GlobalEscape);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user