Skip to content

[GR-66216] [GR-66435] Improve loop explosion performance during partial evaluation #11459

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2025, 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
Expand Down Expand Up @@ -84,6 +84,12 @@ private enum FreezeState {
public final boolean verifyGraphs;
public final boolean verifyGraphEdges;

/**
* Cached actual value of {@link GraalOptions#TrackNodeInsertion} to avoid expensive map lookup
* every time a node is registered.
*/
public final boolean trackNodeInsertion;

/**
* The set of nodes in the graph, ordered by {@linkplain #register(Node) registration} time.
*/
Expand Down Expand Up @@ -329,6 +335,8 @@ public Graph(String name, OptionValues options, DebugContext debug, boolean trac

verifyGraphs = Options.VerifyGraalGraphs.getValue(options);
verifyGraphEdges = Options.VerifyGraalGraphEdges.getValue(options);

trackNodeInsertion = TrackNodeInsertion.getValue(options);
}

int extractOriginalNodeId(Node node) {
Expand Down Expand Up @@ -1311,7 +1319,7 @@ void register(Node node) {
if (currentNodeSourcePosition != null && trackNodeSourcePosition()) {
node.setNodeSourcePosition(currentNodeSourcePosition);
}
if (TrackNodeInsertion.getValue(getOptions()) && node.getInsertionPosition() == null) {
if (trackNodeInsertion && node.getInsertionPosition() == null) {
node.setInsertionPosition(new NodeInsertionStackTrace());
}

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2025, 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
Expand Down Expand Up @@ -38,6 +38,7 @@
import jdk.graal.compiler.debug.TimerKey;
import jdk.graal.compiler.graph.Edges;
import jdk.graal.compiler.graph.Node;
import jdk.graal.compiler.graph.NodeBitMap;
import jdk.graal.compiler.graph.NodeClass;
import jdk.graal.compiler.nodeinfo.InputType;
import jdk.graal.compiler.nodeinfo.NodeInfo;
Expand Down Expand Up @@ -149,22 +150,30 @@ protected void cleanupGraph(MethodScope methodScope) {
GraphUtil.normalizeLoops(graph);
super.cleanupGraph(methodScope);

/*
* To avoid calling tryKillUnused for each individual floating node we remove during
* cleanup, we maintain unused nodes in a bitmap and kill them after we finish iterating the
* new nodes in the graph.
*/
final NodeBitMap unusedNodes = new NodeBitMap(graph);

for (Node node : graph.getNewNodes(methodScope.methodStartMark)) {
if (node instanceof MergeNode) {
MergeNode mergeNode = (MergeNode) node;
if (node instanceof MergeNode mergeNode) {
if (mergeNode.forwardEndCount() == 1) {
graph.reduceTrivialMerge(mergeNode);
graph.reduceTrivialMerge(mergeNode, false, unusedNodes);
}
} else if (node instanceof BeginNode) {
if (!(node.predecessor() instanceof ControlSplitNode) && node.hasNoUsages()) {
GraphUtil.unlinkFixedNode((AbstractBeginNode) node);
node.safeDelete();
}
} else if (GraphUtil.shouldKillUnused(node)) {
unusedNodes.mark(node);
}
}

for (Node node : graph.getNewNodes(methodScope.methodStartMark)) {
GraphUtil.tryKillUnused(node);
if (unusedNodes.isNotEmpty()) {
GraphUtil.killAllWithUnusedFloatingInputs(unusedNodes, false);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,10 @@
import jdk.graal.compiler.debug.TTY;
import jdk.graal.compiler.graph.Graph;
import jdk.graal.compiler.graph.Node;
import jdk.graal.compiler.graph.NodeBitMap;
import jdk.graal.compiler.graph.NodeMap;
import jdk.graal.compiler.graph.NodeSourcePosition;
import jdk.graal.compiler.graph.iterators.NodeIterable;
import jdk.graal.compiler.nodes.GraphState.StageFlag;
import jdk.graal.compiler.nodes.calc.FloatingNode;
import jdk.graal.compiler.nodes.cfg.ControlFlowGraph;
Expand Down Expand Up @@ -1049,8 +1051,21 @@ public void reduceTrivialMerge(AbstractMergeNode merge) {
reduceTrivialMerge(merge, false);
}

@SuppressWarnings("static-method")
public void reduceTrivialMerge(AbstractMergeNode merge, boolean forKillCFG) {
reduceTrivialMerge(merge, forKillCFG, null);
}

/**
* Removes merges with phis that have a single input value.
*
* @param unusedNodes A set to mark unused nodes in the graph that should be killed later by the
* caller. The set should be used when calling this method for multiple merges. The
* resulting set must be killed with
* {@link GraphUtil#killAllWithUnusedFloatingInputs(NodeIterable, boolean)}. If this
* set is null, the nodes are killed immediately.
*/
@SuppressWarnings("static-method")
public void reduceTrivialMerge(AbstractMergeNode merge, boolean forKillCFG, NodeBitMap unusedNodes) {
assert merge.forwardEndCount() == 1 : Assertions.errorMessageContext("merge", merge);
assert !(merge instanceof LoopBeginNode) || ((LoopBeginNode) merge).loopEnds().isEmpty();
for (PhiNode phi : merge.phis().snapshot()) {
Expand All @@ -1061,7 +1076,11 @@ public void reduceTrivialMerge(AbstractMergeNode merge, boolean forKillCFG) {
} else {
phi.safeDelete();
if (singleValue != null) {
GraphUtil.tryKillUnused(singleValue);
if (unusedNodes == null) {
GraphUtil.tryKillUnused(singleValue);
} else if (GraphUtil.shouldKillUnused(singleValue)) {
unusedNodes.mark(singleValue);
}
}
}
}
Expand All @@ -1076,7 +1095,11 @@ public void reduceTrivialMerge(AbstractMergeNode merge, boolean forKillCFG) {
merge.prepareDelete((FixedNode) singleEnd.predecessor());
merge.safeDelete();
if (stateAfter != null) {
GraphUtil.tryKillUnused(stateAfter);
if (unusedNodes == null) {
GraphUtil.tryKillUnused(stateAfter);
} else if (GraphUtil.shouldKillUnused(stateAfter)) {
unusedNodes.mark(stateAfter);
}
}
if (sux == null) {
singleEnd.replaceAtPredecessor(null);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2025, 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
Expand Down Expand Up @@ -1075,13 +1075,17 @@ private static ValueNode originalValueForComplicatedPhi(ValueNode value, PhiNode
}

public static boolean tryKillUnused(Node node) {
if (node.isAlive() && isFloatingNode(node) && node.hasNoUsages() && !(node instanceof GuardNode)) {
if (shouldKillUnused(node)) {
killWithUnusedFloatingInputs(node);
return true;
}
return false;
}

public static boolean shouldKillUnused(Node node) {
return node.isAlive() && isFloatingNode(node) && node.hasNoUsages() && !(node instanceof GuardNode);
}

/**
* Returns an iterator that will return the given node followed by all its predecessors, up
* until the point where {@link Node#predecessor()} returns null.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2025, 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
Expand Down Expand Up @@ -1279,9 +1279,7 @@ protected LoopScope doInline(PEMethodScope methodScope, LoopScope loopScope, Inv
* ParameterNodes.
*/
int firstArgumentNodeId = inlineScope.maxFixedNodeOrderId + 1;
for (int i = 0; i < arguments.length; i++) {
inlineLoopScope.createdNodes[firstArgumentNodeId + i] = arguments[i];
}
inlineLoopScope.setNodes(firstArgumentNodeId, arguments);

// Copy inlined methods from inlinee to caller
recordGraphElements(graphToInline);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright (c) 2025, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 jdk.graal.compiler.util;

import java.util.Arrays;

/**
* Utility class for building dynamically sized int arrays.
*/
public class IntArrayBuilder {
private static final int[] EMPTY_INT_ARRAY = {};

private int[] data;
private int size;

public IntArrayBuilder() {
this.data = new int[1];
this.size = 0;
}

private void ensureCapacity(int expectedSize) {
if (expectedSize >= data.length) {
final int nSize = Integer.highestOneBit(expectedSize) << 1;
final int[] nData = new int[nSize];
System.arraycopy(data, 0, nData, 0, size);
data = nData;
}
}

public void set(int index, int value) {
ensureCapacity(index);
data[index] = value;
size = Math.max(size, index + 1);
}

public void add(int value) {
ensureCapacity(size);
data[size] = value;
size++;
}

/**
* @return The int array generated by the builder. May return the underlying int array or a
* correctly sized copy.
*/
public int[] toArray() {
if (size == 0) {
return EMPTY_INT_ARRAY;
} else if (size == data.length) {
return data;
}
return Arrays.copyOf(data, size);
}
}