--- old/src/share/vm/opto/compile.cpp 2021-04-28 19:35:12.832300418 +0800 +++ new/src/share/vm/opto/compile.cpp 2021-04-28 19:35:12.713295739 +0800 @@ -2883,9 +2883,39 @@ break; } -#ifdef _LP64 case Op_CastPP: - if (n->in(1)->is_DecodeN() && Matcher::gen_narrow_oop_implicit_null_checks()) { + { + // Remove CastPP nodes to gain more freedom during scheduling but + // keep the dependency they encode as control or precedence edges + // (if control is set already) on memory operations. Some CastPP + // nodes don't have a control (don't carry a dependency): skip + // those. + if (n->in(0) != NULL) { + ResourceMark rm; + Unique_Node_List wq; + wq.push(n); + for (uint next = 0; next < wq.size(); ++next) { + Node *m = wq.at(next); + for (DUIterator_Fast imax, i = m->fast_outs(imax); i < imax; i++) { + Node* use = m->fast_out(i); + if (use->is_Mem() || use->is_EncodeNarrowPtr()) { + use->ensure_control_or_add_prec(n->in(0)); + } else if (use->in(0) == NULL) { + switch(use->Opcode()) { + case Op_AddP: + case Op_DecodeN: + case Op_DecodeNKlass: + case Op_CheckCastPP: + case Op_CastPP: + wq.push(use); + break; + } + } + } + } + } + const bool is_LP64 = LP64_ONLY(true) NOT_LP64(false); + if (is_LP64 && n->in(1)->is_DecodeN() && Matcher::gen_narrow_oop_implicit_null_checks()) { Node* in1 = n->in(1); const Type* t = n->bottom_type(); Node* new_in1 = in1->clone(); @@ -2918,9 +2948,15 @@ if (in1->outcnt() == 0) { in1->disconnect_inputs(NULL, this); } + } else { + n->subsume_by(n->in(1), this); + if (n->outcnt() == 0) { + n->disconnect_inputs(NULL, this); + } } break; - + } +#ifdef _LP64 case Op_CmpP: // Do this transformation here to preserve CmpPNode::sub() and // other TypePtr related Ideal optimizations (for example, ptr nullness). --- old/src/share/vm/opto/connode.cpp 2021-04-28 19:35:13.243316581 +0800 +++ new/src/share/vm/opto/connode.cpp 2021-04-28 19:35:13.125311940 +0800 @@ -431,16 +431,6 @@ return (in(0) && remove_dead_region(phase, can_reshape)) ? this : NULL; } -//------------------------------Ideal_DU_postCCP------------------------------- -// Throw away cast after constant propagation -Node *ConstraintCastNode::Ideal_DU_postCCP( PhaseCCP *ccp ) { - const Type *t = ccp->type(in(1)); - ccp->hash_delete(this); - set_type(t); // Turn into ID function - ccp->hash_insert(this); - return this; -} - uint CastIINode::size_of() const { return sizeof(*this); } @@ -524,13 +514,6 @@ return res; } -Node *CastIINode::Ideal_DU_postCCP(PhaseCCP *ccp) { - if (_carry_dependency || _range_check_dependency) { - return NULL; - } - return ConstraintCastNode::Ideal_DU_postCCP(ccp); -} - #ifndef PRODUCT void CastIINode::dump_spec(outputStream *st) const { TypeNode::dump_spec(st); @@ -544,20 +527,6 @@ #endif //============================================================================= - -//------------------------------Ideal_DU_postCCP------------------------------- -// If not converting int->oop, throw away cast after constant propagation -Node *CastPPNode::Ideal_DU_postCCP( PhaseCCP *ccp ) { - const Type *t = ccp->type(in(1)); - if (!t->isa_oop_ptr() || ((in(1)->is_DecodeN()) && Matcher::gen_narrow_oop_implicit_null_checks())) { - return NULL; // do not transform raw pointers or narrow oops - } - return ConstraintCastNode::Ideal_DU_postCCP(ccp); -} - - - -//============================================================================= //------------------------------Identity--------------------------------------- // If input is already higher or equal to cast type, then this is an identity. Node *CheckCastPPNode::Identity( PhaseTransform *phase ) { @@ -690,11 +659,6 @@ return t->make_narrowoop(); } - -Node *EncodeNarrowPtrNode::Ideal_DU_postCCP( PhaseCCP *ccp ) { - return MemNode::Ideal_common_DU_postCCP(ccp, this, in(1)); -} - Node* DecodeNKlassNode::Identity(PhaseTransform* phase) { const Type *t = phase->type( in(1) ); if( t == Type::TOP ) return in(1); --- old/src/share/vm/opto/connode.hpp 2021-04-28 19:35:13.630331799 +0800 +++ new/src/share/vm/opto/connode.hpp 2021-04-28 19:35:13.514327238 +0800 @@ -235,7 +235,6 @@ virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual int Opcode() const; virtual uint ideal_reg() const = 0; - virtual Node *Ideal_DU_postCCP( PhaseCCP * ); }; //------------------------------CastIINode------------------------------------- @@ -260,7 +259,6 @@ virtual uint ideal_reg() const { return Op_RegI; } virtual Node *Identity( PhaseTransform *phase ); virtual const Type *Value( PhaseTransform *phase ) const; - virtual Node *Ideal_DU_postCCP( PhaseCCP * ); const bool has_range_check() { #ifdef _LP64 return _range_check_dependency; @@ -281,7 +279,6 @@ CastPPNode (Node *n, const Type *t ): ConstraintCastNode(n, t) {} virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegP; } - virtual Node *Ideal_DU_postCCP( PhaseCCP * ); }; //------------------------------CheckCastPPNode-------------------------------- @@ -299,9 +296,6 @@ virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegP; } - // No longer remove CheckCast after CCP as it gives me a place to hang - // the proper address type - which is required to compute anti-deps. - //virtual Node *Ideal_DU_postCCP( PhaseCCP * ); }; @@ -316,7 +310,6 @@ } public: virtual uint ideal_reg() const { return Op_RegN; } - virtual Node *Ideal_DU_postCCP( PhaseCCP *ccp ); }; //------------------------------EncodeP-------------------------------- --- old/src/share/vm/opto/gcm.cpp 2021-04-28 19:35:14.011346782 +0800 +++ new/src/share/vm/opto/gcm.cpp 2021-04-28 19:35:13.893342142 +0800 @@ -117,6 +117,9 @@ } +static bool is_dominator(Block* d, Block* n) { + return d->dom_lca(n) == d; +} //------------------------------schedule_pinned_nodes-------------------------- // Set the basic block for Nodes pinned into blocks void PhaseCFG::schedule_pinned_nodes(VectorSet &visited) { @@ -137,6 +140,41 @@ Block* block = get_block_for_node(input); // Basic block of controlling input schedule_node_into_block(node, block); } + // If the node has precedence edges (added when CastPP nodes are + // removed in final_graph_reshaping), fix the control of the + // node to cover the precedence edges and remove the + // dependencies. + Node* n = NULL; + for (uint i = node->len()-1; i >= node->req(); i--) { + Node* m = node->in(i); + if (m == NULL) continue; + // Skip the precedence edge if the test that guarded a CastPP: + // - was optimized out during escape analysis + // (OptimizePtrCompare): the CastPP's control isn't an end of + // block. + // - is moved in the branch of a dominating If: the control of + // the CastPP is then a Region. + if (m->is_block_proj() || m->is_block_start()) { + node->rm_prec(i); + if (n == NULL) { + n = m; + } else { + Block* bn = get_block_for_node(n); + Block* bm = get_block_for_node(m); + assert(is_dominator(bn, bm) || is_dominator(bm, bn), "one must dominate the other"); + n = is_dominator(bn, bm) ? m : n; + } + } + } + if (n != NULL) { + assert(node->in(0), "control should have been set"); + Block* bn = get_block_for_node(n); + Block* bnode = get_block_for_node(node->in(0)); + assert(is_dominator(bn, bnode) || is_dominator(bnode, bn), "one must dominate the other"); + if (!is_dominator(bn, bnode)) { + node->set_req(0, n); + } + } // process all inputs that are non NULL for (int i = node->req() - 1; i >= 0; --i) { --- old/src/share/vm/opto/matcher.cpp 2021-04-28 19:35:14.399362040 +0800 +++ new/src/share/vm/opto/matcher.cpp 2021-04-28 19:35:14.281357400 +0800 @@ -1067,7 +1067,14 @@ // set -1 to call add_prec() instead of set_req() during Step1 mstack.push(m, Visit, n, -1); } - + // Handle precedence edges for interior nodes + for (i = n->len()-1; (uint)i >= n->req(); i--) { + Node *m = n->in(i); + if (m == NULL || C->node_arena()->contains(m)) continue; + n->rm_prec(i); + // set -1 to call add_prec() instead of set_req() during Step1 + mstack.push(m, Visit, n, -1); + } // For constant debug info, I'd rather have unmatched constants. int cnt = n->req(); JVMState* jvms = n->jvms(); @@ -1758,6 +1765,14 @@ return ex; } +void Matcher::handle_precedence_edges(Node* n, MachNode *mach) { + for (uint i = n->req(); i < n->len(); i++) { + if (n->in(i) != NULL) { + mach->add_prec(n->in(i)); + } + } +} + void Matcher::ReduceInst_Chain_Rule( State *s, int rule, Node *&mem, MachNode *mach ) { // 'op' is what I am expecting to receive int op = _leftOp[rule]; @@ -1792,6 +1807,7 @@ uint Matcher::ReduceInst_Interior( State *s, int rule, Node *&mem, MachNode *mach, uint num_opnds ) { + handle_precedence_edges(s->_leaf, mach); if( s->_leaf->is_Load() ) { Node *mem2 = s->_leaf->in(MemNode::Memory); assert( mem == (Node*)1 || mem == mem2, "multiple Memories being matched at once?" ); @@ -1874,6 +1890,9 @@ mem = s->_leaf->in(MemNode::Memory); debug_only(_mem_node = s->_leaf;) } + + handle_precedence_edges(s->_leaf, mach); + if( s->_leaf->in(0) && s->_leaf->req() > 1) { if( !mach->in(0) ) mach->set_req(0,s->_leaf->in(0)); --- old/src/share/vm/opto/matcher.hpp 2021-04-28 19:35:14.795377613 +0800 +++ new/src/share/vm/opto/matcher.hpp 2021-04-28 19:35:14.677372973 +0800 @@ -123,6 +123,7 @@ // Mach node for ConP #NULL MachNode* _mach_null; + void handle_precedence_edges(Node* n, MachNode *mach); public: int LabelRootDepth; --- old/src/share/vm/opto/memnode.cpp 2021-04-28 19:35:15.172392439 +0800 +++ new/src/share/vm/opto/memnode.cpp 2021-04-28 19:35:15.052387720 +0800 @@ -672,216 +672,6 @@ } } -//------------------------adr_phi_is_loop_invariant---------------------------- -// A helper function for Ideal_DU_postCCP to check if a Phi in a counted -// loop is loop invariant. Make a quick traversal of Phi and associated -// CastPP nodes, looking to see if they are a closed group within the loop. -bool MemNode::adr_phi_is_loop_invariant(Node* adr_phi, Node* cast) { - // The idea is that the phi-nest must boil down to only CastPP nodes - // with the same data. This implies that any path into the loop already - // includes such a CastPP, and so the original cast, whatever its input, - // must be covered by an equivalent cast, with an earlier control input. - ResourceMark rm; - - // The loop entry input of the phi should be the unique dominating - // node for every Phi/CastPP in the loop. - Unique_Node_List closure; - closure.push(adr_phi->in(LoopNode::EntryControl)); - - // Add the phi node and the cast to the worklist. - Unique_Node_List worklist; - worklist.push(adr_phi); - if( cast != NULL ){ - if( !cast->is_ConstraintCast() ) return false; - worklist.push(cast); - } - - // Begin recursive walk of phi nodes. - while( worklist.size() ){ - // Take a node off the worklist - Node *n = worklist.pop(); - if( !closure.member(n) ){ - // Add it to the closure. - closure.push(n); - // Make a sanity check to ensure we don't waste too much time here. - if( closure.size() > 20) return false; - // This node is OK if: - // - it is a cast of an identical value - // - or it is a phi node (then we add its inputs to the worklist) - // Otherwise, the node is not OK, and we presume the cast is not invariant - if( n->is_ConstraintCast() ){ - worklist.push(n->in(1)); - } else if( n->is_Phi() ) { - for( uint i = 1; i < n->req(); i++ ) { - worklist.push(n->in(i)); - } - } else { - return false; - } - } - } - - // Quit when the worklist is empty, and we've found no offending nodes. - return true; -} - -//------------------------------Ideal_DU_postCCP------------------------------- -// Find any cast-away of null-ness and keep its control. Null cast-aways are -// going away in this pass and we need to make this memory op depend on the -// gating null check. -Node *MemNode::Ideal_DU_postCCP( PhaseCCP *ccp ) { - return Ideal_common_DU_postCCP(ccp, this, in(MemNode::Address)); -} - -// I tried to leave the CastPP's in. This makes the graph more accurate in -// some sense; we get to keep around the knowledge that an oop is not-null -// after some test. Alas, the CastPP's interfere with GVN (some values are -// the regular oop, some are the CastPP of the oop, all merge at Phi's which -// cannot collapse, etc). This cost us 10% on SpecJVM, even when I removed -// some of the more trivial cases in the optimizer. Removing more useless -// Phi's started allowing Loads to illegally float above null checks. I gave -// up on this approach. CNC 10/20/2000 -// This static method may be called not from MemNode (EncodePNode calls it). -// Only the control edge of the node 'n' might be updated. -Node *MemNode::Ideal_common_DU_postCCP( PhaseCCP *ccp, Node* n, Node* adr ) { - Node *skipped_cast = NULL; - // Need a null check? Regular static accesses do not because they are - // from constant addresses. Array ops are gated by the range check (which - // always includes a NULL check). Just check field ops. - if( n->in(MemNode::Control) == NULL ) { - // Scan upwards for the highest location we can place this memory op. - while( true ) { - switch( adr->Opcode() ) { - - case Op_AddP: // No change to NULL-ness, so peek thru AddP's - adr = adr->in(AddPNode::Base); - continue; - - case Op_DecodeN: // No change to NULL-ness, so peek thru - case Op_DecodeNKlass: - adr = adr->in(1); - continue; - - case Op_EncodeP: - case Op_EncodePKlass: - // EncodeP node's control edge could be set by this method - // when EncodeP node depends on CastPP node. - // - // Use its control edge for memory op because EncodeP may go away - // later when it is folded with following or preceding DecodeN node. - if (adr->in(0) == NULL) { - // Keep looking for cast nodes. - adr = adr->in(1); - continue; - } - ccp->hash_delete(n); - n->set_req(MemNode::Control, adr->in(0)); - ccp->hash_insert(n); - return n; - - case Op_CastPP: - // If the CastPP is useless, just peek on through it. - if( ccp->type(adr) == ccp->type(adr->in(1)) ) { - // Remember the cast that we've peeked though. If we peek - // through more than one, then we end up remembering the highest - // one, that is, if in a loop, the one closest to the top. - skipped_cast = adr; - adr = adr->in(1); - continue; - } - // CastPP is going away in this pass! We need this memory op to be - // control-dependent on the test that is guarding the CastPP. - ccp->hash_delete(n); - n->set_req(MemNode::Control, adr->in(0)); - ccp->hash_insert(n); - return n; - - case Op_Phi: - // Attempt to float above a Phi to some dominating point. - if (adr->in(0) != NULL && adr->in(0)->is_CountedLoop()) { - // If we've already peeked through a Cast (which could have set the - // control), we can't float above a Phi, because the skipped Cast - // may not be loop invariant. - if (adr_phi_is_loop_invariant(adr, skipped_cast)) { - adr = adr->in(1); - continue; - } - } - - // Intentional fallthrough! - - // No obvious dominating point. The mem op is pinned below the Phi - // by the Phi itself. If the Phi goes away (no true value is merged) - // then the mem op can float, but not indefinitely. It must be pinned - // behind the controls leading to the Phi. - case Op_CheckCastPP: - // These usually stick around to change address type, however a - // useless one can be elided and we still need to pick up a control edge - if (adr->in(0) == NULL) { - // This CheckCastPP node has NO control and is likely useless. But we - // need check further up the ancestor chain for a control input to keep - // the node in place. 4959717. - skipped_cast = adr; - adr = adr->in(1); - continue; - } - ccp->hash_delete(n); - n->set_req(MemNode::Control, adr->in(0)); - ccp->hash_insert(n); - return n; - - // List of "safe" opcodes; those that implicitly block the memory - // op below any null check. - case Op_CastX2P: // no null checks on native pointers - case Op_Parm: // 'this' pointer is not null - case Op_LoadP: // Loading from within a klass - case Op_LoadN: // Loading from within a klass - case Op_LoadKlass: // Loading from within a klass - case Op_LoadNKlass: // Loading from within a klass - case Op_ConP: // Loading from a klass - case Op_ConN: // Loading from a klass - case Op_ConNKlass: // Loading from a klass - case Op_CreateEx: // Sucking up the guts of an exception oop - case Op_Con: // Reading from TLS - case Op_CMoveP: // CMoveP is pinned - case Op_CMoveN: // CMoveN is pinned - break; // No progress - - case Op_Proj: // Direct call to an allocation routine - case Op_SCMemProj: // Memory state from store conditional ops -#ifdef ASSERT - { - assert(adr->as_Proj()->_con == TypeFunc::Parms, "must be return value"); - const Node* call = adr->in(0); - if (call->is_CallJava()) { - const CallJavaNode* call_java = call->as_CallJava(); - const TypeTuple *r = call_java->tf()->range(); - assert(r->cnt() > TypeFunc::Parms, "must return value"); - const Type* ret_type = r->field_at(TypeFunc::Parms); - assert(ret_type && ret_type->isa_ptr(), "must return pointer"); - // We further presume that this is one of - // new_instance_Java, new_array_Java, or - // the like, but do not assert for this. - } else if (call->is_Allocate()) { - // similar case to new_instance_Java, etc. - } else if (!call->is_CallLeaf()) { - // Projections from fetch_oop (OSR) are allowed as well. - ShouldNotReachHere(); - } - } -#endif - break; - default: - ShouldNotReachHere(); - } - break; - } - } - - return NULL; // No progress -} - - //============================================================================= // Should LoadNode::Ideal() attempt to remove control edges? bool LoadNode::can_remove_control() const { --- old/src/share/vm/opto/memnode.hpp 2021-04-28 19:35:15.586408720 +0800 +++ new/src/share/vm/opto/memnode.hpp 2021-04-28 19:35:15.468404079 +0800 @@ -89,10 +89,6 @@ // This one should probably be a phase-specific function: static bool all_controls_dominate(Node* dom, Node* sub); - // Find any cast-away of null-ness and keep its control. - static Node *Ideal_common_DU_postCCP( PhaseCCP *ccp, Node* n, Node* adr ); - virtual Node *Ideal_DU_postCCP( PhaseCCP *ccp ); - virtual const class TypePtr *adr_type() const; // returns bottom_type of address // Shared code for Ideal methods: --- old/src/share/vm/opto/node.cpp 2021-04-28 19:35:15.976424056 +0800 +++ new/src/share/vm/opto/node.cpp 2021-04-28 19:35:15.854419259 +0800 @@ -1432,12 +1432,6 @@ return false; } -//------------------------------Ideal_DU_postCCP------------------------------- -// Idealize graph, using DU info. Must clone result into new-space -Node *Node::Ideal_DU_postCCP( PhaseCCP * ) { - return NULL; // Default to no change -} - //------------------------------hash------------------------------------------- // Hash function over Nodes. uint Node::hash() const { @@ -2126,6 +2120,14 @@ return found; } +void Node::ensure_control_or_add_prec(Node* c) { + if (in(0) == NULL) { + set_req(0, c); + } else if (in(0) != c) { + add_prec(c); + } +} + //============================================================================= //------------------------------yank------------------------------------------- // Find and remove --- old/src/share/vm/opto/node.hpp 2021-04-28 19:35:16.360439157 +0800 +++ new/src/share/vm/opto/node.hpp 2021-04-28 19:35:16.244434596 +0800 @@ -926,9 +926,6 @@ bool remove_dead_region(PhaseGVN *phase, bool can_reshape); public: - // Idealize graph, using DU info. Done after constant propagation - virtual Node *Ideal_DU_postCCP( PhaseCCP *ccp ); - // See if there is valid pipeline info static const Pipeline *pipeline_class(); virtual const Pipeline *pipeline() const; @@ -962,6 +959,8 @@ // Return the unique control out if only one. Null if none or more than one. Node* unique_ctrl_out(); + // Set control or add control as precedence edge + void ensure_control_or_add_prec(Node* c); //----------------- Code Generation // Ideal register class for Matching. Zero means unmatched instruction --- old/src/share/vm/opto/phaseX.cpp 2021-04-28 19:35:16.756454730 +0800 +++ new/src/share/vm/opto/phaseX.cpp 2021-04-28 19:35:16.635449971 +0800 @@ -1774,11 +1774,6 @@ _worklist.push(n); // n re-enters the hash table via the worklist } - // Idealize graph using DU info. Must clone() into new-space. - // DU info is generally used to show profitability, progress or safety - // (but generally not needed for correctness). - Node *nn = n->Ideal_DU_postCCP(this); - // TEMPORARY fix to ensure that 2nd GVN pass eliminates NULL checks switch( n->Opcode() ) { case Op_FastLock: // Revisit FastLocks for lock coarsening @@ -1795,13 +1790,6 @@ default: break; } - if( nn ) { - _worklist.push(n); - // Put users of 'n' onto worklist for second igvn transform - add_users_to_worklist(n); - return nn; - } - return n; } --- /dev/null 2020-09-25 10:21:33.958375795 +0800 +++ new/test/compiler/8069191/TestBypassNullCheck.java 2021-04-28 19:35:17.024465269 +0800 @@ -0,0 +1,42 @@ +/* + * @test + * @summary moving predicate out of loops may cause array accesses to bypass null check and caused crash + * @run main/othervm -Xbatch -XX:-TieredCompilation -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+StressGCM -XX:CompileCommand=compileonly,TestBypassNullCheck::getController TestBypassNullCheck + */ +public class TestBypassNullCheck { + Config initConfig; + public TestBypassNullCheck() { + initConfig = new Config(1234, "test"); + } + public TestBypassNullCheck getController() { + String customGroupName = initConfig.userName == null ? "" : initConfig.userName; + customGroupName = customGroupName + ":" + initConfig.dataId; + State.setCustomName(customGroupName); + return this; + } + + public static void main(String [] args) { + System.out.println("test"+":"+args.length+State.getCustomName()); + TestBypassNullCheck t = new TestBypassNullCheck(); + for (int i=0;i<10000;i++) { + t.getController(); + } + t.initConfig = new Config(1234, null); + t.getController(); + } +} + +class Config { + String dataId; + String userName; + public Config(long id, String name) { dataId = ""+id; userName = name; } +} + +class State { + static String customerName="test"; + public static void setCustomName(String name) { + customerName = name; + } + + public static String getCustomName() { return customerName; } +} --- /dev/null 2020-09-25 10:21:33.958375795 +0800 +++ new/test/compiler/8069191/TestPredicateLostDependency.java 2021-04-28 19:35:17.375479072 +0800 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8069191 + * @summary predicate moved out of loops and CastPP removal causes dependency to be lost + * + * @run main/othervm -Xcomp -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+StressGCM + * -XX:CompileCommand=compileonly,compiler.loopopts.TestPredicateLostDependency::m1 + * compiler.loopopts.TestPredicateLostDependency + * + */ + +package compiler.loopopts; + +public class TestPredicateLostDependency { + static class A { + int i; + } + + static class B extends A { + } + + static boolean crash = false; + + static boolean m2() { + return crash; + } + + static int m3(float[] arr) { + return 0; + } + + static float m1(A aa) { + float res = 0; + float[] arr = new float[10]; + for (int i = 0; i < 10; i++) { + if (m2()) { + arr = null; + } + m3(arr); + int j = arr.length; + int k = 0; + for (k = 9; k < j; k++) { + } + if (k == 10) { + if (aa instanceof B) { + } + } + res += arr[0]; + res += arr[1]; + } + return res; + } + + static public void main(String args[]) { + A a = new A(); + B b = new B(); + for (int i = 0; i < 20000; i++) { + m1(a); + } + crash = true; + try { + m1(a); + } catch (NullPointerException npe) {} + } +}