< prev index next >
src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/cfg/Loop.java
Print this page
@@ -23,23 +23,32 @@
package org.graalvm.compiler.core.common.cfg;
+import static org.graalvm.compiler.core.common.cfg.AbstractBlockBase.BLOCK_ID_COMPARATOR;
+
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
public abstract class Loop<T extends AbstractBlockBase<T>> {
private final Loop<T> parent;
- private final List<Loop<T>> children;
+ private final ArrayList<Loop<T>> children;
private final int depth;
private final int index;
private final T header;
- private final List<T> blocks;
- private final List<T> exits;
+ private final ArrayList<T> blocks;
+ private final ArrayList<T> exits;
+ /**
+ * Natural exits, ignoring LoopExitNodes.
+ *
+ * @see #getNaturalExits()
+ */
+ private final ArrayList<T> naturalExits;
protected Loop(Loop<T> parent, int index, T header) {
this.parent = parent;
if (parent != null) {
this.depth = parent.getDepth() + 1;
@@ -49,10 +58,11 @@
this.index = index;
this.header = header;
this.blocks = new ArrayList<>();
this.children = new ArrayList<>();
this.exits = new ArrayList<>();
+ this.naturalExits = new ArrayList<>();
}
public abstract long numBackedges();
@Override
@@ -82,16 +92,91 @@
public List<T> getBlocks() {
return blocks;
}
- public List<T> getExits() {
+ /**
+ * Returns the loop exits.
+ *
+ * This might be a conservative set: before framestate assignment it matches the LoopExitNodes
+ * even if earlier blocks could be considered as exits. After framestate assignments, this is
+ * the same as {@link #getNaturalExits()}.
+ *
+ * <p>
+ * LoopExitNodes are inserted in the control-flow during parsing and are natural exits: they are
+ * the earliest block at which we are guaranteed to have exited the loop. However, after some
+ * transformations of the graph, the natural exit might go up but the LoopExitNodes are not
+ * updated.
+ * </p>
+ *
+ * <p>
+ * For example in:
+ *
+ * <pre>
+ * for (int i = 0; i < N; i++) {
+ * if (c) {
+ * // Block 1
+ * if (dummy) {
+ * // ...
+ * } else {
+ * // ...
+ * }
+ * if (b) {
+ * continue;
+ * } else {
+ * // Block 2
+ * // LoopExitNode
+ * break;
+ * }
+ * }
+ * }
+ * </pre>
+ *
+ * After parsing, the natural exits match the LoopExitNode: Block 2 is a natural exit and has a
+ * LoopExitNode. If the {@code b} condition gets canonicalized to {@code false}, the natural
+ * exit moves to Block 1 while the LoopExitNode remains in Block 2. In such a scenario,
+ * {@code getLoopExits()} will contain block 2 while {@link #getNaturalExits()} will contain
+ * block 1.
+ *
+ *
+ * @see #getNaturalExits()
+ */
+ public List<T> getLoopExits() {
return exits;
}
- public void addExit(T t) {
- exits.add(t);
+ public boolean isLoopExit(T block) {
+ assert isSorted(exits);
+ return Collections.binarySearch(exits, block, BLOCK_ID_COMPARATOR) >= 0;
+ }
+
+ /**
+ * Returns the natural exit points: these are the earliest block that are guaranteed to never
+ * reach a back-edge.
+ *
+ * This can not be used in the context of preserving or using loop-closed form.
+ *
+ * @see #getLoopExits()
+ */
+ public ArrayList<T> getNaturalExits() {
+ return naturalExits;
+ }
+
+ public boolean isNaturalExit(T block) {
+ assert isSorted(naturalExits);
+ return Collections.binarySearch(naturalExits, block, BLOCK_ID_COMPARATOR) >= 0;
+ }
+
+ private static <T extends AbstractBlockBase<T>> boolean isSorted(List<T> list) {
+ int lastId = Integer.MIN_VALUE;
+ for (AbstractBlockBase<?> block : list) {
+ if (block.getId() < lastId) {
+ return false;
+ }
+ lastId = block.getId();
+ }
+ return true;
}
/**
* Determines if one loop is a transitive parent of another loop.
*
@@ -113,6 +198,11 @@
@Override
public int hashCode() {
return index + depth * 31;
}
+
+ @Override
+ public boolean equals(Object obj) {
+ return super.equals(obj);
+ }
}
< prev index next >