mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-30 21:18:25 +00:00
8282045: When loop strip mining fails, safepoints are removed from loop anyway
Reviewed-by: thartmann, chagedorn
This commit is contained in:
parent
341c8bd7f2
commit
2c5d266f9f
@ -690,8 +690,7 @@ SafePointNode* PhaseIdealLoop::find_safepoint(Node* back_control, Node* x, Ideal
|
||||
|
||||
Node* mem = safepoint->in(TypeFunc::Memory);
|
||||
|
||||
// We can only use that safepoint if there's not side effect
|
||||
// between the backedge and the safepoint.
|
||||
// We can only use that safepoint if there's no side effect between the backedge and the safepoint.
|
||||
|
||||
// mm is used for book keeping
|
||||
MergeMemNode* mm = NULL;
|
||||
@ -1621,16 +1620,6 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*&loop, BasicType iv_
|
||||
return false;
|
||||
}
|
||||
|
||||
if (x->in(LoopNode::LoopBackControl)->Opcode() == Op_SafePoint &&
|
||||
((iv_bt == T_INT && LoopStripMiningIter != 0) ||
|
||||
iv_bt == T_LONG)) {
|
||||
// Leaving the safepoint on the backedge and creating a
|
||||
// CountedLoop will confuse optimizations. We can't move the
|
||||
// safepoint around because its jvm state wouldn't match a new
|
||||
// location. Give up on that loop.
|
||||
return false;
|
||||
}
|
||||
|
||||
Node* iftrue = back_control;
|
||||
uint iftrue_op = iftrue->Opcode();
|
||||
Node* iff = iftrue->in(0);
|
||||
@ -1859,6 +1848,37 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*&loop, BasicType iv_
|
||||
}
|
||||
}
|
||||
|
||||
Node* sfpt = NULL;
|
||||
if (loop->_child == NULL) {
|
||||
sfpt = find_safepoint(back_control, x, loop);
|
||||
} else {
|
||||
sfpt = iff->in(0);
|
||||
if (sfpt->Opcode() != Op_SafePoint) {
|
||||
sfpt = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (x->in(LoopNode::LoopBackControl)->Opcode() == Op_SafePoint) {
|
||||
Node* backedge_sfpt = x->in(LoopNode::LoopBackControl);
|
||||
if (((iv_bt == T_INT && LoopStripMiningIter != 0) ||
|
||||
iv_bt == T_LONG) &&
|
||||
sfpt == NULL) {
|
||||
// Leaving the safepoint on the backedge and creating a
|
||||
// CountedLoop will confuse optimizations. We can't move the
|
||||
// safepoint around because its jvm state wouldn't match a new
|
||||
// location. Give up on that loop.
|
||||
return false;
|
||||
}
|
||||
if (is_deleteable_safept(backedge_sfpt)) {
|
||||
lazy_replace(backedge_sfpt, iftrue);
|
||||
if (loop->_safepts != NULL) {
|
||||
loop->_safepts->yank(backedge_sfpt);
|
||||
}
|
||||
loop->_tail = iftrue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef ASSERT
|
||||
if (iv_bt == T_INT &&
|
||||
!x->as_Loop()->is_loop_nest_inner_loop() &&
|
||||
@ -1897,18 +1917,6 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*&loop, BasicType iv_
|
||||
}
|
||||
set_subtree_ctrl(adjusted_limit, false);
|
||||
|
||||
if (iv_bt == T_INT && LoopStripMiningIter == 0) {
|
||||
// Check for SafePoint on backedge and remove
|
||||
Node *sfpt = x->in(LoopNode::LoopBackControl);
|
||||
if (sfpt->Opcode() == Op_SafePoint && is_deleteable_safept(sfpt)) {
|
||||
lazy_replace( sfpt, iftrue );
|
||||
if (loop->_safepts != NULL) {
|
||||
loop->_safepts->yank(sfpt);
|
||||
}
|
||||
loop->_tail = iftrue;
|
||||
}
|
||||
}
|
||||
|
||||
// Build a canonical trip test.
|
||||
// Clone code, as old values may be in use.
|
||||
incr = incr->clone();
|
||||
@ -1980,13 +1988,11 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*&loop, BasicType iv_
|
||||
assert(iff->outcnt() == 0, "should be dead now");
|
||||
lazy_replace( iff, le ); // fix 'get_ctrl'
|
||||
|
||||
Node *sfpt2 = le->in(0);
|
||||
|
||||
Node* entry_control = init_control;
|
||||
bool strip_mine_loop = iv_bt == T_INT &&
|
||||
LoopStripMiningIter > 1 &&
|
||||
loop->_child == NULL &&
|
||||
sfpt2->Opcode() == Op_SafePoint &&
|
||||
sfpt != NULL &&
|
||||
!loop->_has_call;
|
||||
IdealLoopTree* outer_ilt = NULL;
|
||||
if (strip_mine_loop) {
|
||||
@ -2012,30 +2018,30 @@ bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*&loop, BasicType iv_
|
||||
|
||||
if (iv_bt == T_INT && (LoopStripMiningIter == 0 || strip_mine_loop)) {
|
||||
// Check for immediately preceding SafePoint and remove
|
||||
if (sfpt2->Opcode() == Op_SafePoint && (LoopStripMiningIter != 0 || is_deleteable_safept(sfpt2))) {
|
||||
if (sfpt != NULL && (LoopStripMiningIter != 0 || is_deleteable_safept(sfpt))) {
|
||||
if (strip_mine_loop) {
|
||||
Node* outer_le = outer_ilt->_tail->in(0);
|
||||
Node* sfpt = sfpt2->clone();
|
||||
sfpt->set_req(0, iffalse);
|
||||
outer_le->set_req(0, sfpt);
|
||||
Node* sfpt_clone = sfpt->clone();
|
||||
sfpt_clone->set_req(0, iffalse);
|
||||
outer_le->set_req(0, sfpt_clone);
|
||||
|
||||
Node* polladdr = sfpt->in(TypeFunc::Parms);
|
||||
Node* polladdr = sfpt_clone->in(TypeFunc::Parms);
|
||||
if (polladdr != nullptr && polladdr->is_Load()) {
|
||||
// Polling load should be pinned outside inner loop.
|
||||
Node* new_polladdr = polladdr->clone();
|
||||
new_polladdr->set_req(0, iffalse);
|
||||
_igvn.register_new_node_with_optimizer(new_polladdr, polladdr);
|
||||
set_ctrl(new_polladdr, iffalse);
|
||||
sfpt->set_req(TypeFunc::Parms, new_polladdr);
|
||||
sfpt_clone->set_req(TypeFunc::Parms, new_polladdr);
|
||||
}
|
||||
// When this code runs, loop bodies have not yet been populated.
|
||||
const bool body_populated = false;
|
||||
register_control(sfpt, outer_ilt, iffalse, body_populated);
|
||||
set_idom(outer_le, sfpt, dom_depth(sfpt));
|
||||
register_control(sfpt_clone, outer_ilt, iffalse, body_populated);
|
||||
set_idom(outer_le, sfpt_clone, dom_depth(sfpt_clone));
|
||||
}
|
||||
lazy_replace( sfpt2, sfpt2->in(TypeFunc::Control));
|
||||
lazy_replace(sfpt, sfpt->in(TypeFunc::Control));
|
||||
if (loop->_safepts != NULL) {
|
||||
loop->_safepts->yank(sfpt2);
|
||||
loop->_safepts->yank(sfpt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3663,7 +3669,7 @@ void IdealLoopTree::counted_loop( PhaseIdealLoop *phase ) {
|
||||
if (_head->is_CountedLoop() ||
|
||||
phase->is_counted_loop(_head, loop, T_INT)) {
|
||||
|
||||
if (LoopStripMiningIter == 0 || (LoopStripMiningIter > 1 && _child == NULL)) {
|
||||
if (LoopStripMiningIter == 0 || _head->as_CountedLoop()->is_strip_mined()) {
|
||||
// Indicate we do not need a safepoint here
|
||||
_has_sfpt = 1;
|
||||
}
|
||||
|
||||
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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.
|
||||
*/
|
||||
|
||||
package compiler.c2.irTests;
|
||||
|
||||
import compiler.lib.ir_framework.*;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8282045
|
||||
* @summary When loop strip mining fails, safepoints are removed from loop anyway
|
||||
* @library /test/lib /
|
||||
* @run driver compiler.c2.irTests.TestStripMiningDropsSafepoint
|
||||
*/
|
||||
|
||||
public class TestStripMiningDropsSafepoint {
|
||||
public static void main(String[] args) {
|
||||
TestFramework.runWithFlags("-XX:+UseCountedLoopSafepoints", "-XX:LoopStripMiningIter=1000", "-XX:LoopMaxUnroll=1", "-XX:-RangeCheckElimination");
|
||||
TestFramework.runWithFlags("-XX:+UseCountedLoopSafepoints", "-XX:LoopStripMiningIter=1000", "-XX:LoopMaxUnroll=1", "-XX:-RangeCheckElimination", "-XX:-PartialPeelLoop");
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(applyIf = { "PartialPeelLoop", "true" }, counts = { IRNode.COUNTEDLOOP, "1", IRNode.OUTERSTRIPMINEDLOOP, "1", IRNode.SAFEPOINT, "1" })
|
||||
private static void test1(int[] dst, int[] src) {
|
||||
// Partial peel is applied. No side effect between exit and
|
||||
// safepoint.
|
||||
for (int i = 0; ; ) {
|
||||
// prevent ciTypeFlow from cloning head
|
||||
synchronized (new Object()) {}
|
||||
i++;
|
||||
if (i >= src.length) {
|
||||
break;
|
||||
}
|
||||
dst[i] = src[i];
|
||||
if (i / 2 >= 2000) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = "test1")
|
||||
private static void test1_runner() {
|
||||
int[] array1 = new int[1000];
|
||||
int[] array2 = new int[10000];
|
||||
test1(array1, array1);
|
||||
test1(array2, array2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(applyIf = { "PartialPeelLoop", "true" }, counts = { IRNode.COUNTEDLOOP, "1", IRNode.SAFEPOINT, "1" })
|
||||
@IR(applyIf = { "PartialPeelLoop", "true" }, failOn = { IRNode.OUTERSTRIPMINEDLOOP })
|
||||
private static void test2(int[] dst, int[] src) {
|
||||
// Partial peel is applied. Some side effect between exit and
|
||||
// safepoint.
|
||||
int v = src[0];
|
||||
for (int i = 0; ; ) {
|
||||
synchronized (new Object()) {}
|
||||
dst[i] = v;
|
||||
i++;
|
||||
if (i >= src.length) {
|
||||
break;
|
||||
}
|
||||
v = src[i];
|
||||
if (i / 2 >= 2000) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = "test2")
|
||||
private static void test2_runner() {
|
||||
int[] array1 = new int[1000];
|
||||
int[] array2 = new int[10000];
|
||||
test2(array1, array1);
|
||||
test2(array2, array2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(applyIf = { "PartialPeelLoop", "false" }, counts = { IRNode.COUNTEDLOOP, "1", IRNode.OUTERSTRIPMINEDLOOP, "1", IRNode.SAFEPOINT, "1" })
|
||||
private static void test3(int[] dst, int[] src) {
|
||||
int v = src[0];
|
||||
for (int i = 0; ; ) {
|
||||
synchronized (new Object()) {}
|
||||
dst[i] = v;
|
||||
int inc = test3_helper(2);
|
||||
v = src[i];
|
||||
i += (inc / 2);
|
||||
if (i >= src.length) {
|
||||
break;
|
||||
}
|
||||
for (int j = 0; j < 10; j++) {
|
||||
}
|
||||
// safepoint on backedge
|
||||
}
|
||||
}
|
||||
|
||||
private static int test3_helper(int stop) {
|
||||
int i = 1;
|
||||
do {
|
||||
synchronized (new Object()) {}
|
||||
i *= 2;
|
||||
} while (i < stop);
|
||||
return i;
|
||||
}
|
||||
|
||||
@Run(test = "test3")
|
||||
private static void test3_runner() {
|
||||
int[] array1 = new int[1000];
|
||||
test3(array1, array1);
|
||||
test3_helper(10);
|
||||
}
|
||||
}
|
||||
@ -115,6 +115,7 @@ public class IRNode {
|
||||
public static final String LOOP = START + "Loop" + MID + END;
|
||||
public static final String COUNTEDLOOP = START + "CountedLoop\\b" + MID + END;
|
||||
public static final String COUNTEDLOOP_MAIN = START + "CountedLoop\\b" + MID + "main" + END;
|
||||
public static final String OUTERSTRIPMINEDLOOP = START + "OuterStripMinedLoop\\b" + MID + END;
|
||||
public static final String IF = START + "If\\b" + MID + END;
|
||||
|
||||
public static final String CALL = START + "Call.*Java" + MID + END;
|
||||
@ -136,6 +137,7 @@ public class IRNode {
|
||||
|
||||
public static final String SCOPE_OBJECT = "(.*# ScObj.*" + END;
|
||||
public static final String MEMBAR = START + "MemBar" + MID + END;
|
||||
public static final String SAFEPOINT = START + "SafePoint" + MID + END;
|
||||
|
||||
public static final String ABS_I = START + "AbsI" + MID + END;
|
||||
public static final String ABS_L = START + "AbsL" + MID + END;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user