changeset 9501:bef43373de39

coalesce allocations during escape analysis
author Lukas Stadler <lukas.stadler@jku.at>
date Mon, 29 Apr 2013 14:53:08 +0200
parents 9cc37ce426cc
children 6b19d1000809
files graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/BoxingEliminationTest.java graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EscapeAnalysisTest.java graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/DebugInfoBuilder.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/AllocatedObjectNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/CommitAllocationNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualArrayNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualBoxingNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualInstanceNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualObjectNode.java graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/nodes/MaterializeObjectNode.java graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/BlockState.java graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/ObjectState.java graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeAnalysisPhase.java
diffstat 16 files changed, 472 insertions(+), 270 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/BoxingEliminationTest.java	Mon Apr 29 18:38:16 2013 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/BoxingEliminationTest.java	Mon Apr 29 14:53:08 2013 +0200
@@ -31,7 +31,6 @@
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
 import com.oracle.graal.phases.tiers.*;
-import com.oracle.graal.virtual.nodes.*;
 import com.oracle.graal.virtual.phases.ea.*;
 
 /**
@@ -334,10 +333,6 @@
                 new CanonicalizerPhase().apply(graph, context);
                 new PartialEscapeAnalysisPhase(false, false).apply(graph, context);
 
-                for (MaterializeObjectNode materialize : graph.getNodes(MaterializeObjectNode.class)) {
-                    materialize.getVirtualObject().materializeAt(materialize, materialize.getValues(), false, materialize.getLockCount());
-                }
-
                 new CullFrameStatesPhase().apply(graph);
                 new DeadCodeEliminationPhase().apply(graph);
                 new CanonicalizerPhase().apply(graph, context);
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EscapeAnalysisTest.java	Mon Apr 29 18:38:16 2013 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EscapeAnalysisTest.java	Mon Apr 29 14:53:08 2013 +0200
@@ -35,16 +35,15 @@
 import com.oracle.graal.java.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.java.*;
+import com.oracle.graal.nodes.virtual.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
 import com.oracle.graal.phases.tiers.*;
-import com.oracle.graal.virtual.nodes.*;
 import com.oracle.graal.virtual.phases.ea.*;
 
 /**
- * In these test cases the probability of all invokes is set to a high value, such that an
- * InliningPhase should inline them all. After that, the EscapeAnalysisPhase is expected to remove
- * all allocations and return the correct values.
+ * The PartialEscapeAnalysisPhase is expected to remove all allocations and return the correct
+ * values.
  */
 public class EscapeAnalysisTest extends GraalCompilerTest {
 
@@ -230,7 +229,7 @@
                     Assert.assertTrue(returnNode.result().toString(), returnNode.result().isConstant());
                     Assert.assertEquals(expectedConstantResult, returnNode.result().asConstant());
                 }
-                int newInstanceCount = graph.getNodes(NewInstanceNode.class).count() + graph.getNodes(NewArrayNode.class).count() + graph.getNodes(MaterializeObjectNode.class).count();
+                int newInstanceCount = graph.getNodes(NewInstanceNode.class).count() + graph.getNodes(NewArrayNode.class).count() + graph.getNodes(CommitAllocationNode.class).count();
                 Assert.assertEquals(0, newInstanceCount);
                 return returnNode;
             }
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java	Mon Apr 29 18:38:16 2013 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java	Mon Apr 29 14:53:08 2013 +0200
@@ -35,17 +35,16 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.util.*;
+import com.oracle.graal.nodes.virtual.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
 import com.oracle.graal.phases.graph.*;
 import com.oracle.graal.phases.tiers.*;
