hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Node.java
changeset 46371 0337d0617e7b
parent 46344 694c102fd8ed
child 46393 d497d892ab11
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Node.java	Wed Apr 05 22:48:35 2017 +0000
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.graph/src/org/graalvm/compiler/graph/Node.java	Thu Apr 06 14:31:32 2017 -0700
@@ -40,6 +40,8 @@
 import java.util.function.Predicate;
 
 import org.graalvm.compiler.core.common.Fields;
+import org.graalvm.compiler.core.common.type.AbstractPointerStamp;
+import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.debug.DebugCloseable;
 import org.graalvm.compiler.debug.Fingerprint;
 import org.graalvm.compiler.graph.Graph.NodeEvent;
@@ -131,7 +133,9 @@
      * Denotes an injected parameter in a {@linkplain NodeIntrinsic node intrinsic} constructor. If
      * the constructor is called as part of node intrinsification, the node intrinsifier will inject
      * an argument for the annotated parameter. Injected parameters must precede all non-injected
-     * parameters in a constructor.
+     * parameters in a constructor. If the type of the annotated parameter is {@link Stamp}, the
+     * {@linkplain Stamp#javaType type} of the injected stamp is the return type of the annotated
+     * method (which cannot be {@code void}).
      */
     @java.lang.annotation.Retention(RetentionPolicy.RUNTIME)
     @java.lang.annotation.Target(ElementType.PARAMETER)
@@ -140,40 +144,45 @@
 
     /**
      * Annotates a method that can be replaced by a compiler intrinsic. A (resolved) call to the
-     * annotated method can be replaced with an instance of the node class denoted by
-     * {@link #value()}. For this reason, the signature of the annotated method must match the
-     * signature (excluding a prefix of {@linkplain InjectedNodeParameter injected} parameters) of a
-     * constructor in the node class.
+     * annotated method will be processed by a generated {@code InvocationPlugin} that calls either
+     * a factory method or a constructor corresponding with the annotated method.
      * <p>
-     * If the node class has a static method {@code intrinsify} with a matching signature plus a
-     * {@code GraphBuilderContext} as first argument, this method is called instead of creating the
-     * node.
+     * A factory method corresponding to an annotated method is a static method named
+     * {@code intrinsify} defined in the class denoted by {@link #value()}. In order, its signature
+     * is as follows:
+     * <ol>
+     * <li>A {@code GraphBuilderContext} parameter.</li>
+     * <li>A {@code ResolvedJavaMethod} parameter.</li>
+     * <li>A sequence of zero or more {@linkplain InjectedNodeParameter injected} parameters.</li>
+     * <li>Remaining parameters that match the declared parameters of the annotated method.</li>
+     * </ol>
+     * A constructor corresponding to an annotated method is defined in the class denoted by
+     * {@link #value()}. In order, its signature is as follows:
+     * <ol>
+     * <li>A sequence of zero or more {@linkplain InjectedNodeParameter injected} parameters.</li>
+     * <li>Remaining parameters that match the declared parameters of the annotated method.</li>
+     * </ol>
+     * There must be exactly one such factory method or constructor corresponding to a
+     * {@link NodeIntrinsic} annotated method.
      */
     @java.lang.annotation.Retention(RetentionPolicy.RUNTIME)
     @java.lang.annotation.Target(ElementType.METHOD)
     public static @interface NodeIntrinsic {
 
         /**
-         * Gets the {@link Node} subclass instantiated when intrinsifying a call to the annotated
-         * method. If not specified, then the class in which the annotated method is declared is
-         * used (and is assumed to be a {@link Node} subclass).
+         * The class declaring the factory method or {@link Node} subclass declaring the constructor
+         * used to intrinsify a call to the annotated method. The default value is the class in
+         * which the annotated method is declared.
          */
         Class<?> value() default NodeIntrinsic.class;
 
         /**
-         * Determines if the stamp of the instantiated intrinsic node has its stamp set from the
-         * return type of the annotated method.
-         * <p>
-         * When it is set to true, the stamp that is passed in to the constructor of ValueNode is
-         * ignored and can therefore safely be {@code null}.
+         * If {@code true}, the factory method or constructor selected by the annotation must have
+         * an {@linkplain InjectedNodeParameter injected} {@link Stamp} parameter. Calling
+         * {@link AbstractPointerStamp#nonNull()} on the injected stamp is guaranteed to return
+         * {@code true}.
          */
-        boolean setStampFromReturnType() default false;
-
-        /**
-         * Determines if the stamp of the instantiated intrinsic node is guaranteed to be non-null.
-         * Generally used in conjunction with {@link #setStampFromReturnType()}.
-         */
-        boolean returnStampIsNonNull() default false;
+        boolean injectedStampIsNonNull() default false;
     }
 
     /**
@@ -304,7 +313,7 @@
      * @return an {@link NodeIterable iterable} for all non-null successor edges.
      */
     public NodeIterable<Node> successors() {
-        assert !this.isDeleted();
+        assert !this.isDeleted() : this;
         return nodeClass.getSuccessorIterable(this);
     }
 
@@ -328,7 +337,7 @@
         if (usage1 == null) {
             return 1;
         }
-        return 2 + extraUsagesCount;
+        return INLINE_USAGE_COUNT + extraUsagesCount;
     }
 
     /**
@@ -391,30 +400,45 @@
     }
 
     private void movUsageFromEndTo(int destIndex) {
-        int lastIndex = this.getUsageCount() - 1;
-        if (destIndex == 0) {
-            if (lastIndex == 0) {
-                usage0 = null;
-                return;
-            } else if (lastIndex == 1) {
-                usage0 = usage1;
-                usage1 = null;
-                return;
-            } else {
-                usage0 = extraUsages[lastIndex - INLINE_USAGE_COUNT];
-            }
+        if (destIndex >= INLINE_USAGE_COUNT) {
+            movUsageFromEndToExtraUsages(destIndex - INLINE_USAGE_COUNT);
         } else if (destIndex == 1) {
-            if (lastIndex == 1) {
-                usage1 = null;
-                return;
-            }
-            usage1 = extraUsages[lastIndex - INLINE_USAGE_COUNT];
+            movUsageFromEndToIndexOne();
         } else {
-            Node n = extraUsages[lastIndex - INLINE_USAGE_COUNT];
-            extraUsages[destIndex - INLINE_USAGE_COUNT] = n;
+            assert destIndex == 0;
+            movUsageFromEndToIndexZero();
         }
-        extraUsages[lastIndex - INLINE_USAGE_COUNT] = null;
+    }
+
+    private void movUsageFromEndToExtraUsages(int destExtraIndex) {
         this.extraUsagesCount--;
+        Node n = extraUsages[extraUsagesCount];
+        extraUsages[destExtraIndex] = n;
+        extraUsages[extraUsagesCount] = null;
+    }
+
+    private void movUsageFromEndToIndexZero() {
+        if (extraUsagesCount > 0) {
+            this.extraUsagesCount--;
+            usage0 = extraUsages[extraUsagesCount];
+            extraUsages[extraUsagesCount] = null;
+        } else if (usage1 != null) {
+            usage0 = usage1;
+            usage1 = null;
+        } else {
+            usage0 = null;
+        }
+    }
+
+    private void movUsageFromEndToIndexOne() {
+        if (extraUsagesCount > 0) {
+            this.extraUsagesCount--;
+            usage1 = extraUsages[extraUsagesCount];
+            extraUsages[extraUsagesCount] = null;
+        } else {
+            assert usage1 != null;
+            usage1 = null;
+        }
     }
 
     /**
@@ -425,20 +449,21 @@
      */
     public boolean removeUsage(Node node) {
         assert node != null;
-        // It is critical that this method maintains the invariant that
-        // the usage list has no null element preceding a non-null element
+        // For large graphs, usage removal is performance critical.
+        // Furthermore, it is critical that this method maintains the invariant that the usage list
+        // has no null element preceding a non-null element.
         incUsageModCount();
         if (usage0 == node) {
-            this.movUsageFromEndTo(0);
+            movUsageFromEndToIndexZero();
             return true;
         }
         if (usage1 == node) {
-            this.movUsageFromEndTo(1);
+            movUsageFromEndToIndexOne();
             return true;
         }
         for (int i = this.extraUsagesCount - 1; i >= 0; i--) {
             if (extraUsages[i] == node) {
-                this.movUsageFromEndTo(i + INLINE_USAGE_COUNT);
+                movUsageFromEndToExtraUsages(i);
                 return true;
             }
         }
@@ -537,8 +562,9 @@
         assert assertTrue(id == INITIAL_ID, "unexpected id: %d", id);
         this.graph = newGraph;
         newGraph.register(this);
-        this.getNodeClass().registerAtInputsAsUsage(this);
-        this.getNodeClass().registerAtSuccessorsAsPredecessor(this);
+        NodeClass<? extends Node> nc = nodeClass;
+        nc.registerAtInputsAsUsage(this);
+        nc.registerAtSuccessorsAsPredecessor(this);
     }
 
     /**
@@ -588,7 +614,7 @@
     }
 
     public final void replaceAtUsages(Node other) {
-        replaceAtUsages(other, null, null);
+        replaceAtAllUsages(other, (Node) null);
     }
 
     public final void replaceAtUsages(Node other, Predicate<Node> filter) {
@@ -606,22 +632,60 @@
     }
 
     protected void replaceAtUsages(Node other, Predicate<Node> filter, Node toBeDeleted) {
+        if (filter == null) {
+            replaceAtAllUsages(other, toBeDeleted);
+        } else {
+            replaceAtMatchingUsages(other, filter, toBeDeleted);
+        }
+    }
+
+    protected void replaceAtAllUsages(Node other, Node toBeDeleted) {
+        assert checkReplaceWith(other);
+        if (usage0 == null) {
+            return;
+        }
+        replaceAtUsage(other, toBeDeleted, usage0);
+        usage0 = null;
+
+        if (usage1 == null) {
+            return;
+        }
+        replaceAtUsage(other, toBeDeleted, usage1);
+        usage1 = null;
+
+        if (extraUsagesCount <= 0) {
+            return;
+        }
+        for (int i = 0; i < extraUsagesCount; i++) {
+            Node usage = extraUsages[i];
+            replaceAtUsage(other, toBeDeleted, usage);
+        }
+        this.extraUsages = NO_NODES;
+        this.extraUsagesCount = 0;
+    }
+
+    private void replaceAtUsage(Node other, Node toBeDeleted, Node usage) {
+        boolean result = usage.getNodeClass().replaceFirstInput(usage, this, other);
+        assert assertTrue(result, "not found in inputs, usage: %s", usage);
+        /*
+         * Don't notify for nodes which are about to be deleted.
+         */
+        if (toBeDeleted == null || usage != toBeDeleted) {
+            maybeNotifyInputChanged(usage);
+        }
+        if (other != null) {
+            other.addUsage(usage);
+        }
+    }
+
+    private void replaceAtMatchingUsages(Node other, Predicate<Node> filter, Node toBeDeleted) {
+        assert filter != null;
         assert checkReplaceWith(other);
         int i = 0;
         while (i < this.getUsageCount()) {
             Node usage = this.getUsageAt(i);
             if (filter == null || filter.test(usage)) {
-                boolean result = usage.getNodeClass().replaceFirstInput(usage, this, other);
-                assert assertTrue(result, "not found in inputs, usage: %s", usage);
-                /*
-                 * Don't notify for nodes which are about to be deleted.
-                 */
-                if (toBeDeleted == null || usage != toBeDeleted) {
-                    maybeNotifyInputChanged(usage);
-                }
-                if (other != null) {
-                    other.addUsage(usage);
-                }
+                replaceAtUsage(other, toBeDeleted, usage);
                 this.movUsageFromEndTo(i);
             } else {
                 ++i;
@@ -641,21 +705,7 @@
 
     public void replaceAtMatchingUsages(Node other, NodePredicate usagePredicate) {
         assert checkReplaceWith(other);
-        int index = 0;
-        while (index < this.getUsageCount()) {
-            Node usage = getUsageAt(index);
-            if (usagePredicate.apply(usage)) {
-                boolean result = usage.getNodeClass().replaceFirstInput(usage, this, other);
-                assert assertTrue(result, "not found in inputs, usage: %s", usage);
-                if (other != null) {
-                    maybeNotifyInputChanged(usage);
-                    other.addUsage(usage);
-                }
-                this.movUsageFromEndTo(index);
-            } else {
-                index++;
-            }
-        }
+        replaceAtMatchingUsages(other, usagePredicate, null);
     }
 
     public void replaceAtUsages(InputType type, Node other) {