changeset 17040:df448ee85279

added new version of iteration for Node inputs and successors that allows the iterators to be escape analyzed at the cost of extra polymorphism when accessing Node and NodeList fields during iteration
author Doug Simon <doug.simon@oracle.com>
date Wed, 03 Sep 2014 16:25:24 +0200
parents 1e542561783e
children 0bf917d4d061
files graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeAllRefsIterator.java graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeRefIterable.java graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeRefIterator.java graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeRefWithModCountIterator.java graal/com.oracle.graal.nodeinfo.processor/src/com/oracle/graal/nodeinfo/processor/GraphNodeGenerator.java
diffstat 6 files changed, 464 insertions(+), 44 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java	Wed Sep 03 14:58:53 2014 +0200
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java	Wed Sep 03 16:25:24 2014 +0200
@@ -189,6 +189,8 @@
         return graph;
     }
 
+    private static final boolean USE_GENERATED_NODE_ITERATORS = Boolean.getBoolean("graal.useGeneratedNodeIterators");
+
     /**
      * Returns an {@link NodeClassIterable iterable} which can be used to traverse all non-null
      * input edges of this node.
@@ -197,6 +199,9 @@
      */
     public NodeClassIterable inputs() {
         if (USE_GENERATED_NODES) {
+            if (!USE_GENERATED_NODE_ITERATORS) {
+                return new NodeRefIterable(this, true);
+            }
             return inputsV2();
         }
         return getNodeClass().getInputIterable(this);
@@ -210,6 +215,9 @@
      */
     public NodeClassIterable successors() {
         if (USE_GENERATED_NODES) {
+            if (!USE_GENERATED_NODE_ITERATORS) {
+                return new NodeRefIterable(this, false);
+            }
             return successorsV2();
         }
         return getNodeClass().getSuccessorIterable(this);
@@ -1034,6 +1042,24 @@
         return NodeClassIterable.Empty;
     }
 
+    /**
+     * Determines if this node's inputs contain a given node.
+     *
+     * @param other
+     */
+    public boolean inputsContains(Node other) {
+        return false;
+    }
+
+    /**
+     * Determines if this node's successors contain a given node.
+     *
+     * @param other
+     */
+    public boolean successorsContains(Node other) {
+        return false;
+    }
+
     public NodeClassIterable successorsV2() {
         return NodeClassIterable.Empty;
     }
@@ -1056,6 +1082,68 @@
     }
 
     /**
+     * Gets the number of {@link Node} and {@link NodeList} fields in this node that are
+     * {@link Input}s or {@link OptionalInput}s.
+     *
+     * @return {@code L << 16 | N} where {@code N} is the number of {@link Node} fields and
+     *         {@code L} is the number of {@link NodeList} fields
+     */
+    public int getInputsCount() {
+        return 0;
+    }
+
+    /**
+     * Gets the number of {@link Node} and {@link NodeList} fields in this node that are
+     * {@link Successor}s.
+     *
+     * @return {@code L << 16 | N} where {@code N} is the number of {@link Node} fields and
+     *         {@code L} is the number of {@link NodeList} fields
+     */
+    public int getSuccessorsCount() {
+        return 0;
+    }
+
+    /**
+     * Gets an input of this node at a given index.
+     *
+     * @param index index of an input {@link Node} field. This value must be in the range of the
+     *            number of {@link Node} fields returned by {@link #getInputsCount()}.
+     */
+    public Node getInputNodeAt(int index) {
+        throw new NoSuchElementException();
+    }
+
+    /**
+     * Gets a successor of this node at a given index.
+     *
+     * @param index index of a successor {@link Node} field. This value must be in the range of the
+     *            number of {@link Node} fields returned by {@link #getSuccessorsCount()}.
+     */
+    public Node getSuccessorNodeAt(int index) {
+        throw new NoSuchElementException();
+    }
+
+    /**
+     * Gets an input list at a given index.
+     *
+     * @param index index of an input {@link NodeList} field. This value must be in the range of the
+     *            number of {@link NodeList} fields returned by {@link #getInputsCount()}.
+     */
+    public NodeList<? extends Node> getInputNodeListAt(int index) {
+        throw new NoSuchElementException();
+    }
+
+    /**
+     * Gets a successor list at a given index.
+     *
+     * @param index index of a successor {@link NodeList} field. This value must be in the range of
+     *            the number of {@link NodeList} fields returned by {@link #getSuccessorsCount()}.
+     */
+    public NodeList<? extends Node> getSuccessorNodeListAt(int index) {
+        throw new NoSuchElementException();
+    }
+
+    /**
      * Gets an input or successor list at a given position.
      *
      * @param position
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeAllRefsIterator.java	Wed Sep 03 16:25:24 2014 +0200
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ * 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 com.oracle.graal.graph;
+
+import com.oracle.graal.graph.Node.Input;
+
+/**
+ * An iterator over the references to a given {@link Node}'s {@linkplain Input inputs}.
+ *
+ * An iterator of this type will return null values.
+ */
+public final class NodeAllRefsIterator extends NodeRefIterator {
+
+    public NodeAllRefsIterator(Node node, int nodeFields, int nodeListFields, boolean isInputs) {
+        super(node, nodeFields, nodeListFields, isInputs);
+        forward();
+    }
+
+    @Override
+    protected void forward() {
+        if (index < nodeFields) {
+            index++;
+            if (index < nodeFields) {
+                return;
+            }
+        } else {
+            subIndex++;
+        }
+
+        while (index < allNodeRefFields) {
+            if (subIndex == 0) {
+                list = getNodeList(index - nodeFields);
+            }
+            if (subIndex < list.size()) {
+                return;
+            }
+            subIndex = 0;
+            index++;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeRefIterable.java	Wed Sep 03 16:25:24 2014 +0200
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ * 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 com.oracle.graal.graph;
+
+import static com.oracle.graal.graph.Graph.*;
+
+import com.oracle.graal.graph.Node.*;
+
+/**
+ * An iterator over the references to a given {@link Node}'s {@linkplain Input inputs} or
+ * {@linkplain Successor successors}.
+ */
+public class NodeRefIterable implements NodeClassIterable {
+
+    protected final Node node;
+
+    /**
+     * Specifies if {@link #iterator()} and {@link #withNullIterator()} iterate over
+     * {@linkplain Input inputs} or {@linkplain Successor successors}.
+     */
+    protected final boolean isInputs;
+
+    public NodeRefIterable(Node node, boolean isInputs) {
+        this.isInputs = isInputs;
+        this.node = node;
+    }
+
+    @Override
+    public NodePosIterator iterator() {
+        int count = isInputs ? node.getInputsCount() : node.getSuccessorsCount();
+        if (count == 0) {
+            return NodeRefIterator.Empty;
+        }
+        int nodeFields = count & 0xFFFF;
+        int nodeListFields = (count >> 16) & 0xFFFF;
+        if (MODIFICATION_COUNTS_ENABLED) {
+            return new NodeRefWithModCountIterator(node, nodeFields, nodeListFields, isInputs);
+        } else {
+            NodeRefIterator iter = new NodeRefIterator(node, nodeFields, nodeListFields, isInputs);
+            iter.forward();
+            return iter;
+        }
+    }
+
+    public NodePosIterator withNullIterator() {
+        int count = isInputs ? node.getInputsCount() : node.getSuccessorsCount();
+        if (count == 0) {
+            return NodeRefIterator.Empty;
+        }
+        int nodeFields = count & 0xFFFF;
+        int nodeListFields = (count >> 16) & 0xFFFF;
+        return new NodeAllRefsIterator(node, nodeFields, nodeListFields, isInputs);
+    }
+
+    @Override
+    public boolean contains(Node other) {
+        return isInputs ? node.inputsContains(other) : node.successorsContains(other);
+    }
+}
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeRefIterator.java	Wed Sep 03 14:58:53 2014 +0200
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeRefIterator.java	Wed Sep 03 16:25:24 2014 +0200
@@ -36,7 +36,9 @@
  */
 public class NodeRefIterator implements NodePosIterator {
 
-    public static final NodeRefIterator Empty = new NodeRefIterator(0, 0, false);
+    public static final NodeRefIterator Empty = new NodeRefIterator(null, 0, 0, false);
+
+    protected final Node node;
 
     /**
      * The total number of {@link Node} and {@link NodeList} fields.
@@ -52,7 +54,7 @@
      * Specifies if this iterator iterates over {@linkplain Input inputs} or {@linkplain Successor
      * successors}.
      */
-    private final boolean isInputs;
+    protected final boolean isInputs;
 
     /**
      * Current field iteration index.
@@ -65,6 +67,8 @@
      */
     protected int subIndex;
 
+    protected NodeList<? extends Node> list;
+
     /**
      * Creates an iterator over a node's references (i.e., {@linkplain Input inputs} or
      * {@linkplain Successor successors}) to other nodes. The {@link Node} fields are iterated
@@ -76,7 +80,8 @@
      * @param nodeListFields the number of {@link NodeList} fields in the class hierarchy of the
      *            node being iterated
      */
-    protected NodeRefIterator(int nodeFields, int nodeListFields, boolean isInputs) {
+    protected NodeRefIterator(Node node, int nodeFields, int nodeListFields, boolean isInputs) {
+        this.node = node;
         this.allNodeRefFields = nodeListFields + nodeFields;
         this.nodeFields = nodeFields;
         this.isInputs = isInputs;
@@ -91,7 +96,7 @@
      *            be between 0 and the {@code nodeFields} value this iterator was constructed with
      */
     protected Node getNode(int at) {
-        throw new NoSuchElementException();
+        return isInputs ? node.getInputNodeAt(at) : node.getSuccessorNodeAt(at);
     }
 
     /**
@@ -102,15 +107,14 @@
      *            constructed with
      */
     protected NodeList<? extends Node> getNodeList(int at) {
-        throw new NoSuchElementException();
+        return isInputs ? node.getInputNodeListAt(at) : node.getSuccessorNodeListAt(at);
     }
 
     protected void forward() {
         if (index < nodeFields) {
             index++;
             while (index < nodeFields) {
-                Node element = getNode(index);
-                if (element != null) {
+                if (getNode(index) != null) {
                     return;
                 }
                 index++;
@@ -119,7 +123,10 @@
             subIndex++;
         }
         while (index < allNodeRefFields) {
-            NodeList<? extends Node> list = getNodeList(index - nodeFields);
+            if (subIndex == 0) {
+                list = getNodeList(index - nodeFields);
+            }
+            assert list == getNodeList(index - nodeFields);
             while (subIndex < list.size()) {
                 if (list.get(subIndex) != null) {
                     return;
@@ -136,7 +143,7 @@
         if (index < nodeFields) {
             return getNode(index);
         } else if (index < allNodeRefFields) {
-            NodeList<? extends Node> list = getNodeList(index - nodeFields);
+            assert getNodeList(index - nodeFields) == list;
             return list.get(subIndex);
         }
         throw new NoSuchElementException();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeRefWithModCountIterator.java	Wed Sep 03 16:25:24 2014 +0200
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ * 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 com.oracle.graal.graph;
+
+import static com.oracle.graal.graph.Graph.*;
+
+import com.oracle.graal.graph.Node.Input;
+
+/**
+ * An iterator over the references to a given {@link Node}'s {@linkplain Input inputs}.
+ *
+ * An iterator of this type will not return null values, unless the field values are modified
+ * concurrently. Concurrent modifications are detected by an assertion on a best-effort basis.
+ */
+public final class NodeRefWithModCountIterator extends NodeRefIterator {
+
+    private final int modCount;
+
+    public NodeRefWithModCountIterator(Node node, int nodeFields, int nodeListFields, boolean isInputs) {
+        super(node, nodeFields, nodeListFields, isInputs);
+        assert MODIFICATION_COUNTS_ENABLED;
+        this.modCount = node.modCount();
+        forward();
+    }
+
+    @Override
+    public boolean hasNext() {
+        try {
+            return super.hasNext();
+        } finally {
+            assert modCount == node.modCount() : "must not be modified";
+        }
+    }
+
+    @Override
+    public Node next() {
+        try {
+            return super.next();
+        } finally {
+            assert modCount == node.modCount() : "must not be modified";
+        }
+    }
+
+    @Override
+    public Position nextPosition() {
+        try {
+            return super.nextPosition();
+        } finally {
+            assert modCount == node.modCount();
+        }
+    }
+}
--- a/graal/com.oracle.graal.nodeinfo.processor/src/com/oracle/graal/nodeinfo/processor/GraphNodeGenerator.java	Wed Sep 03 14:58:53 2014 +0200
+++ b/graal/com.oracle.graal.nodeinfo.processor/src/com/oracle/graal/nodeinfo/processor/GraphNodeGenerator.java	Wed Sep 03 16:25:24 2014 +0200
@@ -47,6 +47,8 @@
  */
 public class GraphNodeGenerator {
 
+    private static final boolean GENERATE_ASSERTIONS = false;
+
     private final GraphNodeProcessor env;
     private final Types types;
     private final Elements elements;
@@ -283,6 +285,10 @@
     enum NodeRefsType {
         Inputs,
         Successors;
+
+        String singular() {
+            return name().substring(0, name().length() - 1);
+        }
     }
 
     CodeCompilationUnit process(TypeElement node, boolean constructorsOnly) {
@@ -336,7 +342,7 @@
                 createPositionAccessibleFieldOrderClass(packageElement);
 
                 if (!inputListFields.isEmpty() || !successorListFields.isEmpty()) {
-                    createGetNodeListAtMethod();
+                    createGetNodeListAtPositionMethod();
                     createSetNodeListAtMethod();
                 }
             }
@@ -352,6 +358,11 @@
                 createAllIteratorClass(Inputs, inputsIteratorClass.asType(), packageElement, inputFields, inputListFields);
                 createWithModCountIteratorClass(Inputs, inputsIteratorClass.asType(), packageElement);
                 createIterableClass(Inputs, packageElement);
+                createGetNodeAtMethod(NodeRefsType.Inputs, inputFields);
+                createCountMethod(NodeRefsType.Inputs, inputFields.size(), inputListFields.size());
+                if (!inputListFields.isEmpty()) {
+                    createGetNodeListAtIndexMethod(NodeRefsType.Inputs, inputListFields);
+                }
             }
 
             if (hasSuccessors) {
@@ -364,6 +375,11 @@
                 createAllIteratorClass(Successors, successorsIteratorClass.asType(), packageElement, successorFields, successorListFields);
                 createWithModCountIteratorClass(Successors, successorsIteratorClass.asType(), packageElement);
                 createIterableClass(Successors, packageElement);
+                createGetNodeAtMethod(NodeRefsType.Successors, successorFields);
+                createCountMethod(NodeRefsType.Successors, successorFields.size(), successorListFields.size());
+                if (!successorListFields.isEmpty()) {
+                    createGetNodeListAtIndexMethod(NodeRefsType.Successors, successorListFields);
+                }
             }
         }
         compilationUnit.add(genClass);
@@ -426,6 +442,22 @@
         genClassName = null;
     }
 
+    private CodeVariableElement addParameter(CodeExecutableElement method, TypeMirror type, String name) {
+        return addParameter(method, type, name, true);
+    }
+
+    private CodeVariableElement addParameter(CodeExecutableElement method, TypeMirror type, String name, boolean checkHiding) {
+        CodeVariableElement parameter = new CodeVariableElement(type, name);
+        if (checkHiding && hidesField(parameter.getSimpleName().toString())) {
+            DeclaredType suppress = (DeclaredType) getType(SuppressWarnings.class);
+            CodeAnnotationMirror suppressMirror = new CodeAnnotationMirror(suppress);
+            suppressMirror.setElementValue(suppressMirror.findExecutableElement("value"), new CodeAnnotationValue("hiding"));
+            parameter.getAnnotationMirrors().add(suppressMirror);
+        }
+        method.addParameter(parameter);
+        return parameter;
+    }
+
     /**
      * Checks that a generated method overrides exactly one method in a super type and that the
      * super type is Node.
@@ -448,9 +480,11 @@
 
     private ExecutableElement createIsOptionalInputAtMethod() {
         CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), getType(boolean.class), "isOptionalInputAt");
-        method.addParameter(new CodeVariableElement(Position.asType(), "pos"));
+        addParameter(method, Position.asType(), "pos");
         CodeTreeBuilder b = method.createBuilder();
-        b.startAssert().string("pos.isInput()").end();
+        if (GENERATE_ASSERTIONS) {
+            b.startAssert().string("pos.isInput()").end();
+        }
         if (!optionalInputs.isEmpty()) {
             b.startSwitch().string("pos.getIndex()").end().startBlock();
             int index = 0;
@@ -490,9 +524,10 @@
 
         // Constructor
         CodeExecutableElement ctor = new CodeExecutableElement(Collections.emptySet(), null, name);
-        ctor.addParameter(new CodeVariableElement(getType(boolean.class), "callForward"));
+        addParameter(ctor, getType(boolean.class), "callForward");
         CodeTreeBuilder b = ctor.createBuilder();
         b.startStatement().startSuperCall();
+        b.string(genClassName, ".this");
         b.string(String.valueOf(nodeFields.size()));
         b.string(String.valueOf(nodeListFields.size()));
         b.string(String.valueOf(nodeRefsType == NodeRefsType.Inputs));
@@ -512,7 +547,7 @@
     private void createGetFieldMethod(CodeTypeElement cls, List<VariableElement> fields, TypeMirror returnType, String name) {
         if (!fields.isEmpty()) {
             CodeExecutableElement method = new CodeExecutableElement(modifiers(PROTECTED, FINAL), returnType, name);
-            method.addParameter(new CodeVariableElement(getType(int.class), "at"));
+            addParameter(method, getType(int.class), "at");
             CodeTreeBuilder b = method.createBuilder();
             createGetFieldCases(b, fields, returnType, null);
             cls.add(method);
@@ -623,7 +658,7 @@
 
         CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC), getType(String[].class), "getOrderedFieldNames");
 
-        method.addParameter(new CodeVariableElement(getType(boolean.class), "input"));
+        addParameter(method, getType(boolean.class), "input", false);
 
         CodeTreeBuilder b = method.createBuilder();
         b.startIf().string("input").end().startBlock();
@@ -669,10 +704,11 @@
         b.startElseBlock();
         b.startStatement().string("subIndex++").end();
         b.end();
-        DeclaredType nodeListOfNode = types.getDeclaredType(NodeList, types.getWildcardType(Node.asType(), null));
         int count = nodeFieldsSize + nodeListFieldsSize;
         b.startWhile().string("index < " + count).end().startBlock();
-        b.declaration(nodeListOfNode, "list", "getNodeList(index - " + nodeFieldsSize + ")");
+        b.startIf().string("subIndex == 0").end().startBlock();
+        b.startStatement().string("list = getNodeList(index - " + nodeFieldsSize + ")").end();
+        b.end();
         b.startIf().string("subIndex < list.size()").end().startBlock();
         b.startStatement().string("return").end();
         b.end();
@@ -749,16 +785,16 @@
 
         // contains(Node) method
         method = new CodeExecutableElement(modifiers(PUBLIC, FINAL), getType(boolean.class), "contains");
-        method.addParameter(new CodeVariableElement(Node.asType(), "n"));
+        addParameter(method, Node.asType(), "n");
         b = method.createBuilder();
-        b.startStatement().string("return " + nodeRefsType.name().toLowerCase() + "Contain(n)").end();
+        b.startStatement().string("return " + nodeRefsType.name().toLowerCase() + "Contains(n)").end();
         cls.add(method);
         genClass.add(cls);
     }
 
     private void createContainsMethod(NodeRefsType nodeRefsType, List<VariableElement> nodeFields, List<VariableElement> nodeListFields) {
-        CodeExecutableElement method = new CodeExecutableElement(modifiers(PRIVATE, FINAL), getType(boolean.class), nodeRefsType.name().toLowerCase() + "Contain");
-        method.addParameter(new CodeVariableElement(Node.asType(), "n"));
+        CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC, FINAL), getType(boolean.class), nodeRefsType.name().toLowerCase() + "Contains");
+        addParameter(method, Node.asType(), "n");
         CodeTreeBuilder b = method.createBuilder();
         for (VariableElement f : nodeFields) {
             b.startIf().string("n == " + f).end().startBlock();
@@ -788,7 +824,7 @@
 
     private void createGetNodeAtMethod() {
         CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC, FINAL), Node.asType(), "getNodeAt");
-        method.addParameter(new CodeVariableElement(Position.asType(), "pos"));
+        addParameter(method, Position.asType(), "pos");
         CodeTreeBuilder b = method.createBuilder();
         b.startIf().string("pos.isInput()").end().startBlock();
         createGetNodeAt(b, inputFields, inputListFields);
@@ -800,9 +836,79 @@
         checkOnlyInGenNode(method);
     }
 
-    private void createGetNodeListAtMethod() {
+    private void createCountMethod(NodeRefsType nodeRefsType, int nodesCount, int nodeListsCount) {
+        CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC, FINAL), getType(int.class), "get" + nodeRefsType + "Count");
+        CodeTreeBuilder b = method.createBuilder();
+
+        b.startStatement().string("return " + (nodeListsCount << 16 | nodesCount), " /* (" + nodeListsCount + " << 16 | " + nodesCount + ") */").end();
+        genClass.add(method);
+        checkOnlyInGenNode(method);
+    }
+
+    private void createGetNodeAtMethod(NodeRefsType nodeRefsType, List<VariableElement> nodes) {
+        CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC, FINAL), Node.asType(), "get" + nodeRefsType.singular() + "NodeAt");
+        addParameter(method, getType(int.class), "index");
+        CodeTreeBuilder b = method.createBuilder();
+        boolean justOne = nodes.size() == 1;
+        if (!justOne) {
+            b.startSwitch().string("index").end().startBlock();
+        } else if (GENERATE_ASSERTIONS) {
+            b.startAssert().string("index == 0").end();
+        }
+        int index = 0;
+        for (VariableElement f : nodes) {
+            if (!justOne) {
+                b.startCase().string(String.valueOf(index)).end();
+            }
+            b.startReturn();
+            if (!isAssignableWithErasure(f, Node)) {
+                b.cast(((DeclaredType) Node.asType()).asElement().getSimpleName().toString());
+            }
+            b.string(genClassName + ".this." + f.getSimpleName());
+            b.end();
+            index++;
+        }
+        if (!justOne) {
+            b.end();
+            b.startThrow().startNew(getType(NoSuchElementException.class)).end().end();
+        }
+        genClass.add(method);
+        checkOnlyInGenNode(method);
+    }
+
+    private void createGetNodeListAtIndexMethod(NodeRefsType nodeRefsType, List<VariableElement> nodeLists) {
+        CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC, FINAL), types.getDeclaredType(NodeList, types.getWildcardType(Node.asType(), null)), "get" +
+                        nodeRefsType.singular() + "NodeListAt");
+        addParameter(method, getType(int.class), "index");
+        CodeTreeBuilder b = method.createBuilder();
+
+        boolean justOne = nodeLists.size() == 1;
+        if (!justOne) {
+            b.startSwitch().string("index").end().startBlock();
+        } else if (GENERATE_ASSERTIONS) {
+            b.startAssert().string("index == 0").end();
+        }
+        int index = 0;
+        for (VariableElement f : nodeLists) {
+            if (!justOne) {
+                b.startCase().string(String.valueOf(index)).end();
+            }
+            b.startReturn();
+            b.string(genClassName + ".this." + f.getSimpleName());
+            b.end();
+            index++;
+        }
+        if (!justOne) {
+            b.end();
+            b.startThrow().startNew(getType(NoSuchElementException.class)).end().end();
+        }
+        genClass.add(method);
+        checkOnlyInGenNode(method);
+    }
+
+    private void createGetNodeListAtPositionMethod() {
         CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC, FINAL), types.getDeclaredType(NodeList, types.getWildcardType(Node.asType(), null)), "getNodeListAt");
-        method.addParameter(new CodeVariableElement(Position.asType(), "pos"));
+        addParameter(method, Position.asType(), "pos");
         CodeTreeBuilder b = method.createBuilder();
         b.startIf().string("pos.isInput()").end().startBlock();
         createGetNodeListAt(b, inputFields, inputListFields);
@@ -822,8 +928,8 @@
         suppressMirror.setElementValue(suppressMirror.findExecutableElement("value"), new CodeAnnotationValue("unchecked"));
         method.getAnnotationMirrors().add(suppressMirror);
 
-        method.addParameter(new CodeVariableElement(Position.asType(), "pos"));
-        method.addParameter(new CodeVariableElement(types.getDeclaredType(NodeList, types.getWildcardType(Node.asType(), null)), "list"));
+        addParameter(method, Position.asType(), "pos");
+        addParameter(method, types.getDeclaredType(NodeList, types.getWildcardType(Node.asType(), null)), "list");
         CodeTreeBuilder b = method.createBuilder();
         b.startIf().string("pos.isInput()").end().startBlock();
         createSetNodeListAt(b, inputFields, inputListFields);
@@ -837,7 +943,7 @@
 
     private void createGetNameOfMethod() {
         CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC, FINAL), getType(String.class), "getNameOf");
-        method.addParameter(new CodeVariableElement(Position.asType(), "pos"));
+        addParameter(method, Position.asType(), "pos");
         CodeTreeBuilder b = method.createBuilder();
 
         b.startIf().string("pos.isInput()").end().startBlock();
@@ -852,9 +958,11 @@
 
     private void createGetInputTypeAtMethod() {
         CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC, FINAL), getType(InputType.class), "getInputTypeAt");
-        method.addParameter(new CodeVariableElement(Position.asType(), "pos"));
+        addParameter(method, Position.asType(), "pos");
         CodeTreeBuilder b = method.createBuilder();
-        b.startAssert().string("pos.isInput()").end();
+        if (GENERATE_ASSERTIONS) {
+            b.startAssert().string("pos.isInput()").end();
+        }
         boolean hasNodes = !inputFields.isEmpty();
         boolean hasNodeLists = !inputListFields.isEmpty();
         if (hasNodeLists || hasNodes) {
@@ -882,15 +990,8 @@
 
     private void createUpdateOrInitializeNodeAtMethod(boolean isInitialization) {
         CodeExecutableElement method = new CodeExecutableElement(modifiers(PUBLIC, FINAL), getType(void.class), (isInitialization ? "initialize" : "update") + "NodeAt");
-        method.addParameter(new CodeVariableElement(Position.asType(), "pos"));
-        CodeVariableElement newValue = new CodeVariableElement(Node.asType(), "newValue");
-        if (hidesField(newValue.getSimpleName().toString())) {
-            DeclaredType suppress = (DeclaredType) getType(SuppressWarnings.class);
-            CodeAnnotationMirror suppressMirror = new CodeAnnotationMirror(suppress);
-            suppressMirror.setElementValue(suppressMirror.findExecutableElement("value"), new CodeAnnotationValue("hiding"));
-            newValue.getAnnotationMirrors().add(suppressMirror);
-        }
-        method.addParameter(newValue);
+        addParameter(method, Position.asType(), "pos");
+        addParameter(method, Node.asType(), "newValue");
         CodeTreeBuilder b = method.createBuilder();
         b.startIf().string("pos.isInput()").end().startBlock();
         createUpdateOrInitializeNodeAt(b, inputFields, inputListFields, isInitialization);
@@ -910,7 +1011,9 @@
         } else {
             if (hasNodes) {
                 if (!hasNodeLists) {
-                    b.startAssert().string("pos.getSubIndex() == NOT_ITERABLE").end();
+                    if (GENERATE_ASSERTIONS) {
+                        b.startAssert().string("pos.getSubIndex() == NOT_ITERABLE").end();
+                    }
                 } else {
                     b.startIf().string("pos.getSubIndex() == NOT_ITERABLE").end().startBlock();
                 }
@@ -923,7 +1026,9 @@
 
             if (hasNodeLists) {
                 if (!hasNodes) {
-                    b.startAssert().string("pos.getSubIndex() != NOT_ITERABLE").end();
+                    if (GENERATE_ASSERTIONS) {
+                        b.startAssert().string("pos.getSubIndex() != NOT_ITERABLE").end();
+                    }
                 } else {
                     b.startElseBlock();
                 }
@@ -941,7 +1046,9 @@
         if (!hasNodeLists) {
             b.startThrow().startNew(getType(NoSuchElementException.class)).end().end();
         } else {
-            b.startAssert().string("pos.getSubIndex() == NODE_LIST").end();
+            if (GENERATE_ASSERTIONS) {
+                b.startAssert().string("pos.getSubIndex() == NODE_LIST").end();
+            }
             b.declaration("int", "at", "pos.getIndex() - " + nodes.size());
             createGetFieldCases(b, nodeLists, Node.asType(), "");
         }
@@ -952,7 +1059,9 @@
         if (!hasNodeLists) {
             b.startThrow().startNew(getType(NoSuchElementException.class)).end().end();
         } else {
-            b.startAssert().string("pos.getSubIndex() == NODE_LIST").end();
+            if (GENERATE_ASSERTIONS) {
+                b.startAssert().string("pos.getSubIndex() == NODE_LIST").end();
+            }
             b.declaration("int", "at", "pos.getIndex() - " + nodes.size());
             createSetNodeListAtCases(b, nodeLists, Node.asType(), "");
         }
@@ -987,7 +1096,9 @@
         } else {
             if (hasNodes) {
                 if (!hasNodeLists) {
-                    b.startAssert().string("pos.getSubIndex() == NOT_ITERABLE").end();
+                    if (GENERATE_ASSERTIONS) {
+                        b.startAssert().string("pos.getSubIndex() == NOT_ITERABLE").end();
+                    }
                 } else {
                     b.startIf().string("pos.getSubIndex() == NOT_ITERABLE").end().startBlock();
                 }
@@ -1000,7 +1111,9 @@
 
             if (hasNodeLists) {
                 if (!hasNodes) {
-                    b.startAssert().string("pos.getSubIndex() != NOT_ITERABLE").end();
+                    if (GENERATE_ASSERTIONS) {
+                        b.startAssert().string("pos.getSubIndex() != NOT_ITERABLE").end();
+                    }
                 } else {
                     b.startElseBlock();
                 }