Mercurial > hg > graal-jvmci-8
comparison src/share/vm/opto/escape.cpp @ 4937:73df3733f2eb
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
author | kvn |
---|---|
date | Fri, 10 Feb 2012 12:53:43 -0800 |
parents | e9a5e0a812c8 |
children | 9a72c7ece7fb |
comparison
equal
deleted
inserted
replaced
4896:2f985b6ce7ff | 4937:73df3733f2eb |
---|---|
1 /* | 1 /* |
2 * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. | 2 * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 * | 4 * |
5 * This code is free software; you can redistribute it and/or modify it | 5 * This code is free software; you can redistribute it and/or modify it |
6 * under the terms of the GNU General Public License version 2 only, as | 6 * under the terms of the GNU General Public License version 2 only, as |
7 * published by the Free Software Foundation. | 7 * published by the Free Software Foundation. |
1685 // Normally only 1-3 passes needed to build | 1685 // Normally only 1-3 passes needed to build |
1686 // Connection Graph depending on graph complexity. | 1686 // Connection Graph depending on graph complexity. |
1687 // Observed 8 passes in jvm2008 compiler.compiler. | 1687 // Observed 8 passes in jvm2008 compiler.compiler. |
1688 // Set limit to 20 to catch situation when something | 1688 // Set limit to 20 to catch situation when something |
1689 // did go wrong and recompile the method without EA. | 1689 // did go wrong and recompile the method without EA. |
1690 // Also limit build time to 30 sec (60 in debug VM). | |
1690 | 1691 |
1691 #define CG_BUILD_ITER_LIMIT 20 | 1692 #define CG_BUILD_ITER_LIMIT 20 |
1693 | |
1694 #ifdef ASSERT | |
1695 #define CG_BUILD_TIME_LIMIT 60.0 | |
1696 #else | |
1697 #define CG_BUILD_TIME_LIMIT 30.0 | |
1698 #endif | |
1692 | 1699 |
1693 uint length = worklist.length(); | 1700 uint length = worklist.length(); |
1694 int iterations = 0; | 1701 int iterations = 0; |
1695 while(_progress && (iterations++ < CG_BUILD_ITER_LIMIT)) { | 1702 elapsedTimer time; |
1703 while(_progress && | |
1704 (iterations++ < CG_BUILD_ITER_LIMIT) && | |
1705 (time.seconds() < CG_BUILD_TIME_LIMIT)) { | |
1706 time.start(); | |
1696 _progress = false; | 1707 _progress = false; |
1697 for( uint next = 0; next < length; ++next ) { | 1708 for( uint next = 0; next < length; ++next ) { |
1698 int ni = worklist.at(next); | 1709 int ni = worklist.at(next); |
1699 PointsToNode* ptn = ptnode_adr(ni); | 1710 PointsToNode* ptn = ptnode_adr(ni); |
1700 Node* n = ptn->_node; | 1711 Node* n = ptn->_node; |
1701 assert(n != NULL, "should be known node"); | 1712 assert(n != NULL, "should be known node"); |
1702 build_connection_graph(n, igvn); | 1713 build_connection_graph(n, igvn); |
1703 } | 1714 } |
1704 } | 1715 time.stop(); |
1705 if (iterations >= CG_BUILD_ITER_LIMIT) { | 1716 } |
1706 assert(iterations < CG_BUILD_ITER_LIMIT, | 1717 if ((iterations >= CG_BUILD_ITER_LIMIT) || |
1707 err_msg("infinite EA connection graph build with %d nodes and worklist size %d", | 1718 (time.seconds() >= CG_BUILD_TIME_LIMIT)) { |
1708 nodes_size(), length)); | 1719 assert(false, err_msg("infinite EA connection graph build (%f sec, %d iterations) with %d nodes and worklist size %d", |
1720 time.seconds(), iterations, nodes_size(), length)); | |
1709 // Possible infinite build_connection_graph loop, | 1721 // Possible infinite build_connection_graph loop, |
1710 // retry compilation without escape analysis. | 1722 // bailout (no changes to ideal graph were made). |
1711 C->record_failure(C2Compiler::retry_no_escape_analysis()); | |
1712 _collecting = false; | 1723 _collecting = false; |
1713 return false; | 1724 return false; |
1714 } | 1725 } |
1715 #undef CG_BUILD_ITER_LIMIT | 1726 #undef CG_BUILD_ITER_LIMIT |
1727 #undef CG_BUILD_TIME_LIMIT | |
1716 | 1728 |
1717 // 5. Propagate escaped states. | 1729 // 5. Propagate escaped states. |
1718 worklist.clear(); | 1730 worklist.clear(); |
1719 | 1731 |
1720 // mark all nodes reachable from GlobalEscape nodes | 1732 // mark all nodes reachable from GlobalEscape nodes |
2290 Node *arg = call->in(i)->uncast(); | 2302 Node *arg = call->in(i)->uncast(); |
2291 const Type *aat = phase->type(arg); | 2303 const Type *aat = phase->type(arg); |
2292 PointsToNode::EscapeState arg_esc = ptnode_adr(arg->_idx)->escape_state(); | 2304 PointsToNode::EscapeState arg_esc = ptnode_adr(arg->_idx)->escape_state(); |
2293 if (!arg->is_top() && at->isa_ptr() && aat->isa_ptr() && | 2305 if (!arg->is_top() && at->isa_ptr() && aat->isa_ptr() && |
2294 (is_arraycopy || arg_esc < PointsToNode::ArgEscape)) { | 2306 (is_arraycopy || arg_esc < PointsToNode::ArgEscape)) { |
2295 | 2307 #ifdef ASSERT |
2296 assert(aat == Type::TOP || aat == TypePtr::NULL_PTR || | 2308 assert(aat == Type::TOP || aat == TypePtr::NULL_PTR || |
2297 aat->isa_ptr() != NULL, "expecting an Ptr"); | 2309 aat->isa_ptr() != NULL, "expecting an Ptr"); |
2310 if (!(is_arraycopy || | |
2311 call->as_CallLeaf()->_name != NULL && | |
2312 (strcmp(call->as_CallLeaf()->_name, "g1_wb_pre") == 0 || | |
2313 strcmp(call->as_CallLeaf()->_name, "g1_wb_post") == 0 )) | |
2314 ) { | |
2315 call->dump(); | |
2316 assert(false, "EA: unexpected CallLeaf"); | |
2317 } | |
2318 #endif | |
2319 if (arg_esc < PointsToNode::ArgEscape) { | |
2320 set_escape_state(arg->_idx, PointsToNode::ArgEscape); | |
2321 Node* arg_base = arg; | |
2322 if (arg->is_AddP()) { | |
2323 // | |
2324 // The inline_native_clone() case when the arraycopy stub is called | |
2325 // after the allocation before Initialize and CheckCastPP nodes. | |
2326 // Or normal arraycopy for object arrays case. | |
2327 // | |
2328 // Set AddP's base (Allocate) as not scalar replaceable since | |
2329 // pointer to the base (with offset) is passed as argument. | |
2330 // | |
2331 arg_base = get_addp_base(arg); | |
2332 set_escape_state(arg_base->_idx, PointsToNode::ArgEscape); | |
2333 } | |
2334 } | |
2335 | |
2298 bool arg_has_oops = aat->isa_oopptr() && | 2336 bool arg_has_oops = aat->isa_oopptr() && |
2299 (aat->isa_oopptr()->klass() == NULL || aat->isa_instptr() || | 2337 (aat->isa_oopptr()->klass() == NULL || aat->isa_instptr() || |
2300 (aat->isa_aryptr() && aat->isa_aryptr()->klass()->is_obj_array_klass())); | 2338 (aat->isa_aryptr() && aat->isa_aryptr()->klass()->is_obj_array_klass())); |
2301 if (i == TypeFunc::Parms) { | 2339 if (i == TypeFunc::Parms) { |
2302 src_has_oops = arg_has_oops; | 2340 src_has_oops = arg_has_oops; |
2305 // src or dst could be j.l.Object when other is basic type array: | 2343 // src or dst could be j.l.Object when other is basic type array: |
2306 // | 2344 // |
2307 // arraycopy(char[],0,Object*,0,size); | 2345 // arraycopy(char[],0,Object*,0,size); |
2308 // arraycopy(Object*,0,char[],0,size); | 2346 // arraycopy(Object*,0,char[],0,size); |
2309 // | 2347 // |
2310 // Don't add edges from dst's fields in such cases. | 2348 // Do nothing special in such cases. |
2311 // | 2349 // |
2312 bool arg_is_arraycopy_dest = src_has_oops && is_arraycopy && | 2350 if (is_arraycopy && (i > TypeFunc::Parms) && |
2313 arg_has_oops && (i > TypeFunc::Parms); | 2351 src_has_oops && arg_has_oops) { |
2314 #ifdef ASSERT | 2352 // Destination object's fields reference an unknown object. |
2315 if (!(is_arraycopy || | 2353 Node* arg_base = arg; |
2316 call->as_CallLeaf()->_name != NULL && | 2354 if (arg->is_AddP()) { |
2317 (strcmp(call->as_CallLeaf()->_name, "g1_wb_pre") == 0 || | 2355 arg_base = get_addp_base(arg); |
2318 strcmp(call->as_CallLeaf()->_name, "g1_wb_post") == 0 )) | 2356 } |
2319 ) { | 2357 for (VectorSetI s(PointsTo(arg_base)); s.test(); ++s) { |
2320 call->dump(); | 2358 uint ps = s.elem; |
2321 assert(false, "EA: unexpected CallLeaf"); | 2359 set_escape_state(ps, PointsToNode::ArgEscape); |
2322 } | 2360 add_edge_from_fields(ps, _phantom_object, Type::OffsetBot); |
2323 #endif | 2361 } |
2324 // Always process arraycopy's destination object since | 2362 // Conservatively all values in source object fields globally escape |
2325 // we need to add all possible edges to references in | 2363 // since we don't know if values in destination object fields |
2326 // source object. | 2364 // escape (it could be traced but it is too expensive). |
2327 if (arg_esc >= PointsToNode::ArgEscape && | 2365 Node* src = call->in(TypeFunc::Parms)->uncast(); |
2328 !arg_is_arraycopy_dest) { | 2366 Node* src_base = src; |
2329 continue; | 2367 if (src->is_AddP()) { |
2330 } | 2368 src_base = get_addp_base(src); |
2331 set_escape_state(arg->_idx, PointsToNode::ArgEscape); | 2369 } |
2332 Node* arg_base = arg; | 2370 for (VectorSetI s(PointsTo(src_base)); s.test(); ++s) { |
2333 if (arg->is_AddP()) { | 2371 uint ps = s.elem; |
2334 // | 2372 set_escape_state(ps, PointsToNode::ArgEscape); |
2335 // The inline_native_clone() case when the arraycopy stub is called | 2373 // Use OffsetTop to indicate fields global escape. |
2336 // after the allocation before Initialize and CheckCastPP nodes. | 2374 add_edge_from_fields(ps, _phantom_object, Type::OffsetTop); |
2337 // Or normal arraycopy for object arrays case. | |
2338 // | |
2339 // Set AddP's base (Allocate) as not scalar replaceable since | |
2340 // pointer to the base (with offset) is passed as argument. | |
2341 // | |
2342 arg_base = get_addp_base(arg); | |
2343 } | |
2344 VectorSet argset = *PointsTo(arg_base); // Clone set | |
2345 for( VectorSetI j(&argset); j.test(); ++j ) { | |
2346 uint pd = j.elem; // Destination object | |
2347 set_escape_state(pd, PointsToNode::ArgEscape); | |
2348 | |
2349 if (arg_is_arraycopy_dest) { | |
2350 PointsToNode* ptd = ptnode_adr(pd); | |
2351 // Conservatively reference an unknown object since | |
2352 // not all source's fields/elements may be known. | |
2353 add_edge_from_fields(pd, _phantom_object, Type::OffsetBot); | |
2354 | |
2355 Node *src = call->in(TypeFunc::Parms)->uncast(); | |
2356 Node* src_base = src; | |
2357 if (src->is_AddP()) { | |
2358 src_base = get_addp_base(src); | |
2359 } | |
2360 // Create edges from destination's fields to | |
2361 // everything known source's fields could point to. | |
2362 for( VectorSetI s(PointsTo(src_base)); s.test(); ++s ) { | |
2363 uint ps = s.elem; | |
2364 bool has_bottom_offset = false; | |
2365 for (uint fd = 0; fd < ptd->edge_count(); fd++) { | |
2366 assert(ptd->edge_type(fd) == PointsToNode::FieldEdge, "expecting a field edge"); | |
2367 int fdi = ptd->edge_target(fd); | |
2368 PointsToNode* pfd = ptnode_adr(fdi); | |
2369 int offset = pfd->offset(); | |
2370 if (offset == Type::OffsetBot) | |
2371 has_bottom_offset = true; | |
2372 assert(offset != -1, "offset should be set"); | |
2373 add_deferred_edge_to_fields(fdi, ps, offset); | |
2374 } | |
2375 // Destination object may not have access (no field edge) | |
2376 // to fields which are accessed in source object. | |
2377 // As result no edges will be created to those source's | |
2378 // fields and escape state of destination object will | |
2379 // not be propagated to those fields. | |
2380 // | |
2381 // Mark source object as global escape except in | |
2382 // the case with Type::OffsetBot field (which is | |
2383 // common case for array elements access) when | |
2384 // edges are created to all source's fields. | |
2385 if (!has_bottom_offset) { | |
2386 set_escape_state(ps, PointsToNode::GlobalEscape); | |
2387 } | |
2388 } | |
2389 } | 2375 } |
2390 } | 2376 } |
2391 } | 2377 } |
2392 } | 2378 } |
2393 break; | 2379 break; |