8139771: Eliminating CastPP nodes at Phis when they all come from a unique input may cause crash
Summary: Lost dependency when CastPP at Phis are eliminate
Reviewed-by: kvn
--- a/hotspot/src/share/vm/opto/block.hpp Fri Dec 18 20:23:28 2015 +0300
+++ b/hotspot/src/share/vm/opto/block.hpp Fri Dec 11 16:57:08 2015 +0100
@@ -499,6 +499,7 @@
void convert_NeverBranch_to_Goto(Block *b);
CFGLoop* create_loop_tree();
+ bool is_dominator(Node* dom_node, Node* node);
#ifndef PRODUCT
bool _trace_opto_pipelining; // tracing flag
--- a/hotspot/src/share/vm/opto/castnode.cpp Fri Dec 18 20:23:28 2015 +0300
+++ b/hotspot/src/share/vm/opto/castnode.cpp Fri Dec 11 16:57:08 2015 +0100
@@ -24,6 +24,7 @@
#include "precompiled.hpp"
#include "opto/addnode.hpp"
+#include "opto/callnode.hpp"
#include "opto/castnode.hpp"
#include "opto/connode.hpp"
#include "opto/matcher.hpp"
@@ -33,14 +34,22 @@
//=============================================================================
// If input is already higher or equal to cast type, then this is an identity.
-Node *ConstraintCastNode::Identity( PhaseTransform *phase ) {
+Node *ConstraintCastNode::Identity(PhaseTransform *phase) {
+ Node* dom = dominating_cast(phase);
+ if (dom != NULL) {
+ assert(_carry_dependency, "only for casts that carry a dependency");
+ return dom;
+ }
+ if (_carry_dependency) {
+ return this;
+ }
return phase->type(in(1))->higher_equal_speculative(_type) ? in(1) : this;
}
//------------------------------Value------------------------------------------
// Take 'join' of input and cast-up type
-const Type *ConstraintCastNode::Value( PhaseTransform *phase ) const {
- if( in(0) && phase->type(in(0)) == Type::TOP ) return Type::TOP;
+const Type *ConstraintCastNode::Value(PhaseTransform *phase) const {
+ if (in(0) && phase->type(in(0)) == Type::TOP) return Type::TOP;
const Type* ft = phase->type(in(1))->filter_speculative(_type);
#ifdef ASSERT
@@ -69,24 +78,76 @@
//------------------------------Ideal------------------------------------------
// Return a node which is more "ideal" than the current node. Strip out
// control copies
-Node *ConstraintCastNode::Ideal(PhaseGVN *phase, bool can_reshape){
+Node *ConstraintCastNode::Ideal(PhaseGVN *phase, bool can_reshape) {
return (in(0) && remove_dead_region(phase, can_reshape)) ? this : NULL;
}
-uint CastIINode::size_of() const {
+uint ConstraintCastNode::cmp(const Node &n) const {
+ return TypeNode::cmp(n) && ((ConstraintCastNode&)n)._carry_dependency == _carry_dependency;
+}
+
+uint ConstraintCastNode::size_of() const {
return sizeof(*this);
}
-uint CastIINode::cmp(const Node &n) const {
- return TypeNode::cmp(n) && ((CastIINode&)n)._carry_dependency == _carry_dependency;
+Node* ConstraintCastNode::make_cast(int opcode, Node* c, Node *n, const Type *t, bool carry_dependency) {
+ switch(opcode) {
+ case Op_CastII: {
+ Node* cast = new CastIINode(n, t, carry_dependency);
+ cast->set_req(0, c);
+ return cast;
+ }
+ case Op_CastPP: {
+ Node* cast = new CastPPNode(n, t, carry_dependency);
+ cast->set_req(0, c);
+ return cast;
+ }
+ case Op_CheckCastPP: return new CheckCastPPNode(c, n, t, carry_dependency);
+ default:
+ fatal("Bad opcode %d", opcode);
+ }
+ return NULL;
}
-Node *CastIINode::Identity(PhaseTransform *phase) {
+TypeNode* ConstraintCastNode::dominating_cast(PhaseTransform *phase) const {
+ if (!carry_dependency()) {
+ return NULL;
+ }
+ Node* val = in(1);
+ Node* ctl = in(0);
+ int opc = Opcode();
+ if (ctl == NULL) {
+ return NULL;
+ }
+ for (DUIterator_Fast imax, i = val->fast_outs(imax); i < imax; i++) {
+ Node* u = val->fast_out(i);
+ if (u != this &&
+ u->Opcode() == opc &&
+ u->in(0) != NULL &&
+ u->bottom_type()->higher_equal(type())) {
+ if (phase->is_dominator(u->in(0), ctl)) {
+ return u->as_Type();
+ }
+ if (is_CheckCastPP() && u->in(1)->is_Proj() && u->in(1)->in(0)->is_Allocate() &&
+ u->in(0)->is_Proj() && u->in(0)->in(0)->is_Initialize() &&
+ u->in(1)->in(0)->as_Allocate()->initialization() == u->in(0)->in(0)) {
+ // CheckCastPP following an allocation always dominates all
+ // use of the allocation result
+ return u->as_Type();
+ }
+ }
+ }
+ return NULL;
+}
+
+#ifndef PRODUCT
+void ConstraintCastNode::dump_spec(outputStream *st) const {
+ TypeNode::dump_spec(st);
if (_carry_dependency) {
- return this;
+ st->print(" carry dependency");
}
- return ConstraintCastNode::Identity(phase);
}
+#endif
const Type *CastIINode::Value(PhaseTransform *phase) const {
const Type *res = ConstraintCastNode::Value(phase);
@@ -154,19 +215,18 @@
return res;
}
-#ifndef PRODUCT
-void CastIINode::dump_spec(outputStream *st) const {
- TypeNode::dump_spec(st);
- if (_carry_dependency) {
- st->print(" carry dependency");
- }
-}
-#endif
-
//=============================================================================
//------------------------------Identity---------------------------------------
// If input is already higher or equal to cast type, then this is an identity.
Node *CheckCastPPNode::Identity( PhaseTransform *phase ) {
+ Node* dom = dominating_cast(phase);
+ if (dom != NULL) {
+ assert(_carry_dependency, "only for casts that carry a dependency");
+ return dom;
+ }
+ if (_carry_dependency) {
+ return this;
+ }
// Toned down to rescue meeting at a Phi 3 different oops all implementing
// the same interface. CompileTheWorld starting at 502, kd12rc1.zip.
return (phase->type(in(1)) == phase->type(this)) ? in(1) : this;
@@ -255,13 +315,6 @@
// return join;
}
-//------------------------------Ideal------------------------------------------
-// Return a node which is more "ideal" than the current node. Strip out
-// control copies
-Node *CheckCastPPNode::Ideal(PhaseGVN *phase, bool can_reshape){
- return (in(0) && remove_dead_region(phase, can_reshape)) ? this : NULL;
-}
-
//=============================================================================
//------------------------------Value------------------------------------------
const Type *CastX2PNode::Value( PhaseTransform *phase ) const {
--- a/hotspot/src/share/vm/opto/castnode.hpp Fri Dec 18 20:23:28 2015 +0300
+++ b/hotspot/src/share/vm/opto/castnode.hpp Fri Dec 11 16:57:08 2015 +0100
@@ -32,8 +32,15 @@
//------------------------------ConstraintCastNode-----------------------------
// cast to a different range
class ConstraintCastNode: public TypeNode {
+ protected:
+ // Can this node be removed post CCP or does it carry a required dependency?
+ const bool _carry_dependency;
+ virtual uint cmp( const Node &n ) const;
+ virtual uint size_of() const;
+
public:
- ConstraintCastNode (Node *n, const Type *t ): TypeNode(t,2) {
+ ConstraintCastNode(Node *n, const Type *t, bool carry_dependency)
+ : TypeNode(t,2), _carry_dependency(carry_dependency) {
init_class_id(Class_ConstraintCast);
init_req(1, n);
}
@@ -42,53 +49,50 @@
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
virtual int Opcode() const;
virtual uint ideal_reg() const = 0;
+ virtual bool depends_only_on_test() const { return !_carry_dependency; }
+ bool carry_dependency() const { return _carry_dependency; }
+ TypeNode* dominating_cast(PhaseTransform *phase) const;
+ static Node* make_cast(int opcode, Node* c, Node *n, const Type *t, bool carry_dependency);
+
+#ifndef PRODUCT
+ virtual void dump_spec(outputStream *st) const;
+#endif
};
//------------------------------CastIINode-------------------------------------
// cast integer to integer (different range)
class CastIINode: public ConstraintCastNode {
- private:
- // Can this node be removed post CCP or does it carry a required dependency?
- const bool _carry_dependency;
-
- protected:
- virtual uint cmp( const Node &n ) const;
- virtual uint size_of() const;
-
public:
CastIINode(Node *n, const Type *t, bool carry_dependency = false)
- : ConstraintCastNode(n,t), _carry_dependency(carry_dependency) {}
+ : ConstraintCastNode(n, t, carry_dependency) {}
virtual int Opcode() const;
virtual uint ideal_reg() const { return Op_RegI; }
- virtual Node *Identity( PhaseTransform *phase );
virtual const Type *Value( PhaseTransform *phase ) const;
-#ifndef PRODUCT
- virtual void dump_spec(outputStream *st) const;
-#endif
};
//------------------------------CastPPNode-------------------------------------
// cast pointer to pointer (different type)
class CastPPNode: public ConstraintCastNode {
public:
- CastPPNode (Node *n, const Type *t ): ConstraintCastNode(n, t) {}
+ CastPPNode (Node *n, const Type *t, bool carry_dependency = false)
+ : ConstraintCastNode(n, t, carry_dependency) {
+ }
virtual int Opcode() const;
virtual uint ideal_reg() const { return Op_RegP; }
};
//------------------------------CheckCastPPNode--------------------------------
// for _checkcast, cast pointer to pointer (different type), without JOIN,
-class CheckCastPPNode: public TypeNode {
+class CheckCastPPNode: public ConstraintCastNode {
public:
- CheckCastPPNode( Node *c, Node *n, const Type *t ) : TypeNode(t,2) {
+ CheckCastPPNode(Node *c, Node *n, const Type *t, bool carry_dependency = false)
+ : ConstraintCastNode(n, t, carry_dependency) {
init_class_id(Class_CheckCastPP);
init_req(0, c);
- init_req(1, n);
}
- virtual Node *Identity( PhaseTransform *phase );
- virtual const Type *Value( PhaseTransform *phase ) const;
- virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
+ virtual Node *Identity(PhaseTransform *phase);
+ virtual const Type *Value(PhaseTransform *phase) const;
virtual int Opcode() const;
virtual uint ideal_reg() const { return Op_RegP; }
};
--- a/hotspot/src/share/vm/opto/cfgnode.cpp Fri Dec 18 20:23:28 2015 +0300
+++ b/hotspot/src/share/vm/opto/cfgnode.cpp Fri Dec 11 16:57:08 2015 +0100
@@ -27,6 +27,7 @@
#include "memory/allocation.inline.hpp"
#include "oops/objArrayKlass.hpp"
#include "opto/addnode.hpp"
+#include "opto/castnode.hpp"
#include "opto/cfgnode.hpp"
#include "opto/connode.hpp"
#include "opto/convertnode.hpp"
@@ -1148,7 +1149,7 @@
// It would check for a tributary phi on the backedge that the main phi
// trivially, perhaps with a single cast. The unique_input method
// does all this and more, by reducing such tributaries to 'this'.)
- Node* uin = unique_input(phase);
+ Node* uin = unique_input(phase, false);
if (uin != NULL) {
return uin;
}
@@ -1165,8 +1166,9 @@
//-----------------------------unique_input------------------------------------
// Find the unique value, discounting top, self-loops, and casts.
// Return top if there are no inputs, and self if there are multiple.
-Node* PhiNode::unique_input(PhaseTransform* phase) {
- // 1) One unique direct input, or
+Node* PhiNode::unique_input(PhaseTransform* phase, bool uncast) {
+ // 1) One unique direct input,
+ // or if uncast is true:
// 2) some of the inputs have an intervening ConstraintCast and
// the type of input is the same or sharper (more specific)
// than the phi's type.
@@ -1180,8 +1182,7 @@
Node* r = in(0); // RegionNode
if (r == NULL) return in(1); // Already degraded to a Copy
- Node* uncasted_input = NULL; // The unique uncasted input (ConstraintCasts removed)
- Node* direct_input = NULL; // The unique direct input
+ Node* input = NULL; // The unique direct input (maybe uncasted = ConstraintCasts removed)
for (uint i = 1, cnt = req(); i < cnt; ++i) {
Node* rc = r->in(i);
@@ -1190,34 +1191,23 @@
Node* n = in(i);
if (n == NULL)
continue;
- Node* un = n->uncast();
+ Node* un = uncast ? n->uncast() : n;
if (un == NULL || un == this || phase->type(un) == Type::TOP) {
continue; // ignore if top, or in(i) and "this" are in a data cycle
}
- // Check for a unique uncasted input
- if (uncasted_input == NULL) {
- uncasted_input = un;
- } else if (uncasted_input != un) {
- uncasted_input = NodeSentinel; // no unique uncasted input
- }
- // Check for a unique direct input
- if (direct_input == NULL) {
- direct_input = n;
- } else if (direct_input != n) {
- direct_input = NodeSentinel; // no unique direct input
+ // Check for a unique input (maybe uncasted)
+ if (input == NULL) {
+ input = un;
+ } else if (input != un) {
+ input = NodeSentinel; // no unique input
}
}
- if (direct_input == NULL) {
+ if (input == NULL) {
return phase->C->top(); // no inputs
}
- assert(uncasted_input != NULL,"");
- if (direct_input != NodeSentinel) {
- return direct_input; // one unique direct input
- }
- if (uncasted_input != NodeSentinel &&
- phase->type(uncasted_input)->higher_equal(type())) {
- return uncasted_input; // one unique uncasted input
+ if (input != NodeSentinel) {
+ return input; // one unique direct input
}
// Nothing.
@@ -1650,7 +1640,12 @@
return top;
}
- Node* uin = unique_input(phase);
+ bool uncasted = false;
+ Node* uin = unique_input(phase, false);
+ if (uin == NULL) {
+ uncasted = true;
+ uin = unique_input(phase, true);
+ }
if (uin == top) { // Simplest case: no alive inputs.
if (can_reshape) // IGVN transformation
return top;
@@ -1683,6 +1678,31 @@
}
}
+ if (uncasted) {
+ const Type* phi_type = bottom_type();
+ assert(phi_type->isa_int() || phi_type->isa_ptr(), "bad phi type");
+ int opcode;
+ if (phi_type->isa_int()) {
+ opcode = Op_CastII;
+ } else {
+ const Type* uin_type = phase->type(uin);
+ if (phi_type->join(TypePtr::NOTNULL) == uin_type->join(TypePtr::NOTNULL)) {
+ opcode = Op_CastPP;
+ } else {
+ opcode = Op_CheckCastPP;
+ }
+ }
+ // Add a cast to carry the control dependency of the Phi that is
+ // going away
+ Node* cast = ConstraintCastNode::make_cast(opcode, r, uin, phi_type, true);
+ cast = phase->transform(cast);
+ // set all inputs to the new cast so the Phi is removed by Identity
+ for (uint i = 1; i < req(); i++) {
+ set_req(i, cast);
+ }
+ uin = cast;
+ }
+
// One unique input.
debug_only(Node* ident = Identity(phase));
// The unique input must eventually be detected by the Identity call.
@@ -1699,7 +1719,6 @@
return NULL;
}
-
Node* opt = NULL;
int true_path = is_diamond_phi();
if( true_path != 0 ) {
--- a/hotspot/src/share/vm/opto/cfgnode.hpp Fri Dec 18 20:23:28 2015 +0300
+++ b/hotspot/src/share/vm/opto/cfgnode.hpp Fri Dec 11 16:57:08 2015 +0100
@@ -175,7 +175,14 @@
// Determine a unique non-trivial input, if any.
// Ignore casts if it helps. Return NULL on failure.
- Node* unique_input(PhaseTransform *phase);
+ Node* unique_input(PhaseTransform *phase, bool uncast);
+ Node* unique_input(PhaseTransform *phase) {
+ Node* uin = unique_input(phase, false);
+ if (uin == NULL) {
+ uin = unique_input(phase, true);
+ }
+ return uin;
+ }
// Check for a simple dead loop.
enum LoopSafety { Safe = 0, Unsafe, UnsafeLoop };
--- a/hotspot/src/share/vm/opto/compile.cpp Fri Dec 18 20:23:28 2015 +0300
+++ b/hotspot/src/share/vm/opto/compile.cpp Fri Dec 11 16:57:08 2015 +0100
@@ -2296,17 +2296,17 @@
DEBUG_ONLY( _modified_nodes = NULL; )
} // (End scope of igvn; run destructor if necessary for asserts.)
- process_print_inlining();
- // A method with only infinite loops has no edges entering loops from root
- {
- TracePhase tp("graphReshape", &timers[_t_graphReshaping]);
- if (final_graph_reshaping()) {
- assert(failing(), "must bail out w/ explicit message");
- return;
- }
- }
-
- print_method(PHASE_OPTIMIZE_FINISHED, 2);
+ process_print_inlining();
+ // A method with only infinite loops has no edges entering loops from root
+ {
+ TracePhase tp("graphReshape", &timers[_t_graphReshaping]);
+ if (final_graph_reshaping()) {
+ assert(failing(), "must bail out w/ explicit message");
+ return;
+ }
+ }
+
+ print_method(PHASE_OPTIMIZE_FINISHED, 2);
}
@@ -2874,7 +2874,7 @@
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) {
+ } else {
switch(use->Opcode()) {
case Op_AddP:
case Op_DecodeN:
--- a/hotspot/src/share/vm/opto/gcm.cpp Fri Dec 18 20:23:28 2015 +0300
+++ b/hotspot/src/share/vm/opto/gcm.cpp Fri Dec 11 16:57:08 2015 +0100
@@ -101,7 +101,32 @@
}
}
-static bool is_dominator(Block* d, Block* n) {
+bool PhaseCFG::is_dominator(Node* dom_node, Node* node) {
+ if (dom_node == node) {
+ return true;
+ }
+ Block* d = get_block_for_node(dom_node);
+ Block* n = get_block_for_node(node);
+ if (d == n) {
+ if (dom_node->is_block_start()) {
+ return true;
+ }
+ if (node->is_block_start()) {
+ return false;
+ }
+ if (dom_node->is_block_proj()) {
+ return false;
+ }
+ if (node->is_block_proj()) {
+ return true;
+ }
+#ifdef ASSERT
+ node->dump();
+ dom_node->dump();
+#endif
+ fatal("unhandled");
+ return false;
+ }
return d->dom_lca(n) == d;
}
@@ -145,19 +170,15 @@
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;
+ assert(is_dominator(n, m) || is_dominator(m, n), "one must dominate the other");
+ n = is_dominator(n, m) ? 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)) {
+ assert(is_dominator(n, node->in(0)) || is_dominator(node->in(0), n), "one must dominate the other");
+ if (!is_dominator(n, node->in(0))) {
node->set_req(0, n);
}
}
--- a/hotspot/src/share/vm/opto/loopopts.cpp Fri Dec 18 20:23:28 2015 +0300
+++ b/hotspot/src/share/vm/opto/loopopts.cpp Fri Dec 11 16:57:08 2015 +0100
@@ -26,6 +26,7 @@
#include "memory/allocation.inline.hpp"
#include "opto/addnode.hpp"
#include "opto/connode.hpp"
+#include "opto/castnode.hpp"
#include "opto/divnode.hpp"
#include "opto/loopnode.hpp"
#include "opto/matcher.hpp"
@@ -900,6 +901,14 @@
Node *m = remix_address_expressions( n );
if( m ) return m;
+ if (n->is_ConstraintCast()) {
+ Node* dom_cast = n->as_ConstraintCast()->dominating_cast(this);
+ if (dom_cast != NULL) {
+ _igvn.replace_node(n, dom_cast);
+ return dom_cast;
+ }
+ }
+
// Determine if the Node has inputs from some local Phi.
// Returns the block to clone thru.
Node *n_blk = has_local_phi_input( n );
--- a/hotspot/src/share/vm/opto/node.cpp Fri Dec 18 20:23:28 2015 +0300
+++ b/hotspot/src/share/vm/opto/node.cpp Fri Dec 11 16:57:08 2015 +0100
@@ -911,7 +911,7 @@
Node* Node::uncast() const {
// Should be inline:
//return is_ConstraintCast() ? uncast_helper(this) : (Node*) this;
- if (is_ConstraintCast() || is_CheckCastPP())
+ if (is_ConstraintCast())
return uncast_helper(this);
else
return (Node*) this;
@@ -965,8 +965,6 @@
break;
} else if (p->is_ConstraintCast()) {
p = p->in(1);
- } else if (p->is_CheckCastPP()) {
- p = p->in(1);
} else {
break;
}
--- a/hotspot/src/share/vm/opto/phaseX.cpp Fri Dec 18 20:23:28 2015 +0300
+++ b/hotspot/src/share/vm/opto/phaseX.cpp Fri Dec 11 16:57:08 2015 +0100
@@ -835,6 +835,22 @@
return k;
}
+bool PhaseGVN::is_dominator_helper(Node *d, Node *n, bool linear_only) {
+ if (d->is_top() || n->is_top()) {
+ return false;
+ }
+ assert(d->is_CFG() && n->is_CFG(), "must have CFG nodes");
+ int i = 0;
+ while (d != n) {
+ n = IfNode::up_one_dom(n, linear_only);
+ i++;
+ if (n == NULL || i >= 10) {
+ return false;
+ }
+ }
+ return true;
+}
+
#ifdef ASSERT
//------------------------------dead_loop_check--------------------------------
// Check for a simple dead loop when a data node references itself directly
@@ -1525,7 +1541,7 @@
}
// If changed Cast input, check Phi users for simple cycles
- if( use->is_ConstraintCast() || use->is_CheckCastPP() ) {
+ if (use->is_ConstraintCast()) {
for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) {
Node* u = use->fast_out(i2);
if (u->is_Phi())
--- a/hotspot/src/share/vm/opto/phaseX.hpp Fri Dec 18 20:23:28 2015 +0300
+++ b/hotspot/src/share/vm/opto/phaseX.hpp Fri Dec 11 16:57:08 2015 +0100
@@ -330,6 +330,9 @@
// Delayed node rehash if this is an IGVN phase
virtual void igvn_rehash_node_delayed(Node* n) {}
+ // true if CFG node d dominates CFG node n
+ virtual bool is_dominator(Node *d, Node *n) { fatal("unimplemented for this pass"); return false; };
+
#ifndef PRODUCT
void dump_old2new_map() const;
void dump_new( uint new_lidx ) const;
@@ -397,6 +400,9 @@
//------------------------------PhaseGVN---------------------------------------
// Phase for performing local, pessimistic GVN-style optimizations.
class PhaseGVN : public PhaseValues {
+protected:
+ bool is_dominator_helper(Node *d, Node *n, bool linear_only);
+
public:
PhaseGVN( Arena *arena, uint est_max_size ) : PhaseValues( arena, est_max_size ) {}
PhaseGVN( PhaseGVN *gvn ) : PhaseValues( gvn ) {}
@@ -415,6 +421,8 @@
_types = gvn->_types;
}
+ bool is_dominator(Node *d, Node *n) { return is_dominator_helper(d, n, true); }
+
// Check for a simple dead loop when a data node references itself.
DEBUG_ONLY(void dead_loop_check(Node *n);)
};
@@ -545,6 +553,8 @@
_table.check_no_speculative_types();
}
+ bool is_dominator(Node *d, Node *n) { return is_dominator_helper(d, n, false); }
+
#ifndef PRODUCT
protected:
// Sub-quadratic implementation of VerifyIterativeGVN.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/controldependency/TestEliminatedCastPPAtPhi.java Fri Dec 11 16:57:08 2015 +0100
@@ -0,0 +1,105 @@
+/*
+ * 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 8139771
+ * @summary Eliminating CastPP nodes at Phis when they all come from a unique input may cause crash
+ * @requires vm.gc=="Serial" | vm.gc=="Parallel"
+ * @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:+IgnoreUnrecognizedVMOptions -XX:+StressGCM TestEliminatedCastPPAtPhi
+ *
+ */
+
+public class TestEliminatedCastPPAtPhi {
+
+ static TestEliminatedCastPPAtPhi saved;
+ static TestEliminatedCastPPAtPhi saved_not_null;
+
+ int f;
+
+ static int test(TestEliminatedCastPPAtPhi obj, int[] array, boolean flag) {
+ int ret = array[0] + array[20];
+ saved = obj;
+ if (obj == null) {
+ return ret;
+ }
+ saved_not_null = obj;
+
+ // empty loop to be optimized out. Delays range check smearing
+ // for the array access below until the if diamond is
+ // optimized out
+ int i = 0;
+ for (; i < 10; i++);
+
+ ret += array[i];
+
+ TestEliminatedCastPPAtPhi res;
+ if (flag) {
+ // load is optimized out and res is obj here
+ res = saved;
+ } else {
+ // load is optimized out and res is non null CastPP of res here
+ res = saved_not_null;
+ }
+ // null check + CastPP here for res field load below
+
+ // 1) null check is pushed in the branches of the if above by
+ // split through phi because res is non null in the second
+ // branch and the null check can be optimized out in that
+ // branch. The Castpp stays here.
+
+ // 2) null check in the first branch is also optimized out
+ // because a dominating null check is found (the explicit null
+ // check at the beggining of the test)
+
+ // 3) the Phi for the if above merges a CastPP'ed value and
+ // the same value so it's optimized out and replaced by the
+ // uncasted value: obj
+
+ // 4) the if above has 2 empty branches so it's optimized
+ // out. The control of the CastPP that is still here is now
+ // the success branch of the range check for the array access
+ // above
+
+ // 5) the loop above is optimized out, i = 10, the range check
+ // for the array access above is optimized out and all its
+ // uses are replaced by the range check for the array accesses
+ // at the beginning of the method. The castPP here is one of
+ // the uses and so its control is now the range check at the
+ // beginning of the method: the control of the CastPP bypasses
+ // the explicit null check
+
+ return ret + res.f;
+ }
+
+ static public void main(String[] args) {
+ int[] array = new int[100];
+ TestEliminatedCastPPAtPhi obj = new TestEliminatedCastPPAtPhi();
+ for (int i = 0; i < 20000; i++) {
+ test(obj, array, (i%2) == 0);
+ }
+ test(null, array, true);
+ }
+
+}