# HG changeset patch # User kvn # Date 1328907223 28800 # Node ID 73df3733f2eba06a91130e825b2de24256e58378 # Parent 2f985b6ce7ffdb7f8a99510d1c9d43315bc336b3 7129284: +DoEscapeAnalysis regression w/ early build of 7u4 (HotSpot 23) on Linux Summary: 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 diff -r 2f985b6ce7ff -r 73df3733f2eb src/share/vm/opto/escape.cpp --- a/src/share/vm/opto/escape.cpp Thu Feb 09 18:01:20 2012 -0800 +++ b/src/share/vm/opto/escape.cpp Fri Feb 10 12:53:43 2012 -0800 @@ -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. * * This code is free software; you can redistribute it and/or modify it @@ -1687,12 +1687,23 @@ // Observed 8 passes in jvm2008 compiler.compiler. // Set limit to 20 to catch situation when something // 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 +#ifdef ASSERT +#define CG_BUILD_TIME_LIMIT 60.0 +#else +#define CG_BUILD_TIME_LIMIT 30.0 +#endif + uint length = worklist.length(); 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; for( uint next = 0; next < length; ++next ) { int ni = worklist.at(next); @@ -1701,18 +1712,19 @@ assert(n != NULL, "should be known node"); build_connection_graph(n, igvn); } + time.stop(); } - if (iterations >= CG_BUILD_ITER_LIMIT) { - assert(iterations < CG_BUILD_ITER_LIMIT, - err_msg("infinite EA connection graph build with %d nodes and worklist size %d", - nodes_size(), length)); + if ((iterations >= CG_BUILD_ITER_LIMIT) || + (time.seconds() >= CG_BUILD_TIME_LIMIT)) { + assert(false, err_msg("infinite EA connection graph build (%f sec, %d iterations) with %d nodes and worklist size %d", + time.seconds(), iterations, nodes_size(), length)); // Possible infinite build_connection_graph loop, - // retry compilation without escape analysis. - C->record_failure(C2Compiler::retry_no_escape_analysis()); + // bailout (no changes to ideal graph were made). _collecting = false; return false; } #undef CG_BUILD_ITER_LIMIT +#undef CG_BUILD_TIME_LIMIT // 5. Propagate escaped states. worklist.clear(); @@ -2292,9 +2304,35 @@ PointsToNode::EscapeState arg_esc = ptnode_adr(arg->_idx)->escape_state(); if (!arg->is_top() && at->isa_ptr() && aat->isa_ptr() && (is_arraycopy || arg_esc < PointsToNode::ArgEscape)) { - +#ifdef ASSERT assert(aat == Type::TOP || aat == TypePtr::NULL_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() && (aat->isa_oopptr()->klass() == NULL || aat->isa_instptr() || (aat->isa_aryptr() && aat->isa_aryptr()->klass()->is_obj_array_klass())); @@ -2307,85 +2345,33 @@ // arraycopy(char[],0,Object*,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 && - arg_has_oops && (i > TypeFunc::Parms); -#ifdef ASSERT - 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 - // Always process arraycopy's destination object since - // we need to add all possible edges to references in - // source object. - if (arg_esc >= PointsToNode::ArgEscape && - !arg_is_arraycopy_dest) { - continue; - } - 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); - } - 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); - } - } + if (is_arraycopy && (i > TypeFunc::Parms) && + src_has_oops && arg_has_oops) { + // Destination object's fields reference an unknown object. + Node* arg_base = arg; + if (arg->is_AddP()) { + arg_base = get_addp_base(arg); + } + for (VectorSetI s(PointsTo(arg_base)); s.test(); ++s) { + uint ps = s.elem; + set_escape_state(ps, PointsToNode::ArgEscape); + add_edge_from_fields(ps, _phantom_object, Type::OffsetBot); + } + // Conservatively all values in source object fields globally escape + // since we don't know if values in destination object fields + // escape (it could be traced but it is too expensive). + Node* src = call->in(TypeFunc::Parms)->uncast(); + Node* src_base = src; + if (src->is_AddP()) { + src_base = get_addp_base(src); + } + for (VectorSetI s(PointsTo(src_base)); s.test(); ++s) { + uint ps = s.elem; + set_escape_state(ps, PointsToNode::ArgEscape); + // Use OffsetTop to indicate fields global escape. + add_edge_from_fields(ps, _phantom_object, Type::OffsetTop); } } }