8319793: C2 compilation fails with "Bad graph detected in build_loop_late" after JDK-8279888
Reviewed-by: chagedorn, epeter
This commit is contained in:
parent
35e9662767
commit
b922f8d459
src/hotspot/share
gc/shenandoah/c2
opto
test/hotspot/jtreg/compiler/rangechecks
@ -1104,7 +1104,7 @@ Node* ShenandoahBarrierSetC2::ideal_node(PhaseGVN* phase, Node* n, bool can_resh
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return n->as_If()->dominated_by(prev_dom, phase->is_IterGVN());
|
||||
return n->as_If()->dominated_by(prev_dom, phase->is_IterGVN(), false);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
@ -281,6 +281,15 @@ void CastIINode::dump_spec(outputStream* st) const {
|
||||
}
|
||||
#endif
|
||||
|
||||
CastIINode* CastIINode::pin_array_access_node() const {
|
||||
assert(_dependency == RegularDependency, "already pinned");
|
||||
if (has_range_check()) {
|
||||
return new CastIINode(in(0), in(1), bottom_type(), StrongDependency, has_range_check());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
const Type* CastLLNode::Value(PhaseGVN* phase) const {
|
||||
const Type* res = ConstraintCastNode::Value(phase);
|
||||
if (res == Type::TOP) {
|
||||
|
@ -114,7 +114,7 @@ class CastIINode: public ConstraintCastNode {
|
||||
virtual Node* Identity(PhaseGVN* phase);
|
||||
virtual const Type* Value(PhaseGVN* phase) const;
|
||||
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
|
||||
bool has_range_check() {
|
||||
bool has_range_check() const {
|
||||
#ifdef _LP64
|
||||
return _range_check_dependency;
|
||||
#else
|
||||
@ -123,6 +123,8 @@ class CastIINode: public ConstraintCastNode {
|
||||
#endif
|
||||
}
|
||||
|
||||
CastIINode* pin_array_access_node() const;
|
||||
|
||||
#ifndef PRODUCT
|
||||
virtual void dump_spec(outputStream* st) const;
|
||||
#endif
|
||||
|
@ -430,8 +430,8 @@ public:
|
||||
virtual const RegMask &out_RegMask() const;
|
||||
Node* fold_compares(PhaseIterGVN* phase);
|
||||
static Node* up_one_dom(Node* curr, bool linear_only = false);
|
||||
Node* dominated_by(Node* prev_dom, PhaseIterGVN* igvn);
|
||||
bool is_zero_trip_guard() const;
|
||||
Node* dominated_by(Node* prev_dom, PhaseIterGVN* igvn, bool pin_array_access_nodes);
|
||||
|
||||
// Takes the type of val and filters it through the test represented
|
||||
// by if_proj and returns a more refined type if one is produced.
|
||||
@ -505,6 +505,8 @@ public:
|
||||
IfProjNode(IfNode *ifnode, uint idx) : CProjNode(ifnode,idx) {}
|
||||
virtual Node* Identity(PhaseGVN* phase);
|
||||
|
||||
void pin_array_access_nodes(PhaseIterGVN* igvn);
|
||||
|
||||
protected:
|
||||
// Type of If input when this branch is always taken
|
||||
virtual bool always_taken(const TypeTuple* t) const = 0;
|
||||
|
@ -539,7 +539,7 @@ int RangeCheckNode::is_range_check(Node* &range, Node* &index, jint &offset) {
|
||||
|
||||
//------------------------------adjust_check-----------------------------------
|
||||
// Adjust (widen) a prior range check
|
||||
static void adjust_check(Node* proj, Node* range, Node* index,
|
||||
static void adjust_check(IfProjNode* proj, Node* range, Node* index,
|
||||
int flip, jint off_lo, PhaseIterGVN* igvn) {
|
||||
PhaseGVN *gvn = igvn;
|
||||
// Break apart the old check
|
||||
@ -547,25 +547,31 @@ static void adjust_check(Node* proj, Node* range, Node* index,
|
||||
Node *bol = iff->in(1);
|
||||
if( bol->is_top() ) return; // In case a partially dead range check appears
|
||||
// bail (or bomb[ASSERT/DEBUG]) if NOT projection-->IfNode-->BoolNode
|
||||
DEBUG_ONLY( if( !bol->is_Bool() ) { proj->dump(3); fatal("Expect projection-->IfNode-->BoolNode"); } )
|
||||
if( !bol->is_Bool() ) return;
|
||||
DEBUG_ONLY( if (!bol->is_Bool()) { proj->dump(3); fatal("Expect projection-->IfNode-->BoolNode"); } )
|
||||
if (!bol->is_Bool()) return;
|
||||
|
||||
Node *cmp = bol->in(1);
|
||||
// Compute a new check
|
||||
Node *new_add = gvn->intcon(off_lo);
|
||||
if( index ) {
|
||||
new_add = off_lo ? gvn->transform(new AddINode( index, new_add )) : index;
|
||||
if (index) {
|
||||
new_add = off_lo ? gvn->transform(new AddINode(index, new_add)) : index;
|
||||
}
|
||||
Node *new_cmp = (flip == 1)
|
||||
? new CmpUNode( new_add, range )
|
||||
: new CmpUNode( range, new_add );
|
||||
? new CmpUNode(new_add, range)
|
||||
: new CmpUNode(range, new_add);
|
||||
new_cmp = gvn->transform(new_cmp);
|
||||
// See if no need to adjust the existing check
|
||||
if( new_cmp == cmp ) return;
|
||||
if (new_cmp == cmp) return;
|
||||
// Else, adjust existing check
|
||||
Node *new_bol = gvn->transform( new BoolNode( new_cmp, bol->as_Bool()->_test._test ) );
|
||||
igvn->rehash_node_delayed( iff );
|
||||
iff->set_req_X( 1, new_bol, igvn );
|
||||
Node* new_bol = gvn->transform(new BoolNode(new_cmp, bol->as_Bool()->_test._test));
|
||||
igvn->rehash_node_delayed(iff);
|
||||
iff->set_req_X(1, new_bol, igvn);
|
||||
// As part of range check smearing, this range check is widened. Loads and range check Cast nodes that are control
|
||||
// dependent on this range check now depend on multiple dominating range checks. These control dependent nodes end up
|
||||
// at the lowest/nearest dominating check in the graph. To ensure that these Loads/Casts do not float above any of the
|
||||
// dominating checks (even when the lowest dominating check is later replaced by yet another dominating check), we
|
||||
// need to pin them at the lowest dominating check.
|
||||
proj->pin_array_access_nodes(igvn);
|
||||
}
|
||||
|
||||
//------------------------------up_one_dom-------------------------------------
|
||||
@ -1418,7 +1424,7 @@ static Node *remove_useless_bool(IfNode *iff, PhaseGVN *phase) {
|
||||
static IfNode* idealize_test(PhaseGVN* phase, IfNode* iff);
|
||||
|
||||
struct RangeCheck {
|
||||
Node* ctl;
|
||||
IfProjNode* ctl;
|
||||
jint off;
|
||||
};
|
||||
|
||||
@ -1486,14 +1492,14 @@ Node* IfNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
|
||||
if (prev_dom != nullptr) {
|
||||
// Replace dominated IfNode
|
||||
return dominated_by(prev_dom, igvn);
|
||||
return dominated_by(prev_dom, igvn, false);
|
||||
}
|
||||
|
||||
return simple_subsuming(igvn);
|
||||
}
|
||||
|
||||
//------------------------------dominated_by-----------------------------------
|
||||
Node* IfNode::dominated_by(Node* prev_dom, PhaseIterGVN *igvn) {
|
||||
Node* IfNode::dominated_by(Node* prev_dom, PhaseIterGVN* igvn, bool pin_array_access_nodes) {
|
||||
#ifndef PRODUCT
|
||||
if (TraceIterativeGVN) {
|
||||
tty->print(" Removing IfNode: "); this->dump();
|
||||
@ -1506,15 +1512,6 @@ Node* IfNode::dominated_by(Node* prev_dom, PhaseIterGVN *igvn) {
|
||||
int prev_op = prev_dom->Opcode();
|
||||
Node *top = igvn->C->top(); // Shortcut to top
|
||||
|
||||
// Loop predicates may have depending checks which should not
|
||||
// be skipped. For example, range check predicate has two checks
|
||||
// for lower and upper bounds.
|
||||
ProjNode* unc_proj = proj_out(1 - prev_dom->as_Proj()->_con)->as_Proj();
|
||||
if (unc_proj->is_uncommon_trap_proj(Deoptimization::Reason_predicate) != nullptr ||
|
||||
unc_proj->is_uncommon_trap_proj(Deoptimization::Reason_profile_predicate) != nullptr) {
|
||||
prev_dom = idom;
|
||||
}
|
||||
|
||||
// Now walk the current IfNode's projections.
|
||||
// Loop ends when 'this' has no more uses.
|
||||
for (DUIterator_Last imin, i = last_outs(imin); i >= imin; --i) {
|
||||
@ -1537,6 +1534,19 @@ Node* IfNode::dominated_by(Node* prev_dom, PhaseIterGVN *igvn) {
|
||||
// For control producers.
|
||||
// Do not rewire Div and Mod nodes which could have a zero divisor to avoid skipping their zero check.
|
||||
igvn->replace_input_of(s, 0, data_target); // Move child to data-target
|
||||
if (pin_array_access_nodes && data_target != top) {
|
||||
// As a result of range check smearing, Loads and range check Cast nodes that are control dependent on this
|
||||
// range check (that is about to be removed) now depend on multiple dominating range checks. After the removal
|
||||
// of this range check, these control dependent nodes end up at the lowest/nearest dominating check in the
|
||||
// graph. To ensure that these Loads/Casts do not float above any of the dominating checks (even when the
|
||||
// lowest dominating check is later replaced by yet another dominating check), we need to pin them at the
|
||||
// lowest dominating check.
|
||||
Node* clone = s->pin_array_access_node();
|
||||
if (clone != nullptr) {
|
||||
clone = igvn->transform(clone);
|
||||
igvn->replace_node(s, clone);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Find the control input matching this def-use edge.
|
||||
// For Regions it may not be in slot 0.
|
||||
@ -1781,6 +1791,22 @@ bool IfNode::is_zero_trip_guard() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void IfProjNode::pin_array_access_nodes(PhaseIterGVN* igvn) {
|
||||
for (DUIterator i = outs(); has_out(i); i++) {
|
||||
Node* u = out(i);
|
||||
if (!u->depends_only_on_test()) {
|
||||
continue;
|
||||
}
|
||||
Node* clone = u->pin_array_access_node();
|
||||
if (clone != nullptr) {
|
||||
clone = igvn->transform(clone);
|
||||
assert(clone != u, "shouldn't common");
|
||||
igvn->replace_node(u, clone);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
//------------------------------dump_spec--------------------------------------
|
||||
void IfNode::dump_spec(outputStream *st) const {
|
||||
@ -1910,7 +1936,7 @@ Node* RangeCheckNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
off_lo = MIN2(off_lo,offset2);
|
||||
off_hi = MAX2(off_hi,offset2);
|
||||
// Record top NRC range checks
|
||||
prev_checks[nb_checks%NRC].ctl = prev_dom;
|
||||
prev_checks[nb_checks%NRC].ctl = prev_dom->as_IfProj();
|
||||
prev_checks[nb_checks%NRC].off = offset2;
|
||||
nb_checks++;
|
||||
}
|
||||
@ -1927,6 +1953,15 @@ Node* RangeCheckNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
// the interpreter. If we see range-check deopt's, do not widen!
|
||||
if (!phase->C->allow_range_check_smearing()) return nullptr;
|
||||
|
||||
if (can_reshape && !phase->C->post_loop_opts_phase()) {
|
||||
// We are about to perform range check smearing (i.e. remove this RangeCheck if it is dominated by
|
||||
// a series of RangeChecks which have a range that covers this RangeCheck). This can cause array access nodes to
|
||||
// be pinned. We want to avoid that and first allow range check elimination a chance to remove the RangeChecks
|
||||
// from loops. Hence, we delay range check smearing until after loop opts.
|
||||
phase->C->record_for_post_loop_opts_igvn(this);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Didn't find prior covering check, so cannot remove anything.
|
||||
if (nb_checks == 0) {
|
||||
return nullptr;
|
||||
@ -2000,6 +2035,26 @@ Node* RangeCheckNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
// Test is now covered by prior checks, dominate it out
|
||||
prev_dom = rc0.ctl;
|
||||
}
|
||||
// The last RangeCheck is found to be redundant with a sequence of n (n >= 2) preceding RangeChecks.
|
||||
// If an array load is control dependent on the eliminated range check, the array load nodes (CastII and Load)
|
||||
// become control dependent on the last range check of the sequence, but they are really dependent on the entire
|
||||
// sequence of RangeChecks. If RangeCheck#n is later replaced by a dominating identical check, the array load
|
||||
// nodes must not float above the n-1 other RangeCheck in the sequence. We pin the array load nodes here to
|
||||
// guarantee it doesn't happen.
|
||||
//
|
||||
// RangeCheck#1 RangeCheck#1
|
||||
// | \ | \
|
||||
// | uncommon trap | uncommon trap
|
||||
// .. ..
|
||||
// RangeCheck#n -> RangeCheck#n
|
||||
// | \ | \
|
||||
// | uncommon trap CastII uncommon trap
|
||||
// RangeCheck Load
|
||||
// | \
|
||||
// CastII uncommon trap
|
||||
// Load
|
||||
|
||||
return dominated_by(prev_dom, igvn, true);
|
||||
}
|
||||
} else {
|
||||
prev_dom = search_identical(4, igvn);
|
||||
@ -2010,7 +2065,7 @@ Node* RangeCheckNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
}
|
||||
|
||||
// Replace dominated IfNode
|
||||
return dominated_by(prev_dom, igvn);
|
||||
return dominated_by(prev_dom, igvn, false);
|
||||
}
|
||||
|
||||
ParsePredicateNode::ParsePredicateNode(Node* control, Deoptimization::DeoptReason deopt_reason, PhaseGVN* gvn)
|
||||
|
@ -1180,6 +1180,7 @@ bool PhaseIdealLoop::loop_predication_impl_helper(IdealLoopTree* loop, IfProjNod
|
||||
return false;
|
||||
}
|
||||
BoolNode* bol = test->as_Bool();
|
||||
bool range_check_predicate = false;
|
||||
if (invar.is_invariant(bol)) {
|
||||
C->print_method(PHASE_BEFORE_LOOP_PREDICATION_IC, 4, iff);
|
||||
// Invariant test
|
||||
@ -1212,6 +1213,7 @@ bool PhaseIdealLoop::loop_predication_impl_helper(IdealLoopTree* loop, IfProjNod
|
||||
}
|
||||
#endif
|
||||
} else if (cl != nullptr && loop->is_range_check_if(if_success_proj, this, invar DEBUG_ONLY(COMMA parse_predicate_proj))) {
|
||||
range_check_predicate = true;
|
||||
C->print_method(PHASE_BEFORE_LOOP_PREDICATION_RC, 4, iff);
|
||||
// Range check for counted loops
|
||||
assert(if_success_proj->is_IfTrue(), "trap must be on false projection for a range check");
|
||||
@ -1294,7 +1296,10 @@ bool PhaseIdealLoop::loop_predication_impl_helper(IdealLoopTree* loop, IfProjNod
|
||||
invar.map_ctrl(if_success_proj, new_predicate_proj); // so that invariance test can be appropriate
|
||||
|
||||
// Eliminate the old If in the loop body
|
||||
dominated_by(new_predicate_proj, iff, if_success_proj->_con != new_predicate_proj->_con);
|
||||
// If a range check is eliminated, data dependent nodes (Load and range check CastII nodes) are now dependent on 2
|
||||
// Hoisted Check Predicates (one for the start of the loop, one for the end) but we can only keep track of one control
|
||||
// dependency: pin the data dependent nodes.
|
||||
dominated_by(new_predicate_proj, iff, if_success_proj->_con != new_predicate_proj->_con, range_check_predicate);
|
||||
|
||||
C->set_major_progress();
|
||||
return true;
|
||||
|
@ -179,12 +179,12 @@ void PhaseIdealLoop::do_unswitching(IdealLoopTree *loop, Node_List &old_new) {
|
||||
|
||||
// Hardwire the control paths in the loops into if(true) and if(false)
|
||||
_igvn.rehash_node_delayed(unswitch_iff);
|
||||
dominated_by(proj_true->as_IfProj(), unswitch_iff, false, false);
|
||||
dominated_by(proj_true->as_IfProj(), unswitch_iff);
|
||||
|
||||
IfNode* unswitch_iff_clone = old_new[unswitch_iff->_idx]->as_If();
|
||||
_igvn.rehash_node_delayed(unswitch_iff_clone);
|
||||
ProjNode* proj_false = invar_iff->proj_out(0);
|
||||
dominated_by(proj_false->as_IfProj(), unswitch_iff_clone, false, false);
|
||||
dominated_by(proj_false->as_IfProj(), unswitch_iff_clone);
|
||||
|
||||
// Reoptimize loops
|
||||
loop->record_for_igvn();
|
||||
|
@ -1507,7 +1507,7 @@ public:
|
||||
Node *has_local_phi_input( Node *n );
|
||||
// Mark an IfNode as being dominated by a prior test,
|
||||
// without actually altering the CFG (and hence IDOM info).
|
||||
void dominated_by(IfProjNode* prevdom, IfNode* iff, bool flip = false, bool exclude_loop_predicate = false);
|
||||
void dominated_by(IfProjNode* prevdom, IfNode* iff, bool flip = false, bool pin_array_access_nodes = false);
|
||||
|
||||
// Split Node 'n' through merge point
|
||||
RegionNode* split_thru_region(Node* n, RegionNode* region);
|
||||
|
@ -305,7 +305,7 @@ bool PhaseIdealLoop::loop_phi_backedge_type_contains_zero(const Node* phi_diviso
|
||||
// Replace the dominated test with an obvious true or false. Place it on the
|
||||
// IGVN worklist for later cleanup. Move control-dependent data Nodes on the
|
||||
// live path up to the dominating control.
|
||||
void PhaseIdealLoop::dominated_by(IfProjNode* prevdom, IfNode* iff, bool flip, bool exclude_loop_predicate) {
|
||||
void PhaseIdealLoop::dominated_by(IfProjNode* prevdom, IfNode* iff, bool flip, bool pin_array_access_nodes) {
|
||||
if (VerifyLoopOptimizations && PrintOpto) { tty->print_cr("dominating test"); }
|
||||
|
||||
// prevdom is the dominating projection of the dominating test.
|
||||
@ -330,7 +330,7 @@ void PhaseIdealLoop::dominated_by(IfProjNode* prevdom, IfNode* iff, bool flip, b
|
||||
// Hack the dominated test
|
||||
_igvn.replace_input_of(iff, 1, con);
|
||||
|
||||
// If I dont have a reachable TRUE and FALSE path following the IfNode then
|
||||
// If I don't have a reachable TRUE and FALSE path following the IfNode then
|
||||
// I can assume this path reaches an infinite loop. In this case it's not
|
||||
// important to optimize the data Nodes - either the whole compilation will
|
||||
// be tossed or this path (and all data Nodes) will go dead.
|
||||
@ -341,24 +341,9 @@ void PhaseIdealLoop::dominated_by(IfProjNode* prevdom, IfNode* iff, bool flip, b
|
||||
// dominating projection.
|
||||
Node* dp = iff->proj_out_or_null(pop == Op_IfTrue);
|
||||
|
||||
// Loop predicates may have depending checks which should not
|
||||
// be skipped. For example, range check predicate has two checks
|
||||
// for lower and upper bounds.
|
||||
if (dp == nullptr)
|
||||
return;
|
||||
|
||||
ProjNode* dp_proj = dp->as_Proj();
|
||||
ProjNode* unc_proj = iff->proj_out(1 - dp_proj->_con)->as_Proj();
|
||||
if (exclude_loop_predicate &&
|
||||
(unc_proj->is_uncommon_trap_proj(Deoptimization::Reason_predicate) != nullptr ||
|
||||
unc_proj->is_uncommon_trap_proj(Deoptimization::Reason_profile_predicate) != nullptr ||
|
||||
unc_proj->is_uncommon_trap_proj(Deoptimization::Reason_range_check) != nullptr)) {
|
||||
// If this is a range check (IfNode::is_range_check), do not
|
||||
// reorder because Compile::allow_range_check_smearing might have
|
||||
// changed the check.
|
||||
return; // Let IGVN transformation change control dependence.
|
||||
}
|
||||
|
||||
IdealLoopTree* old_loop = get_loop(dp);
|
||||
|
||||
for (DUIterator_Fast imax, i = dp->fast_outs(imax); i < imax; i++) {
|
||||
@ -367,6 +352,20 @@ void PhaseIdealLoop::dominated_by(IfProjNode* prevdom, IfNode* iff, bool flip, b
|
||||
if (cd->depends_only_on_test() && _igvn.no_dependent_zero_check(cd)) {
|
||||
assert(cd->in(0) == dp, "");
|
||||
_igvn.replace_input_of(cd, 0, prevdom);
|
||||
if (pin_array_access_nodes) {
|
||||
// Because of Loop Predication, Loads and range check Cast nodes that are control dependent on this range
|
||||
// check (that is about to be removed) now depend on multiple dominating Hoisted Check Predicates. After the
|
||||
// removal of this range check, these control dependent nodes end up at the lowest/nearest dominating predicate
|
||||
// in the graph. To ensure that these Loads/Casts do not float above any of the dominating checks (even when the
|
||||
// lowest dominating check is later replaced by yet another dominating check), we need to pin them at the lowest
|
||||
// dominating check.
|
||||
Node* clone = cd->pin_array_access_node();
|
||||
if (clone != nullptr) {
|
||||
clone = _igvn.register_new_node_with_optimizer(clone, cd);
|
||||
_igvn.replace_node(cd, clone);
|
||||
cd = clone;
|
||||
}
|
||||
}
|
||||
set_early_ctrl(cd, false);
|
||||
IdealLoopTree* new_loop = get_loop(get_ctrl(cd));
|
||||
if (old_loop != new_loop) {
|
||||
@ -1486,7 +1485,7 @@ void PhaseIdealLoop::split_if_with_blocks_post(Node *n) {
|
||||
// Replace the dominated test with an obvious true or false.
|
||||
// Place it on the IGVN worklist for later cleanup.
|
||||
C->set_major_progress();
|
||||
dominated_by(prevdom->as_IfProj(), n->as_If(), false, true);
|
||||
dominated_by(prevdom->as_IfProj(), n->as_If());
|
||||
DEBUG_ONLY( if (VerifyLoopOptimizations) { verify(); } );
|
||||
return;
|
||||
}
|
||||
@ -1579,10 +1578,10 @@ bool PhaseIdealLoop::try_merge_identical_ifs(Node* n) {
|
||||
// unrelated control dependency.
|
||||
for (uint i = 1; i < new_false_region->req(); i++) {
|
||||
if (is_dominator(dom_proj_true, new_false_region->in(i))) {
|
||||
dominated_by(dom_proj_true->as_IfProj(), new_false_region->in(i)->in(0)->as_If(), false, false);
|
||||
dominated_by(dom_proj_true->as_IfProj(), new_false_region->in(i)->in(0)->as_If());
|
||||
} else {
|
||||
assert(is_dominator(dom_proj_false, new_false_region->in(i)), "bad if");
|
||||
dominated_by(dom_proj_false->as_IfProj(), new_false_region->in(i)->in(0)->as_If(), false, false);
|
||||
dominated_by(dom_proj_false->as_IfProj(), new_false_region->in(i)->in(0)->as_If());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -845,8 +845,12 @@ bool LoadNode::can_remove_control() const {
|
||||
return !has_pinned_control_dependency();
|
||||
}
|
||||
uint LoadNode::size_of() const { return sizeof(*this); }
|
||||
bool LoadNode::cmp( const Node &n ) const
|
||||
{ return !Type::cmp( _type, ((LoadNode&)n)._type ); }
|
||||
bool LoadNode::cmp(const Node &n) const {
|
||||
LoadNode& load = (LoadNode &)n;
|
||||
return !Type::cmp(_type, load._type) &&
|
||||
_control_dependency == load._control_dependency &&
|
||||
_mo == load._mo;
|
||||
}
|
||||
const Type *LoadNode::bottom_type() const { return _type; }
|
||||
uint LoadNode::ideal_reg() const {
|
||||
return _type->ideal_reg();
|
||||
@ -983,6 +987,14 @@ static bool skip_through_membars(Compile::AliasType* atp, const TypeInstPtr* tp,
|
||||
return false;
|
||||
}
|
||||
|
||||
LoadNode* LoadNode::pin_array_access_node() const {
|
||||
const TypePtr* adr_type = this->adr_type();
|
||||
if (adr_type != nullptr && adr_type->isa_aryptr()) {
|
||||
return clone_pinned();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Is the value loaded previously stored by an arraycopy? If so return
|
||||
// a load node that reads from the source array so we may be able to
|
||||
// optimize out the ArrayCopy node later.
|
||||
@ -1002,7 +1014,8 @@ Node* LoadNode::can_see_arraycopy_value(Node* st, PhaseGVN* phase) const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LoadNode* ld = clone()->as_Load();
|
||||
// load depends on the tests that validate the arraycopy
|
||||
LoadNode* ld = clone_pinned();
|
||||
Node* addp = in(MemNode::Address)->clone();
|
||||
if (ac->as_ArrayCopy()->is_clonebasic()) {
|
||||
assert(ld_alloc != nullptr, "need an alloc");
|
||||
@ -1044,8 +1057,6 @@ Node* LoadNode::can_see_arraycopy_value(Node* st, PhaseGVN* phase) const {
|
||||
ld->set_req(MemNode::Address, addp);
|
||||
ld->set_req(0, ctl);
|
||||
ld->set_req(MemNode::Memory, mem);
|
||||
// load depends on the tests that validate the arraycopy
|
||||
ld->_control_dependency = UnknownControl;
|
||||
return ld;
|
||||
}
|
||||
return nullptr;
|
||||
@ -2499,6 +2510,12 @@ Node* LoadNode::klass_identity_common(PhaseGVN* phase) {
|
||||
return this;
|
||||
}
|
||||
|
||||
LoadNode* LoadNode::clone_pinned() const {
|
||||
LoadNode* ld = clone()->as_Load();
|
||||
ld->_control_dependency = UnknownControl;
|
||||
return ld;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------Value------------------------------------------
|
||||
const Type* LoadNKlassNode::Value(PhaseGVN* phase) const {
|
||||
|
@ -292,6 +292,8 @@ public:
|
||||
bool has_unknown_control_dependency() const { return _control_dependency == UnknownControl; }
|
||||
bool has_pinned_control_dependency() const { return _control_dependency == Pinned; }
|
||||
|
||||
LoadNode* pin_array_access_node() const;
|
||||
|
||||
#ifndef PRODUCT
|
||||
virtual void dump_spec(outputStream *st) const;
|
||||
#endif
|
||||
@ -317,6 +319,8 @@ protected:
|
||||
virtual bool depends_only_on_test() const {
|
||||
return adr_type() != TypeRawPtr::BOTTOM && _control_dependency == DependsOnlyOnTest;
|
||||
}
|
||||
|
||||
LoadNode* clone_pinned() const;
|
||||
};
|
||||
|
||||
//------------------------------LoadBNode--------------------------------------
|
||||
|
@ -1136,7 +1136,14 @@ public:
|
||||
template <typename Callback, typename Check>
|
||||
void visit_uses(Callback callback, Check is_boundary) const;
|
||||
|
||||
//----------------- Code Generation
|
||||
// Returns a clone of the current node that's pinned (if the current node is not) for nodes found in array accesses
|
||||
// (Load and range check CastII nodes).
|
||||
// This is used when an array access is made dependent on 2 or more range checks (range check smearing or Loop Predication).
|
||||
virtual Node* pin_array_access_node() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//----------------- Code Generation
|
||||
|
||||
// Ideal register class for Matching. Zero means unmatched instruction
|
||||
// (these are cloned instead of converted to machine nodes).
|
||||
|
158
test/hotspot/jtreg/compiler/rangechecks/TestArrayAccessAboveRCAfterSmearingOrPredication.java
Normal file
158
test/hotspot/jtreg/compiler/rangechecks/TestArrayAccessAboveRCAfterSmearingOrPredication.java
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Red Hat, Inc. 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 8319793
|
||||
* @summary Replacing a test with a dominating test can cause an array load to float above a range check that guards it
|
||||
* @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:-TieredCompilation TestArrayAccessAboveRCAfterSmearingOrPredication
|
||||
*/
|
||||
|
||||
|
||||
public class TestArrayAccessAboveRCAfterSmearingOrPredication {
|
||||
private static int field;
|
||||
private static int flagField;
|
||||
private static volatile int volatileField;
|
||||
|
||||
public static void main(String[] args) {
|
||||
float[] array = new float[100];
|
||||
for (int i = 0; i < 20_000; i++) {
|
||||
testRangeCheckSmearing(array, 0, 1, true, true, true);
|
||||
testRangeCheckSmearing(array, 0, 1, true, false, true);
|
||||
testRangeCheckSmearing(array, 0, 1, false, false, true);
|
||||
testRangeCheckSmearing(array, 0, 1, true, true, false);
|
||||
testRangeCheckSmearing(array, 0, 1, true, false, false);
|
||||
testRangeCheckSmearing(array, 0, 1, false, false, false);
|
||||
testHelper(0);
|
||||
|
||||
testLoopPredication(array, 0, 1, true, true, true);
|
||||
testLoopPredication(array, 0, 1, true, false, true);
|
||||
testLoopPredication(array, 0, 1, false, false, true);
|
||||
testLoopPredication(array, 0, 1, true, true, false);
|
||||
testLoopPredication(array, 0, 1, true, false, false);
|
||||
testLoopPredication(array, 0, 1, false, false, false);
|
||||
}
|
||||
try {
|
||||
testRangeCheckSmearing(array, Integer.MAX_VALUE, 1, false, false, true);
|
||||
} catch (ArrayIndexOutOfBoundsException arrayIndexOutOfBoundsException) {
|
||||
}
|
||||
try {
|
||||
testLoopPredication(array, Integer.MAX_VALUE, 1, false, false, true);
|
||||
} catch (ArrayIndexOutOfBoundsException arrayIndexOutOfBoundsException) {
|
||||
}
|
||||
}
|
||||
|
||||
private static float testRangeCheckSmearing(float[] array, int i, int flag, boolean flag2, boolean flag3, boolean flag4) {
|
||||
if (array == null) {
|
||||
}
|
||||
flagField = flag;
|
||||
int j;
|
||||
for (j = 0; j < 10; j++) {
|
||||
}
|
||||
for (int k = 0; k < 10; k++) {
|
||||
for (int l = 0; l < 10; l++) {
|
||||
}
|
||||
}
|
||||
testHelper(j);
|
||||
float v = 0;
|
||||
if (flag == 1) {
|
||||
if (flag4) {
|
||||
v += array[i];
|
||||
if (flag2) {
|
||||
if (flag3) {
|
||||
field = 0x42;
|
||||
}
|
||||
}
|
||||
if (flagField == 1) {
|
||||
v += array[i];
|
||||
}
|
||||
} else {
|
||||
v += array[i];
|
||||
if (flag2) {
|
||||
if (flag3) {
|
||||
field = 0x42;
|
||||
}
|
||||
}
|
||||
if (flagField == 1) {
|
||||
v += array[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
private static void testHelper(int j) {
|
||||
if (j == 10) {
|
||||
return;
|
||||
}
|
||||
flagField = 0;
|
||||
}
|
||||
|
||||
private static float testLoopPredication(float[] array, int i, int flag, boolean flag2, boolean flag3, boolean flag4) {
|
||||
i = Math.min(i, Integer.MAX_VALUE - 2);
|
||||
if (array == null) {
|
||||
}
|
||||
flagField = flag;
|
||||
int j;
|
||||
for (j = 0; j < 10; j++) {
|
||||
for (int k = 0; k < 10; k++) {
|
||||
}
|
||||
}
|
||||
testHelper(j);
|
||||
|
||||
float v = 0;
|
||||
if (flag == 1) {
|
||||
if (flag4) {
|
||||
float dummy = array[i];
|
||||
dummy = array[i + 2];
|
||||
if (flag2) {
|
||||
if (flag3) {
|
||||
field = 0x42;
|
||||
}
|
||||
}
|
||||
if (flagField == 1) {
|
||||
for (int m = 0; m < 3; m++) {
|
||||
v += array[i + m];
|
||||
}
|
||||
}
|
||||
volatileField = 42;
|
||||
} else {
|
||||
float dummy = array[i];
|
||||
dummy = array[i + 2];
|
||||
if (flag2) {
|
||||
if (flag3) {
|
||||
field = 0x42;
|
||||
}
|
||||
}
|
||||
if (flagField == 1) {
|
||||
for (int m = 0; m < 3; m++) {
|
||||
v += array[i + m];
|
||||
}
|
||||
}
|
||||
volatileField = 42;
|
||||
}
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Red Hat, Inc. 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 8319793
|
||||
* @summary Replacing a test with a dominating test can cause an array access CastII to float above a range check that guards it
|
||||
* @run main/othervm -Xbatch -XX:-TieredCompilation TestArrayAccessCastIIAboveRC
|
||||
*/
|
||||
|
||||
public class TestArrayAccessCastIIAboveRC {
|
||||
static int N = 400;
|
||||
static int iArrFld[] = new int[N];
|
||||
|
||||
static void test() {
|
||||
float fArr[] = new float[N];
|
||||
int i9, i10, i12;
|
||||
long lArr1[] = new long[N];
|
||||
for (i9 = 7; i9 < 43; i9++) {
|
||||
try {
|
||||
i10 = 7 % i9;
|
||||
iArrFld[i9 + 1] = i9 / i10;
|
||||
} catch (ArithmeticException a_e) {
|
||||
}
|
||||
for (i12 = 1; 7 > i12; i12++)
|
||||
lArr1[i9 - 1] = 42;
|
||||
iArrFld[i12] = 4;
|
||||
fArr[i9 - 1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
for (int i = 0; i < 50_000; ++i) {
|
||||
test();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user