1 /*
   2  * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package org.graalvm.compiler.graph;
  24 
  25 import static org.graalvm.compiler.core.common.Fields.translateInto;
  26 import static org.graalvm.compiler.debug.GraalError.shouldNotReachHere;
  27 import static org.graalvm.compiler.graph.Edges.translateInto;
  28 import static org.graalvm.compiler.graph.Graph.isModificationCountsEnabled;
  29 import static org.graalvm.compiler.graph.InputEdges.translateInto;
  30 import static org.graalvm.compiler.graph.Node.WithAllEdges;
  31 import static org.graalvm.compiler.graph.UnsafeAccess.UNSAFE;
  32 
  33 import java.lang.annotation.Annotation;
  34 import java.lang.reflect.AnnotatedElement;
  35 import java.lang.reflect.Field;
  36 import java.lang.reflect.Modifier;
  37 import java.util.ArrayList;
  38 import java.util.Arrays;
  39 import java.util.EnumSet;
  40 import java.util.Iterator;
  41 import java.util.NoSuchElementException;
  42 import java.util.Objects;
  43 import java.util.concurrent.atomic.AtomicInteger;
  44 
  45 import org.graalvm.compiler.core.common.FieldIntrospection;
  46 import org.graalvm.compiler.core.common.Fields;
  47 import org.graalvm.compiler.core.common.FieldsScanner;
  48 import org.graalvm.compiler.debug.Debug;
  49 import org.graalvm.compiler.debug.DebugCloseable;
  50 import org.graalvm.compiler.debug.DebugCounter;
  51 import org.graalvm.compiler.debug.DebugTimer;
  52 import org.graalvm.compiler.debug.GraalError;
  53 import org.graalvm.compiler.graph.Edges.Type;
  54 import org.graalvm.compiler.graph.Graph.DuplicationReplacement;
  55 import org.graalvm.compiler.graph.Node.EdgeVisitor;
  56 import org.graalvm.compiler.graph.Node.Input;
  57 import org.graalvm.compiler.graph.Node.OptionalInput;
  58 import org.graalvm.compiler.graph.Node.Successor;
  59 import org.graalvm.compiler.graph.iterators.NodeIterable;
  60 import org.graalvm.compiler.graph.spi.Canonicalizable;
  61 import org.graalvm.compiler.graph.spi.Canonicalizable.BinaryCommutative;
  62 import org.graalvm.compiler.graph.spi.Simplifiable;
  63 import org.graalvm.compiler.nodeinfo.InputType;
  64 import org.graalvm.compiler.nodeinfo.NodeCycles;
  65 import org.graalvm.compiler.nodeinfo.NodeInfo;
  66 import org.graalvm.compiler.nodeinfo.NodeSize;
  67 import org.graalvm.compiler.nodeinfo.Verbosity;
  68 import org.graalvm.util.EconomicMap;
  69 import org.graalvm.util.Equivalence;
  70 
  71 /**
  72  * Metadata for every {@link Node} type. The metadata includes:
  73  * <ul>
  74  * <li>The offsets of fields annotated with {@link Input} and {@link Successor} as well as methods
  75  * for iterating over such fields.</li>
  76  * <li>The identifier for an {@link IterableNodeType} class.</li>
  77  * </ul>
  78  */
  79 public final class NodeClass<T> extends FieldIntrospection<T> {
  80 
  81     // Timers for creation of a NodeClass instance
  82     private static final DebugTimer Init_FieldScanning = Debug.timer("NodeClass.Init.FieldScanning");
  83     private static final DebugTimer Init_FieldScanningInner = Debug.timer("NodeClass.Init.FieldScanning.Inner");
  84     private static final DebugTimer Init_AnnotationParsing = Debug.timer("NodeClass.Init.AnnotationParsing");
  85     private static final DebugTimer Init_Edges = Debug.timer("NodeClass.Init.Edges");
  86     private static final DebugTimer Init_Data = Debug.timer("NodeClass.Init.Data");
  87     private static final DebugTimer Init_AllowedUsages = Debug.timer("NodeClass.Init.AllowedUsages");
  88     private static final DebugTimer Init_IterableIds = Debug.timer("NodeClass.Init.IterableIds");
  89 
  90     public static final long MAX_EDGES = 8;
  91     public static final long MAX_LIST_EDGES = 6;
  92     public static final long OFFSET_MASK = 0xFC;
  93     public static final long LIST_MASK = 0x01;
  94     public static final long NEXT_EDGE = 0x08;
  95 
  96     @SuppressWarnings("try")
  97     private static <T extends Annotation> T getAnnotationTimed(AnnotatedElement e, Class<T> annotationClass) {
  98         try (DebugCloseable s = Init_AnnotationParsing.start()) {
  99             return e.getAnnotation(annotationClass);
 100         }
 101     }
 102 
 103     /**
 104      * Gets the {@link NodeClass} associated with a given {@link Class}.
 105      */
 106     public static <T> NodeClass<T> create(Class<T> c) {
 107         assert get(c) == null;
 108         Class<? super T> superclass = c.getSuperclass();
 109         NodeClass<? super T> nodeSuperclass = null;
 110         if (superclass != NODE_CLASS) {
 111             nodeSuperclass = get(superclass);
 112         }
 113         return new NodeClass<>(c, nodeSuperclass);
 114     }
 115 
 116     @SuppressWarnings("unchecked")
 117     public static <T> NodeClass<T> get(Class<T> superclass) {
 118         try {
 119             Field field = superclass.getDeclaredField("TYPE");
 120             field.setAccessible(true);
 121             return (NodeClass<T>) field.get(null);
 122         } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
 123             throw new RuntimeException(e);
 124         }
 125     }
 126 
 127     private static final Class<?> NODE_CLASS = Node.class;
 128     private static final Class<?> INPUT_LIST_CLASS = NodeInputList.class;
 129     private static final Class<?> SUCCESSOR_LIST_CLASS = NodeSuccessorList.class;
 130 
 131     private static AtomicInteger nextIterableId = new AtomicInteger();
 132     private static AtomicInteger nextLeafId = new AtomicInteger();
 133 
 134     private final InputEdges inputs;
 135     private final SuccessorEdges successors;
 136     private final NodeClass<? super T> superNodeClass;
 137 
 138     private final boolean canGVN;
 139     private final int startGVNNumber;
 140     private final String nameTemplate;
 141     private final int iterableId;
 142     private final EnumSet<InputType> allowedUsageTypes;
 143     private int[] iterableIds;
 144     private final long inputsIteration;
 145     private final long successorIteration;
 146 
 147     private static final DebugCounter ITERABLE_NODE_TYPES = Debug.counter("IterableNodeTypes");
 148     private final DebugCounter nodeIterableCount;
 149 
 150     /**
 151      * Determines if this node type implements {@link Canonicalizable}.
 152      */
 153     private final boolean isCanonicalizable;
 154 
 155     /**
 156      * Determines if this node type implements {@link BinaryCommutative}.
 157      */
 158     private final boolean isCommutative;
 159 
 160     /**
 161      * Determines if this node type implements {@link Simplifiable}.
 162      */
 163     private final boolean isSimplifiable;
 164     private final boolean isLeafNode;
 165 
 166     private final int leafId;
 167 
 168     public NodeClass(Class<T> clazz, NodeClass<? super T> superNodeClass) {
 169         this(clazz, superNodeClass, new FieldsScanner.DefaultCalcOffset(), null, 0);
 170     }
 171 
 172     @SuppressWarnings("try")
 173     public NodeClass(Class<T> clazz, NodeClass<? super T> superNodeClass, FieldsScanner.CalcOffset calcOffset, int[] presetIterableIds, int presetIterableId) {
 174         super(clazz);
 175         this.superNodeClass = superNodeClass;
 176         assert NODE_CLASS.isAssignableFrom(clazz);
 177 
 178         this.isCanonicalizable = Canonicalizable.class.isAssignableFrom(clazz);
 179         this.isCommutative = BinaryCommutative.class.isAssignableFrom(clazz);
 180         if (Canonicalizable.Unary.class.isAssignableFrom(clazz) || Canonicalizable.Binary.class.isAssignableFrom(clazz)) {
 181             assert Canonicalizable.Unary.class.isAssignableFrom(clazz) ^ Canonicalizable.Binary.class.isAssignableFrom(clazz) : clazz + " should implement either Unary or Binary, not both";
 182         }
 183 
 184         this.isSimplifiable = Simplifiable.class.isAssignableFrom(clazz);
 185 
 186         NodeFieldsScanner fs = new NodeFieldsScanner(calcOffset, superNodeClass);
 187         try (DebugCloseable t = Init_FieldScanning.start()) {
 188             fs.scan(clazz, clazz.getSuperclass(), false);
 189         }
 190 
 191         try (DebugCloseable t1 = Init_Edges.start()) {
 192             successors = new SuccessorEdges(fs.directSuccessors, fs.successors);
 193             successorIteration = computeIterationMask(successors.type(), successors.getDirectCount(), successors.getOffsets());
 194             inputs = new InputEdges(fs.directInputs, fs.inputs);
 195             inputsIteration = computeIterationMask(inputs.type(), inputs.getDirectCount(), inputs.getOffsets());
 196         }
 197         try (DebugCloseable t1 = Init_Data.start()) {
 198             data = new Fields(fs.data);
 199         }
 200 
 201         isLeafNode = inputs.getCount() + successors.getCount() == 0;
 202         if (isLeafNode) {
 203             this.leafId = nextLeafId.getAndIncrement();
 204         } else {
 205             this.leafId = -1;
 206         }
 207 
 208         canGVN = Node.ValueNumberable.class.isAssignableFrom(clazz);
 209         startGVNNumber = clazz.getName().hashCode();
 210 
 211         NodeInfo info = getAnnotationTimed(clazz, NodeInfo.class);
 212         assert info != null : "Missing NodeInfo annotation on " + clazz;
 213         if (!info.nameTemplate().isEmpty()) {
 214             this.nameTemplate = info.nameTemplate();
 215         } else if (!info.shortName().isEmpty()) {
 216             this.nameTemplate = info.shortName();
 217         } else {
 218             this.nameTemplate = "";
 219         }
 220 
 221         try (DebugCloseable t1 = Init_AllowedUsages.start()) {
 222             allowedUsageTypes = superNodeClass == null ? EnumSet.noneOf(InputType.class) : superNodeClass.allowedUsageTypes.clone();
 223             allowedUsageTypes.addAll(Arrays.asList(info.allowedUsageTypes()));
 224         }
 225 
 226         if (presetIterableIds != null) {
 227             this.iterableIds = presetIterableIds;
 228             this.iterableId = presetIterableId;
 229         } else if (IterableNodeType.class.isAssignableFrom(clazz)) {
 230             ITERABLE_NODE_TYPES.increment();
 231             try (DebugCloseable t1 = Init_IterableIds.start()) {
 232                 this.iterableId = nextIterableId.getAndIncrement();
 233 
 234                 NodeClass<?> snc = superNodeClass;
 235                 while (snc != null && IterableNodeType.class.isAssignableFrom(snc.getClazz())) {
 236                     snc.addIterableId(iterableId);
 237                     snc = snc.superNodeClass;
 238                 }
 239 
 240                 this.iterableIds = new int[]{iterableId};
 241             }
 242         } else {
 243             this.iterableId = Node.NOT_ITERABLE;
 244             this.iterableIds = null;
 245         }
 246         nodeIterableCount = Debug.counter("NodeIterable_%s", clazz);
 247         assert verifyIterableIds();
 248 
 249         try (Debug.Scope scope = Debug.scope("NodeCosts")) {
 250             /*
 251              * Note: We do not check for the existence of the node cost annotations during
 252              * construction as not every node needs to have them set. However if costs are queried,
 253              * after the construction of the node class, they must be properly set. This is
 254              * important as we can not trust our cost model if there are unspecified nodes. Nodes
 255              * that do not need cost annotations are e.g. abstractions like FixedNode or
 256              * FloatingNode or ValueNode. Sub classes where costs are not specified will ask the
 257              * superclass for their costs during node class initialization. Therefore getters for
 258              * cycles and size can omit verification during creation.
 259              */
 260             NodeCycles c = info.cycles();
 261             if (c == NodeCycles.CYCLES_UNSET) {
 262                 cycles = superNodeClass != null ? superNodeClass.cycles : NodeCycles.CYCLES_UNSET;
 263             } else {
 264                 cycles = c;
 265             }
 266             assert cycles != null;
 267             NodeSize s = info.size();
 268             if (s == NodeSize.SIZE_UNSET) {
 269                 size = superNodeClass != null ? superNodeClass.size : NodeSize.SIZE_UNSET;
 270             } else {
 271                 size = s;
 272             }
 273             assert size != null;
 274             Debug.log("Node cost for node of type __| %s |_, cycles:%s,size:%s", clazz, cycles, size);
 275         }
 276 
 277     }
 278 
 279     private final NodeCycles cycles;
 280     private final NodeSize size;
 281 
 282     public NodeCycles cycles() {
 283         return cycles;
 284     }
 285 
 286     public NodeSize size() {
 287         return size;
 288     }
 289 
 290     public static long computeIterationMask(Type type, int directCount, long[] offsets) {
 291         long mask = 0;
 292         if (offsets.length > NodeClass.MAX_EDGES) {
 293             throw new GraalError("Exceeded maximum of %d edges (%s)", NodeClass.MAX_EDGES, type);
 294         }
 295         if (offsets.length - directCount > NodeClass.MAX_LIST_EDGES) {
 296             throw new GraalError("Exceeded maximum of %d list edges (%s)", NodeClass.MAX_LIST_EDGES, type);
 297         }
 298 
 299         for (int i = offsets.length - 1; i >= 0; i--) {
 300             long offset = offsets[i];
 301             assert ((offset & 0xFF) == offset) : "field offset too large!";
 302             mask <<= NodeClass.NEXT_EDGE;
 303             mask |= offset;
 304             if (i >= directCount) {
 305                 mask |= 0x3;
 306             }
 307         }
 308         return mask;
 309     }
 310 
 311     private synchronized void addIterableId(int newIterableId) {
 312         assert !containsId(newIterableId, iterableIds);
 313         int[] copy = Arrays.copyOf(iterableIds, iterableIds.length + 1);
 314         copy[iterableIds.length] = newIterableId;
 315         iterableIds = copy;
 316     }
 317 
 318     private boolean verifyIterableIds() {
 319         NodeClass<?> snc = superNodeClass;
 320         while (snc != null && IterableNodeType.class.isAssignableFrom(snc.getClazz())) {
 321             assert containsId(iterableId, snc.iterableIds);
 322             snc = snc.superNodeClass;
 323         }
 324         return true;
 325     }
 326 
 327     private static boolean containsId(int iterableId, int[] iterableIds) {
 328         for (int i : iterableIds) {
 329             if (i == iterableId) {
 330                 return true;
 331             }
 332         }
 333         return false;
 334     }
 335 
 336     private String shortName;
 337 
 338     public String shortName() {
 339         if (shortName == null) {
 340             NodeInfo info = getClazz().getAnnotation(NodeInfo.class);
 341             if (!info.shortName().isEmpty()) {
 342                 shortName = info.shortName();
 343             } else {
 344                 String localShortName = getClazz().getSimpleName();
 345                 if (localShortName.endsWith("Node") && !localShortName.equals("StartNode") && !localShortName.equals("EndNode")) {
 346                     shortName = localShortName.substring(0, localShortName.length() - 4);
 347                 } else {
 348                     shortName = localShortName;
 349                 }
 350             }
 351         }
 352         return shortName;
 353     }
 354 
 355     @Override
 356     public Fields[] getAllFields() {
 357         return new Fields[]{data, inputs, successors};
 358     }
 359 
 360     public int[] iterableIds() {
 361         nodeIterableCount.increment();
 362         return iterableIds;
 363     }
 364 
 365     public int iterableId() {
 366         return iterableId;
 367     }
 368 
 369     public boolean valueNumberable() {
 370         return canGVN;
 371     }
 372 
 373     /**
 374      * Determines if this node type implements {@link Canonicalizable}.
 375      */
 376     public boolean isCanonicalizable() {
 377         return isCanonicalizable;
 378     }
 379 
 380     /**
 381      * Determines if this node type implements {@link BinaryCommutative}.
 382      */
 383     public boolean isCommutative() {
 384         return isCommutative;
 385     }
 386 
 387     /**
 388      * Determines if this node type implements {@link Simplifiable}.
 389      */
 390     public boolean isSimplifiable() {
 391         return isSimplifiable;
 392     }
 393 
 394     static int allocatedNodeIterabledIds() {
 395         return nextIterableId.get();
 396     }
 397 
 398     public EnumSet<InputType> getAllowedUsageTypes() {
 399         return allowedUsageTypes;
 400     }
 401 
 402     /**
 403      * Describes a field representing an input or successor edge in a node.
 404      */
 405     protected static class EdgeInfo extends FieldsScanner.FieldInfo {
 406 
 407         public EdgeInfo(long offset, String name, Class<?> type, Class<?> declaringClass) {
 408             super(offset, name, type, declaringClass);
 409         }
 410 
 411         /**
 412          * Sorts non-list edges before list edges.
 413          */
 414         @Override
 415         public int compareTo(FieldsScanner.FieldInfo o) {
 416             if (NodeList.class.isAssignableFrom(o.type)) {
 417                 if (!NodeList.class.isAssignableFrom(type)) {
 418                     return -1;
 419                 }
 420             } else {
 421                 if (NodeList.class.isAssignableFrom(type)) {
 422                     return 1;
 423                 }
 424             }
 425             return super.compareTo(o);
 426         }
 427     }
 428 
 429     /**
 430      * Describes a field representing an {@linkplain Type#Inputs input} edge in a node.
 431      */
 432     protected static class InputInfo extends EdgeInfo {
 433         final InputType inputType;
 434         final boolean optional;
 435 
 436         public InputInfo(long offset, String name, Class<?> type, Class<?> declaringClass, InputType inputType, boolean optional) {
 437             super(offset, name, type, declaringClass);
 438             this.inputType = inputType;
 439             this.optional = optional;
 440         }
 441 
 442         @Override
 443         public String toString() {
 444             return super.toString() + "{inputType=" + inputType + ", optional=" + optional + "}";
 445         }
 446     }
 447 
 448     protected static class NodeFieldsScanner extends FieldsScanner {
 449 
 450         public final ArrayList<InputInfo> inputs = new ArrayList<>();
 451         public final ArrayList<EdgeInfo> successors = new ArrayList<>();
 452         int directInputs;
 453         int directSuccessors;
 454 
 455         protected NodeFieldsScanner(FieldsScanner.CalcOffset calc, NodeClass<?> superNodeClass) {
 456             super(calc);
 457             if (superNodeClass != null) {
 458                 translateInto(superNodeClass.inputs, inputs);
 459                 translateInto(superNodeClass.successors, successors);
 460                 translateInto(superNodeClass.data, data);
 461                 directInputs = superNodeClass.inputs.getDirectCount();
 462                 directSuccessors = superNodeClass.successors.getDirectCount();
 463             }
 464         }
 465 
 466         @SuppressWarnings("try")
 467         @Override
 468         protected void scanField(Field field, long offset) {
 469             Input inputAnnotation = getAnnotationTimed(field, Node.Input.class);
 470             OptionalInput optionalInputAnnotation = getAnnotationTimed(field, Node.OptionalInput.class);
 471             Successor successorAnnotation = getAnnotationTimed(field, Successor.class);
 472             try (DebugCloseable s = Init_FieldScanningInner.start()) {
 473                 Class<?> type = field.getType();
 474                 int modifiers = field.getModifiers();
 475 
 476                 if (inputAnnotation != null || optionalInputAnnotation != null) {
 477                     assert successorAnnotation == null : "field cannot be both input and successor";
 478                     if (INPUT_LIST_CLASS.isAssignableFrom(type)) {
 479                         // NodeInputList fields should not be final since they are
 480                         // written (via Unsafe) in clearInputs()
 481                         GraalError.guarantee(!Modifier.isFinal(modifiers), "NodeInputList input field %s should not be final", field);
 482                         GraalError.guarantee(!Modifier.isPublic(modifiers), "NodeInputList input field %s should not be public", field);
 483                     } else {
 484                         GraalError.guarantee(NODE_CLASS.isAssignableFrom(type) || type.isInterface(), "invalid input type: %s", type);
 485                         GraalError.guarantee(!Modifier.isFinal(modifiers), "Node input field %s should not be final", field);
 486                         directInputs++;
 487                     }
 488                     InputType inputType;
 489                     if (inputAnnotation != null) {
 490                         assert optionalInputAnnotation == null : "inputs can either be optional or non-optional";
 491                         inputType = inputAnnotation.value();
 492                     } else {
 493                         inputType = optionalInputAnnotation.value();
 494                     }
 495                     inputs.add(new InputInfo(offset, field.getName(), type, field.getDeclaringClass(), inputType, field.isAnnotationPresent(Node.OptionalInput.class)));
 496                 } else if (successorAnnotation != null) {
 497                     if (SUCCESSOR_LIST_CLASS.isAssignableFrom(type)) {
 498                         // NodeSuccessorList fields should not be final since they are
 499                         // written (via Unsafe) in clearSuccessors()
 500                         GraalError.guarantee(!Modifier.isFinal(modifiers), "NodeSuccessorList successor field % should not be final", field);
 501                         GraalError.guarantee(!Modifier.isPublic(modifiers), "NodeSuccessorList successor field %s should not be public", field);
 502                     } else {
 503                         GraalError.guarantee(NODE_CLASS.isAssignableFrom(type), "invalid successor type: %s", type);
 504                         GraalError.guarantee(!Modifier.isFinal(modifiers), "Node successor field %s should not be final", field);
 505                         directSuccessors++;
 506                     }
 507                     successors.add(new EdgeInfo(offset, field.getName(), type, field.getDeclaringClass()));
 508                 } else {
 509                     GraalError.guarantee(!NODE_CLASS.isAssignableFrom(type) || field.getName().equals("Null"), "suspicious node field: %s", field);
 510                     GraalError.guarantee(!INPUT_LIST_CLASS.isAssignableFrom(type), "suspicious node input list field: %s", field);
 511                     GraalError.guarantee(!SUCCESSOR_LIST_CLASS.isAssignableFrom(type), "suspicious node successor list field: %s", field);
 512                     super.scanField(field, offset);
 513                 }
 514             }
 515         }
 516     }
 517 
 518     @Override
 519     public String toString() {
 520         StringBuilder str = new StringBuilder();
 521         str.append("NodeClass ").append(getClazz().getSimpleName()).append(" [");
 522         inputs.appendFields(str);
 523         str.append("] [");
 524         successors.appendFields(str);
 525         str.append("] [");
 526         data.appendFields(str);
 527         str.append("]");
 528         return str.toString();
 529     }
 530 
 531     private static int deepHashCode0(Object o) {
 532         if (o == null) {
 533             return 0;
 534         } else if (!o.getClass().isArray()) {
 535             return o.hashCode();
 536         } else if (o instanceof Object[]) {
 537             return Arrays.deepHashCode((Object[]) o);
 538         } else if (o instanceof byte[]) {
 539             return Arrays.hashCode((byte[]) o);
 540         } else if (o instanceof short[]) {
 541             return Arrays.hashCode((short[]) o);
 542         } else if (o instanceof int[]) {
 543             return Arrays.hashCode((int[]) o);
 544         } else if (o instanceof long[]) {
 545             return Arrays.hashCode((long[]) o);
 546         } else if (o instanceof char[]) {
 547             return Arrays.hashCode((char[]) o);
 548         } else if (o instanceof float[]) {
 549             return Arrays.hashCode((float[]) o);
 550         } else if (o instanceof double[]) {
 551             return Arrays.hashCode((double[]) o);
 552         } else if (o instanceof boolean[]) {
 553             return Arrays.hashCode((boolean[]) o);
 554         } else {
 555             throw shouldNotReachHere();
 556         }
 557     }
 558 
 559     public int valueNumber(Node n) {
 560         int number = 0;
 561         if (canGVN) {
 562             number = startGVNNumber;
 563             for (int i = 0; i < data.getCount(); ++i) {
 564                 Class<?> type = data.getType(i);
 565                 if (type.isPrimitive()) {
 566                     if (type == Integer.TYPE) {
 567                         int intValue = data.getInt(n, i);
 568                         number += intValue;
 569                     } else if (type == Long.TYPE) {
 570                         long longValue = data.getLong(n, i);
 571                         number += longValue ^ (longValue >>> 32);
 572                     } else if (type == Boolean.TYPE) {
 573                         boolean booleanValue = data.getBoolean(n, i);
 574                         if (booleanValue) {
 575                             number += 7;
 576                         }
 577                     } else if (type == Float.TYPE) {
 578                         float floatValue = data.getFloat(n, i);
 579                         number += Float.floatToRawIntBits(floatValue);
 580                     } else if (type == Double.TYPE) {
 581                         double doubleValue = data.getDouble(n, i);
 582                         long longValue = Double.doubleToRawLongBits(doubleValue);
 583                         number += longValue ^ (longValue >>> 32);
 584                     } else if (type == Short.TYPE) {
 585                         short shortValue = data.getShort(n, i);
 586                         number += shortValue;
 587                     } else if (type == Character.TYPE) {
 588                         char charValue = data.getChar(n, i);
 589                         number += charValue;
 590                     } else if (type == Byte.TYPE) {
 591                         byte byteValue = data.getByte(n, i);
 592                         number += byteValue;
 593                     } else {
 594                         assert false : "unhandled property type: " + type;
 595                     }
 596                 } else {
 597                     Object o = data.getObject(n, i);
 598                     number += deepHashCode0(o);
 599                 }
 600                 number *= 13;
 601             }
 602         }
 603         return number;
 604     }
 605 
 606     private static boolean deepEquals0(Object e1, Object e2) {
 607         if (e1 == e2) {
 608             return true;
 609         } else if (e1 == null || e2 == null) {
 610             return false;
 611         } else if (!e1.getClass().isArray() || e1.getClass() != e2.getClass()) {
 612             return e1.equals(e2);
 613         } else if (e1 instanceof Object[] && e2 instanceof Object[]) {
 614             return deepEquals((Object[]) e1, (Object[]) e2);
 615         } else if (e1 instanceof int[]) {
 616             return Arrays.equals((int[]) e1, (int[]) e2);
 617         } else if (e1 instanceof long[]) {
 618             return Arrays.equals((long[]) e1, (long[]) e2);
 619         } else if (e1 instanceof byte[]) {
 620             return Arrays.equals((byte[]) e1, (byte[]) e2);
 621         } else if (e1 instanceof char[]) {
 622             return Arrays.equals((char[]) e1, (char[]) e2);
 623         } else if (e1 instanceof short[]) {
 624             return Arrays.equals((short[]) e1, (short[]) e2);
 625         } else if (e1 instanceof float[]) {
 626             return Arrays.equals((float[]) e1, (float[]) e2);
 627         } else if (e1 instanceof double[]) {
 628             return Arrays.equals((double[]) e1, (double[]) e2);
 629         } else if (e1 instanceof boolean[]) {
 630             return Arrays.equals((boolean[]) e1, (boolean[]) e2);
 631         } else {
 632             throw shouldNotReachHere();
 633         }
 634     }
 635 
 636     private static boolean deepEquals(Object[] a1, Object[] a2) {
 637         int length = a1.length;
 638         if (a2.length != length) {
 639             return false;
 640         }
 641 
 642         for (int i = 0; i < length; i++) {
 643             if (!deepEquals0(a1[i], a2[i])) {
 644                 return false;
 645             }
 646         }
 647         return true;
 648     }
 649 
 650     public boolean dataEquals(Node a, Node b) {
 651         assert a.getClass() == b.getClass();
 652         for (int i = 0; i < data.getCount(); ++i) {
 653             Class<?> type = data.getType(i);
 654             if (type.isPrimitive()) {
 655                 if (type == Integer.TYPE) {
 656                     int aInt = data.getInt(a, i);
 657                     int bInt = data.getInt(b, i);
 658                     if (aInt != bInt) {
 659                         return false;
 660                     }
 661                 } else if (type == Boolean.TYPE) {
 662                     boolean aBoolean = data.getBoolean(a, i);
 663                     boolean bBoolean = data.getBoolean(b, i);
 664                     if (aBoolean != bBoolean) {
 665                         return false;
 666                     }
 667                 } else if (type == Long.TYPE) {
 668                     long aLong = data.getLong(a, i);
 669                     long bLong = data.getLong(b, i);
 670                     if (aLong != bLong) {
 671                         return false;
 672                     }
 673                 } else if (type == Float.TYPE) {
 674                     float aFloat = data.getFloat(a, i);
 675                     float bFloat = data.getFloat(b, i);
 676                     if (aFloat != bFloat) {
 677                         return false;
 678                     }
 679                 } else if (type == Double.TYPE) {
 680                     double aDouble = data.getDouble(a, i);
 681                     double bDouble = data.getDouble(b, i);
 682                     if (aDouble != bDouble) {
 683                         return false;
 684                     }
 685                 } else if (type == Short.TYPE) {
 686                     short aShort = data.getShort(a, i);
 687                     short bShort = data.getShort(b, i);
 688                     if (aShort != bShort) {
 689                         return false;
 690                     }
 691                 } else if (type == Character.TYPE) {
 692                     char aChar = data.getChar(a, i);
 693                     char bChar = data.getChar(b, i);
 694                     if (aChar != bChar) {
 695                         return false;
 696                     }
 697                 } else if (type == Byte.TYPE) {
 698                     byte aByte = data.getByte(a, i);
 699                     byte bByte = data.getByte(b, i);
 700                     if (aByte != bByte) {
 701                         return false;
 702                     }
 703                 } else {
 704                     assert false : "unhandled type: " + type;
 705                 }
 706             } else {
 707                 Object objectA = data.getObject(a, i);
 708                 Object objectB = data.getObject(b, i);
 709                 if (objectA != objectB) {
 710                     if (objectA != null && objectB != null) {
 711                         if (!deepEquals0(objectA, objectB)) {
 712                             return false;
 713                         }
 714                     } else {
 715                         return false;
 716                     }
 717                 }
 718             }
 719         }
 720         return true;
 721     }
 722 
 723     public boolean isValid(Position pos, NodeClass<?> from, Edges fromEdges) {
 724         if (this == from) {
 725             return true;
 726         }
 727         Edges toEdges = getEdges(fromEdges.type());
 728         if (pos.getIndex() >= toEdges.getCount()) {
 729             return false;
 730         }
 731         if (pos.getIndex() >= fromEdges.getCount()) {
 732             return false;
 733         }
 734         return toEdges.isSame(fromEdges, pos.getIndex());
 735     }
 736 
 737     static void updateEdgesInPlace(Node node, InplaceUpdateClosure duplicationReplacement, Edges edges) {
 738         int index = 0;
 739         Type curType = edges.type();
 740         int directCount = edges.getDirectCount();
 741         final long[] curOffsets = edges.getOffsets();
 742         while (index < directCount) {
 743             Node edge = Edges.getNode(node, curOffsets, index);
 744             if (edge != null) {
 745                 Node newEdge = duplicationReplacement.replacement(edge, curType);
 746                 if (curType == Edges.Type.Inputs) {
 747                     node.updateUsages(null, newEdge);
 748                 } else {
 749                     node.updatePredecessor(null, newEdge);
 750                 }
 751                 edges.initializeNode(node, index, newEdge);
 752             }
 753             index++;
 754         }
 755 
 756         while (index < edges.getCount()) {
 757             NodeList<Node> list = Edges.getNodeList(node, curOffsets, index);
 758             if (list != null) {
 759                 edges.initializeList(node, index, updateEdgeListCopy(node, list, duplicationReplacement, curType));
 760             }
 761             index++;
 762         }
 763     }
 764 
 765     void updateInputSuccInPlace(Node node, InplaceUpdateClosure duplicationReplacement) {
 766         updateEdgesInPlace(node, duplicationReplacement, inputs);
 767         updateEdgesInPlace(node, duplicationReplacement, successors);
 768     }
 769 
 770     private static NodeList<Node> updateEdgeListCopy(Node node, NodeList<Node> list, InplaceUpdateClosure duplicationReplacement, Edges.Type type) {
 771         NodeList<Node> result = type == Edges.Type.Inputs ? new NodeInputList<>(node, list.size()) : new NodeSuccessorList<>(node, list.size());
 772 
 773         for (int i = 0; i < list.count(); ++i) {
 774             Node oldNode = list.get(i);
 775             if (oldNode != null) {
 776                 Node newNode = duplicationReplacement.replacement(oldNode, type);
 777                 result.set(i, newNode);
 778             }
 779         }
 780         return result;
 781     }
 782 
 783     /**
 784      * Gets the input or successor edges defined by this node class.
 785      */
 786     public Edges getEdges(Edges.Type type) {
 787         return type == Edges.Type.Inputs ? inputs : successors;
 788     }
 789 
 790     public Edges getInputEdges() {
 791         return inputs;
 792     }
 793 
 794     public Edges getSuccessorEdges() {
 795         return successors;
 796     }
 797 
 798     /**
 799      * Returns a newly allocated node for which no subclass-specific constructor has been called.
 800      */
 801     @SuppressWarnings("unchecked")
 802     public Node allocateInstance() {
 803         try {
 804             Node node = (Node) UNSAFE.allocateInstance(getJavaClass());
 805             node.init((NodeClass<? extends Node>) this);
 806             return node;
 807         } catch (InstantiationException ex) {
 808             throw shouldNotReachHere(ex);
 809         }
 810     }
 811 
 812     public Class<T> getJavaClass() {
 813         return getClazz();
 814     }
 815 
 816     /**
 817      * The template used to build the {@link Verbosity#Name} version. Variable parts are specified
 818      * using &#123;i#inputName&#125; or &#123;p#propertyName&#125;. If no
 819      * {@link NodeInfo#nameTemplate() template} is specified, it uses {@link NodeInfo#shortName()}.
 820      * If none of the two is specified, it returns an empty string.
 821      */
 822     public String getNameTemplate() {
 823         return nameTemplate;
 824     }
 825 
 826     interface InplaceUpdateClosure {
 827 
 828         Node replacement(Node node, Edges.Type type);
 829     }
 830 
 831     static EconomicMap<Node, Node> addGraphDuplicate(final Graph graph, final Graph oldGraph, int estimatedNodeCount, Iterable<? extends Node> nodes, final DuplicationReplacement replacements) {
 832         final EconomicMap<Node, Node> newNodes;
 833         int denseThreshold = oldGraph.getNodeCount() + oldGraph.getNodesDeletedSinceLastCompression() >> 4;
 834         if (estimatedNodeCount > denseThreshold) {
 835             // Use dense map
 836             newNodes = new NodeMap<>(oldGraph);
 837         } else {
 838             // Use sparse map
 839             newNodes = EconomicMap.create(Equivalence.IDENTITY);
 840         }
 841         createNodeDuplicates(graph, nodes, replacements, newNodes);
 842 
 843         InplaceUpdateClosure replacementClosure = new InplaceUpdateClosure() {
 844 
 845             @Override
 846             public Node replacement(Node node, Edges.Type type) {
 847                 Node target = newNodes.get(node);
 848                 if (target == null) {
 849                     Node replacement = node;
 850                     if (replacements != null) {
 851                         replacement = replacements.replacement(node);
 852                     }
 853                     if (replacement != node) {
 854                         target = replacement;
 855                     } else if (node.graph() == graph && type == Edges.Type.Inputs) {
 856                         // patch to the outer world
 857                         target = node;
 858                     }
 859 
 860                 }
 861                 return target;
 862             }
 863 
 864         };
 865 
 866         // re-wire inputs
 867         for (Node oldNode : nodes) {
 868             Node node = newNodes.get(oldNode);
 869             NodeClass<?> nodeClass = node.getNodeClass();
 870             if (replacements == null || replacements.replacement(oldNode) == oldNode) {
 871                 nodeClass.updateInputSuccInPlace(node, replacementClosure);
 872             } else {
 873                 transferEdgesDifferentNodeClass(graph, replacements, newNodes, oldNode, node);
 874             }
 875         }
 876 
 877         return newNodes;
 878     }
 879 
 880     private static void createNodeDuplicates(final Graph graph, Iterable<? extends Node> nodes, final DuplicationReplacement replacements, final EconomicMap<Node, Node> newNodes) {
 881         for (Node node : nodes) {
 882             if (node != null) {
 883                 assert !node.isDeleted() : "trying to duplicate deleted node: " + node;
 884                 Node replacement = node;
 885                 if (replacements != null) {
 886                     replacement = replacements.replacement(node);
 887                 }
 888                 if (replacement != node) {
 889                     assert replacement != null;
 890                     newNodes.put(node, replacement);
 891                 } else {
 892                     Node newNode = node.clone(graph, WithAllEdges);
 893                     assert newNode.getNodeClass().isLeafNode() || newNode.hasNoUsages();
 894                     assert newNode.getClass() == node.getClass();
 895                     newNodes.put(node, newNode);
 896                 }
 897             }
 898         }
 899     }
 900 
 901     private static void transferEdgesDifferentNodeClass(final Graph graph, final DuplicationReplacement replacements, final EconomicMap<Node, Node> newNodes, Node oldNode, Node node) {
 902         transferEdges(graph, replacements, newNodes, oldNode, node, Edges.Type.Inputs);
 903         transferEdges(graph, replacements, newNodes, oldNode, node, Edges.Type.Successors);
 904     }
 905 
 906     private static void transferEdges(final Graph graph, final DuplicationReplacement replacements, final EconomicMap<Node, Node> newNodes, Node oldNode, Node node, Edges.Type type) {
 907         NodeClass<?> nodeClass = node.getNodeClass();
 908         NodeClass<?> oldNodeClass = oldNode.getNodeClass();
 909         Edges oldEdges = oldNodeClass.getEdges(type);
 910         for (Position pos : oldEdges.getPositionsIterable(oldNode)) {
 911             if (!nodeClass.isValid(pos, oldNodeClass, oldEdges)) {
 912                 continue;
 913             }
 914             Node oldEdge = pos.get(oldNode);
 915             if (oldEdge != null) {
 916                 Node target = newNodes.get(oldEdge);
 917                 if (target == null) {
 918                     Node replacement = oldEdge;
 919                     if (replacements != null) {
 920                         replacement = replacements.replacement(oldEdge);
 921                     }
 922                     if (replacement != oldEdge) {
 923                         target = replacement;
 924                     } else if (type == Edges.Type.Inputs && oldEdge.graph() == graph) {
 925                         // patch to the outer world
 926                         target = oldEdge;
 927                     }
 928                 }
 929                 pos.set(node, target);
 930             }
 931         }
 932     }
 933 
 934     /**
 935      * @returns true if the node has no inputs and no successors
 936      */
 937     public boolean isLeafNode() {
 938         return isLeafNode;
 939     }
 940 
 941     public int getLeafId() {
 942         return this.leafId;
 943     }
 944 
 945     public NodeClass<? super T> getSuperNodeClass() {
 946         return superNodeClass;
 947     }
 948 
 949     public long inputsIteration() {
 950         return inputsIteration;
 951     }
 952 
 953     /**
 954      * An iterator that will iterate over edges.
 955      *
 956      * An iterator of this type will not return null values, unless edges are modified concurrently.
 957      * Concurrent modifications are detected by an assertion on a best-effort basis.
 958      */
 959     private static class RawEdgesIterator implements Iterator<Node> {
 960         protected final Node node;
 961         protected long mask;
 962         protected Node nextValue;
 963 
 964         RawEdgesIterator(Node node, long mask) {
 965             this.node = node;
 966             this.mask = mask;
 967         }
 968 
 969         @Override
 970         public boolean hasNext() {
 971             Node next = nextValue;
 972             if (next != null) {
 973                 return true;
 974             } else {
 975                 nextValue = forward();
 976                 return nextValue != null;
 977             }
 978         }
 979 
 980         private Node forward() {
 981             while (mask != 0) {
 982                 Node next = getInput();
 983                 mask = advanceInput();
 984                 if (next != null) {
 985                     return next;
 986                 }
 987             }
 988             return null;
 989         }
 990 
 991         @Override
 992         public Node next() {
 993             Node next = nextValue;
 994             if (next == null) {
 995                 next = forward();
 996                 if (next == null) {
 997                     throw new NoSuchElementException();
 998                 } else {
 999                     return next;
1000                 }
1001             } else {
1002                 nextValue = null;
1003                 return next;
1004             }
1005         }
1006 
1007         public final long advanceInput() {
1008             int state = (int) mask & 0x03;
1009             if (state == 0) {
1010                 // Skip normal field.
1011                 return mask >>> NEXT_EDGE;
1012             } else if (state == 1) {
1013                 // We are iterating a node list.
1014                 if ((mask & 0xFFFF00) != 0) {
1015                     // Node list count is non-zero, decrease by 1.
1016                     return mask - 0x100;
1017                 } else {
1018                     // Node list is finished => go to next input.
1019                     return mask >>> 24;
1020                 }
1021             } else {
1022                 // Need to expand node list.
1023                 NodeList<?> nodeList = Edges.getNodeListUnsafe(node, mask & 0xFC);
1024                 if (nodeList != null) {
1025                     int size = nodeList.size();
1026                     if (size != 0) {
1027                         // Set pointer to upper most index of node list.
1028                         return ((mask >>> NEXT_EDGE) << 24) | (mask & 0xFD) | ((size - 1) << NEXT_EDGE);
1029                     }
1030                 }
1031                 // Node list is empty or null => skip.
1032                 return mask >>> NEXT_EDGE;
1033             }
1034         }
1035 
1036         public Node getInput() {
1037             int state = (int) mask & 0x03;
1038             if (state == 0) {
1039                 return Edges.getNodeUnsafe(node, mask & 0xFC);
1040             } else if (state == 1) {
1041                 // We are iterating a node list.
1042                 NodeList<?> nodeList = Edges.getNodeListUnsafe(node, mask & 0xFC);
1043                 return nodeList.nodes[nodeList.size() - 1 - (int) ((mask >>> NEXT_EDGE) & 0xFFFF)];
1044             } else {
1045                 // Node list needs to expand first.
1046                 return null;
1047             }
1048         }
1049 
1050         @Override
1051         public void remove() {
1052             throw new UnsupportedOperationException();
1053         }
1054 
1055         public Position nextPosition() {
1056             return null;
1057         }
1058     }
1059 
1060     private static final class RawEdgesWithModCountIterator extends RawEdgesIterator {
1061         private final int modCount;
1062 
1063         private RawEdgesWithModCountIterator(Node node, long mask) {
1064             super(node, mask);
1065             assert isModificationCountsEnabled();
1066             this.modCount = node.modCount();
1067         }
1068 
1069         @Override
1070         public boolean hasNext() {
1071             try {
1072                 return super.hasNext();
1073             } finally {
1074                 assert modCount == node.modCount() : "must not be modified";
1075             }
1076         }
1077 
1078         @Override
1079         public Node next() {
1080             try {
1081                 return super.next();
1082             } finally {
1083                 assert modCount == node.modCount() : "must not be modified";
1084             }
1085         }
1086 
1087         @Override
1088         public Position nextPosition() {
1089             try {
1090                 return super.nextPosition();
1091             } finally {
1092                 assert modCount == node.modCount();
1093             }
1094         }
1095     }
1096 
1097     public NodeIterable<Node> getSuccessorIterable(final Node node) {
1098         long mask = this.successorIteration;
1099         return new NodeIterable<Node>() {
1100 
1101             @Override
1102             public Iterator<Node> iterator() {
1103                 if (isModificationCountsEnabled()) {
1104                     return new RawEdgesWithModCountIterator(node, mask);
1105                 } else {
1106                     return new RawEdgesIterator(node, mask);
1107                 }
1108             }
1109 
1110             @Override
1111             public String toString() {
1112                 StringBuilder sb = new StringBuilder();
1113                 Iterator<Node> iterator = iterator();
1114                 boolean first = true;
1115                 sb.append("succs=");
1116                 sb.append('[');
1117                 while (iterator.hasNext()) {
1118                     Node input = iterator.next();
1119                     if (!first) {
1120                         sb.append(", ");
1121                     }
1122                     sb.append(input);
1123                     first = false;
1124                 }
1125                 sb.append(']');
1126                 return sb.toString();
1127             }
1128         };
1129     }
1130 
1131     public NodeIterable<Node> getInputIterable(final Node node) {
1132         long mask = this.inputsIteration;
1133         return new NodeIterable<Node>() {
1134 
1135             @Override
1136             public Iterator<Node> iterator() {
1137                 if (isModificationCountsEnabled()) {
1138                     return new RawEdgesWithModCountIterator(node, mask);
1139                 } else {
1140                     return new RawEdgesIterator(node, mask);
1141                 }
1142             }
1143 
1144             @Override
1145             public String toString() {
1146                 StringBuilder sb = new StringBuilder();
1147                 Iterator<Node> iterator = iterator();
1148                 boolean first = true;
1149                 sb.append("inputs=");
1150                 sb.append('[');
1151                 while (iterator.hasNext()) {
1152                     Node input = iterator.next();
1153                     if (!first) {
1154                         sb.append(", ");
1155                     }
1156                     sb.append(input);
1157                     first = false;
1158                 }
1159                 sb.append(']');
1160                 return sb.toString();
1161             }
1162         };
1163     }
1164 
1165     public boolean equalSuccessors(Node node, Node other) {
1166         return equalEdges(node, other, successorIteration);
1167     }
1168 
1169     public boolean equalInputs(Node node, Node other) {
1170         return equalEdges(node, other, inputsIteration);
1171     }
1172 
1173     private boolean equalEdges(Node node, Node other, long mask) {
1174         long myMask = mask;
1175         assert other.getNodeClass() == this;
1176         while (myMask != 0) {
1177             long offset = (myMask & OFFSET_MASK);
1178             if ((myMask & LIST_MASK) == 0) {
1179                 Object v1 = Edges.getNodeUnsafe(node, offset);
1180                 Object v2 = Edges.getNodeUnsafe(other, offset);
1181                 if (v1 != v2) {
1182                     return false;
1183                 }
1184             } else {
1185                 Object v1 = Edges.getNodeListUnsafe(node, offset);
1186                 Object v2 = Edges.getNodeListUnsafe(other, offset);
1187                 if (!Objects.equals(v1, v2)) {
1188                     return false;
1189                 }
1190             }
1191             myMask >>>= NEXT_EDGE;
1192         }
1193         return true;
1194     }
1195 
1196     public void pushInputs(Node node, NodeStack stack) {
1197         long myMask = this.inputsIteration;
1198         while (myMask != 0) {
1199             long offset = (myMask & OFFSET_MASK);
1200             if ((myMask & LIST_MASK) == 0) {
1201                 Node curNode = Edges.getNodeUnsafe(node, offset);
1202                 if (curNode != null) {
1203                     stack.push(curNode);
1204                 }
1205             } else {
1206                 pushAllHelper(stack, node, offset);
1207             }
1208             myMask >>>= NEXT_EDGE;
1209         }
1210     }
1211 
1212     private static void pushAllHelper(NodeStack stack, Node node, long offset) {
1213         NodeList<Node> list = Edges.getNodeListUnsafe(node, offset);
1214         if (list != null) {
1215             for (int i = 0; i < list.size(); ++i) {
1216                 Node curNode = list.get(i);
1217                 if (curNode != null) {
1218                     stack.push(curNode);
1219                 }
1220             }
1221         }
1222     }
1223 
1224     public void applySuccessors(Node node, EdgeVisitor consumer) {
1225         applyEdges(node, consumer, this.successorIteration);
1226     }
1227 
1228     public void applyInputs(Node node, EdgeVisitor consumer) {
1229         applyEdges(node, consumer, this.inputsIteration);
1230     }
1231 
1232     private static void applyEdges(Node node, EdgeVisitor consumer, long mask) {
1233         long myMask = mask;
1234         while (myMask != 0) {
1235             long offset = (myMask & OFFSET_MASK);
1236             if ((myMask & LIST_MASK) == 0) {
1237                 Node curNode = Edges.getNodeUnsafe(node, offset);
1238                 if (curNode != null) {
1239                     Node newNode = consumer.apply(node, curNode);
1240                     if (newNode != curNode) {
1241                         Edges.putNodeUnsafe(node, offset, newNode);
1242                     }
1243                 }
1244             } else {
1245                 applyHelper(node, consumer, offset);
1246             }
1247             myMask >>>= NEXT_EDGE;
1248         }
1249     }
1250 
1251     private static void applyHelper(Node node, EdgeVisitor consumer, long offset) {
1252         NodeList<Node> list = Edges.getNodeListUnsafe(node, offset);
1253         if (list != null) {
1254             for (int i = 0; i < list.size(); ++i) {
1255                 Node curNode = list.get(i);
1256                 if (curNode != null) {
1257                     Node newNode = consumer.apply(node, curNode);
1258                     if (newNode != curNode) {
1259                         list.initialize(i, newNode);
1260                     }
1261                 }
1262             }
1263         }
1264     }
1265 
1266     public void unregisterAtSuccessorsAsPredecessor(Node node) {
1267         long myMask = this.successorIteration;
1268         while (myMask != 0) {
1269             long offset = (myMask & OFFSET_MASK);
1270             if ((myMask & LIST_MASK) == 0) {
1271                 Node curNode = Edges.getNodeUnsafe(node, offset);
1272                 if (curNode != null) {
1273                     node.updatePredecessor(curNode, null);
1274                     Edges.putNodeUnsafe(node, offset, null);
1275                 }
1276             } else {
1277                 unregisterAtSuccessorsAsPredecessorHelper(node, offset);
1278             }
1279             myMask >>>= NEXT_EDGE;
1280         }
1281     }
1282 
1283     private static void unregisterAtSuccessorsAsPredecessorHelper(Node node, long offset) {
1284         NodeList<Node> list = Edges.getNodeListUnsafe(node, offset);
1285         if (list != null) {
1286             for (int i = 0; i < list.size(); ++i) {
1287                 Node curNode = list.get(i);
1288                 if (curNode != null) {
1289                     node.updatePredecessor(curNode, null);
1290                 }
1291             }
1292             list.clearWithoutUpdate();
1293         }
1294     }
1295 
1296     public void registerAtSuccessorsAsPredecessor(Node node) {
1297         long myMask = this.successorIteration;
1298         while (myMask != 0) {
1299             long offset = (myMask & OFFSET_MASK);
1300             if ((myMask & LIST_MASK) == 0) {
1301                 Node curNode = Edges.getNodeUnsafe(node, offset);
1302                 if (curNode != null) {
1303                     assert curNode.isAlive() : "Successor not alive";
1304                     node.updatePredecessor(null, curNode);
1305                 }
1306             } else {
1307                 registerAtSuccessorsAsPredecessorHelper(node, offset);
1308             }
1309             myMask >>>= NEXT_EDGE;
1310         }
1311     }
1312 
1313     private static void registerAtSuccessorsAsPredecessorHelper(Node node, long offset) {
1314         NodeList<Node> list = Edges.getNodeListUnsafe(node, offset);
1315         if (list != null) {
1316             for (int i = 0; i < list.size(); ++i) {
1317                 Node curNode = list.get(i);
1318                 if (curNode != null) {
1319                     assert curNode.isAlive() : "Successor not alive";
1320                     node.updatePredecessor(null, curNode);
1321                 }
1322             }
1323         }
1324     }
1325 
1326     public boolean replaceFirstInput(Node node, Node key, Node replacement) {
1327         return replaceFirstEdge(node, key, replacement, this.inputsIteration);
1328     }
1329 
1330     public boolean replaceFirstSuccessor(Node node, Node key, Node replacement) {
1331         return replaceFirstEdge(node, key, replacement, this.successorIteration);
1332     }
1333 
1334     public static boolean replaceFirstEdge(Node node, Node key, Node replacement, long mask) {
1335         long myMask = mask;
1336         while (myMask != 0) {
1337             long offset = (myMask & OFFSET_MASK);
1338             if ((myMask & LIST_MASK) == 0) {
1339                 Object curNode = Edges.getNodeUnsafe(node, offset);
1340                 if (curNode == key) {
1341                     Edges.putNodeUnsafe(node, offset, replacement);
1342                     return true;
1343                 }
1344             } else {
1345                 NodeList<Node> list = Edges.getNodeListUnsafe(node, offset);
1346                 if (list != null && list.replaceFirst(key, replacement)) {
1347                     return true;
1348                 }
1349             }
1350             myMask >>>= NEXT_EDGE;
1351         }
1352         return false;
1353     }
1354 
1355     public void registerAtInputsAsUsage(Node node) {
1356         long myMask = this.inputsIteration;
1357         while (myMask != 0) {
1358             long offset = (myMask & OFFSET_MASK);
1359             if ((myMask & LIST_MASK) == 0) {
1360                 Node curNode = Edges.getNodeUnsafe(node, offset);
1361                 if (curNode != null) {
1362                     assert curNode.isAlive() : "Input not alive " + curNode;
1363                     curNode.addUsage(node);
1364                 }
1365             } else {
1366                 registerAtInputsAsUsageHelper(node, offset);
1367             }
1368             myMask >>>= NEXT_EDGE;
1369         }
1370     }
1371 
1372     private static void registerAtInputsAsUsageHelper(Node node, long offset) {
1373         NodeList<Node> list = Edges.getNodeListUnsafe(node, offset);
1374         if (list != null) {
1375             for (int i = 0; i < list.size(); ++i) {
1376                 Node curNode = list.get(i);
1377                 if (curNode != null) {
1378                     assert curNode.isAlive() : "Input not alive";
1379                     curNode.addUsage(node);
1380                 }
1381             }
1382         }
1383     }
1384 
1385     public void unregisterAtInputsAsUsage(Node node) {
1386         long myMask = this.inputsIteration;
1387         while (myMask != 0) {
1388             long offset = (myMask & OFFSET_MASK);
1389             if ((myMask & LIST_MASK) == 0) {
1390                 Node curNode = Edges.getNodeUnsafe(node, offset);
1391                 if (curNode != null) {
1392                     node.removeThisFromUsages(curNode);
1393                     if (curNode.hasNoUsages()) {
1394                         node.maybeNotifyZeroUsages(curNode);
1395                     }
1396                     Edges.putNodeUnsafe(node, offset, null);
1397                 }
1398             } else {
1399                 unregisterAtInputsAsUsageHelper(node, offset);
1400             }
1401             myMask >>>= NEXT_EDGE;
1402         }
1403     }
1404 
1405     private static void unregisterAtInputsAsUsageHelper(Node node, long offset) {
1406         NodeList<Node> list = Edges.getNodeListUnsafe(node, offset);
1407         if (list != null) {
1408             for (int i = 0; i < list.size(); ++i) {
1409                 Node curNode = list.get(i);
1410                 if (curNode != null) {
1411                     node.removeThisFromUsages(curNode);
1412                     if (curNode.hasNoUsages()) {
1413                         node.maybeNotifyZeroUsages(curNode);
1414                     }
1415                 }
1416             }
1417             list.clearWithoutUpdate();
1418         }
1419     }
1420 }