hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/aot/ReplaceConstantNodesPhase.java
changeset 46536 79d8dffda212
parent 43972 1ade39b8381b
child 46566 231c681fa946
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/aot/ReplaceConstantNodesPhase.java	Tue Jun 13 07:30:11 2017 -0400
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/aot/ReplaceConstantNodesPhase.java	Tue Jun 13 09:19:35 2017 -0700
@@ -22,10 +22,12 @@
  */
 package org.graalvm.compiler.hotspot.phases.aot;
 
+import static org.graalvm.compiler.core.common.cfg.AbstractControlFlowGraph.strictlyDominates;
 import static org.graalvm.compiler.hotspot.nodes.aot.LoadMethodCountersNode.getLoadMethodCountersNodes;
 import static org.graalvm.compiler.nodes.ConstantNode.getConstantNodes;
 
 import java.util.HashSet;
+import java.util.List;
 
 import jdk.vm.ci.hotspot.HotSpotMetaspaceConstant;
 import jdk.vm.ci.hotspot.HotSpotObjectConstant;
@@ -35,11 +37,13 @@
 import jdk.vm.ci.meta.ConstantReflectionProvider;
 import jdk.vm.ci.meta.ResolvedJavaType;
 
+import org.graalvm.compiler.core.common.cfg.BlockMap;
 import org.graalvm.compiler.core.common.type.ObjectStamp;
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.Node;
+import org.graalvm.compiler.graph.NodeMap;
 import org.graalvm.compiler.hotspot.FingerprintUtil;
 import org.graalvm.compiler.hotspot.meta.HotSpotConstantLoadAction;
 import org.graalvm.compiler.hotspot.nodes.aot.InitializeKlassNode;
@@ -49,14 +53,21 @@
 import org.graalvm.compiler.hotspot.nodes.aot.ResolveConstantNode;
 import org.graalvm.compiler.hotspot.nodes.aot.ResolveMethodAndLoadCountersNode;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.FixedWithNextNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
+import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
 import org.graalvm.compiler.nodes.ValueNode;
+import org.graalvm.compiler.nodes.cfg.Block;
 import org.graalvm.compiler.phases.BasePhase;
+import org.graalvm.compiler.phases.schedule.SchedulePhase;
+import org.graalvm.compiler.phases.schedule.SchedulePhase.SchedulingStrategy;
 import org.graalvm.compiler.phases.tiers.PhaseContext;
