Skip to content

Commit 576b090

Browse files
committed
Merge remote-tracking branch 'origin/master' into vj/GR-69858-reflection-sec-providers
2 parents 418cca3 + f35d2ea commit 576b090

2 files changed

Lines changed: 135 additions & 0 deletions

File tree

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package jdk.graal.compiler.nodes.test;
26+
27+
import org.junit.Test;
28+
29+
import jdk.graal.compiler.api.directives.GraalDirectives;
30+
import jdk.graal.compiler.core.test.GraalCompilerTest;
31+
32+
/**
33+
* Tests simplification of an {@code IfNode} after a merge when one successor is a loop exit.
34+
*/
35+
public class SplitIfWithLoopExitTest extends GraalCompilerTest {
36+
37+
private static final Object FIRST = new Object();
38+
private static final Object SECOND = new Object();
39+
40+
/* The condition has an additional usage whose result is needed on both loop paths. The transformation is rejected. */
41+
public static int sharedConditionSnippet(boolean selectFirst) {
42+
int result = 0;
43+
int i = 0;
44+
while (i < 2) {
45+
Object selected = selectFirst ? FIRST : SECOND;
46+
boolean isFirst = selected == FIRST;
47+
result = isFirst ? 1 : 2;
48+
if (!isFirst) {
49+
break;
50+
}
51+
GraalDirectives.controlFlowAnchor();
52+
i++;
53+
}
54+
return result;
55+
}
56+
57+
@Test
58+
public void testSharedCondition() {
59+
test("sharedConditionSnippet", true);
60+
test("sharedConditionSnippet", false);
61+
}
62+
63+
/* The condition has an additional usage confined to the loop body. The usage is specialized to true. */
64+
public static int attributableConditionSnippet(boolean selectFirst) {
65+
int i = 0;
66+
while (i < 2) {
67+
Object selected = selectFirst ? FIRST : SECOND;
68+
boolean isFirst = selected == FIRST;
69+
if (!isFirst) {
70+
break;
71+
}
72+
GraalDirectives.blackhole(isFirst);
73+
GraalDirectives.controlFlowAnchor();
74+
i++;
75+
}
76+
return i;
77+
}
78+
79+
@Test
80+
public void testAttributableCondition() {
81+
test("attributableConditionSnippet", true);
82+
test("attributableConditionSnippet", false);
83+
}
84+
85+
/* The condition has an additional usage confined to the loop exit. The usage is specialized to false. */
86+
public static int exitConditionSnippet(boolean selectFirst) {
87+
int result = 0;
88+
int i = 0;
89+
while (i < 2) {
90+
Object selected = selectFirst ? FIRST : SECOND;
91+
boolean isFirst = selected == FIRST;
92+
if (!isFirst) {
93+
result = isFirst ? 1 : 2;
94+
break;
95+
}
96+
GraalDirectives.controlFlowAnchor();
97+
i++;
98+
}
99+
return result;
100+
}
101+
102+
@Test
103+
public void testExitCondition() {
104+
test("exitConditionSnippet", true);
105+
test("exitConditionSnippet", false);
106+
}
107+
}

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/IfNode.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,16 @@ private boolean splitIfWithLoopExit(IfNode ifNode, SimplifierTool tool) {
493493
*/
494494
coloredNodes.put(loopExitNode.loopBegin(), loopBodyBranch);
495495

496+
/* Additional condition usages must be attributable to a branch so they can be specialized. */
497+
for (Node usage : ifNode.condition().usages()) {
498+
if (usage != ifNode) {
499+
NodeColor color = colorUsage(coloredNodes, usage, merge, ifNode.trueSuccessor(), ifNode.falseSuccessor());
500+
if (color != NodeColor.TRUE_BRANCH && color != NodeColor.FALSE_BRANCH) {
501+
return false;
502+
}
503+
}
504+
}
505+
496506
EconomicMap<ValuePhiNode, ValueNode> phiReplacements = EconomicMap.create(merge.phis().count());
497507

498508
for (PhiNode existingPhi : merge.phis()) {
@@ -507,6 +517,7 @@ private boolean splitIfWithLoopExit(IfNode ifNode, SimplifierTool tool) {
507517
}
508518

509519
// Start modifying the graph
520+
specializeConditionUsages(ifNode, coloredNodes);
510521
ifNode.graph().removeSplit(ifNode, survivingSuccessor);
511522

512523
for (ValuePhiNode existingPhi : phiReplacements.getKeys()) {
@@ -527,6 +538,23 @@ private boolean splitIfWithLoopExit(IfNode ifNode, SimplifierTool tool) {
527538
return true;
528539
}
529540

541+
/**
542+
* Replaces additional usages of the split condition with its result on their branch.
543+
*/
544+
private static void specializeConditionUsages(IfNode ifNode, EconomicMap<Node, NodeColor> coloredNodes) {
545+
LogicNode condition = ifNode.condition();
546+
LogicConstantNode trueValue = LogicConstantNode.tautology(ifNode.graph());
547+
LogicConstantNode falseValue = LogicConstantNode.contradiction(ifNode.graph());
548+
for (Node usage : condition.usages().snapshot()) {
549+
if (usage == ifNode) {
550+
continue;
551+
}
552+
NodeColor color = coloredNodes.get(usage);
553+
GraalError.guarantee(color == NodeColor.TRUE_BRANCH || color == NodeColor.FALSE_BRANCH, "Unexpected color %s for %s", color, usage);
554+
usage.replaceAllInputs(condition, color == NodeColor.TRUE_BRANCH ? trueValue : falseValue);
555+
}
556+
}
557+
530558
/**
531559
* Propagates the branch profile of an eliminated {@code IfNode} to its preceding {@code IfNode}
532560
* when one of the successors is a loop exit. This ensures that the original loop frequency is

0 commit comments

Comments
 (0)