| File: | jdk/src/hotspot/share/opto/escape.cpp |
| Warning: | line 2579, column 7 Value stored to 'alias_idx' during its initialization is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* |
| 2 | * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. |
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| 4 | * |
| 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 |
| 7 | * published by the Free Software Foundation. |
| 8 | * |
| 9 | * This code is distributed in the hope that it will be useful, but WITHOUT |
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 12 | * version 2 for more details (a copy is included in the LICENSE file that |
| 13 | * accompanied this code). |
| 14 | * |
| 15 | * You should have received a copy of the GNU General Public License version |
| 16 | * 2 along with this work; if not, write to the Free Software Foundation, |
| 17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| 18 | * |
| 19 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| 20 | * or visit www.oracle.com if you need additional information or have any |
| 21 | * questions. |
| 22 | * |
| 23 | */ |
| 24 | |
| 25 | #include "precompiled.hpp" |
| 26 | #include "ci/bcEscapeAnalyzer.hpp" |
| 27 | #include "compiler/compileLog.hpp" |
| 28 | #include "gc/shared/barrierSet.hpp" |
| 29 | #include "gc/shared/c2/barrierSetC2.hpp" |
| 30 | #include "libadt/vectset.hpp" |
| 31 | #include "memory/allocation.hpp" |
| 32 | #include "memory/resourceArea.hpp" |
| 33 | #include "opto/c2compiler.hpp" |
| 34 | #include "opto/arraycopynode.hpp" |
| 35 | #include "opto/callnode.hpp" |
| 36 | #include "opto/cfgnode.hpp" |
| 37 | #include "opto/compile.hpp" |
| 38 | #include "opto/escape.hpp" |
| 39 | #include "opto/phaseX.hpp" |
| 40 | #include "opto/movenode.hpp" |
| 41 | #include "opto/rootnode.hpp" |
| 42 | #include "utilities/macros.hpp" |
| 43 | |
| 44 | ConnectionGraph::ConnectionGraph(Compile * C, PhaseIterGVN *igvn, int invocation) : |
| 45 | _nodes(C->comp_arena(), C->unique(), C->unique(), NULL__null), |
| 46 | _in_worklist(C->comp_arena()), |
| 47 | _next_pidx(0), |
| 48 | _collecting(true), |
| 49 | _verify(false), |
| 50 | _compile(C), |
| 51 | _igvn(igvn), |
| 52 | _invocation(invocation), |
| 53 | _build_iterations(0), |
| 54 | _build_time(0.), |
| 55 | _node_map(C->comp_arena()) { |
| 56 | // Add unknown java object. |
| 57 | add_java_object(C->top(), PointsToNode::GlobalEscape); |
| 58 | phantom_obj = ptnode_adr(C->top()->_idx)->as_JavaObject(); |
| 59 | // Add ConP(#NULL) and ConN(#NULL) nodes. |
| 60 | Node* oop_null = igvn->zerocon(T_OBJECT); |
| 61 | assert(oop_null->_idx < nodes_size(), "should be created already")do { if (!(oop_null->_idx < nodes_size())) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 61, "assert(" "oop_null->_idx < nodes_size()" ") failed" , "should be created already"); ::breakpoint(); } } while (0); |
| 62 | add_java_object(oop_null, PointsToNode::NoEscape); |
| 63 | null_obj = ptnode_adr(oop_null->_idx)->as_JavaObject(); |
| 64 | if (UseCompressedOops) { |
| 65 | Node* noop_null = igvn->zerocon(T_NARROWOOP); |
| 66 | assert(noop_null->_idx < nodes_size(), "should be created already")do { if (!(noop_null->_idx < nodes_size())) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 66, "assert(" "noop_null->_idx < nodes_size()" ") failed" , "should be created already"); ::breakpoint(); } } while (0); |
| 67 | map_ideal_node(noop_null, null_obj); |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | bool ConnectionGraph::has_candidates(Compile *C) { |
| 72 | // EA brings benefits only when the code has allocations and/or locks which |
| 73 | // are represented by ideal Macro nodes. |
| 74 | int cnt = C->macro_count(); |
| 75 | for (int i = 0; i < cnt; i++) { |
| 76 | Node *n = C->macro_node(i); |
| 77 | if (n->is_Allocate()) { |
| 78 | return true; |
| 79 | } |
| 80 | if (n->is_Lock()) { |
| 81 | Node* obj = n->as_Lock()->obj_node()->uncast(); |
| 82 | if (!(obj->is_Parm() || obj->is_Con())) { |
| 83 | return true; |
| 84 | } |
| 85 | } |
| 86 | if (n->is_CallStaticJava() && |
| 87 | n->as_CallStaticJava()->is_boxing_method()) { |
| 88 | return true; |
| 89 | } |
| 90 | } |
| 91 | return false; |
| 92 | } |
| 93 | |
| 94 | void ConnectionGraph::do_analysis(Compile *C, PhaseIterGVN *igvn) { |
| 95 | Compile::TracePhase tp("escapeAnalysis", &Phase::timers[Phase::_t_escapeAnalysis]); |
| 96 | ResourceMark rm; |
| 97 | |
| 98 | // Add ConP#NULL and ConN#NULL nodes before ConnectionGraph construction |
| 99 | // to create space for them in ConnectionGraph::_nodes[]. |
| 100 | Node* oop_null = igvn->zerocon(T_OBJECT); |
| 101 | Node* noop_null = igvn->zerocon(T_NARROWOOP); |
| 102 | int invocation = 0; |
| 103 | if (C->congraph() != NULL__null) { |
| 104 | invocation = C->congraph()->_invocation + 1; |
| 105 | } |
| 106 | ConnectionGraph* congraph = new(C->comp_arena()) ConnectionGraph(C, igvn, invocation); |
| 107 | // Perform escape analysis |
| 108 | if (congraph->compute_escape()) { |
| 109 | // There are non escaping objects. |
| 110 | C->set_congraph(congraph); |
| 111 | } |
| 112 | // Cleanup. |
| 113 | if (oop_null->outcnt() == 0) { |
| 114 | igvn->hash_delete(oop_null); |
| 115 | } |
| 116 | if (noop_null->outcnt() == 0) { |
| 117 | igvn->hash_delete(noop_null); |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | bool ConnectionGraph::compute_escape() { |
| 122 | Compile* C = _compile; |
| 123 | PhaseGVN* igvn = _igvn; |
| 124 | |
| 125 | // Worklists used by EA. |
| 126 | Unique_Node_List delayed_worklist; |
| 127 | GrowableArray<Node*> alloc_worklist; |
| 128 | GrowableArray<Node*> ptr_cmp_worklist; |
| 129 | GrowableArray<MemBarStoreStoreNode*> storestore_worklist; |
| 130 | GrowableArray<ArrayCopyNode*> arraycopy_worklist; |
| 131 | GrowableArray<PointsToNode*> ptnodes_worklist; |
| 132 | GrowableArray<JavaObjectNode*> java_objects_worklist; |
| 133 | GrowableArray<JavaObjectNode*> non_escaped_allocs_worklist; |
| 134 | GrowableArray<FieldNode*> oop_fields_worklist; |
| 135 | GrowableArray<SafePointNode*> sfn_worklist; |
| 136 | GrowableArray<MergeMemNode*> mergemem_worklist; |
| 137 | DEBUG_ONLY( GrowableArray<Node*> addp_worklist; )GrowableArray<Node*> addp_worklist; |
| 138 | |
| 139 | { Compile::TracePhase tp("connectionGraph", &Phase::timers[Phase::_t_connectionGraph]); |
| 140 | |
| 141 | // 1. Populate Connection Graph (CG) with PointsTo nodes. |
| 142 | ideal_nodes.map(C->live_nodes(), NULL__null); // preallocate space |
| 143 | // Initialize worklist |
| 144 | if (C->root() != NULL__null) { |
| 145 | ideal_nodes.push(C->root()); |
| 146 | } |
| 147 | // Processed ideal nodes are unique on ideal_nodes list |
| 148 | // but several ideal nodes are mapped to the phantom_obj. |
| 149 | // To avoid duplicated entries on the following worklists |
| 150 | // add the phantom_obj only once to them. |
| 151 | ptnodes_worklist.append(phantom_obj); |
| 152 | java_objects_worklist.append(phantom_obj); |
| 153 | for( uint next = 0; next < ideal_nodes.size(); ++next ) { |
| 154 | Node* n = ideal_nodes.at(next); |
| 155 | // Create PointsTo nodes and add them to Connection Graph. Called |
| 156 | // only once per ideal node since ideal_nodes is Unique_Node list. |
| 157 | add_node_to_connection_graph(n, &delayed_worklist); |
| 158 | PointsToNode* ptn = ptnode_adr(n->_idx); |
| 159 | if (ptn != NULL__null && ptn != phantom_obj) { |
| 160 | ptnodes_worklist.append(ptn); |
| 161 | if (ptn->is_JavaObject()) { |
| 162 | java_objects_worklist.append(ptn->as_JavaObject()); |
| 163 | if ((n->is_Allocate() || n->is_CallStaticJava()) && |
| 164 | (ptn->escape_state() < PointsToNode::GlobalEscape)) { |
| 165 | // Only allocations and java static calls results are interesting. |
| 166 | non_escaped_allocs_worklist.append(ptn->as_JavaObject()); |
| 167 | } |
| 168 | } else if (ptn->is_Field() && ptn->as_Field()->is_oop()) { |
| 169 | oop_fields_worklist.append(ptn->as_Field()); |
| 170 | } |
| 171 | } |
| 172 | // Collect some interesting nodes for futher use. |
| 173 | switch (n->Opcode()) { |
| 174 | case Op_MergeMem: |
| 175 | // Collect all MergeMem nodes to add memory slices for |
| 176 | // scalar replaceable objects in split_unique_types(). |
| 177 | mergemem_worklist.append(n->as_MergeMem()); |
| 178 | break; |
| 179 | case Op_CmpP: |
| 180 | case Op_CmpN: |
| 181 | // Collect compare pointers nodes. |
| 182 | if (OptimizePtrCompare) { |
| 183 | ptr_cmp_worklist.append(n); |
| 184 | } |
| 185 | break; |
| 186 | case Op_MemBarStoreStore: |
| 187 | // Collect all MemBarStoreStore nodes so that depending on the |
| 188 | // escape status of the associated Allocate node some of them |
| 189 | // may be eliminated. |
| 190 | storestore_worklist.append(n->as_MemBarStoreStore()); |
| 191 | break; |
| 192 | case Op_MemBarRelease: |
| 193 | if (n->req() > MemBarNode::Precedent) { |
| 194 | record_for_optimizer(n); |
| 195 | } |
| 196 | break; |
| 197 | #ifdef ASSERT1 |
| 198 | case Op_AddP: |
| 199 | // Collect address nodes for graph verification. |
| 200 | addp_worklist.append(n); |
| 201 | break; |
| 202 | #endif |
| 203 | case Op_ArrayCopy: |
| 204 | // Keep a list of ArrayCopy nodes so if one of its input is non |
| 205 | // escaping, we can record a unique type |
| 206 | arraycopy_worklist.append(n->as_ArrayCopy()); |
| 207 | break; |
| 208 | default: |
| 209 | // not interested now, ignore... |
| 210 | break; |
| 211 | } |
| 212 | for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { |
| 213 | Node* m = n->fast_out(i); // Get user |
| 214 | ideal_nodes.push(m); |
| 215 | } |
| 216 | if (n->is_SafePoint()) { |
| 217 | sfn_worklist.append(n->as_SafePoint()); |
| 218 | } |
| 219 | } |
| 220 | if (non_escaped_allocs_worklist.length() == 0) { |
| 221 | _collecting = false; |
| 222 | return false; // Nothing to do. |
| 223 | } |
| 224 | // Add final simple edges to graph. |
| 225 | while(delayed_worklist.size() > 0) { |
| 226 | Node* n = delayed_worklist.pop(); |
| 227 | add_final_edges(n); |
| 228 | } |
| 229 | |
| 230 | #ifdef ASSERT1 |
| 231 | if (VerifyConnectionGraph) { |
| 232 | // Verify that no new simple edges could be created and all |
| 233 | // local vars has edges. |
| 234 | _verify = true; |
| 235 | int ptnodes_length = ptnodes_worklist.length(); |
| 236 | for (int next = 0; next < ptnodes_length; ++next) { |
| 237 | PointsToNode* ptn = ptnodes_worklist.at(next); |
| 238 | add_final_edges(ptn->ideal_node()); |
| 239 | if (ptn->is_LocalVar() && ptn->edge_count() == 0) { |
| 240 | ptn->dump(); |
| 241 | assert(ptn->as_LocalVar()->edge_count() > 0, "sanity")do { if (!(ptn->as_LocalVar()->edge_count() > 0)) { ( *g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 241, "assert(" "ptn->as_LocalVar()->edge_count() > 0" ") failed", "sanity"); ::breakpoint(); } } while (0); |
| 242 | } |
| 243 | } |
| 244 | _verify = false; |
| 245 | } |
| 246 | #endif |
| 247 | // Bytecode analyzer BCEscapeAnalyzer, used for Call nodes |
| 248 | // processing, calls to CI to resolve symbols (types, fields, methods) |
| 249 | // referenced in bytecode. During symbol resolution VM may throw |
| 250 | // an exception which CI cleans and converts to compilation failure. |
| 251 | if (C->failing()) return false; |
| 252 | |
| 253 | // 2. Finish Graph construction by propagating references to all |
| 254 | // java objects through graph. |
| 255 | if (!complete_connection_graph(ptnodes_worklist, non_escaped_allocs_worklist, |
| 256 | java_objects_worklist, oop_fields_worklist)) { |
| 257 | // All objects escaped or hit time or iterations limits. |
| 258 | _collecting = false; |
| 259 | return false; |
| 260 | } |
| 261 | |
| 262 | // 3. Adjust scalar_replaceable state of nonescaping objects and push |
| 263 | // scalar replaceable allocations on alloc_worklist for processing |
| 264 | // in split_unique_types(). |
| 265 | int non_escaped_length = non_escaped_allocs_worklist.length(); |
| 266 | for (int next = 0; next < non_escaped_length; next++) { |
| 267 | JavaObjectNode* ptn = non_escaped_allocs_worklist.at(next); |
| 268 | bool noescape = (ptn->escape_state() == PointsToNode::NoEscape); |
| 269 | Node* n = ptn->ideal_node(); |
| 270 | if (n->is_Allocate()) { |
| 271 | n->as_Allocate()->_is_non_escaping = noescape; |
| 272 | } |
| 273 | if (noescape && ptn->scalar_replaceable()) { |
| 274 | adjust_scalar_replaceable_state(ptn); |
| 275 | if (ptn->scalar_replaceable()) { |
| 276 | alloc_worklist.append(ptn->ideal_node()); |
| 277 | } |
| 278 | } |
| 279 | } |
| 280 | |
| 281 | #ifdef ASSERT1 |
| 282 | if (VerifyConnectionGraph) { |
| 283 | // Verify that graph is complete - no new edges could be added or needed. |
| 284 | verify_connection_graph(ptnodes_worklist, non_escaped_allocs_worklist, |
| 285 | java_objects_worklist, addp_worklist); |
| 286 | } |
| 287 | assert(C->unique() == nodes_size(), "no new ideal nodes should be added during ConnectionGraph build")do { if (!(C->unique() == nodes_size())) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 287, "assert(" "C->unique() == nodes_size()" ") failed", "no new ideal nodes should be added during ConnectionGraph build" ); ::breakpoint(); } } while (0); |
| 288 | assert(null_obj->escape_state() == PointsToNode::NoEscape &&do { if (!(null_obj->escape_state() == PointsToNode::NoEscape && null_obj->edge_count() == 0 && !null_obj ->arraycopy_src() && !null_obj->arraycopy_dst() )) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 291, "assert(" "null_obj->escape_state() == PointsToNode::NoEscape && null_obj->edge_count() == 0 && !null_obj->arraycopy_src() && !null_obj->arraycopy_dst()" ") failed", "sanity"); ::breakpoint(); } } while (0) |
| 289 | null_obj->edge_count() == 0 &&do { if (!(null_obj->escape_state() == PointsToNode::NoEscape && null_obj->edge_count() == 0 && !null_obj ->arraycopy_src() && !null_obj->arraycopy_dst() )) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 291, "assert(" "null_obj->escape_state() == PointsToNode::NoEscape && null_obj->edge_count() == 0 && !null_obj->arraycopy_src() && !null_obj->arraycopy_dst()" ") failed", "sanity"); ::breakpoint(); } } while (0) |
| 290 | !null_obj->arraycopy_src() &&do { if (!(null_obj->escape_state() == PointsToNode::NoEscape && null_obj->edge_count() == 0 && !null_obj ->arraycopy_src() && !null_obj->arraycopy_dst() )) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 291, "assert(" "null_obj->escape_state() == PointsToNode::NoEscape && null_obj->edge_count() == 0 && !null_obj->arraycopy_src() && !null_obj->arraycopy_dst()" ") failed", "sanity"); ::breakpoint(); } } while (0) |
| 291 | !null_obj->arraycopy_dst(), "sanity")do { if (!(null_obj->escape_state() == PointsToNode::NoEscape && null_obj->edge_count() == 0 && !null_obj ->arraycopy_src() && !null_obj->arraycopy_dst() )) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 291, "assert(" "null_obj->escape_state() == PointsToNode::NoEscape && null_obj->edge_count() == 0 && !null_obj->arraycopy_src() && !null_obj->arraycopy_dst()" ") failed", "sanity"); ::breakpoint(); } } while (0); |
| 292 | #endif |
| 293 | |
| 294 | _collecting = false; |
| 295 | |
| 296 | } // TracePhase t3("connectionGraph") |
| 297 | |
| 298 | // 4. Optimize ideal graph based on EA information. |
| 299 | bool has_non_escaping_obj = (non_escaped_allocs_worklist.length() > 0); |
| 300 | if (has_non_escaping_obj) { |
| 301 | optimize_ideal_graph(ptr_cmp_worklist, storestore_worklist); |
| 302 | } |
| 303 | |
| 304 | #ifndef PRODUCT |
| 305 | if (PrintEscapeAnalysis) { |
| 306 | dump(ptnodes_worklist); // Dump ConnectionGraph |
| 307 | } |
| 308 | #endif |
| 309 | |
| 310 | #ifdef ASSERT1 |
| 311 | if (VerifyConnectionGraph) { |
| 312 | int alloc_length = alloc_worklist.length(); |
| 313 | for (int next = 0; next < alloc_length; ++next) { |
| 314 | Node* n = alloc_worklist.at(next); |
| 315 | PointsToNode* ptn = ptnode_adr(n->_idx); |
| 316 | assert(ptn->escape_state() == PointsToNode::NoEscape && ptn->scalar_replaceable(), "sanity")do { if (!(ptn->escape_state() == PointsToNode::NoEscape && ptn->scalar_replaceable())) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 316, "assert(" "ptn->escape_state() == PointsToNode::NoEscape && ptn->scalar_replaceable()" ") failed", "sanity"); ::breakpoint(); } } while (0); |
| 317 | } |
| 318 | } |
| 319 | #endif |
| 320 | |
| 321 | // 5. Separate memory graph for scalar replaceable allcations. |
| 322 | bool has_scalar_replaceable_candidates = (alloc_worklist.length() > 0); |
| 323 | if (has_scalar_replaceable_candidates && |
| 324 | C->AliasLevel() >= 3 && EliminateAllocations) { |
| 325 | // Now use the escape information to create unique types for |
| 326 | // scalar replaceable objects. |
| 327 | split_unique_types(alloc_worklist, arraycopy_worklist, mergemem_worklist); |
| 328 | if (C->failing()) return false; |
| 329 | C->print_method(PHASE_AFTER_EA, 2); |
| 330 | |
| 331 | #ifdef ASSERT1 |
| 332 | } else if (Verbose && (PrintEscapeAnalysis || PrintEliminateAllocations)) { |
| 333 | tty->print("=== No allocations eliminated for "); |
| 334 | C->method()->print_short_name(); |
| 335 | if (!EliminateAllocations) { |
| 336 | tty->print(" since EliminateAllocations is off ==="); |
| 337 | } else if(!has_scalar_replaceable_candidates) { |
| 338 | tty->print(" since there are no scalar replaceable candidates ==="); |
| 339 | } else if(C->AliasLevel() < 3) { |
| 340 | tty->print(" since AliasLevel < 3 ==="); |
| 341 | } |
| 342 | tty->cr(); |
| 343 | #endif |
| 344 | } |
| 345 | |
| 346 | // Annotate at safepoints if they have <= ArgEscape objects in their scope and at |
| 347 | // java calls if they pass ArgEscape objects as parameters. |
| 348 | if (has_non_escaping_obj && |
| 349 | (C->env()->should_retain_local_variables() || |
| 350 | C->env()->jvmti_can_get_owned_monitor_info() || |
| 351 | C->env()->jvmti_can_walk_any_space() || |
| 352 | DeoptimizeObjectsALot)) { |
| 353 | int sfn_length = sfn_worklist.length(); |
| 354 | for (int next = 0; next < sfn_length; next++) { |
| 355 | SafePointNode* sfn = sfn_worklist.at(next); |
| 356 | sfn->set_has_ea_local_in_scope(has_ea_local_in_scope(sfn)); |
| 357 | if (sfn->is_CallJava()) { |
| 358 | CallJavaNode* call = sfn->as_CallJava(); |
| 359 | call->set_arg_escape(has_arg_escape(call)); |
| 360 | } |
| 361 | } |
| 362 | } |
| 363 | |
| 364 | return has_non_escaping_obj; |
| 365 | } |
| 366 | |
| 367 | // Returns true if there is an object in the scope of sfn that does not escape globally. |
| 368 | bool ConnectionGraph::has_ea_local_in_scope(SafePointNode* sfn) { |
| 369 | Compile* C = _compile; |
| 370 | for (JVMState* jvms = sfn->jvms(); jvms != NULL__null; jvms = jvms->caller()) { |
| 371 | if (C->env()->should_retain_local_variables() || C->env()->jvmti_can_walk_any_space() || |
| 372 | DeoptimizeObjectsALot) { |
| 373 | // Jvmti agents can access locals. Must provide info about local objects at runtime. |
| 374 | int num_locs = jvms->loc_size(); |
| 375 | for (int idx = 0; idx < num_locs; idx++) { |
| 376 | Node* l = sfn->local(jvms, idx); |
| 377 | if (not_global_escape(l)) { |
| 378 | return true; |
| 379 | } |
| 380 | } |
| 381 | } |
| 382 | if (C->env()->jvmti_can_get_owned_monitor_info() || |
| 383 | C->env()->jvmti_can_walk_any_space() || DeoptimizeObjectsALot) { |
| 384 | // Jvmti agents can read monitors. Must provide info about locked objects at runtime. |
| 385 | int num_mon = jvms->nof_monitors(); |
| 386 | for (int idx = 0; idx < num_mon; idx++) { |
| 387 | Node* m = sfn->monitor_obj(jvms, idx); |
| 388 | if (m != NULL__null && not_global_escape(m)) { |
| 389 | return true; |
| 390 | } |
| 391 | } |
| 392 | } |
| 393 | } |
| 394 | return false; |
| 395 | } |
| 396 | |
| 397 | // Returns true if at least one of the arguments to the call is an object |
| 398 | // that does not escape globally. |
| 399 | bool ConnectionGraph::has_arg_escape(CallJavaNode* call) { |
| 400 | if (call->method() != NULL__null) { |
| 401 | uint max_idx = TypeFunc::Parms + call->method()->arg_size(); |
| 402 | for (uint idx = TypeFunc::Parms; idx < max_idx; idx++) { |
| 403 | Node* p = call->in(idx); |
| 404 | if (not_global_escape(p)) { |
| 405 | return true; |
| 406 | } |
| 407 | } |
| 408 | } else { |
| 409 | const char* name = call->as_CallStaticJava()->_name; |
| 410 | assert(name != NULL, "no name")do { if (!(name != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 410, "assert(" "name != __null" ") failed", "no name"); ::breakpoint (); } } while (0); |
| 411 | // no arg escapes through uncommon traps |
| 412 | if (strcmp(name, "uncommon_trap") != 0) { |
| 413 | // process_call_arguments() assumes that all arguments escape globally |
| 414 | const TypeTuple* d = call->tf()->domain(); |
| 415 | for (uint i = TypeFunc::Parms; i < d->cnt(); i++) { |
| 416 | const Type* at = d->field_at(i); |
| 417 | if (at->isa_oopptr() != NULL__null) { |
| 418 | return true; |
| 419 | } |
| 420 | } |
| 421 | } |
| 422 | } |
| 423 | return false; |
| 424 | } |
| 425 | |
| 426 | |
| 427 | |
| 428 | // Utility function for nodes that load an object |
| 429 | void ConnectionGraph::add_objload_to_connection_graph(Node *n, Unique_Node_List *delayed_worklist) { |
| 430 | // Using isa_ptr() instead of isa_oopptr() for LoadP and Phi because |
| 431 | // ThreadLocal has RawPtr type. |
| 432 | const Type* t = _igvn->type(n); |
| 433 | if (t->make_ptr() != NULL__null) { |
| 434 | Node* adr = n->in(MemNode::Address); |
| 435 | #ifdef ASSERT1 |
| 436 | if (!adr->is_AddP()) { |
| 437 | assert(_igvn->type(adr)->isa_rawptr(), "sanity")do { if (!(_igvn->type(adr)->isa_rawptr())) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 437, "assert(" "_igvn->type(adr)->isa_rawptr()" ") failed" , "sanity"); ::breakpoint(); } } while (0); |
| 438 | } else { |
| 439 | assert((ptnode_adr(adr->_idx) == NULL ||do { if (!((ptnode_adr(adr->_idx) == __null || ptnode_adr( adr->_idx)->as_Field()->is_oop()))) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 440, "assert(" "(ptnode_adr(adr->_idx) == __null || ptnode_adr(adr->_idx)->as_Field()->is_oop())" ") failed", "sanity"); ::breakpoint(); } } while (0) |
| 440 | ptnode_adr(adr->_idx)->as_Field()->is_oop()), "sanity")do { if (!((ptnode_adr(adr->_idx) == __null || ptnode_adr( adr->_idx)->as_Field()->is_oop()))) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 440, "assert(" "(ptnode_adr(adr->_idx) == __null || ptnode_adr(adr->_idx)->as_Field()->is_oop())" ") failed", "sanity"); ::breakpoint(); } } while (0); |
| 441 | } |
| 442 | #endif |
| 443 | add_local_var_and_edge(n, PointsToNode::NoEscape, |
| 444 | adr, delayed_worklist); |
| 445 | } |
| 446 | } |
| 447 | |
| 448 | // Populate Connection Graph with PointsTo nodes and create simple |
| 449 | // connection graph edges. |
| 450 | void ConnectionGraph::add_node_to_connection_graph(Node *n, Unique_Node_List *delayed_worklist) { |
| 451 | assert(!_verify, "this method should not be called for verification")do { if (!(!_verify)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 451, "assert(" "!_verify" ") failed", "this method should not be called for verification" ); ::breakpoint(); } } while (0); |
| 452 | PhaseGVN* igvn = _igvn; |
| 453 | uint n_idx = n->_idx; |
| 454 | PointsToNode* n_ptn = ptnode_adr(n_idx); |
| 455 | if (n_ptn != NULL__null) { |
| 456 | return; // No need to redefine PointsTo node during first iteration. |
| 457 | } |
| 458 | int opcode = n->Opcode(); |
| 459 | bool gc_handled = BarrierSet::barrier_set()->barrier_set_c2()->escape_add_to_con_graph(this, igvn, delayed_worklist, n, opcode); |
| 460 | if (gc_handled) { |
| 461 | return; // Ignore node if already handled by GC. |
| 462 | } |
| 463 | |
| 464 | if (n->is_Call()) { |
| 465 | // Arguments to allocation and locking don't escape. |
| 466 | if (n->is_AbstractLock()) { |
| 467 | // Put Lock and Unlock nodes on IGVN worklist to process them during |
| 468 | // first IGVN optimization when escape information is still available. |
| 469 | record_for_optimizer(n); |
| 470 | } else if (n->is_Allocate()) { |
| 471 | add_call_node(n->as_Call()); |
| 472 | record_for_optimizer(n); |
| 473 | } else { |
| 474 | if (n->is_CallStaticJava()) { |
| 475 | const char* name = n->as_CallStaticJava()->_name; |
| 476 | if (name != NULL__null && strcmp(name, "uncommon_trap") == 0) { |
| 477 | return; // Skip uncommon traps |
| 478 | } |
| 479 | } |
| 480 | // Don't mark as processed since call's arguments have to be processed. |
| 481 | delayed_worklist->push(n); |
| 482 | // Check if a call returns an object. |
| 483 | if ((n->as_Call()->returns_pointer() && |
| 484 | n->as_Call()->proj_out_or_null(TypeFunc::Parms) != NULL__null) || |
| 485 | (n->is_CallStaticJava() && |
| 486 | n->as_CallStaticJava()->is_boxing_method())) { |
| 487 | add_call_node(n->as_Call()); |
| 488 | } |
| 489 | } |
| 490 | return; |
| 491 | } |
| 492 | // Put this check here to process call arguments since some call nodes |
| 493 | // point to phantom_obj. |
| 494 | if (n_ptn == phantom_obj || n_ptn == null_obj) { |
| 495 | return; // Skip predefined nodes. |
| 496 | } |
| 497 | switch (opcode) { |
| 498 | case Op_AddP: { |
| 499 | Node* base = get_addp_base(n); |
| 500 | PointsToNode* ptn_base = ptnode_adr(base->_idx); |
| 501 | // Field nodes are created for all field types. They are used in |
| 502 | // adjust_scalar_replaceable_state() and split_unique_types(). |
| 503 | // Note, non-oop fields will have only base edges in Connection |
| 504 | // Graph because such fields are not used for oop loads and stores. |
| 505 | int offset = address_offset(n, igvn); |
| 506 | add_field(n, PointsToNode::NoEscape, offset); |
| 507 | if (ptn_base == NULL__null) { |
| 508 | delayed_worklist->push(n); // Process it later. |
| 509 | } else { |
| 510 | n_ptn = ptnode_adr(n_idx); |
| 511 | add_base(n_ptn->as_Field(), ptn_base); |
| 512 | } |
| 513 | break; |
| 514 | } |
| 515 | case Op_CastX2P: { |
| 516 | map_ideal_node(n, phantom_obj); |
| 517 | break; |
| 518 | } |
| 519 | case Op_CastPP: |
| 520 | case Op_CheckCastPP: |
| 521 | case Op_EncodeP: |
| 522 | case Op_DecodeN: |
| 523 | case Op_EncodePKlass: |
| 524 | case Op_DecodeNKlass: { |
| 525 | add_local_var_and_edge(n, PointsToNode::NoEscape, n->in(1), delayed_worklist); |
| 526 | break; |
| 527 | } |
| 528 | case Op_CMoveP: { |
| 529 | add_local_var(n, PointsToNode::NoEscape); |
| 530 | // Do not add edges during first iteration because some could be |
| 531 | // not defined yet. |
| 532 | delayed_worklist->push(n); |
| 533 | break; |
| 534 | } |
| 535 | case Op_ConP: |
| 536 | case Op_ConN: |
| 537 | case Op_ConNKlass: { |
| 538 | // assume all oop constants globally escape except for null |
| 539 | PointsToNode::EscapeState es; |
| 540 | const Type* t = igvn->type(n); |
| 541 | if (t == TypePtr::NULL_PTR || t == TypeNarrowOop::NULL_PTR) { |
| 542 | es = PointsToNode::NoEscape; |
| 543 | } else { |
| 544 | es = PointsToNode::GlobalEscape; |
| 545 | } |
| 546 | add_java_object(n, es); |
| 547 | break; |
| 548 | } |
| 549 | case Op_CreateEx: { |
| 550 | // assume that all exception objects globally escape |
| 551 | map_ideal_node(n, phantom_obj); |
| 552 | break; |
| 553 | } |
| 554 | case Op_LoadKlass: |
| 555 | case Op_LoadNKlass: { |
| 556 | // Unknown class is loaded |
| 557 | map_ideal_node(n, phantom_obj); |
| 558 | break; |
| 559 | } |
| 560 | case Op_LoadP: |
| 561 | case Op_LoadN: |
| 562 | case Op_LoadPLocked: { |
| 563 | add_objload_to_connection_graph(n, delayed_worklist); |
| 564 | break; |
| 565 | } |
| 566 | case Op_Parm: { |
| 567 | map_ideal_node(n, phantom_obj); |
| 568 | break; |
| 569 | } |
| 570 | case Op_PartialSubtypeCheck: { |
| 571 | // Produces Null or notNull and is used in only in CmpP so |
| 572 | // phantom_obj could be used. |
| 573 | map_ideal_node(n, phantom_obj); // Result is unknown |
| 574 | break; |
| 575 | } |
| 576 | case Op_Phi: { |
| 577 | // Using isa_ptr() instead of isa_oopptr() for LoadP and Phi because |
| 578 | // ThreadLocal has RawPtr type. |
| 579 | const Type* t = n->as_Phi()->type(); |
| 580 | if (t->make_ptr() != NULL__null) { |
| 581 | add_local_var(n, PointsToNode::NoEscape); |
| 582 | // Do not add edges during first iteration because some could be |
| 583 | // not defined yet. |
| 584 | delayed_worklist->push(n); |
| 585 | } |
| 586 | break; |
| 587 | } |
| 588 | case Op_Proj: { |
| 589 | // we are only interested in the oop result projection from a call |
| 590 | if (n->as_Proj()->_con == TypeFunc::Parms && n->in(0)->is_Call() && |
| 591 | n->in(0)->as_Call()->returns_pointer()) { |
| 592 | add_local_var_and_edge(n, PointsToNode::NoEscape, n->in(0), delayed_worklist); |
| 593 | } |
| 594 | break; |
| 595 | } |
| 596 | case Op_Rethrow: // Exception object escapes |
| 597 | case Op_Return: { |
| 598 | if (n->req() > TypeFunc::Parms && |
| 599 | igvn->type(n->in(TypeFunc::Parms))->isa_oopptr()) { |
| 600 | // Treat Return value as LocalVar with GlobalEscape escape state. |
| 601 | add_local_var_and_edge(n, PointsToNode::GlobalEscape, n->in(TypeFunc::Parms), delayed_worklist); |
| 602 | } |
| 603 | break; |
| 604 | } |
| 605 | case Op_CompareAndExchangeP: |
| 606 | case Op_CompareAndExchangeN: |
| 607 | case Op_GetAndSetP: |
| 608 | case Op_GetAndSetN: { |
| 609 | add_objload_to_connection_graph(n, delayed_worklist); |
| 610 | // fall-through |
| 611 | } |
| 612 | case Op_StoreP: |
| 613 | case Op_StoreN: |
| 614 | case Op_StoreNKlass: |
| 615 | case Op_StorePConditional: |
| 616 | case Op_WeakCompareAndSwapP: |
| 617 | case Op_WeakCompareAndSwapN: |
| 618 | case Op_CompareAndSwapP: |
| 619 | case Op_CompareAndSwapN: { |
| 620 | add_to_congraph_unsafe_access(n, opcode, delayed_worklist); |
| 621 | break; |
| 622 | } |
| 623 | case Op_AryEq: |
| 624 | case Op_HasNegatives: |
| 625 | case Op_StrComp: |
| 626 | case Op_StrEquals: |
| 627 | case Op_StrIndexOf: |
| 628 | case Op_StrIndexOfChar: |
| 629 | case Op_StrInflatedCopy: |
| 630 | case Op_StrCompressedCopy: |
| 631 | case Op_EncodeISOArray: { |
| 632 | add_local_var(n, PointsToNode::ArgEscape); |
| 633 | delayed_worklist->push(n); // Process it later. |
| 634 | break; |
| 635 | } |
| 636 | case Op_ThreadLocal: { |
| 637 | add_java_object(n, PointsToNode::ArgEscape); |
| 638 | break; |
| 639 | } |
| 640 | default: |
| 641 | ; // Do nothing for nodes not related to EA. |
| 642 | } |
| 643 | return; |
| 644 | } |
| 645 | |
| 646 | // Add final simple edges to graph. |
| 647 | void ConnectionGraph::add_final_edges(Node *n) { |
| 648 | PointsToNode* n_ptn = ptnode_adr(n->_idx); |
| 649 | #ifdef ASSERT1 |
| 650 | if (_verify && n_ptn->is_JavaObject()) |
| 651 | return; // This method does not change graph for JavaObject. |
| 652 | #endif |
| 653 | |
| 654 | if (n->is_Call()) { |
| 655 | process_call_arguments(n->as_Call()); |
| 656 | return; |
| 657 | } |
| 658 | assert(n->is_Store() || n->is_LoadStore() ||do { if (!(n->is_Store() || n->is_LoadStore() || (n_ptn != __null) && (n_ptn->ideal_node() != __null))) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 660, "assert(" "n->is_Store() || n->is_LoadStore() || (n_ptn != __null) && (n_ptn->ideal_node() != __null)" ") failed", "node should be registered already"); ::breakpoint (); } } while (0) |
| 659 | (n_ptn != NULL) && (n_ptn->ideal_node() != NULL),do { if (!(n->is_Store() || n->is_LoadStore() || (n_ptn != __null) && (n_ptn->ideal_node() != __null))) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 660, "assert(" "n->is_Store() || n->is_LoadStore() || (n_ptn != __null) && (n_ptn->ideal_node() != __null)" ") failed", "node should be registered already"); ::breakpoint (); } } while (0) |
| 660 | "node should be registered already")do { if (!(n->is_Store() || n->is_LoadStore() || (n_ptn != __null) && (n_ptn->ideal_node() != __null))) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 660, "assert(" "n->is_Store() || n->is_LoadStore() || (n_ptn != __null) && (n_ptn->ideal_node() != __null)" ") failed", "node should be registered already"); ::breakpoint (); } } while (0); |
| 661 | int opcode = n->Opcode(); |
| 662 | bool gc_handled = BarrierSet::barrier_set()->barrier_set_c2()->escape_add_final_edges(this, _igvn, n, opcode); |
| 663 | if (gc_handled) { |
| 664 | return; // Ignore node if already handled by GC. |
| 665 | } |
| 666 | switch (opcode) { |
| 667 | case Op_AddP: { |
| 668 | Node* base = get_addp_base(n); |
| 669 | PointsToNode* ptn_base = ptnode_adr(base->_idx); |
| 670 | assert(ptn_base != NULL, "field's base should be registered")do { if (!(ptn_base != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 670, "assert(" "ptn_base != __null" ") failed", "field's base should be registered" ); ::breakpoint(); } } while (0); |
| 671 | add_base(n_ptn->as_Field(), ptn_base); |
| 672 | break; |
| 673 | } |
| 674 | case Op_CastPP: |
| 675 | case Op_CheckCastPP: |
| 676 | case Op_EncodeP: |
| 677 | case Op_DecodeN: |
| 678 | case Op_EncodePKlass: |
| 679 | case Op_DecodeNKlass: { |
| 680 | add_local_var_and_edge(n, PointsToNode::NoEscape, n->in(1), NULL__null); |
| 681 | break; |
| 682 | } |
| 683 | case Op_CMoveP: { |
| 684 | for (uint i = CMoveNode::IfFalse; i < n->req(); i++) { |
| 685 | Node* in = n->in(i); |
| 686 | if (in == NULL__null) { |
| 687 | continue; // ignore NULL |
| 688 | } |
| 689 | Node* uncast_in = in->uncast(); |
| 690 | if (uncast_in->is_top() || uncast_in == n) { |
| 691 | continue; // ignore top or inputs which go back this node |
| 692 | } |
| 693 | PointsToNode* ptn = ptnode_adr(in->_idx); |
| 694 | assert(ptn != NULL, "node should be registered")do { if (!(ptn != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 694, "assert(" "ptn != __null" ") failed", "node should be registered" ); ::breakpoint(); } } while (0); |
| 695 | add_edge(n_ptn, ptn); |
| 696 | } |
| 697 | break; |
| 698 | } |
| 699 | case Op_LoadP: |
| 700 | case Op_LoadN: |
| 701 | case Op_LoadPLocked: { |
| 702 | // Using isa_ptr() instead of isa_oopptr() for LoadP and Phi because |
| 703 | // ThreadLocal has RawPtr type. |
| 704 | assert(_igvn->type(n)->make_ptr() != NULL, "Unexpected node type")do { if (!(_igvn->type(n)->make_ptr() != __null)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 704, "assert(" "_igvn->type(n)->make_ptr() != __null" ") failed", "Unexpected node type"); ::breakpoint(); } } while (0); |
| 705 | add_local_var_and_edge(n, PointsToNode::NoEscape, n->in(MemNode::Address), NULL__null); |
| 706 | break; |
| 707 | } |
| 708 | case Op_Phi: { |
| 709 | // Using isa_ptr() instead of isa_oopptr() for LoadP and Phi because |
| 710 | // ThreadLocal has RawPtr type. |
| 711 | assert(n->as_Phi()->type()->make_ptr() != NULL, "Unexpected node type")do { if (!(n->as_Phi()->type()->make_ptr() != __null )) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 711, "assert(" "n->as_Phi()->type()->make_ptr() != __null" ") failed", "Unexpected node type"); ::breakpoint(); } } while (0); |
| 712 | for (uint i = 1; i < n->req(); i++) { |
| 713 | Node* in = n->in(i); |
| 714 | if (in == NULL__null) { |
| 715 | continue; // ignore NULL |
| 716 | } |
| 717 | Node* uncast_in = in->uncast(); |
| 718 | if (uncast_in->is_top() || uncast_in == n) { |
| 719 | continue; // ignore top or inputs which go back this node |
| 720 | } |
| 721 | PointsToNode* ptn = ptnode_adr(in->_idx); |
| 722 | assert(ptn != NULL, "node should be registered")do { if (!(ptn != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 722, "assert(" "ptn != __null" ") failed", "node should be registered" ); ::breakpoint(); } } while (0); |
| 723 | add_edge(n_ptn, ptn); |
| 724 | } |
| 725 | break; |
| 726 | } |
| 727 | case Op_Proj: { |
| 728 | // we are only interested in the oop result projection from a call |
| 729 | assert(n->as_Proj()->_con == TypeFunc::Parms && n->in(0)->is_Call() &&do { if (!(n->as_Proj()->_con == TypeFunc::Parms && n->in(0)->is_Call() && n->in(0)->as_Call ()->returns_pointer())) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 730, "assert(" "n->as_Proj()->_con == TypeFunc::Parms && n->in(0)->is_Call() && n->in(0)->as_Call()->returns_pointer()" ") failed", "Unexpected node type"); ::breakpoint(); } } while (0) |
| 730 | n->in(0)->as_Call()->returns_pointer(), "Unexpected node type")do { if (!(n->as_Proj()->_con == TypeFunc::Parms && n->in(0)->is_Call() && n->in(0)->as_Call ()->returns_pointer())) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 730, "assert(" "n->as_Proj()->_con == TypeFunc::Parms && n->in(0)->is_Call() && n->in(0)->as_Call()->returns_pointer()" ") failed", "Unexpected node type"); ::breakpoint(); } } while (0); |
| 731 | add_local_var_and_edge(n, PointsToNode::NoEscape, n->in(0), NULL__null); |
| 732 | break; |
| 733 | } |
| 734 | case Op_Rethrow: // Exception object escapes |
| 735 | case Op_Return: { |
| 736 | assert(n->req() > TypeFunc::Parms && _igvn->type(n->in(TypeFunc::Parms))->isa_oopptr(),do { if (!(n->req() > TypeFunc::Parms && _igvn-> type(n->in(TypeFunc::Parms))->isa_oopptr())) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 737, "assert(" "n->req() > TypeFunc::Parms && _igvn->type(n->in(TypeFunc::Parms))->isa_oopptr()" ") failed", "Unexpected node type"); ::breakpoint(); } } while (0) |
| 737 | "Unexpected node type")do { if (!(n->req() > TypeFunc::Parms && _igvn-> type(n->in(TypeFunc::Parms))->isa_oopptr())) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 737, "assert(" "n->req() > TypeFunc::Parms && _igvn->type(n->in(TypeFunc::Parms))->isa_oopptr()" ") failed", "Unexpected node type"); ::breakpoint(); } } while (0); |
| 738 | // Treat Return value as LocalVar with GlobalEscape escape state. |
| 739 | add_local_var_and_edge(n, PointsToNode::GlobalEscape, n->in(TypeFunc::Parms), NULL__null); |
| 740 | break; |
| 741 | } |
| 742 | case Op_CompareAndExchangeP: |
| 743 | case Op_CompareAndExchangeN: |
| 744 | case Op_GetAndSetP: |
| 745 | case Op_GetAndSetN:{ |
| 746 | assert(_igvn->type(n)->make_ptr() != NULL, "Unexpected node type")do { if (!(_igvn->type(n)->make_ptr() != __null)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 746, "assert(" "_igvn->type(n)->make_ptr() != __null" ") failed", "Unexpected node type"); ::breakpoint(); } } while (0); |
| 747 | add_local_var_and_edge(n, PointsToNode::NoEscape, n->in(MemNode::Address), NULL__null); |
| 748 | // fall-through |
| 749 | } |
| 750 | case Op_CompareAndSwapP: |
| 751 | case Op_CompareAndSwapN: |
| 752 | case Op_WeakCompareAndSwapP: |
| 753 | case Op_WeakCompareAndSwapN: |
| 754 | case Op_StoreP: |
| 755 | case Op_StoreN: |
| 756 | case Op_StoreNKlass: |
| 757 | case Op_StorePConditional:{ |
| 758 | add_final_edges_unsafe_access(n, opcode); |
| 759 | break; |
| 760 | } |
| 761 | case Op_AryEq: |
| 762 | case Op_HasNegatives: |
| 763 | case Op_StrComp: |
| 764 | case Op_StrEquals: |
| 765 | case Op_StrIndexOf: |
| 766 | case Op_StrIndexOfChar: |
| 767 | case Op_StrInflatedCopy: |
| 768 | case Op_StrCompressedCopy: |
| 769 | case Op_EncodeISOArray: { |
| 770 | // char[]/byte[] arrays passed to string intrinsic do not escape but |
| 771 | // they are not scalar replaceable. Adjust escape state for them. |
| 772 | // Start from in(2) edge since in(1) is memory edge. |
| 773 | for (uint i = 2; i < n->req(); i++) { |
| 774 | Node* adr = n->in(i); |
| 775 | const Type* at = _igvn->type(adr); |
| 776 | if (!adr->is_top() && at->isa_ptr()) { |
| 777 | assert(at == Type::TOP || at == TypePtr::NULL_PTR ||do { if (!(at == Type::TOP || at == TypePtr::NULL_PTR || at-> isa_ptr() != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 778, "assert(" "at == Type::TOP || at == TypePtr::NULL_PTR || at->isa_ptr() != __null" ") failed", "expecting a pointer"); ::breakpoint(); } } while (0) |
| 778 | at->isa_ptr() != NULL, "expecting a pointer")do { if (!(at == Type::TOP || at == TypePtr::NULL_PTR || at-> isa_ptr() != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 778, "assert(" "at == Type::TOP || at == TypePtr::NULL_PTR || at->isa_ptr() != __null" ") failed", "expecting a pointer"); ::breakpoint(); } } while (0); |
| 779 | if (adr->is_AddP()) { |
| 780 | adr = get_addp_base(adr); |
| 781 | } |
| 782 | PointsToNode* ptn = ptnode_adr(adr->_idx); |
| 783 | assert(ptn != NULL, "node should be registered")do { if (!(ptn != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 783, "assert(" "ptn != __null" ") failed", "node should be registered" ); ::breakpoint(); } } while (0); |
| 784 | add_edge(n_ptn, ptn); |
| 785 | } |
| 786 | } |
| 787 | break; |
| 788 | } |
| 789 | default: { |
| 790 | // This method should be called only for EA specific nodes which may |
| 791 | // miss some edges when they were created. |
| 792 | #ifdef ASSERT1 |
| 793 | n->dump(1); |
| 794 | #endif |
| 795 | guarantee(false, "unknown node")do { if (!(false)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 795, "guarantee(" "false" ") failed", "unknown node"); ::breakpoint (); } } while (0); |
| 796 | } |
| 797 | } |
| 798 | return; |
| 799 | } |
| 800 | |
| 801 | void ConnectionGraph::add_to_congraph_unsafe_access(Node* n, uint opcode, Unique_Node_List* delayed_worklist) { |
| 802 | Node* adr = n->in(MemNode::Address); |
| 803 | const Type* adr_type = _igvn->type(adr); |
| 804 | adr_type = adr_type->make_ptr(); |
| 805 | if (adr_type == NULL__null) { |
| 806 | return; // skip dead nodes |
| 807 | } |
| 808 | if (adr_type->isa_oopptr() |
| 809 | || ((opcode == Op_StoreP || opcode == Op_StoreN || opcode == Op_StoreNKlass) |
| 810 | && adr_type == TypeRawPtr::NOTNULL |
| 811 | && is_captured_store_address(adr))) { |
| 812 | delayed_worklist->push(n); // Process it later. |
| 813 | #ifdef ASSERT1 |
| 814 | assert (adr->is_AddP(), "expecting an AddP")do { if (!(adr->is_AddP())) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 814, "assert(" "adr->is_AddP()" ") failed", "expecting an AddP" ); ::breakpoint(); } } while (0); |
| 815 | if (adr_type == TypeRawPtr::NOTNULL) { |
| 816 | // Verify a raw address for a store captured by Initialize node. |
| 817 | int offs = (int) _igvn->find_intptr_t_confind_long_con(adr->in(AddPNode::Offset), Type::OffsetBot); |
| 818 | assert(offs != Type::OffsetBot, "offset must be a constant")do { if (!(offs != Type::OffsetBot)) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 818, "assert(" "offs != Type::OffsetBot" ") failed", "offset must be a constant" ); ::breakpoint(); } } while (0); |
| 819 | } |
| 820 | #endif |
| 821 | } else { |
| 822 | // Ignore copy the displaced header to the BoxNode (OSR compilation). |
| 823 | if (adr->is_BoxLock()) { |
| 824 | return; |
| 825 | } |
| 826 | // Stored value escapes in unsafe access. |
| 827 | if ((opcode == Op_StoreP) && adr_type->isa_rawptr()) { |
| 828 | delayed_worklist->push(n); // Process unsafe access later. |
| 829 | return; |
| 830 | } |
| 831 | #ifdef ASSERT1 |
| 832 | n->dump(1); |
| 833 | assert(false, "not unsafe")do { if (!(false)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 833, "assert(" "false" ") failed", "not unsafe"); ::breakpoint (); } } while (0); |
| 834 | #endif |
| 835 | } |
| 836 | } |
| 837 | |
| 838 | bool ConnectionGraph::add_final_edges_unsafe_access(Node* n, uint opcode) { |
| 839 | Node* adr = n->in(MemNode::Address); |
| 840 | const Type *adr_type = _igvn->type(adr); |
| 841 | adr_type = adr_type->make_ptr(); |
| 842 | #ifdef ASSERT1 |
| 843 | if (adr_type == NULL__null) { |
| 844 | n->dump(1); |
| 845 | assert(adr_type != NULL, "dead node should not be on list")do { if (!(adr_type != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 845, "assert(" "adr_type != __null" ") failed", "dead node should not be on list" ); ::breakpoint(); } } while (0); |
| 846 | return true; |
| 847 | } |
| 848 | #endif |
| 849 | |
| 850 | if (adr_type->isa_oopptr() |
| 851 | || ((opcode == Op_StoreP || opcode == Op_StoreN || opcode == Op_StoreNKlass) |
| 852 | && adr_type == TypeRawPtr::NOTNULL |
| 853 | && is_captured_store_address(adr))) { |
| 854 | // Point Address to Value |
| 855 | PointsToNode* adr_ptn = ptnode_adr(adr->_idx); |
| 856 | assert(adr_ptn != NULL &&do { if (!(adr_ptn != __null && adr_ptn->as_Field( )->is_oop())) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 857, "assert(" "adr_ptn != __null && adr_ptn->as_Field()->is_oop()" ") failed", "node should be registered"); ::breakpoint(); } } while (0) |
| 857 | adr_ptn->as_Field()->is_oop(), "node should be registered")do { if (!(adr_ptn != __null && adr_ptn->as_Field( )->is_oop())) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 857, "assert(" "adr_ptn != __null && adr_ptn->as_Field()->is_oop()" ") failed", "node should be registered"); ::breakpoint(); } } while (0); |
| 858 | Node* val = n->in(MemNode::ValueIn); |
| 859 | PointsToNode* ptn = ptnode_adr(val->_idx); |
| 860 | assert(ptn != NULL, "node should be registered")do { if (!(ptn != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 860, "assert(" "ptn != __null" ") failed", "node should be registered" ); ::breakpoint(); } } while (0); |
| 861 | add_edge(adr_ptn, ptn); |
| 862 | return true; |
| 863 | } else if ((opcode == Op_StoreP) && adr_type->isa_rawptr()) { |
| 864 | // Stored value escapes in unsafe access. |
| 865 | Node* val = n->in(MemNode::ValueIn); |
| 866 | PointsToNode* ptn = ptnode_adr(val->_idx); |
| 867 | assert(ptn != NULL, "node should be registered")do { if (!(ptn != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 867, "assert(" "ptn != __null" ") failed", "node should be registered" ); ::breakpoint(); } } while (0); |
| 868 | set_escape_state(ptn, PointsToNode::GlobalEscape); |
| 869 | // Add edge to object for unsafe access with offset. |
| 870 | PointsToNode* adr_ptn = ptnode_adr(adr->_idx); |
| 871 | assert(adr_ptn != NULL, "node should be registered")do { if (!(adr_ptn != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 871, "assert(" "adr_ptn != __null" ") failed", "node should be registered" ); ::breakpoint(); } } while (0); |
| 872 | if (adr_ptn->is_Field()) { |
| 873 | assert(adr_ptn->as_Field()->is_oop(), "should be oop field")do { if (!(adr_ptn->as_Field()->is_oop())) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 873, "assert(" "adr_ptn->as_Field()->is_oop()" ") failed" , "should be oop field"); ::breakpoint(); } } while (0); |
| 874 | add_edge(adr_ptn, ptn); |
| 875 | } |
| 876 | return true; |
| 877 | } |
| 878 | #ifdef ASSERT1 |
| 879 | n->dump(1); |
| 880 | assert(false, "not unsafe")do { if (!(false)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 880, "assert(" "false" ") failed", "not unsafe"); ::breakpoint (); } } while (0); |
| 881 | #endif |
| 882 | return false; |
| 883 | } |
| 884 | |
| 885 | void ConnectionGraph::add_call_node(CallNode* call) { |
| 886 | assert(call->returns_pointer(), "only for call which returns pointer")do { if (!(call->returns_pointer())) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 886, "assert(" "call->returns_pointer()" ") failed", "only for call which returns pointer" ); ::breakpoint(); } } while (0); |
| 887 | uint call_idx = call->_idx; |
| 888 | if (call->is_Allocate()) { |
| 889 | Node* k = call->in(AllocateNode::KlassNode); |
| 890 | const TypeKlassPtr* kt = k->bottom_type()->isa_klassptr(); |
| 891 | assert(kt != NULL, "TypeKlassPtr required.")do { if (!(kt != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 891, "assert(" "kt != __null" ") failed", "TypeKlassPtr required." ); ::breakpoint(); } } while (0); |
| 892 | ciKlass* cik = kt->klass(); |
| 893 | PointsToNode::EscapeState es = PointsToNode::NoEscape; |
| 894 | bool scalar_replaceable = true; |
| 895 | if (call->is_AllocateArray()) { |
| 896 | if (!cik->is_array_klass()) { // StressReflectiveCode |
| 897 | es = PointsToNode::GlobalEscape; |
| 898 | } else { |
| 899 | int length = call->in(AllocateNode::ALength)->find_int_con(-1); |
| 900 | if (length < 0 || length > EliminateAllocationArraySizeLimit) { |
| 901 | // Not scalar replaceable if the length is not constant or too big. |
| 902 | scalar_replaceable = false; |
| 903 | } |
| 904 | } |
| 905 | } else { // Allocate instance |
| 906 | if (cik->is_subclass_of(_compile->env()->Thread_klass()) || |
| 907 | cik->is_subclass_of(_compile->env()->Reference_klass()) || |
| 908 | !cik->is_instance_klass() || // StressReflectiveCode |
| 909 | !cik->as_instance_klass()->can_be_instantiated() || |
| 910 | cik->as_instance_klass()->has_finalizer()) { |
| 911 | es = PointsToNode::GlobalEscape; |
| 912 | } else { |
| 913 | int nfields = cik->as_instance_klass()->nof_nonstatic_fields(); |
| 914 | if (nfields > EliminateAllocationFieldsLimit) { |
| 915 | // Not scalar replaceable if there are too many fields. |
| 916 | scalar_replaceable = false; |
| 917 | } |
| 918 | } |
| 919 | } |
| 920 | add_java_object(call, es); |
| 921 | PointsToNode* ptn = ptnode_adr(call_idx); |
| 922 | if (!scalar_replaceable && ptn->scalar_replaceable()) { |
| 923 | ptn->set_scalar_replaceable(false); |
| 924 | } |
| 925 | } else if (call->is_CallStaticJava()) { |
| 926 | // Call nodes could be different types: |
| 927 | // |
| 928 | // 1. CallDynamicJavaNode (what happened during call is unknown): |
| 929 | // |
| 930 | // - mapped to GlobalEscape JavaObject node if oop is returned; |
| 931 | // |
| 932 | // - all oop arguments are escaping globally; |
| 933 | // |
| 934 | // 2. CallStaticJavaNode (execute bytecode analysis if possible): |
| 935 | // |
| 936 | // - the same as CallDynamicJavaNode if can't do bytecode analysis; |
| 937 | // |
| 938 | // - mapped to GlobalEscape JavaObject node if unknown oop is returned; |
| 939 | // - mapped to NoEscape JavaObject node if non-escaping object allocated |
| 940 | // during call is returned; |
| 941 | // - mapped to ArgEscape LocalVar node pointed to object arguments |
| 942 | // which are returned and does not escape during call; |
| 943 | // |
| 944 | // - oop arguments escaping status is defined by bytecode analysis; |
| 945 | // |
| 946 | // For a static call, we know exactly what method is being called. |
| 947 | // Use bytecode estimator to record whether the call's return value escapes. |
| 948 | ciMethod* meth = call->as_CallJava()->method(); |
| 949 | if (meth == NULL__null) { |
| 950 | const char* name = call->as_CallStaticJava()->_name; |
| 951 | assert(strncmp(name, "_multianewarray", 15) == 0, "TODO: add failed case check")do { if (!(strncmp(name, "_multianewarray", 15) == 0)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 951, "assert(" "strncmp(name, \"_multianewarray\", 15) == 0" ") failed", "TODO: add failed case check"); ::breakpoint(); } } while (0); |
| 952 | // Returns a newly allocated non-escaped object. |
| 953 | add_java_object(call, PointsToNode::NoEscape); |
| 954 | ptnode_adr(call_idx)->set_scalar_replaceable(false); |
| 955 | } else if (meth->is_boxing_method()) { |
| 956 | // Returns boxing object |
| 957 | PointsToNode::EscapeState es; |
| 958 | vmIntrinsics::ID intr = meth->intrinsic_id(); |
| 959 | if (intr == vmIntrinsics::_floatValue || intr == vmIntrinsics::_doubleValue) { |
| 960 | // It does not escape if object is always allocated. |
| 961 | es = PointsToNode::NoEscape; |
| 962 | } else { |
| 963 | // It escapes globally if object could be loaded from cache. |
| 964 | es = PointsToNode::GlobalEscape; |
| 965 | } |
| 966 | add_java_object(call, es); |
| 967 | } else { |
| 968 | BCEscapeAnalyzer* call_analyzer = meth->get_bcea(); |
| 969 | call_analyzer->copy_dependencies(_compile->dependencies()); |
| 970 | if (call_analyzer->is_return_allocated()) { |
| 971 | // Returns a newly allocated non-escaped object, simply |
| 972 | // update dependency information. |
| 973 | // Mark it as NoEscape so that objects referenced by |
| 974 | // it's fields will be marked as NoEscape at least. |
| 975 | add_java_object(call, PointsToNode::NoEscape); |
| 976 | ptnode_adr(call_idx)->set_scalar_replaceable(false); |
| 977 | } else { |
| 978 | // Determine whether any arguments are returned. |
| 979 | const TypeTuple* d = call->tf()->domain(); |
| 980 | bool ret_arg = false; |
| 981 | for (uint i = TypeFunc::Parms; i < d->cnt(); i++) { |
| 982 | if (d->field_at(i)->isa_ptr() != NULL__null && |
| 983 | call_analyzer->is_arg_returned(i - TypeFunc::Parms)) { |
| 984 | ret_arg = true; |
| 985 | break; |
| 986 | } |
| 987 | } |
| 988 | if (ret_arg) { |
| 989 | add_local_var(call, PointsToNode::ArgEscape); |
| 990 | } else { |
| 991 | // Returns unknown object. |
| 992 | map_ideal_node(call, phantom_obj); |
| 993 | } |
| 994 | } |
| 995 | } |
| 996 | } else { |
| 997 | // An other type of call, assume the worst case: |
| 998 | // returned value is unknown and globally escapes. |
| 999 | assert(call->Opcode() == Op_CallDynamicJava, "add failed case check")do { if (!(call->Opcode() == Op_CallDynamicJava)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 999, "assert(" "call->Opcode() == Op_CallDynamicJava" ") failed" , "add failed case check"); ::breakpoint(); } } while (0); |
| 1000 | map_ideal_node(call, phantom_obj); |
| 1001 | } |
| 1002 | } |
| 1003 | |
| 1004 | void ConnectionGraph::process_call_arguments(CallNode *call) { |
| 1005 | bool is_arraycopy = false; |
| 1006 | switch (call->Opcode()) { |
| 1007 | #ifdef ASSERT1 |
| 1008 | case Op_Allocate: |
| 1009 | case Op_AllocateArray: |
| 1010 | case Op_Lock: |
| 1011 | case Op_Unlock: |
| 1012 | assert(false, "should be done already")do { if (!(false)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1012, "assert(" "false" ") failed", "should be done already" ); ::breakpoint(); } } while (0); |
| 1013 | break; |
| 1014 | #endif |
| 1015 | case Op_ArrayCopy: |
| 1016 | case Op_CallLeafNoFP: |
| 1017 | // Most array copies are ArrayCopy nodes at this point but there |
| 1018 | // are still a few direct calls to the copy subroutines (See |
| 1019 | // PhaseStringOpts::copy_string()) |
| 1020 | is_arraycopy = (call->Opcode() == Op_ArrayCopy) || |
| 1021 | call->as_CallLeaf()->is_call_to_arraycopystub(); |
| 1022 | // fall through |
| 1023 | case Op_CallLeafVector: |
| 1024 | case Op_CallLeaf: { |
| 1025 | // Stub calls, objects do not escape but they are not scale replaceable. |
| 1026 | // Adjust escape state for outgoing arguments. |
| 1027 | const TypeTuple * d = call->tf()->domain(); |
| 1028 | bool src_has_oops = false; |
| 1029 | for (uint i = TypeFunc::Parms; i < d->cnt(); i++) { |
| 1030 | const Type* at = d->field_at(i); |
| 1031 | Node *arg = call->in(i); |
| 1032 | if (arg == NULL__null) { |
| 1033 | continue; |
| 1034 | } |
| 1035 | const Type *aat = _igvn->type(arg); |
| 1036 | if (arg->is_top() || !at->isa_ptr() || !aat->isa_ptr()) { |
| 1037 | continue; |
| 1038 | } |
| 1039 | if (arg->is_AddP()) { |
| 1040 | // |
| 1041 | // The inline_native_clone() case when the arraycopy stub is called |
| 1042 | // after the allocation before Initialize and CheckCastPP nodes. |
| 1043 | // Or normal arraycopy for object arrays case. |
| 1044 | // |
| 1045 | // Set AddP's base (Allocate) as not scalar replaceable since |
| 1046 | // pointer to the base (with offset) is passed as argument. |
| 1047 | // |
| 1048 | arg = get_addp_base(arg); |
| 1049 | } |
| 1050 | PointsToNode* arg_ptn = ptnode_adr(arg->_idx); |
| 1051 | assert(arg_ptn != NULL, "should be registered")do { if (!(arg_ptn != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1051, "assert(" "arg_ptn != __null" ") failed", "should be registered" ); ::breakpoint(); } } while (0); |
| 1052 | PointsToNode::EscapeState arg_esc = arg_ptn->escape_state(); |
| 1053 | if (is_arraycopy || arg_esc < PointsToNode::ArgEscape) { |
| 1054 | assert(aat == Type::TOP || aat == TypePtr::NULL_PTR ||do { if (!(aat == Type::TOP || aat == TypePtr::NULL_PTR || aat ->isa_ptr() != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1055, "assert(" "aat == Type::TOP || aat == TypePtr::NULL_PTR || aat->isa_ptr() != __null" ") failed", "expecting an Ptr"); ::breakpoint(); } } while ( 0) |
| 1055 | aat->isa_ptr() != NULL, "expecting an Ptr")do { if (!(aat == Type::TOP || aat == TypePtr::NULL_PTR || aat ->isa_ptr() != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1055, "assert(" "aat == Type::TOP || aat == TypePtr::NULL_PTR || aat->isa_ptr() != __null" ") failed", "expecting an Ptr"); ::breakpoint(); } } while ( 0); |
| 1056 | bool arg_has_oops = aat->isa_oopptr() && |
| 1057 | (aat->isa_oopptr()->klass() == NULL__null || aat->isa_instptr() || |
| 1058 | (aat->isa_aryptr() && aat->isa_aryptr()->klass()->is_obj_array_klass())); |
| 1059 | if (i == TypeFunc::Parms) { |
| 1060 | src_has_oops = arg_has_oops; |
| 1061 | } |
| 1062 | // |
| 1063 | // src or dst could be j.l.Object when other is basic type array: |
| 1064 | // |
| 1065 | // arraycopy(char[],0,Object*,0,size); |
| 1066 | // arraycopy(Object*,0,char[],0,size); |
| 1067 | // |
| 1068 | // Don't add edges in such cases. |
| 1069 | // |
| 1070 | bool arg_is_arraycopy_dest = src_has_oops && is_arraycopy && |
| 1071 | arg_has_oops && (i > TypeFunc::Parms); |
| 1072 | #ifdef ASSERT1 |
| 1073 | if (!(is_arraycopy || |
| 1074 | BarrierSet::barrier_set()->barrier_set_c2()->is_gc_barrier_node(call) || |
| 1075 | (call->as_CallLeaf()->_name != NULL__null && |
| 1076 | (strcmp(call->as_CallLeaf()->_name, "updateBytesCRC32") == 0 || |
| 1077 | strcmp(call->as_CallLeaf()->_name, "updateBytesCRC32C") == 0 || |
| 1078 | strcmp(call->as_CallLeaf()->_name, "updateBytesAdler32") == 0 || |
| 1079 | strcmp(call->as_CallLeaf()->_name, "aescrypt_encryptBlock") == 0 || |
| 1080 | strcmp(call->as_CallLeaf()->_name, "aescrypt_decryptBlock") == 0 || |
| 1081 | strcmp(call->as_CallLeaf()->_name, "cipherBlockChaining_encryptAESCrypt") == 0 || |
| 1082 | strcmp(call->as_CallLeaf()->_name, "cipherBlockChaining_decryptAESCrypt") == 0 || |
| 1083 | strcmp(call->as_CallLeaf()->_name, "electronicCodeBook_encryptAESCrypt") == 0 || |
| 1084 | strcmp(call->as_CallLeaf()->_name, "electronicCodeBook_decryptAESCrypt") == 0 || |
| 1085 | strcmp(call->as_CallLeaf()->_name, "counterMode_AESCrypt") == 0 || |
| 1086 | strcmp(call->as_CallLeaf()->_name, "galoisCounterMode_AESCrypt") == 0 || |
| 1087 | strcmp(call->as_CallLeaf()->_name, "ghash_processBlocks") == 0 || |
| 1088 | strcmp(call->as_CallLeaf()->_name, "encodeBlock") == 0 || |
| 1089 | strcmp(call->as_CallLeaf()->_name, "decodeBlock") == 0 || |
| 1090 | strcmp(call->as_CallLeaf()->_name, "md5_implCompress") == 0 || |
| 1091 | strcmp(call->as_CallLeaf()->_name, "md5_implCompressMB") == 0 || |
| 1092 | strcmp(call->as_CallLeaf()->_name, "sha1_implCompress") == 0 || |
| 1093 | strcmp(call->as_CallLeaf()->_name, "sha1_implCompressMB") == 0 || |
| 1094 | strcmp(call->as_CallLeaf()->_name, "sha256_implCompress") == 0 || |
| 1095 | strcmp(call->as_CallLeaf()->_name, "sha256_implCompressMB") == 0 || |
| 1096 | strcmp(call->as_CallLeaf()->_name, "sha512_implCompress") == 0 || |
| 1097 | strcmp(call->as_CallLeaf()->_name, "sha512_implCompressMB") == 0 || |
| 1098 | strcmp(call->as_CallLeaf()->_name, "sha3_implCompress") == 0 || |
| 1099 | strcmp(call->as_CallLeaf()->_name, "sha3_implCompressMB") == 0 || |
| 1100 | strcmp(call->as_CallLeaf()->_name, "multiplyToLen") == 0 || |
| 1101 | strcmp(call->as_CallLeaf()->_name, "squareToLen") == 0 || |
| 1102 | strcmp(call->as_CallLeaf()->_name, "mulAdd") == 0 || |
| 1103 | strcmp(call->as_CallLeaf()->_name, "montgomery_multiply") == 0 || |
| 1104 | strcmp(call->as_CallLeaf()->_name, "montgomery_square") == 0 || |
| 1105 | strcmp(call->as_CallLeaf()->_name, "bigIntegerRightShiftWorker") == 0 || |
| 1106 | strcmp(call->as_CallLeaf()->_name, "bigIntegerLeftShiftWorker") == 0 || |
| 1107 | strcmp(call->as_CallLeaf()->_name, "vectorizedMismatch") == 0 || |
| 1108 | strcmp(call->as_CallLeaf()->_name, "get_class_id_intrinsic") == 0) |
| 1109 | ))) { |
| 1110 | call->dump(); |
| 1111 | fatal("EA unexpected CallLeaf %s", call->as_CallLeaf()->_name)do { (*g_assert_poison) = 'X';; report_fatal(INTERNAL_ERROR, "/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1111, "EA unexpected CallLeaf %s", call->as_CallLeaf()-> _name); ::breakpoint(); } while (0); |
| 1112 | } |
| 1113 | #endif |
| 1114 | // Always process arraycopy's destination object since |
| 1115 | // we need to add all possible edges to references in |
| 1116 | // source object. |
| 1117 | if (arg_esc >= PointsToNode::ArgEscape && |
| 1118 | !arg_is_arraycopy_dest) { |
| 1119 | continue; |
| 1120 | } |
| 1121 | PointsToNode::EscapeState es = PointsToNode::ArgEscape; |
| 1122 | if (call->is_ArrayCopy()) { |
| 1123 | ArrayCopyNode* ac = call->as_ArrayCopy(); |
| 1124 | if (ac->is_clonebasic() || |
| 1125 | ac->is_arraycopy_validated() || |
| 1126 | ac->is_copyof_validated() || |
| 1127 | ac->is_copyofrange_validated()) { |
| 1128 | es = PointsToNode::NoEscape; |
| 1129 | } |
| 1130 | } |
| 1131 | set_escape_state(arg_ptn, es); |
| 1132 | if (arg_is_arraycopy_dest) { |
| 1133 | Node* src = call->in(TypeFunc::Parms); |
| 1134 | if (src->is_AddP()) { |
| 1135 | src = get_addp_base(src); |
| 1136 | } |
| 1137 | PointsToNode* src_ptn = ptnode_adr(src->_idx); |
| 1138 | assert(src_ptn != NULL, "should be registered")do { if (!(src_ptn != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1138, "assert(" "src_ptn != __null" ") failed", "should be registered" ); ::breakpoint(); } } while (0); |
| 1139 | if (arg_ptn != src_ptn) { |
| 1140 | // Special arraycopy edge: |
| 1141 | // A destination object's field can't have the source object |
| 1142 | // as base since objects escape states are not related. |
| 1143 | // Only escape state of destination object's fields affects |
| 1144 | // escape state of fields in source object. |
| 1145 | add_arraycopy(call, es, src_ptn, arg_ptn); |
| 1146 | } |
| 1147 | } |
| 1148 | } |
| 1149 | } |
| 1150 | break; |
| 1151 | } |
| 1152 | case Op_CallStaticJava: { |
| 1153 | // For a static call, we know exactly what method is being called. |
| 1154 | // Use bytecode estimator to record the call's escape affects |
| 1155 | #ifdef ASSERT1 |
| 1156 | const char* name = call->as_CallStaticJava()->_name; |
| 1157 | assert((name == NULL || strcmp(name, "uncommon_trap") != 0), "normal calls only")do { if (!((name == __null || strcmp(name, "uncommon_trap") != 0))) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1157, "assert(" "(name == __null || strcmp(name, \"uncommon_trap\") != 0)" ") failed", "normal calls only"); ::breakpoint(); } } while ( 0); |
| 1158 | #endif |
| 1159 | ciMethod* meth = call->as_CallJava()->method(); |
| 1160 | if ((meth != NULL__null) && meth->is_boxing_method()) { |
| 1161 | break; // Boxing methods do not modify any oops. |
| 1162 | } |
| 1163 | BCEscapeAnalyzer* call_analyzer = (meth !=NULL__null) ? meth->get_bcea() : NULL__null; |
| 1164 | // fall-through if not a Java method or no analyzer information |
| 1165 | if (call_analyzer != NULL__null) { |
| 1166 | PointsToNode* call_ptn = ptnode_adr(call->_idx); |
| 1167 | const TypeTuple* d = call->tf()->domain(); |
| 1168 | for (uint i = TypeFunc::Parms; i < d->cnt(); i++) { |
| 1169 | const Type* at = d->field_at(i); |
| 1170 | int k = i - TypeFunc::Parms; |
| 1171 | Node* arg = call->in(i); |
| 1172 | PointsToNode* arg_ptn = ptnode_adr(arg->_idx); |
| 1173 | if (at->isa_ptr() != NULL__null && |
| 1174 | call_analyzer->is_arg_returned(k)) { |
| 1175 | // The call returns arguments. |
| 1176 | if (call_ptn != NULL__null) { // Is call's result used? |
| 1177 | assert(call_ptn->is_LocalVar(), "node should be registered")do { if (!(call_ptn->is_LocalVar())) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1177, "assert(" "call_ptn->is_LocalVar()" ") failed", "node should be registered" ); ::breakpoint(); } } while (0); |
| 1178 | assert(arg_ptn != NULL, "node should be registered")do { if (!(arg_ptn != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1178, "assert(" "arg_ptn != __null" ") failed", "node should be registered" ); ::breakpoint(); } } while (0); |
| 1179 | add_edge(call_ptn, arg_ptn); |
| 1180 | } |
| 1181 | } |
| 1182 | if (at->isa_oopptr() != NULL__null && |
| 1183 | arg_ptn->escape_state() < PointsToNode::GlobalEscape) { |
| 1184 | if (!call_analyzer->is_arg_stack(k)) { |
| 1185 | // The argument global escapes |
| 1186 | set_escape_state(arg_ptn, PointsToNode::GlobalEscape); |
| 1187 | } else { |
| 1188 | set_escape_state(arg_ptn, PointsToNode::ArgEscape); |
| 1189 | if (!call_analyzer->is_arg_local(k)) { |
| 1190 | // The argument itself doesn't escape, but any fields might |
| 1191 | set_fields_escape_state(arg_ptn, PointsToNode::GlobalEscape); |
| 1192 | } |
| 1193 | } |
| 1194 | } |
| 1195 | } |
| 1196 | if (call_ptn != NULL__null && call_ptn->is_LocalVar()) { |
| 1197 | // The call returns arguments. |
| 1198 | assert(call_ptn->edge_count() > 0, "sanity")do { if (!(call_ptn->edge_count() > 0)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1198, "assert(" "call_ptn->edge_count() > 0" ") failed" , "sanity"); ::breakpoint(); } } while (0); |
| 1199 | if (!call_analyzer->is_return_local()) { |
| 1200 | // Returns also unknown object. |
| 1201 | add_edge(call_ptn, phantom_obj); |
| 1202 | } |
| 1203 | } |
| 1204 | break; |
| 1205 | } |
| 1206 | } |
| 1207 | default: { |
| 1208 | // Fall-through here if not a Java method or no analyzer information |
| 1209 | // or some other type of call, assume the worst case: all arguments |
| 1210 | // globally escape. |
| 1211 | const TypeTuple* d = call->tf()->domain(); |
| 1212 | for (uint i = TypeFunc::Parms; i < d->cnt(); i++) { |
| 1213 | const Type* at = d->field_at(i); |
| 1214 | if (at->isa_oopptr() != NULL__null) { |
| 1215 | Node* arg = call->in(i); |
| 1216 | if (arg->is_AddP()) { |
| 1217 | arg = get_addp_base(arg); |
| 1218 | } |
| 1219 | assert(ptnode_adr(arg->_idx) != NULL, "should be defined already")do { if (!(ptnode_adr(arg->_idx) != __null)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1219, "assert(" "ptnode_adr(arg->_idx) != __null" ") failed" , "should be defined already"); ::breakpoint(); } } while (0); |
| 1220 | set_escape_state(ptnode_adr(arg->_idx), PointsToNode::GlobalEscape); |
| 1221 | } |
| 1222 | } |
| 1223 | } |
| 1224 | } |
| 1225 | } |
| 1226 | |
| 1227 | |
| 1228 | // Finish Graph construction. |
| 1229 | bool ConnectionGraph::complete_connection_graph( |
| 1230 | GrowableArray<PointsToNode*>& ptnodes_worklist, |
| 1231 | GrowableArray<JavaObjectNode*>& non_escaped_allocs_worklist, |
| 1232 | GrowableArray<JavaObjectNode*>& java_objects_worklist, |
| 1233 | GrowableArray<FieldNode*>& oop_fields_worklist) { |
| 1234 | // Normally only 1-3 passes needed to build Connection Graph depending |
| 1235 | // on graph complexity. Observed 8 passes in jvm2008 compiler.compiler. |
| 1236 | // Set limit to 20 to catch situation when something did go wrong and |
| 1237 | // bailout Escape Analysis. |
| 1238 | // Also limit build time to 20 sec (60 in debug VM), EscapeAnalysisTimeout flag. |
| 1239 | #define GRAPH_BUILD_ITER_LIMIT 20 |
| 1240 | |
| 1241 | // Propagate GlobalEscape and ArgEscape escape states and check that |
| 1242 | // we still have non-escaping objects. The method pushs on _worklist |
| 1243 | // Field nodes which reference phantom_object. |
| 1244 | if (!find_non_escaped_objects(ptnodes_worklist, non_escaped_allocs_worklist)) { |
| 1245 | return false; // Nothing to do. |
| 1246 | } |
| 1247 | // Now propagate references to all JavaObject nodes. |
| 1248 | int java_objects_length = java_objects_worklist.length(); |
| 1249 | elapsedTimer build_time; |
| 1250 | build_time.start(); |
| 1251 | elapsedTimer time; |
| 1252 | bool timeout = false; |
| 1253 | int new_edges = 1; |
| 1254 | int iterations = 0; |
| 1255 | do { |
| 1256 | while ((new_edges > 0) && |
| 1257 | (iterations++ < GRAPH_BUILD_ITER_LIMIT)) { |
| 1258 | double start_time = time.seconds(); |
| 1259 | time.start(); |
| 1260 | new_edges = 0; |
| 1261 | // Propagate references to phantom_object for nodes pushed on _worklist |
| 1262 | // by find_non_escaped_objects() and find_field_value(). |
| 1263 | new_edges += add_java_object_edges(phantom_obj, false); |
| 1264 | for (int next = 0; next < java_objects_length; ++next) { |
| 1265 | JavaObjectNode* ptn = java_objects_worklist.at(next); |
| 1266 | new_edges += add_java_object_edges(ptn, true); |
| 1267 | |
| 1268 | #define SAMPLE_SIZE 4 |
| 1269 | if ((next % SAMPLE_SIZE) == 0) { |
| 1270 | // Each 4 iterations calculate how much time it will take |
| 1271 | // to complete graph construction. |
| 1272 | time.stop(); |
| 1273 | // Poll for requests from shutdown mechanism to quiesce compiler |
| 1274 | // because Connection graph construction may take long time. |
| 1275 | CompileBroker::maybe_block(); |
| 1276 | double stop_time = time.seconds(); |
| 1277 | double time_per_iter = (stop_time - start_time) / (double)SAMPLE_SIZE; |
| 1278 | double time_until_end = time_per_iter * (double)(java_objects_length - next); |
| 1279 | if ((start_time + time_until_end) >= EscapeAnalysisTimeout) { |
| 1280 | timeout = true; |
| 1281 | break; // Timeout |
| 1282 | } |
| 1283 | start_time = stop_time; |
| 1284 | time.start(); |
| 1285 | } |
| 1286 | #undef SAMPLE_SIZE |
| 1287 | |
| 1288 | } |
| 1289 | if (timeout) break; |
| 1290 | if (new_edges > 0) { |
| 1291 | // Update escape states on each iteration if graph was updated. |
| 1292 | if (!find_non_escaped_objects(ptnodes_worklist, non_escaped_allocs_worklist)) { |
| 1293 | return false; // Nothing to do. |
| 1294 | } |
| 1295 | } |
| 1296 | time.stop(); |
| 1297 | if (time.seconds() >= EscapeAnalysisTimeout) { |
| 1298 | timeout = true; |
| 1299 | break; |
| 1300 | } |
| 1301 | } |
| 1302 | if ((iterations < GRAPH_BUILD_ITER_LIMIT) && !timeout) { |
| 1303 | time.start(); |
| 1304 | // Find fields which have unknown value. |
| 1305 | int fields_length = oop_fields_worklist.length(); |
| 1306 | for (int next = 0; next < fields_length; next++) { |
| 1307 | FieldNode* field = oop_fields_worklist.at(next); |
| 1308 | if (field->edge_count() == 0) { |
| 1309 | new_edges += find_field_value(field); |
| 1310 | // This code may added new edges to phantom_object. |
| 1311 | // Need an other cycle to propagate references to phantom_object. |
| 1312 | } |
| 1313 | } |
| 1314 | time.stop(); |
| 1315 | if (time.seconds() >= EscapeAnalysisTimeout) { |
| 1316 | timeout = true; |
| 1317 | break; |
| 1318 | } |
| 1319 | } else { |
| 1320 | new_edges = 0; // Bailout |
| 1321 | } |
| 1322 | } while (new_edges > 0); |
| 1323 | |
| 1324 | build_time.stop(); |
| 1325 | _build_time = build_time.seconds(); |
| 1326 | _build_iterations = iterations; |
| 1327 | |
| 1328 | // Bailout if passed limits. |
| 1329 | if ((iterations >= GRAPH_BUILD_ITER_LIMIT) || timeout) { |
| 1330 | Compile* C = _compile; |
| 1331 | if (C->log() != NULL__null) { |
| 1332 | C->log()->begin_elem("connectionGraph_bailout reason='reached "); |
| 1333 | C->log()->text("%s", timeout ? "time" : "iterations"); |
| 1334 | C->log()->end_elem(" limit'"); |
| 1335 | } |
| 1336 | assert(ExitEscapeAnalysisOnTimeout, "infinite EA connection graph build during invocation %d (%f sec, %d iterations) with %d nodes and worklist size %d",do { if (!(ExitEscapeAnalysisOnTimeout)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1337, "assert(" "ExitEscapeAnalysisOnTimeout" ") failed", "infinite EA connection graph build during invocation %d (%f sec, %d iterations) with %d nodes and worklist size %d" , _invocation, _build_time, _build_iterations, nodes_size(), ptnodes_worklist .length()); ::breakpoint(); } } while (0) |
| 1337 | _invocation, _build_time, _build_iterations, nodes_size(), ptnodes_worklist.length())do { if (!(ExitEscapeAnalysisOnTimeout)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1337, "assert(" "ExitEscapeAnalysisOnTimeout" ") failed", "infinite EA connection graph build during invocation %d (%f sec, %d iterations) with %d nodes and worklist size %d" , _invocation, _build_time, _build_iterations, nodes_size(), ptnodes_worklist .length()); ::breakpoint(); } } while (0); |
| 1338 | // Possible infinite build_connection_graph loop, |
| 1339 | // bailout (no changes to ideal graph were made). |
| 1340 | return false; |
| 1341 | } |
| 1342 | |
| 1343 | #undef GRAPH_BUILD_ITER_LIMIT |
| 1344 | |
| 1345 | // Find fields initialized by NULL for non-escaping Allocations. |
| 1346 | int non_escaped_length = non_escaped_allocs_worklist.length(); |
| 1347 | for (int next = 0; next < non_escaped_length; next++) { |
| 1348 | JavaObjectNode* ptn = non_escaped_allocs_worklist.at(next); |
| 1349 | PointsToNode::EscapeState es = ptn->escape_state(); |
| 1350 | assert(es <= PointsToNode::ArgEscape, "sanity")do { if (!(es <= PointsToNode::ArgEscape)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1350, "assert(" "es <= PointsToNode::ArgEscape" ") failed" , "sanity"); ::breakpoint(); } } while (0); |
| 1351 | if (es == PointsToNode::NoEscape) { |
| 1352 | if (find_init_values_null(ptn, _igvn) > 0) { |
| 1353 | // Adding references to NULL object does not change escape states |
| 1354 | // since it does not escape. Also no fields are added to NULL object. |
| 1355 | add_java_object_edges(null_obj, false); |
| 1356 | } |
| 1357 | } |
| 1358 | Node* n = ptn->ideal_node(); |
| 1359 | if (n->is_Allocate()) { |
| 1360 | // The object allocated by this Allocate node will never be |
| 1361 | // seen by an other thread. Mark it so that when it is |
| 1362 | // expanded no MemBarStoreStore is added. |
| 1363 | InitializeNode* ini = n->as_Allocate()->initialization(); |
| 1364 | if (ini != NULL__null) |
| 1365 | ini->set_does_not_escape(); |
| 1366 | } |
| 1367 | } |
| 1368 | return true; // Finished graph construction. |
| 1369 | } |
| 1370 | |
| 1371 | // Propagate GlobalEscape and ArgEscape escape states to all nodes |
| 1372 | // and check that we still have non-escaping java objects. |
| 1373 | bool ConnectionGraph::find_non_escaped_objects(GrowableArray<PointsToNode*>& ptnodes_worklist, |
| 1374 | GrowableArray<JavaObjectNode*>& non_escaped_allocs_worklist) { |
| 1375 | GrowableArray<PointsToNode*> escape_worklist; |
| 1376 | // First, put all nodes with GlobalEscape and ArgEscape states on worklist. |
| 1377 | int ptnodes_length = ptnodes_worklist.length(); |
| 1378 | for (int next = 0; next < ptnodes_length; ++next) { |
| 1379 | PointsToNode* ptn = ptnodes_worklist.at(next); |
| 1380 | if (ptn->escape_state() >= PointsToNode::ArgEscape || |
| 1381 | ptn->fields_escape_state() >= PointsToNode::ArgEscape) { |
| 1382 | escape_worklist.push(ptn); |
| 1383 | } |
| 1384 | } |
| 1385 | // Set escape states to referenced nodes (edges list). |
| 1386 | while (escape_worklist.length() > 0) { |
| 1387 | PointsToNode* ptn = escape_worklist.pop(); |
| 1388 | PointsToNode::EscapeState es = ptn->escape_state(); |
| 1389 | PointsToNode::EscapeState field_es = ptn->fields_escape_state(); |
| 1390 | if (ptn->is_Field() && ptn->as_Field()->is_oop() && |
| 1391 | es >= PointsToNode::ArgEscape) { |
| 1392 | // GlobalEscape or ArgEscape state of field means it has unknown value. |
| 1393 | if (add_edge(ptn, phantom_obj)) { |
| 1394 | // New edge was added |
| 1395 | add_field_uses_to_worklist(ptn->as_Field()); |
| 1396 | } |
| 1397 | } |
| 1398 | for (EdgeIterator i(ptn); i.has_next(); i.next()) { |
| 1399 | PointsToNode* e = i.get(); |
| 1400 | if (e->is_Arraycopy()) { |
| 1401 | assert(ptn->arraycopy_dst(), "sanity")do { if (!(ptn->arraycopy_dst())) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1401, "assert(" "ptn->arraycopy_dst()" ") failed", "sanity" ); ::breakpoint(); } } while (0); |
| 1402 | // Propagate only fields escape state through arraycopy edge. |
| 1403 | if (e->fields_escape_state() < field_es) { |
| 1404 | set_fields_escape_state(e, field_es); |
| 1405 | escape_worklist.push(e); |
| 1406 | } |
| 1407 | } else if (es >= field_es) { |
| 1408 | // fields_escape_state is also set to 'es' if it is less than 'es'. |
| 1409 | if (e->escape_state() < es) { |
| 1410 | set_escape_state(e, es); |
| 1411 | escape_worklist.push(e); |
| 1412 | } |
| 1413 | } else { |
| 1414 | // Propagate field escape state. |
| 1415 | bool es_changed = false; |
| 1416 | if (e->fields_escape_state() < field_es) { |
| 1417 | set_fields_escape_state(e, field_es); |
| 1418 | es_changed = true; |
| 1419 | } |
| 1420 | if ((e->escape_state() < field_es) && |
| 1421 | e->is_Field() && ptn->is_JavaObject() && |
| 1422 | e->as_Field()->is_oop()) { |
| 1423 | // Change escape state of referenced fields. |
| 1424 | set_escape_state(e, field_es); |
| 1425 | es_changed = true; |
| 1426 | } else if (e->escape_state() < es) { |
| 1427 | set_escape_state(e, es); |
| 1428 | es_changed = true; |
| 1429 | } |
| 1430 | if (es_changed) { |
| 1431 | escape_worklist.push(e); |
| 1432 | } |
| 1433 | } |
| 1434 | } |
| 1435 | } |
| 1436 | // Remove escaped objects from non_escaped list. |
| 1437 | for (int next = non_escaped_allocs_worklist.length()-1; next >= 0 ; --next) { |
| 1438 | JavaObjectNode* ptn = non_escaped_allocs_worklist.at(next); |
| 1439 | if (ptn->escape_state() >= PointsToNode::GlobalEscape) { |
| 1440 | non_escaped_allocs_worklist.delete_at(next); |
| 1441 | } |
| 1442 | if (ptn->escape_state() == PointsToNode::NoEscape) { |
| 1443 | // Find fields in non-escaped allocations which have unknown value. |
| 1444 | find_init_values_phantom(ptn); |
| 1445 | } |
| 1446 | } |
| 1447 | return (non_escaped_allocs_worklist.length() > 0); |
| 1448 | } |
| 1449 | |
| 1450 | // Add all references to JavaObject node by walking over all uses. |
| 1451 | int ConnectionGraph::add_java_object_edges(JavaObjectNode* jobj, bool populate_worklist) { |
| 1452 | int new_edges = 0; |
| 1453 | if (populate_worklist) { |
| 1454 | // Populate _worklist by uses of jobj's uses. |
| 1455 | for (UseIterator i(jobj); i.has_next(); i.next()) { |
| 1456 | PointsToNode* use = i.get(); |
| 1457 | if (use->is_Arraycopy()) { |
| 1458 | continue; |
| 1459 | } |
| 1460 | add_uses_to_worklist(use); |
| 1461 | if (use->is_Field() && use->as_Field()->is_oop()) { |
| 1462 | // Put on worklist all field's uses (loads) and |
| 1463 | // related field nodes (same base and offset). |
| 1464 | add_field_uses_to_worklist(use->as_Field()); |
| 1465 | } |
| 1466 | } |
| 1467 | } |
| 1468 | for (int l = 0; l < _worklist.length(); l++) { |
| 1469 | PointsToNode* use = _worklist.at(l); |
| 1470 | if (PointsToNode::is_base_use(use)) { |
| 1471 | // Add reference from jobj to field and from field to jobj (field's base). |
| 1472 | use = PointsToNode::get_use_node(use)->as_Field(); |
| 1473 | if (add_base(use->as_Field(), jobj)) { |
| 1474 | new_edges++; |
| 1475 | } |
| 1476 | continue; |
| 1477 | } |
| 1478 | assert(!use->is_JavaObject(), "sanity")do { if (!(!use->is_JavaObject())) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1478, "assert(" "!use->is_JavaObject()" ") failed", "sanity" ); ::breakpoint(); } } while (0); |
| 1479 | if (use->is_Arraycopy()) { |
| 1480 | if (jobj == null_obj) { // NULL object does not have field edges |
| 1481 | continue; |
| 1482 | } |
| 1483 | // Added edge from Arraycopy node to arraycopy's source java object |
| 1484 | if (add_edge(use, jobj)) { |
| 1485 | jobj->set_arraycopy_src(); |
| 1486 | new_edges++; |
| 1487 | } |
| 1488 | // and stop here. |
| 1489 | continue; |
| 1490 | } |
| 1491 | if (!add_edge(use, jobj)) { |
| 1492 | continue; // No new edge added, there was such edge already. |
| 1493 | } |
| 1494 | new_edges++; |
| 1495 | if (use->is_LocalVar()) { |
| 1496 | add_uses_to_worklist(use); |
| 1497 | if (use->arraycopy_dst()) { |
| 1498 | for (EdgeIterator i(use); i.has_next(); i.next()) { |
| 1499 | PointsToNode* e = i.get(); |
| 1500 | if (e->is_Arraycopy()) { |
| 1501 | if (jobj == null_obj) { // NULL object does not have field edges |
| 1502 | continue; |
| 1503 | } |
| 1504 | // Add edge from arraycopy's destination java object to Arraycopy node. |
| 1505 | if (add_edge(jobj, e)) { |
| 1506 | new_edges++; |
| 1507 | jobj->set_arraycopy_dst(); |
| 1508 | } |
| 1509 | } |
| 1510 | } |
| 1511 | } |
| 1512 | } else { |
| 1513 | // Added new edge to stored in field values. |
| 1514 | // Put on worklist all field's uses (loads) and |
| 1515 | // related field nodes (same base and offset). |
| 1516 | add_field_uses_to_worklist(use->as_Field()); |
| 1517 | } |
| 1518 | } |
| 1519 | _worklist.clear(); |
| 1520 | _in_worklist.reset(); |
| 1521 | return new_edges; |
| 1522 | } |
| 1523 | |
| 1524 | // Put on worklist all related field nodes. |
| 1525 | void ConnectionGraph::add_field_uses_to_worklist(FieldNode* field) { |
| 1526 | assert(field->is_oop(), "sanity")do { if (!(field->is_oop())) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1526, "assert(" "field->is_oop()" ") failed", "sanity"); ::breakpoint(); } } while (0); |
| 1527 | int offset = field->offset(); |
| 1528 | add_uses_to_worklist(field); |
| 1529 | // Loop over all bases of this field and push on worklist Field nodes |
| 1530 | // with the same offset and base (since they may reference the same field). |
| 1531 | for (BaseIterator i(field); i.has_next(); i.next()) { |
| 1532 | PointsToNode* base = i.get(); |
| 1533 | add_fields_to_worklist(field, base); |
| 1534 | // Check if the base was source object of arraycopy and go over arraycopy's |
| 1535 | // destination objects since values stored to a field of source object are |
| 1536 | // accessable by uses (loads) of fields of destination objects. |
| 1537 | if (base->arraycopy_src()) { |
| 1538 | for (UseIterator j(base); j.has_next(); j.next()) { |
| 1539 | PointsToNode* arycp = j.get(); |
| 1540 | if (arycp->is_Arraycopy()) { |
| 1541 | for (UseIterator k(arycp); k.has_next(); k.next()) { |
| 1542 | PointsToNode* abase = k.get(); |
| 1543 | if (abase->arraycopy_dst() && abase != base) { |
| 1544 | // Look for the same arraycopy reference. |
| 1545 | add_fields_to_worklist(field, abase); |
| 1546 | } |
| 1547 | } |
| 1548 | } |
| 1549 | } |
| 1550 | } |
| 1551 | } |
| 1552 | } |
| 1553 | |
| 1554 | // Put on worklist all related field nodes. |
| 1555 | void ConnectionGraph::add_fields_to_worklist(FieldNode* field, PointsToNode* base) { |
| 1556 | int offset = field->offset(); |
| 1557 | if (base->is_LocalVar()) { |
| 1558 | for (UseIterator j(base); j.has_next(); j.next()) { |
| 1559 | PointsToNode* f = j.get(); |
| 1560 | if (PointsToNode::is_base_use(f)) { // Field |
| 1561 | f = PointsToNode::get_use_node(f); |
| 1562 | if (f == field || !f->as_Field()->is_oop()) { |
| 1563 | continue; |
| 1564 | } |
| 1565 | int offs = f->as_Field()->offset(); |
| 1566 | if (offs == offset || offset == Type::OffsetBot || offs == Type::OffsetBot) { |
| 1567 | add_to_worklist(f); |
| 1568 | } |
| 1569 | } |
| 1570 | } |
| 1571 | } else { |
| 1572 | assert(base->is_JavaObject(), "sanity")do { if (!(base->is_JavaObject())) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1572, "assert(" "base->is_JavaObject()" ") failed", "sanity" ); ::breakpoint(); } } while (0); |
| 1573 | if (// Skip phantom_object since it is only used to indicate that |
| 1574 | // this field's content globally escapes. |
| 1575 | (base != phantom_obj) && |
| 1576 | // NULL object node does not have fields. |
| 1577 | (base != null_obj)) { |
| 1578 | for (EdgeIterator i(base); i.has_next(); i.next()) { |
| 1579 | PointsToNode* f = i.get(); |
| 1580 | // Skip arraycopy edge since store to destination object field |
| 1581 | // does not update value in source object field. |
| 1582 | if (f->is_Arraycopy()) { |
| 1583 | assert(base->arraycopy_dst(), "sanity")do { if (!(base->arraycopy_dst())) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1583, "assert(" "base->arraycopy_dst()" ") failed", "sanity" ); ::breakpoint(); } } while (0); |
| 1584 | continue; |
| 1585 | } |
| 1586 | if (f == field || !f->as_Field()->is_oop()) { |
| 1587 | continue; |
| 1588 | } |
| 1589 | int offs = f->as_Field()->offset(); |
| 1590 | if (offs == offset || offset == Type::OffsetBot || offs == Type::OffsetBot) { |
| 1591 | add_to_worklist(f); |
| 1592 | } |
| 1593 | } |
| 1594 | } |
| 1595 | } |
| 1596 | } |
| 1597 | |
| 1598 | // Find fields which have unknown value. |
| 1599 | int ConnectionGraph::find_field_value(FieldNode* field) { |
| 1600 | // Escaped fields should have init value already. |
| 1601 | assert(field->escape_state() == PointsToNode::NoEscape, "sanity")do { if (!(field->escape_state() == PointsToNode::NoEscape )) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1601, "assert(" "field->escape_state() == PointsToNode::NoEscape" ") failed", "sanity"); ::breakpoint(); } } while (0); |
| 1602 | int new_edges = 0; |
| 1603 | for (BaseIterator i(field); i.has_next(); i.next()) { |
| 1604 | PointsToNode* base = i.get(); |
| 1605 | if (base->is_JavaObject()) { |
| 1606 | // Skip Allocate's fields which will be processed later. |
| 1607 | if (base->ideal_node()->is_Allocate()) { |
| 1608 | return 0; |
| 1609 | } |
| 1610 | assert(base == null_obj, "only NULL ptr base expected here")do { if (!(base == null_obj)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1610, "assert(" "base == null_obj" ") failed", "only NULL ptr base expected here" ); ::breakpoint(); } } while (0); |
| 1611 | } |
| 1612 | } |
| 1613 | if (add_edge(field, phantom_obj)) { |
| 1614 | // New edge was added |
| 1615 | new_edges++; |
| 1616 | add_field_uses_to_worklist(field); |
| 1617 | } |
| 1618 | return new_edges; |
| 1619 | } |
| 1620 | |
| 1621 | // Find fields initializing values for allocations. |
| 1622 | int ConnectionGraph::find_init_values_phantom(JavaObjectNode* pta) { |
| 1623 | assert(pta->escape_state() == PointsToNode::NoEscape, "Not escaped Allocate nodes only")do { if (!(pta->escape_state() == PointsToNode::NoEscape)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1623, "assert(" "pta->escape_state() == PointsToNode::NoEscape" ") failed", "Not escaped Allocate nodes only"); ::breakpoint (); } } while (0); |
| 1624 | Node* alloc = pta->ideal_node(); |
| 1625 | |
| 1626 | // Do nothing for Allocate nodes since its fields values are |
| 1627 | // "known" unless they are initialized by arraycopy/clone. |
| 1628 | if (alloc->is_Allocate() && !pta->arraycopy_dst()) { |
| 1629 | return 0; |
| 1630 | } |
| 1631 | assert(pta->arraycopy_dst() || alloc->as_CallStaticJava(), "sanity")do { if (!(pta->arraycopy_dst() || alloc->as_CallStaticJava ())) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1631, "assert(" "pta->arraycopy_dst() || alloc->as_CallStaticJava()" ") failed", "sanity"); ::breakpoint(); } } while (0); |
| 1632 | #ifdef ASSERT1 |
| 1633 | if (!pta->arraycopy_dst() && alloc->as_CallStaticJava()->method() == NULL__null) { |
| 1634 | const char* name = alloc->as_CallStaticJava()->_name; |
| 1635 | assert(strncmp(name, "_multianewarray", 15) == 0, "sanity")do { if (!(strncmp(name, "_multianewarray", 15) == 0)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1635, "assert(" "strncmp(name, \"_multianewarray\", 15) == 0" ") failed", "sanity"); ::breakpoint(); } } while (0); |
| 1636 | } |
| 1637 | #endif |
| 1638 | // Non-escaped allocation returned from Java or runtime call have unknown values in fields. |
| 1639 | int new_edges = 0; |
| 1640 | for (EdgeIterator i(pta); i.has_next(); i.next()) { |
| 1641 | PointsToNode* field = i.get(); |
| 1642 | if (field->is_Field() && field->as_Field()->is_oop()) { |
| 1643 | if (add_edge(field, phantom_obj)) { |
| 1644 | // New edge was added |
| 1645 | new_edges++; |
| 1646 | add_field_uses_to_worklist(field->as_Field()); |
| 1647 | } |
| 1648 | } |
| 1649 | } |
| 1650 | return new_edges; |
| 1651 | } |
| 1652 | |
| 1653 | // Find fields initializing values for allocations. |
| 1654 | int ConnectionGraph::find_init_values_null(JavaObjectNode* pta, PhaseTransform* phase) { |
| 1655 | assert(pta->escape_state() == PointsToNode::NoEscape, "Not escaped Allocate nodes only")do { if (!(pta->escape_state() == PointsToNode::NoEscape)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1655, "assert(" "pta->escape_state() == PointsToNode::NoEscape" ") failed", "Not escaped Allocate nodes only"); ::breakpoint (); } } while (0); |
| 1656 | Node* alloc = pta->ideal_node(); |
| 1657 | // Do nothing for Call nodes since its fields values are unknown. |
| 1658 | if (!alloc->is_Allocate()) { |
| 1659 | return 0; |
| 1660 | } |
| 1661 | InitializeNode* ini = alloc->as_Allocate()->initialization(); |
| 1662 | bool visited_bottom_offset = false; |
| 1663 | GrowableArray<int> offsets_worklist; |
| 1664 | int new_edges = 0; |
| 1665 | |
| 1666 | // Check if an oop field's initializing value is recorded and add |
| 1667 | // a corresponding NULL if field's value if it is not recorded. |
| 1668 | // Connection Graph does not record a default initialization by NULL |
| 1669 | // captured by Initialize node. |
| 1670 | // |
| 1671 | for (EdgeIterator i(pta); i.has_next(); i.next()) { |
| 1672 | PointsToNode* field = i.get(); // Field (AddP) |
| 1673 | if (!field->is_Field() || !field->as_Field()->is_oop()) { |
| 1674 | continue; // Not oop field |
| 1675 | } |
| 1676 | int offset = field->as_Field()->offset(); |
| 1677 | if (offset == Type::OffsetBot) { |
| 1678 | if (!visited_bottom_offset) { |
| 1679 | // OffsetBot is used to reference array's element, |
| 1680 | // always add reference to NULL to all Field nodes since we don't |
| 1681 | // known which element is referenced. |
| 1682 | if (add_edge(field, null_obj)) { |
| 1683 | // New edge was added |
| 1684 | new_edges++; |
| 1685 | add_field_uses_to_worklist(field->as_Field()); |
| 1686 | visited_bottom_offset = true; |
| 1687 | } |
| 1688 | } |
| 1689 | } else { |
| 1690 | // Check only oop fields. |
| 1691 | const Type* adr_type = field->ideal_node()->as_AddP()->bottom_type(); |
| 1692 | if (adr_type->isa_rawptr()) { |
| 1693 | #ifdef ASSERT1 |
| 1694 | // Raw pointers are used for initializing stores so skip it |
| 1695 | // since it should be recorded already |
| 1696 | Node* base = get_addp_base(field->ideal_node()); |
| 1697 | assert(adr_type->isa_rawptr() && is_captured_store_address(field->ideal_node()), "unexpected pointer type")do { if (!(adr_type->isa_rawptr() && is_captured_store_address (field->ideal_node()))) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1697, "assert(" "adr_type->isa_rawptr() && is_captured_store_address(field->ideal_node())" ") failed", "unexpected pointer type"); ::breakpoint(); } } while (0); |
| 1698 | #endif |
| 1699 | continue; |
| 1700 | } |
| 1701 | if (!offsets_worklist.contains(offset)) { |
| 1702 | offsets_worklist.append(offset); |
| 1703 | Node* value = NULL__null; |
| 1704 | if (ini != NULL__null) { |
| 1705 | // StoreP::memory_type() == T_ADDRESS |
| 1706 | BasicType ft = UseCompressedOops ? T_NARROWOOP : T_ADDRESS; |
| 1707 | Node* store = ini->find_captured_store(offset, type2aelembytes(ft, true), phase); |
| 1708 | // Make sure initializing store has the same type as this AddP. |
| 1709 | // This AddP may reference non existing field because it is on a |
| 1710 | // dead branch of bimorphic call which is not eliminated yet. |
| 1711 | if (store != NULL__null && store->is_Store() && |
| 1712 | store->as_Store()->memory_type() == ft) { |
| 1713 | value = store->in(MemNode::ValueIn); |
| 1714 | #ifdef ASSERT1 |
| 1715 | if (VerifyConnectionGraph) { |
| 1716 | // Verify that AddP already points to all objects the value points to. |
| 1717 | PointsToNode* val = ptnode_adr(value->_idx); |
| 1718 | assert((val != NULL), "should be processed already")do { if (!((val != __null))) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1718, "assert(" "(val != __null)" ") failed", "should be processed already" ); ::breakpoint(); } } while (0); |
| 1719 | PointsToNode* missed_obj = NULL__null; |
| 1720 | if (val->is_JavaObject()) { |
| 1721 | if (!field->points_to(val->as_JavaObject())) { |
| 1722 | missed_obj = val; |
| 1723 | } |
| 1724 | } else { |
| 1725 | if (!val->is_LocalVar() || (val->edge_count() == 0)) { |
| 1726 | tty->print_cr("----------init store has invalid value -----"); |
| 1727 | store->dump(); |
| 1728 | val->dump(); |
| 1729 | assert(val->is_LocalVar() && (val->edge_count() > 0), "should be processed already")do { if (!(val->is_LocalVar() && (val->edge_count () > 0))) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1729, "assert(" "val->is_LocalVar() && (val->edge_count() > 0)" ") failed", "should be processed already"); ::breakpoint(); } } while (0); |
| 1730 | } |
| 1731 | for (EdgeIterator j(val); j.has_next(); j.next()) { |
| 1732 | PointsToNode* obj = j.get(); |
| 1733 | if (obj->is_JavaObject()) { |
| 1734 | if (!field->points_to(obj->as_JavaObject())) { |
| 1735 | missed_obj = obj; |
| 1736 | break; |
| 1737 | } |
| 1738 | } |
| 1739 | } |
| 1740 | } |
| 1741 | if (missed_obj != NULL__null) { |
| 1742 | tty->print_cr("----------field---------------------------------"); |
| 1743 | field->dump(); |
| 1744 | tty->print_cr("----------missed referernce to object-----------"); |
| 1745 | missed_obj->dump(); |
| 1746 | tty->print_cr("----------object referernced by init store -----"); |
| 1747 | store->dump(); |
| 1748 | val->dump(); |
| 1749 | assert(!field->points_to(missed_obj->as_JavaObject()), "missed JavaObject reference")do { if (!(!field->points_to(missed_obj->as_JavaObject( )))) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1749, "assert(" "!field->points_to(missed_obj->as_JavaObject())" ") failed", "missed JavaObject reference"); ::breakpoint(); } } while (0); |
| 1750 | } |
| 1751 | } |
| 1752 | #endif |
| 1753 | } else { |
| 1754 | // There could be initializing stores which follow allocation. |
| 1755 | // For example, a volatile field store is not collected |
| 1756 | // by Initialize node. |
| 1757 | // |
| 1758 | // Need to check for dependent loads to separate such stores from |
| 1759 | // stores which follow loads. For now, add initial value NULL so |
| 1760 | // that compare pointers optimization works correctly. |
| 1761 | } |
| 1762 | } |
| 1763 | if (value == NULL__null) { |
| 1764 | // A field's initializing value was not recorded. Add NULL. |
| 1765 | if (add_edge(field, null_obj)) { |
| 1766 | // New edge was added |
| 1767 | new_edges++; |
| 1768 | add_field_uses_to_worklist(field->as_Field()); |
| 1769 | } |
| 1770 | } |
| 1771 | } |
| 1772 | } |
| 1773 | } |
| 1774 | return new_edges; |
| 1775 | } |
| 1776 | |
| 1777 | // Adjust scalar_replaceable state after Connection Graph is built. |
| 1778 | void ConnectionGraph::adjust_scalar_replaceable_state(JavaObjectNode* jobj) { |
| 1779 | // Search for non-escaping objects which are not scalar replaceable |
| 1780 | // and mark them to propagate the state to referenced objects. |
| 1781 | |
| 1782 | for (UseIterator i(jobj); i.has_next(); i.next()) { |
| 1783 | PointsToNode* use = i.get(); |
| 1784 | if (use->is_Arraycopy()) { |
| 1785 | continue; |
| 1786 | } |
| 1787 | if (use->is_Field()) { |
| 1788 | FieldNode* field = use->as_Field(); |
| 1789 | assert(field->is_oop() && field->scalar_replaceable(), "sanity")do { if (!(field->is_oop() && field->scalar_replaceable ())) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1789, "assert(" "field->is_oop() && field->scalar_replaceable()" ") failed", "sanity"); ::breakpoint(); } } while (0); |
| 1790 | // 1. An object is not scalar replaceable if the field into which it is |
| 1791 | // stored has unknown offset (stored into unknown element of an array). |
| 1792 | if (field->offset() == Type::OffsetBot) { |
| 1793 | jobj->set_scalar_replaceable(false); |
| 1794 | return; |
| 1795 | } |
| 1796 | // 2. An object is not scalar replaceable if the field into which it is |
| 1797 | // stored has multiple bases one of which is null. |
| 1798 | if (field->base_count() > 1) { |
| 1799 | for (BaseIterator i(field); i.has_next(); i.next()) { |
| 1800 | PointsToNode* base = i.get(); |
| 1801 | if (base == null_obj) { |
| 1802 | jobj->set_scalar_replaceable(false); |
| 1803 | return; |
| 1804 | } |
| 1805 | } |
| 1806 | } |
| 1807 | } |
| 1808 | assert(use->is_Field() || use->is_LocalVar(), "sanity")do { if (!(use->is_Field() || use->is_LocalVar())) { (* g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1808, "assert(" "use->is_Field() || use->is_LocalVar()" ") failed", "sanity"); ::breakpoint(); } } while (0); |
| 1809 | // 3. An object is not scalar replaceable if it is merged with other objects. |
| 1810 | for (EdgeIterator j(use); j.has_next(); j.next()) { |
| 1811 | PointsToNode* ptn = j.get(); |
| 1812 | if (ptn->is_JavaObject() && ptn != jobj) { |
| 1813 | // Mark all objects. |
| 1814 | jobj->set_scalar_replaceable(false); |
| 1815 | ptn->set_scalar_replaceable(false); |
| 1816 | } |
| 1817 | } |
| 1818 | if (!jobj->scalar_replaceable()) { |
| 1819 | return; |
| 1820 | } |
| 1821 | } |
| 1822 | |
| 1823 | for (EdgeIterator j(jobj); j.has_next(); j.next()) { |
| 1824 | if (j.get()->is_Arraycopy()) { |
| 1825 | continue; |
| 1826 | } |
| 1827 | |
| 1828 | // Non-escaping object node should point only to field nodes. |
| 1829 | FieldNode* field = j.get()->as_Field(); |
| 1830 | int offset = field->as_Field()->offset(); |
| 1831 | |
| 1832 | // 4. An object is not scalar replaceable if it has a field with unknown |
| 1833 | // offset (array's element is accessed in loop). |
| 1834 | if (offset == Type::OffsetBot) { |
| 1835 | jobj->set_scalar_replaceable(false); |
| 1836 | return; |
| 1837 | } |
| 1838 | // 5. Currently an object is not scalar replaceable if a LoadStore node |
| 1839 | // access its field since the field value is unknown after it. |
| 1840 | // |
| 1841 | Node* n = field->ideal_node(); |
| 1842 | |
| 1843 | // Test for an unsafe access that was parsed as maybe off heap |
| 1844 | // (with a CheckCastPP to raw memory). |
| 1845 | assert(n->is_AddP(), "expect an address computation")do { if (!(n->is_AddP())) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1845, "assert(" "n->is_AddP()" ") failed", "expect an address computation" ); ::breakpoint(); } } while (0); |
| 1846 | if (n->in(AddPNode::Base)->is_top() && |
| 1847 | n->in(AddPNode::Address)->Opcode() == Op_CheckCastPP) { |
| 1848 | assert(n->in(AddPNode::Address)->bottom_type()->isa_rawptr(), "raw address so raw cast expected")do { if (!(n->in(AddPNode::Address)->bottom_type()-> isa_rawptr())) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1848, "assert(" "n->in(AddPNode::Address)->bottom_type()->isa_rawptr()" ") failed", "raw address so raw cast expected"); ::breakpoint (); } } while (0); |
| 1849 | assert(_igvn->type(n->in(AddPNode::Address)->in(1))->isa_oopptr(), "cast pattern at unsafe access expected")do { if (!(_igvn->type(n->in(AddPNode::Address)->in( 1))->isa_oopptr())) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1849, "assert(" "_igvn->type(n->in(AddPNode::Address)->in(1))->isa_oopptr()" ") failed", "cast pattern at unsafe access expected"); ::breakpoint (); } } while (0); |
| 1850 | jobj->set_scalar_replaceable(false); |
| 1851 | return; |
| 1852 | } |
| 1853 | |
| 1854 | for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { |
| 1855 | Node* u = n->fast_out(i); |
| 1856 | if (u->is_LoadStore() || (u->is_Mem() && u->as_Mem()->is_mismatched_access())) { |
| 1857 | jobj->set_scalar_replaceable(false); |
| 1858 | return; |
| 1859 | } |
| 1860 | } |
| 1861 | |
| 1862 | // 6. Or the address may point to more then one object. This may produce |
| 1863 | // the false positive result (set not scalar replaceable) |
| 1864 | // since the flow-insensitive escape analysis can't separate |
| 1865 | // the case when stores overwrite the field's value from the case |
| 1866 | // when stores happened on different control branches. |
| 1867 | // |
| 1868 | // Note: it will disable scalar replacement in some cases: |
| 1869 | // |
| 1870 | // Point p[] = new Point[1]; |
| 1871 | // p[0] = new Point(); // Will be not scalar replaced |
| 1872 | // |
| 1873 | // but it will save us from incorrect optimizations in next cases: |
| 1874 | // |
| 1875 | // Point p[] = new Point[1]; |
| 1876 | // if ( x ) p[0] = new Point(); // Will be not scalar replaced |
| 1877 | // |
| 1878 | if (field->base_count() > 1) { |
| 1879 | for (BaseIterator i(field); i.has_next(); i.next()) { |
| 1880 | PointsToNode* base = i.get(); |
| 1881 | // Don't take into account LocalVar nodes which |
| 1882 | // may point to only one object which should be also |
| 1883 | // this field's base by now. |
| 1884 | if (base->is_JavaObject() && base != jobj) { |
| 1885 | // Mark all bases. |
| 1886 | jobj->set_scalar_replaceable(false); |
| 1887 | base->set_scalar_replaceable(false); |
| 1888 | } |
| 1889 | } |
| 1890 | } |
| 1891 | } |
| 1892 | } |
| 1893 | |
| 1894 | #ifdef ASSERT1 |
| 1895 | void ConnectionGraph::verify_connection_graph( |
| 1896 | GrowableArray<PointsToNode*>& ptnodes_worklist, |
| 1897 | GrowableArray<JavaObjectNode*>& non_escaped_allocs_worklist, |
| 1898 | GrowableArray<JavaObjectNode*>& java_objects_worklist, |
| 1899 | GrowableArray<Node*>& addp_worklist) { |
| 1900 | // Verify that graph is complete - no new edges could be added. |
| 1901 | int java_objects_length = java_objects_worklist.length(); |
| 1902 | int non_escaped_length = non_escaped_allocs_worklist.length(); |
| 1903 | int new_edges = 0; |
| 1904 | for (int next = 0; next < java_objects_length; ++next) { |
| 1905 | JavaObjectNode* ptn = java_objects_worklist.at(next); |
| 1906 | new_edges += add_java_object_edges(ptn, true); |
| 1907 | } |
| 1908 | assert(new_edges == 0, "graph was not complete")do { if (!(new_edges == 0)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1908, "assert(" "new_edges == 0" ") failed", "graph was not complete" ); ::breakpoint(); } } while (0); |
| 1909 | // Verify that escape state is final. |
| 1910 | int length = non_escaped_allocs_worklist.length(); |
| 1911 | find_non_escaped_objects(ptnodes_worklist, non_escaped_allocs_worklist); |
| 1912 | assert((non_escaped_length == non_escaped_allocs_worklist.length()) &&do { if (!((non_escaped_length == non_escaped_allocs_worklist .length()) && (non_escaped_length == length) && (_worklist.length() == 0))) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1914, "assert(" "(non_escaped_length == non_escaped_allocs_worklist.length()) && (non_escaped_length == length) && (_worklist.length() == 0)" ") failed", "escape state was not final"); ::breakpoint(); } } while (0) |
| 1913 | (non_escaped_length == length) &&do { if (!((non_escaped_length == non_escaped_allocs_worklist .length()) && (non_escaped_length == length) && (_worklist.length() == 0))) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1914, "assert(" "(non_escaped_length == non_escaped_allocs_worklist.length()) && (non_escaped_length == length) && (_worklist.length() == 0)" ") failed", "escape state was not final"); ::breakpoint(); } } while (0) |
| 1914 | (_worklist.length() == 0), "escape state was not final")do { if (!((non_escaped_length == non_escaped_allocs_worklist .length()) && (non_escaped_length == length) && (_worklist.length() == 0))) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1914, "assert(" "(non_escaped_length == non_escaped_allocs_worklist.length()) && (non_escaped_length == length) && (_worklist.length() == 0)" ") failed", "escape state was not final"); ::breakpoint(); } } while (0); |
| 1915 | |
| 1916 | // Verify fields information. |
| 1917 | int addp_length = addp_worklist.length(); |
| 1918 | for (int next = 0; next < addp_length; ++next ) { |
| 1919 | Node* n = addp_worklist.at(next); |
| 1920 | FieldNode* field = ptnode_adr(n->_idx)->as_Field(); |
| 1921 | if (field->is_oop()) { |
| 1922 | // Verify that field has all bases |
| 1923 | Node* base = get_addp_base(n); |
| 1924 | PointsToNode* ptn = ptnode_adr(base->_idx); |
| 1925 | if (ptn->is_JavaObject()) { |
| 1926 | assert(field->has_base(ptn->as_JavaObject()), "sanity")do { if (!(field->has_base(ptn->as_JavaObject()))) { (* g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1926, "assert(" "field->has_base(ptn->as_JavaObject())" ") failed", "sanity"); ::breakpoint(); } } while (0); |
| 1927 | } else { |
| 1928 | assert(ptn->is_LocalVar(), "sanity")do { if (!(ptn->is_LocalVar())) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1928, "assert(" "ptn->is_LocalVar()" ") failed", "sanity" ); ::breakpoint(); } } while (0); |
| 1929 | for (EdgeIterator i(ptn); i.has_next(); i.next()) { |
| 1930 | PointsToNode* e = i.get(); |
| 1931 | if (e->is_JavaObject()) { |
| 1932 | assert(field->has_base(e->as_JavaObject()), "sanity")do { if (!(field->has_base(e->as_JavaObject()))) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1932, "assert(" "field->has_base(e->as_JavaObject())" ") failed", "sanity"); ::breakpoint(); } } while (0); |
| 1933 | } |
| 1934 | } |
| 1935 | } |
| 1936 | // Verify that all fields have initializing values. |
| 1937 | if (field->edge_count() == 0) { |
| 1938 | tty->print_cr("----------field does not have references----------"); |
| 1939 | field->dump(); |
| 1940 | for (BaseIterator i(field); i.has_next(); i.next()) { |
| 1941 | PointsToNode* base = i.get(); |
| 1942 | tty->print_cr("----------field has next base---------------------"); |
| 1943 | base->dump(); |
| 1944 | if (base->is_JavaObject() && (base != phantom_obj) && (base != null_obj)) { |
| 1945 | tty->print_cr("----------base has fields-------------------------"); |
| 1946 | for (EdgeIterator j(base); j.has_next(); j.next()) { |
| 1947 | j.get()->dump(); |
| 1948 | } |
| 1949 | tty->print_cr("----------base has references---------------------"); |
| 1950 | for (UseIterator j(base); j.has_next(); j.next()) { |
| 1951 | j.get()->dump(); |
| 1952 | } |
| 1953 | } |
| 1954 | } |
| 1955 | for (UseIterator i(field); i.has_next(); i.next()) { |
| 1956 | i.get()->dump(); |
| 1957 | } |
| 1958 | assert(field->edge_count() > 0, "sanity")do { if (!(field->edge_count() > 0)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1958, "assert(" "field->edge_count() > 0" ") failed", "sanity"); ::breakpoint(); } } while (0); |
| 1959 | } |
| 1960 | } |
| 1961 | } |
| 1962 | } |
| 1963 | #endif |
| 1964 | |
| 1965 | // Optimize ideal graph. |
| 1966 | void ConnectionGraph::optimize_ideal_graph(GrowableArray<Node*>& ptr_cmp_worklist, |
| 1967 | GrowableArray<MemBarStoreStoreNode*>& storestore_worklist) { |
| 1968 | Compile* C = _compile; |
| 1969 | PhaseIterGVN* igvn = _igvn; |
| 1970 | if (EliminateLocks) { |
| 1971 | // Mark locks before changing ideal graph. |
| 1972 | int cnt = C->macro_count(); |
| 1973 | for (int i = 0; i < cnt; i++) { |
| 1974 | Node *n = C->macro_node(i); |
| 1975 | if (n->is_AbstractLock()) { // Lock and Unlock nodes |
| 1976 | AbstractLockNode* alock = n->as_AbstractLock(); |
| 1977 | if (!alock->is_non_esc_obj()) { |
| 1978 | if (not_global_escape(alock->obj_node())) { |
| 1979 | assert(!alock->is_eliminated() || alock->is_coarsened(), "sanity")do { if (!(!alock->is_eliminated() || alock->is_coarsened ())) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 1979, "assert(" "!alock->is_eliminated() || alock->is_coarsened()" ") failed", "sanity"); ::breakpoint(); } } while (0); |
| 1980 | // The lock could be marked eliminated by lock coarsening |
| 1981 | // code during first IGVN before EA. Replace coarsened flag |
| 1982 | // to eliminate all associated locks/unlocks. |
| 1983 | #ifdef ASSERT1 |
| 1984 | alock->log_lock_optimization(C, "eliminate_lock_set_non_esc3"); |
| 1985 | #endif |
| 1986 | alock->set_non_esc_obj(); |
| 1987 | } |
| 1988 | } |
| 1989 | } |
| 1990 | } |
| 1991 | } |
| 1992 | |
| 1993 | if (OptimizePtrCompare) { |
| 1994 | for (int i = 0; i < ptr_cmp_worklist.length(); i++) { |
| 1995 | Node *n = ptr_cmp_worklist.at(i); |
| 1996 | const TypeInt* tcmp = optimize_ptr_compare(n); |
| 1997 | if (tcmp->singleton()) { |
| 1998 | Node* cmp = igvn->makecon(tcmp); |
| 1999 | #ifndef PRODUCT |
| 2000 | if (PrintOptimizePtrCompare) { |
| 2001 | tty->print_cr("++++ Replaced: %d %s(%d,%d) --> %s", n->_idx, (n->Opcode() == Op_CmpP ? "CmpP" : "CmpN"), n->in(1)->_idx, n->in(2)->_idx, (tcmp == TypeInt::CC_EQ ? "EQ" : "NotEQ")); |
| 2002 | if (Verbose) { |
| 2003 | n->dump(1); |
| 2004 | } |
| 2005 | } |
| 2006 | #endif |
| 2007 | igvn->replace_node(n, cmp); |
| 2008 | } |
| 2009 | } |
| 2010 | } |
| 2011 | |
| 2012 | // For MemBarStoreStore nodes added in library_call.cpp, check |
| 2013 | // escape status of associated AllocateNode and optimize out |
| 2014 | // MemBarStoreStore node if the allocated object never escapes. |
| 2015 | for (int i = 0; i < storestore_worklist.length(); i++) { |
| 2016 | Node* storestore = storestore_worklist.at(i); |
| 2017 | Node* alloc = storestore->in(MemBarNode::Precedent)->in(0); |
| 2018 | if (alloc->is_Allocate() && not_global_escape(alloc)) { |
| 2019 | MemBarNode* mb = MemBarNode::make(C, Op_MemBarCPUOrder, Compile::AliasIdxBot); |
| 2020 | mb->init_req(TypeFunc::Memory, storestore->in(TypeFunc::Memory)); |
| 2021 | mb->init_req(TypeFunc::Control, storestore->in(TypeFunc::Control)); |
| 2022 | igvn->register_new_node_with_optimizer(mb); |
| 2023 | igvn->replace_node(storestore, mb); |
| 2024 | } |
| 2025 | } |
| 2026 | } |
| 2027 | |
| 2028 | // Optimize objects compare. |
| 2029 | const TypeInt* ConnectionGraph::optimize_ptr_compare(Node* n) { |
| 2030 | assert(OptimizePtrCompare, "sanity")do { if (!(OptimizePtrCompare)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2030, "assert(" "OptimizePtrCompare" ") failed", "sanity"); ::breakpoint(); } } while (0); |
| 2031 | assert(n->Opcode() == Op_CmpN || n->Opcode() == Op_CmpP, "must be")do { if (!(n->Opcode() == Op_CmpN || n->Opcode() == Op_CmpP )) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2031, "assert(" "n->Opcode() == Op_CmpN || n->Opcode() == Op_CmpP" ") failed", "must be"); ::breakpoint(); } } while (0); |
| 2032 | const TypeInt* EQ = TypeInt::CC_EQ; // [0] == ZERO |
| 2033 | const TypeInt* NE = TypeInt::CC_GT; // [1] == ONE |
| 2034 | const TypeInt* UNKNOWN = TypeInt::CC; // [-1, 0,1] |
| 2035 | |
| 2036 | PointsToNode* ptn1 = ptnode_adr(n->in(1)->_idx); |
| 2037 | PointsToNode* ptn2 = ptnode_adr(n->in(2)->_idx); |
| 2038 | JavaObjectNode* jobj1 = unique_java_object(n->in(1)); |
| 2039 | JavaObjectNode* jobj2 = unique_java_object(n->in(2)); |
| 2040 | assert(ptn1->is_JavaObject() || ptn1->is_LocalVar(), "sanity")do { if (!(ptn1->is_JavaObject() || ptn1->is_LocalVar() )) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2040, "assert(" "ptn1->is_JavaObject() || ptn1->is_LocalVar()" ") failed", "sanity"); ::breakpoint(); } } while (0); |
| 2041 | assert(ptn2->is_JavaObject() || ptn2->is_LocalVar(), "sanity")do { if (!(ptn2->is_JavaObject() || ptn2->is_LocalVar() )) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2041, "assert(" "ptn2->is_JavaObject() || ptn2->is_LocalVar()" ") failed", "sanity"); ::breakpoint(); } } while (0); |
| 2042 | |
| 2043 | // Check simple cases first. |
| 2044 | if (jobj1 != NULL__null) { |
| 2045 | if (jobj1->escape_state() == PointsToNode::NoEscape) { |
| 2046 | if (jobj1 == jobj2) { |
| 2047 | // Comparing the same not escaping object. |
| 2048 | return EQ; |
| 2049 | } |
| 2050 | Node* obj = jobj1->ideal_node(); |
| 2051 | // Comparing not escaping allocation. |
| 2052 | if ((obj->is_Allocate() || obj->is_CallStaticJava()) && |
| 2053 | !ptn2->points_to(jobj1)) { |
| 2054 | return NE; // This includes nullness check. |
| 2055 | } |
| 2056 | } |
| 2057 | } |
| 2058 | if (jobj2 != NULL__null) { |
| 2059 | if (jobj2->escape_state() == PointsToNode::NoEscape) { |
| 2060 | Node* obj = jobj2->ideal_node(); |
| 2061 | // Comparing not escaping allocation. |
| 2062 | if ((obj->is_Allocate() || obj->is_CallStaticJava()) && |
| 2063 | !ptn1->points_to(jobj2)) { |
| 2064 | return NE; // This includes nullness check. |
| 2065 | } |
| 2066 | } |
| 2067 | } |
| 2068 | if (jobj1 != NULL__null && jobj1 != phantom_obj && |
| 2069 | jobj2 != NULL__null && jobj2 != phantom_obj && |
| 2070 | jobj1->ideal_node()->is_Con() && |
| 2071 | jobj2->ideal_node()->is_Con()) { |
| 2072 | // Klass or String constants compare. Need to be careful with |
| 2073 | // compressed pointers - compare types of ConN and ConP instead of nodes. |
| 2074 | const Type* t1 = jobj1->ideal_node()->get_ptr_type(); |
| 2075 | const Type* t2 = jobj2->ideal_node()->get_ptr_type(); |
| 2076 | if (t1->make_ptr() == t2->make_ptr()) { |
| 2077 | return EQ; |
| 2078 | } else { |
| 2079 | return NE; |
| 2080 | } |
| 2081 | } |
| 2082 | if (ptn1->meet(ptn2)) { |
| 2083 | return UNKNOWN; // Sets are not disjoint |
| 2084 | } |
| 2085 | |
| 2086 | // Sets are disjoint. |
| 2087 | bool set1_has_unknown_ptr = ptn1->points_to(phantom_obj); |
| 2088 | bool set2_has_unknown_ptr = ptn2->points_to(phantom_obj); |
| 2089 | bool set1_has_null_ptr = ptn1->points_to(null_obj); |
| 2090 | bool set2_has_null_ptr = ptn2->points_to(null_obj); |
| 2091 | if ((set1_has_unknown_ptr && set2_has_null_ptr) || |
| 2092 | (set2_has_unknown_ptr && set1_has_null_ptr)) { |
| 2093 | // Check nullness of unknown object. |
| 2094 | return UNKNOWN; |
| 2095 | } |
| 2096 | |
| 2097 | // Disjointness by itself is not sufficient since |
| 2098 | // alias analysis is not complete for escaped objects. |
| 2099 | // Disjoint sets are definitely unrelated only when |
| 2100 | // at least one set has only not escaping allocations. |
| 2101 | if (!set1_has_unknown_ptr && !set1_has_null_ptr) { |
| 2102 | if (ptn1->non_escaping_allocation()) { |
| 2103 | return NE; |
| 2104 | } |
| 2105 | } |
| 2106 | if (!set2_has_unknown_ptr && !set2_has_null_ptr) { |
| 2107 | if (ptn2->non_escaping_allocation()) { |
| 2108 | return NE; |
| 2109 | } |
| 2110 | } |
| 2111 | return UNKNOWN; |
| 2112 | } |
| 2113 | |
| 2114 | // Connection Graph construction functions. |
| 2115 | |
| 2116 | void ConnectionGraph::add_local_var(Node *n, PointsToNode::EscapeState es) { |
| 2117 | PointsToNode* ptadr = _nodes.at(n->_idx); |
| 2118 | if (ptadr != NULL__null) { |
| 2119 | assert(ptadr->is_LocalVar() && ptadr->ideal_node() == n, "sanity")do { if (!(ptadr->is_LocalVar() && ptadr->ideal_node () == n)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2119, "assert(" "ptadr->is_LocalVar() && ptadr->ideal_node() == n" ") failed", "sanity"); ::breakpoint(); } } while (0); |
| 2120 | return; |
| 2121 | } |
| 2122 | Compile* C = _compile; |
| 2123 | ptadr = new (C->comp_arena()) LocalVarNode(this, n, es); |
| 2124 | map_ideal_node(n, ptadr); |
| 2125 | } |
| 2126 | |
| 2127 | void ConnectionGraph::add_java_object(Node *n, PointsToNode::EscapeState es) { |
| 2128 | PointsToNode* ptadr = _nodes.at(n->_idx); |
| 2129 | if (ptadr != NULL__null) { |
| 2130 | assert(ptadr->is_JavaObject() && ptadr->ideal_node() == n, "sanity")do { if (!(ptadr->is_JavaObject() && ptadr->ideal_node () == n)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2130, "assert(" "ptadr->is_JavaObject() && ptadr->ideal_node() == n" ") failed", "sanity"); ::breakpoint(); } } while (0); |
| 2131 | return; |
| 2132 | } |
| 2133 | Compile* C = _compile; |
| 2134 | ptadr = new (C->comp_arena()) JavaObjectNode(this, n, es); |
| 2135 | map_ideal_node(n, ptadr); |
| 2136 | } |
| 2137 | |
| 2138 | void ConnectionGraph::add_field(Node *n, PointsToNode::EscapeState es, int offset) { |
| 2139 | PointsToNode* ptadr = _nodes.at(n->_idx); |
| 2140 | if (ptadr != NULL__null) { |
| 2141 | assert(ptadr->is_Field() && ptadr->ideal_node() == n, "sanity")do { if (!(ptadr->is_Field() && ptadr->ideal_node () == n)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2141, "assert(" "ptadr->is_Field() && ptadr->ideal_node() == n" ") failed", "sanity"); ::breakpoint(); } } while (0); |
| 2142 | return; |
| 2143 | } |
| 2144 | bool unsafe = false; |
| 2145 | bool is_oop = is_oop_field(n, offset, &unsafe); |
| 2146 | if (unsafe) { |
| 2147 | es = PointsToNode::GlobalEscape; |
| 2148 | } |
| 2149 | Compile* C = _compile; |
| 2150 | FieldNode* field = new (C->comp_arena()) FieldNode(this, n, es, offset, is_oop); |
| 2151 | map_ideal_node(n, field); |
| 2152 | } |
| 2153 | |
| 2154 | void ConnectionGraph::add_arraycopy(Node *n, PointsToNode::EscapeState es, |
| 2155 | PointsToNode* src, PointsToNode* dst) { |
| 2156 | assert(!src->is_Field() && !dst->is_Field(), "only for JavaObject and LocalVar")do { if (!(!src->is_Field() && !dst->is_Field() )) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2156, "assert(" "!src->is_Field() && !dst->is_Field()" ") failed", "only for JavaObject and LocalVar"); ::breakpoint (); } } while (0); |
| 2157 | assert((src != null_obj) && (dst != null_obj), "not for ConP NULL")do { if (!((src != null_obj) && (dst != null_obj))) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2157, "assert(" "(src != null_obj) && (dst != null_obj)" ") failed", "not for ConP NULL"); ::breakpoint(); } } while ( 0); |
| 2158 | PointsToNode* ptadr = _nodes.at(n->_idx); |
| 2159 | if (ptadr != NULL__null) { |
| 2160 | assert(ptadr->is_Arraycopy() && ptadr->ideal_node() == n, "sanity")do { if (!(ptadr->is_Arraycopy() && ptadr->ideal_node () == n)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2160, "assert(" "ptadr->is_Arraycopy() && ptadr->ideal_node() == n" ") failed", "sanity"); ::breakpoint(); } } while (0); |
| 2161 | return; |
| 2162 | } |
| 2163 | Compile* C = _compile; |
| 2164 | ptadr = new (C->comp_arena()) ArraycopyNode(this, n, es); |
| 2165 | map_ideal_node(n, ptadr); |
| 2166 | // Add edge from arraycopy node to source object. |
| 2167 | (void)add_edge(ptadr, src); |
| 2168 | src->set_arraycopy_src(); |
| 2169 | // Add edge from destination object to arraycopy node. |
| 2170 | (void)add_edge(dst, ptadr); |
| 2171 | dst->set_arraycopy_dst(); |
| 2172 | } |
| 2173 | |
| 2174 | bool ConnectionGraph::is_oop_field(Node* n, int offset, bool* unsafe) { |
| 2175 | const Type* adr_type = n->as_AddP()->bottom_type(); |
| 2176 | BasicType bt = T_INT; |
| 2177 | if (offset == Type::OffsetBot) { |
| 2178 | // Check only oop fields. |
| 2179 | if (!adr_type->isa_aryptr() || |
| 2180 | (adr_type->isa_aryptr()->klass() == NULL__null) || |
| 2181 | adr_type->isa_aryptr()->klass()->is_obj_array_klass()) { |
| 2182 | // OffsetBot is used to reference array's element. Ignore first AddP. |
| 2183 | if (find_second_addp(n, n->in(AddPNode::Base)) == NULL__null) { |
| 2184 | bt = T_OBJECT; |
| 2185 | } |
| 2186 | } |
| 2187 | } else if (offset != oopDesc::klass_offset_in_bytes()) { |
| 2188 | if (adr_type->isa_instptr()) { |
| 2189 | ciField* field = _compile->alias_type(adr_type->isa_instptr())->field(); |
| 2190 | if (field != NULL__null) { |
| 2191 | bt = field->layout_type(); |
| 2192 | } else { |
| 2193 | // Check for unsafe oop field access |
| 2194 | if (n->has_out_with(Op_StoreP, Op_LoadP, Op_StoreN, Op_LoadN) || |
| 2195 | n->has_out_with(Op_GetAndSetP, Op_GetAndSetN, Op_CompareAndExchangeP, Op_CompareAndExchangeN) || |
| 2196 | n->has_out_with(Op_CompareAndSwapP, Op_CompareAndSwapN, Op_WeakCompareAndSwapP, Op_WeakCompareAndSwapN) || |
| 2197 | BarrierSet::barrier_set()->barrier_set_c2()->escape_has_out_with_unsafe_object(n)) { |
| 2198 | bt = T_OBJECT; |
| 2199 | (*unsafe) = true; |
| 2200 | } |
| 2201 | } |
| 2202 | } else if (adr_type->isa_aryptr()) { |
| 2203 | if (offset == arrayOopDesc::length_offset_in_bytes()) { |
| 2204 | // Ignore array length load. |
| 2205 | } else if (find_second_addp(n, n->in(AddPNode::Base)) != NULL__null) { |
| 2206 | // Ignore first AddP. |
| 2207 | } else { |
| 2208 | const Type* elemtype = adr_type->isa_aryptr()->elem(); |
| 2209 | bt = elemtype->array_element_basic_type(); |
| 2210 | } |
| 2211 | } else if (adr_type->isa_rawptr() || adr_type->isa_klassptr()) { |
| 2212 | // Allocation initialization, ThreadLocal field access, unsafe access |
| 2213 | if (n->has_out_with(Op_StoreP, Op_LoadP, Op_StoreN, Op_LoadN) || |
| 2214 | n->has_out_with(Op_GetAndSetP, Op_GetAndSetN, Op_CompareAndExchangeP, Op_CompareAndExchangeN) || |
| 2215 | n->has_out_with(Op_CompareAndSwapP, Op_CompareAndSwapN, Op_WeakCompareAndSwapP, Op_WeakCompareAndSwapN) || |
| 2216 | BarrierSet::barrier_set()->barrier_set_c2()->escape_has_out_with_unsafe_object(n)) { |
| 2217 | bt = T_OBJECT; |
| 2218 | } |
| 2219 | } |
| 2220 | } |
| 2221 | // Note: T_NARROWOOP is not classed as a real reference type |
| 2222 | return (is_reference_type(bt) || bt == T_NARROWOOP); |
| 2223 | } |
| 2224 | |
| 2225 | // Returns unique pointed java object or NULL. |
| 2226 | JavaObjectNode* ConnectionGraph::unique_java_object(Node *n) { |
| 2227 | assert(!_collecting, "should not call when constructed graph")do { if (!(!_collecting)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2227, "assert(" "!_collecting" ") failed", "should not call when constructed graph" ); ::breakpoint(); } } while (0); |
| 2228 | // If the node was created after the escape computation we can't answer. |
| 2229 | uint idx = n->_idx; |
| 2230 | if (idx >= nodes_size()) { |
| 2231 | return NULL__null; |
| 2232 | } |
| 2233 | PointsToNode* ptn = ptnode_adr(idx); |
| 2234 | if (ptn == NULL__null) { |
| 2235 | return NULL__null; |
| 2236 | } |
| 2237 | if (ptn->is_JavaObject()) { |
| 2238 | return ptn->as_JavaObject(); |
| 2239 | } |
| 2240 | assert(ptn->is_LocalVar(), "sanity")do { if (!(ptn->is_LocalVar())) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2240, "assert(" "ptn->is_LocalVar()" ") failed", "sanity" ); ::breakpoint(); } } while (0); |
| 2241 | // Check all java objects it points to. |
| 2242 | JavaObjectNode* jobj = NULL__null; |
| 2243 | for (EdgeIterator i(ptn); i.has_next(); i.next()) { |
| 2244 | PointsToNode* e = i.get(); |
| 2245 | if (e->is_JavaObject()) { |
| 2246 | if (jobj == NULL__null) { |
| 2247 | jobj = e->as_JavaObject(); |
| 2248 | } else if (jobj != e) { |
| 2249 | return NULL__null; |
| 2250 | } |
| 2251 | } |
| 2252 | } |
| 2253 | return jobj; |
| 2254 | } |
| 2255 | |
| 2256 | // Return true if this node points only to non-escaping allocations. |
| 2257 | bool PointsToNode::non_escaping_allocation() { |
| 2258 | if (is_JavaObject()) { |
| 2259 | Node* n = ideal_node(); |
| 2260 | if (n->is_Allocate() || n->is_CallStaticJava()) { |
| 2261 | return (escape_state() == PointsToNode::NoEscape); |
| 2262 | } else { |
| 2263 | return false; |
| 2264 | } |
| 2265 | } |
| 2266 | assert(is_LocalVar(), "sanity")do { if (!(is_LocalVar())) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2266, "assert(" "is_LocalVar()" ") failed", "sanity"); ::breakpoint (); } } while (0); |
| 2267 | // Check all java objects it points to. |
| 2268 | for (EdgeIterator i(this); i.has_next(); i.next()) { |
| 2269 | PointsToNode* e = i.get(); |
| 2270 | if (e->is_JavaObject()) { |
| 2271 | Node* n = e->ideal_node(); |
| 2272 | if ((e->escape_state() != PointsToNode::NoEscape) || |
| 2273 | !(n->is_Allocate() || n->is_CallStaticJava())) { |
| 2274 | return false; |
| 2275 | } |
| 2276 | } |
| 2277 | } |
| 2278 | return true; |
| 2279 | } |
| 2280 | |
| 2281 | // Return true if we know the node does not escape globally. |
| 2282 | bool ConnectionGraph::not_global_escape(Node *n) { |
| 2283 | assert(!_collecting, "should not call during graph construction")do { if (!(!_collecting)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2283, "assert(" "!_collecting" ") failed", "should not call during graph construction" ); ::breakpoint(); } } while (0); |
| 2284 | // If the node was created after the escape computation we can't answer. |
| 2285 | uint idx = n->_idx; |
| 2286 | if (idx >= nodes_size()) { |
| 2287 | return false; |
| 2288 | } |
| 2289 | PointsToNode* ptn = ptnode_adr(idx); |
| 2290 | if (ptn == NULL__null) { |
| 2291 | return false; // not in congraph (e.g. ConI) |
| 2292 | } |
| 2293 | PointsToNode::EscapeState es = ptn->escape_state(); |
| 2294 | // If we have already computed a value, return it. |
| 2295 | if (es >= PointsToNode::GlobalEscape) { |
| 2296 | return false; |
| 2297 | } |
| 2298 | if (ptn->is_JavaObject()) { |
| 2299 | return true; // (es < PointsToNode::GlobalEscape); |
| 2300 | } |
| 2301 | assert(ptn->is_LocalVar(), "sanity")do { if (!(ptn->is_LocalVar())) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2301, "assert(" "ptn->is_LocalVar()" ") failed", "sanity" ); ::breakpoint(); } } while (0); |
| 2302 | // Check all java objects it points to. |
| 2303 | for (EdgeIterator i(ptn); i.has_next(); i.next()) { |
| 2304 | if (i.get()->escape_state() >= PointsToNode::GlobalEscape) { |
| 2305 | return false; |
| 2306 | } |
| 2307 | } |
| 2308 | return true; |
| 2309 | } |
| 2310 | |
| 2311 | |
| 2312 | // Helper functions |
| 2313 | |
| 2314 | // Return true if this node points to specified node or nodes it points to. |
| 2315 | bool PointsToNode::points_to(JavaObjectNode* ptn) const { |
| 2316 | if (is_JavaObject()) { |
| 2317 | return (this == ptn); |
| 2318 | } |
| 2319 | assert(is_LocalVar() || is_Field(), "sanity")do { if (!(is_LocalVar() || is_Field())) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2319, "assert(" "is_LocalVar() || is_Field()" ") failed", "sanity" ); ::breakpoint(); } } while (0); |
| 2320 | for (EdgeIterator i(this); i.has_next(); i.next()) { |
| 2321 | if (i.get() == ptn) { |
| 2322 | return true; |
| 2323 | } |
| 2324 | } |
| 2325 | return false; |
| 2326 | } |
| 2327 | |
| 2328 | // Return true if one node points to an other. |
| 2329 | bool PointsToNode::meet(PointsToNode* ptn) { |
| 2330 | if (this == ptn) { |
| 2331 | return true; |
| 2332 | } else if (ptn->is_JavaObject()) { |
| 2333 | return this->points_to(ptn->as_JavaObject()); |
| 2334 | } else if (this->is_JavaObject()) { |
| 2335 | return ptn->points_to(this->as_JavaObject()); |
| 2336 | } |
| 2337 | assert(this->is_LocalVar() && ptn->is_LocalVar(), "sanity")do { if (!(this->is_LocalVar() && ptn->is_LocalVar ())) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2337, "assert(" "this->is_LocalVar() && ptn->is_LocalVar()" ") failed", "sanity"); ::breakpoint(); } } while (0); |
| 2338 | int ptn_count = ptn->edge_count(); |
| 2339 | for (EdgeIterator i(this); i.has_next(); i.next()) { |
| 2340 | PointsToNode* this_e = i.get(); |
| 2341 | for (int j = 0; j < ptn_count; j++) { |
| 2342 | if (this_e == ptn->edge(j)) { |
| 2343 | return true; |
| 2344 | } |
| 2345 | } |
| 2346 | } |
| 2347 | return false; |
| 2348 | } |
| 2349 | |
| 2350 | #ifdef ASSERT1 |
| 2351 | // Return true if bases point to this java object. |
| 2352 | bool FieldNode::has_base(JavaObjectNode* jobj) const { |
| 2353 | for (BaseIterator i(this); i.has_next(); i.next()) { |
| 2354 | if (i.get() == jobj) { |
| 2355 | return true; |
| 2356 | } |
| 2357 | } |
| 2358 | return false; |
| 2359 | } |
| 2360 | #endif |
| 2361 | |
| 2362 | bool ConnectionGraph::is_captured_store_address(Node* addp) { |
| 2363 | // Handle simple case first. |
| 2364 | assert(_igvn->type(addp)->isa_oopptr() == NULL, "should be raw access")do { if (!(_igvn->type(addp)->isa_oopptr() == __null)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2364, "assert(" "_igvn->type(addp)->isa_oopptr() == __null" ") failed", "should be raw access"); ::breakpoint(); } } while (0); |
| 2365 | if (addp->in(AddPNode::Address)->is_Proj() && addp->in(AddPNode::Address)->in(0)->is_Allocate()) { |
| 2366 | return true; |
| 2367 | } else if (addp->in(AddPNode::Address)->is_Phi()) { |
| 2368 | for (DUIterator_Fast imax, i = addp->fast_outs(imax); i < imax; i++) { |
| 2369 | Node* addp_use = addp->fast_out(i); |
| 2370 | if (addp_use->is_Store()) { |
| 2371 | for (DUIterator_Fast jmax, j = addp_use->fast_outs(jmax); j < jmax; j++) { |
| 2372 | if (addp_use->fast_out(j)->is_Initialize()) { |
| 2373 | return true; |
| 2374 | } |
| 2375 | } |
| 2376 | } |
| 2377 | } |
| 2378 | } |
| 2379 | return false; |
| 2380 | } |
| 2381 | |
| 2382 | int ConnectionGraph::address_offset(Node* adr, PhaseTransform *phase) { |
| 2383 | const Type *adr_type = phase->type(adr); |
| 2384 | if (adr->is_AddP() && adr_type->isa_oopptr() == NULL__null && is_captured_store_address(adr)) { |
| 2385 | // We are computing a raw address for a store captured by an Initialize |
| 2386 | // compute an appropriate address type. AddP cases #3 and #5 (see below). |
| 2387 | int offs = (int)phase->find_intptr_t_confind_long_con(adr->in(AddPNode::Offset), Type::OffsetBot); |
| 2388 | assert(offs != Type::OffsetBot ||do { if (!(offs != Type::OffsetBot || adr->in(AddPNode::Address )->in(0)->is_AllocateArray())) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2390, "assert(" "offs != Type::OffsetBot || adr->in(AddPNode::Address)->in(0)->is_AllocateArray()" ") failed", "offset must be a constant or it is initialization of array" ); ::breakpoint(); } } while (0) |
| 2389 | adr->in(AddPNode::Address)->in(0)->is_AllocateArray(),do { if (!(offs != Type::OffsetBot || adr->in(AddPNode::Address )->in(0)->is_AllocateArray())) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2390, "assert(" "offs != Type::OffsetBot || adr->in(AddPNode::Address)->in(0)->is_AllocateArray()" ") failed", "offset must be a constant or it is initialization of array" ); ::breakpoint(); } } while (0) |
| 2390 | "offset must be a constant or it is initialization of array")do { if (!(offs != Type::OffsetBot || adr->in(AddPNode::Address )->in(0)->is_AllocateArray())) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2390, "assert(" "offs != Type::OffsetBot || adr->in(AddPNode::Address)->in(0)->is_AllocateArray()" ") failed", "offset must be a constant or it is initialization of array" ); ::breakpoint(); } } while (0); |
| 2391 | return offs; |
| 2392 | } |
| 2393 | const TypePtr *t_ptr = adr_type->isa_ptr(); |
| 2394 | assert(t_ptr != NULL, "must be a pointer type")do { if (!(t_ptr != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2394, "assert(" "t_ptr != __null" ") failed", "must be a pointer type" ); ::breakpoint(); } } while (0); |
| 2395 | return t_ptr->offset(); |
| 2396 | } |
| 2397 | |
| 2398 | Node* ConnectionGraph::get_addp_base(Node *addp) { |
| 2399 | assert(addp->is_AddP(), "must be AddP")do { if (!(addp->is_AddP())) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2399, "assert(" "addp->is_AddP()" ") failed", "must be AddP" ); ::breakpoint(); } } while (0); |
| 2400 | // |
| 2401 | // AddP cases for Base and Address inputs: |
| 2402 | // case #1. Direct object's field reference: |
| 2403 | // Allocate |
| 2404 | // | |
| 2405 | // Proj #5 ( oop result ) |
| 2406 | // | |
| 2407 | // CheckCastPP (cast to instance type) |
| 2408 | // | | |
| 2409 | // AddP ( base == address ) |
| 2410 | // |
| 2411 | // case #2. Indirect object's field reference: |
| 2412 | // Phi |
| 2413 | // | |
| 2414 | // CastPP (cast to instance type) |
| 2415 | // | | |
| 2416 | // AddP ( base == address ) |
| 2417 | // |
| 2418 | // case #3. Raw object's field reference for Initialize node: |
| 2419 | // Allocate |
| 2420 | // | |
| 2421 | // Proj #5 ( oop result ) |
| 2422 | // top | |
| 2423 | // \ | |
| 2424 | // AddP ( base == top ) |
| 2425 | // |
| 2426 | // case #4. Array's element reference: |
| 2427 | // {CheckCastPP | CastPP} |
| 2428 | // | | | |
| 2429 | // | AddP ( array's element offset ) |
| 2430 | // | | |
| 2431 | // AddP ( array's offset ) |
| 2432 | // |
| 2433 | // case #5. Raw object's field reference for arraycopy stub call: |
| 2434 | // The inline_native_clone() case when the arraycopy stub is called |
| 2435 | // after the allocation before Initialize and CheckCastPP nodes. |
| 2436 | // Allocate |
| 2437 | // | |
| 2438 | // Proj #5 ( oop result ) |
| 2439 | // | | |
| 2440 | // AddP ( base == address ) |
| 2441 | // |
| 2442 | // case #6. Constant Pool, ThreadLocal, CastX2P or |
| 2443 | // Raw object's field reference: |
| 2444 | // {ConP, ThreadLocal, CastX2P, raw Load} |
| 2445 | // top | |
| 2446 | // \ | |
| 2447 | // AddP ( base == top ) |
| 2448 | // |
| 2449 | // case #7. Klass's field reference. |
| 2450 | // LoadKlass |
| 2451 | // | | |
| 2452 | // AddP ( base == address ) |
| 2453 | // |
| 2454 | // case #8. narrow Klass's field reference. |
| 2455 | // LoadNKlass |
| 2456 | // | |
| 2457 | // DecodeN |
| 2458 | // | | |
| 2459 | // AddP ( base == address ) |
| 2460 | // |
| 2461 | // case #9. Mixed unsafe access |
| 2462 | // {instance} |
| 2463 | // | |
| 2464 | // CheckCastPP (raw) |
| 2465 | // top | |
| 2466 | // \ | |
| 2467 | // AddP ( base == top ) |
| 2468 | // |
| 2469 | Node *base = addp->in(AddPNode::Base); |
| 2470 | if (base->uncast()->is_top()) { // The AddP case #3 and #6 and #9. |
| 2471 | base = addp->in(AddPNode::Address); |
| 2472 | while (base->is_AddP()) { |
| 2473 | // Case #6 (unsafe access) may have several chained AddP nodes. |
| 2474 | assert(base->in(AddPNode::Base)->uncast()->is_top(), "expected unsafe access address only")do { if (!(base->in(AddPNode::Base)->uncast()->is_top ())) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2474, "assert(" "base->in(AddPNode::Base)->uncast()->is_top()" ") failed", "expected unsafe access address only"); ::breakpoint (); } } while (0); |
| 2475 | base = base->in(AddPNode::Address); |
| 2476 | } |
| 2477 | if (base->Opcode() == Op_CheckCastPP && |
| 2478 | base->bottom_type()->isa_rawptr() && |
| 2479 | _igvn->type(base->in(1))->isa_oopptr()) { |
| 2480 | base = base->in(1); // Case #9 |
| 2481 | } else { |
| 2482 | Node* uncast_base = base->uncast(); |
| 2483 | int opcode = uncast_base->Opcode(); |
| 2484 | assert(opcode == Op_ConP || opcode == Op_ThreadLocal ||do { if (!(opcode == Op_ConP || opcode == Op_ThreadLocal || opcode == Op_CastX2P || uncast_base->is_DecodeNarrowPtr() || (uncast_base ->is_Mem() && (uncast_base->bottom_type()->isa_rawptr () != __null)) || is_captured_store_address(addp))) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2487, "assert(" "opcode == Op_ConP || opcode == Op_ThreadLocal || opcode == Op_CastX2P || uncast_base->is_DecodeNarrowPtr() || (uncast_base->is_Mem() && (uncast_base->bottom_type()->isa_rawptr() != __null)) || is_captured_store_address(addp)" ") failed", "sanity"); ::breakpoint(); } } while (0) |
| 2485 | opcode == Op_CastX2P || uncast_base->is_DecodeNarrowPtr() ||do { if (!(opcode == Op_ConP || opcode == Op_ThreadLocal || opcode == Op_CastX2P || uncast_base->is_DecodeNarrowPtr() || (uncast_base ->is_Mem() && (uncast_base->bottom_type()->isa_rawptr () != __null)) || is_captured_store_address(addp))) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2487, "assert(" "opcode == Op_ConP || opcode == Op_ThreadLocal || opcode == Op_CastX2P || uncast_base->is_DecodeNarrowPtr() || (uncast_base->is_Mem() && (uncast_base->bottom_type()->isa_rawptr() != __null)) || is_captured_store_address(addp)" ") failed", "sanity"); ::breakpoint(); } } while (0) |
| 2486 | (uncast_base->is_Mem() && (uncast_base->bottom_type()->isa_rawptr() != NULL)) ||do { if (!(opcode == Op_ConP || opcode == Op_ThreadLocal || opcode == Op_CastX2P || uncast_base->is_DecodeNarrowPtr() || (uncast_base ->is_Mem() && (uncast_base->bottom_type()->isa_rawptr () != __null)) || is_captured_store_address(addp))) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2487, "assert(" "opcode == Op_ConP || opcode == Op_ThreadLocal || opcode == Op_CastX2P || uncast_base->is_DecodeNarrowPtr() || (uncast_base->is_Mem() && (uncast_base->bottom_type()->isa_rawptr() != __null)) || is_captured_store_address(addp)" ") failed", "sanity"); ::breakpoint(); } } while (0) |
| 2487 | is_captured_store_address(addp), "sanity")do { if (!(opcode == Op_ConP || opcode == Op_ThreadLocal || opcode == Op_CastX2P || uncast_base->is_DecodeNarrowPtr() || (uncast_base ->is_Mem() && (uncast_base->bottom_type()->isa_rawptr () != __null)) || is_captured_store_address(addp))) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2487, "assert(" "opcode == Op_ConP || opcode == Op_ThreadLocal || opcode == Op_CastX2P || uncast_base->is_DecodeNarrowPtr() || (uncast_base->is_Mem() && (uncast_base->bottom_type()->isa_rawptr() != __null)) || is_captured_store_address(addp)" ") failed", "sanity"); ::breakpoint(); } } while (0); |
| 2488 | } |
| 2489 | } |
| 2490 | return base; |
| 2491 | } |
| 2492 | |
| 2493 | Node* ConnectionGraph::find_second_addp(Node* addp, Node* n) { |
| 2494 | assert(addp->is_AddP() && addp->outcnt() > 0, "Don't process dead nodes")do { if (!(addp->is_AddP() && addp->outcnt() > 0)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2494, "assert(" "addp->is_AddP() && addp->outcnt() > 0" ") failed", "Don't process dead nodes"); ::breakpoint(); } } while (0); |
| 2495 | Node* addp2 = addp->raw_out(0); |
| 2496 | if (addp->outcnt() == 1 && addp2->is_AddP() && |
| 2497 | addp2->in(AddPNode::Base) == n && |
| 2498 | addp2->in(AddPNode::Address) == addp) { |
| 2499 | assert(addp->in(AddPNode::Base) == n, "expecting the same base")do { if (!(addp->in(AddPNode::Base) == n)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2499, "assert(" "addp->in(AddPNode::Base) == n" ") failed" , "expecting the same base"); ::breakpoint(); } } while (0); |
| 2500 | // |
| 2501 | // Find array's offset to push it on worklist first and |
| 2502 | // as result process an array's element offset first (pushed second) |
| 2503 | // to avoid CastPP for the array's offset. |
| 2504 | // Otherwise the inserted CastPP (LocalVar) will point to what |
| 2505 | // the AddP (Field) points to. Which would be wrong since |
| 2506 | // the algorithm expects the CastPP has the same point as |
| 2507 | // as AddP's base CheckCastPP (LocalVar). |
| 2508 | // |
| 2509 | // ArrayAllocation |
| 2510 | // | |
| 2511 | // CheckCastPP |
| 2512 | // | |
| 2513 | // memProj (from ArrayAllocation CheckCastPP) |
| 2514 | // | || |
| 2515 | // | || Int (element index) |
| 2516 | // | || | ConI (log(element size)) |
| 2517 | // | || | / |
| 2518 | // | || LShift |
| 2519 | // | || / |
| 2520 | // | AddP (array's element offset) |
| 2521 | // | | |
| 2522 | // | | ConI (array's offset: #12(32-bits) or #24(64-bits)) |
| 2523 | // | / / |
| 2524 | // AddP (array's offset) |
| 2525 | // | |
| 2526 | // Load/Store (memory operation on array's element) |
| 2527 | // |
| 2528 | return addp2; |
| 2529 | } |
| 2530 | return NULL__null; |
| 2531 | } |
| 2532 | |
| 2533 | // |
| 2534 | // Adjust the type and inputs of an AddP which computes the |
| 2535 | // address of a field of an instance |
| 2536 | // |
| 2537 | bool ConnectionGraph::split_AddP(Node *addp, Node *base) { |
| 2538 | PhaseGVN* igvn = _igvn; |
| 2539 | const TypeOopPtr *base_t = igvn->type(base)->isa_oopptr(); |
| 2540 | assert(base_t != NULL && base_t->is_known_instance(), "expecting instance oopptr")do { if (!(base_t != __null && base_t->is_known_instance ())) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2540, "assert(" "base_t != __null && base_t->is_known_instance()" ") failed", "expecting instance oopptr"); ::breakpoint(); } } while (0); |
| 2541 | const TypeOopPtr *t = igvn->type(addp)->isa_oopptr(); |
| 2542 | if (t == NULL__null) { |
| 2543 | // We are computing a raw address for a store captured by an Initialize |
| 2544 | // compute an appropriate address type (cases #3 and #5). |
| 2545 | assert(igvn->type(addp) == TypeRawPtr::NOTNULL, "must be raw pointer")do { if (!(igvn->type(addp) == TypeRawPtr::NOTNULL)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2545, "assert(" "igvn->type(addp) == TypeRawPtr::NOTNULL" ") failed", "must be raw pointer"); ::breakpoint(); } } while (0); |
| 2546 | assert(addp->in(AddPNode::Address)->is_Proj(), "base of raw address must be result projection from allocation")do { if (!(addp->in(AddPNode::Address)->is_Proj())) { ( *g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2546, "assert(" "addp->in(AddPNode::Address)->is_Proj()" ") failed", "base of raw address must be result projection from allocation" ); ::breakpoint(); } } while (0); |
| 2547 | intptr_t offs = (int)igvn->find_intptr_t_confind_long_con(addp->in(AddPNode::Offset), Type::OffsetBot); |
| 2548 | assert(offs != Type::OffsetBot, "offset must be a constant")do { if (!(offs != Type::OffsetBot)) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2548, "assert(" "offs != Type::OffsetBot" ") failed", "offset must be a constant" ); ::breakpoint(); } } while (0); |
| 2549 | t = base_t->add_offset(offs)->is_oopptr(); |
| 2550 | } |
| 2551 | int inst_id = base_t->instance_id(); |
| 2552 | assert(!t->is_known_instance() || t->instance_id() == inst_id,do { if (!(!t->is_known_instance() || t->instance_id() == inst_id)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2553, "assert(" "!t->is_known_instance() || t->instance_id() == inst_id" ") failed", "old type must be non-instance or match new type" ); ::breakpoint(); } } while (0) |
| 2553 | "old type must be non-instance or match new type")do { if (!(!t->is_known_instance() || t->instance_id() == inst_id)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2553, "assert(" "!t->is_known_instance() || t->instance_id() == inst_id" ") failed", "old type must be non-instance or match new type" ); ::breakpoint(); } } while (0); |
| 2554 | |
| 2555 | // The type 't' could be subclass of 'base_t'. |
| 2556 | // As result t->offset() could be large then base_t's size and it will |
| 2557 | // cause the failure in add_offset() with narrow oops since TypeOopPtr() |
| 2558 | // constructor verifies correctness of the offset. |
| 2559 | // |
| 2560 | // It could happened on subclass's branch (from the type profiling |
| 2561 | // inlining) which was not eliminated during parsing since the exactness |
| 2562 | // of the allocation type was not propagated to the subclass type check. |
| 2563 | // |
| 2564 | // Or the type 't' could be not related to 'base_t' at all. |
| 2565 | // It could happened when CHA type is different from MDO type on a dead path |
| 2566 | // (for example, from instanceof check) which is not collapsed during parsing. |
| 2567 | // |
| 2568 | // Do nothing for such AddP node and don't process its users since |
| 2569 | // this code branch will go away. |
| 2570 | // |
| 2571 | if (!t->is_known_instance() && |
| 2572 | !base_t->klass()->is_subtype_of(t->klass())) { |
| 2573 | return false; // bail out |
| 2574 | } |
| 2575 | const TypeOopPtr *tinst = base_t->add_offset(t->offset())->is_oopptr(); |
| 2576 | // Do NOT remove the next line: ensure a new alias index is allocated |
| 2577 | // for the instance type. Note: C++ will not remove it since the call |
| 2578 | // has side effect. |
| 2579 | int alias_idx = _compile->get_alias_index(tinst); |
Value stored to 'alias_idx' during its initialization is never read | |
| 2580 | igvn->set_type(addp, tinst); |
| 2581 | // record the allocation in the node map |
| 2582 | set_map(addp, get_map(base->_idx)); |
| 2583 | // Set addp's Base and Address to 'base'. |
| 2584 | Node *abase = addp->in(AddPNode::Base); |
| 2585 | Node *adr = addp->in(AddPNode::Address); |
| 2586 | if (adr->is_Proj() && adr->in(0)->is_Allocate() && |
| 2587 | adr->in(0)->_idx == (uint)inst_id) { |
| 2588 | // Skip AddP cases #3 and #5. |
| 2589 | } else { |
| 2590 | assert(!abase->is_top(), "sanity")do { if (!(!abase->is_top())) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2590, "assert(" "!abase->is_top()" ") failed", "sanity") ; ::breakpoint(); } } while (0); // AddP case #3 |
| 2591 | if (abase != base) { |
| 2592 | igvn->hash_delete(addp); |
| 2593 | addp->set_req(AddPNode::Base, base); |
| 2594 | if (abase == adr) { |
| 2595 | addp->set_req(AddPNode::Address, base); |
| 2596 | } else { |
| 2597 | // AddP case #4 (adr is array's element offset AddP node) |
| 2598 | #ifdef ASSERT1 |
| 2599 | const TypeOopPtr *atype = igvn->type(adr)->isa_oopptr(); |
| 2600 | assert(adr->is_AddP() && atype != NULL &&do { if (!(adr->is_AddP() && atype != __null && atype->instance_id() == inst_id)) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2601, "assert(" "adr->is_AddP() && atype != __null && atype->instance_id() == inst_id" ") failed", "array's element offset should be processed first" ); ::breakpoint(); } } while (0) |
| 2601 | atype->instance_id() == inst_id, "array's element offset should be processed first")do { if (!(adr->is_AddP() && atype != __null && atype->instance_id() == inst_id)) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2601, "assert(" "adr->is_AddP() && atype != __null && atype->instance_id() == inst_id" ") failed", "array's element offset should be processed first" ); ::breakpoint(); } } while (0); |
| 2602 | #endif |
| 2603 | } |
| 2604 | igvn->hash_insert(addp); |
| 2605 | } |
| 2606 | } |
| 2607 | // Put on IGVN worklist since at least addp's type was changed above. |
| 2608 | record_for_optimizer(addp); |
| 2609 | return true; |
| 2610 | } |
| 2611 | |
| 2612 | // |
| 2613 | // Create a new version of orig_phi if necessary. Returns either the newly |
| 2614 | // created phi or an existing phi. Sets create_new to indicate whether a new |
| 2615 | // phi was created. Cache the last newly created phi in the node map. |
| 2616 | // |
| 2617 | PhiNode *ConnectionGraph::create_split_phi(PhiNode *orig_phi, int alias_idx, GrowableArray<PhiNode *> &orig_phi_worklist, bool &new_created) { |
| 2618 | Compile *C = _compile; |
| 2619 | PhaseGVN* igvn = _igvn; |
| 2620 | new_created = false; |
| 2621 | int phi_alias_idx = C->get_alias_index(orig_phi->adr_type()); |
| 2622 | // nothing to do if orig_phi is bottom memory or matches alias_idx |
| 2623 | if (phi_alias_idx == alias_idx) { |
| 2624 | return orig_phi; |
| 2625 | } |
| 2626 | // Have we recently created a Phi for this alias index? |
| 2627 | PhiNode *result = get_map_phi(orig_phi->_idx); |
| 2628 | if (result != NULL__null && C->get_alias_index(result->adr_type()) == alias_idx) { |
| 2629 | return result; |
| 2630 | } |
| 2631 | // Previous check may fail when the same wide memory Phi was split into Phis |
| 2632 | // for different memory slices. Search all Phis for this region. |
| 2633 | if (result != NULL__null) { |
| 2634 | Node* region = orig_phi->in(0); |
| 2635 | for (DUIterator_Fast imax, i = region->fast_outs(imax); i < imax; i++) { |
| 2636 | Node* phi = region->fast_out(i); |
| 2637 | if (phi->is_Phi() && |
| 2638 | C->get_alias_index(phi->as_Phi()->adr_type()) == alias_idx) { |
| 2639 | assert(phi->_idx >= nodes_size(), "only new Phi per instance memory slice")do { if (!(phi->_idx >= nodes_size())) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2639, "assert(" "phi->_idx >= nodes_size()" ") failed" , "only new Phi per instance memory slice"); ::breakpoint(); } } while (0); |
| 2640 | return phi->as_Phi(); |
| 2641 | } |
| 2642 | } |
| 2643 | } |
| 2644 | if (C->live_nodes() + 2*NodeLimitFudgeFactor > C->max_node_limit()) { |
| 2645 | if (C->do_escape_analysis() == true && !C->failing()) { |
| 2646 | // Retry compilation without escape analysis. |
| 2647 | // If this is the first failure, the sentinel string will "stick" |
| 2648 | // to the Compile object, and the C2Compiler will see it and retry. |
| 2649 | C->record_failure(_invocation > 0 ? C2Compiler::retry_no_iterative_escape_analysis() : C2Compiler::retry_no_escape_analysis()); |
| 2650 | } |
| 2651 | return NULL__null; |
| 2652 | } |
| 2653 | orig_phi_worklist.append_if_missing(orig_phi); |
| 2654 | const TypePtr *atype = C->get_adr_type(alias_idx); |
| 2655 | result = PhiNode::make(orig_phi->in(0), NULL__null, Type::MEMORY, atype); |
| 2656 | C->copy_node_notes_to(result, orig_phi); |
| 2657 | igvn->set_type(result, result->bottom_type()); |
| 2658 | record_for_optimizer(result); |
| 2659 | set_map(orig_phi, result); |
| 2660 | new_created = true; |
| 2661 | return result; |
| 2662 | } |
| 2663 | |
| 2664 | // |
| 2665 | // Return a new version of Memory Phi "orig_phi" with the inputs having the |
| 2666 | // specified alias index. |
| 2667 | // |
| 2668 | PhiNode *ConnectionGraph::split_memory_phi(PhiNode *orig_phi, int alias_idx, GrowableArray<PhiNode *> &orig_phi_worklist) { |
| 2669 | assert(alias_idx != Compile::AliasIdxBot, "can't split out bottom memory")do { if (!(alias_idx != Compile::AliasIdxBot)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2669, "assert(" "alias_idx != Compile::AliasIdxBot" ") failed" , "can't split out bottom memory"); ::breakpoint(); } } while (0); |
| 2670 | Compile *C = _compile; |
| 2671 | PhaseGVN* igvn = _igvn; |
| 2672 | bool new_phi_created; |
| 2673 | PhiNode *result = create_split_phi(orig_phi, alias_idx, orig_phi_worklist, new_phi_created); |
| 2674 | if (!new_phi_created) { |
| 2675 | return result; |
| 2676 | } |
| 2677 | GrowableArray<PhiNode *> phi_list; |
| 2678 | GrowableArray<uint> cur_input; |
| 2679 | PhiNode *phi = orig_phi; |
| 2680 | uint idx = 1; |
| 2681 | bool finished = false; |
| 2682 | while(!finished) { |
| 2683 | while (idx < phi->req()) { |
| 2684 | Node *mem = find_inst_mem(phi->in(idx), alias_idx, orig_phi_worklist); |
| 2685 | if (mem != NULL__null && mem->is_Phi()) { |
| 2686 | PhiNode *newphi = create_split_phi(mem->as_Phi(), alias_idx, orig_phi_worklist, new_phi_created); |
| 2687 | if (new_phi_created) { |
| 2688 | // found an phi for which we created a new split, push current one on worklist and begin |
| 2689 | // processing new one |
| 2690 | phi_list.push(phi); |
| 2691 | cur_input.push(idx); |
| 2692 | phi = mem->as_Phi(); |
| 2693 | result = newphi; |
| 2694 | idx = 1; |
| 2695 | continue; |
| 2696 | } else { |
| 2697 | mem = newphi; |
| 2698 | } |
| 2699 | } |
| 2700 | if (C->failing()) { |
| 2701 | return NULL__null; |
| 2702 | } |
| 2703 | result->set_req(idx++, mem); |
| 2704 | } |
| 2705 | #ifdef ASSERT1 |
| 2706 | // verify that the new Phi has an input for each input of the original |
| 2707 | assert( phi->req() == result->req(), "must have same number of inputs.")do { if (!(phi->req() == result->req())) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2707, "assert(" "phi->req() == result->req()" ") failed" , "must have same number of inputs."); ::breakpoint(); } } while (0); |
| 2708 | assert( result->in(0) != NULL && result->in(0) == phi->in(0), "regions must match")do { if (!(result->in(0) != __null && result->in (0) == phi->in(0))) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2708, "assert(" "result->in(0) != __null && result->in(0) == phi->in(0)" ") failed", "regions must match"); ::breakpoint(); } } while (0); |
| 2709 | #endif |
| 2710 | // Check if all new phi's inputs have specified alias index. |
| 2711 | // Otherwise use old phi. |
| 2712 | for (uint i = 1; i < phi->req(); i++) { |
| 2713 | Node* in = result->in(i); |
| 2714 | assert((phi->in(i) == NULL) == (in == NULL), "inputs must correspond.")do { if (!((phi->in(i) == __null) == (in == __null))) { (* g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2714, "assert(" "(phi->in(i) == __null) == (in == __null)" ") failed", "inputs must correspond."); ::breakpoint(); } } while (0); |
| 2715 | } |
| 2716 | // we have finished processing a Phi, see if there are any more to do |
| 2717 | finished = (phi_list.length() == 0 ); |
| 2718 | if (!finished) { |
| 2719 | phi = phi_list.pop(); |
| 2720 | idx = cur_input.pop(); |
| 2721 | PhiNode *prev_result = get_map_phi(phi->_idx); |
| 2722 | prev_result->set_req(idx++, result); |
| 2723 | result = prev_result; |
| 2724 | } |
| 2725 | } |
| 2726 | return result; |
| 2727 | } |
| 2728 | |
| 2729 | // |
| 2730 | // The next methods are derived from methods in MemNode. |
| 2731 | // |
| 2732 | Node* ConnectionGraph::step_through_mergemem(MergeMemNode *mmem, int alias_idx, const TypeOopPtr *toop) { |
| 2733 | Node *mem = mmem; |
| 2734 | // TypeOopPtr::NOTNULL+any is an OOP with unknown offset - generally |
| 2735 | // means an array I have not precisely typed yet. Do not do any |
| 2736 | // alias stuff with it any time soon. |
| 2737 | if (toop->base() != Type::AnyPtr && |
| 2738 | !(toop->klass() != NULL__null && |
| 2739 | toop->klass()->is_java_lang_Object() && |
| 2740 | toop->offset() == Type::OffsetBot)) { |
| 2741 | mem = mmem->memory_at(alias_idx); |
| 2742 | // Update input if it is progress over what we have now |
| 2743 | } |
| 2744 | return mem; |
| 2745 | } |
| 2746 | |
| 2747 | // |
| 2748 | // Move memory users to their memory slices. |
| 2749 | // |
| 2750 | void ConnectionGraph::move_inst_mem(Node* n, GrowableArray<PhiNode *> &orig_phis) { |
| 2751 | Compile* C = _compile; |
| 2752 | PhaseGVN* igvn = _igvn; |
| 2753 | const TypePtr* tp = igvn->type(n->in(MemNode::Address))->isa_ptr(); |
| 2754 | assert(tp != NULL, "ptr type")do { if (!(tp != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2754, "assert(" "tp != __null" ") failed", "ptr type"); ::breakpoint (); } } while (0); |
| 2755 | int alias_idx = C->get_alias_index(tp); |
| 2756 | int general_idx = C->get_general_index(alias_idx); |
| 2757 | |
| 2758 | // Move users first |
| 2759 | for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { |
| 2760 | Node* use = n->fast_out(i); |
| 2761 | if (use->is_MergeMem()) { |
| 2762 | MergeMemNode* mmem = use->as_MergeMem(); |
| 2763 | assert(n == mmem->memory_at(alias_idx), "should be on instance memory slice")do { if (!(n == mmem->memory_at(alias_idx))) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2763, "assert(" "n == mmem->memory_at(alias_idx)" ") failed" , "should be on instance memory slice"); ::breakpoint(); } } while (0); |
| 2764 | if (n != mmem->memory_at(general_idx) || alias_idx == general_idx) { |
| 2765 | continue; // Nothing to do |
| 2766 | } |
| 2767 | // Replace previous general reference to mem node. |
| 2768 | uint orig_uniq = C->unique(); |
| 2769 | Node* m = find_inst_mem(n, general_idx, orig_phis); |
| 2770 | assert(orig_uniq == C->unique(), "no new nodes")do { if (!(orig_uniq == C->unique())) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2770, "assert(" "orig_uniq == C->unique()" ") failed", "no new nodes" ); ::breakpoint(); } } while (0); |
| 2771 | mmem->set_memory_at(general_idx, m); |
| 2772 | --imax; |
| 2773 | --i; |
| 2774 | } else if (use->is_MemBar()) { |
| 2775 | assert(!use->is_Initialize(), "initializing stores should not be moved")do { if (!(!use->is_Initialize())) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2775, "assert(" "!use->is_Initialize()" ") failed", "initializing stores should not be moved" ); ::breakpoint(); } } while (0); |
| 2776 | if (use->req() > MemBarNode::Precedent && |
| 2777 | use->in(MemBarNode::Precedent) == n) { |
| 2778 | // Don't move related membars. |
| 2779 | record_for_optimizer(use); |
| 2780 | continue; |
| 2781 | } |
| 2782 | tp = use->as_MemBar()->adr_type()->isa_ptr(); |
| 2783 | if ((tp != NULL__null && C->get_alias_index(tp) == alias_idx) || |
| 2784 | alias_idx == general_idx) { |
| 2785 | continue; // Nothing to do |
| 2786 | } |
| 2787 | // Move to general memory slice. |
| 2788 | uint orig_uniq = C->unique(); |
| 2789 | Node* m = find_inst_mem(n, general_idx, orig_phis); |
| 2790 | assert(orig_uniq == C->unique(), "no new nodes")do { if (!(orig_uniq == C->unique())) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2790, "assert(" "orig_uniq == C->unique()" ") failed", "no new nodes" ); ::breakpoint(); } } while (0); |
| 2791 | igvn->hash_delete(use); |
| 2792 | imax -= use->replace_edge(n, m, igvn); |
| 2793 | igvn->hash_insert(use); |
| 2794 | record_for_optimizer(use); |
| 2795 | --i; |
| 2796 | #ifdef ASSERT1 |
| 2797 | } else if (use->is_Mem()) { |
| 2798 | if (use->Opcode() == Op_StoreCM && use->in(MemNode::OopStore) == n) { |
| 2799 | // Don't move related cardmark. |
| 2800 | continue; |
| 2801 | } |
| 2802 | // Memory nodes should have new memory input. |
| 2803 | tp = igvn->type(use->in(MemNode::Address))->isa_ptr(); |
| 2804 | assert(tp != NULL, "ptr type")do { if (!(tp != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2804, "assert(" "tp != __null" ") failed", "ptr type"); ::breakpoint (); } } while (0); |
| 2805 | int idx = C->get_alias_index(tp); |
| 2806 | assert(get_map(use->_idx) != NULL || idx == alias_idx,do { if (!(get_map(use->_idx) != __null || idx == alias_idx )) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2807, "assert(" "get_map(use->_idx) != __null || idx == alias_idx" ") failed", "Following memory nodes should have new memory input or be on the same memory slice" ); ::breakpoint(); } } while (0) |
| 2807 | "Following memory nodes should have new memory input or be on the same memory slice")do { if (!(get_map(use->_idx) != __null || idx == alias_idx )) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2807, "assert(" "get_map(use->_idx) != __null || idx == alias_idx" ") failed", "Following memory nodes should have new memory input or be on the same memory slice" ); ::breakpoint(); } } while (0); |
| 2808 | } else if (use->is_Phi()) { |
| 2809 | // Phi nodes should be split and moved already. |
| 2810 | tp = use->as_Phi()->adr_type()->isa_ptr(); |
| 2811 | assert(tp != NULL, "ptr type")do { if (!(tp != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2811, "assert(" "tp != __null" ") failed", "ptr type"); ::breakpoint (); } } while (0); |
| 2812 | int idx = C->get_alias_index(tp); |
| 2813 | assert(idx == alias_idx, "Following Phi nodes should be on the same memory slice")do { if (!(idx == alias_idx)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2813, "assert(" "idx == alias_idx" ") failed", "Following Phi nodes should be on the same memory slice" ); ::breakpoint(); } } while (0); |
| 2814 | } else { |
| 2815 | use->dump(); |
| 2816 | assert(false, "should not be here")do { if (!(false)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2816, "assert(" "false" ") failed", "should not be here"); :: breakpoint(); } } while (0); |
| 2817 | #endif |
| 2818 | } |
| 2819 | } |
| 2820 | } |
| 2821 | |
| 2822 | // |
| 2823 | // Search memory chain of "mem" to find a MemNode whose address |
| 2824 | // is the specified alias index. |
| 2825 | // |
| 2826 | Node* ConnectionGraph::find_inst_mem(Node *orig_mem, int alias_idx, GrowableArray<PhiNode *> &orig_phis) { |
| 2827 | if (orig_mem == NULL__null) { |
| 2828 | return orig_mem; |
| 2829 | } |
| 2830 | Compile* C = _compile; |
| 2831 | PhaseGVN* igvn = _igvn; |
| 2832 | const TypeOopPtr *toop = C->get_adr_type(alias_idx)->isa_oopptr(); |
| 2833 | bool is_instance = (toop != NULL__null) && toop->is_known_instance(); |
| 2834 | Node *start_mem = C->start()->proj_out_or_null(TypeFunc::Memory); |
| 2835 | Node *prev = NULL__null; |
| 2836 | Node *result = orig_mem; |
| 2837 | while (prev != result) { |
| 2838 | prev = result; |
| 2839 | if (result == start_mem) { |
| 2840 | break; // hit one of our sentinels |
| 2841 | } |
| 2842 | if (result->is_Mem()) { |
| 2843 | const Type *at = igvn->type(result->in(MemNode::Address)); |
| 2844 | if (at == Type::TOP) { |
| 2845 | break; // Dead |
| 2846 | } |
| 2847 | assert (at->isa_ptr() != NULL, "pointer type required.")do { if (!(at->isa_ptr() != __null)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2847, "assert(" "at->isa_ptr() != __null" ") failed", "pointer type required." ); ::breakpoint(); } } while (0); |
| 2848 | int idx = C->get_alias_index(at->is_ptr()); |
| 2849 | if (idx == alias_idx) { |
| 2850 | break; // Found |
| 2851 | } |
| 2852 | if (!is_instance && (at->isa_oopptr() == NULL__null || |
| 2853 | !at->is_oopptr()->is_known_instance())) { |
| 2854 | break; // Do not skip store to general memory slice. |
| 2855 | } |
| 2856 | result = result->in(MemNode::Memory); |
| 2857 | } |
| 2858 | if (!is_instance) { |
| 2859 | continue; // don't search further for non-instance types |
| 2860 | } |
| 2861 | // skip over a call which does not affect this memory slice |
| 2862 | if (result->is_Proj() && result->as_Proj()->_con == TypeFunc::Memory) { |
| 2863 | Node *proj_in = result->in(0); |
| 2864 | if (proj_in->is_Allocate() && proj_in->_idx == (uint)toop->instance_id()) { |
| 2865 | break; // hit one of our sentinels |
| 2866 | } else if (proj_in->is_Call()) { |
| 2867 | // ArrayCopy node processed here as well |
| 2868 | CallNode *call = proj_in->as_Call(); |
| 2869 | if (!call->may_modify(toop, igvn)) { |
| 2870 | result = call->in(TypeFunc::Memory); |
| 2871 | } |
| 2872 | } else if (proj_in->is_Initialize()) { |
| 2873 | AllocateNode* alloc = proj_in->as_Initialize()->allocation(); |
| 2874 | // Stop if this is the initialization for the object instance which |
| 2875 | // which contains this memory slice, otherwise skip over it. |
| 2876 | if (alloc == NULL__null || alloc->_idx != (uint)toop->instance_id()) { |
| 2877 | result = proj_in->in(TypeFunc::Memory); |
| 2878 | } |
| 2879 | } else if (proj_in->is_MemBar()) { |
| 2880 | // Check if there is an array copy for a clone |
| 2881 | // Step over GC barrier when ReduceInitialCardMarks is disabled |
| 2882 | BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); |
| 2883 | Node* control_proj_ac = bs->step_over_gc_barrier(proj_in->in(0)); |
| 2884 | |
| 2885 | if (control_proj_ac->is_Proj() && control_proj_ac->in(0)->is_ArrayCopy()) { |
| 2886 | // Stop if it is a clone |
| 2887 | ArrayCopyNode* ac = control_proj_ac->in(0)->as_ArrayCopy(); |
| 2888 | if (ac->may_modify(toop, igvn)) { |
| 2889 | break; |
| 2890 | } |
| 2891 | } |
| 2892 | result = proj_in->in(TypeFunc::Memory); |
| 2893 | } |
| 2894 | } else if (result->is_MergeMem()) { |
| 2895 | MergeMemNode *mmem = result->as_MergeMem(); |
| 2896 | result = step_through_mergemem(mmem, alias_idx, toop); |
| 2897 | if (result == mmem->base_memory()) { |
| 2898 | // Didn't find instance memory, search through general slice recursively. |
| 2899 | result = mmem->memory_at(C->get_general_index(alias_idx)); |
| 2900 | result = find_inst_mem(result, alias_idx, orig_phis); |
| 2901 | if (C->failing()) { |
| 2902 | return NULL__null; |
| 2903 | } |
| 2904 | mmem->set_memory_at(alias_idx, result); |
| 2905 | } |
| 2906 | } else if (result->is_Phi() && |
| 2907 | C->get_alias_index(result->as_Phi()->adr_type()) != alias_idx) { |
| 2908 | Node *un = result->as_Phi()->unique_input(igvn); |
| 2909 | if (un != NULL__null) { |
| 2910 | orig_phis.append_if_missing(result->as_Phi()); |
| 2911 | result = un; |
| 2912 | } else { |
| 2913 | break; |
| 2914 | } |
| 2915 | } else if (result->is_ClearArray()) { |
| 2916 | if (!ClearArrayNode::step_through(&result, (uint)toop->instance_id(), igvn)) { |
| 2917 | // Can not bypass initialization of the instance |
| 2918 | // we are looking for. |
| 2919 | break; |
| 2920 | } |
| 2921 | // Otherwise skip it (the call updated 'result' value). |
| 2922 | } else if (result->Opcode() == Op_SCMemProj) { |
| 2923 | Node* mem = result->in(0); |
| 2924 | Node* adr = NULL__null; |
| 2925 | if (mem->is_LoadStore()) { |
| 2926 | adr = mem->in(MemNode::Address); |
| 2927 | } else { |
| 2928 | assert(mem->Opcode() == Op_EncodeISOArray ||do { if (!(mem->Opcode() == Op_EncodeISOArray || mem->Opcode () == Op_StrCompressedCopy)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2929, "assert(" "mem->Opcode() == Op_EncodeISOArray || mem->Opcode() == Op_StrCompressedCopy" ") failed", "sanity"); ::breakpoint(); } } while (0) |
| 2929 | mem->Opcode() == Op_StrCompressedCopy, "sanity")do { if (!(mem->Opcode() == Op_EncodeISOArray || mem->Opcode () == Op_StrCompressedCopy)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2929, "assert(" "mem->Opcode() == Op_EncodeISOArray || mem->Opcode() == Op_StrCompressedCopy" ") failed", "sanity"); ::breakpoint(); } } while (0); |
| 2930 | adr = mem->in(3); // Memory edge corresponds to destination array |
| 2931 | } |
| 2932 | const Type *at = igvn->type(adr); |
| 2933 | if (at != Type::TOP) { |
| 2934 | assert(at->isa_ptr() != NULL, "pointer type required.")do { if (!(at->isa_ptr() != __null)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2934, "assert(" "at->isa_ptr() != __null" ") failed", "pointer type required." ); ::breakpoint(); } } while (0); |
| 2935 | int idx = C->get_alias_index(at->is_ptr()); |
| 2936 | if (idx == alias_idx) { |
| 2937 | // Assert in debug mode |
| 2938 | assert(false, "Object is not scalar replaceable if a LoadStore node accesses its field")do { if (!(false)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2938, "assert(" "false" ") failed", "Object is not scalar replaceable if a LoadStore node accesses its field" ); ::breakpoint(); } } while (0); |
| 2939 | break; // In product mode return SCMemProj node |
| 2940 | } |
| 2941 | } |
| 2942 | result = mem->in(MemNode::Memory); |
| 2943 | } else if (result->Opcode() == Op_StrInflatedCopy) { |
| 2944 | Node* adr = result->in(3); // Memory edge corresponds to destination array |
| 2945 | const Type *at = igvn->type(adr); |
| 2946 | if (at != Type::TOP) { |
| 2947 | assert(at->isa_ptr() != NULL, "pointer type required.")do { if (!(at->isa_ptr() != __null)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2947, "assert(" "at->isa_ptr() != __null" ") failed", "pointer type required." ); ::breakpoint(); } } while (0); |
| 2948 | int idx = C->get_alias_index(at->is_ptr()); |
| 2949 | if (idx == alias_idx) { |
| 2950 | // Assert in debug mode |
| 2951 | assert(false, "Object is not scalar replaceable if a StrInflatedCopy node accesses its field")do { if (!(false)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2951, "assert(" "false" ") failed", "Object is not scalar replaceable if a StrInflatedCopy node accesses its field" ); ::breakpoint(); } } while (0); |
| 2952 | break; // In product mode return SCMemProj node |
| 2953 | } |
| 2954 | } |
| 2955 | result = result->in(MemNode::Memory); |
| 2956 | } |
| 2957 | } |
| 2958 | if (result->is_Phi()) { |
| 2959 | PhiNode *mphi = result->as_Phi(); |
| 2960 | assert(mphi->bottom_type() == Type::MEMORY, "memory phi required")do { if (!(mphi->bottom_type() == Type::MEMORY)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 2960, "assert(" "mphi->bottom_type() == Type::MEMORY" ") failed" , "memory phi required"); ::breakpoint(); } } while (0); |
| 2961 | const TypePtr *t = mphi->adr_type(); |
| 2962 | if (!is_instance) { |
| 2963 | // Push all non-instance Phis on the orig_phis worklist to update inputs |
| 2964 | // during Phase 4 if needed. |
| 2965 | orig_phis.append_if_missing(mphi); |
| 2966 | } else if (C->get_alias_index(t) != alias_idx) { |
| 2967 | // Create a new Phi with the specified alias index type. |
| 2968 | result = split_memory_phi(mphi, alias_idx, orig_phis); |
| 2969 | } |
| 2970 | } |
| 2971 | // the result is either MemNode, PhiNode, InitializeNode. |
| 2972 | return result; |
| 2973 | } |
| 2974 | |
| 2975 | // |
| 2976 | // Convert the types of non-escaped object to instance types where possible, |
| 2977 | // propagate the new type information through the graph, and update memory |
| 2978 | // edges and MergeMem inputs to reflect the new type. |
| 2979 | // |
| 2980 | // We start with allocations (and calls which may be allocations) on alloc_worklist. |
| 2981 | // The processing is done in 4 phases: |
| 2982 | // |
| 2983 | // Phase 1: Process possible allocations from alloc_worklist. Create instance |
| 2984 | // types for the CheckCastPP for allocations where possible. |
| 2985 | // Propagate the new types through users as follows: |
| 2986 | // casts and Phi: push users on alloc_worklist |
| 2987 | // AddP: cast Base and Address inputs to the instance type |
| 2988 | // push any AddP users on alloc_worklist and push any memnode |
| 2989 | // users onto memnode_worklist. |
| 2990 | // Phase 2: Process MemNode's from memnode_worklist. compute new address type and |
| 2991 | // search the Memory chain for a store with the appropriate type |
| 2992 | // address type. If a Phi is found, create a new version with |
| 2993 | // the appropriate memory slices from each of the Phi inputs. |
| 2994 | // For stores, process the users as follows: |
| 2995 | // MemNode: push on memnode_worklist |
| 2996 | // MergeMem: push on mergemem_worklist |
| 2997 | // Phase 3: Process MergeMem nodes from mergemem_worklist. Walk each memory slice |
| 2998 | // moving the first node encountered of each instance type to the |
| 2999 | // the input corresponding to its alias index. |
| 3000 | // appropriate memory slice. |
| 3001 | // Phase 4: Update the inputs of non-instance memory Phis and the Memory input of memnodes. |
| 3002 | // |
| 3003 | // In the following example, the CheckCastPP nodes are the cast of allocation |
| 3004 | // results and the allocation of node 29 is non-escaped and eligible to be an |
| 3005 | // instance type. |
| 3006 | // |
| 3007 | // We start with: |
| 3008 | // |
| 3009 | // 7 Parm #memory |
| 3010 | // 10 ConI "12" |
| 3011 | // 19 CheckCastPP "Foo" |
| 3012 | // 20 AddP _ 19 19 10 Foo+12 alias_index=4 |
| 3013 | // 29 CheckCastPP "Foo" |
| 3014 | // 30 AddP _ 29 29 10 Foo+12 alias_index=4 |
| 3015 | // |
| 3016 | // 40 StoreP 25 7 20 ... alias_index=4 |
| 3017 | // 50 StoreP 35 40 30 ... alias_index=4 |
| 3018 | // 60 StoreP 45 50 20 ... alias_index=4 |
| 3019 | // 70 LoadP _ 60 30 ... alias_index=4 |
| 3020 | // 80 Phi 75 50 60 Memory alias_index=4 |
| 3021 | // 90 LoadP _ 80 30 ... alias_index=4 |
| 3022 | // 100 LoadP _ 80 20 ... alias_index=4 |
| 3023 | // |
| 3024 | // |
| 3025 | // Phase 1 creates an instance type for node 29 assigning it an instance id of 24 |
| 3026 | // and creating a new alias index for node 30. This gives: |
| 3027 | // |
| 3028 | // 7 Parm #memory |
| 3029 | // 10 ConI "12" |
| 3030 | // 19 CheckCastPP "Foo" |
| 3031 | // 20 AddP _ 19 19 10 Foo+12 alias_index=4 |
| 3032 | // 29 CheckCastPP "Foo" iid=24 |
| 3033 | // 30 AddP _ 29 29 10 Foo+12 alias_index=6 iid=24 |
| 3034 | // |
| 3035 | // 40 StoreP 25 7 20 ... alias_index=4 |
| 3036 | // 50 StoreP 35 40 30 ... alias_index=6 |
| 3037 | // 60 StoreP 45 50 20 ... alias_index=4 |
| 3038 | // 70 LoadP _ 60 30 ... alias_index=6 |
| 3039 | // 80 Phi 75 50 60 Memory alias_index=4 |
| 3040 | // 90 LoadP _ 80 30 ... alias_index=6 |
| 3041 | // 100 LoadP _ 80 20 ... alias_index=4 |
| 3042 | // |
| 3043 | // In phase 2, new memory inputs are computed for the loads and stores, |
| 3044 | // And a new version of the phi is created. In phase 4, the inputs to |
| 3045 | // node 80 are updated and then the memory nodes are updated with the |
| 3046 | // values computed in phase 2. This results in: |
| 3047 | // |
| 3048 | // 7 Parm #memory |
| 3049 | // 10 ConI "12" |
| 3050 | // 19 CheckCastPP "Foo" |
| 3051 | // 20 AddP _ 19 19 10 Foo+12 alias_index=4 |
| 3052 | // 29 CheckCastPP "Foo" iid=24 |
| 3053 | // 30 AddP _ 29 29 10 Foo+12 alias_index=6 iid=24 |
| 3054 | // |
| 3055 | // 40 StoreP 25 7 20 ... alias_index=4 |
| 3056 | // 50 StoreP 35 7 30 ... alias_index=6 |
| 3057 | // 60 StoreP 45 40 20 ... alias_index=4 |
| 3058 | // 70 LoadP _ 50 30 ... alias_index=6 |
| 3059 | // 80 Phi 75 40 60 Memory alias_index=4 |
| 3060 | // 120 Phi 75 50 50 Memory alias_index=6 |
| 3061 | // 90 LoadP _ 120 30 ... alias_index=6 |
| 3062 | // 100 LoadP _ 80 20 ... alias_index=4 |
| 3063 | // |
| 3064 | void ConnectionGraph::split_unique_types(GrowableArray<Node *> &alloc_worklist, |
| 3065 | GrowableArray<ArrayCopyNode*> &arraycopy_worklist, |
| 3066 | GrowableArray<MergeMemNode*> &mergemem_worklist) { |
| 3067 | GrowableArray<Node *> memnode_worklist; |
| 3068 | GrowableArray<PhiNode *> orig_phis; |
| 3069 | PhaseIterGVN *igvn = _igvn; |
| 3070 | uint new_index_start = (uint) _compile->num_alias_types(); |
| 3071 | VectorSet visited; |
| 3072 | ideal_nodes.clear(); // Reset for use with set_map/get_map. |
| 3073 | uint unique_old = _compile->unique(); |
| 3074 | |
| 3075 | // Phase 1: Process possible allocations from alloc_worklist. |
| 3076 | // Create instance types for the CheckCastPP for allocations where possible. |
| 3077 | // |
| 3078 | // (Note: don't forget to change the order of the second AddP node on |
| 3079 | // the alloc_worklist if the order of the worklist processing is changed, |
| 3080 | // see the comment in find_second_addp().) |
| 3081 | // |
| 3082 | while (alloc_worklist.length() != 0) { |
| 3083 | Node *n = alloc_worklist.pop(); |
| 3084 | uint ni = n->_idx; |
| 3085 | if (n->is_Call()) { |
| 3086 | CallNode *alloc = n->as_Call(); |
| 3087 | // copy escape information to call node |
| 3088 | PointsToNode* ptn = ptnode_adr(alloc->_idx); |
| 3089 | PointsToNode::EscapeState es = ptn->escape_state(); |
| 3090 | // We have an allocation or call which returns a Java object, |
| 3091 | // see if it is non-escaped. |
| 3092 | if (es != PointsToNode::NoEscape || !ptn->scalar_replaceable()) { |
| 3093 | continue; |
| 3094 | } |
| 3095 | // Find CheckCastPP for the allocate or for the return value of a call |
| 3096 | n = alloc->result_cast(); |
| 3097 | if (n == NULL__null) { // No uses except Initialize node |
| 3098 | if (alloc->is_Allocate()) { |
| 3099 | // Set the scalar_replaceable flag for allocation |
| 3100 | // so it could be eliminated if it has no uses. |
| 3101 | alloc->as_Allocate()->_is_scalar_replaceable = true; |
| 3102 | } |
| 3103 | continue; |
| 3104 | } |
| 3105 | if (!n->is_CheckCastPP()) { // not unique CheckCastPP. |
| 3106 | // we could reach here for allocate case if one init is associated with many allocs. |
| 3107 | if (alloc->is_Allocate()) { |
| 3108 | alloc->as_Allocate()->_is_scalar_replaceable = false; |
| 3109 | } |
| 3110 | continue; |
| 3111 | } |
| 3112 | |
| 3113 | // The inline code for Object.clone() casts the allocation result to |
| 3114 | // java.lang.Object and then to the actual type of the allocated |
| 3115 | // object. Detect this case and use the second cast. |
| 3116 | // Also detect j.l.reflect.Array.newInstance(jobject, jint) case when |
| 3117 | // the allocation result is cast to java.lang.Object and then |
| 3118 | // to the actual Array type. |
| 3119 | if (alloc->is_Allocate() && n->as_Type()->type() == TypeInstPtr::NOTNULL |
| 3120 | && (alloc->is_AllocateArray() || |
| 3121 | igvn->type(alloc->in(AllocateNode::KlassNode)) != TypeInstKlassPtr::OBJECT)) { |
| 3122 | Node *cast2 = NULL__null; |
| 3123 | for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { |
| 3124 | Node *use = n->fast_out(i); |
| 3125 | if (use->is_CheckCastPP()) { |
| 3126 | cast2 = use; |
| 3127 | break; |
| 3128 | } |
| 3129 | } |
| 3130 | if (cast2 != NULL__null) { |
| 3131 | n = cast2; |
| 3132 | } else { |
| 3133 | // Non-scalar replaceable if the allocation type is unknown statically |
| 3134 | // (reflection allocation), the object can't be restored during |
| 3135 | // deoptimization without precise type. |
| 3136 | continue; |
| 3137 | } |
| 3138 | } |
| 3139 | |
| 3140 | const TypeOopPtr *t = igvn->type(n)->isa_oopptr(); |
| 3141 | if (t == NULL__null) { |
| 3142 | continue; // not a TypeOopPtr |
| 3143 | } |
| 3144 | if (!t->klass_is_exact()) { |
| 3145 | continue; // not an unique type |
| 3146 | } |
| 3147 | if (alloc->is_Allocate()) { |
| 3148 | // Set the scalar_replaceable flag for allocation |
| 3149 | // so it could be eliminated. |
| 3150 | alloc->as_Allocate()->_is_scalar_replaceable = true; |
| 3151 | } |
| 3152 | set_escape_state(ptnode_adr(n->_idx), es); // CheckCastPP escape state |
| 3153 | // in order for an object to be scalar-replaceable, it must be: |
| 3154 | // - a direct allocation (not a call returning an object) |
| 3155 | // - non-escaping |
| 3156 | // - eligible to be a unique type |
| 3157 | // - not determined to be ineligible by escape analysis |
| 3158 | set_map(alloc, n); |
| 3159 | set_map(n, alloc); |
| 3160 | const TypeOopPtr* tinst = t->cast_to_instance_id(ni); |
| 3161 | igvn->hash_delete(n); |
| 3162 | igvn->set_type(n, tinst); |
| 3163 | n->raise_bottom_type(tinst); |
| 3164 | igvn->hash_insert(n); |
| 3165 | record_for_optimizer(n); |
| 3166 | // Allocate an alias index for the header fields. Accesses to |
| 3167 | // the header emitted during macro expansion wouldn't have |
| 3168 | // correct memory state otherwise. |
| 3169 | _compile->get_alias_index(tinst->add_offset(oopDesc::mark_offset_in_bytes())); |
| 3170 | _compile->get_alias_index(tinst->add_offset(oopDesc::klass_offset_in_bytes())); |
| 3171 | if (alloc->is_Allocate() && (t->isa_instptr() || t->isa_aryptr())) { |
| 3172 | |
| 3173 | // First, put on the worklist all Field edges from Connection Graph |
| 3174 | // which is more accurate than putting immediate users from Ideal Graph. |
| 3175 | for (EdgeIterator e(ptn); e.has_next(); e.next()) { |
| 3176 | PointsToNode* tgt = e.get(); |
| 3177 | if (tgt->is_Arraycopy()) { |
| 3178 | continue; |
| 3179 | } |
| 3180 | Node* use = tgt->ideal_node(); |
| 3181 | assert(tgt->is_Field() && use->is_AddP(),do { if (!(tgt->is_Field() && use->is_AddP())) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3182, "assert(" "tgt->is_Field() && use->is_AddP()" ") failed", "only AddP nodes are Field edges in CG"); ::breakpoint (); } } while (0) |
| 3182 | "only AddP nodes are Field edges in CG")do { if (!(tgt->is_Field() && use->is_AddP())) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3182, "assert(" "tgt->is_Field() && use->is_AddP()" ") failed", "only AddP nodes are Field edges in CG"); ::breakpoint (); } } while (0); |
| 3183 | if (use->outcnt() > 0) { // Don't process dead nodes |
| 3184 | Node* addp2 = find_second_addp(use, use->in(AddPNode::Base)); |
| 3185 | if (addp2 != NULL__null) { |
| 3186 | assert(alloc->is_AllocateArray(),"array allocation was expected")do { if (!(alloc->is_AllocateArray())) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3186, "assert(" "alloc->is_AllocateArray()" ") failed", "array allocation was expected" ); ::breakpoint(); } } while (0); |
| 3187 | alloc_worklist.append_if_missing(addp2); |
| 3188 | } |
| 3189 | alloc_worklist.append_if_missing(use); |
| 3190 | } |
| 3191 | } |
| 3192 | |
| 3193 | // An allocation may have an Initialize which has raw stores. Scan |
| 3194 | // the users of the raw allocation result and push AddP users |
| 3195 | // on alloc_worklist. |
| 3196 | Node *raw_result = alloc->proj_out_or_null(TypeFunc::Parms); |
| 3197 | assert (raw_result != NULL, "must have an allocation result")do { if (!(raw_result != __null)) { (*g_assert_poison) = 'X'; ; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3197, "assert(" "raw_result != __null" ") failed", "must have an allocation result" ); ::breakpoint(); } } while (0); |
| 3198 | for (DUIterator_Fast imax, i = raw_result->fast_outs(imax); i < imax; i++) { |
| 3199 | Node *use = raw_result->fast_out(i); |
| 3200 | if (use->is_AddP() && use->outcnt() > 0) { // Don't process dead nodes |
| 3201 | Node* addp2 = find_second_addp(use, raw_result); |
| 3202 | if (addp2 != NULL__null) { |
| 3203 | assert(alloc->is_AllocateArray(),"array allocation was expected")do { if (!(alloc->is_AllocateArray())) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3203, "assert(" "alloc->is_AllocateArray()" ") failed", "array allocation was expected" ); ::breakpoint(); } } while (0); |
| 3204 | alloc_worklist.append_if_missing(addp2); |
| 3205 | } |
| 3206 | alloc_worklist.append_if_missing(use); |
| 3207 | } else if (use->is_MemBar()) { |
| 3208 | memnode_worklist.append_if_missing(use); |
| 3209 | } |
| 3210 | } |
| 3211 | } |
| 3212 | } else if (n->is_AddP()) { |
| 3213 | JavaObjectNode* jobj = unique_java_object(get_addp_base(n)); |
| 3214 | if (jobj == NULL__null || jobj == phantom_obj) { |
| 3215 | #ifdef ASSERT1 |
| 3216 | ptnode_adr(get_addp_base(n)->_idx)->dump(); |
| 3217 | ptnode_adr(n->_idx)->dump(); |
| 3218 | assert(jobj != NULL && jobj != phantom_obj, "escaped allocation")do { if (!(jobj != __null && jobj != phantom_obj)) { ( *g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3218, "assert(" "jobj != __null && jobj != phantom_obj" ") failed", "escaped allocation"); ::breakpoint(); } } while (0); |
| 3219 | #endif |
| 3220 | _compile->record_failure(_invocation > 0 ? C2Compiler::retry_no_iterative_escape_analysis() : C2Compiler::retry_no_escape_analysis()); |
| 3221 | return; |
| 3222 | } |
| 3223 | Node *base = get_map(jobj->idx()); // CheckCastPP node |
| 3224 | if (!split_AddP(n, base)) continue; // wrong type from dead path |
| 3225 | } else if (n->is_Phi() || |
| 3226 | n->is_CheckCastPP() || |
| 3227 | n->is_EncodeP() || |
| 3228 | n->is_DecodeN() || |
| 3229 | (n->is_ConstraintCast() && n->Opcode() == Op_CastPP)) { |
| 3230 | if (visited.test_set(n->_idx)) { |
| 3231 | assert(n->is_Phi(), "loops only through Phi's")do { if (!(n->is_Phi())) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3231, "assert(" "n->is_Phi()" ") failed", "loops only through Phi's" ); ::breakpoint(); } } while (0); |
| 3232 | continue; // already processed |
| 3233 | } |
| 3234 | JavaObjectNode* jobj = unique_java_object(n); |
| 3235 | if (jobj == NULL__null || jobj == phantom_obj) { |
| 3236 | #ifdef ASSERT1 |
| 3237 | ptnode_adr(n->_idx)->dump(); |
| 3238 | assert(jobj != NULL && jobj != phantom_obj, "escaped allocation")do { if (!(jobj != __null && jobj != phantom_obj)) { ( *g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3238, "assert(" "jobj != __null && jobj != phantom_obj" ") failed", "escaped allocation"); ::breakpoint(); } } while (0); |
| 3239 | #endif |
| 3240 | _compile->record_failure(_invocation > 0 ? C2Compiler::retry_no_iterative_escape_analysis() : C2Compiler::retry_no_escape_analysis()); |
| 3241 | return; |
| 3242 | } else { |
| 3243 | Node *val = get_map(jobj->idx()); // CheckCastPP node |
| 3244 | TypeNode *tn = n->as_Type(); |
| 3245 | const TypeOopPtr* tinst = igvn->type(val)->isa_oopptr(); |
| 3246 | assert(tinst != NULL && tinst->is_known_instance() &&do { if (!(tinst != __null && tinst->is_known_instance () && tinst->instance_id() == jobj->idx())) { ( *g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3247, "assert(" "tinst != __null && tinst->is_known_instance() && tinst->instance_id() == jobj->idx()" ") failed", "instance type expected."); ::breakpoint(); } } while (0) |
| 3247 | tinst->instance_id() == jobj->idx() , "instance type expected.")do { if (!(tinst != __null && tinst->is_known_instance () && tinst->instance_id() == jobj->idx())) { ( *g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3247, "assert(" "tinst != __null && tinst->is_known_instance() && tinst->instance_id() == jobj->idx()" ") failed", "instance type expected."); ::breakpoint(); } } while (0); |
| 3248 | |
| 3249 | const Type *tn_type = igvn->type(tn); |
| 3250 | const TypeOopPtr *tn_t; |
| 3251 | if (tn_type->isa_narrowoop()) { |
| 3252 | tn_t = tn_type->make_ptr()->isa_oopptr(); |
| 3253 | } else { |
| 3254 | tn_t = tn_type->isa_oopptr(); |
| 3255 | } |
| 3256 | if (tn_t != NULL__null && tinst->klass()->is_subtype_of(tn_t->klass())) { |
| 3257 | if (tn_type->isa_narrowoop()) { |
| 3258 | tn_type = tinst->make_narrowoop(); |
| 3259 | } else { |
| 3260 | tn_type = tinst; |
| 3261 | } |
| 3262 | igvn->hash_delete(tn); |
| 3263 | igvn->set_type(tn, tn_type); |
| 3264 | tn->set_type(tn_type); |
| 3265 | igvn->hash_insert(tn); |
| 3266 | record_for_optimizer(n); |
| 3267 | } else { |
| 3268 | assert(tn_type == TypePtr::NULL_PTR ||do { if (!(tn_type == TypePtr::NULL_PTR || tn_t != __null && !tinst->klass()->is_subtype_of(tn_t->klass()))) { ( *g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3270, "assert(" "tn_type == TypePtr::NULL_PTR || tn_t != __null && !tinst->klass()->is_subtype_of(tn_t->klass())" ") failed", "unexpected type"); ::breakpoint(); } } while (0 ) |
| 3269 | tn_t != NULL && !tinst->klass()->is_subtype_of(tn_t->klass()),do { if (!(tn_type == TypePtr::NULL_PTR || tn_t != __null && !tinst->klass()->is_subtype_of(tn_t->klass()))) { ( *g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3270, "assert(" "tn_type == TypePtr::NULL_PTR || tn_t != __null && !tinst->klass()->is_subtype_of(tn_t->klass())" ") failed", "unexpected type"); ::breakpoint(); } } while (0 ) |
| 3270 | "unexpected type")do { if (!(tn_type == TypePtr::NULL_PTR || tn_t != __null && !tinst->klass()->is_subtype_of(tn_t->klass()))) { ( *g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3270, "assert(" "tn_type == TypePtr::NULL_PTR || tn_t != __null && !tinst->klass()->is_subtype_of(tn_t->klass())" ") failed", "unexpected type"); ::breakpoint(); } } while (0 ); |
| 3271 | continue; // Skip dead path with different type |
| 3272 | } |
| 3273 | } |
| 3274 | } else { |
| 3275 | debug_only(n->dump();)n->dump(); |
| 3276 | assert(false, "EA: unexpected node")do { if (!(false)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3276, "assert(" "false" ") failed", "EA: unexpected node"); ::breakpoint(); } } while (0); |
| 3277 | continue; |
| 3278 | } |
| 3279 | // push allocation's users on appropriate worklist |
| 3280 | for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { |
| 3281 | Node *use = n->fast_out(i); |
| 3282 | if(use->is_Mem() && use->in(MemNode::Address) == n) { |
| 3283 | // Load/store to instance's field |
| 3284 | memnode_worklist.append_if_missing(use); |
| 3285 | } else if (use->is_MemBar()) { |
| 3286 | if (use->in(TypeFunc::Memory) == n) { // Ignore precedent edge |
| 3287 | memnode_worklist.append_if_missing(use); |
| 3288 | } |
| 3289 | } else if (use->is_AddP() && use->outcnt() > 0) { // No dead nodes |
| 3290 | Node* addp2 = find_second_addp(use, n); |
| 3291 | if (addp2 != NULL__null) { |
| 3292 | alloc_worklist.append_if_missing(addp2); |
| 3293 | } |
| 3294 | alloc_worklist.append_if_missing(use); |
| 3295 | } else if (use->is_Phi() || |
| 3296 | use->is_CheckCastPP() || |
| 3297 | use->is_EncodeNarrowPtr() || |
| 3298 | use->is_DecodeNarrowPtr() || |
| 3299 | (use->is_ConstraintCast() && use->Opcode() == Op_CastPP)) { |
| 3300 | alloc_worklist.append_if_missing(use); |
| 3301 | #ifdef ASSERT1 |
| 3302 | } else if (use->is_Mem()) { |
| 3303 | assert(use->in(MemNode::Address) != n, "EA: missing allocation reference path")do { if (!(use->in(MemNode::Address) != n)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3303, "assert(" "use->in(MemNode::Address) != n" ") failed" , "EA: missing allocation reference path"); ::breakpoint(); } } while (0); |
| 3304 | } else if (use->is_MergeMem()) { |
| 3305 | assert(mergemem_worklist.contains(use->as_MergeMem()), "EA: missing MergeMem node in the worklist")do { if (!(mergemem_worklist.contains(use->as_MergeMem())) ) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3305, "assert(" "mergemem_worklist.contains(use->as_MergeMem())" ") failed", "EA: missing MergeMem node in the worklist"); :: breakpoint(); } } while (0); |
| 3306 | } else if (use->is_SafePoint()) { |
| 3307 | // Look for MergeMem nodes for calls which reference unique allocation |
| 3308 | // (through CheckCastPP nodes) even for debug info. |
| 3309 | Node* m = use->in(TypeFunc::Memory); |
| 3310 | if (m->is_MergeMem()) { |
| 3311 | assert(mergemem_worklist.contains(m->as_MergeMem()), "EA: missing MergeMem node in the worklist")do { if (!(mergemem_worklist.contains(m->as_MergeMem()))) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3311, "assert(" "mergemem_worklist.contains(m->as_MergeMem())" ") failed", "EA: missing MergeMem node in the worklist"); :: breakpoint(); } } while (0); |
| 3312 | } |
| 3313 | } else if (use->Opcode() == Op_EncodeISOArray) { |
| 3314 | if (use->in(MemNode::Memory) == n || use->in(3) == n) { |
| 3315 | // EncodeISOArray overwrites destination array |
| 3316 | memnode_worklist.append_if_missing(use); |
| 3317 | } |
| 3318 | } else { |
| 3319 | uint op = use->Opcode(); |
| 3320 | if ((op == Op_StrCompressedCopy || op == Op_StrInflatedCopy) && |
| 3321 | (use->in(MemNode::Memory) == n)) { |
| 3322 | // They overwrite memory edge corresponding to destination array, |
| 3323 | memnode_worklist.append_if_missing(use); |
| 3324 | } else if (!(op == Op_CmpP || op == Op_Conv2B || |
| 3325 | op == Op_CastP2X || op == Op_StoreCM || |
| 3326 | op == Op_FastLock || op == Op_AryEq || op == Op_StrComp || op == Op_HasNegatives || |
| 3327 | op == Op_StrCompressedCopy || op == Op_StrInflatedCopy || |
| 3328 | op == Op_StrEquals || op == Op_StrIndexOf || op == Op_StrIndexOfChar || |
| 3329 | op == Op_SubTypeCheck || |
| 3330 | BarrierSet::barrier_set()->barrier_set_c2()->is_gc_barrier_node(use))) { |
| 3331 | n->dump(); |
| 3332 | use->dump(); |
| 3333 | assert(false, "EA: missing allocation reference path")do { if (!(false)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3333, "assert(" "false" ") failed", "EA: missing allocation reference path" ); ::breakpoint(); } } while (0); |
| 3334 | } |
| 3335 | #endif |
| 3336 | } |
| 3337 | } |
| 3338 | |
| 3339 | } |
| 3340 | |
| 3341 | // Go over all ArrayCopy nodes and if one of the inputs has a unique |
| 3342 | // type, record it in the ArrayCopy node so we know what memory this |
| 3343 | // node uses/modified. |
| 3344 | for (int next = 0; next < arraycopy_worklist.length(); next++) { |
| 3345 | ArrayCopyNode* ac = arraycopy_worklist.at(next); |
| 3346 | Node* dest = ac->in(ArrayCopyNode::Dest); |
| 3347 | if (dest->is_AddP()) { |
| 3348 | dest = get_addp_base(dest); |
| 3349 | } |
| 3350 | JavaObjectNode* jobj = unique_java_object(dest); |
| 3351 | if (jobj != NULL__null) { |
| 3352 | Node *base = get_map(jobj->idx()); |
| 3353 | if (base != NULL__null) { |
| 3354 | const TypeOopPtr *base_t = _igvn->type(base)->isa_oopptr(); |
| 3355 | ac->_dest_type = base_t; |
| 3356 | } |
| 3357 | } |
| 3358 | Node* src = ac->in(ArrayCopyNode::Src); |
| 3359 | if (src->is_AddP()) { |
| 3360 | src = get_addp_base(src); |
| 3361 | } |
| 3362 | jobj = unique_java_object(src); |
| 3363 | if (jobj != NULL__null) { |
| 3364 | Node* base = get_map(jobj->idx()); |
| 3365 | if (base != NULL__null) { |
| 3366 | const TypeOopPtr *base_t = _igvn->type(base)->isa_oopptr(); |
| 3367 | ac->_src_type = base_t; |
| 3368 | } |
| 3369 | } |
| 3370 | } |
| 3371 | |
| 3372 | // New alias types were created in split_AddP(). |
| 3373 | uint new_index_end = (uint) _compile->num_alias_types(); |
| 3374 | assert(unique_old == _compile->unique(), "there should be no new ideal nodes after Phase 1")do { if (!(unique_old == _compile->unique())) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3374, "assert(" "unique_old == _compile->unique()" ") failed" , "there should be no new ideal nodes after Phase 1"); ::breakpoint (); } } while (0); |
| 3375 | |
| 3376 | // Phase 2: Process MemNode's from memnode_worklist. compute new address type and |
| 3377 | // compute new values for Memory inputs (the Memory inputs are not |
| 3378 | // actually updated until phase 4.) |
| 3379 | if (memnode_worklist.length() == 0) |
| 3380 | return; // nothing to do |
| 3381 | while (memnode_worklist.length() != 0) { |
| 3382 | Node *n = memnode_worklist.pop(); |
| 3383 | if (visited.test_set(n->_idx)) { |
| 3384 | continue; |
| 3385 | } |
| 3386 | if (n->is_Phi() || n->is_ClearArray()) { |
| 3387 | // we don't need to do anything, but the users must be pushed |
| 3388 | } else if (n->is_MemBar()) { // Initialize, MemBar nodes |
| 3389 | // we don't need to do anything, but the users must be pushed |
| 3390 | n = n->as_MemBar()->proj_out_or_null(TypeFunc::Memory); |
| 3391 | if (n == NULL__null) { |
| 3392 | continue; |
| 3393 | } |
| 3394 | } else if (n->Opcode() == Op_StrCompressedCopy || |
| 3395 | n->Opcode() == Op_EncodeISOArray) { |
| 3396 | // get the memory projection |
| 3397 | n = n->find_out_with(Op_SCMemProj); |
| 3398 | assert(n != NULL && n->Opcode() == Op_SCMemProj, "memory projection required")do { if (!(n != __null && n->Opcode() == Op_SCMemProj )) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3398, "assert(" "n != __null && n->Opcode() == Op_SCMemProj" ") failed", "memory projection required"); ::breakpoint(); } } while (0); |
| 3399 | } else { |
| 3400 | assert(n->is_Mem(), "memory node required.")do { if (!(n->is_Mem())) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3400, "assert(" "n->is_Mem()" ") failed", "memory node required." ); ::breakpoint(); } } while (0); |
| 3401 | Node *addr = n->in(MemNode::Address); |
| 3402 | const Type *addr_t = igvn->type(addr); |
| 3403 | if (addr_t == Type::TOP) { |
| 3404 | continue; |
| 3405 | } |
| 3406 | assert (addr_t->isa_ptr() != NULL, "pointer type required.")do { if (!(addr_t->isa_ptr() != __null)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3406, "assert(" "addr_t->isa_ptr() != __null" ") failed" , "pointer type required."); ::breakpoint(); } } while (0); |
| 3407 | int alias_idx = _compile->get_alias_index(addr_t->is_ptr()); |
| 3408 | assert ((uint)alias_idx < new_index_end, "wrong alias index")do { if (!((uint)alias_idx < new_index_end)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3408, "assert(" "(uint)alias_idx < new_index_end" ") failed" , "wrong alias index"); ::breakpoint(); } } while (0); |
| 3409 | Node *mem = find_inst_mem(n->in(MemNode::Memory), alias_idx, orig_phis); |
| 3410 | if (_compile->failing()) { |
| 3411 | return; |
| 3412 | } |
| 3413 | if (mem != n->in(MemNode::Memory)) { |
| 3414 | // We delay the memory edge update since we need old one in |
| 3415 | // MergeMem code below when instances memory slices are separated. |
| 3416 | set_map(n, mem); |
| 3417 | } |
| 3418 | if (n->is_Load()) { |
| 3419 | continue; // don't push users |
| 3420 | } else if (n->is_LoadStore()) { |
| 3421 | // get the memory projection |
| 3422 | n = n->find_out_with(Op_SCMemProj); |
| 3423 | assert(n != NULL && n->Opcode() == Op_SCMemProj, "memory projection required")do { if (!(n != __null && n->Opcode() == Op_SCMemProj )) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3423, "assert(" "n != __null && n->Opcode() == Op_SCMemProj" ") failed", "memory projection required"); ::breakpoint(); } } while (0); |
| 3424 | } |
| 3425 | } |
| 3426 | // push user on appropriate worklist |
| 3427 | for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) { |
| 3428 | Node *use = n->fast_out(i); |
| 3429 | if (use->is_Phi() || use->is_ClearArray()) { |
| 3430 | memnode_worklist.append_if_missing(use); |
| 3431 | } else if (use->is_Mem() && use->in(MemNode::Memory) == n) { |
| 3432 | if (use->Opcode() == Op_StoreCM) { // Ignore cardmark stores |
| 3433 | continue; |
| 3434 | } |
| 3435 | memnode_worklist.append_if_missing(use); |
| 3436 | } else if (use->is_MemBar()) { |
| 3437 | if (use->in(TypeFunc::Memory) == n) { // Ignore precedent edge |
| 3438 | memnode_worklist.append_if_missing(use); |
| 3439 | } |
| 3440 | #ifdef ASSERT1 |
| 3441 | } else if(use->is_Mem()) { |
| 3442 | assert(use->in(MemNode::Memory) != n, "EA: missing memory path")do { if (!(use->in(MemNode::Memory) != n)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3442, "assert(" "use->in(MemNode::Memory) != n" ") failed" , "EA: missing memory path"); ::breakpoint(); } } while (0); |
| 3443 | } else if (use->is_MergeMem()) { |
| 3444 | assert(mergemem_worklist.contains(use->as_MergeMem()), "EA: missing MergeMem node in the worklist")do { if (!(mergemem_worklist.contains(use->as_MergeMem())) ) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3444, "assert(" "mergemem_worklist.contains(use->as_MergeMem())" ") failed", "EA: missing MergeMem node in the worklist"); :: breakpoint(); } } while (0); |
| 3445 | } else if (use->Opcode() == Op_EncodeISOArray) { |
| 3446 | if (use->in(MemNode::Memory) == n || use->in(3) == n) { |
| 3447 | // EncodeISOArray overwrites destination array |
| 3448 | memnode_worklist.append_if_missing(use); |
| 3449 | } |
| 3450 | } else { |
| 3451 | uint op = use->Opcode(); |
| 3452 | if ((use->in(MemNode::Memory) == n) && |
| 3453 | (op == Op_StrCompressedCopy || op == Op_StrInflatedCopy)) { |
| 3454 | // They overwrite memory edge corresponding to destination array, |
| 3455 | memnode_worklist.append_if_missing(use); |
| 3456 | } else if (!(BarrierSet::barrier_set()->barrier_set_c2()->is_gc_barrier_node(use) || |
| 3457 | op == Op_AryEq || op == Op_StrComp || op == Op_HasNegatives || |
| 3458 | op == Op_StrCompressedCopy || op == Op_StrInflatedCopy || |
| 3459 | op == Op_StrEquals || op == Op_StrIndexOf || op == Op_StrIndexOfChar)) { |
| 3460 | n->dump(); |
| 3461 | use->dump(); |
| 3462 | assert(false, "EA: missing memory path")do { if (!(false)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3462, "assert(" "false" ") failed", "EA: missing memory path" ); ::breakpoint(); } } while (0); |
| 3463 | } |
| 3464 | #endif |
| 3465 | } |
| 3466 | } |
| 3467 | } |
| 3468 | |
| 3469 | // Phase 3: Process MergeMem nodes from mergemem_worklist. |
| 3470 | // Walk each memory slice moving the first node encountered of each |
| 3471 | // instance type to the the input corresponding to its alias index. |
| 3472 | uint length = mergemem_worklist.length(); |
| 3473 | for( uint next = 0; next < length; ++next ) { |
| 3474 | MergeMemNode* nmm = mergemem_worklist.at(next); |
| 3475 | assert(!visited.test_set(nmm->_idx), "should not be visited before")do { if (!(!visited.test_set(nmm->_idx))) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3475, "assert(" "!visited.test_set(nmm->_idx)" ") failed" , "should not be visited before"); ::breakpoint(); } } while ( 0); |
| 3476 | // Note: we don't want to use MergeMemStream here because we only want to |
| 3477 | // scan inputs which exist at the start, not ones we add during processing. |
| 3478 | // Note 2: MergeMem may already contains instance memory slices added |
| 3479 | // during find_inst_mem() call when memory nodes were processed above. |
| 3480 | igvn->hash_delete(nmm); |
| 3481 | uint nslices = MIN2(nmm->req(), new_index_start); |
| 3482 | for (uint i = Compile::AliasIdxRaw+1; i < nslices; i++) { |
| 3483 | Node* mem = nmm->in(i); |
| 3484 | Node* cur = NULL__null; |
| 3485 | if (mem == NULL__null || mem->is_top()) { |
| 3486 | continue; |
| 3487 | } |
| 3488 | // First, update mergemem by moving memory nodes to corresponding slices |
| 3489 | // if their type became more precise since this mergemem was created. |
| 3490 | while (mem->is_Mem()) { |
| 3491 | const Type *at = igvn->type(mem->in(MemNode::Address)); |
| 3492 | if (at != Type::TOP) { |
| 3493 | assert (at->isa_ptr() != NULL, "pointer type required.")do { if (!(at->isa_ptr() != __null)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3493, "assert(" "at->isa_ptr() != __null" ") failed", "pointer type required." ); ::breakpoint(); } } while (0); |
| 3494 | uint idx = (uint)_compile->get_alias_index(at->is_ptr()); |
| 3495 | if (idx == i) { |
| 3496 | if (cur == NULL__null) { |
| 3497 | cur = mem; |
| 3498 | } |
| 3499 | } else { |
| 3500 | if (idx >= nmm->req() || nmm->is_empty_memory(nmm->in(idx))) { |
| 3501 | nmm->set_memory_at(idx, mem); |
| 3502 | } |
| 3503 | } |
| 3504 | } |
| 3505 | mem = mem->in(MemNode::Memory); |
| 3506 | } |
| 3507 | nmm->set_memory_at(i, (cur != NULL__null) ? cur : mem); |
| 3508 | // Find any instance of the current type if we haven't encountered |
| 3509 | // already a memory slice of the instance along the memory chain. |
| 3510 | for (uint ni = new_index_start; ni < new_index_end; ni++) { |
| 3511 | if((uint)_compile->get_general_index(ni) == i) { |
| 3512 | Node *m = (ni >= nmm->req()) ? nmm->empty_memory() : nmm->in(ni); |
| 3513 | if (nmm->is_empty_memory(m)) { |
| 3514 | Node* result = find_inst_mem(mem, ni, orig_phis); |
| 3515 | if (_compile->failing()) { |
| 3516 | return; |
| 3517 | } |
| 3518 | nmm->set_memory_at(ni, result); |
| 3519 | } |
| 3520 | } |
| 3521 | } |
| 3522 | } |
| 3523 | // Find the rest of instances values |
| 3524 | for (uint ni = new_index_start; ni < new_index_end; ni++) { |
| 3525 | const TypeOopPtr *tinst = _compile->get_adr_type(ni)->isa_oopptr(); |
| 3526 | Node* result = step_through_mergemem(nmm, ni, tinst); |
| 3527 | if (result == nmm->base_memory()) { |
| 3528 | // Didn't find instance memory, search through general slice recursively. |
| 3529 | result = nmm->memory_at(_compile->get_general_index(ni)); |
| 3530 | result = find_inst_mem(result, ni, orig_phis); |
| 3531 | if (_compile->failing()) { |
| 3532 | return; |
| 3533 | } |
| 3534 | nmm->set_memory_at(ni, result); |
| 3535 | } |
| 3536 | } |
| 3537 | igvn->hash_insert(nmm); |
| 3538 | record_for_optimizer(nmm); |
| 3539 | } |
| 3540 | |
| 3541 | // Phase 4: Update the inputs of non-instance memory Phis and |
| 3542 | // the Memory input of memnodes |
| 3543 | // First update the inputs of any non-instance Phi's from |
| 3544 | // which we split out an instance Phi. Note we don't have |
| 3545 | // to recursively process Phi's encountered on the input memory |
| 3546 | // chains as is done in split_memory_phi() since they will |
| 3547 | // also be processed here. |
| 3548 | for (int j = 0; j < orig_phis.length(); j++) { |
| 3549 | PhiNode *phi = orig_phis.at(j); |
| 3550 | int alias_idx = _compile->get_alias_index(phi->adr_type()); |
| 3551 | igvn->hash_delete(phi); |
| 3552 | for (uint i = 1; i < phi->req(); i++) { |
| 3553 | Node *mem = phi->in(i); |
| 3554 | Node *new_mem = find_inst_mem(mem, alias_idx, orig_phis); |
| 3555 | if (_compile->failing()) { |
| 3556 | return; |
| 3557 | } |
| 3558 | if (mem != new_mem) { |
| 3559 | phi->set_req(i, new_mem); |
| 3560 | } |
| 3561 | } |
| 3562 | igvn->hash_insert(phi); |
| 3563 | record_for_optimizer(phi); |
| 3564 | } |
| 3565 | |
| 3566 | // Update the memory inputs of MemNodes with the value we computed |
| 3567 | // in Phase 2 and move stores memory users to corresponding memory slices. |
| 3568 | // Disable memory split verification code until the fix for 6984348. |
| 3569 | // Currently it produces false negative results since it does not cover all cases. |
| 3570 | #if 0 // ifdef ASSERT |
| 3571 | visited.Reset(); |
| 3572 | Node_Stack old_mems(arena, _compile->unique() >> 2); |
| 3573 | #endif |
| 3574 | for (uint i = 0; i < ideal_nodes.size(); i++) { |
| 3575 | Node* n = ideal_nodes.at(i); |
| 3576 | Node* nmem = get_map(n->_idx); |
| 3577 | assert(nmem != NULL, "sanity")do { if (!(nmem != __null)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3577, "assert(" "nmem != __null" ") failed", "sanity"); ::breakpoint (); } } while (0); |
| 3578 | if (n->is_Mem()) { |
| 3579 | #if 0 // ifdef ASSERT |
| 3580 | Node* old_mem = n->in(MemNode::Memory); |
| 3581 | if (!visited.test_set(old_mem->_idx)) { |
| 3582 | old_mems.push(old_mem, old_mem->outcnt()); |
| 3583 | } |
| 3584 | #endif |
| 3585 | assert(n->in(MemNode::Memory) != nmem, "sanity")do { if (!(n->in(MemNode::Memory) != nmem)) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3585, "assert(" "n->in(MemNode::Memory) != nmem" ") failed" , "sanity"); ::breakpoint(); } } while (0); |
| 3586 | if (!n->is_Load()) { |
| 3587 | // Move memory users of a store first. |
| 3588 | move_inst_mem(n, orig_phis); |
| 3589 | } |
| 3590 | // Now update memory input |
| 3591 | igvn->hash_delete(n); |
| 3592 | n->set_req(MemNode::Memory, nmem); |
| 3593 | igvn->hash_insert(n); |
| 3594 | record_for_optimizer(n); |
| 3595 | } else { |
| 3596 | assert(n->is_Allocate() || n->is_CheckCastPP() ||do { if (!(n->is_Allocate() || n->is_CheckCastPP() || n ->is_AddP() || n->is_Phi())) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3597, "assert(" "n->is_Allocate() || n->is_CheckCastPP() || n->is_AddP() || n->is_Phi()" ") failed", "unknown node used for set_map()"); ::breakpoint (); } } while (0) |
| 3597 | n->is_AddP() || n->is_Phi(), "unknown node used for set_map()")do { if (!(n->is_Allocate() || n->is_CheckCastPP() || n ->is_AddP() || n->is_Phi())) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3597, "assert(" "n->is_Allocate() || n->is_CheckCastPP() || n->is_AddP() || n->is_Phi()" ") failed", "unknown node used for set_map()"); ::breakpoint (); } } while (0); |
| 3598 | } |
| 3599 | } |
| 3600 | #if 0 // ifdef ASSERT |
| 3601 | // Verify that memory was split correctly |
| 3602 | while (old_mems.is_nonempty()) { |
| 3603 | Node* old_mem = old_mems.node(); |
| 3604 | uint old_cnt = old_mems.index(); |
| 3605 | old_mems.pop(); |
| 3606 | assert(old_cnt == old_mem->outcnt(), "old mem could be lost")do { if (!(old_cnt == old_mem->outcnt())) { (*g_assert_poison ) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/opto/escape.cpp" , 3606, "assert(" "old_cnt == old_mem->outcnt()" ") failed" , "old mem could be lost"); ::breakpoint(); } } while (0); |
| 3607 | } |
| 3608 | #endif |
| 3609 | } |
| 3610 | |
| 3611 | #ifndef PRODUCT |
| 3612 | static const char *node_type_names[] = { |
| 3613 | "UnknownType", |
| 3614 | "JavaObject", |
| 3615 | "LocalVar", |
| 3616 | "Field", |
| 3617 | "Arraycopy" |
| 3618 | }; |
| 3619 | |
| 3620 | static const char *esc_names[] = { |
| 3621 | "UnknownEscape", |
| 3622 | "NoEscape", |
| 3623 | "ArgEscape", |
| 3624 | "GlobalEscape" |
| 3625 | }; |
| 3626 | |
| 3627 | void PointsToNode::dump(bool print_state) const { |
| 3628 | NodeType nt = node_type(); |
| 3629 | tty->print("%s ", node_type_names[(int) nt]); |
| 3630 | if (print_state) { |
| 3631 | EscapeState es = escape_state(); |
| 3632 | EscapeState fields_es = fields_escape_state(); |
| 3633 | tty->print("%s(%s) ", esc_names[(int)es], esc_names[(int)fields_es]); |
| 3634 | if (nt == PointsToNode::JavaObject && !this->scalar_replaceable()) { |
| 3635 | tty->print("NSR "); |
| 3636 | } |
| 3637 | } |
| 3638 | if (is_Field()) { |
| 3639 | FieldNode* f = (FieldNode*)this; |
| 3640 | if (f->is_oop()) { |
| 3641 | tty->print("oop "); |
| 3642 | } |
| 3643 | if (f->offset() > 0) { |
| 3644 | tty->print("+%d ", f->offset()); |
| 3645 | } |
| 3646 | tty->print("("); |
| 3647 | for (BaseIterator i(f); i.has_next(); i.next()) { |
| 3648 | PointsToNode* b = i.get(); |
| 3649 | tty->print(" %d%s", b->idx(),(b->is_JavaObject() ? "P" : "")); |
| 3650 | } |
| 3651 | tty->print(" )"); |
| 3652 | } |
| 3653 | tty->print("["); |
| 3654 | for (EdgeIterator i(this); i.has_next(); i.next()) { |
| 3655 | PointsToNode* e = i.get(); |
| 3656 | tty->print(" %d%s%s", e->idx(),(e->is_JavaObject() ? "P" : (e->is_Field() ? "F" : "")), e->is_Arraycopy() ? "cp" : ""); |
| 3657 | } |
| 3658 | tty->print(" ["); |
| 3659 | for (UseIterator i(this); i.has_next(); i.next()) { |
| 3660 | PointsToNode* u = i.get(); |
| 3661 | bool is_base = false; |
| 3662 | if (PointsToNode::is_base_use(u)) { |
| 3663 | is_base = true; |
| 3664 | u = PointsToNode::get_use_node(u)->as_Field(); |
| 3665 | } |
| 3666 | tty->print(" %d%s%s", u->idx(), is_base ? "b" : "", u->is_Arraycopy() ? "cp" : ""); |
| 3667 | } |
| 3668 | tty->print(" ]] "); |
| 3669 | if (_node == NULL__null) { |
| 3670 | tty->print_cr("<null>"); |
| 3671 | } else { |
| 3672 | _node->dump(); |
| 3673 | } |
| 3674 | } |
| 3675 | |
| 3676 | void ConnectionGraph::dump(GrowableArray<PointsToNode*>& ptnodes_worklist) { |
| 3677 | bool first = true; |
| 3678 | int ptnodes_length = ptnodes_worklist.length(); |
| 3679 | for (int i = 0; i < ptnodes_length; i++) { |
| 3680 | PointsToNode *ptn = ptnodes_worklist.at(i); |
| 3681 | if (ptn == NULL__null || !ptn->is_JavaObject()) { |
| 3682 | continue; |
| 3683 | } |
| 3684 | PointsToNode::EscapeState es = ptn->escape_state(); |
| 3685 | if ((es != PointsToNode::NoEscape) && !Verbose) { |
| 3686 | continue; |
| 3687 | } |
| 3688 | Node* n = ptn->ideal_node(); |
| 3689 | if (n->is_Allocate() || (n->is_CallStaticJava() && |
| 3690 | n->as_CallStaticJava()->is_boxing_method())) { |
| 3691 | if (first) { |
| 3692 | tty->cr(); |
| 3693 | tty->print("======== Connection graph for "); |
| 3694 | _compile->method()->print_short_name(); |
| 3695 | tty->cr(); |
| 3696 | tty->print_cr("invocation #%d: %d iterations and %f sec to build connection graph with %d nodes and worklist size %d", |
| 3697 | _invocation, _build_iterations, _build_time, nodes_size(), ptnodes_worklist.length()); |
| 3698 | tty->cr(); |
| 3699 | first = false; |
| 3700 | } |
| 3701 | ptn->dump(); |
| 3702 | // Print all locals and fields which reference this allocation |
| 3703 | for (UseIterator j(ptn); j.has_next(); j.next()) { |
| 3704 | PointsToNode* use = j.get(); |
| 3705 | if (use->is_LocalVar()) { |
| 3706 | use->dump(Verbose); |
| 3707 | } else if (Verbose) { |
| 3708 | use->dump(); |
| 3709 | } |
| 3710 | } |
| 3711 | tty->cr(); |
| 3712 | } |
| 3713 | } |
| 3714 | } |
| 3715 | #endif |
| 3716 | |
| 3717 | void ConnectionGraph::record_for_optimizer(Node *n) { |
| 3718 | _igvn->_worklist.push(n); |
| 3719 | _igvn->add_users_to_worklist(n); |
| 3720 | } |