+import org.graalvm.util.EconomicMap;
 
 public class ReplaceConstantNodesPhase extends BasePhase<PhaseContext> {
 
     private static final HashSet<Class<?>> builtIns = new HashSet<>();
+    private final boolean verifyFingerprints;
 
     static {
         builtIns.add(Boolean.class);
@@ -91,6 +102,14 @@
         // @formatter:on
     }
 
+    private static boolean anyUsagesNeedReplacement(ConstantNode node) {
+        return node.usages().filter(n -> !isReplacementNode(n)).isNotEmpty();
+    }
+
+    private static boolean anyUsagesNeedReplacement(LoadMethodCountersNode node) {
+        return node.usages().filter(n -> !(n instanceof ResolveMethodAndLoadCountersNode)).isNotEmpty();
+    }
+
     private static boolean checkForBadFingerprint(HotSpotResolvedJavaType type) {
         if (type.isArray()) {
             if (type.getElementalType().isPrimitive()) {
@@ -101,80 +120,223 @@
         return FingerprintUtil.getFingerprint((HotSpotResolvedObjectType) type) == 0;
     }
 
-    private static void handleHotSpotMetaspaceConstant(StructuredGraph graph, ConstantNode node) {
+    /**
+     * Replace {@link ConstantNode} containing a {@link HotSpotResolvedJavaType} with indirection.
+     *
+     * @param graph
+     * @param node {@link ConstantNode} containing a {@link HotSpotResolvedJavaType} that needs
+     *            resolution.
+     */
+    private void handleHotSpotMetaspaceConstant(StructuredGraph graph, ConstantNode node) {
         HotSpotMetaspaceConstant metaspaceConstant = (HotSpotMetaspaceConstant) node.asConstant();
         HotSpotResolvedJavaType type = (HotSpotResolvedJavaType) metaspaceConstant.asResolvedJavaType();
 
         if (type != null) {
-            if (checkForBadFingerprint(type)) {
+            if (verifyFingerprints && checkForBadFingerprint(type)) {
                 throw new GraalError("Type with bad fingerprint: " + type);
             }
-
             assert !metaspaceConstant.isCompressed() : "No support for replacing compressed metaspace constants";
-            ResolvedJavaType topMethodHolder = graph.method().getDeclaringClass();
-            ValueNode replacement;
-
-            if (type.isArray() && type.getComponentType().isPrimitive()) {
-                // Special case for primitive arrays. The AOT runtime pre-resolves them, so we may
-                // omit the resolution call.
-                replacement = new LoadConstantIndirectlyNode(node);
-            } else if (type.equals(topMethodHolder) || (type.isAssignableFrom(topMethodHolder) && !type.isInterface())) {
-                // If it's a supertype of or the same class that declares the top method, we are
-                // guaranteed to have it resolved already. If it's an interface, we just test for
-                // equality.
-                replacement = new LoadConstantIndirectlyNode(node);
-            } else if (builtIns.contains(type.mirror())) {
-                // Special case of klass constants that come from {@link BoxingSnippets}.
-                replacement = new ResolveConstantNode(node, HotSpotConstantLoadAction.INITIALIZE);
-            } else {
-                replacement = new ResolveConstantNode(node);
+            replaceWithInitialization(graph, node);
+            if (anyUsagesNeedReplacement(node)) {
+                replaceWithResolution(graph, node);
             }
-
-            node.replaceAtUsages(graph.addOrUnique(replacement), n -> !isReplacementNode(n));
         } else {
             throw new GraalError("Unsupported metaspace constant type: " + type);
         }
     }
 
+    /**
+     * Find the lowest dominating {@link FixedWithNextNode} before given node.
+     *
+     * @param graph
+     * @param node
+     * @return the last {@link FixedWithNextNode} that is scheduled before node.
+     */
+    private static FixedWithNextNode findFixedWithNextBefore(StructuredGraph graph, Node node) {
+        ScheduleResult schedule = graph.getLastSchedule();
+        NodeMap<Block> nodeToBlock = schedule.getNodeToBlockMap();
+        BlockMap<List<Node>> blockToNodes = schedule.getBlockToNodesMap();
+
+        Block block = nodeToBlock.get(node);
+        FixedWithNextNode result = null;
+        for (Node n : blockToNodes.get(block)) {
+            if (n instanceof FixedWithNextNode) {
+                result = (FixedWithNextNode) n;
+            }
+            if (n.equals(node)) {
+                break;
+            }
+        }
+        assert result != null;
+        return result;
+    }
+
+    /**
+     * Try to find dominating {@link InitializeKlassNode} that can be reused.
+     *
+     * @param graph
+     * @param node {@link ConstantNode} containing a {@link HotSpotResolvedJavaType} that needs
+     *            resolution.
+     */
+    private static void replaceWithInitialization(StructuredGraph graph, ConstantNode node) {
+        ScheduleResult schedule = graph.getLastSchedule();
+        NodeMap<Block> nodeToBlock = schedule.getNodeToBlockMap();
+        BlockMap<List<Node>> blockToNodes = schedule.getBlockToNodesMap();
+
+        EconomicMap<Block, Node> blockToInit = EconomicMap.create();
+        for (Node n : node.usages().filter(InitializeKlassNode.class)) {
+            blockToInit.put(nodeToBlock.get(n), n);
+        }
+        for (Node use : node.usages().filter(n -> !isReplacementNode(n)).snapshot()) {
+            boolean replaced = false;
+            Block b = nodeToBlock.get(use);
+            InitializeKlassNode i = (InitializeKlassNode) blockToInit.get(b);
+            if (i != null) {
+                // There is an initialization in the same block as the use, look if the use is
+                // scheduled after it.
+                for (Node n : blockToNodes.get(b)) {
+                    if (n.equals(use)) {
+                        // Usage is before initialization, can't use it
+                        break;
+                    }
+                    if (n.equals(i)) {
+                        use.replaceFirstInput(node, i);
+                        replaced = true;
+                        break;
+                    }
+                }
+            }
+            if (!replaced) {
+                // Look for dominating blocks that have initializations
+                for (Block d : blockToInit.getKeys()) {
+                    if (strictlyDominates(d, b)) {
+                        use.replaceFirstInput(node, blockToInit.get(d));
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Replace the uses of a constant with either {@link LoadConstantIndirectlyNode} or
+     * {@link ResolveConstantNode}.
+     *
+     * @param graph
+     * @param node {@link ConstantNode} containing a {@link HotSpotResolvedJavaType} that needs
+     *            resolution.
+     */
+    private static void replaceWithResolution(StructuredGraph graph, ConstantNode node) {
+        HotSpotMetaspaceConstant metaspaceConstant = (HotSpotMetaspaceConstant) node.asConstant();
+        HotSpotResolvedJavaType type = (HotSpotResolvedJavaType) metaspaceConstant.asResolvedJavaType();
+        ResolvedJavaType topMethodHolder = graph.method().getDeclaringClass();
+        ValueNode replacement;
+
+        if (type.isArray() && type.getComponentType().isPrimitive()) {
+            // Special case for primitive arrays. The AOT runtime pre-resolves them, so we may
+            // omit the resolution call.
+            replacement = graph.addOrUnique(new LoadConstantIndirectlyNode(node));
+        } else if (type.equals(topMethodHolder) || (type.isAssignableFrom(topMethodHolder) && !type.isInterface())) {
+            // If it's a supertype of or the same class that declares the top method, we are
+            // guaranteed to have it resolved already. If it's an interface, we just test for
+            // equality.
+            replacement = graph.addOrUnique(new LoadConstantIndirectlyNode(node));
+        } else {
+            FixedWithNextNode fixedReplacement;
+            if (builtIns.contains(type.mirror())) {
+                // Special case of klass constants that come from {@link BoxingSnippets}.
+                fixedReplacement = graph.add(new ResolveConstantNode(node, HotSpotConstantLoadAction.INITIALIZE));
+            } else {
+                fixedReplacement = graph.add(new ResolveConstantNode(node));
+            }
+            graph.addAfterFixed(findFixedWithNextBefore(graph, node), fixedReplacement);
+            replacement = fixedReplacement;
+        }
+        node.replaceAtUsages(replacement, n -> !isReplacementNode(n));
+    }
+
+    /**
+     * Replace an object constant with an indirect load {@link ResolveConstantNode}. Currently we
+     * support only strings.
+     *
+     * @param graph
+     * @param node {@link ConstantNode} containing a {@link HotSpotObjectConstant} that needs
+     *            resolution.
+     */
     private static void handleHotSpotObjectConstant(StructuredGraph graph, ConstantNode node) {
         HotSpotObjectConstant constant = (HotSpotObjectConstant) node.asJavaConstant();
         HotSpotResolvedJavaType type = (HotSpotResolvedJavaType) constant.getType();
         if (type.mirror().equals(String.class)) {
             assert !constant.isCompressed() : "No support for replacing compressed oop constants";
-            ValueNode replacement = graph.unique(new ResolveConstantNode(node));
+            FixedWithNextNode replacement = graph.add(new ResolveConstantNode(node));
+            graph.addAfterFixed(findFixedWithNextBefore(graph, node), replacement);
             node.replaceAtUsages(replacement, n -> !(n instanceof ResolveConstantNode));
         } else {
             throw new GraalError("Unsupported object constant type: " + type);
         }
     }
 
+    /**
+     * Replace {@link LoadMethodCountersNode} with indirect load
+     * {@link ResolveMethodAndLoadCountersNode}, expose a klass constant of the holder.
+     *
+     * @param graph
+     * @param node
+     * @param context
+     */
     private static void handleLoadMethodCounters(StructuredGraph graph, LoadMethodCountersNode node, PhaseContext context) {
         ResolvedJavaType type = node.getMethod().getDeclaringClass();
         Stamp hubStamp = context.getStampProvider().createHubStamp((ObjectStamp) StampFactory.objectNonNull());
         ConstantReflectionProvider constantReflection = context.getConstantReflection();
         ConstantNode klassHint = ConstantNode.forConstant(hubStamp, constantReflection.asObjectHub(type), context.getMetaAccess(), graph);
-        ValueNode replacement = graph.unique(new ResolveMethodAndLoadCountersNode(node.getMethod(), klassHint));
+        FixedWithNextNode replacement = graph.add(new ResolveMethodAndLoadCountersNode(node.getMethod(), klassHint));
+        graph.addAfterFixed(findFixedWithNextBefore(graph, node), replacement);
         node.replaceAtUsages(replacement, n -> !(n instanceof ResolveMethodAndLoadCountersNode));
     }
 
+    /**
+     * Replace {@link LoadMethodCountersNode} with {@link ResolveMethodAndLoadCountersNode}, expose
+     * klass constants.
+     *
+     * @param graph
+     * @param context
+     */
+    private static void replaceLoadMethodCounters(StructuredGraph graph, PhaseContext context) {
+        new SchedulePhase(SchedulingStrategy.LATEST_OUT_OF_LOOPS, true).apply(graph, false);
+        for (LoadMethodCountersNode node : getLoadMethodCountersNodes(graph)) {
+            if (anyUsagesNeedReplacement(node)) {
+                handleLoadMethodCounters(graph, node, context);
+            }
+        }
+    }
+
+    /**
+     * Replace object and klass constants with resolution nodes or reuse preceding initializations.
+     *
+     * @param graph
+     */
+    private void replaceKlassesAndObjects(StructuredGraph graph) {
+        new SchedulePhase(SchedulingStrategy.LATEST_OUT_OF_LOOPS, true).apply(graph, false);
+
+        for (ConstantNode node : getConstantNodes(graph)) {
+            Constant constant = node.asConstant();
+            if (constant instanceof HotSpotMetaspaceConstant && anyUsagesNeedReplacement(node)) {
+                handleHotSpotMetaspaceConstant(graph, node);
+            } else if (constant instanceof HotSpotObjectConstant && anyUsagesNeedReplacement(node)) {
+                handleHotSpotObjectConstant(graph, node);
+            }
+        }
+    }
+
     @Override
     protected void run(StructuredGraph graph, PhaseContext context) {
         // Replace LoadMethodCountersNode with ResolveMethodAndLoadCountersNode, expose klass
         // constants.
-        for (LoadMethodCountersNode node : getLoadMethodCountersNodes(graph)) {
-            handleLoadMethodCounters(graph, node, context);
-        }
+        replaceLoadMethodCounters(graph, context);
 
         // Replace object and klass constants (including the ones added in the previous pass) with
         // resolution nodes.
-        for (ConstantNode node : getConstantNodes(graph)) {
-            Constant constant = node.asConstant();
-            if (constant instanceof HotSpotMetaspaceConstant) {
-                handleHotSpotMetaspaceConstant(graph, node);
-            } else if (constant instanceof HotSpotObjectConstant) {
-                handleHotSpotObjectConstant(graph, node);
-            }
-        }
+        replaceKlassesAndObjects(graph);
     }
 
     @Override
@@ -182,4 +344,11 @@
         return false;
     }
 
+    public ReplaceConstantNodesPhase() {
+        this(true);
+    }
+
+    public ReplaceConstantNodesPhase(boolean verifyFingerprints) {
+        this.verifyFingerprints = verifyFingerprints;
+    }
 }