-import com.oracle.graal.virtual.nodes.*;
 import com.oracle.graal.virtual.phases.ea.*;
 
 /**
- * In these test cases the probability of all invokes is set to a high value, such that an
- * InliningPhase should inline them all. After that, the PartialEscapeAnalysisPhase is expected to
- * remove all allocations and return the correct values.
+ * The PartialEscapeAnalysisPhase is expected to remove all allocations and return the correct
+ * values.
  */
 public class PartialEscapeAnalysisTest extends GraalCompilerTest {
 
@@ -137,9 +136,9 @@
             NodesToDoubles nodeProbabilities = new ComputeProbabilityClosure(result).apply();
             double probabilitySum = 0;
             int materializeCount = 0;
-            for (MaterializeObjectNode materialize : result.getNodes(MaterializeObjectNode.class)) {
-                probabilitySum += nodeProbabilities.get(materialize);
-                materializeCount++;
+            for (CommitAllocationNode materialize : result.getNodes(CommitAllocationNode.class)) {
+                probabilitySum += nodeProbabilities.get(materialize) * materialize.getVirtualObjects().size();
+                materializeCount += materialize.getVirtualObjects().size();
             }
             Assert.assertEquals("unexpected number of MaterializeObjectNodes", expectedCount, materializeCount);
             Assert.assertEquals("unexpected probability of MaterializeObjectNodes", expectedProbability, probabilitySum, 0.01);
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/DebugInfoBuilder.java	Mon Apr 29 18:38:16 2013 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/DebugInfoBuilder.java	Mon Apr 29 14:53:08 2013 +0200
@@ -165,7 +165,6 @@
                 throw new GraalInternalError("no mapping found for virtual object %s", obj);
             }
             if (state instanceof MaterializedObjectState) {
-                assert !(((MaterializedObjectState) state).materializedValue() instanceof VirtualObjectNode);
                 return toValue(((MaterializedObjectState) state).materializedValue());
             } else {
                 assert obj.entryCount() == 0 || state instanceof VirtualObjectState;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java	Mon Apr 29 18:38:16 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java	Mon Apr 29 14:53:08 2013 +0200
@@ -78,6 +78,7 @@
 import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.nodes.virtual.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.printer.*;
 import com.oracle.graal.replacements.*;
@@ -773,6 +774,64 @@
             FixedGuardNode node = (FixedGuardNode) n;
             ValueAnchorNode newAnchor = graph.add(new ValueAnchorNode(tool.createGuard(node.condition(), node.getReason(), node.getAction(), node.isNegated())));
             graph.replaceFixedWithFixed(node, newAnchor);
+        } else if (n instanceof CommitAllocationNode) {
+            CommitAllocationNode commit = (CommitAllocationNode) n;
+
+            graph.addBeforeFixed(commit, graph.add(new RegionStartNode()));
+            List<ValueNode> allocations = new ArrayList<>(commit.getVirtualObjects().size());
+            for (int objIndex = 0; objIndex < commit.getVirtualObjects().size(); objIndex++) {
+                VirtualObjectNode virtual = commit.getVirtualObjects().get(objIndex);
+                int lockCount = commit.getLockCounts().get(objIndex);
+                int entryCount = virtual.entryCount();
+
+                FixedWithNextNode newObject;
+                if (virtual instanceof VirtualInstanceNode) {
+                    newObject = graph.add(new NewInstanceNode(virtual.type(), true, lockCount > 0));
+                } else {
+                    ResolvedJavaType element = ((VirtualArrayNode) virtual).componentType();
+                    newObject = graph.add(new NewArrayNode(element, ConstantNode.forInt(entryCount, graph), true, lockCount > 0));
+                }
+                graph.addBeforeFixed(commit, newObject);
+                allocations.add(newObject);
+            }
+            int valuePos = 0;
+            for (int objIndex = 0; objIndex < commit.getVirtualObjects().size(); objIndex++) {
+                VirtualObjectNode virtual = commit.getVirtualObjects().get(objIndex);
+                int entryCount = virtual.entryCount();
+
+                ValueNode newObject = allocations.get(objIndex);
+                if (virtual instanceof VirtualInstanceNode) {
+                    VirtualInstanceNode instance = (VirtualInstanceNode) virtual;
+                    for (int i = 0; i < entryCount; i++) {
+                        ValueNode value = commit.getValues().get(valuePos++);
+                        if (value instanceof VirtualObjectNode) {
+                            value = allocations.get(commit.getVirtualObjects().indexOf(value));
+                        }
+                        graph.addBeforeFixed(commit, graph.add(new WriteNode(newObject, value, createFieldLocation(graph, (HotSpotResolvedJavaField) instance.field(i)), WriteBarrierType.NONE)));
+                    }
+                } else {
+                    VirtualArrayNode array = (VirtualArrayNode) virtual;
+                    ResolvedJavaType element = array.componentType();
+                    for (int i = 0; i < entryCount; i++) {
+                        ValueNode value = commit.getValues().get(valuePos++);
+                        if (value instanceof VirtualObjectNode) {
+                            int indexOf = commit.getVirtualObjects().indexOf(value);
+                            assert indexOf != -1 : commit + " " + value;
+                            value = allocations.get(indexOf);
+                        }
+                        graph.addBeforeFixed(commit, graph.add(new WriteNode(newObject, value, createArrayLocation(graph, element.getKind(), ConstantNode.forInt(i, graph)), WriteBarrierType.NONE)));
+                    }
+                }
+            }
+            graph.addBeforeFixed(commit, graph.add(new RegionEndNode()));
+            for (Node usage : commit.usages().snapshot()) {
+                AllocatedObjectNode addObject = (AllocatedObjectNode) usage;
+                int index = commit.getVirtualObjects().indexOf(addObject.getVirtualObject());
+                FixedValueAnchorNode anchor = graph.add(new FixedValueAnchorNode(allocations.get(index)));
+                graph.addBeforeFixed(commit, anchor);
+                graph.replaceFloating(addObject, anchor);
+            }
+            graph.removeFixed(commit);
         } else if (n instanceof CheckCastNode) {
             checkcastSnippets.lower((CheckCastNode) n, tool);
         } else if (n instanceof CheckCastDynamicNode) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/AllocatedObjectNode.java	Mon Apr 29 14:53:08 2013 +0200
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2009, 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.nodes.virtual;
+
+import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.type.*;
+
+/**
+ * Selects one object from a {@link CommitAllocationNode}. The object is identified by its
+ * {@link VirtualObjectNode}.
+ */
+public class AllocatedObjectNode extends FloatingNode implements Virtualizable {
+
+    @Input private VirtualObjectNode virtualObject;
+    @Input private CommitAllocationNode commit;
+
+    public AllocatedObjectNode(VirtualObjectNode virtualObject) {
+        super(StampFactory.exactNonNull(virtualObject.type()));
+        this.virtualObject = virtualObject;
+    }
+
+    public VirtualObjectNode getVirtualObject() {
+        return virtualObject;
+    }
+
+    public CommitAllocationNode getCommit() {
+        return commit;
+    }
+
+    public void setCommit(CommitAllocationNode x) {
+        updateUsages(commit, x);
+        commit = x;
+    }
+
+    @Override
+    public void virtualize(VirtualizerTool tool) {
+        tool.replaceWithVirtual(getVirtualObject());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/CommitAllocationNode.java	Mon Apr 29 14:53:08 2013 +0200
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2009, 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.nodes.virtual;
+
+import java.util.*;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.type.*;
+
+@NodeInfo(nameTemplate = "Alloc {i#virtualObjects}")
+public final class CommitAllocationNode extends FixedWithNextNode implements VirtualizableAllocation, Lowerable, Node.IterableNodeType, Simplifiable {
+
+    @Input private final NodeInputList<VirtualObjectNode> virtualObjects = new NodeInputList<>(this);
+    @Input private final NodeInputList<ValueNode> values = new NodeInputList<>(this);
+    private final List<Integer> lockCounts = new ArrayList<>();
+
+    public CommitAllocationNode() {
+        super(StampFactory.forVoid());
+    }
+
+    public List<VirtualObjectNode> getVirtualObjects() {
+        return virtualObjects;
+    }
+
+    public List<ValueNode> getValues() {
+        return values;
+    }
+
+    public List<Integer> getLockCounts() {
+        return lockCounts;
+    }
+
+    @Override
+    public boolean verify() {
+        assertTrue(virtualObjects.size() == lockCounts.size(), "lockCounts size doesn't match");
+        int valueCount = 0;
+        for (VirtualObjectNode virtual : virtualObjects) {
+            valueCount += virtual.entryCount();
+        }
+        assertTrue(values.size() == valueCount, "values size doesn't match");
+        return super.verify();
+    }
+
+    @Override
+    public void lower(LoweringTool tool, LoweringType loweringType) {
+        if (loweringType == LoweringType.AFTER_GUARDS) {
+            tool.getRuntime().lower(this, tool);
+        }
+    }
+
+    @Override
+    public void virtualize(VirtualizerTool tool) {
+        int pos = 0;
+        for (int i = 0; i < virtualObjects.size(); i++) {
+            VirtualObjectNode virtualObject = virtualObjects.get(i);
+            int entryCount = virtualObject.entryCount();
+            tool.createVirtualObject(virtualObject, values.subList(pos, pos + entryCount).toArray(new ValueNode[entryCount]), lockCounts.get(i));
+            pos += entryCount;
+        }
+        tool.delete();
+    }
+
+    @Override
+    public Map<Object, Object> getDebugProperties(Map<Object, Object> map) {
+        Map<Object, Object> properties = super.getDebugProperties(map);
+        int valuePos = 0;
+        for (int objIndex = 0; objIndex < virtualObjects.size(); objIndex++) {
+            VirtualObjectNode virtual = virtualObjects.get(objIndex);
+            StringBuilder s = new StringBuilder();
+            s.append(MetaUtil.toJavaName(virtual.type(), false)).append("[");
+            for (int i = 0; i < virtual.entryCount(); i++) {
+                ValueNode value = values.get(valuePos++);
+                s.append(i == 0 ? "" : ",").append(value == null ? "_" : value.toString(Verbosity.Id));
+            }
+            s.append("]");
+            if (lockCounts.get(objIndex) > 0) {
+                s.append(" locked(").append(lockCounts.get(objIndex)).append(")");
+            }
+            properties.put("object(" + virtual.toString(Verbosity.Id) + ")", s.toString());
+        }
+        return properties;
+    }
+
+    @Override
+    public void simplify(SimplifierTool tool) {
+        boolean[] used = new boolean[virtualObjects.size()];
+        int usedCount = 0;
+        for (Node usage : usages()) {
+            AllocatedObjectNode addObject = (AllocatedObjectNode) usage;
+            int index = virtualObjects.indexOf(addObject.getVirtualObject());
+            assert !used[index];
+            used[index] = true;
+            usedCount++;
+        }
+        boolean progress;
+        do {
+            progress = false;
+            int valuePos = 0;
+            for (int objIndex = 0; objIndex < virtualObjects.size(); objIndex++) {
+                VirtualObjectNode virtualObject = virtualObjects.get(objIndex);
+                if (used[objIndex]) {
+                    for (int i = 0; i < virtualObject.entryCount(); i++) {
+                        int index = virtualObjects.indexOf(values.get(valuePos + i));
+                        if (index != -1 && !used[index]) {
+                            progress = true;
+                            used[index] = true;
+                            usedCount++;
+                        }
+                    }
+                }
+                valuePos += virtualObject.entryCount();
+            }
+
+        } while (progress);
+
+        if (usedCount < virtualObjects.size()) {
+            List<VirtualObjectNode> newVirtualObjects = new ArrayList<>(usedCount);
+            List<Integer> newLockCounts = new ArrayList<>(usedCount);
+            List<ValueNode> newValues = new ArrayList<>();
+            int valuePos = 0;
+            for (int objIndex = 0; objIndex < virtualObjects.size(); objIndex++) {
+                VirtualObjectNode virtualObject = virtualObjects.get(objIndex);
+                if (used[objIndex]) {
+                    newVirtualObjects.add(virtualObject);
+                    newLockCounts.add(lockCounts.get(objIndex));
+                    newValues.addAll(values.subList(valuePos, valuePos + virtualObject.entryCount()));
+                }
+                valuePos += virtualObject.entryCount();
+            }
+            virtualObjects.clear();
+            virtualObjects.addAll(newVirtualObjects);
+            lockCounts.clear();
+            lockCounts.addAll(newLockCounts);
+            values.clear();
+            values.addAll(newValues);
+        }
+    }
+
+}
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualArrayNode.java	Mon Apr 29 18:38:16 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualArrayNode.java	Mon Apr 29 14:53:08 2013 +0200
@@ -22,14 +22,11 @@
  */
 package com.oracle.graal.nodes.virtual;
 
-import java.util.*;
-
 import sun.misc.*;
 
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.spi.*;
 
 @NodeInfo(nameTemplate = "VirtualArray {p#componentType/s}[{p#length}]")
@@ -72,7 +69,7 @@
     }
 
     @Override
-    public String fieldName(int index) {
+    public String entryName(int index) {
         return "[" + index + "]";
     }
 
@@ -143,17 +140,8 @@
     }
 
     @Override
-    public void materializeAt(FixedWithNextNode materializeNode, List<ValueNode> values, boolean defaultValuesOnly, int lockCount) {
-        StructuredGraph graph = (StructuredGraph) graph();
-        ResolvedJavaType element = componentType();
-        NewArrayNode newArray = graph.add(new NewArrayNode(element, ConstantNode.forInt(entryCount(), graph), defaultValuesOnly, lockCount > 0));
-        materializeNode.replaceAtUsages(newArray);
-        graph.addBeforeFixed(materializeNode, newArray);
-        if (!defaultValuesOnly) {
-            for (int i = 0; i < entryCount(); i++) {
-                graph.addBeforeFixed(materializeNode, graph.add(new StoreIndexedNode(newArray, ConstantNode.forInt(i, graph), element.getKind(), values.get(i))));
-            }
-        }
-        graph.removeFixed(materializeNode);
+    public AllocatedObjectNode getMaterializedRepresentation(ValueNode[] entries, int lockCount) {
+        assert lockCount == 0;
+        return new AllocatedObjectNode(this);
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualBoxingNode.java	Mon Apr 29 18:38:16 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualBoxingNode.java	Mon Apr 29 14:53:08 2013 +0200
@@ -22,8 +22,6 @@
  */
 package com.oracle.graal.nodes.virtual;
 
-import java.util.*;
-
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.extended.*;
@@ -48,11 +46,9 @@
     }
 
     @Override
-    public void materializeAt(FixedWithNextNode materializeNode, List<ValueNode> values, boolean defaultValuesOnly, int lockCount) {
-        assert values.size() == 1;
+    public ValueNode getMaterializedRepresentation(ValueNode[] entries, int lockCount) {
+        assert entries.length == 1;
         assert lockCount == 0;
-        StructuredGraph graph = (StructuredGraph) graph();
-        BoxNode valueOf = graph.add(new BoxNode(values.get(0), type(), boxingKind));
-        graph.replaceFixedWithFixed(materializeNode, valueOf);
+        return new BoxNode(entries[0], type(), boxingKind);
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualInstanceNode.java	Mon Apr 29 18:38:16 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualInstanceNode.java	Mon Apr 29 14:53:08 2013 +0200
@@ -22,14 +22,11 @@
  */
 package com.oracle.graal.nodes.virtual;
 
-import java.util.*;
-
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.java.*;
 
-@NodeInfo(nameTemplate = "VirtualInstance {p#type}")
+@NodeInfo(nameTemplate = "VirtualInstance {p#type/s}")
 public class VirtualInstanceNode extends VirtualObjectNode {
 
     private final ResolvedJavaType type;
@@ -68,7 +65,7 @@
     }
 
     @Override
-    public String fieldName(int index) {
+    public String entryName(int index) {
         return fields[index].getName();
     }
 
@@ -99,16 +96,7 @@
     }
 
     @Override
-    public void materializeAt(FixedWithNextNode materializeNode, List<ValueNode> values, boolean defaultValuesOnly, int lockCount) {
-        StructuredGraph graph = (StructuredGraph) graph();
-        NewInstanceNode newInstance = graph.add(new NewInstanceNode(type(), defaultValuesOnly, lockCount > 0));
-        materializeNode.replaceAtUsages(newInstance);
-        graph.addBeforeFixed(materializeNode, newInstance);
-        if (!defaultValuesOnly) {
-            for (int i = 0; i < entryCount(); i++) {
-                graph.addBeforeFixed(materializeNode, graph.add(new StoreFieldNode(newInstance, field(i), values.get(i))));
-            }
-        }
-        graph.removeFixed(materializeNode);
+    public ValueNode getMaterializedRepresentation(ValueNode[] entries, int lockCount) {
+        return new AllocatedObjectNode(this);
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualObjectNode.java	Mon Apr 29 18:38:16 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualObjectNode.java	Mon Apr 29 14:53:08 2013 +0200
@@ -22,41 +22,68 @@
  */
 package com.oracle.graal.nodes.virtual;
 
-import java.util.*;
-
 import com.oracle.graal.api.meta.*;
-import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
 
-@NodeInfo(nameTemplate = "VirtualObject {p#type}")
 public abstract class VirtualObjectNode extends ValueNode implements LIRLowerable {
 
     public VirtualObjectNode() {
         super(StampFactory.virtual());
     }
 
+    /**
+     * The type of object described by this {@link VirtualObjectNode}. In case of arrays, this is
+     * the array type (and not the component type).
+     */
     public abstract ResolvedJavaType type();
 
+    /**
+     * The number of entries this virtual object has. Either the number of fields or the number of
+     * array elements.
+     */
     public abstract int entryCount();
 
+    /**
+     * Returns the name of the entry at the given index. Only used for debugging purposes.
+     */
+    public abstract String entryName(int i);
+
+    /**
+     * If the given index denotes an entry in this virtual object, the index of this entry is
+     * returned. If no such entry can be found, this method returns -1.
+     */
+    public abstract int entryIndexForOffset(long constantOffset);
+
+    /**
+     * Returns the {@link Kind} of the entry at the given index.
+     */
+    public abstract Kind entryKind(int index);
+
+    /**
+     * Returns an exact duplicate of this virtual object node, which has not been added to the graph
+     * yet.
+     */
+    public abstract VirtualObjectNode duplicate();
+
+    /**
+     * Specifies whether this virtual object has an object identity. If not, then the result of a
+     * comparison of two virtual objects is determined by comparing their contents.
+     */
+    public boolean hasIdentity() {
+        return true;
+    }
+
+    /**
+     * Returns a node that can be used to materialize this virtual object. If this returns an
+     * {@link AllocatedObjectNode} then this node will be attached to a {@link CommitAllocationNode}
+     * , otherwise the node will just be added to the graph.
+     */
+    public abstract ValueNode getMaterializedRepresentation(ValueNode[] entries, int lockCount);
+
     @Override
     public void generate(LIRGeneratorTool gen) {
         // nothing to do...
     }
-
-    public abstract String fieldName(int i);
-
-    public abstract void materializeAt(FixedWithNextNode materializeNode, List<ValueNode> values, boolean defaultValuesOnly, int lockCount);
-
-    public abstract int entryIndexForOffset(long constantOffset);
-
-    public abstract Kind entryKind(int index);
-
-    public abstract VirtualObjectNode duplicate();
-
-    public boolean hasIdentity() {
-        return true;
-    }
 }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/nodes/MaterializeObjectNode.java	Mon Apr 29 18:38:16 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,100 +0,0 @@
-/*
- * Copyright (c) 2009, 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.virtual.nodes;
-
-import com.oracle.graal.api.meta.*;
-import com.oracle.graal.graph.*;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.nodes.type.*;
-import com.oracle.graal.nodes.virtual.*;
-
-@NodeInfo(nameTemplate = "Materialize {i#virtualObject}")
-public final class MaterializeObjectNode extends FixedWithNextNode implements VirtualizableAllocation, Lowerable, Node.IterableNodeType, Canonicalizable, ArrayLengthProvider {
-
-    @Input private final NodeInputList<ValueNode> values;
-    @Input private final VirtualObjectNode virtualObject;
-    private final int lockCount;
-
-    public MaterializeObjectNode(VirtualObjectNode virtualObject, int lockCount) {
-        super(StampFactory.exactNonNull(virtualObject.type()));
-        this.virtualObject = virtualObject;
-        this.lockCount = lockCount;
-        this.values = new NodeInputList<>(this, virtualObject.entryCount());
-    }
-
-    public NodeInputList<ValueNode> getValues() {
-        return values;
-    }
-
-    public VirtualObjectNode getVirtualObject() {
-        return virtualObject;
-    }
-
-    public int getLockCount() {
-        return lockCount;
-    }
-
-    @Override
-    public ValueNode length() {
-        assert virtualObject.type().isArray();
-        return ConstantNode.forInt(values.size(), graph());
-    }
-
-    /**
-     * @return true if the object that will be created is without locks and has only entries that
-     *         are {@link Constant#defaultForKind(Kind)}, false otherwise.
-     */
-    public boolean isDefault() {
-        if (lockCount > 0) {
-            return false;
-        } else {
-            for (ValueNode value : values) {
-                if (!value.isConstant() || !value.asConstant().isDefaultForKind()) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    @Override
-    public void lower(LoweringTool tool, LoweringType loweringType) {
-        virtualObject.materializeAt(this, values, isDefault(), lockCount);
-    }
-
-    @Override
-    public ValueNode canonical(CanonicalizerTool tool) {
-        if (usages().isEmpty()) {
-            return null;
-        } else {
-            return this;
-        }
-    }
-
-    @Override
-    public void virtualize(VirtualizerTool tool) {
-        tool.createVirtualObject(virtualObject, values.toArray(new ValueNode[values.size()]), lockCount);
-        tool.replaceWithVirtual(virtualObject);
-    }
-}
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/BlockState.java	Mon Apr 29 18:38:16 2013 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/BlockState.java	Mon Apr 29 14:53:08 2013 +0200
@@ -27,13 +27,11 @@
 import java.util.*;
 
 import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.spi.Virtualizable.EscapeState;
 import com.oracle.graal.nodes.virtual.*;
-import com.oracle.graal.virtual.nodes.*;
 
 class BlockState {
 
@@ -151,62 +149,53 @@
 
     public void materializeBefore(FixedNode fixed, VirtualObjectNode virtual, EscapeState state, GraphEffectList materializeEffects) {
         PartialEscapeClosure.METRIC_MATERIALIZATIONS.increment();
-        HashSet<VirtualObjectNode> deferred = new HashSet<>();
-        GraphEffectList deferredStores = new GraphEffectList();
-        materializeChangedBefore(fixed, virtual, state, deferred, deferredStores, materializeEffects);
-        materializeEffects.addAll(deferredStores);
+        List<AllocatedObjectNode> objects = new ArrayList<>(2);
+        List<ValueNode> values = new ArrayList<>(8);
+        List<Integer> lockCounts = new ArrayList<>(2);
+        List<ValueNode> otherAllocations = new ArrayList<>(2);
+        materializeWithCommit(virtual, objects, lockCounts, values, otherAllocations, state);
+
+        materializeEffects.addMaterializationBefore(fixed, objects, lockCounts, values, otherAllocations);
     }
 
-    private void materializeChangedBefore(FixedNode fixed, VirtualObjectNode virtual, EscapeState state, HashSet<VirtualObjectNode> deferred, GraphEffectList deferredStores,
-                    GraphEffectList materializeEffects) {
-        trace("materializing %s at %s", virtual, fixed);
+    private void materializeWithCommit(VirtualObjectNode virtual, List<AllocatedObjectNode> objects, List<Integer> lockCounts, List<ValueNode> values, List<ValueNode> otherAllocations, EscapeState state) {
+        trace("materializing %s", virtual);
         ObjectState obj = getObjectState(virtual);
         if (obj.getLockCount() > 0 && obj.virtual.type().isArray()) {
             throw new BailoutException("array materialized with lock");
         }
 
-        ValueNode[] fieldState = obj.getEntries();
-
-        MaterializeObjectNode materialize = new MaterializeObjectNode(virtual, obj.getLockCount());
-        ValueNode[] values = new ValueNode[obj.getEntries().length];
-        obj.escape(materialize, state);
-        deferred.add(virtual);
-        for (int i = 0; i < fieldState.length; i++) {
-            ObjectState valueObj = getObjectState(fieldState[i]);
-            if (valueObj != null) {
-                if (valueObj.isVirtual()) {
-                    materializeChangedBefore(fixed, valueObj.virtual, state, deferred, deferredStores, materializeEffects);
+        ValueNode[] entries = obj.getEntries();
+        ValueNode representation = virtual.getMaterializedRepresentation(entries, obj.getLockCount());
+        obj.escape(representation, state);
+        if (representation instanceof AllocatedObjectNode) {
+            objects.add((AllocatedObjectNode) representation);
+            lockCounts.add(obj.getLockCount());
+            int pos = values.size();
+            while (values.size() < pos + entries.length) {
+                values.add(null);
+            }
+            for (int i = 0; i < entries.length; i++) {
+                ObjectState entryObj = getObjectState(entries[i]);
+                if (entryObj != null) {
+                    if (entryObj.isVirtual()) {
+                        materializeWithCommit(entryObj.getVirtualObject(), objects, lockCounts, values, otherAllocations, state);
+                    }
+                    values.set(pos + i, entryObj.getMaterializedValue());
+                } else {
+                    values.set(pos + i, entries[i]);
                 }
-                if (deferred.contains(valueObj.virtual)) {
-                    Kind fieldKind;
-                    CyclicMaterializeStoreNode store;
-                    if (virtual instanceof VirtualArrayNode) {
-                        store = new CyclicMaterializeStoreNode(materialize, valueObj.getMaterializedValue(), i);
-                        fieldKind = ((VirtualArrayNode) virtual).componentType().getKind();
-                    } else {
-                        VirtualInstanceNode instanceObject = (VirtualInstanceNode) virtual;
-                        store = new CyclicMaterializeStoreNode(materialize, valueObj.getMaterializedValue(), instanceObject.field(i));
-                        fieldKind = instanceObject.field(i).getType().getKind();
-                    }
-                    deferredStores.addFixedNodeBefore(store, fixed);
-                    values[i] = ConstantNode.defaultForKind(fieldKind, fixed.graph());
-                } else {
-                    values[i] = valueObj.getMaterializedValue();
+            }
+            if (virtual instanceof VirtualInstanceNode) {
+                VirtualInstanceNode instance = (VirtualInstanceNode) virtual;
+                for (int i = 0; i < entries.length; i++) {
+                    readCache.put(new ReadCacheEntry(instance.field(i), representation), values.get(pos + i));
                 }
-            } else {
-                values[i] = fieldState[i];
             }
+        } else {
+            otherAllocations.add(representation);
+            assert obj.getLockCount() == 0;
         }
-        deferred.remove(virtual);
-
-        if (virtual instanceof VirtualInstanceNode) {
-            VirtualInstanceNode instance = (VirtualInstanceNode) virtual;
-            for (int i = 0; i < fieldState.length; i++) {
-                readCache.put(new ReadCacheEntry(instance.field(i), materialize), fieldState[i]);
-            }
-        }
-
-        materializeEffects.addMaterialization(materialize, fixed, values);
     }
 
     void addAndMarkAlias(VirtualObjectNode virtual, ValueNode node, NodeBitMap usages) {
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java	Mon Apr 29 18:38:16 2013 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java	Mon Apr 29 14:53:08 2013 +0200
@@ -26,10 +26,10 @@
 
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.calc.*;
 import com.oracle.graal.nodes.debug.*;
 import com.oracle.graal.nodes.virtual.*;
 import com.oracle.graal.phases.common.*;
-import com.oracle.graal.virtual.nodes.*;
 
 public class GraphEffectList extends EffectList {
 
@@ -118,33 +118,6 @@
     }
 
     /**
-     * Add the materialization node to the graph's control flow at the given position, and then sets
-     * its values.
-     * 
-     * @param node The materialization node that should be added.
-     * @param position The fixed node before which the materialization node should be added.
-     * @param values The values for the materialization node's entries.
-     */
-    public void addMaterialization(final MaterializeObjectNode node, final FixedNode position, final ValueNode[] values) {
-        add(new Effect() {
-
-            @Override
-            public String name() {
-                return "addMaterialization";
-            }
-
-            @Override
-            public void apply(StructuredGraph graph, ArrayList<Node> obsoleteNodes) {
-                assert !node.isAlive() && !node.isDeleted() && position.isAlive();
-                graph.addBeforeFixed(position, graph.add(node));
-                for (int i = 0; i < values.length; i++) {
-                    node.getValues().set(i, values[i]);
-                }
-            }
-        });
-    }
-
-    /**
      * Adds an value to the given phi node.
      * 
      * @param node The phi node to which the value should be added.
@@ -330,4 +303,68 @@
             }
         });
     }
+
+    /**
+     * Add the materialization node to the graph's control flow at the given position, and then sets
+     * its values.
+     * 
+     * @param position The fixed node before which the materialization node should be added.
+     * @param objects The allocated objects.
+     * @param lockCounts The lock count for each object.
+     * @param values The values (field, elements) of all objects.
+     * @param otherAllocations A list of allocations that need to be added before the rest (used for
+     *            boxing allocations).
+     */
+    public void addMaterializationBefore(final FixedNode position, final List<AllocatedObjectNode> objects, final List<Integer> lockCounts, final List<ValueNode> values,
+                    final List<ValueNode> otherAllocations) {
+        add(new Effect() {
+
+            @Override
+            public String name() {
+                return "addMaterializationBefore";
+            }
+
+            @Override
+            public void apply(StructuredGraph graph, ArrayList<Node> obsoleteNodes) {
+                for (ValueNode otherAllocation : otherAllocations) {
+                    graph.add(otherAllocation);
+                    if (otherAllocation instanceof FixedWithNextNode) {
+                        graph.addBeforeFixed(position, (FixedWithNextNode) otherAllocation);
+                    } else {
+                        assert otherAllocation instanceof FloatingNode;
+                    }
+                }
+                if (!objects.isEmpty()) {
+                    CommitAllocationNode commit;
+                    if (position.predecessor() instanceof CommitAllocationNode) {
+                        commit = (CommitAllocationNode) position.predecessor();
+                    } else {
+                        commit = graph.add(new CommitAllocationNode());
+                        graph.addBeforeFixed(position, commit);
+                    }
+                    for (AllocatedObjectNode obj : objects) {
+                        graph.add(obj);
+                        commit.getVirtualObjects().add(obj.getVirtualObject());
+                        obj.setCommit(commit);
+                    }
+                    commit.getValues().addAll(values);
+                    commit.getLockCounts().addAll(lockCounts);
+
+                    assert commit.usages().filter(AllocatedObjectNode.class).count() == commit.usages().count();
+                    HashSet<AllocatedObjectNode> materializedValues = new HashSet<>(commit.usages().filter(AllocatedObjectNode.class).snapshot());
+                    for (int i = 0; i < commit.getValues().size(); i++) {
+                        if (materializedValues.contains(commit.getValues().get(i))) {
+                            commit.getValues().set(i, ((AllocatedObjectNode) commit.getValues().get(i)).getVirtualObject());
+                        }
+                    }
+
+                }
+            }
+
+            @Override
+            public boolean isVisible() {
+                return true;
+            }
+        });
+    }
 }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/ObjectState.java	Mon Apr 29 18:38:16 2013 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/ObjectState.java	Mon Apr 29 14:53:08 2013 +0200
@@ -36,7 +36,7 @@
  */
 class ObjectState extends Virtualizable.State {
 
-    public final VirtualObjectNode virtual;
+    final VirtualObjectNode virtual;
 
     private EscapeState state;
     private ValueNode[] entries;
@@ -137,7 +137,7 @@
         }
         if (entries != null) {
             for (int i = 0; i < entries.length; i++) {
-                str.append(virtual.fieldName(i)).append('=').append(entries[i]).append(' ');
+                str.append(virtual.entryName(i)).append('=').append(entries[i]).append(' ');
             }
         }
         if (materializedValue != null) {
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeAnalysisPhase.java	Mon Apr 29 18:38:16 2013 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeAnalysisPhase.java	Mon Apr 29 14:53:08 2013 +0200
@@ -32,13 +32,13 @@
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.util.*;
+import com.oracle.graal.nodes.virtual.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
 import com.oracle.graal.phases.common.CanonicalizerPhase.CustomCanonicalizer;
 import com.oracle.graal.phases.graph.*;
 import com.oracle.graal.phases.schedule.*;
 import com.oracle.graal.phases.tiers.*;
-import com.oracle.graal.virtual.nodes.*;
 
 public class PartialEscapeAnalysisPhase extends BasePhase<HighTierContext> {
 
@@ -203,32 +203,36 @@
     public static Map<Invoke, Double> getHints(StructuredGraph graph) {
         NodesToDoubles probabilities = new ComputeProbabilityClosure(graph).apply();
         Map<Invoke, Double> hints = null;
-        for (MaterializeObjectNode materialize : graph.getNodes(MaterializeObjectNode.class)) {
+        for (CommitAllocationNode commit : graph.getNodes(CommitAllocationNode.class)) {
             double sum = 0;
             double invokeSum = 0;
-            for (Node usage : materialize.usages()) {
-                if (usage instanceof FixedNode) {
-                    sum += probabilities.get((FixedNode) usage);
-                } else {
-                    if (usage instanceof MethodCallTargetNode) {
-                        invokeSum += probabilities.get(((MethodCallTargetNode) usage).invoke().asNode());
-                    }
-                    for (Node secondLevelUage : materialize.usages()) {
-                        if (secondLevelUage instanceof FixedNode) {
-                            sum += probabilities.get(((FixedNode) secondLevelUage));
+            for (Node commitUsage : commit.usages()) {
+                for (Node usage : commitUsage.usages()) {
+                    if (usage instanceof FixedNode) {
+                        sum += probabilities.get((FixedNode) usage);
+                    } else {
+                        if (usage instanceof MethodCallTargetNode) {
+                            invokeSum += probabilities.get(((MethodCallTargetNode) usage).invoke().asNode());
+                        }
+                        for (Node secondLevelUage : usage.usages()) {
+                            if (secondLevelUage instanceof FixedNode) {
+                                sum += probabilities.get(((FixedNode) secondLevelUage));
+                            }
                         }
                     }
                 }
             }
             // TODO(lstadler) get rid of this magic number
             if (sum > 100 && invokeSum > 0) {
-                for (Node usage : materialize.usages()) {
-                    if (usage instanceof MethodCallTargetNode) {
-                        if (hints == null) {
-                            hints = new HashMap<>();
+                for (Node commitUsage : commit.usages()) {
+                    for (Node usage : commitUsage.usages()) {
+                        if (usage instanceof MethodCallTargetNode) {
+                            if (hints == null) {
+                                hints = new HashMap<>();
+                            }
+                            Invoke invoke = ((MethodCallTargetNode) usage).invoke();
+                            hints.put(invoke, sum / invokeSum);
                         }
-                        Invoke invoke = ((MethodCallTargetNode) usage).invoke();
-                        hints.put(invoke, sum / invokeSum);
                     }
                 }
             }