changeset 17214:a552dd335bde

generalized support for unsafe access to a subset of the fields of an object
author Doug Simon <doug.simon@oracle.com>
date Fri, 26 Sep 2014 00:18:15 +0200
parents f4b939d433a4
children 5d03b4a472c6
files graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/Fields.java graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Edges.java graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/DeferredPiNode.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/EdgesSubstitutions.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/FieldsSubstitutions.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/GraalMethodSubstitutions.java
diffstat 8 files changed, 446 insertions(+), 289 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/Fields.java	Fri Sep 26 00:18:15 2014 +0200
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.compiler.common;
+
+import static com.oracle.graal.compiler.common.UnsafeAccess.*;
+
+import java.util.*;
+
+import sun.misc.*;
+
+/**
+ * Describes fields in a class, primarily for access via {@link Unsafe}.
+ */
+public class Fields {
+
+    protected final Class<?> clazz;
+    private final long[] offsets;
+    private final String[] names;
+    private final Class<?>[] types;
+
+    public Fields(Class<?> clazz, long[] offsets, Map<Long, String> names, Map<Long, Class<?>> types) {
+        this.clazz = clazz;
+        this.offsets = offsets;
+
+        this.names = new String[offsets.length];
+        this.types = new Class[offsets.length];
+        for (int i = 0; i < offsets.length; i++) {
+            this.names[i] = names.get(offsets[i]);
+            this.types[i] = types.get(offsets[i]);
+        }
+    }
+
+    /**
+     * Gets the number of fields represented by this object.
+     */
+    public int getCount() {
+        return offsets.length;
+    }
+
+    /**
+     * Gets the value of a field for a given object.
+     *
+     * @param object the object whose field is to be read
+     * @param index the index of the field (between 0 and {@link #getCount()})
+     * @return the value of the specified field which will be boxed if the field type is primitive
+     */
+    public Object get(Object object, int index) {
+        long offset = offsets[index];
+        Class<?> type = types[index];
+        Object value = null;
+        if (type.isPrimitive()) {
+            if (type == Integer.TYPE) {
+                value = unsafe.getInt(object, offset);
+            } else if (type == Long.TYPE) {
+                value = unsafe.getLong(object, offset);
+            } else if (type == Boolean.TYPE) {
+                value = unsafe.getBoolean(object, offset);
+            } else if (type == Float.TYPE) {
+                value = unsafe.getFloat(object, offset);
+            } else if (type == Double.TYPE) {
+                value = unsafe.getDouble(object, offset);
+            } else if (type == Short.TYPE) {
+                value = unsafe.getShort(object, offset);
+            } else if (type == Character.TYPE) {
+                value = unsafe.getChar(object, offset);
+            } else if (type == Byte.TYPE) {
+                value = unsafe.getByte(object, offset);
+            } else {
+                assert false : "unhandled property type: " + type;
+            }
+        } else {
+            value = unsafe.getObject(object, offset);
+        }
+        return value;
+    }
+
+    /**
+     * Determines if a field in the domain of this object is the same as the field denoted by the
+     * same index in another {@link Fields} object.
+     */
+    public boolean isSame(Fields other, int index) {
+        return other.offsets[index] == offsets[index];
+    }
+
+    /**
+     * Gets the name of a field.
+     *
+     * @param index index of a field
+     */
+    public String getName(int index) {
+        return names[index];
+    }
+
+    /**
+     * Gets the type of a field.
+     *
+     * @param index index of a field
+     */
+    public Class<?> getType(int index) {
+        return types[index];
+    }
+
+    private boolean checkAssignable(int index, Object value) {
+        assert value == null || getType(index).isAssignableFrom(value.getClass()) : String.format("%s.%s of type %s is not assignable from %s", clazz.getSimpleName(), getName(index),
+                        getType(index).getSimpleName(), value.getClass().getSimpleName());
+        return true;
+    }
+
+    public void set(Object object, int index, Object value) {
+        long dataOffset = offsets[index];
+        Class<?> type = types[index];
+        if (type.isPrimitive()) {
+            if (type == Integer.TYPE) {
+                unsafe.putInt(object, dataOffset, (Integer) value);
+            } else if (type == Long.TYPE) {
+                unsafe.putLong(object, dataOffset, (Long) value);
+            } else if (type == Boolean.TYPE) {
+                unsafe.putBoolean(object, dataOffset, (Boolean) value);
+            } else if (type == Float.TYPE) {
+                unsafe.putFloat(object, dataOffset, (Float) value);
+            } else if (type == Double.TYPE) {
+                unsafe.putDouble(object, dataOffset, (Double) value);
+            } else if (type == Short.TYPE) {
+                unsafe.putShort(object, dataOffset, (Short) value);
+            } else if (type == Character.TYPE) {
+                unsafe.putChar(object, dataOffset, (Character) value);
+            } else if (type == Byte.TYPE) {
+                unsafe.putByte(object, dataOffset, (Byte) value);
+            } else {
+                assert false : "unhandled property type: " + type;
+            }
+        } else {
+            assert checkAssignable(index, value);
+            unsafe.putObject(object, dataOffset, value);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return clazz.getSimpleName();
+    }
+
+    public void appendFields(StringBuilder sb) {
+        for (int i = 0; i < offsets.length; i++) {
+            sb.append(i == 0 ? "" : ", ").append(getName(i)).append('@').append(offsets[i]);
+        }
+    }
+
+    public boolean getBoolean(Object n, int i) {
+        assert types[i] == boolean.class;
+        return unsafe.getBoolean(n, offsets[i]);
+    }
+
+    public byte getByte(Object n, int i) {
+        assert types[i] == byte.class;
+        return unsafe.getByte(n, offsets[i]);
+    }
+
+    public short getShort(Object n, int i) {
+        assert types[i] == short.class;
+        return unsafe.getShort(n, offsets[i]);
+    }
+
+    public char getChar(Object n, int i) {
+        assert types[i] == char.class;
+        return unsafe.getChar(n, offsets[i]);
+    }
+
+    public int getInt(Object n, int i) {
+        assert types[i] == int.class;
+        return unsafe.getInt(n, offsets[i]);
+    }
+
+    public long getLong(Object n, int i) {
+        assert types[i] == long.class;
+        return unsafe.getLong(n, offsets[i]);
+    }
+
+    public float getFloat(Object n, int i) {
+        assert types[i] == float.class;
+        return unsafe.getFloat(n, offsets[i]);
+    }
+
+    public double getDouble(Object n, int i) {
+        assert types[i] == double.class;
+        return unsafe.getDouble(n, offsets[i]);
+    }
+
+    /**
+     * Gets the value of an object field.
+     *
+     * @param object the object whose field is to be read
+     * @param index the index of the field (between 0 and {@link #getCount()})
+     * @param asType the type to which the returned object is cast
+     * @return the value of the specified field cast to {@code c}
+     */
+    public <T> T getObject(Object object, int index, Class<T> asType) {
+        return getObject(object, offsets[index], asType);
+    }
+
+    private static <T> T getObject(Object object, long offset, Class<T> asType) {
+        return asType.cast(unsafe.getObject(object, offset));
+    }
+
+    /**
+     * Sets the value of an object field.
+     *
+     * @param object the object whose field is to be written
+     * @param index the index of the field (between 0 and {@link #getCount()})
+     * @param value the value to be written to the field
+     */
+    protected void putObject(Object object, int index, Object value) {
+        assert checkAssignable(index, value);
+        putObject(object, offsets[index], value);
+    }
+
+    private static void putObject(Object object, long offset, Object value) {
+        unsafe.putObject(object, offset, value);
+    }
+
+}
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Edges.java	Thu Sep 25 12:02:50 2014 +0200
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Edges.java	Fri Sep 26 00:18:15 2014 +0200
@@ -22,17 +22,18 @@
  */
 package com.oracle.graal.graph;
 
-import static com.oracle.graal.compiler.common.UnsafeAccess.*;
 import static com.oracle.graal.graph.Graph.*;
 import static com.oracle.graal.graph.Node.*;
 
 import java.util.*;
 
+import com.oracle.graal.compiler.common.*;
+
 /**
  * Describes {@link Node} fields representing the set of inputs for the node or the set of the
  * node's successors.
  */
-public abstract class Edges {
+public abstract class Edges extends Fields {
 
     /**
      * Constants denoting whether a set of edges are inputs or successors.
@@ -42,33 +43,13 @@
         Successors;
     }
 
-    private final Class<? extends Node> nodeClass;
     private final int directCount;
-    private final long[] offsets;
-    private final String[] names;
-    private final Class<?>[] types;
     private final Type type;
 
-    @SuppressWarnings("unchecked")
     public Edges(Class<?> nodeClass, Type type, int directCount, long[] offsets, Map<Long, String> names, Map<Long, Class<?>> types) {
-        this.nodeClass = (Class<? extends Node>) nodeClass;
+        super(nodeClass, offsets, names, types);
         this.type = type;
         this.directCount = directCount;
-        this.offsets = offsets;
-
-        this.names = new String[offsets.length];
-        this.types = new Class[offsets.length];
-        for (int i = 0; i < offsets.length; i++) {
-            this.names[i] = names.get(offsets[i]);
-            this.types[i] = types.get(offsets[i]);
-        }
-    }
-
-    /**
-     * Gets the number of edges represented by this object.
-     */
-    public int getCount() {
-        return offsets.length;
     }
 
     /**
@@ -79,23 +60,6 @@
         return directCount;
     }
 
-    private static Node getNode(Node node, long offset) {
-        return (Node) unsafe.getObject(node, offset);
-    }
-
-    @SuppressWarnings("unchecked")
-    private static NodeList<Node> getNodeList(Node node, long offset) {
-        return (NodeList<Node>) unsafe.getObject(node, offset);
-    }
-
-    private static void putNode(Node node, long offset, Node value) {
-        unsafe.putObject(node, offset, value);
-    }
-
-    private static void putNodeList(Node node, long offset, NodeList<?> value) {
-        unsafe.putObject(node, offset, value);
-    }
-
     /**
      * Gets the {@link Node} at the end point of a {@linkplain #getDirectCount() direct} edge.
      *
@@ -105,7 +69,7 @@
      */
     public Node getNode(Node node, int index) {
         assert index >= 0 && index < directCount;
-        return getNode(node, offsets[index]);
+        return getObject(node, index, Node.class);
     }
 
     /**
@@ -116,9 +80,10 @@
      *            {@link #getDirectCount()})
      * @return the {@link NodeList} at the other edge of the requested edge
      */
+    @SuppressWarnings("unchecked")
     public NodeList<Node> getNodeList(Node node, int index) {
-        assert index >= directCount && index < offsets.length;
-        return getNodeList(node, offsets[index]);
+        assert index >= directCount && index < getCount();
+        return getObject(node, index, NodeList.class);
     }
 
     /**
@@ -152,7 +117,7 @@
      * @param toNode the node to which the edges should be copied.
      */
     public void copy(Node fromNode, Node toNode) {
-        assert fromNode.getNodeClass().getClazz() == nodeClass && toNode.getNodeClass().getClazz() == nodeClass;
+        assert fromNode.getNodeClass().getClazz() == clazz && toNode.getNodeClass().getClazz() == clazz;
         int index = 0;
         while (index < getDirectCount()) {
             initializeNode(toNode, index, getNode(fromNode, index));
@@ -187,7 +152,7 @@
         }
         while (index < getCount()) {
             NodeList<Node> list = getNodeList(node, index);
-            assert list != null : nodeClass;
+            assert list != null : clazz;
             if (list.replaceFirst(key, replacement)) {
                 return true;
             }
@@ -196,50 +161,40 @@
         return false;
     }
 
-    public boolean isSameEdge(Edges other, int index) {
-        return offsets[index] == other.offsets[index];
-    }
-
-    /**
-     * Gets the name of an edge.
-     *
-     * @param index index of an edge
-     */
-    public String getName(int index) {
-        return names[index];
+    @Override
+    public void set(Object node, int index, Object value) {
+        throw new IllegalArgumentException("Cannot call set on " + this);
     }
 
     /**
-     * Gets the type of the field storing the end point of an edge.
+     * Sets the value of a given edge without notifying the new and old nodes on the other end of
+     * the edge of the change.
      *
-     * @param index index of an edge
+     * @param node the node whose edge is to be updated
+     * @param index the index of the edge (between 0 and {@link #getCount()})
+     * @param value the node to be written to the edge
      */
-    public Class<?> getType(int index) {
-        return types[index];
-    }
-
-    private boolean checkAssignable(int index, Node value) {
-        assert value == null || getType(index).isAssignableFrom(value.getClass()) : String.format("%s.%s of type %s is not assignable from %s", nodeClass.getSimpleName(), getName(index),
-                        getType(index).getSimpleName(), value.getClass().getSimpleName());
-        return true;
-    }
-
     public void initializeNode(Node node, int index, Node value) {
-        assert checkAssignable(index, value);
-        putNode(node, offsets[index], value);
+        putObject(node, index, value);
     }
 
     public void initializeList(Node node, int index, NodeList<Node> value) {
         assert index >= directCount;
-        putNodeList(node, offsets[index], value);
+        putObject(node, index, value);
     }
 
+    /**
+     * Sets the value of a given edge and notifies the new and old nodes on the other end of the
+     * edge of the change.
+     *
+     * @param node the node whose edge is to be updated
+     * @param index the index of the edge (between 0 and {@link #getCount()})
+     * @param value the node to be written to the edge
+     */
     public void setNode(Node node, int index, Node value) {
         assert index < directCount;
-        long offset = offsets[index];
-        Node old = getNode(node, offset);
-        assert checkAssignable(index, value);
-        putNode(node, offset, value);
+        Node old = getObject(node, index, Node.class);
+        putObject(node, index, value);
         update(node, old, value);
     }
 
@@ -263,7 +218,7 @@
      * Determines if the edges of two given nodes are the same.
      */
     public boolean areEqualIn(Node node, Node other) {
-        assert node.getNodeClass().getClazz() == nodeClass && other.getNodeClass().getClazz() == nodeClass;
+        assert node.getNodeClass().getClazz() == clazz && other.getNodeClass().getClazz() == clazz;
         int index = 0;
         while (index < directCount) {
             if (getNode(other, index) != getNode(node, index)) {
@@ -476,12 +431,6 @@
 
     @Override
     public String toString() {
-        return nodeClass.getSimpleName() + ":" + type;
-    }
-
-    void appendOffsets(StringBuilder sb) {
-        for (int i = 0; i < offsets.length; i++) {
-            sb.append(i == 0 ? "" : ", ").append(offsets[i]);
-        }
+        return super.toString() + ":" + type;
     }
 }
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java	Thu Sep 25 12:02:50 2014 +0200
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java	Fri Sep 26 00:18:15 2014 +0200
@@ -28,6 +28,7 @@
 import java.lang.annotation.*;
 import java.util.*;
 
+import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.graph.Graph.NodeEventListener;
 import com.oracle.graal.graph.iterators.*;
 import com.oracle.graal.graph.spi.*;
@@ -471,8 +472,9 @@
     }
 
     /**
-     * Updates the usages sets of the given nodes after an input slot is changed from oldInput to
-     * newInput: removes this node from oldInput's usages and adds this node to newInput's usages.
+     * Updates the usages sets of the given nodes after an input slot is changed from
+     * {@code oldInput} to {@code newInput} by removing this node from {@code oldInput}'s usages and
+     * adds this node to {@code newInput}'s usages.
      */
     protected void updateUsages(Node oldInput, Node newInput) {
         assert isAlive() && (newInput == null || newInput.isAlive()) : "adding " + newInput + " to " + this + " instead of " + oldInput;
@@ -883,9 +885,9 @@
      */
     public Map<Object, Object> getDebugProperties(Map<Object, Object> map) {
         NodeClass nodeClass = getNodeClass();
-        for (Integer pos : nodeClass.getPropertyPositions()) {
-            map.put(nodeClass.getPropertyName(pos), nodeClass.getProperty(this, pos));
-
+        Fields properties = nodeClass.getProperties();
+        for (int i = 0; i < properties.getCount(); i++) {
+            map.put(properties.getName(i), properties.get(this, i));
         }
         return map;
     }
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java	Thu Sep 25 12:02:50 2014 +0200
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java	Fri Sep 26 00:18:15 2014 +0200
@@ -111,8 +111,8 @@
 
     private final Edges inputs;
     private final Edges successors;
+    private final Fields properties;
 
-    private final Class<?>[] dataTypes;
     private final boolean canGVN;
     private final int startGVNNumber;
     private final String shortName;
@@ -174,11 +174,7 @@
             inputs = new InputEdges(clazz, fs.inputOffsets.size(), sortedOffsets(fs.inputOffsets, fs.inputListOffsets), fs.fieldNames, fs.fieldTypes, fs.types, fs.optionalInputs);
         }
         try (TimerCloseable t1 = Init_Data.start()) {
-            dataOffsets = sortedLongCopy(fs.dataOffsets);
-            dataTypes = new Class[dataOffsets.length];
-            for (int i = 0; i < dataOffsets.length; i++) {
-                dataTypes[i] = fs.fieldTypes.get(dataOffsets[i]);
-            }
+            properties = new Fields(clazz, sortedLongCopy(fs.dataOffsets), fs.fieldNames, fs.fieldTypes);
         }
 
         isLeafNode = inputs.getCount() + successors.getCount() == 0;
@@ -288,7 +284,7 @@
      *
      * <pre>
      *     if (node.getNodeClass().is(BeginNode.class)) { ... }
-     * 
+     *
      *     // Due to generated Node classes, the test below
      *     // is *not* the same as the test above:
      *     if (node.getClass() == BeginNode.class) { ... }
@@ -402,13 +398,11 @@
     public String toString() {
         StringBuilder str = new StringBuilder();
         str.append("NodeClass ").append(getClazz().getSimpleName()).append(" [");
-        inputs.appendOffsets(str);
-        str.append("] [");
-        successors.appendOffsets(str);
+        inputs.appendFields(str);
         str.append("] [");
-        for (int i = 0; i < dataOffsets.length; i++) {
-            str.append(i == 0 ? "" : ", ").append(dataOffsets[i]);
-        }
+        successors.appendFields(str);
+        str.append("] [");
+        properties.appendFields(str);
         str.append("]");
         return str.toString();
     }
@@ -443,41 +437,41 @@
         int number = 0;
         if (canGVN) {
             number = startGVNNumber;
-            for (int i = 0; i < dataOffsets.length; ++i) {
-                Class<?> type = dataTypes[i];
+            for (int i = 0; i < properties.getCount(); ++i) {
+                Class<?> type = properties.getType(i);
                 if (type.isPrimitive()) {
                     if (type == Integer.TYPE) {
-                        int intValue = unsafe.getInt(n, dataOffsets[i]);
+                        int intValue = properties.getInt(n, i);
                         number += intValue;
                     } else if (type == Long.TYPE) {
-                        long longValue = unsafe.getLong(n, dataOffsets[i]);
+                        long longValue = properties.getLong(n, i);
                         number += longValue ^ (longValue >>> 32);
                     } else if (type == Boolean.TYPE) {
-                        boolean booleanValue = unsafe.getBoolean(n, dataOffsets[i]);
+                        boolean booleanValue = properties.getBoolean(n, i);
                         if (booleanValue) {
                             number += 7;
                         }
                     } else if (type == Float.TYPE) {
-                        float floatValue = unsafe.getFloat(n, dataOffsets[i]);
+                        float floatValue = properties.getFloat(n, i);
                         number += Float.floatToRawIntBits(floatValue);
                     } else if (type == Double.TYPE) {
-                        double doubleValue = unsafe.getDouble(n, dataOffsets[i]);
+                        double doubleValue = properties.getDouble(n, i);
                         long longValue = Double.doubleToRawLongBits(doubleValue);
                         number += longValue ^ (longValue >>> 32);
                     } else if (type == Short.TYPE) {
-                        short shortValue = unsafe.getShort(n, dataOffsets[i]);
+                        short shortValue = properties.getShort(n, i);
                         number += shortValue;
                     } else if (type == Character.TYPE) {
-                        char charValue = unsafe.getChar(n, dataOffsets[i]);
+                        char charValue = properties.getChar(n, i);
                         number += charValue;
                     } else if (type == Byte.TYPE) {
-                        byte byteValue = unsafe.getByte(n, dataOffsets[i]);
+                        byte byteValue = properties.getByte(n, i);
                         number += byteValue;
                     } else {
                         assert false : "unhandled property type: " + type;
                     }
                 } else {
-                    Object o = unsafe.getObject(n, dataOffsets[i]);
+                    Object o = properties.getObject(n, i, Object.class);
                     number += deepHashCode0(o);
                 }
                 number *= 13;
@@ -517,54 +511,54 @@
         if (a.getClass() != b.getClass()) {
             return a == b;
         }
-        for (int i = 0; i < dataOffsets.length; ++i) {
-            Class<?> type = dataTypes[i];
+        for (int i = 0; i < properties.getCount(); ++i) {
+            Class<?> type = properties.getType(i);
             if (type.isPrimitive()) {
                 if (type == Integer.TYPE) {
-                    int aInt = unsafe.getInt(a, dataOffsets[i]);
-                    int bInt = unsafe.getInt(b, dataOffsets[i]);
+                    int aInt = properties.getInt(a, i);
+                    int bInt = properties.getInt(b, i);
                     if (aInt != bInt) {
                         return false;
                     }
                 } else if (type == Boolean.TYPE) {
-                    boolean aBoolean = unsafe.getBoolean(a, dataOffsets[i]);
-                    boolean bBoolean = unsafe.getBoolean(b, dataOffsets[i]);
+                    boolean aBoolean = properties.getBoolean(a, i);
+                    boolean bBoolean = properties.getBoolean(b, i);
                     if (aBoolean != bBoolean) {
                         return false;
                     }
                 } else if (type == Long.TYPE) {
-                    long aLong = unsafe.getLong(a, dataOffsets[i]);
-                    long bLong = unsafe.getLong(b, dataOffsets[i]);
+                    long aLong = properties.getLong(a, i);
+                    long bLong = properties.getLong(b, i);
                     if (aLong != bLong) {
                         return false;
                     }
                 } else if (type == Float.TYPE) {
-                    float aFloat = unsafe.getFloat(a, dataOffsets[i]);
-                    float bFloat = unsafe.getFloat(b, dataOffsets[i]);
+                    float aFloat = properties.getFloat(a, i);
+                    float bFloat = properties.getFloat(b, i);
                     if (aFloat != bFloat) {
                         return false;
                     }
                 } else if (type == Double.TYPE) {
-                    double aDouble = unsafe.getDouble(a, dataOffsets[i]);
-                    double bDouble = unsafe.getDouble(b, dataOffsets[i]);
+                    double aDouble = properties.getDouble(a, i);
+                    double bDouble = properties.getDouble(b, i);
                     if (aDouble != bDouble) {
                         return false;
                     }
                 } else if (type == Short.TYPE) {
-                    short aShort = unsafe.getShort(a, dataOffsets[i]);
-                    short bShort = unsafe.getShort(b, dataOffsets[i]);
+                    short aShort = properties.getShort(a, i);
+                    short bShort = properties.getShort(b, i);
                     if (aShort != bShort) {
                         return false;
                     }
                 } else if (type == Character.TYPE) {
-                    char aChar = unsafe.getChar(a, dataOffsets[i]);
-                    char bChar = unsafe.getChar(b, dataOffsets[i]);
+                    char aChar = properties.getChar(a, i);
+                    char bChar = properties.getChar(b, i);
                     if (aChar != bChar) {
                         return false;
                     }
                 } else if (type == Byte.TYPE) {
-                    byte aByte = unsafe.getByte(a, dataOffsets[i]);
-                    byte bByte = unsafe.getByte(b, dataOffsets[i]);
+                    byte aByte = properties.getByte(a, i);
+                    byte bByte = properties.getByte(b, i);
                     if (aByte != bByte) {
                         return false;
                     }
@@ -572,8 +566,8 @@
                     assert false : "unhandled type: " + type;
                 }
             } else {
-                Object objectA = unsafe.getObject(a, dataOffsets[i]);
-                Object objectB = unsafe.getObject(b, dataOffsets[i]);
+                Object objectA = properties.getObject(a, i, Object.class);
+                Object objectB = properties.getObject(b, i, Object.class);
                 if (objectA != objectB) {
                     if (objectA != null && objectB != null) {
                         if (!deepEquals0(objectA, objectB)) {
@@ -599,74 +593,14 @@
         if (pos.getIndex() >= fromEdges.getCount()) {
             return false;
         }
-        return toEdges.isSameEdge(fromEdges, pos.getIndex());
-    }
-
-    public String getPropertyName(int pos) {
-        return fieldNames.get(dataOffsets[pos]);
-    }
-
-    public Class<?> getPropertyType(int pos) {
-        return fieldTypes.get(dataOffsets[pos]);
+        return toEdges.isSame(fromEdges, pos.getIndex());
     }
 
-    public Object getProperty(Node node, int pos) {
-        long dataOffset = dataOffsets[pos];
-        Class<?> type = fieldTypes.get(dataOffset);
-        Object value = null;
-        if (type.isPrimitive()) {
-            if (type == Integer.TYPE) {
-                value = unsafe.getInt(node, dataOffset);
-            } else if (type == Long.TYPE) {
-                value = unsafe.getLong(node, dataOffset);
-            } else if (type == Boolean.TYPE) {
-                value = unsafe.getBoolean(node, dataOffset);
-            } else if (type == Float.TYPE) {
-                value = unsafe.getFloat(node, dataOffset);
-            } else if (type == Double.TYPE) {
-                value = unsafe.getDouble(node, dataOffset);
-            } else if (type == Short.TYPE) {
-                value = unsafe.getShort(node, dataOffset);
-            } else if (type == Character.TYPE) {
-                value = unsafe.getChar(node, dataOffset);
-            } else if (type == Byte.TYPE) {
-                value = unsafe.getByte(node, dataOffset);
-            } else {
-                assert false : "unhandled property type: " + type;
-            }
-        } else {
-            value = unsafe.getObject(node, dataOffset);
-        }
-        return value;
-    }
-
-    public void setProperty(Node node, int pos, Object value) {
-        long dataOffset = dataOffsets[pos];
-        Class<?> type = fieldTypes.get(dataOffset);
-        if (type.isPrimitive()) {
-            if (type == Integer.TYPE) {
-                unsafe.putInt(node, dataOffset, (Integer) value);
-            } else if (type == Long.TYPE) {
-                unsafe.putLong(node, dataOffset, (Long) value);
-            } else if (type == Boolean.TYPE) {
-                unsafe.putBoolean(node, dataOffset, (Boolean) value);
-            } else if (type == Float.TYPE) {
-                unsafe.putFloat(node, dataOffset, (Float) value);
-            } else if (type == Double.TYPE) {
-                unsafe.putDouble(node, dataOffset, (Double) value);
-            } else if (type == Short.TYPE) {
-                unsafe.putShort(node, dataOffset, (Short) value);
-            } else if (type == Character.TYPE) {
-                unsafe.putChar(node, dataOffset, (Character) value);
-            } else if (type == Byte.TYPE) {
-                unsafe.putByte(node, dataOffset, (Byte) value);
-            } else {
-                assert false : "unhandled property type: " + type;
-            }
-        } else {
-            assert value == null || !value.getClass().isPrimitive();
-            unsafe.putObject(node, dataOffset, value);
-        }
+    /**
+     * Gets the non-edge properties defined by this node class.
+     */
+    public Fields getProperties() {
+        return properties;
     }
 
     static void updateEdgesInPlace(Node node, InplaceUpdateClosure duplicationReplacement, Edges edges) {
@@ -719,36 +653,6 @@
         return type == Edges.Type.Inputs ? inputs : successors;
     }
 
-    public Collection<Integer> getPropertyPositions() {
-        return new AbstractCollection<Integer>() {
-            @Override
-            public Iterator<Integer> iterator() {
-                return new Iterator<Integer>() {
-                    int i = 0;
-
-                    @Override
-                    public void remove() {
-                        throw new UnsupportedOperationException();
-                    }
-
-                    public Integer next() {
-                        Integer pos = i++;
-                        return pos;
-                    }
-
-                    public boolean hasNext() {
-                        return i < dataOffsets.length;
-                    }
-                };
-            }
-
-            @Override
-            public int size() {
-                return dataOffsets.length;
-            }
-        };
-    }
-
     /**
      * Initializes a fresh allocated node for which no constructor is called yet. Needed to
      * implement node factories in svm.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/DeferredPiNode.java	Fri Sep 26 00:18:15 2014 +0200
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.nodes;
+
+//JaCoCo Exclude
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.type.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.spi.*;
+import com.oracle.graal.nodeinfo.*;
+import com.oracle.graal.nodes.calc.*;
+
+/**
+ * A node that changes the type of its input where the type is not immediately available at node
+ * intrinsification time. It is replaced by a {@link PiNode} once the type becomes constant.
+ */
+@NodeInfo
+public class DeferredPiNode extends FloatingNode implements Canonicalizable {
+
+    @Input ValueNode object;
+    @Input ValueNode type;
+
+    public ValueNode object() {
+        return object;
+    }
+
+    public static DeferredPiNode create(ValueNode type, ValueNode object) {
+        return USE_GENERATED_NODES ? new DeferredPiNodeGen(type, object) : new DeferredPiNode(type, object);
+    }
+
+    protected DeferredPiNode(ValueNode type, ValueNode object) {
+        super(StampFactory.object());
+        this.type = type;
+        this.object = object;
+    }
+
+    @Override
+    public Node canonical(CanonicalizerTool tool) {
+        if (type.isConstant()) {
+            ResolvedJavaType javaType = tool.getConstantReflection().asJavaType(type.asConstant());
+            ObjectStamp objectStamp = (ObjectStamp) stamp();
+            return PiNode.create(object, javaType, objectStamp.isExactType(), objectStamp.nonNull());
+        }
+        return this;
+    }
+
+    @NodeIntrinsic
+    public static native <T> T piCast(Class<T> type, Object object);
+}
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/EdgesSubstitutions.java	Thu Sep 25 12:02:50 2014 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-/*
- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.replacements;
-
-import com.oracle.graal.api.meta.*;
-import com.oracle.graal.api.replacements.*;
-import com.oracle.graal.graph.*;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.extended.*;
-
-/**
- * Substitutions for improving the performance of some critical methods in {@link Edges}. These
- * substitutions improve the performance by forcing the relevant methods to be inlined
- * (intrinsification being a special form of inlining) and removing a checked cast. The latter
- * cannot be done directly in Java code as {@link PiNode} is not available to the project containing
- * {@link Edges}.
- */
-@ClassSubstitution(Edges.class)
-public class EdgesSubstitutions {
-
-    @MethodSubstitution
-    private static Node getNode(Node node, long offset) {
-        return PiNode.piCast(UnsafeLoadNode.load(node, offset, Kind.Object, LocationIdentity.ANY_LOCATION), Node.class);
-    }
-
-    @MethodSubstitution
-    private static NodeList<?> getNodeList(Node node, long offset) {
-        return PiNode.piCast(UnsafeLoadNode.load(node, offset, Kind.Object, LocationIdentity.ANY_LOCATION), NodeList.class);
-    }
-
-    @MethodSubstitution
-    private static void putNode(Node node, long offset, Node value) {
-        UnsafeStoreNode.store(node, offset, value, Kind.Object, LocationIdentity.ANY_LOCATION);
-    }
-
-    @MethodSubstitution
-    private static void putNodeList(Node node, long offset, NodeList<?> value) {
-        UnsafeStoreNode.store(node, offset, value, Kind.Object, LocationIdentity.ANY_LOCATION);
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/FieldsSubstitutions.java	Fri Sep 26 00:18:15 2014 +0200
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.replacements;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.api.replacements.*;
+import com.oracle.graal.compiler.common.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.extended.*;
+
+/**
+ * Substitutions for improving the performance of some critical methods in {@link Fields}. These
+ * substitutions improve the performance by forcing the relevant methods to be inlined
+ * (intrinsification being a special form of inlining) and removing a checked cast. The latter
+ * cannot be done directly in Java code as {@link DeferredPiNode} is not available to the project
+ * containing {@link Fields}.
+ */
+@ClassSubstitution(Fields.class)
+public class FieldsSubstitutions {
+
+    @MethodSubstitution
+    private static <T> T getObject(Object object, long offset, Class<T> c) {
+        return DeferredPiNode.piCast(c, UnsafeLoadNode.load(object, offset, Kind.Object, LocationIdentity.ANY_LOCATION));
+    }
+
+    @MethodSubstitution
+    private static void putObject(Object node, long offset, Object value) {
+        UnsafeStoreNode.store(node, offset, value, Kind.Object, LocationIdentity.ANY_LOCATION);
+    }
+}
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/GraalMethodSubstitutions.java	Thu Sep 25 12:02:50 2014 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/GraalMethodSubstitutions.java	Fri Sep 26 00:18:15 2014 +0200
@@ -30,6 +30,7 @@
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.api.replacements.*;
 import com.oracle.graal.api.runtime.*;
+import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.nodes.spi.*;
 
 /**
@@ -50,6 +51,7 @@
             replacements.registerSubstitutions(Character.class, CharacterSubstitutions.class);
             replacements.registerSubstitutions(Short.class, ShortSubstitutions.class);
             replacements.registerSubstitutions(UnsignedMath.class, UnsignedMathSubstitutions.class);
+            replacements.registerSubstitutions(Fields.class, FieldsSubstitutions.class);
         }
     }
 }