| /* |
| * Copyright (c) 2011, 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 org.graalvm.compiler.graph; |
| |
| import static org.graalvm.compiler.core.common.Fields.translateInto; |
| import static org.graalvm.compiler.debug.GraalError.shouldNotReachHere; |
| import static org.graalvm.compiler.graph.Edges.translateInto; |
| import static org.graalvm.compiler.graph.Graph.isModificationCountsEnabled; |
| import static org.graalvm.compiler.graph.InputEdges.translateInto; |
| import static org.graalvm.compiler.graph.Node.WithAllEdges; |
| import static org.graalvm.compiler.graph.Node.newIdentityMap; |
| import static org.graalvm.compiler.graph.UnsafeAccess.UNSAFE; |
| |
| import java.lang.annotation.Annotation; |
| import java.lang.reflect.AnnotatedElement; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Modifier; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.EnumSet; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.NoSuchElementException; |
| import java.util.Objects; |
| import java.util.concurrent.atomic.AtomicInteger; |
| |
| import org.graalvm.compiler.core.common.FieldIntrospection; |
| import org.graalvm.compiler.core.common.Fields; |
| import org.graalvm.compiler.core.common.FieldsScanner; |
| import org.graalvm.compiler.debug.Debug; |
| import org.graalvm.compiler.debug.DebugCloseable; |
| import org.graalvm.compiler.debug.DebugCounter; |
| import org.graalvm.compiler.debug.DebugTimer; |
| import org.graalvm.compiler.debug.Fingerprint; |
| import org.graalvm.compiler.debug.GraalError; |
| import org.graalvm.compiler.graph.Edges.Type; |
| import org.graalvm.compiler.graph.Graph.DuplicationReplacement; |
| import org.graalvm.compiler.graph.Node.EdgeVisitor; |
| import org.graalvm.compiler.graph.Node.Input; |
| import org.graalvm.compiler.graph.Node.OptionalInput; |
| import org.graalvm.compiler.graph.Node.Successor; |
| import org.graalvm.compiler.graph.iterators.NodeIterable; |
| import org.graalvm.compiler.graph.spi.Canonicalizable; |
| import org.graalvm.compiler.graph.spi.Canonicalizable.BinaryCommutative; |
| import org.graalvm.compiler.graph.spi.Simplifiable; |
| import org.graalvm.compiler.nodeinfo.InputType; |
| import org.graalvm.compiler.nodeinfo.NodeCycles; |
| import org.graalvm.compiler.nodeinfo.NodeInfo; |
| import org.graalvm.compiler.nodeinfo.NodeSize; |
| import org.graalvm.compiler.nodeinfo.Verbosity; |
| import org.graalvm.compiler.options.Option; |
| import org.graalvm.compiler.options.OptionValue; |
| import org.graalvm.compiler.options.StableOptionValue; |
| |
| /** |
| * Metadata for every {@link Node} type. The metadata includes: |
| * <ul> |
| * <li>The offsets of fields annotated with {@link Input} and {@link Successor} as well as methods |
| * for iterating over such fields.</li> |
| * <li>The identifier for an {@link IterableNodeType} class.</li> |
| * </ul> |
| */ |
| public final class NodeClass<T> extends FieldIntrospection<T> { |
| |
| public static class Options { |
| // @formatter:off |
| @Option(help = "Verifies that receivers of NodeInfo#size() and NodeInfo#cycles() do not have UNSET values.") |
| public static final OptionValue<Boolean> VerifyNodeCostOnAccess = new StableOptionValue<>(false); |
| // @formatter:on |
| } |
| |
| // Timers for creation of a NodeClass instance |
| private static final DebugTimer Init_FieldScanning = Debug.timer("NodeClass.Init.FieldScanning"); |
| private static final DebugTimer Init_FieldScanningInner = Debug.timer("NodeClass.Init.FieldScanning.Inner"); |
| private static final DebugTimer Init_AnnotationParsing = Debug.timer("NodeClass.Init.AnnotationParsing"); |
| private static final DebugTimer Init_Edges = Debug.timer("NodeClass.Init.Edges"); |
| private static final DebugTimer Init_Data = Debug.timer("NodeClass.Init.Data"); |
| private static final DebugTimer Init_AllowedUsages = Debug.timer("NodeClass.Init.AllowedUsages"); |
| private static final DebugTimer Init_IterableIds = Debug.timer("NodeClass.Init.IterableIds"); |
| |
| public static final long MAX_EDGES = 8; |
| public static final long MAX_LIST_EDGES = 6; |
| public static final long OFFSET_MASK = 0xFC; |
| public static final long LIST_MASK = 0x01; |
| public static final long NEXT_EDGE = 0x08; |
| |
| @SuppressWarnings("try") |
| private static <T extends Annotation> T getAnnotationTimed(AnnotatedElement e, Class<T> annotationClass) { |
| try (DebugCloseable s = Init_AnnotationParsing.start()) { |
| return e.getAnnotation(annotationClass); |
| } |
| } |
| |
| /** |
| * Gets the {@link NodeClass} associated with a given {@link Class}. |
| */ |
| public static <T> NodeClass<T> create(Class<T> c) { |
| assert get(c) == null; |
| Class<? super T> superclass = c.getSuperclass(); |
| NodeClass<? super T> nodeSuperclass = null; |
| if (superclass != NODE_CLASS) { |
| nodeSuperclass = get(superclass); |
| } |
| return new NodeClass<>(c, nodeSuperclass); |
| } |
| |
| @SuppressWarnings("unchecked") |
| public static <T> NodeClass<T> get(Class<T> superclass) { |
| try { |
| Field field = superclass.getDeclaredField("TYPE"); |
| field.setAccessible(true); |
| return (NodeClass<T>) field.get(null); |
| } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| private static final Class<?> NODE_CLASS = Node.class; |
| private static final Class<?> INPUT_LIST_CLASS = NodeInputList.class; |
| private static final Class<?> SUCCESSOR_LIST_CLASS = NodeSuccessorList.class; |
| |
| private static AtomicInteger nextIterableId = new AtomicInteger(); |
| |
| private final InputEdges inputs; |
| private final SuccessorEdges successors; |
| private final NodeClass<? super T> superNodeClass; |
| |
| private final boolean canGVN; |
| private final int startGVNNumber; |
| private final String nameTemplate; |
| private final int iterableId; |
| private final EnumSet<InputType> allowedUsageTypes; |
| private int[] iterableIds; |
| private final long inputsIteration; |
| private final long successorIteration; |
| |
| private static final DebugCounter ITERABLE_NODE_TYPES = Debug.counter("IterableNodeTypes"); |
| private final DebugCounter nodeIterableCount; |
| |
| /** |
| * Determines if this node type implements {@link Canonicalizable}. |
| */ |
| private final boolean isCanonicalizable; |
| |
| /** |
| * Determines if this node type implements {@link BinaryCommutative}. |
| */ |
| private final boolean isCommutative; |
| |
| /** |
| * Determines if this node type implements {@link Simplifiable}. |
| */ |
| private final boolean isSimplifiable; |
| private final boolean isLeafNode; |
| |
| public NodeClass(Class<T> clazz, NodeClass<? super T> superNodeClass) { |
| this(clazz, superNodeClass, new FieldsScanner.DefaultCalcOffset(), null, 0); |
| } |
| |
| @SuppressWarnings("try") |
| public NodeClass(Class<T> clazz, NodeClass<? super T> superNodeClass, FieldsScanner.CalcOffset calcOffset, int[] presetIterableIds, int presetIterableId) { |
| super(clazz); |
| this.superNodeClass = superNodeClass; |
| assert NODE_CLASS.isAssignableFrom(clazz); |
| |
| this.isCanonicalizable = Canonicalizable.class.isAssignableFrom(clazz); |
| this.isCommutative = BinaryCommutative.class.isAssignableFrom(clazz); |
| if (Canonicalizable.Unary.class.isAssignableFrom(clazz) || Canonicalizable.Binary.class.isAssignableFrom(clazz)) { |
| assert Canonicalizable.Unary.class.isAssignableFrom(clazz) ^ Canonicalizable.Binary.class.isAssignableFrom(clazz) : clazz + " should implement either Unary or Binary, not both"; |
| } |
| |
| this.isSimplifiable = Simplifiable.class.isAssignableFrom(clazz); |
| |
| NodeFieldsScanner fs = new NodeFieldsScanner(calcOffset, superNodeClass); |
| try (DebugCloseable t = Init_FieldScanning.start()) { |
| fs.scan(clazz, clazz.getSuperclass(), false); |
| } |
| |
| try (DebugCloseable t1 = Init_Edges.start()) { |
| successors = new SuccessorEdges(fs.directSuccessors, fs.successors); |
| successorIteration = computeIterationMask(successors.type(), successors.getDirectCount(), successors.getOffsets()); |
| inputs = new InputEdges(fs.directInputs, fs.inputs); |
| inputsIteration = computeIterationMask(inputs.type(), inputs.getDirectCount(), inputs.getOffsets()); |
| } |
| try (DebugCloseable t1 = Init_Data.start()) { |
| data = new Fields(fs.data); |
| } |
| |
| isLeafNode = inputs.getCount() + successors.getCount() == 0; |
| |
| canGVN = Node.ValueNumberable.class.isAssignableFrom(clazz); |
| startGVNNumber = clazz.getName().hashCode(); |
| |
| NodeInfo info = getAnnotationTimed(clazz, NodeInfo.class); |
| assert info != null : "Missing NodeInfo annotation on " + clazz; |
| this.nameTemplate = info.nameTemplate(); |
| |
| try (DebugCloseable t1 = Init_AllowedUsages.start()) { |
| allowedUsageTypes = superNodeClass == null ? EnumSet.noneOf(InputType.class) : superNodeClass.allowedUsageTypes.clone(); |
| allowedUsageTypes.addAll(Arrays.asList(info.allowedUsageTypes())); |
| } |
| |
| if (presetIterableIds != null) { |
| this.iterableIds = presetIterableIds; |
| this.iterableId = presetIterableId; |
| } else if (IterableNodeType.class.isAssignableFrom(clazz)) { |
| ITERABLE_NODE_TYPES.increment(); |
| try (DebugCloseable t1 = Init_IterableIds.start()) { |
| this.iterableId = nextIterableId.getAndIncrement(); |
| |
| NodeClass<?> snc = superNodeClass; |
| while (snc != null && IterableNodeType.class.isAssignableFrom(snc.getClazz())) { |
| snc.addIterableId(iterableId); |
| snc = snc.superNodeClass; |
| } |
| |
| this.iterableIds = new int[]{iterableId}; |
| } |
| } else { |
| this.iterableId = Node.NOT_ITERABLE; |
| this.iterableIds = null; |
| } |
| nodeIterableCount = Debug.counter("NodeIterable_%s", clazz); |
| assert verifyIterableIds(); |
| |
| try (Debug.Scope scope = Debug.scope("NodeCosts")) { |
| /* |
| * Note: We do not check for the existence of the node cost annotations during |
| * construction as not every node needs to have them set. However if costs are queried, |
| * after the construction of the node class, they must be properly set. This is |
| * important as we can not trust our cost model if there are unspecified nodes. Nodes |
| * that do not need cost annotations are e.g. abstractions like FixedNode or |
| * FloatingNode or ValueNode. Sub classes where costs are not specified will ask the |
| * superclass for their costs during node class initialization. Therefore getters for |
| * cycles and size can omit verification during creation. |
| */ |
| NodeCycles c = info.cycles(); |
| if (c == NodeCycles.CYCLES_UNSET) { |
| cycles = superNodeClass != null ? superNodeClass.cycles : NodeCycles.CYCLES_UNSET; |
| } else { |
| cycles = c; |
| } |
| assert cycles != null; |
| NodeSize s = info.size(); |
| if (s == NodeSize.SIZE_UNSET) { |
| size = superNodeClass != null ? superNodeClass.size : NodeSize.SIZE_UNSET; |
| } else { |
| size = s; |
| } |
| assert size != null; |
| Debug.log("Node cost for node of type __| %s |_, cycles:%s,size:%s", clazz, cycles, size); |
| } |
| |
| } |
| |
| private final NodeCycles cycles; |
| private final NodeSize size; |
| |
| public NodeCycles cycles() { |
| if (Options.VerifyNodeCostOnAccess.getValue() && cycles == NodeCycles.CYCLES_UNSET) { |
| throw new GraalError("Missing NodeCycles specification in the @NodeInfo annotation of the node %s", this); |
| } |
| return cycles; |
| } |
| |
| public NodeSize size() { |
| if (Options.VerifyNodeCostOnAccess.getValue() && size == NodeSize.SIZE_UNSET) { |
| throw new GraalError("Missing NodeSize specification in the @NodeInfo annotation of the node %s", this); |
| } |
| return size; |
| } |
| |
| public static long computeIterationMask(Type type, int directCount, long[] offsets) { |
| long mask = 0; |
| if (offsets.length > NodeClass.MAX_EDGES) { |
| throw new GraalError("Exceeded maximum of %d edges (%s)", NodeClass.MAX_EDGES, type); |
| } |
| if (offsets.length - directCount > NodeClass.MAX_LIST_EDGES) { |
| throw new GraalError("Exceeded maximum of %d list edges (%s)", NodeClass.MAX_LIST_EDGES, type); |
| } |
| |
| for (int i = offsets.length - 1; i >= 0; i--) { |
| long offset = offsets[i]; |
| assert ((offset & 0xFF) == offset) : "field offset too large!"; |
| mask <<= NodeClass.NEXT_EDGE; |
| mask |= offset; |
| if (i >= directCount) { |
| mask |= 0x3; |
| } |
| } |
| return mask; |
| } |
| |
| private synchronized void addIterableId(int newIterableId) { |
| assert !containsId(newIterableId, iterableIds); |
| int[] copy = Arrays.copyOf(iterableIds, iterableIds.length + 1); |
| copy[iterableIds.length] = newIterableId; |
| iterableIds = copy; |
| } |
| |
| private boolean verifyIterableIds() { |
| NodeClass<?> snc = superNodeClass; |
| while (snc != null && IterableNodeType.class.isAssignableFrom(snc.getClazz())) { |
| assert containsId(iterableId, snc.iterableIds); |
| snc = snc.superNodeClass; |
| } |
| return true; |
| } |
| |
| private static boolean containsId(int iterableId, int[] iterableIds) { |
| for (int i : iterableIds) { |
| if (i == iterableId) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private String shortName; |
| |
| public String shortName() { |
| if (shortName == null) { |
| NodeInfo info = getClazz().getAnnotation(NodeInfo.class); |
| if (!info.shortName().isEmpty()) { |
| shortName = info.shortName(); |
| } else { |
| String localShortName = getClazz().getSimpleName(); |
| if (localShortName.endsWith("Node") && !localShortName.equals("StartNode") && !localShortName.equals("EndNode")) { |
| shortName = localShortName.substring(0, localShortName.length() - 4); |
| } else { |
| shortName = localShortName; |
| } |
| } |
| } |
| return shortName; |
| } |
| |
| @Override |
| public Fields[] getAllFields() { |
| return new Fields[]{data, inputs, successors}; |
| } |
| |
| public int[] iterableIds() { |
| nodeIterableCount.increment(); |
| return iterableIds; |
| } |
| |
| public int iterableId() { |
| return iterableId; |
| } |
| |
| public boolean valueNumberable() { |
| return canGVN; |
| } |
| |
| /** |
| * Determines if this node type implements {@link Canonicalizable}. |
| */ |
| public boolean isCanonicalizable() { |
| return isCanonicalizable; |
| } |
| |
| /** |
| * Determines if this node type implements {@link BinaryCommutative}. |
| */ |
| public boolean isCommutative() { |
| return isCommutative; |
| } |
| |
| /** |
| * Determines if this node type implements {@link Simplifiable}. |
| */ |
| public boolean isSimplifiable() { |
| return isSimplifiable; |
| } |
| |
| static int allocatedNodeIterabledIds() { |
| return nextIterableId.get(); |
| } |
| |
| public EnumSet<InputType> getAllowedUsageTypes() { |
| return allowedUsageTypes; |
| } |
| |
| /** |
| * Describes a field representing an input or successor edge in a node. |
| */ |
| protected static class EdgeInfo extends FieldsScanner.FieldInfo { |
| |
| public EdgeInfo(long offset, String name, Class<?> type, Class<?> declaringClass) { |
| super(offset, name, type, declaringClass); |
| } |
| |
| /** |
| * Sorts non-list edges before list edges. |
| */ |
| @Override |
| public int compareTo(FieldsScanner.FieldInfo o) { |
| if (NodeList.class.isAssignableFrom(o.type)) { |
| if (!NodeList.class.isAssignableFrom(type)) { |
| return -1; |
| } |
| } else { |
| if (NodeList.class.isAssignableFrom(type)) { |
| return 1; |
| } |
| } |
| return super.compareTo(o); |
| } |
| } |
| |
| /** |
| * Describes a field representing an {@linkplain Type#Inputs input} edge in a node. |
| */ |
| protected static class InputInfo extends EdgeInfo { |
| final InputType inputType; |
| final boolean optional; |
| |
| public InputInfo(long offset, String name, Class<?> type, Class<?> declaringClass, InputType inputType, boolean optional) { |
| super(offset, name, type, declaringClass); |
| this.inputType = inputType; |
| this.optional = optional; |
| } |
| |
| @Override |
| public String toString() { |
| return super.toString() + "{inputType=" + inputType + ", optional=" + optional + "}"; |
| } |
| } |
| |
| protected static class NodeFieldsScanner extends FieldsScanner { |
| |
| public final ArrayList<InputInfo> inputs = new ArrayList<>(); |
| public final ArrayList<EdgeInfo> successors = new ArrayList<>(); |
| int directInputs; |
| int directSuccessors; |
| |
| protected NodeFieldsScanner(FieldsScanner.CalcOffset calc, NodeClass<?> superNodeClass) { |
| super(calc); |
| if (superNodeClass != null) { |
| translateInto(superNodeClass.inputs, inputs); |
| translateInto(superNodeClass.successors, successors); |
| translateInto(superNodeClass.data, data); |
| directInputs = superNodeClass.inputs.getDirectCount(); |
| directSuccessors = superNodeClass.successors.getDirectCount(); |
| } |
| } |
| |
| @SuppressWarnings("try") |
| @Override |
| protected void scanField(Field field, long offset) { |
| Input inputAnnotation = getAnnotationTimed(field, Node.Input.class); |
| OptionalInput optionalInputAnnotation = getAnnotationTimed(field, Node.OptionalInput.class); |
| Successor successorAnnotation = getAnnotationTimed(field, Successor.class); |
| try (DebugCloseable s = Init_FieldScanningInner.start()) { |
| Class<?> type = field.getType(); |
| int modifiers = field.getModifiers(); |
| |
| if (inputAnnotation != null || optionalInputAnnotation != null) { |
| assert successorAnnotation == null : "field cannot be both input and successor"; |
| if (INPUT_LIST_CLASS.isAssignableFrom(type)) { |
| // NodeInputList fields should not be final since they are |
| // written (via Unsafe) in clearInputs() |
| GraalError.guarantee(!Modifier.isFinal(modifiers), "NodeInputList input field %s should not be final", field); |
| GraalError.guarantee(!Modifier.isPublic(modifiers), "NodeInputList input field %s should not be public", field); |
| } else { |
| GraalError.guarantee(NODE_CLASS.isAssignableFrom(type) || type.isInterface(), "invalid input type: %s", type); |
| GraalError.guarantee(!Modifier.isFinal(modifiers), "Node input field %s should not be final", field); |
| directInputs++; |
| } |
| InputType inputType; |
| if (inputAnnotation != null) { |
| assert optionalInputAnnotation == null : "inputs can either be optional or non-optional"; |
| inputType = inputAnnotation.value(); |
| } else { |
| inputType = optionalInputAnnotation.value(); |
| } |
| inputs.add(new InputInfo(offset, field.getName(), type, field.getDeclaringClass(), inputType, field.isAnnotationPresent(Node.OptionalInput.class))); |
| } else if (successorAnnotation != null) { |
| if (SUCCESSOR_LIST_CLASS.isAssignableFrom(type)) { |
| // NodeSuccessorList fields should not be final since they are |
| // written (via Unsafe) in clearSuccessors() |
| GraalError.guarantee(!Modifier.isFinal(modifiers), "NodeSuccessorList successor field % should not be final", field); |
| GraalError.guarantee(!Modifier.isPublic(modifiers), "NodeSuccessorList successor field %s should not be public", field); |
| } else { |
| GraalError.guarantee(NODE_CLASS.isAssignableFrom(type), "invalid successor type: %s", type); |
| GraalError.guarantee(!Modifier.isFinal(modifiers), "Node successor field %s should not be final", field); |
| directSuccessors++; |
| } |
| successors.add(new EdgeInfo(offset, field.getName(), type, field.getDeclaringClass())); |
| } else { |
| GraalError.guarantee(!NODE_CLASS.isAssignableFrom(type) || field.getName().equals("Null"), "suspicious node field: %s", field); |
| GraalError.guarantee(!INPUT_LIST_CLASS.isAssignableFrom(type), "suspicious node input list field: %s", field); |
| GraalError.guarantee(!SUCCESSOR_LIST_CLASS.isAssignableFrom(type), "suspicious node successor list field: %s", field); |
| super.scanField(field, offset); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder str = new StringBuilder(); |
| str.append("NodeClass ").append(getClazz().getSimpleName()).append(" ["); |
| inputs.appendFields(str); |
| str.append("] ["); |
| successors.appendFields(str); |
| str.append("] ["); |
| data.appendFields(str); |
| str.append("]"); |
| return str.toString(); |
| } |
| |
| private static int deepHashCode0(Object o) { |
| if (o instanceof Object[]) { |
| return Arrays.deepHashCode((Object[]) o); |
| } else if (o instanceof byte[]) { |
| return Arrays.hashCode((byte[]) o); |
| } else if (o instanceof short[]) { |
| return Arrays.hashCode((short[]) o); |
| } else if (o instanceof int[]) { |
| return Arrays.hashCode((int[]) o); |
| } else if (o instanceof long[]) { |
| return Arrays.hashCode((long[]) o); |
| } else if (o instanceof char[]) { |
| return Arrays.hashCode((char[]) o); |
| } else if (o instanceof float[]) { |
| return Arrays.hashCode((float[]) o); |
| } else if (o instanceof double[]) { |
| return Arrays.hashCode((double[]) o); |
| } else if (o instanceof boolean[]) { |
| return Arrays.hashCode((boolean[]) o); |
| } else if (o != null) { |
| return o.hashCode(); |
| } else { |
| return 0; |
| } |
| } |
| |
| public int valueNumber(Node n) { |
| int number = 0; |
| if (canGVN) { |
| number = startGVNNumber; |
| for (int i = 0; i < data.getCount(); ++i) { |
| Class<?> type = data.getType(i); |
| if (type.isPrimitive()) { |
| if (type == Integer.TYPE) { |
| int intValue = data.getInt(n, i); |
| number += intValue; |
| } else if (type == Long.TYPE) { |
| long longValue = data.getLong(n, i); |
| number += longValue ^ (longValue >>> 32); |
| } else if (type == Boolean.TYPE) { |
| boolean booleanValue = data.getBoolean(n, i); |
| if (booleanValue) { |
| number += 7; |
| } |
| } else if (type == Float.TYPE) { |
| float floatValue = data.getFloat(n, i); |
| number += Float.floatToRawIntBits(floatValue); |
| } else if (type == Double.TYPE) { |
| double doubleValue = data.getDouble(n, i); |
| long longValue = Double.doubleToRawLongBits(doubleValue); |
| number += longValue ^ (longValue >>> 32); |
| } else if (type == Short.TYPE) { |
| short shortValue = data.getShort(n, i); |
| number += shortValue; |
| } else if (type == Character.TYPE) { |
| char charValue = data.getChar(n, i); |
| number += charValue; |
| } else if (type == Byte.TYPE) { |
| byte byteValue = data.getByte(n, i); |
| number += byteValue; |
| } else { |
| assert false : "unhandled property type: " + type; |
| } |
| } else { |
| Object o = data.getObject(n, i); |
| number += deepHashCode0(o); |
| } |
| number *= 13; |
| } |
| } |
| return number; |
| } |
| |
| private static boolean deepEquals0(Object e1, Object e2) { |
| assert e1 != null; |
| boolean eq; |
| if (e1 instanceof Object[] && e2 instanceof Object[]) { |
| eq = Arrays.deepEquals((Object[]) e1, (Object[]) e2); |
| } else if (e1 instanceof byte[] && e2 instanceof byte[]) { |
| eq = Arrays.equals((byte[]) e1, (byte[]) e2); |
| } else if (e1 instanceof short[] && e2 instanceof short[]) { |
| eq = Arrays.equals((short[]) e1, (short[]) e2); |
| } else if (e1 instanceof int[] && e2 instanceof int[]) { |
| eq = Arrays.equals((int[]) e1, (int[]) e2); |
| } else if (e1 instanceof long[] && e2 instanceof long[]) { |
| eq = Arrays.equals((long[]) e1, (long[]) e2); |
| } else if (e1 instanceof char[] && e2 instanceof char[]) { |
| eq = Arrays.equals((char[]) e1, (char[]) e2); |
| } else if (e1 instanceof float[] && e2 instanceof float[]) { |
| eq = Arrays.equals((float[]) e1, (float[]) e2); |
| } else if (e1 instanceof double[] && e2 instanceof double[]) { |
| eq = Arrays.equals((double[]) e1, (double[]) e2); |
| } else if (e1 instanceof boolean[] && e2 instanceof boolean[]) { |
| eq = Arrays.equals((boolean[]) e1, (boolean[]) e2); |
| } else { |
| eq = e1.equals(e2); |
| } |
| return eq; |
| } |
| |
| public boolean dataEquals(Node a, Node b) { |
| assert a.getClass() == b.getClass(); |
| for (int i = 0; i < data.getCount(); ++i) { |
| Class<?> type = data.getType(i); |
| if (type.isPrimitive()) { |
| if (type == Integer.TYPE) { |
| int aInt = data.getInt(a, i); |
| int bInt = data.getInt(b, i); |
| if (aInt != bInt) { |
| return false; |
| } |
| } else if (type == Boolean.TYPE) { |
| boolean aBoolean = data.getBoolean(a, i); |
| boolean bBoolean = data.getBoolean(b, i); |
| if (aBoolean != bBoolean) { |
| return false; |
| } |
| } else if (type == Long.TYPE) { |
| long aLong = data.getLong(a, i); |
| long bLong = data.getLong(b, i); |
| if (aLong != bLong) { |
| return false; |
| } |
| } else if (type == Float.TYPE) { |
| float aFloat = data.getFloat(a, i); |
| float bFloat = data.getFloat(b, i); |
| if (aFloat != bFloat) { |
| return false; |
| } |
| } else if (type == Double.TYPE) { |
| double aDouble = data.getDouble(a, i); |
| double bDouble = data.getDouble(b, i); |
| if (aDouble != bDouble) { |
| return false; |
| } |
| } else if (type == Short.TYPE) { |
| short aShort = data.getShort(a, i); |
| short bShort = data.getShort(b, i); |
| if (aShort != bShort) { |
| return false; |
| } |
| } else if (type == Character.TYPE) { |
| char aChar = data.getChar(a, i); |
| char bChar = data.getChar(b, i); |
| if (aChar != bChar) { |
| return false; |
| } |
| } else if (type == Byte.TYPE) { |
| byte aByte = data.getByte(a, i); |
| byte bByte = data.getByte(b, i); |
| if (aByte != bByte) { |
| return false; |
| } |
| } else { |
| assert false : "unhandled type: " + type; |
| } |
| } else { |
| Object objectA = data.getObject(a, i); |
| Object objectB = data.getObject(b, i); |
| if (objectA != objectB) { |
| if (objectA != null && objectB != null) { |
| if (!deepEquals0(objectA, objectB)) { |
| return false; |
| } |
| } else { |
| return false; |
| } |
| } |
| } |
| } |
| return true; |
| } |
| |
| public boolean isValid(Position pos, NodeClass<?> from, Edges fromEdges) { |
| if (this == from) { |
| return true; |
| } |
| Edges toEdges = getEdges(fromEdges.type()); |
| if (pos.getIndex() >= toEdges.getCount()) { |
| return false; |
| } |
| if (pos.getIndex() >= fromEdges.getCount()) { |
| return false; |
| } |
| return toEdges.isSame(fromEdges, pos.getIndex()); |
| } |
| |
| static void updateEdgesInPlace(Node node, InplaceUpdateClosure duplicationReplacement, Edges edges) { |
| int index = 0; |
| Type curType = edges.type(); |
| int directCount = edges.getDirectCount(); |
| final long[] curOffsets = edges.getOffsets(); |
| while (index < directCount) { |
| Node edge = Edges.getNode(node, curOffsets, index); |
| if (edge != null) { |
| Node newEdge = duplicationReplacement.replacement(edge, curType); |
| if (curType == Edges.Type.Inputs) { |
| node.updateUsages(null, newEdge); |
| } else { |
| node.updatePredecessor(null, newEdge); |
| } |
| edges.initializeNode(node, index, newEdge); |
| } |
| index++; |
| } |
| |
| while (index < edges.getCount()) { |
| NodeList<Node> list = Edges.getNodeList(node, curOffsets, index); |
| if (list != null) { |
| edges.initializeList(node, index, updateEdgeListCopy(node, list, duplicationReplacement, curType)); |
| } |
| index++; |
| } |
| } |
| |
| void updateInputSuccInPlace(Node node, InplaceUpdateClosure duplicationReplacement) { |
| updateEdgesInPlace(node, duplicationReplacement, inputs); |
| updateEdgesInPlace(node, duplicationReplacement, successors); |
| } |
| |
| private static NodeList<Node> updateEdgeListCopy(Node node, NodeList<Node> list, InplaceUpdateClosure duplicationReplacement, Edges.Type type) { |
| NodeList<Node> result = type == Edges.Type.Inputs ? new NodeInputList<>(node, list.size()) : new NodeSuccessorList<>(node, list.size()); |
| |
| for (int i = 0; i < list.count(); ++i) { |
| Node oldNode = list.get(i); |
| if (oldNode != null) { |
| Node newNode = duplicationReplacement.replacement(oldNode, type); |
| result.set(i, newNode); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Gets the input or successor edges defined by this node class. |
| */ |
| public Edges getEdges(Edges.Type type) { |
| return type == Edges.Type.Inputs ? inputs : successors; |
| } |
| |
| public Edges getInputEdges() { |
| return inputs; |
| } |
| |
| public Edges getSuccessorEdges() { |
| return successors; |
| } |
| |
| /** |
| * Returns a newly allocated node for which no subclass-specific constructor has been called. |
| */ |
| @SuppressWarnings("unchecked") |
| public Node allocateInstance() { |
| try { |
| Node node = (Node) UNSAFE.allocateInstance(getJavaClass()); |
| node.init((NodeClass<? extends Node>) this); |
| return node; |
| } catch (InstantiationException ex) { |
| throw shouldNotReachHere(ex); |
| } |
| } |
| |
| public Class<T> getJavaClass() { |
| return getClazz(); |
| } |
| |
| /** |
| * The template used to build the {@link Verbosity#Name} version. Variable parts are specified |
| * using {i#inputName} or {p#propertyName}. |
| */ |
| public String getNameTemplate() { |
| return nameTemplate.isEmpty() ? shortName() : nameTemplate; |
| } |
| |
| interface InplaceUpdateClosure { |
| |
| Node replacement(Node node, Edges.Type type); |
| } |
| |
| static Map<Node, Node> addGraphDuplicate(final Graph graph, final Graph oldGraph, int estimatedNodeCount, Iterable<? extends Node> nodes, final DuplicationReplacement replacements) { |
| final Map<Node, Node> newNodes; |
| int denseThreshold = oldGraph.getNodeCount() + oldGraph.getNodesDeletedSinceLastCompression() >> 4; |
| if (estimatedNodeCount > denseThreshold) { |
| // Use dense map |
| newNodes = new NodeNodeMap(oldGraph); |
| } else { |
| // Use sparse map |
| newNodes = newIdentityMap(); |
| } |
| createNodeDuplicates(graph, nodes, replacements, newNodes); |
| |
| InplaceUpdateClosure replacementClosure = new InplaceUpdateClosure() { |
| |
| @Override |
| public Node replacement(Node node, Edges.Type type) { |
| Node target = newNodes.get(node); |
| if (target == null) { |
| Node replacement = node; |
| if (replacements != null) { |
| replacement = replacements.replacement(node); |
| } |
| if (replacement != node) { |
| target = replacement; |
| } else if (node.graph() == graph && type == Edges.Type.Inputs) { |
| // patch to the outer world |
| target = node; |
| } |
| |
| } |
| return target; |
| } |
| |
| }; |
| |
| // re-wire inputs |
| for (Node oldNode : nodes) { |
| Node node = newNodes.get(oldNode); |
| NodeClass<?> nodeClass = node.getNodeClass(); |
| if (replacements == null || replacements.replacement(oldNode) == oldNode) { |
| nodeClass.updateInputSuccInPlace(node, replacementClosure); |
| } else { |
| transferEdgesDifferentNodeClass(graph, replacements, newNodes, oldNode, node); |
| } |
| } |
| |
| return newNodes; |
| } |
| |
| private static void createNodeDuplicates(final Graph graph, Iterable<? extends Node> nodes, final DuplicationReplacement replacements, final Map<Node, Node> newNodes) { |
| for (Node node : nodes) { |
| if (node != null) { |
| assert !node.isDeleted() : "trying to duplicate deleted node: " + node; |
| Node replacement = node; |
| if (replacements != null) { |
| replacement = replacements.replacement(node); |
| } |
| if (replacement != node) { |
| if (Fingerprint.ENABLED) { |
| Fingerprint.submit("replacing %s with %s", node, replacement); |
| } |
| assert replacement != null; |
| newNodes.put(node, replacement); |
| } else { |
| if (Fingerprint.ENABLED) { |
| Fingerprint.submit("duplicating %s", node); |
| } |
| Node newNode = node.clone(graph, WithAllEdges); |
| assert newNode.getNodeClass().isLeafNode() || newNode.hasNoUsages(); |
| assert newNode.getClass() == node.getClass(); |
| newNodes.put(node, newNode); |
| } |
| } |
| } |
| } |
| |
| private static void transferEdgesDifferentNodeClass(final Graph graph, final DuplicationReplacement replacements, final Map<Node, Node> newNodes, Node oldNode, Node node) { |
| transferEdges(graph, replacements, newNodes, oldNode, node, Edges.Type.Inputs); |
| transferEdges(graph, replacements, newNodes, oldNode, node, Edges.Type.Successors); |
| } |
| |
| private static void transferEdges(final Graph graph, final DuplicationReplacement replacements, final Map<Node, Node> newNodes, Node oldNode, Node node, Edges.Type type) { |
| NodeClass<?> nodeClass = node.getNodeClass(); |
| NodeClass<?> oldNodeClass = oldNode.getNodeClass(); |
| Edges oldEdges = oldNodeClass.getEdges(type); |
| for (Position pos : oldEdges.getPositionsIterable(oldNode)) { |
| if (!nodeClass.isValid(pos, oldNodeClass, oldEdges)) { |
| continue; |
| } |
| Node oldEdge = pos.get(oldNode); |
| if (oldEdge != null) { |
| Node target = newNodes.get(oldEdge); |
| if (target == null) { |
| Node replacement = oldEdge; |
| if (replacements != null) { |
| replacement = replacements.replacement(oldEdge); |
| } |
| if (replacement != oldEdge) { |
| target = replacement; |
| } else if (type == Edges.Type.Inputs && oldEdge.graph() == graph) { |
| // patch to the outer world |
| target = oldEdge; |
| } |
| } |
| pos.set(node, target); |
| } |
| } |
| } |
| |
| /** |
| * @returns true if the node has no inputs and no successors |
| */ |
| public boolean isLeafNode() { |
| return isLeafNode; |
| } |
| |
| public long inputsIteration() { |
| return inputsIteration; |
| } |
| |
| /** |
| * An iterator that will iterate over edges. |
| * |
| * An iterator of this type will not return null values, unless edges are modified concurrently. |
| * Concurrent modifications are detected by an assertion on a best-effort basis. |
| */ |
| private static class RawEdgesIterator implements Iterator<Node> { |
| protected final Node node; |
| protected long mask; |
| protected Node nextValue; |
| |
| RawEdgesIterator(Node node, long mask) { |
| this.node = node; |
| this.mask = mask; |
| } |
| |
| @Override |
| public boolean hasNext() { |
| Node next = nextValue; |
| if (next != null) { |
| return true; |
| } else { |
| nextValue = forward(); |
| return nextValue != null; |
| } |
| } |
| |
| private Node forward() { |
| while (mask != 0) { |
| Node next = getInput(); |
| mask = advanceInput(); |
| if (next != null) { |
| return next; |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public Node next() { |
| Node next = nextValue; |
| if (next == null) { |
| next = forward(); |
| if (next == null) { |
| throw new NoSuchElementException(); |
| } else { |
| return next; |
| } |
| } else { |
| nextValue = null; |
| return next; |
| } |
| } |
| |
| public final long advanceInput() { |
| int state = (int) mask & 0x03; |
| if (state == 0) { |
| // Skip normal field. |
| return mask >>> NEXT_EDGE; |
| } else if (state == 1) { |
| // We are iterating a node list. |
| if ((mask & 0xFFFF00) != 0) { |
| // Node list count is non-zero, decrease by 1. |
| return mask - 0x100; |
| } else { |
| // Node list is finished => go to next input. |
| return mask >>> 24; |
| } |
| } else { |
| // Need to expand node list. |
| NodeList<?> nodeList = Edges.getNodeListUnsafe(node, mask & 0xFC); |
| if (nodeList != null) { |
| int size = nodeList.size(); |
| if (size != 0) { |
| // Set pointer to upper most index of node list. |
| return ((mask >>> NEXT_EDGE) << 24) | (mask & 0xFD) | ((size - 1) << NEXT_EDGE); |
| } |
| } |
| // Node list is empty or null => skip. |
| return mask >>> NEXT_EDGE; |
| } |
| } |
| |
| public Node getInput() { |
| int state = (int) mask & 0x03; |
| if (state == 0) { |
| return Edges.getNodeUnsafe(node, mask & 0xFC); |
| } else if (state == 1) { |
| // We are iterating a node list. |
| NodeList<?> nodeList = Edges.getNodeListUnsafe(node, mask & 0xFC); |
| return nodeList.nodes[nodeList.size() - 1 - (int) ((mask >>> NEXT_EDGE) & 0xFFFF)]; |
| } else { |
| // Node list needs to expand first. |
| return null; |
| } |
| } |
| |
| @Override |
| public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| public Position nextPosition() { |
| return null; |
| } |
| } |
| |
| private static final class RawEdgesWithModCountIterator extends RawEdgesIterator { |
| private final int modCount; |
| |
| private RawEdgesWithModCountIterator(Node node, long mask) { |
| super(node, mask); |
| assert isModificationCountsEnabled(); |
| this.modCount = node.modCount(); |
| } |
| |
| @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(); |
| } |
| } |
| } |
| |
| public NodeIterable<Node> getSuccessorIterable(final Node node) { |
| long mask = this.successorIteration; |
| return new NodeIterable<Node>() { |
| |
| @Override |
| public Iterator<Node> iterator() { |
| if (isModificationCountsEnabled()) { |
| return new RawEdgesWithModCountIterator(node, mask); |
| } else { |
| return new RawEdgesIterator(node, mask); |
| } |
| } |
| }; |
| } |
| |
| public NodeIterable<Node> getInputIterable(final Node node) { |
| long mask = this.inputsIteration; |
| return new NodeIterable<Node>() { |
| |
| @Override |
| public Iterator<Node> iterator() { |
| if (isModificationCountsEnabled()) { |
| return new RawEdgesWithModCountIterator(node, mask); |
| } else { |
| return new RawEdgesIterator(node, mask); |
| } |
| } |
| }; |
| } |
| |
| public boolean equalSuccessors(Node node, Node other) { |
| return equalEdges(node, other, successorIteration); |
| } |
| |
| public boolean equalInputs(Node node, Node other) { |
| return equalEdges(node, other, inputsIteration); |
| } |
| |
| private boolean equalEdges(Node node, Node other, long mask) { |
| long myMask = mask; |
| assert other.getNodeClass() == this; |
| while (myMask != 0) { |
| long offset = (myMask & OFFSET_MASK); |
| Object v1 = UNSAFE.getObject(node, offset); |
| Object v2 = UNSAFE.getObject(other, offset); |
| if ((myMask & LIST_MASK) == 0) { |
| if (v1 != v2) { |
| return false; |
| } |
| } else { |
| if (!Objects.equals(v1, v2)) { |
| return false; |
| } |
| } |
| myMask >>>= NEXT_EDGE; |
| } |
| return true; |
| } |
| |
| public void pushInputs(Node node, NodeStack stack) { |
| long myMask = this.inputsIteration; |
| while (myMask != 0) { |
| long offset = (myMask & OFFSET_MASK); |
| if ((myMask & LIST_MASK) == 0) { |
| Node curNode = Edges.getNodeUnsafe(node, offset); |
| if (curNode != null) { |
| stack.push(curNode); |
| } |
| } else { |
| pushAllHelper(stack, node, offset); |
| } |
| myMask >>>= NEXT_EDGE; |
| } |
| } |
| |
| private static void pushAllHelper(NodeStack stack, Node node, long offset) { |
| NodeList<Node> list = Edges.getNodeListUnsafe(node, offset); |
| if (list != null) { |
| for (int i = 0; i < list.size(); ++i) { |
| Node curNode = list.get(i); |
| if (curNode != null) { |
| stack.push(curNode); |
| } |
| } |
| } |
| } |
| |
| public void applySuccessors(Node node, EdgeVisitor consumer) { |
| applyEdges(node, consumer, this.successorIteration); |
| } |
| |
| public void applyInputs(Node node, EdgeVisitor consumer) { |
| applyEdges(node, consumer, this.inputsIteration); |
| } |
| |
| private static void applyEdges(Node node, EdgeVisitor consumer, long mask) { |
| long myMask = mask; |
| while (myMask != 0) { |
| long offset = (myMask & OFFSET_MASK); |
| if ((myMask & LIST_MASK) == 0) { |
| Node curNode = Edges.getNodeUnsafe(node, offset); |
| if (curNode != null) { |
| Node newNode = consumer.apply(node, curNode); |
| if (newNode != curNode) { |
| UNSAFE.putObject(node, offset, newNode); |
| } |
| } |
| } else { |
| applyHelper(node, consumer, offset); |
| } |
| myMask >>>= NEXT_EDGE; |
| } |
| } |
| |
| private static void applyHelper(Node node, EdgeVisitor consumer, long offset) { |
| NodeList<Node> list = Edges.getNodeListUnsafe(node, offset); |
| if (list != null) { |
| for (int i = 0; i < list.size(); ++i) { |
| Node curNode = list.get(i); |
| if (curNode != null) { |
| Node newNode = consumer.apply(node, curNode); |
| if (newNode != curNode) { |
| list.initialize(i, newNode); |
| } |
| } |
| } |
| } |
| } |
| |
| public void unregisterAtSuccessorsAsPredecessor(Node node) { |
| long myMask = this.successorIteration; |
| while (myMask != 0) { |
| long offset = (myMask & OFFSET_MASK); |
| if ((myMask & LIST_MASK) == 0) { |
| Node curNode = Edges.getNodeUnsafe(node, offset); |
| if (curNode != null) { |
| node.updatePredecessor(curNode, null); |
| UNSAFE.putObject(node, offset, null); |
| } |
| } else { |
| unregisterAtSuccessorsAsPredecessorHelper(node, offset); |
| } |
| myMask >>>= NEXT_EDGE; |
| } |
| } |
| |
| private static void unregisterAtSuccessorsAsPredecessorHelper(Node node, long offset) { |
| NodeList<Node> list = Edges.getNodeListUnsafe(node, offset); |
| if (list != null) { |
| for (int i = 0; i < list.size(); ++i) { |
| Node curNode = list.get(i); |
| if (curNode != null) { |
| node.updatePredecessor(curNode, null); |
| } |
| } |
| list.clearWithoutUpdate(); |
| } |
| } |
| |
| public void registerAtSuccessorsAsPredecessor(Node node) { |
| long myMask = this.successorIteration; |
| while (myMask != 0) { |
| long offset = (myMask & OFFSET_MASK); |
| if ((myMask & LIST_MASK) == 0) { |
| Node curNode = Edges.getNodeUnsafe(node, offset); |
| if (curNode != null) { |
| assert curNode.isAlive() : "Successor not alive"; |
| node.updatePredecessor(null, curNode); |
| } |
| } else { |
| registerAtSuccessorsAsPredecessorHelper(node, offset); |
| } |
| myMask >>>= NEXT_EDGE; |
| } |
| } |
| |
| private static void registerAtSuccessorsAsPredecessorHelper(Node node, long offset) { |
| NodeList<Node> list = Edges.getNodeListUnsafe(node, offset); |
| if (list != null) { |
| for (int i = 0; i < list.size(); ++i) { |
| Node curNode = list.get(i); |
| if (curNode != null) { |
| assert curNode.isAlive() : "Successor not alive"; |
| node.updatePredecessor(null, curNode); |
| } |
| } |
| } |
| } |
| |
| public boolean replaceFirstInput(Node node, Node key, Node replacement) { |
| return replaceFirstEdge(node, key, replacement, this.inputsIteration); |
| } |
| |
| public boolean replaceFirstSuccessor(Node node, Node key, Node replacement) { |
| return replaceFirstEdge(node, key, replacement, this.successorIteration); |
| } |
| |
| public static boolean replaceFirstEdge(Node node, Node key, Node replacement, long mask) { |
| long myMask = mask; |
| while (myMask != 0) { |
| long offset = (myMask & OFFSET_MASK); |
| if ((myMask & LIST_MASK) == 0) { |
| Object curNode = UNSAFE.getObject(node, offset); |
| if (curNode == key) { |
| UNSAFE.putObject(node, offset, replacement); |
| return true; |
| } |
| } else { |
| NodeList<Node> list = Edges.getNodeListUnsafe(node, offset); |
| if (list != null && list.replaceFirst(key, replacement)) { |
| return true; |
| } |
| } |
| myMask >>>= NEXT_EDGE; |
| } |
| return false; |
| } |
| |
| public void registerAtInputsAsUsage(Node node) { |
| long myMask = this.inputsIteration; |
| while (myMask != 0) { |
| long offset = (myMask & OFFSET_MASK); |
| if ((myMask & LIST_MASK) == 0) { |
| Node curNode = Edges.getNodeUnsafe(node, offset); |
| if (curNode != null) { |
| assert curNode.isAlive() : "Input not alive " + curNode; |
| curNode.addUsage(node); |
| } |
| } else { |
| registerAtInputsAsUsageHelper(node, offset); |
| } |
| myMask >>>= NEXT_EDGE; |
| } |
| } |
| |
| private static void registerAtInputsAsUsageHelper(Node node, long offset) { |
| NodeList<Node> list = Edges.getNodeListUnsafe(node, offset); |
| if (list != null) { |
| for (int i = 0; i < list.size(); ++i) { |
| Node curNode = list.get(i); |
| if (curNode != null) { |
| assert curNode.isAlive() : "Input not alive"; |
| curNode.addUsage(node); |
| } |
| } |
| } |
| } |
| |
| public void unregisterAtInputsAsUsage(Node node) { |
| long myMask = this.inputsIteration; |
| while (myMask != 0) { |
| long offset = (myMask & OFFSET_MASK); |
| if ((myMask & LIST_MASK) == 0) { |
| Node curNode = Edges.getNodeUnsafe(node, offset); |
| if (curNode != null) { |
| node.removeThisFromUsages(curNode); |
| if (curNode.hasNoUsages()) { |
| node.maybeNotifyZeroUsages(curNode); |
| } |
| UNSAFE.putObject(node, offset, null); |
| } |
| } else { |
| unregisterAtInputsAsUsageHelper(node, offset); |
| } |
| myMask >>>= NEXT_EDGE; |
| } |
| } |
| |
| private static void unregisterAtInputsAsUsageHelper(Node node, long offset) { |
| NodeList<Node> list = Edges.getNodeListUnsafe(node, offset); |
| if (list != null) { |
| for (int i = 0; i < list.size(); ++i) { |
| Node curNode = list.get(i); |
| if (curNode != null) { |
| node.removeThisFromUsages(curNode); |
| if (curNode.hasNoUsages()) { |
| node.maybeNotifyZeroUsages(curNode); |
| } |
| } |
| } |
| list.clearWithoutUpdate(); |
| } |
| } |
| } |